From 6f90609fca40da17681dcc30dcd05c678c3b70b5 Mon Sep 17 00:00:00 2001 From: Qiu Jianlin Date: Mon, 23 Sep 2024 16:12:00 +0800 Subject: [PATCH] Compare only profile & tier when matching HEVC codec. Level asymmetry is implicitly enabled for HEVC. When comparing two codec params to see if they match, we only compare profile & tier, similar as H.264. Bug: chromium:41480904 Change-Id: I9e9debdf1b34f33986da9344b9fee14071b1ed60 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/363205 Reviewed-by: Harald Alvestrand Reviewed-by: Sergey Silkin Commit-Queue: Jianlin Qiu Cr-Commit-Position: refs/heads/main@{#43069} --- api/video_codecs/h265_profile_tier_level.cc | 18 +++ api/video_codecs/h265_profile_tier_level.h | 14 ++- api/video_codecs/sdp_video_format.cc | 3 +- .../test/h265_profile_tier_level_unittest.cc | 113 +++++++++++++++++- .../test/sdp_video_format_unittest.cc | 4 +- media/base/codec.cc | 3 +- media/base/codec_unittest.cc | 4 +- media/engine/webrtc_video_engine.cc | 2 + 8 files changed, 151 insertions(+), 10 deletions(-) diff --git a/api/video_codecs/h265_profile_tier_level.cc b/api/video_codecs/h265_profile_tier_level.cc index 89eb553616..92ea0c84bc 100644 --- a/api/video_codecs/h265_profile_tier_level.cc +++ b/api/video_codecs/h265_profile_tier_level.cc @@ -285,6 +285,24 @@ bool H265IsSameProfileTierLevel(const CodecParameterMap& params1, ptl1->tier == ptl2->tier && ptl1->level == ptl2->level; } +bool H265IsSameProfile(const CodecParameterMap& params1, + const CodecParameterMap& params2) { + const std::optional ptl1 = + ParseSdpForH265ProfileTierLevel(params1); + const std::optional ptl2 = + ParseSdpForH265ProfileTierLevel(params2); + return ptl1 && ptl2 && ptl1->profile == ptl2->profile; +} + +bool H265IsSameTier(const CodecParameterMap& params1, + const CodecParameterMap& params2) { + const std::optional ptl1 = + ParseSdpForH265ProfileTierLevel(params1); + const std::optional ptl2 = + ParseSdpForH265ProfileTierLevel(params2); + return ptl1 && ptl2 && ptl1->tier == ptl2->tier; +} + std::optional GetSupportedH265Level(const Resolution& resolution, float max_fps) { int aligned_width = diff --git a/api/video_codecs/h265_profile_tier_level.h b/api/video_codecs/h265_profile_tier_level.h index d954055d40..676b1f573b 100644 --- a/api/video_codecs/h265_profile_tier_level.h +++ b/api/video_codecs/h265_profile_tier_level.h @@ -107,11 +107,21 @@ RTC_EXPORT std::optional GetSupportedH265Level( RTC_EXPORT std::optional ParseSdpForH265ProfileTierLevel( const CodecParameterMap& params); -// Returns true if the parameters have the same H265 profile or neither contains -// an H265 profile, otherwise false. +// Returns true if the parameters have the same H265 profile/tier/level or +// neither contains an H265 profile/tier/level, otherwise false. RTC_EXPORT bool H265IsSameProfileTierLevel(const CodecParameterMap& params1, const CodecParameterMap& params2); +// Returns true if the parameters have the same H265 profile, or neither +// contains an H265 profile, otherwise false. +RTC_EXPORT bool H265IsSameProfile(const CodecParameterMap& params1, + const CodecParameterMap& params2); + +// Returns true if the parameters have the same H265 tier, or neither +// contains an H265 tier, otherwise false. +RTC_EXPORT bool H265IsSameTier(const CodecParameterMap& params1, + const CodecParameterMap& params2); + } // namespace webrtc #endif // API_VIDEO_CODECS_H265_PROFILE_TIER_LEVEL_H_ diff --git a/api/video_codecs/sdp_video_format.cc b/api/video_codecs/sdp_video_format.cc index f287d63728..1986573de9 100644 --- a/api/video_codecs/sdp_video_format.cc +++ b/api/video_codecs/sdp_video_format.cc @@ -119,7 +119,8 @@ bool IsSameCodecSpecific(const std::string& name1, AV1IsSameLevelIdx(params1, params2); #ifdef RTC_ENABLE_H265 case kVideoCodecH265: - return H265IsSameProfileTierLevel(params1, params2) && + return H265IsSameProfile(params1, params2) && + H265IsSameTier(params1, params2) && IsSameH265TxMode(params1, params2); #endif default: diff --git a/api/video_codecs/test/h265_profile_tier_level_unittest.cc b/api/video_codecs/test/h265_profile_tier_level_unittest.cc index e9095e2b97..59c75f2ff3 100644 --- a/api/video_codecs/test/h265_profile_tier_level_unittest.cc +++ b/api/video_codecs/test/h265_profile_tier_level_unittest.cc @@ -231,8 +231,9 @@ TEST(H265ProfileTierLevel, TestProfileTierLevelCompare) { params2.clear(); params1["profile-id"] = "1"; params2["profile-id"] = "1"; - params1["level-id"] = "93"; - params2["level-id"] = "93"; + 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)); @@ -247,6 +248,114 @@ TEST(H265ProfileTierLevel, TestProfileTierLevelCompare) { 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}; diff --git a/api/video_codecs/test/sdp_video_format_unittest.cc b/api/video_codecs/test/sdp_video_format_unittest.cc index bf6e95a40e..13c45cd225 100644 --- a/api/video_codecs/test/sdp_video_format_unittest.cc +++ b/api/video_codecs/test/sdp_video_format_unittest.cc @@ -92,7 +92,7 @@ TEST(SdpVideoFormatTest, SameCodecNameDifferentParameters) { EXPECT_FALSE(Sdp("H265").IsSameCodec(Sdp( "H265", Params{{"profile-id", "1"}, {"tier-flag", "1"}, {"level-id", "93"}}))); - EXPECT_FALSE(Sdp("H265").IsSameCodec(Sdp( + EXPECT_TRUE(Sdp("H265").IsSameCodec(Sdp( "H265", Params{{"profile-id", "1"}, {"tier-flag", "0"}, {"level-id", "90"}}))); EXPECT_FALSE( @@ -107,7 +107,7 @@ TEST(SdpVideoFormatTest, SameCodecNameDifferentParameters) { .IsSameCodec(Sdp("H265", Params{{"profile-id", "1"}, {"tier-flag", "0"}, {"level-id", "120"}}))); - EXPECT_FALSE( + EXPECT_TRUE( Sdp("H265", Params{{"profile-id", "1"}, {"tier-flag", "0"}, {"level-id", "93"}}) .IsSameCodec(Sdp("H265", Params{{"profile-id", "1"}, diff --git a/media/base/codec.cc b/media/base/codec.cc index 0116821bf2..f3266514a6 100644 --- a/media/base/codec.cc +++ b/media/base/codec.cc @@ -122,7 +122,8 @@ bool IsSameCodecSpecific(const std::string& name1, AV1IsSameLevelIdx(params1, params2); #ifdef RTC_ENABLE_H265 if (either_name_matches(kH265CodecName)) { - return webrtc::H265IsSameProfileTierLevel(params1, params2) && + return webrtc::H265IsSameProfile(params1, params2) && + webrtc::H265IsSameTier(params1, params2) && IsSameH265TxMode(params1, params2); } #endif diff --git a/media/base/codec_unittest.cc b/media/base/codec_unittest.cc index 84aa9ff0c1..c13cf1aca0 100644 --- a/media/base/codec_unittest.cc +++ b/media/base/codec_unittest.cc @@ -420,8 +420,8 @@ TEST(CodecTest, TestH265CodecMatches) { Codec c_level_id_4 = cricket::CreateVideoCodec(95, cricket::kH265CodecName); c_level_id_4.params[cricket::kH265FmtpLevelId] = kLevel4; - // Does not match since different level-ids are specified. - EXPECT_FALSE(c_ptl_blank.Matches(c_level_id_4)); + // Matches since we ignore level-id when matching H.265 codecs. + EXPECT_TRUE(c_ptl_blank.Matches(c_level_id_4)); } { diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 9c72d044a6..314ab18581 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -1031,6 +1031,8 @@ std::vector WebRtcVideoSendChannel::SelectSendVideoCodecs( // following the spec in https://tools.ietf.org/html/rfc6184#section-8.2.2 // since we should limit the encode level to the lower of local and remote // level when level asymmetry is not allowed. + // For H.265, the level asymmetry is implicitly allowed. We need to make + // sure the encode level is set to the remote offered level. if (format_it->IsSameCodec( {remote_codec.codec.name, remote_codec.codec.params})) { encoders.push_back(remote_codec);