Commit 5ccc8432 authored by Tim-Philipp Müller's avatar Tim-Philipp Müller

audiodecoder: fix tag handling

Before we just merged everything in pretty much random ways
ad-hoc instead of keeping state properly. In 0.10 that was
how it worked, but in 1.x the tag events sent should always
reflect the latest state and replace any previous tags.

So save the upstream (stream) tags, and save the tags set
by the decoder subclass with merge mode, and then update
the merged tags whenever either of those two changes.

This slightly changes the behaviour of gst_audio_decoder_merge_tags()
in case it is called multiple times, since now any call replaces
the previously-set tags. However, it leads to much more predictable
outcomes, and also we are not aware of any subclass which sets this
multiple times and expects all the tags set to be merged.

If more complex tag merging scenarios are required, we'll have
to add a new vfunc for that or the subclass has to intercept
the upstream tags itself and send merged tags itself.
parent 14867e4e
......@@ -252,9 +252,15 @@ struct _GstAudioDecoderPrivate
guint sync_flush;
/* error count */
gint error_count;
/* codec id tag */
GstTagList *taglist;
gboolean taglist_changed;
/* upstream stream tags (global tags are passed through as-is) */
GstTagList *upstream_tags;
/* subclass tags */
GstTagList *taglist; /* FIXME: rename to decoder_tags */
GstTagMergeMode decoder_tags_merge_mode;
gboolean taglist_changed; /* FIXME: rename to tags_changed */
/* whether circumstances allow output aggregation */
gint agg;
......@@ -564,6 +570,11 @@ gst_audio_decoder_reset (GstAudioDecoder * dec, gboolean full)
gst_tag_list_unref (dec->priv->taglist);
dec->priv->taglist = NULL;
dec->priv->decoder_tags_merge_mode = GST_TAG_MERGE_KEEP_ALL;
if (dec->priv->upstream_tags) {
gst_tag_list_unref (dec->priv->upstream_tags);
dec->priv->upstream_tags = NULL;
dec->priv->taglist_changed = FALSE;
gst_segment_init (&dec->input_segment, GST_FORMAT_TIME);
......@@ -624,6 +635,32 @@ gst_audio_decoder_finalize (GObject * object)
G_OBJECT_CLASS (parent_class)->finalize (object);
static GstEvent *
gst_audio_decoder_create_merged_tags_event (GstAudioDecoder * dec)
GstTagList *merged_tags;
GST_LOG_OBJECT (dec, "upstream : %" GST_PTR_FORMAT, dec->priv->upstream_tags);
GST_LOG_OBJECT (dec, "decoder : %" GST_PTR_FORMAT, dec->priv->taglist);
GST_LOG_OBJECT (dec, "mode : %d", dec->priv->decoder_tags_merge_mode);
merged_tags =
gst_tag_list_merge (dec->priv->upstream_tags,
dec->priv->taglist, dec->priv->decoder_tags_merge_mode);
GST_DEBUG_OBJECT (dec, "merged : %" GST_PTR_FORMAT, merged_tags);
if (merged_tags == NULL)
return NULL;
if (gst_tag_list_is_empty (merged_tags)) {
gst_tag_list_unref (merged_tags);
return NULL;
return gst_event_new_tag (merged_tags);
static gboolean
gst_audio_decoder_push_event (GstAudioDecoder * dec, GstEvent * event)
......@@ -1369,10 +1406,13 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
/* delayed one-shot stuff until confirmed data */
if (priv->taglist && priv->taglist_changed) {
GST_DEBUG_OBJECT (dec, "codec tag %" GST_PTR_FORMAT, priv->taglist);
if (!gst_tag_list_is_empty (priv->taglist))
gst_audio_decoder_push_event (dec,
gst_event_new_tag (gst_tag_list_ref (priv->taglist)));
GstEvent *tags_event;
tags_event = gst_audio_decoder_create_merged_tags_event (dec);
if (tags_event != NULL)
gst_audio_decoder_push_event (dec, tags_event);
priv->taglist_changed = FALSE;
......@@ -2158,12 +2198,12 @@ gst_audio_decoder_sink_eventfunc (GstAudioDecoder * dec, GstEvent * event)
gst_audio_decoder_flush (dec, FALSE);
GST_DEBUG_OBJECT (dec, "received STREAM_START. Clearing taglist");
/* Flush our merged taglist after a STREAM_START */
if (dec->priv->taglist) {
gst_tag_list_unref (dec->priv->taglist);
dec->priv->taglist = NULL;
/* Flush upstream tags after a STREAM_START */
if (dec->priv->upstream_tags) {
gst_tag_list_unref (dec->priv->upstream_tags);
dec->priv->upstream_tags = NULL;
dec->priv->taglist_changed = FALSE;
dec->priv->taglist_changed = TRUE;
ret = gst_audio_decoder_push_event (dec, event);
......@@ -2292,11 +2332,16 @@ gst_audio_decoder_sink_eventfunc (GstAudioDecoder * dec, GstEvent * event)
gst_event_parse_tag (event, &tags);
if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_STREAM) {
gst_audio_decoder_merge_tags (dec, tags, GST_TAG_MERGE_REPLACE);
if (dec->priv->upstream_tags != tags) {
if (dec->priv->upstream_tags)
gst_tag_list_unref (dec->priv->upstream_tags);
dec->priv->upstream_tags = gst_tag_list_ref (tags);
GST_INFO_OBJECT (dec, "upstream stream tags: %" GST_PTR_FORMAT, tags);
gst_event_unref (event);
event = NULL;
ret = TRUE;
event = gst_audio_decoder_create_merged_tags_event (dec);
/* fall through */
......@@ -3500,36 +3545,39 @@ gst_audio_decoder_get_needs_format (GstAudioDecoder * dec)
* gst_audio_decoder_merge_tags:
* @dec: a #GstAudioDecoder
* @tags: a #GstTagList to merge
* @mode: the #GstTagMergeMode to use
* @tags: (allow-none): a #GstTagList to merge, or NULL
* @mode: the #GstTagMergeMode to use, usually #GST_TAG_MERGE_REPLACE
* Adds tags to so-called pending tags, which will be processed
* before pushing out data downstream.
* Sets the audio decoder tags and how they should be merged with any
* upstream stream tags. This will override any tags previously-set
* with gst_audio_decoder_merge_tags().
* Note that this is provided for convenience, and the subclass is
* not required to use this and can still do tag handling on its own,
* although it should be aware that baseclass already takes care
* of the usual CODEC/AUDIO_CODEC tags.
* MT safe.
* not required to use this and can still do tag handling on its own.
gst_audio_decoder_merge_tags (GstAudioDecoder * dec,
const GstTagList * tags, GstTagMergeMode mode)
GstTagList *otags;
g_return_if_fail (GST_IS_AUDIO_DECODER (dec));
g_return_if_fail (tags == NULL || GST_IS_TAG_LIST (tags));
g_return_if_fail (mode != GST_TAG_MERGE_UNDEFINED);
if (tags)
GST_DEBUG_OBJECT (dec, "merging tags %" GST_PTR_FORMAT, tags);
otags = dec->priv->taglist;
dec->priv->taglist = gst_tag_list_merge (dec->priv->taglist, tags, mode);
if (otags)
gst_tag_list_unref (otags);
dec->priv->taglist_changed = TRUE;
if (dec->priv->taglist != tags) {
if (dec->priv->taglist) {
gst_tag_list_unref (dec->priv->taglist);
dec->priv->taglist = NULL;
dec->priv->decoder_tags_merge_mode = GST_TAG_MERGE_KEEP_ALL;
if (tags) {
dec->priv->taglist = gst_tag_list_ref ((GstTagList *) tags);
dec->priv->decoder_tags_merge_mode = mode;
GST_DEBUG_OBJECT (dec, "setting decoder tags to %" GST_PTR_FORMAT, tags);
dec->priv->taglist_changed = TRUE;
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment