blob: 6d8f037bb84f3d8a057945d9a62a3f6ee82dd7da [file] [log] [blame]
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//#define LOG_NDEBUG 0
#define LOG_TAG "V4L2EncodeComponent"
#include <v4l2_codec2/v4l2/V4L2EncodeComponent.h>
#include <base/bind_helpers.h>
#include <cutils/properties.h>
#include <v4l2_codec2/components/BitstreamBuffer.h>
#include <v4l2_codec2/components/EncodeInterface.h>
#include <v4l2_codec2/v4l2/V4L2Encoder.h>
namespace android {
namespace {
// Check whether the specified |profile| is an H.264 profile.
bool IsH264Profile(C2Config::profile_t profile) {
return (profile >= C2Config::PROFILE_AVC_BASELINE &&
profile <= C2Config::PROFILE_AVC_ENHANCED_MULTIVIEW_DEPTH_HIGH);
}
} // namespace
// static
std::atomic<int32_t> V4L2EncodeComponent::sConcurrentInstances = 0;
// static
std::shared_ptr<C2Component> V4L2EncodeComponent::create(
C2String name, c2_node_id_t id, std::shared_ptr<EncodeInterface> intfImpl,
C2ComponentFactory::ComponentDeleter deleter) {
ALOGV("%s(%s)", __func__, name.c_str());
static const int32_t kMaxConcurrentInstances =
property_get_int32("ro.vendor.v4l2_codec2.encode_concurrent_instances", -1);
static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex);
if (kMaxConcurrentInstances >= 0 && sConcurrentInstances.load() >= kMaxConcurrentInstances) {
ALOGW("Cannot create additional encoder, maximum number of instances reached: %d",
kMaxConcurrentInstances);
return nullptr;
}
return std::shared_ptr<C2Component>(new V4L2EncodeComponent(name, id, std::move(intfImpl)),
deleter);
}
V4L2EncodeComponent::V4L2EncodeComponent(C2String name, c2_node_id_t id,
std::shared_ptr<EncodeInterface> interface)
: EncodeComponent(name, id, interface) {
ALOGV("%s():", __func__);
sConcurrentInstances.fetch_add(1, std::memory_order_relaxed);
}
V4L2EncodeComponent::~V4L2EncodeComponent() {
ALOGV("%s():", __func__);
sConcurrentInstances.fetch_sub(1, std::memory_order_relaxed);
}
bool V4L2EncodeComponent::initializeEncoder() {
ALOGV("%s()", __func__);
ALOG_ASSERT(mEncoderTaskRunner->RunsTasksInCurrentSequence());
ALOG_ASSERT(!mInputFormatConverter);
ALOG_ASSERT(!mEncoder);
mLastFrameTime = std::nullopt;
// Get the requested profile and level.
C2Config::profile_t outputProfile = mInterface->getOutputProfile();
// CSD only needs to be extracted when using an H.264 profile.
mExtractCSD = IsH264Profile(outputProfile);
std::optional<uint8_t> h264Level;
if (IsH264Profile(outputProfile)) {
h264Level = c2LevelToV4L2Level(mInterface->getOutputLevel());
}
// Get the stride used by the C2 framework, as this might be different from the stride used by
// the V4L2 encoder.
std::optional<uint32_t> stride =
getVideoFrameStride(VideoEncoder::kInputPixelFormat, mInterface->getInputVisibleSize());
if (!stride) {
ALOGE("Failed to get video frame stride");
reportError(C2_CORRUPTED);
return false;
}
// Get the requested bitrate mode and bitrate. The C2 framework doesn't offer a parameter to
// configure the peak bitrate, so we use a multiple of the target bitrate.
mBitrateMode = mInterface->getBitrateMode();
if (property_get_bool("persist.vendor.v4l2_codec2.disable_vbr", false)) {
// NOTE: This is a workaround for b/235771157.
ALOGW("VBR is disabled on this device");
mBitrateMode = C2Config::BITRATE_CONST;
}
mBitrate = mInterface->getBitrate();
mEncoder = V4L2Encoder::create(
outputProfile, h264Level, mInterface->getInputVisibleSize(), *stride,
mInterface->getKeyFramePeriod(), mBitrateMode, mBitrate,
mBitrate * VideoEncoder::kPeakBitrateMultiplier,
::base::BindRepeating(&V4L2EncodeComponent::fetchOutputBlock, mWeakThis),
::base::BindRepeating(&V4L2EncodeComponent::onInputBufferDone, mWeakThis),
::base::BindRepeating(&V4L2EncodeComponent::onOutputBufferDone, mWeakThis),
::base::BindRepeating(&V4L2EncodeComponent::onDrainDone, mWeakThis),
::base::BindRepeating(&V4L2EncodeComponent::reportError, mWeakThis, C2_CORRUPTED),
mEncoderTaskRunner);
if (!mEncoder) {
ALOGE("Failed to create V4L2Encoder (profile: %s)", profileToString(outputProfile));
return false;
}
return true;
}
} // namespace android