/*
* Copyright (C) 2014-2016 Intel Corporation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "vaapiencoder_hevc.h"
#include <assert.h>
#include "codecparsers/bitWriter.h"
#include "common/scopedlogger.h"
#include "common/common_def.h"
#include "common/Functional.h"
#include "vaapi/vaapicontext.h"
#include "vaapi/vaapidisplay.h"
#include "vaapicodedbuffer.h"
#include "vaapiencpicture.h"
#include <algorithm>
namespace YamiMediaCodec{
//shortcuts
typedef VaapiEncoderHEVC::PicturePtr PicturePtr;
typedef VaapiEncoderHEVC::ReferencePtr ReferencePtr;
typedef VaapiEncoderHEVC::StreamHeaderPtr StreamHeaderPtr;
using YamiParser::BitWriter;
using std::list;
using std::vector;
using std::deque;
#define HEVC_NAL_START_CODE 0x000001
#define HEVC_SLICE_TYPE_I 2
#define HEVC_SLICE_TYPE_P 1
#define HEVC_SLICE_TYPE_B 0
#define VAAPI_ENCODER_HEVC_NAL_REF_IDC_NONE 0
#define VAAPI_ENCODER_HEVC_NAL_REF_IDC_LOW 1
#define VAAPI_ENCODER_HEVC_NAL_REF_IDC_MEDIUM 2
#define VAAPI_ENCODER_HEVC_NAL_REF_IDC_HIGH 3
typedef enum
{
TRAIL_N = 0,
TRAIL_R = 1,
TSA_N = 2,
TSA_R = 3 ,
STSA_N = 4,
STSA_R = 5,
RADL_N = 6,
RADL_R = 7,
RASL_N = 8,
RASL_R = 9,
RSV_VCL_N10 = 10,
RSV_VCL_N11 = 11,
RSV_VCL_N12 = 12,
RSV_VCL_N13 = 13,
RSV_VCL_N14 = 14,
RSV_VCL_N15 = 15,
BLA_W_LP = 16,
BLA_W_RADL = 17,
BLA_N_LP = 18,
IDR_W_RADL = 19,
IDR_N_LP = 20,
CRA_NUT = 21,
RSV_IRAP_VCL22 = 22,
RSV_IRAP_VCL23 = 23,
RSV_VCL24 = 24,
RSV_VCL25 = 25,
RSV_VCL26 = 26,
RSV_VCL27 = 27,
RSV_VCL28 = 28,
RSV_VCL29 = 29,
RSV_VCL30 = 30,
RSV_VCL31 = 31,
VPS_NUT = 32,
SPS_NUT = 33,
PPS_NUT = 34,
AUD_NUT = 35,
EOS_NUT = 36,
EOB_NUT = 37,
FD_NUT = 38,
PREFIX_SEI_NUT = 39,
SUFFIX_SEI_NUT = 40
}HevcNalUnitType;
static uint8_t
hevc_get_slice_type (VaapiPictureType type)
{
switch (type) {
case VAAPI_PICTURE_I:
DEBUG("HEVC_SLICE_TYPE_I \n");
return HEVC_SLICE_TYPE_I;
case VAAPI_PICTURE_P:
DEBUG("HEVC_SLICE_TYPE_P \n");
return HEVC_SLICE_TYPE_P;
case VAAPI_PICTURE_B:
DEBUG("HEVC_SLICE_TYPE_B \n");
return HEVC_SLICE_TYPE_B;
default:
return -1;
}
return -1;
}
static uint32_t log2 (uint32_t num)
{
uint32_t ret = 0;
assert(num);
uint32_t one = 1;
while (num > (one << ret))
++ret;
return ret;
}
static uint32_t
hevc_get_log2_max_frame_num (uint32_t num)
{
uint32_t ret = 0;
ret = log2(num);
if (ret <= 4)
ret = 4;
else if (ret > 10)
ret = 10;
/* must be greater than 4 */
return ret;
}
static uint8_t hevc_get_profile_idc(VideoProfile profile)
{
uint8_t idc;
switch (profile) {
case PROFILE_H265_MAIN:
idc = 1;
break;
case PROFILE_H265_MAIN10:
idc = 2;
break;
default:
assert(0);
}
return idc;
}
static BOOL
bit_writer_put_ue(BitWriter *bitwriter, uint32_t value)
{
uint32_t size_in_bits = 0;
uint32_t tmp_value = ++value;
while (tmp_value) {
++size_in_bits;
tmp_value >>= 1;
}
if (size_in_bits > 1
&& !bitwriter->writeBits(0, size_in_bits - 1))
return FALSE;
if (!bitwriter->writeBits(value, size_in_bits))
return FALSE;
return TRUE;
}
static BOOL
bit_writer_put_se(BitWriter *bitwriter, int32_t value)
{
uint32_t new_val;
if (value <= 0)
new_val = -(value<<1);
else
new_val = (value<<1) - 1;
if (!bit_writer_put_ue(bitwriter, new_val))
return FALSE;
return TRUE;
}
static BOOL
bit_writer_write_nal_header(
BitWriter *bitwriter,
uint32_t nal_unit_type
)
{
/* forbidden_zero_bit */
bitwriter->writeBits(0, 1);
/* nal unit_type */
bitwriter->writeBits(nal_unit_type, 6);
/* layer_id */
bitwriter->writeBits(0, 6);
/* temporal_id_plus1*/
bitwriter->writeBits(1, 3);
return TRUE;
}
static BOOL
bit_writer_write_trailing_bits(BitWriter *bitwriter)
{
bitwriter->writeBits(1, 1);
bitwriter->writeToBytesAligned();
return TRUE;
}
static void profile_tier_level(
BitWriter *bitwriter,
BOOL profile_present_flag,
uint32_t max_num_sub_layers,
const VAEncSequenceParameterBufferHEVC* const seq
)
{
uint32_t i;
bool general_profile_compatibility_flag[32] = {0};
unsigned char vps_general_level_idc = seq->general_level_idc * 3;
if (profile_present_flag) {
/* general_profile_space */
bitwriter->writeBits(0, 2);
/* general_tier_flag */
bitwriter->writeBits(seq->general_tier_flag, 1);
/* general_profile_idc */
bitwriter->writeBits(seq->general_profile_idc, 5);
/* general_profile_compatibility_flag. Only the bit corresponding to profile_idc is set */
general_profile_compatibility_flag[seq->general_profile_idc] = 1;
for (i = 0; i < N_ELEMENTS(general_profile_compatibility_flag); i++) {
bitwriter->writeBits(general_profile_compatibility_flag[i], 1);
}
/* general_progressive_source_flag */
bitwriter->writeBits(1, 1);
/* general_interlaced_source_flag */
bitwriter->writeBits(0, 1);
/* general_non_packed_constraint_flag */
bitwriter->writeBits(0, 1);
/* general_frame_only_constraint_flag */
bitwriter->writeBits(1, 1);
/* reserved zero 44bits */
bitwriter->writeBits(0, 12);
bitwriter->writeBits(0, 16);
bitwriter->writeBits(0, 16);
}
/* general_level_idc */
bitwriter->writeBits(vps_general_level_idc, 8);
if (max_num_sub_layers) {
/* TBD: can't support multi-layers now
*/
}
}
void st_ref_pic_set(BitWriter *bs, int stRpsIdx, const ShortRFS& shortRFS)
{
int i;
if (stRpsIdx)
bs->writeBits(shortRFS.inter_ref_pic_set_prediction_flag, 1);
ASSERT(!shortRFS.inter_ref_pic_set_prediction_flag);
bit_writer_put_ue(bs, shortRFS.num_negative_pics);
bit_writer_put_ue(bs, shortRFS.num_positive_pics);
for (i = 0; i < shortRFS.num_negative_pics; i++)
{
bit_writer_put_ue(bs, shortRFS.delta_poc_s0_minus1[i]);
bs->writeBits(shortRFS.used_by_curr_pic_s0_flag[i], 1);
}
for (i = 0; i < shortRFS.num_positive_pics; i++)
{
bit_writer_put_ue(bs, shortRFS.delta_poc_s1_minus1[i]);
bs->writeBits(shortRFS.used_by_curr_pic_s1_flag[i], 1);
}
return;
}
class VaapiEncStreamHeaderHEVC
{
typedef std::vector<uint8_t> Header;
public:
VaapiEncStreamHeaderHEVC();
VaapiEncStreamHeaderHEVC(VaapiEncoderHEVC* encoder) {m_encoder = encoder;};
void setVPS(const VAEncSequenceParameterBufferHEVC* const sequence)
{
ASSERT(m_vps.empty());
BitWriter bs;
bit_writer_write_vps (&bs, sequence);
bsToHeader(m_vps, bs);
}
void setSPS(const VAEncSequenceParameterBufferHEVC* const sequence)
{
ASSERT(m_vps.size() && m_sps.empty());
BitWriter bs;
bit_writer_write_sps (&bs, sequence);
bsToHeader(m_sps, bs);
}
void addPPS(const VAEncPictureParameterBufferHEVC* const picParam)
{
ASSERT(m_sps.size() && m_pps.empty());
BitWriter bs;
bit_writer_write_pps (&bs, picParam);
bsToHeader(m_pps, bs);
}
void generateCodecConfig()
{
std::vector<Header*> headers;
ASSERT(m_vps.size() && m_sps.size() && (m_sps.size() > 4)&& m_pps.size() && m_headers.empty());
headers.push_back(&m_vps);
headers.push_back(&m_sps);
headers.push_back(&m_pps);
uint8_t sync[] = {0, 0, 0, 1};
for (size_t i = 0; i < headers.size(); i++) {
m_headers.insert(m_headers.end(), sync, sync + N_ELEMENTS(sync));
appendHeaderWithEmulation(*headers[i]);
}
}
YamiStatus getCodecConfig(VideoEncOutputBuffer* outBuffer)
{
ASSERT(outBuffer && (outBuffer->format == OUTPUT_CODEC_DATA || outBuffer->format == OUTPUT_EVERYTHING));
if (outBuffer->bufferSize < m_headers.size())
return YAMI_ENCODE_BUFFER_TOO_SMALL;
if (m_headers.empty())
return YAMI_ENCODE_NO_REQUEST_DATA;
std::copy(m_headers.begin(), m_headers.end(), outBuffer->data);
outBuffer->dataSize = m_headers.size();
outBuffer->flag |= ENCODE_BUFFERFLAG_CODECCONFIG;
return YAMI_SUCCESS;
}
private:
BOOL bit_writer_write_vps (
BitWriter *bitwriter,
const VAEncSequenceParameterBufferHEVC* const seq
)
{
BOOL vps_timing_info_present_flag = 0;
bit_writer_write_nal_header(bitwriter, VPS_NUT);
/* vps_video_parameter_set_id */
bitwriter->writeBits(0, 4);
/* vps_base_layer_internal_flag */
bitwriter->writeBits(1, 1);
/* vps_base_layer_available_flag */
bitwriter->writeBits(1, 1);
/* vps_max_layers_minus1 */
bitwriter->writeBits(0, 6);
/* vps_max_sub_layers_minus1 */
bitwriter->writeBits(0, 3);
/* vps_temporal_id_nesting_flag */
bitwriter->writeBits(1, 1);
/* vps_reserved_0xffff_16bits */
bitwriter->writeBits(0xffff, 16);
profile_tier_level(bitwriter, 1, 0, seq);
/* vps_sub_layer_ordering_info_present_flag */
bitwriter->writeBits(0, 1);
/*vps_max_dec_pic_buffering_minus1*/
bit_writer_put_ue(bitwriter, 5);
/*vps_max_num_reorder_pics*/
bit_writer_put_ue(bitwriter, m_encoder->m_numBFrames);
/*vps_max_latency_increase_plus1*/
bit_writer_put_ue(bitwriter, 0);
/* vps_max_layer_id */
bitwriter->writeBits(0, 6);
/* vps_num_layer_sets_minus1 */
bit_writer_put_ue(bitwriter, 0);
/* vps_timing_info_present_flag */
bitwriter->writeBits(vps_timing_info_present_flag, 1);
if (vps_timing_info_present_flag) {
/* TBD: add it later for BRC */
}
/* vps_extension_flag */
bitwriter->writeBits(0, 1);
/* rbsp_trailing_bits */
bit_writer_write_trailing_bits(bitwriter);
return TRUE;
}
BOOL bit_writer_write_sps(
BitWriter *bitwriter,
const VAEncSequenceParameterBufferHEVC* const seq
)
{
uint32_t i = 0;
bit_writer_write_nal_header(bitwriter, SPS_NUT);
/* sps_video_parameter_set_id */
bitwriter->writeBits(0, 4);
/* sps_max_sub_layers_minus1 */
bitwriter->writeBits(0, 3);
/* sps_temporal_id_nesting_flag */
bitwriter->writeBits(1, 1);
profile_tier_level(bitwriter, 1, 0, seq);
/* seq_parameter_set_id */
bit_writer_put_ue(bitwriter, 0);
/* chroma_format_idc: only support 4:2:0 for libva */
bit_writer_put_ue(bitwriter, seq->seq_fields.bits.chroma_format_idc);
if (3 == seq->seq_fields.bits.chroma_format_idc) {
bitwriter->writeBits(seq->seq_fields.bits.separate_colour_plane_flag, 1);
}
/* pic_width_in_luma_samples */
bit_writer_put_ue(bitwriter, seq->pic_width_in_luma_samples);
/* pic_height_in_luma_samples */
bit_writer_put_ue(bitwriter, seq->pic_height_in_luma_samples);
/* conformance_window_flag */
bitwriter->writeBits(m_encoder->m_confWinFlag, 1);
if (m_encoder->m_confWinFlag) {
bit_writer_put_ue(bitwriter, m_encoder->m_confWinLeftOffset);
bit_writer_put_ue(bitwriter, m_encoder->m_confWinRightOffset);
bit_writer_put_ue(bitwriter, m_encoder->m_confWinTopOffset);
bit_writer_put_ue(bitwriter, m_encoder->m_confWinBottomOffset);
}
/* bit_depth_luma_minus8 */
bit_writer_put_ue(bitwriter, seq->seq_fields.bits.bit_depth_luma_minus8);
/* bit_depth_chroma_minus8 */
bit_writer_put_ue(bitwriter, seq->seq_fields.bits.bit_depth_chroma_minus8);
/* log2_max_pic_order_cnt_lsb_minus4 */
assert(m_encoder->m_log2MaxPicOrderCnt >= 4);
bit_writer_put_ue(bitwriter, m_encoder->m_log2MaxPicOrderCnt - 4);
/* sps_sub_layer_ordering_info_present_flag */
bitwriter->writeBits(0, 1);
/* sps_max_dec_pic_buffering_minus1 */
bit_writer_put_ue(bitwriter, 5);
/* sps_max_num_reorder_pics */
bit_writer_put_ue(bitwriter, m_encoder->m_numBFrames);
/* sps_max_latency_increase_plus1 */
bit_writer_put_ue(bitwriter, 0);
bit_writer_put_ue(bitwriter, seq->log2_min_luma_coding_block_size_minus3);
bit_writer_put_ue(bitwriter, seq->log2_diff_max_min_luma_coding_block_size);
bit_writer_put_ue(bitwriter, seq->log2_min_transform_block_size_minus2);
bit_writer_put_ue(bitwriter, seq->log2_diff_max_min_transform_block_size);
bit_writer_put_ue(bitwriter, seq->max_transform_hierarchy_depth_inter);
bit_writer_put_ue(bitwriter, seq->max_transform_hierarchy_depth_intra);
/* scaling_list_enabled_flag */
bitwriter->writeBits(0, 1);
/* amp_enabled_flag */
bitwriter->writeBits(seq->seq_fields.bits.amp_enabled_flag, 1);
/* sample_adaptive_offset_enabled_flag */
bitwriter->writeBits(seq->seq_fields.bits.sample_adaptive_offset_enabled_flag, 1);
/* pcm_enabled_flag */
bitwriter->writeBits(seq->seq_fields.bits.pcm_enabled_flag, 1);
if (seq->seq_fields.bits.pcm_enabled_flag) {
/* pcm_sample_bit_depth_luma_minus1 */
bitwriter->writeBits(seq->pcm_sample_bit_depth_luma_minus1, 4);
/* pcm_sample_bit_depth_luma_minus1 */
bitwriter->writeBits(seq->pcm_sample_bit_depth_chroma_minus1, 4);
/* log2_min_pcm_luma_coding_block_size_minus3 */
bit_writer_put_ue(bitwriter, seq->log2_min_pcm_luma_coding_block_size_minus3);
/* log2_diff_max_min_pcm_luma_coding_block_size */
bit_writer_put_ue(bitwriter, seq->log2_max_pcm_luma_coding_block_size_minus3 - seq->log2_min_pcm_luma_coding_block_size_minus3);
/* pcm_loop_filter_disabled_flag */
bitwriter->writeBits(seq->seq_fields.bits.pcm_loop_filter_disabled_flag, 1);
}
bit_writer_put_ue(bitwriter, m_encoder->m_shortRFS.num_short_term_ref_pic_sets);
for (i = 0; i < m_encoder->m_shortRFS.num_short_term_ref_pic_sets; i++)
st_ref_pic_set(bitwriter, i, m_encoder->m_shortRFS);
/* long_term_ref_pics_present_flag */
bitwriter->writeBits(0, 1);
/*sps_temporal_mvp_enabled_flag*/
bitwriter->writeBits(0, 1);
/* strong_intra_smoothing_enabled_flag */
bitwriter->writeBits(seq->seq_fields.bits.strong_intra_smoothing_enabled_flag, 1);
/* vui_parameters_present_flag */
bitwriter->writeBits(seq->vui_parameters_present_flag, 1);
if (seq->vui_parameters_present_flag) {
/* aspect_ratio_info_present_flag */
bitwriter->writeBits(seq->vui_fields.bits.aspect_ratio_info_present_flag, 1);
if (seq->vui_fields.bits.aspect_ratio_info_present_flag) {
bitwriter->writeBits(seq->aspect_ratio_idc, 8);
if (seq->aspect_ratio_idc == 0xFF) {
bitwriter->writeBits(seq->sar_width, 16);
bitwriter->writeBits(seq->sar_height, 16);
}
}
/* overscan_info_present_flag */
bitwriter->writeBits(0, 1);
/* video_signal_type_present_flag */
bitwriter->writeBits(0, 1);
/* chroma_loc_info_present_flag */
bitwriter->writeBits(0, 1);
/* neutral_chroma_indication_flag */
bitwriter->writeBits(0, 1);
/* field_seq_flag */
bitwriter->writeBits(0, 1);
/* frame_field_info_present_flag */
bitwriter->writeBits(0, 1);
/* default_display_window_flag */
bitwriter->writeBits(0, 1);
/* vui_timing_info_present_flag */
bitwriter->writeBits(seq->vui_fields.bits.vui_timing_info_present_flag, 1);
if (seq->vui_fields.bits.vui_timing_info_present_flag) {
bitwriter->writeBits(seq->vui_num_units_in_tick, 32);
bitwriter->writeBits(seq->vui_time_scale, 32);
/* vui_poc_proportional_to_timing_flag */
bitwriter->writeBits(0, 1);
/* set hrd_parameters */
BOOL vui_hrd_parameters_present_flag = (seq->bits_per_second > 0 ? TRUE : FALSE);
/* vui_hrd_parameters_present_flag */
bitwriter->writeBits(vui_hrd_parameters_present_flag, 1);
if (vui_hrd_parameters_present_flag) {
/* nal_hrd_parameters_present_flag */
bitwriter->writeBits(1, 1);
/* vcl_hrd_parameters_present_flag */
bitwriter->writeBits(0, 1);
/* sub_pic_hrd_params_present_flag */
bitwriter->writeBits(0, 1);
bitwriter->writeBits(4, 4); /* bit_rate_scale */
bitwriter->writeBits(6, 4); /* cpb_size_scale */
/* initial_cpb_removal_delay_length_minus1 */
bitwriter->writeBits(23, 5);
/* au_cpb_removal_delay_length_minus1 */
bitwriter->writeBits(23, 5);
/* dpb_output_delay_length_minus1 */
bitwriter->writeBits(23, 5);
for (i = 0; i < 1; ++i) {
/* fixed_pic_rate_general_flag[0] */
bitwriter->writeBits(0, 1);
/* fixed_pic_rate_within_cvs_flag[0] */
bitwriter->writeBits(0, 1);
/* low_delay_hrd_flag[0] */
bitwriter->writeBits(1, 1);
/* bit_rate_value_minus1[0] */
bit_writer_put_ue(bitwriter, seq->bits_per_second/1024- 1);
/* cpb_size_value_minus1[0] */
bit_writer_put_ue(bitwriter, seq->bits_per_second/(1024*8) - 1);
/* cbr_flag[0] */
bitwriter->writeBits(1, 1);
}
}
}
/* bitwriter_restriction_flag */
bitwriter->writeBits(0, 1);
}
/* sps_extension_present_flag */
bitwriter->writeBits(0, 1);
/* rbsp_trailing_bits */
bit_writer_write_trailing_bits(bitwriter);
return TRUE;
}
BOOL bit_writer_write_pps(
BitWriter *bitwriter,
const VAEncPictureParameterBufferHEVC* const pic
)
{
uint32_t deblocking_filter_control_present_flag = 0;
uint32_t pps_slice_chroma_qp_offsets_present_flag = 1;
bit_writer_write_nal_header(bitwriter, PPS_NUT);
/* pps_pic_parameter_set_id */
bit_writer_put_ue(bitwriter, 0);
/* pps_seq_parameter_set_id */
bit_writer_put_ue(bitwriter, 0);
/* dependent_slice_segments_enabled_flag */
bitwriter->writeBits(pic->pic_fields.bits.dependent_slice_segments_enabled_flag, 1);
/* output_flag_present_flag */
bitwriter->writeBits(0, 1);
/* num_extra_slice_header_bits */
bitwriter->writeBits(0, 3);
/* sign_data_hiding_enabled_flag */
bitwriter->writeBits(pic->pic_fields.bits.sign_data_hiding_enabled_flag, 1);
/* cabac_init_present_flag */
bitwriter->writeBits(0, 1);
bit_writer_put_ue(bitwriter, pic->num_ref_idx_l0_default_active_minus1);
bit_writer_put_ue(bitwriter, pic->num_ref_idx_l1_default_active_minus1);
/* init_qp_minus26 */
bit_writer_put_se(bitwriter, pic->pic_init_qp-26);
/* constrained_intra_pred_flag */
bitwriter->writeBits(pic->pic_fields.bits.constrained_intra_pred_flag, 1);
/* transform_skip_enabled_flag */
bitwriter->writeBits(pic->pic_fields.bits.transform_skip_enabled_flag, 1);
/* cu_qp_delta_enabled_flag */
bitwriter->writeBits(pic->pic_fields.bits.cu_qp_delta_enabled_flag, 1);
if (pic->pic_fields.bits.cu_qp_delta_enabled_flag) {
/* diff_cu_qp_delta_depth */
bit_writer_put_ue(bitwriter, pic->diff_cu_qp_delta_depth);
}
/* pps_cb_qp_offset */
bit_writer_put_se(bitwriter, pic->pps_cb_qp_offset);
/* pps_cr_qp_offset */
bit_writer_put_se(bitwriter, pic->pps_cr_qp_offset);
/* pps_slice_chroma_qp_offsets_present_flag */
bitwriter->writeBits(pps_slice_chroma_qp_offsets_present_flag, 1);
/* weighted_pred_flag */
bitwriter->writeBits(pic->pic_fields.bits.weighted_pred_flag, 1);
/* weighted_bipred_flag */
bitwriter->writeBits(pic->pic_fields.bits.weighted_bipred_flag, 1);
/* transquant_bypass_enabled_flag */
bitwriter->writeBits(pic->pic_fields.bits.transquant_bypass_enabled_flag, 1);
/* tiles_enabled_flag */
bitwriter->writeBits(pic->pic_fields.bits.tiles_enabled_flag, 1);
/* entropy_coding_sync_enabled_flag */
bitwriter->writeBits(pic->pic_fields.bits.entropy_coding_sync_enabled_flag, 1);
if (pic->pic_fields.bits.tiles_enabled_flag) {
/*
* TBD: Add the tile division when tiles are enabled.
*/
assert(!pic->pic_fields.bits.tiles_enabled_flag);
}
/* pps_loop_filter_across_slices_enabled_flag */
bitwriter->writeBits(pic->pic_fields.bits.loop_filter_across_tiles_enabled_flag, 1);
/* deblocking_filter_control_present_flag. 1 */
bitwriter->writeBits(deblocking_filter_control_present_flag, 1);
if (deblocking_filter_control_present_flag) {
/* deblocking_filter_override_enabled_flag */
bitwriter->writeBits(0, 1);
/* pps_deblocking_filter_disabled_flag */
bitwriter->writeBits(0, 1);
}
/* scaling_list_data_present_flag */
bitwriter->writeBits(pic->pic_fields.bits.scaling_list_data_present_flag, 1);
if (pic->pic_fields.bits.scaling_list_data_present_flag) {
/*
* TBD: Add the scaling list data for PPS
*/
assert(!pic->pic_fields.bits.scaling_list_data_present_flag);
}
/* lists_modification_present_flag 0/1 ?*/
bitwriter->writeBits(0, 1);
/* log2_parallel_merge_level_minus2: 2 - 2 */
bit_writer_put_ue(bitwriter, 0);
/* slice_segment_header_extension_present_flag. Zero */
bitwriter->writeBits(0, 1);
/* pps_extension_flag */
bitwriter->writeBits(0, 1);
bit_writer_write_trailing_bits(bitwriter);
return TRUE;
}
void bsToHeader(Header& param, BitWriter& bs)
{
uint64_t codedBits = bs.getCodedBitsCount();
uint64_t codedBytes = codedBits / 8;
ASSERT(codedBytes && codedBits % 8 == 0);
uint8_t* codedData = bs.getBitWriterData();
ASSERT(codedData);
param.insert(param.end(), codedData, codedData + codedBytes);
}
void appendHeaderWithEmulation(Header& h)
{
Header::iterator s = h.begin();
Header::iterator e;
uint8_t zeros[] = {0, 0};
uint8_t emulation[] = {0, 0, 3};
do {
e = std::search(s, h.end(), zeros, zeros + N_ELEMENTS(zeros));
m_headers.insert(m_headers.end(), s, e);
if (e == h.end())
break;
s = e + N_ELEMENTS(zeros);
/* only when bitstream contains 0x000000/0x000001/0x000002/0x000003
need to insert emulation prevention byte 0x03 */
if (*s <= 3)
m_headers.insert(m_headers.end(), emulation, emulation + N_ELEMENTS(emulation));
else
m_headers.insert(m_headers.end(), zeros, zeros + N_ELEMENTS(zeros));
} while (1);
}
Header m_vps;
Header m_sps;
Header m_pps;
Header m_headers;
VaapiEncoderHEVC* m_encoder;
};
class VaapiEncPictureHEVC:public VaapiEncPicture
{
friend class VaapiEncoderHEVC;
friend class VaapiEncoderHEVCRef;
typedef std::function<YamiStatus()> Function;
public:
virtual ~VaapiEncPictureHEVC() {}
virtual YamiStatus getOutput(VideoEncOutputBuffer* outBuffer)
{
ASSERT(outBuffer);
VideoOutputFormat format = outBuffer->format;
//make a local copy of out Buffer;
VideoEncOutputBuffer out = *outBuffer;
out.flag = 0;
std::vector<Function> functions;
if (format == OUTPUT_CODEC_DATA || ((format == OUTPUT_EVERYTHING) && isIdr()))
functions.push_back(std::bind(&VaapiEncStreamHeaderHEVC::getCodecConfig, m_headers,&out));
if (format == OUTPUT_EVERYTHING || format == OUTPUT_FRAME_DATA)
functions.push_back(std::bind(getOutputHelper, this, &out));
YamiStatus ret = getOutput(&out, functions);
if (ret == YAMI_SUCCESS) {
outBuffer->dataSize = out.data - outBuffer->data;
outBuffer->flag = out.flag;
}
return ret;
}
private:
VaapiEncPictureHEVC(const ContextPtr& context, const SurfacePtr& surface, int64_t timeStamp):
VaapiEncPicture(context, surface, timeStamp),
m_frameNum(0),
m_poc(0)
{
}
bool isIdr() const {
return m_type == VAAPI_PICTURE_I && !m_frameNum;
}
//getOutput is a virutal function, we need this to help bind
static YamiStatus getOutputHelper(VaapiEncPictureHEVC* p, VideoEncOutputBuffer* out)
{
return p->VaapiEncPicture::getOutput(out);
}
YamiStatus getOutput(VideoEncOutputBuffer* outBuffer, std::vector<Function>& functions)
{
ASSERT(outBuffer);
outBuffer->dataSize = 0;
YamiStatus ret;
for (size_t i = 0; i < functions.size(); i++) {
ret = functions[i]();
if (ret != YAMI_SUCCESS)
return ret;
outBuffer->bufferSize -= outBuffer->dataSize;
outBuffer->data += outBuffer->dataSize;
}
return YAMI_SUCCESS;
}
uint32_t m_frameNum;
uint32_t m_poc;
StreamHeaderPtr m_headers;
};
class VaapiEncoderHEVCRef
{
public:
VaapiEncoderHEVCRef(const PicturePtr& picture, const SurfacePtr& surface):
m_frameNum(picture->m_frameNum),
m_poc(picture->m_poc),
m_pic(surface)
{
}
uint32_t m_frameNum;
uint32_t m_poc;
SurfacePtr m_pic;
};
VaapiEncoderHEVC::VaapiEncoderHEVC():
m_numBFrames(0),
m_ctbSize(8),
m_cuSize(32),
m_minTbSize(4),
m_maxTbSize(32),
m_reorderState(VAAPI_ENC_REORD_WAIT_FRAMES),
m_keyPeriod(30)
{
m_videoParamCommon.profile = VAProfileHEVCMain;
m_videoParamCommon.level = 51;
m_videoParamCommon.rcParams.initQP = 26;
m_videoParamCommon.rcParams.minQP = 1;
memset(&m_videoParamAVC, 0, sizeof(m_videoParamAVC));
m_videoParamAVC.idrInterval = 0;
}
VaapiEncoderHEVC::~VaapiEncoderHEVC()
{
FUNC_ENTER();
}
bool VaapiEncoderHEVC::ensureCodedBufferSize()
{
AutoLock locker(m_paramLock);
FUNC_ENTER();
if (m_maxCodedbufSize)
return true;
m_maxCodedbufSize = m_AlignedWidth * m_AlignedHeight * 3 / 2 + 0x1000;
DEBUG("m_maxCodedbufSize: %u", m_maxCodedbufSize);
return true;
}
void VaapiEncoderHEVC::resetParams ()
{
m_levelIdc = level();
if (10 == m_videoParamCommon.bitDepth)
m_videoParamCommon.profile = VAProfileHEVCMain10;
m_profileIdc = hevc_get_profile_idc(profile());
m_numSlices = 1;
assert (width() && height());
/* libva driver requred pic_width_in_luma_samples 16 aligned, not ctb aligned */
m_AlignedWidth = (width() + 15) / 16 * 16;
m_AlignedHeight = (height() + 15) / 16 * 16;
m_cuWidth = (width() + m_cuSize -1) / m_cuSize;
m_cuHeight = (height() + m_cuSize-1) / m_cuSize;
m_confWinLeftOffset = m_confWinTopOffset = 0;
if (m_AlignedWidth != width() || m_AlignedHeight !=height()) {
m_confWinFlag = true;
m_confWinRightOffset = (m_AlignedWidth - width()) / 2;
m_confWinBottomOffset = (m_AlignedHeight -height()) / 2;
} else {
m_confWinFlag = false;
m_confWinRightOffset = 0;
m_confWinBottomOffset = 0;
}
if (intraPeriod() == 0) {
ERROR("intra period must larger than 0");
m_videoParamCommon.intraPeriod = 1;
}
if (intraPeriod() <= ipPeriod()) {
WARNING("intra period is not larger than ip period");
m_videoParamCommon.ipPeriod = intraPeriod() - 1;
}
if (ipPeriod() == 0)
m_videoParamCommon.intraPeriod = 1;
else if (ipPeriod() >= 1)
m_numBFrames = ipPeriod() - 1;
m_keyPeriod = intraPeriod() * (m_videoParamAVC.idrInterval + 1);
if (minQP() > initQP() ||
(rateControlMode()== RATE_CONTROL_CQP && minQP() < initQP()))
minQP() = initQP();
if (m_numBFrames > (intraPeriod() + 1) / 2)
m_numBFrames = (intraPeriod() + 1) / 2;
DEBUG("resetParams, ensureCodedBufferSize");
ensureCodedBufferSize();
/* init m_maxFrameNum, max_poc */
m_log2MaxFrameNum =
hevc_get_log2_max_frame_num (m_keyPeriod);
assert (m_log2MaxFrameNum >= 4);
m_maxFrameNum = (1 << m_log2MaxFrameNum);
m_log2MaxPicOrderCnt = m_log2MaxFrameNum + 1;
m_maxPicOrderCnt = (1 << m_log2MaxPicOrderCnt);
m_maxRefList1Count = m_numBFrames > 0;//m_maxRefList1Count <=1, because of currenent order mechanism
m_maxRefList0Count = numRefFrames();
if (m_maxRefList0Count >= m_maxOutputBuffer -1)
m_maxRefList0Count = m_maxOutputBuffer -1;
m_maxRefFrames =
m_maxRefList0Count + m_maxRefList1Count;
assert(m_maxRefFrames <= m_maxOutputBuffer);
INFO("m_maxRefFrames: %d", m_maxRefFrames);
setShortRfs();
resetGopStart();
}
YamiStatus VaapiEncoderHEVC::getMaxOutSize(uint32_t* maxSize)
{
FUNC_ENTER();
if (ensureCodedBufferSize())
*maxSize = m_maxCodedbufSize;
else
*maxSize = 0;
return YAMI_SUCCESS;
}
YamiStatus VaapiEncoderHEVC::start()
{
FUNC_ENTER();
resetParams();
return VaapiEncoderBase::start();
}
void VaapiEncoderHEVC::flush()
{
YamiStatus ret;
FUNC_ENTER();
if (!m_reorderFrameList.empty()) {
changeLastBFrameToPFrame();
m_reorderState = VAAPI_ENC_REORD_DUMP_FRAMES;
ret = encodeAllFrames();
if (ret != YAMI_SUCCESS) {
ERROR("Not all frames are flushed.");
}
}
resetGopStart();
m_reorderFrameList.clear();
referenceListFree();
VaapiEncoderBase::flush();
}
YamiStatus VaapiEncoderHEVC::stop()
{
flush();
return VaapiEncoderBase::stop();
}
YamiStatus VaapiEncoderHEVC::setParameters(VideoParamConfigType type, Yami_PTR videoEncParams)
{
YamiStatus status = YAMI_INVALID_PARAM;
AutoLock locker(m_paramLock);
FUNC_ENTER();
if (!videoEncParams)
return YAMI_INVALID_PARAM;
switch (type) {
case VideoParamsTypeAVC: {
VideoParamsAVC* avc = (VideoParamsAVC*)videoEncParams;
if (avc->size == sizeof(VideoParamsAVC)) {
PARAMETER_ASSIGN(m_videoParamAVC, *avc);
status = YAMI_SUCCESS;
}
}
break;
default:
status = VaapiEncoderBase::setParameters(type, videoEncParams);
break;
}
return status;
}
YamiStatus VaapiEncoderHEVC::getParameters(VideoParamConfigType type, Yami_PTR videoEncParams)
{
YamiStatus status = YAMI_INVALID_PARAM;
AutoLock locker(m_paramLock);
FUNC_ENTER();
if (!videoEncParams)
return status;
switch (type) {
case VideoParamsTypeAVC: {
VideoParamsAVC* avc = (VideoParamsAVC*)videoEncParams;
if (avc->size == sizeof(VideoParamsAVC)) {
PARAMETER_ASSIGN(*avc, m_videoParamAVC);
status = YAMI_SUCCESS;
}
}
break;
default:
status = VaapiEncoderBase::getParameters(type, videoEncParams);
break;
}
return status;
}
void VaapiEncoderHEVC::changeLastBFrameToPFrame()
{
PicturePtr lastPic = m_reorderFrameList.back();
if (lastPic->m_type == VAAPI_PICTURE_B) {
lastPic->m_type = VAAPI_PICTURE_P;
m_reorderFrameList.pop_back();
m_reorderFrameList.push_front(lastPic);
}
}
YamiStatus VaapiEncoderHEVC::reorder(const SurfacePtr& surface, uint64_t timeStamp, bool forceKeyFrame)
{
if (!surface)
return YAMI_INVALID_PARAM;
PicturePtr picture(new VaapiEncPictureHEVC(m_context, surface, timeStamp));
bool isIdr = (m_frameIndex == 0 ||m_frameIndex >= m_keyPeriod || forceKeyFrame);
/* check key frames */
if (isIdr || (m_frameIndex % intraPeriod() == 0)) {
if (isIdr && m_reorderFrameList.size()) {
changeLastBFrameToPFrame();
}
setIntraFrame (picture, isIdr);
m_reorderFrameList.push_back(picture);
m_reorderState = VAAPI_ENC_REORD_DUMP_FRAMES;
} else if (m_frameIndex % (m_numBFrames + 1) != 0) {
setBFrame (picture);
m_reorderFrameList.push_back(picture);
} else {
setPFrame (picture);
m_reorderFrameList.push_front(picture);
m_reorderState = VAAPI_ENC_REORD_DUMP_FRAMES;
}
DEBUG("m_frameIndex is %d\n", m_frameIndex);
picture->m_poc = m_frameIndex;
m_frameIndex++;
return YAMI_SUCCESS;
}
YamiStatus VaapiEncoderHEVC::encodeAllFrames()
{
FUNC_ENTER();
YamiStatus ret;
while (m_reorderState == VAAPI_ENC_REORD_DUMP_FRAMES) {
if (!m_maxCodedbufSize)
ensureCodedBufferSize();
ASSERT(m_maxCodedbufSize);
CodedBufferPtr codedBuffer = VaapiCodedBuffer::create(m_context, m_maxCodedbufSize);
if (!codedBuffer)
return YAMI_OUT_MEMORY;
DEBUG("m_reorderFrameList size: %zu\n", m_reorderFrameList.size());
PicturePtr picture = m_reorderFrameList.front();
m_reorderFrameList.pop_front();
picture->m_codedBuffer = codedBuffer;
if (m_reorderFrameList.empty())
m_reorderState = VAAPI_ENC_REORD_WAIT_FRAMES;
ret = encodePicture(picture);
if (ret != YAMI_SUCCESS) {
return ret;
}
codedBuffer->setFlag(ENCODE_BUFFERFLAG_ENDOFFRAME);
INFO("picture->m_type: 0x%x\n", picture->m_type);
if (picture->isIdr()) {
codedBuffer->setFlag(ENCODE_BUFFERFLAG_SYNCFRAME);
}
if (!output(picture))
return YAMI_INVALID_PARAM;
}
INFO();
return YAMI_SUCCESS;
}
// calls immediately after reorder,
// it makes sure I frame are encoded immediately, so P frames can be pushed to the front of the m_reorderFrameList.
// it also makes sure input thread and output thread runs in parallel
YamiStatus VaapiEncoderHEVC::doEncode(const SurfacePtr& surface, uint64_t timeStamp, bool forceKeyFrame)
{
FUNC_ENTER();
YamiStatus ret;
ret = reorder(surface, timeStamp, forceKeyFrame);
if (ret != YAMI_SUCCESS)
return ret;
ret = encodeAllFrames();
if (ret != YAMI_SUCCESS) {
return ret;
}
return YAMI_SUCCESS;
}
YamiStatus VaapiEncoderHEVC::getCodecConfig(VideoEncOutputBuffer* outBuffer)
{
ASSERT(outBuffer && ((outBuffer->flag == OUTPUT_CODEC_DATA) || outBuffer->flag == OUTPUT_EVERYTHING));
AutoLock locker(m_paramLock);
if (!m_headers)
return YAMI_ENCODE_NO_REQUEST_DATA;
return m_headers->getCodecConfig(outBuffer);
}
/* Handle new GOP starts */
void VaapiEncoderHEVC::resetGopStart ()
{
m_frameIndex = 0;
}
/* Marks the supplied picture as a B-frame */
void VaapiEncoderHEVC::setBFrame (const PicturePtr& pic)
{
pic->m_type = VAAPI_PICTURE_B;
pic->m_frameNum = (m_frameIndex % m_maxFrameNum);
}
/* Marks the supplied picture as a P-frame */
void VaapiEncoderHEVC::setPFrame (const PicturePtr& pic)
{
pic->m_type = VAAPI_PICTURE_P;
pic->m_frameNum = (m_frameIndex % m_maxFrameNum);
}
/* Marks the supplied picture as an I-frame */
void VaapiEncoderHEVC::setIFrame (const PicturePtr& pic)
{
pic->m_type = VAAPI_PICTURE_I;
pic->m_frameNum = (m_frameIndex % m_maxFrameNum);
}
/* Marks the supplied picture as an IDR frame */
void VaapiEncoderHEVC::setIdrFrame (const PicturePtr& pic)
{
pic->m_type = VAAPI_PICTURE_I;
pic->m_frameNum = 0;
pic->m_poc = 0;
}
/* Marks the supplied picture a a key-frame */
void VaapiEncoderHEVC::setIntraFrame (const PicturePtr& picture,bool idIdr)
{
if (idIdr) {
resetGopStart();
setIdrFrame(picture);
} else
setIFrame(picture);
}
bool VaapiEncoderHEVC::
referenceListUpdate (const PicturePtr& picture, const SurfacePtr& surface)
{
if (VAAPI_PICTURE_B == picture->m_type) {
return true;
}
if (picture->isIdr()) {
m_refList.clear();
} else if (m_refList.size() >= m_maxRefFrames) {
m_refList.pop_back();
}
ReferencePtr ref(new VaapiEncoderHEVCRef(picture, surface));
m_refList.push_front(ref); // recent first
assert (m_refList.size() <= m_maxRefFrames);
return true;
}
bool VaapiEncoderHEVC::pictureReferenceListSet (
const PicturePtr& picture)
{
uint32_t i;
/* reset reflist0 and reflist1 every time */
m_refList0.clear();
m_refList1.clear();
if (picture->m_type == VAAPI_PICTURE_I)
return true;
for (i = 0; i < m_refList.size(); i++) {
assert(picture->m_poc != m_refList[i]->m_poc);
if (picture->m_poc > m_refList[i]->m_poc) {
/* set forward reflist: descending order */
m_refList0.push_back(m_refList[i]);
} else {
/* set backward reflist: ascending order */
m_refList1.push_front(m_refList[i]);
}
}
if (m_refList0.size() > m_maxRefList0Count)
m_refList0.resize(m_maxRefList0Count);
if (m_refList1.size() > m_maxRefList1Count)
m_refList1.resize(m_maxRefList1Count);
shortRfsUpdate(picture);
assert (m_refList0.size() + m_refList1.size() <= m_maxRefFrames);
return true;
}
void VaapiEncoderHEVC::referenceListFree()
{
m_refList.clear();
m_refList0.clear();
m_refList1.clear();
}
void VaapiEncoderHEVC::shortRfsUpdate(const PicturePtr& picture)
{
int i;
memset(&m_shortRFS, 0, sizeof(m_shortRFS));
m_shortRFS.num_short_term_ref_pic_sets = 0;
m_shortRFS.inter_ref_pic_set_prediction_flag = 0;
if (intraPeriod() > 1 && m_refList0.size()) {
m_shortRFS.num_negative_pics = 1;
m_shortRFS.delta_poc_s0_minus1[0] = picture->m_poc - m_refList0[0]->m_poc - 1;
m_shortRFS.used_by_curr_pic_s0_flag[0] = 1;
if (m_numBFrames && m_refList1.size()) {
m_shortRFS.num_positive_pics = 1;
m_shortRFS.delta_poc_s1_minus1[0] = m_refList1[0]->m_poc - picture->m_poc - 1;
m_shortRFS.used_by_curr_pic_s1_flag[0] = 1;
DEBUG("m_refList1_size is %zu\n", m_refList1.size());
}
}
for (i = 1; i < m_shortRFS.num_negative_pics; i++)
{
m_shortRFS.delta_poc_s0_minus1[i] = 0;
m_shortRFS.used_by_curr_pic_s0_flag[i] = 1;
}
for (i = 1; i < m_shortRFS.num_positive_pics; i++) {
m_shortRFS.delta_poc_s1_minus1[i] = 0;
m_shortRFS.used_by_curr_pic_s1_flag[i] = 1;
}
}
void VaapiEncoderHEVC::setShortRfs()
{
int i;
memset(&m_shortRFS, 0, sizeof(m_shortRFS));
if (intraPeriod() > 1) {
m_shortRFS.num_negative_pics = 1;
if (m_numBFrames )
m_shortRFS.num_positive_pics = 1;
}
m_shortRFS.num_short_term_ref_pic_sets = 0;
m_shortRFS.inter_ref_pic_set_prediction_flag = 0;
m_shortRFS.delta_poc_s0_minus1[0] = 0;
m_shortRFS.used_by_curr_pic_s0_flag[0] = 1;
for (i = 1; i < m_shortRFS.num_negative_pics; i++)
{
m_shortRFS.delta_poc_s0_minus1[i] = 0;
m_shortRFS.used_by_curr_pic_s0_flag[i] = 1;
}
for (i = 1; i < m_shortRFS.num_positive_pics; i++) {
m_shortRFS.delta_poc_s1_minus1[i] = 0;
m_shortRFS.used_by_curr_pic_s1_flag[i] = 1;
}
}
bool VaapiEncoderHEVC::fill(VAEncSequenceParameterBufferHEVC* seqParam) const
{
seqParam->general_profile_idc = m_profileIdc;
seqParam->general_level_idc = level();
seqParam->general_tier_flag = 0;
seqParam->intra_period = intraPeriod();
seqParam->intra_idr_period = seqParam->intra_period;
seqParam->ip_period = 1 + m_numBFrames;
seqParam->bits_per_second = bitRate();
seqParam->pic_width_in_luma_samples = m_AlignedWidth;
seqParam->pic_height_in_luma_samples = m_AlignedHeight;
/*Only support yuv 4:2:0 format */
seqParam->seq_fields.bits.chroma_format_idc = 1;
/* separate color plane_flag*/
seqParam->seq_fields.bits.separate_colour_plane_flag = 0;
/* bit_depth_luma_minus8. Only 0 is supported for main profile */
seqParam->seq_fields.bits.bit_depth_luma_minus8 = m_videoParamCommon.bitDepth - 8;
/* bit_depth_chroma_minus8. Only 0 is supported for main profile*/
seqParam->seq_fields.bits.bit_depth_chroma_minus8 = m_videoParamCommon.bitDepth - 8;
/* scaling_list_enabled_flag. Use the default value */
seqParam->seq_fields.bits.scaling_list_enabled_flag = 0;
/* strong_intra_smoothing_enabled_flag. Not use the bi-linear interpolation */
seqParam->seq_fields.bits.strong_intra_smoothing_enabled_flag = 0;
/* amp_enabled_flag(nLx2N or nRx2N). This is not supported */
seqParam->seq_fields.bits.amp_enabled_flag = 1;
/* sample_adaptive_offset_enabled_flag. Unsupported */
seqParam->seq_fields.bits.sample_adaptive_offset_enabled_flag = 0;
/* pcm_enabled_flag */
seqParam->seq_fields.bits.pcm_enabled_flag = 0;
/* pcm_loop_filter_disabled_flag */
seqParam->seq_fields.bits.pcm_loop_filter_disabled_flag = 1;
/* sps_temporal_mvp_enabled_flag. Enabled */
seqParam->seq_fields.bits.sps_temporal_mvp_enabled_flag = 0;
seqParam->log2_min_luma_coding_block_size_minus3 = log2(m_ctbSize) -3;
seqParam->log2_diff_max_min_luma_coding_block_size = log2(m_cuSize) - log2(m_ctbSize);
seqParam->log2_min_transform_block_size_minus2 = log2(m_minTbSize) - 2;
seqParam->log2_diff_max_min_transform_block_size = log2(m_maxTbSize) - log2(m_minTbSize);
/* max_transform_hierarchy_depth_inter */
seqParam->max_transform_hierarchy_depth_inter = 2;
/* max_transform_hierarchy_depth_intra */
seqParam->max_transform_hierarchy_depth_intra = 2;
/* The PCM fields can be ignored as PCM is disabled */
seqParam->pcm_sample_bit_depth_luma_minus1 = 7;
seqParam->pcm_sample_bit_depth_chroma_minus1 = 7;
seqParam->log2_min_pcm_luma_coding_block_size_minus3 = 0;
seqParam->log2_max_pcm_luma_coding_block_size_minus3 = 0;
/* VUI parameters are always set for timing_info (framerate/bitrate) */
seqParam->vui_parameters_present_flag = TRUE;
seqParam->vui_fields.bits.vui_timing_info_present_flag = TRUE;
seqParam->vui_num_units_in_tick = frameRateDenom();
/* Fps of hevc is equal to vui_time_scale/vui_time_scale. It is differenet from h264 */
seqParam->vui_time_scale = frameRateNum();
return true;
}
/* Fills in VA picture parameter buffer */
bool VaapiEncoderHEVC::fill(VAEncPictureParameterBufferHEVC* picParam, const PicturePtr& picture,
const SurfacePtr& surface) const
{
uint32_t i = 0;
picParam->decoded_curr_pic.picture_id = surface->getID();
picParam->decoded_curr_pic.flags = VA_PICTURE_HEVC_RPS_LT_CURR;
picParam->decoded_curr_pic.pic_order_cnt = picture->m_poc;
if (picture->m_type != VAAPI_PICTURE_I) {
for (i = 0; i < m_refList.size(); i++) {
picParam->reference_frames[i].picture_id = m_refList[i]->m_pic->getID();
picParam->reference_frames[i].pic_order_cnt= m_refList[i]->m_poc;
}
}
for (; i < N_ELEMENTS(picParam->reference_frames); ++i) {
picParam->reference_frames[i].picture_id = VA_INVALID_ID;
}
picParam->coded_buf = picture->m_codedBuffer->getID();
/*collocated_ref_pic_index should be 0xff when element slice_temporal_mvp_enable_flag is 0 */
picParam->collocated_ref_pic_index = 0xff;
picParam->last_picture = 0; /* means last encoding picture */
picParam->pic_init_qp = initQP();
picParam->diff_cu_qp_delta_depth = 0;
picParam->pps_cb_qp_offset = 0;
picParam->pps_cr_qp_offset = 0;
/* currently multi-tile is disabled */
picParam->num_tile_columns_minus1 = 0;
picParam->num_tile_rows_minus1 = 0;
memset(picParam->column_width_minus1, 0, sizeof(picParam->column_width_minus1));
memset(picParam->row_height_minus1, 0, sizeof(picParam->row_height_minus1));
picParam->log2_parallel_merge_level_minus2 = 0;
/*no bit size limitation*/
picParam->ctu_max_bitsize_allowed = 0;
picParam->num_ref_idx_l0_default_active_minus1 = 0;
picParam->num_ref_idx_l1_default_active_minus1 = 0;
picParam->slice_pic_parameter_set_id = 0;
picParam->nal_unit_type = PPS_NUT;
picParam->pic_fields.value = 0;
picParam->pic_fields.bits.idr_pic_flag = picture->isIdr();
/*FIXME: can't support picture type B1 and B2 now */
picParam->pic_fields.bits.coding_type = picture->m_type;
picParam->pic_fields.bits.reference_pic_flag = (picture->m_type != VAAPI_PICTURE_B);
picParam->pic_fields.bits.dependent_slice_segments_enabled_flag = 0;
picParam->pic_fields.bits.sign_data_hiding_enabled_flag = 0;
picParam->pic_fields.bits.constrained_intra_pred_flag = 0;
picParam->pic_fields.bits.transform_skip_enabled_flag = 0;
/* cu_qp_delta_enabled_flag should be true to bitrate control */
picParam->pic_fields.bits.cu_qp_delta_enabled_flag = 1;
picParam->pic_fields.bits.weighted_pred_flag = 0;
picParam->pic_fields.bits.weighted_bipred_flag = 0;
picParam->pic_fields.bits.transquant_bypass_enabled_flag = 0;
picParam->pic_fields.bits.tiles_enabled_flag = 0;
picParam->pic_fields.bits.entropy_coding_sync_enabled_flag = 0;
picParam->pic_fields.bits.loop_filter_across_tiles_enabled_flag = 0;
picParam->pic_fields.bits.pps_loop_filter_across_slices_enabled_flag = 0;
/* scaling_list_data_present_flag: use default scaling list data*/
picParam->pic_fields.bits.scaling_list_data_present_flag = 0;
picParam->pic_fields.bits.screen_content_flag = 1;
picParam->pic_fields.bits.no_output_of_prior_pics_flag = 0;
return TRUE;
}
bool VaapiEncoderHEVC::ensureSequenceHeader(const PicturePtr& picture,const VAEncSequenceParameterBufferHEVC* const sequence)
{
m_headers.reset(new VaapiEncStreamHeaderHEVC(this));
m_headers->setVPS(sequence);
m_headers->setSPS(sequence);
return true;
}
bool VaapiEncoderHEVC::ensurePictureHeader(const PicturePtr& picture, const VAEncPictureParameterBufferHEVC* const picParam)
{
m_headers->addPPS(picParam);
m_headers->generateCodecConfig();
picture->m_headers = m_headers;
return true;
}
bool VaapiEncoderHEVC:: fillReferenceList(VAEncSliceParameterBufferHEVC* slice) const
{
uint32_t i = 0;
for (i = 0; i < m_refList0.size(); i++) {
assert(m_refList0[i] && m_refList0[i]->m_pic && (m_refList0[i]->m_pic->getID() != VA_INVALID_ID));
slice->ref_pic_list0[i].picture_id = m_refList0[i]->m_pic->getID();
slice->ref_pic_list0[i].pic_order_cnt= m_refList0[i]->m_poc;
}
for (; i < N_ELEMENTS(slice->ref_pic_list0); i++)
slice->ref_pic_list0[i].picture_id = VA_INVALID_SURFACE;
for (i = 0; i < m_refList1.size(); i++){
assert(m_refList1[i] && m_refList1[i]->m_pic && (m_refList1[i]->m_pic->getID() != VA_INVALID_ID));
slice->ref_pic_list1[i].picture_id = m_refList1[i]->m_pic->getID();
slice->ref_pic_list1[i].pic_order_cnt= m_refList1[i]->m_poc;
}
for (; i < N_ELEMENTS(slice->ref_pic_list1); i++)
slice->ref_pic_list1[i].picture_id = VA_INVALID_SURFACE;
return true;
}
bool VaapiEncoderHEVC::addPackedSliceHeader(const PicturePtr& picture,
const VAEncSliceParameterBufferHEVC* const sliceParam,
uint32_t sliceIndex) const
{
bool ret = true;
BitWriter bs;
BOOL short_term_ref_pic_set_sps_flag = !!m_shortRFS.num_short_term_ref_pic_sets;
HevcNalUnitType nalUnitType = (picture->isIdr() ? IDR_W_RADL : TRAIL_R );
bs.writeBits(HEVC_NAL_START_CODE, 32);
bit_writer_write_nal_header(&bs, nalUnitType);
/* first_slice_segment_in_pic_flag */
bs.writeBits(sliceIndex == 0, 1);
/* no_output_of_prior_pics_flag */
if (nalUnitType >= BLA_W_LP && nalUnitType <= RSV_IRAP_VCL23 )
bs.writeBits(0, 1);
/* slice_pic_parameter_set_id */
bit_writer_put_ue(&bs, 0);
if (sliceIndex) {
/* don't support dependent_slice_segments_enabled_flag right now*/
ASSERT (!m_picParam->pic_fields.bits.dependent_slice_segments_enabled_flag &&
!sliceParam->slice_fields.bits.dependent_slice_segment_flag);
bs.writeBits(sliceParam->slice_segment_address, log2(sliceParam->num_ctu_in_slice));
}
if (!sliceParam->slice_fields.bits.dependent_slice_segment_flag) {
bit_writer_put_ue(&bs, sliceParam->slice_type);
ASSERT(!m_seqParam->seq_fields.bits.separate_colour_plane_flag);
if (nalUnitType != IDR_W_RADL && nalUnitType != IDR_N_LP) {
bs.writeBits(m_picParam->decoded_curr_pic.pic_order_cnt, m_log2MaxPicOrderCnt);
bs.writeBits(short_term_ref_pic_set_sps_flag, 1);
if (!short_term_ref_pic_set_sps_flag)
st_ref_pic_set(&bs, m_shortRFS.num_short_term_ref_pic_sets, m_shortRFS);
else if (m_shortRFS.num_short_term_ref_pic_sets > 1)
bs.writeBits(m_shortRFS.short_term_ref_pic_set_idx, log2(m_shortRFS.num_short_term_ref_pic_sets));
/* long_term_ref_pics_present_flag is set to 0 */
if (sliceParam->slice_type != HEVC_SLICE_TYPE_I) {
bs.writeBits(sliceParam->slice_fields.bits.num_ref_idx_active_override_flag, 1);
if (sliceParam->slice_fields.bits.num_ref_idx_active_override_flag) {
bit_writer_put_ue(&bs, sliceParam->num_ref_idx_l0_active_minus1);
if (sliceParam->slice_type == HEVC_SLICE_TYPE_B )
bit_writer_put_ue(&bs, sliceParam->num_ref_idx_l1_active_minus1);
}
/* pps lists_modification_present_flag is set to 0 */
if (sliceParam->slice_type == HEVC_SLICE_TYPE_B)
bs.writeBits(sliceParam->slice_fields.bits.mvd_l1_zero_flag, 1);
if (sliceParam->slice_fields.bits.cabac_init_flag)
bs.writeBits(sliceParam->slice_fields.bits.cabac_init_flag, 1);
/* slice_temporal_mvp_enabled_flag and weighted_pred_flag are set to 0*/
ASSERT(!sliceParam->slice_fields.bits.slice_temporal_mvp_enabled_flag &&
!m_picParam->pic_fields.bits.weighted_bipred_flag);
ASSERT(sliceParam->max_num_merge_cand <= 5);
bit_writer_put_ue(&bs, 5 - sliceParam->max_num_merge_cand);
}
}
bit_writer_put_ue(&bs, sliceParam->slice_qp_delta);
/* pps_slice_chroma_qp_offsets_present_flag is set to 1 */
bit_writer_put_ue(&bs, sliceParam->slice_cb_qp_offset);
bit_writer_put_ue(&bs, sliceParam->slice_cr_qp_offset);
/* deblocking_filter_override_enabled_flag and
* pps_loop_filter_across_slices_enabled_flag are set to 0 */
}
bit_writer_write_trailing_bits(&bs);
uint8_t* codedData = bs.getBitWriterData();
ASSERT(codedData);
if (!picture->addPackedHeader(VAEncPackedHeaderSlice, codedData, bs.getCodedBitsCount())) {
ret = false;
}
return ret;
}
/* Add slice headers to picture */
bool VaapiEncoderHEVC::addSliceHeaders (const PicturePtr& picture) const
{
VAEncSliceParameterBufferHEVC *sliceParam;
uint32_t sliceOfCtus, sliceModCtus, curSliceCtus;
uint32_t numCtus;
uint32_t lastCtuIndex;
assert (picture);
if (picture->m_type != VAAPI_PICTURE_I) {
/* have one reference frame at least */
assert(m_refList0.size() > 0);
}
numCtus= m_cuWidth * m_cuHeight;
assert (m_numSlices && m_numSlices < numCtus);
sliceOfCtus = numCtus / m_numSlices;
sliceModCtus = numCtus % m_numSlices;
lastCtuIndex = 0;
for (uint32_t i = 0; i < m_numSlices; ++i) {
curSliceCtus = sliceOfCtus;
if (sliceModCtus) {
++curSliceCtus;
--sliceModCtus;
}
if (!picture->newSlice(sliceParam))
return false;
sliceParam->slice_segment_address = lastCtuIndex;
sliceParam->num_ctu_in_slice = curSliceCtus;
sliceParam->slice_type = hevc_get_slice_type (picture->m_type);
assert (sliceParam->slice_type != -1);
sliceParam->slice_pic_parameter_set_id = 0;
sliceParam->slice_fields.bits.num_ref_idx_active_override_flag = 1;
if (picture->m_type != VAAPI_PICTURE_I && m_refList0.size() > 0)
sliceParam->num_ref_idx_l0_active_minus1 = m_refList0.size() - 1;
if (picture->m_type == VAAPI_PICTURE_B && m_refList1.size() > 0)
sliceParam->num_ref_idx_l1_active_minus1 = m_refList1.size() - 1;
fillReferenceList(sliceParam);
/* luma_log2_weight_denom should be the range: [0, 7] */
sliceParam->luma_log2_weight_denom = 0;
/* max_num_merge_cand should be the range [1, 5 + NumExtraMergeCand] */
sliceParam->max_num_merge_cand = 5;
/* let slice_qp equal to init_qp*/
sliceParam->slice_qp_delta = 0;
/* slice_beta_offset_div2 and slice_tc_offset_div2 should be the range [-6, 6] */
sliceParam->slice_beta_offset_div2 = 0;
sliceParam->slice_tc_offset_div2 = 0;
/* set calculation for next slice */
lastCtuIndex += curSliceCtus;
sliceParam->slice_fields.bits.slice_deblocking_filter_disabled_flag = 0;
sliceParam->slice_fields.bits.last_slice_of_pic_flag = (lastCtuIndex == numCtus);
addPackedSliceHeader(picture, sliceParam, i);
}
assert (lastCtuIndex == numCtus);
return true;
}
bool VaapiEncoderHEVC::ensureSequence(const PicturePtr& picture)
{
#ifndef __ENABLE_H265_ENC_ON_STUDIO_VA__
//fill sps for every frame,
//or else, occasionally, m_seqParam will be modified by vaCreateBuffer() when vaCreateBuffer()
//is called to create m_picParam;
if (picture->m_type != VAAPI_PICTURE_I) {
return true;
}
#endif
if (!picture->editSequence(m_seqParam) || !fill(m_seqParam)) {
ERROR("failed to create sequence parameter buffer (SPS)");
return false;
}
if (!ensureSequenceHeader(picture, m_seqParam)) {
ERROR ("failed to create packed sequence header buffer");
return false;
}
return true;
}
bool VaapiEncoderHEVC::ensurePicture (const PicturePtr& picture, const SurfacePtr& surface)
{
if (!pictureReferenceListSet(picture)) {
ERROR ("reference list reorder failed");
return false;
}
if (!picture->editPicture(m_picParam) || !fill(m_picParam, picture, surface)) {
ERROR("failed to create picture parameter buffer (PPS)");
return false;
}
if (picture->isIdr() && !ensurePictureHeader (picture, m_picParam)) {
ERROR ("set picture packed header failed");
return false;
}
return true;
}
bool VaapiEncoderHEVC::ensureSlices(const PicturePtr& picture)
{
assert (picture);
if (!addSliceHeaders (picture))
return false;
return true;
}
YamiStatus VaapiEncoderHEVC::encodePicture(const PicturePtr& picture)
{
YamiStatus ret = YAMI_FAIL;
SurfacePtr reconstruct = createSurface();
if (!reconstruct)
return ret;
{
AutoLock locker(m_paramLock);
if (!ensureSequence (picture))
return ret;
if (!ensureMiscParams (picture.get()))
return ret;
if (!ensurePicture(picture, reconstruct))
return ret;
if (!ensureSlices (picture))
return ret;
}
if (!picture->encode())
return ret;
if (!referenceListUpdate (picture, reconstruct))
return ret;
return YAMI_SUCCESS;
}
}