/* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. * * This source code is subject to the terms and conditions defined in below * which is part of this source code package. * * Description: */ // Copyright (C) 2019 Amlogic, Inc. All rights reserved. // // All information contained herein is Amlogic confidential. // // This software is provided to you pursuant to Software License // Agreement (SLA) with Amlogic Inc ("Amlogic"). This software may be // used only in accordance with the terms of this agreement. // // Redistribution and use in source and binary forms, with or without // modification is strictly prohibited without prior written permission // from Amlogic. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #define LOG_NDEBUG 0 #define LOG_TAG "AMLVENC_MULTI" #ifdef MAKEANDROID #include #endif #include #include #include #include #include #include #include #include #include "AML_MultiEncoder.h" #include "vpuapi.h" #include "vpuconfig.h" #include "vdi_osal.h" #include "debug.h" #include "vpuapifunc.h" #include #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif #ifndef max #define max(a,b) (((a) > (b)) ? (a) : (b)) #endif #define ENCODE_TIME_STATISTICS 0 #if ENCODE_TIME_STATISTICS static unsigned long encode_time_per_frame; static unsigned long long total_encode_time; static unsigned long long total_encode_frames; static struct timeval start_test; static struct timeval end_test; #endif #define SUPPORT_SCALE 1 #if SUPPORT_SCALE #include #include #endif #define MULTI_ENC_MAGIC ('A' << 24| 'M' <<16 | 'L' << 8 |'G') /* timeout in ms */ #define VPU_WAIT_TIME_OUT_CQ 100 /* extra source frame buffer required.*/ #define EXTRA_SRC_BUFFER_NUM 0 // user scaling list #define SL_NUM_MATRIX (6) #define vp_align4(a) ((((a)+3)>>2)<<2) #define vp_align8(a) ((((a)+7)>>3)<<3) #define vp_align16(a) ((((a)+15)>>4)<<4) #define vp_align32(a) ((((a)+31)>>5)<<5) #define vp_align64(a) ((((a)+63)>>6)<<6) #define vp_align128(a) ((((a)+127)>>7)<<7) #define vp_align256(a) ((((a)+255)>>8)<<8) typedef union { struct { Uint32 ctu_force_mode : 2; //[ 1: 0] Uint32 ctu_coeff_drop : 1; //[ 2] Uint32 reserved : 5; //[ 7: 3] Uint32 sub_ctu_qp_0 : 6; //[13: 8] Uint32 sub_ctu_qp_1 : 6; //[19:14] Uint32 sub_ctu_qp_2 : 6; //[25:20] Uint32 sub_ctu_qp_3 : 6; //[31:26] Uint32 lambda_sad_0 : 8; //[39:32] Uint32 lambda_sad_1 : 8; //[47:40] Uint32 lambda_sad_2 : 8; //[55:48] Uint32 lambda_sad_3 : 8; //[63:56] } field; } EncCustomMap; // for vp5xx custom map (1 CTU = 64bits) typedef union { struct { Uint8 mb_force_mode : 2; //[ 1: 0] Uint8 mb_qp : 6; //[ 7: 2] } field; } AvcEncCustomMap; // for AVC custom map on vp (1 MB = 8bits) typedef struct { Uint8 s4[SL_NUM_MATRIX][16]; // [INTRA_Y/U/V,INTER_Y/U/V][NUM_COEFF] Uint8 s8[SL_NUM_MATRIX][64]; Uint8 s16[SL_NUM_MATRIX][64]; Uint8 s32[SL_NUM_MATRIX][64]; Uint8 s16dc[SL_NUM_MATRIX]; Uint8 s32dc[2]; }UserScalingList; typedef struct { Uint32 CustMeanY; Uint32 CustMeadCb; Uint32 CustMeadCr; Uint32 CustSigmaY; Uint32 CustSigmaCb; Uint32 CustSigmaCr; } WeigtedPredInfo; typedef enum { ENC_INT_STATUS_NONE, // Interrupt not asserted yet ENC_INT_STATUS_FULL, // Need more buffer ENC_INT_STATUS_DONE, // Interrupt asserted ENC_INT_STATUS_LOW_LATENCY, ENC_INT_STATUS_TIMEOUT, // Interrupt not asserted during given time. } ENC_INT_STATUS; typedef enum { ENCODER_STATE_OPEN, ENCODER_STATE_INIT_SEQ, ENCODER_STATE_REGISTER_FB, ENCODER_STATE_ENCODE_HEADER, ENCODER_STATE_ENCODING, } EncoderState; static CustomGopPicParam AML_svc1[2] = { {PIC_TYPE_P, 1, 1, 1, 0,-1, 1}, {PIC_TYPE_P, 2, 0, 1, 0,-1, 0}, }; static CustomGopPicParam AML_svc2[3] = { {PIC_TYPE_P, 1, 1, 1, 0,-1, 1}, {PIC_TYPE_P, 2, 1, 1, 0,-1, 1}, {PIC_TYPE_P, 3, 0, 1, 0,-1, 0}, }; static CustomGopPicParam AML_svc3[4] = { {PIC_TYPE_P, 1, 1, 1, 0,-1, 1}, {PIC_TYPE_P, 2, 1, 1, 0,-1, 1}, {PIC_TYPE_P, 3, 1, 1, 0,-1, 1}, {PIC_TYPE_P, 4, 0, 1, 0,-1, 0}, }; static CustomGopPicParam AML_svc4[5] = { {PIC_TYPE_P, 1, 1, 1, 0,-1, 1}, {PIC_TYPE_P, 2, 1, 1, 0,-1, 1}, {PIC_TYPE_P, 3, 1, 1, 0,-1, 1}, {PIC_TYPE_P, 4, 1, 1, 0,-1, 1}, {PIC_TYPE_P, 5, 0, 1, 0,-1, 0}, }; static CustomGopPicParam AML_custp[2] = { {PIC_TYPE_P, 1, 0, 2, 0,-1, 0}, {PIC_TYPE_P, 2, 0, 2, 1, 0, 0}, }; //hoan mdf ref static CustomGopPicParam AML_custp_ref_one[1] = { {PIC_TYPE_P, 1, 0, 1, 0,-1, 0}, }; //end //hoan add svc for tencent static CustomGopPicParam AML_svc5[4] = { {PIC_TYPE_P, 1, 0, 1, 0,-1, 2},//type, POC, Qp, num_ref_L0, Ref_L0_POC, Ref_L1_POC, temporalLayer {PIC_TYPE_P, 2, 0, 1, 0,-1, 1}, {PIC_TYPE_P, 3, 0, 1, 2,-1, 2}, {PIC_TYPE_P, 4, 0, 1, 0,-1, 0}, }; //end typedef struct AMVEncContext_s { uint32 magic_num; uint32 instance_id; uint32 src_idx; uint32 src_count; uint32 enc_width; uint32 temp_enc_width; uint32 enc_height; uint32 bitrate; /* target encoding bit rate in bits/second */ uint32 frame_rate; /* frame rate */ int32 idrPeriod; /* IDR period in number of frames */ uint32 op_flag; uint32 averageQP; uint32 src_num; /*total src_frame buffer needed */ uint32 fb_num; /* total reconstuction frame buffer encoder needed */ uint32 enc_counter; uint frame_delay; AMVEncFrameFmt fmt; bool mPrependSPSPPSToIDRFrames; uint32 initQP; /* initial QP */ //AVCNalUnitType nal_unit_type; bool rcEnable; /* rate control enable, on: RC on, off: constant QP */ AMVEncInitParams mInitParams; //copy of initial set up from upper layer /* VPU API interfaces */ EncHandle enchandle; /*VPU encoder handler */ EncOpenParam encOpenParam; /* api open param */ EncChangeParam changeParam; Uint32 param_change_flag; Uint32 frameIdx; vpu_buffer_t vbCustomLambda; vpu_buffer_t vbScalingList; Uint32 customLambda[NUM_CUSTOM_LAMBDA]; UserScalingList scalingList; vpu_buffer_t vbCustomMap[MAX_REG_FRAME]; EncoderState state; EncInitialInfo initialInfo; EncParam encParam; Int32 encodedSrcFrmIdxArr[ENC_SRC_BUF_NUM]; Int32 encMEMSrcFrmIdxArr[ENC_SRC_BUF_NUM]; vpu_buffer_t bsBuffer[ENC_SRC_BUF_NUM]; BOOL fullInterrupt; Uint32 changedCount; Uint64 startTimeout; Uint32 cyclePerTick; Uint32 reconFbStride; Uint32 reconFbHeight; Uint32 srcFbStride; Uint32 srcFbSize; FrameBufferAllocInfo srcFbAllocInfo; BOOL fbAllocated; FrameBuffer pFbRecon[MAX_REG_FRAME]; vpu_buffer_t pFbReconMem[MAX_REG_FRAME]; FrameBuffer pFbSrc[ENC_SRC_BUF_NUM]; vpu_buffer_t pFbSrcMem[ENC_SRC_BUF_NUM]; AMVMultiEncFrameIO FrameIO[ENC_SRC_BUF_NUM]; vpu_buffer_t work_vb; vpu_buffer_t temp_vb; vpu_buffer_t fbc_ltable_vb; vpu_buffer_t fbc_ctable_vb; vpu_buffer_t fbc_mv_vb; vpu_buffer_t subsample_vb; uint32 debugEnable; BOOL force_terminate; unsigned long long mNumInputFrames; AMVEncBufferType bufType; uint32 mNumPlanes; uint8 *CustomRoiMapBuf; // store the latest value uint8 *CustomLambdaMapBuf; uint8 *CustomModeMapBuf; uint32 CustomMapSize; // this is the max value uint32 CustomUpdateID; //store the update count uint32 CustomMapUpdatedId[MAX_REG_FRAME]; // updated ID cnt WeigtedPredInfo wp_para; int pic_type; #if SUPPORT_SCALE uint32 ge2d_initial_done; aml_ge2d_t amlge2d; bool INIT_GE2D; #endif } AMVMultiCtx; #if SUPPORT_SCALE static int SRC1_PIXFORMAT = PIXEL_FORMAT_YCrCb_420_SP; static int SRC2_PIXFORMAT = PIXEL_FORMAT_YCrCb_420_SP; static int DST_PIXFORMAT = PIXEL_FORMAT_YCrCb_420_SP; static GE2DOP OP = AML_GE2D_STRETCHBLIT; static int do_strechblit(aml_ge2d_info_t* pge2dinfo, AMVMultiEncFrameIO* input) { int ret = -1; char code = 0; VLOG(INFO, "do_strechblit test case:\n"); pge2dinfo->src_info[0].memtype = GE2D_CANVAS_ALLOC; pge2dinfo->dst_info.memtype = GE2D_CANVAS_ALLOC; pge2dinfo->src_info[0].canvas_w = input->pitch; // SX_SRC1; pge2dinfo->src_info[0].canvas_h = input->height; // SY_SRC1; pge2dinfo->src_info[0].format = SRC1_PIXFORMAT; if ((input->scale_width != 0) && (input->scale_height != 0)) { pge2dinfo->dst_info.canvas_w = input->scale_width; // SX_DST; pge2dinfo->dst_info.canvas_h = input->scale_height; // SY_DST; } else { pge2dinfo->dst_info.canvas_w = vp_align32(input->pitch) - input->crop_left - input->crop_right; pge2dinfo->dst_info.canvas_h = input->height - input->crop_top - input->crop_bottom; } pge2dinfo->dst_info.format = DST_PIXFORMAT; pge2dinfo->src_info[0].rect.x = input->crop_left; pge2dinfo->src_info[0].rect.y = input->crop_top; pge2dinfo->src_info[0].rect.w = input->pitch - input->crop_left - input->crop_right; pge2dinfo->src_info[0].rect.h = input->height - input->crop_top - input->crop_bottom; pge2dinfo->dst_info.rect.x = 0; pge2dinfo->dst_info.rect.y = 0; if ((input->scale_width != 0) && (input->scale_height != 0)) { pge2dinfo->dst_info.rect.w = input->scale_width; // SX_DST; pge2dinfo->dst_info.rect.h = input->scale_height; // SY_DST; } else { pge2dinfo->dst_info.rect.w = vp_align32(input->pitch) - input->crop_left - input->crop_right; pge2dinfo->dst_info.rect.h = input->height - input->crop_top - input->crop_bottom; } pge2dinfo->dst_info.rotation = GE2D_ROTATION_0; ret = aml_ge2d_process(pge2dinfo); return ret; } static void set_ge2dinfo(aml_ge2d_info_t* pge2dinfo, AMVEncInitParams* encParam) { pge2dinfo->src_info[0].memtype = GE2D_CANVAS_ALLOC; pge2dinfo->src_info[0].canvas_w = encParam->src_width; // SX_SRC1; pge2dinfo->src_info[0].canvas_h = encParam->src_height; // SY_SRC1; pge2dinfo->src_info[0].format = SRC1_PIXFORMAT; //pge2dinfo->src_info[0].plane_number = 1; pge2dinfo->src_info[1].memtype = GE2D_CANVAS_TYPE_INVALID; pge2dinfo->src_info[1].canvas_w = 0; pge2dinfo->src_info[1].canvas_h = 0; pge2dinfo->src_info[1].format = SRC2_PIXFORMAT; //pge2dinfo->src_info[1].plane_number = 1; pge2dinfo->dst_info.memtype = GE2D_CANVAS_ALLOC; pge2dinfo->dst_info.canvas_w = vp_align32(encParam->width);//encParam->width; // SX_DST; pge2dinfo->dst_info.canvas_h = encParam->height; // SY_DST; pge2dinfo->dst_info.format = DST_PIXFORMAT; pge2dinfo->dst_info.rotation = GE2D_ROTATION_0; //pge2dinfo->dst_info.plane_number = 1; pge2dinfo->offset = 0; pge2dinfo->ge2d_op = OP; pge2dinfo->blend_mode = BLEND_MODE_PREMULTIPLIED; } #endif int64_t GetNowUs() { struct timeval tv; gettimeofday(&tv, NULL); return (int64_t)tv.tv_sec * 1000000 + (int64_t)tv.tv_usec; } void static yuv_plane_memcpy(int coreIdx, int dst, char *src, uint32 width, uint32 height, uint32 stride, uint32 src_stride, bool aligned, EndianMode endian) { unsigned i; if (dst == 0 || src == NULL) { VLOG(ERR, "yuv_plane_memcpy error ptr\n"); return; } if (!aligned) { for (i = 0; i < height; i++) { vdi_write_memory(coreIdx, dst, (Uint8 *)src, width, endian); dst += stride; src += src_stride; } } else { vdi_write_memory(coreIdx, dst, (Uint8 *)src, stride * height, endian); } } static BOOL SetupEncoderOpenParam(EncOpenParam *pEncOP, AMVEncInitParams* InitParam) { int i; CustomGopPicParam *GopParam = NULL; EncVpParam *param = &pEncOP->EncStdParam.vpParam; /*basic settings */ if (InitParam->stream_type == AMV_AVC) pEncOP->bitstreamFormat = STD_AVC; else if (InitParam->stream_type == AMV_HEVC) pEncOP->bitstreamFormat = STD_HEVC; else { VLOG(ERR, "[ERROR] Not supported format (%d)\n", InitParam->stream_type); return FALSE; } pEncOP->picWidth = InitParam->width; pEncOP->picHeight = InitParam->height; pEncOP->frameRateInfo = InitParam->frame_rate; pEncOP->bitRate = InitParam->bitrate; pEncOP->rcEnable = InitParam->rate_control; /* default */ pEncOP->srcBitDepth = InitParam->srcBitDepth; /*pCfg->SrcBitDepth; */ param->level = 0; param->tier = 0; param->internalBitDepth = InitParam->internalBitDepth;; /*pCfg->vpCfg.internalBitDepth;*/ if ( param->internalBitDepth == 10 ) { pEncOP->outputFormat = FORMAT_420_P10_16BIT_MSB; if (pEncOP->bitstreamFormat == STD_HEVC) { param->profile = HEVC_PROFILE_MAIN10; } else { param->profile = AVC_PROFILE_HIGH10; } } else if ( param->internalBitDepth == 8 ) { pEncOP->outputFormat = FORMAT_420; if (pEncOP->bitstreamFormat == STD_HEVC) { param->profile = HEVC_PROFILE_MAIN; } else {//AVC if (InitParam->profile < AVC_PROFILE_HIGH10) param->profile = InitParam->profile; else param->profile = 0; } } param->losslessEnable = 0; param->constIntraPredFlag = 0; param->lfCrossSliceBoundaryEnable = 1; param->useLongTerm = InitParam->longterm_ref_enable; /* for CMD_ENC_SEQ_GOP_PARAM */ //param->gopPresetIdx = pCfg->vpCfg.gopPresetIdx; if (InitParam->GopPreset == GOP_OPT_NONE) { if (InitParam->idr_period == 1) param->gopPresetIdx = PRESET_IDX_ALL_I; //all I frame else param->gopPresetIdx = PRESET_IDX_IPP ;//PRESET_IDX_IPP PRESET_IDX_IPP_SINGLE } else if (InitParam->GopPreset == GOP_ALL_I) { param->gopPresetIdx = PRESET_IDX_ALL_I; } else if (InitParam->GopPreset == GOP_IP) { param->gopPresetIdx = PRESET_IDX_IPP;//ori } else if (InitParam->GopPreset == GOP_IBBBP) { param->gopPresetIdx = PRESET_IDX_IBBBP; } else if (InitParam->GopPreset == GOP_IP_CUSTP_REF_ONE) { param->gopPresetIdx = PRESET_IDX_IPP_SINGLE;//ori VLOG(TRACE, "InitParam->GopPreset == GOP_IP_CUSTP_REF_ONE\n"); } else if (InitParam->GopPreset == GOP_IP_SVC1) { param->gopPresetIdx = PRESET_IDX_CUSTOM_GOP; param->gopParam.customGopSize = 2; GopParam = AML_svc1; } else if (InitParam->GopPreset == GOP_IP_SVC2) { param->gopPresetIdx = PRESET_IDX_CUSTOM_GOP; param->gopParam.customGopSize = 3; GopParam = AML_svc2; } else if (InitParam->GopPreset == GOP_IP_SVC3) { param->gopPresetIdx = PRESET_IDX_CUSTOM_GOP; param->gopParam.customGopSize = 4; GopParam = AML_svc3; } else if (InitParam->GopPreset == GOP_IP_SVC4) { param->gopPresetIdx = PRESET_IDX_CUSTOM_GOP; param->gopParam.customGopSize = 5; GopParam = AML_svc4; } else if (InitParam->GopPreset == GOP_IP_CUSTP) { param->gopPresetIdx = PRESET_IDX_CUSTOM_GOP; param->gopParam.customGopSize = 2; GopParam = AML_custp; VLOG(TRACE, "InitParam->GopPreset == GOP_IP_CUSTP,param->gopParam.customGopSize %d\n", param->gopParam.customGopSize); } //hoan add for tencent else if (InitParam->GopPreset == GOP_IP_SVC5) { param->gopPresetIdx = PRESET_IDX_CUSTOM_GOP; param->gopParam.customGopSize = 4; GopParam = AML_svc5; VLOG(TRACE, "AML_svc5!!!!!!!!!!!\n"); } //end else { VLOG(ERR, "[ERROR] Not supported GOP format (%d)\n", InitParam->GopPreset); return FALSE; } /* for CMD_ENC_SEQ_INTRA_PARAM */ param->decodingRefreshType = 1; //pCfg->vpCfg.decodingRefreshType; param->intraPeriod = InitParam->idr_period;//pCfg->vpCfg.intraPeriod; if (InitParam->idr_period == 0) { // only one I then all P param->gopPresetIdx = PRESET_IDX_IPP; param->intraPeriod = 0; } else if (InitParam ->idr_period == 1) { param->gopPresetIdx = PRESET_IDX_ALL_I; param->intraPeriod = InitParam ->idr_period; } else param->intraPeriod = InitParam ->idr_period; VLOG(INFO, "GopPreset GOP format (%d) period %d LongTermRef %d\n", param->gopPresetIdx, param->intraPeriod, param->useLongTerm); param->intraQP = InitParam ->initQP; //pCfg->vpCfg.intraQP; param->forcedIdrHeaderEnable = 0; //pCfg->vpCfg.forcedIdrHeaderEnable; /* for CMD_ENC_SEQ_CONF_WIN_TOP_BOT/LEFT_RIGHT */ param->confWinTop = 0; //pCfg->vpCfg.confWinTop; param->confWinBot = 0; //pCfg->vpCfg.confWinBot; param->confWinLeft = 0; //pCfg->vpCfg.confWinLeft; param->confWinRight = 0; //pCfg->vpCfg.confWinRight; if ((pEncOP->picHeight % 16) && param->confWinBot == 0 && pEncOP->bitstreamFormat == STD_AVC) { /* need set the drop flag */ if (InitParam->rotate_angle != 90 && InitParam->rotate_angle != 270) { // except rotation param->confWinBot = 16 - (pEncOP->picHeight % 16); } } if ((pEncOP->picWidth % 16) && param->confWinRight == 0 && pEncOP->bitstreamFormat == STD_AVC) { /* need set the drop flag for width*/ if (InitParam->rotate_angle != 90 && InitParam->rotate_angle != 270) { // except rotation param->confWinRight = 16 - (pEncOP->picWidth % 16); } } if ((pEncOP->picHeight % 8) && param->confWinBot == 0 && pEncOP->bitstreamFormat == STD_HEVC) { /* need set the drop flag */ if (InitParam->rotate_angle != 90 && InitParam->rotate_angle != 270) { // except rotation param->confWinBot = 8 - (pEncOP->picHeight % 8); } } /* for CMD_ENC_SEQ_INDEPENDENT_SLICE */ if (InitParam->slice_mode && InitParam->slice_mode <= 2 && InitParam->slice_arg && pEncOP->bitstreamFormat == STD_HEVC) { param->independSliceMode = 1; param->independSliceModeArg = InitParam->slice_arg & 0xffff; VLOG(INFO, "HEVC:independSliceMode %d , independSliceModeArg %d\n", param->independSliceMode, param->independSliceModeArg); } else { param->independSliceMode = 0; //pCfg->vpCfg.independSliceMode; param->independSliceModeArg = 0; //pCfg->vpCfg.independSliceModeArg; } /* for CMD_ENC_SEQ_DEPENDENT_SLICE */ if (InitParam->slice_mode ==2 && InitParam->slice_arg && pEncOP->bitstreamFormat == STD_HEVC) { param->dependSliceMode = 2; //pCfg->vpCfg.dependSliceMode; param->dependSliceModeArg = (InitParam->slice_arg >>16) & 0xffff; //pCfg->vpCfg.dependSliceModeArg; VLOG(INFO, "HEVC:dependSliceMode %d , dependSliceModeArg %d\n", param->dependSliceMode, param->dependSliceModeArg); } else { param->dependSliceMode = 0; //pCfg->vpCfg.dependSliceMode; param->dependSliceModeArg = 0; //pCfg->vpCfg.dependSliceModeArg; } /* for CMD_ENC_SEQ_INTRA_REFRESH_PARAM */ if (InitParam->IntraRefreshMode && InitParam->IntraRefreshMode <= 4 && InitParam->IntraRefreshArg && pEncOP->bitstreamFormat == STD_HEVC) { param->intraRefreshMode = InitParam->IntraRefreshMode; param->intraRefreshArg = InitParam->IntraRefreshArg; VLOG(INFO, "HEVC:intraRefreshMode %d, intraRefreshArg %d\n", param->intraRefreshMode, param->intraRefreshArg); } else { param->intraRefreshMode = 0; //pCfg->vpCfg.intraRefreshMode; param->intraRefreshArg = 0; //pCfg->vpCfg.intraRefreshArg; } param->useRecommendEncParam = 0; //pCfg->vpCfg.useRecommendEncParam; /* for CMD_ENC_PARAM */ param->scalingListEnable = 0; //pCfg->vpCfg.scalingListEnable; param->cuSizeMode = 0x7; // always set cu8x8/16x16/32x32 enable to 1. param->tmvpEnable = 1; //pCfg->vpCfg.tmvpEnable; param->wppEnable = 0; //pCfg->vpCfg.wppenable; param->maxNumMerge = 2; //pCfg->vpCfg.maxNumMerge; param->disableDeblk = 0; //pCfg->vpCfg.disableDeblk; param->lfCrossSliceBoundaryEnable = 1; //pCfg->vpCfg.lfCrossSliceBoundaryEnable; param->betaOffsetDiv2 = 0;//pCfg->vpCfg.betaOffsetDiv2; param->tcOffsetDiv2 = 0; //pCfg->vpCfg.tcOffsetDiv2; param->skipIntraTrans = 1;//pCfg->vpCfg.skipIntraTrans; param->saoEnable = 1;//pCfg->vpCfg.saoEnable; param->intraNxNEnable = 1; //pCfg->vpCfg.intraNxNEnable; /* for CMD_ENC_RC_PARAM */ //pEncOP->rcEnable = InitParam -> rate_control; pEncOP->vbvBufferSize = 3000;//pCfg->VbvBufferSize; param->cuLevelRCEnable = 1; //pCfg->vpCfg.cuLevelRCEnable; param->hvsQPEnable = 1;//pCfg->vpCfg.hvsQPEnable; param->hvsQpScale = 2;//pCfg->vpCfg.hvsQpScale; param->bitAllocMode = 0; //pCfg->vpCfg.bitAllocMode; for (i = 0; i < MAX_GOP_NUM; i++) { param->fixedBitRatio[i] = 1;//pCfg->vpCfg.fixedBitRatio[i]; } if (InitParam->qp_mode == 1) { param->minQpI = InitParam->minQP; param->minQpP = InitParam->minQP_P; param->minQpB = InitParam->minQP_B; param->maxQpI = InitParam->maxQP; param->maxQpP = InitParam->maxQP_P; param->maxQpB = InitParam->maxQP_B; param->hvsMaxDeltaQp = InitParam->maxDeltaQP; } else { param->minQpI = 8; param->minQpP = 8; param->minQpB = 8; param->maxQpI = 51; param->maxQpP = 51; param->maxQpB = 51; param->hvsMaxDeltaQp = 10; } if (param->maxQpI < param->maxQpP && param->gopPresetIdx != PRESET_IDX_ALL_I) param->QpModify = 1; if (param->gopPresetIdx == PRESET_IDX_CUSTOM_GOP) { /* for CMD_ENC_CUSTOM_GOP_PARAM */ if (param->intraPeriod % param->gopParam.customGopSize) { VLOG(ERR, "[ERROR] Gop length %d have to be multiple of %d\n", param->intraPeriod, param->gopParam.customGopSize); return FALSE; } for (i= 0; igopParam.customGopSize; i++) { param->gopParam.picParam[i].picType = GopParam->picType; param->gopParam.picParam[i].pocOffset = GopParam->pocOffset; param->gopParam.picParam[i].picQp = GopParam->picQp + param->intraQP + InitParam->cust_qp_delta; if (param->gopParam.picParam[i].picQp > 63) param->gopParam.picParam[i].picQp = 63; param->gopParam.picParam[i].refPocL0 = GopParam->refPocL0; param->gopParam.picParam[i].refPocL1 = GopParam->refPocL1; param->gopParam.picParam[i].temporalId = GopParam->temporalId; param->gopParam.picParam[i].numRefPicL0 = GopParam->numRefPicL0; GopParam++; VLOG(INFO, "CustGop%d type %d offset %d, Qp %d, ref %d, %d, temp %d refn %d \n", i, param->gopParam.picParam[i].picType, param->gopParam.picParam[i].pocOffset, param->gopParam.picParam[i].picQp, param->gopParam.picParam[i].refPocL0, param->gopParam.picParam[i].refPocL1, param->gopParam.picParam[i].temporalId, param->gopParam.picParam[i].numRefPicL0); } } param->roiEnable = InitParam->roi_enable; //pCfg->vpCfg.roiEnable; // VPS & VUI param->numUnitsInTick = 1000;//pCfg->vpCfg.numUnitsInTick; if (pEncOP->bitstreamFormat == STD_AVC) param->timeScale = pEncOP->frameRateInfo * 2000; else param->timeScale = pEncOP->frameRateInfo * 1000; //pCfg->vpCfg.timeScale; param->numTicksPocDiffOne = 0;//pCfg->vpCfg.numTicksPocDiffOne; param->chromaCbQpOffset = 0; //pCfg->vpCfg.chromaCbQpOffset; param->chromaCrQpOffset = 0; //pCfg->vpCfg.chromaCrQpOffset; param->initialRcQp = -1; //63//pCfg->vpCfg.initialRcQp; param->nrYEnable = 1; //pCfg->vpCfg.nrYEnable; param->nrCbEnable = 1; //pCfg->vpCfg.nrCbEnable; param->nrCrEnable = 1; //pCfg->vpCfg.nrCrEnable; param->nrNoiseEstEnable = 1;// pCfg->vpCfg.nrNoiseEstEnable; param->nrNoiseSigmaY = 0; //pCfg->vpCfg.nrNoiseSigmaY; param->nrNoiseSigmaCb = 0; //pCfg->vpCfg.nrNoiseSigmaCb; param->nrNoiseSigmaCr = 0; //pCfg->vpCfg.nrNoiseSigmaCr; param->nrIntraWeightY = 7; //pCfg->vpCfg.nrIntraWeightY; param->nrIntraWeightCb = 7; //pCfg->vpCfg.nrIntraWeightCb; param->nrIntraWeightCr = 7; //pCfg->vpCfg.nrIntraWeightCr; param->nrInterWeightY = 4; //pCfg->vpCfg.nrInterWeightY; param->nrInterWeightCb = 4; //pCfg->vpCfg.nrInterWeightCb; param->nrInterWeightCr = 4; //pCfg->vpCfg.nrInterWeightCr; param->monochromeEnable = 0; //pCfg->vpCfg.monochromeEnable; param->strongIntraSmoothEnable = 1; //pCfg->vpCfg.strongIntraSmoothEnable; param->weightPredEnable = InitParam->weight_pred_enable; //pCfg->vpCfg.weightPredEnable; param->bgDetectEnable = 0; //pCfg->vpCfg.bgDetectEnable; param->bgThrDiff = 8; //pCfg->vpCfg.bgThrDiff; param->bgThrMeanDiff = 1;//pCfg->vpCfg.bgThrMeanDiff; param->bgLambdaQp = 32 ; //pCfg->vpCfg.bgLambdaQp; param->bgDeltaQp = 3; //pCfg->vpCfg.bgDeltaQp; param->customLambdaEnable = 0; //pCfg->vpCfg.customLambdaEnable; param->customMDEnable = 0; //pCfg->vpCfg.customMDEnable; param->pu04DeltaRate = 0; //pCfg->vpCfg.pu04DeltaRate; param->pu08DeltaRate = 0; //pCfg->vpCfg.pu08DeltaRate; param->pu16DeltaRate = 0; //pCfg->vpCfg.pu16DeltaRate; param->pu32DeltaRate = 0; //pCfg->vpCfg.pu32DeltaRate; param->pu04IntraPlanarDeltaRate = 0; //pCfg->vpCfg.pu04IntraPlanarDeltaRate; param->pu04IntraDcDeltaRate = 0; //pCfg->vpCfg.pu04IntraDcDeltaRate; param->pu04IntraAngleDeltaRate = 0; //pCfg->vpCfg.pu04IntraAngleDeltaRate; param->pu08IntraPlanarDeltaRate = 0; //pCfg->vpCfg.pu08IntraPlanarDeltaRate; param->pu08IntraDcDeltaRate = 0; //pCfg->vpCfg.pu08IntraDcDeltaRate; param->pu08IntraAngleDeltaRate = 0; //pCfg->vpCfg.pu08IntraAngleDeltaRate; param->pu16IntraPlanarDeltaRate = 0; //pCfg->vpCfg.pu16IntraPlanarDeltaRate; param->pu16IntraDcDeltaRate = 0; //pCfg->vpCfg.pu16IntraDcDeltaRate; param->pu16IntraAngleDeltaRate = 0; //pCfg->vpCfg.pu16IntraAngleDeltaRate; param->pu32IntraPlanarDeltaRate = 0; // pCfg->vpCfg.pu32IntraPlanarDeltaRate; param->pu32IntraDcDeltaRate = 0; //pCfg->vpCfg.pu32IntraDcDeltaRate; param->pu32IntraAngleDeltaRate = 0; //pCfg->vpCfg.pu32IntraAngleDeltaRate; param->cu08IntraDeltaRate = 0; //pCfg->vpCfg.cu08IntraDeltaRate; param->cu08InterDeltaRate = 0; //pCfg->vpCfg.cu08InterDeltaRate; param->cu08MergeDeltaRate = 0; //pCfg->vpCfg.cu08MergeDeltaRate; param->cu16IntraDeltaRate = 0; //pCfg->vpCfg.cu16IntraDeltaRate; param->cu16InterDeltaRate = 0; //pCfg->vpCfg.cu16InterDeltaRate; param->cu16MergeDeltaRate = 0; //pCfg->vpCfg.cu16MergeDeltaRate; param->cu32IntraDeltaRate = 0; // pCfg->vpCfg.cu32IntraDeltaRate; param->cu32InterDeltaRate = 0; //pCfg->vpCfg.cu32InterDeltaRate; param->cu32MergeDeltaRate = 0; //pCfg->vpCfg.cu32MergeDeltaRate; param->coefClearDisable = 0; //pCfg->vpCfg.coefClearDisable; param->rcWeightParam = 16; //pCfg->vpCfg.rcWeightParam; param->rcWeightBuf = 128; //pCfg->vpCfg.rcWeightBuf; param->s2fmeDisable = 0; //pCfg->vpCfg.s2fmeDisable; // for H.264 on VP if (pEncOP->bitstreamFormat == STD_AVC) param->avcIdrPeriod = param->intraPeriod; else param->avcIdrPeriod = 0; //pCfg->vpCfg.idrPeriod; param->rdoSkip = 1; //pCfg->vpCfg.rdoSkip; param->lambdaScalingEnable = 1; //pCfg->vpCfg.lambdaScalingEnable; if (pEncOP->bitstreamFormat == STD_AVC && (param->profile == AVC_PROFILE_BASE || param->profile == AVC_PROFILE_MAIN)) { param->transform8x8Enable = 0; //pCfg->vpCfg.transform8x8; } else { param->transform8x8Enable = 1; //pCfg->vpCfg.transform8x8; } if (InitParam->slice_mode && InitParam->slice_mode <= 1 && InitParam->slice_arg && pEncOP->bitstreamFormat == STD_AVC) { param->avcSliceMode = InitParam->slice_mode; param->avcSliceArg = InitParam->slice_arg & 0xffff; VLOG(INFO, "AVC:avcSliceMode %d , avcSliceArg %d\n", param->avcSliceMode, param->avcSliceArg); } else { param->avcSliceMode = 0; //pCfg->vpCfg.avcSliceMode; param->avcSliceArg = 0; //pCfg->vpCfg.avcSliceArg; } if (InitParam->IntraRefreshMode && InitParam->IntraRefreshMode <= 3 && InitParam->IntraRefreshArg && pEncOP->bitstreamFormat == STD_AVC) { param->intraMbRefreshMode = InitParam->IntraRefreshMode; param->intraMbRefreshArg = InitParam->IntraRefreshArg; VLOG(INFO, "AVC:intraMbRefreshMode %d , intraMbRefreshArg %d\n", param->intraMbRefreshMode, param->intraMbRefreshArg); } else { param->intraMbRefreshMode = 0; //pCfg->vpCfg.intraMbRefreshMode; param->intraMbRefreshArg = 1; //pCfg->vpCfg.intraMbRefreshArg; } param->mbLevelRcEnable = 0; //pCfg->vpCfg.mbLevelRc; if (pEncOP->bitstreamFormat == STD_AVC && param->profile == AVC_PROFILE_BASE) { param->entropyCodingMode = 0; //pCfg->vpCfg.entropyCodingMode; } else { param->entropyCodingMode = 1; //pCfg->vpCfg.entropyCodingMode; } pEncOP->streamBufCount = ENC_STREAM_BUF_COUNT; if (InitParam->es_buf_sz) pEncOP->streamBufSize = InitParam->es_buf_sz; else pEncOP->streamBufSize = ENC_STREAM_BUF_SIZE; if (pEncOP->streamBufCount < COMMAND_QUEUE_DEPTH ) pEncOP->streamBufCount = COMMAND_QUEUE_DEPTH; //for encoder->numSinkPortQueue if (InitParam->srcBitDepth == 10) pEncOP->srcFormat = FORMAT_420_P10_16BIT_MSB; else pEncOP->srcFormat = FORMAT_420; if (InitParam->fmt == AMVENC_NV21) pEncOP->nv21 = 1; else pEncOP->nv21 = 0; pEncOP->packedFormat = 0; if (InitParam->fmt == AMVENC_NV21 || InitParam->fmt == AMVENC_NV12) pEncOP->cbcrInterleave = 1; else pEncOP->cbcrInterleave = 0; pEncOP->frameEndian = VPU_FRAME_ENDIAN; pEncOP->streamEndian = VPU_STREAM_ENDIAN; pEncOP->sourceEndian = VPU_SOURCE_ENDIAN; pEncOP->lineBufIntEn = 1; pEncOP->coreIdx = 0; pEncOP->cbcrOrder = CBCR_ORDER_NORMAL; pEncOP->lowLatencyMode = 0; // 2bits lowlatency mode setting. bit[1]: low latency interrupt enable, bit[0]: fast bitstream-packing enable. return TRUE; } static ENC_INT_STATUS HandlingInterruptFlag(AMVMultiCtx* ctx) { EncHandle handle = ctx->enchandle; Int32 interruptFlag = 0; Uint32 interruptWaitTime = VPU_WAIT_TIME_OUT_CQ; Uint32 interruptTimeout = VPU_ENC_TIMEOUT; ENC_INT_STATUS status = ENC_INT_STATUS_NONE; if (ctx->startTimeout == 0ULL) { ctx->startTimeout = osal_gettime(); } do { interruptFlag = VPU_WaitInterruptEx(handle, interruptWaitTime); if (INTERRUPT_TIMEOUT_VALUE == interruptFlag) { Uint64 currentTimeout = osal_gettime(); if ((currentTimeout - ctx->startTimeout) > interruptTimeout) { VLOG(ERR, "startTimeout(%ld) currentTime(%ld) diff(%d)\n", ctx->startTimeout, currentTimeout, (Uint32)(currentTimeout - ctx->startTimeout)); status = ENC_INT_STATUS_TIMEOUT; break; } interruptFlag = 0; } if (interruptFlag < 0) VLOG(ERR, "interruptFlag is negative value! %08x\n", interruptFlag); if (interruptFlag > 0) { VPU_ClearInterruptEx(handle, interruptFlag); ctx->startTimeout = 0ULL; if (interruptFlag & (1<instIndex, encodedInfo->encPicCnt, encodedInfo->picType, encodedInfo->reconFrameIndex, encodedInfo->rdPtr, encodedInfo->wrPtr, encodedInfo->bitstreamSize, (srcEndFlag == 1 ? -1 : srcFrameIdx), encodedInfo->encSrcIdx, encodedInfo->releaseSrcFlag, 0, encodedInfo->frameCycle, encodedInfo->encHostCmdTick, encodedInfo->encPrepareStartTick, encodedInfo->encPrepareEndTick, encodedInfo->prepareCycle, encodedInfo->encProcessingStartTick, encodedInfo->encProcessingEndTick, encodedInfo->processing, encodedInfo->encEncodeStartTick, encodedInfo->encEncodeEndTick, encodedInfo->EncodedCycle, queueStatus.reportQueueCount, queueStatus.instanceQueueCount); } else { VLOG(INFO, "%02d %5d %5d %5d %08x %08x %8x %2d %2d %04x %d %8d %8d %8d %8d %d %d\n", handle->instIndex, encodedInfo->encPicCnt, encodedInfo->picType, encodedInfo->reconFrameIndex, encodedInfo->rdPtr, encodedInfo->wrPtr, encodedInfo->bitstreamSize, (srcEndFlag == 1 ? -1 : srcFrameIdx), encodedInfo->encSrcIdx, encodedInfo->releaseSrcFlag, 0, encodedInfo->frameCycle, encodedInfo->prepareCycle, encodedInfo->processing, encodedInfo->EncodedCycle, queueStatus.reportQueueCount, queueStatus.instanceQueueCount); } } } static BOOL SetSequenceInfo (AMVMultiCtx* ctx) { EncHandle handle = ctx->enchandle; RetCode ret = RETCODE_SUCCESS; EncInitialInfo* initialInfo = &ctx->initialInfo; ENC_INT_STATUS status; int retry_cnt = 0; do { ret = VPU_EncIssueSeqInit(handle); retry_cnt++; } while (ret == RETCODE_QUEUEING_FAILURE && retry_cnt < 10); if (ret != RETCODE_SUCCESS) { VLOG(ERR, "Failed to VPU_EncIssueSeqInit() ret(%d)\n", ret); ChekcAndPrintDebugInfo(handle, TRUE, ret); return FALSE; } VLOG(INFO, "Encoder issue VPU_EncIssueSeq \n"); retry_cnt = 0; while (retry_cnt++ < 100) { if ((status = HandlingInterruptFlag(ctx)) == ENC_INT_STATUS_DONE) { break; } else if (status == ENC_INT_STATUS_NONE) { Uint32 intr_usr = 0; VPU_CheckInterrupt(ctx->enchandle, &intr_usr); VLOG(DEBUG, "Check interrupt status inst #%d retry %d result %d\n", ctx->enchandle->instIndex, retry_cnt, intr_usr); if (intr_usr) { VLOG(DEBUG, "Check and cleared interrupt inst #%d go ahead\n", ctx->enchandle->instIndex); break; } usleep(1000); } else if (status == ENC_INT_STATUS_TIMEOUT) { VLOG(INFO, "INSTANCE #%d INTERRUPT TIMEOUT\n", handle->instIndex); HandleEncoderError(ctx->enchandle, ctx->frameIdx, NULL); return FALSE; } else { VLOG(INFO, "Unknown interrupt status: %d\n", status); return FALSE; } usleep(1000); } if ((ret=VPU_EncCompleteSeqInit(handle, initialInfo)) != RETCODE_SUCCESS) { VLOG(ERR, "FAILED TO ENC_PIC_HDR: ret(%d), SEQERR(%08x)\n", ret, initialInfo->seqInitErrReason); ChekcAndPrintDebugInfo(handle, TRUE, ret); return FALSE; } ctx->fb_num = initialInfo->minFrameBufferCount; ctx->src_num = initialInfo->minSrcFrameCount + 2 + EXTRA_SRC_BUFFER_NUM; if (ctx->encOpenParam.sourceBufCount > ctx->src_num) ctx->src_num = ctx->encOpenParam.sourceBufCount; VLOG(TRACE, "Required buffer fb_num=%d, src_num=%d, actual src=%d %dx%d\n", ctx->fb_num, initialInfo->minSrcFrameCount, ctx->src_num, ctx->encOpenParam.picWidth, ctx->encOpenParam.picHeight); VLOG(INFO, "Done sequence init can setup BW and performance Monitor here\n"); return TRUE; } static BOOL AllocateReconFrameBuffer(AMVMultiCtx* ctx) { Uint32 i; Uint32 reconFbSize; Uint32 reconFbStride; Uint32 reconFbWidth; Uint32 reconFbHeight; EncOpenParam encOpenParam = ctx->encOpenParam; if (ctx->encOpenParam.bitstreamFormat == STD_AVC) { reconFbWidth = VPU_ALIGN16(encOpenParam.picWidth); reconFbHeight = VPU_ALIGN16(encOpenParam.picHeight); } else { reconFbWidth = VPU_ALIGN8(encOpenParam.picWidth); reconFbHeight = VPU_ALIGN8(encOpenParam.picHeight); } if ((ctx->mInitParams.rotate_angle != 0 || ctx->mInitParams.mirror != 0) && !(ctx->mInitParams.rotate_angle == 180 && ctx->mInitParams.mirror == MIRDIR_HOR_VER)) { reconFbWidth = VPU_ALIGN32(encOpenParam.picWidth); reconFbHeight = VPU_ALIGN32(encOpenParam.picHeight); } if (ctx->mInitParams.rotate_angle == 90 || ctx->mInitParams.rotate_angle== 270) { reconFbWidth = VPU_ALIGN32(encOpenParam.picHeight); reconFbHeight = VPU_ALIGN32(encOpenParam.picWidth); } /* Allocate framebuffers for recon. */ reconFbStride = CalcStride(reconFbWidth, reconFbHeight, (FrameBufferFormat)encOpenParam.outputFormat, encOpenParam.cbcrInterleave, COMPRESSED_FRAME_MAP, FALSE); reconFbSize = VPU_GetFrameBufSize(ctx->enchandle, encOpenParam.coreIdx, reconFbStride, reconFbHeight, COMPRESSED_FRAME_MAP, (FrameBufferFormat)encOpenParam.outputFormat, encOpenParam.cbcrInterleave, NULL); for (i = 0; i < ctx->fb_num; i++) { ctx->pFbReconMem[i].size = reconFbSize; if (vdi_allocate_dma_memory(encOpenParam.coreIdx, &ctx->pFbReconMem[i], ENC_FBC, ctx->enchandle->instIndex) < 0) { ctx->pFbReconMem[i].size = 0; VLOG(ERR, "fail to allocate recon buffer\n"); return FALSE; } ctx->pFbRecon[i].bufY = ctx->pFbReconMem[i].phys_addr; ctx->pFbRecon[i].bufCb = (PhysicalAddress) - 1; ctx->pFbRecon[i].bufCr = (PhysicalAddress) - 1; ctx->pFbRecon[i].size = reconFbSize; ctx->pFbRecon[i].updateFbInfo = TRUE; ctx->pFbRecon[i].dma_buf_planes = 0; } ctx->reconFbStride = reconFbStride; ctx->reconFbHeight = reconFbHeight; return TRUE; } static BOOL AllocateYuvBufferHeader(AMVMultiCtx* ctx) { Uint32 i; FrameBufferAllocInfo srcFbAllocInfo; Uint32 srcFbSize; Uint32 srcFbStride; EncOpenParam encOpenParam = ctx->encOpenParam; Uint32 srcFbWidth = VPU_ALIGN8(encOpenParam.picWidth); Uint32 srcFbHeight = VPU_ALIGN8(encOpenParam.picHeight); memset(&srcFbAllocInfo, 0x00, sizeof(FrameBufferAllocInfo)); /* calculate sourec buffer stride and size */ srcFbStride = CalcStride(srcFbWidth, srcFbHeight, (FrameBufferFormat)encOpenParam.srcFormat, encOpenParam.cbcrInterleave, LINEAR_FRAME_MAP, FALSE); srcFbSize = VPU_GetFrameBufSize(ctx->enchandle, encOpenParam.coreIdx, srcFbStride, srcFbHeight, LINEAR_FRAME_MAP, (FrameBufferFormat)encOpenParam.srcFormat, encOpenParam.cbcrInterleave, NULL); srcFbAllocInfo.mapType = LINEAR_FRAME_MAP; srcFbAllocInfo.format = (FrameBufferFormat)encOpenParam.srcFormat; srcFbAllocInfo.cbcrInterleave = encOpenParam.cbcrInterleave; srcFbAllocInfo.stride = srcFbStride; srcFbAllocInfo.height = srcFbHeight; srcFbAllocInfo.endian = encOpenParam.sourceEndian; srcFbAllocInfo.type = FB_TYPE_PPU; srcFbAllocInfo.num = ctx->src_num; srcFbAllocInfo.nv21 = encOpenParam.nv21; #if 0 //allocat later when use input buffer, in case DMA buffer no need copy. for (i = 0; i < ctx->src_num; i++) { ctx->pFbSrcMem[i].size = srcFbSize; if (vdi_allocate_dma_memory(encOpenParam.coreIdx, &ctx->pFbSrcMem[i], ENC_SRC, ctx->enchandle->instIndex) < 0) { VLOG(ERR, "fail to allocate src buffer\n"); ctx->pFbSrcMem[i].size = 0; return FALSE; } ctx->pFbSrc[i].bufY = ctx->pFbSrcMem[i].phys_addr; ctx->pFbSrc[i].bufCb = (PhysicalAddress) - 1; ctx->pFbSrc[i].bufCr = (PhysicalAddress) - 1; ctx->pFbSrc[i].updateFbInfo = TRUE; } #else for (i = 0; i < ctx->src_num; i++) { ctx->pFbSrc[i].bufY = (PhysicalAddress) 0; ctx->pFbSrc[i].bufCb = (PhysicalAddress) - 1; ctx->pFbSrc[i].bufCr = (PhysicalAddress) - 1; ctx->pFbSrc[i].updateFbInfo = TRUE; } #endif ctx->srcFbStride = srcFbStride; ctx->srcFbSize = srcFbSize; ctx->srcFbAllocInfo = srcFbAllocInfo; return TRUE; } static BOOL RegisterFrameBuffers(AMVMultiCtx *ctx) { FrameBuffer *pReconFb; FrameBuffer *pSrcFb; FrameBufferAllocInfo srcFbAllocInfo; Uint32 reconFbStride; Uint32 reconFbHeight; RetCode result; Uint32 idx; pReconFb = ctx->pFbRecon; reconFbStride = ctx->reconFbStride; reconFbHeight = ctx->reconFbHeight; result = VPU_EncRegisterFrameBuffer(ctx->enchandle, pReconFb, ctx->fb_num, reconFbStride, reconFbHeight, COMPRESSED_FRAME_MAP); if (result != RETCODE_SUCCESS) { VLOG(ERR, "Failed to VPU_EncRegisterFrameBuffer(%d)\n", result); ChekcAndPrintDebugInfo(ctx->enchandle, TRUE, result); return FALSE; } VLOG(INFO, " finish register frame buffer \n"); pSrcFb = ctx->pFbSrc; srcFbAllocInfo = ctx->srcFbAllocInfo; if (VPU_EncAllocateFrameBuffer(ctx->enchandle, srcFbAllocInfo, pSrcFb) != RETCODE_SUCCESS) { VLOG(ERR, "VPU_EncAllocateFrameBuffer fail to allocate source frame buffer\n"); ChekcAndPrintDebugInfo(ctx->enchandle, TRUE, result); return FALSE; } if (ctx->mInitParams.roi_enable || ctx->mInitParams.lambda_map_enable || ctx->mInitParams.mode_map_enable) { ctx->CustomMapSize = (ctx->encOpenParam.bitstreamFormat == STD_AVC) ? MAX_MB_NUM : MAX_CTU_NUM * 8; if (ctx->mInitParams.roi_enable) { ctx->CustomRoiMapBuf = (unsigned char *)malloc(ctx->CustomMapSize); //set default qp value to 30; memset(ctx->CustomRoiMapBuf, 0x1e , ctx->CustomMapSize); } if (ctx->mInitParams.mode_map_enable) { ctx->CustomModeMapBuf = (unsigned char *)malloc(ctx->CustomMapSize); memset(ctx->CustomModeMapBuf, 0x0 , ctx->CustomMapSize); } if (ctx->mInitParams.lambda_map_enable && ctx->encOpenParam.bitstreamFormat == STD_HEVC) { ctx->CustomLambdaMapBuf = (unsigned char *)malloc(ctx->CustomMapSize); memset(ctx->CustomLambdaMapBuf, 0x0 , ctx->CustomMapSize); } ctx->CustomUpdateID ++; // force the first update for (idx = 0; idx < ctx->src_num; idx++) { ctx->vbCustomMap[idx].size = ctx->CustomMapSize; if (vdi_allocate_dma_memory(ctx->encOpenParam.coreIdx, &ctx->vbCustomMap[idx], ENC_ETC, ctx->enchandle->instIndex) < 0) { ctx->vbCustomMap[idx].size = 0; VLOG(ERR, "fail to allocate ROI buffer\n"); return FALSE; } } } return TRUE; } static void SetCustMapData(AMVMultiCtx *ctx, EncParam *encParam, unsigned long addrCustomMap) { Uint8 *roiMapBuf; int sumQp = 0; int h, w, bufSize; if (ctx->encOpenParam.bitstreamFormat == STD_AVC) { //AvcEncCustomMap customMapBuf[MAX_MB_NUM]; /*For stack optimization*/ AvcEncCustomMap *customMapBuf = (AvcEncCustomMap *)osal_malloc(MAX_MB_NUM * sizeof(AvcEncCustomMap)); if (customMapBuf == NULL) { VLOG(ERR, "fail to allocate customMapBuf\n"); return; } int MbWidth = VPU_ALIGN16(ctx->encOpenParam.picWidth) >> 4; int MbHeight = VPU_ALIGN16(ctx->encOpenParam.picHeight) >> 4; int mbAddr; //osal_memset(&customMapBuf[0], 0x00, MAX_MB_NUM); osal_memset(customMapBuf, 0x00, MAX_MB_NUM * sizeof(AvcEncCustomMap)); roiMapBuf = ctx->CustomRoiMapBuf; if (encParam->customMapOpt.customRoiMapEnable == 1) { bufSize = MbWidth*MbHeight; for (h = 0; h < MbHeight; h++) { for (w = 0; w < MbWidth; w++) { mbAddr = w + h*MbWidth; customMapBuf[mbAddr].field.mb_qp = MAX(MIN(roiMapBuf[mbAddr]&0x3f, 51), 0); sumQp += customMapBuf[mbAddr].field.mb_qp; } } encParam->customMapOpt.roiAvgQp = (sumQp + (bufSize>>1)) / bufSize; // round off. } if (encParam->customMapOpt.customModeMapEnable == 1) { bufSize = MbWidth*MbHeight; for (h = 0; h < MbHeight; h++) { for (w = 0; w < MbWidth; w++) { mbAddr = w + h*MbWidth; customMapBuf[mbAddr].field.mb_force_mode = ctx->CustomModeMapBuf[mbAddr] & 0x3; } } } encParam->customMapOpt.addrCustomMap = addrCustomMap; vdi_write_memory(ctx->encOpenParam.coreIdx, encParam->customMapOpt.addrCustomMap, (unsigned char*)customMapBuf, MAX_MB_NUM, VDI_LITTLE_ENDIAN); if (customMapBuf) osal_free(customMapBuf); } else { // for HEVC custom map. //EncCustomMap customMapBuf[MAX_CTU_NUM];// custom map 1 CTU data = 64bits /*For stack optimization*/ EncCustomMap *customMapBuf = (EncCustomMap *)osal_malloc(MAX_MB_NUM * sizeof(EncCustomMap)); if (customMapBuf == NULL) { VLOG(ERR, "fail to allocate customMapBuf\n"); return; } int ctuMapWidthCnt = VPU_ALIGN64(ctx->encOpenParam.picWidth) >> 6; int ctuMapHeightCnt = VPU_ALIGN64(ctx->encOpenParam.picHeight) >> 6; int ctuMapStride = VPU_ALIGN64(ctx->encOpenParam.picWidth) >> 6; int subCtuMapStride = VPU_ALIGN64(ctx->encOpenParam.picWidth) >> 5; int ctuPos; Uint8* src; //osal_memset(&customMapBuf[0], 0x00, MAX_CTU_NUM * 8); osal_memset(customMapBuf, 0x00, MAX_MB_NUM * sizeof(EncCustomMap)); roiMapBuf = ctx->CustomRoiMapBuf; if (encParam->customMapOpt.customRoiMapEnable == 1) { bufSize = subCtuMapStride*(VPU_ALIGN64(ctx->encOpenParam.picHeight) >> 5); for (h = 0; h < ctuMapHeightCnt; h++) { src = &roiMapBuf[subCtuMapStride * h * 2]; for (w = 0; w < ctuMapWidthCnt; w++, src += 2) { ctuPos = (h * ctuMapStride + w); // store in the order of sub-ctu. customMapBuf[ctuPos].field.sub_ctu_qp_0 = MAX(MIN(*src, 51), 0); customMapBuf[ctuPos].field.sub_ctu_qp_1 = MAX(MIN(*(src + 1), 51), 0); customMapBuf[ctuPos].field.sub_ctu_qp_2 = MAX(MIN(*(src + subCtuMapStride), 51), 0); customMapBuf[ctuPos].field.sub_ctu_qp_3 = MAX(MIN(*(src + subCtuMapStride + 1), 51), 0); sumQp += (customMapBuf[ctuPos].field.sub_ctu_qp_0 + customMapBuf[ctuPos].field.sub_ctu_qp_1 + customMapBuf[ctuPos].field.sub_ctu_qp_2 + customMapBuf[ctuPos].field.sub_ctu_qp_3); } } encParam->customMapOpt.roiAvgQp = (sumQp + (bufSize>>1)) / bufSize; // round off. } if (encParam->customMapOpt.customLambdaMapEnable == 1) { // bufSize = subCtuMapStride*(VPU_ALIGN64(ctx->encOpenParam.picHeight) >> 5); for (h = 0; h < ctuMapHeightCnt; h++) { src = &(ctx->CustomLambdaMapBuf[subCtuMapStride * 2 * h]); for (w = 0; w < ctuMapWidthCnt; w++, src += 2) { ctuPos = (h * ctuMapStride + w); // store in the order of sub-ctu. customMapBuf[ctuPos].field.lambda_sad_0 = *src; customMapBuf[ctuPos].field.lambda_sad_1 = *(src + 1); customMapBuf[ctuPos].field.lambda_sad_2 = *(src + subCtuMapStride); customMapBuf[ctuPos].field.lambda_sad_3 = *(src + subCtuMapStride + 1); } } } if ((encParam->customMapOpt.customModeMapEnable == 1) || (encParam->customMapOpt.customCoefDropEnable == 1)) { // bufSize = ctuMapWidthCnt * ctuMapHeightCnt; for (h = 0; h < ctuMapHeightCnt; h++) { src = &(ctx->CustomModeMapBuf[ctuMapStride * h]); for (w = 0; w < ctuMapWidthCnt; w++, src++) { ctuPos = (h * ctuMapStride + w); customMapBuf[ctuPos].field.ctu_force_mode = (*src) & 0x3; customMapBuf[ctuPos].field.ctu_coeff_drop = ((*src) >> 2) & 0x1; } } } encParam->customMapOpt.addrCustomMap = addrCustomMap; vdi_write_memory(ctx->encOpenParam.coreIdx, encParam->customMapOpt.addrCustomMap, (unsigned char*)customMapBuf, MAX_CTU_NUM * 8, VDI_LITTLE_ENDIAN); if (customMapBuf) osal_free(customMapBuf); } } #if SUPPORT_SCALE AMVEnc_Status ge2d_colorFormat(AMVEncFrameFmt format) { switch (format) { case AMVENC_RGB888: SRC1_PIXFORMAT = PIXEL_FORMAT_RGB_888; SRC2_PIXFORMAT = PIXEL_FORMAT_RGB_888; return AMVENC_SUCCESS; case AMVENC_RGBA8888: SRC1_PIXFORMAT = PIXEL_FORMAT_RGBA_8888; SRC2_PIXFORMAT = PIXEL_FORMAT_RGBA_8888; return AMVENC_SUCCESS; default: VLOG(ERR, "not support color format!"); return AMVENC_FAIL; } } #endif amv_enc_handle_t AML_MultiEncInitialize(AMVEncInitParams* encParam) { RetCode retCode; VpuAttr productInfo; SecAxiUse secAxiUse; Uint32 coreIdx, idx; RetCode result; AMVMultiCtx * ctx = (AMVMultiCtx* )malloc(sizeof(AMVMultiCtx)); if (ctx == NULL) { VLOG(ERR, "ctx == NULL"); return (amv_enc_handle_t) ctx; } memset(ctx, 0, sizeof(AMVMultiCtx)); ctx->magic_num = MULTI_ENC_MAGIC; ctx->instance_id = 0; ctx->src_idx = 0; ctx->enc_counter = 0; ctx->enc_width = encParam->width; ctx->enc_height = encParam->height; // copy a backup on initial parameter memcpy((char *)&ctx->mInitParams, encParam, sizeof(AMVEncInitParams)); if (encParam->idr_period == -1) { ctx->idrPeriod = 0; } else { ctx->idrPeriod = encParam->idr_period; } VLOG(INFO, "ctx->idrPeriod: %d\n", ctx->idrPeriod); ctx->bitrate = encParam->bitrate; ctx->frame_rate = encParam->frame_rate; ctx->mPrependSPSPPSToIDRFrames = encParam->prepend_spspps_to_idr_frames; ctx->mNumPlanes = 0; if (SetupEncoderOpenParam(&(ctx->encOpenParam), encParam) == FALSE) goto fail_exit; coreIdx = ctx->encOpenParam.coreIdx; retCode = VPU_Init(coreIdx); if (retCode != RETCODE_SUCCESS && retCode != RETCODE_CALLED_BEFORE) { VLOG(INFO, "Failed to VPU_Init, ret(%08x)\n", retCode); goto fail_exit; } retCode = PrintVpuProductInfo(coreIdx, &productInfo); if (retCode == RETCODE_VPU_RESPONSE_TIMEOUT ) { VLOG(INFO, "Failed to PrintVpuProductInfo()\n"); HandleEncoderError(ctx->enchandle, 0, NULL); goto fail_exit; } ctx->cyclePerTick = 32768; if (TRUE == productInfo.supportNewTimer) ctx->cyclePerTick = 256; /* prepare stream buffers */ if (encParam->es_buf_sz) ctx->bsBuffer[0].size = encParam->es_buf_sz; //ENC_STREAM_BUF_SIZE; else ctx->bsBuffer[0].size = ENC_STREAM_BUF_SIZE; // default value if (vdi_allocate_dma_memory(coreIdx, &ctx->bsBuffer[0], ENC_BS, 0) < 0) { VLOG(ERR, "fail to allocate bitstream buffer\n"); ctx->bsBuffer[0].size = 0; goto fail_exit; } ctx->encOpenParam.bitstreamBuffer = ctx->bsBuffer[0].phys_addr; ctx->encOpenParam.bitstreamBufferSize = ctx->bsBuffer[0].size; VLOG(DEBUG, "ctx->encOpenParam.bitstreamBuffer %0x, ctx->encOpenParam.bitstreamBufferSiz %0x", ctx->encOpenParam.bitstreamBuffer, ctx->encOpenParam.bitstreamBufferSize); // real open the encoder with OpenParam if ((result = VPU_EncOpen(&ctx->enchandle, &ctx->encOpenParam)) != RETCODE_SUCCESS) { VLOG(ERR, "VPU_EncOpen failed Error code is 0x%x \n", result); ctx->enchandle = NULL; goto fail_exit; } if (VPU_EncInstParamSync(ctx->enchandle,encParam->GopPreset, encParam->cust_qp_delta,NULL) != RETCODE_SUCCESS) { VLOG(ERR, "VPU instance param sync with open param failed\n"); } // customer buffers allocat and settins /* Allocate Buffer and Set Data as needed*/ if (ctx->encOpenParam.EncStdParam.vpParam.scalingListEnable) { ctx->vbScalingList.size = 0x1000; if (vdi_allocate_dma_memory(coreIdx, &ctx->vbScalingList, ENC_ETC, ctx->enchandle->instIndex) < 0) { VLOG(ERR, "fail to allocate scaling list buffer\n"); ctx->vbScalingList.size = 0; goto fail_exit; } ctx->encOpenParam.EncStdParam.vpParam.userScalingListAddr = ctx->vbScalingList.phys_addr; // fill the scaling list data here, configure APIs to be added //parse_user_scaling_list(&ctx->scalingList, testEncConfig.scaling_list_file, testEncConfig.stdMode); vdi_write_memory(coreIdx, ctx->vbScalingList.phys_addr, (unsigned char*)&ctx->scalingList, ctx->vbScalingList.size, VDI_LITTLE_ENDIAN); } if (ctx->encOpenParam.EncStdParam.vpParam.customLambdaEnable) { ctx->vbCustomLambda.size = 0x200; if (vdi_allocate_dma_memory(coreIdx, &ctx->vbCustomLambda, ENC_ETC, ctx->enchandle->instIndex) < 0) { VLOG(ERR, "fail to allocate Lambda map buffer\n"); ctx->vbCustomLambda.size = 0; goto fail_exit; } ctx->encOpenParam.EncStdParam.vpParam.customLambdaAddr = ctx->vbCustomLambda.phys_addr; // fill the customLambda buffer,API to be add as needed. //parse_custom_lambda(ctx->customLambda, testEncConfig.custom_lambda_file); vdi_write_memory(coreIdx, ctx->vbCustomLambda.phys_addr, (unsigned char*)&ctx->customLambda[0], ctx->vbCustomLambda.size, VDI_LITTLE_ENDIAN); } // set rotate and mirror //VPU_EncGiveCommand(ctx->enchandle, ENABLE_LOGGING, 0); if (encParam->rotate_angle != 0 || encParam->mirror != 0) { VLOG(DEBUG, "set rotate and mirror, angle:%d, mirror:%d\n", ctx->mInitParams.rotate_angle, ctx->mInitParams.mirror); VPU_EncGiveCommand(ctx->enchandle, ENABLE_ROTATION, 0); VPU_EncGiveCommand(ctx->enchandle, ENABLE_MIRRORING, 0); VPU_EncGiveCommand(ctx->enchandle, SET_ROTATION_ANGLE, &ctx->mInitParams.rotate_angle); VPU_EncGiveCommand(ctx->enchandle, SET_MIRROR_DIRECTION, &ctx->mInitParams.mirror); } memset(&secAxiUse, 0x00, sizeof(SecAxiUse)); secAxiUse.u.vp.useEncRdoEnable =0; //USE_RDO_INTERNAL_BUF secAxiUse.u.vp.useEncLfEnable = 0; //USE_LF_INTERNAL_BUF VPU_EncGiveCommand(ctx->enchandle, SET_SEC_AXI, &secAxiUse); VPU_EncGiveCommand(ctx->enchandle, SET_CYCLE_PER_TICK, (void*)&ctx->cyclePerTick); // get the seqence buffer requirement info if (SetSequenceInfo(ctx) == FALSE) goto fail_exit; // Allocate and register encoder frame buffer if (AllocateReconFrameBuffer(ctx) == FALSE) goto fail_exit; if (AllocateYuvBufferHeader(ctx) == FALSE) goto fail_exit; if (RegisterFrameBuffers(ctx) == FALSE) goto fail_exit; #if SUPPORT_SCALE if ((encParam->width != encParam->src_width) || (encParam->height != encParam->src_height) || true) { memset(&ctx->amlge2d,0x0,sizeof(aml_ge2d_t)); aml_ge2d_info_t *pge2dinfo = &ctx->amlge2d.ge2dinfo; memset(pge2dinfo, 0, sizeof(aml_ge2d_info_t)); memset(&(pge2dinfo->src_info[0]), 0, sizeof(buffer_info_t)); memset(&(pge2dinfo->src_info[1]), 0, sizeof(buffer_info_t)); memset(&(pge2dinfo->dst_info), 0, sizeof(buffer_info_t)); set_ge2dinfo(pge2dinfo, encParam); int ret = aml_ge2d_init(&ctx->amlge2d); if (ret < 0) { VLOG(ERR, "encode open ge2d failed, ret=0x%x", ret); return AMVENC_FAIL; } ctx->ge2d_initial_done = 1; ctx->INIT_GE2D = true; } #endif return (amv_enc_handle_t) ctx; fail_exit: if (ctx->enchandle) { if (ctx->vbCustomLambda.size) vdi_free_dma_memory(coreIdx, &ctx->vbCustomLambda, ENC_ETC, ctx->enchandle->instIndex); if (ctx->vbScalingList.size) vdi_free_dma_memory(coreIdx, &ctx->vbScalingList, ENC_ETC, ctx->enchandle->instIndex); } if (ctx->bsBuffer[0].size) vdi_free_dma_memory(coreIdx, &ctx->bsBuffer[0], ENC_BS, 0); if (ctx->enchandle) { for (idx = 0; idx < MAX_REG_FRAME; idx++) { if (ctx->pFbReconMem[idx].size) vdi_free_dma_memory(coreIdx, &ctx->pFbReconMem[idx], ENC_FBC, ctx->enchandle->instIndex); if (ctx->vbCustomMap[idx].size) vdi_free_dma_memory(coreIdx, &ctx->vbCustomMap[idx], ENC_ETC, ctx->enchandle->instIndex); } } if (ctx->CustomRoiMapBuf) { free(ctx->CustomRoiMapBuf); ctx->CustomRoiMapBuf = NULL; } if (ctx->CustomLambdaMapBuf) { free(ctx->CustomLambdaMapBuf); ctx->CustomLambdaMapBuf = NULL; } if (ctx->CustomModeMapBuf) { free(ctx->CustomModeMapBuf); ctx->CustomModeMapBuf = NULL; } if (ctx->enchandle) { for (idx = 0; idx < ENC_SRC_BUF_NUM; idx++) { if (ctx->pFbSrcMem[idx].size) vdi_free_dma_memory(coreIdx, &ctx->pFbSrcMem[idx], ENC_SRC, ctx->enchandle->instIndex); } } if (ctx->enchandle) VPU_EncClose(ctx->enchandle); #if SUPPORT_SCALE if (ctx->ge2d_initial_done) { aml_ge2d_exit(&ctx->amlge2d); } #endif free(ctx); return (amv_enc_handle_t) NULL; } AMVEnc_Status AML_MultiEncSetInput(amv_enc_handle_t ctx_handle, AMVMultiEncFrameIO* input) { Uint32 src_stride; Uint32 luma_stride, chroma_stride; Uint32 size_src_luma; // Uint32 size_src_chroma; char *y_dst = NULL; char *src = NULL; int idx, idx_1, endian; bool width32alinged = true; //width is multiple of 32 or not Uint32 is_DMA_buffer = 0; EncParam * param; int64_t total_cost = 0; int64_t t1, t2, t3, t4, t5, t6, t7; AMVMultiCtx * ctx = (AMVMultiCtx* )ctx_handle; VLOG(NONE, "AML_MultiEncSetInput, canvas=%u, input fmt=%d, num_planes=%d, type=%d", input->canvas, input->fmt, input->num_planes, input->type); if (ctx == NULL) return AMVENC_FAIL; if (ctx->magic_num != MULTI_ENC_MAGIC) return AMVENC_FAIL; ctx->pic_type = input->pic_type; if (input->type == CANVAS_BUFFER) { VLOG(INFO, "set fmt to nv21 for canvas buffer mode as default"); input->fmt = AMVENC_NV21; } if (ctx->enc_width % 32) { width32alinged = false; } if ((input->num_planes >0) && (input->type == DMA_BUFF)) { /* DMA buffer, no need allocate */ is_DMA_buffer = 1; } param = &(ctx->encParam); ctx->op_flag = input->op_flag; ctx->fmt = input->fmt; if (ctx->fmt != AMVENC_NV12 && ctx->fmt != AMVENC_NV21 && ctx->fmt != AMVENC_YUV420P) { #if SUPPORT_SCALE if (ctx->INIT_GE2D) { if (ge2d_colorFormat(ctx->fmt) == AMVENC_SUCCESS) { VLOG(DEBUG, "The %d of color format that HEVC need ge2d to change!", ctx->fmt); } else { VLOG(INFO, "Encoder only support NV12/NV21, not support %d", ctx->fmt); return AMVENC_NOT_SUPPORTED; } } #else VLOG(INFO, "Encoder only support NV12/NV21, not support %d", ctx->fmt); return AMVENC_NOT_SUPPORTED; #endif } VLOG(INFO, "fmt %d , is dma %d \n", ctx->fmt, is_DMA_buffer); if (is_DMA_buffer) { if (!input->pitch) { src_stride = vp_align32(ctx->enc_width); } else { src_stride = input->pitch; if (ctx->fmt == AMVENC_NV12 || ctx->fmt == AMVENC_NV21 || ctx->fmt == AMVENC_YUV420P) { if (src_stride % 16) { VLOG(ERR, "DMA buffer stride %d to 16 byte align\n", src_stride); return AMVENC_FAIL; } } else { src_stride = vp_align32(input->pitch); } } } else { src_stride = vp_align32(ctx->enc_width); } if (src_stride != input->pitch) { width32alinged = false; } luma_stride = src_stride * (ctx->encOpenParam.srcBitDepth == 10 ? 2 : 1); chroma_stride = src_stride * (ctx->encOpenParam.srcBitDepth == 10 ? 2 : 1); // Pick a no used src frame buffer descriptor idx_1 = -1; for (idx = 0; idx< ENC_SRC_BUF_NUM; idx++) { if(ctx->encodedSrcFrmIdxArr[idx] == 0 && ctx->pFbSrcMem[idx].size) break; // free and located mem if(ctx->encodedSrcFrmIdxArr[idx] == 0 && idx_1 == -1) idx_1 = idx; } if (idx == ENC_SRC_BUF_NUM) { //no found, just get a free header if(idx_1 >= 0 ) idx = idx_1; else { VLOG(ERR, "can not find a free src buffer\n"); return AMVENC_FAIL; } } if (idx >= ctx->src_num) { VLOG(ERR, "Too many src buffer request\n"); return AMVENC_FAIL; } if (is_DMA_buffer == 0 && input->type != CANVAS_BUFFER) { // need allocate buffer and copy ctx->pFbSrc[idx].dma_buf_planes = 0; //clean the DMA buffer if(ctx->pFbSrcMem[idx].size == 0) { // allocate buffer ctx->pFbSrcMem[idx].size = ctx->srcFbSize; if (vdi_allocate_dma_memory(ctx->encOpenParam.coreIdx, &ctx->pFbSrcMem[idx], ENC_SRC, ctx->enchandle->instIndex) < 0) { VLOG(ERR, "fail to allocate src buffer, short of memory\n"); ctx->pFbSrcMem[idx].size = 0; return AMVENC_FAIL; } ctx->pFbSrc[idx].bufY = ctx->pFbSrcMem[idx].phys_addr; if (ctx->pFbSrc[idx].bufCb != -1) ctx->pFbSrc[idx].bufCb += ctx->pFbSrc[idx].bufY; if (ctx->pFbSrc[idx].bufCr != -1) ctx->pFbSrc[idx].bufCr += ctx->pFbSrc[idx].bufY; VLOG(INFO, "New allocate frame idx %d address y:0x%x cb:0x%x cr:0x%x\n", idx, ctx->pFbSrc[idx].bufY, ctx->pFbSrc[idx].bufCb,ctx->pFbSrc[idx].bufCr); } #if SUPPORT_SCALE if ((input->scale_width !=0 && input->scale_height !=0) || input->crop_left != 0 || input->crop_right != 0 || input->crop_top != 0 || input->crop_bottom != 0 || (ctx->fmt != AMVENC_NV12 && ctx->fmt != AMVENC_NV21 && ctx->fmt != AMVENC_YUV420P)) { if (ctx->INIT_GE2D) { ctx->INIT_GE2D = false; ctx->amlge2d.ge2dinfo.src_info[0].format = SRC1_PIXFORMAT; ctx->amlge2d.ge2dinfo.src_info[1].format = SRC2_PIXFORMAT; ctx->amlge2d.ge2dinfo.src_info[0].plane_number = 1; ctx->amlge2d.ge2dinfo.dst_info.plane_number = 1; int ret = aml_ge2d_mem_alloc(&ctx->amlge2d); if (ret < 0) { VLOG(ERR, "encode ge2di mem alloc failed, ret=0x%x", ret); return AMVENC_FAIL; } VLOG(DEBUG, "ge2d init successful!"); } VLOG(DEBUG, "HEVC TEST sclale, enc_width:%d enc_height:%d pitch:%d height:%d", ctx->enc_width, ctx->enc_height, input->pitch, input->height); if (ctx->fmt == AMVENC_NV12 || ctx->fmt == AMVENC_NV21 || ctx->fmt == AMVENC_YUV420P) { if (input->pitch % 32) { VLOG(ERR, "HEVC crop and scale must be 32bit aligned"); return AMVENC_FAIL; } } else { width32alinged = true; } if (ctx->fmt == AMVENC_RGBA8888) { memcpy((void *)ctx->amlge2d.ge2dinfo.src_info[0].vaddr[0], (void *)input->YCbCr[0], 4 * input->pitch * input->height); } else if (ctx->fmt == AMVENC_NV12 || ctx->fmt == AMVENC_NV21) { //memcpy((void *)ctx->amlge2d.ge2dinfo.src_info[0].vaddr[0], (void *)input->YCbCr[0], input->pitch * input->height); //memcpy((void *) ((char *)ctx->amlge2d.ge2dinfo.src_info[0].vaddr[0] + input->pitch * input->height), (void *)input->YCbCr[1], input->pitch * input->height / 2); } else if (ctx->fmt == AMVENC_YUV420P) { //memcpy((void *)ctx->amlge2d.ge2dinfo.src_info[0].vaddr[0], (void *)input->YCbCr[0], input->pitch * input->height); //memcpy((void *) ((char *)ctx->amlge2d.ge2dinfo.src_info[0].vaddr[0] + input->pitch * input->height), (void *)input->YCbCr[1], input->pitch * input->height / 4); //memcpy((void *) ((char *)ctx->amlge2d.ge2dinfo.src_info[0].vaddr[0] + (input->pitch * input->height * 5) /4), (void *)input->YCbCr[2], input->pitch * input->height / 4); } else if (ctx->fmt == AMVENC_RGB888) { memcpy((void *)ctx->amlge2d.ge2dinfo.src_info[0].vaddr[0], (void *)input->YCbCr[0], input->pitch * input->height * 3); } do_strechblit(&ctx->amlge2d.ge2dinfo, input); aml_ge2d_invalid_cache(&ctx->amlge2d.ge2dinfo); size_src_luma = luma_stride * vp_align32(ctx->enc_height); // size_src_chroma = luma_stride * (vp_align16(ctx->enc_height) / 2); endian = ctx->pFbSrc[idx].endian; y_dst = (char *) ctx->pFbSrcMem[idx].virt_addr; src = (char *) ctx->amlge2d.ge2dinfo.dst_info.vaddr[0]; yuv_plane_memcpy(ctx->encOpenParam.coreIdx, ctx->pFbSrc[idx].bufY, src, ctx->enc_width, ctx->enc_height, luma_stride, input->pitch, width32alinged, endian); src = (char *) ctx->amlge2d.ge2dinfo.dst_info.vaddr[0] + luma_stride * ctx->enc_height; yuv_plane_memcpy(ctx->encOpenParam.coreIdx, ctx->pFbSrc[idx].bufCb, src, ctx->enc_width, ctx->enc_height / 2, chroma_stride, input->pitch, width32alinged,endian); } else #endif { // set frame buffers // calculate required buffer size size_src_luma = luma_stride * vp_align32(ctx->enc_height); // size_src_chroma = luma_stride * (vp_align16(ctx->enc_height) / 2); endian = ctx->pFbSrc[idx].endian; y_dst = (char *) ctx->pFbSrcMem[idx].virt_addr; //for (idx_1 = 0; idx_1 < size_src_luma; idx_1++) *(y_dst + idx_1) = 0; //for (idx_1 = 0; idx_1 < size_src_chroma; idx_1++) *(y_dst + size_src_luma + idx_1) = 0x80; //memset(y_dst, 0x0, size_src_luma); //memset(y_dst + size_src_luma, 0x80, size_src_chroma); src = (char *) input->YCbCr[0]; VLOG(INFO, "idx %d luma %d src %p dst %p width %d, height %d \n", idx, size_src_luma, src, y_dst, ctx->enc_width, ctx->enc_height); ctx->temp_enc_width = ctx->enc_width; input->temp_pitch = input->pitch; if (!width32alinged && ctx->encOpenParam.srcBitDepth == 10) { ctx->temp_enc_width *= 2; input->temp_pitch *= 2; } yuv_plane_memcpy(ctx->encOpenParam.coreIdx, ctx->pFbSrc[idx].bufY, src, ctx->temp_enc_width, ctx->enc_height, luma_stride, input->temp_pitch, width32alinged, endian); if (ctx->fmt == AMVENC_NV12 || ctx->fmt == AMVENC_NV21) { src = (char *)input->YCbCr[1]; yuv_plane_memcpy(ctx->encOpenParam.coreIdx, ctx->pFbSrc[idx].bufCb, src, ctx->enc_width, ctx->enc_height / 2, chroma_stride, input->pitch, width32alinged,endian); } else if (ctx->fmt == AMVENC_YUV420P) { src = (char *)input->YCbCr[1]; yuv_plane_memcpy(ctx->encOpenParam.coreIdx, ctx->pFbSrc[idx].bufCb, src, ctx->temp_enc_width /2, ctx->enc_height/ 2, chroma_stride / 2, input->temp_pitch / 2, width32alinged, endian); // v_dst = (char *)(ctx->pFbSrcMem[idx].virt_addr + size_src_luma + size_src_luma / 4); src = (char *)input->YCbCr[2]; yuv_plane_memcpy(ctx->encOpenParam.coreIdx, ctx->pFbSrc[idx].bufCr, src, ctx->temp_enc_width/2, ctx->enc_height/ 2, chroma_stride / 2, input->temp_pitch / 2, width32alinged, endian); } } vdi_flush_memory(ctx->encOpenParam.coreIdx, &ctx->pFbSrcMem[idx]); VLOG(INFO,"frame %d stride %d address Y 0x%x cb 0x%x cr 0x%x \n", idx, size_src_luma, ctx->pFbSrc[idx].bufY, ctx->pFbSrc[idx].bufCb,ctx->pFbSrc[idx].bufCr); #if 0 if (ctx->fmt == AMVENC_NV12 || ctx->fmt == AMVENC_NV21) { ctx->pFbSrc[idx].bufCb = (PhysicalAddress) (ctx->pFbSrc[idx].bufY + size_src_luma); ctx->pFbSrc[idx].bufCr = (PhysicalAddress) - 1; ctx->pFbSrc[idx].cbcrInterleave = 1; } else if (ctx->fmt == AMVENC_YUV420P) { ctx->pFbSrc[idx].bufCb = (PhysicalAddress) (ctx->pFbSrc[idx].bufY + size_src_luma); ctx->pFbSrc[idx].bufCr = (PhysicalAddress) (ctx->pFbSrc[idx].bufY + size_src_luma + size_src_luma / 4); } #endif VLOG(INFO,"frame %d stride %d address Y 0x%x cb 0x%x cr 0x%x \n", idx, size_src_luma, ctx->pFbSrc[idx].bufY, ctx->pFbSrc[idx].bufCb,ctx->pFbSrc[idx].bufCr); } else if (input->type == CANVAS_BUFFER) { } else { //DMA buffer #if SUPPORT_SCALE if (ctx->fmt != AMVENC_NV12 && ctx->fmt != AMVENC_NV21 && ctx->fmt != AMVENC_YUV420P) { if (ctx->INIT_GE2D) { ctx->INIT_GE2D = false; ctx->amlge2d.ge2dinfo.src_info[0].format = SRC1_PIXFORMAT; ctx->amlge2d.ge2dinfo.src_info[1].format = SRC2_PIXFORMAT; ctx->amlge2d.ge2dinfo.dst_info.plane_number = 1; int ret = aml_ge2d_mem_alloc(&ctx->amlge2d); if (ret < 0) { VLOG(ERR, "encode ge2di mem alloc failed, ret=0x%x", ret); return AMVENC_FAIL; } VLOG(DEBUG, "ge2d init successful!"); } ctx->amlge2d.ge2dinfo.src_info[0].shared_fd[0] = input->shared_fd[0]; do_strechblit(&ctx->amlge2d.ge2dinfo, input); aml_ge2d_invalid_cache(&ctx->amlge2d.ge2dinfo); ctx->pFbSrc[idx].dma_shared_fd[0] = ctx->amlge2d.ge2dinfo.dst_info.shared_fd[0]; ctx->pFbSrc[idx].dma_buf_planes = 1; ctx->amlge2d.ge2dinfo.src_info[0].shared_fd[0] = -1; VLOG(INFO,"Set DMA buffer index %d planes %d fd[%d]\n", idx, ctx->pFbSrc[idx].dma_buf_planes, ctx->pFbSrc[idx].dma_shared_fd[0]); } else #endif { ctx->pFbSrc[idx].dma_buf_planes = input->num_planes; ctx->pFbSrc[idx].dma_shared_fd[0] = input->shared_fd[0]; ctx->pFbSrc[idx].dma_shared_fd[1] = input->shared_fd[1]; ctx->pFbSrc[idx].dma_shared_fd[2] = input->shared_fd[2]; VLOG(INFO,"Set DMA buffer index %d planes %d fd[%d, %d, %d]\n", idx, input->num_planes, input->shared_fd[0], input->shared_fd[1], input->shared_fd[2]); } } ctx->FrameIO[idx] = *input; ctx->encodedSrcFrmIdxArr[idx] = 1; // occupic the frames. ctx->encMEMSrcFrmIdxArr[param->srcIdx] = idx; //indirect link due to reodering. srcIdx increase linear. ctx->pFbSrc[idx].stride = src_stride * (ctx->encOpenParam.srcBitDepth == 10 ? 2 : 1); /**< A horizontal stride for given frame buffer */ ctx->pFbSrc[idx].height = input->height; /**< A height for given frame buffer */ VLOG(INFO, "Assign src buffer,input idx %d poolidx %d stride %d \n",param->srcIdx, idx, src_stride); if (ctx->bsBuffer[idx].size == 0) { // allocate buffer ctx->bsBuffer[idx].size = ctx->bsBuffer[0].size; //ENC_STREAM_BUF_SIZE; if (vdi_allocate_dma_memory(ctx->encOpenParam.coreIdx, &ctx->bsBuffer[idx], ENC_BS, 0) < 0) { VLOG(ERR, "fail to allocate bitstream buffer\n"); ctx->bsBuffer[idx].size = 0; ctx->encodedSrcFrmIdxArr[idx] = 0; return AMVENC_FAIL; } } //hoan add for canvas ctx->pFbSrc[idx].canvas_index = input->canvas; VLOG(NONE, "[canvas_u] >> input->canvas = 0x%x.\n", input->canvas); //end param->canvas = input->canvas; param->sourceFrame = &ctx->pFbSrc[idx]; param->picStreamBufferAddr = ctx->bsBuffer[idx].phys_addr; param->picStreamBufferSize = ctx->bsBuffer[idx].size; VLOG(INFO, "Assign stream buffer, idx %d address 0x%x size %d\n", idx, param->picStreamBufferAddr, param->picStreamBufferSize); // param->srcIdx = param->srcIdx; param->srcEndFlag = 0; //in->last; // encParam->sourceFrame = &in->fb; param->sourceFrame->sourceLBurstEn = 0; if (ctx->op_flag & AMVEncFrameIO_FORCE_SKIP_FLAG) { param->skipPicture = 1; } else { param->skipPicture = 0; } param->forceAllCtuCoefDropEnable = 0; param->forcePicQpEnable = ctx->mInitParams.forcePicQpEnable; param->forcePicQpI = ctx->mInitParams.forcePicQpI; param->forcePicQpP = ctx->mInitParams.forcePicQpP; param->forcePicQpB = ctx->mInitParams.forcePicQpB; if (ctx->op_flag & AMVEncFrameIO_FORCE_IDR_FLAG) { param->forcePicTypeEnable = 1; param->forcePicType = 3; // 0 for I frame. 3 for IDR } else { param->forcePicTypeEnable = 0; param->forcePicType = 0; } if (ctx->op_flag & AMVEncFrameIO_USE_AS_LTR_FLAG) { VLOG(INFO, "Update seCurSrcAsLongtermPic\n"); param->useCurSrcAsLongtermPic = 1; } else { param->useCurSrcAsLongtermPic = 0; } if (ctx->op_flag & AMVEncFrameIO_USE_LTR_FLAG) { VLOG(INFO, "Update useLongtermRef\n"); param->useLongtermRef = 1; } else { param->useLongtermRef = 0; } // FW will encode header data implicitly when changing the header syntaxes param->codeOption.implicitHeaderEncode = 1; param->codeOption.encodeAUD = 0; param->codeOption.encodeEOS = 0; param->codeOption.encodeEOB = 0; // set custom map param param->customMapOpt.customLambdaMapEnable = ctx->mInitParams.lambda_map_enable; param->customMapOpt.customModeMapEnable = ctx->mInitParams.mode_map_enable & 0x1; param->customMapOpt.customCoefDropEnable = (ctx->mInitParams.mode_map_enable & 0x2) >> 1; param->customMapOpt.customRoiMapEnable = ctx->mInitParams.roi_enable; if (param->customMapOpt.customRoiMapEnable || param->customMapOpt.customLambdaMapEnable || param->customMapOpt.customModeMapEnable || param->customMapOpt.customCoefDropEnable) { if (ctx->CustomMapUpdatedId[idx] != ctx->CustomUpdateID) { // the custom map changed refresh the data // packaging roi/lambda/mode data to custom map buffer ctx->CustomMapUpdatedId[idx] = ctx->CustomUpdateID; SetCustMapData(ctx, param, ctx->vbCustomMap[idx].phys_addr); VLOG(INFO, "ROI updted buffer addr %x \n", param->customMapOpt.addrCustomMap); } else { param->customMapOpt.addrCustomMap = ctx->vbCustomMap[idx].phys_addr; VLOG(INFO, "ROI no updte use old buffer addr %x \n", param->customMapOpt.addrCustomMap); } } // weighted prediction if ((ctx->mInitParams.weight_pred_enable & 0x1)) { Uint32 meanY, meanCb, meanCr, sigmaY, sigmaCb, sigmaCr; Uint32 maxMean = (ctx->encOpenParam.bitstreamFormat == STD_AVC) ? 0xffff : ((1 << ctx->encOpenParam.EncStdParam.vpParam.internalBitDepth) - 1); Uint32 maxSigma = (ctx->encOpenParam.bitstreamFormat == STD_AVC) ? 0xffff : ((1 << (ctx->encOpenParam.EncStdParam.vpParam.internalBitDepth + 6)) - 1); meanY = ctx->wp_para.CustMeanY; meanCb = ctx->wp_para.CustMeadCb; meanCr = ctx->wp_para.CustMeadCr; sigmaY = ctx->wp_para.CustSigmaY; sigmaCb = ctx->wp_para.CustSigmaCb; sigmaCr = ctx->wp_para.CustSigmaCr; meanY = max(min(maxMean, meanY), 0); meanCb = max(min(maxMean, meanCb), 0); meanCr = max(min(maxMean, meanCr), 0); sigmaY = max(min(maxSigma, sigmaY), 0); sigmaCb = max(min(maxSigma, sigmaCb), 0); sigmaCr = max(min(maxSigma, sigmaCr), 0); // set weighted prediction param. param->wpPixSigmaY = sigmaY; param->wpPixSigmaCb = sigmaCb; param->wpPixSigmaCr = sigmaCr; param->wpPixMeanY = meanY; param->wpPixMeanCb = meanCb; param->wpPixMeanCr = meanCr; } return AMVENC_SUCCESS; } AMVEnc_Status AML_MultiEncUpdateRoi(amv_enc_handle_t ctx_handle, unsigned char* buffer, int size) { int required_size; AMVMultiCtx * ctx = (AMVMultiCtx* ) ctx_handle; VLOG(INFO, "Update Roi table update cur update ID %d size %d\n", ctx->CustomUpdateID, size); if (ctx == NULL) return AMVENC_FAIL; if (ctx->magic_num != MULTI_ENC_MAGIC) return AMVENC_FAIL; if (ctx->mInitParams.roi_enable == 0) return AMVENC_FAIL; if (ctx->encOpenParam.bitstreamFormat == STD_AVC) required_size = ((ctx->enc_width + 15)/16)*((ctx->enc_height + 15)/16); else //HEVC required_size = ((ctx->enc_width + 31)/32)*((ctx->enc_height + 31)/32); VLOG(INFO, "Update Roi table update cur update ID %d size %d required %d \n", ctx->CustomUpdateID, size, required_size); if (size < required_size) return AMVENC_FAIL; memcpy(ctx->CustomRoiMapBuf, buffer, required_size); ctx->CustomUpdateID ++; if (ctx->CustomUpdateID > 256) ctx->CustomUpdateID = 1; return AMVENC_SUCCESS; } AMVEnc_Status AML_MultiEncChangeBitRate(amv_enc_handle_t ctx_handle, int BitRate) { AMVMultiCtx * ctx = (AMVMultiCtx* ) ctx_handle; EncChangeParam *ChgParam; if (ctx == NULL) return AMVENC_FAIL; if (ctx->magic_num != MULTI_ENC_MAGIC) return AMVENC_FAIL; if (ctx->mInitParams.param_change_enable == 0) return AMVENC_FAIL; VLOG(INFO, "Change bit rate to %d Count %d\n", BitRate, ctx->changedCount); ChgParam = &ctx->changeParam; ChgParam->bitRate = BitRate; ChgParam->enable_option |= ENC_SET_CHANGE_PARAM_RC_TARGET_RATE; ctx->param_change_flag ++; return AMVENC_SUCCESS; } AMVEnc_Status AML_MultiEncChangeFrameRate(amv_enc_handle_t ctx_handle, int FrameRate,int BitRate) { AMVMultiCtx * ctx = (AMVMultiCtx* ) ctx_handle; EncChangeParam *ChgParam; if (ctx == NULL) return AMVENC_FAIL; if (ctx->magic_num != MULTI_ENC_MAGIC) return AMVENC_FAIL; if (ctx->mInitParams.param_change_enable == 0) return AMVENC_FAIL; VLOG(INFO, "Change frame rate to %d Count %d\n", FrameRate, ctx->changedCount); ChgParam = &ctx->changeParam; ChgParam->frameRate = FrameRate; ChgParam->bitRate = BitRate * ctx->encOpenParam.frameRateInfo / FrameRate; ChgParam->enable_option |= ENC_SET_CHANGE_PARAM_RC_TARGET_RATE; ctx->param_change_flag ++; return AMVENC_SUCCESS; } AMVEnc_Status AML_MultiEncChangeQPMinMax(amv_enc_handle_t ctx_handle, int minQpI, int maxQpI, int maxDeltaQp, int minQpP, int maxQpP, int minQpB, int maxQpB) { AMVMultiCtx * ctx = (AMVMultiCtx* ) ctx_handle; EncChangeParam *ChgParam; if (ctx == NULL) return AMVENC_FAIL; if (ctx->magic_num != MULTI_ENC_MAGIC) return AMVENC_FAIL; if (ctx->mInitParams.param_change_enable == 0) return AMVENC_FAIL; VLOG(INFO, "Change MinMax QP Count %d to I:%d-%d delta %d P:%d-%d B:%d-%d\n", ctx->changedCount, minQpI, maxQpI, maxDeltaQp, minQpP, maxQpP, minQpB, maxQpB); ChgParam = &ctx->changeParam; ChgParam->minQpI = minQpI; ChgParam->maxQpI = maxQpI; ChgParam->hvsMaxDeltaQp = maxDeltaQp; ChgParam->minQpP = minQpP; ChgParam->maxQpP = maxQpP; ChgParam->minQpB = minQpB; ChgParam->maxQpB = maxQpB; ChgParam->enable_option |= ENC_SET_CHANGE_PARAM_RC_MIN_MAX_QP; ChgParam->enable_option |= ENC_SET_CHANGE_PARAM_RC_INTER_MIN_MAX_QP; ctx->param_change_flag ++; return AMVENC_SUCCESS; } AMVEnc_Status AML_MultiEncChangeIntraPeriod(amv_enc_handle_t ctx_handle, int IntraQP, int IntraPeriod) { AMVMultiCtx * ctx = (AMVMultiCtx* ) ctx_handle; EncChangeParam *ChgParam; if (ctx == NULL) return AMVENC_FAIL; if (ctx->magic_num != MULTI_ENC_MAGIC) return AMVENC_FAIL; if (ctx->mInitParams.param_change_enable == 0) return AMVENC_FAIL; VLOG(INFO, "Change IntraPeriod to %d, intraQP %d, count %d\n", IntraPeriod, IntraQP, ctx->changedCount); ChgParam = &ctx->changeParam; ChgParam->intraQP = IntraQP; ChgParam->intraPeriod = IntraPeriod; if (ctx->encOpenParam.bitstreamFormat == STD_AVC) ChgParam->avcIdrPeriod = IntraPeriod; else ChgParam->avcIdrPeriod = 0; ChgParam->enable_option |= ENC_SET_CHANGE_PARAM_INTRA_PARAM; ctx->param_change_flag ++; return AMVENC_SUCCESS; } AMVEnc_Status AML_MultiEncChangeMutiSlice(amv_enc_handle_t ctx_handle, int multi_slice_mode, int multi_slice_para) { AMVMultiCtx * ctx = (AMVMultiCtx* ) ctx_handle; EncChangeParam *ChgParam; if (ctx == NULL) return AMVENC_FAIL; if (ctx->magic_num != MULTI_ENC_MAGIC) return AMVENC_FAIL; if (ctx->mInitParams.param_change_enable == 0) return AMVENC_FAIL; VLOG(INFO, "Change Multislice to mode %d, para %d, count %d\n", multi_slice_mode, multi_slice_para, ctx->changedCount); ChgParam = &ctx->changeParam; if (ctx->encOpenParam.bitstreamFormat == STD_AVC) { if (multi_slice_mode < 0 || multi_slice_mode >1) return AMVENC_FAIL; ChgParam->avcSliceMode = multi_slice_mode; ChgParam->avcSliceArg = multi_slice_para; } else { //HEVC if (multi_slice_mode < 0 || multi_slice_mode >2) return AMVENC_FAIL; if (multi_slice_mode == 2) { ChgParam->dependSliceMode = multi_slice_mode; ChgParam->dependSliceModeArg = (multi_slice_para>>16) & 0xffff; ChgParam->independSliceMode = 1; } else { ChgParam->dependSliceMode = 0; ChgParam->dependSliceModeArg = 0; ChgParam->independSliceMode = multi_slice_mode; } ChgParam->enable_option |= ENC_SET_CHANGE_PARAM_DEPEND_SLICE; ChgParam->independSliceModeArg = multi_slice_para & 0xffff; } ChgParam->enable_option |= ENC_SET_CHANGE_PARAM_INDEPEND_SLICE; ctx->param_change_flag ++; return AMVENC_SUCCESS; } AMVEnc_Status AML_MultiEncHeader(amv_enc_handle_t ctx_handle, unsigned char* buffer, unsigned int* buf_nal_size) { EncHeaderParam encHeaderParam; EncOutputInfo encOutputInfo; QueueStatusInfo queueStatus; ENC_INT_STATUS status; PhysicalAddress paRdPtr = 0; PhysicalAddress paWrPtr = 0; int header_size = 0; int retry = 0; RetCode result= RETCODE_SUCCESS; AMVMultiCtx * ctx = (AMVMultiCtx* ) ctx_handle; ENC_QUERY_WRPTR_SEL encWrPtrSel = GET_ENC_PIC_DONE_WRPTR; if (ctx == NULL) return AMVENC_FAIL; if (ctx->magic_num != MULTI_ENC_MAGIC) return AMVENC_FAIL; //EncHandle handle = ctx->enchandle; memset(&encHeaderParam, 0x00, sizeof(EncHeaderParam)); /*Just reuse bitstream buffer or allocat a new one*/ encHeaderParam.buf = ctx->bsBuffer[0].phys_addr; encHeaderParam.size = ctx->bsBuffer[0].size; if (ctx->encOpenParam.bitstreamFormat == STD_HEVC) { encHeaderParam.headerType = CODEOPT_ENC_VPS | CODEOPT_ENC_SPS | CODEOPT_ENC_PPS; } else { // H.264 encHeaderParam.headerType = CODEOPT_ENC_SPS | CODEOPT_ENC_PPS; } #if ENCODE_TIME_STATISTICS gettimeofday(&start_test, NULL); #endif retry_point: result = VPU_EncGiveCommand(ctx->enchandle, ENC_PUT_VIDEO_HEADER, &encHeaderParam); if (result != RETCODE_SUCCESS) { retry ++; if (result == RETCODE_QUEUEING_FAILURE ) { VLOG(DEBUG, "Instance %d HEADER Queue fail\n", ctx->enchandle->instIndex); } else { VLOG(ERR, "HEADER fail instance %d reason %d\n", ctx->enchandle->instIndex, result); } if (retry > 100) { VLOG(ERR, "INSTANCE #%d HEADER retry failed\n", ctx->enchandle->instIndex); HandleEncoderError(ctx->enchandle, ctx->frameIdx, NULL); return AMVENC_TIMEOUT; } usleep(1000); goto retry_point; } while (1) { // wait interrupt */ if ((status = HandlingInterruptFlag(ctx)) == ENC_INT_STATUS_DONE) { break; } else if (status == ENC_INT_STATUS_NONE) { // no interrupt yet Uint32 intr_usr = 0; VPU_CheckInterrupt(ctx->enchandle, &intr_usr); VLOG(DEBUG, "Check interrupt status inst #%d retry %d result %d\n", ctx->enchandle->instIndex, retry, intr_usr); if (intr_usr) { VLOG(DEBUG, "Check and cleared interrupt inst #%d go ahead\n", ctx->enchandle->instIndex); break; } VLOG(DEBUG, "INSTANCE #%d INT timeout \n", ctx->enchandle->instIndex); VPU_EncGiveCommand(ctx->enchandle, ENC_GET_QUEUE_STATUS, &queueStatus); VLOG(DEBUG, "INSTANCE #%d queuedCmd %d allQueued %d \n", ctx->enchandle->instIndex, queueStatus.instanceQueueCount, queueStatus.reportQueueCount); retry++; if (retry > 20) break; } else if (status == ENC_INT_STATUS_TIMEOUT) { if (retry++ < 20) break; VLOG(ERR, "INSTANCE #%d INTERRUPT TIMEOUT\n", ctx->enchandle->instIndex); HandleEncoderError(ctx->enchandle, ctx->frameIdx, NULL); VPU_SWReset(ctx->encOpenParam.coreIdx, SW_RESET_SAFETY, ctx->enchandle); return AMVENC_TIMEOUT; } else { VLOG(ERR, "Unknown interrupt status: %d\n",status); return AMVENC_FAIL; } usleep(1000); //osal_msleep(1); } if (retry) { VLOG(DEBUG, "INSTANCE #%d Possible INT lost rety %d\n", ctx->enchandle->instIndex, retry); } DisplayEncodedInformation(ctx->enchandle, ctx->encOpenParam.bitstreamFormat, 0, NULL, 0, 0, 1); VPU_EncGiveCommand(ctx->enchandle, ENC_WRPTR_SEL, &encWrPtrSel); encOutputInfo.result = VPU_EncGetOutputInfo(ctx->enchandle, &encOutputInfo); if (encOutputInfo.result == RETCODE_REPORT_NOT_READY) { VPU_EncGiveCommand(ctx->enchandle, ENC_GET_QUEUE_STATUS, &queueStatus); VLOG(DEBUG, "HEADER encode ready queue = %d %d\n", queueStatus.instanceQueueCount, queueStatus.reportQueueCount); if (retry++ < 100) { VLOG(DEBUG, "HEADER NO command in queue, retry\n"); goto retry_point; } else { VLOG(DEBUG, "HEADER retry FAILED\n"); return AMVENC_SPS_FAIL; /* Not encoded yet */ } } else if (encOutputInfo.result == RETCODE_VLC_BUF_FULL) { VLOG(ERR, "VLC BUFFER FULL!!! ALLOCATE MORE TASK BUFFER(%d)!!!\n", ONE_TASKBUF_SIZE_FOR_CQ); } else if (encOutputInfo.result != RETCODE_SUCCESS) { /* ERROR */ VLOG(ERR, "Failed to encode error = %d\n", encOutputInfo.result); if ( retry++ >100) { VLOG(ERR, "HEADER RETRY failed reset\n"); HandleEncoderError(ctx->enchandle, encOutputInfo.encPicCnt, &encOutputInfo); VPU_SWReset(ctx->encOpenParam.coreIdx, SW_RESET_SAFETY, ctx->enchandle); return AMVENC_SPS_FAIL; } goto retry_point; } else { if (retry) { VLOG(DEBUG, "INSTANCE #%d INT lost recovered %d\n", ctx->enchandle->instIndex, retry); };/* SUCCESS */ } paRdPtr = encOutputInfo.rdPtr; paWrPtr = encOutputInfo.wrPtr; header_size = encOutputInfo.bitstreamSize; VLOG(DEBUG, "header_size %d paWrPtr-paRdPtr %d", header_size, paWrPtr-paRdPtr); if (paRdPtr != ctx->bsBuffer[0].phys_addr) { VLOG(ERR, "BS buffer no match!! expect %x acutual %x size %d\n", ctx->bsBuffer[0].phys_addr, paRdPtr, header_size); } #if ENCODE_TIME_STATISTICS gettimeofday(&end_test, NULL); encode_time_per_frame = end_test.tv_sec - start_test.tv_sec; encode_time_per_frame = encode_time_per_frame * 1000000 + end_test.tv_usec - start_test.tv_usec; total_encode_frames++; total_encode_time += encode_time_per_frame; VLOG(WARN, "%p#Encode header spent time : %lu us, frame_number : %llu", ctx_handle, encode_time_per_frame, total_encode_frames); #endif VLOG(INFO, "Enc HEADER size %d\n",header_size); *buf_nal_size = header_size; // cache invalid to read out by CPU //vdi_invalitate_memory(ctx->encOpenParam.coreIdx, &ctx->bsBuffer[0]); vdi_read_memory(ctx->encOpenParam.coreIdx, ctx->bsBuffer[0].phys_addr, buffer, header_size, ctx->encOpenParam.streamEndian); return AMVENC_SUCCESS; } AMVEnc_Status AML_MultiEncGetAvgQp(amv_enc_handle_t ctx_handle, int *avgqp) { AMVEnc_Status ret = AMVENC_FAIL; AMVMultiCtx * ctx = (AMVMultiCtx* ) ctx_handle; if (ctx == NULL) return AMVENC_FAIL; if (ctx->averageQP) { *avgqp = ctx->averageQP; ret = AMVENC_SUCCESS; } return ret; } AMVEnc_Status AML_MultiEncNAL(amv_enc_handle_t ctx_handle, unsigned char* buffer, unsigned int* buf_nal_size, AMVMultiEncFrameIO *Retframe) { RetCode result; EncParam* encParam; EncVpParam* VpParam; EncOutputInfo encOutputInfo; ENC_INT_STATUS intStatus; ENC_QUERY_WRPTR_SEL encWrPtrSel = GET_ENC_PIC_DONE_WRPTR; int retry_cnt = 0; int idx; QueueStatusInfo qStatus; AMVMultiCtx * ctx = (AMVMultiCtx* ) ctx_handle; if (ctx == NULL) return AMVENC_FAIL; if (ctx->magic_num != MULTI_ENC_MAGIC) return AMVENC_FAIL; ctx->mNumInputFrames++; VpParam = &ctx->encOpenParam.EncStdParam.vpParam; #if ENCODE_TIME_STATISTICS gettimeofday(&start_test, NULL); #endif retry_point: if (VpParam->QpModify) { ctx->param_change_flag = 1; ctx->changeParam.enable_option |= ENC_SET_CHANGE_PARAM_RC_MIN_MAX_QP; ctx->changeParam.enable_option |= ENC_SET_CHANGE_PARAM_RC_INTER_MIN_MAX_QP; ctx->changeParam.intraPeriod = VpParam->intraPeriod; ctx->changeParam.pic_type = ctx->pic_type; if ((ctx->mNumInputFrames % ctx->changeParam.intraPeriod == 1) || ctx->changeParam.pic_type == 3) { ctx->changeParam.maxQpI = VpParam->maxQpI; ctx->changeParam.maxQpP = VpParam->maxQpP; ctx->changeParam.hvsMaxDeltaQp = VpParam->hvsMaxDeltaQp; } else { ctx->changeParam.maxQpI = VpParam->maxQpP; ctx->changeParam.maxQpP = VpParam->maxQpP; ctx->changeParam.hvsMaxDeltaQp = VpParam->hvsMaxDeltaQp; } } if(ctx ->param_change_flag) { result = VPU_EncGiveCommand(ctx->enchandle, ENC_SET_PARA_CHANGE, &ctx->changeParam); if (result == RETCODE_SUCCESS) { VLOG(INFO, "ENC_SET_PARA_CHANGE queue success option 0x%x\n", ctx->changeParam.enable_option); if (VPU_EncInstParamSync(ctx->enchandle,0,0,&ctx->changeParam) != RETCODE_SUCCESS) { VLOG(ERR, "VPU instance param sync with change param failed!\n"); } osal_memset(&ctx->changeParam, 0x00, sizeof(EncChangeParam)); ctx->param_change_flag = 0; ctx->changedCount ++; } else if (result == RETCODE_QUEUEING_FAILURE) { // Just retry VLOG(INFO, "ENC_SET_PARA_CHANGE Queue Full\n"); goto retry_point; } else { // Error VLOG(ERR, "ENC_SET_PARA_CHANGE failed Error code is 0x%x \n", result); ChekcAndPrintDebugInfo(ctx->enchandle, TRUE, result); return AMVENC_FAIL; } } encParam = &ctx->encParam; result = VPU_EncStartOneFrame(ctx->enchandle, encParam); if (result == RETCODE_SUCCESS) { ctx->frameIdx++; encParam -> srcIdx++; if(encParam -> srcIdx >= ctx->src_num) encParam -> srcIdx = 0; VLOG(INFO, "VPU_EncStartOneFrame sucesss frameIdx %d srcIdx %d \n", ctx->frameIdx, encParam -> srcIdx); } else if (result == RETCODE_QUEUEING_FAILURE) { // Just retry VLOG(ERR, "VPU_EncStartOneFrame retry code is 0x%x \n", result); // Just retry VPU_EncGiveCommand(ctx->enchandle, ENC_GET_QUEUE_STATUS, (void*)&qStatus); if (qStatus.instanceQueueCount == 0) { VLOG(ERR, "<%s:%d> The queue is empty but it can't add a command\n", __FUNCTION__, __LINE__); goto retry_point;//return TRUE; } } else { // Error VLOG(ERR, "VPU_EncStartOneFrame failed Error code is 0x%x \n", result); ChekcAndPrintDebugInfo(ctx->enchandle, TRUE, result); idx = ctx->encMEMSrcFrmIdxArr[encParam->srcIdx]; ctx->encodedSrcFrmIdxArr[idx] = 0; //free slot as failed to start return AMVENC_FAIL; } retry_pointB: while (retry_cnt++ < 100) { if ((intStatus=HandlingInterruptFlag(ctx)) == ENC_INT_STATUS_TIMEOUT) { VPU_SWReset(ctx->encOpenParam.coreIdx, SW_RESET_SAFETY, ctx->enchandle); VLOG(ERR, "Timeout of encoder interrupt reset \n"); return AMVENC_FAIL; } else if (intStatus == ENC_INT_STATUS_FULL || intStatus == ENC_INT_STATUS_LOW_LATENCY) { PhysicalAddress paRdPtr; PhysicalAddress paWrPtr; int size; encWrPtrSel = (intStatus==ENC_INT_STATUS_FULL) ? GET_ENC_BSBUF_FULL_WRPTR : GET_ENC_LOW_LATENCY_WRPTR; VPU_EncGiveCommand(ctx->enchandle, ENC_WRPTR_SEL, &encWrPtrSel); VPU_EncGetBitstreamBuffer(ctx->enchandle, &paRdPtr, &paWrPtr, &size); VLOG(TRACE, "INT_BSBUF_FULL %d, %d\n", paRdPtr, paWrPtr); ctx->fullInterrupt = TRUE; return AMVENC_FAIL; //return TRUE; } else if (intStatus == ENC_INT_STATUS_NONE) { Uint32 intr_usr = 0; VPU_CheckInterrupt(ctx->enchandle, &intr_usr); VLOG(DEBUG, "Check interrupt status inst #%d retry %d result %d\n", ctx->enchandle->instIndex, retry_cnt, intr_usr); if (intr_usr) { VLOG(DEBUG, "Check and cleared interrupt inst #%d go ahead\n", ctx->enchandle->instIndex); break; } usleep(1000); //osal_msleep(1); } else if (intStatus == ENC_INT_STATUS_DONE) break; //success get done intr } // get the interrupt // get result andc output */ VPU_EncGiveCommand(ctx->enchandle, ENC_WRPTR_SEL, &encWrPtrSel); osal_memset(&encOutputInfo, 0x00, sizeof(EncOutputInfo)); encOutputInfo.result = VPU_EncGetOutputInfo(ctx->enchandle, &encOutputInfo); if (encOutputInfo.result == RETCODE_REPORT_NOT_READY) { VLOG(ERR, "no encode yet !!!\n"); return AMVENC_FAIL; //return TRUE; /* Not encoded yet */ } else if (encOutputInfo.result == RETCODE_VLC_BUF_FULL) { VLOG(ERR, "VLC BUFFER FULL!!! ALLOCATE MORE TASK BUFFER(%d)!!!\n", ONE_TASKBUF_SIZE_FOR_CQ); } else if (encOutputInfo.result != RETCODE_SUCCESS) { /* ERROR */ VLOG(ERR, "Failed to encode error = %d 0x%x\n", encOutputInfo.result, encOutputInfo.errorReason); ChekcAndPrintDebugInfo(ctx->enchandle, TRUE, encOutputInfo.result); HandleEncoderError(ctx->enchandle, encOutputInfo.encPicCnt, &encOutputInfo); VPU_SWReset(ctx->encOpenParam.coreIdx, SW_RESET_SAFETY, ctx->enchandle); return AMVENC_FAIL; } else { ;/* SUCCESS */ VLOG(INFO, "Success one = %d\n", encOutputInfo.result); } ctx->averageQP = encOutputInfo.avgCtuQp; if (encOutputInfo.reconFrameIndex == RECON_IDX_FLAG_CHANGE_PARAM) { VLOG(INFO, "CHANGE PARAMETER!\n"); retry_cnt = 0; goto retry_pointB; //return TRUE; /* Try again */ } else if (encOutputInfo.reconFrameIndex == RECON_IDX_FLAG_HEADER_ONLY) { VLOG(INFO, "Only have encoded headers !\n"); retry_cnt = 0; goto retry_pointB; //return TRUE; /* Try again */ } else { // out put frame info DisplayEncodedInformation(ctx->enchandle, ctx->encOpenParam.bitstreamFormat, ctx->frameIdx, &encOutputInfo, encParam->srcEndFlag, encParam->srcIdx, 1); } if ( encOutputInfo.result != RETCODE_SUCCESS ) { VLOG(ERR, "output info result error \n"); return AMVENC_FAIL; } VLOG(INFO, "encOutputInfo.encSrcIdx %d reconFrameIndex %d \n", encOutputInfo.encSrcIdx, encOutputInfo.reconFrameIndex); if (encOutputInfo.reconFrameIndex == RECON_IDX_FLAG_CHANGE_PARAM) { VLOG(ERR, "CHANGE PARAMETER!\n"); // return TRUE; /* Try again */ } if (encOutputInfo.reconFrameIndex == RECON_IDX_FLAG_HEADER_ONLY) { VLOG(ERR, "ENCODED HEADER ONLY!\n"); } #if 0 //release flag for (i = 0; i < ctx->fbCount.srcFbNum; i++) { if ( (encOutputInfo.releaseSrcFlag >> i) & 0x01) { ctx->encodedSrcFrmIdxArr[i] = 1; } } #endif if ( encOutputInfo.encSrcIdx >= 0) { idx = ctx->encMEMSrcFrmIdxArr[encOutputInfo.encSrcIdx]; ctx->encodedSrcFrmIdxArr[idx] = 0; ctx->fullInterrupt = FALSE; // copy frames VLOG(INFO, "Enc bitstream size %d pic_type %d avgQP %d\n", encOutputInfo.bitstreamSize, encOutputInfo.picType, encOutputInfo.avgCtuQp); *buf_nal_size = encOutputInfo.bitstreamSize; vdi_read_memory(ctx->encOpenParam.coreIdx, encOutputInfo.bitstreamBuffer, buffer, encOutputInfo.bitstreamSize, ctx->encOpenParam.streamEndian); *Retframe= ctx->FrameIO[idx]; Retframe->encoded_frame_type = encOutputInfo.picType; Retframe->enc_average_qp = encOutputInfo.avgCtuQp; Retframe->enc_intra_blocks = encOutputInfo.numOfIntra; Retframe->enc_merged_blocks = encOutputInfo.numOfMerge; Retframe->enc_skipped_blocks = encOutputInfo.numOfSkipBlock; } else if ( encOutputInfo.encSrcIdx == 0xfffffffe) { VLOG(INFO, "delay frame delay %d \n", ctx->frame_delay); ctx->frame_delay ++; *buf_nal_size = 0; Retframe ->YCbCr[0] = 0; } else if (encOutputInfo.encSrcIdx == 0xfffffffb) { VLOG(INFO, "non-reference picture !! \n"); *buf_nal_size = 0; Retframe->YCbCr[0] = 0; } #if ENCODE_TIME_STATISTICS gettimeofday(&end_test, NULL); encode_time_per_frame = end_test.tv_sec - start_test.tv_sec; encode_time_per_frame = encode_time_per_frame * 1000000 + end_test.tv_usec - start_test.tv_usec; total_encode_frames++; total_encode_time += encode_time_per_frame; VLOG(WARN, "%p#Encode slice time : %lu us, frame index : %llu", ctx_handle, encode_time_per_frame, total_encode_frames); #endif VLOG(INFO, "Done one picture !! \n"); return AMVENC_PICTURE_READY; } AMVEnc_Status AML_MultiEncRelease(amv_enc_handle_t ctx_handle) { AMVEnc_Status ret = AMVENC_SUCCESS; RetCode result; EncParam* encParam; AMVMultiCtx * ctx = (AMVMultiCtx* ) ctx_handle; ENC_INT_STATUS intStatus; Uint32 coreIdx; int idx; if (ctx == NULL) return AMVENC_FAIL; if (ctx->magic_num != MULTI_ENC_MAGIC) return AMVENC_FAIL; #if ENCODE_TIME_STATISTICS VLOG(WARN, "%p#Total encode time : %llu us, Total encode frames : %llu", ctx_handle, total_encode_time, total_encode_frames); #endif coreIdx = ctx->encOpenParam.coreIdx; if (ctx->frameIdx ) { flush_retry_point: encParam = &ctx->encParam; encParam->srcEndFlag = 1; // flush out frame result = VPU_EncStartOneFrame(ctx->enchandle, encParam); if (result == RETCODE_SUCCESS) { // Queue_Dequeue(ctx->encOutQ); ctx->frameIdx++; } else if (result == RETCODE_QUEUEING_FAILURE) { // Just retry QueueStatusInfo qStatus; // Just retry VPU_EncGiveCommand(ctx->enchandle, ENC_GET_QUEUE_STATUS, (void*)&qStatus); if (qStatus.instanceQueueCount == 0) { goto flush_retry_point;//return TRUE; } } else { // Error VLOG(ERR, "VPU_EncStartOneFrame failed Error code is 0x%x \n", result); //return AMVENC_FAIL; } } while (VPU_EncClose(ctx->enchandle) == RETCODE_VPU_STILL_RUNNING) { if ((intStatus = HandlingInterruptFlag(ctx)) == ENC_INT_STATUS_TIMEOUT) { HandleEncoderError(ctx->enchandle, ctx->frameIdx, NULL); VLOG(ERR, "NO RESPONSE FROM VPU_EncClose2()\n"); ret = AMVENC_TIMEOUT; break; } else if (intStatus == ENC_INT_STATUS_DONE) { EncOutputInfo outputInfo; VLOG(INFO, "VPU_EncClose() : CLEAR REMAIN INTERRUPT\n"); VPU_EncGetOutputInfo(ctx->enchandle, &outputInfo); continue; } osal_msleep(10); } if (ctx->vbCustomLambda.size) vdi_free_dma_memory(coreIdx, &ctx->vbCustomLambda, ENC_ETC, ctx->enchandle->instIndex); if (ctx->vbScalingList.size) vdi_free_dma_memory(coreIdx, &ctx->vbScalingList, ENC_ETC, ctx->enchandle->instIndex); for (idx = 0; idx < MAX_REG_FRAME; idx++) { if (ctx->pFbReconMem[idx].size) vdi_free_dma_memory(coreIdx, &ctx->pFbReconMem[idx], ENC_FBC, ctx->enchandle->instIndex); if (ctx->vbCustomMap[idx].size) vdi_free_dma_memory(coreIdx, &ctx->vbCustomMap[idx], ENC_ETC, ctx->enchandle->instIndex); } for (idx = 0; idx < ENC_SRC_BUF_NUM; idx++) { if (ctx->pFbSrcMem[idx].size) vdi_free_dma_memory(coreIdx, &ctx->pFbSrcMem[idx], ENC_SRC, ctx->enchandle->instIndex); if (ctx->bsBuffer[idx].size) vdi_free_dma_memory(coreIdx, &ctx->bsBuffer[idx], ENC_BS, 0); } if (ctx->CustomRoiMapBuf) { free(ctx->CustomRoiMapBuf); ctx->CustomRoiMapBuf = NULL; } if (ctx->CustomLambdaMapBuf) { free(ctx->CustomLambdaMapBuf); ctx->CustomLambdaMapBuf = NULL; } if (ctx->CustomModeMapBuf) { free(ctx->CustomModeMapBuf); ctx->CustomModeMapBuf = NULL; } #if SUPPORT_SCALE if (ctx->ge2d_initial_done) { aml_ge2d_mem_free(&ctx->amlge2d); aml_ge2d_exit(&ctx->amlge2d); VLOG(DEBUG, "ge2d exit!!!\n"); } #endif VPU_DeInit(ctx->encOpenParam.coreIdx); free(ctx); VLOG(INFO, "AML_HEVCRelease succeed\n"); return ret; }