diff --git a/common_video/BUILD.gn b/common_video/BUILD.gn index 910d789ec1..73a8adb5d3 100644 --- a/common_video/BUILD.gn +++ b/common_video/BUILD.gn @@ -13,6 +13,7 @@ rtc_library("common_video") { sources = [ "bitrate_adjuster.cc", + "corruption_detection_message.h", "frame_rate_estimator.cc", "frame_rate_estimator.h", "framerate_controller.cc", @@ -85,6 +86,7 @@ rtc_library("common_video") { "../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 @@ if (rtc_include_tests && !build_with_chromium) { 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 @@ if (rtc_include_tests && !build_with_chromium) { "../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 0000000000..b6572c9245 --- /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 + +#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 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 sample_values_; +}; + +class CorruptionDetectionMessage::Builder { + public: + Builder() = default; + + Builder(const Builder&) = default; + Builder& operator=(const Builder&) = default; + + ~Builder() = default; + + absl::optional 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& 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 0000000000..ee11161bfd --- /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 + +#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 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 kSampleValues = {255.1}; + + EXPECT_EQ(CorruptionDetectionMessage::Builder() + .WithSampleValues(kSampleValues) + .Build(), + absl::nullopt); +} + +TEST(CorruptionDetectionMessageTest, FailsToCreateWhenSampleValueIsTooSmall) { + const std::vector 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 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