From 62653605da7face548863e8fcbc76ccbad3e3db8 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 24 Feb 2014 20:16:22 +0100 Subject: client API: add support for accessing properties by their native type This actually makes use of the client.h declarations and the mpv_node mechanisms added some commits ago. For now, using MPV_FORMAT_STRING will usually fallback to explicit string conversion, but not in the other cases. E.g. reading a numeric property as string will work, but not reading a string property as number. Other than that, only MPV_FORMAT_INT64->MPV_FORMAT_DOUBLE does an automatic conversion. I'm not sure whether these semantics and API are good, so comments and suggestions are welcome. --- player/client.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 158 insertions(+), 33 deletions(-) (limited to 'player/client.c') diff --git a/player/client.c b/player/client.c index 8cdfe94b15..d61c0a5056 100644 --- a/player/client.c +++ b/player/client.c @@ -441,6 +441,54 @@ void mpv_wakeup(mpv_handle *ctx) pthread_mutex_unlock(&ctx->lock); } +// map client API types to internal types +static const struct m_option type_conv[] = { + [MPV_FORMAT_STRING] = { .type = CONF_TYPE_STRING }, + [MPV_FORMAT_FLAG] = { .type = CONF_TYPE_FLAG }, + [MPV_FORMAT_INT64] = { .type = CONF_TYPE_INT64 }, + [MPV_FORMAT_DOUBLE] = { .type = CONF_TYPE_DOUBLE }, + [MPV_FORMAT_NODE] = { .type = CONF_TYPE_NODE }, +}; + +static const struct m_option *get_mp_type(mpv_format format) +{ + if (format < 0 || format >= MP_ARRAY_SIZE(type_conv)) + return NULL; + if (!type_conv[format].type) + return NULL; + return &type_conv[format]; +} + +// for read requests - MPV_FORMAT_OSD_STRING special handling +static const struct m_option *get_mp_type_get(mpv_format format) +{ + if (format == MPV_FORMAT_OSD_STRING) + format = MPV_FORMAT_STRING; // it's string data, just other semantics + return get_mp_type(format); +} + +// move src->dst, and do implicit conversion if possible (conversions to or +// from strings are handled otherwise) +static bool conv_node_to_format(void *dst, mpv_format dst_fmt, mpv_node *src) +{ + if (dst_fmt == src->format) { + const struct m_option *type = get_mp_type(dst_fmt); + memcpy(dst, &src->u, type->type->size); + return true; + } + if (dst_fmt == MPV_FORMAT_DOUBLE && src->format == MPV_FORMAT_INT64) { + *(double *)dst = src->u.int64; + return true; + } + return false; +} + +void mpv_free_node_contents(mpv_node *node) +{ + static const struct m_option type = { .type = CONF_TYPE_NODE }; + m_option_free(&type, node); +} + int mpv_set_option(mpv_handle *ctx, const char *name, mpv_format format, void *data) { @@ -584,6 +632,7 @@ static int translate_property_error(int errc) case M_PROPERTY_UNAVAILABLE: return MPV_ERROR_PROPERTY_UNAVAILABLE; case M_PROPERTY_NOT_IMPLEMENTED: return MPV_ERROR_PROPERTY_ERROR; case M_PROPERTY_UNKNOWN: return MPV_ERROR_PROPERTY_NOT_FOUND; + case M_PROPERTY_INVALID_FORMAT: return MPV_ERROR_PROPERTY_FORMAT; // shouldn't happen default: return MPV_ERROR_PROPERTY_ERROR; } @@ -599,26 +648,41 @@ struct setproperty_request { uint64_t userdata; }; -static int property_format_to_set_cmd(int format) -{ - switch (format) { - case MPV_FORMAT_STRING: return M_PROPERTY_SET_STRING; - default: return MPV_ERROR_PROPERTY_FORMAT; - } -} - static void setproperty_fn(void *arg) { struct setproperty_request *req = arg; + const struct m_option *type = get_mp_type(req->format); - int cmd = property_format_to_set_cmd(req->format); - if (cmd < 0) { - req->status = cmd; - } else { - int err = mp_property_do(req->name, cmd, req->data, req->mpctx); - req->status = translate_property_error(err); + struct mpv_node node; + node.format = req->format; + + req->status = 0; + switch (req->format) { + case MPV_FORMAT_NODE: + node = *(struct mpv_node *)req->data; + break; + case MPV_FORMAT_STRING: + case MPV_FORMAT_FLAG: + case MPV_FORMAT_INT64: + case MPV_FORMAT_DOUBLE: + // These are basically emulated via mpv_node. + memcpy(&node.u, req->data, type->type->size); + break; + default: + abort(); } + int err = mp_property_do(req->name, M_PROPERTY_SET_NODE, &node, req->mpctx); + if (err == M_PROPERTY_NOT_IMPLEMENTED && req->format == MPV_FORMAT_STRING) { + // Go through explicit string conversion. M_PROPERTY_SET_NODE doesn't + // do this, because it tries to be somewhat type-strict. But the client + // needs a way to set everything by string. + err = mp_property_do(req->name, M_PROPERTY_SET_STRING, node.u.string, + req->mpctx); + } + + req->status = translate_property_error(err); + if (req->reply_ctx) { status_reply(req->reply_ctx, MPV_EVENT_SET_PROPERTY_REPLY, req->userdata, req->status); @@ -630,6 +694,8 @@ int mpv_set_property(mpv_handle *ctx, const char *name, mpv_format format, { if (!ctx->mpctx->initialized) return MPV_ERROR_UNINITIALIZED; + if (!get_mp_type(format)) + return MPV_ERROR_PROPERTY_FORMAT; struct setproperty_request req = { .mpctx = ctx->mpctx, @@ -646,31 +712,36 @@ int mpv_set_property_string(mpv_handle *ctx, const char *name, const char *data) return mpv_set_property(ctx, name, MPV_FORMAT_STRING, &data); } +static void free_prop_set_req(void *ptr) +{ + struct setproperty_request *req = ptr; + const struct m_option *type = get_mp_type(req->format); + m_option_free(type, req->data); +} + int mpv_set_property_async(mpv_handle *ctx, uint64_t ud, const char *name, mpv_format format, void *data) { + const struct m_option *type = get_mp_type(format); if (!ctx->mpctx->initialized) return MPV_ERROR_UNINITIALIZED; + if (!type) + return MPV_ERROR_PROPERTY_FORMAT; struct setproperty_request *req = talloc_ptrtype(NULL, req); *req = (struct setproperty_request){ .mpctx = ctx->mpctx, .name = talloc_strdup(req, name), - .format = MPV_FORMAT_STRING, - .data = talloc_strdup(req, *(char **)data), // for now always a string + .format = format, + .data = talloc_zero_size(req, type->type->size), .reply_ctx = ctx, .userdata = ud, }; - return run_async(ctx, setproperty_fn, req); -} -static int property_format_to_get_cmd(int format) -{ - switch (format) { - case MPV_FORMAT_STRING: return M_PROPERTY_GET_STRING; - case MPV_FORMAT_OSD_STRING: return M_PROPERTY_PRINT; - default: return MPV_ERROR_PROPERTY_FORMAT; - } + m_option_copy(type, req->data, data); + talloc_set_destructor(req, free_prop_set_req); + + return run_async(ctx, setproperty_fn, req); } struct getproperty_request { @@ -683,28 +754,76 @@ struct getproperty_request { uint64_t userdata; }; +static void free_prop_data(void *ptr) +{ + struct mpv_event_property *prop = ptr; + const struct m_option *type = get_mp_type_get(prop->format); + m_option_free(type, prop->data); +} + static void getproperty_fn(void *arg) { struct getproperty_request *req = arg; + const struct m_option *type = get_mp_type_get(req->format); - char *xdata = NULL; // currently, we support strings only + union m_option_value xdata = {0}; void *data = req->data ? req->data : &xdata; - int cmd = property_format_to_get_cmd(req->format); - if (cmd < 0) { - req->status = cmd; - } else { - int err = mp_property_do(req->name, cmd, data, req->mpctx); - req->status = translate_property_error(err); + int err = -1; + switch (req->format) { + case MPV_FORMAT_OSD_STRING: + err = mp_property_do(req->name, M_PROPERTY_PRINT, data, req->mpctx); + break; + case MPV_FORMAT_STRING: { + char *s = NULL; + err = mp_property_do(req->name, M_PROPERTY_GET_STRING, &s, req->mpctx); + if (err == M_PROPERTY_OK) + *(char **)req->data = s; + break; } + case MPV_FORMAT_NODE: + case MPV_FORMAT_FLAG: + case MPV_FORMAT_INT64: + case MPV_FORMAT_DOUBLE: { + struct mpv_node node = {{0}}; + err = mp_property_do(req->name, M_PROPERTY_GET_NODE, &node, req->mpctx); + if (err == M_PROPERTY_NOT_IMPLEMENTED) { + // Go through explicit string conversion. Same reasoning as on the + // GET code path. + char *s = NULL; + err = mp_property_do(req->name, M_PROPERTY_GET_STRING, &s, + req->mpctx); + if (err != M_PROPERTY_OK) + break; + node.format = MPV_FORMAT_STRING; + node.u.string = s; + } + if (req->format == MPV_FORMAT_NODE) { + *(struct mpv_node *)data = node; + } else { + if (!conv_node_to_format(data, req->format, &node)) { + err = M_PROPERTY_INVALID_FORMAT; + mpv_free_node_contents(&node); + } + } + break; + } + default: + abort(); + } + + req->status = translate_property_error(err); if (req->reply_ctx) { struct mpv_event_property *prop = talloc_ptrtype(NULL, prop); *prop = (struct mpv_event_property){ .name = talloc_steal(prop, (char *)req->name), .format = req->format, - .data = talloc_steal(prop, xdata), + .data = talloc_size(prop, type->type->size), }; + // move data + memcpy(prop->data, &xdata, type->type->size); + talloc_set_destructor(prop, free_prop_data); struct mpv_event reply = { .event_id = MPV_EVENT_GET_PROPERTY_REPLY, .data = prop, @@ -720,6 +839,10 @@ int mpv_get_property(mpv_handle *ctx, const char *name, mpv_format format, { if (!ctx->mpctx->initialized) return MPV_ERROR_UNINITIALIZED; + if (!data) + return MPV_ERROR_INVALID_PARAMETER; + if (!get_mp_type_get(format)) + return MPV_ERROR_PROPERTY_FORMAT; struct getproperty_request req = { .mpctx = ctx->mpctx, @@ -750,6 +873,8 @@ int mpv_get_property_async(mpv_handle *ctx, uint64_t ud, const char *name, { if (!ctx->mpctx->initialized) return MPV_ERROR_UNINITIALIZED; + if (!get_mp_type_get(format)) + return MPV_ERROR_PROPERTY_FORMAT; struct getproperty_request *req = talloc_ptrtype(NULL, req); *req = (struct getproperty_request){ -- cgit v1.2.3