// Copyright 2019 Fuzhou Rockchip Electronics Co., Ltd. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "buffer.h" #include "media_type.h" #include "mpp_encoder.h" #define ENCODER_CFG_INVALID 0x7FFFFFFF #define ENCODER_CFG_CHECK(VALUE, MIN, MAX, DEF_VALUE, TAG) \ if (!VALUE) { \ if (DEF_VALUE != ENCODER_CFG_INVALID) { \ LOG("MPP Encoder: %s use default value:%d\n", TAG, DEF_VALUE); \ VALUE = DEF_VALUE; \ } else { \ LOG("ERROR: MPP Encoder: %s invalid value(%d)\n", TAG, VALUE); \ return false; \ } \ } else if ((VALUE > MAX) || (VALUE < MIN)) { \ LOG("ERROR: MPP Encoder: %s invalid value(%d)\n", TAG, VALUE); \ return false; \ } namespace easymedia { #if 0 static float smart_enc_mode_get_bps_factor(int bps, int w, int h) { float den = 1.0; //Reference 1080p resolution: //5Mb: [bpsMax / 4, bpsMax] //4Mb: [bpsMax / 3, bpsMax] //3Mb: [bpsMax / 2, bpsMax] //Others: [bpsMax * 0.8, bpsMax] int relatively_bps = (int)(bps * (2073600.0) / (w * h)); if(relatively_bps >= 5242880) { //5Mb den = 0.25; //1/4 } else if(relatively_bps >= 4194304) { //4Mb den = 0.333; //1/3 } else if(relatively_bps >= 3145728) { //3Mb den = 0.5; //1/2 } else { den = 0.8; } return den; } #endif #define MPP_MIN_BPS_VALUE 2000 // 2Kb #define MPP_MAX_BPS_VALUE 100000000 // 100Mb // According to bps_max, automatically calculate bps_target and bps_min. static int CalcMppBpsWithMax(MppEncRcMode rc_mode, int& bps_max, int& bps_min, int& bps_target) { if ((rc_mode != MPP_ENC_RC_MODE_FIXQP) && ((bps_max > MPP_MAX_BPS_VALUE) || (bps_max < MPP_MIN_BPS_VALUE))) { LOG("ERROR: MPP Encoder: bps max <%d> is not valid!\n", bps_max); return -1; } LOG("MPP Encoder: automatically calculate bsp with bps_max\n"); switch (rc_mode) { case MPP_ENC_RC_MODE_CBR: // constant bitrate has very small bps range of 1/16 bps bps_target = bps_max * 16 / 17; bps_min = bps_max * 15 / 17; break; case MPP_ENC_RC_MODE_VBR: // variable bitrate has large bps range bps_target = bps_max * 16 / 17; bps_min = bps_max * 1 / 17; break; case MPP_ENC_RC_MODE_FIXQP: bps_target = bps_min = bps_max; return 0; // FIXQP mode bps is invalid! default: LOG("ERROR: MPP Encoder: rc_mode=%d is invalid!\n", rc_mode); return -1; } if (bps_min < MPP_MIN_BPS_VALUE) { bps_min = MPP_MIN_BPS_VALUE; } if (bps_target < bps_min) { bps_target = (bps_min + bps_max) / 2; } return 0; } // According to bps_target, automatically calculate bps_max and bps_min. static int CalcMppBpsWithTarget(MppEncRcMode rc_mode, int& bps_max, int& bps_min, int& bps_target) { if ((rc_mode != MPP_ENC_RC_MODE_FIXQP) && ((bps_target > MPP_MAX_BPS_VALUE) || (bps_target < MPP_MIN_BPS_VALUE))) { LOG("ERROR: MPP Encoder: bps <%d> is not valid!\n", bps_target); return -1; } LOG("MPP Encoder: automatically calculate bsp with bps_target\n"); switch (rc_mode) { case MPP_ENC_RC_MODE_CBR: // constant bitrate has very small bps range of 1/16 bps bps_max = bps_target * 17 / 16; bps_min = bps_target * 15 / 16; break; case MPP_ENC_RC_MODE_VBR: // variable bitrate has large bps range bps_max = bps_target * 17 / 16; bps_min = bps_target * 1 / 16; break; case MPP_ENC_RC_MODE_FIXQP: bps_target = bps_min = bps_max; return 0; // FIXQP mode bps is invalid! default: LOG("ERROR: MPP Encoder: rc_mode=%d is invalid!\n", rc_mode); return -1; } if (bps_min < MPP_MIN_BPS_VALUE) { bps_min = MPP_MIN_BPS_VALUE; } if (bps_max > MPP_MAX_BPS_VALUE) { bps_max = MPP_MAX_BPS_VALUE; } return 0; } class MPPConfig { public: MPPConfig(); virtual ~MPPConfig(); virtual bool InitConfig(MPPEncoder& mpp_enc, MediaConfig& cfg) = 0; virtual bool CheckConfigChange(MPPEncoder& mpp_enc, uint32_t change, std::shared_ptr val) = 0; MppEncCfg enc_cfg; }; MPPConfig::MPPConfig() { enc_cfg = NULL; if (mpp_enc_cfg_init(&enc_cfg)) { LOG("ERROR: MPP Encoder: MPPConfig: cfg init failed!"); enc_cfg = NULL; return; } LOG("MPP Encoder: MPPConfig: cfg init sucess!\n"); } MPPConfig::~MPPConfig() { if (enc_cfg) { mpp_enc_cfg_deinit(enc_cfg); LOG("MPP Encoder: MPPConfig: cfg deinit done!\n"); } } class MPPMJPEGConfig : public MPPConfig { public: MPPMJPEGConfig() { } ~MPPMJPEGConfig() = default; virtual bool InitConfig(MPPEncoder& mpp_enc, MediaConfig& cfg) override; virtual bool CheckConfigChange(MPPEncoder& mpp_enc, uint32_t change, std::shared_ptr val) override; }; bool MPPMJPEGConfig::InitConfig(MPPEncoder& mpp_enc, MediaConfig& cfg) { // for jpeg/mjpeg common. ImageConfig img_cfg; ImageInfo img_info; // for mjpeg. VideoConfig vid_cfg; int bps_max = 0, bps_min = 0, bps_target = 0; int fps_in_num = 0, fps_in_den = 0; int fps_out_num = 0, fps_out_den = 0; MppEncRcMode rc_mode = MPP_ENC_RC_MODE_FIXQP; int ret = 0; if (!enc_cfg) { LOG("ERROR: MPP Encoder[JPEG]: mpp enc cfg is null!\n"); return false; } if (cfg.type == Type::Image) { LOG("MPP Encoder[JPEG]: config for JPEG...\n"); img_cfg = cfg.img_cfg; img_info = cfg.img_cfg.image_info; ENCODER_CFG_CHECK(img_cfg.qfactor, 1, 99, 70, "qfactor"); } else if (cfg.type == Type::Video) { LOG("MPP Encoder[JPEG]: config for MJPEG...\n"); vid_cfg = cfg.vid_cfg; img_cfg = vid_cfg.image_cfg; img_info = cfg.img_cfg.image_info; // Encoder param check. ENCODER_CFG_CHECK(vid_cfg.frame_rate, 1, 60, ENCODER_CFG_INVALID, "fpsNum"); ENCODER_CFG_CHECK(vid_cfg.frame_rate_den, 1, 16, 1, "fpsDen"); ENCODER_CFG_CHECK(vid_cfg.frame_in_rate, 1, 60, ENCODER_CFG_INVALID, "fpsInNum"); ENCODER_CFG_CHECK(vid_cfg.frame_in_rate_den, 1, 16, 1, "fpsInDen"); if (!vid_cfg.rc_mode) { LOG("MPP Encoder[JPEG]: rcMode use defalut value: vbr\n"); vid_cfg.rc_mode = KEY_VBR; } rc_mode = GetMPPRCMode(vid_cfg.rc_mode); if (rc_mode == MPP_ENC_RC_MODE_BUTT) { LOG("ERROR: MPP Encoder[JPEG]: Invalid rc mode %s\n", vid_cfg.rc_mode); return false; } bps_max = vid_cfg.bit_rate_max; bps_min = vid_cfg.bit_rate_min; bps_target = vid_cfg.bit_rate; fps_in_num = vid_cfg.frame_in_rate; fps_in_den = vid_cfg.frame_in_rate_den; fps_out_num = vid_cfg.frame_rate; fps_out_den = vid_cfg.frame_rate_den; if (CalcMppBpsWithMax(rc_mode, bps_max, bps_min, bps_target)) { LOG("ERROR: MPP Encoder[JPEG]: Invalid bps:[%d, %d, %d]\n", vid_cfg.bit_rate_min, vid_cfg.bit_rate, vid_cfg.bit_rate_max); return false; } } else { LOG("ERROR: MPP Encoder[JPEG]: mpp enc cfg type(%d) is invalid!\n"); return false; } ENCODER_CFG_CHECK(img_info.width, 1, 8192, ENCODER_CFG_INVALID, "width"); ENCODER_CFG_CHECK(img_info.height, 1, 8192, ENCODER_CFG_INVALID, "height"); ENCODER_CFG_CHECK(img_info.vir_width, 1, 8192, img_info.width, "virWidth"); ENCODER_CFG_CHECK(img_info.vir_height, 1, 8192, img_info.height, "virHeight"); MppFrameFormat pic_type = ConvertToMppPixFmt(img_info.pix_fmt); if (pic_type == -1) { LOG("ERROR: MPP Encoder[JPEG]: invalid pixel format!\n"); return false; } MppPollType timeout = MPP_POLL_BLOCK; LOG("MPP Encoder[JPEG]: Set output block mode.\n"); ret = mpp_enc.EncodeControl(MPP_SET_OUTPUT_TIMEOUT, &timeout); if (ret != 0) { LOG("ERROR: MPP Encoder[JPEG]: set output block failed! ret %d\n", ret); return false; } LOG("MPP Encoder[JPEG]: Set input block mode.\n"); ret = mpp_enc.EncodeControl(MPP_SET_INPUT_TIMEOUT, &timeout); if (ret != 0) { LOG("ERROR: MPP Encoder[JPEG]: set input block failed! ret %d\n", ret); return false; } int line_size = img_info.vir_width; if (pic_type == MPP_FMT_YUV422_YUYV || pic_type == MPP_FMT_YUV422_UYVY) { line_size *= 2; } // precfg set. ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:width", img_info.width); ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:height", img_info.height); ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:hor_stride", line_size); ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:ver_stride", img_info.vir_height); ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:format", pic_type); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:mode", rc_mode); // default fixqp. ret |= mpp_enc_cfg_set_s32(enc_cfg, "jpeg:q_factor", img_cfg.qfactor); if (cfg.type == Type::Video) { LOG("MPP Encoder[JPEG]: set rc cfg for MJPEG...\n"); // mjpeg rc confg set. ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_min", bps_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_max", bps_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_target", bps_target); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_in_flex", 0); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_in_num", fps_in_num); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_in_denorm", fps_in_den); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_out_flex", 0); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_out_num", fps_out_num); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_out_denorm", fps_out_den); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:gop", 1); // no use, default 300. LOG("MPP Encoder[JPEG]: bps:[%d, %d, %d], fps_in:%d/%d, fps_out:%d/%d, " "gop:1\n", bps_min, bps_target, bps_max, fps_in_num, fps_in_den, fps_out_num, fps_out_den); // for mjpeg only ret |= mpp_enc_cfg_set_s32(enc_cfg, "jpeg:qf_max", 99); ret |= mpp_enc_cfg_set_s32(enc_cfg, "jpeg:qf_min", 10); } if (ret) { LOG("ERROR: MPP Encoder[JPEG]: cfg set s32 failed ret %d\n", ret); return false; } ret = mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg); if (ret) { LOG("ERROR: MPP Encoder[JPEG]: encoder set cfg failed! ret=%d\n", ret); return false; } LOG("MPP Encoder[JPEG]: w x h(%d[%d] x %d[%d]), qfactor:%d\n", img_info.width, line_size, img_info.height, img_info.vir_height, img_cfg.qfactor); mpp_enc.GetConfig() = cfg; return true; } bool MPPMJPEGConfig::CheckConfigChange(MPPEncoder& mpp_enc, uint32_t change, std::shared_ptr val) { ImageConfig& iconfig = mpp_enc.GetConfig().img_cfg; VideoConfig& vconfig = mpp_enc.GetConfig().vid_cfg; int ret = 0; if (!enc_cfg) { LOG("ERROR: MPP Encoder[JPEG]: mpp enc cfg is null!\n"); return false; } if (change & VideoEncoder::kFrameRateChange) { uint8_t* values = (uint8_t*)val->GetPtr(); if (val->GetSize() < 4) { LOG("ERROR: MPP Encoder[JPEG]: fps should be uint8_t array[4]:" "{inFpsNum, inFpsDen, outFpsNum, outFpsDen}"); return false; } uint8_t in_fps_num = values[0]; uint8_t in_fps_den = values[1]; uint8_t out_fps_num = values[2]; uint8_t out_fps_den = values[3]; if (!out_fps_num || !out_fps_den || (out_fps_num > 60)) { LOG("ERROR: MPP Encoder[JPEG]: invalid out fps: [%d/%d]\n", out_fps_num, out_fps_den); return false; } if (in_fps_num && in_fps_den) { ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_in_num", in_fps_num); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_in_denorm", in_fps_den); } ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_out_num", out_fps_num); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_out_denorm", out_fps_den); if (ret) { LOG("ERROR: MPP Encoder[JPEG]: fps: cfg set s32 failed ret %d\n", ret); return false; } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder[JPEG]: change fps cfg failed!\n"); return false; } if (in_fps_num && in_fps_den) LOG("MPP Encoder[JPEG]: new fps: [%d/%d]->[%d/%d]\n", in_fps_num, in_fps_den, out_fps_num, out_fps_den); else LOG("MPP Encoder[JPEG]: new out fps: [%d/%d]\n", out_fps_num, out_fps_den); vconfig.frame_rate = out_fps_num; } else if (change & VideoEncoder::kBitRateChange) { int* values = (int*)val->GetPtr(); if (val->GetSize() < 3 * sizeof(int)) { LOG("ERROR: MPP Encoder[JPEG]: fps should be int array[3]:" "{bpsMin, bpsTarget, bpsMax}"); return false; } int bps_min = values[0]; int bps_target = values[1]; int bps_max = values[2]; MppEncRcMode rc_mode = GetMPPRCMode(vconfig.rc_mode); if (rc_mode == MPP_ENC_RC_MODE_BUTT) { LOG("ERROR: MPP Encoder[JPEG]: bps: invalid rc mode %s\n", vconfig.rc_mode); return false; } else if (rc_mode == MPP_ENC_RC_MODE_FIXQP) { LOG("WARN: MPP Encoder[JPEG]: bps: FIXQP no need bps!\n"); return true; } if (CalcMppBpsWithMax(rc_mode, bps_max, bps_min, bps_target)) { LOG("ERROR: MPP Encoder[JPEG]: Invalid bps:[%d, %d, %d]\n", bps_min, bps_target, bps_max); return false; } LOG("MPP Encoder[JPEG]: new bps:[%d, %d, %d]\n", bps_min, bps_target, bps_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_min", bps_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_max", bps_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_target", bps_target); if (ret) { LOG("ERROR: MPP Encoder[JPEG]: bps: cfg set s32 failed ret %d\n", ret); return false; } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder[JPEG]: change bps cfg failed!\n"); return false; } // save new value to config. vconfig.bit_rate = bps_target; vconfig.bit_rate_max = bps_max; vconfig.bit_rate_min = bps_min; } else if (change & VideoEncoder::kRcModeChange) { char* new_mode = (char*)val->GetPtr(); LOG("MPP Encoder[JPEG]: new rc_mode:%s\n", new_mode); MppEncRcMode rc_mode = GetMPPRCMode(new_mode); if (rc_mode == MPP_ENC_RC_MODE_BUTT) { LOG("ERROR: MPP Encoder[JPEG]: rc_mode is invalid! should be cbr/vbr.\n"); return false; } // Recalculate bps int bps_max = vconfig.bit_rate_max; int bps_min = bps_max; int bps_target = bps_max; if (CalcMppBpsWithMax(rc_mode, bps_max, bps_min, bps_target) < 0) { return false; } ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:mode", rc_mode); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_min", bps_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_max", bps_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_target", bps_target); if (ret) { LOG("ERROR: MPP Encoder[JPEG]: rc mode: cfg set s32 failed ret %d\n", ret); return false; } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder[JPEG]: change rc_mode cfg failed!\n"); return false; } // save new value to encoder->vconfig. vconfig.rc_mode = ConvertRcMode(new_mode); } else if (change & VideoEncoder::kQPChange) { int qfactor = val->GetValue(); LOG("MPP Encoder[JPEG]: new qfactor:%d\n", qfactor); ENCODER_CFG_CHECK(qfactor, 1, 99, ENCODER_CFG_INVALID, "JPEG:qfactor"); ret = mpp_enc_cfg_set_s32(enc_cfg, "jpeg:q_factor", qfactor); if (ret) { LOG("ERROR: MPP Encoder[JPEG]: cfg set s32 failed! ret=%d\n", ret); return false; } ret = mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg); if (ret) { LOG("ERROR: MPP Encoder[JPEG]: qfactor: set cfg failed! ret=%d\n", ret); return false; } iconfig.qfactor = qfactor; } else { LOG("MPP Encoder[JPEG]: Unsupport request change 0x%08x!\n", change); return false; } return true; } class MPPCommonConfig : public MPPConfig { public: MPPCommonConfig(MppCodingType type) : code_type(type) { } ~MPPCommonConfig() = default; virtual bool InitConfig(MPPEncoder& mpp_enc, MediaConfig& cfg) override; virtual bool CheckConfigChange(MPPEncoder& mpp_enc, uint32_t change, std::shared_ptr val) override; private: MppCodingType code_type; }; static int CalcQpWithRcQuality(const char* level, VideoEncoderQp& qps) { VideoEncoderQp qp_table[7] = { {-1, 6, 20, 51, 24, 51}, // Highest {-1, 6, 24, 51, 24, 51}, // Higher {-1, 6, 26, 51, 24, 51}, // High {-1, 6, 29, 51, 24, 51}, // Medium {-1, 6, 30, 51, 24, 51}, // Low {-1, 6, 35, 51, 24, 51}, // Lower {-1, 6, 38, 51, 24, 51} // Lowest }; // Read From qp cfg file. // To Do... if (!strcmp(KEY_HIGHEST, level)) { qps = qp_table[0]; } else if (!strcmp(KEY_HIGHER, level)) { qps = qp_table[1]; } else if (!strcmp(KEY_HIGH, level)) { qps = qp_table[2]; } else if (!strcmp(KEY_MEDIUM, level)) { qps = qp_table[3]; } else if (!strcmp(KEY_LOW, level)) { qps = qp_table[4]; } else if (!strcmp(KEY_LOWER, level)) { qps = qp_table[5]; } else if (!strcmp(KEY_LOWEST, level)) { qps = qp_table[6]; } else { LOG("ERROR: MPP Encoder: invalid rcQualit:%s\n", level); return -1; } return 0; } bool MPPCommonConfig::InitConfig(MPPEncoder& mpp_enc, MediaConfig& cfg) { VideoConfig vconfig = cfg.vid_cfg; ImageConfig& img_cfg = vconfig.image_cfg; ImageInfo& image_info = cfg.img_cfg.image_info; MppPollType timeout = MPP_POLL_BLOCK; MppFrameFormat pic_type = ConvertToMppPixFmt(image_info.pix_fmt); int ret = 0; if (!enc_cfg) { LOG("ERROR: MPP Encoder: mpp enc cfg is null!\n"); return false; } // In VBR mode, and the user has not set qp, // at this time, the qp value is obtained according to RcQuality. if (vconfig.rc_mode && vconfig.rc_quality && (!strcmp(vconfig.rc_mode, KEY_VBR)) && (!vconfig.qp_max || !vconfig.qp_min)) { VideoEncoderQp qps; if (CalcQpWithRcQuality(vconfig.rc_quality, qps)) { return false; } LOG("MPP Encoder: [%s:%s] init:%d, setp:%d, min:%d, " "max:%d, min_i:%d, max_i:%d\n", vconfig.rc_mode, vconfig.rc_quality, qps.qp_init, qps.qp_step, qps.qp_min, qps.qp_max, qps.qp_min_i, qps.qp_max_i); vconfig.qp_init = qps.qp_init; vconfig.qp_step = qps.qp_step; vconfig.qp_min = qps.qp_min; vconfig.qp_max = qps.qp_max; vconfig.qp_min_i = qps.qp_min_i; vconfig.qp_max_i = qps.qp_max_i; } // Encoder param check. ENCODER_CFG_CHECK(vconfig.frame_rate, 1, 60, ENCODER_CFG_INVALID, "fpsNum"); ENCODER_CFG_CHECK(vconfig.frame_rate_den, 1, 16, 1, "fpsDen"); ENCODER_CFG_CHECK(vconfig.frame_in_rate, 1, 60, ENCODER_CFG_INVALID, "fpsInNum"); ENCODER_CFG_CHECK(vconfig.frame_in_rate_den, 1, 16, 1, "fpsInDen"); ENCODER_CFG_CHECK(vconfig.gop_size, 1, 3000, (vconfig.frame_rate > 10) ? vconfig.frame_rate : 30, "gopSize"); ENCODER_CFG_CHECK(vconfig.qp_max, 8, 51, 48, "qpMax"); ENCODER_CFG_CHECK(vconfig.qp_min, 1, VALUE_MIN(vconfig.qp_max, 48), VALUE_MIN(vconfig.qp_max, 8), "qpMin"); // qp_init = -1: mpp encoder automatically generate // a value for qp_init. if (vconfig.qp_init != -1) { ENCODER_CFG_CHECK(vconfig.qp_init, vconfig.qp_min, vconfig.qp_max, (vconfig.qp_min + vconfig.qp_max) / 2, "qpInit"); } ENCODER_CFG_CHECK(vconfig.qp_step, 1, (vconfig.qp_max - vconfig.qp_min), VALUE_MIN((vconfig.qp_max - vconfig.qp_min), 2), "qpStep"); ENCODER_CFG_CHECK(img_cfg.image_info.width, 1, 8192, ENCODER_CFG_INVALID, "width"); ENCODER_CFG_CHECK(img_cfg.image_info.height, 1, 8192, ENCODER_CFG_INVALID, "height"); ENCODER_CFG_CHECK(img_cfg.image_info.vir_width, 1, 8192, img_cfg.image_info.width, "virWidth"); ENCODER_CFG_CHECK(img_cfg.image_info.vir_height, 1, 8192, img_cfg.image_info.height, "virHeight"); ENCODER_CFG_CHECK(vconfig.qp_max_i, 8, 51, 48, "qpMaxi"); ENCODER_CFG_CHECK(vconfig.qp_min_i, 1, VALUE_MIN(vconfig.qp_max_i, 48), VALUE_MIN(vconfig.qp_max_i, 8), "qpMini"); if (pic_type == -1) { LOG("error input pixel format\n"); return false; } if (!vconfig.rc_mode) { LOG("MPP Encoder: rcMode use defalut value: vbr\n"); vconfig.rc_mode = KEY_VBR; } MppEncRcMode rc_mode = GetMPPRCMode(vconfig.rc_mode); if (rc_mode == MPP_ENC_RC_MODE_BUTT) { LOG("ERROR: MPP Encoder: Invalid rc mode %s\n", vconfig.rc_mode); return false; } int bps_max = vconfig.bit_rate_max; int bps_min = vconfig.bit_rate_min; int bps_target = vconfig.bit_rate; int fps_in_num = vconfig.frame_in_rate; int fps_in_den = vconfig.frame_in_rate_den; int fps_out_num = vconfig.frame_rate; int fps_out_den = vconfig.frame_rate_den; int gop = vconfig.gop_size; int full_range = vconfig.full_range; MppEncRotationCfg rotation = MPP_ENC_ROT_0; switch (vconfig.rotation) { case 0: rotation = MPP_ENC_ROT_0; LOG("MPP Encoder: rotaion = 0\n"); break; case 90: LOG("MPP Encoder: rotaion = 90\n"); rotation = MPP_ENC_ROT_90; break; case 180: LOG("MPP Encoder: rotaion = 180\n"); rotation = MPP_ENC_ROT_180; break; case 270: LOG("MPP Encoder: rotaion = 270\n"); rotation = MPP_ENC_ROT_270; break; default: LOG("ERROR: MPP Encoder: rotaion(%d) is invalid!\n", vconfig.rotation); return false; } // encoder not support fbc rotation. if ((image_info.pix_fmt == PIX_FMT_FBC0) || (image_info.pix_fmt == PIX_FMT_FBC2)) { if ((rotation == MPP_ENC_ROT_90) || (rotation == MPP_ENC_ROT_270)) { int tmp_value = image_info.width; image_info.width = image_info.height; image_info.height = tmp_value; tmp_value = image_info.vir_width; image_info.vir_width = image_info.vir_height; image_info.vir_height = tmp_value; LOG("MPP Encoder: rotation in fbc mode, Resolution " "from %d(%d)x%d(%d) to %d(%d)x%d(%d)\n", image_info.height, image_info.vir_height, image_info.width, image_info.vir_width, image_info.width, image_info.vir_width, image_info.height, image_info.vir_height); } rotation = MPP_ENC_ROT_0; } // Three bit rate configuration methods: // 1. Straight-through mode: all three code rate values must be valid. // 2. Only bps_max: Generate three values based on bps_max. // 3. Only bps_target: Generate three values based on bps_target. if (bps_max && bps_min && bps_target) { if ((bps_max < MPP_MIN_BPS_VALUE) || (bps_max > MPP_MAX_BPS_VALUE) || (bps_min < MPP_MIN_BPS_VALUE) || (bps_min > MPP_MAX_BPS_VALUE) || (bps_target < bps_min) || (bps_target > bps_max)) { ret = -1; } } else if (bps_max && !bps_target && !bps_min) { ret = CalcMppBpsWithMax(rc_mode, bps_max, bps_min, bps_target); } else if (bps_target && !bps_max && !bps_min) { ret = CalcMppBpsWithTarget(rc_mode, bps_max, bps_min, bps_target); } else { ret = -1; } if (ret < 0) { LOG("ERROR: MPP Encoder: Invalid bps:[%d, %d, %d]\n", vconfig.bit_rate_min, vconfig.bit_rate, vconfig.bit_rate_max); return false; } int line_size = image_info.vir_width; if (pic_type == MPP_FMT_YUV422_YUYV || pic_type == MPP_FMT_YUV422_UYVY) { line_size *= 2; } LOG("MPP Encoder: Set output block mode.\n"); ret = mpp_enc.EncodeControl(MPP_SET_OUTPUT_TIMEOUT, &timeout); if (ret != 0) { LOG("ERROR: MPP Encoder: set output block failed ret %d\n", ret); return false; } LOG("MPP Encoder: Set input block mode.\n"); ret = mpp_enc.EncodeControl(MPP_SET_INPUT_TIMEOUT, &timeout); if (ret != 0) { LOG("ERROR: MPP Encoder: set input block failed ret %d\n", ret); return false; } // precfg set. ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:width", image_info.width); ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:height", image_info.height); ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:hor_stride", line_size); ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:ver_stride", image_info.vir_height); ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:format", pic_type); ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:rotation", rotation); if (full_range) { ret |= mpp_enc_cfg_set_s32(enc_cfg, "prep:range", MPP_FRAME_RANGE_JPEG); } // rccfg set. ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:mode", rc_mode); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_min", bps_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_max", bps_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_target", bps_target); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_in_flex", 0); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_in_num", fps_in_num); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_in_denorm", fps_in_den); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_out_flex", 0); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_out_num", fps_out_num); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_out_denorm", fps_out_den); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:gop", gop); vconfig.frame_rate = fps_in_num; LOG("MPP Encoder: bps:[%d,%d,%d] fps: [%d/%d]->[%d/%d], gop:%d\n", bps_max, bps_target, bps_min, fps_in_num, fps_in_den, fps_out_num, fps_out_den, gop); // codeccfg set. ret |= mpp_enc_cfg_set_s32(enc_cfg, "codec:type", code_type); switch (code_type) { case MPP_VIDEO_CodingAVC: // H.264 profile_idc parameter // 66 - Baseline profile // 77 - Main profile // 100 - High profile if (vconfig.profile != 66 && vconfig.profile != 77) { LOG("MPP Encoder: H264 profile use defalut value: 100"); vconfig.profile = 100; // default PROFILE_HIGH 100 } ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:profile", vconfig.profile); // H.264 level_idc parameter // 10 / 11 / 12 / 13 - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps // 20 / 21 / 22 - cif@30fps / half-D1@@25fps / D1@12.5fps // 30 / 31 / 32 - D1@25fps / 720p@30fps / 720p@60fps // 40 / 41 / 42 - 1080p@30fps / 1080p@30fps / 1080p@60fps // 50 / 51 / 52 - 4K@30fps ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:level", vconfig.level); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:cabac_en", (vconfig.profile == 100) ? 1 : 0); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:cabac_idc", 0); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:trans8x8", (vconfig.trans_8x8 && (vconfig.profile == 100)) ? 1 : 0); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_init", vconfig.qp_init); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_max", vconfig.qp_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_min", vconfig.qp_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_step", vconfig.qp_step); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_max_i", vconfig.qp_max_i); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_min_i", vconfig.qp_min_i); LOG("MPP Encoder: AVC: encode profile %d level %d init_qp %d\n", vconfig.profile, vconfig.level, vconfig.qp_init); break; case MPP_VIDEO_CodingHEVC: ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_init", vconfig.qp_init); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_max", vconfig.qp_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_min", vconfig.qp_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_step", vconfig.qp_step); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_max_i", vconfig.qp_max_i); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_min_i", vconfig.qp_min_i); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_delta_ip", 3); break; default: // will never go here, avoid gcc warning return false; } if (ret) { LOG("ERROR: MPP Encoder: cfg set s32 failed ret %d\n", ret); return false; } ret = mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg); if (ret) { LOG("ERROR: MPP Encoder: set cfg failed ret %d\n", ret); return false; } // save bps to vconfig. vconfig.bit_rate_max = bps_max; vconfig.bit_rate_min = bps_min; vconfig.bit_rate = bps_target; LOG("MPP Encoder: w x h(%d[%d] x %d[%d])\n", image_info.width, line_size, image_info.height, image_info.vir_height); #if 0 MppPacket packet = nullptr; ret = mpp_enc.EncodeControl(MPP_ENC_GET_EXTRA_INFO, &packet); if(ret) { LOG("ERROR: MPP Encoder: get extra info failed\n"); return false; } // Get and write sps/pps for H.264/5 if(packet) { void* ptr = mpp_packet_get_pos(packet); size_t len = mpp_packet_get_length(packet); if(!mpp_enc.SetExtraData(ptr, len)) { LOG("ERROR: MPP Encoder: SetExtraData failed\n"); return false; } mpp_enc.GetExtraData()->SetUserFlag(MediaBuffer::kExtraIntra); packet = NULL; } #endif if (vconfig.ref_frm_cfg) { MppEncRefCfg ref = NULL; LOG("MPP Encoder: enable tsvc mode...\n"); if (mpp_enc_ref_cfg_init(&ref)) { LOG("ERROR: MPP Encoder: ref cfg init failed!\n"); return false; } if (mpi_enc_gen_ref_cfg(ref)) { LOG("ERROR: MPP Encoder: ref cfg gen failed!\n"); mpp_enc_ref_cfg_deinit(&ref); return false; } ret = mpp_enc.EncodeControl(MPP_ENC_SET_REF_CFG, ref); mpp_enc_ref_cfg_deinit(&ref); } int header_mode = MPP_ENC_HEADER_MODE_EACH_IDR; ret = mpp_enc.EncodeControl(MPP_ENC_SET_HEADER_MODE, &header_mode); if (ret) { LOG("ERROR: MPP Encoder: set heder mode failed ret %d\n", ret); return false; } mpp_enc.GetConfig().vid_cfg = vconfig; mpp_enc.GetConfig().type = Type::Video; return true; } bool MPPCommonConfig::CheckConfigChange(MPPEncoder& mpp_enc, uint32_t change, std::shared_ptr val) { VideoConfig& vconfig = mpp_enc.GetConfig().vid_cfg; int ret = 0; if (!enc_cfg) { LOG("ERROR: MPP Encoder: mpp enc cfg is null!\n"); return false; } if (change & VideoEncoder::kFrameRateChange) { uint8_t* values = (uint8_t*)val->GetPtr(); if (val->GetSize() < 4) { LOG("ERROR: MPP Encoder: fps should be uint8_t array[4]:" "{inFpsNum, inFpsDen, outFpsNum, outFpsDen}"); return false; } uint8_t in_fps_num = values[0]; uint8_t in_fps_den = values[1]; uint8_t out_fps_num = values[2]; uint8_t out_fps_den = values[3]; if (!out_fps_num || !out_fps_den || (out_fps_num > 60)) { LOG("ERROR: MPP Encoder: invalid out fps: [%d/%d]\n", out_fps_num, out_fps_den); return false; } if (in_fps_num && in_fps_den) { ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_in_num", in_fps_num); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_in_denorm", in_fps_den); } ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_out_num", out_fps_num); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:fps_out_denorm", out_fps_den); if (ret) { LOG("ERROR: MPP Encoder: fps: cfg set s32 failed ret %d\n", ret); return false; } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: change fps cfg failed!\n"); return false; } if (in_fps_num && in_fps_den) { LOG("MPP Encoder: new fps: [%d/%d]->[%d/%d]\n", in_fps_num, in_fps_den, out_fps_num, out_fps_den); } else { LOG("MPP Encoder: new out fps: [%d/%d]\n", out_fps_num, out_fps_den); } vconfig.frame_rate = out_fps_num; } else if (change & VideoEncoder::kBitRateChange) { int* values = (int*)val->GetPtr(); if (val->GetSize() < 3 * sizeof(int)) { LOG("ERROR: MPP Encoder: fps should be int array[3]:" "{bpsMin, bpsTarget, bpsMax}"); return false; } int bps_min = values[0]; int bps_target = values[1]; int bps_max = values[2]; MppEncRcMode rc_mode = GetMPPRCMode(vconfig.rc_mode); if (rc_mode == MPP_ENC_RC_MODE_BUTT) { LOG("ERROR: MPP Encoder: bps: invalid rc mode %s\n", vconfig.rc_mode); return false; } // Three bit rate configuration methods: // 1. Straight-through mode: all three code rate values must be valid. // 2. Only bps_max: Generate three values based on bps_max. // 3. Only bps_target: Generate three values based on bps_target. if (bps_max && bps_min && bps_target) { if ((bps_max > MPP_MAX_BPS_VALUE) || (bps_max < MPP_MIN_BPS_VALUE) || (bps_min > MPP_MAX_BPS_VALUE) || (bps_min < MPP_MIN_BPS_VALUE) || (bps_target > bps_max) || (bps_target < bps_min)) { ret = -1; } } else if (bps_max && !bps_target && !bps_min) { ret = CalcMppBpsWithMax(rc_mode, bps_max, bps_min, bps_target); } else if (bps_target && !bps_max && !bps_min) { ret = CalcMppBpsWithTarget(rc_mode, bps_max, bps_min, bps_target); } else { ret = -1; } if (ret < 0) { LOG("ERROR: MPP Encoder: Invalid bps:[%d, %d, %d]\n", bps_min, bps_target, bps_max); return false; } ret = 0; LOG("MPP Encoder: new bps:[%d, %d, %d]\n", bps_min, bps_target, bps_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_min", bps_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_max", bps_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_target", bps_target); if (ret) { LOG("ERROR: MPP Encoder: bps: cfg set s32 failed ret %d\n", ret); return false; } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: change bps cfg failed!\n"); return false; } // save new value to config. vconfig.bit_rate = bps_target; vconfig.bit_rate_max = bps_max; vconfig.bit_rate_min = bps_min; } else if (change & VideoEncoder::kRcModeChange) { char* new_mode = (char*)val->GetPtr(); LOG("MPP Encoder: new rc_mode:%s\n", new_mode); MppEncRcMode rc_mode = GetMPPRCMode(new_mode); if (rc_mode == MPP_ENC_RC_MODE_BUTT) { LOG("ERROR: MPP Encoder: rc_mode is invalid! should be cbr/vbr.\n"); return false; } // Recalculate bps int bps_max = vconfig.bit_rate_max; int bps_min = bps_max; int bps_target = bps_max; if (CalcMppBpsWithMax(rc_mode, bps_max, bps_min, bps_target) < 0) { return false; } ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:mode", rc_mode); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_min", bps_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_max", bps_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_target", bps_target); if (ret) { LOG("ERROR: MPP Encoder: rc mode: cfg set s32 failed ret %d\n", ret); return false; } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: change rc_mode cfg failed!\n"); return false; } // save new value to encoder->vconfig. vconfig.rc_mode = ConvertRcMode(new_mode); ; } else if (change & VideoEncoder::kProfileChange) { if (val->GetSize() < 2 * sizeof(int)) { LOG("ERROR: MPP Encoder: fps should be int array[2]:" "{profile_idc, level}"); return false; } int profile_idc = *((int*)val->GetPtr()); int level = *((int*)val->GetPtr() + 1); LOG("MPP Encoder: new profile_idc:%d, level:%d\n", profile_idc, level); if (vconfig.image_cfg.codec_type != CODEC_TYPE_H264) { LOG("ERROR: MPP Encoder: Current codec:%d not support Profile change.\n", vconfig.image_cfg.codec_type); return false; } // H.264 profile_idc parameter // 66 - Baseline profile // 77 - Main profile // 100 - High profile if ((profile_idc != 66) && (profile_idc != 77) && (profile_idc != 100)) { LOG("MPP Encoder: Invalid H264 profile(%d)\n", profile_idc); return false; } ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:profile", profile_idc); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:cabac_en", (profile_idc == 100) ? 1 : 0); // H.264 level_idc parameter // 10 / 11 / 12 / 13 - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps // 20 / 21 / 22 - cif@30fps / half-D1@@25fps / D1@12.5fps // 30 / 31 / 32 - D1@25fps / 720p@30fps / 720p@60fps // 40 / 41 / 42 - 1080p@30fps / 1080p@30fps / 1080p@60fps // 50 / 51 / 52 - 4K@30fps ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:level", level); if (ret) { LOG("ERROR: MPP Encoder: profile: cfg set s32 failed ret %d\n", ret); return false; } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: change rc_mode cfg failed!\n"); return false; } // save new value to encoder->vconfig. vconfig.profile = profile_idc; vconfig.level = level; } else if (change & VideoEncoder::kRcQualityChange) { char* rc_quality = (char*)val->GetPtr(); VideoEncoderQp qps; if (strcmp(vconfig.rc_mode, KEY_VBR)) { LOG("ERROR: MPP Encoder: only vbr mode support rcQuality changes!\n"); return false; } if (CalcQpWithRcQuality(rc_quality, qps)) { return false; } LOG("MPP Encoder: [%s:%s->%s] init:%d, setp:%d, min:%d, " "max:%d, min_i:%d, max_i:%d\n", vconfig.rc_mode, vconfig.rc_quality, rc_quality, qps.qp_init, qps.qp_step, qps.qp_min, qps.qp_max, qps.qp_min_i, qps.qp_max_i); if (code_type == MPP_VIDEO_CodingAVC) { ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_init", qps.qp_init); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_max", qps.qp_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_min", qps.qp_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_step", qps.qp_step); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_max_i", qps.qp_max_i); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_min_i", qps.qp_min_i); } else if (code_type == MPP_VIDEO_CodingHEVC) { ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_init", qps.qp_init); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_max", qps.qp_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_min", qps.qp_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_step", qps.qp_step); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_max_i", qps.qp_max_i); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_min_i", qps.qp_min_i); // when qp changes qp_delta_ip will reset 0, // so we should set default 3 again. ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_delta_ip", 3); } if (ret) { LOG("ERROR: MPP Encoder: qp: cfg set s32 failed ret %d\n", ret); return false; } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: change qp cfg failed!\n"); return false; } vconfig.qp_init = qps.qp_init; vconfig.qp_min = qps.qp_min; vconfig.qp_max = qps.qp_max; vconfig.qp_step = qps.qp_step; vconfig.qp_max_i = qps.qp_max_i; vconfig.qp_min_i = qps.qp_min_i; vconfig.rc_quality = ConvertRcQuality(rc_quality); } else if (change & VideoEncoder::kGopChange) { int new_gop_size = val->GetValue(); if (new_gop_size < 0) { LOG("ERROR: MPP Encoder: gop size invalid!\n"); return false; } LOG("MPP Encoder: gop change frome %d to %d\n", vconfig.gop_size, new_gop_size); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:gop", new_gop_size); if (ret) { LOG("ERROR: MPP Encoder: gop: cfg set s32 failed ret %d\n", ret); return false; } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: change gop cfg failed!\n"); return false; } // save to vconfig vconfig.gop_size = new_gop_size; // gop change restart userata status mpp_enc.RestartUserData(); } else if (change & VideoEncoder::kQPChange) { VideoEncoderQp* qps = (VideoEncoderQp*)val->GetPtr(); if (val->GetSize() < sizeof(VideoEncoderQp)) { LOG("ERROR: MPP Encoder: Incomplete VideoEncoderQp information\n"); return false; } LOG("MPP Encoder: new qp:[%d, %d, %d, %d, %d, %d]\n", qps->qp_init, qps->qp_step, qps->qp_min, qps->qp_max, qps->qp_min_i, qps->qp_max_i); if (code_type == MPP_VIDEO_CodingAVC) { ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_init", qps->qp_init); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_max", qps->qp_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_min", qps->qp_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_step", qps->qp_step); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_max_i", qps->qp_max_i); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_min_i", qps->qp_min_i); } else if (code_type == MPP_VIDEO_CodingHEVC) { ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_init", qps->qp_init); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_max", qps->qp_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_min", qps->qp_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_step", qps->qp_step); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_max_i", qps->qp_max_i); ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_min_i", qps->qp_min_i); // when qp changes qp_delta_ip will reset 0, // so we should set default 3 again. ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_delta_ip", 3); } if (ret) { LOG("ERROR: MPP Encoder: qp: cfg set s32 failed ret %d\n", ret); return false; } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: change qp cfg failed!\n"); return false; } vconfig.qp_init = qps->qp_init; vconfig.qp_min = qps->qp_min; vconfig.qp_max = qps->qp_max; vconfig.qp_step = qps->qp_step; vconfig.qp_max_i = qps->qp_max_i; vconfig.qp_min_i = qps->qp_min_i; } else if (change & VideoEncoder::kROICfgChange) { EncROIRegion* regions = (EncROIRegion*)val->GetPtr(); if (val->GetSize() && (val->GetSize() < sizeof(EncROIRegion))) { LOG("ERROR: MPP Encoder: ParameterBuffer size is invalid!\n"); return false; } int region_cnt = val->GetSize() / sizeof(EncROIRegion); mpp_enc.RoiUpdateRegions(regions, region_cnt); } else if (change & VideoEncoder::kForceIdrFrame) { LOG("MPP Encoder: force idr frame...\n"); if (mpp_enc.EncodeControl(MPP_ENC_SET_IDR_FRAME, nullptr) != 0) { LOG("ERROR: MPP Encoder: force idr frame control failed!\n"); return false; } // force idr frame, restart userata status mpp_enc.RestartUserData(); } else if (change & VideoEncoder::kSplitChange) { if (val->GetSize() < (2 * sizeof(int))) { LOG("ERROR: MPP Encoder: Incomplete split information\n"); return false; } RK_U32 split_mode = *((unsigned int*)val->GetPtr()); RK_U32 split_arg = *((unsigned int*)val->GetPtr() + 1); LOG("MPP Encoder: split_mode:%u, split_arg:%u\n", split_mode, split_arg); ret |= mpp_enc_cfg_set_u32(enc_cfg, "split:mode", split_mode); ret |= mpp_enc_cfg_set_u32(enc_cfg, "split:arg", split_arg); if (ret) { LOG("ERROR: MPP Encoder: split: cfg set s32 failed ret %d\n", ret); return false; } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: set split mode failed!\n"); return false; } } else if (change & VideoEncoder::kGopModeChange) { if (val->GetSize() < sizeof(EncGopModeParam)) { LOG("ERROR: MPP Encoder: Incomplete gop mode params\n"); return false; } EncGopModeParam* gop_param = (EncGopModeParam*)val->GetPtr(); EncGopMode gop_mode = gop_param->mode; MppEncRefCfg ref = NULL; switch (gop_mode) { case GOP_MODE_TSVC2: case GOP_MODE_TSVC3: case GOP_MODE_TSVC4: { LOG("MPP Encoder: Set GopMode to \"TSVC\" mode...\n"); if (mpp_enc_ref_cfg_init(&ref)) { LOG("ERROR: MPP Encoder: ref cfg init failed!\n"); return false; } if (mpi_enc_gen_ref_cfg(ref, gop_mode)) { LOG("ERROR: MPP Encoder: ref cfg gen failed!\n"); mpp_enc_ref_cfg_deinit(&ref); return false; } ret = mpp_enc.EncodeControl(MPP_ENC_SET_REF_CFG, ref); mpp_enc_ref_cfg_deinit(&ref); } break; case GOP_MODE_SMARTP: { /********************************************************* * Set Gop Mode * *******************************************************/ // virtual intra frame gap. int smartp_vi_len = gop_param->gop_size; int smartp_gop_len = gop_param->interval; int smartp_qp_delta = gop_param->ip_qp_delta; LOG("MPP Encoder: Set GopMode to \"SMARTP\" mode. " "gop_size:%d, interval:%d, ip_qp_delta:%d...\n", gop_param->gop_size, gop_param->interval, gop_param->ip_qp_delta); if (mpp_enc_ref_cfg_init(&ref)) { LOG("ERROR: MPP Encoder: ref cfg init failed!\n"); return false; } if (mpi_enc_gen_ref_cfg(ref, gop_mode, smartp_gop_len, smartp_vi_len)) { LOG("ERROR: MPP Encoder: ref cfg gen failed!\n"); mpp_enc_ref_cfg_deinit(&ref); return false; } ret = mpp_enc.EncodeControl(MPP_ENC_SET_REF_CFG, ref); mpp_enc_ref_cfg_deinit(&ref); /********************************************************* * Set Gop size * *******************************************************/ ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:gop", smartp_gop_len); if (ret) { LOG("ERROR: MPP Encoder: gop mode: cfg set s32 failed ret %d\n", ret); return false; } /********************************************************* * Set qp delta * *******************************************************/ if (vconfig.image_cfg.codec_type == CODEC_TYPE_H265) { ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_delta_ip", smartp_qp_delta); if (ret) { LOG("ERROR: MPP Encoder: gop mode: cfg set s32 failed ret %d\n", ret); return false; } } else if (vconfig.image_cfg.codec_type == CODEC_TYPE_H264) { ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_delta_ip", smartp_qp_delta); if (ret) { LOG("ERROR: MPP Encoder: gop mode: cfg set s32 failed ret %d\n", ret); return false; } } /********************************************************* * Set vi gop size * *******************************************************/ // ToDo... if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: change gop cfg failed!\n"); return false; } } break; case GOP_MODE_NORMALP: { LOG("MPP Encoder: Set GopMode to \"NORMALP\" mode...\n"); ret = mpp_enc.EncodeControl(MPP_ENC_SET_REF_CFG, NULL); /********************************************************* * Reset Gop size * *******************************************************/ ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:gop", vconfig.gop_size); if (ret) { LOG("ERROR: MPP Encoder: gop mode: cfg set s32 failed ret %d\n", ret); return false; } /********************************************************* * Set qp delta * *******************************************************/ int normal_qp_delta = gop_param->ip_qp_delta; if (vconfig.image_cfg.codec_type == CODEC_TYPE_H265) { ret |= mpp_enc_cfg_set_s32(enc_cfg, "h265:qp_delta_ip", normal_qp_delta); if (ret) { LOG("ERROR: MPP Encoder: gop mode: cfg set s32 failed ret %d\n", ret); return false; } } else if (vconfig.image_cfg.codec_type == CODEC_TYPE_H264) { ret |= mpp_enc_cfg_set_s32(enc_cfg, "h264:qp_delta_ip", normal_qp_delta); if (ret) { LOG("ERROR: MPP Encoder: gop mode: cfg set s32 failed ret %d\n", ret); return false; } } if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: change gop cfg failed!\n"); return false; } } break; default: LOG("ERROR: MPP Encoder: gop mode: unsupport mode value(%d)!\n", gop_mode); return false; } if (ret) { LOG("ERROR: MPP Encoder: set gop mode failed!\n"); return false; } } #ifdef MPP_SUPPORT_HW_OSD else if (change & VideoEncoder::kOSDDataChange) { // type: OsdRegionData* LOGD("MPP Encoder: config osd regions\n"); if (val->GetSize() < sizeof(OsdRegionData)) { LOG("ERROR: MPP Encoder: palette buff should be OsdRegionData type\n"); return false; } OsdRegionData* param = (OsdRegionData*)val->GetPtr(); if (mpp_enc.OsdRegionSet(param)) { LOG("ERROR: MPP Encoder: set osd regions error!\n"); return false; } } else if (change & VideoEncoder::kOSDPltChange) { // type: 265 * U32 array. LOG("MPP Encoder: config osd palette\n"); if (val->GetSize() < (sizeof(int) * 4)) { LOG("ERROR: MPP Encoder: palette buff should be U32 * 256\n"); return false; } uint32_t* param = (uint32_t*)val->GetPtr(); if (mpp_enc.OsdPaletteSet(param)) { LOG("ERROR: MPP Encoder: set Palette error!\n"); return false; } } #endif else if (change & VideoEncoder::kUserDataChange) { // type: OsdRegionData* LOGD("MPP Encoder: config userdata\n"); if (val->GetSize() <= 0) { LOG("ERROR: MPP Encoder: invalid userdata size\n"); return false; } uint8_t enable_all_frames = *(uint8_t*)val->GetPtr(); mpp_enc.EnableUserDataAllFrame(enable_all_frames ? true : false); const char* data = (char*)val->GetPtr() + 1; uint16_t data_len = val->GetSize() - 1; if (mpp_enc.SetUserData(data, data_len)) { LOG("ERROR: MPP Encoder: set userdata error!\n"); return false; } } else if (change & VideoEncoder::kMoveDetectionFlow) { RcApiBrief brief; if (val->GetPtr()) { #if 0 int bps_max = vconfig.bit_rate_max; int bps_min = vconfig.bit_rate_min; int bps_target = vconfig.bit_rate; int w = vconfig.image_cfg.image_info.vir_width; int h = vconfig.image_cfg.image_info.vir_height; float bps_factor = smart_enc_mode_get_bps_factor(vconfig.bit_rate_max, w, h); bps_min = (int)(vconfig.bit_rate_max * bps_factor); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:mode", MPP_ENC_RC_MODE_VBR); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_min", bps_min); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_max", bps_max); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:bps_target", bps_target); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:gop", 300); LOG("MPP Encoder: smart enc mode: factor:%f, bps:[%d,%d,%d] gop:%d\n", bps_factor, bps_max, bps_target, bps_min, 300); if(mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: rc control for smart enc failed!\n"); return false; } //save to vconfig vconfig.gop_size = 300; vconfig.rc_mode = KEY_VBR; vconfig.bit_rate_min = bps_min; #endif ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:mode", MPP_ENC_RC_MODE_VBR); ret |= mpp_enc_cfg_set_s32(enc_cfg, "rc:gop", 300); if (mpp_enc.EncodeControl(MPP_ENC_SET_CFG, enc_cfg) != 0) { LOG("ERROR: MPP Encoder: rc control for smart enc failed!\n"); return false; } // save to vconfig vconfig.gop_size = 300; vconfig.rc_mode = KEY_VBR; // gop change restart userata status mpp_enc.RestartUserData(); // Enable smart mode. brief.name = "smart"; brief.type = code_type; LOG("MPP Encoder: enable smart enc mode...\n"); } else { brief.name = "defalut"; brief.type = code_type; LOG("MPP Encoder: disable smart enc mode...\n"); } if (mpp_enc.EncodeControl(MPP_ENC_SET_RC_API_CURRENT, &brief) != 0) { LOG("ERROR: MPP Encoder: enable smart enc control failed!\n"); return false; } mpp_enc.rc_api_brief_name = brief.name; } else { LOG("Unsupport request change 0x%08x!\n", change); return false; } return true; } class MPPFinalEncoder : public MPPEncoder { public: MPPFinalEncoder(const char* param); virtual ~MPPFinalEncoder() { if (mpp_config) { delete mpp_config; } } static const char* GetCodecName() { return "rkmpp"; } virtual bool InitConfig(const MediaConfig& cfg) override; protected: // Change configs which are not contained in sps/pps. virtual bool CheckConfigChange(std::pair>) override; MPPConfig* mpp_config; }; MPPFinalEncoder::MPPFinalEncoder(const char* param) : mpp_config(nullptr) { std::string output_data_type = get_media_value_by_key(param, KEY_OUTPUTDATATYPE); SetMppCodeingType(output_data_type.empty() ? MPP_VIDEO_CodingUnused : GetMPPCodingType(output_data_type)); } bool MPPFinalEncoder::InitConfig(const MediaConfig& cfg) { assert(!mpp_config); MediaConfig new_cfg = cfg; switch (coding_type) { case MPP_VIDEO_CodingMJPEG: mpp_config = new MPPMJPEGConfig(); new_cfg.img_cfg.codec_type = codec_type; break; case MPP_VIDEO_CodingAVC: case MPP_VIDEO_CodingHEVC: new_cfg.vid_cfg.image_cfg.codec_type = codec_type; mpp_config = new MPPCommonConfig(coding_type); break; default: LOG("Unsupport mpp encode type: %d\n", coding_type); return false; } if (!mpp_config) { LOG_NO_MEMORY(); return false; } return mpp_config->InitConfig(*this, new_cfg); } bool MPPFinalEncoder::CheckConfigChange(std::pair> change_pair) { // common ConfigChange process if (change_pair.first & VideoEncoder::kEnableStatistics) { bool value = (change_pair.second->GetValue()) ? true : false; set_statistics_switch(value); return true; } assert(mpp_config); if (!mpp_config) { return false; } return mpp_config->CheckConfigChange(*this, change_pair.first, change_pair.second); } DEFINE_VIDEO_ENCODER_FACTORY(MPPFinalEncoder) const char* FACTORY(MPPFinalEncoder)::ExpectedInputDataType() { return MppAcceptImageFmts(); } #define VIDEO_ENC_OUTPUT \ TYPENEAR(VIDEO_H264) \ TYPENEAR(VIDEO_H265) TYPENEAR(VIDEO_MJPEG) TYPENEAR(IMAGE_JPEG) const char* FACTORY(MPPFinalEncoder)::OutPutDataType() { return VIDEO_ENC_OUTPUT; } } // namespace easymedia