android13/vendor/rockchip/hardware/interfaces/codec2/component/mpi/C2RKMpiEnc.cpp

2590 lines
88 KiB
C++
Executable File

/*
* Copyright (C) 2020 Rockchip Electronics Co. LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#undef ROCKCHIP_LOG_TAG
#define ROCKCHIP_LOG_TAG "C2RKMpiEnc"
#include <stdio.h>
#include <Codec2Mapper.h>
#include <C2PlatformSupport.h>
#include <Codec2BufferUtils.h>
#include <C2RKInterface.h>
#include <util/C2InterfaceHelper.h>
#include <C2AllocatorGralloc.h>
#include <ui/GraphicBufferMapper.h>
#include <ui/GraphicBufferAllocator.h>
#include <gralloc_priv_omx.h>
#include <sys/syscall.h>
#include "hardware/hardware_rockchip.h"
#include "hardware/gralloc_rockchip.h"
#include "C2RKMpiEnc.h"
#include "C2RKMediaUtils.h"
#include "C2RKRgaDef.h"
#include "C2RKLog.h"
#include "C2RKEnv.h"
#include "C2RKExtendParam.h"
#include "C2RKCodecMapper.h"
#include "C2RKVersion.h"
#include "C2RKChips.h"
namespace android {
namespace {
void ParseGop(
const C2StreamGopTuning::output &gop,
uint32_t *syncInterval, uint32_t *iInterval, uint32_t *maxBframes) {
uint32_t syncInt = 1;
uint32_t iInt = 1;
for (size_t i = 0; i < gop.flexCount(); ++i) {
const C2GopLayerStruct &layer = gop.m.values[i];
if (layer.count == UINT32_MAX) {
syncInt = 0;
} else if (syncInt <= UINT32_MAX / (layer.count + 1)) {
syncInt *= (layer.count + 1);
}
if ((layer.type_ & I_FRAME) == 0) {
if (layer.count == UINT32_MAX) {
iInt = 0;
} else if (iInt <= UINT32_MAX / (layer.count + 1)) {
iInt *= (layer.count + 1);
}
}
if (layer.type_ == C2Config::picture_type_t(P_FRAME | B_FRAME) && maxBframes) {
*maxBframes = layer.count;
}
}
if (syncInterval) {
*syncInterval = syncInt;
}
if (iInterval) {
*iInterval = iInt;
}
}
} // namepsace
struct MlvecParams {
std::shared_ptr<C2DriverVersion::output> driverInfo;
std::shared_ptr<C2MaxLayerCount::output> maxLayerCount;
std::shared_ptr<C2LowLatencyMode::output> lowLatencyMode;
std::shared_ptr<C2MaxLTRFramesCount::output> maxLTRFramesCount;
std::shared_ptr<C2PreOPSupport::output> preOPSupport;
std::shared_ptr<C2MProfileLevel::output> profileLevel;
std::shared_ptr<C2SliceSpacing::output> sliceSpacing;
std::shared_ptr<C2RateControl::output> rateControl;
std::shared_ptr<C2NumLTRFrms::output> numLTRFrms;
std::shared_ptr<C2SarSize::output> sarSize;
std::shared_ptr<C2InputQueuCtl::output> inputQueueCtl;
std::shared_ptr<C2LtrCtlMark::input> ltrMarkFrmCtl;
std::shared_ptr<C2LtrCtlUse::input> ltrUseFrmCtl;
std::shared_ptr<C2FrameQPCtl::input> frameQPCtl;
std::shared_ptr<C2BaseLayerPid::input> baseLayerPid;
std::shared_ptr<C2TriggerTime::input> triggerTime;
};
class C2RKMpiEnc::IntfImpl : public C2RKInterface<void>::BaseParams {
public:
explicit IntfImpl(
const std::shared_ptr<C2ReflectorHelper> &helper,
C2String name,
C2Component::kind_t kind,
C2Component::domain_t domain,
C2String mediaType)
: C2RKInterface<void>::BaseParams(
helper,
name,
kind,
domain,
mediaType) {
noPrivateBuffers(); // TODO: account for our buffers here
noInputReferences();
noOutputReferences();
noTimeStretch();
setDerivedInstance(this);
mMlvecParams = std::make_shared<MlvecParams>();
addParameter(
DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
.withConstValue(new C2StreamUsageTuning::input(
0u, 0u))
.build());
addParameter(
DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
.withConstValue(new C2ComponentAttributesSetting(
C2Component::ATTRIB_IS_TEMPORAL))
.build());
addParameter(
DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
.withDefault(new C2StreamPictureSizeInfo::input(0u, 176, 144))
.withFields({
C2F(mSize, width).inRange(90, 7680, 2),
C2F(mSize, height).inRange(90, 7680, 2),
})
.withSetter(SizeSetter)
.build());
addParameter(
DefineParam(mGop, C2_PARAMKEY_GOP)
.withDefault(C2StreamGopTuning::output::AllocShared(
0 /* flexCount */, 0u /* stream */))
.withFields({C2F(mGop, m.values[0].type_).any(),
C2F(mGop, m.values[0].count).any()})
.withSetter(GopSetter)
.build());
addParameter(
DefineParam(mPictureQuantization, C2_PARAMKEY_PICTURE_QUANTIZATION)
.withDefault(C2StreamPictureQuantizationTuning::output::AllocShared(
0 /* flexCount */, 0u /* stream */))
.withFields({C2F(mPictureQuantization, m.values[0].type_).oneOf(
{C2Config::picture_type_t(I_FRAME),
C2Config::picture_type_t(P_FRAME),
C2Config::picture_type_t(B_FRAME)}),
C2F(mPictureQuantization, m.values[0].min).any(),
C2F(mPictureQuantization, m.values[0].max).any()})
.withSetter(PictureQuantizationSetter)
.build());
addParameter(
DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY)
.withDefault(new C2PortActualDelayTuning::input(0))
.withFields({C2F(mActualInputDelay, value).inRange(0, 2)})
.calculatedAs(InputDelaySetter, mGop)
.build());
addParameter(
DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
.withDefault(new C2StreamFrameRateInfo::output(0u, 1.))
// TODO: More restriction?
.withFields({C2F(mFrameRate, value).greaterThan(0.)})
.withSetter(Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
.build());
addParameter(
DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
.withDefault(new C2StreamBitrateModeTuning::output(
0u, C2Config::BITRATE_VARIABLE))
.withFields({
C2F(mBitrateMode, value).oneOf({
C2Config::BITRATE_CONST,
C2Config::BITRATE_VARIABLE,
C2Config::BITRATE_IGNORE})
})
.withSetter(
Setter<decltype(*mBitrateMode)>::StrictValueWithNoDeps)
.build());
addParameter(
DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
.withDefault(new C2StreamBitrateInfo::output(0u, 64000))
.withFields({C2F(mBitrate, value).inRange(4096, 10000000)})
.withSetter(BitrateSetter)
.build());
addParameter(
DefineParam(mIntraRefresh, C2_PARAMKEY_INTRA_REFRESH)
.withDefault(new C2StreamIntraRefreshTuning::output(
0u, C2Config::INTRA_REFRESH_DISABLED, 0.))
.withFields({
C2F(mIntraRefresh, mode).oneOf({
C2Config::INTRA_REFRESH_DISABLED, C2Config::INTRA_REFRESH_ARBITRARY }),
C2F(mIntraRefresh, period).any()
})
.withSetter(IntraRefreshSetter)
.build());
if (mediaType == MEDIA_MIMETYPE_VIDEO_AVC) {
addParameter(
DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
.withDefault(new C2StreamProfileLevelInfo::output(
0u, PROFILE_AVC_BASELINE, LEVEL_AVC_3_1))
.withFields({
C2F(mProfileLevel, profile).oneOf({
PROFILE_AVC_BASELINE,
PROFILE_AVC_MAIN,
PROFILE_AVC_HIGH,
}),
C2F(mProfileLevel, level).oneOf({
LEVEL_AVC_1,
LEVEL_AVC_1B,
LEVEL_AVC_1_1,
LEVEL_AVC_1_2,
LEVEL_AVC_1_3,
LEVEL_AVC_2,
LEVEL_AVC_2_1,
LEVEL_AVC_2_2,
LEVEL_AVC_3,
LEVEL_AVC_3_1,
LEVEL_AVC_3_2,
LEVEL_AVC_4,
LEVEL_AVC_4_1,
LEVEL_AVC_4_2,
LEVEL_AVC_5,
LEVEL_AVC_5_1,
}),
})
.withSetter(AVCProfileLevelSetter, mSize, mFrameRate, mBitrate)
.build());
} else if (mediaType == MEDIA_MIMETYPE_VIDEO_HEVC) {
addParameter(
DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
.withDefault(new C2StreamProfileLevelInfo::output(
0u, PROFILE_HEVC_MAIN, LEVEL_HEVC_MAIN_4_1))
.withFields({
C2F(mProfileLevel, profile).oneOf({
PROFILE_HEVC_MAIN,
PROFILE_HEVC_MAIN_10,
}),
C2F(mProfileLevel, level).oneOf({
LEVEL_HEVC_MAIN_4_1,
}),
})
.withSetter(HEVCProfileLevelSetter, mSize, mFrameRate, mBitrate)
.build());
} else {
addParameter(
DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
.withDefault(new C2StreamProfileLevelInfo::output(
0u, PROFILE_UNUSED, LEVEL_UNUSED))
.withFields({
C2F(mProfileLevel, profile).any(),
C2F(mProfileLevel, level).any(),
})
.withSetter(DefaultProfileLevelSetter, mSize, mFrameRate, mBitrate)
.build());
}
addParameter(
DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
.withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
.withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) })
.withSetter(Setter<decltype(*mRequestSync)>::NonStrictValueWithNoDeps)
.build());
addParameter(
DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL)
.withDefault(new C2StreamSyncFrameIntervalTuning::output(0u, 1000000))
.withFields({C2F(mSyncFramePeriod, value).any()})
.withSetter(Setter<decltype(*mSyncFramePeriod)>::StrictValueWithNoDeps)
.build());
addParameter(
DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
.withDefault(new C2StreamColorAspectsInfo::input(
0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
.withFields({
C2F(mColorAspects, range).inRange(
C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
C2F(mColorAspects, primaries).inRange(
C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
C2F(mColorAspects, transfer).inRange(
C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER),
C2F(mColorAspects, matrix).inRange(
C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)
})
.withSetter(ColorAspectsSetter)
.build());
addParameter(
DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
.withDefault(new C2StreamColorAspectsInfo::output(
0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
.withFields({
C2F(mCodedColorAspects, range).inRange(
C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
C2F(mCodedColorAspects, primaries).inRange(
C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
C2F(mCodedColorAspects, transfer).inRange(
C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER),
C2F(mCodedColorAspects, matrix).inRange(
C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)
})
.withSetter(CodedColorAspectsSetter, mColorAspects)
.build());
addParameter(
DefineParam(mLayering, C2_PARAMKEY_TEMPORAL_LAYERING)
.withDefault(C2StreamTemporalLayeringTuning::output::AllocShared(0u, 0, 0, 0))
.withFields({
C2F(mLayering, m.layerCount).inRange(0, 4),
C2F(mLayering, m.bLayerCount).inRange(0, 0),
C2F(mLayering, m.bitrateRatios).inRange(0., 1.)
})
.withSetter(LayeringSetter)
.build());
addParameter(
DefineParam(mPrependHeaderMode, C2_PARAMKEY_PREPEND_HEADER_MODE)
.withDefault(new C2PrependHeaderModeSetting(PREPEND_HEADER_TO_NONE))
.withFields({C2F(mPrependHeaderMode, value).any()})
.withSetter(PrependHeaderModeSetter)
.build());
/* extend parameter definition */
addParameter(
DefineParam(mSceneMode, C2_PARAMKEY_SCENE_MODE)
.withDefault(new C2StreamSceneModeInfo::input(0))
.withFields({C2F(mSceneMode, value).any()})
.withSetter(Setter<decltype(mSceneMode)::element_type>::StrictValueWithNoDeps)
.build());
addParameter(
DefineParam(mSliceSize, C2_PARAMKEY_SLICE_SIZE)
.withDefault(new C2StreamSliceSizeInfo::input(0))
.withFields({C2F(mSliceSize, value).any()})
.withSetter(Setter<decltype(mSliceSize)::element_type>::StrictValueWithNoDeps)
.build());
addParameter(
DefineParam(mMlvecParams->driverInfo, C2_PARAMKEY_MLVEC_ENC_DRI_VERSION)
.withConstValue(new C2DriverVersion::output(MLVEC_DRIVER_VERSION))
.build());
addParameter(
DefineParam(mMlvecParams->maxLayerCount, C2_PARAMKEY_MLVEC_MAX_TEMPORAL_LAYERS)
.withConstValue(new C2MaxLayerCount::output(MLVEC_MAX_LAYER_COUNT))
.build());
addParameter(
DefineParam(mMlvecParams->lowLatencyMode, C2_PARAMKEY_MLVEC_ENC_LOW_LATENCY_MODE)
.withConstValue(new C2LowLatencyMode::output(MLVEC_LOW_LATENCY_MODE_ENABLE))
.build());
addParameter(
DefineParam(mMlvecParams->maxLTRFramesCount, C2_PARAMKEY_MLVEC_MAX_LTR_FRAMES)
.withConstValue(new C2MaxLTRFramesCount::output(MLVEC_MAX_LTR_FRAMES_COUNT))
.build());
addParameter(
DefineParam(mMlvecParams->preOPSupport, C2_PARAMKEY_MLVEC_PRE_OP)
.withConstValue(new C2PreOPSupport::output(
MLVEC_PRE_PROCESS_SCALE_SUPPORT, MLVEC_PRE_PROCESS_ROTATION_SUPPORT))
.build());
addParameter(
DefineParam(mMlvecParams->profileLevel, C2_PARAMKEY_MLVEC_PROFILE_LEVEL)
.withDefault(new C2MProfileLevel::output(0, 0))
.withFields({
C2F(mMlvecParams->profileLevel, profile).any(),
C2F(mMlvecParams->profileLevel, level).any()
})
.withSetter(MProfileLevelSetter)
.build());
addParameter(
DefineParam(mMlvecParams->sliceSpacing, C2_PARAMKEY_MLVEC_SLICE_SPACING)
.withDefault(new C2SliceSpacing::output(0))
.withFields({C2F(mMlvecParams->sliceSpacing, spacing).any()})
.withSetter(MSliceSpaceSetter)
.build());
addParameter(
DefineParam(mMlvecParams->rateControl, C2_PARAMKEY_MLVEC_RATE_CONTROL)
.withDefault(new C2RateControl::output(-1))
.withFields({C2F(mMlvecParams->rateControl, value).any()})
.withSetter(Setter<decltype(
mMlvecParams->rateControl)::element_type>::StrictValueWithNoDeps)
.build());
addParameter(
DefineParam(mMlvecParams->numLTRFrms, C2_PARAMKEY_MLVEC_NUM_LTR_FRAMES)
.withDefault(new C2NumLTRFrms::output(0))
.withFields({C2F(mMlvecParams->numLTRFrms, num).any()})
.withSetter(MNumLTRFrmsSetter)
.build());
addParameter(
DefineParam(mMlvecParams->sarSize, C2_PARAMKEY_MLVEC_SET_SAR_SIZE)
.withDefault(new C2SarSize::output(0, 0))
.withFields({
C2F(mMlvecParams->sarSize, width).any(),
C2F(mMlvecParams->sarSize, height).any(),
})
.withSetter(MSarSizeSetter)
.build());
addParameter(
DefineParam(mMlvecParams->inputQueueCtl, C2_PARAMKEY_MLVEC_INPUT_QUEUE_CTL)
.withDefault(new C2InputQueuCtl::output(0))
.withFields({C2F(mMlvecParams->inputQueueCtl, enable).oneOf({0, 1})})
.withSetter(MInputQueueCtlSetter)
.build());
addParameter(
DefineParam(mMlvecParams->ltrMarkFrmCtl, C2_PARAMKEY_MLVEC_LTR_CTL_MARK)
.withDefault(new C2LtrCtlMark::input(-1))
.withFields({C2F(mMlvecParams->ltrMarkFrmCtl, markFrame).any()})
.withSetter(MLtrMarkFrmSetter)
.build());
addParameter(
DefineParam(mMlvecParams->ltrUseFrmCtl, C2_PARAMKEY_MLVEC_LTR_CTL_USE)
.withDefault(new C2LtrCtlUse::input(-1))
.withFields({C2F(mMlvecParams->ltrUseFrmCtl, useFrame).any()})
.withSetter(MLtrUseFrmSetter)
.build());
addParameter(
DefineParam(mMlvecParams->frameQPCtl, C2_PARAMKEY_MLVEC_FRAME_QP_CTL)
.withDefault(new C2FrameQPCtl::input(-1))
.withFields({C2F(mMlvecParams->frameQPCtl, value).any()})
.withSetter(Setter<decltype(
mMlvecParams->frameQPCtl)::element_type>::StrictValueWithNoDeps)
.build());
addParameter(
DefineParam(mMlvecParams->baseLayerPid, C2_PARAMKEY_MLVEC_BASE_LAYER_PID)
.withDefault(new C2BaseLayerPid::input(-1))
.withFields({C2F(mMlvecParams->baseLayerPid, value).any()})
.withSetter(Setter<decltype(
mMlvecParams->baseLayerPid)::element_type>::StrictValueWithNoDeps)
.build());
addParameter(
DefineParam(mMlvecParams->triggerTime, C2_PARAMKEY_MLVEC_TRIGGER_TIME)
.withDefault(new C2TriggerTime::input(-1))
.withFields({C2F(mMlvecParams->triggerTime, timestamp).any()})
.withSetter(MTriggerTimeSetter)
.build());
}
static C2R InputDelaySetter(
bool mayBlock,
C2P<C2PortActualDelayTuning::input> &me,
const C2P<C2StreamGopTuning::output> &gop) {
(void)mayBlock;
uint32_t maxBframes = 0;
ParseGop(gop.v, nullptr, nullptr, &maxBframes);
me.set().value = maxBframes;
c2_log_func_enter();
return C2R::Ok();
}
static C2R BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output> &me) {
(void)mayBlock;
C2R res = C2R::Ok();
if (me.v.value <= 4096) {
me.set().value = 4096;
}
return res;
}
static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input> &oldMe,
C2P<C2StreamPictureSizeInfo::input> &me) {
(void)mayBlock;
C2R res = C2R::Ok();
if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
me.set().width = oldMe.v.width;
}
if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
me.set().height = oldMe.v.height;
}
return res;
}
static C2R IntraRefreshSetter(bool mayBlock, C2P<C2StreamIntraRefreshTuning::output> &me) {
(void)mayBlock;
C2R res = C2R::Ok();
if (me.v.period < 1) {
me.set().mode = C2Config::INTRA_REFRESH_DISABLED;
me.set().period = 0;
} else {
// only support arbitrary mode (cyclic in our case)
me.set().mode = C2Config::INTRA_REFRESH_ARBITRARY;
}
return res;
}
static C2R GopSetter(bool mayBlock, C2P<C2StreamGopTuning::output> &me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
static C2R PictureQuantizationSetter(bool mayBlock,
C2P<C2StreamPictureQuantizationTuning::output> &me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
uint32_t getSyncFramePeriod_l() const {
if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) {
return 0;
}
double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
}
static C2R AVCProfileLevelSetter(
bool mayBlock,
C2P<C2StreamProfileLevelInfo::output> &me,
const C2P<C2StreamPictureSizeInfo::input> &size,
const C2P<C2StreamFrameRateInfo::output> &frameRate,
const C2P<C2StreamBitrateInfo::output> &bitrate) {
(void)mayBlock;
if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
me.set().profile = PROFILE_AVC_MAIN;
}
struct LevelLimits {
C2Config::level_t level;
float mbsPerSec;
uint64_t mbs;
uint32_t bitrate;
};
constexpr LevelLimits kLimits[] = {
{ LEVEL_AVC_1, 1485, 99, 64000 },
// Decoder does not properly handle level 1b.
// { LEVEL_AVC_1B, 1485, 99, 128000 },
{ LEVEL_AVC_1_1, 3000, 396, 192000 },
{ LEVEL_AVC_1_2, 6000, 396, 384000 },
{ LEVEL_AVC_1_3, 11880, 396, 768000 },
{ LEVEL_AVC_2, 11880, 396, 2000000 },
{ LEVEL_AVC_2_1, 19800, 792, 4000000 },
{ LEVEL_AVC_2_2, 20250, 1620, 4000000 },
{ LEVEL_AVC_3, 40500, 1620, 10000000 },
{ LEVEL_AVC_3_1, 108000, 3600, 14000000 },
{ LEVEL_AVC_3_2, 216000, 5120, 20000000 },
{ LEVEL_AVC_4, 245760, 8192, 20000000 },
{ LEVEL_AVC_4_1, 245760, 8192, 50000000 },
{ LEVEL_AVC_4_2, 522240, 8704, 50000000 },
{ LEVEL_AVC_5, 589824, 22080, 135000000 },
};
uint64_t mbs = uint64_t((size.v.width + 15) / 16) * ((size.v.height + 15) / 16);
float mbsPerSec = float(mbs) * frameRate.v.value;
// Check if the supplied level meets the MB / bitrate requirements. If
// not, update the level with the lowest level meeting the requirements.
bool found = false;
// By default needsUpdate = false in case the supplied level does meet
// the requirements. For Level 1b, we want to update the level anyway,
// so we set it to true in that case.
bool needsUpdate = (me.v.level == LEVEL_AVC_1B);
for (const LevelLimits &limit : kLimits) {
if (mbs <= limit.mbs && mbsPerSec <= limit.mbsPerSec &&
bitrate.v.value <= limit.bitrate) {
// This is the lowest level that meets the requirements, and if
// we haven't seen the supplied level yet, that means we don't
// need the update.
if (needsUpdate) {
c2_info("Given level %x does not cover current configuration: "
"adjusting to %x", me.v.level, limit.level);
me.set().level = limit.level;
}
found = true;
break;
}
if (me.v.level == limit.level) {
// We break out of the loop when the lowest feasible level is
// found. The fact that we're here means that our level doesn't
// meet the requirement and needs to be updated.
needsUpdate = true;
}
}
if (!found) {
// We set to the highest supported level.
me.set().level = LEVEL_AVC_5;
}
return C2R::Ok();
}
static C2R HEVCProfileLevelSetter(
bool mayBlock,
C2P<C2StreamProfileLevelInfo::output> &me,
const C2P<C2StreamPictureSizeInfo::input> &size,
const C2P<C2StreamFrameRateInfo::output> &frameRate,
const C2P<C2StreamBitrateInfo::output> &bitrate) {
(void)mayBlock;
if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
me.set().profile = PROFILE_HEVC_MAIN;
}
struct LevelLimits {
C2Config::level_t level;
uint64_t samplesPerSec;
uint64_t samples;
uint32_t bitrate;
};
constexpr LevelLimits kLimits[] = {
{ LEVEL_HEVC_MAIN_1, 552960, 36864, 128000 },
{ LEVEL_HEVC_MAIN_2, 3686400, 122880, 1500000 },
{ LEVEL_HEVC_MAIN_2_1, 7372800, 245760, 3000000 },
{ LEVEL_HEVC_MAIN_3, 16588800, 552960, 6000000 },
{ LEVEL_HEVC_MAIN_3_1, 33177600, 983040, 10000000 },
{ LEVEL_HEVC_MAIN_4, 66846720, 2228224, 12000000 },
{ LEVEL_HEVC_MAIN_4_1, 133693440, 2228224, 20000000 },
{ LEVEL_HEVC_MAIN_5, 267386880, 8912896, 25000000 },
{ LEVEL_HEVC_MAIN_5_1, 534773760, 8912896, 40000000 },
{ LEVEL_HEVC_MAIN_5_2, 1069547520, 8912896, 60000000 },
{ LEVEL_HEVC_MAIN_6, 1069547520, 35651584, 60000000 },
{ LEVEL_HEVC_MAIN_6_1, 2139095040, 35651584, 120000000 },
{ LEVEL_HEVC_MAIN_6_2, 4278190080, 35651584, 240000000 },
};
uint64_t samples = size.v.width * size.v.height;
uint64_t samplesPerSec = samples * frameRate.v.value;
// Check if the supplied level meets the MB / bitrate requirements. If
// not, update the level with the lowest level meeting the requirements.
bool found = false;
// By default needsUpdate = false in case the supplied level does meet
// the requirements.
bool needsUpdate = false;
for (const LevelLimits &limit : kLimits) {
if (samples <= limit.samples && samplesPerSec <= limit.samplesPerSec &&
bitrate.v.value <= limit.bitrate) {
// This is the lowest level that meets the requirements, and if
// we haven't seen the supplied level yet, that means we don't
// need the update.
if (needsUpdate) {
c2_info("Given level %x does not cover current configuration: "
"adjusting to %x", me.v.level, limit.level);
me.set().level = limit.level;
}
found = true;
break;
}
if (me.v.level == limit.level) {
// We break out of the loop when the lowest feasible level is
// found. The fact that we're here means that our level doesn't
// meet the requirement and needs to be updated.
needsUpdate = true;
}
}
if (!found) {
// We set to the highest supported level.
me.set().level = LEVEL_HEVC_MAIN_4_1;
}
return C2R::Ok();
}
static C2R DefaultProfileLevelSetter(
bool mayBlock,
C2P<C2StreamProfileLevelInfo::output> &me,
const C2P<C2StreamPictureSizeInfo::input> &size,
const C2P<C2StreamFrameRateInfo::output> &frameRate,
const C2P<C2StreamBitrateInfo::output> &bitrate) {
(void)mayBlock;
(void)me;
(void)size;
(void)frameRate;
(void)bitrate;
return C2R::Ok();
}
static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input> &me) {
(void)mayBlock;
if (me.v.range > C2Color::RANGE_OTHER) {
me.set().range = C2Color::RANGE_OTHER;
}
if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
me.set().primaries = C2Color::PRIMARIES_OTHER;
}
if (me.v.transfer > C2Color::TRANSFER_OTHER) {
me.set().transfer = C2Color::TRANSFER_OTHER;
}
if (me.v.matrix > C2Color::MATRIX_OTHER) {
me.set().matrix = C2Color::MATRIX_OTHER;
}
return C2R::Ok();
}
static C2R CodedColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output> &me,
const C2P<C2StreamColorAspectsInfo::input> &coded) {
(void)mayBlock;
me.set().range = coded.v.range;
me.set().primaries = coded.v.primaries;
me.set().transfer = coded.v.transfer;
me.set().matrix = coded.v.matrix;
return C2R::Ok();
}
static C2R LayeringSetter(
bool mayBlock, C2P<C2StreamTemporalLayeringTuning::output>& me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
static C2R PrependHeaderModeSetter(
bool mayBlock, C2P<C2PrependHeaderModeSetting>& me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
static C2R MProfileLevelSetter(
bool mayBlock, C2P<C2MProfileLevel::output> &me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
static C2R MSliceSpaceSetter(
bool mayBlock, C2P<C2SliceSpacing::output> &me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
static C2R MNumLTRFrmsSetter(
bool mayBlock, C2P<C2NumLTRFrms::output> &me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
static C2R MSarSizeSetter(
bool mayBlock, C2P<C2SarSize::output> &me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
static C2R MInputQueueCtlSetter(
bool mayBlock, C2P<C2InputQueuCtl::output> &me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
static C2R MLtrMarkFrmSetter(
bool mayBlock, C2P<C2LtrCtlMark::input> &me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
static C2R MLtrUseFrmSetter(
bool mayBlock, C2P<C2LtrCtlUse::input> &me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
static C2R MTriggerTimeSetter(
bool mayBlock, C2P<C2TriggerTime::input> &me) {
(void)mayBlock;
(void)me;
c2_log_func_enter();
return C2R::Ok();
}
uint32_t getProfile_l(MppCodingType type) const {
uint32_t cProfile = mProfileLevel->profile;
uint32_t mProfile = mMlvecParams->profileLevel->profile;
if (type == MPP_VIDEO_CodingAVC) {
if (mProfile > 0) {
return C2RKCodecMapper::getMppH264Profile(mProfile, false);
} else {
return C2RKCodecMapper::getMppH264Profile(cProfile, true);
}
} else if (type == MPP_VIDEO_CodingHEVC) {
return C2RKCodecMapper::getMppH265Profile(cProfile);
} else {
return 0;
}
}
uint32_t getLevel_l(MppCodingType type) const {
uint32_t cLevel = mProfileLevel->level;
uint32_t mLevel = mMlvecParams->profileLevel->level;
if (type == MPP_VIDEO_CodingAVC) {
if (mLevel) {
return C2RKCodecMapper::getMppH264Level(mLevel, false);
} else {
return C2RKCodecMapper::getMppH264Level(cLevel, true);
}
} else if (type == MPP_VIDEO_CodingHEVC) {
return C2RKCodecMapper::getMppH265Level(cLevel);
} else {
return 0;
}
}
uint32_t getBitrateMode_l() const {
int32_t cMode = mBitrateMode->value;
int32_t mMode = mMlvecParams->rateControl->value;
if (mMode >= 0) {
return C2RKCodecMapper::getMppBitrateMode(mMode, false);
} else {
return C2RKCodecMapper::getMppBitrateMode(cMode, true);
}
}
// unsafe getters
std::shared_ptr<C2StreamPictureSizeInfo::input> getSize_l() const
{ return mSize; }
std::shared_ptr<C2StreamIntraRefreshTuning::output> getIntraRefresh_l() const
{ return mIntraRefresh; }
std::shared_ptr<C2StreamFrameRateInfo::output> getFrameRate_l() const
{ return mFrameRate; }
std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const
{ return mBitrate; }
std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const
{ return mRequestSync; }
std::shared_ptr<C2StreamGopTuning::output> getGop_l() const
{ return mGop; }
std::shared_ptr<C2StreamPictureQuantizationTuning::output> getPictureQuantization_l() const
{ return mPictureQuantization; }
std::shared_ptr<C2StreamColorAspectsInfo::output> getCodedColorAspects_l() const
{ return mCodedColorAspects; }
std::shared_ptr<C2StreamTemporalLayeringTuning::output> getTemporalLayers_l() const
{ return mLayering; }
std::shared_ptr<C2PrependHeaderModeSetting> getPrependHeaderMode_l() const
{ return mPrependHeaderMode; }
std::shared_ptr<C2StreamSceneModeInfo::input> getSceneMode_l() const
{ return mSceneMode; }
std::shared_ptr<C2StreamSliceSizeInfo::input> getSliceSize_l() const
{ return mSliceSize; }
std::shared_ptr<MlvecParams> getMlvecParams_l() const
{ return mMlvecParams; }
private:
std::shared_ptr<C2StreamUsageTuning::input> mUsage;
std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
std::shared_ptr<C2StreamIntraRefreshTuning::output> mIntraRefresh;
std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
std::shared_ptr<C2StreamGopTuning::output> mGop;
std::shared_ptr<C2StreamPictureQuantizationTuning::output> mPictureQuantization;
std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
std::shared_ptr<C2StreamColorAspectsInfo::input> mColorAspects;
std::shared_ptr<C2StreamColorAspectsInfo::output> mCodedColorAspects;
std::shared_ptr<C2StreamTemporalLayeringTuning::output> mLayering;
std::shared_ptr<C2PrependHeaderModeSetting> mPrependHeaderMode;
std::shared_ptr<C2StreamSceneModeInfo::input> mSceneMode;
std::shared_ptr<C2StreamSliceSizeInfo::input> mSliceSize;
std::shared_ptr<MlvecParams> mMlvecParams;
};
C2RKMpiEnc::C2RKMpiEnc(
const char *name, c2_node_id_t id, const std::shared_ptr<IntfImpl> &intfImpl)
: C2RKComponent(std::make_shared<C2RKInterface<IntfImpl>>(name, id, intfImpl)),
mIntf(intfImpl),
mDmaMem(nullptr),
mMlvec(nullptr),
mDump(nullptr),
mMppCtx(nullptr),
mMppMpi(nullptr),
mEncCfg(nullptr),
mCodingType(MPP_VIDEO_CodingUnused),
mInputMppFmt(MPP_FMT_YUV420SP),
mChipType(0),
mStarted(false),
mSpsPpsHeaderReceived(false),
mSawInputEOS(false),
mOutputEOS(false),
mSignalledError(false),
mHorStride(0),
mVerStride(0),
mCurLayerCount(0),
mInputCount(0),
mOutputCount(0) {
if (!C2RKMediaUtils::getCodingTypeFromComponentName(name, &mCodingType)) {
c2_err("failed to get MppCodingType from component %s", name);
}
RKChipInfo *chipInfo = getChipName();
if (chipInfo != nullptr) {
mChipType = getChipName()->type;
} else {
mChipType = RK_CHIP_UNKOWN;
}
sEncConcurrentInstances.fetch_add(1, std::memory_order_relaxed);
//c2_info("component name %s\r\nversion: %s", name, C2_GIT_BUILD_VERSION);
}
C2RKMpiEnc::~C2RKMpiEnc() {
c2_log_func_enter();
if (sEncConcurrentInstances.load() > 0) {
sEncConcurrentInstances.fetch_sub(1, std::memory_order_relaxed);
}
onRelease();
}
c2_status_t C2RKMpiEnc::onInit() {
c2_log_func_enter();
return C2_OK;
}
c2_status_t C2RKMpiEnc::onStop() {
c2_log_func_enter();
releaseEncoder();
return C2_OK;
}
void C2RKMpiEnc::onReset() {
c2_log_func_enter();
releaseEncoder();
}
void C2RKMpiEnc::onRelease() {
c2_log_func_enter();
releaseEncoder();
}
c2_status_t C2RKMpiEnc::onFlush_sm() {
c2_log_func_enter();
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupBaseCodec() {
/* default stride */
mHorStride = C2_ALIGN(mSize->width, 16);
if (mCodingType == MPP_VIDEO_CodingVP8) {
mVerStride = C2_ALIGN(mSize->height, 16);
} else {
mVerStride = C2_ALIGN(mSize->height, 8);
}
c2_info("setupBaseCodec: coding %d w %d h %d hor %d ver %d",
mCodingType, mSize->width, mSize->height, mHorStride, mVerStride);
mpp_enc_cfg_set_s32(mEncCfg, "codec:type", mCodingType);
mpp_enc_cfg_set_s32(mEncCfg, "prep:width", mSize->width);
mpp_enc_cfg_set_s32(mEncCfg, "prep:height", mSize->height);
mpp_enc_cfg_set_s32(mEncCfg, "prep:hor_stride", mHorStride);
mpp_enc_cfg_set_s32(mEncCfg, "prep:ver_stride", mVerStride);
mpp_enc_cfg_set_s32(mEncCfg, "prep:format", MPP_FMT_YUV420SP);
mpp_enc_cfg_set_s32(mEncCfg, "prep:rotation", MPP_ENC_ROT_0);
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupSceneMode() {
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<C2StreamSceneModeInfo::input> c2Mode = mIntf->getSceneMode_l();
c2_info("setupSceneMode: scene-mode %d", c2Mode->value);
/*
* scene-mode of encoder, this feature only support on rk3588
* - 0: deault none ipc mode
* - 1: ipc mode
*/
mpp_enc_cfg_set_s32(mEncCfg, "tune:scene_mode", c2Mode->value);
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupSliceSize() {
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<C2StreamSliceSizeInfo::input> c2Size = mIntf->getSliceSize_l();
if (c2Size->value > 0) {
c2_info("setupSliceSize: slice-size %d", c2Size->value);
mpp_enc_cfg_set_s32(mEncCfg, "split:mode", MPP_ENC_SPLIT_BY_BYTE);
mpp_enc_cfg_set_s32(mEncCfg, "split:arg", c2Size->value);
}
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupFrameRate() {
float frameRate = 0.0f;
uint32_t idrInterval = 0;
int32_t gop = 0;
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<C2StreamGopTuning::output> c2Gop = mIntf->getGop_l();
std::shared_ptr<C2StreamFrameRateInfo::output> c2FrameRate
= mIntf->getFrameRate_l();
idrInterval = mIntf->getSyncFramePeriod_l();
frameRate = c2FrameRate->value;
if (frameRate == 1) {
// set default frameRate 30
frameRate = 30;
}
if (c2Gop && c2Gop->flexCount() > 0) {
uint32_t syncInterval = 30;
uint32_t iInterval = 0;
uint32_t maxBframes = 0;
ParseGop(*c2Gop, &syncInterval, &iInterval, &maxBframes);
if (syncInterval > 0) {
c2_info("updating IDR interval: %d -> %d", idrInterval, syncInterval);
idrInterval = syncInterval;
}
}
c2_info("setupFrameRate: framerate %.2f gop %u", frameRate, idrInterval);
gop = (idrInterval < INT_MAX) ? idrInterval : 0;
mpp_enc_cfg_set_s32(mEncCfg, "rc:gop", gop);
/* fix input / output frame rate */
mpp_enc_cfg_set_s32(mEncCfg, "rc:fps_in_flex", 0);
mpp_enc_cfg_set_s32(mEncCfg, "rc:fps_in_num", frameRate);
mpp_enc_cfg_set_s32(mEncCfg, "rc:fps_in_denorm", 1);
mpp_enc_cfg_set_s32(mEncCfg, "rc:fps_out_flex", 0);
mpp_enc_cfg_set_s32(mEncCfg, "rc:fps_out_num", frameRate);
mpp_enc_cfg_set_s32(mEncCfg, "rc:fps_out_denorm", 1);
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupBitRate() {
uint32_t bitrate = 0;
uint32_t bitrateMode = 0;
IntfImpl::Lock lock = mIntf->lock();
bitrate = mIntf->getBitrate_l()->value;
bitrateMode = mIntf->getBitrateMode_l();
c2_info("setupBitRate: mode %s bitrate %d",
toStr_BitrateMode(bitrateMode), bitrate);
mpp_enc_cfg_set_s32(mEncCfg, "rc:bps_target", bitrate);
switch (bitrateMode) {
case MPP_ENC_RC_MODE_CBR: {
/* CBR mode has narrow bound */
mpp_enc_cfg_set_s32(mEncCfg, "rc:mode", MPP_ENC_RC_MODE_CBR);
mpp_enc_cfg_set_s32(mEncCfg, "rc:bps_max", bitrate * 17 / 16);
mpp_enc_cfg_set_s32(mEncCfg, "rc:bps_min", bitrate * 15 / 16);
} break;
case MPP_ENC_RC_MODE_VBR: {
/* VBR mode has wide bound */
mpp_enc_cfg_set_s32(mEncCfg, "rc:mode", MPP_ENC_RC_MODE_VBR);
mpp_enc_cfg_set_s32(mEncCfg, "rc:bps_max", bitrate * 17 / 16);
mpp_enc_cfg_set_s32(mEncCfg, "rc:bps_min", bitrate * 1 / 16);
} break;
case MPP_ENC_RC_MODE_FIXQP: {
/* FIXQP mode */
mpp_enc_cfg_set_s32(mEncCfg, "rc:mode", MPP_ENC_RC_MODE_FIXQP);
mpp_enc_cfg_set_s32(mEncCfg, "rc:bps_max", bitrate * 17 / 16);
mpp_enc_cfg_set_s32(mEncCfg, "rc:bps_min", bitrate * 15 / 16);
} break;
default: {
/* default use CBR mode */
mpp_enc_cfg_set_s32(mEncCfg, "rc:mode", MPP_ENC_RC_MODE_CBR);
mpp_enc_cfg_set_s32(mEncCfg, "rc:bps_max", bitrate * 17 / 16);
mpp_enc_cfg_set_s32(mEncCfg, "rc:bps_min", bitrate * 15 / 16);
} break;
}
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupProfileParams() {
uint32_t profile, level;
IntfImpl::Lock lock = mIntf->lock();
profile = mIntf->getProfile_l(mCodingType);
level = mIntf->getLevel_l(mCodingType);
c2_info("setupProfileParams: profile %s level %s",
toStr_Profile(profile, mCodingType), toStr_Level(level, mCodingType));
switch (mCodingType) {
case MPP_VIDEO_CodingAVC : {
mpp_enc_cfg_set_s32(mEncCfg, "h264:profile", profile);
mpp_enc_cfg_set_s32(mEncCfg, "h264:level", level);
if (profile >= MPP_H264_HIGH) {
mpp_enc_cfg_set_s32(mEncCfg, "h264:cabac_en", 1);
mpp_enc_cfg_set_s32(mEncCfg, "h264:cabac_idc", 0);
mpp_enc_cfg_set_s32(mEncCfg, "h264:trans8x8", 1);
}
} break;
case MPP_VIDEO_CodingHEVC : {
mpp_enc_cfg_set_s32(mEncCfg, "h265:profile", profile);
mpp_enc_cfg_set_s32(mEncCfg, "h265:level", level);
} break;
default : {
c2_err("setupProfileParams: unsupport coding type %d", mCodingType);
} break;
}
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupQp() {
int32_t defaultIMin = 0, defaultIMax = 0;
int32_t defaultPMin = 0, defaultPMax = 0;
int32_t qpInit = 0, fixQPMode /* const qp mode */ = 0;
if (mCodingType == MPP_VIDEO_CodingVP8) {
defaultIMin = defaultPMin = 0;
defaultIMax = defaultPMax = 127;
qpInit = 40;
} else {
/* the quality of h264/265 range from 10~51 */
defaultIMin = defaultPMin = 10;
defaultIMax = 51;
// TODO: CTS testEncoderQualityAVCCBR 49
defaultPMax = 49;
qpInit = 26;
}
int32_t iMin = defaultIMin, iMax = defaultIMax;
int32_t pMin = defaultPMin, pMax = defaultPMax;
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<C2StreamPictureQuantizationTuning::output> qp =
mIntf->getPictureQuantization_l();
fixQPMode = (mIntf->getBitrateMode_l() == MPP_ENC_RC_MODE_FIXQP) ? 1 : 0;
for (size_t i = 0; i < qp->flexCount(); ++i) {
const C2PictureQuantizationStruct &layer = qp->m.values[i];
if (layer.type_ == C2Config::picture_type_t(I_FRAME)) {
iMax = layer.max;
iMin = layer.min;
c2_info("PictureQuanlitySetter: iMin %d iMax %d", iMin, iMax);
} else if (layer.type_ == C2Config::picture_type_t(P_FRAME)) {
pMax = layer.max;
pMin = layer.min;
c2_info("PictureQuanlitySetter: pMin %d pMax %d", pMin, pMax);
}
}
iMax = std::clamp(iMax, defaultIMin, defaultIMax);
iMin = std::clamp(iMin, defaultIMin, defaultIMax);
pMax = std::clamp(pMax, defaultPMin, defaultPMax);
pMin = std::clamp(pMin, defaultPMin, defaultPMax);
if (qpInit > iMax || qpInit < iMin) {
qpInit = iMin;
}
if (fixQPMode) {
/* use const qp for p-frame in FIXQP mode */
pMax = pMin = qpInit;
}
c2_info("setupQp: qpInit %d i %d-%d p %d-%d", qpInit, iMin, iMax, pMin, pMax);
switch (mCodingType) {
case MPP_VIDEO_CodingAVC:
mpp_enc_cfg_set_s32(mEncCfg, "h264:cb_qp_offset", 0);
mpp_enc_cfg_set_s32(mEncCfg, "h264:cr_qp_offset", 0);
[[fallthrough]];
case MPP_VIDEO_CodingHEVC: {
/*
* disable mb_rc for vepu, this cfg does not apply to rkvenc.
* since the vepu has pool performance, mb_rc will cause mosaic.
*/
mpp_enc_cfg_set_s32(mEncCfg, "hw:mb_rc_disable", 1);
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_min", pMin);
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_max", pMax);
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_min_i", iMin);
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_max_i", iMax);
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_init", qpInit);
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_ip", 2);
} break;
case MPP_VIDEO_CodingVP8: {
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_min", pMin);
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_max", pMax);
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_min_i", iMin);
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_max_i", iMax);
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_init", qpInit);
mpp_enc_cfg_set_s32(mEncCfg, "rc:qp_ip", 6);
} break;
default: {
c2_err("setupQp: unsupport coding type %d", mCodingType);
break;
}
}
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupVuiParams() {
ColorAspects sfAspects;
int32_t primaries, transfer, matrixCoeffs;
bool range;
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<C2StreamColorAspectsInfo::output> colorAspects
= mIntf->getCodedColorAspects_l();
if (!C2Mapper::map(colorAspects->primaries, &sfAspects.mPrimaries)) {
sfAspects.mPrimaries = android::ColorAspects::PrimariesUnspecified;
}
if (!C2Mapper::map(colorAspects->range, &sfAspects.mRange)) {
sfAspects.mRange = android::ColorAspects::RangeUnspecified;
}
if (!C2Mapper::map(colorAspects->matrix, &sfAspects.mMatrixCoeffs)) {
sfAspects.mMatrixCoeffs = android::ColorAspects::MatrixUnspecified;
}
if (!C2Mapper::map(colorAspects->transfer, &sfAspects.mTransfer)) {
sfAspects.mTransfer = android::ColorAspects::TransferUnspecified;
}
ColorUtils::convertCodecColorAspectsToIsoAspects(
sfAspects, &primaries, &transfer,
&matrixCoeffs, &range);
c2_info("setupVuiParams: (R:%d(%s), P:%d(%s), M:%d(%s), T:%d(%s))",
sfAspects.mRange, asString(sfAspects.mRange),
sfAspects.mPrimaries, asString(sfAspects.mPrimaries),
sfAspects.mMatrixCoeffs, asString(sfAspects.mMatrixCoeffs),
sfAspects.mTransfer, asString(sfAspects.mTransfer));
mpp_enc_cfg_set_s32(mEncCfg, "prep:range", range ? 2 : 0);
mpp_enc_cfg_set_s32(mEncCfg, "prep:colorprim", primaries);
mpp_enc_cfg_set_s32(mEncCfg, "prep:colortrc", transfer);
mpp_enc_cfg_set_s32(mEncCfg, "prep:colorspace", matrixCoeffs);
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupTemporalLayers() {
int32_t layerCount = 0;
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<C2StreamTemporalLayeringTuning::output> layering =
mIntf->getTemporalLayers_l();
layerCount = layering->m.layerCount;
if (layerCount == 0 || layerCount == 1) {
return C2_OK;
}
if (layerCount < 2 || layerCount > 4) {
c2_warn("only support tsvc layer 2 ~ 4(%d); ignored.", layerCount);
return C2_OK;
}
/*
* NOTE:
* 1. not support set bLayerCount and bitrateRatios yet.
* - layering->m.bLayerCount
* - layering->m.bitrateRatios
* 2. only support tsvc layer 2 ~ 4.
*/
int ret = 0;
MppEncRefCfg ref;
MppEncRefLtFrmCfg ltRef[4];
MppEncRefStFrmCfg stRef[16];
RK_S32 ltCnt = 0;
RK_S32 stCnt = 0;
memset(&ltRef, 0, sizeof(ltRef));
memset(&stRef, 0, sizeof(stRef));
mpp_enc_ref_cfg_init(&ref);
c2_info("setupTemporalLayers: layers %d", layerCount);
switch (layerCount) {
case 4: {
// tsvc4
// /-> P1 /-> P3 /-> P5 /-> P7
// / / / /
// //--------> P2 //--------> P6
// // //
// ///---------------------> P4
// ///
// P0 ------------------------------------------------> P8
ltCnt = 1;
/* set 8 frame lt-ref gap */
ltRef[0].lt_idx = 0;
ltRef[0].temporal_id = 0;
ltRef[0].ref_mode = REF_TO_PREV_LT_REF;
ltRef[0].lt_gap = 8;
ltRef[0].lt_delay = 0;
stCnt = 9;
/* set tsvc4 st-ref struct */
/* st 0 layer 0 - ref */
stRef[0].is_non_ref = 0;
stRef[0].temporal_id = 0;
stRef[0].ref_mode = REF_TO_TEMPORAL_LAYER;
stRef[0].ref_arg = 0;
stRef[0].repeat = 0;
/* st 1 layer 3 - non-ref */
stRef[1].is_non_ref = 1;
stRef[1].temporal_id = 3;
stRef[1].ref_mode = REF_TO_PREV_REF_FRM;
stRef[1].ref_arg = 0;
stRef[1].repeat = 0;
/* st 2 layer 2 - ref */
stRef[2].is_non_ref = 0;
stRef[2].temporal_id = 2;
stRef[2].ref_mode = REF_TO_PREV_REF_FRM;
stRef[2].ref_arg = 0;
stRef[2].repeat = 0;
/* st 3 layer 3 - non-ref */
stRef[3].is_non_ref = 1;
stRef[3].temporal_id = 3;
stRef[3].ref_mode = REF_TO_PREV_REF_FRM;
stRef[3].ref_arg = 0;
stRef[3].repeat = 0;
/* st 4 layer 1 - ref */
stRef[4].is_non_ref = 0;
stRef[4].temporal_id = 1;
stRef[4].ref_mode = REF_TO_PREV_LT_REF;
stRef[4].ref_arg = 0;
stRef[4].repeat = 0;
/* st 5 layer 3 - non-ref */
stRef[5].is_non_ref = 1;
stRef[5].temporal_id = 3;
stRef[5].ref_mode = REF_TO_PREV_REF_FRM;
stRef[5].ref_arg = 0;
stRef[5].repeat = 0;
/* st 6 layer 2 - ref */
stRef[6].is_non_ref = 0;
stRef[6].temporal_id = 2;
stRef[6].ref_mode = REF_TO_PREV_REF_FRM;
stRef[6].ref_arg = 0;
stRef[6].repeat = 0;
/* st 7 layer 3 - non-ref */
stRef[7].is_non_ref = 1;
stRef[7].temporal_id = 3;
stRef[7].ref_mode = REF_TO_PREV_REF_FRM;
stRef[7].ref_arg = 0;
stRef[7].repeat = 0;
/* st 8 layer 0 - ref */
stRef[8].is_non_ref = 0;
stRef[8].temporal_id = 0;
stRef[8].ref_mode = REF_TO_TEMPORAL_LAYER;
stRef[8].ref_arg = 0;
stRef[8].repeat = 0;
} break;
case 3: {
// tsvc3
// /-> P1 /-> P3
// / /
// //--------> P2
// //
// P0/---------------------> P4
ltCnt = 0;
stCnt = 5;
/* set tsvc4 st-ref struct */
/* st 0 layer 0 - ref */
stRef[0].is_non_ref = 0;
stRef[0].temporal_id = 0;
stRef[0].ref_mode = REF_TO_TEMPORAL_LAYER;
stRef[0].ref_arg = 0;
stRef[0].repeat = 0;
/* st 1 layer 2 - non-ref */
stRef[1].is_non_ref = 1;
stRef[1].temporal_id = 2;
stRef[1].ref_mode = REF_TO_PREV_REF_FRM;
stRef[1].ref_arg = 0;
stRef[1].repeat = 0;
/* st 2 layer 1 - ref */
stRef[2].is_non_ref = 0;
stRef[2].temporal_id = 1;
stRef[2].ref_mode = REF_TO_PREV_REF_FRM;
stRef[2].ref_arg = 0;
stRef[2].repeat = 0;
/* st 3 layer 2 - non-ref */
stRef[3].is_non_ref = 1;
stRef[3].temporal_id = 2;
stRef[3].ref_mode = REF_TO_PREV_REF_FRM;
stRef[3].ref_arg = 0;
stRef[3].repeat = 0;
/* st 4 layer 0 - ref */
stRef[4].is_non_ref = 0;
stRef[4].temporal_id = 0;
stRef[4].ref_mode = REF_TO_TEMPORAL_LAYER;
stRef[4].ref_arg = 0;
stRef[4].repeat = 0;
} break;
case 2: {
// tsvc2
// /-> P1
// /
// P0--------> P2
ltCnt = 0;
stCnt = 3;
/* set tsvc4 st-ref struct */
/* st 0 layer 0 - ref */
stRef[0].is_non_ref = 0;
stRef[0].temporal_id = 0;
stRef[0].ref_mode = REF_TO_TEMPORAL_LAYER;
stRef[0].ref_arg = 0;
stRef[0].repeat = 0;
/* st 1 layer 2 - non-ref */
stRef[1].is_non_ref = 1;
stRef[1].temporal_id = 1;
stRef[1].ref_mode = REF_TO_PREV_REF_FRM;
stRef[1].ref_arg = 0;
stRef[1].repeat = 0;
/* st 2 layer 1 - ref */
stRef[2].is_non_ref = 0;
stRef[2].temporal_id = 0;
stRef[2].ref_mode = REF_TO_PREV_REF_FRM;
stRef[2].ref_arg = 0;
stRef[2].repeat = 0;
} break;
default : {
} break;
}
if (ltCnt || stCnt) {
mpp_enc_ref_cfg_set_cfg_cnt(ref, ltCnt, stCnt);
if (ltCnt)
mpp_enc_ref_cfg_add_lt_cfg(ref, ltCnt, ltRef);
if (stCnt)
mpp_enc_ref_cfg_add_st_cfg(ref, stCnt, stRef);
/* check and get dpb size */
mpp_enc_ref_cfg_check(ref);
}
ret = mMppMpi->control(mMppCtx, MPP_ENC_SET_REF_CFG, ref);
if (ret) {
c2_err("setupTemporalLayers: failed to set ref cfg ret %d", ret);
return C2_CORRUPTED;
}
mCurLayerCount = layerCount;
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupPrependHeaderSetting() {
std::shared_ptr<C2PrependHeaderModeSetting> prepend;
IntfImpl::Lock lock = mIntf->lock();
prepend = mIntf->getPrependHeaderMode_l();
if (prepend->value == C2Config::PREPEND_HEADER_TO_ALL_SYNC) {
c2_info("setupPrependHeaderSetting: prepend sps pps to idr frames.");
MppEncHeaderMode mode = MPP_ENC_HEADER_MODE_EACH_IDR;
int ret = mMppMpi->control(mMppCtx, MPP_ENC_SET_HEADER_MODE, &mode);
if (ret) {
c2_err("setupPrependHeaderSetting: failed to set mode ret %d", ret);
return C2_CORRUPTED;
}
}
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupMlvecIfNeccessary() {
int32_t layerCount = 0;
int32_t spacing = 0;
int32_t numLTRFrms = 0;
int32_t inputCtlMode = 0;
uint32_t sarWidth = 0, sarHeight = 0;
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<MlvecParams> params = mIntf->getMlvecParams_l();
std::shared_ptr<C2StreamTemporalLayeringTuning::output> layering =
mIntf->getTemporalLayers_l();
layerCount = layering->m.layerCount;
spacing = params->sliceSpacing->spacing;
numLTRFrms = params->numLTRFrms->num;
sarWidth = params->sarSize->width;
sarHeight = params->sarSize->height;
inputCtlMode = params->inputQueueCtl->enable;
/* enable mlvec */
if (spacing > 0 || numLTRFrms > 0 || sarWidth > 0 ||
sarHeight > 0 || inputCtlMode > 0) {
C2RKMlvecLegacy::MStaticCfg stCfg;
if (numLTRFrms > MLVEC_MAX_LTR_FRAMES_COUNT) {
c2_warn("not support LTRFrames num %d(max %d), quit mlvec mode",
numLTRFrms, MLVEC_MAX_LTR_FRAMES_COUNT);
return C2_CANNOT_DO;
}
if (sarWidth > mSize->width || sarHeight > mSize->height) {
c2_warn("not support sarSize %dx%d, picture size %dx%d, quit mlvec mode",
sarWidth, sarHeight, mSize->width, mSize->height);
return C2_CANNOT_DO;
}
c2_info("setupMlvec: layerCount %d spacing %d numLTRFrms %d",
layerCount, spacing, numLTRFrms);
c2_info("setupMlvec: w %d h %d sarWidth %d sarHeight %d",
mSize->width, mSize->height, sarWidth, sarHeight);
c2_info("setupMlvec: inputCtlMode %d", inputCtlMode);
mMlvec = new C2RKMlvecLegacy(mMppCtx, mMppMpi, mEncCfg);
memset(&stCfg, 0, sizeof(stCfg));
stCfg.magic = ((int32_t)'M') << 24;
stCfg.magic |= ((int32_t)'0') << 16;
stCfg.width = mSize->width;
stCfg.height = mSize->height;
stCfg.sarWidth = sarWidth;
stCfg.sarHeight = sarHeight;
stCfg.maxTid = layerCount;
stCfg.ltrFrames = numLTRFrms;
stCfg.addPrefix = (layerCount >= 1) ? 1 : 0;
stCfg.sliceMbs = spacing;
if (!mMlvec->setupStaticConfig(&stCfg)) {
c2_err("failed to setup mlvec static config");
} else {
mCurLayerCount = layerCount;
}
// mlvec need pic_order_cnt_type equal to 2
mpp_enc_cfg_set_s32(mEncCfg, "h264:poc_type", 2);
}
return C2_OK;
}
c2_status_t C2RKMpiEnc::setupEncCfg() {
c2_status_t ret = C2_OK;
int err = 0;
err = mpp_enc_cfg_init(&mEncCfg);
if (err) {
c2_err("failed to get enc_cfg, ret %d", err);
return C2_CORRUPTED;
}
err = mMppMpi->control(mMppCtx, MPP_ENC_GET_CFG, mEncCfg);
if (err) {
c2_err("failed to get codec cfg, ret %d", err);
return C2_CORRUPTED;
}
/* Video control Set Base Codec */
setupBaseCodec();
/* Video control Set Scene Mode */
setupSceneMode();
/* Video control Set Slice Size */
setupSliceSize();
/* Video control Set FrameRates and gop */
setupFrameRate();
/* Video control Set Bitrate */
setupBitRate();
/* Video control Set Profile params */
setupProfileParams();
/* Video control Set QP */
setupQp();
/* Video control Set VUI params */
setupVuiParams();
/* Video control Set Temporal Layers */
setupTemporalLayers();
/* Video control Set Prepend Header Setting */
setupPrependHeaderSetting();
/* Video control Set MLVEC encoder */
setupMlvecIfNeccessary();
err = mMppMpi->control(mMppCtx, MPP_ENC_SET_CFG, mEncCfg);
if (err) {
c2_err("failed to setup codec cfg, ret %d", err);
ret = C2_CORRUPTED;
} else {
/* optional */
MppEncSeiMode seiMode = MPP_ENC_SEI_MODE_ONE_FRAME;
err = mMppMpi->control(mMppCtx, MPP_ENC_SET_SEI_CFG, &seiMode);
if (err) {
c2_err("failed to setup sei cfg, ret %d", err);
ret = C2_CORRUPTED;
}
}
return ret;
}
c2_status_t C2RKMpiEnc::initEncoder() {
c2_status_t ret = C2_OK;
int err = 0;
MppPollType timeout = MPP_POLL_BLOCK;
c2_log_func_enter();
{
IntfImpl::Lock lock = mIntf->lock();
mSize = mIntf->getSize_l();
mBitrate = mIntf->getBitrate_l();
mFrameRate = mIntf->getFrameRate_l();
mProfile = mIntf->getProfile_l(mCodingType);
}
/*
* create vpumem for mpp input
*
* NOTE: We need temporary buffer to store rga nv12 output for some rgba input,
* since mpp can't process rgba input properly. in addition to this, alloc buffer
* within 4G in view of rga efficiency.
*/
buffer_handle_t bufferHandle;
gralloc_private_handle_t privHandle;
uint32_t stride = 0;
uint64_t usage = (GRALLOC_USAGE_SW_READ_OFTEN |
GRALLOC_USAGE_SW_WRITE_OFTEN);
// only limit rga2
if (mChipType == RK_CHIP_3588 ||
mChipType == RK_CHIP_3566 ||
mChipType == RK_CHIP_3568) {
usage = RK_GRALLOC_USAGE_WITHIN_4G;
}
status_t status = GraphicBufferAllocator::get().allocate(
C2_ALIGN(mSize->width, 16), C2_ALIGN(mSize->height, 16),
0x15 /* NV12 */, 1u /* layer count */,
usage, &bufferHandle, &stride, "C2RKMpiEnc");
if (status) {
c2_err("failed transaction: allocate");
goto error;
}
Rockchip_get_gralloc_private((uint32_t *)bufferHandle, &privHandle);
mDmaMem = (MyDmaBuffer_t *)malloc(sizeof(MyDmaBuffer_t));
mDmaMem->fd = privHandle.share_fd;
mDmaMem->size = privHandle.size;
mDmaMem->handler = (void *)bufferHandle;
c2_info("alloc temporary DmaMem fd %d size %d", mDmaMem->fd, mDmaMem->size);
// create mpp and init mpp
err = mpp_create(&mMppCtx, &mMppMpi);
if (err) {
c2_err("failed to mpp_create, ret %d", err);
ret = C2_CORRUPTED;
goto error;
}
err = mMppMpi->control(mMppCtx, MPP_SET_OUTPUT_TIMEOUT, &timeout);
if (MPP_OK != err) {
c2_err("failed to set output timeout %d, ret %d", timeout, err);
ret = C2_CORRUPTED;
goto error;
}
err = mpp_init(mMppCtx, MPP_CTX_ENC, mCodingType);
if (err) {
c2_err("failed to mpp_init, ret %d", err);
ret = C2_CORRUPTED;
goto error;
}
ret = setupEncCfg();
if (ret) {
c2_err("failed to set config, ret=0x%x", ret);
ret = C2_CORRUPTED;
goto error;
}
if (!mDump) {
// init dump object.
mDump = new C2RKDump();
mDump->initDump(mSize->width, mSize->height, true);
}
mStarted = true;
return C2_OK;
error:
releaseEncoder();
return ret;
}
c2_status_t C2RKMpiEnc::releaseEncoder() {
mStarted = false;
mSpsPpsHeaderReceived = false;
mSawInputEOS = false;
mOutputEOS = false;
mSignalledError = false;
if (mInputCount != mOutputCount) {
c2_warn("release but input count %d doesn't equal to output count %d.",
mInputCount, mOutputCount);
}
if (mEncCfg) {
mpp_enc_cfg_deinit(mEncCfg);
mEncCfg = nullptr;
}
if (mMppCtx){
mpp_destroy(mMppCtx);
mMppCtx = nullptr;
}
if (mDmaMem != nullptr) {
GraphicBufferAllocator::get().free((buffer_handle_t)mDmaMem->handler);
free(mDmaMem);
mDmaMem = nullptr;
}
if (mMlvec != nullptr) {
delete mMlvec;
mMlvec = nullptr;
}
if (mDump != nullptr) {
delete mDump;
mDump = nullptr;
}
return C2_OK;
}
void C2RKMpiEnc::fillEmptyWork(const std::unique_ptr<C2Work>& work) {
uint32_t flags = 0;
c2_trace("called");
if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
flags |= C2FrameData::FLAG_END_OF_STREAM;
c2_info("Signalling EOS");
}
work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.ordinal = work->input.ordinal;
work->workletsProcessed = 1u;
}
void C2RKMpiEnc::finishWork(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool>& pool,
OutWorkEntry entry) {
c2_status_t ret = C2_OK;
uint64_t frmIndex = 0;
MppPacket packet = nullptr;
std::shared_ptr<C2LinearBlock> block;
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
frmIndex = entry.frameIndex;
packet = entry.outPacket;
void *data = mpp_packet_get_data(packet);
size_t len = mpp_packet_get_length(packet);
size_t size = mpp_packet_get_size(packet);
ret = pool->fetchLinearBlock(size, usage, &block);
if (ret != C2_OK) {
c2_err("failed to fetch block for output, ret 0x%x", ret);
work->result = ret;
work->workletsProcessed = 1u;
mSignalledError = true;
return;
}
C2WriteView wView = block->map().get();
if (C2_OK != wView.error()) {
c2_err("write view map failed with status 0x%x", wView.error());
work->result = wView.error();
work->workletsProcessed = 1u;
mSignalledError = true;
return;
}
// copy mpp output to c2 output
memcpy(wView.data(), data, len);
RK_S32 isIntra = 0;
std::shared_ptr<C2Buffer> buffer = createLinearBuffer(block, 0, len);
MppMeta meta = mpp_packet_get_meta(packet);
mpp_meta_get_s32(meta, KEY_OUTPUT_INTRA, &isIntra);
if (isIntra) {
c2_info("IDR frame produced");
buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
0u /* stream id */, C2Config::SYNC_FRAME));
}
mpp_packet_deinit(&packet);
auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.buffers.push_back(buffer);
work->worklets.front()->output.ordinal = work->input.ordinal;
work->workletsProcessed = 1u;
};
if (work && c2_cntr64_t(frmIndex) == work->input.ordinal.frameIndex) {
fillWork(work);
if (mSawInputEOS) {
work->worklets.front()->output.flags = C2FrameData::FLAG_END_OF_STREAM;
}
} else {
finish(frmIndex, fillWork);
}
}
c2_status_t C2RKMpiEnc::drainInternal(
uint32_t drainMode,
const std::shared_ptr<C2BlockPool> &pool,
const std::unique_ptr<C2Work> &work) {
c2_log_func_enter();
if (drainMode != DRAIN_COMPONENT_WITH_EOS) {
c2_info("drainMode %d: no-op", drainMode);
return C2_OK;
}
if (mInputCount == mOutputCount) {
// no need
return C2_OK;
}
c2_status_t ret = C2_OK;
OutWorkEntry entry;
while (true) {
memset(&entry, 0, sizeof(entry));
ret = getoutpacket(&entry);
if (ret == C2_OK) {
finishWork(work, pool, entry);
} else {
if (work && work->workletsProcessed != 1u) fillEmptyWork(work);
break;
}
}
c2_log_func_leave();
return C2_OK;
}
c2_status_t C2RKMpiEnc::drain(
uint32_t drainMode,
const std::shared_ptr<C2BlockPool> &pool) {
return drainInternal(drainMode, pool, nullptr);
}
void C2RKMpiEnc::process(
const std::unique_ptr<C2Work> &work,
const std::shared_ptr<C2BlockPool> &pool) {
c2_status_t err = C2_OK;
// Initialize output work
work->result = C2_OK;
work->workletsProcessed = 0u;
work->worklets.front()->output.flags = work->input.flags;
// Initialize encoder if not already initialized
if (!mStarted) {
err = initEncoder();
if (err != C2_OK) {
work->result = C2_BAD_VALUE;
c2_info("failed to initialize, signalled Error");
return;
}
}
if (mSignalledError) {
work->result = C2_BAD_VALUE;
c2_info("Signalled Error");
return;
}
std::shared_ptr<const C2GraphicView> view;
std::shared_ptr<C2Buffer> inputBuffer = nullptr;
if (!work->input.buffers.empty()) {
inputBuffer = work->input.buffers[0];
view = std::make_shared<const C2GraphicView>(
inputBuffer->data().graphicBlocks().front().map().get());
if (view->error() != C2_OK) {
c2_err("graphic view map err = %d", view->error());
mSignalledError = true;
work->result = C2_CORRUPTED;
work->workletsProcessed = 1u;
return;
}
const C2GraphicView *const input = view.get();
if ((input != nullptr) && (input->width() < mSize->width ||
input->height() < mSize->height)) {
/* Expect width height to be configured */
c2_err("unexpected Capacity Aspect %d(%d) x %d(%d)",
input->width(), mSize->width, input->height(), mSize->height);
mSignalledError = true;
work->result = C2_CORRUPTED;
work->workletsProcessed = 1u;
return;
}
}
uint32_t flags = work->input.flags;
uint64_t frameIndex = work->input.ordinal.frameIndex.peekull();
uint64_t timestamp = work->input.ordinal.timestamp.peekll();
c2_trace("process one work timestamp %llu frameindex %llu, flags %x",
timestamp, frameIndex, flags);
mSawInputEOS = (flags & C2FrameData::FLAG_END_OF_STREAM);
if (!mSpsPpsHeaderReceived) {
MppPacket hdrPkt = nullptr;
void *hdrBuf = nullptr;
void *extradata = nullptr;
uint32_t hdrBufSize = 1024;
uint32_t extradataSize = 0;
hdrBuf = malloc(hdrBufSize * sizeof(uint8_t));
if (hdrBuf)
mpp_packet_init(&hdrPkt, hdrBuf, hdrBufSize);
if (hdrPkt) {
mMppMpi->control(mMppCtx, MPP_ENC_GET_HDR_SYNC, hdrPkt);
extradataSize = mpp_packet_get_length(hdrPkt);
extradata = mpp_packet_get_data(hdrPkt);
}
std::unique_ptr<C2StreamInitDataInfo::output> csd =
C2StreamInitDataInfo::output::AllocUnique(extradataSize, 0u);
if (!csd) {
c2_err("CSD allocation failed");
work->result = C2_NO_MEMORY;
work->workletsProcessed = 1u;
C2_SAFE_FREE(hdrBuf);
return;
}
memcpy(csd->m.value, extradata, extradataSize);
work->worklets.front()->output.configUpdate.push_back(std::move(csd));
/* dump output data if neccessary */
mDump->recordOutFile(extradata, extradataSize);
mSpsPpsHeaderReceived = true;
if (hdrPkt){
mpp_packet_deinit(&hdrPkt);
hdrPkt = NULL;
}
C2_SAFE_FREE(hdrBuf);
if (work->input.buffers.empty()) {
work->workletsProcessed = 1u;
return;
}
}
// handle common dynamic config change
handleCommonDynamicCfg();
MyDmaBuffer_t inDmaBuf;
OutWorkEntry entry;
memset(&inDmaBuf, 0, sizeof(MyDmaBuffer_t));
memset(&entry, 0, sizeof(OutWorkEntry));
err = getInBufferFromWork(work, &inDmaBuf);
if (err != C2_OK) {
mSignalledError = true;
work->result = C2_CORRUPTED;
work->workletsProcessed = 1u;
return;
}
/* send frame to mpp */
err = sendframe(inDmaBuf, frameIndex, flags);
if (C2_OK != err) {
c2_err("failed to enqueue frame, err %d", err);
mSignalledError = true;
work->result = C2_CORRUPTED;
work->workletsProcessed = 1u;
return;
}
/* get packet from mpp */
err = getoutpacket(&entry);
if (err == C2_OK) {
finishWork(work, pool, entry);
} else {
if (work && work->workletsProcessed != 1u) {
fillEmptyWork(work);
}
}
if (!mSawInputEOS && work->input.buffers.empty()) {
fillEmptyWork(work);
}
if (mSawInputEOS && !mOutputEOS) {
drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
}
}
c2_status_t C2RKMpiEnc::handleCommonDynamicCfg() {
bool change = false;
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<C2StreamPictureSizeInfo::input> size = mIntf->getSize_l();
std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
std::shared_ptr<C2StreamFrameRateInfo::output> frameRate = mIntf->getFrameRate_l();
uint32_t profile = mIntf->getProfile_l(mCodingType);
lock.unlock();
// handle dynamic size config.
if (size != mSize) {
c2_info("new size request, w %d h %d", size->width, size->height);
mSize = size;
setupBaseCodec();
change = true;
}
// handle dynamic bitrate config.
if (bitrate != mBitrate) {
c2_info("new bitrate request, value %d", bitrate->value);
mBitrate = bitrate;
setupBitRate();
change = true;
}
// handle dynamic frameRate config.
if (frameRate != mFrameRate) {
c2_info("new frameRate request, value %.2f", frameRate->value);
mFrameRate = frameRate;
setupFrameRate();
change = true;
}
// handle dynamic profile config.
if (profile != mProfile) {
c2_info("new profile request, value %s", toStr_Profile(profile, mCodingType));
mProfile = profile;
setupProfileParams();
change = true;
}
if (change) {
int32_t err = mMppMpi->control(mMppCtx, MPP_ENC_SET_CFG, mEncCfg);
if (err) {
c2_err("failed to setup dynamic config, ret %d", err);
}
}
return C2_OK;
}
c2_status_t C2RKMpiEnc::handleRequestSyncFrame() {
int32_t layerPos = 0;
// TODO Is there a better way to count frame layer?
if (mCurLayerCount >= 2) {
layerPos = mInputCount % (2 << (mCurLayerCount - 2));
}
// only handle IDR request at layer 0
if (layerPos == 0) {
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<C2StreamRequestSyncFrameTuning::output> requestSync;
requestSync = mIntf->getRequestSync_l();
lock.unlock();
// we can handle IDR immediately
if (requestSync->value) {
c2_info("got sync request");
// unset request
C2StreamRequestSyncFrameTuning::output clearSync(0u, C2_FALSE);
std::vector<std::unique_ptr<C2SettingResult>> failures;
mIntf->config({ &clearSync }, C2_MAY_BLOCK, &failures);
// force set IDR frame
mMppMpi->control(mMppCtx, MPP_ENC_SET_IDR_FRAME, nullptr);
}
}
return C2_OK;
}
c2_status_t C2RKMpiEnc::handleMlvecDynamicCfg(MppMeta meta) {
int32_t layerCount = 0;
int32_t layerPos = 0;
if (!mMlvec) {
return C2_OK;
}
IntfImpl::Lock lock = mIntf->lock();
C2RKMlvecLegacy::MDynamicCfg cfg;
std::shared_ptr<MlvecParams> params = mIntf->getMlvecParams_l();
std::shared_ptr<C2StreamTemporalLayeringTuning::output> layering =
mIntf->getTemporalLayers_l();
layerCount = layering->m.layerCount;
memset(&cfg, 0, sizeof(cfg));
/* count layer position */
if (layerCount >= 2) {
layerPos = mInputCount % (2 << (layerCount - 2));
c2_trace("layer %d/%d frameNum %d", layerPos, layerCount, mInputCount);
}
if (layerPos == 0) {
if (mCurLayerCount != layerCount) {
c2_info("temporalLayers change, %d to %d", mCurLayerCount, layerCount);
mMlvec->setupMaxTid(layerCount);
mCurLayerCount = layerCount;
}
if (params->ltrMarkFrmCtl->markFrame >= 0) {
c2_trace("ltrMarkFrm change, value %d", params->ltrMarkFrmCtl->markFrame);
cfg.updated |= MLVEC_ENC_MARK_LTR_UPDATED;
cfg.markLtr = params->ltrMarkFrmCtl->markFrame;
params->ltrMarkFrmCtl->markFrame = -1;
}
if (params->ltrUseFrmCtl->useFrame >= 0) {
c2_trace("ltrUseFrm change, value %d", params->ltrUseFrmCtl->useFrame);
cfg.updated |= MLVEC_ENC_USE_LTR_UPDATED;
cfg.useLtr = params->ltrUseFrmCtl->useFrame;
params->ltrUseFrmCtl->useFrame = -1;
}
}
if (params->frameQPCtl->value >= 0) {
c2_trace("frameQP change, value %d", params->frameQPCtl->value);
cfg.updated |= MLVEC_ENC_FRAME_QP_UPDATED;
cfg.frameQP = params->frameQPCtl->value;
params->frameQPCtl->value = -1;
}
if (params->baseLayerPid->value >= 0) {
c2_trace("baseLayerPid change, value %d", params->baseLayerPid->value);
cfg.updated |= MLVEC_ENC_BASE_PID_UPDATED;
cfg.baseLayerPid = params->baseLayerPid->value;
params->baseLayerPid->value = -1;
}
if (cfg.updated) {
mMlvec->setupDynamicConfig(&cfg, meta);
}
return C2_OK;
}
c2_status_t C2RKMpiEnc::getInBufferFromWork(
const std::unique_ptr<C2Work> &work, MyDmaBuffer_t *outBuffer) {
c2_status_t ret = C2_OK;
uint64_t frameIndex = work->input.ordinal.frameIndex.peekull();
bool configChanged = false;
if (work->input.buffers.empty()) {
c2_warn("ignore empty input with frameIndex %lld", frameIndex);
return C2_OK;
}
std::shared_ptr<const C2GraphicView> view;
std::shared_ptr<C2Buffer> inputBuffer = nullptr;
inputBuffer = work->input.buffers[0];
view = std::make_shared<const C2GraphicView>(
inputBuffer->data().graphicBlocks().front().map().get());
const C2GraphicView* const input = view.get();
const C2PlanarLayout& layout = input->layout();
const C2Handle *c2Handle = inputBuffer->data().graphicBlocks().front().handle();
uint32_t bqSlot, width, height, format, stride, generation;
uint64_t usage, bqId;
android::_UnwrapNativeCodec2GrallocMetadata(
c2Handle, &width, &height, &format, &usage,
&stride, &generation, &bqId, &bqSlot);
// Fix error for wifidisplay when stride is 0
if (stride == 0) {
std::vector<ui::PlaneLayout> layouts;
buffer_handle_t bufferHandle;
native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(c2Handle);
GraphicBufferMapper &gm(GraphicBufferMapper::get());
gm.importBuffer(const_cast<native_handle_t *>(grallocHandle),
width, height, 1, format, usage,
stride, &bufferHandle);
gm.getPlaneLayouts(const_cast<native_handle_t *>(bufferHandle), &layouts);
if (layouts[0].sampleIncrementInBits != 0) {
stride = layouts[0].strideInBytes * 8 / layouts[0].sampleIncrementInBits;
} else {
c2_err("layouts[0].sampleIncrementInBits = 0");
stride = mHorStride;
}
gm.freeBuffer(bufferHandle);
native_handle_delete(grallocHandle);
}
c2_trace("in buffer attr. w %d h %d stride %d layout 0x%x frameIndex %lld",
width, height, stride, layout.type, frameIndex);
switch (layout.type) {
case C2PlanarLayout::TYPE_RGB:
[[fallthrough]];
case C2PlanarLayout::TYPE_RGBA: {
uint32_t fd = c2Handle->data[0];
/* dump input data if neccessary */
mDump->recordInFile((void*)input->data()[0], stride, height, RAW_TYPE_RGBA);
if ((mChipType == RK_CHIP_3588 && mCodingType != MPP_VIDEO_CodingVP8)
|| !((stride & 0xf) || (height & 0xf))) {
outBuffer->fd = fd;
outBuffer->size = mHorStride * mVerStride * 4;
if (mInputMppFmt != MPP_FMT_RGBA8888) {
c2_info("update use rgba input format.");
mInputMppFmt = MPP_FMT_RGBA8888;
configChanged = true;
}
} else {
RgaInfo src, dst;
C2RKRgaDef::SetRgaInfo(&src, fd, width, height, stride, height);
C2RKRgaDef::SetRgaInfo(&dst, mDmaMem->fd,
mSize->width, mSize->height, mHorStride, mVerStride);
if (!C2RKRgaDef::RGBToNV12(src, dst)) {
c2_err("faild to convert rgba to nv12");
ret = C2_CORRUPTED;
}
outBuffer->fd = mDmaMem->fd;
outBuffer->size = mHorStride * mVerStride * 3 / 2;
}
} break;
case C2PlanarLayout::TYPE_YUV: {
uint32_t fd = c2Handle->data[0];
/* dump input data if neccessary */
mDump->recordInFile((void*)input->data()[0], stride, height, RAW_TYPE_YUV420SP);
if (mInputMppFmt != MPP_FMT_YUV420SP) {
c2_info("update use yuv input format.");
mInputMppFmt = MPP_FMT_YUV420SP;
configChanged = true;
}
/*
* mpp-driver fetch buffer 16 bits at one time, so the stride of
* input buffer shoule be aligned to 16.
* For this reason if the stride of buffer not aligned to 16, we
* copy input buffer to anothor larger dmaBuffer, and than import
* this dmaBuffer to encoder.
*/
if ((mChipType != RK_CHIP_3588) && ((stride & 0xf) || (height & 0xf))) {
RgaInfo src, dst;
C2RKRgaDef::SetRgaInfo(&src, fd, width, height, stride, height);
C2RKRgaDef::SetRgaInfo(&dst, mDmaMem->fd,
mSize->width, mSize->height, mHorStride, mVerStride);
if (!C2RKRgaDef::NV12ToNV12(src, dst)) {
c2_err("faild to copy nv12");
ret = C2_CORRUPTED;
}
outBuffer->fd = mDmaMem->fd;
outBuffer->size = mHorStride * mVerStride * 3 / 2;
} else {
if (mHorStride != stride || mVerStride != height) {
// setup encoder using new stride config
c2_info("cfg stride change from [%d:%d] -> [%d %d]",
mHorStride, mVerStride, stride, height);
mHorStride = stride;
mVerStride = height;
configChanged = true;
}
outBuffer->fd = fd;
outBuffer->size = mHorStride * mVerStride * 3 / 2;
}
} break;
default:
c2_err("Unrecognized plane type: %d", layout.type);
ret = C2_BAD_VALUE;
}
if (configChanged) {
if (mInputMppFmt == MPP_FMT_RGBA8888) {
mpp_enc_cfg_set_s32(mEncCfg, "prep:hor_stride", mHorStride * 4);
} else {
mpp_enc_cfg_set_s32(mEncCfg, "prep:hor_stride", mHorStride);
}
mpp_enc_cfg_set_s32(mEncCfg, "prep:ver_stride", mVerStride);
mpp_enc_cfg_set_s32(mEncCfg, "prep:format", mInputMppFmt);
int err = mMppMpi->control(mMppCtx, MPP_ENC_SET_CFG, mEncCfg);
if (err) {
c2_err("failed to setup new mpp config.");
ret = C2_CORRUPTED;
}
}
return ret;
}
c2_status_t C2RKMpiEnc::sendframe(
MyDmaBuffer_t dBuffer, uint64_t pts, uint32_t flags) {
int err = 0;
c2_status_t ret = C2_OK;
MppFrame frame = nullptr;
mpp_frame_init(&frame);
if (flags & C2FrameData::FLAG_END_OF_STREAM) {
c2_info("send input eos");
mpp_frame_set_eos(frame, 1);
}
c2_trace("send frame fd %d size %d pts %lld", dBuffer.fd, dBuffer.size, pts);
if (dBuffer.fd > 0) {
MppBuffer buffer = nullptr;
MppBufferInfo commit;
memset(&commit, 0, sizeof(commit));
commit.type = MPP_BUFFER_TYPE_ION;
commit.fd = dBuffer.fd;
commit.size = dBuffer.size;
err = mpp_buffer_import(&buffer, &commit);
if (err) {
c2_err("failed to import input buffer");
ret = C2_NOT_FOUND;
goto error;
}
mpp_frame_set_buffer(frame, buffer);
mpp_buffer_put(buffer);
buffer = nullptr;
} else {
mpp_frame_set_buffer(frame, nullptr);
}
mpp_frame_set_width(frame, mSize->width);
mpp_frame_set_height(frame, mSize->height);
mpp_frame_set_ver_stride(frame, mVerStride);
mpp_frame_set_pts(frame, pts);
mpp_frame_set_fmt(frame, mInputMppFmt);
switch(mInputMppFmt) {
case MPP_FMT_RGBA8888:
mpp_frame_set_hor_stride(frame, mHorStride * 4);
break;
case MPP_FMT_YUV420P:
case MPP_FMT_YUV420SP:
mpp_frame_set_hor_stride(frame, mHorStride);
break;
default:
break;
}
/* handle dynamic configurations from teams mlvec */
if (mMlvec) {
MppMeta meta = mpp_frame_get_meta(frame);
handleMlvecDynamicCfg(meta);
}
/* handle IDR request */
handleRequestSyncFrame();
err = mMppMpi->encode_put_frame(mMppCtx, frame);
if (err) {
c2_err("failed to put_frame, err %d", err);
ret = C2_NOT_FOUND;
goto error;
}
/* dump show input process fps if neccessary */
mDump->showDebugFps(DUMP_ROLE_INPUT);
mInputCount++;
ret = C2_OK;
error:
if (frame) {
mpp_frame_deinit(&frame);
}
return ret;
}
c2_status_t C2RKMpiEnc::getoutpacket(OutWorkEntry *entry) {
int err = 0;
MppPacket packet = nullptr;
err = mMppMpi->encode_get_packet(mMppCtx, &packet);
if (err) {
return C2_NOT_FOUND;
} else {
int64_t pts = mpp_packet_get_pts(packet);
size_t len = mpp_packet_get_length(packet);
uint32_t eos = mpp_packet_get_eos(packet);
void *data = mpp_packet_get_data(packet);
mOutputCount++;
c2_trace("get outpacket pts %lld size %d eos %d", pts, len, eos);
/* dump output data if neccessary */
mDump->recordOutFile(data, len);
/* dump show input process fps if neccessary */
mDump->showDebugFps(DUMP_ROLE_OUTPUT);
if (eos) {
c2_info("get output eos");
mOutputEOS = true;
if (pts == 0 || !len) {
c2_info("eos with empty pkt");
return C2_CORRUPTED;
}
}
if (!len) {
c2_warn("ignore empty output with pts %lld", pts);
return C2_CORRUPTED;
}
entry->frameIndex = pts;
entry->outPacket = packet;
return C2_OK;
}
}
class C2RKMpiEncFactory : public C2ComponentFactory {
public:
explicit C2RKMpiEncFactory(std::string componentName)
: mHelper(std::static_pointer_cast<C2ReflectorHelper>(
GetCodec2PlatformComponentStore()->getParamReflector())),
mComponentName(componentName) {
if (!C2RKMediaUtils::getMimeFromComponentName(componentName, &mMime)) {
c2_err("failed to get mime from component %s", componentName.c_str());
}
if (!C2RKMediaUtils::getDomainFromComponentName(componentName, &mDomain)) {
c2_err("failed to get domain from component %s", componentName.c_str());
}
if (!C2RKMediaUtils::getKindFromComponentName(componentName, &mKind)) {
c2_err("failed to get kind from component %s", componentName.c_str());
}
}
virtual c2_status_t createComponent(
c2_node_id_t id,
std::shared_ptr<C2Component>* const component,
std::function<void(C2Component*)> deleter) override {
if (sEncConcurrentInstances.load() >= kMaxEncConcurrentInstances) {
c2_warn("Reject to Initialize() due to too many enc instances: %d",
sEncConcurrentInstances.load());
return C2_NO_MEMORY;
}
*component = std::shared_ptr<C2Component>(
new C2RKMpiEnc(
mComponentName.c_str(),
id,
std::make_shared<C2RKMpiEnc::IntfImpl>
(mHelper, mComponentName, mKind, mDomain, mMime)),
deleter);
return C2_OK;
}
virtual c2_status_t createInterface(
c2_node_id_t id,
std::shared_ptr<C2ComponentInterface>* const interface,
std::function<void(C2ComponentInterface*)> deleter) override {
c2_log_func_enter();
*interface = std::shared_ptr<C2ComponentInterface>(
new C2RKInterface<C2RKMpiEnc::IntfImpl>(
mComponentName.c_str(),
id,
std::make_shared<C2RKMpiEnc::IntfImpl>
(mHelper, mComponentName, mKind, mDomain, mMime)),
deleter);
return C2_OK;
}
virtual ~C2RKMpiEncFactory() override = default;
private:
std::shared_ptr<C2ReflectorHelper> mHelper;
std::string mComponentName;
std::string mMime;
C2Component::kind_t mKind;
C2Component::domain_t mDomain;
};
C2ComponentFactory* CreateRKMpiEncFactory(std::string componentName) {
return new ::android::C2RKMpiEncFactory(componentName);
}
} // namespace android