mirror of https://github.com/mpv-player/mpv
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.
This commit is contained in:
parent
91f752f43f
commit
62653605da
191
player/client.c
191
player/client.c
|
@ -441,6 +441,54 @@ void mpv_wakeup(mpv_handle *ctx)
|
||||||
pthread_mutex_unlock(&ctx->lock);
|
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,
|
int mpv_set_option(mpv_handle *ctx, const char *name, mpv_format format,
|
||||||
void *data)
|
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_UNAVAILABLE: return MPV_ERROR_PROPERTY_UNAVAILABLE;
|
||||||
case M_PROPERTY_NOT_IMPLEMENTED: return MPV_ERROR_PROPERTY_ERROR;
|
case M_PROPERTY_NOT_IMPLEMENTED: return MPV_ERROR_PROPERTY_ERROR;
|
||||||
case M_PROPERTY_UNKNOWN: return MPV_ERROR_PROPERTY_NOT_FOUND;
|
case M_PROPERTY_UNKNOWN: return MPV_ERROR_PROPERTY_NOT_FOUND;
|
||||||
|
case M_PROPERTY_INVALID_FORMAT: return MPV_ERROR_PROPERTY_FORMAT;
|
||||||
// shouldn't happen
|
// shouldn't happen
|
||||||
default: return MPV_ERROR_PROPERTY_ERROR;
|
default: return MPV_ERROR_PROPERTY_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -599,26 +648,41 @@ struct setproperty_request {
|
||||||
uint64_t userdata;
|
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)
|
static void setproperty_fn(void *arg)
|
||||||
{
|
{
|
||||||
struct setproperty_request *req = 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);
|
struct mpv_node node;
|
||||||
if (cmd < 0) {
|
node.format = req->format;
|
||||||
req->status = cmd;
|
|
||||||
} else {
|
req->status = 0;
|
||||||
int err = mp_property_do(req->name, cmd, req->data, req->mpctx);
|
switch (req->format) {
|
||||||
req->status = translate_property_error(err);
|
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) {
|
if (req->reply_ctx) {
|
||||||
status_reply(req->reply_ctx, MPV_EVENT_SET_PROPERTY_REPLY,
|
status_reply(req->reply_ctx, MPV_EVENT_SET_PROPERTY_REPLY,
|
||||||
req->userdata, req->status);
|
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)
|
if (!ctx->mpctx->initialized)
|
||||||
return MPV_ERROR_UNINITIALIZED;
|
return MPV_ERROR_UNINITIALIZED;
|
||||||
|
if (!get_mp_type(format))
|
||||||
|
return MPV_ERROR_PROPERTY_FORMAT;
|
||||||
|
|
||||||
struct setproperty_request req = {
|
struct setproperty_request req = {
|
||||||
.mpctx = ctx->mpctx,
|
.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);
|
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,
|
int mpv_set_property_async(mpv_handle *ctx, uint64_t ud, const char *name,
|
||||||
mpv_format format, void *data)
|
mpv_format format, void *data)
|
||||||
{
|
{
|
||||||
|
const struct m_option *type = get_mp_type(format);
|
||||||
if (!ctx->mpctx->initialized)
|
if (!ctx->mpctx->initialized)
|
||||||
return MPV_ERROR_UNINITIALIZED;
|
return MPV_ERROR_UNINITIALIZED;
|
||||||
|
if (!type)
|
||||||
|
return MPV_ERROR_PROPERTY_FORMAT;
|
||||||
|
|
||||||
struct setproperty_request *req = talloc_ptrtype(NULL, req);
|
struct setproperty_request *req = talloc_ptrtype(NULL, req);
|
||||||
*req = (struct setproperty_request){
|
*req = (struct setproperty_request){
|
||||||
.mpctx = ctx->mpctx,
|
.mpctx = ctx->mpctx,
|
||||||
.name = talloc_strdup(req, name),
|
.name = talloc_strdup(req, name),
|
||||||
.format = MPV_FORMAT_STRING,
|
.format = format,
|
||||||
.data = talloc_strdup(req, *(char **)data), // for now always a string
|
.data = talloc_zero_size(req, type->type->size),
|
||||||
.reply_ctx = ctx,
|
.reply_ctx = ctx,
|
||||||
.userdata = ud,
|
.userdata = ud,
|
||||||
};
|
};
|
||||||
return run_async(ctx, setproperty_fn, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int property_format_to_get_cmd(int format)
|
m_option_copy(type, req->data, data);
|
||||||
{
|
talloc_set_destructor(req, free_prop_set_req);
|
||||||
switch (format) {
|
|
||||||
case MPV_FORMAT_STRING: return M_PROPERTY_GET_STRING;
|
return run_async(ctx, setproperty_fn, req);
|
||||||
case MPV_FORMAT_OSD_STRING: return M_PROPERTY_PRINT;
|
|
||||||
default: return MPV_ERROR_PROPERTY_FORMAT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct getproperty_request {
|
struct getproperty_request {
|
||||||
|
@ -683,28 +754,76 @@ struct getproperty_request {
|
||||||
uint64_t userdata;
|
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)
|
static void getproperty_fn(void *arg)
|
||||||
{
|
{
|
||||||
struct getproperty_request *req = 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;
|
void *data = req->data ? req->data : &xdata;
|
||||||
|
|
||||||
int cmd = property_format_to_get_cmd(req->format);
|
int err = -1;
|
||||||
if (cmd < 0) {
|
switch (req->format) {
|
||||||
req->status = cmd;
|
case MPV_FORMAT_OSD_STRING:
|
||||||
} else {
|
err = mp_property_do(req->name, M_PROPERTY_PRINT, data, req->mpctx);
|
||||||
int err = mp_property_do(req->name, cmd, data, req->mpctx);
|
break;
|
||||||
req->status = translate_property_error(err);
|
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) {
|
if (req->reply_ctx) {
|
||||||
struct mpv_event_property *prop = talloc_ptrtype(NULL, prop);
|
struct mpv_event_property *prop = talloc_ptrtype(NULL, prop);
|
||||||
*prop = (struct mpv_event_property){
|
*prop = (struct mpv_event_property){
|
||||||
.name = talloc_steal(prop, (char *)req->name),
|
.name = talloc_steal(prop, (char *)req->name),
|
||||||
.format = req->format,
|
.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 = {
|
struct mpv_event reply = {
|
||||||
.event_id = MPV_EVENT_GET_PROPERTY_REPLY,
|
.event_id = MPV_EVENT_GET_PROPERTY_REPLY,
|
||||||
.data = prop,
|
.data = prop,
|
||||||
|
@ -720,6 +839,10 @@ int mpv_get_property(mpv_handle *ctx, const char *name, mpv_format format,
|
||||||
{
|
{
|
||||||
if (!ctx->mpctx->initialized)
|
if (!ctx->mpctx->initialized)
|
||||||
return MPV_ERROR_UNINITIALIZED;
|
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 = {
|
struct getproperty_request req = {
|
||||||
.mpctx = ctx->mpctx,
|
.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)
|
if (!ctx->mpctx->initialized)
|
||||||
return MPV_ERROR_UNINITIALIZED;
|
return MPV_ERROR_UNINITIALIZED;
|
||||||
|
if (!get_mp_type_get(format))
|
||||||
|
return MPV_ERROR_PROPERTY_FORMAT;
|
||||||
|
|
||||||
struct getproperty_request *req = talloc_ptrtype(NULL, req);
|
struct getproperty_request *req = talloc_ptrtype(NULL, req);
|
||||||
*req = (struct getproperty_request){
|
*req = (struct getproperty_request){
|
||||||
|
|
Loading…
Reference in New Issue