From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../libwebrtc/api/video_codecs/test/BUILD.gn | 86 ++ .../test/builtin_video_encoder_factory_unittest.cc | 39 + .../test/h264_profile_level_id_unittest.cc | 171 +++ .../test/h265_profile_tier_level_unittest.cc | 248 +++++ .../video_codecs/test/sdp_video_format_unittest.cc | 155 +++ .../test/video_decoder_factory_template_tests.cc | 123 +++ ...o_decoder_software_fallback_wrapper_unittest.cc | 304 ++++++ .../test/video_encoder_factory_template_tests.cc | 173 +++ ...o_encoder_software_fallback_wrapper_unittest.cc | 1144 ++++++++++++++++++++ 9 files changed, 2443 insertions(+) create mode 100644 third_party/libwebrtc/api/video_codecs/test/BUILD.gn create mode 100644 third_party/libwebrtc/api/video_codecs/test/builtin_video_encoder_factory_unittest.cc create mode 100644 third_party/libwebrtc/api/video_codecs/test/h264_profile_level_id_unittest.cc create mode 100644 third_party/libwebrtc/api/video_codecs/test/h265_profile_tier_level_unittest.cc create mode 100644 third_party/libwebrtc/api/video_codecs/test/sdp_video_format_unittest.cc create mode 100644 third_party/libwebrtc/api/video_codecs/test/video_decoder_factory_template_tests.cc create mode 100644 third_party/libwebrtc/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc create mode 100644 third_party/libwebrtc/api/video_codecs/test/video_encoder_factory_template_tests.cc create mode 100644 third_party/libwebrtc/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc (limited to 'third_party/libwebrtc/api/video_codecs/test') 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 +#include +#include + +#include "api/video_codecs/sdp_video_format.h" +#include "test/gtest.h" + +namespace webrtc { + +TEST(BuiltinVideoEncoderFactoryTest, AnnouncesVp9AccordingToBuildFlags) { + std::unique_ptr 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 +#include + +#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(255), H264Level::kLevel3_1))); +} + +TEST(H264ProfileLevelId, TestParseSdpProfileLevelIdEmpty) { + const absl::optional 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 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 + +#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 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 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 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 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 + +#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 SupportedFormats() { return {kFooSdp}; } + + static std::unique_ptr CreateDecoder( + const SdpVideoFormat& format) { + auto decoder = std::make_unique>(); + EXPECT_CALL(*decoder, Destruct); + return decoder; + } +}; + +struct BarDecoderTemplateAdapter { + static std::vector SupportedFormats() { + return {kBarLowSdp, kBarHighSdp}; + } + + static std::unique_ptr CreateDecoder( + const SdpVideoFormat& format) { + auto decoder = std::make_unique>(); + EXPECT_CALL(*decoder, Destruct); + return decoder; + } +}; + +TEST(VideoDecoderFactoryTemplate, OneTemplateAdapterCreateDecoder) { + VideoDecoderFactoryTemplate 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 + factory; + EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kFooSdp)); +} + +TEST(VideoDecoderFactoryTemplate, TwoTemplateAdaptersCreateDecoders) { + VideoDecoderFactoryTemplate + 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 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 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 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 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 + +#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(VP8Decoder::Create()), + std::unique_ptr(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 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 decode_time_ms, + absl::optional 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(sw_fallback_decoder_), + std::unique_ptr(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 SupportedFormats() { return {kFooSdp}; } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return std::make_unique>(); + } + + static bool IsScalabilityModeSupported(ScalabilityMode scalability_mode) { + return scalability_mode == ScalabilityMode::kL1T2 || + scalability_mode == ScalabilityMode::kL1T3; + } +}; + +struct BarEncoderTemplateAdapter { + static std::vector SupportedFormats() { + return {kBarLowSdp, kBarHighSdp}; + } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return std::make_unique>(); + } + + 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 factory; + EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kFooSdp)); + EXPECT_THAT(factory.CreateVideoEncoder(kFooSdp), NotNull()); + EXPECT_THAT(factory.CreateVideoEncoder(SdpVideoFormat("FooX")), IsNull()); +} + +TEST(VideoEncoderFactoryTemplate, OneTemplateAdapterCodecSupport) { + VideoEncoderFactoryTemplate 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 + factory; + EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kFooSdp)); +} + +TEST(VideoEncoderFactoryTemplate, TwoTemplateAdaptersCreateEncoders) { + VideoEncoderFactoryTemplate + 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 + 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 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 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 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 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 +#include + +#include +#include +#include + +#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 sw_encoder) + : override_field_trials_(field_trials), + fake_encoder_(new CountingFakeEncoder()), + wrapper_initialized_(false), + fallback_wrapper_(CreateVideoEncoderSoftwareFallbackWrapper( + std::move(sw_encoder), + std::unique_ptr(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* 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 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 fallback_wrapper_; + VideoCodec codec_ = {}; + std::unique_ptr frame_; + std::unique_ptr rate_allocator_; +}; + +class VideoEncoderSoftwareFallbackWrapperTest + : public VideoEncoderSoftwareFallbackWrapperTestBase { + protected: + VideoEncoderSoftwareFallbackWrapperTest() + : VideoEncoderSoftwareFallbackWrapperTest(new CountingFakeEncoder()) {} + explicit VideoEncoderSoftwareFallbackWrapperTest( + CountingFakeEncoder* fake_sw_encoder) + : VideoEncoderSoftwareFallbackWrapperTestBase( + "", + std::unique_ptr(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 buffer = + I420Buffer::Create(codec_.width, codec_.height); + I420Buffer::SetBlack(buffer.get()); + std::vector types(1, VideoFrameType::kVideoFrameKey); + + frame_ = + std::make_unique(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 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 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 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(); + auto* hw_encoder = new ::testing::NiceMock(); + + EXPECT_CALL(*sw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(false))); + EXPECT_CALL(*hw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(false))); + + std::unique_ptr wrapper = + CreateVideoEncoderSoftwareFallbackWrapper( + std::unique_ptr(sw_encoder), + std::unique_ptr(hw_encoder)); + EXPECT_FALSE(wrapper->GetEncoderInfo().has_trusted_rate_controller); +} + +TEST(SoftwareFallbackEncoderTest, SwRateControllerTrusted) { + auto* sw_encoder = new ::testing::NiceMock(); + auto* hw_encoder = new ::testing::NiceMock(); + EXPECT_CALL(*sw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(true))); + EXPECT_CALL(*hw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(false))); + + std::unique_ptr wrapper = + CreateVideoEncoderSoftwareFallbackWrapper( + std::unique_ptr(sw_encoder), + std::unique_ptr(hw_encoder)); + EXPECT_FALSE(wrapper->GetEncoderInfo().has_trusted_rate_controller); +} + +TEST(SoftwareFallbackEncoderTest, HwRateControllerTrusted) { + auto* sw_encoder = new ::testing::NiceMock(); + auto* hw_encoder = new ::testing::NiceMock(); + EXPECT_CALL(*sw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(false))); + EXPECT_CALL(*hw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(true))); + + std::unique_ptr wrapper = + CreateVideoEncoderSoftwareFallbackWrapper( + std::unique_ptr(sw_encoder), + std::unique_ptr(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(); + auto* hw_encoder = new ::testing::NiceMock(); + EXPECT_CALL(*sw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(true))); + EXPECT_CALL(*hw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithTrustedRateController(true))); + + std::unique_ptr wrapper = + CreateVideoEncoderSoftwareFallbackWrapper( + std::unique_ptr(sw_encoder), + std::unique_ptr(hw_encoder)); + EXPECT_TRUE(wrapper->GetEncoderInfo().has_trusted_rate_controller); +} + +TEST(SoftwareFallbackEncoderTest, ReportsHardwareAccelerated) { + auto* sw_encoder = new ::testing::NiceMock(); + auto* hw_encoder = new ::testing::NiceMock(); + EXPECT_CALL(*sw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithHardwareAccelerated(false))); + EXPECT_CALL(*hw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithHardwareAccelerated(true))); + + std::unique_ptr wrapper = + CreateVideoEncoderSoftwareFallbackWrapper( + std::unique_ptr(sw_encoder), + std::unique_ptr(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(); + auto* hw_encoder = new ::testing::NiceMock(); + EXPECT_CALL(*sw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithHardwareAccelerated(false))); + EXPECT_CALL(*hw_encoder, GetEncoderInfo()) + .WillRepeatedly(Return(GetEncoderInfoWithHardwareAccelerated(true))); + + std::unique_ptr wrapper = + CreateVideoEncoderSoftwareFallbackWrapper( + std::unique_ptr(sw_encoder), + std::unique_ptr(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(); + 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(); + 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(sw_), std::unique_ptr(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* sw_; + ::testing::NiceMock* hw_; + VideoEncoder::EncoderInfo sw_info_; + VideoEncoder::EncoderInfo hw_info_; + std::unique_ptr 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; + +INSTANTIATE_TEST_SUITE_P( + VideoEncoderFallbackTest, + ResolutionBasedFallbackTest, + ValuesIn( + {{.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& + 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(fallback), + std::unique_ptr(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 -- cgit v1.2.3