/* * 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> * Edward Lin <edward.lin@intel.com> * Zhaohan Ren <zhaohan.ren@intel.com> * */ #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <stdint.h> #include <string.h> #include "psb_def.h" #include "psb_surface.h" #include "tng_cmdbuf.h" #include "tng_hostcode.h" #include "tng_hostheader.h" #include "tng_MPEG4ES.h" #include "psb_drv_debug.h" #include "hwdefs/coreflags.h" #include "hwdefs/topaz_vlc_regs.h" #include "hwdefs/topaz_db_regs.h" #include "hwdefs/topazhp_default_params.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 tng_MPEG4ES_QueryConfigAttributes( VAProfile __maybe_unused profile, VAEntrypoint __maybe_unused entrypoint, VAConfigAttrib *attrib_list, int num_attribs) { int i; drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s\n", __FUNCTION__); /* RateControl attributes */ for (i = 0; i < num_attribs; i++) { switch (attrib_list[i].type) { case VAConfigAttribRTFormat: break; case VAConfigAttribEncAutoReference: attrib_list[i].value = 1; break; case VAConfigAttribEncMaxRefFrames: attrib_list[i].value = 2; break; case VAConfigAttribRateControl: attrib_list[i].value = VA_RC_NONE | VA_RC_CBR | VA_RC_VBR; break; default: attrib_list[i].value = VA_ATTRIB_NOT_SUPPORTED; break; } } } static VAStatus tng_MPEG4ES_ValidateConfig( object_config_p obj_config) { int i; drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s\n", __FUNCTION__); /* 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; case VAConfigAttribEncAutoReference: break; case VAConfigAttribEncMaxRefFrames: break; default: return VA_STATUS_ERROR_ATTR_NOT_SUPPORTED; } } return VA_STATUS_SUCCESS; } static VAStatus tng_MPEG4ES_CreateContext( object_context_p obj_context, object_config_p obj_config) { VAStatus vaStatus = VA_STATUS_SUCCESS; context_ENC_p ctx; unsigned int eRCMode; drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s\n", __FUNCTION__); vaStatus = tng_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; ctx->eStandard = IMG_STANDARD_MPEG4; ctx->eFormat = IMG_CODEC_PL12; // use default switch(ctx->sRCParams.eRCMode) { case IMG_RCMODE_NONE: ctx->eCodec = IMG_CODEC_MPEG4_NO_RC; break; case IMG_RCMODE_VBR: ctx->eCodec = IMG_CODEC_MPEG4_VBR; break; case IMG_RCMODE_CBR: ctx->eCodec = IMG_CODEC_MPEG4_CBR; break; default: drv_debug_msg(VIDEO_DEBUG_ERROR, "Unknown RCMode %08x\n", ctx->sRCParams.eRCMode); break; } ctx->bIsInterlaced = IMG_FALSE; ctx->bIsInterleaved = IMG_FALSE; ctx->ui16PictureHeight = ctx->ui16FrameHeight; ctx->bVPAdaptiveRoundingDisable = IMG_TRUE; /* TopazHP only support Simple Profile */ switch (obj_config->profile) { case VAProfileMPEG4Simple: ctx->ui8ProfileIdc = SP; break; default: ctx->ui8ProfileIdc = SP; break; } //This parameter need not be exposed ctx->ui8InterIntraIndex = 3; ctx->ui8CodedSkippedIndex = 3; ctx->bEnableHostQP = IMG_FALSE; ctx->uMaxChunks = 0xA0; ctx->uChunksPerMb = 0x40; ctx->uPriorityChunks = (0xA0 - 0x60); ctx->ui32FCode = 4; ctx->iFineYSearchSize = 2; //This parameter need not be exposed //host to control the encoding process ctx->bEnableInpCtrl = IMG_FALSE; ctx->bEnableHostBias = IMG_FALSE; //By default false Newly Added ctx->bEnableCumulativeBiases = IMG_FALSE; //Weighted Prediction is not supported in TopazHP Version 3.0 ctx->bWeightedPrediction = IMG_FALSE; ctx->ui8VPWeightedImplicitBiPred = 0; ctx->bInsertHRDParams = 0; ctx->bArbitrarySO = IMG_FALSE; ctx->ui32BasicUnit = 0; return vaStatus; } static void tng_MPEG4ES_DestroyContext( object_context_p obj_context) { drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s\n", __FUNCTION__); tng_DestroyContext(obj_context, 0); } static VAStatus tng_MPEG4ES_BeginPicture( object_context_p obj_context) { INIT_CONTEXT_MPEG4ES; tng_cmdbuf_p cmdbuf = ctx->obj_context->tng_cmdbuf; context_ENC_mem *ps_mem = &(ctx->ctx_mem[ctx->ui32StreamID]); VAStatus vaStatus = VA_STATUS_SUCCESS; drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s start\n", __FUNCTION__); vaStatus = tng_BeginPicture(ctx); drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s end\n", __FUNCTION__); return vaStatus; } static VAStatus tng__MPEG4ES_process_sequence_param(context_ENC_p ctx, object_buffer_p obj_buffer) { context_ENC_mem *ps_mem = &(ctx->ctx_mem[ctx->ui32StreamID]); VAEncSequenceParameterBufferMPEG4 *psSeqParams; tng_cmdbuf_p cmdbuf = ctx->obj_context->tng_cmdbuf; IMG_RC_PARAMS *psRCParams = &(ctx->sRCParams); ASSERT(obj_buffer->type == VAEncSequenceParameterBufferType); ASSERT(obj_buffer->size == sizeof(VAEncSequenceParameterBufferMPEG4)); if (obj_buffer->size != sizeof(VAEncSequenceParameterBufferMPEG4)) { return VA_STATUS_ERROR_UNKNOWN; } ctx->obj_context->frame_count = 0; psSeqParams = (VAEncSequenceParameterBufferMPEG4 *) obj_buffer->buffer_data; obj_buffer->buffer_data = NULL; obj_buffer->size = 0; ctx->ui32IdrPeriod = psSeqParams->intra_period; ctx->ui32IntraCnt = psSeqParams->intra_period; if (ctx->ui32IntraCnt == 0) { ctx->ui32IntraCnt = INT_MAX; ctx->ui32IdrPeriod = 1; drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: only ONE I frame in the sequence, %d\n", __FUNCTION__, ctx->ui32IdrPeriod); } ctx->bCustomScaling = IMG_FALSE; ctx->bUseDefaultScalingList = IMG_FALSE; //set MV limit infor ctx->ui32VertMVLimit = 255 ;//(63.75 in qpel increments) ctx->bLimitNumVectors = IMG_TRUE; ctx->ui8LevelIdc = psSeqParams->profile_and_level_indication; /**************set rc params ****************/ if (psSeqParams->bits_per_second > TOPAZ_MPEG4_MAX_BITRATE) { ctx->sRCParams.ui32BitsPerSecond = TOPAZ_MPEG4_MAX_BITRATE; drv_debug_msg(VIDEO_DEBUG_GENERAL, " bits_per_second(%d) exceeds \ the maximum bitrate, set it with %d\n", psSeqParams->bits_per_second, TOPAZ_MPEG4_MAX_BITRATE); } else ctx->sRCParams.ui32BitsPerSecond = psSeqParams->bits_per_second; //FIXME: Zhaohan, this should be figured out in testsuite? if (!ctx->uiCbrBufferTenths) ctx->uiCbrBufferTenths = TOPAZHP_DEFAULT_uiCbrBufferTenths; if (ctx->uiCbrBufferTenths) { psRCParams->ui32BufferSize = (IMG_UINT32)(psRCParams->ui32BitsPerSecond * ctx->uiCbrBufferTenths / 10.0); } else { if (psRCParams->ui32BitsPerSecond < 256000) psRCParams->ui32BufferSize = ((9 * psRCParams->ui32BitsPerSecond) >> 1); else psRCParams->ui32BufferSize = ((5 * psRCParams->ui32BitsPerSecond) >> 1); } psRCParams->i32InitialDelay = (13 * psRCParams->ui32BufferSize) >> 4; psRCParams->i32InitialLevel = (3 * psRCParams->ui32BufferSize) >> 4; psRCParams->ui32IntraFreq = psSeqParams->intra_period; psRCParams->ui32InitialQp = psSeqParams->initial_qp; psRCParams->iMinQP = psSeqParams->min_qp; ctx->ui32BasicUnit = psSeqParams->min_qp; //psRCParams->ui32BUSize = psSeqParams->basic_unit_size; //ctx->ui32KickSize = psRCParams->ui32BUSize; psRCParams->ui32FrameRate = psSeqParams->frame_rate; //B-frames are not supported for non-H.264 streams ctx->sRCParams.ui16BFrames = 0; ctx->ui8SlotsInUse = psRCParams->ui16BFrames + 2; #if HEADERS_VERBOSE_OUTPUT drv_debug_msg(VIDEO_DEBUG_GENERAL, "\n\n**********************************************************************\n"); drv_debug_msg(VIDEO_DEBUG_GENERAL, "******** HOST FIRMWARE ROUTINES TO PASS HEADERS AND TOKENS TO MTX******\n"); drv_debug_msg(VIDEO_DEBUG_GENERAL, "**********************************************************************\n\n"); #endif free(psSeqParams); return VA_STATUS_SUCCESS; } static VAStatus tng__MPEG4ES_process_picture_param(context_ENC_p ctx, object_buffer_p obj_buffer) { VAStatus vaStatus = VA_STATUS_SUCCESS; context_ENC_mem *ps_mem = &(ctx->ctx_mem[ctx->ui32StreamID]); context_ENC_frame_buf *ps_buf = &(ctx->ctx_frame_buf); VAEncPictureParameterBufferMPEG4 *psPicParams; IMG_BOOL bDepViewPPS = IMG_FALSE; void* pTmpBuf = NULL; drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: start\n",__FUNCTION__); ASSERT(obj_buffer->type == VAEncPictureParameterBufferType); if (obj_buffer->size != sizeof(VAEncPictureParameterBufferMPEG4)) { drv_debug_msg(VIDEO_DEBUG_ERROR, "%s L%d Invalid coded buffer handle\n", __FUNCTION__, __LINE__); return VA_STATUS_ERROR_UNKNOWN; } /* Transfer ownership of VAEncPictureParameterBufferMPEG4 data */ psPicParams = (VAEncPictureParameterBufferMPEG4 *) obj_buffer->buffer_data; obj_buffer->buffer_data = NULL; obj_buffer->size = 0; ASSERT(ctx->ui16Width == psPicParams->picture_width); ASSERT(ctx->ui16PictureHeight == psPicParams->picture_height); #ifndef _TNG_FRAMES_ ps_buf->ref_surface[0] = ps_buf->ref_surface[2] = SURFACE(psPicParams->reference_picture); ps_buf->ref_surface[1] = ps_buf->ref_surface[3] = SURFACE(psPicParams->reconstructed_picture); ps_buf->ref_surface[0]->is_ref_surface = ps_buf->ref_surface[2]->is_ref_surface = 1; ps_buf->ref_surface[1]->is_ref_surface = ps_buf->ref_surface[3]->is_ref_surface = 1; #else ps_buf->ref_surface = SURFACE(psPicParams->reference_picture); ps_buf->rec_surface = SURFACE(psPicParams->reconstructed_picture); #endif ps_buf->coded_buf = BUFFER(psPicParams->coded_buf); if (NULL == ps_buf->coded_buf) { drv_debug_msg(VIDEO_DEBUG_ERROR, "%s L%d Invalid coded buffer handle\n", __FUNCTION__, __LINE__); free(psPicParams); return VA_STATUS_ERROR_INVALID_BUFFER; } free(psPicParams); drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: end\n",__FUNCTION__); return vaStatus; } static VAStatus tng__MPEG4ES_process_slice_param(context_ENC_p ctx, object_buffer_p obj_buffer) { VAStatus vaStatus = VA_STATUS_SUCCESS; VAEncSliceParameterBuffer *psSliceParams; ASSERT(obj_buffer->type == VAEncSliceParameterBufferType); /* Prepare InParams for macros of current slice, insert slice header, insert do slice command */ /* Transfer ownership of VAEncPictureParameterBufferMPEG4 data */ psSliceParams = (VAEncSliceParameterBuffer*) obj_buffer->buffer_data; obj_buffer->size = 0; //deblocking behaviour ctx->bArbitrarySO = IMG_FALSE; ctx->ui8DeblockIDC = psSliceParams->slice_flags.bits.disable_deblocking_filter_idc; ++ctx->ui8SlicesPerPicture; return vaStatus; } static VAStatus tng__MPEG4ES_process_misc_param(context_ENC_p ctx, object_buffer_p obj_buffer) { VAStatus vaStatus = VA_STATUS_SUCCESS; VAEncMiscParameterBuffer *pBuffer; VAEncMiscParameterFrameRate *frame_rate_param; VAEncMiscParameterRateControl *rate_control_param; IMG_RC_PARAMS *psRCParams = &(ctx->sRCParams); ASSERT(obj_buffer->type == VAEncMiscParameterBufferType); /* Transfer ownership of VAEncMiscParameterBuffer data */ pBuffer = (VAEncMiscParameterBuffer *) obj_buffer->buffer_data; obj_buffer->size = 0; switch (pBuffer->type) { case VAEncMiscParameterTypeRateControl: rate_control_param = (VAEncMiscParameterRateControl *)pBuffer->data; 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 MPEG4\n", rate_control_param->initial_qp, rate_control_param->min_qp); vaStatus = VA_STATUS_ERROR_INVALID_PARAMETER; break; } drv_debug_msg(VIDEO_DEBUG_GENERAL, "rate control changed from %d to %d\n", psRCParams->ui32BitsPerSecond, rate_control_param->bits_per_second); if ((rate_control_param->bits_per_second == psRCParams->ui32BitsPerSecond) && (psRCParams->ui32BufferSize == psRCParams->ui32BitsPerSecond / 1000 * rate_control_param->window_size) && (psRCParams->iMinQP == rate_control_param->min_qp) && (psRCParams->ui32InitialQp == rate_control_param->initial_qp)) break; if (rate_control_param->bits_per_second > TOPAZ_MPEG4_MAX_BITRATE) { psRCParams->ui32BitsPerSecond = 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 psRCParams->ui32BitsPerSecond = rate_control_param->bits_per_second; if (rate_control_param->window_size != 0) psRCParams->ui32BufferSize = psRCParams->ui32BitsPerSecond * rate_control_param->window_size / 1000; if (rate_control_param->initial_qp != 0) psRCParams->ui32InitialQp = rate_control_param->initial_qp; if (rate_control_param->min_qp != 0) psRCParams->iMinQP = rate_control_param->min_qp; break; default: break; } return vaStatus; } static VAStatus tng_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, "%s: start\n", __FUNCTION__); for (i = 0; i < num_buffers; i++) { object_buffer_p obj_buffer = buffers[i]; drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: type = %d, num = %d\n", __FUNCTION__, obj_buffer->type, num_buffers); switch (obj_buffer->type) { case VAEncSequenceParameterBufferType: drv_debug_msg(VIDEO_DEBUG_GENERAL, "tng_MPEG4_RenderPicture got VAEncSequenceParameterBufferType\n"); vaStatus = tng__MPEG4ES_process_sequence_param(ctx, obj_buffer); DEBUG_FAILURE; break; case VAEncPictureParameterBufferType: drv_debug_msg(VIDEO_DEBUG_GENERAL, "tng_MPEG4_RenderPicture got VAEncPictureParameterBuffer\n"); vaStatus = tng__MPEG4ES_process_picture_param(ctx, obj_buffer); DEBUG_FAILURE; break; case VAEncSliceParameterBufferType: drv_debug_msg(VIDEO_DEBUG_GENERAL, "tng_MPEG4_RenderPicture got VAEncSliceParameterBufferType\n"); vaStatus = tng__MPEG4ES_process_slice_param(ctx, obj_buffer); DEBUG_FAILURE; break; case VAEncMiscParameterBufferType: drv_debug_msg(VIDEO_DEBUG_GENERAL, "tng_MPEG4_RenderPicture got VAEncMiscParameterBufferType\n"); vaStatus = tng__MPEG4ES_process_misc_param(ctx, obj_buffer); DEBUG_FAILURE; break; default: vaStatus = VA_STATUS_ERROR_UNKNOWN; DEBUG_FAILURE; } if (vaStatus != VA_STATUS_SUCCESS) { break; } } drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s: end\n", __FUNCTION__); return vaStatus; } static VAStatus tng_MPEG4ES_EndPicture( object_context_p obj_context) { INIT_CONTEXT_MPEG4ES; VAStatus vaStatus = VA_STATUS_SUCCESS; drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s start\n", __FUNCTION__); vaStatus = tng_EndPicture(ctx); drv_debug_msg(VIDEO_DEBUG_GENERAL, "%s end\n", __FUNCTION__); return vaStatus; } struct format_vtable_s tng_MPEG4ES_vtable = { queryConfigAttributes: tng_MPEG4ES_QueryConfigAttributes, validateConfig: tng_MPEG4ES_ValidateConfig, createContext: tng_MPEG4ES_CreateContext, destroyContext: tng_MPEG4ES_DestroyContext, beginPicture: tng_MPEG4ES_BeginPicture, renderPicture: tng_MPEG4ES_RenderPicture, endPicture: tng_MPEG4ES_EndPicture }; /*EOF*/