gstreamer/gst-libs/gst/vaapi/gstvaapifeipak_h264.c

1890 lines
60 KiB
C
Raw Normal View History

/*
* gstvaapifeipak_h264.c - H.264 FEI PAK
*
* Copyright (C) 2012-2016 Intel Corporation
* Author: Chen, Xiaomin <xiaomin.chen@intel.com>
* Author: Sreerenj Balachandran <sreerenj.balachandran@intel.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
/* GValueArray has deprecated without providing an alternative in glib >= 2.32
* See https://bugzilla.gnome.org/show_bug.cgi?id=667228
*/
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include "sysdeps.h"
#include <va/va.h>
#include <gst/base/gstbitwriter.h>
#include <gst/codecparsers/gsth264parser.h>
#include "gstvaapicompat.h"
#include "gstvaapiencoder_priv.h"
#include "gstvaapifeipak_h264.h"
#include "gstvaapiutils_h264_priv.h"
#include "gstvaapicodedbufferproxy_priv.h"
#include "gstvaapisurface.h"
#define DEBUG 1
#include "gstvaapidebug.h"
/* Define the maximum number of views supported */
#define MAX_NUM_VIEWS 10
/* Define the maximum value for view-id */
#define MAX_VIEW_ID 1023
/* Default CPB length (in milliseconds) */
#define DEFAULT_CPB_LENGTH 1500
/* Scale factor for CPB size (HRD cpb_size_scale: min = 4) */
#define SX_CPB_SIZE 4
/* Scale factor for bitrate (HRD bit_rate_scale: min = 6) */
#define SX_BITRATE 6
/* Define default rate control mode ("constant-qp") */
#define DEFAULT_RATECONTROL GST_VAAPI_RATECONTROL_CQP
/* Supported set of VA rate controls, within this implementation */
#define SUPPORTED_RATECONTROLS \
(GST_VAAPI_RATECONTROL_MASK (CQP) | \
GST_VAAPI_RATECONTROL_MASK (CBR) | \
GST_VAAPI_RATECONTROL_MASK (VBR) | \
GST_VAAPI_RATECONTROL_MASK (VBR_CONSTRAINED))
/* Supported set of tuning options, within this implementation */
#define SUPPORTED_TUNE_OPTIONS \
(GST_VAAPI_ENCODER_TUNE_MASK (NONE) | \
GST_VAAPI_ENCODER_TUNE_MASK (HIGH_COMPRESSION) | \
GST_VAAPI_ENCODER_TUNE_MASK (LOW_POWER))
/* Supported set of VA packed headers, within this implementation */
#define SUPPORTED_PACKED_HEADERS \
(VA_ENC_PACKED_HEADER_SEQUENCE | \
VA_ENC_PACKED_HEADER_PICTURE | \
VA_ENC_PACKED_HEADER_SLICE | \
VA_ENC_PACKED_HEADER_RAW_DATA)
#define GST_H264_NAL_REF_IDC_NONE 0
#define GST_H264_NAL_REF_IDC_LOW 1
#define GST_H264_NAL_REF_IDC_MEDIUM 2
#define GST_H264_NAL_REF_IDC_HIGH 3
typedef struct
{
GstVaapiSurfaceProxy *pic;
guint poc;
guint frame_num;
} GstVaapiFEIPakH264Ref;
typedef enum
{
GST_VAAPI_FEIPAK_H264_REORD_NONE = 0,
GST_VAAPI_FEIPAK_H264_REORD_DUMP_FRAMES = 1,
GST_VAAPI_FEIPAK_H264_REORD_WAIT_FRAMES = 2
} GstVaapiFEIPakH264ReorderState;
typedef struct _GstVaapiH264FEIPakViewRefPool
{
GQueue ref_list;
guint max_ref_frames;
guint max_reflist0_count;
guint max_reflist1_count;
} GstVaapiH264FEIPakViewRefPool;
typedef struct _GstVaapiH264FEIPakViewReorderPool
{
GQueue reorder_frame_list;
guint reorder_state;
guint frame_index;
guint frame_count; /* monotonically increasing with in every idr period */
guint cur_frame_num;
guint cur_present_index;
} GstVaapiH264FEIPakViewReorderPool;
/* ------------------------------------------------------------------------- */
/* --- H.264 FEI PAK --- */
/* ------------------------------------------------------------------------- */
struct _GstVaapiFEIPakH264
{
GstVaapiMiniObject parent_instance;
GstVaapiEncoder *encoder;
VAEncSequenceParameterBufferH264 h264_sps;
VAEncPictureParameterBufferH264 h264_pps;
GArray *h264_slice_params;
GstVaapiProfile profile;
GstVaapiEntrypoint entrypoint;
GstVaapiDisplay *display;
VAContextID va_context;
guint8 profile_idc;
guint8 hw_max_profile_idc;
guint32 num_slices;
guint slice_type;
gboolean is_idr;
guint32 num_bframes;
guint32 mb_width;
guint32 mb_height;
gboolean props_reconfigured;
gboolean config_changed;
guint32 max_pic_order_cnt;
guint32 log2_max_pic_order_cnt;
GstBuffer *sps_data;
GstBuffer *subset_sps_data;
GstBuffer *pps_data;
guint32 num_ref_frames; // set reference frame num
/* MVC */
gboolean is_mvc;
guint32 view_idx; /* View Order Index (VOIdx) */
guint32 num_views;
guint16 view_ids[MAX_NUM_VIEWS];
GstVaapiH264FEIPakViewRefPool ref_pools[MAX_NUM_VIEWS];
};
static inline gboolean
_poc_greater_than (guint poc1, guint poc2, guint max_poc)
{
return (((poc1 - poc2) & (max_poc - 1)) < max_poc / 2);
}
/* ------------------------------------------------------------------------- */
/* --- H.264 Bitstream Writer --- */
/* ------------------------------------------------------------------------- */
#define WRITE_UINT32(bs, val, nbits) do { \
if (!gst_bit_writer_put_bits_uint32 (bs, val, nbits)) { \
GST_WARNING ("failed to write uint32, nbits: %d", nbits); \
goto bs_error; \
} \
} while (0)
#define WRITE_UE(bs, val) do { \
if (!bs_write_ue (bs, val)) { \
GST_WARNING ("failed to write ue(v)"); \
goto bs_error; \
} \
} while (0)
#define WRITE_SE(bs, val) do { \
if (!bs_write_se (bs, val)) { \
GST_WARNING ("failed to write se(v)"); \
goto bs_error; \
} \
} while (0)
/* Write an unsigned integer Exp-Golomb-coded syntax element. i.e. ue(v) */
static gboolean
bs_write_ue (GstBitWriter * bs, guint32 value)
{
guint32 size_in_bits = 0;
guint32 tmp_value = ++value;
while (tmp_value) {
++size_in_bits;
tmp_value >>= 1;
}
if (size_in_bits > 1
&& !gst_bit_writer_put_bits_uint32 (bs, 0, size_in_bits - 1))
return FALSE;
if (!gst_bit_writer_put_bits_uint32 (bs, value, size_in_bits))
return FALSE;
return TRUE;
}
/* Write a signed integer Exp-Golomb-coded syntax element. i.e. se(v) */
static gboolean
bs_write_se (GstBitWriter * bs, gint32 value)
{
guint32 new_val;
if (value <= 0)
new_val = -(value << 1);
else
new_val = (value << 1) - 1;
if (!bs_write_ue (bs, new_val))
return FALSE;
return TRUE;
}
/* Write the NAL unit header */
static gboolean
bs_write_nal_header (GstBitWriter * bs, guint32 nal_ref_idc,
guint32 nal_unit_type)
{
WRITE_UINT32 (bs, 0, 1);
WRITE_UINT32 (bs, nal_ref_idc, 2);
WRITE_UINT32 (bs, nal_unit_type, 5);
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write NAL unit header");
return FALSE;
}
}
/* Write the MVC NAL unit header extension */
static gboolean
bs_write_nal_header_mvc_extension (GstBitWriter * bs,
GstVaapiEncPicture * picture, guint32 view_id)
{
guint32 svc_extension_flag = 0;
guint32 non_idr_flag = 1;
guint32 priority_id = 0;
guint32 temporal_id = 0;
guint32 anchor_pic_flag = 0;
guint32 inter_view_flag = 0;
if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
non_idr_flag = 0;
if (picture->type == GST_VAAPI_PICTURE_TYPE_I)
anchor_pic_flag = 1;
/* svc_extension_flag == 0 for mvc stream */
WRITE_UINT32 (bs, svc_extension_flag, 1);
WRITE_UINT32 (bs, non_idr_flag, 1);
WRITE_UINT32 (bs, priority_id, 6);
WRITE_UINT32 (bs, view_id, 10);
WRITE_UINT32 (bs, temporal_id, 3);
WRITE_UINT32 (bs, anchor_pic_flag, 1);
WRITE_UINT32 (bs, inter_view_flag, 1);
WRITE_UINT32 (bs, 1, 1);
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write NAL unit header");
return FALSE;
}
}
/* Write the NAL unit trailing bits */
static gboolean
bs_write_trailing_bits (GstBitWriter * bs)
{
if (!gst_bit_writer_put_bits_uint32 (bs, 1, 1))
goto bs_error;
gst_bit_writer_align_bytes_unchecked (bs, 0);
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write NAL unit trailing bits");
return FALSE;
}
}
/* Write an SPS NAL unit */
static gboolean
bs_write_sps_data (GstBitWriter * bs,
const VAEncSequenceParameterBufferH264 * seq_param, GstVaapiProfile profile,
const VAEncMiscParameterHRD * hrd_params)
{
guint8 profile_idc;
guint32 constraint_set0_flag, constraint_set1_flag;
guint32 constraint_set2_flag, constraint_set3_flag;
guint32 gaps_in_frame_num_value_allowed_flag = 0; // ??
gboolean nal_hrd_parameters_present_flag;
guint32 b_qpprime_y_zero_transform_bypass = 0;
guint32 residual_color_transform_flag = 0;
guint32 pic_height_in_map_units =
(seq_param->seq_fields.bits.frame_mbs_only_flag ?
seq_param->picture_height_in_mbs : seq_param->picture_height_in_mbs / 2);
guint32 mb_adaptive_frame_field =
!seq_param->seq_fields.bits.frame_mbs_only_flag;
guint32 i = 0;
profile_idc = gst_vaapi_utils_h264_get_profile_idc (profile);
constraint_set0_flag = /* A.2.1 (baseline profile constraints) */
profile == GST_VAAPI_PROFILE_H264_BASELINE ||
profile == GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE;
constraint_set1_flag = /* A.2.2 (main profile constraints) */
profile == GST_VAAPI_PROFILE_H264_MAIN ||
profile == GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE;
constraint_set2_flag = 0;
constraint_set3_flag = 0;
/* profile_idc */
WRITE_UINT32 (bs, profile_idc, 8);
/* constraint_set0_flag */
WRITE_UINT32 (bs, constraint_set0_flag, 1);
/* constraint_set1_flag */
WRITE_UINT32 (bs, constraint_set1_flag, 1);
/* constraint_set2_flag */
WRITE_UINT32 (bs, constraint_set2_flag, 1);
/* constraint_set3_flag */
WRITE_UINT32 (bs, constraint_set3_flag, 1);
/* reserved_zero_4bits */
WRITE_UINT32 (bs, 0, 4);
/* level_idc */
WRITE_UINT32 (bs, seq_param->level_idc, 8);
/* seq_parameter_set_id */
WRITE_UE (bs, seq_param->seq_parameter_set_id);
if (profile == GST_VAAPI_PROFILE_H264_HIGH ||
profile == GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH ||
profile == GST_VAAPI_PROFILE_H264_STEREO_HIGH) {
/* for high profile */
/* chroma_format_idc = 1, 4:2:0 */
WRITE_UE (bs, seq_param->seq_fields.bits.chroma_format_idc);
if (3 == seq_param->seq_fields.bits.chroma_format_idc) {
WRITE_UINT32 (bs, residual_color_transform_flag, 1);
}
/* bit_depth_luma_minus8 */
WRITE_UE (bs, seq_param->bit_depth_luma_minus8);
/* bit_depth_chroma_minus8 */
WRITE_UE (bs, seq_param->bit_depth_chroma_minus8);
/* b_qpprime_y_zero_transform_bypass */
WRITE_UINT32 (bs, b_qpprime_y_zero_transform_bypass, 1);
/* seq_scaling_matrix_present_flag */
g_assert (seq_param->seq_fields.bits.seq_scaling_matrix_present_flag == 0);
WRITE_UINT32 (bs,
seq_param->seq_fields.bits.seq_scaling_matrix_present_flag, 1);
#if 0
if (seq_param->seq_fields.bits.seq_scaling_matrix_present_flag) {
for (i = 0;
i < (seq_param->seq_fields.bits.chroma_format_idc != 3 ? 8 : 12);
i++) {
gst_bit_writer_put_bits_uint8 (bs,
seq_param->seq_fields.bits.seq_scaling_list_present_flag, 1);
if (seq_param->seq_fields.bits.seq_scaling_list_present_flag) {
g_assert (0);
/* FIXME, need write scaling list if seq_scaling_matrix_present_flag ==1 */
}
}
}
#endif
}
/* log2_max_frame_num_minus4 */
WRITE_UE (bs, seq_param->seq_fields.bits.log2_max_frame_num_minus4);
/* pic_order_cnt_type */
WRITE_UE (bs, seq_param->seq_fields.bits.pic_order_cnt_type);
if (seq_param->seq_fields.bits.pic_order_cnt_type == 0) {
/* log2_max_pic_order_cnt_lsb_minus4 */
WRITE_UE (bs, seq_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4);
} else if (seq_param->seq_fields.bits.pic_order_cnt_type == 1) {
g_assert (0 && "only POC type 0 is supported");
WRITE_UINT32 (bs,
seq_param->seq_fields.bits.delta_pic_order_always_zero_flag, 1);
WRITE_SE (bs, seq_param->offset_for_non_ref_pic);
WRITE_SE (bs, seq_param->offset_for_top_to_bottom_field);
WRITE_UE (bs, seq_param->num_ref_frames_in_pic_order_cnt_cycle);
for (i = 0; i < seq_param->num_ref_frames_in_pic_order_cnt_cycle; i++) {
WRITE_SE (bs, seq_param->offset_for_ref_frame[i]);
}
}
/* num_ref_frames */
WRITE_UE (bs, seq_param->max_num_ref_frames);
/* gaps_in_frame_num_value_allowed_flag */
WRITE_UINT32 (bs, gaps_in_frame_num_value_allowed_flag, 1);
/* pic_width_in_mbs_minus1 */
WRITE_UE (bs, seq_param->picture_width_in_mbs - 1);
/* pic_height_in_map_units_minus1 */
WRITE_UE (bs, pic_height_in_map_units - 1);
/* frame_mbs_only_flag */
WRITE_UINT32 (bs, seq_param->seq_fields.bits.frame_mbs_only_flag, 1);
if (!seq_param->seq_fields.bits.frame_mbs_only_flag) { //ONLY mbs
g_assert (0 && "only progressive frames encoding is supported");
WRITE_UINT32 (bs, mb_adaptive_frame_field, 1);
}
/* direct_8x8_inference_flag */
WRITE_UINT32 (bs, 0, 1);
/* frame_cropping_flag */
WRITE_UINT32 (bs, seq_param->frame_cropping_flag, 1);
if (seq_param->frame_cropping_flag) {
/* frame_crop_left_offset */
WRITE_UE (bs, seq_param->frame_crop_left_offset);
/* frame_crop_right_offset */
WRITE_UE (bs, seq_param->frame_crop_right_offset);
/* frame_crop_top_offset */
WRITE_UE (bs, seq_param->frame_crop_top_offset);
/* frame_crop_bottom_offset */
WRITE_UE (bs, seq_param->frame_crop_bottom_offset);
}
/* vui_parameters_present_flag */
WRITE_UINT32 (bs, seq_param->vui_parameters_present_flag, 1);
if (seq_param->vui_parameters_present_flag) {
/* aspect_ratio_info_present_flag */
WRITE_UINT32 (bs,
seq_param->vui_fields.bits.aspect_ratio_info_present_flag, 1);
if (seq_param->vui_fields.bits.aspect_ratio_info_present_flag) {
WRITE_UINT32 (bs, seq_param->aspect_ratio_idc, 8);
if (seq_param->aspect_ratio_idc == 0xFF) {
WRITE_UINT32 (bs, seq_param->sar_width, 16);
WRITE_UINT32 (bs, seq_param->sar_height, 16);
}
}
/* overscan_info_present_flag */
WRITE_UINT32 (bs, 0, 1);
/* video_signal_type_present_flag */
WRITE_UINT32 (bs, 0, 1);
/* chroma_loc_info_present_flag */
WRITE_UINT32 (bs, 0, 1);
/* timing_info_present_flag */
WRITE_UINT32 (bs, seq_param->vui_fields.bits.timing_info_present_flag, 1);
if (seq_param->vui_fields.bits.timing_info_present_flag) {
WRITE_UINT32 (bs, seq_param->num_units_in_tick, 32);
WRITE_UINT32 (bs, seq_param->time_scale, 32);
WRITE_UINT32 (bs, 1, 1); /* fixed_frame_rate_flag */
}
/* nal_hrd_parameters_present_flag */
nal_hrd_parameters_present_flag = seq_param->bits_per_second > 0;
WRITE_UINT32 (bs, nal_hrd_parameters_present_flag, 1);
if (nal_hrd_parameters_present_flag) {
/* hrd_parameters */
/* cpb_cnt_minus1 */
WRITE_UE (bs, 0);
WRITE_UINT32 (bs, SX_BITRATE - 6, 4); /* bit_rate_scale */
WRITE_UINT32 (bs, SX_CPB_SIZE - 4, 4); /* cpb_size_scale */
for (i = 0; i < 1; ++i) {
/* bit_rate_value_minus1[0] */
WRITE_UE (bs, (seq_param->bits_per_second >> SX_BITRATE) - 1);
/* cpb_size_value_minus1[0] */
WRITE_UE (bs, (hrd_params->buffer_size >> SX_CPB_SIZE) - 1);
/* cbr_flag[0] */
WRITE_UINT32 (bs, 1, 1);
}
/* initial_cpb_removal_delay_length_minus1 */
WRITE_UINT32 (bs, 23, 5);
/* cpb_removal_delay_length_minus1 */
WRITE_UINT32 (bs, 23, 5);
/* dpb_output_delay_length_minus1 */
WRITE_UINT32 (bs, 23, 5);
/* time_offset_length */
WRITE_UINT32 (bs, 23, 5);
}
/* vcl_hrd_parameters_present_flag */
WRITE_UINT32 (bs, 0, 1);
if (nal_hrd_parameters_present_flag
|| 0 /*vcl_hrd_parameters_present_flag */ ) {
/* low_delay_hrd_flag */
WRITE_UINT32 (bs, 0, 1);
}
/* pic_struct_present_flag */
WRITE_UINT32 (bs, 1, 1);
/* bs_restriction_flag */
WRITE_UINT32 (bs, 0, 1);
}
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write SPS NAL unit");
return FALSE;
}
}
static gboolean
bs_write_sps (GstVaapiFEIPakH264 * feipak, GstBitWriter * bs,
const VAEncSequenceParameterBufferH264 * seq_param, GstVaapiProfile profile,
const VAEncMiscParameterHRD * hrd_params)
{
if (!bs_write_sps_data (bs, seq_param, profile, hrd_params))
return FALSE;
/* rbsp_trailing_bits */
bs_write_trailing_bits (bs);
return FALSE;
}
static gboolean
bs_write_subset_sps (GstVaapiFEIPakH264 * feipak, GstBitWriter * bs,
const VAEncSequenceParameterBufferH264 * seq_param, GstVaapiProfile profile,
guint num_views, guint16 * view_ids,
const VAEncMiscParameterHRD * hrd_params)
{
guint32 i, j, k;
if (!bs_write_sps_data (bs, seq_param, profile, hrd_params))
return FALSE;
if (profile == GST_VAAPI_PROFILE_H264_STEREO_HIGH ||
profile == GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH) {
guint32 num_views_minus1, num_level_values_signalled_minus1;
num_views_minus1 = num_views - 1;
g_assert (num_views_minus1 < 1024);
/* bit equal to one */
WRITE_UINT32 (bs, 1, 1);
WRITE_UE (bs, num_views_minus1);
for (i = 0; i <= num_views_minus1; i++)
WRITE_UE (bs, view_ids[i]);
for (i = 1; i <= num_views_minus1; i++) {
guint32 num_anchor_refs_l0 = 0;
guint32 num_anchor_refs_l1 = 0;
WRITE_UE (bs, num_anchor_refs_l0);
for (j = 0; j < num_anchor_refs_l0; j++)
WRITE_UE (bs, 0);
WRITE_UE (bs, num_anchor_refs_l1);
for (j = 0; j < num_anchor_refs_l1; j++)
WRITE_UE (bs, 0);
}
for (i = 1; i <= num_views_minus1; i++) {
guint32 num_non_anchor_refs_l0 = 0;
guint32 num_non_anchor_refs_l1 = 0;
WRITE_UE (bs, num_non_anchor_refs_l0);
for (j = 0; j < num_non_anchor_refs_l0; j++)
WRITE_UE (bs, 0);
WRITE_UE (bs, num_non_anchor_refs_l1);
for (j = 0; j < num_non_anchor_refs_l1; j++)
WRITE_UE (bs, 0);
}
/* num level values signalled minus1 */
num_level_values_signalled_minus1 = 0;
g_assert (num_level_values_signalled_minus1 < 64);
WRITE_UE (bs, num_level_values_signalled_minus1);
for (i = 0; i <= num_level_values_signalled_minus1; i++) {
guint16 num_applicable_ops_minus1 = 0;
g_assert (num_applicable_ops_minus1 < 1024);
WRITE_UINT32 (bs, seq_param->level_idc, 8);
WRITE_UE (bs, num_applicable_ops_minus1);
for (j = 0; j <= num_applicable_ops_minus1; j++) {
guint8 temporal_id = 0;
guint16 num_target_views_minus1 = 1;
WRITE_UINT32 (bs, temporal_id, 3);
WRITE_UE (bs, num_target_views_minus1);
for (k = 0; k <= num_target_views_minus1; k++)
WRITE_UE (bs, k);
WRITE_UE (bs, num_views_minus1);
}
}
/* mvc_vui_parameters_present_flag */
WRITE_UINT32 (bs, 0, 1);
}
/* additional_extension2_flag */
WRITE_UINT32 (bs, 0, 1);
/* rbsp_trailing_bits */
bs_write_trailing_bits (bs);
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write subset SPS NAL unit");
return FALSE;
}
return FALSE;
}
/* Write a PPS NAL unit */
static gboolean
bs_write_pps (GstBitWriter * bs,
const VAEncPictureParameterBufferH264 * pic_param, GstVaapiProfile profile)
{
guint32 num_slice_groups_minus1 = 0;
guint32 pic_init_qs_minus26 = 0;
guint32 redundant_pic_cnt_present_flag = 0;
/* pic_parameter_set_id */
WRITE_UE (bs, pic_param->pic_parameter_set_id);
/* seq_parameter_set_id */
WRITE_UE (bs, pic_param->seq_parameter_set_id);
/* entropy_coding_mode_flag */
WRITE_UINT32 (bs, pic_param->pic_fields.bits.entropy_coding_mode_flag, 1);
/* pic_order_present_flag */
WRITE_UINT32 (bs, pic_param->pic_fields.bits.pic_order_present_flag, 1);
/* slice_groups-1 */
WRITE_UE (bs, num_slice_groups_minus1);
if (num_slice_groups_minus1 > 0) {
/*FIXME*/ g_assert (0 && "unsupported arbitrary slice ordering (ASO)");
}
WRITE_UE (bs, pic_param->num_ref_idx_l0_active_minus1);
WRITE_UE (bs, pic_param->num_ref_idx_l1_active_minus1);
WRITE_UINT32 (bs, pic_param->pic_fields.bits.weighted_pred_flag, 1);
WRITE_UINT32 (bs, pic_param->pic_fields.bits.weighted_bipred_idc, 2);
/* pic_init_qp_minus26 */
WRITE_SE (bs, pic_param->pic_init_qp - 26);
/* pic_init_qs_minus26 */
WRITE_SE (bs, pic_init_qs_minus26);
/* chroma_qp_index_offset */
WRITE_SE (bs, pic_param->chroma_qp_index_offset);
WRITE_UINT32 (bs,
pic_param->pic_fields.bits.deblocking_filter_control_present_flag, 1);
WRITE_UINT32 (bs, pic_param->pic_fields.bits.constrained_intra_pred_flag, 1);
WRITE_UINT32 (bs, redundant_pic_cnt_present_flag, 1);
/* more_rbsp_data */
if (profile == GST_VAAPI_PROFILE_H264_HIGH) {
WRITE_UINT32 (bs, pic_param->pic_fields.bits.transform_8x8_mode_flag, 1);
WRITE_UINT32 (bs,
pic_param->pic_fields.bits.pic_scaling_matrix_present_flag, 1);
if (pic_param->pic_fields.bits.pic_scaling_matrix_present_flag) {
g_assert (0 && "unsupported scaling lists");
/* FIXME */
/*
for (i = 0; i <
(6+(-( (chroma_format_idc ! = 3) ? 2 : 6) * -pic_param->pic_fields.bits.transform_8x8_mode_flag));
i++) {
gst_bit_writer_put_bits_uint8(bs, pic_param->pic_fields.bits.pic_scaling_list_present_flag, 1);
}
*/
}
WRITE_SE (bs, pic_param->second_chroma_qp_index_offset);
}
/* rbsp_trailing_bits */
bs_write_trailing_bits (bs);
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write PPS NAL unit");
return FALSE;
}
}
/* Write a Slice NAL unit */
static gboolean
bs_write_slice (GstBitWriter * bs,
const VAEncSliceParameterBufferH264 * slice_param,
GstVaapiFEIPakH264 * feipak, GstVaapiEncPicture * picture)
{
const VAEncPictureParameterBufferH264 *const pic_param = picture->param;
guint32 field_pic_flag = 0;
guint32 ref_pic_list_modification_flag_l0 = 0;
guint32 ref_pic_list_modification_flag_l1 = 0;
guint32 no_output_of_prior_pics_flag = 0;
guint32 long_term_reference_flag = 0;
guint32 adaptive_ref_pic_marking_mode_flag = 0;
/* first_mb_in_slice */
WRITE_UE (bs, slice_param->macroblock_address);
/* slice_type */
WRITE_UE (bs, slice_param->slice_type);
/* pic_parameter_set_id */
WRITE_UE (bs, slice_param->pic_parameter_set_id);
/* frame_num */
WRITE_UINT32 (bs, picture->frame_num,
feipak->h264_sps.seq_fields.bits.log2_max_frame_num_minus4 + 4);
/* XXX: only frames (i.e. non-interlaced) are supported for now */
/* frame_mbs_only_flag == 0 */
/* idr_pic_id */
if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
WRITE_UE (bs, slice_param->idr_pic_id);
/* XXX: only POC type 0 is supported */
if (!feipak->h264_sps.seq_fields.bits.pic_order_cnt_type) {
WRITE_UINT32 (bs, slice_param->pic_order_cnt_lsb,
feipak->h264_sps.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 + 4);
/* bottom_field_pic_order_in_frame_present_flag is FALSE */
if (pic_param->pic_fields.bits.pic_order_present_flag && !field_pic_flag)
WRITE_SE (bs, slice_param->delta_pic_order_cnt_bottom);
} else if (feipak->h264_sps.seq_fields.bits.pic_order_cnt_type == 1 &&
!feipak->h264_sps.seq_fields.bits.delta_pic_order_always_zero_flag) {
WRITE_SE (bs, slice_param->delta_pic_order_cnt[0]);
if (pic_param->pic_fields.bits.pic_order_present_flag && !field_pic_flag)
WRITE_SE (bs, slice_param->delta_pic_order_cnt[1]);
}
/* redundant_pic_cnt_present_flag is FALSE, no redundant coded pictures */
/* only works for B-frames */
if (slice_param->slice_type == GST_H264_B_SLICE)
WRITE_UINT32 (bs, slice_param->direct_spatial_mv_pred_flag, 1);
/* not supporting SP slices */
if (slice_param->slice_type == 0 || slice_param->slice_type == 1) {
WRITE_UINT32 (bs, slice_param->num_ref_idx_active_override_flag, 1);
if (slice_param->num_ref_idx_active_override_flag) {
WRITE_UE (bs, slice_param->num_ref_idx_l0_active_minus1);
if (slice_param->slice_type == 1)
WRITE_UE (bs, slice_param->num_ref_idx_l1_active_minus1);
}
}
/* XXX: not supporting custom reference picture list modifications */
if ((slice_param->slice_type != 2) && (slice_param->slice_type != 4))
WRITE_UINT32 (bs, ref_pic_list_modification_flag_l0, 1);
if (slice_param->slice_type == 1)
WRITE_UINT32 (bs, ref_pic_list_modification_flag_l1, 1);
/* we have: weighted_pred_flag == FALSE and */
/* : weighted_bipred_idc == FALSE */
if ((pic_param->pic_fields.bits.weighted_pred_flag &&
(slice_param->slice_type == 0)) ||
((pic_param->pic_fields.bits.weighted_bipred_idc == 1) &&
(slice_param->slice_type == 1))) {
/* XXXX: add pred_weight_table() */
}
/* dec_ref_pic_marking() */
if (slice_param->slice_type == 0 || slice_param->slice_type == 2) {
if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture)) {
/* no_output_of_prior_pics_flag = 0 */
WRITE_UINT32 (bs, no_output_of_prior_pics_flag, 1);
/* long_term_reference_flag = 0 */
WRITE_UINT32 (bs, long_term_reference_flag, 1);
} else {
/* only sliding_window reference picture marking mode is supported */
/* adpative_ref_pic_marking_mode_flag = 0 */
WRITE_UINT32 (bs, adaptive_ref_pic_marking_mode_flag, 1);
}
}
/* cabac_init_idc */
if (pic_param->pic_fields.bits.entropy_coding_mode_flag &&
slice_param->slice_type != 2)
WRITE_UE (bs, slice_param->cabac_init_idc);
/*slice_qp_delta */
WRITE_SE (bs, slice_param->slice_qp_delta);
/* XXX: only supporting I, P and B type slices */
/* no sp_for_switch_flag and no slice_qs_delta */
if (pic_param->pic_fields.bits.deblocking_filter_control_present_flag) {
/* disable_deblocking_filter_idc */
WRITE_UE (bs, slice_param->disable_deblocking_filter_idc);
if (slice_param->disable_deblocking_filter_idc != 1) {
WRITE_SE (bs, slice_param->slice_alpha_c0_offset_div2);
WRITE_SE (bs, slice_param->slice_beta_offset_div2);
}
}
/* XXX: unsupported arbitrary slice ordering (ASO) */
/* num_slic_groups_minus1 should be zero */
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write Slice NAL unit");
return FALSE;
}
}
static inline void
_check_sps_pps_status (GstVaapiFEIPakH264 * feipak,
const guint8 * nal, guint32 size)
{
guint8 nal_type;
G_GNUC_UNUSED gsize ret; /* FIXME */
gboolean has_subset_sps;
g_assert (size);
has_subset_sps = !feipak->is_mvc || (feipak->subset_sps_data != NULL);
if (feipak->sps_data && feipak->pps_data && has_subset_sps)
return;
nal_type = nal[0] & 0x1F;
switch (nal_type) {
case GST_H264_NAL_SPS:
feipak->sps_data = gst_buffer_new_allocate (NULL, size, NULL);
ret = gst_buffer_fill (feipak->sps_data, 0, nal, size);
g_assert (ret == size);
break;
case GST_H264_NAL_SUBSET_SPS:
feipak->subset_sps_data = gst_buffer_new_allocate (NULL, size, NULL);
ret = gst_buffer_fill (feipak->subset_sps_data, 0, nal, size);
g_assert (ret == size);
break;
case GST_H264_NAL_PPS:
feipak->pps_data = gst_buffer_new_allocate (NULL, size, NULL);
ret = gst_buffer_fill (feipak->pps_data, 0, nal, size);
g_assert (ret == size);
break;
default:
break;
}
}
/* Determines the largest supported profile by the underlying hardware */
static gboolean
ensure_hw_profile_limits (GstVaapiFEIPakH264 * feipak)
{
GstVaapiDisplay *const display = feipak->display;
GArray *profiles;
guint i, profile_idc, max_profile_idc;
if (feipak->hw_max_profile_idc)
return TRUE;
profiles = gst_vaapi_display_get_encode_profiles (display);
if (!profiles)
return FALSE;
max_profile_idc = 0;
for (i = 0; i < profiles->len; i++) {
const GstVaapiProfile profile =
g_array_index (profiles, GstVaapiProfile, i);
profile_idc = gst_vaapi_utils_h264_get_profile_idc (profile);
if (!profile_idc)
continue;
if (max_profile_idc < profile_idc)
max_profile_idc = profile_idc;
}
g_array_unref (profiles);
feipak->hw_max_profile_idc = max_profile_idc;
return TRUE;
}
/* Fills in VA HRD parameters */
static void
fill_hrd_params (GstVaapiFEIPakH264 * feipak, VAEncMiscParameterHRD * hrd)
{
hrd->buffer_size = 0;
hrd->initial_buffer_fullness = 0;
}
/* Adds the supplied sequence header (SPS) to the list of packed
headers to pass down as-is to the feipak */
static gboolean
add_packed_sequence_header (GstVaapiFEIPakH264 * feipak,
GstVaapiEncPicture * picture, GstVaapiEncSequence * sequence)
{
GstVaapiEncPackedHeader *packed_seq;
GstBitWriter bs;
VAEncPackedHeaderParameterBuffer packed_seq_param = { 0 };
const VAEncSequenceParameterBufferH264 *const seq_param = sequence->param;
GstVaapiProfile profile = feipak->profile;
VAEncMiscParameterHRD hrd_params;
guint32 data_bit_size;
guint8 *data;
fill_hrd_params (feipak, &hrd_params);
gst_bit_writer_init (&bs, 128 * 8);
WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
bs_write_nal_header (&bs, GST_H264_NAL_REF_IDC_HIGH, GST_H264_NAL_SPS);
/* Set High profile for encoding the MVC base view. Otherwise, some
traditional decoder cannot recognize MVC profile streams with
only the base view in there */
if (profile == GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH ||
profile == GST_VAAPI_PROFILE_H264_STEREO_HIGH)
profile = GST_VAAPI_PROFILE_H264_HIGH;
bs_write_sps (feipak, &bs, seq_param, profile, &hrd_params);
g_assert (GST_BIT_WRITER_BIT_SIZE (&bs) % 8 == 0);
data_bit_size = GST_BIT_WRITER_BIT_SIZE (&bs);
data = GST_BIT_WRITER_DATA (&bs);
packed_seq_param.type = VAEncPackedHeaderSequence;
packed_seq_param.bit_length = data_bit_size;
packed_seq_param.has_emulation_bytes = 0;
packed_seq =
gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (feipak->encoder),
&packed_seq_param, sizeof (packed_seq_param), data,
(data_bit_size + 7) / 8);
g_assert (packed_seq);
gst_vaapi_enc_picture_add_packed_header (picture, packed_seq);
gst_vaapi_codec_object_replace (&packed_seq, NULL);
/* store sps data */
_check_sps_pps_status (feipak, data + 4, data_bit_size / 8 - 4);
gst_bit_writer_clear (&bs, TRUE);
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write SPS NAL unit");
gst_bit_writer_clear (&bs, TRUE);
return FALSE;
}
}
static gboolean
add_packed_sequence_header_mvc (GstVaapiFEIPakH264 * feipak,
GstVaapiEncPicture * picture, GstVaapiEncSequence * sequence)
{
GstVaapiEncPackedHeader *packed_seq;
GstBitWriter bs;
VAEncPackedHeaderParameterBuffer packed_header_param_buffer = { 0 };
const VAEncSequenceParameterBufferH264 *const seq_param = sequence->param;
VAEncMiscParameterHRD hrd_params;
guint32 data_bit_size;
guint8 *data;
fill_hrd_params (feipak, &hrd_params);
/* non-base layer, pack one subset sps */
gst_bit_writer_init (&bs, 128 * 8);
WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
bs_write_nal_header (&bs, GST_H264_NAL_REF_IDC_HIGH, GST_H264_NAL_SUBSET_SPS);
bs_write_subset_sps (feipak, &bs, seq_param, feipak->profile,
feipak->num_views, feipak->view_ids, &hrd_params);
g_assert (GST_BIT_WRITER_BIT_SIZE (&bs) % 8 == 0);
data_bit_size = GST_BIT_WRITER_BIT_SIZE (&bs);
data = GST_BIT_WRITER_DATA (&bs);
packed_header_param_buffer.type = VAEncPackedHeaderSequence;
packed_header_param_buffer.bit_length = data_bit_size;
packed_header_param_buffer.has_emulation_bytes = 0;
packed_seq =
gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (feipak->encoder),
&packed_header_param_buffer, sizeof (packed_header_param_buffer), data,
(data_bit_size + 7) / 8);
g_assert (packed_seq);
gst_vaapi_enc_picture_add_packed_header (picture, packed_seq);
gst_vaapi_mini_object_replace ((GstVaapiMiniObject **) & packed_seq, NULL);
/* store subset sps data */
_check_sps_pps_status (feipak, data + 4, data_bit_size / 8 - 4);
gst_bit_writer_clear (&bs, TRUE);
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write SPS NAL unit");
gst_bit_writer_clear (&bs, TRUE);
return FALSE;
}
}
/* Adds the supplied picture header (PPS) to the list of packed
headers to pass down as-is to the feipak */
static gboolean
add_packed_picture_header (GstVaapiFEIPakH264 * feipak,
GstVaapiEncPicture * picture)
{
GstVaapiEncPackedHeader *packed_pic;
GstBitWriter bs;
VAEncPackedHeaderParameterBuffer packed_pic_param = { 0 };
const VAEncPictureParameterBufferH264 *const pic_param = picture->param;
guint32 data_bit_size;
guint8 *data;
gst_bit_writer_init (&bs, 128 * 8);
WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
bs_write_nal_header (&bs, GST_H264_NAL_REF_IDC_HIGH, GST_H264_NAL_PPS);
bs_write_pps (&bs, pic_param, feipak->profile);
g_assert (GST_BIT_WRITER_BIT_SIZE (&bs) % 8 == 0);
data_bit_size = GST_BIT_WRITER_BIT_SIZE (&bs);
data = GST_BIT_WRITER_DATA (&bs);
packed_pic_param.type = VAEncPackedHeaderPicture;
packed_pic_param.bit_length = data_bit_size;
packed_pic_param.has_emulation_bytes = 0;
packed_pic =
gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (feipak->encoder),
&packed_pic_param, sizeof (packed_pic_param), data,
(data_bit_size + 7) / 8);
g_assert (packed_pic);
gst_vaapi_enc_picture_add_packed_header (picture, packed_pic);
gst_vaapi_codec_object_replace (&packed_pic, NULL);
/* store pps data */
_check_sps_pps_status (feipak, data + 4, data_bit_size / 8 - 4);
gst_bit_writer_clear (&bs, TRUE);
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write PPS NAL unit");
gst_bit_writer_clear (&bs, TRUE);
return FALSE;
}
}
static gboolean
get_nal_hdr_attributes (GstVaapiEncPicture * picture,
guint8 * nal_ref_idc, guint8 * nal_unit_type)
{
switch (picture->type) {
case GST_VAAPI_PICTURE_TYPE_I:
*nal_ref_idc = GST_H264_NAL_REF_IDC_HIGH;
if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
*nal_unit_type = GST_H264_NAL_SLICE_IDR;
else
*nal_unit_type = GST_H264_NAL_SLICE;
break;
case GST_VAAPI_PICTURE_TYPE_P:
*nal_ref_idc = GST_H264_NAL_REF_IDC_MEDIUM;
*nal_unit_type = GST_H264_NAL_SLICE;
break;
case GST_VAAPI_PICTURE_TYPE_B:
*nal_ref_idc = GST_H264_NAL_REF_IDC_NONE;
*nal_unit_type = GST_H264_NAL_SLICE;
break;
default:
return FALSE;
}
return TRUE;
}
/* Adds the supplied prefix nal header to the list of packed
headers to pass down as-is to the feipak */
static gboolean
add_packed_prefix_nal_header (GstVaapiFEIPakH264 * feipak,
GstVaapiEncPicture * picture, GstVaapiEncSlice * slice)
{
GstVaapiEncPackedHeader *packed_prefix_nal;
GstBitWriter bs;
VAEncPackedHeaderParameterBuffer packed_prefix_nal_param = { 0 };
guint32 data_bit_size;
guint8 *data;
guint8 nal_ref_idc, nal_unit_type;
gst_bit_writer_init (&bs, 128 * 8);
WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
if (!get_nal_hdr_attributes (picture, &nal_ref_idc, &nal_unit_type))
goto bs_error;
nal_unit_type = GST_H264_NAL_PREFIX_UNIT;
bs_write_nal_header (&bs, nal_ref_idc, nal_unit_type);
bs_write_nal_header_mvc_extension (&bs, picture, feipak->view_idx);
g_assert (GST_BIT_WRITER_BIT_SIZE (&bs) % 8 == 0);
data_bit_size = GST_BIT_WRITER_BIT_SIZE (&bs);
data = GST_BIT_WRITER_DATA (&bs);
packed_prefix_nal_param.type = VAEncPackedHeaderRawData;
packed_prefix_nal_param.bit_length = data_bit_size;
packed_prefix_nal_param.has_emulation_bytes = 0;
packed_prefix_nal =
gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (feipak->encoder),
&packed_prefix_nal_param, sizeof (packed_prefix_nal_param), data,
(data_bit_size + 7) / 8);
g_assert (packed_prefix_nal);
gst_vaapi_enc_slice_add_packed_header (slice, packed_prefix_nal);
gst_vaapi_codec_object_replace (&packed_prefix_nal, NULL);
gst_bit_writer_clear (&bs, TRUE);
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write Prefix NAL unit header");
gst_bit_writer_clear (&bs, TRUE);
return FALSE;
}
}
/* Adds the supplied slice header to the list of packed
headers to pass down as-is to the feipak */
static gboolean
add_packed_slice_header (GstVaapiFEIPakH264 * feipak,
GstVaapiEncPicture * picture, GstVaapiEncSlice * slice)
{
GstVaapiEncPackedHeader *packed_slice;
GstBitWriter bs;
VAEncPackedHeaderParameterBuffer packed_slice_param = { 0 };
const VAEncSliceParameterBufferH264 *const slice_param = slice->param;
guint32 data_bit_size;
guint8 *data;
guint8 nal_ref_idc, nal_unit_type;
gst_bit_writer_init (&bs, 128 * 8);
WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
if (!get_nal_hdr_attributes (picture, &nal_ref_idc, &nal_unit_type))
goto bs_error;
/* pack nal_unit_header_mvc_extension() for the non base view */
if (feipak->is_mvc && feipak->view_idx) {
bs_write_nal_header (&bs, nal_ref_idc, GST_H264_NAL_SLICE_EXT);
bs_write_nal_header_mvc_extension (&bs, picture,
feipak->view_ids[feipak->view_idx]);
} else
bs_write_nal_header (&bs, nal_ref_idc, nal_unit_type);
bs_write_slice (&bs, slice_param, feipak, picture);
data_bit_size = GST_BIT_WRITER_BIT_SIZE (&bs);
data = GST_BIT_WRITER_DATA (&bs);
packed_slice_param.type = VAEncPackedHeaderSlice;
packed_slice_param.bit_length = data_bit_size;
packed_slice_param.has_emulation_bytes = 0;
packed_slice =
gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (feipak->encoder),
&packed_slice_param, sizeof (packed_slice_param), data,
(data_bit_size + 7) / 8);
g_assert (packed_slice);
gst_vaapi_enc_slice_add_packed_header (slice, packed_slice);
gst_vaapi_codec_object_replace (&packed_slice, NULL);
gst_bit_writer_clear (&bs, TRUE);
return TRUE;
/* ERRORS */
bs_error:
{
GST_WARNING ("failed to write Slice NAL unit header");
gst_bit_writer_clear (&bs, TRUE);
return FALSE;
}
}
/* Reference picture management */
static void
reference_pic_free (GstVaapiFEIPakH264 * feipak, GstVaapiFEIPakH264Ref * ref)
{
if (!ref)
return;
if (ref->pic)
gst_vaapi_surface_proxy_unref (ref->pic);
g_slice_free (GstVaapiFEIPakH264Ref, ref);
}
static inline GstVaapiFEIPakH264Ref *
reference_pic_create (GstVaapiFEIPakH264 * feipak,
GstVaapiEncPicture * picture, GstVaapiSurfaceProxy * surface)
{
GstVaapiFEIPakH264Ref *const ref = g_slice_new0 (GstVaapiFEIPakH264Ref);
ref->pic = surface;
ref->frame_num = picture->frame_num;
ref->poc = picture->poc;
return ref;
}
static gboolean
reference_list_update (GstVaapiFEIPakH264 * feipak,
GstVaapiEncPicture * picture, GstVaapiSurfaceProxy * surface)
{
GstVaapiFEIPakH264Ref *ref;
GstVaapiH264FEIPakViewRefPool *const ref_pool =
&feipak->ref_pools[feipak->view_idx];
if (GST_VAAPI_PICTURE_TYPE_B == picture->type) {
gst_vaapi_surface_proxy_unref (surface);
return TRUE;
}
if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture)) {
while (!g_queue_is_empty (&ref_pool->ref_list))
reference_pic_free (feipak, g_queue_pop_head (&ref_pool->ref_list));
} else if (g_queue_get_length (&ref_pool->ref_list) >=
ref_pool->max_ref_frames) {
reference_pic_free (feipak, g_queue_pop_head (&ref_pool->ref_list));
}
ref = reference_pic_create (feipak, picture, surface);
g_queue_push_tail (&ref_pool->ref_list, ref);
g_assert (g_queue_get_length (&ref_pool->ref_list) <=
ref_pool->max_ref_frames);
return TRUE;
}
static gboolean
reference_list_init (GstVaapiFEIPakH264 * feipak,
GstVaapiEncPicture * picture,
GstVaapiFEIPakH264Ref ** reflist_0,
guint * reflist_0_count,
GstVaapiFEIPakH264Ref ** reflist_1, guint * reflist_1_count)
{
GstVaapiFEIPakH264Ref *tmp;
GstVaapiH264FEIPakViewRefPool *const ref_pool =
&feipak->ref_pools[feipak->view_idx];
GList *iter, *list_0_start = NULL, *list_1_start = NULL;
guint count;
*reflist_0_count = 0;
*reflist_1_count = 0;
if (picture->type == GST_VAAPI_PICTURE_TYPE_I)
return TRUE;
iter = g_queue_peek_tail_link (&ref_pool->ref_list);
for (; iter; iter = g_list_previous (iter)) {
tmp = (GstVaapiFEIPakH264Ref *) iter->data;
g_assert (tmp && tmp->poc != picture->poc);
if (_poc_greater_than (picture->poc, tmp->poc,
1 << (feipak->h264_sps.seq_fields.bits.
log2_max_pic_order_cnt_lsb_minus4 + 4))) {
list_0_start = iter;
list_1_start = g_list_next (iter);
break;
}
}
/* order reflist_0 */
g_assert (list_0_start);
iter = list_0_start;
count = 0;
for (; iter; iter = g_list_previous (iter)) {
reflist_0[count] = (GstVaapiFEIPakH264Ref *) iter->data;
++count;
}
*reflist_0_count = count;
if (picture->type != GST_VAAPI_PICTURE_TYPE_B)
return TRUE;
/* order reflist_1 */
count = 0;
iter = list_1_start;
for (; iter; iter = g_list_next (iter)) {
reflist_1[count] = (GstVaapiFEIPakH264Ref *) iter->data;
++count;
}
*reflist_1_count = count;
return TRUE;
}
/* Fills in VA sequence parameter buffer */
static gboolean
fill_sequence (GstVaapiFEIPakH264 * feipak, GstVaapiEncSequence * sequence)
{
VAEncSequenceParameterBufferH264 *const seq_param = sequence->param;
memset (seq_param, 0, sizeof (VAEncSequenceParameterBufferH264));
*seq_param = feipak->h264_sps;
return TRUE;
}
/* Fills in VA picture parameter buffer */
static gboolean
fill_picture (GstVaapiFEIPakH264 * feipak, GstVaapiEncPicture * picture,
GstVaapiCodedBuffer * codedbuf, GstVaapiSurfaceProxy * surface)
{
VAEncPictureParameterBufferH264 *const pic_param = picture->param;
GstVaapiH264FEIPakViewRefPool *const ref_pool =
&feipak->ref_pools[feipak->view_idx];
GstVaapiFEIPakH264Ref *ref_pic;
GList *reflist;
guint i;
memset (pic_param, 0, sizeof (VAEncPictureParameterBufferH264));
*pic_param = feipak->h264_pps;
feipak->is_idr = feipak->h264_pps.pic_fields.bits.idr_pic_flag;
/* reference list, */
pic_param->CurrPic.picture_id = GST_VAAPI_SURFACE_PROXY_SURFACE_ID (surface);
pic_param->CurrPic.TopFieldOrderCnt = picture->poc;
pic_param->CurrPic.frame_idx = picture->frame_num;
i = 0;
if (picture->type != GST_VAAPI_PICTURE_TYPE_I) {
for (reflist = g_queue_peek_head_link (&ref_pool->ref_list);
reflist; reflist = g_list_next (reflist)) {
ref_pic = reflist->data;
g_assert (ref_pic && ref_pic->pic &&
GST_VAAPI_SURFACE_PROXY_SURFACE_ID (ref_pic->pic) != VA_INVALID_ID);
pic_param->ReferenceFrames[i].picture_id =
GST_VAAPI_SURFACE_PROXY_SURFACE_ID (ref_pic->pic);
pic_param->ReferenceFrames[i].TopFieldOrderCnt = ref_pic->poc;
pic_param->ReferenceFrames[i].flags |=
VA_PICTURE_H264_SHORT_TERM_REFERENCE;
pic_param->ReferenceFrames[i].frame_idx = ref_pic->frame_num;
++i;
}
g_assert (i <= 16 && i <= ref_pool->max_ref_frames);
}
for (; i < 16; ++i) {
pic_param->ReferenceFrames[i].picture_id = VA_INVALID_ID;
pic_param->ReferenceFrames[i].frame_idx = VA_PICTURE_H264_INVALID;
}
pic_param->coded_buf = GST_VAAPI_OBJECT_ID (codedbuf);
return TRUE;
}
/* Adds slice headers to picture */
static gboolean
add_slice_headers (GstVaapiFEIPakH264 * feipak, GstVaapiEncPicture * picture,
GstVaapiFEIPakH264Ref ** reflist_0, guint reflist_0_count,
GstVaapiFEIPakH264Ref ** reflist_1, guint reflist_1_count)
{
VAEncSliceParameterBufferH264 *slice_param;
GstVaapiEncSlice *slice;
guint slice_of_mbs, slice_mod_mbs, cur_slice_mbs;
guint mb_size;
guint last_mb_index;
guint i_slice, i_ref;
GArray *h264_slice_params = feipak->h264_slice_params;
g_assert (picture);
mb_size = feipak->mb_width * feipak->mb_height;
g_assert (feipak->num_slices && feipak->num_slices < mb_size);
slice_of_mbs = mb_size / feipak->num_slices;
slice_mod_mbs = mb_size % feipak->num_slices;
last_mb_index = 0;
for (i_slice = 0; i_slice < feipak->num_slices; ++i_slice) {
cur_slice_mbs = slice_of_mbs;
if (slice_mod_mbs) {
++cur_slice_mbs;
--slice_mod_mbs;
}
slice = GST_VAAPI_ENC_SLICE_NEW (H264, feipak->encoder);
g_assert (slice && slice->param_id != VA_INVALID_ID);
slice_param = slice->param;
memset (slice_param, 0, sizeof (VAEncSliceParameterBufferH264));
*slice_param =
g_array_index (h264_slice_params, VAEncSliceParameterBufferH264,
i_slice);
g_assert ((gint8) slice_param->slice_type != -1);
g_assert (slice_param->num_ref_idx_l0_active_minus1 >= 0);
g_assert (slice_param->num_ref_idx_l1_active_minus1 == 0);
i_ref = 0;
if (picture->type != GST_VAAPI_PICTURE_TYPE_I) {
for (; i_ref < reflist_0_count; ++i_ref) {
slice_param->RefPicList0[i_ref].picture_id =
GST_VAAPI_SURFACE_PROXY_SURFACE_ID (reflist_0[i_ref]->pic);
slice_param->RefPicList0[i_ref].TopFieldOrderCnt =
reflist_0[i_ref]->poc;
slice_param->RefPicList0[i_ref].flags |=
VA_PICTURE_H264_SHORT_TERM_REFERENCE;
slice_param->RefPicList0[i_ref].frame_idx = reflist_0[i_ref]->frame_num;
}
g_assert (i_ref >= 1);
}
for (; i_ref < G_N_ELEMENTS (slice_param->RefPicList0); ++i_ref) {
slice_param->RefPicList0[i_ref].picture_id = VA_INVALID_SURFACE;
slice_param->RefPicList0[i_ref].frame_idx = VA_PICTURE_H264_INVALID;
}
i_ref = 0;
if (picture->type == GST_VAAPI_PICTURE_TYPE_B) {
for (; i_ref < reflist_1_count; ++i_ref) {
slice_param->RefPicList1[i_ref].picture_id =
GST_VAAPI_SURFACE_PROXY_SURFACE_ID (reflist_1[i_ref]->pic);
slice_param->RefPicList1[i_ref].TopFieldOrderCnt =
reflist_1[i_ref]->poc;
slice_param->RefPicList1[i_ref].flags |=
VA_PICTURE_H264_SHORT_TERM_REFERENCE;
slice_param->RefPicList1[i_ref].frame_idx = reflist_1[i_ref]->frame_num;
}
g_assert (i_ref == 1);
}
for (; i_ref < G_N_ELEMENTS (slice_param->RefPicList1); ++i_ref) {
slice_param->RefPicList1[i_ref].picture_id = VA_INVALID_SURFACE;
slice_param->RefPicList1[i_ref].frame_idx = VA_PICTURE_H264_INVALID;
}
/* set calculation for next slice */
last_mb_index += cur_slice_mbs;
/* add packed Prefix NAL unit before each Coded slice NAL in base view */
if (feipak->is_mvc && !feipak->view_idx
&& !add_packed_prefix_nal_header (feipak, picture, slice))
goto error_create_packed_prefix_nal_hdr;
if (!add_packed_slice_header (feipak, picture, slice))
goto error_create_packed_slice_hdr;
gst_vaapi_enc_picture_add_slice (picture, slice);
gst_vaapi_codec_object_replace (&slice, NULL);
}
g_assert (last_mb_index == mb_size);
return TRUE;
error_create_packed_slice_hdr:
{
GST_ERROR ("failed to create packed slice header buffer");
gst_vaapi_codec_object_replace (&slice, NULL);
return FALSE;
}
error_create_packed_prefix_nal_hdr:
{
GST_ERROR ("failed to create packed prefix nal header buffer");
gst_vaapi_codec_object_replace (&slice, NULL);
return FALSE;
}
}
/* Generates and submits SPS header accordingly into the bitstream */
static gboolean
ensure_sequence (GstVaapiFEIPakH264 * feipak, GstVaapiEncPicture * picture)
{
GstVaapiEncSequence *sequence = NULL;
if (!feipak->config_changed || picture->type != GST_VAAPI_PICTURE_TYPE_I)
return TRUE;
sequence = GST_VAAPI_ENC_SEQUENCE_NEW (H264, feipak->encoder);
if (!sequence || !fill_sequence (feipak, sequence))
goto error_create_seq_param;
/* add subset sps for non-base view and sps for base view */
if (feipak->is_mvc && feipak->view_idx) {
if (!add_packed_sequence_header_mvc (feipak, picture, sequence))
goto error_create_packed_seq_hdr;
} else {
if (!add_packed_sequence_header (feipak, picture, sequence))
goto error_create_packed_seq_hdr;
}
if (sequence) {
gst_vaapi_enc_picture_set_sequence (picture, sequence);
gst_vaapi_codec_object_replace (&sequence, NULL);
}
if (!feipak->is_mvc || feipak->view_idx > 0)
feipak->config_changed = FALSE;
return TRUE;
/* ERRORS */
error_create_seq_param:
{
GST_ERROR ("failed to create sequence parameter buffer (SPS)");
gst_vaapi_codec_object_replace (&sequence, NULL);
return FALSE;
}
error_create_packed_seq_hdr:
{
GST_ERROR ("failed to create packed sequence header buffer");
gst_vaapi_codec_object_replace (&sequence, NULL);
return FALSE;
}
}
/* Generates additional fei control parameters */
static gboolean
ensure_fei_misc_params (GstVaapiFEIPakH264 * feipak,
GstVaapiEncPicture * picture, GstVaapiCodedBufferProxy * codedbuf_proxy)
{
GstVaapiEncMiscParam *misc = NULL;
VAEncMiscParameterFEIFrameControlH264 *misc_fei_pic_control_param;
/*fei pic control params */
misc = GST_VAAPI_ENC_FEI_MISC_PARAM_NEW (H264, feipak->encoder);
g_assert (misc);
if (!misc)
return FALSE;
misc_fei_pic_control_param = misc->data;
misc_fei_pic_control_param->function = VA_FEI_FUNCTION_PAK;
misc_fei_pic_control_param->mv_predictor = VA_INVALID_ID;
misc_fei_pic_control_param->qp = VA_INVALID_ID;
misc_fei_pic_control_param->mb_ctrl = VA_INVALID_ID;
g_assert (codedbuf_proxy->mbcode != NULL);
g_assert (codedbuf_proxy->mv != NULL);
misc_fei_pic_control_param->mb_code_data =
GST_VAAPI_FEI_CODEC_OBJECT (codedbuf_proxy->mbcode)->param_id;
misc_fei_pic_control_param->mv_data =
GST_VAAPI_FEI_CODEC_OBJECT (codedbuf_proxy->mv)->param_id;
gst_vaapi_enc_picture_add_misc_param (picture, misc);
gst_vaapi_codec_object_replace (&misc, NULL);
return TRUE;
}
/* Generates additional control parameters */
static gboolean
ensure_misc_params (GstVaapiFEIPakH264 * feipak, GstVaapiEncPicture * picture)
{
GstVaapiEncMiscParam *misc = NULL;
/* HRD params */
misc = GST_VAAPI_ENC_MISC_PARAM_NEW (HRD, feipak->encoder);
g_assert (misc);
if (!misc)
return FALSE;
fill_hrd_params (feipak, misc->data);
gst_vaapi_enc_picture_add_misc_param (picture, misc);
gst_vaapi_codec_object_replace (&misc, NULL);
return TRUE;
}
/* Generates and submits PPS header accordingly into the bitstream */
static gboolean
ensure_picture (GstVaapiFEIPakH264 * feipak, GstVaapiEncPicture * picture,
GstVaapiCodedBufferProxy * codedbuf_proxy, GstVaapiSurfaceProxy * surface)
{
GstVaapiCodedBuffer *const codedbuf =
GST_VAAPI_CODED_BUFFER_PROXY_BUFFER (codedbuf_proxy);
gboolean res = FALSE;
res = fill_picture (feipak, picture, codedbuf, surface);
if (!res)
return FALSE;
if (picture->type == GST_VAAPI_PICTURE_TYPE_I
&& !add_packed_picture_header (feipak, picture)) {
GST_ERROR ("set picture packed header failed");
return FALSE;
}
return TRUE;
}
/* Generates slice headers */
static gboolean
ensure_slices (GstVaapiFEIPakH264 * feipak, GstVaapiEncPicture * picture)
{
GstVaapiFEIPakH264Ref *reflist_0[16];
GstVaapiFEIPakH264Ref *reflist_1[16];
GstVaapiH264FEIPakViewRefPool *const ref_pool =
&feipak->ref_pools[feipak->view_idx];
guint reflist_0_count = 0, reflist_1_count = 0;
g_assert (picture);
if (picture->type != GST_VAAPI_PICTURE_TYPE_I &&
!reference_list_init (feipak, picture,
reflist_0, &reflist_0_count, reflist_1, &reflist_1_count)) {
GST_ERROR ("reference list reorder failed");
return FALSE;
}
g_assert (reflist_0_count + reflist_1_count <= ref_pool->max_ref_frames);
if (reflist_0_count > ref_pool->max_reflist0_count)
reflist_0_count = ref_pool->max_reflist0_count;
if (reflist_1_count > ref_pool->max_reflist1_count)
reflist_1_count = ref_pool->max_reflist1_count;
if (!add_slice_headers (feipak, picture,
reflist_0, reflist_0_count, reflist_1, reflist_1_count))
return FALSE;
return TRUE;
}
/* Constructs profile and level information based on user-defined limits */
static GstVaapiEncoderStatus
ensure_profile_and_level (GstVaapiFEIPakH264 * feipak)
{
const GstVaapiProfile profile = feipak->profile;
/* Check HW constraints */
if (!ensure_hw_profile_limits (feipak))
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
if (feipak->profile_idc > feipak->hw_max_profile_idc)
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
if (feipak->profile != profile) {
feipak->config_changed = TRUE;
}
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
static void
reset_properties (GstVaapiFEIPakH264 * feipak)
{
guint i;
guint max_reflist0_count;
if (feipak->num_bframes > 0) {
if (feipak->num_ref_frames == 1) {
GST_INFO ("num ref frames is modified as 2 as b frame is set");
feipak->num_ref_frames = 2;
}
max_reflist0_count = feipak->num_ref_frames - 1;
} else {
max_reflist0_count = feipak->num_ref_frames;
}
max_reflist0_count = max_reflist0_count > 5 ? 5 : max_reflist0_count;
for (i = 0; i < feipak->num_views; i++) {
GstVaapiH264FEIPakViewRefPool *const ref_pool = &feipak->ref_pools[i];
ref_pool->max_reflist0_count = max_reflist0_count;
ref_pool->max_reflist1_count = feipak->num_bframes > 0;
ref_pool->max_ref_frames = ref_pool->max_reflist0_count
+ ref_pool->max_reflist1_count;
}
}
GstVaapiEncoderStatus
gst_vaapi_feipak_h264_encode (GstVaapiFEIPakH264 * feipak,
GstVaapiEncPicture * picture, GstVaapiCodedBufferProxy * codedbuf,
GstVaapiSurfaceProxy * surface, GstVaapiFeiInfoToPakH264 * info_to_pak)
{
GstVaapiEncoderStatus ret = GST_VAAPI_ENCODER_STATUS_ERROR_UNKNOWN;
GstVaapiSurfaceProxy *reconstruct = surface;
GstVaapiSurfaceProxy *proxy = picture->proxy;
VAEncSliceParameterBufferH264 slice_header;
g_assert (GST_VAAPI_SURFACE_PROXY_SURFACE (reconstruct));
g_assert (GST_VAAPI_SURFACE_PROXY_SURFACE (proxy));
g_assert (info_to_pak != NULL);
feipak->h264_sps = info_to_pak->h264_enc_sps;
feipak->h264_pps = info_to_pak->h264_enc_pps;
feipak->h264_slice_params = info_to_pak->h264_slice_headers;
feipak->mb_width = feipak->h264_sps.picture_width_in_mbs;
feipak->mb_height = feipak->h264_sps.picture_height_in_mbs;
slice_header =
g_array_index (feipak->h264_slice_params, VAEncSliceParameterBufferH264,
0);
feipak->slice_type = slice_header.slice_type;
if (!ensure_sequence (feipak, picture))
goto error;
if (!ensure_misc_params (feipak, picture))
goto error;
if (!ensure_fei_misc_params (feipak, picture, codedbuf))
goto error;
if (!ensure_picture (feipak, picture, codedbuf, reconstruct))
goto error;
if (!ensure_slices (feipak, picture))
goto error;
if (!gst_vaapi_enc_picture_encode (picture))
goto error;
if (!reference_list_update (feipak, picture, reconstruct))
goto error;
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
error:
return ret;
}
GstVaapiEncoderStatus
gst_vaapi_feipak_h264_flush (GstVaapiFEIPakH264 * feipak)
{
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
GstVaapiEncoderStatus
gst_vaapi_feipak_h264_reconfigure (GstVaapiFEIPakH264 * feipak,
VAContextID va_context, GstVaapiProfile profile,
guint8 profile_idc, guint mb_width, guint mb_height,
guint32 num_views, guint slices_num, guint32 num_ref_frames)
{
GstVaapiEncoderStatus status;
if (mb_width != feipak->mb_width || mb_height != feipak->mb_height) {
feipak->mb_width = mb_width;
feipak->mb_height = mb_height;
feipak->config_changed = TRUE;
}
feipak->va_context = va_context;
/* Take number of MVC views from input caps if provided */
feipak->num_views = num_views;
feipak->is_mvc = feipak->num_views > 1;
feipak->profile_idc = profile_idc;
feipak->profile = profile;
feipak->num_slices = slices_num;
feipak->num_ref_frames = num_ref_frames;
status = ensure_profile_and_level (feipak);
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
return status;
reset_properties (feipak);
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
static gboolean
gst_vaapi_feipak_h264_init (GstVaapiFEIPakH264 * feipak,
GstVaapiEncoder * encoder, GstVaapiDisplay * display,
VAContextID va_context)
{
guint32 i;
feipak->encoder = encoder;
/* Default encoding entrypoint */
feipak->entrypoint = GST_VAAPI_ENTRYPOINT_SLICE_ENCODE_FEI;
feipak->h264_slice_params = NULL;
/* Multi-view coding information */
feipak->is_mvc = FALSE;
feipak->num_views = 1;
feipak->view_idx = 0;
feipak->display = display;
feipak->va_context = va_context;
feipak->num_bframes = 0;
feipak->is_idr = FALSE;
/* default num ref frames */
feipak->num_ref_frames = 1;
memset (feipak->view_ids, 0, sizeof (feipak->view_ids));
feipak->props_reconfigured = FALSE;
/* reference list info initialize */
for (i = 0; i < MAX_NUM_VIEWS; i++) {
GstVaapiH264FEIPakViewRefPool *const ref_pool = &feipak->ref_pools[i];
g_queue_init (&ref_pool->ref_list);
ref_pool->max_ref_frames = 0;
ref_pool->max_reflist0_count = 1;
ref_pool->max_reflist1_count = 1;
}
return TRUE;
}
static void
gst_vaapi_feipak_h264_finalize (GstVaapiFEIPakH264 * feipak)
{
GstVaapiFEIPakH264Ref *ref;
guint32 i;
gst_buffer_replace (&feipak->sps_data, NULL);
gst_buffer_replace (&feipak->subset_sps_data, NULL);
gst_buffer_replace (&feipak->pps_data, NULL);
/* reference list info de-init */
for (i = 0; i < MAX_NUM_VIEWS; i++) {
GstVaapiH264FEIPakViewRefPool *const ref_pool = &feipak->ref_pools[i];
while (!g_queue_is_empty (&ref_pool->ref_list)) {
ref = (GstVaapiFEIPakH264Ref *) g_queue_pop_head (&ref_pool->ref_list);
reference_pic_free (feipak, ref);
}
g_queue_clear (&ref_pool->ref_list);
}
}
GstVaapiEncoderStatus
gst_vaapi_feipak_h264_set_property (GstVaapiFEIPakH264 * feipak,
gint prop_id, const GValue * value)
{
switch (prop_id) {
case GST_VAAPI_FEIPAK_H264_PROP_MAX_BFRAMES:
feipak->num_bframes = g_value_get_uint (value);
break;
case GST_VAAPI_FEIPAK_H264_PROP_NUM_VIEWS:
feipak->num_views = g_value_get_uint (value);
break;
case GST_VAAPI_FEIPAK_H264_PROP_VIEW_IDS:{
guint i;
GValueArray *view_ids = g_value_get_boxed (value);
if (view_ids == NULL) {
for (i = 0; i < feipak->num_views; i++)
feipak->view_ids[i] = i;
} else {
g_assert (view_ids->n_values <= feipak->num_views);
for (i = 0; i < feipak->num_views; i++) {
GValue *val = g_value_array_get_nth (view_ids, i);
feipak->view_ids[i] = g_value_get_uint (val);
}
}
break;
}
default:
return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
}
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
static inline const GstVaapiMiniObjectClass *
gst_vaapi_feipak_h264_class (void)
{
static const GstVaapiMiniObjectClass GstVaapiFEIPakH264Class = {
.size = sizeof (GstVaapiFEIPakH264),
.finalize = (GDestroyNotify) gst_vaapi_feipak_h264_finalize
};
return &GstVaapiFEIPakH264Class;
}
/**
* gst_vaapi_feipak_h264_new:
* @display: a #GstVaapiDisplay
*
* Creates a new #GstVaapiEncoder for H.264 encoding. Note that the
* only supported output stream format is "byte-stream" format.
*
* Return value: the newly allocated #GstVaapiEncoder object
*/
GstVaapiFEIPakH264 *
gst_vaapi_feipak_h264_new (GstVaapiEncoder * encoder, GstVaapiDisplay * display,
VAContextID va_context)
{
GstVaapiFEIPakH264 *feipak;
feipak = (GstVaapiFEIPakH264 *)
gst_vaapi_mini_object_new0 (GST_VAAPI_MINI_OBJECT_CLASS
(gst_vaapi_feipak_h264_class ()));
if (!feipak)
return NULL;
if (!gst_vaapi_feipak_h264_init (feipak, encoder, display, va_context))
goto error;
return feipak;
error:
gst_vaapi_object_unref (feipak);
return NULL;
}
gboolean
gst_vaapi_feipak_h264_get_ref_pool (GstVaapiFEIPakH264 * feipak,
gpointer * ref_pool_ptr)
{
g_return_val_if_fail (feipak != NULL, FALSE);
if (!(&feipak->ref_pools[0]))
return FALSE;
if (ref_pool_ptr)
*ref_pool_ptr = (gpointer) (&feipak->ref_pools[0]);
return TRUE;
}