blob: 5edd1a38c43496a4ebf76b01f48224533e02f259 [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,
57 AudioProcessing::Create())),
58 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
245// Tests for JSEP SetRemoteDescription with a remote offer.
246
247// Test that setting a remote offer with sendrecv audio and video creates two
248// transceivers, one for receiving audio and one for receiving video.
249TEST_F(PeerConnectionJsepTest, SetRemoteOfferCreatesTransceivers) {
250 auto caller = CreatePeerConnection();
251 auto caller_audio = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
252 auto caller_video = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
253 auto callee = CreatePeerConnection();
254
255 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
256
257 auto transceivers = callee->pc()->GetTransceivers();
258 ASSERT_EQ(2u, transceivers.size());
259 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO,
260 transceivers[0]->receiver()->media_type());
261 EXPECT_EQ(caller_audio->mid(), transceivers[0]->mid());
262 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly, transceivers[0]->direction());
263 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO,
264 transceivers[1]->receiver()->media_type());
265 EXPECT_EQ(caller_video->mid(), transceivers[1]->mid());
266 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly, transceivers[1]->direction());
267}
268
269// Test that setting a remote offer with an audio track will reuse the
270// transceiver created for a local audio track added by AddTrack.
271// This is specified in JSEP section 5.10 (Applying a Remote Description). The
272// intent is to preserve backwards compatibility with clients who only use the
273// AddTrack API.
274TEST_F(PeerConnectionJsepTest, SetRemoteOfferReusesTransceiverFromAddTrack) {
275 auto caller = CreatePeerConnection();
276 caller->AddAudioTrack("a");
277 auto caller_audio = caller->pc()->GetTransceivers()[0];
278 auto callee = CreatePeerConnection();
279 callee->AddAudioTrack("a");
280
281 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
282
283 auto transceivers = callee->pc()->GetTransceivers();
284 ASSERT_EQ(1u, transceivers.size());
285 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
286 transceivers[0]->receiver()->track()->kind());
287 EXPECT_EQ(caller_audio->mid(), transceivers[0]->mid());
288}
289
290// Test that setting a remote offer with an audio track marked sendonly will not
291// reuse a transceiver created by AddTrack. JSEP only allows the transceiver to
292// be reused if the offer direction is sendrecv or recvonly.
293TEST_F(PeerConnectionJsepTest,
294 SetRemoteOfferDoesNotReuseTransceiverIfDirectionSendOnly) {
295 auto caller = CreatePeerConnection();
296 caller->AddAudioTrack("a");
297 auto caller_audio = caller->pc()->GetTransceivers()[0];
298 caller_audio->SetDirection(RtpTransceiverDirection::kSendOnly);
299 auto callee = CreatePeerConnection();
300 callee->AddAudioTrack("a");
301
302 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
303
304 auto transceivers = callee->pc()->GetTransceivers();
305 ASSERT_EQ(2u, transceivers.size());
306 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
307 EXPECT_EQ(caller_audio->mid(), transceivers[1]->mid());
308}
309
310// Test that setting a remote offer with an audio track will not reuse a
311// transceiver added by AddTransceiver. The logic for reusing a transceiver is
312// specific to those added by AddTrack and is tested above.
313TEST_F(PeerConnectionJsepTest,
314 SetRemoteOfferDoesNotReuseTransceiverFromAddTransceiver) {
315 auto caller = CreatePeerConnection();
316 caller->AddAudioTrack("a");
317 auto callee = CreatePeerConnection();
318 auto transceiver = callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
319
320 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
321
322 auto transceivers = callee->pc()->GetTransceivers();
323 ASSERT_EQ(2u, transceivers.size());
324 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
325 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
326 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
327 transceivers[1]->receiver()->track()->kind());
328}
329
330// Test that setting a remote offer with an audio track will not reuse a
331// transceiver created for a local video track added by AddTrack.
332TEST_F(PeerConnectionJsepTest,
333 SetRemoteOfferDoesNotReuseTransceiverOfWrongType) {
334 auto caller = CreatePeerConnection();
335 caller->AddAudioTrack("a");
336 auto callee = CreatePeerConnection();
337 auto video_sender = callee->AddVideoTrack("v");
338
339 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
340
341 auto transceivers = callee->pc()->GetTransceivers();
342 ASSERT_EQ(2u, transceivers.size());
343 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
344 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
345 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
346 transceivers[1]->receiver()->track()->kind());
347}
348
349// Test that setting a remote offer with an audio track will not reuse a
350// stopped transceiver.
351TEST_F(PeerConnectionJsepTest, SetRemoteOfferDoesNotReuseStoppedTransceiver) {
352 auto caller = CreatePeerConnection();
353 caller->AddAudioTrack("a");
354 auto callee = CreatePeerConnection();
355 callee->AddAudioTrack("a");
356 callee->pc()->GetTransceivers()[0]->Stop();
357
358 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
359
360 auto transceivers = callee->pc()->GetTransceivers();
361 ASSERT_EQ(2u, transceivers.size());
362 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
363 EXPECT_TRUE(transceivers[0]->stopped());
364 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
365 EXPECT_FALSE(transceivers[1]->stopped());
366}
367
368// Test that audio and video transceivers created on the remote side with
369// AddTrack will all be reused if there is the same number of audio/video tracks
370// in the remote offer. Additionally, this tests that transceivers are
371// successfully matched even if they are in a different order on the remote
372// side.
373TEST_F(PeerConnectionJsepTest, SetRemoteOfferReusesTransceiversOfBothTypes) {
374 auto caller = CreatePeerConnection();
375 caller->AddVideoTrack("v");
376 caller->AddAudioTrack("a");
377 auto callee = CreatePeerConnection();
378 callee->AddAudioTrack("a");
379 callee->AddVideoTrack("v");
380
381 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
382
383 auto caller_transceivers = caller->pc()->GetTransceivers();
384 auto callee_transceivers = callee->pc()->GetTransceivers();
385 ASSERT_EQ(2u, callee_transceivers.size());
386 EXPECT_EQ(caller_transceivers[0]->mid(), callee_transceivers[1]->mid());
387 EXPECT_EQ(caller_transceivers[1]->mid(), callee_transceivers[0]->mid());
388}
389
390// Tests for JSEP initial CreateAnswer.
391
392// Test that the answer to a remote offer creates media sections for each
393// offered media in the same order and with the same mids.
394TEST_F(PeerConnectionJsepTest, CreateAnswerHasSameMidsAsOffer) {
395 auto caller = CreatePeerConnection();
396 auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
397 auto second_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
398 auto third_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
Steve Antonfa2260d2017-12-28 16:38:23 -0800399 caller->CreateDataChannel("dc");
Steve Antondcc3c022017-12-22 16:02:54 -0800400 auto callee = CreatePeerConnection();
401
Steve Antonfa2260d2017-12-28 16:38:23 -0800402 auto offer = caller->CreateOffer();
403 const auto* offer_data = cricket::GetFirstDataContent(offer->description());
404 ASSERT_TRUE(
405 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
406 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
Steve Antondcc3c022017-12-22 16:02:54 -0800407
408 auto answer = callee->CreateAnswer();
409 auto contents = answer->description()->contents();
Steve Antonfa2260d2017-12-28 16:38:23 -0800410 ASSERT_EQ(4u, contents.size());
Steve Antondcc3c022017-12-22 16:02:54 -0800411 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[0].media_description()->type());
Steve Antonfa2260d2017-12-28 16:38:23 -0800412 EXPECT_EQ(first_transceiver->mid(), contents[0].name);
Steve Antondcc3c022017-12-22 16:02:54 -0800413 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[1].media_description()->type());
Steve Antonfa2260d2017-12-28 16:38:23 -0800414 EXPECT_EQ(second_transceiver->mid(), contents[1].name);
Steve Antondcc3c022017-12-22 16:02:54 -0800415 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[2].media_description()->type());
Steve Antonfa2260d2017-12-28 16:38:23 -0800416 EXPECT_EQ(third_transceiver->mid(), contents[2].name);
417 EXPECT_EQ(cricket::MEDIA_TYPE_DATA, contents[3].media_description()->type());
418 EXPECT_EQ(offer_data->name, contents[3].name);
Steve Antondcc3c022017-12-22 16:02:54 -0800419}
420
421// Test that an answering media section is marked as rejected if the underlying
422// transceiver has been stopped.
423TEST_F(PeerConnectionJsepTest, CreateAnswerRejectsStoppedTransceiver) {
424 auto caller = CreatePeerConnection();
425 caller->AddAudioTrack("a");
426 auto callee = CreatePeerConnection();
427
428 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
429
430 callee->pc()->GetTransceivers()[0]->Stop();
431
432 auto answer = callee->CreateAnswer();
433 auto contents = answer->description()->contents();
434 ASSERT_EQ(1u, contents.size());
435 EXPECT_TRUE(contents[0].rejected);
436}
437
438// Test that CreateAnswer will generate media sections which will only send or
439// receive if the offer indicates it can do the reciprocating direction.
440// The full matrix is tested more extensively in MediaSession.
441TEST_F(PeerConnectionJsepTest, CreateAnswerNegotiatesDirection) {
442 auto caller = CreatePeerConnection();
443 RtpTransceiverInit init;
444 init.direction = RtpTransceiverDirection::kSendOnly;
445 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
446 auto callee = CreatePeerConnection();
447 callee->AddAudioTrack("a");
448
449 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
450
451 auto answer = callee->CreateAnswer();
452 auto contents = answer->description()->contents();
453 ASSERT_EQ(1u, contents.size());
454 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
455 contents[0].media_description()->direction());
456}
457
458// Tests for JSEP SetLocalDescription with a local answer.
459// Note that these test only the additional behaviors not covered by
460// SetLocalDescription with a local offer.
461
462// Test that SetLocalDescription with an answer sets the current_direction
463// property of the transceivers mentioned in the session description.
464TEST_F(PeerConnectionJsepTest, SetLocalAnswerUpdatesCurrentDirection) {
465 auto caller = CreatePeerConnection();
466 auto caller_audio = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
467 caller_audio->SetDirection(RtpTransceiverDirection::kRecvOnly);
468 auto callee = CreatePeerConnection();
469 callee->AddAudioTrack("a");
470
471 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
472 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
473
474 auto transceivers = callee->pc()->GetTransceivers();
475 ASSERT_EQ(1u, transceivers.size());
476 // Since the offer was recvonly and the transceiver direction is sendrecv,
477 // the negotiated direction will be sendonly.
478 EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
479 transceivers[0]->current_direction());
480}
481
482// Tests for JSEP SetRemoteDescription with a remote answer.
483// Note that these test only the additional behaviors not covered by
484// SetRemoteDescription with a remote offer.
485
486TEST_F(PeerConnectionJsepTest, SetRemoteAnswerUpdatesCurrentDirection) {
487 auto caller = CreatePeerConnection();
488 caller->AddAudioTrack("a");
489 auto callee = CreatePeerConnection();
490 callee->AddAudioTrack("a");
491 auto callee_audio = callee->pc()->GetTransceivers()[0];
492 callee_audio->SetDirection(RtpTransceiverDirection::kSendOnly);
493
494 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
495 ASSERT_TRUE(
496 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
497
498 auto transceivers = caller->pc()->GetTransceivers();
499 ASSERT_EQ(1u, transceivers.size());
500 // Since the remote transceiver was set to sendonly, the negotiated direction
501 // in the answer would be sendonly which we apply as recvonly to the local
502 // transceiver.
503 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
504 transceivers[0]->current_direction());
505}
506
507// Tests for multiple round trips.
508
509// Test that setting a transceiver with the inactive direction does not stop it
510// on either the caller or the callee.
511TEST_F(PeerConnectionJsepTest, SettingTransceiverInactiveDoesNotStopIt) {
512 auto caller = CreatePeerConnection();
513 caller->AddAudioTrack("a");
514 auto callee = CreatePeerConnection();
515 callee->AddAudioTrack("a");
516 callee->pc()->GetTransceivers()[0]->SetDirection(
517 RtpTransceiverDirection::kInactive);
518
519 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
520 ASSERT_TRUE(
521 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
522
523 EXPECT_FALSE(caller->pc()->GetTransceivers()[0]->stopped());
524 EXPECT_FALSE(callee->pc()->GetTransceivers()[0]->stopped());
525}
526
527// Test that if a transceiver had been associated and later stopped, then a
528// media section is still generated for it and the media section is marked as
529// rejected.
530TEST_F(PeerConnectionJsepTest,
531 ReOfferMediaSectionForAssociatedStoppedTransceiverIsRejected) {
532 auto caller = CreatePeerConnection();
533 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
534 auto callee = CreatePeerConnection();
535
536 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
537 ASSERT_TRUE(
538 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
539
540 ASSERT_TRUE(transceiver->mid());
541 transceiver->Stop();
542
543 auto reoffer = caller->CreateOffer();
544 auto contents = reoffer->description()->contents();
545 ASSERT_EQ(1u, contents.size());
546 EXPECT_TRUE(contents[0].rejected);
547}
548
549// Test that stopping an associated transceiver on the caller side will stop the
550// corresponding transceiver on the remote side when the remote offer is
551// applied.
552TEST_F(PeerConnectionJsepTest,
553 StoppingTransceiverInOfferStopsTransceiverOnRemoteSide) {
554 auto caller = CreatePeerConnection();
555 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
556 auto callee = CreatePeerConnection();
557
558 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
559 ASSERT_TRUE(
560 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
561
562 transceiver->Stop();
563
564 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
565
566 auto transceivers = callee->pc()->GetTransceivers();
567 EXPECT_TRUE(transceivers[0]->stopped());
568 EXPECT_TRUE(transceivers[0]->mid());
569}
570
571// Test that CreateOffer will only generate a recycled media section if the
572// transceiver to be recycled has been seen stopped by the other side first.
573TEST_F(PeerConnectionJsepTest,
574 CreateOfferDoesNotRecycleMediaSectionIfFirstStopped) {
575 auto caller = CreatePeerConnection();
576 auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
577 auto callee = CreatePeerConnection();
578
579 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
580 ASSERT_TRUE(
581 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
582
583 auto second_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
584 first_transceiver->Stop();
585
586 auto reoffer = caller->CreateOffer();
587 auto contents = reoffer->description()->contents();
588 ASSERT_EQ(2u, contents.size());
589 EXPECT_TRUE(contents[0].rejected);
590 EXPECT_FALSE(contents[1].rejected);
591}
592
593// Test that the offer/answer and transceivers for both the caller and callee
594// side are generated/updated correctly when recycling an audio/video media
595// section as a media section of either the same or opposite type.
596class RecycleMediaSectionTest
597 : public PeerConnectionJsepTest,
598 public testing::WithParamInterface<
599 std::tuple<cricket::MediaType, cricket::MediaType>> {
600 protected:
601 RecycleMediaSectionTest() {
602 first_type_ = std::get<0>(GetParam());
603 second_type_ = std::get<1>(GetParam());
604 }
605
606 cricket::MediaType first_type_;
607 cricket::MediaType second_type_;
608};
609
610TEST_P(RecycleMediaSectionTest, VerifyOfferAnswerAndTransceivers) {
611 auto caller = CreatePeerConnection();
612 auto first_transceiver = caller->AddTransceiver(first_type_);
613 auto callee = CreatePeerConnection();
614
615 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
616
617 std::string first_mid = *first_transceiver->mid();
618 first_transceiver->Stop();
619
620 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
621
622 auto second_transceiver = caller->AddTransceiver(second_type_);
623
624 // The offer should reuse the previous media section but allocate a new MID
625 // and change the media type.
626 auto offer = caller->CreateOffer();
627 auto offer_contents = offer->description()->contents();
628 ASSERT_EQ(1u, offer_contents.size());
629 EXPECT_FALSE(offer_contents[0].rejected);
630 EXPECT_EQ(second_type_, offer_contents[0].media_description()->type());
631 std::string second_mid = offer_contents[0].name;
632 EXPECT_NE(first_mid, second_mid);
633
634 // Setting the local offer will dissociate the previous transceiver and set
635 // the MID for the new transceiver.
636 ASSERT_TRUE(
637 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
638 EXPECT_EQ(rtc::nullopt, first_transceiver->mid());
639 EXPECT_EQ(second_mid, second_transceiver->mid());
640
641 // Setting the remote offer will dissociate the previous transceiver and
642 // create a new transceiver for the media section.
643 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
644 auto callee_transceivers = callee->pc()->GetTransceivers();
645 ASSERT_EQ(2u, callee_transceivers.size());
646 EXPECT_EQ(rtc::nullopt, callee_transceivers[0]->mid());
647 EXPECT_EQ(first_type_, callee_transceivers[0]->receiver()->media_type());
648 EXPECT_EQ(second_mid, callee_transceivers[1]->mid());
649 EXPECT_EQ(second_type_, callee_transceivers[1]->receiver()->media_type());
650
651 // The answer should have only one media section for the new transceiver.
652 auto answer = callee->CreateAnswer();
653 auto answer_contents = answer->description()->contents();
654 ASSERT_EQ(1u, answer_contents.size());
655 EXPECT_FALSE(answer_contents[0].rejected);
656 EXPECT_EQ(second_mid, answer_contents[0].name);
657 EXPECT_EQ(second_type_, answer_contents[0].media_description()->type());
658
659 // Setting the local answer should succeed.
660 ASSERT_TRUE(
661 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
662
663 // Setting the remote answer should succeed.
664 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
665}
666
667// Test all combinations of audio and video as the first and second media type
668// for the media section. This is needed for full test coverage because
669// MediaSession has separate functions for processing audio and video media
670// sections.
671INSTANTIATE_TEST_CASE_P(
672 PeerConnectionJsepTest,
673 RecycleMediaSectionTest,
674 Combine(Values(cricket::MEDIA_TYPE_AUDIO, cricket::MEDIA_TYPE_VIDEO),
675 Values(cricket::MEDIA_TYPE_AUDIO, cricket::MEDIA_TYPE_VIDEO)));
676
Steve Antonfa2260d2017-12-28 16:38:23 -0800677// Test that a new data channel section will not reuse a recycleable audio or
678// video media section. Additionally, tests that the new section is added to the
679// end of the session description.
680TEST_F(PeerConnectionJsepTest, DataChannelDoesNotRecycleMediaSection) {
681 auto caller = CreatePeerConnection();
682 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
683 auto callee = CreatePeerConnection();
684
685 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
686
687 transceiver->Stop();
688
689 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
690
691 caller->CreateDataChannel("dc");
692
693 auto offer = caller->CreateOffer();
694 auto offer_contents = offer->description()->contents();
695 ASSERT_EQ(2u, offer_contents.size());
696 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO,
697 offer_contents[0].media_description()->type());
698 EXPECT_EQ(cricket::MEDIA_TYPE_DATA,
699 offer_contents[1].media_description()->type());
700
701 ASSERT_TRUE(
702 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
703 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
704
705 auto answer = callee->CreateAnswer();
706 auto answer_contents = answer->description()->contents();
707 ASSERT_EQ(2u, answer_contents.size());
708 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO,
709 answer_contents[0].media_description()->type());
710 EXPECT_EQ(cricket::MEDIA_TYPE_DATA,
711 answer_contents[1].media_description()->type());
712}
713
714// Test that if a new track is added to an existing session that has a data,
715// the new section comes at the end of the new offer, after the existing data
716// section.
717TEST_F(PeerConnectionJsepTest, AudioTrackAddedAfterDataSectionInReoffer) {
718 auto caller = CreatePeerConnection();
719 caller->CreateDataChannel("dc");
720 auto callee = CreatePeerConnection();
721
722 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
723
724 caller->AddAudioTrack("a");
725
726 auto offer = caller->CreateOffer();
727 auto contents = offer->description()->contents();
728 ASSERT_EQ(2u, contents.size());
729 EXPECT_EQ(cricket::MEDIA_TYPE_DATA, contents[0].media_description()->type());
730 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[1].media_description()->type());
731}
732
Steve Antondcc3c022017-12-22 16:02:54 -0800733// Tests for MID properties.
734
735static void RenameSection(size_t mline_index,
736 const std::string& new_mid,
737 SessionDescriptionInterface* sdesc) {
738 cricket::SessionDescription* desc = sdesc->description();
739 std::string old_mid = desc->contents()[mline_index].name;
740 desc->contents()[mline_index].name = new_mid;
741 desc->transport_infos()[mline_index].content_name = new_mid;
742 const cricket::ContentGroup* bundle =
743 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
744 if (bundle) {
745 cricket::ContentGroup new_bundle = *bundle;
746 if (new_bundle.RemoveContentName(old_mid)) {
747 new_bundle.AddContentName(new_mid);
748 }
749 desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
750 desc->AddGroup(new_bundle);
751 }
752}
753
754// Test that two PeerConnections can have a successful offer/answer exchange if
755// the MIDs are changed from the defaults.
756TEST_F(PeerConnectionJsepTest, OfferAnswerWithChangedMids) {
757 constexpr char kFirstMid[] = "nondefaultmid";
758 constexpr char kSecondMid[] = "randommid";
759
760 auto caller = CreatePeerConnection();
761 caller->AddAudioTrack("a");
762 caller->AddAudioTrack("b");
763 auto callee = CreatePeerConnection();
764
765 auto offer = caller->CreateOffer();
766 RenameSection(0, kFirstMid, offer.get());
767 RenameSection(1, kSecondMid, offer.get());
768
769 ASSERT_TRUE(
770 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
771 auto caller_transceivers = caller->pc()->GetTransceivers();
772 EXPECT_EQ(kFirstMid, caller_transceivers[0]->mid());
773 EXPECT_EQ(kSecondMid, caller_transceivers[1]->mid());
774
775 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
776 auto callee_transceivers = callee->pc()->GetTransceivers();
777 EXPECT_EQ(kFirstMid, callee_transceivers[0]->mid());
778 EXPECT_EQ(kSecondMid, callee_transceivers[1]->mid());
779
780 auto answer = callee->CreateAnswer();
781 auto answer_contents = answer->description()->contents();
782 EXPECT_EQ(kFirstMid, answer_contents[0].name);
783 EXPECT_EQ(kSecondMid, answer_contents[1].name);
784
785 ASSERT_TRUE(
786 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
787 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
788}
789
790// Test that CreateOffer will generate a MID that is not already used if the
791// default it would have picked is already taken. This is tested by using a
792// third PeerConnection to determine what the default would be for the second
793// media section then setting that as the first media section's MID.
794TEST_F(PeerConnectionJsepTest, CreateOfferGeneratesUniqueMidIfAlreadyTaken) {
795 // First, find what the default MID is for the second media section.
796 auto pc = CreatePeerConnection();
797 pc->AddAudioTrack("a");
798 pc->AddAudioTrack("b");
799 auto default_offer = pc->CreateOffer();
800 std::string default_second_mid =
801 default_offer->description()->contents()[1].name;
802
803 // Now, do an offer/answer with one track which has the MID set to the default
804 // second MID.
805 auto caller = CreatePeerConnection();
806 caller->AddAudioTrack("a");
807 auto callee = CreatePeerConnection();
808
809 auto offer = caller->CreateOffer();
810 RenameSection(0, default_second_mid, offer.get());
811
812 ASSERT_TRUE(
813 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
814 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
815 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
816
817 // Add a second track and ensure that the MID is different.
818 caller->AddAudioTrack("b");
819
820 auto reoffer = caller->CreateOffer();
821 auto reoffer_contents = reoffer->description()->contents();
822 EXPECT_EQ(default_second_mid, reoffer_contents[0].name);
823 EXPECT_NE(reoffer_contents[0].name, reoffer_contents[1].name);
824}
825
Steve Antonfa2260d2017-12-28 16:38:23 -0800826// Test that if an audio or video section has the default data section MID, then
827// CreateOffer will generate a unique MID for the newly added data section.
828TEST_F(PeerConnectionJsepTest,
829 CreateOfferGeneratesUniqueMidForDataSectionIfAlreadyTaken) {
830 // First, find what the default MID is for the data channel.
831 auto pc = CreatePeerConnection();
832 pc->CreateDataChannel("dc");
833 auto default_offer = pc->CreateOffer();
834 std::string default_data_mid =
835 default_offer->description()->contents()[0].name;
836
837 // Now do an offer/answer with one audio track which has a MID set to the
838 // default data MID.
839 auto caller = CreatePeerConnection();
840 caller->AddAudioTrack("a");
841 auto callee = CreatePeerConnection();
842
843 auto offer = caller->CreateOffer();
844 RenameSection(0, default_data_mid, offer.get());
845
846 ASSERT_TRUE(
847 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
848 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
849 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
850
851 // Add a data channel and ensure that the MID is different.
852 caller->CreateDataChannel("dc");
853
854 auto reoffer = caller->CreateOffer();
855 auto reoffer_contents = reoffer->description()->contents();
856 EXPECT_EQ(default_data_mid, reoffer_contents[0].name);
857 EXPECT_NE(reoffer_contents[0].name, reoffer_contents[1].name);
858}
859
Steve Antondcc3c022017-12-22 16:02:54 -0800860// Test that a reoffer initiated by the callee adds a new track to the caller.
861TEST_F(PeerConnectionJsepTest, CalleeDoesReoffer) {
862 auto caller = CreatePeerConnection();
863 caller->AddAudioTrack("a");
864 auto callee = CreatePeerConnection();
865 callee->AddAudioTrack("a");
866 callee->AddVideoTrack("v");
867
868 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
869
870 EXPECT_EQ(1u, caller->pc()->GetTransceivers().size());
871 EXPECT_EQ(2u, callee->pc()->GetTransceivers().size());
872
873 ASSERT_TRUE(callee->ExchangeOfferAnswerWith(caller.get()));
874
875 EXPECT_EQ(2u, caller->pc()->GetTransceivers().size());
876 EXPECT_EQ(2u, callee->pc()->GetTransceivers().size());
877}
878
879} // namespace webrtc