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