summaryrefslogtreecommitdiffstats
path: root/player
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-10-15 23:09:53 +0200
committerwm4 <wm4@nowhere>2014-10-16 01:00:22 +0200
commit8e4fa5fcd15ecba6a046aa71650bbfc759856fa8 (patch)
tree5ee966a04f590cc2b1ac482c420f7c704faa17ec /player
parentbc0ed904811b4e4a0fc3d6129ba92e2c786cbabf (diff)
downloadmpv-8e4fa5fcd15ecba6a046aa71650bbfc759856fa8.tar.bz2
mpv-8e4fa5fcd15ecba6a046aa71650bbfc759856fa8.tar.xz
command: add a mechanism to allow scripts to intercept file loads
A vague idea to get something similar what libquvi did. Undocumented because it might change a lot, or even be removed. To give an idea what it does, a Lua script could do the following: -- type ID priority mp.commandv("hook_add", "on_load", 0, 0) mp.register_script_message("hook_run", function(param, param2) -- param is "0", the user-chosen ID from the hook_add command -- param2 is the magic value that has to be passed to finish -- the hook mp.resume_all() -- do something, maybe set options that are reset on end: mp.set_property("file-local-options/name", "value") -- or change the URL that's being opened: local url = mp.get_property("stream-open-filename") mp.set_property("stream-open-filename", url .. ".png") -- let the player (or the next script) continue mp.commandv("hook_ack", param2) end)
Diffstat (limited to 'player')
-rw-r--r--player/client.c4
-rw-r--r--player/command.c138
-rw-r--r--player/command.h4
-rw-r--r--player/core.h1
-rw-r--r--player/loadfile.c51
5 files changed, 186 insertions, 12 deletions
diff --git a/player/client.c b/player/client.c
index 38b0898f86..2166255c2c 100644
--- a/player/client.c
+++ b/player/client.c
@@ -871,6 +871,8 @@ static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd)
if (mp_input_is_abort_cmd(cmd))
mp_cancel_trigger(ctx->mpctx->playback_abort);
+ cmd->sender = ctx->name;
+
struct cmd_request req = {
.mpctx = ctx->mpctx,
.cmd = cmd,
@@ -905,6 +907,8 @@ static int run_cmd_async(mpv_handle *ctx, uint64_t ud, struct mp_cmd *cmd)
if (!cmd)
return MPV_ERROR_INVALID_PARAMETER;
+ cmd->sender = ctx->name;
+
struct cmd_request *req = talloc_ptrtype(NULL, req);
*req = (struct cmd_request){
.mpctx = ctx->mpctx,
diff --git a/player/command.c b/player/command.c
index ee782da8ee..2d05ff6c4e 100644
--- a/player/command.c
+++ b/player/command.c
@@ -85,6 +85,10 @@ struct command_ctx {
struct sub_bitmaps overlay_osd[2];
struct sub_bitmaps *overlay_osd_current;
+ struct hook_handler **hooks;
+ int num_hooks;
+ int64_t hook_seq; // for hook_handler.seq
+
struct ao_device_list *cached_ao_devices;
};
@@ -94,11 +98,107 @@ struct overlay {
struct sub_bitmap osd;
};
+struct hook_handler {
+ char *client; // client API user name
+ char *type; // kind of hook, e.g. "on_load"
+ char *user_id; // numeric user-chosen ID, printed as string
+ int priority; // priority for global hook order
+ int64_t seq; // unique ID (also age -> fixed order for equal priorities)
+ bool active; // hook is currently in progress (only 1 at a time for now)
+};
+
static int edit_filters(struct MPContext *mpctx, enum stream_type mediatype,
const char *cmd, const char *arg);
static int set_filters(struct MPContext *mpctx, enum stream_type mediatype,
struct m_obj_settings *new_chain);
+bool mp_hook_test_completion(struct MPContext *mpctx, char *type)
+{
+ struct command_ctx *cmd = mpctx->command_ctx;
+ for (int n = 0; n < cmd->num_hooks; n++) {
+ struct hook_handler *h = cmd->hooks[n];
+ if (h->active && strcmp(h->type, type) == 0)
+ return false;
+ }
+ return true;
+}
+
+static bool send_hook_msg(struct MPContext *mpctx, struct hook_handler *h,
+ char *cmd)
+{
+ mpv_event_client_message *m = talloc_ptrtype(NULL, m);
+ *m = (mpv_event_client_message){0};
+ MP_TARRAY_APPEND(m, m->args, m->num_args, "hook_run");
+ MP_TARRAY_APPEND(m, m->args, m->num_args, talloc_strdup(m, h->user_id));
+ MP_TARRAY_APPEND(m, m->args, m->num_args, talloc_strdup(m, h->type));
+ bool r =
+ mp_client_send_event(mpctx, h->client, MPV_EVENT_CLIENT_MESSAGE, m) >= 0;
+ if (!r)
+ MP_WARN(mpctx, "Sending hook command failed.\n");
+ return r;
+}
+
+// client==NULL means start the hook chain
+void mp_hook_run(struct MPContext *mpctx, char *client, char *type)
+{
+ struct command_ctx *cmd = mpctx->command_ctx;
+ struct hook_handler *next = NULL;
+ bool found_current = !client;
+ for (int n = 0; n < cmd->num_hooks; n++) {
+ struct hook_handler *h = cmd->hooks[n];
+ if (!found_current) {
+ if (h->active && strcmp(h->type, type) == 0) {
+ h->active = false;
+ found_current = true;
+ }
+ } else if (strcmp(h->type, type) == 0) {
+ next = h;
+ break;
+ }
+ }
+ if (!next)
+ return;
+ MP_VERBOSE(mpctx, "Running hook: %s/%s\n", next->client, type);
+ next->active = true;
+ send_hook_msg(mpctx, next, "hook_run");
+}
+
+void mp_hook_abort(struct MPContext *mpctx, char *type)
+{
+ struct command_ctx *cmd = mpctx->command_ctx;
+ for (int n = 0; n < cmd->num_hooks; n++) {
+ struct hook_handler *h = cmd->hooks[n];
+ if (h->active && strcmp(h->type, type) == 0)
+ send_hook_msg(mpctx, h, "hook_abort");
+ }
+}
+
+static int compare_hook(const void *pa, const void *pb)
+{
+ struct hook_handler **h1 = (void *)pa;
+ struct hook_handler **h2 = (void *)pb;
+ if ((*h1)->priority != (*h2)->priority)
+ return (*h1)->priority - (*h2)->priority;
+ return (*h1)->seq - (*h2)->seq;
+}
+
+static void mp_hook_add(struct MPContext *mpctx, char *client, char *name,
+ int id, int pri)
+{
+ struct command_ctx *cmd = mpctx->command_ctx;
+ struct hook_handler *h = talloc_ptrtype(cmd, h);
+ int64_t seq = cmd->hook_seq++;
+ *h = (struct hook_handler){
+ .client = talloc_strdup(h, client),
+ .type = talloc_strdup(h, name),
+ .user_id = talloc_asprintf(h, "%d", id),
+ .priority = pri,
+ .seq = seq,
+ };
+ MP_TARRAY_APPEND(cmd, cmd->hooks, cmd->num_hooks, h);
+ qsort(cmd->hooks, cmd->num_hooks, sizeof(cmd->hooks[0]), compare_hook);
+}
+
// Call before a seek, in order to allow revert_seek to undo the seek.
static void mark_seek(struct MPContext *mpctx)
{
@@ -213,6 +313,27 @@ static int mp_property_filename(void *ctx, struct m_property *prop,
return r;
}
+static int mp_property_stream_open_filename(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ if (!mpctx->stream_open_filename || !mpctx->playing)
+ return M_PROPERTY_UNAVAILABLE;
+ switch (action) {
+ case M_PROPERTY_SET: {
+ if (mpctx->stream)
+ return M_PROPERTY_ERROR;
+ mpctx->stream_open_filename =
+ talloc_strdup(mpctx->stream_open_filename, *(char **)arg);
+ return M_PROPERTY_OK;
+ }
+ case M_PROPERTY_GET_TYPE:
+ case M_PROPERTY_GET:
+ return m_property_strdup_ro(action, arg, mpctx->stream_open_filename);
+ }
+ return M_PROPERTY_NOT_IMPLEMENTED;
+}
+
static int mp_property_file_size(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -2771,6 +2892,7 @@ static const struct m_property mp_properties[] = {
{"loop-file", mp_property_generic_option},
{"speed", mp_property_playback_speed},
{"filename", mp_property_filename},
+ {"stream-open-filename", mp_property_stream_open_filename},
{"file-size", mp_property_file_size},
{"path", mp_property_path},
{"media-title", mp_property_media_title},
@@ -4066,6 +4188,22 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
break;
}
+ case MP_CMD_HOOK_ADD:
+ if (!cmd->sender) {
+ MP_ERR(mpctx, "Can be used from client API only.\n");
+ return -1;
+ }
+ mp_hook_add(mpctx, cmd->sender, cmd->args[0].v.s, cmd->args[1].v.i,
+ cmd->args[2].v.i);
+ break;
+ case MP_CMD_HOOK_ACK:
+ if (!cmd->sender) {
+ MP_ERR(mpctx, "Can be used from client API only.\n");
+ return -1;
+ }
+ mp_hook_run(mpctx, cmd->sender, cmd->args[0].v.s);
+ break;
+
default:
MP_VERBOSE(mpctx, "Received unknown cmd %s\n", cmd->name);
return -1;
diff --git a/player/command.h b/player/command.h
index 6e3312b806..f9bb7c49a0 100644
--- a/player/command.h
+++ b/player/command.h
@@ -43,4 +43,8 @@ uint64_t mp_get_property_event_mask(const char *name);
#define INTERNAL_EVENT_BASE 24
#define MP_EVENT_CACHE_UPDATE (INTERNAL_EVENT_BASE + 0)
+bool mp_hook_test_completion(struct MPContext *mpctx, char *type);
+void mp_hook_run(struct MPContext *mpctx, char *client, char *type);
+void mp_hook_abort(struct MPContext *mpctx, char *type);
+
#endif /* MPLAYER_COMMAND_H */
diff --git a/player/core.h b/player/core.h
index f16cca166d..4f88710081 100644
--- a/player/core.h
+++ b/player/core.h
@@ -179,6 +179,7 @@ typedef struct MPContext {
struct playlist *playlist;
struct playlist_entry *playing; // currently playing file
char *filename; // immutable copy of playing->filename (or NULL)
+ char *stream_open_filename;
struct mp_resolve_result *resolve_result;
enum stop_play_reason stop_play;
bool playback_initialized; // playloop can be run/is running
diff --git a/player/loadfile.c b/player/loadfile.c
index 306e340127..6d6de1676b 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -794,6 +794,36 @@ static void transfer_playlist(struct MPContext *mpctx, struct playlist *pl)
}
}
+static int process_open_hooks(struct MPContext *mpctx)
+{
+
+ mp_hook_run(mpctx, NULL, "on_load");
+
+ while (!mp_hook_test_completion(mpctx, "on_load")) {
+ mp_idle(mpctx);
+ if (mpctx->stop_play) {
+ if (mpctx->stop_play == PT_QUIT)
+ return -1;
+ // Can't exit immediately, the script would interfere with the
+ // next file being loaded.
+ mp_hook_abort(mpctx, "on_load");
+ }
+ }
+
+ // quvi stuff
+ char *filename = mpctx->stream_open_filename;
+ mpctx->resolve_result = resolve_url(filename, mpctx->global);
+ if (mpctx->resolve_result) {
+ print_resolve_contents(mpctx->log, mpctx->resolve_result);
+ if (mpctx->resolve_result->playlist) {
+ transfer_playlist(mpctx, mpctx->resolve_result->playlist);
+ return 1;
+ }
+ mpctx->stream_open_filename = mpctx->resolve_result->url;
+ }
+ return 0;
+}
+
static void print_timeline(struct MPContext *mpctx)
{
if (mpctx->timeline) {
@@ -936,6 +966,7 @@ static void play_current_file(struct MPContext *mpctx)
mpctx->playing->reserved += 1;
mpctx->filename = talloc_strdup(tmp, mpctx->playing->filename);
+ mpctx->stream_open_filename = mpctx->filename;
mpctx->add_osd_seek_info &= OSD_SEEK_INFO_EDITION;
@@ -972,21 +1003,16 @@ static void play_current_file(struct MPContext *mpctx)
assert(mpctx->d_sub[0] == NULL);
assert(mpctx->d_sub[1] == NULL);
- char *stream_filename = mpctx->filename;
- mpctx->resolve_result = resolve_url(stream_filename, mpctx->global);
- if (mpctx->resolve_result) {
- talloc_steal(tmp, mpctx->resolve_result);
- print_resolve_contents(mpctx->log, mpctx->resolve_result);
- if (mpctx->resolve_result->playlist) {
- transfer_playlist(mpctx, mpctx->resolve_result->playlist);
- goto terminate_playback;
- }
- stream_filename = mpctx->resolve_result->url;
- }
+ int hooks_res = process_open_hooks(mpctx);
+ talloc_steal(tmp, mpctx->resolve_result);
+ if (hooks_res)
+ goto terminate_playback; // quit or preloaded playlist special-case
+
int stream_flags = STREAM_READ;
if (!opts->load_unsafe_playlists)
stream_flags |= mpctx->playing->stream_flags;
- mpctx->stream = open_stream_async(mpctx, stream_filename, stream_flags);
+ mpctx->stream = open_stream_async(mpctx, mpctx->stream_open_filename,
+ stream_flags);
if (!mpctx->stream)
goto terminate_playback;
@@ -1245,6 +1271,7 @@ terminate_playback:
playlist_entry_unref(mpctx->playing);
mpctx->playing = NULL;
mpctx->filename = NULL;
+ mpctx->stream_open_filename = NULL;
talloc_free(tmp);
}