summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/api/video_codecs/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/api/video_codecs/test
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/api/video_codecs/test')
-rw-r--r--third_party/libwebrtc/api/video_codecs/test/BUILD.gn86
-rw-r--r--third_party/libwebrtc/api/video_codecs/test/builtin_video_encoder_factory_unittest.cc39
-rw-r--r--third_party/libwebrtc/api/video_codecs/test/h264_profile_level_id_unittest.cc171
-rw-r--r--third_party/libwebrtc/api/video_codecs/test/h265_profile_tier_level_unittest.cc248
-rw-r--r--third_party/libwebrtc/api/video_codecs/test/sdp_video_format_unittest.cc155
-rw-r--r--third_party/libwebrtc/api/video_codecs/test/video_decoder_factory_template_tests.cc123
-rw-r--r--third_party/libwebrtc/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc304
-rw-r--r--third_party/libwebrtc/api/video_codecs/test/video_encoder_factory_template_tests.cc173
-rw-r--r--third_party/libwebrtc/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc1144
9 files changed, 2443 insertions, 0 deletions
diff --git a/third_party/libwebrtc/api/video_codecs/test/BUILD.gn b/third_party/libwebrtc/api/video_codecs/test/BUILD.gn
new file mode 100644
index 0000000000..7bfe86e9f4
--- /dev/null
+++ b/third_party/libwebrtc/api/video_codecs/test/BUILD.gn
@@ -0,0 +1,86 @@
+# Copyright (c) 2018 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.
+
+import("../../../webrtc.gni")
+
+if (rtc_include_tests) {
+ rtc_library("video_codecs_api_unittests") {
+ testonly = true
+ sources = [
+ "builtin_video_encoder_factory_unittest.cc",
+ "h264_profile_level_id_unittest.cc",
+ "sdp_video_format_unittest.cc",
+ "video_decoder_software_fallback_wrapper_unittest.cc",
+ "video_encoder_software_fallback_wrapper_unittest.cc",
+ ]
+
+ if (rtc_use_h265) {
+ sources += [ "h265_profile_tier_level_unittest.cc" ]
+ }
+
+ deps = [
+ ":video_decoder_factory_template_tests",
+ ":video_encoder_factory_template_tests",
+ "..:builtin_video_encoder_factory",
+ "..:rtc_software_fallback_wrappers",
+ "..:video_codecs_api",
+ "../..:fec_controller_api",
+ "../..:mock_video_encoder",
+ "../../../api:scoped_refptr",
+ "../../../media:media_constants",
+ "../../../media:rtc_media_base",
+ "../../../modules/video_coding:video_codec_interface",
+ "../../../modules/video_coding:video_coding_utility",
+ "../../../modules/video_coding:webrtc_vp8",
+ "../../../rtc_base:checks",
+ "../../../rtc_base:rtc_base_tests_utils",
+ "../../../test:fake_video_codecs",
+ "../../../test:field_trial",
+ "../../../test:test_support",
+ "../../../test:video_test_common",
+ "../../video:encoded_image",
+ "../../video:video_bitrate_allocation",
+ "../../video:video_frame",
+ "../../video:video_rtp_headers",
+ "//testing/gtest",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
+ }
+
+ rtc_library("video_encoder_factory_template_tests") {
+ testonly = true
+ sources = [ "video_encoder_factory_template_tests.cc" ]
+
+ deps = [
+ "..:video_encoder_factory_template",
+ "..:video_encoder_factory_template_libaom_av1_adapter",
+ "..:video_encoder_factory_template_libvpx_vp8_adapter",
+ "..:video_encoder_factory_template_libvpx_vp9_adapter",
+ "..:video_encoder_factory_template_open_h264_adapter",
+ "../../:mock_video_encoder",
+ "../../../test:test_support",
+ "//testing/gtest",
+ ]
+ }
+
+ rtc_library("video_decoder_factory_template_tests") {
+ testonly = true
+ sources = [ "video_decoder_factory_template_tests.cc" ]
+
+ deps = [
+ "..:video_decoder_factory_template",
+ "..:video_decoder_factory_template_dav1d_adapter",
+ "..:video_decoder_factory_template_libvpx_vp8_adapter",
+ "..:video_decoder_factory_template_libvpx_vp9_adapter",
+ "..:video_decoder_factory_template_open_h264_adapter",
+ "../../:mock_video_decoder",
+ "../../../test:test_support",
+ "//testing/gtest",
+ ]
+ }
+}
diff --git a/third_party/libwebrtc/api/video_codecs/test/builtin_video_encoder_factory_unittest.cc b/third_party/libwebrtc/api/video_codecs/test/builtin_video_encoder_factory_unittest.cc
new file mode 100644
index 0000000000..84fd594b4c
--- /dev/null
+++ b/third_party/libwebrtc/api/video_codecs/test/builtin_video_encoder_factory_unittest.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 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 "api/video_codecs/builtin_video_encoder_factory.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "api/video_codecs/sdp_video_format.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(BuiltinVideoEncoderFactoryTest, AnnouncesVp9AccordingToBuildFlags) {
+ std::unique_ptr<VideoEncoderFactory> factory =
+ CreateBuiltinVideoEncoderFactory();
+ bool claims_vp9_support = false;
+ for (const SdpVideoFormat& format : factory->GetSupportedFormats()) {
+ if (format.name == "VP9") {
+ claims_vp9_support = true;
+ break;
+ }
+ }
+#if defined(RTC_ENABLE_VP9)
+ EXPECT_TRUE(claims_vp9_support);
+#else
+ EXPECT_FALSE(claims_vp9_support);
+#endif // defined(RTC_ENABLE_VP9)
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/api/video_codecs/test/h264_profile_level_id_unittest.cc b/third_party/libwebrtc/api/video_codecs/test/h264_profile_level_id_unittest.cc
new file mode 100644
index 0000000000..47098d2682
--- /dev/null
+++ b/third_party/libwebrtc/api/video_codecs/test/h264_profile_level_id_unittest.cc
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2021 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 "api/video_codecs/h264_profile_level_id.h"
+
+#include <map>
+#include <string>
+
+#include "absl/types/optional.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(H264ProfileLevelId, TestParsingInvalid) {
+ // Malformed strings.
+ EXPECT_FALSE(ParseH264ProfileLevelId(""));
+ EXPECT_FALSE(ParseH264ProfileLevelId(" 42e01f"));
+ EXPECT_FALSE(ParseH264ProfileLevelId("4242e01f"));
+ EXPECT_FALSE(ParseH264ProfileLevelId("e01f"));
+ EXPECT_FALSE(ParseH264ProfileLevelId("gggggg"));
+
+ // Invalid level.
+ EXPECT_FALSE(ParseH264ProfileLevelId("42e000"));
+ EXPECT_FALSE(ParseH264ProfileLevelId("42e00f"));
+ EXPECT_FALSE(ParseH264ProfileLevelId("42e0ff"));
+
+ // Invalid profile.
+ EXPECT_FALSE(ParseH264ProfileLevelId("42e11f"));
+ EXPECT_FALSE(ParseH264ProfileLevelId("58601f"));
+ EXPECT_FALSE(ParseH264ProfileLevelId("64e01f"));
+}
+
+TEST(H264ProfileLevelId, TestParsingLevel) {
+ EXPECT_EQ(H264Level::kLevel3_1, ParseH264ProfileLevelId("42e01f")->level);
+ EXPECT_EQ(H264Level::kLevel1_1, ParseH264ProfileLevelId("42e00b")->level);
+ EXPECT_EQ(H264Level::kLevel1_b, ParseH264ProfileLevelId("42f00b")->level);
+ EXPECT_EQ(H264Level::kLevel4_2, ParseH264ProfileLevelId("42C02A")->level);
+ EXPECT_EQ(H264Level::kLevel5_2, ParseH264ProfileLevelId("640c34")->level);
+}
+
+TEST(H264ProfileLevelId, TestParsingConstrainedBaseline) {
+ EXPECT_EQ(H264Profile::kProfileConstrainedBaseline,
+ ParseH264ProfileLevelId("42e01f")->profile);
+ EXPECT_EQ(H264Profile::kProfileConstrainedBaseline,
+ ParseH264ProfileLevelId("42C02A")->profile);
+ EXPECT_EQ(H264Profile::kProfileConstrainedBaseline,
+ ParseH264ProfileLevelId("4de01f")->profile);
+ EXPECT_EQ(H264Profile::kProfileConstrainedBaseline,
+ ParseH264ProfileLevelId("58f01f")->profile);
+}
+
+TEST(H264ProfileLevelId, TestParsingBaseline) {
+ EXPECT_EQ(H264Profile::kProfileBaseline,
+ ParseH264ProfileLevelId("42a01f")->profile);
+ EXPECT_EQ(H264Profile::kProfileBaseline,
+ ParseH264ProfileLevelId("58A01F")->profile);
+}
+
+TEST(H264ProfileLevelId, TestParsingMain) {
+ EXPECT_EQ(H264Profile::kProfileMain,
+ ParseH264ProfileLevelId("4D401f")->profile);
+}
+
+TEST(H264ProfileLevelId, TestParsingHigh) {
+ EXPECT_EQ(H264Profile::kProfileHigh,
+ ParseH264ProfileLevelId("64001f")->profile);
+}
+
+TEST(H264ProfileLevelId, TestParsingConstrainedHigh) {
+ EXPECT_EQ(H264Profile::kProfileConstrainedHigh,
+ ParseH264ProfileLevelId("640c1f")->profile);
+}
+
+TEST(H264ProfileLevelId, TestSupportedLevel) {
+ EXPECT_EQ(H264Level::kLevel2_1, *H264SupportedLevel(640 * 480, 25));
+ EXPECT_EQ(H264Level::kLevel3_1, *H264SupportedLevel(1280 * 720, 30));
+ EXPECT_EQ(H264Level::kLevel4_2, *H264SupportedLevel(1920 * 1280, 60));
+}
+
+// Test supported level below level 1 requirements.
+TEST(H264ProfileLevelId, TestSupportedLevelInvalid) {
+ EXPECT_FALSE(H264SupportedLevel(0, 0));
+ // All levels support fps > 5.
+ EXPECT_FALSE(H264SupportedLevel(1280 * 720, 5));
+ // All levels support frame sizes > 183 * 137.
+ EXPECT_FALSE(H264SupportedLevel(183 * 137, 30));
+}
+
+TEST(H264ProfileLevelId, TestToString) {
+ EXPECT_EQ("42e01f", *H264ProfileLevelIdToString(H264ProfileLevelId(
+ H264Profile::kProfileConstrainedBaseline,
+ H264Level::kLevel3_1)));
+ EXPECT_EQ("42000a", *H264ProfileLevelIdToString(H264ProfileLevelId(
+ H264Profile::kProfileBaseline, H264Level::kLevel1)));
+ EXPECT_EQ("4d001f", H264ProfileLevelIdToString(H264ProfileLevelId(
+ H264Profile::kProfileMain, H264Level::kLevel3_1)));
+ EXPECT_EQ("640c2a",
+ *H264ProfileLevelIdToString(H264ProfileLevelId(
+ H264Profile::kProfileConstrainedHigh, H264Level::kLevel4_2)));
+ EXPECT_EQ("64002a", *H264ProfileLevelIdToString(H264ProfileLevelId(
+ H264Profile::kProfileHigh, H264Level::kLevel4_2)));
+}
+
+TEST(H264ProfileLevelId, TestToStringLevel1b) {
+ EXPECT_EQ("42f00b", *H264ProfileLevelIdToString(H264ProfileLevelId(
+ H264Profile::kProfileConstrainedBaseline,
+ H264Level::kLevel1_b)));
+ EXPECT_EQ("42100b",
+ *H264ProfileLevelIdToString(H264ProfileLevelId(
+ H264Profile::kProfileBaseline, H264Level::kLevel1_b)));
+ EXPECT_EQ("4d100b", *H264ProfileLevelIdToString(H264ProfileLevelId(
+ H264Profile::kProfileMain, H264Level::kLevel1_b)));
+}
+
+TEST(H264ProfileLevelId, TestToStringRoundTrip) {
+ EXPECT_EQ("42e01f",
+ *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("42e01f")));
+ EXPECT_EQ("42e01f",
+ *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("42E01F")));
+ EXPECT_EQ("4d100b",
+ *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("4d100b")));
+ EXPECT_EQ("4d100b",
+ *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("4D100B")));
+ EXPECT_EQ("640c2a",
+ *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("640c2a")));
+ EXPECT_EQ("640c2a",
+ *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("640C2A")));
+}
+
+TEST(H264ProfileLevelId, TestToStringInvalid) {
+ EXPECT_FALSE(H264ProfileLevelIdToString(
+ H264ProfileLevelId(H264Profile::kProfileHigh, H264Level::kLevel1_b)));
+ EXPECT_FALSE(H264ProfileLevelIdToString(H264ProfileLevelId(
+ H264Profile::kProfileConstrainedHigh, H264Level::kLevel1_b)));
+ EXPECT_FALSE(H264ProfileLevelIdToString(
+ H264ProfileLevelId(static_cast<H264Profile>(255), H264Level::kLevel3_1)));
+}
+
+TEST(H264ProfileLevelId, TestParseSdpProfileLevelIdEmpty) {
+ const absl::optional<H264ProfileLevelId> profile_level_id =
+ ParseSdpForH264ProfileLevelId(SdpVideoFormat::Parameters());
+ EXPECT_TRUE(profile_level_id);
+ EXPECT_EQ(H264Profile::kProfileConstrainedBaseline,
+ profile_level_id->profile);
+ EXPECT_EQ(H264Level::kLevel3_1, profile_level_id->level);
+}
+
+TEST(H264ProfileLevelId, TestParseSdpProfileLevelIdConstrainedHigh) {
+ SdpVideoFormat::Parameters params;
+ params["profile-level-id"] = "640c2a";
+ const absl::optional<H264ProfileLevelId> profile_level_id =
+ ParseSdpForH264ProfileLevelId(params);
+ EXPECT_TRUE(profile_level_id);
+ EXPECT_EQ(H264Profile::kProfileConstrainedHigh, profile_level_id->profile);
+ EXPECT_EQ(H264Level::kLevel4_2, profile_level_id->level);
+}
+
+TEST(H264ProfileLevelId, TestParseSdpProfileLevelIdInvalid) {
+ SdpVideoFormat::Parameters params;
+ params["profile-level-id"] = "foobar";
+ EXPECT_FALSE(ParseSdpForH264ProfileLevelId(params));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/api/video_codecs/test/h265_profile_tier_level_unittest.cc b/third_party/libwebrtc/api/video_codecs/test/h265_profile_tier_level_unittest.cc
new file mode 100644
index 0000000000..a9fdf966a5
--- /dev/null
+++ b/third_party/libwebrtc/api/video_codecs/test/h265_profile_tier_level_unittest.cc
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2023 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 "api/video_codecs/h265_profile_tier_level.h"
+
+#include <string>
+
+#include "absl/types/optional.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(H265ProfileTierLevel, TestLevelToString) {
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel1), "30");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel2), "60");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel2_1), "63");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel3), "90");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel3_1), "93");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel4), "120");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel4_1), "123");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel5), "150");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel5_1), "153");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel5_2), "156");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel6), "180");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel6_1), "183");
+ EXPECT_EQ(H265LevelToString(H265Level::kLevel6_2), "186");
+}
+
+TEST(H265ProfileTierLevel, TestProfileToString) {
+ EXPECT_EQ(H265ProfileToString(H265Profile::kProfileMain), "1");
+ EXPECT_EQ(H265ProfileToString(H265Profile::kProfileMain10), "2");
+ EXPECT_EQ(H265ProfileToString(H265Profile::kProfileMainStill), "3");
+ EXPECT_EQ(H265ProfileToString(H265Profile::kProfileRangeExtensions), "4");
+ EXPECT_EQ(H265ProfileToString(H265Profile::kProfileHighThroughput), "5");
+ EXPECT_EQ(H265ProfileToString(H265Profile::kProfileMultiviewMain), "6");
+ EXPECT_EQ(H265ProfileToString(H265Profile::kProfileScalableMain), "7");
+ EXPECT_EQ(H265ProfileToString(H265Profile::kProfile3dMain), "8");
+ EXPECT_EQ(H265ProfileToString(H265Profile::kProfileScreenContentCoding), "9");
+ EXPECT_EQ(H265ProfileToString(H265Profile::kProfileScalableRangeExtensions),
+ "10");
+ EXPECT_EQ(H265ProfileToString(
+ H265Profile::kProfileHighThroughputScreenContentCoding),
+ "11");
+}
+
+TEST(H265ProfileTierLevel, TestTierToString) {
+ EXPECT_EQ(H265TierToString(H265Tier::kTier0), "0");
+ EXPECT_EQ(H265TierToString(H265Tier::kTier1), "1");
+}
+
+TEST(H265ProfileTierLevel, TestStringToProfile) {
+ // Invalid profiles.
+ EXPECT_FALSE(StringToH265Profile("0"));
+ EXPECT_FALSE(StringToH265Profile("12"));
+
+ // Malformed profiles
+ EXPECT_FALSE(StringToH265Profile(""));
+ EXPECT_FALSE(StringToH265Profile(" 1"));
+ EXPECT_FALSE(StringToH265Profile("12x"));
+ EXPECT_FALSE(StringToH265Profile("x12"));
+ EXPECT_FALSE(StringToH265Profile("gggg"));
+
+ // Valid profiles.
+ EXPECT_EQ(StringToH265Profile("1"), H265Profile::kProfileMain);
+ EXPECT_EQ(StringToH265Profile("2"), H265Profile::kProfileMain10);
+ EXPECT_EQ(StringToH265Profile("4"), H265Profile::kProfileRangeExtensions);
+}
+
+TEST(H265ProfileTierLevel, TestStringToLevel) {
+ // Invalid levels.
+ EXPECT_FALSE(StringToH265Level("0"));
+ EXPECT_FALSE(StringToH265Level("200"));
+
+ // Malformed levels.
+ EXPECT_FALSE(StringToH265Level(""));
+ EXPECT_FALSE(StringToH265Level(" 30"));
+ EXPECT_FALSE(StringToH265Level("30x"));
+ EXPECT_FALSE(StringToH265Level("x30"));
+ EXPECT_FALSE(StringToH265Level("ggggg"));
+
+ // Valid levels.
+ EXPECT_EQ(StringToH265Level("30"), H265Level::kLevel1);
+ EXPECT_EQ(StringToH265Level("93"), H265Level::kLevel3_1);
+ EXPECT_EQ(StringToH265Level("183"), H265Level::kLevel6_1);
+}
+
+TEST(H265ProfileTierLevel, TestStringToTier) {
+ // Invalid tiers.
+ EXPECT_FALSE(StringToH265Tier("4"));
+ EXPECT_FALSE(StringToH265Tier("-1"));
+
+ // Malformed tiers.
+ EXPECT_FALSE(StringToH265Tier(""));
+ EXPECT_FALSE(StringToH265Tier(" 1"));
+ EXPECT_FALSE(StringToH265Tier("t1"));
+
+ // Valid tiers.
+ EXPECT_EQ(StringToH265Tier("0"), H265Tier::kTier0);
+ EXPECT_EQ(StringToH265Tier("1"), H265Tier::kTier1);
+}
+
+TEST(H265ProfileTierLevel, TestParseSdpProfileTierLevelAllEmpty) {
+ const absl::optional<H265ProfileTierLevel> profile_tier_level =
+ ParseSdpForH265ProfileTierLevel(SdpVideoFormat::Parameters());
+ EXPECT_TRUE(profile_tier_level);
+ EXPECT_EQ(H265Profile::kProfileMain, profile_tier_level->profile);
+ EXPECT_EQ(H265Level::kLevel3_1, profile_tier_level->level);
+ EXPECT_EQ(H265Tier::kTier0, profile_tier_level->tier);
+}
+
+TEST(H265ProfileTierLevel, TestParseSdpProfileTierLevelPartialEmpty) {
+ SdpVideoFormat::Parameters params;
+ params["profile-id"] = "1";
+ params["tier-flag"] = "0";
+ absl::optional<H265ProfileTierLevel> profile_tier_level =
+ ParseSdpForH265ProfileTierLevel(params);
+ EXPECT_TRUE(profile_tier_level);
+ EXPECT_EQ(H265Profile::kProfileMain, profile_tier_level->profile);
+ EXPECT_EQ(H265Level::kLevel3_1, profile_tier_level->level);
+ EXPECT_EQ(H265Tier::kTier0, profile_tier_level->tier);
+
+ params.clear();
+ params["profile-id"] = "2";
+ profile_tier_level = ParseSdpForH265ProfileTierLevel(params);
+ EXPECT_TRUE(profile_tier_level);
+ EXPECT_EQ(H265Profile::kProfileMain10, profile_tier_level->profile);
+ EXPECT_EQ(H265Level::kLevel3_1, profile_tier_level->level);
+ EXPECT_EQ(H265Tier::kTier0, profile_tier_level->tier);
+
+ params.clear();
+ params["level-id"] = "180";
+ profile_tier_level = ParseSdpForH265ProfileTierLevel(params);
+ EXPECT_TRUE(profile_tier_level);
+ EXPECT_EQ(H265Profile::kProfileMain, profile_tier_level->profile);
+ EXPECT_EQ(H265Level::kLevel6, profile_tier_level->level);
+ EXPECT_EQ(H265Tier::kTier0, profile_tier_level->tier);
+}
+
+TEST(H265ProfileTierLevel, TestParseSdpProfileTierLevelInvalid) {
+ SdpVideoFormat::Parameters params;
+
+ // Invalid profile-tier-level combination.
+ params["profile-id"] = "1";
+ params["tier-flag"] = "1";
+ params["level-id"] = "93";
+ absl::optional<H265ProfileTierLevel> profile_tier_level =
+ ParseSdpForH265ProfileTierLevel(params);
+ EXPECT_FALSE(profile_tier_level);
+ params.clear();
+ params["profile-id"] = "1";
+ params["tier-flag"] = "4";
+ params["level-id"] = "180";
+ profile_tier_level = ParseSdpForH265ProfileTierLevel(params);
+ EXPECT_FALSE(profile_tier_level);
+
+ // Valid profile-tier-level combination.
+ params.clear();
+ params["profile-id"] = "1";
+ params["tier-flag"] = "0";
+ params["level-id"] = "153";
+ profile_tier_level = ParseSdpForH265ProfileTierLevel(params);
+ EXPECT_TRUE(profile_tier_level);
+}
+
+TEST(H265ProfileTierLevel, TestToStringRoundTrip) {
+ SdpVideoFormat::Parameters params;
+ params["profile-id"] = "1";
+ params["tier-flag"] = "0";
+ params["level-id"] = "93";
+ absl::optional<H265ProfileTierLevel> profile_tier_level =
+ ParseSdpForH265ProfileTierLevel(params);
+ EXPECT_TRUE(profile_tier_level);
+ EXPECT_EQ("1", H265ProfileToString(profile_tier_level->profile));
+ EXPECT_EQ("0", H265TierToString(profile_tier_level->tier));
+ EXPECT_EQ("93", H265LevelToString(profile_tier_level->level));
+
+ params.clear();
+ params["profile-id"] = "2";
+ params["tier-flag"] = "1";
+ params["level-id"] = "180";
+ profile_tier_level = ParseSdpForH265ProfileTierLevel(params);
+ EXPECT_TRUE(profile_tier_level);
+ EXPECT_EQ("2", H265ProfileToString(profile_tier_level->profile));
+ EXPECT_EQ("1", H265TierToString(profile_tier_level->tier));
+ EXPECT_EQ("180", H265LevelToString(profile_tier_level->level));
+}
+
+TEST(H265ProfileTierLevel, TestProfileTierLevelCompare) {
+ SdpVideoFormat::Parameters params1;
+ SdpVideoFormat::Parameters params2;
+
+ // None of profile-id/tier-flag/level-id is specified,
+ EXPECT_TRUE(H265IsSameProfileTierLevel(params1, params2));
+
+ // Same non-empty PTL
+ params1["profile-id"] = "1";
+ params1["tier-flag"] = "0";
+ params1["level-id"] = "120";
+ params2["profile-id"] = "1";
+ params2["tier-flag"] = "0";
+ params2["level-id"] = "120";
+ EXPECT_TRUE(H265IsSameProfileTierLevel(params1, params2));
+
+ // Different profiles.
+ params1.clear();
+ params2.clear();
+ params1["profile-id"] = "1";
+ params2["profile-id"] = "2";
+ EXPECT_FALSE(H265IsSameProfileTierLevel(params1, params2));
+
+ // Different levels.
+ params1.clear();
+ params2.clear();
+ params1["profile-id"] = "1";
+ params2["profile-id"] = "1";
+ params1["level-id"] = "93";
+ params2["level-id"] = "183";
+ EXPECT_FALSE(H265IsSameProfileTierLevel(params1, params2));
+
+ // Different tiers.
+ params1.clear();
+ params2.clear();
+ params1["profile-id"] = "1";
+ params2["profile-id"] = "1";
+ params1["level-id"] = "93";
+ params2["level-id"] = "93";
+ params1["tier-flag"] = "0";
+ params2["tier-flag"] = "1";
+ EXPECT_FALSE(H265IsSameProfileTierLevel(params1, params2));
+
+ // One of the SdpVideoFormat::Parameters is invalid.
+ params1.clear();
+ params2.clear();
+ params1["profile-id"] = "1";
+ params2["profile-id"] = "1";
+ params1["tier-flag"] = "0";
+ params2["tier-flag"] = "4";
+ EXPECT_FALSE(H265IsSameProfileTierLevel(params1, params2));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/api/video_codecs/test/sdp_video_format_unittest.cc b/third_party/libwebrtc/api/video_codecs/test/sdp_video_format_unittest.cc
new file mode 100644
index 0000000000..797a9a2e72
--- /dev/null
+++ b/third_party/libwebrtc/api/video_codecs/test/sdp_video_format_unittest.cc
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2021 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 "api/video_codecs/sdp_video_format.h"
+
+#include <stdint.h>
+
+#include "media/base/media_constants.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+typedef SdpVideoFormat Sdp;
+typedef SdpVideoFormat::Parameters Params;
+
+TEST(SdpVideoFormatTest, SameCodecNameNoParameters) {
+ EXPECT_TRUE(Sdp("H264").IsSameCodec(Sdp("h264")));
+ EXPECT_TRUE(Sdp("VP8").IsSameCodec(Sdp("vp8")));
+ EXPECT_TRUE(Sdp("VP9").IsSameCodec(Sdp("vp9")));
+ EXPECT_TRUE(Sdp("AV1").IsSameCodec(Sdp("Av1")));
+#ifdef RTC_ENABLE_H265
+ EXPECT_TRUE(Sdp("H265").IsSameCodec(Sdp("h265")));
+#endif
+}
+
+TEST(SdpVideoFormatTest, DifferentCodecNameNoParameters) {
+ EXPECT_FALSE(Sdp("H264").IsSameCodec(Sdp("VP8")));
+ EXPECT_FALSE(Sdp("VP8").IsSameCodec(Sdp("VP9")));
+ EXPECT_FALSE(Sdp("AV1").IsSameCodec(Sdp("VP8")));
+#ifdef RTC_ENABLE_H265
+ EXPECT_FALSE(Sdp("H265").IsSameCodec(Sdp("VP8")));
+#endif
+}
+
+TEST(SdpVideoFormatTest, SameCodecNameSameParameters) {
+ EXPECT_TRUE(Sdp("VP9").IsSameCodec(Sdp("VP9", Params{{"profile-id", "0"}})));
+ EXPECT_TRUE(Sdp("VP9", Params{{"profile-id", "0"}})
+ .IsSameCodec(Sdp("VP9", Params{{"profile-id", "0"}})));
+ EXPECT_TRUE(Sdp("VP9", Params{{"profile-id", "2"}})
+ .IsSameCodec(Sdp("VP9", Params{{"profile-id", "2"}})));
+ EXPECT_TRUE(
+ Sdp("H264", Params{{"profile-level-id", "42e01f"}})
+ .IsSameCodec(Sdp("H264", Params{{"profile-level-id", "42e01f"}})));
+ EXPECT_TRUE(
+ Sdp("H264", Params{{"profile-level-id", "640c34"}})
+ .IsSameCodec(Sdp("H264", Params{{"profile-level-id", "640c34"}})));
+ EXPECT_TRUE(Sdp("AV1").IsSameCodec(Sdp("AV1", Params{{"profile", "0"}})));
+ EXPECT_TRUE(Sdp("AV1", Params{{"profile", "0"}})
+ .IsSameCodec(Sdp("AV1", Params{{"profile", "0"}})));
+ EXPECT_TRUE(Sdp("AV1", Params{{"profile", "2"}})
+ .IsSameCodec(Sdp("AV1", Params{{"profile", "2"}})));
+#ifdef RTC_ENABLE_H265
+ EXPECT_TRUE(Sdp("H265").IsSameCodec(Sdp(
+ "H265",
+ Params{{"profile-id", "1"}, {"tier-flag", "0"}, {"level-id", "93"}})));
+ EXPECT_TRUE(
+ Sdp("H265",
+ Params{{"profile-id", "2"}, {"tier-flag", "0"}, {"level-id", "93"}})
+ .IsSameCodec(Sdp("H265", Params{{"profile-id", "2"},
+ {"tier-flag", "0"},
+ {"level-id", "93"}})));
+#endif
+}
+
+TEST(SdpVideoFormatTest, SameCodecNameDifferentParameters) {
+ EXPECT_FALSE(Sdp("VP9").IsSameCodec(Sdp("VP9", Params{{"profile-id", "2"}})));
+ EXPECT_FALSE(Sdp("VP9", Params{{"profile-id", "0"}})
+ .IsSameCodec(Sdp("VP9", Params{{"profile-id", "1"}})));
+ EXPECT_FALSE(Sdp("VP9", Params{{"profile-id", "2"}})
+ .IsSameCodec(Sdp("VP9", Params{{"profile-id", "0"}})));
+ EXPECT_FALSE(
+ Sdp("H264", Params{{"profile-level-id", "42e01f"}})
+ .IsSameCodec(Sdp("H264", Params{{"profile-level-id", "640c34"}})));
+ EXPECT_FALSE(
+ Sdp("H264", Params{{"profile-level-id", "640c34"}})
+ .IsSameCodec(Sdp("H264", Params{{"profile-level-id", "42f00b"}})));
+ EXPECT_FALSE(Sdp("AV1").IsSameCodec(Sdp("AV1", Params{{"profile", "1"}})));
+ EXPECT_FALSE(Sdp("AV1", Params{{"profile", "0"}})
+ .IsSameCodec(Sdp("AV1", Params{{"profile", "1"}})));
+ EXPECT_FALSE(Sdp("AV1", Params{{"profile", "1"}})
+ .IsSameCodec(Sdp("AV1", Params{{"profile", "2"}})));
+#ifdef RTC_ENABLE_H265
+ EXPECT_FALSE(Sdp("H265").IsSameCodec(Sdp(
+ "H265",
+ Params{{"profile-id", "0"}, {"tier-flag", "0"}, {"level-id", "93"}})));
+ EXPECT_FALSE(Sdp("H265").IsSameCodec(Sdp(
+ "H265",
+ Params{{"profile-id", "1"}, {"tier-flag", "1"}, {"level-id", "93"}})));
+ EXPECT_FALSE(Sdp("H265").IsSameCodec(Sdp(
+ "H265",
+ Params{{"profile-id", "1"}, {"tier-flag", "0"}, {"level-id", "90"}})));
+ EXPECT_FALSE(
+ Sdp("H265",
+ Params{{"profile-id", "2"}, {"tier-flag", "0"}, {"level-id", "93"}})
+ .IsSameCodec(Sdp("H265", Params{{"profile-id", "1"},
+ {"tier-flag", "0"},
+ {"level-id", "93"}})));
+ EXPECT_FALSE(
+ Sdp("H265",
+ Params{{"profile-id", "1"}, {"tier-flag", "1"}, {"level-id", "120"}})
+ .IsSameCodec(Sdp("H265", Params{{"profile-id", "1"},
+ {"tier-flag", "0"},
+ {"level-id", "120"}})));
+ EXPECT_FALSE(
+ Sdp("H265",
+ Params{{"profile-id", "1"}, {"tier-flag", "0"}, {"level-id", "93"}})
+ .IsSameCodec(Sdp("H265", Params{{"profile-id", "1"},
+ {"tier-flag", "0"},
+ {"level-id", "90"}})));
+#endif
+}
+
+TEST(SdpVideoFormatTest, DifferentCodecNameSameParameters) {
+ EXPECT_FALSE(Sdp("VP9", Params{{"profile-id", "0"}})
+ .IsSameCodec(Sdp("H264", Params{{"profile-id", "0"}})));
+ EXPECT_FALSE(Sdp("VP9", Params{{"profile-id", "2"}})
+ .IsSameCodec(Sdp("VP8", Params{{"profile-id", "2"}})));
+ EXPECT_FALSE(
+ Sdp("H264", Params{{"profile-level-id", "42e01f"}})
+ .IsSameCodec(Sdp("VP9", Params{{"profile-level-id", "42e01f"}})));
+ EXPECT_FALSE(
+ Sdp("H264", Params{{"profile-level-id", "640c34"}})
+ .IsSameCodec(Sdp("VP8", Params{{"profile-level-id", "640c34"}})));
+ EXPECT_FALSE(Sdp("AV1", Params{{"profile", "0"}})
+ .IsSameCodec(Sdp("H264", Params{{"profile", "0"}})));
+ EXPECT_FALSE(Sdp("AV1", Params{{"profile", "2"}})
+ .IsSameCodec(Sdp("VP9", Params{{"profile", "2"}})));
+#ifdef RTC_ENABLE_H265
+ EXPECT_FALSE(Sdp("H265", Params{{"profile-id", "0"}})
+ .IsSameCodec(Sdp("H264", Params{{"profile-id", "0"}})));
+ EXPECT_FALSE(Sdp("H265", Params{{"profile-id", "2"}})
+ .IsSameCodec(Sdp("VP9", Params{{"profile-id", "2"}})));
+#endif
+}
+
+TEST(SdpVideoFormatTest, H264PacketizationMode) {
+ // The default packetization mode is 0.
+ EXPECT_TRUE(Sdp("H264", Params{{cricket::kH264FmtpPacketizationMode, "0"}})
+ .IsSameCodec(Sdp("H264")));
+ EXPECT_FALSE(Sdp("H264", Params{{cricket::kH264FmtpPacketizationMode, "1"}})
+ .IsSameCodec(Sdp("H264")));
+
+ EXPECT_TRUE(
+ Sdp("H264", Params{{cricket::kH264FmtpPacketizationMode, "1"}})
+ .IsSameCodec(
+ Sdp("H264", Params{{cricket::kH264FmtpPacketizationMode, "1"}})));
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/api/video_codecs/test/video_decoder_factory_template_tests.cc b/third_party/libwebrtc/api/video_codecs/test/video_decoder_factory_template_tests.cc
new file mode 100644
index 0000000000..1cc2b58274
--- /dev/null
+++ b/third_party/libwebrtc/api/video_codecs/test/video_decoder_factory_template_tests.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2022 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 "api/test/mock_video_decoder.h"
+#include "api/video_codecs/video_decoder_factory_template.h"
+#include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h"
+#include "api/video_codecs/video_decoder_factory_template_libvpx_vp8_adapter.h"
+#include "api/video_codecs/video_decoder_factory_template_libvpx_vp9_adapter.h"
+#include "api/video_codecs/video_decoder_factory_template_open_h264_adapter.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+using ::testing::Contains;
+using ::testing::Each;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::Ne;
+using ::testing::Not;
+using ::testing::UnorderedElementsAre;
+
+namespace webrtc {
+namespace {
+const SdpVideoFormat kFooSdp("Foo");
+const SdpVideoFormat kBarLowSdp("Bar", {{"profile", "low"}});
+const SdpVideoFormat kBarHighSdp("Bar", {{"profile", "high"}});
+
+struct FooDecoderTemplateAdapter {
+ static std::vector<SdpVideoFormat> SupportedFormats() { return {kFooSdp}; }
+
+ static std::unique_ptr<VideoDecoder> CreateDecoder(
+ const SdpVideoFormat& format) {
+ auto decoder = std::make_unique<testing::StrictMock<MockVideoDecoder>>();
+ EXPECT_CALL(*decoder, Destruct);
+ return decoder;
+ }
+};
+
+struct BarDecoderTemplateAdapter {
+ static std::vector<SdpVideoFormat> SupportedFormats() {
+ return {kBarLowSdp, kBarHighSdp};
+ }
+
+ static std::unique_ptr<VideoDecoder> CreateDecoder(
+ const SdpVideoFormat& format) {
+ auto decoder = std::make_unique<testing::StrictMock<MockVideoDecoder>>();
+ EXPECT_CALL(*decoder, Destruct);
+ return decoder;
+ }
+};
+
+TEST(VideoDecoderFactoryTemplate, OneTemplateAdapterCreateDecoder) {
+ VideoDecoderFactoryTemplate<FooDecoderTemplateAdapter> factory;
+ EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kFooSdp));
+ EXPECT_THAT(factory.CreateVideoDecoder(kFooSdp), Ne(nullptr));
+ EXPECT_THAT(factory.CreateVideoDecoder(SdpVideoFormat("FooX")), Eq(nullptr));
+}
+
+TEST(VideoDecoderFactoryTemplate, TwoTemplateAdaptersNoDuplicates) {
+ VideoDecoderFactoryTemplate<FooDecoderTemplateAdapter,
+ FooDecoderTemplateAdapter>
+ factory;
+ EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kFooSdp));
+}
+
+TEST(VideoDecoderFactoryTemplate, TwoTemplateAdaptersCreateDecoders) {
+ VideoDecoderFactoryTemplate<FooDecoderTemplateAdapter,
+ BarDecoderTemplateAdapter>
+ factory;
+ EXPECT_THAT(factory.GetSupportedFormats(),
+ UnorderedElementsAre(kFooSdp, kBarLowSdp, kBarHighSdp));
+ EXPECT_THAT(factory.CreateVideoDecoder(kFooSdp), Ne(nullptr));
+ EXPECT_THAT(factory.CreateVideoDecoder(kBarLowSdp), Ne(nullptr));
+ EXPECT_THAT(factory.CreateVideoDecoder(kBarHighSdp), Ne(nullptr));
+ EXPECT_THAT(factory.CreateVideoDecoder(SdpVideoFormat("FooX")), Eq(nullptr));
+ EXPECT_THAT(factory.CreateVideoDecoder(SdpVideoFormat("Bar")), Eq(nullptr));
+}
+
+TEST(VideoDecoderFactoryTemplate, LibvpxVp8) {
+ VideoDecoderFactoryTemplate<LibvpxVp8DecoderTemplateAdapter> factory;
+ auto formats = factory.GetSupportedFormats();
+ EXPECT_THAT(formats.size(), 1);
+ EXPECT_THAT(formats[0], Field(&SdpVideoFormat::name, "VP8"));
+ EXPECT_THAT(factory.CreateVideoDecoder(formats[0]), Ne(nullptr));
+}
+
+TEST(VideoDecoderFactoryTemplate, LibvpxVp9) {
+ VideoDecoderFactoryTemplate<LibvpxVp9DecoderTemplateAdapter> factory;
+ auto formats = factory.GetSupportedFormats();
+ EXPECT_THAT(formats, Not(IsEmpty()));
+ EXPECT_THAT(formats, Each(Field(&SdpVideoFormat::name, "VP9")));
+ EXPECT_THAT(factory.CreateVideoDecoder(formats[0]), Ne(nullptr));
+}
+
+// TODO(bugs.webrtc.org/13573): When OpenH264 is no longer a conditional build
+// target remove this #ifdef.
+#if defined(WEBRTC_USE_H264)
+TEST(VideoDecoderFactoryTemplate, OpenH264) {
+ VideoDecoderFactoryTemplate<OpenH264DecoderTemplateAdapter> factory;
+ auto formats = factory.GetSupportedFormats();
+ EXPECT_THAT(formats, Not(IsEmpty()));
+ EXPECT_THAT(formats, Each(Field(&SdpVideoFormat::name, "H264")));
+ EXPECT_THAT(factory.CreateVideoDecoder(formats[0]), Ne(nullptr));
+}
+#endif // defined(WEBRTC_USE_H264)
+
+TEST(VideoDecoderFactoryTemplate, Dav1d) {
+ VideoDecoderFactoryTemplate<Dav1dDecoderTemplateAdapter> factory;
+ auto formats = factory.GetSupportedFormats();
+ EXPECT_THAT(formats, Not(IsEmpty()));
+ EXPECT_THAT(formats, Each(Field(&SdpVideoFormat::name, "AV1")));
+ EXPECT_THAT(factory.CreateVideoDecoder(formats[0]), Ne(nullptr));
+}
+
+} // namespace
+} // namespace webrtc
diff --git a/third_party/libwebrtc/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc b/third_party/libwebrtc/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc
new file mode 100644
index 0000000000..97be6250db
--- /dev/null
+++ b/third_party/libwebrtc/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2015 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 "api/video_codecs/video_decoder_software_fallback_wrapper.h"
+
+#include <stdint.h>
+
+#include "absl/types/optional.h"
+#include "api/video/encoded_image.h"
+#include "api/video/video_frame.h"
+#include "api/video_codecs/video_decoder.h"
+#include "modules/video_coding/codecs/vp8/include/vp8.h"
+#include "modules/video_coding/include/video_codec_interface.h"
+#include "modules/video_coding/include/video_error_codes.h"
+#include "rtc_base/checks.h"
+#include "test/field_trial.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+class VideoDecoderSoftwareFallbackWrapperTest : public ::testing::Test {
+ protected:
+ VideoDecoderSoftwareFallbackWrapperTest()
+ : VideoDecoderSoftwareFallbackWrapperTest("") {}
+ explicit VideoDecoderSoftwareFallbackWrapperTest(
+ const std::string& field_trials)
+ : override_field_trials_(field_trials),
+ fake_decoder_(new CountingFakeDecoder()),
+ fallback_wrapper_(CreateVideoDecoderSoftwareFallbackWrapper(
+ std::unique_ptr<VideoDecoder>(VP8Decoder::Create()),
+ std::unique_ptr<VideoDecoder>(fake_decoder_))) {}
+
+ class CountingFakeDecoder : public VideoDecoder {
+ public:
+ bool Configure(const Settings& settings) override {
+ ++configure_count_;
+ return configure_return_value_;
+ }
+
+ int32_t Decode(const EncodedImage& input_image,
+ int64_t render_time_ms) override {
+ ++decode_count_;
+ return decode_return_code_;
+ }
+
+ int32_t RegisterDecodeCompleteCallback(
+ DecodedImageCallback* callback) override {
+ decode_complete_callback_ = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+ }
+
+ int32_t Release() override {
+ ++release_count_;
+ return WEBRTC_VIDEO_CODEC_OK;
+ }
+
+ const char* ImplementationName() const override { return "fake-decoder"; }
+
+ int configure_count_ = 0;
+ int decode_count_ = 0;
+ bool configure_return_value_ = true;
+ int32_t decode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
+ DecodedImageCallback* decode_complete_callback_ = nullptr;
+ int release_count_ = 0;
+ int reset_count_ = 0;
+ };
+ test::ScopedFieldTrials override_field_trials_;
+ // `fake_decoder_` is owned and released by `fallback_wrapper_`.
+ CountingFakeDecoder* fake_decoder_;
+ std::unique_ptr<VideoDecoder> fallback_wrapper_;
+};
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest, InitializesDecoder) {
+ fallback_wrapper_->Configure({});
+ EXPECT_EQ(1, fake_decoder_->configure_count_);
+
+ EncodedImage encoded_image;
+ encoded_image._frameType = VideoFrameType::kVideoFrameKey;
+ fallback_wrapper_->Decode(encoded_image, -1);
+ EXPECT_EQ(1, fake_decoder_->configure_count_)
+ << "Initialized decoder should not be reinitialized.";
+ EXPECT_EQ(1, fake_decoder_->decode_count_);
+}
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
+ UsesFallbackDecoderAfterAnyInitDecodeFailure) {
+ fake_decoder_->configure_return_value_ = false;
+ fallback_wrapper_->Configure({});
+ EXPECT_EQ(1, fake_decoder_->configure_count_);
+
+ EncodedImage encoded_image;
+ encoded_image._frameType = VideoFrameType::kVideoFrameKey;
+ fallback_wrapper_->Decode(encoded_image, -1);
+ EXPECT_EQ(1, fake_decoder_->configure_count_)
+ << "Should not have attempted reinitializing the fallback decoder on "
+ "keyframe.";
+ // Unfortunately faking a VP8 frame is hard. Rely on no Decode -> using SW
+ // decoder.
+ EXPECT_EQ(0, fake_decoder_->decode_count_)
+ << "Decoder used even though no InitDecode had succeeded.";
+}
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest, IsSoftwareFallbackSticky) {
+ fallback_wrapper_->Configure({});
+
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
+ EncodedImage encoded_image;
+ fallback_wrapper_->Decode(encoded_image, -1);
+ EXPECT_EQ(1, fake_decoder_->decode_count_);
+
+ // Software fallback should be sticky, fake_decoder_ shouldn't be used.
+ encoded_image._frameType = VideoFrameType::kVideoFrameKey;
+ fallback_wrapper_->Decode(encoded_image, -1);
+ EXPECT_EQ(1, fake_decoder_->decode_count_)
+ << "Decoder shouldn't be used after failure.";
+
+ // fake_decoder_ should have only been initialized once during the test.
+ EXPECT_EQ(1, fake_decoder_->configure_count_);
+}
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest, DoesNotFallbackOnEveryError) {
+ fallback_wrapper_->Configure({});
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
+ EncodedImage encoded_image;
+ EXPECT_EQ(fake_decoder_->decode_return_code_,
+ fallback_wrapper_->Decode(encoded_image, -1));
+ EXPECT_EQ(1, fake_decoder_->decode_count_);
+
+ fallback_wrapper_->Decode(encoded_image, -1);
+ EXPECT_EQ(2, fake_decoder_->decode_count_)
+ << "Decoder should be active even though previous decode failed.";
+}
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest, UsesHwDecoderAfterReinit) {
+ fallback_wrapper_->Configure({});
+
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
+ EncodedImage encoded_image;
+ fallback_wrapper_->Decode(encoded_image, -1);
+ EXPECT_EQ(1, fake_decoder_->decode_count_);
+
+ fallback_wrapper_->Release();
+ fallback_wrapper_->Configure({});
+
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
+ fallback_wrapper_->Decode(encoded_image, -1);
+ EXPECT_EQ(2, fake_decoder_->decode_count_)
+ << "Should not be using fallback after reinit.";
+}
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest, ForwardsReleaseCall) {
+ fallback_wrapper_->Configure({});
+ fallback_wrapper_->Release();
+ EXPECT_EQ(1, fake_decoder_->release_count_);
+
+ fallback_wrapper_->Configure({});
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
+ EncodedImage encoded_image;
+ fallback_wrapper_->Decode(encoded_image, -1);
+ EXPECT_EQ(2, fake_decoder_->release_count_)
+ << "Decoder should be released during fallback.";
+ fallback_wrapper_->Release();
+ EXPECT_EQ(2, fake_decoder_->release_count_);
+}
+
+// TODO(pbos): Fake a VP8 frame well enough to actually receive a callback from
+// the software decoder.
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
+ ForwardsRegisterDecodeCompleteCallback) {
+ class FakeDecodedImageCallback : public DecodedImageCallback {
+ int32_t Decoded(VideoFrame& decodedImage) override { return 0; }
+ int32_t Decoded(webrtc::VideoFrame& decodedImage,
+ int64_t decode_time_ms) override {
+ RTC_DCHECK_NOTREACHED();
+ return -1;
+ }
+ void Decoded(webrtc::VideoFrame& decodedImage,
+ absl::optional<int32_t> decode_time_ms,
+ absl::optional<uint8_t> qp) override {
+ RTC_DCHECK_NOTREACHED();
+ }
+ } callback;
+
+ fallback_wrapper_->Configure({});
+ fallback_wrapper_->RegisterDecodeCompleteCallback(&callback);
+ EXPECT_EQ(&callback, fake_decoder_->decode_complete_callback_);
+}
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
+ ReportsFallbackImplementationName) {
+ fallback_wrapper_->Configure({});
+
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
+ EncodedImage encoded_image;
+ fallback_wrapper_->Decode(encoded_image, -1);
+ // Hard coded expected value since libvpx is the software implementation name
+ // for VP8. Change accordingly if the underlying implementation does.
+ EXPECT_STREQ("libvpx (fallback from: fake-decoder)",
+ fallback_wrapper_->ImplementationName());
+ fallback_wrapper_->Release();
+}
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest, FallbacksOnTooManyErrors) {
+ fallback_wrapper_->Configure({});
+
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
+ EncodedImage encoded_image;
+ encoded_image._frameType = VideoFrameType::kVideoFrameKey;
+ // Doesn't fallback from a single error.
+ fallback_wrapper_->Decode(encoded_image, -1);
+ EXPECT_STREQ("fake-decoder", fallback_wrapper_->ImplementationName());
+
+ // However, many frames with the same error, fallback should happen.
+ const int kNumFramesToEncode = 10;
+ for (int i = 0; i < kNumFramesToEncode; ++i) {
+ fallback_wrapper_->Decode(encoded_image, -1);
+ }
+ // Hard coded expected value since libvpx is the software implementation name
+ // for VP8. Change accordingly if the underlying implementation does.
+ EXPECT_STREQ("libvpx (fallback from: fake-decoder)",
+ fallback_wrapper_->ImplementationName());
+ fallback_wrapper_->Release();
+}
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
+ DoesNotFallbackOnDeltaFramesErrors) {
+ fallback_wrapper_->Configure({});
+
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
+ EncodedImage encoded_image;
+ encoded_image._frameType = VideoFrameType::kVideoFrameDelta;
+
+ // Many decoded frames with the same error
+ const int kNumFramesToEncode = 10;
+ for (int i = 0; i < kNumFramesToEncode; ++i) {
+ fallback_wrapper_->Decode(encoded_image, -1);
+ }
+ EXPECT_STREQ("fake-decoder", fallback_wrapper_->ImplementationName());
+
+ fallback_wrapper_->Release();
+}
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
+ DoesNotFallbacksOnNonConsequtiveErrors) {
+ fallback_wrapper_->Configure({});
+
+ EncodedImage encoded_image;
+ encoded_image._frameType = VideoFrameType::kVideoFrameKey;
+
+ const int kNumFramesToEncode = 10;
+ for (int i = 0; i < kNumFramesToEncode; ++i) {
+ // Interleaved errors and successful decodes.
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
+ fallback_wrapper_->Decode(encoded_image, -1);
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
+ fallback_wrapper_->Decode(encoded_image, -1);
+ }
+ EXPECT_STREQ("fake-decoder", fallback_wrapper_->ImplementationName());
+ fallback_wrapper_->Release();
+}
+
+class ForcedSoftwareDecoderFallbackTest
+ : public VideoDecoderSoftwareFallbackWrapperTest {
+ public:
+ ForcedSoftwareDecoderFallbackTest()
+ : VideoDecoderSoftwareFallbackWrapperTest(
+ "WebRTC-Video-ForcedSwDecoderFallback/Enabled/") {
+ fake_decoder_ = new CountingFakeDecoder();
+ sw_fallback_decoder_ = new CountingFakeDecoder();
+ fallback_wrapper_ = CreateVideoDecoderSoftwareFallbackWrapper(
+ std::unique_ptr<VideoDecoder>(sw_fallback_decoder_),
+ std::unique_ptr<VideoDecoder>(fake_decoder_));
+ }
+
+ CountingFakeDecoder* sw_fallback_decoder_;
+};
+
+TEST_F(ForcedSoftwareDecoderFallbackTest, UsesForcedFallback) {
+ fallback_wrapper_->Configure({});
+ EXPECT_EQ(1, sw_fallback_decoder_->configure_count_);
+
+ EncodedImage encoded_image;
+ encoded_image._frameType = VideoFrameType::kVideoFrameKey;
+ fallback_wrapper_->Decode(encoded_image, -1);
+ EXPECT_EQ(1, sw_fallback_decoder_->configure_count_);
+ EXPECT_EQ(1, sw_fallback_decoder_->decode_count_);
+
+ fallback_wrapper_->Release();
+ EXPECT_EQ(1, sw_fallback_decoder_->release_count_);
+
+ // Only fallback decoder should have been used.
+ EXPECT_EQ(0, fake_decoder_->configure_count_);
+ EXPECT_EQ(0, fake_decoder_->decode_count_);
+ EXPECT_EQ(0, fake_decoder_->release_count_);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/api/video_codecs/test/video_encoder_factory_template_tests.cc b/third_party/libwebrtc/api/video_codecs/test/video_encoder_factory_template_tests.cc
new file mode 100644
index 0000000000..91b02aa905
--- /dev/null
+++ b/third_party/libwebrtc/api/video_codecs/test/video_encoder_factory_template_tests.cc
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2022 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 "api/test/mock_video_encoder.h"
+#include "api/video_codecs/video_encoder_factory_template.h"
+#include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h"
+#include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h"
+#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h"
+#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+using ::testing::Contains;
+using ::testing::Each;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::IsNull;
+using ::testing::Not;
+using ::testing::NotNull;
+using ::testing::UnorderedElementsAre;
+
+namespace webrtc {
+namespace {
+using CodecSupport = VideoEncoderFactory::CodecSupport;
+const SdpVideoFormat kFooSdp("Foo");
+const SdpVideoFormat kBarLowSdp("Bar", {{"profile", "low"}});
+const SdpVideoFormat kBarHighSdp("Bar", {{"profile", "high"}});
+
+struct FooEncoderTemplateAdapter {
+ static std::vector<SdpVideoFormat> SupportedFormats() { return {kFooSdp}; }
+
+ static std::unique_ptr<VideoEncoder> CreateEncoder(
+ const SdpVideoFormat& format) {
+ return std::make_unique<testing::StrictMock<MockVideoEncoder>>();
+ }
+
+ static bool IsScalabilityModeSupported(ScalabilityMode scalability_mode) {
+ return scalability_mode == ScalabilityMode::kL1T2 ||
+ scalability_mode == ScalabilityMode::kL1T3;
+ }
+};
+
+struct BarEncoderTemplateAdapter {
+ static std::vector<SdpVideoFormat> SupportedFormats() {
+ return {kBarLowSdp, kBarHighSdp};
+ }
+
+ static std::unique_ptr<VideoEncoder> CreateEncoder(
+ const SdpVideoFormat& format) {
+ return std::make_unique<testing::StrictMock<MockVideoEncoder>>();
+ }
+
+ static bool IsScalabilityModeSupported(ScalabilityMode scalability_mode) {
+ return scalability_mode == ScalabilityMode::kL1T2 ||
+ scalability_mode == ScalabilityMode::kL1T3 ||
+ scalability_mode == ScalabilityMode::kS2T1 ||
+ scalability_mode == ScalabilityMode::kS3T3;
+ }
+};
+
+TEST(VideoEncoderFactoryTemplate, OneTemplateAdapterCreateEncoder) {
+ VideoEncoderFactoryTemplate<FooEncoderTemplateAdapter> factory;
+ EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kFooSdp));
+ EXPECT_THAT(factory.CreateVideoEncoder(kFooSdp), NotNull());
+ EXPECT_THAT(factory.CreateVideoEncoder(SdpVideoFormat("FooX")), IsNull());
+}
+
+TEST(VideoEncoderFactoryTemplate, OneTemplateAdapterCodecSupport) {
+ VideoEncoderFactoryTemplate<FooEncoderTemplateAdapter> factory;
+ EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, absl::nullopt),
+ Field(&CodecSupport::is_supported, true));
+ EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "L1T2"),
+ Field(&CodecSupport::is_supported, true));
+ EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "S3T3"),
+ Field(&CodecSupport::is_supported, false));
+ EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat("FooX"), absl::nullopt),
+ Field(&CodecSupport::is_supported, false));
+}
+
+TEST(VideoEncoderFactoryTemplate, TwoTemplateAdaptersNoDuplicates) {
+ VideoEncoderFactoryTemplate<FooEncoderTemplateAdapter,
+ FooEncoderTemplateAdapter>
+ factory;
+ EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kFooSdp));
+}
+
+TEST(VideoEncoderFactoryTemplate, TwoTemplateAdaptersCreateEncoders) {
+ VideoEncoderFactoryTemplate<FooEncoderTemplateAdapter,
+ BarEncoderTemplateAdapter>
+ factory;
+ EXPECT_THAT(factory.GetSupportedFormats(),
+ UnorderedElementsAre(kFooSdp, kBarLowSdp, kBarHighSdp));
+ EXPECT_THAT(factory.CreateVideoEncoder(kFooSdp), NotNull());
+ EXPECT_THAT(factory.CreateVideoEncoder(kBarLowSdp), NotNull());
+ EXPECT_THAT(factory.CreateVideoEncoder(kBarHighSdp), NotNull());
+ EXPECT_THAT(factory.CreateVideoEncoder(SdpVideoFormat("FooX")), IsNull());
+ EXPECT_THAT(factory.CreateVideoEncoder(SdpVideoFormat("Bar")), NotNull());
+}
+
+TEST(VideoEncoderFactoryTemplate, TwoTemplateAdaptersCodecSupport) {
+ VideoEncoderFactoryTemplate<FooEncoderTemplateAdapter,
+ BarEncoderTemplateAdapter>
+ factory;
+ EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, absl::nullopt),
+ Field(&CodecSupport::is_supported, true));
+ EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "L1T2"),
+ Field(&CodecSupport::is_supported, true));
+ EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "S3T3"),
+ Field(&CodecSupport::is_supported, false));
+ EXPECT_THAT(factory.QueryCodecSupport(kBarLowSdp, absl::nullopt),
+ Field(&CodecSupport::is_supported, true));
+ EXPECT_THAT(factory.QueryCodecSupport(kBarHighSdp, absl::nullopt),
+ Field(&CodecSupport::is_supported, true));
+ EXPECT_THAT(factory.QueryCodecSupport(kBarLowSdp, "S2T1"),
+ Field(&CodecSupport::is_supported, true));
+ EXPECT_THAT(factory.QueryCodecSupport(kBarHighSdp, "S3T2"),
+ Field(&CodecSupport::is_supported, false));
+}
+
+TEST(VideoEncoderFactoryTemplate, LibvpxVp8) {
+ VideoEncoderFactoryTemplate<LibvpxVp8EncoderTemplateAdapter> factory;
+ auto formats = factory.GetSupportedFormats();
+ EXPECT_THAT(formats.size(), 1);
+ EXPECT_THAT(formats[0], Field(&SdpVideoFormat::name, "VP8"));
+ EXPECT_THAT(formats[0], Field(&SdpVideoFormat::scalability_modes,
+ Contains(ScalabilityMode::kL1T3)));
+ EXPECT_THAT(factory.CreateVideoEncoder(formats[0]), NotNull());
+}
+
+TEST(VideoEncoderFactoryTemplate, LibvpxVp9) {
+ VideoEncoderFactoryTemplate<LibvpxVp9EncoderTemplateAdapter> factory;
+ auto formats = factory.GetSupportedFormats();
+ EXPECT_THAT(formats, Not(IsEmpty()));
+ EXPECT_THAT(formats, Each(Field(&SdpVideoFormat::name, "VP9")));
+ EXPECT_THAT(formats, Each(Field(&SdpVideoFormat::scalability_modes,
+ Contains(ScalabilityMode::kL3T3_KEY))));
+ EXPECT_THAT(factory.CreateVideoEncoder(formats[0]), NotNull());
+}
+
+// TODO(bugs.webrtc.org/13573): When OpenH264 is no longer a conditional build
+// target remove this #ifdef.
+#if defined(WEBRTC_USE_H264)
+TEST(VideoEncoderFactoryTemplate, OpenH264) {
+ VideoEncoderFactoryTemplate<OpenH264EncoderTemplateAdapter> factory;
+ auto formats = factory.GetSupportedFormats();
+ EXPECT_THAT(formats, Not(IsEmpty()));
+ EXPECT_THAT(formats, Each(Field(&SdpVideoFormat::name, "H264")));
+ EXPECT_THAT(formats, Each(Field(&SdpVideoFormat::scalability_modes,
+ Contains(ScalabilityMode::kL1T3))));
+ EXPECT_THAT(factory.CreateVideoEncoder(formats[0]), NotNull());
+}
+#endif // defined(WEBRTC_USE_H264)
+
+TEST(VideoEncoderFactoryTemplate, LibaomAv1) {
+ VideoEncoderFactoryTemplate<LibaomAv1EncoderTemplateAdapter> factory;
+ auto formats = factory.GetSupportedFormats();
+ EXPECT_THAT(formats.size(), 1);
+ EXPECT_THAT(formats[0], Field(&SdpVideoFormat::name, "AV1"));
+ EXPECT_THAT(formats[0], Field(&SdpVideoFormat::scalability_modes,
+ Contains(ScalabilityMode::kL3T3_KEY)));
+ EXPECT_THAT(factory.CreateVideoEncoder(formats[0]), NotNull());
+}
+
+} // namespace
+} // namespace webrtc
diff --git a/third_party/libwebrtc/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc b/third_party/libwebrtc/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc
new file mode 100644
index 0000000000..b3fadcbecf
--- /dev/null
+++ b/third_party/libwebrtc/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc
@@ -0,0 +1,1144 @@
+/*
+ * Copyright (c) 2015 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 "api/video_codecs/video_encoder_software_fallback_wrapper.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/fec_controller_override.h"
+#include "api/scoped_refptr.h"
+#include "api/test/mock_video_encoder.h"
+#include "api/video/encoded_image.h"
+#include "api/video/i420_buffer.h"
+#include "api/video/video_bitrate_allocation.h"
+#include "api/video/video_frame.h"
+#include "api/video/video_frame_buffer.h"
+#include "api/video/video_rotation.h"
+#include "api/video_codecs/video_codec.h"
+#include "api/video_codecs/video_encoder.h"
+#include "modules/video_coding/codecs/vp8/include/vp8.h"
+#include "modules/video_coding/include/video_codec_interface.h"
+#include "modules/video_coding/include/video_error_codes.h"
+#include "modules/video_coding/utility/simulcast_rate_allocator.h"
+#include "rtc_base/fake_clock.h"
+#include "test/fake_encoder.h"
+#include "test/fake_texture_frame.h"
+#include "test/field_trial.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+using ::testing::_;
+using ::testing::Return;
+using ::testing::ValuesIn;
+
+namespace {
+const int kWidth = 320;
+const int kHeight = 240;
+const int kNumCores = 2;
+const uint32_t kFramerate = 30;
+const size_t kMaxPayloadSize = 800;
+const int kLowThreshold = 10;
+const int kHighThreshold = 20;
+
+const VideoEncoder::Capabilities kCapabilities(false);
+const VideoEncoder::Settings kSettings(kCapabilities,
+ kNumCores,
+ kMaxPayloadSize);
+
+VideoEncoder::EncoderInfo GetEncoderInfoWithTrustedRateController(
+ bool trusted_rate_controller) {
+ VideoEncoder::EncoderInfo info;
+ info.has_trusted_rate_controller = trusted_rate_controller;
+ return info;
+}
+
+VideoEncoder::EncoderInfo GetEncoderInfoWithHardwareAccelerated(
+ bool hardware_accelerated) {
+ VideoEncoder::EncoderInfo info;
+ info.is_hardware_accelerated = hardware_accelerated;
+ return info;
+}
+
+class FakeEncodedImageCallback : public EncodedImageCallback {
+ public:
+ Result OnEncodedImage(const EncodedImage& encoded_image,
+ const CodecSpecificInfo* codec_specific_info) override {
+ ++callback_count_;
+ return Result(Result::OK, callback_count_);
+ }
+ int callback_count_ = 0;
+};
+} // namespace
+
+class VideoEncoderSoftwareFallbackWrapperTestBase : public ::testing::Test {
+ protected:
+ VideoEncoderSoftwareFallbackWrapperTestBase(
+ const std::string& field_trials,
+ std::unique_ptr<VideoEncoder> sw_encoder)
+ : override_field_trials_(field_trials),
+ fake_encoder_(new CountingFakeEncoder()),
+ wrapper_initialized_(false),
+ fallback_wrapper_(CreateVideoEncoderSoftwareFallbackWrapper(
+ std::move(sw_encoder),
+ std::unique_ptr<VideoEncoder>(fake_encoder_),
+ false)) {}
+
+ class CountingFakeEncoder : public VideoEncoder {
+ public:
+ void SetFecControllerOverride(
+ FecControllerOverride* fec_controller_override) override {
+ // Ignored.
+ }
+
+ int32_t InitEncode(const VideoCodec* codec_settings,
+ const VideoEncoder::Settings& settings) override {
+ ++init_encode_count_;
+ return init_encode_return_code_;
+ }
+
+ int32_t Encode(const VideoFrame& frame,
+ const std::vector<VideoFrameType>* frame_types) override {
+ ++encode_count_;
+ last_video_frame_ = frame;
+ if (encode_complete_callback_ &&
+ encode_return_code_ == WEBRTC_VIDEO_CODEC_OK) {
+ encode_complete_callback_->OnEncodedImage(EncodedImage(), nullptr);
+ }
+ return encode_return_code_;
+ }
+
+ int32_t RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) override {
+ encode_complete_callback_ = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+ }
+
+ int32_t Release() override {
+ ++release_count_;
+ return WEBRTC_VIDEO_CODEC_OK;
+ }
+
+ void SetRates(const RateControlParameters& parameters) override {}
+
+ EncoderInfo GetEncoderInfo() const override {
+ ++supports_native_handle_count_;
+ EncoderInfo info;
+ info.scaling_settings = ScalingSettings(kLowThreshold, kHighThreshold);
+ info.supports_native_handle = supports_native_handle_;
+ info.implementation_name = implementation_name_;
+ if (is_qp_trusted_)
+ info.is_qp_trusted = is_qp_trusted_;
+ return info;
+ }
+
+ int init_encode_count_ = 0;
+ int32_t init_encode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
+ int32_t encode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
+ int encode_count_ = 0;
+ EncodedImageCallback* encode_complete_callback_ = nullptr;
+ int release_count_ = 0;
+ mutable int supports_native_handle_count_ = 0;
+ bool supports_native_handle_ = false;
+ bool is_qp_trusted_ = false;
+ std::string implementation_name_ = "fake-encoder";
+ absl::optional<VideoFrame> last_video_frame_;
+ };
+
+ void InitEncode();
+ void UtilizeFallbackEncoder();
+ void FallbackFromEncodeRequest();
+ void EncodeFrame();
+ void EncodeFrame(int expected_ret);
+ void CheckLastEncoderName(const char* expected_name) {
+ EXPECT_EQ(expected_name,
+ fallback_wrapper_->GetEncoderInfo().implementation_name);
+ }
+
+ test::ScopedFieldTrials override_field_trials_;
+ FakeEncodedImageCallback callback_;
+ // `fake_encoder_` is owned and released by `fallback_wrapper_`.
+ CountingFakeEncoder* fake_encoder_;
+ CountingFakeEncoder* fake_sw_encoder_;
+ bool wrapper_initialized_;
+ std::unique_ptr<VideoEncoder> fallback_wrapper_;
+ VideoCodec codec_ = {};
+ std::unique_ptr<VideoFrame> frame_;
+ std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
+};
+
+class VideoEncoderSoftwareFallbackWrapperTest
+ : public VideoEncoderSoftwareFallbackWrapperTestBase {
+ protected:
+ VideoEncoderSoftwareFallbackWrapperTest()
+ : VideoEncoderSoftwareFallbackWrapperTest(new CountingFakeEncoder()) {}
+ explicit VideoEncoderSoftwareFallbackWrapperTest(
+ CountingFakeEncoder* fake_sw_encoder)
+ : VideoEncoderSoftwareFallbackWrapperTestBase(
+ "",
+ std::unique_ptr<VideoEncoder>(fake_sw_encoder)),
+ fake_sw_encoder_(fake_sw_encoder) {
+ fake_sw_encoder_->implementation_name_ = "fake_sw_encoder";
+ }
+
+ CountingFakeEncoder* fake_sw_encoder_;
+};
+
+void VideoEncoderSoftwareFallbackWrapperTestBase::EncodeFrame() {
+ EncodeFrame(WEBRTC_VIDEO_CODEC_OK);
+}
+
+void VideoEncoderSoftwareFallbackWrapperTestBase::EncodeFrame(
+ int expected_ret) {
+ rtc::scoped_refptr<I420Buffer> buffer =
+ I420Buffer::Create(codec_.width, codec_.height);
+ I420Buffer::SetBlack(buffer.get());
+ std::vector<VideoFrameType> types(1, VideoFrameType::kVideoFrameKey);
+
+ frame_ =
+ std::make_unique<VideoFrame>(VideoFrame::Builder()
+ .set_video_frame_buffer(buffer)
+ .set_rotation(webrtc::kVideoRotation_0)
+ .set_timestamp_us(0)
+ .build());
+ EXPECT_EQ(expected_ret, fallback_wrapper_->Encode(*frame_, &types));
+}
+
+void VideoEncoderSoftwareFallbackWrapperTestBase::InitEncode() {
+ if (!wrapper_initialized_) {
+ fallback_wrapper_->RegisterEncodeCompleteCallback(&callback_);
+ EXPECT_EQ(&callback_, fake_encoder_->encode_complete_callback_);
+ }
+
+ // Register fake encoder as main.
+ codec_.codecType = kVideoCodecVP8;
+ codec_.maxFramerate = kFramerate;
+ codec_.width = kWidth;
+ codec_.height = kHeight;
+ codec_.VP8()->numberOfTemporalLayers = 1;
+ rate_allocator_.reset(new SimulcastRateAllocator(codec_));
+
+ if (wrapper_initialized_) {
+ fallback_wrapper_->Release();
+ }
+
+ fake_encoder_->init_encode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ fallback_wrapper_->InitEncode(&codec_, kSettings));
+
+ if (!wrapper_initialized_) {
+ fallback_wrapper_->SetRates(VideoEncoder::RateControlParameters(
+ rate_allocator_->Allocate(
+ VideoBitrateAllocationParameters(300000, kFramerate)),
+ kFramerate));
+ }
+ wrapper_initialized_ = true;
+}
+
+void VideoEncoderSoftwareFallbackWrapperTestBase::UtilizeFallbackEncoder() {
+ if (!wrapper_initialized_) {
+ fallback_wrapper_->RegisterEncodeCompleteCallback(&callback_);
+ EXPECT_EQ(&callback_, fake_encoder_->encode_complete_callback_);
+ }
+
+ // Register with failing fake encoder. Should succeed with VP8 fallback.
+ codec_.codecType = kVideoCodecVP8;
+ codec_.maxFramerate = kFramerate;
+ codec_.width = kWidth;
+ codec_.height = kHeight;
+ codec_.VP8()->numberOfTemporalLayers = 1;
+ rate_allocator_.reset(new SimulcastRateAllocator(codec_));
+
+ if (wrapper_initialized_) {
+ fallback_wrapper_->Release();
+ }
+
+ fake_encoder_->init_encode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ fallback_wrapper_->InitEncode(&codec_, kSettings));
+ fallback_wrapper_->SetRates(VideoEncoder::RateControlParameters(
+ rate_allocator_->Allocate(
+ VideoBitrateAllocationParameters(300000, kFramerate)),
+ kFramerate));
+
+ int callback_count = callback_.callback_count_;
+ int encode_count = fake_encoder_->encode_count_;
+ EncodeFrame();
+ EXPECT_EQ(encode_count, fake_encoder_->encode_count_);
+ EXPECT_EQ(callback_count + 1, callback_.callback_count_);
+}
+
+void VideoEncoderSoftwareFallbackWrapperTestBase::FallbackFromEncodeRequest() {
+ fallback_wrapper_->RegisterEncodeCompleteCallback(&callback_);
+ codec_.codecType = kVideoCodecVP8;
+ codec_.maxFramerate = kFramerate;
+ codec_.width = kWidth;
+ codec_.height = kHeight;
+ codec_.VP8()->numberOfTemporalLayers = 1;
+ rate_allocator_.reset(new SimulcastRateAllocator(codec_));
+ if (wrapper_initialized_) {
+ fallback_wrapper_->Release();
+ }
+ fallback_wrapper_->InitEncode(&codec_, kSettings);
+ fallback_wrapper_->SetRates(VideoEncoder::RateControlParameters(
+ rate_allocator_->Allocate(
+ VideoBitrateAllocationParameters(300000, kFramerate)),
+ kFramerate));
+ EXPECT_EQ(1, fake_encoder_->init_encode_count_);
+
+ // Have the non-fallback encoder request a software fallback.
+ fake_encoder_->encode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
+ int callback_count = callback_.callback_count_;
+ int encode_count = fake_encoder_->encode_count_;
+ EncodeFrame();
+ // Single encode request, which returned failure.
+ EXPECT_EQ(encode_count + 1, fake_encoder_->encode_count_);
+ EXPECT_EQ(callback_count + 1, callback_.callback_count_);
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest, InitializesEncoder) {
+ VideoCodec codec = {};
+ fallback_wrapper_->InitEncode(&codec, kSettings);
+ EXPECT_EQ(1, fake_encoder_->init_encode_count_);
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest, EncodeRequestsFallback) {
+ FallbackFromEncodeRequest();
+ // After fallback, further encodes shouldn't hit the fake encoder.
+ int encode_count = fake_encoder_->encode_count_;
+ EncodeFrame();
+ EXPECT_EQ(encode_count, fake_encoder_->encode_count_);
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest, CanUtilizeFallbackEncoder) {
+ UtilizeFallbackEncoder();
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
+ InternalEncoderReleasedDuringFallback) {
+ EXPECT_EQ(0, fake_encoder_->init_encode_count_);
+ EXPECT_EQ(0, fake_encoder_->release_count_);
+
+ InitEncode();
+
+ EXPECT_EQ(1, fake_encoder_->init_encode_count_);
+ EXPECT_EQ(0, fake_encoder_->release_count_);
+
+ UtilizeFallbackEncoder();
+
+ // One successful InitEncode(), one failed.
+ EXPECT_EQ(2, fake_encoder_->init_encode_count_);
+ EXPECT_EQ(1, fake_encoder_->release_count_);
+
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
+
+ // No extra release when the fallback is released.
+ EXPECT_EQ(2, fake_encoder_->init_encode_count_);
+ EXPECT_EQ(1, fake_encoder_->release_count_);
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
+ InternalEncoderNotEncodingDuringFallback) {
+ UtilizeFallbackEncoder();
+ int encode_count = fake_encoder_->encode_count_;
+ EncodeFrame();
+ EXPECT_EQ(encode_count, fake_encoder_->encode_count_);
+
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
+ CanRegisterCallbackWhileUsingFallbackEncoder) {
+ InitEncode();
+ EXPECT_EQ(&callback_, fake_encoder_->encode_complete_callback_);
+
+ UtilizeFallbackEncoder();
+
+ // Registering an encode-complete callback will now pass to the fallback
+ // instead of the main encoder.
+ FakeEncodedImageCallback callback2;
+ fallback_wrapper_->RegisterEncodeCompleteCallback(&callback2);
+ EXPECT_EQ(&callback_, fake_encoder_->encode_complete_callback_);
+
+ // Encoding a frame using the fallback should arrive at the new callback.
+ std::vector<VideoFrameType> types(1, VideoFrameType::kVideoFrameKey);
+ frame_->set_timestamp(frame_->timestamp() + 1000);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Encode(*frame_, &types));
+ EXPECT_EQ(callback2.callback_count_, 1);
+
+ // Re-initialize to use the main encoder, the new callback should be in use.
+ InitEncode();
+ EXPECT_EQ(&callback2, fake_encoder_->encode_complete_callback_);
+
+ frame_->set_timestamp(frame_->timestamp() + 2000);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Encode(*frame_, &types));
+ EXPECT_EQ(callback2.callback_count_, 2);
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
+ SupportsNativeHandleForwardedWithoutFallback) {
+ fallback_wrapper_->GetEncoderInfo();
+ EXPECT_EQ(1, fake_encoder_->supports_native_handle_count_);
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
+ SupportsNativeHandleNotForwardedDuringFallback) {
+ // Fake encoder signals support for native handle, default (libvpx) does not.
+ fake_encoder_->supports_native_handle_ = true;
+ EXPECT_TRUE(fallback_wrapper_->GetEncoderInfo().supports_native_handle);
+ UtilizeFallbackEncoder();
+ EXPECT_FALSE(fallback_wrapper_->GetEncoderInfo().supports_native_handle);
+ // Both times, both encoders are queried.
+ EXPECT_EQ(2, fake_encoder_->supports_native_handle_count_);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest, ReportsImplementationName) {
+ codec_.width = kWidth;
+ codec_.height = kHeight;
+ fallback_wrapper_->RegisterEncodeCompleteCallback(&callback_);
+ fallback_wrapper_->InitEncode(&codec_, kSettings);
+ EncodeFrame();
+ CheckLastEncoderName("fake-encoder");
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
+ IsQpTrustedNotForwardedDuringFallback) {
+ // Fake encoder signals trusted QP, default (libvpx) does not.
+ fake_encoder_->is_qp_trusted_ = true;
+ EXPECT_TRUE(fake_encoder_->GetEncoderInfo().is_qp_trusted.value_or(false));
+ UtilizeFallbackEncoder();
+ EXPECT_FALSE(fallback_wrapper_->GetEncoderInfo().is_qp_trusted.has_value());
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
+ ReportsFallbackImplementationName) {
+ UtilizeFallbackEncoder();
+ CheckLastEncoderName(fake_sw_encoder_->implementation_name_.c_str());
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
+ OnEncodeFallbackNativeFrameScaledIfFallbackDoesNotSupportNativeFrames) {
+ fake_encoder_->supports_native_handle_ = true;
+ fake_sw_encoder_->supports_native_handle_ = false;
+ InitEncode();
+ int width = codec_.width * 2;
+ int height = codec_.height * 2;
+ VideoFrame native_frame = test::FakeNativeBuffer::CreateFrame(
+ width, height, 0, 0, VideoRotation::kVideoRotation_0);
+ std::vector<VideoFrameType> types(1, VideoFrameType::kVideoFrameKey);
+ fake_encoder_->encode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
+
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ fallback_wrapper_->Encode(native_frame, &types));
+ EXPECT_EQ(1, fake_sw_encoder_->encode_count_);
+ ASSERT_TRUE(fake_sw_encoder_->last_video_frame_.has_value());
+ EXPECT_NE(VideoFrameBuffer::Type::kNative,
+ fake_sw_encoder_->last_video_frame_->video_frame_buffer()->type());
+ EXPECT_EQ(codec_.width, fake_sw_encoder_->last_video_frame_->width());
+ EXPECT_EQ(codec_.height, fake_sw_encoder_->last_video_frame_->height());
+}
+
+TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
+ OnEncodeFallbackNativeFrameForwardedToFallbackIfItSupportsNativeFrames) {
+ fake_encoder_->supports_native_handle_ = true;
+ fake_sw_encoder_->supports_native_handle_ = true;
+ InitEncode();
+ int width = codec_.width * 2;
+ int height = codec_.height * 2;
+ VideoFrame native_frame = test::FakeNativeBuffer::CreateFrame(
+ width, height, 0, 0, VideoRotation::kVideoRotation_0);
+ std::vector<VideoFrameType> types(1, VideoFrameType::kVideoFrameKey);
+ fake_encoder_->encode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
+
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ fallback_wrapper_->Encode(native_frame, &types));
+ EXPECT_EQ(1, fake_sw_encoder_->encode_count_);
+ ASSERT_TRUE(fake_sw_encoder_->last_video_frame_.has_value());
+ EXPECT_EQ(VideoFrameBuffer::Type::kNative,
+ fake_sw_encoder_->last_video_frame_->video_frame_buffer()->type());
+ EXPECT_EQ(native_frame.width(), fake_sw_encoder_->last_video_frame_->width());
+ EXPECT_EQ(native_frame.height(),
+ fake_sw_encoder_->last_video_frame_->height());
+}
+
+namespace {
+const int kBitrateKbps = 200;
+const int kMinPixelsPerFrame = 1;
+const char kFieldTrial[] = "WebRTC-VP8-Forced-Fallback-Encoder-v2";
+} // namespace
+
+class ForcedFallbackTest : public VideoEncoderSoftwareFallbackWrapperTestBase {
+ public:
+ explicit ForcedFallbackTest(const std::string& field_trials)
+ : VideoEncoderSoftwareFallbackWrapperTestBase(field_trials,
+ VP8Encoder::Create()) {}
+
+ ~ForcedFallbackTest() override {}
+
+ protected:
+ void SetUp() override {
+ clock_.SetTime(Timestamp::Micros(1234));
+ ConfigureVp8Codec();
+ }
+
+ void TearDown() override {
+ if (wrapper_initialized_) {
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
+ }
+ }
+
+ void ConfigureVp8Codec() {
+ codec_.codecType = kVideoCodecVP8;
+ codec_.maxFramerate = kFramerate;
+ codec_.width = kWidth;
+ codec_.height = kHeight;
+ codec_.VP8()->numberOfTemporalLayers = 1;
+ codec_.VP8()->automaticResizeOn = true;
+ codec_.SetFrameDropEnabled(true);
+ rate_allocator_.reset(new SimulcastRateAllocator(codec_));
+ }
+
+ void InitEncode(int width, int height) {
+ codec_.width = width;
+ codec_.height = height;
+ if (wrapper_initialized_) {
+ fallback_wrapper_->Release();
+ }
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ fallback_wrapper_->InitEncode(&codec_, kSettings));
+ fallback_wrapper_->RegisterEncodeCompleteCallback(&callback_);
+ wrapper_initialized_ = true;
+ SetRateAllocation(kBitrateKbps);
+ }
+
+ void SetRateAllocation(uint32_t bitrate_kbps) {
+ fallback_wrapper_->SetRates(VideoEncoder::RateControlParameters(
+ rate_allocator_->Allocate(
+ VideoBitrateAllocationParameters(bitrate_kbps * 1000, kFramerate)),
+ kFramerate));
+ }
+
+ void EncodeFrameAndVerifyLastName(const char* expected_name) {
+ EncodeFrameAndVerifyLastName(expected_name, WEBRTC_VIDEO_CODEC_OK);
+ }
+
+ void EncodeFrameAndVerifyLastName(const char* expected_name,
+ int expected_ret) {
+ EncodeFrame(expected_ret);
+ CheckLastEncoderName(expected_name);
+ }
+
+ rtc::ScopedFakeClock clock_;
+};
+
+class ForcedFallbackTestEnabled : public ForcedFallbackTest {
+ public:
+ ForcedFallbackTestEnabled()
+ : ForcedFallbackTest(std::string(kFieldTrial) + "/Enabled-" +
+ std::to_string(kMinPixelsPerFrame) + "," +
+ std::to_string(kWidth * kHeight) + ",30000/") {}
+};
+
+class ForcedFallbackTestDisabled : public ForcedFallbackTest {
+ public:
+ ForcedFallbackTestDisabled()
+ : ForcedFallbackTest(std::string(kFieldTrial) + "/Disabled/") {}
+};
+
+TEST_F(ForcedFallbackTestDisabled, NoFallbackWithoutFieldTrial) {
+ // Resolution above max threshold.
+ InitEncode(kWidth + 1, kHeight);
+ EXPECT_EQ(1, fake_encoder_->init_encode_count_);
+ EncodeFrameAndVerifyLastName("fake-encoder");
+
+ // Resolution at max threshold.
+ InitEncode(kWidth, kHeight);
+ EncodeFrameAndVerifyLastName("fake-encoder");
+}
+
+TEST_F(ForcedFallbackTestEnabled, FallbackIfAtMaxResolutionLimit) {
+ // Resolution above max threshold.
+ InitEncode(kWidth + 1, kHeight);
+ EXPECT_EQ(1, fake_encoder_->init_encode_count_);
+ EncodeFrameAndVerifyLastName("fake-encoder");
+
+ // Resolution at max threshold.
+ InitEncode(kWidth, kHeight);
+ EncodeFrameAndVerifyLastName("libvpx");
+}
+
+TEST_F(ForcedFallbackTestEnabled, FallbackIsKeptWhenInitEncodeIsCalled) {
+ // Resolution above max threshold.
+ InitEncode(kWidth + 1, kHeight);
+ EXPECT_EQ(1, fake_encoder_->init_encode_count_);
+ EncodeFrameAndVerifyLastName("fake-encoder");
+
+ // Resolution at max threshold.
+ InitEncode(kWidth, kHeight);
+ EncodeFrameAndVerifyLastName("libvpx");
+
+ // Re-initialize encoder, still expect fallback.
+ InitEncode(kWidth / 2, kHeight / 2);
+ EXPECT_EQ(1, fake_encoder_->init_encode_count_); // No change.
+ EncodeFrameAndVerifyLastName("libvpx");
+}
+
+TEST_F(ForcedFallbackTestEnabled, FallbackIsEndedWhenResolutionIsTooLarge) {
+ // Resolution above max threshold.
+ InitEncode(kWidth + 1, kHeight);
+ EXPECT_EQ(1, fake_encoder_->init_encode_count_);
+ EncodeFrameAndVerifyLastName("fake-encoder");
+
+ // Resolution at max threshold.
+ InitEncode(kWidth, kHeight);
+ EncodeFrameAndVerifyLastName("libvpx");
+
+ // Re-initialize encoder with a larger resolution, expect no fallback.
+ InitEncode(kWidth + 1, kHeight);
+ EXPECT_EQ(2, fake_encoder_->init_encode_count_);
+ EncodeFrameAndVerifyLastName("fake-encoder");
+}
+
+TEST_F(ForcedFallbackTestEnabled, FallbackIsEndedForNonValidSettings) {
+ // Resolution at max threshold.
+ InitEncode(kWidth, kHeight);
+ EncodeFrameAndVerifyLastName("libvpx");
+
+ // Re-initialize encoder with invalid setting, expect no fallback.
+ codec_.numberOfSimulcastStreams = 2;
+ InitEncode(kWidth, kHeight);
+ EXPECT_EQ(1, fake_encoder_->init_encode_count_);
+ EncodeFrameAndVerifyLastName("fake-encoder");
+
+ // Re-initialize encoder with valid setting.
+ codec_.numberOfSimulcastStreams = 1;
+ InitEncode(kWidth, kHeight);
+ EXPECT_EQ(1, fake_encoder_->init_encode_count_);
+ EncodeFrameAndVerifyLastName("libvpx");
+}
+
+TEST_F(ForcedFallbackTestEnabled, MultipleStartEndFallback) {
+ const int kNumRuns = 5;
+ for (int i = 1; i <= kNumRuns; ++i) {
+ // Resolution at max threshold.
+ InitEncode(kWidth, kHeight);
+ EncodeFrameAndVerifyLastName("libvpx");
+ // Resolution above max threshold.
+ InitEncode(kWidth + 1, kHeight);
+ EXPECT_EQ(i, fake_encoder_->init_encode_count_);
+ EncodeFrameAndVerifyLastName("fake-encoder");
+ }
+}
+
+TEST_F(ForcedFallbackTestDisabled, GetScaleSettings) {
+ // Resolution above max threshold.
+ InitEncode(kWidth + 1, kHeight);
+ EXPECT_EQ(1, fake_encoder_->init_encode_count_);
+ EncodeFrameAndVerifyLastName("fake-encoder");
+
+ // Default min pixels per frame should be used.
+ const auto settings = fallback_wrapper_->GetEncoderInfo().scaling_settings;
+ EXPECT_TRUE(settings.thresholds.has_value());
+ EXPECT_EQ(kDefaultMinPixelsPerFrame, settings.min_pixels_per_frame);
+}
+
+TEST_F(ForcedFallbackTestEnabled, GetScaleSettingsWithNoFallback) {
+ // Resolution above max threshold.
+ InitEncode(kWidth + 1, kHeight);
+ EncodeFrameAndVerifyLastName("fake-encoder");
+
+ // Configured min pixels per frame should be used.
+ const auto settings = fallback_wrapper_->GetEncoderInfo().scaling_settings;
+ EXPECT_EQ(kMinPixelsPerFrame, settings.min_pixels_per_frame);
+ ASSERT_TRUE(settings.thresholds);
+ EXPECT_EQ(kLowThreshold, settings.thresholds->low);
+ EXPECT_EQ(kHighThreshold, settings.thresholds->high);
+}
+
+TEST_F(ForcedFallbackTestEnabled, GetScaleSettingsWithFallback) {
+ // Resolution at max threshold.
+ InitEncode(kWidth, kHeight);
+ EncodeFrameAndVerifyLastName("libvpx");
+
+ // Configured min pixels per frame should be used.
+ const auto settings = fallback_wrapper_->GetEncoderInfo().scaling_settings;
+ EXPECT_TRUE(settings.thresholds.has_value());
+ EXPECT_EQ(kMinPixelsPerFrame, settings.min_pixels_per_frame);
+}
+
+TEST_F(ForcedFallbackTestEnabled, ScalingDisabledIfResizeOff) {
+ // Resolution at max threshold.
+ codec_.VP8()->automaticResizeOn = false;
+ InitEncode(kWidth, kHeight);
+ EncodeFrameAndVerifyLastName("libvpx");
+
+ // Should be disabled for automatic resize off.
+ const auto settings = fallback_wrapper_->GetEncoderInfo().scaling_settings;
+ EXPECT_FALSE(settings.thresholds.has_value());
+}
+
+TEST(SoftwareFallbackEncoderTest, BothRateControllersNotTrusted) {
+ auto* sw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+ auto* hw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+
+ EXPECT_CALL(*sw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(false)));
+ EXPECT_CALL(*hw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(false)));
+
+ std::unique_ptr<VideoEncoder> wrapper =
+ CreateVideoEncoderSoftwareFallbackWrapper(
+ std::unique_ptr<VideoEncoder>(sw_encoder),
+ std::unique_ptr<VideoEncoder>(hw_encoder));
+ EXPECT_FALSE(wrapper->GetEncoderInfo().has_trusted_rate_controller);
+}
+
+TEST(SoftwareFallbackEncoderTest, SwRateControllerTrusted) {
+ auto* sw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+ auto* hw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+ EXPECT_CALL(*sw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(true)));
+ EXPECT_CALL(*hw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(false)));
+
+ std::unique_ptr<VideoEncoder> wrapper =
+ CreateVideoEncoderSoftwareFallbackWrapper(
+ std::unique_ptr<VideoEncoder>(sw_encoder),
+ std::unique_ptr<VideoEncoder>(hw_encoder));
+ EXPECT_FALSE(wrapper->GetEncoderInfo().has_trusted_rate_controller);
+}
+
+TEST(SoftwareFallbackEncoderTest, HwRateControllerTrusted) {
+ auto* sw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+ auto* hw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+ EXPECT_CALL(*sw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(false)));
+ EXPECT_CALL(*hw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(true)));
+
+ std::unique_ptr<VideoEncoder> wrapper =
+ CreateVideoEncoderSoftwareFallbackWrapper(
+ std::unique_ptr<VideoEncoder>(sw_encoder),
+ std::unique_ptr<VideoEncoder>(hw_encoder));
+ EXPECT_TRUE(wrapper->GetEncoderInfo().has_trusted_rate_controller);
+
+ VideoCodec codec_ = {};
+ codec_.width = 100;
+ codec_.height = 100;
+ wrapper->InitEncode(&codec_, kSettings);
+
+ // Trigger fallback to software.
+ EXPECT_CALL(*hw_encoder, Encode)
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE));
+ VideoFrame frame = VideoFrame::Builder()
+ .set_video_frame_buffer(I420Buffer::Create(100, 100))
+ .build();
+ wrapper->Encode(frame, nullptr);
+
+ EXPECT_FALSE(wrapper->GetEncoderInfo().has_trusted_rate_controller);
+}
+
+TEST(SoftwareFallbackEncoderTest, BothRateControllersTrusted) {
+ auto* sw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+ auto* hw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+ EXPECT_CALL(*sw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(true)));
+ EXPECT_CALL(*hw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(true)));
+
+ std::unique_ptr<VideoEncoder> wrapper =
+ CreateVideoEncoderSoftwareFallbackWrapper(
+ std::unique_ptr<VideoEncoder>(sw_encoder),
+ std::unique_ptr<VideoEncoder>(hw_encoder));
+ EXPECT_TRUE(wrapper->GetEncoderInfo().has_trusted_rate_controller);
+}
+
+TEST(SoftwareFallbackEncoderTest, ReportsHardwareAccelerated) {
+ auto* sw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+ auto* hw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+ EXPECT_CALL(*sw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithHardwareAccelerated(false)));
+ EXPECT_CALL(*hw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithHardwareAccelerated(true)));
+
+ std::unique_ptr<VideoEncoder> wrapper =
+ CreateVideoEncoderSoftwareFallbackWrapper(
+ std::unique_ptr<VideoEncoder>(sw_encoder),
+ std::unique_ptr<VideoEncoder>(hw_encoder));
+ EXPECT_TRUE(wrapper->GetEncoderInfo().is_hardware_accelerated);
+
+ VideoCodec codec_ = {};
+ codec_.width = 100;
+ codec_.height = 100;
+ wrapper->InitEncode(&codec_, kSettings);
+
+ // Trigger fallback to software.
+ EXPECT_CALL(*hw_encoder, Encode)
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE));
+ VideoFrame frame = VideoFrame::Builder()
+ .set_video_frame_buffer(I420Buffer::Create(100, 100))
+ .build();
+ wrapper->Encode(frame, nullptr);
+ EXPECT_FALSE(wrapper->GetEncoderInfo().is_hardware_accelerated);
+}
+
+TEST(SoftwareFallbackEncoderTest, ConfigureHardwareOnSecondAttempt) {
+ auto* sw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+ auto* hw_encoder = new ::testing::NiceMock<MockVideoEncoder>();
+ EXPECT_CALL(*sw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithHardwareAccelerated(false)));
+ EXPECT_CALL(*hw_encoder, GetEncoderInfo())
+ .WillRepeatedly(Return(GetEncoderInfoWithHardwareAccelerated(true)));
+
+ std::unique_ptr<VideoEncoder> wrapper =
+ CreateVideoEncoderSoftwareFallbackWrapper(
+ std::unique_ptr<VideoEncoder>(sw_encoder),
+ std::unique_ptr<VideoEncoder>(hw_encoder));
+ EXPECT_TRUE(wrapper->GetEncoderInfo().is_hardware_accelerated);
+
+ // Initialize the encoder. When HW attempt fails we fallback to SW.
+ VideoCodec codec_ = {};
+ codec_.width = 100;
+ codec_.height = 100;
+ EXPECT_CALL(*hw_encoder, InitEncode(_, _))
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_ERR_PARAMETER));
+ EXPECT_CALL(*sw_encoder, InitEncode(_, _))
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK));
+ wrapper->InitEncode(&codec_, kSettings);
+
+ // When reconfiguring (Release+InitEncode) we should re-attempt HW.
+ wrapper->Release();
+ EXPECT_CALL(*hw_encoder, InitEncode(_, _))
+ .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK));
+ wrapper->InitEncode(&codec_, kSettings);
+}
+
+class PreferTemporalLayersFallbackTest : public ::testing::Test {
+ public:
+ PreferTemporalLayersFallbackTest() {}
+ void SetUp() override {
+ sw_ = new ::testing::NiceMock<MockVideoEncoder>();
+ sw_info_.implementation_name = "sw";
+ EXPECT_CALL(*sw_, GetEncoderInfo).WillRepeatedly([&]() {
+ return sw_info_;
+ });
+ EXPECT_CALL(*sw_, InitEncode(_, _, _))
+ .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK));
+
+ hw_ = new ::testing::NiceMock<MockVideoEncoder>();
+ hw_info_.implementation_name = "hw";
+ EXPECT_CALL(*hw_, GetEncoderInfo()).WillRepeatedly([&]() {
+ return hw_info_;
+ });
+ EXPECT_CALL(*hw_, InitEncode(_, _, _))
+ .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK));
+
+ wrapper_ = CreateVideoEncoderSoftwareFallbackWrapper(
+ std::unique_ptr<VideoEncoder>(sw_), std::unique_ptr<VideoEncoder>(hw_),
+ /*prefer_temporal_support=*/true);
+
+ codec_settings.codecType = kVideoCodecVP8;
+ codec_settings.maxFramerate = kFramerate;
+ codec_settings.width = kWidth;
+ codec_settings.height = kHeight;
+ codec_settings.numberOfSimulcastStreams = 1;
+ codec_settings.VP8()->numberOfTemporalLayers = 1;
+ }
+
+ protected:
+ void SetSupportsLayers(VideoEncoder::EncoderInfo* info, bool tl_enabled) {
+ int num_layers = 1;
+ if (tl_enabled) {
+ num_layers = codec_settings.VP8()->numberOfTemporalLayers;
+ }
+ SetNumLayers(info, num_layers);
+ }
+
+ void SetNumLayers(VideoEncoder::EncoderInfo* info, int num_layers) {
+ info->fps_allocation[0].clear();
+ for (int i = 0; i < num_layers; ++i) {
+ info->fps_allocation[0].push_back(
+ VideoEncoder::EncoderInfo::kMaxFramerateFraction >>
+ (num_layers - i - 1));
+ }
+ }
+
+ VideoCodec codec_settings;
+ ::testing::NiceMock<MockVideoEncoder>* sw_;
+ ::testing::NiceMock<MockVideoEncoder>* hw_;
+ VideoEncoder::EncoderInfo sw_info_;
+ VideoEncoder::EncoderInfo hw_info_;
+ std::unique_ptr<VideoEncoder> wrapper_;
+};
+
+TEST_F(PreferTemporalLayersFallbackTest, UsesMainWhenLayersNotUsed) {
+ codec_settings.VP8()->numberOfTemporalLayers = 1;
+ SetSupportsLayers(&hw_info_, true);
+ SetSupportsLayers(&sw_info_, true);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ wrapper_->InitEncode(&codec_settings, kSettings));
+ EXPECT_EQ(wrapper_->GetEncoderInfo().implementation_name, "hw");
+}
+
+TEST_F(PreferTemporalLayersFallbackTest, UsesMainWhenLayersSupported) {
+ codec_settings.VP8()->numberOfTemporalLayers = 2;
+ SetSupportsLayers(&hw_info_, true);
+ SetSupportsLayers(&sw_info_, true);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ wrapper_->InitEncode(&codec_settings, kSettings));
+ EXPECT_EQ(wrapper_->GetEncoderInfo().implementation_name, "hw");
+}
+
+TEST_F(PreferTemporalLayersFallbackTest,
+ UsesFallbackWhenLayersNotSupportedOnMain) {
+ codec_settings.VP8()->numberOfTemporalLayers = 2;
+ SetSupportsLayers(&hw_info_, false);
+ SetSupportsLayers(&sw_info_, true);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ wrapper_->InitEncode(&codec_settings, kSettings));
+ EXPECT_EQ(wrapper_->GetEncoderInfo().implementation_name, "sw");
+}
+
+TEST_F(PreferTemporalLayersFallbackTest, UsesMainWhenNeitherSupportsTemporal) {
+ codec_settings.VP8()->numberOfTemporalLayers = 2;
+ SetSupportsLayers(&hw_info_, false);
+ SetSupportsLayers(&sw_info_, false);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ wrapper_->InitEncode(&codec_settings, kSettings));
+ EXPECT_EQ(wrapper_->GetEncoderInfo().implementation_name, "hw");
+}
+
+TEST_F(PreferTemporalLayersFallbackTest, UsesFallbackWhenLayersAreUndefined) {
+ codec_settings.VP8()->numberOfTemporalLayers = 2;
+ SetNumLayers(&hw_info_, 1);
+ SetNumLayers(&sw_info_, 0);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ wrapper_->InitEncode(&codec_settings, kSettings));
+ EXPECT_EQ(wrapper_->GetEncoderInfo().implementation_name, "sw");
+}
+
+TEST_F(PreferTemporalLayersFallbackTest, PrimesEncoderOnSwitch) {
+ codec_settings.VP8()->numberOfTemporalLayers = 2;
+ // Both support temporal layers, will use main one.
+ SetSupportsLayers(&hw_info_, true);
+ SetSupportsLayers(&sw_info_, true);
+
+ // On first InitEncode most params have no state and will not be
+ // called to update.
+ EXPECT_CALL(*hw_, RegisterEncodeCompleteCallback).Times(0);
+ EXPECT_CALL(*sw_, RegisterEncodeCompleteCallback).Times(0);
+
+ EXPECT_CALL(*hw_, SetFecControllerOverride).Times(0);
+ EXPECT_CALL(*sw_, SetFecControllerOverride).Times(0);
+
+ EXPECT_CALL(*hw_, SetRates).Times(0);
+ EXPECT_CALL(*hw_, SetRates).Times(0);
+
+ EXPECT_CALL(*hw_, OnPacketLossRateUpdate).Times(0);
+ EXPECT_CALL(*sw_, OnPacketLossRateUpdate).Times(0);
+
+ EXPECT_CALL(*hw_, OnRttUpdate).Times(0);
+ EXPECT_CALL(*sw_, OnRttUpdate).Times(0);
+
+ EXPECT_CALL(*hw_, OnLossNotification).Times(0);
+ EXPECT_CALL(*sw_, OnLossNotification).Times(0);
+
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ wrapper_->InitEncode(&codec_settings, kSettings));
+ EXPECT_EQ(wrapper_->GetEncoderInfo().implementation_name, "hw");
+
+ FakeEncodedImageCallback callback1;
+ class DummyFecControllerOverride : public FecControllerOverride {
+ public:
+ void SetFecAllowed(bool fec_allowed) override {}
+ };
+ DummyFecControllerOverride fec_controller_override1;
+ VideoEncoder::RateControlParameters rate_params1;
+ float packet_loss1 = 0.1;
+ int64_t rtt1 = 1;
+ VideoEncoder::LossNotification lntf1;
+
+ EXPECT_CALL(*hw_, RegisterEncodeCompleteCallback(&callback1));
+ EXPECT_CALL(*sw_, RegisterEncodeCompleteCallback).Times(0);
+ wrapper_->RegisterEncodeCompleteCallback(&callback1);
+
+ EXPECT_CALL(*hw_, SetFecControllerOverride(&fec_controller_override1));
+ EXPECT_CALL(*sw_, SetFecControllerOverride).Times(1);
+ wrapper_->SetFecControllerOverride(&fec_controller_override1);
+
+ EXPECT_CALL(*hw_, SetRates(rate_params1));
+ EXPECT_CALL(*sw_, SetRates).Times(0);
+ wrapper_->SetRates(rate_params1);
+
+ EXPECT_CALL(*hw_, OnPacketLossRateUpdate(packet_loss1));
+ EXPECT_CALL(*sw_, OnPacketLossRateUpdate).Times(0);
+ wrapper_->OnPacketLossRateUpdate(packet_loss1);
+
+ EXPECT_CALL(*hw_, OnRttUpdate(rtt1));
+ EXPECT_CALL(*sw_, OnRttUpdate).Times(0);
+ wrapper_->OnRttUpdate(rtt1);
+
+ EXPECT_CALL(*hw_, OnLossNotification).Times(1);
+ EXPECT_CALL(*sw_, OnLossNotification).Times(0);
+ wrapper_->OnLossNotification(lntf1);
+
+ // Release and re-init, with fallback to software. This should trigger
+ // the software encoder to be primed with the current state.
+ wrapper_->Release();
+ EXPECT_CALL(*sw_, RegisterEncodeCompleteCallback(&callback1));
+ EXPECT_CALL(*hw_, RegisterEncodeCompleteCallback).Times(0);
+
+ EXPECT_CALL(*sw_, SetFecControllerOverride).Times(0);
+ EXPECT_CALL(*hw_, SetFecControllerOverride).Times(0);
+
+ // Rate control parameters are cleared on InitEncode.
+ EXPECT_CALL(*sw_, SetRates).Times(0);
+ EXPECT_CALL(*hw_, SetRates).Times(0);
+
+ EXPECT_CALL(*sw_, OnPacketLossRateUpdate(packet_loss1));
+ EXPECT_CALL(*hw_, OnPacketLossRateUpdate).Times(0);
+
+ EXPECT_CALL(*sw_, OnRttUpdate(rtt1));
+ EXPECT_CALL(*hw_, OnRttUpdate).Times(0);
+
+ EXPECT_CALL(*sw_, OnLossNotification).Times(1);
+ EXPECT_CALL(*hw_, OnLossNotification).Times(0);
+
+ SetSupportsLayers(&hw_info_, false);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ wrapper_->InitEncode(&codec_settings, kSettings));
+ EXPECT_EQ(wrapper_->GetEncoderInfo().implementation_name, "sw");
+
+ // Update with all-new params for the software encoder.
+ FakeEncodedImageCallback callback2;
+ DummyFecControllerOverride fec_controller_override2;
+ VideoEncoder::RateControlParameters rate_params2;
+ float packet_loss2 = 0.2;
+ int64_t rtt2 = 2;
+ VideoEncoder::LossNotification lntf2;
+
+ EXPECT_CALL(*sw_, RegisterEncodeCompleteCallback(&callback2));
+ EXPECT_CALL(*hw_, RegisterEncodeCompleteCallback).Times(0);
+ wrapper_->RegisterEncodeCompleteCallback(&callback2);
+
+ EXPECT_CALL(*sw_, SetFecControllerOverride(&fec_controller_override2));
+ EXPECT_CALL(*hw_, SetFecControllerOverride).Times(1);
+ wrapper_->SetFecControllerOverride(&fec_controller_override2);
+
+ EXPECT_CALL(*sw_, SetRates(rate_params2));
+ EXPECT_CALL(*hw_, SetRates).Times(0);
+ wrapper_->SetRates(rate_params2);
+
+ EXPECT_CALL(*sw_, OnPacketLossRateUpdate(packet_loss2));
+ EXPECT_CALL(*hw_, OnPacketLossRateUpdate).Times(0);
+ wrapper_->OnPacketLossRateUpdate(packet_loss2);
+
+ EXPECT_CALL(*sw_, OnRttUpdate(rtt2));
+ EXPECT_CALL(*hw_, OnRttUpdate).Times(0);
+ wrapper_->OnRttUpdate(rtt2);
+
+ EXPECT_CALL(*sw_, OnLossNotification).Times(1);
+ EXPECT_CALL(*hw_, OnLossNotification).Times(0);
+ wrapper_->OnLossNotification(lntf2);
+
+ // Release and re-init, back to main encoder. This should trigger
+ // the main encoder to be primed with the current state.
+ wrapper_->Release();
+ EXPECT_CALL(*hw_, RegisterEncodeCompleteCallback(&callback2));
+ EXPECT_CALL(*sw_, RegisterEncodeCompleteCallback).Times(0);
+
+ EXPECT_CALL(*hw_, SetFecControllerOverride).Times(0);
+ EXPECT_CALL(*sw_, SetFecControllerOverride).Times(0);
+
+ // Rate control parameters are cleared on InitEncode.
+ EXPECT_CALL(*sw_, SetRates).Times(0);
+ EXPECT_CALL(*hw_, SetRates).Times(0);
+
+ EXPECT_CALL(*hw_, OnPacketLossRateUpdate(packet_loss2));
+ EXPECT_CALL(*sw_, OnPacketLossRateUpdate).Times(0);
+
+ EXPECT_CALL(*hw_, OnRttUpdate(rtt2));
+ EXPECT_CALL(*sw_, OnRttUpdate).Times(0);
+
+ EXPECT_CALL(*hw_, OnLossNotification).Times(1);
+ EXPECT_CALL(*sw_, OnLossNotification).Times(0);
+
+ SetSupportsLayers(&hw_info_, true);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ wrapper_->InitEncode(&codec_settings, kSettings));
+ EXPECT_EQ(wrapper_->GetEncoderInfo().implementation_name, "hw");
+}
+
+struct ResolutionBasedFallbackTestParams {
+ std::string test_name;
+ std::string field_trials = "";
+ VideoCodecType codec_type = kVideoCodecGeneric;
+ int width = 16;
+ int height = 16;
+ std::string expect_implementation_name;
+};
+
+using ResolutionBasedFallbackTest =
+ ::testing::TestWithParam<ResolutionBasedFallbackTestParams>;
+
+INSTANTIATE_TEST_SUITE_P(
+ VideoEncoderFallbackTest,
+ ResolutionBasedFallbackTest,
+ ValuesIn<ResolutionBasedFallbackTestParams>(
+ {{.test_name = "FallbackNotConfigured",
+ .expect_implementation_name = "primary"},
+ {.test_name = "ResolutionAboveFallbackThreshold",
+ .field_trials = "WebRTC-Video-EncoderFallbackSettings/"
+ "resolution_threshold_px:255/",
+ .expect_implementation_name = "primary"},
+ {.test_name = "ResolutionEqualFallbackThreshold",
+ .field_trials = "WebRTC-Video-EncoderFallbackSettings/"
+ "resolution_threshold_px:256/",
+ .expect_implementation_name = "fallback"},
+ {.test_name = "GenericFallbackSettingsTakePrecedence",
+ .field_trials =
+ "WebRTC-Video-EncoderFallbackSettings/"
+ "resolution_threshold_px:255/"
+ "WebRTC-VP8-Forced-Fallback-Encoder-v2/Enabled-1,256,1/",
+ .codec_type = kVideoCodecVP8,
+ .expect_implementation_name = "primary"}}),
+ [](const testing::TestParamInfo<ResolutionBasedFallbackTest::ParamType>&
+ info) { return info.param.test_name; });
+
+TEST_P(ResolutionBasedFallbackTest, VerifyForcedEncoderFallback) {
+ const ResolutionBasedFallbackTestParams& params = GetParam();
+ test::ScopedFieldTrials field_trials(params.field_trials);
+ auto primary = new test::FakeEncoder(Clock::GetRealTimeClock());
+ auto fallback = new test::FakeEncoder(Clock::GetRealTimeClock());
+ auto encoder = CreateVideoEncoderSoftwareFallbackWrapper(
+ std::unique_ptr<VideoEncoder>(fallback),
+ std::unique_ptr<VideoEncoder>(primary),
+ /*prefer_temporal_support=*/false);
+ primary->SetImplementationName("primary");
+ fallback->SetImplementationName("fallback");
+ VideoCodec codec;
+ codec.codecType = params.codec_type;
+ codec.width = params.width;
+ codec.height = params.height;
+ encoder->InitEncode(&codec, kSettings);
+ EXPECT_EQ(encoder->GetEncoderInfo().implementation_name,
+ params.expect_implementation_name);
+}
+
+} // namespace webrtc