/* * 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" #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 // 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", }; 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 } CfgChangeParam; 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); int main(int argc, const char *argv[]) { 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; 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; uint32_t frame_mirroring; encoding_metadata_t encoding_metadata; 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; if (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)\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 \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(" 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"); return -1; } else { printf("%s\n", argv[1]); printf("%s\n", argv[2]); } width = atoi(argv[3]); if ((width < 1) || (width > WIDTH)) { printf("invalid width \n"); return -1; } height = atoi(argv[4]); if ((height < 1) || (height > HEIGHT)) { printf("invalid height \n"); return -1; } gop = atoi(argv[5]); framerate = atoi(argv[6]); bitrate = atoi(argv[7]); num = atoi(argv[8]); fmt = (vl_img_format_t) atoi(argv[9]); buf_type = atoi(argv[10]); num_planes = atoi(argv[11]); switch (atoi(argv[12])) { 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); return -1; } if ((framerate < 0) || (framerate > FRAMERATE)) { printf("invalid framerate %d \n", framerate); return -1; } if (bitrate <= 0) { printf("invalid bitrate \n"); return -1; } if (num < 0) { printf("invalid num \n"); return -1; } if (buf_type == 3 && (num_planes < 1 || num_planes > 3)) { printf("invalid num_planes \n"); return -1; } printf("src_url is\t: %s ;\n", argv[1]); printf("out_url is\t: %s ;\n", argv[2]); 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", num_planes); if (codec_id == CODEC_ID_H265) printf("codec is H265\n"); else printf("codec is H264\n"); if (argc > 13) { cfg_option = atoi(argv[13]); 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 > 8) { printf("gop_pattern_is: %d invalid;\n", gop_pattern); return -1; } 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); return -1; } 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); } if (argc > 14) { arg_count = 14; if ((cfg_option & 0x1) == 0x1 && argc > arg_count) { printf("roi_url is\t: %s ;\n", argv[arg_count]); roi_enabled = 1; arg_roi = arg_count; arg_count ++; } if ((cfg_option & 0x2) == 0x2 && argc > arg_count) { printf("cfg_upd_url is\t: %s ;\n", argv[arg_count]); cfg_upd_enabled = 1; arg_upd = arg_count; arg_count ++; } if ((cfg_option & 0x100) == 0x100 && argc > arg_count) { src_buf_stride = atoi(argv[arg_count]); conver_stride = (cfg_option & 0x200) >> 9; printf("cust stride is\t: %d convert: %d;\n", src_buf_stride, conver_stride); arg_count ++; } if ((cfg_option & 0x400) == 0x400 && argc > arg_count) { intra_refresh_comb = atoi(argv[arg_count]); printf("IntraRefresh mode %d, arg %d;\n", (intra_refresh_comb & 0xf), (intra_refresh_comb >> 4)); arg_count ++; } if ((cfg_option & 0x40000) == 0x40000 && argc > arg_count) { bitstream_buf_size = atoi(argv[arg_count]); printf("bitstream_buf_size: %d \n", bitstream_buf_size); arg_count ++; } if (multi_slice && argc > arg_count) { multi_slice_para = atoi(argv[arg_count]); printf("Multi-slices para %d \n", multi_slice_para); arg_count ++; } if (cust_qp_delta_en && argc > arg_count) { cust_qp_delta = atoi(argv[arg_count]); printf("cust_qp_delta %d \n", cust_qp_delta); arg_count ++; } if (strict_rate_ctrl_en && argc > arg_count) { bitrate_window = atoi(argv[arg_count]); skip_threshold = bitrate_window & 0xffff; bitrate_window >>=16; printf("bit-rate window %d frames, skip_threshold %d\n", bitrate_window, skip_threshold); arg_count ++; } if (arg_count != argc) { printf("config no match conf %d argc %d\n", cfg_option, argc); return -1; } } } if (src_buf_stride && src_buf_stride < width) { printf("incorrect cust source stride %d width %d", src_buf_stride, width); return -1; } unsigned int outbuffer_len = 8*1024 * 1024 * sizeof(char); if (bitstream_buf_size > 8) outbuffer_len = bitstream_buf_size*1024 * 1024 * sizeof(char); unsigned char *outbuffer = (unsigned char *) malloc(outbuffer_len); 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; if (src_buf_stride) { 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 { framesize = width * height * 3 / 2; ysize = width * height; usize = width * height / 4; vsize = width * height / 4; uvsize = width * height / 2; } memset(&inbuf_info, 0, sizeof(vl_buffer_info_t)); inbuf_info.buf_type = (vl_buffer_type_t) buf_type; inbuf_info.buf_stride = src_buf_stride; 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; 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 >= 0 && 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 exit; } } 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 exit; } 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; } 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; 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; } 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); 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((argv[1]), "rb"); if (fp == NULL) { printf("open src file error!\n"); goto exit; } if (roi_enabled) { fp_roi = fopen((argv[arg_roi]), "rb"); if (fp_roi == NULL) { printf("open roi file error!\n"); goto exit; } } if (cfg_upd_enabled) { fp_cfg = fopen((argv[arg_upd]), "r"); if (fp_cfg == NULL) { printf("open cfg file error!\n"); goto exit; } has_cfg_update = ParseCfgUpdateFile(fp_cfg, &cfgChange); } outfd = open((argv[2]), 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; } } while (num > 0) { 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; 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.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); 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); } num--; frame_num++; playStat.PrevFrameQP = encoding_metadata.extra.average_qp_value; playStat.CurFrameNum++; playStat.CurGOPCounter++; if (playStat.cust_ltr_enable) { playStat.GopLTRCounter++; } } exit: if (handle_enc) vl_multi_encoder_destroy(handle_enc); if (outfd >= 0) close(outfd); if (fp) fclose(fp); if (fp_roi) fclose(fp_roi); if (fp_cfg) fclose(fp_cfg); if (outbuffer != NULL) free(outbuffer); if (roi_buffer != NULL) free(roi_buffer); if (inbuf_info.buf_type == DMA_TYPE) { 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!\n"); 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; } } lineStr[0] = 0x0; continue; } }