tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 969a53ef2106b2a263385751000866802bd74e72
parent 43fc2cd9eaaa347e082ea1be4c112a1288800a21
Author: Dan Baker <dbaker@mozilla.com>
Date:   Mon,  1 Dec 2025 23:28:40 -0700

Bug 2000941 - Vendor libwebrtc from 6db6f177d3

Upstream commit: https://webrtc.googlesource.com/src/+/6db6f177d3be614eaf0c300ef7b9803483e6542d
    [AV1] Drop repeat frames on enhancement layers.

    This CL introduces a field trial which allows the libaom AV1 encoder
    wrapper to drop frames if the input frame is repat frame and the
    scalabilit controller assigns it a non-base layer.

    Bug: webrtc:445115234
    Change-Id: I56692808c07251c01136b2246b5ee3bf7c7bfeda
    Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/409564
    Commit-Queue: Erik Språng <sprang@webrtc.org>
    Reviewed-by: Markus Handell <handellm@webrtc.org>
    Cr-Commit-Position: refs/heads/main@{#45712}

Diffstat:
Mthird_party/libwebrtc/README.mozilla.last-vendor | 4++--
Mthird_party/libwebrtc/experiments/field_trials.py | 3+++
Mthird_party/libwebrtc/modules/video_coding/codecs/av1/BUILD.gn | 1+
Mthird_party/libwebrtc/modules/video_coding/codecs/av1/libaom_av1_encoder.cc | 45+++++++++++++++++++++++++++++++++++++++++----
Mthird_party/libwebrtc/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
5 files changed, 252 insertions(+), 43 deletions(-)

diff --git a/third_party/libwebrtc/README.mozilla.last-vendor b/third_party/libwebrtc/README.mozilla.last-vendor @@ -1,4 +1,4 @@ # ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc -libwebrtc updated from /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-12-02T06:25:17.904692+00:00. +libwebrtc updated from /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-12-02T06:28:26.322059+00:00. # base of lastest vendoring -ea14c99d67 +6db6f177d3 diff --git a/third_party/libwebrtc/experiments/field_trials.py b/third_party/libwebrtc/experiments/field_trials.py @@ -110,6 +110,9 @@ ACTIVE_FIELD_TRIALS: FrozenSet[FieldTrial] = frozenset([ FieldTrial('WebRTC-JitterEstimatorConfig', 42224404, date(2024, 4, 1)), + FieldTrial('WebRTC-LibaomAv1Encoder-DropRepeatFramesOnEnhancementLayers', + 445115234, + date(2026, 3, 19)), FieldTrial('WebRTC-LibaomAv1Encoder-PostEncodeFrameDrop', 351644568, date(2026, 1, 30)), diff --git a/third_party/libwebrtc/modules/video_coding/codecs/av1/BUILD.gn b/third_party/libwebrtc/modules/video_coding/codecs/av1/BUILD.gn @@ -138,6 +138,7 @@ if (rtc_include_tests) { "../../../../api/units:time_delta", "../../../../modules/rtp_rtcp:rtp_rtcp_format", "../../../../test:fileutils", + "../../../../test:test_support", "../../../../test:video_test_support", "../../svc:scalability_mode_util", "../../svc:scalability_structures", diff --git a/third_party/libwebrtc/modules/video_coding/codecs/av1/libaom_av1_encoder.cc b/third_party/libwebrtc/modules/video_coding/codecs/av1/libaom_av1_encoder.cc @@ -11,6 +11,7 @@ #include <cstddef> #include <cstdint> +#include <map> #include <memory> #include <numeric> #include <optional> @@ -182,6 +183,8 @@ class LibaomAv1Encoder final : public VideoEncoder { FrameSampler psnr_frame_sampler_; // TODO(webrtc:388070060): Remove after rollout. const bool calculate_psnr_; + const bool drop_repeat_frames_on_enhancement_layers_; + std::map<int, uint32_t> last_encoded_timestamp_by_sid_; }; int32_t VerifyCodecSettings(const VideoCodec& codec_settings) { @@ -225,7 +228,9 @@ LibaomAv1Encoder::LibaomAv1Encoder(const Environment& env, post_encode_frame_drop_(!env.field_trials().IsDisabled( "WebRTC-LibaomAv1Encoder-PostEncodeFrameDrop")), calculate_psnr_( - env.field_trials().IsEnabled("WebRTC-Video-CalculatePsnr")) {} + env.field_trials().IsEnabled("WebRTC-Video-CalculatePsnr")), + drop_repeat_frames_on_enhancement_layers_(env.field_trials().IsEnabled( + "WebRTC-LibaomAv1Encoder-DropRepeatFramesOnEnhancementLayers")) {} LibaomAv1Encoder::~LibaomAv1Encoder() { Release(); @@ -665,6 +670,35 @@ int32_t LibaomAv1Encoder::Encode( return WEBRTC_VIDEO_CODEC_ERROR; } + if (drop_repeat_frames_on_enhancement_layers_ && frame.is_repeat_frame()) { + bool all_layers_droppable = !layer_frames.empty(); + for (const auto& layer_frame : layer_frames) { + if (layer_frame.TemporalId() == 0) { + all_layers_droppable = false; + break; + } + if (auto it = + last_encoded_timestamp_by_sid_.find(layer_frame.SpatialId()); + it != last_encoded_timestamp_by_sid_.end()) { + // Get the time since the last encoded frame for this spatial layer. + // Don't drop enhancement layer repeat frame if last encode was more + // than one second ago. + if ((frame.rtp_timestamp() - it->second) > kVideoPayloadTypeFrequency) { + all_layers_droppable = false; + break; + } + } + } + + if (all_layers_droppable) { + RTC_LOG(LS_VERBOSE) << "Dropping repeat frame on enhancement layers."; + for (const auto& layer_frame : layer_frames) { + svc_controller_->OnEncodeDone(layer_frame); + } + return WEBRTC_VIDEO_CODEC_OK; // Frame dropped + } + } + scoped_refptr<VideoFrameBuffer> buffer = frame.video_frame_buffer(); absl::InlinedVector<VideoFrameBuffer::Type, kMaxPreferredPixelFormats> supported_formats = {VideoFrameBuffer::Type::kI420, @@ -820,9 +854,12 @@ int32_t LibaomAv1Encoder::Encode( if (!encoded_images.empty()) { encoded_images.back().second.end_of_picture = true; } - for (auto& [encoded_image, codec_specific_info] : encoded_images) { - encoded_image_callback_->OnEncodedImage(encoded_image, - &codec_specific_info); + for (auto& [encoded_image, codec_specifics] : encoded_images) { + encoded_image_callback_->OnEncodedImage(encoded_image, &codec_specifics); + if (encoded_image.SpatialIndex().has_value()) { + last_encoded_timestamp_by_sid_[*encoded_image.SpatialIndex()] = + frame.rtp_timestamp(); + } } return WEBRTC_VIDEO_CODEC_OK; diff --git a/third_party/libwebrtc/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc b/third_party/libwebrtc/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc @@ -25,6 +25,7 @@ #include "api/field_trials.h" #include "api/test/create_frame_generator.h" #include "api/test/frame_generator_interface.h" +#include "api/test/mock_video_encoder.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -51,10 +52,13 @@ namespace webrtc { namespace { +using ::testing::_; using ::testing::ElementsAre; using ::testing::Eq; using ::testing::Field; using ::testing::IsEmpty; +using ::testing::Property; +using ::testing::Return; using ::testing::SizeIs; using ::testing::Values; @@ -87,15 +91,38 @@ VideoEncoder::Settings DefaultEncoderSettings() { /*number_of_cores=*/1, /*max_payload_size=*/1200); } -TEST(LibaomAv1EncoderTest, CanCreate) { +class LibaomAv1EncoderTest : public ::testing::TestWithParam<bool> { + public: + LibaomAv1EncoderTest() + : drop_repeat_frames_on_enhancement_layers_(GetParam()) {} + + protected: + Environment CreateTestEnvironment() { + auto field_trials = std::make_unique<FieldTrials>(CreateTestFieldTrials( + drop_repeat_frames_on_enhancement_layers_ + ? "WebRTC-LibaomAv1Encoder-DropRepeatFramesOnEnhancementLayers/" + "Enabled/" + : "WebRTC-LibaomAv1Encoder-DropRepeatFramesOnEnhancementLayers/" + "Disabled/")); + return CreateEnvironment(std::move(field_trials)); + } + + const bool drop_repeat_frames_on_enhancement_layers_; +}; + +INSTANTIATE_TEST_SUITE_P(DropRepeatFrames, + LibaomAv1EncoderTest, + testing::Bool()); + +TEST_P(LibaomAv1EncoderTest, CanCreate) { std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); EXPECT_TRUE(encoder); } -TEST(LibaomAv1EncoderTest, InitAndRelease) { +TEST_P(LibaomAv1EncoderTest, InitAndRelease) { std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); ASSERT_TRUE(encoder); VideoCodec codec_settings = DefaultCodecSettings(); EXPECT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), @@ -103,10 +130,11 @@ TEST(LibaomAv1EncoderTest, InitAndRelease) { EXPECT_EQ(encoder->Release(), WEBRTC_VIDEO_CODEC_OK); } -TEST(LibaomAv1EncoderTest, NoBitrateOnTopLayerRefecltedInActiveDecodeTargets) { +TEST_P(LibaomAv1EncoderTest, + NoBitrateOnTopLayerRefecltedInActiveDecodeTargets) { // Configure encoder with 2 temporal layers. std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.SetScalabilityMode(ScalabilityMode::kL1T2); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), @@ -130,10 +158,10 @@ TEST(LibaomAv1EncoderTest, NoBitrateOnTopLayerRefecltedInActiveDecodeTargets) { 0b01); } -TEST(LibaomAv1EncoderTest, - SpatialScalabilityInTemporalUnitReportedAsDeltaFrame) { +TEST_P(LibaomAv1EncoderTest, + SpatialScalabilityInTemporalUnitReportedAsDeltaFrame) { std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.SetScalabilityMode(ScalabilityMode::kL2T1); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), @@ -154,9 +182,9 @@ TEST(LibaomAv1EncoderTest, Eq(VideoFrameType::kVideoFrameDelta)); } -TEST(LibaomAv1EncoderTest, NoBitrateOnTopSpatialLayerProduceDeltaFrames) { +TEST_P(LibaomAv1EncoderTest, NoBitrateOnTopSpatialLayerProduceDeltaFrames) { std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.SetScalabilityMode(ScalabilityMode::kL2T1); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), @@ -177,14 +205,14 @@ TEST(LibaomAv1EncoderTest, NoBitrateOnTopSpatialLayerProduceDeltaFrames) { Eq(VideoFrameType::kVideoFrameDelta)); } -TEST(LibaomAv1EncoderTest, SetsEndOfPictureForLastFrameInTemporalUnit) { +TEST_P(LibaomAv1EncoderTest, SetsEndOfPictureForLastFrameInTemporalUnit) { VideoBitrateAllocation allocation; allocation.SetBitrate(0, 0, 30000); allocation.SetBitrate(1, 0, 40000); allocation.SetBitrate(2, 0, 30000); std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = DefaultCodecSettings(); // Configure encoder with 3 spatial layers. codec_settings.SetScalabilityMode(ScalabilityMode::kL3T1); @@ -206,8 +234,8 @@ TEST(LibaomAv1EncoderTest, SetsEndOfPictureForLastFrameInTemporalUnit) { EXPECT_TRUE(encoded_frames[5].codec_specific_info.end_of_picture); } -TEST(LibaomAv1EncoderTest, - SetsEndOfPictureForLastFrameInTemporalUnitWhenLayerDrop) { +TEST_P(LibaomAv1EncoderTest, + SetsEndOfPictureForLastFrameInTemporalUnitWhenLayerDrop) { VideoBitrateAllocation allocation; allocation.SetBitrate(0, 0, 30000); allocation.SetBitrate(1, 0, 40000); @@ -215,7 +243,7 @@ TEST(LibaomAv1EncoderTest, allocation.SetBitrate(2, 0, 500); std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = DefaultCodecSettings(); // Configure encoder with 3 spatial layers. codec_settings.SetScalabilityMode(ScalabilityMode::kL3T1); @@ -235,13 +263,13 @@ TEST(LibaomAv1EncoderTest, EXPECT_TRUE(encoded_frames[3].codec_specific_info.end_of_picture); } -TEST(LibaomAv1EncoderTest, CheckOddDimensionsWithSpatialLayers) { +TEST_P(LibaomAv1EncoderTest, CheckOddDimensionsWithSpatialLayers) { VideoBitrateAllocation allocation; allocation.SetBitrate(0, 0, 30000); allocation.SetBitrate(1, 0, 40000); allocation.SetBitrate(2, 0, 30000); std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = DefaultCodecSettings(); // Configure encoder with 3 spatial layers. codec_settings.SetScalabilityMode(ScalabilityMode::kL3T1); @@ -308,13 +336,13 @@ INSTANTIATE_TEST_SUITE_P(LibaomAv1EncoderMaxConsecDropTests, LibaomAv1EncoderMaxConsecDropTest, Values(1, 2, 5, 15, 30, 60)); -TEST(LibaomAv1EncoderTest, EncoderInfoWithoutResolutionBitrateLimits) { +TEST_P(LibaomAv1EncoderTest, EncoderInfoWithoutResolutionBitrateLimits) { std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); EXPECT_TRUE(encoder->GetEncoderInfo().resolution_bitrate_limits.empty()); } -TEST(LibaomAv1EncoderTest, EncoderInfoWithBitrateLimitsFromFieldTrial) { +TEST_P(LibaomAv1EncoderTest, EncoderInfoWithBitrateLimitsFromFieldTrial) { auto field_trials = std::make_unique<FieldTrials>( CreateTestFieldTrials("WebRTC-Av1-GetEncoderInfoOverride/" "frame_size_pixels:123|456|789," @@ -332,9 +360,9 @@ TEST(LibaomAv1EncoderTest, EncoderInfoWithBitrateLimitsFromFieldTrial) { VideoEncoder::ResolutionBitrateLimits{789, 33000, 66000, 99000})); } -TEST(LibaomAv1EncoderTest, EncoderInfoProvidesFpsAllocation) { +TEST_P(LibaomAv1EncoderTest, EncoderInfoProvidesFpsAllocation) { std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.SetScalabilityMode(ScalabilityMode::kL3T3); codec_settings.maxFramerate = 60; @@ -351,13 +379,13 @@ TEST(LibaomAv1EncoderTest, EncoderInfoProvidesFpsAllocation) { EXPECT_THAT(encoder_info.fps_allocation[3], IsEmpty()); } -TEST(LibaomAv1EncoderTest, PopulatesEncodedFrameSize) { +TEST_P(LibaomAv1EncoderTest, PopulatesEncodedFrameSize) { VideoBitrateAllocation allocation; allocation.SetBitrate(0, 0, 30000); allocation.SetBitrate(1, 0, 40000); allocation.SetBitrate(2, 0, 30000); std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.startBitrate = allocation.get_sum_kbps(); ASSERT_GT(codec_settings.width, 4); @@ -389,9 +417,9 @@ TEST(LibaomAv1EncoderTest, PopulatesEncodedFrameSize) { codec_settings.height))))); } -TEST(LibaomAv1EncoderTest, RtpTimestampWrap) { +TEST_P(LibaomAv1EncoderTest, RtpTimestampWrap) { std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.SetScalabilityMode(ScalabilityMode::kL1T1); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), @@ -414,9 +442,9 @@ TEST(LibaomAv1EncoderTest, RtpTimestampWrap) { Eq(VideoFrameType::kVideoFrameDelta)); } -TEST(LibaomAv1EncoderTest, TestPresentationTimestamp) { +TEST_P(LibaomAv1EncoderTest, TestPresentationTimestamp) { std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); const Timestamp presentation_timestamp = Timestamp::Micros(2000); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.SetScalabilityMode(ScalabilityMode::kL2T1); @@ -447,9 +475,9 @@ TEST(LibaomAv1EncoderTest, TestPresentationTimestamp) { presentation_timestamp.us()); } -TEST(LibaomAv1EncoderTest, AdheresToTargetBitrateDespiteUnevenFrameTiming) { +TEST_P(LibaomAv1EncoderTest, AdheresToTargetBitrateDespiteUnevenFrameTiming) { std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.SetScalabilityMode(ScalabilityMode::kL1T1); codec_settings.startBitrate = 300; // kbps @@ -526,9 +554,9 @@ TEST(LibaomAv1EncoderTest, AdheresToTargetBitrateDespiteUnevenFrameTiming) { kTargetBitrateBps, kTargetBitrateBps / 10); } -TEST(LibaomAv1EncoderTest, DisableAutomaticResize) { +TEST_P(LibaomAv1EncoderTest, DisableAutomaticResize) { std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); ASSERT_TRUE(encoder); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.AV1()->automatic_resize_on = false; @@ -538,7 +566,7 @@ TEST(LibaomAv1EncoderTest, DisableAutomaticResize) { std::nullopt); } -TEST(LibaomAv1EncoderTest, PostEncodeFrameDrop) { +TEST_P(LibaomAv1EncoderTest, PostEncodeFrameDrop) { // To trigger post-encode frame drop, encode a frame of a high complexity // using a medium bitrate, then reduce the bitrate and encode the same frame // again. @@ -559,7 +587,7 @@ TEST(LibaomAv1EncoderTest, PostEncodeFrameDrop) { allocation.SetBitrate(/*spatial_index=*/0, /*temporal_index=*/0, /*bitrate_bps=*/10000000); std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = DefaultCodecSettings(); codec_settings.width = input_frame.width(); codec_settings.height = input_frame.height(); @@ -605,7 +633,7 @@ TEST(LibaomAv1EncoderTest, PostEncodeFrameDrop) { RTC_CHECK_EQ(callback.frames_encoded(), 1); } -TEST(LibaomAv1EncoderTest, EnableDisableSpatialLayersWithSvcController) { +TEST_P(LibaomAv1EncoderTest, EnableDisableSpatialLayersWithSvcController) { constexpr int kNumSpatialLayers = 3; constexpr int kNumTemporalLayers = 1; constexpr size_t kWidth = 1280; @@ -618,7 +646,7 @@ TEST(LibaomAv1EncoderTest, EnableDisableSpatialLayersWithSvcController) { // control, the encoder should always produce a frame. A dropped // frame indicates a problem and the test will fail. std::unique_ptr<VideoEncoder> encoder = - CreateLibaomAv1Encoder(CreateEnvironment()); + CreateLibaomAv1Encoder(CreateTestEnvironment()); VideoCodec codec_settings = HDCodecSettings(); SetAv1SvcConfig(codec_settings, kNumTemporalLayers, kNumSpatialLayers); codec_settings.SetFrameDropEnabled(true); @@ -687,5 +715,145 @@ TEST(LibaomAv1EncoderTest, EnableDisableSpatialLayersWithSvcController) { } } +TEST_P(LibaomAv1EncoderTest, L1T2RepeatFrame) { + std::unique_ptr<VideoEncoder> encoder = + CreateLibaomAv1Encoder(CreateTestEnvironment()); + VideoCodec codec_settings = DefaultCodecSettings(); + codec_settings.SetScalabilityMode(ScalabilityMode::kL1T2); + ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), + WEBRTC_VIDEO_CODEC_OK); + + VideoEncoder::RateControlParameters rate_parameters; + rate_parameters.framerate_fps = 30; + rate_parameters.bitrate.SetBitrate(0, 0, 150'000); + rate_parameters.bitrate.SetBitrate(0, 1, 150'000); + encoder->SetRates(rate_parameters); + + MockEncodedImageCallback callback; + encoder->RegisterEncodeCompleteCallback(&callback); + + auto frame_generator = test::CreateSquareFrameGenerator( + codec_settings.width, codec_settings.height, + test::FrameGeneratorInterface::OutputType::kI420, std::nullopt); + + // Encode a key frame. + VideoFrame key_frame = + VideoFrame::Builder() + .set_video_frame_buffer(frame_generator->NextFrame().buffer) + .set_rtp_timestamp(1000) + .build(); + std::vector<VideoFrameType> key_frame_types = { + VideoFrameType::kVideoFrameKey}; + EXPECT_CALL(callback, + OnEncodedImage(Property(&EncodedImage::TemporalIndex, Eq(0)), _)) + .WillOnce(Return(EncodedImageCallback::Result( + EncodedImageCallback::Result::Error::OK))); + ASSERT_EQ(encoder->Encode(key_frame, &key_frame_types), + WEBRTC_VIDEO_CODEC_OK); + testing::Mock::VerifyAndClearExpectations(&callback); + + // Encode 4 delta frames, marked as repeat. + const int expected_frames = drop_repeat_frames_on_enhancement_layers_ ? 2 : 4; + EXPECT_CALL(callback, OnEncodedImage) + .Times(expected_frames) + .WillRepeatedly(testing::Invoke( + [&](const EncodedImage& image, const CodecSpecificInfo* info) { + if (drop_repeat_frames_on_enhancement_layers_) { + EXPECT_EQ(image.TemporalIndex(), 0); + } + return EncodedImageCallback::Result( + EncodedImageCallback::Result::Error::OK); + })); + + for (int i = 0; i < 4; ++i) { + VideoFrame delta_frame = + VideoFrame::Builder() + .set_video_frame_buffer(frame_generator->NextFrame().buffer) + .set_rtp_timestamp(key_frame.rtp_timestamp() + (i + 1) * 3000) + .set_is_repeat_frame(true) + .build(); + std::vector<VideoFrameType> delta_frame_types = { + VideoFrameType::kVideoFrameDelta}; + ASSERT_EQ(encoder->Encode(delta_frame, &delta_frame_types), + WEBRTC_VIDEO_CODEC_OK); + } +} + +TEST_P(LibaomAv1EncoderTest, L1T2RepeatFrameNotDroppedIfDeltaTooLarge) { + if (!drop_repeat_frames_on_enhancement_layers_) { + GTEST_SKIP() << "Test only relevant when drop feature is enabled"; + } + + const Environment env = CreateTestEnvironment(); + std::unique_ptr<VideoEncoder> encoder = CreateLibaomAv1Encoder(env); + VideoCodec codec_settings = DefaultCodecSettings(); + codec_settings.SetScalabilityMode(ScalabilityMode::kL1T2); + ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), + WEBRTC_VIDEO_CODEC_OK); + + VideoEncoder::RateControlParameters rate_parameters; + rate_parameters.framerate_fps = 30; + rate_parameters.bitrate.SetBitrate(0, 0, 150'000); + rate_parameters.bitrate.SetBitrate(0, 1, 150'000); + encoder->SetRates(rate_parameters); + + MockEncodedImageCallback callback; + encoder->RegisterEncodeCompleteCallback(&callback); + + auto frame_generator = test::CreateSquareFrameGenerator( + codec_settings.width, codec_settings.height, + test::FrameGeneratorInterface::OutputType::kI420, std::nullopt); + + // Encode a key frame. + VideoFrame key_frame = + VideoFrame::Builder() + .set_video_frame_buffer(frame_generator->NextFrame().buffer) + .set_rtp_timestamp(1000) + .build(); + std::vector<VideoFrameType> key_frame_types = { + VideoFrameType::kVideoFrameKey}; + EXPECT_CALL(callback, OnEncodedImage) + .Times(1) + .WillOnce(testing::Invoke( + [](const EncodedImage& image, const CodecSpecificInfo* info) { + return EncodedImageCallback::Result( + EncodedImageCallback::Result::Error::OK); + })); + ASSERT_EQ(encoder->Encode(key_frame, &key_frame_types), + WEBRTC_VIDEO_CODEC_OK); + testing::Mock::VerifyAndClearExpectations(&callback); + + // Encode a repeat delta frame with a small gap - should be dropped. + EXPECT_CALL(callback, OnEncodedImage).Times(0); + VideoFrame delta_frame_drop = + VideoFrame::Builder() + .set_video_frame_buffer(frame_generator->NextFrame().buffer) + .set_rtp_timestamp(key_frame.rtp_timestamp() + 3000) + .set_is_repeat_frame(true) + .build(); + std::vector<VideoFrameType> delta_frame_types = { + VideoFrameType::kVideoFrameDelta}; + ASSERT_EQ(encoder->Encode(delta_frame_drop, &delta_frame_types), + WEBRTC_VIDEO_CODEC_OK); + testing::Mock::VerifyAndClearExpectations(&callback); + + // Encode a repeat delta frame with a large gap - should NOT be dropped. + EXPECT_CALL(callback, OnEncodedImage) + .Times(1) + .WillOnce(testing::Invoke( + [](const EncodedImage& image, const CodecSpecificInfo* info) { + return EncodedImageCallback::Result( + EncodedImageCallback::Result::Error::OK); + })); + VideoFrame delta_frame_keep = + VideoFrame::Builder() + .set_video_frame_buffer(frame_generator->NextFrame().buffer) + .set_rtp_timestamp(key_frame.rtp_timestamp() + 93000) + .set_is_repeat_frame(true) + .build(); + ASSERT_EQ(encoder->Encode(delta_frame_keep, &delta_frame_types), + WEBRTC_VIDEO_CODEC_OK); +} + } // namespace } // namespace webrtc