diff options
Diffstat (limited to 'demux')
-rw-r--r-- | demux/demux.c | 235 | ||||
-rw-r--r-- | demux/demux.h | 17 | ||||
-rw-r--r-- | demux/demux_disc.c | 16 | ||||
-rw-r--r-- | demux/demux_lavf.c | 4 | ||||
-rw-r--r-- | demux/demux_mkv_timeline.c | 18 | ||||
-rw-r--r-- | demux/demux_playlist.c | 1 | ||||
-rw-r--r-- | demux/demux_timeline.c | 6 | ||||
-rw-r--r-- | demux/demux_tv.c | 2 | ||||
-rw-r--r-- | demux/timeline.c | 6 |
9 files changed, 213 insertions, 92 deletions
diff --git a/demux/demux.c b/demux/demux.c index f02f9b77aa..33f3eec231 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 "misc/thread_tools.h" #include "osdep/atomic.h" #include "osdep/threads.h" @@ -86,6 +87,7 @@ const demuxer_desc_t *const demuxer_list[] = { }; struct demux_opts { + int enable_cache; int64_t max_bytes; int64_t max_bytes_bw; double min_secs; @@ -102,6 +104,8 @@ struct demux_opts { const struct m_sub_options demux_conf = { .opts = (const struct m_option[]){ + OPT_CHOICE("cache", enable_cache, 0, + ({"no", 0}, {"auto", -1}, {"yes", 1})), OPT_DOUBLE("demuxer-readahead-secs", min_secs, M_OPT_MIN, .min = 0), // (The MAX_BYTES sizes may not be accurate because the max field is // of double type.) @@ -117,6 +121,7 @@ const struct m_sub_options demux_conf = { }, .size = sizeof(struct demux_opts), .defaults = &(const struct demux_opts){ + .enable_cache = -1, // auto .max_bytes = 150 * 1024 * 1024, .max_bytes_bw = 50 * 1024 * 1024, .min_secs = 1.0, @@ -129,11 +134,15 @@ const struct m_sub_options demux_conf = { struct demux_internal { struct mp_log *log; + struct demux_opts *opts; + // The demuxer runs potentially in another thread, so we keep two demuxer // structs; the real demuxer can access the shadow struct only. struct demuxer *d_thread; // accessed by demuxer impl. (producer) struct demuxer *d_user; // accessed by player (consumer) + bool owns_stream; + // The lock protects the packet queues (struct demux_stream), // and the fields below. pthread_mutex_t lock; @@ -144,6 +153,7 @@ struct demux_internal { bool thread_terminate; bool threading; + bool shutdown_async; void (*wakeup_cb)(void *ctx); void *wakeup_cb_ctx; @@ -212,8 +222,6 @@ struct demux_internal { // Transient state. double duration; // Cached state. - bool force_cache_update; - struct stream_cache_info stream_cache_info; int64_t stream_size; // Updated during init only. char *stream_base_filename; @@ -513,23 +521,40 @@ static void update_seek_ranges(struct demux_cached_range *range) range->is_bof = true; range->is_eof = true; + double min_start_pts = MP_NOPTS_VALUE; + double max_end_pts = MP_NOPTS_VALUE; + for (int n = 0; n < range->num_streams; n++) { struct demux_queue *queue = range->streams[n]; if (queue->ds->selected && queue->ds->eager) { - range->seek_start = MP_PTS_MAX(range->seek_start, queue->seek_start); - range->seek_end = MP_PTS_MIN(range->seek_end, queue->seek_end); + if (queue->is_bof) { + min_start_pts = MP_PTS_MIN(min_start_pts, queue->seek_start); + } else { + range->seek_start = + MP_PTS_MAX(range->seek_start, queue->seek_start); + } + + if (queue->is_eof) { + max_end_pts = MP_PTS_MAX(max_end_pts, queue->seek_end); + } else { + range->seek_end = MP_PTS_MIN(range->seek_end, queue->seek_end); + } range->is_eof &= queue->is_eof; range->is_bof &= queue->is_bof; - if (queue->seek_start >= queue->seek_end) { - range->seek_start = range->seek_end = MP_NOPTS_VALUE; - break; - } + bool empty = queue->is_eof && !queue->head; + if (queue->seek_start >= queue->seek_end && !empty) + goto broken; } } + if (range->is_eof) + range->seek_end = max_end_pts; + if (range->is_bof) + range->seek_start = min_start_pts; + // Sparse stream behavior is not very clearly defined, but usually we don't // want it to restrict the range of other streams, unless // This is incorrect in any of these cases: @@ -557,7 +582,12 @@ static void update_seek_ranges(struct demux_cached_range *range) } if (range->seek_start >= range->seek_end) - range->seek_start = range->seek_end = MP_NOPTS_VALUE; + goto broken; + + return; + +broken: + range->seek_start = range->seek_end = MP_NOPTS_VALUE; } // Remove queue->head from the queue. Does not update in->fw_bytes/in->fw_packs. @@ -925,35 +955,108 @@ int demux_get_num_stream(struct demuxer *demuxer) return r; } -void free_demuxer(demuxer_t *demuxer) +static void demux_shutdown(struct demux_internal *in) { - if (!demuxer) - return; - struct demux_internal *in = demuxer->in; - assert(demuxer == in->d_user); - - demux_stop_thread(demuxer); + struct demuxer *demuxer = in->d_user; if (demuxer->desc->close) demuxer->desc->close(in->d_thread); + demuxer->priv = NULL; + in->d_thread->priv = NULL; demux_flush(demuxer); assert(in->total_bytes == 0); + if (in->owns_stream) + free_stream(demuxer->stream); + demuxer->stream = NULL; +} + +static void demux_dealloc(struct demux_internal *in) +{ for (int n = 0; n < in->num_streams; n++) talloc_free(in->streams[n]); pthread_mutex_destroy(&in->lock); pthread_cond_destroy(&in->wakeup); - talloc_free(demuxer); + talloc_free(in->d_user); +} + +void demux_free(struct demuxer *demuxer) +{ + if (!demuxer) + return; + struct demux_internal *in = demuxer->in; + assert(demuxer == in->d_user); + + demux_stop_thread(demuxer); + demux_shutdown(in); + demux_dealloc(in); +} + +// Start closing the demuxer and eventually freeing the demuxer asynchronously. +// You must not access the demuxer once this has been started. Once the demuxer +// is shutdown, the wakeup callback is invoked. Then you need to call +// demux_free_async_finish() to end the operation (it must not be called from +// the wakeup callback). +// This can return NULL. Then the demuxer cannot be free'd asynchronously, and +// you need to call demux_free() instead. +struct demux_free_async_state *demux_free_async(struct demuxer *demuxer) +{ + struct demux_internal *in = demuxer->in; + assert(demuxer == in->d_user); + + if (!in->threading) + return NULL; + + pthread_mutex_lock(&in->lock); + in->thread_terminate = true; + in->shutdown_async = true; + pthread_cond_signal(&in->wakeup); + pthread_mutex_unlock(&in->lock); + + return (struct demux_free_async_state *)demuxer->in; // lies } -void free_demuxer_and_stream(struct demuxer *demuxer) +// As long as state is valid, you can call this to request immediate abort. +// Roughly behaves as demux_cancel_and_free(), except you still need to wait +// for the result. +void demux_free_async_force(struct demux_free_async_state *state) +{ + struct demux_internal *in = (struct demux_internal *)state; // reverse lies + + mp_cancel_trigger(in->d_user->cancel); +} + +// Check whether the demuxer is shutdown yet. If not, return false, and you +// need to call this again in the future (preferably after you were notified by +// the wakeup callback). If yes, deallocate all state, and return true (in +// particular, the state ptr becomes invalid, and the wakeup callback will never +// be called again). +bool demux_free_async_finish(struct demux_free_async_state *state) +{ + struct demux_internal *in = (struct demux_internal *)state; // reverse lies + + pthread_mutex_lock(&in->lock); + bool busy = in->shutdown_async; + pthread_mutex_unlock(&in->lock); + + if (busy) + return false; + + demux_stop_thread(in->d_user); + demux_dealloc(in); + return true; +} + +// Like demux_free(), but trigger an abort, which will force the demuxer to +// terminate immediately. If this wasn't opened with demux_open_url(), there is +// some chance this will accidentally abort other things via demuxer->cancel. +void demux_cancel_and_free(struct demuxer *demuxer) { if (!demuxer) return; - struct stream *s = demuxer->stream; - free_demuxer(demuxer); - free_stream(s); + mp_cancel_trigger(demuxer->cancel); + demux_free(demuxer); } // Start the demuxer thread, which reads ahead packets on its own. @@ -1597,9 +1700,6 @@ static void execute_trackswitch(struct demux_internal *in) if (in->d_thread->desc->control) in->d_thread->desc->control(in->d_thread, DEMUXER_CTRL_SWITCHED_TRACKS, 0); - stream_control(in->d_thread->stream, STREAM_CTRL_SET_READAHEAD, - &(int){any_selected}); - pthread_mutex_lock(&in->lock); } @@ -1648,13 +1748,6 @@ static bool thread_work(struct demux_internal *in) if (read_packet(in)) return true; // read_packet unlocked, so recheck conditions } - if (in->force_cache_update) { - pthread_mutex_unlock(&in->lock); - update_cache(in); - pthread_mutex_lock(&in->lock); - in->force_cache_update = false; - return true; - } return false; } @@ -1663,12 +1756,23 @@ static void *demux_thread(void *pctx) struct demux_internal *in = pctx; mpthread_set_name("demux"); pthread_mutex_lock(&in->lock); + while (!in->thread_terminate) { if (thread_work(in)) continue; pthread_cond_signal(&in->wakeup); pthread_cond_wait(&in->wakeup, &in->lock); } + + if (in->shutdown_async) { + pthread_mutex_unlock(&in->lock); + demux_shutdown(in); + pthread_mutex_lock(&in->lock); + in->shutdown_async = false; + if (in->wakeup_cb) + in->wakeup_cb(in->wakeup_cb_ctx); + } + pthread_mutex_unlock(&in->lock); return NULL; } @@ -2167,6 +2271,19 @@ static void fixup_metadata(struct demux_internal *in) } } +// Return whether "heavy" caching on this stream is enabled. By default, this +// corresponds to whether the source stream is considered in the network. The +// only effect should be adjusting display behavior (of cache stats etc.), and +// possibly switching between which set of options influence cache settings. +bool demux_is_network_cached(demuxer_t *demuxer) +{ + struct demux_internal *in = demuxer->in; + bool use_cache = demuxer->is_network; + if (in->opts->enable_cache >= 0) + use_cache = in->opts->enable_cache == 1; + return use_cache; +} + static struct demuxer *open_given_type(struct mpv_global *global, struct mp_log *log, const struct demuxer_desc *desc, @@ -2182,6 +2299,7 @@ static struct demuxer *open_given_type(struct mpv_global *global, *demuxer = (struct demuxer) { .desc = desc, .stream = stream, + .cancel = stream->cancel, .seekable = stream->seekable, .filepos = -1, .global = global, @@ -2192,14 +2310,14 @@ static struct demuxer *open_given_type(struct mpv_global *global, .access_references = opts->access_references, .events = DEMUX_EVENT_ALL, .duration = -1, + .extended_ctrls = stream->extended_ctrls, }; demuxer->seekable = stream->seekable; - if (demuxer->stream->underlying && !demuxer->stream->underlying->seekable) - demuxer->seekable = false; struct demux_internal *in = demuxer->in = talloc_ptrtype(demuxer, in); *in = (struct demux_internal){ .log = demuxer->log, + .opts = opts, .d_thread = talloc(demuxer, struct demuxer), .d_user = demuxer, .min_secs = opts->min_secs, @@ -2260,10 +2378,8 @@ static struct demuxer *open_given_type(struct mpv_global *global, fixup_metadata(in); in->events = DEMUX_EVENT_ALL; demux_update(demuxer); - stream_control(demuxer->stream, STREAM_CTRL_SET_READAHEAD, - &(int){params ? params->initial_readahead : false}); int seekable = opts->seekable_cache; - if (demuxer->is_network || stream->caching) { + if (demux_is_network_cached(demuxer)) { in->min_secs = MPMAX(in->min_secs, opts->min_secs_cache); if (seekable < 0) seekable = 1; @@ -2287,7 +2403,7 @@ static struct demuxer *open_given_type(struct mpv_global *global, return demuxer; } - free_demuxer(demuxer); + demux_free(demuxer); return NULL; } @@ -2296,6 +2412,9 @@ static const int d_request[] = {DEMUX_CHECK_REQUEST, -1}; static const int d_force[] = {DEMUX_CHECK_FORCE, -1}; // params can be NULL +// If params->does_not_own_stream==false, this does _not_ free the stream if +// opening fails. But if it succeeds, a later demux_free() call will free the +// stream. struct demuxer *demux_open(struct stream *stream, struct demuxer_params *params, struct mpv_global *global) { @@ -2335,6 +2454,8 @@ struct demuxer *demux_open(struct stream *stream, struct demuxer_params *params, if (demuxer) { talloc_steal(demuxer, log); log = NULL; + demuxer->in->owns_stream = + params ? !params->does_not_own_stream : false; goto done; } } @@ -2348,28 +2469,36 @@ done: // Convenience function: open the stream, enable the cache (according to params // and global opts.), open the demuxer. -// (use free_demuxer_and_stream() to free the underlying stream too) // Also for some reason may close the opened stream if it's not needed. +// demuxer->cancel is not the cancel parameter, but is its own object that will +// be a slave (mp_cancel_set_parent()) to provided cancel object. +// demuxer->cancel is automatically freed. struct demuxer *demux_open_url(const char *url, - struct demuxer_params *params, - struct mp_cancel *cancel, - struct mpv_global *global) + struct demuxer_params *params, + struct mp_cancel *cancel, + struct mpv_global *global) { struct demuxer_params dummy = {0}; if (!params) params = &dummy; + assert(!params->does_not_own_stream); // API user error + struct mp_cancel *priv_cancel = mp_cancel_new(NULL); + if (cancel) + mp_cancel_set_parent(priv_cancel, cancel); struct stream *s = stream_create(url, STREAM_READ | params->stream_flags, - cancel, global); - if (!s) + priv_cancel, global); + if (!s) { + talloc_free(priv_cancel); return NULL; - if (!params->disable_cache) - stream_enable_cache_defaults(&s); + } struct demuxer *d = demux_open(s, params, global); if (d) { + talloc_steal(d->in, priv_cancel); demux_maybe_replace_stream(d); } else { params->demuxer_failed = true; free_stream(s); + talloc_free(priv_cancel); } return d; } @@ -2916,15 +3045,12 @@ static void update_cache(struct demux_internal *in) // Don't lock while querying the stream. struct mp_tags *stream_metadata = NULL; - struct stream_cache_info stream_cache_info = {.size = -1}; int64_t stream_size = stream_get_size(stream); stream_control(stream, STREAM_CTRL_GET_METADATA, &stream_metadata); - stream_control(stream, STREAM_CTRL_GET_CACHE_INFO, &stream_cache_info); pthread_mutex_lock(&in->lock); in->stream_size = stream_size; - in->stream_cache_info = stream_cache_info; if (stream_metadata) { for (int n = 0; n < in->num_streams; n++) { struct demux_stream *ds = in->streams[n]->ds; @@ -2939,18 +3065,7 @@ static void update_cache(struct demux_internal *in) // must be called locked static int cached_stream_control(struct demux_internal *in, int cmd, void *arg) { - // If the cache is active, wake up the thread to possibly update cache state. - if (in->stream_cache_info.size >= 0) { - in->force_cache_update = true; - pthread_cond_signal(&in->wakeup); - } - switch (cmd) { - case STREAM_CTRL_GET_CACHE_INFO: - if (in->stream_cache_info.size < 0) - return STREAM_UNSUPPORTED; - *(struct stream_cache_info *)arg = in->stream_cache_info; - return STREAM_OK; case STREAM_CTRL_GET_SIZE: if (in->stream_size < 0) return STREAM_UNSUPPORTED; @@ -3118,7 +3233,7 @@ int demux_stream_control(demuxer_t *demuxer, int ctrl, void *arg) bool demux_cancel_test(struct demuxer *demuxer) { - return mp_cancel_test(demuxer->stream->cancel); + return mp_cancel_test(demuxer->cancel); } struct demux_chapter *demux_copy_chapter_data(struct demux_chapter *c, int num) diff --git a/demux/demux.h b/demux/demux.h index 0150ce1a1a..5b92e97f49 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -174,12 +174,11 @@ struct demuxer_params { bool *matroska_was_valid; struct timeline *timeline; bool disable_timeline; - bool initial_readahead; bstr init_fragment; bool skip_lavf_probing; + bool does_not_own_stream; // if false, stream is free'd on demux_free() // -- demux_open_url() only int stream_flags; - bool disable_cache; // result bool demuxer_failed; }; @@ -202,6 +201,7 @@ typedef struct demuxer { bool fully_read; bool is_network; // opened directly from a network stream bool access_references; // allow opening other files/URLs + bool extended_ctrls; // supports some of BD/DVD/DVB/TV controls // Bitmask of DEMUX_EVENT_* int events; @@ -233,6 +233,9 @@ typedef struct demuxer { struct mp_tags **update_stream_tags; int num_update_stream_tags; + // Triggered when ending demuxing forcefully. Usually bound to the stream too. + struct mp_cancel *cancel; + // Since the demuxer can run in its own thread, and the stream is not // thread-safe, only the demuxer is allowed to access the stream directly. // You can freely use demux_stream_control() to send STREAM_CTRLs. @@ -245,8 +248,13 @@ typedef struct { int aid, vid, sid; //audio, video and subtitle id } demux_program_t; -void free_demuxer(struct demuxer *demuxer); -void free_demuxer_and_stream(struct demuxer *demuxer); +void demux_free(struct demuxer *demuxer); +void demux_cancel_and_free(struct demuxer *demuxer); + +struct demux_free_async_state; +struct demux_free_async_state *demux_free_async(struct demuxer *demuxer); +void demux_free_async_force(struct demux_free_async_state *state); +bool demux_free_async_finish(struct demux_free_async_state *state); void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp); void demuxer_feed_caption(struct sh_stream *stream, demux_packet_t *dp); @@ -307,6 +315,7 @@ void demux_metadata_changed(demuxer_t *demuxer); void demux_update(demuxer_t *demuxer); void demux_disable_cache(demuxer_t *demuxer); +bool demux_is_network_cached(demuxer_t *demuxer); struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d, enum stream_type t, int id); diff --git a/demux/demux_disc.c b/demux/demux_disc.c index 6ab17e69c8..e5c63cea17 100644 --- a/demux/demux_disc.c +++ b/demux/demux_disc.c @@ -285,15 +285,15 @@ static int d_open(demuxer_t *demuxer, enum demux_check check) if (check != DEMUX_CHECK_FORCE) return -1; - struct demuxer_params params = {.force_format = "+lavf"}; + struct demuxer_params params = { + .force_format = "+lavf", + .does_not_own_stream = true, + }; struct stream *cur = demuxer->stream; const char *sname = ""; - while (cur) { - if (cur->info) - sname = cur->info->name; - cur = cur->underlying; // down the caching chain - } + if (cur->info) + sname = cur->info->name; p->is_cdda = strcmp(sname, "cdda") == 0; p->is_dvd = strcmp(sname, "dvd") == 0 || @@ -342,13 +342,15 @@ static int d_open(demuxer_t *demuxer, enum demux_check check) if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) >= 1) demuxer->duration = len; + demuxer->extended_ctrls = true; + return 0; } static void d_close(demuxer_t *demuxer) { struct priv *p = demuxer->priv; - free_demuxer(p->slave); + demux_free(p->slave); } static int d_control(demuxer_t *demuxer, int cmd, void *arg) diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index b1800018c4..058f13cabf 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -42,6 +42,7 @@ #include "common/av_common.h" #include "misc/bstr.h" #include "misc/charset_conv.h" +#include "misc/thread_tools.h" #include "stream/stream.h" #include "demux.h" @@ -781,8 +782,7 @@ static void update_metadata(demuxer_t *demuxer) static int interrupt_cb(void *ctx) { struct demuxer *demuxer = ctx; - lavf_priv_t *priv = demuxer->priv; - return mp_cancel_test(priv->stream->cancel); + return mp_cancel_test(demuxer->cancel); } static int block_io_open(struct AVFormatContext *s, AVIOContext **pb, diff --git a/demux/demux_mkv_timeline.c b/demux/demux_mkv_timeline.c index 3ad24eb8a5..69cf26e681 100644 --- a/demux/demux_mkv_timeline.c +++ b/demux/demux_mkv_timeline.c @@ -39,6 +39,7 @@ #include "options/options.h" #include "options/path.h" #include "misc/bstr.h" +#include "misc/thread_tools.h" #include "common/common.h" #include "common/playlist.h" #include "stream/stream.h" @@ -171,7 +172,6 @@ static bool check_file_seg(struct tl_ctx *ctx, char *filename, int segment) .matroska_wanted_segment = segment, .matroska_was_valid = &was_valid, .disable_timeline = true, - .disable_cache = true, }; struct mp_cancel *cancel = ctx->tl->cancel; if (mp_cancel_test(cancel)) @@ -215,21 +215,12 @@ static bool check_file_seg(struct tl_ctx *ctx, char *filename, int segment) } } - if (stream_wants_cache(d->stream, ctx->opts->stream_cache)) { - free_demuxer_and_stream(d); - params.disable_cache = false; - params.matroska_wanted_uids = ctx->uids; // potentially reallocated, same data - d = demux_open_url(filename, ¶ms, cancel, ctx->global); - if (!d) - return false; - } - ctx->sources[i] = d; return true; } } - free_demuxer_and_stream(d); + demux_free(d); return was_valid; } @@ -263,7 +254,8 @@ static void find_ordered_chapter_sources(struct tl_ctx *ctx) MP_INFO(ctx, "Loading references from '%s'.\n", opts->ordered_chapters_files); struct playlist *pl = - playlist_parse_file(opts->ordered_chapters_files, ctx->global); + playlist_parse_file(opts->ordered_chapters_files, + ctx->tl->cancel, ctx->global); talloc_steal(tmp, pl); for (struct playlist_entry *e = pl ? pl->first : NULL; e; e = e->next) MP_TARRAY_APPEND(tmp, filenames, num_filenames, e->filename); @@ -515,7 +507,7 @@ void build_ordered_chapter_timeline(struct timeline *tl) .global = tl->global, .tl = tl, .demuxer = demuxer, - .opts = mp_get_config_group(ctx, tl->global, NULL), + .opts = mp_get_config_group(ctx, tl->global, GLOBAL_CONFIG), }; if (!ctx->opts->ordered_chapters || !demuxer->access_references) { diff --git a/demux/demux_playlist.c b/demux/demux_playlist.c index 3a65ada451..0aa542534d 100644 --- a/demux/demux_playlist.c +++ b/demux/demux_playlist.c @@ -25,6 +25,7 @@ #include "options/options.h" #include "common/msg.h" #include "common/playlist.h" +#include "misc/thread_tools.h" #include "options/path.h" #include "stream/stream.h" #include "osdep/io.h" diff --git a/demux/demux_timeline.c b/demux/demux_timeline.c index 4b9b3124fa..1eb73956c3 100644 --- a/demux/demux_timeline.c +++ b/demux/demux_timeline.c @@ -147,7 +147,7 @@ static void close_lazy_segments(struct demuxer *demuxer) for (int n = 0; n < p->num_segments; n++) { struct segment *seg = p->segments[n]; if (seg != p->current && seg->d && seg->lazy) { - free_demuxer_and_stream(seg->d); + demux_free(seg->d); seg->d = NULL; } } @@ -167,7 +167,7 @@ static void reopen_lazy_segments(struct demuxer *demuxer) .skip_lavf_probing = true, }; p->current->d = demux_open_url(p->current->url, ¶ms, - demuxer->stream->cancel, demuxer->global); + demuxer->cancel, demuxer->global); if (!p->current->d && !demux_cancel_test(demuxer)) MP_ERR(demuxer, "failed to load segment\n"); if (p->current->d) @@ -431,7 +431,7 @@ static void d_close(struct demuxer *demuxer) p->current = NULL; close_lazy_segments(demuxer); timeline_destroy(p->tl); - free_demuxer(master); + demux_free(master); } static int d_control(struct demuxer *demuxer, int cmd, void *arg) diff --git a/demux/demux_tv.c b/demux/demux_tv.c index 0e9bee4317..79cff5d79d 100644 --- a/demux/demux_tv.c +++ b/demux/demux_tv.c @@ -181,6 +181,8 @@ no_audio: if(funcs->control(tvh->priv,TVI_CONTROL_VID_SET_GAIN,&tvh->tv_param->gain)!=TVI_CONTROL_TRUE) MP_WARN(tvh, "Unable to set gain control!\n"); + demuxer->extended_ctrls = true; + return 0; } diff --git a/demux/timeline.c b/demux/timeline.c index 700a6dfd05..c44f67b166 100644 --- a/demux/timeline.c +++ b/demux/timeline.c @@ -14,7 +14,7 @@ struct timeline *timeline_load(struct mpv_global *global, struct mp_log *log, *tl = (struct timeline){ .global = global, .log = log, - .cancel = demuxer->stream->cancel, + .cancel = demuxer->cancel, .demuxer = demuxer, .track_layout = demuxer, }; @@ -34,9 +34,9 @@ void timeline_destroy(struct timeline *tl) for (int n = 0; n < tl->num_sources; n++) { struct demuxer *d = tl->sources[n]; if (d != tl->demuxer && d != tl->track_layout) - free_demuxer_and_stream(d); + demux_free(d); } if (tl->track_layout && tl->track_layout != tl->demuxer) - free_demuxer_and_stream(tl->track_layout); + demux_free(tl->track_layout); talloc_free(tl); } |