h264parse: allow full negotiation for packetized input
authorAlessandro Decina <alessandro.d@gmail.com>
Sun, 15 May 2011 11:23:39 +0000 (13:23 +0200)
committerMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Wed, 25 May 2011 19:51:55 +0000 (21:51 +0200)
... by defaulting to allow splitting packetized input and having
negotiation with downstream deciding whether or not this applies.

Also enable pass-through parsing mode if input and output format
(stream-format and alignment) match.

API: GstH264Parse:split-packetized (removed)

Fixes #650228.

gst/videoparsers/gsth264parse.c

index fd63301..2b55f2f 100644 (file)
 GST_DEBUG_CATEGORY (h264_parse_debug);
 #define GST_CAT_DEFAULT h264_parse_debug
 
-#define DEFAULT_SPLIT_PACKETIZED     FALSE
 #define DEFAULT_CONFIG_INTERVAL      (0)
 
 enum
 {
   PROP_0,
-  PROP_SPLIT_PACKETIZED,
   PROP_CONFIG_INTERVAL,
   PROP_LAST
 };
@@ -118,10 +116,6 @@ gst_h264_parse_class_init (GstH264ParseClass * klass)
   gobject_class->set_property = gst_h264_parse_set_property;
   gobject_class->get_property = gst_h264_parse_get_property;
 
-  g_object_class_install_property (gobject_class, PROP_SPLIT_PACKETIZED,
-      g_param_spec_boolean ("split-packetized", "Split packetized",
-          "Split NAL units of packetized streams", DEFAULT_SPLIT_PACKETIZED,
-          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, PROP_CONFIG_INTERVAL,
       g_param_spec_uint ("config-interval",
           "SPS PPS Send Interval",
@@ -252,41 +246,52 @@ gst_h264_parse_get_string (GstH264Parse * parse, gboolean format, gint code)
   }
 }
 
-/* check downstream caps to configure format and alignment */
 static void
-gst_h264_parse_negotiate (GstH264Parse * h264parse)
+gst_h264_parse_format_from_caps (GstCaps * caps, guint * format, guint * align)
 {
-  GstCaps *caps;
-  guint format = GST_H264_PARSE_FORMAT_NONE;
-  guint align = GST_H264_PARSE_ALIGN_NONE;
 
-  caps = gst_pad_get_allowed_caps (GST_BASE_PARSE_SRC_PAD (h264parse));
-  GST_DEBUG_OBJECT (h264parse, "allowed caps: %" GST_PTR_FORMAT, caps);
+  if (format)
+    *format = GST_H264_PARSE_FORMAT_NONE;
+
+  if (align)
+    *align = GST_H264_PARSE_ALIGN_NONE;
 
   if (caps && gst_caps_get_size (caps) > 0) {
     GstStructure *s = gst_caps_get_structure (caps, 0);
     const gchar *str = NULL;
 
-    if ((str = gst_structure_get_string (s, "stream-format"))) {
-      if (strcmp (str, "avc") == 0) {
-        format = GST_H264_PARSE_FORMAT_AVC;
-      } else if (strcmp (str, "byte-stream") == 0) {
-        format = GST_H264_PARSE_FORMAT_BYTE;
-      } else {
-        GST_DEBUG_OBJECT (h264parse, "unknown stream-format: %s", str);
+    if (format) {
+      if ((str = gst_structure_get_string (s, "stream-format"))) {
+        if (strcmp (str, "avc") == 0)
+          *format = GST_H264_PARSE_FORMAT_AVC;
+        else if (strcmp (str, "byte-stream") == 0)
+          *format = GST_H264_PARSE_FORMAT_BYTE;
       }
     }
 
-    if ((str = gst_structure_get_string (s, "alignment"))) {
-      if (strcmp (str, "au") == 0) {
-        align = GST_H264_PARSE_ALIGN_AU;
-      } else if (strcmp (str, "nal") == 0) {
-        align = GST_H264_PARSE_ALIGN_NAL;
-      } else {
-        GST_DEBUG_OBJECT (h264parse, "unknown alignment: %s", str);
+    if (align) {
+      if ((str = gst_structure_get_string (s, "alignment"))) {
+        if (strcmp (str, "au") == 0)
+          *align = GST_H264_PARSE_ALIGN_AU;
+        else if (strcmp (str, "nal") == 0)
+          *align = GST_H264_PARSE_ALIGN_NAL;
       }
     }
   }
+}
+
+/* check downstream caps to configure format and alignment */
+static void
+gst_h264_parse_negotiate (GstH264Parse * h264parse)
+{
+  GstCaps *caps;
+  guint format = GST_H264_PARSE_FORMAT_NONE;
+  guint align = GST_H264_PARSE_ALIGN_NONE;
+
+  caps = gst_pad_get_allowed_caps (GST_BASE_PARSE_SRC_PAD (h264parse));
+  GST_DEBUG_OBJECT (h264parse, "allowed caps: %" GST_PTR_FORMAT, caps);
+
+  gst_h264_parse_format_from_caps (caps, &format, &align);
 
   if (caps)
     gst_caps_unref (caps);
@@ -662,10 +667,10 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse)
 }
 
 static void
-gst_h264_parse_update_src_caps (GstH264Parse * h264parse)
+gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps)
 {
   GstH264ParamsSPS *sps;
-  GstCaps *caps = NULL, *sink_caps;
+  GstCaps *sink_caps;
   gboolean modified = FALSE;
   GstBuffer *buf = NULL;
 
@@ -674,8 +679,14 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse)
   else if (G_UNLIKELY (!h264parse->update_caps))
     return;
 
+  /* if this is being called from the first _setcaps call, caps on the sinkpad
+   * aren't set yet and so they need to be passed as an argument */
+  if (caps)
+    sink_caps = caps;
+  else
+    sink_caps = GST_PAD_CAPS (GST_BASE_PARSE_SINK_PAD (h264parse));
+
   /* carry over input caps as much as possible; override with our own stuff */
-  sink_caps = GST_PAD_CAPS (GST_BASE_PARSE_SINK_PAD (h264parse));
   if (sink_caps)
     gst_caps_ref (sink_caps);
   else
@@ -700,6 +711,7 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse)
     }
   }
 
+  caps = NULL;
   if (G_UNLIKELY (!sps)) {
     caps = gst_caps_copy (sink_caps);
   } else if (G_UNLIKELY (h264parse->width != sps->width ||
@@ -759,7 +771,7 @@ gst_h264_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
   h264parse = GST_H264_PARSE (parse);
   buffer = frame->buffer;
 
-  gst_h264_parse_update_src_caps (h264parse);
+  gst_h264_parse_update_src_caps (h264parse, NULL);
 
   gst_h264_params_get_timestamp (h264parse->params,
       &GST_BUFFER_TIMESTAMP (buffer), &GST_BUFFER_DURATION (buffer),
@@ -921,8 +933,8 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
   GstH264Parse *h264parse;
   GstStructure *str;
   const GValue *value;
-  GstBuffer *buffer = NULL;
-  guint size;
+  GstBuffer *codec_data = NULL;
+  guint size, format, align;
 
   h264parse = GST_H264_PARSE (parse);
 
@@ -947,11 +959,11 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
     /* make note for optional split processing */
     h264parse->packetized = TRUE;
 
-    buffer = gst_value_get_buffer (value);
-    if (!buffer)
+    codec_data = gst_value_get_buffer (value);
+    if (!codec_data)
       goto wrong_type;
-    data = GST_BUFFER_DATA (buffer);
-    size = GST_BUFFER_SIZE (buffer);
+    data = GST_BUFFER_DATA (codec_data);
+    size = GST_BUFFER_SIZE (codec_data);
 
     /* parse the avcC data */
     if (size < 7)
@@ -996,6 +1008,8 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
       data += len + 2;
       size -= len + 2;
     }
+
+    h264parse->codec_data = gst_buffer_ref (codec_data);
   } else {
     GST_DEBUG_OBJECT (h264parse, "have bytestream h264");
     /* nothing to pre-process */
@@ -1004,32 +1018,35 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
     h264parse->nal_length_size = 4;
   }
 
-  if (h264parse->packetized) {
-    if (h264parse->split_packetized) {
-      GST_DEBUG_OBJECT (h264parse,
-          "converting AVC to nal bytestream prior to parsing");
-      /* negotiate behaviour with upstream */
-      gst_h264_parse_negotiate (h264parse);
-      if (h264parse->format == GST_H264_PARSE_FORMAT_BYTE) {
-        /* arrange to insert codec-data in-stream if needed */
-        h264parse->push_codec = h264parse->packetized;
-      }
-      gst_base_parse_set_passthrough (parse, FALSE);
-    } else {
-      GST_DEBUG_OBJECT (h264parse, "passing on packetized AVC");
-      /* no choice to negotiate */
-      h264parse->format = GST_H264_PARSE_FORMAT_AVC;
-      h264parse->align = GST_H264_PARSE_ALIGN_AU;
-      /* fallback codec-data */
-      h264parse->codec_data = gst_buffer_ref (buffer);
-      /* pass through unharmed, though _chain will parse a bit */
-      gst_base_parse_set_passthrough (parse, TRUE);
-      /* we did parse codec-data and might supplement src caps */
-      gst_h264_parse_update_src_caps (h264parse);
-    }
+  /* negotiate with downstream, sets ->format and ->align */
+  gst_h264_parse_negotiate (h264parse);
+
+  /* get upstream format and align from caps */
+  gst_h264_parse_format_from_caps (caps, &format, &align);
+
+  /* if upstream sets codec_data without setting stream-format and alignment, we
+   * assume stream-format=avc,alignment=au */
+  if (format == GST_H264_PARSE_FORMAT_NONE) {
+    if (codec_data == NULL)
+      goto unknown_input_format;
+
+    format = GST_H264_PARSE_FORMAT_AVC;
+    align = GST_H264_PARSE_ALIGN_AU;
+  }
+
+  if (format == h264parse->format && align == h264parse->align) {
+    gst_base_parse_set_passthrough (parse, TRUE);
+
+    /* we did parse codec-data and might supplement src caps */
+    gst_h264_parse_update_src_caps (h264parse, caps);
+  } else if (format == GST_H264_PARSE_FORMAT_AVC &&
+      h264parse->format == GST_H264_PARSE_FORMAT_BYTE) {
+    /* arrange to insert codec-data in-stream if needed.
+     * src caps are only arranged for later on */
+    h264parse->push_codec = TRUE;
+    h264parse->split_packetized = TRUE;
   }
 
-  /* src caps are only arranged for later on */
   return TRUE;
 
   /* ERRORS */
@@ -1048,6 +1065,11 @@ wrong_type:
     GST_DEBUG_OBJECT (h264parse, "wrong codec-data type");
     goto refuse_caps;
   }
+unknown_input_format:
+  {
+    GST_DEBUG_OBJECT (h264parse, "unknown stream-format and no codec_data");
+    goto refuse_caps;
+  }
 refuse_caps:
   {
     GST_WARNING_OBJECT (h264parse, "refused caps %" GST_PTR_FORMAT, caps);
@@ -1155,9 +1177,6 @@ gst_h264_parse_set_property (GObject * object, guint prop_id,
   parse = GST_H264_PARSE (object);
 
   switch (prop_id) {
-    case PROP_SPLIT_PACKETIZED:
-      parse->split_packetized = g_value_get_boolean (value);
-      break;
     case PROP_CONFIG_INTERVAL:
       parse->interval = g_value_get_uint (value);
       break;
@@ -1176,9 +1195,6 @@ gst_h264_parse_get_property (GObject * object, guint prop_id, GValue * value,
   parse = GST_H264_PARSE (object);
 
   switch (prop_id) {
-    case PROP_SPLIT_PACKETIZED:
-      g_value_set_boolean (value, parse->split_packetized);
-      break;
     case PROP_CONFIG_INTERVAL:
       g_value_set_uint (value, parse->interval);
       break;