mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-14 05:12:09 +00:00
5997093ba1
gst_vaapi_encoder_h264_get_pending_reordered() does not consider the case for HIERARCHICAL_B mode. The pipeline: gst-launch-1.0 videotestsrc num-buffers=48 ! vaapih264enc prediction-type=2 \ keyframe-period=32 ! fakesink get a assert: ERROR:../gst-libs/gst/vaapi/gstvaapiencoder_h264.c:1996:reflist1_init_hierarchical_b: assertion failed: (count != 0) The last few B frames are not fetched in correct order when HIERARCHICAL_B is enabled. We also fix a latent bug for normal mode. The g_queue_pop_tail() of B frames make the last several frames encoded in reverse order. The NAL of last few frames come in reverse order in the bit stream, though it can still output the correct image. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-vaapi/-/merge_requests/405>
4165 lines
136 KiB
C
4165 lines
136 KiB
C
/*
|
|
* gstvaapiencoder_h264.c - H.264 encoder
|
|
*
|
|
* Copyright (C) 2012-2014 Intel Corporation
|
|
* Author: Wind Yuan <feng.yuan@intel.com>
|
|
* Author: Gwenole Beauchesne <gwenole.beauchesne@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
|
|
*/
|
|
|
|
#define GLIB_DISABLE_DEPRECATION_WARNINGS
|
|
|
|
#include "sysdeps.h"
|
|
#include <gst/base/gstbitwriter.h>
|
|
#include <gst/codecparsers/gsth264parser.h>
|
|
#include "gstvaapicompat.h"
|
|
#include "gstvaapiencoder_priv.h"
|
|
#include "gstvaapiencoder_h264.h"
|
|
#include "gstvaapiutils_h264.h"
|
|
#include "gstvaapiutils_h264_priv.h"
|
|
#include "gstvaapiutils_h26x_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
|
|
|
|
/* Define default temporal levels */
|
|
#define MIN_TEMPORAL_LEVELS 1
|
|
#define MAX_TEMPORAL_LEVELS 4
|
|
|
|
/* 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) | \
|
|
GST_VAAPI_RATECONTROL_MASK (ICQ) | \
|
|
GST_VAAPI_RATECONTROL_MASK (QVBR))
|
|
|
|
/* 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 | \
|
|
VA_ENC_PACKED_HEADER_MISC)
|
|
|
|
#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 enum
|
|
{
|
|
GST_VAAPI_ENCODER_H264_COMPLIANCE_MODE_STRICT = 0,
|
|
GST_VAAPI_ENCODER_H264_COMPLIANCE_MODE_RESTRICT_CODED_BUFFER_ALLOC = 1,
|
|
} GstVaapiEncoderH264ComplianceMode;
|
|
|
|
static GType
|
|
gst_vaapi_encoder_h264_compliance_mode_type (void)
|
|
{
|
|
static GType gtype = 0;
|
|
|
|
if (gtype == 0) {
|
|
static const GEnumValue values[] = {
|
|
{GST_VAAPI_ENCODER_H264_COMPLIANCE_MODE_STRICT,
|
|
"Strict compliance to the H264 Specification ",
|
|
"strict"},
|
|
/* The main intention is to reduce the CodedBuffer Size allocation.
|
|
* This will help to get better performance in some of the Intel
|
|
* platforms which has LLC restirictions */
|
|
{GST_VAAPI_ENCODER_H264_COMPLIANCE_MODE_RESTRICT_CODED_BUFFER_ALLOC,
|
|
"Restrict the allocation size of coded-buffer",
|
|
"restrict-buf-alloc"},
|
|
{0, NULL, NULL},
|
|
};
|
|
|
|
gtype =
|
|
g_enum_register_static ("GstVaapiEncoderH264ComplianceMode", values);
|
|
}
|
|
return gtype;
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT,
|
|
GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_P,
|
|
GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B
|
|
} GstVaapiEncoderH264PredictionType;
|
|
|
|
static GType
|
|
gst_vaapi_encoder_h264_prediction_type (void)
|
|
{
|
|
static GType gtype = 0;
|
|
|
|
if (gtype == 0) {
|
|
static const GEnumValue values[] = {
|
|
{GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT,
|
|
"Default encode, prev/next frame as ref ",
|
|
"default"},
|
|
{GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_P,
|
|
"Hierarchical P frame encode",
|
|
"hierarchical-p"},
|
|
{GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B,
|
|
"Hierarchical B frame encode",
|
|
"hierarchical-b"},
|
|
{0, NULL, NULL},
|
|
};
|
|
|
|
gtype =
|
|
g_enum_register_static ("GstVaapiEncoderH264PredictionType", values);
|
|
}
|
|
return gtype;
|
|
}
|
|
|
|
/* only for internal usage, values won't be equal to actual payload type */
|
|
typedef enum
|
|
{
|
|
GST_VAAPI_H264_SEI_UNKNOWN = 0,
|
|
GST_VAAPI_H264_SEI_BUF_PERIOD = (1 << 0),
|
|
GST_VAAPI_H264_SEI_PIC_TIMING = (1 << 1)
|
|
} GstVaapiH264SeiPayloadType;
|
|
|
|
typedef struct
|
|
{
|
|
GstVaapiSurfaceProxy *pic;
|
|
guint poc;
|
|
guint frame_num;
|
|
guint temporal_id;
|
|
} GstVaapiEncoderH264Ref;
|
|
|
|
typedef enum
|
|
{
|
|
GST_VAAPI_ENC_H264_REORD_NONE = 0,
|
|
GST_VAAPI_ENC_H264_REORD_DUMP_FRAMES = 1,
|
|
GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES = 2
|
|
} GstVaapiEncH264ReorderState;
|
|
|
|
typedef struct _GstVaapiH264ViewRefPool
|
|
{
|
|
GQueue ref_list;
|
|
guint max_ref_frames;
|
|
guint max_reflist0_count;
|
|
guint max_reflist1_count;
|
|
} GstVaapiH264ViewRefPool;
|
|
|
|
typedef struct _GstVaapiH264ViewReorderPool
|
|
{
|
|
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;
|
|
gboolean prev_frame_is_ref; /* previous frame is ref or not */
|
|
} GstVaapiH264ViewReorderPool;
|
|
|
|
static inline gboolean
|
|
_poc_greater_than (guint poc1, guint poc2, guint max_poc)
|
|
{
|
|
return (((poc1 - poc2) & (max_poc - 1)) < max_poc / 2);
|
|
}
|
|
|
|
/* Get slice_type value for H.264 specification */
|
|
static guint8
|
|
h264_get_slice_type (GstVaapiPictureType type)
|
|
{
|
|
switch (type) {
|
|
case GST_VAAPI_PICTURE_TYPE_I:
|
|
return GST_H264_I_SLICE;
|
|
case GST_VAAPI_PICTURE_TYPE_P:
|
|
return GST_H264_P_SLICE;
|
|
case GST_VAAPI_PICTURE_TYPE_B:
|
|
return GST_H264_B_SLICE;
|
|
default:
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Get log2_max_frame_num value for H.264 specification */
|
|
static guint
|
|
h264_get_log2_max_frame_num (guint num)
|
|
{
|
|
guint ret = 0;
|
|
|
|
while (num) {
|
|
++ret;
|
|
num >>= 1;
|
|
}
|
|
if (ret <= 4)
|
|
ret = 4;
|
|
else if (ret > 10)
|
|
ret = 10;
|
|
/* must be greater than 4 */
|
|
return ret;
|
|
}
|
|
|
|
/* Determines the cpbBrNalFactor based on the supplied profile */
|
|
static guint
|
|
h264_get_cpb_nal_factor (GstVaapiProfile profile)
|
|
{
|
|
guint f;
|
|
|
|
/* Table A-2 */
|
|
switch (profile) {
|
|
case GST_VAAPI_PROFILE_H264_HIGH:
|
|
f = 1500;
|
|
break;
|
|
case GST_VAAPI_PROFILE_H264_HIGH10:
|
|
f = 3600;
|
|
break;
|
|
case GST_VAAPI_PROFILE_H264_HIGH_422:
|
|
case GST_VAAPI_PROFILE_H264_HIGH_444:
|
|
f = 4800;
|
|
break;
|
|
case GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH:
|
|
case GST_VAAPI_PROFILE_H264_STEREO_HIGH:
|
|
f = 1500; /* H.10.2.1 (r) */
|
|
break;
|
|
default:
|
|
f = 1200;
|
|
break;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
/* 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,
|
|
GstVaapiRateControl rate_control, 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 cbr_flag = rate_control == GST_VAAPI_RATECONTROL_CBR ? 1 : 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, cbr_flag, 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 (GstBitWriter * bs,
|
|
const VAEncSequenceParameterBufferH264 * seq_param, GstVaapiProfile profile,
|
|
GstVaapiRateControl rate_control, const VAEncMiscParameterHRD * hrd_params)
|
|
{
|
|
if (!bs_write_sps_data (bs, seq_param, profile, rate_control, hrd_params))
|
|
return FALSE;
|
|
|
|
/* rbsp_trailing_bits */
|
|
bs_write_trailing_bits (bs);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
bs_write_subset_sps (GstBitWriter * bs,
|
|
const VAEncSequenceParameterBufferH264 * seq_param, GstVaapiProfile profile,
|
|
GstVaapiRateControl rate_control, guint num_views, guint16 * view_ids,
|
|
const VAEncMiscParameterHRD * hrd_params)
|
|
{
|
|
guint32 i, j, k;
|
|
|
|
if (!bs_write_sps_data (bs, seq_param, profile, rate_control, 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
|
|
|| profile == GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH
|
|
|| profile == GST_VAAPI_PROFILE_H264_STEREO_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;
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* --- H.264 Encoder --- */
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
struct _GstVaapiEncoderH264
|
|
{
|
|
GstVaapiEncoder parent_instance;
|
|
|
|
GstVaapiProfile profile;
|
|
GstVaapiLevelH264 level;
|
|
GstVaapiEntrypoint entrypoint;
|
|
guint8 profile_idc;
|
|
guint8 max_profile_idc;
|
|
guint8 hw_max_profile_idc;
|
|
guint8 level_idc;
|
|
guint32 idr_period;
|
|
guint32 ip_period;
|
|
guint32 init_qp;
|
|
guint32 min_qp;
|
|
guint32 max_qp;
|
|
guint32 qp_i;
|
|
guint32 qp_ip;
|
|
guint32 qp_ib;
|
|
guint32 num_slices;
|
|
guint32 num_bframes;
|
|
guint32 mb_width;
|
|
guint32 mb_height;
|
|
guint32 quality_factor;
|
|
gboolean use_cabac;
|
|
gboolean use_dct8x8;
|
|
guint temporal_levels; /* Number of temporal levels */
|
|
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;
|
|
|
|
/* frame, poc */
|
|
guint32 max_frame_num;
|
|
guint32 log2_max_frame_num;
|
|
guint32 max_pic_order_cnt;
|
|
guint32 log2_max_pic_order_cnt;
|
|
guint32 idr_num;
|
|
guint8 pic_order_cnt_type;
|
|
guint8 delta_pic_order_always_zero_flag;
|
|
guint num_ref_frames;
|
|
|
|
GstBuffer *sps_data;
|
|
GstBuffer *subset_sps_data;
|
|
GstBuffer *pps_data;
|
|
|
|
guint bitrate_bits; // bitrate (bits)
|
|
guint cpb_length; // length of CPB buffer (ms)
|
|
guint cpb_length_bits; // length of CPB buffer (bits)
|
|
GstVaapiEncoderMbbrc mbbrc; // macroblock bitrate control
|
|
|
|
/* MVC */
|
|
gboolean is_mvc;
|
|
guint32 view_idx; /* View Order Index (VOIdx) */
|
|
guint32 num_views;
|
|
guint16 view_ids[MAX_NUM_VIEWS];
|
|
GstVaapiH264ViewRefPool ref_pools[MAX_NUM_VIEWS];
|
|
GstVaapiH264ViewReorderPool reorder_pools[MAX_NUM_VIEWS];
|
|
|
|
gboolean use_aud;
|
|
|
|
/* Complance mode */
|
|
GstVaapiEncoderH264ComplianceMode compliance_mode;
|
|
guint min_cr; // Minimum Compression Ratio (A.3.1)
|
|
};
|
|
|
|
/* Write a SEI buffering period payload */
|
|
static gboolean
|
|
bs_write_sei_buf_period (GstBitWriter * bs,
|
|
GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
|
|
{
|
|
guint initial_cpb_removal_delay = 0;
|
|
guint initial_cpb_removal_delay_offset = 0;
|
|
guint8 initial_cpb_removal_delay_length = 24;
|
|
|
|
/* sequence_parameter_set_id */
|
|
WRITE_UE (bs, encoder->view_idx);
|
|
/* NalHrdBpPresentFlag == TRUE */
|
|
/* cpb_cnt_minus1 == 0 */
|
|
|
|
/* decoding should start when the CPB fullness reaches half of cpb size
|
|
* initial_cpb_remvoal_delay = (((cpb_length / 2) * 90000) / 1000) */
|
|
initial_cpb_removal_delay = encoder->cpb_length * 45;
|
|
|
|
/* initial_cpb_remvoal_dealy */
|
|
WRITE_UINT32 (bs, initial_cpb_removal_delay,
|
|
initial_cpb_removal_delay_length);
|
|
|
|
/* initial_cpb_removal_delay_offset */
|
|
WRITE_UINT32 (bs, initial_cpb_removal_delay_offset,
|
|
initial_cpb_removal_delay_length);
|
|
|
|
/* VclHrdBpPresentFlag == FALSE */
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
bs_error:
|
|
{
|
|
GST_WARNING ("failed to write Buffering Period SEI message");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Write a SEI picture timing payload */
|
|
static gboolean
|
|
bs_write_sei_pic_timing (GstBitWriter * bs,
|
|
GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
|
|
{
|
|
GstVaapiH264ViewReorderPool *reorder_pool = NULL;
|
|
guint cpb_removal_delay;
|
|
guint dpb_output_delay;
|
|
guint8 cpb_removal_delay_length = 24;
|
|
guint8 dpb_output_delay_length = 24;
|
|
guint pic_struct = 0;
|
|
guint clock_timestamp_flag = 0;
|
|
|
|
reorder_pool = &encoder->reorder_pools[encoder->view_idx];
|
|
if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
|
|
reorder_pool->frame_count = 0;
|
|
else
|
|
reorder_pool->frame_count++;
|
|
|
|
/* clock-tick = no_units_in_tick/time_scale (C-1)
|
|
* time_scale = FPS_N * 2 (E.2.1)
|
|
* num_units_in_tick = FPS_D (E.2.1)
|
|
* frame_duration = clock-tick * 2
|
|
* so removal time for one frame is 2 clock-ticks.
|
|
* but adding a tolerance of one frame duration,
|
|
* which is 2 more clock-ticks */
|
|
cpb_removal_delay = (reorder_pool->frame_count * 2 + 2);
|
|
|
|
if (picture->type == GST_VAAPI_PICTURE_TYPE_B)
|
|
dpb_output_delay = 0;
|
|
else
|
|
dpb_output_delay = picture->poc - reorder_pool->frame_count * 2;
|
|
|
|
/* CpbDpbDelaysPresentFlag == 1 */
|
|
WRITE_UINT32 (bs, cpb_removal_delay, cpb_removal_delay_length);
|
|
WRITE_UINT32 (bs, dpb_output_delay, dpb_output_delay_length);
|
|
|
|
/* pic_struct_present_flag == 1 */
|
|
/* pic_struct */
|
|
WRITE_UINT32 (bs, pic_struct, 4);
|
|
/* clock_timestamp_flag */
|
|
WRITE_UINT32 (bs, clock_timestamp_flag, 1);
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
bs_error:
|
|
{
|
|
GST_WARNING ("failed to write Picture Timing SEI message");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Write a Slice NAL unit */
|
|
static gboolean
|
|
bs_write_slice (GstBitWriter * bs,
|
|
const VAEncSliceParameterBufferH264 * slice_param,
|
|
GstVaapiEncoderH264 * encoder, 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, encoder->log2_max_frame_num);
|
|
|
|
/* 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 (!encoder->pic_order_cnt_type) {
|
|
WRITE_UINT32 (bs, slice_param->pic_order_cnt_lsb,
|
|
encoder->log2_max_pic_order_cnt);
|
|
/* 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 (encoder->pic_order_cnt_type == 1 &&
|
|
!encoder->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 == 1)
|
|
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);
|
|
}
|
|
}
|
|
|
|
if ((slice_param->slice_type != 2) && (slice_param->slice_type != 4)) {
|
|
if ((encoder->prediction_type != GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT)
|
|
&& (encoder->abs_diff_pic_num_list0 > 1))
|
|
ref_pic_list_modification_flag_l0 = 1;
|
|
|
|
WRITE_UINT32 (bs, ref_pic_list_modification_flag_l0, 1);
|
|
|
|
if (ref_pic_list_modification_flag_l0) {
|
|
/*modification_of_pic_num_idc */
|
|
WRITE_UE (bs, 0);
|
|
/* abs_diff_pic_num_minus1 */
|
|
WRITE_UE (bs, encoder->abs_diff_pic_num_list0 - 1);
|
|
/*modification_of_pic_num_idc */
|
|
WRITE_UE (bs, 3);
|
|
}
|
|
}
|
|
|
|
/* 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 &&
|
|
(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 (GST_VAAPI_ENC_PICTURE_IS_REFRENCE (picture)) {
|
|
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 (GstVaapiEncoderH264 * encoder,
|
|
const guint8 * nal, guint32 size)
|
|
{
|
|
guint8 nal_type;
|
|
G_GNUC_UNUSED gsize ret; /* FIXME */
|
|
gboolean has_subset_sps;
|
|
|
|
g_assert (size);
|
|
|
|
has_subset_sps = !encoder->is_mvc || (encoder->subset_sps_data != NULL);
|
|
if (encoder->sps_data && encoder->pps_data && has_subset_sps)
|
|
return;
|
|
|
|
nal_type = nal[0] & 0x1F;
|
|
switch (nal_type) {
|
|
case GST_H264_NAL_SPS:
|
|
encoder->sps_data = gst_buffer_new_allocate (NULL, size, NULL);
|
|
ret = gst_buffer_fill (encoder->sps_data, 0, nal, size);
|
|
g_assert (ret == size);
|
|
break;
|
|
case GST_H264_NAL_SUBSET_SPS:
|
|
encoder->subset_sps_data = gst_buffer_new_allocate (NULL, size, NULL);
|
|
ret = gst_buffer_fill (encoder->subset_sps_data, 0, nal, size);
|
|
g_assert (ret == size);
|
|
break;
|
|
case GST_H264_NAL_PPS:
|
|
encoder->pps_data = gst_buffer_new_allocate (NULL, size, NULL);
|
|
ret = gst_buffer_fill (encoder->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 (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
GstVaapiDisplay *const display = GST_VAAPI_ENCODER_DISPLAY (encoder);
|
|
GArray *profiles;
|
|
guint i, profile_idc, max_profile_idc;
|
|
|
|
if (encoder->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);
|
|
|
|
encoder->hw_max_profile_idc = max_profile_idc;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Derives the profile supported by the underlying hardware */
|
|
static gboolean
|
|
ensure_hw_profile (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
GstVaapiDisplay *const display = GST_VAAPI_ENCODER_DISPLAY (encoder);
|
|
GstVaapiEntrypoint entrypoint = encoder->entrypoint;
|
|
GstVaapiProfile profile, profiles[4];
|
|
guint i, num_profiles = 0;
|
|
|
|
profiles[num_profiles++] = encoder->profile;
|
|
switch (encoder->profile) {
|
|
case GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE:
|
|
profiles[num_profiles++] = GST_VAAPI_PROFILE_H264_BASELINE;
|
|
profiles[num_profiles++] = GST_VAAPI_PROFILE_H264_MAIN;
|
|
// fall-through
|
|
case GST_VAAPI_PROFILE_H264_MAIN:
|
|
profiles[num_profiles++] = GST_VAAPI_PROFILE_H264_HIGH;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
profile = GST_VAAPI_PROFILE_UNKNOWN;
|
|
for (i = 0; i < num_profiles; i++) {
|
|
if (gst_vaapi_display_has_encoder (display, profiles[i], entrypoint)) {
|
|
profile = profiles[i];
|
|
break;
|
|
}
|
|
}
|
|
if (profile == GST_VAAPI_PROFILE_UNKNOWN)
|
|
goto error_unsupported_profile;
|
|
|
|
GST_VAAPI_ENCODER_CAST (encoder)->profile = profile;
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
error_unsupported_profile:
|
|
{
|
|
GST_ERROR ("unsupported HW profile %s",
|
|
gst_vaapi_profile_get_va_name (encoder->profile));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Check target decoder constraints */
|
|
static gboolean
|
|
ensure_profile_limits (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
GstVaapiProfile profile;
|
|
|
|
if (!encoder->max_profile_idc
|
|
|| encoder->profile_idc == encoder->max_profile_idc)
|
|
return TRUE;
|
|
|
|
/* Give an error if the given parameters are invalid for requested
|
|
* profile rather than lowering profile.
|
|
*/
|
|
if (encoder->profile_idc > encoder->max_profile_idc) {
|
|
GST_WARNING ("Invalid parameter for maximum profile");
|
|
return FALSE;
|
|
}
|
|
|
|
profile = GST_VAAPI_PROFILE_UNKNOWN;
|
|
|
|
if (encoder->profile_idc < encoder->max_profile_idc) {
|
|
/* Let profile be higher to fit in the maximum profile
|
|
* without changing parameters */
|
|
if (encoder->max_profile_idc > GST_H264_PROFILE_BASELINE)
|
|
profile = GST_VAAPI_PROFILE_H264_MAIN;
|
|
|
|
if (encoder->max_profile_idc > GST_H264_PROFILE_MAIN)
|
|
profile = GST_VAAPI_PROFILE_H264_HIGH;
|
|
|
|
if (encoder->max_profile_idc > GST_H264_PROFILE_HIGH) {
|
|
if (encoder->num_views > 2)
|
|
profile = GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH;
|
|
else if (encoder->num_views == 2)
|
|
profile = GST_VAAPI_PROFILE_H264_STEREO_HIGH;
|
|
}
|
|
}
|
|
|
|
if (profile) {
|
|
encoder->profile = profile;
|
|
encoder->profile_idc = encoder->max_profile_idc;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* Derives the minimum profile from the active coding tools */
|
|
static gboolean
|
|
ensure_profile (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
GstVaapiProfile profile;
|
|
|
|
/* Always start from "constrained-baseline" profile for maximum
|
|
compatibility */
|
|
profile = GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE;
|
|
|
|
/* Main profile coding tools */
|
|
if (encoder->num_bframes > 0 || encoder->use_cabac)
|
|
profile = GST_VAAPI_PROFILE_H264_MAIN;
|
|
|
|
/* High profile coding tools */
|
|
if (encoder->use_dct8x8)
|
|
profile = GST_VAAPI_PROFILE_H264_HIGH;
|
|
|
|
/* MVC profiles coding tools */
|
|
if (encoder->num_views == 2)
|
|
profile = GST_VAAPI_PROFILE_H264_STEREO_HIGH;
|
|
else if (encoder->num_views > 2)
|
|
profile = GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH;
|
|
|
|
encoder->profile = profile;
|
|
encoder->profile_idc = gst_vaapi_utils_h264_get_profile_idc (profile);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Derives the level from the currently set limits */
|
|
static gboolean
|
|
ensure_level (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
const guint cpb_factor = h264_get_cpb_nal_factor (encoder->profile);
|
|
const GstVaapiH264LevelLimits *limits_table;
|
|
guint i, num_limits, PicSizeMbs, MaxDpbMbs, MaxMBPS;
|
|
|
|
PicSizeMbs = encoder->mb_width * encoder->mb_height;
|
|
MaxDpbMbs = PicSizeMbs * ((encoder->num_bframes) ? 2 : 1);
|
|
MaxMBPS = gst_util_uint64_scale_int_ceil (PicSizeMbs,
|
|
GST_VAAPI_ENCODER_FPS_N (encoder), GST_VAAPI_ENCODER_FPS_D (encoder));
|
|
|
|
limits_table = gst_vaapi_utils_h264_get_level_limits_table (&num_limits);
|
|
for (i = 0; i < num_limits; i++) {
|
|
const GstVaapiH264LevelLimits *const limits = &limits_table[i];
|
|
if (PicSizeMbs <= limits->MaxFS &&
|
|
MaxDpbMbs <= limits->MaxDpbMbs &&
|
|
MaxMBPS <= limits->MaxMBPS && (!encoder->bitrate_bits
|
|
|| encoder->bitrate_bits <= (limits->MaxBR * cpb_factor)) &&
|
|
(!encoder->cpb_length_bits ||
|
|
encoder->cpb_length_bits <= (limits->MaxCPB * cpb_factor)))
|
|
break;
|
|
}
|
|
if (i == num_limits)
|
|
goto error_unsupported_level;
|
|
|
|
encoder->level = limits_table[i].level;
|
|
encoder->level_idc = limits_table[i].level_idc;
|
|
encoder->min_cr = limits_table[i].MinCR;
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
error_unsupported_level:
|
|
{
|
|
GST_ERROR ("failed to find a suitable level matching codec config");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Enable "high-compression" tuning options */
|
|
static gboolean
|
|
ensure_tuning_high_compression (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
guint8 profile_idc;
|
|
|
|
if (!ensure_hw_profile_limits (encoder))
|
|
return FALSE;
|
|
|
|
profile_idc = encoder->hw_max_profile_idc;
|
|
if (encoder->max_profile_idc && encoder->max_profile_idc < profile_idc)
|
|
profile_idc = encoder->max_profile_idc;
|
|
|
|
/* Tuning options to enable Main profile */
|
|
if (profile_idc >= GST_H264_PROFILE_MAIN
|
|
&& profile_idc != GST_H264_PROFILE_EXTENDED) {
|
|
encoder->use_cabac = TRUE;
|
|
if (!encoder->num_bframes)
|
|
encoder->num_bframes = 1;
|
|
}
|
|
|
|
/* Tuning options to enable High profile */
|
|
if (profile_idc >= GST_H264_PROFILE_HIGH) {
|
|
encoder->use_dct8x8 = TRUE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* Ensure tuning options */
|
|
static gboolean
|
|
ensure_tuning (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
gboolean success;
|
|
|
|
switch (GST_VAAPI_ENCODER_TUNE (encoder)) {
|
|
case GST_VAAPI_ENCODER_TUNE_HIGH_COMPRESSION:
|
|
success = ensure_tuning_high_compression (encoder);
|
|
break;
|
|
default:
|
|
success = TRUE;
|
|
break;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
static gboolean
|
|
is_temporal_id_max (GstVaapiEncoderH264 * encoder, guint32 temporal_id)
|
|
{
|
|
g_assert (temporal_id < encoder->temporal_levels);
|
|
return temporal_id == encoder->temporal_levels - 1;
|
|
}
|
|
|
|
/* Handle new GOP starts */
|
|
static void
|
|
reset_gop_start (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
GstVaapiH264ViewReorderPool *const reorder_pool =
|
|
&encoder->reorder_pools[encoder->view_idx];
|
|
|
|
reorder_pool->frame_index = 1;
|
|
reorder_pool->cur_present_index = 0;
|
|
++encoder->idr_num;
|
|
}
|
|
|
|
/* Marks the supplied picture as a B-frame */
|
|
static void
|
|
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 */
|
|
static void
|
|
set_p_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
|
|
{
|
|
g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
|
|
pic->type = GST_VAAPI_PICTURE_TYPE_P;
|
|
|
|
if (encoder->temporal_levels == 1) {
|
|
/* Default prediction mode */
|
|
GST_VAAPI_PICTURE_FLAG_SET (pic, GST_VAAPI_ENC_PICTURE_FLAG_REFERENCE);
|
|
} else {
|
|
/* temporal_encode: all frames in highest level are not reference frames
|
|
* for hierarhical-p and hierarchical-b prediction mode */
|
|
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 an I-frame */
|
|
static void
|
|
set_i_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
|
|
{
|
|
g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
|
|
pic->type = GST_VAAPI_PICTURE_TYPE_I;
|
|
GST_VAAPI_PICTURE_FLAG_SET (pic, GST_VAAPI_ENC_PICTURE_FLAG_REFERENCE);
|
|
|
|
g_assert (pic->frame);
|
|
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (pic->frame);
|
|
}
|
|
|
|
/* Marks the supplied picture as an IDR frame */
|
|
static void
|
|
set_idr_frame (GstVaapiEncPicture * pic, GstVaapiEncoderH264 * encoder)
|
|
{
|
|
g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
|
|
pic->type = GST_VAAPI_PICTURE_TYPE_I;
|
|
pic->poc = 0;
|
|
GST_VAAPI_ENC_PICTURE_FLAG_SET (pic,
|
|
GST_VAAPI_ENC_PICTURE_FLAG_IDR | GST_VAAPI_ENC_PICTURE_FLAG_REFERENCE);
|
|
|
|
g_assert (pic->frame);
|
|
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (pic->frame);
|
|
}
|
|
|
|
/* Marks the supplied picture a a key-frame */
|
|
static void
|
|
set_key_frame (GstVaapiEncPicture * picture,
|
|
GstVaapiEncoderH264 * encoder, gboolean is_idr)
|
|
{
|
|
if (is_idr) {
|
|
reset_gop_start (encoder);
|
|
set_idr_frame (picture, encoder);
|
|
} else
|
|
set_i_frame (picture, encoder);
|
|
}
|
|
|
|
static void
|
|
set_frame_num (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
|
|
{
|
|
GstVaapiH264ViewReorderPool *reorder_pool = NULL;
|
|
|
|
reorder_pool = &encoder->reorder_pools[encoder->view_idx];
|
|
|
|
picture->frame_num = (reorder_pool->cur_frame_num % encoder->max_frame_num);
|
|
|
|
if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture)) {
|
|
picture->frame_num = 0;
|
|
reorder_pool->cur_frame_num = 0;
|
|
}
|
|
|
|
reorder_pool->prev_frame_is_ref = GST_VAAPI_ENC_PICTURE_IS_REFRENCE (picture);
|
|
|
|
if (reorder_pool->prev_frame_is_ref)
|
|
++reorder_pool->cur_frame_num;
|
|
}
|
|
|
|
/* Fills in VA HRD parameters */
|
|
static void
|
|
fill_hrd_params (GstVaapiEncoderH264 * encoder, VAEncMiscParameterHRD * hrd)
|
|
{
|
|
if (encoder->bitrate_bits > 0) {
|
|
hrd->buffer_size = encoder->cpb_length_bits;
|
|
hrd->initial_buffer_fullness = hrd->buffer_size / 2;
|
|
} else {
|
|
hrd->buffer_size = 0;
|
|
hrd->initial_buffer_fullness = 0;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
add_packed_au_delimiter (GstVaapiEncoderH264 * encoder,
|
|
GstVaapiEncPicture * picture)
|
|
{
|
|
GstVaapiEncPackedHeader *packed_aud;
|
|
GstBitWriter bs;
|
|
VAEncPackedHeaderParameterBuffer packed_header_param_buffer = { 0 };
|
|
guint32 data_bit_size;
|
|
guint8 *data;
|
|
|
|
gst_bit_writer_init_with_size (&bs, 128, FALSE);
|
|
WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
|
|
bs_write_nal_header (&bs, GST_H264_NAL_REF_IDC_NONE,
|
|
GST_H264_NAL_AU_DELIMITER);
|
|
WRITE_UINT32 (&bs, picture->type - 1, 3);
|
|
if (!bs_write_trailing_bits (&bs))
|
|
goto bs_error;
|
|
|
|
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 = VAEncPackedHeaderRawData;
|
|
packed_header_param_buffer.bit_length = data_bit_size;
|
|
packed_header_param_buffer.has_emulation_bytes = 0;
|
|
|
|
packed_aud = gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (encoder),
|
|
&packed_header_param_buffer, sizeof (packed_header_param_buffer),
|
|
data, (data_bit_size + 7) / 8);
|
|
g_assert (packed_aud);
|
|
|
|
gst_vaapi_enc_picture_add_packed_header (picture, packed_aud);
|
|
gst_vaapi_codec_object_replace (&packed_aud, NULL);
|
|
|
|
gst_bit_writer_reset (&bs);
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
bs_error:
|
|
{
|
|
GST_WARNING ("failed to write AU Delimiter NAL unit");
|
|
gst_bit_writer_reset (&bs);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Adds the supplied sequence header (SPS) to the list of packed
|
|
headers to pass down as-is to the encoder */
|
|
static gboolean
|
|
add_packed_sequence_header (GstVaapiEncoderH264 * encoder,
|
|
GstVaapiEncPicture * picture, GstVaapiEncSequence * sequence)
|
|
{
|
|
GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
|
|
GstVaapiEncPackedHeader *packed_seq;
|
|
GstBitWriter bs;
|
|
VAEncPackedHeaderParameterBuffer packed_seq_param = { 0 };
|
|
const VAEncSequenceParameterBufferH264 *const seq_param = sequence->param;
|
|
GstVaapiProfile profile = encoder->profile;
|
|
|
|
VAEncMiscParameterHRD hrd_params;
|
|
guint32 data_bit_size;
|
|
guint8 *data;
|
|
|
|
fill_hrd_params (encoder, &hrd_params);
|
|
|
|
gst_bit_writer_init_with_size (&bs, 128, FALSE);
|
|
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 (&bs, seq_param, profile, base_encoder->rate_control,
|
|
&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 (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 (encoder, data + 4, data_bit_size / 8 - 4);
|
|
gst_bit_writer_reset (&bs);
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
bs_error:
|
|
{
|
|
GST_WARNING ("failed to write SPS NAL unit");
|
|
gst_bit_writer_reset (&bs);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
add_packed_sequence_header_mvc (GstVaapiEncoderH264 * encoder,
|
|
GstVaapiEncPicture * picture, GstVaapiEncSequence * sequence)
|
|
{
|
|
GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
|
|
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 (encoder, &hrd_params);
|
|
|
|
/* non-base layer, pack one subset sps */
|
|
gst_bit_writer_init_with_size (&bs, 128, FALSE);
|
|
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 (&bs, seq_param, encoder->profile,
|
|
base_encoder->rate_control, encoder->num_views, encoder->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 (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 (encoder, data + 4, data_bit_size / 8 - 4);
|
|
gst_bit_writer_reset (&bs);
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
bs_error:
|
|
{
|
|
GST_WARNING ("failed to write SPS NAL unit");
|
|
gst_bit_writer_reset (&bs);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Adds the supplied picture header (PPS) to the list of packed
|
|
headers to pass down as-is to the encoder */
|
|
static gboolean
|
|
add_packed_picture_header (GstVaapiEncoderH264 * encoder,
|
|
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_with_size (&bs, 128, FALSE);
|
|
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, encoder->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 (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 (encoder, data + 4, data_bit_size / 8 - 4);
|
|
gst_bit_writer_reset (&bs);
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
bs_error:
|
|
{
|
|
GST_WARNING ("failed to write PPS NAL unit");
|
|
gst_bit_writer_reset (&bs);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
add_packed_sei_header (GstVaapiEncoderH264 * encoder,
|
|
GstVaapiEncPicture * picture, GstVaapiH264SeiPayloadType payloadtype)
|
|
{
|
|
GstVaapiEncPackedHeader *packed_sei;
|
|
GstBitWriter bs, bs_buf_period, bs_pic_timing;
|
|
VAEncPackedHeaderParameterBuffer packed_sei_param = { 0 };
|
|
guint32 data_bit_size;
|
|
guint8 buf_period_payload_size = 0, pic_timing_payload_size = 0;
|
|
guint8 *data, *buf_period_payload = NULL, *pic_timing_payload = NULL;
|
|
gboolean need_buf_period, need_pic_timing;
|
|
|
|
gst_bit_writer_init_with_size (&bs_buf_period, 128, FALSE);
|
|
gst_bit_writer_init_with_size (&bs_pic_timing, 128, FALSE);
|
|
gst_bit_writer_init_with_size (&bs, 128, FALSE);
|
|
|
|
need_buf_period = GST_VAAPI_H264_SEI_BUF_PERIOD & payloadtype;
|
|
need_pic_timing = GST_VAAPI_H264_SEI_PIC_TIMING & payloadtype;
|
|
|
|
if (need_buf_period) {
|
|
/* Write a Buffering Period SEI message */
|
|
bs_write_sei_buf_period (&bs_buf_period, encoder, picture);
|
|
/* Write byte alignment bits */
|
|
if (GST_BIT_WRITER_BIT_SIZE (&bs_buf_period) % 8 != 0)
|
|
bs_write_trailing_bits (&bs_buf_period);
|
|
buf_period_payload_size = (GST_BIT_WRITER_BIT_SIZE (&bs_buf_period)) / 8;
|
|
buf_period_payload = GST_BIT_WRITER_DATA (&bs_buf_period);
|
|
}
|
|
|
|
if (need_pic_timing) {
|
|
/* Write a Picture Timing SEI message */
|
|
if (GST_VAAPI_H264_SEI_PIC_TIMING & payloadtype)
|
|
bs_write_sei_pic_timing (&bs_pic_timing, encoder, picture);
|
|
/* Write byte alignment bits */
|
|
if (GST_BIT_WRITER_BIT_SIZE (&bs_pic_timing) % 8 != 0)
|
|
bs_write_trailing_bits (&bs_pic_timing);
|
|
pic_timing_payload_size = (GST_BIT_WRITER_BIT_SIZE (&bs_pic_timing)) / 8;
|
|
pic_timing_payload = GST_BIT_WRITER_DATA (&bs_pic_timing);
|
|
}
|
|
|
|
/* Write the SEI message */
|
|
WRITE_UINT32 (&bs, 0x00000001, 32); /* start code */
|
|
bs_write_nal_header (&bs, GST_H264_NAL_REF_IDC_NONE, GST_H264_NAL_SEI);
|
|
|
|
if (need_buf_period) {
|
|
WRITE_UINT32 (&bs, GST_H264_SEI_BUF_PERIOD, 8);
|
|
WRITE_UINT32 (&bs, buf_period_payload_size, 8);
|
|
/* Add buffering period sei message */
|
|
gst_bit_writer_put_bytes (&bs, buf_period_payload, buf_period_payload_size);
|
|
}
|
|
|
|
if (need_pic_timing) {
|
|
WRITE_UINT32 (&bs, GST_H264_SEI_PIC_TIMING, 8);
|
|
WRITE_UINT32 (&bs, pic_timing_payload_size, 8);
|
|
/* Add picture timing sei message */
|
|
gst_bit_writer_put_bytes (&bs, pic_timing_payload, pic_timing_payload_size);
|
|
}
|
|
|
|
/* rbsp_trailing_bits */
|
|
bs_write_trailing_bits (&bs);
|
|
|
|
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_sei_param.type = VA_ENC_PACKED_HEADER_H264_SEI;
|
|
packed_sei_param.bit_length = data_bit_size;
|
|
packed_sei_param.has_emulation_bytes = 0;
|
|
|
|
packed_sei = gst_vaapi_enc_packed_header_new (GST_VAAPI_ENCODER (encoder),
|
|
&packed_sei_param, sizeof (packed_sei_param),
|
|
data, (data_bit_size + 7) / 8);
|
|
g_assert (packed_sei);
|
|
|
|
gst_vaapi_enc_picture_add_packed_header (picture, packed_sei);
|
|
gst_vaapi_codec_object_replace (&packed_sei, NULL);
|
|
|
|
gst_bit_writer_reset (&bs_buf_period);
|
|
gst_bit_writer_reset (&bs_pic_timing);
|
|
gst_bit_writer_reset (&bs);
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
bs_error:
|
|
{
|
|
GST_WARNING ("failed to write SEI NAL unit");
|
|
gst_bit_writer_reset (&bs_buf_period);
|
|
gst_bit_writer_reset (&bs_pic_timing);
|
|
gst_bit_writer_reset (&bs);
|
|
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:
|
|
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_MEDIUM;
|
|
|
|
*nal_unit_type = GST_H264_NAL_SLICE;
|
|
break;
|
|
case GST_VAAPI_PICTURE_TYPE_B:
|
|
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:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* Adds the supplied prefix nal header to the list of packed
|
|
headers to pass down as-is to the encoder */
|
|
static gboolean
|
|
add_packed_prefix_nal_header (GstVaapiEncoderH264 * encoder,
|
|
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_with_size (&bs, 128, FALSE);
|
|
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, encoder->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 (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_reset (&bs);
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
bs_error:
|
|
{
|
|
GST_WARNING ("failed to write Prefix NAL unit header");
|
|
gst_bit_writer_reset (&bs);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Adds the supplied slice header to the list of packed
|
|
headers to pass down as-is to the encoder */
|
|
static gboolean
|
|
add_packed_slice_header (GstVaapiEncoderH264 * encoder,
|
|
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_with_size (&bs, 128, FALSE);
|
|
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 (encoder->is_mvc && encoder->view_idx) {
|
|
bs_write_nal_header (&bs, nal_ref_idc, GST_H264_NAL_SLICE_EXT);
|
|
bs_write_nal_header_mvc_extension (&bs, picture,
|
|
encoder->view_ids[encoder->view_idx]);
|
|
} else
|
|
bs_write_nal_header (&bs, nal_ref_idc, nal_unit_type);
|
|
|
|
bs_write_slice (&bs, slice_param, encoder, 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 (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_reset (&bs);
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
bs_error:
|
|
{
|
|
GST_WARNING ("failed to write Slice NAL unit header");
|
|
gst_bit_writer_reset (&bs);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Reference picture management */
|
|
static void
|
|
reference_pic_free (GstVaapiEncoderH264 * encoder, GstVaapiEncoderH264Ref * ref)
|
|
{
|
|
if (!ref)
|
|
return;
|
|
if (ref->pic)
|
|
gst_vaapi_encoder_release_surface (GST_VAAPI_ENCODER (encoder), ref->pic);
|
|
g_slice_free (GstVaapiEncoderH264Ref, ref);
|
|
}
|
|
|
|
static inline GstVaapiEncoderH264Ref *
|
|
reference_pic_create (GstVaapiEncoderH264 * encoder,
|
|
GstVaapiEncPicture * picture, GstVaapiSurfaceProxy * surface)
|
|
{
|
|
GstVaapiEncoderH264Ref *const ref = g_slice_new0 (GstVaapiEncoderH264Ref);
|
|
|
|
ref->pic = surface;
|
|
ref->frame_num = picture->frame_num;
|
|
ref->poc = picture->poc;
|
|
ref->temporal_id = picture->temporal_id;
|
|
return ref;
|
|
}
|
|
|
|
static gboolean
|
|
reference_list_update (GstVaapiEncoderH264 * encoder,
|
|
GstVaapiEncPicture * picture, GstVaapiSurfaceProxy * surface)
|
|
{
|
|
GstVaapiEncoderH264Ref *ref;
|
|
GstVaapiH264ViewRefPool *const ref_pool =
|
|
&encoder->ref_pools[encoder->view_idx];
|
|
|
|
if (encoder->prediction_type == GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT
|
|
&& GST_VAAPI_PICTURE_TYPE_B == picture->type) {
|
|
gst_vaapi_encoder_release_surface (GST_VAAPI_ENCODER (encoder), surface);
|
|
return TRUE;
|
|
}
|
|
if (GST_VAAPI_ENC_PICTURE_IS_IDR (picture)) {
|
|
while (!g_queue_is_empty (&ref_pool->ref_list))
|
|
reference_pic_free (encoder, 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 (encoder, g_queue_pop_head (&ref_pool->ref_list));
|
|
}
|
|
ref = reference_pic_create (encoder, 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;
|
|
}
|
|
|
|
/* update reflist0 for hierarchical-p and hierarchical-b encode */
|
|
static void
|
|
reflist0_init_hierarchical (GstVaapiEncoderH264 * encoder,
|
|
GstVaapiEncPicture * picture, GQueue * ref_list,
|
|
GstVaapiEncoderH264Ref ** reflist_0, guint * reflist_0_count)
|
|
{
|
|
GstVaapiEncoderH264Ref *tmp = NULL;
|
|
GList *iter;
|
|
guint count = 0, i;
|
|
|
|
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 (picture->poc, tmp->poc, encoder->max_pic_order_cnt)
|
|
&& ((picture->temporal_id && (tmp->temporal_id < picture->temporal_id))
|
|
|| (!picture->temporal_id
|
|
&& (tmp->temporal_id == picture->temporal_id)))) {
|
|
reflist_0[count++] = tmp;
|
|
}
|
|
}
|
|
|
|
g_assert (count != 0);
|
|
|
|
/* Only need one ref frame */
|
|
tmp = reflist_0[0];
|
|
for (i = 1; i < count; i++) {
|
|
if (tmp->poc < reflist_0[i]->poc)
|
|
tmp = reflist_0[i];
|
|
}
|
|
reflist_0[0] = tmp;
|
|
*reflist_0_count = 1;
|
|
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,
|
|
GQueue * ref_list,
|
|
GstVaapiEncoderH264Ref ** reflist_0,
|
|
guint * reflist_0_count,
|
|
GstVaapiEncoderH264Ref ** reflist_1, guint * reflist_1_count)
|
|
{
|
|
/* reflist_0 ordering is same for hierarchical-P and hierarchical-B */
|
|
reflist0_init_hierarchical (encoder, picture, ref_list, reflist_0,
|
|
reflist_0_count);
|
|
|
|
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;
|
|
}
|
|
|
|
static gboolean
|
|
reference_list_init (GstVaapiEncoderH264 * encoder,
|
|
GstVaapiEncPicture * picture,
|
|
GstVaapiEncoderH264Ref ** reflist_0,
|
|
guint * reflist_0_count,
|
|
GstVaapiEncoderH264Ref ** reflist_1, guint * reflist_1_count)
|
|
{
|
|
GstVaapiEncoderH264Ref *tmp;
|
|
GstVaapiH264ViewRefPool *const ref_pool =
|
|
&encoder->ref_pools[encoder->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;
|
|
|
|
/* reference picture handling for hierarchial encode */
|
|
if (encoder->prediction_type != GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT) {
|
|
return reference_list_init_hierarchical (encoder, picture,
|
|
&ref_pool->ref_list, reflist_0, reflist_0_count, reflist_1,
|
|
reflist_1_count);
|
|
}
|
|
|
|
iter = g_queue_peek_tail_link (&ref_pool->ref_list);
|
|
for (; iter; iter = g_list_previous (iter)) {
|
|
tmp = (GstVaapiEncoderH264Ref *) iter->data;
|
|
g_assert (tmp && tmp->poc != picture->poc);
|
|
if (_poc_greater_than (picture->poc, tmp->poc, encoder->max_pic_order_cnt)) {
|
|
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] = (GstVaapiEncoderH264Ref *) 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] = (GstVaapiEncoderH264Ref *) iter->data;
|
|
++count;
|
|
}
|
|
*reflist_1_count = count;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Fills in VA sequence parameter buffer */
|
|
static gboolean
|
|
fill_sequence (GstVaapiEncoderH264 * encoder, GstVaapiEncSequence * sequence)
|
|
{
|
|
VAEncSequenceParameterBufferH264 *const seq_param = sequence->param;
|
|
GstVaapiH264ViewRefPool *const ref_pool =
|
|
&encoder->ref_pools[encoder->view_idx];
|
|
|
|
memset (seq_param, 0, sizeof (VAEncSequenceParameterBufferH264));
|
|
seq_param->seq_parameter_set_id = encoder->view_idx;
|
|
seq_param->level_idc = encoder->level_idc;
|
|
seq_param->intra_period = GST_VAAPI_ENCODER_KEYFRAME_PERIOD (encoder);
|
|
seq_param->intra_idr_period = GST_VAAPI_ENCODER_KEYFRAME_PERIOD (encoder);
|
|
seq_param->ip_period = encoder->ip_period;
|
|
seq_param->bits_per_second = encoder->bitrate_bits;
|
|
|
|
seq_param->max_num_ref_frames = ref_pool->max_ref_frames;
|
|
seq_param->picture_width_in_mbs = encoder->mb_width;
|
|
seq_param->picture_height_in_mbs = encoder->mb_height;
|
|
|
|
/*sequence field values */
|
|
seq_param->seq_fields.value = 0;
|
|
seq_param->seq_fields.bits.chroma_format_idc = 1;
|
|
seq_param->seq_fields.bits.frame_mbs_only_flag = 1;
|
|
seq_param->seq_fields.bits.mb_adaptive_frame_field_flag = FALSE;
|
|
seq_param->seq_fields.bits.seq_scaling_matrix_present_flag = FALSE;
|
|
/* direct_8x8_inference_flag default false */
|
|
seq_param->seq_fields.bits.direct_8x8_inference_flag = FALSE;
|
|
g_assert (encoder->log2_max_frame_num >= 4);
|
|
seq_param->seq_fields.bits.log2_max_frame_num_minus4 =
|
|
encoder->log2_max_frame_num - 4;
|
|
/* picture order count */
|
|
encoder->pic_order_cnt_type = seq_param->seq_fields.bits.pic_order_cnt_type =
|
|
0;
|
|
g_assert (encoder->log2_max_pic_order_cnt >= 4);
|
|
seq_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 =
|
|
encoder->log2_max_pic_order_cnt - 4;
|
|
|
|
seq_param->bit_depth_luma_minus8 = 0;
|
|
seq_param->bit_depth_chroma_minus8 = 0;
|
|
|
|
/* not used if pic_order_cnt_type == 0 */
|
|
if (seq_param->seq_fields.bits.pic_order_cnt_type == 1) {
|
|
encoder->delta_pic_order_always_zero_flag =
|
|
seq_param->seq_fields.bits.delta_pic_order_always_zero_flag = TRUE;
|
|
seq_param->num_ref_frames_in_pic_order_cnt_cycle = 0;
|
|
seq_param->offset_for_non_ref_pic = 0;
|
|
seq_param->offset_for_top_to_bottom_field = 0;
|
|
memset (seq_param->offset_for_ref_frame, 0,
|
|
sizeof (seq_param->offset_for_ref_frame));
|
|
}
|
|
|
|
/* frame_cropping_flag */
|
|
if ((GST_VAAPI_ENCODER_WIDTH (encoder) & 15) ||
|
|
(GST_VAAPI_ENCODER_HEIGHT (encoder) & 15)) {
|
|
static const guint SubWidthC[] = { 1, 2, 2, 1 };
|
|
static const guint SubHeightC[] = { 1, 2, 1, 1 };
|
|
const guint CropUnitX =
|
|
SubWidthC[seq_param->seq_fields.bits.chroma_format_idc];
|
|
const guint CropUnitY =
|
|
SubHeightC[seq_param->seq_fields.bits.chroma_format_idc] *
|
|
(2 - seq_param->seq_fields.bits.frame_mbs_only_flag);
|
|
|
|
seq_param->frame_cropping_flag = 1;
|
|
seq_param->frame_crop_left_offset = 0;
|
|
seq_param->frame_crop_right_offset =
|
|
(16 * encoder->mb_width -
|
|
GST_VAAPI_ENCODER_WIDTH (encoder)) / CropUnitX;
|
|
seq_param->frame_crop_top_offset = 0;
|
|
seq_param->frame_crop_bottom_offset =
|
|
(16 * encoder->mb_height -
|
|
GST_VAAPI_ENCODER_HEIGHT (encoder)) / CropUnitY;
|
|
}
|
|
|
|
/* VUI parameters are always set, at least for timing_info (framerate) */
|
|
seq_param->vui_parameters_present_flag = TRUE;
|
|
if (seq_param->vui_parameters_present_flag) {
|
|
seq_param->vui_fields.bits.aspect_ratio_info_present_flag = TRUE;
|
|
if (seq_param->vui_fields.bits.aspect_ratio_info_present_flag) {
|
|
const GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);
|
|
seq_param->aspect_ratio_idc = 0xff;
|
|
seq_param->sar_width = GST_VIDEO_INFO_PAR_N (vip);
|
|
seq_param->sar_height = GST_VIDEO_INFO_PAR_D (vip);
|
|
}
|
|
seq_param->vui_fields.bits.bitstream_restriction_flag = FALSE;
|
|
/* if vui_parameters_present_flag is TRUE and sps data belongs to
|
|
* subset sps, timing_info_preset_flag should be zero (H.7.4.2.1.1) */
|
|
seq_param->vui_fields.bits.timing_info_present_flag = !encoder->view_idx;
|
|
if (seq_param->vui_fields.bits.timing_info_present_flag) {
|
|
seq_param->num_units_in_tick = GST_VAAPI_ENCODER_FPS_D (encoder);
|
|
seq_param->time_scale = GST_VAAPI_ENCODER_FPS_N (encoder) * 2;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* Fills in VA picture parameter buffer */
|
|
static gboolean
|
|
fill_picture (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture,
|
|
GstVaapiCodedBuffer * codedbuf, GstVaapiSurfaceProxy * surface)
|
|
{
|
|
VAEncPictureParameterBufferH264 *const pic_param = picture->param;
|
|
GstVaapiH264ViewRefPool *const ref_pool =
|
|
&encoder->ref_pools[encoder->view_idx];
|
|
GstVaapiEncoderH264Ref *ref_pic;
|
|
GList *reflist;
|
|
guint i;
|
|
|
|
memset (pic_param, 0, sizeof (VAEncPictureParameterBufferH264));
|
|
|
|
/* reference list, */
|
|
pic_param->CurrPic.picture_id = GST_VAAPI_SURFACE_PROXY_SURFACE_ID (surface);
|
|
pic_param->CurrPic.TopFieldOrderCnt = picture->poc;
|
|
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->coded_buf = GST_VAAPI_CODED_BUFFER_ID (codedbuf);
|
|
|
|
pic_param->pic_parameter_set_id = encoder->view_idx;
|
|
pic_param->seq_parameter_set_id = encoder->view_idx ? 1 : 0;
|
|
pic_param->last_picture = 0; /* means last encoding picture */
|
|
pic_param->frame_num = picture->frame_num;
|
|
pic_param->pic_init_qp = encoder->qp_i;
|
|
pic_param->num_ref_idx_l0_active_minus1 =
|
|
(ref_pool->max_reflist0_count ? (ref_pool->max_reflist0_count - 1) : 0);
|
|
pic_param->num_ref_idx_l1_active_minus1 =
|
|
(ref_pool->max_reflist1_count ? (ref_pool->max_reflist1_count - 1) : 0);
|
|
pic_param->chroma_qp_index_offset = 0;
|
|
pic_param->second_chroma_qp_index_offset = 0;
|
|
|
|
/* set picture fields */
|
|
pic_param->pic_fields.value = 0;
|
|
pic_param->pic_fields.bits.idr_pic_flag =
|
|
GST_VAAPI_ENC_PICTURE_IS_IDR (picture);
|
|
pic_param->pic_fields.bits.reference_pic_flag =
|
|
GST_VAAPI_ENC_PICTURE_IS_REFRENCE (picture);
|
|
pic_param->pic_fields.bits.entropy_coding_mode_flag = encoder->use_cabac;
|
|
pic_param->pic_fields.bits.weighted_pred_flag = FALSE;
|
|
pic_param->pic_fields.bits.weighted_bipred_idc = 0;
|
|
pic_param->pic_fields.bits.constrained_intra_pred_flag = 0;
|
|
pic_param->pic_fields.bits.transform_8x8_mode_flag = encoder->use_dct8x8;
|
|
/* enable debloking */
|
|
pic_param->pic_fields.bits.deblocking_filter_control_present_flag = TRUE;
|
|
pic_param->pic_fields.bits.redundant_pic_cnt_present_flag = FALSE;
|
|
/* bottom_field_pic_order_in_frame_present_flag */
|
|
pic_param->pic_fields.bits.pic_order_present_flag = FALSE;
|
|
pic_param->pic_fields.bits.pic_scaling_matrix_present_flag = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Adds slice headers to picture */
|
|
static gboolean
|
|
add_slice_headers (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture,
|
|
GstVaapiEncoderH264Ref ** reflist_0, guint reflist_0_count,
|
|
GstVaapiEncoderH264Ref ** 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;
|
|
|
|
g_assert (picture);
|
|
|
|
mb_size = encoder->mb_width * encoder->mb_height;
|
|
|
|
g_assert (encoder->num_slices && encoder->num_slices < mb_size);
|
|
slice_of_mbs = mb_size / encoder->num_slices;
|
|
slice_mod_mbs = mb_size % encoder->num_slices;
|
|
last_mb_index = 0;
|
|
for (i_slice = 0; i_slice < encoder->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, encoder);
|
|
g_assert (slice && slice->param_id != VA_INVALID_ID);
|
|
slice_param = slice->param;
|
|
|
|
memset (slice_param, 0, sizeof (VAEncSliceParameterBufferH264));
|
|
slice_param->macroblock_address = last_mb_index;
|
|
slice_param->num_macroblocks = cur_slice_mbs;
|
|
slice_param->macroblock_info = VA_INVALID_ID;
|
|
slice_param->slice_type = h264_get_slice_type (picture->type);
|
|
g_assert ((gint8) slice_param->slice_type != -1);
|
|
slice_param->pic_parameter_set_id = encoder->view_idx;
|
|
slice_param->idr_pic_id = encoder->idr_num;
|
|
slice_param->pic_order_cnt_lsb = picture->poc;
|
|
|
|
/* not used if pic_order_cnt_type = 0 */
|
|
slice_param->delta_pic_order_cnt_bottom = 0;
|
|
memset (slice_param->delta_pic_order_cnt, 0,
|
|
sizeof (slice_param->delta_pic_order_cnt));
|
|
|
|
/* only works for B frames */
|
|
slice_param->direct_spatial_mv_pred_flag = TRUE;
|
|
/* default equal to picture parameters */
|
|
slice_param->num_ref_idx_active_override_flag = reflist_0_count
|
|
|| reflist_1_count;
|
|
if (picture->type != GST_VAAPI_PICTURE_TYPE_I && reflist_0_count > 0)
|
|
slice_param->num_ref_idx_l0_active_minus1 = reflist_0_count - 1;
|
|
else
|
|
slice_param->num_ref_idx_l0_active_minus1 = 0;
|
|
if (picture->type == GST_VAAPI_PICTURE_TYPE_B && reflist_1_count > 0)
|
|
slice_param->num_ref_idx_l1_active_minus1 = reflist_1_count - 1;
|
|
else
|
|
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;
|
|
}
|
|
}
|
|
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].flags = 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;
|
|
}
|
|
}
|
|
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].flags = VA_PICTURE_H264_INVALID;
|
|
}
|
|
|
|
/* not used if pic_param.pic_fields.bits.weighted_pred_flag == FALSE */
|
|
slice_param->luma_log2_weight_denom = 0;
|
|
slice_param->chroma_log2_weight_denom = 0;
|
|
slice_param->luma_weight_l0_flag = FALSE;
|
|
memset (slice_param->luma_weight_l0, 0,
|
|
sizeof (slice_param->luma_weight_l0));
|
|
memset (slice_param->luma_offset_l0, 0,
|
|
sizeof (slice_param->luma_offset_l0));
|
|
slice_param->chroma_weight_l0_flag = FALSE;
|
|
memset (slice_param->chroma_weight_l0, 0,
|
|
sizeof (slice_param->chroma_weight_l0));
|
|
memset (slice_param->chroma_offset_l0, 0,
|
|
sizeof (slice_param->chroma_offset_l0));
|
|
slice_param->luma_weight_l1_flag = FALSE;
|
|
memset (slice_param->luma_weight_l1, 0,
|
|
sizeof (slice_param->luma_weight_l1));
|
|
memset (slice_param->luma_offset_l1, 0,
|
|
sizeof (slice_param->luma_offset_l1));
|
|
slice_param->chroma_weight_l1_flag = FALSE;
|
|
memset (slice_param->chroma_weight_l1, 0,
|
|
sizeof (slice_param->chroma_weight_l1));
|
|
memset (slice_param->chroma_offset_l1, 0,
|
|
sizeof (slice_param->chroma_offset_l1));
|
|
|
|
slice_param->cabac_init_idc = 0;
|
|
slice_param->slice_qp_delta = encoder->qp_i - encoder->init_qp;
|
|
if (GST_VAAPI_ENCODER_RATE_CONTROL (encoder) == GST_VAAPI_RATECONTROL_CQP) {
|
|
if (picture->type == GST_VAAPI_PICTURE_TYPE_P) {
|
|
slice_param->slice_qp_delta += encoder->qp_ip;
|
|
} else if (picture->type == GST_VAAPI_PICTURE_TYPE_B) {
|
|
slice_param->slice_qp_delta += encoder->qp_ib;
|
|
}
|
|
if ((gint) encoder->init_qp + slice_param->slice_qp_delta <
|
|
(gint) encoder->min_qp) {
|
|
slice_param->slice_qp_delta = encoder->min_qp - encoder->init_qp;
|
|
}
|
|
if ((gint) encoder->init_qp + slice_param->slice_qp_delta >
|
|
(gint) encoder->max_qp) {
|
|
slice_param->slice_qp_delta = encoder->max_qp - encoder->init_qp;
|
|
}
|
|
}
|
|
slice_param->disable_deblocking_filter_idc = 0;
|
|
slice_param->slice_alpha_c0_offset_div2 = 2;
|
|
slice_param->slice_beta_offset_div2 = 2;
|
|
|
|
/* 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 (encoder->is_mvc && !encoder->view_idx &&
|
|
(GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
|
|
VA_ENC_PACKED_HEADER_RAW_DATA)
|
|
&& !add_packed_prefix_nal_header (encoder, picture, slice))
|
|
goto error_create_packed_prefix_nal_hdr;
|
|
if ((GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
|
|
VA_ENC_PACKED_HEADER_SLICE)
|
|
&& !add_packed_slice_header (encoder, 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 (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
|
|
{
|
|
GstVaapiEncSequence *sequence = NULL;
|
|
|
|
/* Insert an AU delimiter */
|
|
if ((GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
|
|
VA_ENC_PACKED_HEADER_RAW_DATA) && encoder->use_aud) {
|
|
if (!add_packed_au_delimiter (encoder, picture))
|
|
goto error_create_packed_au_delimiter;
|
|
}
|
|
|
|
/* submit an SPS header before every new I-frame, if codec config changed
|
|
* or if the picture is IDR.
|
|
*/
|
|
if ((!encoder->config_changed || picture->type != GST_VAAPI_PICTURE_TYPE_I)
|
|
&& !GST_VAAPI_ENC_PICTURE_IS_IDR (picture))
|
|
return TRUE;
|
|
|
|
sequence = GST_VAAPI_ENC_SEQUENCE_NEW (H264, encoder);
|
|
if (!sequence || !fill_sequence (encoder, sequence))
|
|
goto error_create_seq_param;
|
|
|
|
/* add subset sps for non-base view and sps for base view */
|
|
if (encoder->is_mvc && encoder->view_idx) {
|
|
if ((GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
|
|
VA_ENC_PACKED_HEADER_SEQUENCE)
|
|
&& !add_packed_sequence_header_mvc (encoder, picture, sequence))
|
|
goto error_create_packed_seq_hdr;
|
|
} else {
|
|
if ((GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
|
|
VA_ENC_PACKED_HEADER_SEQUENCE)
|
|
&& !add_packed_sequence_header (encoder, 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 (!encoder->is_mvc || encoder->view_idx > 0)
|
|
encoder->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_au_delimiter:
|
|
{
|
|
GST_ERROR ("failed to create AU delimiter");
|
|
gst_vaapi_codec_object_replace (&sequence, NULL);
|
|
}
|
|
error_create_packed_seq_hdr:
|
|
{
|
|
GST_ERROR ("failed to create packed sequence header buffer");
|
|
gst_vaapi_codec_object_replace (&sequence, NULL);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
ensure_control_rate_params (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
if (GST_VAAPI_ENCODER_RATE_CONTROL (encoder) == GST_VAAPI_RATECONTROL_CQP)
|
|
return TRUE;
|
|
|
|
#if VA_CHECK_VERSION(1,1,0)
|
|
if (GST_VAAPI_ENCODER_RATE_CONTROL (encoder) == GST_VAAPI_RATECONTROL_ICQ) {
|
|
GST_VAAPI_ENCODER_VA_RATE_CONTROL (encoder).ICQ_quality_factor =
|
|
encoder->quality_factor;
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
/* RateControl params */
|
|
GST_VAAPI_ENCODER_VA_RATE_CONTROL (encoder).bits_per_second =
|
|
encoder->bitrate_bits;
|
|
GST_VAAPI_ENCODER_VA_RATE_CONTROL (encoder).window_size = encoder->cpb_length;
|
|
GST_VAAPI_ENCODER_VA_RATE_CONTROL (encoder).initial_qp = encoder->init_qp;
|
|
GST_VAAPI_ENCODER_VA_RATE_CONTROL (encoder).min_qp = encoder->min_qp;
|
|
|
|
#if VA_CHECK_VERSION(1,1,0)
|
|
GST_VAAPI_ENCODER_VA_RATE_CONTROL (encoder).max_qp = encoder->max_qp;
|
|
#endif
|
|
|
|
#if VA_CHECK_VERSION(1,0,0)
|
|
GST_VAAPI_ENCODER_VA_RATE_CONTROL (encoder).rc_flags.bits.mb_rate_control =
|
|
(guint) encoder->mbbrc;
|
|
#endif
|
|
|
|
#if VA_CHECK_VERSION(1,3,0)
|
|
GST_VAAPI_ENCODER_VA_RATE_CONTROL (encoder).quality_factor =
|
|
encoder->quality_factor;
|
|
#endif
|
|
|
|
/* HRD params */
|
|
fill_hrd_params (encoder, &GST_VAAPI_ENCODER_VA_HRD (encoder));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Generates additional control parameters */
|
|
static gboolean
|
|
ensure_misc_params (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
|
|
{
|
|
GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
|
|
|
|
if (!gst_vaapi_encoder_ensure_param_control_rate (base_encoder, picture))
|
|
return FALSE;
|
|
|
|
if (GST_VAAPI_ENCODER_RATE_CONTROL (encoder) == GST_VAAPI_RATECONTROL_CBR ||
|
|
GST_VAAPI_ENCODER_RATE_CONTROL (encoder) == GST_VAAPI_RATECONTROL_VBR) {
|
|
if (!encoder->view_idx) {
|
|
if ((GST_VAAPI_ENC_PICTURE_IS_IDR (picture)) &&
|
|
(GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
|
|
VA_ENC_PACKED_HEADER_MISC) &&
|
|
!add_packed_sei_header (encoder, picture,
|
|
GST_VAAPI_H264_SEI_BUF_PERIOD | GST_VAAPI_H264_SEI_PIC_TIMING))
|
|
goto error_create_packed_sei_hdr;
|
|
|
|
else if (!GST_VAAPI_ENC_PICTURE_IS_IDR (picture) &&
|
|
(GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
|
|
VA_ENC_PACKED_HEADER_MISC) &&
|
|
!add_packed_sei_header (encoder, picture,
|
|
GST_VAAPI_H264_SEI_PIC_TIMING))
|
|
goto error_create_packed_sei_hdr;
|
|
}
|
|
}
|
|
|
|
if (!gst_vaapi_encoder_ensure_param_trellis (base_encoder, picture))
|
|
return FALSE;
|
|
|
|
if (!gst_vaapi_encoder_ensure_param_roi_regions (base_encoder, picture))
|
|
return FALSE;
|
|
|
|
if (!gst_vaapi_encoder_ensure_param_quality_level (base_encoder, picture))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
error_create_packed_sei_hdr:
|
|
{
|
|
GST_ERROR ("failed to create packed SEI header");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Generates and submits PPS header accordingly into the bitstream */
|
|
static gboolean
|
|
ensure_picture (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture,
|
|
GstVaapiCodedBufferProxy * codedbuf_proxy, GstVaapiSurfaceProxy * surface)
|
|
{
|
|
GstVaapiCodedBuffer *const codedbuf =
|
|
GST_VAAPI_CODED_BUFFER_PROXY_BUFFER (codedbuf_proxy);
|
|
gboolean res = FALSE;
|
|
|
|
res = fill_picture (encoder, picture, codedbuf, surface);
|
|
|
|
if (!res)
|
|
return FALSE;
|
|
|
|
if (picture->type == GST_VAAPI_PICTURE_TYPE_I &&
|
|
(GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
|
|
VA_ENC_PACKED_HEADER_PICTURE)
|
|
&& !add_packed_picture_header (encoder, picture)) {
|
|
GST_ERROR ("set picture packed header failed");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* Generates slice headers */
|
|
static gboolean
|
|
ensure_slices (GstVaapiEncoderH264 * encoder, GstVaapiEncPicture * picture)
|
|
{
|
|
GstVaapiEncoderH264Ref *reflist_0[16];
|
|
GstVaapiEncoderH264Ref *reflist_1[16];
|
|
GstVaapiH264ViewRefPool *const ref_pool =
|
|
&encoder->ref_pools[encoder->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 (encoder, 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 (encoder, picture,
|
|
reflist_0, reflist_0_count, reflist_1, reflist_1_count))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Normalizes bitrate (and CPB size) for HRD conformance */
|
|
static void
|
|
ensure_bitrate_hrd (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
|
|
guint bitrate, cpb_size;
|
|
|
|
if (!base_encoder->bitrate) {
|
|
encoder->bitrate_bits = 0;
|
|
return;
|
|
}
|
|
|
|
/* Round down bitrate. This is a hard limit mandated by the user */
|
|
g_assert (SX_BITRATE >= 6);
|
|
bitrate = (base_encoder->bitrate * 1000) & ~((1U << SX_BITRATE) - 1);
|
|
if (bitrate != encoder->bitrate_bits) {
|
|
GST_DEBUG ("HRD bitrate: %u bits/sec", bitrate);
|
|
encoder->bitrate_bits = bitrate;
|
|
encoder->config_changed = TRUE;
|
|
}
|
|
|
|
/* Round up CPB size. This is an HRD compliance detail */
|
|
g_assert (SX_CPB_SIZE >= 4);
|
|
cpb_size = gst_util_uint64_scale (bitrate, encoder->cpb_length, 1000) &
|
|
~((1U << SX_CPB_SIZE) - 1);
|
|
if (cpb_size != encoder->cpb_length_bits) {
|
|
GST_DEBUG ("HRD CPB size: %u bits", cpb_size);
|
|
encoder->cpb_length_bits = cpb_size;
|
|
encoder->config_changed = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Estimates a good enough bitrate if none was supplied */
|
|
static void
|
|
ensure_bitrate (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
|
|
|
|
/* Default compression: 48 bits per macroblock in "high-compression" mode */
|
|
switch (GST_VAAPI_ENCODER_RATE_CONTROL (encoder)) {
|
|
case GST_VAAPI_RATECONTROL_CBR:
|
|
case GST_VAAPI_RATECONTROL_VBR:
|
|
case GST_VAAPI_RATECONTROL_VBR_CONSTRAINED:
|
|
case GST_VAAPI_RATECONTROL_QVBR:
|
|
if (!base_encoder->bitrate) {
|
|
/* According to the literature and testing, CABAC entropy coding
|
|
mode could provide for +10% to +18% improvement in general,
|
|
thus estimating +15% here ; and using adaptive 8x8 transforms
|
|
in I-frames could bring up to +10% improvement. */
|
|
guint bits_per_mb = 48;
|
|
guint64 factor;
|
|
|
|
if (!encoder->use_cabac)
|
|
bits_per_mb += (bits_per_mb * 15) / 100;
|
|
if (!encoder->use_dct8x8)
|
|
bits_per_mb += (bits_per_mb * 10) / 100;
|
|
|
|
factor = (guint64) encoder->mb_width * encoder->mb_height * bits_per_mb;
|
|
base_encoder->bitrate =
|
|
gst_util_uint64_scale (factor, GST_VAAPI_ENCODER_FPS_N (encoder),
|
|
GST_VAAPI_ENCODER_FPS_D (encoder)) / 1000;
|
|
GST_INFO ("target bitrate computed to %u kbps", base_encoder->bitrate);
|
|
}
|
|
break;
|
|
default:
|
|
base_encoder->bitrate = 0;
|
|
break;
|
|
}
|
|
ensure_bitrate_hrd (encoder);
|
|
}
|
|
|
|
/* Constructs profile and level information based on user-defined limits */
|
|
static GstVaapiEncoderStatus
|
|
ensure_profile_and_level (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
const GstVaapiProfile profile = encoder->profile;
|
|
const GstVaapiLevelH264 level = encoder->level;
|
|
|
|
if (!ensure_tuning (encoder))
|
|
GST_WARNING ("Failed to set some of the tuning option as expected! ");
|
|
|
|
if (!ensure_profile (encoder) || !ensure_profile_limits (encoder))
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
|
|
|
|
/* If set low-power encode entry point and hardware doesn't have
|
|
* support, it will fail in ensure_hw_profile() in later stage. */
|
|
encoder->entrypoint =
|
|
gst_vaapi_encoder_get_entrypoint (GST_VAAPI_ENCODER_CAST (encoder),
|
|
encoder->profile);
|
|
if (encoder->entrypoint == GST_VAAPI_ENTRYPOINT_INVALID) {
|
|
GST_WARNING ("Cannot find valid entrypoint");
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
|
|
}
|
|
|
|
/* Check HW constraints */
|
|
if (!ensure_hw_profile_limits (encoder))
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
|
|
if (encoder->profile_idc > encoder->hw_max_profile_idc)
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
|
|
|
|
/* Ensure bitrate if not set already and derive the right level to use */
|
|
ensure_bitrate (encoder);
|
|
if (!ensure_level (encoder))
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
|
|
|
|
if (encoder->profile != profile || encoder->level != level) {
|
|
GST_DEBUG ("selected %s profile at level %s",
|
|
gst_vaapi_utils_h264_get_profile_string (encoder->profile),
|
|
gst_vaapi_utils_h264_get_level_string (encoder->level));
|
|
encoder->config_changed = TRUE;
|
|
}
|
|
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
reset_properties (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (encoder);
|
|
guint mb_size, i;
|
|
gboolean ret;
|
|
|
|
if (encoder->idr_period < base_encoder->keyframe_period)
|
|
encoder->idr_period = base_encoder->keyframe_period;
|
|
|
|
g_assert (encoder->min_qp <= encoder->max_qp);
|
|
if (encoder->min_qp > encoder->init_qp)
|
|
encoder->min_qp = encoder->init_qp;
|
|
if (encoder->max_qp < encoder->init_qp)
|
|
encoder->max_qp = encoder->init_qp;
|
|
|
|
encoder->qp_i = encoder->init_qp;
|
|
|
|
mb_size = encoder->mb_width * encoder->mb_height;
|
|
ret = gst_vaapi_encoder_ensure_num_slices (base_encoder, encoder->profile,
|
|
encoder->entrypoint, (mb_size + 1) / 2, &encoder->num_slices);
|
|
g_assert (ret);
|
|
|
|
if (encoder->num_bframes > (base_encoder->keyframe_period + 1) / 2)
|
|
encoder->num_bframes = (base_encoder->keyframe_period + 1) / 2;
|
|
|
|
gst_vaapi_encoder_ensure_max_num_ref_frames (base_encoder, encoder->profile,
|
|
encoder->entrypoint);
|
|
|
|
if (base_encoder->max_num_ref_frames_1 < 1 && encoder->num_bframes > 0) {
|
|
GST_WARNING ("Disabling b-frame since the driver doesn't support it");
|
|
encoder->num_bframes = 0;
|
|
|
|
if (encoder->prediction_type ==
|
|
GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B)
|
|
encoder->prediction_type = GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT;
|
|
}
|
|
|
|
if (encoder->num_ref_frames > base_encoder->max_num_ref_frames_0) {
|
|
GST_INFO ("Lowering the number of reference frames to %d",
|
|
base_encoder->max_num_ref_frames_0);
|
|
encoder->num_ref_frames = base_encoder->max_num_ref_frames_0;
|
|
}
|
|
|
|
if (encoder->num_bframes > 0 && GST_VAAPI_ENCODER_FPS_N (encoder) > 0)
|
|
encoder->cts_offset = gst_util_uint64_scale (GST_SECOND,
|
|
GST_VAAPI_ENCODER_FPS_D (encoder), GST_VAAPI_ENCODER_FPS_N (encoder));
|
|
else
|
|
encoder->cts_offset = 0;
|
|
|
|
/* init max_frame_num, max_poc */
|
|
encoder->log2_max_frame_num =
|
|
h264_get_log2_max_frame_num (encoder->idr_period);
|
|
g_assert (encoder->log2_max_frame_num >= 4);
|
|
encoder->max_frame_num = (1 << encoder->log2_max_frame_num);
|
|
encoder->log2_max_pic_order_cnt = encoder->log2_max_frame_num + 1;
|
|
encoder->max_pic_order_cnt = (1 << encoder->log2_max_pic_order_cnt);
|
|
encoder->idr_num = 0;
|
|
|
|
/* If temporal scalability enabled then use hierarchical-p/b
|
|
* according to num_bframes as default prediction */
|
|
if (encoder->temporal_levels > 1
|
|
&& encoder->prediction_type ==
|
|
GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT) {
|
|
if (encoder->num_bframes > 0)
|
|
encoder->prediction_type =
|
|
GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_B;
|
|
else
|
|
encoder->prediction_type =
|
|
GST_VAAPI_ENCODER_H264_PREDICTION_HIERARCHICAL_P;
|
|
}
|
|
|
|
if (encoder->prediction_type != GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT) {
|
|
|
|
/* Hierarchical prediction should have a temporal level count
|
|
* greater than one and we use 4 temporal levels as default */
|
|
if (encoder->temporal_levels <= 1)
|
|
encoder->temporal_levels = 4;
|
|
|
|
/* this ip_period calculation is for supporting hierarchical-p
|
|
* and hierarchical-b encode */
|
|
encoder->ip_period = 1 << (encoder->temporal_levels - 1);
|
|
|
|
/* align the idr_period to ip_peroid to simplify encode process */
|
|
encoder->idr_period =
|
|
GST_ROUND_UP_N (encoder->idr_period, encoder->ip_period);
|
|
|
|
GST_VAAPI_ENCODER_KEYFRAME_PERIOD (base_encoder) = encoder->idr_period;
|
|
|
|
/* Disable mvc-encode in hierarchical mode */
|
|
if (encoder->num_views > 1) {
|
|
encoder->num_views = 1;
|
|
encoder->is_mvc = FALSE;
|
|
}
|
|
|
|
/* no b-frames in Hierarchical-P */
|
|
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;
|
|
} else {
|
|
encoder->ip_period = GST_VAAPI_ENCODER_KEYFRAME_PERIOD (encoder) > 1 ?
|
|
(1 + encoder->num_bframes) : 0;
|
|
}
|
|
|
|
for (i = 0; i < encoder->num_views; i++) {
|
|
GstVaapiH264ViewRefPool *const ref_pool = &encoder->ref_pools[i];
|
|
GstVaapiH264ViewReorderPool *const reorder_pool =
|
|
&encoder->reorder_pools[i];
|
|
|
|
if (encoder->prediction_type == GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT) {
|
|
ref_pool->max_reflist0_count = encoder->num_ref_frames;
|
|
ref_pool->max_reflist1_count = encoder->num_bframes > 0;
|
|
ref_pool->max_ref_frames = ref_pool->max_reflist0_count
|
|
+ ref_pool->max_reflist1_count;
|
|
} else {
|
|
guint d;
|
|
|
|
/* This shouldn't be executed on MVC encoding */
|
|
g_assert (i < 1);
|
|
|
|
ref_pool->max_ref_frames =
|
|
encoder->temporal_levels * (encoder->temporal_levels) / 2 +
|
|
(encoder->num_bframes > 0);
|
|
ref_pool->max_reflist0_count = 1;
|
|
ref_pool->max_reflist1_count = encoder->num_bframes > 0;
|
|
encoder->num_ref_frames = ref_pool->max_ref_frames;
|
|
|
|
d = encoder->ip_period;
|
|
/* temporal_level_div[] is helpful to find out the temporal level
|
|
* where each frame should belongs */
|
|
for (i = 0; i < encoder->temporal_levels; i++) {
|
|
encoder->temporal_level_div[i] = d;
|
|
d >>= 1;
|
|
}
|
|
}
|
|
|
|
reorder_pool->frame_index = 0;
|
|
}
|
|
}
|
|
|
|
static GstVaapiEncoderStatus
|
|
gst_vaapi_encoder_h264_encode (GstVaapiEncoder * base_encoder,
|
|
GstVaapiEncPicture * picture, GstVaapiCodedBufferProxy * codedbuf)
|
|
{
|
|
GstVaapiEncoderH264 *const encoder = GST_VAAPI_ENCODER_H264 (base_encoder);
|
|
GstVaapiEncoderStatus ret = GST_VAAPI_ENCODER_STATUS_ERROR_UNKNOWN;
|
|
GstVaapiSurfaceProxy *reconstruct = NULL;
|
|
|
|
reconstruct = gst_vaapi_encoder_create_surface (base_encoder);
|
|
|
|
g_assert (GST_VAAPI_SURFACE_PROXY_SURFACE (reconstruct));
|
|
|
|
if (!ensure_sequence (encoder, picture))
|
|
goto error;
|
|
if (!ensure_misc_params (encoder, picture))
|
|
goto error;
|
|
if (!ensure_picture (encoder, picture, codedbuf, reconstruct))
|
|
goto error;
|
|
if (!ensure_slices (encoder, picture))
|
|
goto error;
|
|
if (!gst_vaapi_enc_picture_encode (picture))
|
|
goto error;
|
|
|
|
if (!reference_list_update (encoder, picture, reconstruct))
|
|
goto error;
|
|
|
|
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
|
|
|
|
/* ERRORS */
|
|
error:
|
|
{
|
|
if (reconstruct)
|
|
gst_vaapi_encoder_release_surface (GST_VAAPI_ENCODER (encoder),
|
|
reconstruct);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
struct _PendingIterState
|
|
{
|
|
guint cur_view;
|
|
GstVaapiPictureType pic_type;
|
|
};
|
|
|
|
static gboolean
|
|
gst_vaapi_encoder_h264_get_pending_reordered (GstVaapiEncoder * base_encoder,
|
|
GstVaapiEncPicture ** picture, gpointer * state)
|
|
{
|
|
GstVaapiEncoderH264 *const encoder = GST_VAAPI_ENCODER_H264 (base_encoder);
|
|
GstVaapiH264ViewReorderPool *reorder_pool;
|
|
GstVaapiEncPicture *pic = NULL;
|
|
struct _PendingIterState *iter;
|
|
|
|
g_return_val_if_fail (state, FALSE);
|
|
|
|
if (!*state) {
|
|
iter = g_new0 (struct _PendingIterState, 1);
|
|
iter->pic_type = GST_VAAPI_PICTURE_TYPE_P;
|
|
*state = iter;
|
|
} else {
|
|
iter = *state;
|
|
}
|
|
|
|
*picture = NULL;
|
|
|
|
if (iter->cur_view >= encoder->num_views)
|
|
return FALSE;
|
|
|
|
reorder_pool = &encoder->reorder_pools[iter->cur_view];
|
|
if (g_queue_is_empty (&reorder_pool->reorder_frame_list)) {
|
|
iter->cur_view++;
|
|
return TRUE; /* perhaps other views has pictures? */
|
|
}
|
|
|
|
if (iter->pic_type == GST_VAAPI_PICTURE_TYPE_P) {
|
|
pic = g_queue_pop_tail (&reorder_pool->reorder_frame_list);
|
|
g_assert (pic);
|
|
set_p_frame (pic, encoder);
|
|
|
|
g_queue_foreach (&reorder_pool->reorder_frame_list, (GFunc) set_b_frame,
|
|
encoder);
|
|
/* 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) {
|
|
pic->temporal_id = 0;
|
|
GST_VAAPI_PICTURE_FLAG_SET (pic, GST_VAAPI_ENC_PICTURE_FLAG_REFERENCE);
|
|
|
|
g_queue_sort (&reorder_pool->reorder_frame_list, sort_hierarchical_b,
|
|
NULL);
|
|
}
|
|
|
|
iter->pic_type = GST_VAAPI_PICTURE_TYPE_B;
|
|
} else if (iter->pic_type == GST_VAAPI_PICTURE_TYPE_B) {
|
|
pic = g_queue_pop_head (&reorder_pool->reorder_frame_list);
|
|
g_assert (pic);
|
|
} else {
|
|
GST_WARNING ("Unhandled pending picture type");
|
|
}
|
|
|
|
set_frame_num (encoder, pic);
|
|
|
|
if (GST_CLOCK_TIME_IS_VALID (pic->frame->pts))
|
|
pic->frame->pts += encoder->cts_offset;
|
|
|
|
*picture = pic;
|
|
return TRUE;
|
|
}
|
|
|
|
static GstVaapiEncoderStatus
|
|
gst_vaapi_encoder_h264_flush (GstVaapiEncoder * base_encoder)
|
|
{
|
|
GstVaapiEncoderH264 *const encoder = GST_VAAPI_ENCODER_H264 (base_encoder);
|
|
GstVaapiH264ViewReorderPool *reorder_pool;
|
|
GstVaapiEncPicture *pic;
|
|
guint i;
|
|
|
|
for (i = 0; i < encoder->num_views; i++) {
|
|
reorder_pool = &encoder->reorder_pools[i];
|
|
reorder_pool->frame_index = 0;
|
|
reorder_pool->cur_frame_num = 0;
|
|
reorder_pool->cur_present_index = 0;
|
|
reorder_pool->prev_frame_is_ref = FALSE;
|
|
|
|
while (!g_queue_is_empty (&reorder_pool->reorder_frame_list)) {
|
|
pic = (GstVaapiEncPicture *)
|
|
g_queue_pop_head (&reorder_pool->reorder_frame_list);
|
|
gst_vaapi_enc_picture_unref (pic);
|
|
}
|
|
g_queue_clear (&reorder_pool->reorder_frame_list);
|
|
}
|
|
|
|
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Generate "codec-data" buffer */
|
|
static GstVaapiEncoderStatus
|
|
gst_vaapi_encoder_h264_get_codec_data (GstVaapiEncoder * base_encoder,
|
|
GstBuffer ** out_buffer_ptr)
|
|
{
|
|
GstVaapiEncoderH264 *const encoder = GST_VAAPI_ENCODER_H264 (base_encoder);
|
|
const guint32 configuration_version = 0x01;
|
|
const guint32 nal_length_size = 4;
|
|
guint8 profile_idc, profile_comp, level_idc;
|
|
GstMapInfo sps_info, pps_info;
|
|
GstBitWriter bs;
|
|
GstBuffer *buffer;
|
|
|
|
if (!encoder->sps_data || !encoder->pps_data)
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_HEADER;
|
|
if (gst_buffer_get_size (encoder->sps_data) < 4)
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_HEADER;
|
|
|
|
if (!gst_buffer_map (encoder->sps_data, &sps_info, GST_MAP_READ))
|
|
goto error_map_sps_buffer;
|
|
|
|
if (!gst_buffer_map (encoder->pps_data, &pps_info, GST_MAP_READ))
|
|
goto error_map_pps_buffer;
|
|
|
|
/* skip sps_data[0], which is the nal_unit_type */
|
|
profile_idc = sps_info.data[1];
|
|
profile_comp = sps_info.data[2];
|
|
level_idc = sps_info.data[3];
|
|
|
|
/* Header */
|
|
gst_bit_writer_init_with_size (&bs, (sps_info.size + pps_info.size + 64),
|
|
FALSE);
|
|
WRITE_UINT32 (&bs, configuration_version, 8);
|
|
WRITE_UINT32 (&bs, profile_idc, 8);
|
|
WRITE_UINT32 (&bs, profile_comp, 8);
|
|
WRITE_UINT32 (&bs, level_idc, 8);
|
|
WRITE_UINT32 (&bs, 0x3f, 6); /* 111111 */
|
|
WRITE_UINT32 (&bs, nal_length_size - 1, 2);
|
|
WRITE_UINT32 (&bs, 0x07, 3); /* 111 */
|
|
|
|
/* Write SPS */
|
|
WRITE_UINT32 (&bs, 1, 5); /* SPS count = 1 */
|
|
g_assert (GST_BIT_WRITER_BIT_SIZE (&bs) % 8 == 0);
|
|
/* Write Nal unit length and data of SPS */
|
|
if (!gst_vaapi_utils_h26x_write_nal_unit (&bs, sps_info.data, sps_info.size))
|
|
goto nal_to_byte_stream_error;
|
|
|
|
/* Write PPS */
|
|
WRITE_UINT32 (&bs, 1, 8); /* PPS count = 1 */
|
|
/* Write Nal unit length and data of PPS */
|
|
if (!gst_vaapi_utils_h26x_write_nal_unit (&bs, pps_info.data, pps_info.size))
|
|
goto nal_to_byte_stream_error;
|
|
|
|
gst_buffer_unmap (encoder->pps_data, &pps_info);
|
|
gst_buffer_unmap (encoder->sps_data, &sps_info);
|
|
|
|
buffer = gst_bit_writer_reset_and_get_buffer (&bs);
|
|
if (!buffer)
|
|
goto error_alloc_buffer;
|
|
if (gst_buffer_n_memory (buffer) == 0) {
|
|
gst_buffer_unref (buffer);
|
|
goto error_alloc_buffer;
|
|
}
|
|
*out_buffer_ptr = buffer;
|
|
|
|
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
|
|
|
|
/* ERRORS */
|
|
bs_error:
|
|
{
|
|
GST_ERROR ("failed to write codec-data");
|
|
gst_buffer_unmap (encoder->sps_data, &sps_info);
|
|
gst_buffer_unmap (encoder->pps_data, &pps_info);
|
|
gst_bit_writer_reset (&bs);
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
|
|
}
|
|
nal_to_byte_stream_error:
|
|
{
|
|
GST_ERROR ("failed to write nal unit");
|
|
gst_buffer_unmap (encoder->sps_data, &sps_info);
|
|
gst_buffer_unmap (encoder->pps_data, &pps_info);
|
|
gst_bit_writer_reset (&bs);
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
|
|
}
|
|
error_map_sps_buffer:
|
|
{
|
|
GST_ERROR ("failed to map SPS packed header");
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
|
|
}
|
|
error_map_pps_buffer:
|
|
{
|
|
GST_ERROR ("failed to map PPS packed header");
|
|
gst_buffer_unmap (encoder->sps_data, &sps_info);
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
|
|
}
|
|
error_alloc_buffer:
|
|
{
|
|
GST_ERROR ("failed to allocate codec-data buffer");
|
|
gst_bit_writer_reset (&bs);
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
|
|
}
|
|
}
|
|
|
|
static guint32
|
|
get_temporal_id (GstVaapiEncoderH264 * encoder, guint32 display_order)
|
|
{
|
|
int l;
|
|
for (l = 0; l < encoder->temporal_levels; l++) {
|
|
if ((display_order % encoder->temporal_level_div[l]) == 0)
|
|
return l;
|
|
}
|
|
|
|
GST_WARNING ("Couldn't find valid temporal id");
|
|
return 0;
|
|
}
|
|
|
|
static GstVaapiEncoderStatus
|
|
gst_vaapi_encoder_h264_reordering (GstVaapiEncoder * base_encoder,
|
|
GstVideoCodecFrame * frame, GstVaapiEncPicture ** output)
|
|
{
|
|
GstVaapiEncoderH264 *const encoder = GST_VAAPI_ENCODER_H264 (base_encoder);
|
|
GstVaapiH264ViewReorderPool *reorder_pool = NULL;
|
|
GstVaapiEncPicture *picture;
|
|
gboolean is_idr = FALSE;
|
|
|
|
*output = NULL;
|
|
|
|
/* encoding views alternatively for MVC */
|
|
if (encoder->is_mvc) {
|
|
/* FIXME: Use first-in-bundle flag on buffers to reset view idx? */
|
|
if (frame)
|
|
encoder->view_idx = frame->system_frame_number % encoder->num_views;
|
|
else
|
|
encoder->view_idx = (encoder->view_idx + 1) % encoder->num_views;
|
|
}
|
|
reorder_pool = &encoder->reorder_pools[encoder->view_idx];
|
|
|
|
if (!frame) {
|
|
if (reorder_pool->reorder_state != GST_VAAPI_ENC_H264_REORD_DUMP_FRAMES)
|
|
return GST_VAAPI_ENCODER_STATUS_NO_SURFACE;
|
|
|
|
/* reorder_state = GST_VAAPI_ENC_H264_REORD_DUMP_FRAMES
|
|
dump B frames from queue, sometime, there may also have P frame or I frame */
|
|
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)) {
|
|
reorder_pool->reorder_state = GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES;
|
|
}
|
|
goto end;
|
|
}
|
|
|
|
/* new frame coming */
|
|
picture = GST_VAAPI_ENC_PICTURE_NEW (H264, encoder, frame);
|
|
if (!picture) {
|
|
GST_WARNING ("create H264 picture failed, frame timestamp:%"
|
|
GST_TIME_FORMAT, GST_TIME_ARGS (frame->pts));
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
|
|
}
|
|
++reorder_pool->cur_present_index;
|
|
picture->poc = ((reorder_pool->cur_present_index * 2) %
|
|
encoder->max_pic_order_cnt);
|
|
|
|
picture->temporal_id = (encoder->temporal_levels == 1) ? 1 :
|
|
get_temporal_id (encoder, reorder_pool->frame_index);
|
|
|
|
is_idr = (reorder_pool->frame_index == 0 ||
|
|
reorder_pool->frame_index >= encoder->idr_period);
|
|
|
|
/* check key frames */
|
|
if (is_idr || GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame) ||
|
|
(reorder_pool->frame_index %
|
|
GST_VAAPI_ENCODER_KEYFRAME_PERIOD (encoder)) == 0) {
|
|
++reorder_pool->frame_index;
|
|
|
|
/* b frame enabled, check queue of reorder_frame_list */
|
|
if (encoder->num_bframes
|
|
&& !g_queue_is_empty (&reorder_pool->reorder_frame_list)) {
|
|
GstVaapiEncPicture *p_pic;
|
|
|
|
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 | GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame));
|
|
g_queue_push_tail (&reorder_pool->reorder_frame_list, picture);
|
|
picture = p_pic;
|
|
reorder_pool->reorder_state = GST_VAAPI_ENC_H264_REORD_DUMP_FRAMES;
|
|
} else { /* no b frames in queue */
|
|
set_key_frame (picture, encoder,
|
|
is_idr | GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame));
|
|
g_assert (g_queue_is_empty (&reorder_pool->reorder_frame_list));
|
|
if (encoder->num_bframes)
|
|
reorder_pool->reorder_state = GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES;
|
|
}
|
|
goto end;
|
|
}
|
|
|
|
/* new p/b frames coming */
|
|
++reorder_pool->frame_index;
|
|
if (reorder_pool->reorder_state == GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES &&
|
|
g_queue_get_length (&reorder_pool->reorder_frame_list) <
|
|
encoder->num_bframes) {
|
|
g_queue_push_tail (&reorder_pool->reorder_frame_list, picture);
|
|
return GST_VAAPI_ENCODER_STATUS_NO_SURFACE;
|
|
}
|
|
|
|
set_p_frame (picture, encoder);
|
|
|
|
if (reorder_pool->reorder_state == GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES) {
|
|
g_queue_foreach (&reorder_pool->reorder_frame_list, (GFunc) set_b_frame,
|
|
encoder);
|
|
reorder_pool->reorder_state = GST_VAAPI_ENC_H264_REORD_DUMP_FRAMES;
|
|
g_assert (!g_queue_is_empty (&reorder_pool->reorder_frame_list));
|
|
}
|
|
|
|
end:
|
|
g_assert (picture);
|
|
frame = picture->frame;
|
|
if (GST_CLOCK_TIME_IS_VALID (frame->pts))
|
|
frame->pts += encoder->cts_offset;
|
|
|
|
/* set frame_num based on previous frame reference type */
|
|
set_frame_num (encoder, picture);
|
|
|
|
*output = picture;
|
|
|
|
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiEncoderStatus
|
|
set_context_info (GstVaapiEncoder * base_encoder)
|
|
{
|
|
GstVaapiEncoderH264 *const encoder = GST_VAAPI_ENCODER_H264 (base_encoder);
|
|
GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);
|
|
const guint DEFAULT_SURFACES_COUNT = 3;
|
|
|
|
/* Maximum sizes for common headers (in bits) */
|
|
enum
|
|
{
|
|
MAX_SPS_HDR_SIZE = 16473,
|
|
MAX_VUI_PARAMS_SIZE = 210,
|
|
MAX_HRD_PARAMS_SIZE = 4103,
|
|
MAX_PPS_HDR_SIZE = 101,
|
|
MAX_SLICE_HDR_SIZE = 397 + 2572 + 6670 + 2402,
|
|
};
|
|
|
|
if (!ensure_hw_profile (encoder))
|
|
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
|
|
|
|
base_encoder->num_ref_frames = (encoder->num_ref_frames
|
|
+ (encoder->num_bframes > 0 ? 1 : 0) + DEFAULT_SURFACES_COUNT)
|
|
* encoder->num_views;
|
|
|
|
/* Only YUV 4:2:0 formats are supported for now. This means that we
|
|
have a limit of 3200 bits per macroblock. */
|
|
/* XXX: check profile and compute RawMbBits */
|
|
base_encoder->codedbuf_size = (GST_ROUND_UP_16 (vip->width) *
|
|
GST_ROUND_UP_16 (vip->height) / 256) * 400;
|
|
|
|
/* Account for SPS header */
|
|
/* XXX: exclude scaling lists, MVC/SVC extensions */
|
|
base_encoder->codedbuf_size += 4 + GST_ROUND_UP_8 (MAX_SPS_HDR_SIZE +
|
|
MAX_VUI_PARAMS_SIZE + 2 * MAX_HRD_PARAMS_SIZE) / 8;
|
|
|
|
/* Account for PPS header */
|
|
/* XXX: exclude slice groups, scaling lists, MVC/SVC extensions */
|
|
base_encoder->codedbuf_size += 4 + GST_ROUND_UP_8 (MAX_PPS_HDR_SIZE) / 8;
|
|
|
|
/* Account for slice header */
|
|
base_encoder->codedbuf_size += encoder->num_slices * (4 +
|
|
GST_ROUND_UP_8 (MAX_SLICE_HDR_SIZE) / 8);
|
|
/* Some of the Intel Platforms(eg: APL) doesn't have LLC so
|
|
* the driver call cflush to ensure data consistency which is an
|
|
* expensive operation but we can still reduce the impact by
|
|
* limitting the pre-calculated coded_buffer size. This is not
|
|
* strictly following the h264 specification, but should be safe
|
|
* enough with intel-vaapi-driver. Our test cases showing significat
|
|
* performance improvement on APL platfrom with small coded-buffer size */
|
|
if (encoder->compliance_mode ==
|
|
GST_VAAPI_ENCODER_H264_COMPLIANCE_MODE_RESTRICT_CODED_BUFFER_ALLOC)
|
|
base_encoder->codedbuf_size /= encoder->min_cr;
|
|
|
|
base_encoder->context_info.profile = base_encoder->profile;
|
|
base_encoder->context_info.entrypoint = encoder->entrypoint;
|
|
|
|
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiEncoderStatus
|
|
gst_vaapi_encoder_h264_reconfigure (GstVaapiEncoder * base_encoder)
|
|
{
|
|
GstVaapiEncoderH264 *const encoder = GST_VAAPI_ENCODER_H264 (base_encoder);
|
|
GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);
|
|
GstVaapiEncoderStatus status;
|
|
guint mb_width, mb_height;
|
|
|
|
mb_width = (GST_VAAPI_ENCODER_WIDTH (encoder) + 15) / 16;
|
|
mb_height = (GST_VAAPI_ENCODER_HEIGHT (encoder) + 15) / 16;
|
|
if (mb_width != encoder->mb_width || mb_height != encoder->mb_height) {
|
|
GST_DEBUG ("resolution: %dx%d", GST_VAAPI_ENCODER_WIDTH (encoder),
|
|
GST_VAAPI_ENCODER_HEIGHT (encoder));
|
|
encoder->mb_width = mb_width;
|
|
encoder->mb_height = mb_height;
|
|
encoder->config_changed = TRUE;
|
|
}
|
|
|
|
/* Take number of MVC views from input caps if provided */
|
|
if (GST_VIDEO_INFO_MULTIVIEW_MODE (vip) ==
|
|
GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME
|
|
|| GST_VIDEO_INFO_MULTIVIEW_MODE (vip) ==
|
|
GST_VIDEO_MULTIVIEW_MODE_MULTIVIEW_FRAME_BY_FRAME)
|
|
encoder->num_views = GST_VIDEO_INFO_VIEWS (vip);
|
|
|
|
encoder->is_mvc = encoder->num_views > 1;
|
|
|
|
status = ensure_profile_and_level (encoder);
|
|
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
|
|
return status;
|
|
|
|
reset_properties (encoder);
|
|
ensure_control_rate_params (encoder);
|
|
return set_context_info (base_encoder);
|
|
}
|
|
|
|
struct _GstVaapiEncoderH264Class
|
|
{
|
|
GstVaapiEncoderClass parent_class;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GstVaapiEncoderH264, gst_vaapi_encoder_h264,
|
|
GST_TYPE_VAAPI_ENCODER);
|
|
|
|
static void
|
|
gst_vaapi_encoder_h264_init (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
guint32 i;
|
|
|
|
/* Default encoding entrypoint */
|
|
encoder->entrypoint = GST_VAAPI_ENTRYPOINT_SLICE_ENCODE;
|
|
|
|
/* Multi-view coding information */
|
|
encoder->is_mvc = FALSE;
|
|
encoder->num_views = 1;
|
|
encoder->view_idx = 0;
|
|
encoder->temporal_levels = MIN_TEMPORAL_LEVELS;
|
|
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 */
|
|
for (i = 0; i < MAX_NUM_VIEWS; i++) {
|
|
GstVaapiH264ViewReorderPool *const reorder_pool =
|
|
&encoder->reorder_pools[i];
|
|
g_queue_init (&reorder_pool->reorder_frame_list);
|
|
reorder_pool->reorder_state = GST_VAAPI_ENC_H264_REORD_NONE;
|
|
reorder_pool->frame_index = 0;
|
|
reorder_pool->cur_frame_num = 0;
|
|
reorder_pool->cur_present_index = 0;
|
|
}
|
|
|
|
/* reference list info initialize */
|
|
for (i = 0; i < MAX_NUM_VIEWS; i++) {
|
|
GstVaapiH264ViewRefPool *const ref_pool = &encoder->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;
|
|
}
|
|
|
|
encoder->compliance_mode = GST_VAAPI_ENCODER_H264_COMPLIANCE_MODE_STRICT;
|
|
encoder->min_cr = 1;
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_encoder_h264_finalize (GObject * object)
|
|
{
|
|
/*free private buffers */
|
|
GstVaapiEncoderH264 *const encoder = GST_VAAPI_ENCODER_H264 (object);
|
|
GstVaapiEncPicture *pic;
|
|
GstVaapiEncoderH264Ref *ref;
|
|
guint32 i;
|
|
|
|
gst_buffer_replace (&encoder->sps_data, NULL);
|
|
gst_buffer_replace (&encoder->subset_sps_data, NULL);
|
|
gst_buffer_replace (&encoder->pps_data, NULL);
|
|
|
|
/* reference list info de-init */
|
|
for (i = 0; i < MAX_NUM_VIEWS; i++) {
|
|
GstVaapiH264ViewRefPool *const ref_pool = &encoder->ref_pools[i];
|
|
while (!g_queue_is_empty (&ref_pool->ref_list)) {
|
|
ref = (GstVaapiEncoderH264Ref *) g_queue_pop_head (&ref_pool->ref_list);
|
|
reference_pic_free (encoder, ref);
|
|
}
|
|
g_queue_clear (&ref_pool->ref_list);
|
|
}
|
|
|
|
/* re-ordering list initialize */
|
|
for (i = 0; i < MAX_NUM_VIEWS; i++) {
|
|
GstVaapiH264ViewReorderPool *const reorder_pool =
|
|
&encoder->reorder_pools[i];
|
|
while (!g_queue_is_empty (&reorder_pool->reorder_frame_list)) {
|
|
pic = (GstVaapiEncPicture *)
|
|
g_queue_pop_head (&reorder_pool->reorder_frame_list);
|
|
gst_vaapi_enc_picture_unref (pic);
|
|
}
|
|
g_queue_clear (&reorder_pool->reorder_frame_list);
|
|
}
|
|
|
|
G_OBJECT_CLASS (gst_vaapi_encoder_h264_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
set_view_ids (GstVaapiEncoderH264 * const encoder, const GValue * value)
|
|
{
|
|
guint i, j;
|
|
guint len = gst_value_array_get_size (value);
|
|
|
|
if (len == 0)
|
|
goto set_default_ids;
|
|
|
|
if (len != encoder->num_views) {
|
|
GST_WARNING ("The view number is %d, but %d view IDs are provided. Just "
|
|
"fallback to use default view IDs.", encoder->num_views, len);
|
|
goto set_default_ids;
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
const GValue *val = gst_value_array_get_value (value, i);
|
|
encoder->view_ids[i] = g_value_get_uint (val);
|
|
}
|
|
|
|
/* check whether duplicated ID */
|
|
for (i = 0; i < len; i++) {
|
|
for (j = i + 1; j < len; j++) {
|
|
if (encoder->view_ids[i] == encoder->view_ids[j]) {
|
|
GST_WARNING ("The view %d and view %d have same view ID %d. Just "
|
|
"fallback to use default view IDs.", i, j, encoder->view_ids[i]);
|
|
goto set_default_ids;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
set_default_ids:
|
|
{
|
|
for (i = 0; i < encoder->num_views; i++)
|
|
encoder->view_ids[i] = i;
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_view_ids (GstVaapiEncoderH264 * const encoder, GValue * value)
|
|
{
|
|
guint i;
|
|
GValue id = G_VALUE_INIT;
|
|
|
|
g_value_reset (value);
|
|
g_value_init (&id, G_TYPE_UINT);
|
|
|
|
for (i = 0; i < encoder->num_views; i++) {
|
|
g_value_set_uint (&id, encoder->view_ids[i]);
|
|
gst_value_array_append_value (value, &id);
|
|
}
|
|
g_value_unset (&id);
|
|
}
|
|
|
|
/**
|
|
* @ENCODER_H264_PROP_RATECONTROL: Rate control (#GstVaapiRateControl).
|
|
* @ENCODER_H264_PROP_TUNE: The tuning options (#GstVaapiEncoderTune).
|
|
* @ENCODER_H264_PROP_MAX_BFRAMES: Number of B-frames between I
|
|
* and P (uint).
|
|
* @ENCODER_H264_PROP_INIT_QP: Initial quantizer value (uint).
|
|
* @ENCODER_H264_PROP_MIN_QP: Minimal quantizer value (uint).
|
|
* @ENCODER_H264_PROP_NUM_SLICES: Number of slices per frame (uint).
|
|
* @ENCODER_H264_PROP_CABAC: Enable CABAC entropy coding mode (bool).
|
|
* @ENCODER_H264_PROP_DCT8X8: Enable adaptive use of 8x8
|
|
* transforms in I-frames (bool).
|
|
* @ENCODER_H264_PROP_CPB_LENGTH: Length of the CPB buffer
|
|
* in milliseconds (uint).
|
|
* @ENCODER_H264_PROP_NUM_VIEWS: Number of views per frame.
|
|
* @ENCODER_H264_PROP_VIEW_IDS: View IDs
|
|
* @ENCODER_H264_PROP_AUD: Insert AUD as first NAL per frame.
|
|
* @ENCODER_H264_PROP_COMPLIANCE_MODE: Relax Compliance restrictions
|
|
* @ENCODER_H264_PROP_NUM_REF_FRAMES: Maximum number of reference frames.
|
|
* @ENCODER_H264_PROP_MBBRC: Macroblock level Bitrate Control.
|
|
* @ENCODER_H264_PROP_QP_IP: Difference of QP between I and P frame.
|
|
* @ENCODER_H264_PROP_QP_IB: Difference of QP between I and B frame.
|
|
* @ENCODER_H264_PROP_TEMPORAL_LEVELS: Number of temporal levels
|
|
* @ENCODER_H264_PROP_PREDICTION_TYPE: Reference picture selection modes
|
|
* @ENCODER_H264_PROP_MAX_QP: Maximal quantizer value (uint).
|
|
* @ENCODER_H264_PROP_QUALITY_FACTOR: Factor for ICQ/QVBR bitrate control mode.
|
|
*
|
|
* The set of H.264 encoder specific configurable properties.
|
|
*/
|
|
enum
|
|
{
|
|
ENCODER_H264_PROP_RATECONTROL = 1,
|
|
ENCODER_H264_PROP_TUNE,
|
|
ENCODER_H264_PROP_MAX_BFRAMES,
|
|
ENCODER_H264_PROP_INIT_QP,
|
|
ENCODER_H264_PROP_MIN_QP,
|
|
ENCODER_H264_PROP_NUM_SLICES,
|
|
ENCODER_H264_PROP_CABAC,
|
|
ENCODER_H264_PROP_DCT8X8,
|
|
ENCODER_H264_PROP_CPB_LENGTH,
|
|
ENCODER_H264_PROP_NUM_VIEWS,
|
|
ENCODER_H264_PROP_VIEW_IDS,
|
|
ENCODER_H264_PROP_AUD,
|
|
ENCODER_H264_PROP_COMPLIANCE_MODE,
|
|
ENCODER_H264_PROP_NUM_REF_FRAMES,
|
|
ENCODER_H264_PROP_MBBRC,
|
|
ENCODER_H264_PROP_QP_IP,
|
|
ENCODER_H264_PROP_QP_IB,
|
|
ENCODER_H264_PROP_TEMPORAL_LEVELS,
|
|
ENCODER_H264_PROP_PREDICTION_TYPE,
|
|
ENCODER_H264_PROP_MAX_QP,
|
|
ENCODER_H264_PROP_QUALITY_FACTOR,
|
|
ENCODER_H264_N_PROPERTIES
|
|
};
|
|
|
|
static GParamSpec *properties[ENCODER_H264_N_PROPERTIES];
|
|
|
|
static void
|
|
gst_vaapi_encoder_h264_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstVaapiEncoderH264 *const encoder = GST_VAAPI_ENCODER_H264 (object);
|
|
GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER (object);
|
|
|
|
if (base_encoder->num_codedbuf_queued > 0) {
|
|
GST_ERROR_OBJECT (object,
|
|
"failed to set any property after encoding started");
|
|
return;
|
|
}
|
|
|
|
switch (prop_id) {
|
|
case ENCODER_H264_PROP_RATECONTROL:
|
|
gst_vaapi_encoder_set_rate_control (base_encoder,
|
|
g_value_get_enum (value));
|
|
break;
|
|
case ENCODER_H264_PROP_TUNE:
|
|
gst_vaapi_encoder_set_tuning (base_encoder, g_value_get_enum (value));
|
|
break;
|
|
case ENCODER_H264_PROP_MAX_BFRAMES:
|
|
encoder->num_bframes = g_value_get_uint (value);
|
|
break;
|
|
case ENCODER_H264_PROP_INIT_QP:
|
|
encoder->init_qp = g_value_get_uint (value);
|
|
break;
|
|
case ENCODER_H264_PROP_MIN_QP:
|
|
encoder->min_qp = g_value_get_uint (value);
|
|
break;
|
|
case ENCODER_H264_PROP_QP_IP:
|
|
encoder->qp_ip = g_value_get_int (value);
|
|
break;
|
|
case ENCODER_H264_PROP_QP_IB:
|
|
encoder->qp_ib = g_value_get_int (value);
|
|
break;
|
|
case ENCODER_H264_PROP_NUM_SLICES:
|
|
encoder->num_slices = g_value_get_uint (value);
|
|
break;
|
|
case ENCODER_H264_PROP_CABAC:
|
|
encoder->use_cabac = g_value_get_boolean (value);
|
|
break;
|
|
case ENCODER_H264_PROP_DCT8X8:
|
|
encoder->use_dct8x8 = g_value_get_boolean (value);
|
|
break;
|
|
case ENCODER_H264_PROP_CPB_LENGTH:
|
|
encoder->cpb_length = g_value_get_uint (value);
|
|
break;
|
|
case ENCODER_H264_PROP_NUM_VIEWS:
|
|
encoder->num_views = g_value_get_uint (value);
|
|
break;
|
|
case ENCODER_H264_PROP_VIEW_IDS:
|
|
set_view_ids (encoder, value);
|
|
break;
|
|
case ENCODER_H264_PROP_AUD:
|
|
encoder->use_aud = g_value_get_boolean (value);
|
|
break;
|
|
case ENCODER_H264_PROP_COMPLIANCE_MODE:
|
|
encoder->compliance_mode = g_value_get_enum (value);
|
|
break;
|
|
case ENCODER_H264_PROP_NUM_REF_FRAMES:
|
|
encoder->num_ref_frames = g_value_get_uint (value);
|
|
break;
|
|
case ENCODER_H264_PROP_MBBRC:
|
|
encoder->mbbrc = g_value_get_enum (value);
|
|
break;
|
|
case ENCODER_H264_PROP_TEMPORAL_LEVELS:
|
|
encoder->temporal_levels = g_value_get_uint (value);
|
|
break;
|
|
case ENCODER_H264_PROP_PREDICTION_TYPE:
|
|
encoder->prediction_type = g_value_get_enum (value);
|
|
break;
|
|
case ENCODER_H264_PROP_MAX_QP:
|
|
encoder->max_qp = g_value_get_uint (value);
|
|
break;
|
|
case ENCODER_H264_PROP_QUALITY_FACTOR:
|
|
encoder->quality_factor = g_value_get_uint (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_encoder_h264_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstVaapiEncoderH264 *const encoder = GST_VAAPI_ENCODER_H264 (object);
|
|
GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER (object);
|
|
|
|
switch (prop_id) {
|
|
case ENCODER_H264_PROP_RATECONTROL:
|
|
g_value_set_enum (value, base_encoder->rate_control);
|
|
break;
|
|
case ENCODER_H264_PROP_TUNE:
|
|
g_value_set_enum (value, base_encoder->tune);
|
|
break;
|
|
case ENCODER_H264_PROP_MAX_BFRAMES:
|
|
g_value_set_uint (value, encoder->num_bframes);
|
|
break;
|
|
case ENCODER_H264_PROP_INIT_QP:
|
|
g_value_set_uint (value, encoder->init_qp);
|
|
break;
|
|
case ENCODER_H264_PROP_MIN_QP:
|
|
g_value_set_uint (value, encoder->min_qp);
|
|
break;
|
|
case ENCODER_H264_PROP_QP_IP:
|
|
g_value_set_int (value, encoder->qp_ip);
|
|
break;
|
|
case ENCODER_H264_PROP_QP_IB:
|
|
g_value_set_int (value, encoder->qp_ib);
|
|
break;
|
|
case ENCODER_H264_PROP_NUM_SLICES:
|
|
g_value_set_uint (value, encoder->num_slices);
|
|
break;
|
|
case ENCODER_H264_PROP_CABAC:
|
|
g_value_set_boolean (value, encoder->use_cabac);
|
|
break;
|
|
case ENCODER_H264_PROP_DCT8X8:
|
|
g_value_set_boolean (value, encoder->use_dct8x8);
|
|
break;
|
|
case ENCODER_H264_PROP_CPB_LENGTH:
|
|
g_value_set_uint (value, encoder->cpb_length);
|
|
break;
|
|
case ENCODER_H264_PROP_NUM_VIEWS:
|
|
g_value_set_uint (value, encoder->num_views);
|
|
break;
|
|
case ENCODER_H264_PROP_VIEW_IDS:
|
|
get_view_ids (encoder, value);
|
|
break;
|
|
case ENCODER_H264_PROP_AUD:
|
|
g_value_set_boolean (value, encoder->use_aud);
|
|
break;
|
|
case ENCODER_H264_PROP_COMPLIANCE_MODE:
|
|
g_value_set_enum (value, encoder->compliance_mode);
|
|
break;
|
|
case ENCODER_H264_PROP_NUM_REF_FRAMES:
|
|
g_value_set_uint (value, encoder->num_ref_frames);
|
|
break;
|
|
case ENCODER_H264_PROP_MBBRC:
|
|
g_value_set_enum (value, encoder->mbbrc);
|
|
break;
|
|
case ENCODER_H264_PROP_TEMPORAL_LEVELS:
|
|
g_value_set_uint (value, encoder->temporal_levels);
|
|
break;
|
|
case ENCODER_H264_PROP_PREDICTION_TYPE:
|
|
g_value_set_enum (value, encoder->prediction_type);
|
|
break;
|
|
case ENCODER_H264_PROP_MAX_QP:
|
|
g_value_set_uint (value, encoder->max_qp);
|
|
break;
|
|
case ENCODER_H264_PROP_QUALITY_FACTOR:
|
|
g_value_set_uint (value, encoder->quality_factor);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
GST_VAAPI_ENCODER_DEFINE_CLASS_DATA (H264);
|
|
|
|
static void
|
|
gst_vaapi_encoder_h264_class_init (GstVaapiEncoderH264Class * klass)
|
|
{
|
|
GObjectClass *const object_class = G_OBJECT_CLASS (klass);
|
|
GstVaapiEncoderClass *const encoder_class = GST_VAAPI_ENCODER_CLASS (klass);
|
|
|
|
encoder_class->class_data = &g_class_data;
|
|
encoder_class->reconfigure = gst_vaapi_encoder_h264_reconfigure;
|
|
encoder_class->reordering = gst_vaapi_encoder_h264_reordering;
|
|
encoder_class->encode = gst_vaapi_encoder_h264_encode;
|
|
encoder_class->flush = gst_vaapi_encoder_h264_flush;
|
|
encoder_class->get_codec_data = gst_vaapi_encoder_h264_get_codec_data;
|
|
encoder_class->get_pending_reordered =
|
|
gst_vaapi_encoder_h264_get_pending_reordered;
|
|
|
|
object_class->set_property = gst_vaapi_encoder_h264_set_property;
|
|
object_class->get_property = gst_vaapi_encoder_h264_get_property;
|
|
object_class->finalize = gst_vaapi_encoder_h264_finalize;
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:rate-control:
|
|
*
|
|
* The desired rate control mode, expressed as a #GstVaapiRateControl.
|
|
*/
|
|
properties[ENCODER_H264_PROP_RATECONTROL] =
|
|
g_param_spec_enum ("rate-control",
|
|
"Rate Control", "Rate control mode",
|
|
g_class_data.rate_control_get_type (),
|
|
g_class_data.default_rate_control,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:tune:
|
|
*
|
|
* The desired encoder tuning option.
|
|
*/
|
|
properties[ENCODER_H264_PROP_TUNE] =
|
|
g_param_spec_enum ("tune",
|
|
"Encoder Tuning",
|
|
"Encoder tuning option",
|
|
g_class_data.encoder_tune_get_type (),
|
|
g_class_data.default_encoder_tune,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:max-bframes:
|
|
*
|
|
* The number of B-frames between I and P.
|
|
*/
|
|
properties[ENCODER_H264_PROP_MAX_BFRAMES] =
|
|
g_param_spec_uint ("max-bframes",
|
|
"Max B-Frames", "Number of B-frames between I and P", 0, 10, 0,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:refs:
|
|
*
|
|
* The number of reference frames.
|
|
* If B frame is encoded, it will add 1 reference frame more.
|
|
*/
|
|
properties[ENCODER_H264_PROP_NUM_REF_FRAMES] =
|
|
g_param_spec_uint ("refs", "Number of Reference Frames",
|
|
"Number of reference frames", 1, 8, 1,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:init-qp:
|
|
*
|
|
* The initial quantizer value.
|
|
*/
|
|
properties[ENCODER_H264_PROP_INIT_QP] =
|
|
g_param_spec_uint ("init-qp",
|
|
"Initial QP", "Initial quantizer value", 0, 51, 26,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:min-qp:
|
|
*
|
|
* The minimum quantizer value.
|
|
*/
|
|
properties[ENCODER_H264_PROP_MIN_QP] =
|
|
g_param_spec_uint ("min-qp",
|
|
"Minimum QP", "Minimum quantizer value", 0, 51, 1,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:max-qp:
|
|
*
|
|
* The maximum quantizer value.
|
|
*
|
|
* Since: 1.18
|
|
*/
|
|
properties[ENCODER_H264_PROP_MAX_QP] =
|
|
g_param_spec_uint ("max-qp",
|
|
"Maximum QP", "Maximum quantizer value", 0, 51, 51,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:qp-ip:
|
|
*
|
|
* The difference of QP between I and P Frame.
|
|
* This is available only on CQP mode.
|
|
*/
|
|
properties[ENCODER_H264_PROP_QP_IP] =
|
|
g_param_spec_int ("qp-ip",
|
|
"Difference of QP between I and P frame",
|
|
"Difference of QP between I and P frame (available only on CQP)",
|
|
-51, 51, 0,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:qp-ib:
|
|
*
|
|
* The difference of QP between I and B Frame.
|
|
* This is available only on CQP mode.
|
|
*/
|
|
properties[ENCODER_H264_PROP_QP_IB] =
|
|
g_param_spec_int ("qp-ib",
|
|
"Difference of QP between I and B frame",
|
|
"Difference of QP between I and B frame (available only on CQP)",
|
|
-51, 51, 0,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:num-slices:
|
|
*
|
|
* The number of slices per frame.
|
|
*/
|
|
properties[ENCODER_H264_PROP_NUM_SLICES] =
|
|
g_param_spec_uint ("num-slices",
|
|
"Number of Slices",
|
|
"Number of slices per frame",
|
|
1, 200, 1,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:cabac:
|
|
*
|
|
* Enable CABAC entropy coding mode for improved compression ratio,
|
|
* at the expense that the minimum target profile is Main. Default
|
|
* is CAVLC entropy coding mode.
|
|
*/
|
|
properties[ENCODER_H264_PROP_CABAC] =
|
|
g_param_spec_boolean ("cabac",
|
|
"Enable CABAC",
|
|
"Enable CABAC entropy coding mode",
|
|
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:dct8x8:
|
|
*
|
|
* Enable adaptive use of 8x8 transforms in I-frames. This improves
|
|
* the compression ratio by the minimum target profile is High.
|
|
* Default is to use 4x4 DCT only.
|
|
*/
|
|
properties[ENCODER_H264_PROP_DCT8X8] =
|
|
g_param_spec_boolean ("dct8x8",
|
|
"Enable 8x8 DCT",
|
|
"Enable adaptive use of 8x8 transforms in I-frames",
|
|
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:mbbrc:
|
|
*
|
|
* Macroblock level bitrate control.
|
|
* This is not compatible with Constant QP rate control.
|
|
*/
|
|
properties[ENCODER_H264_PROP_MBBRC] =
|
|
g_param_spec_enum ("mbbrc",
|
|
"Macroblock level Bitrate Control",
|
|
"Macroblock level Bitrate Control",
|
|
GST_VAAPI_TYPE_ENCODER_MBBRC, GST_VAAPI_ENCODER_MBBRC_AUTO,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:temporal-levels:
|
|
*
|
|
* Number of temporal levels in the encoded stream.
|
|
*/
|
|
properties[ENCODER_H264_PROP_TEMPORAL_LEVELS] =
|
|
g_param_spec_uint ("temporal-levels",
|
|
"temporal levels",
|
|
"Number of temporal levels in the encoded stream ",
|
|
MIN_TEMPORAL_LEVELS, MAX_TEMPORAL_LEVELS, MIN_TEMPORAL_LEVELS,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:prediction-type:
|
|
*
|
|
* Select the referece picture selection modes
|
|
*/
|
|
properties[ENCODER_H264_PROP_PREDICTION_TYPE] =
|
|
g_param_spec_enum ("prediction-type",
|
|
"RefPic Selection",
|
|
"Reference Picture Selection Modes",
|
|
gst_vaapi_encoder_h264_prediction_type (),
|
|
GST_VAAPI_ENCODER_H264_PREDICTION_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:cpb-length:
|
|
*
|
|
* The size of the CPB buffer in milliseconds.
|
|
*/
|
|
properties[ENCODER_H264_PROP_CPB_LENGTH] =
|
|
g_param_spec_uint ("cpb-length",
|
|
"CPB Length", "Length of the CPB buffer in milliseconds",
|
|
1, 10000, DEFAULT_CPB_LENGTH,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:num-views:
|
|
*
|
|
* The number of views for MVC encoding .
|
|
*/
|
|
properties[ENCODER_H264_PROP_NUM_VIEWS] =
|
|
g_param_spec_uint ("num-views",
|
|
"Number of Views",
|
|
"Number of Views for MVC encoding",
|
|
1, MAX_NUM_VIEWS, 1,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:view-ids:
|
|
*
|
|
* The view ids for MVC encoding .
|
|
*/
|
|
properties[ENCODER_H264_PROP_VIEW_IDS] =
|
|
gst_param_spec_array ("view-ids",
|
|
"View IDs", "Set of View Ids used for MVC encoding",
|
|
g_param_spec_uint ("view-id-value", "View id value",
|
|
"view id values used for mvc encoding", 0, MAX_VIEW_ID, 0,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS),
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:aud:
|
|
*
|
|
* Use AU (Access Unit) delimeter.
|
|
*/
|
|
properties[ENCODER_H264_PROP_AUD] =
|
|
g_param_spec_boolean ("aud", "AU delimiter",
|
|
"Use AU (Access Unit) delimeter", FALSE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:Compliance Mode:
|
|
*
|
|
* Encode Tuning(Tweaking) with different compliance modes .
|
|
*
|
|
*
|
|
*/
|
|
properties[ENCODER_H264_PROP_COMPLIANCE_MODE] =
|
|
g_param_spec_enum ("compliance-mode",
|
|
"Spec Compliance Mode",
|
|
"Tune Encode quality/performance by relaxing specification"
|
|
" compliance restrictions",
|
|
gst_vaapi_encoder_h264_compliance_mode_type (),
|
|
GST_VAAPI_ENCODER_H264_COMPLIANCE_MODE_STRICT,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
/**
|
|
* GstVaapiEncoderH264:quality_factor:
|
|
*
|
|
* quality factor used under ICQ/QVBR bitrate control mode.
|
|
*/
|
|
properties[ENCODER_H264_PROP_QUALITY_FACTOR] =
|
|
g_param_spec_uint ("quality-factor",
|
|
"Quality factor for ICQ/QVBR",
|
|
"quality factor for ICQ/QVBR bitrate control mode"
|
|
"(low value means higher-quality, higher value means lower-quality)",
|
|
1, 51, 26,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT |
|
|
GST_VAAPI_PARAM_ENCODER_EXPOSURE);
|
|
|
|
g_object_class_install_properties (object_class, ENCODER_H264_N_PROPERTIES,
|
|
properties);
|
|
|
|
gst_type_mark_as_plugin_api (GST_VAAPI_TYPE_ENCODER_MBBRC, 0);
|
|
gst_type_mark_as_plugin_api (gst_vaapi_encoder_h264_prediction_type (), 0);
|
|
gst_type_mark_as_plugin_api (g_class_data.rate_control_get_type (), 0);
|
|
gst_type_mark_as_plugin_api (g_class_data.encoder_tune_get_type (), 0);
|
|
gst_type_mark_as_plugin_api (gst_vaapi_encoder_h264_compliance_mode_type (),
|
|
0);
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_encoder_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
|
|
*/
|
|
GstVaapiEncoder *
|
|
gst_vaapi_encoder_h264_new (GstVaapiDisplay * display)
|
|
{
|
|
return g_object_new (GST_TYPE_VAAPI_ENCODER_H264, "display", display, NULL);
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_encoder_h264_set_max_profile:
|
|
* @encoder: a #GstVaapiEncoderH264
|
|
* @profile: an H.264 #GstVaapiProfile
|
|
*
|
|
* Notifies the @encoder to use coding tools from the supplied
|
|
* @profile at most.
|
|
*
|
|
* This means that if the minimal profile derived to
|
|
* support the specified coding tools is greater than this @profile,
|
|
* then an error is returned when the @encoder is configured.
|
|
*
|
|
* Return value: %TRUE on success
|
|
*/
|
|
gboolean
|
|
gst_vaapi_encoder_h264_set_max_profile (GstVaapiEncoderH264 * encoder,
|
|
GstVaapiProfile profile)
|
|
{
|
|
guint8 profile_idc;
|
|
|
|
g_return_val_if_fail (encoder != NULL, FALSE);
|
|
g_return_val_if_fail (profile != GST_VAAPI_PROFILE_UNKNOWN, FALSE);
|
|
|
|
if (gst_vaapi_profile_get_codec (profile) != GST_VAAPI_CODEC_H264)
|
|
return FALSE;
|
|
|
|
profile_idc = gst_vaapi_utils_h264_get_profile_idc (profile);
|
|
if (!profile_idc)
|
|
return FALSE;
|
|
|
|
encoder->max_profile_idc = profile_idc;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_encoder_h264_get_profile_and_level:
|
|
* @encoder: a #GstVaapiEncoderH264
|
|
* @out_profile_ptr: return location for the #GstVaapiProfile
|
|
* @out_level_ptr: return location for the #GstVaapiLevelH264
|
|
*
|
|
* Queries the H.264 @encoder for the active profile and level. That
|
|
* information is only constructed and valid after the encoder is
|
|
* configured, i.e. after the gst_vaapi_encoder_set_codec_state()
|
|
* function is called.
|
|
*
|
|
* Return value: %TRUE on success
|
|
*/
|
|
gboolean
|
|
gst_vaapi_encoder_h264_get_profile_and_level (GstVaapiEncoderH264 * encoder,
|
|
GstVaapiProfile * out_profile_ptr, GstVaapiLevelH264 * out_level_ptr)
|
|
{
|
|
g_return_val_if_fail (encoder != NULL, FALSE);
|
|
|
|
if (!encoder->profile || !encoder->level)
|
|
return FALSE;
|
|
|
|
if (out_profile_ptr)
|
|
*out_profile_ptr = encoder->profile;
|
|
if (out_level_ptr)
|
|
*out_level_ptr = encoder->level;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_encoder_h264_supports_avc:
|
|
* @encoder: a #GstVaapiEncoderH264
|
|
*
|
|
* Queries the H.264 @encoder if it supports the generation of avC
|
|
* stream format.
|
|
*
|
|
* Returns: %TRUE if @encoder supports avC; %FALSE otherwise
|
|
**/
|
|
gboolean
|
|
gst_vaapi_encoder_h264_supports_avc (GstVaapiEncoderH264 * encoder)
|
|
{
|
|
return ((GST_VAAPI_ENCODER_PACKED_HEADERS (encoder) &
|
|
(VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE)) ==
|
|
(VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE));
|
|
}
|