mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-14 06:10:40 +01:00
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}
This commit is contained in:
parent
930e1af76f
commit
3df5dcac9b
16 changed files with 1642 additions and 1310 deletions
|
@ -488,12 +488,11 @@ class FakeVoiceMediaChannel : public RtpHelper<VoiceMediaChannel> {
|
|||
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 @@ class FakeVoiceMediaChannel : public RtpHelper<VoiceMediaChannel> {
|
|||
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_;
|
||||
};
|
||||
|
|
|
@ -394,7 +394,9 @@ if (rtc_include_tests) {
|
|||
"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 @@ if (rtc_include_tests) {
|
|||
"../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",
|
||||
|
|
|
@ -828,10 +828,7 @@ PeerConnection::CreateDataChannel(
|
|||
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,
|
|||
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 @@ void PeerConnection::CreateAnswer(
|
|||
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 @@ void PeerConnection::CreateAnswer(
|
|||
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 @@ void PeerConnection::CreateAnswer(CreateSessionDescriptionObserver* observer,
|
|||
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 @@ void PeerConnection::SetLocalDescription(
|
|||
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 @@ void PeerConnection::SetLocalDescription(
|
|||
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 @@ void PeerConnection::SetRemoteDescription(
|
|||
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 @@ void PeerConnection::SetRemoteDescription(
|
|||
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 @@ void PeerConnection::SetRemoteDescription(
|
|||
// 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) {
|
||||
|
|
|
@ -75,7 +75,8 @@ class PeerConnectionCryptoUnitTest : public ::testing::Test {
|
|||
if (!wrapper) {
|
||||
return nullptr;
|
||||
}
|
||||
wrapper->AddAudioVideoStream("s", "a", "v");
|
||||
wrapper->AddAudioTrack("a");
|
||||
wrapper->AddVideoTrack("v");
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,8 @@ class PeerConnectionIceUnitTest : public ::testing::Test {
|
|||
if (!wrapper) {
|
||||
return nullptr;
|
||||
}
|
||||
wrapper->AddAudioVideoStream("s", "a", "v");
|
||||
wrapper->AddAudioTrack("a");
|
||||
wrapper->AddVideoTrack("v");
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
|
|
889
pc/peerconnection_media_unittest.cc
Normal file
889
pc/peerconnection_media_unittest.cc
Normal file
|
@ -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
|
501
pc/peerconnection_signaling_unittest.cc
Normal file
501
pc/peerconnection_signaling_unittest.cc
Normal file
|
@ -0,0 +1,501 @@
|
|||
/*
|
||||
* 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 {
|
||||
EXPECT_PRED_FORMAT2(AssertStartsWith, error,
|
||||
"CreateAnswer called without remote offer.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
|
@ -2517,9 +2517,9 @@ TEST_F(PeerConnectionInterfaceTest, CloseAndTestMethods) {
|
|||
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 @@ TEST_F(PeerConnectionInterfaceTest, CreateOfferWithVideoOnlyOptions) {
|
|||
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 @@ TEST_F(PeerConnectionInterfaceTest, CreateOfferWithRtpMux) {
|
|||
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 @@ TEST_F(PeerConnectionInterfaceTest, MediaStreamAddTrackRemoveTrackRenegotiate) {
|
|||
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 {
|
||||
|
|
|
@ -30,7 +30,7 @@ PeerConnectionWrapper::PeerConnectionWrapper(
|
|||
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 @@ PeerConnectionWrapper::CreateOffer() {
|
|||
}
|
||||
|
||||
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 @@ PeerConnectionWrapper::CreateAnswer() {
|
|||
|
||||
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 @@ PeerConnectionWrapper::CreateAnswerAndSetAsLocal() {
|
|||
}
|
||||
|
||||
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() {
|
||||
|
|
|
@ -54,54 +54,69 @@ class PeerConnectionWrapper {
|
|||
// 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
|
||||
|
|
|
@ -20,7 +20,14 @@ namespace webrtc {
|
|||
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
|
||||
|
|
|
@ -23,6 +23,11 @@ namespace webrtc {
|
|||
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*,
|
||||
|
|
|
@ -177,26 +177,27 @@ class MockCreateSessionDescriptionObserver
|
|||
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 @@ class MockSetSessionDescriptionObserver
|
|||
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 {
|
||||
|
|
|
@ -337,7 +337,7 @@ static bool BadSdp(const std::string& source,
|
|||
if (!type.empty()) {
|
||||
desc << " " << type;
|
||||
}
|
||||
desc << " sdp: " << reason;
|
||||
desc << " SDP: " << reason;
|
||||
|
||||
if (err_desc) {
|
||||
*err_desc = desc.str();
|
||||
|
@ -707,15 +707,13 @@ void WebRtcSession::CreateAnswer(
|
|||
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 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
|||
}
|
||||
|
||||
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 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
|||
|
||||
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 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
|||
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 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
|||
// 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 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
|||
// 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 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
|||
// 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);
|
||||
}
|
||||
|
|
|
@ -254,11 +254,9 @@ class WebRtcSession :
|
|||
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);
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue