You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2214 lines
92 KiB
2214 lines
92 KiB
/*
|
|
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include <memory>
|
|
|
|
#include "api/task_queue/default_task_queue_factory.h"
|
|
#include "media/engine/webrtc_media_engine.h"
|
|
#include "media/engine/webrtc_media_engine_defaults.h"
|
|
#include "pc/media_session.h"
|
|
#include "pc/peer_connection_factory.h"
|
|
#include "pc/peer_connection_wrapper.h"
|
|
#include "pc/sdp_utils.h"
|
|
#ifdef WEBRTC_ANDROID
|
|
#include "pc/test/android_test_initializer.h"
|
|
#endif
|
|
#include "pc/test/fake_audio_capture_module.h"
|
|
#include "pc/test/fake_sctp_transport.h"
|
|
#include "rtc_base/gunit.h"
|
|
#include "rtc_base/virtual_socket_server.h"
|
|
#include "test/gmock.h"
|
|
|
|
// This file contains tests that ensure the PeerConnection's implementation of
|
|
// CreateOffer/CreateAnswer/SetLocalDescription/SetRemoteDescription conform
|
|
// to the JavaScript Session Establishment Protocol (JSEP).
|
|
// For now these semantics are only available when configuring the
|
|
// PeerConnection with Unified Plan, but eventually that will be the default.
|
|
|
|
namespace webrtc {
|
|
|
|
using cricket::MediaContentDescription;
|
|
using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
|
|
using ::testing::Combine;
|
|
using ::testing::ElementsAre;
|
|
using ::testing::UnorderedElementsAre;
|
|
using ::testing::Values;
|
|
|
|
class PeerConnectionFactoryForJsepTest : public PeerConnectionFactory {
|
|
public:
|
|
PeerConnectionFactoryForJsepTest()
|
|
: PeerConnectionFactory([] {
|
|
PeerConnectionFactoryDependencies dependencies;
|
|
dependencies.worker_thread = rtc::Thread::Current();
|
|
dependencies.network_thread = rtc::Thread::Current();
|
|
dependencies.signaling_thread = rtc::Thread::Current();
|
|
dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
|
|
cricket::MediaEngineDependencies media_deps;
|
|
media_deps.task_queue_factory = dependencies.task_queue_factory.get();
|
|
media_deps.adm = FakeAudioCaptureModule::Create();
|
|
SetMediaEngineDefaults(&media_deps);
|
|
dependencies.media_engine =
|
|
cricket::CreateMediaEngine(std::move(media_deps));
|
|
dependencies.call_factory = CreateCallFactory();
|
|
return dependencies;
|
|
}()) {}
|
|
|
|
std::unique_ptr<cricket::SctpTransportInternalFactory>
|
|
CreateSctpTransportInternalFactory() {
|
|
return std::make_unique<FakeSctpTransportFactory>();
|
|
}
|
|
};
|
|
|
|
class PeerConnectionJsepTest : public ::testing::Test {
|
|
protected:
|
|
typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
|
|
|
|
PeerConnectionJsepTest()
|
|
: vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
|
|
#ifdef WEBRTC_ANDROID
|
|
InitializeAndroidObjects();
|
|
#endif
|
|
}
|
|
|
|
WrapperPtr CreatePeerConnection() {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
return CreatePeerConnection(config);
|
|
}
|
|
|
|
WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
|
|
rtc::scoped_refptr<PeerConnectionFactory> pc_factory(
|
|
new rtc::RefCountedObject<PeerConnectionFactoryForJsepTest>());
|
|
RTC_CHECK(pc_factory->Initialize());
|
|
auto observer = std::make_unique<MockPeerConnectionObserver>();
|
|
auto pc = pc_factory->CreatePeerConnection(config, nullptr, nullptr,
|
|
observer.get());
|
|
if (!pc) {
|
|
return nullptr;
|
|
}
|
|
|
|
observer->SetPeerConnectionInterface(pc.get());
|
|
return std::make_unique<PeerConnectionWrapper>(pc_factory, pc,
|
|
std::move(observer));
|
|
}
|
|
|
|
std::unique_ptr<rtc::VirtualSocketServer> vss_;
|
|
rtc::AutoSocketServerThread main_;
|
|
};
|
|
|
|
// Tests for JSEP initial offer generation.
|
|
|
|
// Test that an offer created by a PeerConnection with no transceivers generates
|
|
// no media sections.
|
|
TEST_F(PeerConnectionJsepTest, EmptyInitialOffer) {
|
|
auto caller = CreatePeerConnection();
|
|
|
|
auto offer = caller->CreateOffer();
|
|
ASSERT_EQ(0u, offer->description()->contents().size());
|
|
}
|
|
|
|
// Test that an initial offer with one audio track generates one audio media
|
|
// section.
|
|
TEST_F(PeerConnectionJsepTest, AudioOnlyInitialOffer) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
|
|
auto offer = caller->CreateOffer();
|
|
auto contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, contents.size());
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[0].media_description()->type());
|
|
}
|
|
|
|
// Test than an initial offer with one video track generates one video media
|
|
// section
|
|
TEST_F(PeerConnectionJsepTest, VideoOnlyInitialOffer) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
|
|
auto offer = caller->CreateOffer();
|
|
auto contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, contents.size());
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[0].media_description()->type());
|
|
}
|
|
|
|
// Test that an initial offer with one data channel generates one data media
|
|
// section.
|
|
TEST_F(PeerConnectionJsepTest, DataOnlyInitialOffer) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->CreateDataChannel("dc");
|
|
|
|
auto offer = caller->CreateOffer();
|
|
auto contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, contents.size());
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_DATA, contents[0].media_description()->type());
|
|
}
|
|
|
|
// Test that creating multiple data channels only results in one data section
|
|
// generated in the offer.
|
|
TEST_F(PeerConnectionJsepTest, MultipleDataChannelsCreateOnlyOneDataSection) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->CreateDataChannel("first");
|
|
caller->CreateDataChannel("second");
|
|
caller->CreateDataChannel("third");
|
|
|
|
auto offer = caller->CreateOffer();
|
|
ASSERT_EQ(1u, offer->description()->contents().size());
|
|
}
|
|
|
|
// Test that multiple media sections in the initial offer are ordered in the
|
|
// order the transceivers were added to the PeerConnection. This is required by
|
|
// JSEP section 5.2.1.
|
|
TEST_F(PeerConnectionJsepTest, MediaSectionsInInitialOfferOrderedCorrectly) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
RtpTransceiverInit init;
|
|
init.direction = RtpTransceiverDirection::kSendOnly;
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
|
|
|
|
auto offer = caller->CreateOffer();
|
|
auto contents = offer->description()->contents();
|
|
ASSERT_EQ(3u, contents.size());
|
|
|
|
const MediaContentDescription* media_description1 =
|
|
contents[0].media_description();
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description1->type());
|
|
EXPECT_EQ(RtpTransceiverDirection::kSendRecv,
|
|
media_description1->direction());
|
|
|
|
const MediaContentDescription* media_description2 =
|
|
contents[1].media_description();
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, media_description2->type());
|
|
EXPECT_EQ(RtpTransceiverDirection::kSendRecv,
|
|
media_description2->direction());
|
|
|
|
const MediaContentDescription* media_description3 =
|
|
contents[2].media_description();
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, media_description3->type());
|
|
EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
|
|
media_description3->direction());
|
|
}
|
|
|
|
// Test that media sections in the initial offer have different mids.
|
|
TEST_F(PeerConnectionJsepTest, MediaSectionsInInitialOfferHaveDifferentMids) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
|
|
auto offer = caller->CreateOffer();
|
|
auto contents = offer->description()->contents();
|
|
ASSERT_EQ(2u, contents.size());
|
|
EXPECT_NE(contents[0].name, contents[1].name);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest,
|
|
StoppedTransceiverHasNoMediaSectionInInitialOffer) {
|
|
auto caller = CreatePeerConnection();
|
|
auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
transceiver->Stop();
|
|
|
|
auto offer = caller->CreateOffer();
|
|
EXPECT_EQ(0u, offer->description()->contents().size());
|
|
}
|
|
|
|
// Tests for JSEP SetLocalDescription with a local offer.
|
|
|
|
TEST_F(PeerConnectionJsepTest, SetLocalEmptyOfferCreatesNoTransceivers) {
|
|
auto caller = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
|
|
|
|
EXPECT_THAT(caller->pc()->GetTransceivers(), ElementsAre());
|
|
EXPECT_THAT(caller->pc()->GetSenders(), ElementsAre());
|
|
EXPECT_THAT(caller->pc()->GetReceivers(), ElementsAre());
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, SetLocalOfferSetsTransceiverMid) {
|
|
auto caller = CreatePeerConnection();
|
|
auto audio_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto video_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
|
|
auto offer = caller->CreateOffer();
|
|
std::string audio_mid = offer->description()->contents()[0].name;
|
|
std::string video_mid = offer->description()->contents()[1].name;
|
|
|
|
ASSERT_TRUE(caller->SetLocalDescription(std::move(offer)));
|
|
|
|
EXPECT_EQ(audio_mid, audio_transceiver->mid());
|
|
EXPECT_EQ(video_mid, video_transceiver->mid());
|
|
}
|
|
|
|
// Tests for JSEP SetRemoteDescription with a remote offer.
|
|
|
|
// Test that setting a remote offer with sendrecv audio and video creates two
|
|
// transceivers, one for receiving audio and one for receiving video.
|
|
TEST_F(PeerConnectionJsepTest, SetRemoteOfferCreatesTransceivers) {
|
|
auto caller = CreatePeerConnection();
|
|
auto caller_audio = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto caller_video = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto transceivers = callee->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, transceivers.size());
|
|
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, transceivers[0]->media_type());
|
|
EXPECT_EQ(caller_audio->mid(), transceivers[0]->mid());
|
|
EXPECT_EQ(RtpTransceiverDirection::kRecvOnly, transceivers[0]->direction());
|
|
EXPECT_EQ(0u, transceivers[0]->sender()->stream_ids().size());
|
|
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, transceivers[1]->media_type());
|
|
EXPECT_EQ(caller_video->mid(), transceivers[1]->mid());
|
|
EXPECT_EQ(RtpTransceiverDirection::kRecvOnly, transceivers[1]->direction());
|
|
EXPECT_EQ(0u, transceivers[1]->sender()->stream_ids().size());
|
|
}
|
|
|
|
// Test that setting a remote offer with an audio track will reuse the
|
|
// transceiver created for a local audio track added by AddTrack.
|
|
// This is specified in JSEP section 5.10 (Applying a Remote Description). The
|
|
// intent is to preserve backwards compatibility with clients who only use the
|
|
// AddTrack API.
|
|
TEST_F(PeerConnectionJsepTest, SetRemoteOfferReusesTransceiverFromAddTrack) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
auto caller_audio = caller->pc()->GetTransceivers()[0];
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("a");
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto transceivers = callee->pc()->GetTransceivers();
|
|
ASSERT_EQ(1u, transceivers.size());
|
|
EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
|
|
transceivers[0]->receiver()->track()->kind());
|
|
EXPECT_EQ(caller_audio->mid(), transceivers[0]->mid());
|
|
}
|
|
|
|
// Test that setting a remote offer with an audio track marked sendonly will not
|
|
// reuse a transceiver created by AddTrack. JSEP only allows the transceiver to
|
|
// be reused if the offer direction is sendrecv or recvonly.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
SetRemoteOfferDoesNotReuseTransceiverIfDirectionSendOnly) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
auto caller_audio = caller->pc()->GetTransceivers()[0];
|
|
caller_audio->SetDirection(RtpTransceiverDirection::kSendOnly);
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("a");
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto transceivers = callee->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, transceivers.size());
|
|
EXPECT_EQ(absl::nullopt, transceivers[0]->mid());
|
|
EXPECT_EQ(caller_audio->mid(), transceivers[1]->mid());
|
|
}
|
|
|
|
// Test that setting a remote offer with an audio track will not reuse a
|
|
// transceiver added by AddTransceiver. The logic for reusing a transceiver is
|
|
// specific to those added by AddTrack and is tested above.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
SetRemoteOfferDoesNotReuseTransceiverFromAddTransceiver) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
auto callee = CreatePeerConnection();
|
|
auto transceiver = callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto transceivers = callee->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, transceivers.size());
|
|
EXPECT_EQ(absl::nullopt, transceivers[0]->mid());
|
|
EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
|
|
EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
|
|
transceivers[1]->receiver()->track()->kind());
|
|
}
|
|
|
|
// Test that setting a remote offer with an audio track will not reuse a
|
|
// transceiver created for a local video track added by AddTrack.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
SetRemoteOfferDoesNotReuseTransceiverOfWrongType) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
auto callee = CreatePeerConnection();
|
|
auto video_sender = callee->AddVideoTrack("v");
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto transceivers = callee->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, transceivers.size());
|
|
EXPECT_EQ(absl::nullopt, transceivers[0]->mid());
|
|
EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
|
|
EXPECT_EQ(MediaStreamTrackInterface::kAudioKind,
|
|
transceivers[1]->receiver()->track()->kind());
|
|
}
|
|
|
|
// Test that setting a remote offer with an audio track will not reuse a
|
|
// stopped transceiver.
|
|
TEST_F(PeerConnectionJsepTest, SetRemoteOfferDoesNotReuseStoppedTransceiver) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("a");
|
|
callee->pc()->GetTransceivers()[0]->Stop();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto transceivers = callee->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, transceivers.size());
|
|
EXPECT_EQ(absl::nullopt, transceivers[0]->mid());
|
|
EXPECT_TRUE(transceivers[0]->stopped());
|
|
EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
|
|
EXPECT_FALSE(transceivers[1]->stopped());
|
|
}
|
|
|
|
// Test that audio and video transceivers created on the remote side with
|
|
// AddTrack will all be reused if there is the same number of audio/video tracks
|
|
// in the remote offer. Additionally, this tests that transceivers are
|
|
// successfully matched even if they are in a different order on the remote
|
|
// side.
|
|
TEST_F(PeerConnectionJsepTest, SetRemoteOfferReusesTransceiversOfBothTypes) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddVideoTrack("v");
|
|
caller->AddAudioTrack("a");
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("a");
|
|
callee->AddVideoTrack("v");
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto caller_transceivers = caller->pc()->GetTransceivers();
|
|
auto callee_transceivers = callee->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, callee_transceivers.size());
|
|
EXPECT_EQ(caller_transceivers[0]->mid(), callee_transceivers[1]->mid());
|
|
EXPECT_EQ(caller_transceivers[1]->mid(), callee_transceivers[0]->mid());
|
|
}
|
|
|
|
// Tests for JSEP initial CreateAnswer.
|
|
|
|
// Test that the answer to a remote offer creates media sections for each
|
|
// offered media in the same order and with the same mids.
|
|
TEST_F(PeerConnectionJsepTest, CreateAnswerHasSameMidsAsOffer) {
|
|
auto caller = CreatePeerConnection();
|
|
auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
auto second_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto third_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
caller->CreateDataChannel("dc");
|
|
auto callee = CreatePeerConnection();
|
|
|
|
auto offer = caller->CreateOffer();
|
|
const auto* offer_data = cricket::GetFirstDataContent(offer->description());
|
|
ASSERT_TRUE(
|
|
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
|
|
auto answer = callee->CreateAnswer();
|
|
auto contents = answer->description()->contents();
|
|
ASSERT_EQ(4u, contents.size());
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[0].media_description()->type());
|
|
EXPECT_EQ(first_transceiver->mid(), contents[0].name);
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[1].media_description()->type());
|
|
EXPECT_EQ(second_transceiver->mid(), contents[1].name);
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, contents[2].media_description()->type());
|
|
EXPECT_EQ(third_transceiver->mid(), contents[2].name);
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_DATA, contents[3].media_description()->type());
|
|
EXPECT_EQ(offer_data->name, contents[3].name);
|
|
}
|
|
|
|
// Test that an answering media section is marked as rejected if the underlying
|
|
// transceiver has been stopped.
|
|
TEST_F(PeerConnectionJsepTest, CreateAnswerRejectsStoppedTransceiver) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
callee->pc()->GetTransceivers()[0]->Stop();
|
|
|
|
auto answer = callee->CreateAnswer();
|
|
auto contents = answer->description()->contents();
|
|
ASSERT_EQ(1u, contents.size());
|
|
EXPECT_TRUE(contents[0].rejected);
|
|
}
|
|
|
|
// Test that CreateAnswer will generate media sections which will only send or
|
|
// receive if the offer indicates it can do the reciprocating direction.
|
|
// The full matrix is tested more extensively in MediaSession.
|
|
TEST_F(PeerConnectionJsepTest, CreateAnswerNegotiatesDirection) {
|
|
auto caller = CreatePeerConnection();
|
|
RtpTransceiverInit init;
|
|
init.direction = RtpTransceiverDirection::kSendOnly;
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("a");
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto answer = callee->CreateAnswer();
|
|
auto contents = answer->description()->contents();
|
|
ASSERT_EQ(1u, contents.size());
|
|
EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
|
|
contents[0].media_description()->direction());
|
|
}
|
|
|
|
// Tests for JSEP SetLocalDescription with a local answer.
|
|
// Note that these test only the additional behaviors not covered by
|
|
// SetLocalDescription with a local offer.
|
|
|
|
// Test that SetLocalDescription with an answer sets the current_direction
|
|
// property of the transceivers mentioned in the session description.
|
|
TEST_F(PeerConnectionJsepTest, SetLocalAnswerUpdatesCurrentDirection) {
|
|
auto caller = CreatePeerConnection();
|
|
auto caller_audio = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
caller_audio->SetDirection(RtpTransceiverDirection::kRecvOnly);
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("a");
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
|
|
|
|
auto transceivers = callee->pc()->GetTransceivers();
|
|
ASSERT_EQ(1u, transceivers.size());
|
|
// Since the offer was recvonly and the transceiver direction is sendrecv,
|
|
// the negotiated direction will be sendonly.
|
|
EXPECT_EQ(RtpTransceiverDirection::kSendOnly,
|
|
transceivers[0]->current_direction());
|
|
}
|
|
|
|
// Tests for JSEP SetRemoteDescription with a remote answer.
|
|
// Note that these test only the additional behaviors not covered by
|
|
// SetRemoteDescription with a remote offer.
|
|
|
|
TEST_F(PeerConnectionJsepTest, SetRemoteAnswerUpdatesCurrentDirection) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("a");
|
|
auto callee_audio = callee->pc()->GetTransceivers()[0];
|
|
callee_audio->SetDirection(RtpTransceiverDirection::kSendOnly);
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
ASSERT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
|
|
auto transceivers = caller->pc()->GetTransceivers();
|
|
ASSERT_EQ(1u, transceivers.size());
|
|
// Since the remote transceiver was set to sendonly, the negotiated direction
|
|
// in the answer would be sendonly which we apply as recvonly to the local
|
|
// transceiver.
|
|
EXPECT_EQ(RtpTransceiverDirection::kRecvOnly,
|
|
transceivers[0]->current_direction());
|
|
}
|
|
|
|
// Tests for multiple round trips.
|
|
|
|
// Test that setting a transceiver with the inactive direction does not stop it
|
|
// on either the caller or the callee.
|
|
TEST_F(PeerConnectionJsepTest, SettingTransceiverInactiveDoesNotStopIt) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("a");
|
|
callee->pc()->GetTransceivers()[0]->SetDirection(
|
|
RtpTransceiverDirection::kInactive);
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
ASSERT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
|
|
EXPECT_FALSE(caller->pc()->GetTransceivers()[0]->stopped());
|
|
EXPECT_FALSE(callee->pc()->GetTransceivers()[0]->stopped());
|
|
}
|
|
|
|
// Test that if a transceiver had been associated and later stopped, then a
|
|
// media section is still generated for it and the media section is marked as
|
|
// rejected.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
ReOfferMediaSectionForAssociatedStoppedTransceiverIsRejected) {
|
|
auto caller = CreatePeerConnection();
|
|
auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
ASSERT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
|
|
ASSERT_TRUE(transceiver->mid());
|
|
transceiver->Stop();
|
|
|
|
auto reoffer = caller->CreateOffer();
|
|
auto contents = reoffer->description()->contents();
|
|
ASSERT_EQ(1u, contents.size());
|
|
EXPECT_TRUE(contents[0].rejected);
|
|
}
|
|
|
|
// Test that stopping an associated transceiver on the caller side will stop the
|
|
// corresponding transceiver on the remote side when the remote offer is
|
|
// applied.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
StoppingTransceiverInOfferStopsTransceiverOnRemoteSide) {
|
|
auto caller = CreatePeerConnection();
|
|
auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
ASSERT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
|
|
transceiver->Stop();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto transceivers = callee->pc()->GetTransceivers();
|
|
EXPECT_TRUE(transceivers[0]->stopped());
|
|
EXPECT_TRUE(transceivers[0]->mid());
|
|
}
|
|
|
|
// Test that CreateOffer will only generate a recycled media section if the
|
|
// transceiver to be recycled has been seen stopped by the other side first.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
CreateOfferDoesNotRecycleMediaSectionIfFirstStopped) {
|
|
auto caller = CreatePeerConnection();
|
|
auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
ASSERT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
|
|
auto second_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
first_transceiver->Stop();
|
|
|
|
auto reoffer = caller->CreateOffer();
|
|
auto contents = reoffer->description()->contents();
|
|
ASSERT_EQ(2u, contents.size());
|
|
EXPECT_TRUE(contents[0].rejected);
|
|
EXPECT_FALSE(contents[1].rejected);
|
|
}
|
|
|
|
// Test that the offer/answer and the transceivers are correctly generated and
|
|
// updated when the media section is recycled after the callee stops a
|
|
// transceiver and sends an answer with a 0 port.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
RecycleMediaSectionWhenStoppingTransceiverOnAnswerer) {
|
|
auto caller = CreatePeerConnection();
|
|
auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
callee->pc()->GetTransceivers()[0]->Stop();
|
|
ASSERT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
EXPECT_TRUE(first_transceiver->stopped());
|
|
// First transceivers aren't dissociated yet.
|
|
ASSERT_NE(absl::nullopt, first_transceiver->mid());
|
|
std::string first_mid = *first_transceiver->mid();
|
|
EXPECT_EQ(first_mid, callee->pc()->GetTransceivers()[0]->mid());
|
|
|
|
// New offer exchange with new transceivers that recycles the m section
|
|
// correctly.
|
|
caller->AddAudioTrack("audio2");
|
|
callee->AddAudioTrack("audio2");
|
|
auto offer = caller->CreateOffer();
|
|
auto offer_contents = offer->description()->contents();
|
|
std::string second_mid = offer_contents[0].name;
|
|
ASSERT_EQ(1u, offer_contents.size());
|
|
EXPECT_FALSE(offer_contents[0].rejected);
|
|
EXPECT_NE(first_mid, second_mid);
|
|
|
|
// Setting the offer on each side will dissociate the first transceivers and
|
|
// associate the new transceivers.
|
|
ASSERT_TRUE(
|
|
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
EXPECT_EQ(absl::nullopt, first_transceiver->mid());
|
|
EXPECT_EQ(second_mid, caller->pc()->GetTransceivers()[1]->mid());
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
EXPECT_EQ(absl::nullopt, callee->pc()->GetTransceivers()[0]->mid());
|
|
EXPECT_EQ(second_mid, callee->pc()->GetTransceivers()[1]->mid());
|
|
|
|
// The new answer should also recycle the m section correctly.
|
|
auto answer = callee->CreateAnswer();
|
|
auto answer_contents = answer->description()->contents();
|
|
ASSERT_EQ(1u, answer_contents.size());
|
|
EXPECT_FALSE(answer_contents[0].rejected);
|
|
EXPECT_EQ(second_mid, answer_contents[0].name);
|
|
|
|
// Finishing the negotiation shouldn't add or dissociate any transceivers.
|
|
ASSERT_TRUE(
|
|
callee->SetLocalDescription(CloneSessionDescription(answer.get())));
|
|
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
|
auto caller_transceivers = caller->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, caller_transceivers.size());
|
|
EXPECT_EQ(absl::nullopt, caller_transceivers[0]->mid());
|
|
EXPECT_EQ(second_mid, caller_transceivers[1]->mid());
|
|
auto callee_transceivers = callee->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, callee_transceivers.size());
|
|
EXPECT_EQ(absl::nullopt, callee_transceivers[0]->mid());
|
|
EXPECT_EQ(second_mid, callee_transceivers[1]->mid());
|
|
}
|
|
|
|
// Test that creating/setting a local offer that recycles an m= section is
|
|
// idempotent.
|
|
TEST_F(PeerConnectionJsepTest, CreateOfferRecyclesWhenOfferingTwice) {
|
|
// Do a negotiation with a port 0 for the media section.
|
|
auto caller = CreatePeerConnection();
|
|
auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
first_transceiver->Stop();
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
caller->AddAudioTrack("audio2");
|
|
|
|
// Create a new offer that recycles the media section and set it as a local
|
|
// description.
|
|
auto offer = caller->CreateOffer();
|
|
auto offer_contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, offer_contents.size());
|
|
EXPECT_FALSE(offer_contents[0].rejected);
|
|
ASSERT_TRUE(caller->SetLocalDescription(std::move(offer)));
|
|
EXPECT_FALSE(caller->pc()->GetTransceivers()[1]->stopped());
|
|
std::string second_mid = offer_contents[0].name;
|
|
|
|
// Create another new offer and set the local description again without the
|
|
// rest of any negotation ocurring.
|
|
auto second_offer = caller->CreateOffer();
|
|
auto second_offer_contents = second_offer->description()->contents();
|
|
ASSERT_EQ(1u, second_offer_contents.size());
|
|
EXPECT_FALSE(second_offer_contents[0].rejected);
|
|
// The mid shouldn't change.
|
|
EXPECT_EQ(second_mid, second_offer_contents[0].name);
|
|
|
|
ASSERT_TRUE(caller->SetLocalDescription(std::move(second_offer)));
|
|
// Make sure that the caller's transceivers are associated correctly.
|
|
auto caller_transceivers = caller->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, caller_transceivers.size());
|
|
EXPECT_EQ(absl::nullopt, caller_transceivers[0]->mid());
|
|
EXPECT_EQ(second_mid, caller_transceivers[1]->mid());
|
|
EXPECT_FALSE(caller_transceivers[1]->stopped());
|
|
}
|
|
|
|
// Test that the offer/answer and transceivers for both the caller and callee
|
|
// side are generated/updated correctly when recycling an audio/video media
|
|
// section as a media section of either the same or opposite type.
|
|
// Correct recycling works as follows:
|
|
// - The m= section is re-offered with a new MID value and the new media type.
|
|
// - The previously-associated transceiver is dissociated when the new offer is
|
|
// set as a local description on the offerer or as a remote description on
|
|
// the answerer.
|
|
// - The new transceiver is associated with the new MID value.
|
|
class RecycleMediaSectionTest
|
|
: public PeerConnectionJsepTest,
|
|
public ::testing::WithParamInterface<
|
|
std::tuple<cricket::MediaType, cricket::MediaType>> {
|
|
protected:
|
|
RecycleMediaSectionTest() {
|
|
first_type_ = std::get<0>(GetParam());
|
|
second_type_ = std::get<1>(GetParam());
|
|
}
|
|
|
|
cricket::MediaType first_type_;
|
|
cricket::MediaType second_type_;
|
|
};
|
|
|
|
// Test that recycling works properly when a new transceiver recycles an m=
|
|
// section that was rejected in both the current local and remote descriptions.
|
|
TEST_P(RecycleMediaSectionTest, CurrentLocalAndCurrentRemoteRejected) {
|
|
auto caller = CreatePeerConnection();
|
|
auto first_transceiver = caller->AddTransceiver(first_type_);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
std::string first_mid = *first_transceiver->mid();
|
|
first_transceiver->Stop();
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
auto second_transceiver = caller->AddTransceiver(second_type_);
|
|
|
|
// The offer should reuse the previous media section but allocate a new MID
|
|
// and change the media type.
|
|
auto offer = caller->CreateOffer();
|
|
auto offer_contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, offer_contents.size());
|
|
EXPECT_FALSE(offer_contents[0].rejected);
|
|
EXPECT_EQ(second_type_, offer_contents[0].media_description()->type());
|
|
std::string second_mid = offer_contents[0].name;
|
|
EXPECT_NE(first_mid, second_mid);
|
|
|
|
// Setting the local offer will dissociate the previous transceiver and set
|
|
// the MID for the new transceiver.
|
|
ASSERT_TRUE(
|
|
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
EXPECT_EQ(absl::nullopt, first_transceiver->mid());
|
|
EXPECT_EQ(second_mid, second_transceiver->mid());
|
|
|
|
// Setting the remote offer will dissociate the previous transceiver and
|
|
// create a new transceiver for the media section.
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
auto callee_transceivers = callee->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, callee_transceivers.size());
|
|
EXPECT_EQ(absl::nullopt, callee_transceivers[0]->mid());
|
|
EXPECT_EQ(first_type_, callee_transceivers[0]->media_type());
|
|
EXPECT_EQ(second_mid, callee_transceivers[1]->mid());
|
|
EXPECT_EQ(second_type_, callee_transceivers[1]->media_type());
|
|
|
|
// The answer should have only one media section for the new transceiver.
|
|
auto answer = callee->CreateAnswer();
|
|
auto answer_contents = answer->description()->contents();
|
|
ASSERT_EQ(1u, answer_contents.size());
|
|
EXPECT_FALSE(answer_contents[0].rejected);
|
|
EXPECT_EQ(second_mid, answer_contents[0].name);
|
|
EXPECT_EQ(second_type_, answer_contents[0].media_description()->type());
|
|
|
|
// Setting the local answer should succeed.
|
|
ASSERT_TRUE(
|
|
callee->SetLocalDescription(CloneSessionDescription(answer.get())));
|
|
|
|
// Setting the remote answer should succeed and not create any new
|
|
// transceivers.
|
|
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
|
ASSERT_EQ(2u, caller->pc()->GetTransceivers().size());
|
|
ASSERT_EQ(2u, callee->pc()->GetTransceivers().size());
|
|
}
|
|
|
|
// Test that recycling works properly when a new transceiver recycles an m=
|
|
// section that was rejected in only the current remote description.
|
|
TEST_P(RecycleMediaSectionTest, CurrentRemoteOnlyRejected) {
|
|
auto caller = CreatePeerConnection();
|
|
auto caller_first_transceiver = caller->AddTransceiver(first_type_);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
std::string first_mid = *caller_first_transceiver->mid();
|
|
ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
|
|
auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
|
|
callee_first_transceiver->Stop();
|
|
|
|
// The answer will have a rejected m= section.
|
|
ASSERT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
|
|
// The offer should reuse the previous media section but allocate a new MID
|
|
// and change the media type.
|
|
auto caller_second_transceiver = caller->AddTransceiver(second_type_);
|
|
auto offer = caller->CreateOffer();
|
|
const auto& offer_contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, offer_contents.size());
|
|
EXPECT_FALSE(offer_contents[0].rejected);
|
|
EXPECT_EQ(second_type_, offer_contents[0].media_description()->type());
|
|
std::string second_mid = offer_contents[0].name;
|
|
EXPECT_NE(first_mid, second_mid);
|
|
|
|
// Setting the local offer will dissociate the previous transceiver and set
|
|
// the MID for the new transceiver.
|
|
ASSERT_TRUE(
|
|
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
EXPECT_EQ(absl::nullopt, caller_first_transceiver->mid());
|
|
EXPECT_EQ(second_mid, caller_second_transceiver->mid());
|
|
|
|
// Setting the remote offer will dissociate the previous transceiver and
|
|
// create a new transceiver for the media section.
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
auto callee_transceivers = callee->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, callee_transceivers.size());
|
|
EXPECT_EQ(absl::nullopt, callee_transceivers[0]->mid());
|
|
EXPECT_EQ(first_type_, callee_transceivers[0]->media_type());
|
|
EXPECT_EQ(second_mid, callee_transceivers[1]->mid());
|
|
EXPECT_EQ(second_type_, callee_transceivers[1]->media_type());
|
|
|
|
// The answer should have only one media section for the new transceiver.
|
|
auto answer = callee->CreateAnswer();
|
|
auto answer_contents = answer->description()->contents();
|
|
ASSERT_EQ(1u, answer_contents.size());
|
|
EXPECT_FALSE(answer_contents[0].rejected);
|
|
EXPECT_EQ(second_mid, answer_contents[0].name);
|
|
EXPECT_EQ(second_type_, answer_contents[0].media_description()->type());
|
|
|
|
// Setting the local answer should succeed.
|
|
ASSERT_TRUE(
|
|
callee->SetLocalDescription(CloneSessionDescription(answer.get())));
|
|
|
|
// Setting the remote answer should succeed and not create any new
|
|
// transceivers.
|
|
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
|
ASSERT_EQ(2u, caller->pc()->GetTransceivers().size());
|
|
ASSERT_EQ(2u, callee->pc()->GetTransceivers().size());
|
|
}
|
|
|
|
// Test that recycling works properly when a new transceiver recycles an m=
|
|
// section that was rejected only in the current local description.
|
|
TEST_P(RecycleMediaSectionTest, CurrentLocalOnlyRejected) {
|
|
auto caller = CreatePeerConnection();
|
|
auto caller_first_transceiver = caller->AddTransceiver(first_type_);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
std::string first_mid = *caller_first_transceiver->mid();
|
|
ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
|
|
auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
|
|
callee_first_transceiver->Stop();
|
|
|
|
// The answer will have a rejected m= section.
|
|
ASSERT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
|
|
// The offer should reuse the previous media section but allocate a new MID
|
|
// and change the media type.
|
|
auto callee_second_transceiver = callee->AddTransceiver(second_type_);
|
|
auto offer = callee->CreateOffer();
|
|
const auto& offer_contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, offer_contents.size());
|
|
EXPECT_FALSE(offer_contents[0].rejected);
|
|
EXPECT_EQ(second_type_, offer_contents[0].media_description()->type());
|
|
std::string second_mid = offer_contents[0].name;
|
|
EXPECT_NE(first_mid, second_mid);
|
|
|
|
// Setting the local offer will dissociate the previous transceiver and set
|
|
// the MID for the new transceiver.
|
|
ASSERT_TRUE(
|
|
callee->SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
EXPECT_EQ(absl::nullopt, callee_first_transceiver->mid());
|
|
EXPECT_EQ(second_mid, callee_second_transceiver->mid());
|
|
|
|
// Setting the remote offer will dissociate the previous transceiver and
|
|
// create a new transceiver for the media section.
|
|
ASSERT_TRUE(caller->SetRemoteDescription(std::move(offer)));
|
|
auto caller_transceivers = caller->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, caller_transceivers.size());
|
|
EXPECT_EQ(absl::nullopt, caller_transceivers[0]->mid());
|
|
EXPECT_EQ(first_type_, caller_transceivers[0]->media_type());
|
|
EXPECT_EQ(second_mid, caller_transceivers[1]->mid());
|
|
EXPECT_EQ(second_type_, caller_transceivers[1]->media_type());
|
|
|
|
// The answer should have only one media section for the new transceiver.
|
|
auto answer = caller->CreateAnswer();
|
|
auto answer_contents = answer->description()->contents();
|
|
ASSERT_EQ(1u, answer_contents.size());
|
|
EXPECT_FALSE(answer_contents[0].rejected);
|
|
EXPECT_EQ(second_mid, answer_contents[0].name);
|
|
EXPECT_EQ(second_type_, answer_contents[0].media_description()->type());
|
|
|
|
// Setting the local answer should succeed.
|
|
ASSERT_TRUE(
|
|
caller->SetLocalDescription(CloneSessionDescription(answer.get())));
|
|
|
|
// Setting the remote answer should succeed and not create any new
|
|
// transceivers.
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
|
|
ASSERT_EQ(2u, callee->pc()->GetTransceivers().size());
|
|
ASSERT_EQ(2u, caller->pc()->GetTransceivers().size());
|
|
}
|
|
|
|
// Test that a m= section is *not* recycled if the media section is only
|
|
// rejected in the pending local description and there is no current remote
|
|
// description.
|
|
TEST_P(RecycleMediaSectionTest, PendingLocalRejectedAndNoRemote) {
|
|
auto caller = CreatePeerConnection();
|
|
auto caller_first_transceiver = caller->AddTransceiver(first_type_);
|
|
|
|
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
|
|
|
|
std::string first_mid = *caller_first_transceiver->mid();
|
|
caller_first_transceiver->Stop();
|
|
|
|
// The reoffer will have a rejected m= section.
|
|
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
|
|
|
|
auto caller_second_transceiver = caller->AddTransceiver(second_type_);
|
|
|
|
// The reoffer should not recycle the existing m= section since it is not
|
|
// rejected in either the *current* local or *current* remote description.
|
|
auto reoffer = caller->CreateOffer();
|
|
auto reoffer_contents = reoffer->description()->contents();
|
|
ASSERT_EQ(2u, reoffer_contents.size());
|
|
EXPECT_TRUE(reoffer_contents[0].rejected);
|
|
EXPECT_EQ(first_type_, reoffer_contents[0].media_description()->type());
|
|
EXPECT_EQ(first_mid, reoffer_contents[0].name);
|
|
EXPECT_FALSE(reoffer_contents[1].rejected);
|
|
EXPECT_EQ(second_type_, reoffer_contents[1].media_description()->type());
|
|
std::string second_mid = reoffer_contents[1].name;
|
|
EXPECT_NE(first_mid, second_mid);
|
|
|
|
ASSERT_TRUE(caller->SetLocalDescription(std::move(reoffer)));
|
|
|
|
// Both RtpTransceivers are associated.
|
|
EXPECT_EQ(first_mid, caller_first_transceiver->mid());
|
|
EXPECT_EQ(second_mid, caller_second_transceiver->mid());
|
|
}
|
|
|
|
// Test that a m= section is *not* recycled if the media section is only
|
|
// rejected in the pending local description and not rejected in the current
|
|
// remote description.
|
|
TEST_P(RecycleMediaSectionTest, PendingLocalRejectedAndNotRejectedRemote) {
|
|
auto caller = CreatePeerConnection();
|
|
auto caller_first_transceiver = caller->AddTransceiver(first_type_);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
std::string first_mid = *caller_first_transceiver->mid();
|
|
caller_first_transceiver->Stop();
|
|
|
|
// The reoffer will have a rejected m= section.
|
|
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
|
|
|
|
auto caller_second_transceiver = caller->AddTransceiver(second_type_);
|
|
|
|
// The reoffer should not recycle the existing m= section since it is not
|
|
// rejected in either the *current* local or *current* remote description.
|
|
auto reoffer = caller->CreateOffer();
|
|
auto reoffer_contents = reoffer->description()->contents();
|
|
ASSERT_EQ(2u, reoffer_contents.size());
|
|
EXPECT_TRUE(reoffer_contents[0].rejected);
|
|
EXPECT_EQ(first_type_, reoffer_contents[0].media_description()->type());
|
|
EXPECT_EQ(first_mid, reoffer_contents[0].name);
|
|
EXPECT_FALSE(reoffer_contents[1].rejected);
|
|
EXPECT_EQ(second_type_, reoffer_contents[1].media_description()->type());
|
|
std::string second_mid = reoffer_contents[1].name;
|
|
EXPECT_NE(first_mid, second_mid);
|
|
|
|
ASSERT_TRUE(caller->SetLocalDescription(std::move(reoffer)));
|
|
|
|
// Both RtpTransceivers are associated.
|
|
EXPECT_EQ(first_mid, caller_first_transceiver->mid());
|
|
EXPECT_EQ(second_mid, caller_second_transceiver->mid());
|
|
}
|
|
|
|
// Test that an m= section is *not* recycled if the media section is only
|
|
// rejected in the pending remote description and there is no current local
|
|
// description.
|
|
TEST_P(RecycleMediaSectionTest, PendingRemoteRejectedAndNoLocal) {
|
|
auto caller = CreatePeerConnection();
|
|
auto caller_first_transceiver = caller->AddTransceiver(first_type_);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
|
|
auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
|
|
std::string first_mid = *callee_first_transceiver->mid();
|
|
caller_first_transceiver->Stop();
|
|
|
|
// The reoffer will have a rejected m= section.
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto callee_second_transceiver = callee->AddTransceiver(second_type_);
|
|
|
|
// The reoffer should not recycle the existing m= section since it is not
|
|
// rejected in either the *current* local or *current* remote description.
|
|
auto reoffer = callee->CreateOffer();
|
|
auto reoffer_contents = reoffer->description()->contents();
|
|
ASSERT_EQ(2u, reoffer_contents.size());
|
|
EXPECT_TRUE(reoffer_contents[0].rejected);
|
|
EXPECT_EQ(first_type_, reoffer_contents[0].media_description()->type());
|
|
EXPECT_EQ(first_mid, reoffer_contents[0].name);
|
|
EXPECT_FALSE(reoffer_contents[1].rejected);
|
|
EXPECT_EQ(second_type_, reoffer_contents[1].media_description()->type());
|
|
std::string second_mid = reoffer_contents[1].name;
|
|
EXPECT_NE(first_mid, second_mid);
|
|
|
|
// Note: Cannot actually set the reoffer since the callee is in the signaling
|
|
// state 'have-remote-offer'.
|
|
}
|
|
|
|
// Test that an m= section is *not* recycled if the media section is only
|
|
// rejected in the pending remote description and not rejected in the current
|
|
// local description.
|
|
TEST_P(RecycleMediaSectionTest, PendingRemoteRejectedAndNotRejectedLocal) {
|
|
auto caller = CreatePeerConnection();
|
|
auto caller_first_transceiver = caller->AddTransceiver(first_type_);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
|
|
auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
|
|
std::string first_mid = *callee_first_transceiver->mid();
|
|
caller_first_transceiver->Stop();
|
|
|
|
// The reoffer will have a rejected m= section.
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto callee_second_transceiver = callee->AddTransceiver(second_type_);
|
|
|
|
// The reoffer should not recycle the existing m= section since it is not
|
|
// rejected in either the *current* local or *current* remote description.
|
|
auto reoffer = callee->CreateOffer();
|
|
auto reoffer_contents = reoffer->description()->contents();
|
|
ASSERT_EQ(2u, reoffer_contents.size());
|
|
EXPECT_TRUE(reoffer_contents[0].rejected);
|
|
EXPECT_EQ(first_type_, reoffer_contents[0].media_description()->type());
|
|
EXPECT_EQ(first_mid, reoffer_contents[0].name);
|
|
EXPECT_FALSE(reoffer_contents[1].rejected);
|
|
EXPECT_EQ(second_type_, reoffer_contents[1].media_description()->type());
|
|
std::string second_mid = reoffer_contents[1].name;
|
|
EXPECT_NE(first_mid, second_mid);
|
|
|
|
// Note: Cannot actually set the reoffer since the callee is in the signaling
|
|
// state 'have-remote-offer'.
|
|
}
|
|
|
|
// Test all combinations of audio and video as the first and second media type
|
|
// for the media section. This is needed for full test coverage because
|
|
// MediaSession has separate functions for processing audio and video media
|
|
// sections.
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
PeerConnectionJsepTest,
|
|
RecycleMediaSectionTest,
|
|
Combine(Values(cricket::MEDIA_TYPE_AUDIO, cricket::MEDIA_TYPE_VIDEO),
|
|
Values(cricket::MEDIA_TYPE_AUDIO, cricket::MEDIA_TYPE_VIDEO)));
|
|
|
|
// Test that a new data channel section will not reuse a recycleable audio or
|
|
// video media section. Additionally, tests that the new section is added to the
|
|
// end of the session description.
|
|
TEST_F(PeerConnectionJsepTest, DataChannelDoesNotRecycleMediaSection) {
|
|
auto caller = CreatePeerConnection();
|
|
auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
transceiver->Stop();
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
caller->CreateDataChannel("dc");
|
|
|
|
auto offer = caller->CreateOffer();
|
|
auto offer_contents = offer->description()->contents();
|
|
ASSERT_EQ(2u, offer_contents.size());
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO,
|
|
offer_contents[0].media_description()->type());
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_DATA,
|
|
offer_contents[1].media_description()->type());
|
|
|
|
ASSERT_TRUE(
|
|
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
|
|
auto answer = callee->CreateAnswer();
|
|
auto answer_contents = answer->description()->contents();
|
|
ASSERT_EQ(2u, answer_contents.size());
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO,
|
|
answer_contents[0].media_description()->type());
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_DATA,
|
|
answer_contents[1].media_description()->type());
|
|
}
|
|
|
|
// Test that if a new track is added to an existing session that has a data,
|
|
// the new section comes at the end of the new offer, after the existing data
|
|
// section.
|
|
TEST_F(PeerConnectionJsepTest, AudioTrackAddedAfterDataSectionInReoffer) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->CreateDataChannel("dc");
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
caller->AddAudioTrack("a");
|
|
|
|
auto offer = caller->CreateOffer();
|
|
auto contents = offer->description()->contents();
|
|
ASSERT_EQ(2u, contents.size());
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_DATA, contents[0].media_description()->type());
|
|
EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, contents[1].media_description()->type());
|
|
}
|
|
|
|
// Tests for MID properties.
|
|
|
|
static void RenameSection(size_t mline_index,
|
|
const std::string& new_mid,
|
|
SessionDescriptionInterface* sdesc) {
|
|
cricket::SessionDescription* desc = sdesc->description();
|
|
std::string old_mid = desc->contents()[mline_index].name;
|
|
desc->contents()[mline_index].name = new_mid;
|
|
desc->transport_infos()[mline_index].content_name = new_mid;
|
|
const cricket::ContentGroup* bundle =
|
|
desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
|
|
if (bundle) {
|
|
cricket::ContentGroup new_bundle = *bundle;
|
|
if (new_bundle.RemoveContentName(old_mid)) {
|
|
new_bundle.AddContentName(new_mid);
|
|
}
|
|
desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
|
|
desc->AddGroup(new_bundle);
|
|
}
|
|
}
|
|
|
|
// Test that two PeerConnections can have a successful offer/answer exchange if
|
|
// the MIDs are changed from the defaults.
|
|
TEST_F(PeerConnectionJsepTest, OfferAnswerWithChangedMids) {
|
|
constexpr char kFirstMid[] = "nondefaultmid";
|
|
constexpr char kSecondMid[] = "randommid";
|
|
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
caller->AddAudioTrack("b");
|
|
auto callee = CreatePeerConnection();
|
|
|
|
auto offer = caller->CreateOffer();
|
|
RenameSection(0, kFirstMid, offer.get());
|
|
RenameSection(1, kSecondMid, offer.get());
|
|
|
|
ASSERT_TRUE(
|
|
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
auto caller_transceivers = caller->pc()->GetTransceivers();
|
|
EXPECT_EQ(kFirstMid, caller_transceivers[0]->mid());
|
|
EXPECT_EQ(kSecondMid, caller_transceivers[1]->mid());
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
auto callee_transceivers = callee->pc()->GetTransceivers();
|
|
EXPECT_EQ(kFirstMid, callee_transceivers[0]->mid());
|
|
EXPECT_EQ(kSecondMid, callee_transceivers[1]->mid());
|
|
|
|
auto answer = callee->CreateAnswer();
|
|
auto answer_contents = answer->description()->contents();
|
|
EXPECT_EQ(kFirstMid, answer_contents[0].name);
|
|
EXPECT_EQ(kSecondMid, answer_contents[1].name);
|
|
|
|
ASSERT_TRUE(
|
|
callee->SetLocalDescription(CloneSessionDescription(answer.get())));
|
|
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
|
}
|
|
|
|
// Test that CreateOffer will generate a MID that is not already used if the
|
|
// default it would have picked is already taken. This is tested by using a
|
|
// third PeerConnection to determine what the default would be for the second
|
|
// media section then setting that as the first media section's MID.
|
|
TEST_F(PeerConnectionJsepTest, CreateOfferGeneratesUniqueMidIfAlreadyTaken) {
|
|
// First, find what the default MID is for the second media section.
|
|
auto pc = CreatePeerConnection();
|
|
pc->AddAudioTrack("a");
|
|
pc->AddAudioTrack("b");
|
|
auto default_offer = pc->CreateOffer();
|
|
std::string default_second_mid =
|
|
default_offer->description()->contents()[1].name;
|
|
|
|
// Now, do an offer/answer with one track which has the MID set to the default
|
|
// second MID.
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
auto callee = CreatePeerConnection();
|
|
|
|
auto offer = caller->CreateOffer();
|
|
RenameSection(0, default_second_mid, offer.get());
|
|
|
|
ASSERT_TRUE(
|
|
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
// Add a second track and ensure that the MID is different.
|
|
caller->AddAudioTrack("b");
|
|
|
|
auto reoffer = caller->CreateOffer();
|
|
auto reoffer_contents = reoffer->description()->contents();
|
|
EXPECT_EQ(default_second_mid, reoffer_contents[0].name);
|
|
EXPECT_NE(reoffer_contents[0].name, reoffer_contents[1].name);
|
|
}
|
|
|
|
// Test that if an audio or video section has the default data section MID, then
|
|
// CreateOffer will generate a unique MID for the newly added data section.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
CreateOfferGeneratesUniqueMidForDataSectionIfAlreadyTaken) {
|
|
// First, find what the default MID is for the data channel.
|
|
auto pc = CreatePeerConnection();
|
|
pc->CreateDataChannel("dc");
|
|
auto default_offer = pc->CreateOffer();
|
|
std::string default_data_mid =
|
|
default_offer->description()->contents()[0].name;
|
|
|
|
// Now do an offer/answer with one audio track which has a MID set to the
|
|
// default data MID.
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
auto callee = CreatePeerConnection();
|
|
|
|
auto offer = caller->CreateOffer();
|
|
RenameSection(0, default_data_mid, offer.get());
|
|
|
|
ASSERT_TRUE(
|
|
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
// Add a data channel and ensure that the MID is different.
|
|
caller->CreateDataChannel("dc");
|
|
|
|
auto reoffer = caller->CreateOffer();
|
|
auto reoffer_contents = reoffer->description()->contents();
|
|
EXPECT_EQ(default_data_mid, reoffer_contents[0].name);
|
|
EXPECT_NE(reoffer_contents[0].name, reoffer_contents[1].name);
|
|
}
|
|
|
|
// Test that a reoffer initiated by the callee adds a new track to the caller.
|
|
TEST_F(PeerConnectionJsepTest, CalleeDoesReoffer) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a");
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("a");
|
|
callee->AddVideoTrack("v");
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
EXPECT_EQ(1u, caller->pc()->GetTransceivers().size());
|
|
EXPECT_EQ(2u, callee->pc()->GetTransceivers().size());
|
|
|
|
ASSERT_TRUE(callee->ExchangeOfferAnswerWith(caller.get()));
|
|
|
|
EXPECT_EQ(2u, caller->pc()->GetTransceivers().size());
|
|
EXPECT_EQ(2u, callee->pc()->GetTransceivers().size());
|
|
}
|
|
|
|
// Tests for MSID properties.
|
|
|
|
// Test that adding a track with AddTrack results in an offer that signals the
|
|
// track's ID.
|
|
TEST_F(PeerConnectionJsepTest, AddingTrackWithAddTrackSpecifiesTrackId) {
|
|
const std::string kTrackId = "audio_track";
|
|
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack(kTrackId);
|
|
|
|
auto offer = caller->CreateOffer();
|
|
auto contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, contents.size());
|
|
auto streams = contents[0].media_description()->streams();
|
|
ASSERT_EQ(1u, streams.size());
|
|
EXPECT_EQ(kTrackId, streams[0].id);
|
|
}
|
|
|
|
// Test that adding a track by calling AddTransceiver then SetTrack results in
|
|
// an offer that does not signal the track's ID and signals a random ID.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
AddingTrackWithAddTransceiverSpecifiesRandomTrackId) {
|
|
const std::string kTrackId = "audio_track";
|
|
|
|
auto caller = CreatePeerConnection();
|
|
auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
transceiver->sender()->SetTrack(caller->CreateAudioTrack(kTrackId));
|
|
|
|
auto offer = caller->CreateOffer();
|
|
auto contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, contents.size());
|
|
auto streams = contents[0].media_description()->streams();
|
|
ASSERT_EQ(1u, streams.size());
|
|
EXPECT_NE(kTrackId, streams[0].id);
|
|
}
|
|
|
|
// Test that if the transceiver is recvonly or inactive, then no MSID
|
|
// information is included in the offer.
|
|
TEST_F(PeerConnectionJsepTest, NoMsidInOfferIfTransceiverDirectionHasNoSend) {
|
|
auto caller = CreatePeerConnection();
|
|
|
|
RtpTransceiverInit init_recvonly;
|
|
init_recvonly.direction = RtpTransceiverDirection::kRecvOnly;
|
|
ASSERT_TRUE(caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init_recvonly));
|
|
|
|
RtpTransceiverInit init_inactive;
|
|
init_inactive.direction = RtpTransceiverDirection::kInactive;
|
|
ASSERT_TRUE(caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init_inactive));
|
|
|
|
auto offer = caller->CreateOffer();
|
|
auto contents = offer->description()->contents();
|
|
ASSERT_EQ(2u, contents.size());
|
|
// MSID is specified in the first stream, so no streams means no MSID.
|
|
EXPECT_EQ(0u, contents[0].media_description()->streams().size());
|
|
EXPECT_EQ(0u, contents[1].media_description()->streams().size());
|
|
}
|
|
|
|
// Test that if an answer negotiates transceiver directions of recvonly or
|
|
// inactive, then no MSID information is included in the answer.
|
|
TEST_F(PeerConnectionJsepTest, NoMsidInAnswerIfNoRespondingTracks) {
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
|
|
// recvonly transceiver will get negotiated to inactive since the callee has
|
|
// no tracks to send in response.
|
|
RtpTransceiverInit init_recvonly;
|
|
init_recvonly.direction = RtpTransceiverDirection::kRecvOnly;
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init_recvonly);
|
|
|
|
// sendrecv transceiver will get negotiated to recvonly since the callee has
|
|
// no tracks to send in response.
|
|
RtpTransceiverInit init_sendrecv;
|
|
init_sendrecv.direction = RtpTransceiverDirection::kSendRecv;
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init_sendrecv);
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto answer = callee->CreateAnswer();
|
|
auto contents = answer->description()->contents();
|
|
ASSERT_EQ(2u, contents.size());
|
|
// MSID is specified in the first stream, so no streams means no MSID.
|
|
EXPECT_EQ(0u, contents[0].media_description()->streams().size());
|
|
EXPECT_EQ(0u, contents[1].media_description()->streams().size());
|
|
}
|
|
|
|
// Test that the MSID is included even if the transceiver direction has changed
|
|
// to inactive if the transceiver had previously sent media.
|
|
TEST_F(PeerConnectionJsepTest, IncludeMsidEvenIfDirectionHasChanged) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("audio");
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("audio");
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
caller->pc()->GetTransceivers()[0]->SetDirection(
|
|
RtpTransceiverDirection::kInactive);
|
|
|
|
// The transceiver direction on both sides will turn to inactive.
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
auto* offer = callee->pc()->remote_description();
|
|
auto offer_contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, offer_contents.size());
|
|
// MSID is specified in the first stream. If it is present, assume that MSID
|
|
// is there.
|
|
EXPECT_EQ(1u, offer_contents[0].media_description()->streams().size());
|
|
|
|
auto* answer = caller->pc()->remote_description();
|
|
auto answer_contents = answer->description()->contents();
|
|
ASSERT_EQ(1u, answer_contents.size());
|
|
EXPECT_EQ(1u, answer_contents[0].media_description()->streams().size());
|
|
}
|
|
|
|
// Test that stopping a RtpTransceiver will cause future offers to not include
|
|
// any MSID information for that section.
|
|
TEST_F(PeerConnectionJsepTest, RemoveMsidIfTransceiverStopped) {
|
|
auto caller = CreatePeerConnection();
|
|
auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
transceiver->Stop();
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
auto* offer = callee->pc()->remote_description();
|
|
auto offer_contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, offer_contents.size());
|
|
// MSID is specified in the first stream, so no streams means no MSID.
|
|
EXPECT_EQ(0u, offer_contents[0].media_description()->streams().size());
|
|
}
|
|
|
|
// Test that the callee RtpReceiver created by a call to SetRemoteDescription
|
|
// has its ID set to the signaled track ID.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
RtpReceiverCreatedBySetRemoteDescriptionHasSignaledTrackId) {
|
|
const std::string kTrackId = "audio_track";
|
|
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
caller->AddAudioTrack(kTrackId);
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
ASSERT_EQ(1u, callee->pc()->GetReceivers().size());
|
|
auto receiver = callee->pc()->GetReceivers()[0];
|
|
EXPECT_EQ(kTrackId, receiver->id());
|
|
}
|
|
|
|
// Test that if the callee RtpReceiver is reused by a call to
|
|
// SetRemoteDescription, its ID does not change.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
RtpReceiverCreatedBeforeSetRemoteDescriptionKeepsId) {
|
|
const std::string kTrackId = "audio_track";
|
|
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
caller->AddAudioTrack(kTrackId);
|
|
callee->AddAudioTrack("dummy_track");
|
|
|
|
ASSERT_EQ(1u, callee->pc()->GetReceivers().size());
|
|
auto receiver = callee->pc()->GetReceivers()[0];
|
|
std::string receiver_id = receiver->id();
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
EXPECT_EQ(receiver_id, receiver->id());
|
|
}
|
|
|
|
// Test that setting a remote offer with one track that has no streams fires off
|
|
// the correct OnAddTrack event.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
SetRemoteOfferWithOneTrackNoStreamFiresOnAddTrack) {
|
|
const std::string kTrackLabel = "audio_track";
|
|
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
ASSERT_TRUE(caller->AddAudioTrack(kTrackLabel));
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
const auto& track_events = callee->observer()->add_track_events_;
|
|
ASSERT_EQ(1u, track_events.size());
|
|
const auto& event = track_events[0];
|
|
EXPECT_EQ(kTrackLabel, event.receiver->track()->id());
|
|
EXPECT_EQ(0u, event.streams.size());
|
|
}
|
|
|
|
// Test that setting a remote offer with one track that has one stream fires off
|
|
// the correct OnAddTrack event.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
SetRemoteOfferWithOneTrackOneStreamFiresOnAddTrack) {
|
|
const std::string kTrackLabel = "audio_track";
|
|
const std::string kStreamId = "audio_stream";
|
|
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
ASSERT_TRUE(caller->AddAudioTrack(kTrackLabel, {kStreamId}));
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
const auto& track_events = callee->observer()->add_track_events_;
|
|
ASSERT_EQ(1u, track_events.size());
|
|
const auto& event = track_events[0];
|
|
ASSERT_EQ(1u, event.streams.size());
|
|
auto stream = event.streams[0];
|
|
EXPECT_EQ(kStreamId, stream->id());
|
|
EXPECT_THAT(track_events[0].snapshotted_stream_tracks.at(stream),
|
|
ElementsAre(event.receiver->track()));
|
|
EXPECT_EQ(event.receiver->streams(), track_events[0].streams);
|
|
}
|
|
|
|
// Test that setting a remote offer with two tracks that share the same stream
|
|
// fires off two OnAddTrack events, both with the same stream that has both
|
|
// tracks present at the time of firing. This is to ensure that track events are
|
|
// not fired until SetRemoteDescription has finished processing all the media
|
|
// sections.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
SetRemoteOfferWithTwoTracksSameStreamFiresOnAddTrack) {
|
|
const std::string kTrack1Label = "audio_track1";
|
|
const std::string kTrack2Label = "audio_track2";
|
|
const std::string kSharedStreamId = "stream";
|
|
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
ASSERT_TRUE(caller->AddAudioTrack(kTrack1Label, {kSharedStreamId}));
|
|
ASSERT_TRUE(caller->AddAudioTrack(kTrack2Label, {kSharedStreamId}));
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
const auto& track_events = callee->observer()->add_track_events_;
|
|
ASSERT_EQ(2u, track_events.size());
|
|
const auto& event1 = track_events[0];
|
|
const auto& event2 = track_events[1];
|
|
ASSERT_EQ(1u, event1.streams.size());
|
|
auto stream = event1.streams[0];
|
|
ASSERT_THAT(event2.streams, ElementsAre(stream));
|
|
auto track1 = event1.receiver->track();
|
|
auto track2 = event2.receiver->track();
|
|
EXPECT_THAT(event1.snapshotted_stream_tracks.at(stream),
|
|
UnorderedElementsAre(track1, track2));
|
|
EXPECT_THAT(event2.snapshotted_stream_tracks.at(stream),
|
|
UnorderedElementsAre(track1, track2));
|
|
}
|
|
|
|
// Test that setting a remote offer with one track that has two streams fires
|
|
// off the correct OnAddTrack event.
|
|
TEST_F(PeerConnectionJsepTest,
|
|
SetRemoteOfferWithOneTrackTwoStreamFiresOnAddTrack) {
|
|
const std::string kTrackLabel = "audio_track";
|
|
const std::string kStreamId1 = "audio_stream1";
|
|
const std::string kStreamId2 = "audio_stream2";
|
|
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
ASSERT_TRUE(caller->AddAudioTrack(kTrackLabel, {kStreamId1, kStreamId2}));
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
const auto& track_events = callee->observer()->add_track_events_;
|
|
ASSERT_EQ(1u, track_events.size());
|
|
const auto& event = track_events[0];
|
|
ASSERT_EQ(2u, event.streams.size());
|
|
EXPECT_EQ(kStreamId1, event.streams[0]->id());
|
|
EXPECT_EQ(kStreamId2, event.streams[1]->id());
|
|
}
|
|
|
|
// Test that if an RtpTransceiver with a current_direction set is stopped, then
|
|
// current_direction is changed to null.
|
|
TEST_F(PeerConnectionJsepTest, CurrentDirectionResetWhenRtpTransceiverStopped) {
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
|
|
auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
|
|
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
|
|
|
|
ASSERT_TRUE(transceiver->current_direction());
|
|
transceiver->Stop();
|
|
EXPECT_FALSE(transceiver->current_direction());
|
|
}
|
|
|
|
// Test that you can't set an answer on a PeerConnection before setting the
|
|
// offer.
|
|
TEST_F(PeerConnectionJsepTest, AnswerBeforeOfferFails) {
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
caller->AddAudioTrack("audio");
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
|
|
RTCError error;
|
|
ASSERT_FALSE(caller->SetRemoteDescription(callee->CreateAnswer(), &error));
|
|
EXPECT_EQ(RTCErrorType::INVALID_STATE, error.type());
|
|
}
|
|
|
|
// Test that a Unified Plan PeerConnection fails to set a Plan B offer if it has
|
|
// two video tracks.
|
|
TEST_F(PeerConnectionJsepTest, TwoVideoPlanBToUnifiedPlanFails) {
|
|
RTCConfiguration config_planb;
|
|
config_planb.sdp_semantics = SdpSemantics::kPlanB;
|
|
auto caller = CreatePeerConnection(config_planb);
|
|
auto callee = CreatePeerConnection();
|
|
caller->AddVideoTrack("video1");
|
|
caller->AddVideoTrack("video2");
|
|
|
|
RTCError error;
|
|
ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
|
|
EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, error.type());
|
|
}
|
|
|
|
// Test that a Unified Plan PeerConnection fails to set a Plan B answer if it
|
|
// has two video tracks.
|
|
TEST_F(PeerConnectionJsepTest, OneVideoUnifiedPlanToTwoVideoPlanBFails) {
|
|
auto caller = CreatePeerConnection();
|
|
RTCConfiguration config_planb;
|
|
config_planb.sdp_semantics = SdpSemantics::kPlanB;
|
|
auto callee = CreatePeerConnection(config_planb);
|
|
caller->AddVideoTrack("video");
|
|
callee->AddVideoTrack("video1");
|
|
callee->AddVideoTrack("video2");
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
RTCError error;
|
|
ASSERT_FALSE(caller->SetRemoteDescription(caller->CreateAnswer(), &error));
|
|
EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, error.type());
|
|
}
|
|
|
|
// Removes the RTP header extension associated with the given URI from the media
|
|
// description.
|
|
static void RemoveRtpHeaderExtensionByUri(
|
|
MediaContentDescription* media_description,
|
|
absl::string_view uri) {
|
|
std::vector<RtpExtension> header_extensions =
|
|
media_description->rtp_header_extensions();
|
|
header_extensions.erase(std::remove_if(
|
|
header_extensions.begin(), header_extensions.end(),
|
|
[uri](const RtpExtension& extension) { return extension.uri == uri; }));
|
|
media_description->set_rtp_header_extensions(header_extensions);
|
|
}
|
|
|
|
// Transforms a session description to emulate a legacy endpoint which does not
|
|
// support a=mid, BUNDLE, and the MID header extension.
|
|
static void ClearMids(SessionDescriptionInterface* sdesc) {
|
|
cricket::SessionDescription* desc = sdesc->description();
|
|
desc->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
|
|
cricket::ContentInfo* audio_content = cricket::GetFirstAudioContent(desc);
|
|
if (audio_content) {
|
|
desc->GetTransportInfoByName(audio_content->name)->content_name = "";
|
|
audio_content->name = "";
|
|
RemoveRtpHeaderExtensionByUri(audio_content->media_description(),
|
|
RtpExtension::kMidUri);
|
|
}
|
|
cricket::ContentInfo* video_content = cricket::GetFirstVideoContent(desc);
|
|
if (video_content) {
|
|
desc->GetTransportInfoByName(video_content->name)->content_name = "";
|
|
video_content->name = "";
|
|
RemoveRtpHeaderExtensionByUri(video_content->media_description(),
|
|
RtpExtension::kMidUri);
|
|
}
|
|
}
|
|
|
|
// Test that negotiation works with legacy endpoints which do not support a=mid.
|
|
TEST_F(PeerConnectionJsepTest, LegacyNoMidAudioOnlyOffer) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("audio");
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("audio");
|
|
|
|
auto offer = caller->CreateOffer();
|
|
ClearMids(offer.get());
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
EXPECT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
|
|
}
|
|
TEST_F(PeerConnectionJsepTest, LegacyNoMidAudioVideoOffer) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("audio");
|
|
caller->AddVideoTrack("video");
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("audio");
|
|
callee->AddVideoTrack("video");
|
|
|
|
auto offer = caller->CreateOffer();
|
|
ClearMids(offer.get());
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
EXPECT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
|
|
}
|
|
TEST_F(PeerConnectionJsepTest, LegacyNoMidAudioOnlyAnswer) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("audio");
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("audio");
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto answer = callee->CreateAnswer();
|
|
ClearMids(answer.get());
|
|
|
|
EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
|
}
|
|
TEST_F(PeerConnectionJsepTest, LegacyNoMidAudioVideoAnswer) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("audio");
|
|
caller->AddVideoTrack("video");
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("audio");
|
|
callee->AddVideoTrack("video");
|
|
|
|
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
auto answer = callee->CreateAnswer();
|
|
ClearMids(answer.get());
|
|
|
|
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
|
}
|
|
|
|
// Test that negotiation works with legacy endpoints which do not support a=mid
|
|
// when setting two remote descriptions without setting a local description in
|
|
// between.
|
|
TEST_F(PeerConnectionJsepTest, LegacyNoMidTwoRemoteOffers) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("audio");
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("audio");
|
|
|
|
auto offer = caller->CreateOffer();
|
|
ClearMids(offer.get());
|
|
|
|
ASSERT_TRUE(
|
|
callee->SetRemoteDescription(CloneSessionDescription(offer.get())));
|
|
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
EXPECT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
|
|
}
|
|
|
|
// Test that SetLocalDescription fails if a=mid lines are missing.
|
|
TEST_F(PeerConnectionJsepTest, SetLocalDescriptionFailsMissingMid) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("audio");
|
|
|
|
auto offer = caller->CreateOffer();
|
|
ClearMids(offer.get());
|
|
|
|
std::string error;
|
|
ASSERT_FALSE(caller->SetLocalDescription(std::move(offer), &error));
|
|
EXPECT_EQ(
|
|
"Failed to set local offer sdp: A media section is missing a MID "
|
|
"attribute.",
|
|
error);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackSupportedInUnifiedPlan) {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
config.enable_implicit_rollback = true;
|
|
auto caller = CreatePeerConnection(config);
|
|
auto callee = CreatePeerConnection(config);
|
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
|
EXPECT_TRUE(caller->SetLocalDescription(caller->CreateRollback()));
|
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
|
EXPECT_TRUE(caller->SetRemoteDescription(caller->CreateRollback()));
|
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
|
EXPECT_TRUE(caller->SetRemoteDescription(callee->CreateOffer()));
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackNotSupportedInPlanB) {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kPlanB;
|
|
config.enable_implicit_rollback = true;
|
|
auto caller = CreatePeerConnection(config);
|
|
auto callee = CreatePeerConnection(config);
|
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
|
EXPECT_FALSE(caller->SetLocalDescription(caller->CreateRollback()));
|
|
EXPECT_FALSE(caller->SetRemoteDescription(caller->CreateRollback()));
|
|
EXPECT_FALSE(caller->SetRemoteDescription(callee->CreateOffer()));
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackFailsInStableState) {
|
|
auto caller = CreatePeerConnection();
|
|
EXPECT_FALSE(caller->SetLocalDescription(caller->CreateRollback()));
|
|
EXPECT_FALSE(caller->SetRemoteDescription(caller->CreateRollback()));
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackToStableStateAndClearLocalOffer) {
|
|
auto caller = CreatePeerConnection();
|
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
|
EXPECT_TRUE(caller->SetLocalDescription(caller->CreateRollback()));
|
|
EXPECT_EQ(caller->signaling_state(), PeerConnectionInterface::kStable);
|
|
EXPECT_EQ(caller->pc()->pending_local_description(), nullptr);
|
|
|
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
|
EXPECT_TRUE(caller->SetRemoteDescription(caller->CreateRollback()));
|
|
EXPECT_EQ(caller->signaling_state(), PeerConnectionInterface::kStable);
|
|
EXPECT_EQ(caller->pc()->pending_local_description(), nullptr);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackToStableStateAndClearRemoteOffer) {
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
|
EXPECT_EQ(callee->signaling_state(), PeerConnectionInterface::kStable);
|
|
EXPECT_EQ(callee->pc()->pending_remote_description(), nullptr);
|
|
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_TRUE(callee->SetLocalDescription(caller->CreateRollback()));
|
|
EXPECT_EQ(callee->signaling_state(), PeerConnectionInterface::kStable);
|
|
EXPECT_EQ(callee->pc()->pending_remote_description(), nullptr);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackImplicitly) {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
config.enable_implicit_rollback = true;
|
|
auto caller = CreatePeerConnection(config);
|
|
auto callee = CreatePeerConnection(config);
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_EQ(callee->signaling_state(),
|
|
PeerConnectionInterface::kHaveRemoteOffer);
|
|
EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
|
|
EXPECT_FALSE(callee->observer()->negotiation_needed());
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackImplicitlyNegotatiationNotNeeded) {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
config.enable_implicit_rollback = true;
|
|
auto caller = CreatePeerConnection(config);
|
|
auto callee = CreatePeerConnection(config);
|
|
caller->AddAudioTrack("a");
|
|
callee->AddAudioTrack("b");
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
callee->observer()->clear_negotiation_needed();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_EQ(callee->signaling_state(),
|
|
PeerConnectionInterface::kHaveRemoteOffer);
|
|
EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
|
|
// No negotiation needed as track got attached in the answer.
|
|
EXPECT_FALSE(callee->observer()->negotiation_needed());
|
|
EXPECT_EQ(callee->observer()->remove_track_events_.size(), 0u);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackImplicitlyAndNegotiationNeeded) {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
config.enable_implicit_rollback = true;
|
|
auto caller = CreatePeerConnection(config);
|
|
auto callee = CreatePeerConnection(config);
|
|
callee->AddAudioTrack("a");
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
callee->observer()->clear_negotiation_needed();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_EQ(callee->signaling_state(),
|
|
PeerConnectionInterface::kHaveRemoteOffer);
|
|
EXPECT_FALSE(callee->observer()->negotiation_needed());
|
|
EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
|
|
EXPECT_TRUE(callee->observer()->negotiation_needed());
|
|
EXPECT_EQ(callee->observer()->remove_track_events_.size(), 0u);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, AttemptToRollbackImplicitly) {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
config.enable_implicit_rollback = true;
|
|
auto caller = CreatePeerConnection(config);
|
|
auto callee = CreatePeerConnection(config);
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
EXPECT_FALSE(callee->SetRemoteDescription(
|
|
CreateSessionDescription(SdpType::kOffer, "invalid sdp")));
|
|
EXPECT_EQ(callee->signaling_state(),
|
|
PeerConnectionInterface::kHaveLocalOffer);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackRemovesTransceiver) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 0u);
|
|
EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackKeepsTransceiverAndClearsMid) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
callee->AddAudioTrack("a");
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
|
// Transceiver can't be removed as track was added to it.
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
// Mid got cleared to make it reusable.
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
|
|
// Transceiver should be counted as addTrack-created after rollback.
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest,
|
|
RollbackKeepsTransceiverAfterAddTrackEvenWhenTrackIsNulled) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
callee->AddAudioTrack("a");
|
|
callee->pc()->GetTransceivers()[0]->sender()->SetTrack(nullptr);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->track(), nullptr);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
|
// Transceiver can't be removed as track was added to it.
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
// Mid got cleared to make it reusable.
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
|
|
// Transceiver should be counted as addTrack-created after rollback.
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackRestoresMid) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("a");
|
|
auto offer = callee->CreateOffer();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
EXPECT_NE(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
|
|
EXPECT_TRUE(callee->SetLocalDescription(std::move(offer)));
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackRestoresMidAndRemovesTransceiver) {
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddVideoTrack("a");
|
|
auto offer = callee->CreateOffer();
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("b");
|
|
caller->AddVideoTrack("c");
|
|
auto mid = callee->pc()->GetTransceivers()[0]->mid();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), mid);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->media_type(),
|
|
cricket::MEDIA_TYPE_VIDEO);
|
|
EXPECT_TRUE(callee->SetLocalDescription(std::move(offer)));
|
|
EXPECT_EQ(callee->observer()->remove_track_events_.size(),
|
|
callee->observer()->add_track_events_.size());
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackHasNoEffectOnStableTransceivers) {
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddVideoTrack("a");
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("b");
|
|
caller->AddVideoTrack("c");
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
EXPECT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
// In stable don't add or remove anything.
|
|
callee->observer()->clear_negotiation_needed();
|
|
size_t transceiver_count = callee->pc()->GetTransceivers().size();
|
|
auto mid_0 = callee->pc()->GetTransceivers()[0]->mid();
|
|
auto mid_1 = callee->pc()->GetTransceivers()[1]->mid();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), transceiver_count);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), mid_0);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[1]->mid(), mid_1);
|
|
EXPECT_EQ(callee->observer()->remove_track_events_.size(), 0u);
|
|
EXPECT_FALSE(callee->observer()->negotiation_needed());
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, ImplicitlyRollbackTransceiversWithSameMids) {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
config.enable_implicit_rollback = true;
|
|
auto caller = CreatePeerConnection(config);
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
auto callee = CreatePeerConnection(config);
|
|
callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
auto initial_mid = callee->pc()->GetTransceivers()[0]->mid();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[1]->mid(),
|
|
caller->pc()->GetTransceivers()[0]->mid());
|
|
EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal()); // Go to stable.
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
EXPECT_NE(callee->pc()->GetTransceivers()[0]->mid(), initial_mid);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackToNegotiatedStableState) {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle;
|
|
auto caller = CreatePeerConnection(config);
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection(config);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
|
|
caller->AddVideoTrack("a");
|
|
callee->AddVideoTrack("b");
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
|
|
auto audio_transport =
|
|
callee->pc()->GetTransceivers()[0]->sender()->dtls_transport();
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
|
|
callee->pc()->GetTransceivers()[1]->sender()->dtls_transport());
|
|
EXPECT_NE(callee->pc()->GetTransceivers()[1]->sender()->dtls_transport(),
|
|
nullptr);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
|
|
audio_transport); // Audio must remain working after rollback.
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[1]->sender()->dtls_transport(),
|
|
nullptr);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
|
|
audio_transport); // Audio transport is still the same.
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackHasToDestroyTransport) {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle;
|
|
auto pc = CreatePeerConnection(config);
|
|
pc->AddAudioTrack("a");
|
|
pc->AddVideoTrack("b");
|
|
EXPECT_TRUE(pc->CreateOfferAndSetAsLocal());
|
|
auto offer = pc->CreateOffer();
|
|
EXPECT_EQ(pc->pc()->GetTransceivers().size(), 2u);
|
|
auto audio_transport =
|
|
pc->pc()->GetTransceivers()[0]->sender()->dtls_transport();
|
|
EXPECT_EQ(pc->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
|
|
pc->pc()->GetTransceivers()[1]->sender()->dtls_transport());
|
|
EXPECT_NE(pc->pc()->GetTransceivers()[1]->sender()->dtls_transport(),
|
|
nullptr);
|
|
EXPECT_TRUE(pc->SetRemoteDescription(pc->CreateRollback()));
|
|
EXPECT_TRUE(pc->SetLocalDescription(std::move(offer)));
|
|
EXPECT_NE(pc->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
|
|
nullptr);
|
|
EXPECT_NE(pc->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
|
|
audio_transport);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackLocalDirectionChange) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
EXPECT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
callee->AddAudioTrack("a");
|
|
callee->pc()->GetTransceivers()[0]->SetDirection(
|
|
RtpTransceiverDirection::kSendOnly);
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
auto audio_transport =
|
|
callee->pc()->GetTransceivers()[0]->receiver()->dtls_transport();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->direction(),
|
|
RtpTransceiverDirection::kSendOnly);
|
|
// One way audio must remain working after rollback as local direction change
|
|
// comes in effect after completing full negotiation round.
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->receiver()->dtls_transport(),
|
|
audio_transport);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackRemoteDirectionChange) {
|
|
auto caller = CreatePeerConnection();
|
|
auto caller_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddAudioTrack("a");
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
EXPECT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
// In stable make remote audio receive only.
|
|
caller_transceiver->SetDirection(RtpTransceiverDirection::kRecvOnly);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
// The direction attribute is not modified by the offer.
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->direction(),
|
|
RtpTransceiverDirection::kSendRecv);
|
|
auto audio_transport =
|
|
callee->pc()->GetTransceivers()[0]->sender()->dtls_transport();
|
|
EXPECT_EQ(callee->observer()->add_track_events_.size(), 1u);
|
|
EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->direction(),
|
|
RtpTransceiverDirection::kSendRecv);
|
|
// One way audio must remain working after rollback.
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
|
|
audio_transport);
|
|
EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackAfterMultipleSLD) {
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
callee->observer()->clear_negotiation_needed();
|
|
EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
|
|
EXPECT_TRUE(callee->observer()->negotiation_needed());
|
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[1]->mid(), absl::nullopt);
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, NoRollbackNeeded) {
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
auto callee = CreatePeerConnection();
|
|
callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackMultipleStreamChanges) {
|
|
auto callee = CreatePeerConnection();
|
|
auto caller = CreatePeerConnection();
|
|
caller->AddAudioTrack("a_1", {"id_1"});
|
|
caller->AddVideoTrack("v_0", {"id_0"}); // Provide an extra stream id.
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
EXPECT_TRUE(
|
|
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
|
|
caller->pc()->GetTransceivers()[0]->sender()->SetStreams({"id_2"});
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
caller->pc()->GetTransceivers()[0]->sender()->SetStreams({"id_3"});
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->receiver()->stream_ids()[0],
|
|
"id_3");
|
|
EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->receiver()->stream_ids().size(),
|
|
1u);
|
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->receiver()->stream_ids()[0],
|
|
"id_1");
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, DataChannelImplicitRollback) {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
config.enable_implicit_rollback = true;
|
|
auto caller = CreatePeerConnection(config);
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
auto callee = CreatePeerConnection(config);
|
|
callee->CreateDataChannel("dummy");
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
|
|
EXPECT_TRUE(callee->observer()->negotiation_needed());
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackRemoteDataChannelThenAddTransceiver) {
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
caller->CreateDataChannel("dummy");
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
|
|
callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest,
|
|
RollbackRemoteDataChannelThenAddTransceiverAndDataChannel) {
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
caller->CreateDataChannel("dummy");
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
|
|
callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
callee->CreateDataChannel("dummy");
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackRemoteDataChannelThenAddDataChannel) {
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
caller->CreateDataChannel("dummy");
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
|
|
callee->CreateDataChannel("dummy");
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackRemoteTransceiverThenAddDataChannel) {
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
|
|
callee->CreateDataChannel("dummy");
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest,
|
|
RollbackRemoteTransceiverThenAddDataChannelAndTransceiver) {
|
|
auto caller = CreatePeerConnection();
|
|
auto callee = CreatePeerConnection();
|
|
caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
|
EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
|
|
callee->CreateDataChannel("dummy");
|
|
callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
|
}
|
|
|
|
TEST_F(PeerConnectionJsepTest, RollbackRtpDataChannel) {
|
|
RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
config.enable_rtp_data_channel = true;
|
|
auto pc = CreatePeerConnection(config);
|
|
pc->CreateDataChannel("dummy");
|
|
auto offer = pc->CreateOffer();
|
|
EXPECT_TRUE(pc->CreateOfferAndSetAsLocal());
|
|
EXPECT_TRUE(pc->SetRemoteDescription(pc->CreateRollback()));
|
|
EXPECT_TRUE(pc->SetLocalDescription(std::move(offer)));
|
|
}
|
|
|
|
} // namespace webrtc
|