diff options
author | Sreerenj Balachandran <sreerenj.balachandran@intel.com> | 2016-07-29 14:58:49 +0300 |
---|---|---|
committer | Sreerenj Balachandran <sreerenj.balachandran@intel.com> | 2016-07-29 15:05:56 +0300 |
commit | acdbd721fb090c964b3fbf1829485db31b06b63c (patch) | |
tree | 6b8f2238892f2d8285415f25b33eb9070b7e69bf | |
parent | 6d58490974af9a885b7935d059f9046dd96fac86 (diff) |
encoder: h264: Add Hierarchical-B encodeadvanced_enc
Frames are encoded as differnt layers. Frame in a particular
layer will use pictures in lower or same layer as references.
Which means decoder can drop the frames in upper layer ,but still
decode lower layer frames.
B-frames, except the one in top most layer are reference frames, all
th base layer frames are I or P.
eg: with 3 temporal layers
T3: B1 B3 B5 B7
T2: B2 B6
T1: I0 P4 P8
T1, T2, T3: Temporal Layers
P1...Pn: P-Frames:
B1...Bn: B-frames:
T1: I0->P4 , P4->P8 etc..
T2: I0--> B2 <-- P4
T3: I0--> B1 <-- B2, B2 --> B3 <-- P4
https://bugzilla.gnome.org/show_bug.cgi?id=725536
-rw-r--r-- | gst-libs/gst/vaapi/gstvaapiencoder_h264.c | 154 |
1 files changed, 145 insertions, 9 deletions
diff --git a/gst-libs/gst/vaapi/gstvaapiencoder_h264.c b/gst-libs/gst/vaapi/gstvaapiencoder_h264.c index e14208e4..90d8dae5 100644 --- a/gst-libs/gst/vaapi/gstvaapiencoder_h264.c +++ b/gst-libs/gst/vaapi/gstvaapiencoder_h264.c @@ -804,6 +804,7 @@ struct _GstVaapiEncoderH264 guint temporal_level_div[MAX_TEMPORAL_LEVELS]; /* to find the temporal id */ guint prediction_type; guint abs_diff_pic_num_list0; + guint abs_diff_pic_num_list1; GstClockTime cts_offset; gboolean config_changed; @@ -985,8 +986,10 @@ bs_write_slice (GstBitWriter * bs, } if ((slice_param->slice_type != 2) && (slice_param->slice_type != 4)) { - if ((encoder->prediction_type == - GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_P) + if (((encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_P) + || (encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B)) && (encoder->abs_diff_pic_num_list0 > 1)) ref_pic_list_modification_flag_l0 = 1; @@ -1002,9 +1005,25 @@ bs_write_slice (GstBitWriter * bs, } } - if (slice_param->slice_type == 1) + /* B-frame */ + if (slice_param->slice_type == 1) { + if ((encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B) + && (encoder->abs_diff_pic_num_list1 > 1)) + ref_pic_list_modification_flag_l1 = 1; + WRITE_UINT32 (bs, ref_pic_list_modification_flag_l1, 1); + if (ref_pic_list_modification_flag_l1) { + /*modification_of_pic_num_idc */ + WRITE_UE (bs, 0); + /* abs_diff_pic_num_minus1 */ + WRITE_UE (bs, encoder->abs_diff_pic_num_list1 - 1); + /*modification_of_pic_num_idc */ + WRITE_UE (bs, 3); + } + } + /* we have: weighted_pred_flag == FALSE and */ /* : weighted_bipred_idc == FALSE */ if ((pic_param->pic_fields.bits.weighted_pred_flag && @@ -1349,6 +1368,17 @@ set_b_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder) g_assert (pic && encoder); g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE); pic->type = GST_VAAPI_PICTURE_TYPE_B; + + if (encoder->temporal_levels > 1) { + /* while doing temporal encoding, b frames are allowded + * only in hierarchical-b mode */ + g_assert (encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B); + /* temporal_encode: set b-frame as reference frames in + * hierarchical-b encode unless they belongs to highest level */ + if (!is_temporal_id_max (encoder, pic->temporal_id)) + GST_VAAPI_PICTURE_FLAG_SET (pic, GST_VAAPI_ENC_PICTURE_FLAG_REFERENCE); + } } /* Marks the supplied picture as a P-frame */ @@ -1693,7 +1723,10 @@ get_nal_hdr_attributes (GstVaapiEncPicture * picture, *nal_unit_type = GST_H264_NAL_SLICE; break; case GST_VAAPI_PICTURE_TYPE_B: - *nal_ref_idc = GST_H264_NAL_REF_IDC_NONE; + if (!GST_VAAPI_ENC_PICTURE_IS_REFRENCE (picture)) + *nal_ref_idc = GST_H264_NAL_REF_IDC_NONE; + else + *nal_ref_idc = GST_H264_NAL_REF_IDC_LOW; *nal_unit_type = GST_H264_NAL_SLICE; break; default: @@ -1859,6 +1892,7 @@ reference_list_update (GstVaapiEncoderH264 * encoder, return TRUE; } +/* update reflist0 for hierarchical-p and hierarchical-b encode */ static void reflist0_init_hierarchical (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture, GQueue * ref_list, @@ -1895,6 +1929,44 @@ reflist0_init_hierarchical (GstVaapiEncoderH264 * encoder, encoder->abs_diff_pic_num_list0 = picture->frame_num - tmp->frame_num; } +/* update reflist1 for hierarchical-b encode */ +static void +reflist1_init_hierarchical_b (GstVaapiEncoderH264 * encoder, + GstVaapiEncPicture * picture, GQueue * ref_list, + GstVaapiEncoderH264Ref ** reflist_1, guint * reflist_1_count) +{ + GstVaapiEncoderH264Ref *tmp = NULL; + GList *iter; + guint count = 0, i; + + /* base layer should have only P frames */ + g_assert (picture->temporal_id != 0); + + iter = g_queue_peek_tail_link (ref_list); + for (; iter; iter = g_list_previous (iter)) { + tmp = (GstVaapiEncoderH264Ref *) iter->data; + + g_assert (tmp && tmp->poc != picture->poc); + + if (_poc_greater_than (tmp->poc, picture->poc, encoder->max_pic_order_cnt) + && (tmp->temporal_id < picture->temporal_id)) { + reflist_1[count++] = tmp; + } + } + + g_assert (count != 0); + + /* Only need one ref frame */ + tmp = reflist_1[0]; + for (i = 1; i < count; i++) { + if (tmp->poc > reflist_1[i]->poc) + tmp = reflist_1[i]; + } + reflist_1[0] = tmp; + *reflist_1_count = 1; + encoder->abs_diff_pic_num_list1 = picture->frame_num - tmp->frame_num; +} + static gboolean reference_list_init_hierarchical (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture, @@ -1911,6 +1983,16 @@ reference_list_init_hierarchical (GstVaapiEncoderH264 * encoder, if (picture->type != GST_VAAPI_PICTURE_TYPE_B) return TRUE; + g_assert (encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B); + + reflist1_init_hierarchical_b (encoder, picture, ref_list, + reflist_1, reflist_1_count); + + /* Fixme: Combine and optimize reflist_0_init and reflist_1_init. + * Keeping separate blocks for now to make it more + * readable and easy to debug */ + return TRUE; } @@ -1934,8 +2016,11 @@ reference_list_init (GstVaapiEncoderH264 * encoder, return TRUE; /* reference picture handling for hierarchial encode */ - if (encoder->prediction_type == - GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_P) { + if ((encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_P) + || (encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B)) { + return reference_list_init_hierarchical (encoder, picture, &ref_pool->ref_list, reflist_0, reflist_0_count, reflist_1, reflist_1_count); @@ -2597,6 +2682,11 @@ reset_properties (GstVaapiEncoderH264 * encoder) GST_WARNING ("Disabling b-frame since the driver doesn't supporting it in low-power encode"); encoder->num_bframes = 0; + + /* no hierarchical-b for low-power encode */ + if (encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B) + encoder->prediction_type = GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT; } /* if temporal scalability enabled then use hierarchical-p @@ -2605,8 +2695,10 @@ reset_properties (GstVaapiEncoderH264 * encoder) && encoder->prediction_type == GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT) encoder->prediction_type = GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_P; - if (encoder->prediction_type == - GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_P) { + if ((encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_P) + || (encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B)) { /* Hierarchical prediction should have a temporal level count * greater than one and we use 4 temporal levels as default */ @@ -2629,7 +2721,14 @@ reset_properties (GstVaapiEncoderH264 * encoder) encoder->num_views = 1; /* no b-frames in Hierarchical-P */ - encoder->num_bframes = 0; + if (encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_P) + encoder->num_bframes = 0; + + /* reset number of b-frames in Hierarchical-B */ + if (encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B) + encoder->num_bframes = (1 << (encoder->temporal_levels - 1)) - 1; /* temporal_level_div[] is helpful to find out the temporal level * where each frame should belongs */ @@ -2841,6 +2940,22 @@ get_temporal_id (GstVaapiEncoderH264 * encoder, guint32 display_order) return 0; } +/* reorder_list sorting for hierarchical-b encode */ +static gint +sort_hierarchical_b (gconstpointer a, gconstpointer b, gpointer user_data) +{ + GstVaapiEncPicture *pic1 = (GstVaapiEncPicture *) a; + GstVaapiEncPicture *pic2 = (GstVaapiEncPicture *) b; + + if (pic1->type != GST_VAAPI_PICTURE_TYPE_B) + return 1; + if (pic2->type != GST_VAAPI_PICTURE_TYPE_B) + return -1; + if (pic1->temporal_id == pic2->temporal_id) + return pic1->poc - pic2->poc; + else + return pic1->temporal_id - pic2->temporal_id; +} static void set_frame_num (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture) @@ -2897,6 +3012,14 @@ gst_vaapi_encoder_h264_reordering (GstVaapiEncoder * base_encoder, g_assert (encoder->num_bframes > 0); g_return_val_if_fail (!g_queue_is_empty (&reorder_pool->reorder_frame_list), GST_VAAPI_ENCODER_STATUS_ERROR_UNKNOWN); + + /* sort the queued list of frames for hierarchical-b based on + * temporal level where each frame belongs */ + if (encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B) + g_queue_sort (&reorder_pool->reorder_frame_list, sort_hierarchical_b, + NULL); + picture = g_queue_pop_head (&reorder_pool->reorder_frame_list); g_assert (picture); if (g_queue_is_empty (&reorder_pool->reorder_frame_list)) { @@ -2936,6 +3059,18 @@ gst_vaapi_encoder_h264_reordering (GstVaapiEncoder * base_encoder, p_pic = g_queue_pop_tail (&reorder_pool->reorder_frame_list); set_p_frame (p_pic, encoder); + + /* for hierarchical-b, if idr-period reached , make sure the + * most recent queued frame get encoded as a reference + * p-frame in base-layer */ + if (encoder->prediction_type == + GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B) { + p_pic->temporal_id = 0; + GST_VAAPI_PICTURE_FLAG_SET (p_pic, + GST_VAAPI_ENC_PICTURE_FLAG_REFERENCE); + } + /* Fix : make sure the detached head is non-ref, currently it is ref */ + g_queue_foreach (&reorder_pool->reorder_frame_list, (GFunc) set_b_frame, encoder); set_key_frame (picture, encoder, is_idr); @@ -3085,6 +3220,7 @@ gst_vaapi_encoder_h264_init (GstVaapiEncoder * base_encoder) encoder->temporal_levels = 1; encoder->prediction_type = GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT; encoder->abs_diff_pic_num_list0 = 1; + encoder->abs_diff_pic_num_list1 = 1; memset (encoder->view_ids, 0, sizeof (encoder->view_ids)); /* re-ordering list initialize */ |