[gst-cvs] gst-plugins-bad: h264parse: optionally output AUs rather than NALUs

Mark Nauwelaerts mnauw at kemper.freedesktop.org
Mon Nov 23 05:49:41 PST 2009


Module: gst-plugins-bad
Branch: master
Commit: 47041f610d9d0cc8c0ec6b21d77e69cf98b99152
URL:    http://cgit.freedesktop.org/gstreamer/gst-plugins-bad/commit/?id=47041f610d9d0cc8c0ec6b21d77e69cf98b99152

Author: Mark Nauwelaerts <mark.nauwelaerts at collabora.co.uk>
Date:   Sun Nov 22 19:34:25 2009 +0100

h264parse: optionally output AUs rather than NALUs

That is, Access Units (frames/fields) instead of (possibly) parts thereof.

---

 gst/h264parse/gsth264parse.c |  115 ++++++++++++++++++++++++++++++++++++++++--
 gst/h264parse/gsth264parse.h |    4 ++
 2 files changed, 114 insertions(+), 5 deletions(-)

diff --git a/gst/h264parse/gsth264parse.c b/gst/h264parse/gsth264parse.c
index 51f391d..e65c9bd 100644
--- a/gst/h264parse/gsth264parse.c
+++ b/gst/h264parse/gsth264parse.c
@@ -51,11 +51,13 @@ GST_ELEMENT_DETAILS ("H264Parse",
     "Wim Taymans <wim.taymans at gmail.com>");
 
 #define DEFAULT_SPLIT_PACKETIZED     FALSE
+#define DEFAULT_ACCESS_UNIT          FALSE
 
 enum
 {
   PROP_0,
-  PROP_SPLIT_PACKETIZED
+  PROP_SPLIT_PACKETIZED,
+  PROP_ACCESS_UNIT
 };
 
 typedef enum
@@ -740,6 +742,8 @@ gst_nal_decode_slice_header (GstH264Parse * h, GstNalBs * bs)
   /* FIXME: note that pps might be uninitialized */
   sps_id = h->pps->sps_id;
   h->sps = gst_h264_parse_get_sps (h, sps_id);
+  if (!h->sps)
+    return FALSE;
   /* FIXME: in some streams sps/pps may not be ready before the first slice
    * header. In this case it is not a good idea to _get_sps()/_pps() at this
    * point
@@ -808,6 +812,10 @@ gst_h264_parse_class_init (GstH264ParseClass * klass)
       g_param_spec_boolean ("split-packetized", "Split packetized",
           "Split NAL units of packetized streams", DEFAULT_SPLIT_PACKETIZED,
           G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, PROP_ACCESS_UNIT,
+      g_param_spec_boolean ("access-unit", "Access Units",
+          "Output Acess Units rather than NALUs", DEFAULT_ACCESS_UNIT,
+          G_PARAM_READWRITE));
 
   gstelement_class->change_state = gst_h264_parse_change_state;
 }
@@ -831,6 +839,9 @@ gst_h264_parse_init (GstH264Parse * h264parse, GstH264ParseClass * g_class)
   h264parse->split_packetized = DEFAULT_SPLIT_PACKETIZED;
   h264parse->adapter = gst_adapter_new ();
 
+  h264parse->merge = DEFAULT_ACCESS_UNIT;
+  h264parse->picture_adapter = gst_adapter_new ();
+
   for (i = 0; i < MAX_SPS_COUNT; i++)
     h264parse->sps_buffers[i] = NULL;
   h264parse->sps = NULL;
@@ -864,6 +875,7 @@ gst_h264_parse_finalize (GObject * object)
   h264parse = GST_H264PARSE (object);
 
   g_object_unref (h264parse->adapter);
+  g_object_unref (h264parse->picture_adapter);
 
   for (i = 0; i < MAX_SPS_COUNT; i++) {
     if (h264parse->sps_buffers[i] != NULL)
@@ -890,6 +902,9 @@ gst_h264_parse_set_property (GObject * object, guint prop_id,
     case PROP_SPLIT_PACKETIZED:
       parse->split_packetized = g_value_get_boolean (value);
       break;
+    case PROP_ACCESS_UNIT:
+      parse->merge = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -908,6 +923,9 @@ gst_h264_parse_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_SPLIT_PACKETIZED:
       g_value_set_boolean (value, parse->split_packetized);
       break;
+    case PROP_ACCESS_UNIT:
+      g_value_set_boolean (value, parse->merge);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -985,6 +1003,71 @@ wrong_version:
   }
 }
 
+/* takes over ownership of nal and returns fresh buffer */
+static GstBuffer *
+gst_h264_parse_push_nal (GstH264Parse * h264parse, GstBuffer * nal,
+    guint8 * next_nal, gboolean * _start)
+{
+  gint nal_type;
+  guint8 *data;
+  GstBuffer *outbuf = NULL;
+  guint outsize, size, nal_length = h264parse->nal_length_size;
+  gboolean start = h264parse->picture_start;
+  gboolean complete;
+
+  data = GST_BUFFER_DATA (nal);
+  size = GST_BUFFER_SIZE (nal);
+
+  /* caller ensures number of bytes available */
+  g_return_val_if_fail (size >= nal_length + 1, NULL);
+
+  /* determine if AU complete */
+  nal_type = data[nal_length] & 0x1f;
+  h264parse->picture_start |= (nal_type == 1 || nal_type == 2 || nal_type == 5);
+  if (G_UNLIKELY (!next_nal)) {
+    complete = TRUE;
+  } else {
+    /* consider a coded slices (IDR or not) to start a picture,
+     * (so ending the previous one) if first_mb_in_slice == 0
+     * (non-0 is part of previous one) */
+    /* NOTE this is not entirely according to Access Unit specs in 7.4.1.2.4,
+     * but in practice it works in sane cases, needs not much parsing,
+     * and also works with broken frame_num in NAL
+     * (where spec-wise would fail) */
+    nal_type = next_nal[nal_length] & 0x1f;
+    complete = h264parse->picture_start && (nal_type >= 6 && nal_type <= 9);
+    complete |= h264parse->picture_start &&
+        (nal_type == 1 || nal_type == 2 || nal_type == 5) &&
+        (next_nal[nal_length + 1] & 0x80);
+  }
+
+  if (h264parse->merge) {
+    /* regardless, collect this NALU */
+    gst_adapter_push (h264parse->picture_adapter, nal);
+
+    if (complete) {
+      GstClockTime ts;
+
+      h264parse->picture_start = FALSE;
+      ts = gst_adapter_prev_timestamp (h264parse->picture_adapter, NULL);
+      outsize = gst_adapter_available (h264parse->picture_adapter);
+      outbuf = gst_adapter_take_buffer (h264parse->picture_adapter, outsize);
+      GST_BUFFER_TIMESTAMP (outbuf) = ts;
+    }
+  } else {
+    outbuf = nal;
+  }
+
+  if (_start)
+    *_start = (h264parse->picture_start != start);
+
+  /* ensure metadata can be messed with later on */
+  if (G_LIKELY (outbuf))
+    outbuf = gst_buffer_make_metadata_writable (outbuf);
+
+  return outbuf;
+}
+
 static void
 gst_h264_parse_clear_queues (GstH264Parse * h264parse)
 {
@@ -1003,6 +1086,8 @@ gst_h264_parse_clear_queues (GstH264Parse * h264parse)
   }
   gst_adapter_clear (h264parse->adapter);
   h264parse->have_i_frame = FALSE;
+  gst_adapter_clear (h264parse->picture_adapter);
+  h264parse->picture_start = FALSE;
 }
 
 static GstFlowReturn
@@ -1027,7 +1112,7 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont,
     gboolean got_frame = FALSE;
 
     avail = gst_adapter_available (h264parse->adapter);
-    if (avail < h264parse->nal_length_size + 1)
+    if (avail < h264parse->nal_length_size + 2)
       break;
     data = gst_adapter_peek (h264parse->adapter, avail);
 
@@ -1155,10 +1240,23 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont,
     if (next_nalu_pos > 0) {
       GstBuffer *outbuf;
       GstClockTime outbuf_dts = GST_CLOCK_TIME_NONE;
+      gboolean start;
+      guint8 *next_data;
 
       outbuf = gst_adapter_take_buffer (h264parse->adapter, next_nalu_pos);
       outbuf_dts = gst_adapter_prev_timestamp (h264parse->adapter, NULL);       /* Better value for the second parameter? */
 
+      /* packetized will have no next data, which serves fine here */
+      next_data = (guint8 *) gst_adapter_peek (h264parse->adapter, 6);
+      outbuf = gst_h264_parse_push_nal (h264parse, outbuf, next_data, &start);
+      if (!outbuf) {
+        /* no complete unit yet, go for next round */
+        continue;
+      } else {
+        if (h264parse->merge)
+          start = TRUE;
+      }
+
       /* Ignore upstream dts that stalls or goes backward. Upstream elements
        * like filesrc would keep on writing timestamp=0.  XXX: is this correct?
        * TODO: better way to detect whether upstream timstamps are useful */
@@ -1167,7 +1265,7 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont,
           && outbuf_dts <= h264parse->last_outbuf_dts)
         outbuf_dts = GST_CLOCK_TIME_NONE;
 
-      if (got_frame || delta_unit) {
+      if ((got_frame || delta_unit) && start) {
         GstH264Sps *sps = h264parse->sps;
         gint duration = 1;
 
@@ -1316,9 +1414,18 @@ gst_h264_parse_flush_decode (GstH264Parse * h264parse)
     link = h264parse->decode;
     buf = link->buffer;
 
+    h264parse->decode = gst_nal_list_delete_head (h264parse->decode);
+    h264parse->decode_len--;
+
     GST_DEBUG_OBJECT (h264parse, "have type: %d, I frame: %d", link->nal_type,
         link->i_frame);
 
+    buf = gst_h264_parse_push_nal (h264parse, buf,
+        h264parse->decode ? GST_BUFFER_DATA (h264parse->decode->buffer) : NULL,
+        NULL);
+    if (!buf)
+      continue;
+
     if (first) {
       /* first buffer has discont */
       GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
@@ -1338,8 +1445,6 @@ gst_h264_parse_flush_decode (GstH264Parse * h264parse)
 
     res = gst_pad_push (h264parse->srcpad, buf);
 
-    h264parse->decode = gst_nal_list_delete_head (h264parse->decode);
-    h264parse->decode_len--;
   }
   /* the i frame is gone now */
   h264parse->have_i_frame = FALSE;
diff --git a/gst/h264parse/gsth264parse.h b/gst/h264parse/gsth264parse.h
index 2958ad1..a7e7d7b 100644
--- a/gst/h264parse/gsth264parse.h
+++ b/gst/h264parse/gsth264parse.h
@@ -116,6 +116,10 @@ struct _GstH264Parse
 
   /* for debug purpose */ 
   guint32 frame_cnt;
+
+  /* NALU AU */
+  GstAdapter *picture_adapter;
+  gboolean picture_start;
 };
 
 struct _GstH264ParseClass





More information about the Gstreamer-commits mailing list