Add generic mapping functions for automatic corruption detection

The functions will be used to map a frame's QP to its optimal standard
deviation for the Gaussian kernel for the filter applied in the
automatic corruption detection algorithm.

Bug: b/358039777
Change-Id: Idb3b8cfdbd4a405151c660df87205e3949f9b085
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/359380
Commit-Queue: Fanny Linderborg <linderborg@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42777}
diff --git a/video/corruption_detection/BUILD.gn b/video/corruption_detection/BUILD.gn
index 879639d..df603aa 100644
--- a/video/corruption_detection/BUILD.gn
+++ b/video/corruption_detection/BUILD.gn
@@ -8,6 +8,18 @@
 
 import("../../webrtc.gni")
 
+rtc_library("generic_mapping_functions") {
+  sources = [
+    "generic_mapping_functions.cc",
+    "generic_mapping_functions.h",
+  ]
+  deps = [
+    "../../api/video:video_frame",
+    "../../api/video_codecs:video_codecs_api",
+    "../../rtc_base:checks",
+  ]
+}
+
 rtc_library("halton_sequence") {
   sources = [
     "halton_sequence.cc",
@@ -17,6 +29,16 @@
 }
 
 if (rtc_include_tests) {
+  rtc_library("generic_mapping_functions_unittest") {
+    testonly = true
+    sources = [ "generic_mapping_functions_unittest.cc" ]
+    deps = [
+      ":generic_mapping_functions",
+      "../../api/video:video_frame",
+      "../../test/:test_support",
+    ]
+  }
+
   rtc_library("halton_sequence_unittest") {
     testonly = true
     sources = [ "halton_sequence_unittest.cc" ]
@@ -29,6 +51,9 @@
   rtc_library("corruption_detection_tests") {
     testonly = true
     sources = []
-    deps = [ ":halton_sequence_unittest" ]
+    deps = [
+      ":generic_mapping_functions_unittest",
+      ":halton_sequence_unittest",
+    ]
   }
 }
diff --git a/video/corruption_detection/generic_mapping_functions.cc b/video/corruption_detection/generic_mapping_functions.cc
new file mode 100644
index 0000000..ff37734
--- /dev/null
+++ b/video/corruption_detection/generic_mapping_functions.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright 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.
+ */
+
+#include "video/corruption_detection/generic_mapping_functions.h"
+
+#include <cmath>
+
+#include "api/video/video_codec_type.h"
+#include "api/video_codecs/video_codec.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace {
+
+constexpr int kLumaThreshold = 5;
+constexpr int kChromaThresholdVp8 = 6;
+constexpr int kChromaThresholdVp9 = 4;
+constexpr int kChromaThresholdAv1 = 4;
+constexpr int kChromaThresholdH264 = 2;
+
+int LumaThreshold(VideoCodecType codec_type) {
+  return kLumaThreshold;
+}
+
+int ChromaThreshold(VideoCodecType codec_type) {
+  switch (codec_type) {
+    case VideoCodecType::kVideoCodecVP8:
+      return kChromaThresholdVp8;
+    case VideoCodecType::kVideoCodecVP9:
+      return kChromaThresholdVp9;
+    case VideoCodecType::kVideoCodecAV1:
+      return kChromaThresholdAv1;
+    case VideoCodecType::kVideoCodecH264:
+      return kChromaThresholdH264;
+    default:
+      RTC_FATAL() << "Codec type " << CodecTypeToPayloadString(codec_type)
+                  << " is not supported.";
+  }
+}
+
+double ExponentialFunction(double a, double b, double c, int qp) {
+  return a * std::exp(b * qp - c);
+}
+
+double RationalFunction(double a, double b, double c, int qp) {
+  return (-a * qp) / (qp + b) + c;
+}
+
+// Maps QP to the optimal standard deviation for the Gausian kernel.
+// Observe that the values below can be changed unnoticed.
+double MapQpToOptimalStdDev(int qp, VideoCodecType codec_type) {
+  switch (codec_type) {
+    case VideoCodecType::kVideoCodecVP8:
+      return ExponentialFunction(0.006, 0.01857465, -4.26470513, qp);
+    case VideoCodecType::kVideoCodecVP9:
+      return RationalFunction(1, -257, 0.3, qp);
+    case VideoCodecType::kVideoCodecAV1:
+      return RationalFunction(0.69, -256, 0.42, qp);
+    case VideoCodecType::kVideoCodecH264:
+      return ExponentialFunction(0.016, 0.13976962, -1.40179328, qp);
+    default:
+      RTC_FATAL() << "Codec type " << CodecTypeToPayloadString(codec_type)
+                  << " is not supported.";
+  }
+}
+
+}  // 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)};
+}
+
+}  // namespace webrtc
diff --git a/video/corruption_detection/generic_mapping_functions.h b/video/corruption_detection/generic_mapping_functions.h
new file mode 100644
index 0000000..81100df
--- /dev/null
+++ b/video/corruption_detection/generic_mapping_functions.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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 VIDEO_CORRUPTION_DETECTION_GENERIC_MAPPING_FUNCTIONS_H_
+#define VIDEO_CORRUPTION_DETECTION_GENERIC_MAPPING_FUNCTIONS_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;
+};
+
+FilterSettings GetCorruptionFilterSettings(int qp, VideoCodecType codec_type);
+
+}  // namespace webrtc
+
+#endif  // VIDEO_CORRUPTION_DETECTION_GENERIC_MAPPING_FUNCTIONS_H_
diff --git a/video/corruption_detection/generic_mapping_functions_unittest.cc b/video/corruption_detection/generic_mapping_functions_unittest.cc
new file mode 100644
index 0000000..e8d6c3d
--- /dev/null
+++ b/video/corruption_detection/generic_mapping_functions_unittest.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright 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.
+ */
+
+#include "video/corruption_detection/generic_mapping_functions.h"
+
+#include "api/video/video_codec_type.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+using ::testing::DoubleNear;
+using ::testing::FieldsAre;
+
+constexpr double kMaxAbsoluteError = 1e-4;
+
+constexpr int kLumaThreshold = 5;
+constexpr int kChromaThresholdVp8 = 6;
+constexpr int kChromaThresholdVp9 = 4;
+constexpr int kChromaThresholdAv1 = 4;
+constexpr int kChromaThresholdH264 = 2;
+
+TEST(GenericMappingFunctionsTest, TestVp8) {
+  constexpr VideoCodecType kCodecType = VideoCodecType::kVideoCodecVP8;
+  EXPECT_THAT(GetCorruptionFilterSettings(
+                  /*qp=*/10, kCodecType),
+              FieldsAre(DoubleNear(0.5139, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdVp8));
+  EXPECT_THAT(GetCorruptionFilterSettings(
+                  /*qp=*/100, kCodecType),
+              FieldsAre(DoubleNear(2.7351, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdVp8));
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/127, kCodecType),
+              FieldsAre(DoubleNear(4.5162, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdVp8));
+}
+
+TEST(GenericMappingFunctionsTest, TestVp9) {
+  constexpr VideoCodecType kCodecType = VideoCodecType::kVideoCodecVP9;
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/10, kCodecType),
+              FieldsAre(DoubleNear(0.3405, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdVp9));
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/100, kCodecType),
+              FieldsAre(DoubleNear(0.9369, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdVp9));
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/200, kCodecType),
+              FieldsAre(DoubleNear(3.8088, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdVp9));
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/255, kCodecType),
+              FieldsAre(DoubleNear(127.8, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdVp9));
+}
+
+TEST(GenericMappingFunctionsTest, TestAv1) {
+  constexpr VideoCodecType kCodecType = VideoCodecType::kVideoCodecAV1;
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/10, kCodecType),
+              FieldsAre(DoubleNear(0.4480, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdAv1));
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/100, kCodecType),
+              FieldsAre(DoubleNear(0.8623, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdAv1));
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/200, kCodecType),
+              FieldsAre(DoubleNear(2.8842, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdAv1));
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/255, kCodecType),
+              FieldsAre(DoubleNear(176.37, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdAv1));
+}
+
+TEST(GenericMappingFunctionsTest, TestH264) {
+  constexpr VideoCodecType kCodecType = VideoCodecType::kVideoCodecH264;
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/10, kCodecType),
+              FieldsAre(DoubleNear(0.263, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdH264));
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/30, kCodecType),
+              FieldsAre(DoubleNear(4.3047, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdH264));
+  EXPECT_THAT(GetCorruptionFilterSettings(/*qp=*/51, kCodecType),
+              FieldsAre(DoubleNear(81.0346, kMaxAbsoluteError), kLumaThreshold,
+                        kChromaThresholdH264));
+}
+
+}  // namespace
+}  // namespace webrtc