Test Android HW codecs

Bug: b/261160916, webrtc:14852
Change-Id: Iebeab244a9ca6ae196735016064ccd056b7c888e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/293401
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39326}
This commit is contained in:
Sergey Silkin 2023-02-16 13:57:22 +01:00 committed by WebRTC LUCI CQ
parent 2bdf79ac91
commit 72b99a1128
4 changed files with 98 additions and 60 deletions

View File

@ -89,6 +89,8 @@ class VideoCodecTester {
virtual ~Encoder() = default;
virtual void Encode(const VideoFrame& frame, EncodeCallback callback) = 0;
virtual void Flush() = 0;
};
// Interface for a video decoder.
@ -100,6 +102,8 @@ class VideoCodecTester {
virtual ~Decoder() = default;
virtual void Decode(const EncodedImage& frame, DecodeCallback callback) = 0;
virtual void Flush() = 0;
};
// Pulls coded video frames from `video_source` and passes them to `decoder`.

View File

@ -32,6 +32,9 @@
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/video_coding/include/video_error_codes.h"
#include "modules/video_coding/svc/scalability_mode_util.h"
#if defined(WEBRTC_ANDROID)
#include "modules/video_coding/codecs/test/android_codec_factory_helper.h"
#endif
#include "rtc_base/strings/string_builder.h"
#include "test/gtest.h"
#include "test/testsupport/file_utils.h"
@ -43,6 +46,7 @@ namespace test {
namespace {
using ::testing::Combine;
using ::testing::Values;
using PacingMode = VideoCodecTester::PacingSettings::PacingMode;
struct VideoInfo {
std::string name;
@ -92,10 +96,6 @@ struct DecodingTestSettings {
std::string name;
};
struct QualityExpectations {
double min_apsnr_y;
};
struct EncodeDecodeTestParams {
CodecInfo codec;
VideoInfo video;
@ -103,7 +103,9 @@ struct EncodeDecodeTestParams {
VideoCodecTester::DecoderSettings decoder_settings;
EncodingTestSettings encoding_settings;
DecodingTestSettings decoding_settings;
QualityExpectations quality_expectations;
struct Expectations {
double min_apsnr_y;
} test_expectations;
};
const EncodingSettings kQvga64Kbps30Fps = {
@ -118,24 +120,10 @@ const EncodingTestSettings kConstantRateQvga64Kbps30Fps = {
.num_frames = 300,
.frame_settings = {{/*frame_num=*/0, kQvga64Kbps30Fps}}};
const QualityExpectations kLowQuality = {.min_apsnr_y = 30};
const VideoInfo kFourPeople_1280x720_30 = {
.name = "FourPeople_1280x720_30",
.resolution = {.width = 1280, .height = 720}};
const CodecInfo kLibvpxVp8 = {.type = "VP8",
.encoder = "libvpx",
.decoder = "libvpx"};
const CodecInfo kLibvpxVp9 = {.type = "VP9",
.encoder = "libvpx",
.decoder = "libvpx"};
const CodecInfo kOpenH264 = {.type = "H264",
.encoder = "openh264",
.decoder = "ffmpeg"};
class TestRawVideoSource : public VideoCodecTester::RawVideoSource {
public:
static constexpr Frequency k90kHz = Frequency::Hertz(90000);
@ -236,11 +224,17 @@ class TestEncoder : public VideoCodecTester::Encoder,
}
}
int result = encoder_->Encode(frame, nullptr);
RTC_CHECK_EQ(result, WEBRTC_VIDEO_CODEC_OK);
encoder_->Encode(frame, nullptr);
++frame_num_;
}
void Flush() override {
// TODO(webrtc:14852): For codecs which buffer frames we need a to
// flush them to get last frames. Add such functionality to VideoEncoder
// API. On Android it will map directly to `MediaCodec.flush()`.
encoder_->Release();
}
protected:
Result OnEncodedImage(const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info) override {
@ -331,6 +325,7 @@ class TestDecoder : public VideoCodecTester::Decoder,
: decoder_(std::move(decoder)), codec_info_(codec_info), frame_num_(0) {
decoder_->RegisterDecodeCompleteCallback(this);
}
void Decode(const EncodedImage& frame, DecodeCallback callback) override {
callbacks_[frame.Timestamp()] = std::move(callback);
@ -343,16 +338,24 @@ class TestDecoder : public VideoCodecTester::Decoder,
++frame_num_;
}
void Flush() override {
// TODO(webrtc:14852): For codecs which buffer frames we need a to
// flush them to get last frames. Add such functionality to VideoDecoder
// API. On Android it will map directly to `MediaCodec.flush()`.
decoder_->Release();
}
protected:
void Configure() {
VideoDecoder::Settings ds;
ds.set_codec_type(PayloadStringToCodecType(codec_info_.type));
ds.set_number_of_cores(1);
ds.set_max_render_resolution({1280, 720});
bool result = decoder_->Configure(ds);
RTC_CHECK(result);
}
protected:
int Decoded(VideoFrame& decoded_frame) override {
auto cb = callbacks_.find(decoded_frame.timestamp());
RTC_CHECK(cb != callbacks_.end());
@ -371,7 +374,18 @@ class TestDecoder : public VideoCodecTester::Decoder,
std::unique_ptr<VideoCodecTester::Encoder> CreateEncoder(
const CodecInfo& codec_info,
const std::map<int, EncodingSettings>& frame_settings) {
auto factory = CreateBuiltinVideoEncoderFactory();
std::unique_ptr<VideoEncoderFactory> factory;
if (codec_info.encoder == "libvpx" || codec_info.encoder == "libaom" ||
codec_info.encoder == "openh264") {
factory = CreateBuiltinVideoEncoderFactory();
} else if (codec_info.encoder == "mediacodec") {
#if defined(WEBRTC_ANDROID)
InitializeAndroidObjects();
factory = CreateAndroidEncoderFactory();
#endif
}
RTC_CHECK(factory);
auto encoder = factory->CreateVideoEncoder(SdpVideoFormat(codec_info.type));
return std::make_unique<TestEncoder>(std::move(encoder), codec_info,
frame_settings);
@ -379,7 +393,18 @@ std::unique_ptr<VideoCodecTester::Encoder> CreateEncoder(
std::unique_ptr<VideoCodecTester::Decoder> CreateDecoder(
const CodecInfo& codec_info) {
auto factory = CreateBuiltinVideoDecoderFactory();
std::unique_ptr<VideoDecoderFactory> factory;
if (codec_info.decoder == "libvpx" || codec_info.decoder == "dav1d" ||
codec_info.decoder == "ffmpeg") {
factory = CreateBuiltinVideoDecoderFactory();
} else if (codec_info.decoder == "mediacodec") {
#if defined(WEBRTC_ANDROID)
InitializeAndroidObjects();
factory = CreateAndroidDecoderFactory();
#endif
}
RTC_CHECK(factory);
auto decoder = factory->CreateVideoDecoder(SdpVideoFormat(codec_info.type));
return std::make_unique<TestDecoder>(std::move(decoder), codec_info);
}
@ -410,7 +435,7 @@ class EncodeDecodeTest
const ::testing::TestParamInfo<EncodeDecodeTest::ParamType>& info) {
return std::string(info.param.encoding_settings.name +
info.param.codec.type + info.param.codec.encoder +
info.param.codec.decoder);
info.param.codec.decoder + info.param.video.name);
}
protected:
@ -437,35 +462,37 @@ TEST_P(EncodeDecodeTest, DISABLED_TestEncodeDecode) {
std::vector<VideoCodecStats::Frame> frames = stats->Slice(slicer);
VideoCodecStats::Stream stream = stats->Aggregate(frames);
EXPECT_GE(stream.psnr.y.GetAverage(),
test_params_.quality_expectations.min_apsnr_y);
test_params_.test_expectations.min_apsnr_y);
}
}
std::list<EncodeDecodeTestParams> ConstantRateTestParameters() {
std::list<EncodeDecodeTestParams> test_params;
std::vector<CodecInfo> codecs = {kLibvpxVp8};
std::vector<VideoInfo> videos = {kFourPeople_1280x720_30};
std::vector<std::pair<EncodingTestSettings, QualityExpectations>>
encoding_settings = {{kConstantRateQvga64Kbps30Fps, kLowQuality}};
for (const CodecInfo& codec : codecs) {
for (const VideoInfo& video : videos) {
for (const auto& es : encoding_settings) {
EncodeDecodeTestParams p;
p.codec = codec;
p.video = video;
p.encoding_settings = es.first;
p.quality_expectations = es.second;
test_params.push_back(p);
}
}
}
return test_params;
}
INSTANTIATE_TEST_SUITE_P(ConstantRate,
EncodeDecodeTest,
::testing::ValuesIn(ConstantRateTestParameters()),
EncodeDecodeTest::TestParametersToStr);
INSTANTIATE_TEST_SUITE_P(
ConstantRate,
EncodeDecodeTest,
::testing::ValuesIn({
EncodeDecodeTestParams({
.codec = {.type = "VP8", .encoder = "libvpx", .decoder = "libvpx"},
.video = kFourPeople_1280x720_30,
.encoder_settings = {.pacing = {.mode = PacingMode::kNoPacing}},
.decoder_settings = {.pacing = {.mode = PacingMode::kNoPacing}},
.encoding_settings = kConstantRateQvga64Kbps30Fps,
.test_expectations = {.min_apsnr_y = 30.0},
})
#if defined(WEBRTC_ANDROID)
,
EncodeDecodeTestParams({
.codec = {.type = "VP8",
.encoder = "mediacodec",
.decoder = "mediacodec"},
.video = kFourPeople_1280x720_30,
.encoder_settings = {.pacing = {.mode = PacingMode::kRealTime}},
.decoder_settings = {.pacing = {.mode = PacingMode::kRealTime}},
.encoding_settings = kConstantRateQvga64Kbps30Fps,
.test_expectations = {.min_apsnr_y = 30.0},
})
#endif
}),
EncodeDecodeTest::TestParametersToStr);
} // namespace test
} // namespace webrtc

View File

@ -142,10 +142,7 @@ class LimitedTaskQueue {
}
void WaitForPreviouslyPostedTasks() {
while (queue_size_ > 0) {
task_executed_.Wait(rtc::Event::kForever);
task_executed_.Reset();
}
task_queue_.SendTask([] {});
}
TaskQueueForTest task_queue_;
@ -174,13 +171,17 @@ class TesterDecoder {
decoder_->Decode(
frame, [this, spatial_idx = frame.SpatialIndex().value_or(0)](
const VideoFrame& decoded_frame) {
this->analyzer_->FinishDecode(decoded_frame, spatial_idx);
analyzer_->FinishDecode(decoded_frame, spatial_idx);
});
},
pacer_.Schedule(timestamp));
}
void Flush() { task_queue_.WaitForPreviouslyPostedTasks(); }
void Flush() {
Timestamp now = Timestamp::Micros(rtc::TimeMicros());
task_queue_.PostScheduledTask([this] { decoder_->Flush(); }, now);
task_queue_.WaitForPreviouslyPostedTasks();
}
protected:
Decoder* const decoder_;
@ -211,16 +212,20 @@ class TesterEncoder {
[this, frame] {
analyzer_->StartEncode(frame);
encoder_->Encode(frame, [this](const EncodedImage& encoded_frame) {
this->analyzer_->FinishEncode(encoded_frame);
analyzer_->FinishEncode(encoded_frame);
if (decoder_ != nullptr) {
this->decoder_->Decode(encoded_frame);
decoder_->Decode(encoded_frame);
}
});
},
pacer_.Schedule(timestamp));
}
void Flush() { task_queue_.WaitForPreviouslyPostedTasks(); }
void Flush() {
Timestamp now = Timestamp::Micros(rtc::TimeMicros());
task_queue_.PostScheduledTask([this] { encoder_->Flush(); }, now);
task_queue_.WaitForPreviouslyPostedTasks();
}
protected:
Encoder* const encoder_;

View File

@ -121,6 +121,7 @@ class MockDecoder : public Decoder {
Decode,
(const EncodedImage& frame, DecodeCallback callback),
(override));
MOCK_METHOD(void, Flush, (), (override));
};
class MockEncoder : public Encoder {
@ -129,6 +130,7 @@ class MockEncoder : public Encoder {
Encode,
(const VideoFrame& frame, EncodeCallback callback),
(override));
MOCK_METHOD(void, Flush, (), (override));
};
class MockTaskQueueFactory : public TaskQueueFactory {