blob: edab0820e431945f501ddffa765408b9ed99f698 [file] [log] [blame]
Steve Antondcc3c022017-12-22 16:02:54 -08001/*
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#include "api/audio_codecs/builtin_audio_decoder_factory.h"
12#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Steve Antonfa2260d2017-12-28 16:38:23 -080013#include "media/engine/webrtcmediaengine.h"
14#include "modules/audio_processing/include/audio_processing.h"
Steve Antondcc3c022017-12-22 16:02:54 -080015#include "pc/mediasession.h"
Steve Antonfa2260d2017-12-28 16:38:23 -080016#include "pc/peerconnectionfactory.h"
Steve Antondcc3c022017-12-22 16:02:54 -080017#include "pc/peerconnectionwrapper.h"
18#include "pc/sdputils.h"
19#ifdef WEBRTC_ANDROID
20#include "pc/test/androidtestinitializer.h"
21#endif
22#include "pc/test/fakeaudiocapturemodule.h"
Steve Antonfa2260d2017-12-28 16:38:23 -080023#include "pc/test/fakesctptransport.h"
Steve Antondcc3c022017-12-22 16:02:54 -080024#include "rtc_base/gunit.h"
25#include "rtc_base/ptr_util.h"
26#include "rtc_base/virtualsocketserver.h"
27#include "test/gmock.h"
28
29// This file contains tests that ensure the PeerConnection's implementation of
30// CreateOffer/CreateAnswer/SetLocalDescription/SetRemoteDescription conform
31// to the JavaScript Session Establishment Protocol (JSEP).
32// For now these semantics are only available when configuring the
33// PeerConnection with Unified Plan, but eventually that will be the default.
34
35namespace webrtc {
36
37using cricket::MediaContentDescription;
38using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
39using ::testing::Values;
40using ::testing::Combine;
41using ::testing::ElementsAre;
42
Steve Antonfa2260d2017-12-28 16:38:23 -080043class PeerConnectionFactoryForJsepTest : public PeerConnectionFactory {
44 public:
45 PeerConnectionFactoryForJsepTest()
46 : PeerConnectionFactory(
47 rtc::Thread::Current(),
48 rtc::Thread::Current(),
49 rtc::Thread::Current(),
50 rtc::WrapUnique(cricket::WebRtcMediaEngineFactory::Create(
51 FakeAudioCaptureModule::Create(),
52 CreateBuiltinAudioEncoderFactory(),
53 CreateBuiltinAudioDecoderFactory(),
54 nullptr,
55 nullptr,
56 nullptr,
Ivo Creusen62337e52018-01-09 14:17:33 +010057 AudioProcessingBuilder().Create())),
Steve Antonfa2260d2017-12-28 16:38:23 -080058 CreateCallFactory(),
59 nullptr) {}
60
61 std::unique_ptr<cricket::SctpTransportInternalFactory>
62 CreateSctpTransportInternalFactory() {
63 return rtc::MakeUnique<FakeSctpTransportFactory>();
64 }
65};
66
Steve Antondcc3c022017-12-22 16:02:54 -080067class PeerConnectionJsepTest : public ::testing::Test {
68 protected:
69 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
70
71 PeerConnectionJsepTest()
72 : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
73#ifdef WEBRTC_ANDROID
74 InitializeAndroidObjects();
75#endif
Steve Antondcc3c022017-12-22 16:02:54 -080076 }
77
78 WrapperPtr CreatePeerConnection() {
79 RTCConfiguration config;
80 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
81 return CreatePeerConnection(config);
82 }
83
84 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
Steve Antonfa2260d2017-12-28 16:38:23 -080085 rtc::scoped_refptr<PeerConnectionFactory> pc_factory(
86 new rtc::RefCountedObject<PeerConnectionFactoryForJsepTest>());
87 RTC_CHECK(pc_factory->Initialize());
Steve Antondcc3c022017-12-22 16:02:54 -080088 auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
Steve Antonfa2260d2017-12-28 16:38:23 -080089 auto pc = pc_factory->CreatePeerConnection(config, nullptr, nullptr,
90 observer.get());
Steve Antondcc3c022017-12-22 16:02:54 -080091 if (!pc) {
92 return nullptr;
93 }
94
Steve Antonfa2260d2017-12-28 16:38:23 -080095 return rtc::MakeUnique<PeerConnectionWrapper>(pc_factory, pc,
Steve Antondcc3c022017-12-22 16:02:54 -080096 std::move(observer));
97 }
98
99 std::unique_ptr<rtc::VirtualSocketServer> vss_;
100 rtc::AutoSocketServerThread main_;
Steve Antondcc3c022017-12-22 16:02:54 -0800101};
102
103// Tests for JSEP initial offer generation.
104
105// Test that an offer created by a PeerConnection with no transceivers generates
106// no media sections.
107TEST_F(PeerConnectionJsepTest, EmptyInitialOffer) {
108 auto caller = CreatePeerConnection();
Steve Antonfa2260d2017-12-28 16:38:23 -0800109
Steve Antondcc3c022017-12-22 16:02:54 -0800110 auto offer = caller->CreateOffer();
Steve Antonfa2260d2017-12-28 16:38:23 -0800111 ASSERT_EQ(0u, offer->description()->contents().size());
Steve Antondcc3c022017-12-22 16:02:54 -0800112}
113
114// Test that an initial offer with one audio track generates one audio media
115// section.
116TEST_F(PeerConnectionJsepTest, AudioOnlyInitialOffer) {
117 auto caller = CreatePeerConnection();
118 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
Steve Antondcc3c022017-12-22 16:02:54 -0800119
Steve Antonfa2260d2017-12-28 16:38:23 -0800120 auto offer = caller->CreateOffer();
Steve Antondcc3c022017-12-22 16:02:54 -0800121 auto contents = offer->description()->contents();
122 ASSERT_EQ(1u, contents.size());
123 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[0].media_description()->type());
124}
125
126// Test than an initial offer with one video track generates one video media
127// section
128TEST_F(PeerConnectionJsepTest, VideoOnlyInitialOffer) {
129 auto caller = CreatePeerConnection();
130 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
Steve Antondcc3c022017-12-22 16:02:54 -0800131
Steve Antonfa2260d2017-12-28 16:38:23 -0800132 auto offer = caller->CreateOffer();
Steve Antondcc3c022017-12-22 16:02:54 -0800133 auto contents = offer->description()->contents();
134 ASSERT_EQ(1u, contents.size());
135 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[0].media_description()->type());
136}
137
Steve Antonfa2260d2017-12-28 16:38:23 -0800138// Test that an initial offer with one data channel generates one data media
139// section.
140TEST_F(PeerConnectionJsepTest, DataOnlyInitialOffer) {
141 auto caller = CreatePeerConnection();
142 caller->CreateDataChannel("dc");
143
144 auto offer = caller->CreateOffer();
145 auto contents = offer->description()->contents();
146 ASSERT_EQ(1u, contents.size());
147 EXPECT_EQ(cricket::MEDIA_TYPE_DATA, contents[0].media_description()->type());
148}
149
150// Test that creating multiple data channels only results in one data section
151// generated in the offer.
152TEST_F(PeerConnectionJsepTest, MultipleDataChannelsCreateOnlyOneDataSection) {
153 auto caller = CreatePeerConnection();
154 caller->CreateDataChannel("first");
155 caller->CreateDataChannel("second");
156 caller->CreateDataChannel("third");
157
158 auto offer = caller->CreateOffer();
159 ASSERT_EQ(1u, offer->description()->contents().size());
160}
161
Steve Antondcc3c022017-12-22 16:02:54 -0800162// Test that multiple media sections in the initial offer are ordered in the
163// order the transceivers were added to the PeerConnection. This is required by
164// JSEP section 5.2.1.
165TEST_F(PeerConnectionJsepTest, MediaSectionsInInitialOfferOrderedCorrectly) {
166 auto caller = CreatePeerConnection();
167 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
168 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
169 RtpTransceiverInit init;
170 init.direction = RtpTransceiverDirection::kSendOnly;
171 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
Steve Antondcc3c022017-12-22 16:02:54 -0800172
Steve Antonfa2260d2017-12-28 16:38:23 -0800173 auto offer = caller->CreateOffer();
Steve Antondcc3c022017-12-22 16:02:54 -0800174 auto contents = offer->description()->contents();
175 ASSERT_EQ(3u, contents.size());
176
177 const MediaContentDescription* media_description1 =
178 contents[0].media_description();
179 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description1->type());
180 EXPECT_EQ(RtpTransceiverDirection::kSendRecv,
181 media_description1->direction());
182
183 const MediaContentDescription* media_description2 =
184 contents[1].media_description();
185 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, media_description2->type());
186 EXPECT_EQ(RtpTransceiverDirection::kSendRecv,
187 media_description2->direction());
188
189 const MediaContentDescription* media_description3 =
190 contents[2].media_description();
191 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description3->type());
192 EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
193 media_description3->direction());
194}
195
196// Test that media sections in the initial offer have different mids.
197TEST_F(PeerConnectionJsepTest, MediaSectionsInInitialOfferHaveDifferentMids) {
198 auto caller = CreatePeerConnection();
199 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
200 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
Steve Antonfa2260d2017-12-28 16:38:23 -0800201
Steve Antondcc3c022017-12-22 16:02:54 -0800202 auto offer = caller->CreateOffer();
Steve Antondcc3c022017-12-22 16:02:54 -0800203 auto contents = offer->description()->contents();
204 ASSERT_EQ(2u, contents.size());
205 EXPECT_NE(contents[0].name, contents[1].name);
206}
207
208TEST_F(PeerConnectionJsepTest,
209 StoppedTransceiverHasNoMediaSectionInInitialOffer) {
210 auto caller = CreatePeerConnection();
211 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
212 transceiver->Stop();
213
214 auto offer = caller->CreateOffer();
215 EXPECT_EQ(0u, offer->description()->contents().size());
216}
217
218// Tests for JSEP SetLocalDescription with a local offer.
219
220TEST_F(PeerConnectionJsepTest, SetLocalEmptyOfferCreatesNoTransceivers) {
221 auto caller = CreatePeerConnection();
Steve Antonfa2260d2017-12-28 16:38:23 -0800222
Steve Antondcc3c022017-12-22 16:02:54 -0800223 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
224
225 EXPECT_THAT(caller->pc()->GetTransceivers(), ElementsAre());
226 EXPECT_THAT(caller->pc()->GetSenders(), ElementsAre());
227 EXPECT_THAT(caller->pc()->GetReceivers(), ElementsAre());
228}
229
230TEST_F(PeerConnectionJsepTest, SetLocalOfferSetsTransceiverMid) {
231 auto caller = CreatePeerConnection();
232 auto audio_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
233 auto video_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
234
235 auto offer = caller->CreateOffer();
236 std::string audio_mid = offer->description()->contents()[0].name;
237 std::string video_mid = offer->description()->contents()[1].name;
238
239 ASSERT_TRUE(caller->SetLocalDescription(std::move(offer)));
240
241 EXPECT_EQ(audio_mid, audio_transceiver->mid());
242 EXPECT_EQ(video_mid, video_transceiver->mid());
243}
244
Steve Anton02ee47c2018-01-10 16:26:06 -0800245TEST_F(PeerConnectionJsepTest, SetLocalOfferFailsWithNoTrackInMediaSection) {
246 auto caller = CreatePeerConnection();
247 caller->AddAudioTrack("audio");
248
249 auto offer = caller->CreateOffer();
250 auto& contents = offer->description()->contents();
251 ASSERT_EQ(1u, contents.size());
252 contents[0].description->mutable_streams().clear();
253
254 ASSERT_FALSE(caller->SetLocalDescription(std::move(offer)));
255}
256
Steve Antondcc3c022017-12-22 16:02:54 -0800257// Tests for JSEP SetRemoteDescription with a remote offer.
258
259// Test that setting a remote offer with sendrecv audio and video creates two
260// transceivers, one for receiving audio and one for receiving video.
261TEST_F(PeerConnectionJsepTest, SetRemoteOfferCreatesTransceivers) {
262 auto caller = CreatePeerConnection();
263 auto caller_audio = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
264 auto caller_video = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
265 auto callee = CreatePeerConnection();
266
267 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
268
269 auto transceivers = callee->pc()->GetTransceivers();
270 ASSERT_EQ(2u, transceivers.size());
271 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO,
272 transceivers[0]->receiver()->media_type());
273 EXPECT_EQ(caller_audio->mid(), transceivers[0]->mid());
274 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly, transceivers[0]->direction());
275 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO,
276 transceivers[1]->receiver()->media_type());
277 EXPECT_EQ(caller_video->mid(), transceivers[1]->mid());
278 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly, transceivers[1]->direction());
279}
280
281// Test that setting a remote offer with an audio track will reuse the
282// transceiver created for a local audio track added by AddTrack.
283// This is specified in JSEP section 5.10 (Applying a Remote Description). The
284// intent is to preserve backwards compatibility with clients who only use the
285// AddTrack API.
286TEST_F(PeerConnectionJsepTest, SetRemoteOfferReusesTransceiverFromAddTrack) {
287 auto caller = CreatePeerConnection();
288 caller->AddAudioTrack("a");
289 auto caller_audio = caller->pc()->GetTransceivers()[0];
290 auto callee = CreatePeerConnection();
291 callee->AddAudioTrack("a");
292
293 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
294
295 auto transceivers = callee->pc()->GetTransceivers();
296 ASSERT_EQ(1u, transceivers.size());
297 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
298 transceivers[0]->receiver()->track()->kind());
299 EXPECT_EQ(caller_audio->mid(), transceivers[0]->mid());
300}
301
302// Test that setting a remote offer with an audio track marked sendonly will not
303// reuse a transceiver created by AddTrack. JSEP only allows the transceiver to
304// be reused if the offer direction is sendrecv or recvonly.
305TEST_F(PeerConnectionJsepTest,
306 SetRemoteOfferDoesNotReuseTransceiverIfDirectionSendOnly) {
307 auto caller = CreatePeerConnection();
308 caller->AddAudioTrack("a");
309 auto caller_audio = caller->pc()->GetTransceivers()[0];
310 caller_audio->SetDirection(RtpTransceiverDirection::kSendOnly);
311 auto callee = CreatePeerConnection();
312 callee->AddAudioTrack("a");
313
314 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
315
316 auto transceivers = callee->pc()->GetTransceivers();
317 ASSERT_EQ(2u, transceivers.size());
318 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
319 EXPECT_EQ(caller_audio->mid(), transceivers[1]->mid());
320}
321
322// Test that setting a remote offer with an audio track will not reuse a
323// transceiver added by AddTransceiver. The logic for reusing a transceiver is
324// specific to those added by AddTrack and is tested above.
325TEST_F(PeerConnectionJsepTest,
326 SetRemoteOfferDoesNotReuseTransceiverFromAddTransceiver) {
327 auto caller = CreatePeerConnection();
328 caller->AddAudioTrack("a");
329 auto callee = CreatePeerConnection();
330 auto transceiver = callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
331
332 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
333
334 auto transceivers = callee->pc()->GetTransceivers();
335 ASSERT_EQ(2u, transceivers.size());
336 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
337 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
338 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
339 transceivers[1]->receiver()->track()->kind());
340}
341
342// Test that setting a remote offer with an audio track will not reuse a
343// transceiver created for a local video track added by AddTrack.
344TEST_F(PeerConnectionJsepTest,
345 SetRemoteOfferDoesNotReuseTransceiverOfWrongType) {
346 auto caller = CreatePeerConnection();
347 caller->AddAudioTrack("a");
348 auto callee = CreatePeerConnection();
349 auto video_sender = callee->AddVideoTrack("v");
350
351 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
352
353 auto transceivers = callee->pc()->GetTransceivers();
354 ASSERT_EQ(2u, transceivers.size());
355 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
356 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
357 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
358 transceivers[1]->receiver()->track()->kind());
359}
360
361// Test that setting a remote offer with an audio track will not reuse a
362// stopped transceiver.
363TEST_F(PeerConnectionJsepTest, SetRemoteOfferDoesNotReuseStoppedTransceiver) {
364 auto caller = CreatePeerConnection();
365 caller->AddAudioTrack("a");
366 auto callee = CreatePeerConnection();
367 callee->AddAudioTrack("a");
368 callee->pc()->GetTransceivers()[0]->Stop();
369
370 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
371
372 auto transceivers = callee->pc()->GetTransceivers();
373 ASSERT_EQ(2u, transceivers.size());
374 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
375 EXPECT_TRUE(transceivers[0]->stopped());
376 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
377 EXPECT_FALSE(transceivers[1]->stopped());
378}
379
380// Test that audio and video transceivers created on the remote side with
381// AddTrack will all be reused if there is the same number of audio/video tracks
382// in the remote offer. Additionally, this tests that transceivers are
383// successfully matched even if they are in a different order on the remote
384// side.
385TEST_F(PeerConnectionJsepTest, SetRemoteOfferReusesTransceiversOfBothTypes) {
386 auto caller = CreatePeerConnection();
387 caller->AddVideoTrack("v");
388 caller->AddAudioTrack("a");
389 auto callee = CreatePeerConnection();
390 callee->AddAudioTrack("a");
391 callee->AddVideoTrack("v");
392
393 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
394
395 auto caller_transceivers = caller->pc()->GetTransceivers();
396 auto callee_transceivers = callee->pc()->GetTransceivers();
397 ASSERT_EQ(2u, callee_transceivers.size());
398 EXPECT_EQ(caller_transceivers[0]->mid(), callee_transceivers[1]->mid());
399 EXPECT_EQ(caller_transceivers[1]->mid(), callee_transceivers[0]->mid());
400}
401
402// Tests for JSEP initial CreateAnswer.
403
404// Test that the answer to a remote offer creates media sections for each
405// offered media in the same order and with the same mids.
406TEST_F(PeerConnectionJsepTest, CreateAnswerHasSameMidsAsOffer) {
407 auto caller = CreatePeerConnection();
408 auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
409 auto second_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
410 auto third_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
Steve Antonfa2260d2017-12-28 16:38:23 -0800411 caller->CreateDataChannel("dc");
Steve Antondcc3c022017-12-22 16:02:54 -0800412 auto callee = CreatePeerConnection();
413
Steve Antonfa2260d2017-12-28 16:38:23 -0800414 auto offer = caller->CreateOffer();
415 const auto* offer_data = cricket::GetFirstDataContent(offer->description());
416 ASSERT_TRUE(
417 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
418 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
Steve Antondcc3c022017-12-22 16:02:54 -0800419
420 auto answer = callee->CreateAnswer();
421 auto contents = answer->description()->contents();
Steve Antonfa2260d2017-12-28 16:38:23 -0800422 ASSERT_EQ(4u, contents.size());
Steve Antondcc3c022017-12-22 16:02:54 -0800423 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[0].media_description()->type());
Steve Antonfa2260d2017-12-28 16:38:23 -0800424 EXPECT_EQ(first_transceiver->mid(), contents[0].name);
Steve Antondcc3c022017-12-22 16:02:54 -0800425 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[1].media_description()->type());
Steve Antonfa2260d2017-12-28 16:38:23 -0800426 EXPECT_EQ(second_transceiver->mid(), contents[1].name);
Steve Antondcc3c022017-12-22 16:02:54 -0800427 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[2].media_description()->type());
Steve Antonfa2260d2017-12-28 16:38:23 -0800428 EXPECT_EQ(third_transceiver->mid(), contents[2].name);
429 EXPECT_EQ(cricket::MEDIA_TYPE_DATA, contents[3].media_description()->type());
430 EXPECT_EQ(offer_data->name, contents[3].name);
Steve Antondcc3c022017-12-22 16:02:54 -0800431}
432
433// Test that an answering media section is marked as rejected if the underlying
434// transceiver has been stopped.
435TEST_F(PeerConnectionJsepTest, CreateAnswerRejectsStoppedTransceiver) {
436 auto caller = CreatePeerConnection();
437 caller->AddAudioTrack("a");
438 auto callee = CreatePeerConnection();
439
440 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
441
442 callee->pc()->GetTransceivers()[0]->Stop();
443
444 auto answer = callee->CreateAnswer();
445 auto contents = answer->description()->contents();
446 ASSERT_EQ(1u, contents.size());
447 EXPECT_TRUE(contents[0].rejected);
448}
449
450// Test that CreateAnswer will generate media sections which will only send or
451// receive if the offer indicates it can do the reciprocating direction.
452// The full matrix is tested more extensively in MediaSession.
453TEST_F(PeerConnectionJsepTest, CreateAnswerNegotiatesDirection) {
454 auto caller = CreatePeerConnection();
455 RtpTransceiverInit init;
456 init.direction = RtpTransceiverDirection::kSendOnly;
457 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
458 auto callee = CreatePeerConnection();
459 callee->AddAudioTrack("a");
460
461 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
462
463 auto answer = callee->CreateAnswer();
464 auto contents = answer->description()->contents();
465 ASSERT_EQ(1u, contents.size());
466 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
467 contents[0].media_description()->direction());
468}
469
470// Tests for JSEP SetLocalDescription with a local answer.
471// Note that these test only the additional behaviors not covered by
472// SetLocalDescription with a local offer.
473
474// Test that SetLocalDescription with an answer sets the current_direction
475// property of the transceivers mentioned in the session description.
476TEST_F(PeerConnectionJsepTest, SetLocalAnswerUpdatesCurrentDirection) {
477 auto caller = CreatePeerConnection();
478 auto caller_audio = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
479 caller_audio->SetDirection(RtpTransceiverDirection::kRecvOnly);
480 auto callee = CreatePeerConnection();
481 callee->AddAudioTrack("a");
482
483 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
484 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
485
486 auto transceivers = callee->pc()->GetTransceivers();
487 ASSERT_EQ(1u, transceivers.size());
488 // Since the offer was recvonly and the transceiver direction is sendrecv,
489 // the negotiated direction will be sendonly.
490 EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
491 transceivers[0]->current_direction());
492}
493
494// Tests for JSEP SetRemoteDescription with a remote answer.
495// Note that these test only the additional behaviors not covered by
496// SetRemoteDescription with a remote offer.
497
498TEST_F(PeerConnectionJsepTest, SetRemoteAnswerUpdatesCurrentDirection) {
499 auto caller = CreatePeerConnection();
500 caller->AddAudioTrack("a");
501 auto callee = CreatePeerConnection();
502 callee->AddAudioTrack("a");
503 auto callee_audio = callee->pc()->GetTransceivers()[0];
504 callee_audio->SetDirection(RtpTransceiverDirection::kSendOnly);
505
506 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
507 ASSERT_TRUE(
508 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
509
510 auto transceivers = caller->pc()->GetTransceivers();
511 ASSERT_EQ(1u, transceivers.size());
512 // Since the remote transceiver was set to sendonly, the negotiated direction
513 // in the answer would be sendonly which we apply as recvonly to the local
514 // transceiver.
515 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
516 transceivers[0]->current_direction());
517}
518
519// Tests for multiple round trips.
520
521// Test that setting a transceiver with the inactive direction does not stop it
522// on either the caller or the callee.
523TEST_F(PeerConnectionJsepTest, SettingTransceiverInactiveDoesNotStopIt) {
524 auto caller = CreatePeerConnection();
525 caller->AddAudioTrack("a");
526 auto callee = CreatePeerConnection();
527 callee->AddAudioTrack("a");
528 callee->pc()->GetTransceivers()[0]->SetDirection(
529 RtpTransceiverDirection::kInactive);
530
531 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
532 ASSERT_TRUE(
533 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
534
535 EXPECT_FALSE(caller->pc()->GetTransceivers()[0]->stopped());
536 EXPECT_FALSE(callee->pc()->GetTransceivers()[0]->stopped());
537}
538
539// Test that if a transceiver had been associated and later stopped, then a
540// media section is still generated for it and the media section is marked as
541// rejected.
542TEST_F(PeerConnectionJsepTest,
543 ReOfferMediaSectionForAssociatedStoppedTransceiverIsRejected) {
544 auto caller = CreatePeerConnection();
545 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
546 auto callee = CreatePeerConnection();
547
548 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
549 ASSERT_TRUE(
550 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
551
552 ASSERT_TRUE(transceiver->mid());
553 transceiver->Stop();
554
555 auto reoffer = caller->CreateOffer();
556 auto contents = reoffer->description()->contents();
557 ASSERT_EQ(1u, contents.size());
558 EXPECT_TRUE(contents[0].rejected);
559}
560
561// Test that stopping an associated transceiver on the caller side will stop the
562// corresponding transceiver on the remote side when the remote offer is
563// applied.
564TEST_F(PeerConnectionJsepTest,
565 StoppingTransceiverInOfferStopsTransceiverOnRemoteSide) {
566 auto caller = CreatePeerConnection();
567 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
568 auto callee = CreatePeerConnection();
569
570 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
571 ASSERT_TRUE(
572 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
573
574 transceiver->Stop();
575
576 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
577
578 auto transceivers = callee->pc()->GetTransceivers();
579 EXPECT_TRUE(transceivers[0]->stopped());
580 EXPECT_TRUE(transceivers[0]->mid());
581}
582
583// Test that CreateOffer will only generate a recycled media section if the
584// transceiver to be recycled has been seen stopped by the other side first.
585TEST_F(PeerConnectionJsepTest,
586 CreateOfferDoesNotRecycleMediaSectionIfFirstStopped) {
587 auto caller = CreatePeerConnection();
588 auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
589 auto callee = CreatePeerConnection();
590
591 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
592 ASSERT_TRUE(
593 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
594
595 auto second_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
596 first_transceiver->Stop();
597
598 auto reoffer = caller->CreateOffer();
599 auto contents = reoffer->description()->contents();
600 ASSERT_EQ(2u, contents.size());
601 EXPECT_TRUE(contents[0].rejected);
602 EXPECT_FALSE(contents[1].rejected);
603}
604
605// Test that the offer/answer and transceivers for both the caller and callee
606// side are generated/updated correctly when recycling an audio/video media
607// section as a media section of either the same or opposite type.
608class RecycleMediaSectionTest
609 : public PeerConnectionJsepTest,
610 public testing::WithParamInterface<
611 std::tuple<cricket::MediaType, cricket::MediaType>> {
612 protected:
613 RecycleMediaSectionTest() {
614 first_type_ = std::get<0>(GetParam());
615 second_type_ = std::get<1>(GetParam());
616 }
617
618 cricket::MediaType first_type_;
619 cricket::MediaType second_type_;
620};
621
622TEST_P(RecycleMediaSectionTest, VerifyOfferAnswerAndTransceivers) {
623 auto caller = CreatePeerConnection();
624 auto first_transceiver = caller->AddTransceiver(first_type_);
625 auto callee = CreatePeerConnection();
626
627 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
628
629 std::string first_mid = *first_transceiver->mid();
630 first_transceiver->Stop();
631
632 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
633
634 auto second_transceiver = caller->AddTransceiver(second_type_);
635
636 // The offer should reuse the previous media section but allocate a new MID
637 // and change the media type.
638 auto offer = caller->CreateOffer();
639 auto offer_contents = offer->description()->contents();
640 ASSERT_EQ(1u, offer_contents.size());
641 EXPECT_FALSE(offer_contents[0].rejected);
642 EXPECT_EQ(second_type_, offer_contents[0].media_description()->type());
643 std::string second_mid = offer_contents[0].name;
644 EXPECT_NE(first_mid, second_mid);
645
646 // Setting the local offer will dissociate the previous transceiver and set
647 // the MID for the new transceiver.
648 ASSERT_TRUE(
649 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
650 EXPECT_EQ(rtc::nullopt, first_transceiver->mid());
651 EXPECT_EQ(second_mid, second_transceiver->mid());
652
653 // Setting the remote offer will dissociate the previous transceiver and
654 // create a new transceiver for the media section.
655 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
656 auto callee_transceivers = callee->pc()->GetTransceivers();
657 ASSERT_EQ(2u, callee_transceivers.size());
658 EXPECT_EQ(rtc::nullopt, callee_transceivers[0]->mid());
659 EXPECT_EQ(first_type_, callee_transceivers[0]->receiver()->media_type());
660 EXPECT_EQ(second_mid, callee_transceivers[1]->mid());
661 EXPECT_EQ(second_type_, callee_transceivers[1]->receiver()->media_type());
662
663 // The answer should have only one media section for the new transceiver.
664 auto answer = callee->CreateAnswer();
665 auto answer_contents = answer->description()->contents();
666 ASSERT_EQ(1u, answer_contents.size());
667 EXPECT_FALSE(answer_contents[0].rejected);
668 EXPECT_EQ(second_mid, answer_contents[0].name);
669 EXPECT_EQ(second_type_, answer_contents[0].media_description()->type());
670
671 // Setting the local answer should succeed.
672 ASSERT_TRUE(
673 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
674
675 // Setting the remote answer should succeed.
676 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
677}
678
679// Test all combinations of audio and video as the first and second media type
680// for the media section. This is needed for full test coverage because
681// MediaSession has separate functions for processing audio and video media
682// sections.
683INSTANTIATE_TEST_CASE_P(
684 PeerConnectionJsepTest,
685 RecycleMediaSectionTest,
686 Combine(Values(cricket::MEDIA_TYPE_AUDIO, cricket::MEDIA_TYPE_VIDEO),
687 Values(cricket::MEDIA_TYPE_AUDIO, cricket::MEDIA_TYPE_VIDEO)));
688
Steve Antonfa2260d2017-12-28 16:38:23 -0800689// Test that a new data channel section will not reuse a recycleable audio or
690// video media section. Additionally, tests that the new section is added to the
691// end of the session description.
692TEST_F(PeerConnectionJsepTest, DataChannelDoesNotRecycleMediaSection) {
693 auto caller = CreatePeerConnection();
694 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
695 auto callee = CreatePeerConnection();
696
697 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
698
699 transceiver->Stop();
700
701 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
702
703 caller->CreateDataChannel("dc");
704
705 auto offer = caller->CreateOffer();
706 auto offer_contents = offer->description()->contents();
707 ASSERT_EQ(2u, offer_contents.size());
708 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO,
709 offer_contents[0].media_description()->type());
710 EXPECT_EQ(cricket::MEDIA_TYPE_DATA,
711 offer_contents[1].media_description()->type());
712
713 ASSERT_TRUE(
714 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
715 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
716
717 auto answer = callee->CreateAnswer();
718 auto answer_contents = answer->description()->contents();
719 ASSERT_EQ(2u, answer_contents.size());
720 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO,
721 answer_contents[0].media_description()->type());
722 EXPECT_EQ(cricket::MEDIA_TYPE_DATA,
723 answer_contents[1].media_description()->type());
724}
725
726// Test that if a new track is added to an existing session that has a data,
727// the new section comes at the end of the new offer, after the existing data
728// section.
729TEST_F(PeerConnectionJsepTest, AudioTrackAddedAfterDataSectionInReoffer) {
730 auto caller = CreatePeerConnection();
731 caller->CreateDataChannel("dc");
732 auto callee = CreatePeerConnection();
733
734 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
735
736 caller->AddAudioTrack("a");
737
738 auto offer = caller->CreateOffer();
739 auto contents = offer->description()->contents();
740 ASSERT_EQ(2u, contents.size());
741 EXPECT_EQ(cricket::MEDIA_TYPE_DATA, contents[0].media_description()->type());
742 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[1].media_description()->type());
743}
744
Steve Antondcc3c022017-12-22 16:02:54 -0800745// Tests for MID properties.
746
747static void RenameSection(size_t mline_index,
748 const std::string& new_mid,
749 SessionDescriptionInterface* sdesc) {
750 cricket::SessionDescription* desc = sdesc->description();
751 std::string old_mid = desc->contents()[mline_index].name;
752 desc->contents()[mline_index].name = new_mid;
753 desc->transport_infos()[mline_index].content_name = new_mid;
754 const cricket::ContentGroup* bundle =
755 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
756 if (bundle) {
757 cricket::ContentGroup new_bundle = *bundle;
758 if (new_bundle.RemoveContentName(old_mid)) {
759 new_bundle.AddContentName(new_mid);
760 }
761 desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
762 desc->AddGroup(new_bundle);
763 }
764}
765
766// Test that two PeerConnections can have a successful offer/answer exchange if
767// the MIDs are changed from the defaults.
768TEST_F(PeerConnectionJsepTest, OfferAnswerWithChangedMids) {
769 constexpr char kFirstMid[] = "nondefaultmid";
770 constexpr char kSecondMid[] = "randommid";
771
772 auto caller = CreatePeerConnection();
773 caller->AddAudioTrack("a");
774 caller->AddAudioTrack("b");
775 auto callee = CreatePeerConnection();
776
777 auto offer = caller->CreateOffer();
778 RenameSection(0, kFirstMid, offer.get());
779 RenameSection(1, kSecondMid, offer.get());
780
781 ASSERT_TRUE(
782 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
783 auto caller_transceivers = caller->pc()->GetTransceivers();
784 EXPECT_EQ(kFirstMid, caller_transceivers[0]->mid());
785 EXPECT_EQ(kSecondMid, caller_transceivers[1]->mid());
786
787 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
788 auto callee_transceivers = callee->pc()->GetTransceivers();
789 EXPECT_EQ(kFirstMid, callee_transceivers[0]->mid());
790 EXPECT_EQ(kSecondMid, callee_transceivers[1]->mid());
791
792 auto answer = callee->CreateAnswer();
793 auto answer_contents = answer->description()->contents();
794 EXPECT_EQ(kFirstMid, answer_contents[0].name);
795 EXPECT_EQ(kSecondMid, answer_contents[1].name);
796
797 ASSERT_TRUE(
798 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
799 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
800}
801
802// Test that CreateOffer will generate a MID that is not already used if the
803// default it would have picked is already taken. This is tested by using a
804// third PeerConnection to determine what the default would be for the second
805// media section then setting that as the first media section's MID.
806TEST_F(PeerConnectionJsepTest, CreateOfferGeneratesUniqueMidIfAlreadyTaken) {
807 // First, find what the default MID is for the second media section.
808 auto pc = CreatePeerConnection();
809 pc->AddAudioTrack("a");
810 pc->AddAudioTrack("b");
811 auto default_offer = pc->CreateOffer();
812 std::string default_second_mid =
813 default_offer->description()->contents()[1].name;
814
815 // Now, do an offer/answer with one track which has the MID set to the default
816 // second MID.
817 auto caller = CreatePeerConnection();
818 caller->AddAudioTrack("a");
819 auto callee = CreatePeerConnection();
820
821 auto offer = caller->CreateOffer();
822 RenameSection(0, default_second_mid, offer.get());
823
824 ASSERT_TRUE(
825 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
826 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
827 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
828
829 // Add a second track and ensure that the MID is different.
830 caller->AddAudioTrack("b");
831
832 auto reoffer = caller->CreateOffer();
833 auto reoffer_contents = reoffer->description()->contents();
834 EXPECT_EQ(default_second_mid, reoffer_contents[0].name);
835 EXPECT_NE(reoffer_contents[0].name, reoffer_contents[1].name);
836}
837
Steve Antonfa2260d2017-12-28 16:38:23 -0800838// Test that if an audio or video section has the default data section MID, then
839// CreateOffer will generate a unique MID for the newly added data section.
840TEST_F(PeerConnectionJsepTest,
841 CreateOfferGeneratesUniqueMidForDataSectionIfAlreadyTaken) {
842 // First, find what the default MID is for the data channel.
843 auto pc = CreatePeerConnection();
844 pc->CreateDataChannel("dc");
845 auto default_offer = pc->CreateOffer();
846 std::string default_data_mid =
847 default_offer->description()->contents()[0].name;
848
849 // Now do an offer/answer with one audio track which has a MID set to the
850 // default data MID.
851 auto caller = CreatePeerConnection();
852 caller->AddAudioTrack("a");
853 auto callee = CreatePeerConnection();
854
855 auto offer = caller->CreateOffer();
856 RenameSection(0, default_data_mid, offer.get());
857
858 ASSERT_TRUE(
859 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
860 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
861 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
862
863 // Add a data channel and ensure that the MID is different.
864 caller->CreateDataChannel("dc");
865
866 auto reoffer = caller->CreateOffer();
867 auto reoffer_contents = reoffer->description()->contents();
868 EXPECT_EQ(default_data_mid, reoffer_contents[0].name);
869 EXPECT_NE(reoffer_contents[0].name, reoffer_contents[1].name);
870}
871
Steve Antondcc3c022017-12-22 16:02:54 -0800872// Test that a reoffer initiated by the callee adds a new track to the caller.
873TEST_F(PeerConnectionJsepTest, CalleeDoesReoffer) {
874 auto caller = CreatePeerConnection();
875 caller->AddAudioTrack("a");
876 auto callee = CreatePeerConnection();
877 callee->AddAudioTrack("a");
878 callee->AddVideoTrack("v");
879
880 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
881
882 EXPECT_EQ(1u, caller->pc()->GetTransceivers().size());
883 EXPECT_EQ(2u, callee->pc()->GetTransceivers().size());
884
885 ASSERT_TRUE(callee->ExchangeOfferAnswerWith(caller.get()));
886
887 EXPECT_EQ(2u, caller->pc()->GetTransceivers().size());
888 EXPECT_EQ(2u, callee->pc()->GetTransceivers().size());
889}
890
Steve Anton02ee47c2018-01-10 16:26:06 -0800891// Tests for MSID properties.
892
893// Test that adding a track with AddTrack results in an offer that signals the
894// track's ID.
895TEST_F(PeerConnectionJsepTest, AddingTrackWithAddTrackSpecifiesTrackId) {
896 const std::string kTrackId = "audio_track";
897
898 auto caller = CreatePeerConnection();
899 caller->AddAudioTrack(kTrackId);
900
901 auto offer = caller->CreateOffer();
902 auto contents = offer->description()->contents();
903 ASSERT_EQ(1u, contents.size());
904 auto streams = contents[0].media_description()->streams();
905 ASSERT_EQ(1u, streams.size());
906 EXPECT_EQ(kTrackId, streams[0].id);
907}
908
909// Test that adding a track by calling AddTransceiver then SetTrack results in
910// an offer that does not signal the track's ID and signals a random ID.
911TEST_F(PeerConnectionJsepTest,
912 AddingTrackWithAddTransceiverSpecifiesRandomTrackId) {
913 const std::string kTrackId = "audio_track";
914
915 auto caller = CreatePeerConnection();
916 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
917 transceiver->sender()->SetTrack(caller->CreateAudioTrack(kTrackId));
918
919 auto offer = caller->CreateOffer();
920 auto contents = offer->description()->contents();
921 ASSERT_EQ(1u, contents.size());
922 auto streams = contents[0].media_description()->streams();
923 ASSERT_EQ(1u, streams.size());
924 EXPECT_NE(kTrackId, streams[0].id);
925}
926
927// Test that the callee RtpReceiver created by a call to SetRemoteDescription
928// has its ID set to the signaled track ID.
929TEST_F(PeerConnectionJsepTest,
930 RtpReceiverCreatedBySetRemoteDescriptionHasSignaledTrackId) {
931 const std::string kTrackId = "audio_track";
932
933 auto caller = CreatePeerConnection();
934 auto callee = CreatePeerConnection();
935 caller->AddAudioTrack(kTrackId);
936
937 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
938
939 ASSERT_EQ(1u, callee->pc()->GetReceivers().size());
940 auto receiver = callee->pc()->GetReceivers()[0];
941 EXPECT_EQ(kTrackId, receiver->id());
942}
943
944// Test that if the callee RtpReceiver is reused by a call to
945// SetRemoteDescription, its ID does not change.
946TEST_F(PeerConnectionJsepTest,
947 RtpReceiverCreatedBeforeSetRemoteDescriptionKeepsId) {
948 const std::string kTrackId = "audio_track";
949
950 auto caller = CreatePeerConnection();
951 auto callee = CreatePeerConnection();
952 caller->AddAudioTrack(kTrackId);
953 callee->AddAudioTrack("dummy_track");
954
955 ASSERT_EQ(1u, callee->pc()->GetReceivers().size());
956 auto receiver = callee->pc()->GetReceivers()[0];
957 std::string receiver_id = receiver->id();
958
959 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
960
961 EXPECT_EQ(receiver_id, receiver->id());
962}
963
Steve Antondcc3c022017-12-22 16:02:54 -0800964} // namespace webrtc