From 733a780e9d35b5aa45c92c4c3b90cbc39ef7aae1 Mon Sep 17 00:00:00 2001 From: Alessandro Decina Date: Wed, 11 Dec 2013 07:58:23 +0100 Subject: [PATCH] applemedia: vtdec: set reorder queue length to the max DPB length 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 | 145 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 12 deletions(-) diff --git a/sys/applemedia/vtdec.c b/sys/applemedia/vtdec.c index 7442906865..bc5218d73e 100644 --- a/sys/applemedia/vtdec.c +++ b/sys/applemedia/vtdec.c @@ -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,7 +185,15 @@ 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) { + if (!strcmp (caps_name, "video/x-h264")) { + cm_format = kCMVideoCodecType_H264; + } 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; } @@ -191,19 +201,17 @@ gst_vtdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state) 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; - } - 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; +}