blob: 119b1351a0fc3089d6ba09b09570952fe128ec19 [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"
13#include "pc/mediasession.h"
14#include "pc/peerconnectionwrapper.h"
15#include "pc/sdputils.h"
16#ifdef WEBRTC_ANDROID
17#include "pc/test/androidtestinitializer.h"
18#endif
19#include "pc/test/fakeaudiocapturemodule.h"
20#include "rtc_base/gunit.h"
21#include "rtc_base/ptr_util.h"
22#include "rtc_base/virtualsocketserver.h"
23#include "test/gmock.h"
24
25// This file contains tests that ensure the PeerConnection's implementation of
26// CreateOffer/CreateAnswer/SetLocalDescription/SetRemoteDescription conform
27// to the JavaScript Session Establishment Protocol (JSEP).
28// For now these semantics are only available when configuring the
29// PeerConnection with Unified Plan, but eventually that will be the default.
30
31namespace webrtc {
32
33using cricket::MediaContentDescription;
34using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
35using ::testing::Values;
36using ::testing::Combine;
37using ::testing::ElementsAre;
38
39class PeerConnectionJsepTest : public ::testing::Test {
40 protected:
41 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
42
43 PeerConnectionJsepTest()
44 : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
45#ifdef WEBRTC_ANDROID
46 InitializeAndroidObjects();
47#endif
48 pc_factory_ = CreatePeerConnectionFactory(
49 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
50 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
51 CreateBuiltinAudioDecoderFactory(), nullptr, nullptr);
52 }
53
54 WrapperPtr CreatePeerConnection() {
55 RTCConfiguration config;
56 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
57 return CreatePeerConnection(config);
58 }
59
60 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
61 auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
62 auto pc = pc_factory_->CreatePeerConnection(config, nullptr, nullptr,
63 observer.get());
64 if (!pc) {
65 return nullptr;
66 }
67
68 return rtc::MakeUnique<PeerConnectionWrapper>(pc_factory_, pc,
69 std::move(observer));
70 }
71
72 std::unique_ptr<rtc::VirtualSocketServer> vss_;
73 rtc::AutoSocketServerThread main_;
74 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
75};
76
77// Tests for JSEP initial offer generation.
78
79// Test that an offer created by a PeerConnection with no transceivers generates
80// no media sections.
81TEST_F(PeerConnectionJsepTest, EmptyInitialOffer) {
82 auto caller = CreatePeerConnection();
83 auto offer = caller->CreateOffer();
84 EXPECT_EQ(0u, offer->description()->contents().size());
85}
86
87// Test that an initial offer with one audio track generates one audio media
88// section.
89TEST_F(PeerConnectionJsepTest, AudioOnlyInitialOffer) {
90 auto caller = CreatePeerConnection();
91 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
92 auto offer = caller->CreateOffer();
93
94 auto contents = offer->description()->contents();
95 ASSERT_EQ(1u, contents.size());
96 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[0].media_description()->type());
97}
98
99// Test than an initial offer with one video track generates one video media
100// section
101TEST_F(PeerConnectionJsepTest, VideoOnlyInitialOffer) {
102 auto caller = CreatePeerConnection();
103 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
104 auto offer = caller->CreateOffer();
105
106 auto contents = offer->description()->contents();
107 ASSERT_EQ(1u, contents.size());
108 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[0].media_description()->type());
109}
110
111// Test that multiple media sections in the initial offer are ordered in the
112// order the transceivers were added to the PeerConnection. This is required by
113// JSEP section 5.2.1.
114TEST_F(PeerConnectionJsepTest, MediaSectionsInInitialOfferOrderedCorrectly) {
115 auto caller = CreatePeerConnection();
116 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
117 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
118 RtpTransceiverInit init;
119 init.direction = RtpTransceiverDirection::kSendOnly;
120 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
121 auto offer = caller->CreateOffer();
122
123 auto contents = offer->description()->contents();
124 ASSERT_EQ(3u, contents.size());
125
126 const MediaContentDescription* media_description1 =
127 contents[0].media_description();
128 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description1->type());
129 EXPECT_EQ(RtpTransceiverDirection::kSendRecv,
130 media_description1->direction());
131
132 const MediaContentDescription* media_description2 =
133 contents[1].media_description();
134 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, media_description2->type());
135 EXPECT_EQ(RtpTransceiverDirection::kSendRecv,
136 media_description2->direction());
137
138 const MediaContentDescription* media_description3 =
139 contents[2].media_description();
140 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description3->type());
141 EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
142 media_description3->direction());
143}
144
145// Test that media sections in the initial offer have different mids.
146TEST_F(PeerConnectionJsepTest, MediaSectionsInInitialOfferHaveDifferentMids) {
147 auto caller = CreatePeerConnection();
148 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
149 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
150 auto offer = caller->CreateOffer();
151
152 std::string sdp;
153 offer->ToString(&sdp);
154 RTC_LOG(LS_INFO) << sdp;
155
156 auto contents = offer->description()->contents();
157 ASSERT_EQ(2u, contents.size());
158 EXPECT_NE(contents[0].name, contents[1].name);
159}
160
161TEST_F(PeerConnectionJsepTest,
162 StoppedTransceiverHasNoMediaSectionInInitialOffer) {
163 auto caller = CreatePeerConnection();
164 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
165 transceiver->Stop();
166
167 auto offer = caller->CreateOffer();
168 EXPECT_EQ(0u, offer->description()->contents().size());
169}
170
171// Tests for JSEP SetLocalDescription with a local offer.
172
173TEST_F(PeerConnectionJsepTest, SetLocalEmptyOfferCreatesNoTransceivers) {
174 auto caller = CreatePeerConnection();
175 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
176
177 EXPECT_THAT(caller->pc()->GetTransceivers(), ElementsAre());
178 EXPECT_THAT(caller->pc()->GetSenders(), ElementsAre());
179 EXPECT_THAT(caller->pc()->GetReceivers(), ElementsAre());
180}
181
182TEST_F(PeerConnectionJsepTest, SetLocalOfferSetsTransceiverMid) {
183 auto caller = CreatePeerConnection();
184 auto audio_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
185 auto video_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
186
187 auto offer = caller->CreateOffer();
188 std::string audio_mid = offer->description()->contents()[0].name;
189 std::string video_mid = offer->description()->contents()[1].name;
190
191 ASSERT_TRUE(caller->SetLocalDescription(std::move(offer)));
192
193 EXPECT_EQ(audio_mid, audio_transceiver->mid());
194 EXPECT_EQ(video_mid, video_transceiver->mid());
195}
196
197// Tests for JSEP SetRemoteDescription with a remote offer.
198
199// Test that setting a remote offer with sendrecv audio and video creates two
200// transceivers, one for receiving audio and one for receiving video.
201TEST_F(PeerConnectionJsepTest, SetRemoteOfferCreatesTransceivers) {
202 auto caller = CreatePeerConnection();
203 auto caller_audio = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
204 auto caller_video = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
205 auto callee = CreatePeerConnection();
206
207 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
208
209 auto transceivers = callee->pc()->GetTransceivers();
210 ASSERT_EQ(2u, transceivers.size());
211 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO,
212 transceivers[0]->receiver()->media_type());
213 EXPECT_EQ(caller_audio->mid(), transceivers[0]->mid());
214 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly, transceivers[0]->direction());
215 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO,
216 transceivers[1]->receiver()->media_type());
217 EXPECT_EQ(caller_video->mid(), transceivers[1]->mid());
218 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly, transceivers[1]->direction());
219}
220
221// Test that setting a remote offer with an audio track will reuse the
222// transceiver created for a local audio track added by AddTrack.
223// This is specified in JSEP section 5.10 (Applying a Remote Description). The
224// intent is to preserve backwards compatibility with clients who only use the
225// AddTrack API.
226TEST_F(PeerConnectionJsepTest, SetRemoteOfferReusesTransceiverFromAddTrack) {
227 auto caller = CreatePeerConnection();
228 caller->AddAudioTrack("a");
229 auto caller_audio = caller->pc()->GetTransceivers()[0];
230 auto callee = CreatePeerConnection();
231 callee->AddAudioTrack("a");
232
233 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
234
235 auto transceivers = callee->pc()->GetTransceivers();
236 ASSERT_EQ(1u, transceivers.size());
237 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
238 transceivers[0]->receiver()->track()->kind());
239 EXPECT_EQ(caller_audio->mid(), transceivers[0]->mid());
240}
241
242// Test that setting a remote offer with an audio track marked sendonly will not
243// reuse a transceiver created by AddTrack. JSEP only allows the transceiver to
244// be reused if the offer direction is sendrecv or recvonly.
245TEST_F(PeerConnectionJsepTest,
246 SetRemoteOfferDoesNotReuseTransceiverIfDirectionSendOnly) {
247 auto caller = CreatePeerConnection();
248 caller->AddAudioTrack("a");
249 auto caller_audio = caller->pc()->GetTransceivers()[0];
250 caller_audio->SetDirection(RtpTransceiverDirection::kSendOnly);
251 auto callee = CreatePeerConnection();
252 callee->AddAudioTrack("a");
253
254 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
255
256 auto transceivers = callee->pc()->GetTransceivers();
257 ASSERT_EQ(2u, transceivers.size());
258 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
259 EXPECT_EQ(caller_audio->mid(), transceivers[1]->mid());
260}
261
262// Test that setting a remote offer with an audio track will not reuse a
263// transceiver added by AddTransceiver. The logic for reusing a transceiver is
264// specific to those added by AddTrack and is tested above.
265TEST_F(PeerConnectionJsepTest,
266 SetRemoteOfferDoesNotReuseTransceiverFromAddTransceiver) {
267 auto caller = CreatePeerConnection();
268 caller->AddAudioTrack("a");
269 auto callee = CreatePeerConnection();
270 auto transceiver = callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
271
272 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
273
274 auto transceivers = callee->pc()->GetTransceivers();
275 ASSERT_EQ(2u, transceivers.size());
276 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
277 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
278 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
279 transceivers[1]->receiver()->track()->kind());
280}
281
282// Test that setting a remote offer with an audio track will not reuse a
283// transceiver created for a local video track added by AddTrack.
284TEST_F(PeerConnectionJsepTest,
285 SetRemoteOfferDoesNotReuseTransceiverOfWrongType) {
286 auto caller = CreatePeerConnection();
287 caller->AddAudioTrack("a");
288 auto callee = CreatePeerConnection();
289 auto video_sender = callee->AddVideoTrack("v");
290
291 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
292
293 auto transceivers = callee->pc()->GetTransceivers();
294 ASSERT_EQ(2u, transceivers.size());
295 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
296 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
297 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
298 transceivers[1]->receiver()->track()->kind());
299}
300
301// Test that setting a remote offer with an audio track will not reuse a
302// stopped transceiver.
303TEST_F(PeerConnectionJsepTest, SetRemoteOfferDoesNotReuseStoppedTransceiver) {
304 auto caller = CreatePeerConnection();
305 caller->AddAudioTrack("a");
306 auto callee = CreatePeerConnection();
307 callee->AddAudioTrack("a");
308 callee->pc()->GetTransceivers()[0]->Stop();
309
310 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
311
312 auto transceivers = callee->pc()->GetTransceivers();
313 ASSERT_EQ(2u, transceivers.size());
314 EXPECT_EQ(rtc::nullopt, transceivers[0]->mid());
315 EXPECT_TRUE(transceivers[0]->stopped());
316 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
317 EXPECT_FALSE(transceivers[1]->stopped());
318}
319
320// Test that audio and video transceivers created on the remote side with
321// AddTrack will all be reused if there is the same number of audio/video tracks
322// in the remote offer. Additionally, this tests that transceivers are
323// successfully matched even if they are in a different order on the remote
324// side.
325TEST_F(PeerConnectionJsepTest, SetRemoteOfferReusesTransceiversOfBothTypes) {
326 auto caller = CreatePeerConnection();
327 caller->AddVideoTrack("v");
328 caller->AddAudioTrack("a");
329 auto callee = CreatePeerConnection();
330 callee->AddAudioTrack("a");
331 callee->AddVideoTrack("v");
332
333 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
334
335 auto caller_transceivers = caller->pc()->GetTransceivers();
336 auto callee_transceivers = callee->pc()->GetTransceivers();
337 ASSERT_EQ(2u, callee_transceivers.size());
338 EXPECT_EQ(caller_transceivers[0]->mid(), callee_transceivers[1]->mid());
339 EXPECT_EQ(caller_transceivers[1]->mid(), callee_transceivers[0]->mid());
340}
341
342// Tests for JSEP initial CreateAnswer.
343
344// Test that the answer to a remote offer creates media sections for each
345// offered media in the same order and with the same mids.
346TEST_F(PeerConnectionJsepTest, CreateAnswerHasSameMidsAsOffer) {
347 auto caller = CreatePeerConnection();
348 auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
349 auto second_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
350 auto third_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
351 auto callee = CreatePeerConnection();
352
353 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
354
355 auto answer = callee->CreateAnswer();
356 auto contents = answer->description()->contents();
357 ASSERT_EQ(3u, contents.size());
358 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[0].media_description()->type());
359 EXPECT_EQ(*first_transceiver->mid(), contents[0].name);
360 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[1].media_description()->type());
361 EXPECT_EQ(*second_transceiver->mid(), contents[1].name);
362 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[2].media_description()->type());
363 EXPECT_EQ(*third_transceiver->mid(), contents[2].name);
364}
365
366// Test that an answering media section is marked as rejected if the underlying
367// transceiver has been stopped.
368TEST_F(PeerConnectionJsepTest, CreateAnswerRejectsStoppedTransceiver) {
369 auto caller = CreatePeerConnection();
370 caller->AddAudioTrack("a");
371 auto callee = CreatePeerConnection();
372
373 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
374
375 callee->pc()->GetTransceivers()[0]->Stop();
376
377 auto answer = callee->CreateAnswer();
378 auto contents = answer->description()->contents();
379 ASSERT_EQ(1u, contents.size());
380 EXPECT_TRUE(contents[0].rejected);
381}
382
383// Test that CreateAnswer will generate media sections which will only send or
384// receive if the offer indicates it can do the reciprocating direction.
385// The full matrix is tested more extensively in MediaSession.
386TEST_F(PeerConnectionJsepTest, CreateAnswerNegotiatesDirection) {
387 auto caller = CreatePeerConnection();
388 RtpTransceiverInit init;
389 init.direction = RtpTransceiverDirection::kSendOnly;
390 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
391 auto callee = CreatePeerConnection();
392 callee->AddAudioTrack("a");
393
394 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
395
396 auto answer = callee->CreateAnswer();
397 auto contents = answer->description()->contents();
398 ASSERT_EQ(1u, contents.size());
399 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
400 contents[0].media_description()->direction());
401}
402
403// Tests for JSEP SetLocalDescription with a local answer.
404// Note that these test only the additional behaviors not covered by
405// SetLocalDescription with a local offer.
406
407// Test that SetLocalDescription with an answer sets the current_direction
408// property of the transceivers mentioned in the session description.
409TEST_F(PeerConnectionJsepTest, SetLocalAnswerUpdatesCurrentDirection) {
410 auto caller = CreatePeerConnection();
411 auto caller_audio = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
412 caller_audio->SetDirection(RtpTransceiverDirection::kRecvOnly);
413 auto callee = CreatePeerConnection();
414 callee->AddAudioTrack("a");
415
416 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
417 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
418
419 auto transceivers = callee->pc()->GetTransceivers();
420 ASSERT_EQ(1u, transceivers.size());
421 // Since the offer was recvonly and the transceiver direction is sendrecv,
422 // the negotiated direction will be sendonly.
423 EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
424 transceivers[0]->current_direction());
425}
426
427// Tests for JSEP SetRemoteDescription with a remote answer.
428// Note that these test only the additional behaviors not covered by
429// SetRemoteDescription with a remote offer.
430
431TEST_F(PeerConnectionJsepTest, SetRemoteAnswerUpdatesCurrentDirection) {
432 auto caller = CreatePeerConnection();
433 caller->AddAudioTrack("a");
434 auto callee = CreatePeerConnection();
435 callee->AddAudioTrack("a");
436 auto callee_audio = callee->pc()->GetTransceivers()[0];
437 callee_audio->SetDirection(RtpTransceiverDirection::kSendOnly);
438
439 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
440 ASSERT_TRUE(
441 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
442
443 auto transceivers = caller->pc()->GetTransceivers();
444 ASSERT_EQ(1u, transceivers.size());
445 // Since the remote transceiver was set to sendonly, the negotiated direction
446 // in the answer would be sendonly which we apply as recvonly to the local
447 // transceiver.
448 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
449 transceivers[0]->current_direction());
450}
451
452// Tests for multiple round trips.
453
454// Test that setting a transceiver with the inactive direction does not stop it
455// on either the caller or the callee.
456TEST_F(PeerConnectionJsepTest, SettingTransceiverInactiveDoesNotStopIt) {
457 auto caller = CreatePeerConnection();
458 caller->AddAudioTrack("a");
459 auto callee = CreatePeerConnection();
460 callee->AddAudioTrack("a");
461 callee->pc()->GetTransceivers()[0]->SetDirection(
462 RtpTransceiverDirection::kInactive);
463
464 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
465 ASSERT_TRUE(
466 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
467
468 EXPECT_FALSE(caller->pc()->GetTransceivers()[0]->stopped());
469 EXPECT_FALSE(callee->pc()->GetTransceivers()[0]->stopped());
470}
471
472// Test that if a transceiver had been associated and later stopped, then a
473// media section is still generated for it and the media section is marked as
474// rejected.
475TEST_F(PeerConnectionJsepTest,
476 ReOfferMediaSectionForAssociatedStoppedTransceiverIsRejected) {
477 auto caller = CreatePeerConnection();
478 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
479 auto callee = CreatePeerConnection();
480
481 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
482 ASSERT_TRUE(
483 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
484
485 ASSERT_TRUE(transceiver->mid());
486 transceiver->Stop();
487
488 auto reoffer = caller->CreateOffer();
489 auto contents = reoffer->description()->contents();
490 ASSERT_EQ(1u, contents.size());
491 EXPECT_TRUE(contents[0].rejected);
492}
493
494// Test that stopping an associated transceiver on the caller side will stop the
495// corresponding transceiver on the remote side when the remote offer is
496// applied.
497TEST_F(PeerConnectionJsepTest,
498 StoppingTransceiverInOfferStopsTransceiverOnRemoteSide) {
499 auto caller = CreatePeerConnection();
500 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
501 auto callee = CreatePeerConnection();
502
503 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
504 ASSERT_TRUE(
505 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
506
507 transceiver->Stop();
508
509 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
510
511 auto transceivers = callee->pc()->GetTransceivers();
512 EXPECT_TRUE(transceivers[0]->stopped());
513 EXPECT_TRUE(transceivers[0]->mid());
514}
515
516// Test that CreateOffer will only generate a recycled media section if the
517// transceiver to be recycled has been seen stopped by the other side first.
518TEST_F(PeerConnectionJsepTest,
519 CreateOfferDoesNotRecycleMediaSectionIfFirstStopped) {
520 auto caller = CreatePeerConnection();
521 auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
522 auto callee = CreatePeerConnection();
523
524 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
525 ASSERT_TRUE(
526 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
527
528 auto second_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
529 first_transceiver->Stop();
530
531 auto reoffer = caller->CreateOffer();
532 auto contents = reoffer->description()->contents();
533 ASSERT_EQ(2u, contents.size());
534 EXPECT_TRUE(contents[0].rejected);
535 EXPECT_FALSE(contents[1].rejected);
536}
537
538// Test that the offer/answer and transceivers for both the caller and callee
539// side are generated/updated correctly when recycling an audio/video media
540// section as a media section of either the same or opposite type.
541class RecycleMediaSectionTest
542 : public PeerConnectionJsepTest,
543 public testing::WithParamInterface<
544 std::tuple<cricket::MediaType, cricket::MediaType>> {
545 protected:
546 RecycleMediaSectionTest() {
547 first_type_ = std::get<0>(GetParam());
548 second_type_ = std::get<1>(GetParam());
549 }
550
551 cricket::MediaType first_type_;
552 cricket::MediaType second_type_;
553};
554
555TEST_P(RecycleMediaSectionTest, VerifyOfferAnswerAndTransceivers) {
556 auto caller = CreatePeerConnection();
557 auto first_transceiver = caller->AddTransceiver(first_type_);
558 auto callee = CreatePeerConnection();
559
560 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
561
562 std::string first_mid = *first_transceiver->mid();
563 first_transceiver->Stop();
564
565 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
566
567 auto second_transceiver = caller->AddTransceiver(second_type_);
568
569 // The offer should reuse the previous media section but allocate a new MID
570 // and change the media type.
571 auto offer = caller->CreateOffer();
572 auto offer_contents = offer->description()->contents();
573 ASSERT_EQ(1u, offer_contents.size());
574 EXPECT_FALSE(offer_contents[0].rejected);
575 EXPECT_EQ(second_type_, offer_contents[0].media_description()->type());
576 std::string second_mid = offer_contents[0].name;
577 EXPECT_NE(first_mid, second_mid);
578
579 // Setting the local offer will dissociate the previous transceiver and set
580 // the MID for the new transceiver.
581 ASSERT_TRUE(
582 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
583 EXPECT_EQ(rtc::nullopt, first_transceiver->mid());
584 EXPECT_EQ(second_mid, second_transceiver->mid());
585
586 // Setting the remote offer will dissociate the previous transceiver and
587 // create a new transceiver for the media section.
588 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
589 auto callee_transceivers = callee->pc()->GetTransceivers();
590 ASSERT_EQ(2u, callee_transceivers.size());
591 EXPECT_EQ(rtc::nullopt, callee_transceivers[0]->mid());
592 EXPECT_EQ(first_type_, callee_transceivers[0]->receiver()->media_type());
593 EXPECT_EQ(second_mid, callee_transceivers[1]->mid());
594 EXPECT_EQ(second_type_, callee_transceivers[1]->receiver()->media_type());
595
596 // The answer should have only one media section for the new transceiver.
597 auto answer = callee->CreateAnswer();
598 auto answer_contents = answer->description()->contents();
599 ASSERT_EQ(1u, answer_contents.size());
600 EXPECT_FALSE(answer_contents[0].rejected);
601 EXPECT_EQ(second_mid, answer_contents[0].name);
602 EXPECT_EQ(second_type_, answer_contents[0].media_description()->type());
603
604 // Setting the local answer should succeed.
605 ASSERT_TRUE(
606 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
607
608 // Setting the remote answer should succeed.
609 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
610}
611
612// Test all combinations of audio and video as the first and second media type
613// for the media section. This is needed for full test coverage because
614// MediaSession has separate functions for processing audio and video media
615// sections.
616INSTANTIATE_TEST_CASE_P(
617 PeerConnectionJsepTest,
618 RecycleMediaSectionTest,
619 Combine(Values(cricket::MEDIA_TYPE_AUDIO, cricket::MEDIA_TYPE_VIDEO),
620 Values(cricket::MEDIA_TYPE_AUDIO, cricket::MEDIA_TYPE_VIDEO)));
621
622// Tests for MID properties.
623
624static void RenameSection(size_t mline_index,
625 const std::string& new_mid,
626 SessionDescriptionInterface* sdesc) {
627 cricket::SessionDescription* desc = sdesc->description();
628 std::string old_mid = desc->contents()[mline_index].name;
629 desc->contents()[mline_index].name = new_mid;
630 desc->transport_infos()[mline_index].content_name = new_mid;
631 const cricket::ContentGroup* bundle =
632 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
633 if (bundle) {
634 cricket::ContentGroup new_bundle = *bundle;
635 if (new_bundle.RemoveContentName(old_mid)) {
636 new_bundle.AddContentName(new_mid);
637 }
638 desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
639 desc->AddGroup(new_bundle);
640 }
641}
642
643// Test that two PeerConnections can have a successful offer/answer exchange if
644// the MIDs are changed from the defaults.
645TEST_F(PeerConnectionJsepTest, OfferAnswerWithChangedMids) {
646 constexpr char kFirstMid[] = "nondefaultmid";
647 constexpr char kSecondMid[] = "randommid";
648
649 auto caller = CreatePeerConnection();
650 caller->AddAudioTrack("a");
651 caller->AddAudioTrack("b");
652 auto callee = CreatePeerConnection();
653
654 auto offer = caller->CreateOffer();
655 RenameSection(0, kFirstMid, offer.get());
656 RenameSection(1, kSecondMid, offer.get());
657
658 ASSERT_TRUE(
659 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
660 auto caller_transceivers = caller->pc()->GetTransceivers();
661 EXPECT_EQ(kFirstMid, caller_transceivers[0]->mid());
662 EXPECT_EQ(kSecondMid, caller_transceivers[1]->mid());
663
664 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
665 auto callee_transceivers = callee->pc()->GetTransceivers();
666 EXPECT_EQ(kFirstMid, callee_transceivers[0]->mid());
667 EXPECT_EQ(kSecondMid, callee_transceivers[1]->mid());
668
669 auto answer = callee->CreateAnswer();
670 auto answer_contents = answer->description()->contents();
671 EXPECT_EQ(kFirstMid, answer_contents[0].name);
672 EXPECT_EQ(kSecondMid, answer_contents[1].name);
673
674 ASSERT_TRUE(
675 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
676 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
677}
678
679// Test that CreateOffer will generate a MID that is not already used if the
680// default it would have picked is already taken. This is tested by using a
681// third PeerConnection to determine what the default would be for the second
682// media section then setting that as the first media section's MID.
683TEST_F(PeerConnectionJsepTest, CreateOfferGeneratesUniqueMidIfAlreadyTaken) {
684 // First, find what the default MID is for the second media section.
685 auto pc = CreatePeerConnection();
686 pc->AddAudioTrack("a");
687 pc->AddAudioTrack("b");
688 auto default_offer = pc->CreateOffer();
689 std::string default_second_mid =
690 default_offer->description()->contents()[1].name;
691
692 // Now, do an offer/answer with one track which has the MID set to the default
693 // second MID.
694 auto caller = CreatePeerConnection();
695 caller->AddAudioTrack("a");
696 auto callee = CreatePeerConnection();
697
698 auto offer = caller->CreateOffer();
699 RenameSection(0, default_second_mid, offer.get());
700
701 ASSERT_TRUE(
702 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
703 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
704 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
705
706 // Add a second track and ensure that the MID is different.
707 caller->AddAudioTrack("b");
708
709 auto reoffer = caller->CreateOffer();
710 auto reoffer_contents = reoffer->description()->contents();
711 EXPECT_EQ(default_second_mid, reoffer_contents[0].name);
712 EXPECT_NE(reoffer_contents[0].name, reoffer_contents[1].name);
713}
714
715// Test that a reoffer initiated by the callee adds a new track to the caller.
716TEST_F(PeerConnectionJsepTest, CalleeDoesReoffer) {
717 auto caller = CreatePeerConnection();
718 caller->AddAudioTrack("a");
719 auto callee = CreatePeerConnection();
720 callee->AddAudioTrack("a");
721 callee->AddVideoTrack("v");
722
723 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
724
725 EXPECT_EQ(1u, caller->pc()->GetTransceivers().size());
726 EXPECT_EQ(2u, callee->pc()->GetTransceivers().size());
727
728 ASSERT_TRUE(callee->ExchangeOfferAnswerWith(caller.get()));
729
730 EXPECT_EQ(2u, caller->pc()->GetTransceivers().size());
731 EXPECT_EQ(2u, callee->pc()->GetTransceivers().size());
732}
733
734} // namespace webrtc