Enable post-encode frame drop in libaom AV1 encoder

Bug: webrtc:351644568
Change-Id: Ic761fbaf21f1c55b8839a21cc54e450f71606b8d
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/376060
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43884}
diff --git a/experiments/field_trials.py b/experiments/field_trials.py
index 6d8e83c..768b42f 100755
--- a/experiments/field_trials.py
+++ b/experiments/field_trials.py
@@ -104,6 +104,9 @@
     FieldTrial('WebRTC-LibaomAv1Encoder-AdaptiveMaxConsecDrops',
                351644568,
                date(2025, 7, 1)),
+    FieldTrial('WebRTC-LibaomAv1Encoder-PostEncodeFrameDrop',
+               351644568,
+               date(2026, 1, 30)),
     FieldTrial('WebRTC-LibvpxVp8Encoder-AndroidSpecificThreadingSettings',
                42226191,
                date(2024, 9, 1)),
diff --git a/modules/BUILD.gn b/modules/BUILD.gn
index 76fcf08..d2be5c5 100644
--- a/modules/BUILD.gn
+++ b/modules/BUILD.gn
@@ -163,6 +163,7 @@
     "../resources/near88_stereo.pcm",
     "../resources/near8_stereo.pcm",
     "../resources/near96_stereo.pcm",
+    "../resources/photo_1850_1110.yuv",
     "../resources/ref03.aecdump",
     "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_0_AST.bin",
     "../resources/remote_bitrate_estimator/VideoSendersTest_BweTest_IncreasingChoke1_0_TOF.bin",
@@ -199,7 +200,7 @@
     bundle_data("modules_unittests_bundle_data") {
       testonly = true
       sources = modules_unittests_resources
-      outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
+      outputs = [ "{{bundle_resources_dir}}/{{source_root_relative_dir}}/{{source_file_part}}" ]
     }
   }
 
diff --git a/modules/video_coding/codecs/av1/BUILD.gn b/modules/video_coding/codecs/av1/BUILD.gn
index 8a2d490..2769420 100644
--- a/modules/video_coding/codecs/av1/BUILD.gn
+++ b/modules/video_coding/codecs/av1/BUILD.gn
@@ -105,7 +105,9 @@
         "../../../../api/units:time_delta",
         "../../../../api/video:video_frame",
         "../../../../modules/rtp_rtcp:rtp_rtcp_format",
+        "../../../../test:fileutils",
         "../../../../test:scoped_key_value_config",
+        "../../../../test:video_test_support",
         "../../svc:scalability_mode_util",
         "../../svc:scalability_structures",
         "../../svc:scalable_video_controller",
diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder.cc
index a5a87ee..919c79a 100644
--- a/modules/video_coding/codecs/av1/libaom_av1_encoder.cc
+++ b/modules/video_coding/codecs/av1/libaom_av1_encoder.cc
@@ -135,7 +135,10 @@
   const LibaomAv1EncoderInfoSettings encoder_info_override_;
   // TODO(webrtc:351644568): Remove this kill-switch after the feature is fully
   // deployed.
-  bool adaptive_max_consec_drops_;
+  const bool adaptive_max_consec_drops_;
+  // TODO(webrtc:351644568): Remove this kill-switch after the feature is fully
+  // deployed.
+  const bool post_encode_frame_drop_;
 };
 
 int32_t VerifyCodecSettings(const VideoCodec& codec_settings) {
@@ -177,7 +180,9 @@
       timestamp_(0),
       encoder_info_override_(env.field_trials()),
       adaptive_max_consec_drops_(!env.field_trials().IsDisabled(
-          "WebRTC-LibaomAv1Encoder-AdaptiveMaxConsecDrops")) {}
+          "WebRTC-LibaomAv1Encoder-AdaptiveMaxConsecDrops")),
+      post_encode_frame_drop_(!env.field_trials().IsDisabled(
+          "WebRTC-LibaomAv1Encoder-PostEncodeFrameDrop")) {}
 
 LibaomAv1Encoder::~LibaomAv1Encoder() {
   Release();
@@ -336,6 +341,10 @@
                                       250);
   }
 
+  if (post_encode_frame_drop_) {
+    SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_POSTENCODE_DROP_RTC, 1);
+  }
+
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc
index 79275e1..ba2f1af 100644
--- a/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc
+++ b/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc
@@ -21,6 +21,7 @@
 #include "api/environment/environment_factory.h"
 #include "api/test/create_frame_generator.h"
 #include "api/test/frame_generator_interface.h"
+#include "api/video/i420_buffer.h"
 #include "api/video_codecs/video_codec.h"
 #include "api/video_codecs/video_encoder.h"
 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
@@ -29,6 +30,8 @@
 #include "test/gmock.h"
 #include "test/gtest.h"
 #include "test/scoped_key_value_config.h"
+#include "test/testsupport/file_utils.h"
+#include "test/testsupport/frame_reader.h"
 
 namespace webrtc {
 namespace {
@@ -237,7 +240,7 @@
 TEST_P(LibaomAv1EncoderMaxConsecDropTest, MaxConsecDrops) {
   VideoBitrateAllocation allocation;
   allocation.SetBitrate(0, 0,
-                        1000);  // Very low bitrate to provoke frame drops.
+                        2000);  // A low bitrate to provoke frame drops.
   std::unique_ptr<VideoEncoder> encoder =
       CreateLibaomAv1Encoder(CreateEnvironment());
   VideoCodec codec_settings = DefaultCodecSettings();
@@ -509,5 +512,72 @@
             std::nullopt);
 }
 
+TEST(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.
+  // Using a medium bitrate for the first frame prevents quality and QP
+  // saturation. Encoding the same content twice prevents scene change
+  // detection. The second frame overshoots RC buffer and provokes post-encode
+  // drop.
+  VideoFrame input_frame =
+      VideoFrame::Builder()
+          .set_video_frame_buffer(
+              test::CreateYuvFrameReader(
+                  test::ResourcePath("photo_1850_1110", "yuv"),
+                  {.width = 1850, .height = 1110})
+                  ->PullFrame())
+          .build();
+
+  VideoBitrateAllocation allocation;
+  allocation.SetBitrate(/*spatial_index=*/0, /*temporal_index=*/0,
+                        /*bitrate_bps=*/10000000);
+  std::unique_ptr<VideoEncoder> encoder =
+      CreateLibaomAv1Encoder(CreateEnvironment());
+  VideoCodec codec_settings = DefaultCodecSettings();
+  codec_settings.width = input_frame.width();
+  codec_settings.height = input_frame.height();
+  codec_settings.startBitrate = allocation.get_sum_kbps();
+  codec_settings.SetFrameDropEnabled(true);
+  codec_settings.SetScalabilityMode(ScalabilityMode::kL1T1);
+  ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()),
+            WEBRTC_VIDEO_CODEC_OK);
+  encoder->SetRates(VideoEncoder::RateControlParameters(
+      allocation, codec_settings.maxFramerate));
+
+  class EncoderCallback : public EncodedImageCallback {
+   public:
+    EncoderCallback() = default;
+    int frames_encoded() const { return frames_encoded_; }
+
+   private:
+    Result OnEncodedImage(
+        const EncodedImage& encoded_image,
+        const CodecSpecificInfo* /* codec_specific_info */) override {
+      frames_encoded_++;
+      return Result(Result::Error::OK);
+    }
+
+    int frames_encoded_ = 0;
+  } callback;
+  encoder->RegisterEncodeCompleteCallback(&callback);
+
+  input_frame.set_rtp_timestamp(1 * kVideoPayloadTypeFrequency /
+                                codec_settings.maxFramerate);
+  RTC_CHECK_EQ(encoder->Encode(input_frame, /*frame_types=*/nullptr),
+               WEBRTC_VIDEO_CODEC_OK);
+
+  allocation.SetBitrate(/*spatial_index=*/0, /*temporal_index=*/0,
+                        /*bitrate_bps=*/1000);
+  encoder->SetRates(VideoEncoder::RateControlParameters(
+      allocation, codec_settings.maxFramerate));
+
+  input_frame.set_rtp_timestamp(2 * kVideoPayloadTypeFrequency /
+                                codec_settings.maxFramerate);
+  RTC_CHECK_EQ(encoder->Encode(input_frame, /*frame_types=*/nullptr),
+               WEBRTC_VIDEO_CODEC_OK);
+  RTC_CHECK_EQ(callback.frames_encoded(), 1);
+}
+
 }  // namespace
 }  // namespace webrtc