decoder: h264: dynamically allocate the DPB.
authorGwenole Beauchesne <gwenole.beauchesne@intel.com>
Thu, 1 May 2014 17:16:09 +0000 (19:16 +0200)
committerGwenole Beauchesne <gwenole.beauchesne@intel.com>
Wed, 21 May 2014 17:59:52 +0000 (19:59 +0200)
Dynamically allocate the Decoded Picture Buffer (DPB) and add provisions
for supporting the MVC allocation requirements.

gst-libs/gst/vaapi/gstvaapidecoder_h264.c

index 2792442..a9ee127 100644 (file)
@@ -424,9 +424,11 @@ struct _GstVaapiDecoderH264Private {
     GstVaapiParserInfoH264     *prev_pi;
     GstVaapiParserInfoH264     *prev_slice_pi;
     GstVaapiFrameStore         *prev_frame;
-    GstVaapiFrameStore         *dpb[16];
+    GstVaapiFrameStore        **dpb;
     guint                       dpb_count;
     guint                       dpb_size;
+    guint                       dpb_size_max;
+    guint                       max_views;
     GstVaapiProfile             profile;
     GstVaapiEntrypoint          entrypoint;
     GstVaapiChromaType          chroma_type;
@@ -481,6 +483,14 @@ struct _GstVaapiDecoderH264Class {
 static gboolean
 exec_ref_pic_marking(GstVaapiDecoderH264 *decoder, GstVaapiPictureH264 *picture);
 
+/* Determines if the supplied profile is one of the MVC set */
+static gboolean
+is_mvc_profile(GstH264Profile profile)
+{
+    return profile == GST_H264_PROFILE_MULTIVIEW_HIGH ||
+        profile == GST_H264_PROFILE_STEREO_HIGH;
+}
+
 /* Determines the view order index (VOIdx) from the supplied view_id */
 static gint
 get_view_order_index(GstH264SPS *sps, guint16 view_id)
@@ -500,10 +510,19 @@ get_view_order_index(GstH264SPS *sps, guint16 view_id)
     return -1;
 }
 
+/* Determines NumViews */
+static guint
+get_num_views(GstH264SPS *sps)
+{
+    return 1 + (sps->extension_type == GST_H264_NAL_EXTENSION_MVC ?
+        sps->extension.mvc.num_views_minus1 : 0);
+}
+
 /* Get number of reference frames to use */
 static guint
 get_max_dec_frame_buffering(GstH264SPS *sps)
 {
+    guint num_views, max_dpb_frames;
     guint max_dec_frame_buffering, PicSizeMbs;
     GstVaapiLevelH264 level;
     const GstVaapiH264LevelLimits *level_limits;
@@ -514,13 +533,18 @@ get_max_dec_frame_buffering(GstH264SPS *sps)
     else
         level = gst_vaapi_utils_h264_get_level(sps->level_idc);
     level_limits = gst_vaapi_utils_h264_get_level_limits(level);
-    if (!level_limits)
-        return 16;
-
-    PicSizeMbs = ((sps->pic_width_in_mbs_minus1 + 1) *
-                  (sps->pic_height_in_map_units_minus1 + 1) *
-                  (sps->frame_mbs_only_flag ? 1 : 2));
-    max_dec_frame_buffering = level_limits->MaxDpbMbs / PicSizeMbs;
+    if (G_UNLIKELY(!level_limits)) {
+        GST_FIXME("unsupported level_idc value (%d)", sps->level_idc);
+        max_dec_frame_buffering = 16;
+    }
+    else {
+        PicSizeMbs = ((sps->pic_width_in_mbs_minus1 + 1) *
+                      (sps->pic_height_in_map_units_minus1 + 1) *
+                      (sps->frame_mbs_only_flag ? 1 : 2));
+        max_dec_frame_buffering = level_limits->MaxDpbMbs / PicSizeMbs;
+    }
+    if (is_mvc_profile(sps->profile_idc))
+        max_dec_frame_buffering <<= 1;
 
     /* VUI parameters */
     if (sps->vui_parameters_present_flag) {
@@ -542,8 +566,10 @@ get_max_dec_frame_buffering(GstH264SPS *sps)
         }
     }
 
-    if (max_dec_frame_buffering > 16)
-        max_dec_frame_buffering = 16;
+    num_views = get_num_views(sps);
+    max_dpb_frames = 16 * (num_views > 1 ? g_bit_storage(num_views - 1) : 1);
+    if (max_dec_frame_buffering > max_dpb_frames)
+        max_dec_frame_buffering = max_dpb_frames;
     else if (max_dec_frame_buffering < sps->num_ref_frames)
         max_dec_frame_buffering = sps->num_ref_frames;
     return MAX(1, max_dec_frame_buffering);
@@ -760,13 +786,30 @@ dpb_add(GstVaapiDecoderH264 *decoder, GstVaapiPictureH264 *picture)
     return TRUE;
 }
 
-static inline void
-dpb_reset(GstVaapiDecoderH264 *decoder, GstH264SPS *sps)
+static gboolean
+dpb_reset(GstVaapiDecoderH264 *decoder, guint dpb_size)
 {
     GstVaapiDecoderH264Private * const priv = &decoder->priv;
 
-    priv->dpb_size = get_max_dec_frame_buffering(sps);
+    if (dpb_size < priv->dpb_count)
+        return FALSE;
+
+    if (dpb_size > priv->dpb_size_max) {
+        priv->dpb = g_try_realloc_n(priv->dpb, dpb_size, sizeof(*priv->dpb));
+        if (!priv->dpb)
+            return FALSE;
+        memset(&priv->dpb[priv->dpb_size_max], 0,
+            (dpb_size - priv->dpb_size_max) * sizeof(*priv->dpb));
+        priv->dpb_size_max = dpb_size;
+    }
+
+    if (priv->dpb_size < dpb_size)
+        priv->dpb_size = dpb_size;
+    else if (dpb_size < priv->dpb_count)
+        return FALSE;
+
     GST_DEBUG("DPB size %u", priv->dpb_size);
+    return TRUE;
 }
 
 static GstVaapiDecoderStatus
@@ -831,6 +874,10 @@ gst_vaapi_decoder_h264_destroy(GstVaapiDecoder *base_decoder)
 
     gst_vaapi_decoder_h264_close(decoder);
 
+    g_free(priv->dpb);
+    priv->dpb = NULL;
+    priv->dpb_size = 0;
+
     for (i = 0; i < G_N_ELEMENTS(priv->pps); i++)
         gst_vaapi_parser_info_h264_replace(&priv->pps[i], NULL);
     gst_vaapi_parser_info_h264_replace(&priv->active_pps, NULL);
@@ -965,7 +1012,13 @@ ensure_context(GstVaapiDecoderH264 *decoder, GstH264SPS *sps)
     GstVaapiProfile profile;
     GstVaapiChromaType chroma_type;
     gboolean reset_context = FALSE;
-    guint mb_width, mb_height;
+    guint mb_width, mb_height, dpb_size;
+
+    dpb_size = get_max_dec_frame_buffering(sps);
+    if (priv->dpb_size < dpb_size) {
+        GST_DEBUG("DPB size increased");
+        reset_context = TRUE;
+    }
 
     profile = get_profile(decoder, sps);
     if (!profile) {
@@ -1022,14 +1075,15 @@ ensure_context(GstVaapiDecoderH264 *decoder, GstH264SPS *sps)
     info.chroma_type = priv->chroma_type;
     info.width      = sps->width;
     info.height     = sps->height;
-    info.ref_frames = get_max_dec_frame_buffering(sps);
+    info.ref_frames = dpb_size;
 
     if (!gst_vaapi_decoder_ensure_context(GST_VAAPI_DECODER(decoder), &info))
         return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
     priv->has_context = TRUE;
 
     /* Reset DPB */
-    dpb_reset(decoder, sps);
+    if (!dpb_reset(decoder, dpb_size))
+        return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
     return GST_VAAPI_DECODER_STATUS_SUCCESS;
 }
 
@@ -1153,6 +1207,9 @@ parse_sps(GstVaapiDecoderH264 *decoder, GstVaapiDecoderUnit *unit)
     if (result != GST_H264_PARSER_OK)
         return get_status(result);
 
+    /* Reset defaults */
+    priv->max_views = 1;
+
     priv->parser_state |= GST_H264_VIDEO_STATE_GOT_SPS;
     return GST_VAAPI_DECODER_STATUS_SUCCESS;
 }
@@ -1231,6 +1288,7 @@ parse_slice(GstVaapiDecoderH264 *decoder, GstVaapiDecoderUnit *unit)
     GstH264SliceHdr * const slice_hdr = &pi->data.slice_hdr;
     GstH264NalUnit * const nalu = &pi->nalu;
     GstH264ParserResult result;
+    guint num_views;
 
     GST_DEBUG("parse slice");
 
@@ -1275,6 +1333,12 @@ parse_slice(GstVaapiDecoderH264 *decoder, GstVaapiDecoderUnit *unit)
     if (result != GST_H264_PARSER_OK)
         return get_status(result);
 
+    num_views = get_num_views(slice_hdr->pps->sequence);
+    if (priv->max_views < num_views) {
+        priv->max_views = num_views;
+        GST_DEBUG("maximum number of views changed to %u", num_views);
+    }
+
     priv->parser_state |= GST_H264_VIDEO_STATE_GOT_SLICE;
     return GST_VAAPI_DECODER_STATUS_SUCCESS;
 }