Reland "Rewrite WebRtcSession media tests as PeerConnection tests"
This is a reland of 3df5dcac9b339ba4d3f4969602f094c2c8035b51
Original change's description:
> Rewrite WebRtcSession media tests as PeerConnection tests
>
> Bug: webrtc:8222
> Change-Id: I782a3227e30de70eb8f6c26a48723cb3510a84ad
> Reviewed-on: https://webrtc-review.googlesource.com/6640
> Commit-Queue: Steve Anton <steveanton@webrtc.org>
> Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#20364}
Bug: webrtc:8222
Change-Id: I0a5398170d469eb9223bc781bfb417a85a72a2d2
Reviewed-on: https://webrtc-review.googlesource.com/14380
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20377}
diff --git a/media/base/fakemediaengine.h b/media/base/fakemediaengine.h
index 29a129f..7b09dd4 100644
--- a/media/base/fakemediaengine.h
+++ b/media/base/fakemediaengine.h
@@ -488,12 +488,11 @@
if (it != local_sinks_.end()) {
RTC_CHECK(it->second->source() == source);
} else {
- local_sinks_.insert(
- std::make_pair(ssrc, new VoiceChannelAudioSink(source)));
+ local_sinks_.insert(std::make_pair(
+ ssrc, rtc::MakeUnique<VoiceChannelAudioSink>(source)));
}
} else {
if (it != local_sinks_.end()) {
- delete it->second;
local_sinks_.erase(it);
}
}
@@ -506,7 +505,7 @@
std::map<uint32_t, double> output_scalings_;
std::vector<DtmfInfo> dtmf_info_queue_;
AudioOptions options_;
- std::map<uint32_t, VoiceChannelAudioSink*> local_sinks_;
+ std::map<uint32_t, std::unique_ptr<VoiceChannelAudioSink>> local_sinks_;
std::unique_ptr<webrtc::AudioSinkInterface> sink_;
int max_bps_;
};
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index c77a638..02a8e9a 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -394,7 +394,9 @@
"peerconnection_crypto_unittest.cc",
"peerconnection_ice_unittest.cc",
"peerconnection_integrationtest.cc",
+ "peerconnection_media_unittest.cc",
"peerconnection_rtp_unittest.cc",
+ "peerconnection_signaling_unittest.cc",
"peerconnectionendtoend_unittest.cc",
"peerconnectionfactory_unittest.cc",
"peerconnectioninterface_unittest.cc",
@@ -463,7 +465,9 @@
"../api/audio_codecs:builtin_audio_encoder_factory",
"../api/audio_codecs/L16:audio_decoder_L16",
"../api/audio_codecs/L16:audio_encoder_L16",
+ "../call:call_interfaces",
"../logging:rtc_event_log_api",
+ "../logging:rtc_event_log_impl",
"../media:rtc_audio_video",
"../media:rtc_data", # TODO(phoglund): AFAIK only used for one sctp constant.
"../media:rtc_media_base",
diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc
index 9eb12d6..ac552ef 100644
--- a/pc/peerconnection.cc
+++ b/pc/peerconnection.cc
@@ -828,10 +828,7 @@
void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
const MediaConstraintsInterface* constraints) {
TRACE_EVENT0("webrtc", "PeerConnection::CreateOffer");
- if (!observer) {
- LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
- return;
- }
+
PeerConnectionInterface::RTCOfferAnswerOptions offer_answer_options;
// Always create an offer even if |ConvertConstraintsToOfferAnswerOptions|
// returns false for now. Because |ConvertConstraintsToOfferAnswerOptions|
@@ -848,11 +845,19 @@
void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
const RTCOfferAnswerOptions& options) {
TRACE_EVENT0("webrtc", "PeerConnection::CreateOffer");
+
if (!observer) {
LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
return;
}
+ if (IsClosed()) {
+ std::string error = "CreateOffer called when PeerConnection is closed.";
+ LOG(LS_ERROR) << error;
+ PostCreateSessionDescriptionFailure(observer, error);
+ return;
+ }
+
if (!ValidateOfferAnswerOptions(options)) {
std::string error = "CreateOffer called with invalid options.";
LOG(LS_ERROR) << error;
@@ -869,20 +874,12 @@
CreateSessionDescriptionObserver* observer,
const MediaConstraintsInterface* constraints) {
TRACE_EVENT0("webrtc", "PeerConnection::CreateAnswer");
+
if (!observer) {
LOG(LS_ERROR) << "CreateAnswer - observer is NULL.";
return;
}
- if (!session_->remote_description() ||
- session_->remote_description()->type() !=
- SessionDescriptionInterface::kOffer) {
- std::string error = "CreateAnswer called without remote offer.";
- LOG(LS_ERROR) << error;
- PostCreateSessionDescriptionFailure(observer, error);
- return;
- }
-
PeerConnectionInterface::RTCOfferAnswerOptions offer_answer_options;
if (!ConvertConstraintsToOfferAnswerOptions(constraints,
&offer_answer_options)) {
@@ -892,9 +889,7 @@
return;
}
- cricket::MediaSessionOptions session_options;
- GetOptionsForAnswer(offer_answer_options, &session_options);
- session_->CreateAnswer(observer, session_options);
+ CreateAnswer(observer, offer_answer_options);
}
void PeerConnection::CreateAnswer(CreateSessionDescriptionObserver* observer,
@@ -905,6 +900,22 @@
return;
}
+ if (IsClosed()) {
+ std::string error = "CreateAnswer called when PeerConnection is closed.";
+ LOG(LS_ERROR) << error;
+ PostCreateSessionDescriptionFailure(observer, error);
+ return;
+ }
+
+ if (session_->remote_description() &&
+ session_->remote_description()->type() !=
+ SessionDescriptionInterface::kOffer) {
+ std::string error = "CreateAnswer called without remote offer.";
+ LOG(LS_ERROR) << error;
+ PostCreateSessionDescriptionFailure(observer, error);
+ return;
+ }
+
cricket::MediaSessionOptions session_options;
GetOptionsForAnswer(options, &session_options);
@@ -915,9 +926,6 @@
SetSessionDescriptionObserver* observer,
SessionDescriptionInterface* desc) {
TRACE_EVENT0("webrtc", "PeerConnection::SetLocalDescription");
- if (IsClosed()) {
- return;
- }
if (!observer) {
LOG(LS_ERROR) << "SetLocalDescription - observer is NULL.";
return;
@@ -926,11 +934,23 @@
PostSetSessionDescriptionFailure(observer, "SessionDescription is NULL.");
return;
}
+
+ // Takes the ownership of |desc| regardless of the result.
+ std::unique_ptr<SessionDescriptionInterface> desc_temp(desc);
+
+ if (IsClosed()) {
+ std::string error = "Failed to set local " + desc->type() +
+ " sdp: Called in wrong state: STATE_CLOSED";
+ LOG(LS_ERROR) << error;
+ PostSetSessionDescriptionFailure(observer, error);
+ return;
+ }
+
// Update stats here so that we have the most recent stats for tracks and
// streams that might be removed by updating the session description.
stats_->UpdateStats(kStatsOutputLevelStandard);
std::string error;
- if (!session_->SetLocalDescription(desc, &error)) {
+ if (!session_->SetLocalDescription(std::move(desc_temp), &error)) {
PostSetSessionDescriptionFailure(observer, error);
return;
}
@@ -1011,9 +1031,6 @@
SetSessionDescriptionObserver* observer,
SessionDescriptionInterface* desc) {
TRACE_EVENT0("webrtc", "PeerConnection::SetRemoteDescription");
- if (IsClosed()) {
- return;
- }
if (!observer) {
LOG(LS_ERROR) << "SetRemoteDescription - observer is NULL.";
return;
@@ -1022,11 +1039,23 @@
PostSetSessionDescriptionFailure(observer, "SessionDescription is NULL.");
return;
}
+
+ // Takes the ownership of |desc| regardless of the result.
+ std::unique_ptr<SessionDescriptionInterface> desc_temp(desc);
+
+ if (IsClosed()) {
+ std::string error = "Failed to set remote " + desc->type() +
+ " sdp: Called in wrong state: STATE_CLOSED";
+ LOG(LS_ERROR) << error;
+ PostSetSessionDescriptionFailure(observer, error);
+ return;
+ }
+
// Update stats here so that we have the most recent stats for tracks and
// streams that might be removed by updating the session description.
stats_->UpdateStats(kStatsOutputLevelStandard);
std::string error;
- if (!session_->SetRemoteDescription(desc, &error)) {
+ if (!session_->SetRemoteDescription(std::move(desc_temp), &error)) {
PostSetSessionDescriptionFailure(observer, error);
return;
}
@@ -1061,6 +1090,15 @@
// since only at that point will new streams have all their tracks.
rtc::scoped_refptr<StreamCollection> new_streams(StreamCollection::Create());
+ // TODO(steveanton): When removing RTP senders/receivers in response to a
+ // rejected media section, there is some cleanup logic that expects the voice/
+ // video channel to still be set. But in this method the voice/video channel
+ // would have been destroyed by WebRtcSession's SetRemoteDescription method
+ // above, so the cleanup that relies on them fails to run. This is hard to fix
+ // with WebRtcSession and PeerConnection separated, but once the classes are
+ // merged it will be easy to call RemoveTracks right before destroying the
+ // voice/video channels.
+
// Find all audio rtp streams and create corresponding remote AudioTracks
// and MediaStreams.
if (audio_content) {
diff --git a/pc/peerconnection_crypto_unittest.cc b/pc/peerconnection_crypto_unittest.cc
index 081e11a..68eec08 100644
--- a/pc/peerconnection_crypto_unittest.cc
+++ b/pc/peerconnection_crypto_unittest.cc
@@ -75,7 +75,8 @@
if (!wrapper) {
return nullptr;
}
- wrapper->AddAudioVideoStream("s", "a", "v");
+ wrapper->AddAudioTrack("a");
+ wrapper->AddVideoTrack("v");
return wrapper;
}
diff --git a/pc/peerconnection_ice_unittest.cc b/pc/peerconnection_ice_unittest.cc
index 0880018..3ab9acb 100644
--- a/pc/peerconnection_ice_unittest.cc
+++ b/pc/peerconnection_ice_unittest.cc
@@ -120,7 +120,8 @@
if (!wrapper) {
return nullptr;
}
- wrapper->AddAudioVideoStream("s", "a", "v");
+ wrapper->AddAudioTrack("a");
+ wrapper->AddVideoTrack("v");
return wrapper;
}
diff --git a/pc/peerconnection_media_unittest.cc b/pc/peerconnection_media_unittest.cc
new file mode 100644
index 0000000..20690d2
--- /dev/null
+++ b/pc/peerconnection_media_unittest.cc
@@ -0,0 +1,889 @@
+/*
+ * 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.
+ */
+
+// This file contains tests that check the interaction between the
+// PeerConnection and the underlying media engine, as well as tests that check
+// the media-related aspects of SDP.
+
+#include <tuple>
+
+#include "call/callfactoryinterface.h"
+#include "logging/rtc_event_log/rtc_event_log_factory.h"
+#include "media/base/fakemediaengine.h"
+#include "p2p/base/fakeportallocator.h"
+#include "pc/mediasession.h"
+#include "pc/peerconnectionwrapper.h"
+#include "pc/sdputils.h"
+#ifdef WEBRTC_ANDROID
+#include "pc/test/androidtestinitializer.h"
+#endif
+#include "pc/test/fakertccertificategenerator.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/ptr_util.h"
+#include "rtc_base/virtualsocketserver.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+using cricket::FakeMediaEngine;
+using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
+using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
+using ::testing::Bool;
+using ::testing::Combine;
+using ::testing::Values;
+using ::testing::ElementsAre;
+
+class PeerConnectionWrapperForMediaTest : public PeerConnectionWrapper {
+ public:
+ using PeerConnectionWrapper::PeerConnectionWrapper;
+
+ FakeMediaEngine* media_engine() { return media_engine_; }
+ void set_media_engine(FakeMediaEngine* media_engine) {
+ media_engine_ = media_engine;
+ }
+
+ private:
+ FakeMediaEngine* media_engine_;
+};
+
+class PeerConnectionMediaTest : public ::testing::Test {
+ protected:
+ typedef std::unique_ptr<PeerConnectionWrapperForMediaTest> WrapperPtr;
+
+ PeerConnectionMediaTest()
+ : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
+#ifdef WEBRTC_ANDROID
+ InitializeAndroidObjects();
+#endif
+ }
+
+ WrapperPtr CreatePeerConnection() {
+ return CreatePeerConnection(RTCConfiguration());
+ }
+
+ WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
+ auto media_engine = rtc::MakeUnique<FakeMediaEngine>();
+ auto* media_engine_ptr = media_engine.get();
+ auto pc_factory = CreateModularPeerConnectionFactory(
+ rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
+ std::move(media_engine), CreateCallFactory(),
+ CreateRtcEventLogFactory());
+
+ auto fake_port_allocator = rtc::MakeUnique<cricket::FakePortAllocator>(
+ rtc::Thread::Current(), nullptr);
+ auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
+ auto pc = pc_factory->CreatePeerConnection(
+ config, std::move(fake_port_allocator), nullptr, observer.get());
+ if (!pc) {
+ return nullptr;
+ }
+
+ auto wrapper = rtc::MakeUnique<PeerConnectionWrapperForMediaTest>(
+ pc_factory, pc, std::move(observer));
+ wrapper->set_media_engine(media_engine_ptr);
+ return wrapper;
+ }
+
+ // Accepts the same arguments as CreatePeerConnection and adds default audio
+ // and video tracks.
+ template <typename... Args>
+ WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
+ auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
+ if (!wrapper) {
+ return nullptr;
+ }
+ wrapper->AddAudioTrack("a");
+ wrapper->AddVideoTrack("v");
+ return wrapper;
+ }
+
+ const cricket::MediaContentDescription* GetMediaContent(
+ const SessionDescriptionInterface* sdesc,
+ const std::string& mid) {
+ const auto* content_desc =
+ sdesc->description()->GetContentDescriptionByName(mid);
+ return static_cast<const cricket::MediaContentDescription*>(content_desc);
+ }
+
+ cricket::MediaContentDirection GetMediaContentDirection(
+ const SessionDescriptionInterface* sdesc,
+ const std::string& mid) {
+ auto* media_content = GetMediaContent(sdesc, mid);
+ RTC_DCHECK(media_content);
+ return media_content->direction();
+ }
+
+ std::unique_ptr<rtc::VirtualSocketServer> vss_;
+ rtc::AutoSocketServerThread main_;
+};
+
+TEST_F(PeerConnectionMediaTest,
+ FailToSetRemoteDescriptionIfCreateMediaChannelFails) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+ callee->media_engine()->set_fail_create_channel(true);
+
+ std::string error;
+ ASSERT_FALSE(callee->SetRemoteDescription(caller->CreateOffer(), &error));
+ EXPECT_EQ("Failed to set remote offer sdp: Failed to create channels.",
+ error);
+}
+
+TEST_F(PeerConnectionMediaTest,
+ FailToSetLocalDescriptionIfCreateMediaChannelFails) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ caller->media_engine()->set_fail_create_channel(true);
+
+ std::string error;
+ ASSERT_FALSE(caller->SetLocalDescription(caller->CreateOffer(), &error));
+ EXPECT_EQ("Failed to set local offer sdp: Failed to create channels.", error);
+}
+
+std::vector<std::string> GetIds(
+ const std::vector<cricket::StreamParams>& streams) {
+ std::vector<std::string> ids;
+ for (const auto& stream : streams) {
+ ids.push_back(stream.id);
+ }
+ return ids;
+}
+
+// Test that exchanging an offer and answer with each side having an audio and
+// video stream creates the appropriate send/recv streams in the underlying
+// media engine on both sides.
+TEST_F(PeerConnectionMediaTest, AudioVideoOfferAnswerCreateSendRecvStreams) {
+ const std::string kCallerAudioId = "caller_a";
+ const std::string kCallerVideoId = "caller_v";
+ const std::string kCalleeAudioId = "callee_a";
+ const std::string kCalleeVideoId = "callee_v";
+
+ auto caller = CreatePeerConnection();
+ caller->AddAudioTrack(kCallerAudioId);
+ caller->AddVideoTrack(kCallerVideoId);
+
+ auto callee = CreatePeerConnection();
+ callee->AddAudioTrack(kCalleeAudioId);
+ callee->AddVideoTrack(kCalleeVideoId);
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ auto* caller_voice = caller->media_engine()->GetVoiceChannel(0);
+ EXPECT_THAT(GetIds(caller_voice->recv_streams()),
+ ElementsAre(kCalleeAudioId));
+ EXPECT_THAT(GetIds(caller_voice->send_streams()),
+ ElementsAre(kCallerAudioId));
+
+ auto* caller_video = caller->media_engine()->GetVideoChannel(0);
+ EXPECT_THAT(GetIds(caller_video->recv_streams()),
+ ElementsAre(kCalleeVideoId));
+ EXPECT_THAT(GetIds(caller_video->send_streams()),
+ ElementsAre(kCallerVideoId));
+
+ auto* callee_voice = callee->media_engine()->GetVoiceChannel(0);
+ EXPECT_THAT(GetIds(callee_voice->recv_streams()),
+ ElementsAre(kCallerAudioId));
+ EXPECT_THAT(GetIds(callee_voice->send_streams()),
+ ElementsAre(kCalleeAudioId));
+
+ auto* callee_video = callee->media_engine()->GetVideoChannel(0);
+ EXPECT_THAT(GetIds(callee_video->recv_streams()),
+ ElementsAre(kCallerVideoId));
+ EXPECT_THAT(GetIds(callee_video->send_streams()),
+ ElementsAre(kCalleeVideoId));
+}
+
+// Test that removing streams from a subsequent offer causes the receive streams
+// on the callee to be removed.
+TEST_F(PeerConnectionMediaTest, EmptyRemoteOfferRemovesRecvStreams) {
+ auto caller = CreatePeerConnection();
+ auto caller_audio_track = caller->AddAudioTrack("a");
+ auto caller_video_track = caller->AddVideoTrack("v");
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ // Remove both tracks from caller.
+ caller->pc()->RemoveTrack(caller_audio_track);
+ caller->pc()->RemoveTrack(caller_video_track);
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
+ EXPECT_EQ(1u, callee_voice->send_streams().size());
+ EXPECT_EQ(0u, callee_voice->recv_streams().size());
+
+ auto callee_video = callee->media_engine()->GetVideoChannel(0);
+ EXPECT_EQ(1u, callee_video->send_streams().size());
+ EXPECT_EQ(0u, callee_video->recv_streams().size());
+}
+
+// Test that removing streams from a subsequent answer causes the send streams
+// on the callee to be removed when applied locally.
+TEST_F(PeerConnectionMediaTest, EmptyLocalAnswerRemovesSendStreams) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnection();
+ auto callee_audio_track = callee->AddAudioTrack("a");
+ auto callee_video_track = callee->AddVideoTrack("v");
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ // Remove both tracks from callee.
+ callee->pc()->RemoveTrack(callee_audio_track);
+ callee->pc()->RemoveTrack(callee_video_track);
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
+ EXPECT_EQ(0u, callee_voice->send_streams().size());
+ EXPECT_EQ(1u, callee_voice->recv_streams().size());
+
+ auto callee_video = callee->media_engine()->GetVideoChannel(0);
+ EXPECT_EQ(0u, callee_video->send_streams().size());
+ EXPECT_EQ(1u, callee_video->recv_streams().size());
+}
+
+// Test that a new stream in a subsequent offer causes a new receive stream to
+// be created on the callee.
+TEST_F(PeerConnectionMediaTest, NewStreamInRemoteOfferAddsRecvStreams) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnection();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ // Add second set of tracks to the caller.
+ caller->AddAudioTrack("a2");
+ caller->AddVideoTrack("v2");
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
+ EXPECT_EQ(2u, callee_voice->recv_streams().size());
+ auto callee_video = callee->media_engine()->GetVideoChannel(0);
+ EXPECT_EQ(2u, callee_video->recv_streams().size());
+}
+
+// Test that a new stream in a subsequent answer causes a new send stream to be
+// created on the callee when added locally.
+TEST_F(PeerConnectionMediaTest, NewStreamInLocalAnswerAddsSendStreams) {
+ auto caller = CreatePeerConnection();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ RTCOfferAnswerOptions options;
+ options.offer_to_receive_audio =
+ RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
+ options.offer_to_receive_video =
+ RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
+
+ ASSERT_TRUE(
+ callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(options)));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ // Add second set of tracks to the callee.
+ callee->AddAudioTrack("a2");
+ callee->AddVideoTrack("v2");
+
+ ASSERT_TRUE(
+ callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(options)));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
+ EXPECT_EQ(2u, callee_voice->send_streams().size());
+ auto callee_video = callee->media_engine()->GetVideoChannel(0);
+ EXPECT_EQ(2u, callee_video->send_streams().size());
+}
+
+// A PeerConnection with no local streams and no explicit answer constraints
+// should not reject any offered media sections.
+TEST_F(PeerConnectionMediaTest,
+ CreateAnswerWithNoStreamsAndDefaultOptionsDoesNotReject) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnection();
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ auto answer = callee->CreateAnswer();
+
+ const auto* audio_content =
+ cricket::GetFirstAudioContent(answer->description());
+ ASSERT_TRUE(audio_content);
+ EXPECT_FALSE(audio_content->rejected);
+
+ const auto* video_content =
+ cricket::GetFirstVideoContent(answer->description());
+ ASSERT_TRUE(video_content);
+ EXPECT_FALSE(video_content->rejected);
+}
+
+class PeerConnectionMediaOfferDirectionTest
+ : public PeerConnectionMediaTest,
+ public ::testing::WithParamInterface<
+ std::tuple<bool, int, cricket::MediaContentDirection>> {
+ protected:
+ PeerConnectionMediaOfferDirectionTest() {
+ send_media_ = std::get<0>(GetParam());
+ offer_to_receive_ = std::get<1>(GetParam());
+ expected_direction_ = std::get<2>(GetParam());
+ }
+
+ bool send_media_;
+ int offer_to_receive_;
+ cricket::MediaContentDirection expected_direction_;
+};
+
+// Tests that the correct direction is set on the media description according
+// to the presence of a local media track and the offer_to_receive setting.
+TEST_P(PeerConnectionMediaOfferDirectionTest, VerifyDirection) {
+ auto caller = CreatePeerConnection();
+ if (send_media_) {
+ caller->AddAudioTrack("a");
+ }
+
+ RTCOfferAnswerOptions options;
+ options.offer_to_receive_audio = offer_to_receive_;
+ auto offer = caller->CreateOffer(options);
+
+ auto* media_content = GetMediaContent(offer.get(), cricket::CN_AUDIO);
+ if (expected_direction_ == cricket::MD_INACTIVE) {
+ EXPECT_FALSE(media_content);
+ } else {
+ EXPECT_EQ(expected_direction_, media_content->direction());
+ }
+}
+
+// Note that in these tests, MD_INACTIVE indicates that no media section is
+// included in the offer, not that the media direction is inactive.
+INSTANTIATE_TEST_CASE_P(PeerConnectionMediaTest,
+ PeerConnectionMediaOfferDirectionTest,
+ Values(std::make_tuple(false, -1, cricket::MD_INACTIVE),
+ std::make_tuple(false, 0, cricket::MD_INACTIVE),
+ std::make_tuple(false, 1, cricket::MD_RECVONLY),
+ std::make_tuple(true, -1, cricket::MD_SENDRECV),
+ std::make_tuple(true, 0, cricket::MD_SENDONLY),
+ std::make_tuple(true, 1, cricket::MD_SENDRECV)));
+
+class PeerConnectionMediaAnswerDirectionTest
+ : public PeerConnectionMediaTest,
+ public ::testing::WithParamInterface<
+ std::tuple<cricket::MediaContentDirection, bool, int>> {
+ protected:
+ PeerConnectionMediaAnswerDirectionTest() {
+ offer_direction_ = std::get<0>(GetParam());
+ send_media_ = std::get<1>(GetParam());
+ offer_to_receive_ = std::get<2>(GetParam());
+ }
+
+ cricket::MediaContentDirection offer_direction_;
+ bool send_media_;
+ int offer_to_receive_;
+};
+
+// Tests that the direction in an answer is correct according to direction sent
+// in the offer, the presence of a local media track on the receive side and the
+// offer_to_receive setting.
+TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyDirection) {
+ auto caller = CreatePeerConnection();
+ caller->AddAudioTrack("a");
+
+ // Create the offer with an audio section and set its direction.
+ auto offer = caller->CreateOffer();
+ cricket::GetFirstAudioContentDescription(offer->description())
+ ->set_direction(offer_direction_);
+
+ auto callee = CreatePeerConnection();
+ if (send_media_) {
+ callee->AddAudioTrack("a");
+ }
+ ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
+
+ // Create the answer according to the test parameters.
+ RTCOfferAnswerOptions options;
+ options.offer_to_receive_audio = offer_to_receive_;
+ auto answer = callee->CreateAnswer(options);
+
+ // The expected direction in the answer is the intersection of each side's
+ // capability to send/recv media.
+ // For the offerer, the direction is given in the offer (offer_direction_).
+ // For the answerer, the direction has two components:
+ // 1. Send if the answerer has a local track to send.
+ // 2. Receive if the answerer has explicitly set the offer_to_receive to 1 or
+ // if it has been left as default.
+ auto offer_direction =
+ cricket::RtpTransceiverDirection::FromMediaContentDirection(
+ offer_direction_);
+
+ // The negotiated components determine the direction set in the answer.
+ bool negotiate_send = (send_media_ && offer_direction.recv);
+ bool negotiate_recv = ((offer_to_receive_ != 0) && offer_direction.send);
+
+ auto expected_direction =
+ cricket::RtpTransceiverDirection(negotiate_send, negotiate_recv)
+ .ToMediaContentDirection();
+ EXPECT_EQ(expected_direction,
+ GetMediaContentDirection(answer.get(), cricket::CN_AUDIO));
+}
+
+// Tests that the media section is rejected if and only if the callee has no
+// local media track and has set offer_to_receive to 0, no matter which
+// direction the caller indicated in the offer.
+TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyRejected) {
+ auto caller = CreatePeerConnection();
+ caller->AddAudioTrack("a");
+
+ // Create the offer with an audio section and set its direction.
+ auto offer = caller->CreateOffer();
+ cricket::GetFirstAudioContentDescription(offer->description())
+ ->set_direction(offer_direction_);
+
+ auto callee = CreatePeerConnection();
+ if (send_media_) {
+ callee->AddAudioTrack("a");
+ }
+ ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
+
+ // Create the answer according to the test parameters.
+ RTCOfferAnswerOptions options;
+ options.offer_to_receive_audio = offer_to_receive_;
+ auto answer = callee->CreateAnswer(options);
+
+ // The media section is rejected if and only if offer_to_receive is explicitly
+ // set to 0 and there is no media to send.
+ auto* audio_content = cricket::GetFirstAudioContent(answer->description());
+ ASSERT_TRUE(audio_content);
+ EXPECT_EQ((offer_to_receive_ == 0 && !send_media_), audio_content->rejected);
+}
+
+INSTANTIATE_TEST_CASE_P(PeerConnectionMediaTest,
+ PeerConnectionMediaAnswerDirectionTest,
+ Combine(Values(cricket::MD_INACTIVE,
+ cricket::MD_SENDONLY,
+ cricket::MD_RECVONLY,
+ cricket::MD_SENDRECV),
+ Bool(),
+ Values(-1, 0, 1)));
+
+TEST_F(PeerConnectionMediaTest, OfferHasDifferentDirectionForAudioVideo) {
+ auto caller = CreatePeerConnection();
+ caller->AddVideoTrack("v");
+
+ RTCOfferAnswerOptions options;
+ options.offer_to_receive_audio = 1;
+ options.offer_to_receive_video = 0;
+ auto offer = caller->CreateOffer(options);
+
+ EXPECT_EQ(cricket::MD_RECVONLY,
+ GetMediaContentDirection(offer.get(), cricket::CN_AUDIO));
+ EXPECT_EQ(cricket::MD_SENDONLY,
+ GetMediaContentDirection(offer.get(), cricket::CN_VIDEO));
+}
+
+TEST_F(PeerConnectionMediaTest, AnswerHasDifferentDirectionsForAudioVideo) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnection();
+ callee->AddVideoTrack("v");
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ RTCOfferAnswerOptions options;
+ options.offer_to_receive_audio = 1;
+ options.offer_to_receive_video = 0;
+ auto answer = callee->CreateAnswer(options);
+
+ EXPECT_EQ(cricket::MD_RECVONLY,
+ GetMediaContentDirection(answer.get(), cricket::CN_AUDIO));
+ EXPECT_EQ(cricket::MD_SENDONLY,
+ GetMediaContentDirection(answer.get(), cricket::CN_VIDEO));
+}
+
+void AddComfortNoiseCodecsToSend(cricket::FakeMediaEngine* media_engine) {
+ const cricket::AudioCodec kComfortNoiseCodec8k(102, "CN", 8000, 0, 1);
+ const cricket::AudioCodec kComfortNoiseCodec16k(103, "CN", 16000, 0, 1);
+
+ auto codecs = media_engine->audio_send_codecs();
+ codecs.push_back(kComfortNoiseCodec8k);
+ codecs.push_back(kComfortNoiseCodec16k);
+ media_engine->SetAudioCodecs(codecs);
+}
+
+bool HasAnyComfortNoiseCodecs(const cricket::SessionDescription* desc) {
+ const auto* audio_desc = cricket::GetFirstAudioContentDescription(desc);
+ for (const auto& codec : audio_desc->codecs()) {
+ if (codec.name == "CN") {
+ return true;
+ }
+ }
+ return false;
+}
+
+TEST_F(PeerConnectionMediaTest,
+ CreateOfferWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ AddComfortNoiseCodecsToSend(caller->media_engine());
+
+ RTCOfferAnswerOptions options;
+ options.voice_activity_detection = false;
+ auto offer = caller->CreateOffer(options);
+
+ EXPECT_FALSE(HasAnyComfortNoiseCodecs(offer->description()));
+}
+
+TEST_F(PeerConnectionMediaTest,
+ CreateAnswerWithNoVoiceActivityDetectionIncludesNoComfortNoiseCodecs) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ AddComfortNoiseCodecsToSend(caller->media_engine());
+ auto callee = CreatePeerConnectionWithAudioVideo();
+ AddComfortNoiseCodecsToSend(callee->media_engine());
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ RTCOfferAnswerOptions options;
+ options.voice_activity_detection = false;
+ auto answer = callee->CreateAnswer(options);
+
+ EXPECT_FALSE(HasAnyComfortNoiseCodecs(answer->description()));
+}
+
+// The following test group verifies that we reject answers with invalid media
+// sections as per RFC 3264.
+
+class PeerConnectionMediaInvalidMediaTest
+ : public PeerConnectionMediaTest,
+ public ::testing::WithParamInterface<
+ std::tuple<std::string,
+ std::function<void(cricket::SessionDescription*)>,
+ std::string>> {
+ protected:
+ PeerConnectionMediaInvalidMediaTest() {
+ mutator_ = std::get<1>(GetParam());
+ expected_error_ = std::get<2>(GetParam());
+ }
+
+ std::function<void(cricket::SessionDescription*)> mutator_;
+ std::string expected_error_;
+};
+
+TEST_P(PeerConnectionMediaInvalidMediaTest, FailToSetRemoteAnswer) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ auto answer = callee->CreateAnswer();
+ mutator_(answer->description());
+
+ std::string error;
+ ASSERT_FALSE(caller->SetRemoteDescription(std::move(answer), &error));
+ EXPECT_EQ("Failed to set remote answer sdp: " + expected_error_, error);
+}
+
+TEST_P(PeerConnectionMediaInvalidMediaTest, FailToSetLocalAnswer) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ auto answer = callee->CreateAnswer();
+ mutator_(answer->description());
+
+ std::string error;
+ ASSERT_FALSE(callee->SetLocalDescription(std::move(answer), &error));
+ EXPECT_EQ("Failed to set local answer sdp: " + expected_error_, error);
+}
+
+void RemoveVideoContent(cricket::SessionDescription* desc) {
+ auto content_name = cricket::GetFirstVideoContent(desc)->name;
+ desc->RemoveContentByName(content_name);
+ desc->RemoveTransportInfoByName(content_name);
+}
+
+void RenameVideoContent(cricket::SessionDescription* desc) {
+ auto* video_content = cricket::GetFirstVideoContent(desc);
+ auto* transport_info = desc->GetTransportInfoByName(video_content->name);
+ video_content->name = "video_renamed";
+ transport_info->content_name = video_content->name;
+}
+
+void ReverseMediaContent(cricket::SessionDescription* desc) {
+ std::reverse(desc->contents().begin(), desc->contents().end());
+ std::reverse(desc->transport_infos().begin(), desc->transport_infos().end());
+}
+
+void ChangeMediaTypeAudioToVideo(cricket::SessionDescription* desc) {
+ desc->RemoveContentByName(cricket::CN_AUDIO);
+ auto* video_content = desc->GetContentByName(cricket::CN_VIDEO);
+ desc->AddContent(cricket::CN_AUDIO, cricket::NS_JINGLE_RTP,
+ video_content->description->Copy());
+}
+
+constexpr char kMLinesOutOfOrder[] =
+ "The order of m-lines in answer doesn't match order in offer. Rejecting "
+ "answer.";
+
+INSTANTIATE_TEST_CASE_P(
+ PeerConnectionMediaTest,
+ PeerConnectionMediaInvalidMediaTest,
+ Values(
+ std::make_tuple("remove video", RemoveVideoContent, kMLinesOutOfOrder),
+ std::make_tuple("rename video", RenameVideoContent, kMLinesOutOfOrder),
+ std::make_tuple("reverse media sections",
+ ReverseMediaContent,
+ kMLinesOutOfOrder),
+ std::make_tuple("change audio type to video type",
+ ChangeMediaTypeAudioToVideo,
+ kMLinesOutOfOrder)));
+
+// Test that the correct media engine send/recv streams are created when doing
+// a series of offer/answers where audio/video are both sent, then audio is
+// rejected, then both audio/video sent again.
+TEST_F(PeerConnectionMediaTest, TestAVOfferWithAudioOnlyAnswer) {
+ RTCOfferAnswerOptions options_reject_video;
+ options_reject_video.offer_to_receive_audio =
+ RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
+ options_reject_video.offer_to_receive_video = 0;
+
+ auto caller = CreatePeerConnection();
+ caller->AddAudioTrack("a");
+ caller->AddVideoTrack("v");
+ auto callee = CreatePeerConnection();
+
+ // Caller initially offers to send/recv audio and video.
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ // Callee accepts the audio as recv only but rejects the video.
+ ASSERT_TRUE(caller->SetRemoteDescription(
+ callee->CreateAnswerAndSetAsLocal(options_reject_video)));
+
+ auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
+ ASSERT_TRUE(caller_voice);
+ EXPECT_EQ(0u, caller_voice->recv_streams().size());
+ EXPECT_EQ(1u, caller_voice->send_streams().size());
+ auto caller_video = caller->media_engine()->GetVideoChannel(0);
+ EXPECT_FALSE(caller_video);
+
+ // Callee adds its own audio/video stream and offers to receive audio/video
+ // too.
+ callee->AddAudioTrack("a");
+ auto callee_video_track = callee->AddVideoTrack("v");
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
+ ASSERT_TRUE(callee_voice);
+ EXPECT_EQ(1u, callee_voice->recv_streams().size());
+ EXPECT_EQ(1u, callee_voice->send_streams().size());
+ auto callee_video = callee->media_engine()->GetVideoChannel(0);
+ ASSERT_TRUE(callee_video);
+ EXPECT_EQ(1u, callee_video->recv_streams().size());
+ EXPECT_EQ(1u, callee_video->send_streams().size());
+
+ // Callee removes video but keeps audio and rejects the video once again.
+ callee->pc()->RemoveTrack(callee_video_track);
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ callee->SetLocalDescription(callee->CreateAnswer(options_reject_video)));
+
+ callee_voice = callee->media_engine()->GetVoiceChannel(0);
+ ASSERT_TRUE(callee_voice);
+ EXPECT_EQ(1u, callee_voice->recv_streams().size());
+ EXPECT_EQ(1u, callee_voice->send_streams().size());
+ callee_video = callee->media_engine()->GetVideoChannel(0);
+ EXPECT_FALSE(callee_video);
+}
+
+// Test that the correct media engine send/recv streams are created when doing
+// a series of offer/answers where audio/video are both sent, then video is
+// rejected, then both audio/video sent again.
+TEST_F(PeerConnectionMediaTest, TestAVOfferWithVideoOnlyAnswer) {
+ // Disable the bundling here. If the media is bundled on audio
+ // transport, then we can't reject the audio because switching the bundled
+ // transport is not currently supported.
+ // (https://bugs.chromium.org/p/webrtc/issues/detail?id=6704)
+ RTCOfferAnswerOptions options_no_bundle;
+ options_no_bundle.use_rtp_mux = false;
+ RTCOfferAnswerOptions options_reject_audio = options_no_bundle;
+ options_reject_audio.offer_to_receive_audio = 0;
+ options_reject_audio.offer_to_receive_video =
+ RTCOfferAnswerOptions::kMaxOfferToReceiveMedia;
+
+ auto caller = CreatePeerConnection();
+ caller->AddAudioTrack("a");
+ caller->AddVideoTrack("v");
+ auto callee = CreatePeerConnection();
+
+ // Caller initially offers to send/recv audio and video.
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ // Callee accepts the video as recv only but rejects the audio.
+ ASSERT_TRUE(caller->SetRemoteDescription(
+ callee->CreateAnswerAndSetAsLocal(options_reject_audio)));
+
+ auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
+ EXPECT_FALSE(caller_voice);
+ auto caller_video = caller->media_engine()->GetVideoChannel(0);
+ ASSERT_TRUE(caller_video);
+ EXPECT_EQ(0u, caller_video->recv_streams().size());
+ EXPECT_EQ(1u, caller_video->send_streams().size());
+
+ // Callee adds its own audio/video stream and offers to receive audio/video
+ // too.
+ auto callee_audio_track = callee->AddAudioTrack("a");
+ callee->AddVideoTrack("v");
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(caller->SetRemoteDescription(
+ callee->CreateAnswerAndSetAsLocal(options_no_bundle)));
+
+ auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
+ ASSERT_TRUE(callee_voice);
+ EXPECT_EQ(1u, callee_voice->recv_streams().size());
+ EXPECT_EQ(1u, callee_voice->send_streams().size());
+ auto callee_video = callee->media_engine()->GetVideoChannel(0);
+ ASSERT_TRUE(callee_video);
+ EXPECT_EQ(1u, callee_video->recv_streams().size());
+ EXPECT_EQ(1u, callee_video->send_streams().size());
+
+ // Callee removes audio but keeps video and rejects the audio once again.
+ callee->pc()->RemoveTrack(callee_audio_track);
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ callee->SetLocalDescription(callee->CreateAnswer(options_reject_audio)));
+
+ callee_voice = callee->media_engine()->GetVoiceChannel(0);
+ EXPECT_FALSE(callee_voice);
+ callee_video = callee->media_engine()->GetVideoChannel(0);
+ ASSERT_TRUE(callee_video);
+ EXPECT_EQ(1u, callee_video->recv_streams().size());
+ EXPECT_EQ(1u, callee_video->send_streams().size());
+}
+
+// Tests that if the underlying video encoder fails to be initialized (signaled
+// by failing to set send codecs), the PeerConnection signals the error to the
+// client.
+TEST_F(PeerConnectionMediaTest, MediaEngineErrorPropagatedToClients) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ auto video_channel = caller->media_engine()->GetVideoChannel(0);
+ video_channel->set_fail_set_send_codecs(true);
+
+ std::string error;
+ ASSERT_FALSE(caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(),
+ &error));
+ EXPECT_EQ(
+ "Failed to set remote answer sdp: Session error code: ERROR_CONTENT. "
+ "Session error description: Failed to set remote video description send "
+ "parameters..",
+ error);
+}
+
+// Tests that if the underlying video encoder fails once then subsequent
+// attempts at setting the local/remote description will also fail, even if
+// SetSendCodecs no longer fails.
+TEST_F(PeerConnectionMediaTest,
+ FailToApplyDescriptionIfVideoEncoderHasEverFailed) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ auto video_channel = caller->media_engine()->GetVideoChannel(0);
+ video_channel->set_fail_set_send_codecs(true);
+
+ EXPECT_FALSE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ video_channel->set_fail_set_send_codecs(false);
+
+ EXPECT_FALSE(caller->SetRemoteDescription(callee->CreateAnswer()));
+ EXPECT_FALSE(caller->SetLocalDescription(caller->CreateOffer()));
+}
+
+void RenameContent(cricket::SessionDescription* desc,
+ const std::string& old_name,
+ const std::string& new_name) {
+ auto* content = desc->GetContentByName(old_name);
+ RTC_DCHECK(content);
+ content->name = new_name;
+ auto* transport = desc->GetTransportInfoByName(old_name);
+ RTC_DCHECK(transport);
+ transport->content_name = new_name;
+}
+
+// Tests that an answer responds with the same MIDs as the offer.
+TEST_F(PeerConnectionMediaTest, AnswerHasSameMidsAsOffer) {
+ const std::string kAudioMid = "not default1";
+ const std::string kVideoMid = "not default2";
+
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ auto offer = caller->CreateOffer();
+ RenameContent(offer->description(), cricket::CN_AUDIO, kAudioMid);
+ RenameContent(offer->description(), cricket::CN_VIDEO, kVideoMid);
+ ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
+
+ auto answer = callee->CreateAnswer();
+ EXPECT_EQ(kAudioMid,
+ cricket::GetFirstAudioContent(answer->description())->name);
+ EXPECT_EQ(kVideoMid,
+ cricket::GetFirstVideoContent(answer->description())->name);
+}
+
+// Test that if the callee creates a re-offer, the MIDs are the same as the
+// original offer.
+TEST_F(PeerConnectionMediaTest, ReOfferHasSameMidsAsFirstOffer) {
+ const std::string kAudioMid = "not default1";
+ const std::string kVideoMid = "not default2";
+
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ auto offer = caller->CreateOffer();
+ RenameContent(offer->description(), cricket::CN_AUDIO, kAudioMid);
+ RenameContent(offer->description(), cricket::CN_VIDEO, kVideoMid);
+ ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
+ ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
+
+ auto reoffer = callee->CreateOffer();
+ EXPECT_EQ(kAudioMid,
+ cricket::GetFirstAudioContent(reoffer->description())->name);
+ EXPECT_EQ(kVideoMid,
+ cricket::GetFirstVideoContent(reoffer->description())->name);
+}
+
+TEST_F(PeerConnectionMediaTest,
+ CombinedAudioVideoBweConfigPropagatedToMediaEngine) {
+ RTCConfiguration config;
+ config.combined_audio_video_bwe.emplace(true);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+
+ ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
+
+ auto caller_voice = caller->media_engine()->GetVoiceChannel(0);
+ ASSERT_TRUE(caller_voice);
+ const cricket::AudioOptions& audio_options = caller_voice->options();
+ EXPECT_EQ(config.combined_audio_video_bwe,
+ audio_options.combined_audio_video_bwe);
+}
+
+} // namespace webrtc
diff --git a/pc/peerconnection_signaling_unittest.cc b/pc/peerconnection_signaling_unittest.cc
new file mode 100644
index 0000000..0d9bf62
--- /dev/null
+++ b/pc/peerconnection_signaling_unittest.cc
@@ -0,0 +1,506 @@
+/*
+ * 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.
+ */
+
+// This file contains tests that check the PeerConnection's signaling state
+// machine, as well as tests that check basic, media-agnostic aspects of SDP.
+
+#include <tuple>
+
+#include "api/audio_codecs/builtin_audio_decoder_factory.h"
+#include "api/audio_codecs/builtin_audio_encoder_factory.h"
+#include "api/peerconnectionproxy.h"
+#include "pc/peerconnection.h"
+#include "pc/peerconnectionwrapper.h"
+#include "pc/sdputils.h"
+#ifdef WEBRTC_ANDROID
+#include "pc/test/androidtestinitializer.h"
+#endif
+#include "pc/test/fakeaudiocapturemodule.h"
+#include "pc/test/fakertccertificategenerator.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/ptr_util.h"
+#include "rtc_base/stringutils.h"
+#include "rtc_base/virtualsocketserver.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+using SignalingState = PeerConnectionInterface::SignalingState;
+using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
+using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
+using ::testing::Bool;
+using ::testing::Combine;
+using ::testing::Values;
+
+class PeerConnectionWrapperForSignalingTest : public PeerConnectionWrapper {
+ public:
+ using PeerConnectionWrapper::PeerConnectionWrapper;
+
+ bool initial_offerer() {
+ return GetInternalPeerConnection()->initial_offerer();
+ }
+
+ PeerConnection* GetInternalPeerConnection() {
+ auto* pci = reinterpret_cast<
+ PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(pc());
+ return reinterpret_cast<PeerConnection*>(pci->internal());
+ }
+};
+
+class PeerConnectionSignalingTest : public ::testing::Test {
+ protected:
+ typedef std::unique_ptr<PeerConnectionWrapperForSignalingTest> WrapperPtr;
+
+ PeerConnectionSignalingTest()
+ : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
+#ifdef WEBRTC_ANDROID
+ InitializeAndroidObjects();
+#endif
+ pc_factory_ = CreatePeerConnectionFactory(
+ rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
+ FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
+ CreateBuiltinAudioDecoderFactory(), nullptr, nullptr);
+ }
+
+ WrapperPtr CreatePeerConnection() {
+ return CreatePeerConnection(RTCConfiguration());
+ }
+
+ WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
+ auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
+ auto pc = pc_factory_->CreatePeerConnection(config, nullptr, nullptr,
+ observer.get());
+ if (!pc) {
+ return nullptr;
+ }
+
+ return rtc::MakeUnique<PeerConnectionWrapperForSignalingTest>(
+ pc_factory_, pc, std::move(observer));
+ }
+
+ // Accepts the same arguments as CreatePeerConnection and adds default audio
+ // and video tracks.
+ template <typename... Args>
+ WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
+ auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
+ if (!wrapper) {
+ return nullptr;
+ }
+ wrapper->AddAudioTrack("a");
+ wrapper->AddVideoTrack("v");
+ return wrapper;
+ }
+
+ std::unique_ptr<rtc::VirtualSocketServer> vss_;
+ rtc::AutoSocketServerThread main_;
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
+};
+
+TEST_F(PeerConnectionSignalingTest, SetLocalOfferTwiceWorks) {
+ auto caller = CreatePeerConnection();
+
+ EXPECT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
+ EXPECT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
+}
+
+TEST_F(PeerConnectionSignalingTest, SetRemoteOfferTwiceWorks) {
+ auto caller = CreatePeerConnection();
+ auto callee = CreatePeerConnection();
+
+ EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
+ EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
+}
+
+TEST_F(PeerConnectionSignalingTest, FailToSetNullLocalDescription) {
+ auto caller = CreatePeerConnection();
+ std::string error;
+ ASSERT_FALSE(caller->SetLocalDescription(nullptr, &error));
+ EXPECT_EQ("SessionDescription is NULL.", error);
+}
+
+TEST_F(PeerConnectionSignalingTest, FailToSetNullRemoteDescription) {
+ auto caller = CreatePeerConnection();
+ std::string error;
+ ASSERT_FALSE(caller->SetRemoteDescription(nullptr, &error));
+ EXPECT_EQ("SessionDescription is NULL.", error);
+}
+
+// The following parameterized test verifies that calls to various signaling
+// methods on PeerConnection will succeed/fail depending on what is the
+// PeerConnection's signaling state. Note that the test tries many different
+// forms of SignalingState::kClosed by arriving at a valid state then calling
+// |Close()|. This is intended to catch cases where the PeerConnection signaling
+// method ignores the closed flag but may work/not work because of the single
+// state the PeerConnection was created in before it was closed.
+
+class PeerConnectionSignalingStateTest
+ : public PeerConnectionSignalingTest,
+ public ::testing::WithParamInterface<std::tuple<SignalingState, bool>> {
+ protected:
+ RTCConfiguration GetConfig() {
+ RTCConfiguration config;
+ config.certificates.push_back(
+ FakeRTCCertificateGenerator::GenerateCertificate());
+ return config;
+ }
+
+ WrapperPtr CreatePeerConnectionInState(SignalingState state) {
+ return CreatePeerConnectionInState(std::make_tuple(state, false));
+ }
+
+ WrapperPtr CreatePeerConnectionInState(
+ std::tuple<SignalingState, bool> state_tuple) {
+ SignalingState state = std::get<0>(state_tuple);
+ bool closed = std::get<1>(state_tuple);
+
+ auto wrapper = CreatePeerConnectionWithAudioVideo(GetConfig());
+ switch (state) {
+ case SignalingState::kStable: {
+ break;
+ }
+ case SignalingState::kHaveLocalOffer: {
+ wrapper->SetLocalDescription(wrapper->CreateOffer());
+ break;
+ }
+ case SignalingState::kHaveLocalPrAnswer: {
+ auto caller = CreatePeerConnectionWithAudioVideo(GetConfig());
+ wrapper->SetRemoteDescription(caller->CreateOffer());
+ auto answer = wrapper->CreateAnswer();
+ wrapper->SetLocalDescription(CloneSessionDescriptionAsType(
+ answer.get(), SessionDescriptionInterface::kPrAnswer));
+ break;
+ }
+ case SignalingState::kHaveRemoteOffer: {
+ auto caller = CreatePeerConnectionWithAudioVideo(GetConfig());
+ wrapper->SetRemoteDescription(caller->CreateOffer());
+ break;
+ }
+ case SignalingState::kHaveRemotePrAnswer: {
+ auto callee = CreatePeerConnectionWithAudioVideo(GetConfig());
+ callee->SetRemoteDescription(wrapper->CreateOfferAndSetAsLocal());
+ auto answer = callee->CreateAnswer();
+ wrapper->SetRemoteDescription(CloneSessionDescriptionAsType(
+ answer.get(), SessionDescriptionInterface::kPrAnswer));
+ break;
+ }
+ case SignalingState::kClosed: {
+ RTC_NOTREACHED() << "Set the second member of the tuple to true to "
+ "achieve a closed state from an existing, valid "
+ "state.";
+ }
+ }
+
+ RTC_DCHECK_EQ(state, wrapper->pc()->signaling_state());
+
+ if (closed) {
+ wrapper->pc()->Close();
+ RTC_DCHECK_EQ(SignalingState::kClosed, wrapper->signaling_state());
+ }
+
+ return wrapper;
+ }
+};
+
+::testing::AssertionResult AssertStartsWith(const char* str_expr,
+ const char* prefix_expr,
+ const std::string& str,
+ const std::string& prefix) {
+ if (rtc::starts_with(str.c_str(), prefix.c_str())) {
+ return ::testing::AssertionSuccess();
+ } else {
+ return ::testing::AssertionFailure()
+ << str_expr << "\nwhich is\n\"" << str << "\"\ndoes not start with\n"
+ << prefix_expr << "\nwhich is\n\"" << prefix << "\"";
+ }
+}
+
+TEST_P(PeerConnectionSignalingStateTest, CreateOffer) {
+ auto wrapper = CreatePeerConnectionInState(GetParam());
+ if (wrapper->signaling_state() != SignalingState::kClosed) {
+ EXPECT_TRUE(wrapper->CreateOffer());
+ } else {
+ std::string error;
+ ASSERT_FALSE(wrapper->CreateOffer(RTCOfferAnswerOptions(), &error));
+ EXPECT_PRED_FORMAT2(AssertStartsWith, error,
+ "CreateOffer called when PeerConnection is closed.");
+ }
+}
+
+TEST_P(PeerConnectionSignalingStateTest, CreateAnswer) {
+ auto wrapper = CreatePeerConnectionInState(GetParam());
+ if (wrapper->signaling_state() == SignalingState::kHaveLocalPrAnswer ||
+ wrapper->signaling_state() == SignalingState::kHaveRemoteOffer) {
+ EXPECT_TRUE(wrapper->CreateAnswer());
+ } else {
+ std::string error;
+ ASSERT_FALSE(wrapper->CreateAnswer(RTCOfferAnswerOptions(), &error));
+ if (wrapper->signaling_state() == SignalingState::kClosed) {
+ EXPECT_PRED_FORMAT2(AssertStartsWith, error,
+ "CreateAnswer called when PeerConnection is closed.");
+ } else if (wrapper->signaling_state() ==
+ SignalingState::kHaveRemotePrAnswer) {
+ EXPECT_PRED_FORMAT2(AssertStartsWith, error,
+ "CreateAnswer called without remote offer.");
+ } else {
+ EXPECT_PRED_FORMAT2(
+ AssertStartsWith, error,
+ "CreateAnswer can't be called before SetRemoteDescription.");
+ }
+ }
+}
+
+TEST_P(PeerConnectionSignalingStateTest, SetLocalOffer) {
+ auto wrapper = CreatePeerConnectionInState(GetParam());
+ if (wrapper->signaling_state() == SignalingState::kStable ||
+ wrapper->signaling_state() == SignalingState::kHaveLocalOffer) {
+ // Need to call CreateOffer on the PeerConnection under test, otherwise when
+ // setting the local offer it will want to verify the DTLS fingerprint
+ // against the locally generated certificate, but without a call to
+ // CreateOffer the certificate will never be generated.
+ EXPECT_TRUE(wrapper->SetLocalDescription(wrapper->CreateOffer()));
+ } else {
+ auto wrapper_for_offer =
+ CreatePeerConnectionInState(SignalingState::kHaveLocalOffer);
+ auto offer =
+ CloneSessionDescription(wrapper_for_offer->pc()->local_description());
+
+ std::string error;
+ ASSERT_FALSE(wrapper->SetLocalDescription(std::move(offer), &error));
+ EXPECT_PRED_FORMAT2(
+ AssertStartsWith, error,
+ "Failed to set local offer sdp: Called in wrong state:");
+ }
+}
+
+TEST_P(PeerConnectionSignalingStateTest, SetLocalPrAnswer) {
+ auto wrapper_for_pranswer =
+ CreatePeerConnectionInState(SignalingState::kHaveLocalPrAnswer);
+ auto pranswer =
+ CloneSessionDescription(wrapper_for_pranswer->pc()->local_description());
+
+ auto wrapper = CreatePeerConnectionInState(GetParam());
+ if (wrapper->signaling_state() == SignalingState::kHaveLocalPrAnswer ||
+ wrapper->signaling_state() == SignalingState::kHaveRemoteOffer) {
+ EXPECT_TRUE(wrapper->SetLocalDescription(std::move(pranswer)));
+ } else {
+ std::string error;
+ ASSERT_FALSE(wrapper->SetLocalDescription(std::move(pranswer), &error));
+ EXPECT_PRED_FORMAT2(
+ AssertStartsWith, error,
+ "Failed to set local pranswer sdp: Called in wrong state:");
+ }
+}
+
+TEST_P(PeerConnectionSignalingStateTest, SetLocalAnswer) {
+ auto wrapper_for_answer =
+ CreatePeerConnectionInState(SignalingState::kHaveRemoteOffer);
+ auto answer = wrapper_for_answer->CreateAnswer();
+
+ auto wrapper = CreatePeerConnectionInState(GetParam());
+ if (wrapper->signaling_state() == SignalingState::kHaveLocalPrAnswer ||
+ wrapper->signaling_state() == SignalingState::kHaveRemoteOffer) {
+ EXPECT_TRUE(wrapper->SetLocalDescription(std::move(answer)));
+ } else {
+ std::string error;
+ ASSERT_FALSE(wrapper->SetLocalDescription(std::move(answer), &error));
+ EXPECT_PRED_FORMAT2(
+ AssertStartsWith, error,
+ "Failed to set local answer sdp: Called in wrong state:");
+ }
+}
+
+TEST_P(PeerConnectionSignalingStateTest, SetRemoteOffer) {
+ auto wrapper_for_offer =
+ CreatePeerConnectionInState(SignalingState::kHaveRemoteOffer);
+ auto offer =
+ CloneSessionDescription(wrapper_for_offer->pc()->remote_description());
+
+ auto wrapper = CreatePeerConnectionInState(GetParam());
+ if (wrapper->signaling_state() == SignalingState::kStable ||
+ wrapper->signaling_state() == SignalingState::kHaveRemoteOffer) {
+ EXPECT_TRUE(wrapper->SetRemoteDescription(std::move(offer)));
+ } else {
+ std::string error;
+ ASSERT_FALSE(wrapper->SetRemoteDescription(std::move(offer), &error));
+ EXPECT_PRED_FORMAT2(
+ AssertStartsWith, error,
+ "Failed to set remote offer sdp: Called in wrong state:");
+ }
+}
+
+TEST_P(PeerConnectionSignalingStateTest, SetRemotePrAnswer) {
+ auto wrapper_for_pranswer =
+ CreatePeerConnectionInState(SignalingState::kHaveRemotePrAnswer);
+ auto pranswer =
+ CloneSessionDescription(wrapper_for_pranswer->pc()->remote_description());
+
+ auto wrapper = CreatePeerConnectionInState(GetParam());
+ if (wrapper->signaling_state() == SignalingState::kHaveLocalOffer ||
+ wrapper->signaling_state() == SignalingState::kHaveRemotePrAnswer) {
+ EXPECT_TRUE(wrapper->SetRemoteDescription(std::move(pranswer)));
+ } else {
+ std::string error;
+ ASSERT_FALSE(wrapper->SetRemoteDescription(std::move(pranswer), &error));
+ EXPECT_PRED_FORMAT2(
+ AssertStartsWith, error,
+ "Failed to set remote pranswer sdp: Called in wrong state:");
+ }
+}
+
+TEST_P(PeerConnectionSignalingStateTest, SetRemoteAnswer) {
+ auto wrapper_for_answer =
+ CreatePeerConnectionInState(SignalingState::kHaveRemoteOffer);
+ auto answer = wrapper_for_answer->CreateAnswer();
+
+ auto wrapper = CreatePeerConnectionInState(GetParam());
+ if (wrapper->signaling_state() == SignalingState::kHaveLocalOffer ||
+ wrapper->signaling_state() == SignalingState::kHaveRemotePrAnswer) {
+ EXPECT_TRUE(wrapper->SetRemoteDescription(std::move(answer)));
+ } else {
+ std::string error;
+ ASSERT_FALSE(wrapper->SetRemoteDescription(std::move(answer), &error));
+ EXPECT_PRED_FORMAT2(
+ AssertStartsWith, error,
+ "Failed to set remote answer sdp: Called in wrong state:");
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(PeerConnectionSignalingTest,
+ PeerConnectionSignalingStateTest,
+ Combine(Values(SignalingState::kStable,
+ SignalingState::kHaveLocalOffer,
+ SignalingState::kHaveLocalPrAnswer,
+ SignalingState::kHaveRemoteOffer,
+ SignalingState::kHaveRemotePrAnswer),
+ Bool()));
+
+TEST_F(PeerConnectionSignalingTest,
+ CreateAnswerSucceedsIfStableAndRemoteDescriptionIsOffer) {
+ auto caller = CreatePeerConnection();
+ auto callee = CreatePeerConnection();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ ASSERT_EQ(SignalingState::kStable, callee->signaling_state());
+ EXPECT_TRUE(callee->CreateAnswer());
+}
+
+TEST_F(PeerConnectionSignalingTest,
+ CreateAnswerFailsIfStableButRemoteDescriptionIsAnswer) {
+ auto caller = CreatePeerConnection();
+ auto callee = CreatePeerConnection();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ ASSERT_EQ(SignalingState::kStable, caller->signaling_state());
+ std::string error;
+ ASSERT_FALSE(caller->CreateAnswer(RTCOfferAnswerOptions(), &error));
+ EXPECT_EQ("CreateAnswer called without remote offer.", error);
+}
+
+// According to https://tools.ietf.org/html/rfc3264#section-8, the session id
+// stays the same but the version must be incremented if a later, different
+// session description is generated. These two tests verify that is the case for
+// both offers and answers.
+TEST_F(PeerConnectionSignalingTest,
+ SessionVersionIncrementedInSubsequentDifferentOffer) {
+ auto caller = CreatePeerConnection();
+ auto callee = CreatePeerConnection();
+
+ auto original_offer = caller->CreateOfferAndSetAsLocal();
+ const std::string original_id = original_offer->session_id();
+ const std::string original_version = original_offer->session_version();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(std::move(original_offer)));
+ ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateAnswer()));
+
+ // Add track to get a different offer.
+ caller->AddAudioTrack("a");
+
+ auto later_offer = caller->CreateOffer();
+
+ EXPECT_EQ(original_id, later_offer->session_id());
+ EXPECT_LT(rtc::FromString<uint64_t>(original_version),
+ rtc::FromString<uint64_t>(later_offer->session_version()));
+}
+TEST_F(PeerConnectionSignalingTest,
+ SessionVersionIncrementedInSubsequentDifferentAnswer) {
+ auto caller = CreatePeerConnection();
+ auto callee = CreatePeerConnection();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ auto original_answer = callee->CreateAnswerAndSetAsLocal();
+ const std::string original_id = original_answer->session_id();
+ const std::string original_version = original_answer->session_version();
+
+ // Add track to get a different answer.
+ callee->AddAudioTrack("a");
+
+ auto later_answer = callee->CreateAnswer();
+
+ EXPECT_EQ(original_id, later_answer->session_id());
+ EXPECT_LT(rtc::FromString<uint64_t>(original_version),
+ rtc::FromString<uint64_t>(later_answer->session_version()));
+}
+
+TEST_F(PeerConnectionSignalingTest, InitiatorFlagSetOnCallerAndNotOnCallee) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ EXPECT_FALSE(caller->initial_offerer());
+ EXPECT_FALSE(callee->initial_offerer());
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ EXPECT_TRUE(caller->initial_offerer());
+ EXPECT_FALSE(callee->initial_offerer());
+
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ EXPECT_TRUE(caller->initial_offerer());
+ EXPECT_FALSE(callee->initial_offerer());
+}
+
+// Test creating a PeerConnection, request multiple offers, destroy the
+// PeerConnection and make sure we get success/failure callbacks for all of the
+// requests.
+// Background: crbug.com/507307
+TEST_F(PeerConnectionSignalingTest, CreateOffersAndShutdown) {
+ auto caller = CreatePeerConnection();
+
+ RTCOfferAnswerOptions options;
+ options.offer_to_receive_audio =
+ RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
+
+ rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observers[100];
+ for (auto& observer : observers) {
+ observer =
+ new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
+ caller->pc()->CreateOffer(observer, options);
+ }
+
+ // Destroy the PeerConnection.
+ caller.reset(nullptr);
+
+ for (auto& observer : observers) {
+ // We expect to have received a notification now even if the PeerConnection
+ // was terminated. The offer creation may or may not have succeeded, but we
+ // must have received a notification.
+ EXPECT_TRUE(observer->called());
+ }
+}
+
+} // namespace webrtc
diff --git a/pc/peerconnectioninterface_unittest.cc b/pc/peerconnectioninterface_unittest.cc
index 99f3301..a8b4f72 100644
--- a/pc/peerconnectioninterface_unittest.cc
+++ b/pc/peerconnectioninterface_unittest.cc
@@ -2517,9 +2517,9 @@
EXPECT_TRUE(pc_->remote_description() != NULL);
std::unique_ptr<SessionDescriptionInterface> offer;
- EXPECT_TRUE(DoCreateOffer(&offer, nullptr));
+ EXPECT_FALSE(DoCreateOffer(&offer, nullptr));
std::unique_ptr<SessionDescriptionInterface> answer;
- EXPECT_TRUE(DoCreateAnswer(&answer, nullptr));
+ EXPECT_FALSE(DoCreateAnswer(&answer, nullptr));
std::string sdp;
ASSERT_TRUE(pc_->remote_description()->ToString(&sdp));
@@ -3558,32 +3558,6 @@
EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
}
-// Test that if |voice_activity_detection| is false, no CN codec is added to the
-// offer.
-TEST_F(PeerConnectionInterfaceTest, CreateOfferWithVADOptions) {
- RTCOfferAnswerOptions rtc_options;
- rtc_options.offer_to_receive_audio = 1;
- rtc_options.offer_to_receive_video = 0;
-
- std::unique_ptr<SessionDescriptionInterface> offer;
- CreatePeerConnection();
- offer = CreateOfferWithOptions(rtc_options);
- ASSERT_TRUE(offer);
- const cricket::ContentInfo* audio_content =
- offer->description()->GetContentByName(cricket::CN_AUDIO);
- ASSERT_TRUE(audio_content);
- // |voice_activity_detection| is true by default.
- EXPECT_TRUE(HasCNCodecs(audio_content));
-
- rtc_options.voice_activity_detection = false;
- CreatePeerConnection();
- offer = CreateOfferWithOptions(rtc_options);
- ASSERT_TRUE(offer);
- audio_content = offer->description()->GetContentByName(cricket::CN_AUDIO);
- ASSERT_TRUE(audio_content);
- EXPECT_FALSE(HasCNCodecs(audio_content));
-}
-
// Test that no media content will be added to the offer if using default
// RTCOfferAnswerOptions.
TEST_F(PeerConnectionInterfaceTest, CreateOfferWithDefaultOfferAnswerOptions) {
@@ -3664,42 +3638,6 @@
EXPECT_FALSE(offer->description()->HasGroup(cricket::GROUP_TYPE_BUNDLE));
}
-// If SetMandatoryReceiveAudio(false) and SetMandatoryReceiveVideo(false) are
-// called for the answer constraints, but an audio and a video section were
-// offered, there will still be an audio and a video section in the answer.
-TEST_F(PeerConnectionInterfaceTest,
- RejectAudioAndVideoInAnswerWithConstraints) {
- // Offer both audio and video.
- RTCOfferAnswerOptions rtc_offer_options;
- rtc_offer_options.offer_to_receive_audio = 1;
- rtc_offer_options.offer_to_receive_video = 1;
-
- CreatePeerConnection();
- std::unique_ptr<SessionDescriptionInterface> offer;
- CreateOfferWithOptionsAsRemoteDescription(&offer, rtc_offer_options);
- EXPECT_NE(nullptr, GetFirstAudioContent(offer->description()));
- EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
-
- // Since an offer has been created with both audio and video,
- // Answers will contain the media types that exist in the offer regardless of
- // the value of |answer_options.has_audio| and |answer_options.has_video|.
- FakeConstraints answer_c;
- // Reject both audio and video.
- answer_c.SetMandatoryReceiveAudio(false);
- answer_c.SetMandatoryReceiveVideo(false);
-
- std::unique_ptr<SessionDescriptionInterface> answer;
- ASSERT_TRUE(DoCreateAnswer(&answer, &answer_c));
- const cricket::ContentInfo* audio_content =
- GetFirstAudioContent(answer->description());
- const cricket::ContentInfo* video_content =
- GetFirstVideoContent(answer->description());
- ASSERT_NE(nullptr, audio_content);
- ASSERT_NE(nullptr, video_content);
- EXPECT_TRUE(audio_content->rejected);
- EXPECT_TRUE(video_content->rejected);
-}
-
// This test ensures OnRenegotiationNeeded is called when we add track with
// MediaStream -> AddTrack in the same way it is called when we add track with
// PeerConnection -> AddTrack.
@@ -3734,52 +3672,6 @@
observer_.renegotiation_needed_ = false;
}
-// Tests that creating answer would fail gracefully without being crashed if the
-// remote description is unset.
-TEST_F(PeerConnectionInterfaceTest, CreateAnswerWithoutRemoteDescription) {
- CreatePeerConnection();
- // Creating answer fails because the remote description is unset.
- std::unique_ptr<SessionDescriptionInterface> answer;
- EXPECT_FALSE(DoCreateAnswer(&answer, nullptr));
-
- // Createing answer succeeds when the remote description is set.
- CreateOfferAsRemoteDescription();
- EXPECT_TRUE(DoCreateAnswer(&answer, nullptr));
-}
-
-// Test that an error is returned if a description is applied that doesn't
-// respect the order of existing media sections.
-TEST_F(PeerConnectionInterfaceTest,
- MediaSectionOrderEnforcedForSubsequentOffers) {
- CreatePeerConnection();
- FakeConstraints constraints;
- constraints.SetMandatoryReceiveAudio(true);
- constraints.SetMandatoryReceiveVideo(true);
- std::unique_ptr<SessionDescriptionInterface> offer;
- ASSERT_TRUE(DoCreateOffer(&offer, &constraints));
- EXPECT_TRUE(DoSetRemoteDescription(std::move(offer)));
-
- std::unique_ptr<SessionDescriptionInterface> answer;
- ASSERT_TRUE(DoCreateAnswer(&answer, nullptr));
- EXPECT_TRUE(DoSetLocalDescription(std::move(answer)));
-
- // A remote offer with different m=line order should be rejected.
- ASSERT_TRUE(DoCreateOffer(&offer, &constraints));
- std::reverse(offer->description()->contents().begin(),
- offer->description()->contents().end());
- std::reverse(offer->description()->transport_infos().begin(),
- offer->description()->transport_infos().end());
- EXPECT_FALSE(DoSetRemoteDescription(std::move(offer)));
-
- // A subsequent local offer with different m=line order should be rejected.
- ASSERT_TRUE(DoCreateOffer(&offer, &constraints));
- std::reverse(offer->description()->contents().begin(),
- offer->description()->contents().end());
- std::reverse(offer->description()->transport_infos().begin(),
- offer->description()->transport_infos().end());
- EXPECT_FALSE(DoSetLocalDescription(std::move(offer)));
-}
-
class PeerConnectionMediaConfigTest : public testing::Test {
protected:
void SetUp() override {
diff --git a/pc/peerconnectionwrapper.cc b/pc/peerconnectionwrapper.cc
index dd11460..9be9309 100644
--- a/pc/peerconnectionwrapper.cc
+++ b/pc/peerconnectionwrapper.cc
@@ -30,7 +30,7 @@
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory,
rtc::scoped_refptr<PeerConnectionInterface> pc,
std::unique_ptr<MockPeerConnectionObserver> observer)
- : pc_factory_(pc_factory), pc_(pc), observer_(std::move(observer)) {
+ : pc_factory_(pc_factory), observer_(std::move(observer)), pc_(pc) {
RTC_DCHECK(pc_factory_);
RTC_DCHECK(pc_);
RTC_DCHECK(observer_);
@@ -57,15 +57,25 @@
}
std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateOffer(
- const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
- return CreateSdp([this, options](CreateSessionDescriptionObserver* observer) {
- pc()->CreateOffer(observer, options);
- });
+ const PeerConnectionInterface::RTCOfferAnswerOptions& options,
+ std::string* error_out) {
+ return CreateSdp(
+ [this, options](CreateSessionDescriptionObserver* observer) {
+ pc()->CreateOffer(observer, options);
+ },
+ error_out);
}
std::unique_ptr<SessionDescriptionInterface>
PeerConnectionWrapper::CreateOfferAndSetAsLocal() {
- auto offer = CreateOffer();
+ return CreateOfferAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+}
+
+std::unique_ptr<SessionDescriptionInterface>
+PeerConnectionWrapper::CreateOfferAndSetAsLocal(
+ const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
+ auto offer = CreateOffer(options);
if (!offer) {
return nullptr;
}
@@ -80,15 +90,25 @@
std::unique_ptr<SessionDescriptionInterface>
PeerConnectionWrapper::CreateAnswer(
- const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
- return CreateSdp([this, options](CreateSessionDescriptionObserver* observer) {
- pc()->CreateAnswer(observer, options);
- });
+ const PeerConnectionInterface::RTCOfferAnswerOptions& options,
+ std::string* error_out) {
+ return CreateSdp(
+ [this, options](CreateSessionDescriptionObserver* observer) {
+ pc()->CreateAnswer(observer, options);
+ },
+ error_out);
}
std::unique_ptr<SessionDescriptionInterface>
PeerConnectionWrapper::CreateAnswerAndSetAsLocal() {
- auto answer = CreateAnswer();
+ return CreateAnswerAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+}
+
+std::unique_ptr<SessionDescriptionInterface>
+PeerConnectionWrapper::CreateAnswerAndSetAsLocal(
+ const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
+ auto answer = CreateAnswer(options);
if (!answer) {
return nullptr;
}
@@ -97,73 +117,72 @@
}
std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateSdp(
- std::function<void(CreateSessionDescriptionObserver*)> fn) {
+ std::function<void(CreateSessionDescriptionObserver*)> fn,
+ std::string* error_out) {
rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer(
new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>());
fn(observer);
EXPECT_EQ_WAIT(true, observer->called(), kWaitTimeout);
+ if (error_out && !observer->result()) {
+ *error_out = observer->error();
+ }
return observer->MoveDescription();
}
bool PeerConnectionWrapper::SetLocalDescription(
- std::unique_ptr<SessionDescriptionInterface> desc) {
- return SetSdp([this, &desc](SetSessionDescriptionObserver* observer) {
- pc()->SetLocalDescription(observer, desc.release());
- });
+ std::unique_ptr<SessionDescriptionInterface> desc,
+ std::string* error_out) {
+ return SetSdp(
+ [this, &desc](SetSessionDescriptionObserver* observer) {
+ pc()->SetLocalDescription(observer, desc.release());
+ },
+ error_out);
}
bool PeerConnectionWrapper::SetRemoteDescription(
- std::unique_ptr<SessionDescriptionInterface> desc) {
- return SetSdp([this, &desc](SetSessionDescriptionObserver* observer) {
- pc()->SetRemoteDescription(observer, desc.release());
- });
+ std::unique_ptr<SessionDescriptionInterface> desc,
+ std::string* error_out) {
+ return SetSdp(
+ [this, &desc](SetSessionDescriptionObserver* observer) {
+ pc()->SetRemoteDescription(observer, desc.release());
+ },
+ error_out);
}
bool PeerConnectionWrapper::SetSdp(
- std::function<void(SetSessionDescriptionObserver*)> fn) {
+ std::function<void(SetSessionDescriptionObserver*)> fn,
+ std::string* error_out) {
rtc::scoped_refptr<MockSetSessionDescriptionObserver> observer(
new rtc::RefCountedObject<MockSetSessionDescriptionObserver>());
fn(observer);
- if (pc()->signaling_state() != PeerConnectionInterface::kClosed) {
- EXPECT_EQ_WAIT(true, observer->called(), kWaitTimeout);
+ EXPECT_EQ_WAIT(true, observer->called(), kWaitTimeout);
+ if (error_out && !observer->result()) {
+ *error_out = observer->error();
}
return observer->result();
}
-void PeerConnectionWrapper::AddAudioStream(const std::string& stream_label,
- const std::string& track_label) {
- auto stream = pc_factory()->CreateLocalMediaStream(stream_label);
- auto audio_track = pc_factory()->CreateAudioTrack(track_label, nullptr);
- EXPECT_TRUE(pc()->AddTrack(audio_track, {stream}));
- EXPECT_TRUE_WAIT(observer()->renegotiation_needed_, kWaitTimeout);
- observer()->renegotiation_needed_ = false;
+rtc::scoped_refptr<RtpSenderInterface> PeerConnectionWrapper::AddAudioTrack(
+ const std::string& track_label,
+ std::vector<MediaStreamInterface*> streams) {
+ auto media_stream_track =
+ pc_factory()->CreateAudioTrack(track_label, nullptr);
+ return pc()->AddTrack(media_stream_track, streams);
}
-void PeerConnectionWrapper::AddVideoStream(const std::string& stream_label,
- const std::string& track_label) {
- auto stream = pc_factory()->CreateLocalMediaStream(stream_label);
+rtc::scoped_refptr<RtpSenderInterface> PeerConnectionWrapper::AddVideoTrack(
+ const std::string& track_label,
+ std::vector<MediaStreamInterface*> streams) {
auto video_source = pc_factory()->CreateVideoSource(
rtc::MakeUnique<cricket::FakeVideoCapturer>());
- auto video_track = pc_factory()->CreateVideoTrack(track_label, video_source);
- EXPECT_TRUE(pc()->AddTrack(video_track, {stream}));
- EXPECT_TRUE_WAIT(observer()->renegotiation_needed_, kWaitTimeout);
- observer()->renegotiation_needed_ = false;
+ auto media_stream_track =
+ pc_factory()->CreateVideoTrack(track_label, video_source);
+ return pc()->AddTrack(media_stream_track, streams);
}
-void PeerConnectionWrapper::AddAudioVideoStream(
- const std::string& stream_label,
- const std::string& audio_track_label,
- const std::string& video_track_label) {
- auto stream = pc_factory()->CreateLocalMediaStream(stream_label);
- auto audio_track = pc_factory()->CreateAudioTrack(audio_track_label, nullptr);
- EXPECT_TRUE(pc()->AddTrack(audio_track, {stream}));
- auto video_source = pc_factory()->CreateVideoSource(
- rtc::MakeUnique<cricket::FakeVideoCapturer>());
- auto video_track =
- pc_factory()->CreateVideoTrack(video_track_label, video_source);
- EXPECT_TRUE(pc()->AddTrack(video_track, {stream}));
- EXPECT_TRUE_WAIT(observer()->renegotiation_needed_, kWaitTimeout);
- observer()->renegotiation_needed_ = false;
+PeerConnectionInterface::SignalingState
+PeerConnectionWrapper::signaling_state() {
+ return pc()->signaling_state();
}
bool PeerConnectionWrapper::IsIceGatheringDone() {
diff --git a/pc/peerconnectionwrapper.h b/pc/peerconnectionwrapper.h
index 783ae38..f74fcdb 100644
--- a/pc/peerconnectionwrapper.h
+++ b/pc/peerconnectionwrapper.h
@@ -54,54 +54,69 @@
// resulting SessionDescription once it is available. If the method call
// failed, null is returned.
std::unique_ptr<SessionDescriptionInterface> CreateOffer(
- const PeerConnectionInterface::RTCOfferAnswerOptions& options);
+ const PeerConnectionInterface::RTCOfferAnswerOptions& options,
+ std::string* error_out = nullptr);
// Calls CreateOffer with default options.
std::unique_ptr<SessionDescriptionInterface> CreateOffer();
// Calls CreateOffer and sets a copy of the offer as the local description.
+ std::unique_ptr<SessionDescriptionInterface> CreateOfferAndSetAsLocal(
+ const PeerConnectionInterface::RTCOfferAnswerOptions& options);
+ // Calls CreateOfferAndSetAsLocal with default options.
std::unique_ptr<SessionDescriptionInterface> CreateOfferAndSetAsLocal();
// Calls the underlying PeerConnection's CreateAnswer method and returns the
// resulting SessionDescription once it is available. If the method call
// failed, null is returned.
std::unique_ptr<SessionDescriptionInterface> CreateAnswer(
- const PeerConnectionInterface::RTCOfferAnswerOptions& options);
+ const PeerConnectionInterface::RTCOfferAnswerOptions& options,
+ std::string* error_out = nullptr);
// Calls CreateAnswer with the default options.
std::unique_ptr<SessionDescriptionInterface> CreateAnswer();
// Calls CreateAnswer and sets a copy of the offer as the local description.
+ std::unique_ptr<SessionDescriptionInterface> CreateAnswerAndSetAsLocal(
+ const PeerConnectionInterface::RTCOfferAnswerOptions& options);
+ // Calls CreateAnswerAndSetAsLocal with default options.
std::unique_ptr<SessionDescriptionInterface> CreateAnswerAndSetAsLocal();
// Calls the underlying PeerConnection's SetLocalDescription method with the
// given session description and waits for the success/failure response.
// Returns true if the description was successfully set.
- bool SetLocalDescription(std::unique_ptr<SessionDescriptionInterface> desc);
+ bool SetLocalDescription(std::unique_ptr<SessionDescriptionInterface> desc,
+ std::string* error_out = nullptr);
// Calls the underlying PeerConnection's SetRemoteDescription method with the
// given session description and waits for the success/failure response.
// Returns true if the description was successfully set.
- bool SetRemoteDescription(std::unique_ptr<SessionDescriptionInterface> desc);
+ bool SetRemoteDescription(std::unique_ptr<SessionDescriptionInterface> desc,
+ std::string* error_out = nullptr);
- // Adds a new stream with one audio track to the underlying PeerConnection.
- void AddAudioStream(const std::string& stream_label,
- const std::string& track_label);
- // Adds a new stream with one video track to the underlying PeerConnection.
- void AddVideoStream(const std::string& stream_label,
- const std::string& track_label);
- // Adds a new stream with one audio and one video track to the underlying
- // PeerConnection.
- void AddAudioVideoStream(const std::string& stream_label,
- const std::string& audio_track_label,
- const std::string& video_track_label);
+ // Calls the underlying PeerConnection's AddTrack method with an audio media
+ // stream track not bound to any source.
+ rtc::scoped_refptr<RtpSenderInterface> AddAudioTrack(
+ const std::string& track_label,
+ std::vector<MediaStreamInterface*> streams = {});
+
+ // Calls the underlying PeerConnection's AddTrack method with a video media
+ // stream track fed by a fake video capturer.
+ rtc::scoped_refptr<RtpSenderInterface> AddVideoTrack(
+ const std::string& track_label,
+ std::vector<MediaStreamInterface*> streams = {});
+
+ // Returns the signaling state of the underlying PeerConnection.
+ PeerConnectionInterface::SignalingState signaling_state();
// Returns true if ICE has finished gathering candidates.
bool IsIceGatheringDone();
private:
std::unique_ptr<SessionDescriptionInterface> CreateSdp(
- std::function<void(CreateSessionDescriptionObserver*)> fn);
- bool SetSdp(std::function<void(SetSessionDescriptionObserver*)> fn);
+ std::function<void(CreateSessionDescriptionObserver*)> fn,
+ std::string* error_out);
+ bool SetSdp(std::function<void(SetSessionDescriptionObserver*)> fn,
+ std::string* error_out);
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
- rtc::scoped_refptr<PeerConnectionInterface> pc_;
std::unique_ptr<MockPeerConnectionObserver> observer_;
+ rtc::scoped_refptr<PeerConnectionInterface> pc_;
};
} // namespace webrtc
diff --git a/pc/sdputils.cc b/pc/sdputils.cc
index 9339fdb..8932bea 100644
--- a/pc/sdputils.cc
+++ b/pc/sdputils.cc
@@ -20,7 +20,14 @@
std::unique_ptr<SessionDescriptionInterface> CloneSessionDescription(
const SessionDescriptionInterface* sdesc) {
RTC_DCHECK(sdesc);
- auto clone = rtc::MakeUnique<JsepSessionDescription>(sdesc->type());
+ return CloneSessionDescriptionAsType(sdesc, sdesc->type());
+}
+
+std::unique_ptr<SessionDescriptionInterface> CloneSessionDescriptionAsType(
+ const SessionDescriptionInterface* sdesc,
+ const std::string& type) {
+ RTC_DCHECK(sdesc);
+ auto clone = rtc::MakeUnique<JsepSessionDescription>(type);
clone->Initialize(sdesc->description()->Copy(), sdesc->session_id(),
sdesc->session_version());
// As of writing, our version of GCC does not allow returning a unique_ptr of
diff --git a/pc/sdputils.h b/pc/sdputils.h
index 7d67fd8..3a53a41 100644
--- a/pc/sdputils.h
+++ b/pc/sdputils.h
@@ -23,6 +23,11 @@
std::unique_ptr<SessionDescriptionInterface> CloneSessionDescription(
const SessionDescriptionInterface* sdesc);
+// Returns a copy of the given session description with the type changed.
+std::unique_ptr<SessionDescriptionInterface> CloneSessionDescriptionAsType(
+ const SessionDescriptionInterface* sdesc,
+ const std::string& type);
+
// Function that takes a single session description content with its
// corresponding transport and produces a boolean.
typedef std::function<bool(const cricket::ContentInfo*,
diff --git a/pc/test/mockpeerconnectionobservers.h b/pc/test/mockpeerconnectionobservers.h
index 9b077f5..82098ca 100644
--- a/pc/test/mockpeerconnectionobservers.h
+++ b/pc/test/mockpeerconnectionobservers.h
@@ -177,26 +177,27 @@
public:
MockCreateSessionDescriptionObserver()
: called_(false),
- result_(false) {}
+ error_("MockCreateSessionDescriptionObserver not called") {}
virtual ~MockCreateSessionDescriptionObserver() {}
virtual void OnSuccess(SessionDescriptionInterface* desc) {
called_ = true;
- result_ = true;
+ error_ = "";
desc_.reset(desc);
}
virtual void OnFailure(const std::string& error) {
called_ = true;
- result_ = false;
+ error_ = error;
}
bool called() const { return called_; }
- bool result() const { return result_; }
+ bool result() const { return error_.empty(); }
+ const std::string& error() const { return error_; }
std::unique_ptr<SessionDescriptionInterface> MoveDescription() {
return std::move(desc_);
}
private:
bool called_;
- bool result_;
+ std::string error_;
std::unique_ptr<SessionDescriptionInterface> desc_;
};
@@ -205,22 +206,23 @@
public:
MockSetSessionDescriptionObserver()
: called_(false),
- result_(false) {}
+ error_("MockSetSessionDescriptionObserver not called") {}
virtual ~MockSetSessionDescriptionObserver() {}
virtual void OnSuccess() {
called_ = true;
- result_ = true;
+ error_ = "";
}
virtual void OnFailure(const std::string& error) {
called_ = true;
- result_ = false;
+ error_ = error;
}
bool called() const { return called_; }
- bool result() const { return result_; }
+ bool result() const { return error_.empty(); }
+ const std::string& error() const { return error_; }
private:
bool called_;
- bool result_;
+ std::string error_;
};
class MockDataChannelObserver : public webrtc::DataChannelObserver {
diff --git a/pc/webrtcsession.cc b/pc/webrtcsession.cc
index f556204..314ba52 100644
--- a/pc/webrtcsession.cc
+++ b/pc/webrtcsession.cc
@@ -707,15 +707,13 @@
webrtc_session_desc_factory_->CreateAnswer(observer, session_options);
}
-bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
- std::string* err_desc) {
+bool WebRtcSession::SetLocalDescription(
+ std::unique_ptr<SessionDescriptionInterface> desc,
+ std::string* err_desc) {
RTC_DCHECK(signaling_thread()->IsCurrent());
- // Takes the ownership of |desc| regardless of the result.
- std::unique_ptr<SessionDescriptionInterface> desc_temp(desc);
-
// Validate SDP.
- if (!ValidateSessionDescription(desc, cricket::CS_LOCAL, err_desc)) {
+ if (!ValidateSessionDescription(desc.get(), cricket::CS_LOCAL, err_desc)) {
return false;
}
@@ -727,18 +725,19 @@
}
if (action == kAnswer) {
- current_local_description_.reset(desc_temp.release());
- pending_local_description_.reset(nullptr);
- current_remote_description_.reset(pending_remote_description_.release());
+ current_local_description_ = std::move(desc);
+ pending_local_description_ = nullptr;
+ current_remote_description_ = std::move(pending_remote_description_);
} else {
- pending_local_description_.reset(desc_temp.release());
+ pending_local_description_ = std::move(desc);
}
// Transport and Media channels will be created only when offer is set.
if (action == kOffer && !CreateChannels(local_description()->description())) {
// TODO(mallinath) - Handle CreateChannel failure, as new local description
// is applied. Restore back to old description.
- return BadLocalSdp(desc->type(), kCreateChannelFailed, err_desc);
+ return BadLocalSdp(local_description()->type(), kCreateChannelFailed,
+ err_desc);
}
// Remove unused channels if MediaContentDescription is rejected.
@@ -754,50 +753,54 @@
pending_ice_restarts_.clear();
if (error() != ERROR_NONE) {
- return BadLocalSdp(desc->type(), GetSessionErrorMsg(), err_desc);
+ return BadLocalSdp(local_description()->type(), GetSessionErrorMsg(),
+ err_desc);
}
return true;
}
-bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
- std::string* err_desc) {
+bool WebRtcSession::SetRemoteDescription(
+ std::unique_ptr<SessionDescriptionInterface> desc,
+ std::string* err_desc) {
RTC_DCHECK(signaling_thread()->IsCurrent());
- // Takes the ownership of |desc| regardless of the result.
- std::unique_ptr<SessionDescriptionInterface> desc_temp(desc);
-
// Validate SDP.
- if (!ValidateSessionDescription(desc, cricket::CS_REMOTE, err_desc)) {
+ if (!ValidateSessionDescription(desc.get(), cricket::CS_REMOTE, err_desc)) {
return false;
}
+ // Hold this pointer so candidates can be copied to it later in the method.
+ SessionDescriptionInterface* desc_ptr = desc.get();
+
const SessionDescriptionInterface* old_remote_description =
remote_description();
// Grab ownership of the description being replaced for the remainder of this
- // method, since it's used below.
+ // method, since it's used below as |old_remote_description|.
std::unique_ptr<SessionDescriptionInterface> replaced_remote_description;
Action action = GetAction(desc->type());
if (action == kAnswer) {
- replaced_remote_description.reset(
- pending_remote_description_ ? pending_remote_description_.release()
- : current_remote_description_.release());
- current_remote_description_.reset(desc_temp.release());
- pending_remote_description_.reset(nullptr);
- current_local_description_.reset(pending_local_description_.release());
+ replaced_remote_description = pending_remote_description_
+ ? std::move(pending_remote_description_)
+ : std::move(current_remote_description_);
+ current_remote_description_ = std::move(desc);
+ pending_remote_description_ = nullptr;
+ current_local_description_ = std::move(pending_local_description_);
} else {
- replaced_remote_description.reset(pending_remote_description_.release());
- pending_remote_description_.reset(desc_temp.release());
+ replaced_remote_description = std::move(pending_remote_description_);
+ pending_remote_description_ = std::move(desc);
}
// Transport and Media channels will be created only when offer is set.
- if (action == kOffer && !CreateChannels(desc->description())) {
+ if (action == kOffer &&
+ !CreateChannels(remote_description()->description())) {
// TODO(mallinath) - Handle CreateChannel failure, as new local description
// is applied. Restore back to old description.
- return BadRemoteSdp(desc->type(), kCreateChannelFailed, err_desc);
+ return BadRemoteSdp(remote_description()->type(), kCreateChannelFailed,
+ err_desc);
}
// Remove unused channels if MediaContentDescription is rejected.
- RemoveUnusedChannels(desc->description());
+ RemoveUnusedChannels(remote_description()->description());
// NOTE: Candidates allocation will be initiated only when SetLocalDescription
// is called.
@@ -805,8 +808,10 @@
return false;
}
- if (local_description() && !UseCandidatesInSessionDescription(desc)) {
- return BadRemoteSdp(desc->type(), kInvalidCandidates, err_desc);
+ if (local_description() &&
+ !UseCandidatesInSessionDescription(remote_description())) {
+ return BadRemoteSdp(remote_description()->type(), kInvalidCandidates,
+ err_desc);
}
if (old_remote_description) {
@@ -817,7 +822,7 @@
// TODO(deadbeef): When we start storing both the current and pending
// remote description, this should reset pending_ice_restarts and compare
// against the current description.
- if (CheckForRemoteIceRestart(old_remote_description, desc,
+ if (CheckForRemoteIceRestart(old_remote_description, remote_description(),
content.name)) {
if (action == kOffer) {
pending_ice_restarts_.insert(content.name);
@@ -831,13 +836,14 @@
// description plus any candidates added since then. We should remove
// this once we're sure it won't break anything.
WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
- old_remote_description, content.name, desc);
+ old_remote_description, content.name, desc_ptr);
}
}
}
if (error() != ERROR_NONE) {
- return BadRemoteSdp(desc->type(), GetSessionErrorMsg(), err_desc);
+ return BadRemoteSdp(remote_description()->type(), GetSessionErrorMsg(),
+ err_desc);
}
// Set the the ICE connection state to connecting since the connection may
@@ -848,7 +854,7 @@
// transport and expose a new checking() member from transport that can be
// read to determine the current checking state. The existing SignalConnecting
// actually means "gathering candidates", so cannot be be used here.
- if (desc->type() != SessionDescriptionInterface::kOffer &&
+ if (remote_description()->type() != SessionDescriptionInterface::kOffer &&
ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew) {
SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
}
diff --git a/pc/webrtcsession.h b/pc/webrtcsession.h
index 16c3931..185fa05 100644
--- a/pc/webrtcsession.h
+++ b/pc/webrtcsession.h
@@ -254,11 +254,9 @@
const cricket::MediaSessionOptions& session_options);
void CreateAnswer(CreateSessionDescriptionObserver* observer,
const cricket::MediaSessionOptions& session_options);
- // The ownership of |desc| will be transferred after this call.
- bool SetLocalDescription(SessionDescriptionInterface* desc,
+ bool SetLocalDescription(std::unique_ptr<SessionDescriptionInterface> desc,
std::string* err_desc);
- // The ownership of |desc| will be transferred after this call.
- bool SetRemoteDescription(SessionDescriptionInterface* desc,
+ bool SetRemoteDescription(std::unique_ptr<SessionDescriptionInterface> desc,
std::string* err_desc);
bool ProcessIceMessage(const IceCandidateInterface* ice_candidate);
diff --git a/pc/webrtcsession_unittest.cc b/pc/webrtcsession_unittest.cc
index 25cb35a..0c54abc 100644
--- a/pc/webrtcsession_unittest.cc
+++ b/pc/webrtcsession_unittest.cc
@@ -92,26 +92,6 @@
static const int kDefaultTimeout = 10000; // 10 seconds.
static const int kIceCandidatesTimeout = 10000;
-static const char kSdpWithRtx[] =
- "v=0\r\n"
- "o=- 4104004319237231850 2 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "t=0 0\r\n"
- "a=msid-semantic: WMS stream1\r\n"
- "m=video 9 RTP/SAVPF 0 96\r\n"
- "c=IN IP4 0.0.0.0\r\n"
- "a=rtcp:9 IN IP4 0.0.0.0\r\n"
- "a=ice-ufrag:CerjGp19G7wpXwl7\r\n"
- "a=ice-pwd:cMvOlFvQ6ochez1ZOoC2uBEC\r\n"
- "a=mid:video\r\n"
- "a=sendrecv\r\n"
- "a=rtcp-mux\r\n"
- "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
- "inline:5/4N5CDvMiyDArHtBByUM71VIkguH17ZNoX60GrA\r\n"
- "a=rtpmap:0 fake_video_codec/90000\r\n"
- "a=rtpmap:96 rtx/90000\r\n"
- "a=fmtp:96 apt=0\r\n";
-
static const char kStream1[] = "stream1";
static const char kVideoTrack1[] = "video1";
static const char kAudioTrack1[] = "audio1";
@@ -120,7 +100,6 @@
static const char kVideoTrack2[] = "video2";
static const char kAudioTrack2[] = "audio2";
-static constexpr bool kStopped = true;
static constexpr bool kActive = false;
enum RTCCertificateGenerationMethod { ALREADY_GENERATED, DTLS_IDENTITY_STORE };
@@ -465,24 +444,6 @@
remote_send_video_ = true;
}
- void SendAudioVideoStream1And2() {
- send_stream_1_ = true;
- send_stream_2_ = true;
- local_send_audio_ = true;
- local_send_video_ = true;
- remote_send_audio_ = true;
- remote_send_video_ = true;
- }
-
- void SendNothing() {
- send_stream_1_ = false;
- send_stream_2_ = false;
- local_send_audio_ = false;
- local_send_video_ = false;
- remote_send_audio_ = false;
- remote_send_video_ = false;
- }
-
void SendAudioOnlyStream2() {
send_stream_1_ = false;
send_stream_2_ = true;
@@ -501,19 +462,6 @@
remote_send_video_ = true;
}
- // Helper function used to add a specific media section to the
- // |session_options|.
- void AddMediaSection(cricket::MediaType type,
- const std::string& mid,
- cricket::MediaContentDirection direction,
- bool stopped,
- cricket::MediaSessionOptions* opts) {
- opts->media_description_options.push_back(cricket::MediaDescriptionOptions(
- type, mid,
- cricket::RtpTransceiverDirection::FromMediaContentDirection(direction),
- stopped));
- }
-
// Add the media sections to the options from |offered_media_sections_| when
// creating an answer or a new offer.
// This duplicates a lot of logic from PeerConnection but this can be fixed
@@ -688,13 +636,6 @@
session_options->crypto_options = crypto_options_;
}
- void GetOptionsForAudioOnlyRemoteOffer(
- cricket::MediaSessionOptions* session_options) {
- remote_recv_audio_ = true;
- remote_recv_video_ = false;
- GetOptionsForRemoteOffer(session_options);
- }
-
void GetOptionsForRemoteOffer(cricket::MediaSessionOptions* session_options) {
AddMediaSectionsAndSendersToOptions(session_options, remote_send_audio_,
remote_recv_audio_, remote_send_video_,
@@ -811,30 +752,15 @@
transport_desc->ice_pwd = pwd;
}
- // Creates a remote offer and and applies it as a remote description,
- // creates a local answer and applies is as a local description.
- // Call SendAudioVideoStreamX() before this function
- // to decide which local and remote streams to create.
- void CreateAndSetRemoteOfferAndLocalAnswer() {
- SessionDescriptionInterface* offer = CreateRemoteOffer();
- SetRemoteDescriptionWithoutError(offer);
- SessionDescriptionInterface* answer = CreateAnswer();
- SetLocalDescriptionWithoutError(answer);
- }
void SetLocalDescriptionWithoutError(SessionDescriptionInterface* desc) {
- ASSERT_TRUE(session_->SetLocalDescription(desc, nullptr));
+ ASSERT_TRUE(session_->SetLocalDescription(rtc::WrapUnique(desc), nullptr));
session_->MaybeStartGathering();
}
- void SetLocalDescriptionExpectState(SessionDescriptionInterface* desc,
- WebRtcSession::State expected_state) {
- SetLocalDescriptionWithoutError(desc);
- EXPECT_EQ(expected_state, session_->state());
- }
void SetLocalDescriptionExpectError(const std::string& action,
const std::string& expected_error,
SessionDescriptionInterface* desc) {
std::string error;
- EXPECT_FALSE(session_->SetLocalDescription(desc, &error));
+ EXPECT_FALSE(session_->SetLocalDescription(rtc::WrapUnique(desc), &error));
std::string sdp_type = "local ";
sdp_type.append(action);
EXPECT_NE(std::string::npos, error.find(sdp_type));
@@ -845,24 +771,14 @@
SetLocalDescriptionExpectError(SessionDescriptionInterface::kOffer,
expected_error, desc);
}
- void SetLocalDescriptionAnswerExpectError(const std::string& expected_error,
- SessionDescriptionInterface* desc) {
- SetLocalDescriptionExpectError(SessionDescriptionInterface::kAnswer,
- expected_error, desc);
- }
void SetRemoteDescriptionWithoutError(SessionDescriptionInterface* desc) {
- ASSERT_TRUE(session_->SetRemoteDescription(desc, nullptr));
- }
- void SetRemoteDescriptionExpectState(SessionDescriptionInterface* desc,
- WebRtcSession::State expected_state) {
- SetRemoteDescriptionWithoutError(desc);
- EXPECT_EQ(expected_state, session_->state());
+ ASSERT_TRUE(session_->SetRemoteDescription(rtc::WrapUnique(desc), nullptr));
}
void SetRemoteDescriptionExpectError(const std::string& action,
const std::string& expected_error,
SessionDescriptionInterface* desc) {
std::string error;
- EXPECT_FALSE(session_->SetRemoteDescription(desc, &error));
+ EXPECT_FALSE(session_->SetRemoteDescription(rtc::WrapUnique(desc), &error));
std::string sdp_type = "remote ";
sdp_type.append(action);
EXPECT_NE(std::string::npos, error.find(sdp_type));
@@ -873,11 +789,6 @@
SetRemoteDescriptionExpectError(SessionDescriptionInterface::kOffer,
expected_error, desc);
}
- void SetRemoteDescriptionAnswerExpectError(
- const std::string& expected_error, SessionDescriptionInterface* desc) {
- SetRemoteDescriptionExpectError(SessionDescriptionInterface::kAnswer,
- expected_error, desc);
- }
JsepSessionDescription* CreateRemoteOfferWithVersion(
cricket::MediaSessionOptions options,
@@ -1035,23 +946,6 @@
}
}
- bool ContainsVideoCodecWithName(const SessionDescriptionInterface* desc,
- const std::string& codec_name) {
- for (const auto& content : desc->description()->contents()) {
- if (static_cast<cricket::MediaContentDescription*>(content.description)
- ->type() == cricket::MEDIA_TYPE_VIDEO) {
- const auto* mdesc =
- static_cast<cricket::VideoContentDescription*>(content.description);
- for (const auto& codec : mdesc->codecs()) {
- if (codec.name == codec_name) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
// The method sets up a call from the session to itself, in a loopback
// arrangement. It also uses a firewall rule to create a temporary
// disconnection, and then a permanent disconnection.
@@ -1115,33 +1009,6 @@
EXPECT_GT(fake_call_.last_sent_packet().send_time_ms, -1);
}
- // Adds CN codecs to FakeMediaEngine and MediaDescriptionFactory.
- void AddCNCodecs() {
- const cricket::AudioCodec kCNCodec1(102, "CN", 8000, 0, 1);
- const cricket::AudioCodec kCNCodec2(103, "CN", 16000, 0, 1);
-
- // Add kCNCodec for dtmf test.
- std::vector<cricket::AudioCodec> codecs =
- media_engine_->audio_send_codecs();
- codecs.push_back(kCNCodec1);
- codecs.push_back(kCNCodec2);
- media_engine_->SetAudioCodecs(codecs);
- desc_factory_->set_audio_codecs(codecs, codecs);
- }
-
- bool VerifyNoCNCodecs(const cricket::ContentInfo* content) {
- const cricket::ContentDescription* description = content->description;
- RTC_CHECK(description != NULL);
- const cricket::AudioContentDescription* audio_content_desc =
- static_cast<const cricket::AudioContentDescription*>(description);
- RTC_CHECK(audio_content_desc != NULL);
- for (size_t i = 0; i < audio_content_desc->codecs().size(); ++i) {
- if (audio_content_desc->codecs()[i].name == "CN")
- return false;
- }
- return true;
- }
-
void CreateDataChannel() {
webrtc::InternalDataChannelInit dci;
RTC_CHECK(session_.get());
@@ -1214,141 +1081,6 @@
TestSessionCandidatesWithBundleRtcpMux(true, true);
}
-TEST_F(WebRtcSessionTest, SetSdpFailedOnInvalidSdp) {
- Init();
- SessionDescriptionInterface* offer = NULL;
- // Since |offer| is NULL, there's no way to tell if it's an offer or answer.
- std::string unknown_action;
- SetLocalDescriptionExpectError(unknown_action, kInvalidSdp, offer);
- SetRemoteDescriptionExpectError(unknown_action, kInvalidSdp, offer);
-}
-
-// Test creating offers and receive answers and make sure the
-// media engine creates the expected send and receive streams.
-TEST_F(WebRtcSessionTest, TestCreateSdesOfferReceiveSdesAnswer) {
- Init();
- SendAudioVideoStream1();
- SessionDescriptionInterface* offer = CreateOffer();
- const std::string session_id_orig = offer->session_id();
- const std::string session_version_orig = offer->session_version();
- SetLocalDescriptionWithoutError(offer);
-
- SendAudioVideoStream2();
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(session_->local_description());
- SetRemoteDescriptionWithoutError(answer);
-
- video_channel_ = media_engine_->GetVideoChannel(0);
- voice_channel_ = media_engine_->GetVoiceChannel(0);
-
- ASSERT_EQ(1u, video_channel_->recv_streams().size());
- EXPECT_TRUE(kVideoTrack2 == video_channel_->recv_streams()[0].id);
-
- ASSERT_EQ(1u, voice_channel_->recv_streams().size());
- EXPECT_TRUE(kAudioTrack2 == voice_channel_->recv_streams()[0].id);
-
- ASSERT_EQ(1u, video_channel_->send_streams().size());
- EXPECT_TRUE(kVideoTrack1 == video_channel_->send_streams()[0].id);
- ASSERT_EQ(1u, voice_channel_->send_streams().size());
- EXPECT_TRUE(kAudioTrack1 == voice_channel_->send_streams()[0].id);
-
- // Create new offer without send streams.
- SendNothing();
- offer = CreateOffer();
-
- // Verify the session id is the same and the session version is
- // increased.
- EXPECT_EQ(session_id_orig, offer->session_id());
- EXPECT_LT(rtc::FromString<uint64_t>(session_version_orig),
- rtc::FromString<uint64_t>(offer->session_version()));
-
- SetLocalDescriptionWithoutError(offer);
- EXPECT_EQ(0u, video_channel_->send_streams().size());
- EXPECT_EQ(0u, voice_channel_->send_streams().size());
-
- SendAudioVideoStream2();
- answer = CreateRemoteAnswer(session_->local_description());
- SetRemoteDescriptionWithoutError(answer);
-
- // Make sure the receive streams have not changed.
- ASSERT_EQ(1u, video_channel_->recv_streams().size());
- EXPECT_TRUE(kVideoTrack2 == video_channel_->recv_streams()[0].id);
- ASSERT_EQ(1u, voice_channel_->recv_streams().size());
- EXPECT_TRUE(kAudioTrack2 == voice_channel_->recv_streams()[0].id);
-}
-
-// Test receiving offers and creating answers and make sure the
-// media engine creates the expected send and receive streams.
-TEST_F(WebRtcSessionTest, TestReceiveSdesOfferCreateSdesAnswer) {
- Init();
- SendAudioVideoStream2();
- SessionDescriptionInterface* offer = CreateOffer();
- SetRemoteDescriptionWithoutError(offer);
-
- SendAudioVideoStream1();
- SessionDescriptionInterface* answer = CreateAnswer();
- SetLocalDescriptionWithoutError(answer);
-
- const std::string session_id_orig = answer->session_id();
- const std::string session_version_orig = answer->session_version();
-
- video_channel_ = media_engine_->GetVideoChannel(0);
- voice_channel_ = media_engine_->GetVoiceChannel(0);
-
- ASSERT_TRUE(video_channel_);
- ASSERT_TRUE(voice_channel_);
- ASSERT_EQ(1u, video_channel_->recv_streams().size());
- EXPECT_TRUE(kVideoTrack2 == video_channel_->recv_streams()[0].id);
-
- ASSERT_EQ(1u, voice_channel_->recv_streams().size());
- EXPECT_TRUE(kAudioTrack2 == voice_channel_->recv_streams()[0].id);
-
- ASSERT_EQ(1u, video_channel_->send_streams().size());
- EXPECT_TRUE(kVideoTrack1 == video_channel_->send_streams()[0].id);
- ASSERT_EQ(1u, voice_channel_->send_streams().size());
- EXPECT_TRUE(kAudioTrack1 == voice_channel_->send_streams()[0].id);
-
- SendAudioVideoStream1And2();
- offer = CreateOffer();
- SetRemoteDescriptionWithoutError(offer);
-
- // Answer by turning off all send streams.
- SendNothing();
- answer = CreateAnswer();
-
- // Verify the session id is the same and the session version is
- // increased.
- EXPECT_EQ(session_id_orig, answer->session_id());
- EXPECT_LT(rtc::FromString<uint64_t>(session_version_orig),
- rtc::FromString<uint64_t>(answer->session_version()));
- SetLocalDescriptionWithoutError(answer);
-
- ASSERT_EQ(2u, video_channel_->recv_streams().size());
- EXPECT_TRUE(kVideoTrack1 == video_channel_->recv_streams()[0].id);
- EXPECT_TRUE(kVideoTrack2 == video_channel_->recv_streams()[1].id);
- ASSERT_EQ(2u, voice_channel_->recv_streams().size());
- EXPECT_TRUE(kAudioTrack1 == voice_channel_->recv_streams()[0].id);
- EXPECT_TRUE(kAudioTrack2 == voice_channel_->recv_streams()[1].id);
-
- // Make sure we have no send streams.
- EXPECT_EQ(0u, video_channel_->send_streams().size());
- EXPECT_EQ(0u, voice_channel_->send_streams().size());
-}
-
-TEST_F(WebRtcSessionTest, SetLocalSdpFailedOnCreateChannel) {
- Init();
- media_engine_->set_fail_create_channel(true);
-
- SessionDescriptionInterface* offer = CreateOffer();
- ASSERT_TRUE(offer != NULL);
- // SetRemoteDescription and SetLocalDescription will take the ownership of
- // the offer.
- SetRemoteDescriptionOfferExpectError(kCreateChannelFailed, offer);
- offer = CreateOffer();
- ASSERT_TRUE(offer != NULL);
- SetLocalDescriptionOfferExpectError(kCreateChannelFailed, offer);
-}
-
// Test that we can create and set an answer correctly when different
// SSL roles have been negotiated for different transports.
// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
@@ -1412,592 +1144,6 @@
SetLocalDescriptionWithoutError(answer);
}
-TEST_F(WebRtcSessionTest, TestSetLocalOfferTwice) {
- Init();
- SendNothing();
- // SetLocalDescription take ownership of offer.
- SessionDescriptionInterface* offer = CreateOffer();
- SetLocalDescriptionWithoutError(offer);
-
- // SetLocalDescription take ownership of offer.
- SessionDescriptionInterface* offer2 = CreateOffer();
- SetLocalDescriptionWithoutError(offer2);
-}
-
-TEST_F(WebRtcSessionTest, TestSetRemoteOfferTwice) {
- Init();
- SendNothing();
- // SetLocalDescription take ownership of offer.
- SessionDescriptionInterface* offer = CreateOffer();
- SetRemoteDescriptionWithoutError(offer);
-
- SessionDescriptionInterface* offer2 = CreateOffer();
- SetRemoteDescriptionWithoutError(offer2);
-}
-
-TEST_F(WebRtcSessionTest, TestSetLocalAndRemoteOffer) {
- Init();
- SendNothing();
- SessionDescriptionInterface* offer = CreateOffer();
- SetLocalDescriptionWithoutError(offer);
- offer = CreateOffer();
- SetRemoteDescriptionOfferExpectError("Called in wrong state: STATE_SENTOFFER",
- offer);
-}
-
-TEST_F(WebRtcSessionTest, TestSetRemoteAndLocalOffer) {
- Init();
- SendNothing();
- SessionDescriptionInterface* offer = CreateOffer();
- SetRemoteDescriptionWithoutError(offer);
- offer = CreateOffer();
- SetLocalDescriptionOfferExpectError(
- "Called in wrong state: STATE_RECEIVEDOFFER", offer);
-}
-
-TEST_F(WebRtcSessionTest, TestSetLocalPrAnswer) {
- Init();
- SendNothing();
- SessionDescriptionInterface* offer = CreateRemoteOffer();
- SetRemoteDescriptionExpectState(offer, WebRtcSession::STATE_RECEIVEDOFFER);
-
- JsepSessionDescription* pranswer =
- static_cast<JsepSessionDescription*>(CreateAnswer());
- pranswer->set_type(SessionDescriptionInterface::kPrAnswer);
- SetLocalDescriptionExpectState(pranswer, WebRtcSession::STATE_SENTPRANSWER);
-
- SendAudioVideoStream1();
- JsepSessionDescription* pranswer2 =
- static_cast<JsepSessionDescription*>(CreateAnswer());
- pranswer2->set_type(SessionDescriptionInterface::kPrAnswer);
-
- SetLocalDescriptionExpectState(pranswer2, WebRtcSession::STATE_SENTPRANSWER);
-
- SendAudioVideoStream2();
- SessionDescriptionInterface* answer = CreateAnswer();
- SetLocalDescriptionExpectState(answer, WebRtcSession::STATE_INPROGRESS);
-}
-
-TEST_F(WebRtcSessionTest, TestSetRemotePrAnswer) {
- Init();
- SendNothing();
- SessionDescriptionInterface* offer = CreateOffer();
- SetLocalDescriptionExpectState(offer, WebRtcSession::STATE_SENTOFFER);
-
- JsepSessionDescription* pranswer =
- CreateRemoteAnswer(session_->local_description());
- pranswer->set_type(SessionDescriptionInterface::kPrAnswer);
-
- SetRemoteDescriptionExpectState(pranswer,
- WebRtcSession::STATE_RECEIVEDPRANSWER);
-
- SendAudioVideoStream1();
- JsepSessionDescription* pranswer2 =
- CreateRemoteAnswer(session_->local_description());
- pranswer2->set_type(SessionDescriptionInterface::kPrAnswer);
-
- SetRemoteDescriptionExpectState(pranswer2,
- WebRtcSession::STATE_RECEIVEDPRANSWER);
-
- SendAudioVideoStream2();
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(session_->local_description());
- SetRemoteDescriptionExpectState(answer, WebRtcSession::STATE_INPROGRESS);
-}
-
-TEST_F(WebRtcSessionTest, TestSetLocalAnswerWithoutOffer) {
- Init();
- SendNothing();
- std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer());
-
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(offer.get());
- SetLocalDescriptionAnswerExpectError("Called in wrong state: STATE_INIT",
- answer);
-}
-
-TEST_F(WebRtcSessionTest, TestSetRemoteAnswerWithoutOffer) {
- Init();
- SendNothing();
- std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer());
-
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(offer.get());
- SetRemoteDescriptionAnswerExpectError(
- "Called in wrong state: STATE_INIT", answer);
-}
-
-// Verifies TransportProxy and media channels are created with content names
-// present in the SessionDescription.
-TEST_F(WebRtcSessionTest, TestChannelCreationsWithContentNames) {
- Init();
- SendAudioVideoStream1();
- std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer());
-
- // CreateOffer creates session description with the content names "audio" and
- // "video". Goal is to modify these content names and verify transport
- // channels
- // in the WebRtcSession, as channels are created with the content names
- // present in SDP.
- std::string sdp;
- EXPECT_TRUE(offer->ToString(&sdp));
-
- SessionDescriptionInterface* modified_offer =
- CreateSessionDescription(JsepSessionDescription::kOffer, sdp, NULL);
-
- SetRemoteDescriptionWithoutError(modified_offer);
-
- cricket::MediaSessionOptions answer_options;
- answer_options.bundle_enabled = false;
- SessionDescriptionInterface* answer = CreateAnswer(answer_options);
- SetLocalDescriptionWithoutError(answer);
-
- rtc::PacketTransportInternal* voice_transport_channel =
- session_->voice_rtp_transport_channel();
- EXPECT_TRUE(voice_transport_channel != NULL);
- EXPECT_EQ(voice_transport_channel->debug_name(),
- "audio " + std::to_string(cricket::ICE_CANDIDATE_COMPONENT_RTP));
- rtc::PacketTransportInternal* video_transport_channel =
- session_->video_rtp_transport_channel();
- ASSERT_TRUE(video_transport_channel != NULL);
- EXPECT_EQ(video_transport_channel->debug_name(),
- "video " + std::to_string(cricket::ICE_CANDIDATE_COMPONENT_RTP));
- EXPECT_TRUE((video_channel_ = media_engine_->GetVideoChannel(0)) != NULL);
- EXPECT_TRUE((voice_channel_ = media_engine_->GetVoiceChannel(0)) != NULL);
-}
-
-// Test that an offer contains the correct media content descriptions based on
-// the send streams when no constraints have been set.
-TEST_F(WebRtcSessionTest, CreateOfferWithoutConstraintsOrStreams) {
- Init();
- std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer());
-
- ASSERT_TRUE(offer != NULL);
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(offer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_EQ(
- cricket::MD_RECVONLY,
- static_cast<const cricket::AudioContentDescription*>(content->description)
- ->direction());
- content = cricket::GetFirstVideoContent(offer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_EQ(
- cricket::MD_RECVONLY,
- static_cast<const cricket::VideoContentDescription*>(content->description)
- ->direction());
-}
-
-// Test that an offer contains the correct media content descriptions based on
-// the send streams when no constraints have been set.
-TEST_F(WebRtcSessionTest, CreateOfferWithoutConstraints) {
- Init();
- // Test Audio only offer.
- SendAudioOnlyStream2();
- std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer());
-
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(offer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_EQ(
- cricket::MD_SENDRECV,
- static_cast<const cricket::AudioContentDescription*>(content->description)
- ->direction());
- content = cricket::GetFirstVideoContent(offer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_EQ(
- cricket::MD_RECVONLY,
- static_cast<const cricket::VideoContentDescription*>(content->description)
- ->direction());
-
- // Test Audio / Video offer.
- SendAudioVideoStream1();
- offer.reset(CreateOffer());
- content = cricket::GetFirstAudioContent(offer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_EQ(
- cricket::MD_SENDRECV,
- static_cast<const cricket::AudioContentDescription*>(content->description)
- ->direction());
-
- content = cricket::GetFirstVideoContent(offer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_EQ(
- cricket::MD_SENDRECV,
- static_cast<const cricket::VideoContentDescription*>(content->description)
- ->direction());
-}
-
-// Test that an offer contains no media content descriptions if
-// kOfferToReceiveVideo and kOfferToReceiveAudio constraints are set to false.
-TEST_F(WebRtcSessionTest, CreateOfferWithConstraintsWithoutStreams) {
- Init();
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.offer_to_receive_audio = 0;
- options.offer_to_receive_video = 0;
-
- std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer(options));
-
- ASSERT_TRUE(offer != NULL);
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(offer->description());
- EXPECT_TRUE(content == NULL);
- content = cricket::GetFirstVideoContent(offer->description());
- EXPECT_TRUE(content == NULL);
-}
-
-// Test that an offer contains only audio media content descriptions if
-// kOfferToReceiveAudio constraints are set to true.
-TEST_F(WebRtcSessionTest, CreateAudioOnlyOfferWithConstraints) {
- Init();
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.offer_to_receive_audio =
- RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
- options.offer_to_receive_video = 0;
-
- std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer(options));
-
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(offer->description());
- EXPECT_TRUE(content != NULL);
- content = cricket::GetFirstVideoContent(offer->description());
- EXPECT_TRUE(content == NULL);
-}
-
-// Test that an offer contains audio and video media content descriptions if
-// kOfferToReceiveAudio and kOfferToReceiveVideo constraints are set to true.
-TEST_F(WebRtcSessionTest, CreateOfferWithConstraints) {
- Init();
- // Test Audio / Video offer.
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.offer_to_receive_audio =
- RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
- options.offer_to_receive_video =
- RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
-
- std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer(options));
-
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(offer->description());
- EXPECT_TRUE(content != NULL);
-
- content = cricket::GetFirstVideoContent(offer->description());
- EXPECT_TRUE(content != NULL);
-
- // Sets constraints to false and verifies that audio/video contents are
- // removed.
- options.offer_to_receive_audio = 0;
- options.offer_to_receive_video = 0;
- // Remove the media sections added in previous offer.
- offered_media_sections_.clear();
- offer.reset(CreateOffer(options));
-
- content = cricket::GetFirstAudioContent(offer->description());
- EXPECT_TRUE(content == NULL);
- content = cricket::GetFirstVideoContent(offer->description());
- EXPECT_TRUE(content == NULL);
-}
-
-// Test that an answer can not be created if the last remote description is not
-// an offer.
-TEST_F(WebRtcSessionTest, CreateAnswerWithoutAnOffer) {
- Init();
- SessionDescriptionInterface* offer = CreateOffer();
- SetLocalDescriptionWithoutError(offer);
- SessionDescriptionInterface* answer = CreateRemoteAnswer(offer);
- SetRemoteDescriptionWithoutError(answer);
- EXPECT_TRUE(CreateAnswer() == NULL);
-}
-
-// Test that an answer contains the correct media content descriptions when no
-// constraints have been set.
-TEST_F(WebRtcSessionTest, CreateAnswerWithoutConstraintsOrStreams) {
- Init();
- // Create a remote offer with audio and video content.
- std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer());
- SetRemoteDescriptionWithoutError(offer.release());
- std::unique_ptr<SessionDescriptionInterface> answer(CreateAnswer());
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(answer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_FALSE(content->rejected);
-
- content = cricket::GetFirstVideoContent(answer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_FALSE(content->rejected);
-}
-
-// Test that an answer contains the correct media content descriptions when no
-// constraints have been set and the offer only contain audio.
-TEST_F(WebRtcSessionTest, CreateAudioAnswerWithoutConstraintsOrStreams) {
- Init();
- // Create a remote offer with audio only.
- cricket::MediaSessionOptions options;
- GetOptionsForAudioOnlyRemoteOffer(&options);
-
- std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer(options));
- ASSERT_TRUE(cricket::GetFirstVideoContent(offer->description()) == NULL);
- ASSERT_TRUE(cricket::GetFirstAudioContent(offer->description()) != NULL);
-
- SetRemoteDescriptionWithoutError(offer.release());
- std::unique_ptr<SessionDescriptionInterface> answer(CreateAnswer());
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(answer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_FALSE(content->rejected);
-
- EXPECT_TRUE(cricket::GetFirstVideoContent(answer->description()) == NULL);
-}
-
-// Test that an answer contains the correct media content descriptions when no
-// constraints have been set.
-TEST_F(WebRtcSessionTest, CreateAnswerWithoutConstraints) {
- Init();
- // Create a remote offer with audio and video content.
- std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer());
- SetRemoteDescriptionWithoutError(offer.release());
- // Test with a stream with tracks.
- SendAudioVideoStream1();
- std::unique_ptr<SessionDescriptionInterface> answer(CreateAnswer());
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(answer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_FALSE(content->rejected);
-
- content = cricket::GetFirstVideoContent(answer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_FALSE(content->rejected);
-}
-
-// Test that an answer contains the correct media content descriptions when
-// constraints have been set but no stream is sent.
-TEST_F(WebRtcSessionTest, CreateAnswerWithConstraintsWithoutStreams) {
- Init();
- // Create a remote offer with audio and video content.
- std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer());
- SetRemoteDescriptionWithoutError(offer.release());
-
- cricket::MediaSessionOptions session_options;
- remote_send_audio_ = false;
- remote_send_video_ = false;
- local_recv_audio_ = false;
- local_recv_video_ = false;
- std::unique_ptr<SessionDescriptionInterface> answer(
- CreateAnswer(session_options));
-
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(answer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_TRUE(content->rejected);
-
- content = cricket::GetFirstVideoContent(answer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_TRUE(content->rejected);
-}
-
-// Test that an answer contains the correct media content descriptions when
-// constraints have been set and streams are sent.
-TEST_F(WebRtcSessionTest, CreateAnswerWithConstraints) {
- Init();
- // Create a remote offer with audio and video content.
- std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer());
- SetRemoteDescriptionWithoutError(offer.release());
-
- cricket::MediaSessionOptions options;
- // Test with a stream with tracks.
- SendAudioVideoStream1();
- std::unique_ptr<SessionDescriptionInterface> answer(CreateAnswer(options));
-
- // TODO(perkj): Should the direction be set to SEND_ONLY?
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(answer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_FALSE(content->rejected);
-
- // TODO(perkj): Should the direction be set to SEND_ONLY?
- content = cricket::GetFirstVideoContent(answer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_FALSE(content->rejected);
-}
-
-TEST_F(WebRtcSessionTest, CreateOfferWithoutCNCodecs) {
- AddCNCodecs();
- Init();
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.offer_to_receive_audio =
- RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
- options.voice_activity_detection = false;
-
- std::unique_ptr<SessionDescriptionInterface> offer(CreateOffer(options));
-
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(offer->description());
- EXPECT_TRUE(content != NULL);
- EXPECT_TRUE(VerifyNoCNCodecs(content));
-}
-
-TEST_F(WebRtcSessionTest, CreateAnswerWithoutCNCodecs) {
- AddCNCodecs();
- Init();
- // Create a remote offer with audio and video content.
- std::unique_ptr<JsepSessionDescription> offer(CreateRemoteOffer());
- SetRemoteDescriptionWithoutError(offer.release());
-
- cricket::MediaSessionOptions options;
- options.vad_enabled = false;
- std::unique_ptr<SessionDescriptionInterface> answer(CreateAnswer(options));
- const cricket::ContentInfo* content =
- cricket::GetFirstAudioContent(answer->description());
- ASSERT_TRUE(content != NULL);
- EXPECT_TRUE(VerifyNoCNCodecs(content));
-}
-
-// This test verifies the call setup when remote answer with audio only and
-// later updates with video.
-TEST_F(WebRtcSessionTest, TestAVOfferWithAudioOnlyAnswer) {
- Init();
- EXPECT_TRUE(media_engine_->GetVideoChannel(0) == NULL);
- EXPECT_TRUE(media_engine_->GetVoiceChannel(0) == NULL);
-
- SendAudioVideoStream1();
- SessionDescriptionInterface* offer = CreateOffer();
-
- cricket::MediaSessionOptions options;
- AddMediaSection(cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
- cricket::MD_RECVONLY, kActive, &options);
- AddMediaSection(cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
- cricket::MD_INACTIVE, kStopped, &options);
- local_recv_video_ = false;
- SessionDescriptionInterface* answer = CreateRemoteAnswer(offer, options);
-
- // SetLocalDescription and SetRemoteDescriptions takes ownership of offer
- // and answer;
- SetLocalDescriptionWithoutError(offer);
- SetRemoteDescriptionWithoutError(answer);
-
- video_channel_ = media_engine_->GetVideoChannel(0);
- voice_channel_ = media_engine_->GetVoiceChannel(0);
-
- ASSERT_TRUE(video_channel_ == nullptr);
-
- ASSERT_EQ(0u, voice_channel_->recv_streams().size());
- ASSERT_EQ(1u, voice_channel_->send_streams().size());
- EXPECT_EQ(kAudioTrack1, voice_channel_->send_streams()[0].id);
-
- // Let the remote end update the session descriptions, with Audio and Video.
- SendAudioVideoStream2();
- local_recv_video_ = true;
- CreateAndSetRemoteOfferAndLocalAnswer();
-
- video_channel_ = media_engine_->GetVideoChannel(0);
- voice_channel_ = media_engine_->GetVoiceChannel(0);
-
- ASSERT_TRUE(video_channel_ != nullptr);
- ASSERT_TRUE(voice_channel_ != nullptr);
-
- ASSERT_EQ(1u, video_channel_->recv_streams().size());
- ASSERT_EQ(1u, video_channel_->send_streams().size());
- EXPECT_EQ(kVideoTrack2, video_channel_->recv_streams()[0].id);
- EXPECT_EQ(kVideoTrack2, video_channel_->send_streams()[0].id);
- ASSERT_EQ(1u, voice_channel_->recv_streams().size());
- ASSERT_EQ(1u, voice_channel_->send_streams().size());
- EXPECT_EQ(kAudioTrack2, voice_channel_->recv_streams()[0].id);
- EXPECT_EQ(kAudioTrack2, voice_channel_->send_streams()[0].id);
-
- // Change session back to audio only.
- // The remote side doesn't send and recv video.
- SendAudioOnlyStream2();
- remote_recv_video_ = false;
- CreateAndSetRemoteOfferAndLocalAnswer();
-
- video_channel_ = media_engine_->GetVideoChannel(0);
- voice_channel_ = media_engine_->GetVoiceChannel(0);
-
- // The audio is expected to be rejected.
- EXPECT_TRUE(video_channel_ == nullptr);
-
- ASSERT_EQ(1u, voice_channel_->recv_streams().size());
- EXPECT_EQ(kAudioTrack2, voice_channel_->recv_streams()[0].id);
- ASSERT_EQ(1u, voice_channel_->send_streams().size());
- EXPECT_EQ(kAudioTrack2, voice_channel_->send_streams()[0].id);
-}
-
-// This test verifies the call setup when remote answer with video only and
-// later updates with audio.
-TEST_F(WebRtcSessionTest, TestAVOfferWithVideoOnlyAnswer) {
- Init();
- EXPECT_TRUE(media_engine_->GetVideoChannel(0) == NULL);
- EXPECT_TRUE(media_engine_->GetVoiceChannel(0) == NULL);
- SendAudioVideoStream1();
- SessionDescriptionInterface* offer = CreateOffer();
-
- cricket::MediaSessionOptions options;
- AddMediaSection(cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
- cricket::MD_INACTIVE, kStopped, &options);
- AddMediaSection(cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
- cricket::MD_RECVONLY, kActive, &options);
- local_recv_audio_ = false;
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(offer, options, cricket::SEC_ENABLED);
-
- // SetLocalDescription and SetRemoteDescriptions takes ownership of offer
- // and answer.
- SetLocalDescriptionWithoutError(offer);
- SetRemoteDescriptionWithoutError(answer);
-
- video_channel_ = media_engine_->GetVideoChannel(0);
- voice_channel_ = media_engine_->GetVoiceChannel(0);
-
- ASSERT_TRUE(voice_channel_ == NULL);
- ASSERT_TRUE(video_channel_ != NULL);
-
- EXPECT_EQ(0u, video_channel_->recv_streams().size());
- ASSERT_EQ(1u, video_channel_->send_streams().size());
- EXPECT_EQ(kVideoTrack1, video_channel_->send_streams()[0].id);
-
- // Update the session descriptions, with Audio and Video.
- SendAudioVideoStream2();
- local_recv_audio_ = true;
- SessionDescriptionInterface* offer2 = CreateRemoteOffer();
- SetRemoteDescriptionWithoutError(offer2);
- cricket::MediaSessionOptions answer_options;
- // Disable the bundling here. If the media is bundled on audio
- // transport, then we can't reject the audio because switching the bundled
- // transport is not currently supported.
- // (https://bugs.chromium.org/p/webrtc/issues/detail?id=6704)
- answer_options.bundle_enabled = false;
- SessionDescriptionInterface* answer2 = CreateAnswer(answer_options);
- SetLocalDescriptionWithoutError(answer2);
-
- voice_channel_ = media_engine_->GetVoiceChannel(0);
-
- ASSERT_TRUE(voice_channel_ != NULL);
- ASSERT_EQ(1u, voice_channel_->recv_streams().size());
- ASSERT_EQ(1u, voice_channel_->send_streams().size());
- EXPECT_EQ(kAudioTrack2, voice_channel_->recv_streams()[0].id);
- EXPECT_EQ(kAudioTrack2, voice_channel_->send_streams()[0].id);
-
- // Change session back to video only.
- // The remote side doesn't send and recv audio.
- SendVideoOnlyStream2();
- remote_recv_audio_ = false;
- SessionDescriptionInterface* offer3 = CreateRemoteOffer();
- SetRemoteDescriptionWithoutError(offer3);
- SessionDescriptionInterface* answer3 = CreateAnswer(answer_options);
- SetLocalDescriptionWithoutError(answer3);
-
- video_channel_ = media_engine_->GetVideoChannel(0);
- voice_channel_ = media_engine_->GetVoiceChannel(0);
-
- // The video is expected to be rejected.
- EXPECT_TRUE(voice_channel_ == nullptr);
-
- ASSERT_EQ(1u, video_channel_->recv_streams().size());
- EXPECT_EQ(kVideoTrack2, video_channel_->recv_streams()[0].id);
- ASSERT_EQ(1u, video_channel_->send_streams().size());
- EXPECT_EQ(kVideoTrack2, video_channel_->send_streams()[0].id);
-}
-
// Test that candidates sent to the "video" transport do not get pushed down to
// the "audio" transport channel when bundling.
TEST_F(WebRtcSessionTest, TestIgnoreCandidatesForUnusedTransportWhenBundling) {
@@ -2458,127 +1604,6 @@
SetLocalDescriptionWithoutError(offer);
}
-// This test verifies the |initial_offerer| flag when session initiates the
-// call.
-TEST_F(WebRtcSessionTest, TestInitiatorFlagAsOriginator) {
- Init();
- EXPECT_FALSE(session_->initial_offerer());
- SessionDescriptionInterface* offer = CreateOffer();
- SessionDescriptionInterface* answer = CreateRemoteAnswer(offer);
- SetLocalDescriptionWithoutError(offer);
- EXPECT_TRUE(session_->initial_offerer());
- SetRemoteDescriptionWithoutError(answer);
- EXPECT_TRUE(session_->initial_offerer());
-}
-
-// This test verifies the |initial_offerer| flag when session receives the call.
-TEST_F(WebRtcSessionTest, TestInitiatorFlagAsReceiver) {
- Init();
- EXPECT_FALSE(session_->initial_offerer());
- SessionDescriptionInterface* offer = CreateRemoteOffer();
- SetRemoteDescriptionWithoutError(offer);
- SessionDescriptionInterface* answer = CreateAnswer();
-
- EXPECT_FALSE(session_->initial_offerer());
- SetLocalDescriptionWithoutError(answer);
- EXPECT_FALSE(session_->initial_offerer());
-}
-
-// Verifing local offer and remote answer have matching m-lines as per RFC 3264.
-TEST_F(WebRtcSessionTest, TestIncorrectMLinesInRemoteAnswer) {
- Init();
- SendAudioVideoStream1();
- SessionDescriptionInterface* offer = CreateOffer();
- SetLocalDescriptionWithoutError(offer);
- std::unique_ptr<SessionDescriptionInterface> answer(
- CreateRemoteAnswer(session_->local_description()));
-
- cricket::SessionDescription* answer_copy = answer->description()->Copy();
- answer_copy->RemoveContentByName("video");
- JsepSessionDescription* modified_answer =
- new JsepSessionDescription(JsepSessionDescription::kAnswer);
-
- EXPECT_TRUE(modified_answer->Initialize(answer_copy,
- answer->session_id(),
- answer->session_version()));
- SetRemoteDescriptionAnswerExpectError(kMlineMismatchInAnswer,
- modified_answer);
-
- // Different content names.
- std::string sdp;
- EXPECT_TRUE(answer->ToString(&sdp));
- const std::string kAudioMid = "a=mid:audio";
- const std::string kAudioMidReplaceStr = "a=mid:audio_content_name";
- rtc::replace_substrs(kAudioMid.c_str(), kAudioMid.length(),
- kAudioMidReplaceStr.c_str(),
- kAudioMidReplaceStr.length(),
- &sdp);
- SessionDescriptionInterface* modified_answer1 =
- CreateSessionDescription(JsepSessionDescription::kAnswer, sdp, NULL);
- SetRemoteDescriptionAnswerExpectError(kMlineMismatchInAnswer,
- modified_answer1);
-
- // Different media types.
- EXPECT_TRUE(answer->ToString(&sdp));
- const std::string kAudioMline = "m=audio";
- const std::string kAudioMlineReplaceStr = "m=video";
- rtc::replace_substrs(kAudioMline.c_str(), kAudioMline.length(),
- kAudioMlineReplaceStr.c_str(),
- kAudioMlineReplaceStr.length(),
- &sdp);
- SessionDescriptionInterface* modified_answer2 =
- CreateSessionDescription(JsepSessionDescription::kAnswer, sdp, NULL);
- SetRemoteDescriptionAnswerExpectError(kMlineMismatchInAnswer,
- modified_answer2);
-
- SetRemoteDescriptionWithoutError(answer.release());
-}
-
-// Verifying remote offer and local answer have matching m-lines as per
-// RFC 3264.
-TEST_F(WebRtcSessionTest, TestIncorrectMLinesInLocalAnswer) {
- Init();
- SendAudioVideoStream1();
- SessionDescriptionInterface* offer = CreateRemoteOffer();
- SetRemoteDescriptionWithoutError(offer);
- SessionDescriptionInterface* answer = CreateAnswer();
-
- cricket::SessionDescription* answer_copy = answer->description()->Copy();
- answer_copy->RemoveContentByName("video");
- JsepSessionDescription* modified_answer =
- new JsepSessionDescription(JsepSessionDescription::kAnswer);
-
- EXPECT_TRUE(modified_answer->Initialize(answer_copy,
- answer->session_id(),
- answer->session_version()));
- SetLocalDescriptionAnswerExpectError(kMlineMismatchInAnswer, modified_answer);
- SetLocalDescriptionWithoutError(answer);
-}
-
-TEST_F(WebRtcSessionTest, TestSessionContentError) {
- Init();
- SendAudioVideoStream1();
- SessionDescriptionInterface* offer = CreateOffer();
- const std::string session_id_orig = offer->session_id();
- const std::string session_version_orig = offer->session_version();
- SetLocalDescriptionWithoutError(offer);
-
- video_channel_ = media_engine_->GetVideoChannel(0);
- video_channel_->set_fail_set_send_codecs(true);
-
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(session_->local_description());
- SetRemoteDescriptionAnswerExpectError("ERROR_CONTENT", answer);
-
- // Test that after a content error, setting any description will
- // result in an error.
- video_channel_->set_fail_set_send_codecs(false);
- answer = CreateRemoteAnswer(session_->local_description());
- SetRemoteDescriptionExpectError("", "ERROR_CONTENT", answer);
- offer = CreateRemoteOffer();
- SetLocalDescriptionExpectError("", "ERROR_CONTENT", offer);
-}
-
TEST_F(WebRtcSessionTest, TestRtpDataChannel) {
configuration_.enable_rtp_data_channel = true;
Init();
@@ -2757,21 +1782,6 @@
last_data_channel_config_.open_handshake_role);
}
-TEST_F(WebRtcSessionTest, TestCombinedAudioVideoBweConstraint) {
- configuration_.combined_audio_video_bwe = rtc::Optional<bool>(true);
- Init();
- SendAudioVideoStream1();
- SessionDescriptionInterface* offer = CreateOffer();
-
- SetLocalDescriptionWithoutError(offer);
-
- voice_channel_ = media_engine_->GetVoiceChannel(0);
-
- ASSERT_TRUE(voice_channel_ != NULL);
- const cricket::AudioOptions& audio_options = voice_channel_->options();
- EXPECT_EQ(rtc::Optional<bool>(true), audio_options.combined_audio_video_bwe);
-}
-
#ifdef HAVE_QUIC
TEST_P(WebRtcSessionTest, TestNegotiateQuic) {
configuration_.enable_quic = true;
@@ -2791,33 +1801,6 @@
}
#endif // HAVE_QUIC
-// Tests that RTX codec is removed from the answer when it isn't supported
-// by local side.
-TEST_F(WebRtcSessionTest, TestRtxRemovedByCreateAnswer) {
- Init();
- // Send video only to match the |kSdpWithRtx|.
- SendVideoOnlyStream2();
- std::string offer_sdp(kSdpWithRtx);
-
- SessionDescriptionInterface* offer =
- CreateSessionDescription(JsepSessionDescription::kOffer, offer_sdp, NULL);
- EXPECT_TRUE(offer->ToString(&offer_sdp));
-
- // Offer SDP contains the RTX codec.
- EXPECT_TRUE(ContainsVideoCodecWithName(offer, "rtx"));
- SetRemoteDescriptionWithoutError(offer);
-
- // |offered_media_sections_| is used when creating answer.
- offered_media_sections_.push_back(cricket::MediaDescriptionOptions(
- cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
- cricket::RtpTransceiverDirection(true, true), false));
- // Don't create media section for audio in the answer.
- SessionDescriptionInterface* answer = CreateAnswer();
- // Answer SDP does not contain the RTX codec.
- EXPECT_FALSE(ContainsVideoCodecWithName(answer, "rtx"));
- SetLocalDescriptionWithoutError(answer);
-}
-
// This verifies that the voice channel after bundle has both options from video
// and voice channels.
TEST_F(WebRtcSessionTest, TestSetSocketOptionBeforeBundle) {
@@ -2866,34 +1849,6 @@
EXPECT_EQ(8000, option_val);
}
-// Test creating a session, request multiple offers, destroy the session
-// and make sure we got success/failure callbacks for all of the requests.
-// Background: crbug.com/507307
-TEST_F(WebRtcSessionTest, CreateOffersAndShutdown) {
- Init();
-
- rtc::scoped_refptr<WebRtcSessionCreateSDPObserverForTest> observers[100];
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.offer_to_receive_audio =
- RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
- cricket::MediaSessionOptions session_options;
- GetOptionsForOffer(options, &session_options);
- for (auto& o : observers) {
- o = new WebRtcSessionCreateSDPObserverForTest();
- session_->CreateOffer(o, options, session_options);
- }
-
- session_.reset();
-
- for (auto& o : observers) {
- // We expect to have received a notification now even if the session was
- // terminated. The offer creation may or may not have succeeded, but we
- // must have received a notification which, so the only invalid state
- // is kInit.
- EXPECT_NE(WebRtcSessionCreateSDPObserverForTest::kInit, o->state());
- }
-}
-
TEST_F(WebRtcSessionTest, TestPacketOptionsAndOnPacketSent) {
TestPacketOptions();
}