summaryrefslogtreecommitdiffstats
path: root/player
diff options
context:
space:
mode:
Diffstat (limited to 'player')
-rw-r--r--player/client.c36
-rw-r--r--player/command.c22
-rw-r--r--player/command.h3
-rw-r--r--player/core.h22
-rw-r--r--player/loadfile.c51
-rw-r--r--player/main.c2
-rw-r--r--player/playloop.c2
-rw-r--r--player/screenshot.c2
8 files changed, 131 insertions, 9 deletions
diff --git a/player/client.c b/player/client.c
index b6282a1134..31339f47a3 100644
--- a/player/client.c
+++ b/player/client.c
@@ -1063,9 +1063,14 @@ static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd, mpv_node *res
lock_core(ctx);
if (async) {
- run_command(ctx->mpctx, req.cmd, NULL, NULL);
+ run_command(ctx->mpctx, cmd, NULL, NULL, NULL);
} else {
- run_command(ctx->mpctx, req.cmd, cmd_complete, &req);
+ 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);
@@ -1129,9 +1134,17 @@ static void async_cmd_fn(void *data)
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, async_cmd_complete, req);
+ 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)
@@ -1163,6 +1176,23 @@ int mpv_command_node_async(mpv_handle *ctx, uint64_t ud, mpv_node *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)
+{
+ struct MPContext *mpctx = ctx->mpctx;
+
+ pthread_mutex_lock(&mpctx->abort_lock);
+ for (int n = 0; n < mpctx->num_abort_list; n++) {
+ struct mp_abort_entry *abort = mpctx->abort_list[n];
+ if (abort->client == ctx &&
+ abort->client_work_type == MPV_EVENT_COMMAND_REPLY &&
+ abort->client_work_id == reply_userdata)
+ {
+ mp_abort_trigger_locked(mpctx, abort);
+ }
+ }
+ pthread_mutex_unlock(&mpctx->abort_lock);
+}
+
static int translate_property_error(int errc)
{
switch (errc) {
diff --git a/player/command.c b/player/command.c
index f2d7eedd5f..cd623c03f4 100644
--- a/player/command.c
+++ b/player/command.c
@@ -4865,7 +4865,7 @@ static void continue_cmd_list(struct cmd_list_ctx *list)
if (sub->flags & MP_ASYNC_CMD) {
// We run it "detached" (fire & forget)
- run_command(list->mpctx, sub, NULL, NULL);
+ run_command(list->mpctx, sub, NULL, NULL, NULL);
} else {
// Run the next command once this one completes.
@@ -4873,7 +4873,7 @@ static void continue_cmd_list(struct cmd_list_ctx *list)
list->current_valid = true;
list->current = pthread_self();
- run_command(list->mpctx, sub, on_cmd_list_sub_completion, list);
+ run_command(list->mpctx, sub, NULL, on_cmd_list_sub_completion, list);
list->current_valid = false;
@@ -4920,8 +4920,9 @@ void mp_cmd_ctx_complete(struct mp_cmd_ctx *cmd)
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->cmd);
talloc_free(cmd);
}
@@ -4949,27 +4950,40 @@ static void run_command_on_worker_thread(void *p)
// function returns (the caller is supposed to be able to handle both cases). In
// both cases, the callback will be called while the core is locked (i.e. you
// can access the core freely).
+// If abort is non-NULL, then the caller creates the abort object. It must have
+// been allocated with talloc. run_command() will register/unregister/destroy
+// it. Must not be set if cmd->def->can_abort==false.
// on_completion_priv is copied to mp_cmd_ctx.on_completion_priv and can be
// accessed from the completion callback.
// The completion callback is invoked exactly once. If it's NULL, it's ignored.
// Ownership of cmd goes to the caller.
void run_command(struct MPContext *mpctx, struct mp_cmd *cmd,
+ struct mp_abort_entry *abort,
void (*on_completion)(struct mp_cmd_ctx *cmd),
void *on_completion_priv)
{
struct mp_cmd_ctx *ctx = talloc(NULL, struct mp_cmd_ctx);
*ctx = (struct mp_cmd_ctx){
.mpctx = mpctx,
- .cmd = cmd,
+ .cmd = talloc_steal(ctx, cmd),
.args = cmd->args,
.num_args = cmd->nargs,
.priv = cmd->def->priv,
+ .abort = talloc_steal(ctx, abort),
.success = true,
.completed = true,
.on_completion = on_completion,
.on_completion_priv = on_completion_priv,
};
+ if (!ctx->abort && cmd->def->can_abort)
+ ctx->abort = talloc_zero(ctx, struct mp_abort_entry);
+
+ assert(cmd->def->can_abort == !!ctx->abort);
+
+ if (ctx->abort)
+ mp_abort_add(mpctx, ctx->abort);
+
struct MPOpts *opts = mpctx->opts;
ctx->on_osd = cmd->flags & MP_ON_OSD_FLAGS;
bool auto_osd = ctx->on_osd == MP_ON_OSD_AUTO;
diff --git a/player/command.h b/player/command.h
index 9e385dbc16..ae3af82886 100644
--- a/player/command.h
+++ b/player/command.h
@@ -45,6 +45,8 @@ struct mp_cmd_ctx {
bool bar_osd; // OSD bar requested
bool seek_msg_osd; // same as above, but for seek commands
bool seek_bar_osd;
+ // If mp_cmd_def.can_abort is set, this will be set.
+ struct mp_abort_entry *abort;
// Return values (to be set by command implementation, read by the
// completion callback).
bool success; // true by default
@@ -64,6 +66,7 @@ struct mp_cmd_ctx {
};
void run_command(struct MPContext *mpctx, struct mp_cmd *cmd,
+ struct mp_abort_entry *abort,
void (*on_completion)(struct mp_cmd_ctx *cmd),
void *on_completion_priv);
void mp_cmd_ctx_complete(struct mp_cmd_ctx *cmd);
diff --git a/player/core.h b/player/core.h
index 0434c5cb64..a42b1252b8 100644
--- a/player/core.h
+++ b/player/core.h
@@ -442,6 +442,8 @@ typedef struct MPContext {
// --- The following fields are protected by abort_lock
struct mp_cancel *demuxer_cancel; // cancel handle for MPContext.demuxer
+ struct mp_abort_entry **abort_list;
+ int num_abort_list;
// --- Owned by MPContext
pthread_t open_thread;
@@ -459,6 +461,20 @@ typedef struct MPContext {
int open_res_error;
} MPContext;
+// Contains information about an asynchronous work item, how it can be aborted,
+// and when. All fields are protected by MPContext.abort_lock.
+struct mp_abort_entry {
+ // General conditions.
+ bool coupled_to_playback; // trigger when playback is terminated
+ // Actual trigger to abort the work.
+ struct mp_cancel *cancel;
+ // For client API.
+ struct mpv_handle *client; // non-NULL if done by a client API user
+ int client_work_type; // client API type, e.h. MPV_EVENT_COMMAND_REPLY
+ uint64_t client_work_id; // client API user reply_userdata value
+ // (only valid if client_work_type set)
+};
+
// audio.c
void reset_audio_state(struct MPContext *mpctx);
void reinit_audio_chain(struct MPContext *mpctx);
@@ -488,6 +504,12 @@ struct playlist_entry *mp_check_playlist_resume(struct MPContext *mpctx,
// loadfile.c
void mp_abort_playback_async(struct MPContext *mpctx);
+void mp_abort_add(struct MPContext *mpctx, struct mp_abort_entry *abort);
+void mp_abort_remove(struct MPContext *mpctx, struct mp_abort_entry *abort);
+void mp_abort_recheck_locked(struct MPContext *mpctx,
+ struct mp_abort_entry *abort);
+void mp_abort_trigger_locked(struct MPContext *mpctx,
+ struct mp_abort_entry *abort);
void uninit_player(struct MPContext *mpctx, unsigned int mask);
int mp_add_external_file(struct MPContext *mpctx, char *filename,
enum stream_type filter);
diff --git a/player/loadfile.c b/player/loadfile.c
index e1864f3fd5..6f28d2ee38 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -67,11 +67,62 @@ void mp_abort_playback_async(struct MPContext *mpctx)
mp_cancel_trigger(mpctx->playback_abort);
pthread_mutex_lock(&mpctx->abort_lock);
+
if (mpctx->demuxer_cancel)
mp_cancel_trigger(mpctx->demuxer_cancel);
+
+ for (int n = 0; n < mpctx->num_abort_list; n++) {
+ struct mp_abort_entry *abort = mpctx->abort_list[n];
+ if (abort->coupled_to_playback)
+ mp_abort_trigger_locked(mpctx, abort);
+ }
+
+ pthread_mutex_unlock(&mpctx->abort_lock);
+}
+
+// Add it to the global list, and allocate required data structures.
+void mp_abort_add(struct MPContext *mpctx, struct mp_abort_entry *abort)
+{
+ pthread_mutex_lock(&mpctx->abort_lock);
+ assert(!abort->cancel);
+ abort->cancel = mp_cancel_new(NULL);
+ MP_TARRAY_APPEND(NULL, mpctx->abort_list, mpctx->num_abort_list, abort);
+ mp_abort_recheck_locked(mpctx, abort);
pthread_mutex_unlock(&mpctx->abort_lock);
}
+// Remove Add it to the global list, and free/clear required data structures.
+// Does not deallocate the abort value itself.
+void mp_abort_remove(struct MPContext *mpctx, struct mp_abort_entry *abort)
+{
+ pthread_mutex_lock(&mpctx->abort_lock);
+ for (int n = 0; n < mpctx->num_abort_list; n++) {
+ if (mpctx->abort_list[n] == abort) {
+ MP_TARRAY_REMOVE_AT(mpctx->abort_list, mpctx->num_abort_list, n);
+ TA_FREEP(&abort->cancel);
+ abort = NULL; // it's not free'd, just clear for the assert below
+ break;
+ }
+ }
+ assert(!abort); // should have been in the list
+ pthread_mutex_unlock(&mpctx->abort_lock);
+}
+
+// Verify whether the abort needs to be signaled after changing certain fields
+// in abort.
+void mp_abort_recheck_locked(struct MPContext *mpctx,
+ struct mp_abort_entry *abort)
+{
+ if (abort->coupled_to_playback && mp_cancel_test(mpctx->playback_abort))
+ mp_abort_trigger_locked(mpctx, abort);
+}
+
+void mp_abort_trigger_locked(struct MPContext *mpctx,
+ struct mp_abort_entry *abort)
+{
+ mp_cancel_trigger(abort->cancel);
+}
+
static void uninit_demuxer(struct MPContext *mpctx)
{
for (int r = 0; r < NUM_PTRACKS; r++) {
diff --git a/player/main.c b/player/main.c
index d744c9cf12..c51f93a430 100644
--- a/player/main.c
+++ b/player/main.c
@@ -189,6 +189,8 @@ void mp_destroy(struct MPContext *mpctx)
uninit_libav(mpctx->global);
mp_msg_uninit(mpctx->global);
+ assert(!mpctx->num_abort_list);
+ talloc_free(mpctx->abort_list);
pthread_mutex_destroy(&mpctx->abort_lock);
talloc_free(mpctx);
}
diff --git a/player/playloop.c b/player/playloop.c
index 26f9f12d82..a784bfd554 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -110,7 +110,7 @@ void mp_process_input(struct MPContext *mpctx)
mp_cmd_t *cmd = mp_input_read_cmd(mpctx->input);
if (!cmd)
break;
- run_command(mpctx, cmd, NULL, NULL);
+ run_command(mpctx, cmd, NULL, NULL, NULL);
}
mp_set_timeout(mpctx, mp_input_get_delay(mpctx->input));
}
diff --git a/player/screenshot.c b/player/screenshot.c
index e654ce2081..e24ca051f1 100644
--- a/player/screenshot.c
+++ b/player/screenshot.c
@@ -519,7 +519,7 @@ void screenshot_flip(struct MPContext *mpctx)
struct mp_waiter wait = MP_WAITER_INITIALIZER;
void *a[] = {mpctx, &wait};
- run_command(mpctx, mp_cmd_clone(ctx->each_frame), screenshot_fin, a);
+ run_command(mpctx, mp_cmd_clone(ctx->each_frame), NULL, screenshot_fin, a);
// Block (in a reentrant way) until he screenshot was written. Otherwise,
// we could pile up screenshot requests forever.