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