applemedia: vtdec: set reorder queue length to the max DPB length
authorAlessandro Decina <alessandro.d@gmail.com>
Wed, 11 Dec 2013 06:58:23 +0000 (07:58 +0100)
committerAlessandro Decina <alessandro.d@gmail.com>
Wed, 11 Dec 2013 07:12:36 +0000 (08:12 +0100)
Set reorder_queue_frame_delay from the DPB size (in frames). Still not optimal,
as the DPB size is larger than the max bframe forward prediction length, but I
don't know how to compute the latter without parsing every group of pictures.

sys/applemedia/vtdec.c

index 7442906..bc5218d 100644 (file)
@@ -72,6 +72,8 @@ static void gst_vtdec_session_output_callback (void
     *decompression_output_ref_con, void *source_frame_ref_con, OSStatus status,
     VTDecodeInfoFlags info_flags, CVImageBufferRef image_buffer, CMTime pts,
     CMTime duration);
+static gboolean compute_h264_decode_picture_buffer_length (GstVtdec * vtdec,
+    GstBuffer * codec_data, int *length);
 
 static GstStaticPadTemplate gst_vtdec_sink_template =
     GST_STATIC_PAD_TEMPLATE ("sink",
@@ -183,27 +185,33 @@ gst_vtdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
 
   structure = gst_caps_get_structure (state->caps, 0);
   caps_name = gst_structure_get_name (structure);
-  if (!strcmp (caps_name, "video/x-h264") && state->codec_data == NULL) {
-    GST_INFO_OBJECT (vtdec, "no codec data, wait for one");
-    return TRUE;
-  }
-
-  if (vtdec->session)
-    gst_vtdec_invalidate_session (vtdec);
-
-  vtdec->reorder_queue_frame_delay = 0;
-
   if (!strcmp (caps_name, "video/x-h264")) {
     cm_format = kCMVideoCodecType_H264;
-    vtdec->reorder_queue_frame_delay = 16;
   } else if (!strcmp (caps_name, "video/mpeg")) {
     cm_format = kCMVideoCodecType_MPEG2Video;
   } else if (!strcmp (caps_name, "image/jpeg")) {
     cm_format = kCMVideoCodecType_JPEG;
   }
 
+  if (cm_format == kCMVideoCodecType_H264 && state->codec_data == NULL) {
+    GST_INFO_OBJECT (vtdec, "no codec data, wait for one");
+    return TRUE;
+  }
+
+  if (vtdec->session)
+    gst_vtdec_invalidate_session (vtdec);
+
   gst_video_info_from_caps (&vtdec->video_info, state->caps);
 
+  if (cm_format == kCMVideoCodecType_H264) {
+    if (!compute_h264_decode_picture_buffer_length (vtdec, state->codec_data,
+            &vtdec->reorder_queue_frame_delay)) {
+      return FALSE;
+    }
+  } else {
+    vtdec->reorder_queue_frame_delay = 0;
+  }
+
   if (state->codec_data) {
     format_description = create_format_description_from_codec_data (vtdec,
         cm_format, state->codec_data);
@@ -558,3 +566,116 @@ gst_vtdec_push_frames_if_needed (GstVtdec * vtdec, gboolean drain,
 
   return ret;
 }
+
+static gboolean
+parse_h264_profile_and_level_from_codec_data (GstVtdec * vtdec,
+    GstBuffer * codec_data, int *profile, int *level)
+{
+  GstMapInfo map;
+  guint8 *data;
+  gint size;
+  gboolean ret = TRUE;
+
+  gst_buffer_map (codec_data, &map, GST_MAP_READ);
+  data = map.data;
+  size = map.size;
+
+  /* parse the avcC data */
+  if (size < 7)
+    goto avcc_too_small;
+
+  /* parse the version, this must be 1 */
+  if (data[0] != 1)
+    goto wrong_version;
+
+  /* AVCProfileIndication */
+  /* profile_compat */
+  /* AVCLevelIndication */
+  if (profile)
+    *profile = data[1];
+
+  if (level)
+    *level = data[3];
+
+out:
+  gst_buffer_unmap (codec_data, &map);
+
+  return ret;
+
+avcc_too_small:
+  GST_ELEMENT_ERROR (vtdec, STREAM, DECODE, (NULL),
+      ("invalid codec_data buffer length"));
+  ret = FALSE;
+  goto out;
+
+wrong_version:
+  GST_ELEMENT_ERROR (vtdec, STREAM, DECODE, (NULL),
+      ("wrong avcC version in codec_data"));
+  ret = FALSE;
+  goto out;
+}
+
+static int
+get_dpb_max_mb_s_from_level (int level)
+{
+  switch (level) {
+    case 10:
+      /* 1b?? */
+      return 396;
+    case 11:
+      return 900;
+    case 12:
+    case 13:
+    case 20:
+      return 2376;
+    case 21:
+      return 4752;
+    case 22:
+      return 8100;
+    case 31:
+      return 18000;
+    case 32:
+      return 20480;
+    case 40:
+    case 41:
+      return 32768;
+    case 42:
+      return 34816;
+    case 50:
+      return 110400;
+    case 51:
+    case 52:
+      return 184320;
+    default:
+      return -1;
+  }
+}
+
+static gboolean
+compute_h264_decode_picture_buffer_length (GstVtdec * vtdec,
+    GstBuffer * codec_data, int *length)
+{
+  int profile, level;
+  int dpb_mb_size = 16;
+  int max_dpb_size_frames = 16;
+  int max_dpb_mb_s = -1;
+  int width_in_mb_s = vtdec->video_info.width / dpb_mb_size;
+  int height_in_mb_s = vtdec->video_info.height / dpb_mb_size;
+
+  if (!parse_h264_profile_and_level_from_codec_data (vtdec, codec_data,
+          &profile, &level))
+    return FALSE;
+
+  max_dpb_mb_s = get_dpb_max_mb_s_from_level (level);
+  if (max_dpb_mb_s == -1) {
+    GST_ELEMENT_ERROR (vtdec, STREAM, DECODE, (NULL),
+        ("invalid level in codec_data, could not compute max_dpb_mb_s"));
+    return FALSE;
+  }
+
+  /* this formula is specified in sections A.3.1.h and A.3.2.f of the 2009
+   * edition of the standard */
+  *length = MIN (floor (max_dpb_mb_s / (width_in_mb_s * height_in_mb_s)),
+      max_dpb_size_frames);
+  return TRUE;
+}