codec_comparators_unittest.cc (27584B)
1 /* 2 * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 #include "media/base/codec_comparators.h" 11 12 #include <string> 13 14 #include "api/audio_codecs/audio_format.h" 15 #include "api/rtp_parameters.h" 16 #include "api/video_codecs/scalability_mode.h" 17 #include "api/video_codecs/sdp_video_format.h" 18 #include "api/video_codecs/vp9_profile.h" 19 #include "media/base/codec.h" 20 #include "media/base/media_constants.h" 21 #include "test/gtest.h" 22 23 namespace webrtc { 24 25 using ::testing::TestWithParam; 26 using ::testing::ValuesIn; 27 28 TEST(CodecComparatorsTest, CodecMatchesItself) { 29 Codec codec = CreateVideoCodec("custom"); 30 EXPECT_TRUE(MatchesWithCodecRules(codec, codec)); 31 } 32 33 TEST(CodecComparatorsTest, MismatchedBasicParameters) { 34 Codec codec = CreateAudioCodec(SdpAudioFormat("opus", 48000, 2)); 35 Codec nonmatch_codec = codec; 36 nonmatch_codec.name = "g711"; 37 EXPECT_FALSE(MatchesWithCodecRules(nonmatch_codec, codec)); 38 nonmatch_codec = codec; 39 nonmatch_codec.clockrate = 8000; 40 EXPECT_FALSE(MatchesWithCodecRules(nonmatch_codec, codec)); 41 nonmatch_codec = codec; 42 nonmatch_codec.channels = 1; 43 EXPECT_FALSE(MatchesWithCodecRules(nonmatch_codec, codec)); 44 } 45 46 TEST(CodecComparatorsTest, H264PacketizationModeMismatch) { 47 Codec pt_mode_1 = CreateVideoCodec(kH264CodecName); 48 Codec pt_mode_0 = pt_mode_1; 49 pt_mode_0.SetParam(kH264FmtpPacketizationMode, "0"); 50 EXPECT_FALSE(MatchesWithCodecRules(pt_mode_1, pt_mode_0)); 51 EXPECT_FALSE(MatchesWithCodecRules(pt_mode_0, pt_mode_1)); 52 Codec no_pt_mode = pt_mode_1; 53 no_pt_mode.RemoveParam(kH264FmtpPacketizationMode); 54 EXPECT_TRUE(MatchesWithCodecRules(pt_mode_0, no_pt_mode)); 55 EXPECT_TRUE(MatchesWithCodecRules(no_pt_mode, pt_mode_0)); 56 EXPECT_FALSE(MatchesWithCodecRules(no_pt_mode, pt_mode_1)); 57 } 58 59 TEST(CodecComparatorsTest, AudioParametersIgnored) { 60 // Currently, all parameters on audio codecs are ignored for matching. 61 Codec basic_opus = CreateAudioCodec(SdpAudioFormat("opus", 48000, 2)); 62 Codec opus_with_parameters = basic_opus; 63 opus_with_parameters.SetParam("stereo", "0"); 64 EXPECT_TRUE(MatchesWithCodecRules(basic_opus, opus_with_parameters)); 65 EXPECT_TRUE(MatchesWithCodecRules(opus_with_parameters, basic_opus)); 66 opus_with_parameters.SetParam("nonsense", "stuff"); 67 EXPECT_TRUE(MatchesWithCodecRules(basic_opus, opus_with_parameters)); 68 EXPECT_TRUE(MatchesWithCodecRules(opus_with_parameters, basic_opus)); 69 } 70 71 TEST(CodecComparatorsTest, StaticPayloadTypesIgnoreName) { 72 // This is the IANA registered format for PT 8 73 Codec codec_1 = CreateAudioCodec(8, "pcma", 8000, 1); 74 Codec codec_2 = CreateAudioCodec(8, "nonsense", 8000, 1); 75 EXPECT_TRUE(MatchesWithCodecRules(codec_1, codec_2)); 76 } 77 78 TEST(CodecComparatorsTest, MatchesWithReferenceAttributesRed) { 79 // Test that RED codecs' reference attributes get parsed correctly. 80 Codec codec_1 = CreateAudioCodec(101, kRedCodecName, 48000, 2); 81 codec_1.SetParam(kCodecParamNotInNameValueFormat, "100/100"); 82 Codec codec_2 = CreateAudioCodec(102, kRedCodecName, 48000, 2); 83 codec_2.SetParam(kCodecParamNotInNameValueFormat, "101/101"); 84 // Mixed codecs in RED 85 Codec codec_3 = CreateAudioCodec(103, kRedCodecName, 48000, 2); 86 codec_3.SetParam(kCodecParamNotInNameValueFormat, "100/101"); 87 // Identical codecs always match. 88 EXPECT_TRUE(MatchesWithReferenceAttributes(codec_1, codec_1)); 89 EXPECT_TRUE(MatchesWithReferenceAttributes(codec_2, codec_2)); 90 EXPECT_TRUE(MatchesWithReferenceAttributes(codec_3, codec_3)); 91 // Mismatched reference codec lists. 92 EXPECT_FALSE(MatchesWithReferenceAttributes(codec_1, codec_2)); 93 EXPECT_FALSE(MatchesWithReferenceAttributes(codec_1, codec_3)); 94 EXPECT_FALSE(MatchesWithReferenceAttributes(codec_2, codec_3)); 95 // Overflow of longer lists are ignored. 96 // Overlong list - overflow should be ignored. 97 Codec codec_4 = CreateAudioCodec(103, kRedCodecName, 48000, 2); 98 codec_4.SetParam(kCodecParamNotInNameValueFormat, "100/100/101/102"); 99 EXPECT_TRUE(MatchesWithReferenceAttributes(codec_4, codec_4)); 100 EXPECT_TRUE(MatchesWithReferenceAttributes(codec_1, codec_4)); 101 // Broken syntax will cause a non-match with anything except itself. 102 Codec codec_5 = CreateAudioCodec(103, kRedCodecName, 48000, 2); 103 codec_5.SetParam(kCodecParamNotInNameValueFormat, ""); 104 EXPECT_TRUE(MatchesWithReferenceAttributes(codec_5, codec_5)); 105 EXPECT_FALSE(MatchesWithReferenceAttributes(codec_1, codec_5)); 106 } 107 108 struct TestParams { 109 std::string name; 110 SdpVideoFormat codec1; 111 SdpVideoFormat codec2; 112 bool expected_result; 113 }; 114 115 using IsSameRtpCodecTest = TestWithParam<TestParams>; 116 117 TEST_P(IsSameRtpCodecTest, IsSameRtpCodec) { 118 TestParams param = GetParam(); 119 Codec codec1 = CreateVideoCodec(param.codec1); 120 Codec codec2 = CreateVideoCodec(param.codec2); 121 122 EXPECT_EQ(IsSameRtpCodec(codec1, codec2.ToCodecParameters()), 123 param.expected_result); 124 } 125 126 INSTANTIATE_TEST_SUITE_P( 127 CodecTest, 128 IsSameRtpCodecTest, 129 ValuesIn<TestParams>({ 130 {.name = "CodecWithDifferentName", 131 .codec1 = {"VP9", {}}, 132 .codec2 = {"VP8", {}}, 133 .expected_result = false}, 134 {.name = "Vp8WithoutParameters", 135 .codec1 = {"vp8", {}}, 136 .codec2 = {"VP8", {}}, 137 .expected_result = true}, 138 {.name = "Vp8WithSameParameters", 139 .codec1 = {"VP8", {{"x", "1"}}}, 140 .codec2 = {"VP8", {{"x", "1"}}}, 141 .expected_result = true}, 142 {.name = "Vp8WithDifferentParameters", 143 .codec1 = {"VP8", {}}, 144 .codec2 = {"VP8", {{"x", "1"}}}, 145 .expected_result = false}, 146 {.name = "Av1WithoutParameters", 147 .codec1 = {"AV1", {}}, 148 .codec2 = {"AV1", {}}, 149 .expected_result = true}, 150 {.name = "Av1WithSameProfile", 151 .codec1 = {"AV1", SdpVideoFormat::AV1Profile0().parameters}, 152 .codec2 = {"AV1", SdpVideoFormat::AV1Profile0().parameters}, 153 .expected_result = true}, 154 {.name = "Av1WithoutParametersTreatedAsProfile0", 155 .codec1 = {"AV1", SdpVideoFormat::AV1Profile0().parameters}, 156 .codec2 = {"AV1", {}}, 157 .expected_result = true}, 158 {.name = "Av1WithoutProfileTreatedAsProfile0", 159 .codec1 = {"AV1", {{kAv1FmtpProfile, "0"}, {"x", "1"}}}, 160 .codec2 = {"AV1", {{"x", "1"}}}, 161 .expected_result = true}, 162 {.name = "Av1WithDifferentProfile", 163 .codec1 = {"AV1", SdpVideoFormat::AV1Profile0().parameters}, 164 .codec2 = {"AV1", SdpVideoFormat::AV1Profile1().parameters}, 165 .expected_result = false}, 166 {.name = "Av1WithDifferentParameters", 167 .codec1 = {"AV1", {{kAv1FmtpProfile, "0"}, {"x", "1"}}}, 168 .codec2 = {"AV1", {{kAv1FmtpProfile, "0"}, {"x", "2"}}}, 169 .expected_result = false}, 170 {.name = "Vp9WithSameProfile", 171 .codec1 = {"VP9", SdpVideoFormat::VP9Profile0().parameters}, 172 .codec2 = {"VP9", SdpVideoFormat::VP9Profile0().parameters}, 173 .expected_result = true}, 174 {.name = "Vp9WithoutProfileTreatedAsProfile0", 175 .codec1 = {"VP9", {{kVP9FmtpProfileId, "0"}, {"x", "1"}}}, 176 .codec2 = {"VP9", {{"x", "1"}}}, 177 .expected_result = true}, 178 {.name = "Vp9WithDifferentProfile", 179 .codec1 = {"VP9", SdpVideoFormat::VP9Profile0().parameters}, 180 .codec2 = {"VP9", SdpVideoFormat::VP9Profile1().parameters}, 181 .expected_result = false}, 182 {.name = "H264WithSamePacketizationMode", 183 .codec1 = {"H264", {{kH264FmtpPacketizationMode, "0"}}}, 184 .codec2 = {"H264", {{kH264FmtpPacketizationMode, "0"}}}, 185 .expected_result = true}, 186 {.name = "H264WithoutPacketizationModeTreatedAsMode0", 187 .codec1 = {"H264", {{kH264FmtpPacketizationMode, "0"}, {"x", "1"}}}, 188 .codec2 = {"H264", {{"x", "1"}}}, 189 .expected_result = true}, 190 {.name = "H264WithDifferentPacketizationMode", 191 .codec1 = {"H264", {{kH264FmtpPacketizationMode, "0"}}}, 192 .codec2 = {"H264", {{kH264FmtpPacketizationMode, "1"}}}, 193 .expected_result = false}, 194 #ifdef RTC_ENABLE_H265 195 {.name = "H265WithSameProfile", 196 .codec1 = {"H265", 197 {{kH265FmtpProfileId, "1"}, 198 {kH265FmtpTierFlag, "0"}, 199 {kH265FmtpLevelId, "93"}, 200 {kH265FmtpTxMode, "SRST"}}}, 201 .codec2 = {"H265", 202 {{kH265FmtpProfileId, "1"}, 203 {kH265FmtpTierFlag, "0"}, 204 {kH265FmtpLevelId, "93"}, 205 {kH265FmtpTxMode, "SRST"}}}, 206 .expected_result = true}, 207 {.name = "H265WithoutParametersTreatedAsDefault", 208 .codec1 = {"H265", 209 {{kH265FmtpProfileId, "1"}, 210 {kH265FmtpTierFlag, "0"}, 211 {kH265FmtpLevelId, "93"}, 212 {kH265FmtpTxMode, "SRST"}}}, 213 .codec2 = {"H265", {}}, 214 .expected_result = true}, 215 {.name = "H265WithDifferentProfile", 216 .codec1 = {"H265", 217 {{kH265FmtpProfileId, "1"}, 218 {kH265FmtpTierFlag, "0"}, 219 {kH265FmtpLevelId, "93"}, 220 {kH265FmtpTxMode, "SRST"}}}, 221 .codec2 = {"H265", 222 {{kH265FmtpProfileId, "1"}, 223 {kH265FmtpTierFlag, "1"}, 224 {kH265FmtpLevelId, "93"}, 225 {kH265FmtpTxMode, "SRST"}}}, 226 .expected_result = false}, 227 #endif 228 }), 229 [](const testing::TestParamInfo<IsSameRtpCodecTest::ParamType>& info) { 230 return info.param.name; 231 }); 232 233 // For H264, the profile and level IDs are entangled into a single 234 // "profile-level-id" attribute, so let's test many different versions. 235 // See https://cconcolato.github.io/media-mime-support/ for inspiration. 236 TEST(IsSameRtpCodecIgnoringLevelTest, IgnoresH264Levels) { 237 // AVC Baseline Level 3.1 238 Codec baseline_3_1 = 239 CreateVideoCodec(SdpVideoFormat("H264", 240 {{kH264FmtpLevelAsymmetryAllowed, "1"}, 241 {kH264FmtpPacketizationMode, "1"}, 242 {kH264FmtpProfileLevelId, "42001f"}}, 243 {ScalabilityMode::kL1T1})); 244 // AVC Baseline Level 5.2 245 Codec baseline_5_2 = 246 CreateVideoCodec(SdpVideoFormat("H264", 247 {{kH264FmtpLevelAsymmetryAllowed, "1"}, 248 {kH264FmtpPacketizationMode, "1"}, 249 {kH264FmtpProfileLevelId, "420034"}}, 250 {ScalabilityMode::kL1T1})); 251 // AVC High Level 3.1 252 Codec high_3_1 = 253 CreateVideoCodec(SdpVideoFormat("H264", 254 {{kH264FmtpLevelAsymmetryAllowed, "1"}, 255 {kH264FmtpPacketizationMode, "1"}, 256 {kH264FmtpProfileLevelId, "64001f"}}, 257 {ScalabilityMode::kL1T1})); 258 // AVC High Level 5.2 259 Codec high_5_2 = 260 CreateVideoCodec(SdpVideoFormat("H264", 261 {{kH264FmtpLevelAsymmetryAllowed, "1"}, 262 {kH264FmtpPacketizationMode, "1"}, 263 {kH264FmtpProfileLevelId, "640034"}}, 264 {ScalabilityMode::kL1T1})); 265 // AVC High 4:4:4 Predictive Level 3.1 266 Codec high_444_predictive_3_1 = 267 CreateVideoCodec(SdpVideoFormat("H264", 268 {{kH264FmtpLevelAsymmetryAllowed, "1"}, 269 {kH264FmtpPacketizationMode, "1"}, 270 {kH264FmtpProfileLevelId, "f4001f"}}, 271 {ScalabilityMode::kL1T1})); 272 273 // AVC Baseline Level 5.2 is compatible with AVC Baseline Level 3.1. 274 EXPECT_TRUE(IsSameRtpCodecIgnoringLevel(baseline_5_2, 275 baseline_3_1.ToCodecParameters())); 276 // AVC High is NOT compatible with AVC Baseline. 277 EXPECT_FALSE( 278 IsSameRtpCodecIgnoringLevel(baseline_3_1, high_3_1.ToCodecParameters())); 279 EXPECT_FALSE( 280 IsSameRtpCodecIgnoringLevel(baseline_3_1, high_5_2.ToCodecParameters())); 281 EXPECT_FALSE( 282 IsSameRtpCodecIgnoringLevel(baseline_5_2, high_3_1.ToCodecParameters())); 283 EXPECT_FALSE( 284 IsSameRtpCodecIgnoringLevel(baseline_5_2, high_5_2.ToCodecParameters())); 285 // AVC High 5.2 is compatible with AVC High 3.1 286 EXPECT_TRUE( 287 IsSameRtpCodecIgnoringLevel(high_5_2, high_3_1.ToCodecParameters())); 288 // 4:4:4 Predictive is NOT compatible with either High or Baseline. 289 EXPECT_FALSE(IsSameRtpCodecIgnoringLevel(high_444_predictive_3_1, 290 high_3_1.ToCodecParameters())); 291 EXPECT_FALSE(IsSameRtpCodecIgnoringLevel(high_444_predictive_3_1, 292 high_5_2.ToCodecParameters())); 293 EXPECT_FALSE(IsSameRtpCodecIgnoringLevel(high_444_predictive_3_1, 294 baseline_3_1.ToCodecParameters())); 295 EXPECT_FALSE(IsSameRtpCodecIgnoringLevel(high_444_predictive_3_1, 296 baseline_3_1.ToCodecParameters())); 297 } 298 299 #ifdef RTC_ENABLE_H265 300 // For H265, the "profile-id" and "level-id" are separate so test can be simple. 301 // The level-id value for Level X.Y is calculated as (X * 10 + Y) * 3. 302 // The lowest Level, 1.0, is thus (1 * 10 + 0) * 3 = 30. 303 TEST(IsSameRtpCodecIgnoringLevelTest, IgnoresH265Levels) { 304 // Profile 1, Level 5.2 305 Codec profile_1_level_5_2 = 306 CreateVideoCodec(SdpVideoFormat("H265", 307 {{kH265FmtpProfileId, "1"}, 308 {kH265FmtpTierFlag, "0"}, 309 {kH265FmtpLevelId, "156"}, 310 {kH265FmtpTxMode, "SRST"}}, 311 {ScalabilityMode::kL1T1})); 312 // Profile 1, Level 6.0 313 Codec profile_1_level_6_0 = 314 CreateVideoCodec(SdpVideoFormat("H265", 315 {{kH265FmtpProfileId, "1"}, 316 {kH265FmtpTierFlag, "0"}, 317 {kH265FmtpLevelId, "180"}, 318 {kH265FmtpTxMode, "SRST"}}, 319 {ScalabilityMode::kL1T1})); 320 // Profile 2, Level 6.0 321 Codec profile_2_level_6_0 = 322 CreateVideoCodec(SdpVideoFormat("H265", 323 {{kH265FmtpProfileId, "2"}, 324 {kH265FmtpTierFlag, "0"}, 325 {kH265FmtpLevelId, "180"}, 326 {kH265FmtpTxMode, "SRST"}}, 327 {ScalabilityMode::kL1T1})); 328 // Profile 1 codecs are compatible with each other. 329 EXPECT_TRUE(IsSameRtpCodecIgnoringLevel( 330 profile_1_level_5_2, profile_1_level_6_0.ToCodecParameters())); 331 // Profile 2 codecs are NOT compatible with profile 1 codecs. 332 EXPECT_FALSE(IsSameRtpCodecIgnoringLevel( 333 profile_2_level_6_0, profile_1_level_5_2.ToCodecParameters())); 334 EXPECT_FALSE(IsSameRtpCodecIgnoringLevel( 335 profile_2_level_6_0, profile_1_level_6_0.ToCodecParameters())); 336 } 337 #endif // RTC_ENABLE_H265 338 339 TEST(CodecTest, TestCodecMatches) { 340 // Test a codec with a static payload type. 341 Codec c0 = CreateAudioCodec(34, "A", 44100, 1); 342 EXPECT_TRUE(c0.Matches(CreateAudioCodec(34, "", 44100, 1))); 343 EXPECT_TRUE(c0.Matches(CreateAudioCodec(34, "", 44100, 0))); 344 EXPECT_TRUE(c0.Matches(CreateAudioCodec(34, "", 44100, 0))); 345 EXPECT_FALSE(c0.Matches(CreateAudioCodec(96, "A", 44100, 1))); 346 EXPECT_FALSE(c0.Matches(CreateAudioCodec(96, "", 44100, 1))); 347 EXPECT_FALSE(c0.Matches(CreateAudioCodec(95, "", 55100, 1))); 348 EXPECT_FALSE(c0.Matches(CreateAudioCodec(95, "", 44100, 1))); 349 EXPECT_FALSE(c0.Matches(CreateAudioCodec(95, "", 44100, 2))); 350 EXPECT_FALSE(c0.Matches(CreateAudioCodec(95, "", 55100, 2))); 351 352 // Test a codec with a dynamic payload type. 353 Codec c1 = CreateAudioCodec(96, "A", 44100, 1); 354 EXPECT_TRUE(c1.Matches(CreateAudioCodec(96, "A", 44100, 0))); 355 EXPECT_TRUE(c1.Matches(CreateAudioCodec(97, "A", 44100, 0))); 356 EXPECT_TRUE(c1.Matches(CreateAudioCodec(96, "a", 44100, 0))); 357 EXPECT_TRUE(c1.Matches(CreateAudioCodec(97, "a", 44100, 0))); 358 EXPECT_TRUE(c1.Matches(CreateAudioCodec(35, "a", 44100, 0))); 359 EXPECT_TRUE(c1.Matches(CreateAudioCodec(42, "a", 44100, 0))); 360 EXPECT_TRUE(c1.Matches(CreateAudioCodec(65, "a", 44100, 0))); 361 EXPECT_FALSE(c1.Matches(CreateAudioCodec(95, "A", 44100, 0))); 362 EXPECT_FALSE(c1.Matches(CreateAudioCodec(34, "A", 44100, 0))); 363 EXPECT_FALSE(c1.Matches(CreateAudioCodec(96, "", 44100, 2))); 364 EXPECT_FALSE(c1.Matches(CreateAudioCodec(96, "A", 55100, 1))); 365 366 // Test a codec with a dynamic payload type, and auto bitrate. 367 Codec c2 = CreateAudioCodec(97, "A", 16000, 1); 368 // Use default bitrate. 369 EXPECT_TRUE(c2.Matches(CreateAudioCodec(97, "A", 16000, 1))); 370 EXPECT_TRUE(c2.Matches(CreateAudioCodec(97, "A", 16000, 0))); 371 // Use explicit bitrate. 372 EXPECT_TRUE(c2.Matches(CreateAudioCodec(97, "A", 16000, 1))); 373 // Backward compatibility with clients that might send "-1" (for default). 374 EXPECT_TRUE(c2.Matches(CreateAudioCodec(97, "A", 16000, 1))); 375 376 // Stereo doesn't match channels = 0. 377 Codec c3 = CreateAudioCodec(96, "A", 44100, 2); 378 EXPECT_TRUE(c3.Matches(CreateAudioCodec(96, "A", 44100, 2))); 379 EXPECT_FALSE(c3.Matches(CreateAudioCodec(96, "A", 44100, 1))); 380 EXPECT_FALSE(c3.Matches(CreateAudioCodec(96, "A", 44100, 0))); 381 } 382 383 TEST(CodecTest, TestOpusAudioCodecWithDifferentParameters) { 384 Codec opus_with_fec = CreateAudioCodec(96, "opus", 48000, 2); 385 opus_with_fec.params["useinbandfec"] = "1"; 386 Codec opus_without_fec = CreateAudioCodec(96, "opus", 48000, 2); 387 388 EXPECT_TRUE(opus_with_fec != opus_without_fec); 389 // Matches does not compare parameters for audio. 390 EXPECT_TRUE(opus_with_fec.Matches(opus_without_fec)); 391 392 RtpCodecParameters rtp_opus_with_fec = opus_with_fec.ToCodecParameters(); 393 // MatchesRtpCodec takes parameters into account. 394 EXPECT_TRUE(opus_with_fec.MatchesRtpCodec(rtp_opus_with_fec)); 395 EXPECT_FALSE(opus_without_fec.MatchesRtpCodec(rtp_opus_with_fec)); 396 } 397 398 TEST(CodecTest, TestVideoCodecMatches) { 399 // Test a codec with a static payload type. 400 Codec c0 = CreateVideoCodec(34, "V"); 401 EXPECT_TRUE(c0.Matches(CreateVideoCodec(34, ""))); 402 EXPECT_FALSE(c0.Matches(CreateVideoCodec(96, ""))); 403 EXPECT_FALSE(c0.Matches(CreateVideoCodec(96, "V"))); 404 405 // Test a codec with a dynamic payload type. 406 Codec c1 = CreateVideoCodec(96, "V"); 407 EXPECT_TRUE(c1.Matches(CreateVideoCodec(96, "V"))); 408 EXPECT_TRUE(c1.Matches(CreateVideoCodec(97, "V"))); 409 EXPECT_TRUE(c1.Matches(CreateVideoCodec(96, "v"))); 410 EXPECT_TRUE(c1.Matches(CreateVideoCodec(97, "v"))); 411 EXPECT_TRUE(c1.Matches(CreateVideoCodec(35, "v"))); 412 EXPECT_TRUE(c1.Matches(CreateVideoCodec(42, "v"))); 413 EXPECT_TRUE(c1.Matches(CreateVideoCodec(65, "v"))); 414 EXPECT_FALSE(c1.Matches(CreateVideoCodec(96, ""))); 415 EXPECT_FALSE(c1.Matches(CreateVideoCodec(95, "V"))); 416 EXPECT_FALSE(c1.Matches(CreateVideoCodec(34, "V"))); 417 } 418 419 TEST(CodecTest, TestVideoCodecMatchesWithDifferentPacketization) { 420 Codec c0 = CreateVideoCodec(100, kVp8CodecName); 421 Codec c1 = CreateVideoCodec(101, kVp8CodecName); 422 c1.packetization = "raw"; 423 424 EXPECT_TRUE(c0.Matches(c1)); 425 EXPECT_TRUE(c1.Matches(c0)); 426 } 427 428 // AV1 codecs do not compare profile information. 429 TEST(CodecTest, TestAV1CodecMatches) { 430 const char kProfile0[] = "0"; 431 const char kProfile1[] = "1"; 432 const char kProfile2[] = "2"; 433 434 Codec c_no_profile = CreateVideoCodec(95, kAv1CodecName); 435 Codec c_profile0 = CreateVideoCodec(95, kAv1CodecName); 436 c_profile0.params[kAv1FmtpProfile] = kProfile0; 437 Codec c_profile1 = CreateVideoCodec(95, kAv1CodecName); 438 c_profile1.params[kAv1FmtpProfile] = kProfile1; 439 Codec c_profile2 = CreateVideoCodec(95, kAv1CodecName); 440 c_profile2.params[kAv1FmtpProfile] = kProfile2; 441 442 // An AV1 entry with no profile specified should be treated as profile-0. 443 EXPECT_TRUE(c_profile0.Matches(c_no_profile)); 444 445 { 446 // Two AV1 entries without a profile specified are treated as duplicates. 447 Codec c_no_profile_eq = CreateVideoCodec(95, kAv1CodecName); 448 EXPECT_TRUE(c_no_profile.Matches(c_no_profile_eq)); 449 } 450 451 { 452 // Two AV1 entries with profile 0 specified are treated as duplicates. 453 Codec c_profile0_eq = CreateVideoCodec(95, kAv1CodecName); 454 c_profile0_eq.params[kAv1FmtpProfile] = kProfile0; 455 EXPECT_TRUE(c_profile0.Matches(c_profile0_eq)); 456 } 457 458 { 459 // Two AV1 entries with profile 1 specified are treated as duplicates. 460 Codec c_profile1_eq = CreateVideoCodec(95, kAv1CodecName); 461 c_profile1_eq.params[kAv1FmtpProfile] = kProfile1; 462 EXPECT_TRUE(c_profile1.Matches(c_profile1_eq)); 463 } 464 465 // AV1 entries with different profiles (0 and 1) are seen as distinct. 466 EXPECT_FALSE(c_profile0.Matches(c_profile1)); 467 EXPECT_FALSE(c_no_profile.Matches(c_profile1)); 468 469 // AV1 entries with different profiles (0 and 2) are seen as distinct. 470 EXPECT_FALSE(c_profile0.Matches(c_profile2)); 471 EXPECT_FALSE(c_no_profile.Matches(c_profile2)); 472 473 // AV1 entries with same profile and different tier are seen as equal. 474 Codec c_tier0 = CreateVideoCodec(95, kAv1CodecName); 475 c_tier0.params[kAv1FmtpProfile] = kProfile0; 476 c_tier0.params[kAv1FmtpTier] = "0"; 477 Codec c_tier1 = CreateVideoCodec(95, kAv1CodecName); 478 c_tier1.params[kAv1FmtpProfile] = kProfile0; 479 c_tier1.params[kAv1FmtpTier] = "1"; 480 EXPECT_TRUE(c_tier0.Matches(c_tier1)); 481 482 // AV1 entries with profile and different level are seen as equal. 483 Codec c_level0 = CreateVideoCodec(95, kAv1CodecName); 484 c_level0.params[kAv1FmtpProfile] = kProfile0; 485 c_level0.params[kAv1FmtpLevelIdx] = "0"; 486 Codec c_level1 = CreateVideoCodec(95, kAv1CodecName); 487 c_level1.params[kAv1FmtpProfile] = kProfile0; 488 c_level1.params[kAv1FmtpLevelIdx] = "1"; 489 EXPECT_TRUE(c_level0.Matches(c_level1)); 490 } 491 492 // VP9 codecs compare profile information. 493 TEST(CodecTest, TestVP9CodecMatches) { 494 const char kProfile0[] = "0"; 495 const char kProfile2[] = "2"; 496 497 Codec c_no_profile = CreateVideoCodec(95, kVp9CodecName); 498 Codec c_profile0 = CreateVideoCodec(95, kVp9CodecName); 499 c_profile0.params[kVP9FmtpProfileId] = kProfile0; 500 501 EXPECT_TRUE(c_profile0.Matches(c_no_profile)); 502 503 { 504 Codec c_profile0_eq = CreateVideoCodec(95, kVp9CodecName); 505 c_profile0_eq.params[kVP9FmtpProfileId] = kProfile0; 506 EXPECT_TRUE(c_profile0.Matches(c_profile0_eq)); 507 } 508 509 { 510 Codec c_profile2 = CreateVideoCodec(95, kVp9CodecName); 511 c_profile2.params[kVP9FmtpProfileId] = kProfile2; 512 EXPECT_FALSE(c_profile0.Matches(c_profile2)); 513 EXPECT_FALSE(c_no_profile.Matches(c_profile2)); 514 } 515 516 { 517 Codec c_no_profile_eq = CreateVideoCodec(95, kVp9CodecName); 518 EXPECT_TRUE(c_no_profile.Matches(c_no_profile_eq)); 519 } 520 } 521 522 // Matching H264 codecs also need to have matching profile-level-id and 523 // packetization-mode. 524 TEST(CodecTest, TestH264CodecMatches) { 525 const char kProfileLevelId1[] = "42e01f"; 526 const char kProfileLevelId2[] = "42a01e"; 527 const char kProfileLevelId3[] = "42e01e"; 528 529 Codec pli_1_pm_0 = CreateVideoCodec(95, "H264"); 530 pli_1_pm_0.params[kH264FmtpProfileLevelId] = kProfileLevelId1; 531 pli_1_pm_0.params[kH264FmtpPacketizationMode] = "0"; 532 533 { 534 Codec pli_1_pm_blank = CreateVideoCodec(95, "H264"); 535 pli_1_pm_blank.params[kH264FmtpProfileLevelId] = kProfileLevelId1; 536 pli_1_pm_blank.params.erase( 537 pli_1_pm_blank.params.find(kH264FmtpPacketizationMode)); 538 539 // Matches since if packetization-mode is not specified it defaults to "0". 540 EXPECT_TRUE(pli_1_pm_0.Matches(pli_1_pm_blank)); 541 542 // MatchesRtpCodec does exact comparison of parameters. 543 EXPECT_FALSE( 544 pli_1_pm_0.MatchesRtpCodec(pli_1_pm_blank.ToCodecParameters())); 545 } 546 547 { 548 Codec pli_1_pm_1 = CreateVideoCodec(95, "H264"); 549 pli_1_pm_1.params[kH264FmtpProfileLevelId] = kProfileLevelId1; 550 pli_1_pm_1.params[kH264FmtpPacketizationMode] = "1"; 551 552 // Does not match since packetization-mode is different. 553 EXPECT_FALSE(pli_1_pm_0.Matches(pli_1_pm_1)); 554 555 EXPECT_FALSE(pli_1_pm_0.MatchesRtpCodec(pli_1_pm_1.ToCodecParameters())); 556 } 557 558 { 559 Codec pli_2_pm_0 = CreateVideoCodec(95, "H264"); 560 pli_2_pm_0.params[kH264FmtpProfileLevelId] = kProfileLevelId2; 561 pli_2_pm_0.params[kH264FmtpPacketizationMode] = "0"; 562 563 // Does not match since profile-level-id is different. 564 EXPECT_FALSE(pli_1_pm_0.Matches(pli_2_pm_0)); 565 566 EXPECT_FALSE(pli_1_pm_0.MatchesRtpCodec(pli_2_pm_0.ToCodecParameters())); 567 } 568 569 { 570 Codec pli_3_pm_0_asym = CreateVideoCodec(95, "H264"); 571 pli_3_pm_0_asym.params[kH264FmtpProfileLevelId] = kProfileLevelId3; 572 pli_3_pm_0_asym.params[kH264FmtpPacketizationMode] = "0"; 573 574 // Does match, profile-level-id is different but the level is not compared. 575 // and the profile matches. 576 EXPECT_TRUE(pli_1_pm_0.Matches(pli_3_pm_0_asym)); 577 578 EXPECT_FALSE( 579 pli_1_pm_0.MatchesRtpCodec(pli_3_pm_0_asym.ToCodecParameters())); 580 581 // 582 } 583 } 584 585 #ifdef RTC_ENABLE_H265 586 // Matching H.265 codecs should have matching profile/tier/level and tx-mode. 587 TEST(CodecTest, TestH265CodecMatches) { 588 constexpr char kProfile1[] = "1"; 589 constexpr char kTier1[] = "1"; 590 constexpr char kLevel3_1[] = "93"; 591 constexpr char kLevel4[] = "120"; 592 constexpr char kTxMrst[] = "MRST"; 593 594 Codec c_ptl_blank = CreateVideoCodec(95, kH265CodecName); 595 596 { 597 Codec c_profile_1 = CreateVideoCodec(95, kH265CodecName); 598 c_profile_1.params[kH265FmtpProfileId] = kProfile1; 599 600 // Matches since profile-id unspecified defaults to "1". 601 EXPECT_TRUE(c_ptl_blank.Matches(c_profile_1)); 602 } 603 604 { 605 Codec c_tier_flag_1 = CreateVideoCodec(95, kH265CodecName); 606 c_tier_flag_1.params[kH265FmtpTierFlag] = kTier1; 607 608 // Does not match since profile-space unspecified defaults to "0". 609 EXPECT_FALSE(c_ptl_blank.Matches(c_tier_flag_1)); 610 } 611 612 { 613 Codec c_level_id_3_1 = CreateVideoCodec(95, kH265CodecName); 614 c_level_id_3_1.params[kH265FmtpLevelId] = kLevel3_1; 615 616 // Matches since level-id unspecified defaults to "93". 617 EXPECT_TRUE(c_ptl_blank.Matches(c_level_id_3_1)); 618 } 619 620 { 621 Codec c_level_id_4 = CreateVideoCodec(95, kH265CodecName); 622 c_level_id_4.params[kH265FmtpLevelId] = kLevel4; 623 624 // Matches since we ignore level-id when matching H.265 codecs. 625 EXPECT_TRUE(c_ptl_blank.Matches(c_level_id_4)); 626 } 627 628 { 629 Codec c_tx_mode_mrst = CreateVideoCodec(95, kH265CodecName); 630 c_tx_mode_mrst.params[kH265FmtpTxMode] = kTxMrst; 631 632 // Does not match since tx-mode implies to "SRST" and must be not specified 633 // when it is the only mode supported: 634 // https://datatracker.ietf.org/doc/html/draft-ietf-avtcore-hevc-webrtc 635 EXPECT_FALSE(c_ptl_blank.Matches(c_tx_mode_mrst)); 636 } 637 } 638 #endif 639 640 TEST(CodecTest, TestMatchesRtpCodecRtx) { 641 const Codec rtx_codec_1 = CreateVideoRtxCodec(96, 120); 642 const Codec rtx_codec_2 = CreateVideoRtxCodec(96, 121); 643 EXPECT_TRUE(rtx_codec_1.Matches(rtx_codec_2)); 644 // MatchesRtpCodec ignores the different associated payload type (apt) for 645 // RTX. 646 EXPECT_TRUE(rtx_codec_1.MatchesRtpCodec(rtx_codec_2.ToCodecParameters())); 647 } 648 649 } // namespace webrtc