diff options
Diffstat (limited to 'demux')
-rw-r--r-- | demux/demux.c | 334 | ||||
-rw-r--r-- | demux/demux.h | 16 | ||||
-rw-r--r-- | demux/demux_disc.c | 2 | ||||
-rw-r--r-- | demux/demux_lavf.c | 67 | ||||
-rw-r--r-- | demux/demux_libarchive.c | 19 | ||||
-rw-r--r-- | demux/demux_mkv.c | 21 | ||||
-rw-r--r-- | demux/demux_raw.c | 19 | ||||
-rw-r--r-- | demux/demux_timeline.c | 10 | ||||
-rw-r--r-- | demux/stheader.h | 2 |
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; |