diff --git a/video/corruption_detection/BUILD.gn b/video/corruption_detection/BUILD.gn index 879639dc91..df603aa500 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 @@ rtc_library("halton_sequence") { } 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 @@ if (rtc_include_tests) { 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 0000000000..ff37734007 --- /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 + +#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 0000000000..81100df850 --- /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 0000000000..e8d6c3dc0b --- /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