Merge branch 'upstream/1.16' into tizen_gst_1.16.2
[platform/upstream/gst-plugins-good.git] / gst / flv / gstflvdemux.c
index 70e7fba..283a76c 100644 (file)
@@ -70,8 +70,8 @@ static GstStaticPadTemplate audio_src_template =
         "audio/mpeg, mpegversion = (int) 4, stream-format = (string) raw, framed = (boolean) TRUE; "
         "audio/x-nellymoser, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 16000, 22050, 44100 }; "
         "audio/x-raw, format = (string) { U8, S16LE }, layout = (string) interleaved, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
-        "audio/x-alaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
-        "audio/x-mulaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
+        "audio/x-alaw, channels = (int) { 1, 2 }, rate = (int) 8000; "
+        "audio/x-mulaw, channels = (int) { 1, 2 }, rate = (int) 8000; "
         "audio/x-speex, channels = (int) 1, rate = (int) 16000;")
     );
 
@@ -114,6 +114,8 @@ static gboolean gst_flv_demux_src_event (GstPad * pad, GstObject * parent,
 
 static GstIndex *gst_flv_demux_get_index (GstElement * element);
 
+static void gst_flv_demux_push_tags (GstFlvDemux * demux);
+
 static void
 gst_flv_demux_parse_and_add_index_entry (GstFlvDemux * demux, GstClockTime ts,
     guint64 pos, gboolean keyframe)
@@ -255,13 +257,17 @@ parse_flv_demux_parse_date_string (const gchar * s)
   GstDateTime *dt = NULL;
   gchar **tokens;
   guint64 d;
-  gchar *endptr;
+  gchar *endptr, *stripped;
   gint i, hh, mm, ss;
   gint year = -1, month = -1, day = -1;
   gint hour = -1, minute = -1, seconds = -1;
 
+  stripped = g_strstrip (g_strdup (s));
+
   /* "Fri Oct 15 15:13:16 2004" needs to be parsed */
-  tokens = g_strsplit (s, " ", -1);
+  tokens = g_strsplit (stripped, " ", -1);
+
+  g_free (stripped);
 
   if (g_strv_length (tokens) != 5)
     goto out;
@@ -364,6 +370,12 @@ gst_flv_demux_parse_metadata_item (GstFlvDemux * demux, GstByteReader * reader,
         demux->h = d;
       } else if (!strcmp (tag_name, "framerate")) {
         demux->framerate = d;
+      } else if (!strcmp (tag_name, "audiodatarate")) {
+        gst_tag_list_add (demux->audio_tags, GST_TAG_MERGE_REPLACE,
+            GST_TAG_NOMINAL_BITRATE, (guint) (d * 1024), NULL);
+      } else if (!strcmp (tag_name, "videodatarate")) {
+        gst_tag_list_add (demux->video_tags, GST_TAG_MERGE_REPLACE,
+            GST_TAG_NOMINAL_BITRATE, (guint) (d * 1024), NULL);
       } else {
         GST_INFO_OBJECT (demux, "Tag \'%s\' not handled", tag_name);
       }
@@ -402,15 +414,16 @@ gst_flv_demux_parse_metadata_item (GstFlvDemux * demux, GstByteReader * reader,
         } else {
           gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
               GST_TAG_DATE_TIME, dt, NULL);
+          gst_date_time_unref (dt);
         }
-        gst_date_time_unref (dt);
       } else if (!strcmp (tag_name, "creator")) {
         gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
             GST_TAG_ARTIST, s, NULL);
       } else if (!strcmp (tag_name, "title")) {
         gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
             GST_TAG_TITLE, s, NULL);
-      } else if (!strcmp (tag_name, "metadatacreator")) {
+      } else if (!strcmp (tag_name, "metadatacreator")
+          || !strcmp (tag_name, "encoder")) {
         gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
             GST_TAG_ENCODER, s, NULL);
       } else {
@@ -557,6 +570,28 @@ error:
   return FALSE;
 }
 
+static void
+gst_flv_demux_clear_tags (GstFlvDemux * demux)
+{
+  GST_DEBUG_OBJECT (demux, "clearing taglist");
+
+  if (demux->taglist) {
+    gst_tag_list_unref (demux->taglist);
+  }
+  demux->taglist = gst_tag_list_new_empty ();
+  gst_tag_list_set_scope (demux->taglist, GST_TAG_SCOPE_GLOBAL);
+
+  if (demux->audio_tags) {
+    gst_tag_list_unref (demux->audio_tags);
+  }
+  demux->audio_tags = gst_tag_list_new_empty ();
+
+  if (demux->video_tags) {
+    gst_tag_list_unref (demux->video_tags);
+  }
+  demux->video_tags = gst_tag_list_new_empty ();
+}
+
 static GstFlowReturn
 gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
 {
@@ -590,6 +625,8 @@ gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
       gboolean end_marker = FALSE;
       GST_DEBUG_OBJECT (demux, "we have a metadata script object");
 
+      gst_flv_demux_clear_tags (demux);
+
       if (!gst_byte_reader_get_uint8 (&reader, &type)) {
         g_free (function_name);
         goto cleanup;
@@ -632,7 +669,7 @@ gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
           goto cleanup;
       }
 
-      demux->push_tags = TRUE;
+      gst_flv_demux_push_tags (demux);
     }
 
     g_free (function_name);
@@ -685,9 +722,9 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag,
     guint32 rate, guint32 channels, guint32 width)
 {
   GstCaps *caps = NULL, *old_caps;
-  gchar *codec_name = NULL;
   gboolean ret = FALSE;
   guint adjusted_rate = rate;
+  guint adjusted_channels = channels;
   GstEvent *event;
   gchar *stream_id;
 
@@ -749,6 +786,16 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag,
         } else {
           adjusted_rate = rate;
         }
+
+        adjusted_channels =
+            gst_codec_utils_aac_get_channels (map.data, map.size);
+
+        if (adjusted_channels && (channels != adjusted_channels)) {
+          GST_LOG_OBJECT (demux, "Ajusting AAC channels %d -> %d", channels,
+              adjusted_channels);
+        } else {
+          adjusted_channels = channels;
+        }
       }
       gst_buffer_unmap (demux->audio_codec_data, &map);
 
@@ -831,7 +878,7 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag,
   }
 
   gst_caps_set_simple (caps, "rate", G_TYPE_INT, adjusted_rate,
-      "channels", G_TYPE_INT, channels, NULL);
+      "channels", G_TYPE_INT, adjusted_channels, NULL);
 
   if (demux->audio_codec_data) {
     gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER,
@@ -867,18 +914,10 @@ done:
     demux->width = width;
 
     if (caps) {
-      codec_name = gst_pb_utils_get_codec_description (caps);
-
-      if (codec_name) {
-        if (demux->taglist == NULL)
-          demux->taglist = gst_tag_list_new_empty ();
-        gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
-            GST_TAG_AUDIO_CODEC, codec_name, NULL);
-        g_free (codec_name);
-      }
-
       GST_DEBUG_OBJECT (demux->audio_pad, "successfully negotiated caps %"
           GST_PTR_FORMAT, caps);
+
+      gst_flv_demux_push_tags (demux);
     } else {
       GST_DEBUG_OBJECT (demux->audio_pad, "delayed setting caps");
     }
@@ -911,25 +950,58 @@ gst_flv_demux_push_src_event (GstFlvDemux * demux, GstEvent * event)
 }
 
 static void
-gst_flv_demux_push_tags (GstFlvDemux * demux)
+gst_flv_demux_add_codec_tag (GstFlvDemux * demux, const gchar * tag,
+    GstPad * pad)
 {
-  if (demux->has_audio && !demux->audio_pad) {
-    GST_DEBUG_OBJECT (demux,
-        "Waiting for audio stream pad to come up before we can push tags");
-    return;
+  if (pad) {
+    GstCaps *caps = gst_pad_get_current_caps (pad);
+
+    if (caps) {
+      gchar *codec_name = gst_pb_utils_get_codec_description (caps);
+
+      if (codec_name) {
+        gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
+            tag, codec_name, NULL);
+        g_free (codec_name);
+      }
+
+      gst_caps_unref (caps);
+    }
   }
-  if (demux->has_video && !demux->video_pad) {
-    GST_DEBUG_OBJECT (demux,
-        "Waiting for video stream pad to come up before we can push tags");
-    return;
+}
+
+static void
+gst_flv_demux_push_tags (GstFlvDemux * demux)
+{
+  gst_flv_demux_add_codec_tag (demux, GST_TAG_AUDIO_CODEC, demux->audio_pad);
+  gst_flv_demux_add_codec_tag (demux, GST_TAG_VIDEO_CODEC, demux->video_pad);
+
+  GST_DEBUG_OBJECT (demux, "pushing %" GST_PTR_FORMAT, demux->taglist);
+
+  gst_flv_demux_push_src_event (demux,
+      gst_event_new_tag (gst_tag_list_copy (demux->taglist)));
+
+#ifdef TIZEN_FEATURE_FLVDEMUX_MODIFICATION
+  GST_DEBUG_OBJECT (demux, "post tag msg %" GST_PTR_FORMAT, demux->taglist);
+
+  /* post message flv tag (for early recive application) */
+  gst_element_post_message (GST_ELEMENT_CAST (demux),
+      gst_message_new_tag (GST_OBJECT_CAST (demux),
+        gst_tag_list_copy (demux->taglist)));
+#endif
+
+  if (demux->audio_pad) {
+    GST_DEBUG_OBJECT (demux->audio_pad, "pushing audio %" GST_PTR_FORMAT,
+        demux->audio_tags);
+    gst_pad_push_event (demux->audio_pad,
+        gst_event_new_tag (gst_tag_list_copy (demux->audio_tags)));
   }
-  if (demux->taglist) {
-    GST_DEBUG_OBJECT (demux, "pushing tags out %" GST_PTR_FORMAT,
-        demux->taglist);
-    gst_tag_list_set_scope (demux->taglist, GST_TAG_SCOPE_GLOBAL);
-    gst_flv_demux_push_src_event (demux, gst_event_new_tag (demux->taglist));
-    demux->taglist = gst_tag_list_new_empty ();
-    demux->push_tags = FALSE;
+
+  if (demux->video_pad) {
+    GST_DEBUG_OBJECT (demux->video_pad, "pushing video %" GST_PTR_FORMAT,
+        demux->video_tags);
+    gst_pad_push_event (demux->video_pad,
+        gst_event_new_tag (gst_tag_list_copy (demux->video_tags)));
   }
 }
 
@@ -1034,7 +1106,7 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
   }
 
   /* codec tags with special rates */
-  if (codec_tag == 5 || codec_tag == 14)
+  if (codec_tag == 5 || codec_tag == 14 || codec_tag == 7 || codec_tag == 8)
     rate = 8000;
   else if ((codec_tag == 4) || (codec_tag == 11))
     rate = 16000;
@@ -1043,9 +1115,44 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
       "%d bits width, codec tag %u (flags %02X)", channels, rate, width,
       codec_tag, flags);
 
+  if (codec_tag == 10) {
+    guint8 aac_packet_type = GST_READ_UINT8 (data + 8);
+
+    switch (aac_packet_type) {
+      case 0:
+      {
+        /* AudioSpecificConfig data */
+        GST_LOG_OBJECT (demux, "got an AAC codec data packet");
+        if (demux->audio_codec_data) {
+          gst_buffer_unref (demux->audio_codec_data);
+        }
+        demux->audio_codec_data =
+            gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY,
+            7 + codec_data, demux->tag_data_size - codec_data);
+
+        /* Use that buffer data in the caps */
+        if (demux->audio_pad)
+          gst_flv_demux_audio_negotiate (demux, codec_tag, rate, channels,
+              width);
+        goto beach;
+      }
+      case 1:
+        if (!demux->audio_codec_data) {
+          GST_ERROR_OBJECT (demux, "got AAC audio packet before codec data");
+          ret = GST_FLOW_OK;
+          goto beach;
+        }
+        /* AAC raw packet */
+        GST_LOG_OBJECT (demux, "got a raw AAC audio packet");
+        break;
+      default:
+        GST_WARNING_OBJECT (demux, "invalid AAC packet type %u",
+            aac_packet_type);
+    }
+  }
+
   /* If we don't have our audio pad created, then create it. */
   if (G_UNLIKELY (!demux->audio_pad)) {
-
     demux->audio_pad =
         gst_pad_new_from_template (gst_element_class_get_pad_template
         (GST_ELEMENT_GET_CLASS (demux), "audio"), "audio");
@@ -1098,7 +1205,6 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
       GST_DEBUG_OBJECT (demux, "emitting no more pads");
       gst_element_no_more_pads (GST_ELEMENT (demux));
       demux->no_more_pads = TRUE;
-      demux->push_tags = TRUE;
     }
   }
 
@@ -1117,10 +1223,6 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
     }
   }
 
-  /* Push taglist if present */
-  if (G_UNLIKELY (demux->push_tags))
-    gst_flv_demux_push_tags (demux);
-
   /* Check if we have anything to push */
   if (demux->tag_data_size <= codec_data) {
     GST_LOG_OBJECT (demux, "Nothing left in this tag, returning");
@@ -1131,38 +1233,6 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
   outbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY,
       7 + codec_data, demux->tag_data_size - codec_data);
 
-  if (demux->audio_codec_tag == 10) {
-    guint8 aac_packet_type = GST_READ_UINT8 (data + 8);
-
-    switch (aac_packet_type) {
-      case 0:
-      {
-        /* AudioSpecificConfig data */
-        GST_LOG_OBJECT (demux, "got an AAC codec data packet");
-        if (demux->audio_codec_data) {
-          gst_buffer_unref (demux->audio_codec_data);
-        }
-        demux->audio_codec_data = outbuf;
-        /* Use that buffer data in the caps */
-        gst_flv_demux_audio_negotiate (demux, codec_tag, rate, channels, width);
-        goto beach;
-      }
-      case 1:
-        if (!demux->audio_codec_data) {
-          GST_ERROR_OBJECT (demux, "got AAC audio packet before codec data");
-          ret = GST_FLOW_OK;
-          gst_buffer_unref (outbuf);
-          goto beach;
-        }
-        /* AAC raw packet */
-        GST_LOG_OBJECT (demux, "got a raw AAC audio packet");
-        break;
-      default:
-        GST_WARNING_OBJECT (demux, "invalid AAC packet type %u",
-            aac_packet_type);
-    }
-  }
-
   /* detect (and deem to be resyncs)  large pts gaps */
   if (gst_flv_demux_update_resync (demux, pts, demux->audio_need_discont,
           &demux->last_audio_pts, &demux->audio_time_offset)) {
@@ -1234,7 +1304,6 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
         " after 6 seconds of audio");
     gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
     demux->no_more_pads = TRUE;
-    demux->push_tags = TRUE;
   }
 
   /* Push downstream */
@@ -1266,7 +1335,6 @@ gst_flv_demux_video_negotiate (GstFlvDemux * demux, guint32 codec_tag)
 {
   gboolean ret = FALSE;
   GstCaps *caps = NULL, *old_caps;
-  gchar *codec_name = NULL;
   GstEvent *event;
   gchar *stream_id;
 
@@ -1296,6 +1364,18 @@ gst_flv_demux_video_negotiate (GstFlvDemux * demux, guint32 codec_tag)
           gst_caps_new_simple ("video/x-h264", "stream-format", G_TYPE_STRING,
           "avc", NULL);
       break;
+      /* The following two are non-standard but apparently used, see in ffmpeg
+       * https://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavformat/flvdec.c;h=2bf1e059e1cbeeb79e4af9542da23f4560e1cf59;hb=b18d6c58000beed872d6bb1fe7d0fbe75ae26aef#l254
+       * https://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavformat/flvdec.c;h=2bf1e059e1cbeeb79e4af9542da23f4560e1cf59;hb=b18d6c58000beed872d6bb1fe7d0fbe75ae26aef#l282
+       */
+    case 8:
+      caps = gst_caps_new_empty_simple ("video/x-h263");
+      break;
+    case 9:
+      caps =
+          gst_caps_new_simple ("video/mpeg", "mpegversion", G_TYPE_INT, 4,
+          "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
+      break;
     default:
       GST_WARNING_OBJECT (demux, "unsupported video codec tag %u", codec_tag);
   }
@@ -1305,8 +1385,10 @@ gst_flv_demux_video_negotiate (GstFlvDemux * demux, guint32 codec_tag)
     goto beach;
   }
 
-  gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
-      demux->par_x, demux->par_y, NULL);
+  if (demux->got_par) {
+    gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
+        demux->par_x, demux->par_y, NULL);
+  }
 
   if (G_LIKELY (demux->w)) {
     gst_caps_set_simple (caps, "width", G_TYPE_INT, demux->w, NULL);
@@ -1359,18 +1441,10 @@ done:
     demux->video_codec_tag = codec_tag;
 
     if (caps) {
-      codec_name = gst_pb_utils_get_codec_description (caps);
-
-      if (codec_name) {
-        if (demux->taglist == NULL)
-          demux->taglist = gst_tag_list_new_empty ();
-        gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
-            GST_TAG_VIDEO_CODEC, codec_name, NULL);
-        g_free (codec_name);
-      }
-
       GST_DEBUG_OBJECT (demux->video_pad, "successfully negotiated caps %"
           GST_PTR_FORMAT, caps);
+
+      gst_flv_demux_push_tags (demux);
     } else {
       GST_DEBUG_OBJECT (demux->video_pad, "delayed setting caps");
     }
@@ -1451,12 +1525,57 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
     cts = GST_READ_UINT24_BE (data + 9);
     cts = (cts + 0xff800000) ^ 0xff800000;
 
+    if (cts < 0 && ABS (cts) > dts) {
+      GST_ERROR_OBJECT (demux, "Detected a negative composition time offset "
+          "'%d' that would lead to negative PTS, fixing", cts);
+      cts += ABS (cts) - dts;
+    }
+
     GST_LOG_OBJECT (demux, "got cts %d", cts);
   }
 
   GST_LOG_OBJECT (demux, "video tag with codec tag %u, keyframe (%d) "
       "(flags %02X)", codec_tag, keyframe, flags);
 
+  if (codec_tag == 7) {
+    guint8 avc_packet_type = GST_READ_UINT8 (data + 8);
+
+    switch (avc_packet_type) {
+      case 0:
+      {
+        if (demux->tag_data_size < codec_data) {
+          GST_ERROR_OBJECT (demux, "Got invalid H.264 codec, ignoring.");
+          break;
+        }
+
+        /* AVCDecoderConfigurationRecord data */
+        GST_LOG_OBJECT (demux, "got an H.264 codec data packet");
+        if (demux->video_codec_data) {
+          gst_buffer_unref (demux->video_codec_data);
+        }
+        demux->video_codec_data = gst_buffer_copy_region (buffer,
+            GST_BUFFER_COPY_MEMORY, 7 + codec_data,
+            demux->tag_data_size - codec_data);;
+        /* Use that buffer data in the caps */
+        if (demux->video_pad)
+          gst_flv_demux_video_negotiate (demux, codec_tag);
+        goto beach;
+      }
+      case 1:
+        /* H.264 NALU packet */
+        if (!demux->video_codec_data) {
+          GST_ERROR_OBJECT (demux, "got H.264 video packet before codec data");
+          ret = GST_FLOW_OK;
+          goto beach;
+        }
+        GST_LOG_OBJECT (demux, "got a H.264 NALU video packet");
+        break;
+      default:
+        GST_WARNING_OBJECT (demux, "invalid video packet type %u",
+            avc_packet_type);
+    }
+  }
+
   /* If we don't have our video pad created, then create it. */
   if (G_UNLIKELY (!demux->video_pad)) {
     demux->video_pad =
@@ -1515,7 +1634,6 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
       GST_DEBUG_OBJECT (demux, "emitting no more pads");
       gst_element_no_more_pads (GST_ELEMENT (demux));
       demux->no_more_pads = TRUE;
-      demux->push_tags = TRUE;
     }
   }
 
@@ -1534,10 +1652,6 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
     demux->got_par = FALSE;
   }
 
-  /* Push taglist if present */
-  if (G_UNLIKELY (demux->push_tags))
-    gst_flv_demux_push_tags (demux);
-
   /* Check if we have anything to push */
   if (demux->tag_data_size <= codec_data) {
     GST_LOG_OBJECT (demux, "Nothing left in this tag, returning");
@@ -1548,38 +1662,6 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
   outbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY,
       7 + codec_data, demux->tag_data_size - codec_data);
 
-  if (demux->video_codec_tag == 7) {
-    guint8 avc_packet_type = GST_READ_UINT8 (data + 8);
-
-    switch (avc_packet_type) {
-      case 0:
-      {
-        /* AVCDecoderConfigurationRecord data */
-        GST_LOG_OBJECT (demux, "got an H.264 codec data packet");
-        if (demux->video_codec_data) {
-          gst_buffer_unref (demux->video_codec_data);
-        }
-        demux->video_codec_data = outbuf;
-        /* Use that buffer data in the caps */
-        gst_flv_demux_video_negotiate (demux, codec_tag);
-        goto beach;
-      }
-      case 1:
-        /* H.264 NALU packet */
-        if (!demux->video_codec_data) {
-          GST_ERROR_OBJECT (demux, "got H.264 video packet before codec data");
-          ret = GST_FLOW_OK;
-          gst_buffer_unref (outbuf);
-          goto beach;
-        }
-        GST_LOG_OBJECT (demux, "got a H.264 NALU video packet");
-        break;
-      default:
-        GST_WARNING_OBJECT (demux, "invalid video packet type %u",
-            avc_packet_type);
-    }
-  }
-
   /* detect (and deem to be resyncs)  large dts gaps */
   if (gst_flv_demux_update_resync (demux, dts, demux->video_need_discont,
           &demux->last_video_dts, &demux->video_time_offset)) {
@@ -1656,7 +1738,6 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
         " after 6 seconds of video");
     gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
     demux->no_more_pads = TRUE;
-    demux->push_tags = TRUE;
   }
 
   /* Push downstream */
@@ -1904,7 +1985,6 @@ gst_flv_demux_cleanup (GstFlvDemux * demux)
 
   demux->has_audio = FALSE;
   demux->has_video = FALSE;
-  demux->push_tags = FALSE;
   demux->got_par = FALSE;
 
   demux->indexed = FALSE;
@@ -1976,6 +2056,8 @@ gst_flv_demux_cleanup (GstFlvDemux * demux)
     g_array_free (demux->filepositions, TRUE);
     demux->filepositions = NULL;
   }
+
+  gst_flv_demux_clear_tags (demux);
 }
 
 /*
@@ -2511,6 +2593,9 @@ gst_flv_demux_get_metadata (GstFlvDemux * demux)
   gst_buffer_unref (buffer);
   buffer = NULL;
 
+  if (G_UNLIKELY (offset < tag_size))
+    goto exit;
+
   offset -= tag_size;
   if (GST_FLOW_OK != gst_flv_demux_pull_range (demux, demux->sinkpad, offset,
           12, &buffer))
@@ -2611,7 +2696,8 @@ gst_flv_demux_loop (GstPad * pad)
   }
 
   /* pause if something went wrong or at end */
-  if (G_UNLIKELY (ret != GST_FLOW_OK))
+  if (G_UNLIKELY (ret != GST_FLOW_OK) && !(ret == GST_FLOW_NOT_LINKED
+          && !demux->no_more_pads))
     goto pause;
 
   gst_object_unref (demux);
@@ -2681,9 +2767,7 @@ pause:
           GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
       }
     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
-      GST_ELEMENT_ERROR (demux, STREAM, FAILED,
-          ("Internal data stream error."),
-          ("stream stopped, reason %s", reason));
+      GST_ELEMENT_FLOW_ERROR (demux, ret);
       gst_flv_demux_push_src_event (demux, gst_event_new_eos ());
     }
     gst_object_unref (demux);
@@ -3474,6 +3558,16 @@ gst_flv_demux_dispose (GObject * object)
     demux->taglist = NULL;
   }
 
+  if (demux->audio_tags) {
+    gst_tag_list_unref (demux->audio_tags);
+    demux->audio_tags = NULL;
+  }
+
+  if (demux->video_tags) {
+    gst_tag_list_unref (demux->video_tags);
+    demux->video_tags = NULL;
+  }
+
   if (demux->flowcombiner) {
     gst_flow_combiner_free (demux->flowcombiner);
     demux->flowcombiner = NULL;
@@ -3538,15 +3632,14 @@ gst_flv_demux_class_init (GstFlvDemuxClass * klass)
   gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_flv_demux_get_index);
 #endif
 
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&flv_sink_template));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&audio_src_template));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&video_src_template));
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &flv_sink_template);
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &audio_src_template);
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &video_src_template);
   gst_element_class_set_static_metadata (gstelement_class, "FLV Demuxer",
-      "Codec/Demuxer",
-      "Demux FLV feeds into digital streams",
+      "Codec/Demuxer", "Demux FLV feeds into digital streams",
       "Julien Moutte <julien@moutte.net>");
 }
 
@@ -3568,9 +3661,7 @@ gst_flv_demux_init (GstFlvDemux * demux)
   gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
 
   demux->adapter = gst_adapter_new ();
-  demux->taglist = gst_tag_list_new_empty ();
   demux->flowcombiner = gst_flow_combiner_new ();
-  gst_segment_init (&demux->segment, GST_FORMAT_TIME);
 
   demux->own_index = FALSE;