summaryrefslogtreecommitdiffstats
path: root/demux
diff options
context:
space:
mode:
authorAnton Kindestam <antonki@kth.se>2018-12-05 19:02:03 +0100
committerAnton Kindestam <antonki@kth.se>2018-12-05 19:19:24 +0100
commit8b83c8996686072bc743b112ae5cb3bf93aa33ed (patch)
treeb09ce6a7ff470b05006622f19914b3d39d2f7d9f /demux
parent5bcac8580df6fc62323136f756a3a6d1e754fe9c (diff)
parent559a400ac36e75a8d73ba263fd7fa6736df1c2da (diff)
downloadmpv-8b83c8996686072bc743b112ae5cb3bf93aa33ed.tar.bz2
mpv-8b83c8996686072bc743b112ae5cb3bf93aa33ed.tar.xz
Merge commit '559a400ac36e75a8d73ba263fd7fa6736df1c2da' into wm4-commits--merge-edition
This bumps libmpv version to 1.103
Diffstat (limited to 'demux')
-rw-r--r--demux/demux.c235
-rw-r--r--demux/demux.h17
-rw-r--r--demux/demux_disc.c16
-rw-r--r--demux/demux_lavf.c4
-rw-r--r--demux/demux_mkv_timeline.c18
-rw-r--r--demux/demux_playlist.c1
-rw-r--r--demux/demux_timeline.c6
-rw-r--r--demux/demux_tv.c2
-rw-r--r--demux/timeline.c6
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, &params, 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, &params,
- 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);
}