summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-06-30 20:09:27 +0200
committerwm4 <wm4@nowhere>2019-09-19 20:37:05 +0200
commitb945952e0da1b476ce347932a151d9c01a15e742 (patch)
treed2403ffff2fbd52f38a028669a9649e2d4ce7a83
parentc942178c92321a99ddf079e55af85724fc74ee16 (diff)
downloadmpv-b945952e0da1b476ce347932a151d9c01a15e742.tar.bz2
mpv-b945952e0da1b476ce347932a151d9c01a15e742.tar.xz
demux: runtime option changing for cache and stream recording
Make most of the demuxer options runtime-changeable. This includes the cache options and stream recording. The manpage documents some of the possibly weird issues related to this. In particular, the disk cache isn't shuffled around if the setting changes at runtime.
-rw-r--r--DOCS/man/options.rst16
-rw-r--r--demux/demux.c125
2 files changed, 106 insertions, 35 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index 8439a00163..4900ccc0e8 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -4004,6 +4004,12 @@ Cache
generally worthless after the media is closed, and it's hard to retrieve
any media data from it (it's not supported by design).
+ If the option is enabled at runtime, the cache file is created, but old data
+ will remain in the memory cache. If the option is disabled at runtime, old
+ data remains in the disk cache, and the cache file is not closed until the
+ media is closed. If the option is disabled and enabled again, it will
+ continue to use the cache file that was opened first.
+
``--cache-dir=<path>``
Directory where to create temporary files (default: none).
@@ -5863,8 +5869,14 @@ Miscellaneous
``--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.
+ or streams being selected/deselected during recording. Use with care.
+
+ If this is set at runtime, the old file is closed, and the new file is
+ opened. Note that this will write only data that is appended at the end of
+ the cache, and the already cached data cannot be written. (A fix for that
+ would be a command that dumps the cache using a given time range, possibly
+ with the option to be open-ended, which would continue to write data
+ appended to the cache. Such a command doesn't exist yet.)
``--lavfi-complex=<string>``
Set a "complex" libavfilter filter, which means a single filter graph can
diff --git a/demux/demux.c b/demux/demux.c
index f646158e22..d3bea4b841 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -149,8 +149,10 @@ const struct m_sub_options demux_conf = {
struct demux_internal {
struct mp_log *log;
+ struct mpv_global *global;
- struct demux_opts *opts;
+ bool can_cache; // not a slave demuxer; caching makes sense
+ bool can_record; // stream recording is allowed
// The demuxer runs potentially in another thread, so we keep two demuxer
// structs; the real demuxer can access the shadow struct only.
@@ -165,6 +167,9 @@ struct demux_internal {
// -- All the following fields are protected by lock.
+ struct demux_opts *opts;
+ struct m_config_cache *opts_cache;
+
bool thread_terminate;
bool threading;
bool shutdown_async;
@@ -190,6 +195,8 @@ struct demux_internal {
size_t max_bytes;
size_t max_bytes_bw;
bool seekable_cache;
+ bool using_network_cache_opts;
+ char *record_filename;
// At least one decoder actually requested data since init or the last seek.
// Do this to allow the decoder thread to select streams before starting.
@@ -1962,7 +1969,7 @@ static void add_packet_locked(struct sh_stream *stream, demux_packet_t *dp)
record_packet(in, dp);
- if (in->cache) {
+ if (in->cache && in->opts->disk_cache) {
int64_t pos = demux_cache_write(in->cache, dp);
if (pos >= 0) {
demux_packet_unref_contents(dp);
@@ -2327,9 +2334,73 @@ static void execute_seek(struct demux_internal *in)
in->seeking_in_progress = MP_NOPTS_VALUE;
}
+static void update_opts(struct demux_internal *in)
+{
+ struct demux_opts *opts = in->opts;
+
+ in->min_secs = opts->min_secs;
+ in->max_bytes = opts->max_bytes;
+ in->max_bytes_bw = opts->max_bytes_bw;
+
+ int seekable = opts->seekable_cache;
+ bool is_streaming = in->d_thread->is_network ||
+ (in->d_thread->stream && in->d_thread->stream->streaming);
+ bool use_cache = is_streaming;
+ if (opts->enable_cache >= 0)
+ use_cache = opts->enable_cache == 1;
+
+ if (use_cache) {
+ in->min_secs = MPMAX(in->min_secs, opts->min_secs_cache);
+ if (seekable < 0)
+ seekable = 1;
+ }
+ in->seekable_cache = seekable == 1;
+ in->using_network_cache_opts = is_streaming && use_cache;
+
+ if (!in->can_cache) {
+ in->seekable_cache = false;
+ in->min_secs = 0;
+ in->max_bytes = 1;
+ in->max_bytes_bw = 0;
+ in->using_network_cache_opts = false;
+ }
+
+ if (in->seekable_cache && opts->disk_cache && !in->cache) {
+ in->cache = demux_cache_create(in->global, in->log);
+ if (!in->cache)
+ MP_ERR(in, "Failed to create file cache.\n");
+ }
+
+ // The filename option really decides whether recording should be active.
+ // So if the filename changes, act upon it.
+ char *old = in->record_filename ? in->record_filename : "";
+ char *new = opts->record_file ? opts->record_file : "";
+ if (strcmp(old, new) != 0) {
+ if (in->recorder) {
+ MP_WARN(in, "Stopping recording.\n");
+ mp_recorder_destroy(in->recorder);
+ in->recorder = NULL;
+ }
+ in->record_filename = talloc_strdup(in, opts->record_file);
+ talloc_free(in->record_filename);
+ // Note: actual recording only starts once packets are read. It may be
+ // important to delay creating in->recorder to that point, because the
+ // demuxer might detect more streams until finding the first packet.
+ in->enable_recording = in->can_record;
+ }
+
+ // In case the cache was reduced in size.
+ prune_old_packets(in);
+
+ // In case the seekable cache was disabled.
+ free_empty_cached_ranges(in);
+}
+
// Make demuxing progress. Return whether progress was made.
static bool thread_work(struct demux_internal *in)
{
+ if (m_config_cache_update(in->opts_cache))
+ update_opts(in);
if (in->tracks_switched) {
execute_trackswitch(in);
return true;
@@ -2994,11 +3065,10 @@ static void demux_init_ccs(struct demuxer *demuxer, struct demux_opts *opts)
bool demux_is_network_cached(demuxer_t *demuxer)
{
struct demux_internal *in = demuxer->in;
- bool use_cache = demuxer->is_network ||
- (demuxer->stream && demuxer->stream->streaming);
- if (in->opts->enable_cache >= 0)
- use_cache = in->opts->enable_cache == 1;
- return use_cache;
+ pthread_mutex_lock(&in->lock);
+ bool r = in->using_network_cache_opts;
+ pthread_mutex_unlock(&in->lock);
+ return r;
}
struct parent_stream_info {
@@ -3020,7 +3090,9 @@ static struct demuxer *open_given_type(struct mpv_global *global,
return NULL;
struct demuxer *demuxer = talloc_ptrtype(NULL, demuxer);
- struct demux_opts *opts = mp_get_config_group(demuxer, global, &demux_conf);
+ struct m_config_cache *opts_cache =
+ m_config_cache_alloc(demuxer, global, &demux_conf);
+ struct demux_opts *opts = opts_cache->opts;
*demuxer = (struct demuxer) {
.desc = desc,
.stream = stream,
@@ -3039,19 +3111,19 @@ static struct demuxer *open_given_type(struct mpv_global *global,
struct demux_internal *in = demuxer->in = talloc_ptrtype(demuxer, in);
*in = (struct demux_internal){
+ .global = global,
.log = demuxer->log,
+ .can_cache = params && params->is_top_level,
+ .can_record = params && params->stream_record,
.opts = opts,
+ .opts_cache = opts_cache,
.d_thread = talloc(demuxer, struct demuxer),
.d_user = demuxer,
- .min_secs = opts->min_secs,
- .max_bytes = opts->max_bytes,
- .max_bytes_bw = opts->max_bytes_bw,
.after_seek = true, // (assumed identical to initial demuxer state)
.after_seek_to_start = true,
.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);
@@ -3085,13 +3157,7 @@ static struct demuxer *open_given_type(struct mpv_global *global,
in->duration = in->d_thread->duration;
demuxer_sort_chapters(demuxer);
in->events = DEMUX_EVENT_ALL;
- int seekable = opts->seekable_cache;
- if (demux_is_network_cached(demuxer)) {
- in->min_secs = MPMAX(in->min_secs, opts->min_secs_cache);
- if (seekable < 0)
- seekable = 1;
- }
- in->seekable_cache = seekable == 1;
+
struct demuxer *sub = NULL;
if (!(params && params->disable_timeline)) {
struct timeline *tl = timeline_load(global, log, demuxer);
@@ -3103,26 +3169,19 @@ static struct demuxer *open_given_type(struct mpv_global *global,
sub =
open_given_type(global, log, &demuxer_desc_timeline,
NULL, sinfo, &params2, DEMUX_CHECK_FORCE);
- if (!sub)
+ if (sub) {
+ in->can_cache = false;
+ in->can_record = false;
+ } else {
timeline_destroy(tl);
+ }
}
}
- if (!(params && params->is_top_level) || sub) {
- in->seekable_cache = false;
- in->min_secs = 0;
- in->max_bytes = 1;
- in->enable_recording = false;
- }
-
- if (in->seekable_cache && opts->disk_cache) {
- in->cache = demux_cache_create(global, log);
- if (!in->cache)
- MP_ERR(in, "Failed to create file cache.\n");
- }
-
switch_to_fresh_cache_range(in);
+ update_opts(in);
+
demux_update(demuxer, MP_NOPTS_VALUE);
demuxer = sub ? sub : demuxer;