summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2018-09-01 16:06:41 +0200
committerAnton Kindestam <antonki@kth.se>2018-12-06 10:31:10 +0100
commit9d8afcf79e3a4b563800dcf7fc08779355e9d602 (patch)
tree4e7e6e74d02f2b692194b79569d895d3ccbf7652
parent02756c3735f8e7e8ac6b5ca1168b68a6a325122d (diff)
downloadmpv-9d8afcf79e3a4b563800dcf7fc08779355e9d602.tar.bz2
mpv-9d8afcf79e3a4b563800dcf7fc08779355e9d602.tar.xz
demux: add another stream recording feature
--record-file is nice, but only sometimes. If you watch some sort of livestream which you want to record, it's actually much nicer not to record what you're currently "seeing", but anything you're receiving.
-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++;
}
}