blob: 851d9257e046a6c1549639419498675a1242c78e [file] [log] [blame]
Steve Antondcc3c022017-12-23 00:02:541/*
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
Harald Alvestrandc24a2182022-02-23 13:44:5911#include <stddef.h>
Mirko Bonadei317a1f02019-09-17 15:06:1812
Harald Alvestrandc24a2182022-02-23 13:44:5913#include <algorithm>
14#include <map>
15#include <memory>
16#include <string>
17#include <tuple>
18#include <utility>
19#include <vector>
20
21#include "absl/strings/string_view.h"
22#include "absl/types/optional.h"
23#include "api/call/call_factory_interface.h"
Jonas Orelande62c2f22022-03-29 09:04:4824#include "api/field_trials_view.h"
Harald Alvestrandc24a2182022-02-23 13:44:5925#include "api/jsep.h"
26#include "api/media_stream_interface.h"
27#include "api/media_types.h"
28#include "api/peer_connection_interface.h"
29#include "api/rtc_error.h"
30#include "api/rtp_parameters.h"
31#include "api/rtp_receiver_interface.h"
32#include "api/rtp_sender_interface.h"
33#include "api/rtp_transceiver_direction.h"
34#include "api/rtp_transceiver_interface.h"
35#include "api/scoped_refptr.h"
Danil Chapovalov9da25bd2019-06-20 08:19:4236#include "api/task_queue/default_task_queue_factory.h"
Harald Alvestrandc24a2182022-02-23 13:44:5937#include "api/task_queue/task_queue_factory.h"
Erik Språngceb44952020-09-22 09:36:3538#include "api/transport/field_trial_based_config.h"
Harald Alvestrandc24a2182022-02-23 13:44:5939#include "api/transport/sctp_transport_factory_interface.h"
Harald Alvestrandc24a2182022-02-23 13:44:5940#include "media/base/media_engine.h"
41#include "media/base/stream_params.h"
Steve Anton10542f22019-01-11 17:11:0042#include "media/engine/webrtc_media_engine.h"
Danil Chapovalov9da25bd2019-06-20 08:19:4243#include "media/engine/webrtc_media_engine_defaults.h"
Harald Alvestrandc24a2182022-02-23 13:44:5944#include "modules/audio_device/include/audio_device.h"
45#include "p2p/base/p2p_constants.h"
46#include "p2p/base/port_allocator.h"
47#include "p2p/base/transport_info.h"
48#include "pc/channel_interface.h"
Steve Anton10542f22019-01-11 17:11:0049#include "pc/media_session.h"
Steve Anton10542f22019-01-11 17:11:0050#include "pc/peer_connection_wrapper.h"
51#include "pc/sdp_utils.h"
Harald Alvestrandc24a2182022-02-23 13:44:5952#include "pc/session_description.h"
53#include "pc/test/mock_peer_connection_observers.h"
54#include "rtc_base/rtc_certificate_generator.h"
55#include "rtc_base/thread.h"
56#include "test/gtest.h"
Steve Antondcc3c022017-12-23 00:02:5457#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 17:11:0058#include "pc/test/android_test_initializer.h"
Steve Antondcc3c022017-12-23 00:02:5459#endif
Steve Anton10542f22019-01-11 17:11:0060#include "pc/test/fake_audio_capture_module.h"
Steve Anton10542f22019-01-11 17:11:0061#include "rtc_base/virtual_socket_server.h"
Steve Antondcc3c022017-12-23 00:02:5462#include "test/gmock.h"
Per Kjellander2bca0082020-08-28 07:15:1563#include "test/pc/sctp/fake_sctp_transport.h"
Steve Antondcc3c022017-12-23 00:02:5464
65// This file contains tests that ensure the PeerConnection's implementation of
66// CreateOffer/CreateAnswer/SetLocalDescription/SetRemoteDescription conform
67// to the JavaScript Session Establishment Protocol (JSEP).
68// For now these semantics are only available when configuring the
69// PeerConnection with Unified Plan, but eventually that will be the default.
70
71namespace webrtc {
72
73using cricket::MediaContentDescription;
74using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
Steve Antondcc3c022017-12-23 00:02:5475using ::testing::Combine;
76using ::testing::ElementsAre;
Steve Antonef65ef12018-01-11 01:15:2077using ::testing::UnorderedElementsAre;
Jonas Olssona4d87372019-07-05 17:08:3378using ::testing::Values;
Steve Antondcc3c022017-12-23 00:02:5479
Per Kjellander2bca0082020-08-28 07:15:1580PeerConnectionFactoryDependencies CreatePeerConnectionFactoryDependencies() {
81 PeerConnectionFactoryDependencies dependencies;
82 dependencies.worker_thread = rtc::Thread::Current();
83 dependencies.network_thread = rtc::Thread::Current();
84 dependencies.signaling_thread = rtc::Thread::Current();
85 dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
Erik Språngceb44952020-09-22 09:36:3586 dependencies.trials = std::make_unique<FieldTrialBasedConfig>();
Per Kjellander2bca0082020-08-28 07:15:1587 cricket::MediaEngineDependencies media_deps;
88 media_deps.task_queue_factory = dependencies.task_queue_factory.get();
89 media_deps.adm = FakeAudioCaptureModule::Create();
Erik Språngceb44952020-09-22 09:36:3590 media_deps.trials = dependencies.trials.get();
Per Kjellander2bca0082020-08-28 07:15:1591 SetMediaEngineDefaults(&media_deps);
92 dependencies.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
93 dependencies.call_factory = CreateCallFactory();
94 dependencies.sctp_factory = std::make_unique<FakeSctpTransportFactory>();
95 return dependencies;
96}
Steve Antonfa2260d2017-12-29 00:38:2397
Steve Antondcc3c022017-12-23 00:02:5498class PeerConnectionJsepTest : public ::testing::Test {
99 protected:
100 typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
101
102 PeerConnectionJsepTest()
103 : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
104#ifdef WEBRTC_ANDROID
105 InitializeAndroidObjects();
106#endif
Steve Antondcc3c022017-12-23 00:02:54107 }
108
109 WrapperPtr CreatePeerConnection() {
110 RTCConfiguration config;
111 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
112 return CreatePeerConnection(config);
113 }
114
115 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
Per Kjellander2bca0082020-08-28 07:15:15116 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory =
117 CreateModularPeerConnectionFactory(
118 CreatePeerConnectionFactoryDependencies());
Mirko Bonadei317a1f02019-09-17 15:06:18119 auto observer = std::make_unique<MockPeerConnectionObserver>();
Florent Castelli72424402022-04-06 01:45:10120 auto result = pc_factory->CreatePeerConnectionOrError(
121 config, PeerConnectionDependencies(observer.get()));
122 if (!result.ok()) {
Steve Antondcc3c022017-12-23 00:02:54123 return nullptr;
124 }
125
Niels Möllerafb246b2022-04-20 12:26:50126 observer->SetPeerConnectionInterface(result.value().get());
Florent Castelli72424402022-04-06 01:45:10127 return std::make_unique<PeerConnectionWrapper>(
128 pc_factory, result.MoveValue(), std::move(observer));
Steve Antondcc3c022017-12-23 00:02:54129 }
130
131 std::unique_ptr<rtc::VirtualSocketServer> vss_;
132 rtc::AutoSocketServerThread main_;
Steve Antondcc3c022017-12-23 00:02:54133};
134
135// Tests for JSEP initial offer generation.
136
137// Test that an offer created by a PeerConnection with no transceivers generates
138// no media sections.
139TEST_F(PeerConnectionJsepTest, EmptyInitialOffer) {
140 auto caller = CreatePeerConnection();
Steve Antonfa2260d2017-12-29 00:38:23141
Steve Antondcc3c022017-12-23 00:02:54142 auto offer = caller->CreateOffer();
Steve Antonfa2260d2017-12-29 00:38:23143 ASSERT_EQ(0u, offer->description()->contents().size());
Steve Antondcc3c022017-12-23 00:02:54144}
145
146// Test that an initial offer with one audio track generates one audio media
147// section.
148TEST_F(PeerConnectionJsepTest, AudioOnlyInitialOffer) {
149 auto caller = CreatePeerConnection();
150 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
Steve Antondcc3c022017-12-23 00:02:54151
Steve Antonfa2260d2017-12-29 00:38:23152 auto offer = caller->CreateOffer();
Steve Antondcc3c022017-12-23 00:02:54153 auto contents = offer->description()->contents();
154 ASSERT_EQ(1u, contents.size());
155 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[0].media_description()->type());
156}
157
158// Test than an initial offer with one video track generates one video media
159// section
160TEST_F(PeerConnectionJsepTest, VideoOnlyInitialOffer) {
161 auto caller = CreatePeerConnection();
162 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
Steve Antondcc3c022017-12-23 00:02:54163
Steve Antonfa2260d2017-12-29 00:38:23164 auto offer = caller->CreateOffer();
Steve Antondcc3c022017-12-23 00:02:54165 auto contents = offer->description()->contents();
166 ASSERT_EQ(1u, contents.size());
167 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[0].media_description()->type());
168}
169
Steve Antonfa2260d2017-12-29 00:38:23170// Test that an initial offer with one data channel generates one data media
171// section.
172TEST_F(PeerConnectionJsepTest, DataOnlyInitialOffer) {
173 auto caller = CreatePeerConnection();
174 caller->CreateDataChannel("dc");
175
176 auto offer = caller->CreateOffer();
177 auto contents = offer->description()->contents();
178 ASSERT_EQ(1u, contents.size());
179 EXPECT_EQ(cricket::MEDIA_TYPE_DATA, contents[0].media_description()->type());
180}
181
182// Test that creating multiple data channels only results in one data section
183// generated in the offer.
184TEST_F(PeerConnectionJsepTest, MultipleDataChannelsCreateOnlyOneDataSection) {
185 auto caller = CreatePeerConnection();
186 caller->CreateDataChannel("first");
187 caller->CreateDataChannel("second");
188 caller->CreateDataChannel("third");
189
190 auto offer = caller->CreateOffer();
191 ASSERT_EQ(1u, offer->description()->contents().size());
192}
193
Steve Antondcc3c022017-12-23 00:02:54194// Test that multiple media sections in the initial offer are ordered in the
195// order the transceivers were added to the PeerConnection. This is required by
196// JSEP section 5.2.1.
197TEST_F(PeerConnectionJsepTest, MediaSectionsInInitialOfferOrderedCorrectly) {
198 auto caller = CreatePeerConnection();
199 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
200 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
201 RtpTransceiverInit init;
202 init.direction = RtpTransceiverDirection::kSendOnly;
203 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
Steve Antondcc3c022017-12-23 00:02:54204
Steve Antonfa2260d2017-12-29 00:38:23205 auto offer = caller->CreateOffer();
Steve Antondcc3c022017-12-23 00:02:54206 auto contents = offer->description()->contents();
207 ASSERT_EQ(3u, contents.size());
208
209 const MediaContentDescription* media_description1 =
210 contents[0].media_description();
211 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description1->type());
212 EXPECT_EQ(RtpTransceiverDirection::kSendRecv,
213 media_description1->direction());
214
215 const MediaContentDescription* media_description2 =
216 contents[1].media_description();
217 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, media_description2->type());
218 EXPECT_EQ(RtpTransceiverDirection::kSendRecv,
219 media_description2->direction());
220
221 const MediaContentDescription* media_description3 =
222 contents[2].media_description();
223 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description3->type());
224 EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
225 media_description3->direction());
226}
227
228// Test that media sections in the initial offer have different mids.
229TEST_F(PeerConnectionJsepTest, MediaSectionsInInitialOfferHaveDifferentMids) {
230 auto caller = CreatePeerConnection();
231 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
232 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
Steve Antonfa2260d2017-12-29 00:38:23233
Steve Antondcc3c022017-12-23 00:02:54234 auto offer = caller->CreateOffer();
Steve Antondcc3c022017-12-23 00:02:54235 auto contents = offer->description()->contents();
236 ASSERT_EQ(2u, contents.size());
237 EXPECT_NE(contents[0].name, contents[1].name);
238}
239
240TEST_F(PeerConnectionJsepTest,
241 StoppedTransceiverHasNoMediaSectionInInitialOffer) {
242 auto caller = CreatePeerConnection();
243 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
Harald Alvestrand6060df52020-08-11 07:54:02244 transceiver->StopInternal();
Steve Antondcc3c022017-12-23 00:02:54245
246 auto offer = caller->CreateOffer();
247 EXPECT_EQ(0u, offer->description()->contents().size());
248}
249
250// Tests for JSEP SetLocalDescription with a local offer.
251
252TEST_F(PeerConnectionJsepTest, SetLocalEmptyOfferCreatesNoTransceivers) {
253 auto caller = CreatePeerConnection();
Steve Antonfa2260d2017-12-29 00:38:23254
Steve Antondcc3c022017-12-23 00:02:54255 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
256
257 EXPECT_THAT(caller->pc()->GetTransceivers(), ElementsAre());
258 EXPECT_THAT(caller->pc()->GetSenders(), ElementsAre());
259 EXPECT_THAT(caller->pc()->GetReceivers(), ElementsAre());
260}
261
262TEST_F(PeerConnectionJsepTest, SetLocalOfferSetsTransceiverMid) {
263 auto caller = CreatePeerConnection();
264 auto audio_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
265 auto video_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
266
267 auto offer = caller->CreateOffer();
268 std::string audio_mid = offer->description()->contents()[0].name;
269 std::string video_mid = offer->description()->contents()[1].name;
270
271 ASSERT_TRUE(caller->SetLocalDescription(std::move(offer)));
272
273 EXPECT_EQ(audio_mid, audio_transceiver->mid());
274 EXPECT_EQ(video_mid, video_transceiver->mid());
275}
276
277// Tests for JSEP SetRemoteDescription with a remote offer.
278
279// Test that setting a remote offer with sendrecv audio and video creates two
280// transceivers, one for receiving audio and one for receiving video.
281TEST_F(PeerConnectionJsepTest, SetRemoteOfferCreatesTransceivers) {
282 auto caller = CreatePeerConnection();
283 auto caller_audio = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
284 auto caller_video = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
285 auto callee = CreatePeerConnection();
286
287 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
288
289 auto transceivers = callee->pc()->GetTransceivers();
290 ASSERT_EQ(2u, transceivers.size());
Steve Anton1bc97162018-06-25 21:04:01291
Steve Anton69470252018-02-09 19:43:08292 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, transceivers[0]->media_type());
Steve Antondcc3c022017-12-23 00:02:54293 EXPECT_EQ(caller_audio->mid(), transceivers[0]->mid());
294 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly, transceivers[0]->direction());
Steve Anton1bc97162018-06-25 21:04:01295 EXPECT_EQ(0u, transceivers[0]->sender()->stream_ids().size());
296
Steve Anton69470252018-02-09 19:43:08297 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, transceivers[1]->media_type());
Steve Antondcc3c022017-12-23 00:02:54298 EXPECT_EQ(caller_video->mid(), transceivers[1]->mid());
299 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly, transceivers[1]->direction());
Steve Anton1bc97162018-06-25 21:04:01300 EXPECT_EQ(0u, transceivers[1]->sender()->stream_ids().size());
Steve Antondcc3c022017-12-23 00:02:54301}
302
303// Test that setting a remote offer with an audio track will reuse the
304// transceiver created for a local audio track added by AddTrack.
305// This is specified in JSEP section 5.10 (Applying a Remote Description). The
306// intent is to preserve backwards compatibility with clients who only use the
307// AddTrack API.
308TEST_F(PeerConnectionJsepTest, SetRemoteOfferReusesTransceiverFromAddTrack) {
309 auto caller = CreatePeerConnection();
310 caller->AddAudioTrack("a");
311 auto caller_audio = caller->pc()->GetTransceivers()[0];
312 auto callee = CreatePeerConnection();
313 callee->AddAudioTrack("a");
314
315 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
316
317 auto transceivers = callee->pc()->GetTransceivers();
318 ASSERT_EQ(1u, transceivers.size());
319 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
320 transceivers[0]->receiver()->track()->kind());
321 EXPECT_EQ(caller_audio->mid(), transceivers[0]->mid());
322}
323
324// Test that setting a remote offer with an audio track marked sendonly will not
325// reuse a transceiver created by AddTrack. JSEP only allows the transceiver to
326// be reused if the offer direction is sendrecv or recvonly.
327TEST_F(PeerConnectionJsepTest,
328 SetRemoteOfferDoesNotReuseTransceiverIfDirectionSendOnly) {
329 auto caller = CreatePeerConnection();
330 caller->AddAudioTrack("a");
331 auto caller_audio = caller->pc()->GetTransceivers()[0];
Harald Alvestrand6060df52020-08-11 07:54:02332 caller_audio->SetDirectionWithError(RtpTransceiverDirection::kSendOnly);
Steve Antondcc3c022017-12-23 00:02:54333 auto callee = CreatePeerConnection();
334 callee->AddAudioTrack("a");
335
336 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
337
338 auto transceivers = callee->pc()->GetTransceivers();
339 ASSERT_EQ(2u, transceivers.size());
Danil Chapovalov66cadcc2018-06-19 14:47:43340 EXPECT_EQ(absl::nullopt, transceivers[0]->mid());
Steve Antondcc3c022017-12-23 00:02:54341 EXPECT_EQ(caller_audio->mid(), transceivers[1]->mid());
342}
343
344// Test that setting a remote offer with an audio track will not reuse a
345// transceiver added by AddTransceiver. The logic for reusing a transceiver is
346// specific to those added by AddTrack and is tested above.
347TEST_F(PeerConnectionJsepTest,
348 SetRemoteOfferDoesNotReuseTransceiverFromAddTransceiver) {
349 auto caller = CreatePeerConnection();
350 caller->AddAudioTrack("a");
351 auto callee = CreatePeerConnection();
352 auto transceiver = callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
353
354 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
355
356 auto transceivers = callee->pc()->GetTransceivers();
357 ASSERT_EQ(2u, transceivers.size());
Danil Chapovalov66cadcc2018-06-19 14:47:43358 EXPECT_EQ(absl::nullopt, transceivers[0]->mid());
Steve Antondcc3c022017-12-23 00:02:54359 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
360 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
361 transceivers[1]->receiver()->track()->kind());
362}
363
364// Test that setting a remote offer with an audio track will not reuse a
365// transceiver created for a local video track added by AddTrack.
366TEST_F(PeerConnectionJsepTest,
367 SetRemoteOfferDoesNotReuseTransceiverOfWrongType) {
368 auto caller = CreatePeerConnection();
369 caller->AddAudioTrack("a");
370 auto callee = CreatePeerConnection();
371 auto video_sender = callee->AddVideoTrack("v");
372
373 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
374
375 auto transceivers = callee->pc()->GetTransceivers();
376 ASSERT_EQ(2u, transceivers.size());
Danil Chapovalov66cadcc2018-06-19 14:47:43377 EXPECT_EQ(absl::nullopt, transceivers[0]->mid());
Steve Antondcc3c022017-12-23 00:02:54378 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
379 EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
380 transceivers[1]->receiver()->track()->kind());
381}
382
383// Test that setting a remote offer with an audio track will not reuse a
384// stopped transceiver.
385TEST_F(PeerConnectionJsepTest, SetRemoteOfferDoesNotReuseStoppedTransceiver) {
386 auto caller = CreatePeerConnection();
387 caller->AddAudioTrack("a");
388 auto callee = CreatePeerConnection();
389 callee->AddAudioTrack("a");
Harald Alvestrand6060df52020-08-11 07:54:02390 callee->pc()->GetTransceivers()[0]->StopInternal();
Steve Antondcc3c022017-12-23 00:02:54391
392 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
393
394 auto transceivers = callee->pc()->GetTransceivers();
Harald Alvestrand936f1af2020-09-22 07:41:50395 ASSERT_EQ(2u, transceivers.size());
396 // The stopped transceiver is removed in SetLocalDescription(answer)
397 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
398 transceivers = callee->pc()->GetTransceivers();
Harald Alvestrand6060df52020-08-11 07:54:02399 ASSERT_EQ(1u, transceivers.size());
400 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[0]->mid());
401 EXPECT_FALSE(transceivers[0]->stopped());
Steve Antondcc3c022017-12-23 00:02:54402}
403
404// Test that audio and video transceivers created on the remote side with
405// AddTrack will all be reused if there is the same number of audio/video tracks
406// in the remote offer. Additionally, this tests that transceivers are
407// successfully matched even if they are in a different order on the remote
408// side.
409TEST_F(PeerConnectionJsepTest, SetRemoteOfferReusesTransceiversOfBothTypes) {
410 auto caller = CreatePeerConnection();
411 caller->AddVideoTrack("v");
412 caller->AddAudioTrack("a");
413 auto callee = CreatePeerConnection();
414 callee->AddAudioTrack("a");
415 callee->AddVideoTrack("v");
416
417 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
418
419 auto caller_transceivers = caller->pc()->GetTransceivers();
420 auto callee_transceivers = callee->pc()->GetTransceivers();
421 ASSERT_EQ(2u, callee_transceivers.size());
422 EXPECT_EQ(caller_transceivers[0]->mid(), callee_transceivers[1]->mid());
423 EXPECT_EQ(caller_transceivers[1]->mid(), callee_transceivers[0]->mid());
424}
425
426// Tests for JSEP initial CreateAnswer.
427
428// Test that the answer to a remote offer creates media sections for each
429// offered media in the same order and with the same mids.
430TEST_F(PeerConnectionJsepTest, CreateAnswerHasSameMidsAsOffer) {
431 auto caller = CreatePeerConnection();
432 auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
433 auto second_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
434 auto third_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
Steve Antonfa2260d2017-12-29 00:38:23435 caller->CreateDataChannel("dc");
Steve Antondcc3c022017-12-23 00:02:54436 auto callee = CreatePeerConnection();
437
Steve Antonfa2260d2017-12-29 00:38:23438 auto offer = caller->CreateOffer();
439 const auto* offer_data = cricket::GetFirstDataContent(offer->description());
440 ASSERT_TRUE(
441 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
442 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
Steve Antondcc3c022017-12-23 00:02:54443
444 auto answer = callee->CreateAnswer();
445 auto contents = answer->description()->contents();
Steve Antonfa2260d2017-12-29 00:38:23446 ASSERT_EQ(4u, contents.size());
Steve Antondcc3c022017-12-23 00:02:54447 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[0].media_description()->type());
Steve Antonfa2260d2017-12-29 00:38:23448 EXPECT_EQ(first_transceiver->mid(), contents[0].name);
Steve Antondcc3c022017-12-23 00:02:54449 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[1].media_description()->type());
Steve Antonfa2260d2017-12-29 00:38:23450 EXPECT_EQ(second_transceiver->mid(), contents[1].name);
Steve Antondcc3c022017-12-23 00:02:54451 EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[2].media_description()->type());
Steve Antonfa2260d2017-12-29 00:38:23452 EXPECT_EQ(third_transceiver->mid(), contents[2].name);
453 EXPECT_EQ(cricket::MEDIA_TYPE_DATA, contents[3].media_description()->type());
454 EXPECT_EQ(offer_data->name, contents[3].name);
Steve Antondcc3c022017-12-23 00:02:54455}
456
457// Test that an answering media section is marked as rejected if the underlying
458// transceiver has been stopped.
459TEST_F(PeerConnectionJsepTest, CreateAnswerRejectsStoppedTransceiver) {
460 auto caller = CreatePeerConnection();
461 caller->AddAudioTrack("a");
462 auto callee = CreatePeerConnection();
463
464 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
465
Harald Alvestrand6060df52020-08-11 07:54:02466 callee->pc()->GetTransceivers()[0]->StopInternal();
Steve Antondcc3c022017-12-23 00:02:54467
468 auto answer = callee->CreateAnswer();
469 auto contents = answer->description()->contents();
470 ASSERT_EQ(1u, contents.size());
471 EXPECT_TRUE(contents[0].rejected);
472}
473
474// Test that CreateAnswer will generate media sections which will only send or
475// receive if the offer indicates it can do the reciprocating direction.
476// The full matrix is tested more extensively in MediaSession.
477TEST_F(PeerConnectionJsepTest, CreateAnswerNegotiatesDirection) {
478 auto caller = CreatePeerConnection();
479 RtpTransceiverInit init;
480 init.direction = RtpTransceiverDirection::kSendOnly;
481 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
482 auto callee = CreatePeerConnection();
483 callee->AddAudioTrack("a");
484
485 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
486
487 auto answer = callee->CreateAnswer();
488 auto contents = answer->description()->contents();
489 ASSERT_EQ(1u, contents.size());
490 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
491 contents[0].media_description()->direction());
492}
493
494// Tests for JSEP SetLocalDescription with a local answer.
495// Note that these test only the additional behaviors not covered by
496// SetLocalDescription with a local offer.
497
498// Test that SetLocalDescription with an answer sets the current_direction
499// property of the transceivers mentioned in the session description.
500TEST_F(PeerConnectionJsepTest, SetLocalAnswerUpdatesCurrentDirection) {
501 auto caller = CreatePeerConnection();
502 auto caller_audio = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
Harald Alvestrand6060df52020-08-11 07:54:02503 caller_audio->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly);
Steve Antondcc3c022017-12-23 00:02:54504 auto callee = CreatePeerConnection();
505 callee->AddAudioTrack("a");
506
507 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
508 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
509
510 auto transceivers = callee->pc()->GetTransceivers();
511 ASSERT_EQ(1u, transceivers.size());
512 // Since the offer was recvonly and the transceiver direction is sendrecv,
513 // the negotiated direction will be sendonly.
514 EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
515 transceivers[0]->current_direction());
516}
517
518// Tests for JSEP SetRemoteDescription with a remote answer.
519// Note that these test only the additional behaviors not covered by
520// SetRemoteDescription with a remote offer.
521
522TEST_F(PeerConnectionJsepTest, SetRemoteAnswerUpdatesCurrentDirection) {
523 auto caller = CreatePeerConnection();
524 caller->AddAudioTrack("a");
525 auto callee = CreatePeerConnection();
526 callee->AddAudioTrack("a");
527 auto callee_audio = callee->pc()->GetTransceivers()[0];
Harald Alvestrand6060df52020-08-11 07:54:02528 callee_audio->SetDirectionWithError(RtpTransceiverDirection::kSendOnly);
Steve Antondcc3c022017-12-23 00:02:54529
530 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
531 ASSERT_TRUE(
532 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
533
534 auto transceivers = caller->pc()->GetTransceivers();
535 ASSERT_EQ(1u, transceivers.size());
536 // Since the remote transceiver was set to sendonly, the negotiated direction
537 // in the answer would be sendonly which we apply as recvonly to the local
538 // transceiver.
539 EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
540 transceivers[0]->current_direction());
541}
542
Stefan Mitic3aa99372022-02-03 12:26:22543TEST_F(PeerConnectionJsepTest,
544 ChangeDirectionFromRecvOnlyToSendRecvDoesNotBreakVideoNegotiation) {
545 auto caller = CreatePeerConnection();
546 auto caller_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
547 auto callee = CreatePeerConnection();
548 caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly);
549
550 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
551 ASSERT_TRUE(
552 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
553
554 caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
555
556 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
557 ASSERT_TRUE(
558 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
559}
560
561TEST_F(PeerConnectionJsepTest,
562 ChangeDirectionFromRecvOnlyToSendRecvDoesNotBreakAudioNegotiation) {
563 auto caller = CreatePeerConnection();
564 auto caller_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
565 auto callee = CreatePeerConnection();
566 caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly);
567
568 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
569 ASSERT_TRUE(
570 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
571
572 caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
573
574 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
575 ASSERT_TRUE(
576 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
577}
578
Steve Antondcc3c022017-12-23 00:02:54579// Tests for multiple round trips.
580
581// Test that setting a transceiver with the inactive direction does not stop it
582// on either the caller or the callee.
583TEST_F(PeerConnectionJsepTest, SettingTransceiverInactiveDoesNotStopIt) {
584 auto caller = CreatePeerConnection();
585 caller->AddAudioTrack("a");
586 auto callee = CreatePeerConnection();
587 callee->AddAudioTrack("a");
Harald Alvestrand6060df52020-08-11 07:54:02588 callee->pc()->GetTransceivers()[0]->SetDirectionWithError(
Steve Antondcc3c022017-12-23 00:02:54589 RtpTransceiverDirection::kInactive);
590
591 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
592 ASSERT_TRUE(
593 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
594
595 EXPECT_FALSE(caller->pc()->GetTransceivers()[0]->stopped());
596 EXPECT_FALSE(callee->pc()->GetTransceivers()[0]->stopped());
597}
598
599// Test that if a transceiver had been associated and later stopped, then a
600// media section is still generated for it and the media section is marked as
601// rejected.
602TEST_F(PeerConnectionJsepTest,
603 ReOfferMediaSectionForAssociatedStoppedTransceiverIsRejected) {
604 auto caller = CreatePeerConnection();
605 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
606 auto callee = CreatePeerConnection();
607
608 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
609 ASSERT_TRUE(
610 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
611
612 ASSERT_TRUE(transceiver->mid());
Harald Alvestrand6060df52020-08-11 07:54:02613 transceiver->StopInternal();
Steve Antondcc3c022017-12-23 00:02:54614
615 auto reoffer = caller->CreateOffer();
616 auto contents = reoffer->description()->contents();
617 ASSERT_EQ(1u, contents.size());
618 EXPECT_TRUE(contents[0].rejected);
619}
620
621// Test that stopping an associated transceiver on the caller side will stop the
622// corresponding transceiver on the remote side when the remote offer is
623// applied.
624TEST_F(PeerConnectionJsepTest,
625 StoppingTransceiverInOfferStopsTransceiverOnRemoteSide) {
626 auto caller = CreatePeerConnection();
627 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
628 auto callee = CreatePeerConnection();
629
630 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
631 ASSERT_TRUE(
632 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
633
Harald Alvestrand6060df52020-08-11 07:54:02634 transceiver->StopInternal();
Steve Antondcc3c022017-12-23 00:02:54635
636 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
637
638 auto transceivers = callee->pc()->GetTransceivers();
Harald Alvestrand936f1af2020-09-22 07:41:50639 EXPECT_EQ(1u, transceivers.size());
640 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
641 transceivers = callee->pc()->GetTransceivers();
Harald Alvestrand6060df52020-08-11 07:54:02642 EXPECT_EQ(0u, transceivers.size());
Steve Antondcc3c022017-12-23 00:02:54643}
644
645// Test that CreateOffer will only generate a recycled media section if the
646// transceiver to be recycled has been seen stopped by the other side first.
647TEST_F(PeerConnectionJsepTest,
648 CreateOfferDoesNotRecycleMediaSectionIfFirstStopped) {
649 auto caller = CreatePeerConnection();
650 auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
651 auto callee = CreatePeerConnection();
652
653 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
654 ASSERT_TRUE(
655 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
656
657 auto second_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
Harald Alvestrand6060df52020-08-11 07:54:02658 first_transceiver->StopInternal();
Steve Antondcc3c022017-12-23 00:02:54659
660 auto reoffer = caller->CreateOffer();
661 auto contents = reoffer->description()->contents();
662 ASSERT_EQ(2u, contents.size());
663 EXPECT_TRUE(contents[0].rejected);
664 EXPECT_FALSE(contents[1].rejected);
665}
666
Seth Hampsonae8a90a2018-02-13 23:33:48667// Test that the offer/answer and the transceivers are correctly generated and
668// updated when the media section is recycled after the callee stops a
669// transceiver and sends an answer with a 0 port.
670TEST_F(PeerConnectionJsepTest,
671 RecycleMediaSectionWhenStoppingTransceiverOnAnswerer) {
672 auto caller = CreatePeerConnection();
673 auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
674 auto callee = CreatePeerConnection();
675
676 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
Harald Alvestrand936f1af2020-09-22 07:41:50677 std::string first_mid = *first_transceiver->mid();
Harald Alvestrand6060df52020-08-11 07:54:02678 ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
679 callee->pc()->GetTransceivers()[0]->StopInternal();
Harald Alvestrand936f1af2020-09-22 07:41:50680 ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
Seth Hampsonae8a90a2018-02-13 23:33:48681 ASSERT_TRUE(
682 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
683 EXPECT_TRUE(first_transceiver->stopped());
Harald Alvestrand936f1af2020-09-22 07:41:50684 // First transceivers are dissociated on caller side.
685 ASSERT_EQ(absl::nullopt, first_transceiver->mid());
Harald Alvestrand6060df52020-08-11 07:54:02686 // They are disassociated on callee side.
687 ASSERT_EQ(0u, callee->pc()->GetTransceivers().size());
Seth Hampsonae8a90a2018-02-13 23:33:48688
689 // New offer exchange with new transceivers that recycles the m section
690 // correctly.
691 caller->AddAudioTrack("audio2");
692 callee->AddAudioTrack("audio2");
693 auto offer = caller->CreateOffer();
694 auto offer_contents = offer->description()->contents();
695 std::string second_mid = offer_contents[0].name;
696 ASSERT_EQ(1u, offer_contents.size());
697 EXPECT_FALSE(offer_contents[0].rejected);
698 EXPECT_NE(first_mid, second_mid);
699
700 // Setting the offer on each side will dissociate the first transceivers and
701 // associate the new transceivers.
702 ASSERT_TRUE(
703 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
Danil Chapovalov66cadcc2018-06-19 14:47:43704 EXPECT_EQ(absl::nullopt, first_transceiver->mid());
Harald Alvestrand6060df52020-08-11 07:54:02705 ASSERT_EQ(1u, caller->pc()->GetTransceivers().size());
706 EXPECT_EQ(second_mid, caller->pc()->GetTransceivers()[0]->mid());
Seth Hampsonae8a90a2018-02-13 23:33:48707 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
Harald Alvestrand6060df52020-08-11 07:54:02708 ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
709 EXPECT_EQ(second_mid, callee->pc()->GetTransceivers()[0]->mid());
Seth Hampsonae8a90a2018-02-13 23:33:48710
711 // The new answer should also recycle the m section correctly.
712 auto answer = callee->CreateAnswer();
713 auto answer_contents = answer->description()->contents();
714 ASSERT_EQ(1u, answer_contents.size());
715 EXPECT_FALSE(answer_contents[0].rejected);
716 EXPECT_EQ(second_mid, answer_contents[0].name);
717
718 // Finishing the negotiation shouldn't add or dissociate any transceivers.
719 ASSERT_TRUE(
720 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
721 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
722 auto caller_transceivers = caller->pc()->GetTransceivers();
Harald Alvestrand6060df52020-08-11 07:54:02723 ASSERT_EQ(1u, caller_transceivers.size());
724 EXPECT_EQ(second_mid, caller_transceivers[0]->mid());
Seth Hampsonae8a90a2018-02-13 23:33:48725 auto callee_transceivers = callee->pc()->GetTransceivers();
Harald Alvestrand6060df52020-08-11 07:54:02726 ASSERT_EQ(1u, callee_transceivers.size());
727 EXPECT_EQ(second_mid, callee_transceivers[0]->mid());
Seth Hampsonae8a90a2018-02-13 23:33:48728}
729
730// Test that creating/setting a local offer that recycles an m= section is
731// idempotent.
732TEST_F(PeerConnectionJsepTest, CreateOfferRecyclesWhenOfferingTwice) {
733 // Do a negotiation with a port 0 for the media section.
734 auto caller = CreatePeerConnection();
735 auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
736 auto callee = CreatePeerConnection();
737 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
Harald Alvestrand6060df52020-08-11 07:54:02738 first_transceiver->StopInternal();
Seth Hampsonae8a90a2018-02-13 23:33:48739 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
740 caller->AddAudioTrack("audio2");
741
742 // Create a new offer that recycles the media section and set it as a local
743 // description.
744 auto offer = caller->CreateOffer();
745 auto offer_contents = offer->description()->contents();
746 ASSERT_EQ(1u, offer_contents.size());
747 EXPECT_FALSE(offer_contents[0].rejected);
748 ASSERT_TRUE(caller->SetLocalDescription(std::move(offer)));
Harald Alvestrand6060df52020-08-11 07:54:02749 ASSERT_EQ(1u, caller->pc()->GetTransceivers().size());
750 EXPECT_FALSE(caller->pc()->GetTransceivers()[0]->stopped());
Seth Hampsonae8a90a2018-02-13 23:33:48751 std::string second_mid = offer_contents[0].name;
752
753 // Create another new offer and set the local description again without the
754 // rest of any negotation ocurring.
755 auto second_offer = caller->CreateOffer();
756 auto second_offer_contents = second_offer->description()->contents();
757 ASSERT_EQ(1u, second_offer_contents.size());
758 EXPECT_FALSE(second_offer_contents[0].rejected);
759 // The mid shouldn't change.
760 EXPECT_EQ(second_mid, second_offer_contents[0].name);
761
762 ASSERT_TRUE(caller->SetLocalDescription(std::move(second_offer)));
763 // Make sure that the caller's transceivers are associated correctly.
764 auto caller_transceivers = caller->pc()->GetTransceivers();
Harald Alvestrand6060df52020-08-11 07:54:02765 ASSERT_EQ(1u, caller_transceivers.size());
766 EXPECT_EQ(second_mid, caller_transceivers[0]->mid());
767 EXPECT_FALSE(caller_transceivers[0]->stopped());
Seth Hampsonae8a90a2018-02-13 23:33:48768}
769
Steve Antondcc3c022017-12-23 00:02:54770// Test that the offer/answer and transceivers for both the caller and callee
771// side are generated/updated correctly when recycling an audio/video media
772// section as a media section of either the same or opposite type.
Steve Anton5c72e712018-12-10 22:25:30773// Correct recycling works as follows:
774// - The m= section is re-offered with a new MID value and the new media type.
775// - The previously-associated transceiver is dissociated when the new offer is
776// set as a local description on the offerer or as a remote description on
777// the answerer.
778// - The new transceiver is associated with the new MID value.
Steve Antondcc3c022017-12-23 00:02:54779class RecycleMediaSectionTest
780 : public PeerConnectionJsepTest,
Mirko Bonadei6a489f22019-04-09 13:11:12781 public ::testing::WithParamInterface<
Steve Antondcc3c022017-12-23 00:02:54782 std::tuple<cricket::MediaType, cricket::MediaType>> {
783 protected:
784 RecycleMediaSectionTest() {
785 first_type_ = std::get<0>(GetParam());
786 second_type_ = std::get<1>(GetParam());
787 }
788
789 cricket::MediaType first_type_;
790 cricket::MediaType second_type_;
791};
792
Steve Anton5c72e712018-12-10 22:25:30793// Test that recycling works properly when a new transceiver recycles an m=
794// section that was rejected in both the current local and remote descriptions.
795TEST_P(RecycleMediaSectionTest, CurrentLocalAndCurrentRemoteRejected) {
Steve Antondcc3c022017-12-23 00:02:54796 auto caller = CreatePeerConnection();
797 auto first_transceiver = caller->AddTransceiver(first_type_);
798 auto callee = CreatePeerConnection();
799
800 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
801
802 std::string first_mid = *first_transceiver->mid();
Harald Alvestrand6060df52020-08-11 07:54:02803 first_transceiver->StopInternal();
Steve Antondcc3c022017-12-23 00:02:54804
805 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
806
807 auto second_transceiver = caller->AddTransceiver(second_type_);
808
809 // The offer should reuse the previous media section but allocate a new MID
810 // and change the media type.
811 auto offer = caller->CreateOffer();
812 auto offer_contents = offer->description()->contents();
813 ASSERT_EQ(1u, offer_contents.size());
814 EXPECT_FALSE(offer_contents[0].rejected);
815 EXPECT_EQ(second_type_, offer_contents[0].media_description()->type());
816 std::string second_mid = offer_contents[0].name;
817 EXPECT_NE(first_mid, second_mid);
818
819 // Setting the local offer will dissociate the previous transceiver and set
820 // the MID for the new transceiver.
821 ASSERT_TRUE(
822 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
Danil Chapovalov66cadcc2018-06-19 14:47:43823 EXPECT_EQ(absl::nullopt, first_transceiver->mid());
Steve Antondcc3c022017-12-23 00:02:54824 EXPECT_EQ(second_mid, second_transceiver->mid());
825
826 // Setting the remote offer will dissociate the previous transceiver and
827 // create a new transceiver for the media section.
828 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
829 auto callee_transceivers = callee->pc()->GetTransceivers();
Harald Alvestrand6060df52020-08-11 07:54:02830 ASSERT_EQ(1u, callee_transceivers.size());
831 EXPECT_EQ(second_mid, callee_transceivers[0]->mid());
832 EXPECT_EQ(second_type_, callee_transceivers[0]->media_type());
Steve Antondcc3c022017-12-23 00:02:54833
834 // The answer should have only one media section for the new transceiver.
835 auto answer = callee->CreateAnswer();
836 auto answer_contents = answer->description()->contents();
837 ASSERT_EQ(1u, answer_contents.size());
838 EXPECT_FALSE(answer_contents[0].rejected);
839 EXPECT_EQ(second_mid, answer_contents[0].name);
840 EXPECT_EQ(second_type_, answer_contents[0].media_description()->type());
841
842 // Setting the local answer should succeed.
843 ASSERT_TRUE(
844 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
845
Seth Hampsonae8a90a2018-02-13 23:33:48846 // Setting the remote answer should succeed and not create any new
847 // transceivers.
Steve Antondcc3c022017-12-23 00:02:54848 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
Harald Alvestrand6060df52020-08-11 07:54:02849 ASSERT_EQ(1u, caller->pc()->GetTransceivers().size());
850 ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
Steve Antondcc3c022017-12-23 00:02:54851}
852
Steve Anton5c72e712018-12-10 22:25:30853// Test that recycling works properly when a new transceiver recycles an m=
854// section that was rejected in only the current remote description.
855TEST_P(RecycleMediaSectionTest, CurrentRemoteOnlyRejected) {
856 auto caller = CreatePeerConnection();
857 auto caller_first_transceiver = caller->AddTransceiver(first_type_);
858 auto callee = CreatePeerConnection();
859
860 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
861
862 std::string first_mid = *caller_first_transceiver->mid();
863 ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
864 auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
Harald Alvestrand6060df52020-08-11 07:54:02865 callee_first_transceiver->StopInternal();
Steve Anton5c72e712018-12-10 22:25:30866
867 // The answer will have a rejected m= section.
868 ASSERT_TRUE(
869 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
870
871 // The offer should reuse the previous media section but allocate a new MID
872 // and change the media type.
873 auto caller_second_transceiver = caller->AddTransceiver(second_type_);
874 auto offer = caller->CreateOffer();
875 const auto& offer_contents = offer->description()->contents();
876 ASSERT_EQ(1u, offer_contents.size());
877 EXPECT_FALSE(offer_contents[0].rejected);
878 EXPECT_EQ(second_type_, offer_contents[0].media_description()->type());
879 std::string second_mid = offer_contents[0].name;
880 EXPECT_NE(first_mid, second_mid);
881
882 // Setting the local offer will dissociate the previous transceiver and set
883 // the MID for the new transceiver.
884 ASSERT_TRUE(
885 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
886 EXPECT_EQ(absl::nullopt, caller_first_transceiver->mid());
887 EXPECT_EQ(second_mid, caller_second_transceiver->mid());
888
889 // Setting the remote offer will dissociate the previous transceiver and
890 // create a new transceiver for the media section.
891 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
892 auto callee_transceivers = callee->pc()->GetTransceivers();
Harald Alvestrand6060df52020-08-11 07:54:02893 ASSERT_EQ(1u, callee_transceivers.size());
894 EXPECT_EQ(second_mid, callee_transceivers[0]->mid());
895 EXPECT_EQ(second_type_, callee_transceivers[0]->media_type());
Steve Anton5c72e712018-12-10 22:25:30896
897 // The answer should have only one media section for the new transceiver.
898 auto answer = callee->CreateAnswer();
899 auto answer_contents = answer->description()->contents();
900 ASSERT_EQ(1u, answer_contents.size());
901 EXPECT_FALSE(answer_contents[0].rejected);
902 EXPECT_EQ(second_mid, answer_contents[0].name);
903 EXPECT_EQ(second_type_, answer_contents[0].media_description()->type());
904
905 // Setting the local answer should succeed.
906 ASSERT_TRUE(
907 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
908
909 // Setting the remote answer should succeed and not create any new
910 // transceivers.
911 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
Harald Alvestrand6060df52020-08-11 07:54:02912 ASSERT_EQ(1u, caller->pc()->GetTransceivers().size());
913 ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
Steve Anton5c72e712018-12-10 22:25:30914}
915
916// Test that recycling works properly when a new transceiver recycles an m=
917// section that was rejected only in the current local description.
918TEST_P(RecycleMediaSectionTest, CurrentLocalOnlyRejected) {
919 auto caller = CreatePeerConnection();
920 auto caller_first_transceiver = caller->AddTransceiver(first_type_);
921 auto callee = CreatePeerConnection();
922
923 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
924
925 std::string first_mid = *caller_first_transceiver->mid();
926 ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
927 auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
Harald Alvestrand6060df52020-08-11 07:54:02928 callee_first_transceiver->StopInternal();
Steve Anton5c72e712018-12-10 22:25:30929
930 // The answer will have a rejected m= section.
931 ASSERT_TRUE(
932 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
933
934 // The offer should reuse the previous media section but allocate a new MID
935 // and change the media type.
936 auto callee_second_transceiver = callee->AddTransceiver(second_type_);
937 auto offer = callee->CreateOffer();
938 const auto& offer_contents = offer->description()->contents();
939 ASSERT_EQ(1u, offer_contents.size());
940 EXPECT_FALSE(offer_contents[0].rejected);
941 EXPECT_EQ(second_type_, offer_contents[0].media_description()->type());
942 std::string second_mid = offer_contents[0].name;
943 EXPECT_NE(first_mid, second_mid);
944
945 // Setting the local offer will dissociate the previous transceiver and set
946 // the MID for the new transceiver.
947 ASSERT_TRUE(
948 callee->SetLocalDescription(CloneSessionDescription(offer.get())));
949 EXPECT_EQ(absl::nullopt, callee_first_transceiver->mid());
950 EXPECT_EQ(second_mid, callee_second_transceiver->mid());
951
952 // Setting the remote offer will dissociate the previous transceiver and
953 // create a new transceiver for the media section.
954 ASSERT_TRUE(caller->SetRemoteDescription(std::move(offer)));
955 auto caller_transceivers = caller->pc()->GetTransceivers();
Harald Alvestrand6060df52020-08-11 07:54:02956 ASSERT_EQ(1u, caller_transceivers.size());
957 EXPECT_EQ(second_mid, caller_transceivers[0]->mid());
958 EXPECT_EQ(second_type_, caller_transceivers[0]->media_type());
Steve Anton5c72e712018-12-10 22:25:30959
960 // The answer should have only one media section for the new transceiver.
961 auto answer = caller->CreateAnswer();
962 auto answer_contents = answer->description()->contents();
963 ASSERT_EQ(1u, answer_contents.size());
964 EXPECT_FALSE(answer_contents[0].rejected);
965 EXPECT_EQ(second_mid, answer_contents[0].name);
966 EXPECT_EQ(second_type_, answer_contents[0].media_description()->type());
967
968 // Setting the local answer should succeed.
969 ASSERT_TRUE(
970 caller->SetLocalDescription(CloneSessionDescription(answer.get())));
971
972 // Setting the remote answer should succeed and not create any new
973 // transceivers.
974 ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
Harald Alvestrand6060df52020-08-11 07:54:02975 ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
976 ASSERT_EQ(1u, caller->pc()->GetTransceivers().size());
Steve Anton5c72e712018-12-10 22:25:30977}
978
979// Test that a m= section is *not* recycled if the media section is only
980// rejected in the pending local description and there is no current remote
981// description.
982TEST_P(RecycleMediaSectionTest, PendingLocalRejectedAndNoRemote) {
983 auto caller = CreatePeerConnection();
984 auto caller_first_transceiver = caller->AddTransceiver(first_type_);
985
986 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
987
988 std::string first_mid = *caller_first_transceiver->mid();
Harald Alvestrand6060df52020-08-11 07:54:02989 caller_first_transceiver->StopInternal();
Steve Anton5c72e712018-12-10 22:25:30990
991 // The reoffer will have a rejected m= section.
992 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
993
994 auto caller_second_transceiver = caller->AddTransceiver(second_type_);
995
996 // The reoffer should not recycle the existing m= section since it is not
997 // rejected in either the *current* local or *current* remote description.
998 auto reoffer = caller->CreateOffer();
999 auto reoffer_contents = reoffer->description()->contents();
1000 ASSERT_EQ(2u, reoffer_contents.size());
1001 EXPECT_TRUE(reoffer_contents[0].rejected);
1002 EXPECT_EQ(first_type_, reoffer_contents[0].media_description()->type());
1003 EXPECT_EQ(first_mid, reoffer_contents[0].name);
1004 EXPECT_FALSE(reoffer_contents[1].rejected);
1005 EXPECT_EQ(second_type_, reoffer_contents[1].media_description()->type());
1006 std::string second_mid = reoffer_contents[1].name;
1007 EXPECT_NE(first_mid, second_mid);
1008
1009 ASSERT_TRUE(caller->SetLocalDescription(std::move(reoffer)));
1010
1011 // Both RtpTransceivers are associated.
1012 EXPECT_EQ(first_mid, caller_first_transceiver->mid());
1013 EXPECT_EQ(second_mid, caller_second_transceiver->mid());
1014}
1015
1016// Test that a m= section is *not* recycled if the media section is only
1017// rejected in the pending local description and not rejected in the current
1018// remote description.
1019TEST_P(RecycleMediaSectionTest, PendingLocalRejectedAndNotRejectedRemote) {
1020 auto caller = CreatePeerConnection();
1021 auto caller_first_transceiver = caller->AddTransceiver(first_type_);
1022 auto callee = CreatePeerConnection();
1023
1024 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
1025
1026 std::string first_mid = *caller_first_transceiver->mid();
Harald Alvestrand6060df52020-08-11 07:54:021027 caller_first_transceiver->StopInternal();
Steve Anton5c72e712018-12-10 22:25:301028
1029 // The reoffer will have a rejected m= section.
1030 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
1031
1032 auto caller_second_transceiver = caller->AddTransceiver(second_type_);
1033
1034 // The reoffer should not recycle the existing m= section since it is not
1035 // rejected in either the *current* local or *current* remote description.
1036 auto reoffer = caller->CreateOffer();
1037 auto reoffer_contents = reoffer->description()->contents();
1038 ASSERT_EQ(2u, reoffer_contents.size());
1039 EXPECT_TRUE(reoffer_contents[0].rejected);
1040 EXPECT_EQ(first_type_, reoffer_contents[0].media_description()->type());
1041 EXPECT_EQ(first_mid, reoffer_contents[0].name);
1042 EXPECT_FALSE(reoffer_contents[1].rejected);
1043 EXPECT_EQ(second_type_, reoffer_contents[1].media_description()->type());
1044 std::string second_mid = reoffer_contents[1].name;
1045 EXPECT_NE(first_mid, second_mid);
1046
1047 ASSERT_TRUE(caller->SetLocalDescription(std::move(reoffer)));
1048
1049 // Both RtpTransceivers are associated.
1050 EXPECT_EQ(first_mid, caller_first_transceiver->mid());
1051 EXPECT_EQ(second_mid, caller_second_transceiver->mid());
1052}
1053
1054// Test that an m= section is *not* recycled if the media section is only
1055// rejected in the pending remote description and there is no current local
1056// description.
1057TEST_P(RecycleMediaSectionTest, PendingRemoteRejectedAndNoLocal) {
1058 auto caller = CreatePeerConnection();
1059 auto caller_first_transceiver = caller->AddTransceiver(first_type_);
1060 auto callee = CreatePeerConnection();
1061
1062 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1063
1064 ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
1065 auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
1066 std::string first_mid = *callee_first_transceiver->mid();
Harald Alvestrand6060df52020-08-11 07:54:021067 caller_first_transceiver->StopInternal();
Steve Anton5c72e712018-12-10 22:25:301068
1069 // The reoffer will have a rejected m= section.
1070 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1071
1072 auto callee_second_transceiver = callee->AddTransceiver(second_type_);
1073
1074 // The reoffer should not recycle the existing m= section since it is not
1075 // rejected in either the *current* local or *current* remote description.
1076 auto reoffer = callee->CreateOffer();
1077 auto reoffer_contents = reoffer->description()->contents();
1078 ASSERT_EQ(2u, reoffer_contents.size());
1079 EXPECT_TRUE(reoffer_contents[0].rejected);
1080 EXPECT_EQ(first_type_, reoffer_contents[0].media_description()->type());
1081 EXPECT_EQ(first_mid, reoffer_contents[0].name);
1082 EXPECT_FALSE(reoffer_contents[1].rejected);
1083 EXPECT_EQ(second_type_, reoffer_contents[1].media_description()->type());
1084 std::string second_mid = reoffer_contents[1].name;
1085 EXPECT_NE(first_mid, second_mid);
1086
1087 // Note: Cannot actually set the reoffer since the callee is in the signaling
1088 // state 'have-remote-offer'.
1089}
1090
1091// Test that an m= section is *not* recycled if the media section is only
1092// rejected in the pending remote description and not rejected in the current
1093// local description.
1094TEST_P(RecycleMediaSectionTest, PendingRemoteRejectedAndNotRejectedLocal) {
1095 auto caller = CreatePeerConnection();
1096 auto caller_first_transceiver = caller->AddTransceiver(first_type_);
1097 auto callee = CreatePeerConnection();
1098
1099 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
1100
1101 ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
1102 auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
1103 std::string first_mid = *callee_first_transceiver->mid();
Harald Alvestrand6060df52020-08-11 07:54:021104 caller_first_transceiver->StopInternal();
Steve Anton5c72e712018-12-10 22:25:301105
1106 // The reoffer will have a rejected m= section.
1107 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1108
1109 auto callee_second_transceiver = callee->AddTransceiver(second_type_);
1110
1111 // The reoffer should not recycle the existing m= section since it is not
1112 // rejected in either the *current* local or *current* remote description.
1113 auto reoffer = callee->CreateOffer();
1114 auto reoffer_contents = reoffer->description()->contents();
1115 ASSERT_EQ(2u, reoffer_contents.size());
1116 EXPECT_TRUE(reoffer_contents[0].rejected);
1117 EXPECT_EQ(first_type_, reoffer_contents[0].media_description()->type());
1118 EXPECT_EQ(first_mid, reoffer_contents[0].name);
1119 EXPECT_FALSE(reoffer_contents[1].rejected);
1120 EXPECT_EQ(second_type_, reoffer_contents[1].media_description()->type());
1121 std::string second_mid = reoffer_contents[1].name;
1122 EXPECT_NE(first_mid, second_mid);
1123
1124 // Note: Cannot actually set the reoffer since the callee is in the signaling
1125 // state 'have-remote-offer'.
1126}
1127
Steve Antondcc3c022017-12-23 00:02:541128// Test all combinations of audio and video as the first and second media type
1129// for the media section. This is needed for full test coverage because
1130// MediaSession has separate functions for processing audio and video media
1131// sections.
Mirko Bonadeic84f6612019-01-31 11:20:571132INSTANTIATE_TEST_SUITE_P(
Steve Antondcc3c022017-12-23 00:02:541133 PeerConnectionJsepTest,
1134 RecycleMediaSectionTest,
1135 Combine(Values(cricket::MEDIA_TYPE_AUDIO, cricket::MEDIA_TYPE_VIDEO),
1136 Values(cricket::MEDIA_TYPE_AUDIO, cricket::MEDIA_TYPE_VIDEO)));
1137
Steve Antonfa2260d2017-12-29 00:38:231138// Test that a new data channel section will not reuse a recycleable audio or
1139// video media section. Additionally, tests that the new section is added to the
1140// end of the session description.
1141TEST_F(PeerConnectionJsepTest, DataChannelDoesNotRecycleMediaSection) {
1142 auto caller = CreatePeerConnection();
1143 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1144 auto callee = CreatePeerConnection();
1145
1146 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
1147
Harald Alvestrand6060df52020-08-11 07:54:021148 transceiver->StopInternal();
Steve Antonfa2260d2017-12-29 00:38:231149
1150 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
1151
1152 caller->CreateDataChannel("dc");
1153
1154 auto offer = caller->CreateOffer();
1155 auto offer_contents = offer->description()->contents();
1156 ASSERT_EQ(2u, offer_contents.size());
1157 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO,
1158 offer_contents[0].media_description()->type());
1159 EXPECT_EQ(cricket::MEDIA_TYPE_DATA,
1160 offer_contents[1].media_description()->type());
1161
1162 ASSERT_TRUE(
1163 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
1164 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1165
1166 auto answer = callee->CreateAnswer();
1167 auto answer_contents = answer->description()->contents();
1168 ASSERT_EQ(2u, answer_contents.size());
1169 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO,
1170 answer_contents[0].media_description()->type());
1171 EXPECT_EQ(cricket::MEDIA_TYPE_DATA,
1172 answer_contents[1].media_description()->type());
1173}
1174
1175// Test that if a new track is added to an existing session that has a data,
1176// the new section comes at the end of the new offer, after the existing data
1177// section.
1178TEST_F(PeerConnectionJsepTest, AudioTrackAddedAfterDataSectionInReoffer) {
1179 auto caller = CreatePeerConnection();
1180 caller->CreateDataChannel("dc");
1181 auto callee = CreatePeerConnection();
1182
1183 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
1184
1185 caller->AddAudioTrack("a");
1186
1187 auto offer = caller->CreateOffer();
1188 auto contents = offer->description()->contents();
1189 ASSERT_EQ(2u, contents.size());
1190 EXPECT_EQ(cricket::MEDIA_TYPE_DATA, contents[0].media_description()->type());
1191 EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[1].media_description()->type());
1192}
1193
Steve Antondcc3c022017-12-23 00:02:541194// Tests for MID properties.
1195
1196static void RenameSection(size_t mline_index,
1197 const std::string& new_mid,
1198 SessionDescriptionInterface* sdesc) {
1199 cricket::SessionDescription* desc = sdesc->description();
1200 std::string old_mid = desc->contents()[mline_index].name;
1201 desc->contents()[mline_index].name = new_mid;
1202 desc->transport_infos()[mline_index].content_name = new_mid;
1203 const cricket::ContentGroup* bundle =
1204 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1205 if (bundle) {
1206 cricket::ContentGroup new_bundle = *bundle;
1207 if (new_bundle.RemoveContentName(old_mid)) {
1208 new_bundle.AddContentName(new_mid);
1209 }
1210 desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1211 desc->AddGroup(new_bundle);
1212 }
1213}
1214
1215// Test that two PeerConnections can have a successful offer/answer exchange if
1216// the MIDs are changed from the defaults.
1217TEST_F(PeerConnectionJsepTest, OfferAnswerWithChangedMids) {
1218 constexpr char kFirstMid[] = "nondefaultmid";
1219 constexpr char kSecondMid[] = "randommid";
1220
1221 auto caller = CreatePeerConnection();
1222 caller->AddAudioTrack("a");
1223 caller->AddAudioTrack("b");
1224 auto callee = CreatePeerConnection();
1225
1226 auto offer = caller->CreateOffer();
1227 RenameSection(0, kFirstMid, offer.get());
1228 RenameSection(1, kSecondMid, offer.get());
1229
1230 ASSERT_TRUE(
1231 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
1232 auto caller_transceivers = caller->pc()->GetTransceivers();
1233 EXPECT_EQ(kFirstMid, caller_transceivers[0]->mid());
1234 EXPECT_EQ(kSecondMid, caller_transceivers[1]->mid());
1235
1236 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1237 auto callee_transceivers = callee->pc()->GetTransceivers();
1238 EXPECT_EQ(kFirstMid, callee_transceivers[0]->mid());
1239 EXPECT_EQ(kSecondMid, callee_transceivers[1]->mid());
1240
1241 auto answer = callee->CreateAnswer();
1242 auto answer_contents = answer->description()->contents();
1243 EXPECT_EQ(kFirstMid, answer_contents[0].name);
1244 EXPECT_EQ(kSecondMid, answer_contents[1].name);
1245
1246 ASSERT_TRUE(
1247 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
1248 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
1249}
1250
1251// Test that CreateOffer will generate a MID that is not already used if the
1252// default it would have picked is already taken. This is tested by using a
1253// third PeerConnection to determine what the default would be for the second
1254// media section then setting that as the first media section's MID.
1255TEST_F(PeerConnectionJsepTest, CreateOfferGeneratesUniqueMidIfAlreadyTaken) {
1256 // First, find what the default MID is for the second media section.
1257 auto pc = CreatePeerConnection();
1258 pc->AddAudioTrack("a");
1259 pc->AddAudioTrack("b");
1260 auto default_offer = pc->CreateOffer();
1261 std::string default_second_mid =
1262 default_offer->description()->contents()[1].name;
1263
1264 // Now, do an offer/answer with one track which has the MID set to the default
1265 // second MID.
1266 auto caller = CreatePeerConnection();
1267 caller->AddAudioTrack("a");
1268 auto callee = CreatePeerConnection();
1269
1270 auto offer = caller->CreateOffer();
1271 RenameSection(0, default_second_mid, offer.get());
1272
1273 ASSERT_TRUE(
1274 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
1275 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1276 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1277
1278 // Add a second track and ensure that the MID is different.
1279 caller->AddAudioTrack("b");
1280
1281 auto reoffer = caller->CreateOffer();
1282 auto reoffer_contents = reoffer->description()->contents();
1283 EXPECT_EQ(default_second_mid, reoffer_contents[0].name);
1284 EXPECT_NE(reoffer_contents[0].name, reoffer_contents[1].name);
1285}
1286
Steve Antonfa2260d2017-12-29 00:38:231287// Test that if an audio or video section has the default data section MID, then
1288// CreateOffer will generate a unique MID for the newly added data section.
1289TEST_F(PeerConnectionJsepTest,
1290 CreateOfferGeneratesUniqueMidForDataSectionIfAlreadyTaken) {
1291 // First, find what the default MID is for the data channel.
1292 auto pc = CreatePeerConnection();
1293 pc->CreateDataChannel("dc");
1294 auto default_offer = pc->CreateOffer();
1295 std::string default_data_mid =
1296 default_offer->description()->contents()[0].name;
1297
1298 // Now do an offer/answer with one audio track which has a MID set to the
1299 // default data MID.
1300 auto caller = CreatePeerConnection();
1301 caller->AddAudioTrack("a");
1302 auto callee = CreatePeerConnection();
1303
1304 auto offer = caller->CreateOffer();
1305 RenameSection(0, default_data_mid, offer.get());
1306
1307 ASSERT_TRUE(
1308 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
1309 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1310 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1311
1312 // Add a data channel and ensure that the MID is different.
1313 caller->CreateDataChannel("dc");
1314
1315 auto reoffer = caller->CreateOffer();
1316 auto reoffer_contents = reoffer->description()->contents();
1317 EXPECT_EQ(default_data_mid, reoffer_contents[0].name);
1318 EXPECT_NE(reoffer_contents[0].name, reoffer_contents[1].name);
1319}
1320
Steve Antondcc3c022017-12-23 00:02:541321// Test that a reoffer initiated by the callee adds a new track to the caller.
1322TEST_F(PeerConnectionJsepTest, CalleeDoesReoffer) {
1323 auto caller = CreatePeerConnection();
1324 caller->AddAudioTrack("a");
1325 auto callee = CreatePeerConnection();
1326 callee->AddAudioTrack("a");
1327 callee->AddVideoTrack("v");
1328
1329 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
1330
1331 EXPECT_EQ(1u, caller->pc()->GetTransceivers().size());
1332 EXPECT_EQ(2u, callee->pc()->GetTransceivers().size());
1333
1334 ASSERT_TRUE(callee->ExchangeOfferAnswerWith(caller.get()));
1335
1336 EXPECT_EQ(2u, caller->pc()->GetTransceivers().size());
1337 EXPECT_EQ(2u, callee->pc()->GetTransceivers().size());
1338}
1339
Steve Anton02ee47c2018-01-11 00:26:061340// Tests for MSID properties.
1341
1342// Test that adding a track with AddTrack results in an offer that signals the
1343// track's ID.
1344TEST_F(PeerConnectionJsepTest, AddingTrackWithAddTrackSpecifiesTrackId) {
1345 const std::string kTrackId = "audio_track";
1346
1347 auto caller = CreatePeerConnection();
1348 caller->AddAudioTrack(kTrackId);
1349
1350 auto offer = caller->CreateOffer();
1351 auto contents = offer->description()->contents();
1352 ASSERT_EQ(1u, contents.size());
1353 auto streams = contents[0].media_description()->streams();
1354 ASSERT_EQ(1u, streams.size());
1355 EXPECT_EQ(kTrackId, streams[0].id);
1356}
1357
1358// Test that adding a track by calling AddTransceiver then SetTrack results in
1359// an offer that does not signal the track's ID and signals a random ID.
1360TEST_F(PeerConnectionJsepTest,
1361 AddingTrackWithAddTransceiverSpecifiesRandomTrackId) {
1362 const std::string kTrackId = "audio_track";
1363
1364 auto caller = CreatePeerConnection();
1365 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
Niels Möllerafb246b2022-04-20 12:26:501366 transceiver->sender()->SetTrack(caller->CreateAudioTrack(kTrackId).get());
Steve Anton02ee47c2018-01-11 00:26:061367
1368 auto offer = caller->CreateOffer();
1369 auto contents = offer->description()->contents();
1370 ASSERT_EQ(1u, contents.size());
1371 auto streams = contents[0].media_description()->streams();
1372 ASSERT_EQ(1u, streams.size());
1373 EXPECT_NE(kTrackId, streams[0].id);
1374}
1375
Steve Anton5f94aa22018-02-01 18:58:301376// Test that if the transceiver is recvonly or inactive, then no MSID
1377// information is included in the offer.
1378TEST_F(PeerConnectionJsepTest, NoMsidInOfferIfTransceiverDirectionHasNoSend) {
1379 auto caller = CreatePeerConnection();
1380
1381 RtpTransceiverInit init_recvonly;
1382 init_recvonly.direction = RtpTransceiverDirection::kRecvOnly;
1383 ASSERT_TRUE(caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init_recvonly));
1384
1385 RtpTransceiverInit init_inactive;
1386 init_inactive.direction = RtpTransceiverDirection::kInactive;
1387 ASSERT_TRUE(caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init_inactive));
1388
1389 auto offer = caller->CreateOffer();
1390 auto contents = offer->description()->contents();
1391 ASSERT_EQ(2u, contents.size());
1392 // MSID is specified in the first stream, so no streams means no MSID.
1393 EXPECT_EQ(0u, contents[0].media_description()->streams().size());
1394 EXPECT_EQ(0u, contents[1].media_description()->streams().size());
1395}
1396
1397// Test that if an answer negotiates transceiver directions of recvonly or
1398// inactive, then no MSID information is included in the answer.
1399TEST_F(PeerConnectionJsepTest, NoMsidInAnswerIfNoRespondingTracks) {
1400 auto caller = CreatePeerConnection();
1401 auto callee = CreatePeerConnection();
1402
1403 // recvonly transceiver will get negotiated to inactive since the callee has
1404 // no tracks to send in response.
1405 RtpTransceiverInit init_recvonly;
1406 init_recvonly.direction = RtpTransceiverDirection::kRecvOnly;
1407 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init_recvonly);
1408
1409 // sendrecv transceiver will get negotiated to recvonly since the callee has
1410 // no tracks to send in response.
1411 RtpTransceiverInit init_sendrecv;
1412 init_sendrecv.direction = RtpTransceiverDirection::kSendRecv;
1413 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init_sendrecv);
1414
1415 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1416
1417 auto answer = callee->CreateAnswer();
1418 auto contents = answer->description()->contents();
1419 ASSERT_EQ(2u, contents.size());
1420 // MSID is specified in the first stream, so no streams means no MSID.
1421 EXPECT_EQ(0u, contents[0].media_description()->streams().size());
1422 EXPECT_EQ(0u, contents[1].media_description()->streams().size());
1423}
1424
1425// Test that the MSID is included even if the transceiver direction has changed
1426// to inactive if the transceiver had previously sent media.
1427TEST_F(PeerConnectionJsepTest, IncludeMsidEvenIfDirectionHasChanged) {
1428 auto caller = CreatePeerConnection();
1429 caller->AddAudioTrack("audio");
1430 auto callee = CreatePeerConnection();
1431 callee->AddAudioTrack("audio");
1432
1433 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
1434
Harald Alvestrand6060df52020-08-11 07:54:021435 caller->pc()->GetTransceivers()[0]->SetDirectionWithError(
Steve Anton5f94aa22018-02-01 18:58:301436 RtpTransceiverDirection::kInactive);
1437
1438 // The transceiver direction on both sides will turn to inactive.
1439 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
1440
1441 auto* offer = callee->pc()->remote_description();
1442 auto offer_contents = offer->description()->contents();
1443 ASSERT_EQ(1u, offer_contents.size());
1444 // MSID is specified in the first stream. If it is present, assume that MSID
1445 // is there.
1446 EXPECT_EQ(1u, offer_contents[0].media_description()->streams().size());
1447
1448 auto* answer = caller->pc()->remote_description();
1449 auto answer_contents = answer->description()->contents();
1450 ASSERT_EQ(1u, answer_contents.size());
1451 EXPECT_EQ(1u, answer_contents[0].media_description()->streams().size());
1452}
1453
1454// Test that stopping a RtpTransceiver will cause future offers to not include
1455// any MSID information for that section.
1456TEST_F(PeerConnectionJsepTest, RemoveMsidIfTransceiverStopped) {
1457 auto caller = CreatePeerConnection();
1458 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1459 auto callee = CreatePeerConnection();
1460
1461 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
1462
Harald Alvestrand6060df52020-08-11 07:54:021463 transceiver->StopInternal();
Steve Anton5f94aa22018-02-01 18:58:301464
1465 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
1466
1467 auto* offer = callee->pc()->remote_description();
1468 auto offer_contents = offer->description()->contents();
1469 ASSERT_EQ(1u, offer_contents.size());
1470 // MSID is specified in the first stream, so no streams means no MSID.
1471 EXPECT_EQ(0u, offer_contents[0].media_description()->streams().size());
1472}
1473
Steve Anton02ee47c2018-01-11 00:26:061474// Test that the callee RtpReceiver created by a call to SetRemoteDescription
1475// has its ID set to the signaled track ID.
1476TEST_F(PeerConnectionJsepTest,
1477 RtpReceiverCreatedBySetRemoteDescriptionHasSignaledTrackId) {
1478 const std::string kTrackId = "audio_track";
1479
1480 auto caller = CreatePeerConnection();
1481 auto callee = CreatePeerConnection();
1482 caller->AddAudioTrack(kTrackId);
1483
1484 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1485
1486 ASSERT_EQ(1u, callee->pc()->GetReceivers().size());
1487 auto receiver = callee->pc()->GetReceivers()[0];
1488 EXPECT_EQ(kTrackId, receiver->id());
1489}
1490
1491// Test that if the callee RtpReceiver is reused by a call to
1492// SetRemoteDescription, its ID does not change.
1493TEST_F(PeerConnectionJsepTest,
1494 RtpReceiverCreatedBeforeSetRemoteDescriptionKeepsId) {
1495 const std::string kTrackId = "audio_track";
1496
1497 auto caller = CreatePeerConnection();
1498 auto callee = CreatePeerConnection();
1499 caller->AddAudioTrack(kTrackId);
1500 callee->AddAudioTrack("dummy_track");
1501
1502 ASSERT_EQ(1u, callee->pc()->GetReceivers().size());
1503 auto receiver = callee->pc()->GetReceivers()[0];
1504 std::string receiver_id = receiver->id();
1505
1506 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1507
1508 EXPECT_EQ(receiver_id, receiver->id());
1509}
1510
Steve Antonef65ef12018-01-11 01:15:201511// Test that setting a remote offer with one track that has no streams fires off
1512// the correct OnAddTrack event.
1513TEST_F(PeerConnectionJsepTest,
1514 SetRemoteOfferWithOneTrackNoStreamFiresOnAddTrack) {
1515 const std::string kTrackLabel = "audio_track";
1516
1517 auto caller = CreatePeerConnection();
1518 auto callee = CreatePeerConnection();
1519 ASSERT_TRUE(caller->AddAudioTrack(kTrackLabel));
1520
1521 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1522
Seth Hampson5b4f0752018-04-02 23:31:361523 const auto& track_events = callee->observer()->add_track_events_;
1524 ASSERT_EQ(1u, track_events.size());
1525 const auto& event = track_events[0];
1526 EXPECT_EQ(kTrackLabel, event.receiver->track()->id());
1527 EXPECT_EQ(0u, event.streams.size());
Steve Antonef65ef12018-01-11 01:15:201528}
1529
1530// Test that setting a remote offer with one track that has one stream fires off
1531// the correct OnAddTrack event.
1532TEST_F(PeerConnectionJsepTest,
1533 SetRemoteOfferWithOneTrackOneStreamFiresOnAddTrack) {
1534 const std::string kTrackLabel = "audio_track";
Seth Hampson845e8782018-03-02 19:34:101535 const std::string kStreamId = "audio_stream";
Steve Antonef65ef12018-01-11 01:15:201536
1537 auto caller = CreatePeerConnection();
1538 auto callee = CreatePeerConnection();
Seth Hampson845e8782018-03-02 19:34:101539 ASSERT_TRUE(caller->AddAudioTrack(kTrackLabel, {kStreamId}));
Steve Antonef65ef12018-01-11 01:15:201540
1541 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1542
1543 const auto& track_events = callee->observer()->add_track_events_;
1544 ASSERT_EQ(1u, track_events.size());
1545 const auto& event = track_events[0];
1546 ASSERT_EQ(1u, event.streams.size());
1547 auto stream = event.streams[0];
Seth Hampson13b8bad2018-03-13 23:05:281548 EXPECT_EQ(kStreamId, stream->id());
Steve Antonef65ef12018-01-11 01:15:201549 EXPECT_THAT(track_events[0].snapshotted_stream_tracks.at(stream),
1550 ElementsAre(event.receiver->track()));
1551 EXPECT_EQ(event.receiver->streams(), track_events[0].streams);
1552}
1553
1554// Test that setting a remote offer with two tracks that share the same stream
1555// fires off two OnAddTrack events, both with the same stream that has both
1556// tracks present at the time of firing. This is to ensure that track events are
1557// not fired until SetRemoteDescription has finished processing all the media
1558// sections.
1559TEST_F(PeerConnectionJsepTest,
1560 SetRemoteOfferWithTwoTracksSameStreamFiresOnAddTrack) {
1561 const std::string kTrack1Label = "audio_track1";
1562 const std::string kTrack2Label = "audio_track2";
Seth Hampson845e8782018-03-02 19:34:101563 const std::string kSharedStreamId = "stream";
Steve Antonef65ef12018-01-11 01:15:201564
1565 auto caller = CreatePeerConnection();
1566 auto callee = CreatePeerConnection();
Seth Hampson845e8782018-03-02 19:34:101567 ASSERT_TRUE(caller->AddAudioTrack(kTrack1Label, {kSharedStreamId}));
1568 ASSERT_TRUE(caller->AddAudioTrack(kTrack2Label, {kSharedStreamId}));
Steve Antonef65ef12018-01-11 01:15:201569
1570 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1571
1572 const auto& track_events = callee->observer()->add_track_events_;
1573 ASSERT_EQ(2u, track_events.size());
1574 const auto& event1 = track_events[0];
1575 const auto& event2 = track_events[1];
1576 ASSERT_EQ(1u, event1.streams.size());
1577 auto stream = event1.streams[0];
1578 ASSERT_THAT(event2.streams, ElementsAre(stream));
1579 auto track1 = event1.receiver->track();
1580 auto track2 = event2.receiver->track();
1581 EXPECT_THAT(event1.snapshotted_stream_tracks.at(stream),
1582 UnorderedElementsAre(track1, track2));
1583 EXPECT_THAT(event2.snapshotted_stream_tracks.at(stream),
1584 UnorderedElementsAre(track1, track2));
1585}
1586
Seth Hampson5b4f0752018-04-02 23:31:361587// Test that setting a remote offer with one track that has two streams fires
1588// off the correct OnAddTrack event.
1589TEST_F(PeerConnectionJsepTest,
1590 SetRemoteOfferWithOneTrackTwoStreamFiresOnAddTrack) {
1591 const std::string kTrackLabel = "audio_track";
1592 const std::string kStreamId1 = "audio_stream1";
1593 const std::string kStreamId2 = "audio_stream2";
1594
1595 auto caller = CreatePeerConnection();
1596 auto callee = CreatePeerConnection();
1597 ASSERT_TRUE(caller->AddAudioTrack(kTrackLabel, {kStreamId1, kStreamId2}));
1598
1599 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1600
1601 const auto& track_events = callee->observer()->add_track_events_;
1602 ASSERT_EQ(1u, track_events.size());
1603 const auto& event = track_events[0];
1604 ASSERT_EQ(2u, event.streams.size());
1605 EXPECT_EQ(kStreamId1, event.streams[0]->id());
1606 EXPECT_EQ(kStreamId2, event.streams[1]->id());
1607}
Steve Antonef65ef12018-01-11 01:15:201608
Steve Anton54b84072018-02-20 23:19:521609// Test that if an RtpTransceiver with a current_direction set is stopped, then
1610// current_direction is changed to null.
1611TEST_F(PeerConnectionJsepTest, CurrentDirectionResetWhenRtpTransceiverStopped) {
1612 auto caller = CreatePeerConnection();
1613 auto callee = CreatePeerConnection();
1614
1615 auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1616
1617 ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
1618
1619 ASSERT_TRUE(transceiver->current_direction());
Harald Alvestrand6060df52020-08-11 07:54:021620 transceiver->StopInternal();
1621 EXPECT_EQ(transceiver->current_direction(),
1622 RtpTransceiverDirection::kStopped);
Steve Anton54b84072018-02-20 23:19:521623}
1624
Steve Antonba42e992018-04-09 21:10:011625// Test that you can't set an answer on a PeerConnection before setting the
1626// offer.
Harald Alvestrand5081c0c2018-03-09 14:18:031627TEST_F(PeerConnectionJsepTest, AnswerBeforeOfferFails) {
1628 auto caller = CreatePeerConnection();
1629 auto callee = CreatePeerConnection();
Harald Alvestrand5081c0c2018-03-09 14:18:031630 caller->AddAudioTrack("audio");
Steve Antonba42e992018-04-09 21:10:011631
1632 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
1633
1634 RTCError error;
1635 ASSERT_FALSE(caller->SetRemoteDescription(callee->CreateAnswer(), &error));
1636 EXPECT_EQ(RTCErrorType::INVALID_STATE, error.type());
1637}
1638
1639// Test that a Unified Plan PeerConnection fails to set a Plan B offer if it has
1640// two video tracks.
1641TEST_F(PeerConnectionJsepTest, TwoVideoPlanBToUnifiedPlanFails) {
1642 RTCConfiguration config_planb;
Florent Castelli15a38de2022-04-05 22:38:211643 config_planb.sdp_semantics = SdpSemantics::kPlanB_DEPRECATED;
Steve Antonba42e992018-04-09 21:10:011644 auto caller = CreatePeerConnection(config_planb);
1645 auto callee = CreatePeerConnection();
1646 caller->AddVideoTrack("video1");
1647 caller->AddVideoTrack("video2");
1648
1649 RTCError error;
1650 ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
1651 EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, error.type());
1652}
1653
1654// Test that a Unified Plan PeerConnection fails to set a Plan B answer if it
1655// has two video tracks.
1656TEST_F(PeerConnectionJsepTest, OneVideoUnifiedPlanToTwoVideoPlanBFails) {
1657 auto caller = CreatePeerConnection();
1658 RTCConfiguration config_planb;
Florent Castelli15a38de2022-04-05 22:38:211659 config_planb.sdp_semantics = SdpSemantics::kPlanB_DEPRECATED;
Steve Antonba42e992018-04-09 21:10:011660 auto callee = CreatePeerConnection(config_planb);
1661 caller->AddVideoTrack("video");
1662 callee->AddVideoTrack("video1");
1663 callee->AddVideoTrack("video2");
1664
1665 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1666
1667 RTCError error;
1668 ASSERT_FALSE(caller->SetRemoteDescription(caller->CreateAnswer(), &error));
1669 EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, error.type());
Harald Alvestrand5081c0c2018-03-09 14:18:031670}
1671
Steve Anton06817cd2018-12-18 23:55:301672// Removes the RTP header extension associated with the given URI from the media
1673// description.
1674static void RemoveRtpHeaderExtensionByUri(
1675 MediaContentDescription* media_description,
1676 absl::string_view uri) {
1677 std::vector<RtpExtension> header_extensions =
1678 media_description->rtp_header_extensions();
1679 header_extensions.erase(std::remove_if(
1680 header_extensions.begin(), header_extensions.end(),
1681 [uri](const RtpExtension& extension) { return extension.uri == uri; }));
1682 media_description->set_rtp_header_extensions(header_extensions);
1683}
1684
1685// Transforms a session description to emulate a legacy endpoint which does not
1686// support a=mid, BUNDLE, and the MID header extension.
1687static void ClearMids(SessionDescriptionInterface* sdesc) {
1688 cricket::SessionDescription* desc = sdesc->description();
1689 desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1690 cricket::ContentInfo* audio_content = cricket::GetFirstAudioContent(desc);
1691 if (audio_content) {
1692 desc->GetTransportInfoByName(audio_content->name)->content_name = "";
1693 audio_content->name = "";
1694 RemoveRtpHeaderExtensionByUri(audio_content->media_description(),
1695 RtpExtension::kMidUri);
1696 }
1697 cricket::ContentInfo* video_content = cricket::GetFirstVideoContent(desc);
1698 if (video_content) {
1699 desc->GetTransportInfoByName(video_content->name)->content_name = "";
1700 video_content->name = "";
1701 RemoveRtpHeaderExtensionByUri(video_content->media_description(),
1702 RtpExtension::kMidUri);
1703 }
1704}
1705
1706// Test that negotiation works with legacy endpoints which do not support a=mid.
1707TEST_F(PeerConnectionJsepTest, LegacyNoMidAudioOnlyOffer) {
1708 auto caller = CreatePeerConnection();
1709 caller->AddAudioTrack("audio");
1710 auto callee = CreatePeerConnection();
1711 callee->AddAudioTrack("audio");
1712
1713 auto offer = caller->CreateOffer();
1714 ClearMids(offer.get());
1715
1716 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1717 EXPECT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
1718}
1719TEST_F(PeerConnectionJsepTest, LegacyNoMidAudioVideoOffer) {
1720 auto caller = CreatePeerConnection();
1721 caller->AddAudioTrack("audio");
1722 caller->AddVideoTrack("video");
1723 auto callee = CreatePeerConnection();
1724 callee->AddAudioTrack("audio");
1725 callee->AddVideoTrack("video");
1726
1727 auto offer = caller->CreateOffer();
1728 ClearMids(offer.get());
1729
1730 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1731 EXPECT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
1732}
1733TEST_F(PeerConnectionJsepTest, LegacyNoMidAudioOnlyAnswer) {
1734 auto caller = CreatePeerConnection();
1735 caller->AddAudioTrack("audio");
1736 auto callee = CreatePeerConnection();
1737 callee->AddAudioTrack("audio");
1738
1739 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1740
1741 auto answer = callee->CreateAnswer();
1742 ClearMids(answer.get());
1743
1744 EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
1745}
1746TEST_F(PeerConnectionJsepTest, LegacyNoMidAudioVideoAnswer) {
1747 auto caller = CreatePeerConnection();
1748 caller->AddAudioTrack("audio");
1749 caller->AddVideoTrack("video");
1750 auto callee = CreatePeerConnection();
1751 callee->AddAudioTrack("audio");
1752 callee->AddVideoTrack("video");
1753
1754 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
1755
1756 auto answer = callee->CreateAnswer();
1757 ClearMids(answer.get());
1758
1759 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
1760}
1761
Steve Antond7180cc2019-02-07 18:44:531762// Test that negotiation works with legacy endpoints which do not support a=mid
1763// when setting two remote descriptions without setting a local description in
1764// between.
1765TEST_F(PeerConnectionJsepTest, LegacyNoMidTwoRemoteOffers) {
1766 auto caller = CreatePeerConnection();
1767 caller->AddAudioTrack("audio");
1768 auto callee = CreatePeerConnection();
1769 callee->AddAudioTrack("audio");
1770
1771 auto offer = caller->CreateOffer();
1772 ClearMids(offer.get());
1773
1774 ASSERT_TRUE(
1775 callee->SetRemoteDescription(CloneSessionDescription(offer.get())));
1776 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1777 EXPECT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
1778}
1779
Steve Antonceac0152018-12-19 19:32:201780// Test that SetLocalDescription fails if a=mid lines are missing.
1781TEST_F(PeerConnectionJsepTest, SetLocalDescriptionFailsMissingMid) {
1782 auto caller = CreatePeerConnection();
1783 caller->AddAudioTrack("audio");
1784
1785 auto offer = caller->CreateOffer();
1786 ClearMids(offer.get());
1787
1788 std::string error;
1789 ASSERT_FALSE(caller->SetLocalDescription(std::move(offer), &error));
1790 EXPECT_EQ(
1791 "Failed to set local offer sdp: A media section is missing a MID "
1792 "attribute.",
1793 error);
1794}
1795
Eldar Rello5ab79e62019-10-09 15:29:441796TEST_F(PeerConnectionJsepTest, RollbackSupportedInUnifiedPlan) {
1797 RTCConfiguration config;
1798 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
1799 config.enable_implicit_rollback = true;
1800 auto caller = CreatePeerConnection(config);
1801 auto callee = CreatePeerConnection(config);
1802 EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
1803 EXPECT_TRUE(caller->SetLocalDescription(caller->CreateRollback()));
1804 EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
1805 EXPECT_TRUE(caller->SetRemoteDescription(caller->CreateRollback()));
1806 EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
1807 EXPECT_TRUE(caller->SetRemoteDescription(callee->CreateOffer()));
1808}
1809
1810TEST_F(PeerConnectionJsepTest, RollbackNotSupportedInPlanB) {
1811 RTCConfiguration config;
Florent Castelli15a38de2022-04-05 22:38:211812 config.sdp_semantics = SdpSemantics::kPlanB_DEPRECATED;
Eldar Rello5ab79e62019-10-09 15:29:441813 config.enable_implicit_rollback = true;
1814 auto caller = CreatePeerConnection(config);
1815 auto callee = CreatePeerConnection(config);
1816 EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
1817 EXPECT_FALSE(caller->SetLocalDescription(caller->CreateRollback()));
1818 EXPECT_FALSE(caller->SetRemoteDescription(caller->CreateRollback()));
1819 EXPECT_FALSE(caller->SetRemoteDescription(callee->CreateOffer()));
1820}
1821
1822TEST_F(PeerConnectionJsepTest, RollbackFailsInStableState) {
1823 auto caller = CreatePeerConnection();
1824 EXPECT_FALSE(caller->SetLocalDescription(caller->CreateRollback()));
1825 EXPECT_FALSE(caller->SetRemoteDescription(caller->CreateRollback()));
1826}
1827
1828TEST_F(PeerConnectionJsepTest, RollbackToStableStateAndClearLocalOffer) {
1829 auto caller = CreatePeerConnection();
1830 EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
1831 EXPECT_TRUE(caller->SetLocalDescription(caller->CreateRollback()));
1832 EXPECT_EQ(caller->signaling_state(), PeerConnectionInterface::kStable);
1833 EXPECT_EQ(caller->pc()->pending_local_description(), nullptr);
1834
1835 EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
1836 EXPECT_TRUE(caller->SetRemoteDescription(caller->CreateRollback()));
1837 EXPECT_EQ(caller->signaling_state(), PeerConnectionInterface::kStable);
1838 EXPECT_EQ(caller->pc()->pending_local_description(), nullptr);
1839}
1840
1841TEST_F(PeerConnectionJsepTest, RollbackToStableStateAndClearRemoteOffer) {
1842 auto caller = CreatePeerConnection();
1843 auto callee = CreatePeerConnection();
1844 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
1845 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
1846 EXPECT_EQ(callee->signaling_state(), PeerConnectionInterface::kStable);
1847 EXPECT_EQ(callee->pc()->pending_remote_description(), nullptr);
1848
1849 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
1850 EXPECT_TRUE(callee->SetLocalDescription(caller->CreateRollback()));
1851 EXPECT_EQ(callee->signaling_state(), PeerConnectionInterface::kStable);
1852 EXPECT_EQ(callee->pc()->pending_remote_description(), nullptr);
1853}
1854
Eldar Relloead0ec92019-10-21 20:01:311855TEST_F(PeerConnectionJsepTest, RollbackImplicitly) {
Eldar Rello5ab79e62019-10-09 15:29:441856 RTCConfiguration config;
1857 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
1858 config.enable_implicit_rollback = true;
1859 auto caller = CreatePeerConnection(config);
1860 auto callee = CreatePeerConnection(config);
1861 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
1862 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
1863 EXPECT_EQ(callee->signaling_state(),
1864 PeerConnectionInterface::kHaveRemoteOffer);
Eldar Relloead0ec92019-10-21 20:01:311865 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
Henrik Boströme574a312020-08-25 08:20:111866 EXPECT_FALSE(callee->observer()->legacy_renegotiation_needed());
1867 EXPECT_FALSE(callee->observer()->has_negotiation_needed_event());
Eldar Rello5ab79e62019-10-09 15:29:441868}
1869
Eldar Relloead0ec92019-10-21 20:01:311870TEST_F(PeerConnectionJsepTest, RollbackImplicitlyNegotatiationNotNeeded) {
1871 RTCConfiguration config;
1872 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
1873 config.enable_implicit_rollback = true;
1874 auto caller = CreatePeerConnection(config);
1875 auto callee = CreatePeerConnection(config);
1876 caller->AddAudioTrack("a");
1877 callee->AddAudioTrack("b");
1878 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
Henrik Boströme574a312020-08-25 08:20:111879 callee->observer()->clear_legacy_renegotiation_needed();
1880 callee->observer()->clear_latest_negotiation_needed_event();
Eldar Relloead0ec92019-10-21 20:01:311881 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
1882 EXPECT_EQ(callee->signaling_state(),
1883 PeerConnectionInterface::kHaveRemoteOffer);
1884 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
1885 // No negotiation needed as track got attached in the answer.
Henrik Boströme574a312020-08-25 08:20:111886 EXPECT_FALSE(callee->observer()->legacy_renegotiation_needed());
1887 EXPECT_FALSE(callee->observer()->has_negotiation_needed_event());
Eldar Relloead0ec92019-10-21 20:01:311888 EXPECT_EQ(callee->observer()->remove_track_events_.size(), 0u);
1889}
1890
1891TEST_F(PeerConnectionJsepTest, RollbackImplicitlyAndNegotiationNeeded) {
1892 RTCConfiguration config;
1893 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
1894 config.enable_implicit_rollback = true;
1895 auto caller = CreatePeerConnection(config);
1896 auto callee = CreatePeerConnection(config);
1897 callee->AddAudioTrack("a");
1898 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
Henrik Boströme574a312020-08-25 08:20:111899 callee->observer()->clear_legacy_renegotiation_needed();
1900 callee->observer()->clear_latest_negotiation_needed_event();
Eldar Relloead0ec92019-10-21 20:01:311901 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
1902 EXPECT_EQ(callee->signaling_state(),
1903 PeerConnectionInterface::kHaveRemoteOffer);
Henrik Boströme574a312020-08-25 08:20:111904 EXPECT_FALSE(callee->observer()->legacy_renegotiation_needed());
1905 EXPECT_FALSE(callee->observer()->has_negotiation_needed_event());
Eldar Relloead0ec92019-10-21 20:01:311906 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
Henrik Boströme574a312020-08-25 08:20:111907 EXPECT_TRUE(callee->observer()->legacy_renegotiation_needed());
1908 EXPECT_TRUE(callee->observer()->has_negotiation_needed_event());
Eldar Relloead0ec92019-10-21 20:01:311909 EXPECT_EQ(callee->observer()->remove_track_events_.size(), 0u);
1910}
1911
1912TEST_F(PeerConnectionJsepTest, AttemptToRollbackImplicitly) {
Eldar Rello5ab79e62019-10-09 15:29:441913 RTCConfiguration config;
1914 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
1915 config.enable_implicit_rollback = true;
1916 auto caller = CreatePeerConnection(config);
1917 auto callee = CreatePeerConnection(config);
1918 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
1919 EXPECT_FALSE(callee->SetRemoteDescription(
1920 CreateSessionDescription(SdpType::kOffer, "invalid sdp")));
1921 EXPECT_EQ(callee->signaling_state(),
1922 PeerConnectionInterface::kHaveLocalOffer);
1923}
1924
1925TEST_F(PeerConnectionJsepTest, RollbackRemovesTransceiver) {
1926 auto caller = CreatePeerConnection();
1927 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1928 auto callee = CreatePeerConnection();
1929 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
Henrik Boströma8ad11d2022-04-27 09:54:011930 ASSERT_EQ(callee->pc()->GetTransceivers().size(), 1u);
1931 auto transceiver = callee->pc()->GetTransceivers()[0];
Eldar Rello5ab79e62019-10-09 15:29:441932 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
Eldar Relloead0ec92019-10-21 20:01:311933 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 0u);
1934 EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
Henrik Boströma8ad11d2022-04-27 09:54:011935 // The removed transceiver should be stopped and its receiver track should be
1936 // ended.
1937 EXPECT_TRUE(transceiver->stopping());
1938 EXPECT_TRUE(transceiver->stopping());
1939 EXPECT_EQ(transceiver->receiver()->track()->state(),
1940 MediaStreamTrackInterface::kEnded);
Eldar Rello5ab79e62019-10-09 15:29:441941}
1942
1943TEST_F(PeerConnectionJsepTest, RollbackKeepsTransceiverAndClearsMid) {
1944 auto caller = CreatePeerConnection();
1945 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1946 auto callee = CreatePeerConnection();
1947 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
1948 callee->AddAudioTrack("a");
Eldar Relloead0ec92019-10-21 20:01:311949 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
Eldar Rello5ab79e62019-10-09 15:29:441950 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
1951 // Transceiver can't be removed as track was added to it.
Eldar Relloead0ec92019-10-21 20:01:311952 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
Eldar Rello5ab79e62019-10-09 15:29:441953 // Mid got cleared to make it reusable.
1954 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
1955 // Transceiver should be counted as addTrack-created after rollback.
1956 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
Eldar Relloead0ec92019-10-21 20:01:311957 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
1958 EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
Henrik Boströma8ad11d2022-04-27 09:54:011959 // Because the transceiver is reusable, it must not be stopped and its
1960 // receiver track must still be live.
1961 auto transceiver = callee->pc()->GetTransceivers()[0];
1962 EXPECT_FALSE(transceiver->stopping());
1963 EXPECT_FALSE(transceiver->stopping());
1964 EXPECT_EQ(transceiver->receiver()->track()->state(),
1965 MediaStreamTrackInterface::kLive);
Eldar Rello5ab79e62019-10-09 15:29:441966}
1967
Eldar Rello353a7182019-11-25 16:49:441968TEST_F(PeerConnectionJsepTest,
1969 RollbackKeepsTransceiverAfterAddTrackEvenWhenTrackIsNulled) {
1970 auto caller = CreatePeerConnection();
1971 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1972 auto callee = CreatePeerConnection();
1973 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
1974 callee->AddAudioTrack("a");
1975 callee->pc()->GetTransceivers()[0]->sender()->SetTrack(nullptr);
1976 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->track(), nullptr);
1977 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
1978 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
1979 // Transceiver can't be removed as track was added to it.
1980 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
1981 // Mid got cleared to make it reusable.
1982 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
1983 // Transceiver should be counted as addTrack-created after rollback.
1984 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
1985 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
1986 EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
1987}
1988
Eldar Rello5ab79e62019-10-09 15:29:441989TEST_F(PeerConnectionJsepTest, RollbackRestoresMid) {
1990 auto caller = CreatePeerConnection();
1991 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
1992 auto callee = CreatePeerConnection();
1993 callee->AddAudioTrack("a");
1994 auto offer = callee->CreateOffer();
1995 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
Eldar Relloead0ec92019-10-21 20:01:311996 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
Eldar Rello5ab79e62019-10-09 15:29:441997 EXPECT_NE(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
1998 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
1999 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
2000 EXPECT_TRUE(callee->SetLocalDescription(std::move(offer)));
2001}
2002
Eldar Rello950d6b92021-04-06 19:38:002003TEST_F(PeerConnectionJsepTest, RollbackRestoresInitSendEncodings) {
2004 auto caller = CreatePeerConnection();
2005 RtpTransceiverInit init;
2006 init.direction = RtpTransceiverDirection::kSendRecv;
2007 RtpEncodingParameters encoding;
2008 encoding.rid = "hi";
2009 init.send_encodings.push_back(encoding);
2010 encoding.rid = "mid";
2011 init.send_encodings.push_back(encoding);
2012 encoding.rid = "lo";
2013 init.send_encodings.push_back(encoding);
2014 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
2015 auto encodings =
2016 caller->pc()->GetTransceivers()[0]->sender()->init_send_encodings();
2017 EXPECT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
2018 EXPECT_NE(caller->pc()->GetTransceivers()[0]->sender()->init_send_encodings(),
2019 encodings);
2020 EXPECT_TRUE(caller->SetLocalDescription(caller->CreateRollback()));
2021 EXPECT_EQ(caller->pc()->GetTransceivers()[0]->sender()->init_send_encodings(),
2022 encodings);
2023}
2024
2025TEST_F(PeerConnectionJsepTest, RollbackDoesNotAffectSendEncodings) {
2026 auto caller = CreatePeerConnection();
2027 auto callee = CreatePeerConnection();
2028 RtpTransceiverInit init;
2029 init.direction = RtpTransceiverDirection::kSendOnly;
2030 RtpEncodingParameters encoding;
2031 encoding.rid = "hi";
2032 init.send_encodings.push_back(encoding);
2033 encoding.rid = "mid";
2034 init.send_encodings.push_back(encoding);
2035 encoding.rid = "lo";
2036 init.send_encodings.push_back(encoding);
2037 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
2038 callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
2039 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
2040 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal());
2041 auto params = caller->pc()->GetTransceivers()[0]->sender()->GetParameters();
2042 EXPECT_TRUE(params.encodings[0].active);
2043 params.encodings[0].active = false;
2044 caller->pc()->GetTransceivers()[0]->sender()->SetParameters(params);
2045 auto offer = caller->CreateOffer();
2046 std::string offer_string;
2047 EXPECT_TRUE(offer.get()->ToString(&offer_string));
2048 std::string simulcast_line =
2049 offer_string.substr(offer_string.find("a=simulcast"));
2050 EXPECT_FALSE(simulcast_line.empty());
2051 EXPECT_TRUE(caller->SetLocalDescription(std::move(offer)));
2052 EXPECT_TRUE(caller->SetLocalDescription(caller->CreateRollback()));
2053 EXPECT_FALSE(caller->pc()
2054 ->GetTransceivers()[0]
2055 ->sender()
2056 ->GetParameters()
2057 .encodings[0]
2058 .active);
2059 offer = caller->CreateOffer();
2060 EXPECT_TRUE(offer.get()->ToString(&offer_string));
2061 EXPECT_EQ(offer_string.substr(offer_string.find("a=simulcast")),
2062 simulcast_line);
2063}
2064
Eldar Rello5ab79e62019-10-09 15:29:442065TEST_F(PeerConnectionJsepTest, RollbackRestoresMidAndRemovesTransceiver) {
2066 auto callee = CreatePeerConnection();
2067 callee->AddVideoTrack("a");
2068 auto offer = callee->CreateOffer();
2069 auto caller = CreatePeerConnection();
2070 caller->AddAudioTrack("b");
2071 caller->AddVideoTrack("c");
2072 auto mid = callee->pc()->GetTransceivers()[0]->mid();
2073 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
Eldar Relloead0ec92019-10-21 20:01:312074 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
Eldar Rello5ab79e62019-10-09 15:29:442075 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
Eldar Relloead0ec92019-10-21 20:01:312076 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
Eldar Rello5ab79e62019-10-09 15:29:442077 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), mid);
2078 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->media_type(),
2079 cricket::MEDIA_TYPE_VIDEO);
2080 EXPECT_TRUE(callee->SetLocalDescription(std::move(offer)));
Eldar Relloead0ec92019-10-21 20:01:312081 EXPECT_EQ(callee->observer()->remove_track_events_.size(),
2082 callee->observer()->add_track_events_.size());
2083}
2084
2085TEST_F(PeerConnectionJsepTest, RollbackHasNoEffectOnStableTransceivers) {
2086 auto callee = CreatePeerConnection();
2087 callee->AddVideoTrack("a");
2088 auto caller = CreatePeerConnection();
2089 caller->AddAudioTrack("b");
2090 caller->AddVideoTrack("c");
2091 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2092 EXPECT_TRUE(
2093 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
2094 // In stable don't add or remove anything.
Henrik Boströme574a312020-08-25 08:20:112095 callee->observer()->clear_legacy_renegotiation_needed();
2096 callee->observer()->clear_latest_negotiation_needed_event();
Eldar Relloead0ec92019-10-21 20:01:312097 size_t transceiver_count = callee->pc()->GetTransceivers().size();
2098 auto mid_0 = callee->pc()->GetTransceivers()[0]->mid();
2099 auto mid_1 = callee->pc()->GetTransceivers()[1]->mid();
2100 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
2101 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
2102 EXPECT_EQ(callee->pc()->GetTransceivers().size(), transceiver_count);
2103 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), mid_0);
2104 EXPECT_EQ(callee->pc()->GetTransceivers()[1]->mid(), mid_1);
2105 EXPECT_EQ(callee->observer()->remove_track_events_.size(), 0u);
Henrik Boströme574a312020-08-25 08:20:112106 EXPECT_FALSE(callee->observer()->legacy_renegotiation_needed());
2107 EXPECT_FALSE(callee->observer()->has_negotiation_needed_event());
Eldar Rello5ab79e62019-10-09 15:29:442108}
2109
2110TEST_F(PeerConnectionJsepTest, ImplicitlyRollbackTransceiversWithSameMids) {
2111 RTCConfiguration config;
2112 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
2113 config.enable_implicit_rollback = true;
2114 auto caller = CreatePeerConnection(config);
2115 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
2116 auto callee = CreatePeerConnection(config);
2117 callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
2118 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
2119 auto initial_mid = callee->pc()->GetTransceivers()[0]->mid();
2120 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
Eldar Relloead0ec92019-10-21 20:01:312121 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
Eldar Rello5ab79e62019-10-09 15:29:442122 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
2123 EXPECT_EQ(callee->pc()->GetTransceivers()[1]->mid(),
2124 caller->pc()->GetTransceivers()[0]->mid());
2125 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal()); // Go to stable.
2126 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
2127 EXPECT_NE(callee->pc()->GetTransceivers()[0]->mid(), initial_mid);
2128}
2129
2130TEST_F(PeerConnectionJsepTest, RollbackToNegotiatedStableState) {
2131 RTCConfiguration config;
2132 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
2133 config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle;
2134 auto caller = CreatePeerConnection(config);
2135 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
2136 auto callee = CreatePeerConnection(config);
2137 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2138 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
2139 caller->AddVideoTrack("a");
2140 callee->AddVideoTrack("b");
2141 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
Eldar Relloead0ec92019-10-21 20:01:312142 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
Eldar Rello5ab79e62019-10-09 15:29:442143 auto audio_transport =
2144 callee->pc()->GetTransceivers()[0]->sender()->dtls_transport();
2145 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
2146 callee->pc()->GetTransceivers()[1]->sender()->dtls_transport());
2147 EXPECT_NE(callee->pc()->GetTransceivers()[1]->sender()->dtls_transport(),
2148 nullptr);
2149 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
2150 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
2151 audio_transport); // Audio must remain working after rollback.
2152 EXPECT_EQ(callee->pc()->GetTransceivers()[1]->sender()->dtls_transport(),
2153 nullptr);
2154 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2155
2156 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
2157 audio_transport); // Audio transport is still the same.
2158}
2159
Eldar Rello353a7182019-11-25 16:49:442160TEST_F(PeerConnectionJsepTest, RollbackHasToDestroyTransport) {
2161 RTCConfiguration config;
2162 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
2163 config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle;
2164 auto pc = CreatePeerConnection(config);
2165 pc->AddAudioTrack("a");
2166 pc->AddVideoTrack("b");
2167 EXPECT_TRUE(pc->CreateOfferAndSetAsLocal());
2168 auto offer = pc->CreateOffer();
2169 EXPECT_EQ(pc->pc()->GetTransceivers().size(), 2u);
2170 auto audio_transport =
2171 pc->pc()->GetTransceivers()[0]->sender()->dtls_transport();
2172 EXPECT_EQ(pc->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
2173 pc->pc()->GetTransceivers()[1]->sender()->dtls_transport());
2174 EXPECT_NE(pc->pc()->GetTransceivers()[1]->sender()->dtls_transport(),
2175 nullptr);
2176 EXPECT_TRUE(pc->SetRemoteDescription(pc->CreateRollback()));
2177 EXPECT_TRUE(pc->SetLocalDescription(std::move(offer)));
2178 EXPECT_NE(pc->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
2179 nullptr);
2180 EXPECT_NE(pc->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
2181 audio_transport);
2182}
2183
Eldar Relloead0ec92019-10-21 20:01:312184TEST_F(PeerConnectionJsepTest, RollbackLocalDirectionChange) {
2185 auto caller = CreatePeerConnection();
2186 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
2187 auto callee = CreatePeerConnection();
2188 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2189 EXPECT_TRUE(
2190 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
2191 callee->AddAudioTrack("a");
Harald Alvestrand6060df52020-08-11 07:54:022192 callee->pc()->GetTransceivers()[0]->SetDirectionWithError(
Eldar Relloead0ec92019-10-21 20:01:312193 RtpTransceiverDirection::kSendOnly);
2194 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
2195 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
2196 auto audio_transport =
2197 callee->pc()->GetTransceivers()[0]->receiver()->dtls_transport();
2198 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
2199 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->direction(),
2200 RtpTransceiverDirection::kSendOnly);
2201 // One way audio must remain working after rollback as local direction change
2202 // comes in effect after completing full negotiation round.
2203 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->receiver()->dtls_transport(),
2204 audio_transport);
2205}
2206
2207TEST_F(PeerConnectionJsepTest, RollbackRemoteDirectionChange) {
2208 auto caller = CreatePeerConnection();
2209 auto caller_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
2210 auto callee = CreatePeerConnection();
2211 callee->AddAudioTrack("a");
2212 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2213 EXPECT_TRUE(
2214 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
2215 // In stable make remote audio receive only.
Harald Alvestrand6060df52020-08-11 07:54:022216 caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly);
Eldar Relloead0ec92019-10-21 20:01:312217 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2218 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
2219 // The direction attribute is not modified by the offer.
2220 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->direction(),
2221 RtpTransceiverDirection::kSendRecv);
2222 auto audio_transport =
2223 callee->pc()->GetTransceivers()[0]->sender()->dtls_transport();
2224 EXPECT_EQ(callee->observer()->add_track_events_.size(), 1u);
2225 EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
2226 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
2227 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
2228 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->direction(),
2229 RtpTransceiverDirection::kSendRecv);
2230 // One way audio must remain working after rollback.
2231 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
2232 audio_transport);
2233 EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
2234}
2235
Henrik Boström0a162762022-05-02 13:47:522236TEST_F(PeerConnectionJsepTest,
2237 RollbackRestoresFiredDirectionAndOnTrackCanFireAgain) {
2238 auto caller = CreatePeerConnection();
2239 auto caller_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
2240 auto callee = CreatePeerConnection();
2241 callee->AddAudioTrack("a");
2242 ASSERT_EQ(callee->pc()->GetTransceivers().size(), 1u);
2243 auto callee_transceiver = callee->pc()->GetTransceivers()[0];
2244 EXPECT_FALSE(callee_transceiver->fired_direction().has_value());
2245 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2246 EXPECT_TRUE(callee_transceiver->fired_direction().has_value());
2247 EXPECT_EQ(callee->observer()->add_track_events_.size(), 1u);
2248 // The existing transceiver becomes associated. Because it already exists,
2249 // rolling it back does not remove the transceiver, so if ontrack fires again
2250 // later it will be because the transceiver's internal states were restored
2251 // rather than due to the creation of a new transceiver.
2252 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
2253
2254 // Rollback: the transceiver is no longer receiving.
2255 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
2256 EXPECT_FALSE(callee_transceiver->fired_direction().has_value());
2257 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
2258
2259 // Set the remote offer again: ontrack should fire on the same transceiver.
2260 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2261 EXPECT_TRUE(callee_transceiver->fired_direction().has_value());
2262 EXPECT_EQ(callee->observer()->add_track_events_.size(), 2u);
2263 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
2264}
2265
2266TEST_F(PeerConnectionJsepTest,
2267 RollbackFromInactiveToReceivingMakesOnTrackFire) {
2268 auto caller = CreatePeerConnection();
2269 auto caller_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
2270 auto callee = CreatePeerConnection();
2271 // Perform full O/A so that transceiver is associated. Ontrack fires.
2272 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2273 EXPECT_EQ(callee->observer()->add_track_events_.size(), 1u);
2274 EXPECT_EQ(callee->observer()->remove_track_events_.size(), 0u);
2275 ASSERT_TRUE(
2276 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
2277
2278 // Start negotiating to make the transceiver inactive. Onremovetrack fires.
2279 caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kInactive);
2280 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2281 EXPECT_EQ(callee->observer()->add_track_events_.size(), 1u);
2282 EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
2283
2284 // Rollback the inactivation. Ontrack should fire again.
2285 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
2286 EXPECT_EQ(callee->observer()->add_track_events_.size(), 2u);
2287 EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
2288}
2289
Eldar Rello5ab79e62019-10-09 15:29:442290TEST_F(PeerConnectionJsepTest, RollbackAfterMultipleSLD) {
2291 auto callee = CreatePeerConnection();
2292 callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
2293 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
2294 callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
2295 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
Henrik Boströme574a312020-08-25 08:20:112296 callee->observer()->clear_legacy_renegotiation_needed();
2297 callee->observer()->clear_latest_negotiation_needed_event();
Eldar Rello5ab79e62019-10-09 15:29:442298 EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
Henrik Boströme574a312020-08-25 08:20:112299 EXPECT_TRUE(callee->observer()->legacy_renegotiation_needed());
2300 EXPECT_TRUE(callee->observer()->has_negotiation_needed_event());
Eldar Relloead0ec92019-10-21 20:01:312301 EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
Eldar Rello5ab79e62019-10-09 15:29:442302 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
2303 EXPECT_EQ(callee->pc()->GetTransceivers()[1]->mid(), absl::nullopt);
2304}
2305
2306TEST_F(PeerConnectionJsepTest, NoRollbackNeeded) {
2307 auto caller = CreatePeerConnection();
2308 caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
2309 auto callee = CreatePeerConnection();
2310 callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
2311 EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
2312 EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
2313 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
2314 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
2315}
2316
Eldar Rello353a7182019-11-25 16:49:442317TEST_F(PeerConnectionJsepTest, RollbackMultipleStreamChanges) {
2318 auto callee = CreatePeerConnection();
2319 auto caller = CreatePeerConnection();
2320 caller->AddAudioTrack("a_1", {"id_1"});
2321 caller->AddVideoTrack("v_0", {"id_0"}); // Provide an extra stream id.
2322 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2323 EXPECT_TRUE(
2324 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
2325 caller->pc()->GetTransceivers()[0]->sender()->SetStreams({"id_2"});
2326 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2327 caller->pc()->GetTransceivers()[0]->sender()->SetStreams({"id_3"});
2328 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
2329 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->receiver()->stream_ids()[0],
2330 "id_3");
2331 EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
2332 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->receiver()->stream_ids().size(),
2333 1u);
2334 EXPECT_EQ(callee->pc()->GetTransceivers()[0]->receiver()->stream_ids()[0],
2335 "id_1");
2336}
2337
Eldar Rellod85ea752020-02-19 18:41:072338TEST_F(PeerConnectionJsepTest, DataChannelImplicitRollback) {
2339 RTCConfiguration config;
2340 config.sdp_semantics = SdpSemantics::kUnifiedPlan;
2341 config.enable_implicit_rollback = true;
2342 auto caller = CreatePeerConnection(config);
2343 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
2344 auto callee = CreatePeerConnection(config);
2345 callee->CreateDataChannel("dummy");
2346 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
2347 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
2348 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
Henrik Boströme574a312020-08-25 08:20:112349 EXPECT_TRUE(callee->observer()->legacy_renegotiation_needed());
2350 EXPECT_TRUE(callee->observer()->has_negotiation_needed_event());
Eldar Rellod85ea752020-02-19 18:41:072351 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
2352}
2353
2354TEST_F(PeerConnectionJsepTest, RollbackRemoteDataChannelThenAddTransceiver) {
2355 auto caller = CreatePeerConnection();
2356 auto callee = CreatePeerConnection();
2357 caller->CreateDataChannel("dummy");
2358 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
2359 EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
2360 callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
2361 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
2362}
2363
2364TEST_F(PeerConnectionJsepTest,
2365 RollbackRemoteDataChannelThenAddTransceiverAndDataChannel) {
2366 auto caller = CreatePeerConnection();
2367 auto callee = CreatePeerConnection();
2368 caller->CreateDataChannel("dummy");
2369 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
2370 EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
2371 callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
2372 callee->CreateDataChannel("dummy");
2373 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
2374}
2375
2376TEST_F(PeerConnectionJsepTest, RollbackRemoteDataChannelThenAddDataChannel) {
2377 auto caller = CreatePeerConnection();
2378 auto callee = CreatePeerConnection();
2379 caller->CreateDataChannel("dummy");
2380 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
2381 EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
2382 callee->CreateDataChannel("dummy");
2383 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
2384}
2385
2386TEST_F(PeerConnectionJsepTest, RollbackRemoteTransceiverThenAddDataChannel) {
2387 auto caller = CreatePeerConnection();
2388 auto callee = CreatePeerConnection();
2389 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
2390 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
2391 EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
2392 callee->CreateDataChannel("dummy");
2393 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
2394}
2395
2396TEST_F(PeerConnectionJsepTest,
2397 RollbackRemoteTransceiverThenAddDataChannelAndTransceiver) {
2398 auto caller = CreatePeerConnection();
2399 auto callee = CreatePeerConnection();
2400 caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
2401 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
2402 EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
2403 callee->CreateDataChannel("dummy");
2404 callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
2405 EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
2406}
2407
Steve Antondcc3c022017-12-23 00:02:542408} // namespace webrtc