Add optional corruption filter settings to EncodedImage.

This is a prerequisite for enabling implementation-specific filter
settings for automatic corruption detection.

Bug: webrtc:358039777
Change-Id: I363c592aa35164f690dd4ad1204e90afc0277d8b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/368940
Commit-Queue: Erik Språng <sprang@webrtc.org>
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43443}
diff --git a/api/video/BUILD.gn b/api/video/BUILD.gn
index fd6cbd2..57e4d9f 100644
--- a/api/video/BUILD.gn
+++ b/api/video/BUILD.gn
@@ -142,6 +142,11 @@
   deps = [ "../../rtc_base/system:rtc_export" ]
 }
 
+rtc_source_set("corruption_detection_filter_settings") {
+  visibility = [ "*" ]
+  public = [ "corruption_detection_filter_settings.h" ]
+}
+
 rtc_library("encoded_image") {
   visibility = [ "*" ]
   sources = [
@@ -149,6 +154,7 @@
     "encoded_image.h",
   ]
   deps = [
+    ":corruption_detection_filter_settings",
     ":video_codec_constants",
     ":video_frame",
     ":video_frame_type",
diff --git a/api/video/corruption_detection_filter_settings.h b/api/video/corruption_detection_filter_settings.h
new file mode 100644
index 0000000..9ae2414
--- /dev/null
+++ b/api/video/corruption_detection_filter_settings.h
@@ -0,0 +1,27 @@
+/*
+ *  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.
+ */
+
+#ifndef API_VIDEO_CORRUPTION_DETECTION_FILTER_SETTINGS_H_
+#define API_VIDEO_CORRUPTION_DETECTION_FILTER_SETTINGS_H_
+
+#include <stdint.h>
+
+// Filter settings for automatic corruption detection. See
+// http://www.webrtc.org/experiments/rtp-hdrext/corruption-detection for more
+// information.
+struct CorruptionDetectionFilterSettings {
+  // Size of the blur kernel used.
+  double std_dev = 0.0;
+  // Allowed error thresholds (maps to `Y err` and `UV err` respectively).
+  int luma_error_threshold = 0;
+  int chroma_error_threshold = 0;
+};
+
+#endif  // API_VIDEO_CORRUPTION_DETECTION_FILTER_SETTINGS_H_
diff --git a/api/video/encoded_image.h b/api/video/encoded_image.h
index 53264be..553768e 100644
--- a/api/video/encoded_image.h
+++ b/api/video/encoded_image.h
@@ -23,6 +23,7 @@
 #include "api/scoped_refptr.h"
 #include "api/units/timestamp.h"
 #include "api/video/color_space.h"
+#include "api/video/corruption_detection_filter_settings.h"
 #include "api/video/video_codec_constants.h"
 #include "api/video/video_content_type.h"
 #include "api/video/video_frame_type.h"
@@ -228,6 +229,15 @@
   VideoContentType contentType() const { return content_type_; }
   VideoRotation rotation() const { return rotation_; }
 
+  std::optional<CorruptionDetectionFilterSettings>
+  corruption_detection_filter_settings() const {
+    return corruption_detection_filter_settings_;
+  }
+  void set_corruption_detection_filter_settings(
+      const CorruptionDetectionFilterSettings& settings) {
+    corruption_detection_filter_settings_ = settings;
+  }
+
   uint32_t _encodedWidth = 0;
   uint32_t _encodedHeight = 0;
   // NTP time of the capture time in local timebase in milliseconds.
@@ -283,6 +293,12 @@
   // True if the frame that was encoded is a steady-state refresh frame intended
   // to improve the visual quality.
   bool is_steady_state_refresh_frame_ = false;
+
+  // Filter settings for corruption detection suggested by the encoder
+  // implementation, if any. Otherwise generic per-codec-type settings will be
+  // used.
+  std::optional<CorruptionDetectionFilterSettings>
+      corruption_detection_filter_settings_;
 };
 
 }  // namespace webrtc
diff --git a/video/corruption_detection/BUILD.gn b/video/corruption_detection/BUILD.gn
index c7ab70b..9f98783 100644
--- a/video/corruption_detection/BUILD.gn
+++ b/video/corruption_detection/BUILD.gn
@@ -47,6 +47,7 @@
     ":generic_mapping_functions",
     ":halton_frame_sampler",
     "../../api:scoped_refptr",
+    "../../api/video:corruption_detection_filter_settings",
     "../../api/video:encoded_image",
     "../../api/video:video_frame",
     "../../api/video:video_frame_type",
@@ -84,6 +85,7 @@
     "generic_mapping_functions.h",
   ]
   deps = [
+    "../../api/video:corruption_detection_filter_settings",
     "../../api/video:video_frame",
     "../../api/video_codecs:video_codecs_api",
     "../../rtc_base:checks",
diff --git a/video/corruption_detection/frame_instrumentation_generator.cc b/video/corruption_detection/frame_instrumentation_generator.cc
index df6fe86..508afc6 100644
--- a/video/corruption_detection/frame_instrumentation_generator.cc
+++ b/video/corruption_detection/frame_instrumentation_generator.cc
@@ -19,6 +19,7 @@
 #include "absl/algorithm/container.h"
 #include "absl/types/variant.h"
 #include "api/scoped_refptr.h"
+#include "api/video/corruption_detection_filter_settings.h"
 #include "api/video/encoded_image.h"
 #include "api/video/video_codec_type.h"
 #include "api/video/video_frame.h"
@@ -40,36 +41,33 @@
 // can lead to frame buffer pools draining.
 constexpr size_t kMaxPendingFrames = 3;
 
-std::optional<FilterSettings> GetCorruptionFilterSettings(
+std::optional<CorruptionDetectionFilterSettings> GetCorruptionFilterSettings(
     const EncodedImage& encoded_image,
     VideoCodecType video_codec_type,
     int layer_id) {
-  /* TODO: bugs.webrtc.org/358039777 - Uncomment when parameters are available
-     in EncodedImage.
-  if (encoded_image.CorruptionDetectionParameters()) {
-    return FilterSettings{
-        .std_dev = encoded_image.CorruptionDetectionParameters()->std_dev,
-        .luma_error_threshold =
-            encoded_image.CorruptionDetectionParameters()->luma_error_threshold,
-        .chroma_error_threshold = encoded_image.CorruptionDetectionParameters()
-                                      ->chroma_error_threshold};
-  }
-  */
+  std::optional<CorruptionDetectionFilterSettings> filter_settings =
+      encoded_image.corruption_detection_filter_settings();
 
-  int qp = encoded_image.qp_;
-  if (qp == -1) {
-    std::optional<uint32_t> parsed_qp = QpParser().Parse(
-        video_codec_type, layer_id, encoded_image.data(), encoded_image.size());
-    if (!parsed_qp.has_value()) {
-      RTC_LOG(LS_VERBOSE) << "Missing QP for "
-                          << CodecTypeToPayloadString(video_codec_type)
-                          << " layer " << layer_id << ".";
-      return std::nullopt;
+  if (!filter_settings.has_value()) {
+    // No implementation specific filter settings available, using a generic
+    // QP-based settings instead.
+    int qp = encoded_image.qp_;
+    if (qp == -1) {
+      std::optional<uint32_t> parsed_qp =
+          QpParser().Parse(video_codec_type, layer_id, encoded_image.data(),
+                           encoded_image.size());
+      if (!parsed_qp.has_value()) {
+        RTC_LOG(LS_VERBOSE)
+            << "Missing QP for " << CodecTypeToPayloadString(video_codec_type)
+            << " layer " << layer_id << ".";
+        return std::nullopt;
+      }
+      qp = *parsed_qp;
     }
-    qp = *parsed_qp;
-  }
 
-  return GetCorruptionFilterSettings(qp, video_codec_type);
+    filter_settings = GetCorruptionFilterSettings(qp, video_codec_type);
+  }
+  return filter_settings;
 }
 
 }  // namespace
@@ -158,7 +156,7 @@
                                         .communicate_upper_bits = true};
   }
 
-  std::optional<FilterSettings> filter_settings =
+  std::optional<CorruptionDetectionFilterSettings> filter_settings =
       GetCorruptionFilterSettings(encoded_image, video_codec_type_, layer_id);
   if (!filter_settings.has_value()) {
     return std::nullopt;
diff --git a/video/corruption_detection/frame_instrumentation_generator_unittest.cc b/video/corruption_detection/frame_instrumentation_generator_unittest.cc
index 7f8992b..5f3b011 100644
--- a/video/corruption_detection/frame_instrumentation_generator_unittest.cc
+++ b/video/corruption_detection/frame_instrumentation_generator_unittest.cc
@@ -722,5 +722,37 @@
   EXPECT_THAT(frames_destroyed, ElementsAre(true, true, true, true));
 }
 
+TEST(FrameInstrumentationGeneratorTest,
+     UsesFilterSettingsFromFrameWhenAvailable) {
+  FrameInstrumentationGenerator generator(VideoCodecType::kVideoCodecVP8);
+  VideoFrame frame = VideoFrame::Builder()
+                         .set_video_frame_buffer(MakeDefaultI420FrameBuffer())
+                         .set_rtp_timestamp(1)
+                         .build();
+  // No QP needed when frame provides filter settings.
+  EncodedImage encoded_image;
+  encoded_image.SetRtpTimestamp(1);
+  encoded_image.SetFrameType(VideoFrameType::kVideoFrameKey);
+  encoded_image._encodedWidth = kDefaultScaledWidth;
+  encoded_image._encodedHeight = kDefaultScaledHeight;
+  encoded_image.set_corruption_detection_filter_settings(
+      CorruptionDetectionFilterSettings{.std_dev = 1.0,
+                                        .luma_error_threshold = 2,
+                                        .chroma_error_threshold = 3});
+
+  generator.OnCapturedFrame(frame);
+  std::optional<
+      absl::variant<FrameInstrumentationSyncData, FrameInstrumentationData>>
+      data = generator.OnEncodedImage(encoded_image);
+
+  ASSERT_TRUE(data.has_value());
+  ASSERT_TRUE(absl::holds_alternative<FrameInstrumentationData>(*data));
+  FrameInstrumentationData frame_instrumentation_data =
+      absl::get<FrameInstrumentationData>(*data);
+  EXPECT_EQ(frame_instrumentation_data.std_dev, 1.0);
+  EXPECT_EQ(frame_instrumentation_data.luma_error_threshold, 2);
+  EXPECT_EQ(frame_instrumentation_data.chroma_error_threshold, 3);
+}
+
 }  // namespace
 }  // namespace webrtc
diff --git a/video/corruption_detection/frame_pair_corruption_score.cc b/video/corruption_detection/frame_pair_corruption_score.cc
index 084005a..91b789c 100644
--- a/video/corruption_detection/frame_pair_corruption_score.cc
+++ b/video/corruption_detection/frame_pair_corruption_score.cc
@@ -77,7 +77,8 @@
   scoped_refptr<I420Buffer> test_i420_buffer =
       GetAsI420Buffer(test_buffer.ToI420());
 
-  FilterSettings filter_settings = GetCorruptionFilterSettings(qp, codec_type_);
+  CorruptionDetectionFilterSettings filter_settings =
+      GetCorruptionFilterSettings(qp, codec_type_);
 
   const std::vector<FilteredSample> filtered_reference_sample_values =
       GetSampleValuesForFrame(
diff --git a/video/corruption_detection/generic_mapping_functions.cc b/video/corruption_detection/generic_mapping_functions.cc
index ff37734..cb489b8 100644
--- a/video/corruption_detection/generic_mapping_functions.cc
+++ b/video/corruption_detection/generic_mapping_functions.cc
@@ -73,10 +73,13 @@
 
 }  // namespace
 
-FilterSettings GetCorruptionFilterSettings(int qp, VideoCodecType codec_type) {
-  return FilterSettings{.std_dev = MapQpToOptimalStdDev(qp, codec_type),
-                        .luma_error_threshold = LumaThreshold(codec_type),
-                        .chroma_error_threshold = ChromaThreshold(codec_type)};
+CorruptionDetectionFilterSettings GetCorruptionFilterSettings(
+    int qp,
+    VideoCodecType codec_type) {
+  return CorruptionDetectionFilterSettings{
+      .std_dev = MapQpToOptimalStdDev(qp, codec_type),
+      .luma_error_threshold = LumaThreshold(codec_type),
+      .chroma_error_threshold = ChromaThreshold(codec_type)};
 }
 
 }  // namespace webrtc
diff --git a/video/corruption_detection/generic_mapping_functions.h b/video/corruption_detection/generic_mapping_functions.h
index 81100df..191c229 100644
--- a/video/corruption_detection/generic_mapping_functions.h
+++ b/video/corruption_detection/generic_mapping_functions.h
@@ -11,17 +11,17 @@
 #ifndef VIDEO_CORRUPTION_DETECTION_GENERIC_MAPPING_FUNCTIONS_H_
 #define VIDEO_CORRUPTION_DETECTION_GENERIC_MAPPING_FUNCTIONS_H_
 
+#include "api/video/corruption_detection_filter_settings.h"
 #include "api/video/video_codec_type.h"
 
 namespace webrtc {
 
-struct FilterSettings {
-  double std_dev = 0.0;
-  int luma_error_threshold = 0;
-  int chroma_error_threshold = 0;
-};
+// TODO: bugs.webrtc.org/358039777 - Remove when downstream usage is gone.
+using FilterSettings = CorruptionDetectionFilterSettings;
 
-FilterSettings GetCorruptionFilterSettings(int qp, VideoCodecType codec_type);
+CorruptionDetectionFilterSettings GetCorruptionFilterSettings(
+    int qp,
+    VideoCodecType codec_type);
 
 }  // namespace webrtc