summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/options.rst6
-rw-r--r--common/recorder.c3
-rw-r--r--demux/demux.c40
-rw-r--r--demux/demux.h1
-rw-r--r--player/loadfile.c7
5 files changed, 53 insertions, 4 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index 9ebbdd797b..f384123f6d 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -5565,6 +5565,12 @@ Miscellaneous
it to a template (similar to ``--screenshot-template``), being renamed,
removed, or anything else, until it is declared semi-stable.
+``--stream-record=<file>``
+ Similar to ``--record-file``, but write packets as they are received. The
+ implementation of this does not tolerate seeks (outside of demuxer cache),
+ or streams being selected/deselected during recording. Can not be set at
+ runtime. Use with care.
+
``--lavfi-complex=<string>``
Set a "complex" libavfilter filter, which means a single filter graph can
take input from multiple source audio and video tracks. The graph can result
diff --git a/common/recorder.c b/common/recorder.c
index 9970c05c32..6b233d41bb 100644
--- a/common/recorder.c
+++ b/common/recorder.c
@@ -335,8 +335,7 @@ void mp_recorder_mark_discontinuity(struct mp_recorder *priv)
// mp_recorder_create().
struct mp_recorder_sink *mp_recorder_get_sink(struct mp_recorder *r, int stream)
{
- assert(stream >= 0 && stream < r->num_streams);
- return r->streams[stream];
+ return stream >= 0 && stream < r->num_streams ? r->streams[stream] : NULL;
}
// Pass a packet to the given stream. The function does not own the packet, but
diff --git a/demux/demux.c b/demux/demux.c
index b3b18257cf..e9565166ab 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -35,6 +35,7 @@
#include "mpv_talloc.h"
#include "common/msg.h"
#include "common/global.h"
+#include "common/recorder.h"
#include "misc/thread_tools.h"
#include "osdep/atomic.h"
#include "osdep/timer.h"
@@ -97,6 +98,7 @@ struct demux_opts {
int access_references;
int seekable_cache;
int create_ccs;
+ char *record_file;
};
#define OPT_BASE_STRUCT struct demux_opts
@@ -118,6 +120,7 @@ const struct m_sub_options demux_conf = {
OPT_CHOICE("demuxer-seekable-cache", seekable_cache, 0,
({"auto", -1}, {"no", 0}, {"yes", 1})),
OPT_FLAG("sub-create-cc-track", create_ccs, 0),
+ OPT_STRING("stream-record", record_file, 0),
{0}
},
.size = sizeof(struct demux_opts),
@@ -229,6 +232,10 @@ struct demux_internal {
int64_t next_cache_update;
// Updated during init only.
char *stream_base_filename;
+
+ // -- Access from demuxer thread only
+ bool enable_recording;
+ struct mp_recorder *recorder;
};
// A continuous range of cached packets for all enabled streams.
@@ -963,6 +970,11 @@ static void demux_shutdown(struct demux_internal *in)
{
struct demuxer *demuxer = in->d_user;
+ if (in->recorder) {
+ mp_recorder_destroy(in->recorder);
+ in->recorder = NULL;
+ }
+
if (demuxer->desc->close)
demuxer->desc->close(in->d_thread);
demuxer->priv = NULL;
@@ -1509,6 +1521,33 @@ void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
}
}
+ // (should preferable be outside of the lock)
+ if (in->enable_recording && !in->recorder &&
+ in->opts->record_file && in->opts->record_file[0])
+ {
+ // Later failures shouldn't make it retry and overwrite the previously
+ // recorded file.
+ in->enable_recording = false;
+
+ in->recorder =
+ mp_recorder_create(in->d_thread->global, in->opts->record_file,
+ in->streams, in->num_streams);
+ if (!in->recorder)
+ MP_ERR(in, "Disabling recording.\n");
+ }
+
+ if (in->recorder) {
+ struct mp_recorder_sink *sink =
+ mp_recorder_get_sink(in->recorder, dp->stream);
+ if (sink) {
+ mp_recorder_feed_packet(sink, dp);
+ } else {
+ MP_ERR(in, "New stream appeared; stopping recording.\n");
+ mp_recorder_destroy(in->recorder);
+ in->recorder = NULL;
+ }
+ }
+
wakeup_ds(ds);
pthread_mutex_unlock(&in->lock);
}
@@ -2338,6 +2377,7 @@ static struct demuxer *open_given_type(struct mpv_global *global,
.highest_av_pts = MP_NOPTS_VALUE,
.seeking_in_progress = MP_NOPTS_VALUE,
.demux_ts = MP_NOPTS_VALUE,
+ .enable_recording = params && params->stream_record,
};
pthread_mutex_init(&in->lock, NULL);
pthread_cond_init(&in->wakeup, NULL);
diff --git a/demux/demux.h b/demux/demux.h
index 956548d90b..4fcf8cfbae 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -178,6 +178,7 @@ struct demuxer_params {
bstr init_fragment;
bool skip_lavf_probing;
bool does_not_own_stream; // if false, stream is free'd on demux_free()
+ bool stream_record; // if true, enable stream recording if option is set
// -- demux_open_url() only
int stream_flags;
// result
diff --git a/player/loadfile.c b/player/loadfile.c
index 9d37c2ca21..4dff4c710c 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -974,6 +974,7 @@ static void *open_demux_thread(void *ctx)
struct demuxer_params p = {
.force_format = mpctx->open_format,
.stream_flags = mpctx->open_url_flags,
+ .stream_record = true,
};
mpctx->open_res_demuxer =
demux_open_url(mpctx->open_url, &p, mpctx->open_cancel, mpctx->global);
@@ -1848,8 +1849,10 @@ void open_recorder(struct MPContext *mpctx, bool on_init)
break;
// (We expect track->stream not to be reused on other tracks.)
if (track->stream == streams[n_stream]) {
- set_track_recorder_sink(track,
- mp_recorder_get_sink(mpctx->recorder, n_stream));
+ struct mp_recorder_sink * sink =
+ mp_recorder_get_sink(mpctx->recorder, n_stream);
+ assert(sink);
+ set_track_recorder_sink(track, sink);
n_stream++;
}
}