summaryrefslogtreecommitdiffstats
path: root/player
diff options
context:
space:
mode:
authorAnton Kindestam <antonki@kth.se>2018-12-05 19:02:03 +0100
committerAnton Kindestam <antonki@kth.se>2018-12-05 19:19:24 +0100
commit8b83c8996686072bc743b112ae5cb3bf93aa33ed (patch)
treeb09ce6a7ff470b05006622f19914b3d39d2f7d9f /player
parent5bcac8580df6fc62323136f756a3a6d1e754fe9c (diff)
parent559a400ac36e75a8d73ba263fd7fa6736df1c2da (diff)
downloadmpv-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.c262
-rw-r--r--player/client.h1
-rw-r--r--player/command.c949
-rw-r--r--player/command.h27
-rw-r--r--player/core.h45
-rw-r--r--player/external_files.c27
-rw-r--r--player/external_files.h4
-rw-r--r--player/loadfile.c355
-rw-r--r--player/lua.c136
-rw-r--r--player/lua/defaults.lua62
-rw-r--r--player/main.c34
-rw-r--r--player/misc.c18
-rw-r--r--player/osd.c40
-rw-r--r--player/playloop.c27
-rw-r--r--player/screenshot.c185
-rw-r--r--player/screenshot.h22
-rw-r--r--player/sub.c6
-rw-r--r--player/video.c3
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
+// can happen within the function, or for async commands, some time after the