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 <memory>
16 #include <tuple>
17 
18 #include "absl/algorithm/container.h"
19 #include "absl/types/optional.h"
20 #include "api/call/call_factory_interface.h"
21 #include "api/rtc_event_log/rtc_event_log_factory.h"
22 #include "api/task_queue/default_task_queue_factory.h"
23 #include "media/base/fake_media_engine.h"
24 #include "p2p/base/fake_port_allocator.h"
25 #include "pc/media_session.h"
26 #include "pc/peer_connection_wrapper.h"
27 #include "pc/rtp_media_utils.h"
28 #include "pc/sdp_utils.h"
29 #ifdef WEBRTC_ANDROID
30 #include "pc/test/android_test_initializer.h"
31 #endif
32 #include "pc/test/fake_rtc_certificate_generator.h"
33 #include "rtc_base/gunit.h"
34 #include "rtc_base/virtual_socket_server.h"
35 #include "test/gmock.h"
36 
37 namespace webrtc {
38 
39 using cricket::FakeMediaEngine;
40 using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
41 using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
42 using ::testing::Bool;
43 using ::testing::Combine;
44 using ::testing::ElementsAre;
45 using ::testing::Values;
46 
47 class PeerConnectionWrapperForMediaTest : public PeerConnectionWrapper {
48  public:
49   using PeerConnectionWrapper::PeerConnectionWrapper;
50 
media_engine()51   FakeMediaEngine* media_engine() { return media_engine_; }
set_media_engine(FakeMediaEngine * media_engine)52   void set_media_engine(FakeMediaEngine* media_engine) {
53     media_engine_ = media_engine;
54   }
55 
56  private:
57   FakeMediaEngine* media_engine_;
58 };
59 
60 class PeerConnectionMediaBaseTest : public ::testing::Test {
61  protected:
62   typedef std::unique_ptr<PeerConnectionWrapperForMediaTest> WrapperPtr;
63 
PeerConnectionMediaBaseTest(SdpSemantics sdp_semantics)64   explicit PeerConnectionMediaBaseTest(SdpSemantics sdp_semantics)
65       : vss_(new rtc::VirtualSocketServer()),
66         main_(vss_.get()),
67         sdp_semantics_(sdp_semantics) {
68 #ifdef WEBRTC_ANDROID
69     InitializeAndroidObjects();
70 #endif
71   }
72 
CreatePeerConnection()73   WrapperPtr CreatePeerConnection() {
74     return CreatePeerConnection(RTCConfiguration());
75   }
76 
CreatePeerConnection(const RTCConfiguration & config)77   WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
78     return CreatePeerConnection(config, std::make_unique<FakeMediaEngine>());
79   }
80 
CreatePeerConnection(std::unique_ptr<FakeMediaEngine> media_engine)81   WrapperPtr CreatePeerConnection(
82       std::unique_ptr<FakeMediaEngine> media_engine) {
83     return CreatePeerConnection(RTCConfiguration(), std::move(media_engine));
84   }
85 
86   // Creates PeerConnectionFactory and PeerConnection for given configuration.
CreatePeerConnection(const RTCConfiguration & config,std::unique_ptr<FakeMediaEngine> media_engine)87   WrapperPtr CreatePeerConnection(
88       const RTCConfiguration& config,
89       std::unique_ptr<FakeMediaEngine> media_engine) {
90     auto* media_engine_ptr = media_engine.get();
91 
92     PeerConnectionFactoryDependencies factory_dependencies;
93 
94     factory_dependencies.network_thread = rtc::Thread::Current();
95     factory_dependencies.worker_thread = rtc::Thread::Current();
96     factory_dependencies.signaling_thread = rtc::Thread::Current();
97     factory_dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
98     factory_dependencies.media_engine = std::move(media_engine);
99     factory_dependencies.call_factory = CreateCallFactory();
100     factory_dependencies.event_log_factory =
101         std::make_unique<RtcEventLogFactory>(
102             factory_dependencies.task_queue_factory.get());
103 
104     auto pc_factory =
105         CreateModularPeerConnectionFactory(std::move(factory_dependencies));
106 
107     auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
108         rtc::Thread::Current(), nullptr);
109     auto observer = std::make_unique<MockPeerConnectionObserver>();
110     auto modified_config = config;
111     modified_config.sdp_semantics = sdp_semantics_;
112     auto pc = pc_factory->CreatePeerConnection(modified_config,
113                                                std::move(fake_port_allocator),
114                                                nullptr, observer.get());
115     if (!pc) {
116       return nullptr;
117     }
118 
119     observer->SetPeerConnectionInterface(pc.get());
120     auto wrapper = std::make_unique<PeerConnectionWrapperForMediaTest>(
121         pc_factory, pc, std::move(observer));
122     wrapper->set_media_engine(media_engine_ptr);
123     return wrapper;
124   }
125 
126   // Accepts the same arguments as CreatePeerConnection and adds default audio
127   // track (but no video).
128   template <typename... Args>
CreatePeerConnectionWithAudio(Args &&...args)129   WrapperPtr CreatePeerConnectionWithAudio(Args&&... args) {
130     auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
131     if (!wrapper) {
132       return nullptr;
133     }
134     wrapper->AddAudioTrack("a");
135     return wrapper;
136   }
137 
138   // Accepts the same arguments as CreatePeerConnection and adds default video
139   // track (but no audio).
140   template <typename... Args>
CreatePeerConnectionWithVideo(Args &&...args)141   WrapperPtr CreatePeerConnectionWithVideo(Args&&... args) {
142     auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
143     if (!wrapper) {
144       return nullptr;
145     }
146     wrapper->AddVideoTrack("v");
147     return wrapper;
148   }
149 
150   // Accepts the same arguments as CreatePeerConnection and adds default audio
151   // and video tracks.
152   template <typename... Args>
CreatePeerConnectionWithAudioVideo(Args &&...args)153   WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
154     auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
155     if (!wrapper) {
156       return nullptr;
157     }
158     wrapper->AddAudioTrack("a");
159     wrapper->AddVideoTrack("v");
160     return wrapper;
161   }
162 
GetMediaContentDirection(const SessionDescriptionInterface * sdesc,cricket::MediaType media_type)163   RtpTransceiverDirection GetMediaContentDirection(
164       const SessionDescriptionInterface* sdesc,
165       cricket::MediaType media_type) {
166     auto* content =
167         cricket::GetFirstMediaContent(sdesc->description(), media_type);
168     RTC_DCHECK(content);
169     return content->media_description()->direction();
170   }
171 
IsUnifiedPlan() const172   bool IsUnifiedPlan() const {
173     return sdp_semantics_ == SdpSemantics::kUnifiedPlan;
174   }
175 
176   std::unique_ptr<rtc::VirtualSocketServer> vss_;
177   rtc::AutoSocketServerThread main_;
178   const SdpSemantics sdp_semantics_;
179 };
180 
181 class PeerConnectionMediaTest
182     : public PeerConnectionMediaBaseTest,
183       public ::testing::WithParamInterface<SdpSemantics> {
184  protected:
PeerConnectionMediaTest()185   PeerConnectionMediaTest() : PeerConnectionMediaBaseTest(GetParam()) {}
186 };
187 
188 class PeerConnectionMediaTestUnifiedPlan : public PeerConnectionMediaBaseTest {
189  protected:
PeerConnectionMediaTestUnifiedPlan()190   PeerConnectionMediaTestUnifiedPlan()
191       : PeerConnectionMediaBaseTest(SdpSemantics::kUnifiedPlan) {}
192 };
193 
194 class PeerConnectionMediaTestPlanB : public PeerConnectionMediaBaseTest {
195  protected:
PeerConnectionMediaTestPlanB()196   PeerConnectionMediaTestPlanB()
197       : PeerConnectionMediaBaseTest(SdpSemantics::kPlanB) {}
198 };
199 
TEST_P(PeerConnectionMediaTest,FailToSetRemoteDescriptionIfCreateMediaChannelFails)200 TEST_P(PeerConnectionMediaTest,
201        FailToSetRemoteDescriptionIfCreateMediaChannelFails) {
202   auto caller = CreatePeerConnectionWithAudioVideo();
203   auto callee = CreatePeerConnectionWithAudioVideo();
204   callee->media_engine()->set_fail_create_channel(true);
205 
206   std::string error;
207   ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
208   EXPECT_PRED_FORMAT2(AssertStartsWith, error,
209                       "Failed to set remote offer sdp: Failed to create");
210 }
211 
TEST_P(PeerConnectionMediaTest,FailToSetLocalDescriptionIfCreateMediaChannelFails)212 TEST_P(PeerConnectionMediaTest,
213        FailToSetLocalDescriptionIfCreateMediaChannelFails) {
214   auto caller = CreatePeerConnectionWithAudioVideo();
215   caller->media_engine()->set_fail_create_channel(true);
216 
217   std::string error;
218   ASSERT_FALSE(caller->SetLocalDescription(caller->CreateOffer(), &error));
219   EXPECT_PRED_FORMAT2(AssertStartsWith, error,
220                       "Failed to set local offer sdp: Failed to create");
221 }
222 
GetIds(const std::vector<cricket::StreamParams> & streams)223 std::vector<std::string> GetIds(
224     const std::vector<cricket::StreamParams>& streams) {
225   std::vector<std::string> ids;
226   ids.reserve(streams.size());
227   for (const auto& stream : streams) {
228     ids.push_back(stream.id);
229   }
230   return ids;
231 }
232 
233 // Test that exchanging an offer and answer with each side having an audio and
234 // video stream creates the appropriate send/recv streams in the underlying
235 // media engine on both sides.
TEST_P(PeerConnectionMediaTest,AudioVideoOfferAnswerCreateSendRecvStreams)236 TEST_P(PeerConnectionMediaTest, AudioVideoOfferAnswerCreateSendRecvStreams) {
237   const std::string kCallerAudioId = "caller_a";
238   const std::string kCallerVideoId = "caller_v";
239   const std::string kCalleeAudioId = "callee_a";
240   const std::string kCalleeVideoId = "callee_v";
241 
242   auto caller = CreatePeerConnection();
243   caller->AddAudioTrack(kCallerAudioId);
244   caller->AddVideoTrack(kCallerVideoId);
245 
246   auto callee = CreatePeerConnection();
247   callee->AddAudioTrack(kCalleeAudioId);
248   callee->AddVideoTrack(kCalleeVideoId);
249 
250   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
251   ASSERT_TRUE(
252       caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
253 
254   auto* caller_voice = caller->media_engine()->GetVoiceChannel(0);
255   EXPECT_THAT(GetIds(caller_voice->recv_streams()),
256               ElementsAre(kCalleeAudioId));
257   EXPECT_THAT(GetIds(caller_voice->send_streams()),
258               ElementsAre(kCallerAudioId));
259 
260   auto* caller_video = caller->media_engine()->GetVideoChannel(0);
261   EXPECT_THAT(GetIds(caller_video->recv_streams()),
262               ElementsAre(kCalleeVideoId));
263   EXPECT_THAT(GetIds(caller_video->send_streams()),
264               ElementsAre(kCallerVideoId));
265 
266   auto* callee_voice = callee->media_engine()->GetVoiceChannel(0);
267   EXPECT_THAT(GetIds(callee_voice->recv_streams()),
268               ElementsAre(kCallerAudioId));
269   EXPECT_THAT(GetIds(callee_voice->send_streams()),
270               ElementsAre(kCalleeAudioId));
271 
272   auto* callee_video = callee->media_engine()->GetVideoChannel(0);
273   EXPECT_THAT(GetIds(callee_video->recv_streams()),
274               ElementsAre(kCallerVideoId));
275   EXPECT_THAT(GetIds(callee_video->send_streams()),
276               ElementsAre(kCalleeVideoId));
277 }
278 
279 // Test that stopping the caller transceivers causes the media channels on the
280 // callee to be destroyed after calling SetRemoteDescription on the generated
281 // offer.
282 // See next test for equivalent behavior with Plan B semantics.
TEST_F(PeerConnectionMediaTestUnifiedPlan,StoppedRemoteTransceiversRemovesMediaChannels)283 TEST_F(PeerConnectionMediaTestUnifiedPlan,
284        StoppedRemoteTransceiversRemovesMediaChannels) {
285   auto caller = CreatePeerConnectionWithAudioVideo();
286   auto callee = CreatePeerConnection();
287 
288   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
289 
290   // Stop both audio and video transceivers on the caller.
291   auto transceivers = caller->pc()->GetTransceivers();
292   ASSERT_EQ(2u, transceivers.size());
293   transceivers[0]->StopInternal();
294   transceivers[1]->StopInternal();
295 
296   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
297 
298   ASSERT_FALSE(callee->media_engine()->GetVoiceChannel(0));
299   ASSERT_FALSE(callee->media_engine()->GetVideoChannel(0));
300 }
301 
302 // Test that removing streams from a subsequent offer causes the receive streams
303 // on the callee to be removed.
304 // See previous test for equivalent behavior with Unified Plan semantics.
TEST_F(PeerConnectionMediaTestPlanB,EmptyRemoteOfferRemovesRecvStreams)305 TEST_F(PeerConnectionMediaTestPlanB, EmptyRemoteOfferRemovesRecvStreams) {
306   auto caller = CreatePeerConnection();
307   auto caller_audio_track = caller->AddAudioTrack("a");
308   auto caller_video_track = caller->AddVideoTrack("v");
309   auto callee = CreatePeerConnectionWithAudioVideo();
310 
311   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
312 
313   // Remove both tracks from caller.
314   caller->pc()->RemoveTrack(caller_audio_track);
315   caller->pc()->RemoveTrack(caller_video_track);
316 
317   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
318 
319   auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
320   auto callee_video = callee->media_engine()->GetVideoChannel(0);
321   EXPECT_EQ(1u, callee_voice->send_streams().size());
322   EXPECT_EQ(0u, callee_voice->recv_streams().size());
323   EXPECT_EQ(1u, callee_video->send_streams().size());
324   EXPECT_EQ(0u, callee_video->recv_streams().size());
325 }
326 
327 // Test enabling of simulcast with Plan B semantics.
328 // This test creating an offer.
TEST_F(PeerConnectionMediaTestPlanB,SimulcastOffer)329 TEST_F(PeerConnectionMediaTestPlanB, SimulcastOffer) {
330   auto caller = CreatePeerConnection();
331   auto caller_video_track = caller->AddVideoTrack("v");
332   RTCOfferAnswerOptions options;
333   options.num_simulcast_layers = 3;
334   auto offer = caller->CreateOffer(options);
335   auto* description = cricket::GetFirstMediaContent(offer->description(),
336                                                     cricket::MEDIA_TYPE_VIDEO)
337                           ->media_description();
338   ASSERT_EQ(1u, description->streams().size());
339   ASSERT_TRUE(description->streams()[0].get_ssrc_group("SIM"));
340   EXPECT_EQ(3u, description->streams()[0].get_ssrc_group("SIM")->ssrcs.size());
341 
342   // Check that it actually creates simulcast aswell.
343   caller->SetLocalDescription(std::move(offer));
344   auto senders = caller->pc()->GetSenders();
345   ASSERT_EQ(1u, senders.size());
346   EXPECT_EQ(cricket::MediaType::MEDIA_TYPE_VIDEO, senders[0]->media_type());
347   EXPECT_EQ(3u, senders[0]->GetParameters().encodings.size());
348 }
349 
350 // Test enabling of simulcast with Plan B semantics.
351 // This test creating an answer.
TEST_F(PeerConnectionMediaTestPlanB,SimulcastAnswer)352 TEST_F(PeerConnectionMediaTestPlanB, SimulcastAnswer) {
353   auto caller = CreatePeerConnection();
354   caller->AddVideoTrack("v0");
355   auto offer = caller->CreateOffer();
356   auto callee = CreatePeerConnection();
357   auto callee_video_track = callee->AddVideoTrack("v1");
358   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
359   RTCOfferAnswerOptions options;
360   options.num_simulcast_layers = 3;
361   auto answer = callee->CreateAnswer(options);
362   auto* description = cricket::GetFirstMediaContent(answer->description(),
363                                                     cricket::MEDIA_TYPE_VIDEO)
364                           ->media_description();
365   ASSERT_EQ(1u, description->streams().size());
366   ASSERT_TRUE(description->streams()[0].get_ssrc_group("SIM"));
367   EXPECT_EQ(3u, description->streams()[0].get_ssrc_group("SIM")->ssrcs.size());
368 
369   // Check that it actually creates simulcast aswell.
370   callee->SetLocalDescription(std::move(answer));
371   auto senders = callee->pc()->GetSenders();
372   ASSERT_EQ(1u, senders.size());
373   EXPECT_EQ(cricket::MediaType::MEDIA_TYPE_VIDEO, senders[0]->media_type());
374   EXPECT_EQ(3u, senders[0]->GetParameters().encodings.size());
375 }
376 
377 // Test that stopping the callee transceivers causes the media channels to be
378 // destroyed on the callee after calling SetLocalDescription on the local
379 // answer.
380 // See next test for equivalent behavior with Plan B semantics.
TEST_F(PeerConnectionMediaTestUnifiedPlan,StoppedLocalTransceiversRemovesMediaChannels)381 TEST_F(PeerConnectionMediaTestUnifiedPlan,
382        StoppedLocalTransceiversRemovesMediaChannels) {
383   auto caller = CreatePeerConnectionWithAudioVideo();
384   auto callee = CreatePeerConnectionWithAudioVideo();
385 
386   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
387 
388   // Stop both audio and video transceivers on the callee.
389   auto transceivers = callee->pc()->GetTransceivers();
390   ASSERT_EQ(2u, transceivers.size());
391   transceivers[0]->StopInternal();
392   transceivers[1]->StopInternal();
393 
394   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
395 
396   EXPECT_FALSE(callee->media_engine()->GetVoiceChannel(0));
397   EXPECT_FALSE(callee->media_engine()->GetVideoChannel(0));
398 }
399 
400 // Test that removing streams from a subsequent answer causes the send streams
401 // on the callee to be removed when applied locally.
402 // See previous test for equivalent behavior with Unified Plan semantics.
TEST_F(PeerConnectionMediaTestPlanB,EmptyLocalAnswerRemovesSendStreams)403 TEST_F(PeerConnectionMediaTestPlanB, EmptyLocalAnswerRemovesSendStreams) {
404   auto caller = CreatePeerConnectionWithAudioVideo();
405   auto callee = CreatePeerConnection();
406   auto callee_audio_track = callee->AddAudioTrack("a");
407   auto callee_video_track = callee->AddVideoTrack("v");
408 
409   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
410 
411   // Remove both tracks from callee.
412   callee->pc()->RemoveTrack(callee_audio_track);
413   callee->pc()->RemoveTrack(callee_video_track);
414 
415   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
416 
417   auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
418   auto callee_video = callee->media_engine()->GetVideoChannel(0);
419   EXPECT_EQ(0u, callee_voice->send_streams().size());
420   EXPECT_EQ(1u, callee_voice->recv_streams().size());
421   EXPECT_EQ(0u, callee_video->send_streams().size());
422   EXPECT_EQ(1u, callee_video->recv_streams().size());
423 }
424 
425 // Test that a new stream in a subsequent offer causes a new receive stream to
426 // be created on the callee.
TEST_P(PeerConnectionMediaTest,NewStreamInRemoteOfferAddsRecvStreams)427 TEST_P(PeerConnectionMediaTest, NewStreamInRemoteOfferAddsRecvStreams) {
428   auto caller = CreatePeerConnectionWithAudioVideo();
429   auto callee = CreatePeerConnection();
430 
431   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
432 
433   // Add second set of tracks to the caller.
434   caller->AddAudioTrack("a2");
435   caller->AddVideoTrack("v2");
436 
437   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
438 
439   auto a1 = callee->media_engine()->GetVoiceChannel(0);
440   auto a2 = callee->media_engine()->GetVoiceChannel(1);
441   auto v1 = callee->media_engine()->GetVideoChannel(0);
442   auto v2 = callee->media_engine()->GetVideoChannel(1);
443   if (IsUnifiedPlan()) {
444     ASSERT_TRUE(a1);
445     EXPECT_EQ(1u, a1->recv_streams().size());
446     ASSERT_TRUE(a2);
447     EXPECT_EQ(1u, a2->recv_streams().size());
448     ASSERT_TRUE(v1);
449     EXPECT_EQ(1u, v1->recv_streams().size());
450     ASSERT_TRUE(v2);
451     EXPECT_EQ(1u, v2->recv_streams().size());
452   } else {
453     ASSERT_TRUE(a1);
454     EXPECT_EQ(2u, a1->recv_streams().size());
455     ASSERT_FALSE(a2);
456     ASSERT_TRUE(v1);
457     EXPECT_EQ(2u, v1->recv_streams().size());
458     ASSERT_FALSE(v2);
459   }
460 }
461 
462 // Test that a new stream in a subsequent answer causes a new send stream to be
463 // created on the callee when added locally.
TEST_P(PeerConnectionMediaTest,NewStreamInLocalAnswerAddsSendStreams)464 TEST_P(PeerConnectionMediaTest, NewStreamInLocalAnswerAddsSendStreams) {
465   auto caller = CreatePeerConnection();
466   auto callee = CreatePeerConnectionWithAudioVideo();
467 
468   RTCOfferAnswerOptions offer_options;
469   offer_options.offer_to_receive_audio =
470       RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
471   offer_options.offer_to_receive_video =
472       RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
473   RTCOfferAnswerOptions answer_options;
474 
475   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get(), offer_options,
476                                               answer_options));
477 
478   // Add second set of tracks to the callee.
479   callee->AddAudioTrack("a2");
480   callee->AddVideoTrack("v2");
481 
482   ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get(), offer_options,
483                                               answer_options));
484 
485   auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
486   ASSERT_TRUE(callee_voice);
487   auto callee_video = callee->media_engine()->GetVideoChannel(0);
488   ASSERT_TRUE(callee_video);
489 
490   if (IsUnifiedPlan()) {
491     EXPECT_EQ(1u, callee_voice->send_streams().size());
492     EXPECT_EQ(1u, callee_video->send_streams().size());
493   } else {
494     EXPECT_EQ(2u, callee_voice->send_streams().size());
495     EXPECT_EQ(2u, callee_video->send_streams().size());
496   }
497 }
498 
499 // A PeerConnection with no local streams and no explicit answer constraints
500 // should not reject any offered media sections.
TEST_P(PeerConnectionMediaTest,CreateAnswerWithNoStreamsAndDefaultOptionsDoesNotReject)501 TEST_P(PeerConnectionMediaTest,
502        CreateAnswerWithNoStreamsAndDefaultOptionsDoesNotReject) {
503   auto caller = CreatePeerConnectionWithAudioVideo();
504   auto callee = CreatePeerConnection();
505   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
506   auto answer = callee->CreateAnswer();
507 
508   const auto* audio_content =
509       cricket::GetFirstAudioContent(answer->description());
510   ASSERT_TRUE(audio_content);
511   EXPECT_FALSE(audio_content->rejected);
512 
513   const auto* video_content =
514       cricket::GetFirstVideoContent(answer->description());
515   ASSERT_TRUE(video_content);
516   EXPECT_FALSE(video_content->rejected);
517 }
518 
519 // Test that raw packetization is not set in the offer by default.
TEST_P(PeerConnectionMediaTest,RawPacketizationNotSetInOffer)520 TEST_P(PeerConnectionMediaTest, RawPacketizationNotSetInOffer) {
521   std::vector<cricket::VideoCodec> fake_codecs;
522   fake_codecs.push_back(cricket::VideoCodec(111, cricket::kVp8CodecName));
523   fake_codecs.push_back(cricket::VideoCodec(112, cricket::kRtxCodecName));
524   fake_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "111";
525   fake_codecs.push_back(cricket::VideoCodec(113, cricket::kVp9CodecName));
526   fake_codecs.push_back(cricket::VideoCodec(114, cricket::kH264CodecName));
527   fake_codecs.push_back(cricket::VideoCodec(115, "HEVC"));
528   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
529   caller_fake_engine->SetVideoCodecs(fake_codecs);
530 
531   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
532   auto offer = caller->CreateOfferAndSetAsLocal();
533   auto* offer_description =
534       cricket::GetFirstVideoContentDescription(offer->description());
535   for (const auto& codec : offer_description->codecs()) {
536     EXPECT_EQ(codec.packetization, absl::nullopt);
537   }
538 }
539 
540 // Test that raw packetization is set in the offer and answer for all
541 // video payload when raw_packetization_for_video is true.
TEST_P(PeerConnectionMediaTest,RawPacketizationSetInOfferAndAnswer)542 TEST_P(PeerConnectionMediaTest, RawPacketizationSetInOfferAndAnswer) {
543   std::vector<cricket::VideoCodec> fake_codecs;
544   fake_codecs.push_back(cricket::VideoCodec(111, cricket::kVp8CodecName));
545   fake_codecs.push_back(cricket::VideoCodec(112, cricket::kRtxCodecName));
546   fake_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "111";
547   fake_codecs.push_back(cricket::VideoCodec(113, cricket::kVp9CodecName));
548   fake_codecs.push_back(cricket::VideoCodec(114, cricket::kH264CodecName));
549   fake_codecs.push_back(cricket::VideoCodec(115, "HEVC"));
550   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
551   caller_fake_engine->SetVideoCodecs(fake_codecs);
552   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
553   callee_fake_engine->SetVideoCodecs(fake_codecs);
554 
555   RTCOfferAnswerOptions options;
556   options.raw_packetization_for_video = true;
557 
558   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
559   auto offer = caller->CreateOfferAndSetAsLocal(options);
560   auto* offer_description =
561       cricket::GetFirstVideoContentDescription(offer->description());
562   for (const auto& codec : offer_description->codecs()) {
563     if (codec.GetCodecType() == cricket::VideoCodec::CODEC_VIDEO) {
564       EXPECT_EQ(codec.packetization, cricket::kPacketizationParamRaw);
565     }
566   }
567 
568   auto callee = CreatePeerConnectionWithVideo(std::move(callee_fake_engine));
569   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
570   auto answer = callee->CreateAnswerAndSetAsLocal(options);
571   auto* answer_description =
572       cricket::GetFirstVideoContentDescription(answer->description());
573   for (const auto& codec : answer_description->codecs()) {
574     if (codec.GetCodecType() == cricket::VideoCodec::CODEC_VIDEO) {
575       EXPECT_EQ(codec.packetization, cricket::kPacketizationParamRaw);
576     }
577   }
578 
579   ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
580 }
581 
582 // Test that raw packetization is not set in the answer when
583 // raw_packetization_for_video is true if it was not set in the offer.
TEST_P(PeerConnectionMediaTest,RawPacketizationNotSetInAnswerWhenNotSetInOffer)584 TEST_P(PeerConnectionMediaTest,
585        RawPacketizationNotSetInAnswerWhenNotSetInOffer) {
586   std::vector<cricket::VideoCodec> fake_codecs;
587   fake_codecs.push_back(cricket::VideoCodec(111, cricket::kVp8CodecName));
588   fake_codecs.push_back(cricket::VideoCodec(112, cricket::kRtxCodecName));
589   fake_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] = "111";
590   fake_codecs.push_back(cricket::VideoCodec(113, cricket::kVp9CodecName));
591   fake_codecs.push_back(cricket::VideoCodec(114, cricket::kH264CodecName));
592   fake_codecs.push_back(cricket::VideoCodec(115, "HEVC"));
593   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
594   caller_fake_engine->SetVideoCodecs(fake_codecs);
595   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
596   callee_fake_engine->SetVideoCodecs(fake_codecs);
597 
598   RTCOfferAnswerOptions caller_options;
599   caller_options.raw_packetization_for_video = false;
600   RTCOfferAnswerOptions callee_options;
601   callee_options.raw_packetization_for_video = true;
602 
603   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
604   auto offer = caller->CreateOfferAndSetAsLocal(caller_options);
605 
606   auto callee = CreatePeerConnectionWithVideo(std::move(callee_fake_engine));
607   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
608   auto answer = callee->CreateAnswerAndSetAsLocal(callee_options);
609 
610   auto* answer_description =
611       cricket::GetFirstVideoContentDescription(answer->description());
612   for (const auto& codec : answer_description->codecs()) {
613     EXPECT_EQ(codec.packetization, absl::nullopt);
614   }
615 
616   ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
617 }
618 
619 class PeerConnectionMediaOfferDirectionTest
620     : public PeerConnectionMediaBaseTest,
621       public ::testing::WithParamInterface<
622           std::tuple<SdpSemantics,
623                      std::tuple<bool, int, RtpTransceiverDirection>>> {
624  protected:
PeerConnectionMediaOfferDirectionTest()625   PeerConnectionMediaOfferDirectionTest()
626       : PeerConnectionMediaBaseTest(std::get<0>(GetParam())) {
627     auto param = std::get<1>(GetParam());
628     send_media_ = std::get<0>(param);
629     offer_to_receive_ = std::get<1>(param);
630     expected_direction_ = std::get<2>(param);
631   }
632 
633   bool send_media_;
634   int offer_to_receive_;
635   RtpTransceiverDirection expected_direction_;
636 };
637 
638 // Tests that the correct direction is set on the media description according
639 // to the presence of a local media track and the offer_to_receive setting.
TEST_P(PeerConnectionMediaOfferDirectionTest,VerifyDirection)640 TEST_P(PeerConnectionMediaOfferDirectionTest, VerifyDirection) {
641   auto caller = CreatePeerConnection();
642   if (send_media_) {
643     caller->AddAudioTrack("a");
644   }
645 
646   RTCOfferAnswerOptions options;
647   options.offer_to_receive_audio = offer_to_receive_;
648   auto offer = caller->CreateOffer(options);
649 
650   auto* content = cricket::GetFirstMediaContent(offer->description(),
651                                                 cricket::MEDIA_TYPE_AUDIO);
652   if (expected_direction_ == RtpTransceiverDirection::kInactive) {
653     EXPECT_FALSE(content);
654   } else {
655     EXPECT_EQ(expected_direction_, content->media_description()->direction());
656   }
657 }
658 
659 // Note that in these tests, MD_INACTIVE indicates that no media section is
660 // included in the offer, not that the media direction is inactive.
661 INSTANTIATE_TEST_SUITE_P(
662     PeerConnectionMediaTest,
663     PeerConnectionMediaOfferDirectionTest,
664     Combine(
665         Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
666         Values(std::make_tuple(false, -1, RtpTransceiverDirection::kInactive),
667                std::make_tuple(false, 0, RtpTransceiverDirection::kInactive),
668                std::make_tuple(false, 1, RtpTransceiverDirection::kRecvOnly),
669                std::make_tuple(true, -1, RtpTransceiverDirection::kSendRecv),
670                std::make_tuple(true, 0, RtpTransceiverDirection::kSendOnly),
671                std::make_tuple(true, 1, RtpTransceiverDirection::kSendRecv))));
672 
673 class PeerConnectionMediaAnswerDirectionTest
674     : public PeerConnectionMediaBaseTest,
675       public ::testing::WithParamInterface<
676           std::tuple<SdpSemantics, RtpTransceiverDirection, bool, int>> {
677  protected:
PeerConnectionMediaAnswerDirectionTest()678   PeerConnectionMediaAnswerDirectionTest()
679       : PeerConnectionMediaBaseTest(std::get<0>(GetParam())) {
680     offer_direction_ = std::get<1>(GetParam());
681     send_media_ = std::get<2>(GetParam());
682     offer_to_receive_ = std::get<3>(GetParam());
683   }
684 
685   RtpTransceiverDirection offer_direction_;
686   bool send_media_;
687   int offer_to_receive_;
688 };
689 
690 // Tests that the direction in an answer is correct according to direction sent
691 // in the offer, the presence of a local media track on the receive side and the
692 // offer_to_receive setting.
TEST_P(PeerConnectionMediaAnswerDirectionTest,VerifyDirection)693 TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyDirection) {
694   if (IsUnifiedPlan() &&
695       offer_to_receive_ != RTCOfferAnswerOptions::kUndefined) {
696     // offer_to_receive_ is not implemented when creating answers with Unified
697     // Plan semantics specified.
698     return;
699   }
700 
701   auto caller = CreatePeerConnection();
702   caller->AddAudioTrack("a");
703 
704   // Create the offer with an audio section and set its direction.
705   auto offer = caller->CreateOffer();
706   cricket::GetFirstAudioContentDescription(offer->description())
707       ->set_direction(offer_direction_);
708 
709   auto callee = CreatePeerConnection();
710   if (send_media_) {
711     callee->AddAudioTrack("a");
712   }
713   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
714 
715   // Create the answer according to the test parameters.
716   RTCOfferAnswerOptions options;
717   options.offer_to_receive_audio = offer_to_receive_;
718   auto answer = callee->CreateAnswer(options);
719 
720   // The expected direction in the answer is the intersection of each side's
721   // capability to send/recv media.
722   // For the offerer, the direction is given in the offer (offer_direction_).
723   // For the answerer, the direction has two components:
724   // 1. Send if the answerer has a local track to send.
725   // 2. Receive if the answerer has explicitly set the offer_to_receive to 1 or
726   //    if it has been left as default.
727   bool offer_send = RtpTransceiverDirectionHasSend(offer_direction_);
728   bool offer_recv = RtpTransceiverDirectionHasRecv(offer_direction_);
729 
730   // The negotiated components determine the direction set in the answer.
731   bool negotiate_send = (send_media_ && offer_recv);
732   bool negotiate_recv = ((offer_to_receive_ != 0) && offer_send);
733 
734   auto expected_direction =
735       RtpTransceiverDirectionFromSendRecv(negotiate_send, negotiate_recv);
736   EXPECT_EQ(expected_direction,
737             GetMediaContentDirection(answer.get(), cricket::MEDIA_TYPE_AUDIO));
738 }
739 
740 // Tests that the media section is rejected if and only if the callee has no
741 // local media track and has set offer_to_receive to 0, no matter which
742 // direction the caller indicated in the offer.
TEST_P(PeerConnectionMediaAnswerDirectionTest,VerifyRejected)743 TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyRejected) {
744   if (IsUnifiedPlan() &&
745       offer_to_receive_ != RTCOfferAnswerOptions::kUndefined) {
746     // offer_to_receive_ is not implemented when creating answers with Unified
747     // Plan semantics specified.
748     return;
749   }
750 
751   auto caller = CreatePeerConnection();
752   caller->AddAudioTrack("a");
753 
754   // Create the offer with an audio section and set its direction.
755   auto offer = caller->CreateOffer();
756   cricket::GetFirstAudioContentDescription(offer->description())
757       ->set_direction(offer_direction_);
758 
759   auto callee = CreatePeerConnection();
760   if (send_media_) {
761     callee->AddAudioTrack("a");
762   }
763   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
764 
765   // Create the answer according to the test parameters.
766   RTCOfferAnswerOptions options;
767   options.offer_to_receive_audio = offer_to_receive_;
768   auto answer = callee->CreateAnswer(options);
769 
770   // The media section is rejected if and only if offer_to_receive is explicitly
771   // set to 0 and there is no media to send.
772   auto* audio_content = cricket::GetFirstAudioContent(answer->description());
773   ASSERT_TRUE(audio_content);
774   EXPECT_EQ((offer_to_receive_ == 0 && !send_media_), audio_content->rejected);
775 }
776 
777 INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaTest,
778                          PeerConnectionMediaAnswerDirectionTest,
779                          Combine(Values(SdpSemantics::kPlanB,
780                                         SdpSemantics::kUnifiedPlan),
781                                  Values(RtpTransceiverDirection::kInactive,
782                                         RtpTransceiverDirection::kSendOnly,
783                                         RtpTransceiverDirection::kRecvOnly,
784                                         RtpTransceiverDirection::kSendRecv),
785                                  Bool(),
786                                  Values(-1, 0, 1)));
787 
TEST_P(PeerConnectionMediaTest,OfferHasDifferentDirectionForAudioVideo)788 TEST_P(PeerConnectionMediaTest, OfferHasDifferentDirectionForAudioVideo) {
789   auto caller = CreatePeerConnection();
790   caller->AddVideoTrack("v");
791 
792   RTCOfferAnswerOptions options;
793   options.offer_to_receive_audio = 1;
794   options.offer_to_receive_video = 0;
795   auto offer = caller->CreateOffer(options);
796 
797   EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
798             GetMediaContentDirection(offer.get(), cricket::MEDIA_TYPE_AUDIO));
799   EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
800             GetMediaContentDirection(offer.get(), cricket::MEDIA_TYPE_VIDEO));
801 }
802 
TEST_P(PeerConnectionMediaTest,AnswerHasDifferentDirectionsForAudioVideo)803 TEST_P(PeerConnectionMediaTest, AnswerHasDifferentDirectionsForAudioVideo) {
804   if (IsUnifiedPlan()) {
805     // offer_to_receive_ is not implemented when creating answers with Unified
806     // Plan semantics specified.
807     return;
808   }
809 
810   auto caller = CreatePeerConnectionWithAudioVideo();
811   auto callee = CreatePeerConnection();
812   callee->AddVideoTrack("v");
813 
814   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
815 
816   RTCOfferAnswerOptions options;
817   options.offer_to_receive_audio = 1;
818   options.offer_to_receive_video = 0;
819   auto answer = callee->CreateAnswer(options);
820 
821   EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
822             GetMediaContentDirection(answer.get(), cricket::MEDIA_TYPE_AUDIO));
823   EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
824             GetMediaContentDirection(answer.get(), cricket::MEDIA_TYPE_VIDEO));
825 }
826 
AddComfortNoiseCodecsToSend(cricket::FakeMediaEngine * media_engine)827 void AddComfortNoiseCodecsToSend(cricket::FakeMediaEngine* media_engine) {
828   const cricket::AudioCodec kComfortNoiseCodec8k(102, cricket::kCnCodecName,
829                                                  8000, 0, 1);
830   const cricket::AudioCodec kComfortNoiseCodec16k(103, cricket::kCnCodecName,
831                                                   16000, 0, 1);
832 
833   auto codecs = media_engine->voice().send_codecs();
834   codecs.push_back(kComfortNoiseCodec8k);
835   codecs.push_back(kComfortNoiseCodec16k);
836   media_engine->SetAudioCodecs(codecs);
837 }
838 
HasAnyComfortNoiseCodecs(const cricket::SessionDescription * desc)839 bool HasAnyComfortNoiseCodecs(const cricket::SessionDescription* desc) {
840   const auto* audio_desc = cricket::GetFirstAudioContentDescription(desc);
841   for (const auto& codec : audio_desc->codecs()) {
842     if (codec.name == cricket::kCnCodecName) {
843       return true;
844     }
845   }
846   return false;
847 }
848 
TEST_P(PeerConnectionMediaTest,CreateOfferWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs)849 TEST_P(PeerConnectionMediaTest,
850        CreateOfferWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs) {
851   auto caller = CreatePeerConnectionWithAudioVideo();
852   AddComfortNoiseCodecsToSend(caller->media_engine());
853 
854   RTCOfferAnswerOptions options;
855   options.voice_activity_detection = false;
856   auto offer = caller->CreateOffer(options);
857 
858   EXPECT_FALSE(HasAnyComfortNoiseCodecs(offer->description()));
859 }
860 
TEST_P(PeerConnectionMediaTest,CreateAnswerWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs)861 TEST_P(PeerConnectionMediaTest,
862        CreateAnswerWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs) {
863   auto caller = CreatePeerConnectionWithAudioVideo();
864   AddComfortNoiseCodecsToSend(caller->media_engine());
865   auto callee = CreatePeerConnectionWithAudioVideo();
866   AddComfortNoiseCodecsToSend(callee->media_engine());
867 
868   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
869 
870   RTCOfferAnswerOptions options;
871   options.voice_activity_detection = false;
872   auto answer = callee->CreateAnswer(options);
873 
874   EXPECT_FALSE(HasAnyComfortNoiseCodecs(answer->description()));
875 }
876 
877 // The following test group verifies that we reject answers with invalid media
878 // sections as per RFC 3264.
879 
880 class PeerConnectionMediaInvalidMediaTest
881     : public PeerConnectionMediaBaseTest,
882       public ::testing::WithParamInterface<std::tuple<
883           SdpSemantics,
884           std::tuple<std::string,
885                      std::function<void(cricket::SessionDescription*)>,
886                      std::string>>> {
887  protected:
PeerConnectionMediaInvalidMediaTest()888   PeerConnectionMediaInvalidMediaTest()
889       : PeerConnectionMediaBaseTest(std::get<0>(GetParam())) {
890     auto param = std::get<1>(GetParam());
891     mutator_ = std::get<1>(param);
892     expected_error_ = std::get<2>(param);
893   }
894 
895   std::function<void(cricket::SessionDescription*)> mutator_;
896   std::string expected_error_;
897 };
898 
TEST_P(PeerConnectionMediaInvalidMediaTest,FailToSetRemoteAnswer)899 TEST_P(PeerConnectionMediaInvalidMediaTest, FailToSetRemoteAnswer) {
900   auto caller = CreatePeerConnectionWithAudioVideo();
901   auto callee = CreatePeerConnectionWithAudioVideo();
902 
903   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
904 
905   auto answer = callee->CreateAnswer();
906   mutator_(answer->description());
907 
908   std::string error;
909   ASSERT_FALSE(caller->SetRemoteDescription(std::move(answer), &error));
910   EXPECT_EQ("Failed to set remote answer sdp: " + expected_error_, error);
911 }
912 
TEST_P(PeerConnectionMediaInvalidMediaTest,FailToSetLocalAnswer)913 TEST_P(PeerConnectionMediaInvalidMediaTest, FailToSetLocalAnswer) {
914   auto caller = CreatePeerConnectionWithAudioVideo();
915   auto callee = CreatePeerConnectionWithAudioVideo();
916 
917   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
918 
919   auto answer = callee->CreateAnswer();
920   mutator_(answer->description());
921 
922   std::string error;
923   ASSERT_FALSE(callee->SetLocalDescription(std::move(answer), &error));
924   EXPECT_EQ("Failed to set local answer sdp: " + expected_error_, error);
925 }
926 
RemoveVideoContent(cricket::SessionDescription * desc)927 void RemoveVideoContent(cricket::SessionDescription* desc) {
928   auto content_name = cricket::GetFirstVideoContent(desc)->name;
929   desc->RemoveContentByName(content_name);
930   desc->RemoveTransportInfoByName(content_name);
931 }
932 
RenameVideoContent(cricket::SessionDescription * desc)933 void RenameVideoContent(cricket::SessionDescription* desc) {
934   auto* video_content = cricket::GetFirstVideoContent(desc);
935   auto* transport_info = desc->GetTransportInfoByName(video_content->name);
936   video_content->name = "video_renamed";
937   transport_info->content_name = video_content->name;
938 }
939 
ReverseMediaContent(cricket::SessionDescription * desc)940 void ReverseMediaContent(cricket::SessionDescription* desc) {
941   absl::c_reverse(desc->contents());
942   absl::c_reverse(desc->transport_infos());
943 }
944 
ChangeMediaTypeAudioToVideo(cricket::SessionDescription * desc)945 void ChangeMediaTypeAudioToVideo(cricket::SessionDescription* desc) {
946   std::string audio_mid = cricket::GetFirstAudioContent(desc)->name;
947   desc->RemoveContentByName(audio_mid);
948   auto* video_content = cricket::GetFirstVideoContent(desc);
949   desc->AddContent(audio_mid, video_content->type,
950                    video_content->media_description()->Clone());
951 }
952 
953 constexpr char kMLinesOutOfOrder[] =
954     "The order of m-lines in answer doesn't match order in offer. Rejecting "
955     "answer.";
956 
957 INSTANTIATE_TEST_SUITE_P(
958     PeerConnectionMediaTest,
959     PeerConnectionMediaInvalidMediaTest,
960     Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
961             Values(std::make_tuple("remove video",
962                                    RemoveVideoContent,
963                                    kMLinesOutOfOrder),
964                    std::make_tuple("rename video",
965                                    RenameVideoContent,
966                                    kMLinesOutOfOrder),
967                    std::make_tuple("reverse media sections",
968                                    ReverseMediaContent,
969                                    kMLinesOutOfOrder),
970                    std::make_tuple("change audio type to video type",
971                                    ChangeMediaTypeAudioToVideo,
972                                    kMLinesOutOfOrder))));
973 
974 // Test that the correct media engine send/recv streams are created when doing
975 // a series of offer/answers where audio/video are both sent, then audio is
976 // rejected, then both audio/video sent again.
TEST_P(PeerConnectionMediaTest,TestAVOfferWithAudioOnlyAnswer)977 TEST_P(PeerConnectionMediaTest, TestAVOfferWithAudioOnlyAnswer) {
978   if (IsUnifiedPlan()) {
979     // offer_to_receive_ is not implemented when creating answers with Unified
980     // Plan semantics specified.
981     return;
982   }
983 
984   RTCOfferAnswerOptions options_reject_video;
985   options_reject_video.offer_to_receive_audio =
986       RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
987   options_reject_video.offer_to_receive_video = 0;
988 
989   auto caller = CreatePeerConnection();
990   caller->AddAudioTrack("a");
991   caller->AddVideoTrack("v");
992   auto callee = CreatePeerConnection();
993 
994   // Caller initially offers to send/recv audio and video.
995   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
996   // Callee accepts the audio as recv only but rejects the video.
997   ASSERT_TRUE(caller->SetRemoteDescription(
998       callee->CreateAnswerAndSetAsLocal(options_reject_video)));
999 
1000   auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
1001   ASSERT_TRUE(caller_voice);
1002   EXPECT_EQ(0u, caller_voice->recv_streams().size());
1003   EXPECT_EQ(1u, caller_voice->send_streams().size());
1004   auto caller_video = caller->media_engine()->GetVideoChannel(0);
1005   EXPECT_FALSE(caller_video);
1006 
1007   // Callee adds its own audio/video stream and offers to receive audio/video
1008   // too.
1009   callee->AddAudioTrack("a");
1010   auto callee_video_track = callee->AddVideoTrack("v");
1011   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1012   ASSERT_TRUE(
1013       caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
1014 
1015   auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
1016   ASSERT_TRUE(callee_voice);
1017   EXPECT_EQ(1u, callee_voice->recv_streams().size());
1018   EXPECT_EQ(1u, callee_voice->send_streams().size());
1019   auto callee_video = callee->media_engine()->GetVideoChannel(0);
1020   ASSERT_TRUE(callee_video);
1021   EXPECT_EQ(1u, callee_video->recv_streams().size());
1022   EXPECT_EQ(1u, callee_video->send_streams().size());
1023 
1024   // Callee removes video but keeps audio and rejects the video once again.
1025   callee->pc()->RemoveTrack(callee_video_track);
1026   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1027   ASSERT_TRUE(
1028       callee->SetLocalDescription(callee->CreateAnswer(options_reject_video)));
1029 
1030   callee_voice = callee->media_engine()->GetVoiceChannel(0);
1031   ASSERT_TRUE(callee_voice);
1032   EXPECT_EQ(1u, callee_voice->recv_streams().size());
1033   EXPECT_EQ(1u, callee_voice->send_streams().size());
1034   callee_video = callee->media_engine()->GetVideoChannel(0);
1035   EXPECT_FALSE(callee_video);
1036 }
1037 
1038 // Test that the correct media engine send/recv streams are created when doing
1039 // a series of offer/answers where audio/video are both sent, then video is
1040 // rejected, then both audio/video sent again.
TEST_P(PeerConnectionMediaTest,TestAVOfferWithVideoOnlyAnswer)1041 TEST_P(PeerConnectionMediaTest, TestAVOfferWithVideoOnlyAnswer) {
1042   if (IsUnifiedPlan()) {
1043     // offer_to_receive_ is not implemented when creating answers with Unified
1044     // Plan semantics specified.
1045     return;
1046   }
1047 
1048   // Disable the bundling here. If the media is bundled on audio
1049   // transport, then we can't reject the audio because switching the bundled
1050   // transport is not currently supported.
1051   // (https://bugs.chromium.org/p/webrtc/issues/detail?id=6704)
1052   RTCOfferAnswerOptions options_no_bundle;
1053   options_no_bundle.use_rtp_mux = false;
1054   RTCOfferAnswerOptions options_reject_audio = options_no_bundle;
1055   options_reject_audio.offer_to_receive_audio = 0;
1056   options_reject_audio.offer_to_receive_video =
1057       RTCOfferAnswerOptions::kMaxOfferToReceiveMedia;
1058 
1059   auto caller = CreatePeerConnection();
1060   caller->AddAudioTrack("a");
1061   caller->AddVideoTrack("v");
1062   auto callee = CreatePeerConnection();
1063 
1064   // Caller initially offers to send/recv audio and video.
1065   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1066   // Callee accepts the video as recv only but rejects the audio.
1067   ASSERT_TRUE(caller->SetRemoteDescription(
1068       callee->CreateAnswerAndSetAsLocal(options_reject_audio)));
1069 
1070   auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
1071   EXPECT_FALSE(caller_voice);
1072   auto caller_video = caller->media_engine()->GetVideoChannel(0);
1073   ASSERT_TRUE(caller_video);
1074   EXPECT_EQ(0u, caller_video->recv_streams().size());
1075   EXPECT_EQ(1u, caller_video->send_streams().size());
1076 
1077   // Callee adds its own audio/video stream and offers to receive audio/video
1078   // too.
1079   auto callee_audio_track = callee->AddAudioTrack("a");
1080   callee->AddVideoTrack("v");
1081   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1082   ASSERT_TRUE(caller->SetRemoteDescription(
1083       callee->CreateAnswerAndSetAsLocal(options_no_bundle)));
1084 
1085   auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
1086   ASSERT_TRUE(callee_voice);
1087   EXPECT_EQ(1u, callee_voice->recv_streams().size());
1088   EXPECT_EQ(1u, callee_voice->send_streams().size());
1089   auto callee_video = callee->media_engine()->GetVideoChannel(0);
1090   ASSERT_TRUE(callee_video);
1091   EXPECT_EQ(1u, callee_video->recv_streams().size());
1092   EXPECT_EQ(1u, callee_video->send_streams().size());
1093 
1094   // Callee removes audio but keeps video and rejects the audio once again.
1095   callee->pc()->RemoveTrack(callee_audio_track);
1096   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1097   ASSERT_TRUE(
1098       callee->SetLocalDescription(callee->CreateAnswer(options_reject_audio)));
1099 
1100   callee_voice = callee->media_engine()->GetVoiceChannel(0);
1101   EXPECT_FALSE(callee_voice);
1102   callee_video = callee->media_engine()->GetVideoChannel(0);
1103   ASSERT_TRUE(callee_video);
1104   EXPECT_EQ(1u, callee_video->recv_streams().size());
1105   EXPECT_EQ(1u, callee_video->send_streams().size());
1106 }
1107 
1108 // Tests that if the underlying video encoder fails to be initialized (signaled
1109 // by failing to set send codecs), the PeerConnection signals the error to the
1110 // client.
TEST_P(PeerConnectionMediaTest,MediaEngineErrorPropagatedToClients)1111 TEST_P(PeerConnectionMediaTest, MediaEngineErrorPropagatedToClients) {
1112   auto caller = CreatePeerConnectionWithAudioVideo();
1113   auto callee = CreatePeerConnectionWithAudioVideo();
1114 
1115   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1116 
1117   auto video_channel = caller->media_engine()->GetVideoChannel(0);
1118   video_channel->set_fail_set_send_codecs(true);
1119 
1120   std::string error;
1121   ASSERT_FALSE(caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(),
1122                                             &error));
1123   EXPECT_EQ(std::string("Failed to set remote answer sdp: Failed to set remote "
1124                         "video description "
1125                         "send parameters for m-section with mid='") +
1126                 (IsUnifiedPlan() ? "1" : "video") + "'.",
1127             error);
1128 }
1129 
1130 // Tests that if the underlying video encoder fails once then subsequent
1131 // attempts at setting the local/remote description will also fail, even if
1132 // SetSendCodecs no longer fails.
TEST_P(PeerConnectionMediaTest,FailToApplyDescriptionIfVideoEncoderHasEverFailed)1133 TEST_P(PeerConnectionMediaTest,
1134        FailToApplyDescriptionIfVideoEncoderHasEverFailed) {
1135   auto caller = CreatePeerConnectionWithAudioVideo();
1136   auto callee = CreatePeerConnectionWithAudioVideo();
1137 
1138   ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1139 
1140   auto video_channel = caller->media_engine()->GetVideoChannel(0);
1141   video_channel->set_fail_set_send_codecs(true);
1142 
1143   EXPECT_FALSE(
1144       caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
1145 
1146   video_channel->set_fail_set_send_codecs(false);
1147 
1148   EXPECT_FALSE(caller->SetRemoteDescription(callee->CreateAnswer()));
1149   EXPECT_FALSE(caller->SetLocalDescription(caller->CreateOffer()));
1150 }
1151 
RenameContent(cricket::SessionDescription * desc,cricket::MediaType media_type,const std::string & new_name)1152 void RenameContent(cricket::SessionDescription* desc,
1153                    cricket::MediaType media_type,
1154                    const std::string& new_name) {
1155   auto* content = cricket::GetFirstMediaContent(desc, media_type);
1156   RTC_DCHECK(content);
1157   std::string old_name = content->name;
1158   content->name = new_name;
1159   auto* transport = desc->GetTransportInfoByName(old_name);
1160   RTC_DCHECK(transport);
1161   transport->content_name = new_name;
1162 
1163   // Rename the content name in the BUNDLE group.
1164   cricket::ContentGroup new_bundle_group =
1165       *desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1166   new_bundle_group.RemoveContentName(old_name);
1167   new_bundle_group.AddContentName(new_name);
1168   desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1169   desc->AddGroup(new_bundle_group);
1170 }
1171 
1172 // Tests that an answer responds with the same MIDs as the offer.
TEST_P(PeerConnectionMediaTest,AnswerHasSameMidsAsOffer)1173 TEST_P(PeerConnectionMediaTest, AnswerHasSameMidsAsOffer) {
1174   const std::string kAudioMid = "notdefault1";
1175   const std::string kVideoMid = "notdefault2";
1176 
1177   auto caller = CreatePeerConnectionWithAudioVideo();
1178   auto callee = CreatePeerConnectionWithAudioVideo();
1179 
1180   auto offer = caller->CreateOffer();
1181   RenameContent(offer->description(), cricket::MEDIA_TYPE_AUDIO, kAudioMid);
1182   RenameContent(offer->description(), cricket::MEDIA_TYPE_VIDEO, kVideoMid);
1183   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1184 
1185   auto answer = callee->CreateAnswer();
1186   EXPECT_EQ(kAudioMid,
1187             cricket::GetFirstAudioContent(answer->description())->name);
1188   EXPECT_EQ(kVideoMid,
1189             cricket::GetFirstVideoContent(answer->description())->name);
1190 }
1191 
1192 // Test that if the callee creates a re-offer, the MIDs are the same as the
1193 // original offer.
TEST_P(PeerConnectionMediaTest,ReOfferHasSameMidsAsFirstOffer)1194 TEST_P(PeerConnectionMediaTest, ReOfferHasSameMidsAsFirstOffer) {
1195   const std::string kAudioMid = "notdefault1";
1196   const std::string kVideoMid = "notdefault2";
1197 
1198   auto caller = CreatePeerConnectionWithAudioVideo();
1199   auto callee = CreatePeerConnectionWithAudioVideo();
1200 
1201   auto offer = caller->CreateOffer();
1202   RenameContent(offer->description(), cricket::MEDIA_TYPE_AUDIO, kAudioMid);
1203   RenameContent(offer->description(), cricket::MEDIA_TYPE_VIDEO, kVideoMid);
1204   ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1205   ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
1206 
1207   auto reoffer = callee->CreateOffer();
1208   EXPECT_EQ(kAudioMid,
1209             cricket::GetFirstAudioContent(reoffer->description())->name);
1210   EXPECT_EQ(kVideoMid,
1211             cricket::GetFirstVideoContent(reoffer->description())->name);
1212 }
1213 
1214 // Test that SetRemoteDescription returns an error if there are two m= sections
1215 // with the same MID value.
TEST_P(PeerConnectionMediaTest,SetRemoteDescriptionFailsWithDuplicateMids)1216 TEST_P(PeerConnectionMediaTest, SetRemoteDescriptionFailsWithDuplicateMids) {
1217   auto caller = CreatePeerConnectionWithAudioVideo();
1218   auto callee = CreatePeerConnectionWithAudioVideo();
1219 
1220   auto offer = caller->CreateOffer();
1221   RenameContent(offer->description(), cricket::MEDIA_TYPE_AUDIO, "same");
1222   RenameContent(offer->description(), cricket::MEDIA_TYPE_VIDEO, "same");
1223 
1224   std::string error;
1225   EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer), &error));
1226   EXPECT_EQ(error,
1227             "Failed to set remote offer sdp: Duplicate a=mid value 'same'.");
1228 }
1229 
TEST_P(PeerConnectionMediaTest,CombinedAudioVideoBweConfigPropagatedToMediaEngine)1230 TEST_P(PeerConnectionMediaTest,
1231        CombinedAudioVideoBweConfigPropagatedToMediaEngine) {
1232   RTCConfiguration config;
1233   config.combined_audio_video_bwe.emplace(true);
1234   auto caller = CreatePeerConnectionWithAudioVideo(config);
1235 
1236   ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
1237 
1238   auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
1239   ASSERT_TRUE(caller_voice);
1240   const cricket::AudioOptions& audio_options = caller_voice->options();
1241   EXPECT_EQ(config.combined_audio_video_bwe,
1242             audio_options.combined_audio_video_bwe);
1243 }
1244 
1245 template <typename C>
CompareCodecs(const std::vector<webrtc::RtpCodecCapability> & capabilities,const std::vector<C> & codecs)1246 bool CompareCodecs(const std::vector<webrtc::RtpCodecCapability>& capabilities,
1247                    const std::vector<C>& codecs) {
1248   bool capability_has_rtx =
1249       absl::c_any_of(capabilities, [](const webrtc::RtpCodecCapability& codec) {
1250         return codec.name == cricket::kRtxCodecName;
1251       });
1252   bool codecs_has_rtx = absl::c_any_of(codecs, [](const C& codec) {
1253     return codec.name == cricket::kRtxCodecName;
1254   });
1255 
1256   std::vector<C> codecs_no_rtx;
1257   absl::c_copy_if(
1258       codecs, std::back_inserter(codecs_no_rtx),
1259       [](const C& codec) { return codec.name != cricket::kRtxCodecName; });
1260 
1261   std::vector<webrtc::RtpCodecCapability> capabilities_no_rtx;
1262   absl::c_copy_if(capabilities, std::back_inserter(capabilities_no_rtx),
1263                   [](const webrtc::RtpCodecCapability& codec) {
1264                     return codec.name != cricket::kRtxCodecName;
1265                   });
1266 
1267   return capability_has_rtx == codecs_has_rtx &&
1268          absl::c_equal(
1269              capabilities_no_rtx, codecs_no_rtx,
1270              [](const webrtc::RtpCodecCapability& capability, const C& codec) {
1271                return codec.MatchesCapability(capability);
1272              });
1273 }
1274 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAudioMissingRecvCodec)1275 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1276        SetCodecPreferencesAudioMissingRecvCodec) {
1277   auto fake_engine = std::make_unique<FakeMediaEngine>();
1278   auto send_codecs = fake_engine->voice().send_codecs();
1279   send_codecs.push_back(cricket::AudioCodec(send_codecs.back().id + 1,
1280                                             "send_only_codec", 0, 0, 1));
1281   fake_engine->SetAudioSendCodecs(send_codecs);
1282 
1283   auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
1284 
1285   auto transceiver = caller->pc()->GetTransceivers().front();
1286   auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
1287       cricket::MediaType::MEDIA_TYPE_AUDIO);
1288 
1289   std::vector<webrtc::RtpCodecCapability> codecs;
1290   absl::c_copy_if(capabilities.codecs, std::back_inserter(codecs),
1291                   [](const webrtc::RtpCodecCapability& codec) {
1292                     return codec.name.find("_only_") != std::string::npos;
1293                   });
1294 
1295   auto result = transceiver->SetCodecPreferences(codecs);
1296   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1297 }
1298 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAudioMissingSendCodec)1299 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1300        SetCodecPreferencesAudioMissingSendCodec) {
1301   auto fake_engine = std::make_unique<FakeMediaEngine>();
1302   auto recv_codecs = fake_engine->voice().recv_codecs();
1303   recv_codecs.push_back(cricket::AudioCodec(recv_codecs.back().id + 1,
1304                                             "recv_only_codec", 0, 0, 1));
1305   fake_engine->SetAudioRecvCodecs(recv_codecs);
1306   auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
1307 
1308   auto transceiver = caller->pc()->GetTransceivers().front();
1309   auto capabilities = caller->pc_factory()->GetRtpReceiverCapabilities(
1310       cricket::MediaType::MEDIA_TYPE_AUDIO);
1311 
1312   std::vector<webrtc::RtpCodecCapability> codecs;
1313   absl::c_copy_if(capabilities.codecs, std::back_inserter(codecs),
1314                   [](const webrtc::RtpCodecCapability& codec) {
1315                     return codec.name.find("_only_") != std::string::npos;
1316                   });
1317 
1318   auto result = transceiver->SetCodecPreferences(codecs);
1319   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1320 }
1321 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAudioRejectsVideoCodec)1322 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1323        SetCodecPreferencesAudioRejectsVideoCodec) {
1324   auto caller = CreatePeerConnectionWithAudio();
1325 
1326   auto transceiver = caller->pc()->GetTransceivers().front();
1327   auto video_codecs =
1328       caller->pc_factory()
1329           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
1330           .codecs;
1331   auto codecs =
1332       caller->pc_factory()
1333           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
1334           .codecs;
1335   codecs.insert(codecs.end(), video_codecs.begin(), video_codecs.end());
1336   auto result = transceiver->SetCodecPreferences(codecs);
1337   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1338 }
1339 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAudioRejectsOnlyRtxRedFec)1340 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1341        SetCodecPreferencesAudioRejectsOnlyRtxRedFec) {
1342   auto fake_engine = std::make_unique<FakeMediaEngine>();
1343   auto audio_codecs = fake_engine->voice().send_codecs();
1344   audio_codecs.push_back(cricket::AudioCodec(audio_codecs.back().id + 1,
1345                                              cricket::kRtxCodecName, 0, 0, 1));
1346   audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1347       std::to_string(audio_codecs.back().id - 1);
1348   audio_codecs.push_back(cricket::AudioCodec(audio_codecs.back().id + 1,
1349                                              cricket::kRedCodecName, 0, 0, 1));
1350   audio_codecs.push_back(cricket::AudioCodec(
1351       audio_codecs.back().id + 1, cricket::kUlpfecCodecName, 0, 0, 1));
1352   fake_engine->SetAudioCodecs(audio_codecs);
1353 
1354   auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
1355 
1356   auto transceiver = caller->pc()->GetTransceivers().front();
1357   auto codecs =
1358       caller->pc_factory()
1359           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
1360           .codecs;
1361   auto codecs_only_rtx_red_fec = codecs;
1362   auto it = std::remove_if(codecs_only_rtx_red_fec.begin(),
1363                            codecs_only_rtx_red_fec.end(),
1364                            [](const webrtc::RtpCodecCapability& codec) {
1365                              return !(codec.name == cricket::kRtxCodecName ||
1366                                       codec.name == cricket::kRedCodecName ||
1367                                       codec.name == cricket::kUlpfecCodecName);
1368                            });
1369   codecs_only_rtx_red_fec.erase(it, codecs_only_rtx_red_fec.end());
1370 
1371   auto result = transceiver->SetCodecPreferences(codecs_only_rtx_red_fec);
1372   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1373 }
1374 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAllAudioCodecs)1375 TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesAllAudioCodecs) {
1376   auto caller = CreatePeerConnectionWithAudio();
1377 
1378   auto sender_audio_codecs =
1379       caller->pc_factory()
1380           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1381           .codecs;
1382 
1383   auto audio_transceiver = caller->pc()->GetTransceivers().front();
1384 
1385   // Normal case, set all capabilities as preferences
1386   EXPECT_TRUE(audio_transceiver->SetCodecPreferences(sender_audio_codecs).ok());
1387   auto offer = caller->CreateOffer();
1388   auto codecs = offer->description()
1389                     ->contents()[0]
1390                     .media_description()
1391                     ->as_audio()
1392                     ->codecs();
1393   EXPECT_TRUE(CompareCodecs(sender_audio_codecs, codecs));
1394 }
1395 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesResetAudioCodecs)1396 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1397        SetCodecPreferencesResetAudioCodecs) {
1398   auto caller = CreatePeerConnectionWithAudio();
1399 
1400   auto sender_audio_codecs =
1401       caller->pc_factory()
1402           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
1403           .codecs;
1404   std::vector<webrtc::RtpCodecCapability> empty_codecs = {};
1405 
1406   auto audio_transceiver = caller->pc()->GetTransceivers().front();
1407 
1408   // Normal case, reset codec preferences
1409   EXPECT_TRUE(audio_transceiver->SetCodecPreferences(empty_codecs).ok());
1410   auto offer = caller->CreateOffer();
1411   auto codecs = offer->description()
1412                     ->contents()[0]
1413                     .media_description()
1414                     ->as_audio()
1415                     ->codecs();
1416   EXPECT_TRUE(CompareCodecs(sender_audio_codecs, codecs));
1417 }
1418 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoRejectsAudioCodec)1419 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1420        SetCodecPreferencesVideoRejectsAudioCodec) {
1421   auto caller = CreatePeerConnectionWithVideo();
1422 
1423   auto transceiver = caller->pc()->GetTransceivers().front();
1424   auto audio_codecs =
1425       caller->pc_factory()
1426           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
1427           .codecs;
1428   auto codecs =
1429       caller->pc_factory()
1430           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
1431           .codecs;
1432   codecs.insert(codecs.end(), audio_codecs.begin(), audio_codecs.end());
1433   auto result = transceiver->SetCodecPreferences(codecs);
1434   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1435 }
1436 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoRejectsOnlyRtxRedFec)1437 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1438        SetCodecPreferencesVideoRejectsOnlyRtxRedFec) {
1439   auto fake_engine = std::make_unique<FakeMediaEngine>();
1440   auto video_codecs = fake_engine->video().send_codecs();
1441   video_codecs.push_back(
1442       cricket::VideoCodec(video_codecs.back().id + 1, cricket::kRtxCodecName));
1443   video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1444       std::to_string(video_codecs.back().id - 1);
1445   video_codecs.push_back(
1446       cricket::VideoCodec(video_codecs.back().id + 1, cricket::kRedCodecName));
1447   video_codecs.push_back(cricket::VideoCodec(video_codecs.back().id + 1,
1448                                              cricket::kUlpfecCodecName));
1449   fake_engine->SetVideoCodecs(video_codecs);
1450 
1451   auto caller = CreatePeerConnectionWithVideo(std::move(fake_engine));
1452 
1453   auto transceiver = caller->pc()->GetTransceivers().front();
1454   auto codecs =
1455       caller->pc_factory()
1456           ->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
1457           .codecs;
1458   auto codecs_only_rtx_red_fec = codecs;
1459   auto it = std::remove_if(codecs_only_rtx_red_fec.begin(),
1460                            codecs_only_rtx_red_fec.end(),
1461                            [](const webrtc::RtpCodecCapability& codec) {
1462                              return !(codec.name == cricket::kRtxCodecName ||
1463                                       codec.name == cricket::kRedCodecName ||
1464                                       codec.name == cricket::kUlpfecCodecName);
1465                            });
1466   codecs_only_rtx_red_fec.erase(it, codecs_only_rtx_red_fec.end());
1467 
1468   auto result = transceiver->SetCodecPreferences(codecs_only_rtx_red_fec);
1469   EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
1470 }
1471 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesAllVideoCodecs)1472 TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesAllVideoCodecs) {
1473   auto caller = CreatePeerConnectionWithVideo();
1474 
1475   auto sender_video_codecs =
1476       caller->pc_factory()
1477           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1478           .codecs;
1479 
1480   auto video_transceiver = caller->pc()->GetTransceivers().front();
1481 
1482   // Normal case, setting preferences to normal capabilities
1483   EXPECT_TRUE(video_transceiver->SetCodecPreferences(sender_video_codecs).ok());
1484   auto offer = caller->CreateOffer();
1485   auto codecs = offer->description()
1486                     ->contents()[0]
1487                     .media_description()
1488                     ->as_video()
1489                     ->codecs();
1490   EXPECT_TRUE(CompareCodecs(sender_video_codecs, codecs));
1491 }
1492 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesResetVideoCodecs)1493 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1494        SetCodecPreferencesResetVideoCodecs) {
1495   auto caller = CreatePeerConnectionWithVideo();
1496 
1497   auto sender_video_codecs =
1498       caller->pc_factory()
1499           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1500           .codecs;
1501 
1502   std::vector<webrtc::RtpCodecCapability> empty_codecs = {};
1503 
1504   auto video_transceiver = caller->pc()->GetTransceivers().front();
1505 
1506   // Normal case, resetting preferences with empty list of codecs
1507   EXPECT_TRUE(video_transceiver->SetCodecPreferences(empty_codecs).ok());
1508   auto offer = caller->CreateOffer();
1509   auto codecs = offer->description()
1510                     ->contents()[0]
1511                     .media_description()
1512                     ->as_video()
1513                     ->codecs();
1514   EXPECT_TRUE(CompareCodecs(sender_video_codecs, codecs));
1515 }
1516 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoCodecDuplicatesRemoved)1517 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1518        SetCodecPreferencesVideoCodecDuplicatesRemoved) {
1519   auto caller = CreatePeerConnectionWithVideo();
1520 
1521   auto sender_video_codecs =
1522       caller->pc_factory()
1523           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1524           .codecs;
1525 
1526   auto video_transceiver = caller->pc()->GetTransceivers().front();
1527 
1528   // Check duplicates are removed
1529   auto single_codec = sender_video_codecs;
1530   single_codec.resize(1);
1531   auto duplicate_codec = single_codec;
1532   duplicate_codec.push_back(duplicate_codec.front());
1533   duplicate_codec.push_back(duplicate_codec.front());
1534   duplicate_codec.push_back(duplicate_codec.front());
1535 
1536   EXPECT_TRUE(video_transceiver->SetCodecPreferences(duplicate_codec).ok());
1537   auto offer = caller->CreateOffer();
1538   auto codecs = offer->description()
1539                     ->contents()[0]
1540                     .media_description()
1541                     ->as_video()
1542                     ->codecs();
1543   EXPECT_TRUE(CompareCodecs(single_codec, codecs));
1544 }
1545 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoWithRtx)1546 TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesVideoWithRtx) {
1547   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1548   auto caller_video_codecs = caller_fake_engine->video().send_codecs();
1549   caller_video_codecs.push_back(cricket::VideoCodec(
1550       caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
1551   caller_video_codecs.push_back(cricket::VideoCodec(
1552       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1553   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1554       std::to_string(caller_video_codecs.back().id - 1);
1555   caller_video_codecs.push_back(cricket::VideoCodec(
1556       caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
1557   caller_video_codecs.push_back(cricket::VideoCodec(
1558       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1559   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1560       std::to_string(caller_video_codecs.back().id - 1);
1561   caller_fake_engine->SetVideoCodecs(caller_video_codecs);
1562 
1563   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
1564 
1565   auto sender_video_codecs =
1566       caller->pc_factory()
1567           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1568           .codecs;
1569 
1570   auto video_transceiver = caller->pc()->GetTransceivers().front();
1571 
1572   // Check that RTX codec is properly added
1573   auto video_codecs_vpx_rtx = sender_video_codecs;
1574   auto it =
1575       std::remove_if(video_codecs_vpx_rtx.begin(), video_codecs_vpx_rtx.end(),
1576                      [](const webrtc::RtpCodecCapability& codec) {
1577                        return codec.name != cricket::kRtxCodecName &&
1578                               codec.name != cricket::kVp8CodecName &&
1579                               codec.name != cricket::kVp9CodecName;
1580                      });
1581   video_codecs_vpx_rtx.erase(it, video_codecs_vpx_rtx.end());
1582   absl::c_reverse(video_codecs_vpx_rtx);
1583   EXPECT_EQ(video_codecs_vpx_rtx.size(), 3u);  // VP8, VP9, RTX
1584   EXPECT_TRUE(
1585       video_transceiver->SetCodecPreferences(video_codecs_vpx_rtx).ok());
1586   auto offer = caller->CreateOffer();
1587   auto codecs = offer->description()
1588                     ->contents()[0]
1589                     .media_description()
1590                     ->as_video()
1591                     ->codecs();
1592 
1593   EXPECT_TRUE(CompareCodecs(video_codecs_vpx_rtx, codecs));
1594   EXPECT_EQ(codecs.size(), 4u);
1595 }
1596 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoCodecsNegotiation)1597 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1598        SetCodecPreferencesVideoCodecsNegotiation) {
1599   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1600   auto caller_video_codecs = caller_fake_engine->video().send_codecs();
1601   caller_video_codecs.push_back(cricket::VideoCodec(
1602       caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
1603   caller_video_codecs.push_back(cricket::VideoCodec(
1604       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1605   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1606       std::to_string(caller_video_codecs.back().id - 1);
1607   caller_video_codecs.push_back(cricket::VideoCodec(
1608       caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
1609   caller_video_codecs.push_back(cricket::VideoCodec(
1610       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1611   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1612       std::to_string(caller_video_codecs.back().id - 1);
1613   caller_fake_engine->SetVideoCodecs(caller_video_codecs);
1614 
1615   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1616   callee_fake_engine->SetVideoCodecs(caller_video_codecs);
1617 
1618   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
1619   auto callee = CreatePeerConnection(std::move(callee_fake_engine));
1620 
1621   auto video_codecs = caller->pc_factory()
1622                           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1623                           .codecs;
1624 
1625   auto send_transceiver = caller->pc()->GetTransceivers().front();
1626 
1627   auto video_codecs_vpx = video_codecs;
1628   auto it = std::remove_if(video_codecs_vpx.begin(), video_codecs_vpx.end(),
1629                            [](const webrtc::RtpCodecCapability& codec) {
1630                              return codec.name != cricket::kVp8CodecName &&
1631                                     codec.name != cricket::kVp9CodecName;
1632                            });
1633   video_codecs_vpx.erase(it, video_codecs_vpx.end());
1634   EXPECT_EQ(video_codecs_vpx.size(), 2u);  // VP8, VP9
1635   EXPECT_TRUE(send_transceiver->SetCodecPreferences(video_codecs_vpx).ok());
1636 
1637   auto offer = caller->CreateOfferAndSetAsLocal();
1638   auto codecs = offer->description()
1639                     ->contents()[0]
1640                     .media_description()
1641                     ->as_video()
1642                     ->codecs();
1643 
1644   EXPECT_EQ(codecs.size(), 2u);  // VP8, VP9
1645   EXPECT_TRUE(CompareCodecs(video_codecs_vpx, codecs));
1646 
1647   callee->SetRemoteDescription(std::move(offer));
1648 
1649   auto recv_transceiver = callee->pc()->GetTransceivers().front();
1650   auto video_codecs_vp8_rtx = video_codecs;
1651   it = std::remove_if(video_codecs_vp8_rtx.begin(), video_codecs_vp8_rtx.end(),
1652                       [](const webrtc::RtpCodecCapability& codec) {
1653                         bool r = codec.name != cricket::kVp8CodecName &&
1654                                  codec.name != cricket::kRtxCodecName;
1655                         return r;
1656                       });
1657   video_codecs_vp8_rtx.erase(it, video_codecs_vp8_rtx.end());
1658   EXPECT_EQ(video_codecs_vp8_rtx.size(), 2u);  // VP8, RTX
1659   recv_transceiver->SetCodecPreferences(video_codecs_vp8_rtx);
1660 
1661   auto answer = callee->CreateAnswerAndSetAsLocal();
1662 
1663   auto recv_codecs = answer->description()
1664                          ->contents()[0]
1665                          .media_description()
1666                          ->as_video()
1667                          ->codecs();
1668   EXPECT_EQ(recv_codecs.size(), 1u);  // VP8
1669 }
1670 
TEST_F(PeerConnectionMediaTestUnifiedPlan,SetCodecPreferencesVideoCodecsNegotiationReverseOrder)1671 TEST_F(PeerConnectionMediaTestUnifiedPlan,
1672        SetCodecPreferencesVideoCodecsNegotiationReverseOrder) {
1673   auto caller_fake_engine = std::make_unique<FakeMediaEngine>();
1674   auto caller_video_codecs = caller_fake_engine->video().send_codecs();
1675   caller_video_codecs.push_back(cricket::VideoCodec(
1676       caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
1677   caller_video_codecs.push_back(cricket::VideoCodec(
1678       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1679   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1680       std::to_string(caller_video_codecs.back().id - 1);
1681   caller_video_codecs.push_back(cricket::VideoCodec(
1682       caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
1683   caller_video_codecs.push_back(cricket::VideoCodec(
1684       caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
1685   caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
1686       std::to_string(caller_video_codecs.back().id - 1);
1687   caller_fake_engine->SetVideoCodecs(caller_video_codecs);
1688 
1689   auto callee_fake_engine = std::make_unique<FakeMediaEngine>();
1690   callee_fake_engine->SetVideoCodecs(caller_video_codecs);
1691 
1692   auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
1693   auto callee = CreatePeerConnection(std::move(callee_fake_engine));
1694 
1695   auto video_codecs = caller->pc_factory()
1696                           ->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
1697                           .codecs;
1698 
1699   auto send_transceiver = caller->pc()->GetTransceivers().front();
1700 
1701   auto video_codecs_vpx = video_codecs;
1702   auto it = std::remove_if(video_codecs_vpx.begin(), video_codecs_vpx.end(),
1703                            [](const webrtc::RtpCodecCapability& codec) {
1704                              return codec.name != cricket::kVp8CodecName &&
1705                                     codec.name != cricket::kVp9CodecName;
1706                            });
1707   video_codecs_vpx.erase(it, video_codecs_vpx.end());
1708   EXPECT_EQ(video_codecs_vpx.size(), 2u);  // VP8, VP9
1709   EXPECT_TRUE(send_transceiver->SetCodecPreferences(video_codecs_vpx).ok());
1710 
1711   auto video_codecs_vpx_reverse = video_codecs_vpx;
1712   absl::c_reverse(video_codecs_vpx_reverse);
1713 
1714   auto offer = caller->CreateOfferAndSetAsLocal();
1715   auto codecs = offer->description()
1716                     ->contents()[0]
1717                     .media_description()
1718                     ->as_video()
1719                     ->codecs();
1720   EXPECT_EQ(codecs.size(), 2u);  // VP9, VP8
1721   EXPECT_TRUE(CompareCodecs(video_codecs_vpx, codecs));
1722 
1723   callee->SetRemoteDescription(std::move(offer));
1724 
1725   auto recv_transceiver = callee->pc()->GetTransceivers().front();
1726   recv_transceiver->SetCodecPreferences(video_codecs_vpx_reverse);
1727 
1728   auto answer = callee->CreateAnswerAndSetAsLocal();
1729 
1730   auto recv_codecs = answer->description()
1731                          ->contents()[0]
1732                          .media_description()
1733                          ->as_video()
1734                          ->codecs();
1735 
1736   EXPECT_TRUE(CompareCodecs(video_codecs_vpx_reverse, recv_codecs));
1737 }
1738 
1739 INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaTest,
1740                          PeerConnectionMediaTest,
1741                          Values(SdpSemantics::kPlanB,
1742                                 SdpSemantics::kUnifiedPlan));
1743 
1744 }  // namespace webrtc
1745