* -------------------------------------------------------------------------
* This file is part of the Vision SDK project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* Vision SDK is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
* Description: Checker of the Video Encode.
* Author: MindX SDK
* Create: 2024
* History: NA
*/
#ifndef MXBASE_VIDEOENCODERCHECK_H
#define MXBASE_VIDEOENCODERCHECK_H
#include "MxBase/E2eInfer/VideoEncoder/VideoEncoder.h"
namespace MxBase {
constexpr uint32_t MAX_FIRST_FRAME_START_QP = 51;
constexpr uint32_t CVBR_MAX_QP = 47;
constexpr uint32_t CVBR_MODE_VALUE = 5;
constexpr uint32_t MAX_CACHE_COUNT = 256;
struct Config310 {
size_t inputWidthMin = 128;
size_t inputWidthMax = 1920;
size_t inputHeightMin = 128;
size_t inputHeightMax = 1920;
size_t rcModeMin = 0;
size_t rcModeMax = 2;
size_t ipPropMin = 1;
size_t ipPropMax = 100;
size_t keyFrameIntervalMin = 1;
size_t keyFrameIntervalMax = 2000;
size_t srcRateMin = 1;
size_t srcRateMax = 120;
size_t maxBitRateMin = 10;
size_t maxBitRateMax = 30000;
size_t widthStride = 16;
size_t heightStride = 2;
std::vector<ImageFormat> formats = {ImageFormat::YUV_SP_420, ImageFormat::YVU_SP_420};
};
struct Config310B {
size_t inputWidthMin = 114;
size_t inputWidthMax = 8192;
size_t inputHeightMin = 114;
size_t inputHeightMax = 8192;
size_t rcModeMin = 0;
size_t rcModeMax = 2;
size_t ipPropMin = 1;
size_t ipPropMax = 100;
size_t keyFrameIntervalMin = 1;
size_t keyFrameIntervalMax = 65536;
size_t srcRateMin = 1;
size_t srcRateMax = 240;
size_t maxBitRateMin = 2;
size_t maxBitRateMax = 614400;
size_t widthStride = 2;
size_t heightStride = 2;
std::vector<ImageFormat> formats = {ImageFormat::YUV_SP_420, ImageFormat::YVU_SP_420};
};
struct Config310P {
size_t channelIdMax = 127;
size_t inputWidthMin = 128;
size_t inputWidthMax = 4096;
size_t inputHeightMin = 128;
size_t inputHeightMax = 4096;
size_t maxArea = 4096 * 2304;
size_t displayRateMin = 1;
size_t displayRateMax = 120;
size_t rcModeMin = 0;
size_t rcModeMax = 5;
size_t ipPropMin = 1;
size_t ipPropMax = 100;
size_t sceneModeMin = 0;
size_t sceneModeMax = 1;
size_t keyFrameIntervalMin = 1;
size_t keyFrameIntervalMax = 65536;
size_t srcRateMin = 1;
size_t srcRateMax = 240;
size_t maxBitRateMin = 2;
size_t maxBitRateMax = 614400;
size_t statsTimeMin = 1;
size_t statsTimeMax = 60;
size_t directionMin = 0;
size_t directionMax = 16;
size_t rowQpDeltaMin = 0;
size_t rowQpDeltaMax = 10;
size_t thresholdMin = 0;
size_t thresholdMax = 255;
size_t thresholdSize = 16;
size_t shortTermStatsTimeMin = 1;
size_t shortTermStatsTimeMax = 120;
size_t longTermStatsTimeMin = 1;
size_t longTermStatsTimeMax = 1440;
size_t longTermMaxBitRateMin = 2;
size_t longTermMinBitRateMin = 0;
size_t widthStride = 2;
size_t heightStride = 2;
const std::map<uint32_t, uint32_t> MIN_QP_MAP = {
{0, 10},
{1, 10},
{2, 24},
{3, 24},
{4, 16},
{5, 20}
};
std::vector<ImageFormat> formats = {ImageFormat::YUV_SP_420, ImageFormat::YVU_SP_420, ImageFormat::RGB_888,
ImageFormat::BGR_888};
};
class Checker {
public:
Checker() = default;
virtual ~Checker() = default;
APP_ERROR InputImageCheck(const VideoEncodeConfig& input, const Image &inputImage, int32_t deviceId)
{
if (inputImage.GetDeviceId() == -1) {
LogError << "Input image cannot be on the host, please check."
<< GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
} else if (inputImage.GetDeviceId() != deviceId) {
LogError << "Input image get wrong deviceId(" << inputImage.GetDeviceId()
<< "), which should be equal to VideoEncoder(" << deviceId << ")."
<< GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (inputImage.GetFormat() != input.inputImageFormat) {
LogError << "input image get wrong format(" << IMAGE_FORMAT_STRING.at(inputImage.GetFormat())
<< "), which should be " << IMAGE_FORMAT_STRING.at(input.inputImageFormat) << "."
<< GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (inputImage.GetData() == nullptr) {
LogError << "input image data is nullptr." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
return APP_ERR_OK;
}
protected:
template<class ConfigType>
APP_ERROR BaseCheck(const ConfigType& config, const VideoEncodeConfig& input)
{
if (input.callbackFunc == nullptr) {
LogError << "Input param(callbackFunc) in VideoEncodeConfig is invalid, which should not be nullptr"
<< GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("width", input.width, config.inputWidthMin, config.inputWidthMax) ||
(input.width % config.widthStride != 0)) {
LogError << "Or width should be stride with " << config.widthStride << "."
<< GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("height", input.height, config.inputHeightMin, config.inputHeightMax) ||
(input.height % config.heightStride != 0)) {
LogError << "Or height should be stride with " << config.heightStride << "."
<< GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("srcRate", input.srcRate, config.srcRateMin, config.srcRateMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("maxBitRate", input.maxBitRate, config.maxBitRateMin, config.maxBitRateMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("rcMode", input.rcMode, config.rcModeMin, config.rcModeMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("ipProp", input.ipProp, config.ipPropMin, config.ipPropMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("keyFrameInterval", input.keyFrameInterval, config.keyFrameIntervalMin,
config.keyFrameIntervalMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
for (const auto& iter: config.formats) {
if (iter == input.inputImageFormat) {
return APP_ERR_OK;
}
}
LogError << "Input param(inputImageFormat) in VideoEncodeConfig is invalid("
<< IMAGE_FORMAT_STRING.at(input.inputImageFormat)
<< "), which should be YUV_SP_420, YVU_SP_420, RGB_888, BGR_888 for 310P or YUV_SP_420, "
"YVU_SP_420 for 310 or 310B."
<< GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
bool CheckRange(const std::string paramName, uint32_t input, uint32_t min, uint32_t max) const
{
if (input < min || input > max) {
LogError << "Input param(" << paramName << ") is invalid(" << input
<< "), which should be in range[" << min << ", " << max << "]."
<< GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return true;
}
return false;
}
};
class Checker310 : public Checker {
public:
APP_ERROR Check(const Config310& config, const VideoEncodeConfig& input)
{
return BaseCheck(config, input);
}
};
class Checker310B : public Checker {
public:
APP_ERROR Check(const Config310B& config, const VideoEncodeConfig& input)
{
return BaseCheck(config, input);
}
};
class Checker310P : public Checker {
public:
APP_ERROR Check(const Config310P& config, const VideoEncodeConfig& input, uint32_t channelId)
{
APP_ERROR ret = BaseCheck(config, input);
if (ret != APP_ERR_OK) {
return ret;
}
if (CheckRange("channelId", channelId, 0, config.channelIdMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckHeightAndWidth(config, input) != APP_ERR_OK) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("displayRate", input.displayRate, config.displayRateMin, config.displayRateMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("sceneMode", input.sceneMode, config.sceneModeMin, config.sceneModeMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("statsTime", input.statsTime, config.statsTimeMin, config.statsTimeMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("direction", input.direction, config.directionMin, config.directionMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("rowQpDelta", input.rowQpDelta, config.rowQpDeltaMin, config.rowQpDeltaMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (input.rcMode == CVBR_MODE_VALUE) {
if (CheckRange("shortTermStatsTime", input.shortTermStatsTime, config.shortTermStatsTimeMin,
config.shortTermStatsTimeMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("longTermStatsTime", input.longTermStatsTime, config.longTermStatsTimeMin,
config.longTermStatsTimeMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("longTermMaxBitRate", input.longTermMaxBitRate, config.longTermMaxBitRateMin,
input.maxBitRate)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("longTermMinBitRate", input.longTermMinBitRate, config.longTermMinBitRateMin,
input.longTermMaxBitRate)) {
return APP_ERR_COMM_INVALID_PARAM;
}
}
if (CheckThresholdIBP(config, input)) {
return APP_ERR_COMM_INVALID_PARAM;
}
return CheckFirstFrameQpParam(config, input);
}
private:
APP_ERROR CheckThresholdIBP(const Config310P& config, const VideoEncodeConfig& input)
{
if (input.thresholdI.size() != config.thresholdSize || input.thresholdP.size() != config.thresholdSize ||
input.thresholdB.size() != config.thresholdSize) {
LogError << "The size of [thresholdI/thresholdP/thresholdB] must equal to"
<< config.thresholdSize << "." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
for (uint32_t i = 0; i < config.thresholdSize; i++) {
if (CheckRange("thresholdI[" + std::to_string(i) + "]", input.thresholdI[i], config.thresholdMin,
config.thresholdMax) ||
CheckRange("thresholdP[" + std::to_string(i) + "]", input.thresholdP[i], config.thresholdMin,
config.thresholdMax) ||
CheckRange("thresholdB[" + std::to_string(i) + "]", input.thresholdB[i], config.thresholdMin,
config.thresholdMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
}
return APP_ERR_OK;
}
APP_ERROR CheckFirstFrameQpParam(const Config310P& config, const VideoEncodeConfig& input)
{
uint32_t maxQpValue = MAX_FIRST_FRAME_START_QP;
if (input.rcMode == CVBR_MODE_VALUE) {
maxQpValue = CVBR_MAX_QP;
}
auto iter = config.MIN_QP_MAP.find(input.rcMode);
if (iter == config.MIN_QP_MAP.end()) {
LogError << "Wrong rcMode, support range is [" << config.rcModeMin << ", " << config.rcModeMax
<< "], please check." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("firstFrameStartQp", input.firstFrameStartQp, iter->second, maxQpValue)) {
return APP_ERR_COMM_INVALID_PARAM;
}
return APP_ERR_OK;
}
APP_ERROR CheckHeightAndWidth(const Config310P& config, const VideoEncodeConfig& input)
{
if (input.maxPicHeight < input.height || input.maxPicWidth < input.width) {
LogError << "The maxPicHeight should be large than or equal to the height and "
<< "the maxPicWidth should be large than or equal to the width."
<< GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if ((input.width * input.height) > config.maxArea) {
LogError << "Input width * height in VideoEncodeConfig should not be bigger than " << config.maxArea
<< "." << GetErrorInfo(APP_ERR_COMM_INVALID_PARAM);
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("maxPicWidth", input.maxPicWidth, config.inputWidthMin, config.inputWidthMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
if (CheckRange("maxPicHeight", input.maxPicHeight, config.inputHeightMin, config.inputHeightMax)) {
return APP_ERR_COMM_INVALID_PARAM;
}
return APP_ERR_OK;
}
};
}
#endif