//#define LOG_NDEBUG 0 #define LOG_TAG "AMLVENC" #ifdef MAKEANDROID #include #endif #include #include #include #include #include #include #include #include "AML_HWEncoder.h" #include "enc_api.h" AMVEnc_Status DetermineFrameNum(AMVEncHandle *Handle, amvenc_info_t* info, uint32 modTime, uint32 new_frame_rate, bool force_IDR) { uint32 modTimeRef = info->modTimeRef; int32 currFrameNum; int frameInc; int border = 200; bool no_skip = false; /* for now, the default is to encode every frame, To Be Changed */ if (info->first_frame) { info->modTimeRef = modTime; info->wrapModTime = 0; info->prevProcFrameNum = 0; info->prevProcFrameNumOffset = 0; info->lastTimeRef = 0; /* set frame type to IDR-frame */ info->nal_unit_type = AVC_NALTYPE_IDR; info->slice_type = AVC_I_SLICE; info->late_frame_count = 0; info->frame_rate = new_frame_rate; return AMVENC_SUCCESS; } else { if ((info->freerun == true) || (force_IDR == true)) no_skip = true; if (info->frame_rate != new_frame_rate) { info->prevProcFrameNumOffset += info->prevProcFrameNum; info->late_frame_count = 0; info->prevProcFrameNum = 0; info->modTimeRef = info->lastTimeRef; modTimeRef = info->modTimeRef; info->wrapModTime = 0; info->frame_rate = new_frame_rate; no_skip = true; LOGAPI("update frame_rate %d\n",new_frame_rate); } //LOGAPI("modTime %lld, modTimeRef %lld\n",modTime,modTimeRef); if (modTime < modTimeRef) { /* modTime wrapped around */ info->wrapModTime += ((uint32) 0xFFFFFFFF - modTimeRef) + 1; info->modTimeRef = modTimeRef = 0; LOGAPI("update info->wrapModTime %u\n",info->wrapModTime); } modTime += info->wrapModTime; /* wrapModTime is non zero after wrap-around */ currFrameNum = (int32) (((modTime - modTimeRef) * (float) info->frame_rate + border) / 1000); /* add small roundings */ //LOGAPI("modTime %lld, currFrameNum %lld, prevProcFrameNum %lld, late_frame_count %d\n",modTime,currFrameNum,info->prevProcFrameNum,info->late_frame_count); if ((currFrameNum <= (int32) (info->prevProcFrameNum - info->late_frame_count)) && (no_skip == false)) { return AMVENC_FAIL; /* this is a late frame do not encode it */ } frameInc = currFrameNum - info->prevProcFrameNum + info->late_frame_count; if ((currFrameNum - info->prevProcFrameNum) > 1) info->late_frame_count += (int) (currFrameNum - info->prevProcFrameNum - 1); else if ((info->late_frame_count > 0) && ((currFrameNum - info->prevProcFrameNum) == 0)) info->late_frame_count--; //LOGAPI("frameInc %d, skip_next_frame %d, late_frame_count %d\n",frameInc,info->skip_next_frame,info->late_frame_count); if ((frameInc < info->skip_next_frame + 1) && (no_skip == false)) { LOGAPI("skip frameInc %d, skip_next_frame %d\n",frameInc, info->skip_next_frame); return AMVENC_FAIL; /* frame skip required to maintain the target bit rate. */ } //AMPreRateControl(&info->hw_info, frameInc, &IDR); AMPreRateControl(&info->hw_info, modTime, force_IDR); //frameInc is useless in m8 ratectrl,so use it to pass timecode; currFrameNum += info->prevProcFrameNumOffset; info->lastTimeRef = modTime; //LOGAPI("currFrameNum %lld, lastTimeRef %lld\n",currFrameNum,info->lastTimeRef); if (currFrameNum >= (int32) info->frame_rate) { /* first frame or IDR*/ //info->modTimeRef += (uint32)(info->idrPeriod * 1000 / info->frame_rate); info->modTimeRef = modTime; //add this to avoid next modTime too small currFrameNum -= info->frame_rate; info->prevProcFrameNum = currFrameNum; info->prevProcFrameNumOffset = 0; } else { info->prevProcFrameNum = currFrameNum; } //LOGAPI("currFrameNum %lld, modTimeRef %lld\n",currFrameNum,info->modTimeRef); if (info->frame_in_gop >= (uint) info->idrPeriod && info->idrPeriod > 0) { /* first frame or IDR*/ info->nal_unit_type = AVC_NALTYPE_IDR; info->slice_type = AVC_I_SLICE; } else { if (force_IDR) { // todo: force_IDR only need one IDR or reset gop. LOGAPI("force next frame to idr :%d, handle: %p.", force_IDR, Handle); info->nal_unit_type = AVC_NALTYPE_IDR; info->slice_type = AVC_I_SLICE; } else { info->nal_unit_type = AVC_NALTYPE_SLICE; info->slice_type = AVC_P_SLICE; } } } LOGAPI("Get Nal Type: %s, handle: %p.", (info->nal_unit_type == AVC_NALTYPE_IDR) ? "IDR" : "SLICE", Handle); return AMVENC_SUCCESS; } AMVEnc_Status AML_HWEncInitialize(AMVEncHandle *Handle, AMVEncParams *encParam, bool* has_mix, int force_mode) { AMVEnc_Status status = AMVENC_FAIL; amvenc_info_t* info = (amvenc_info_t*) calloc(1, sizeof(amvenc_info_t)); if ((!info) || (!Handle) || (!encParam)) { status = AMVENC_MEMORY_FAIL; goto exit; } if (encParam->frame_rate == 0) { status = AMVENC_INVALID_FRAMERATE; goto exit; } memset(info, 0, sizeof(amvenc_info_t)); info->hw_info.dev_fd = -1; info->hw_info.dev_id = NO_DEFINE; info->enc_width = encParam->width; info->enc_height = encParam->height; info->outOfBandParamSet = encParam->out_of_band_param_set; info->fullsearch_enable = encParam->fullsearch; /* now the rate control and performance related parameters */ info->scdEnable = encParam->auto_scd; info->idrPeriod = encParam->idr_period; info->search_range = encParam->search_range; info->frame_rate = (float) (encParam->frame_rate * 1.0 / 1000); info->rcEnable = (encParam->rate_control == AVC_ON) ? true : false; info->bitrate = encParam->bitrate; info->cpbSize = encParam->CPB_size; info->initDelayOffset = (info->bitrate * encParam->init_CBP_removal_delay / 1000); info->initQP = encParam->initQP; //add for qp limit info->i_qp_min = encParam->i_qp_min; info->i_qp_max = encParam->i_qp_max; info->p_qp_min = encParam->p_qp_min; info->p_qp_max = encParam->p_qp_max; info->freerun = (encParam->FreeRun == AVC_ON) ? true : false; info->nSliceHeaderSpacing = encParam->nSliceHeaderSpacing; info->MBsIntraRefresh = encParam->MBsIntraRefresh; info->MBsIntraOverlap = encParam->MBsIntraOverlap; if (info->initQP == 0) { #if 0 double L1, L2, L3, bpp; bpp = 1.0 * info->bitrate /(info->frame_rate * (info->enc_width *info->enc_height)); if (info->enc_width <= 176) { L1 = 0.1; L2 = 0.3; L3 = 0.6; } else if (info->enc_width <= 352) { L1 = 0.2; L2 = 0.6; L3 = 1.2; } else { L1 = 0.6; L2 = 1.4; L3 = 2.4; } if (bpp <= L1) info->initQP = 30; else if (bpp <= L2) info->initQP = 25; else if (bpp <= L3) info->initQP = 20; else info->initQP = 15; #else info->initQP = 26; #endif } info->hw_info.init_para.enc_width = info->enc_width; info->hw_info.init_para.enc_height = info->enc_height; info->hw_info.init_para.initQP = info->initQP; //add for qp limit info->hw_info.init_para.i_qp_min = info->i_qp_min; info->hw_info.init_para.i_qp_max = info->i_qp_max; info->hw_info.init_para.p_qp_min = info->p_qp_min; info->hw_info.init_para.p_qp_max = info->p_qp_max; info->hw_info.init_para.nSliceHeaderSpacing = info->nSliceHeaderSpacing; info->hw_info.init_para.MBsIntraRefresh = info->MBsIntraRefresh; info->hw_info.init_para.MBsIntraOverlap = info->MBsIntraOverlap; info->hw_info.init_para.rcEnable = info->rcEnable; info->hw_info.init_para.bitrate = info->bitrate; info->hw_info.init_para.frame_rate = info->frame_rate; info->hw_info.init_para.cpbSize = info->cpbSize; info->hw_info.init_para.bitrate_scale = (encParam->BitrateScale == AVC_ON) ? true : false; info->hw_info.init_para.encode_once = encParam->encode_once; status = InitAMVEncode(&info->hw_info, force_mode); if (status != AMVENC_SUCCESS) { goto exit; } encParam->dev_id = info->hw_info.dev_id; if (AMInitRateControlModule(&info->hw_info) != AMVENC_SUCCESS) { status = AMVENC_MEMORY_FAIL; goto exit; } info->first_frame = true; /* set this flag for the first time */ info->modTimeRef = 0; /* ALWAYS ASSUME THAT TIMESTAMP START FROM 0 !!!*/ info->late_frame_count = 0; if (info->outOfBandParamSet) info->state = AMVEnc_Encoding_SPS; else info->state = AMVEnc_Analyzing_Frame; Handle->object = (void *) info; *has_mix = (info->hw_info.dev_id == M8) ? true : false; LOGAPI("AML_HWEncInitialize success, handle: %p, fd:%d", Handle, info->hw_info.dev_fd); return AMVENC_SUCCESS; exit: if (info) { AMCleanupRateControlModule(&info->hw_info); UnInitAMVEncode(&info->hw_info); free(info); } LOGAPI("AML_HWEncInitialize Fail, error=%d. handle: %p", status, Handle); return status; } AMVEnc_Status AML_HWSetInput(AMVEncHandle *Handle, AMVEncFrameIO *input) { amvenc_info_t* info = (amvenc_info_t*) Handle->object; AMVEnc_Status status = AMVENC_FAIL; ulong yuv[17]; if (info == NULL) { LOGAPI("AML_HWSetInput Fail: UNINITIALIZED. handle: %p", Handle); return AMVENC_UNINITIALIZED; } if (info->state == AMVEnc_WaitingForBuffer) { goto RECALL_INITFRAME; } else if (info->state != AMVEnc_Analyzing_Frame) { LOGAPI("AML_HWSetInput Wrong state: %d. handle: %p", info->state, Handle); return AMVENC_FAIL; } if (input->pitch > 0xFFFF) { LOGAPI("AML_HWSetInput Fail: NOT_SUPPORTED. handle: %p", Handle); return AMVENC_NOT_SUPPORTED; // we use 2-bytes for pitch } if (AMVENC_SUCCESS != DetermineFrameNum(Handle, info, input->coding_timestamp, (uint32) input->frame_rate, (input->op_flag & AMVEncFrameIO_FORCE_IDR_FLAG))) { LOGAPI("AML_HWSetInput SKIPPED_PICTURE, handle: %p", Handle); return AMVENC_SKIPPED_PICTURE; /* not time to encode, thus skipping */ } yuv[0] = input->YCbCr[0]; yuv[1] = input->YCbCr[1]; yuv[2] = input->YCbCr[2]; yuv[3] = input->canvas; yuv[4] = (input->op_flag & AMVEncFrameIO_FORCE_SKIP_FLAG) ? 1 : 0; yuv[5] = input->crop_top; yuv[6] = input->crop_bottom; yuv[7] = input->crop_left; yuv[8] = input->crop_right; yuv[9] = input->pitch; yuv[10] = input->height; yuv[11] = input->scale_width; yuv[12] = input->scale_height; yuv[13] = input->num_planes; yuv[14] = input->shared_fd[0]; yuv[15] = input->shared_fd[1]; yuv[16] = input->shared_fd[2]; RECALL_INITFRAME: /* initialize and analyze the frame */ status = AMVEncodeInitFrame(&info->hw_info, &yuv[0], input->type, input->fmt, (info->nal_unit_type == AVC_NALTYPE_IDR)); if ((status == AMVENC_NEW_IDR) && (info->nal_unit_type != AVC_NALTYPE_IDR)) { info->modTimeRef = input->coding_timestamp; info->nal_unit_type = AVC_NALTYPE_IDR; info->slice_type = AVC_I_SLICE; info->prevProcFrameNum = 0; } if (info->nal_unit_type == AVC_NALTYPE_IDR) info->frame_in_gop = 0; AMRCInitFrameQP(&info->hw_info, (info->nal_unit_type == AVC_NALTYPE_IDR), input->bitrate, input->frame_rate); if (status == AMVENC_SUCCESS) { info->state = AMVEnc_Encoding_Frame; } else if (status == AMVENC_NEW_IDR) { if (info->outOfBandParamSet) info->state = AMVEnc_Encoding_Frame; else // assuming that in-band paramset keeps sending new SPS and PPS. info->state = AMVEnc_Encoding_SPS; } else if (status == AMVENC_PICTURE_READY) { info->state = AMVEnc_WaitingForBuffer; // Input accepted but can't continue } LOGAPI("AML_HWSetInput status: %d. handle: %p", status, Handle); return status; } AMVEnc_Status AML_HWEncNAL(AMVEncHandle *Handle, unsigned char *buffer, unsigned int *buf_nal_size, int *nal_type) { AMVEnc_Status ret = AMVENC_FAIL; amvenc_info_t* info = (amvenc_info_t*) Handle->object; int datalen = 0; if (info == NULL) { LOGAPI("AML_HWEncNAL Fail: UNINITIALIZED. handle: %p", Handle); return AMVENC_UNINITIALIZED; } LOGAPI("AML_HWEncNAL state: %d. handle: %p", info->state, Handle); switch (info->state) { case AMVEnc_Initializing: return AMVENC_ALREADY_INITIALIZED; case AMVEnc_Encoding_SPS: /* encode SPS */ ret = AMVEncodeSPS_PPS(&info->hw_info, buffer, &datalen); if (ret != AMVENC_SUCCESS) { LOGAPI("AML_HWEncNAL state: %d, err=%d, handle: %p", info->state, ret, Handle); return ret; } if (info->outOfBandParamSet) info->state = AMVEnc_Analyzing_Frame; else // SetInput has been called before SPS and PPS. info->state = AMVEnc_Encoding_Frame; *nal_type = AVC_NALTYPE_PPS; *buf_nal_size = datalen; break; case AMVEnc_Encoding_Frame: if (*nal_type == 0xff) { LOGAPI("AML_HWEncNAL Cancel Current Frame. handle: %p", Handle); info->state = AMVEnc_Analyzing_Frame; ret = AMVENC_SUCCESS; return ret; } ret = AMVEncodeSlice(&info->hw_info, buffer, &datalen); if (ret == AMVENC_TIMEOUT) { LOGAPI("AML_HWEncNAL state: %d, err=%d. handle: %p", info->state, ret, Handle); //ret = AMVENC_SKIPPED_PICTURE; //need debug info->state = AMVEnc_Analyzing_Frame; // walk around to fix the poll timeout return ret; } if ((ret != AMVENC_PICTURE_READY) && (ret != AMVENC_SUCCESS) && (ret != AMVENC_NEW_IDR)) { LOGAPI("AML_HWEncNAL state: %d, err=%d. handle: %p", info->state, ret, Handle); return ret; } info->nal_unit_type = (ret == AMVENC_NEW_IDR) ? AVC_NALTYPE_IDR : info->nal_unit_type; *nal_type = info->nal_unit_type; *buf_nal_size = datalen; if ((ret == AMVENC_PICTURE_READY) || (ret == AMVENC_NEW_IDR)) { info->first_frame = false; ret = AMPostRateControl(&info->hw_info, (info->nal_unit_type == AVC_NALTYPE_IDR), &info->skip_next_frame, (datalen << 3)); if ((ret == AMVENC_SKIPPED_PICTURE) && (info->freerun == false)) { info->state = AMVEnc_Analyzing_Frame; return ret; } /*we need to reencode the frame**/ if (ret == AMVENC_REENCODE_PICTURE) { ret = AMVEncodeSlice(&info->hw_info, buffer, &datalen); if ((ret != AMVENC_PICTURE_READY) && (ret != AMVENC_SUCCESS) && (ret != AMVENC_NEW_IDR)) { LOGAPI("AML_HWEncNAL state: %d, err=%d. handle: %p", info->state, ret, Handle); return ret; } info->nal_unit_type = (ret == AMVENC_NEW_IDR) ? AVC_NALTYPE_IDR : info->nal_unit_type; *nal_type = info->nal_unit_type; *buf_nal_size = datalen; if (ret == AMVENC_NEW_IDR || ret == AMVENC_PICTURE_READY) { info->first_frame = false; ret = AMPostRateControl(&info->hw_info, (info->nal_unit_type == AVC_NALTYPE_IDR)/*true*/, &info->skip_next_frame, (datalen << 3)); if ((ret == AMVENC_SKIPPED_PICTURE) && (info->freerun == false)) { info->state = AMVEnc_Analyzing_Frame; return ret; } } else { LOGAPI("re-encode failed:%d. handle: %p", ret, Handle); } } if (AMVEncodeCommit(&info->hw_info, (info->nal_unit_type == AVC_NALTYPE_IDR)) != AMVENC_SUCCESS) { LOGAPI("Encode Commit failed:%d. handle: %p", ret, Handle); } else { info->frame_in_gop++; } info->state = AMVEnc_Analyzing_Frame; ret = AMVENC_PICTURE_READY; } else { ret = AMVENC_SUCCESS; } break; default: ret = AMVENC_WRONG_STATE; } LOGAPI("AML_HWEncNAL next state: %d, ret=%d. handle: %p", info->state, ret, Handle); return ret; } AMVEnc_Status AML_HWEncRelease(AMVEncHandle *Handle) { amvenc_info_t* info = (amvenc_info_t*) Handle->object; if (info) { AMCleanupRateControlModule(&info->hw_info); UnInitAMVEncode(&info->hw_info); free(info); } Handle->object = NULL; LOGAPI("AML_HWEncRelease Done, handle: %p", Handle); return AMVENC_SUCCESS; }