/*
* 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:
* Zeng Li <zeng.li@intel.com>
* Shengquan Yuan <shengquan.yuan@intel.com>
* Binglin Chen <binglin.chen@intel.com>
*
*/
#include "psb_def.h"
#include "psb_surface.h"
#include "psb_cmdbuf.h"
#include "lnc_MPEG4ES.h"
#include "lnc_hostcode.h"
#include "lnc_hostheader.h"
#include "psb_drv_debug.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define TOPAZ_MPEG4_MAX_BITRATE 16000000
#define INIT_CONTEXT_MPEG4ES 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 lnc_MPEG4ES_QueryConfigAttributes(
VAProfile profile,
VAEntrypoint entrypoint,
VAConfigAttrib *attrib_list,
int num_attribs)
{
int i;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "lnc_MPEG4ES_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;
break;
#if 0
case VAConfigAttribEncMaxSliceSize:
attrib_list[i].value = 0;
break;
#endif
default:
attrib_list[i].value = VA_ATTRIB_NOT_SUPPORTED;
break;
}
}
return;
}
static VAStatus lnc_MPEG4ES_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 lnc_MPEG4ES_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, "lnc_MPEG4ES_CreateContext\n");
vaStatus = lnc_CreateContext(obj_context, obj_config);
if (VA_STATUS_SUCCESS != vaStatus)
return VA_STATUS_ERROR_ALLOCATION_FAILED;
ctx = (context_ENC_p) obj_context->format_data;
ctx->FCode = 3;
ctx->max_slice_size = 0;
eRCmode = VA_RC_NONE;
for (i = 0; i < obj_config->attrib_count; i++) {
#if 0
if (obj_config->attrib_list[i].type == VAConfigAttribEncMaxSliceSize)
ctx->max_slice_size = obj_config->attrib_list[i].value;
else if (obj_config->attrib_list[i].type == VAConfigAttribRateControl)
eRCmode = obj_config->attrib_list[i].value;
#else
if (obj_config->attrib_list[i].type == VAConfigAttribRateControl)
eRCmode = obj_config->attrib_list[i].value;
#endif
}
if (eRCmode == VA_RC_VBR) {
ctx->eCodec = IMG_CODEC_MPEG4_VBR;
ctx->sRCParams.RCEnable = IMG_TRUE;
} else if (eRCmode == VA_RC_CBR) {
ctx->eCodec = IMG_CODEC_MPEG4_CBR;
ctx->sRCParams.RCEnable = IMG_TRUE;
} else if (eRCmode == VA_RC_NONE) {
ctx->eCodec = IMG_CODEC_MPEG4_NO_RC;
ctx->sRCParams.RCEnable = IMG_FALSE;
} else
return VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT;
ctx->eFormat = IMG_CODEC_PL12;
ctx->IPEControl = lnc__get_ipe_control(ctx->eCodec);
switch (obj_config->profile) {
case VAProfileMPEG4Simple:
ctx->profile_idc = 2;
break;
case VAProfileMPEG4AdvancedSimple:
ctx->profile_idc = 3;
break;
default:
ctx->profile_idc = 2;
break;
}
return vaStatus;
}
static void lnc_MPEG4ES_DestroyContext(
object_context_p obj_context)
{
drv_debug_msg(VIDEO_DEBUG_GENERAL, "lnc_MPEG4ES_DestroyPicture\n");
lnc_DestroyContext(obj_context);
}
static VAStatus lnc_MPEG4ES_BeginPicture(
object_context_p obj_context)
{
INIT_CONTEXT_MPEG4ES;
VAStatus vaStatus = VA_STATUS_SUCCESS;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "lnc_MPEG4ES_BeginPicture\n");
vaStatus = lnc_BeginPicture(ctx);
return vaStatus;
}
static VAStatus lnc__MPEG4ES_process_sequence_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
VAEncSequenceParameterBufferMPEG4 *seq_params;
lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;
MPEG4_PROFILE_TYPE profile;
ASSERT(obj_buffer->type == VAEncSequenceParameterBufferType);
ASSERT(obj_buffer->num_elements == 1);
ASSERT(obj_buffer->size == sizeof(VAEncSequenceParameterBufferMPEG4));
if ((obj_buffer->num_elements != 1) ||
(obj_buffer->size != sizeof(VAEncSequenceParameterBufferMPEG4))) {
return VA_STATUS_ERROR_UNKNOWN;
}
seq_params = (VAEncSequenceParameterBufferMPEG4 *) obj_buffer->buffer_data;
obj_buffer->buffer_data = NULL;
obj_buffer->size = 0;
if ((ctx->obj_context->frame_count != 0) &&
(ctx->sRCParams.BitsPerSecond != seq_params->bits_per_second))
ctx->update_rc_control = 1;
if (seq_params->bits_per_second > TOPAZ_MPEG4_MAX_BITRATE) {
ctx->sRCParams.BitsPerSecond = TOPAZ_MPEG4_MAX_BITRATE;
drv_debug_msg(VIDEO_DEBUG_GENERAL, " bits_per_second(%d) exceeds \
the maximum bitrate, set it with %d\n",
seq_params->bits_per_second,
TOPAZ_MPEG4_MAX_BITRATE);
} else
ctx->sRCParams.BitsPerSecond = seq_params->bits_per_second;
if (seq_params->initial_qp < 3)
seq_params->initial_qp = 3;
ctx->sRCParams.FrameRate = seq_params->frame_rate;
ctx->sRCParams.InitialQp = seq_params->initial_qp;
ctx->sRCParams.MinQP = seq_params->min_qp;
ctx->sRCParams.BUSize = 0; /* default 0, and will be set in lnc__setup_busize */
ctx->sRCParams.Slices = 1;
ctx->sRCParams.IntraFreq = seq_params->intra_period;
cmdbuf = ctx->obj_context->lnc_cmdbuf;
switch (ctx->profile_idc) {
case 2:
profile = SP;
break;
case 3:
profile = ASP;
break;
default:
profile = SP;
break;
}
lnc__MPEG4_prepare_sequence_header(
(IMG_UINT32 *)(cmdbuf->header_mem_p + ctx->seq_header_ofs),
0, /* BFrame? */
profile, /* sProfile */
seq_params->profile_and_level_indication, /* */
seq_params->fixed_vop_time_increment, /*3,*/ /* sFixed_vop_time_increment */
seq_params->video_object_layer_width,/* Picture_Width_Pixels */
seq_params->video_object_layer_height, /* Picture_Height_Pixels */
0, /* bVBVPresent */
0, /* First_half_bit_rate */
0, /* Latter_half_bit_rate */
0, /* First_half_vbv_buffer_size */
0, /* Latter_half_vbv_buffer_size */
0, /* First_half_vbv_occupancy */
0, /* Latter_half_vbv_occupancy */
seq_params->vop_time_increment_resolution); /* VopTimeResolution */
ctx->MPEG4_vop_time_increment_resolution = seq_params->vop_time_increment_resolution;
lnc_cmdbuf_insert_command(cmdbuf, MTX_CMDID_DO_HEADER, 2, 0);
RELOC_CMDBUF(cmdbuf->cmd_idx++, ctx->seq_header_ofs, &cmdbuf->header_mem);
free(seq_params);
return vaStatus;
}
static VAStatus lnc__MPEG4ES_process_picture_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
VAEncPictureParameterBufferMPEG4 *pBuffer;
lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;
IMG_BOOL bIsVOPCoded = IMG_TRUE;
ASSERT(obj_buffer->type == VAEncPictureParameterBufferType);
if ((obj_buffer->num_elements != 1) ||
(obj_buffer->size != sizeof(VAEncPictureParameterBufferMPEG4))) {
return VA_STATUS_ERROR_UNKNOWN;
}
/* Transfer ownership of VAEncPictureParameterBufferMPEG4 data */
pBuffer = (VAEncPictureParameterBufferMPEG4 *) obj_buffer->buffer_data;
obj_buffer->buffer_data = NULL;
obj_buffer->size = 0;
ctx->ref_surface = SURFACE(pBuffer->reference_picture);
ctx->dest_surface = SURFACE(pBuffer->reconstructed_picture);
ctx->coded_buf = BUFFER(pBuffer->coded_buf);
ASSERT(ctx->Width == pBuffer->picture_width);
ASSERT(ctx->Height == pBuffer->picture_height);
if (ctx->sRCParams.RCEnable && ctx->sRCParams.FrameSkip)
bIsVOPCoded = IMG_FALSE;
/* save current cmdbuf write pointer for MPEG4 frameskip redo
* MPEG4 picture header need re-patch, and no slice header needed
* for a skipped frame
*/
cmdbuf->cmd_idx_saved_frameskip = cmdbuf->cmd_idx;
lnc__MPEG4_prepare_vop_header((IMG_UINT32 *)(cmdbuf->header_mem_p + ctx->pic_header_ofs),
bIsVOPCoded,
pBuffer->vop_time_increment, /* In testbench, this should be FrameNum */
4,/* default value is 4,search range */
pBuffer->picture_type,
ctx->MPEG4_vop_time_increment_resolution/* defaule value */);
lnc_cmdbuf_insert_command(cmdbuf, MTX_CMDID_DO_HEADER, 2, 1);
RELOC_CMDBUF(cmdbuf->cmd_idx++, ctx->pic_header_ofs, &cmdbuf->header_mem);
vaStatus = lnc_RenderPictureParameter(ctx);
free(pBuffer);
return vaStatus;
}
static VAStatus lnc__MPEG4ES_process_slice_param(context_ENC_p ctx, object_buffer_p obj_buffer)
{
VAStatus vaStatus = VA_STATUS_SUCCESS;
VAEncSliceParameterBuffer *pBuffer;
lnc_cmdbuf_p cmdbuf = ctx->obj_context->lnc_cmdbuf;
PIC_PARAMS *psPicParams = (PIC_PARAMS *)(cmdbuf->pic_params_p);
unsigned int i;
int slice_param_idx;
ASSERT(obj_buffer->type == VAEncSliceParameterBufferType);
pBuffer = (VAEncSliceParameterBuffer *) obj_buffer->buffer_data;
if (0 == pBuffer->start_row_number) {
if (pBuffer->slice_flags.bits.is_intra)
RELOC_PIC_PARAMS(&psPicParams->InParamsBase, ctx->in_params_ofs, cmdbuf->topaz_in_params_I);
else
RELOC_PIC_PARAMS(&psPicParams->InParamsBase, ctx->in_params_ofs, cmdbuf->topaz_in_params_P);
}
/*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) {
drv_debug_msg(VIDEO_DEBUG_GENERAL, "Allocate %d VAEncSliceParameterBuffer cache buffers\n", 2 * ctx->slice_param_num);
ctx->slice_param_num = obj_buffer->num_elements;
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(obj_buffer->buffer_data);
return VA_STATUS_ERROR_ALLOCATION_FAILED;
}
}
for (i = 0; i < obj_buffer->num_elements; i++) {
if (!(ctx->sRCParams.RCEnable && ctx->sRCParams.FrameSkip)) {
if ((ctx->obj_context->frame_count == 0) && (pBuffer->start_row_number == 0) && pBuffer->slice_flags.bits.is_intra)
lnc_reset_encoder_params(ctx);
/*The corresponding slice buffer cache*/
slice_param_idx = (pBuffer->slice_flags.bits.is_intra ? 0 : 1) * ctx->slice_param_num + i;
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*/
lnc_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);
}
lnc__send_encode_slice_params(ctx,
pBuffer->slice_flags.bits.is_intra,
pBuffer->start_row_number * 16,
IMG_FALSE, /* Deblock is off for MPEG4*/
ctx->obj_context->frame_count,
pBuffer->slice_height * 16,
ctx->obj_context->slice_count,
ctx->max_slice_size);
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++;
pBuffer++;
ASSERT(ctx->obj_context->slice_count < MAX_SLICES_PER_PICTURE);
}
free(obj_buffer->buffer_data);
obj_buffer->buffer_data = NULL;
return vaStatus;
}
static VAStatus lnc__MPEG4ES_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;
VAStatus vaStatus = VA_STATUS_SUCCESS;
ASSERT(obj_buffer->type == VAEncMiscParameterBufferType);
pBuffer = (VAEncMiscParameterBuffer *) obj_buffer->buffer_data;
obj_buffer->size = 0;
switch (pBuffer->type) {
case VAEncMiscParameterTypeFrameRate:
frame_rate_param = (VAEncMiscParameterFrameRate *)pBuffer->data;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: frame rate changed to %d\n",
frame_rate_param->framerate);
break;
case VAEncMiscParameterTypeRateControl:
rate_control_param = (VAEncMiscParameterRateControl *)pBuffer->data;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: bit rate changed to %d\n",
rate_control_param->bits_per_second);
if (rate_control_param->bits_per_second == ctx->sRCParams.BitsPerSecond)
break;
else
ctx->update_rc_control = 1;
if (rate_control_param->bits_per_second > TOPAZ_MPEG4_MAX_BITRATE) {
ctx->sRCParams.BitsPerSecond = TOPAZ_MPEG4_MAX_BITRATE;
drv_debug_msg(VIDEO_DEBUG_GENERAL, " bits_per_second(%d) exceeds \
the maximum bitrate, set it with %d\n",
rate_control_param->bits_per_second,
TOPAZ_MPEG4_MAX_BITRATE);
} else
ctx->sRCParams.BitsPerSecond = rate_control_param->bits_per_second;
break;
case VAEncMiscParameterTypeMaxSliceSize:
max_slice_size_param = (VAEncMiscParameterMaxSliceSize *)pBuffer->data;
if (ctx->max_slice_size == max_slice_size_param->max_slice_size)
break;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: 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;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: 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);
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;
default:
vaStatus = VA_STATUS_ERROR_UNKNOWN;
DEBUG_FAILURE;
break;
}
free(obj_buffer->buffer_data);
obj_buffer->buffer_data = NULL;
return VA_STATUS_SUCCESS;
}
static VAStatus lnc_MPEG4ES_RenderPicture(
object_context_p obj_context,
object_buffer_p *buffers,
int num_buffers)
{
INIT_CONTEXT_MPEG4ES;
VAStatus vaStatus = VA_STATUS_SUCCESS;
int i;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "lnc_MPEG4ES_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, "lnc_MPEG4ES_RenderPicture got VAEncSequenceParameterBufferType\n");
vaStatus = lnc__MPEG4ES_process_sequence_param(ctx, obj_buffer);
DEBUG_FAILURE;
break;
case VAEncPictureParameterBufferType:
drv_debug_msg(VIDEO_DEBUG_GENERAL, "lnc_MPEG4ES_RenderPicture got VAEncPictureParameterBufferType\n");
vaStatus = lnc__MPEG4ES_process_picture_param(ctx, obj_buffer);
DEBUG_FAILURE;
break;
case VAEncSliceParameterBufferType:
drv_debug_msg(VIDEO_DEBUG_GENERAL, "lnc_MPEG4ES_RenderPicture got VAEncSliceParameterBufferType\n");
vaStatus = lnc__MPEG4ES_process_slice_param(ctx, obj_buffer);
DEBUG_FAILURE;
break;
case VAEncMiscParameterBufferType:
drv_debug_msg(VIDEO_DEBUG_GENERAL, "lnc_MPEG4ES_RenderPicture got VAEncMiscParameterBufferType\n");
vaStatus = lnc__MPEG4ES_process_misc_param(ctx, obj_buffer);
DEBUG_FAILURE;
break;
default:
vaStatus = VA_STATUS_ERROR_UNKNOWN;
DEBUG_FAILURE;
}
}
return vaStatus;
}
static VAStatus lnc_MPEG4ES_EndPicture(
object_context_p obj_context)
{
INIT_CONTEXT_MPEG4ES;
drv_debug_msg(VIDEO_DEBUG_GENERAL, "lnc_MPEG4ES_EndPicture\n");
return lnc_EndPicture(ctx);
}
struct format_vtable_s lnc_MPEG4ES_vtable = {
queryConfigAttributes:
lnc_MPEG4ES_QueryConfigAttributes,
validateConfig:
lnc_MPEG4ES_ValidateConfig,
createContext:
lnc_MPEG4ES_CreateContext,
destroyContext:
lnc_MPEG4ES_DestroyContext,
beginPicture:
lnc_MPEG4ES_BeginPicture,
renderPicture:
lnc_MPEG4ES_RenderPicture,
endPicture:
lnc_MPEG4ES_EndPicture
};