diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
commit | d8bbc7858622b6d9c278469aab701ca0b609cddf (patch) | |
tree | eff41dc61d9f714852212739e6b3738b82a2af87 /third_party/libwebrtc/test/video_codec_tester_unittest.cc | |
parent | Releasing progress-linux version 125.0.3-1~progress7.99u1. (diff) | |
download | firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | third_party/libwebrtc/test/video_codec_tester_unittest.cc | 513 |
1 files changed, 340 insertions, 173 deletions
diff --git a/third_party/libwebrtc/test/video_codec_tester_unittest.cc b/third_party/libwebrtc/test/video_codec_tester_unittest.cc index af31fe2c13..df5dca90a2 100644 --- a/third_party/libwebrtc/test/video_codec_tester_unittest.cc +++ b/third_party/libwebrtc/test/video_codec_tester_unittest.cc @@ -22,9 +22,14 @@ #include "api/test/mock_video_encoder.h" #include "api/test/mock_video_encoder_factory.h" #include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/frequency.h" #include "api/units/time_delta.h" #include "api/video/i420_buffer.h" #include "api/video/video_frame.h" +#include "api/video_codecs/scalability_mode.h" +#include "api/video_codecs/video_decoder.h" +#include "api/video_codecs/video_encoder.h" #include "modules/video_coding/include/video_codec_interface.h" #include "modules/video_coding/svc/scalability_mode_util.h" #include "test/gmock.h" @@ -44,6 +49,8 @@ using ::testing::InvokeWithoutArgs; using ::testing::NiceMock; using ::testing::Return; using ::testing::SizeIs; +using ::testing::UnorderedElementsAreArray; +using ::testing::Values; using VideoCodecStats = VideoCodecTester::VideoCodecStats; using VideoSourceSettings = VideoCodecTester::VideoSourceSettings; @@ -77,14 +84,19 @@ rtc::scoped_refptr<I420Buffer> CreateYuvBuffer(uint8_t y = 0, return buffer; } +// TODO(ssilkin): Wrap this into a class that removes file in dtor. std::string CreateYuvFile(int width, int height, int num_frames) { std::string path = webrtc::test::TempFilename(webrtc::test::OutputPath(), "video_codec_tester_unittest"); FILE* file = fopen(path.c_str(), "wb"); for (int frame_num = 0; frame_num < num_frames; ++frame_num) { - uint8_t y = (frame_num + 0) & 255; - uint8_t u = (frame_num + 1) & 255; - uint8_t v = (frame_num + 2) & 255; + // For purposes of testing quality estimation, we need Y, U, V values in + // source and decoded video to be unique and deterministic. In source video + // we make them functions of frame number. The test decoder makes them + // functions of encoded frame size in decoded video. + uint8_t y = (frame_num * 3 + 0) & 255; + uint8_t u = (frame_num * 3 + 1) & 255; + uint8_t v = (frame_num * 3 + 2) & 255; rtc::scoped_refptr<I420Buffer> buffer = CreateYuvBuffer(y, u, v); fwrite(buffer->DataY(), 1, width * height, file); int chroma_size_bytes = (width + 1) / 2 * (height + 1) / 2; @@ -95,115 +107,161 @@ std::string CreateYuvFile(int width, int height, int num_frames) { return path; } -std::unique_ptr<VideoCodecStats> RunTest(std::vector<std::vector<Frame>> frames, - ScalabilityMode scalability_mode) { - int num_frames = static_cast<int>(frames.size()); - std::string source_yuv_path = CreateYuvFile(kWidth, kHeight, num_frames); - VideoSourceSettings source_settings{ - .file_path = source_yuv_path, - .resolution = {.width = kWidth, .height = kHeight}, - .framerate = kTargetFramerate}; +class TestVideoEncoder : public MockVideoEncoder { + public: + TestVideoEncoder(ScalabilityMode scalability_mode, + std::vector<std::vector<Frame>> encoded_frames) + : scalability_mode_(scalability_mode), encoded_frames_(encoded_frames) {} + int32_t Encode(const VideoFrame& input_frame, + const std::vector<VideoFrameType>*) override { + for (const Frame& frame : encoded_frames_[num_encoded_frames_]) { + if (frame.frame_size.IsZero()) { + continue; // Frame drop. + } + EncodedImage encoded_frame; + encoded_frame._encodedWidth = frame.width; + encoded_frame._encodedHeight = frame.height; + encoded_frame.SetFrameType(frame.keyframe + ? VideoFrameType::kVideoFrameKey + : VideoFrameType::kVideoFrameDelta); + encoded_frame.SetRtpTimestamp(input_frame.timestamp()); + encoded_frame.SetSpatialIndex(frame.layer_id.spatial_idx); + encoded_frame.SetTemporalIndex(frame.layer_id.temporal_idx); + encoded_frame.SetEncodedData( + EncodedImageBuffer::Create(frame.frame_size.bytes())); + CodecSpecificInfo codec_specific_info; + codec_specific_info.scalability_mode = scalability_mode_; + callback_->OnEncodedImage(encoded_frame, &codec_specific_info); + } + ++num_encoded_frames_; + return WEBRTC_VIDEO_CODEC_OK; + } - int num_encoded_frames = 0; - EncodedImageCallback* encoded_frame_callback; - NiceMock<MockVideoEncoderFactory> encoder_factory; - ON_CALL(encoder_factory, CreateVideoEncoder) - .WillByDefault([&](const SdpVideoFormat&) { - auto encoder = std::make_unique<NiceMock<MockVideoEncoder>>(); - ON_CALL(*encoder, RegisterEncodeCompleteCallback) - .WillByDefault([&](EncodedImageCallback* callback) { - encoded_frame_callback = callback; - return WEBRTC_VIDEO_CODEC_OK; - }); - ON_CALL(*encoder, Encode) - .WillByDefault([&](const VideoFrame& input_frame, - const std::vector<VideoFrameType>*) { - for (const Frame& frame : frames[num_encoded_frames]) { - EncodedImage encoded_frame; - encoded_frame._encodedWidth = frame.width; - encoded_frame._encodedHeight = frame.height; - encoded_frame.SetFrameType( - frame.keyframe ? VideoFrameType::kVideoFrameKey - : VideoFrameType::kVideoFrameDelta); - encoded_frame.SetRtpTimestamp(input_frame.timestamp()); - encoded_frame.SetSpatialIndex(frame.layer_id.spatial_idx); - encoded_frame.SetTemporalIndex(frame.layer_id.temporal_idx); - encoded_frame.SetEncodedData( - EncodedImageBuffer::Create(frame.frame_size.bytes())); - encoded_frame_callback->OnEncodedImage( - encoded_frame, - /*codec_specific_info=*/nullptr); - } - ++num_encoded_frames; - return WEBRTC_VIDEO_CODEC_OK; - }); - return encoder; - }); + int32_t RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) override { + callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; + } - int num_decoded_frames = 0; - DecodedImageCallback* decode_callback; - NiceMock<MockVideoDecoderFactory> decoder_factory; - ON_CALL(decoder_factory, CreateVideoDecoder) - .WillByDefault([&](const SdpVideoFormat&) { - auto decoder = std::make_unique<NiceMock<MockVideoDecoder>>(); - ON_CALL(*decoder, RegisterDecodeCompleteCallback) - .WillByDefault([&](DecodedImageCallback* callback) { - decode_callback = callback; - return WEBRTC_VIDEO_CODEC_OK; - }); - ON_CALL(*decoder, Decode(_, _)) - .WillByDefault([&](const EncodedImage& encoded_frame, int64_t) { - // Make values to be different from source YUV generated in - // `CreateYuvFile`. - uint8_t y = ((num_decoded_frames + 1) * 2) & 255; - uint8_t u = ((num_decoded_frames + 2) * 2) & 255; - uint8_t v = ((num_decoded_frames + 3) * 2) & 255; - rtc::scoped_refptr<I420Buffer> frame_buffer = - CreateYuvBuffer(y, u, v); - VideoFrame decoded_frame = - VideoFrame::Builder() - .set_video_frame_buffer(frame_buffer) - .set_timestamp_rtp(encoded_frame.RtpTimestamp()) - .build(); - decode_callback->Decoded(decoded_frame); - ++num_decoded_frames; - return WEBRTC_VIDEO_CODEC_OK; - }); - return decoder; - }); + private: + ScalabilityMode scalability_mode_; + std::vector<std::vector<Frame>> encoded_frames_; + int num_encoded_frames_ = 0; + EncodedImageCallback* callback_; +}; + +class TestVideoDecoder : public MockVideoDecoder { + public: + int32_t Decode(const EncodedImage& encoded_frame, int64_t) { + uint8_t y = (encoded_frame.size() + 0) & 255; + uint8_t u = (encoded_frame.size() + 2) & 255; + uint8_t v = (encoded_frame.size() + 4) & 255; + rtc::scoped_refptr<I420Buffer> frame_buffer = CreateYuvBuffer(y, u, v); + VideoFrame decoded_frame = + VideoFrame::Builder() + .set_video_frame_buffer(frame_buffer) + .set_timestamp_rtp(encoded_frame.RtpTimestamp()) + .build(); + callback_->Decoded(decoded_frame); + frame_sizes_.push_back(DataSize::Bytes(encoded_frame.size())); + return WEBRTC_VIDEO_CODEC_OK; + } - int num_spatial_layers = ScalabilityModeToNumSpatialLayers(scalability_mode); - int num_temporal_layers = - ScalabilityModeToNumTemporalLayers(scalability_mode); + int32_t RegisterDecodeCompleteCallback(DecodedImageCallback* callback) { + callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; + } - std::map<uint32_t, EncodingSettings> encoding_settings; - for (int frame_num = 0; frame_num < num_frames; ++frame_num) { - std::map<LayerId, LayerSettings> layers_settings; - for (int sidx = 0; sidx < num_spatial_layers; ++sidx) { - for (int tidx = 0; tidx < num_temporal_layers; ++tidx) { - layers_settings.emplace( - LayerId{.spatial_idx = sidx, .temporal_idx = tidx}, - LayerSettings{.resolution = {.width = kWidth, .height = kHeight}, - .framerate = kTargetFramerate / - (1 << (num_temporal_layers - 1 - tidx)), - .bitrate = kTargetLayerBitrate}); + const std::vector<DataSize>& frame_sizes() const { return frame_sizes_; } + + private: + DecodedImageCallback* callback_; + std::vector<DataSize> frame_sizes_; +}; + +class VideoCodecTesterTest : public ::testing::Test { + public: + std::unique_ptr<VideoCodecStats> RunEncodeDecodeTest( + std::string codec_type, + ScalabilityMode scalability_mode, + std::vector<std::vector<Frame>> encoded_frames) { + int num_frames = encoded_frames.size(); + std::string yuv_path = CreateYuvFile(kWidth, kHeight, num_frames); + VideoSourceSettings video_source_settings{ + .file_path = yuv_path, + .resolution = {.width = kWidth, .height = kHeight}, + .framerate = kTargetFramerate}; + + NiceMock<MockVideoEncoderFactory> encoder_factory; + ON_CALL(encoder_factory, CreateVideoEncoder) + .WillByDefault([&](const SdpVideoFormat&) { + return std::make_unique<NiceMock<TestVideoEncoder>>(scalability_mode, + encoded_frames); + }); + + NiceMock<MockVideoDecoderFactory> decoder_factory; + ON_CALL(decoder_factory, CreateVideoDecoder) + .WillByDefault([&](const SdpVideoFormat&) { + // Video codec tester destroyes decoder at the end of test. Test + // decoder collects stats which we need to access after test. To keep + // the decode alive we wrap it into a wrapper and pass the wrapper to + // the tester. + class DecoderWrapper : public TestVideoDecoder { + public: + explicit DecoderWrapper(TestVideoDecoder* decoder) + : decoder_(decoder) {} + int32_t Decode(const EncodedImage& encoded_frame, + int64_t render_time_ms) { + return decoder_->Decode(encoded_frame, render_time_ms); + } + int32_t RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) { + return decoder_->RegisterDecodeCompleteCallback(callback); + } + TestVideoDecoder* decoder_; + }; + decoders_.push_back(std::make_unique<NiceMock<TestVideoDecoder>>()); + return std::make_unique<NiceMock<DecoderWrapper>>( + decoders_.back().get()); + }); + + int num_spatial_layers = + ScalabilityModeToNumSpatialLayers(scalability_mode); + int num_temporal_layers = + ScalabilityModeToNumTemporalLayers(scalability_mode); + std::map<uint32_t, EncodingSettings> encoding_settings; + for (int frame_num = 0; frame_num < num_frames; ++frame_num) { + std::map<LayerId, LayerSettings> layers_settings; + for (int sidx = 0; sidx < num_spatial_layers; ++sidx) { + for (int tidx = 0; tidx < num_temporal_layers; ++tidx) { + layers_settings.emplace( + LayerId{.spatial_idx = sidx, .temporal_idx = tidx}, + LayerSettings{ + .resolution = {.width = kWidth, .height = kHeight}, + .framerate = kTargetFramerate / + (1 << (num_temporal_layers - 1 - tidx)), + .bitrate = kTargetLayerBitrate}); + } } + encoding_settings.emplace( + encoded_frames[frame_num].front().timestamp_rtp, + EncodingSettings{.sdp_video_format = SdpVideoFormat(codec_type), + .scalability_mode = scalability_mode, + .layers_settings = layers_settings}); } - encoding_settings.emplace( - frames[frame_num][0].timestamp_rtp, - EncodingSettings{.scalability_mode = scalability_mode, - .layers_settings = layers_settings}); + + std::unique_ptr<VideoCodecStats> stats = + VideoCodecTester::RunEncodeDecodeTest( + video_source_settings, &encoder_factory, &decoder_factory, + EncoderSettings{}, DecoderSettings{}, encoding_settings); + + remove(yuv_path.c_str()); + return stats; } - EncoderSettings encoder_settings; - DecoderSettings decoder_settings; - std::unique_ptr<VideoCodecStats> stats = - VideoCodecTester::RunEncodeDecodeTest( - source_settings, &encoder_factory, &decoder_factory, encoder_settings, - decoder_settings, encoding_settings); - remove(source_yuv_path.c_str()); - return stats; -} + protected: + std::vector<std::unique_ptr<TestVideoDecoder>> decoders_; +}; EncodedImage CreateEncodedImage(uint32_t timestamp_rtp) { EncodedImage encoded_image; @@ -233,52 +291,63 @@ class MockCodedVideoSource : public CodedVideoSource { } // namespace -TEST(VideoCodecTester, Slice) { - std::unique_ptr<VideoCodecStats> stats = RunTest( - {{{.timestamp_rtp = 0, .layer_id = {.spatial_idx = 0, .temporal_idx = 0}}, - {.timestamp_rtp = 0, - .layer_id = {.spatial_idx = 1, .temporal_idx = 0}}}, - {{.timestamp_rtp = 1, - .layer_id = {.spatial_idx = 0, .temporal_idx = 1}}}}, - ScalabilityMode::kL2T2); +TEST_F(VideoCodecTesterTest, Slice) { + std::unique_ptr<VideoCodecStats> stats = + RunEncodeDecodeTest("VP9", ScalabilityMode::kL2T2, + {{{.timestamp_rtp = 0, + .layer_id = {.spatial_idx = 0, .temporal_idx = 0}, + .frame_size = DataSize::Bytes(1)}, + {.timestamp_rtp = 0, + .layer_id = {.spatial_idx = 1, .temporal_idx = 0}, + .frame_size = DataSize::Bytes(2)}}, + {{.timestamp_rtp = 1, + .layer_id = {.spatial_idx = 0, .temporal_idx = 1}, + .frame_size = DataSize::Bytes(3)}}}); std::vector<Frame> slice = stats->Slice(Filter{}, /*merge=*/false); - EXPECT_THAT(slice, ElementsAre(Field(&Frame::timestamp_rtp, 0), - Field(&Frame::timestamp_rtp, 0), - Field(&Frame::timestamp_rtp, 1))); + EXPECT_THAT(slice, + ElementsAre(Field(&Frame::frame_size, DataSize::Bytes(1)), + Field(&Frame::frame_size, DataSize::Bytes(2)), + Field(&Frame::frame_size, DataSize::Bytes(3)), + Field(&Frame::frame_size, DataSize::Bytes(0)))); slice = stats->Slice({.min_timestamp_rtp = 1}, /*merge=*/false); - EXPECT_THAT(slice, ElementsAre(Field(&Frame::timestamp_rtp, 1))); + EXPECT_THAT(slice, + ElementsAre(Field(&Frame::frame_size, DataSize::Bytes(3)), + Field(&Frame::frame_size, DataSize::Bytes(0)))); slice = stats->Slice({.max_timestamp_rtp = 0}, /*merge=*/false); - EXPECT_THAT(slice, ElementsAre(Field(&Frame::timestamp_rtp, 0), - Field(&Frame::timestamp_rtp, 0))); + EXPECT_THAT(slice, + ElementsAre(Field(&Frame::frame_size, DataSize::Bytes(1)), + Field(&Frame::frame_size, DataSize::Bytes(2)))); slice = stats->Slice({.layer_id = {{.spatial_idx = 0, .temporal_idx = 0}}}, /*merge=*/false); - EXPECT_THAT(slice, ElementsAre(Field(&Frame::timestamp_rtp, 0))); + EXPECT_THAT(slice, + ElementsAre(Field(&Frame::frame_size, DataSize::Bytes(1)))); slice = stats->Slice({.layer_id = {{.spatial_idx = 0, .temporal_idx = 1}}}, /*merge=*/false); - EXPECT_THAT(slice, ElementsAre(Field(&Frame::timestamp_rtp, 0), - Field(&Frame::timestamp_rtp, 1))); + EXPECT_THAT(slice, + ElementsAre(Field(&Frame::frame_size, DataSize::Bytes(1)), + Field(&Frame::frame_size, DataSize::Bytes(3)))); } -TEST(VideoCodecTester, Merge) { +TEST_F(VideoCodecTesterTest, Merge) { std::unique_ptr<VideoCodecStats> stats = - RunTest({{{.timestamp_rtp = 0, - .layer_id = {.spatial_idx = 0, .temporal_idx = 0}, - .frame_size = DataSize::Bytes(1), - .keyframe = true}, - {.timestamp_rtp = 0, - .layer_id = {.spatial_idx = 1, .temporal_idx = 0}, - .frame_size = DataSize::Bytes(2)}}, - {{.timestamp_rtp = 1, - .layer_id = {.spatial_idx = 0, .temporal_idx = 1}, - .frame_size = DataSize::Bytes(4)}, - {.timestamp_rtp = 1, - .layer_id = {.spatial_idx = 1, .temporal_idx = 1}, - .frame_size = DataSize::Bytes(8)}}}, - ScalabilityMode::kL2T2_KEY); + RunEncodeDecodeTest("VP8", ScalabilityMode::kL2T2_KEY, + {{{.timestamp_rtp = 0, + .layer_id = {.spatial_idx = 0, .temporal_idx = 0}, + .frame_size = DataSize::Bytes(1), + .keyframe = true}, + {.timestamp_rtp = 0, + .layer_id = {.spatial_idx = 1, .temporal_idx = 0}, + .frame_size = DataSize::Bytes(2)}}, + {{.timestamp_rtp = 1, + .layer_id = {.spatial_idx = 0, .temporal_idx = 1}, + .frame_size = DataSize::Bytes(4)}, + {.timestamp_rtp = 1, + .layer_id = {.spatial_idx = 1, .temporal_idx = 1}, + .frame_size = DataSize::Bytes(8)}}}); std::vector<Frame> slice = stats->Slice(Filter{}, /*merge=*/true); EXPECT_THAT( @@ -300,33 +369,34 @@ struct AggregationTestParameters { }; class VideoCodecTesterTestAggregation - : public ::testing::TestWithParam<AggregationTestParameters> {}; + : public VideoCodecTesterTest, + public ::testing::WithParamInterface<AggregationTestParameters> {}; TEST_P(VideoCodecTesterTestAggregation, Aggregate) { AggregationTestParameters test_params = GetParam(); std::unique_ptr<VideoCodecStats> stats = - RunTest({{// L0T0 - {.timestamp_rtp = 0, - .layer_id = {.spatial_idx = 0, .temporal_idx = 0}, - .frame_size = DataSize::Bytes(1), - .keyframe = true}, - // L1T0 - {.timestamp_rtp = 0, - .layer_id = {.spatial_idx = 1, .temporal_idx = 0}, - .frame_size = DataSize::Bytes(2)}}, - // Emulate frame drop (frame_size = 0). - {{.timestamp_rtp = 3000, - .layer_id = {.spatial_idx = 0, .temporal_idx = 0}, - .frame_size = DataSize::Zero()}}, - {// L0T1 - {.timestamp_rtp = 87000, - .layer_id = {.spatial_idx = 0, .temporal_idx = 1}, - .frame_size = DataSize::Bytes(4)}, - // L1T1 - {.timestamp_rtp = 87000, - .layer_id = {.spatial_idx = 1, .temporal_idx = 1}, - .frame_size = DataSize::Bytes(8)}}}, - ScalabilityMode::kL2T2_KEY); + RunEncodeDecodeTest("VP8", ScalabilityMode::kL2T2_KEY, + {{// L0T0 + {.timestamp_rtp = 0, + .layer_id = {.spatial_idx = 0, .temporal_idx = 0}, + .frame_size = DataSize::Bytes(1), + .keyframe = true}, + // L1T0 + {.timestamp_rtp = 0, + .layer_id = {.spatial_idx = 1, .temporal_idx = 0}, + .frame_size = DataSize::Bytes(2)}}, + // Emulate frame drop (frame_size = 0). + {{.timestamp_rtp = 3000, + .layer_id = {.spatial_idx = 0, .temporal_idx = 0}, + .frame_size = DataSize::Zero()}}, + {// L0T1 + {.timestamp_rtp = 87000, + .layer_id = {.spatial_idx = 0, .temporal_idx = 1}, + .frame_size = DataSize::Bytes(4)}, + // L1T1 + {.timestamp_rtp = 87000, + .layer_id = {.spatial_idx = 1, .temporal_idx = 1}, + .frame_size = DataSize::Bytes(8)}}}); Stream stream = stats->Aggregate(test_params.filter); EXPECT_EQ(stream.keyframe.GetSum(), test_params.expected_keyframe_sum); @@ -343,7 +413,7 @@ TEST_P(VideoCodecTesterTestAggregation, Aggregate) { INSTANTIATE_TEST_SUITE_P( All, VideoCodecTesterTestAggregation, - ::testing::Values( + Values( // No filtering. AggregationTestParameters{ .filter = {}, @@ -400,11 +470,11 @@ INSTANTIATE_TEST_SUITE_P( .expected_framerate_mismatch_pct = 100 * (2.0 / kTargetFramerate.hertz() - 1)})); -TEST(VideoCodecTester, Psnr) { - std::unique_ptr<VideoCodecStats> stats = - RunTest({{{.timestamp_rtp = 0, .frame_size = DataSize::Bytes(1)}}, - {{.timestamp_rtp = 3000, .frame_size = DataSize::Bytes(1)}}}, - ScalabilityMode::kL1T1); +TEST_F(VideoCodecTesterTest, Psnr) { + std::unique_ptr<VideoCodecStats> stats = RunEncodeDecodeTest( + "VP8", ScalabilityMode::kL1T1, + {{{.timestamp_rtp = 0, .frame_size = DataSize::Bytes(2)}}, + {{.timestamp_rtp = 3000, .frame_size = DataSize::Bytes(6)}}}); std::vector<Frame> slice = stats->Slice(Filter{}, /*merge=*/false); ASSERT_THAT(slice, SizeIs(2)); @@ -418,6 +488,107 @@ TEST(VideoCodecTester, Psnr) { EXPECT_NEAR(slice[1].psnr->v, 34, 1); } +struct ScalabilityTestParameters { + std::string codec_type; + ScalabilityMode scalability_mode; + // Temporal unit -> spatial layer -> frame size. + std::vector<std::map<int, DataSize>> encoded_frame_sizes; + std::vector<DataSize> expected_decode_frame_sizes; +}; + +class VideoCodecTesterTestScalability + : public VideoCodecTesterTest, + public ::testing::WithParamInterface<ScalabilityTestParameters> {}; + +TEST_P(VideoCodecTesterTestScalability, EncodeDecode) { + ScalabilityTestParameters test_params = GetParam(); + std::vector<std::vector<Frame>> frames; + for (size_t frame_num = 0; frame_num < test_params.encoded_frame_sizes.size(); + ++frame_num) { + std::vector<Frame> temporal_unit; + for (auto [sidx, frame_size] : test_params.encoded_frame_sizes[frame_num]) { + temporal_unit.push_back( + Frame{.timestamp_rtp = static_cast<uint32_t>(3000 * frame_num), + .layer_id = {.spatial_idx = sidx, .temporal_idx = 0}, + .frame_size = frame_size, + .keyframe = (frame_num == 0 && sidx == 0)}); + } + frames.push_back(temporal_unit); + } + RunEncodeDecodeTest(test_params.codec_type, test_params.scalability_mode, + frames); + + size_t num_spatial_layers = + ScalabilityModeToNumSpatialLayers(test_params.scalability_mode); + EXPECT_EQ(num_spatial_layers, decoders_.size()); + + // Collect input frame sizes from all decoders. + std::vector<DataSize> decode_frame_sizes; + for (const auto& decoder : decoders_) { + const auto& frame_sizes = decoder->frame_sizes(); + decode_frame_sizes.insert(decode_frame_sizes.end(), frame_sizes.begin(), + frame_sizes.end()); + } + EXPECT_THAT(decode_frame_sizes, UnorderedElementsAreArray( + test_params.expected_decode_frame_sizes)); +} + +INSTANTIATE_TEST_SUITE_P( + All, + VideoCodecTesterTestScalability, + Values( + ScalabilityTestParameters{ + .codec_type = "VP8", + .scalability_mode = ScalabilityMode::kS2T1, + .encoded_frame_sizes = {{{0, DataSize::Bytes(1)}, + {1, DataSize::Bytes(2)}}, + {{0, DataSize::Bytes(4)}, + // Emulate frame drop. + {1, DataSize::Bytes(0)}}}, + .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 : public ::testing::TestWithParam<std::tuple<PacingSettings, int>> { public: @@ -428,15 +599,11 @@ class VideoCodecTesterTestPacing const Frequency kTargetFramerate = Frequency::Hertz(10); void SetUp() override { - source_yuv_file_path_ = webrtc::test::TempFilename( - webrtc::test::OutputPath(), "video_codec_tester_impl_unittest"); - FILE* file = fopen(source_yuv_file_path_.c_str(), "wb"); - for (int i = 0; i < 3 * kSourceWidth * kSourceHeight / 2; ++i) { - fwrite("x", 1, 1, file); - } - fclose(file); + source_yuv_file_path_ = CreateYuvFile(kSourceWidth, kSourceHeight, 1); } + void TearDown() override { remove(source_yuv_file_path_.c_str()); } + protected: std::string source_yuv_file_path_; }; @@ -498,7 +665,7 @@ TEST_P(VideoCodecTesterTestPacing, PaceDecode) { INSTANTIATE_TEST_SUITE_P( DISABLED_All, VideoCodecTesterTestPacing, - ::testing::Values( + Values( // No pacing. std::make_tuple(PacingSettings{.mode = PacingMode::kNoPacing}, /*expected_delta_ms=*/0), |