mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00

Bug: b/336978562 Change-Id: I25cb6662a6b66fba9d22d974e1b6da48e137d0ea Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/349225 Reviewed-by: Erik Språng <sprang@webrtc.org> Commit-Queue: Philip Eliasson <philipel@webrtc.org> Cr-Commit-Position: refs/heads/main@{#42284}
302 lines
11 KiB
C++
302 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2024 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.
|
|
*/
|
|
|
|
#include "api/video_codecs/simple_encoder_wrapper.h"
|
|
|
|
#include "api/video/i420_buffer.h"
|
|
#include "api/video_codecs/libaom_av1_encoder_factory.h"
|
|
#include "api/video_codecs/video_encoder_interface.h"
|
|
#include "test/gmock.h"
|
|
#include "test/gtest.h"
|
|
#include "test/testsupport/file_utils.h"
|
|
#include "test/testsupport/frame_reader.h"
|
|
|
|
namespace webrtc {
|
|
|
|
using ::testing::Eq;
|
|
using ::testing::Gt;
|
|
using ::testing::IsEmpty;
|
|
using ::testing::Ne;
|
|
using ::testing::Not;
|
|
using ::testing::NotNull;
|
|
using ::testing::UnorderedElementsAre;
|
|
using PredictionConstraints =
|
|
VideoEncoderFactoryInterface::Capabilities::PredictionConstraints;
|
|
|
|
namespace {
|
|
|
|
std::unique_ptr<test::FrameReader> CreateFrameReader() {
|
|
return CreateY4mFrameReader(
|
|
test::ResourcePath("reference_video_640x360_30fps", "y4m"),
|
|
test::YuvFrameReaderImpl::RepeatMode::kPingPong);
|
|
}
|
|
|
|
TEST(SimpleEncoderWrapper, SupportedSvcModesOnlyL1T1) {
|
|
PredictionConstraints constraints = {
|
|
.num_buffers = 2,
|
|
.max_references = 2,
|
|
.max_temporal_layers = 1,
|
|
.buffer_space_type =
|
|
PredictionConstraints::BufferSpaceType::kSingleKeyframe,
|
|
.max_spatial_layers = 1,
|
|
.scaling_factors = {{1, 1}},
|
|
};
|
|
|
|
EXPECT_THAT(SimpleEncoderWrapper::SupportedWebrtcSvcModes(constraints),
|
|
UnorderedElementsAre("L1T1"));
|
|
}
|
|
|
|
TEST(SimpleEncoderWrapper, SupportedSvcModesUpToL1T3) {
|
|
PredictionConstraints constraints = {
|
|
.num_buffers = 8,
|
|
.max_references = 1,
|
|
.max_temporal_layers = 3,
|
|
.buffer_space_type =
|
|
PredictionConstraints::BufferSpaceType::kSingleKeyframe,
|
|
.max_spatial_layers = 1,
|
|
.scaling_factors = {{1, 1}, {1, 2}},
|
|
};
|
|
|
|
EXPECT_THAT(SimpleEncoderWrapper::SupportedWebrtcSvcModes(constraints),
|
|
UnorderedElementsAre("L1T1", "L1T2", "L1T3"));
|
|
}
|
|
|
|
TEST(SimpleEncoderWrapper, SupportedSvcModesUpToL3T3Key) {
|
|
PredictionConstraints constraints = {
|
|
.num_buffers = 8,
|
|
.max_references = 2,
|
|
.max_temporal_layers = 3,
|
|
.buffer_space_type =
|
|
PredictionConstraints::BufferSpaceType::kSingleKeyframe,
|
|
.max_spatial_layers = 3,
|
|
.scaling_factors = {{1, 1}, {1, 2}},
|
|
};
|
|
|
|
EXPECT_THAT(
|
|
SimpleEncoderWrapper::SupportedWebrtcSvcModes(constraints),
|
|
UnorderedElementsAre("L1T1", "L1T2", "L1T3", "L2T1", "L2T1_KEY", "L2T2",
|
|
"L2T2_KEY", "L2T3", "L2T3_KEY", "L3T1", "L3T1_KEY",
|
|
"L3T2", "L3T2_KEY", "L3T3", "L3T3_KEY", "S2T1",
|
|
"S2T2", "S2T3", "S3T1", "S3T2", "S3T3"));
|
|
}
|
|
|
|
TEST(SimpleEncoderWrapper, SupportedSvcModesUpToS3T3) {
|
|
PredictionConstraints constraints = {
|
|
.num_buffers = 8,
|
|
.max_references = 2,
|
|
.max_temporal_layers = 3,
|
|
.buffer_space_type =
|
|
PredictionConstraints::BufferSpaceType::kMultiInstance,
|
|
.max_spatial_layers = 3,
|
|
.scaling_factors = {{1, 1}, {1, 2}},
|
|
};
|
|
|
|
EXPECT_THAT(SimpleEncoderWrapper::SupportedWebrtcSvcModes(constraints),
|
|
UnorderedElementsAre("L1T1", "L1T2", "L1T3", "S2T1", "S2T2",
|
|
"S2T3", "S3T1", "S3T2", "S3T3"));
|
|
}
|
|
|
|
TEST(SimpleEncoderWrapper, SupportedSvcModesUpToL3T3KeyWithHScaling) {
|
|
PredictionConstraints constraints = {
|
|
.num_buffers = 8,
|
|
.max_references = 2,
|
|
.max_temporal_layers = 3,
|
|
.buffer_space_type =
|
|
PredictionConstraints::BufferSpaceType::kSingleKeyframe,
|
|
.max_spatial_layers = 3,
|
|
.scaling_factors = {{1, 1}, {1, 2}, {2, 3}},
|
|
};
|
|
|
|
EXPECT_THAT(
|
|
SimpleEncoderWrapper::SupportedWebrtcSvcModes(constraints),
|
|
UnorderedElementsAre(
|
|
"L1T1", "L1T2", "L1T3", "L2T1", "L2T1h", "L2T1_KEY", "L2T1h_KEY",
|
|
"L2T2", "L2T2h", "L2T2_KEY", "L2T2h_KEY", "L2T3", "L2T3h", "L2T3_KEY",
|
|
"L2T3h_KEY", "L3T1", "L3T1h", "L3T1_KEY", "L3T1h_KEY", "L3T2",
|
|
"L3T2h", "L3T2_KEY", "L3T2h_KEY", "L3T3", "L3T3h", "L3T3_KEY",
|
|
"L3T3h_KEY", "S2T1", "S2T1h", "S2T2", "S2T2h", "S2T3", "S2T3h",
|
|
"S3T1", "S3T1h", "S3T2", "S3T2h", "S3T3", "S3T3h"));
|
|
}
|
|
|
|
// TD: The encoder wrapper shouldn't really use an actual encoder
|
|
// implementation for testing, but hey, this is just a PoC.
|
|
TEST(SimpleEncoderWrapper, EncodeL1T1) {
|
|
auto encoder = LibaomAv1EncoderFactory().CreateEncoder(
|
|
{.max_encode_dimensions = {1080, 720},
|
|
.encoding_format = {.sub_sampling = EncodingFormat::k420,
|
|
.bit_depth = 8},
|
|
.rc_mode = VideoEncoderFactoryInterface::StaticEncoderSettings::Cqp(),
|
|
.max_number_of_threads = 1},
|
|
{});
|
|
|
|
std::unique_ptr<SimpleEncoderWrapper> simple_encoder =
|
|
SimpleEncoderWrapper::Create(std::move(encoder), "L1T1");
|
|
|
|
ASSERT_THAT(simple_encoder, NotNull());
|
|
|
|
simple_encoder->SetEncodeQp(30);
|
|
simple_encoder->SetEncodeFps(15);
|
|
auto frame_reader = CreateFrameReader();
|
|
|
|
int num_callbacks = 0;
|
|
simple_encoder->Encode(
|
|
frame_reader->PullFrame(), /*force_keyframe=*/true,
|
|
[&](const SimpleEncoderWrapper::EncodeResult& result) {
|
|
++num_callbacks;
|
|
ASSERT_THAT(result.oh_no, Eq(false));
|
|
EXPECT_THAT(result.dependency_structure, Ne(absl::nullopt));
|
|
EXPECT_THAT(result.bitstream_data, NotNull());
|
|
EXPECT_THAT(result.frame_type, Eq(FrameType::kKeyframe));
|
|
EXPECT_THAT(result.generic_frame_info.spatial_id, Eq(0));
|
|
EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0));
|
|
});
|
|
|
|
simple_encoder->Encode(
|
|
frame_reader->PullFrame(), /*force_keyframe=*/false,
|
|
[&](const SimpleEncoderWrapper::EncodeResult& result) {
|
|
++num_callbacks;
|
|
ASSERT_THAT(result.oh_no, Eq(false));
|
|
EXPECT_THAT(result.dependency_structure, Eq(absl::nullopt));
|
|
EXPECT_THAT(result.bitstream_data, NotNull());
|
|
EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame));
|
|
EXPECT_THAT(result.generic_frame_info.spatial_id, Eq(0));
|
|
EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0));
|
|
});
|
|
}
|
|
|
|
TEST(SimpleEncoderWrapper, EncodeL2T2_KEY) {
|
|
auto encoder = LibaomAv1EncoderFactory().CreateEncoder(
|
|
{.max_encode_dimensions = {1080, 720},
|
|
.encoding_format = {.sub_sampling = EncodingFormat::k420,
|
|
.bit_depth = 8},
|
|
.rc_mode = VideoEncoderFactoryInterface::StaticEncoderSettings::Cqp(),
|
|
.max_number_of_threads = 1},
|
|
{});
|
|
|
|
std::unique_ptr<SimpleEncoderWrapper> simple_encoder =
|
|
SimpleEncoderWrapper::Create(std::move(encoder), "L2T2_KEY");
|
|
|
|
ASSERT_THAT(simple_encoder, NotNull());
|
|
|
|
simple_encoder->SetEncodeQp(30);
|
|
simple_encoder->SetEncodeFps(15);
|
|
auto frame_reader = CreateFrameReader();
|
|
|
|
int num_callbacks = 0;
|
|
simple_encoder->Encode(
|
|
frame_reader->PullFrame(), /*force_keyframe=*/true,
|
|
[&](const SimpleEncoderWrapper::EncodeResult& result) {
|
|
ASSERT_THAT(result.oh_no, Eq(false));
|
|
if (result.generic_frame_info.spatial_id == 0) {
|
|
++num_callbacks;
|
|
EXPECT_THAT(result.dependency_structure, Ne(absl::nullopt));
|
|
EXPECT_THAT(result.bitstream_data, NotNull());
|
|
EXPECT_THAT(result.frame_type, Eq(FrameType::kKeyframe));
|
|
EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0));
|
|
} else if (result.generic_frame_info.spatial_id == 1) {
|
|
++num_callbacks;
|
|
EXPECT_THAT(result.dependency_structure, Eq(absl::nullopt));
|
|
EXPECT_THAT(result.bitstream_data, NotNull());
|
|
EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame));
|
|
EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0));
|
|
}
|
|
});
|
|
|
|
simple_encoder->Encode(
|
|
frame_reader->PullFrame(), /*force_keyframe=*/false,
|
|
[&](const SimpleEncoderWrapper::EncodeResult& result) {
|
|
ASSERT_THAT(result.oh_no, Eq(false));
|
|
if (result.generic_frame_info.spatial_id == 0) {
|
|
++num_callbacks;
|
|
EXPECT_THAT(result.dependency_structure, Eq(absl::nullopt));
|
|
EXPECT_THAT(result.bitstream_data, NotNull());
|
|
EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame));
|
|
EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(1));
|
|
} else if (result.generic_frame_info.spatial_id == 1) {
|
|
++num_callbacks;
|
|
EXPECT_THAT(result.dependency_structure, Eq(absl::nullopt));
|
|
EXPECT_THAT(result.bitstream_data, NotNull());
|
|
EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame));
|
|
EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(1));
|
|
}
|
|
});
|
|
|
|
EXPECT_THAT(num_callbacks, Eq(4));
|
|
}
|
|
|
|
TEST(SimpleEncoderWrapper, EncodeL1T3ForceKeyframe) {
|
|
auto encoder = LibaomAv1EncoderFactory().CreateEncoder(
|
|
{.max_encode_dimensions = {1080, 720},
|
|
.encoding_format = {.sub_sampling = EncodingFormat::k420,
|
|
.bit_depth = 8},
|
|
.rc_mode = VideoEncoderFactoryInterface::StaticEncoderSettings::Cqp(),
|
|
.max_number_of_threads = 1},
|
|
{});
|
|
|
|
std::unique_ptr<SimpleEncoderWrapper> simple_encoder =
|
|
SimpleEncoderWrapper::Create(std::move(encoder), "L1T3");
|
|
|
|
ASSERT_THAT(simple_encoder, NotNull());
|
|
|
|
simple_encoder->SetEncodeQp(30);
|
|
simple_encoder->SetEncodeFps(15);
|
|
auto frame_reader = CreateFrameReader();
|
|
|
|
int num_callbacks = 0;
|
|
simple_encoder->Encode(
|
|
frame_reader->PullFrame(), /*force_keyframe=*/true,
|
|
[&](const SimpleEncoderWrapper::EncodeResult& result) {
|
|
++num_callbacks;
|
|
ASSERT_THAT(result.oh_no, Eq(false));
|
|
EXPECT_THAT(result.frame_type, Eq(FrameType::kKeyframe));
|
|
EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0));
|
|
});
|
|
|
|
simple_encoder->Encode(
|
|
frame_reader->PullFrame(), /*force_keyframe=*/false,
|
|
[&](const SimpleEncoderWrapper::EncodeResult& result) {
|
|
++num_callbacks;
|
|
ASSERT_THAT(result.oh_no, Eq(false));
|
|
EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame));
|
|
EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(2));
|
|
});
|
|
|
|
simple_encoder->Encode(
|
|
frame_reader->PullFrame(), /*force_keyframe=*/false,
|
|
[&](const SimpleEncoderWrapper::EncodeResult& result) {
|
|
++num_callbacks;
|
|
ASSERT_THAT(result.oh_no, Eq(false));
|
|
EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame));
|
|
EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(1));
|
|
});
|
|
|
|
simple_encoder->Encode(
|
|
frame_reader->PullFrame(), /*force_keyframe=*/true,
|
|
[&](const SimpleEncoderWrapper::EncodeResult& result) {
|
|
++num_callbacks;
|
|
ASSERT_THAT(result.oh_no, Eq(false));
|
|
EXPECT_THAT(result.frame_type, Eq(FrameType::kKeyframe));
|
|
EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(0));
|
|
});
|
|
|
|
simple_encoder->Encode(
|
|
frame_reader->PullFrame(), /*force_keyframe=*/false,
|
|
[&](const SimpleEncoderWrapper::EncodeResult& result) {
|
|
++num_callbacks;
|
|
ASSERT_THAT(result.oh_no, Eq(false));
|
|
EXPECT_THAT(result.frame_type, Eq(FrameType::kDeltaFrame));
|
|
EXPECT_THAT(result.generic_frame_info.temporal_id, Eq(2));
|
|
});
|
|
|
|
EXPECT_THAT(num_callbacks, Eq(5));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace webrtc
|