/* * 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. #include #include #include #if defined(__ANDROID__) #include #define LOGCAT #endif #include #include "include/vp_multi_codec_1_0.h" #include "include/AML_MultiEncoder.h" #include "include/enc_define.h" #include "vdi_osal.h" #include "include/h264bitstream.h" #include "include/h265bitstream.h" #define MAX_FRAME_HIST (128) #define MAX_FRAME_WINDOW (120) const char version[] = "Amlogic libvp_multi_codec version 1.0"; #define ENCODE_TIME_OUTER 0 #if ENCODE_TIME_OUTER 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 H264_HEADER_LEN 5 #define H265_HEADER_LEN 6 #define GET_DROPPABLE_P 1 #if defined(__ANDROID__) #define LOG_LINE() ALOGD("[%s:%d]\n", __FUNCTION__, __LINE__) #endif unsigned char avc_supportlevel_list[] = { AVC_LEVEL1_B, AVC_LEVEL1, AVC_LEVEL1_1, AVC_LEVEL1_2, AVC_LEVEL1_3, AVC_LEVEL2, AVC_LEVEL2_1, AVC_LEVEL2_2, AVC_LEVEL3, AVC_LEVEL3_1, AVC_LEVEL3_2, AVC_LEVEL4, AVC_LEVEL4_1, AVC_LEVEL4_2, AVC_LEVEL5, AVC_LEVEL5_1, }; unsigned char hevc_supportlevel_list[] = { HEVC_LEVEL1, HEVC_LEVEL2, HEVC_LEVEL2_1, HEVC_LEVEL3, HEVC_LEVEL3_1, HEVC_LEVEL4, HEVC_LEVEL4_1, HEVC_LEVEL5, HEVC_LEVEL5_1, HEVC_LEVEL5_2, HEVC_LEVEL6, HEVC_LEVEL6_1, HEVC_LEVEL6_2, HEVC_LEVEL8_5, }; const char* vl_get_version() { return version; } typedef struct { int userDataEnabled; const unsigned char *pUserData; int userDataSize; int activated_sps; int fps; int bitrate; int frame_num; int maxQp_I; int minQp_I; int maxQp_P; int minQp_P; int gop; int SeiDataSize; }sei_s; typedef struct vp_multi_vui_info_s { uint8_t vui_parameters_present_flag; uint8_t video_full_range_flag; uint8_t video_signal_type_present_flag; uint8_t colour_description_present_flag; uint8_t colour_primaries; uint8_t transfer_characteristics; uint8_t matrix_coefficients; int vui_timing_info_present_flag; int vui_num_units_in_tick; int vui_time_scale; }VPMultiVuiInfo; typedef struct vp_multi_s { AMVEncInitParams mEncParams; sei_s sei; AMVEncBufferType bufType; bool mPrependSPSPPSToIDRFrames; bool mSpsPpsHeaderReceived; bool mKeyFrameRequested; bool mSkipFrameRequested; int mLongTermRefRequestFlags; int mNumInputFrames; AMVEncFrameFmt fmt; int mSPSPPSDataSize; char *mSPSPPSData; amv_enc_handle_t am_enc_handle; int shared_fd[3]; uint32 mNumPlanes; int frame_sz_hist[MAX_FRAME_HIST]; int hist_in_idx; int hist_out_idx; int total_win_size; int hist_win_len; int hist_skip_thresh; VPMultiVuiInfo vui_info; int mSEIDataSize; char *mSEIData; } VPMultiEncHandle; static int64_t GetNowUs() { struct timeval tv; gettimeofday(&tv, NULL); return (int64_t) tv.tv_sec * 1000000 + (int64_t) tv.tv_usec; } static int validateLevel(int stream_type,uint32 level) { int iloop = 0; if (AMV_AVC == stream_type) { for (iloop = 0;iloop < sizeof(avc_supportlevel_list);iloop++) { if (avc_supportlevel_list[iloop] == level) { return level; } } return AVC_LEVEL_AUTO; // firmware determines a level. } else if(AMV_HEVC == stream_type) { for (iloop = 0;iloop < sizeof(hevc_supportlevel_list);iloop++) { if (hevc_supportlevel_list[iloop] == level) { return level; } } return HEVC_LEVEL_NONE; // firmware determines a level. } VLOG(DEBUG,"validateLevel stream_type err!"); return 0; //AVC AUTO/HEVC NONE } AMVEnc_Status initEncParams(VPMultiEncHandle *handle, vl_codec_id_t codec_id, vl_encode_info_t encode_info, qp_param_t* qp_tbl) { int width = encode_info.width; int height = encode_info.height; VLOG(DEBUG, "bit_rate:%d", encode_info.bit_rate); if ((width % 16 != 0 || height % 2 != 0)) { VLOG(DEBUG, "Video frame size %dx%d must be a multiple of 16", width, height); //return -1; } else if (height % 16 != 0) { VLOG(DEBUG, "Video frame height is not standard:%d", height); } else { VLOG(DEBUG, "Video frame size is %d x %d", width, height); } handle->mEncParams.rate_control = ENC_SETTING_ON; handle->mEncParams.initQP = qp_tbl->qp_I_base; handle->mEncParams.initQP_P = qp_tbl->qp_P_base; handle->mEncParams.initQP_B = qp_tbl->qp_B_base; handle->mEncParams.maxDeltaQP = qp_tbl->qp_I_max - qp_tbl->qp_I_min; handle->mEncParams.maxQP = qp_tbl->qp_I_max; handle->mEncParams.minQP = qp_tbl->qp_I_min; handle->mEncParams.maxQP_P = qp_tbl->qp_P_max; handle->mEncParams.minQP_P = qp_tbl->qp_P_min; handle->mEncParams.maxQP_B = qp_tbl->qp_B_max; handle->mEncParams.minQP_B = qp_tbl->qp_B_min; handle->mEncParams.qp_mode = encode_info.qp_mode; handle->mEncParams.forcePicQpEnable = encode_info.forcePicQpEnable; //printf("handle->mEncParams.forcePicQpEnable %d\n",handle->mEncParams.forcePicQpEnable); handle->mEncParams.forcePicQpI = encode_info.forcePicQpI; handle->mEncParams.forcePicQpP = encode_info.forcePicQpP; handle->mEncParams.forcePicQpB = encode_info.forcePicQpB; if (handle->mEncParams.forcePicQpEnable) { handle->mEncParams.rate_control = ENC_SETTING_OFF; } handle->mEncParams.rotate_angle = encode_info.frame_rotation; handle->mEncParams.mirror = encode_info.frame_mirroring; handle->mEncParams.init_CBP_removal_delay = 1600; handle->mEncParams.auto_scd = ENC_SETTING_OFF; handle->mEncParams.out_of_band_param_set = ENC_SETTING_OFF; handle->mEncParams.num_ref_frame = 1; handle->mEncParams.num_slice_group = 1; //handle->mEncParams.nSliceHeaderSpacing = 0; handle->mEncParams.fullsearch = ENC_SETTING_OFF; handle->mEncParams.search_range = 16; //handle->mEncParams.sub_pel = ENC_SETTING_OFF; //handle->mEncParams.submb_pred = ENC_SETTING_OFF; handle->mEncParams.width = width; handle->mEncParams.height = height; handle->mEncParams.src_width = width; handle->mEncParams.src_height = height; handle->mEncParams.bitrate = encode_info.bit_rate; handle->mEncParams.frame_rate = encode_info.frame_rate; handle->mEncParams.CPB_size = (uint32)(encode_info.bit_rate >> 1); handle->mEncParams.FreeRun = ENC_SETTING_OFF; handle->mEncParams.srcBitDepth = encode_info.srcBitDepth; handle->mEncParams.internalBitDepth = encode_info.internalBitDepth; if (handle->mEncParams.srcBitDepth != 8 && handle->mEncParams.srcBitDepth != 10) handle->mEncParams.srcBitDepth = 8; if (handle->mEncParams.internalBitDepth != 8 && handle->mEncParams.internalBitDepth != 10) handle->mEncParams.internalBitDepth = 8; if (encode_info.bitstream_buf_sz_kb > 0 && encode_info.bitstream_buf_sz_kb <= (32 * 1024)) { handle->mEncParams.es_buf_sz = encode_info.bitstream_buf_sz_kb*1024; } else { if (encode_info.bitstream_buf_sz > 0 && encode_info.bitstream_buf_sz <= 32) handle->mEncParams.es_buf_sz = encode_info.bitstream_buf_sz*1024*1024; else //default value handle->mEncParams.es_buf_sz = 0; //out of range, use default } if (encode_info.intra_refresh_mode && encode_info.intra_refresh_mode <= 4) { handle->mEncParams.IntraRefreshMode = encode_info.intra_refresh_mode; handle->mEncParams.IntraRefreshArg = encode_info.intra_refresh_arg; } else { handle->mEncParams.IntraRefreshMode = 0; handle->mEncParams.IntraRefreshArg = 0; } handle->mEncParams.encode_once = 1; if (encode_info.enc_feature_opts & ENABLE_ROI_FEATURE) handle->mEncParams.roi_enable = 1; if (encode_info.enc_feature_opts & ENABLE_PARA_UPDATE) handle->mEncParams.param_change_enable = 1; if (encode_info.enc_feature_opts & ENABLE_LONG_TERM_REF) handle->mEncParams.longterm_ref_enable = 1; if (encode_info.enc_feature_opts & 0x7c) { handle->mEncParams.GopPreset = (AMVGOPModeOPT) ((encode_info.enc_feature_opts >>2) & 0x1f); } VLOG(TRACE, "enc_feature_opts is 0x%x , GopPresetis 0x%x \n",encode_info.enc_feature_opts,handle->mEncParams.GopPreset); if (encode_info.img_format == IMG_FMT_NV12) { VLOG(INFO, "img_format is IMG_FMT_NV12 \n"); handle->fmt = AMVENC_NV12; } else if (encode_info.img_format == IMG_FMT_NV21) { VLOG(INFO, "img_format is IMG_FMT_NV21 \n"); handle->fmt = AMVENC_NV21; } else if (encode_info.img_format == IMG_FMT_YUV420P) { VLOG(INFO, "img_format is IMG_FMT_YUV420P \n"); handle->fmt = AMVENC_YUV420P; } else if (encode_info.img_format == IMG_FMT_RGB888) { VLOG(INFO, "img_format is IMG_FMT_RGB888 \n"); handle->fmt = AMVENC_RGB888; } else if (encode_info.img_format == IMG_FMT_RGBA8888) { VLOG(INFO, "img_format is IMG_FMT_RGBA8888 \n"); handle->fmt = AMVENC_RGBA8888; } else { VLOG(ERR, "img_format %d not supprot\n",encode_info.img_format); return AMVENC_FAIL; } handle->mEncParams.fmt = handle->fmt; if ((encode_info.img_format == IMG_FMT_RGB888) || (encode_info.img_format == IMG_FMT_RGBA8888)) { handle->mEncParams.fmt = AMVENC_NV21; //ge2d dst format PIXEL_FORMAT_YCrCb_420_SP } // Set IDR frame refresh interval if (encode_info.gop <= 0) { handle->mEncParams.idr_period = 0; //an infinite period, only one I frame } else { handle->mEncParams.idr_period = encode_info.gop; //period of I frame, 1 means all frames are I type. } VLOG(DEBUG, "mEncParams.idrPeriod: %d, gop %d\n", handle->mEncParams.idr_period, encode_info.gop); // Set profile and level if (codec_id == CODEC_ID_H265) { handle->mEncParams.stream_type = AMV_HEVC; handle->mEncParams.profile = encode_info.profile; handle->mEncParams.level = validateLevel(AMV_HEVC,encode_info.level); //handle->mEncParams.level = HEVC_LEVEL_NONE; // firmware determines a level. handle->mEncParams.hevc_tier = HEVC_TIER_MAIN; handle->mEncParams.initQP = 30; handle->mEncParams.BitrateScale = ENC_SETTING_OFF; handle->mEncParams.refresh_type = HEVC_CRA; } else if (codec_id == CODEC_ID_H264) { handle->mEncParams.stream_type = AMV_AVC; handle->mEncParams.profile = encode_info.profile; handle->mEncParams.level = validateLevel(AMV_AVC,encode_info.level); //handle->mEncParams.level = AVC_LEVEL4; handle->mEncParams.initQP = 30; handle->mEncParams.BitrateScale = ENC_SETTING_OFF; } else { VLOG(ERR, "No surpported codec_id %d\n", codec_id); return AMVENC_FAIL; } /* multi-slice */ if (encode_info.multi_slice_mode && encode_info.multi_slice_arg) { if (encode_info.multi_slice_mode > 2 || encode_info.multi_slice_mode < 0) { VLOG(ERR, "Invalid muti_slice mode setting %d\n", encode_info.multi_slice_mode); } else if (encode_info.multi_slice_mode > 1 && codec_id == CODEC_ID_H264) { VLOG(ERR, "H264 Invalid muti_slice mode setting %d\n", encode_info.multi_slice_mode); } else { VLOG(DEBUG, "Multi slice set mode %d par %d\n", encode_info.multi_slice_mode, encode_info.multi_slice_arg); handle->mEncParams.slice_mode = encode_info.multi_slice_mode; handle->mEncParams.slice_arg = encode_info.multi_slice_arg; } } handle->mEncParams.cust_qp_delta = encode_info.cust_gop_qp_delta; handle->vui_info.vui_parameters_present_flag = encode_info.vui_parameters_present_flag; handle->vui_info.video_full_range_flag = encode_info.video_full_range_flag; handle->vui_info.video_signal_type_present_flag = encode_info.video_signal_type_present_flag; handle->vui_info.colour_description_present_flag = encode_info.colour_description_present_flag; handle->vui_info.colour_primaries = encode_info.colour_primaries; handle->vui_info.transfer_characteristics = encode_info.transfer_characteristics; handle->vui_info.matrix_coefficients = encode_info.matrix_coefficients; handle->mEncParams.crop_enable = encode_info.crop_enable; if (handle->mEncParams.crop_enable) { handle->mEncParams.crop_left = encode_info.crop.left; handle->mEncParams.crop_top = encode_info.crop.top; handle->mEncParams.crop_right = encode_info.crop.right; handle->mEncParams.crop_bottom = encode_info.crop.bottom; } handle->sei.SeiDataSize = 100; handle->sei.fps = handle->mEncParams.frame_rate; handle->sei.bitrate = handle->mEncParams.bitrate; handle->sei.gop = handle->mEncParams.idr_period; handle->sei.maxQp_I = handle->mEncParams.maxQP; handle->sei.minQp_I = handle->mEncParams.minQP; handle->sei.maxQp_P = handle->mEncParams.maxQP_P; handle->sei.minQp_P = handle->mEncParams.minQP_P; return AMVENC_SUCCESS; } static void hist_reset(VPMultiEncHandle *handle) { if (handle) { handle->hist_in_idx = 0; handle->hist_out_idx = 0; handle->total_win_size = 0; } } static void strict_rc_check(VPMultiEncHandle *handle) { int fullness, bitate, bitrate_target; fullness = handle->hist_in_idx - handle->hist_out_idx; if (fullness < 0) fullness += MAX_FRAME_HIST; while (fullness > handle->hist_win_len) { // have extra, moveout no need handle->total_win_size -= handle->frame_sz_hist[handle->hist_out_idx]; if (handle->total_win_size <=0) { VLOG(ERR, "Size zero already size %d, fullness %d\n", handle->total_win_size, fullness); break; } handle->hist_out_idx++; if (handle->hist_out_idx >= MAX_FRAME_HIST) handle->hist_out_idx = 0; fullness --; } if (fullness == handle->hist_win_len) {// get the enough check bitrate bitate = (handle->total_win_size * 8 * handle->mEncParams.frame_rate)/ handle->hist_win_len; bitrate_target = handle->mEncParams.bitrate*handle->hist_skip_thresh/100; VLOG(INFO, "real bitrate %d target %d\n", bitate, bitrate_target); if (bitate > bitrate_target) { VLOG(DEBUG, " frame %d real bitrate %d over target %d skip\n", handle->mNumInputFrames, bitate, bitrate_target); handle->mSkipFrameRequested = true; } } } static void strict_rc_filled(VPMultiEncHandle *handle, int dataLength) { int fullness, space; fullness = handle->hist_in_idx - handle->hist_out_idx; if (fullness < 0) fullness += MAX_FRAME_HIST; space = MAX_FRAME_HIST - fullness; if (space) { // we shall have space handle->frame_sz_hist[handle->hist_in_idx] = dataLength; handle->hist_in_idx ++; handle->total_win_size += dataLength; if (handle->hist_in_idx >= MAX_FRAME_HIST) handle->hist_in_idx = 0; } else { VLOG(ERR, "no space for frame %d, size %d out %d total %d\n", handle->hist_in_idx, dataLength, handle->hist_out_idx, handle->total_win_size); } } bool check_qp_tbl(const qp_param_t* qp_tbl) { if (qp_tbl == NULL) { return false; } if (qp_tbl->qp_min < 0 || qp_tbl->qp_min > 51 || qp_tbl->qp_max < 0 || qp_tbl->qp_max > 51) { VLOG(ERR,"qp_min or qp_max out of range [0, 51]\n"); return false; } if (qp_tbl->qp_I_base < 0 || qp_tbl->qp_I_base > 51 || qp_tbl->qp_P_base < 0 || qp_tbl->qp_P_base > 51) { VLOG(ERR,"qp_I_base or qp_P_base out of range [0, 51]\n"); return false; } if (qp_tbl->qp_min > qp_tbl->qp_max || qp_tbl->qp_I_min > qp_tbl->qp_I_max || qp_tbl->qp_P_min > qp_tbl->qp_P_max) { VLOG(ERR,"min qp larger than max qp\n"); return false; } if (qp_tbl->qp_I_min < qp_tbl->qp_min || qp_tbl->qp_I_min > qp_tbl->qp_max || qp_tbl->qp_I_max < qp_tbl->qp_min || qp_tbl->qp_I_max > qp_tbl->qp_max) { VLOG(ERR,"qp_min_I or qp_max_I out of range [qp_min, qp_max]\n"); return false; } if (qp_tbl->qp_P_min < qp_tbl->qp_min || qp_tbl->qp_P_min > qp_tbl->qp_max || qp_tbl->qp_P_max < qp_tbl->qp_min || qp_tbl->qp_P_max > qp_tbl->qp_max) { VLOG(ERR,"qp_min_P or qp_max_P out of range [qp_min, qp_max]\n"); return false; } return true; } vl_codec_handle_t vl_multi_encoder_init(vl_codec_id_t codec_id, vl_encode_info_t encode_info, qp_param_t* qp_tbl) { int ret; VPMultiEncHandle* mHandle = (VPMultiEncHandle *) malloc(sizeof(VPMultiEncHandle)); if (mHandle == NULL) goto exit; memset(mHandle, 0, sizeof(VPMultiEncHandle)); if (!check_qp_tbl(qp_tbl)) { goto exit; } vp5_set_log_level(ERR); ret = initEncParams(mHandle, codec_id, encode_info, qp_tbl); if (ret < 0) goto exit; mHandle->am_enc_handle = AML_MultiEncInitialize(&(mHandle->mEncParams)); if (mHandle->am_enc_handle == 0) goto exit; mHandle->mPrependSPSPPSToIDRFrames = encode_info.prepend_spspps_to_idr_frames; mHandle->mSpsPpsHeaderReceived = false; mHandle->mNumInputFrames = -1; // 1st two buffers contain SPS and PPS if (encode_info.strict_rc_window > MAX_FRAME_WINDOW) encode_info.strict_rc_window = MAX_FRAME_WINDOW; mHandle->hist_win_len = encode_info.strict_rc_window; mHandle->hist_skip_thresh = encode_info.strict_rc_skip_thresh; return (vl_codec_handle_t)mHandle; exit: if (mHandle != NULL) free(mHandle); return (vl_codec_handle_t)NULL; } int int_to_ascii(int value, unsigned char *userData, int index) { int num_digits = 0, temp_value = value; do { num_digits++; temp_value /= 10; } while (temp_value > 0); temp_value = value; for (int i = index + num_digits - 1; i >= index; i--) { userData[i] = (temp_value % 10) + '0'; temp_value /= 10; } return num_digits; } void InitUserSei(sei_s *sei,uint32 op_flag) { static unsigned char userData[44]; int userDataIndex = 16; int fps,bitrate; unsigned char payLoadType[11] = {0x57, 0x41, 0x56, 0x45, 0x46, 0x49,0x4E,0x49, 0x49, 0x2D, 0x50}; const unsigned char uuid[16] = { 0x41, 0xFE, 0x3D, 0xAF, 0x9D, 0x22, 0x4C, 0xD7, 0x8F, 0x10, 0x79, 0x9F, 0x3F, 0x39, 0x9F, 0xEF }; for (int i = 0; i < 16; i++) { userData[i] = uuid[i]; } fps = sei->fps / 5; bitrate = sei->bitrate / 100000; // fps userDataIndex += int_to_ascii(fps, userData, userDataIndex); for (int i = 0; i < 4; i++) { userData[userDataIndex++] = payLoadType[i]; } // force I if (op_flag & AMVEncFrameIO_FORCE_IDR_FLAG) { userData[userDataIndex++] = payLoadType[4]; userData[userDataIndex++] = payLoadType[5]; } else { userData[userDataIndex++] = payLoadType[6]; userData[userDataIndex++] = payLoadType[7]; } // bitrate userDataIndex += int_to_ascii(bitrate, userData, userDataIndex); // I minQP userData[userDataIndex++] = payLoadType[8]; userDataIndex += int_to_ascii(sei->minQp_I, userData, userDataIndex); // I maxQP userData[userDataIndex++] = payLoadType[9]; userDataIndex += int_to_ascii(sei->maxQp_I, userData, userDataIndex); // P minQP userData[userDataIndex++] = payLoadType[10]; userDataIndex += int_to_ascii(sei->minQp_P, userData, userDataIndex); // P maxQP userData[userDataIndex++] = payLoadType[9]; userDataIndex += int_to_ascii(sei->maxQp_P, userData, userDataIndex); sei->userDataSize = userDataIndex; sei->pUserData = userData; } void vl_multi_encoder_adjust_h264_sei(VPMultiEncHandle* handle, uint32 op_flag) { int ret; bs_t bs = {0}; int sei_nalu_size = handle->sei.SeiDataSize; char *sei_nalu = handle->mSEIData; char startCodePrefix[] = {0, 0, 0, 1}; InitUserSei(&handle->sei,op_flag); memcpy(sei_nalu, startCodePrefix, sizeof(startCodePrefix)); bs_init(&bs, sei_nalu + sizeof(startCodePrefix), sei_nalu_size - sizeof(startCodePrefix)); write_sei_rbsp(&bs,handle->sei.pUserData,handle->sei.userDataSize); write_rbsp_trailing_bits(&bs); handle->mSEIDataSize = bs.p - bs.start + sizeof(startCodePrefix); return; } void vl_multi_encoder_adjust_h265_sei(VPMultiEncHandle* handle, uint32 op_flag) { int ret; bs_t bs = {0}; int sei_nalu_size = handle->sei.SeiDataSize; char *sei_nalu = handle->mSEIData; char seiUserData[35] = {0}; char startCodePrefix[] = {0, 0, 0, 1}; if (handle->mEncParams.stream_type == AMV_AVC) { handle->mSEIDataSize = 0; return; } if (op_flag & AMVEncFrameIO_FORCE_IDR_FLAG) ret = snprintf(seiUserData, sizeof(seiUserData), "WVIF%dB%dX%dI%d", handle->mEncParams.frame_rate / 5, handle->mEncParams.bitrate / 1000, handle->mEncParams.maxQP, handle->mEncParams.minQP); else ret = snprintf(seiUserData, sizeof(seiUserData), "WVF%dB%dX%dI%d", handle->mEncParams.frame_rate / 5, handle->mEncParams.bitrate / 1000, handle->mEncParams.maxQP, handle->mEncParams.minQP); if (ret >= sizeof(seiUserData)) VLOG(ERR, "sei info exceeds the userdatasize.\n"); memcpy(sei_nalu, startCodePrefix, sizeof(startCodePrefix)); bs_init(&bs, sei_nalu + sizeof(startCodePrefix), sei_nalu_size - sizeof(startCodePrefix)); write_sei_userdataunreg(&bs, seiUserData); write_rbsp_trailing_bits(&bs); handle->mSEIDataSize = bs.p - bs.start + sizeof(startCodePrefix); return; } int vl_multi_encoder_adjust_h264_sps(VPMultiEncHandle* handle,char *sps_nalu,int sps_nalu_size,int buffer_len,VPMultiVuiInfo vui_info) { sps_t sps = {0}; bs_t bs = {0}; int new_sps_size = 0; if ((!handle->vui_info.vui_parameters_present_flag) && (!((handle->mEncParams.idr_period != 1) && ((handle->mEncParams.GopPreset == GOP_OPT_NONE) || (handle->mEncParams.GopPreset == GOP_IP) || (handle->mEncParams.GopPreset == GOP_IP_SVC5))))) { VLOG(INFO,"vui_parameters_present_flag is false,do not add vui info"); return sps_nalu_size; } sps_nalu_size = EBSPtoRBSP(sps_nalu,H264_HEADER_LEN,sps_nalu_size); bs_init(&bs, sps_nalu + H264_HEADER_LEN, sps_nalu_size - H264_HEADER_LEN); read_seq_parameter_set_rbsp(&sps, &bs); read_rbsp_trailing_bits(&bs); if ((handle->mEncParams.idr_period != 1) && ((handle->mEncParams.GopPreset == GOP_OPT_NONE) || (handle->mEncParams.GopPreset == GOP_IP) || (handle->mEncParams.GopPreset == GOP_IP_SVC5))) { sps.vui_parameters_present_flag = 1; sps.vui.bitstream_restriction_flag = 1; if (sps.vui.bitstream_restriction_flag) { sps.vui.motion_vectors_over_pic_boundaries_flag = 1; sps.vui.max_bytes_per_pic_denom = 0; sps.vui.max_bits_per_mb_denom = 0; sps.vui.log2_max_mv_length_horizontal = 16; sps.vui.log2_max_mv_length_vertical = 16; sps.vui.num_reorder_frames = 0; sps.vui.max_dec_frame_buffering = 2; } } if (handle->vui_info.vui_parameters_present_flag) { sps.vui_parameters_present_flag = handle->vui_info.vui_parameters_present_flag; sps.vui.video_full_range_flag = handle->vui_info.video_full_range_flag; if (sps.vui_parameters_present_flag) { sps.vui.video_signal_type_present_flag = 1;//handle->video_signal_type_present_flag; } if (handle->vui_info.colour_primaries && handle->vui_info.transfer_characteristics && handle->vui_info.matrix_coefficients) { sps.vui.colour_description_present_flag = 1;//handle->colour_description_present_flag; sps.vui.colour_primaries = handle->vui_info.colour_primaries; sps.vui.transfer_characteristics = handle->vui_info.transfer_characteristics; sps.vui.matrix_coefficients = handle->vui_info.matrix_coefficients; } VLOG(INFO,"new header range =%d,primaries = %d,transfer:%d,matrix:%d ", sps.vui.video_full_range_flag,sps.vui.colour_primaries,sps.vui.transfer_characteristics,sps.vui.matrix_coefficients); } if (handle->vui_info.vui_timing_info_present_flag) { sps.vui.timing_info_present_flag = 1; sps.vui.num_units_in_tick = 1000; sps.vui.time_scale = handle->mEncParams.frame_rate * 2000; VLOG(INFO,"new header num_units_in_tick =%d,time_scale = %d ", sps.vui.num_units_in_tick, sps.vui.time_scale); } if (AVC_LEVEL_AUTO != handle->mEncParams.level) { sps.level_idc = handle->mEncParams.level; } //sps.frame_cropping_flag = handle->mEncParams.crop_enable;//1; if (handle->mEncParams.crop_enable || ((sps.pic_width_in_mbs_minus1 + 1) << 4) != handle->mEncParams.width || ((sps.pic_height_in_map_units_minus1 + 1) << 4) != handle->mEncParams.height) { sps.frame_cropping_flag = 1; } if (sps.frame_cropping_flag) { sps.frame_crop_top_offset = handle->mEncParams.crop_top / 2; sps.frame_crop_left_offset = handle->mEncParams.crop_left / 2; if (handle->mEncParams.crop_right || handle->mEncParams.crop_bottom) { sps.frame_crop_right_offset = (handle->mEncParams.width - handle->mEncParams.crop_right) / 2;//0;//(256 - 176)/2; sps.frame_crop_bottom_offset = (handle->mEncParams.height - handle->mEncParams.crop_bottom) / 2; } else { sps.frame_crop_right_offset = (((sps.pic_width_in_mbs_minus1 + 1) << 4) - handle->mEncParams.width) / 2; sps.frame_crop_bottom_offset = (((sps.pic_height_in_map_units_minus1 + 1) << 4) - handle->mEncParams.height) / 2; } VLOG(ERR,"crop top:%d,left:%d,right:%d,bottom:%d,enable:%d",sps.frame_crop_top_offset, sps.frame_crop_left_offset, sps.frame_crop_right_offset, sps.frame_crop_bottom_offset, sps.frame_cropping_flag); } memset(sps_nalu + H264_HEADER_LEN, 0, buffer_len - H264_HEADER_LEN); bs_init(&bs, sps_nalu + H264_HEADER_LEN, buffer_len - H264_HEADER_LEN); write_seq_parameter_set_rbsp(&sps, &bs); write_rbsp_trailing_bits(&bs); new_sps_size = bs.p - bs.start + H264_HEADER_LEN; new_sps_size = RBSPtoEBSP(sps_nalu,H264_HEADER_LEN,new_sps_size,0); return new_sps_size; } int vl_multi_encoder_adjust_h264_pps(VPMultiEncHandle* handle,char *pps_nalu,int pps_nalu_size) { sps_t pps = {0}; bs_t bs = {0}; int new_pps_size = 0; bs_init(&bs, pps_nalu + H264_HEADER_LEN, pps_nalu_size - H264_HEADER_LEN); read_pic_parameter_set_rbsp(&pps, &bs); read_rbsp_trailing_bits(&bs); memset(pps_nalu + H264_HEADER_LEN, 0, pps_nalu_size - H264_HEADER_LEN); bs_init(&bs, pps_nalu + H264_HEADER_LEN, pps_nalu_size - H264_HEADER_LEN); write_pic_parameter_set_rbsp(&pps, &bs); write_rbsp_trailing_bits(&bs); new_pps_size = bs.p - bs.start + H264_HEADER_LEN; return new_pps_size; } void vl_multi_encoder_adjust_h264_header(VPMultiEncHandle* handle,char *header,int *dataLength) { //adjust pps baseline profile sps_t sps; pps_t pps; bs_t bs = {0}; uint32_t i = 0; int sps_nalu_size; int pps_nalu_size; int new_pps_size; int new_sps_size; int pps_start = 0; int sps_start = 0; //VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; VLOG(INFO,"vl_multi_encoder_adjust_header,stream_type:%d",handle->mEncParams.stream_type); if (handle->mEncParams.stream_type != AMV_AVC) return; uint8_t *sps_nalu = (uint8_t *) malloc(sizeof(uint8_t) * (*dataLength)); if (sps_nalu == NULL) { VLOG(ERR,"malloc for sps failed"); return; } uint8_t *pps_nalu = (uint8_t *) malloc(sizeof(uint8_t) * (*dataLength)); if (pps_nalu == NULL) { VLOG(ERR,"malloc for sps or pps failed"); free(sps_nalu); return; } for (i = 0 ; i < *dataLength - H264_HEADER_LEN; i++) { if ((uint8_t)header[i+0] == 0 && (uint8_t)header[i+1] == 0 && (uint8_t)header[i+2] == 0 && (uint8_t)header[i+3] == 1 && (((uint8_t)header[i+4]) & 0x1f) == 7) { sps_start = i; VLOG(ERR,"sps_start=%d\n", sps_start); break; } } for (i = 0 ; i < *dataLength - H264_HEADER_LEN; i++) { if ((uint8_t)header[i+0] == 0 && (uint8_t)header[i+1] == 0 && (uint8_t)header[i+2] == 0 && (uint8_t)header[i+3] == 1 && (((uint8_t)header[i+4]) & 0x1f) == 8) { pps_start = i; VLOG(INFO,"pps_start=%d\n", pps_start); break; } } #if 0 char tmp[1024] = {0}; char tmp1[128] = {0}; for (int i = 0;i < *dataLength;i++) { sprintf(tmp1," %x",header[i]); strcat(tmp,tmp1); } VLOG(INFO,"old header data:%s",tmp); #endif memcpy(sps_nalu, header + sps_start, pps_start - sps_start); memcpy(pps_nalu, header + pps_start, *dataLength - pps_start); sps_nalu_size = pps_start - sps_start; pps_nalu_size = *dataLength - pps_start; VLOG(INFO,"old sps_nalu_size=%d,pps_nalu_size:%d", sps_nalu_size,pps_nalu_size); new_sps_size = vl_multi_encoder_adjust_h264_sps(handle,sps_nalu,sps_nalu_size,*dataLength,handle->vui_info); memset(header + sps_start, 0, *dataLength - sps_start); memcpy(header + sps_start, sps_nalu, new_sps_size); new_pps_size = vl_multi_encoder_adjust_h264_pps(handle,pps_nalu,pps_nalu_size); VLOG(INFO,"new sps_size:%d,new_pps_size=%d", new_sps_size,new_pps_size); memcpy(header + sps_start + new_sps_size, pps_nalu, new_pps_size); *dataLength = sps_start + new_sps_size + new_pps_size; #if 0 memset(tmp,0,sizeof(tmp)); memset(tmp1,0,sizeof(tmp1)); for (int i = 0;i < *dataLength;i++) { sprintf(tmp1," %x",header[i]); strcat(tmp,tmp1); } VLOG(INFO,"new header data:%s",tmp); #endif if (sps_nalu) free(sps_nalu); if (pps_nalu) free(pps_nalu); } void vl_multi_encoder_adjust_h265_header(VPMultiEncHandle* handle,char *header,int *dataLength) { bs_t bs; bs_t bs_v; //sps_h265_t sps = {0}; uint32_t i = 0; int vps_nalu_size = 0; int sps_nalu_size = 0; int pps_nalu_size = 0; int new_sps_size = 0; int new_vps_size = 0; int vps_start = 0; int sps_start = 0; int pps_start = 0; int ret = 0; h265_stream_t *pstream_handle = NULL; VLOG(INFO,"vl_multi_encoder_adjust_header,stream_type:%d",handle->mEncParams.stream_type); if (handle->mEncParams.stream_type != AMV_HEVC || !handle->vui_info.vui_parameters_present_flag) return; uint8_t *vps_nalu = (uint8_t *) malloc(sizeof(uint8_t) * (*dataLength)); if (vps_nalu == NULL) { VLOG(ERR,"hack_sps malloc for vps failed"); return; } uint8_t *sps_nalu = (uint8_t *) malloc(sizeof(uint8_t) * (*dataLength)); if (sps_nalu == NULL) { VLOG(ERR,"hack_sps malloc for sps failed"); free(vps_nalu); return; } uint8_t *pps_nalu = (uint8_t *) malloc(sizeof(uint8_t) * (*dataLength)); if (pps_nalu == NULL) { VLOG(ERR,"hack_sps malloc for pps failed"); free(vps_nalu); free(sps_nalu); return; } for (i=0;i<*dataLength-H265_HEADER_LEN;i++) { if ((uint8_t)header[i+0] == 0 && (uint8_t)header[i+1] == 0 && (uint8_t)header[i+2] == 0 && (uint8_t)header[i+3] == 1 && (((uint8_t)header[i+4]) & 0x7e) == 0x40) { vps_start = i; VLOG(INFO,"hack_sps vps_start=%d\n", vps_start); //break; } if ((uint8_t)header[i+0] == 0 && (uint8_t)header[i+1] == 0 && (uint8_t)header[i+2] == 0 && (uint8_t)header[i+3] == 1 && (((uint8_t)header[i+4]) & 0x7e) == 0x42) { sps_start = i; VLOG(INFO,"hack_sps pps_start=%d\n", sps_start); } if ((uint8_t)header[i+0] == 0 && (uint8_t)header[i+1] == 0 && (uint8_t)header[i+2] == 0 && (uint8_t)header[i+3] == 1 && (((uint8_t)header[i+4]) & 0x7e) == 0x44) { pps_start = i; VLOG(INFO,"hack_sps pps_start=%d\n", pps_start); break; } } vps_nalu_size = sps_start; sps_nalu_size = pps_start - sps_start; pps_nalu_size = *dataLength - pps_start; VLOG(INFO,"hack_sps old vps_nalu_size=%d,sps_nalu_size=%d,pps_nalu_size=%d",vps_nalu_size, sps_nalu_size, pps_nalu_size); memcpy(vps_nalu, header, vps_nalu_size); memcpy(sps_nalu, header + sps_start, sps_nalu_size); memcpy(pps_nalu, header + pps_start, pps_nalu_size); #if 0 char tmp[1024] = {0}; char tmp1[128] = {0}; for (int i = 0;i < *dataLength;i++) { sprintf(tmp1," %x",header[i]); strcat(tmp,tmp1); } VLOG(ERR,"old header data:%s",tmp); #endif if (sps_nalu_size <= H265_HEADER_LEN || vps_nalu_size <= H265_HEADER_LEN) { VLOG(ERR,"Invalid sps_nalu_size or vps_nalu_size: %d,%d", sps_nalu_size, vps_nalu_size); free(vps_nalu); free(sps_nalu); free(pps_nalu); return; } sps_nalu_size = EBSPtoRBSP(sps_nalu,H265_HEADER_LEN,sps_nalu_size); vps_nalu_size = EBSPtoRBSP(vps_nalu,H265_HEADER_LEN,vps_nalu_size); pstream_handle = h265bitstream_init(); //vps adjust bs_init(&bs_v,vps_nalu + H265_HEADER_LEN, vps_nalu_size - H265_HEADER_LEN); read_debug_video_parameter_set_rbsp(pstream_handle, &bs_v); read_debug_rbsp_trailing_bits(pstream_handle,&bs_v); if (HEVC_LEVEL_NONE != handle->mEncParams.level) { //level is valid pstream_handle->ptl->general_level_idc = handle->mEncParams.level; VLOG(INFO,"write level:%d",pstream_handle->ptl->general_level_idc); } if (handle->vui_info.vui_timing_info_present_flag) { pstream_handle->vps->vps_timing_info_present_flag = 1; pstream_handle->vps->vps_num_units_in_tick = 1000; pstream_handle->vps->vps_time_scale = handle->mEncParams.frame_rate * 1000; VLOG(INFO,"new header vps num_units_in_tick =%d,time_scale = %d ", pstream_handle->vps->vps_num_units_in_tick, pstream_handle->vps->vps_time_scale); } bs_init(&bs_v,vps_nalu + H265_HEADER_LEN, vps_nalu_size - H265_HEADER_LEN); write_debug_video_parameter_set_rbsp(pstream_handle, &bs_v); new_vps_size = vps_nalu_size; new_vps_size = RBSPtoEBSP(vps_nalu,H265_HEADER_LEN,new_vps_size,0); //sps_adjust bs_init(&bs, sps_nalu + H265_HEADER_LEN, sps_nalu_size - H265_HEADER_LEN); read_debug_seq_parameter_set_rbsp(pstream_handle, &bs); read_rbsp_trailing_bits(&bs); pstream_handle->sps->vui_parameters_present_flag = handle->vui_info.vui_parameters_present_flag; pstream_handle->vui->video_full_range_flag = handle->vui_info.video_full_range_flag; if (pstream_handle->sps->vui_parameters_present_flag) { pstream_handle->vui->video_signal_type_present_flag = 1;//handle->video_signal_type_present_flag; } if (handle->vui_info.colour_primaries && handle->vui_info.transfer_characteristics && handle->vui_info.matrix_coefficients) { pstream_handle->vui->colour_description_present_flag = 1;//handle->colour_description_present_flag; pstream_handle->vui->colour_primaries = handle->vui_info.colour_primaries; pstream_handle->vui->transfer_characteristics = handle->vui_info.transfer_characteristics; pstream_handle->vui->matrix_coeffs = handle->vui_info.matrix_coefficients; } if (handle->vui_info.vui_timing_info_present_flag) { pstream_handle->vui->vui_timing_info_present_flag = 1; pstream_handle->vui->vui_num_units_in_tick = 1000; pstream_handle->vui->vui_time_scale = handle->mEncParams.frame_rate * 1000; VLOG(INFO,"new header vui num_units_in_tick =%d,time_scale = %d ", pstream_handle->vui->vui_num_units_in_tick, pstream_handle->vui->vui_time_scale); } if (HEVC_LEVEL_NONE != handle->mEncParams.level) {//level is valid pstream_handle->ptl->general_level_idc = handle->mEncParams.level; //level_idc xts csd check!! } VLOG(INFO,"old header sps.vui_parameters_present_flag:%d, range =%d,primaries = %d,transfer:%d,matrix:%d", pstream_handle->sps->vui_parameters_present_flag,pstream_handle->vui->video_full_range_flag,pstream_handle->vui->colour_primaries,pstream_handle->vui->transfer_characteristics,pstream_handle->vui->matrix_coeffs); if (handle->mEncParams.crop_enable || pstream_handle->sps->pic_width_in_luma_samples != handle->mEncParams.width || pstream_handle->sps->pic_height_in_luma_samples != handle->mEncParams.height) { pstream_handle->sps->conformance_window_flag = 1; } //pstream_handle->sps->conformance_window_flag = handle->mEncParams.crop_enable;//1; if (pstream_handle->sps->conformance_window_flag) { pstream_handle->sps->conf_win_top_offse = handle->mEncParams.crop_top / 2; pstream_handle->sps->conf_win_left_offset = handle->mEncParams.crop_left / 2; if (handle->mEncParams.crop_right || handle->mEncParams.crop_bottom) { pstream_handle->sps->conf_win_right_offset = (handle->mEncParams.width - handle->mEncParams.crop_right) / 2;//0;//(256 - 176)/2; pstream_handle->sps->conf_win_bottom_offset = (handle->mEncParams.height - handle->mEncParams.crop_bottom) / 2; } else { pstream_handle->sps->conf_win_right_offset = (pstream_handle->sps->pic_width_in_luma_samples - handle->mEncParams.width) / 2; pstream_handle->sps->conf_win_bottom_offset = (pstream_handle->sps->pic_height_in_luma_samples - handle->mEncParams.height) / 2; } VLOG(ERR,"crop top:%d,left:%d,right:%d,bottom:%d,enable:%d",pstream_handle->sps->conf_win_top_offse, pstream_handle->sps->conf_win_left_offset, pstream_handle->sps->conf_win_right_offset, pstream_handle->sps->conf_win_bottom_offset, pstream_handle->sps->conformance_window_flag); } memset(sps_nalu + H265_HEADER_LEN, 0, *dataLength - H265_HEADER_LEN); bs_init(&bs, sps_nalu + H265_HEADER_LEN, *dataLength - H265_HEADER_LEN); write_debug_seq_parameter_set_rbsp(pstream_handle, &bs); write_rbsp_trailing_bits(&bs); new_sps_size = bs.p - bs.start + H265_HEADER_LEN; memset(header, 0, new_vps_size + new_sps_size + pps_nalu_size); memcpy(header,vps_nalu, new_vps_size); new_sps_size = RBSPtoEBSP(sps_nalu,H265_HEADER_LEN,new_sps_size,0); memcpy(header + new_vps_size,sps_nalu,new_sps_size); memcpy(header + new_sps_size + new_vps_size, pps_nalu, pps_nalu_size); *dataLength = new_sps_size + pps_nalu_size + new_vps_size; #if 0 memset(tmp,0,sizeof(tmp)); memset(tmp1,0,sizeof(tmp1)); for (int i = 0;i < *dataLength;i++) { sprintf(tmp1," %x",header[i]); strcat(tmp,tmp1); } VLOG(ERR,"new header:%s",tmp); #endif if (vps_nalu) free(vps_nalu); if (sps_nalu) free(sps_nalu); if (pps_nalu) free(pps_nalu); h265_free(pstream_handle); } void vl_multi_encoder_adjust_header(VPMultiEncHandle* handle,char *header,int *dataLength) { //adjust pps baseline profile //VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; VLOG(INFO,"vl_multi_encoder_adjust_header,stream_type:%d",handle->mEncParams.stream_type); if (handle->mEncParams.stream_type == AMV_AVC) vl_multi_encoder_adjust_h264_header(handle,header,dataLength); else if (handle->mEncParams.stream_type == AMV_HEVC) vl_multi_encoder_adjust_h265_header(handle,header,dataLength); return; } encoding_metadata_t vl_multi_encoder_generate_header(vl_codec_handle_t codec_handle, unsigned char *pHeader, unsigned int *pLength) { int ret = AMVENC_FAIL; //uint32_t dataLength = 0; VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; encoding_metadata_t result; memset(&result, 0, sizeof(encoding_metadata_t)); if (!handle->mSpsPpsHeaderReceived) { ret = AML_MultiEncHeader(handle->am_enc_handle, (unsigned char*)pHeader, pLength); VLOG(DEBUG, "ret = %d", ret); if (ret == AMVENC_SUCCESS) { vl_multi_encoder_adjust_header(handle, (char *)pHeader,(int *)pLength); handle->mSPSPPSDataSize = 0; handle->mSPSPPSData = (char *)malloc(*pLength); if (handle->mSPSPPSData) { handle->mSPSPPSDataSize = *pLength; //*pLength = dataLength; VLOG(DEBUG, "begin memcpy"); memcpy(handle->mSPSPPSData, (unsigned char*)pHeader, handle->mSPSPPSDataSize); VLOG(DEBUG, "get mSPSPPSData size= %d at line %d \n", handle->mSPSPPSDataSize, __LINE__); } handle->mSEIData = (char *)malloc(handle->sei.SeiDataSize); handle->mNumInputFrames = 0; handle->mSpsPpsHeaderReceived = true; } handle->mNumInputFrames = 0; handle->mSpsPpsHeaderReceived = true; result.is_valid = true; result.err_cod = AMVENC_SUCCESS; } else { *pLength = 0; VLOG(ERR, "Encode SPS and PPS error, mSpsPpsHeaderReceived = %d. handle: %p\n", handle->mSpsPpsHeaderReceived, (void*)handle); result.is_valid = false; result.err_cod = AMVENC_INVALID_PARAM; return result; } return result; } encoding_metadata_t vl_multi_encoder_encode(vl_codec_handle_t codec_handle, vl_frame_type_t type, unsigned char* out, vl_buffer_info_t *in_buffer_info, vl_buffer_info_t *ret_buf) { int ret; int i; uint32_t dataLength = 0; unsigned int uHeaderLength = 0; unsigned char header[128] = {0}; VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; encoding_metadata_t result; memset(&result, 0, sizeof(encoding_metadata_t)); #if ENCODE_TIME_OUTER gettimeofday(&start_test, NULL); #endif if (in_buffer_info == NULL) { VLOG(ERR, "invalid input buffer_info\n"); result.is_valid = false; result.err_cod = AMVENC_UNINITIALIZED; return result; } handle->bufType = (AMVEncBufferType)(in_buffer_info->buf_type); if (handle->bufType == DMA_BUFF) { if (in_buffer_info->buf_fmt == AMVENC_NV12 || in_buffer_info->buf_fmt == AMVENC_NV21 || in_buffer_info->buf_fmt == AMVENC_YUV420P) { if ((handle->mEncParams.width % 16 && in_buffer_info->buf_stride == 0) || in_buffer_info->buf_stride % 16) { VLOG(ERR, "dma buffer stride must be multiple of 16!"); result.is_valid = false; result.err_cod = AMVENC_ENCPARAM_MEM_FAIL; return result; } } } if (!handle->mSpsPpsHeaderReceived) { ret = AML_MultiEncHeader(handle->am_enc_handle, (unsigned char*)out, (unsigned int *)&dataLength); VLOG(DEBUG, "ret = %d", ret); if (ret == AMVENC_SUCCESS) { vl_multi_encoder_adjust_header(handle, out,&dataLength); handle->mSPSPPSDataSize = 0; handle->mSPSPPSData = (char *)malloc(dataLength); if (handle->mSPSPPSData) { handle->mSPSPPSDataSize = dataLength; VLOG(DEBUG, "begin memcpy"); memcpy(handle->mSPSPPSData, (unsigned char*)out, handle->mSPSPPSDataSize); VLOG(DEBUG, "get mSPSPPSData size= %d at line %d \n", handle->mSPSPPSDataSize, __LINE__); } handle->mSEIData = (char *)malloc(handle->sei.SeiDataSize); handle->mNumInputFrames = 0; handle->mSpsPpsHeaderReceived = true; } else { VLOG(ERR, "Encode SPS and PPS error, encoderStatus = %d. handle: %p\n", ret, (void*)handle); result.is_valid = false; if (ret == AMVENC_FAIL) result.err_cod = AMVENC_INVALID_PARAM; else result.err_cod = ret; return result; } } if (type == FRAME_TYPE_I || type == FRAME_TYPE_IDR) handle->mKeyFrameRequested = true; if (handle->mNumInputFrames >= 0) { AMVMultiEncFrameIO videoInput, videoRet; memset(&videoInput, 0, sizeof(videoInput)); memset(&videoRet, 0, sizeof(videoRet)); videoInput.height = handle->mEncParams.height; if (!in_buffer_info-> buf_stride) videoInput.pitch = handle->mEncParams.width; //((handle->mEncParams.width + 15) >> 4) << 4; else videoInput.pitch = in_buffer_info-> buf_stride; /* TODO*/ videoInput.bitrate = handle->mEncParams.bitrate; videoInput.frame_rate = handle->mEncParams.frame_rate / 1000.0f; videoInput.coding_timestamp = (unsigned long long)handle->mNumInputFrames * 1000 / videoInput.frame_rate; // in us VLOG(INFO, "videoInput.frame_rate %f videoInput.coding_timestamp %lld, mNumInputFrames %d", videoInput.frame_rate * 1000, videoInput.coding_timestamp, handle->mNumInputFrames); result.timestamp_us = videoInput.coding_timestamp; handle->shared_fd[0] = -1; handle->shared_fd[1] = -1; handle->shared_fd[2] = -1; if (handle->hist_win_len) { // have strict bitrate control strict_rc_check(handle); } videoInput.fmt = handle->fmt; VLOG(INFO, "in_buffer_info->buf_fmt=%d", in_buffer_info->buf_fmt); if (in_buffer_info->buf_fmt == IMG_FMT_NV12) { VLOG(INFO, "img_format is IMG_FMT_NV12 \n"); videoInput.fmt = AMVENC_NV12; } else if (in_buffer_info->buf_fmt == IMG_FMT_NV21) { VLOG(INFO, "img_format is IMG_FMT_NV21 \n"); videoInput.fmt = AMVENC_NV21; } else if (in_buffer_info->buf_fmt == IMG_FMT_YUV420P) { VLOG(INFO, "img_format is IMG_FMT_YUV420P \n"); videoInput.fmt = AMVENC_YUV420P; } else if (in_buffer_info->buf_fmt == IMG_FMT_RGB888) { VLOG(INFO, "img_format is IMG_FMT_RGB888 \n"); videoInput.fmt = AMVENC_RGB888; } else if (in_buffer_info->buf_fmt == IMG_FMT_RGBA8888) { VLOG(INFO, "img_format is IMG_FMT_RGBA8888 \n"); videoInput.fmt = AMVENC_RGBA8888; } if (handle->bufType == CANVAS_BUFFER) { VLOG(INFO, "setting canvas to videoInput"); videoInput.canvas = in_buffer_info->buf_info.canvas; videoInput.fmt = AMVENC_NV21; } else if (handle->bufType == DMA_BUFF) { vl_dma_info_t *dma_info; dma_info = &(in_buffer_info->buf_info.dma_info); if (videoInput.fmt == AMVENC_NV21 || videoInput.fmt == AMVENC_NV12) { if (dma_info->num_planes != 1 && dma_info->num_planes != 2) { VLOG(ERR, "invalid num_planes %d\n", dma_info->num_planes); result.is_valid = false; result.err_cod = AMVENC_ENCPARAM_MEM_FAIL; return result; } } else if (videoInput.fmt == AMVENC_YUV420P) { if (dma_info->num_planes != 1 && dma_info->num_planes != 3) { VLOG(ERR, "YUV420P invalid num_planes %d\n", dma_info->num_planes); result.is_valid = false; result.err_cod = AMVENC_ENCPARAM_MEM_FAIL; return result; } } else if (videoInput.fmt == AMVENC_RGB888) { VLOG(INFO, "process rgb dma input"); if (dma_info->num_planes != 1) { VLOG(ERR, "RGBA invalid num_planes %d\n", dma_info->num_planes); result.is_valid = false; result.err_cod = AMVENC_ENCPARAM_MEM_FAIL; return result; } } else if (videoInput.fmt == AMVENC_RGBA8888) { VLOG(INFO, "process rgba dma input"); if (dma_info->num_planes != 1) { VLOG(ERR, "RGBA invalid num_planes %d\n", dma_info->num_planes); result.is_valid = false; result.err_cod = AMVENC_ENCPARAM_MEM_FAIL; return result; } } else { VLOG(ERR, "unsupported color format"); result.is_valid = false; result.err_cod = AMVENC_ENCPARAM_MEM_FAIL; return result; } handle->mNumPlanes = dma_info->num_planes; for (i = 0; i < dma_info->num_planes; i++) { if (dma_info->shared_fd[i] < 0) { VLOG(ERR, "invalid dma_fd %d\n", dma_info->shared_fd[i]); result.is_valid = false; result.err_cod = AMVENC_ENCPARAM_MEM_FAIL; return result; } handle->shared_fd[i] = dma_info->shared_fd[i]; VLOG(INFO, "**shared_fd %d\n", handle->shared_fd[i]); videoInput.shared_fd[i] = dma_info->shared_fd[i]; } videoInput.num_planes = handle->mNumPlanes; #if defined(__ANDROID__) ALOGD("videoInput.num_planes=%d", videoInput.num_planes); #endif } else { unsigned long* in = in_buffer_info->buf_info.in_ptr; videoInput.YCbCr[0] = (unsigned long)in[0]; videoInput.YCbCr[1] = (unsigned long)(videoInput.YCbCr[0] + videoInput.height * videoInput.pitch * (handle->mEncParams.srcBitDepth == 10 ? 2 : 1)); if (videoInput.fmt == AMVENC_NV21 || videoInput.fmt == AMVENC_NV12) { videoInput.YCbCr[2] = 0; } else if (videoInput.fmt == AMVENC_YUV420P) { videoInput.YCbCr[2] = (unsigned long)(videoInput.YCbCr[1] + videoInput.height * videoInput.pitch / (handle->mEncParams.srcBitDepth == 10 ? 2 : 4)); } } //videoInput.fmt = handle->fmt; //videoInput.canvas = 0x00030201; videoInput.type = handle->bufType; videoInput.disp_order = handle->mNumInputFrames; videoInput.op_flag = 0; if (handle->mKeyFrameRequested == true) { if (handle->mEncParams.GopPreset == GOP_IP_SVC1 && (handle->mNumInputFrames % 2) || handle->mEncParams.GopPreset == GOP_IP_SVC2 && (handle->mNumInputFrames % 3) || handle->mEncParams.GopPreset == GOP_IP_SVC3 && (handle->mNumInputFrames % 4) || handle->mEncParams.GopPreset == GOP_IP_SVC4 && (handle->mNumInputFrames % 5) || handle->mEncParams.GopPreset == GOP_IP_SVC5 && (handle->mNumInputFrames % 4)) { VLOG(INFO, "Force encode key frame at %d frame,but this frame can't insert,skip!!",handle->mNumInputFrames); } else { videoInput.op_flag |= AMVEncFrameIO_FORCE_IDR_FLAG; handle->mKeyFrameRequested = false; VLOG(INFO, "Force encode key frame at %d frame",handle->mNumInputFrames); } } if (handle->mSkipFrameRequested == true) { videoInput.op_flag |= AMVEncFrameIO_FORCE_SKIP_FLAG; handle->mSkipFrameRequested = false; VLOG(INFO, "Force encode a skip frame at %d frame", handle->mNumInputFrames); } if (handle->mEncParams.longterm_ref_enable) { if (handle->mLongTermRefRequestFlags) { videoInput.op_flag |= ((handle->mLongTermRefRequestFlags & 0x3)<<2); VLOG(INFO, "Use LTR flags 0x%x at %d frame", handle->mLongTermRefRequestFlags, handle->mNumInputFrames); handle->mLongTermRefRequestFlags = 0; } } // if (handle->idrPeriod == 0) { // videoInput.op_flag |= AMVEncFrameIO_FORCE_IDR_FLAG; //} videoInput.pic_type = type; ret = AML_MultiEncSetInput(handle->am_enc_handle, &videoInput); ++(handle->mNumInputFrames); VLOG(NONE, "AML_MultiEncSetInput ret %d\n", ret); if (ret < AMVENC_SUCCESS) { VLOG(ERR, "encoderStatus = %d, handle: %p", ret,(void*)handle); result.is_valid = false; if (ret == AMVENC_FAIL) result.err_cod = AMVENC_INVALID_PARAM; else result.err_cod = ret; return result; } //t1 = GetNowUs(); ret = AML_MultiEncNAL(handle->am_enc_handle, out, (unsigned int*)&dataLength, &videoRet); //t2 = GetNowUs(); //VLOG(ERR, "AML_MultiEncNAL cost: %3.1f ms", (t2-t1)/1000.0); VLOG(NONE, "AML_MultiEnc ret %d, dataLength %d\n", ret,dataLength); if (ret == AMVENC_PICTURE_READY) { if ((videoRet.encoded_frame_type == 0) || handle->mNumInputFrames == 1){ if ((handle->mPrependSPSPPSToIDRFrames || handle->mNumInputFrames == 1) && (handle->mSPSPPSData)) { if (handle->mEncParams.stream_type == AMV_HEVC) vl_multi_encoder_adjust_h265_sei(handle, videoInput.op_flag); else if (handle->mEncParams.stream_type == AMV_AVC) vl_multi_encoder_adjust_h264_sei(handle, videoInput.op_flag); memmove(out + handle->mSPSPPSDataSize + handle->mSEIDataSize, out, dataLength); memcpy(out, handle->mSPSPPSData, handle->mSPSPPSDataSize); memcpy(out + handle->mSPSPPSDataSize, handle->mSEIData, handle->mSEIDataSize); dataLength += (handle->mSPSPPSDataSize + handle->mSEIDataSize); VLOG(DEBUG, "copy mSPSPPSData to buffer size= %d\n",handle->mSPSPPSDataSize); } else { if (handle->mEncParams.stream_type == AMV_HEVC) vl_multi_encoder_adjust_h265_sei(handle, videoInput.op_flag); else if (handle->mEncParams.stream_type == AMV_AVC) vl_multi_encoder_adjust_h264_sei(handle, videoInput.op_flag); memmove(out + handle->mSEIDataSize, out, dataLength); memcpy(out, handle->mSEIData, handle->mSEIDataSize); dataLength += handle->mSEIDataSize; if ((videoInput.op_flag & AMVEncFrameIO_FORCE_IDR_FLAG) && handle->vui_info.vui_timing_info_present_flag) { ret = AML_MultiEncHeader(handle->am_enc_handle, (unsigned char*)header, (unsigned int *)&uHeaderLength); VLOG(DEBUG, "ret = %d", ret); if (ret == AMVENC_SUCCESS) { vl_multi_encoder_adjust_header(handle, (char *)header,(int *)&uHeaderLength); if (handle->mSPSPPSData) free(handle->mSPSPPSData); handle->mSPSPPSDataSize = 0; handle->mSPSPPSData = (char *)malloc(uHeaderLength); if (handle->mSPSPPSData) { handle->mSPSPPSDataSize = uHeaderLength; VLOG(DEBUG, "begin memcpy"); memcpy(handle->mSPSPPSData, (unsigned char*)header, handle->mSPSPPSDataSize); VLOG(DEBUG, "get mSPSPPSData size= %d at line %d \n", handle->mSPSPPSDataSize, __LINE__); } } memmove(out + handle->mSPSPPSDataSize, out, dataLength); memcpy(out, handle->mSPSPPSData, handle->mSPSPPSDataSize); dataLength += handle->mSPSPPSDataSize; } } result.is_key_frame = true; } if (videoRet.encoded_frame_type == 5) result.extra.frame_type = FRAME_TYPE_IDR; else if (videoRet.encoded_frame_type == 0) result.extra.frame_type = FRAME_TYPE_I; else if (videoRet.encoded_frame_type == 1) { #if GET_DROPPABLE_P uint8 nal_ref_idc = 0; if (handle->mEncParams.stream_type == AMV_AVC) { nal_ref_idc = (out[4] >> 5) & 3; VLOG(DEBUG, "nal_ref_idc %d\n", nal_ref_idc); if (nal_ref_idc < 2) result.extra.frame_type = FRAME_TYPE_DROPPABLE_P; else result.extra.frame_type = FRAME_TYPE_P; } else #endif result.extra.frame_type = FRAME_TYPE_P; } else if (videoRet.encoded_frame_type == 2) result.extra.frame_type = FRAME_TYPE_B; result.extra.average_qp_value = videoRet.enc_average_qp; result.extra.intra_blocks = videoRet.enc_intra_blocks; result.extra.merged_blocks = videoRet.enc_merged_blocks; result.extra.skipped_blocks = videoRet.enc_skipped_blocks; } else if ((ret == AMVENC_SKIPPED_PICTURE) || (ret == AMVENC_TIMEOUT)) { dataLength = 0; if (ret == AMVENC_TIMEOUT) { handle->mKeyFrameRequested = true; ret = AMVENC_SKIPPED_PICTURE; } VLOG(INFO, "ret = %d, handle: %p", ret,(void*)handle); } else if (ret != AMVENC_SUCCESS) { dataLength = 0; } if (ret < AMVENC_SUCCESS) { VLOG(ERR, "encoderStatus = %d, handle: %p", ret,(void*)handle); result.is_valid = false; if (ret == AMVENC_FAIL) result.err_cod = AMVENC_INVALID_PARAM; else result.err_cod = ret; return result; } if (handle->hist_win_len) { // one frame encoded fill hist strict_rc_filled(handle, dataLength); } /* check the returned frame if it has */ if (videoRet.type == DMA_BUFF) { //have buffer return? if (videoRet.num_planes) { ret_buf ->buf_type = (vl_buffer_type_t)(videoRet.type); ret_buf ->buf_info.dma_info.num_planes = videoRet.num_planes; for (i = 0; i < videoRet.num_planes; i++) ret_buf ->buf_info.dma_info.shared_fd[i] = videoRet.shared_fd[i]; } } else if (videoRet.YCbCr[0] != 0) { ret_buf ->buf_type = (vl_buffer_type_t)(videoRet.type); ret_buf ->buf_info.in_ptr[0] = videoRet.YCbCr[0]; } } result.is_valid = true; result.encoded_data_length_in_bytes = dataLength; #if ENCODE_TIME_OUTER 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; printf("%p#Encode time : %lu us, frame_number : %llu\n", handle->am_enc_handle, encode_time_per_frame, total_encode_frames); #endif // VLOG(INFO, "frame extra info type %d, av_qp %d,intra %d,merged %d,skip %d\n", // result.extra.frame_type, result.extra.average_qp_value, // result.extra.intra_blocks, result.extra.merged_blocks, // result.extra.skipped_blocks); return result; } int vl_video_encoder_getavgqp(vl_codec_handle_t codec_handle, int *avg_qp) { AMVEnc_Status ret; VPMultiEncHandle *handle = (VPMultiEncHandle *)codec_handle; int avgqp; ret = AML_MultiEncGetAvgQp(handle->am_enc_handle, &avgqp); if (ret == AMVENC_SUCCESS) { *avg_qp = avgqp; } else { VLOG(ERR, "get avgqp fail! ret = %d at line %d", ret, __LINE__); } return ret; } int vl_video_encoder_update_qp_hint(vl_codec_handle_t codec_handle, unsigned char *pq_hint_table, int size) { int ret; VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; if (handle->am_enc_handle == 0) //not init the encoder yet return -1; if (handle->mEncParams.roi_enable == 0) //no eoi enabled return -2; ret = AML_MultiEncUpdateRoi(handle->am_enc_handle, pq_hint_table, size); if (ret != AMVENC_SUCCESS) return -3; return 0; } int vl_video_encoder_change_bitrate(vl_codec_handle_t codec_handle, int bitRate) { int ret; VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; if (handle->am_enc_handle == 0) //not init the encoder yet return -1; if (handle->mEncParams.param_change_enable == 0) //no change enabled return -2; ret = AML_MultiEncChangeBitRate(handle->am_enc_handle, bitRate); if (ret != AMVENC_SUCCESS) return -3; hist_reset(handle); //bitrate target change reset the hist handle->mEncParams.bitrate = bitRate; return 0; } int vl_video_encoder_change_framerate(vl_codec_handle_t codec_handle, int frameRate,int bitRate) { int ret; VPMultiEncHandle *handle = (VPMultiEncHandle *)codec_handle; if (handle->am_enc_handle == 0) //not init the encoder yet return -1; if (handle->mEncParams.param_change_enable == 0) //no change enabled return -2; ret = AML_MultiEncChangeFrameRate(handle->am_enc_handle, frameRate, bitRate); if (ret != AMVENC_SUCCESS) return -3; //hist_reset(handle); //bitrate target change reset the hist handle->mEncParams.frame_rate = frameRate; handle->mKeyFrameRequested = true; handle->vui_info.vui_parameters_present_flag = 1; handle->vui_info.vui_timing_info_present_flag = 1; //handle->mEncParams.bitrate = bitRate; return 0; } int vl_video_encoder_skip_frame(vl_codec_handle_t codec_handle) { VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; if (handle->am_enc_handle == 0) //not init the encoder yet return -1; handle->mSkipFrameRequested = true; return 0; } int vl_video_encoder_change_qp(vl_codec_handle_t codec_handle, int minQpI, int maxQpI, int maxDeltaQp, int minQpP, int maxQpP, int minQpB, int maxQpB) { int ret; VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; if (handle->am_enc_handle == 0) //not init the encoder yet return -1; if (handle->mEncParams.param_change_enable == 0) //no change enabled return -2; if (minQpI < 0 || minQpI > 51 || maxQpI < 0 || maxQpI > 51 || maxDeltaQp < 0 || maxDeltaQp > 51 || minQpP < 0 || minQpP > 51 || maxQpP < 0 || maxQpP > 51 || minQpB < 0 || minQpB > 51 || maxQpB < 0 || maxQpB > 51) { VLOG(ERR,"qp min or qp max out of range [0, 51]\n"); return -3; } if (minQpI >= maxQpI || minQpP >= maxQpP || minQpB >= maxQpB) { VLOG(ERR,"qp min or qp_max out of range [min, max]\n"); return -4; } ret = AML_MultiEncChangeQPMinMax(handle->am_enc_handle, minQpI, maxQpI, maxDeltaQp, minQpP, maxQpP, minQpB, maxQpB); if (ret != AMVENC_SUCCESS) return -5; return 0; } int vl_video_encoder_change_gop(vl_codec_handle_t codec_handle, int IntraQP, int IntraPeriod) { int ret; VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; if (handle->am_enc_handle == 0) //not init the encoder yet return -1; if (handle->mEncParams.param_change_enable == 0) //no change enabled return -2; if (IntraQP < 0 || IntraQP > 51 ) { VLOG(ERR, "QP value out of range [0, 51]\n"); return -3; } if (IntraPeriod <= 1 ) { VLOG(ERR, "Invalid Intra Period %d\n", IntraPeriod); return -4; } ret = AML_MultiEncChangeIntraPeriod(handle->am_enc_handle, IntraQP, IntraPeriod); if (ret != AMVENC_SUCCESS) return -5; return 0; } int vl_video_encoder_change_multi_slice(vl_codec_handle_t codec_handle, int multi_slice_mode, int multi_slice_para) { int ret; VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; if (handle->am_enc_handle == 0) //not init the encoder yet return -1; if (handle->mEncParams.param_change_enable == 0) //no change enabled return -2; if (multi_slice_mode < 0 || multi_slice_mode > 2 ) { VLOG(ERR, "multi slice mode value out of range [0, 2]\n"); return -3; } if (multi_slice_mode == 2 && handle->mEncParams.stream_type == AMV_AVC) { VLOG(ERR, "multi slice mode 2 no support for H.264\n"); return -4; } if (multi_slice_mode && multi_slice_para == 0) { VLOG(ERR, "Invalid multi-slice para %d\n", multi_slice_para); return -5; } ret = AML_MultiEncChangeMutiSlice(handle->am_enc_handle, multi_slice_mode, multi_slice_para); if (ret != AMVENC_SUCCESS) return -6; return 0; } int vl_video_encoder_longterm_ref(vl_codec_handle_t codec_handle, int LongtermRefFlags) { VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; if (handle->am_enc_handle == 0) //not init the encoder yet return -1; if (handle->mEncParams.longterm_ref_enable == 0) //no enabled return -2; if (LongtermRefFlags > 3 || LongtermRefFlags < 0) { VLOG(ERR, "Longterm ref flag invalid 0x%x\n", LongtermRefFlags); return -3; } handle->mLongTermRefRequestFlags = LongtermRefFlags; return 0; } int vl_video_encoder_change_strict_rc(vl_codec_handle_t codec_handle, int bitrate_window, int skip_threshold) { int ret; VPMultiEncHandle* handle = (VPMultiEncHandle *)codec_handle; if (handle->am_enc_handle == 0) //not init the encoder yet return -1; if (bitrate_window > MAX_FRAME_WINDOW) bitrate_window = MAX_FRAME_WINDOW; else if (bitrate_window < 0) bitrate_window = 0; handle->hist_win_len = bitrate_window; handle->hist_skip_thresh = skip_threshold; if (bitrate_window == 0) {// disable hist_reset(handle); } return 0; } int vl_multi_encoder_destroy(vl_codec_handle_t codec_handle) { VPMultiEncHandle *handle = (VPMultiEncHandle *)codec_handle; if (handle == NULL) return -1; AML_MultiEncRelease(handle->am_enc_handle); #if ENCODE_TIME_OUTER printf("%p#Total encode time : %llu, Total encode frames : %llu\n", handle->am_enc_handle, total_encode_time, total_encode_frames); #endif if (handle->mSPSPPSData) free(handle->mSPSPPSData); if (handle->mSEIData) free(handle->mSEIData); if (handle) free(handle); return 1; }