xref: /aosp_15_r20/external/webrtc/pc/peer_connection_media_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright 2017 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 
11 // This file contains tests that check the interaction between the
12 // PeerConnection and the underlying media engine, as well as tests that check
13 // the media-related aspects of SDP.
14 
15 #include <algorithm>
16 #include <functional>
17 #include <iterator>
18 #include <map>
19 #include <memory>
20 #include <set>
21 #include <string>
22 #include <tuple>
23 #include <type_traits>
24 #include <utility>
25 #include <vector>
26 
27 #include "absl/algorithm/container.h"
28 #include "absl/types/optional.h"
29 #include "api/audio_options.h"
30 #include "api/call/call_factory_interface.h"
31 #include "api/jsep.h"
32 #include "api/media_types.h"
33 #include "api/peer_connection_interface.h"
34 #include "api/rtc_error.h"
35 #include "api/rtc_event_log/rtc_event_log_factory.h"
36 #include "api/rtc_event_log/rtc_event_log_factory_interface.h"
37 #include "api/rtp_parameters.h"
38 #include "api/rtp_sender_interface.h"
39 #include "api/rtp_transceiver_direction.h"
40 #include "api/rtp_transceiver_interface.h"
41 #include "api/scoped_refptr.h"
42 #include "api/task_queue/default_task_queue_factory.h"
43 #include "api/task_queue/task_queue_factory.h"
44 #include "media/base/codec.h"
45 #include "media/base/fake_media_engine.h"
46 #include "media/base/media_constants.h"
47 #include "media/base/media_engine.h"
48 #include "media/base/stream_params.h"
49 #include "p2p/base/fake_port_allocator.h"
50 #include "p2p/base/p2p_constants.h"
51 #include "p2p/base/port_allocator.h"
52 #include "p2p/base/transport_info.h"
53 #include "pc/media_session.h"
54 #include "pc/peer_connection_wrapper.h"
55 #include "pc/rtp_media_utils.h"
56 #include "pc/session_description.h"
57 #include "pc/test/mock_peer_connection_observers.h"
58 #include "rtc_base/checks.h"
59 #include "rtc_base/rtc_certificate_generator.h"
60 #include "rtc_base/thread.h"
61 #include "test/gtest.h"
62 #ifdef WEBRTC_ANDROID
63 #include "pc/test/android_test_initializer.h"
64 #endif
65 #include "rtc_base/gunit.h"
66 #include "rtc_base/virtual_socket_server.h"
67 #include "test/gmock.h"
68 
69 namespace webrtc {
70 
71 using cricket::FakeMediaEngine;
72 using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
73 using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
74 using ::testing::Bool;
75 using ::testing::Combine;
76 using ::testing::ElementsAre;
77 using ::testing::Values;
78 
79 class PeerConnectionWrapperForMediaTest : public PeerConnectionWrapper {
80  public:
81   using PeerConnectionWrapper::PeerConnectionWrapper;
82 
media_engine()83   FakeMediaEngine* media_engine() { return media_engine_; }
set_media_engine(FakeMediaEngine * media_engine)84   void set_media_engine(FakeMediaEngine* media_engine) {
85     media_engine_ = media_engine;
86   }
87 
88  private:
89   FakeMediaEngine* media_engine_;
90 };
91 
92 class PeerConnectionMediaBaseTest : public ::testing::Test {
93  protected:
94   typedef std::unique_ptr<PeerConnectionWrapperForMediaTest> WrapperPtr;
95 
PeerConnectionMediaBaseTest(SdpSemantics sdp_semantics)96   explicit PeerConnectionMediaBaseTest(SdpSemantics sdp_semantics)
97       : vss_(new rtc::VirtualSocketServer()),
98         main_(vss_.get()),
99         sdp_semantics_(sdp_semantics) {
100 #ifdef WEBRTC_ANDROID
101     InitializeAndroidObjects();
102 #endif
103   }
104 
CreatePeerConnection()105   WrapperPtr CreatePeerConnection() {
106     return CreatePeerConnection(RTCConfiguration());
107   }
108 
CreatePeerConnection(const RTCConfiguration & config)109   WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
110     return CreatePeerConnection(config, std::make_unique<FakeMediaEngine>());
111   }
112 
CreatePeerConnection(std::unique_ptr<FakeMediaEngine> media_engine)113   WrapperPtr CreatePeerConnection(
114       std::unique_ptr<FakeMediaEngine> media_engine) {
115     return CreatePeerConnection(RTCConfiguration(), std::move(media_engine));
116   }
117 
118   // Creates PeerConnectionFactory and PeerConnection for given configuration.
CreatePeerConnection(const RTCConfiguration & config,std::unique_ptr<FakeMediaEngine> media_engine)119   WrapperPtr CreatePeerConnection(
120       const RTCConfiguration& config,
121       std::unique_ptr<FakeMediaEngine> media_engine) {
122     auto* media_engine_ptr = media_engine.get();
123 
124     PeerConnectionFactoryDependencies factory_dependencies;
125 
126     factory_dependencies.network_thread = rtc::Thread::Current();
127     factory_dependencies.worker_thread = rtc::Thread::Current();
128     factory_dependencies.signaling_thread = rtc::Thread::Current();
129     factory_dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
130     factory_dependencies.media_engine = std::move(media_engine);
131     factory_dependencies.call_factory = CreateCallFactory();
132     factory_dependencies.event_log_factory =
133         std::make_unique<RtcEventLogFactory>(
134             factory_dependencies.task_queue_factory.get());
135 
136     auto pc_factory =
137         CreateModularPeerConnectionFactory(std::move(factory_dependencies));
138 
139     auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
140         rtc::Thread::Current(),
141         std::make_unique<rtc::BasicPacketSocketFactory>(vss_.get()));
142     auto observer = std::make_unique<MockPeerConnectionObserver>();
143     auto modified_config = config;
144     modified_config.sdp_semantics = sdp_semantics_;
145     PeerConnectionDependencies pc_dependencies(observer.get());
146     pc_dependencies.allocator = std::move(fake_port_allocator);
147     auto result = pc_factory->CreatePeerConnectionOrError(
148         modified_config, std::move(pc_dependencies));
149     if (!result.ok()) {
150       return nullptr;
151     }
152 
153     auto pc = result.MoveValue();
154     observer->SetPeerConnectionInterface(pc.get());
155     auto wrapper = std::make_unique<PeerConnectionWrapperForMediaTest>(
156         pc_factory, pc, std::move(observer));
157     wrapper->set_media_engine(media_engine_ptr);
158     return wrapper;
159   }
160 
161   // Accepts the same arguments as CreatePeerConnection and adds default audio
162   // track (but no video).
163   template <typename... Args>
CreatePeerConnectionWithAudio(Args &&...args)164   WrapperPtr CreatePeerConnectionWithAudio(Args&&... args) {
165     auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
166     if (!wrapper) {
167       return nullptr;
168     }
169     wrapper->AddAudioTrack("a");
170     return wrapper;
171   }
172 
173   // Accepts the same arguments as CreatePeerConnection and adds default video
174   // track (but no audio).
175   template <typename... Args>
CreatePeerConnectionWithVideo(Args &&...args)176   WrapperPtr CreatePeerConnectionWithVideo(Args&&... args) {
177     auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
178     if (!wrapper) {
179       return nullptr;
180     }
181     wrapper->AddVideoTrack("v");
182     return wrapper;
183   }
184 
185   // Accepts the same arguments as CreatePeerConnection and adds default audio
186   // and video tracks.
187   template <typename... Args>
CreatePeerConnectionWithAudioVideo(Args &&...args)188   WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
189     auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
190     if (!wrapper) {
191       return nullptr;
192     }
193     wrapper->AddAudioTrack("a");
194     wrapper->AddVideoTrack("v");
195     return wrapper;
196   }
197 
GetMediaContentDirection(const SessionDescriptionInterface * sdesc,cricket::MediaType media_type)198   RtpTransceiverDirection GetMediaContentDirection(
199       const SessionDescriptionInterface* sdesc,
200       cricket::MediaType media_type) {
201     auto* content =
202         cricket::GetFirstMediaContent(sdesc->description(), media_type);
203     RTC_DCHECK(content);
204     return content->media_description()->direction();
205   }
206 
IsUnifiedPlan() const207   bool IsUnifiedPlan() const {
208     return sdp_semantics_ == SdpSemantics::kUnifiedPlan;
209   }
210 
211   std::unique_ptr<rtc::VirtualSocketServer> vss_;
212   rtc::AutoSocketServerThread main_;
213   const SdpSemantics sdp_semantics_;
214 };
215 
216 class PeerConnectionMediaTest
217     : public PeerConnectionMediaBaseTest,
218       public ::testing::WithParamInterface<SdpSemantics> {
219  protected:
PeerConnectionMediaTest()220   PeerConnectionMediaTest() : PeerConnectionMediaBaseTest(GetParam()) {}
221 };
222 
223 class PeerConnectionMediaTestUnifiedPlan : public PeerConnectionMediaBaseTest {
224  protected:
PeerConnectionMediaTestUnifiedPlan()225   PeerConnectionMediaTestUnifiedPlan()
226       : PeerConnectionMediaBaseTest(SdpSemantics::kUnifiedPlan) {}
227 };
228 
229 class PeerConnectionMediaTestPlanB : public PeerConnectionMediaBaseTest {
230  protected:
PeerConnectionMediaTestPlanB()231   PeerConnectionMediaTestPlanB()
232       : PeerConnectionMediaBaseTest(SdpSemantics::kPlanB_DEPRECATED) {}
233 };
234 
TEST_P(PeerConnectionMediaTest,FailToSetRemoteDescriptionIfCreateMediaChannelFails)235 TEST_P(PeerConnectionMediaTest,
236        FailToSetRemoteDescriptionIfCreateMediaChannelFails) {
237   auto caller = CreatePeerConnectionWithAudioVideo();
238   auto callee = CreatePeerConnectionWithAudioVideo();
239   callee->media_engine()->set_fail_create_channel(true);
240 
241   std::string error;
242   ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
243   EXPECT_PRED_FORMAT2(AssertStartsWith, error,
244                       "Failed to set remote offer sdp: Failed to create");
245 }
246 
TEST_P(PeerConnectionMediaTest,FailToSetLocalDescriptionIfCreateMediaChannelFails)247 TEST_P(PeerConnectionMediaTest,
248        FailToSetLocalDescriptionIfCreateMediaChannelFails) {
249   auto caller = CreatePeerConnectionWithAudioVideo();
250   caller->media_engine()->set_fail_create_channel(true);
251 
252   std::string error;
253   ASSERT_FALSE(caller->SetLocalDescription(caller->CreateOffer(), &error));
254   EXPECT_PRED_FORMAT2(AssertStartsWith, error,
255                       "Failed to set local offer sdp: Failed to create");
256 }
257 
GetIds(const std::vector<cricket::StreamParams> & streams)258 std::vector<std::string> GetIds(
259     const std::vector<cricket::StreamParams>& streams) {
260   std::vector<std::string> ids;
261   ids.reserve(streams.size());
262   for (const auto& stream : streams) {
263     ids.push_back(stream.id);
264   }
265   return ids;
266 }
267 
268 // Test that exchanging an offer and answer with each side having an audio and
269 // video stream creates the appropriate send/recv streams in the underlying
270 // media engine on both sides.
TEST_P(PeerConnectionMediaTest,AudioVideoOfferAnswerCreateSendRecvStreams)271 TEST_P(PeerConnectionMediaTest, AudioVideoOfferAnswerCreateSendRecvStreams) {
272   const std::string kCallerAudioId = "caller_a";
273   const std::string kCallerVideoId = "caller_v";
274   const std::string kCalleeAudioId = "callee_a";
275   const std::string kCalleeVideoId = "callee_v";
276 
277   auto caller = CreatePeerConnection();
278   caller->AddAudioTrack(kCallerAudioId);
279   caller->AddVideoTrack(kCallerVideoId);
280 
281   auto callee = CreatePeerConnection();
282   callee->AddAudioTrack(kCalleeAudioId);
283   callee->AddVideoTrack(kCalleeVideoId);
284 
285   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
286   ASSERT_TRUE(
287       caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
288 
289   auto* caller_voice = caller->media_engine()->GetVoiceChannel(0);
290   EXPECT_THAT(GetIds(caller_voice->recv_streams()),
291               ElementsAre(kCalleeAudioId));
292   EXPECT_THAT(GetIds(caller_voice->send_streams()),
293               ElementsAre(kCallerAudioId));
294 
295   auto* caller_video = caller->media_engine()->GetVideoChannel(0);
296   EXPECT_THAT(GetIds(caller_video->recv_streams()),
297               ElementsAre(kCalleeVideoId));
298   EXPECT_THAT(GetIds(caller_video->send_streams()),
299               ElementsAre(kCallerVideoId));
300 
301   auto* callee_voice = callee->media_engine()->GetVoiceChannel(0);
302   EXPECT_THAT(GetIds(callee_voice->recv_streams()),
303               ElementsAre(kCallerAudioId));
304   EXPECT_THAT(GetIds(callee_voice->send_streams()),
305               ElementsAre(kCalleeAudioId));
306 
307   auto* callee_video = callee->media_engine()->GetVideoChannel(0);
308   EXPECT_THAT(GetIds(callee_video->recv_streams()),
309               ElementsAre(kCallerVideoId));
310   EXPECT_THAT(GetIds(callee_video->send_streams()),
311               ElementsAre(kCalleeVideoId));
312 }
313 
314 // Test that stopping the caller transceivers causes the media channels on the
315 // callee to be destroyed after calling SetRemoteDescription on the generated
316 // offer.
317 // See next test for equivalent behavior with Plan B semantics.
TEST_F(PeerConnectionMediaTestUnifiedPlan,StoppedRemoteTransceiversRemovesMediaChannels)318 TEST_F(PeerConnectionMediaTestUnifiedPlan,
319        StoppedRemoteTransceiversRemovesMediaChannels) {
320   auto caller = CreatePeerConnectionWithAudioVideo();
321   auto callee = CreatePeerConnection();
322 
323   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
324 
325   // Stop both audio and video transceivers on the caller.
326   auto transceivers = caller->pc()->GetTransceivers();
327   ASSERT_EQ(2u, transceivers.size());
328   transceivers[0]->StopInternal();
329   transceivers[1]->StopInternal();
330 
331   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
332 
333   ASSERT_FALSE(callee->media_engine()->GetVoiceChannel(0));
334   ASSERT_FALSE(callee->media_engine()->GetVideoChannel(0));
335 }
336 
337 // Test that removing streams from a subsequent offer causes the receive streams
338 // on the callee to be removed.
339 // See previous test for equivalent behavior with Unified Plan semantics.
TEST_F(PeerConnectionMediaTestPlanB,EmptyRemoteOfferRemovesRecvStreams)340 TEST_F(PeerConnectionMediaTestPlanB, EmptyRemoteOfferRemovesRecvStreams) {
341   auto caller = CreatePeerConnection();
342   auto caller_audio_track = caller->AddAudioTrack("a");
343   auto caller_video_track = caller->AddVideoTrack("v");
344   auto callee = CreatePeerConnectionWithAudioVideo();
345 
346   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
347 
348   // Remove both tracks from caller.
349   caller->pc()->RemoveTrackOrError(caller_audio_track);
350   caller->pc()->RemoveTrackOrError(caller_video_track);
351 
352   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
353 
354   auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
355   auto callee_video = callee->media_engine()->GetVideoChannel(0);
356   EXPECT_EQ(1u, callee_voice->send_streams().size());
357   EXPECT_EQ(0u, callee_voice->recv_streams().size());
358   EXPECT_EQ(1u, callee_video->send_streams().size());
359   EXPECT_EQ(0u, callee_video->recv_streams().size());
360 }
361 
362 // Test enabling of simulcast with Plan B semantics.
363 // This test creating an offer.
TEST_F(PeerConnectionMediaTestPlanB,SimulcastOffer)364 TEST_F(PeerConnectionMediaTestPlanB, SimulcastOffer) {
365   auto caller = CreatePeerConnection();
366   auto caller_video_track = caller->AddVideoTrack("v");
367   RTCOfferAnswerOptions options;
368   options.num_simulcast_layers = 3;
369   auto offer = caller->CreateOffer(options);
370   auto* description = cricket::GetFirstMediaContent(offer->description(),
371                                                     cricket::MEDIA_TYPE_VIDEO)
372                           ->media_description();
373   ASSERT_EQ(1u, description->streams().size());
374   ASSERT_TRUE(description->streams()[0].get_ssrc_group("SIM"));
375   EXPECT_EQ(3u, description->streams()[0].get_ssrc_group("SIM")->ssrcs.size());
376 
377   // Check that it actually creates simulcast aswell.
378   caller->SetLocalDescription(std::move(offer));
379   auto senders = caller->pc()->GetSenders();
380   ASSERT_EQ(1u, senders.size());
381   EXPECT_EQ(cricket::MediaType::MEDIA_TYPE_VIDEO, senders[0]->media_type());
382   EXPECT_EQ(3u, senders[0]->GetParameters().encodings.size());
383 }
384 
385 // Test enabling of simulcast with Plan B semantics.
386 // This test creating an answer.
TEST_F(PeerConnectionMediaTestPlanB,SimulcastAnswer)387 TEST_F(PeerConnectionMediaTestPlanB, SimulcastAnswer) {
388   auto caller = CreatePeerConnection();
389   caller->AddVideoTrack("v0");
390   auto offer = caller->CreateOffer();
391   auto callee = CreatePeerConnection();
392   auto callee_video_track = callee->AddVideoTrack("v1");
393   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
394   RTCOfferAnswerOptions options;
395   options.num_simulcast_layers = 3;
396   auto answer = callee->CreateAnswer(options);
397   auto* description = cricket::GetFirstMediaContent(answer->description(),
398                                                     cricket::MEDIA_TYPE_VIDEO)
399                           ->media_description();
400   ASSERT_EQ(1u, description->streams().size());
401   ASSERT_TRUE(description->streams()[0].get_ssrc_group("SIM"));
402   EXPECT_EQ(3u, description->streams()[0].get_ssrc_group("SIM")->ssrcs.size());
403 
404   // Check that it actually creates simulcast aswell.
405   callee->SetLocalDescription(std::move(answer));
406   auto senders = callee->pc()->GetSenders();
407   ASSERT_EQ(1u, senders.size());
408   EXPECT_EQ(cricket::MediaType::MEDIA_TYPE_VIDEO, senders[0]->media_type());
409   EXPECT_EQ(3u, senders[0]->GetParameters().encodings.size());
410 }
411 
412 // Test that stopping the callee transceivers causes the media channels to be
413 // destroyed on the callee after calling SetLocalDescription on the local
414 // answer.
415 // See next test for equivalent behavior with Plan B semantics.
TEST_F(PeerConnectionMediaTestUnifiedPlan,StoppedLocalTransceiversRemovesMediaChannels)416 TEST_F(PeerConnectionMediaTestUnifiedPlan,
417        StoppedLocalTransceiversRemovesMediaChannels) {
418   auto caller = CreatePeerConnectionWithAudioVideo();
419   auto callee = CreatePeerConnectionWithAudioVideo();
420 
421   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
422 
423   // Stop both audio and video transceivers on the callee.
424   auto transceivers = callee->pc()->GetTransceivers();
425   ASSERT_EQ(2u, transceivers.size());
426   transceivers[0]->StopInternal();
427   transceivers[1]->StopInternal();
428 
429   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
430 
431   EXPECT_FALSE(callee->media_engine()->GetVoiceChannel(0));
432   EXPECT_FALSE(callee->media_engine()->GetVideoChannel(0));
433 }
434 
435 // Test that removing streams from a subsequent answer causes the send streams
436 // on the callee to be removed when applied locally.
437 // See previous test for equivalent behavior with Unified Plan semantics.
TEST_F(PeerConnectionMediaTestPlanB,EmptyLocalAnswerRemovesSendStreams)438 TEST_F(PeerConnectionMediaTestPlanB, EmptyLocalAnswerRemovesSendStreams) {
439   auto caller = CreatePeerConnectionWithAudioVideo();
440   auto callee = CreatePeerConnection();
441   auto callee_audio_track = callee->AddAudioTrack("a");
442   auto callee_video_track = callee->AddVideoTrack("v");
443 
444   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
445 
446   // Remove both tracks from callee.
447   callee->pc()->RemoveTrackOrError(callee_audio_track);
448   callee->pc()->RemoveTrackOrError(callee_video_track);
449 
450   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
451 
452   auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
453   auto callee_video = callee->media_engine()->GetVideoChannel(0);
454   EXPECT_EQ(0u, callee_voice->send_streams().size());
455   EXPECT_EQ(1u, callee_voice->recv_streams().size());
456   EXPECT_EQ(0u, callee_video->send_streams().size());
457   EXPECT_EQ(1u, callee_video->recv_streams().size());
458 }
459 
460 // Test that a new stream in a subsequent offer causes a new receive stream to
461 // be created on the callee.
TEST_P(PeerConnectionMediaTest,NewStreamInRemoteOfferAddsRecvStreams)462 TEST_P(PeerConnectionMediaTest, NewStreamInRemoteOfferAddsRecvStreams) {
463   auto caller = CreatePeerConnectionWithAudioVideo();
464   auto callee = CreatePeerConnection();
465 
466   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
467 
468   // Add second set of tracks to the caller.
469   caller->AddAudioTrack("a2");
470   caller->AddVideoTrack("v2");
471 
472   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
473 
474   auto a1 = callee->media_engine()->GetVoiceChannel(0);
475   auto a2 = callee->media_engine()->GetVoiceChannel(1);
476   auto v1 = callee->media_engine()->GetVideoChannel(0);
477   auto v2 = callee->media_engine()->GetVideoChannel(1);
478   if (IsUnifiedPlan()) {
479     ASSERT_TRUE(a1);
480     EXPECT_EQ(1u, a1->recv_streams().size());
481     ASSERT_TRUE(a2);
482     EXPECT_EQ(1u, a2->recv_streams().size());
483     ASSERT_TRUE(v1);
484     EXPECT_EQ(1u, v1->recv_streams().size());
485     ASSERT_TRUE(v2);
486     EXPECT_EQ(1u, v2->recv_streams().size());
487   } else {
488     ASSERT_TRUE(a1);
489     EXPECT_EQ(2u, a1->recv_streams().size());
490     ASSERT_FALSE(a2);
491     ASSERT_TRUE(v1);
492     EXPECT_EQ(2u, v1->recv_streams().size());
493     ASSERT_FALSE(v2);
494   }
495 }
496 
497 // Test that a new stream in a subsequent answer causes a new send stream to be
498 // created on the callee when added locally.
TEST_P(PeerConnectionMediaTest,NewStreamInLocalAnswerAddsSendStreams)499 TEST_P(PeerConnectionMediaTest, NewStreamInLocalAnswerAddsSendStreams) {
500   auto caller = CreatePeerConnection();
501   auto callee = CreatePeerConnectionWithAudioVideo();
502 
503   RTCOfferAnswerOptions offer_options;
504   offer_options.offer_to_receive_audio =
505       RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
506   offer_options.offer_to_receive_video =
507       RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
508   RTCOfferAnswerOptions answer_options;
509 
510   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get(), offer_options,
511                                               answer_options));
512 
513   // Add second set of tracks to the callee.
514   callee->AddAudioTrack("a2");
515   callee->AddVideoTrack("v2");
516 
517   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get(), offer_options,
518                                               answer_options));
519 
520   auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
521   ASSERT_TRUE(callee_voice);
522   auto callee_video = callee->media_engine()->GetVideoChannel(0);
523   ASSERT_TRUE(callee_video);
524 
525   if (IsUnifiedPlan()) {
526     EXPECT_EQ(1u, callee_voice->send_streams().size());
527     EXPECT_EQ(1u, callee_video->send_streams().size());
528   } else {
529     EXPECT_EQ(2u, callee_voice->send_streams().size());
530     EXPECT_EQ(2u, callee_video->send_streams().size());
531   }
532 }
533 
534 // A PeerConnection with no local streams and no explicit answer constraints
535 // should not reject any offered media sections.
TEST_P(PeerConnectionMediaTest,CreateAnswerWithNoStreamsAndDefaultOptionsDoesNotReject)536 TEST_P(PeerConnectionMediaTest,
537        CreateAnswerWithNoStreamsAndDefaultOptionsDoesNotReject) {
538   auto caller = CreatePeerConnectionWithAudioVideo();
539   auto callee = CreatePeerConnection();
540   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
541   auto answer = callee->CreateAnswer();
542 
543   const auto* audio_content =
544       cricket::GetFirstAudioContent(answer->description());
545   ASSERT_TRUE(audio_content);
546   EXPECT_FALSE(audio_content->rejected);
547 
548   const auto* video_content =
549       cricket::GetFirstVideoContent(answer->description());
550   ASSERT_TRUE(video_content);
551   EXPECT_FALSE(video_content->rejected);
552 }
553 
554 // Test that raw packetization is not set in the offer by default.
TEST_P(PeerConnectionMediaTest,RawPacketizationNotSetInOffer)555 TEST_P(PeerConnectionMediaTest, RawPacketizationNotSetInOffer) {
556   std::vector<cricket::VideoCodec> fake_codecs;
557   fake_codecs.push_back(cricket::VideoCodec(111, cricket::kVp8CodecName));
558   fake_codecs.push_back(cricket::VideoCodec(112, cricket::kRtxCodecName));
559   fake_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "111";
560   fake_codecs.push_back(cricket::VideoCodec(113, cricket::kVp9CodecName));
561   fake_codecs.push_back(cricket::VideoCodec(114, cricket::kH264CodecName));
562   fake_codecs.push_back(cricket::VideoCodec(115, "HEVC"));
563   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
564   caller_fake_engine->SetVideoCodecs(fake_codecs);
565 
566   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
567   auto offer = caller->CreateOfferAndSetAsLocal();
568   auto* offer_description =
569       cricket::GetFirstVideoContentDescription(offer->description());
570   for (const auto& codec : offer_description->codecs()) {
571     EXPECT_EQ(codec.packetization, absl::nullopt);
572   }
573 }
574 
575 // Test that raw packetization is set in the offer and answer for all
576 // video payload when raw_packetization_for_video is true.
TEST_P(PeerConnectionMediaTest,RawPacketizationSetInOfferAndAnswer)577 TEST_P(PeerConnectionMediaTest, RawPacketizationSetInOfferAndAnswer) {
578   std::vector<cricket::VideoCodec> fake_codecs;
579   fake_codecs.push_back(cricket::VideoCodec(111, cricket::kVp8CodecName));
580   fake_codecs.push_back(cricket::VideoCodec(112, cricket::kRtxCodecName));
581   fake_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "111";
582   fake_codecs.push_back(cricket::VideoCodec(113, cricket::kVp9CodecName));
583   fake_codecs.push_back(cricket::VideoCodec(114, cricket::kH264CodecName));
584   fake_codecs.push_back(cricket::VideoCodec(115, "HEVC"));
585   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
586   caller_fake_engine->SetVideoCodecs(fake_codecs);
587   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
588   callee_fake_engine->SetVideoCodecs(fake_codecs);
589 
590   RTCOfferAnswerOptions options;
591   options.raw_packetization_for_video = true;
592 
593   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
594   auto offer = caller->CreateOfferAndSetAsLocal(options);
595   auto* offer_description =
596       cricket::GetFirstVideoContentDescription(offer->description());
597   for (const auto& codec : offer_description->codecs()) {
598     if (codec.GetCodecType() == cricket::VideoCodec::CODEC_VIDEO) {
599       EXPECT_EQ(codec.packetization, cricket::kPacketizationParamRaw);
600     }
601   }
602 
603   auto callee = CreatePeerConnectionWithVideo(std::move(callee_fake_engine));
604   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
605   auto answer = callee->CreateAnswerAndSetAsLocal(options);
606   auto* answer_description =
607       cricket::GetFirstVideoContentDescription(answer->description());
608   for (const auto& codec : answer_description->codecs()) {
609     if (codec.GetCodecType() == cricket::VideoCodec::CODEC_VIDEO) {
610       EXPECT_EQ(codec.packetization, cricket::kPacketizationParamRaw);
611     }
612   }
613 
614   ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
615 }
616 
617 // Test that raw packetization is not set in the answer when
618 // raw_packetization_for_video is true if it was not set in the offer.
TEST_P(PeerConnectionMediaTest,RawPacketizationNotSetInAnswerWhenNotSetInOffer)619 TEST_P(PeerConnectionMediaTest,
620        RawPacketizationNotSetInAnswerWhenNotSetInOffer) {
621   std::vector<cricket::VideoCodec> fake_codecs;
622   fake_codecs.push_back(cricket::VideoCodec(111, cricket::kVp8CodecName));
623   fake_codecs.push_back(cricket::VideoCodec(112, cricket::kRtxCodecName));
624   fake_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "111";
625   fake_codecs.push_back(cricket::VideoCodec(113, cricket::kVp9CodecName));
626   fake_codecs.push_back(cricket::VideoCodec(114, cricket::kH264CodecName));
627   fake_codecs.push_back(cricket::VideoCodec(115, "HEVC"));
628   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
629   caller_fake_engine->SetVideoCodecs(fake_codecs);
630   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
631   callee_fake_engine->SetVideoCodecs(fake_codecs);
632 
633   RTCOfferAnswerOptions caller_options;
634   caller_options.raw_packetization_for_video = false;
635   RTCOfferAnswerOptions callee_options;
636   callee_options.raw_packetization_for_video = true;
637 
638   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
639   auto offer = caller->CreateOfferAndSetAsLocal(caller_options);
640 
641   auto callee = CreatePeerConnectionWithVideo(std::move(callee_fake_engine));
642   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
643   auto answer = callee->CreateAnswerAndSetAsLocal(callee_options);
644 
645   auto* answer_description =
646       cricket::GetFirstVideoContentDescription(answer->description());
647   for (const auto& codec : answer_description->codecs()) {
648     EXPECT_EQ(codec.packetization, absl::nullopt);
649   }
650 
651   ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
652 }
653 
654 class PeerConnectionMediaOfferDirectionTest
655     : public PeerConnectionMediaBaseTest,
656       public ::testing::WithParamInterface<
657           std::tuple<SdpSemantics,
658                      std::tuple<bool, int, RtpTransceiverDirection>>> {
659  protected:
PeerConnectionMediaOfferDirectionTest()660   PeerConnectionMediaOfferDirectionTest()
661       : PeerConnectionMediaBaseTest(std::get<0>(GetParam())) {
662     auto param = std::get<1>(GetParam());
663     send_media_ = std::get<0>(param);
664     offer_to_receive_ = std::get<1>(param);
665     expected_direction_ = std::get<2>(param);
666   }
667 
668   bool send_media_;
669   int offer_to_receive_;
670   RtpTransceiverDirection expected_direction_;
671 };
672 
673 // Tests that the correct direction is set on the media description according
674 // to the presence of a local media track and the offer_to_receive setting.
TEST_P(PeerConnectionMediaOfferDirectionTest,VerifyDirection)675 TEST_P(PeerConnectionMediaOfferDirectionTest, VerifyDirection) {
676   auto caller = CreatePeerConnection();
677   if (send_media_) {
678     caller->AddAudioTrack("a");
679   }
680 
681   RTCOfferAnswerOptions options;
682   options.offer_to_receive_audio = offer_to_receive_;
683   auto offer = caller->CreateOffer(options);
684 
685   auto* content = cricket::GetFirstMediaContent(offer->description(),
686                                                 cricket::MEDIA_TYPE_AUDIO);
687   if (expected_direction_ == RtpTransceiverDirection::kInactive) {
688     EXPECT_FALSE(content);
689   } else {
690     EXPECT_EQ(expected_direction_, content->media_description()->direction());
691   }
692 }
693 
694 // Note that in these tests, MD_INACTIVE indicates that no media section is
695 // included in the offer, not that the media direction is inactive.
696 INSTANTIATE_TEST_SUITE_P(
697     PeerConnectionMediaTest,
698     PeerConnectionMediaOfferDirectionTest,
699     Combine(
700         Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
701         Values(std::make_tuple(false, -1, RtpTransceiverDirection::kInactive),
702                std::make_tuple(false, 0, RtpTransceiverDirection::kInactive),
703                std::make_tuple(false, 1, RtpTransceiverDirection::kRecvOnly),
704                std::make_tuple(true, -1, RtpTransceiverDirection::kSendRecv),
705                std::make_tuple(true, 0, RtpTransceiverDirection::kSendOnly),
706                std::make_tuple(true, 1, RtpTransceiverDirection::kSendRecv))));
707 
708 class PeerConnectionMediaAnswerDirectionTest
709     : public PeerConnectionMediaBaseTest,
710       public ::testing::WithParamInterface<
711           std::tuple<SdpSemantics, RtpTransceiverDirection, bool, int>> {
712  protected:
PeerConnectionMediaAnswerDirectionTest()713   PeerConnectionMediaAnswerDirectionTest()
714       : PeerConnectionMediaBaseTest(std::get<0>(GetParam())) {
715     offer_direction_ = std::get<1>(GetParam());
716     send_media_ = std::get<2>(GetParam());
717     offer_to_receive_ = std::get<3>(GetParam());
718   }
719 
720   RtpTransceiverDirection offer_direction_;
721   bool send_media_;
722   int offer_to_receive_;
723 };
724 
725 // Tests that the direction in an answer is correct according to direction sent
726 // in the offer, the presence of a local media track on the receive side and the
727 // offer_to_receive setting.
TEST_P(PeerConnectionMediaAnswerDirectionTest,VerifyDirection)728 TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyDirection) {
729   if (IsUnifiedPlan() &&
730       offer_to_receive_ != RTCOfferAnswerOptions::kUndefined) {
731     // offer_to_receive_ is not implemented when creating answers with Unified
732     // Plan semantics specified.
733     return;
734   }
735 
736   auto caller = CreatePeerConnection();
737   caller->AddAudioTrack("a");
738 
739   // Create the offer with an audio section and set its direction.
740   auto offer = caller->CreateOffer();
741   cricket::GetFirstAudioContentDescription(offer->description())
742       ->set_direction(offer_direction_);
743 
744   auto callee = CreatePeerConnection();
745   if (send_media_) {
746     callee->AddAudioTrack("a");
747   }
748   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
749 
750   // Create the answer according to the test parameters.
751   RTCOfferAnswerOptions options;
752   options.offer_to_receive_audio = offer_to_receive_;
753   auto answer = callee->CreateAnswer(options);
754 
755   // The expected direction in the answer is the intersection of each side's
756   // capability to send/recv media.
757   // For the offerer, the direction is given in the offer (offer_direction_).
758   // For the answerer, the direction has two components:
759   // 1. Send if the answerer has a local track to send.
760   // 2. Receive if the answerer has explicitly set the offer_to_receive to 1 or
761   //    if it has been left as default.
762   bool offer_send = RtpTransceiverDirectionHasSend(offer_direction_);
763   bool offer_recv = RtpTransceiverDirectionHasRecv(offer_direction_);
764 
765   // The negotiated components determine the direction set in the answer.
766   bool negotiate_send = (send_media_ && offer_recv);
767   bool negotiate_recv = ((offer_to_receive_ != 0) && offer_send);
768 
769   auto expected_direction =
770       RtpTransceiverDirectionFromSendRecv(negotiate_send, negotiate_recv);
771   EXPECT_EQ(expected_direction,
772             GetMediaContentDirection(answer.get(), cricket::MEDIA_TYPE_AUDIO));
773 }
774 
775 // Tests that the media section is rejected if and only if the callee has no
776 // local media track and has set offer_to_receive to 0, no matter which
777 // direction the caller indicated in the offer.
TEST_P(PeerConnectionMediaAnswerDirectionTest,VerifyRejected)778 TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyRejected) {
779   if (IsUnifiedPlan() &&
780       offer_to_receive_ != RTCOfferAnswerOptions::kUndefined) {
781     // offer_to_receive_ is not implemented when creating answers with Unified
782     // Plan semantics specified.
783     return;
784   }
785 
786   auto caller = CreatePeerConnection();
787   caller->AddAudioTrack("a");
788 
789   // Create the offer with an audio section and set its direction.
790   auto offer = caller->CreateOffer();
791   cricket::GetFirstAudioContentDescription(offer->description())
792       ->set_direction(offer_direction_);
793 
794   auto callee = CreatePeerConnection();
795   if (send_media_) {
796     callee->AddAudioTrack("a");
797   }
798   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
799 
800   // Create the answer according to the test parameters.
801   RTCOfferAnswerOptions options;
802   options.offer_to_receive_audio = offer_to_receive_;
803   auto answer = callee->CreateAnswer(options);
804 
805   // The media section is rejected if and only if offer_to_receive is explicitly
806   // set to 0 and there is no media to send.
807   auto* audio_content = cricket::GetFirstAudioContent(answer->description());
808   ASSERT_TRUE(audio_content);
809   EXPECT_EQ((offer_to_receive_ == 0 && !send_media_), audio_content->rejected);
810 }
811 
812 INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaTest,
813                          PeerConnectionMediaAnswerDirectionTest,
814                          Combine(Values(SdpSemantics::kPlanB_DEPRECATED,
815                                         SdpSemantics::kUnifiedPlan),
816                                  Values(RtpTransceiverDirection::kInactive,
817                                         RtpTransceiverDirection::kSendOnly,
818                                         RtpTransceiverDirection::kRecvOnly,
819                                         RtpTransceiverDirection::kSendRecv),
820                                  Bool(),
821                                  Values(-1, 0, 1)));
822 
TEST_P(PeerConnectionMediaTest,OfferHasDifferentDirectionForAudioVideo)823 TEST_P(PeerConnectionMediaTest, OfferHasDifferentDirectionForAudioVideo) {
824   auto caller = CreatePeerConnection();
825   caller->AddVideoTrack("v");
826 
827   RTCOfferAnswerOptions options;
828   options.offer_to_receive_audio = 1;
829   options.offer_to_receive_video = 0;
830   auto offer = caller->CreateOffer(options);
831 
832   EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
833             GetMediaContentDirection(offer.get(), cricket::MEDIA_TYPE_AUDIO));
834   EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
835             GetMediaContentDirection(offer.get(), cricket::MEDIA_TYPE_VIDEO));
836 }
837 
TEST_P(PeerConnectionMediaTest,AnswerHasDifferentDirectionsForAudioVideo)838 TEST_P(PeerConnectionMediaTest, AnswerHasDifferentDirectionsForAudioVideo) {
839   if (IsUnifiedPlan()) {
840     // offer_to_receive_ is not implemented when creating answers with Unified
841     // Plan semantics specified.
842     return;
843   }
844 
845   auto caller = CreatePeerConnectionWithAudioVideo();
846   auto callee = CreatePeerConnection();
847   callee->AddVideoTrack("v");
848 
849   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
850 
851   RTCOfferAnswerOptions options;
852   options.offer_to_receive_audio = 1;
853   options.offer_to_receive_video = 0;
854   auto answer = callee->CreateAnswer(options);
855 
856   EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
857             GetMediaContentDirection(answer.get(), cricket::MEDIA_TYPE_AUDIO));
858   EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
859             GetMediaContentDirection(answer.get(), cricket::MEDIA_TYPE_VIDEO));
860 }
861 
AddComfortNoiseCodecsToSend(cricket::FakeMediaEngine * media_engine)862 void AddComfortNoiseCodecsToSend(cricket::FakeMediaEngine* media_engine) {
863   const cricket::AudioCodec kComfortNoiseCodec8k(102, cricket::kCnCodecName,
864                                                  8000, 0, 1);
865   const cricket::AudioCodec kComfortNoiseCodec16k(103, cricket::kCnCodecName,
866                                                   16000, 0, 1);
867 
868   auto codecs = media_engine->voice().send_codecs();
869   codecs.push_back(kComfortNoiseCodec8k);
870   codecs.push_back(kComfortNoiseCodec16k);
871   media_engine->SetAudioCodecs(codecs);
872 }
873 
HasAnyComfortNoiseCodecs(const cricket::SessionDescription * desc)874 bool HasAnyComfortNoiseCodecs(const cricket::SessionDescription* desc) {
875   const auto* audio_desc = cricket::GetFirstAudioContentDescription(desc);
876   for (const auto& codec : audio_desc->codecs()) {
877     if (codec.name == cricket::kCnCodecName) {
878       return true;
879     }
880   }
881   return false;
882 }
883 
HasPayloadTypeConflict(const cricket::SessionDescription * desc)884 bool HasPayloadTypeConflict(const cricket::SessionDescription* desc) {
885   std::set<int> payload_types;
886   const auto* audio_desc = cricket::GetFirstAudioContentDescription(desc);
887   if (audio_desc) {
888     for (const auto& codec : audio_desc->codecs()) {
889       if (payload_types.count(codec.id) > 0) {
890         return true;
891       }
892       payload_types.insert(codec.id);
893     }
894   }
895   const auto* video_desc = cricket::GetFirstVideoContentDescription(desc);
896   if (video_desc) {
897     for (const auto& codec : video_desc->codecs()) {
898       if (payload_types.count(codec.id) > 0) {
899         return true;
900       }
901       payload_types.insert(codec.id);
902     }
903   }
904   return false;
905 }
906 
TEST_P(PeerConnectionMediaTest,CreateOfferWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs)907 TEST_P(PeerConnectionMediaTest,
908        CreateOfferWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs) {
909   auto fake_engine = std::make_unique<FakeMediaEngine>();
910   AddComfortNoiseCodecsToSend(fake_engine.get());
911   auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine));
912 
913   RTCOfferAnswerOptions options;
914   options.voice_activity_detection = false;
915   auto offer = caller->CreateOffer(options);
916 
917   EXPECT_FALSE(HasAnyComfortNoiseCodecs(offer->description()));
918 }
919 
TEST_P(PeerConnectionMediaTest,CreateOfferWithVoiceActivityDetectionIncludesComfortNoiseCodecs)920 TEST_P(PeerConnectionMediaTest,
921        CreateOfferWithVoiceActivityDetectionIncludesComfortNoiseCodecs) {
922   auto fake_engine = std::make_unique<FakeMediaEngine>();
923   AddComfortNoiseCodecsToSend(fake_engine.get());
924   auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine));
925 
926   RTCOfferAnswerOptions options;
927   options.voice_activity_detection = true;
928   auto offer = caller->CreateOffer(options);
929 
930   EXPECT_TRUE(HasAnyComfortNoiseCodecs(offer->description()));
931 }
932 
TEST_P(PeerConnectionMediaTest,CreateAnswerWithVoiceActivityDetectionIncludesNoComfortNoiseCodecs)933 TEST_P(PeerConnectionMediaTest,
934        CreateAnswerWithVoiceActivityDetectionIncludesNoComfortNoiseCodecs) {
935   auto caller = CreatePeerConnectionWithAudioVideo();
936 
937   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
938   AddComfortNoiseCodecsToSend(callee_fake_engine.get());
939   auto callee =
940       CreatePeerConnectionWithAudioVideo(std::move(callee_fake_engine));
941 
942   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
943 
944   RTCOfferAnswerOptions options;
945   options.voice_activity_detection = true;
946   auto answer = callee->CreateAnswer(options);
947 
948   EXPECT_FALSE(HasAnyComfortNoiseCodecs(answer->description()));
949 }
950 
TEST_P(PeerConnectionMediaTest,CreateAnswerWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs)951 TEST_P(PeerConnectionMediaTest,
952        CreateAnswerWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs) {
953   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
954   AddComfortNoiseCodecsToSend(caller_fake_engine.get());
955   auto caller =
956       CreatePeerConnectionWithAudioVideo(std::move(caller_fake_engine));
957 
958   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
959   AddComfortNoiseCodecsToSend(callee_fake_engine.get());
960   auto callee =
961       CreatePeerConnectionWithAudioVideo(std::move(callee_fake_engine));
962 
963   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
964 
965   RTCOfferAnswerOptions options;
966   options.voice_activity_detection = false;
967   auto answer = callee->CreateAnswer(options);
968 
969   EXPECT_FALSE(HasAnyComfortNoiseCodecs(answer->description()));
970 }
971 
972 // The following test group verifies that we reject answers with invalid media
973 // sections as per RFC 3264.
974 
975 class PeerConnectionMediaInvalidMediaTest
976     : public PeerConnectionMediaBaseTest,
977       public ::testing::WithParamInterface<std::tuple<
978           SdpSemantics,
979           std::tuple<std::string,
980                      std::function<void(cricket::SessionDescription*)>,
981                      std::string>>> {
982  protected:
PeerConnectionMediaInvalidMediaTest()983   PeerConnectionMediaInvalidMediaTest()
984       : PeerConnectionMediaBaseTest(std::get<0>(GetParam())) {
985     auto param = std::get<1>(GetParam());
986     mutator_ = std::get<1>(param);
987     expected_error_ = std::get<2>(param);
988   }
989 
990   std::function<void(cricket::SessionDescription*)> mutator_;
991   std::string expected_error_;
992 };
993 
TEST_P(PeerConnectionMediaInvalidMediaTest,FailToSetRemoteAnswer)994 TEST_P(PeerConnectionMediaInvalidMediaTest, FailToSetRemoteAnswer) {
995   auto caller = CreatePeerConnectionWithAudioVideo();
996   auto callee = CreatePeerConnectionWithAudioVideo();
997 
998   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
999 
1000   auto answer = callee->CreateAnswer();
1001   mutator_(answer->description());
1002 
1003   std::string error;
1004   ASSERT_FALSE(caller->SetRemoteDescription(std::move(answer), &error));
1005   EXPECT_EQ("Failed to set remote answer sdp: " + expected_error_, error);
1006 }
1007 
TEST_P(PeerConnectionMediaInvalidMediaTest,FailToSetLocalAnswer)1008 TEST_P(PeerConnectionMediaInvalidMediaTest, FailToSetLocalAnswer) {
1009   auto caller = CreatePeerConnectionWithAudioVideo();
1010   auto callee = CreatePeerConnectionWithAudioVideo();
1011 
1012   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1013 
1014   auto answer = callee->CreateAnswer();
1015   mutator_(answer->description());
1016 
1017   std::string error;
1018   ASSERT_FALSE(callee->SetLocalDescription(std::move(answer), &error));
1019   EXPECT_EQ("Failed to set local answer sdp: " + expected_error_, error);
1020 }
1021 
RemoveVideoContent(cricket::SessionDescription * desc)1022 void RemoveVideoContent(cricket::SessionDescription* desc) {
1023   auto content_name = cricket::GetFirstVideoContent(desc)->name;
1024   desc->RemoveContentByName(content_name);
1025   desc->RemoveTransportInfoByName(content_name);
1026 }
1027 
RenameVideoContent(cricket::SessionDescription * desc)1028 void RenameVideoContent(cricket::SessionDescription* desc) {
1029   auto* video_content = cricket::GetFirstVideoContent(desc);
1030   auto* transport_info = desc->GetTransportInfoByName(video_content->name);
1031   video_content->name = "video_renamed";
1032   transport_info->content_name = video_content->name;
1033 }
1034 
ReverseMediaContent(cricket::SessionDescription * desc)1035 void ReverseMediaContent(cricket::SessionDescription* desc) {
1036   absl::c_reverse(desc->contents());
1037   absl::c_reverse(desc->transport_infos());
1038 }
1039 
ChangeMediaTypeAudioToVideo(cricket::SessionDescription * desc)1040 void ChangeMediaTypeAudioToVideo(cricket::SessionDescription* desc) {
1041   std::string audio_mid = cricket::GetFirstAudioContent(desc)->name;
1042   desc->RemoveContentByName(audio_mid);
1043   auto* video_content = cricket::GetFirstVideoContent(desc);
1044   desc->AddContent(audio_mid, video_content->type,
1045                    video_content->media_description()->Clone());
1046 }
1047 
1048 constexpr char kMLinesOutOfOrder[] =
1049     "The order of m-lines in answer doesn't match order in offer. Rejecting "
1050     "answer.";
1051 
1052 INSTANTIATE_TEST_SUITE_P(
1053     PeerConnectionMediaTest,
1054     PeerConnectionMediaInvalidMediaTest,
1055     Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
1056             Values(std::make_tuple("remove video",
1057                                    RemoveVideoContent,
1058                                    kMLinesOutOfOrder),
1059                    std::make_tuple("rename video",
1060                                    RenameVideoContent,
1061                                    kMLinesOutOfOrder),
1062                    std::make_tuple("reverse media sections",
1063                                    ReverseMediaContent,
1064                                    kMLinesOutOfOrder),
1065                    std::make_tuple("change audio type to video type",
1066                                    ChangeMediaTypeAudioToVideo,
1067                                    kMLinesOutOfOrder))));
1068 
1069 // Test that the correct media engine send/recv streams are created when doing
1070 // a series of offer/answers where audio/video are both sent, then audio is
1071 // rejected, then both audio/video sent again.
TEST_P(PeerConnectionMediaTest,TestAVOfferWithAudioOnlyAnswer)1072 TEST_P(PeerConnectionMediaTest, TestAVOfferWithAudioOnlyAnswer) {
1073   if (IsUnifiedPlan()) {
1074     // offer_to_receive_ is not implemented when creating answers with Unified
1075     // Plan semantics specified.
1076     return;
1077   }
1078 
1079   RTCOfferAnswerOptions options_reject_video;
1080   options_reject_video.offer_to_receive_audio =
1081       RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
1082   options_reject_video.offer_to_receive_video = 0;
1083 
1084   auto caller = CreatePeerConnection();
1085   caller->AddAudioTrack("a");
1086   caller->AddVideoTrack("v");
1087   auto callee = CreatePeerConnection();
1088 
1089   // Caller initially offers to send/recv audio and video.
1090   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1091   // Callee accepts the audio as recv only but rejects the video.
1092   ASSERT_TRUE(caller->SetRemoteDescription(
1093       callee->CreateAnswerAndSetAsLocal(options_reject_video)));
1094 
1095   auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
1096   ASSERT_TRUE(caller_voice);
1097   EXPECT_EQ(0u, caller_voice->recv_streams().size());
1098   EXPECT_EQ(1u, caller_voice->send_streams().size());
1099   auto caller_video = caller->media_engine()->GetVideoChannel(0);
1100   EXPECT_FALSE(caller_video);
1101 
1102   // Callee adds its own audio/video stream and offers to receive audio/video
1103   // too.
1104   callee->AddAudioTrack("a");
1105   auto callee_video_track = callee->AddVideoTrack("v");
1106   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1107   ASSERT_TRUE(
1108       caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
1109 
1110   auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
1111   ASSERT_TRUE(callee_voice);
1112   EXPECT_EQ(1u, callee_voice->recv_streams().size());
1113   EXPECT_EQ(1u, callee_voice->send_streams().size());
1114   auto callee_video = callee->media_engine()->GetVideoChannel(0);
1115   ASSERT_TRUE(callee_video);
1116   EXPECT_EQ(1u, callee_video->recv_streams().size());
1117   EXPECT_EQ(1u, callee_video->send_streams().size());
1118 
1119   // Callee removes video but keeps audio and rejects the video once again.
1120   callee->pc()->RemoveTrackOrError(callee_video_track);
1121   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1122   ASSERT_TRUE(
1123       callee->SetLocalDescription(callee->CreateAnswer(options_reject_video)));
1124 
1125   callee_voice = callee->media_engine()->GetVoiceChannel(0);
1126   ASSERT_TRUE(callee_voice);
1127   EXPECT_EQ(1u, callee_voice->recv_streams().size());
1128   EXPECT_EQ(1u, callee_voice->send_streams().size());
1129   callee_video = callee->media_engine()->GetVideoChannel(0);
1130   EXPECT_FALSE(callee_video);
1131 }
1132 
1133 // Test that the correct media engine send/recv streams are created when doing
1134 // a series of offer/answers where audio/video are both sent, then video is
1135 // rejected, then both audio/video sent again.
TEST_P(PeerConnectionMediaTest,TestAVOfferWithVideoOnlyAnswer)1136 TEST_P(PeerConnectionMediaTest, TestAVOfferWithVideoOnlyAnswer) {
1137   if (IsUnifiedPlan()) {
1138     // offer_to_receive_ is not implemented when creating answers with Unified
1139     // Plan semantics specified.
1140     return;
1141   }
1142 
1143   // Disable the bundling here. If the media is bundled on audio
1144   // transport, then we can't reject the audio because switching the bundled
1145   // transport is not currently supported.
1146   // (https://bugs.chromium.org/p/webrtc/issues/detail?id=6704)
1147   RTCOfferAnswerOptions options_no_bundle;
1148   options_no_bundle.use_rtp_mux = false;
1149   RTCOfferAnswerOptions options_reject_audio = options_no_bundle;
1150   options_reject_audio.offer_to_receive_audio = 0;
1151   options_reject_audio.offer_to_receive_video =
1152       RTCOfferAnswerOptions::kMaxOfferToReceiveMedia;
1153 
1154   auto caller = CreatePeerConnection();
1155   caller->AddAudioTrack("a");
1156   caller->AddVideoTrack("v");
1157   auto callee = CreatePeerConnection();
1158 
1159   // Caller initially offers to send/recv audio and video.
1160   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1161   // Callee accepts the video as recv only but rejects the audio.
1162   ASSERT_TRUE(caller->SetRemoteDescription(
1163       callee->CreateAnswerAndSetAsLocal(options_reject_audio)));
1164 
1165   auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
1166   EXPECT_FALSE(caller_voice);
1167   auto caller_video = caller->media_engine()->GetVideoChannel(0);
1168   ASSERT_TRUE(caller_video);
1169   EXPECT_EQ(0u, caller_video->recv_streams().size());
1170   EXPECT_EQ(1u, caller_video->send_streams().size());
1171 
1172   // Callee adds its own audio/video stream and offers to receive audio/video
1173   // too.
1174   auto callee_audio_track = callee->AddAudioTrack("a");
1175   callee->AddVideoTrack("v");
1176   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1177   ASSERT_TRUE(caller->SetRemoteDescription(
1178       callee->CreateAnswerAndSetAsLocal(options_no_bundle)));
1179 
1180   auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
1181   ASSERT_TRUE(callee_voice);
1182   EXPECT_EQ(1u, callee_voice->recv_streams().size());
1183   EXPECT_EQ(1u, callee_voice->send_streams().size());
1184   auto callee_video = callee->media_engine()->GetVideoChannel(0);
1185   ASSERT_TRUE(callee_video);
1186   EXPECT_EQ(1u, callee_video->recv_streams().size());
1187   EXPECT_EQ(1u, callee_video->send_streams().size());
1188 
1189   // Callee removes audio but keeps video and rejects the audio once again.
1190   callee->pc()->RemoveTrackOrError(callee_audio_track);
1191   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1192   ASSERT_TRUE(
1193       callee->SetLocalDescription(callee->CreateAnswer(options_reject_audio)));
1194 
1195   callee_voice = callee->media_engine()->GetVoiceChannel(0);
1196   EXPECT_FALSE(callee_voice);
1197   callee_video = callee->media_engine()->GetVideoChannel(0);
1198   ASSERT_TRUE(callee_video);
1199   EXPECT_EQ(1u, callee_video->recv_streams().size());
1200   EXPECT_EQ(1u, callee_video->send_streams().size());
1201 }
1202 
1203 // Tests that if the underlying video encoder fails to be initialized (signaled
1204 // by failing to set send codecs), the PeerConnection signals the error to the
1205 // client.
TEST_P(PeerConnectionMediaTest,MediaEngineErrorPropagatedToClients)1206 TEST_P(PeerConnectionMediaTest, MediaEngineErrorPropagatedToClients) {
1207   auto caller = CreatePeerConnectionWithAudioVideo();
1208   auto callee = CreatePeerConnectionWithAudioVideo();
1209 
1210   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1211 
1212   auto video_channel = caller->media_engine()->GetVideoChannel(0);
1213   video_channel->set_fail_set_send_codecs(true);
1214 
1215   std::string error;
1216   ASSERT_FALSE(caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(),
1217                                             &error));
1218   EXPECT_EQ(std::string("Failed to set remote answer sdp: Failed to set remote "
1219                         "video description "
1220                         "send parameters for m-section with mid='") +
1221                 (IsUnifiedPlan() ? "1" : "video") + "'.",
1222             error);
1223 }
1224 
1225 // Tests that if the underlying video encoder fails once then subsequent
1226 // attempts at setting the local/remote description will also fail, even if
1227 // SetSendCodecs no longer fails.
TEST_P(PeerConnectionMediaTest,FailToApplyDescriptionIfVideoEncoderHasEverFailed)1228 TEST_P(PeerConnectionMediaTest,
1229        FailToApplyDescriptionIfVideoEncoderHasEverFailed) {
1230   auto caller = CreatePeerConnectionWithAudioVideo();
1231   auto callee = CreatePeerConnectionWithAudioVideo();
1232 
1233   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1234 
1235   auto video_channel = caller->media_engine()->GetVideoChannel(0);
1236   video_channel->set_fail_set_send_codecs(true);
1237 
1238   EXPECT_FALSE(
1239       caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
1240 
1241   video_channel->set_fail_set_send_codecs(false);
1242 
1243   EXPECT_FALSE(caller->SetRemoteDescription(callee->CreateAnswer()));
1244   EXPECT_FALSE(caller->SetLocalDescription(caller->CreateOffer()));
1245 }
1246 
RenameContent(cricket::SessionDescription * desc,cricket::MediaType media_type,const std::string & new_name)1247 void RenameContent(cricket::SessionDescription* desc,
1248                    cricket::MediaType media_type,
1249                    const std::string& new_name) {
1250   auto* content = cricket::GetFirstMediaContent(desc, media_type);
1251   RTC_DCHECK(content);
1252   std::string old_name = content->name;
1253   content->name = new_name;
1254   auto* transport = desc->GetTransportInfoByName(old_name);
1255   RTC_DCHECK(transport);
1256   transport->content_name = new_name;
1257 
1258   // Rename the content name in the BUNDLE group.
1259   cricket::ContentGroup new_bundle_group =
1260       *desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1261   new_bundle_group.RemoveContentName(old_name);
1262   new_bundle_group.AddContentName(new_name);
1263   desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1264   desc->AddGroup(new_bundle_group);
1265 }
1266 
1267 // Tests that an answer responds with the same MIDs as the offer.
TEST_P(PeerConnectionMediaTest,AnswerHasSameMidsAsOffer)1268 TEST_P(PeerConnectionMediaTest, AnswerHasSameMidsAsOffer) {
1269   const std::string kAudioMid = "notdefault1";
1270   const std::string kVideoMid = "notdefault2";
1271 
1272   auto caller = CreatePeerConnectionWithAudioVideo();
1273   auto callee = CreatePeerConnectionWithAudioVideo();
1274 
1275   auto offer = caller->CreateOffer();
1276   RenameContent(offer->description(), cricket::MEDIA_TYPE_AUDIO, kAudioMid);
1277   RenameContent(offer->description(), cricket::MEDIA_TYPE_VIDEO, kVideoMid);
1278   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1279 
1280   auto answer = callee->CreateAnswer();
1281   EXPECT_EQ(kAudioMid,
1282             cricket::GetFirstAudioContent(answer->description())->name);
1283   EXPECT_EQ(kVideoMid,
1284             cricket::GetFirstVideoContent(answer->description())->name);
1285 }
1286 
1287 // Test that if the callee creates a re-offer, the MIDs are the same as the
1288 // original offer.
TEST_P(PeerConnectionMediaTest,ReOfferHasSameMidsAsFirstOffer)1289 TEST_P(PeerConnectionMediaTest, ReOfferHasSameMidsAsFirstOffer) {
1290   const std::string kAudioMid = "notdefault1";
1291   const std::string kVideoMid = "notdefault2";
1292 
1293   auto caller = CreatePeerConnectionWithAudioVideo();
1294   auto callee = CreatePeerConnectionWithAudioVideo();
1295 
1296   auto offer = caller->CreateOffer();
1297   RenameContent(offer->description(), cricket::MEDIA_TYPE_AUDIO, kAudioMid);
1298   RenameContent(offer->description(), cricket::MEDIA_TYPE_VIDEO, kVideoMid);
1299   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1300   ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
1301 
1302   auto reoffer = callee->CreateOffer();
1303   EXPECT_EQ(kAudioMid,
1304             cricket::GetFirstAudioContent(reoffer->description())->name);
1305   EXPECT_EQ(kVideoMid,
1306             cricket::GetFirstVideoContent(reoffer->description())->name);
1307 }
1308 
1309 // Test that SetRemoteDescription returns an error if there are two m= sections
1310 // with the same MID value.
TEST_P(PeerConnectionMediaTest,SetRemoteDescriptionFailsWithDuplicateMids)1311 TEST_P(PeerConnectionMediaTest, SetRemoteDescriptionFailsWithDuplicateMids) {
1312   auto caller = CreatePeerConnectionWithAudioVideo();
1313   auto callee = CreatePeerConnectionWithAudioVideo();
1314 
1315   auto offer = caller->CreateOffer();
1316   RenameContent(offer->description(), cricket::MEDIA_TYPE_AUDIO, "same");
1317   RenameContent(offer->description(), cricket::MEDIA_TYPE_VIDEO, "same");
1318 
1319   std::string error;
1320   EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer), &error));
1321   EXPECT_EQ(error,
1322             "Failed to set remote offer sdp: Duplicate a=mid value 'same'.");
1323 }
1324 
TEST_P(PeerConnectionMediaTest,CombinedAudioVideoBweConfigPropagatedToMediaEngine)1325 TEST_P(PeerConnectionMediaTest,
1326        CombinedAudioVideoBweConfigPropagatedToMediaEngine) {
1327   RTCConfiguration config;
1328   config.combined_audio_video_bwe.emplace(true);
1329   auto caller = CreatePeerConnectionWithAudioVideo(config);
1330 
1331   ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
1332 
1333   auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
1334   ASSERT_TRUE(caller_voice);
1335   const cricket::AudioOptions& audio_options = caller_voice->options();
1336   EXPECT_EQ(config.combined_audio_video_bwe,
1337             audio_options.combined_audio_video_bwe);
1338 }
1339 
1340 // Test that if a RED codec refers to another codec in its fmtp line, but that
1341 // codec's payload type was reassigned for some reason (either the remote
1342 // endpoint selected a different payload type or there was a conflict), the RED
1343 // fmtp line is modified to refer to the correct payload type.
TEST_P(PeerConnectionMediaTest,RedFmtpPayloadTypeReassigned)1344 TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeReassigned) {
1345   std::vector<cricket::AudioCodec> caller_fake_codecs;
1346   caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1347   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1348   caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1349   auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1350 
1351   std::vector<cricket::AudioCodec> callee_fake_codecs;
1352   callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1353   callee_fake_codecs.push_back(
1354       cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1355   callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1356                                      "120/120");
1357   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1358   callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1359   auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1360 
1361   // Offer from the caller establishes 100 as the "foo" payload type.
1362   auto offer = caller->CreateOfferAndSetAsLocal();
1363   callee->SetRemoteDescription(std::move(offer));
1364   auto answer = callee->CreateAnswerAndSetAsLocal();
1365   auto answer_description =
1366       cricket::GetFirstAudioContentDescription(answer->description());
1367   ASSERT_EQ(1u, answer_description->codecs().size());
1368 
1369   // Offer from the callee should respect the established payload type, and
1370   // attempt to add RED, which should refer to the correct payload type.
1371   offer = callee->CreateOfferAndSetAsLocal();
1372   auto* offer_description =
1373       cricket::GetFirstAudioContentDescription(offer->description());
1374   ASSERT_EQ(2u, offer_description->codecs().size());
1375   for (const auto& codec : offer_description->codecs()) {
1376     if (codec.name == "foo") {
1377       ASSERT_EQ(100, codec.id);
1378     } else if (codec.name == cricket::kRedCodecName) {
1379       std::string fmtp;
1380       ASSERT_TRUE(codec.GetParam("", &fmtp));
1381       EXPECT_EQ("100/100", fmtp);
1382     }
1383   }
1384 }
1385 
1386 // Test that RED without fmtp does match RED without fmtp.
TEST_P(PeerConnectionMediaTest,RedFmtpPayloadTypeNoFmtpMatchNoFmtp)1387 TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeNoFmtpMatchNoFmtp) {
1388   std::vector<cricket::AudioCodec> caller_fake_codecs;
1389   caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1390   caller_fake_codecs.push_back(
1391       cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
1392   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1393   caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1394   auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1395 
1396   std::vector<cricket::AudioCodec> callee_fake_codecs;
1397   callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1398   callee_fake_codecs.push_back(
1399       cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1400   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1401   callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1402   auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1403 
1404   // Offer from the caller establishes 100 as the "foo" payload type.
1405   // Red (without fmtp) is negotiated.
1406   auto offer = caller->CreateOfferAndSetAsLocal();
1407   callee->SetRemoteDescription(std::move(offer));
1408   auto answer = callee->CreateAnswerAndSetAsLocal();
1409   auto answer_description =
1410       cricket::GetFirstAudioContentDescription(answer->description());
1411   ASSERT_EQ(2u, answer_description->codecs().size());
1412 
1413   // Offer from the callee should respect the established payload type, and
1414   // attempt to add RED.
1415   offer = callee->CreateOfferAndSetAsLocal();
1416   auto* offer_description =
1417       cricket::GetFirstAudioContentDescription(offer->description());
1418   ASSERT_EQ(2u, offer_description->codecs().size());
1419   for (const auto& codec : offer_description->codecs()) {
1420     if (codec.name == "foo") {
1421       ASSERT_EQ(100, codec.id);
1422     } else if (codec.name == cricket::kRedCodecName) {
1423       ASSERT_EQ(101, codec.id);
1424     }
1425   }
1426 }
1427 
1428 // Test that RED without fmtp does not match RED with fmtp.
TEST_P(PeerConnectionMediaTest,RedFmtpPayloadTypeNoFmtpNoMatchFmtp)1429 TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeNoFmtpNoMatchFmtp) {
1430   std::vector<cricket::AudioCodec> caller_fake_codecs;
1431   caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1432   caller_fake_codecs.push_back(
1433       cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
1434   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1435   caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1436   auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1437 
1438   std::vector<cricket::AudioCodec> callee_fake_codecs;
1439   callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1440   callee_fake_codecs.push_back(
1441       cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1442   callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1443                                      "120/120");
1444   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1445   callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1446   auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1447 
1448   // Offer from the caller establishes 100 as the "foo" payload type.
1449   // It should not negotiate RED.
1450   auto offer = caller->CreateOfferAndSetAsLocal();
1451   callee->SetRemoteDescription(std::move(offer));
1452   auto answer = callee->CreateAnswerAndSetAsLocal();
1453   auto answer_description =
1454       cricket::GetFirstAudioContentDescription(answer->description());
1455   ASSERT_EQ(1u, answer_description->codecs().size());
1456 
1457   // Offer from the callee should respect the established payload type, and
1458   // attempt to add RED, which should refer to the correct payload type.
1459   offer = callee->CreateOfferAndSetAsLocal();
1460   auto* offer_description =
1461       cricket::GetFirstAudioContentDescription(offer->description());
1462   ASSERT_EQ(2u, offer_description->codecs().size());
1463   for (const auto& codec : offer_description->codecs()) {
1464     if (codec.name == "foo") {
1465       ASSERT_EQ(100, codec.id);
1466     } else if (codec.name == cricket::kRedCodecName) {
1467       std::string fmtp;
1468       ASSERT_TRUE(
1469           codec.GetParam(cricket::kCodecParamNotInNameValueFormat, &fmtp));
1470       EXPECT_EQ("100/100", fmtp);
1471     }
1472   }
1473 }
1474 
1475 // Test that RED with fmtp must match base codecs.
TEST_P(PeerConnectionMediaTest,RedFmtpPayloadTypeMustMatchBaseCodecs)1476 TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeMustMatchBaseCodecs) {
1477   std::vector<cricket::AudioCodec> caller_fake_codecs;
1478   caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1479   caller_fake_codecs.push_back(
1480       cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
1481   caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1482                                      "100/100");
1483   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1484   caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1485   auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1486 
1487   std::vector<cricket::AudioCodec> callee_fake_codecs;
1488   callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1489   callee_fake_codecs.push_back(
1490       cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1491   callee_fake_codecs.push_back(cricket::AudioCodec(122, "bar", 0, 0, 1));
1492   callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1493                                      "122/122");
1494   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1495   callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1496   auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1497 
1498   // Offer from the caller establishes 100 as the "foo" payload type.
1499   // It should not negotiate RED since RED is associated with foo, not bar.
1500   auto offer = caller->CreateOfferAndSetAsLocal();
1501   callee->SetRemoteDescription(std::move(offer));
1502   auto answer = callee->CreateAnswerAndSetAsLocal();
1503   auto answer_description =
1504       cricket::GetFirstAudioContentDescription(answer->description());
1505   ASSERT_EQ(1u, answer_description->codecs().size());
1506 }
1507 
1508 // Test behaviour when the RED fmtp attempts to specify different codecs
1509 // which is not supported.
TEST_P(PeerConnectionMediaTest,RedFmtpPayloadMixed)1510 TEST_P(PeerConnectionMediaTest, RedFmtpPayloadMixed) {
1511   std::vector<cricket::AudioCodec> caller_fake_codecs;
1512   caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1513   caller_fake_codecs.push_back(cricket::AudioCodec(102, "bar", 0, 0, 1));
1514   caller_fake_codecs.push_back(
1515       cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
1516   caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1517                                      "100/102");
1518   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1519   caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1520   auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1521 
1522   std::vector<cricket::AudioCodec> callee_fake_codecs;
1523   callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1524   callee_fake_codecs.push_back(
1525       cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1526   callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1527                                      "120/120");
1528   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1529   callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1530   auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1531 
1532   // Offer from the caller establishes 100 as the "foo" payload type.
1533   auto offer = caller->CreateOfferAndSetAsLocal();
1534   callee->SetRemoteDescription(std::move(offer));
1535   auto answer = callee->CreateAnswerAndSetAsLocal();
1536   auto answer_description =
1537       cricket::GetFirstAudioContentDescription(answer->description());
1538   // RED is not negotiated.
1539   ASSERT_EQ(1u, answer_description->codecs().size());
1540 }
1541 
1542 // Test behaviour when the RED fmtp attempts to negotiate different levels of
1543 // redundancy.
TEST_P(PeerConnectionMediaTest,RedFmtpPayloadDifferentRedundancy)1544 TEST_P(PeerConnectionMediaTest, RedFmtpPayloadDifferentRedundancy) {
1545   std::vector<cricket::AudioCodec> caller_fake_codecs;
1546   caller_fake_codecs.push_back(cricket::AudioCodec(100, "foo", 0, 0, 1));
1547   caller_fake_codecs.push_back(
1548       cricket::AudioCodec(101, cricket::kRedCodecName, 0, 0, 1));
1549   caller_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1550                                      "100/100");
1551   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1552   caller_fake_engine->SetAudioCodecs(caller_fake_codecs);
1553   auto caller = CreatePeerConnectionWithAudio(std::move(caller_fake_engine));
1554 
1555   std::vector<cricket::AudioCodec> callee_fake_codecs;
1556   callee_fake_codecs.push_back(cricket::AudioCodec(120, "foo", 0, 0, 1));
1557   callee_fake_codecs.push_back(
1558       cricket::AudioCodec(121, cricket::kRedCodecName, 0, 0, 1));
1559   callee_fake_codecs.back().SetParam(cricket::kCodecParamNotInNameValueFormat,
1560                                      "120/120/120");
1561   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1562   callee_fake_engine->SetAudioCodecs(callee_fake_codecs);
1563   auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine));
1564 
1565   // Offer from the caller establishes 100 as the "foo" payload type.
1566   auto offer = caller->CreateOfferAndSetAsLocal();
1567   callee->SetRemoteDescription(std::move(offer));
1568   auto answer = callee->CreateAnswerAndSetAsLocal();
1569   auto answer_description =
1570       cricket::GetFirstAudioContentDescription(answer->description());
1571   // RED is negotiated.
1572   ASSERT_EQ(2u, answer_description->codecs().size());
1573 
1574   // Offer from the callee should respect the established payload type, and
1575   // attempt to add RED, which should refer to the correct payload type.
1576   offer = callee->CreateOfferAndSetAsLocal();
1577   auto* offer_description =
1578       cricket::GetFirstAudioContentDescription(offer->description());
1579   ASSERT_EQ(2u, offer_description->codecs().size());
1580   for (const auto& codec : offer_description->codecs()) {
1581     if (codec.name == "foo") {
1582       ASSERT_EQ(100, codec.id);
1583     } else if (codec.name == cricket::kRedCodecName) {
1584       std::string fmtp;
1585       ASSERT_TRUE(
1586           codec.GetParam(cricket::kCodecParamNotInNameValueFormat, &fmtp));
1587       EXPECT_EQ("100/100", fmtp);
1588     }
1589   }
1590 }
1591 
1592 template <typename C>
CompareCodecs(const std::vector<webrtc::RtpCodecCapability> & capabilities,const std::vector<C> & codecs)1593 bool CompareCodecs(const std::vector<webrtc::RtpCodecCapability>& capabilities,
1594                    const std::vector<C>& codecs) {
1595   bool capability_has_rtx =
1596       absl::c_any_of(capabilities, [](const webrtc::RtpCodecCapability& codec) {
1597         return codec.name == cricket::kRtxCodecName;
1598       });
1599   bool codecs_has_rtx = absl::c_any_of(codecs, [](const C& codec) {
1600     return codec.name == cricket::kRtxCodecName;
1601   });
1602 
1603   std::vector<C> codecs_no_rtx;
1604   absl::c_copy_if(
1605       codecs, std::back_inserter(codecs_no_rtx),
1606       [](const C& codec) { return codec.name != cricket::kRtxCodecName; });
1607 
1608   std::vector<webrtc::RtpCodecCapability> capabilities_no_rtx;
1609   absl::c_copy_if(capabilities, std::back_inserter(capabilities_no_rtx),
1610                   [](const webrtc::RtpCodecCapability& codec) {
1611                     return codec.name != cricket::kRtxCodecName;
1612                   });
1613 
1614   return capability_has_rtx == codecs_has_rtx &&
1615          absl::c_equal(
1616              capabilities_no_rtx, codecs_no_rtx,
1617              [](const webrtc::RtpCodecCapability& capability, const C& codec) {
1618                return codec.MatchesCapability(capability);
1619              });
1620 }
1621 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAudioMissingRecvCodec)1622 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1623        SetCodecPreferencesAudioMissingRecvCodec) {
1624   auto fake_engine = std::make_unique<FakeMediaEngine>();
1625   auto send_codecs = fake_engine->voice().send_codecs();
1626   send_codecs.push_back(cricket::AudioCodec(send_codecs.back().id + 1,
1627                                             "send_only_codec", 0, 0, 1));
1628   fake_engine->SetAudioSendCodecs(send_codecs);
1629 
1630   auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
1631 
1632   auto transceiver = caller->pc()->GetTransceivers().front();
1633   auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
1634       cricket::MediaType::MEDIA_TYPE_AUDIO);
1635 
1636   std::vector<webrtc::RtpCodecCapability> codecs;
1637   absl::c_copy_if(capabilities.codecs, std::back_inserter(codecs),
1638                   [](const webrtc::RtpCodecCapability& codec) {
1639                     return codec.name.find("_only_") != std::string::npos;
1640                   });
1641 
1642   auto result = transceiver->SetCodecPreferences(codecs);
1643   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1644 }
1645 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAudioMissingSendCodec)1646 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1647        SetCodecPreferencesAudioMissingSendCodec) {
1648   auto fake_engine = std::make_unique<FakeMediaEngine>();
1649   auto recv_codecs = fake_engine->voice().recv_codecs();
1650   recv_codecs.push_back(cricket::AudioCodec(recv_codecs.back().id + 1,
1651                                             "recv_only_codec", 0, 0, 1));
1652   fake_engine->SetAudioRecvCodecs(recv_codecs);
1653   auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
1654 
1655   auto transceiver = caller->pc()->GetTransceivers().front();
1656   auto capabilities = caller->pc_factory()->GetRtpReceiverCapabilities(
1657       cricket::MediaType::MEDIA_TYPE_AUDIO);
1658 
1659   std::vector<webrtc::RtpCodecCapability> codecs;
1660   absl::c_copy_if(capabilities.codecs, std::back_inserter(codecs),
1661                   [](const webrtc::RtpCodecCapability& codec) {
1662                     return codec.name.find("_only_") != std::string::npos;
1663                   });
1664 
1665   auto result = transceiver->SetCodecPreferences(codecs);
1666   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1667 }
1668 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAudioRejectsVideoCodec)1669 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1670        SetCodecPreferencesAudioRejectsVideoCodec) {
1671   auto caller = CreatePeerConnectionWithAudio();
1672 
1673   auto transceiver = caller->pc()->GetTransceivers().front();
1674   auto video_codecs =
1675       caller->pc_factory()
1676           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
1677           .codecs;
1678   auto codecs =
1679       caller->pc_factory()
1680           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
1681           .codecs;
1682   codecs.insert(codecs.end(), video_codecs.begin(), video_codecs.end());
1683   auto result = transceiver->SetCodecPreferences(codecs);
1684   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1685 }
1686 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAudioRejectsOnlyRtxRedFec)1687 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1688        SetCodecPreferencesAudioRejectsOnlyRtxRedFec) {
1689   auto fake_engine = std::make_unique<FakeMediaEngine>();
1690   auto audio_codecs = fake_engine->voice().send_codecs();
1691   audio_codecs.push_back(cricket::AudioCodec(audio_codecs.back().id + 1,
1692                                              cricket::kRtxCodecName, 0, 0, 1));
1693   audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1694       std::to_string(audio_codecs.back().id - 1);
1695   audio_codecs.push_back(cricket::AudioCodec(audio_codecs.back().id + 1,
1696                                              cricket::kRedCodecName, 0, 0, 1));
1697   audio_codecs.push_back(cricket::AudioCodec(
1698       audio_codecs.back().id + 1, cricket::kUlpfecCodecName, 0, 0, 1));
1699   fake_engine->SetAudioCodecs(audio_codecs);
1700 
1701   auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
1702 
1703   auto transceiver = caller->pc()->GetTransceivers().front();
1704   auto codecs =
1705       caller->pc_factory()
1706           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
1707           .codecs;
1708   auto codecs_only_rtx_red_fec = codecs;
1709   auto it = std::remove_if(codecs_only_rtx_red_fec.begin(),
1710                            codecs_only_rtx_red_fec.end(),
1711                            [](const webrtc::RtpCodecCapability& codec) {
1712                              return !(codec.name == cricket::kRtxCodecName ||
1713                                       codec.name == cricket::kRedCodecName ||
1714                                       codec.name == cricket::kUlpfecCodecName);
1715                            });
1716   codecs_only_rtx_red_fec.erase(it, codecs_only_rtx_red_fec.end());
1717 
1718   auto result = transceiver->SetCodecPreferences(codecs_only_rtx_red_fec);
1719   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1720 }
1721 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAllAudioCodecs)1722 TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesAllAudioCodecs) {
1723   auto caller = CreatePeerConnectionWithAudio();
1724 
1725   auto sender_audio_codecs =
1726       caller->pc_factory()
1727           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1728           .codecs;
1729 
1730   auto audio_transceiver = caller->pc()->GetTransceivers().front();
1731 
1732   // Normal case, set all capabilities as preferences
1733   EXPECT_TRUE(audio_transceiver->SetCodecPreferences(sender_audio_codecs).ok());
1734   auto offer = caller->CreateOffer();
1735   auto codecs = offer->description()
1736                     ->contents()[0]
1737                     .media_description()
1738                     ->as_audio()
1739                     ->codecs();
1740   EXPECT_TRUE(CompareCodecs(sender_audio_codecs, codecs));
1741 }
1742 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesResetAudioCodecs)1743 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1744        SetCodecPreferencesResetAudioCodecs) {
1745   auto caller = CreatePeerConnectionWithAudio();
1746 
1747   auto sender_audio_codecs =
1748       caller->pc_factory()
1749           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1750           .codecs;
1751   std::vector<webrtc::RtpCodecCapability> empty_codecs = {};
1752 
1753   auto audio_transceiver = caller->pc()->GetTransceivers().front();
1754 
1755   // Normal case, reset codec preferences
1756   EXPECT_TRUE(audio_transceiver->SetCodecPreferences(empty_codecs).ok());
1757   auto offer = caller->CreateOffer();
1758   auto codecs = offer->description()
1759                     ->contents()[0]
1760                     .media_description()
1761                     ->as_audio()
1762                     ->codecs();
1763   EXPECT_TRUE(CompareCodecs(sender_audio_codecs, codecs));
1764 }
1765 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoRejectsAudioCodec)1766 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1767        SetCodecPreferencesVideoRejectsAudioCodec) {
1768   auto caller = CreatePeerConnectionWithVideo();
1769 
1770   auto transceiver = caller->pc()->GetTransceivers().front();
1771   auto audio_codecs =
1772       caller->pc_factory()
1773           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
1774           .codecs;
1775   auto codecs =
1776       caller->pc_factory()
1777           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
1778           .codecs;
1779   codecs.insert(codecs.end(), audio_codecs.begin(), audio_codecs.end());
1780   auto result = transceiver->SetCodecPreferences(codecs);
1781   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1782 }
1783 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoRejectsOnlyRtxRedFec)1784 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1785        SetCodecPreferencesVideoRejectsOnlyRtxRedFec) {
1786   auto fake_engine = std::make_unique<FakeMediaEngine>();
1787   auto video_codecs = fake_engine->video().send_codecs();
1788   video_codecs.push_back(
1789       cricket::VideoCodec(video_codecs.back().id + 1, cricket::kRtxCodecName));
1790   video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1791       std::to_string(video_codecs.back().id - 1);
1792   video_codecs.push_back(
1793       cricket::VideoCodec(video_codecs.back().id + 1, cricket::kRedCodecName));
1794   video_codecs.push_back(cricket::VideoCodec(video_codecs.back().id + 1,
1795                                              cricket::kUlpfecCodecName));
1796   fake_engine->SetVideoCodecs(video_codecs);
1797 
1798   auto caller = CreatePeerConnectionWithVideo(std::move(fake_engine));
1799 
1800   auto transceiver = caller->pc()->GetTransceivers().front();
1801   auto codecs =
1802       caller->pc_factory()
1803           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
1804           .codecs;
1805   auto codecs_only_rtx_red_fec = codecs;
1806   auto it = std::remove_if(codecs_only_rtx_red_fec.begin(),
1807                            codecs_only_rtx_red_fec.end(),
1808                            [](const webrtc::RtpCodecCapability& codec) {
1809                              return !(codec.name == cricket::kRtxCodecName ||
1810                                       codec.name == cricket::kRedCodecName ||
1811                                       codec.name == cricket::kUlpfecCodecName);
1812                            });
1813   codecs_only_rtx_red_fec.erase(it, codecs_only_rtx_red_fec.end());
1814 
1815   auto result = transceiver->SetCodecPreferences(codecs_only_rtx_red_fec);
1816   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1817 }
1818 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAllVideoCodecs)1819 TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesAllVideoCodecs) {
1820   auto caller = CreatePeerConnectionWithVideo();
1821 
1822   auto sender_video_codecs =
1823       caller->pc_factory()
1824           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1825           .codecs;
1826 
1827   auto video_transceiver = caller->pc()->GetTransceivers().front();
1828 
1829   // Normal case, setting preferences to normal capabilities
1830   EXPECT_TRUE(video_transceiver->SetCodecPreferences(sender_video_codecs).ok());
1831   auto offer = caller->CreateOffer();
1832   auto codecs = offer->description()
1833                     ->contents()[0]
1834                     .media_description()
1835                     ->as_video()
1836                     ->codecs();
1837   EXPECT_TRUE(CompareCodecs(sender_video_codecs, codecs));
1838 }
1839 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesResetVideoCodecs)1840 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1841        SetCodecPreferencesResetVideoCodecs) {
1842   auto caller = CreatePeerConnectionWithVideo();
1843 
1844   auto sender_video_codecs =
1845       caller->pc_factory()
1846           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1847           .codecs;
1848 
1849   std::vector<webrtc::RtpCodecCapability> empty_codecs = {};
1850 
1851   auto video_transceiver = caller->pc()->GetTransceivers().front();
1852 
1853   // Normal case, resetting preferences with empty list of codecs
1854   EXPECT_TRUE(video_transceiver->SetCodecPreferences(empty_codecs).ok());
1855   auto offer = caller->CreateOffer();
1856   auto codecs = offer->description()
1857                     ->contents()[0]
1858                     .media_description()
1859                     ->as_video()
1860                     ->codecs();
1861   EXPECT_TRUE(CompareCodecs(sender_video_codecs, codecs));
1862 }
1863 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoCodecDuplicatesRemoved)1864 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1865        SetCodecPreferencesVideoCodecDuplicatesRemoved) {
1866   auto caller = CreatePeerConnectionWithVideo();
1867 
1868   auto sender_video_codecs =
1869       caller->pc_factory()
1870           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1871           .codecs;
1872 
1873   auto video_transceiver = caller->pc()->GetTransceivers().front();
1874 
1875   // Check duplicates are removed
1876   auto single_codec = sender_video_codecs;
1877   single_codec.resize(1);
1878   auto duplicate_codec = single_codec;
1879   duplicate_codec.push_back(duplicate_codec.front());
1880   duplicate_codec.push_back(duplicate_codec.front());
1881   duplicate_codec.push_back(duplicate_codec.front());
1882 
1883   EXPECT_TRUE(video_transceiver->SetCodecPreferences(duplicate_codec).ok());
1884   auto offer = caller->CreateOffer();
1885   auto codecs = offer->description()
1886                     ->contents()[0]
1887                     .media_description()
1888                     ->as_video()
1889                     ->codecs();
1890   EXPECT_TRUE(CompareCodecs(single_codec, codecs));
1891 }
1892 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoWithRtx)1893 TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesVideoWithRtx) {
1894   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1895   auto caller_video_codecs = caller_fake_engine->video().send_codecs();
1896   caller_video_codecs.push_back(cricket::VideoCodec(
1897       caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
1898   caller_video_codecs.push_back(cricket::VideoCodec(
1899       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1900   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1901       std::to_string(caller_video_codecs.back().id - 1);
1902   caller_video_codecs.push_back(cricket::VideoCodec(
1903       caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
1904   caller_video_codecs.push_back(cricket::VideoCodec(
1905       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1906   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1907       std::to_string(caller_video_codecs.back().id - 1);
1908   caller_fake_engine->SetVideoCodecs(caller_video_codecs);
1909 
1910   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
1911 
1912   auto sender_video_codecs =
1913       caller->pc_factory()
1914           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1915           .codecs;
1916 
1917   auto video_transceiver = caller->pc()->GetTransceivers().front();
1918 
1919   // Check that RTX codec is properly added
1920   auto video_codecs_vpx_rtx = sender_video_codecs;
1921   auto it =
1922       std::remove_if(video_codecs_vpx_rtx.begin(), video_codecs_vpx_rtx.end(),
1923                      [](const webrtc::RtpCodecCapability& codec) {
1924                        return codec.name != cricket::kRtxCodecName &&
1925                               codec.name != cricket::kVp8CodecName &&
1926                               codec.name != cricket::kVp9CodecName;
1927                      });
1928   video_codecs_vpx_rtx.erase(it, video_codecs_vpx_rtx.end());
1929   absl::c_reverse(video_codecs_vpx_rtx);
1930   EXPECT_EQ(video_codecs_vpx_rtx.size(), 3u);  // VP8, VP9, RTX
1931   EXPECT_TRUE(
1932       video_transceiver->SetCodecPreferences(video_codecs_vpx_rtx).ok());
1933   auto offer = caller->CreateOffer();
1934   auto codecs = offer->description()
1935                     ->contents()[0]
1936                     .media_description()
1937                     ->as_video()
1938                     ->codecs();
1939 
1940   EXPECT_TRUE(CompareCodecs(video_codecs_vpx_rtx, codecs));
1941   EXPECT_EQ(codecs.size(), 4u);
1942 }
1943 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoCodecsNegotiation)1944 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1945        SetCodecPreferencesVideoCodecsNegotiation) {
1946   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1947   auto caller_video_codecs = caller_fake_engine->video().send_codecs();
1948   caller_video_codecs.push_back(cricket::VideoCodec(
1949       caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
1950   caller_video_codecs.push_back(cricket::VideoCodec(
1951       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1952   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1953       std::to_string(caller_video_codecs.back().id - 1);
1954   caller_video_codecs.push_back(cricket::VideoCodec(
1955       caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
1956   caller_video_codecs.push_back(cricket::VideoCodec(
1957       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1958   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1959       std::to_string(caller_video_codecs.back().id - 1);
1960   caller_fake_engine->SetVideoCodecs(caller_video_codecs);
1961 
1962   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1963   callee_fake_engine->SetVideoCodecs(caller_video_codecs);
1964 
1965   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
1966   auto callee = CreatePeerConnection(std::move(callee_fake_engine));
1967 
1968   auto video_codecs = caller->pc_factory()
1969                           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1970                           .codecs;
1971 
1972   auto send_transceiver = caller->pc()->GetTransceivers().front();
1973 
1974   auto video_codecs_vpx = video_codecs;
1975   auto it = std::remove_if(video_codecs_vpx.begin(), video_codecs_vpx.end(),
1976                            [](const webrtc::RtpCodecCapability& codec) {
1977                              return codec.name != cricket::kVp8CodecName &&
1978                                     codec.name != cricket::kVp9CodecName;
1979                            });
1980   video_codecs_vpx.erase(it, video_codecs_vpx.end());
1981   EXPECT_EQ(video_codecs_vpx.size(), 2u);  // VP8, VP9
1982   EXPECT_TRUE(send_transceiver->SetCodecPreferences(video_codecs_vpx).ok());
1983 
1984   auto offer = caller->CreateOfferAndSetAsLocal();
1985   auto codecs = offer->description()
1986                     ->contents()[0]
1987                     .media_description()
1988                     ->as_video()
1989                     ->codecs();
1990 
1991   EXPECT_EQ(codecs.size(), 2u);  // VP8, VP9
1992   EXPECT_TRUE(CompareCodecs(video_codecs_vpx, codecs));
1993 
1994   callee->SetRemoteDescription(std::move(offer));
1995 
1996   auto recv_transceiver = callee->pc()->GetTransceivers().front();
1997   auto video_codecs_vp8_rtx = video_codecs;
1998   it = std::remove_if(video_codecs_vp8_rtx.begin(), video_codecs_vp8_rtx.end(),
1999                       [](const webrtc::RtpCodecCapability& codec) {
2000                         bool r = codec.name != cricket::kVp8CodecName &&
2001                                  codec.name != cricket::kRtxCodecName;
2002                         return r;
2003                       });
2004   video_codecs_vp8_rtx.erase(it, video_codecs_vp8_rtx.end());
2005   EXPECT_EQ(video_codecs_vp8_rtx.size(), 2u);  // VP8, RTX
2006   recv_transceiver->SetCodecPreferences(video_codecs_vp8_rtx);
2007 
2008   auto answer = callee->CreateAnswerAndSetAsLocal();
2009 
2010   auto recv_codecs = answer->description()
2011                          ->contents()[0]
2012                          .media_description()
2013                          ->as_video()
2014                          ->codecs();
2015   EXPECT_EQ(recv_codecs.size(), 1u);  // VP8
2016 }
2017 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoCodecsNegotiationReverseOrder)2018 TEST_F(PeerConnectionMediaTestUnifiedPlan,
2019        SetCodecPreferencesVideoCodecsNegotiationReverseOrder) {
2020   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
2021   auto caller_video_codecs = caller_fake_engine->video().send_codecs();
2022   caller_video_codecs.push_back(cricket::VideoCodec(
2023       caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
2024   caller_video_codecs.push_back(cricket::VideoCodec(
2025       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
2026   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
2027       std::to_string(caller_video_codecs.back().id - 1);
2028   caller_video_codecs.push_back(cricket::VideoCodec(
2029       caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
2030   caller_video_codecs.push_back(cricket::VideoCodec(
2031       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
2032   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
2033       std::to_string(caller_video_codecs.back().id - 1);
2034   caller_fake_engine->SetVideoCodecs(caller_video_codecs);
2035 
2036   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
2037   callee_fake_engine->SetVideoCodecs(caller_video_codecs);
2038 
2039   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
2040   auto callee = CreatePeerConnection(std::move(callee_fake_engine));
2041 
2042   auto video_codecs = caller->pc_factory()
2043                           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
2044                           .codecs;
2045 
2046   auto send_transceiver = caller->pc()->GetTransceivers().front();
2047 
2048   auto video_codecs_vpx = video_codecs;
2049   auto it = std::remove_if(video_codecs_vpx.begin(), video_codecs_vpx.end(),
2050                            [](const webrtc::RtpCodecCapability& codec) {
2051                              return codec.name != cricket::kVp8CodecName &&
2052                                     codec.name != cricket::kVp9CodecName;
2053                            });
2054   video_codecs_vpx.erase(it, video_codecs_vpx.end());
2055   EXPECT_EQ(video_codecs_vpx.size(), 2u);  // VP8, VP9
2056   EXPECT_TRUE(send_transceiver->SetCodecPreferences(video_codecs_vpx).ok());
2057 
2058   auto video_codecs_vpx_reverse = video_codecs_vpx;
2059   absl::c_reverse(video_codecs_vpx_reverse);
2060 
2061   auto offer = caller->CreateOfferAndSetAsLocal();
2062   auto codecs = offer->description()
2063                     ->contents()[0]
2064                     .media_description()
2065                     ->as_video()
2066                     ->codecs();
2067   EXPECT_EQ(codecs.size(), 2u);  // VP9, VP8
2068   EXPECT_TRUE(CompareCodecs(video_codecs_vpx, codecs));
2069 
2070   callee->SetRemoteDescription(std::move(offer));
2071 
2072   auto recv_transceiver = callee->pc()->GetTransceivers().front();
2073   recv_transceiver->SetCodecPreferences(video_codecs_vpx_reverse);
2074 
2075   auto answer = callee->CreateAnswerAndSetAsLocal();
2076 
2077   auto recv_codecs = answer->description()
2078                          ->contents()[0]
2079                          .media_description()
2080                          ->as_video()
2081                          ->codecs();
2082 
2083   EXPECT_TRUE(CompareCodecs(video_codecs_vpx_reverse, recv_codecs));
2084 }
2085 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVoiceActivityDetection)2086 TEST_F(PeerConnectionMediaTestUnifiedPlan,
2087        SetCodecPreferencesVoiceActivityDetection) {
2088   auto fake_engine = std::make_unique<FakeMediaEngine>();
2089   AddComfortNoiseCodecsToSend(fake_engine.get());
2090   auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
2091 
2092   RTCOfferAnswerOptions options;
2093   auto offer = caller->CreateOffer(options);
2094   EXPECT_TRUE(HasAnyComfortNoiseCodecs(offer->description()));
2095 
2096   auto transceiver = caller->pc()->GetTransceivers().front();
2097   auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2098       cricket::MediaType::MEDIA_TYPE_AUDIO);
2099   EXPECT_TRUE(transceiver->SetCodecPreferences(capabilities.codecs).ok());
2100 
2101   options.voice_activity_detection = false;
2102   offer = caller->CreateOffer(options);
2103   EXPECT_FALSE(HasAnyComfortNoiseCodecs(offer->description()));
2104 }
2105 
2106 // If the "default" payload types of audio/video codecs are the same, and
2107 // audio/video are bundled (as is the default), payload types should be
2108 // remapped to avoid conflict, as normally happens without using
2109 // SetCodecPreferences.
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAvoidsPayloadTypeConflictInOffer)2110 TEST_F(PeerConnectionMediaTestUnifiedPlan,
2111        SetCodecPreferencesAvoidsPayloadTypeConflictInOffer) {
2112   auto fake_engine = std::make_unique<cricket::FakeMediaEngine>();
2113 
2114   std::vector<cricket::AudioCodec> audio_codecs;
2115   audio_codecs.emplace_back(100, "foo", 0, 0, 1);
2116   audio_codecs.emplace_back(101, cricket::kRtxCodecName, 0, 0, 1);
2117   audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2118   fake_engine->SetAudioCodecs(audio_codecs);
2119 
2120   std::vector<cricket::VideoCodec> video_codecs;
2121   video_codecs.emplace_back(100, "bar");
2122   video_codecs.emplace_back(101, cricket::kRtxCodecName);
2123   video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2124   fake_engine->SetVideoCodecs(video_codecs);
2125 
2126   auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine));
2127   auto transceivers = caller->pc()->GetTransceivers();
2128   ASSERT_EQ(2u, transceivers.size());
2129 
2130   auto audio_transceiver = caller->pc()->GetTransceivers()[0];
2131   auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2132       cricket::MediaType::MEDIA_TYPE_AUDIO);
2133   EXPECT_TRUE(audio_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2134 
2135   auto video_transceiver = caller->pc()->GetTransceivers()[1];
2136   capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2137       cricket::MediaType::MEDIA_TYPE_VIDEO);
2138   EXPECT_TRUE(video_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2139 
2140   RTCOfferAnswerOptions options;
2141   auto offer = caller->CreateOffer(options);
2142   EXPECT_FALSE(HasPayloadTypeConflict(offer->description()));
2143   // Sanity check that we got the primary codec and RTX.
2144   EXPECT_EQ(2u, cricket::GetFirstAudioContentDescription(offer->description())
2145                     ->codecs()
2146                     .size());
2147   EXPECT_EQ(2u, cricket::GetFirstVideoContentDescription(offer->description())
2148                     ->codecs()
2149                     .size());
2150 }
2151 
2152 // Same as above, but preferences set for the answer.
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAvoidsPayloadTypeConflictInAnswer)2153 TEST_F(PeerConnectionMediaTestUnifiedPlan,
2154        SetCodecPreferencesAvoidsPayloadTypeConflictInAnswer) {
2155   auto fake_engine = std::make_unique<cricket::FakeMediaEngine>();
2156 
2157   std::vector<cricket::AudioCodec> audio_codecs;
2158   audio_codecs.emplace_back(100, "foo", 0, 0, 1);
2159   audio_codecs.emplace_back(101, cricket::kRtxCodecName, 0, 0, 1);
2160   audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2161   fake_engine->SetAudioCodecs(audio_codecs);
2162 
2163   std::vector<cricket::VideoCodec> video_codecs;
2164   video_codecs.emplace_back(100, "bar");
2165   video_codecs.emplace_back(101, cricket::kRtxCodecName);
2166   video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2167   fake_engine->SetVideoCodecs(video_codecs);
2168 
2169   auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine));
2170 
2171   RTCOfferAnswerOptions options;
2172   caller->SetRemoteDescription(caller->CreateOffer(options));
2173 
2174   auto transceivers = caller->pc()->GetTransceivers();
2175   ASSERT_EQ(2u, transceivers.size());
2176 
2177   auto audio_transceiver = caller->pc()->GetTransceivers()[0];
2178   auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2179       cricket::MediaType::MEDIA_TYPE_AUDIO);
2180   EXPECT_TRUE(audio_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2181 
2182   auto video_transceiver = caller->pc()->GetTransceivers()[1];
2183   capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2184       cricket::MediaType::MEDIA_TYPE_VIDEO);
2185   EXPECT_TRUE(video_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2186 
2187   auto answer = caller->CreateAnswer(options);
2188 
2189   EXPECT_FALSE(HasPayloadTypeConflict(answer->description()));
2190   // Sanity check that we got the primary codec and RTX.
2191   EXPECT_EQ(2u, cricket::GetFirstAudioContentDescription(answer->description())
2192                     ->codecs()
2193                     .size());
2194   EXPECT_EQ(2u, cricket::GetFirstVideoContentDescription(answer->description())
2195                     ->codecs()
2196                     .size());
2197 }
2198 
2199 // Same as above, but preferences set for a subsequent offer.
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAvoidsPayloadTypeConflictInSubsequentOffer)2200 TEST_F(PeerConnectionMediaTestUnifiedPlan,
2201        SetCodecPreferencesAvoidsPayloadTypeConflictInSubsequentOffer) {
2202   auto fake_engine = std::make_unique<cricket::FakeMediaEngine>();
2203 
2204   std::vector<cricket::AudioCodec> audio_codecs;
2205   audio_codecs.emplace_back(100, "foo", 0, 0, 1);
2206   audio_codecs.emplace_back(101, cricket::kRtxCodecName, 0, 0, 1);
2207   audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2208   fake_engine->SetAudioCodecs(audio_codecs);
2209 
2210   std::vector<cricket::VideoCodec> video_codecs;
2211   video_codecs.emplace_back(100, "bar");
2212   video_codecs.emplace_back(101, cricket::kRtxCodecName);
2213   video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "100";
2214   fake_engine->SetVideoCodecs(video_codecs);
2215 
2216   auto caller = CreatePeerConnectionWithAudioVideo(std::move(fake_engine));
2217 
2218   RTCOfferAnswerOptions options;
2219   caller->SetRemoteDescription(caller->CreateOffer(options));
2220   caller->SetLocalDescription(caller->CreateAnswer(options));
2221 
2222   auto transceivers = caller->pc()->GetTransceivers();
2223   ASSERT_EQ(2u, transceivers.size());
2224 
2225   auto audio_transceiver = caller->pc()->GetTransceivers()[0];
2226   auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2227       cricket::MediaType::MEDIA_TYPE_AUDIO);
2228   EXPECT_TRUE(audio_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2229 
2230   auto video_transceiver = caller->pc()->GetTransceivers()[1];
2231   capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
2232       cricket::MediaType::MEDIA_TYPE_VIDEO);
2233   EXPECT_TRUE(video_transceiver->SetCodecPreferences(capabilities.codecs).ok());
2234 
2235   auto reoffer = caller->CreateOffer(options);
2236 
2237   EXPECT_FALSE(HasPayloadTypeConflict(reoffer->description()));
2238   // Sanity check that we got the primary codec and RTX.
2239   EXPECT_EQ(2u, cricket::GetFirstAudioContentDescription(reoffer->description())
2240                     ->codecs()
2241                     .size());
2242   EXPECT_EQ(2u, cricket::GetFirstVideoContentDescription(reoffer->description())
2243                     ->codecs()
2244                     .size());
2245 }
2246 
2247 INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaTest,
2248                          PeerConnectionMediaTest,
2249                          Values(SdpSemantics::kPlanB_DEPRECATED,
2250                                 SdpSemantics::kUnifiedPlan));
2251 
2252 }  // namespace webrtc
2253