1717 lines
66 KiB
C
Executable File
1717 lines
66 KiB
C
Executable File
/*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
|
|
#if defined(__ANDROID__)
|
|
#include <utils/Log.h>
|
|
#define LOGCAT
|
|
#endif
|
|
|
|
#include <sys/time.h>
|
|
#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;
|
|
}
|