/*
* Copyright (c) 2011 Intel Corporation. All Rights Reserved.
* Copyright (c) Imagination Technologies Limited, UK
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Authors:
* Elaine Wang <elaine.wang@intel.com>
* Zeng Li <zeng.li@intel.com>
*
*/
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include "psb_def.h"
#include "psb_drv_debug.h"
#include "psb_surface.h"
#include "psb_cmdbuf.h"
#include "pnw_hostcode.h"
#include "pnw_H264ES.h"
#include "pnw_hostheader.h"
#include "va/va_enc_h264.h"
#define TOPAZ_H264_MAX_BITRATE 50000000
#define INIT_CONTEXT_H264ES context_ENC_p ctx = (context_ENC_p) obj_context->format_data
#define SURFACE(id) ((object_surface_p) object_heap_lookup( &ctx->obj_context->driver_data->surface_heap, id ))
#define BUFFER(id) ((object_buffer_p) object_heap_lookup( &ctx->obj_context->driver_data->buffer_heap, id ))
static void pnw_H264ES_QueryConfigAttributes(
VAProfile __maybe_unused profile,
VAEntrypoint __maybe_unused entrypoint,
VAConfigAttrib *attrib_list,
int num_attribs)
{
int i;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264ES_QueryConfigAttributes\n");
/* RateControl attributes */
for (i = 0; i < num_attribs; i++) {
switch (attrib_list[i].type) {
case VAConfigAttribRTFormat:
break;
case VAConfigAttribRateControl:
attrib_list[i].value = VA_RC_NONE | VA_RC_CBR | VA_RC_VBR | VA_RC_VCM;
break;
case VAConfigAttribEncMaxRefFrames:
attrib_list[i].value = 1;
break;
default:
attrib_list[i].value = VA_ATTRIB_NOT_SUPPORTED;
break;
}
}
}
static VAStatus pnw_H264ES_ValidateConfig(
object_config_p obj_config)
{
int i;
/* Check all attributes */
for (i = 0; i < obj_config->attrib_count; i++) {
switch (obj_config->attrib_list[i].type) {
case VAConfigAttribRTFormat:
/* Ignore */
break;
case VAConfigAttribRateControl:
break;
default:
return VA_STATUS_ERROR_ATTR_NOT_SUPPORTED;
}
}
return VA_STATUS_SUCCESS;
}
static VAStatus pnw_H264ES_CreateContext(
object_context_p obj_context,
object_config_p obj_config)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
context_ENC_p ctx;
int i;
unsigned int eRCmode;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264ES_CreateContext\n");
vaStatus = pnw_CreateContext(obj_context, obj_config, 0);
if (VA_STATUS_SUCCESS != vaStatus)
return VA_STATUS_ERROR_ALLOCATION_FAILED;
ctx = (context_ENC_p) obj_context->format_data;
for (i = 0; i < obj_config->attrib_count; i++) {
if (obj_config->attrib_list[i].type == VAConfigAttribRateControl)
break;
}
if (i >= obj_config->attrib_count)
eRCmode = VA_RC_NONE;
else
eRCmode = obj_config->attrib_list[i].value;
if (eRCmode == VA_RC_VBR) {
ctx->eCodec = IMG_CODEC_H264_VBR;
ctx->sRCParams.RCEnable = IMG_TRUE;
ctx->sRCParams.bDisableBitStuffing = IMG_FALSE;
} else if (eRCmode == VA_RC_CBR) {
ctx->eCodec = IMG_CODEC_H264_CBR;
ctx->sRCParams.RCEnable = IMG_TRUE;
ctx->sRCParams.bDisableBitStuffing = IMG_TRUE;
} else if (eRCmode == VA_RC_NONE) {
ctx->eCodec = IMG_CODEC_H264_NO_RC;
ctx->sRCParams.RCEnable = IMG_FALSE;
ctx->sRCParams.bDisableBitStuffing = IMG_FALSE;
} else if (eRCmode == VA_RC_VCM) {
ctx->eCodec = IMG_CODEC_H264_VCM;
ctx->sRCParams.RCEnable = IMG_TRUE;
ctx->sRCParams.bDisableBitStuffing = IMG_FALSE;
} else
return VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "eCodec is %d\n", ctx->eCodec);
ctx->eFormat = IMG_CODEC_PL12; /* use default */
ctx->Slices = 1;
ctx->idr_pic_id = 1;
ctx->buffer_size = 0;
ctx->initial_buffer_fullness = 0;
//initialize the frame_rate and qp
ctx->sRCParams.FrameRate = 30;
if (getenv("PSB_VIDEO_SIG_CORE") == NULL) {
ctx->Slices = 2;
ctx->NumCores = 2;
}
ctx->ParallelCores = min(ctx->NumCores, ctx->Slices);
ctx->IPEControl = pnw__get_ipe_control(ctx->eCodec);
switch (obj_config->profile) {
case VAProfileH264Baseline:
ctx->profile_idc = 5;
break;
case VAProfileH264Main:
ctx->profile_idc = 6;
break;
default:
ctx->profile_idc = 6;
break;
}
return vaStatus;
}
static void pnw_H264ES_DestroyContext(
object_context_p obj_context)
{
drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264ES_DestroyPicture\n");
pnw_DestroyContext(obj_context);
}
static VAStatus pnw_H264ES_BeginPicture(
object_context_p obj_context)
{
INIT_CONTEXT_H264ES;
VAStatus vaStatus = VA_STATUS_SUCCESS;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264ES_BeginPicture\n");
vaStatus = pnw_BeginPicture(ctx);
return vaStatus;
}
static VAStatus pnw__H264ES_process_sequence_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
VAEncSequenceParameterBufferH264 *pSequenceParams;
pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
H264_VUI_PARAMS *pVUI_Params = &(ctx->VUI_Params);
H264_CROP_PARAMS sCrop;
int i;
unsigned int frame_size;
unsigned int max_bps;
ASSERT(obj_buffer->type == VAEncSequenceParameterBufferType);
ASSERT(obj_buffer->num_elements == 1);
ASSERT(obj_buffer->size == sizeof(VAEncSequenceParameterBufferH264));
if ((obj_buffer->num_elements != 1) ||
(obj_buffer->size != sizeof(VAEncSequenceParameterBufferH264))) {
return VA_STATUS_ERROR_UNKNOWN;
}
if(ctx->sRCParams.FrameRate == 0)
ctx->sRCParams.FrameRate = 30;
ctx->obj_context->frame_count = 0;
pSequenceParams = (VAEncSequenceParameterBufferH264 *) obj_buffer->buffer_data;
obj_buffer->buffer_data = NULL;
obj_buffer->size = 0;
if (!pSequenceParams->bits_per_second) {
pSequenceParams->bits_per_second = ctx->Height * ctx->Width * 30 * 12;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "bits_per_second is 0, set to %d\n",
pSequenceParams->bits_per_second);
}
ctx->sRCParams.bBitrateChanged =
(pSequenceParams->bits_per_second == ctx->sRCParams.BitsPerSecond ?
IMG_FALSE : IMG_TRUE);
if (pSequenceParams->bits_per_second > TOPAZ_H264_MAX_BITRATE) {
ctx->sRCParams.BitsPerSecond = TOPAZ_H264_MAX_BITRATE;
drv_debug_msg(VIDEO_DEBUG_GENERAL, " bits_per_second(%d) exceeds \
the maximum bitrate, set it with %d\n",
pSequenceParams->bits_per_second,
TOPAZ_H264_MAX_BITRATE);
}
/* According to Table A-1 Level limits, if resolution is bigger than 625SD,
min compression ratio is 4, otherwise min compression ratio is 2 */
max_bps = (ctx->Width * ctx->Height * 3 / 2 ) * 8 * ctx->sRCParams.FrameRate;
if (ctx->Width > 720)
max_bps /= 4;
else
max_bps /= 2;
drv_debug_msg(VIDEO_DEBUG_GENERAL, " width %d height %d, frame rate %d\n",
ctx->Width, ctx->Height, ctx->sRCParams.FrameRate);
if (pSequenceParams->bits_per_second > max_bps) {
drv_debug_msg(VIDEO_DEBUG_ERROR,
"Invalid bitrate %d, violate ITU-T Rec. H.264 (03/2005) A.3.1"
"\n clip to %d bps\n", pSequenceParams->bits_per_second, max_bps);
ctx->sRCParams.BitsPerSecond = max_bps;
} else {
/* See 110% target bitrate for VCM. Otherwise, the resulted bitrate is much lower
than target bitrate */
if (ctx->eCodec == IMG_CODEC_H264_VCM)
pSequenceParams->bits_per_second =
pSequenceParams->bits_per_second / 100 * 110;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Bitrate is set to %d\n",
pSequenceParams->bits_per_second);
ctx->sRCParams.BitsPerSecond = pSequenceParams->bits_per_second;
}
/*if (ctx->sRCParams.IntraFreq != pSequenceParams->intra_period)
ctx->sRCParams.bBitrateChanged = IMG_TRUE;*/
ctx->sRCParams.IDRFreq = pSequenceParams->intra_idr_period;
ctx->sRCParams.Slices = ctx->Slices;
ctx->sRCParams.QCPOffset = 0;
if (ctx->sRCParams.IntraFreq != pSequenceParams->intra_period
&& ctx->raw_frame_count != 0
&& ctx->sRCParams.IntraFreq != 0
&& ((ctx->obj_context->frame_count + 1) % ctx->sRCParams.IntraFreq) != 0
&& (!ctx->sRCParams.bDisableFrameSkipping)) {
drv_debug_msg(VIDEO_DEBUG_ERROR,
"Changing intra period value in the middle of a GOP is\n"
"not allowed if frame skip isn't disabled.\n"
"it can cause I frame been skipped\n");
free(pSequenceParams);
return VA_STATUS_ERROR_INVALID_PARAMETER;
}
else
ctx->sRCParams.IntraFreq = pSequenceParams->intra_period;
frame_size = ctx->sRCParams.BitsPerSecond / ctx->sRCParams.FrameRate;
if (ctx->bInserHRDParams &&
ctx->buffer_size != 0 && ctx->initial_buffer_fullness != 0) {
ctx->sRCParams.BufferSize = ctx->buffer_size;
ctx->sRCParams.InitialLevel = ctx->buffer_size - ctx->initial_buffer_fullness;
ctx->sRCParams.InitialDelay = ctx->initial_buffer_fullness;
}
else {
ctx->buffer_size = ctx->sRCParams.BitsPerSecond;
ctx->initial_buffer_fullness = ctx->sRCParams.BitsPerSecond;
ctx->sRCParams.BufferSize = ctx->buffer_size;
ctx->sRCParams.InitialLevel = (3 * ctx->sRCParams.BufferSize) >> 4;
/* Aligned with target frame size */
ctx->sRCParams.InitialLevel += (frame_size / 2);
ctx->sRCParams.InitialLevel /= frame_size;
ctx->sRCParams.InitialLevel *= frame_size;
ctx->sRCParams.InitialDelay = ctx->buffer_size - ctx->sRCParams.InitialLevel;
}
if (ctx->raw_frame_count == 0) {
for (i = (ctx->ParallelCores - 1); i >= 0; i--)
pnw_set_bias(ctx, i);
}
pVUI_Params->bit_rate_value_minus1 = ctx->sRCParams.BitsPerSecond / 64 - 1;
pVUI_Params->cbp_size_value_minus1 = ctx->sRCParams.BufferSize / 64 - 1;
if (IMG_CODEC_H264_CBR != ctx->eCodec ||
ctx->sRCParams.bDisableBitStuffing ||
ctx->sRCParams.bDisableFrameSkipping)
pVUI_Params->CBR = 0;
else
pVUI_Params->CBR = 1;
pVUI_Params->initial_cpb_removal_delay_length_minus1 = BPH_SEI_NAL_INITIAL_CPB_REMOVAL_DELAY_SIZE - 1;
pVUI_Params->cpb_removal_delay_length_minus1 = PTH_SEI_NAL_CPB_REMOVAL_DELAY_SIZE - 1;
pVUI_Params->dpb_output_delay_length_minus1 = PTH_SEI_NAL_DPB_OUTPUT_DELAY_SIZE - 1;
pVUI_Params->time_offset_length = 24;
ctx->bInsertVUI = pSequenceParams->vui_parameters_present_flag ? IMG_TRUE: IMG_FALSE;
if (ctx->bInsertVUI) {
if (pSequenceParams->num_units_in_tick !=0 && pSequenceParams->time_scale !=0
&& (pSequenceParams->time_scale > pSequenceParams->num_units_in_tick) ) {
pVUI_Params->Time_Scale = pSequenceParams->time_scale;
pVUI_Params->num_units_in_tick = pSequenceParams->num_units_in_tick;
}
else {
pVUI_Params->num_units_in_tick = 1;
pVUI_Params->Time_Scale = ctx->sRCParams.FrameRate * 2;
}
}
if (ctx->bInsertVUI && pSequenceParams->vui_fields.bits.aspect_ratio_info_present_flag &&
(pSequenceParams->aspect_ratio_idc == 0xff /* Extended_SAR */)) {
pVUI_Params->aspect_ratio_info_present_flag = IMG_TRUE;
pVUI_Params->aspect_ratio_idc = 0xff;
pVUI_Params->sar_width = pSequenceParams->sar_width;
pVUI_Params->sar_height = pSequenceParams->sar_height;
}
sCrop.bClip = pSequenceParams->frame_cropping_flag;
sCrop.LeftCropOffset = 0;
sCrop.RightCropOffset = 0;
sCrop.TopCropOffset = 0;
sCrop.BottomCropOffset = 0;
if (!sCrop.bClip) {
if (ctx->RawHeight & 0xf) {
sCrop.bClip = IMG_TRUE;
sCrop.BottomCropOffset = (((ctx->RawHeight + 0xf) & (~0xf)) - ctx->RawHeight) / 2;
}
if (ctx->RawWidth & 0xf) {
sCrop.bClip = IMG_TRUE;
sCrop.RightCropOffset = (((ctx->RawWidth + 0xf) & (~0xf)) - ctx->RawWidth) / 2;
}
} else {
sCrop.LeftCropOffset = pSequenceParams->frame_crop_left_offset;
sCrop.RightCropOffset = pSequenceParams->frame_crop_right_offset;
sCrop.TopCropOffset = pSequenceParams->frame_crop_top_offset;
sCrop.BottomCropOffset = pSequenceParams->frame_crop_bottom_offset;
}
/* sequence header is always inserted */
memset(cmdbuf->header_mem_p + ctx->seq_header_ofs,
0,
HEADER_SIZE);
/*
if (ctx->bInserHRDParams) {
memset(cmdbuf->header_mem_p + ctx->aud_header_ofs,
0,
HEADER_SIZE);
pnw__H264_prepare_AUD_header(cmdbuf->header_mem_p + ctx->aud_header_ofs);
pnw_cmdbuf_insert_command_package(ctx->obj_context,
ctx->ParallelCores - 1,
MTX_CMDID_DO_HEADER,
&cmdbuf->header_mem,
ctx->aud_header_ofs);
}
*/
if (ctx->eCodec == IMG_CODEC_H264_NO_RC)
pnw__H264_prepare_sequence_header(cmdbuf->header_mem_p + ctx->seq_header_ofs,
pSequenceParams->picture_width_in_mbs,
pSequenceParams->picture_height_in_mbs,
pSequenceParams->vui_parameters_present_flag,
pSequenceParams->vui_parameters_present_flag ? (pVUI_Params) : NULL,
&sCrop,
pSequenceParams->level_idc, ctx->profile_idc);
else
pnw__H264_prepare_sequence_header(cmdbuf->header_mem_p + ctx->seq_header_ofs,
pSequenceParams->picture_width_in_mbs,
pSequenceParams->picture_height_in_mbs,
pSequenceParams->vui_parameters_present_flag,
pSequenceParams->vui_parameters_present_flag ? (pVUI_Params) : NULL,
&sCrop,
pSequenceParams->level_idc, ctx->profile_idc);
/*Periodic IDR need SPS. We save the sequence header here*/
if (ctx->sRCParams.IDRFreq != 0) {
if (NULL == ctx->save_seq_header_p) {
ctx->save_seq_header_p = malloc(HEADER_SIZE);
if (NULL == ctx->save_seq_header_p) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "Ran out of memory!\n");
free(pSequenceParams);
return VA_STATUS_ERROR_ALLOCATION_FAILED;
}
memcpy((unsigned char *)ctx->save_seq_header_p,
(unsigned char *)(cmdbuf->header_mem_p + ctx->seq_header_ofs),
HEADER_SIZE);
}
}
ctx->none_vcl_nal++;
cmdbuf->cmd_idx_saved[PNW_CMDBUF_SEQ_HEADER_IDX] = cmdbuf->cmd_idx;
/* Send to the last core as this will complete first */
pnw_cmdbuf_insert_command_package(ctx->obj_context,
ctx->ParallelCores - 1,
MTX_CMDID_DO_HEADER,
&cmdbuf->header_mem,
ctx->seq_header_ofs);
free(pSequenceParams);
return VA_STATUS_SUCCESS;
}
static VAStatus pnw__H264ES_insert_SEI_buffer_period(context_ENC_p ctx)
{
unsigned int ui32nal_initial_cpb_removal_delay;
unsigned int ui32nal_initial_cpb_removal_delay_offset;
pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
ui32nal_initial_cpb_removal_delay =
90000 * (1.0 * ctx->sRCParams.InitialDelay / ctx->sRCParams.BitsPerSecond);
ui32nal_initial_cpb_removal_delay_offset =
90000 * (1.0 * ctx->buffer_size / ctx->sRCParams.BitsPerSecond)
- ui32nal_initial_cpb_removal_delay;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Insert SEI buffer period message with "
"ui32nal_initial_cpb_removal_delay(%d) and "
"ui32nal_initial_cpb_removal_delay_offset(%d)\n",
ui32nal_initial_cpb_removal_delay,
ui32nal_initial_cpb_removal_delay_offset);
memset(cmdbuf->header_mem_p + ctx->sei_buf_prd_ofs,
0,
HEADER_SIZE);
pnw__H264_prepare_SEI_buffering_period_header(
(MTX_HEADER_PARAMS *)(cmdbuf->header_mem_p + ctx->sei_buf_prd_ofs),
1, //ui8NalHrdBpPresentFlag,
0, //ui8nal_cpb_cnt_minus1,
1 + ctx->VUI_Params.initial_cpb_removal_delay_length_minus1, //ui8nal_initial_cpb_removal_delay_length,
ui32nal_initial_cpb_removal_delay, //ui32nal_initial_cpb_removal_delay,
ui32nal_initial_cpb_removal_delay_offset, //ui32nal_initial_cpb_removal_delay_offset,
0, //ui8VclHrdBpPresentFlag,
NOT_USED_BY_TOPAZ, //ui8vcl_cpb_cnt_minus1,
0, //ui32vcl_initial_cpb_removal_delay,
0 //ui32vcl_initial_cpb_removal_delay_offset
);
cmdbuf->cmd_idx_saved[PNW_CMDBUF_SEI_BUF_PERIOD_IDX] = cmdbuf->cmd_idx;
pnw_cmdbuf_insert_command_package(ctx->obj_context,
ctx->ParallelCores - 1,
MTX_CMDID_DO_HEADER,
&cmdbuf->header_mem,
ctx->sei_buf_prd_ofs);
ctx->none_vcl_nal++;
return VA_STATUS_SUCCESS;
}
static VAStatus pnw__H264ES_insert_SEI_pic_timing(context_ENC_p ctx)
{
pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
uint32_t ui32cpb_removal_delay;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Insert SEI picture timing message. \n");
memset(cmdbuf->header_mem_p + ctx->sei_pic_tm_ofs,
0,
HEADER_SIZE);
/* ui32cpb_removal_delay is zero for 1st frame and will be reset
* after a IDR frame */
if (ctx->obj_context->frame_count == 0) {
if (ctx->raw_frame_count == 0)
ui32cpb_removal_delay = 0;
else
ui32cpb_removal_delay =
ctx->sRCParams.IDRFreq * ctx->sRCParams.IntraFreq * 2;
} else
ui32cpb_removal_delay = 2 * ctx->obj_context->frame_count;
pnw__H264_prepare_SEI_picture_timing_header(
(MTX_HEADER_PARAMS *)(cmdbuf->header_mem_p + ctx->sei_pic_tm_ofs),
1,
ctx->VUI_Params.cpb_removal_delay_length_minus1,
ctx->VUI_Params.dpb_output_delay_length_minus1,
ui32cpb_removal_delay, //ui32cpb_removal_delay,
2, //ui32dpb_output_delay,
0, //ui8pic_struct_present_flag,
0, //ui8pic_struct,
0, //ui8NumClockTS,
0, //*aui8clock_timestamp_flag,
0, //ui8full_timestamp_flag,
0, //ui8seconds_flag,
0, //ui8minutes_flag,
0, //ui8hours_flag,
0, //ui8seconds_value,
0, //ui8minutes_value,
0, //ui8hours_value,
0, //ui8ct_type,
0, //ui8nuit_field_based_flag,
0, //ui8counting_type,
0, //ui8discontinuity_flag,
0, //ui8cnt_dropped_flag,
0, //ui8n_frames,
0, //ui8time_offset_length,
0 //i32time_offset)
);
cmdbuf->cmd_idx_saved[PNW_CMDBUF_SEI_PIC_TIMING_IDX] = cmdbuf->cmd_idx;
pnw_cmdbuf_insert_command_package(ctx->obj_context,
ctx->ParallelCores - 1,
MTX_CMDID_DO_HEADER,
&cmdbuf->header_mem,
ctx->sei_pic_tm_ofs);
ctx->none_vcl_nal++;
return VA_STATUS_SUCCESS;
}
#if PSB_MFLD_DUMMY_CODE
static VAStatus pnw__H264ES_insert_SEI_FPA_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
VAEncPackedHeaderParameterBuffer *sei_param_buf = (VAEncPackedHeaderParameterBuffer *)obj_buffer->buffer_data;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Insert SEI frame packing arrangement message. \n");
ctx->sei_pic_data_size = sei_param_buf->bit_length/8;
return VA_STATUS_SUCCESS;
}
static VAStatus pnw__H264ES_insert_SEI_FPA_data(context_ENC_p ctx, object_buffer_p obj_buffer)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
char *sei_data_buf;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Insert SEI frame packing arrangement message. \n");
memset(cmdbuf->header_mem_p + ctx->sei_pic_fpa_ofs,
0,
HEADER_SIZE);
sei_data_buf = (char *)obj_buffer->buffer_data;
pnw__H264_prepare_SEI_FPA_header((MTX_HEADER_PARAMS *)(cmdbuf->header_mem_p + ctx->sei_pic_fpa_ofs), sei_data_buf, ctx->sei_pic_data_size);
pnw_cmdbuf_insert_command_package(ctx->obj_context,
ctx->ParallelCores - 1,
MTX_CMDID_DO_HEADER,
&cmdbuf->header_mem,
ctx->sei_pic_fpa_ofs);
return VA_STATUS_SUCCESS;
}
#endif
static VAStatus pnw__H264ES_process_picture_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
int i;
VAEncPictureParameterBufferH264 *pBuffer;
int need_sps = 0;
pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
ASSERT(obj_buffer->type == VAEncPictureParameterBufferType);
if ((obj_buffer->num_elements != 1) ||
(obj_buffer->size != sizeof(VAEncPictureParameterBufferH264))) {
return VA_STATUS_ERROR_UNKNOWN;
}
/* Transfer ownership of VAEncPictureParameterBufferH264 data */
pBuffer = (VAEncPictureParameterBufferH264 *) obj_buffer->buffer_data;
obj_buffer->buffer_data = NULL;
obj_buffer->size = 0;
ctx->ref_surface = SURFACE(pBuffer->ReferenceFrames[0].picture_id);
ctx->dest_surface = SURFACE(pBuffer->CurrPic.picture_id);
ctx->coded_buf = BUFFER(pBuffer->coded_buf);
//ASSERT(ctx->Width == pBuffer->picture_width);
//ASSERT(ctx->Height == pBuffer->picture_height);
if (NULL == ctx->coded_buf) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s L%d Invalid coded buffer handle\n", __FUNCTION__, __LINE__);
free(pBuffer);
return VA_STATUS_ERROR_INVALID_BUFFER;
}
if ((ctx->sRCParams.IntraFreq != 0) && (ctx->sRCParams.IDRFreq != 0)) { /* period IDR is desired */
unsigned int is_intra = 0;
unsigned int intra_cnt = 0;
ctx->force_idr_h264 = 0;
if ((ctx->obj_context->frame_count % ctx->sRCParams.IntraFreq) == 0) {
is_intra = 1; /* suppose current frame is I frame */
intra_cnt = ctx->obj_context->frame_count / ctx->sRCParams.IntraFreq;
}
/* current frame is I frame (suppose), and an IDR frame is desired*/
if ((is_intra) && ((intra_cnt % ctx->sRCParams.IDRFreq) == 0)) {
ctx->force_idr_h264 = 1;
/*When two consecutive access units in decoding order are both IDR access
* units, the value of idr_pic_id in the slices of the first such IDR
* access unit shall differ from the idr_pic_id in the second such IDR
* access unit. We set it with 1 or 0 alternately.*/
ctx->idr_pic_id = 1 - ctx->idr_pic_id;
/* it is periodic IDR in the middle of one sequence encoding, need SPS */
if (ctx->obj_context->frame_count > 0)
need_sps = 1;
ctx->obj_context->frame_count = 0;
}
}
/* If VUI header isn't enabled, we'll igore the request for HRD header insertion */
if (ctx->bInserHRDParams)
ctx->bInserHRDParams = ctx->bInsertVUI;
/* For H264, PicHeader only needed in the first picture*/
if (!(ctx->obj_context->frame_count)) {
cmdbuf = ctx->obj_context->pnw_cmdbuf;
if (need_sps) {
drv_debug_msg(VIDEO_DEBUG_GENERAL, "TOPAZ: insert a SPS before IDR frame\n");
/* reuse the previous SPS */
memcpy((unsigned char *)(cmdbuf->header_mem_p + ctx->seq_header_ofs),
(unsigned char *)ctx->save_seq_header_p,
HEADER_SIZE);
cmdbuf->cmd_idx_saved[PNW_CMDBUF_SEQ_HEADER_IDX] = cmdbuf->cmd_idx;
/* Send to the last core as this will complete first */
pnw_cmdbuf_insert_command_package(ctx->obj_context,
ctx->ParallelCores - 1,
MTX_CMDID_DO_HEADER,
&cmdbuf->header_mem,
ctx->seq_header_ofs);
ctx->none_vcl_nal++;
}
if (ctx->bInserHRDParams) {
pnw__H264ES_insert_SEI_buffer_period(ctx);
pnw__H264ES_insert_SEI_pic_timing(ctx);
}
pnw__H264_prepare_picture_header(cmdbuf->header_mem_p + ctx->pic_header_ofs, IMG_FALSE, ctx->sRCParams.QCPOffset);
cmdbuf->cmd_idx_saved[PNW_CMDBUF_PIC_HEADER_IDX] = cmdbuf->cmd_idx;
/* Send to the last core as this will complete first */
pnw_cmdbuf_insert_command_package(ctx->obj_context,
ctx->ParallelCores - 1,
MTX_CMDID_DO_HEADER,
&cmdbuf->header_mem,
ctx->pic_header_ofs);
ctx->none_vcl_nal++;
}
else if (ctx->bInserHRDParams)
pnw__H264ES_insert_SEI_pic_timing(ctx);
if (ctx->ParallelCores == 1) {
ctx->coded_buf_per_slice = 0;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "TOPAZ: won't splite coded buffer(%d) since only one slice being encoded\n",
ctx->coded_buf->size);
} else {
/*Make sure DMA start is 128bits alignment*/
ctx->coded_buf_per_slice = (ctx->coded_buf->size / ctx->ParallelCores) & (~0xf) ;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "TOPAZ: the size of coded_buf per slice %d( Total %d) \n", ctx->coded_buf_per_slice,
ctx->coded_buf->size);
}
/* Prepare START_PICTURE params */
/* FIXME is really need multiple picParams? Need multiple calculate for each? */
for (i = (ctx->ParallelCores - 1); i >= 0; i--)
vaStatus = pnw_RenderPictureParameter(ctx, i);
free(pBuffer);
return vaStatus;
}
static VAStatus pnw__H264ES_encode_one_slice(context_ENC_p ctx,
VAEncSliceParameterBuffer *pBuffer)
{
pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
unsigned int MBSkipRun, FirstMBAddress;
unsigned char deblock_idc;
unsigned char is_intra = 0;
int slice_param_idx;
PIC_PARAMS *psPicParams = (PIC_PARAMS *)(cmdbuf->pic_params_p);
VAStatus vaStatus = VA_STATUS_SUCCESS;
/*Slice encoding Order:
*1.Insert Do header command
*2.setup InRowParams
*3.setup Slice params
*4.Insert Do slice command
* */
if (pBuffer->slice_height > (ctx->Height / 16) ||
pBuffer->start_row_number > (ctx->Height / 16) ||
(pBuffer->slice_height + pBuffer->start_row_number) > (ctx->Height / 16)) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "slice height %d or start row number %d is too large",
pBuffer->slice_height, pBuffer->start_row_number);
return VA_STATUS_ERROR_INVALID_PARAMETER;
}
MBSkipRun = (pBuffer->slice_height * ctx->Width) / 16;
deblock_idc = pBuffer->slice_flags.bits.disable_deblocking_filter_idc;
/*If the frame is skipped, it shouldn't be a I frame*/
if (ctx->force_idr_h264 || (ctx->obj_context->frame_count == 0)) {
is_intra = 1;
} else
is_intra = (ctx->sRCParams.RCEnable && ctx->sRCParams.FrameSkip) ? 0 : pBuffer->slice_flags.bits.is_intra;
FirstMBAddress = (pBuffer->start_row_number * ctx->Width) / 16;
memset(cmdbuf->header_mem_p + ctx->slice_header_ofs
+ ctx->obj_context->slice_count * HEADER_SIZE,
0,
HEADER_SIZE);
/* Insert Do Header command, relocation is needed */
pnw__H264_prepare_slice_header(cmdbuf->header_mem_p + ctx->slice_header_ofs
+ ctx->obj_context->slice_count * HEADER_SIZE,
is_intra,
pBuffer->slice_flags.bits.disable_deblocking_filter_idc,
ctx->obj_context->frame_count,
FirstMBAddress,
MBSkipRun,
0,
ctx->force_idr_h264,
IMG_FALSE,
IMG_FALSE,
ctx->idr_pic_id);
/* ensure that this slice is consequtive to that last processed by the target core */
/*
ASSERT( -1 == ctx->LastSliceNum[ctx->SliceToCore]
|| ctx->obj_context->slice_count == 1 + ctx->LastSliceNum[ctx->SliceToCore] );
*/
/* note the slice number the target core is now processing */
ctx->LastSliceNum[ctx->SliceToCore] = ctx->obj_context->slice_count;
pnw_cmdbuf_insert_command_package(ctx->obj_context,
ctx->SliceToCore,
MTX_CMDID_DO_HEADER,
&cmdbuf->header_mem,
ctx->slice_header_ofs + ctx->obj_context->slice_count * HEADER_SIZE);
if (!(ctx->sRCParams.RCEnable && ctx->sRCParams.FrameSkip)) {
/*Only reset on the first frame. It's more effective than DDK. Have confirmed with IMG*/
if (ctx->obj_context->frame_count == 0)
pnw_reset_encoder_params(ctx);
if ((pBuffer->start_row_number == 0) && pBuffer->slice_flags.bits.is_intra) {
ctx->BelowParamsBufIdx = (ctx->BelowParamsBufIdx + 1) & 0x1;
}
slice_param_idx = (pBuffer->slice_flags.bits.is_intra ? 0 : 1) * ctx->slice_param_num
+ ctx->obj_context->slice_count;
if (VAEncSliceParameter_Equal(&ctx->slice_param_cache[slice_param_idx], pBuffer) == 0) {
/* cache current param parameters */
memcpy(&ctx->slice_param_cache[slice_param_idx],
pBuffer, sizeof(VAEncSliceParameterBuffer));
/* Setup InParams value*/
pnw_setup_slice_params(ctx,
pBuffer->start_row_number * 16,
pBuffer->slice_height * 16,
pBuffer->slice_flags.bits.is_intra,
ctx->obj_context->frame_count > 0,
psPicParams->sInParams.SeInitQP);
}
/* Insert do slice command and setup related buffer value */
pnw__send_encode_slice_params(ctx,
pBuffer->slice_flags.bits.is_intra,
pBuffer->start_row_number * 16,
deblock_idc,
ctx->obj_context->frame_count,
pBuffer->slice_height * 16,
ctx->obj_context->slice_count);
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Now frame_count/slice_count is %d/%d\n",
ctx->obj_context->frame_count, ctx->obj_context->slice_count);
}
ctx->obj_context->slice_count++;
return vaStatus;
}
/* convert from VAEncSliceParameterBufferH264 to VAEncSliceParameterBuffer */
static VAStatus pnw__convert_sliceparameter_buffer(VAEncSliceParameterBufferH264 *pBufferH264,
VAEncSliceParameterBuffer *pBuffer,
int picture_width_in_mbs,
unsigned int num_elemenent)
{
unsigned int i;
for (i = 0; i < num_elemenent; i++) {
pBuffer->start_row_number = pBufferH264->macroblock_address / picture_width_in_mbs;
pBuffer->slice_height = pBufferH264->num_macroblocks / picture_width_in_mbs;
pBuffer->slice_flags.bits.is_intra =
(((pBufferH264->slice_type == 2) || (pBufferH264->slice_type == 7)) ? 1 : 0);
pBuffer->slice_flags.bits.disable_deblocking_filter_idc = pBufferH264->disable_deblocking_filter_idc;
/* next conversion */
pBuffer++;
pBufferH264++;
}
return 0;
}
static VAStatus pnw__H264ES_process_slice_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
/* Prepare InParams for macros of current slice, insert slice header, insert do slice command */
VAEncSliceParameterBuffer *pBuf_per_core = NULL, *pBuffer = NULL;
pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
PIC_PARAMS *psPicParams = (PIC_PARAMS *)(cmdbuf->pic_params_p);
bool pBufferAlloced = false;
unsigned int i, j, slice_per_core;
VAStatus vaStatus = VA_STATUS_SUCCESS;
ASSERT(obj_buffer->type == VAEncSliceParameterBufferType);
if (obj_buffer->num_elements > (ctx->Height / 16)) {
vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
goto out2;
}
cmdbuf = ctx->obj_context->pnw_cmdbuf;
psPicParams = (PIC_PARAMS *)cmdbuf->pic_params_p;
/* Transfer ownership of VAEncPictureParameterBuffer data */
if (obj_buffer->size == sizeof(VAEncSliceParameterBufferH264)) {
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Receive VAEncSliceParameterBufferH264 buffer");
pBuffer = calloc(obj_buffer->num_elements, sizeof(VAEncSliceParameterBuffer));
if (pBuffer == NULL) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "Run out of memory!\n");
vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
goto out2;
}
pBufferAlloced = true;
pnw__convert_sliceparameter_buffer((VAEncSliceParameterBufferH264 *)obj_buffer->buffer_data,
pBuffer,
ctx->Width / 16,
obj_buffer->num_elements);
} else if (obj_buffer->size == sizeof(VAEncSliceParameterBuffer)) {
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Receive VAEncSliceParameterBuffer buffer");
pBuffer = (VAEncSliceParameterBuffer *) obj_buffer->buffer_data;
} else {
drv_debug_msg(VIDEO_DEBUG_ERROR, "Buffer size(%d) is wrong. It should be %d or %d\n",
obj_buffer->size, sizeof(VAEncSliceParameterBuffer),
sizeof(VAEncSliceParameterBufferH264));
vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
goto out2;
}
obj_buffer->size = 0;
/*In case the slice number changes*/
if ((ctx->slice_param_cache != NULL) && (obj_buffer->num_elements != ctx->slice_param_num)) {
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Slice number changes. Previous value is %d. Now it's %d\n",
ctx->slice_param_num, obj_buffer->num_elements);
free(ctx->slice_param_cache);
ctx->slice_param_cache = NULL;
ctx->slice_param_num = 0;
}
if (NULL == ctx->slice_param_cache) {
ctx->slice_param_num = obj_buffer->num_elements;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Allocate %d VAEncSliceParameterBuffer cache buffers\n", 2 * ctx->slice_param_num);
ctx->slice_param_cache = calloc(2 * ctx->slice_param_num, sizeof(VAEncSliceParameterBuffer));
if (NULL == ctx->slice_param_cache) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "Run out of memory!\n");
/* free the converted VAEncSliceParameterBuffer */
if (pBufferAlloced)
free(pBuffer);
free(obj_buffer->buffer_data);
return VA_STATUS_ERROR_ALLOCATION_FAILED;
}
}
ctx->sRCParams.Slices = obj_buffer->num_elements;
if (getenv("PSB_VIDEO_SIG_CORE") == NULL) {
if ((ctx->ParallelCores == 2) && (obj_buffer->num_elements == 1)) {
/*Need to replace unneccesary MTX_CMDID_STARTPICs with MTX_CMDID_PAD*/
for (i = 0; i < (ctx->ParallelCores - 1); i++) {
*(cmdbuf->cmd_idx_saved[PNW_CMDBUF_START_PIC_IDX] + i * 4) &= (~MTX_CMDWORD_ID_MASK);
*(cmdbuf->cmd_idx_saved[PNW_CMDBUF_START_PIC_IDX] + i * 4) |= MTX_CMDID_PAD;
}
drv_debug_msg(VIDEO_DEBUG_GENERAL, " Remove unneccesary %d MTX_CMDID_STARTPIC commands from cmdbuf\n",
ctx->ParallelCores - obj_buffer->num_elements);
ctx->ParallelCores = obj_buffer->num_elements;
/* All header generation commands should be send to core 0*/
for (i = PNW_CMDBUF_SEQ_HEADER_IDX; i < PNW_CMDBUF_SAVING_MAX; i++) {
if (cmdbuf->cmd_idx_saved[i] != 0)
*(cmdbuf->cmd_idx_saved[i]) &=
~(MTX_CMDWORD_CORE_MASK << MTX_CMDWORD_CORE_SHIFT);
}
ctx->SliceToCore = ctx->ParallelCores - 1;
}
}
slice_per_core = obj_buffer->num_elements / ctx->ParallelCores;
pBuf_per_core = pBuffer;
for (i = 0; i < slice_per_core; i++) {
pBuffer = pBuf_per_core;
for (j = 0; j < ctx->ParallelCores; j++) {
vaStatus = pnw__H264ES_encode_one_slice(ctx, pBuffer);
if (vaStatus != VA_STATUS_SUCCESS)
goto out1;
if (0 == ctx->SliceToCore) {
ctx->SliceToCore = ctx->ParallelCores;
}
ctx->SliceToCore--;
ASSERT(ctx->obj_context->slice_count < MAX_SLICES_PER_PICTURE);
/*Move to the next buffer which will be sent to core j*/
pBuffer += slice_per_core;
}
pBuf_per_core++; /* Move to the next buffer */
}
/*Cope with last slice when slice number is odd and parallelCores is even*/
if (obj_buffer->num_elements > (slice_per_core * ctx->ParallelCores)) {
ctx->SliceToCore = 0;
pBuffer -= slice_per_core;
pBuffer ++;
vaStatus = pnw__H264ES_encode_one_slice(ctx, pBuffer);
}
out1:
/* free the converted VAEncSliceParameterBuffer */
if (pBufferAlloced)
free(pBuffer);
out2:
free(obj_buffer->buffer_data);
obj_buffer->buffer_data = NULL;
return vaStatus;
}
static VAStatus pnw__H264ES_process_misc_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
/* Prepare InParams for macros of current slice, insert slice header, insert do slice command */
VAEncMiscParameterBuffer *pBuffer;
VAEncMiscParameterRateControl *rate_control_param;
VAEncMiscParameterAIR *air_param;
VAEncMiscParameterMaxSliceSize *max_slice_size_param;
VAEncMiscParameterFrameRate *frame_rate_param;
VAEncMiscParameterHRD *hrd_param;
VAStatus vaStatus = VA_STATUS_SUCCESS;
unsigned int max_bps;
unsigned int frame_size;
ASSERT(obj_buffer->type == VAEncMiscParameterBufferType);
/* Transfer ownership of VAEncMiscParameterBuffer data */
pBuffer = (VAEncMiscParameterBuffer *) obj_buffer->buffer_data;
obj_buffer->size = 0;
if (ctx->eCodec != IMG_CODEC_H264_VCM
&& (pBuffer->type != VAEncMiscParameterTypeHRD
&& pBuffer->type != VAEncMiscParameterTypeRateControl
&& pBuffer->type != VAEncMiscParameterTypeFrameRate)) {
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Buffer type %d isn't supported in none VCM mode.\n",
pBuffer->type);
free(obj_buffer->buffer_data);
obj_buffer->buffer_data = NULL;
return VA_STATUS_SUCCESS;
}
switch (pBuffer->type) {
case VAEncMiscParameterTypeFrameRate:
frame_rate_param = (VAEncMiscParameterFrameRate *)pBuffer->data;
if (frame_rate_param->framerate < 1 || frame_rate_param->framerate > 65535) {
vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
break;
}
if (ctx->sRCParams.FrameRate == frame_rate_param->framerate)
break;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "frame rate changed from %d to %d\n",
ctx->sRCParams.FrameRate,
frame_rate_param->framerate);
ctx->sRCParams.FrameRate = frame_rate_param->framerate;
ctx->sRCParams.bBitrateChanged = IMG_TRUE;
ctx->sRCParams.FrameRate = (frame_rate_param->framerate < 1) ? 1 :
((65535 < frame_rate_param->framerate) ? 65535 : frame_rate_param->framerate);
break;
case VAEncMiscParameterTypeRateControl:
rate_control_param = (VAEncMiscParameterRateControl *)pBuffer->data;
/* Currently, none VCM mode only supports frame skip and bit stuffing
* disable flag and doesn't accept other parameters in
* buffer of VAEncMiscParameterTypeRateControl type */
if (rate_control_param->rc_flags.value != 0 || ctx->raw_frame_count == 0) {
if (rate_control_param->rc_flags.bits.disable_frame_skip)
ctx->sRCParams.bDisableFrameSkipping = IMG_TRUE;
if (rate_control_param->rc_flags.bits.disable_bit_stuffing)
ctx->sRCParams.bDisableBitStuffing = IMG_TRUE;
drv_debug_msg(VIDEO_DEBUG_GENERAL,
"bDisableFrameSkipping is %d and bDisableBitStuffing is %d\n",
ctx->sRCParams.bDisableFrameSkipping, ctx->sRCParams.bDisableBitStuffing);
}
if (rate_control_param->initial_qp > 51 ||
rate_control_param->min_qp > 51) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "Initial_qp(%d) and min_qpinitial_qp(%d) "
"are invalid.\nQP shouldn't be larger than 51 for H264\n",
rate_control_param->initial_qp, rate_control_param->min_qp);
vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
break;
}
if (rate_control_param->window_size > 2000) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "window_size is too much!\n");
vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
break;
}
/* Check if any none-zero RC parameter is changed*/
if ((rate_control_param->bits_per_second == 0 ||
rate_control_param->bits_per_second == ctx->sRCParams.BitsPerSecond) &&
(rate_control_param->window_size == 0 ||
ctx->sRCParams.BufferSize == ctx->sRCParams.BitsPerSecond / 1000 * rate_control_param->window_size) &&
(ctx->sRCParams.MinQP == rate_control_param->min_qp) &&
(ctx->sRCParams.InitialQp == rate_control_param->initial_qp) &&
(rate_control_param->basic_unit_size == 0 ||
ctx->sRCParams.BUSize == rate_control_param->basic_unit_size)) {
drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s No RC parameter is changed\n",
__FUNCTION__);
break;
}
else if (ctx->raw_frame_count != 0 || ctx->eCodec == IMG_CODEC_H264_VCM)
ctx->sRCParams.bBitrateChanged = IMG_TRUE;
/* The initial target bitrate is set by Sequence parameter buffer.
Here is for changed bitrate only */
if (rate_control_param->bits_per_second > TOPAZ_H264_MAX_BITRATE) {
drv_debug_msg(VIDEO_DEBUG_ERROR, " bits_per_second(%d) exceeds \
the maximum bitrate, set it with %d\n",
rate_control_param->bits_per_second,
TOPAZ_H264_MAX_BITRATE);
break;
}
/* The initial target bitrate is set by Sequence parameter buffer.
Here is for changed bitrate only */
if (rate_control_param->bits_per_second != 0 &&
ctx->raw_frame_count != 0) {
drv_debug_msg(VIDEO_DEBUG_GENERAL,
"bitrate is changed from %d to %d on frame %d\n",
ctx->sRCParams.BitsPerSecond,
rate_control_param->bits_per_second,
ctx->raw_frame_count);
max_bps = (ctx->Width * ctx->Height * 3 / 2 ) * 8 * ctx->sRCParams.FrameRate;
if (ctx->Width > 720)
max_bps /= 4;
else
max_bps /= 2;
drv_debug_msg(VIDEO_DEBUG_GENERAL, " width %d height %d, frame rate %d\n",
ctx->Width, ctx->Height, ctx->sRCParams.FrameRate);
if (rate_control_param->bits_per_second > max_bps) {
drv_debug_msg(VIDEO_DEBUG_ERROR,
"Invalid bitrate %d, violate ITU-T Rec. H.264 (03/2005) A.3.1"
"\n clip to %d bps\n", rate_control_param->bits_per_second, max_bps);
ctx->sRCParams.BitsPerSecond = max_bps;
} else {
/* See 110% target bitrate for VCM. Otherwise, the resulted bitrate is much lower
than target bitrate */
if (ctx->eCodec == IMG_CODEC_H264_VCM)
rate_control_param->bits_per_second =
rate_control_param->bits_per_second / 100 * 110;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Bitrate is set to %d\n",
rate_control_param->bits_per_second);
ctx->sRCParams.BitsPerSecond = rate_control_param->bits_per_second;
}
}
if (rate_control_param->min_qp != 0)
ctx->sRCParams.MinQP = rate_control_param->min_qp;
if (rate_control_param->window_size != 0) {
ctx->sRCParams.BufferSize =
ctx->sRCParams.BitsPerSecond / 1000 * rate_control_param->window_size;
if (ctx->sRCParams.FrameRate == 0) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "frame rate can't be zero. Set it to 30");
ctx->sRCParams.FrameRate = 30;
}
frame_size = ctx->sRCParams.BitsPerSecond / ctx->sRCParams.FrameRate;
if (frame_size == 0) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "Bitrate is too low %d\n",
ctx->sRCParams.BitsPerSecond);
break;
}
ctx->sRCParams.InitialLevel = (3 * ctx->sRCParams.BufferSize) >> 4;
ctx->sRCParams.InitialLevel += (frame_size / 2);
ctx->sRCParams.InitialLevel /= frame_size;
ctx->sRCParams.InitialLevel *= frame_size;
ctx->sRCParams.InitialDelay =
ctx->sRCParams.BufferSize - ctx->sRCParams.InitialLevel;
}
if (rate_control_param->initial_qp != 0)
ctx->sRCParams.InitialQp = rate_control_param->initial_qp;
if (rate_control_param->basic_unit_size != 0)
ctx->sRCParams.BUSize = rate_control_param->basic_unit_size;
drv_debug_msg(VIDEO_DEBUG_GENERAL,
"Set Misc parameters(frame %d): window_size %d, initial qp %d\n" \
"\tmin qp %d, bunit size %d\n",
ctx->raw_frame_count,
rate_control_param->window_size,
rate_control_param->initial_qp,
rate_control_param->min_qp,
rate_control_param->basic_unit_size);
break;
case VAEncMiscParameterTypeMaxSliceSize:
max_slice_size_param = (VAEncMiscParameterMaxSliceSize *)pBuffer->data;
/*The max slice size should not be bigger than 1920x1080x1.5x8 */
if (max_slice_size_param->max_slice_size > 24883200) {
drv_debug_msg(VIDEO_DEBUG_ERROR,"Invalid max_slice_size. It should be 1~ 24883200.\n");
vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
break;
}
if (ctx->max_slice_size == max_slice_size_param->max_slice_size)
break;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "max slice size changed to %d\n",
max_slice_size_param->max_slice_size);
ctx->max_slice_size = max_slice_size_param->max_slice_size;
break;
case VAEncMiscParameterTypeAIR:
air_param = (VAEncMiscParameterAIR *)pBuffer->data;
if (air_param->air_num_mbs > 65535 ||
air_param->air_threshold > 65535) {
vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
break;
}
drv_debug_msg(VIDEO_DEBUG_GENERAL,"air slice size changed to num_air_mbs %d "
"air_threshold %d, air_auto %d\n",
air_param->air_num_mbs, air_param->air_threshold,
air_param->air_auto);
if (((ctx->Height * ctx->Width) >> 8) < (int)air_param->air_num_mbs)
air_param->air_num_mbs = ((ctx->Height * ctx->Width) >> 8);
if (air_param->air_threshold == 0)
drv_debug_msg(VIDEO_DEBUG_GENERAL,"%s: air threshold is set to zero\n",
__func__);
ctx->num_air_mbs = air_param->air_num_mbs;
ctx->air_threshold = air_param->air_threshold;
//ctx->autotune_air_flag = air_param->air_auto;
break;
case VAEncMiscParameterTypeHRD:
hrd_param = (VAEncMiscParameterHRD *)pBuffer->data;
if (hrd_param->buffer_size == 0
|| hrd_param->initial_buffer_fullness == 0)
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Find zero value for buffer_size "
"and initial_buffer_fullness.\n"
"Will assign default value to them later \n");
if (ctx->initial_buffer_fullness > ctx->buffer_size) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "initial_buffer_fullnessi(%d) shouldn't be"
" larger that buffer_size(%d)!\n",
hrd_param->initial_buffer_fullness,
hrd_param->buffer_size);
vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER;
break;
}
if (!ctx->sRCParams.RCEnable) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "Only when rate control is enabled,"
" VAEncMiscParameterTypeHRD will take effect.\n");
break;
}
ctx->buffer_size = hrd_param->buffer_size;
ctx->initial_buffer_fullness = hrd_param->initial_buffer_fullness;
ctx->bInserHRDParams = IMG_TRUE;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "hrd param buffer_size set to %d "
"initial buffer fullness set to %d\n",
ctx->buffer_size, ctx->initial_buffer_fullness);
break;
default:
vaStatus = VA_STATUS_ERROR_UNKNOWN;
DEBUG_FAILURE;
break;
}
free(obj_buffer->buffer_data);
obj_buffer->buffer_data = NULL;
return vaStatus;
}
static VAStatus pnw_H264ES_RenderPicture(
object_context_p obj_context,
object_buffer_p *buffers,
int num_buffers)
{
INIT_CONTEXT_H264ES;
VAStatus vaStatus = VA_STATUS_SUCCESS;
int i;
drv_debug_msg(VIDEO_DEBUG_GENERAL,"pnw_H264ES_RenderPicture\n");
for (i = 0; i < num_buffers; i++) {
object_buffer_p obj_buffer = buffers[i];
switch (obj_buffer->type) {
case VAEncSequenceParameterBufferType:
drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncSequenceParameterBufferType\n");
vaStatus = pnw__H264ES_process_sequence_param(ctx, obj_buffer);
DEBUG_FAILURE;
break;
case VAEncPictureParameterBufferType:
drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncPictureParameterBuffer\n");
vaStatus = pnw__H264ES_process_picture_param(ctx, obj_buffer);
DEBUG_FAILURE;
break;
case VAEncSliceParameterBufferType:
drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncSliceParameterBufferType\n");
vaStatus = pnw__H264ES_process_slice_param(ctx, obj_buffer);
DEBUG_FAILURE;
break;
case VAEncMiscParameterBufferType:
drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncMiscParameterBufferType\n");
vaStatus = pnw__H264ES_process_misc_param(ctx, obj_buffer);
DEBUG_FAILURE;
break;
#if PSB_MFLD_DUMMY_CODE
case VAEncPackedHeaderParameterBufferType:
drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncPackedHeaderParameterBufferType\n");
vaStatus = pnw__H264ES_insert_SEI_FPA_param(ctx, obj_buffer);
DEBUG_FAILURE;
break;
case VAEncPackedHeaderDataBufferType:
drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264_RenderPicture got VAEncPackedHeaderDataBufferType\n");
vaStatus = pnw__H264ES_insert_SEI_FPA_data(ctx, obj_buffer);
DEBUG_FAILURE;
break;
#endif
default:
vaStatus = VA_STATUS_ERROR_UNKNOWN;
DEBUG_FAILURE;
}
if (vaStatus != VA_STATUS_SUCCESS) {
break;
}
}
return vaStatus;
}
static VAStatus pnw_H264ES_EndPicture(
object_context_p obj_context)
{
INIT_CONTEXT_H264ES;
pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
PIC_PARAMS *psPicParams = (PIC_PARAMS *)cmdbuf->pic_params_p;
VAStatus vaStatus = VA_STATUS_SUCCESS;
unsigned char core = 0;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_H264ES_EndPicture\n");
/* Unlike MPEG4 and H263, slices number is defined by user */
for (core = 0; core < ctx->ParallelCores; core++) {
psPicParams = (PIC_PARAMS *)
(cmdbuf->pic_params_p + ctx->pic_params_size * core);
psPicParams->NumSlices = ctx->sRCParams.Slices;
}
vaStatus = pnw_EndPicture(ctx);
return vaStatus;
}
struct format_vtable_s pnw_H264ES_vtable = {
queryConfigAttributes:
pnw_H264ES_QueryConfigAttributes,
validateConfig:
pnw_H264ES_ValidateConfig,
createContext:
pnw_H264ES_CreateContext,
destroyContext:
pnw_H264ES_DestroyContext,
beginPicture:
pnw_H264ES_BeginPicture,
renderPicture:
pnw_H264ES_RenderPicture,
endPicture:
pnw_H264ES_EndPicture
};
VAStatus pnw_set_frame_skip_flag(
object_context_p obj_context)
{
INIT_CONTEXT_H264ES;
VAStatus vaStatus = VA_STATUS_SUCCESS;
if (ctx && ctx->previous_src_surface) {
SET_SURFACE_INFO_skipped_flag(ctx->previous_src_surface->psb_surface, 1);
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Detected a skipped frame for surface 0x%08x.\n",
ctx->previous_src_surface->psb_surface);
}
return vaStatus;
}