| /* |
| * 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 <optional> |
| #include <string> |
| |
| #include "api/rtp_parameters.h" |
| #include "api/video/resolution.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 std::optional<H265ProfileTierLevel> profile_tier_level = |
| ParseSdpForH265ProfileTierLevel(CodecParameterMap()); |
| 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) { |
| CodecParameterMap params; |
| params["profile-id"] = "1"; |
| params["tier-flag"] = "0"; |
| std::optional<H265ProfileTierLevel> profile_tier_level = |
| ParseSdpForH265ProfileTierLevel(params); |
| EXPECT_TRUE(profile_tier_level); |
| EXPECT_EQ(H265Profile::kProfileMain, profile_tier_level->profile); |
| EXPECT_EQ(H265Level::kLevel3_1, profile_tier_level->level); |
| EXPECT_EQ(H265Tier::kTier0, profile_tier_level->tier); |
| |
| params.clear(); |
| params["profile-id"] = "2"; |
| profile_tier_level = ParseSdpForH265ProfileTierLevel(params); |
| EXPECT_TRUE(profile_tier_level); |
| EXPECT_EQ(H265Profile::kProfileMain10, profile_tier_level->profile); |
| EXPECT_EQ(H265Level::kLevel3_1, profile_tier_level->level); |
| EXPECT_EQ(H265Tier::kTier0, profile_tier_level->tier); |
| |
| params.clear(); |
| params["level-id"] = "180"; |
| profile_tier_level = ParseSdpForH265ProfileTierLevel(params); |
| EXPECT_TRUE(profile_tier_level); |
| EXPECT_EQ(H265Profile::kProfileMain, profile_tier_level->profile); |
| EXPECT_EQ(H265Level::kLevel6, profile_tier_level->level); |
| EXPECT_EQ(H265Tier::kTier0, profile_tier_level->tier); |
| } |
| |
| TEST(H265ProfileTierLevel, TestParseSdpProfileTierLevelInvalid) { |
| CodecParameterMap params; |
| |
| // Invalid profile-tier-level combination. |
| params["profile-id"] = "1"; |
| params["tier-flag"] = "1"; |
| params["level-id"] = "93"; |
| std::optional<H265ProfileTierLevel> profile_tier_level = |
| ParseSdpForH265ProfileTierLevel(params); |
| EXPECT_FALSE(profile_tier_level); |
| params.clear(); |
| params["profile-id"] = "1"; |
| params["tier-flag"] = "4"; |
| params["level-id"] = "180"; |
| profile_tier_level = ParseSdpForH265ProfileTierLevel(params); |
| EXPECT_FALSE(profile_tier_level); |
| |
| // Valid profile-tier-level combination. |
| params.clear(); |
| params["profile-id"] = "1"; |
| params["tier-flag"] = "0"; |
| params["level-id"] = "153"; |
| profile_tier_level = ParseSdpForH265ProfileTierLevel(params); |
| EXPECT_TRUE(profile_tier_level); |
| } |
| |
| TEST(H265ProfileTierLevel, TestToStringRoundTrip) { |
| CodecParameterMap params; |
| params["profile-id"] = "1"; |
| params["tier-flag"] = "0"; |
| params["level-id"] = "93"; |
| std::optional<H265ProfileTierLevel> profile_tier_level = |
| ParseSdpForH265ProfileTierLevel(params); |
| EXPECT_TRUE(profile_tier_level); |
| EXPECT_EQ("1", H265ProfileToString(profile_tier_level->profile)); |
| EXPECT_EQ("0", H265TierToString(profile_tier_level->tier)); |
| EXPECT_EQ("93", H265LevelToString(profile_tier_level->level)); |
| |
| params.clear(); |
| params["profile-id"] = "2"; |
| params["tier-flag"] = "1"; |
| params["level-id"] = "180"; |
| profile_tier_level = ParseSdpForH265ProfileTierLevel(params); |
| EXPECT_TRUE(profile_tier_level); |
| EXPECT_EQ("2", H265ProfileToString(profile_tier_level->profile)); |
| EXPECT_EQ("1", H265TierToString(profile_tier_level->tier)); |
| EXPECT_EQ("180", H265LevelToString(profile_tier_level->level)); |
| } |
| |
| TEST(H265ProfileTierLevel, TestProfileTierLevelCompare) { |
| CodecParameterMap params1; |
| CodecParameterMap 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"] = "180"; |
| // Level 3.1 is not allowed for tier 1. |
| params2["level-id"] = "180"; |
| params1["tier-flag"] = "0"; |
| params2["tier-flag"] = "1"; |
| EXPECT_FALSE(H265IsSameProfileTierLevel(params1, params2)); |
| |
| // One of the CodecParameterMap 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)); |
| } |
| |
| TEST(H265ProfileTierLevel, TestProfileCompare) { |
| CodecParameterMap params1; |
| CodecParameterMap params2; |
| |
| // None of profile-id/tier-flag/level-id is specified, |
| EXPECT_TRUE(H265IsSameProfile(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(H265IsSameProfile(params1, params2)); |
| |
| // Different profiles. |
| params1.clear(); |
| params2.clear(); |
| params1["profile-id"] = "1"; |
| params2["profile-id"] = "2"; |
| EXPECT_FALSE(H265IsSameProfile(params1, params2)); |
| |
| // Different levels. We do not compare HEVC levels. |
| params1.clear(); |
| params2.clear(); |
| params1["profile-id"] = "1"; |
| params2["profile-id"] = "1"; |
| params1["level-id"] = "93"; |
| params2["level-id"] = "183"; |
| EXPECT_TRUE(H265IsSameProfile(params1, params2)); |
| |
| // Different tiers. |
| params1.clear(); |
| params2.clear(); |
| params1["profile-id"] = "1"; |
| params2["profile-id"] = "1"; |
| params1["level-id"] = "180"; |
| // level 3.1 is not allowed for tier 1. |
| params2["level-id"] = "180"; |
| params1["tier-flag"] = "0"; |
| params2["tier-flag"] = "1"; |
| EXPECT_TRUE(H265IsSameProfile(params1, params2)); |
| |
| // One of the CodecParameterMap is invalid. |
| params1.clear(); |
| params2.clear(); |
| params1["profile-id"] = "1"; |
| params2["profile-id"] = "1"; |
| params1["tier-flag"] = "0"; |
| params2["tier-flag"] = "4"; |
| EXPECT_FALSE(H265IsSameProfile(params1, params2)); |
| } |
| |
| TEST(H265ProfileTierLevel, TestTierCompare) { |
| CodecParameterMap params1; |
| CodecParameterMap params2; |
| |
| // None of profile-id/tier-flag/level-id is specified, |
| EXPECT_TRUE(H265IsSameTier(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(H265IsSameTier(params1, params2)); |
| |
| // Different profiles. |
| params1.clear(); |
| params2.clear(); |
| params1["profile-id"] = "1"; |
| params2["profile-id"] = "2"; |
| EXPECT_TRUE(H265IsSameTier(params1, params2)); |
| |
| // Different levels. We do not compare HEVC levels. |
| params1.clear(); |
| params2.clear(); |
| params1["profile-id"] = "1"; |
| params2["profile-id"] = "1"; |
| params1["level-id"] = "93"; |
| params2["level-id"] = "183"; |
| EXPECT_TRUE(H265IsSameTier(params1, params2)); |
| |
| // Different tiers. |
| params1.clear(); |
| params2.clear(); |
| params1["profile-id"] = "1"; |
| params2["profile-id"] = "1"; |
| params1["level-id"] = "180"; |
| // level 3.1 is not allowed for tier 1. |
| params2["level-id"] = "180"; |
| params1["tier-flag"] = "0"; |
| params2["tier-flag"] = "1"; |
| EXPECT_FALSE(H265IsSameTier(params1, params2)); |
| |
| // One of the CodecParameterMap is invalid. |
| params1.clear(); |
| params2.clear(); |
| params1["profile-id"] = "1"; |
| params2["profile-id"] = "1"; |
| params1["tier-flag"] = "0"; |
| params2["tier-flag"] = "4"; |
| EXPECT_FALSE(H265IsSameTier(params1, params2)); |
| } |
| |
| TEST(H265ProfileTierLevel, TestGetSupportedH265Level) { |
| // Test with 720p at 30fps |
| Resolution r{.width = 1280, .height = 720}; |
| EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1), |
| H265Level::kLevel3); |
| |
| // Test with QCIF at 15fps |
| r.width = 176; |
| r.height = 144; |
| EXPECT_EQ(GetSupportedH265Level(r, 15).value_or(H265Level::kLevel2), |
| H265Level::kLevel1); |
| |
| // Test with 1080p at 30fps |
| r.width = 1920; |
| r.height = 1080; |
| EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1), |
| H265Level::kLevel3_1); |
| |
| // Test with 1080p at 60fps |
| EXPECT_EQ(GetSupportedH265Level(r, 60).value_or(H265Level::kLevel1), |
| H265Level::kLevel3_1); |
| |
| // Test with 4K at 30fps |
| r.width = 3840; |
| r.height = 2160; |
| EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1), |
| H265Level::kLevel4_1); |
| |
| // Test with 4K at 60fps |
| EXPECT_EQ(GetSupportedH265Level(r, 60).value_or(H265Level::kLevel1), |
| H265Level::kLevel4_1); |
| |
| // Test with 8K at 30fps |
| r.width = 8192; |
| r.height = 4320; |
| EXPECT_EQ(GetSupportedH265Level(r, 30).value_or(H265Level::kLevel1), |
| H265Level::kLevel6); |
| |
| // Test with 64x64 at 30fps |
| r.width = 64; |
| r.height = 64; |
| EXPECT_EQ(GetSupportedH265Level(r, 30), std::nullopt); |
| |
| // Test with extremly large width or height at 15fps |
| r.width = 16928; |
| r.height = 64; |
| EXPECT_EQ(GetSupportedH265Level(r, 15), std::nullopt); |
| } |
| |
| } // namespace webrtc |