Add SVC support to the video codec tester

Bug: webrtc:14852
Change-Id: Iaa060fea396b8ec317d8f20d0c1bdad21bf739db
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/331502
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41440}
This commit is contained in:
Sergey Silkin 2023-12-22 09:40:56 +01:00 committed by WebRTC LUCI CQ
parent 9e5c979743
commit 0a01ffcd3f
2 changed files with 141 additions and 16 deletions

View File

@ -944,13 +944,54 @@ class Encoder : public EncodedImageCallback {
void Flush() { void Flush() {
task_queue_.PostTaskAndWait([this] { encoder_->Release(); }); task_queue_.PostTaskAndWait([this] { encoder_->Release(); });
if (last_superframe_) {
int num_spatial_layers =
ScalabilityModeToNumSpatialLayers(last_superframe_->scalability_mode);
for (int sidx = *last_superframe_->encoded_frame.SpatialIndex() + 1;
sidx < num_spatial_layers; ++sidx) {
last_superframe_->encoded_frame.SetSpatialIndex(sidx);
DeliverEncodedFrame(last_superframe_->encoded_frame);
}
last_superframe_.reset();
}
} }
private: private:
struct Superframe {
EncodedImage encoded_frame;
rtc::scoped_refptr<EncodedImageBuffer> encoded_data;
ScalabilityMode scalability_mode;
};
Result OnEncodedImage(const EncodedImage& encoded_frame, Result OnEncodedImage(const EncodedImage& encoded_frame,
const CodecSpecificInfo* codec_specific_info) override { const CodecSpecificInfo* codec_specific_info) override {
analyzer_->FinishEncode(encoded_frame); analyzer_->FinishEncode(encoded_frame);
if (last_superframe_ && last_superframe_->encoded_frame.RtpTimestamp() !=
encoded_frame.RtpTimestamp()) {
// New temporal unit. We have frame of previous temporal unit (TU) stored
// which means that the previous TU used spatial prediction. If encoder
// dropped a frame of layer X in the previous TU, mark the stored frame
// as a frame belonging to layer >X and deliver it such that decoders of
// layer >X receive encoded lower layers.
int num_spatial_layers =
ScalabilityModeToNumSpatialLayers(last_superframe_->scalability_mode);
for (int sidx = *last_superframe_->encoded_frame.SpatialIndex() + 1;
sidx < num_spatial_layers; ++sidx) {
last_superframe_->encoded_frame.SetSpatialIndex(sidx);
DeliverEncodedFrame(last_superframe_->encoded_frame);
}
last_superframe_.reset();
}
const EncodedImage& superframe =
MakeSuperFrame(encoded_frame, codec_specific_info);
DeliverEncodedFrame(superframe);
return Result(Result::Error::OK);
}
void DeliverEncodedFrame(const EncodedImage& encoded_frame) {
{ {
MutexLock lock(&mutex_); MutexLock lock(&mutex_);
auto it = callbacks_.find(encoded_frame.RtpTimestamp()); auto it = callbacks_.find(encoded_frame.RtpTimestamp());
@ -962,8 +1003,6 @@ class Encoder : public EncodedImageCallback {
if (ivf_writer_ != nullptr) { if (ivf_writer_ != nullptr) {
ivf_writer_->Write(encoded_frame); ivf_writer_->Write(encoded_frame);
} }
return Result(Result::Error::OK);
} }
void Configure(const EncodingSettings& es) { void Configure(const EncodingSettings& es) {
@ -1081,6 +1120,48 @@ class Encoder : public EncodedImageCallback {
return true; return true;
} }
static bool IsSvc(const EncodedImage& encoded_frame,
const CodecSpecificInfo* codec_specific_info) {
ScalabilityMode scalability_mode = *codec_specific_info->scalability_mode;
return (kFullSvcScalabilityModes.count(scalability_mode) ||
(kKeySvcScalabilityModes.count(scalability_mode) &&
encoded_frame.FrameType() == VideoFrameType::kVideoFrameKey));
}
const EncodedImage& MakeSuperFrame(
const EncodedImage& encoded_frame,
const CodecSpecificInfo* codec_specific_info) {
if (last_superframe_) {
// Append to base spatial layer frame(s).
RTC_CHECK_EQ(*encoded_frame.SpatialIndex(),
*last_superframe_->encoded_frame.SpatialIndex() + 1)
<< "Inter-layer frame drops are not supported.";
size_t current_size = last_superframe_->encoded_data->size();
last_superframe_->encoded_data->Realloc(current_size +
encoded_frame.size());
memcpy(last_superframe_->encoded_data->data() + current_size,
encoded_frame.data(), encoded_frame.size());
last_superframe_->encoded_frame.SetEncodedData(
last_superframe_->encoded_data);
last_superframe_->encoded_frame.SetSpatialIndex(
encoded_frame.SpatialIndex());
return last_superframe_->encoded_frame;
}
if (IsSvc(encoded_frame, codec_specific_info)) {
last_superframe_ = Superframe{
.encoded_frame = EncodedImage(encoded_frame),
.encoded_data = EncodedImageBuffer::Create(encoded_frame.data(),
encoded_frame.size()),
.scalability_mode = *codec_specific_info->scalability_mode};
last_superframe_->encoded_frame.SetEncodedData(
last_superframe_->encoded_data);
return last_superframe_->encoded_frame;
}
return encoded_frame;
}
VideoEncoderFactory* const encoder_factory_; VideoEncoderFactory* const encoder_factory_;
std::unique_ptr<VideoEncoder> encoder_; std::unique_ptr<VideoEncoder> encoder_;
VideoCodecAnalyzer* const analyzer_; VideoCodecAnalyzer* const analyzer_;
@ -1092,6 +1173,7 @@ class Encoder : public EncodedImageCallback {
std::unique_ptr<TesterIvfWriter> ivf_writer_; std::unique_ptr<TesterIvfWriter> ivf_writer_;
std::map<uint32_t, int> sidx_ RTC_GUARDED_BY(mutex_); std::map<uint32_t, int> sidx_ RTC_GUARDED_BY(mutex_);
std::map<uint32_t, EncodeCallback> callbacks_ RTC_GUARDED_BY(mutex_); std::map<uint32_t, EncodeCallback> callbacks_ RTC_GUARDED_BY(mutex_);
absl::optional<Superframe> last_superframe_;
Mutex mutex_; Mutex mutex_;
}; };

View File

@ -293,7 +293,7 @@ class MockCodedVideoSource : public CodedVideoSource {
TEST_F(VideoCodecTesterTest, Slice) { TEST_F(VideoCodecTesterTest, Slice) {
std::unique_ptr<VideoCodecStats> stats = std::unique_ptr<VideoCodecStats> stats =
RunEncodeDecodeTest("VP8", ScalabilityMode::kL2T2, RunEncodeDecodeTest("VP9", ScalabilityMode::kL2T2,
{{{.timestamp_rtp = 0, {{{.timestamp_rtp = 0,
.layer_id = {.spatial_idx = 0, .temporal_idx = 0}, .layer_id = {.spatial_idx = 0, .temporal_idx = 0},
.frame_size = DataSize::Bytes(1)}, .frame_size = DataSize::Bytes(1)},
@ -307,11 +307,13 @@ TEST_F(VideoCodecTesterTest, Slice) {
EXPECT_THAT(slice, EXPECT_THAT(slice,
ElementsAre(Field(&Frame::frame_size, DataSize::Bytes(1)), ElementsAre(Field(&Frame::frame_size, DataSize::Bytes(1)),
Field(&Frame::frame_size, DataSize::Bytes(2)), Field(&Frame::frame_size, DataSize::Bytes(2)),
Field(&Frame::frame_size, DataSize::Bytes(3)))); Field(&Frame::frame_size, DataSize::Bytes(3)),
Field(&Frame::frame_size, DataSize::Bytes(0))));
slice = stats->Slice({.min_timestamp_rtp = 1}, /*merge=*/false); slice = stats->Slice({.min_timestamp_rtp = 1}, /*merge=*/false);
EXPECT_THAT(slice, EXPECT_THAT(slice,
ElementsAre(Field(&Frame::frame_size, DataSize::Bytes(3)))); ElementsAre(Field(&Frame::frame_size, DataSize::Bytes(3)),
Field(&Frame::frame_size, DataSize::Bytes(0))));
slice = stats->Slice({.max_timestamp_rtp = 0}, /*merge=*/false); slice = stats->Slice({.max_timestamp_rtp = 0}, /*merge=*/false);
EXPECT_THAT(slice, EXPECT_THAT(slice,
@ -534,17 +536,58 @@ TEST_P(VideoCodecTesterTestScalability, EncodeDecode) {
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
All, All,
VideoCodecTesterTestScalability, VideoCodecTesterTestScalability,
Values(ScalabilityTestParameters{ Values(
.codec_type = "VP8", ScalabilityTestParameters{
.scalability_mode = ScalabilityMode::kS2T1, .codec_type = "VP8",
.encoded_frame_sizes = {{{0, DataSize::Bytes(1)}, .scalability_mode = ScalabilityMode::kS2T1,
{1, DataSize::Bytes(2)}}, .encoded_frame_sizes = {{{0, DataSize::Bytes(1)},
{{0, DataSize::Bytes(3)}, {1, DataSize::Bytes(2)}},
// Emulate frame drop. {{0, DataSize::Bytes(4)},
{1, DataSize::Bytes(0)}}}, // Emulate frame drop.
.expected_decode_frame_sizes = {DataSize::Bytes(1), DataSize::Bytes(2), {1, DataSize::Bytes(0)}}},
DataSize::Bytes(3)}, .expected_decode_frame_sizes = {DataSize::Bytes(1),
})); DataSize::Bytes(2),
DataSize::Bytes(4)},
},
ScalabilityTestParameters{
.codec_type = "VP9",
.scalability_mode = ScalabilityMode::kL2T1,
.encoded_frame_sizes =
{{{0, DataSize::Bytes(1)}, {1, DataSize::Bytes(2)}},
{{0, DataSize::Bytes(4)}, {1, DataSize::Bytes(8)}},
{{0, DataSize::Bytes(16)},
// Emulate frame drop.
{1, DataSize::Bytes(0)}}},
.expected_decode_frame_sizes =
{DataSize::Bytes(1), DataSize::Bytes(3), DataSize::Bytes(4),
DataSize::Bytes(12), DataSize::Bytes(16), DataSize::Bytes(16)},
},
ScalabilityTestParameters{
.codec_type = "VP9",
.scalability_mode = ScalabilityMode::kL2T1_KEY,
.encoded_frame_sizes =
{{{0, DataSize::Bytes(1)}, {1, DataSize::Bytes(2)}},
{{0, DataSize::Bytes(4)}, {1, DataSize::Bytes(8)}},
{{0, DataSize::Bytes(16)},
// Emulate frame drop.
{1, DataSize::Bytes(0)}}},
.expected_decode_frame_sizes =
{DataSize::Bytes(1), DataSize::Bytes(3), DataSize::Bytes(4),
DataSize::Bytes(8), DataSize::Bytes(16)},
},
ScalabilityTestParameters{
.codec_type = "VP9",
.scalability_mode = ScalabilityMode::kS2T1,
.encoded_frame_sizes =
{{{0, DataSize::Bytes(1)}, {1, DataSize::Bytes(2)}},
{{0, DataSize::Bytes(4)}, {1, DataSize::Bytes(8)}},
{{0, DataSize::Bytes(16)},
// Emulate frame drop.
{1, DataSize::Bytes(0)}}},
.expected_decode_frame_sizes =
{DataSize::Bytes(1), DataSize::Bytes(2), DataSize::Bytes(4),
DataSize::Bytes(8), DataSize::Bytes(16)},
}));
class VideoCodecTesterTestPacing class VideoCodecTesterTestPacing
: public ::testing::TestWithParam<std::tuple<PacingSettings, int>> { : public ::testing::TestWithParam<std::tuple<PacingSettings, int>> {