blob: 5027931edb9b371ec9da86dfb5b760138de57304 [file]
/*
* Copyright (c) 2026 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef API_VIDEO_CODECS_TEST_VIDEO_CODEC_TEST_UTILS_H_
#define API_VIDEO_CODECS_TEST_VIDEO_CODEC_TEST_UTILS_H_
#include <algorithm>
#include <cstdint>
#include <memory>
#include <span>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
#include "absl/strings/match.h"
#include "api/environment/environment.h"
#include "api/scoped_refptr.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/video/encoded_image.h"
#include "api/video/video_frame.h"
#include "api/video/video_frame_buffer.h"
#include "api/video_codecs/video_decoder.h"
#include "api/video_codecs/video_decoder_factory.h"
#include "api/video_codecs/video_decoder_factory_template.h"
#include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h"
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp8_adapter.h"
#include "api/video_codecs/video_decoder_factory_template_libvpx_vp9_adapter.h"
#include "api/video_codecs/video_decoder_factory_template_open_h264_adapter.h"
#include "api/video_codecs/video_encoder_builders.h"
#include "api/video_codecs/video_encoder_builders_for_test.h"
#include "api/video_codecs/video_encoder_factory_interface.h"
#include "api/video_codecs/video_encoder_interface.h"
#include "api/video_codecs/video_encoding_general.h"
#include "common_video/libyuv/include/webrtc_libyuv.h"
#include "rtc_base/checks.h"
#include "rtc_base/event.h"
#include "test/testsupport/file_utils.h"
#include "test/testsupport/frame_reader.h"
namespace webrtc {
using Cbr = VideoEncoderInterface::FrameEncodeSettings::Cbr;
using Cqp = VideoEncoderInterface::FrameEncodeSettings::Cqp;
using EncodedData = VideoEncoderInterface::EncodedData;
using FrameType = VideoEncoderInterface::FrameType;
using TemporalUnitSettings = VideoEncoderInterface::TemporalUnitSettings;
using EncOut = TestEncodedOutput;
using Fb = FrameEncodeSettingsBuilderForTest;
using FactoryCreator = std::unique_ptr<VideoEncoderFactoryInterface> (*)();
template <int N>
std::vector<VideoEncoderInterface::FrameEncodeSettings> ToVec(
VideoEncoderInterface::FrameEncodeSettings (&&settings)[N]) {
return std::vector<VideoEncoderInterface::FrameEncodeSettings>(
std::make_move_iterator(std::begin(settings)),
std::make_move_iterator(std::end(settings)));
}
inline double Psnr(const scoped_refptr<I420BufferInterface>& ref_buffer,
const VideoFrame& decoded_frame) {
return I420PSNR(*ref_buffer, *decoded_frame.video_frame_buffer()->ToI420());
}
inline std::unique_ptr<test::FrameReader> CreateFrameReader() {
return CreateY4mFrameReader(
test::ResourcePath("reference_video_640x360_30fps", "y4m"),
test::YuvFrameReaderImpl::RepeatMode::kPingPong);
}
inline std::unique_ptr<VideoDecoderFactory> CreateTestDecoderFactory() {
return std::make_unique<VideoDecoderFactoryTemplate<
LibvpxVp8DecoderTemplateAdapter, LibvpxVp9DecoderTemplateAdapter,
OpenH264DecoderTemplateAdapter, Dav1dDecoderTemplateAdapter>>();
}
class TestDecoder : public DecodedImageCallback {
public:
TestDecoder(const Environment& env,
VideoDecoderFactory* factory,
const std::string& codec_name) {
for (const auto& format : factory->GetSupportedFormats()) {
if (absl::EqualsIgnoreCase(format.name, codec_name)) {
decoder_ = factory->Create(env, format);
break;
}
}
if (decoder_) {
decoder_->Configure({});
decoder_->RegisterDecodeCompleteCallback(this);
}
}
bool IsSupported() const { return decoder_ != nullptr; }
// DecodedImageCallback
int32_t Decoded(VideoFrame& frame) override {
decode_result_ = std::make_unique<VideoFrame>(std::move(frame));
decoded_event_.Set();
return 0;
}
VideoFrame Decode(std::span<const uint8_t> bitstream_data) {
RTC_CHECK(decoder_);
EncodedImage img;
img.SetEncodedData(EncodedImageBuffer::Create(bitstream_data.data(),
bitstream_data.size()));
decoded_event_.Reset();
decoder_->Decode(img, /*dont_care=*/0);
RTC_CHECK(decoded_event_.Wait(TimeDelta::Seconds(5)));
RTC_CHECK(decode_result_);
VideoFrame res(std::move(*decode_result_));
decode_result_.reset();
return res;
}
private:
std::unique_ptr<VideoDecoder> decoder_;
std::unique_ptr<VideoFrame> decode_result_;
Event decoded_event_;
};
struct TestConfig {
VideoEncoderFactoryInterface::StaticEncoderSettings static_settings;
std::variant<VideoEncoderInterface::FrameEncodeSettings::Cbr,
VideoEncoderInterface::FrameEncodeSettings::Cqp>
rate_options;
};
inline void ConfigureRate(FrameEncodeSettingsBuilderForTest& fb,
const std::variant<Cbr, Cqp>& rate_options) {
std::visit(
[&fb](const auto& rate) -> void {
using T = std::decay_t<decltype(rate)>;
if constexpr (std::is_same_v<
T, VideoEncoderInterface::FrameEncodeSettings::Cbr>) {
fb.Cbr(rate);
} else if constexpr (std::is_same_v<T, VideoEncoderInterface::
FrameEncodeSettings::Cqp>) {
fb.Cqp(rate.target_qp);
}
},
rate_options);
}
inline VideoEncoderInterface::FrameEncodeSettings BuildSettings(
FrameEncodeSettingsBuilderForTest&& fb,
const std::variant<Cbr, Cqp>& rate_options) {
ConfigureRate(fb, rate_options);
return fb.Build();
}
inline TestConfig CreateTestConfig(
const VideoEncoderFactoryInterface::Capabilities& capabilities) {
const auto& rc_modes = capabilities.bitrate_control().rc_modes();
RTC_DCHECK(!rc_modes.empty()) << "Encoder must support at least one RC mode.";
TestConfig config;
if (std::find(rc_modes.begin(), rc_modes.end(),
VideoEncoderFactoryInterface::RateControlMode::kCqp) !=
rc_modes.end()) {
config.static_settings =
StaticEncoderSettingsBuilder()
.MaxEncodeDimensions({.width = 640, .height = 360})
.EncodingFormat({.sub_sampling = EncodingFormat::SubSampling::k420,
.bit_depth = 8})
.CqpRcMode()
.MaxNumberOfThreads(1)
.Build();
config.rate_options = VideoEncoderInterface::FrameEncodeSettings::Cqp{
.target_qp = std::clamp(20, capabilities.bitrate_control().min_qp(),
capabilities.bitrate_control().max_qp())};
} else {
config.static_settings =
StaticEncoderSettingsBuilder()
.MaxEncodeDimensions({.width = 640, .height = 360})
.EncodingFormat({.sub_sampling = EncodingFormat::SubSampling::k420,
.bit_depth = 8})
.CbrRcMode(TimeDelta::Millis(1000), TimeDelta::Millis(600))
.MaxNumberOfThreads(1)
.Build();
config.rate_options = VideoEncoderInterface::FrameEncodeSettings::Cbr{
.duration = TimeDelta::Millis(100),
.target_bitrate = DataRate::KilobitsPerSec(1000)};
}
return config;
}
} // namespace webrtc
#endif // API_VIDEO_CODECS_TEST_VIDEO_CODEC_TEST_UTILS_H_