From b9ba9a898a9354b52c2154e25a0b5afcb359c5b1 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 12 Aug 2016 21:39:32 +0200 Subject: demux: add per-track metadata ...and ignore it. The main purpose is for retrieving per-track replaygain tags. Other than that per-track tags are not used or accessed by the playback core yet. The demuxer infrastructure is still not really good with that whole synchronization thing (at least in part due to being inherited from mplayer's single-threaded architecture). A convoluted mechanism is needed to transport the tags from demuxer thread to user thread. Two factors contribute to the complexity: tags can change during playback, and tracks (i.e. struct sh_stream) are not duplicated per thread. In particular, we update the way replaygain tags are retrieved. We first try to use per-track tags (common in Matroska) and global tags (effectively formats like mp3). This part fixes #3405. --- demux/demux.c | 136 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 32 deletions(-) (limited to 'demux/demux.c') diff --git a/demux/demux.c b/demux/demux.c index 5b4b3ec4c2..599218b690 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -232,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; @@ -276,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, @@ -900,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"); + mp_msg(log, MSGL_ERR, "Invalid replaygain value\n"); return -1; } @@ -918,14 +947,15 @@ 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; @@ -939,38 +969,47 @@ static int decode_peak(demuxer_t *demuxer, const char *tag, float *out) 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) && + !decode_peak(log, tags, "REPLAYGAIN_TRACK_PEAK", &rg.track_peak) && + !decode_gain(log, tags, "REPLAYGAIN_ALBUM_GAIN", &rg.album_gain) && + !decode_peak(log, tags, "REPLAYGAIN_ALBUM_PEAK", &rg.album_peak)) { - apply_replaygain(demuxer, &rg); + 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) && + !decode_peak(log, tags, "REPLAYGAIN_PEAK", &rg.track_peak)) { 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) { + MP_VERBOSE(demuxer, "Replaygain/%d: Track=%f/%f Album=%f/%f\n", + sh->index, + rg->track_gain, rg->track_peak, + rg->album_gain, rg->album_peak); + sh->codec->replaygain_data = talloc_steal(in, rg); + } + } } } @@ -998,10 +1037,25 @@ static void demux_copy(struct demuxer *dst, struct demuxer *src) 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; } @@ -1023,8 +1077,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); @@ -1047,8 +1099,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); } -- cgit v1.2.3