Fix payload type allocation issues for Audio RED and MID recycling. This CL addresses several issues identified while enabling the WebRTC-PayloadTypesInTransport field trial: - Enforce media type equality in codec matching to prevent Audio/Video RED collisions. - Relax RED matching rules to allow negotiation with unlinked RED codecs. - Validate media type during MID recycling in CodecVendor to prevent incorrect codec merging. - Update documentation with the video implementation strategy. Bug: webrtc:360058654 Change-Id: I1ecbdad2179d346b3682875d823889aa076e353e Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/453001 Reviewed-by: Henrik Boström <hbos@webrtc.org> Commit-Queue: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#47113}
diff --git a/g3doc/todo/payload_type_redesign.md b/g3doc/todo/payload_type_redesign.md new file mode 100644 index 0000000..5fa5f12 --- /dev/null +++ b/g3doc/todo/payload_type_redesign.md
@@ -0,0 +1,127 @@ +# Payload type allocation redesign + +## Background: What a payload type is + +The payload type is a property of a codec that is established between a sender +and a receiver using SDP offer/answer. + +Each end of the connection independently indicates the list of codecs it is +willing to send and/or receive, and assigns a payload type to each. It is +conventional but not required to use the same payload types in an answer as +those suggested in an offer. + +For SDP sendonly media sections, it indicates a willingness to send; for +recvonly media sections, it indicates a willingness to receive; for sendrecv +media sections, it indicates a willingness to receive AND a willingness to send. +Presence in a media section does not guarantee that it will be used. (RFC 3264) + +## Current allocation strategy + +The payload types are assigned by the VideoEngine and VoiceEngine according to a +fixed list. If the assignments collide with already established payload types, +the assignments are munged in the CodecVendor class so that there are no +colliding payload types. + +The PayloadTypeRecorder class records what payload types are assigned - there is +one case (PR-answer followed by a later Answer that has different PTs) where +they are changed, but otherwise, once the PayloadTypeRecorder has assigned them, +that PT is permanent for that transport and that direction. + +Picking a payload type is done in the PayloadTypePicker class. This is preloaded +with some assignments, so that if a PT is free, the commonly used PT for that +codec can be used. + +## Desired future strategy + +There should be no assignment of payload types in VideoEngine and VoiceEngine. +Payload types should be picked only when creating an offer or answer; they are +assigned permanently in SetLocalDescription / SetRemoteDescription. + +Once an assignment is made, it should never need to change (apart from the case +noted above). + +## Dealing with resiliency mechanisms + +Resiliency mechanisms (RED, RTX) are signaled using "codecs", and are +traditionally represented as such within the library. They associate with the +codec they are resiliency for by using the "apt=" parameter in their a=fmtp +lines. However, this complicates assignment, since that parameter cannot be +filled in until the payload type for the "protected" codec is assigned. + +## Backwards compatibility issues + +Experience has shown that changing the PT allocation strategy often trips up +applications that have depended on the result of the old allocation strategy, so +user-visible changes should be avoided. One example is that it's preferred (but +not strictly required) to have the RTX PT assigned the PT number of 1 more than +the PT of the codec it refers to. + +## Current implementation status + +The new strategy is implemented for audio codecs. Several issues that caused +test failures when enabling the `WebRTC-PayloadTypesInTransport` field trial +have been identified and fixed: + +- **Audio/Video RED Collision:** RED codecs of different media types were + incorrectly matching, leading to payload type conflicts. + `MatchesWithCodecRules` now enforces media type equality. +- **MID Recycling:** When a MID was recycled for a different media type (e.g., + Audio -> Video), `CodecVendor` was incorrectly merging codecs from the old + description. This has been fixed by validating the media type before merging. +- **RED Matching Logic:** Relaxed the matching rules for RED to allow + negotiation to proceed even when parameters (linking RED to primary codecs) + are not yet populated, as this linking now happens late in the `CodecVendor`. + +The new strategy is not yet implemented for video codecs. + +## Implementation Strategy for Video + +The goal is to transition video codec handling to the same late-assignment model +used for audio. + +### 1. Unified Codec Collection + +- Implement `VideoCodecsFromFactory` in `pc/typed_codec_vendor.cc`. This will + query the `VideoEncoderFactory` and `VideoDecoderFactory` to gather supported + `SdpVideoFormat`s. +- These codecs will be initialized with `kIdNotSet`, allowing + `SdpPayloadTypeSuggester` to assign payload types only during the creation of + an offer or answer. + +### 2. Payload Type Suggester Integration + +- Ensure `PayloadTypePicker` has a complete list of preferred payload types for + video codecs (VP8, VP9, H.264, AV1, etc.) to maintain stable and conventional + assignments. +- Update `CodecVendor` to use the unassigned video codec list when the + `WebRTC-PayloadTypesInTransport` trial is active. + +### 3. Resiliency and Parameter Linking + +- Refine `MergeCodecs` and `AssignCodecIdsAndLinkRed` to handle video-specific + resiliency: + - **RTX:** Correctly link RTX codecs to their primary video codecs by matching + names and specific parameters (like `profile-level-id` for H.264). + - **RED/ULPFEC:** Ensure parameters for video redundancy are correctly + populated once the primary codec PTs are assigned. + +### 4. Verification and Testing + +- **Integration Tests:** Enable the `WebRTC-PayloadTypesInTransport` trial in + `peerconnection_unittests` and `rtc_unittests` to identify any video-specific + regressions. +- **Stable PT Tests:** Add coverage to ensure that payload types remain stable + across renegotiations, even when the order of codecs in the transceiver + preferences changes. +- **MID Recycling:** Verify that MID recycling for video-to-audio and vice-versa + works correctly without PT collisions or crashes. + +## Desired next steps + +Analyze the current situation. (Done) + +### Test, isolate and fix failures + +The identified failures in `PeerConnectionEncodingsIntegrationTest` and +`PeerConnectionIntegrationTest` have been resolved. Next, focus on implementing +the video strategy outlined above.
diff --git a/media/base/codec_comparators.cc b/media/base/codec_comparators.cc index 9da9d4a..cb67f67 100644 --- a/media/base/codec_comparators.cc +++ b/media/base/codec_comparators.cc
@@ -163,15 +163,7 @@ potential_match.params.find(kCodecParamNotInNameValueFormat); bool has_parameters_1 = red_parameters_1 != codec_to_match.params.end(); bool has_parameters_2 = red_parameters_2 != potential_match.params.end(); - // If codec_to_match has unassigned PT and no parameter, - // we assume that it'll be assigned later and return a match. - // Note - this should be deleted. It's untidy. - if (potential_match.id == Codec::kIdNotSet && !has_parameters_2) { - return true; - } - if (codec_to_match.id == Codec::kIdNotSet && !has_parameters_1) { - return true; - } + if (has_parameters_1 && has_parameters_2) { // Different levels of redundancy between offer and answer are OK // since RED is considered to be declarative. @@ -203,7 +195,16 @@ return true; } if (!has_parameters_1 && !has_parameters_2) { - // Both parameters are missing. Happens for video RED. + return true; + } + // Exactly one lacks parameters. + // Allow match if it is an audio RED codec and at least one of the + // codecs has an unassigned payload type or they have the same ID. + if (codec_to_match.type == Codec::Type::kAudio && + codec_to_match.name == kRedCodecName && + (codec_to_match.id == Codec::kIdNotSet || + potential_match.id == Codec::kIdNotSet || + codec_to_match.id == potential_match.id)) { return true; } return false; @@ -295,6 +296,11 @@ right_codec.id <= kLowerDynamicRangeMax) || (right_codec.id >= kUpperDynamicRangeMin && right_codec.id <= kUpperDynamicRangeMax); + + if (left_codec.type != right_codec.type) { + return false; + } + bool matches_id; if ((is_id_in_dynamic_range && is_codec_id_in_dynamic_range) || left_codec.id == Codec::kIdNotSet || right_codec.id == Codec::kIdNotSet) { @@ -360,12 +366,22 @@ bool IsSameRtpCodec(const Codec& codec, const RtpCodec& rtp_codec) { RtpCodecParameters rtp_codec2 = codec.ToCodecParameters(); - return absl::EqualsIgnoreCase(rtp_codec.name, rtp_codec2.name) && - rtp_codec.kind == rtp_codec2.kind && - rtp_codec.num_channels == rtp_codec2.num_channels && - rtp_codec.clock_rate == rtp_codec2.clock_rate && - InsertDefaultParams(rtp_codec.name, rtp_codec.parameters) == - InsertDefaultParams(rtp_codec2.name, rtp_codec2.parameters); + if (!absl::EqualsIgnoreCase(rtp_codec.name, rtp_codec2.name) || + rtp_codec.kind != rtp_codec2.kind || + rtp_codec.num_channels != rtp_codec2.num_channels || + rtp_codec.clock_rate != rtp_codec2.clock_rate) { + return false; + } + + // audio/RED should ignore the parameters which specify payload types so + // can not be compared. + if (rtp_codec.kind == MediaType::AUDIO && + absl::EqualsIgnoreCase(rtp_codec.name, kRedCodecName)) { + return true; + } + + return InsertDefaultParams(rtp_codec.name, rtp_codec.parameters) == + InsertDefaultParams(rtp_codec2.name, rtp_codec2.parameters); } bool IsSameRtpCodecIgnoringLevel(const Codec& codec, @@ -393,7 +409,8 @@ } // audio/RED should ignore the parameters which specify payload types so // can not be compared. - if (rtp_codec.kind == MediaType::AUDIO && rtp_codec.name == kRedCodecName) { + if (rtp_codec.kind == MediaType::AUDIO && + absl::EqualsIgnoreCase(rtp_codec.name, kRedCodecName)) { return true; }
diff --git a/media/base/codec_comparators_unittest.cc b/media/base/codec_comparators_unittest.cc index 2f78042..0487656 100644 --- a/media/base/codec_comparators_unittest.cc +++ b/media/base/codec_comparators_unittest.cc
@@ -105,6 +105,63 @@ EXPECT_FALSE(MatchesWithReferenceAttributes(codec_1, codec_5)); } +TEST(CodecComparatorsTest, RedParametersMismatch) { + Codec with_params = CreateAudioCodec(101, kRedCodecName, 48000, 2); + with_params.SetParam(kCodecParamNotInNameValueFormat, "111/111"); + + Codec no_params = CreateAudioCodec(102, kRedCodecName, 48000, 2); + + // Case 1: Exactly one lacks parameters, both have IDs -> SHOULD NOT match for + // audio RED. + EXPECT_FALSE(MatchesWithReferenceAttributes(with_params, no_params)); + EXPECT_FALSE(MatchesWithReferenceAttributes(no_params, with_params)); + + // Case 2: Exactly one lacks parameters, the one WITHOUT parameters is + // unassigned -> SHOULD match. + // This is the case with late assignment. + Codec no_params_unassigned = no_params; + no_params_unassigned.id = Codec::kIdNotSet; + EXPECT_TRUE( + MatchesWithReferenceAttributes(with_params, no_params_unassigned)); + EXPECT_TRUE( + MatchesWithReferenceAttributes(no_params_unassigned, with_params)); + + // Case 3: Exactly one lacks parameters, the one WITH parameters is + // unassigned -> SHOULD match for audio RED because one side is unassigned. + Codec with_params_unassigned = with_params; + with_params_unassigned.id = Codec::kIdNotSet; + EXPECT_TRUE( + MatchesWithReferenceAttributes(with_params_unassigned, no_params)); + EXPECT_TRUE( + MatchesWithReferenceAttributes(no_params, with_params_unassigned)); + + // Case 4: Exactly one lacks parameters, both are unassigned -> SHOULD match. + EXPECT_TRUE(MatchesWithReferenceAttributes(with_params_unassigned, + no_params_unassigned)); + EXPECT_TRUE(MatchesWithReferenceAttributes(no_params_unassigned, + with_params_unassigned)); + + // Case 5: Both lack parameters -> SHOULD match. + Codec no_params_2 = CreateAudioCodec(103, kRedCodecName, 48000, 2); + EXPECT_TRUE(MatchesWithReferenceAttributes(no_params, no_params_2)); + + // A video RED codec should not match any audio RED codec, + // independent of parameters. + Codec video_red_codec = CreateVideoCodec(107, kRedCodecName); + EXPECT_FALSE(MatchesWithReferenceAttributes(no_params, video_red_codec)); + EXPECT_FALSE(MatchesWithReferenceAttributes(with_params, video_red_codec)); + + // Two video RED codecs with different IDs and one lacking parameters + // should NOT match. + Codec video_red_with_params = CreateVideoCodec(108, kRedCodecName); + video_red_with_params.SetParam(kCodecParamNotInNameValueFormat, "120/120"); + Codec video_red_no_params = CreateVideoCodec(109, kRedCodecName); + EXPECT_FALSE(MatchesWithReferenceAttributes(video_red_with_params, + video_red_no_params)); + EXPECT_FALSE(MatchesWithReferenceAttributes(video_red_no_params, + video_red_with_params)); +} + struct TestParams { std::string name; SdpVideoFormat codec1;
diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 9863aa6..4315b46 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn
@@ -407,6 +407,7 @@ ":typed_codec_vendor", ":used_ids", "../api:field_trials_view", + "../api:payload_type", "../api:rtc_error", "../api:rtp_parameters", "../api:rtp_transceiver_direction",
diff --git a/pc/codec_vendor.cc b/pc/codec_vendor.cc index 1d865fd..50f3ef8 100644 --- a/pc/codec_vendor.cc +++ b/pc/codec_vendor.cc
@@ -23,6 +23,7 @@ #include "absl/strings/string_view.h" #include "api/field_trials_view.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" @@ -620,7 +621,8 @@ CodecList codecs; std::string mid = media_description_options.mid; // If current content exists and is not being recycled, use its codecs. - if (current_content && current_content->mid() == mid) { + if (current_content && current_content->mid() == mid && + IsMediaContentOfType(current_content, media_description_options.type)) { RTCErrorOr<CodecList> checked_codec_list = CodecList::Create(current_content->media_description()->codecs()); if (!checked_codec_list.ok()) { @@ -758,7 +760,8 @@ RTC_LOG_THREAD_BLOCK_COUNT(); CodecList codecs; std::string mid = media_description_options.mid; - if (current_content && current_content->mid() == mid) { + if (current_content && current_content->mid() == mid && + IsMediaContentOfType(current_content, media_description_options.type)) { RTCErrorOr<CodecList> checked_codec_list = CodecList::Create(current_content->media_description()->codecs()); if (!checked_codec_list.ok()) {
diff --git a/pc/peer_connection_encodings_integrationtest.cc b/pc/peer_connection_encodings_integrationtest.cc index 6b84d6b..9b9ee25 100644 --- a/pc/peer_connection_encodings_integrationtest.cc +++ b/pc/peer_connection_encodings_integrationtest.cc
@@ -2130,6 +2130,64 @@ } TEST_F(PeerConnectionEncodingsIntegrationTest, + EncodingParametersRedEnabledBeforeNegotiationAudioWithFieldTrial) { + scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = + CreatePc("WebRTC-PayloadTypesInTransport/Enabled/"); + scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = + CreatePc("WebRTC-PayloadTypesInTransport/Enabled/"); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + std::vector<RtpCodecCapability> send_codecs = + local_pc_wrapper->pc_factory() + ->GetRtpSenderCapabilities(MediaType::AUDIO) + .codecs; + + std::optional<RtpCodecCapability> opus = + local_pc_wrapper->FindFirstSendCodecWithName(MediaType::AUDIO, "opus"); + ASSERT_TRUE(opus); + + std::optional<RtpCodecCapability> red = + local_pc_wrapper->FindFirstSendCodecWithName(MediaType::AUDIO, "red"); + ASSERT_TRUE(red); + + RtpTransceiverInit init; + init.direction = RtpTransceiverDirection::kSendOnly; + RtpEncodingParameters encoding_parameters; + encoding_parameters.codec = opus; + init.send_encodings.push_back(encoding_parameters); + + auto transceiver_or_error = + local_pc_wrapper->pc()->AddTransceiver(MediaType::AUDIO, init); + ASSERT_TRUE(transceiver_or_error.ok()); + scoped_refptr<RtpTransceiverInterface> audio_transceiver = + transceiver_or_error.MoveValue(); + + // Preferring RED over Opus should enable RED with Opus encoding. + send_codecs[0] = red.value(); + send_codecs[1] = opus.value(); + + ASSERT_TRUE(audio_transceiver->SetCodecPreferences(send_codecs).ok()); + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); + local_pc_wrapper->WaitForConnection(); + remote_pc_wrapper->WaitForConnection(); + + RtpParameters parameters = audio_transceiver->sender()->GetParameters(); + EXPECT_EQ(parameters.encodings[0].codec, opus); + EXPECT_EQ(parameters.codecs[0].name, red->name); + + // Check that it's possible to switch back to Opus without RED. + send_codecs[0] = opus.value(); + send_codecs[1] = red.value(); + + ASSERT_TRUE(audio_transceiver->SetCodecPreferences(send_codecs).ok()); + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); + + parameters = audio_transceiver->sender()->GetParameters(); + EXPECT_EQ(parameters.encodings[0].codec, opus); + EXPECT_EQ(parameters.codecs[0].name, opus->name); +} + +TEST_F(PeerConnectionEncodingsIntegrationTest, SetParametersRejectsScalabilityModeForSelectedCodec) { scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
diff --git a/pc/peer_connection_media_unittest.cc b/pc/peer_connection_media_unittest.cc index 3890be2..a4671c9 100644 --- a/pc/peer_connection_media_unittest.cc +++ b/pc/peer_connection_media_unittest.cc
@@ -1376,10 +1376,10 @@ CreateAudioCodec(120, "foo", kDefaultAudioClockRateHz, 1)); callee_fake_codecs.push_back( CreateAudioCodec(121, kRedCodecName, kDefaultAudioClockRateHz, 1)); - callee_fake_codecs.push_back( - CreateAudioCodec(122, "bar", kDefaultAudioClockRateHz, 1)); callee_fake_codecs.back().SetParam(kCodecParamNotInNameValueFormat, "122/122"); + callee_fake_codecs.push_back( + CreateAudioCodec(122, "bar", kDefaultAudioClockRateHz, 1)); auto callee_fake_engine = std::make_unique<FakeMediaEngine>(); callee_fake_engine->SetAudioCodecs(callee_fake_codecs); auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
diff --git a/pc/peer_connection_stability_integrationtest.cc b/pc/peer_connection_stability_integrationtest.cc index 8d30522..3acfd99 100644 --- a/pc/peer_connection_stability_integrationtest.cc +++ b/pc/peer_connection_stability_integrationtest.cc
@@ -949,5 +949,554 @@ ElementsAreArray(this_golden.callee_remote)); } +TEST_F(PeerConnectionIntegrationTest, + BasicOfferAnswerPayloadTypesStableWithFieldTrial) { + SetFieldTrials("WebRTC-PayloadTypesInTransport/Enabled/"); + FactorySignature factory_signature; + ASSERT_THAT(factory_signature.id(), + Not(Eq(FactorySignature::Id::kNotRecognized))); + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignalingForSdpOnly(); + caller()->AddAudioVideoTracks(); + callee()->AddAudioVideoTracks(); + // Start offer/answer exchange and wait for it to complete. + caller()->CreateAndSetAndSignalOffer(); + + ASSERT_THAT( + WaitUntil([&] { return SignalingStateStable(); }, ::testing::IsTrue()), + IsRtcOk()); + + // Extract PT and codec from all media sections, and check that they + // are stable (what was expected). + // Maintenance: In order to get a new golden set of strings, make the list + // empty and run. Gmock will output a valid C++ array initializer for you. + + std::vector<ResultingCodecList> golden_answers = { + {.factory_id = FactorySignature::Id::kWebRtcTipOfTree, + .caller_local = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", + "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", + "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", + "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", + "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 " + "[103:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "42001f]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 " + "[107:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "42001f]", + "2 [108:video/rtx/90000/0;apt=107]", + "2 " + "[109:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "42e01f]", + "2 [114:video/rtx/90000/0;apt=109]", + "2 " + "[115:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "42e01f]", + "2 [116:video/rtx/90000/0;apt=115]", + "2 " + "[117:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "4d001f]", + "2 [118:video/rtx/90000/0;apt=117]", + "2 " + "[39:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "4d001f]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [45:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [46:video/rtx/90000/0;apt=45]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [100:video/VP9/90000/0;profile-id=2]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 [119:video/red/90000/0]", + "2 [120:video/rtx/90000/0;apt=119]", + "2 [121:video/ulpfec/90000/0]"}, + .caller_remote = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", + "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", + "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", + "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", + "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 " + "[103:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "42001f]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 " + "[107:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "42001f]", + "2 [108:video/rtx/90000/0;apt=107]", + "2 " + "[109:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "42e01f]", + "2 [114:video/rtx/90000/0;apt=109]", + "2 " + "[115:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "42e01f]", + "2 [116:video/rtx/90000/0;apt=115]", + "2 " + "[117:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "4d001f]", + "2 [118:video/rtx/90000/0;apt=117]", + "2 " + "[39:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "4d001f]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [45:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [46:video/rtx/90000/0;apt=45]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [100:video/VP9/90000/0;profile-id=2]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 [119:video/red/90000/0]", + "2 [120:video/rtx/90000/0;apt=119]", + "2 [121:video/ulpfec/90000/0]"}, + .callee_local = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", + "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", + "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", + "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", + "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 " + "[103:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "42001f]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 " + "[107:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "42001f]", + "2 [108:video/rtx/90000/0;apt=107]", + "2 " + "[109:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "42e01f]", + "2 [114:video/rtx/90000/0;apt=109]", + "2 " + "[115:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "42e01f]", + "2 [116:video/rtx/90000/0;apt=115]", + "2 " + "[117:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "4d001f]", + "2 [118:video/rtx/90000/0;apt=117]", + "2 " + "[39:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "4d001f]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [45:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [46:video/rtx/90000/0;apt=45]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [100:video/VP9/90000/0;profile-id=2]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 [119:video/red/90000/0]", + "2 [120:video/rtx/90000/0;apt=119]", + "2 [121:video/ulpfec/90000/0]"}, + .callee_remote = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", + "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", + "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", + "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", + "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 " + "[103:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "42001f]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 " + "[107:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "42001f]", + "2 [108:video/rtx/90000/0;apt=107]", + "2 " + "[109:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "42e01f]", + "2 [114:video/rtx/90000/0;apt=109]", + "2 " + "[115:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "42e01f]", + "2 [116:video/rtx/90000/0;apt=115]", + "2 " + "[117:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "4d001f]", + "2 [118:video/rtx/90000/0;apt=117]", + "2 " + "[39:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "4d001f]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [45:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [46:video/rtx/90000/0;apt=45]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [100:video/VP9/90000/0;profile-id=2]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 [119:video/red/90000/0]", + "2 [120:video/rtx/90000/0;apt=119]", + "2 [121:video/ulpfec/90000/0]"}}, + + {.factory_id = FactorySignature::Id::kWebRtcMoreConfigs1, + .caller_local = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 [39:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [100:video/VP9/90000/0;profile-id=2]", + "2 [101:video/rtx/90000/0;apt=100]", "2 [103:video/red/90000/0]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 [107:video/ulpfec/90000/0]"}, + .caller_remote = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 [39:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [100:video/VP9/90000/0;profile-id=2]", + "2 [101:video/rtx/90000/0;apt=100]", "2 [103:video/red/90000/0]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 [107:video/ulpfec/90000/0]"}, + .callee_local = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 [39:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [100:video/VP9/90000/0;profile-id=2]", + "2 [101:video/rtx/90000/0;apt=100]", "2 [103:video/red/90000/0]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 [107:video/ulpfec/90000/0]"}, + .callee_remote = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 [39:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [100:video/VP9/90000/0;profile-id=2]", + "2 [101:video/rtx/90000/0;apt=100]", "2 [103:video/red/90000/0]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 [107:video/ulpfec/90000/0]"}}, + {.factory_id = FactorySignature::Id::kWebRtcAndroid, + .caller_local = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 [39:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", "2 [100:video/red/90000/0]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 [103:video/ulpfec/90000/0]"}, + .caller_remote = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 [39:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", "2 [100:video/red/90000/0]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 [103:video/ulpfec/90000/0]"}, + .callee_local = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 [39:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", "2 [100:video/red/90000/0]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 [103:video/ulpfec/90000/0]"}, + .callee_remote = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 [39:video/AV1/90000/0;level-idx=5;profile=0;tier=0]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", "2 [100:video/red/90000/0]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 [103:video/ulpfec/90000/0]"}}, + {.factory_id = FactorySignature::Id::kGoogleInternal, + .caller_local = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", + "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", + "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", + "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", + "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 " + "[100:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-" + "id=42001f]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 " + "[103:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-" + "id=42001f]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 " + "[107:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-" + "id=42e01f]", + "2 [108:video/rtx/90000/0;apt=107]", + "2 " + "[109:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-" + "id=42e01f]", + "2 [114:video/rtx/90000/0;apt=109]", + "2 " + "[115:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-" + "id=4d001f]", + "2 [116:video/rtx/90000/0;apt=115]", + "2 " + "[39:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-" + "id=4d001f]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [117:video/red/90000/0]", + "2 [118:video/rtx/90000/0;apt=117]", + "2 [119:video/ulpfec/90000/0]"}, + .caller_remote = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", + "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", + "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", + "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", + "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 " + "[100:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-" + "id=42001f]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 " + "[103:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-" + "id=42001f]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 " + "[107:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-" + "id=42e01f]", + "2 [108:video/rtx/90000/0;apt=107]", + "2 " + "[109:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-" + "id=42e01f]", + "2 [114:video/rtx/90000/0;apt=109]", + "2 " + "[115:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-" + "id=4d001f]", + "2 [116:video/rtx/90000/0;apt=115]", + "2 " + "[39:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-" + "id=4d001f]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [117:video/red/90000/0]", + "2 [118:video/rtx/90000/0;apt=117]", + "2 [119:video/ulpfec/90000/0]"}, + .callee_local = + {"1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", + "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", + "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", + "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", + "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 " + "[100:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-" + "id=42001f]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 " + "[103:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-" + "id=42001f]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 " + "[107:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-" + "id=42e01f]", + "2 [108:video/rtx/90000/0;apt=107]", + "2 " + "[109:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-" + "id=42e01f]", + "2 [114:video/rtx/90000/0;apt=109]", + "2 " + "[115:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-" + "id=4d001f]", + "2 [116:video/rtx/90000/0;apt=115]", + "2 " + "[39:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-" + "id=4d001f]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [117:video/red/90000/0]", + "2 [118:video/rtx/90000/0;apt=117]", + "2 [119:video/ulpfec/90000/0]"}, + .callee_remote = { + "1 [111:audio/opus/48000/2;minptime=10;useinbandfec=1]", + "1 [63:audio/red/48000/2;=111/111]", + "1 [9:audio/G722/8000/1]", + "1 [0:audio/PCMU/8000/1]", + "1 [8:audio/PCMA/8000/1]", + "1 [13:audio/CN/8000/1]", + "1 [110:audio/telephone-event/48000/1]", + "1 [126:audio/telephone-event/8000/1]", + "2 [96:video/VP8/90000/0]", + "2 [97:video/rtx/90000/0;apt=96]", + "2 " + "[100:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "42001f]", + "2 [101:video/rtx/90000/0;apt=100]", + "2 " + "[103:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "42001f]", + "2 [104:video/rtx/90000/0;apt=103]", + "2 " + "[107:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "42e01f]", + "2 [108:video/rtx/90000/0;apt=107]", + "2 " + "[109:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "42e01f]", + "2 [114:video/rtx/90000/0;apt=109]", + "2 " + "[115:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=" + "4d001f]", + "2 [116:video/rtx/90000/0;apt=115]", + "2 " + "[39:video/H264/90000/" + "0;level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=" + "4d001f]", + "2 [40:video/rtx/90000/0;apt=39]", + "2 [98:video/VP9/90000/0;profile-id=0]", + "2 [99:video/rtx/90000/0;apt=98]", + "2 [117:video/red/90000/0]", + "2 [118:video/rtx/90000/0;apt=117]", + "2 [119:video/ulpfec/90000/0]"}}}; + auto this_golden_it = + std::find_if(golden_answers.begin(), golden_answers.end(), + [&](const ResultingCodecList& candidate) { + return candidate.factory_id == factory_signature.id(); + }); + ASSERT_THAT(this_golden_it, Not(Eq(golden_answers.end()))) + << "Add this result set to golden_answers:\n" + << DumpAsResultingCodecList( + factory_signature.id(), + CodecList(*caller()->pc()->local_description()), + CodecList(*caller()->pc()->remote_description()), + CodecList(*callee()->pc()->local_description()), + CodecList(*callee()->pc()->remote_description())); + + const ResultingCodecList& this_golden = *this_golden_it; + EXPECT_THAT(CodecList(*caller()->pc()->local_description()), + ElementsAreArray(this_golden.caller_local)); + EXPECT_THAT(CodecList(*caller()->pc()->remote_description()), + ElementsAreArray(this_golden.caller_remote)); + EXPECT_THAT(CodecList(*callee()->pc()->local_description()), + ElementsAreArray(this_golden.callee_local)); + EXPECT_THAT(CodecList(*callee()->pc()->remote_description()), + ElementsAreArray(this_golden.callee_remote)); +} + } // namespace } // namespace webrtc