/* * 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 #include #include #include #include #include #include #include #include #include #include "./test_dma_api.h" #include "../amvenc_lib/include/vp_multi_codec_1_0.h" #include #include #define WIDTH 3840 #define HEIGHT 3840 // consider rotated frame #define FRAMERATE 60 #define INIT_RETRY 100 #define FORCE_PICTURE_TYPE 0x1 #define CHANGE_TARGET_RATE 0x2 #define CHANGE_MIN_MAX_QP 0x4 #define CHANGE_GOP_PERIOD 0x8 #define LONGTERM_REF_SET 0x10 #define CUST_SIZE_SMOOTH 0x20 #define CUST_APP_H264_PLUS 0x40 #define CHANGE_MULTI_SLICE 0x80 #define FORCE_PICTURE_SKIP 0x100 #define CHANGE_STRICT_RC 0x200 #define CHANGE_FRAME_RATE 0x400 #define ENCODER_STRING_LEN_MAX 256 // GOP mode strings static const char *Gop_string[] = { "Default mode IP only", "All I frame mode", "IP only mode", "IBBBP mode", "IP only 1-frame SVC mode", "IP only 2-frame SVC mode", "IP only 3-frame SVC mode", "IP only 4-frame SVC mode", "IP only customer mode", "reserved", "IP only 5-frame SVC mode", }; typedef struct { int FrameNum; //change frame number int enable_option; //FORCE_PICTURE_TYPE int picType; //vl_frame_type_t //CHANGE_TARGET_RATE int bitRate; //CHANGE_MIN_MAX_QP int minQpI; int maxQpI; int maxDeltaQp; int minQpP; int maxQpP; int minQpB; int maxQpB; //CHANGE_GOP_PERIOD int intraQP; int GOPPeriod; //LONGTERM_REF_SET int LTRFlags; //bit 0: UseCurSrcAsLongtermPic; bit 1: Use LTR frame // CUST_SIZE_SMOOTH int smooth_level; //0 normal, 50(middle), 100 (high) // CUST_APP_H264_PLUS int newIDRIntervel; // shall be multiply of gop length int QPdelta_IDR; // QP delta apply to IDRs int QPdelta_LTR; // QP delta apply to LTR P frames // Change multi-slice int Slice_Mode; //multi slice mode int Slice_Para; //para // Change strict bitrate control int bitrate_window; //bitrate cal window int pskip_threshold; //threshold for pskip //CHANGE_FRAME_RATE int frameRate; } CfgChangeParam; typedef struct { int seqNum; struct timeval timeFrameStart; struct timeval timeFrameEnd; char srcfile[ENCODER_STRING_LEN_MAX]; // yuv data url in your root fs char outfile[ENCODER_STRING_LEN_MAX]; // stream url in your root fs int width; // width int height; // height int gop; // I frame refresh interval int framerate; // framerate int bitrate; // bit rate int num; // encode frame count int fmt; // encode input fmt 1 : nv12, 2 : nv21, 3 : yuv420p(yv12/yu12) int buf_type; // 0:vmalloc, 3:dma buffer int num_planes; // used for dma buffer case. 2 : nv12/nv21, 3 : yuv420p(yv12/yu12) int codec_id; // 4 : h.264, 5 : h.265 int cfg_opt; /* optional, flags to control the encode feature settings bit 0:roi control. 0: disabled (default) 1: enabled bit 1: update control. 0: disabled (default) 1: enabled bit 2 ~ bit 6 encode GOP patttern options: 0 default(IP) 1:I only 2:IP_only 3: IBBBP 4:IP_SVC1 5:IP_SVC2 6: IP_SVC3 7: IP_SVC4 8: CUSTP bit 7:long term refereces. 0: disabled (default) 1: enabled bit 8:cust source buffer stride . 0: disabled (default) 1: enabled bit 9: convert normal source file stride to cust source stride. 0: disabled (default) 1: enabled bit 10:enable intraRefresh. 0: disabled (default) 1: enabled bit 11 ~ bit 13: profile 0: auto (default) others as following 1: BASELINE(AVC) MAIN (HEVC); 2 MAIN (AVC) main10 (HEVC) 3: HIGH (AVC) STILLPICTURE (HEVC); 4 HIGH10 (AVC) bit 14 ~ bit 15: frame rotation(counter-clockwise, in degree): 0:none, 1:90, 2:180, 3:270 bit 16 ~ bit 17: frame mirroring: 0:none, 1:vertical, 2:horizontal, 3:both V and H bit 18: enable customer bitstream buffer size bit 19 ~ bit 20: enable multi slice mode: 0, one slice(no para), 1 by MB(16x16)/CTU(64x64), 2 by CTU + size(byte) bit 21 cust_qp_delta_en bit 22 strict_rate_ctrl_en (skipP allowed to limit bitrate) default 0 bit 23 forcePicQpEnable default 0 */ char roifile[ENCODER_STRING_LEN_MAX]; // optional, roi info url in root fs, no present if roi is disabled char cfg_file[ENCODER_STRING_LEN_MAX]; // optional, cfg update info url. no present if update is disabled int src_buf_stride; // optional, source buffer stride int intraRefresh ; // optional, lower 4 bits for intra-refresh mode; others for refresh parameter, controlled by intraRefresh bit int bitStreamBuf ; // optional, encoded bitstream buffer size in MB int MultiSlice_parameter; // optional, multi slice parameter in MB/CTU or CTU and size (HIGH_16bits size + LOW_16bit CTU) combined int cust_qp_delta; // optional, cust_qp_delta apply to P frames value >0 lower; <0 increase the quality int strict_rate_control_parmeter; // optional, High 16 bit bitrate window (frames max 120). low 16 bit bitrate threshold (percent) int forcePicQpIPB; } encodecCfgParam; typedef struct { int CurFrameNum; //current frame number int CurGOPSize; // current int CurGOPCounter; int PrevFrameQP; // previous frame QP value int minQpI_base; int maxQpI_base; int maxDeltaQp_base; int minQpP_base; int maxQpP_base; int minQpB_base; int maxQpB_base; int temp_QP_change; // QP enforcing flag int cust_size_smooth_enable; // flag indentify size_smooth is on going int smooth_qpdelta; // QP delata apply to I frames. // CUST_APP_H264_PLUS int cust_ltr_enable; //CUST H264 feature enabled int GopLTRIntervel; // LTR P frame interval. int GopLTRCounter; // counter to decide LTR P frames. int QPdelta_IDR; // QP delta apply to IDRs int QPdelta_LTR; // QP delta apply to LTR P frames int qp_idr; // real applied qp_idr int qp_ltr; //real applied qp_ltr } PlayStatInfo; static int ParseCfgUpdateFile(FILE *fp, CfgChangeParam *cfg_update); static int ParseEncodeCfgFile(FILE *fp, encodecCfgParam *cfg_encode); static void *encode_thread(void *param); #define CFG_ENCODE_NUM_MAX 6 // Helper function to calculate time diffs. static unsigned int uTimeDiff(struct timeval end, struct timeval start) { return (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); } //return timeDiff in u64 static unsigned long long uTimeDiffLL(struct timeval end, struct timeval start) { return (unsigned long long )(end.tv_sec - start.tv_sec) * 1000000 + (unsigned long long )(end.tv_usec - start.tv_usec); } int main(int argc, const char *argv[]) { int i = 0; int arg_count = 0; FILE *fp_cfg_encode = NULL; encodecCfgParam cfg_encode[CFG_ENCODE_NUM_MAX]; if ((argc != 2) && (argc < 13)) { printf("Amlogic Encoder API \n"); printf(" usage: aml_enc_test " "[srcfile][outfile][width][height][gop][framerate][bitrate][num][fmt][" "buf type][num_planes][codec_id] [cfg_opt] [roifile] [cfg_file] \n"); printf(" options \t:\n"); printf(" srcfile \t: yuv data url in your root fs\n"); printf(" outfile \t: stream url in your root fs\n"); printf(" width \t: width\n"); printf(" height \t: height\n"); printf(" gop \t: I frame refresh interval\n"); printf(" framerate\t: framerate \n"); printf(" bitrate \t: bit rate \n"); printf(" num \t: encode frame count \n"); printf(" fmt \t: encode input fmt 1 : nv12, 2 : nv21, 3 : yuv420p(yv12/yu12), 5 : RGB888, 6 : RGBA8888\n"); printf(" buf_type \t: 0:vmalloc, 3:dma buffer\n"); printf(" num_planes \t: used for dma buffer case. 2 : nv12/nv21, 3 : yuv420p(yv12/yu12)\n"); printf(" codec_id \t: 4 : h.264, 5 : h.265\n"); printf(" cfg_opt \t: optional, flags to control the encode feature settings\n"); printf(" \t\t bit 0:roi control. 0: disabled (default) 1: enabled\n"); printf(" \t\t bit 1: update control. 0: disabled (default) 1: enabled\n"); printf(" \t\t bit 2 ~ bit 6 encode GOP patttern options:\n"); printf(" \t\t\t 0 default(IP) 1:I only 2:IP_only 3: IBBBP\n"); printf(" \t\t\t 4:IP_SVC1 5:IP_SVC2 6: IP_SVC3 7: IP_SVC4 8: CUSTP 10: IP_SVC5\n"); printf(" \t\t bit 7:long term refereces. 0: disabled (default) 1: enabled\n"); printf(" \t\t bit 8:cust source buffer stride . 0: disabled (default) 1: enabled\n"); printf(" \t\t bit 9: convert normal source file stride to cust source stride. 0: disabled (default) 1: enabled\n"); printf(" \t\t bit 10:enable intraRefresh. 0: disabled (default) 1: enabled\n"); printf(" \t\t bit 11 ~ bit 13: profile 0: auto (default) others as following\n"); printf(" \t\t 1: BASELINE(AVC) MAIN (HEVC); 2 MAIN (AVC) main10 (HEVC) \n"); printf(" \t\t 3: HIGH (AVC) STILLPICTURE (HEVC); 4 HIGH10 (AVC) \n"); printf(" \t\t bit 14 ~ bit 15: frame rotation(counter-clockwise, in degree): 0:none, 1:90, 2:180, 3:270\n"); printf(" \t\t bit 16 ~ bit 17: frame mirroring: 0:none, 1:vertical, 2:horizontal, 3:both V and H\n"); printf(" \t\t bit 18: enable customer bitstream buffer size\n"); printf(" \t\t bit 19 ~ bit 20: enable multi slice mode: 0, one slice(no para), 1 by MB(16x16)/CTU(64x64), 2 by CTU + size(byte)\n"); printf(" \t\t bit 21 cust_qp_delta_en \n"); printf(" \t\t bit 22 strict_rate_ctrl_en (skipP allowed to limit bitrate) default 0 \n"); printf(" \t\t bit 23 forcePicQpEnable default 0 \n"); printf(" \t\t bit 24 ~ 26:SrcBitDepth setting/internal setting as following\n"); printf(" \t\t 1:src yuv is 8bit,internal encode 8bit ; 2:src yuv is 8bit,internal encode 10bit \n"); printf(" \t\t 3:internal encode 10bit,internal encode 8bit ; 4:internal encode 10bit,internal encode 10bit \n"); printf(" roifile \t: optional, roi info url in root fs, no present if roi is disabled\n"); printf(" cfg_file \t: optional, cfg update info url. no present if update is disabled\n"); printf(" src_buf_stride \t: optional, source buffer stride\n"); printf(" intraRefresh \t: optional, lower 4 bits for intra-refresh mode; others for refresh parameter, controlled by intraRefresh bit\n"); printf(" bitStreamBuf \t: optional, encoded bitstream buffer size in MB\n"); printf(" MultiSlice parameter \t: optional, multi slice parameter in MB/CTU or CTU and size (HIGH_16bits size + LOW_16bit CTU) combined\n"); printf(" cust_qp_delta \t: optional, cust_qp_delta apply to P frames value >0 lower; <0 increase the quality \n"); printf(" strict rate control parmeter \t: optional, High 16 bit bitrate window (frames max 120). low 16 bit bitrate threshold (percent)\n"); printf(" forcePicQpIPB: force qp value\n"); printf("or\n"); printf(" usage: aml_enc_test " "[encode_cfg_file]\n"); printf(" options \t:\n"); printf(" encode_cfg_file \t: encode cfg file info url\n"); return -1; } else if (argc == 2) { printf("argv[1]:%s\n", argv[1]); fp_cfg_encode = fopen((argv[1]), "r"); if (fp_cfg_encode == NULL) { printf("open encode cfg file error!\n"); return 0; } memset(cfg_encode, 0, CFG_ENCODE_NUM_MAX * sizeof(encodecCfgParam)); ParseEncodeCfgFile(fp_cfg_encode, cfg_encode); fclose(fp_cfg_encode); if (cfg_encode[0].seqNum == 0) return 0; } else { memset(&cfg_encode[0], 0, sizeof(encodecCfgParam)); if (strlen(argv[1]) >= ENCODER_STRING_LEN_MAX) { printf("Warning: Input filename too long (max length: %d), will be truncated\n", ENCODER_STRING_LEN_MAX - 1); } snprintf(cfg_encode[0].srcfile, ENCODER_STRING_LEN_MAX, "%s", argv[1]); if (strlen(argv[2]) >= ENCODER_STRING_LEN_MAX) { printf("Warning: Output filename too long (max length: %d), will be truncated\n", ENCODER_STRING_LEN_MAX - 1); } snprintf(cfg_encode[0].outfile, ENCODER_STRING_LEN_MAX, "%s", argv[2]); cfg_encode[0].width = atoi(argv[3]); cfg_encode[0].height = atoi(argv[4]); cfg_encode[0].gop = atoi(argv[5]); cfg_encode[0].framerate = atoi(argv[6]); cfg_encode[0].bitrate = atoi(argv[7]); cfg_encode[0].num = atoi(argv[8]); cfg_encode[0].fmt = atoi(argv[9]); cfg_encode[0].buf_type = atoi(argv[10]); cfg_encode[0].num_planes = atoi(argv[11]); cfg_encode[0].codec_id = atoi(argv[12]); if (argc > 13) { cfg_encode[0].cfg_opt = atoi(argv[13]); } if (argc > 14) { arg_count = 14; if ((cfg_encode[0].cfg_opt & 0x1) == 0x1 && argc > arg_count) { if (strlen(argv[arg_count]) >= ENCODER_STRING_LEN_MAX) { printf("Warning: roi filename too long (max length: %d), will be truncated\n", ENCODER_STRING_LEN_MAX - 1); } snprintf(cfg_encode[0].roifile, ENCODER_STRING_LEN_MAX, "%s", argv[arg_count]); printf("roi_url is\t: %s ;\n", cfg_encode[0].roifile); arg_count ++; } if ((cfg_encode[0].cfg_opt & 0x2) == 0x2 && argc > arg_count) { if (strlen(argv[arg_count]) >= ENCODER_STRING_LEN_MAX) { printf("Warning: config filename too long (max length: %d), will be truncated\n", ENCODER_STRING_LEN_MAX - 1); } snprintf(cfg_encode[0].cfg_file, ENCODER_STRING_LEN_MAX, "%s", argv[arg_count]); printf("cfg_upd_url is\t: %s ;\n", cfg_encode[0].cfg_file); arg_count ++; } if ((cfg_encode[0].cfg_opt & 0x100) == 0x100 && argc > arg_count) { cfg_encode[0].src_buf_stride = atoi(argv[arg_count]); arg_count ++; } if ((cfg_encode[0].cfg_opt & 0x400) == 0x400 && argc > arg_count) { cfg_encode[0].intraRefresh = atoi(argv[arg_count]); printf("IntraRefresh mode %d, arg %d;\n", (cfg_encode[0].intraRefresh & 0xf), (cfg_encode[0].intraRefresh >> 4)); arg_count ++; } if ((cfg_encode[0].cfg_opt & 0x40000) == 0x40000 && argc > arg_count) { cfg_encode[0].bitStreamBuf = atoi(argv[arg_count]); printf("bitstream_buf_size: %d \n", cfg_encode[0].bitStreamBuf); arg_count ++; } if ((cfg_encode[0].cfg_opt >> 19) & 0x3 && argc > arg_count) { cfg_encode[0].MultiSlice_parameter = atoi(argv[arg_count]); printf("Multi-slices para %d \n", cfg_encode[0].MultiSlice_parameter); arg_count ++; } if ((cfg_encode[0].cfg_opt >> 21) & 0x1 && argc > arg_count) { cfg_encode[0].cust_qp_delta = atoi(argv[arg_count]); printf("cust_qp_delta %d \n", cfg_encode[0].cust_qp_delta); arg_count ++; } if ((cfg_encode[0].cfg_opt >> 22) & 0x1 && argc > arg_count) { cfg_encode[0].strict_rate_control_parmeter = atoi(argv[arg_count]); printf("strict_rate_control_parmeter %d\n", cfg_encode[0].strict_rate_control_parmeter); printf("bit-rate window %d frames, skip_threshold %d\n", cfg_encode[0].strict_rate_control_parmeter>>16, cfg_encode[0].strict_rate_control_parmeter& 0xffff); arg_count ++; } if ((cfg_encode[0].cfg_opt >> 23) & 0x1 && argc > arg_count) { cfg_encode[0].forcePicQpIPB = atoi(argv[arg_count]); printf("forcePicQpIPB %d\n", cfg_encode[0].forcePicQpIPB); arg_count ++; } if (arg_count != argc) { printf("config no match conf %d argc %d\n", cfg_encode[0].cfg_opt, argc); return -1; } } cfg_encode[0].seqNum = 1; } pthread_t tid[CFG_ENCODE_NUM_MAX]; for (i = 0; i < cfg_encode[0].seqNum; i ++) { if (i >= CFG_ENCODE_NUM_MAX) break; pthread_create(&tid[i], NULL, encode_thread, &cfg_encode[i]); } for (i = 0; i < cfg_encode[0].seqNum; i ++) { if (i >= CFG_ENCODE_NUM_MAX) break; pthread_join(tid[i], NULL); } return 0; } static void *encode_thread(void *param) { int width, height, gop, framerate, bitrate, num, my_qp; int ret = 0; int outfd = -1; FILE *fp = NULL; FILE *fp_roi = NULL; FILE *fp_cfg = NULL; int datalen = 0; int retry_cnt = 0; int roi_enabled = 0; int ltf_enabled = 0; int cfg_upd_enabled = 0, has_cfg_update = 0; int cfg_option = 0, frame_num = 0, gop_pattern = 0, profile = 0; int src_buf_stride = 0, conver_stride = 0, intra_refresh_comb = 0; int bitstream_buf_size = 0; int multi_slice = 0, multi_slice_para = 0; int cust_qp_delta = 0, cust_qp_delta_en = 0; int arg_count = 0, arg_roi = 0, arg_upd = 0; int strict_rate_ctrl_en = 0, bitrate_window = 0, skip_threshold = 0; int srcBitDepth = 8,internalBitDepth = 8; int BitDepthParam = 0; vl_img_format_t fmt = IMG_FMT_NONE; CfgChangeParam cfgChange; vl_frame_type_t enc_frame_type; PlayStatInfo playStat; unsigned char *vaddr = NULL; vl_codec_handle_t handle_enc = 0; vl_encode_info_t encode_info; vl_buffer_info_t ret_buf; vl_codec_id_t codec_id = CODEC_ID_H264; uint32_t frame_rotation = 0; uint32_t frame_mirroring = 0; encoding_metadata_t encoding_metadata; int forcePicQpEnable = 0; int forcePicQpIPB = 0; unsigned int framesize; unsigned ysize; unsigned usize; unsigned vsize; unsigned uvsize; int tmp_idr; int fd = -1; qp_param_t qp_tbl; int qp_mode = 0; int buf_type = 0; int num_planes = 1; struct usr_ctx_s ctx; unsigned int frameWriteTime = 0; unsigned long long totalTime = 0, ioTime = 0; struct timeval timeStart, timeEnd, readStart, readEnd, writeStart, writeEnd; unsigned char srcfile[ENCODER_STRING_LEN_MAX] = {0}; unsigned char outfile[ENCODER_STRING_LEN_MAX] = {0}; unsigned char roifile[ENCODER_STRING_LEN_MAX] = {0}; unsigned char cfg_file[ENCODER_STRING_LEN_MAX] = {0}; encodecCfgParam *cfg_encode = NULL; unsigned char *inputBuffer = NULL; unsigned char *input[3] = { NULL }; vl_buffer_info_t inbuf_info; vl_dma_info_t *dma_info = NULL; unsigned char *roi_buffer = NULL; unsigned int roi_size; unsigned char *outbuffer = NULL; unsigned int outbuffer_len; memset(&inbuf_info, 0, sizeof(vl_buffer_info_t)); pthread_detach(pthread_self()); if (NULL != param) cfg_encode = (encodecCfgParam *)param; else { goto end; } width = cfg_encode->width; if ((width < 1) || (width > WIDTH)) { printf("invalid width \n"); goto end; } height = cfg_encode->height; if ((height < 1) || (height > HEIGHT)) { printf("invalid height \n"); goto end; } gop = cfg_encode->gop; framerate = cfg_encode->framerate; bitrate = cfg_encode->bitrate; num = cfg_encode->num; fmt = cfg_encode->fmt; buf_type = cfg_encode->buf_type; num_planes = cfg_encode->num_planes; switch (cfg_encode->codec_id) { case 0: codec_id = CODEC_ID_NONE; break; case 1: codec_id = CODEC_ID_H261; break; case 2: codec_id = CODEC_ID_H261; break; case 3: codec_id = CODEC_ID_H263; break; case 4: codec_id = CODEC_ID_H264; break; case 5: codec_id = CODEC_ID_H265; break; default: break; } if (codec_id != CODEC_ID_H264 && codec_id != CODEC_ID_H265) { printf("unsupported codec id %d \n", codec_id); goto end; } if ((framerate < 0) || (framerate > FRAMERATE)) { printf("invalid framerate %d \n", framerate); goto end; } if (bitrate <= 0) { printf("invalid bitrate \n"); goto end; } if (num < 0) { printf("invalid num \n"); goto end; } if (buf_type == 3 && (num_planes < 1 || num_planes > 3)) { printf("invalid num_planes \n"); goto end; } printf("src_url is\t: %s ;\n", cfg_encode->srcfile); printf("out_url is\t: %s ;\n", cfg_encode->outfile); printf("width is\t: %d ;\n", width); printf("height is\t: %d ;\n", height); printf("gop is\t: %d ;\n", gop); printf("frmrate is\t: %d ;\n", framerate); printf("bitrate is\t: %d ;\n", bitrate); printf("frm_num is\t: %d ;\n", num); printf("fmt is\t: %d ;\n", fmt); printf("buf_type is\t: %d ;\n", buf_type); printf("num_planes is\t: %d ;\n", num_planes); printf("codec is\t: %d ;\n", codec_id); if (codec_id == CODEC_ID_H265) printf("codec is H265\n"); else printf("codec is H264\n"); if (cfg_encode->cfg_opt > 0) { cfg_option = cfg_encode->cfg_opt; gop_pattern = (cfg_option >> 2) & 0x1f; frame_rotation = (cfg_option >> 14) & 0x03; frame_mirroring = (cfg_option >> 16) & 0x03; printf("frame_rotation=%u, frame_mirroring=%u\n", frame_rotation, frame_mirroring); if (gop_pattern) { if (gop_pattern > 10) { printf("gop_pattern_is: %d invalid;\n", gop_pattern); goto end; } printf("gop_pattern_is \t: %d --> %s;\n", gop_pattern, Gop_string[gop_pattern]); } profile = (cfg_option >>11) & 0x7; if (profile) { if ((profile > 3 && codec_id == CODEC_ID_H265) || profile > 4) { printf("profile: %d invalid;\n", profile); goto end; } printf("profile is \t: %d\n", profile); } if (cfg_option & 0x80) { printf("longterm reference is enabled\n"); ltf_enabled = 1; } multi_slice = (cfg_option >> 19) & 0x3; if (multi_slice) { printf("multi slice is enabled mode: %d\n", multi_slice); } cust_qp_delta_en = (cfg_option >> 21) & 0x1; if (cust_qp_delta_en) { printf("cust qp delta enabled: %d\n", cust_qp_delta_en); } strict_rate_ctrl_en = (cfg_option >> 22) & 0x1; if (strict_rate_ctrl_en) { printf("strict bitrate control enabled: %d\n", strict_rate_ctrl_en); } forcePicQpEnable = (cfg_option >> 23) & 0x1; if (forcePicQpEnable) { printf("forcePicQpEnable: %d\n", forcePicQpEnable); } if ((cfg_option & 0x1) == 0x1 && (strlen(cfg_encode->roifile) > 1)) { printf("roi_url is\t: %s ;\n", cfg_encode->roifile); roi_enabled = 1; if (strlen(cfg_encode->roifile) >= ENCODER_STRING_LEN_MAX) { printf("Warning: roi filename too long (max length: %d), will be truncated\n", ENCODER_STRING_LEN_MAX - 1); } snprintf(roifile, ENCODER_STRING_LEN_MAX, "%s", cfg_encode->roifile); } if ((cfg_option & 0x2) == 0x2 && (strlen(cfg_encode->cfg_file) > 1)) { printf("cfg_upd_url is\t: %s ;\n", cfg_encode->cfg_file); cfg_upd_enabled = 1; if (strlen(cfg_encode->cfg_file) >= ENCODER_STRING_LEN_MAX) { printf("Warning: config filename too long (max length: %d), will be truncated\n", ENCODER_STRING_LEN_MAX - 1); } snprintf(cfg_file, ENCODER_STRING_LEN_MAX, "%s", cfg_encode->cfg_file); } if ((cfg_option & 0x100) == 0x100 && cfg_encode->src_buf_stride > 0) { src_buf_stride = cfg_encode->src_buf_stride; conver_stride = (cfg_option & 0x200) >> 9; printf("cust stride is\t: %d convert: %d;\n", src_buf_stride, conver_stride); } if ((cfg_option & 0x400) == 0x400 && cfg_encode->intraRefresh > 0) { intra_refresh_comb = cfg_encode->intraRefresh; printf("IntraRefresh mode %d, arg %d;\n", (intra_refresh_comb & 0xf), (intra_refresh_comb >> 4)); } if ((cfg_option & 0x40000) == 0x40000 && cfg_encode->bitStreamBuf > 0) { bitstream_buf_size = cfg_encode->bitStreamBuf; printf("bitstream_buf_size: %d \n", bitstream_buf_size); } if (multi_slice && cfg_encode->MultiSlice_parameter > 0) { multi_slice_para = cfg_encode->MultiSlice_parameter; printf("Multi-slices para %d \n", multi_slice_para); } if (cust_qp_delta_en && cfg_encode->cust_qp_delta > 0) { cust_qp_delta = cfg_encode->cust_qp_delta; printf("cust_qp_delta %d \n", cust_qp_delta); } if (strict_rate_ctrl_en && cfg_encode->strict_rate_control_parmeter > 0) { bitrate_window = cfg_encode->strict_rate_control_parmeter; skip_threshold = bitrate_window & 0xffff; bitrate_window >>=16; printf("bit-rate window %d frames, skip_threshold %d\n", bitrate_window, skip_threshold); } if (forcePicQpEnable && cfg_encode->forcePicQpIPB > 0) { forcePicQpIPB = cfg_encode->forcePicQpIPB; printf("forcePicQpIPB %d \n", forcePicQpIPB); } BitDepthParam = (cfg_option >> 24) & 0xf; switch (BitDepthParam) { case 1: srcBitDepth = 8; internalBitDepth = 8; break; case 2: srcBitDepth = 8; internalBitDepth = 10; break; case 3: srcBitDepth = 10; internalBitDepth = 8; break; case 4: srcBitDepth = 10; internalBitDepth = 10; break; default: break; } } printf("srcBitDepth = %d\n",srcBitDepth); printf("internalBitDepth = %d\n",internalBitDepth); if (src_buf_stride && src_buf_stride < width) { printf("incorrect cust source stride %d width %d", src_buf_stride, width); goto end; } if (strlen(cfg_encode->srcfile) >= ENCODER_STRING_LEN_MAX) { printf("Warning: Input filename too long (max length: %d), will be truncated\n", ENCODER_STRING_LEN_MAX - 1); } snprintf(srcfile, ENCODER_STRING_LEN_MAX, "%s", cfg_encode->srcfile); if (strlen(cfg_encode->outfile) >= ENCODER_STRING_LEN_MAX) { printf("Warning: output filename too long (max length: %d), will be truncated\n", ENCODER_STRING_LEN_MAX - 1); } snprintf(outfile, ENCODER_STRING_LEN_MAX, "%s", cfg_encode->outfile); outbuffer_len = 8 * 1024 * 1024 * sizeof(char); if (bitstream_buf_size > 8) outbuffer_len = bitstream_buf_size*1024 * 1024 * sizeof(char); outbuffer = (unsigned char *) malloc(outbuffer_len); if (outbuffer == NULL) { printf("Can not malloc outbuffer\n"); goto end; } if (src_buf_stride) { if (fmt == IMG_FMT_RGB888) { framesize = src_buf_stride * height * 3; } else if (fmt == IMG_FMT_RGBA8888) { framesize = src_buf_stride * height * 4; } else { framesize = src_buf_stride * height * 3 / 2; } ysize = src_buf_stride * height; usize = src_buf_stride * height / 4; vsize = src_buf_stride * height / 4; uvsize = src_buf_stride * height / 2; } else { if (fmt == IMG_FMT_RGB888) { framesize = width * height * 3; } else if (fmt == IMG_FMT_RGBA8888) { framesize = width * height * 4; } else { framesize = width * height * 3 / 2; } ysize = width * height; usize = width * height / 4; vsize = width * height / 4; uvsize = width * height / 2; } if (srcBitDepth == 10) { framesize = width * height * 3; ysize = width * height * 2; usize = width * height / 2; vsize = width * height / 2; uvsize = width * height; } inbuf_info.buf_type = (vl_buffer_type_t) buf_type; inbuf_info.buf_stride = src_buf_stride; inbuf_info.buf_fmt = fmt; memset(&qp_tbl, 0, sizeof(qp_param_t)); memset(&playStat, 0, sizeof(PlayStatInfo)); qp_tbl.qp_min = 0; qp_tbl.qp_max = 51; qp_tbl.qp_I_base = 30; qp_tbl.qp_I_min = 0; qp_tbl.qp_I_max = 51; qp_tbl.qp_P_base = 30; qp_tbl.qp_P_min = 0; qp_tbl.qp_P_max = 51; memset(&encode_info, 0, sizeof(vl_encode_info_t)); encode_info.width = width; encode_info.height = height; encode_info.bit_rate = bitrate; encode_info.frame_rate = framerate; encode_info.gop = gop; encode_info.img_format = fmt; encode_info.qp_mode = qp_mode; encode_info.profile = profile; encode_info.forcePicQpEnable = forcePicQpEnable; encode_info.forcePicQpI = forcePicQpIPB; encode_info.forcePicQpB = forcePicQpIPB; encode_info.forcePicQpP = forcePicQpIPB; encode_info.srcBitDepth = srcBitDepth; encode_info.internalBitDepth = internalBitDepth; if (frame_rotation == 0) encode_info.frame_rotation = 0; else if (frame_rotation == 1) encode_info.frame_rotation = 90; else if (frame_rotation == 2) encode_info.frame_rotation = 180; else if (frame_rotation == 3) encode_info.frame_rotation = 270; else { printf("frame rotation angle is wrong\n"); encode_info.frame_rotation = 0; } if (frame_mirroring <=3) encode_info.frame_mirroring = frame_mirroring; else encode_info.frame_mirroring = 0; if (intra_refresh_comb) { encode_info.intra_refresh_mode = intra_refresh_comb & 0xf; encode_info.intra_refresh_arg = intra_refresh_comb >> 4; } if (roi_enabled) { encode_info.enc_feature_opts |= ENABLE_ROI_FEATURE; roi_size = ((width+15)/16)*((height+15)/16); roi_buffer = (unsigned char *) malloc(roi_size + 1); if (roi_buffer == NULL) { printf("Can not allocat roi_buffer\n"); goto end; } } if (cfg_upd_enabled) { encode_info.enc_feature_opts |= ENABLE_PARA_UPDATE; memset(&cfgChange, 0, sizeof(CfgChangeParam)); } if (ltf_enabled) { encode_info.enc_feature_opts |= ENABLE_LONG_TERM_REF; } if (gop_pattern) { encode_info.enc_feature_opts |= ((gop_pattern<<2) & 0x7c); } if (bitstream_buf_size) { encode_info.bitstream_buf_sz = bitstream_buf_size; } if (multi_slice) { encode_info.multi_slice_mode = multi_slice; encode_info.multi_slice_arg = multi_slice_para; } if (cust_qp_delta) { encode_info.cust_gop_qp_delta = cust_qp_delta; } if (strict_rate_ctrl_en) { encode_info.strict_rc_window = bitrate_window; encode_info.strict_rc_skip_thresh = skip_threshold; } if (inbuf_info.buf_type == DMA_TYPE) { dma_info = &(inbuf_info.buf_info.dma_info); dma_info->num_planes = num_planes; dma_info->shared_fd[0] = -1; // dma buf fd dma_info->shared_fd[1] = -1; dma_info->shared_fd[2] = -1; ret = create_ctx(&ctx); if (ret < 0) { printf("gdc create fail ret=%d\n", ret); goto end; } if (dma_info->num_planes == 3) { if (fmt != IMG_FMT_YUV420P) { printf("error fmt %d\n", fmt); goto exit; } ret = alloc_dma_buffer(&ctx, INPUT_BUFF_TYPE, ysize); if (ret < 0) { printf("alloc fail ret=%d, len=%u\n", ret, ysize); goto exit; } vaddr = (unsigned char *) ctx.i_buff[0]; if (!vaddr) { printf("mmap failed,Not enough memory\n"); goto exit; } printf("hoan debug canvas >> alloc_dma_buffer y,ret=%d, vaddr=%p\n", ret,vaddr); dma_info->shared_fd[0] = ret; input[0] = vaddr; ret = alloc_dma_buffer(&ctx, INPUT_BUFF_TYPE, usize); if (ret < 0) { printf("alloc fail ret=%d, len=%u\n", ret, usize); goto exit; } vaddr = (unsigned char *) ctx.i_buff[1]; if (!vaddr) { printf("mmap failed,Not enough memory\n"); goto exit; } dma_info->shared_fd[1] = ret; input[1] = vaddr; printf("hoan debug canvas >> alloc_dma_buffer u,ret=%d, vaddr=%p\n", ret,vaddr); ret = alloc_dma_buffer(&ctx, INPUT_BUFF_TYPE, vsize); if (ret < 0) { printf("alloc fail ret=%d, len=%u\n", ret, vsize); goto exit; } vaddr = (unsigned char *) ctx.i_buff[2]; if (!vaddr) { printf("mmap failed,Not enough memory\n"); goto exit; } dma_info->shared_fd[2] = ret; input[2] = vaddr; printf("hoan debug canvas >> alloc_dma_buffer v,ret=%d, vaddr=%p\n", ret,vaddr); } else if (dma_info->num_planes == 2) { ret = alloc_dma_buffer(&ctx, INPUT_BUFF_TYPE, ysize); if (ret < 0) { printf("alloc fail ret=%d, len=%u\n", ret, ysize); goto exit; } vaddr = (unsigned char *) ctx.i_buff[0]; if (!vaddr) { printf("mmap failed,Not enough memory\n"); goto exit; } dma_info->shared_fd[0] = ret; input[0] = vaddr; if (fmt != IMG_FMT_NV12 && fmt != IMG_FMT_NV21) { printf("error fmt %d\n", fmt); goto exit; } ret = alloc_dma_buffer(&ctx, INPUT_BUFF_TYPE, uvsize); if (ret < 0) { printf("alloc fail ret=%d, len=%u\n", ret, uvsize); goto exit; } vaddr = (unsigned char *) ctx.i_buff[1]; if (!vaddr) { printf("mmap failed,Not enough memory\n"); goto exit; } dma_info->shared_fd[1] = ret; input[1] = vaddr; } else if (dma_info->num_planes == 1) { ret = alloc_dma_buffer(&ctx, INPUT_BUFF_TYPE, framesize); if (ret < 0) { printf("alloc fail ret=%d, len=%u\n", ret, framesize); goto exit; } vaddr = (unsigned char *) ctx.i_buff[0]; if (!vaddr) { printf("mmap failed,Not enough memory\n"); goto exit; } dma_info->shared_fd[0] = ret; input[0] = vaddr; } printf("in[0] %d, in[1] %d, in[2] %d\n", dma_info->shared_fd[0], dma_info->shared_fd[1], dma_info->shared_fd[2]); printf("input[0] %p, input[1] %p,input[2] %p\n", input[0], input[1], input[2]); } else { inputBuffer = (unsigned char *) malloc(framesize); if (inputBuffer == NULL) { printf("Can not allocat inputBuffer\n"); goto exit; } inbuf_info.buf_info.in_ptr[0] = (ulong) (inputBuffer); inbuf_info.buf_info.in_ptr[1] = (ulong) (inputBuffer + ysize); inbuf_info.buf_info.in_ptr[2] = (ulong) (inputBuffer + ysize + usize); } fp = fopen((const char*)srcfile, "rb"); if (fp == NULL) { printf("open src file error!\n"); goto exit; } if (roi_enabled) { fp_roi = fopen((const char*)roifile, "rb"); if (fp_roi == NULL) { printf("open roi file error!\n"); goto exit; } } if (cfg_upd_enabled) { fp_cfg = fopen((const char*)cfg_file, "r"); if (fp_cfg == NULL) { printf("open cfg file error!\n"); goto exit; } has_cfg_update = ParseCfgUpdateFile(fp_cfg, &cfgChange); } outfd = open((const char*)outfile, O_CREAT | O_RDWR | O_TRUNC, 0644); if (outfd < 0) { printf("open dest file error!\n"); goto exit; } // init playState playStat.minQpI_base = 8; playStat.minQpP_base = 8; playStat.minQpB_base = 8; playStat.maxQpI_base = 51; playStat.maxQpP_base = 51; playStat.maxQpB_base = 51; playStat.maxDeltaQp_base = 10; playStat.PrevFrameQP = 30; if (encode_info.qp_mode) { playStat.minQpP_base = qp_tbl.qp_P_min; playStat.maxQpP_base = qp_tbl.qp_P_max; playStat.minQpI_base = qp_tbl.qp_I_min; playStat.maxQpI_base = qp_tbl.qp_I_max; playStat.maxDeltaQp_base = qp_tbl.qp_I_max - qp_tbl.qp_I_min; playStat.PrevFrameQP = qp_tbl.qp_I_base; } playStat.CurGOPSize = encode_info.gop; playStat.qp_ltr = -1; playStat.qp_idr = -1; retry: handle_enc = vl_multi_encoder_init(codec_id, encode_info, &qp_tbl); if (handle_enc == 0) { if ( retry_cnt >= INIT_RETRY) { printf("Init encoder retry timeout\n"); goto exit; } else { printf("Init encoder fail retrying \n"); retry_cnt ++; usleep(100*1000); goto retry; } } gettimeofday(&timeStart, NULL); while (num > 0) { gettimeofday(&readStart, NULL); if (conver_stride) { // exmaple of covert normal with stride to cust stride int line; unsigned char *dst; /* y */ if (inbuf_info.buf_type == DMA_TYPE) dst = input[0]; else dst = inputBuffer; memset(dst, 0, ysize); for (line = 0; line < height; line++) { if (fread(dst, 1, width, fp) != width) { printf("read input file error!\n"); goto exit; } dst += src_buf_stride; } if (inbuf_info.buf_type == DMA_TYPE && dma_info->num_planes > 1) dst = input[1]; if (fmt != IMG_FMT_YUV420P) { //nv12 or nv21 memset(dst, 0, uvsize); for (line = 0; line < height / 2; line++) { if (fread(dst, 1, width, fp) != width) { printf("read input file error!\n"); goto exit; } dst += src_buf_stride; } } else { // YUV420p seperated memset(dst, 0, usize); for (line = 0; line < height / 2; line++) { if (fread(dst, 1, width / 2, fp) != width / 2) { printf("read input file error!\n"); goto exit; } dst += (src_buf_stride / 2); } if (inbuf_info.buf_type == DMA_TYPE && dma_info->num_planes ==3) dst = input[2]; memset(dst, 0, vsize); for (line = 0; line < height / 2; line++) { if (fread(dst, 1, width / 2, fp) != width / 2) { printf("read input file error!\n"); goto exit; } dst += (src_buf_stride / 2); } } } else if (inbuf_info.buf_type == DMA_TYPE) { // read data to dma buf vaddr if (dma_info->num_planes == 1) { if (fread(input[0], 1, framesize, fp) != framesize) { printf("read input file error!\n"); goto exit; } } else if (dma_info->num_planes == 2) { if (fread(input[0], 1, ysize, fp) != ysize) { printf("read input file error!\n"); goto exit; } if (fread(input[1], 1, uvsize, fp) != uvsize) { printf("read input file error!\n"); goto exit; } } else if (dma_info->num_planes == 3) { if (fread(input[0], 1, ysize, fp) != ysize) { printf("read input file error!\n"); goto exit; } if (fread(input[1], 1, usize, fp) != usize) { printf("read input file error!\n"); goto exit; } if (fread(input[2], 1, vsize, fp) != vsize) { printf("read input file error!\n"); goto exit; } } } else { // read data to vmalloc buf vaddr if (fread(inputBuffer, 1, framesize, fp) != framesize) { printf("read input file error!\n"); goto exit; } } memset(outbuffer, 0, outbuffer_len); if (inbuf_info.buf_type == DMA_TYPE) { sync_cpu(&ctx); } if (roi_enabled) { if (fread(roi_buffer, 1, roi_size, fp_roi) != roi_size) { fseek(fp_roi, 0, SEEK_SET); //back to start if (fread(roi_buffer, 1, roi_size, fp_roi) != roi_size) { printf("read roi file error!\n"); goto exit; } } if (vl_video_encoder_update_qp_hint(handle_enc, roi_buffer, roi_size) != 0) { printf("update qp hint failed.\n"); } } enc_frame_type = FRAME_TYPE_AUTO; gettimeofday(&readEnd, NULL); ioTime += uTimeDiff(readEnd, readStart); gettimeofday(&cfg_encode->timeFrameStart, NULL); if (cfg_upd_enabled && has_cfg_update) { if (cfgChange.FrameNum == frame_num) { // apply updates if (cfgChange.enable_option & FORCE_PICTURE_TYPE) { if (cfgChange.picType == FRAME_TYPE_IDR || cfgChange.picType == FRAME_TYPE_I) { enc_frame_type = FRAME_TYPE_I; printf("force I frame on %d \n", frame_num); } } if (cfgChange.enable_option & FORCE_PICTURE_SKIP) { vl_video_encoder_skip_frame(handle_enc); printf("force skip frame on %d \n", frame_num); } if (cfgChange.enable_option & CHANGE_TARGET_RATE) { vl_video_encoder_change_bitrate( handle_enc, cfgChange.bitRate); printf("Change bitrate to %d on %d \n", cfgChange.bitRate, frame_num); } if (cfgChange.enable_option & CHANGE_MIN_MAX_QP) { vl_video_encoder_change_qp( handle_enc, cfgChange.minQpI,cfgChange.maxQpI, cfgChange.maxDeltaQp, cfgChange.minQpP,cfgChange.maxQpP, cfgChange.minQpB,cfgChange.maxQpB); printf("Change QP table on %d QPs: ", frame_num); printf("%d %d %d %d %d %d %d\n", cfgChange.minQpI,cfgChange.maxQpI, cfgChange.maxDeltaQp, cfgChange.minQpP,cfgChange.maxQpP, cfgChange.minQpB,cfgChange.maxQpB); playStat.minQpI_base = cfgChange.minQpI; playStat.maxQpI_base = cfgChange.maxQpI; playStat.maxDeltaQp_base = cfgChange.maxDeltaQp; playStat.minQpP_base = cfgChange.minQpP; playStat.maxQpP_base = cfgChange.maxQpP; playStat.minQpB_base = cfgChange.minQpB; playStat.maxQpB_base = cfgChange.maxQpB; } if (cfgChange.enable_option & CHANGE_GOP_PERIOD) { vl_video_encoder_change_gop( handle_enc, cfgChange.intraQP, cfgChange.GOPPeriod); printf("Change gop on %d QP %d %d\n", frame_num, cfgChange.intraQP, cfgChange.GOPPeriod); playStat.CurGOPSize = cfgChange.GOPPeriod; if (playStat.CurGOPCounter >= playStat.CurGOPSize) playStat.CurGOPCounter = 0; } if ((cfgChange.enable_option & LONGTERM_REF_SET) && ltf_enabled) { vl_video_encoder_longterm_ref( handle_enc, cfgChange.LTRFlags); printf("Longterm Ref on %d flag 0x%x\n", frame_num, cfgChange.LTRFlags); } if (cfgChange.enable_option & CUST_SIZE_SMOOTH) { if (cfgChange.smooth_level) { playStat.cust_size_smooth_enable = 1; if (cfgChange.smooth_level< 100) playStat.smooth_qpdelta = 4; else playStat.smooth_qpdelta = 6; } else { playStat.cust_size_smooth_enable = 0; playStat.smooth_qpdelta = 0; playStat.qp_ltr = -1; playStat.qp_idr = -1; } printf("Set CustSizeSmooth on %d level %d \n", frame_num, cfgChange.smooth_level); } if ((cfgChange.enable_option & CUST_APP_H264_PLUS) && ltf_enabled) { if (cfgChange.newIDRIntervel) { //enable if (playStat.cust_ltr_enable == 0) { playStat.cust_ltr_enable = 1; playStat.GopLTRIntervel = playStat.CurGOPSize; playStat.GopLTRCounter = playStat.CurGOPCounter; } if (playStat.CurGOPSize != cfgChange.newIDRIntervel) { vl_video_encoder_change_gop( handle_enc, playStat.PrevFrameQP, cfgChange.newIDRIntervel ); printf("Gop change on %d to %d qp %d\n", frame_num, cfgChange.newIDRIntervel, playStat.PrevFrameQP); } playStat.CurGOPSize = cfgChange.newIDRIntervel; playStat.QPdelta_IDR = cfgChange.QPdelta_IDR; playStat.QPdelta_LTR = cfgChange.QPdelta_LTR; if ( playStat.CurGOPCounter >= playStat.CurGOPSize) playStat.CurGOPCounter = 0; if (playStat.GopLTRCounter >= playStat.GopLTRIntervel) playStat.GopLTRCounter = 0; } else { if (playStat.cust_ltr_enable == 1) { playStat.cust_ltr_enable = 0; playStat.CurGOPSize = playStat.GopLTRIntervel; vl_video_encoder_change_gop( handle_enc, playStat.PrevFrameQP, playStat.CurGOPSize); printf("Gop change on %d to %d qp %d\n", frame_num, playStat.CurGOPSize, playStat.PrevFrameQP); if (playStat.CurGOPCounter >= playStat.CurGOPSize) playStat.CurGOPCounter = 0; playStat.GopLTRIntervel = 0; playStat.GopLTRCounter = 0; playStat.QPdelta_IDR = 0; playStat.QPdelta_LTR = 0; playStat.qp_ltr = -1; playStat.qp_idr = -1; } } printf("Set Cust APP H264+ on %d interval %d qp delta idr %d, ltr %d \n", frame_num, cfgChange.newIDRIntervel, cfgChange.QPdelta_IDR, cfgChange.QPdelta_LTR); } if (cfgChange.enable_option & CHANGE_MULTI_SLICE) { vl_video_encoder_change_multi_slice( handle_enc, cfgChange.Slice_Mode, cfgChange.Slice_Para); printf("Change slice on %d mode %d %d\n", frame_num, cfgChange.Slice_Mode, cfgChange.Slice_Para); } if (cfgChange.enable_option & CHANGE_STRICT_RC) { vl_video_encoder_change_strict_rc( handle_enc, cfgChange.bitrate_window, cfgChange.pskip_threshold); printf("Change strict rate control on %d window %d threshold %d\n", frame_num, cfgChange.bitrate_window, cfgChange.pskip_threshold); } if (cfgChange.enable_option & CHANGE_FRAME_RATE) { vl_video_encoder_change_framerate( handle_enc, cfgChange.frameRate, encode_info.bit_rate); printf("Change frameRate to %d on %d \n", cfgChange.frameRate, frame_num); } } if (cfgChange.FrameNum <= frame_num) { has_cfg_update = ParseCfgUpdateFile(fp_cfg, &cfgChange); } } if (playStat.CurGOPCounter >= playStat.CurGOPSize) playStat.CurGOPCounter = 0; if (playStat.cust_ltr_enable) { if (playStat.GopLTRCounter >= playStat.GopLTRIntervel) playStat.GopLTRCounter = 0; } if ( playStat.CurGOPCounter == 0) { // I frame if (playStat.cust_size_smooth_enable) { my_qp = playStat.PrevFrameQP + playStat.smooth_qpdelta; if (my_qp > playStat.maxQpI_base) my_qp = playStat.maxQpI_base; if (playStat.qp_idr < 0) // first-time playStat.qp_idr = my_qp; if ((playStat.qp_idr > my_qp + 5) || (playStat.qp_idr < my_qp - 5)) playStat.qp_idr = my_qp; vl_video_encoder_change_qp( handle_enc, playStat.qp_idr, playStat.maxQpI_base, playStat.maxDeltaQp_base, playStat.qp_idr, playStat.maxQpI_base, playStat.qp_idr, playStat.maxQpI_base); playStat.temp_QP_change = 1; printf("apply QPminI at frame %d min qp %d, my_qp %d \n", frame_num, playStat.qp_idr, my_qp); } else if (playStat.cust_ltr_enable) { my_qp = playStat.PrevFrameQP - playStat.QPdelta_IDR; if (my_qp < playStat.minQpI_base) my_qp =playStat.minQpI_base; if (playStat.qp_idr < 0) // first-time playStat.qp_idr = my_qp; if ((playStat.qp_idr > my_qp + 5) || (playStat.qp_idr < my_qp - 5)) playStat.qp_idr = my_qp; vl_video_encoder_change_qp( handle_enc, playStat.minQpI_base, playStat.qp_idr, playStat.maxDeltaQp_base, playStat.minQpI_base, playStat.qp_idr, playStat.minQpI_base, playStat.qp_idr); printf("apply QPMaxI at frame %d qp %d , my_qp %d \n", frame_num, playStat.qp_idr, my_qp); playStat.temp_QP_change = 1; } if (playStat.cust_ltr_enable) { vl_video_encoder_longterm_ref( handle_enc, 1); // set LTR printf("set ltr at frame %d \n", frame_num); } } else if (playStat.GopLTRCounter == 0 && playStat.cust_ltr_enable) { // LTR P my_qp = playStat.PrevFrameQP - playStat.QPdelta_LTR; if (my_qp < playStat.minQpP_base) my_qp = playStat.minQpP_base; if (playStat.qp_ltr < 0) // first-time playStat.qp_ltr = my_qp; if ((playStat.qp_ltr > my_qp + 5) || (playStat.qp_ltr < my_qp - 5)) playStat.qp_ltr = my_qp; vl_video_encoder_change_qp( handle_enc, playStat.minQpI_base,playStat.maxQpI_base, playStat.maxDeltaQp_base, playStat.minQpP_base, playStat.qp_ltr, playStat.minQpB_base, playStat.maxQpB_base); vl_video_encoder_longterm_ref( handle_enc, 2); // USE LTR printf("apply QPMaxP LTR at frame %d qp %d, my_qp %d \n", frame_num, playStat.qp_ltr, my_qp); playStat.temp_QP_change = 1; } else if(playStat.temp_QP_change == 1) { vl_video_encoder_change_qp( handle_enc, playStat.minQpI_base,playStat.maxQpI_base, playStat.maxDeltaQp_base, playStat.minQpP_base,playStat.maxQpP_base, playStat.minQpB_base, playStat.maxQpB_base); printf("recover QP frame %d to %d, %d, %d P %d, %d, B %d, %d\n", frame_num, playStat.minQpI_base,playStat.maxQpI_base, playStat.maxDeltaQp_base, playStat.minQpP_base,playStat.maxQpP_base, playStat.minQpB_base, playStat.maxQpB_base); playStat.temp_QP_change = 0; } encoding_metadata = vl_multi_encoder_encode(handle_enc, enc_frame_type, outbuffer, &inbuf_info, &ret_buf); gettimeofday(&writeStart, NULL); if (encoding_metadata.is_valid) { write(outfd, (unsigned char *) outbuffer, encoding_metadata.encoded_data_length_in_bytes); } else { printf("encode error %d!\n", encoding_metadata.encoded_data_length_in_bytes); } gettimeofday(&writeEnd, NULL); frameWriteTime = uTimeDiff(writeEnd, writeStart); ioTime += frameWriteTime; gettimeofday(&cfg_encode->timeFrameEnd, NULL); //printf("=== Time(us %u HW+SW) ===\n", //uTimeDiff(cfg_encode->timeFrameEnd, cfg_encode->timeFrameStart) - frameWriteTime); num--; frame_num++; playStat.PrevFrameQP = encoding_metadata.extra.average_qp_value; playStat.CurFrameNum++; playStat.CurGOPCounter++; if (playStat.cust_ltr_enable) { playStat.GopLTRCounter++; } } gettimeofday(&timeEnd, NULL); totalTime = uTimeDiffLL(timeEnd, timeStart); printf("[Total time: %llu us], [IO time: %llu us]\n", totalTime, ioTime); if (frame_num > 1) { printf("[Encode Total time: %llu us], [avgTime: %llu us], [avgfps: %d]\n", totalTime - ioTime, (totalTime - ioTime) / frame_num, (int)(1*1000*1000/((totalTime - ioTime) / frame_num))); } exit: if (handle_enc) vl_multi_encoder_destroy(handle_enc); if (inbuf_info.buf_type == DMA_TYPE) { if (dma_info) { if (dma_info->shared_fd[0] >= 0) close(dma_info->shared_fd[0]); if (dma_info->shared_fd[1] >= 0) close(dma_info->shared_fd[1]); if (dma_info->shared_fd[2] >= 0) close(dma_info->shared_fd[2]); } destroy_ctx(&ctx); } else { if (inputBuffer) free(inputBuffer); } printf("Encode End!width:%d\n",width); end: if (outbuffer != NULL) free(outbuffer); if (roi_buffer != NULL) free(roi_buffer); if (outfd >= 0) close(outfd); if (fp) fclose(fp); if (fp_roi) fclose(fp_roi); if (fp_cfg) fclose(fp_cfg); pthread_exit(0); return NULL; } static int ParseEncodeCfgFile(FILE *fp, encodecCfgParam *cfg_encode) { char* token = NULL; char *delim = " "; static char lineStr[ENCODER_STRING_LEN_MAX*4] = {0}; char valueStr[ENCODER_STRING_LEN_MAX] = {0}; int i = 0; while (1) { if (i >= CFG_ENCODE_NUM_MAX) return 0; if (lineStr[0] == 0x0) { //need a new line; if (fgets(lineStr, ENCODER_STRING_LEN_MAX*4, fp) == NULL) { if (feof(fp)) return 0; continue; } } if (strlen(lineStr) == 1) { // skip null lineStr[0] = 0x0; continue; } if ((lineStr[0] == '#') ||(lineStr[0] == ';')||(lineStr[0] == ':')) { // skip comments lineStr[0] = 0x0; continue; } //printf("[%s][%d]:%s\n",__func__,__LINE__,lineStr); // keep the original memcpy(valueStr, lineStr, strlen(lineStr)); //param is separated by ' ' token = strtok(valueStr, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } snprintf(cfg_encode[i].srcfile, ENCODER_STRING_LEN_MAX, "%s", token); token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } snprintf(cfg_encode[i].outfile, ENCODER_STRING_LEN_MAX, "%s", token); token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } cfg_encode[i].width = atoi(token); token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } cfg_encode[i].height = atoi(token); token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } cfg_encode[i].gop = atoi(token); token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } cfg_encode[i].framerate = atoi(token); token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } cfg_encode[i].bitrate = atoi(token); token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } cfg_encode[i].num = atoi(token); token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } cfg_encode[i].fmt = atoi(token); token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } cfg_encode[i].buf_type = atoi(token); token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } cfg_encode[i].num_planes = atoi(token); token = strtok(NULL, delim); if (token == NULL) { lineStr[0] = 0x0; continue; } cfg_encode[i].codec_id = atoi(token); token = strtok(NULL, delim);//cfg_opt optional if (token == NULL) { i++; cfg_encode[0].seqNum++; lineStr[0] = 0x0; continue; } cfg_encode[i].cfg_opt = atoi(token); token = strtok(NULL, delim); if (token == NULL) { i++; cfg_encode[0].seqNum++; lineStr[0] = 0x0; continue; } if ((cfg_encode[i].cfg_opt & 0x1) == 0x1) { snprintf(cfg_encode[i].roifile, ENCODER_STRING_LEN_MAX, "%s", token); printf("roi_url is\t: %s ;\n", cfg_encode[i].roifile); token = strtok(NULL, delim); if (token == NULL) { i++; cfg_encode[0].seqNum++; lineStr[0] = 0x0; continue; } } if ((cfg_encode[i].cfg_opt & 0x2) == 0x2) { snprintf(cfg_encode[i].cfg_file, ENCODER_STRING_LEN_MAX, "%s", token); printf("cfg_upd_url[%d] is\t: %s ;\n", i, cfg_encode[i].cfg_file); token = strtok(NULL, delim); if (token == NULL) { i++; cfg_encode[0].seqNum++; lineStr[0] = 0x0; continue; } } if ((cfg_encode[i].cfg_opt & 0x100) == 0x100) { cfg_encode[i].src_buf_stride = atoi(token); token = strtok(NULL, delim); if (token == NULL) { i++; cfg_encode[0].seqNum++; lineStr[0] = 0x0; continue; } } if ((cfg_encode[i].cfg_opt & 0x400) == 0x400) { cfg_encode[i].intraRefresh = atoi(token); printf("IntraRefresh mode %d, arg %d;\n", (cfg_encode[i].intraRefresh & 0xf), (cfg_encode[i].intraRefresh >> 4)); token = strtok(NULL, delim); if (token == NULL) { i++; cfg_encode[0].seqNum++; lineStr[0] = 0x0; continue; } } if ((cfg_encode[i].cfg_opt & 0x40000) == 0x40000) { cfg_encode[i].bitStreamBuf = atoi(token); printf("bitstream_buf_size: %d \n", cfg_encode[i].bitStreamBuf); token = strtok(NULL, delim); if (token == NULL) { i++; cfg_encode[0].seqNum++; lineStr[0] = 0x0; continue; } } if ((cfg_encode[i].cfg_opt >> 19) & 0x3) { cfg_encode[i].MultiSlice_parameter = atoi(token); printf("Multi-slices para %d \n", cfg_encode[i].MultiSlice_parameter); token = strtok(NULL, delim); if (token == NULL) { i++; cfg_encode[0].seqNum++; lineStr[0] = 0x0; continue; } } if ((cfg_encode[i].cfg_opt >> 21) & 0x1) { cfg_encode[i].cust_qp_delta = atoi(token); printf("cust_qp_delta %d \n", cfg_encode[i].cust_qp_delta); token = strtok(NULL, delim); if (token == NULL) { i++; cfg_encode[0].seqNum++; lineStr[0] = 0x0; continue; } } if ((cfg_encode[i].cfg_opt >> 22) & 0x1) { cfg_encode[i].strict_rate_control_parmeter = atoi(token); printf("strict_rate_control_parmeter %d\n", cfg_encode[i].strict_rate_control_parmeter); printf("bit-rate window %d frames, skip_threshold %d\n", cfg_encode[i].strict_rate_control_parmeter>>16, cfg_encode[i].strict_rate_control_parmeter& 0xffff); token = strtok(NULL, delim); if (token == NULL) { i++; cfg_encode[0].seqNum++; lineStr[0] = 0x0; continue; } } if ((cfg_encode[i].cfg_opt >> 23) & 0x1) { cfg_encode[i].forcePicQpIPB = atoi(token); printf("forcePicQpIPB %d\n", cfg_encode[i].forcePicQpIPB); } token = strtok(NULL, delim); if (token == NULL) { i++; cfg_encode[0].seqNum++; lineStr[0] = 0x0; continue; } else { //config no match printf("config no match conf %d command: %s\n", cfg_encode[i].cfg_opt, lineStr); lineStr[0] = 0x0; continue; } } return 0; } static int ParseCfgUpdateFile(FILE *fp, CfgChangeParam *cfg_update) { int parsed_num = 0, frameIdx = 0; char* token = NULL; static char lineStr[256] = {0, }; char valueStr[256] = {0, }; int has_update = 0; while (1) { if (lineStr[0] == 0x0) { //need a new line; if (fgets(lineStr, 256, fp) == NULL ) { if (cfg_update->enable_option && has_update) return 1; else { if (feof(fp)) return 0; continue; } } } if ((lineStr[0] == '#') ||(lineStr[0] == ';')||(lineStr[0] == ':')) { // skip comments lineStr[0] = 0x0; continue; } // keep the original memcpy(valueStr, lineStr, strlen(lineStr)); //frame number is separated by ' ' or ':' token = strtok(valueStr, ": "); if (token == NULL) { lineStr[0] = 0x0; continue; } frameIdx = atoi(token); if (frameIdx <0 || frameIdx < cfg_update->FrameNum) { // invid number lineStr[0] = 0x0; continue; } else if (frameIdx > cfg_update->FrameNum && has_update) {// done the currrent frame; if (cfg_update->enable_option) return 1; has_update = 0; continue; } else if (has_update ==0) {// clear the options cfg_update->FrameNum = frameIdx; cfg_update->enable_option = 0; has_update = 1; } token = strtok(NULL, " :"); // check the operation code while (token && strlen(token) == 1 && strncmp(token, " ", 1) == 0) token = strtok(NULL, " :"); // skip spaces if (token == NULL) { // no operate code lineStr[0] = 0x0; continue; } if (strcasecmp("ForceType",token) == 0) { // force picture type token = strtok(NULL, ":\r\n"); while (token && strlen(token) == 1 && strncmp(token, " ", 1) == 0) token = strtok(NULL, ":\r\n"); //check space if (token) { while ( *token == ' ' ) token++;//skip spaces; cfg_update->picType = atoi(token); cfg_update->enable_option |= FORCE_PICTURE_TYPE; } } else if (strcasecmp("ForceSkip",token) == 0) { // force picture type if (token) { cfg_update->enable_option |= FORCE_PICTURE_SKIP; } } else if (strcasecmp("ChangeBitRate",token) == 0) { token = strtok(NULL, ":\r\n"); while ( token && strlen(token) == 1 && strncmp(token, " ", 1) == 0) token = strtok(NULL, ":\r\n"); //check space if (token) { while ( *token == ' ' ) token++;//skip spaces; cfg_update->bitRate = atoi(token); cfg_update->enable_option |= CHANGE_TARGET_RATE; } } else if (strcasecmp("ChangeMinMaxQP",token) == 0) { token = strtok(NULL, ":\r\n"); while (token && strlen(token) == 1 && strncmp(token, " ", 1) == 0) token = strtok(NULL, ":\r\n"); //check space if (token) { while ( *token == ' ' ) token++;//skip spaces; parsed_num = sscanf(token, "%d %d %d %d %d %d %d", &cfg_update->minQpI, &cfg_update->maxQpI, &cfg_update->maxDeltaQp, &cfg_update->minQpP, &cfg_update->maxQpP, &cfg_update->minQpB, &cfg_update->maxQpB); if (parsed_num == 7) cfg_update->enable_option |= CHANGE_MIN_MAX_QP; } } else if (strcasecmp("ChangePeriodGOP",token) == 0) { token = strtok(NULL, ":\r\n"); while ( token && strlen(token) == 1 && strncmp(token, " ", 1) == 0) token = strtok(NULL, ":\r\n"); //check space if (token) { while ( *token == ' ' ) token++;//skip spaces; parsed_num = sscanf(token, "%d %d", &cfg_update->intraQP, &cfg_update->GOPPeriod); if (parsed_num == 2) cfg_update->enable_option |= CHANGE_GOP_PERIOD; } } else if (strcasecmp("SetLongTermRef",token) == 0) { token = strtok(NULL, ":\r\n"); while ( token && strlen(token) == 1 && strncmp(token, " ", 1) == 0) token = strtok(NULL, ":\r\n"); //check space if (token) { while ( *token == ' ' ) token++;//skip spaces; cfg_update->LTRFlags = atoi(token); cfg_update->enable_option |= LONGTERM_REF_SET; } } else if (strcasecmp("SetCustSmooth",token) == 0) { token = strtok(NULL, ":\r\n"); while ( token && strlen(token) == 1 && strncmp(token, " ", 1) == 0) token = strtok(NULL, ":\r\n"); //check space if (token) { while ( *token == ' ' ) token++;//skip spaces; cfg_update->smooth_level = atoi(token); cfg_update->enable_option |= CUST_SIZE_SMOOTH; } } else if (strcasecmp("SetCustH264P",token) == 0) { token = strtok(NULL, ":\r\n"); while ( token && strlen(token) == 1 && strncmp(token, " ", 1) == 0) token = strtok(NULL, ":\r\n"); //check space if (token) { while ( *token == ' ' ) token++;//skip spaces; parsed_num = sscanf(token, "%d %d %d", &cfg_update->newIDRIntervel, &cfg_update->QPdelta_IDR, &cfg_update->QPdelta_LTR); if (parsed_num == 3) cfg_update->enable_option |= CUST_APP_H264_PLUS; } } else if (strcasecmp("ChangeMultiSlice",token) == 0) { token = strtok(NULL, ":\r\n"); while ( token && strlen(token) == 1 && strncmp(token, " ", 1) == 0) token = strtok(NULL, ":\r\n"); //check space if (token) { while ( *token == ' ' ) token++;//skip spaces; parsed_num = sscanf(token, "%d %d", &cfg_update->Slice_Mode, &cfg_update->Slice_Para); if (parsed_num == 2) cfg_update->enable_option |= CHANGE_MULTI_SLICE; } } else if (strcasecmp("ChangeStrictRC",token) == 0) { token = strtok(NULL, ":\r\n"); while ( token && strlen(token) == 1 && strncmp(token, " ", 1) == 0) token = strtok(NULL, ":\r\n"); //check space if (token) { while ( *token == ' ' ) token++;//skip spaces; parsed_num = sscanf(token, "%d %d", &cfg_update->bitrate_window, &cfg_update->pskip_threshold); if (parsed_num == 2) cfg_update->enable_option |= CHANGE_STRICT_RC; } } else if (strcasecmp("ChangeFrameRate",token) == 0) { token = strtok(NULL, ":\r\n"); while ( token && strlen(token) == 1 && strncmp(token, " ", 1) == 0) token = strtok(NULL, ":\r\n"); //check space if (token) { while ( *token == ' ' ) token++;//skip spaces; cfg_update->frameRate = atoi(token); cfg_update->enable_option |= CHANGE_FRAME_RATE; } } lineStr[0] = 0x0; continue; } }