Files

431 lines
17 KiB
C++
Raw Permalink Normal View History

2026-08-13 11:28:40 +08:00
//#define LOG_NDEBUG 0
#define LOG_TAG "AMLVENC"
#ifdef MAKEANDROID
#include <utils/Log.h>
#endif
#include <stdio.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#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;
}