#define LOG_TAG "libvpcodec" #include "vpcodec_1_0.h" #include #include #include #include #include #include #include #include "include/AML_HWEncoder.h" #include "include/enc_define.h" #ifndef __ANDROID__ #include #endif #define LOG_LINE() printf("[%s:%d]\n", __FUNCTION__, __LINE__) const char version[] = "Amlogic libvpcodec version 1.0"; const char *vl_get_version() { return version; } int initEncParams(AMVEncHandle *handle, int width, int height, int frame_rate, int bit_rate, int gop, int i_qp_min, int i_qp_max, int p_qp_min, int p_qp_max) { memset(&(handle->mEncParams), 0, sizeof(AMVEncParams)); LOGAPI("bit_rate:%d", bit_rate); if ((width % 16 != 0 || height % 2 != 0)) { LOGAPI("Video frame size %dx%d must be a multiple of 16", width, height); return -1; } else if (height % 16 != 0) { LOGAPI("Video frame height is not standard:%d", height); } else { LOGAPI("Video frame size is %d x %d", width, height); } handle->mEncParams.rate_control = AVC_ON; handle->mEncParams.initQP = 0; handle->mEncParams.init_CBP_removal_delay = 1600; handle->mEncParams.auto_scd = AVC_ON; handle->mEncParams.out_of_band_param_set = AVC_ON; handle->mEncParams.num_ref_frame = 1; handle->mEncParams.num_slice_group = 1; handle->mEncParams.nSliceHeaderSpacing = 0; handle->mEncParams.fullsearch = AVC_OFF; handle->mEncParams.search_range = 16; //handle->mEncParams.sub_pel = AVC_OFF; //handle->mEncParams.submb_pred = AVC_OFF; handle->mEncParams.width = width; handle->mEncParams.height = height; handle->mEncParams.bitrate = bit_rate; handle->mEncParams.frame_rate = 1000 * frame_rate; // In frames/ms! handle->mEncParams.CPB_size = (uint32)(bit_rate >> 1); handle->mEncParams.FreeRun = AVC_OFF; handle->mEncParams.MBsIntraRefresh = 0; handle->mEncParams.MBsIntraOverlap = 0; handle->mEncParams.encode_once = 1; // Set IDR frame refresh interval /*if ((unsigned) gop == 0xffffffff) { handle->mEncParams.idr_period = -1;//(mIDRFrameRefreshIntervalInSec * mVideoFrameRate); } else if (gop == 0) { handle->mEncParams.idr_period = 0; // All I frames } else { handle->mEncParams.idr_period = gop + 1; }*/ if (gop == 0 || gop < 0) { handle->mEncParams.idr_period = 0; //an infinite period, only one I frame } else { handle->mEncParams.idr_period = gop; //period of I frame, 1 means all frames are I type. } // Set profile and level handle->mEncParams.profile = AVC_BASELINE; handle->mEncParams.level = AVC_LEVEL4; handle->mEncParams.initQP = 30; handle->mEncParams.BitrateScale = AVC_OFF; //add for qp limit handle->mEncParams.i_qp_min = i_qp_min; handle->mEncParams.i_qp_max = i_qp_max; handle->mEncParams.p_qp_min = p_qp_min; handle->mEncParams.p_qp_max = p_qp_max; return 0; } /* modify for const qp*/ int initEncParamsFixQp(AMVEncHandle *handle, int width, int height, int frame_rate, int bit_rate, int gop, int fix_qp) { memset(&(handle->mEncParams), 0, sizeof(AMVEncParams)); LOGAPI("bit_rate:%d", bit_rate); if ((width % 16 != 0 || height % 2 != 0)) { LOGAPI("Video frame size %dx%d must be a multiple of 16", width, height); return -1; } else if (height % 16 != 0) { LOGAPI("Video frame height is not standard:%d", height); } else { LOGAPI("Video frame size is %d x %d", width, height); } /* modify for const qp*/ if (fix_qp >= 0) { handle->mEncParams.rate_control = AVC_OFF; handle->mEncParams.initQP = fix_qp; printf("handle->mEncParams.rate_control, handle->mEncParams.initQP %d,%d\n",handle->mEncParams.rate_control, handle->mEncParams.initQP); } else { handle->mEncParams.rate_control = AVC_ON; handle->mEncParams.initQP = 30; } handle->mEncParams.init_CBP_removal_delay = 1600; handle->mEncParams.auto_scd = AVC_ON; handle->mEncParams.out_of_band_param_set = AVC_ON; handle->mEncParams.num_ref_frame = 1; handle->mEncParams.num_slice_group = 1; handle->mEncParams.nSliceHeaderSpacing = 0; handle->mEncParams.fullsearch = AVC_OFF; handle->mEncParams.search_range = 16; //handle->mEncParams.sub_pel = AVC_OFF; //handle->mEncParams.submb_pred = AVC_OFF; handle->mEncParams.width = width; handle->mEncParams.height = height; handle->mEncParams.bitrate = bit_rate; handle->mEncParams.frame_rate = 1000 * frame_rate; // In frames/ms! handle->mEncParams.CPB_size = (uint32)(bit_rate >> 1); handle->mEncParams.FreeRun = AVC_OFF; handle->mEncParams.MBsIntraRefresh = 0; handle->mEncParams.MBsIntraOverlap = 0; handle->mEncParams.encode_once = 1; // Set IDR frame refresh interval /*if ((unsigned) gop == 0xffffffff) { handle->mEncParams.idr_period = -1;//(mIDRFrameRefreshIntervalInSec * mVideoFrameRate); } else if (gop == 0) { handle->mEncParams.idr_period = 0; // All I frames } else { handle->mEncParams.idr_period = gop + 1; }*/ if (gop == 0 || gop < 0) { handle->mEncParams.idr_period = 0; //an infinite period, only one I frame } else { handle->mEncParams.idr_period = gop; //period of I frame, 1 means all frames are I type. } // Set profile and level handle->mEncParams.profile = AVC_BASELINE; handle->mEncParams.level = AVC_LEVEL4; handle->mEncParams.BitrateScale = AVC_OFF; return 0; } vl_codec_handle_t vl_video_encoder_init_fix_qp(vl_codec_id_t codec_id, int width, int height, int frame_rate, int bit_rate, int gop, vl_img_format_t img_format, int fix_qp) { int ret; AMVEncHandle *mHandle = new AMVEncHandle; bool has_mix = false; int dump_opts = 0; char *env_h264enc_dump; if (mHandle == NULL) goto exit; memset(mHandle, 0, sizeof(AMVEncHandle)); ret = initEncParamsFixQp(mHandle, width, height, frame_rate, bit_rate, gop, fix_qp); if (ret < 0) goto exit; ret = AML_HWEncInitialize(mHandle, &(mHandle->mEncParams), &has_mix, 2); if (ret < 0) goto exit; mHandle->mSpsPpsHeaderReceived = false; mHandle->mNumInputFrames = -1; // 1st two buffers contain SPS and PPS mHandle->fd_in = -1; mHandle->fd_out = -1; /*env_h264enc_dump = getenv("h264enc_dump"); LOGAPI("h264enc_dump=%s\n", env_h264enc_dump); if (env_h264enc_dump) dump_opts = atoi(env_h264enc_dump); if (dump_opts == 1) { mHandle->fd_in = open("/tmp/h264enc_dump_in.raw", O_CREAT | O_WRONLY); if (mHandle->fd_in == -1) LOGAPI("OPEN file for dump h264enc input failed: %s\n", strerror(errno)); mHandle->fd_out = -1; } else if (dump_opts == 2) { mHandle->fd_in = -1; mHandle->fd_out = open("/tmp/h264enc_dump_out.264", O_CREAT | O_WRONLY); if (mHandle->fd_out == -1) LOGAPI("OPEN file for dump h264enc output failed: %s\n", strerror(errno)); } else if (dump_opts == 3) { mHandle->fd_in = open("/tmp/h264enc_dump_in.raw", O_CREAT | O_WRONLY); if (mHandle->fd_in == -1) LOGAPI("OPEN file for dump h264enc input failed: %s\n", strerror(errno)); mHandle->fd_out = open("/tmp/h264enc_dump_out.264", O_CREAT | O_WRONLY); if (mHandle->fd_out == -1) LOGAPI("OPEN file for dump h264enc output failed: %s\n", strerror(errno)); } else { LOGAPI("h264enc_dump disabled\n"); mHandle->fd_in = -1; mHandle->fd_out = -1; }*/ return (vl_codec_handle_t) mHandle; exit: if (mHandle != NULL) delete mHandle; return (vl_codec_handle_t) NULL; } vl_codec_handle_t vl_video_encoder_init(vl_codec_id_t codec_id, int width, int height, int frame_rate, int bit_rate, int gop, vl_img_format_t img_format, int i_qp_min, int i_qp_max, int p_qp_min, int p_qp_max) { int ret; AMVEncHandle *mHandle = new AMVEncHandle; bool has_mix = false; int dump_opts = 0; char *env_h264enc_dump; if (mHandle == NULL) goto exit; memset(mHandle, 0, sizeof(AMVEncHandle)); ret = initEncParams(mHandle, width, height, frame_rate, bit_rate, gop, i_qp_min, i_qp_max, p_qp_min, p_qp_max); if (ret < 0) goto exit; ret = AML_HWEncInitialize(mHandle, &(mHandle->mEncParams), &has_mix, 2); if (ret < 0) goto exit; mHandle->mSpsPpsHeaderReceived = false; mHandle->mNumInputFrames = -1; // 1st two buffers contain SPS and PPS mHandle->fd_in = -1; mHandle->fd_out = -1; #if ES_DUMP_ENABLE mHandle->esDumpFd = -1; mHandle->esDumpFd = open(ES_FILE_NAME, O_CREAT | O_RDWR | O_TRUNC, 0644); if (mHandle->esDumpFd < 0) { printf("open es dump file error!\n"); goto exit; } #endif /*env_h264enc_dump = getenv("h264enc_dump"); LOGAPI("h264enc_dump=%s\n", env_h264enc_dump); if (env_h264enc_dump) dump_opts = atoi(env_h264enc_dump); if (dump_opts == 1) { mHandle->fd_in = open("/tmp/h264enc_dump_in.raw", O_CREAT | O_WRONLY); if (mHandle->fd_in == -1) LOGAPI("OPEN file for dump h264enc input failed: %s\n", strerror(errno)); mHandle->fd_out = -1; } else if (dump_opts == 2) { mHandle->fd_in = -1; mHandle->fd_out = open("/tmp/h264enc_dump_out.264", O_CREAT | O_WRONLY); if (mHandle->fd_out == -1) LOGAPI("OPEN file for dump h264enc output failed: %s\n", strerror(errno)); } else if (dump_opts == 3) { mHandle->fd_in = open("/tmp/h264enc_dump_in.raw", O_CREAT | O_WRONLY); if (mHandle->fd_in == -1) LOGAPI("OPEN file for dump h264enc input failed: %s\n", strerror(errno)); mHandle->fd_out = open("/tmp/h264enc_dump_out.264", O_CREAT | O_WRONLY); if (mHandle->fd_out == -1) LOGAPI("OPEN file for dump h264enc output failed: %s\n", strerror(errno)); } else { LOGAPI("h264enc_dump disabled\n"); mHandle->fd_in = -1; mHandle->fd_out = -1; }*/ return (vl_codec_handle_t) mHandle; exit: if (mHandle != NULL) delete mHandle; return (vl_codec_handle_t) NULL; } int vl_video_encoder_encode(vl_codec_handle_t codec_handle, vl_frame_type_t frame_type, unsigned char *in, int in_size, unsigned char *out, int format, int buf_type, vl_dma_info_t *dma_info) { int ret; uint8_t *outPtr = NULL; uint32_t dataLength = 0; int type; size_t rawdata_size = in_size; ssize_t io_ret; uint32_t is_DMA_buffer = 0; AMVEncHandle *handle = (AMVEncHandle *)codec_handle; if (!handle->mSpsPpsHeaderReceived) { ret = AML_HWEncNAL(handle, (unsigned char *)out, (unsigned int *)&in_size/*should be out size*/, &type); if (ret == AMVENC_SUCCESS) { handle->mSPSPPSDataSize = 0; handle->mSPSPPSData = (uint8_t *)malloc(in_size); if (handle->mSPSPPSData) { handle->mSPSPPSDataSize = in_size; memcpy(handle->mSPSPPSData, (unsigned char *)out, handle->mSPSPPSDataSize); LOGAPI("get mSPSPPSData size= %d at line %d \n", handle->mSPSPPSDataSize, __LINE__); #ifndef __ANDROID__ size_t merge_size = sizeof(sps_t) + sizeof(pps_t) + 5 + 5; uint8_t *merge_buf = (uint8_t *) malloc(merge_size); size_t merge_pos = 0; uint8_t *aux_buf = (uint8_t *) malloc(handle->mSPSPPSDataSize + 5); if (!merge_buf || !aux_buf) return -1; memset(merge_buf, 0, merge_size); memset(aux_buf, 0, handle->mSPSPPSDataSize + 5); memcpy(aux_buf, handle->mSPSPPSData, handle->mSPSPPSDataSize); *((int *)(aux_buf + handle->mSPSPPSDataSize)) = 0x01000000; // add trailing nal delimeter h264_stream_t* h = h264_new(); uint8_t* p = aux_buf; int nal_start, nal_end, nal_size; int64_t off = 0; size_t sz = handle->mSPSPPSDataSize + 5; size_t cursor = 0; uint8_t *sps_buf = (uint8_t *) malloc(sizeof(sps_t) + 5); uint8_t *pps_buf = (uint8_t *) malloc(sizeof(pps_t) + 5); memset(sps_buf, 0, sizeof(sps_t) + 5); memset(pps_buf, 0, sizeof(pps_t) + 5); while (find_nal_unit(p, sz, &nal_start, &nal_end) > 0) { printf("!! Found NAL at offset %lld (0x%04llX), size %lld (0x%04llX) \n", (long long int)(off + (p - aux_buf) + nal_start), (long long int)(off + (p - aux_buf) + nal_start), (long long int)(nal_end - nal_start), (long long int)(nal_end - nal_start) ); p += nal_start; read_nal_unit(h, p, nal_end - nal_start); p += (nal_end - nal_start); sz -= nal_end; if (h->nal->nal_unit_type == NAL_UNIT_TYPE_SPS) { h->sps->vui_parameters_present_flag = 1; h->sps->vui.timing_info_present_flag = 1; h->sps->vui.num_units_in_tick = 1; h->sps->vui.time_scale = handle->mEncParams.frame_rate / 500; nal_size = write_nal_unit(h, sps_buf, sizeof(sps_t) + 5); *((int*)(merge_buf + merge_pos)) = 0x01000000; merge_pos += 4; memcpy(merge_buf + merge_pos, sps_buf + 1, nal_size); merge_pos += nal_size; free(sps_buf); } else if (h->nal->nal_unit_type == NAL_UNIT_TYPE_PPS) { nal_size = write_nal_unit(h, pps_buf, sizeof(pps_t) + 5); *((int*)(merge_buf + merge_pos)) = 0x01000000; merge_pos += 4; memcpy(merge_buf + merge_pos, pps_buf + 1, nal_size); merge_pos += nal_size; free(pps_buf); } } free(handle->mSPSPPSData); handle->mSPSPPSData = merge_buf; handle->mSPSPPSDataSize = merge_pos; free(aux_buf); h264_free(h); #endif } handle->mNumInputFrames = 0; handle->mSpsPpsHeaderReceived = true; } else { LOGAPI("Encode SPS and PPS error, encoderStatus = %d. handle: %p\n", ret, (void *)handle); return -1; } } if (handle->mNumInputFrames >= 0) { AMVEncFrameIO videoInput; memset(&videoInput, 0, sizeof(videoInput)); videoInput.height = handle->mEncParams.height; videoInput.pitch = ((handle->mEncParams.width + 15) >> 4) << 4; /* TODO*/ videoInput.bitrate = handle->mEncParams.bitrate; videoInput.frame_rate = handle->mEncParams.frame_rate / 1000; videoInput.coding_timestamp = handle->mNumInputFrames * 1000 / videoInput.frame_rate; // in ms //LOGAPI("mNumInputFrames %lld, videoInput.coding_timestamp %llu, videoInput.frame_rate %f\n", handle->mNumInputFrames, videoInput.coding_timestamp,videoInput.frame_rate); if (format == 0) { //NV12 videoInput.fmt = AMVENC_NV12; } else if(format == 1) { //NV21 videoInput.fmt = AMVENC_NV21; } else if (format == 2) { //YV12 videoInput.fmt = AMVENC_YUV420; } else if (format == 3) { //rgb888 videoInput.fmt = AMVENC_RGB888; } else if (format == 4) { //bgr888 videoInput.fmt = AMVENC_BGR888; } if ((dma_info->num_planes > 0) && (buf_type == DMA_BUFF)) { /* DMA buffer, no need allocate */ is_DMA_buffer = 1; } if (is_DMA_buffer == 0) { videoInput.YCbCr[0] = (unsigned long)&in[0]; videoInput.YCbCr[1] = (unsigned long)(videoInput.YCbCr[0] + videoInput.height * videoInput.pitch); if (videoInput.fmt == AMVENC_NV12) { //NV12 videoInput.YCbCr[2] = 0; } else if(videoInput.fmt == AMVENC_NV21) { //NV21 videoInput.YCbCr[2] = 0; } else if (videoInput.fmt == AMVENC_YUV420) { //YV12 videoInput.YCbCr[2] = (unsigned long)(videoInput.YCbCr[1] + videoInput.height * videoInput.pitch / 4); } else if (videoInput.fmt == AMVENC_RGB888) { //rgb888 videoInput.YCbCr[1] = 0; videoInput.YCbCr[2] = 0; } else if (videoInput.fmt = AMVENC_BGR888) { //bgr888 ; } videoInput.canvas = 0xffffffff; videoInput.type = VMALLOC_BUFFER; videoInput.disp_order = handle->mNumInputFrames; videoInput.op_flag = 0; } else { videoInput.canvas = 0xffffffff; videoInput.type = DMA_BUFF; videoInput.num_planes = dma_info->num_planes; videoInput.shared_fd[0] = dma_info->shared_fd[0]; videoInput.shared_fd[1] = dma_info->shared_fd[1]; videoInput.shared_fd[2] = dma_info->shared_fd[2]; } if (handle->mKeyFrameRequested == true) { videoInput.op_flag = AMVEncFrameIO_FORCE_IDR_FLAG; handle->mKeyFrameRequested = false; } ret = AML_HWSetInput(handle, &videoInput); ++(handle->mNumInputFrames); if (ret == AMVENC_SUCCESS || ret == AMVENC_NEW_IDR) { if (ret == AMVENC_NEW_IDR) { outPtr = (uint8_t *) out + handle->mSPSPPSDataSize; dataLength = /*should be out size */in_size - handle->mSPSPPSDataSize; } else { outPtr = (uint8_t *) out; dataLength = /*should be out size */in_size; } if (is_DMA_buffer == 0) { if (handle->fd_in >= 0) { io_ret = write(handle->fd_in, in, rawdata_size); if (io_ret == -1) { printf("write raw frame failed: %s\n", errno); } else if (io_ret < rawdata_size) { printf("write raw frame: short write %zu vs %zd\n", rawdata_size, io_ret); } } } } else if (ret < AMVENC_SUCCESS) { LOGAPI("encoderStatus = %d at line %d, handle: %p", ret, __LINE__, (void *)handle); return -1; } ret = AML_HWEncNAL(handle, (unsigned char *)outPtr, (unsigned int *)&dataLength, &type); if (ret == AMVENC_PICTURE_READY) { if (type == AVC_NALTYPE_IDR) { if (handle->mSPSPPSData) { memcpy((uint8_t *) out, handle->mSPSPPSData, handle->mSPSPPSDataSize); dataLength += handle->mSPSPPSDataSize; LOGAPI("copy mSPSPPSData to buffer size= %d at line %d \n", handle->mSPSPPSDataSize, __LINE__); } } if (handle->fd_out >= 0) { io_ret = write(handle->fd_out, (void *)out, dataLength); if (io_ret == -1) { printf("write h264 packet failed: %s\n", errno); } else if (io_ret < dataLength) { printf("write raw h264 packet: short write %zu vs %zd\n", dataLength, io_ret); } } } else if ((ret == AMVENC_SKIPPED_PICTURE) || (ret == AMVENC_TIMEOUT)) { dataLength = 0; if (ret == AMVENC_TIMEOUT) { handle->mKeyFrameRequested = true; ret = AMVENC_SKIPPED_PICTURE; } LOGAPI("ret = %d at line %d, handle: %p", ret, __LINE__, (void *)handle); } else if (ret != AMVENC_SUCCESS) { dataLength = 0; } if (ret < AMVENC_SUCCESS) { LOGAPI("encoderStatus = %d at line %d, handle: %p", ret , __LINE__, (void *)handle); return -1; } } #if ES_DUMP_ENABLE if (dataLength >= 0) { write(handle->esDumpFd, (unsigned char *)out, dataLength); } #endif LOGAPI("vl_video_encoder_encode return %d\n",dataLength); return dataLength; } int vl_video_encoder_destroy(vl_codec_handle_t codec_handle) { AMVEncHandle *handle = (AMVEncHandle *)codec_handle; if (handle->fd_in >= 0) { fsync(handle->fd_in); close(handle->fd_in); } if (handle->fd_out >= 0) { fsync(handle->fd_out); close(handle->fd_out); } AML_HWEncRelease(handle); if (handle->mSPSPPSData) free(handle->mSPSPPSData); if (handle) delete handle; #if ES_DUMP_ENABLE close(handle->esDumpFd); #endif return 1; }