Add message container for the corruption detection extension
Bug: b/358039777
Change-Id: I8f0fbf4b6188293efe621a509e06763bccb800b0
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/359520
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Fanny Linderborg <linderborg@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42780}
diff --git a/common_video/BUILD.gn b/common_video/BUILD.gn
index 910d789..73a8adb 100644
--- a/common_video/BUILD.gn
+++ b/common_video/BUILD.gn
@@ -13,6 +13,7 @@
sources = [
"bitrate_adjuster.cc",
+ "corruption_detection_message.h",
"frame_rate_estimator.cc",
"frame_rate_estimator.h",
"framerate_controller.cc",
@@ -85,6 +86,7 @@
"../rtc_base/synchronization:mutex",
"../rtc_base/system:rtc_export",
"../system_wrappers:metrics",
+ "//third_party/abseil-cpp/absl/container:inlined_vector",
"//third_party/abseil-cpp/absl/numeric:bits",
"//third_party/abseil-cpp/absl/types:optional",
"//third_party/libyuv",
@@ -119,6 +121,7 @@
sources = [
"bitrate_adjuster_unittest.cc",
+ "corruption_detection_message_unittest.cc",
"frame_rate_estimator_unittest.cc",
"framerate_controller_unittest.cc",
"h264/h264_bitstream_parser_unittest.cc",
@@ -161,6 +164,7 @@
"../test:test_support",
"../test:video_test_common",
"//testing/gtest",
+ "//third_party/abseil-cpp/absl/types:optional",
"//third_party/libyuv",
]
diff --git a/common_video/corruption_detection_message.h b/common_video/corruption_detection_message.h
new file mode 100644
index 0000000..b6572c9
--- /dev/null
+++ b/common_video/corruption_detection_message.h
@@ -0,0 +1,153 @@
+/*
+ * 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 COMMON_VIDEO_CORRUPTION_DETECTION_MESSAGE_H_
+#define COMMON_VIDEO_CORRUPTION_DETECTION_MESSAGE_H_
+
+#include <cstddef>
+
+#include "absl/container/inlined_vector.h"
+#include "absl/types/optional.h"
+#include "api/array_view.h"
+
+namespace webrtc {
+
+class CorruptionDetectionMessage {
+ public:
+ class Builder;
+
+ CorruptionDetectionMessage() = default;
+
+ CorruptionDetectionMessage(const CorruptionDetectionMessage&) = default;
+ CorruptionDetectionMessage& operator=(const CorruptionDetectionMessage&) =
+ default;
+
+ ~CorruptionDetectionMessage() = default;
+
+ int sequence_index() const { return sequence_index_; }
+ bool interpret_sequence_index_as_most_significant_bits() const {
+ return interpret_sequence_index_as_most_significant_bits_;
+ }
+ double std_dev() const { return std_dev_; }
+ int luma_error_threshold() const { return luma_error_threshold_; }
+ int chroma_error_threshold() const { return chroma_error_threshold_; }
+ rtc::ArrayView<const double> sample_values() const {
+ return rtc::MakeArrayView(sample_values_.data(), sample_values_.size());
+ }
+
+ private:
+ friend class CorruptionDetectionExtension;
+
+ static const size_t kMaxSampleSize = 13;
+
+ // Sequence index in the Halton sequence.
+ // Valid values: [0, 2^7-1]
+ int sequence_index_ = 0;
+
+ // Whether to interpret the `sequence_index_` as the most significant bits of
+ // the true sequence index.
+ bool interpret_sequence_index_as_most_significant_bits_ = false;
+
+ // Standard deviation of the Gaussian filter kernel.
+ // Valid values: [0, 40.0]
+ double std_dev_ = 0.0;
+
+ // Corruption threshold for the luma layer.
+ // Valid values: [0, 2^4 - 1]
+ int luma_error_threshold_ = 0;
+
+ // Corruption threshold for the chroma layer.
+ // Valid values: [0, 2^4 - 1]
+ int chroma_error_threshold_ = 0;
+
+ // An ordered list of samples that are the result of applying the Gaussian
+ // filter on the image. The coordinates of the samples and their layer are
+ // determined by the Halton sequence.
+ // An empty list should be interpreted as a way to keep the `sequence_index`
+ // in sync.
+ absl::InlinedVector<double, kMaxSampleSize> sample_values_;
+};
+
+class CorruptionDetectionMessage::Builder {
+ public:
+ Builder() = default;
+
+ Builder(const Builder&) = default;
+ Builder& operator=(const Builder&) = default;
+
+ ~Builder() = default;
+
+ absl::optional<CorruptionDetectionMessage> Build() {
+ if (message_.sequence_index_ < 0 ||
+ message_.sequence_index_ > 0b0111'1111) {
+ return absl::nullopt;
+ }
+ if (message_.std_dev_ < 0.0 || message_.std_dev_ > 40.0) {
+ return absl::nullopt;
+ }
+ if (message_.luma_error_threshold_ < 0 ||
+ message_.luma_error_threshold_ > 15) {
+ return absl::nullopt;
+ }
+ if (message_.chroma_error_threshold_ < 0 ||
+ message_.chroma_error_threshold_ > 15) {
+ return absl::nullopt;
+ }
+ if (message_.sample_values_.size() > kMaxSampleSize) {
+ return absl::nullopt;
+ }
+ for (double sample_value : message_.sample_values_) {
+ if (sample_value < 0.0 || sample_value > 255.0) {
+ return absl::nullopt;
+ }
+ }
+ return message_;
+ }
+
+ Builder& WithSequenceIndex(int sequence_index) {
+ message_.sequence_index_ = sequence_index;
+ return *this;
+ }
+
+ Builder& WithInterpretSequenceIndexAsMostSignificantBits(
+ bool interpret_sequence_index_as_most_significant_bits) {
+ message_.interpret_sequence_index_as_most_significant_bits_ =
+ interpret_sequence_index_as_most_significant_bits;
+ return *this;
+ }
+
+ Builder& WithStdDev(double std_dev) {
+ message_.std_dev_ = std_dev;
+ return *this;
+ }
+
+ Builder& WithLumaErrorThreshold(int luma_error_threshold) {
+ message_.luma_error_threshold_ = luma_error_threshold;
+ return *this;
+ }
+
+ Builder& WithChromaErrorThreshold(int chroma_error_threshold) {
+ message_.chroma_error_threshold_ = chroma_error_threshold;
+ return *this;
+ }
+
+ Builder& WithSampleValues(const rtc::ArrayView<const double>& sample_values) {
+ message_.sample_values_.assign(sample_values.cbegin(),
+ sample_values.cend());
+ return *this;
+ }
+
+ private:
+ CorruptionDetectionMessage message_;
+};
+
+} // namespace webrtc
+
+#endif // COMMON_VIDEO_CORRUPTION_DETECTION_MESSAGE_H_
diff --git a/common_video/corruption_detection_message_unittest.cc b/common_video/corruption_detection_message_unittest.cc
new file mode 100644
index 0000000..ee11161
--- /dev/null
+++ b/common_video/corruption_detection_message_unittest.cc
@@ -0,0 +1,124 @@
+/*
+ * 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 "common_video/corruption_detection_message.h"
+
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+TEST(CorruptionDetectionMessageTest, FailsToCreateWhenSequenceIndexIsTooLarge) {
+ EXPECT_EQ(CorruptionDetectionMessage::Builder()
+ .WithSequenceIndex(0b1000'0000)
+ .Build(),
+ absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest, FailsToCreateWhenSequenceIndexIsTooSmall) {
+ EXPECT_EQ(CorruptionDetectionMessage::Builder().WithSequenceIndex(-1).Build(),
+ absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest, FailsToCreateWhenStddevIsTooLarge) {
+ EXPECT_EQ(CorruptionDetectionMessage::Builder().WithStdDev(45.0).Build(),
+ absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest, FailsToCreateWhenStddevIsTooSmall) {
+ EXPECT_EQ(CorruptionDetectionMessage::Builder().WithStdDev(-1.0).Build(),
+ absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest,
+ FailsToCreateWhenLumaErrorThresholdIsTooLarge) {
+ EXPECT_EQ(
+ CorruptionDetectionMessage::Builder().WithLumaErrorThreshold(16).Build(),
+ absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest,
+ FailsToCreateWhenLumaErrorThresholdIsTooSmall) {
+ EXPECT_EQ(
+ CorruptionDetectionMessage::Builder().WithLumaErrorThreshold(-1).Build(),
+ absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest,
+ FailsToCreateWhenChromaErrorThresholdIsTooLarge) {
+ EXPECT_EQ(CorruptionDetectionMessage::Builder()
+ .WithChromaErrorThreshold(16)
+ .Build(),
+ absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest,
+ FailsToCreateWhenChromaErrorThresholdIsTooSmall) {
+ EXPECT_EQ(CorruptionDetectionMessage::Builder()
+ .WithChromaErrorThreshold(-1)
+ .Build(),
+ absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest,
+ FailsToCreateWhenTooManySamplesAreSpecified) {
+ const std::vector<double> kSampleValues = {1.0, 2.0, 3.0, 4.0, 5.0,
+ 6.0, 7.0, 8.0, 9.0, 10.0,
+ 11.0, 12.0, 13.0, 14.0};
+
+ EXPECT_EQ(CorruptionDetectionMessage::Builder()
+ .WithSampleValues(kSampleValues)
+ .Build(),
+ absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest, FailsToCreateWhenSampleValueIsTooLarge) {
+ const std::vector<double> kSampleValues = {255.1};
+
+ EXPECT_EQ(CorruptionDetectionMessage::Builder()
+ .WithSampleValues(kSampleValues)
+ .Build(),
+ absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest, FailsToCreateWhenSampleValueIsTooSmall) {
+ const std::vector<double> kSampleValues = {-0.1};
+
+ EXPECT_EQ(CorruptionDetectionMessage::Builder()
+ .WithSampleValues(kSampleValues)
+ .Build(),
+ absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest,
+ CreatesDefaultWhenNoParametersAreSpecified) {
+ EXPECT_NE(CorruptionDetectionMessage::Builder().Build(), absl::nullopt);
+}
+
+TEST(CorruptionDetectionMessageTest, CreatesWhenValidParametersAreSpecified) {
+ const std::vector<double> kSampleValues = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0,
+ 7.0, 8.0, 9.0, 10.0, 11.0, 12.0};
+
+ EXPECT_NE(CorruptionDetectionMessage::Builder()
+ .WithSequenceIndex(0b0111'1111)
+ .WithInterpretSequenceIndexAsMostSignificantBits(true)
+ .WithStdDev(40.0)
+ .WithLumaErrorThreshold(15)
+ .WithChromaErrorThreshold(15)
+ .WithSampleValues(kSampleValues)
+ .Build(),
+ absl::nullopt);
+}
+
+} // namespace
+} // namespace webrtc