/* * 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; }