flvmux: Don't refuse caps changes after starting to write headers in streamable mode.
authorYeongjin Jeong <gingerbk247@gmail.com>
Fri, 5 Oct 2018 08:16:26 +0000 (17:16 +0900)
committerOlivier CrĂȘte <olivier.crete@collabora.com>
Thu, 11 Oct 2018 19:35:24 +0000 (15:35 -0400)
Flv does support changing the stream type and stream properties
after the headers were started to be written, and for example H264
codec_data changes can be supported.

https://bugzilla.gnome.org/show_bug.cgi?id=797256

gst/flv/gstflvmux.c
gst/flv/gstflvmux.h

index 6d0a140..c094016 100644 (file)
@@ -324,6 +324,7 @@ gst_flv_mux_reset (GstElement * element)
   mux->first_timestamp = GST_CLOCK_STIME_NONE;
 
   mux->state = GST_FLV_MUX_STATE_HEADER;
+  mux->sent_header = FALSE;
 
   /* tags */
   gst_tag_setter_reset_tags (GST_TAG_SETTER (mux));
@@ -397,6 +398,12 @@ gst_flv_mux_video_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
   GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
   gboolean ret = TRUE;
   GstStructure *s;
+  guint old_codec;
+  GstBuffer *old_codec_data = NULL;
+
+  old_codec = pad->codec;
+  if (pad->codec_data)
+    old_codec_data = gst_buffer_ref (pad->codec_data);
 
   s = gst_caps_get_structure (caps, 0);
 
@@ -419,8 +426,35 @@ gst_flv_mux_video_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
 
     if (val)
       gst_buffer_replace (&pad->codec_data, gst_value_get_buffer (val));
+    else if (!val && pad->codec_data)
+      gst_buffer_unref (pad->codec_data);
+  }
+
+  if (ret && mux->streamable && mux->state != GST_FLV_MUX_STATE_HEADER) {
+    if (old_codec != pad->codec) {
+      pad->info_changed = TRUE;
+    }
+
+    if (old_codec_data && pad->codec_data) {
+      GstMapInfo map;
+
+      gst_buffer_map (old_codec_data, &map, GST_MAP_READ);
+      if (map.size != gst_buffer_get_size (pad->codec_data) ||
+          gst_buffer_memcmp (pad->codec_data, 0, map.data, map.size))
+        pad->info_changed = TRUE;
+
+      gst_buffer_unmap (old_codec_data, &map);
+    } else if (!old_codec_data && pad->codec_data) {
+      pad->info_changed = TRUE;
+    }
+
+    if (pad->info_changed)
+      mux->state = GST_FLV_MUX_STATE_HEADER;
   }
 
+  if (old_codec_data)
+    gst_buffer_unref (old_codec_data);
+
   gst_object_unref (mux);
 
   return ret;
@@ -432,6 +466,15 @@ gst_flv_mux_audio_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
   GstFlvMux *mux = GST_FLV_MUX (gst_pad_get_parent (pad));
   gboolean ret = TRUE;
   GstStructure *s;
+  guint old_codec, old_rate, old_width, old_channels;
+  GstBuffer *old_codec_data = NULL;
+
+  old_codec = pad->codec;
+  old_rate = pad->rate;
+  old_width = pad->width;
+  old_channels = pad->channels;
+  if (pad->codec_data)
+    old_codec_data = gst_buffer_ref (pad->codec_data);
 
   s = gst_caps_get_structure (caps, 0);
 
@@ -560,8 +603,36 @@ gst_flv_mux_audio_pad_setcaps (GstFlvMuxPad * pad, GstCaps * caps)
 
     if (val)
       gst_buffer_replace (&pad->codec_data, gst_value_get_buffer (val));
+    else if (!val && pad->codec_data)
+      gst_buffer_unref (pad->codec_data);
   }
 
+  if (ret && mux->streamable && mux->state != GST_FLV_MUX_STATE_HEADER) {
+    if (old_codec != pad->codec || old_rate != pad->rate ||
+        old_width != pad->width || old_channels != pad->channels) {
+      pad->info_changed = TRUE;
+    }
+
+    if (old_codec_data && pad->codec_data) {
+      GstMapInfo map;
+
+      gst_buffer_map (old_codec_data, &map, GST_MAP_READ);
+      if (map.size != gst_buffer_get_size (pad->codec_data) ||
+          gst_buffer_memcmp (pad->codec_data, 0, map.data, map.size))
+        pad->info_changed = TRUE;
+
+      gst_buffer_unmap (old_codec_data, &map);
+    } else if (!old_codec_data && pad->codec_data) {
+      pad->info_changed = TRUE;
+    }
+
+    if (pad->info_changed)
+      mux->state = GST_FLV_MUX_STATE_HEADER;
+  }
+
+  if (old_codec_data)
+    gst_buffer_unref (old_codec_data);
+
   gst_object_unref (mux);
 
   return ret;
@@ -579,6 +650,7 @@ gst_flv_mux_reset_pad (GstFlvMuxPad * pad)
   pad->rate = G_MAXUINT;
   pad->width = G_MAXUINT;
   pad->channels = G_MAXUINT;
+  pad->info_changed = FALSE;
 
   gst_flv_mux_pad_flush (GST_AGGREGATOR_PAD_CAST (pad), NULL);
 }
@@ -1371,29 +1443,48 @@ gst_flv_mux_write_header (GstFlvMux * mux)
     gst_query_unref (query);
   }
 
-  caps = gst_flv_mux_prepare_src_caps (mux,
-      &header, &metadata, &video_codec_data, &audio_codec_data);
+  if (!mux->streamable) {
+    caps = gst_flv_mux_prepare_src_caps (mux,
+        &header, &metadata, &video_codec_data, &audio_codec_data);
+  } else {
+    if (!mux->sent_header) {
+      caps = gst_flv_mux_prepare_src_caps (mux,
+          &header, &metadata, &video_codec_data, &audio_codec_data);
+    } else {
+      caps = gst_flv_mux_prepare_src_caps (mux,
+          NULL, NULL,
+          (mux->video_pad->info_changed ? &video_codec_data : NULL),
+          (mux->audio_pad->info_changed ? &audio_codec_data : NULL));
+    }
+  }
 
   gst_aggregator_set_src_caps (GST_AGGREGATOR_CAST (mux), caps);
 
   gst_caps_unref (caps);
 
   /* push the header buffer, the metadata and the codec info, if any */
-  ret = gst_flv_mux_push (mux, header);
-  if (ret != GST_FLOW_OK)
-    goto failure_header;
-  ret = gst_flv_mux_push (mux, metadata);
-  if (ret != GST_FLOW_OK)
-    goto failure_metadata;
+  if (header != NULL) {
+    ret = gst_flv_mux_push (mux, header);
+    if (ret != GST_FLOW_OK)
+      goto failure_header;
+    mux->sent_header = TRUE;
+  }
+  if (metadata != NULL) {
+    ret = gst_flv_mux_push (mux, metadata);
+    if (ret != GST_FLOW_OK)
+      goto failure_metadata;
+  }
   if (video_codec_data != NULL) {
     ret = gst_flv_mux_push (mux, video_codec_data);
     if (ret != GST_FLOW_OK)
       goto failure_video_codec_data;
+    mux->video_pad->info_changed = FALSE;
   }
   if (audio_codec_data != NULL) {
     ret = gst_flv_mux_push (mux, audio_codec_data);
     if (ret != GST_FLOW_OK)
       goto failure_audio_codec_data;
+    mux->audio_pad->info_changed = FALSE;
   }
   return GST_FLOW_OK;
 
@@ -1722,10 +1813,13 @@ gst_flv_mux_aggregate (GstAggregator * aggregator, gboolean timeout)
     mux->state = GST_FLV_MUX_STATE_DATA;
 
     best = gst_flv_mux_find_best_pad (aggregator, &ts);
-    if (best && GST_CLOCK_STIME_IS_VALID (ts))
-      mux->first_timestamp = ts;
-    else
-      mux->first_timestamp = 0;
+
+    if (!mux->streamable || mux->first_timestamp == GST_CLOCK_STIME_NONE) {
+      if (best && GST_CLOCK_STIME_IS_VALID (ts))
+        mux->first_timestamp = ts;
+      else
+        mux->first_timestamp = 0;
+    }
   } else {
     best = gst_flv_mux_find_best_pad (aggregator, &ts);
   }
index 4db5093..e116d4c 100644 (file)
@@ -66,6 +66,8 @@ struct _GstFlvMuxPad
   GstClockTime last_timestamp;
   gint64 pts;
   gint64 dts;
+
+  gboolean info_changed;
 };
 
 typedef struct _GstFlvMuxPadClass {
@@ -96,6 +98,8 @@ typedef struct _GstFlvMux {
   guint64 byte_count;
   guint64 duration;
   gint64 first_timestamp;
+
+  gboolean sent_header;
 } GstFlvMux;
 
 typedef struct _GstFlvMuxClass {