summaryrefslogtreecommitdiffstats
path: root/player
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-07-07 20:38:22 +0200
committerwm4 <wm4@nowhere>2019-09-19 20:37:05 +0200
commit023b5964b047d83772163195c322936968e7d47a (patch)
tree8236e49d39e364bab04439aded260603bb566b31 /player
parent226e050b83b1c9946c52ae77b78a63e82a3ae628 (diff)
downloadmpv-023b5964b047d83772163195c322936968e7d47a.tar.bz2
mpv-023b5964b047d83772163195c322936968e7d47a.tar.xz
demux, command: add a third stream recording mechanism
That's right, and it's probably not the end of it. I'll just claim that I have no idea how to create a proper user interface for this, so I'm creating multiple partially-orthogonal, of which some may work better in each of its special use cases. Until now, there was --record-file. You get relatively good control about what is muxed, and it can use the cache. But it sucks that it's bound to playback. If you pause while it's set, muxing stops. If you seek while it's set, the output will be sort-of trashed, and that's by design. Then --stream-record was added. This is a bit better (especially for live streams), but you can't really control well when muxing stops or ends. In particular, it can't use the cache (it just dumps whatever the underlying demuxer returns). Today, the idea is that the user should just be able to select a time range to dump to a file, and it should not affected by the user seeking around in the cache. In addition, the stream may still be running, so there's some need to continue dumping, even if it's redundant to --stream-record. One notable thing is that it uses the async command shit. Not sure whether this is a good idea. Maybe not, but whatever. Also, a user can always use the "async" prefix to pretend it doesn't. Much of this was barely tested (especially the reinterleaving crap), let's just hope it mostly works. I'm sure you can tolerate the one or other crash?
Diffstat (limited to 'player')
-rw-r--r--player/command.c112
-rw-r--r--player/command.h2
-rw-r--r--player/core.h3
-rw-r--r--player/loadfile.c2
4 files changed, 117 insertions, 2 deletions
diff --git a/player/command.c b/player/command.c
index 2472aab5e9..a2bdb19585 100644
--- a/player/command.c
+++ b/player/command.c
@@ -63,6 +63,7 @@
#include "misc/dispatch.h"
#include "misc/node.h"
#include "misc/thread_pool.h"
+#include "misc/thread_tools.h"
#include "osdep/io.h"
#include "osdep/subprocess.h"
@@ -104,6 +105,8 @@ struct command_ctx {
char *cur_ipc_input;
int silence_option_deprecations;
+
+ struct mp_cmd_ctx *cache_dump_cmd; // in progress cache dumping
};
struct overlay {
@@ -5560,6 +5563,91 @@ static void cmd_load_script(void *p)
cmd->success = false;
}
+static void cache_dump_poll(struct MPContext *mpctx)
+{
+ struct command_ctx *ctx = mpctx->command_ctx;
+ struct mp_cmd_ctx *cmd = ctx->cache_dump_cmd;
+
+ if (!cmd)
+ return;
+
+ // Can't close demuxer without stopping dumping.
+ assert(mpctx->demuxer);
+
+ if (mp_cancel_test(cmd->abort->cancel)) {
+ // Synchronous abort. In particular, the dump command shall not report
+ // completion to the user before the dump target file was closed.
+ demux_cache_dump_set(mpctx->demuxer, 0, 0, NULL);
+ assert(demux_cache_dump_get_status(mpctx->demuxer) <= 0);
+ }
+
+ int status = demux_cache_dump_get_status(mpctx->demuxer);
+ if (status <= 0) {
+ if (status < 0) {
+ mp_cmd_msg(cmd, MSGL_ERR, "Cache dumping stopped due to error.");
+ cmd->success = false;
+ } else {
+ mp_cmd_msg(cmd, MSGL_INFO, "Cache dumping successfully ended.");
+ cmd->success = true;
+ }
+ ctx->cache_dump_cmd = NULL;
+ mp_cmd_ctx_complete(cmd);
+ }
+}
+
+void mp_abort_cache_dumping(struct MPContext *mpctx)
+{
+ struct command_ctx *ctx = mpctx->command_ctx;
+
+ if (ctx->cache_dump_cmd)
+ mp_cancel_trigger(ctx->cache_dump_cmd->abort->cancel);
+ cache_dump_poll(mpctx);
+ assert(!ctx->cache_dump_cmd); // synchronous abort, must have worked
+}
+
+static void run_dump_cmd(struct mp_cmd_ctx *cmd, double start, double end,
+ char *filename)
+{
+ struct MPContext *mpctx = cmd->mpctx;
+ struct command_ctx *ctx = mpctx->command_ctx;
+
+ mp_abort_cache_dumping(mpctx);
+
+ if (!mpctx->demuxer) {
+ mp_cmd_msg(cmd, MSGL_ERR, "No demuxer open.");
+ cmd->success = false;
+ mp_cmd_ctx_complete(cmd);
+ return;
+ }
+
+ if (!demux_cache_dump_set(mpctx->demuxer, start, end, filename)) {
+ mp_cmd_msg(cmd, MSGL_INFO, "Cache dumping stopped.");
+ mp_cmd_ctx_complete(cmd);
+ return;
+ }
+
+ mp_cmd_msg(cmd, MSGL_INFO, "Cache dumping started.");
+
+ ctx->cache_dump_cmd = cmd;
+ cache_dump_poll(mpctx);
+}
+
+static void cmd_dump_cache(void *p)
+{
+ struct mp_cmd_ctx *cmd = p;
+
+ run_dump_cmd(cmd, cmd->args[0].v.d, cmd->args[1].v.d, cmd->args[2].v.s);
+}
+
+static void cmd_dump_cache_ab(void *p)
+{
+ struct mp_cmd_ctx *cmd = p;
+ struct MPContext *mpctx = cmd->mpctx;
+
+ run_dump_cmd(cmd, mpctx->opts->ab_loop[0], mpctx->opts->ab_loop[1],
+ cmd->args[0].v.s);
+}
+
/* This array defines all known commands.
* The first field the command name used in libmpv and input.conf.
* The second field is the handler function (see mp_cmd_def.handler and
@@ -5884,6 +5972,20 @@ const struct mp_cmd_def mp_cmds[] = {
{ "load-script", cmd_load_script, {OPT_STRING("filename", v.s, 0)} },
+ { "dump-cache", cmd_dump_cache, { OPT_TIME("start", v.d, 0,
+ .min = MP_NOPTS_VALUE),
+ OPT_TIME("end", v.d, 0,
+ .min = MP_NOPTS_VALUE),
+ OPT_STRING("filename", v.s, 0) },
+ .exec_async = true,
+ .can_abort = true,
+ },
+
+ { "ab-loop-dump-cache", cmd_dump_cache_ab, { OPT_STRING("filename", v.s, 0) },
+ .exec_async = true,
+ .can_abort = true,
+ },
+
{0}
};
@@ -5892,8 +5994,13 @@ const struct mp_cmd_def mp_cmds[] = {
void command_uninit(struct MPContext *mpctx)
{
+ struct command_ctx *ctx = mpctx->command_ctx;
+
+ assert(!ctx->cache_dump_cmd); // closing the demuxer must have aborted it
+
overlay_uninit(mpctx);
- ao_hotplug_destroy(mpctx->command_ctx->hotplug);
+ ao_hotplug_destroy(ctx->hotplug);
+
talloc_free(mpctx->command_ctx);
mpctx->command_ctx = NULL;
}
@@ -5967,6 +6074,9 @@ void handle_command_updates(struct MPContext *mpctx)
// to recheck the state. Then the client(s) will read the property.
if (ctx->hotplug && ao_hotplug_check_update(ctx->hotplug))
mp_notify_property(mpctx, "audio-device-list");
+
+ // Depends on polling demuxer wakeup callback notifications.
+ cache_dump_poll(mpctx);
}
void mp_notify(struct MPContext *mpctx, int event, void *arg)
diff --git a/player/command.h b/player/command.h
index 4cc774c6e8..3952ce37e7 100644
--- a/player/command.h
+++ b/player/command.h
@@ -110,4 +110,6 @@ void mp_hook_add(struct MPContext *mpctx, const char *client, const char *name,
void mark_seek(struct MPContext *mpctx);
+void mp_abort_cache_dumping(struct MPContext *mpctx);
+
#endif /* MPLAYER_COMMAND_H */
diff --git a/player/core.h b/player/core.h
index bf4b62fa56..893a4cf3e8 100644
--- a/player/core.h
+++ b/player/core.h
@@ -468,7 +468,8 @@ typedef struct MPContext {
struct mp_abort_entry {
// General conditions.
bool coupled_to_playback; // trigger when playback is terminated
- // Actual trigger to abort the work.
+ // Actual trigger to abort the work. Pointer immutable, owner may access
+ // without holding the abort_lock.
struct mp_cancel *cancel;
// For client API.
struct mpv_handle *client; // non-NULL if done by a client API user
diff --git a/player/loadfile.c b/player/loadfile.c
index af84a82c99..ec84e8a003 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -201,6 +201,8 @@ static void uninit_demuxer(struct MPContext *mpctx)
mpctx->chapters = NULL;
mpctx->num_chapters = 0;
+ mp_abort_cache_dumping(mpctx);
+
struct demuxer **demuxers = NULL;
int num_demuxers = 0;