diff options
author | Anton Kindestam <antonki@kth.se> | 2018-12-05 19:02:03 +0100 |
---|---|---|
committer | Anton Kindestam <antonki@kth.se> | 2018-12-05 19:19:24 +0100 |
commit | 8b83c8996686072bc743b112ae5cb3bf93aa33ed (patch) | |
tree | b09ce6a7ff470b05006622f19914b3d39d2f7d9f /player | |
parent | 5bcac8580df6fc62323136f756a3a6d1e754fe9c (diff) | |
parent | 559a400ac36e75a8d73ba263fd7fa6736df1c2da (diff) | |
download | mpv-8b83c8996686072bc743b112ae5cb3bf93aa33ed.tar.bz2 mpv-8b83c8996686072bc743b112ae5cb3bf93aa33ed.tar.xz |
Merge commit '559a400ac36e75a8d73ba263fd7fa6736df1c2da' into wm4-commits--merge-edition
This bumps libmpv version to 1.103
Diffstat (limited to 'player')
-rw-r--r-- | player/client.c | 262 | ||||
-rw-r--r-- | player/client.h | 1 | ||||
-rw-r--r-- | player/command.c | 949 | ||||
-rw-r--r-- | player/command.h | 27 | ||||
-rw-r--r-- | player/core.h | 45 | ||||
-rw-r--r-- | player/external_files.c | 27 | ||||
-rw-r--r-- | player/external_files.h | 4 | ||||
-rw-r--r-- | player/loadfile.c | 355 | ||||
-rw-r--r-- | player/lua.c | 136 | ||||
-rw-r--r-- | player/lua/defaults.lua | 62 | ||||
-rw-r--r-- | player/main.c | 34 | ||||
-rw-r--r-- | player/misc.c | 18 | ||||
-rw-r--r-- | player/osd.c | 40 | ||||
-rw-r--r-- | player/playloop.c | 27 | ||||
-rw-r--r-- | player/screenshot.c | 185 | ||||
-rw-r--r-- | player/screenshot.h | 22 | ||||
-rw-r--r-- | player/sub.c | 6 | ||||
-rw-r--r-- | player/video.c | 3 |
18 files changed, 1332 insertions, 871 deletions
diff --git a/player/client.c b/player/client.c index 2c21dd727c..03e0cbfeb6 100644 --- a/player/client.c +++ b/player/client.c @@ -31,7 +31,9 @@ #include "input/cmd.h" #include "misc/ctype.h" #include "misc/dispatch.h" +#include "misc/node.h" #include "misc/rendezvous.h" +#include "misc/thread_tools.h" #include "options/m_config.h" #include "options/m_option.h" #include "options/m_property.h" @@ -370,6 +372,30 @@ void mpv_wait_async_requests(mpv_handle *ctx) pthread_mutex_unlock(&ctx->lock); } +// Send abort signal to all matching work items. +// If type==0, destroy all of the matching ctx. +// If ctx==0, destroy all. +static void abort_async(struct MPContext *mpctx, mpv_handle *ctx, + int type, uint64_t id) +{ + pthread_mutex_lock(&mpctx->abort_lock); + + // Destroy all => ensure any newly appearing work is aborted immediately. + if (ctx == NULL) + mpctx->abort_all = true; + + for (int n = 0; n < mpctx->num_abort_list; n++) { + struct mp_abort_entry *abort = mpctx->abort_list[n]; + if (!ctx || (abort->client == ctx && (!type || + (abort->client_work_type == type && abort->client_work_id == id)))) + { + mp_abort_trigger_locked(mpctx, abort); + } + } + + pthread_mutex_unlock(&mpctx->abort_lock); +} + static void get_thread(void *ptr) { *(pthread_t *)ptr = pthread_self(); @@ -388,6 +414,8 @@ static void mp_destroy_client(mpv_handle *ctx, bool terminate) if (terminate) mpv_command(ctx, (const char*[]){"quit", NULL}); + abort_async(mpctx, ctx, 0, 0); + // reserved_events equals the number of asynchronous requests that weren't // yet replied. In order to avoid that trying to reply to a removed client // causes a crash, block until all asynchronous requests were served. @@ -483,32 +511,52 @@ void mpv_terminate_destroy(mpv_handle *ctx) mp_destroy_client(ctx, true); } -static bool can_terminate(struct MPContext *mpctx) -{ - struct mp_client_api *clients = mpctx->clients; - - pthread_mutex_lock(&clients->lock); - bool ok = clients->num_clients == 0 && mpctx->outstanding_async == 0 && - (mpctx->is_cli || clients->terminate_core_thread); - pthread_mutex_unlock(&clients->lock); - - return ok; -} - // Can be called on the core thread only. Idempotent. +// Also happens to take care of shutting down any async work. void mp_shutdown_clients(struct MPContext *mpctx) { struct mp_client_api *clients = mpctx->clients; - // Prevent that new clients can appear. + // Forcefully abort async work after 2 seconds of waiting. + double abort_time = mp_time_sec() + 2; + pthread_mutex_lock(&clients->lock); + + // Prevent that new clients can appear. clients->shutting_down = true; - pthread_mutex_unlock(&clients->lock); - while (!can_terminate(mpctx)) { + // Wait until we can terminate. + while (clients->num_clients || mpctx->outstanding_async || + !(mpctx->is_cli || clients->terminate_core_thread)) + { + pthread_mutex_unlock(&clients->lock); + + double left = abort_time - mp_time_sec(); + if (left >= 0) { + mp_set_timeout(mpctx, left); + } else { + // Forcefully abort any ongoing async work. This is quite rude and + // probably not what everyone wants, so it happens only after a + // timeout. + abort_async(mpctx, NULL, 0, 0); + } + mp_client_broadcast_event(mpctx, MPV_EVENT_SHUTDOWN, NULL); mp_wait_events(mpctx); + + pthread_mutex_lock(&clients->lock); } + + pthread_mutex_unlock(&clients->lock); +} + +bool mp_is_shutting_down(struct MPContext *mpctx) +{ + struct mp_client_api *clients = mpctx->clients; + pthread_mutex_lock(&clients->lock); + bool res = clients->shutting_down; + pthread_mutex_unlock(&clients->lock); + return res; } static void *core_thread(void *p) @@ -677,16 +725,6 @@ static void send_reply(struct mpv_handle *ctx, uint64_t userdata, pthread_mutex_unlock(&ctx->lock); } -static void status_reply(struct mpv_handle *ctx, int event, - uint64_t userdata, int status) -{ - struct mpv_event reply = { - .event_id = event, - .error = status, - }; - send_reply(ctx, userdata, &reply); -} - // Return whether there's any client listening to this event. // If false is returned, the core doesn't need to send it. bool mp_client_event_is_registered(struct MPContext *mpctx, int event) @@ -905,54 +943,6 @@ static bool conv_node_to_format(void *dst, mpv_format dst_fmt, mpv_node *src) return false; } -// Note: for MPV_FORMAT_NODE_MAP, this (incorrectly) takes the order into -// account, instead of treating it as set. -static bool compare_value(void *a, void *b, mpv_format format) -{ - switch (format) { - case MPV_FORMAT_NONE: - return true; - case MPV_FORMAT_STRING: - case MPV_FORMAT_OSD_STRING: - return strcmp(*(char **)a, *(char **)b) == 0; - case MPV_FORMAT_FLAG: - return *(int *)a == *(int *)b; - case MPV_FORMAT_INT64: - return *(int64_t *)a == *(int64_t *)b; - case MPV_FORMAT_DOUBLE: - return *(double *)a == *(double *)b; - case MPV_FORMAT_NODE: { - struct mpv_node *a_n = a, *b_n = b; - if (a_n->format != b_n->format) - return false; - return compare_value(&a_n->u, &b_n->u, a_n->format); - } - case MPV_FORMAT_BYTE_ARRAY: { - struct mpv_byte_array *a_r = a, *b_r = b; - if (a_r->size != b_r->size) - return false; - return memcmp(a_r->data, b_r->data, a_r->size) == 0; - } - case MPV_FORMAT_NODE_ARRAY: - case MPV_FORMAT_NODE_MAP: - { - mpv_node_list *l_a = *(mpv_node_list **)a, *l_b = *(mpv_node_list **)b; - if (l_a->num != l_b->num) - return false; - for (int n = 0; n < l_a->num; n++) { - if (!compare_value(&l_a->values[n], &l_b->values[n], MPV_FORMAT_NODE)) - return false; - if (format == MPV_FORMAT_NODE_MAP) { - if (strcmp(l_a->keys[n], l_b->keys[n]) != 0) - return false; - } - } - return true; - } - } - abort(); -} - void mpv_free_node_contents(mpv_node *node) { static const struct m_option type = { .type = CONF_TYPE_NODE }; @@ -1017,29 +1007,30 @@ static int run_async(mpv_handle *ctx, void (*fn)(void *fn_data), void *fn_data) talloc_free(fn_data); return err; } - mp_dispatch_enqueue_autofree(ctx->mpctx->dispatch, fn, fn_data); + mp_dispatch_enqueue(ctx->mpctx->dispatch, fn, fn_data); return 0; } struct cmd_request { struct MPContext *mpctx; struct mp_cmd *cmd; - struct mpv_node *res; int status; - struct mpv_handle *reply_ctx; - uint64_t userdata; + struct mpv_node *res; + struct mp_waiter completion; }; -static void cmd_fn(void *data) +static void cmd_complete(struct mp_cmd_ctx *cmd) { - struct cmd_request *req = data; - int r = run_command(req->mpctx, req->cmd, req->res); - req->status = r >= 0 ? 0 : MPV_ERROR_COMMAND; - talloc_free(req->cmd); - if (req->reply_ctx) { - status_reply(req->reply_ctx, MPV_EVENT_COMMAND_REPLY, - req->userdata, req->status); + struct cmd_request *req = cmd->on_completion_priv; + + req->status = cmd->success ? 0 : MPV_ERROR_COMMAND; + if (req->res) { + *req->res = cmd->result; + cmd->result = (mpv_node){0}; } + + // Unblock the waiting thread (especially for async commands). + mp_waiter_wakeup(&req->completion, 0); } static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd, mpv_node *res) @@ -1049,17 +1040,33 @@ static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd, mpv_node *res if (!cmd) return MPV_ERROR_INVALID_PARAMETER; - if (mp_input_is_abort_cmd(cmd)) - mp_abort_playback_async(ctx->mpctx); - cmd->sender = ctx->name; struct cmd_request req = { .mpctx = ctx->mpctx, .cmd = cmd, .res = res, + .completion = MP_WAITER_INITIALIZER, }; - run_locked(ctx, cmd_fn, &req); + + bool async = cmd->flags & MP_ASYNC_CMD; + + lock_core(ctx); + if (async) { + run_command(ctx->mpctx, cmd, NULL, NULL, NULL); + } else { + struct mp_abort_entry *abort = NULL; + if (cmd->def->can_abort) { + abort = talloc_zero(NULL, struct mp_abort_entry); + abort->client = ctx; + } + run_command(ctx->mpctx, cmd, abort, cmd_complete, &req); + } + unlock_core(ctx); + + if (!async) + mp_waiter_wait(&req.completion); + return req.status; } @@ -1083,7 +1090,54 @@ int mpv_command_string(mpv_handle *ctx, const char *args) mp_input_parse_cmd(ctx->mpctx->input, bstr0((char*)args), ctx->name), NULL); } -static int run_cmd_async(mpv_handle *ctx, uint64_t ud, struct mp_cmd *cmd) +struct async_cmd_request { + struct MPContext *mpctx; + struct mp_cmd *cmd; + struct mpv_handle *reply_ctx; + uint64_t userdata; +}; + +static void async_cmd_complete(struct mp_cmd_ctx *cmd) +{ + struct async_cmd_request *req = cmd->on_completion_priv; + + struct mpv_event_command *data = talloc_zero(NULL, struct mpv_event_command); + data->result = cmd->result; + cmd->result = (mpv_node){0}; + talloc_steal(data, node_get_alloc(&data->result)); + + struct mpv_event reply = { + .event_id = MPV_EVENT_COMMAND_REPLY, + .data = data, + .error = cmd->success ? 0 : MPV_ERROR_COMMAND, + }; + send_reply(req->reply_ctx, req->userdata, &reply); + + talloc_free(req); +} + +static void async_cmd_fn(void *data) +{ + struct async_cmd_request *req = data; + + struct mp_cmd *cmd = req->cmd; + ta_xset_parent(cmd, NULL); + req->cmd = NULL; + + struct mp_abort_entry *abort = NULL; + if (cmd->def->can_abort) { + abort = talloc_zero(NULL, struct mp_abort_entry); + abort->client = req->reply_ctx; + abort->client_work_type = MPV_EVENT_COMMAND_REPLY; + abort->client_work_id = req->userdata; + } + + // This will synchronously or asynchronously call cmd_complete (depending + // on the command). + run_command(req->mpctx, cmd, abort, async_cmd_complete, req); +} + +static int run_async_cmd(mpv_handle *ctx, uint64_t ud, struct mp_cmd *cmd) { if (!ctx->mpctx->initialized) return MPV_ERROR_UNINITIALIZED; @@ -1092,24 +1146,29 @@ static int run_cmd_async(mpv_handle *ctx, uint64_t ud, struct mp_cmd *cmd) cmd->sender = ctx->name; - struct cmd_request *req = talloc_ptrtype(NULL, req); - *req = (struct cmd_request){ + struct async_cmd_request *req = talloc_ptrtype(NULL, req); + *req = (struct async_cmd_request){ .mpctx = ctx->mpctx, - .cmd = cmd, + .cmd = talloc_steal(req, cmd), .reply_ctx = ctx, .userdata = ud, }; - return run_async(ctx, cmd_fn, req); + return run_async(ctx, async_cmd_fn, req); } int mpv_command_async(mpv_handle *ctx, uint64_t ud, const char **args) { - return run_cmd_async(ctx, ud, mp_input_parse_cmd_strv(ctx->log, args)); + return run_async_cmd(ctx, ud, mp_input_parse_cmd_strv(ctx->log, args)); } int mpv_command_node_async(mpv_handle *ctx, uint64_t ud, mpv_node *args) { - return run_cmd_async(ctx, ud, mp_input_parse_cmd_node(ctx->log, args)); + return run_async_cmd(ctx, ud, mp_input_parse_cmd_node(ctx->log, args)); +} + +void mpv_abort_async_command(mpv_handle *ctx, uint64_t reply_userdata) +{ + abort_async(ctx->mpctx, ctx, MPV_EVENT_COMMAND_REPLY, reply_userdata); } static int translate_property_error(int errc) @@ -1156,8 +1215,12 @@ static void setproperty_fn(void *arg) req->status = translate_property_error(err); if (req->reply_ctx) { - status_reply(req->reply_ctx, MPV_EVENT_SET_PROPERTY_REPLY, - req->userdata, req->status); + struct mpv_event reply = { + .event_id = MPV_EVENT_SET_PROPERTY_REPLY, + .error = req->status, + }; + send_reply(req->reply_ctx, req->userdata, &reply); + talloc_free(req); } } @@ -1313,6 +1376,7 @@ static void getproperty_fn(void *arg) .error = req->status, }; send_reply(req->reply_ctx, req->userdata, &reply); + talloc_free(req); } } @@ -1508,7 +1572,7 @@ static void update_prop(void *p) if (prop->user_value_valid != prop->new_value_valid) { prop->changed = true; } else if (prop->user_value_valid && prop->new_value_valid) { - if (!compare_value(&prop->user_value, &prop->new_value, prop->format)) + if (!equal_mpv_value(&prop->user_value, &prop->new_value, prop->format)) prop->changed = true; } if (prop->dead) diff --git a/player/client.h b/player/client.h index 2512315d42..7426e94372 100644 --- a/player/client.h +++ b/player/client.h @@ -20,6 +20,7 @@ struct mpv_global; void mp_clients_init(struct MPContext *mpctx); void mp_clients_destroy(struct MPContext *mpctx); void mp_shutdown_clients(struct MPContext *mpctx); +bool mp_is_shutting_down(struct MPContext *mpctx); bool mp_clients_all_initialized(struct MPContext *mpctx); bool mp_client_exists(struct MPContext *mpctx, const char *client_name); diff --git a/player/command.c b/player/command.c index 7237b08820..30629eae54 100644 --- a/player/command.c +++ b/player/command.c @@ -60,7 +60,9 @@ #include "video/out/bitmap_packer.h" #include "options/path.h" #include "screenshot.h" +#include "misc/dispatch.h" #include "misc/node.h" +#include "misc/thread_pool.h" #include "osdep/io.h" #include "osdep/subprocess.h" @@ -451,10 +453,10 @@ static int mp_property_playback_speed(void *ctx, struct m_property *prop, double speed = mpctx->opts->playback_speed; switch (action) { case M_PROPERTY_SET: { - mpctx->opts->playback_speed = *(double *)arg; + int r = mp_property_generic_option(mpctx, prop, action, arg); update_playback_speed(mpctx); mp_wakeup_core(mpctx); - return M_PROPERTY_OK; + return r; } case M_PROPERTY_PRINT: *(char **)arg = talloc_asprintf(NULL, "%.2f", speed); @@ -905,7 +907,7 @@ static int mp_property_disc_title(void *ctx, struct m_property *prop, { MPContext *mpctx = ctx; struct demuxer *d = mpctx->demuxer; - if (!d) + if (!d || !d->extended_ctrls) return M_PROPERTY_UNAVAILABLE; unsigned int title = -1; switch (action) { @@ -1141,11 +1143,10 @@ static int mp_property_edition(void *ctx, struct m_property *prop, case M_PROPERTY_SET: { edition = *(int *)arg; if (edition != demuxer->edition) { - mpctx->opts->edition_id = edition; if (!mpctx->stop_play) mpctx->stop_play = PT_CURRENT_ENTRY; mp_wakeup_core(mpctx); - break; // make it accessible to the demuxer via option change notify + break; // write value, trigger option change notify } return M_PROPERTY_OK; } @@ -1222,8 +1223,9 @@ static int mp_property_disc_titles(void *ctx, struct m_property *prop, MPContext *mpctx = ctx; struct demuxer *demuxer = mpctx->demuxer; unsigned int num_titles; - if (!demuxer || demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_TITLES, - &num_titles) < 1) + if (!demuxer || !demuxer->extended_ctrls || + demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_TITLES, + &num_titles) < 1) return M_PROPERTY_UNAVAILABLE; return m_property_int_ro(action, arg, num_titles); } @@ -1253,8 +1255,9 @@ static int mp_property_list_disc_titles(void *ctx, struct m_property *prop, MPContext *mpctx = ctx; struct demuxer *demuxer = mpctx->demuxer; unsigned int num_titles; - if (!demuxer || demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_TITLES, - &num_titles) < 1) + if (!demuxer || !demuxer->extended_ctrls || + demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_TITLES, + &num_titles) < 1) return M_PROPERTY_UNAVAILABLE; return m_property_read_list(action, arg, num_titles, get_disc_title_entry, mpctx); @@ -1289,7 +1292,7 @@ static int mp_property_angle(void *ctx, struct m_property *prop, { MPContext *mpctx = ctx; struct demuxer *demuxer = mpctx->demuxer; - if (!demuxer) + if (!demuxer || !demuxer->extended_ctrls) return M_PROPERTY_UNAVAILABLE; int ris, angles = -1, angle = 1; @@ -1556,134 +1559,6 @@ static int mp_property_playback_abort(void *ctx, struct m_property *prop, return m_property_flag_ro(action, arg, !mpctx->playing || mpctx->stop_play); } -static int mp_property_cache(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - float cache = mp_get_cache_percent(mpctx); - if (cache < 0) - return M_PROPERTY_UNAVAILABLE; - - if (action == M_PROPERTY_PRINT) { - *(char **)arg = talloc_asprintf(NULL, "%d", (int)cache); - return M_PROPERTY_OK; - } - - return m_property_float_ro(action, arg, cache); -} - -static int property_int_kb_size(int kb_size, int action, void *arg) -{ - switch (action) { - case M_PROPERTY_GET: - *(int *)arg = kb_size; - return M_PROPERTY_OK; - case M_PROPERTY_PRINT: - *(char **)arg = format_file_size(kb_size * 1024LL); - return M_PROPERTY_OK; - case M_PROPERTY_GET_TYPE: - *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_INT}; - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static int mp_property_cache_size(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - struct demuxer *demuxer = mpctx->demuxer; - if (!demuxer) - return M_PROPERTY_UNAVAILABLE; - switch (action) { - case M_PROPERTY_GET: - case M_PROPERTY_PRINT: { - struct stream_cache_info info = {0}; - demux_stream_control(demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size <= 0) - break; - return property_int_kb_size(info.size / 1024, action, arg); - } - case M_PROPERTY_GET_TYPE: - *(struct m_option *)arg = (struct m_option){ - .type = CONF_TYPE_INT, - .flags = M_OPT_MIN, - .min = 0, - }; - return M_PROPERTY_OK; - case M_PROPERTY_SET: { - int64_t size = *(int *)arg * 1024LL; - int r = demux_stream_control(demuxer, STREAM_CTRL_SET_CACHE_SIZE, &size); - if (r == STREAM_UNSUPPORTED) - break; - if (r == STREAM_OK) - return M_PROPERTY_OK; - return M_PROPERTY_ERROR; - } - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - -static int mp_property_cache_used(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - if (!mpctx->demuxer) - return M_PROPERTY_UNAVAILABLE; - - struct stream_cache_info info = {0}; - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size <= 0) - return M_PROPERTY_UNAVAILABLE; - return property_int_kb_size(info.fill / 1024, action, arg); -} - -static int mp_property_cache_free(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - if (!mpctx->demuxer) - return M_PROPERTY_UNAVAILABLE; - - struct stream_cache_info info = {0}; - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size <= 0) - return M_PROPERTY_UNAVAILABLE; - - return property_int_kb_size((info.size - info.fill) / 1024, action, arg); -} - -static int mp_property_cache_speed(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - if (!mpctx->demuxer) - return M_PROPERTY_UNAVAILABLE; - - struct stream_cache_info info = {0}; - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size <= 0) - return M_PROPERTY_UNAVAILABLE; - - if (action == M_PROPERTY_PRINT) { - *(char **)arg = talloc_strdup_append(format_file_size(info.speed), "/s"); - return M_PROPERTY_OK; - } - return m_property_int64_ro(action, arg, info.speed); -} - -static int mp_property_cache_idle(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - struct stream_cache_info info = {0}; - if (mpctx->demuxer) - demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_CACHE_INFO, &info); - if (info.size <= 0) - return M_PROPERTY_UNAVAILABLE; - return m_property_flag_ro(action, arg, info.idle); -} - static int mp_property_demuxer_cache_duration(void *ctx, struct m_property *prop, int action, void *arg) { @@ -2004,14 +1879,21 @@ static int mp_property_audio_device(void *ctx, struct m_property *prop, if (action == M_PROPERTY_PRINT) { create_hotplug(mpctx); + char *name = NULL; + if (mp_property_generic_option(mpctx, prop, M_PROPERTY_GET, &name) < 1) + name = NULL; + struct ao_device_list *list = ao_hotplug_get_device_list(cmd->hotplug); for (int n = 0; n < list->num_devices; n++) { struct ao_device_desc *dev = &list->devices[n]; - if (dev->name && strcmp(dev->name, mpctx->opts->audio_device) == 0) { + if (dev->name && name && strcmp(dev->name, name) == 0) { *(char **)arg = talloc_strdup(NULL, dev->desc ? dev->desc : "?"); + talloc_free(name); return M_PROPERTY_OK; } } + + talloc_free(name); } return mp_property_generic_option(mpctx, prop, action, arg); } @@ -2045,12 +1927,13 @@ static int mp_property_audio_delay(void *ctx, struct m_property *prop, case M_PROPERTY_PRINT: *(char **)arg = format_delay(delay); return M_PROPERTY_OK; - case M_PROPERTY_SET: - mpctx->opts->audio_delay = *(float *)arg; + case M_PROPERTY_SET: { + int r = mp_property_generic_option(mpctx, prop, action, arg); if (mpctx->ao_chain && mpctx->vo_chain) mpctx->delay += mpctx->opts->audio_delay - delay; mp_wakeup_core(mpctx); - return M_PROPERTY_OK; + return r; + } } return mp_property_generic_option(mpctx, prop, action, arg); } @@ -2414,18 +2297,20 @@ static int mp_property_hwdec(void *ctx, struct m_property *prop, MPContext *mpctx = ctx; struct track *track = mpctx->current_track[0][STREAM_VIDEO]; struct mp_decoder_wrapper *dec = track ? track->dec : NULL; - struct MPOpts *opts = mpctx->opts; if (action == M_PROPERTY_SET) { char *new = *(char **)arg; + char *old = NULL; + if (mp_property_generic_option(mpctx, prop, M_PROPERTY_GET, &old) < 1) + old = NULL; - if (strcmp(opts->hwdec_api, new) == 0) - return M_PROPERTY_OK; + bool same = bstr_equals(bstr0(old), bstr0(new)); + + mp_property_generic_option(mpctx, prop, M_PROPERTY_SET, &new); - talloc_free(opts->hwdec_api); - opts->hwdec_api = talloc_strdup(NULL, new); + talloc_free(old); - if (!dec) + if (!dec || same) return M_PROPERTY_OK; mp_decoder_wrapper_control(dec, VDCTRL_REINIT, NULL); @@ -3117,7 +3002,7 @@ static int mp_property_cursor_autohide(void *ctx, struct m_property *prop, static int prop_stream_ctrl(struct MPContext *mpctx, int ctrl, void *arg) { - if (!mpctx->demuxer) + if (!mpctx->demuxer || !mpctx->demuxer->extended_ctrls) return M_PROPERTY_UNAVAILABLE; int r = demux_stream_control(mpctx->demuxer, ctrl, arg); switch (r) { @@ -3730,12 +3615,13 @@ static int mp_property_option_info(void *ctx, struct m_property *prop, struct m_config_option *co = m_config_get_co(mpctx->mconfig, key); if (!co) return M_PROPERTY_UNKNOWN; + const struct m_option *opt = co->opt; union m_option_value def = {0}; - if (co->default_data) - memcpy(&def, co->default_data, co->opt->type->size); + const void *def_ptr = m_config_get_co_default(mpctx->mconfig, co); + if (def_ptr && opt->type->size > 0) + memcpy(&def, def_ptr, opt->type->size); - const struct m_option *opt = co->opt; bool has_minmax = opt->type == &m_option_type_int || opt->type == &m_option_type_int64 || @@ -3885,12 +3771,6 @@ static const struct m_property mp_properties_base[] = { {"eof-reached", mp_property_eof_reached}, {"seeking", mp_property_seeking}, {"playback-abort", mp_property_playback_abort}, - {"cache-percent", mp_property_cache}, - {"cache-free", mp_property_cache_free}, - {"cache-used", mp_property_cache_used}, - {"cache-size", mp_property_cache_size}, - {"cache-idle", mp_property_cache_idle}, - {"cache-speed", mp_property_cache_speed}, {"demuxer-cache-duration", mp_property_demuxer_cache_duration}, {"demuxer-cache-time", mp_property_demuxer_cache_time}, {"demuxer-cache-idle", mp_property_demuxer_cache_idle}, @@ -4827,19 +4707,165 @@ static void cmd_cycle_values(void *p) change_property_cmd(cmd, name, M_PROPERTY_SET_STRING, cmd->args[current].v.s); } -int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *res) +struct cmd_list_ctx { + struct MPContext *mpctx; + + // actual list command + struct mp_cmd_ctx *parent; + + bool current_valid; + pthread_t current; + bool completed_recursive; + + // list of sub commands yet to run + struct mp_cmd **sub; + int num_sub; +}; + +static void continue_cmd_list(struct cmd_list_ctx *list); + +static void on_cmd_list_sub_completion(struct mp_cmd_ctx *cmd) +{ + struct cmd_list_ctx *list = cmd->on_completion_priv; + + if (list->current_valid && pthread_equal(list->current, pthread_self())) { + list->completed_recursive = true; + } else { + continue_cmd_list(list); + } +} + +static void continue_cmd_list(struct cmd_list_ctx *list) +{ + while (list->parent->args[0].v.p) { + struct mp_cmd *sub = list->parent->args[0].v.p; + list->parent->args[0].v.p = sub->queue_next; + + ta_xset_parent(sub, NULL); + + if (sub->flags & MP_ASYNC_CMD) { + // We run it "detached" (fire & forget) + run_command(list->mpctx, sub, NULL, NULL, NULL); + } else { + // Run the next command once this one completes. + + list->completed_recursive = false; + list->current_valid = true; + list->current = pthread_self(); + + run_command(list->mpctx, sub, NULL, on_cmd_list_sub_completion, list); + + list->current_valid = false; + + // run_command() either recursively calls the completion function, + // or lets the command continue run in the background. If it was + // completed recursively, we can just continue our loop. Otherwise + // the completion handler will invoke this loop again elsewhere. + // We could unconditionally call continue_cmd_list() in the handler + // instead, but then stack depth would grow with list length. + if (!list->completed_recursive) + return; + } + } + + mp_cmd_ctx_complete(list->parent); + talloc_free(list); +} + +static void cmd_list(void *p) { - struct mpv_node dummy_node = {0}; - struct mp_cmd_ctx *ctx = &(struct mp_cmd_ctx){ + struct mp_cmd_ctx *cmd = p; + + cmd->completed = false; + + struct cmd_list_ctx *list = talloc_zero(NULL, struct cmd_list_ctx); + list->mpctx = cmd->mpctx; + list->parent = p; + + continue_cmd_list(list); +} + +const struct mp_cmd_def mp_cmd_list = { "list", cmd_list, .exec_async = true }; + +// Signal that the command is complete now. This also deallocates cmd. +// You must call this function in a state where the core is locked for the +// current thread (e.g. from the main thread, or from within mp_dispatch_lock()). +// Completion means the command is finished, even if it errored or never ran. +// Keep in mind that calling this can execute further user command that can +// change arbitrary state (due to cmd_list). +void mp_cmd_ctx_complete(struct mp_cmd_ctx *cmd) +{ + cmd->completed = true; + if (!cmd->success) + mpv_free_node_contents(&cmd->result); + if (cmd->on_completion) + cmd->on_completion(cmd); + if (cmd->abort) + mp_abort_remove(cmd->mpctx, cmd->abort); + mpv_free_node_contents(&cmd->result); + talloc_free(cmd); +} + +static void run_command_on_worker_thread(void *p) +{ + struct mp_cmd_ctx *ctx = p; + struct MPContext *mpctx = ctx->mpctx; + + mp_core_lock(mpctx); + + bool exec_async = ctx->cmd->def->exec_async; + ctx->cmd->def->handler(ctx); + if (!exec_async) + mp_cmd_ctx_complete(ctx); + + mpctx->outstanding_async -= 1; + if (!mpctx->outstanding_async && mp_is_shutting_down(mpctx)) + mp_wakeup_core(mpctx); + + mp_core_unlock(mpctx); +} + +// Run the given command. Upon command completion, on_completion is called. This< |