Merge branch 'upstream/1.16' into tizen_gst_1.16.2
[platform/upstream/gst-plugins-good.git] / gst / flv / gstflvdemux.c
index ce05380..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)
@@ -368,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);
       }
@@ -414,7 +422,8 @@ gst_flv_demux_parse_metadata_item (GstFlvDemux * demux, GstByteReader * reader,
       } 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 {
@@ -561,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)
 {
@@ -594,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;
@@ -636,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);
@@ -689,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;
 
@@ -753,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);
 
@@ -835,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,
@@ -871,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");
     }
@@ -915,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)));
   }
 }
 
@@ -1038,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;
@@ -1137,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;
     }
   }
 
@@ -1156,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");
@@ -1241,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 */
@@ -1273,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;
 
@@ -1303,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);
   }
@@ -1312,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);
@@ -1366,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");
     }
@@ -1458,6 +1525,12 @@ 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);
   }
 
@@ -1470,6 +1543,11 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
     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) {
@@ -1556,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;
     }
   }
 
@@ -1575,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");
@@ -1665,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 */
@@ -1913,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;
@@ -1985,6 +2056,8 @@ gst_flv_demux_cleanup (GstFlvDemux * demux)
     g_array_free (demux->filepositions, TRUE);
     demux->filepositions = NULL;
   }
+
+  gst_flv_demux_clear_tags (demux);
 }
 
 /*
@@ -2520,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))
@@ -2620,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);
@@ -2690,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);
@@ -3483,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;
@@ -3576,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;