gstreamer/gst-libs/gst/vaapi/gstvaapifeienc_h264.c
Matteo Valdina 83886ced66 libs: encoder: h264,h265: extend max periodic keyframe.
Increased max values of periodic key frame for h26x codecs.
This allow more fine tunning of encoder that in certian scenario
want higher periodic key frame.

For example: it doesn't want a key frame each 10 seconds but
each 120 seconds.

https://bugzilla.gnome.org/show_bug.cgi?id=786320
2018-02-19 22:34:53 +01:00

2259 lines
75 KiB
C

/*
* gstvaapifeienc_h264.c - H264 FEI ENC
*
* Copyright (C) 2016-2018 Intel Corporation
* Author: Leilei Shang <leilei.shang@intel.com>
* Author: Sreerenj Balachandran <sreerenj.balachandran@intel.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
/* GValueArray has deprecated without providing an alternative in glib >= 2.32
* See https://bugzilla.gnome.org/show_bug.cgi?id=667228
*/
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include "sysdeps.h"
#include <va/va.h>
#include <gst/base/gstbitwriter.h>
#include <gst/codecparsers/gsth264parser.h>
#include "gstvaapicompat.h"
#include "gstvaapiencoder_priv.h"
#include "gstvaapifeienc_h264.h"
#include "gstvaapiutils_h264_priv.h"
#include "gstvaapicodedbufferproxy_priv.h"
#include "gstvaapisurfaceproxy_priv.h"
#include "gstvaapisurface.h"
#include "gstvaapiutils.h"
#include <unistd.h>
#define DEBUG 1
#include "gstvaapidebug.h"
/* Define the maximum number of views supported */
#define MAX_NUM_VIEWS 10
/* Define the maximum value for view-id */
#define MAX_VIEW_ID 1023
/* Default CPB length (in milliseconds) */
#define DEFAULT_CPB_LENGTH 1500
/* Scale factor for CPB size (HRD cpb_size_scale: min = 4) */
#define SX_CPB_SIZE 4
/* Scale factor for bitrate (HRD bit_rate_scale: min = 6) */
#define SX_BITRATE 6
/* Define default rate control mode ("constant-qp") */
#define DEFAULT_RATECONTROL GST_VAAPI_RATECONTROL_CQP
/* Supported set of VA rate controls, within this implementation */
#define SUPPORTED_RATECONTROLS \
(GST_VAAPI_RATECONTROL_MASK (CQP) | \
GST_VAAPI_RATECONTROL_MASK (CBR) | \
GST_VAAPI_RATECONTROL_MASK (VBR) | \
GST_VAAPI_RATECONTROL_MASK (VBR_CONSTRAINED))
/* Supported set of tuning options, within this implementation */
#define SUPPORTED_TUNE_OPTIONS \
(GST_VAAPI_ENCODER_TUNE_MASK (NONE) | \
GST_VAAPI_ENCODER_TUNE_MASK (HIGH_COMPRESSION) | \
GST_VAAPI_ENCODER_TUNE_MASK (LOW_POWER))
/* Supported set of VA packed headers, within this implementation */
#define SUPPORTED_PACKED_HEADERS \
VA_ENC_PACKED_HEADER_NONE
typedef struct
{
GstVaapiSurfaceProxy *pic;
guint poc;
guint frame_num;
} GstVaapiFeiEncH264Ref;
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;
} 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;
}
/* ------------------------------------------------------------------------- */
/* --- FEI Enc --- */
/* ------------------------------------------------------------------------- */
#define GST_VAAPI_FEI_H264_ENC_CAST(feienc) \
((GstVaapiFeiEncH264 *)(feienc))
struct _GstVaapiFeiEncH264
{
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 init_qp;
guint32 min_qp;
guint32 num_slices;
guint32 num_bframes;
guint32 mb_width;
guint32 mb_height;
gboolean use_cabac;
gboolean use_dct8x8;
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;
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)
guint32 num_ref_frames; // set reference frame num
/* MVC */
gboolean is_mvc;
guint32 view_idx; /* View Order Index (VOIdx) */
guint32 num_views;
guint16 view_ids[MAX_NUM_VIEWS];
GstVaapiH264ViewRefPool ref_pools[MAX_NUM_VIEWS];
GstVaapiH264ViewReorderPool reorder_pools[MAX_NUM_VIEWS];
/*Fei frame level control */
guint search_window;
guint len_sp;
guint search_path;
guint ref_width;
guint ref_height;
guint submb_part_mask;
guint subpel_mode;
guint intra_part_mask;
guint intra_sad;
guint inter_sad;
guint num_mv_predictors_l0;
guint num_mv_predictors_l1;
guint adaptive_search;
guint multi_predL0;
guint multi_predL1;
};
/* Determines the largest supported profile by the underlying hardware */
static gboolean
ensure_hw_profile_limits (GstVaapiFeiEncH264 * feienc)
{
GstVaapiDisplay *const display = GST_VAAPI_ENCODER_DISPLAY (feienc);
GArray *profiles;
guint i, profile_idc, max_profile_idc;
if (feienc->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);
feienc->hw_max_profile_idc = max_profile_idc;
return TRUE;
}
/* Derives the profile supported by the underlying hardware */
static gboolean
ensure_hw_profile (GstVaapiFeiEncH264 * feienc)
{
GstVaapiDisplay *const display = GST_VAAPI_ENCODER_DISPLAY (feienc);
GstVaapiEntrypoint entrypoint = feienc->entrypoint;
GstVaapiProfile profile, profiles[4];
guint i, num_profiles = 0;
profiles[num_profiles++] = feienc->profile;
switch (feienc->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 (feienc)->profile = profile;
return TRUE;
/* ERRORS */
error_unsupported_profile:
{
GST_ERROR ("unsupported HW profile (0x%08x)", feienc->profile);
return FALSE;
}
}
/* Check target decoder constraints */
static gboolean
ensure_profile_limits (GstVaapiFeiEncH264 * feienc)
{
GstVaapiProfile profile;
if (!feienc->max_profile_idc
|| feienc->profile_idc <= feienc->max_profile_idc)
return TRUE;
GST_WARNING ("lowering coding tools to meet target decoder constraints");
profile = GST_VAAPI_PROFILE_UNKNOWN;
/* Try Main profile coding tools */
if (feienc->max_profile_idc < 100) {
feienc->use_dct8x8 = FALSE;
profile = GST_VAAPI_PROFILE_H264_MAIN;
}
/* Try Constrained Baseline profile coding tools */
if (feienc->max_profile_idc < 77) {
feienc->num_bframes = 0;
feienc->use_cabac = FALSE;
profile = GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE;
}
if (profile) {
feienc->profile = profile;
feienc->profile_idc = feienc->max_profile_idc;
}
return TRUE;
}
/* Derives the minimum profile from the active coding tools */
static gboolean
ensure_profile (GstVaapiFeiEncH264 * feienc)
{
GstVaapiProfile profile;
/* Always start from "constrained-baseline" profile for maximum
compatibility */
profile = GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE;
/* Main profile coding tools */
if (feienc->num_bframes > 0 || feienc->use_cabac)
profile = GST_VAAPI_PROFILE_H264_MAIN;
/* High profile coding tools */
if (feienc->use_dct8x8)
profile = GST_VAAPI_PROFILE_H264_HIGH;
/* MVC profiles coding tools */
if (feienc->num_views == 2)
profile = GST_VAAPI_PROFILE_H264_STEREO_HIGH;
else if (feienc->num_views > 2)
profile = GST_VAAPI_PROFILE_H264_MULTIVIEW_HIGH;
feienc->profile = profile;
feienc->profile_idc = gst_vaapi_utils_h264_get_profile_idc (profile);
return TRUE;
}
/* Derives the level from the currently set limits */
static gboolean
ensure_level (GstVaapiFeiEncH264 * feienc)
{
const guint cpb_factor = h264_get_cpb_nal_factor (feienc->profile);
const GstVaapiH264LevelLimits *limits_table;
guint i, num_limits, PicSizeMbs, MaxDpbMbs, MaxMBPS;
PicSizeMbs = feienc->mb_width * feienc->mb_height;
MaxDpbMbs = PicSizeMbs * ((feienc->num_bframes) ? 2 : 1);
MaxMBPS = gst_util_uint64_scale_int_ceil (PicSizeMbs,
GST_VAAPI_ENCODER_FPS_N (feienc), GST_VAAPI_ENCODER_FPS_D (feienc));
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 && (!feienc->bitrate_bits
|| feienc->bitrate_bits <= (limits->MaxBR * cpb_factor)) &&
(!feienc->cpb_length_bits ||
feienc->cpb_length_bits <= (limits->MaxCPB * cpb_factor)))
break;
}
if (i == num_limits)
goto error_unsupported_level;
feienc->level = limits_table[i].level;
feienc->level_idc = limits_table[i].level_idc;
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 (GstVaapiFeiEncH264 * feienc)
{
guint8 profile_idc;
if (!ensure_hw_profile_limits (feienc))
return FALSE;
profile_idc = feienc->hw_max_profile_idc;
if (feienc->max_profile_idc && feienc->max_profile_idc < profile_idc)
profile_idc = feienc->max_profile_idc;
/* Tuning options to enable Main profile */
if (profile_idc >= 77 && profile_idc != 88) {
feienc->use_cabac = TRUE;
if (!feienc->num_bframes)
feienc->num_bframes = 1;
}
/* Tuning options to enable High profile */
if (profile_idc >= 100) {
feienc->use_dct8x8 = TRUE;
}
return TRUE;
}
/* Ensure tuning options */
static gboolean
ensure_tuning (GstVaapiFeiEncH264 * feienc)
{
gboolean success;
switch (GST_VAAPI_ENCODER_TUNE (feienc)) {
case GST_VAAPI_ENCODER_TUNE_HIGH_COMPRESSION:
success = ensure_tuning_high_compression (feienc);
break;
case GST_VAAPI_ENCODER_TUNE_LOW_POWER:
/* Set low-power encode entry point. If hardware doesn't have
* support, it will fail in ensure_hw_profile() in later stage.
* So not duplicating the profile/entrypont query mechanism
* here as a part of optimization */
feienc->entrypoint = GST_VAAPI_ENTRYPOINT_SLICE_ENCODE_LP;
success = TRUE;
break;
default:
success = TRUE;
break;
}
return success;
}
/* Handle new GOP starts */
static void
reset_gop_start (GstVaapiFeiEncH264 * feienc)
{
GstVaapiH264ViewReorderPool *const reorder_pool =
&feienc->reorder_pools[feienc->view_idx];
reorder_pool->frame_index = 1;
reorder_pool->cur_frame_num = 0;
reorder_pool->cur_present_index = 0;
++feienc->idr_num;
}
/* Marks the supplied picture as a B-frame */
static void
set_b_frame (GstVaapiEncPicture * pic, GstVaapiFeiEncH264 * feienc)
{
GstVaapiH264ViewReorderPool *const reorder_pool =
&feienc->reorder_pools[feienc->view_idx];
g_assert (pic && feienc);
g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
pic->type = GST_VAAPI_PICTURE_TYPE_B;
pic->frame_num = (reorder_pool->cur_frame_num % feienc->max_frame_num);
}
/* Marks the supplied picture as a P-frame */
static void
set_p_frame (GstVaapiEncPicture * pic, GstVaapiFeiEncH264 * feienc)
{
GstVaapiH264ViewReorderPool *const reorder_pool =
&feienc->reorder_pools[feienc->view_idx];
g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
pic->type = GST_VAAPI_PICTURE_TYPE_P;
pic->frame_num = (reorder_pool->cur_frame_num % feienc->max_frame_num);
}
/* Marks the supplied picture as an I-frame */
static void
set_i_frame (GstVaapiEncPicture * pic, GstVaapiFeiEncH264 * feienc)
{
GstVaapiH264ViewReorderPool *const reorder_pool =
&feienc->reorder_pools[feienc->view_idx];
g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
pic->type = GST_VAAPI_PICTURE_TYPE_I;
pic->frame_num = (reorder_pool->cur_frame_num % feienc->max_frame_num);
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, GstVaapiFeiEncH264 * feienc)
{
g_return_if_fail (pic->type == GST_VAAPI_PICTURE_TYPE_NONE);
pic->type = GST_VAAPI_PICTURE_TYPE_I;
pic->frame_num = 0;
pic->poc = 0;
GST_VAAPI_ENC_PICTURE_FLAG_SET (pic, GST_VAAPI_ENC_PICTURE_FLAG_IDR);
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,
GstVaapiFeiEncH264 * feienc, gboolean is_idr)
{
if (is_idr) {
reset_gop_start (feienc);
set_idr_frame (picture, feienc);
} else
set_i_frame (picture, feienc);
}
/* Fills in VA HRD parameters */
static void
fill_hrd_params (GstVaapiFeiEncH264 * feienc, VAEncMiscParameterHRD * hrd)
{
if (feienc->bitrate_bits > 0) {
hrd->buffer_size = feienc->cpb_length_bits;
hrd->initial_buffer_fullness = hrd->buffer_size / 2;
} else {
hrd->buffer_size = 0;
hrd->initial_buffer_fullness = 0;
}
}
/* Reference list */
static gboolean
reference_list_init (GstVaapiFeiEncH264 * feienc,
GstVaapiEncPicture * picture,
GstVaapiFeiEncH264Ref ** reflist_0,
guint * reflist_0_count,
GstVaapiFeiEncH264Ref ** reflist_1, guint * reflist_1_count)
{
GstVaapiFeiEncH264Ref *tmp;
GstVaapiH264ViewRefPool *const ref_pool =
&feienc->ref_pools[feienc->view_idx];
GList *iter, *list_0_start = NULL, *list_1_start = NULL;
guint count;
*reflist_0_count = 0;
*reflist_1_count = 0;
if (picture->type == GST_VAAPI_PICTURE_TYPE_I)
return TRUE;
iter = g_queue_peek_tail_link (&ref_pool->ref_list);
for (; iter; iter = g_list_previous (iter)) {
tmp = (GstVaapiFeiEncH264Ref *) iter->data;
g_assert (tmp && tmp->poc != picture->poc);
if (_poc_greater_than (picture->poc, tmp->poc, feienc->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] = (GstVaapiFeiEncH264Ref *) 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] = (GstVaapiFeiEncH264Ref *) iter->data;
++count;
}
*reflist_1_count = count;
return TRUE;
}
/* Fills in VA sequence parameter buffer */
static gboolean
fill_sequence (GstVaapiFeiEncH264 * feienc, GstVaapiEncSequence * sequence)
{
VAEncSequenceParameterBufferH264 *const seq_param = sequence->param;
GstVaapiH264ViewRefPool *const ref_pool =
&feienc->ref_pools[feienc->view_idx];
memset (seq_param, 0, sizeof (VAEncSequenceParameterBufferH264));
seq_param->seq_parameter_set_id = feienc->view_idx;
seq_param->level_idc = feienc->level_idc;
seq_param->intra_period = GST_VAAPI_ENCODER_KEYFRAME_PERIOD (feienc);
seq_param->intra_idr_period = GST_VAAPI_ENCODER_KEYFRAME_PERIOD (feienc);
seq_param->ip_period = 1 + feienc->num_bframes;
seq_param->ip_period = seq_param->intra_period > 1 ?
(1 + feienc->num_bframes) : 0;
seq_param->bits_per_second = feienc->bitrate_bits;
seq_param->max_num_ref_frames = ref_pool->max_ref_frames;
seq_param->picture_width_in_mbs = feienc->mb_width;
seq_param->picture_height_in_mbs = feienc->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 (feienc->log2_max_frame_num >= 4);
seq_param->seq_fields.bits.log2_max_frame_num_minus4 =
feienc->log2_max_frame_num - 4;
/* picture order count */
feienc->pic_order_cnt_type = seq_param->seq_fields.bits.pic_order_cnt_type =
0;
g_assert (feienc->log2_max_pic_order_cnt >= 4);
seq_param->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 =
feienc->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) {
feienc->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 (feienc) & 15) ||
(GST_VAAPI_ENCODER_HEIGHT (feienc) & 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 * feienc->mb_width - GST_VAAPI_ENCODER_WIDTH (feienc)) / CropUnitX;
seq_param->frame_crop_top_offset = 0;
seq_param->frame_crop_bottom_offset =
(16 * feienc->mb_height -
GST_VAAPI_ENCODER_HEIGHT (feienc)) / 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 (feienc);
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 = !feienc->view_idx;
if (seq_param->vui_fields.bits.timing_info_present_flag) {
seq_param->num_units_in_tick = GST_VAAPI_ENCODER_FPS_D (feienc);
seq_param->time_scale = GST_VAAPI_ENCODER_FPS_N (feienc) * 2;
}
}
return TRUE;
}
/* Fills in VA picture parameter buffer */
static gboolean
fill_picture (GstVaapiFeiEncH264 * feienc, GstVaapiEncPicture * picture,
GstVaapiSurfaceProxy * surface, GstVaapiCodedBuffer * const codedbuf)
{
VAEncPictureParameterBufferH264 *const pic_param = picture->param;
GstVaapiH264ViewRefPool *const ref_pool =
&feienc->ref_pools[feienc->view_idx];
GstVaapiFeiEncH264Ref *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;
pic_param->CurrPic.frame_idx = picture->frame_num;
i = 0;
if (picture->type != GST_VAAPI_PICTURE_TYPE_I) {
for (reflist = g_queue_peek_head_link (&ref_pool->ref_list);
reflist; reflist = g_list_next (reflist)) {
ref_pic = reflist->data;
g_assert (ref_pic && ref_pic->pic &&
GST_VAAPI_SURFACE_PROXY_SURFACE_ID (ref_pic->pic) != VA_INVALID_ID);
pic_param->ReferenceFrames[i].picture_id =
GST_VAAPI_SURFACE_PROXY_SURFACE_ID (ref_pic->pic);
pic_param->ReferenceFrames[i].TopFieldOrderCnt = ref_pic->poc;
pic_param->ReferenceFrames[i].flags |=
VA_PICTURE_H264_SHORT_TERM_REFERENCE;
pic_param->ReferenceFrames[i].frame_idx = ref_pic->frame_num;
++i;
}
g_assert (i <= 16 && i <= ref_pool->max_ref_frames);
}
for (; i < 16; ++i) {
pic_param->ReferenceFrames[i].picture_id = VA_INVALID_ID;
pic_param->ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID;
}
pic_param->coded_buf = GST_VAAPI_OBJECT_ID (codedbuf);
pic_param->pic_parameter_set_id = feienc->view_idx;
pic_param->seq_parameter_set_id = feienc->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 = feienc->init_qp;
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 =
(picture->type != GST_VAAPI_PICTURE_TYPE_B);
pic_param->pic_fields.bits.entropy_coding_mode_flag = feienc->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 = feienc->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 (GstVaapiFeiEncH264 * feienc, GstVaapiEncPicture * picture,
GstVaapiFeiEncH264Ref ** reflist_0, guint reflist_0_count,
GstVaapiFeiEncH264Ref ** reflist_1, guint reflist_1_count,
GstVaapiFeiInfoToPakH264 * info_to_pak)
{
VAEncSliceParameterBufferH264 *slice_param;
GstVaapiEncSlice *slice;
GArray *h264_slice_params;
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 = feienc->mb_width * feienc->mb_height;
g_assert (feienc->num_slices && feienc->num_slices < mb_size);
slice_of_mbs = mb_size / feienc->num_slices;
slice_mod_mbs = mb_size % feienc->num_slices;
last_mb_index = 0;
h264_slice_params =
g_array_new (FALSE, TRUE, sizeof (VAEncSliceParameterBufferH264));
for (i_slice = 0; i_slice < feienc->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, feienc);
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 = feienc->view_idx;
slice_param->idr_pic_id = feienc->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 */
if (slice_param->slice_type == GST_H264_B_SLICE)
slice_param->direct_spatial_mv_pred_flag = TRUE;
/* default equal to picture parameters */
slice_param->num_ref_idx_active_override_flag = TRUE;
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;
g_assert (slice_param->num_ref_idx_l0_active_minus1 >= 0);
g_assert (slice_param->num_ref_idx_l1_active_minus1 == 0);
i_ref = 0;
if (picture->type != GST_VAAPI_PICTURE_TYPE_I) {
for (; i_ref < reflist_0_count; ++i_ref) {
slice_param->RefPicList0[i_ref].picture_id =
GST_VAAPI_SURFACE_PROXY_SURFACE_ID (reflist_0[i_ref]->pic);
slice_param->RefPicList0[i_ref].TopFieldOrderCnt =
reflist_0[i_ref]->poc;
slice_param->RefPicList0[i_ref].flags |=
VA_PICTURE_H264_SHORT_TERM_REFERENCE;
slice_param->RefPicList0[i_ref].frame_idx = reflist_0[i_ref]->frame_num;
}
g_assert (i_ref >= 1);
}
for (; i_ref < G_N_ELEMENTS (slice_param->RefPicList0); ++i_ref) {
slice_param->RefPicList0[i_ref].picture_id = VA_INVALID_SURFACE;
slice_param->RefPicList0[i_ref].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;
}
g_assert (i_ref == 1);
}
for (; i_ref < G_N_ELEMENTS (slice_param->RefPicList1); ++i_ref) {
slice_param->RefPicList1[i_ref].picture_id = VA_INVALID_SURFACE;
slice_param->RefPicList1[i_ref].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 = feienc->init_qp - feienc->min_qp;
if (slice_param->slice_qp_delta > 4)
slice_param->slice_qp_delta = 4;
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;
g_array_append_val (h264_slice_params, *slice_param);
gst_vaapi_enc_picture_add_slice (picture, slice);
gst_vaapi_codec_object_replace (&slice, NULL);
}
g_assert (last_mb_index == mb_size);
info_to_pak->h264_slice_headers = h264_slice_params;
return TRUE;
}
/* Generates and submits SPS header accordingly into the bitstream */
static gboolean
ensure_sequence (GstVaapiFeiEncH264 * feienc, GstVaapiEncPicture * picture,
GstVaapiFeiInfoToPakH264 * info_to_pak)
{
GstVaapiEncSequence *sequence = NULL;
VAEncSequenceParameterBufferH264 *seq_param;
sequence = GST_VAAPI_ENC_SEQUENCE_NEW (H264, feienc);
if (!sequence || !fill_sequence (feienc, sequence))
goto error_create_seq_param;
seq_param = sequence->param;
info_to_pak->h264_enc_sps = *seq_param;
if (sequence) {
gst_vaapi_enc_picture_set_sequence (picture, sequence);
gst_vaapi_codec_object_replace (&sequence, NULL);
}
if (!feienc->is_mvc || feienc->view_idx > 0)
feienc->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;
}
}
/* Generates additional fei control parameters */
static gboolean
ensure_fei_misc_params (GstVaapiFeiEncH264 * feienc,
GstVaapiEncPicture * picture, GstVaapiCodedBufferProxy * codedbuf_proxy)
{
GstVaapiEncMiscParam *misc = NULL;
GstVaapiSurfaceProxy *surface_proxy = picture->proxy;
VAEncMiscParameterFEIFrameControlH264 *misc_fei_pic_control_param;
guint mbcode_size = 0;
guint mv_size = 0;
guint dist_size = 0;
/*fei pic control params */
misc = GST_VAAPI_ENC_FEI_MISC_PARAM_NEW (H264, feienc);
g_assert (misc);
if (!misc)
return FALSE;
misc_fei_pic_control_param = misc->data;
surface_proxy = picture->proxy;
misc_fei_pic_control_param->function = VA_FEI_FUNCTION_ENC;
misc_fei_pic_control_param->search_path = feienc->search_path;
misc_fei_pic_control_param->num_mv_predictors_l0 =
feienc->num_mv_predictors_l0;
misc_fei_pic_control_param->num_mv_predictors_l1 =
feienc->num_mv_predictors_l1;
misc_fei_pic_control_param->len_sp = feienc->len_sp;
misc_fei_pic_control_param->sub_mb_part_mask = feienc->submb_part_mask;
if (!feienc->use_dct8x8)
misc_fei_pic_control_param->intra_part_mask = feienc->intra_part_mask | 2;
misc_fei_pic_control_param->multi_pred_l0 = feienc->multi_predL0;
misc_fei_pic_control_param->multi_pred_l1 = feienc->multi_predL1;
misc_fei_pic_control_param->sub_pel_mode = feienc->subpel_mode;
misc_fei_pic_control_param->inter_sad = feienc->inter_sad;
misc_fei_pic_control_param->intra_sad = feienc->intra_sad;
misc_fei_pic_control_param->distortion_type = 0;
misc_fei_pic_control_param->repartition_check_enable = 0;
misc_fei_pic_control_param->adaptive_search = feienc->adaptive_search;
misc_fei_pic_control_param->mb_size_ctrl = 0;
misc_fei_pic_control_param->ref_width = feienc->ref_width;
misc_fei_pic_control_param->ref_height = feienc->ref_height;
misc_fei_pic_control_param->search_window = feienc->search_window;
/***** ENC input: mv_predictor *****/
if (surface_proxy->mvpred) {
misc_fei_pic_control_param->mv_predictor =
GST_VAAPI_FEI_CODEC_OBJECT (surface_proxy->mvpred)->param_id;
misc_fei_pic_control_param->mv_predictor_enable = TRUE;
gst_vaapi_codec_object_replace (&picture->mvpred, surface_proxy->mvpred);
} else {
misc_fei_pic_control_param->mv_predictor = VA_INVALID_ID;
misc_fei_pic_control_param->mv_predictor_enable = FALSE;
picture->mvpred = NULL;
}
/***** ENC input: qp ******/
if (surface_proxy->qp) {
misc_fei_pic_control_param->qp =
GST_VAAPI_FEI_CODEC_OBJECT (surface_proxy->qp)->param_id;
misc_fei_pic_control_param->mb_qp = TRUE;
gst_vaapi_codec_object_replace (&picture->qp, surface_proxy->qp);
} else {
misc_fei_pic_control_param->qp = VA_INVALID_ID;
misc_fei_pic_control_param->mb_qp = FALSE;
picture->qp = NULL;
}
/***** ENC input: mb_control ******/
if (surface_proxy->mbcntrl) {
misc_fei_pic_control_param->mb_ctrl =
GST_VAAPI_FEI_CODEC_OBJECT (surface_proxy->mbcntrl)->param_id;
misc_fei_pic_control_param->mb_input = TRUE;
gst_vaapi_codec_object_replace (&picture->mbcntrl, surface_proxy->mbcntrl);
} else {
misc_fei_pic_control_param->mb_ctrl = VA_INVALID_ID;
misc_fei_pic_control_param->mb_input = FALSE;
picture->mbcntrl = NULL;
}
mbcode_size = sizeof (VAEncFEIMBCodeH264) *
feienc->mb_width * feienc->mb_height;
mv_size = sizeof (VAMotionVector) * 16 * feienc->mb_width * feienc->mb_height;
dist_size = sizeof (VAEncFEIDistortionH264) *
feienc->mb_width * feienc->mb_height;
/***** ENC_PAK/ENC output: macroblock code buffer *****/
codedbuf_proxy->mbcode =
gst_vaapi_enc_fei_mb_code_new (GST_VAAPI_ENCODER_CAST (feienc),
NULL, mbcode_size);
misc_fei_pic_control_param->mb_code_data =
GST_VAAPI_FEI_CODEC_OBJECT (codedbuf_proxy->mbcode)->param_id;
picture->mbcode = gst_vaapi_codec_object_ref (codedbuf_proxy->mbcode);
/***** ENC_PAK/ENC output: motion vector buffer *****/
codedbuf_proxy->mv =
gst_vaapi_enc_fei_mv_new (GST_VAAPI_ENCODER_CAST (feienc), NULL, mv_size);
misc_fei_pic_control_param->mv_data =
GST_VAAPI_FEI_CODEC_OBJECT (codedbuf_proxy->mv)->param_id;
picture->mv = gst_vaapi_codec_object_ref (codedbuf_proxy->mv);
/* Fixme: a copy needed in coded_buf proxy */
/***** ENC_PAK/ENC output: distortion buffer *****/
picture->dist =
gst_vaapi_enc_fei_distortion_new (GST_VAAPI_ENCODER_CAST (feienc),
NULL, dist_size);
misc_fei_pic_control_param->distortion =
GST_VAAPI_FEI_CODEC_OBJECT (picture->dist)->param_id;
codedbuf_proxy->dist = gst_vaapi_codec_object_ref (picture->dist);
gst_vaapi_enc_picture_add_misc_param (picture, misc);
gst_vaapi_codec_object_replace (&misc, NULL);
return TRUE;
}
/* Generates additional control parameters */
static gboolean
ensure_misc_params (GstVaapiFeiEncH264 * feienc, GstVaapiEncPicture * picture)
{
GstVaapiEncMiscParam *misc = NULL;
VAEncMiscParameterRateControl *rate_control;
/* HRD params */
misc = GST_VAAPI_ENC_MISC_PARAM_NEW (HRD, feienc);
g_assert (misc);
if (!misc)
return FALSE;
fill_hrd_params (feienc, misc->data);
gst_vaapi_enc_picture_add_misc_param (picture, misc);
gst_vaapi_codec_object_replace (&misc, NULL);
/* RateControl params */
if (GST_VAAPI_ENCODER_RATE_CONTROL (feienc) == GST_VAAPI_RATECONTROL_CBR ||
GST_VAAPI_ENCODER_RATE_CONTROL (feienc) == GST_VAAPI_RATECONTROL_VBR) {
misc = GST_VAAPI_ENC_MISC_PARAM_NEW (RateControl, feienc);
g_assert (misc);
if (!misc)
return FALSE;
rate_control = misc->data;
memset (rate_control, 0, sizeof (VAEncMiscParameterRateControl));
rate_control->bits_per_second = feienc->bitrate_bits;
rate_control->target_percentage = 70;
rate_control->window_size = feienc->cpb_length;
rate_control->initial_qp = feienc->init_qp;
rate_control->min_qp = feienc->min_qp;
rate_control->basic_unit_size = 0;
gst_vaapi_enc_picture_add_misc_param (picture, misc);
gst_vaapi_codec_object_replace (&misc, NULL);
}
return TRUE;
}
/* Generates and submits PPS header accordingly into the bitstream */
static gboolean
ensure_picture (GstVaapiFeiEncH264 * feienc, GstVaapiEncPicture * picture,
GstVaapiSurfaceProxy * surface, GstVaapiCodedBufferProxy * codedbuf_proxy,
GstVaapiFeiInfoToPakH264 * info_to_pak)
{
gboolean res = FALSE;
GstVaapiCodedBuffer *const codedbuf =
GST_VAAPI_CODED_BUFFER_PROXY_BUFFER (codedbuf_proxy);
VAEncPictureParameterBufferH264 *const pic_param = picture->param;
res = fill_picture (feienc, picture, surface, codedbuf);
if (!res)
return FALSE;
info_to_pak->h264_enc_pps = *pic_param;
return TRUE;
}
/* Generates slice headers */
static gboolean
ensure_slices (GstVaapiFeiEncH264 * feienc, GstVaapiEncPicture * picture,
GstVaapiFeiInfoToPakH264 * info_to_pak)
{
GstVaapiFeiEncH264Ref *reflist_0[16];
GstVaapiFeiEncH264Ref *reflist_1[16];
GstVaapiH264ViewRefPool *const ref_pool =
&feienc->ref_pools[feienc->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 (feienc, 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 (feienc, picture,
reflist_0, reflist_0_count, reflist_1, reflist_1_count, info_to_pak))
return FALSE;
return TRUE;
}
/* Normalizes bitrate (and CPB size) for HRD conformance */
static void
ensure_bitrate_hrd (GstVaapiFeiEncH264 * feienc)
{
GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (feienc);
guint bitrate, cpb_size;
if (!base_encoder->bitrate) {
feienc->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 != feienc->bitrate_bits) {
GST_DEBUG ("HRD bitrate: %u bits/sec", bitrate);
feienc->bitrate_bits = bitrate;
feienc->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, feienc->cpb_length, 1000) &
~((1U << SX_CPB_SIZE) - 1);
if (cpb_size != feienc->cpb_length_bits) {
GST_DEBUG ("HRD CPB size: %u bits", cpb_size);
feienc->cpb_length_bits = cpb_size;
feienc->config_changed = TRUE;
}
}
/* Estimates a good enough bitrate if none was supplied */
static void
ensure_bitrate (GstVaapiFeiEncH264 * feienc)
{
GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (feienc);
/* Default compression: 48 bits per macroblock in "high-compression" mode */
switch (GST_VAAPI_ENCODER_RATE_CONTROL (feienc)) {
case GST_VAAPI_RATECONTROL_CBR:
case GST_VAAPI_RATECONTROL_VBR:
case GST_VAAPI_RATECONTROL_VBR_CONSTRAINED:
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;
if (!feienc->use_cabac)
bits_per_mb += (bits_per_mb * 15) / 100;
if (!feienc->use_dct8x8)
bits_per_mb += (bits_per_mb * 10) / 100;
base_encoder->bitrate =
feienc->mb_width * feienc->mb_height * bits_per_mb *
GST_VAAPI_ENCODER_FPS_N (feienc) /
GST_VAAPI_ENCODER_FPS_D (feienc) / 1000;
GST_INFO ("target bitrate computed to %u kbps", base_encoder->bitrate);
}
break;
default:
base_encoder->bitrate = 0;
break;
}
ensure_bitrate_hrd (feienc);
}
/* Constructs profile and level information based on user-defined limits */
static GstVaapiEncoderStatus
ensure_profile_and_level (GstVaapiFeiEncH264 * feienc)
{
const GstVaapiProfile profile = feienc->profile;
const GstVaapiLevelH264 level = feienc->level;
if (!ensure_tuning (feienc))
GST_WARNING ("Failed to set some of the tuning option as expected! ");
if (!ensure_profile (feienc) || !ensure_profile_limits (feienc))
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
/* Check HW constraints */
if (!ensure_hw_profile_limits (feienc))
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
if (feienc->profile_idc > feienc->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 (feienc);
if (!ensure_level (feienc))
return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
if (feienc->profile != profile || feienc->level != level) {
GST_DEBUG ("selected %s profile at level %s",
gst_vaapi_utils_h264_get_profile_string (feienc->profile),
gst_vaapi_utils_h264_get_level_string (feienc->level));
feienc->config_changed = TRUE;
}
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
static void
reset_properties (GstVaapiFeiEncH264 * feienc)
{
GstVaapiEncoder *const base_encoder = GST_VAAPI_ENCODER_CAST (feienc);
guint mb_size, i;
guint max_reflist0_count;
if (feienc->idr_period < base_encoder->keyframe_period)
feienc->idr_period = base_encoder->keyframe_period;
if (feienc->min_qp > feienc->init_qp ||
(GST_VAAPI_ENCODER_RATE_CONTROL (feienc) == GST_VAAPI_RATECONTROL_CQP &&
feienc->min_qp < feienc->init_qp))
feienc->min_qp = feienc->init_qp;
mb_size = feienc->mb_width * feienc->mb_height;
if (feienc->num_slices > (mb_size + 1) / 2)
feienc->num_slices = (mb_size + 1) / 2;
g_assert (feienc->num_slices);
if (feienc->num_bframes > (base_encoder->keyframe_period + 1) / 2)
feienc->num_bframes = (base_encoder->keyframe_period + 1) / 2;
/* Workaround : vaapi-intel-driver doesn't have support for
* B-frame encode when utilizing low-power encode hardware block.
* So Disabling b-frame encoding in low-pwer encode.
*
* Fixme :We should query the VAConfigAttribEncMaxRefFrames
* instead of blindly disabling b-frame support and set b/p frame count,
* buffer pool size etc based on that.*/
if ((feienc->num_bframes > 0)
&& (feienc->entrypoint == GST_VAAPI_ENTRYPOINT_SLICE_ENCODE_LP)) {
GST_WARNING
("Disabling b-frame since the driver doesn't supporting it in low-power encode");
feienc->num_bframes = 0;
}
if (feienc->num_bframes > 0 && GST_VAAPI_ENCODER_FPS_N (feienc) > 0)
feienc->cts_offset = gst_util_uint64_scale (GST_SECOND,
GST_VAAPI_ENCODER_FPS_D (feienc), GST_VAAPI_ENCODER_FPS_N (feienc));
else
feienc->cts_offset = 0;
/* init max_frame_num, max_poc */
feienc->log2_max_frame_num = h264_get_log2_max_frame_num (feienc->idr_period);
g_assert (feienc->log2_max_frame_num >= 4);
feienc->max_frame_num = (1 << feienc->log2_max_frame_num);
feienc->log2_max_pic_order_cnt = feienc->log2_max_frame_num + 1;
feienc->max_pic_order_cnt = (1 << feienc->log2_max_pic_order_cnt);
feienc->idr_num = 0;
if (feienc->num_bframes > 0) {
if (feienc->num_ref_frames == 1) {
GST_INFO ("num ref frames is modified as 2 as b frame is set");
feienc->num_ref_frames = 2;
}
max_reflist0_count = feienc->num_ref_frames - 1;
} else {
max_reflist0_count = feienc->num_ref_frames;
}
max_reflist0_count = max_reflist0_count > 5 ? 5 : max_reflist0_count;
for (i = 0; i < feienc->num_views; i++) {
GstVaapiH264ViewRefPool *const ref_pool = &feienc->ref_pools[i];
GstVaapiH264ViewReorderPool *const reorder_pool = &feienc->reorder_pools[i];
ref_pool->max_reflist0_count = max_reflist0_count;
ref_pool->max_reflist1_count = feienc->num_bframes > 0;
ref_pool->max_ref_frames = ref_pool->max_reflist0_count
+ ref_pool->max_reflist1_count;
reorder_pool->frame_index = 0;
}
}
/* only for vaapi encoder framework checking */
static GstVaapiEncoderStatus
gst_vaapi_feienc_h264_fake_encode (GstVaapiEncoder * base_encoder,
GstVaapiEncPicture * picture, GstVaapiCodedBufferProxy * codedbuf)
{
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
GstVaapiEncoderStatus
gst_vaapi_feienc_h264_encode (GstVaapiEncoder * base_encoder,
GstVaapiEncPicture * picture, GstVaapiSurfaceProxy * reconstruct,
GstVaapiCodedBufferProxy * codedbuf_proxy,
GstVaapiFeiInfoToPakH264 * info_to_pak)
{
GstVaapiFeiEncH264 *const feienc = GST_VAAPI_FEI_H264_ENC_CAST (base_encoder);
GstVaapiEncoderStatus ret = GST_VAAPI_ENCODER_STATUS_ERROR_UNKNOWN;
if (!reconstruct || !codedbuf_proxy)
return ret;
if (!ensure_sequence (feienc, picture, info_to_pak))
goto error;
if (!ensure_misc_params (feienc, picture))
goto error;
if (!ensure_fei_misc_params (feienc, picture, codedbuf_proxy))
goto error;
if (!ensure_picture (feienc, picture, reconstruct, codedbuf_proxy,
info_to_pak))
goto error;
if (!ensure_slices (feienc, picture, info_to_pak))
goto error;
if (!gst_vaapi_enc_picture_encode (picture))
goto error;
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
error:
g_slice_free (GstVaapiFeiInfoToPakH264, info_to_pak);
return ret;
}
GstVaapiEncoderStatus
gst_vaapi_feienc_h264_flush (GstVaapiEncoder * base_encoder)
{
GstVaapiFeiEncH264 *const feienc = GST_VAAPI_FEI_H264_ENC_CAST (base_encoder);
GstVaapiH264ViewReorderPool *reorder_pool;
GstVaapiEncPicture *pic;
guint i;
for (i = 0; i < feienc->num_views; i++) {
reorder_pool = &feienc->reorder_pools[i];
reorder_pool->frame_index = 0;
reorder_pool->cur_frame_num = 0;
reorder_pool->cur_present_index = 0;
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_feienc_h264_get_codec_data (GstVaapiEncoder * base_encoder,
GstBuffer ** out_buffer_ptr)
{
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
GstVaapiEncoderStatus
gst_vaapi_feienc_h264_reordering (GstVaapiEncoder * base_encoder,
GstVideoCodecFrame * frame, GstVaapiEncPicture ** output)
{
GstVaapiFeiEncH264 *const feienc = GST_VAAPI_FEI_H264_ENC_CAST (base_encoder);
GstVaapiH264ViewReorderPool *reorder_pool = NULL;
GstVaapiEncPicture *picture;
gboolean is_idr = FALSE;
*output = NULL;
/* encoding views alternatively for MVC */
if (feienc->is_mvc) {
/* FIXME: Use first-in-bundle flag on buffers to reset view idx? */
if (frame)
feienc->view_idx = frame->system_frame_number % feienc->num_views;
else
feienc->view_idx = (feienc->view_idx + 1) % feienc->num_views;
}
reorder_pool = &feienc->reorder_pools[feienc->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 (feienc->num_bframes > 0);
g_return_val_if_fail (!g_queue_is_empty (&reorder_pool->reorder_frame_list),
GST_VAAPI_ENCODER_STATUS_ERROR_UNKNOWN);
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, feienc, 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) %
feienc->max_pic_order_cnt);
is_idr = (reorder_pool->frame_index == 0 ||
reorder_pool->frame_index >= feienc->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 (feienc)) == 0) {
++reorder_pool->cur_frame_num;
++reorder_pool->frame_index;
/* b frame enabled, check queue of reorder_frame_list */
if (feienc->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, feienc);
g_queue_foreach (&reorder_pool->reorder_frame_list,
(GFunc) set_b_frame, feienc);
++reorder_pool->cur_frame_num;
set_key_frame (picture, feienc, is_idr);
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, feienc, is_idr);
g_assert (g_queue_is_empty (&reorder_pool->reorder_frame_list));
if (feienc->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) <
feienc->num_bframes) {
g_queue_push_tail (&reorder_pool->reorder_frame_list, picture);
return GST_VAAPI_ENCODER_STATUS_NO_SURFACE;
}
++reorder_pool->cur_frame_num;
set_p_frame (picture, feienc);
if (reorder_pool->reorder_state == GST_VAAPI_ENC_H264_REORD_WAIT_FRAMES) {
g_queue_foreach (&reorder_pool->reorder_frame_list, (GFunc) set_b_frame,
feienc);
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 += feienc->cts_offset;
*output = picture;
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
static GstVaapiEncoderStatus
set_context_info (GstVaapiEncoder * base_encoder)
{
GstVaapiFeiEncH264 *const feienc = GST_VAAPI_FEI_H264_ENC_CAST (base_encoder);
GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (feienc);
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 (feienc))
return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
base_encoder->num_ref_frames =
(feienc->num_ref_frames + DEFAULT_SURFACES_COUNT) * feienc->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 += feienc->num_slices * (4 +
GST_ROUND_UP_8 (MAX_SLICE_HDR_SIZE) / 8);
base_encoder->context_info.entrypoint = feienc->entrypoint;
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
GstVaapiEncoderStatus
gst_vaapi_feienc_h264_reconfigure (GstVaapiEncoder * base_encoder)
{
GstVaapiFeiEncH264 *const feienc = GST_VAAPI_FEI_H264_ENC_CAST (base_encoder);
GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (feienc);
GstVaapiEncoderStatus status;
guint mb_width, mb_height;
mb_width = (GST_VAAPI_ENCODER_WIDTH (feienc) + 15) / 16;
mb_height = (GST_VAAPI_ENCODER_HEIGHT (feienc) + 15) / 16;
if (mb_width != feienc->mb_width || mb_height != feienc->mb_height) {
GST_DEBUG ("resolution: %dx%d", GST_VAAPI_ENCODER_WIDTH (feienc),
GST_VAAPI_ENCODER_HEIGHT (feienc));
feienc->mb_width = mb_width;
feienc->mb_height = mb_height;
feienc->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)
feienc->num_views = GST_VIDEO_INFO_VIEWS (vip);
feienc->is_mvc = feienc->num_views > 1;
status = ensure_profile_and_level (feienc);
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
return status;
reset_properties (feienc);
status = set_context_info (base_encoder);
if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
return status;
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
static gboolean
gst_vaapi_feienc_h264_init (GstVaapiEncoder * base_encoder)
{
GstVaapiFeiEncH264 *const feienc = GST_VAAPI_FEI_H264_ENC_CAST (base_encoder);
guint32 i;
/* Default encoding entrypoint */
feienc->entrypoint = GST_VAAPI_ENTRYPOINT_SLICE_ENCODE;
feienc->search_path = GST_VAAPI_FEI_H264_SEARCH_PATH_DEFAULT;
feienc->len_sp = GST_VAAPI_FEI_H264_SEARCH_PATH_LENGTH_DEFAULT;
feienc->ref_width = GST_VAAPI_FEI_H264_REF_WIDTH_DEFAULT;
feienc->ref_height = GST_VAAPI_FEI_H264_REF_HEIGHT_DEFAULT;
feienc->intra_part_mask = GST_VAAPI_FEI_H264_INTRA_PART_MASK_DEFAULT;
feienc->submb_part_mask = GST_VAAPI_FEI_H264_SUB_MB_PART_MASK_DEFAULT;
/* Multi-view coding information */
feienc->is_mvc = FALSE;
feienc->num_views = 1;
feienc->view_idx = 0;
/* default num ref frames */
feienc->num_ref_frames = 1;
memset (feienc->view_ids, 0, sizeof (feienc->view_ids));
feienc->entrypoint = GST_VAAPI_ENTRYPOINT_SLICE_ENCODE_FEI;
/* re-ordering list initialize */
for (i = 0; i < MAX_NUM_VIEWS; i++) {
GstVaapiH264ViewReorderPool *const reorder_pool = &feienc->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;
}
return TRUE;
}
static void
gst_vaapi_feienc_h264_finalize (GstVaapiEncoder * base_encoder)
{
/*free private buffers */
GstVaapiFeiEncH264 *const feienc = GST_VAAPI_FEI_H264_ENC_CAST (base_encoder);
GstVaapiEncPicture *pic;
guint32 i;
gst_buffer_replace (&feienc->sps_data, NULL);
gst_buffer_replace (&feienc->subset_sps_data, NULL);
gst_buffer_replace (&feienc->pps_data, NULL);
/* re-ordering list initialize */
for (i = 0; i < MAX_NUM_VIEWS; i++) {
GstVaapiH264ViewReorderPool *const reorder_pool = &feienc->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);
}
}
GstVaapiEncoderStatus
gst_vaapi_feienc_h264_set_property (GstVaapiEncoder * base_encoder,
gint prop_id, const GValue * value)
{
GstVaapiFeiEncH264 *const feienc = GST_VAAPI_FEI_H264_ENC_CAST (base_encoder);
switch (prop_id) {
case GST_VAAPI_FEI_H264_ENC_PROP_MAX_BFRAMES:
feienc->num_bframes = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_INIT_QP:
feienc->init_qp = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_MIN_QP:
feienc->min_qp = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_NUM_SLICES:
feienc->num_slices = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_CABAC:
feienc->use_cabac = g_value_get_boolean (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_DCT8X8:
feienc->use_dct8x8 = g_value_get_boolean (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_CPB_LENGTH:
feienc->cpb_length = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_NUM_VIEWS:
feienc->num_views = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_VIEW_IDS:{
guint i;
GValueArray *view_ids = g_value_get_boxed (value);
if (view_ids == NULL) {
for (i = 0; i < feienc->num_views; i++)
feienc->view_ids[i] = i;
} else {
g_assert (view_ids->n_values <= feienc->num_views);
for (i = 0; i < feienc->num_views; i++) {
GValue *val = g_value_array_get_nth (view_ids, i);
feienc->view_ids[i] = g_value_get_uint (val);
}
}
break;
}
case GST_VAAPI_FEI_H264_ENC_PROP_NUM_REF:
feienc->num_ref_frames = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_NUM_MV_PREDICT_L0:
feienc->num_mv_predictors_l0 = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_NUM_MV_PREDICT_L1:
feienc->num_mv_predictors_l1 = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_SEARCH_WINDOW:
feienc->search_window = g_value_get_enum (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_LEN_SP:
feienc->len_sp = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_SEARCH_PATH:
feienc->search_path = g_value_get_enum (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_REF_WIDTH:
feienc->ref_width = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_REF_HEIGHT:
feienc->ref_height = g_value_get_uint (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_SUBMB_MASK:
feienc->submb_part_mask = g_value_get_flags (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_SUBPEL_MODE:
feienc->subpel_mode = g_value_get_enum (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_INTRA_PART_MASK:
feienc->intra_part_mask = g_value_get_flags (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_INTRA_SAD:
feienc->intra_sad = g_value_get_enum (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_INTER_SAD:
feienc->inter_sad = g_value_get_enum (value);
break;
case GST_VAAPI_FEI_H264_ENC_PROP_ADAPT_SEARCH:
feienc->adaptive_search = g_value_get_boolean (value) ? 1 : 0;
break;
case GST_VAAPI_FEI_H264_ENC_PROP_MULTI_PRED_L0:
feienc->multi_predL0 = g_value_get_boolean (value) ? 1 : 0;
break;
case GST_VAAPI_FEI_H264_ENC_PROP_MULTI_PRED_L1:
feienc->multi_predL1 = g_value_get_boolean (value) ? 1 : 0;
break;
default:
return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
}
return GST_VAAPI_ENCODER_STATUS_SUCCESS;
}
static const GstVaapiEncoderClassData fei_enc_class_data = {
.codec = GST_VAAPI_CODEC_H264,
.packed_headers = SUPPORTED_PACKED_HEADERS,
.rate_control_get_type = gst_vaapi_rate_control_get_type,
.default_rate_control = DEFAULT_RATECONTROL,
.rate_control_mask = SUPPORTED_RATECONTROLS,
.encoder_tune_get_type = gst_vaapi_encoder_tune_get_type,
.default_encoder_tune = GST_VAAPI_ENCODER_TUNE_NONE,
.encoder_tune_mask = SUPPORTED_TUNE_OPTIONS,
};
static inline const GstVaapiEncoderClass *
gst_vaapi_feienc_h264_class (void)
{
static const GstVaapiEncoderClass GstVaapiFeiEncH264Class = {
.parent_class = {
.size = sizeof (GstVaapiFeiEncH264),
.finalize = (GDestroyNotify) gst_vaapi_encoder_finalize,
}
,
.class_data = &fei_enc_class_data,
.init = gst_vaapi_feienc_h264_init,
.finalize = gst_vaapi_feienc_h264_finalize,
.reconfigure = gst_vaapi_feienc_h264_reconfigure,
.get_default_properties = gst_vaapi_feienc_h264_get_default_properties,
.reordering = gst_vaapi_feienc_h264_reordering,
.encode = gst_vaapi_feienc_h264_fake_encode,
.flush = gst_vaapi_feienc_h264_flush,
.set_property = gst_vaapi_feienc_h264_set_property,
.get_codec_data = gst_vaapi_feienc_h264_get_codec_data,
};
return &GstVaapiFeiEncH264Class;
}
/**
* gst_vaapi_feienc_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_feienc_h264_new (GstVaapiDisplay * display)
{
return gst_vaapi_encoder_new (gst_vaapi_feienc_h264_class (), display);
}
/**
* gst_vaapi_feienc_h264_get_fei_properties:
*
* Determines the set of common and H.264 Fei specific feienc properties.
* The caller owns an extra reference to the resulting array of
* #GstVaapiEncoderPropInfo elements, so it shall be released with
* g_ptr_array_unref() after usage.
*
* Return value: the set of feienc properties for #GstVaapiFeiEncH264,
* or %NULL if an error occurred.
*/
static GPtrArray *
gst_vaapi_feienc_h264_get_fei_properties (GPtrArray * props)
{
/**
* GstVaapiFeiEncH264:num_mv_predictors_l0:
*
* The number of mv predict
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_NUM_MV_PREDICT_L0,
g_param_spec_uint ("num-mvpredict-l0",
"Num mv predict l0",
"Indicate how many predictors should be used for l0",
0, 3, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:num_mv_predictors_l1:
*
* The number of mv predict
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_NUM_MV_PREDICT_L1,
g_param_spec_uint ("num-mvpredict-l1",
"Num mv predict l1",
"Indicate how many predictors should be used for l1",
0, 3, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:search-window:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_SEARCH_WINDOW,
g_param_spec_enum ("search-window",
"search window",
"Specify one of the predefined search path",
GST_VAAPI_TYPE_FEI_H264_SEARCH_WINDOW,
GST_VAAPI_FEI_H264_SEARCH_WINDOW_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:len-sp:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_LEN_SP,
g_param_spec_uint ("len-sp",
"len sp",
"This value defines number of search units in search path",
1, 63, 32, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:search-path:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_SEARCH_PATH,
g_param_spec_enum ("search-path",
"search path",
"Specify search path",
GST_VAAPI_TYPE_FEI_H264_SEARCH_PATH,
GST_VAAPI_FEI_H264_SEARCH_PATH_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:ref-width:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_REF_WIDTH,
g_param_spec_uint ("ref-width",
"ref width",
"Width of search region in pixel, must be multiple of 4",
4, 64, 32, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:ref-height:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_REF_HEIGHT,
g_param_spec_uint ("ref-height",
"ref height",
"Height of search region in pixel, must be multiple of 4",
4, 32, 32, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:submb-mask:
* Defines the bit-mask for disabling sub-partition
*
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_SUBMB_MASK,
g_param_spec_flags ("submbpart-mask",
"submb part mask",
"defines the bit-mask for disabling sub mb partition",
GST_VAAPI_TYPE_FEI_H264_SUB_MB_PART_MASK,
GST_VAAPI_FEI_H264_SUB_MB_PART_MASK_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:subpel-mode:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_SUBPEL_MODE,
g_param_spec_enum ("subpel-mode",
"subpel mode",
"Sub pixel precision for motion estimation",
GST_VAAPI_TYPE_FEI_H264_SUB_PEL_MODE,
GST_VAAPI_FEI_H264_SUB_PEL_MODE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:intrapart-mask:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_INTRA_PART_MASK,
g_param_spec_flags ("intrapart-mask",
"intra part mask",
"What block and sub-block partitions are disabled for intra MBs",
GST_VAAPI_TYPE_FEI_H264_INTRA_PART_MASK,
GST_VAAPI_FEI_H264_INTRA_PART_MASK_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:intra-sad:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_INTRA_SAD,
g_param_spec_enum ("intra-sad",
"intra sad",
"Specifies distortion measure adjustments used in the motion search SAD comparison for intra MB",
GST_VAAPI_TYPE_FEI_H264_SAD_MODE, GST_VAAPI_FEI_H264_SAD_MODE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:inter-sad:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_INTER_SAD,
g_param_spec_enum ("inter-sad",
"inter sad",
"Specifies distortion measure adjustments used in the motion search SAD comparison for inter MB",
GST_VAAPI_TYPE_FEI_H264_SAD_MODE, GST_VAAPI_FEI_H264_SAD_MODE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:adaptive-search:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_ADAPT_SEARCH,
g_param_spec_boolean ("adaptive-search",
"adaptive-search",
"Enable adaptive search",
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:multi-predL0:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_MULTI_PRED_L0,
g_param_spec_boolean ("multi-predL0",
"multi predL0",
"Enable multi prediction for ref L0 list",
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:multi-predL0:
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_MULTI_PRED_L1,
g_param_spec_boolean ("multi-predL1",
"multi predL1",
"Enable multi prediction for ref L1 list",
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
return props;
}
/**
* gst_vaapi_feienc_h264_get_default_properties:
*
* Determines the set of common and H.264 specific feienc properties.
* The caller owns an extra reference to the resulting array of
* #GstVaapiEncoderPropInfo elements, so it shall be released with
* g_ptr_array_unref() after usage.
*
* Return value: the set of feienc properties for #GstVaapiFeiEncH264,
* or %NULL if an error occurred.
*/
GPtrArray *
gst_vaapi_feienc_h264_get_default_properties (void)
{
const GstVaapiEncoderClass *const klass = gst_vaapi_feienc_h264_class ();
GPtrArray *props;
props = gst_vaapi_encoder_properties_get_default (klass);
if (!props)
return NULL;
/**
* GstVaapiFeiEncH264:max-bframes:
*
* The number of B-frames between I and P.
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_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));
/**
* GstVaapiFeiEncH264:init-qp:
*
* The initial quantizer value.
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_INIT_QP,
g_param_spec_uint ("init-qp",
"Initial QP", "Initial quantizer value", 1, 51, 26,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:min-qp:
*
* The minimum quantizer value.
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_MIN_QP,
g_param_spec_uint ("min-qp",
"Minimum QP", "Minimum quantizer value", 1, 51, 1,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264:num-slices:
*
* The number of slices per frame.
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_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));
/**
* GstVaapiFeiEncH264: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.
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_CABAC,
g_param_spec_boolean ("cabac",
"Enable CABAC",
"Enable CABAC entropy coding mode",
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstVaapiFeiEncH264: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.
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_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));
/**
* GstVaapiFeiEncH264:cpb-length:
*
* The size of the CPB buffer in milliseconds.
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_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));
/**
* GstVaapiFeiEncH264:num-views:
*
* The number of views for MVC encoding .
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_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));
/**
* GstVaapiFeiEncH264:view-ids:
*
* The view ids for MVC encoding .
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_VIEW_IDS,
g_param_spec_value_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));
/**
* GstVaapiFeiEncH264:num-ref:
*
* The number of reference frames.
*/
GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
GST_VAAPI_FEI_H264_ENC_PROP_NUM_REF,
g_param_spec_uint ("num-ref",
"Num Ref",
"reference frame number",
1, 6, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
props = gst_vaapi_feienc_h264_get_fei_properties (props);
return props;
}
/**
* gst_vaapi_feienc_h264_set_max_profile:
* @feienc: a #GstVaapiFeiEncH264
* @profile: an H.264 #GstVaapiProfile
*
* Notifies the @feienc 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 @feienc is configured.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_feienc_h264_set_max_profile (GstVaapiFeiEncH264 * feienc,
GstVaapiProfile profile)
{
guint8 profile_idc;
g_return_val_if_fail (feienc != 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;
feienc->max_profile_idc = profile_idc;
return TRUE;
}
gboolean
gst_vaapi_feienc_h264_set_ref_pool (GstVaapiFeiEncH264 * feienc,
gpointer ref_pool_ptr)
{
g_return_val_if_fail (feienc != NULL, FALSE);
if (!ref_pool_ptr)
return FALSE;
memcpy (&feienc->ref_pools[0], ref_pool_ptr,
sizeof (GstVaapiH264ViewRefPool) * MAX_NUM_VIEWS);
return TRUE;
}
/**
* gst_vaapi_feienc_h264_get_profile_and_level
* @feienc: a #GstVaapiFeiEncH264
* @out_profile_ptr: return location for the #GstVaapiProfile
* @out_profile_idc_ptr: return location for the #GstVaapiLevelH264
*
* Queries the H.264 @feienc for the active profile and level. That
* information is only constructed and valid after the feienc is
* configured, i.e. after the gst_vaapi_feienc_set_codec_state()
* function is called.
*
* Return value: %TRUE on success
*/
gboolean
gst_vaapi_feienc_h264_get_profile_and_idc (GstVaapiFeiEncH264 * feienc,
GstVaapiProfile * out_profile_ptr, guint8 * out_profile_idc_ptr)
{
g_return_val_if_fail (feienc != NULL, FALSE);
if (!feienc->profile || !feienc->profile_idc)
return FALSE;
if (out_profile_ptr)
*out_profile_ptr = feienc->profile;
if (out_profile_idc_ptr)
*out_profile_idc_ptr = feienc->profile_idc;
return TRUE;
}