summaryrefslogtreecommitdiffstats
path: root/demux
diff options
context:
space:
mode:
Diffstat (limited to 'demux')
-rw-r--r--demux/demux.c334
-rw-r--r--demux/demux.h16
-rw-r--r--demux/demux_disc.c2
-rw-r--r--demux/demux_lavf.c67
-rw-r--r--demux/demux_libarchive.c19
-rw-r--r--demux/demux_mkv.c21
-rw-r--r--demux/demux_raw.c19
-rw-r--r--demux/demux_timeline.c10
-rw-r--r--demux/stheader.h2
9 files changed, 294 insertions, 196 deletions
diff --git a/demux/demux.c b/demux/demux.c
index 6038d244e0..648e629f77 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -119,16 +119,20 @@ struct demux_internal {
int max_packs;
int max_bytes;
+ // Set if we know that we are at the start of the file. This is used to
+ // avoid a redundant initial seek after enabling streams. We could just
+ // allow it, but to avoid buggy seeking affecting normal playback, we don't.
+ bool initial_state;
+
bool tracks_switched; // thread needs to inform demuxer of this
bool seeking; // there's a seek queued
int seek_flags; // flags for next seek (if seeking==true)
double seek_pts;
- bool refresh_seeks_enabled;
- bool start_refresh_seek;
+ double ref_pts; // assumed player position (only for track switches)
- double ts_offset; // timestamp offset to apply everything
+ double ts_offset; // timestamp offset to apply to everything
void (*run_fn)(void *); // if non-NULL, function queued to be run on
void *run_fn_arg; // the thread as run_fn(run_fn_arg)
@@ -152,7 +156,10 @@ struct demux_stream {
// if false, this stream is disabled, or passively
// read (like subtitles)
bool eof; // end of demuxed stream? (true if all buffer empty)
+ bool need_refresh; // enabled mid-stream
bool refreshing;
+ bool correct_dts; // packet DTS is strictly monotonically increasing
+ bool correct_pos; // packet pos is strictly monotonically increasing
size_t packs; // number of packets in buffer
size_t bytes; // total bytes of packets in buffer
double base_ts; // timestamp of the last packet returned to decoder
@@ -161,12 +168,12 @@ struct demux_stream {
size_t last_br_bytes; // summed packet sizes since last bitrate calculation
double bitrate;
int64_t last_pos;
+ double last_dts;
struct demux_packet *head;
struct demux_packet *tail;
// for closed captions (demuxer_feed_caption)
struct sh_stream *cc;
-
};
// Return "a", or if that is NOPTS, return "def".
@@ -199,7 +206,10 @@ static void ds_flush(struct demux_stream *ds)
ds->eof = false;
ds->active = false;
ds->refreshing = false;
+ ds->need_refresh = false;
ds->last_pos = -1;
+ ds->last_dts = MP_NOPTS_VALUE;
+ ds->correct_dts = ds->correct_pos = true;
}
void demux_set_ts_offset(struct demuxer *demuxer, double offset)
@@ -222,6 +232,7 @@ struct sh_stream *demux_alloc_sh_stream(enum stream_type type)
.ff_index = -1, // may be overwritten by demuxer
.demuxer_id = -1, // ... same
.codec = talloc_zero(sh, struct mp_codec_params),
+ .tags = talloc_zero(sh, struct mp_tags),
};
sh->codec->type = type;
return sh;
@@ -266,6 +277,33 @@ void demux_add_sh_stream(struct demuxer *demuxer, struct sh_stream *sh)
pthread_mutex_unlock(&in->lock);
}
+// Update sh->tags (lazily). This must be called by demuxers which update
+// stream tags after init. (sh->tags can be accessed by the playback thread,
+// which means the demuxer thread cannot write or read it directly.)
+// Before init is finished, sh->tags can still be accessed freely.
+// Ownership of tags goes to the function.
+void demux_set_stream_tags(struct demuxer *demuxer, struct sh_stream *sh,
+ struct mp_tags *tags)
+{
+ struct demux_internal *in = demuxer->in;
+ assert(demuxer == in->d_thread);
+
+ if (sh->ds) {
+ while (demuxer->num_update_stream_tags <= sh->index) {
+ MP_TARRAY_APPEND(demuxer, demuxer->update_stream_tags,
+ demuxer->num_update_stream_tags, NULL);
+ }
+ talloc_free(demuxer->update_stream_tags[sh->index]);
+ demuxer->update_stream_tags[sh->index] = talloc_steal(demuxer, tags);
+
+ demux_changed(demuxer, DEMUX_EVENT_METADATA);
+ } else {
+ // not added yet
+ talloc_free(sh->tags);
+ sh->tags = talloc_steal(sh, tags);
+ }
+}
+
// Return a stream with the given index. Since streams can only be added during
// the lifetime of the demuxer, it is guaranteed that an index within the valid
// range [0, demux_get_num_stream()) always returns a valid sh_stream pointer,
@@ -389,6 +427,59 @@ void demuxer_feed_caption(struct sh_stream *stream, demux_packet_t *dp)
demux_add_packet(sh, dp);
}
+// An obscure mechanism to get stream switching to be executed faster.
+// On a switch, it seeks back, and then grabs all packets that were
+// "missing" from the packet queue of the newly selected stream.
+// Returns MP_NOPTS_VALUE if no seek should happen.
+static double get_refresh_seek_pts(struct demux_internal *in)
+{
+ struct demuxer *demux = in->d_thread;
+
+ double start_ts = in->ref_pts;
+ bool needed = false;
+ bool normal_seek = true;
+ bool refresh_possible = true;
+ for (int n = 0; n < in->num_streams; n++) {
+ struct demux_stream *ds = in->streams[n]->ds;
+
+ if (!ds->selected)
+ continue;
+
+ if (ds->type == STREAM_VIDEO || ds->type == STREAM_AUDIO)
+ start_ts = MP_PTS_MIN(start_ts, ds->base_ts);
+
+ needed |= ds->need_refresh;
+ // If there were no other streams selected, we can use a normal seek.
+ normal_seek &= ds->need_refresh;
+ ds->need_refresh = false;
+
+ refresh_possible &= ds->correct_dts || ds->correct_pos;
+ }
+
+ if (!needed || start_ts == MP_NOPTS_VALUE || !demux->desc->seek ||
+ !demux->seekable || demux->partially_seekable)
+ return MP_NOPTS_VALUE;
+
+ if (normal_seek)
+ return start_ts;
+
+ if (!refresh_possible) {
+ MP_VERBOSE(in, "can't issue refresh seek\n");
+ return MP_NOPTS_VALUE;
+ }
+
+ for (int n = 0; n < in->num_streams; n++) {
+ struct demux_stream *ds = in->streams[n]->ds;
+ // Streams which didn't have any packets yet will return all packets,
+ // other streams return packets only starting from the last position.
+ if (ds->last_pos != -1 || ds->last_dts != MP_NOPTS_VALUE)
+ ds->refreshing = true;
+ }
+
+ // Seek back to player's current position, with a small offset added.
+ return start_ts - 1.0;
+}
+
void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
{
struct demux_stream *ds = stream ? stream->ds : NULL;
@@ -399,25 +490,34 @@ void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
struct demux_internal *in = ds->in;
pthread_mutex_lock(&in->lock);
- bool drop = false;
+ bool drop = ds->refreshing;
if (ds->refreshing) {
// Resume reading once the old position was reached (i.e. we start
// returning packets where we left off before the refresh).
- drop = dp->pos <= ds->last_pos;
- if (dp->pos >= ds->last_pos)
- ds->refreshing = false;
+ // If it's the same position, drop, but continue normally next time.
+ if (ds->correct_dts) {
+ ds->refreshing = dp->dts < ds->last_dts;
+ } else if (ds->correct_pos) {
+ ds->refreshing = dp->pos < ds->last_pos;
+ } else {
+ ds->refreshing = false; // should not happen
+ }
}
- if (!ds->selected || in->seeking || drop) {
+ if (!ds->selected || ds->need_refresh || in->seeking || drop) {
pthread_mutex_unlock(&in->lock);
talloc_free(dp);
return;
}
+ ds->correct_pos &= dp->pos >= 0 && dp->pos > ds->last_pos;
+ ds->correct_dts &= dp->dts != MP_NOPTS_VALUE && dp->dts > ds->last_dts;
+ ds->last_pos = dp->pos;
+ ds->last_dts = dp->dts;
+
dp->stream = stream->index;
dp->next = NULL;
- ds->last_pos = dp->pos;
ds->packs++;
ds->bytes += dp->len;
if (ds->tail) {
@@ -505,27 +605,40 @@ static bool read_packet(struct demux_internal *in)
if (!read_more)
return false;
+ double seek_pts = get_refresh_seek_pts(in);
+
// Actually read a packet. Drop the lock while doing so, because waiting
// for disk or network I/O can take time.
in->idle = false;
+ in->initial_state = false;
pthread_mutex_unlock(&in->lock);
+
struct demuxer *demux = in->d_thread;
+
+ if (seek_pts != MP_NOPTS_VALUE) {
+ MP_VERBOSE(in, "refresh seek to %f\n", seek_pts);
+ demux->desc->seek(demux, seek_pts, SEEK_BACKWARD | SEEK_HR);
+ }
+
bool eof = !demux->desc->fill_buffer || demux->desc->fill_buffer(demux) <= 0;
update_cache(in);
+
pthread_mutex_lock(&in->lock);
- if (eof) {
- for (int n = 0; n < in->num_streams; n++)
- in->streams[n]->ds->eof = true;
- // If we had EOF previously, then don't wakeup (avoids wakeup loop)
- if (!in->last_eof) {
- if (in->wakeup_cb)
- in->wakeup_cb(in->wakeup_cb_ctx);
- pthread_cond_signal(&in->wakeup);
- MP_VERBOSE(in, "EOF reached.\n");
+ if (!in->seeking) {
+ if (eof) {
+ for (int n = 0; n < in->num_streams; n++)
+ in->streams[n]->ds->eof = true;
+ // If we had EOF previously, then don't wakeup (avoids wakeup loop)
+ if (!in->last_eof) {
+ if (in->wakeup_cb)
+ in->wakeup_cb(in->wakeup_cb_ctx);
+ pthread_cond_signal(&in->wakeup);
+ MP_VERBOSE(in, "EOF reached.\n");
+ }
}
+ in->eof = in->last_eof = eof;
}
- in->eof = in->last_eof = eof;
return true;
}
@@ -550,42 +663,6 @@ static void ds_get_packets(struct demux_stream *ds)
}
}
-// An obscure mechanism to get stream switching to be executed faster.
-// On a switch, it seeks back, and then grabs all packets that were
-// "missing" from the packet queue of the newly selected stream.
-static void start_refreshing(struct demux_internal *in)
-{
- struct demuxer *demux = in->d_thread;
-
- in->start_refresh_seek = false;
-
- double start_ts = MP_NOPTS_VALUE;
- for (int n = 0; n < in->num_streams; n++) {
- struct demux_stream *ds = in->streams[n]->ds;
- if (ds->type == STREAM_VIDEO || ds->type == STREAM_AUDIO)
- start_ts = MP_PTS_MIN(start_ts, ds->base_ts);
- }
-
- if (start_ts == MP_NOPTS_VALUE || !demux->desc->seek || !demux->seekable ||
- demux->partially_seekable || !demux->allow_refresh_seeks)
- return;
-
- for (int n = 0; n < in->num_streams; n++) {
- struct demux_stream *ds = in->streams[n]->ds;
- // Streams which didn't read any packets yet can return all packets,
- // or they'd be stuck forever; affects newly selected streams too.
- if (ds->last_pos != -1)
- ds->refreshing = true;
- }
-
- pthread_mutex_unlock(&in->lock);
-
- // Seek back to player's current position, with a small offset added.
- in->d_thread->desc->seek(in->d_thread, start_ts - 1.0, SEEK_BACKWARD | SEEK_HR);
-
- pthread_mutex_lock(&in->lock);
-}
-
static void execute_trackswitch(struct demux_internal *in)
{
in->tracks_switched = false;
@@ -603,9 +680,6 @@ static void execute_trackswitch(struct demux_internal *in)
&(int){any_selected});
pthread_mutex_lock(&in->lock);
-
- if (in->start_refresh_seek)
- start_refreshing(in);
}
static void execute_seek(struct demux_internal *in)
@@ -613,6 +687,7 @@ static void execute_seek(struct demux_internal *in)
int flags = in->seek_flags;
double pts = in->seek_pts;
in->seeking = false;
+ in->initial_state = false;
pthread_mutex_unlock(&in->lock);
@@ -853,17 +928,18 @@ static int decode_float(char *str, float *out)
return 0;
}
-static int decode_gain(demuxer_t *demuxer, const char *tag, float *out)
+static int decode_gain(struct mp_log *log, struct mp_tags *tags,
+ const char *tag, float *out)
{
char *tag_val = NULL;
float dec_val;
- tag_val = mp_tags_get_str(demuxer->metadata, tag);
+ tag_val = mp_tags_get_str(tags, tag);
if (!tag_val)
return -1;
- if (decode_float(tag_val, &dec_val)) {
- mp_msg(demuxer->log, MSGL_ERR, "Invalid replaygain value\n");
+ if (decode_float(tag_val, &dec_val) < 0) {
+ mp_msg(log, MSGL_ERR, "Invalid replaygain value\n");
return -1;
}
@@ -871,59 +947,65 @@ static int decode_gain(demuxer_t *demuxer, const char *tag, float *out)
return 0;
}
-static int decode_peak(demuxer_t *demuxer, const char *tag, float *out)
+static int decode_peak(struct mp_log *log, struct mp_tags *tags,
+ const char *tag, float *out)
{
char *tag_val = NULL;
float dec_val;
*out = 1.0;
- tag_val = mp_tags_get_str(demuxer->metadata, tag);
+ tag_val = mp_tags_get_str(tags, tag);
if (!tag_val)
return 0;
- if (decode_float(tag_val, &dec_val))
- return 0;
-
- if (dec_val == 0.0)
- return 0;
+ if (decode_float(tag_val, &dec_val) < 0 || dec_val <= 0.0)
+ return -1;
*out = dec_val;
return 0;
}
-static void apply_replaygain(demuxer_t *demuxer, struct replaygain_data *rg)
-{
- struct demux_internal *in = demuxer->in;
- for (int n = 0; n < in->num_streams; n++) {
- struct sh_stream *sh = in->streams[n];
- if (sh->type == STREAM_AUDIO && !sh->codec->replaygain_data) {
- MP_VERBOSE(demuxer, "Replaygain: Track=%f/%f Album=%f/%f\n",
- rg->track_gain, rg->track_peak,
- rg->album_gain, rg->album_peak);
- sh->codec->replaygain_data = talloc_memdup(in, rg, sizeof(*rg));
- }
- }
-}
-
-static void demux_export_replaygain(demuxer_t *demuxer)
+static struct replaygain_data *decode_rgain(struct mp_log *log,
+ struct mp_tags *tags)
{
struct replaygain_data rg = {0};
- if (!decode_gain(demuxer, "REPLAYGAIN_TRACK_GAIN", &rg.track_gain) &&
- !decode_peak(demuxer, "REPLAYGAIN_TRACK_PEAK", &rg.track_peak) &&
- !decode_gain(demuxer, "REPLAYGAIN_ALBUM_GAIN", &rg.album_gain) &&
- !decode_peak(demuxer, "REPLAYGAIN_ALBUM_PEAK", &rg.album_peak))
+ if (decode_gain(log, tags, "REPLAYGAIN_TRACK_GAIN", &rg.track_gain) >= 0 &&
+ decode_peak(log, tags, "REPLAYGAIN_TRACK_PEAK", &rg.track_peak) >= 0)
{
- apply_replaygain(demuxer, &rg);
+ if (decode_gain(log, tags, "REPLAYGAIN_ALBUM_GAIN", &rg.album_gain) < 0 ||
+ decode_peak(log, tags, "REPLAYGAIN_ALBUM_PEAK", &rg.album_peak) < 0)
+ {
+ rg.album_gain = rg.track_gain;
+ rg.album_peak = rg.track_peak;
+ }
+ return talloc_memdup(NULL, &rg, sizeof(rg));
}
- if (!decode_gain(demuxer, "REPLAYGAIN_GAIN", &rg.track_gain) &&
- !decode_peak(demuxer, "REPLAYGAIN_PEAK", &rg.track_peak))
+ if (decode_gain(log, tags, "REPLAYGAIN_GAIN", &rg.track_gain) >= 0 &&
+ decode_peak(log, tags, "REPLAYGAIN_PEAK", &rg.track_peak) >= 0)
{
rg.album_gain = rg.track_gain;
rg.album_peak = rg.track_peak;
- apply_replaygain(demuxer, &rg);
+ return talloc_memdup(NULL, &rg, sizeof(rg));
+ }
+
+ return NULL;
+}
+
+static void demux_update_replaygain(demuxer_t *demuxer)
+{
+ struct demux_internal *in = demuxer->in;
+ for (int n = 0; n < in->num_streams; n++) {
+ struct sh_stream *sh = in->streams[n];
+ if (sh->type == STREAM_AUDIO && !sh->codec->replaygain_data) {
+ struct replaygain_data *rg = decode_rgain(demuxer->log, sh->tags);
+ if (!rg)
+ rg = decode_rgain(demuxer->log, demuxer->metadata);
+ if (rg)
+ sh->codec->replaygain_data = talloc_steal(in, rg);
+ }
}
}
@@ -947,15 +1029,29 @@ static void demux_copy(struct demuxer *dst, struct demuxer *src)
dst->partially_seekable = src->partially_seekable;
dst->filetype = src->filetype;
dst->ts_resets_possible = src->ts_resets_possible;
- dst->allow_refresh_seeks = src->allow_refresh_seeks;
dst->fully_read = src->fully_read;
dst->start_time = src->start_time;
dst->priv = src->priv;
}
+
if (src->events & DEMUX_EVENT_METADATA) {
talloc_free(dst->metadata);
dst->metadata = mp_tags_dup(dst, src->metadata);
+
+ if (dst->num_update_stream_tags != src->num_update_stream_tags) {
+ talloc_free(dst->update_stream_tags);
+ dst->update_stream_tags =
+ talloc_zero_array(dst, struct mp_tags *, dst->num_update_stream_tags);
+ dst->num_update_stream_tags = src->num_update_stream_tags;
+ }
+ for (int n = 0; n < dst->num_update_stream_tags; n++) {
+ talloc_free(dst->update_stream_tags[n]);
+ dst->update_stream_tags[n] =
+ talloc_steal(dst->update_stream_tags, src->update_stream_tags[n]);
+ src->update_stream_tags[n] = NULL;
+ }
}
+
dst->events |= src->events;
src->events = 0;
}
@@ -977,8 +1073,6 @@ void demux_changed(demuxer_t *demuxer, int events)
if (demuxer->events & DEMUX_EVENT_INIT)
demuxer_sort_chapters(demuxer);
- if (demuxer->events & (DEMUX_EVENT_METADATA | DEMUX_EVENT_STREAMS))
- demux_export_replaygain(demuxer);
demux_copy(in->d_buffer, demuxer);
@@ -1001,8 +1095,28 @@ void demux_update(demuxer_t *demuxer)
demux_copy(demuxer, in->d_buffer);
demuxer->events |= in->events;
in->events = 0;
- if (in->stream_metadata && (demuxer->events & DEMUX_EVENT_METADATA))
- mp_tags_merge(demuxer->metadata, in->stream_metadata);
+ if (demuxer->events & DEMUX_EVENT_METADATA) {
+ int num_streams = MPMIN(in->num_streams, demuxer->num_update_stream_tags);
+ for (int n = 0; n < num_streams; n++) {
+ struct mp_tags *tags = demuxer->update_stream_tags[n];
+ demuxer->update_stream_tags[n] = NULL;
+ if (tags) {
+ struct sh_stream *sh = in->streams[n];
+ talloc_free(sh->tags);
+ sh->tags = talloc_steal(sh, tags);
+ }
+ }
+
+ // Often useful audio-only files, which have metadata in the audio track
+ // metadata instead of the main metadata (especially OGG).
+ if (in->num_streams == 1)
+ mp_tags_merge(demuxer->metadata, in->streams[0]->tags);
+
+ if (in->stream_metadata)
+ mp_tags_merge(demuxer->metadata, in->stream_metadata);
+ }
+ if (demuxer->events & (DEMUX_EVENT_METADATA | DEMUX_EVENT_STREAMS))
+ demux_update_replaygain(demuxer);
pthread_mutex_unlock(&in->lock);
}
@@ -1074,6 +1188,7 @@ static struct demuxer *open_given_type(struct mpv_global *global,
.min_secs = demuxer->opts->demuxer_min_secs,
.max_packs = demuxer->opts->demuxer_max_packs,
.max_bytes = demuxer->opts->demuxer_max_bytes,
+ .initial_state = true,
};
pthread_mutex_init(&in->lock, NULL);
pthread_cond_init(&in->wakeup, NULL);
@@ -1277,18 +1392,6 @@ int demux_seek(demuxer_t *demuxer, double seek_pts, int flags)
return 1;
}
-// Enable doing a "refresh seek" on the next stream switch.
-// Note that this by design does not disable ongoing refresh seeks, and
-// does not affect previous stream switch commands (even if they were
-// asynchronous).
-void demux_set_enable_refresh_seeks(struct demuxer *demuxer, bool enabled)
-{
- struct demux_internal *in = demuxer->in;
- pthread_mutex_lock(&in->lock);
- in->refresh_seeks_enabled = enabled;
- pthread_mutex_unlock(&in->lock);
-}
-
struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
enum stream_type t, int id)
{
@@ -1301,19 +1404,22 @@ struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
return NULL;
}
+// Set whether the given stream should return packets.
+// ref_pts is used only if the stream is enabled. Then it serves as approximate
+// start pts for this stream (in the worst case it is ignored).
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
- bool selected)
+ double ref_pts, bool selected)
{
struct demux_internal *in = demuxer->in;
pthread_mutex_lock(&in->lock);
// don't flush buffers if stream is already selected / unselected
if (stream->ds->selected != selected) {
stream->ds->selected = selected;
- stream->ds->active = false;
ds_flush(stream->ds);
in->tracks_switched = true;
- if (selected && in->refresh_seeks_enabled)
- in->start_refresh_seek = true;
+ stream->ds->need_refresh = selected && !in->initial_state;
+ if (stream->ds->need_refresh)
+ in->ref_pts = MP_ADD_PTS(ref_pts, -in->ts_offset);
if (in->threading) {
pthread_cond_signal(&in->wakeup);
} else {
diff --git a/demux/demux.h b/demux/demux.h
index a26dada6c8..07803d2838 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -178,12 +178,6 @@ typedef struct demuxer {
double start_time;
// File format allows PTS resets (even if the current file is without)
bool ts_resets_possible;
- // Enable fast track switching hacks. This requires from the demuxer:
- // - seeking is somewhat reliable; packet contents must not change
- // - packet position (demux_packet.pos) is set, not negative, unique, and
- // monotonically increasing
- // - seeking leaves packet positions invariant
- bool allow_refresh_seeks;
// The file data was fully read, and there is no need to keep the stream
// open, keep the cache active, or to run the demuxer thread. Generating
// packets is not slow either (unlike e.g. libavdevice pseudo-demuxers).
@@ -216,7 +210,10 @@ typedef struct demuxer {
struct mp_log *log, *glog;
struct demuxer_params *params;
- struct demux_internal *in; // internal to demux.c
+ // internal to demux.c
+ struct demux_internal *in;
+ struct mp_tags **update_stream_tags;
+ int num_update_stream_tags;
// 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.
@@ -265,13 +262,12 @@ bool demux_cancel_test(struct demuxer *demuxer);
void demux_flush(struct demuxer *demuxer);
int demux_seek(struct demuxer *demuxer, double rel_seek_secs, int flags);
-void demux_set_enable_refresh_seeks(struct demuxer *demuxer, bool enabled);
void demux_set_ts_offset(struct demuxer *demuxer, double offset);
int demux_control(struct demuxer *demuxer, int cmd, void *arg);
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
- bool selected);
+ double ref_pts, bool selected);
void demux_set_stream_autoselect(struct demuxer *demuxer, bool autoselect);
void demuxer_help(struct mp_log *log);
@@ -280,6 +276,8 @@ int demuxer_add_attachment(struct demuxer *demuxer, char *name,
char *type, void *data, size_t data_size);
int demuxer_add_chapter(demuxer_t *demuxer, char *name,
double pts, uint64_t demuxer_id);
+void demux_set_stream_tags(struct demuxer *demuxer, struct sh_stream *sh,
+ struct mp_tags *tags);
double demuxer_get_time_length(struct demuxer *demuxer);
diff --git a/demux/demux_disc.c b/demux/demux_disc.c
index 91b87a4631..805ba4ccff 100644
--- a/demux/demux_disc.c
+++ b/demux/demux_disc.c
@@ -57,7 +57,7 @@ static void reselect_streams(demuxer_t *demuxer)
for (int n = 0; n < MPMIN(num_slave, p->num_streams); n++) {
if (p->streams[n]) {
demuxer_select_track(p->slave, demux_get_stream(p->slave, n),
- demux_stream_is_selected(p->streams[n]));
+ MP_NOPTS_VALUE, demux_stream_is_selected(p->streams[n]));
}
}
}
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index a6f51dc5f3..e28ebd0c23 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -168,7 +168,6 @@ typedef struct lavf_priv {
int num_streams;
int cur_program;
char *mime_type;
- bool merge_track_metadata;
double seek_delay;
} lavf_priv_t;
@@ -502,7 +501,7 @@ static void select_tracks(struct demuxer *demuxer, int start)
}
}
-static void export_replaygain(demuxer_t *demuxer, struct mp_codec_params *c,
+static void export_replaygain(demuxer_t *demuxer, struct sh_stream *sh,
AVStream *st)
{
for (int i = 0; i < st->nb_side_data; i++) {
@@ -528,7 +527,10 @@ static void export_replaygain(demuxer_t *demuxer, struct mp_codec_params *c,
rgain->album_peak = (av_rgain->album_peak != 0.0) ?
av_rgain->album_peak / 100000.0f : 1.0;
- c->replaygain_data = rgain;
+ // This must be run only before the stream was added, otherwise there
+ // will be race conditions with accesses from the user thread.
+ assert(!sh->ds);
+ sh->codec->replaygain_data = rgain;
}
}
@@ -575,7 +577,7 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
delay = lavc_delay / (double)codec->sample_rate;
priv->seek_delay = MPMAX(priv->seek_delay, delay);
- export_replaygain(demuxer, sh->codec, st);
+ export_replaygain(demuxer, sh, st);
break;
}
@@ -681,6 +683,7 @@ static void handle_new_stream(demuxer_t *demuxer, int i)
if (!sh->title && sh->hls_bitrate > 0)
sh->title = talloc_asprintf(sh, "bitrate %d", sh->hls_bitrate);
sh->missing_timestamps = !!(priv->avif_flags & AVFMT_NOTIMESTAMPS);
+ mp_tags_copy_from_av_dictionary(sh->tags, st->metadata);
demux_add_sh_stream(demuxer, sh);
}
@@ -703,14 +706,14 @@ static void update_metadata(demuxer_t *demuxer, AVPacket *pkt)
priv->avfc->event_flags = 0;
demux_changed(demuxer, DEMUX_EVENT_METADATA);
}
- if (priv->merge_track_metadata) {
- for (int n = 0; n < priv->num_streams; n++) {
- AVStream *st = priv->streams[n] ? priv->avfc->streams[n] : NULL;
- if (st && st->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
- mp_tags_copy_from_av_dictionary(demuxer->metadata, st->metadata);
- st->event_flags = 0;
- demux_changed(demuxer, DEMUX_EVENT_METADATA);
- }
+
+ for (int n = 0; n < priv->num_streams; n++) {
+ AVStream *st = priv->streams[n] ? priv->avfc->streams[n] : NULL;
+ if (st && st->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
+ st->event_flags = 0;
+ struct mp_tags *tags = talloc_zero(NULL, struct mp_tags);
+ mp_tags_copy_from_av_dictionary(tags, st->metadata);
+ demux_set_stream_tags(demuxer, priv->streams[n], tags);
}
}
}
@@ -846,16 +849,6 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
add_new_streams(demuxer);
- // Often useful with OGG audio-only files, which have metadata in the audio
- // track metadata instead of the main metadata.
- if (demux_get_num_stream(demuxer) == 1) {
- priv->merge_track_metadata = true;
- for (int n = 0; n < priv->num_streams; n++) {
- if (priv->streams[n])
- mp_tags_copy_from_av_dictionary(demuxer->metadata, avfc->streams[n]->metadata);
- }
- }
-
mp_tags_copy_from_av_dictionary(demuxer->metadata, avfc->metadata);
update_metadata(demuxer, NULL);
@@ -865,7 +858,6 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
demuxer->start_time = priv->avfc->start_time == AV_NOPTS_VALUE ?
0 : (double)priv->avfc->start_time / AV_TIME_BASE;
- demuxer->allow_refresh_seeks = matches_avinputformat_name(priv, "mp4");
demuxer->fully_read = priv->format_hack.fully_read;
return 0;
@@ -953,30 +945,15 @@ static void demux_seek_lavf(demuxer_t *demuxer, double seek_pts, int flags)
seek_pts_av = seek_pts * AV_TIME_BASE;
}
- int r;
- if (!priv->avfc->iformat->read_seek2) {
- // Normal seeking.
+ int r = av_seek_frame(priv->avfc, -1, seek_pts_av, avsflags);
+ if (r < 0 && (avsflags & AVSEEK_FLAG_BACKWARD)) {
+ // When seeking before the beginning of the file, and seeking fails,
+ // try again without the backwards flag to make it seek to the
+ // beginning.
+ avsflags &= ~AVSEEK_FLAG_BACKWARD;
r = av_seek_frame(priv->avfc, -1, seek_pts_av, avsflags);
- if (r < 0 && (avsflags & AVSEEK_FLAG_BACKWARD)) {
- // When seeking before the beginning of the file, and seeking fails,
- // try again without the backwards flag to make it seek to the
- // beginning.
- avsflags &= ~AVSEEK_FLAG_BACKWARD;
- r = av_seek_frame(priv->avfc, -1, seek_pts_av, avsflags);
- }
- } else {
- // av_seek_frame() won't work. Use "new" seeking API. We don't use this
- // API by default, because there are some major issues.
- // Set max_ts==ts, so that demuxing starts from an earlier position in
- // the worst case.
- r = avformat_seek_file(priv->avfc, -1, INT64_MIN,
- seek_pts_av, seek_pts_av, avsflags);
- // Similar issue as in the normal seeking codepath.
- if (r < 0) {
- r = avformat_seek_file(priv->avfc, -1, INT64_MIN,
- seek_pts_av, INT64_MAX, avsflags);
- }
}
+
if (r < 0) {
char buf[180];
av_strerror(r, buf, sizeof(buf));
diff --git a/demux/demux_libarchive.c b/demux/demux_libarchive.c
index 3f1a655726..dcdbe65fc0 100644
--- a/demux/demux_libarchive.c
+++ b/demux/demux_libarchive.c
@@ -65,23 +65,10 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
char **files = NULL;
int num_files = 0;
- for (;;) {
- struct archive_entry *entry;
- int r = archive_read_next_header(mpa->arch, &entry);
- if (r == ARCHIVE_EOF)
- break;
- if (r < ARCHIVE_OK)
- MP_ERR(demuxer, "%s\n", archive_error_string(mpa->arch));
- if (r < ARCHIVE_WARN)
- break;
- if (archive_entry_filetype(entry) != AE_IFREG)
- continue;
- const char *fn = archive_entry_pathname(entry);
- // Some archives may have no filenames.
- if (!fn)
- fn = talloc_asprintf(mpa, "mpv_unknown#%d\n", num_files);
+ while (mp_archive_next_entry(mpa)) {
// stream_libarchive.c does the real work
- char *f = talloc_asprintf(mpa, "archive://%s|%s", prefix, fn);
+ char *f = talloc_asprintf(mpa, "archive://%s|%s", prefix,
+ mpa->entry_filename);
MP_TARRAY_APPEND(mpa, files, num_files, f);
}
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index f454f5fd20..da15c0fc0f 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -93,6 +93,7 @@ typedef struct mkv_content_encoding {
typedef struct mkv_track {
int tnum;
+ uint64_t uid;
char *name;
struct sh_stream *stream;
@@ -595,6 +596,7 @@ static void parse_trackentry(struct demuxer *demuxer,
} else {
MP_ERR(demuxer, "Missing track number!\n");
}
+ track->uid = entry->track_uid;
if (entry->name) {
track->name = talloc_strdup(track, entry->name);
@@ -986,9 +988,6 @@ static void process_tags(demuxer_t *demuxer)
for (int i = 0; i < tags->n_tag; i++) {
struct ebml_tag tag = tags->tag[i];
- if (tag.targets.target_track_uid || tag.targets.target_attachment_uid)
- continue;
-
struct mp_tags *dst = NULL;
if (tag.targets.target_chapter_uid) {
@@ -1009,6 +1008,19 @@ static void process_tags(demuxer_t *demuxer)
break;
}
}
+ } else if (tag.targets.target_track_uid) {
+ for (int n = 0; n < mkv_d->num_tracks; n++) {
+ if (mkv_d->tracks[n]->uid ==
+ tag.targets.target_track_uid)
+ {
+ struct sh_stream *sh = mkv_d->tracks[n]->stream;
+ if (sh)
+ dst = sh->tags;
+ break;
+ }
+ }
+ } else if (tag.targets.target_attachment_uid) {
+ /* ignore */
} else {
dst = demuxer->metadata;
}
@@ -1924,10 +1936,9 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
MP_VERBOSE(demuxer, "All headers are parsed!\n");
- process_tags(demuxer);
display_create_tracks(demuxer);
add_coverart(demuxer);
- demuxer->allow_refresh_seeks = true;
+ process_tags(demuxer);
probe_first_timestamp(demuxer);
if (opts->demux_mkv->probe_duration)
diff --git a/demux/demux_raw.c b/demux/demux_raw.c
index 0d7517c3f0..bd8e11306d 100644
--- a/demux/demux_raw.c
+++ b/demux/demux_raw.c
@@ -36,7 +36,7 @@
#include "osdep/endian.h"
struct demux_rawaudio_opts {
- struct mp_chmap channels;
+ struct m_channels channels;
int samplerate;
int aformat;