TransportController refactoring.
Getting rid of TransportProxy, and in its place adding a TransportController class which will facilitate access to and manage the lifetimes of Transports. These Transports will now be accessed solely from the worker thread, simplifying their implementation. This refactoring also pulls Transport-related code out of BaseSession. Which means that BaseChannels will now rely on the TransportController interface to create channels, rather than BaseSession. Review URL: https://codereview.webrtc.org/1350523003 Cr-Commit-Position: refs/heads/master@{#10022}
This commit is contained in:
parent
8967183bf7
commit
47ee2f3b9f
@ -196,8 +196,11 @@
|
||||
if (newState == RTCICEGatheringGathering) {
|
||||
return;
|
||||
}
|
||||
NSAssert([_expectedICEGatheringChanges count] > 0,
|
||||
@"Unexpected ICE gathering state change");
|
||||
int expectedState = [self popFirstElementAsInt:_expectedICEGatheringChanges];
|
||||
NSAssert(expectedState == (int)newState, @"Empty expectation array");
|
||||
NSAssert(expectedState == (int)newState,
|
||||
@"ICE gathering state should match expectation");
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||
@ -205,8 +208,11 @@
|
||||
// See TODO(fischman) in RTCPeerConnectionTest.mm about Completed.
|
||||
if (newState == RTCICEConnectionCompleted)
|
||||
return;
|
||||
NSAssert([_expectedICEConnectionChanges count] > 0,
|
||||
@"Unexpected ICE connection state change");
|
||||
int expectedState = [self popFirstElementAsInt:_expectedICEConnectionChanges];
|
||||
NSAssert(expectedState == (int)newState, @"Empty expectation array");
|
||||
NSAssert(expectedState == (int)newState,
|
||||
@"ICE connection state should match expectation");
|
||||
}
|
||||
|
||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||
|
||||
@ -182,7 +182,10 @@
|
||||
EXPECT_GT([answerSDP.description length], 0);
|
||||
|
||||
[offeringExpectations expectICECandidates:2];
|
||||
[answeringExpectations expectICECandidates:2];
|
||||
// It's possible to only have 1 ICE candidate for the answerer, since we use
|
||||
// BUNDLE and rtcp-mux by default, and don't provide any ICE servers in this
|
||||
// test.
|
||||
[answeringExpectations expectICECandidates:1];
|
||||
|
||||
sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init];
|
||||
[answeringExpectations expectSignalingChange:RTCSignalingStable];
|
||||
|
||||
@ -616,6 +616,10 @@ void PeerConnection::SetLocalDescription(
|
||||
}
|
||||
SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer);
|
||||
signaling_thread()->Post(this, MSG_SET_SESSIONDESCRIPTION_SUCCESS, msg);
|
||||
// MaybeStartGathering needs to be called after posting
|
||||
// MSG_SET_SESSIONDESCRIPTION_SUCCESS, so that we don't signal any candidates
|
||||
// before signaling that SetLocalDescription completed.
|
||||
session_->MaybeStartGathering();
|
||||
}
|
||||
|
||||
void PeerConnection::SetRemoteDescription(
|
||||
@ -868,6 +872,11 @@ void PeerConnection::OnRemoveLocalStream(MediaStreamInterface* stream) {
|
||||
void PeerConnection::OnIceConnectionChange(
|
||||
PeerConnectionInterface::IceConnectionState new_state) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
// After transitioning to "closed", ignore any additional states from
|
||||
// WebRtcSession (such as "disconnected").
|
||||
if (ice_connection_state_ == kIceConnectionClosed) {
|
||||
return;
|
||||
}
|
||||
ice_connection_state_ = new_state;
|
||||
observer_->OnIceConnectionChange(ice_connection_state_);
|
||||
}
|
||||
|
||||
@ -697,24 +697,18 @@ void StatsCollector::ExtractSessionInfo() {
|
||||
// expose them in stats reports. All channels in a transport share the
|
||||
// same local and remote certificates.
|
||||
//
|
||||
// Note that Transport::GetCertificate and Transport::GetRemoteCertificate
|
||||
// invoke method calls on the worker thread and block this thread, but
|
||||
// messages are still processed on this thread, which may blow way the
|
||||
// existing transports. So we cannot reuse |transport| after these calls.
|
||||
StatsReport::Id local_cert_report_id, remote_cert_report_id;
|
||||
|
||||
cricket::Transport* transport =
|
||||
session_->GetTransport(transport_iter.second.content_name);
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate;
|
||||
if (transport && transport->GetCertificate(&certificate)) {
|
||||
if (session_->GetLocalCertificate(transport_iter.second.transport_name,
|
||||
&certificate)) {
|
||||
StatsReport* r = AddCertificateReports(&(certificate->ssl_certificate()));
|
||||
if (r)
|
||||
local_cert_report_id = r->id();
|
||||
}
|
||||
|
||||
transport = session_->GetTransport(transport_iter.second.content_name);
|
||||
rtc::scoped_ptr<rtc::SSLCertificate> cert;
|
||||
if (transport && transport->GetRemoteSSLCertificate(cert.accept())) {
|
||||
if (session_->GetRemoteSSLCertificate(transport_iter.second.transport_name,
|
||||
cert.accept())) {
|
||||
StatsReport* r = AddCertificateReports(cert.get());
|
||||
if (r)
|
||||
remote_cert_report_id = r->id();
|
||||
@ -722,7 +716,7 @@ void StatsCollector::ExtractSessionInfo() {
|
||||
|
||||
for (const auto& channel_iter : transport_iter.second.channel_stats) {
|
||||
StatsReport::Id id(StatsReport::NewComponentId(
|
||||
transport_iter.second.content_name, channel_iter.component));
|
||||
transport_iter.second.transport_name, channel_iter.component));
|
||||
StatsReport* channel_report = reports_.ReplaceOrAddNew(id);
|
||||
channel_report->set_timestamp(stats_gathering_started_);
|
||||
channel_report->AddInt(StatsReport::kStatsValueNameComponent,
|
||||
@ -939,7 +933,6 @@ void StatsCollector::UpdateTrackReports() {
|
||||
StatsReport* report = entry.second;
|
||||
report->set_timestamp(stats_gathering_started_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void StatsCollector::ClearUpdateStatsCacheForTest() {
|
||||
|
||||
@ -27,6 +27,8 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "talk/app/webrtc/statscollector.h"
|
||||
|
||||
#include "talk/app/webrtc/mediastream.h"
|
||||
@ -45,7 +47,7 @@
|
||||
#include "webrtc/base/fakesslidentity.h"
|
||||
#include "webrtc/base/gunit.h"
|
||||
#include "webrtc/base/network.h"
|
||||
#include "webrtc/p2p/base/fakesession.h"
|
||||
#include "webrtc/p2p/base/faketransportcontroller.h"
|
||||
|
||||
using rtc::scoped_ptr;
|
||||
using testing::_;
|
||||
@ -89,7 +91,12 @@ class MockWebRtcSession : public webrtc::WebRtcSession {
|
||||
MOCK_METHOD2(GetLocalTrackIdBySsrc, bool(uint32, std::string*));
|
||||
MOCK_METHOD2(GetRemoteTrackIdBySsrc, bool(uint32, std::string*));
|
||||
MOCK_METHOD1(GetTransportStats, bool(cricket::SessionStats*));
|
||||
MOCK_METHOD1(GetTransport, cricket::Transport*(const std::string&));
|
||||
MOCK_METHOD2(GetLocalCertificate,
|
||||
bool(const std::string& transport_name,
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate));
|
||||
MOCK_METHOD2(GetRemoteSSLCertificate,
|
||||
bool(const std::string& transport_name,
|
||||
rtc::SSLCertificate** cert));
|
||||
};
|
||||
|
||||
class MockVideoMediaChannel : public cricket::FakeVideoMediaChannel {
|
||||
@ -500,7 +507,7 @@ class StatsCollectorTest : public testing::Test {
|
||||
cricket::TransportStats transport_stats;
|
||||
cricket::TransportChannelStats channel_stats;
|
||||
channel_stats.component = 1;
|
||||
transport_stats.content_name = kTransportName;
|
||||
transport_stats.transport_name = kTransportName;
|
||||
transport_stats.channel_stats.push_back(channel_stats);
|
||||
|
||||
session_stats_.transport_stats[kTransportName] = transport_stats;
|
||||
@ -647,36 +654,27 @@ class StatsCollectorTest : public testing::Test {
|
||||
channel_stats.ssl_cipher = "the-ssl-cipher";
|
||||
|
||||
cricket::TransportStats transport_stats;
|
||||
transport_stats.content_name = "audio";
|
||||
transport_stats.transport_name = "audio";
|
||||
transport_stats.channel_stats.push_back(channel_stats);
|
||||
|
||||
cricket::SessionStats session_stats;
|
||||
session_stats.transport_stats[transport_stats.content_name] =
|
||||
session_stats.transport_stats[transport_stats.transport_name] =
|
||||
transport_stats;
|
||||
|
||||
// Fake certificates to report.
|
||||
// Fake certificate to report
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate(
|
||||
rtc::RTCCertificate::Create(rtc::scoped_ptr<rtc::FakeSSLIdentity>(
|
||||
new rtc::FakeSSLIdentity(local_cert)).Pass()));
|
||||
rtc::scoped_ptr<rtc::FakeSSLCertificate> remote_cert_copy(
|
||||
remote_cert.GetReference());
|
||||
|
||||
// Fake transport object.
|
||||
rtc::scoped_ptr<cricket::FakeTransport> transport(
|
||||
new cricket::FakeTransport(
|
||||
session_.signaling_thread(),
|
||||
session_.worker_thread(),
|
||||
transport_stats.content_name));
|
||||
transport->SetCertificate(local_certificate);
|
||||
cricket::FakeTransportChannel* channel =
|
||||
static_cast<cricket::FakeTransportChannel*>(
|
||||
transport->CreateChannel(channel_stats.component));
|
||||
EXPECT_FALSE(channel == NULL);
|
||||
channel->SetRemoteSSLCertificate(remote_cert_copy.get());
|
||||
new rtc::FakeSSLIdentity(local_cert))
|
||||
.Pass()));
|
||||
|
||||
// Configure MockWebRtcSession
|
||||
EXPECT_CALL(session_, GetTransport(transport_stats.content_name))
|
||||
.WillRepeatedly(Return(transport.get()));
|
||||
EXPECT_CALL(session_,
|
||||
GetLocalCertificate(transport_stats.transport_name, _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(local_certificate), Return(true)));
|
||||
EXPECT_CALL(session_,
|
||||
GetRemoteSSLCertificate(transport_stats.transport_name, _))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<1>(remote_cert.GetReference()), Return(true)));
|
||||
EXPECT_CALL(session_, GetTransportStats(_))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(session_stats),
|
||||
Return(true)));
|
||||
@ -790,14 +788,17 @@ TEST_F(StatsCollectorTest, ExtractDataInfo) {
|
||||
TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
const char kVideoChannelName[] = "video";
|
||||
|
||||
InitSessionStats(kVideoChannelName);
|
||||
EXPECT_CALL(session_, GetTransportStats(_))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
|
||||
Return(true)));
|
||||
EXPECT_CALL(session_, GetTransport(_))
|
||||
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||
|
||||
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
|
||||
cricket::VideoChannel video_channel(rtc::Thread::Current(),
|
||||
@ -833,14 +834,17 @@ TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) {
|
||||
TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
const char kVideoChannelName[] = "video";
|
||||
|
||||
InitSessionStats(kVideoChannelName);
|
||||
EXPECT_CALL(session_, GetTransportStats(_))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
|
||||
Return(true)));
|
||||
EXPECT_CALL(session_, GetTransport(_))
|
||||
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||
|
||||
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
|
||||
cricket::VideoChannel video_channel(rtc::Thread::Current(),
|
||||
@ -946,13 +950,16 @@ TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) {
|
||||
TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
const char kVideoChannelName[] = "video";
|
||||
InitSessionStats(kVideoChannelName);
|
||||
EXPECT_CALL(session_, GetTransportStats(_))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
|
||||
Return(true)));
|
||||
EXPECT_CALL(session_, GetTransport(_))
|
||||
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||
|
||||
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
|
||||
cricket::VideoChannel video_channel(rtc::Thread::Current(),
|
||||
@ -1011,11 +1018,13 @@ TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) {
|
||||
TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
// Ignore unused callback (logspam).
|
||||
EXPECT_CALL(session_, GetTransport(_))
|
||||
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
|
||||
// The content_name known by the video channel.
|
||||
// The transport_name known by the video channel.
|
||||
const std::string kVcName("vcname");
|
||||
cricket::VideoChannel video_channel(rtc::Thread::Current(),
|
||||
media_channel, NULL, kVcName, false);
|
||||
@ -1073,7 +1082,7 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
|
||||
// The content_name known by the video channel.
|
||||
// The transport_name known by the video channel.
|
||||
const std::string kVcName("vcname");
|
||||
cricket::VideoChannel video_channel(rtc::Thread::Current(),
|
||||
media_channel, NULL, kVcName, false);
|
||||
@ -1096,11 +1105,13 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) {
|
||||
TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
// Ignore unused callback (logspam).
|
||||
EXPECT_CALL(session_, GetTransport(_))
|
||||
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
|
||||
// The content_name known by the video channel.
|
||||
// The transport_name known by the video channel.
|
||||
const std::string kVcName("vcname");
|
||||
cricket::VideoChannel video_channel(rtc::Thread::Current(),
|
||||
media_channel, NULL, kVcName, false);
|
||||
@ -1145,13 +1156,16 @@ TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) {
|
||||
TEST_F(StatsCollectorTest, ReportsFromRemoteTrack) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
const char kVideoChannelName[] = "video";
|
||||
InitSessionStats(kVideoChannelName);
|
||||
EXPECT_CALL(session_, GetTransportStats(_))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
|
||||
Return(true)));
|
||||
EXPECT_CALL(session_, GetTransport(_))
|
||||
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||
|
||||
MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
|
||||
cricket::VideoChannel video_channel(rtc::Thread::Current(),
|
||||
@ -1330,6 +1344,11 @@ TEST_F(StatsCollectorTest, ChainlessCertificateReportsCreated) {
|
||||
TEST_F(StatsCollectorTest, NoTransport) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
StatsReports reports; // returned values.
|
||||
|
||||
// Fake stats to process.
|
||||
@ -1337,16 +1356,14 @@ TEST_F(StatsCollectorTest, NoTransport) {
|
||||
channel_stats.component = 1;
|
||||
|
||||
cricket::TransportStats transport_stats;
|
||||
transport_stats.content_name = "audio";
|
||||
transport_stats.transport_name = "audio";
|
||||
transport_stats.channel_stats.push_back(channel_stats);
|
||||
|
||||
cricket::SessionStats session_stats;
|
||||
session_stats.transport_stats[transport_stats.content_name] =
|
||||
session_stats.transport_stats[transport_stats.transport_name] =
|
||||
transport_stats;
|
||||
|
||||
// Configure MockWebRtcSession
|
||||
EXPECT_CALL(session_, GetTransport(transport_stats.content_name))
|
||||
.WillRepeatedly(ReturnNull());
|
||||
EXPECT_CALL(session_, GetTransportStats(_))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(session_stats),
|
||||
Return(true)));
|
||||
@ -1389,6 +1406,11 @@ TEST_F(StatsCollectorTest, NoTransport) {
|
||||
TEST_F(StatsCollectorTest, NoCertificates) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
StatsReports reports; // returned values.
|
||||
|
||||
// Fake stats to process.
|
||||
@ -1396,23 +1418,18 @@ TEST_F(StatsCollectorTest, NoCertificates) {
|
||||
channel_stats.component = 1;
|
||||
|
||||
cricket::TransportStats transport_stats;
|
||||
transport_stats.content_name = "audio";
|
||||
transport_stats.transport_name = "audio";
|
||||
transport_stats.channel_stats.push_back(channel_stats);
|
||||
|
||||
cricket::SessionStats session_stats;
|
||||
session_stats.transport_stats[transport_stats.content_name] =
|
||||
session_stats.transport_stats[transport_stats.transport_name] =
|
||||
transport_stats;
|
||||
|
||||
// Fake transport object.
|
||||
rtc::scoped_ptr<cricket::FakeTransport> transport(
|
||||
new cricket::FakeTransport(
|
||||
session_.signaling_thread(),
|
||||
session_.worker_thread(),
|
||||
transport_stats.content_name));
|
||||
new cricket::FakeTransport(transport_stats.transport_name));
|
||||
|
||||
// Configure MockWebRtcSession
|
||||
EXPECT_CALL(session_, GetTransport(transport_stats.content_name))
|
||||
.WillRepeatedly(Return(transport.get()));
|
||||
EXPECT_CALL(session_, GetTransportStats(_))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(session_stats),
|
||||
Return(true)));
|
||||
@ -1458,12 +1475,13 @@ TEST_F(StatsCollectorTest, UnsupportedDigestIgnored) {
|
||||
TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
// Ignore unused callback (logspam).
|
||||
EXPECT_CALL(session_, GetTransport(_))
|
||||
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
|
||||
// The content_name known by the voice channel.
|
||||
// The transport_name known by the voice channel.
|
||||
const std::string kVcName("vcname");
|
||||
cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
|
||||
media_engine_, media_channel, NULL, kVcName, false);
|
||||
@ -1492,11 +1510,13 @@ TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) {
|
||||
TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
// Ignore unused callback (logspam).
|
||||
EXPECT_CALL(session_, GetTransport(_))
|
||||
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
|
||||
// The content_name known by the voice channel.
|
||||
// The transport_name known by the voice channel.
|
||||
const std::string kVcName("vcname");
|
||||
cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
|
||||
media_engine_, media_channel, NULL, kVcName, false);
|
||||
@ -1519,11 +1539,13 @@ TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) {
|
||||
TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
// Ignore unused callback (logspam).
|
||||
EXPECT_CALL(session_, GetTransport(_))
|
||||
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
|
||||
// The content_name known by the voice channel.
|
||||
// The transport_name known by the voice channel.
|
||||
const std::string kVcName("vcname");
|
||||
cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
|
||||
media_engine_, media_channel, NULL, kVcName, false);
|
||||
@ -1578,11 +1600,13 @@ TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) {
|
||||
TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
// Ignore unused callback (logspam).
|
||||
EXPECT_CALL(session_, GetTransport(_))
|
||||
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
|
||||
// The content_name known by the voice channel.
|
||||
// The transport_name known by the voice channel.
|
||||
const std::string kVcName("vcname");
|
||||
cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
|
||||
media_engine_, media_channel, NULL, kVcName, false);
|
||||
@ -1663,11 +1687,13 @@ TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) {
|
||||
TEST_F(StatsCollectorTest, TwoLocalTracksWithSameSsrc) {
|
||||
StatsCollectorForTest stats(&session_);
|
||||
|
||||
// Ignore unused callback (logspam).
|
||||
EXPECT_CALL(session_, GetTransport(_))
|
||||
.WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
|
||||
EXPECT_CALL(session_, GetLocalCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
|
||||
.WillRepeatedly(Return(false));
|
||||
|
||||
MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
|
||||
// The content_name known by the voice channel.
|
||||
// The transport_name known by the voice channel.
|
||||
const std::string kVcName("vcname");
|
||||
cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
|
||||
media_engine_, media_channel, NULL, kVcName, false);
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "talk/app/webrtc/jsepicecandidate.h"
|
||||
#include "talk/app/webrtc/jsepsessiondescription.h"
|
||||
@ -86,6 +87,7 @@ const char kDtlsSetupFailureRtp[] =
|
||||
"Couldn't set up DTLS-SRTP on RTP channel.";
|
||||
const char kDtlsSetupFailureRtcp[] =
|
||||
"Couldn't set up DTLS-SRTP on RTCP channel.";
|
||||
const char kEnableBundleFailed[] = "Failed to enable BUNDLE.";
|
||||
const int kMaxUnsignalledRecvStreams = 20;
|
||||
|
||||
IceCandidatePairType GetIceCandidatePairCounter(
|
||||
@ -543,7 +545,6 @@ WebRtcSession::WebRtcSession(
|
||||
worker_thread,
|
||||
port_allocator,
|
||||
rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX),
|
||||
cricket::NS_JINGLE_RTP,
|
||||
false),
|
||||
// RFC 3264: The numeric value of the session id and version in the
|
||||
// o line MUST be representable with a "64 bit signed integer".
|
||||
@ -558,6 +559,14 @@ WebRtcSession::WebRtcSession(
|
||||
data_channel_type_(cricket::DCT_NONE),
|
||||
ice_restart_latch_(new IceRestartAnswerLatch),
|
||||
metrics_observer_(NULL) {
|
||||
transport_controller()->SignalConnectionState.connect(
|
||||
this, &WebRtcSession::OnTransportControllerConnectionState);
|
||||
transport_controller()->SignalReceiving.connect(
|
||||
this, &WebRtcSession::OnTransportControllerReceiving);
|
||||
transport_controller()->SignalGatheringState.connect(
|
||||
this, &WebRtcSession::OnTransportControllerGatheringState);
|
||||
transport_controller()->SignalCandidatesGathered.connect(
|
||||
this, &WebRtcSession::OnTransportControllerCandidatesGathered);
|
||||
}
|
||||
|
||||
WebRtcSession::~WebRtcSession() {
|
||||
@ -583,12 +592,12 @@ WebRtcSession::~WebRtcSession() {
|
||||
|
||||
bool WebRtcSession::Initialize(
|
||||
const PeerConnectionFactoryInterface::Options& options,
|
||||
const MediaConstraintsInterface* constraints,
|
||||
const MediaConstraintsInterface* constraints,
|
||||
rtc::scoped_ptr<DtlsIdentityStoreInterface> dtls_identity_store,
|
||||
const PeerConnectionInterface::RTCConfiguration& rtc_configuration) {
|
||||
bundle_policy_ = rtc_configuration.bundle_policy;
|
||||
rtcp_mux_policy_ = rtc_configuration.rtcp_mux_policy;
|
||||
SetSslMaxProtocolVersion(options.ssl_max_version);
|
||||
transport_controller()->SetSslMaxProtocolVersion(options.ssl_max_version);
|
||||
|
||||
// Obtain a certificate from RTCConfiguration if any were provided (optional).
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate;
|
||||
@ -613,10 +622,8 @@ bool WebRtcSession::Initialize(
|
||||
// Enable DTLS by default if we have an identity store or a certificate.
|
||||
dtls_enabled_ = (dtls_identity_store || certificate);
|
||||
// |constraints| can override the default |dtls_enabled_| value.
|
||||
if (FindConstraint(
|
||||
constraints,
|
||||
MediaConstraintsInterface::kEnableDtlsSrtp,
|
||||
&value, nullptr)) {
|
||||
if (FindConstraint(constraints, MediaConstraintsInterface::kEnableDtlsSrtp,
|
||||
&value, nullptr)) {
|
||||
dtls_enabled_ = value;
|
||||
}
|
||||
}
|
||||
@ -736,35 +743,21 @@ bool WebRtcSession::Initialize(
|
||||
if (!dtls_enabled_) {
|
||||
// Construct with DTLS disabled.
|
||||
webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
|
||||
signaling_thread(),
|
||||
channel_manager_,
|
||||
mediastream_signaling_,
|
||||
this,
|
||||
id(),
|
||||
data_channel_type_));
|
||||
signaling_thread(), channel_manager_, mediastream_signaling_, this,
|
||||
id(), data_channel_type_));
|
||||
} else {
|
||||
// Construct with DTLS enabled.
|
||||
if (!certificate) {
|
||||
// Use the |dtls_identity_store| to generate a certificate.
|
||||
RTC_DCHECK(dtls_identity_store);
|
||||
webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
|
||||
signaling_thread(),
|
||||
channel_manager_,
|
||||
mediastream_signaling_,
|
||||
dtls_identity_store.Pass(),
|
||||
this,
|
||||
id(),
|
||||
data_channel_type_));
|
||||
signaling_thread(), channel_manager_, mediastream_signaling_,
|
||||
dtls_identity_store.Pass(), this, id(), data_channel_type_));
|
||||
} else {
|
||||
// Use the already generated certificate.
|
||||
webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
|
||||
signaling_thread(),
|
||||
channel_manager_,
|
||||
mediastream_signaling_,
|
||||
certificate,
|
||||
this,
|
||||
id(),
|
||||
data_channel_type_));
|
||||
signaling_thread(), channel_manager_, mediastream_signaling_,
|
||||
certificate, this, id(), data_channel_type_));
|
||||
}
|
||||
}
|
||||
|
||||
@ -791,26 +784,12 @@ bool WebRtcSession::Initialize(
|
||||
|
||||
void WebRtcSession::Terminate() {
|
||||
SetState(STATE_RECEIVEDTERMINATE);
|
||||
RemoveUnusedChannelsAndTransports(NULL);
|
||||
RemoveUnusedChannels(NULL);
|
||||
ASSERT(!voice_channel_);
|
||||
ASSERT(!video_channel_);
|
||||
ASSERT(!data_channel_);
|
||||
}
|
||||
|
||||
bool WebRtcSession::StartCandidatesAllocation() {
|
||||
// SpeculativelyConnectTransportChannels, will call ConnectChannels method
|
||||
// from TransportProxy to start gathering ice candidates.
|
||||
SpeculativelyConnectAllTransportChannels();
|
||||
if (!saved_candidates_.empty()) {
|
||||
// If there are saved candidates which arrived before local description is
|
||||
// set, copy those to remote description.
|
||||
CopySavedCandidates(remote_desc_.get());
|
||||
}
|
||||
// Push remote candidates present in remote description to transport channels.
|
||||
UseCandidatesInSessionDescription(remote_desc_.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebRtcSession::SetSdesPolicy(cricket::SecurePolicy secure_policy) {
|
||||
webrtc_session_desc_factory_->SetSdesPolicy(secure_policy);
|
||||
}
|
||||
@ -826,17 +805,7 @@ bool WebRtcSession::GetSslRole(rtc::SSLRole* role) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(mallinath) - Return role of each transport, as role may differ from
|
||||
// one another.
|
||||
// In current implementaion we just return the role of first transport in the
|
||||
// transport map.
|
||||
for (cricket::TransportMap::const_iterator iter = transport_proxies().begin();
|
||||
iter != transport_proxies().end(); ++iter) {
|
||||
if (iter->second->impl()) {
|
||||
return iter->second->impl()->GetSslRole(role);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return transport_controller()->GetSslRole(role);
|
||||
}
|
||||
|
||||
void WebRtcSession::CreateOffer(
|
||||
@ -852,6 +821,8 @@ void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer,
|
||||
|
||||
bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
||||
std::string* err_desc) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
|
||||
// Takes the ownership of |desc| regardless of the result.
|
||||
rtc::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
|
||||
|
||||
@ -884,16 +855,24 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
||||
return BadLocalSdp(desc->type(), kCreateChannelFailed, err_desc);
|
||||
}
|
||||
|
||||
// Remove channel and transport proxies, if MediaContentDescription is
|
||||
// rejected.
|
||||
RemoveUnusedChannelsAndTransports(local_desc_->description());
|
||||
// Remove unused channels if MediaContentDescription is rejected.
|
||||
RemoveUnusedChannels(local_desc_->description());
|
||||
|
||||
if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Kick starting the ice candidates allocation.
|
||||
StartCandidatesAllocation();
|
||||
if (remote_description()) {
|
||||
// Now that we have a local description, we can push down remote candidates
|
||||
// that we stored, and those from the remote description.
|
||||
if (!saved_candidates_.empty()) {
|
||||
// If there are saved candidates which arrived before the local
|
||||
// description was set, copy those to the remote description.
|
||||
CopySavedCandidates(remote_desc_.get());
|
||||
}
|
||||
// Push remote candidates in remote description to transport channels.
|
||||
UseCandidatesInSessionDescription(remote_desc_.get());
|
||||
}
|
||||
|
||||
// Update state and SSRC of local MediaStreams and DataChannels based on the
|
||||
// local session description.
|
||||
@ -911,6 +890,8 @@ bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
|
||||
|
||||
bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
||||
std::string* err_desc) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
|
||||
// Takes the ownership of |desc| regardless of the result.
|
||||
rtc::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
|
||||
|
||||
@ -927,9 +908,8 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
||||
return BadRemoteSdp(desc->type(), kCreateChannelFailed, err_desc);
|
||||
}
|
||||
|
||||
// Remove channel and transport proxies, if MediaContentDescription is
|
||||
// rejected.
|
||||
RemoveUnusedChannelsAndTransports(desc->description());
|
||||
// Remove unused channels if MediaContentDescription is rejected.
|
||||
RemoveUnusedChannels(desc->description());
|
||||
|
||||
// NOTE: Candidates allocation will be initiated only when SetLocalDescription
|
||||
// is called.
|
||||
@ -988,6 +968,8 @@ bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
|
||||
bool WebRtcSession::UpdateSessionState(
|
||||
Action action, cricket::ContentSource source,
|
||||
std::string* err_desc) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
|
||||
// If there's already a pending error then no state transition should happen.
|
||||
// But all call-sites should be verifying this before calling us!
|
||||
ASSERT(error() == cricket::BaseSession::ERROR_NONE);
|
||||
@ -1021,7 +1003,21 @@ bool WebRtcSession::UpdateSessionState(
|
||||
if (!PushdownTransportDescription(source, cricket::CA_ANSWER, &td_err)) {
|
||||
return BadAnswerSdp(source, MakeTdErrorString(td_err), err_desc);
|
||||
}
|
||||
MaybeEnableMuxingSupport();
|
||||
const cricket::ContentGroup* local_bundle =
|
||||
BaseSession::local_description()->GetGroupByName(
|
||||
cricket::GROUP_TYPE_BUNDLE);
|
||||
const cricket::ContentGroup* remote_bundle =
|
||||
BaseSession::remote_description()->GetGroupByName(
|
||||
cricket::GROUP_TYPE_BUNDLE);
|
||||
if (local_bundle && remote_bundle) {
|
||||
// The answerer decides the transport to bundle on
|
||||
const cricket::ContentGroup* answer_bundle =
|
||||
(source == cricket::CS_LOCAL ? local_bundle : remote_bundle);
|
||||
if (!EnableBundle(*answer_bundle)) {
|
||||
LOG(LS_WARNING) << "Failed to enable BUNDLE.";
|
||||
return BadAnswerSdp(source, kEnableBundleFailed, err_desc);
|
||||
}
|
||||
}
|
||||
EnableChannels();
|
||||
SetState(source == cricket::CS_LOCAL ?
|
||||
STATE_SENTACCEPT : STATE_RECEIVEDACCEPT);
|
||||
@ -1070,32 +1066,101 @@ WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) {
|
||||
|
||||
bool WebRtcSession::GetTransportStats(cricket::SessionStats* stats) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
return (GetChannelTransportStats(voice_channel(), stats) &&
|
||||
GetChannelTransportStats(video_channel(), stats) &&
|
||||
GetChannelTransportStats(data_channel(), stats));
|
||||
}
|
||||
|
||||
const auto get_transport_stats = [stats](const std::string& content_name,
|
||||
cricket::Transport* transport) {
|
||||
const std::string& transport_id = transport->content_name();
|
||||
stats->proxy_to_transport[content_name] = transport_id;
|
||||
if (stats->transport_stats.find(transport_id)
|
||||
!= stats->transport_stats.end()) {
|
||||
// Transport stats already done for this transport.
|
||||
bool WebRtcSession::GetChannelTransportStats(cricket::BaseChannel* ch,
|
||||
cricket::SessionStats* stats) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
if (!ch) {
|
||||
// Not using this channel.
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& content_name = ch->content_name();
|
||||
const std::string& transport_name = ch->transport_name();
|
||||
stats->proxy_to_transport[content_name] = transport_name;
|
||||
if (stats->transport_stats.find(transport_name) !=
|
||||
stats->transport_stats.end()) {
|
||||
// Transport stats already done for this transport.
|
||||
return true;
|
||||
}
|
||||
|
||||
cricket::TransportStats tstats;
|
||||
if (!transport_controller()->GetStats(transport_name, &tstats)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stats->transport_stats[transport_name] = tstats;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcSession::GetLocalCertificate(
|
||||
const std::string& transport_name,
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
return transport_controller()->GetLocalCertificate(transport_name,
|
||||
certificate);
|
||||
}
|
||||
|
||||
bool WebRtcSession::GetRemoteSSLCertificate(const std::string& transport_name,
|
||||
rtc::SSLCertificate** cert) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
return transport_controller()->GetRemoteSSLCertificate(transport_name, cert);
|
||||
}
|
||||
|
||||
cricket::BaseChannel* WebRtcSession::GetChannel(
|
||||
const std::string& content_name) {
|
||||
if (voice_channel() && voice_channel()->content_name() == content_name) {
|
||||
return voice_channel();
|
||||
}
|
||||
if (video_channel() && video_channel()->content_name() == content_name) {
|
||||
return video_channel();
|
||||
}
|
||||
if (data_channel() && data_channel()->content_name() == content_name) {
|
||||
return data_channel();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
|
||||
const std::string* first_content_name = bundle.FirstContentName();
|
||||
if (!first_content_name) {
|
||||
LOG(LS_WARNING) << "Tried to BUNDLE with no contents.";
|
||||
return false;
|
||||
}
|
||||
const std::string& transport_name = *first_content_name;
|
||||
cricket::BaseChannel* first_channel = GetChannel(transport_name);
|
||||
|
||||
auto maybe_set_transport = [this, bundle, transport_name,
|
||||
first_channel](cricket::BaseChannel* ch) {
|
||||
if (!ch || !bundle.HasContentName(ch->content_name())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cricket::TransportStats tstats;
|
||||
if (!transport->GetStats(&tstats)) {
|
||||
return false;
|
||||
if (ch->transport_name() == transport_name) {
|
||||
LOG(LS_INFO) << "BUNDLE already enabled for " << ch->content_name()
|
||||
<< " on " << transport_name << ".";
|
||||
return true;
|
||||
}
|
||||
|
||||
stats->transport_stats[transport_id] = tstats;
|
||||
if (!ch->SetTransport(transport_name)) {
|
||||
LOG(LS_WARNING) << "Failed to enable BUNDLE for " << ch->content_name();
|
||||
return false;
|
||||
}
|
||||
LOG(LS_INFO) << "Enabled BUNDLE for " << ch->content_name() << " on "
|
||||
<< transport_name << ".";
|
||||
return true;
|
||||
};
|
||||
|
||||
for (const auto& kv : transport_proxies()) {
|
||||
cricket::Transport* transport = kv.second->impl();
|
||||
if (transport && !get_transport_stats(kv.first, transport)) {
|
||||
return false;
|
||||
}
|
||||
if (!maybe_set_transport(voice_channel()) ||
|
||||
!maybe_set_transport(video_channel()) ||
|
||||
!maybe_set_transport(data_channel())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1401,13 +1466,18 @@ void WebRtcSession::ResetIceRestartLatch() {
|
||||
|
||||
void WebRtcSession::OnCertificateReady(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
|
||||
SetCertificate(certificate);
|
||||
transport_controller()->SetLocalCertificate(certificate);
|
||||
}
|
||||
|
||||
bool WebRtcSession::waiting_for_certificate_for_testing() const {
|
||||
return webrtc_session_desc_factory_->waiting_for_certificate_for_testing();
|
||||
}
|
||||
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>&
|
||||
WebRtcSession::certificate_for_testing() {
|
||||
return transport_controller()->certificate_for_testing();
|
||||
}
|
||||
|
||||
void WebRtcSession::SetIceConnectionState(
|
||||
PeerConnectionInterface::IceConnectionState state) {
|
||||
if (ice_connection_state_ == state) {
|
||||
@ -1418,6 +1488,8 @@ void WebRtcSession::SetIceConnectionState(
|
||||
// WebRtcSession does not implement "kIceConnectionClosed" (that is handled
|
||||
// within PeerConnection). This switch statement should compile away when
|
||||
// ASSERTs are disabled.
|
||||
LOG(LS_INFO) << "Changing IceConnectionState " << ice_connection_state_
|
||||
<< " => " << state;
|
||||
switch (ice_connection_state_) {
|
||||
case PeerConnectionInterface::kIceConnectionNew:
|
||||
ASSERT(state == PeerConnectionInterface::kIceConnectionChecking);
|
||||
@ -1458,70 +1530,52 @@ void WebRtcSession::SetIceConnectionState(
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcSession::OnTransportRequestSignaling(
|
||||
cricket::Transport* transport) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
transport->OnSignalingReady();
|
||||
if (ice_observer_) {
|
||||
ice_observer_->OnIceGatheringChange(
|
||||
PeerConnectionInterface::kIceGatheringGathering);
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
// start monitoring for the write state of the transport.
|
||||
OnTransportWritable(transport);
|
||||
}
|
||||
|
||||
void WebRtcSession::OnTransportWritable(cricket::Transport* transport) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
if (transport->all_channels_writable()) {
|
||||
SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
|
||||
} else if (transport->HasChannels()) {
|
||||
// If the current state is Connected or Completed, then there were writable
|
||||
// channels but now there are not, so the next state must be Disconnected.
|
||||
if (ice_connection_state_ ==
|
||||
PeerConnectionInterface::kIceConnectionConnected ||
|
||||
ice_connection_state_ ==
|
||||
PeerConnectionInterface::kIceConnectionCompleted) {
|
||||
SetIceConnectionState(
|
||||
PeerConnectionInterface::kIceConnectionDisconnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcSession::OnTransportCompleted(cricket::Transport* transport) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
PeerConnectionInterface::IceConnectionState old_state = ice_connection_state_;
|
||||
SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted);
|
||||
// Only report once when Ice connection is completed.
|
||||
if (old_state != PeerConnectionInterface::kIceConnectionCompleted) {
|
||||
cricket::TransportStats stats;
|
||||
if (metrics_observer_ && transport->GetStats(&stats)) {
|
||||
ReportBestConnectionState(stats);
|
||||
ReportNegotiatedCiphers(stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcSession::OnTransportFailed(cricket::Transport* transport) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed);
|
||||
}
|
||||
|
||||
void WebRtcSession::OnTransportReceiving(cricket::Transport* transport) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
// The ice connection is considered receiving if at least one transport is
|
||||
// receiving on any channels.
|
||||
bool receiving = false;
|
||||
for (const auto& kv : transport_proxies()) {
|
||||
cricket::Transport* transport = kv.second->impl();
|
||||
if (transport && transport->any_channel_receiving()) {
|
||||
receiving = true;
|
||||
void WebRtcSession::OnTransportControllerConnectionState(
|
||||
cricket::IceConnectionState state) {
|
||||
switch (state) {
|
||||
case cricket::kIceConnectionConnecting:
|
||||
// If the current state is Connected or Completed, then there were
|
||||
// writable channels but now there are not, so the next state must
|
||||
// be Disconnected.
|
||||
// kIceConnectionConnecting is currently used as the default,
|
||||
// un-connected state by the TransportController, so its only use is
|
||||
// detecting disconnections.
|
||||
if (ice_connection_state_ ==
|
||||
PeerConnectionInterface::kIceConnectionConnected ||
|
||||
ice_connection_state_ ==
|
||||
PeerConnectionInterface::kIceConnectionCompleted) {
|
||||
SetIceConnectionState(
|
||||
PeerConnectionInterface::kIceConnectionDisconnected);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cricket::kIceConnectionFailed:
|
||||
SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed);
|
||||
break;
|
||||
case cricket::kIceConnectionConnected:
|
||||
LOG(LS_INFO) << "Changing to ICE connected state because "
|
||||
<< "all transports are writable.";
|
||||
SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
|
||||
break;
|
||||
case cricket::kIceConnectionCompleted:
|
||||
LOG(LS_INFO) << "Changing to ICE completed state because "
|
||||
<< "all transports are complete.";
|
||||
if (ice_connection_state_ !=
|
||||
PeerConnectionInterface::kIceConnectionConnected) {
|
||||
// If jumping directly from "checking" to "connected",
|
||||
// signal "connected" first.
|
||||
SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
|
||||
}
|
||||
SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted);
|
||||
if (metrics_observer_) {
|
||||
ReportTransportStats();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcSession::OnTransportControllerReceiving(bool receiving) {
|
||||
SetIceConnectionReceiving(receiving);
|
||||
}
|
||||
|
||||
@ -1535,18 +1589,27 @@ void WebRtcSession::SetIceConnectionReceiving(bool receiving) {
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcSession::OnTransportProxyCandidatesReady(
|
||||
cricket::TransportProxy* proxy, const cricket::Candidates& candidates) {
|
||||
void WebRtcSession::OnTransportControllerCandidatesGathered(
|
||||
const std::string& transport_name,
|
||||
const cricket::Candidates& candidates) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
ProcessNewLocalCandidate(proxy->content_name(), candidates);
|
||||
}
|
||||
int sdp_mline_index;
|
||||
if (!GetLocalCandidateMediaIndex(transport_name, &sdp_mline_index)) {
|
||||
LOG(LS_ERROR) << "OnTransportControllerCandidatesGathered: content name "
|
||||
<< transport_name << " not found";
|
||||
return;
|
||||
}
|
||||
|
||||
void WebRtcSession::OnCandidatesAllocationDone() {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
if (ice_observer_) {
|
||||
ice_observer_->OnIceGatheringChange(
|
||||
PeerConnectionInterface::kIceGatheringComplete);
|
||||
ice_observer_->OnIceComplete();
|
||||
for (cricket::Candidates::const_iterator citer = candidates.begin();
|
||||
citer != candidates.end(); ++citer) {
|
||||
// Use transport_name as the candidate media id.
|
||||
JsepIceCandidate candidate(transport_name, sdp_mline_index, *citer);
|
||||
if (ice_observer_) {
|
||||
ice_observer_->OnIceCandidate(&candidate);
|
||||
}
|
||||
if (local_desc_) {
|
||||
local_desc_->AddCandidate(&candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1562,29 +1625,6 @@ void WebRtcSession::EnableChannels() {
|
||||
data_channel_->Enable(true);
|
||||
}
|
||||
|
||||
void WebRtcSession::ProcessNewLocalCandidate(
|
||||
const std::string& content_name,
|
||||
const cricket::Candidates& candidates) {
|
||||
int sdp_mline_index;
|
||||
if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) {
|
||||
LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name "
|
||||
<< content_name << " not found";
|
||||
return;
|
||||
}
|
||||
|
||||
for (cricket::Candidates::const_iterator citer = candidates.begin();
|
||||
citer != candidates.end(); ++citer) {
|
||||
// Use content_name as the candidate media id.
|
||||
JsepIceCandidate candidate(content_name, sdp_mline_index, *citer);
|
||||
if (ice_observer_) {
|
||||
ice_observer_->OnIceCandidate(&candidate);
|
||||
}
|
||||
if (local_desc_) {
|
||||
local_desc_->AddCandidate(&candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the media index for a local ice candidate given the content name.
|
||||
bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name,
|
||||
int* sdp_mline_index) {
|
||||
@ -1649,7 +1689,8 @@ bool WebRtcSession::UseCandidate(
|
||||
candidates.push_back(candidate->candidate());
|
||||
// Invoking BaseSession method to handle remote candidates.
|
||||
std::string error;
|
||||
if (OnRemoteCandidates(content.name, candidates, &error)) {
|
||||
if (transport_controller()->AddRemoteCandidates(content.name, candidates,
|
||||
&error)) {
|
||||
// Candidates successfully submitted for checking.
|
||||
if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew ||
|
||||
ice_connection_state_ ==
|
||||
@ -1673,8 +1714,7 @@ bool WebRtcSession::UseCandidate(
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebRtcSession::RemoveUnusedChannelsAndTransports(
|
||||
const SessionDescription* desc) {
|
||||
void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) {
|
||||
// Destroy video_channel_ first since it may have a pointer to the
|
||||
// voice_channel_.
|
||||
const cricket::ContentInfo* video_info =
|
||||
@ -1684,7 +1724,6 @@ void WebRtcSession::RemoveUnusedChannelsAndTransports(
|
||||
SignalVideoChannelDestroyed();
|
||||
const std::string content_name = video_channel_->content_name();
|
||||
channel_manager_->DestroyVideoChannel(video_channel_.release());
|
||||
DestroyTransportProxy(content_name);
|
||||
}
|
||||
|
||||
const cricket::ContentInfo* voice_info =
|
||||
@ -1694,7 +1733,6 @@ void WebRtcSession::RemoveUnusedChannelsAndTransports(
|
||||
SignalVoiceChannelDestroyed();
|
||||
const std::string content_name = voice_channel_->content_name();
|
||||
channel_manager_->DestroyVoiceChannel(voice_channel_.release());
|
||||
DestroyTransportProxy(content_name);
|
||||
}
|
||||
|
||||
const cricket::ContentInfo* data_info =
|
||||
@ -1704,7 +1742,6 @@ void WebRtcSession::RemoveUnusedChannelsAndTransports(
|
||||
SignalDataChannelDestroyed();
|
||||
const std::string content_name = data_channel_->content_name();
|
||||
channel_manager_->DestroyDataChannel(data_channel_.release());
|
||||
DestroyTransportProxy(content_name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1749,7 +1786,7 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
|
||||
}
|
||||
}
|
||||
|
||||
// Enable bundle before when kMaxBundle policy is in effect.
|
||||
// Enable BUNDLE immediately when kBundlePolicyMaxBundle is in effect.
|
||||
if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle) {
|
||||
const cricket::ContentGroup* bundle_group = desc->GetGroupByName(
|
||||
cricket::GROUP_TYPE_BUNDLE);
|
||||
@ -1757,7 +1794,7 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
|
||||
LOG(LS_WARNING) << "max-bundle specified without BUNDLE specified";
|
||||
return false;
|
||||
}
|
||||
if (!BaseSession::BundleContentGroup(bundle_group)) {
|
||||
if (!EnableBundle(*bundle_group)) {
|
||||
LOG(LS_WARNING) << "max-bundle failed to enable bundling.";
|
||||
return false;
|
||||
}
|
||||
@ -1768,7 +1805,8 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
|
||||
|
||||
bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
|
||||
voice_channel_.reset(channel_manager_->CreateVoiceChannel(
|
||||
media_controller_.get(), this, content->name, true, audio_options_));
|
||||
media_controller_.get(), transport_controller(), content->name, true,
|
||||
audio_options_));
|
||||
if (!voice_channel_) {
|
||||
return false;
|
||||
}
|
||||
@ -1780,7 +1818,8 @@ bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
|
||||
|
||||
bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
|
||||
video_channel_.reset(channel_manager_->CreateVideoChannel(
|
||||
media_controller_.get(), this, content->name, true, video_options_));
|
||||
media_controller_.get(), transport_controller(), content->name, true,
|
||||
video_options_));
|
||||
if (!video_channel_) {
|
||||
return false;
|
||||
}
|
||||
@ -1793,7 +1832,7 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
|
||||
bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
|
||||
bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
|
||||
data_channel_.reset(channel_manager_->CreateDataChannel(
|
||||
this, content->name, !sctp, data_channel_type_));
|
||||
transport_controller(), content->name, !sctp, data_channel_type_));
|
||||
if (!data_channel_) {
|
||||
return false;
|
||||
}
|
||||
@ -1974,7 +2013,6 @@ bool WebRtcSession::ReadyToUseRemoteCandidate(
|
||||
const SessionDescriptionInterface* remote_desc,
|
||||
bool* valid) {
|
||||
*valid = true;;
|
||||
cricket::TransportProxy* transport_proxy = NULL;
|
||||
|
||||
const SessionDescriptionInterface* current_remote_desc =
|
||||
remote_desc ? remote_desc : remote_description();
|
||||
@ -1996,12 +2034,53 @@ bool WebRtcSession::ReadyToUseRemoteCandidate(
|
||||
|
||||
cricket::ContentInfo content =
|
||||
current_remote_desc->description()->contents()[mediacontent_index];
|
||||
transport_proxy = GetTransportProxy(content.name);
|
||||
cricket::BaseChannel* channel = GetChannel(content.name);
|
||||
if (!channel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return transport_proxy && transport_proxy->local_description_set() &&
|
||||
transport_proxy->remote_description_set();
|
||||
return transport_controller()->ReadyForRemoteCandidates(
|
||||
channel->transport_name());
|
||||
}
|
||||
|
||||
void WebRtcSession::OnTransportControllerGatheringState(
|
||||
cricket::IceGatheringState state) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
if (state == cricket::kIceGatheringGathering) {
|
||||
if (ice_observer_) {
|
||||
ice_observer_->OnIceGatheringChange(
|
||||
PeerConnectionInterface::kIceGatheringGathering);
|
||||
}
|
||||
} else if (state == cricket::kIceGatheringComplete) {
|
||||
if (ice_observer_) {
|
||||
ice_observer_->OnIceGatheringChange(
|
||||
PeerConnectionInterface::kIceGatheringComplete);
|
||||
ice_observer_->OnIceComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcSession::ReportTransportStats() {
|
||||
// Use a set so we don't report the same stats twice if two channels share
|
||||
// a transport.
|
||||
std::set<std::string> transport_names;
|
||||
if (voice_channel()) {
|
||||
transport_names.insert(voice_channel()->transport_name());
|
||||
}
|
||||
if (video_channel()) {
|
||||
transport_names.insert(video_channel()->transport_name());
|
||||
}
|
||||
if (data_channel()) {
|
||||
transport_names.insert(data_channel()->transport_name());
|
||||
}
|
||||
for (const auto& name : transport_names) {
|
||||
cricket::TransportStats stats;
|
||||
if (transport_controller()->GetStats(name, &stats)) {
|
||||
ReportBestConnectionState(stats);
|
||||
ReportNegotiatedCiphers(stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Walk through the ConnectionInfos to gather best connection usage
|
||||
// for IPv4 and IPv6.
|
||||
void WebRtcSession::ReportBestConnectionState(
|
||||
@ -2069,13 +2148,13 @@ void WebRtcSession::ReportNegotiatedCiphers(
|
||||
|
||||
PeerConnectionMetricsName srtp_name;
|
||||
PeerConnectionMetricsName ssl_name;
|
||||
if (stats.content_name == cricket::CN_AUDIO) {
|
||||
if (stats.transport_name == cricket::CN_AUDIO) {
|
||||
srtp_name = kAudioSrtpCipher;
|
||||
ssl_name = kAudioSslCipher;
|
||||
} else if (stats.content_name == cricket::CN_VIDEO) {
|
||||
} else if (stats.transport_name == cricket::CN_VIDEO) {
|
||||
srtp_name = kVideoSrtpCipher;
|
||||
ssl_name = kVideoSslCipher;
|
||||
} else if (stats.content_name == cricket::CN_DATA) {
|
||||
} else if (stats.transport_name == cricket::CN_DATA) {
|
||||
srtp_name = kDataSrtpCipher;
|
||||
ssl_name = kDataSslCipher;
|
||||
} else {
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#define TALK_APP_WEBRTC_WEBRTCSESSION_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "talk/app/webrtc/datachannel.h"
|
||||
#include "talk/app/webrtc/dtmfsender.h"
|
||||
@ -49,7 +50,6 @@ class BaseChannel;
|
||||
class ChannelManager;
|
||||
class DataChannel;
|
||||
class StatsReport;
|
||||
class Transport;
|
||||
class VideoCapturer;
|
||||
class VideoChannel;
|
||||
class VoiceChannel;
|
||||
@ -77,6 +77,8 @@ extern const char kSessionError[];
|
||||
extern const char kSessionErrorDesc[];
|
||||
extern const char kDtlsSetupFailureRtp[];
|
||||
extern const char kDtlsSetupFailureRtcp[];
|
||||
extern const char kEnableBundleFailed[];
|
||||
|
||||
// Maximum number of received video streams that will be processed by webrtc
|
||||
// even if they are not signalled beforehand.
|
||||
extern const int kMaxUnsignalledRecvStreams;
|
||||
@ -235,6 +237,19 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
// This avoids exposing the internal structures used to track them.
|
||||
virtual bool GetTransportStats(cricket::SessionStats* stats);
|
||||
|
||||
// Get stats for a specific channel
|
||||
bool GetChannelTransportStats(cricket::BaseChannel* ch,
|
||||
cricket::SessionStats* stats);
|
||||
|
||||
// virtual so it can be mocked in unit tests
|
||||
virtual bool GetLocalCertificate(
|
||||
const std::string& transport_name,
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate);
|
||||
|
||||
// Caller owns returned certificate
|
||||
virtual bool GetRemoteSSLCertificate(const std::string& transport_name,
|
||||
rtc::SSLCertificate** cert);
|
||||
|
||||
// Implements DataChannelFactory.
|
||||
rtc::scoped_refptr<DataChannel> CreateDataChannel(
|
||||
const std::string& label,
|
||||
@ -254,6 +269,7 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
|
||||
// For unit test.
|
||||
bool waiting_for_certificate_for_testing() const;
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate_for_testing();
|
||||
|
||||
void set_metrics_observer(
|
||||
webrtc::MetricsObserverInterface* metrics_observer) {
|
||||
@ -269,9 +285,6 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
kAnswer,
|
||||
};
|
||||
|
||||
// Invokes ConnectChannels() on transport proxies, which initiates ice
|
||||
// candidates allocation.
|
||||
bool StartCandidatesAllocation();
|
||||
bool UpdateSessionState(Action action, cricket::ContentSource source,
|
||||
std::string* err_desc);
|
||||
static Action GetAction(const std::string& type);
|
||||
@ -281,25 +294,13 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
cricket::ContentSource source,
|
||||
std::string* error_desc);
|
||||
|
||||
|
||||
// Transport related callbacks, override from cricket::BaseSession.
|
||||
virtual void OnTransportRequestSignaling(cricket::Transport* transport);
|
||||
virtual void OnTransportConnecting(cricket::Transport* transport);
|
||||
virtual void OnTransportWritable(cricket::Transport* transport);
|
||||
virtual void OnTransportCompleted(cricket::Transport* transport);
|
||||
virtual void OnTransportFailed(cricket::Transport* transport);
|
||||
virtual void OnTransportProxyCandidatesReady(
|
||||
cricket::TransportProxy* proxy,
|
||||
const cricket::Candidates& candidates);
|
||||
virtual void OnCandidatesAllocationDone();
|
||||
void OnTransportReceiving(cricket::Transport* transport) override;
|
||||
cricket::BaseChannel* GetChannel(const std::string& content_name);
|
||||
// Cause all the BaseChannels in the bundle group to have the same
|
||||
// transport channel.
|
||||
bool EnableBundle(const cricket::ContentGroup& bundle);
|
||||
|
||||
// Enables media channels to allow sending of media.
|
||||
void EnableChannels();
|
||||
// Creates a JsepIceCandidate and adds it to the local session description
|
||||
// and notify observers. Called when a new local candidate have been found.
|
||||
void ProcessNewLocalCandidate(const std::string& content_name,
|
||||
const cricket::Candidates& candidates);
|
||||
// Returns the media index for a local ice candidate given the content name.
|
||||
// Returns false if the local session description does not have a media
|
||||
// content called |content_name|.
|
||||
@ -312,8 +313,7 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
bool UseCandidate(const IceCandidateInterface* candidate);
|
||||
// Deletes the corresponding channel of contents that don't exist in |desc|.
|
||||
// |desc| can be null. This means that all channels are deleted.
|
||||
void RemoveUnusedChannelsAndTransports(
|
||||
const cricket::SessionDescription* desc);
|
||||
void RemoveUnusedChannels(const cricket::SessionDescription* desc);
|
||||
|
||||
// Allocates media channels based on the |desc|. If |desc| doesn't have
|
||||
// the BUNDLE option, this method will disable BUNDLE in PortAllocator.
|
||||
@ -362,10 +362,20 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
const SessionDescriptionInterface* remote_desc,
|
||||
bool* valid);
|
||||
|
||||
void OnTransportControllerConnectionState(cricket::IceConnectionState state);
|
||||
void OnTransportControllerReceiving(bool receiving);
|
||||
void OnTransportControllerGatheringState(cricket::IceGatheringState state);
|
||||
void OnTransportControllerCandidatesGathered(
|
||||
const std::string& transport_name,
|
||||
const cricket::Candidates& candidates);
|
||||
|
||||
std::string GetSessionErrorMsg();
|
||||
|
||||
// Invoked when OnTransportCompleted is signaled to gather the usage
|
||||
// of IPv4/IPv6 as best connection.
|
||||
// Invoked when TransportController connection completion is signaled.
|
||||
// Reports stats for all transports in use.
|
||||
void ReportTransportStats();
|
||||
|
||||
// Gather the usage of IPv4/IPv6 as best connection.
|
||||
void ReportBestConnectionState(const cricket::TransportStats& stats);
|
||||
|
||||
void ReportNegotiatedCiphers(const cricket::TransportStats& stats);
|
||||
|
||||
@ -25,6 +25,8 @@
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "talk/app/webrtc/audiotrack.h"
|
||||
#include "talk/app/webrtc/fakemetricsobserver.h"
|
||||
#include "talk/app/webrtc/jsepicecandidate.h"
|
||||
@ -163,8 +165,8 @@ static void InjectAfter(const std::string& line,
|
||||
const std::string& newlines,
|
||||
std::string* message) {
|
||||
const std::string tmp = line + newlines;
|
||||
rtc::replace_substrs(line.c_str(), line.length(),
|
||||
tmp.c_str(), tmp.length(), message);
|
||||
rtc::replace_substrs(line.c_str(), line.length(), tmp.c_str(), tmp.length(),
|
||||
message);
|
||||
}
|
||||
|
||||
class MockIceObserver : public webrtc::IceObserver {
|
||||
@ -244,12 +246,52 @@ class WebRtcSessionForTest : public webrtc::WebRtcSession {
|
||||
}
|
||||
virtual ~WebRtcSessionForTest() {}
|
||||
|
||||
using cricket::BaseSession::GetTransportProxy;
|
||||
// Note that these methods are only safe to use if the signaling thread
|
||||
// is the same as the worker thread
|
||||
cricket::TransportChannel* voice_rtp_transport_channel() {
|
||||
return rtp_transport_channel(voice_channel());
|
||||
}
|
||||
|
||||
cricket::TransportChannel* voice_rtcp_transport_channel() {
|
||||
return rtcp_transport_channel(voice_channel());
|
||||
}
|
||||
|
||||
cricket::TransportChannel* video_rtp_transport_channel() {
|
||||
return rtp_transport_channel(video_channel());
|
||||
}
|
||||
|
||||
cricket::TransportChannel* video_rtcp_transport_channel() {
|
||||
return rtcp_transport_channel(video_channel());
|
||||
}
|
||||
|
||||
cricket::TransportChannel* data_rtp_transport_channel() {
|
||||
return rtp_transport_channel(data_channel());
|
||||
}
|
||||
|
||||
cricket::TransportChannel* data_rtcp_transport_channel() {
|
||||
return rtcp_transport_channel(data_channel());
|
||||
}
|
||||
|
||||
using webrtc::WebRtcSession::SetAudioPlayout;
|
||||
using webrtc::WebRtcSession::SetAudioSend;
|
||||
using webrtc::WebRtcSession::SetCaptureDevice;
|
||||
using webrtc::WebRtcSession::SetVideoPlayout;
|
||||
using webrtc::WebRtcSession::SetVideoSend;
|
||||
|
||||
private:
|
||||
cricket::TransportChannel* rtp_transport_channel(cricket::BaseChannel* ch) {
|
||||
if (!ch) {
|
||||
return nullptr;
|
||||
}
|
||||
return ch->transport_channel();
|
||||
}
|
||||
|
||||
cricket::TransportChannel* rtcp_transport_channel(cricket::BaseChannel* ch) {
|
||||
if (!ch) {
|
||||
return nullptr;
|
||||
}
|
||||
return ch->rtcp_transport_channel();
|
||||
}
|
||||
};
|
||||
|
||||
class WebRtcSessionCreateSDPObserverForTest
|
||||
@ -375,9 +417,9 @@ class WebRtcSessionTest
|
||||
EXPECT_EQ(PeerConnectionInterface::kIceGatheringNew,
|
||||
observer_.ice_gathering_state_);
|
||||
|
||||
EXPECT_TRUE(session_->Initialize(
|
||||
options_, constraints_.get(), dtls_identity_store.Pass(),
|
||||
rtc_configuration));
|
||||
EXPECT_TRUE(session_->Initialize(options_, constraints_.get(),
|
||||
dtls_identity_store.Pass(),
|
||||
rtc_configuration));
|
||||
session_->set_metrics_observer(metrics_observer_);
|
||||
}
|
||||
|
||||
@ -490,13 +532,6 @@ class WebRtcSessionTest
|
||||
session_->video_channel() != NULL);
|
||||
}
|
||||
|
||||
void CheckTransportChannels() const {
|
||||
EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, 1) != NULL);
|
||||
EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, 2) != NULL);
|
||||
EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, 1) != NULL);
|
||||
EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, 2) != NULL);
|
||||
}
|
||||
|
||||
void VerifyCryptoParams(const cricket::SessionDescription* sdp) {
|
||||
ASSERT_TRUE(session_.get() != NULL);
|
||||
const cricket::ContentInfo* content = cricket::GetFirstAudioContent(sdp);
|
||||
@ -722,6 +757,7 @@ class WebRtcSessionTest
|
||||
}
|
||||
void SetLocalDescriptionWithoutError(SessionDescriptionInterface* desc) {
|
||||
EXPECT_TRUE(session_->SetLocalDescription(desc, NULL));
|
||||
session_->MaybeStartGathering();
|
||||
}
|
||||
void SetLocalDescriptionExpectState(SessionDescriptionInterface* desc,
|
||||
BaseSession::State expected_state) {
|
||||
@ -968,15 +1004,10 @@ class WebRtcSessionTest
|
||||
SetRemoteDescriptionWithoutError(new_answer);
|
||||
EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
|
||||
EXPECT_EQ(expected_candidate_num, observer_.mline_0_candidates_.size());
|
||||
EXPECT_EQ(expected_candidate_num, observer_.mline_1_candidates_.size());
|
||||
for (size_t i = 0; i < observer_.mline_0_candidates_.size(); ++i) {
|
||||
cricket::Candidate c0 = observer_.mline_0_candidates_[i];
|
||||
cricket::Candidate c1 = observer_.mline_1_candidates_[i];
|
||||
if (bundle) {
|
||||
EXPECT_TRUE(c0.IsEquivalent(c1));
|
||||
} else {
|
||||
EXPECT_FALSE(c0.IsEquivalent(c1));
|
||||
}
|
||||
if (bundle) {
|
||||
EXPECT_EQ(0, observer_.mline_1_candidates_.size());
|
||||
} else {
|
||||
EXPECT_EQ(expected_candidate_num, observer_.mline_1_candidates_.size());
|
||||
}
|
||||
}
|
||||
// Tests that we can only send DTMF when the dtmf codec is supported.
|
||||
@ -1001,7 +1032,7 @@ class WebRtcSessionTest
|
||||
// initial ICE convergences.
|
||||
|
||||
class LoopbackNetworkConfiguration {
|
||||
public:
|
||||
public:
|
||||
LoopbackNetworkConfiguration()
|
||||
: test_ipv6_network_(false),
|
||||
test_extra_ipv4_network_(false),
|
||||
@ -1150,11 +1181,8 @@ class WebRtcSessionTest
|
||||
|
||||
// Clearing the rules, session should move back to completed state.
|
||||
loopback_network_manager.ClearRules(fss_.get());
|
||||
// Session is automatically calling OnSignalingReady after creation of
|
||||
// new portallocator session which will allocate new set of candidates.
|
||||
|
||||
LOG(LS_INFO) << "Firewall Rules cleared";
|
||||
|
||||
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
|
||||
observer_.ice_connection_state_,
|
||||
kIceCandidatesTimeout);
|
||||
@ -1707,15 +1735,14 @@ TEST_P(WebRtcSessionTest, TestSetLocalNonDtlsAnswerWhenDtlsOn) {
|
||||
// a DTLS fingerprint when DTLS is required.
|
||||
TEST_P(WebRtcSessionTest, TestSetRemoteNonDtlsAnswerWhenDtlsOn) {
|
||||
MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
|
||||
// Enable both SDES and DTLS, so that offer won't be outright rejected as a
|
||||
// result of using the "UDP/TLS/RTP/SAVPF" profile.
|
||||
InitWithDtls(GetParam());
|
||||
session_->SetSdesPolicy(cricket::SEC_ENABLED);
|
||||
SessionDescriptionInterface* offer = CreateOffer();
|
||||
cricket::MediaSessionOptions options;
|
||||
options.recv_video = true;
|
||||
rtc::scoped_ptr<SessionDescriptionInterface> temp_offer(
|
||||
CreateRemoteOffer(options, cricket::SEC_ENABLED));
|
||||
JsepSessionDescription* answer =
|
||||
CreateRemoteAnswer(offer, options, cricket::SEC_ENABLED);
|
||||
CreateRemoteAnswer(temp_offer.get(), options, cricket::SEC_ENABLED);
|
||||
|
||||
// SetRemoteDescription and SetLocalDescription will take the ownership of
|
||||
// the offer and answer.
|
||||
@ -2017,7 +2044,7 @@ TEST_F(WebRtcSessionTest, TestLocalCandidatesAddedToSessionDescription) {
|
||||
EXPECT_LT(0u, candidates->count());
|
||||
candidates = local_desc->candidates(1);
|
||||
ASSERT_TRUE(candidates != NULL);
|
||||
EXPECT_LT(0u, candidates->count());
|
||||
EXPECT_EQ(0u, candidates->count());
|
||||
|
||||
// Update the session descriptions.
|
||||
mediastream_signaling_.SendAudioVideoStream1();
|
||||
@ -2029,7 +2056,7 @@ TEST_F(WebRtcSessionTest, TestLocalCandidatesAddedToSessionDescription) {
|
||||
EXPECT_LT(0u, candidates->count());
|
||||
candidates = local_desc->candidates(1);
|
||||
ASSERT_TRUE(candidates != NULL);
|
||||
EXPECT_LT(0u, candidates->count());
|
||||
EXPECT_EQ(0u, candidates->count());
|
||||
}
|
||||
|
||||
// Test that we can set a remote session description with remote candidates.
|
||||
@ -2073,23 +2100,17 @@ TEST_F(WebRtcSessionTest, TestSetLocalAndRemoteDescriptionWithCandidates) {
|
||||
// Wait until at least one local candidate has been collected.
|
||||
EXPECT_TRUE_WAIT(0u < observer_.mline_0_candidates_.size(),
|
||||
kIceCandidatesTimeout);
|
||||
EXPECT_TRUE_WAIT(0u < observer_.mline_1_candidates_.size(),
|
||||
kIceCandidatesTimeout);
|
||||
|
||||
rtc::scoped_ptr<SessionDescriptionInterface> local_offer(CreateOffer());
|
||||
|
||||
ASSERT_TRUE(local_offer->candidates(kMediaContentIndex0) != NULL);
|
||||
EXPECT_LT(0u, local_offer->candidates(kMediaContentIndex0)->count());
|
||||
ASSERT_TRUE(local_offer->candidates(kMediaContentIndex1) != NULL);
|
||||
EXPECT_LT(0u, local_offer->candidates(kMediaContentIndex1)->count());
|
||||
|
||||
SessionDescriptionInterface* remote_offer(CreateRemoteOffer());
|
||||
SetRemoteDescriptionWithoutError(remote_offer);
|
||||
SessionDescriptionInterface* answer = CreateAnswer(NULL);
|
||||
ASSERT_TRUE(answer->candidates(kMediaContentIndex0) != NULL);
|
||||
EXPECT_LT(0u, answer->candidates(kMediaContentIndex0)->count());
|
||||
ASSERT_TRUE(answer->candidates(kMediaContentIndex1) != NULL);
|
||||
EXPECT_LT(0u, answer->candidates(kMediaContentIndex1)->count());
|
||||
SetLocalDescriptionWithoutError(answer);
|
||||
}
|
||||
|
||||
@ -2131,8 +2152,14 @@ TEST_F(WebRtcSessionTest, TestChannelCreationsWithContentNames) {
|
||||
CreateAnswer(NULL);
|
||||
SetLocalDescriptionWithoutError(answer);
|
||||
|
||||
EXPECT_TRUE(session_->GetTransportProxy("audio_content_name") != NULL);
|
||||
EXPECT_TRUE(session_->GetTransportProxy("video_content_name") != NULL);
|
||||
cricket::TransportChannel* voice_transport_channel =
|
||||
session_->voice_rtp_transport_channel();
|
||||
EXPECT_TRUE(voice_transport_channel != NULL);
|
||||
EXPECT_EQ(voice_transport_channel->transport_name(), "audio_content_name");
|
||||
cricket::TransportChannel* video_transport_channel =
|
||||
session_->video_rtp_transport_channel();
|
||||
EXPECT_TRUE(video_transport_channel != NULL);
|
||||
EXPECT_EQ(video_transport_channel->transport_name(), "video_content_name");
|
||||
EXPECT_TRUE((video_channel_ = media_engine_->GetVideoChannel(0)) != NULL);
|
||||
EXPECT_TRUE((voice_channel_ = media_engine_->GetVoiceChannel(0)) != NULL);
|
||||
}
|
||||
@ -2692,20 +2719,23 @@ TEST_F(WebRtcSessionTest, TestIgnoreCandidatesForUnusedTransportWhenBundling) {
|
||||
SessionDescriptionInterface* answer = CreateAnswer(NULL);
|
||||
SetLocalDescriptionWithoutError(answer);
|
||||
|
||||
EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_EQ(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
|
||||
cricket::Transport* t = session_->GetTransport("audio");
|
||||
cricket::BaseChannel* voice_channel = session_->voice_channel();
|
||||
ASSERT(voice_channel != NULL);
|
||||
|
||||
// Checks if one of the transport channels contains a connection using a given
|
||||
// port.
|
||||
auto connection_with_remote_port = [t](int port) {
|
||||
cricket::TransportStats stats;
|
||||
t->GetStats(&stats);
|
||||
for (auto& chan_stat : stats.channel_stats) {
|
||||
for (auto& conn_info : chan_stat.connection_infos) {
|
||||
if (conn_info.remote_candidate.address().port() == port) {
|
||||
return true;
|
||||
auto connection_with_remote_port = [this, voice_channel](int port) {
|
||||
cricket::SessionStats stats;
|
||||
session_->GetChannelTransportStats(voice_channel, &stats);
|
||||
for (auto& kv : stats.transport_stats) {
|
||||
for (auto& chan_stat : kv.second.channel_stats) {
|
||||
for (auto& conn_info : chan_stat.connection_infos) {
|
||||
if (conn_info.remote_candidate.address().port() == port) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2758,7 +2788,7 @@ TEST_F(WebRtcSessionTest, TestIgnoreCandidatesForUnusedTransportWhenBundling) {
|
||||
EXPECT_FALSE(connection_with_remote_port(6000));
|
||||
}
|
||||
|
||||
// kBundlePolicyBalanced bundle policy and answer contains BUNDLE.
|
||||
// kBundlePolicyBalanced BUNDLE policy and answer contains BUNDLE.
|
||||
TEST_F(WebRtcSessionTest, TestBalancedBundleInAnswer) {
|
||||
InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyBalanced);
|
||||
mediastream_signaling_.SendAudioVideoStream1();
|
||||
@ -2769,19 +2799,19 @@ TEST_F(WebRtcSessionTest, TestBalancedBundleInAnswer) {
|
||||
SessionDescriptionInterface* offer = CreateOffer(options);
|
||||
SetLocalDescriptionWithoutError(offer);
|
||||
|
||||
EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_NE(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
|
||||
mediastream_signaling_.SendAudioVideoStream2();
|
||||
SessionDescriptionInterface* answer =
|
||||
CreateRemoteAnswer(session_->local_description());
|
||||
SetRemoteDescriptionWithoutError(answer);
|
||||
|
||||
EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_EQ(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
}
|
||||
|
||||
// kBundlePolicyBalanced bundle policy but no BUNDLE in the answer.
|
||||
// kBundlePolicyBalanced BUNDLE policy but no BUNDLE in the answer.
|
||||
TEST_F(WebRtcSessionTest, TestBalancedNoBundleInAnswer) {
|
||||
InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyBalanced);
|
||||
mediastream_signaling_.SendAudioVideoStream1();
|
||||
@ -2792,8 +2822,8 @@ TEST_F(WebRtcSessionTest, TestBalancedNoBundleInAnswer) {
|
||||
SessionDescriptionInterface* offer = CreateOffer(options);
|
||||
SetLocalDescriptionWithoutError(offer);
|
||||
|
||||
EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_NE(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
|
||||
mediastream_signaling_.SendAudioVideoStream2();
|
||||
|
||||
@ -2807,8 +2837,8 @@ TEST_F(WebRtcSessionTest, TestBalancedNoBundleInAnswer) {
|
||||
modified_answer->Initialize(answer_copy, "1", "1");
|
||||
SetRemoteDescriptionWithoutError(modified_answer); //
|
||||
|
||||
EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_NE(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
}
|
||||
|
||||
// kBundlePolicyMaxBundle policy with BUNDLE in the answer.
|
||||
@ -2822,16 +2852,49 @@ TEST_F(WebRtcSessionTest, TestMaxBundleBundleInAnswer) {
|
||||
SessionDescriptionInterface* offer = CreateOffer(options);
|
||||
SetLocalDescriptionWithoutError(offer);
|
||||
|
||||
EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_EQ(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
|
||||
mediastream_signaling_.SendAudioVideoStream2();
|
||||
SessionDescriptionInterface* answer =
|
||||
CreateRemoteAnswer(session_->local_description());
|
||||
SetRemoteDescriptionWithoutError(answer);
|
||||
|
||||
EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_EQ(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
}
|
||||
|
||||
// kBundlePolicyMaxBundle policy with BUNDLE in the answer, but no
|
||||
// audio content in the answer.
|
||||
TEST_F(WebRtcSessionTest, TestMaxBundleRejectAudio) {
|
||||
InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
|
||||
mediastream_signaling_.SendAudioVideoStream1();
|
||||
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions options;
|
||||
options.use_rtp_mux = true;
|
||||
|
||||
SessionDescriptionInterface* offer = CreateOffer(options);
|
||||
SetLocalDescriptionWithoutError(offer);
|
||||
|
||||
EXPECT_EQ(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
|
||||
mediastream_signaling_.SendAudioVideoStream2();
|
||||
cricket::MediaSessionOptions recv_options;
|
||||
recv_options.recv_audio = false;
|
||||
recv_options.recv_video = true;
|
||||
SessionDescriptionInterface* answer =
|
||||
CreateRemoteAnswer(session_->local_description(), recv_options);
|
||||
SetRemoteDescriptionWithoutError(answer);
|
||||
|
||||
EXPECT_TRUE(NULL == session_->voice_channel());
|
||||
EXPECT_TRUE(NULL != session_->video_rtp_transport_channel());
|
||||
|
||||
session_->Terminate();
|
||||
EXPECT_TRUE(NULL == session_->voice_rtp_transport_channel());
|
||||
EXPECT_TRUE(NULL == session_->voice_rtcp_transport_channel());
|
||||
EXPECT_TRUE(NULL == session_->video_rtp_transport_channel());
|
||||
EXPECT_TRUE(NULL == session_->video_rtcp_transport_channel());
|
||||
}
|
||||
|
||||
// kBundlePolicyMaxBundle policy but no BUNDLE in the answer.
|
||||
@ -2845,8 +2908,8 @@ TEST_F(WebRtcSessionTest, TestMaxBundleNoBundleInAnswer) {
|
||||
SessionDescriptionInterface* offer = CreateOffer(options);
|
||||
SetLocalDescriptionWithoutError(offer);
|
||||
|
||||
EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_EQ(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
|
||||
mediastream_signaling_.SendAudioVideoStream2();
|
||||
|
||||
@ -2860,8 +2923,45 @@ TEST_F(WebRtcSessionTest, TestMaxBundleNoBundleInAnswer) {
|
||||
modified_answer->Initialize(answer_copy, "1", "1");
|
||||
SetRemoteDescriptionWithoutError(modified_answer);
|
||||
|
||||
EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_EQ(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
}
|
||||
|
||||
// kBundlePolicyMaxBundle policy with BUNDLE in the remote offer.
|
||||
TEST_F(WebRtcSessionTest, TestMaxBundleBundleInRemoteOffer) {
|
||||
InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
|
||||
mediastream_signaling_.SendAudioVideoStream1();
|
||||
|
||||
SessionDescriptionInterface* offer = CreateRemoteOffer();
|
||||
SetRemoteDescriptionWithoutError(offer);
|
||||
|
||||
EXPECT_EQ(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
|
||||
mediastream_signaling_.SendAudioVideoStream2();
|
||||
SessionDescriptionInterface* answer = CreateAnswer(nullptr);
|
||||
SetLocalDescriptionWithoutError(answer);
|
||||
|
||||
EXPECT_EQ(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
}
|
||||
|
||||
// kBundlePolicyMaxBundle policy but no BUNDLE in the remote offer.
|
||||
TEST_F(WebRtcSessionTest, TestMaxBundleNoBundleInRemoteOffer) {
|
||||
InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
|
||||
mediastream_signaling_.SendAudioVideoStream1();
|
||||
|
||||
// Remove BUNDLE from the offer.
|
||||
rtc::scoped_ptr<SessionDescriptionInterface> offer(CreateRemoteOffer());
|
||||
cricket::SessionDescription* offer_copy = offer->description()->Copy();
|
||||
offer_copy->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
|
||||
JsepSessionDescription* modified_offer =
|
||||
new JsepSessionDescription(JsepSessionDescription::kOffer);
|
||||
modified_offer->Initialize(offer_copy, "1", "1");
|
||||
|
||||
// Expect an error when applying the remote description
|
||||
SetRemoteDescriptionExpectError(JsepSessionDescription::kOffer,
|
||||
kCreateChannelFailed, modified_offer);
|
||||
}
|
||||
|
||||
// kBundlePolicyMaxCompat bundle policy and answer contains BUNDLE.
|
||||
@ -2875,8 +2975,8 @@ TEST_F(WebRtcSessionTest, TestMaxCompatBundleInAnswer) {
|
||||
SessionDescriptionInterface* offer = CreateOffer(options);
|
||||
SetLocalDescriptionWithoutError(offer);
|
||||
|
||||
EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_NE(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
|
||||
mediastream_signaling_.SendAudioVideoStream2();
|
||||
SessionDescriptionInterface* answer =
|
||||
@ -2885,11 +2985,11 @@ TEST_F(WebRtcSessionTest, TestMaxCompatBundleInAnswer) {
|
||||
|
||||
// This should lead to an audio-only call but isn't implemented
|
||||
// correctly yet.
|
||||
EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_EQ(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
}
|
||||
|
||||
// kBundlePolicyMaxCompat bundle policy but no BUNDLE in the answer.
|
||||
// kBundlePolicyMaxCompat BUNDLE policy but no BUNDLE in the answer.
|
||||
TEST_F(WebRtcSessionTest, TestMaxCompatNoBundleInAnswer) {
|
||||
InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxCompat);
|
||||
mediastream_signaling_.SendAudioVideoStream1();
|
||||
@ -2899,8 +2999,8 @@ TEST_F(WebRtcSessionTest, TestMaxCompatNoBundleInAnswer) {
|
||||
SessionDescriptionInterface* offer = CreateOffer(options);
|
||||
SetLocalDescriptionWithoutError(offer);
|
||||
|
||||
EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_NE(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
|
||||
mediastream_signaling_.SendAudioVideoStream2();
|
||||
|
||||
@ -2914,8 +3014,8 @@ TEST_F(WebRtcSessionTest, TestMaxCompatNoBundleInAnswer) {
|
||||
modified_answer->Initialize(answer_copy, "1", "1");
|
||||
SetRemoteDescriptionWithoutError(modified_answer); //
|
||||
|
||||
EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_NE(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
}
|
||||
|
||||
// kBundlePolicyMaxbundle and then we call SetRemoteDescription first.
|
||||
@ -2929,8 +3029,8 @@ TEST_F(WebRtcSessionTest, TestMaxBundleWithSetRemoteDescriptionFirst) {
|
||||
SessionDescriptionInterface* offer = CreateOffer(options);
|
||||
SetRemoteDescriptionWithoutError(offer);
|
||||
|
||||
EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
|
||||
session_->GetTransportProxy("video")->impl());
|
||||
EXPECT_EQ(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSessionTest, TestRequireRtcpMux) {
|
||||
@ -2941,16 +3041,16 @@ TEST_F(WebRtcSessionTest, TestRequireRtcpMux) {
|
||||
SessionDescriptionInterface* offer = CreateOffer(options);
|
||||
SetLocalDescriptionWithoutError(offer);
|
||||
|
||||
EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2));
|
||||
EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2));
|
||||
EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL);
|
||||
EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL);
|
||||
|
||||
mediastream_signaling_.SendAudioVideoStream2();
|
||||
SessionDescriptionInterface* answer =
|
||||
CreateRemoteAnswer(session_->local_description());
|
||||
SetRemoteDescriptionWithoutError(answer);
|
||||
|
||||
EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2));
|
||||
EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2));
|
||||
EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL);
|
||||
EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSessionTest, TestNegotiateRtcpMux) {
|
||||
@ -2961,16 +3061,16 @@ TEST_F(WebRtcSessionTest, TestNegotiateRtcpMux) {
|
||||
SessionDescriptionInterface* offer = CreateOffer(options);
|
||||
SetLocalDescriptionWithoutError(offer);
|
||||
|
||||
EXPECT_TRUE(session_->GetTransportProxy("audio")->impl()->HasChannel(2));
|
||||
EXPECT_TRUE(session_->GetTransportProxy("video")->impl()->HasChannel(2));
|
||||
EXPECT_TRUE(session_->voice_rtcp_transport_channel() != NULL);
|
||||
EXPECT_TRUE(session_->video_rtcp_transport_channel() != NULL);
|
||||
|
||||
mediastream_signaling_.SendAudioVideoStream2();
|
||||
SessionDescriptionInterface* answer =
|
||||
CreateRemoteAnswer(session_->local_description());
|
||||
SetRemoteDescriptionWithoutError(answer);
|
||||
|
||||
EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2));
|
||||
EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2));
|
||||
EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL);
|
||||
EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL);
|
||||
}
|
||||
|
||||
// This test verifies that SetLocalDescription and SetRemoteDescription fails
|
||||
@ -2991,11 +3091,11 @@ TEST_F(WebRtcSessionTest, TestDisabledRtcpMuxWithBundleEnabled) {
|
||||
rtc::replace_substrs(rtcp_mux.c_str(), rtcp_mux.length(),
|
||||
xrtcp_mux.c_str(), xrtcp_mux.length(),
|
||||
&offer_str);
|
||||
JsepSessionDescription *local_offer =
|
||||
JsepSessionDescription* local_offer =
|
||||
new JsepSessionDescription(JsepSessionDescription::kOffer);
|
||||
EXPECT_TRUE((local_offer)->Initialize(offer_str, NULL));
|
||||
SetLocalDescriptionOfferExpectError(kBundleWithoutRtcpMux, local_offer);
|
||||
JsepSessionDescription *remote_offer =
|
||||
JsepSessionDescription* remote_offer =
|
||||
new JsepSessionDescription(JsepSessionDescription::kOffer);
|
||||
EXPECT_TRUE((remote_offer)->Initialize(offer_str, NULL));
|
||||
SetRemoteDescriptionOfferExpectError(kBundleWithoutRtcpMux, remote_offer);
|
||||
@ -3258,8 +3358,8 @@ TEST_F(WebRtcSessionTest, TestIceStartAfterSetLocalDescriptionOnly) {
|
||||
candidate1);
|
||||
EXPECT_TRUE(offer->AddCandidate(&ice_candidate1));
|
||||
SetRemoteDescriptionWithoutError(offer);
|
||||
ASSERT_TRUE(session_->GetTransportProxy("audio") != NULL);
|
||||
ASSERT_TRUE(session_->GetTransportProxy("video") != NULL);
|
||||
ASSERT_TRUE(session_->voice_rtp_transport_channel() != NULL);
|
||||
ASSERT_TRUE(session_->video_rtp_transport_channel() != NULL);
|
||||
|
||||
// Pump for 1 second and verify that no candidates are generated.
|
||||
rtc::Thread::Current()->ProcessMessages(1000);
|
||||
@ -3268,8 +3368,6 @@ TEST_F(WebRtcSessionTest, TestIceStartAfterSetLocalDescriptionOnly) {
|
||||
|
||||
SessionDescriptionInterface* answer = CreateAnswer(NULL);
|
||||
SetLocalDescriptionWithoutError(answer);
|
||||
EXPECT_TRUE(session_->GetTransportProxy("audio")->negotiated());
|
||||
EXPECT_TRUE(session_->GetTransportProxy("video")->negotiated());
|
||||
EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
|
||||
}
|
||||
|
||||
@ -3304,7 +3402,7 @@ TEST_F(WebRtcSessionTest, TestCryptoAfterSetLocalDescriptionWithDisabled) {
|
||||
// will be set as per MediaSessionDescriptionFactory.
|
||||
std::string offer_str;
|
||||
offer->ToString(&offer_str);
|
||||
SessionDescriptionInterface *jsep_offer_str =
|
||||
SessionDescriptionInterface* jsep_offer_str =
|
||||
CreateSessionDescription(JsepSessionDescription::kOffer, offer_str, NULL);
|
||||
SetLocalDescriptionWithoutError(jsep_offer_str);
|
||||
EXPECT_FALSE(session_->voice_channel()->secure_required());
|
||||
@ -3657,8 +3755,8 @@ TEST_F(WebRtcSessionTest, TestCreateOfferAfterIdentityRequestReturnFailure) {
|
||||
TEST_P(WebRtcSessionTest,
|
||||
TestMultipleCreateOfferBeforeIdentityRequestReturnSuccess) {
|
||||
MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
|
||||
VerifyMultipleAsyncCreateDescription(
|
||||
GetParam(), CreateSessionDescriptionRequest::kOffer);
|
||||
VerifyMultipleAsyncCreateDescription(GetParam(),
|
||||
CreateSessionDescriptionRequest::kOffer);
|
||||
}
|
||||
|
||||
// Verifies that CreateOffer fails when Multiple CreateOffer calls are made
|
||||
@ -3881,31 +3979,31 @@ TEST_F(WebRtcSessionTest, TestSetSocketOptionBeforeBundle) {
|
||||
rtc::Socket::Option::OPT_RCVBUF, 8000);
|
||||
|
||||
int option_val;
|
||||
EXPECT_TRUE(session_->video_channel()->transport_channel()->GetOption(
|
||||
EXPECT_TRUE(session_->video_rtp_transport_channel()->GetOption(
|
||||
rtc::Socket::Option::OPT_SNDBUF, &option_val));
|
||||
EXPECT_EQ(4000, option_val);
|
||||
EXPECT_FALSE(session_->voice_channel()->transport_channel()->GetOption(
|
||||
EXPECT_FALSE(session_->voice_rtp_transport_channel()->GetOption(
|
||||
rtc::Socket::Option::OPT_SNDBUF, &option_val));
|
||||
|
||||
EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption(
|
||||
EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption(
|
||||
rtc::Socket::Option::OPT_RCVBUF, &option_val));
|
||||
EXPECT_EQ(8000, option_val);
|
||||
EXPECT_FALSE(session_->video_channel()->transport_channel()->GetOption(
|
||||
EXPECT_FALSE(session_->video_rtp_transport_channel()->GetOption(
|
||||
rtc::Socket::Option::OPT_RCVBUF, &option_val));
|
||||
|
||||
EXPECT_NE(session_->voice_channel()->transport_channel(),
|
||||
session_->video_channel()->transport_channel());
|
||||
EXPECT_NE(session_->voice_rtp_transport_channel(),
|
||||
session_->video_rtp_transport_channel());
|
||||
|
||||
mediastream_signaling_.SendAudioVideoStream2();
|
||||
SessionDescriptionInterface* answer =
|
||||
CreateRemoteAnswer(session_->local_description());
|
||||
SetRemoteDescriptionWithoutError(answer);
|
||||
|
||||
EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption(
|
||||
EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption(
|
||||
rtc::Socket::Option::OPT_SNDBUF, &option_val));
|
||||
EXPECT_EQ(4000, option_val);
|
||||
|
||||
EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption(
|
||||
EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption(
|
||||
rtc::Socket::Option::OPT_RCVBUF, &option_val));
|
||||
EXPECT_EQ(8000, option_val);
|
||||
}
|
||||
@ -3941,6 +4039,7 @@ TEST_F(WebRtcSessionTest, CreateOffersAndShutdown) {
|
||||
// currently fails because upon disconnection and reconnection OnIceComplete is
|
||||
// called more than once without returning to IceGatheringGathering.
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
WebRtcSessionTests, WebRtcSessionTest,
|
||||
testing::Values(ALREADY_GENERATED, DTLS_IDENTITY_STORE));
|
||||
INSTANTIATE_TEST_CASE_P(WebRtcSessionTests,
|
||||
WebRtcSessionTest,
|
||||
testing::Values(ALREADY_GENERATED,
|
||||
DTLS_IDENTITY_STORE));
|
||||
|
||||
@ -165,9 +165,15 @@ WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
|
||||
WebRtcSession* session,
|
||||
const std::string& session_id,
|
||||
cricket::DataChannelType dct)
|
||||
: WebRtcSessionDescriptionFactory(
|
||||
signaling_thread, channel_manager, mediastream_signaling, nullptr,
|
||||
nullptr, session, session_id, dct, false) {
|
||||
: WebRtcSessionDescriptionFactory(signaling_thread,
|
||||
channel_manager,
|
||||
mediastream_signaling,
|
||||
nullptr,
|
||||
nullptr,
|
||||
session,
|
||||
session_id,
|
||||
dct,
|
||||
false) {
|
||||
LOG(LS_VERBOSE) << "DTLS-SRTP disabled.";
|
||||
}
|
||||
|
||||
@ -226,9 +232,9 @@ WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
|
||||
// We already have a certificate but we wait to do SetIdentity; if we do
|
||||
// it in the constructor then the caller has not had a chance to connect to
|
||||
// SignalIdentityReady.
|
||||
signaling_thread_->Post(this, MSG_USE_CONSTRUCTOR_CERTIFICATE,
|
||||
new rtc::ScopedRefMessageData<rtc::RTCCertificate>(
|
||||
certificate));
|
||||
signaling_thread_->Post(
|
||||
this, MSG_USE_CONSTRUCTOR_CERTIFICATE,
|
||||
new rtc::ScopedRefMessageData<rtc::RTCCertificate>(certificate));
|
||||
}
|
||||
|
||||
WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() {
|
||||
@ -254,8 +260,6 @@ WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() {
|
||||
delete msg.pdata;
|
||||
}
|
||||
}
|
||||
|
||||
transport_desc_factory_.set_certificate(nullptr);
|
||||
}
|
||||
|
||||
void WebRtcSessionDescriptionFactory::CreateOffer(
|
||||
|
||||
@ -90,13 +90,12 @@ class WebRtcSessionDescriptionFactory : public rtc::MessageHandler,
|
||||
public sigslot::has_slots<> {
|
||||
public:
|
||||
// Construct with DTLS disabled.
|
||||
WebRtcSessionDescriptionFactory(
|
||||
rtc::Thread* signaling_thread,
|
||||
cricket::ChannelManager* channel_manager,
|
||||
MediaStreamSignaling* mediastream_signaling,
|
||||
WebRtcSession* session,
|
||||
const std::string& session_id,
|
||||
cricket::DataChannelType dct);
|
||||
WebRtcSessionDescriptionFactory(rtc::Thread* signaling_thread,
|
||||
cricket::ChannelManager* channel_manager,
|
||||
MediaStreamSignaling* mediastream_signaling,
|
||||
WebRtcSession* session,
|
||||
const std::string& session_id,
|
||||
cricket::DataChannelType dct);
|
||||
|
||||
// Construct with DTLS enabled using the specified |dtls_identity_store| to
|
||||
// generate a certificate.
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
#include "talk/media/webrtc/fakewebrtccall.h"
|
||||
#include "talk/media/webrtc/fakewebrtcvoiceengine.h"
|
||||
#include "talk/media/webrtc/webrtcvoiceengine.h"
|
||||
#include "webrtc/p2p/base/fakesession.h"
|
||||
#include "webrtc/p2p/base/faketransportcontroller.h"
|
||||
#include "talk/session/media/channel.h"
|
||||
|
||||
// Tests for the WebRtcVoiceEngine/VoiceChannel code.
|
||||
|
||||
@ -170,15 +170,17 @@ void RtpSendParametersFromMediaDescription(
|
||||
}
|
||||
|
||||
BaseChannel::BaseChannel(rtc::Thread* thread,
|
||||
MediaChannel* media_channel, BaseSession* session,
|
||||
const std::string& content_name, bool rtcp)
|
||||
MediaChannel* media_channel,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp)
|
||||
: worker_thread_(thread),
|
||||
session_(session),
|
||||
transport_controller_(transport_controller),
|
||||
media_channel_(media_channel),
|
||||
content_name_(content_name),
|
||||
rtcp_(rtcp),
|
||||
transport_channel_(NULL),
|
||||
rtcp_transport_channel_(NULL),
|
||||
rtcp_transport_enabled_(rtcp),
|
||||
transport_channel_(nullptr),
|
||||
rtcp_transport_channel_(nullptr),
|
||||
enabled_(false),
|
||||
writable_(false),
|
||||
rtp_ready_to_send_(false),
|
||||
@ -204,20 +206,31 @@ BaseChannel::~BaseChannel() {
|
||||
// the media channel may try to send on the dead transport channel. NULLing
|
||||
// is not an effective strategy since the sends will come on another thread.
|
||||
delete media_channel_;
|
||||
set_transport_channel(nullptr);
|
||||
set_rtcp_transport_channel(nullptr);
|
||||
// Note that we don't just call set_transport_channel(nullptr) because that
|
||||
// would call a pure virtual method which we can't do from a destructor.
|
||||
if (transport_channel_) {
|
||||
DisconnectFromTransportChannel(transport_channel_);
|
||||
transport_controller_->DestroyTransportChannel_w(
|
||||
transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
}
|
||||
if (rtcp_transport_channel_) {
|
||||
DisconnectFromTransportChannel(rtcp_transport_channel_);
|
||||
transport_controller_->DestroyTransportChannel_w(
|
||||
transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
}
|
||||
LOG(LS_INFO) << "Destroyed channel";
|
||||
}
|
||||
|
||||
bool BaseChannel::Init() {
|
||||
if (!SetTransportChannels(session(), rtcp())) {
|
||||
if (!SetTransport(content_name())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetDtlsSrtpCiphers(transport_channel(), false)) {
|
||||
return false;
|
||||
}
|
||||
if (rtcp() && !SetDtlsSrtpCiphers(rtcp_transport_channel(), true)) {
|
||||
if (rtcp_transport_enabled() &&
|
||||
!SetDtlsSrtpCiphers(rtcp_transport_channel(), true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -231,29 +244,35 @@ void BaseChannel::Deinit() {
|
||||
media_channel_->SetInterface(NULL);
|
||||
}
|
||||
|
||||
bool BaseChannel::SetTransportChannels(BaseSession* session, bool rtcp) {
|
||||
return worker_thread_->Invoke<bool>(Bind(
|
||||
&BaseChannel::SetTransportChannels_w, this, session, rtcp));
|
||||
bool BaseChannel::SetTransport(const std::string& transport_name) {
|
||||
return worker_thread_->Invoke<bool>(
|
||||
Bind(&BaseChannel::SetTransport_w, this, transport_name));
|
||||
}
|
||||
|
||||
bool BaseChannel::SetTransportChannels_w(BaseSession* session, bool rtcp) {
|
||||
bool BaseChannel::SetTransport_w(const std::string& transport_name) {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
|
||||
set_transport_channel(session->CreateChannel(
|
||||
content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTP));
|
||||
if (transport_name == transport_name_) {
|
||||
// Nothing to do if transport name isn't changing
|
||||
return true;
|
||||
}
|
||||
|
||||
set_transport_channel(transport_controller_->CreateTransportChannel_w(
|
||||
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP));
|
||||
if (!transport_channel()) {
|
||||
return false;
|
||||
}
|
||||
if (rtcp) {
|
||||
set_rtcp_transport_channel(session->CreateChannel(
|
||||
content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTCP));
|
||||
if (rtcp_transport_enabled()) {
|
||||
LOG(LS_INFO) << "Create RTCP TransportChannel for " << content_name()
|
||||
<< " on " << transport_name << " transport ";
|
||||
set_rtcp_transport_channel(transport_controller_->CreateTransportChannel_w(
|
||||
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP));
|
||||
if (!rtcp_transport_channel()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
set_rtcp_transport_channel(nullptr);
|
||||
}
|
||||
|
||||
transport_name_ = transport_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -261,42 +280,62 @@ void BaseChannel::set_transport_channel(TransportChannel* new_tc) {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
|
||||
TransportChannel* old_tc = transport_channel_;
|
||||
|
||||
if (old_tc == new_tc) {
|
||||
if (!old_tc && !new_tc) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
ASSERT(old_tc != new_tc);
|
||||
|
||||
if (old_tc) {
|
||||
DisconnectFromTransportChannel(old_tc);
|
||||
session()->DestroyChannel(
|
||||
content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
transport_controller_->DestroyTransportChannel_w(
|
||||
transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTP);
|
||||
}
|
||||
|
||||
transport_channel_ = new_tc;
|
||||
|
||||
if (new_tc) {
|
||||
ConnectToTransportChannel(new_tc);
|
||||
for (const auto& pair : socket_options_) {
|
||||
new_tc->SetOption(pair.first, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Update aggregate writable/ready-to-send state between RTP and RTCP upon
|
||||
// setting new channel
|
||||
UpdateWritableState_w();
|
||||
SetReadyToSend(false, new_tc && new_tc->writable());
|
||||
}
|
||||
|
||||
void BaseChannel::set_rtcp_transport_channel(TransportChannel* new_tc) {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
|
||||
TransportChannel* old_tc = rtcp_transport_channel_;
|
||||
|
||||
if (old_tc == new_tc) {
|
||||
if (!old_tc && !new_tc) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
ASSERT(old_tc != new_tc);
|
||||
|
||||
if (old_tc) {
|
||||
DisconnectFromTransportChannel(old_tc);
|
||||
session()->DestroyChannel(
|
||||
content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
transport_controller_->DestroyTransportChannel_w(
|
||||
transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
|
||||
}
|
||||
|
||||
rtcp_transport_channel_ = new_tc;
|
||||
|
||||
if (new_tc) {
|
||||
ConnectToTransportChannel(new_tc);
|
||||
for (const auto& pair : rtcp_socket_options_) {
|
||||
new_tc->SetOption(pair.first, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Update aggregate writable/ready-to-send state between RTP and RTCP upon
|
||||
// setting new channel
|
||||
UpdateWritableState_w();
|
||||
SetReadyToSend(true, new_tc && new_tc->writable());
|
||||
}
|
||||
|
||||
void BaseChannel::ConnectToTransportChannel(TransportChannel* tc) {
|
||||
@ -407,9 +446,13 @@ int BaseChannel::SetOption(SocketType type, rtc::Socket::Option opt,
|
||||
switch (type) {
|
||||
case ST_RTP:
|
||||
channel = transport_channel_;
|
||||
socket_options_.push_back(
|
||||
std::pair<rtc::Socket::Option, int>(opt, value));
|
||||
break;
|
||||
case ST_RTCP:
|
||||
channel = rtcp_transport_channel_;
|
||||
rtcp_socket_options_.push_back(
|
||||
std::pair<rtc::Socket::Option, int>(opt, value));
|
||||
break;
|
||||
}
|
||||
return channel ? channel->SetOption(opt, value) : -1;
|
||||
@ -417,12 +460,7 @@ int BaseChannel::SetOption(SocketType type, rtc::Socket::Option opt,
|
||||
|
||||
void BaseChannel::OnWritableState(TransportChannel* channel) {
|
||||
ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_);
|
||||
if (transport_channel_->writable()
|
||||
&& (!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) {
|
||||
ChannelWritable_w();
|
||||
} else {
|
||||
ChannelNotWritable_w();
|
||||
}
|
||||
UpdateWritableState_w();
|
||||
}
|
||||
|
||||
void BaseChannel::OnChannelRead(TransportChannel* channel,
|
||||
@ -440,26 +478,25 @@ void BaseChannel::OnChannelRead(TransportChannel* channel,
|
||||
}
|
||||
|
||||
void BaseChannel::OnReadyToSend(TransportChannel* channel) {
|
||||
SetReadyToSend(channel, true);
|
||||
ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_);
|
||||
SetReadyToSend(channel == rtcp_transport_channel_, true);
|
||||
}
|
||||
|
||||
void BaseChannel::SetReadyToSend(TransportChannel* channel, bool ready) {
|
||||
ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_);
|
||||
if (channel == transport_channel_) {
|
||||
void BaseChannel::SetReadyToSend(bool rtcp, bool ready) {
|
||||
if (rtcp) {
|
||||
rtcp_ready_to_send_ = ready;
|
||||
} else {
|
||||
rtp_ready_to_send_ = ready;
|
||||
}
|
||||
if (channel == rtcp_transport_channel_) {
|
||||
rtcp_ready_to_send_ = ready;
|
||||
}
|
||||
|
||||
if (!ready) {
|
||||
// Notify the MediaChannel when either rtp or rtcp channel can't send.
|
||||
media_channel_->OnReadyToSend(false);
|
||||
} else if (rtp_ready_to_send_ &&
|
||||
// In the case of rtcp mux |rtcp_transport_channel_| will be null.
|
||||
(rtcp_ready_to_send_ || !rtcp_transport_channel_)) {
|
||||
if (rtp_ready_to_send_ &&
|
||||
// In the case of rtcp mux |rtcp_transport_channel_| will be null.
|
||||
(rtcp_ready_to_send_ || !rtcp_transport_channel_)) {
|
||||
// Notify the MediaChannel when both rtp and rtcp channel can send.
|
||||
media_channel_->OnReadyToSend(true);
|
||||
} else {
|
||||
// Notify the MediaChannel when either rtp or rtcp channel can't send.
|
||||
media_channel_->OnReadyToSend(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -581,7 +618,7 @@ bool BaseChannel::SendPacket(bool rtcp, rtc::Buffer* packet,
|
||||
if (ret != static_cast<int>(packet->size())) {
|
||||
if (channel->GetError() == EWOULDBLOCK) {
|
||||
LOG(LS_WARNING) << "Got EWOULDBLOCK from socket.";
|
||||
SetReadyToSend(channel, false);
|
||||
SetReadyToSend(rtcp, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -715,14 +752,21 @@ void BaseChannel::DisableMedia_w() {
|
||||
ChangeState();
|
||||
}
|
||||
|
||||
void BaseChannel::UpdateWritableState_w() {
|
||||
if (transport_channel_ && transport_channel_->writable() &&
|
||||
(!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) {
|
||||
ChannelWritable_w();
|
||||
} else {
|
||||
ChannelNotWritable_w();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseChannel::ChannelWritable_w() {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
if (writable_)
|
||||
return;
|
||||
|
||||
LOG(LS_INFO) << "Channel socket writable ("
|
||||
<< transport_channel_->content_name() << ", "
|
||||
<< transport_channel_->component() << ")"
|
||||
LOG(LS_INFO) << "Channel writable (" << content_name_ << ")"
|
||||
<< (was_ever_writable_ ? "" : " for the first time");
|
||||
|
||||
std::vector<ConnectionInfo> infos;
|
||||
@ -739,13 +783,13 @@ void BaseChannel::ChannelWritable_w() {
|
||||
// If we're doing DTLS-SRTP, now is the time.
|
||||
if (!was_ever_writable_ && ShouldSetupDtlsSrtp()) {
|
||||
if (!SetupDtlsSrtp(false)) {
|
||||
SignalDtlsSetupFailure(this, false);
|
||||
SignalDtlsSetupFailure_w(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rtcp_transport_channel_) {
|
||||
if (!SetupDtlsSrtp(true)) {
|
||||
SignalDtlsSetupFailure(this, true);
|
||||
SignalDtlsSetupFailure_w(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -788,8 +832,8 @@ bool BaseChannel::ShouldSetupDtlsSrtp() const {
|
||||
bool BaseChannel::SetupDtlsSrtp(bool rtcp_channel) {
|
||||
bool ret = false;
|
||||
|
||||
TransportChannel *channel = rtcp_channel ?
|
||||
rtcp_transport_channel_ : transport_channel_;
|
||||
TransportChannel* channel =
|
||||
rtcp_channel ? rtcp_transport_channel_ : transport_channel_;
|
||||
|
||||
// No DTLS
|
||||
if (!channel->IsDtlsActive())
|
||||
@ -884,9 +928,7 @@ void BaseChannel::ChannelNotWritable_w() {
|
||||
if (!writable_)
|
||||
return;
|
||||
|
||||
LOG(LS_INFO) << "Channel socket not writable ("
|
||||
<< transport_channel_->content_name() << ", "
|
||||
<< transport_channel_->component() << ")";
|
||||
LOG(LS_INFO) << "Channel not writable (" << content_name_ << ")";
|
||||
writable_ = false;
|
||||
ChangeState();
|
||||
}
|
||||
@ -985,7 +1027,8 @@ void BaseChannel::ActivateRtcpMux() {
|
||||
void BaseChannel::ActivateRtcpMux_w() {
|
||||
if (!rtcp_mux_filter_.IsActive()) {
|
||||
rtcp_mux_filter_.SetActive();
|
||||
set_rtcp_transport_channel(NULL);
|
||||
set_rtcp_transport_channel(nullptr);
|
||||
rtcp_transport_enabled_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1004,7 +1047,11 @@ bool BaseChannel::SetRtcpMux_w(bool enable, ContentAction action,
|
||||
ret = rtcp_mux_filter_.SetAnswer(enable, src);
|
||||
if (ret && rtcp_mux_filter_.IsActive()) {
|
||||
// We activated RTCP mux, close down the RTCP transport.
|
||||
set_rtcp_transport_channel(NULL);
|
||||
LOG(LS_INFO) << "Enabling rtcp-mux for " << content_name()
|
||||
<< " by destroying RTCP transport channel for "
|
||||
<< transport_name();
|
||||
set_rtcp_transport_channel(nullptr);
|
||||
rtcp_transport_enabled_ = false;
|
||||
}
|
||||
break;
|
||||
case CA_UPDATE:
|
||||
@ -1231,14 +1278,16 @@ void BaseChannel::FlushRtcpMessages() {
|
||||
VoiceChannel::VoiceChannel(rtc::Thread* thread,
|
||||
MediaEngineInterface* media_engine,
|
||||
VoiceMediaChannel* media_channel,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp)
|
||||
: BaseChannel(thread, media_channel, session, content_name,
|
||||
: BaseChannel(thread,
|
||||
media_channel,
|
||||
transport_controller,
|
||||
content_name,
|
||||
rtcp),
|
||||
media_engine_(media_engine),
|
||||
received_media_(false) {
|
||||
}
|
||||
received_media_(false) {}
|
||||
|
||||
VoiceChannel::~VoiceChannel() {
|
||||
StopAudioMonitor();
|
||||
@ -1264,11 +1313,12 @@ bool VoiceChannel::SetRemoteRenderer(uint32 ssrc, AudioRenderer* renderer) {
|
||||
media_channel(), ssrc, renderer));
|
||||
}
|
||||
|
||||
bool VoiceChannel::SetAudioSend(uint32 ssrc, bool mute,
|
||||
bool VoiceChannel::SetAudioSend(uint32 ssrc,
|
||||
bool mute,
|
||||
const AudioOptions* options,
|
||||
AudioRenderer* renderer) {
|
||||
return InvokeOnWorker(Bind(&VoiceMediaChannel::SetAudioSend,
|
||||
media_channel(), ssrc, mute, options, renderer));
|
||||
return InvokeOnWorker(Bind(&VoiceMediaChannel::SetAudioSend, media_channel(),
|
||||
ssrc, mute, options, renderer));
|
||||
}
|
||||
|
||||
// TODO(juberti): Handle early media the right way. We should get an explicit
|
||||
@ -1583,14 +1633,16 @@ void VoiceChannel::GetSrtpCiphers(std::vector<std::string>* ciphers) const {
|
||||
|
||||
VideoChannel::VideoChannel(rtc::Thread* thread,
|
||||
VideoMediaChannel* media_channel,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp)
|
||||
: BaseChannel(thread, media_channel, session, content_name,
|
||||
: BaseChannel(thread,
|
||||
media_channel,
|
||||
transport_controller,
|
||||
content_name,
|
||||
rtcp),
|
||||
renderer_(NULL),
|
||||
previous_we_(rtc::WE_CLOSE) {
|
||||
}
|
||||
previous_we_(rtc::WE_CLOSE) {}
|
||||
|
||||
bool VideoChannel::Init() {
|
||||
if (!BaseChannel::Init()) {
|
||||
@ -1683,10 +1735,11 @@ bool VideoChannel::RequestIntraFrame() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VideoChannel::SetVideoSend(uint32 ssrc, bool mute,
|
||||
bool VideoChannel::SetVideoSend(uint32 ssrc,
|
||||
bool mute,
|
||||
const VideoOptions* options) {
|
||||
return InvokeOnWorker(Bind(&VideoMediaChannel::SetVideoSend,
|
||||
media_channel(), ssrc, mute, options));
|
||||
return InvokeOnWorker(Bind(&VideoMediaChannel::SetVideoSend, media_channel(),
|
||||
ssrc, mute, options));
|
||||
}
|
||||
|
||||
void VideoChannel::ChangeState() {
|
||||
@ -2021,13 +2074,16 @@ void VideoChannel::GetSrtpCiphers(std::vector<std::string>* ciphers) const {
|
||||
|
||||
DataChannel::DataChannel(rtc::Thread* thread,
|
||||
DataMediaChannel* media_channel,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp)
|
||||
: BaseChannel(thread, media_channel, session, content_name, rtcp),
|
||||
: BaseChannel(thread,
|
||||
media_channel,
|
||||
transport_controller,
|
||||
content_name,
|
||||
rtcp),
|
||||
data_channel_type_(cricket::DCT_NONE),
|
||||
ready_to_send_data_(false) {
|
||||
}
|
||||
ready_to_send_data_(false) {}
|
||||
|
||||
DataChannel::~DataChannel() {
|
||||
StopMediaMonitor();
|
||||
|
||||
@ -30,12 +30,15 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include "talk/media/base/mediachannel.h"
|
||||
#include "talk/media/base/mediaengine.h"
|
||||
#include "talk/media/base/streamparams.h"
|
||||
#include "talk/media/base/videocapturer.h"
|
||||
#include "webrtc/p2p/base/session.h"
|
||||
#include "webrtc/p2p/base/transportcontroller.h"
|
||||
#include "webrtc/p2p/client/socketmonitor.h"
|
||||
#include "talk/session/media/audiomonitor.h"
|
||||
#include "talk/session/media/bundlefilter.h"
|
||||
@ -74,8 +77,11 @@ class BaseChannel
|
||||
public MediaChannel::NetworkInterface,
|
||||
public ConnectionStatsGetter {
|
||||
public:
|
||||
BaseChannel(rtc::Thread* thread, MediaChannel* channel, BaseSession* session,
|
||||
const std::string& content_name, bool rtcp);
|
||||
BaseChannel(rtc::Thread* thread,
|
||||
MediaChannel* channel,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp);
|
||||
virtual ~BaseChannel();
|
||||
bool Init();
|
||||
// Deinit may be called multiple times and is simply ignored if it's alreay
|
||||
@ -83,8 +89,8 @@ class BaseChannel
|
||||
void Deinit();
|
||||
|
||||
rtc::Thread* worker_thread() const { return worker_thread_; }
|
||||
BaseSession* session() const { return session_; }
|
||||
const std::string& content_name() { return content_name_; }
|
||||
const std::string& content_name() const { return content_name_; }
|
||||
const std::string& transport_name() const { return transport_name_; }
|
||||
TransportChannel* transport_channel() const {
|
||||
return transport_channel_;
|
||||
}
|
||||
@ -109,6 +115,7 @@ class BaseChannel
|
||||
// description doesn't support RTCP mux, setting the remote
|
||||
// description will fail.
|
||||
void ActivateRtcpMux();
|
||||
bool SetTransport(const std::string& transport_name);
|
||||
bool PushdownLocalDescription(const SessionDescription* local_desc,
|
||||
ContentAction action,
|
||||
std::string* error_desc);
|
||||
@ -135,7 +142,7 @@ class BaseChannel
|
||||
void StartConnectionMonitor(int cms);
|
||||
void StopConnectionMonitor();
|
||||
// For ConnectionStatsGetter, used by ConnectionMonitor
|
||||
virtual bool GetConnectionStats(ConnectionInfos* infos) override;
|
||||
bool GetConnectionStats(ConnectionInfos* infos) override;
|
||||
|
||||
void set_srtp_signal_silent_time(uint32 silent_time) {
|
||||
srtp_filter_.set_signal_silent_time(silent_time);
|
||||
@ -158,19 +165,16 @@ class BaseChannel
|
||||
sigslot::signal1<BaseChannel*> SignalFirstPacketReceived;
|
||||
|
||||
// Made public for easier testing.
|
||||
void SetReadyToSend(TransportChannel* channel, bool ready);
|
||||
void SetReadyToSend(bool rtcp, bool ready);
|
||||
|
||||
// Only public for unit tests. Otherwise, consider protected.
|
||||
virtual int SetOption(SocketType type, rtc::Socket::Option o, int val);
|
||||
|
||||
protected:
|
||||
virtual MediaChannel* media_channel() const { return media_channel_; }
|
||||
// Sets the transport_channel_ and rtcp_transport_channel_. If
|
||||
// |rtcp| is false, set rtcp_transport_channel_ is set to NULL. Get
|
||||
// the transport channels from |session|.
|
||||
// TODO(pthatcher): Pass in a Transport instead of a BaseSession.
|
||||
bool SetTransportChannels(BaseSession* session, bool rtcp);
|
||||
bool SetTransportChannels_w(BaseSession* session, bool rtcp);
|
||||
// Sets the |transport_channel_| (and |rtcp_transport_channel_|, if |rtcp_| is
|
||||
// true). Gets the transport channels from |transport_controller_|.
|
||||
bool SetTransport_w(const std::string& transport_name);
|
||||
void set_transport_channel(TransportChannel* transport);
|
||||
void set_rtcp_transport_channel(TransportChannel* transport);
|
||||
bool was_ever_writable() const { return was_ever_writable_; }
|
||||
@ -185,9 +189,11 @@ class BaseChannel
|
||||
}
|
||||
bool IsReadyToReceive() const;
|
||||
bool IsReadyToSend() const;
|
||||
rtc::Thread* signaling_thread() { return session_->signaling_thread(); }
|
||||
rtc::Thread* signaling_thread() {
|
||||
return transport_controller_->signaling_thread();
|
||||
}
|
||||
SrtpFilter* srtp_filter() { return &srtp_filter_; }
|
||||
bool rtcp() const { return rtcp_; }
|
||||
bool rtcp_transport_enabled() const { return rtcp_transport_enabled_; }
|
||||
|
||||
void ConnectToTransportChannel(TransportChannel* tc);
|
||||
void DisconnectFromTransportChannel(TransportChannel* tc);
|
||||
@ -217,12 +223,9 @@ class BaseChannel
|
||||
void HandlePacket(bool rtcp, rtc::Buffer* packet,
|
||||
const rtc::PacketTime& packet_time);
|
||||
|
||||
// Apply the new local/remote session description.
|
||||
void OnNewLocalDescription(BaseSession* session, ContentAction action);
|
||||
void OnNewRemoteDescription(BaseSession* session, ContentAction action);
|
||||
|
||||
void EnableMedia_w();
|
||||
void DisableMedia_w();
|
||||
void UpdateWritableState_w();
|
||||
void ChannelWritable_w();
|
||||
void ChannelNotWritable_w();
|
||||
bool AddRecvStream_w(const StreamParams& sp);
|
||||
@ -293,15 +296,18 @@ class BaseChannel
|
||||
|
||||
private:
|
||||
rtc::Thread* worker_thread_;
|
||||
BaseSession* session_;
|
||||
TransportController* transport_controller_;
|
||||
MediaChannel* media_channel_;
|
||||
std::vector<StreamParams> local_streams_;
|
||||
std::vector<StreamParams> remote_streams_;
|
||||
|
||||
const std::string content_name_;
|
||||
bool rtcp_;
|
||||
std::string transport_name_;
|
||||
bool rtcp_transport_enabled_;
|
||||
TransportChannel* transport_channel_;
|
||||
std::vector<std::pair<rtc::Socket::Option, int> > socket_options_;
|
||||
TransportChannel* rtcp_transport_channel_;
|
||||
std::vector<std::pair<rtc::Socket::Option, int> > rtcp_socket_options_;
|
||||
SrtpFilter srtp_filter_;
|
||||
RtcpMuxFilter rtcp_mux_filter_;
|
||||
BundleFilter bundle_filter_;
|
||||
@ -323,16 +329,21 @@ class BaseChannel
|
||||
// and input/output level monitoring.
|
||||
class VoiceChannel : public BaseChannel {
|
||||
public:
|
||||
VoiceChannel(rtc::Thread* thread, MediaEngineInterface* media_engine,
|
||||
VoiceMediaChannel* channel, BaseSession* session,
|
||||
const std::string& content_name, bool rtcp);
|
||||
VoiceChannel(rtc::Thread* thread,
|
||||
MediaEngineInterface* media_engine,
|
||||
VoiceMediaChannel* channel,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp);
|
||||
~VoiceChannel();
|
||||
bool Init();
|
||||
bool SetRemoteRenderer(uint32 ssrc, AudioRenderer* renderer);
|
||||
|
||||
// Configure sending media on the stream with SSRC |ssrc|
|
||||
// If there is only one sending stream SSRC 0 can be used.
|
||||
bool SetAudioSend(uint32 ssrc, bool mute, const AudioOptions* options,
|
||||
bool SetAudioSend(uint32 ssrc,
|
||||
bool mute,
|
||||
const AudioOptions* options,
|
||||
AudioRenderer* renderer);
|
||||
|
||||
// downcasts a MediaChannel
|
||||
@ -429,8 +440,10 @@ class VoiceChannel : public BaseChannel {
|
||||
// VideoChannel is a specialization for video.
|
||||
class VideoChannel : public BaseChannel {
|
||||
public:
|
||||
VideoChannel(rtc::Thread* thread, VideoMediaChannel* channel,
|
||||
BaseSession* session, const std::string& content_name,
|
||||
VideoChannel(rtc::Thread* thread,
|
||||
VideoMediaChannel* channel,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp);
|
||||
~VideoChannel();
|
||||
bool Init();
|
||||
@ -529,7 +542,7 @@ class DataChannel : public BaseChannel {
|
||||
public:
|
||||
DataChannel(rtc::Thread* thread,
|
||||
DataMediaChannel* media_channel,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp);
|
||||
~DataChannel();
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
#include "talk/media/base/rtpdump.h"
|
||||
#include "talk/media/base/screencastid.h"
|
||||
#include "talk/media/base/testutils.h"
|
||||
#include "webrtc/p2p/base/fakesession.h"
|
||||
#include "webrtc/p2p/base/faketransportcontroller.h"
|
||||
#include "talk/session/media/channel.h"
|
||||
#include "webrtc/base/fileutils.h"
|
||||
#include "webrtc/base/gunit.h"
|
||||
@ -73,12 +73,12 @@ static const uint32 kSsrc3 = 0x3333;
|
||||
static const int kAudioPts[] = {0, 8};
|
||||
static const int kVideoPts[] = {97, 99};
|
||||
|
||||
template<class ChannelT,
|
||||
class MediaChannelT,
|
||||
class ContentT,
|
||||
class CodecT,
|
||||
class MediaInfoT,
|
||||
class OptionsT>
|
||||
template <class ChannelT,
|
||||
class MediaChannelT,
|
||||
class ContentT,
|
||||
class CodecT,
|
||||
class MediaInfoT,
|
||||
class OptionsT>
|
||||
class Traits {
|
||||
public:
|
||||
typedef ChannelT Channel;
|
||||
@ -98,25 +98,21 @@ class VoiceTraits : public Traits<cricket::VoiceChannel,
|
||||
cricket::AudioContentDescription,
|
||||
cricket::AudioCodec,
|
||||
cricket::VoiceMediaInfo,
|
||||
cricket::AudioOptions> {
|
||||
};
|
||||
cricket::AudioOptions> {};
|
||||
|
||||
class VideoTraits : public Traits<cricket::VideoChannel,
|
||||
cricket::FakeVideoMediaChannel,
|
||||
cricket::VideoContentDescription,
|
||||
cricket::VideoCodec,
|
||||
cricket::VideoMediaInfo,
|
||||
cricket::VideoOptions> {
|
||||
};
|
||||
cricket::VideoOptions> {};
|
||||
|
||||
class DataTraits : public Traits<cricket::DataChannel,
|
||||
cricket::FakeDataMediaChannel,
|
||||
cricket::DataContentDescription,
|
||||
cricket::DataCodec,
|
||||
cricket::DataMediaInfo,
|
||||
cricket::DataOptions> {
|
||||
};
|
||||
|
||||
cricket::DataOptions> {};
|
||||
|
||||
rtc::StreamInterface* Open(const std::string& path) {
|
||||
return rtc::Filesystem::OpenFile(
|
||||
@ -130,10 +126,12 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
enum Flags { RTCP = 0x1, RTCP_MUX = 0x2, SECURE = 0x4, SSRC_MUX = 0x8,
|
||||
DTLS = 0x10 };
|
||||
|
||||
ChannelTest(const uint8* rtp_data, int rtp_len,
|
||||
const uint8* rtcp_data, int rtcp_len)
|
||||
: session1_(true),
|
||||
session2_(false),
|
||||
ChannelTest(const uint8* rtp_data,
|
||||
int rtp_len,
|
||||
const uint8* rtcp_data,
|
||||
int rtcp_len)
|
||||
: transport_controller1_(cricket::ICEROLE_CONTROLLING),
|
||||
transport_controller2_(cricket::ICEROLE_CONTROLLED),
|
||||
media_channel1_(NULL),
|
||||
media_channel2_(NULL),
|
||||
rtp_packet_(reinterpret_cast<const char*>(rtp_data), rtp_len),
|
||||
@ -141,8 +139,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
media_info_callbacks1_(),
|
||||
media_info_callbacks2_(),
|
||||
ssrc_(0),
|
||||
error_(T::MediaChannel::ERROR_NONE) {
|
||||
}
|
||||
error_(T::MediaChannel::ERROR_NONE) {}
|
||||
|
||||
void CreateChannels(int flags1, int flags2) {
|
||||
CreateChannels(new typename T::MediaChannel(NULL, typename T::Options()),
|
||||
@ -154,9 +151,11 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
int flags1, int flags2, rtc::Thread* thread) {
|
||||
media_channel1_ = ch1;
|
||||
media_channel2_ = ch2;
|
||||
channel1_.reset(CreateChannel(thread, &media_engine_, ch1, &session1_,
|
||||
channel1_.reset(CreateChannel(thread, &media_engine_, ch1,
|
||||
&transport_controller1_,
|
||||
(flags1 & RTCP) != 0));
|
||||
channel2_.reset(CreateChannel(thread, &media_engine_, ch2, &session2_,
|
||||
channel2_.reset(CreateChannel(thread, &media_engine_, ch2,
|
||||
&transport_controller2_,
|
||||
(flags2 & RTCP) != 0));
|
||||
channel1_->SignalMediaMonitor.connect(
|
||||
this, &ChannelTest<T>::OnMediaMonitor);
|
||||
@ -179,15 +178,17 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
|
||||
if (flags1 & DTLS) {
|
||||
// Confirmed to work with KT_RSA and KT_ECDSA.
|
||||
session1_.set_ssl_rtccertificate(rtc::RTCCertificate::Create(
|
||||
rtc::scoped_ptr<rtc::SSLIdentity>(rtc::SSLIdentity::Generate(
|
||||
"session1", rtc::KT_DEFAULT)).Pass()));
|
||||
transport_controller1_.SetLocalCertificate(rtc::RTCCertificate::Create(
|
||||
rtc::scoped_ptr<rtc::SSLIdentity>(
|
||||
rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT))
|
||||
.Pass()));
|
||||
}
|
||||
if (flags2 & DTLS) {
|
||||
// Confirmed to work with KT_RSA and KT_ECDSA.
|
||||
session2_.set_ssl_rtccertificate(rtc::RTCCertificate::Create(
|
||||
rtc::scoped_ptr<rtc::SSLIdentity>(rtc::SSLIdentity::Generate(
|
||||
"session2", rtc::KT_DEFAULT)).Pass()));
|
||||
transport_controller2_.SetLocalCertificate(rtc::RTCCertificate::Create(
|
||||
rtc::scoped_ptr<rtc::SSLIdentity>(
|
||||
rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT))
|
||||
.Pass()));
|
||||
}
|
||||
|
||||
// Add stream information (SSRC) to the local content but not to the remote
|
||||
@ -204,13 +205,14 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
AddLegacyStreamInContent(kSsrc2, flags2, &remote_media_content2_);
|
||||
}
|
||||
}
|
||||
typename T::Channel* CreateChannel(rtc::Thread* thread,
|
||||
cricket::MediaEngineInterface* engine,
|
||||
typename T::MediaChannel* ch,
|
||||
cricket::BaseSession* session,
|
||||
bool rtcp) {
|
||||
typename T::Channel* CreateChannel(
|
||||
rtc::Thread* thread,
|
||||
cricket::MediaEngineInterface* engine,
|
||||
typename T::MediaChannel* ch,
|
||||
cricket::TransportController* transport_controller,
|
||||
bool rtcp) {
|
||||
typename T::Channel* channel = new typename T::Channel(
|
||||
thread, engine, ch, session, cricket::CN_AUDIO, rtcp);
|
||||
thread, engine, ch, transport_controller, cricket::CN_AUDIO, rtcp);
|
||||
if (!channel->Init()) {
|
||||
delete channel;
|
||||
channel = NULL;
|
||||
@ -226,7 +228,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
result = channel2_->SetRemoteContent(&remote_media_content1_,
|
||||
CA_OFFER, NULL);
|
||||
if (result) {
|
||||
session1_.Connect(&session2_);
|
||||
transport_controller1_.Connect(&transport_controller2_);
|
||||
|
||||
result = channel2_->SetLocalContent(&local_media_content2_,
|
||||
CA_ANSWER, NULL);
|
||||
@ -259,7 +261,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
channel2_->Enable(true);
|
||||
result = channel1_->SetRemoteContent(&remote_media_content2_,
|
||||
CA_PRANSWER, NULL);
|
||||
session1_.Connect(&session2_);
|
||||
transport_controller1_.Connect(&transport_controller2_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -286,11 +288,12 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
return channel1_->RemoveRecvStream(id);
|
||||
}
|
||||
|
||||
// Calling "_w" method here is ok since we only use one thread for this test
|
||||
cricket::FakeTransport* GetTransport1() {
|
||||
return session1_.GetTransport(channel1_->content_name());
|
||||
return transport_controller1_.GetTransport_w(channel1_->content_name());
|
||||
}
|
||||
cricket::FakeTransport* GetTransport2() {
|
||||
return session2_.GetTransport(channel2_->content_name());
|
||||
return transport_controller2_.GetTransport_w(channel2_->content_name());
|
||||
}
|
||||
|
||||
bool SendRtp1() {
|
||||
@ -769,7 +772,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
|
||||
EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER, NULL));
|
||||
EXPECT_EQ(1u, media_channel2_->recv_streams().size());
|
||||
session1_.Connect(&session2_);
|
||||
transport_controller1_.Connect(&transport_controller2_);
|
||||
|
||||
// Channel 2 do not send anything.
|
||||
typename T::Content content2;
|
||||
@ -832,7 +835,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CA_ANSWER, NULL));
|
||||
EXPECT_FALSE(media_channel2_->playout());
|
||||
EXPECT_FALSE(media_channel2_->sending());
|
||||
session1_.Connect(&session2_);
|
||||
transport_controller1_.Connect(&transport_controller2_);
|
||||
EXPECT_TRUE(media_channel1_->playout());
|
||||
EXPECT_FALSE(media_channel1_->sending());
|
||||
EXPECT_FALSE(media_channel2_->playout());
|
||||
@ -868,7 +871,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER, NULL));
|
||||
EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_PRANSWER, NULL));
|
||||
EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_PRANSWER, NULL));
|
||||
session1_.Connect(&session2_);
|
||||
transport_controller1_.Connect(&transport_controller2_);
|
||||
|
||||
EXPECT_TRUE(media_channel1_->playout());
|
||||
EXPECT_FALSE(media_channel1_->sending()); // remote InActive
|
||||
@ -938,6 +941,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CreateChannels(0, 0);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(1U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(1U, GetTransport2()->channels().size());
|
||||
EXPECT_TRUE(SendRtp1());
|
||||
@ -953,6 +958,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CreateChannels(0, 0);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(1U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(1U, GetTransport2()->channels().size());
|
||||
EXPECT_FALSE(SendRtcp1());
|
||||
@ -966,6 +973,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CreateChannels(0, RTCP);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(1U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(2U, GetTransport2()->channels().size());
|
||||
EXPECT_FALSE(SendRtcp1());
|
||||
@ -979,6 +988,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CreateChannels(RTCP, 0);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(2U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(1U, GetTransport2()->channels().size());
|
||||
EXPECT_FALSE(SendRtcp1());
|
||||
@ -992,6 +1003,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CreateChannels(RTCP, RTCP);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(2U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(2U, GetTransport2()->channels().size());
|
||||
EXPECT_TRUE(SendRtcp1());
|
||||
@ -1007,6 +1020,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CreateChannels(RTCP | RTCP_MUX, RTCP);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(2U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(2U, GetTransport2()->channels().size());
|
||||
EXPECT_TRUE(SendRtcp1());
|
||||
@ -1021,6 +1036,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
void SendRtcpMuxToRtcpMux() {
|
||||
CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(2U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(1U, GetTransport2()->channels().size());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
@ -1045,6 +1062,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX);
|
||||
channel1_->ActivateRtcpMux();
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(1U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(1U, GetTransport2()->channels().size());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
@ -1068,6 +1087,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX);
|
||||
channel2_->ActivateRtcpMux();
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(2U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(1U, GetTransport2()->channels().size());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
@ -1093,6 +1114,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
channel1_->ActivateRtcpMux();
|
||||
channel2_->ActivateRtcpMux();
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(1U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(1U, GetTransport2()->channels().size());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
@ -1117,6 +1140,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CreateChannels(RTCP | RTCP_MUX, RTCP);
|
||||
channel1_->ActivateRtcpMux();
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(1U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(2U, GetTransport2()->channels().size());
|
||||
EXPECT_FALSE(SendAccept());
|
||||
@ -1126,6 +1151,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
void SendEarlyRtcpMuxToRtcp() {
|
||||
CreateChannels(RTCP | RTCP_MUX, RTCP);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(2U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(2U, GetTransport2()->channels().size());
|
||||
|
||||
@ -1156,6 +1183,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
void SendEarlyRtcpMuxToRtcpMux() {
|
||||
CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(2U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(1U, GetTransport2()->channels().size());
|
||||
|
||||
@ -1246,6 +1275,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
EXPECT_TRUE(SendProvisionalAnswer());
|
||||
EXPECT_TRUE(channel1_->secure());
|
||||
EXPECT_TRUE(channel2_->secure());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(2U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(2U, GetTransport2()->channels().size());
|
||||
EXPECT_TRUE(SendCustomRtcp1(kSsrc1));
|
||||
@ -1329,6 +1360,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CreateChannels(0, 0);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(1U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(1U, GetTransport2()->channels().size());
|
||||
EXPECT_TRUE(SendRtp1());
|
||||
@ -1393,6 +1426,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
}
|
||||
CreateChannels(flags, flags);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(2U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(expected_channels, GetTransport2()->channels().size());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
@ -1581,6 +1616,8 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
CreateChannels(RTCP, RTCP);
|
||||
EXPECT_TRUE(SendInitiate());
|
||||
EXPECT_TRUE(SendAccept());
|
||||
ASSERT_TRUE(GetTransport1());
|
||||
ASSERT_TRUE(GetTransport2());
|
||||
EXPECT_EQ(2U, GetTransport1()->channels().size());
|
||||
EXPECT_EQ(2U, GetTransport2()->channels().size());
|
||||
|
||||
@ -1669,15 +1706,15 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
EXPECT_TRUE(media_channel1_->ready_to_send());
|
||||
|
||||
// rtp channel becomes not ready to send will be propagated to mediachannel
|
||||
channel1_->SetReadyToSend(rtp, false);
|
||||
channel1_->SetReadyToSend(false, false);
|
||||
EXPECT_FALSE(media_channel1_->ready_to_send());
|
||||
channel1_->SetReadyToSend(rtp, true);
|
||||
channel1_->SetReadyToSend(false, true);
|
||||
EXPECT_TRUE(media_channel1_->ready_to_send());
|
||||
|
||||
// rtcp channel becomes not ready to send will be propagated to mediachannel
|
||||
channel1_->SetReadyToSend(rtcp, false);
|
||||
channel1_->SetReadyToSend(true, false);
|
||||
EXPECT_FALSE(media_channel1_->ready_to_send());
|
||||
channel1_->SetReadyToSend(rtcp, true);
|
||||
channel1_->SetReadyToSend(true, true);
|
||||
EXPECT_TRUE(media_channel1_->ready_to_send());
|
||||
}
|
||||
|
||||
@ -1696,13 +1733,13 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
||||
// should trigger the MediaChannel's OnReadyToSend.
|
||||
rtp->SignalReadyToSend(rtp);
|
||||
EXPECT_TRUE(media_channel1_->ready_to_send());
|
||||
channel1_->SetReadyToSend(rtp, false);
|
||||
channel1_->SetReadyToSend(false, false);
|
||||
EXPECT_FALSE(media_channel1_->ready_to_send());
|
||||
}
|
||||
|
||||
protected:
|
||||
cricket::FakeSession session1_;
|
||||
cricket::FakeSession session2_;
|
||||
cricket::FakeTransportController transport_controller1_;
|
||||
cricket::FakeTransportController transport_controller2_;
|
||||
cricket::FakeMediaEngine media_engine_;
|
||||
// The media channels are owned by the voice channel objects below.
|
||||
typename T::MediaChannel* media_channel1_;
|
||||
@ -1763,18 +1800,21 @@ class VoiceChannelTest
|
||||
: public ChannelTest<VoiceTraits> {
|
||||
public:
|
||||
typedef ChannelTest<VoiceTraits> Base;
|
||||
VoiceChannelTest() : Base(kPcmuFrame, sizeof(kPcmuFrame),
|
||||
kRtcpReport, sizeof(kRtcpReport)) {}
|
||||
VoiceChannelTest()
|
||||
: Base(kPcmuFrame, sizeof(kPcmuFrame), kRtcpReport, sizeof(kRtcpReport)) {
|
||||
}
|
||||
};
|
||||
|
||||
// override to add NULL parameter
|
||||
template<>
|
||||
template <>
|
||||
cricket::VideoChannel* ChannelTest<VideoTraits>::CreateChannel(
|
||||
rtc::Thread* thread, cricket::MediaEngineInterface* engine,
|
||||
cricket::FakeVideoMediaChannel* ch, cricket::BaseSession* session,
|
||||
rtc::Thread* thread,
|
||||
cricket::MediaEngineInterface* engine,
|
||||
cricket::FakeVideoMediaChannel* ch,
|
||||
cricket::TransportController* transport_controller,
|
||||
bool rtcp) {
|
||||
cricket::VideoChannel* channel = new cricket::VideoChannel(
|
||||
thread, ch, session, cricket::CN_VIDEO, rtcp);
|
||||
thread, ch, transport_controller, cricket::CN_VIDEO, rtcp);
|
||||
if (!channel->Init()) {
|
||||
delete channel;
|
||||
channel = NULL;
|
||||
@ -1827,8 +1867,11 @@ class VideoChannelTest
|
||||
: public ChannelTest<VideoTraits> {
|
||||
public:
|
||||
typedef ChannelTest<VideoTraits> Base;
|
||||
VideoChannelTest() : Base(kH264Packet, sizeof(kH264Packet),
|
||||
kRtcpReport, sizeof(kRtcpReport)) {}
|
||||
VideoChannelTest()
|
||||
: Base(kH264Packet,
|
||||
sizeof(kH264Packet),
|
||||
kRtcpReport,
|
||||
sizeof(kRtcpReport)) {}
|
||||
};
|
||||
|
||||
|
||||
@ -2519,13 +2562,15 @@ class DataChannelTest
|
||||
};
|
||||
|
||||
// Override to avoid engine channel parameter.
|
||||
template<>
|
||||
template <>
|
||||
cricket::DataChannel* ChannelTest<DataTraits>::CreateChannel(
|
||||
rtc::Thread* thread, cricket::MediaEngineInterface* engine,
|
||||
cricket::FakeDataMediaChannel* ch, cricket::BaseSession* session,
|
||||
rtc::Thread* thread,
|
||||
cricket::MediaEngineInterface* engine,
|
||||
cricket::FakeDataMediaChannel* ch,
|
||||
cricket::TransportController* transport_controller,
|
||||
bool rtcp) {
|
||||
cricket::DataChannel* channel = new cricket::DataChannel(
|
||||
thread, ch, session, cricket::CN_DATA, rtcp);
|
||||
thread, ch, transport_controller, cricket::CN_DATA, rtcp);
|
||||
if (!channel->Init()) {
|
||||
delete channel;
|
||||
channel = NULL;
|
||||
|
||||
@ -318,23 +318,18 @@ void ChannelManager::Terminate_w() {
|
||||
|
||||
VoiceChannel* ChannelManager::CreateVoiceChannel(
|
||||
webrtc::MediaControllerInterface* media_controller,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
const AudioOptions& options) {
|
||||
return worker_thread_->Invoke<VoiceChannel*>(
|
||||
Bind(&ChannelManager::CreateVoiceChannel_w,
|
||||
this,
|
||||
media_controller,
|
||||
session,
|
||||
content_name,
|
||||
rtcp,
|
||||
options));
|
||||
Bind(&ChannelManager::CreateVoiceChannel_w, this, media_controller,
|
||||
transport_controller, content_name, rtcp, options));
|
||||
}
|
||||
|
||||
VoiceChannel* ChannelManager::CreateVoiceChannel_w(
|
||||
webrtc::MediaControllerInterface* media_controller,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
const AudioOptions& options) {
|
||||
@ -346,9 +341,9 @@ VoiceChannel* ChannelManager::CreateVoiceChannel_w(
|
||||
if (!media_channel)
|
||||
return nullptr;
|
||||
|
||||
VoiceChannel* voice_channel = new VoiceChannel(
|
||||
worker_thread_, media_engine_.get(), media_channel,
|
||||
session, content_name, rtcp);
|
||||
VoiceChannel* voice_channel =
|
||||
new VoiceChannel(worker_thread_, media_engine_.get(), media_channel,
|
||||
transport_controller, content_name, rtcp);
|
||||
if (!voice_channel->Init()) {
|
||||
delete voice_channel;
|
||||
return nullptr;
|
||||
@ -379,23 +374,18 @@ void ChannelManager::DestroyVoiceChannel_w(VoiceChannel* voice_channel) {
|
||||
|
||||
VideoChannel* ChannelManager::CreateVideoChannel(
|
||||
webrtc::MediaControllerInterface* media_controller,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
const VideoOptions& options) {
|
||||
return worker_thread_->Invoke<VideoChannel*>(
|
||||
Bind(&ChannelManager::CreateVideoChannel_w,
|
||||
this,
|
||||
media_controller,
|
||||
session,
|
||||
content_name,
|
||||
rtcp,
|
||||
options));
|
||||
Bind(&ChannelManager::CreateVideoChannel_w, this, media_controller,
|
||||
transport_controller, content_name, rtcp, options));
|
||||
}
|
||||
|
||||
VideoChannel* ChannelManager::CreateVideoChannel_w(
|
||||
webrtc::MediaControllerInterface* media_controller,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
const VideoOptions& options) {
|
||||
@ -404,12 +394,12 @@ VideoChannel* ChannelManager::CreateVideoChannel_w(
|
||||
ASSERT(nullptr != media_controller);
|
||||
VideoMediaChannel* media_channel =
|
||||
media_engine_->CreateVideoChannel(media_controller->call_w(), options);
|
||||
if (media_channel == NULL)
|
||||
if (media_channel == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VideoChannel* video_channel = new VideoChannel(
|
||||
worker_thread_, media_channel,
|
||||
session, content_name, rtcp);
|
||||
worker_thread_, media_channel, transport_controller, content_name, rtcp);
|
||||
if (!video_channel->Init()) {
|
||||
delete video_channel;
|
||||
return NULL;
|
||||
@ -440,16 +430,20 @@ void ChannelManager::DestroyVideoChannel_w(VideoChannel* video_channel) {
|
||||
}
|
||||
|
||||
DataChannel* ChannelManager::CreateDataChannel(
|
||||
BaseSession* session, const std::string& content_name,
|
||||
bool rtcp, DataChannelType channel_type) {
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
DataChannelType channel_type) {
|
||||
return worker_thread_->Invoke<DataChannel*>(
|
||||
Bind(&ChannelManager::CreateDataChannel_w, this, session, content_name,
|
||||
rtcp, channel_type));
|
||||
Bind(&ChannelManager::CreateDataChannel_w, this, transport_controller,
|
||||
content_name, rtcp, channel_type));
|
||||
}
|
||||
|
||||
DataChannel* ChannelManager::CreateDataChannel_w(
|
||||
BaseSession* session, const std::string& content_name,
|
||||
bool rtcp, DataChannelType data_channel_type) {
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
DataChannelType data_channel_type) {
|
||||
// This is ok to alloc from a thread other than the worker thread.
|
||||
ASSERT(initialized_);
|
||||
DataMediaChannel* media_channel = data_media_engine_->CreateChannel(
|
||||
@ -461,8 +455,7 @@ DataChannel* ChannelManager::CreateDataChannel_w(
|
||||
}
|
||||
|
||||
DataChannel* data_channel = new DataChannel(
|
||||
worker_thread_, media_channel,
|
||||
session, content_name, rtcp);
|
||||
worker_thread_, media_channel, transport_controller, content_name, rtcp);
|
||||
if (!data_channel->Init()) {
|
||||
LOG(LS_WARNING) << "Failed to init data channel.";
|
||||
delete data_channel;
|
||||
|
||||
@ -105,11 +105,10 @@ class ChannelManager : public rtc::MessageHandler,
|
||||
void Terminate();
|
||||
|
||||
// The operations below all occur on the worker thread.
|
||||
|
||||
// Creates a voice channel, to be associated with the specified session.
|
||||
VoiceChannel* CreateVoiceChannel(
|
||||
webrtc::MediaControllerInterface* media_controller,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
const AudioOptions& options);
|
||||
@ -119,15 +118,16 @@ class ChannelManager : public rtc::MessageHandler,
|
||||
// associated with the specified session.
|
||||
VideoChannel* CreateVideoChannel(
|
||||
webrtc::MediaControllerInterface* media_controller,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
const VideoOptions& options);
|
||||
// Destroys a video channel created with the Create API.
|
||||
void DestroyVideoChannel(VideoChannel* video_channel);
|
||||
DataChannel* CreateDataChannel(
|
||||
BaseSession* session, const std::string& content_name,
|
||||
bool rtcp, DataChannelType data_channel_type);
|
||||
DataChannel* CreateDataChannel(TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
DataChannelType data_channel_type);
|
||||
// Destroys a data channel created with the Create API.
|
||||
void DestroyDataChannel(DataChannel* data_channel);
|
||||
|
||||
@ -249,21 +249,22 @@ class ChannelManager : public rtc::MessageHandler,
|
||||
void Terminate_w();
|
||||
VoiceChannel* CreateVoiceChannel_w(
|
||||
webrtc::MediaControllerInterface* media_controller,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
const AudioOptions& options);
|
||||
void DestroyVoiceChannel_w(VoiceChannel* voice_channel);
|
||||
VideoChannel* CreateVideoChannel_w(
|
||||
webrtc::MediaControllerInterface* media_controller,
|
||||
BaseSession* session,
|
||||
TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
const VideoOptions& options);
|
||||
void DestroyVideoChannel_w(VideoChannel* video_channel);
|
||||
DataChannel* CreateDataChannel_w(
|
||||
BaseSession* session, const std::string& content_name,
|
||||
bool rtcp, DataChannelType data_channel_type);
|
||||
DataChannel* CreateDataChannel_w(TransportController* transport_controller,
|
||||
const std::string& content_name,
|
||||
bool rtcp,
|
||||
DataChannelType data_channel_type);
|
||||
void DestroyDataChannel_w(DataChannel* data_channel);
|
||||
bool SetAudioOptions_w(const AudioOptions& options, int delay_offset,
|
||||
const Device* in_dev, const Device* out_dev);
|
||||
|
||||
@ -32,11 +32,11 @@
|
||||
#include "talk/media/base/testutils.h"
|
||||
#include "talk/media/devices/fakedevicemanager.h"
|
||||
#include "talk/media/webrtc/fakewebrtccall.h"
|
||||
#include "webrtc/p2p/base/fakesession.h"
|
||||
#include "talk/session/media/channelmanager.h"
|
||||
#include "webrtc/base/gunit.h"
|
||||
#include "webrtc/base/logging.h"
|
||||
#include "webrtc/base/thread.h"
|
||||
#include "webrtc/p2p/base/faketransportcontroller.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
@ -58,14 +58,20 @@ class FakeMediaController : public webrtc::MediaControllerInterface {
|
||||
}
|
||||
~FakeMediaController() override {}
|
||||
webrtc::Call* call_w() override { return call_; }
|
||||
|
||||
private:
|
||||
webrtc::Call* call_;
|
||||
};
|
||||
|
||||
class ChannelManagerTest : public testing::Test {
|
||||
protected:
|
||||
ChannelManagerTest() : fake_call_(webrtc::Call::Config()),
|
||||
fake_mc_(&fake_call_), fme_(NULL), fdm_(NULL), fcm_(NULL), cm_(NULL) {}
|
||||
ChannelManagerTest()
|
||||
: fake_call_(webrtc::Call::Config()),
|
||||
fake_mc_(&fake_call_),
|
||||
fme_(NULL),
|
||||
fdm_(NULL),
|
||||
fcm_(NULL),
|
||||
cm_(NULL) {}
|
||||
|
||||
virtual void SetUp() {
|
||||
fme_ = new cricket::FakeMediaEngine();
|
||||
@ -76,7 +82,8 @@ class ChannelManagerTest : public testing::Test {
|
||||
fcm_ = new cricket::FakeCaptureManager();
|
||||
cm_ = new cricket::ChannelManager(
|
||||
fme_, fdme_, fdm_, fcm_, rtc::Thread::Current());
|
||||
session_ = new cricket::FakeSession(true);
|
||||
transport_controller_ =
|
||||
new cricket::FakeTransportController(ICEROLE_CONTROLLING);
|
||||
|
||||
std::vector<std::string> in_device_list, out_device_list, vid_device_list;
|
||||
in_device_list.push_back("audio-in1");
|
||||
@ -91,7 +98,7 @@ class ChannelManagerTest : public testing::Test {
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete session_;
|
||||
delete transport_controller_;
|
||||
delete cm_;
|
||||
cm_ = NULL;
|
||||
fdm_ = NULL;
|
||||
@ -108,7 +115,7 @@ class ChannelManagerTest : public testing::Test {
|
||||
cricket::FakeDeviceManager* fdm_;
|
||||
cricket::FakeCaptureManager* fcm_;
|
||||
cricket::ChannelManager* cm_;
|
||||
cricket::FakeSession* session_;
|
||||
cricket::FakeTransportController* transport_controller_;
|
||||
};
|
||||
|
||||
// Test that we startup/shutdown properly.
|
||||
@ -139,15 +146,16 @@ TEST_F(ChannelManagerTest, StartupShutdownOnThread) {
|
||||
// Test that we can create and destroy a voice and video channel.
|
||||
TEST_F(ChannelManagerTest, CreateDestroyChannels) {
|
||||
EXPECT_TRUE(cm_->Init());
|
||||
cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
|
||||
&fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions());
|
||||
cricket::VoiceChannel* voice_channel =
|
||||
cm_->CreateVoiceChannel(&fake_mc_, transport_controller_,
|
||||
cricket::CN_AUDIO, false, AudioOptions());
|
||||
EXPECT_TRUE(voice_channel != nullptr);
|
||||
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
|
||||
&fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions());
|
||||
cricket::VideoChannel* video_channel =
|
||||
cm_->CreateVideoChannel(&fake_mc_, transport_controller_,
|
||||
cricket::CN_VIDEO, false, VideoOptions());
|
||||
EXPECT_TRUE(video_channel != nullptr);
|
||||
cricket::DataChannel* data_channel =
|
||||
cm_->CreateDataChannel(session_, cricket::CN_DATA,
|
||||
false, cricket::DCT_RTP);
|
||||
cricket::DataChannel* data_channel = cm_->CreateDataChannel(
|
||||
transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP);
|
||||
EXPECT_TRUE(data_channel != nullptr);
|
||||
cm_->DestroyVideoChannel(video_channel);
|
||||
cm_->DestroyVoiceChannel(voice_channel);
|
||||
@ -160,17 +168,19 @@ TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) {
|
||||
worker_.Start();
|
||||
EXPECT_TRUE(cm_->set_worker_thread(&worker_));
|
||||
EXPECT_TRUE(cm_->Init());
|
||||
delete session_;
|
||||
session_ = new cricket::FakeSession(&worker_, true);
|
||||
cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
|
||||
&fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions());
|
||||
delete transport_controller_;
|
||||
transport_controller_ =
|
||||
new cricket::FakeTransportController(&worker_, ICEROLE_CONTROLLING);
|
||||
cricket::VoiceChannel* voice_channel =
|
||||
cm_->CreateVoiceChannel(&fake_mc_, transport_controller_,
|
||||
cricket::CN_AUDIO, false, AudioOptions());
|
||||
EXPECT_TRUE(voice_channel != nullptr);
|
||||
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
|
||||
&fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions());
|
||||
cricket::VideoChannel* video_channel =
|
||||
cm_->CreateVideoChannel(&fake_mc_, transport_controller_,
|
||||
cricket::CN_VIDEO, false, VideoOptions());
|
||||
EXPECT_TRUE(video_channel != nullptr);
|
||||
cricket::DataChannel* data_channel =
|
||||
cm_->CreateDataChannel(session_, cricket::CN_DATA,
|
||||
false, cricket::DCT_RTP);
|
||||
cricket::DataChannel* data_channel = cm_->CreateDataChannel(
|
||||
transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP);
|
||||
EXPECT_TRUE(data_channel != nullptr);
|
||||
cm_->DestroyVideoChannel(video_channel);
|
||||
cm_->DestroyVoiceChannel(voice_channel);
|
||||
@ -182,21 +192,22 @@ TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) {
|
||||
// to create a cricket::TransportChannel
|
||||
TEST_F(ChannelManagerTest, NoTransportChannelTest) {
|
||||
EXPECT_TRUE(cm_->Init());
|
||||
session_->set_fail_channel_creation(true);
|
||||
transport_controller_->set_fail_channel_creation(true);
|
||||
// The test is useless unless the session does not fail creating
|
||||
// cricket::TransportChannel.
|
||||
ASSERT_TRUE(session_->CreateChannel(
|
||||
ASSERT_TRUE(transport_controller_->CreateTransportChannel_w(
|
||||
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP) == nullptr);
|
||||
|
||||
cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
|
||||
&fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions());
|
||||
cricket::VoiceChannel* voice_channel =
|
||||
cm_->CreateVoiceChannel(&fake_mc_, transport_controller_,
|
||||
cricket::CN_AUDIO, false, AudioOptions());
|
||||
EXPECT_TRUE(voice_channel == nullptr);
|
||||
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
|
||||
&fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions());
|
||||
cricket::VideoChannel* video_channel =
|
||||
cm_->CreateVideoChannel(&fake_mc_, transport_controller_,
|
||||
cricket::CN_VIDEO, false, VideoOptions());
|
||||
EXPECT_TRUE(video_channel == nullptr);
|
||||
cricket::DataChannel* data_channel =
|
||||
cm_->CreateDataChannel(session_, cricket::CN_DATA,
|
||||
false, cricket::DCT_RTP);
|
||||
cricket::DataChannel* data_channel = cm_->CreateDataChannel(
|
||||
transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP);
|
||||
EXPECT_TRUE(data_channel == nullptr);
|
||||
cm_->Terminate();
|
||||
}
|
||||
|
||||
@ -29,12 +29,7 @@ const int kFakeIPv6NetworkPrefixLength = 64;
|
||||
class FakeNetworkManager : public NetworkManagerBase,
|
||||
public MessageHandler {
|
||||
public:
|
||||
FakeNetworkManager()
|
||||
: thread_(Thread::Current()),
|
||||
next_index_(0),
|
||||
started_(false),
|
||||
sent_first_update_(false) {
|
||||
}
|
||||
FakeNetworkManager() : thread_(Thread::Current()) {}
|
||||
|
||||
typedef std::vector<SocketAddress> IfaceList;
|
||||
|
||||
@ -58,20 +53,18 @@ class FakeNetworkManager : public NetworkManagerBase,
|
||||
}
|
||||
|
||||
virtual void StartUpdating() {
|
||||
if (started_) {
|
||||
if (sent_first_update_)
|
||||
++start_count_;
|
||||
if (start_count_ == 1) {
|
||||
sent_first_update_ = false;
|
||||
thread_->Post(this);
|
||||
} else {
|
||||
if (sent_first_update_) {
|
||||
SignalNetworksChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
started_ = true;
|
||||
sent_first_update_ = false;
|
||||
thread_->Post(this);
|
||||
}
|
||||
|
||||
virtual void StopUpdating() {
|
||||
started_ = false;
|
||||
}
|
||||
virtual void StopUpdating() { --start_count_; }
|
||||
|
||||
// MessageHandler interface.
|
||||
virtual void OnMessage(Message* msg) {
|
||||
@ -82,7 +75,7 @@ class FakeNetworkManager : public NetworkManagerBase,
|
||||
|
||||
private:
|
||||
void DoUpdateNetworks() {
|
||||
if (!started_)
|
||||
if (start_count_ == 0)
|
||||
return;
|
||||
std::vector<Network*> networks;
|
||||
for (IfaceList::iterator it = ifaces_.begin();
|
||||
@ -111,9 +104,9 @@ class FakeNetworkManager : public NetworkManagerBase,
|
||||
|
||||
Thread* thread_;
|
||||
IfaceList ifaces_;
|
||||
int next_index_;
|
||||
bool started_;
|
||||
bool sent_first_update_;
|
||||
int next_index_ = 0;
|
||||
int start_count_ = 0;
|
||||
bool sent_first_update_ = false;
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
#ifndef WEBRTC_P2P_BASE_DTLSTRANSPORT_H_
|
||||
#define WEBRTC_P2P_BASE_DTLSTRANSPORT_H_
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/p2p/base/dtlstransportchannel.h"
|
||||
#include "webrtc/p2p/base/transport.h"
|
||||
|
||||
@ -23,33 +22,31 @@ namespace cricket {
|
||||
|
||||
class PortAllocator;
|
||||
|
||||
// Base should be a descendant of cricket::Transport
|
||||
// TODO(hbos): Add appropriate RTC_DCHECK thread checks to all methods.
|
||||
// Base should be a descendant of cricket::Transport and have a constructor
|
||||
// that takes a transport name and PortAllocator.
|
||||
//
|
||||
// Everything in this class should be called on the worker thread.
|
||||
template<class Base>
|
||||
class DtlsTransport : public Base {
|
||||
public:
|
||||
DtlsTransport(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
const std::string& content_name,
|
||||
DtlsTransport(const std::string& name,
|
||||
PortAllocator* allocator,
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate)
|
||||
: Base(signaling_thread, worker_thread, content_name, allocator),
|
||||
: Base(name, allocator),
|
||||
certificate_(certificate),
|
||||
secure_role_(rtc::SSL_CLIENT),
|
||||
ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10) {
|
||||
}
|
||||
ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10) {}
|
||||
|
||||
~DtlsTransport() {
|
||||
Base::DestroyAllChannels();
|
||||
}
|
||||
void SetCertificate_w(
|
||||
|
||||
void SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override {
|
||||
RTC_DCHECK(Base::worker_thread()->IsCurrent());
|
||||
certificate_ = certificate;
|
||||
}
|
||||
bool GetCertificate_w(
|
||||
bool GetLocalCertificate(
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override {
|
||||
RTC_DCHECK(Base::worker_thread()->IsCurrent());
|
||||
if (!certificate_)
|
||||
return false;
|
||||
|
||||
@ -57,15 +54,13 @@ class DtlsTransport : public Base {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetSslMaxProtocolVersion_w(rtc::SSLProtocolVersion version) override {
|
||||
RTC_DCHECK(Base::worker_thread()->IsCurrent());
|
||||
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override {
|
||||
ssl_max_version_ = version;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel,
|
||||
std::string* error_desc) override {
|
||||
RTC_DCHECK(Base::worker_thread()->IsCurrent());
|
||||
bool ApplyLocalTransportDescription(TransportChannelImpl* channel,
|
||||
std::string* error_desc) override {
|
||||
rtc::SSLFingerprint* local_fp =
|
||||
Base::local_description()->identity_fingerprint.get();
|
||||
|
||||
@ -98,12 +93,11 @@ class DtlsTransport : public Base {
|
||||
}
|
||||
|
||||
// Apply the description in the base class.
|
||||
return Base::ApplyLocalTransportDescription_w(channel, error_desc);
|
||||
return Base::ApplyLocalTransportDescription(channel, error_desc);
|
||||
}
|
||||
|
||||
bool NegotiateTransportDescription_w(ContentAction local_role,
|
||||
std::string* error_desc) override {
|
||||
RTC_DCHECK(Base::worker_thread()->IsCurrent());
|
||||
bool NegotiateTransportDescription(ContentAction local_role,
|
||||
std::string* error_desc) override {
|
||||
if (!Base::local_description() || !Base::remote_description()) {
|
||||
const std::string msg = "Local and Remote description must be set before "
|
||||
"transport descriptions are negotiated";
|
||||
@ -200,7 +194,7 @@ class DtlsTransport : public Base {
|
||||
}
|
||||
|
||||
// Now run the negotiation for the base class.
|
||||
return Base::NegotiateTransportDescription_w(local_role, error_desc);
|
||||
return Base::NegotiateTransportDescription(local_role, error_desc);
|
||||
}
|
||||
|
||||
DtlsTransportChannelWrapper* CreateTransportChannel(int component) override {
|
||||
@ -219,18 +213,15 @@ class DtlsTransport : public Base {
|
||||
Base::DestroyTransportChannel(base_channel);
|
||||
}
|
||||
|
||||
bool GetSslRole_w(rtc::SSLRole* ssl_role) const override {
|
||||
RTC_DCHECK(Base::worker_thread()->IsCurrent());
|
||||
bool GetSslRole(rtc::SSLRole* ssl_role) const override {
|
||||
ASSERT(ssl_role != NULL);
|
||||
*ssl_role = secure_role_;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool ApplyNegotiatedTransportDescription_w(
|
||||
TransportChannelImpl* channel,
|
||||
std::string* error_desc) override {
|
||||
RTC_DCHECK(Base::worker_thread()->IsCurrent());
|
||||
bool ApplyNegotiatedTransportDescription(TransportChannelImpl* channel,
|
||||
std::string* error_desc) override {
|
||||
// Set ssl role. Role must be set before fingerprint is applied, which
|
||||
// initiates DTLS setup.
|
||||
if (!channel->SetSslRole(secure_role_)) {
|
||||
@ -245,7 +236,7 @@ class DtlsTransport : public Base {
|
||||
return BadTransportDescription("Failed to apply remote fingerprint.",
|
||||
error_desc);
|
||||
}
|
||||
return Base::ApplyNegotiatedTransportDescription_w(channel, error_desc);
|
||||
return Base::ApplyNegotiatedTransportDescription(channel, error_desc);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
|
||||
|
||||
@ -87,9 +87,9 @@ bool StreamInterfaceChannel::OnPacketReceived(const char* data, size_t size) {
|
||||
}
|
||||
|
||||
DtlsTransportChannelWrapper::DtlsTransportChannelWrapper(
|
||||
Transport* transport,
|
||||
TransportChannelImpl* channel)
|
||||
: TransportChannelImpl(channel->content_name(), channel->component()),
|
||||
Transport* transport,
|
||||
TransportChannelImpl* channel)
|
||||
: TransportChannelImpl(channel->transport_name(), channel->component()),
|
||||
transport_(transport),
|
||||
worker_thread_(rtc::Thread::Current()),
|
||||
channel_(channel),
|
||||
@ -103,12 +103,10 @@ DtlsTransportChannelWrapper::DtlsTransportChannelWrapper(
|
||||
&DtlsTransportChannelWrapper::OnReadPacket);
|
||||
channel_->SignalReadyToSend.connect(this,
|
||||
&DtlsTransportChannelWrapper::OnReadyToSend);
|
||||
channel_->SignalRequestSignaling.connect(this,
|
||||
&DtlsTransportChannelWrapper::OnRequestSignaling);
|
||||
channel_->SignalCandidateReady.connect(this,
|
||||
&DtlsTransportChannelWrapper::OnCandidateReady);
|
||||
channel_->SignalCandidatesAllocationDone.connect(this,
|
||||
&DtlsTransportChannelWrapper::OnCandidatesAllocationDone);
|
||||
channel_->SignalGatheringState.connect(
|
||||
this, &DtlsTransportChannelWrapper::OnGatheringState);
|
||||
channel_->SignalCandidateGathered.connect(
|
||||
this, &DtlsTransportChannelWrapper::OnCandidateGathered);
|
||||
channel_->SignalRoleConflict.connect(this,
|
||||
&DtlsTransportChannelWrapper::OnRoleConflict);
|
||||
channel_->SignalRouteChange.connect(this,
|
||||
@ -211,7 +209,7 @@ bool DtlsTransportChannelWrapper::SetRemoteFingerprint(
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow SetRemoteFingerprint with a NULL digest even if SetLocalIdentity
|
||||
// Allow SetRemoteFingerprint with a NULL digest even if SetLocalCertificate
|
||||
// hasn't been called.
|
||||
if (dtls_state_ > STATE_OFFERED ||
|
||||
(dtls_state_ == STATE_NONE && !digest_alg.empty())) {
|
||||
@ -591,22 +589,17 @@ bool DtlsTransportChannelWrapper::HandleDtlsPacket(const char* data,
|
||||
return downward_->OnPacketReceived(data, size);
|
||||
}
|
||||
|
||||
void DtlsTransportChannelWrapper::OnRequestSignaling(
|
||||
void DtlsTransportChannelWrapper::OnGatheringState(
|
||||
TransportChannelImpl* channel) {
|
||||
ASSERT(channel == channel_);
|
||||
SignalRequestSignaling(this);
|
||||
SignalGatheringState(this);
|
||||
}
|
||||
|
||||
void DtlsTransportChannelWrapper::OnCandidateReady(
|
||||
TransportChannelImpl* channel, const Candidate& c) {
|
||||
void DtlsTransportChannelWrapper::OnCandidateGathered(
|
||||
TransportChannelImpl* channel,
|
||||
const Candidate& c) {
|
||||
ASSERT(channel == channel_);
|
||||
SignalCandidateReady(this, c);
|
||||
}
|
||||
|
||||
void DtlsTransportChannelWrapper::OnCandidatesAllocationDone(
|
||||
TransportChannelImpl* channel) {
|
||||
ASSERT(channel == channel_);
|
||||
SignalCandidatesAllocationDone(this);
|
||||
SignalCandidateGathered(this, c);
|
||||
}
|
||||
|
||||
void DtlsTransportChannelWrapper::OnRoleConflict(
|
||||
|
||||
@ -27,7 +27,7 @@ namespace cricket {
|
||||
// the bottom and a StreamInterface on the top.
|
||||
class StreamInterfaceChannel : public rtc::StreamInterface {
|
||||
public:
|
||||
StreamInterfaceChannel(TransportChannel* channel);
|
||||
explicit StreamInterfaceChannel(TransportChannel* channel);
|
||||
|
||||
// Push in a packet; this gets pulled out from Read().
|
||||
bool OnPacketReceived(const char* data, size_t size);
|
||||
@ -35,10 +35,14 @@ class StreamInterfaceChannel : public rtc::StreamInterface {
|
||||
// Implementations of StreamInterface
|
||||
rtc::StreamState GetState() const override { return state_; }
|
||||
void Close() override { state_ = rtc::SS_CLOSED; }
|
||||
rtc::StreamResult Read(void* buffer, size_t buffer_len,
|
||||
size_t* read, int* error) override;
|
||||
rtc::StreamResult Write(const void* data, size_t data_len,
|
||||
size_t* written, int* error) override;
|
||||
rtc::StreamResult Read(void* buffer,
|
||||
size_t buffer_len,
|
||||
size_t* read,
|
||||
int* error) override;
|
||||
rtc::StreamResult Write(const void* data,
|
||||
size_t data_len,
|
||||
size_t* written,
|
||||
int* error) override;
|
||||
|
||||
private:
|
||||
TransportChannel* channel_; // owned by DtlsTransportChannelWrapper
|
||||
@ -93,12 +97,8 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl {
|
||||
TransportChannelImpl* channel);
|
||||
~DtlsTransportChannelWrapper() override;
|
||||
|
||||
void SetIceRole(IceRole role) override {
|
||||
channel_->SetIceRole(role);
|
||||
}
|
||||
IceRole GetIceRole() const override {
|
||||
return channel_->GetIceRole();
|
||||
}
|
||||
void SetIceRole(IceRole role) override { channel_->SetIceRole(role); }
|
||||
IceRole GetIceRole() const override { return channel_->GetIceRole(); }
|
||||
bool SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override;
|
||||
@ -109,7 +109,8 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl {
|
||||
bool IsDtlsActive() const override { return dtls_state_ != STATE_NONE; }
|
||||
|
||||
// Called to send a packet (via DTLS, if turned on).
|
||||
int SendPacket(const char* data, size_t size,
|
||||
int SendPacket(const char* data,
|
||||
size_t size,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) override;
|
||||
|
||||
@ -120,15 +121,11 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl {
|
||||
bool GetOption(rtc::Socket::Option opt, int* value) override {
|
||||
return channel_->GetOption(opt, value);
|
||||
}
|
||||
int GetError() override {
|
||||
return channel_->GetError();
|
||||
}
|
||||
int GetError() override { return channel_->GetError(); }
|
||||
bool GetStats(ConnectionInfos* infos) override {
|
||||
return channel_->GetStats(infos);
|
||||
}
|
||||
const std::string SessionId() const override {
|
||||
return channel_->SessionId();
|
||||
}
|
||||
const std::string SessionId() const override { return channel_->SessionId(); }
|
||||
|
||||
virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version);
|
||||
|
||||
@ -168,9 +165,7 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl {
|
||||
}
|
||||
|
||||
// TransportChannelImpl calls.
|
||||
Transport* GetTransport() override {
|
||||
return transport_;
|
||||
}
|
||||
Transport* GetTransport() override { return transport_; }
|
||||
|
||||
TransportChannelState GetState() const override {
|
||||
return channel_->GetState();
|
||||
@ -192,11 +187,14 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl {
|
||||
|
||||
void Connect() override;
|
||||
|
||||
void OnSignalingReady() override {
|
||||
channel_->OnSignalingReady();
|
||||
void MaybeStartGathering() override { channel_->MaybeStartGathering(); }
|
||||
|
||||
IceGatheringState gathering_state() const override {
|
||||
return channel_->gathering_state();
|
||||
}
|
||||
void OnCandidate(const Candidate& candidate) override {
|
||||
channel_->OnCandidate(candidate);
|
||||
|
||||
void AddRemoteCandidate(const Candidate& candidate) override {
|
||||
channel_->AddRemoteCandidate(candidate);
|
||||
}
|
||||
|
||||
void SetReceivingTimeout(int receiving_timeout_ms) override {
|
||||
@ -217,9 +215,8 @@ class DtlsTransportChannelWrapper : public TransportChannelImpl {
|
||||
bool SetupDtls();
|
||||
bool MaybeStartDtls();
|
||||
bool HandleDtlsPacket(const char* data, size_t size);
|
||||
void OnRequestSignaling(TransportChannelImpl* channel);
|
||||
void OnCandidateReady(TransportChannelImpl* channel, const Candidate& c);
|
||||
void OnCandidatesAllocationDone(TransportChannelImpl* channel);
|
||||
void OnGatheringState(TransportChannelImpl* channel);
|
||||
void OnCandidateGathered(TransportChannelImpl* channel, const Candidate& c);
|
||||
void OnRoleConflict(TransportChannelImpl* channel);
|
||||
void OnRouteChange(TransportChannel* channel, const Candidate& candidate);
|
||||
void OnConnectionRemoved(TransportChannelImpl* channel);
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
#include <set>
|
||||
|
||||
#include "webrtc/p2p/base/dtlstransport.h"
|
||||
#include "webrtc/p2p/base/fakesession.h"
|
||||
#include "webrtc/p2p/base/faketransportcontroller.h"
|
||||
#include "webrtc/base/common.h"
|
||||
#include "webrtc/base/dscp.h"
|
||||
#include "webrtc/base/gunit.h"
|
||||
@ -21,7 +21,6 @@
|
||||
#include "webrtc/base/sslidentity.h"
|
||||
#include "webrtc/base/sslstreamadapter.h"
|
||||
#include "webrtc/base/stringutils.h"
|
||||
#include "webrtc/base/thread.h"
|
||||
|
||||
#define MAYBE_SKIP_TEST(feature) \
|
||||
if (!(rtc::SSLStreamAdapter::feature())) { \
|
||||
@ -45,19 +44,14 @@ enum Flags { NF_REOFFER = 0x1, NF_EXPECT_FAILURE = 0x2 };
|
||||
|
||||
class DtlsTestClient : public sigslot::has_slots<> {
|
||||
public:
|
||||
DtlsTestClient(const std::string& name,
|
||||
rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread) :
|
||||
name_(name),
|
||||
signaling_thread_(signaling_thread),
|
||||
worker_thread_(worker_thread),
|
||||
packet_size_(0),
|
||||
use_dtls_srtp_(false),
|
||||
ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10),
|
||||
negotiated_dtls_(false),
|
||||
received_dtls_client_hello_(false),
|
||||
received_dtls_server_hello_(false) {
|
||||
}
|
||||
DtlsTestClient(const std::string& name)
|
||||
: name_(name),
|
||||
packet_size_(0),
|
||||
use_dtls_srtp_(false),
|
||||
ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10),
|
||||
negotiated_dtls_(false),
|
||||
received_dtls_client_hello_(false),
|
||||
received_dtls_server_hello_(false) {}
|
||||
void CreateCertificate(rtc::KeyType key_type) {
|
||||
certificate_ = rtc::RTCCertificate::Create(
|
||||
rtc::scoped_ptr<rtc::SSLIdentity>(
|
||||
@ -71,13 +65,12 @@ class DtlsTestClient : public sigslot::has_slots<> {
|
||||
use_dtls_srtp_ = true;
|
||||
}
|
||||
void SetupMaxProtocolVersion(rtc::SSLProtocolVersion version) {
|
||||
ASSERT(transport_.get() == NULL);
|
||||
ASSERT(!transport_);
|
||||
ssl_max_version_ = version;
|
||||
}
|
||||
void SetupChannels(int count, cricket::IceRole role) {
|
||||
transport_.reset(new cricket::DtlsTransport<cricket::FakeTransport>(
|
||||
signaling_thread_, worker_thread_, "dtls content name", nullptr,
|
||||
certificate_));
|
||||
"dtls content name", nullptr, certificate_));
|
||||
transport_->SetAsync(true);
|
||||
transport_->SetIceRole(role);
|
||||
transport_->SetIceTiebreaker(
|
||||
@ -118,8 +111,8 @@ class DtlsTestClient : public sigslot::has_slots<> {
|
||||
void Negotiate(DtlsTestClient* peer, cricket::ContentAction action,
|
||||
ConnectionRole local_role, ConnectionRole remote_role,
|
||||
int flags) {
|
||||
Negotiate(certificate_, certificate_ ? peer->certificate_ : nullptr,
|
||||
action, local_role, remote_role, flags);
|
||||
Negotiate(certificate_, certificate_ ? peer->certificate_ : nullptr, action,
|
||||
local_role, remote_role, flags);
|
||||
}
|
||||
|
||||
// Allow any DTLS configuration to be specified (including invalid ones).
|
||||
@ -163,18 +156,18 @@ class DtlsTestClient : public sigslot::has_slots<> {
|
||||
}
|
||||
|
||||
cricket::TransportDescription local_desc(
|
||||
std::vector<std::string>(), kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, local_role,
|
||||
// If remote if the offerer and has no DTLS support, answer will be
|
||||
std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
|
||||
local_role,
|
||||
// If remote if the offerer and has no DTLS support, answer will be
|
||||
// without any fingerprint.
|
||||
(action == cricket::CA_ANSWER && !remote_cert) ?
|
||||
NULL : local_fingerprint.get(),
|
||||
(action == cricket::CA_ANSWER && !remote_cert)
|
||||
? nullptr
|
||||
: local_fingerprint.get(),
|
||||
cricket::Candidates());
|
||||
|
||||
cricket::TransportDescription remote_desc(
|
||||
std::vector<std::string>(), kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, remote_role, remote_fingerprint.get(),
|
||||
cricket::Candidates());
|
||||
std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
|
||||
remote_role, remote_fingerprint.get(), cricket::Candidates());
|
||||
|
||||
bool expect_success = (flags & NF_EXPECT_FAILURE) ? false : true;
|
||||
// If |expect_success| is false, expect SRTD or SLTD to fail when
|
||||
@ -199,7 +192,9 @@ class DtlsTestClient : public sigslot::has_slots<> {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writable() const { return transport_->writable(); }
|
||||
bool all_channels_writable() const {
|
||||
return transport_->all_channels_writable();
|
||||
}
|
||||
|
||||
void CheckRole(rtc::SSLRole role) {
|
||||
if (role == rtc::SSL_CLIENT) {
|
||||
@ -337,8 +332,8 @@ class DtlsTestClient : public sigslot::has_slots<> {
|
||||
ASSERT_TRUE(VerifyPacket(data, size, &packet_num));
|
||||
received_.insert(packet_num);
|
||||
// Only DTLS-SRTP packets should have the bypass flag set.
|
||||
int expected_flags = (certificate_ && IsRtpLeadByte(data[0])) ?
|
||||
cricket::PF_SRTP_BYPASS : 0;
|
||||
int expected_flags =
|
||||
(certificate_ && IsRtpLeadByte(data[0])) ? cricket::PF_SRTP_BYPASS : 0;
|
||||
ASSERT_EQ(expected_flags, flags);
|
||||
}
|
||||
|
||||
@ -372,8 +367,6 @@ class DtlsTestClient : public sigslot::has_slots<> {
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
rtc::Thread* signaling_thread_;
|
||||
rtc::Thread* worker_thread_;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
|
||||
rtc::scoped_ptr<cricket::FakeTransport> transport_;
|
||||
std::vector<cricket::DtlsTransportChannelWrapper*> channels_;
|
||||
@ -389,16 +382,13 @@ class DtlsTestClient : public sigslot::has_slots<> {
|
||||
|
||||
class DtlsTransportChannelTest : public testing::Test {
|
||||
public:
|
||||
DtlsTransportChannelTest() :
|
||||
client1_("P1", rtc::Thread::Current(),
|
||||
rtc::Thread::Current()),
|
||||
client2_("P2", rtc::Thread::Current(),
|
||||
rtc::Thread::Current()),
|
||||
channel_ct_(1),
|
||||
use_dtls_(false),
|
||||
use_dtls_srtp_(false),
|
||||
ssl_expected_version_(rtc::SSL_PROTOCOL_DTLS_10) {
|
||||
}
|
||||
DtlsTransportChannelTest()
|
||||
: client1_("P1"),
|
||||
client2_("P2"),
|
||||
channel_ct_(1),
|
||||
use_dtls_(false),
|
||||
use_dtls_srtp_(false),
|
||||
ssl_expected_version_(rtc::SSL_PROTOCOL_DTLS_10) {}
|
||||
|
||||
void SetChannelCount(size_t channel_ct) {
|
||||
channel_ct_ = static_cast<int>(channel_ct);
|
||||
@ -440,8 +430,10 @@ class DtlsTransportChannelTest : public testing::Test {
|
||||
if (!rv)
|
||||
return false;
|
||||
|
||||
EXPECT_TRUE_WAIT(client1_.writable() && client2_.writable(), 10000);
|
||||
if (!client1_.writable() || !client2_.writable())
|
||||
EXPECT_TRUE_WAIT(
|
||||
client1_.all_channels_writable() && client2_.all_channels_writable(),
|
||||
10000);
|
||||
if (!client1_.all_channels_writable() || !client2_.all_channels_writable())
|
||||
return false;
|
||||
|
||||
// Check that we used the right roles.
|
||||
@ -818,7 +810,9 @@ TEST_F(DtlsTransportChannelTest, TestRenegotiateBeforeConnect) {
|
||||
cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER);
|
||||
bool rv = client1_.Connect(&client2_);
|
||||
EXPECT_TRUE(rv);
|
||||
EXPECT_TRUE_WAIT(client1_.writable() && client2_.writable(), 10000);
|
||||
EXPECT_TRUE_WAIT(
|
||||
client1_.all_channels_writable() && client2_.all_channels_writable(),
|
||||
10000);
|
||||
|
||||
TestTransfer(0, 1000, 100, true);
|
||||
TestTransfer(1, 1000, 100, true);
|
||||
@ -837,8 +831,8 @@ TEST_F(DtlsTransportChannelTest, TestCertificatesBeforeConnect) {
|
||||
|
||||
// After negotiation, each side has a distinct local certificate, but still no
|
||||
// remote certificate, because connection has not yet occurred.
|
||||
ASSERT_TRUE(client1_.transport()->GetCertificate(&certificate1));
|
||||
ASSERT_TRUE(client2_.transport()->GetCertificate(&certificate2));
|
||||
ASSERT_TRUE(client1_.transport()->GetLocalCertificate(&certificate1));
|
||||
ASSERT_TRUE(client2_.transport()->GetLocalCertificate(&certificate2));
|
||||
ASSERT_NE(certificate1->ssl_certificate().ToPEMString(),
|
||||
certificate2->ssl_certificate().ToPEMString());
|
||||
ASSERT_FALSE(
|
||||
@ -861,8 +855,8 @@ TEST_F(DtlsTransportChannelTest, TestCertificatesAfterConnect) {
|
||||
rtc::scoped_ptr<rtc::SSLCertificate> remote_cert2;
|
||||
|
||||
// After connection, each side has a distinct local certificate.
|
||||
ASSERT_TRUE(client1_.transport()->GetCertificate(&certificate1));
|
||||
ASSERT_TRUE(client2_.transport()->GetCertificate(&certificate2));
|
||||
ASSERT_TRUE(client1_.transport()->GetLocalCertificate(&certificate1));
|
||||
ASSERT_TRUE(client2_.transport()->GetLocalCertificate(&certificate2));
|
||||
ASSERT_NE(certificate1->ssl_certificate().ToPEMString(),
|
||||
certificate2->ssl_certificate().ToPEMString());
|
||||
|
||||
|
||||
@ -8,30 +8,31 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_P2P_BASE_FAKESESSION_H_
|
||||
#define WEBRTC_P2P_BASE_FAKESESSION_H_
|
||||
#ifndef WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_
|
||||
#define WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/p2p/base/session.h"
|
||||
#include "webrtc/p2p/base/transport.h"
|
||||
#include "webrtc/p2p/base/transportchannel.h"
|
||||
#include "webrtc/p2p/base/transportcontroller.h"
|
||||
#include "webrtc/p2p/base/transportchannelimpl.h"
|
||||
#include "webrtc/base/bind.h"
|
||||
#include "webrtc/base/buffer.h"
|
||||
#include "webrtc/base/fakesslidentity.h"
|
||||
#include "webrtc/base/messagequeue.h"
|
||||
#include "webrtc/base/sigslot.h"
|
||||
#include "webrtc/base/sslfingerprint.h"
|
||||
#include "webrtc/base/thread.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class FakeTransport;
|
||||
|
||||
struct PacketMessageData : public rtc::MessageData {
|
||||
PacketMessageData(const char* data, size_t len) : packet(data, len) {
|
||||
}
|
||||
PacketMessageData(const char* data, size_t len) : packet(data, len) {}
|
||||
rtc::Buffer packet;
|
||||
};
|
||||
|
||||
@ -43,24 +44,12 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
public rtc::MessageHandler {
|
||||
public:
|
||||
explicit FakeTransportChannel(Transport* transport,
|
||||
const std::string& content_name,
|
||||
const std::string& name,
|
||||
int component)
|
||||
: TransportChannelImpl(content_name, component),
|
||||
: TransportChannelImpl(name, component),
|
||||
transport_(transport),
|
||||
dest_(nullptr),
|
||||
state_(STATE_INIT),
|
||||
async_(false),
|
||||
do_dtls_(false),
|
||||
role_(ICEROLE_UNKNOWN),
|
||||
tiebreaker_(0),
|
||||
remote_ice_mode_(ICEMODE_FULL),
|
||||
dtls_fingerprint_("", nullptr, 0),
|
||||
ssl_role_(rtc::SSL_CLIENT),
|
||||
connection_count_(0) {
|
||||
}
|
||||
~FakeTransportChannel() {
|
||||
Reset();
|
||||
}
|
||||
dtls_fingerprint_("", nullptr, 0) {}
|
||||
~FakeTransportChannel() { Reset(); }
|
||||
|
||||
uint64 IceTiebreaker() const { return tiebreaker_; }
|
||||
IceMode remote_ice_mode() const { return remote_ice_mode_; }
|
||||
@ -72,24 +61,23 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
return dtls_fingerprint_;
|
||||
}
|
||||
|
||||
void SetAsync(bool async) {
|
||||
async_ = async;
|
||||
}
|
||||
// If async, will send packets by "Post"-ing to message queue instead of
|
||||
// synchronously "Send"-ing.
|
||||
void SetAsync(bool async) { async_ = async; }
|
||||
|
||||
Transport* GetTransport() override {
|
||||
return transport_;
|
||||
}
|
||||
Transport* GetTransport() override { return transport_; }
|
||||
|
||||
TransportChannelState GetState() const override {
|
||||
if (connection_count_ == 0) {
|
||||
return TransportChannelState::STATE_FAILED;
|
||||
return had_connection_ ? TransportChannelState::STATE_FAILED
|
||||
: TransportChannelState::STATE_INIT;
|
||||
}
|
||||
|
||||
if (connection_count_ == 1) {
|
||||
return TransportChannelState::STATE_COMPLETED;
|
||||
}
|
||||
|
||||
return TransportChannelState::STATE_FAILED;
|
||||
return TransportChannelState::STATE_CONNECTING;
|
||||
}
|
||||
|
||||
void SetIceRole(IceRole role) override { role_ = role; }
|
||||
@ -109,7 +97,8 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
}
|
||||
|
||||
void SetRemoteIceMode(IceMode mode) override { remote_ice_mode_ = mode; }
|
||||
bool SetRemoteFingerprint(const std::string& alg, const uint8* digest,
|
||||
bool SetRemoteFingerprint(const std::string& alg,
|
||||
const uint8* digest,
|
||||
size_t digest_len) override {
|
||||
dtls_fingerprint_ = rtc::SSLFingerprint(alg, digest, digest_len);
|
||||
return true;
|
||||
@ -128,27 +117,37 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
state_ = STATE_CONNECTING;
|
||||
}
|
||||
}
|
||||
virtual void Reset() {
|
||||
|
||||
void MaybeStartGathering() override {
|
||||
if (gathering_state_ == kIceGatheringNew) {
|
||||
gathering_state_ = kIceGatheringGathering;
|
||||
SignalGatheringState(this);
|
||||
}
|
||||
}
|
||||
|
||||
IceGatheringState gathering_state() const override {
|
||||
return gathering_state_;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
if (state_ != STATE_INIT) {
|
||||
state_ = STATE_INIT;
|
||||
if (dest_) {
|
||||
dest_->state_ = STATE_INIT;
|
||||
dest_->dest_ = NULL;
|
||||
dest_ = NULL;
|
||||
dest_->dest_ = nullptr;
|
||||
dest_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetWritable(bool writable) {
|
||||
set_writable(writable);
|
||||
}
|
||||
void SetWritable(bool writable) { set_writable(writable); }
|
||||
|
||||
void SetDestination(FakeTransportChannel* dest) {
|
||||
if (state_ == STATE_CONNECTING && dest) {
|
||||
// This simulates the delivery of candidates.
|
||||
dest_ = dest;
|
||||
dest_->dest_ = this;
|
||||
if (certificate_ && dest_->certificate_) {
|
||||
if (local_cert_ && dest_->local_cert_) {
|
||||
do_dtls_ = true;
|
||||
dest_->do_dtls_ = true;
|
||||
NegotiateSrtpCiphers();
|
||||
@ -159,7 +158,7 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
dest_->set_writable(true);
|
||||
} else if (state_ == STATE_CONNECTED && !dest) {
|
||||
// Simulates loss of connectivity, by asymmetrically forgetting dest_.
|
||||
dest_ = NULL;
|
||||
dest_ = nullptr;
|
||||
state_ = STATE_CONNECTING;
|
||||
set_writable(false);
|
||||
}
|
||||
@ -168,18 +167,31 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
void SetConnectionCount(size_t connection_count) {
|
||||
size_t old_connection_count = connection_count_;
|
||||
connection_count_ = connection_count;
|
||||
if (connection_count)
|
||||
had_connection_ = true;
|
||||
if (connection_count_ < old_connection_count)
|
||||
SignalConnectionRemoved(this);
|
||||
}
|
||||
|
||||
void SetReceiving(bool receiving) {
|
||||
set_receiving(receiving);
|
||||
void SetCandidatesGatheringComplete() {
|
||||
if (gathering_state_ != kIceGatheringComplete) {
|
||||
gathering_state_ = kIceGatheringComplete;
|
||||
SignalGatheringState(this);
|
||||
}
|
||||
}
|
||||
|
||||
void SetReceivingTimeout(int timeout) override {}
|
||||
void SetReceiving(bool receiving) { set_receiving(receiving); }
|
||||
|
||||
int SendPacket(const char* data, size_t len,
|
||||
const rtc::PacketOptions& options, int flags) override {
|
||||
void SetReceivingTimeout(int timeout) override {
|
||||
receiving_timeout_ = timeout;
|
||||
}
|
||||
|
||||
int receiving_timeout() const { return receiving_timeout_; }
|
||||
|
||||
int SendPacket(const char* data,
|
||||
size_t len,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) override {
|
||||
if (state_ != STATE_CONNECTED) {
|
||||
return -1;
|
||||
}
|
||||
@ -196,32 +208,25 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
}
|
||||
return static_cast<int>(len);
|
||||
}
|
||||
int SetOption(rtc::Socket::Option opt, int value) override {
|
||||
return true;
|
||||
}
|
||||
bool GetOption(rtc::Socket::Option opt, int* value) override {
|
||||
return true;
|
||||
}
|
||||
int GetError() override {
|
||||
return 0;
|
||||
}
|
||||
int SetOption(rtc::Socket::Option opt, int value) override { return true; }
|
||||
bool GetOption(rtc::Socket::Option opt, int* value) override { return true; }
|
||||
int GetError() override { return 0; }
|
||||
|
||||
void OnSignalingReady() override {
|
||||
}
|
||||
void OnCandidate(const Candidate& candidate) override {
|
||||
void AddRemoteCandidate(const Candidate& candidate) override {
|
||||
remote_candidates_.push_back(candidate);
|
||||
}
|
||||
const Candidates& remote_candidates() const { return remote_candidates_; }
|
||||
|
||||
void OnMessage(rtc::Message* msg) override {
|
||||
PacketMessageData* data = static_cast<PacketMessageData*>(
|
||||
msg->pdata);
|
||||
PacketMessageData* data = static_cast<PacketMessageData*>(msg->pdata);
|
||||
dest_->SignalReadPacket(dest_, data->packet.data<char>(),
|
||||
data->packet.size(), rtc::CreatePacketTime(0), 0);
|
||||
delete data;
|
||||
}
|
||||
|
||||
bool SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override {
|
||||
certificate_ = certificate;
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
|
||||
local_cert_ = certificate;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -229,9 +234,7 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
remote_cert_ = cert;
|
||||
}
|
||||
|
||||
bool IsDtlsActive() const override {
|
||||
return do_dtls_;
|
||||
}
|
||||
bool IsDtlsActive() const override { return do_dtls_; }
|
||||
|
||||
bool SetSrtpCiphers(const std::vector<std::string>& ciphers) override {
|
||||
srtp_ciphers_ = ciphers;
|
||||
@ -246,13 +249,10 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetSslCipher(std::string* cipher) override {
|
||||
return false;
|
||||
}
|
||||
bool GetSslCipher(std::string* cipher) override { return false; }
|
||||
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>
|
||||
GetLocalCertificate() const override {
|
||||
return certificate_;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const {
|
||||
return local_cert_;
|
||||
}
|
||||
|
||||
bool GetRemoteSSLCertificate(rtc::SSLCertificate** cert) const override {
|
||||
@ -277,12 +277,12 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void NegotiateSrtpCiphers() {
|
||||
void NegotiateSrtpCiphers() {
|
||||
for (std::vector<std::string>::const_iterator it1 = srtp_ciphers_.begin();
|
||||
it1 != srtp_ciphers_.end(); ++it1) {
|
||||
it1 != srtp_ciphers_.end(); ++it1) {
|
||||
for (std::vector<std::string>::const_iterator it2 =
|
||||
dest_->srtp_ciphers_.begin();
|
||||
it2 != dest_->srtp_ciphers_.end(); ++it2) {
|
||||
dest_->srtp_ciphers_.begin();
|
||||
it2 != dest_->srtp_ciphers_.end(); ++it2) {
|
||||
if (*it1 == *it2) {
|
||||
chosen_srtp_cipher_ = *it1;
|
||||
dest_->chosen_srtp_cipher_ = *it2;
|
||||
@ -299,27 +299,39 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_ssl_max_protocol_version(rtc::SSLProtocolVersion version) {
|
||||
ssl_max_version_ = version;
|
||||
}
|
||||
rtc::SSLProtocolVersion ssl_max_protocol_version() const {
|
||||
return ssl_max_version_;
|
||||
}
|
||||
|
||||
private:
|
||||
enum State { STATE_INIT, STATE_CONNECTING, STATE_CONNECTED };
|
||||
Transport* transport_;
|
||||
FakeTransportChannel* dest_;
|
||||
State state_;
|
||||
bool async_;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
|
||||
rtc::FakeSSLCertificate* remote_cert_;
|
||||
bool do_dtls_;
|
||||
FakeTransportChannel* dest_ = nullptr;
|
||||
State state_ = STATE_INIT;
|
||||
bool async_ = false;
|
||||
Candidates remote_candidates_;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> local_cert_;
|
||||
rtc::FakeSSLCertificate* remote_cert_ = nullptr;
|
||||
bool do_dtls_ = false;
|
||||
std::vector<std::string> srtp_ciphers_;
|
||||
std::string chosen_srtp_cipher_;
|
||||
IceRole role_;
|
||||
uint64 tiebreaker_;
|
||||
int receiving_timeout_ = -1;
|
||||
IceRole role_ = ICEROLE_UNKNOWN;
|
||||
uint64 tiebreaker_ = 0;
|
||||
std::string ice_ufrag_;
|
||||
std::string ice_pwd_;
|
||||
std::string remote_ice_ufrag_;
|
||||
std::string remote_ice_pwd_;
|
||||
IceMode remote_ice_mode_;
|
||||
IceMode remote_ice_mode_ = ICEMODE_FULL;
|
||||
rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10;
|
||||
rtc::SSLFingerprint dtls_fingerprint_;
|
||||
rtc::SSLRole ssl_role_;
|
||||
size_t connection_count_;
|
||||
rtc::SSLRole ssl_role_ = rtc::SSL_CLIENT;
|
||||
size_t connection_count_ = 0;
|
||||
IceGatheringState gathering_state_ = kIceGatheringNew;
|
||||
bool had_connection_ = false;
|
||||
};
|
||||
|
||||
// Fake transport class, which can be passed to anything that needs a Transport.
|
||||
@ -328,67 +340,40 @@ class FakeTransportChannel : public TransportChannelImpl,
|
||||
class FakeTransport : public Transport {
|
||||
public:
|
||||
typedef std::map<int, FakeTransportChannel*> ChannelMap;
|
||||
FakeTransport(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
const std::string& content_name,
|
||||
PortAllocator* alllocator = nullptr)
|
||||
: Transport(signaling_thread, worker_thread,
|
||||
content_name, nullptr),
|
||||
dest_(nullptr),
|
||||
async_(false) {
|
||||
}
|
||||
~FakeTransport() {
|
||||
DestroyAllChannels();
|
||||
}
|
||||
|
||||
explicit FakeTransport(const std::string& name) : Transport(name, nullptr) {}
|
||||
|
||||
// Note that we only have a constructor with the allocator parameter so it can
|
||||
// be wrapped by a DtlsTransport.
|
||||
FakeTransport(const std::string& name, PortAllocator* allocator)
|
||||
: Transport(name, nullptr) {}
|
||||
|
||||
~FakeTransport() { DestroyAllChannels(); }
|
||||
|
||||
const ChannelMap& channels() const { return channels_; }
|
||||
|
||||
// If async, will send packets by "Post"-ing to message queue instead of
|
||||
// synchronously "Send"-ing.
|
||||
void SetAsync(bool async) { async_ = async; }
|
||||
void SetDestination(FakeTransport* dest) {
|
||||
dest_ = dest;
|
||||
for (ChannelMap::iterator it = channels_.begin(); it != channels_.end();
|
||||
++it) {
|
||||
it->second->SetLocalCertificate(certificate_);
|
||||
SetChannelDestination(it->first, it->second);
|
||||
for (const auto& kv : channels_) {
|
||||
kv.second->SetLocalCertificate(certificate_);
|
||||
SetChannelDestination(kv.first, kv.second);
|
||||
}
|
||||
}
|
||||
|
||||
void SetWritable(bool writable) {
|
||||
for (ChannelMap::iterator it = channels_.begin(); it != channels_.end();
|
||||
++it) {
|
||||
it->second->SetWritable(writable);
|
||||
for (const auto& kv : channels_) {
|
||||
kv.second->SetWritable(writable);
|
||||
}
|
||||
}
|
||||
|
||||
void set_certificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
|
||||
certificate_ = certificate;
|
||||
}
|
||||
|
||||
using Transport::local_description;
|
||||
using Transport::remote_description;
|
||||
|
||||
protected:
|
||||
TransportChannelImpl* CreateTransportChannel(int component) override {
|
||||
if (channels_.find(component) != channels_.end()) {
|
||||
return NULL;
|
||||
}
|
||||
FakeTransportChannel* channel =
|
||||
new FakeTransportChannel(this, content_name(), component);
|
||||
channel->SetAsync(async_);
|
||||
SetChannelDestination(component, channel);
|
||||
channels_[component] = channel;
|
||||
return channel;
|
||||
}
|
||||
void DestroyTransportChannel(TransportChannelImpl* channel) override {
|
||||
channels_.erase(channel->component());
|
||||
delete channel;
|
||||
}
|
||||
void SetCertificate_w(
|
||||
void SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override {
|
||||
certificate_ = certificate;
|
||||
}
|
||||
bool GetCertificate_w(
|
||||
bool GetLocalCertificate(
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override {
|
||||
if (!certificate_)
|
||||
return false;
|
||||
@ -397,103 +382,149 @@ class FakeTransport : public Transport {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetSslRole(rtc::SSLRole* role) const override {
|
||||
if (channels_.empty()) {
|
||||
return false;
|
||||
}
|
||||
return channels_.begin()->second->GetSslRole(role);
|
||||
}
|
||||
|
||||
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override {
|
||||
ssl_max_version_ = version;
|
||||
for (const auto& kv : channels_) {
|
||||
kv.second->set_ssl_max_protocol_version(ssl_max_version_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
rtc::SSLProtocolVersion ssl_max_protocol_version() const {
|
||||
return ssl_max_version_;
|
||||
}
|
||||
|
||||
using Transport::local_description;
|
||||
using Transport::remote_description;
|
||||
|
||||
protected:
|
||||
TransportChannelImpl* CreateTransportChannel(int component) override {
|
||||
if (channels_.find(component) != channels_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
FakeTransportChannel* channel =
|
||||
new FakeTransportChannel(this, name(), component);
|
||||
channel->set_ssl_max_protocol_version(ssl_max_version_);
|
||||
channel->SetAsync(async_);
|
||||
SetChannelDestination(component, channel);
|
||||
channels_[component] = channel;
|
||||
return channel;
|
||||
}
|
||||
|
||||
void DestroyTransportChannel(TransportChannelImpl* channel) override {
|
||||
channels_.erase(channel->component());
|
||||
delete channel;
|
||||
}
|
||||
|
||||
private:
|
||||
FakeTransportChannel* GetFakeChannel(int component) {
|
||||
ChannelMap::iterator it = channels_.find(component);
|
||||
return (it != channels_.end()) ? it->second : NULL;
|
||||
auto it = channels_.find(component);
|
||||
return (it != channels_.end()) ? it->second : nullptr;
|
||||
}
|
||||
void SetChannelDestination(int component,
|
||||
FakeTransportChannel* channel) {
|
||||
FakeTransportChannel* dest_channel = NULL;
|
||||
|
||||
void SetChannelDestination(int component, FakeTransportChannel* channel) {
|
||||
FakeTransportChannel* dest_channel = nullptr;
|
||||
if (dest_) {
|
||||
dest_channel = dest_->GetFakeChannel(component);
|
||||
if (dest_channel)
|
||||
if (dest_channel) {
|
||||
dest_channel->SetLocalCertificate(dest_->certificate_);
|
||||
}
|
||||
}
|
||||
channel->SetDestination(dest_channel);
|
||||
}
|
||||
|
||||
// Note, this is distinct from the Channel map owned by Transport.
|
||||
// This map just tracks the FakeTransportChannels created by this class.
|
||||
// It's mainly needed so that we can access a FakeTransportChannel directly,
|
||||
// even if wrapped by a DtlsTransportChannelWrapper.
|
||||
ChannelMap channels_;
|
||||
FakeTransport* dest_;
|
||||
bool async_;
|
||||
FakeTransport* dest_ = nullptr;
|
||||
bool async_ = false;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
|
||||
rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10;
|
||||
};
|
||||
|
||||
// Fake session class, which can be passed into a BaseChannel object for
|
||||
// test purposes. Can be connected to other FakeSessions via Connect().
|
||||
class FakeSession : public BaseSession {
|
||||
// Fake TransportController class, which can be passed into a BaseChannel object
|
||||
// for test purposes. Can be connected to other FakeTransportControllers via
|
||||
// Connect().
|
||||
//
|
||||
// This fake is unusual in that for the most part, it's implemented with the
|
||||
// real TransportController code, but with fake TransportChannels underneath.
|
||||
class FakeTransportController : public TransportController {
|
||||
public:
|
||||
explicit FakeSession()
|
||||
: BaseSession(rtc::Thread::Current(),
|
||||
rtc::Thread::Current(),
|
||||
NULL, "", "", true),
|
||||
fail_create_channel_(false) {
|
||||
}
|
||||
explicit FakeSession(bool initiator)
|
||||
: BaseSession(rtc::Thread::Current(),
|
||||
rtc::Thread::Current(),
|
||||
NULL, "", "", initiator),
|
||||
fail_create_channel_(false) {
|
||||
}
|
||||
FakeSession(rtc::Thread* worker_thread, bool initiator)
|
||||
: BaseSession(rtc::Thread::Current(),
|
||||
worker_thread,
|
||||
NULL, "", "", initiator),
|
||||
fail_create_channel_(false) {
|
||||
FakeTransportController()
|
||||
: TransportController(rtc::Thread::Current(),
|
||||
rtc::Thread::Current(),
|
||||
nullptr),
|
||||
fail_create_channel_(false) {}
|
||||
|
||||
explicit FakeTransportController(IceRole role)
|
||||
: TransportController(rtc::Thread::Current(),
|
||||
rtc::Thread::Current(),
|
||||
nullptr),
|
||||
fail_create_channel_(false) {
|
||||
SetIceRole(role);
|
||||
}
|
||||
|
||||
FakeTransport* GetTransport(const std::string& content_name) {
|
||||
explicit FakeTransportController(rtc::Thread* worker_thread)
|
||||
: TransportController(rtc::Thread::Current(), worker_thread, nullptr),
|
||||
fail_create_channel_(false) {}
|
||||
|
||||
FakeTransportController(rtc::Thread* worker_thread, IceRole role)
|
||||
: TransportController(rtc::Thread::Current(), worker_thread, nullptr),
|
||||
fail_create_channel_(false) {
|
||||
SetIceRole(role);
|
||||
}
|
||||
|
||||
FakeTransport* GetTransport_w(const std::string& transport_name) {
|
||||
return static_cast<FakeTransport*>(
|
||||
BaseSession::GetTransport(content_name));
|
||||
TransportController::GetTransport_w(transport_name));
|
||||
}
|
||||
|
||||
void Connect(FakeSession* dest) {
|
||||
// Simulate the exchange of candidates.
|
||||
CompleteNegotiation();
|
||||
dest->CompleteNegotiation();
|
||||
for (TransportMap::const_iterator it = transport_proxies().begin();
|
||||
it != transport_proxies().end(); ++it) {
|
||||
static_cast<FakeTransport*>(it->second->impl())->SetDestination(
|
||||
dest->GetTransport(it->first));
|
||||
}
|
||||
void Connect(FakeTransportController* dest) {
|
||||
worker_thread()->Invoke<void>(
|
||||
rtc::Bind(&FakeTransportController::Connect_w, this, dest));
|
||||
}
|
||||
|
||||
TransportChannel* CreateChannel(const std::string& content_name,
|
||||
int component) override {
|
||||
TransportChannel* CreateTransportChannel_w(const std::string& transport_name,
|
||||
int component) override {
|
||||
if (fail_create_channel_) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
return BaseSession::CreateChannel(content_name, component);
|
||||
return TransportController::CreateTransportChannel_w(transport_name,
|
||||
component);
|
||||
}
|
||||
|
||||
void set_fail_channel_creation(bool fail_channel_creation) {
|
||||
fail_create_channel_ = fail_channel_creation;
|
||||
}
|
||||
|
||||
// TODO: Hoist this into Session when we re-work the Session code.
|
||||
void set_ssl_rtccertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
|
||||
for (TransportMap::const_iterator it = transport_proxies().begin();
|
||||
it != transport_proxies().end(); ++it) {
|
||||
// We know that we have a FakeTransport*
|
||||
protected:
|
||||
Transport* CreateTransport_w(const std::string& transport_name) override {
|
||||
return new FakeTransport(transport_name);
|
||||
}
|
||||
|
||||
static_cast<FakeTransport*>(it->second->impl())->set_certificate
|
||||
(certificate);
|
||||
void Connect_w(FakeTransportController* dest) {
|
||||
// Simulate the exchange of candidates.
|
||||
ConnectChannels_w();
|
||||
dest->ConnectChannels_w();
|
||||
for (auto& kv : transports()) {
|
||||
FakeTransport* transport = static_cast<FakeTransport*>(kv.second);
|
||||
transport->SetDestination(dest->GetTransport_w(kv.first));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
Transport* CreateTransport(const std::string& content_name) override {
|
||||
return new FakeTransport(signaling_thread(), worker_thread(), content_name);
|
||||
}
|
||||
|
||||
void CompleteNegotiation() {
|
||||
for (TransportMap::const_iterator it = transport_proxies().begin();
|
||||
it != transport_proxies().end(); ++it) {
|
||||
it->second->CompleteNegotiation();
|
||||
it->second->ConnectChannels();
|
||||
void ConnectChannels_w() {
|
||||
for (auto& kv : transports()) {
|
||||
FakeTransport* transport = static_cast<FakeTransport*>(kv.second);
|
||||
transport->ConnectChannels();
|
||||
transport->MaybeStartGathering();
|
||||
}
|
||||
}
|
||||
|
||||
@ -503,4 +534,4 @@ class FakeSession : public BaseSession {
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // WEBRTC_P2P_BASE_FAKESESSION_H_
|
||||
#endif // WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_
|
||||
@ -20,21 +20,15 @@
|
||||
|
||||
namespace cricket {
|
||||
|
||||
P2PTransport::P2PTransport(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
const std::string& content_name,
|
||||
PortAllocator* allocator)
|
||||
: Transport(signaling_thread, worker_thread,
|
||||
content_name, allocator) {
|
||||
}
|
||||
P2PTransport::P2PTransport(const std::string& name, PortAllocator* allocator)
|
||||
: Transport(name, allocator) {}
|
||||
|
||||
P2PTransport::~P2PTransport() {
|
||||
DestroyAllChannels();
|
||||
}
|
||||
|
||||
TransportChannelImpl* P2PTransport::CreateTransportChannel(int component) {
|
||||
return new P2PTransportChannel(content_name(), component, this,
|
||||
port_allocator());
|
||||
return new P2PTransportChannel(name(), component, this, port_allocator());
|
||||
}
|
||||
|
||||
void P2PTransport::DestroyTransportChannel(TransportChannelImpl* channel) {
|
||||
|
||||
@ -16,12 +16,10 @@
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// Everything in this class should be called on the worker thread.
|
||||
class P2PTransport : public Transport {
|
||||
public:
|
||||
P2PTransport(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
const std::string& content_name,
|
||||
PortAllocator* allocator);
|
||||
P2PTransport(const std::string& name, PortAllocator* allocator);
|
||||
virtual ~P2PTransport();
|
||||
|
||||
protected:
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "webrtc/p2p/base/p2ptransportchannel.h"
|
||||
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include "webrtc/p2p/base/common.h"
|
||||
#include "webrtc/p2p/base/relayport.h" // For RELAY_PORT_TYPE.
|
||||
#include "webrtc/p2p/base/stunport.h" // For STUN_PORT_TYPE.
|
||||
@ -169,28 +170,27 @@ bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) {
|
||||
|
||||
namespace cricket {
|
||||
|
||||
P2PTransportChannel::P2PTransportChannel(const std::string& content_name,
|
||||
P2PTransportChannel::P2PTransportChannel(const std::string& transport_name,
|
||||
int component,
|
||||
P2PTransport* transport,
|
||||
PortAllocator *allocator) :
|
||||
TransportChannelImpl(content_name, component),
|
||||
transport_(transport),
|
||||
allocator_(allocator),
|
||||
worker_thread_(rtc::Thread::Current()),
|
||||
incoming_only_(false),
|
||||
waiting_for_signaling_(false),
|
||||
error_(0),
|
||||
best_connection_(NULL),
|
||||
pending_best_connection_(NULL),
|
||||
sort_dirty_(false),
|
||||
was_writable_(false),
|
||||
remote_ice_mode_(ICEMODE_FULL),
|
||||
ice_role_(ICEROLE_UNKNOWN),
|
||||
tiebreaker_(0),
|
||||
remote_candidate_generation_(0),
|
||||
check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5),
|
||||
receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) {
|
||||
}
|
||||
PortAllocator* allocator)
|
||||
: TransportChannelImpl(transport_name, component),
|
||||
transport_(transport),
|
||||
allocator_(allocator),
|
||||
worker_thread_(rtc::Thread::Current()),
|
||||
incoming_only_(false),
|
||||
error_(0),
|
||||
best_connection_(NULL),
|
||||
pending_best_connection_(NULL),
|
||||
sort_dirty_(false),
|
||||
was_writable_(false),
|
||||
remote_ice_mode_(ICEMODE_FULL),
|
||||
ice_role_(ICEROLE_UNKNOWN),
|
||||
tiebreaker_(0),
|
||||
remote_candidate_generation_(0),
|
||||
gathering_state_(kIceGatheringNew),
|
||||
check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5),
|
||||
receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) {}
|
||||
|
||||
P2PTransportChannel::~P2PTransportChannel() {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
@ -231,6 +231,7 @@ void P2PTransportChannel::AddConnection(Connection* connection) {
|
||||
connection->SignalDestroyed.connect(
|
||||
this, &P2PTransportChannel::OnConnectionDestroyed);
|
||||
connection->SignalNominated.connect(this, &P2PTransportChannel::OnNominated);
|
||||
had_connection_ = true;
|
||||
}
|
||||
|
||||
void P2PTransportChannel::SetIceRole(IceRole ice_role) {
|
||||
@ -265,8 +266,9 @@ void P2PTransportChannel::SetIceTiebreaker(uint64 tiebreaker) {
|
||||
TransportChannelState P2PTransportChannel::GetState() const {
|
||||
std::set<rtc::Network*> networks;
|
||||
|
||||
if (connections_.size() == 0) {
|
||||
return TransportChannelState::STATE_FAILED;
|
||||
if (connections_.empty()) {
|
||||
return had_connection_ ? TransportChannelState::STATE_FAILED
|
||||
: TransportChannelState::STATE_INIT;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < connections_.size(); ++i) {
|
||||
@ -288,21 +290,10 @@ TransportChannelState P2PTransportChannel::GetState() const {
|
||||
void P2PTransportChannel::SetIceCredentials(const std::string& ice_ufrag,
|
||||
const std::string& ice_pwd) {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
bool ice_restart = false;
|
||||
if (!ice_ufrag_.empty() && !ice_pwd_.empty()) {
|
||||
// Restart candidate allocation if there is any change in either
|
||||
// ice ufrag or password.
|
||||
ice_restart =
|
||||
IceCredentialsChanged(ice_ufrag_, ice_pwd_, ice_ufrag, ice_pwd);
|
||||
}
|
||||
|
||||
ice_ufrag_ = ice_ufrag;
|
||||
ice_pwd_ = ice_pwd;
|
||||
|
||||
if (ice_restart) {
|
||||
// Restart candidate gathering.
|
||||
Allocate();
|
||||
}
|
||||
// Note: Candidate gathering will restart when MaybeStartGathering is next
|
||||
// called.
|
||||
}
|
||||
|
||||
void P2PTransportChannel::SetRemoteIceCredentials(const std::string& ice_ufrag,
|
||||
@ -359,9 +350,6 @@ void P2PTransportChannel::Connect() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Kick off an allocator session
|
||||
Allocate();
|
||||
|
||||
// Start pinging as the ports come in.
|
||||
thread()->Post(this, MSG_PING);
|
||||
|
||||
@ -369,6 +357,22 @@ void P2PTransportChannel::Connect() {
|
||||
check_receiving_delay_, this, MSG_CHECK_RECEIVING);
|
||||
}
|
||||
|
||||
void P2PTransportChannel::MaybeStartGathering() {
|
||||
// Start gathering if we never started before, or if an ICE restart occurred.
|
||||
if (allocator_sessions_.empty() ||
|
||||
IceCredentialsChanged(allocator_sessions_.back()->ice_ufrag(),
|
||||
allocator_sessions_.back()->ice_pwd(), ice_ufrag_,
|
||||
ice_pwd_)) {
|
||||
if (gathering_state_ != kIceGatheringGathering) {
|
||||
gathering_state_ = kIceGatheringGathering;
|
||||
SignalGatheringState(this);
|
||||
}
|
||||
// Time for a new allocator
|
||||
AddAllocatorSession(allocator_->CreateSession(
|
||||
SessionId(), transport_name(), component(), ice_ufrag_, ice_pwd_));
|
||||
}
|
||||
}
|
||||
|
||||
// A new port is available, attempt to make connections for it
|
||||
void P2PTransportChannel::OnPortReady(PortAllocatorSession *session,
|
||||
PortInterface* port) {
|
||||
@ -413,17 +417,21 @@ void P2PTransportChannel::OnPortReady(PortAllocatorSession *session,
|
||||
|
||||
// A new candidate is available, let listeners know
|
||||
void P2PTransportChannel::OnCandidatesReady(
|
||||
PortAllocatorSession *session, const std::vector<Candidate>& candidates) {
|
||||
PortAllocatorSession* session,
|
||||
const std::vector<Candidate>& candidates) {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
for (size_t i = 0; i < candidates.size(); ++i) {
|
||||
SignalCandidateReady(this, candidates[i]);
|
||||
SignalCandidateGathered(this, candidates[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void P2PTransportChannel::OnCandidatesAllocationDone(
|
||||
PortAllocatorSession* session) {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
SignalCandidatesAllocationDone(this);
|
||||
gathering_state_ = kIceGatheringComplete;
|
||||
LOG(LS_INFO) << "P2PTransportChannel: " << transport_name() << ", component "
|
||||
<< component() << " gathering complete";
|
||||
SignalGatheringState(this);
|
||||
}
|
||||
|
||||
// Handle stun packets
|
||||
@ -494,8 +502,7 @@ void P2PTransportChannel::OnUnknownAddress(
|
||||
LOG(LS_WARNING) << "P2PTransportChannel::OnUnknownAddress - "
|
||||
<< "No STUN_ATTR_PRIORITY found in the "
|
||||
<< "stun request message";
|
||||
port->SendBindingErrorResponse(stun_msg, address,
|
||||
STUN_ERROR_BAD_REQUEST,
|
||||
port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_BAD_REQUEST,
|
||||
STUN_ERROR_REASON_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
@ -545,8 +552,7 @@ void P2PTransportChannel::OnUnknownAddress(
|
||||
remote_candidate, cricket::PortInterface::ORIGIN_THIS_PORT);
|
||||
if (!connection) {
|
||||
ASSERT(false);
|
||||
port->SendBindingErrorResponse(stun_msg, address,
|
||||
STUN_ERROR_SERVER_ERROR,
|
||||
port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR,
|
||||
STUN_ERROR_REASON_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
@ -575,16 +581,6 @@ void P2PTransportChannel::OnRoleConflict(PortInterface* port) {
|
||||
// from Transport.
|
||||
}
|
||||
|
||||
// When the signalling channel is ready, we can really kick off the allocator
|
||||
void P2PTransportChannel::OnSignalingReady() {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
if (waiting_for_signaling_) {
|
||||
waiting_for_signaling_ = false;
|
||||
AddAllocatorSession(allocator_->CreateSession(
|
||||
SessionId(), content_name(), component(), ice_ufrag_, ice_pwd_));
|
||||
}
|
||||
}
|
||||
|
||||
void P2PTransportChannel::OnNominated(Connection* conn) {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
ASSERT(ice_role_ == ICEROLE_CONTROLLED);
|
||||
@ -606,7 +602,7 @@ void P2PTransportChannel::OnNominated(Connection* conn) {
|
||||
}
|
||||
}
|
||||
|
||||
void P2PTransportChannel::OnCandidate(const Candidate& candidate) {
|
||||
void P2PTransportChannel::AddRemoteCandidate(const Candidate& candidate) {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
|
||||
uint32 generation = candidate.generation();
|
||||
@ -847,7 +843,7 @@ bool P2PTransportChannel::GetStats(ConnectionInfos *infos) {
|
||||
|
||||
std::vector<Connection *>::const_iterator it;
|
||||
for (it = connections_.begin(); it != connections_.end(); ++it) {
|
||||
Connection *connection = *it;
|
||||
Connection* connection = *it;
|
||||
ConnectionInfo info;
|
||||
info.best_connection = (best_connection_ == connection);
|
||||
info.receiving = connection->receiving();
|
||||
@ -881,14 +877,6 @@ rtc::DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const {
|
||||
return static_cast<rtc::DiffServCodePoint> (it->second);
|
||||
}
|
||||
|
||||
// Begin allocate (or immediately re-allocate, if MSG_ALLOCATE pending)
|
||||
void P2PTransportChannel::Allocate() {
|
||||
// Time for a new allocator, lets make sure we have a signalling channel
|
||||
// to communicate candidates through first.
|
||||
waiting_for_signaling_ = true;
|
||||
SignalRequestSignaling(this);
|
||||
}
|
||||
|
||||
// Monitor connection states.
|
||||
void P2PTransportChannel::UpdateConnectionStates() {
|
||||
uint32 now = rtc::Time();
|
||||
@ -1228,8 +1216,7 @@ Connection* P2PTransportChannel::FindNextPingableConnection() {
|
||||
void P2PTransportChannel::PingConnection(Connection* conn) {
|
||||
bool use_candidate = false;
|
||||
if (remote_ice_mode_ == ICEMODE_FULL && ice_role_ == ICEROLE_CONTROLLING) {
|
||||
use_candidate = (conn == best_connection_) ||
|
||||
(best_connection_ == NULL) ||
|
||||
use_candidate = (conn == best_connection_) || (best_connection_ == NULL) ||
|
||||
(!best_connection_->writable()) ||
|
||||
(conn->priority() > best_connection_->priority());
|
||||
} else if (remote_ice_mode_ == ICEMODE_LITE && conn == best_connection_) {
|
||||
@ -1311,9 +1298,10 @@ void P2PTransportChannel::OnPortDestroyed(PortInterface* port) {
|
||||
}
|
||||
|
||||
// We data is available, let listeners know
|
||||
void P2PTransportChannel::OnReadPacket(
|
||||
Connection *connection, const char *data, size_t len,
|
||||
const rtc::PacketTime& packet_time) {
|
||||
void P2PTransportChannel::OnReadPacket(Connection* connection,
|
||||
const char* data,
|
||||
size_t len,
|
||||
const rtc::PacketTime& packet_time) {
|
||||
ASSERT(worker_thread_ == rtc::Thread::Current());
|
||||
|
||||
// Do not deliver, if packet doesn't belong to the correct transport channel.
|
||||
|
||||
@ -51,11 +51,11 @@ class RemoteCandidate : public Candidate {
|
||||
class P2PTransportChannel : public TransportChannelImpl,
|
||||
public rtc::MessageHandler {
|
||||
public:
|
||||
P2PTransportChannel(const std::string& content_name,
|
||||
P2PTransportChannel(const std::string& transport_name,
|
||||
int component,
|
||||
P2PTransport* transport,
|
||||
PortAllocator *allocator);
|
||||
~P2PTransportChannel() override;
|
||||
PortAllocator* allocator);
|
||||
virtual ~P2PTransportChannel();
|
||||
|
||||
// From TransportChannelImpl:
|
||||
Transport* GetTransport() override { return transport_; }
|
||||
@ -69,15 +69,20 @@ class P2PTransportChannel : public TransportChannelImpl,
|
||||
const std::string& ice_pwd) override;
|
||||
void SetRemoteIceMode(IceMode mode) override;
|
||||
void Connect() override;
|
||||
void OnSignalingReady() override;
|
||||
void OnCandidate(const Candidate& candidate) override;
|
||||
void MaybeStartGathering() override;
|
||||
IceGatheringState gathering_state() const override {
|
||||
return gathering_state_;
|
||||
}
|
||||
void AddRemoteCandidate(const Candidate& candidate) override;
|
||||
// Sets the receiving timeout in milliseconds.
|
||||
// This also sets the check_receiving_delay proportionally.
|
||||
void SetReceivingTimeout(int receiving_timeout_ms) override;
|
||||
|
||||
// From TransportChannel:
|
||||
int SendPacket(const char *data, size_t len,
|
||||
const rtc::PacketOptions& options, int flags) override;
|
||||
int SendPacket(const char* data,
|
||||
size_t len,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) override;
|
||||
int SetOption(rtc::Socket::Option opt, int value) override;
|
||||
bool GetOption(rtc::Socket::Option opt, int* value) override;
|
||||
int GetError() override { return error_; }
|
||||
@ -96,13 +101,9 @@ class P2PTransportChannel : public TransportChannelImpl,
|
||||
bool IsDtlsActive() const override { return false; }
|
||||
|
||||
// Default implementation.
|
||||
bool GetSslRole(rtc::SSLRole* role) const override {
|
||||
return false;
|
||||
}
|
||||
bool GetSslRole(rtc::SSLRole* role) const override { return false; }
|
||||
|
||||
bool SetSslRole(rtc::SSLRole role) override {
|
||||
return false;
|
||||
}
|
||||
bool SetSslRole(rtc::SSLRole role) override { return false; }
|
||||
|
||||
// Set up the ciphers to use for DTLS-SRTP.
|
||||
bool SetSrtpCiphers(const std::vector<std::string>& ciphers) override {
|
||||
@ -110,14 +111,10 @@ class P2PTransportChannel : public TransportChannelImpl,
|
||||
}
|
||||
|
||||
// Find out which DTLS-SRTP cipher was negotiated.
|
||||
bool GetSrtpCipher(std::string* cipher) override {
|
||||
return false;
|
||||
}
|
||||
bool GetSrtpCipher(std::string* cipher) override { return false; }
|
||||
|
||||
// Find out which DTLS cipher was negotiated.
|
||||
bool GetSslCipher(std::string* cipher) override {
|
||||
return false;
|
||||
}
|
||||
bool GetSslCipher(std::string* cipher) override { return false; }
|
||||
|
||||
// Returns null because the channel is not encrypted by default.
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override {
|
||||
@ -165,7 +162,6 @@ class P2PTransportChannel : public TransportChannelImpl,
|
||||
return allocator_sessions_.back();
|
||||
}
|
||||
|
||||
void Allocate();
|
||||
void UpdateConnectionStates();
|
||||
void RequestSort();
|
||||
void SortConnections();
|
||||
@ -213,7 +209,7 @@ class P2PTransportChannel : public TransportChannelImpl,
|
||||
|
||||
void OnNominated(Connection* conn);
|
||||
|
||||
void OnMessage(rtc::Message *pmsg) override;
|
||||
void OnMessage(rtc::Message* pmsg) override;
|
||||
void OnSort();
|
||||
void OnPing();
|
||||
|
||||
@ -223,10 +219,9 @@ class P2PTransportChannel : public TransportChannelImpl,
|
||||
Connection* best_nominated_connection() const;
|
||||
|
||||
P2PTransport* transport_;
|
||||
PortAllocator *allocator_;
|
||||
rtc::Thread *worker_thread_;
|
||||
PortAllocator* allocator_;
|
||||
rtc::Thread* worker_thread_;
|
||||
bool incoming_only_;
|
||||
bool waiting_for_signaling_;
|
||||
int error_;
|
||||
std::vector<PortAllocatorSession*> allocator_sessions_;
|
||||
std::vector<PortInterface *> ports_;
|
||||
@ -238,6 +233,7 @@ class P2PTransportChannel : public TransportChannelImpl,
|
||||
std::vector<RemoteCandidate> remote_candidates_;
|
||||
bool sort_dirty_; // indicates whether another sort is needed right now
|
||||
bool was_writable_;
|
||||
bool had_connection_ = false; // if connections_ has ever been nonempty
|
||||
typedef std::map<rtc::Socket::Option, int> OptionMap;
|
||||
OptionMap options_;
|
||||
std::string ice_ufrag_;
|
||||
@ -248,6 +244,7 @@ class P2PTransportChannel : public TransportChannelImpl,
|
||||
IceRole ice_role_;
|
||||
uint64 tiebreaker_;
|
||||
uint32 remote_candidate_generation_;
|
||||
IceGatheringState gathering_state_;
|
||||
|
||||
int check_receiving_delay_;
|
||||
int receiving_timeout_;
|
||||
|
||||
@ -297,10 +297,8 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
const std::string& remote_ice_pwd) {
|
||||
cricket::P2PTransportChannel* channel = new cricket::P2PTransportChannel(
|
||||
"test content name", component, NULL, GetAllocator(endpoint));
|
||||
channel->SignalRequestSignaling.connect(
|
||||
this, &P2PTransportChannelTestBase::OnChannelRequestSignaling);
|
||||
channel->SignalCandidateReady.connect(this,
|
||||
&P2PTransportChannelTestBase::OnCandidate);
|
||||
channel->SignalCandidateGathered.connect(
|
||||
this, &P2PTransportChannelTestBase::OnCandidate);
|
||||
channel->SignalReadPacket.connect(
|
||||
this, &P2PTransportChannelTestBase::OnReadPacket);
|
||||
channel->SignalRoleConflict.connect(
|
||||
@ -314,6 +312,7 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
channel->SetIceRole(GetEndpoint(endpoint)->ice_role());
|
||||
channel->SetIceTiebreaker(GetEndpoint(endpoint)->GetIceTiebreaker());
|
||||
channel->Connect();
|
||||
channel->MaybeStartGathering();
|
||||
return channel;
|
||||
}
|
||||
void DestroyChannels() {
|
||||
@ -389,10 +388,9 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
}
|
||||
|
||||
bool IsLocalToPrflxOrTheReverse(const Result& expected) {
|
||||
return ((expected.local_type == "local" &&
|
||||
expected.remote_type == "prflx") ||
|
||||
(expected.local_type == "prflx" &&
|
||||
expected.remote_type == "local"));
|
||||
return (
|
||||
(expected.local_type == "local" && expected.remote_type == "prflx") ||
|
||||
(expected.local_type == "prflx" && expected.remote_type == "local"));
|
||||
}
|
||||
|
||||
// Return true if the approprite parts of the expected Result, based
|
||||
@ -512,8 +510,8 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
ep2_ch1()->best_connection()) {
|
||||
int32 converge_start = rtc::Time(), converge_time;
|
||||
int converge_wait = 2000;
|
||||
EXPECT_TRUE_WAIT_MARGIN(CheckCandidate1(expected),
|
||||
converge_wait, converge_wait);
|
||||
EXPECT_TRUE_WAIT_MARGIN(CheckCandidate1(expected), converge_wait,
|
||||
converge_wait);
|
||||
// Also do EXPECT_EQ on each part so that failures are more verbose.
|
||||
ExpectCandidate1(expected);
|
||||
|
||||
@ -562,7 +560,7 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
}
|
||||
|
||||
// This test waits for the transport to become receiving and writable on both
|
||||
// end points. Once they are, the end points set new local ice credentials to
|
||||
// end points. Once they are, the end points set new local ice credentials and
|
||||
// restart the ice gathering. Finally it waits for the transport to select a
|
||||
// new connection using the newly generated ice candidates.
|
||||
// Before calling this function the end points must be configured.
|
||||
@ -582,8 +580,10 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
|
||||
ep1_ch1()->SetIceCredentials(kIceUfrag[2], kIcePwd[2]);
|
||||
ep1_ch1()->SetRemoteIceCredentials(kIceUfrag[3], kIcePwd[3]);
|
||||
ep1_ch1()->MaybeStartGathering();
|
||||
ep2_ch1()->SetIceCredentials(kIceUfrag[3], kIcePwd[3]);
|
||||
ep2_ch1()->SetRemoteIceCredentials(kIceUfrag[2], kIcePwd[2]);
|
||||
ep2_ch1()->MaybeStartGathering();
|
||||
|
||||
EXPECT_TRUE_WAIT_MARGIN(LocalCandidate(ep1_ch1())->generation() !=
|
||||
old_local_candidate1->generation(),
|
||||
@ -626,9 +626,6 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
TestSendRecv(1);
|
||||
}
|
||||
|
||||
void OnChannelRequestSignaling(cricket::TransportChannelImpl* channel) {
|
||||
channel->OnSignalingReady();
|
||||
}
|
||||
// We pass the candidates directly to the other side.
|
||||
void OnCandidate(cricket::TransportChannelImpl* ch,
|
||||
const cricket::Candidate& c) {
|
||||
@ -669,7 +666,7 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
}
|
||||
LOG(LS_INFO) << "Candidate(" << data->channel->component() << "->"
|
||||
<< rch->component() << "): " << c.ToString();
|
||||
rch->OnCandidate(c);
|
||||
rch->AddRemoteCandidate(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -804,8 +801,10 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase {
|
||||
static const Result* kMatrixSharedUfrag[NUM_CONFIGS][NUM_CONFIGS];
|
||||
static const Result* kMatrixSharedSocketAsGice[NUM_CONFIGS][NUM_CONFIGS];
|
||||
static const Result* kMatrixSharedSocketAsIce[NUM_CONFIGS][NUM_CONFIGS];
|
||||
void ConfigureEndpoints(Config config1, Config config2,
|
||||
int allocator_flags1, int allocator_flags2) {
|
||||
void ConfigureEndpoints(Config config1,
|
||||
Config config2,
|
||||
int allocator_flags1,
|
||||
int allocator_flags2) {
|
||||
ServerAddresses stun_servers;
|
||||
stun_servers.insert(kStunAddr);
|
||||
GetEndpoint(0)->allocator_.reset(
|
||||
@ -821,8 +820,8 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase {
|
||||
|
||||
cricket::RelayServerConfig relay_server(cricket::RELAY_TURN);
|
||||
relay_server.credentials = kRelayCredentials;
|
||||
relay_server.ports.push_back(cricket::ProtocolAddress(
|
||||
kTurnUdpIntAddr, cricket::PROTO_UDP, false));
|
||||
relay_server.ports.push_back(
|
||||
cricket::ProtocolAddress(kTurnUdpIntAddr, cricket::PROTO_UDP, false));
|
||||
GetEndpoint(0)->allocator_->AddRelay(relay_server);
|
||||
GetEndpoint(1)->allocator_->AddRelay(relay_server);
|
||||
|
||||
@ -1026,15 +1025,14 @@ const P2PTransportChannelTest::Result*
|
||||
|
||||
// The actual tests that exercise all the various configurations.
|
||||
// Test names are of the form P2PTransportChannelTest_TestOPENToNAT_FULL_CONE
|
||||
#define P2P_TEST_DECLARATION(x, y, z) \
|
||||
TEST_F(P2PTransportChannelTest, z##Test##x##To##y) { \
|
||||
ConfigureEndpoints(x, y, \
|
||||
PORTALLOCATOR_ENABLE_SHARED_SOCKET, \
|
||||
PORTALLOCATOR_ENABLE_SHARED_SOCKET); \
|
||||
if (kMatrixSharedSocketAsIce[x][y] != NULL) \
|
||||
Test(*kMatrixSharedSocketAsIce[x][y]); \
|
||||
else \
|
||||
LOG(LS_WARNING) << "Not yet implemented"; \
|
||||
#define P2P_TEST_DECLARATION(x, y, z) \
|
||||
TEST_F(P2PTransportChannelTest, z##Test##x##To##y) { \
|
||||
ConfigureEndpoints(x, y, PORTALLOCATOR_ENABLE_SHARED_SOCKET, \
|
||||
PORTALLOCATOR_ENABLE_SHARED_SOCKET); \
|
||||
if (kMatrixSharedSocketAsIce[x][y] != NULL) \
|
||||
Test(*kMatrixSharedSocketAsIce[x][y]); \
|
||||
else \
|
||||
LOG(LS_WARNING) << "Not yet implemented"; \
|
||||
}
|
||||
|
||||
#define P2P_TEST(x, y) \
|
||||
@ -1089,8 +1087,7 @@ P2P_TEST_SET(PROXY_SOCKS)
|
||||
// Test that we restart candidate allocation when local ufrag&pwd changed.
|
||||
// Standard Ice protocol is used.
|
||||
TEST_F(P2PTransportChannelTest, HandleUfragPwdChange) {
|
||||
ConfigureEndpoints(OPEN, OPEN,
|
||||
kDefaultPortAllocatorFlags,
|
||||
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
|
||||
kDefaultPortAllocatorFlags);
|
||||
CreateChannels(1);
|
||||
TestHandleIceUfragPasswordChanged();
|
||||
@ -1099,8 +1096,7 @@ TEST_F(P2PTransportChannelTest, HandleUfragPwdChange) {
|
||||
|
||||
// Test the operation of GetStats.
|
||||
TEST_F(P2PTransportChannelTest, GetStats) {
|
||||
ConfigureEndpoints(OPEN, OPEN,
|
||||
kDefaultPortAllocatorFlags,
|
||||
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
|
||||
kDefaultPortAllocatorFlags);
|
||||
CreateChannels(1);
|
||||
EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
|
||||
@ -1126,8 +1122,7 @@ TEST_F(P2PTransportChannelTest, GetStats) {
|
||||
// Test that we properly create a connection on a STUN ping from unknown address
|
||||
// when the signaling is slow.
|
||||
TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) {
|
||||
ConfigureEndpoints(OPEN, OPEN,
|
||||
kDefaultPortAllocatorFlags,
|
||||
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
|
||||
kDefaultPortAllocatorFlags);
|
||||
// Emulate no remote credentials coming in.
|
||||
set_clear_remote_candidates_ufrag_pwd(false);
|
||||
@ -1171,8 +1166,7 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) {
|
||||
// Test that we properly create a connection on a STUN ping from unknown address
|
||||
// when the signaling is slow and the end points are behind NAT.
|
||||
TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithNAT) {
|
||||
ConfigureEndpoints(OPEN, NAT_SYMMETRIC,
|
||||
kDefaultPortAllocatorFlags,
|
||||
ConfigureEndpoints(OPEN, NAT_SYMMETRIC, kDefaultPortAllocatorFlags,
|
||||
kDefaultPortAllocatorFlags);
|
||||
// Emulate no remote credentials coming in.
|
||||
set_clear_remote_candidates_ufrag_pwd(false);
|
||||
@ -1214,8 +1208,7 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithNAT) {
|
||||
// Test that if remote candidates don't have ufrag and pwd, we still work.
|
||||
TEST_F(P2PTransportChannelTest, RemoteCandidatesWithoutUfragPwd) {
|
||||
set_clear_remote_candidates_ufrag_pwd(true);
|
||||
ConfigureEndpoints(OPEN, OPEN,
|
||||
kDefaultPortAllocatorFlags,
|
||||
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
|
||||
kDefaultPortAllocatorFlags);
|
||||
CreateChannels(1);
|
||||
const cricket::Connection* best_connection = NULL;
|
||||
@ -1230,8 +1223,7 @@ TEST_F(P2PTransportChannelTest, RemoteCandidatesWithoutUfragPwd) {
|
||||
// Test that a host behind NAT cannot be reached when incoming_only
|
||||
// is set to true.
|
||||
TEST_F(P2PTransportChannelTest, IncomingOnlyBlocked) {
|
||||
ConfigureEndpoints(NAT_FULL_CONE, OPEN,
|
||||
kDefaultPortAllocatorFlags,
|
||||
ConfigureEndpoints(NAT_FULL_CONE, OPEN, kDefaultPortAllocatorFlags,
|
||||
kDefaultPortAllocatorFlags);
|
||||
|
||||
SetAllocatorFlags(0, kOnlyLocalPorts);
|
||||
@ -1252,8 +1244,7 @@ TEST_F(P2PTransportChannelTest, IncomingOnlyBlocked) {
|
||||
// Test that a peer behind NAT can connect to a peer that has
|
||||
// incoming_only flag set.
|
||||
TEST_F(P2PTransportChannelTest, IncomingOnlyOpen) {
|
||||
ConfigureEndpoints(OPEN, NAT_FULL_CONE,
|
||||
kDefaultPortAllocatorFlags,
|
||||
ConfigureEndpoints(OPEN, NAT_FULL_CONE, kDefaultPortAllocatorFlags,
|
||||
kDefaultPortAllocatorFlags);
|
||||
|
||||
SetAllocatorFlags(0, kOnlyLocalPorts);
|
||||
@ -1414,11 +1405,10 @@ TEST_F(P2PTransportChannelTest, TestIPv6Connections) {
|
||||
|
||||
// Testing forceful TURN connections.
|
||||
TEST_F(P2PTransportChannelTest, TestForceTurn) {
|
||||
ConfigureEndpoints(NAT_PORT_RESTRICTED, NAT_SYMMETRIC,
|
||||
kDefaultPortAllocatorFlags |
|
||||
cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET,
|
||||
kDefaultPortAllocatorFlags |
|
||||
cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET);
|
||||
ConfigureEndpoints(
|
||||
NAT_PORT_RESTRICTED, NAT_SYMMETRIC,
|
||||
kDefaultPortAllocatorFlags | cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET,
|
||||
kDefaultPortAllocatorFlags | cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET);
|
||||
set_force_relay(true);
|
||||
|
||||
SetAllocationStepDelay(0, kMinimumStepDelay);
|
||||
@ -1473,8 +1463,7 @@ class P2PTransportChannelSameNatTest : public P2PTransportChannelTestBase {
|
||||
TEST_F(P2PTransportChannelSameNatTest, TestConesBehindSameCone) {
|
||||
ConfigureEndpoints(NAT_FULL_CONE, NAT_FULL_CONE, NAT_FULL_CONE);
|
||||
Test(P2PTransportChannelTestBase::Result(
|
||||
"prflx", "udp", "stun", "udp",
|
||||
"stun", "udp", "prflx", "udp", 1000));
|
||||
"prflx", "udp", "stun", "udp", "stun", "udp", "prflx", "udp", 1000));
|
||||
}
|
||||
|
||||
// Test what happens when we have multiple available pathways.
|
||||
@ -1560,11 +1549,12 @@ TEST_F(P2PTransportChannelMultihomedTest, TestDrain) {
|
||||
|
||||
|
||||
// Remove the public interface, add the alternate interface, and allocate
|
||||
// a new generation of candidates for the new interface (via Connect()).
|
||||
// a new generation of candidates for the new interface (via
|
||||
// MaybeStartGathering()).
|
||||
LOG(LS_INFO) << "Draining...";
|
||||
AddAddress(1, kAlternateAddrs[1]);
|
||||
RemoveAddress(1, kPublicAddrs[1]);
|
||||
ep2_ch1()->Connect();
|
||||
ep2_ch1()->MaybeStartGathering();
|
||||
|
||||
// We should switch over to use the alternate address after
|
||||
// an exchange of pings.
|
||||
@ -1591,17 +1581,11 @@ class P2PTransportChannelPingTest : public testing::Test,
|
||||
|
||||
protected:
|
||||
void PrepareChannel(cricket::P2PTransportChannel* ch) {
|
||||
ch->SignalRequestSignaling.connect(
|
||||
this, &P2PTransportChannelPingTest::OnChannelRequestSignaling);
|
||||
ch->SetIceRole(cricket::ICEROLE_CONTROLLING);
|
||||
ch->SetIceCredentials(kIceUfrag[0], kIcePwd[0]);
|
||||
ch->SetRemoteIceCredentials(kIceUfrag[1], kIcePwd[1]);
|
||||
}
|
||||
|
||||
void OnChannelRequestSignaling(cricket::TransportChannelImpl* channel) {
|
||||
channel->OnSignalingReady();
|
||||
}
|
||||
|
||||
cricket::Candidate CreateCandidate(const std::string& ip,
|
||||
int port,
|
||||
int priority) {
|
||||
@ -1648,8 +1632,9 @@ TEST_F(P2PTransportChannelPingTest, TestTriggeredChecks) {
|
||||
cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa);
|
||||
PrepareChannel(&ch);
|
||||
ch.Connect();
|
||||
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
|
||||
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2));
|
||||
ch.MaybeStartGathering();
|
||||
ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1));
|
||||
ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2));
|
||||
|
||||
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
||||
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
||||
@ -1672,8 +1657,9 @@ TEST_F(P2PTransportChannelPingTest, TestNoTriggeredChecksWhenWritable) {
|
||||
cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa);
|
||||
PrepareChannel(&ch);
|
||||
ch.Connect();
|
||||
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
|
||||
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2));
|
||||
ch.MaybeStartGathering();
|
||||
ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1));
|
||||
ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2));
|
||||
|
||||
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
||||
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
||||
@ -1696,16 +1682,17 @@ TEST_F(P2PTransportChannelPingTest, ConnectionResurrection) {
|
||||
cricket::P2PTransportChannel ch("connection resurrection", 1, nullptr, &pa);
|
||||
PrepareChannel(&ch);
|
||||
ch.Connect();
|
||||
ch.MaybeStartGathering();
|
||||
|
||||
// Create conn1 and keep track of original candidate priority.
|
||||
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
|
||||
ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1));
|
||||
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
||||
ASSERT_TRUE(conn1 != nullptr);
|
||||
uint32 remote_priority = conn1->remote_candidate().priority();
|
||||
|
||||
// Create a higher priority candidate and make the connection
|
||||
// receiving/writable. This will prune conn1.
|
||||
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2));
|
||||
ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2));
|
||||
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
||||
ASSERT_TRUE(conn2 != nullptr);
|
||||
conn2->ReceivedPing();
|
||||
@ -1752,7 +1739,8 @@ TEST_F(P2PTransportChannelPingTest, TestReceivingStateChange) {
|
||||
EXPECT_EQ(500, ch.receiving_timeout());
|
||||
EXPECT_EQ(50, ch.check_receiving_delay());
|
||||
ch.Connect();
|
||||
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
|
||||
ch.MaybeStartGathering();
|
||||
ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1));
|
||||
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
||||
ASSERT_TRUE(conn1 != nullptr);
|
||||
|
||||
@ -1773,14 +1761,15 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) {
|
||||
PrepareChannel(&ch);
|
||||
ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
|
||||
ch.Connect();
|
||||
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
|
||||
ch.MaybeStartGathering();
|
||||
ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1));
|
||||
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
||||
ASSERT_TRUE(conn1 != nullptr);
|
||||
EXPECT_EQ(conn1, ch.best_connection());
|
||||
|
||||
// When a higher priority candidate comes in, the new connection is chosen
|
||||
// as the best connection.
|
||||
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 10));
|
||||
ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 10));
|
||||
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
||||
ASSERT_TRUE(conn1 != nullptr);
|
||||
EXPECT_EQ(conn2, ch.best_connection());
|
||||
@ -1788,7 +1777,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) {
|
||||
// If a stun request with use-candidate attribute arrives, the receiving
|
||||
// connection will be set as the best connection, even though
|
||||
// its priority is lower.
|
||||
ch.OnCandidate(CreateCandidate("3.3.3.3", 3, 1));
|
||||
ch.AddRemoteCandidate(CreateCandidate("3.3.3.3", 3, 1));
|
||||
cricket::Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3);
|
||||
ASSERT_TRUE(conn3 != nullptr);
|
||||
// Because it has a lower priority, the best connection is still conn2.
|
||||
@ -1803,7 +1792,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) {
|
||||
// Even if another higher priority candidate arrives,
|
||||
// it will not be set as the best connection because the best connection
|
||||
// is nominated by the controlling side.
|
||||
ch.OnCandidate(CreateCandidate("4.4.4.4", 4, 100));
|
||||
ch.AddRemoteCandidate(CreateCandidate("4.4.4.4", 4, 100));
|
||||
cricket::Connection* conn4 = WaitForConnectionTo(&ch, "4.4.4.4", 4);
|
||||
ASSERT_TRUE(conn4 != nullptr);
|
||||
EXPECT_EQ(conn3, ch.best_connection());
|
||||
@ -1828,6 +1817,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) {
|
||||
PrepareChannel(&ch);
|
||||
ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
|
||||
ch.Connect();
|
||||
ch.MaybeStartGathering();
|
||||
// A minimal STUN message with prflx priority.
|
||||
cricket::IceMessage request;
|
||||
request.SetType(cricket::STUN_BINDING_REQUEST);
|
||||
@ -1846,7 +1836,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) {
|
||||
EXPECT_EQ(conn1, ch.best_connection());
|
||||
|
||||
// Another connection is nominated via use_candidate.
|
||||
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 1));
|
||||
ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 1));
|
||||
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
||||
ASSERT_TRUE(conn2 != nullptr);
|
||||
// Because it has a lower priority, the best connection is still conn1.
|
||||
@ -1892,7 +1882,8 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) {
|
||||
PrepareChannel(&ch);
|
||||
ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
|
||||
ch.Connect();
|
||||
ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 10));
|
||||
ch.MaybeStartGathering();
|
||||
ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 10));
|
||||
cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
||||
ASSERT_TRUE(conn1 != nullptr);
|
||||
EXPECT_EQ(conn1, ch.best_connection());
|
||||
@ -1900,7 +1891,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) {
|
||||
// If a data packet is received on conn2, the best connection should
|
||||
// switch to conn2 because the controlled side must mirror the media path
|
||||
// chosen by the controlling side.
|
||||
ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 1));
|
||||
ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 1));
|
||||
cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
||||
ASSERT_TRUE(conn2 != nullptr);
|
||||
conn2->ReceivedPing(); // Start receiving.
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/p2p/base/portallocator.h"
|
||||
|
||||
namespace cricket {
|
||||
@ -21,8 +22,10 @@ PortAllocatorSession::PortAllocatorSession(const std::string& content_name,
|
||||
component_(component),
|
||||
flags_(flags),
|
||||
generation_(0),
|
||||
username_(ice_ufrag),
|
||||
password_(ice_pwd) {
|
||||
ice_ufrag_(ice_ufrag),
|
||||
ice_pwd_(ice_pwd) {
|
||||
RTC_DCHECK(!ice_ufrag.empty());
|
||||
RTC_DCHECK(!ice_pwd.empty());
|
||||
}
|
||||
|
||||
PortAllocatorSession* PortAllocator::CreateSession(
|
||||
|
||||
@ -74,11 +74,10 @@ enum {
|
||||
class PortAllocatorSession : public sigslot::has_slots<> {
|
||||
public:
|
||||
// Content name passed in mostly for logging and debugging.
|
||||
// TODO(mallinath) - Change username and password to ice_ufrag and ice_pwd.
|
||||
PortAllocatorSession(const std::string& content_name,
|
||||
int component,
|
||||
const std::string& username,
|
||||
const std::string& password,
|
||||
const std::string& ice_ufrag,
|
||||
const std::string& ice_pwd,
|
||||
uint32 flags);
|
||||
|
||||
// Subclasses should clean up any ports created.
|
||||
@ -103,9 +102,14 @@ class PortAllocatorSession : public sigslot::has_slots<> {
|
||||
virtual void set_generation(uint32 generation) { generation_ = generation; }
|
||||
sigslot::signal1<PortAllocatorSession*> SignalDestroyed;
|
||||
|
||||
const std::string& ice_ufrag() const { return ice_ufrag_; }
|
||||
const std::string& ice_pwd() const { return ice_pwd_; }
|
||||
|
||||
protected:
|
||||
const std::string& username() const { return username_; }
|
||||
const std::string& password() const { return password_; }
|
||||
// TODO(deadbeef): Get rid of these when everyone switches to ice_ufrag and
|
||||
// ice_pwd.
|
||||
const std::string& username() const { return ice_ufrag_; }
|
||||
const std::string& password() const { return ice_pwd_; }
|
||||
|
||||
std::string content_name_;
|
||||
int component_;
|
||||
@ -113,8 +117,8 @@ class PortAllocatorSession : public sigslot::has_slots<> {
|
||||
private:
|
||||
uint32 flags_;
|
||||
uint32 generation_;
|
||||
std::string username_;
|
||||
std::string password_;
|
||||
std::string ice_ufrag_;
|
||||
std::string ice_pwd_;
|
||||
};
|
||||
|
||||
class PortAllocator : public sigslot::has_slots<> {
|
||||
|
||||
@ -10,11 +10,6 @@
|
||||
|
||||
#include "webrtc/p2p/base/session.h"
|
||||
|
||||
#include "webrtc/p2p/base/dtlstransport.h"
|
||||
#include "webrtc/p2p/base/p2ptransport.h"
|
||||
#include "webrtc/p2p/base/transport.h"
|
||||
#include "webrtc/p2p/base/transportchannelproxy.h"
|
||||
#include "webrtc/p2p/base/transportinfo.h"
|
||||
#include "webrtc/base/bind.h"
|
||||
#include "webrtc/base/common.h"
|
||||
#include "webrtc/base/helpers.h"
|
||||
@ -22,266 +17,15 @@
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/base/stringencode.h"
|
||||
#include "webrtc/base/sslstreamadapter.h"
|
||||
|
||||
#include "webrtc/p2p/base/transport.h"
|
||||
#include "webrtc/p2p/base/transportinfo.h"
|
||||
#include "webrtc/p2p/base/transportcontroller.h"
|
||||
#include "webrtc/p2p/base/constants.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
using rtc::Bind;
|
||||
|
||||
TransportProxy::~TransportProxy() {
|
||||
for (ChannelMap::iterator iter = channels_.begin();
|
||||
iter != channels_.end(); ++iter) {
|
||||
iter->second->SignalDestroyed(iter->second);
|
||||
delete iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
TransportChannel* TransportProxy::GetChannel(int component) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
return GetChannelProxy(component);
|
||||
}
|
||||
|
||||
TransportChannel* TransportProxy::CreateChannel(int component) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
ASSERT(GetChannel(component) == NULL);
|
||||
ASSERT(!transport_->get()->HasChannel(component));
|
||||
|
||||
// We always create a proxy in case we need to change out the transport later.
|
||||
TransportChannelProxy* channel_proxy =
|
||||
new TransportChannelProxy(content_name(), component);
|
||||
channels_[component] = channel_proxy;
|
||||
|
||||
// If we're already negotiated, create an impl and hook it up to the proxy
|
||||
// channel. If we're connecting, create an impl but don't hook it up yet.
|
||||
if (negotiated_) {
|
||||
CreateChannelImpl_w(component);
|
||||
SetChannelImplFromTransport_w(channel_proxy, component);
|
||||
} else if (connecting_) {
|
||||
CreateChannelImpl_w(component);
|
||||
}
|
||||
return channel_proxy;
|
||||
}
|
||||
|
||||
bool TransportProxy::HasChannel(int component) {
|
||||
return transport_->get()->HasChannel(component);
|
||||
}
|
||||
|
||||
void TransportProxy::DestroyChannel(int component) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
TransportChannelProxy* channel_proxy = GetChannelProxy(component);
|
||||
if (channel_proxy) {
|
||||
// If the state of TransportProxy is not NEGOTIATED then
|
||||
// TransportChannelProxy and its impl are not connected. Both must
|
||||
// be connected before deletion.
|
||||
//
|
||||
// However, if we haven't entered the connecting state then there
|
||||
// is no implementation to hook up.
|
||||
if (connecting_ && !negotiated_) {
|
||||
SetChannelImplFromTransport_w(channel_proxy, component);
|
||||
}
|
||||
|
||||
channels_.erase(component);
|
||||
channel_proxy->SignalDestroyed(channel_proxy);
|
||||
delete channel_proxy;
|
||||
}
|
||||
}
|
||||
|
||||
void TransportProxy::ConnectChannels() {
|
||||
if (!connecting_) {
|
||||
if (!negotiated_) {
|
||||
for (auto& iter : channels_) {
|
||||
CreateChannelImpl(iter.first);
|
||||
}
|
||||
}
|
||||
connecting_ = true;
|
||||
}
|
||||
// TODO(juberti): Right now Transport::ConnectChannels doesn't work if we
|
||||
// don't have any channels yet, so we need to allow this method to be called
|
||||
// multiple times. Once we fix Transport, we can move this call inside the
|
||||
// if (!connecting_) block.
|
||||
transport_->get()->ConnectChannels();
|
||||
}
|
||||
|
||||
void TransportProxy::CompleteNegotiation() {
|
||||
if (!negotiated_) {
|
||||
// Negotiating assumes connecting_ has happened and
|
||||
// implementations exist. If not we need to create the
|
||||
// implementations.
|
||||
for (auto& iter : channels_) {
|
||||
if (!connecting_) {
|
||||
CreateChannelImpl(iter.first);
|
||||
}
|
||||
SetChannelImplFromTransport(iter.second, iter.first);
|
||||
}
|
||||
negotiated_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TransportProxy::AddSentCandidates(const Candidates& candidates) {
|
||||
for (Candidates::const_iterator cand = candidates.begin();
|
||||
cand != candidates.end(); ++cand) {
|
||||
sent_candidates_.push_back(*cand);
|
||||
}
|
||||
}
|
||||
|
||||
void TransportProxy::AddUnsentCandidates(const Candidates& candidates) {
|
||||
for (Candidates::const_iterator cand = candidates.begin();
|
||||
cand != candidates.end(); ++cand) {
|
||||
unsent_candidates_.push_back(*cand);
|
||||
}
|
||||
}
|
||||
|
||||
TransportChannelProxy* TransportProxy::GetChannelProxy(int component) const {
|
||||
ChannelMap::const_iterator iter = channels_.find(component);
|
||||
return (iter != channels_.end()) ? iter->second : NULL;
|
||||
}
|
||||
|
||||
void TransportProxy::CreateChannelImpl(int component) {
|
||||
worker_thread_->Invoke<void>(Bind(
|
||||
&TransportProxy::CreateChannelImpl_w, this, component));
|
||||
}
|
||||
|
||||
void TransportProxy::CreateChannelImpl_w(int component) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
transport_->get()->CreateChannel(component);
|
||||
}
|
||||
|
||||
void TransportProxy::SetChannelImplFromTransport(TransportChannelProxy* proxy,
|
||||
int component) {
|
||||
worker_thread_->Invoke<void>(Bind(
|
||||
&TransportProxy::SetChannelImplFromTransport_w, this, proxy, component));
|
||||
}
|
||||
|
||||
void TransportProxy::SetChannelImplFromTransport_w(TransportChannelProxy* proxy,
|
||||
int component) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
TransportChannelImpl* impl = transport_->get()->GetChannel(component);
|
||||
ASSERT(impl != NULL);
|
||||
ReplaceChannelImpl_w(proxy, impl);
|
||||
}
|
||||
|
||||
void TransportProxy::ReplaceChannelImpl(TransportChannelProxy* proxy,
|
||||
TransportChannelImpl* impl) {
|
||||
worker_thread_->Invoke<void>(Bind(
|
||||
&TransportProxy::ReplaceChannelImpl_w, this, proxy, impl));
|
||||
}
|
||||
|
||||
void TransportProxy::ReplaceChannelImpl_w(TransportChannelProxy* proxy,
|
||||
TransportChannelImpl* impl) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
ASSERT(proxy != NULL);
|
||||
proxy->SetImplementation(impl);
|
||||
}
|
||||
|
||||
// This function muxes |this| onto |target| by repointing |this| at
|
||||
// |target|'s transport and setting our TransportChannelProxies
|
||||
// to point to |target|'s underlying implementations.
|
||||
bool TransportProxy::SetupMux(TransportProxy* target) {
|
||||
// Bail out if there's nothing to do.
|
||||
if (transport_ == target->transport_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Run through all channels and remove any non-rtp transport channels before
|
||||
// setting target transport channels.
|
||||
for (ChannelMap::const_iterator iter = channels_.begin();
|
||||
iter != channels_.end(); ++iter) {
|
||||
if (!target->transport_->get()->HasChannel(iter->first)) {
|
||||
// Remove if channel doesn't exist in |transport_|.
|
||||
ReplaceChannelImpl(iter->second, NULL);
|
||||
} else {
|
||||
// Replace the impl for all the TransportProxyChannels with the channels
|
||||
// from |target|'s transport. Fail if there's not an exact match.
|
||||
ReplaceChannelImpl(
|
||||
iter->second, target->transport_->get()->CreateChannel(iter->first));
|
||||
}
|
||||
}
|
||||
|
||||
// Now replace our transport. Must happen afterwards because
|
||||
// it deletes all impls as a side effect.
|
||||
transport_ = target->transport_;
|
||||
transport_->get()->SignalCandidatesReady.connect(
|
||||
this, &TransportProxy::OnTransportCandidatesReady);
|
||||
set_candidates_allocated(target->candidates_allocated());
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransportProxy::SetIceRole(IceRole role) {
|
||||
transport_->get()->SetIceRole(role);
|
||||
}
|
||||
|
||||
bool TransportProxy::SetLocalTransportDescription(
|
||||
const TransportDescription& description,
|
||||
ContentAction action,
|
||||
std::string* error_desc) {
|
||||
// If this is an answer, finalize the negotiation.
|
||||
if (action == CA_ANSWER) {
|
||||
CompleteNegotiation();
|
||||
}
|
||||
bool result = transport_->get()->SetLocalTransportDescription(description,
|
||||
action,
|
||||
error_desc);
|
||||
if (result)
|
||||
local_description_set_ = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransportProxy::SetRemoteTransportDescription(
|
||||
const TransportDescription& description,
|
||||
ContentAction action,
|
||||
std::string* error_desc) {
|
||||
// If this is an answer, finalize the negotiation.
|
||||
if (action == CA_ANSWER) {
|
||||
CompleteNegotiation();
|
||||
}
|
||||
bool result = transport_->get()->SetRemoteTransportDescription(description,
|
||||
action,
|
||||
error_desc);
|
||||
if (result)
|
||||
remote_description_set_ = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void TransportProxy::OnSignalingReady() {
|
||||
// If we're starting a new allocation sequence, reset our state.
|
||||
set_candidates_allocated(false);
|
||||
transport_->get()->OnSignalingReady();
|
||||
}
|
||||
|
||||
bool TransportProxy::OnRemoteCandidates(const Candidates& candidates,
|
||||
std::string* error) {
|
||||
// Ensure the transport is negotiated before handling candidates.
|
||||
// TODO(juberti): Remove this once everybody calls SetLocalTD.
|
||||
CompleteNegotiation();
|
||||
|
||||
// Ignore candidates for if the proxy content_name doesn't match the content
|
||||
// name of the actual transport. This stops video candidates from being sent
|
||||
// down to the audio transport when BUNDLE is enabled.
|
||||
if (content_name_ != transport_->get()->content_name()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Verify each candidate before passing down to transport layer.
|
||||
for (Candidates::const_iterator cand = candidates.begin();
|
||||
cand != candidates.end(); ++cand) {
|
||||
if (!transport_->get()->VerifyCandidate(*cand, error))
|
||||
return false;
|
||||
if (!HasChannel(cand->component())) {
|
||||
*error = "Candidate has unknown component: " + cand->ToString() +
|
||||
" for content: " + content_name_;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
transport_->get()->OnRemoteCandidates(candidates);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransportProxy::SetCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
|
||||
transport_->get()->SetCertificate(certificate);
|
||||
}
|
||||
|
||||
std::string BaseSession::StateToString(State state) {
|
||||
switch (state) {
|
||||
case STATE_INIT:
|
||||
@ -326,7 +70,6 @@ BaseSession::BaseSession(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
PortAllocator* port_allocator,
|
||||
const std::string& sid,
|
||||
const std::string& content_type,
|
||||
bool initiator)
|
||||
: state_(STATE_INIT),
|
||||
error_(ERROR_NONE),
|
||||
@ -334,13 +77,11 @@ BaseSession::BaseSession(rtc::Thread* signaling_thread,
|
||||
worker_thread_(worker_thread),
|
||||
port_allocator_(port_allocator),
|
||||
sid_(sid),
|
||||
content_type_(content_type),
|
||||
initiator_(initiator),
|
||||
ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10),
|
||||
ice_tiebreaker_(rtc::CreateRandomId64()),
|
||||
role_switch_(false),
|
||||
ice_receiving_timeout_(-1) {
|
||||
transport_controller_(new TransportController(signaling_thread,
|
||||
worker_thread,
|
||||
port_allocator)) {
|
||||
ASSERT(signaling_thread->IsCurrent());
|
||||
set_initiator(initiator);
|
||||
}
|
||||
|
||||
BaseSession::~BaseSession() {
|
||||
@ -350,11 +91,6 @@ BaseSession::~BaseSession() {
|
||||
LogState(state_, STATE_DEINIT);
|
||||
state_ = STATE_DEINIT;
|
||||
SignalState(this, state_);
|
||||
|
||||
for (TransportMap::iterator iter = transports_.begin();
|
||||
iter != transports_.end(); ++iter) {
|
||||
delete iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
const SessionDescription* BaseSession::local_description() const {
|
||||
@ -384,37 +120,23 @@ void BaseSession::set_remote_description(SessionDescription* sdesc) {
|
||||
remote_description_.reset(sdesc);
|
||||
}
|
||||
|
||||
void BaseSession::set_initiator(bool initiator) {
|
||||
initiator_ = initiator;
|
||||
|
||||
IceRole ice_role = initiator ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED;
|
||||
transport_controller_->SetIceRole(ice_role);
|
||||
}
|
||||
|
||||
const SessionDescription* BaseSession::initiator_description() const {
|
||||
// TODO(tommi): Assert on thread correctness.
|
||||
return initiator_ ? local_description_.get() : remote_description_.get();
|
||||
}
|
||||
|
||||
bool BaseSession::SetCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
|
||||
if (certificate_)
|
||||
return false;
|
||||
if (!certificate)
|
||||
return false;
|
||||
certificate_ = certificate;
|
||||
for (TransportMap::iterator iter = transports_.begin();
|
||||
iter != transports_.end(); ++iter) {
|
||||
iter->second->SetCertificate(certificate_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseSession::SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) {
|
||||
if (state_ != STATE_INIT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ssl_max_version_ = version;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseSession::PushdownTransportDescription(ContentSource source,
|
||||
ContentAction action,
|
||||
std::string* error_desc) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
|
||||
if (source == CS_LOCAL) {
|
||||
return PushdownLocalTransportDescription(local_description(),
|
||||
action,
|
||||
@ -428,23 +150,17 @@ bool BaseSession::PushdownTransportDescription(ContentSource source,
|
||||
bool BaseSession::PushdownLocalTransportDescription(
|
||||
const SessionDescription* sdesc,
|
||||
ContentAction action,
|
||||
std::string* error_desc) {
|
||||
// Update the Transports with the right information, and trigger them to
|
||||
// start connecting.
|
||||
for (TransportMap::iterator iter = transports_.begin();
|
||||
iter != transports_.end(); ++iter) {
|
||||
// If no transport info was in this session description, ret == false
|
||||
// and we just skip this one.
|
||||
TransportDescription tdesc;
|
||||
bool ret = GetTransportDescription(
|
||||
sdesc, iter->second->content_name(), &tdesc);
|
||||
if (ret) {
|
||||
if (!iter->second->SetLocalTransportDescription(tdesc, action,
|
||||
error_desc)) {
|
||||
return false;
|
||||
}
|
||||
std::string* err) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
|
||||
iter->second->ConnectChannels();
|
||||
if (!sdesc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const TransportInfo& tinfo : sdesc->transport_infos()) {
|
||||
if (!transport_controller_->SetLocalTransportDescription(
|
||||
tinfo.content_name, tinfo.description, action, err)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -454,134 +170,23 @@ bool BaseSession::PushdownLocalTransportDescription(
|
||||
bool BaseSession::PushdownRemoteTransportDescription(
|
||||
const SessionDescription* sdesc,
|
||||
ContentAction action,
|
||||
std::string* error_desc) {
|
||||
// Update the Transports with the right information.
|
||||
for (TransportMap::iterator iter = transports_.begin();
|
||||
iter != transports_.end(); ++iter) {
|
||||
TransportDescription tdesc;
|
||||
std::string* err) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
|
||||
// If no transport info was in this session description, ret == false
|
||||
// and we just skip this one.
|
||||
bool ret = GetTransportDescription(
|
||||
sdesc, iter->second->content_name(), &tdesc);
|
||||
if (ret) {
|
||||
if (!iter->second->SetRemoteTransportDescription(tdesc, action,
|
||||
error_desc)) {
|
||||
return false;
|
||||
}
|
||||
if (!sdesc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const TransportInfo& tinfo : sdesc->transport_infos()) {
|
||||
if (!transport_controller_->SetRemoteTransportDescription(
|
||||
tinfo.content_name, tinfo.description, action, err)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseSession::SetIceConnectionReceivingTimeout(int timeout_ms) {
|
||||
ice_receiving_timeout_ = timeout_ms;
|
||||
for (const auto& kv : transport_proxies()) {
|
||||
Transport* transport = kv.second->impl();
|
||||
if (transport) {
|
||||
transport->SetChannelReceivingTimeout(timeout_ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TransportChannel* BaseSession::CreateChannel(const std::string& content_name,
|
||||
int component) {
|
||||
// We create the proxy "on demand" here because we need to support
|
||||
// creating channels at any time, even before we send or receive
|
||||
// initiate messages, which is before we create the transports.
|
||||
TransportProxy* transproxy = GetOrCreateTransportProxy(content_name);
|
||||
return transproxy->CreateChannel(component);
|
||||
}
|
||||
|
||||
TransportChannel* BaseSession::GetChannel(const std::string& content_name,
|
||||
int component) {
|
||||
TransportProxy* transproxy = GetTransportProxy(content_name);
|
||||
if (transproxy == NULL)
|
||||
return NULL;
|
||||
|
||||
return transproxy->GetChannel(component);
|
||||
}
|
||||
|
||||
void BaseSession::DestroyChannel(const std::string& content_name,
|
||||
int component) {
|
||||
TransportProxy* transproxy = GetTransportProxy(content_name);
|
||||
ASSERT(transproxy != NULL);
|
||||
transproxy->DestroyChannel(component);
|
||||
}
|
||||
|
||||
TransportProxy* BaseSession::GetOrCreateTransportProxy(
|
||||
const std::string& content_name) {
|
||||
TransportProxy* transproxy = GetTransportProxy(content_name);
|
||||
if (transproxy)
|
||||
return transproxy;
|
||||
|
||||
Transport* transport = CreateTransport(content_name);
|
||||
transport->SetIceRole(initiator_ ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED);
|
||||
transport->SetIceTiebreaker(ice_tiebreaker_);
|
||||
transport->SetSslMaxProtocolVersion(ssl_max_version_);
|
||||
// TODO: Connect all the Transport signals to TransportProxy
|
||||
// then to the BaseSession.
|
||||
transport->SignalConnecting.connect(
|
||||
this, &BaseSession::OnTransportConnecting);
|
||||
transport->SignalWritableState.connect(
|
||||
this, &BaseSession::OnTransportWritable);
|
||||
transport->SignalReceivingState.connect(
|
||||
this, &BaseSession::OnTransportReceiving);
|
||||
transport->SignalRequestSignaling.connect(
|
||||
this, &BaseSession::OnTransportRequestSignaling);
|
||||
transport->SignalRouteChange.connect(
|
||||
this, &BaseSession::OnTransportRouteChange);
|
||||
transport->SignalCandidatesAllocationDone.connect(
|
||||
this, &BaseSession::OnTransportCandidatesAllocationDone);
|
||||
transport->SignalRoleConflict.connect(
|
||||
this, &BaseSession::OnRoleConflict);
|
||||
transport->SignalCompleted.connect(
|
||||
this, &BaseSession::OnTransportCompleted);
|
||||
transport->SignalFailed.connect(
|
||||
this, &BaseSession::OnTransportFailed);
|
||||
|
||||
transproxy = new TransportProxy(worker_thread_, sid_, content_name,
|
||||
new TransportWrapper(transport));
|
||||
transproxy->SignalCandidatesReady.connect(
|
||||
this, &BaseSession::OnTransportProxyCandidatesReady);
|
||||
if (certificate_)
|
||||
transproxy->SetCertificate(certificate_);
|
||||
transports_[content_name] = transproxy;
|
||||
|
||||
return transproxy;
|
||||
}
|
||||
|
||||
Transport* BaseSession::GetTransport(const std::string& content_name) {
|
||||
TransportProxy* transproxy = GetTransportProxy(content_name);
|
||||
if (transproxy == NULL)
|
||||
return NULL;
|
||||
return transproxy->impl();
|
||||
}
|
||||
|
||||
TransportProxy* BaseSession::GetTransportProxy(
|
||||
const std::string& content_name) {
|
||||
TransportMap::iterator iter = transports_.find(content_name);
|
||||
return (iter != transports_.end()) ? iter->second : NULL;
|
||||
}
|
||||
|
||||
void BaseSession::DestroyTransportProxy(
|
||||
const std::string& content_name) {
|
||||
TransportMap::iterator iter = transports_.find(content_name);
|
||||
if (iter != transports_.end()) {
|
||||
delete iter->second;
|
||||
transports_.erase(content_name);
|
||||
}
|
||||
}
|
||||
|
||||
Transport* BaseSession::CreateTransport(const std::string& content_name) {
|
||||
Transport* transport = new DtlsTransport<P2PTransport>(
|
||||
signaling_thread(), worker_thread(), content_name, port_allocator(),
|
||||
certificate_);
|
||||
transport->SetChannelReceivingTimeout(ice_receiving_timeout_);
|
||||
return transport;
|
||||
}
|
||||
|
||||
void BaseSession::SetState(State state) {
|
||||
ASSERT(signaling_thread_->IsCurrent());
|
||||
if (state != state_) {
|
||||
@ -601,180 +206,18 @@ void BaseSession::SetError(Error error, const std::string& error_desc) {
|
||||
}
|
||||
}
|
||||
|
||||
void BaseSession::OnSignalingReady() {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
for (TransportMap::iterator iter = transports_.begin();
|
||||
iter != transports_.end(); ++iter) {
|
||||
iter->second->OnSignalingReady();
|
||||
}
|
||||
void BaseSession::SetIceConnectionReceivingTimeout(int timeout_ms) {
|
||||
transport_controller_->SetIceConnectionReceivingTimeout(timeout_ms);
|
||||
}
|
||||
|
||||
// TODO(juberti): Since PushdownLocalTD now triggers the connection process to
|
||||
// start, remove this method once everyone calls PushdownLocalTD.
|
||||
void BaseSession::SpeculativelyConnectAllTransportChannels() {
|
||||
// Put all transports into the connecting state.
|
||||
for (TransportMap::iterator iter = transports_.begin();
|
||||
iter != transports_.end(); ++iter) {
|
||||
iter->second->ConnectChannels();
|
||||
}
|
||||
}
|
||||
|
||||
bool BaseSession::OnRemoteCandidates(const std::string& content_name,
|
||||
const Candidates& candidates,
|
||||
std::string* error) {
|
||||
// Give candidates to the appropriate transport, and tell that transport
|
||||
// to start connecting, if it's not already doing so.
|
||||
TransportProxy* transproxy = GetTransportProxy(content_name);
|
||||
if (!transproxy) {
|
||||
*error = "Unknown content name " + content_name;
|
||||
return false;
|
||||
}
|
||||
if (!transproxy->OnRemoteCandidates(candidates, error)) {
|
||||
return false;
|
||||
}
|
||||
// TODO(juberti): Remove this call once we can be sure that we always have
|
||||
// a local transport description (which will trigger the connection).
|
||||
transproxy->ConnectChannels();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseSession::MaybeEnableMuxingSupport() {
|
||||
// We need both a local and remote description to decide if we should mux.
|
||||
if ((state_ == STATE_SENTINITIATE ||
|
||||
state_ == STATE_RECEIVEDINITIATE) &&
|
||||
((local_description_ == NULL) ||
|
||||
(remote_description_ == NULL))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// In order to perform the multiplexing, we need all proxies to be in the
|
||||
// negotiated state, i.e. to have implementations underneath.
|
||||
// Ensure that this is the case, regardless of whether we are going to mux.
|
||||
for (TransportMap::iterator iter = transports_.begin();
|
||||
iter != transports_.end(); ++iter) {
|
||||
ASSERT(iter->second->negotiated());
|
||||
if (!iter->second->negotiated()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If both sides agree to BUNDLE, mux all the specified contents onto the
|
||||
// transport belonging to the first content name in the BUNDLE group.
|
||||
// If the contents are already muxed, this will be a no-op.
|
||||
// TODO(juberti): Should this check that local and remote have configured
|
||||
// BUNDLE the same way?
|
||||
bool candidates_allocated = IsCandidateAllocationDone();
|
||||
const ContentGroup* local_bundle_group =
|
||||
local_description_->GetGroupByName(GROUP_TYPE_BUNDLE);
|
||||
const ContentGroup* remote_bundle_group =
|
||||
remote_description_->GetGroupByName(GROUP_TYPE_BUNDLE);
|
||||
if (local_bundle_group && remote_bundle_group) {
|
||||
if (!BundleContentGroup(local_bundle_group)) {
|
||||
LOG(LS_WARNING) << "Failed to set up BUNDLE";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we weren't done gathering before, we might be done now, as a result
|
||||
// of enabling mux.
|
||||
if (!candidates_allocated) {
|
||||
MaybeCandidateAllocationDone();
|
||||
}
|
||||
} else {
|
||||
LOG(LS_INFO) << "BUNDLE group missing from remote or local description.";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseSession::BundleContentGroup(const ContentGroup* bundle_group) {
|
||||
const std::string* content_name = bundle_group->FirstContentName();
|
||||
if (!content_name) {
|
||||
LOG(LS_INFO) << "No content names specified in BUNDLE group.";
|
||||
return true;
|
||||
}
|
||||
|
||||
TransportProxy* selected_proxy = GetTransportProxy(*content_name);
|
||||
if (!selected_proxy) {
|
||||
LOG(LS_WARNING) << "No transport found for content \""
|
||||
<< *content_name << "\".";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (TransportMap::iterator iter = transports_.begin();
|
||||
iter != transports_.end(); ++iter) {
|
||||
// If content is part of the mux group, then repoint its proxy at the
|
||||
// transport object that we have chosen to mux onto. If the proxy
|
||||
// is already pointing at the right object, it will be a no-op.
|
||||
if (bundle_group->HasContentName(iter->first) &&
|
||||
!iter->second->SetupMux(selected_proxy)) {
|
||||
LOG(LS_WARNING) << "Failed to bundle " << iter->first << " to "
|
||||
<< *content_name;
|
||||
return false;
|
||||
}
|
||||
LOG(LS_INFO) << "Bundling " << iter->first << " to " << *content_name;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseSession::OnTransportCandidatesAllocationDone(Transport* transport) {
|
||||
// TODO(juberti): This is a clunky way of processing the done signal. Instead,
|
||||
// TransportProxy should receive the done signal directly, set its allocated
|
||||
// flag internally, and then reissue the done signal to Session.
|
||||
// Overall we should make TransportProxy receive *all* the signals from
|
||||
// Transport, since this removes the need to manually iterate over all
|
||||
// the transports, as is needed to make sure signals are handled properly
|
||||
// when BUNDLEing.
|
||||
// TODO(juberti): Per b/7998978, devs and QA are hitting this assert in ways
|
||||
// that make it prohibitively difficult to run dbg builds. Disabled for now.
|
||||
//ASSERT(!IsCandidateAllocationDone());
|
||||
for (TransportMap::iterator iter = transports_.begin();
|
||||
iter != transports_.end(); ++iter) {
|
||||
if (iter->second->impl() == transport) {
|
||||
iter->second->set_candidates_allocated(true);
|
||||
}
|
||||
}
|
||||
MaybeCandidateAllocationDone();
|
||||
}
|
||||
|
||||
bool BaseSession::IsCandidateAllocationDone() const {
|
||||
for (TransportMap::const_iterator iter = transports_.begin();
|
||||
iter != transports_.end(); ++iter) {
|
||||
if (!iter->second->candidates_allocated()) {
|
||||
LOG(LS_INFO) << "Candidate allocation not done for "
|
||||
<< iter->second->content_name();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseSession::MaybeCandidateAllocationDone() {
|
||||
if (IsCandidateAllocationDone()) {
|
||||
LOG(LS_INFO) << "Candidate gathering is complete.";
|
||||
OnCandidatesAllocationDone();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseSession::OnRoleConflict() {
|
||||
if (role_switch_) {
|
||||
LOG(LS_WARNING) << "Repeat of role conflict signal from Transport.";
|
||||
return;
|
||||
}
|
||||
|
||||
role_switch_ = true;
|
||||
for (TransportMap::iterator iter = transports_.begin();
|
||||
iter != transports_.end(); ++iter) {
|
||||
// Role will be reverse of initial role setting.
|
||||
IceRole role = initiator_ ? ICEROLE_CONTROLLED : ICEROLE_CONTROLLING;
|
||||
iter->second->SetIceRole(role);
|
||||
}
|
||||
void BaseSession::MaybeStartGathering() {
|
||||
transport_controller_->MaybeStartGathering();
|
||||
}
|
||||
|
||||
void BaseSession::LogState(State old_state, State new_state) {
|
||||
LOG(LS_INFO) << "Session:" << id()
|
||||
<< " Old state:" << StateToString(old_state)
|
||||
<< " New state:" << StateToString(new_state)
|
||||
<< " Type:" << content_type();
|
||||
<< " New state:" << StateToString(new_state);
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@ -16,14 +16,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/p2p/base/candidate.h"
|
||||
#include "webrtc/p2p/base/port.h"
|
||||
#include "webrtc/p2p/base/transport.h"
|
||||
#include "webrtc/base/refcount.h"
|
||||
#include "webrtc/base/rtccertificate.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/base/scoped_ref_ptr.h"
|
||||
#include "webrtc/base/socketaddress.h"
|
||||
#include "webrtc/p2p/base/candidate.h"
|
||||
#include "webrtc/p2p/base/port.h"
|
||||
#include "webrtc/p2p/base/transport.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
@ -31,136 +31,8 @@ class BaseSession;
|
||||
class P2PTransportChannel;
|
||||
class Transport;
|
||||
class TransportChannel;
|
||||
class TransportChannelProxy;
|
||||
class TransportChannelImpl;
|
||||
|
||||
typedef rtc::RefCountedObject<rtc::scoped_ptr<Transport> >
|
||||
TransportWrapper;
|
||||
|
||||
// Bundles a Transport and ChannelMap together. ChannelMap is used to
|
||||
// create transport channels before receiving or sending a session
|
||||
// initiate, and for speculatively connecting channels. Previously, a
|
||||
// session had one ChannelMap and transport. Now, with multiple
|
||||
// transports per session, we need multiple ChannelMaps as well.
|
||||
|
||||
typedef std::map<int, TransportChannelProxy*> ChannelMap;
|
||||
|
||||
class TransportProxy : public sigslot::has_slots<> {
|
||||
public:
|
||||
TransportProxy(
|
||||
rtc::Thread* worker_thread,
|
||||
const std::string& sid,
|
||||
const std::string& content_name,
|
||||
TransportWrapper* transport)
|
||||
: worker_thread_(worker_thread),
|
||||
sid_(sid),
|
||||
content_name_(content_name),
|
||||
transport_(transport),
|
||||
connecting_(false),
|
||||
negotiated_(false),
|
||||
sent_candidates_(false),
|
||||
candidates_allocated_(false),
|
||||
local_description_set_(false),
|
||||
remote_description_set_(false) {
|
||||
transport_->get()->SignalCandidatesReady.connect(
|
||||
this, &TransportProxy::OnTransportCandidatesReady);
|
||||
}
|
||||
~TransportProxy();
|
||||
|
||||
const std::string& content_name() const { return content_name_; }
|
||||
// TODO(juberti): It's not good form to expose the object you're wrapping,
|
||||
// since callers can mutate it. Can we make this return a const Transport*?
|
||||
Transport* impl() const { return transport_->get(); }
|
||||
|
||||
const std::string& type() const;
|
||||
bool negotiated() const { return negotiated_; }
|
||||
const Candidates& sent_candidates() const { return sent_candidates_; }
|
||||
const Candidates& unsent_candidates() const { return unsent_candidates_; }
|
||||
bool candidates_allocated() const { return candidates_allocated_; }
|
||||
void set_candidates_allocated(bool allocated) {
|
||||
candidates_allocated_ = allocated;
|
||||
}
|
||||
|
||||
TransportChannel* GetChannel(int component);
|
||||
TransportChannel* CreateChannel(int component);
|
||||
bool HasChannel(int component);
|
||||
void DestroyChannel(int component);
|
||||
|
||||
void AddSentCandidates(const Candidates& candidates);
|
||||
void AddUnsentCandidates(const Candidates& candidates);
|
||||
void ClearSentCandidates() { sent_candidates_.clear(); }
|
||||
void ClearUnsentCandidates() { unsent_candidates_.clear(); }
|
||||
|
||||
// Start the connection process for any channels, creating impls if needed.
|
||||
void ConnectChannels();
|
||||
// Hook up impls to the proxy channels. Doesn't change connect state.
|
||||
void CompleteNegotiation();
|
||||
|
||||
// Mux this proxy onto the specified proxy's transport.
|
||||
bool SetupMux(TransportProxy* proxy);
|
||||
|
||||
// Simple functions that thunk down to the same functions on Transport.
|
||||
void SetIceRole(IceRole role);
|
||||
void SetCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
|
||||
bool SetLocalTransportDescription(const TransportDescription& description,
|
||||
ContentAction action,
|
||||
std::string* error_desc);
|
||||
bool SetRemoteTransportDescription(const TransportDescription& description,
|
||||
ContentAction action,
|
||||
std::string* error_desc);
|
||||
void OnSignalingReady();
|
||||
bool OnRemoteCandidates(const Candidates& candidates, std::string* error);
|
||||
|
||||
// Called when a transport signals that it has new candidates.
|
||||
void OnTransportCandidatesReady(cricket::Transport* transport,
|
||||
const Candidates& candidates) {
|
||||
SignalCandidatesReady(this, candidates);
|
||||
}
|
||||
|
||||
bool local_description_set() const {
|
||||
return local_description_set_;
|
||||
}
|
||||
bool remote_description_set() const {
|
||||
return remote_description_set_;
|
||||
}
|
||||
|
||||
// Handles sending of ready candidates and receiving of remote candidates.
|
||||
sigslot::signal2<TransportProxy*,
|
||||
const std::vector<Candidate>&> SignalCandidatesReady;
|
||||
|
||||
private:
|
||||
TransportChannelProxy* GetChannelProxy(int component) const;
|
||||
|
||||
// Creates a new channel on the Transport which causes the reference
|
||||
// count to increment.
|
||||
void CreateChannelImpl(int component);
|
||||
void CreateChannelImpl_w(int component);
|
||||
|
||||
// Manipulators of transportchannelimpl in channel proxy.
|
||||
void SetChannelImplFromTransport(TransportChannelProxy* proxy, int component);
|
||||
void SetChannelImplFromTransport_w(TransportChannelProxy* proxy,
|
||||
int component);
|
||||
void ReplaceChannelImpl(TransportChannelProxy* proxy,
|
||||
TransportChannelImpl* impl);
|
||||
void ReplaceChannelImpl_w(TransportChannelProxy* proxy,
|
||||
TransportChannelImpl* impl);
|
||||
|
||||
rtc::Thread* const worker_thread_;
|
||||
const std::string sid_;
|
||||
const std::string content_name_;
|
||||
rtc::scoped_refptr<TransportWrapper> transport_;
|
||||
bool connecting_;
|
||||
bool negotiated_;
|
||||
ChannelMap channels_;
|
||||
Candidates sent_candidates_;
|
||||
Candidates unsent_candidates_;
|
||||
bool candidates_allocated_;
|
||||
bool local_description_set_;
|
||||
bool remote_description_set_;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, TransportProxy*> TransportMap;
|
||||
class TransportController;
|
||||
|
||||
// Statistics for all the transports of this session.
|
||||
typedef std::map<std::string, TransportStats> TransportStatsMap;
|
||||
@ -224,7 +96,6 @@ class BaseSession : public sigslot::has_slots<>,
|
||||
rtc::Thread* worker_thread,
|
||||
PortAllocator* port_allocator,
|
||||
const std::string& sid,
|
||||
const std::string& content_type,
|
||||
bool initiator);
|
||||
virtual ~BaseSession();
|
||||
|
||||
@ -236,14 +107,6 @@ class BaseSession : public sigslot::has_slots<>,
|
||||
// The ID of this session.
|
||||
const std::string& id() const { return sid_; }
|
||||
|
||||
// TODO(juberti): This data is largely redundant, as it can now be obtained
|
||||
// from local/remote_description(). Remove these functions and members.
|
||||
// Returns the XML namespace identifying the type of this session.
|
||||
const std::string& content_type() const { return content_type_; }
|
||||
|
||||
// Indicates whether we initiated this session.
|
||||
bool initiator() const { return initiator_; }
|
||||
|
||||
// Returns the application-level description given by our client.
|
||||
// If we are the recipient, this will be NULL until we send an accept.
|
||||
const SessionDescription* local_description() const;
|
||||
@ -260,6 +123,9 @@ class BaseSession : public sigslot::has_slots<>,
|
||||
// Takes ownership of SessionDescription*
|
||||
void set_remote_description(SessionDescription* sdesc);
|
||||
|
||||
void set_initiator(bool initiator);
|
||||
bool initiator() const { return initiator_; }
|
||||
|
||||
const SessionDescription* initiator_description() const;
|
||||
|
||||
// Returns the current state of the session. See the enum above for details.
|
||||
@ -280,151 +146,29 @@ class BaseSession : public sigslot::has_slots<>,
|
||||
// TODO(ronghuawu): remove the SetError method that doesn't take |error_desc|.
|
||||
virtual void SetError(Error error, const std::string& error_desc);
|
||||
|
||||
// Fired when the remote description is updated, with the updated
|
||||
// contents.
|
||||
sigslot::signal2<BaseSession* , const ContentInfos&>
|
||||
SignalRemoteDescriptionUpdate;
|
||||
|
||||
// Fired when SetState is called (regardless if there's a state change), which
|
||||
// indicates the session description might have be updated.
|
||||
sigslot::signal2<BaseSession*, ContentAction> SignalNewLocalDescription;
|
||||
|
||||
// Fired when SetState is called (regardless if there's a state change), which
|
||||
// indicates the session description might have be updated.
|
||||
sigslot::signal2<BaseSession*, ContentAction> SignalNewRemoteDescription;
|
||||
|
||||
// Returns the transport that has been negotiated or NULL if
|
||||
// negotiation is still in progress.
|
||||
virtual Transport* GetTransport(const std::string& content_name);
|
||||
|
||||
// Creates a new channel with the given names. This method may be called
|
||||
// immediately after creating the session. However, the actual
|
||||
// implementation may not be fixed until transport negotiation completes.
|
||||
// This will usually be called from the worker thread, but that
|
||||
// shouldn't be an issue since the main thread will be blocked in
|
||||
// Send when doing so.
|
||||
virtual TransportChannel* CreateChannel(const std::string& content_name,
|
||||
int component);
|
||||
|
||||
// Returns the channel with the given names.
|
||||
virtual TransportChannel* GetChannel(const std::string& content_name,
|
||||
int component);
|
||||
|
||||
// Destroys the channel with the given names.
|
||||
// This will usually be called from the worker thread, but that
|
||||
// shouldn't be an issue since the main thread will be blocked in
|
||||
// Send when doing so.
|
||||
virtual void DestroyChannel(const std::string& content_name,
|
||||
int component);
|
||||
|
||||
// Set the ice connection receiving timeout.
|
||||
void SetIceConnectionReceivingTimeout(int timeout_ms);
|
||||
|
||||
// For testing.
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>&
|
||||
certificate_for_testing() const {
|
||||
return certificate_;
|
||||
}
|
||||
// Start gathering candidates for any new transports, or transports doing an
|
||||
// ICE restart.
|
||||
void MaybeStartGathering();
|
||||
|
||||
protected:
|
||||
// Specifies the identity to use in this session.
|
||||
bool SetCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
|
||||
|
||||
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version);
|
||||
|
||||
bool PushdownTransportDescription(ContentSource source,
|
||||
ContentAction action,
|
||||
std::string* error_desc);
|
||||
void set_initiator(bool initiator) { initiator_ = initiator; }
|
||||
|
||||
const TransportMap& transport_proxies() const { return transports_; }
|
||||
// Get a TransportProxy by content_name or transport. NULL if not found.
|
||||
TransportProxy* GetTransportProxy(const std::string& content_name);
|
||||
void DestroyTransportProxy(const std::string& content_name);
|
||||
// TransportProxy is owned by session. Return proxy just for convenience.
|
||||
TransportProxy* GetOrCreateTransportProxy(const std::string& content_name);
|
||||
// Creates the actual transport object. Overridable for testing.
|
||||
virtual Transport* CreateTransport(const std::string& content_name);
|
||||
|
||||
void OnSignalingReady();
|
||||
void SpeculativelyConnectAllTransportChannels();
|
||||
// Helper method to provide remote candidates to the transport.
|
||||
bool OnRemoteCandidates(const std::string& content_name,
|
||||
const Candidates& candidates,
|
||||
std::string* error);
|
||||
|
||||
// This method will mux transport channels by content_name.
|
||||
// First content is used for muxing.
|
||||
bool MaybeEnableMuxingSupport();
|
||||
|
||||
// Called when a transport requests signaling.
|
||||
virtual void OnTransportRequestSignaling(Transport* transport) {
|
||||
}
|
||||
|
||||
// Called when the first channel of a transport begins connecting. We use
|
||||
// this to start a timer, to make sure that the connection completes in a
|
||||
// reasonable amount of time.
|
||||
virtual void OnTransportConnecting(Transport* transport) {
|
||||
}
|
||||
|
||||
// Called when a transport changes its writable state. We track this to make
|
||||
// sure that the transport becomes writable within a reasonable amount of
|
||||
// time. If this does not occur, we signal an error.
|
||||
virtual void OnTransportWritable(Transport* transport) {
|
||||
}
|
||||
virtual void OnTransportReadable(Transport* transport) {
|
||||
}
|
||||
|
||||
virtual void OnTransportReceiving(Transport* transport) {
|
||||
}
|
||||
|
||||
// Called when a transport has found its steady-state connections.
|
||||
virtual void OnTransportCompleted(Transport* transport) {
|
||||
}
|
||||
|
||||
// Called when a transport has failed permanently.
|
||||
virtual void OnTransportFailed(Transport* transport) {
|
||||
}
|
||||
|
||||
// Called when a transport signals that it has new candidates.
|
||||
virtual void OnTransportProxyCandidatesReady(TransportProxy* proxy,
|
||||
const Candidates& candidates) {
|
||||
}
|
||||
|
||||
virtual void OnTransportRouteChange(
|
||||
Transport* transport,
|
||||
int component,
|
||||
const cricket::Candidate& remote_candidate) {
|
||||
}
|
||||
|
||||
virtual void OnTransportCandidatesAllocationDone(Transport* transport);
|
||||
|
||||
// Called when all transport channels allocated required candidates.
|
||||
// This method should be used as an indication of candidates gathering process
|
||||
// is completed and application can now send local candidates list to remote.
|
||||
virtual void OnCandidatesAllocationDone() {
|
||||
}
|
||||
|
||||
// Handles the ice role change callback from Transport. This must be
|
||||
// propagated to all the transports.
|
||||
virtual void OnRoleConflict();
|
||||
|
||||
// Handles messages posted to us.
|
||||
virtual void OnMessage(rtc::Message *pmsg);
|
||||
|
||||
protected:
|
||||
bool IsCandidateAllocationDone() const;
|
||||
TransportController* transport_controller() {
|
||||
return transport_controller_.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
State state_;
|
||||
Error error_;
|
||||
std::string error_desc_;
|
||||
|
||||
// This method will delete the Transport and TransportChannelImpls
|
||||
// and replace those with the Transport object of the first
|
||||
// MediaContent in bundle_group.
|
||||
bool BundleContentGroup(const ContentGroup* bundle_group);
|
||||
|
||||
private:
|
||||
// Helper methods to push local and remote transport descriptions.
|
||||
bool PushdownLocalTransportDescription(
|
||||
@ -434,8 +178,6 @@ class BaseSession : public sigslot::has_slots<>,
|
||||
const SessionDescription* sdesc, ContentAction action,
|
||||
std::string* error_desc);
|
||||
|
||||
void MaybeCandidateAllocationDone();
|
||||
|
||||
// Log session state.
|
||||
void LogState(State old_state, State new_state);
|
||||
|
||||
@ -449,21 +191,10 @@ class BaseSession : public sigslot::has_slots<>,
|
||||
rtc::Thread* const worker_thread_;
|
||||
PortAllocator* const port_allocator_;
|
||||
const std::string sid_;
|
||||
const std::string content_type_;
|
||||
bool initiator_;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
|
||||
rtc::SSLProtocolVersion ssl_max_version_;
|
||||
rtc::scoped_ptr<TransportController> transport_controller_;
|
||||
rtc::scoped_ptr<const SessionDescription> local_description_;
|
||||
rtc::scoped_ptr<SessionDescription> remote_description_;
|
||||
uint64 ice_tiebreaker_;
|
||||
// This flag will be set to true after the first role switch. This flag
|
||||
// will enable us to stop any role switch during the call.
|
||||
bool role_switch_;
|
||||
TransportMap transports_;
|
||||
|
||||
// Timeout value in milliseconds for which no ICE connection receives
|
||||
// any packets.
|
||||
int ice_receiving_timeout_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/base/gunit.h"
|
||||
#include "webrtc/base/helpers.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/base/thread.h"
|
||||
#include "webrtc/p2p/base/dtlstransportchannel.h"
|
||||
#include "webrtc/p2p/base/p2ptransportchannel.h"
|
||||
#include "webrtc/p2p/base/portallocator.h"
|
||||
#include "webrtc/p2p/base/session.h"
|
||||
#include "webrtc/p2p/base/transportchannelproxy.h"
|
||||
#include "webrtc/p2p/client/fakeportallocator.h"
|
||||
|
||||
using cricket::BaseSession;
|
||||
using cricket::DtlsTransportChannelWrapper;
|
||||
using cricket::FakePortAllocator;
|
||||
using cricket::P2PTransportChannel;
|
||||
using cricket::PortAllocator;
|
||||
using cricket::TransportChannelProxy;
|
||||
using cricket::TransportProxy;
|
||||
|
||||
class BaseSessionForTest : public BaseSession {
|
||||
public:
|
||||
BaseSessionForTest(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
PortAllocator* port_allocator,
|
||||
const std::string& sid,
|
||||
const std::string& content_type,
|
||||
bool initiator)
|
||||
: BaseSession(signaling_thread,
|
||||
worker_thread,
|
||||
port_allocator,
|
||||
sid,
|
||||
content_type,
|
||||
initiator) {}
|
||||
using BaseSession::GetOrCreateTransportProxy;
|
||||
};
|
||||
|
||||
class BaseSessionTest : public testing::Test {
|
||||
public:
|
||||
BaseSessionTest()
|
||||
: port_allocator_(new FakePortAllocator(rtc::Thread::Current(), nullptr)),
|
||||
session_(new BaseSessionForTest(rtc::Thread::Current(),
|
||||
rtc::Thread::Current(),
|
||||
port_allocator_.get(),
|
||||
"123",
|
||||
cricket::NS_JINGLE_RTP,
|
||||
false)) {}
|
||||
P2PTransportChannel* CreateChannel(const std::string& content,
|
||||
int component) {
|
||||
TransportProxy* transport_proxy =
|
||||
session_->GetOrCreateTransportProxy(content);
|
||||
// This hacking is needed in order that the p2p transport channel
|
||||
// will be created in the following.
|
||||
transport_proxy->CompleteNegotiation();
|
||||
|
||||
TransportChannelProxy* channel_proxy = static_cast<TransportChannelProxy*>(
|
||||
session_->CreateChannel(content, component));
|
||||
DtlsTransportChannelWrapper* dtls_channel =
|
||||
static_cast<DtlsTransportChannelWrapper*>(channel_proxy->impl());
|
||||
return static_cast<P2PTransportChannel*>(dtls_channel->channel());
|
||||
}
|
||||
|
||||
rtc::scoped_ptr<PortAllocator> port_allocator_;
|
||||
rtc::scoped_ptr<BaseSessionForTest> session_;
|
||||
};
|
||||
|
||||
TEST_F(BaseSessionTest, TestSetIceReceivingTimeout) {
|
||||
P2PTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(channel1, nullptr);
|
||||
// These are the default values.
|
||||
EXPECT_EQ(2500, channel1->receiving_timeout());
|
||||
EXPECT_EQ(250, channel1->check_receiving_delay());
|
||||
// Set the timeout to a different value.
|
||||
session_->SetIceConnectionReceivingTimeout(1000);
|
||||
EXPECT_EQ(1000, channel1->receiving_timeout());
|
||||
EXPECT_EQ(100, channel1->check_receiving_delay());
|
||||
|
||||
// Even if a channel is created after setting the receiving timeout,
|
||||
// the set timeout value is applied to the new channel.
|
||||
P2PTransportChannel* channel2 = CreateChannel("video", 2);
|
||||
ASSERT_NE(channel2, nullptr);
|
||||
EXPECT_EQ(1000, channel2->receiving_timeout());
|
||||
EXPECT_EQ(100, channel2->check_receiving_delay());
|
||||
|
||||
// Test minimum checking delay.
|
||||
session_->SetIceConnectionReceivingTimeout(200);
|
||||
EXPECT_EQ(200, channel1->receiving_timeout());
|
||||
EXPECT_EQ(50, channel1->check_receiving_delay());
|
||||
EXPECT_EQ(200, channel2->receiving_timeout());
|
||||
EXPECT_EQ(50, channel2->check_receiving_delay());
|
||||
}
|
||||
@ -8,6 +8,8 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <utility> // for std::pair
|
||||
|
||||
#include "webrtc/p2p/base/transport.h"
|
||||
|
||||
#include "webrtc/p2p/base/candidate.h"
|
||||
@ -22,39 +24,6 @@ namespace cricket {
|
||||
|
||||
using rtc::Bind;
|
||||
|
||||
enum {
|
||||
MSG_ONSIGNALINGREADY = 1,
|
||||
MSG_ONREMOTECANDIDATE,
|
||||
MSG_WRITESTATE,
|
||||
MSG_REQUESTSIGNALING,
|
||||
MSG_CANDIDATEREADY,
|
||||
MSG_ROUTECHANGE,
|
||||
MSG_CONNECTING,
|
||||
MSG_CANDIDATEALLOCATIONCOMPLETE,
|
||||
MSG_ROLECONFLICT,
|
||||
MSG_COMPLETED,
|
||||
MSG_FAILED,
|
||||
MSG_RECEIVINGSTATE,
|
||||
};
|
||||
|
||||
struct ChannelParams : public rtc::MessageData {
|
||||
ChannelParams() : channel(NULL), candidate(NULL) {}
|
||||
explicit ChannelParams(int component)
|
||||
: component(component), channel(NULL), candidate(NULL) {}
|
||||
explicit ChannelParams(Candidate* candidate)
|
||||
: channel(NULL), candidate(candidate) {
|
||||
}
|
||||
|
||||
~ChannelParams() {
|
||||
delete candidate;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
int component;
|
||||
TransportChannelImpl* channel;
|
||||
Candidate* candidate;
|
||||
};
|
||||
|
||||
static bool VerifyIceParams(const TransportDescription& desc) {
|
||||
// For legacy protocols.
|
||||
if (desc.ice_ufrag.empty() && desc.ice_pwd.empty())
|
||||
@ -96,58 +65,59 @@ static bool IceCredentialsChanged(const TransportDescription& old_desc,
|
||||
new_desc.ice_ufrag, new_desc.ice_pwd);
|
||||
}
|
||||
|
||||
Transport::Transport(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
const std::string& content_name,
|
||||
PortAllocator* allocator)
|
||||
: signaling_thread_(signaling_thread),
|
||||
worker_thread_(worker_thread),
|
||||
content_name_(content_name),
|
||||
allocator_(allocator),
|
||||
destroyed_(false),
|
||||
readable_(TRANSPORT_STATE_NONE),
|
||||
writable_(TRANSPORT_STATE_NONE),
|
||||
receiving_(TRANSPORT_STATE_NONE),
|
||||
was_writable_(false),
|
||||
connect_requested_(false),
|
||||
ice_role_(ICEROLE_UNKNOWN),
|
||||
tiebreaker_(0),
|
||||
remote_ice_mode_(ICEMODE_FULL),
|
||||
channel_receiving_timeout_(-1) {
|
||||
}
|
||||
Transport::Transport(const std::string& name, PortAllocator* allocator)
|
||||
: name_(name), allocator_(allocator) {}
|
||||
|
||||
Transport::~Transport() {
|
||||
ASSERT(signaling_thread_->IsCurrent());
|
||||
ASSERT(destroyed_);
|
||||
ASSERT(channels_destroyed_);
|
||||
}
|
||||
|
||||
bool Transport::AllChannelsCompleted() const {
|
||||
// We aren't completed until at least one channel is complete, so if there
|
||||
// are no channels, we aren't complete yet.
|
||||
if (channels_.empty()) {
|
||||
LOG(LS_INFO) << name() << " transport is not complete"
|
||||
<< " because it has no TransportChannels";
|
||||
return false;
|
||||
}
|
||||
|
||||
// A Transport's ICE process is completed if all of its channels are writable,
|
||||
// have finished allocating candidates, and have pruned all but one of their
|
||||
// connections.
|
||||
for (const auto& iter : channels_) {
|
||||
const TransportChannelImpl* channel = iter.second.get();
|
||||
bool complete =
|
||||
channel->writable() &&
|
||||
channel->GetState() == TransportChannelState::STATE_COMPLETED &&
|
||||
channel->GetIceRole() == ICEROLE_CONTROLLING &&
|
||||
channel->gathering_state() == kIceGatheringComplete;
|
||||
if (!complete) {
|
||||
LOG(LS_INFO) << name() << " transport is not complete"
|
||||
<< " because a channel is still incomplete.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Transport::AnyChannelFailed() const {
|
||||
for (const auto& iter : channels_) {
|
||||
if (iter.second->GetState() == TransportChannelState::STATE_FAILED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Transport::SetIceRole(IceRole role) {
|
||||
worker_thread_->Invoke<void>(Bind(&Transport::SetIceRole_w, this, role));
|
||||
}
|
||||
|
||||
void Transport::SetCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
|
||||
worker_thread_->Invoke<void>(Bind(&Transport::SetCertificate_w, this,
|
||||
certificate));
|
||||
}
|
||||
|
||||
bool Transport::GetCertificate(
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
|
||||
// The identity is set on the worker thread, so for safety it must also be
|
||||
// acquired on the worker thread.
|
||||
return worker_thread_->Invoke<bool>(
|
||||
Bind(&Transport::GetCertificate_w, this, certificate));
|
||||
ice_role_ = role;
|
||||
for (auto& iter : channels_) {
|
||||
iter.second->SetIceRole(ice_role_);
|
||||
}
|
||||
}
|
||||
|
||||
bool Transport::GetRemoteSSLCertificate(rtc::SSLCertificate** cert) {
|
||||
// Channels can be deleted on the worker thread, so for safety the remote
|
||||
// certificate is acquired on the worker thread.
|
||||
return worker_thread_->Invoke<bool>(
|
||||
Bind(&Transport::GetRemoteSSLCertificate_w, this, cert));
|
||||
}
|
||||
|
||||
bool Transport::GetRemoteSSLCertificate_w(rtc::SSLCertificate** cert) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
if (channels_.empty())
|
||||
return false;
|
||||
|
||||
@ -156,12 +126,6 @@ bool Transport::GetRemoteSSLCertificate_w(rtc::SSLCertificate** cert) {
|
||||
}
|
||||
|
||||
void Transport::SetChannelReceivingTimeout(int timeout_ms) {
|
||||
worker_thread_->Invoke<void>(
|
||||
Bind(&Transport::SetChannelReceivingTimeout_w, this, timeout_ms));
|
||||
}
|
||||
|
||||
void Transport::SetChannelReceivingTimeout_w(int timeout_ms) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
channel_receiving_timeout_ = timeout_ms;
|
||||
for (const auto& kv : channels_) {
|
||||
kv.second->SetReceivingTimeout(timeout_ms);
|
||||
@ -172,35 +136,73 @@ bool Transport::SetLocalTransportDescription(
|
||||
const TransportDescription& description,
|
||||
ContentAction action,
|
||||
std::string* error_desc) {
|
||||
return worker_thread_->Invoke<bool>(Bind(
|
||||
&Transport::SetLocalTransportDescription_w, this,
|
||||
description, action, error_desc));
|
||||
bool ret = true;
|
||||
|
||||
if (!VerifyIceParams(description)) {
|
||||
return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
|
||||
error_desc);
|
||||
}
|
||||
|
||||
if (local_description_ &&
|
||||
IceCredentialsChanged(*local_description_, description)) {
|
||||
IceRole new_ice_role =
|
||||
(action == CA_OFFER) ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED;
|
||||
|
||||
// It must be called before ApplyLocalTransportDescription, which may
|
||||
// trigger an ICE restart and depends on the new ICE role.
|
||||
SetIceRole(new_ice_role);
|
||||
}
|
||||
|
||||
local_description_.reset(new TransportDescription(description));
|
||||
|
||||
for (auto& iter : channels_) {
|
||||
ret &= ApplyLocalTransportDescription(iter.second.get(), error_desc);
|
||||
}
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
|
||||
if (action == CA_PRANSWER || action == CA_ANSWER) {
|
||||
ret &= NegotiateTransportDescription(action, error_desc);
|
||||
}
|
||||
if (ret) {
|
||||
local_description_set_ = true;
|
||||
ConnectChannels();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Transport::SetRemoteTransportDescription(
|
||||
const TransportDescription& description,
|
||||
ContentAction action,
|
||||
std::string* error_desc) {
|
||||
return worker_thread_->Invoke<bool>(Bind(
|
||||
&Transport::SetRemoteTransportDescription_w, this,
|
||||
description, action, error_desc));
|
||||
bool ret = true;
|
||||
|
||||
if (!VerifyIceParams(description)) {
|
||||
return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
|
||||
error_desc);
|
||||
}
|
||||
|
||||
remote_description_.reset(new TransportDescription(description));
|
||||
for (auto& iter : channels_) {
|
||||
ret &= ApplyRemoteTransportDescription(iter.second.get(), error_desc);
|
||||
}
|
||||
|
||||
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
|
||||
if (action == CA_PRANSWER || action == CA_ANSWER) {
|
||||
ret = NegotiateTransportDescription(CA_OFFER, error_desc);
|
||||
}
|
||||
if (ret) {
|
||||
remote_description_set_ = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
TransportChannelImpl* Transport::CreateChannel(int component) {
|
||||
return worker_thread_->Invoke<TransportChannelImpl*>(Bind(
|
||||
&Transport::CreateChannel_w, this, component));
|
||||
}
|
||||
|
||||
TransportChannelImpl* Transport::CreateChannel_w(int component) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
TransportChannelImpl* impl;
|
||||
// TODO(tommi): We don't really need to grab the lock until the actual call
|
||||
// to insert() below and presumably hold it throughout initialization of
|
||||
// |impl| after the impl_exists check. Maybe we can factor that out to
|
||||
// a separate function and not grab the lock in this function.
|
||||
// Actually, we probably don't need to hold the lock while initializing
|
||||
// |impl| since we can just do the insert when that's done.
|
||||
rtc::CritScope cs(&crit_);
|
||||
|
||||
// Create the entry if it does not exist.
|
||||
bool impl_exists = false;
|
||||
@ -216,7 +218,7 @@ TransportChannelImpl* Transport::CreateChannel_w(int component) {
|
||||
|
||||
// Increase the ref count.
|
||||
iterator->second.AddRef();
|
||||
destroyed_ = false;
|
||||
channels_destroyed_ = false;
|
||||
|
||||
if (impl_exists) {
|
||||
// If this is an existing channel, we should just return it without
|
||||
@ -228,23 +230,21 @@ TransportChannelImpl* Transport::CreateChannel_w(int component) {
|
||||
impl->SetIceRole(ice_role_);
|
||||
impl->SetIceTiebreaker(tiebreaker_);
|
||||
impl->SetReceivingTimeout(channel_receiving_timeout_);
|
||||
// TODO(ronghuawu): Change CreateChannel_w to be able to return error since
|
||||
// below Apply**Description_w calls can fail.
|
||||
// TODO(ronghuawu): Change CreateChannel to be able to return error since
|
||||
// below Apply**Description calls can fail.
|
||||
if (local_description_)
|
||||
ApplyLocalTransportDescription_w(impl, NULL);
|
||||
ApplyLocalTransportDescription(impl, NULL);
|
||||
if (remote_description_)
|
||||
ApplyRemoteTransportDescription_w(impl, NULL);
|
||||
ApplyRemoteTransportDescription(impl, NULL);
|
||||
if (local_description_ && remote_description_)
|
||||
ApplyNegotiatedTransportDescription_w(impl, NULL);
|
||||
ApplyNegotiatedTransportDescription(impl, NULL);
|
||||
|
||||
impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState);
|
||||
impl->SignalReceivingState.connect(this, &Transport::OnChannelReceivingState);
|
||||
impl->SignalRequestSignaling.connect(
|
||||
this, &Transport::OnChannelRequestSignaling);
|
||||
impl->SignalCandidateReady.connect(this, &Transport::OnChannelCandidateReady);
|
||||
impl->SignalGatheringState.connect(this, &Transport::OnChannelGatheringState);
|
||||
impl->SignalCandidateGathered.connect(this,
|
||||
&Transport::OnChannelCandidateGathered);
|
||||
impl->SignalRouteChange.connect(this, &Transport::OnChannelRouteChange);
|
||||
impl->SignalCandidatesAllocationDone.connect(
|
||||
this, &Transport::OnChannelCandidatesAllocationDone);
|
||||
impl->SignalRoleConflict.connect(this, &Transport::OnRoleConflict);
|
||||
impl->SignalConnectionRemoved.connect(
|
||||
this, &Transport::OnChannelConnectionRemoved);
|
||||
@ -254,36 +254,22 @@ TransportChannelImpl* Transport::CreateChannel_w(int component) {
|
||||
if (channels_.size() == 1) {
|
||||
// If this is the first channel, then indicate that we have started
|
||||
// connecting.
|
||||
signaling_thread()->Post(this, MSG_CONNECTING, NULL);
|
||||
SignalConnecting(this);
|
||||
}
|
||||
}
|
||||
return impl;
|
||||
}
|
||||
|
||||
TransportChannelImpl* Transport::GetChannel(int component) {
|
||||
// TODO(tommi,pthatcher): Since we're returning a pointer from the channels_
|
||||
// map, shouldn't we assume that we're on the worker thread? (The pointer
|
||||
// will be used outside of the lock).
|
||||
// And if we're on the worker thread, which is the only thread that modifies
|
||||
// channels_, can we skip grabbing the lock?
|
||||
rtc::CritScope cs(&crit_);
|
||||
ChannelMap::iterator iter = channels_.find(component);
|
||||
return (iter != channels_.end()) ? iter->second.get() : NULL;
|
||||
}
|
||||
|
||||
bool Transport::HasChannels() {
|
||||
rtc::CritScope cs(&crit_);
|
||||
return !channels_.empty();
|
||||
}
|
||||
|
||||
void Transport::DestroyChannel(int component) {
|
||||
worker_thread_->Invoke<void>(Bind(
|
||||
&Transport::DestroyChannel_w, this, component));
|
||||
}
|
||||
|
||||
void Transport::DestroyChannel_w(int component) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
|
||||
ChannelMap::iterator iter = channels_.find(component);
|
||||
if (iter == channels_.end())
|
||||
return;
|
||||
@ -293,34 +279,30 @@ void Transport::DestroyChannel_w(int component) {
|
||||
iter->second.DecRef();
|
||||
if (!iter->second.ref()) {
|
||||
impl = iter->second.get();
|
||||
rtc::CritScope cs(&crit_);
|
||||
channels_.erase(iter);
|
||||
}
|
||||
|
||||
if (connect_requested_ && channels_.empty()) {
|
||||
// We're no longer attempting to connect.
|
||||
signaling_thread()->Post(this, MSG_CONNECTING, NULL);
|
||||
SignalConnecting(this);
|
||||
}
|
||||
|
||||
if (impl) {
|
||||
// Check in case the deleted channel was the only non-writable channel.
|
||||
OnChannelWritableState(impl);
|
||||
DestroyTransportChannel(impl);
|
||||
// Need to update aggregate state after destroying a channel,
|
||||
// for example if it was the only one that wasn't yet writable.
|
||||
UpdateWritableState();
|
||||
UpdateReceivingState();
|
||||
UpdateGatheringState();
|
||||
MaybeSignalCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::ConnectChannels() {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
worker_thread_->Invoke<void>(Bind(&Transport::ConnectChannels_w, this));
|
||||
}
|
||||
|
||||
void Transport::ConnectChannels_w() {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
if (connect_requested_ || channels_.empty())
|
||||
return;
|
||||
|
||||
connect_requested_ = true;
|
||||
signaling_thread()->Post(this, MSG_CANDIDATEREADY, NULL);
|
||||
|
||||
if (!local_description_) {
|
||||
// TOOD(mallinath) : TransportDescription(TD) shouldn't be generated here.
|
||||
@ -329,38 +311,28 @@ void Transport::ConnectChannels_w() {
|
||||
// Session.
|
||||
// Session must generate local TD before remote candidates pushed when
|
||||
// initiate request initiated by the remote.
|
||||
LOG(LS_INFO) << "Transport::ConnectChannels_w: No local description has "
|
||||
LOG(LS_INFO) << "Transport::ConnectChannels: No local description has "
|
||||
<< "been set. Will generate one.";
|
||||
TransportDescription desc(std::vector<std::string>(),
|
||||
rtc::CreateRandomString(ICE_UFRAG_LENGTH),
|
||||
rtc::CreateRandomString(ICE_PWD_LENGTH),
|
||||
ICEMODE_FULL, CONNECTIONROLE_NONE, NULL,
|
||||
Candidates());
|
||||
SetLocalTransportDescription_w(desc, CA_OFFER, NULL);
|
||||
TransportDescription desc(
|
||||
std::vector<std::string>(), rtc::CreateRandomString(ICE_UFRAG_LENGTH),
|
||||
rtc::CreateRandomString(ICE_PWD_LENGTH), ICEMODE_FULL,
|
||||
CONNECTIONROLE_NONE, NULL, Candidates());
|
||||
SetLocalTransportDescription(desc, CA_OFFER, NULL);
|
||||
}
|
||||
|
||||
CallChannels_w(&TransportChannelImpl::Connect);
|
||||
if (!channels_.empty()) {
|
||||
signaling_thread()->Post(this, MSG_CONNECTING, NULL);
|
||||
CallChannels(&TransportChannelImpl::Connect);
|
||||
if (HasChannels()) {
|
||||
SignalConnecting(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::OnConnecting_s() {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
SignalConnecting(this);
|
||||
void Transport::MaybeStartGathering() {
|
||||
if (connect_requested_) {
|
||||
CallChannels(&TransportChannelImpl::MaybeStartGathering);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::DestroyAllChannels() {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
worker_thread_->Invoke<void>(Bind(&Transport::DestroyAllChannels_w, this));
|
||||
worker_thread()->Clear(this);
|
||||
signaling_thread()->Clear(this);
|
||||
destroyed_ = true;
|
||||
}
|
||||
|
||||
void Transport::DestroyAllChannels_w() {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
|
||||
std::vector<TransportChannelImpl*> impls;
|
||||
for (auto& iter : channels_) {
|
||||
iter.second.DecRef();
|
||||
@ -368,27 +340,15 @@ void Transport::DestroyAllChannels_w() {
|
||||
impls.push_back(iter.second.get());
|
||||
}
|
||||
|
||||
{
|
||||
rtc::CritScope cs(&crit_);
|
||||
channels_.clear();
|
||||
channels_.clear();
|
||||
|
||||
for (TransportChannelImpl* impl : impls) {
|
||||
DestroyTransportChannel(impl);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < impls.size(); ++i)
|
||||
DestroyTransportChannel(impls[i]);
|
||||
channels_destroyed_ = true;
|
||||
}
|
||||
|
||||
void Transport::OnSignalingReady() {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
if (destroyed_) return;
|
||||
|
||||
worker_thread()->Post(this, MSG_ONSIGNALINGREADY, NULL);
|
||||
|
||||
// Notify the subclass.
|
||||
OnTransportSignalingReady();
|
||||
}
|
||||
|
||||
void Transport::CallChannels_w(TransportChannelFunc func) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
void Transport::CallChannels(TransportChannelFunc func) {
|
||||
for (const auto& iter : channels_) {
|
||||
((iter.second.get())->*func)();
|
||||
}
|
||||
@ -427,14 +387,7 @@ bool Transport::VerifyCandidate(const Candidate& cand, std::string* error) {
|
||||
|
||||
|
||||
bool Transport::GetStats(TransportStats* stats) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
return worker_thread_->Invoke<bool>(Bind(
|
||||
&Transport::GetStats_w, this, stats));
|
||||
}
|
||||
|
||||
bool Transport::GetStats_w(TransportStats* stats) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
stats->content_name = content_name();
|
||||
stats->transport_name = name();
|
||||
stats->channel_stats.clear();
|
||||
for (auto iter : channels_) {
|
||||
ChannelMapEntry& entry = iter.second;
|
||||
@ -450,82 +403,45 @@ bool Transport::GetStats_w(TransportStats* stats) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Transport::GetSslRole(rtc::SSLRole* ssl_role) const {
|
||||
return worker_thread_->Invoke<bool>(Bind(
|
||||
&Transport::GetSslRole_w, this, ssl_role));
|
||||
}
|
||||
bool Transport::AddRemoteCandidates(const std::vector<Candidate>& candidates,
|
||||
std::string* error) {
|
||||
ASSERT(!channels_destroyed_);
|
||||
// Verify each candidate before passing down to transport layer.
|
||||
for (const Candidate& cand : candidates) {
|
||||
if (!VerifyCandidate(cand, error)) {
|
||||
return false;
|
||||
}
|
||||
if (!HasChannel(cand.component())) {
|
||||
*error = "Candidate has unknown component: " + cand.ToString() +
|
||||
" for content: " + name();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Transport::SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) {
|
||||
return worker_thread_->Invoke<bool>(Bind(
|
||||
&Transport::SetSslMaxProtocolVersion_w, this, version));
|
||||
}
|
||||
|
||||
void Transport::OnRemoteCandidates(const std::vector<Candidate>& candidates) {
|
||||
for (std::vector<Candidate>::const_iterator iter = candidates.begin();
|
||||
iter != candidates.end();
|
||||
++iter) {
|
||||
OnRemoteCandidate(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::OnRemoteCandidate(const Candidate& candidate) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
if (destroyed_) return;
|
||||
|
||||
if (!HasChannel(candidate.component())) {
|
||||
LOG(LS_WARNING) << "Ignoring candidate for unknown component "
|
||||
<< candidate.component();
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelParams* params = new ChannelParams(new Candidate(candidate));
|
||||
worker_thread()->Post(this, MSG_ONREMOTECANDIDATE, params);
|
||||
}
|
||||
|
||||
void Transport::OnRemoteCandidate_w(const Candidate& candidate) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
ChannelMap::iterator iter = channels_.find(candidate.component());
|
||||
// It's ok for a channel to go away while this message is in transit.
|
||||
if (iter != channels_.end()) {
|
||||
iter->second->OnCandidate(candidate);
|
||||
TransportChannelImpl* channel = GetChannel(iter->component());
|
||||
if (channel != NULL) {
|
||||
channel->AddRemoteCandidate(*iter);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Transport::OnChannelWritableState(TransportChannel* channel) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
signaling_thread()->Post(this, MSG_WRITESTATE, NULL);
|
||||
|
||||
MaybeCompleted_w();
|
||||
}
|
||||
|
||||
void Transport::OnChannelWritableState_s() {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
TransportState writable = GetTransportState_s(TRANSPORT_WRITABLE_STATE);
|
||||
if (writable_ != writable) {
|
||||
was_writable_ = (writable_ == TRANSPORT_STATE_ALL);
|
||||
writable_ = writable;
|
||||
SignalWritableState(this);
|
||||
}
|
||||
LOG(LS_INFO) << name() << " TransportChannel " << channel->component()
|
||||
<< " writability changed to " << channel->writable()
|
||||
<< ". Check if transport is complete.";
|
||||
UpdateWritableState();
|
||||
MaybeSignalCompleted();
|
||||
}
|
||||
|
||||
void Transport::OnChannelReceivingState(TransportChannel* channel) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
signaling_thread()->Post(this, MSG_RECEIVINGSTATE);
|
||||
UpdateReceivingState();
|
||||
}
|
||||
|
||||
void Transport::OnChannelReceivingState_s() {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
TransportState receiving = GetTransportState_s(TRANSPORT_RECEIVING_STATE);
|
||||
if (receiving_ != receiving) {
|
||||
receiving_ = receiving;
|
||||
SignalReceivingState(this);
|
||||
}
|
||||
}
|
||||
|
||||
TransportState Transport::GetTransportState_s(TransportStateType state_type) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
|
||||
rtc::CritScope cs(&crit_);
|
||||
TransportState Transport::GetTransportState(TransportStateType state_type) {
|
||||
bool any = false;
|
||||
bool all = !channels_.empty();
|
||||
for (const auto iter : channels_) {
|
||||
@ -553,106 +469,44 @@ TransportState Transport::GetTransportState_s(TransportStateType state_type) {
|
||||
return TRANSPORT_STATE_NONE;
|
||||
}
|
||||
|
||||
void Transport::OnChannelRequestSignaling(TransportChannelImpl* channel) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
// Resetting ICE state for the channel.
|
||||
ChannelMap::iterator iter = channels_.find(channel->component());
|
||||
if (iter != channels_.end())
|
||||
iter->second.set_candidates_allocated(false);
|
||||
signaling_thread()->Post(this, MSG_REQUESTSIGNALING, nullptr);
|
||||
void Transport::OnChannelGatheringState(TransportChannelImpl* channel) {
|
||||
ASSERT(channels_.find(channel->component()) != channels_.end());
|
||||
UpdateGatheringState();
|
||||
if (gathering_state_ == kIceGatheringComplete) {
|
||||
// If UpdateGatheringState brought us to kIceGatheringComplete, check if
|
||||
// our connection state is also "Completed". Otherwise, there's no point in
|
||||
// checking (since it would only produce log messages).
|
||||
MaybeSignalCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::OnChannelRequestSignaling_s() {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
LOG(LS_INFO) << "Transport: " << content_name_ << ", allocating candidates";
|
||||
SignalRequestSignaling(this);
|
||||
}
|
||||
|
||||
void Transport::OnChannelCandidateReady(TransportChannelImpl* channel,
|
||||
const Candidate& candidate) {
|
||||
void Transport::OnChannelCandidateGathered(TransportChannelImpl* channel,
|
||||
const Candidate& candidate) {
|
||||
// We should never signal peer-reflexive candidates.
|
||||
if (candidate.type() == PRFLX_PORT_TYPE) {
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
rtc::CritScope cs(&crit_);
|
||||
ready_candidates_.push_back(candidate);
|
||||
|
||||
// We hold any messages until the client lets us connect.
|
||||
if (connect_requested_) {
|
||||
signaling_thread()->Post(
|
||||
this, MSG_CANDIDATEREADY, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::OnChannelCandidateReady_s() {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
ASSERT(connect_requested_);
|
||||
|
||||
std::vector<Candidate> candidates;
|
||||
{
|
||||
rtc::CritScope cs(&crit_);
|
||||
candidates.swap(ready_candidates_);
|
||||
}
|
||||
|
||||
// we do the deleting of Candidate* here to keep the new above and
|
||||
// delete below close to each other
|
||||
if (!candidates.empty()) {
|
||||
SignalCandidatesReady(this, candidates);
|
||||
}
|
||||
candidates.push_back(candidate);
|
||||
SignalCandidatesGathered(this, candidates);
|
||||
}
|
||||
|
||||
void Transport::OnChannelRouteChange(TransportChannel* channel,
|
||||
const Candidate& remote_candidate) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
ChannelParams* params = new ChannelParams(new Candidate(remote_candidate));
|
||||
params->channel = static_cast<cricket::TransportChannelImpl*>(channel);
|
||||
signaling_thread()->Post(this, MSG_ROUTECHANGE, params);
|
||||
}
|
||||
|
||||
void Transport::OnChannelRouteChange_s(const TransportChannel* channel,
|
||||
const Candidate& remote_candidate) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
SignalRouteChange(this, remote_candidate.component(), remote_candidate);
|
||||
}
|
||||
|
||||
void Transport::OnChannelCandidatesAllocationDone(
|
||||
TransportChannelImpl* channel) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
ChannelMap::iterator iter = channels_.find(channel->component());
|
||||
ASSERT(iter != channels_.end());
|
||||
LOG(LS_INFO) << "Transport: " << content_name_ << ", component "
|
||||
<< channel->component() << " allocation complete";
|
||||
|
||||
iter->second.set_candidates_allocated(true);
|
||||
|
||||
// If all channels belonging to this Transport got signal, then
|
||||
// forward this signal to upper layer.
|
||||
// Can this signal arrive before all transport channels are created?
|
||||
for (auto& iter : channels_) {
|
||||
if (!iter.second.candidates_allocated())
|
||||
return;
|
||||
}
|
||||
signaling_thread_->Post(this, MSG_CANDIDATEALLOCATIONCOMPLETE);
|
||||
|
||||
MaybeCompleted_w();
|
||||
}
|
||||
|
||||
void Transport::OnChannelCandidatesAllocationDone_s() {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
LOG(LS_INFO) << "Transport: " << content_name_ << " allocation complete";
|
||||
SignalCandidatesAllocationDone(this);
|
||||
}
|
||||
|
||||
void Transport::OnRoleConflict(TransportChannelImpl* channel) {
|
||||
signaling_thread_->Post(this, MSG_ROLECONFLICT);
|
||||
SignalRoleConflict();
|
||||
}
|
||||
|
||||
void Transport::OnChannelConnectionRemoved(TransportChannelImpl* channel) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
MaybeCompleted_w();
|
||||
LOG(LS_INFO) << name() << " TransportChannel " << channel->component()
|
||||
<< " connection removed. Check if transport is complete.";
|
||||
MaybeSignalCompleted();
|
||||
|
||||
// Check if the state is now Failed.
|
||||
// Failed is only available in the Controlling ICE role.
|
||||
@ -660,158 +514,97 @@ void Transport::OnChannelConnectionRemoved(TransportChannelImpl* channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelMap::iterator iter = channels_.find(channel->component());
|
||||
ASSERT(iter != channels_.end());
|
||||
// Failed can only occur after candidate allocation has stopped.
|
||||
if (!iter->second.candidates_allocated()) {
|
||||
// Failed can only occur after candidate gathering has stopped.
|
||||
if (channel->gathering_state() != kIceGatheringComplete) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (channel->GetState() == TransportChannelState::STATE_FAILED) {
|
||||
// A Transport has failed if any of its channels have no remaining
|
||||
// connections.
|
||||
signaling_thread_->Post(this, MSG_FAILED);
|
||||
SignalFailed(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::MaybeCompleted_w() {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
void Transport::MaybeSignalCompleted() {
|
||||
if (AllChannelsCompleted()) {
|
||||
LOG(LS_INFO) << name() << " transport is complete"
|
||||
<< " because all the channels are complete.";
|
||||
SignalCompleted(this);
|
||||
}
|
||||
// TODO(deadbeef): Should we do anything if we previously were completed,
|
||||
// but now are not (if, for example, a new remote candidate is added)?
|
||||
}
|
||||
|
||||
// When there is no channel created yet, calling this function could fire an
|
||||
// IceConnectionCompleted event prematurely.
|
||||
if (channels_.empty()) {
|
||||
return;
|
||||
void Transport::UpdateGatheringState() {
|
||||
IceGatheringState new_state = kIceGatheringNew;
|
||||
bool any_gathering = false;
|
||||
bool all_complete = !channels_.empty();
|
||||
for (const auto& kv : channels_) {
|
||||
any_gathering =
|
||||
any_gathering || kv.second->gathering_state() != kIceGatheringNew;
|
||||
all_complete =
|
||||
all_complete && kv.second->gathering_state() == kIceGatheringComplete;
|
||||
}
|
||||
if (all_complete) {
|
||||
new_state = kIceGatheringComplete;
|
||||
} else if (any_gathering) {
|
||||
new_state = kIceGatheringGathering;
|
||||
}
|
||||
|
||||
// A Transport's ICE process is completed if all of its channels are writable,
|
||||
// have finished allocating candidates, and have pruned all but one of their
|
||||
// connections.
|
||||
for (const auto& iter : channels_) {
|
||||
const TransportChannelImpl* channel = iter.second.get();
|
||||
if (!(channel->writable() &&
|
||||
channel->GetState() == TransportChannelState::STATE_COMPLETED &&
|
||||
channel->GetIceRole() == ICEROLE_CONTROLLING &&
|
||||
iter.second.candidates_allocated())) {
|
||||
return;
|
||||
if (gathering_state_ != new_state) {
|
||||
gathering_state_ = new_state;
|
||||
if (gathering_state_ == kIceGatheringGathering) {
|
||||
LOG(LS_INFO) << "Transport: " << name_ << ", gathering candidates";
|
||||
} else if (gathering_state_ == kIceGatheringComplete) {
|
||||
LOG(LS_INFO) << "Transport " << name() << " gathering complete.";
|
||||
}
|
||||
}
|
||||
|
||||
signaling_thread_->Post(this, MSG_COMPLETED);
|
||||
}
|
||||
|
||||
void Transport::SetIceRole_w(IceRole role) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
rtc::CritScope cs(&crit_);
|
||||
ice_role_ = role;
|
||||
for (auto& iter : channels_) {
|
||||
iter.second->SetIceRole(ice_role_);
|
||||
SignalGatheringState(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::SetRemoteIceMode_w(IceMode mode) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
remote_ice_mode_ = mode;
|
||||
// Shouldn't channels be created after this method executed?
|
||||
for (auto& iter : channels_) {
|
||||
iter.second->SetRemoteIceMode(remote_ice_mode_);
|
||||
void Transport::UpdateReceivingState() {
|
||||
TransportState receiving = GetTransportState(TRANSPORT_RECEIVING_STATE);
|
||||
if (receiving_ != receiving) {
|
||||
receiving_ = receiving;
|
||||
SignalReceivingState(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool Transport::SetLocalTransportDescription_w(
|
||||
const TransportDescription& desc,
|
||||
ContentAction action,
|
||||
std::string* error_desc) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
bool ret = true;
|
||||
|
||||
if (!VerifyIceParams(desc)) {
|
||||
return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
|
||||
error_desc);
|
||||
void Transport::UpdateWritableState() {
|
||||
TransportState writable = GetTransportState(TRANSPORT_WRITABLE_STATE);
|
||||
LOG(LS_INFO) << name() << " transport writable state changed? " << writable_
|
||||
<< " => " << writable;
|
||||
if (writable_ != writable) {
|
||||
was_writable_ = (writable_ == TRANSPORT_STATE_ALL);
|
||||
writable_ = writable;
|
||||
SignalWritableState(this);
|
||||
}
|
||||
|
||||
// TODO(tommi,pthatcher): I'm not sure why we need to grab this lock at this
|
||||
// point. |local_description_| seems to always be modified on the worker
|
||||
// thread, so we should be able to use it here without grabbing the lock.
|
||||
// However, we _might_ need it before the call to reset() below?
|
||||
// Raw access to |local_description_| is granted to derived transports outside
|
||||
// of locking (see local_description() in the header file).
|
||||
// The contract is that the derived implementations must be aware of when the
|
||||
// description might change and do appropriate synchronization.
|
||||
rtc::CritScope cs(&crit_);
|
||||
if (local_description_ && IceCredentialsChanged(*local_description_, desc)) {
|
||||
IceRole new_ice_role = (action == CA_OFFER) ? ICEROLE_CONTROLLING
|
||||
: ICEROLE_CONTROLLED;
|
||||
|
||||
// It must be called before ApplyLocalTransportDescription_w, which may
|
||||
// trigger an ICE restart and depends on the new ICE role.
|
||||
SetIceRole_w(new_ice_role);
|
||||
}
|
||||
|
||||
local_description_.reset(new TransportDescription(desc));
|
||||
|
||||
for (auto& iter : channels_) {
|
||||
ret &= ApplyLocalTransportDescription_w(iter.second.get(), error_desc);
|
||||
}
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
|
||||
if (action == CA_PRANSWER || action == CA_ANSWER) {
|
||||
ret &= NegotiateTransportDescription_w(action, error_desc);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Transport::SetRemoteTransportDescription_w(
|
||||
const TransportDescription& desc,
|
||||
ContentAction action,
|
||||
std::string* error_desc) {
|
||||
bool ret = true;
|
||||
|
||||
if (!VerifyIceParams(desc)) {
|
||||
return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
|
||||
error_desc);
|
||||
}
|
||||
|
||||
// TODO(tommi,pthatcher): See todo for local_description_ above.
|
||||
rtc::CritScope cs(&crit_);
|
||||
remote_description_.reset(new TransportDescription(desc));
|
||||
for (auto& iter : channels_) {
|
||||
ret &= ApplyRemoteTransportDescription_w(iter.second.get(), error_desc);
|
||||
}
|
||||
|
||||
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
|
||||
if (action == CA_PRANSWER || action == CA_ANSWER) {
|
||||
ret = NegotiateTransportDescription_w(CA_OFFER, error_desc);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Transport::ApplyLocalTransportDescription_w(TransportChannelImpl* ch,
|
||||
std::string* error_desc) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
bool Transport::ApplyLocalTransportDescription(TransportChannelImpl* ch,
|
||||
std::string* error_desc) {
|
||||
ch->SetIceCredentials(local_description_->ice_ufrag,
|
||||
local_description_->ice_pwd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Transport::ApplyRemoteTransportDescription_w(TransportChannelImpl* ch,
|
||||
std::string* error_desc) {
|
||||
bool Transport::ApplyRemoteTransportDescription(TransportChannelImpl* ch,
|
||||
std::string* error_desc) {
|
||||
ch->SetRemoteIceCredentials(remote_description_->ice_ufrag,
|
||||
remote_description_->ice_pwd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Transport::ApplyNegotiatedTransportDescription_w(
|
||||
TransportChannelImpl* channel, std::string* error_desc) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
bool Transport::ApplyNegotiatedTransportDescription(
|
||||
TransportChannelImpl* channel,
|
||||
std::string* error_desc) {
|
||||
channel->SetRemoteIceMode(remote_ice_mode_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Transport::NegotiateTransportDescription_w(ContentAction local_role,
|
||||
std::string* error_desc) {
|
||||
ASSERT(worker_thread()->IsCurrent());
|
||||
bool Transport::NegotiateTransportDescription(ContentAction local_role,
|
||||
std::string* error_desc) {
|
||||
// TODO(ekr@rtfm.com): This is ICE-specific stuff. Refactor into
|
||||
// P2PTransport.
|
||||
|
||||
@ -819,7 +612,7 @@ bool Transport::NegotiateTransportDescription_w(ContentAction local_role,
|
||||
// ice_lite, this local end point should take CONTROLLING role.
|
||||
if (ice_role_ == ICEROLE_CONTROLLED &&
|
||||
remote_description_->ice_mode == ICEMODE_LITE) {
|
||||
SetIceRole_w(ICEROLE_CONTROLLING);
|
||||
SetIceRole(ICEROLE_CONTROLLING);
|
||||
}
|
||||
|
||||
// Update remote ice_mode to all existing channels.
|
||||
@ -831,57 +624,10 @@ bool Transport::NegotiateTransportDescription_w(ContentAction local_role,
|
||||
// creation, we have the negotiation state saved until a new
|
||||
// negotiation happens.
|
||||
for (auto& iter : channels_) {
|
||||
if (!ApplyNegotiatedTransportDescription_w(iter.second.get(), error_desc))
|
||||
if (!ApplyNegotiatedTransportDescription(iter.second.get(), error_desc))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Transport::OnMessage(rtc::Message* msg) {
|
||||
switch (msg->message_id) {
|
||||
case MSG_ONSIGNALINGREADY:
|
||||
CallChannels_w(&TransportChannelImpl::OnSignalingReady);
|
||||
break;
|
||||
case MSG_ONREMOTECANDIDATE: {
|
||||
ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
|
||||
OnRemoteCandidate_w(*params->candidate);
|
||||
delete params;
|
||||
}
|
||||
break;
|
||||
case MSG_CONNECTING:
|
||||
OnConnecting_s();
|
||||
break;
|
||||
case MSG_WRITESTATE:
|
||||
OnChannelWritableState_s();
|
||||
break;
|
||||
case MSG_RECEIVINGSTATE:
|
||||
OnChannelReceivingState_s();
|
||||
break;
|
||||
case MSG_REQUESTSIGNALING:
|
||||
OnChannelRequestSignaling_s();
|
||||
break;
|
||||
case MSG_CANDIDATEREADY:
|
||||
OnChannelCandidateReady_s();
|
||||
break;
|
||||
case MSG_ROUTECHANGE: {
|
||||
ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
|
||||
OnChannelRouteChange_s(params->channel, *params->candidate);
|
||||
delete params;
|
||||
}
|
||||
break;
|
||||
case MSG_CANDIDATEALLOCATIONCOMPLETE:
|
||||
OnChannelCandidatesAllocationDone_s();
|
||||
break;
|
||||
case MSG_ROLECONFLICT:
|
||||
SignalRoleConflict();
|
||||
break;
|
||||
case MSG_COMPLETED:
|
||||
SignalCompleted(this);
|
||||
break;
|
||||
case MSG_FAILED:
|
||||
SignalFailed(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
@ -15,15 +15,11 @@
|
||||
// state changes (in order to update the manager's state), and forwards
|
||||
// requests to begin connecting or to reset to each of the channels.
|
||||
//
|
||||
// On Threading: Transport performs work on both the signaling and worker
|
||||
// threads. For subclasses, the rule is that all signaling related calls will
|
||||
// be made on the signaling thread and all channel related calls (including
|
||||
// signaling for a channel) will be made on the worker thread. When
|
||||
// information needs to be sent between the two threads, this class should do
|
||||
// the work (e.g., OnRemoteCandidate).
|
||||
// On Threading: Transport performs work solely on the worker thread, and so
|
||||
// its methods should only be called on the worker thread.
|
||||
//
|
||||
// Note: Subclasses must call DestroyChannels() in their own constructors.
|
||||
// It is not possible to do so here because the subclass constructor will
|
||||
// Note: Subclasses must call DestroyChannels() in their own destructors.
|
||||
// It is not possible to do so here because the subclass destructor will
|
||||
// already have run.
|
||||
|
||||
#ifndef WEBRTC_P2P_BASE_TRANSPORT_H_
|
||||
@ -36,16 +32,11 @@
|
||||
#include "webrtc/p2p/base/constants.h"
|
||||
#include "webrtc/p2p/base/sessiondescription.h"
|
||||
#include "webrtc/p2p/base/transportinfo.h"
|
||||
#include "webrtc/base/criticalsection.h"
|
||||
#include "webrtc/base/messagequeue.h"
|
||||
#include "webrtc/base/rtccertificate.h"
|
||||
#include "webrtc/base/sigslot.h"
|
||||
#include "webrtc/base/sslstreamadapter.h"
|
||||
|
||||
namespace rtc {
|
||||
class Thread;
|
||||
}
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class PortAllocator;
|
||||
@ -54,6 +45,26 @@ class TransportChannelImpl;
|
||||
|
||||
typedef std::vector<Candidate> Candidates;
|
||||
|
||||
// TODO(deadbeef): Unify with PeerConnectionInterface::IceConnectionState
|
||||
// once /talk/ and /webrtc/ are combined, and also switch to ENUM_NAME naming
|
||||
// style.
|
||||
enum IceConnectionState {
|
||||
kIceConnectionConnecting = 0,
|
||||
kIceConnectionFailed,
|
||||
kIceConnectionConnected, // Writable, but still checking one or more
|
||||
// connections
|
||||
kIceConnectionCompleted,
|
||||
};
|
||||
|
||||
// TODO(deadbeef): Unify with PeerConnectionInterface::IceConnectionState
|
||||
// once /talk/ and /webrtc/ are combined, and also switch to ENUM_NAME naming
|
||||
// style.
|
||||
enum IceGatheringState {
|
||||
kIceGatheringNew = 0,
|
||||
kIceGatheringGathering,
|
||||
kIceGatheringComplete,
|
||||
};
|
||||
|
||||
// For "writable" and "receiving", we need to differentiate between
|
||||
// none, all, and some.
|
||||
enum TransportState {
|
||||
@ -124,7 +135,7 @@ typedef std::vector<TransportChannelStats> TransportChannelStatsList;
|
||||
|
||||
// Information about the stats of a transport.
|
||||
struct TransportStats {
|
||||
std::string content_name;
|
||||
std::string transport_name;
|
||||
TransportChannelStatsList channel_stats;
|
||||
};
|
||||
|
||||
@ -135,22 +146,13 @@ bool IceCredentialsChanged(const std::string& old_ufrag,
|
||||
const std::string& new_ufrag,
|
||||
const std::string& new_pwd);
|
||||
|
||||
class Transport : public rtc::MessageHandler,
|
||||
public sigslot::has_slots<> {
|
||||
class Transport : public sigslot::has_slots<> {
|
||||
public:
|
||||
Transport(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
const std::string& content_name,
|
||||
PortAllocator* allocator);
|
||||
Transport(const std::string& name, PortAllocator* allocator);
|
||||
virtual ~Transport();
|
||||
|
||||
// Returns the signaling thread. The app talks to Transport on this thread.
|
||||
rtc::Thread* signaling_thread() const { return signaling_thread_; }
|
||||
// Returns the worker thread. The actual networking is done on this thread.
|
||||
rtc::Thread* worker_thread() const { return worker_thread_; }
|
||||
|
||||
// Returns the content_name of this transport.
|
||||
const std::string& content_name() const { return content_name_; }
|
||||
// Returns the name of this transport.
|
||||
const std::string& name() const { return name_; }
|
||||
|
||||
// Returns the port allocator object for this transport.
|
||||
PortAllocator* port_allocator() { return allocator_; }
|
||||
@ -172,6 +174,14 @@ class Transport : public rtc::MessageHandler,
|
||||
return (receiving_ == TRANSPORT_STATE_SOME ||
|
||||
receiving_ == TRANSPORT_STATE_ALL);
|
||||
}
|
||||
bool ready_for_remote_candidates() const {
|
||||
return local_description_set_ && remote_description_set_;
|
||||
}
|
||||
|
||||
bool AllChannelsCompleted() const;
|
||||
bool AnyChannelFailed() const;
|
||||
|
||||
IceGatheringState gathering_state() const { return gathering_state_; }
|
||||
|
||||
sigslot::signal1<Transport*> SignalWritableState;
|
||||
sigslot::signal1<Transport*> SignalReceivingState;
|
||||
@ -190,21 +200,23 @@ class Transport : public rtc::MessageHandler,
|
||||
void SetChannelReceivingTimeout(int timeout_ms);
|
||||
|
||||
// Must be called before applying local session description.
|
||||
void SetCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
|
||||
virtual void SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {}
|
||||
|
||||
// Get a copy of the local identity provided by SetIdentity.
|
||||
bool GetCertificate(rtc::scoped_refptr<rtc::RTCCertificate>* certificate);
|
||||
// Get a copy of the local certificate provided by SetLocalCertificate.
|
||||
virtual bool GetLocalCertificate(
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get a copy of the remote certificate in use by the specified channel.
|
||||
bool GetRemoteSSLCertificate(rtc::SSLCertificate** cert);
|
||||
|
||||
// Create, destroy, and lookup the channels of this type by their components.
|
||||
TransportChannelImpl* CreateChannel(int component);
|
||||
// Note: GetChannel may lead to race conditions, since the mutex is not held
|
||||
// after the pointer is returned.
|
||||
|
||||
TransportChannelImpl* GetChannel(int component);
|
||||
// Note: HasChannel does not lead to race conditions, unlike GetChannel.
|
||||
|
||||
bool HasChannel(int component) {
|
||||
return (NULL != GetChannel(component));
|
||||
}
|
||||
@ -212,7 +224,6 @@ class Transport : public rtc::MessageHandler,
|
||||
void DestroyChannel(int component);
|
||||
|
||||
// Set the local TransportDescription to be used by TransportChannels.
|
||||
// This should be called before ConnectChannels().
|
||||
bool SetLocalTransportDescription(const TransportDescription& description,
|
||||
ContentAction action,
|
||||
std::string* error_desc);
|
||||
@ -227,6 +238,11 @@ class Transport : public rtc::MessageHandler,
|
||||
void ConnectChannels();
|
||||
sigslot::signal1<Transport*> SignalConnecting;
|
||||
|
||||
// Tells channels to start gathering candidates if necessary.
|
||||
// Should be called after ConnectChannels() has been called at least once,
|
||||
// which will happen in SetLocalTransportDescription.
|
||||
void MaybeStartGathering();
|
||||
|
||||
// Resets all of the channels back to their initial state. They are no
|
||||
// longer connecting.
|
||||
void ResetChannels();
|
||||
@ -236,20 +252,15 @@ class Transport : public rtc::MessageHandler,
|
||||
|
||||
bool GetStats(TransportStats* stats);
|
||||
|
||||
// Before any stanza is sent, the manager will request signaling. Once
|
||||
// signaling is available, the client should call OnSignalingReady. Once
|
||||
// this occurs, the transport (or its channels) can send any waiting stanzas.
|
||||
// OnSignalingReady invokes OnTransportSignalingReady and then forwards this
|
||||
// signal to each channel.
|
||||
sigslot::signal1<Transport*> SignalRequestSignaling;
|
||||
void OnSignalingReady();
|
||||
sigslot::signal1<Transport*> SignalGatheringState;
|
||||
|
||||
// Handles sending of ready candidates and receiving of remote candidates.
|
||||
sigslot::signal2<Transport*,
|
||||
const std::vector<Candidate>&> SignalCandidatesReady;
|
||||
sigslot::signal2<Transport*, const std::vector<Candidate>&>
|
||||
SignalCandidatesGathered;
|
||||
|
||||
sigslot::signal1<Transport*> SignalCandidatesAllocationDone;
|
||||
void OnRemoteCandidates(const std::vector<Candidate>& candidates);
|
||||
// Called when one or more candidates are ready from the remote peer.
|
||||
bool AddRemoteCandidates(const std::vector<Candidate>& candidates,
|
||||
std::string* error);
|
||||
|
||||
// If candidate is not acceptable, returns false and sets error.
|
||||
// Call this before calling OnRemoteCandidates.
|
||||
@ -264,10 +275,12 @@ class Transport : public rtc::MessageHandler,
|
||||
// Forwards the signal from TransportChannel to BaseSession.
|
||||
sigslot::signal0<> SignalRoleConflict;
|
||||
|
||||
virtual bool GetSslRole(rtc::SSLRole* ssl_role) const;
|
||||
virtual bool GetSslRole(rtc::SSLRole* ssl_role) const { return false; }
|
||||
|
||||
// Must be called before channel is starting to connect.
|
||||
virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version);
|
||||
virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
// These are called by Create/DestroyChannel above in order to create or
|
||||
@ -275,9 +288,6 @@ class Transport : public rtc::MessageHandler,
|
||||
virtual TransportChannelImpl* CreateTransportChannel(int component) = 0;
|
||||
virtual void DestroyTransportChannel(TransportChannelImpl* channel) = 0;
|
||||
|
||||
// Informs the subclass that we received the signaling ready message.
|
||||
virtual void OnTransportSignalingReady() {}
|
||||
|
||||
// The current local transport description, for use by derived classes
|
||||
// when performing transport description negotiation.
|
||||
const TransportDescription* local_description() const {
|
||||
@ -290,53 +300,37 @@ class Transport : public rtc::MessageHandler,
|
||||
return remote_description_.get();
|
||||
}
|
||||
|
||||
virtual void SetCertificate_w(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {}
|
||||
|
||||
virtual bool GetCertificate_w(
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pushes down the transport parameters from the local description, such
|
||||
// as the ICE ufrag and pwd.
|
||||
// Derived classes can override, but must call the base as well.
|
||||
virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel,
|
||||
std::string* error_desc);
|
||||
virtual bool ApplyLocalTransportDescription(TransportChannelImpl* channel,
|
||||
std::string* error_desc);
|
||||
|
||||
// Pushes down remote ice credentials from the remote description to the
|
||||
// transport channel.
|
||||
virtual bool ApplyRemoteTransportDescription_w(TransportChannelImpl* ch,
|
||||
std::string* error_desc);
|
||||
virtual bool ApplyRemoteTransportDescription(TransportChannelImpl* ch,
|
||||
std::string* error_desc);
|
||||
|
||||
// Negotiates the transport parameters based on the current local and remote
|
||||
// transport description, such as the ICE role to use, and whether DTLS
|
||||
// should be activated.
|
||||
// Derived classes can negotiate their specific parameters here, but must call
|
||||
// the base as well.
|
||||
virtual bool NegotiateTransportDescription_w(ContentAction local_role,
|
||||
std::string* error_desc);
|
||||
virtual bool NegotiateTransportDescription(ContentAction local_role,
|
||||
std::string* error_desc);
|
||||
|
||||
// Pushes down the transport parameters obtained via negotiation.
|
||||
// Derived classes can set their specific parameters here, but must call the
|
||||
// base as well.
|
||||
virtual bool ApplyNegotiatedTransportDescription_w(
|
||||
TransportChannelImpl* channel, std::string* error_desc);
|
||||
|
||||
virtual bool GetSslRole_w(rtc::SSLRole* ssl_role) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool SetSslMaxProtocolVersion_w(rtc::SSLProtocolVersion version) {
|
||||
return false;
|
||||
}
|
||||
virtual bool ApplyNegotiatedTransportDescription(
|
||||
TransportChannelImpl* channel,
|
||||
std::string* error_desc);
|
||||
|
||||
private:
|
||||
struct ChannelMapEntry {
|
||||
ChannelMapEntry() : impl_(NULL), candidates_allocated_(false), ref_(0) {}
|
||||
ChannelMapEntry() : impl_(NULL), ref_(0) {}
|
||||
explicit ChannelMapEntry(TransportChannelImpl *impl)
|
||||
: impl_(impl),
|
||||
candidates_allocated_(false),
|
||||
ref_(0) {
|
||||
}
|
||||
|
||||
@ -349,14 +343,9 @@ class Transport : public rtc::MessageHandler,
|
||||
|
||||
TransportChannelImpl* get() const { return impl_; }
|
||||
TransportChannelImpl* operator->() const { return impl_; }
|
||||
void set_candidates_allocated(bool status) {
|
||||
candidates_allocated_ = status;
|
||||
}
|
||||
bool candidates_allocated() const { return candidates_allocated_; }
|
||||
|
||||
private:
|
||||
TransportChannelImpl *impl_;
|
||||
bool candidates_allocated_;
|
||||
private:
|
||||
TransportChannelImpl* impl_;
|
||||
int ref_;
|
||||
};
|
||||
|
||||
@ -369,92 +358,55 @@ class Transport : public rtc::MessageHandler,
|
||||
// Called when the receiving state of a channel changes.
|
||||
void OnChannelReceivingState(TransportChannel* channel);
|
||||
|
||||
// Called when a channel requests signaling.
|
||||
void OnChannelRequestSignaling(TransportChannelImpl* channel);
|
||||
// Called when a channel starts finishes gathering candidates
|
||||
void OnChannelGatheringState(TransportChannelImpl* channel);
|
||||
|
||||
// Called when a candidate is ready from remote peer.
|
||||
void OnRemoteCandidate(const Candidate& candidate);
|
||||
// Called when a candidate is ready from channel.
|
||||
void OnChannelCandidateReady(TransportChannelImpl* channel,
|
||||
const Candidate& candidate);
|
||||
void OnChannelCandidateGathered(TransportChannelImpl* channel,
|
||||
const Candidate& candidate);
|
||||
void OnChannelRouteChange(TransportChannel* channel,
|
||||
const Candidate& remote_candidate);
|
||||
void OnChannelCandidatesAllocationDone(TransportChannelImpl* channel);
|
||||
// Called when there is ICE role change.
|
||||
void OnRoleConflict(TransportChannelImpl* channel);
|
||||
// Called when the channel removes a connection.
|
||||
void OnChannelConnectionRemoved(TransportChannelImpl* channel);
|
||||
|
||||
// Dispatches messages to the appropriate handler (below).
|
||||
void OnMessage(rtc::Message* msg);
|
||||
|
||||
// These are versions of the above methods that are called only on a
|
||||
// particular thread (s = signaling, w = worker). The above methods post or
|
||||
// send a message to invoke this version.
|
||||
TransportChannelImpl* CreateChannel_w(int component);
|
||||
void DestroyChannel_w(int component);
|
||||
void ConnectChannels_w();
|
||||
void ResetChannels_w();
|
||||
void DestroyAllChannels_w();
|
||||
void OnRemoteCandidate_w(const Candidate& candidate);
|
||||
void OnChannelWritableState_s();
|
||||
void OnChannelReceivingState_s();
|
||||
void OnChannelRequestSignaling_s();
|
||||
void OnConnecting_s();
|
||||
void OnChannelRouteChange_s(const TransportChannel* channel,
|
||||
const Candidate& remote_candidate);
|
||||
void OnChannelCandidatesAllocationDone_s();
|
||||
|
||||
// Helper function that invokes the given function on every channel.
|
||||
typedef void (TransportChannelImpl::* TransportChannelFunc)();
|
||||
void CallChannels_w(TransportChannelFunc func);
|
||||
void CallChannels(TransportChannelFunc func);
|
||||
|
||||
// Computes the AND and OR of the channel's read/write/receiving state
|
||||
// (argument picks the operation).
|
||||
TransportState GetTransportState_s(TransportStateType type);
|
||||
|
||||
void OnChannelCandidateReady_s();
|
||||
|
||||
void SetIceRole_w(IceRole role);
|
||||
void SetRemoteIceMode_w(IceMode mode);
|
||||
bool SetLocalTransportDescription_w(const TransportDescription& desc,
|
||||
ContentAction action,
|
||||
std::string* error_desc);
|
||||
bool SetRemoteTransportDescription_w(const TransportDescription& desc,
|
||||
ContentAction action,
|
||||
std::string* error_desc);
|
||||
bool GetStats_w(TransportStats* infos);
|
||||
bool GetRemoteSSLCertificate_w(rtc::SSLCertificate** cert);
|
||||
|
||||
void SetChannelReceivingTimeout_w(int timeout_ms);
|
||||
TransportState GetTransportState(TransportStateType type);
|
||||
|
||||
// Sends SignalCompleted if we are now in that state.
|
||||
void MaybeCompleted_w();
|
||||
void MaybeSignalCompleted();
|
||||
|
||||
rtc::Thread* const signaling_thread_;
|
||||
rtc::Thread* const worker_thread_;
|
||||
const std::string content_name_;
|
||||
// Sends SignalGatheringState if gathering state changed
|
||||
void UpdateGatheringState();
|
||||
|
||||
void UpdateWritableState();
|
||||
void UpdateReceivingState();
|
||||
|
||||
const std::string name_;
|
||||
PortAllocator* const allocator_;
|
||||
bool destroyed_;
|
||||
TransportState readable_;
|
||||
TransportState writable_;
|
||||
TransportState receiving_;
|
||||
bool was_writable_;
|
||||
bool connect_requested_;
|
||||
IceRole ice_role_;
|
||||
uint64 tiebreaker_;
|
||||
IceMode remote_ice_mode_;
|
||||
int channel_receiving_timeout_;
|
||||
bool channels_destroyed_ = false;
|
||||
TransportState readable_ = TRANSPORT_STATE_NONE;
|
||||
TransportState writable_ = TRANSPORT_STATE_NONE;
|
||||
TransportState receiving_ = TRANSPORT_STATE_NONE;
|
||||
bool was_writable_ = false;
|
||||
bool connect_requested_ = false;
|
||||
IceRole ice_role_ = ICEROLE_UNKNOWN;
|
||||
uint64 tiebreaker_ = 0;
|
||||
IceMode remote_ice_mode_ = ICEMODE_FULL;
|
||||
int channel_receiving_timeout_ = -1;
|
||||
rtc::scoped_ptr<TransportDescription> local_description_;
|
||||
rtc::scoped_ptr<TransportDescription> remote_description_;
|
||||
bool local_description_set_ = false;
|
||||
bool remote_description_set_ = false;
|
||||
IceGatheringState gathering_state_ = kIceGatheringNew;
|
||||
|
||||
// TODO(tommi): Make sure we only use this on the worker thread.
|
||||
ChannelMap channels_;
|
||||
// Buffers the ready_candidates so that SignalCanidatesReady can
|
||||
// provide them in multiples.
|
||||
std::vector<Candidate> ready_candidates_;
|
||||
// Protects changes to channels and messages
|
||||
rtc::CriticalSection crit_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(Transport);
|
||||
};
|
||||
|
||||
@ -11,8 +11,7 @@
|
||||
#include "webrtc/base/fakesslidentity.h"
|
||||
#include "webrtc/base/gunit.h"
|
||||
#include "webrtc/base/network.h"
|
||||
#include "webrtc/base/thread.h"
|
||||
#include "webrtc/p2p/base/fakesession.h"
|
||||
#include "webrtc/p2p/base/faketransportcontroller.h"
|
||||
#include "webrtc/p2p/base/p2ptransport.h"
|
||||
|
||||
using cricket::Candidate;
|
||||
@ -35,9 +34,7 @@ class TransportTest : public testing::Test,
|
||||
public sigslot::has_slots<> {
|
||||
public:
|
||||
TransportTest()
|
||||
: thread_(rtc::Thread::Current()),
|
||||
transport_(new FakeTransport(
|
||||
thread_, thread_, "test content name", NULL)),
|
||||
: transport_(new FakeTransport("test content name")),
|
||||
channel_(NULL),
|
||||
connecting_signalled_(false),
|
||||
completed_(false),
|
||||
@ -73,7 +70,6 @@ class TransportTest : public testing::Test,
|
||||
failed_ = true;
|
||||
}
|
||||
|
||||
rtc::Thread* thread_;
|
||||
rtc::scoped_ptr<FakeTransport> transport_;
|
||||
FakeTransportChannel* channel_;
|
||||
bool connecting_signalled_;
|
||||
@ -85,20 +81,7 @@ class TransportTest : public testing::Test,
|
||||
TEST_F(TransportTest, TestConnectChannelsDoesSignal) {
|
||||
EXPECT_TRUE(SetupChannel());
|
||||
transport_->ConnectChannels();
|
||||
EXPECT_FALSE(connecting_signalled_);
|
||||
|
||||
EXPECT_TRUE_WAIT(connecting_signalled_, 100);
|
||||
}
|
||||
|
||||
// Test that DestroyAllChannels kills any pending OnConnecting signals.
|
||||
TEST_F(TransportTest, TestDestroyAllClearsPosts) {
|
||||
EXPECT_TRUE(transport_->CreateChannel(1) != NULL);
|
||||
|
||||
transport_->ConnectChannels();
|
||||
transport_->DestroyAllChannels();
|
||||
|
||||
thread_->ProcessMessages(0);
|
||||
EXPECT_FALSE(connecting_signalled_);
|
||||
EXPECT_TRUE(connecting_signalled_);
|
||||
}
|
||||
|
||||
// This test verifies channels are created with proper ICE
|
||||
@ -232,7 +215,7 @@ TEST_F(TransportTest, TestChannelCompletedAndFailed) {
|
||||
NULL));
|
||||
|
||||
channel_->SetConnectionCount(2);
|
||||
channel_->SignalCandidatesAllocationDone(channel_);
|
||||
channel_->SetCandidatesGatheringComplete();
|
||||
channel_->SetWritable(true);
|
||||
EXPECT_TRUE_WAIT(transport_->all_channels_writable(), 100);
|
||||
// ICE is not yet completed because there is still more than one connection.
|
||||
|
||||
@ -18,7 +18,7 @@ std::string TransportChannel::ToString() const {
|
||||
const char RECEIVING_ABBREV[2] = { '_', 'R' };
|
||||
const char WRITABLE_ABBREV[2] = { '_', 'W' };
|
||||
std::stringstream ss;
|
||||
ss << "Channel[" << content_name_ << "|" << component_ << "|"
|
||||
ss << "Channel[" << transport_name_ << "|" << component_ << "|"
|
||||
<< RECEIVING_ABBREV[receiving_] << WRITABLE_ABBREV[writable_] << "]";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@ -37,14 +37,19 @@ enum PacketFlags {
|
||||
};
|
||||
|
||||
// Used to indicate channel's connection state.
|
||||
enum TransportChannelState { STATE_CONNECTING, STATE_COMPLETED, STATE_FAILED };
|
||||
enum TransportChannelState {
|
||||
STATE_INIT,
|
||||
STATE_CONNECTING, // Will enter this state once a connection is created
|
||||
STATE_COMPLETED,
|
||||
STATE_FAILED
|
||||
};
|
||||
|
||||
// A TransportChannel represents one logical stream of packets that are sent
|
||||
// between the two sides of a session.
|
||||
class TransportChannel : public sigslot::has_slots<> {
|
||||
public:
|
||||
explicit TransportChannel(const std::string& content_name, int component)
|
||||
: content_name_(content_name),
|
||||
explicit TransportChannel(const std::string& transport_name, int component)
|
||||
: transport_name_(transport_name),
|
||||
component_(component),
|
||||
writable_(false),
|
||||
receiving_(false) {}
|
||||
@ -60,7 +65,7 @@ class TransportChannel : public sigslot::has_slots<> {
|
||||
// Returns the session id of this channel.
|
||||
virtual const std::string SessionId() const { return std::string(); }
|
||||
|
||||
const std::string& content_name() const { return content_name_; }
|
||||
const std::string& transport_name() const { return transport_name_; }
|
||||
int component() const { return component_; }
|
||||
|
||||
// Returns the states of this channel. Each time one of these states changes,
|
||||
@ -146,10 +151,9 @@ class TransportChannel : public sigslot::has_slots<> {
|
||||
// Sets the receiving state, signaling if necessary.
|
||||
void set_receiving(bool receiving);
|
||||
|
||||
|
||||
private:
|
||||
// Used mostly for debugging.
|
||||
std::string content_name_;
|
||||
std::string transport_name_;
|
||||
int component_;
|
||||
bool writable_;
|
||||
bool receiving_;
|
||||
|
||||
@ -32,8 +32,9 @@ enum IceProtocolType {
|
||||
// client.
|
||||
class TransportChannelImpl : public TransportChannel {
|
||||
public:
|
||||
explicit TransportChannelImpl(const std::string& content_name, int component)
|
||||
: TransportChannel(content_name, component) {}
|
||||
explicit TransportChannelImpl(const std::string& transport_name,
|
||||
int component)
|
||||
: TransportChannel(transport_name, component) {}
|
||||
|
||||
// Returns the transport that created this channel.
|
||||
virtual Transport* GetTransport() = 0;
|
||||
@ -63,11 +64,11 @@ class TransportChannelImpl : public TransportChannel {
|
||||
// Begins the process of attempting to make a connection to the other client.
|
||||
virtual void Connect() = 0;
|
||||
|
||||
// Allows an individual channel to request signaling and be notified when it
|
||||
// is ready. This is useful if the individual named channels have need to
|
||||
// send their own transport-info stanzas.
|
||||
sigslot::signal1<TransportChannelImpl*> SignalRequestSignaling;
|
||||
virtual void OnSignalingReady() = 0;
|
||||
// Start gathering candidates if not already started, or if an ICE restart
|
||||
// occurred.
|
||||
virtual void MaybeStartGathering() = 0;
|
||||
|
||||
sigslot::signal1<TransportChannelImpl*> SignalGatheringState;
|
||||
|
||||
// Handles sending and receiving of candidates. The Transport
|
||||
// receives the candidates and may forward them to the relevant
|
||||
@ -77,9 +78,11 @@ class TransportChannelImpl : public TransportChannel {
|
||||
// channel, they cannot return an error if the message is invalid.
|
||||
// It is assumed that the Transport will have checked validity
|
||||
// before forwarding.
|
||||
sigslot::signal2<TransportChannelImpl*,
|
||||
const Candidate&> SignalCandidateReady;
|
||||
virtual void OnCandidate(const Candidate& candidate) = 0;
|
||||
sigslot::signal2<TransportChannelImpl*, const Candidate&>
|
||||
SignalCandidateGathered;
|
||||
virtual void AddRemoteCandidate(const Candidate& candidate) = 0;
|
||||
|
||||
virtual IceGatheringState gathering_state() const = 0;
|
||||
|
||||
// DTLS methods
|
||||
virtual bool SetLocalCertificate(
|
||||
@ -92,9 +95,6 @@ class TransportChannelImpl : public TransportChannel {
|
||||
|
||||
virtual bool SetSslRole(rtc::SSLRole role) = 0;
|
||||
|
||||
// TransportChannel is forwarding this signal from PortAllocatorSession.
|
||||
sigslot::signal1<TransportChannelImpl*> SignalCandidatesAllocationDone;
|
||||
|
||||
// Invoked when there is conflict in the ICE role between local and remote
|
||||
// agents.
|
||||
sigslot::signal1<TransportChannelImpl*> SignalRoleConflict;
|
||||
|
||||
@ -1,276 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/p2p/base/transport.h"
|
||||
#include "webrtc/p2p/base/transportchannelimpl.h"
|
||||
#include "webrtc/p2p/base/transportchannelproxy.h"
|
||||
#include "webrtc/base/common.h"
|
||||
#include "webrtc/base/logging.h"
|
||||
#include "webrtc/base/thread.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
enum {
|
||||
MSG_UPDATESTATE,
|
||||
};
|
||||
|
||||
TransportChannelProxy::TransportChannelProxy(const std::string& content_name,
|
||||
int component)
|
||||
: TransportChannel(content_name, component),
|
||||
impl_(NULL) {
|
||||
worker_thread_ = rtc::Thread::Current();
|
||||
}
|
||||
|
||||
TransportChannelProxy::~TransportChannelProxy() {
|
||||
// Clearing any pending signal.
|
||||
worker_thread_->Clear(this);
|
||||
if (impl_) {
|
||||
impl_->GetTransport()->DestroyChannel(impl_->component());
|
||||
}
|
||||
}
|
||||
|
||||
void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
|
||||
if (impl == impl_) {
|
||||
// Ignore if the |impl| has already been set.
|
||||
LOG(LS_WARNING) << "Ignored TransportChannelProxy::SetImplementation call "
|
||||
<< "with a same impl as the existing one.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroy any existing impl_.
|
||||
if (impl_) {
|
||||
impl_->GetTransport()->DestroyChannel(impl_->component());
|
||||
}
|
||||
|
||||
// Adopt the supplied impl, and connect to its signals.
|
||||
impl_ = impl;
|
||||
|
||||
if (impl_) {
|
||||
impl_->SignalWritableState.connect(
|
||||
this, &TransportChannelProxy::OnWritableState);
|
||||
impl_->SignalReceivingState.connect(
|
||||
this, &TransportChannelProxy::OnReceivingState);
|
||||
impl_->SignalReadPacket.connect(
|
||||
this, &TransportChannelProxy::OnReadPacket);
|
||||
impl_->SignalReadyToSend.connect(
|
||||
this, &TransportChannelProxy::OnReadyToSend);
|
||||
impl_->SignalRouteChange.connect(
|
||||
this, &TransportChannelProxy::OnRouteChange);
|
||||
for (const auto& pair : options_) {
|
||||
impl_->SetOption(pair.first, pair.second);
|
||||
}
|
||||
|
||||
// Push down the SRTP ciphers, if any were set.
|
||||
if (!pending_srtp_ciphers_.empty()) {
|
||||
impl_->SetSrtpCiphers(pending_srtp_ciphers_);
|
||||
}
|
||||
}
|
||||
|
||||
// Post ourselves a message to see if we need to fire state callbacks.
|
||||
worker_thread_->Post(this, MSG_UPDATESTATE);
|
||||
}
|
||||
|
||||
int TransportChannelProxy::SendPacket(const char* data, size_t len,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
// Fail if we don't have an impl yet.
|
||||
if (!impl_) {
|
||||
return -1;
|
||||
}
|
||||
return impl_->SendPacket(data, len, options, flags);
|
||||
}
|
||||
|
||||
int TransportChannelProxy::SetOption(rtc::Socket::Option opt, int value) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
options_.push_back(OptionPair(opt, value));
|
||||
if (!impl_) {
|
||||
return 0;
|
||||
}
|
||||
return impl_->SetOption(opt, value);
|
||||
}
|
||||
|
||||
bool TransportChannelProxy::GetOption(rtc::Socket::Option opt, int* value) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (impl_) {
|
||||
return impl_->GetOption(opt, value);
|
||||
}
|
||||
|
||||
for (const auto& pair : options_) {
|
||||
if (pair.first == opt) {
|
||||
*value = pair.second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int TransportChannelProxy::GetError() {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return 0;
|
||||
}
|
||||
return impl_->GetError();
|
||||
}
|
||||
|
||||
TransportChannelState TransportChannelProxy::GetState() const {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return TransportChannelState::STATE_CONNECTING;
|
||||
}
|
||||
return impl_->GetState();
|
||||
}
|
||||
|
||||
bool TransportChannelProxy::GetStats(ConnectionInfos* infos) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return false;
|
||||
}
|
||||
return impl_->GetStats(infos);
|
||||
}
|
||||
|
||||
bool TransportChannelProxy::IsDtlsActive() const {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return false;
|
||||
}
|
||||
return impl_->IsDtlsActive();
|
||||
}
|
||||
|
||||
bool TransportChannelProxy::GetSslRole(rtc::SSLRole* role) const {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return false;
|
||||
}
|
||||
return impl_->GetSslRole(role);
|
||||
}
|
||||
|
||||
bool TransportChannelProxy::SetSslRole(rtc::SSLRole role) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return false;
|
||||
}
|
||||
return impl_->SetSslRole(role);
|
||||
}
|
||||
|
||||
bool TransportChannelProxy::SetSrtpCiphers(const std::vector<std::string>&
|
||||
ciphers) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
pending_srtp_ciphers_ = ciphers; // Cache so we can send later, but always
|
||||
// set so it stays consistent.
|
||||
if (impl_) {
|
||||
return impl_->SetSrtpCiphers(ciphers);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransportChannelProxy::GetSrtpCipher(std::string* cipher) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return false;
|
||||
}
|
||||
return impl_->GetSrtpCipher(cipher);
|
||||
}
|
||||
|
||||
bool TransportChannelProxy::GetSslCipher(std::string* cipher) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return false;
|
||||
}
|
||||
return impl_->GetSslCipher(cipher);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>
|
||||
TransportChannelProxy::GetLocalCertificate() const {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return nullptr;
|
||||
}
|
||||
return impl_->GetLocalCertificate();
|
||||
}
|
||||
|
||||
bool TransportChannelProxy::GetRemoteSSLCertificate(
|
||||
rtc::SSLCertificate** cert) const {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return false;
|
||||
}
|
||||
return impl_->GetRemoteSSLCertificate(cert);
|
||||
}
|
||||
|
||||
bool TransportChannelProxy::ExportKeyingMaterial(const std::string& label,
|
||||
const uint8* context,
|
||||
size_t context_len,
|
||||
bool use_context,
|
||||
uint8* result,
|
||||
size_t result_len) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return false;
|
||||
}
|
||||
return impl_->ExportKeyingMaterial(label, context, context_len, use_context,
|
||||
result, result_len);
|
||||
}
|
||||
|
||||
IceRole TransportChannelProxy::GetIceRole() const {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (!impl_) {
|
||||
return ICEROLE_UNKNOWN;
|
||||
}
|
||||
return impl_->GetIceRole();
|
||||
}
|
||||
|
||||
void TransportChannelProxy::OnWritableState(TransportChannel* channel) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
ASSERT(channel == impl_);
|
||||
set_writable(impl_->writable());
|
||||
// Note: SignalWritableState fired by set_writable.
|
||||
}
|
||||
|
||||
void TransportChannelProxy::OnReceivingState(TransportChannel* channel) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
ASSERT(channel == impl_);
|
||||
set_receiving(impl_->receiving());
|
||||
// Note: SignalReceivingState fired by set_receiving.
|
||||
}
|
||||
|
||||
void TransportChannelProxy::OnReadPacket(
|
||||
TransportChannel* channel, const char* data, size_t size,
|
||||
const rtc::PacketTime& packet_time, int flags) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
ASSERT(channel == impl_);
|
||||
SignalReadPacket(this, data, size, packet_time, flags);
|
||||
}
|
||||
|
||||
void TransportChannelProxy::OnReadyToSend(TransportChannel* channel) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
ASSERT(channel == impl_);
|
||||
SignalReadyToSend(this);
|
||||
}
|
||||
|
||||
void TransportChannelProxy::OnRouteChange(TransportChannel* channel,
|
||||
const Candidate& candidate) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
ASSERT(channel == impl_);
|
||||
SignalRouteChange(this, candidate);
|
||||
}
|
||||
|
||||
void TransportChannelProxy::OnMessage(rtc::Message* msg) {
|
||||
ASSERT(rtc::Thread::Current() == worker_thread_);
|
||||
if (msg->message_id == MSG_UPDATESTATE) {
|
||||
// If impl_ is already receiving or writable, push up those signals.
|
||||
set_writable(impl_ ? impl_->writable() : false);
|
||||
set_receiving(impl_ ? impl_->receiving() : false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_P2P_BASE_TRANSPORTCHANNELPROXY_H_
|
||||
#define WEBRTC_P2P_BASE_TRANSPORTCHANNELPROXY_H_
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/p2p/base/transportchannel.h"
|
||||
#include "webrtc/base/messagehandler.h"
|
||||
|
||||
namespace rtc {
|
||||
class Thread;
|
||||
}
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class TransportChannelImpl;
|
||||
|
||||
// Proxies calls between the client and the transport channel implementation.
|
||||
// This is needed because clients are allowed to create channels before the
|
||||
// network negotiation is complete. Hence, we create a proxy up front, and
|
||||
// when negotiation completes, connect the proxy to the implementaiton.
|
||||
class TransportChannelProxy : public TransportChannel,
|
||||
public rtc::MessageHandler {
|
||||
public:
|
||||
TransportChannelProxy(const std::string& content_name,
|
||||
int component);
|
||||
~TransportChannelProxy() override;
|
||||
|
||||
TransportChannelImpl* impl() { return impl_; }
|
||||
|
||||
TransportChannelState GetState() const override;
|
||||
|
||||
// Sets the implementation to which we will proxy.
|
||||
void SetImplementation(TransportChannelImpl* impl);
|
||||
|
||||
// Implementation of the TransportChannel interface. These simply forward to
|
||||
// the implementation.
|
||||
int SendPacket(const char* data, size_t len,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) override;
|
||||
int SetOption(rtc::Socket::Option opt, int value) override;
|
||||
bool GetOption(rtc::Socket::Option opt, int* value) override;
|
||||
int GetError() override;
|
||||
virtual IceRole GetIceRole() const;
|
||||
bool GetStats(ConnectionInfos* infos) override;
|
||||
bool IsDtlsActive() const override;
|
||||
bool GetSslRole(rtc::SSLRole* role) const override;
|
||||
virtual bool SetSslRole(rtc::SSLRole role);
|
||||
bool SetSrtpCiphers(const std::vector<std::string>& ciphers) override;
|
||||
bool GetSrtpCipher(std::string* cipher) override;
|
||||
bool GetSslCipher(std::string* cipher) override;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override;
|
||||
bool GetRemoteSSLCertificate(rtc::SSLCertificate** cert) const override;
|
||||
bool ExportKeyingMaterial(const std::string& label,
|
||||
const uint8* context,
|
||||
size_t context_len,
|
||||
bool use_context,
|
||||
uint8* result,
|
||||
size_t result_len) override;
|
||||
|
||||
private:
|
||||
// Catch signals from the implementation channel. These just forward to the
|
||||
// client (after updating our state to match).
|
||||
void OnReceivingState(TransportChannel* channel);
|
||||
void OnWritableState(TransportChannel* channel);
|
||||
void OnReadPacket(TransportChannel* channel, const char* data, size_t size,
|
||||
const rtc::PacketTime& packet_time, int flags);
|
||||
void OnReadyToSend(TransportChannel* channel);
|
||||
void OnRouteChange(TransportChannel* channel, const Candidate& candidate);
|
||||
|
||||
void OnMessage(rtc::Message* message) override;
|
||||
|
||||
typedef std::pair<rtc::Socket::Option, int> OptionPair;
|
||||
typedef std::vector<OptionPair> OptionList;
|
||||
rtc::Thread* worker_thread_;
|
||||
TransportChannelImpl* impl_;
|
||||
OptionList options_;
|
||||
std::vector<std::string> pending_srtp_ciphers_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(TransportChannelProxy);
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // WEBRTC_P2P_BASE_TRANSPORTCHANNELPROXY_H_
|
||||
575
webrtc/p2p/base/transportcontroller.cc
Normal file
575
webrtc/p2p/base/transportcontroller.cc
Normal file
@ -0,0 +1,575 @@
|
||||
/*
|
||||
* Copyright 2015 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/p2p/base/transportcontroller.h"
|
||||
|
||||
#include "webrtc/base/bind.h"
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/base/thread.h"
|
||||
#include "webrtc/p2p/base/dtlstransport.h"
|
||||
#include "webrtc/p2p/base/p2ptransport.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
enum {
|
||||
MSG_ICECONNECTIONSTATE,
|
||||
MSG_RECEIVING,
|
||||
MSG_ICEGATHERINGSTATE,
|
||||
MSG_CANDIDATESGATHERED,
|
||||
};
|
||||
|
||||
struct CandidatesData : public rtc::MessageData {
|
||||
CandidatesData(const std::string& transport_name,
|
||||
const Candidates& candidates)
|
||||
: transport_name(transport_name), candidates(candidates) {}
|
||||
|
||||
std::string transport_name;
|
||||
Candidates candidates;
|
||||
};
|
||||
|
||||
TransportController::TransportController(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
PortAllocator* port_allocator)
|
||||
: signaling_thread_(signaling_thread),
|
||||
worker_thread_(worker_thread),
|
||||
port_allocator_(port_allocator) {}
|
||||
|
||||
TransportController::~TransportController() {
|
||||
worker_thread_->Invoke<void>(
|
||||
rtc::Bind(&TransportController::DestroyAllTransports_w, this));
|
||||
signaling_thread_->Clear(this);
|
||||
}
|
||||
|
||||
bool TransportController::SetSslMaxProtocolVersion(
|
||||
rtc::SSLProtocolVersion version) {
|
||||
return worker_thread_->Invoke<bool>(rtc::Bind(
|
||||
&TransportController::SetSslMaxProtocolVersion_w, this, version));
|
||||
}
|
||||
|
||||
void TransportController::SetIceConnectionReceivingTimeout(int timeout_ms) {
|
||||
worker_thread_->Invoke<void>(
|
||||
rtc::Bind(&TransportController::SetIceConnectionReceivingTimeout_w, this,
|
||||
timeout_ms));
|
||||
}
|
||||
|
||||
void TransportController::SetIceRole(IceRole ice_role) {
|
||||
worker_thread_->Invoke<void>(
|
||||
rtc::Bind(&TransportController::SetIceRole_w, this, ice_role));
|
||||
}
|
||||
|
||||
bool TransportController::GetSslRole(rtc::SSLRole* role) {
|
||||
return worker_thread_->Invoke<bool>(
|
||||
rtc::Bind(&TransportController::GetSslRole_w, this, role));
|
||||
}
|
||||
|
||||
bool TransportController::SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
|
||||
return worker_thread_->Invoke<bool>(rtc::Bind(
|
||||
&TransportController::SetLocalCertificate_w, this, certificate));
|
||||
}
|
||||
|
||||
bool TransportController::GetLocalCertificate(
|
||||
const std::string& transport_name,
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
|
||||
return worker_thread_->Invoke<bool>(
|
||||
rtc::Bind(&TransportController::GetLocalCertificate_w, this,
|
||||
transport_name, certificate));
|
||||
}
|
||||
|
||||
bool TransportController::GetRemoteSSLCertificate(
|
||||
const std::string& transport_name,
|
||||
rtc::SSLCertificate** cert) {
|
||||
return worker_thread_->Invoke<bool>(
|
||||
rtc::Bind(&TransportController::GetRemoteSSLCertificate_w, this,
|
||||
transport_name, cert));
|
||||
}
|
||||
|
||||
bool TransportController::SetLocalTransportDescription(
|
||||
const std::string& transport_name,
|
||||
const TransportDescription& tdesc,
|
||||
ContentAction action,
|
||||
std::string* err) {
|
||||
return worker_thread_->Invoke<bool>(
|
||||
rtc::Bind(&TransportController::SetLocalTransportDescription_w, this,
|
||||
transport_name, tdesc, action, err));
|
||||
}
|
||||
|
||||
bool TransportController::SetRemoteTransportDescription(
|
||||
const std::string& transport_name,
|
||||
const TransportDescription& tdesc,
|
||||
ContentAction action,
|
||||
std::string* err) {
|
||||
return worker_thread_->Invoke<bool>(
|
||||
rtc::Bind(&TransportController::SetRemoteTransportDescription_w, this,
|
||||
transport_name, tdesc, action, err));
|
||||
}
|
||||
|
||||
void TransportController::MaybeStartGathering() {
|
||||
worker_thread_->Invoke<void>(
|
||||
rtc::Bind(&TransportController::MaybeStartGathering_w, this));
|
||||
}
|
||||
|
||||
bool TransportController::AddRemoteCandidates(const std::string& transport_name,
|
||||
const Candidates& candidates,
|
||||
std::string* err) {
|
||||
return worker_thread_->Invoke<bool>(
|
||||
rtc::Bind(&TransportController::AddRemoteCandidates_w, this,
|
||||
transport_name, candidates, err));
|
||||
}
|
||||
|
||||
bool TransportController::ReadyForRemoteCandidates(
|
||||
const std::string& transport_name) {
|
||||
return worker_thread_->Invoke<bool>(rtc::Bind(
|
||||
&TransportController::ReadyForRemoteCandidates_w, this, transport_name));
|
||||
}
|
||||
|
||||
bool TransportController::GetStats(const std::string& transport_name,
|
||||
TransportStats* stats) {
|
||||
return worker_thread_->Invoke<bool>(
|
||||
rtc::Bind(&TransportController::GetStats_w, this, transport_name, stats));
|
||||
}
|
||||
|
||||
TransportChannel* TransportController::CreateTransportChannel_w(
|
||||
const std::string& transport_name,
|
||||
int component) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
Transport* transport = GetOrCreateTransport_w(transport_name);
|
||||
return transport->CreateChannel(component);
|
||||
}
|
||||
|
||||
void TransportController::DestroyTransportChannel_w(
|
||||
const std::string& transport_name,
|
||||
int component) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
Transport* transport = GetTransport_w(transport_name);
|
||||
if (!transport) {
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
transport->DestroyChannel(component);
|
||||
|
||||
// Just as we create a Transport when its first channel is created,
|
||||
// we delete it when its last channel is deleted.
|
||||
if (!transport->HasChannels()) {
|
||||
DestroyTransport_w(transport_name);
|
||||
}
|
||||
}
|
||||
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>&
|
||||
TransportController::certificate_for_testing() {
|
||||
return certificate_;
|
||||
}
|
||||
|
||||
Transport* TransportController::CreateTransport_w(
|
||||
const std::string& transport_name) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
Transport* transport = new DtlsTransport<P2PTransport>(
|
||||
transport_name, port_allocator(), certificate_);
|
||||
return transport;
|
||||
}
|
||||
|
||||
Transport* TransportController::GetTransport_w(
|
||||
const std::string& transport_name) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
auto iter = transports_.find(transport_name);
|
||||
return (iter != transports_.end()) ? iter->second : nullptr;
|
||||
}
|
||||
|
||||
void TransportController::OnMessage(rtc::Message* pmsg) {
|
||||
RTC_DCHECK(signaling_thread_->IsCurrent());
|
||||
|
||||
switch (pmsg->message_id) {
|
||||
case MSG_ICECONNECTIONSTATE: {
|
||||
rtc::TypedMessageData<IceConnectionState>* data =
|
||||
static_cast<rtc::TypedMessageData<IceConnectionState>*>(pmsg->pdata);
|
||||
SignalConnectionState(data->data());
|
||||
delete data;
|
||||
break;
|
||||
}
|
||||
case MSG_RECEIVING: {
|
||||
rtc::TypedMessageData<bool>* data =
|
||||
static_cast<rtc::TypedMessageData<bool>*>(pmsg->pdata);
|
||||
SignalReceiving(data->data());
|
||||
delete data;
|
||||
break;
|
||||
}
|
||||
case MSG_ICEGATHERINGSTATE: {
|
||||
rtc::TypedMessageData<IceGatheringState>* data =
|
||||
static_cast<rtc::TypedMessageData<IceGatheringState>*>(pmsg->pdata);
|
||||
SignalGatheringState(data->data());
|
||||
delete data;
|
||||
break;
|
||||
}
|
||||
case MSG_CANDIDATESGATHERED: {
|
||||
CandidatesData* data = static_cast<CandidatesData*>(pmsg->pdata);
|
||||
SignalCandidatesGathered(data->transport_name, data->candidates);
|
||||
delete data;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
Transport* TransportController::GetOrCreateTransport_w(
|
||||
const std::string& transport_name) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
Transport* transport = GetTransport_w(transport_name);
|
||||
if (transport) {
|
||||
return transport;
|
||||
}
|
||||
|
||||
transport = CreateTransport_w(transport_name);
|
||||
// The stuff below happens outside of CreateTransport_w so that unit tests
|
||||
// can override CreateTransport_w to return a different type of transport.
|
||||
transport->SetSslMaxProtocolVersion(ssl_max_version_);
|
||||
transport->SetChannelReceivingTimeout(ice_receiving_timeout_ms_);
|
||||
transport->SetIceRole(ice_role_);
|
||||
transport->SetIceTiebreaker(ice_tiebreaker_);
|
||||
if (certificate_) {
|
||||
transport->SetLocalCertificate(certificate_);
|
||||
}
|
||||
transport->SignalConnecting.connect(
|
||||
this, &TransportController::OnTransportConnecting_w);
|
||||
transport->SignalWritableState.connect(
|
||||
this, &TransportController::OnTransportWritableState_w);
|
||||
transport->SignalReceivingState.connect(
|
||||
this, &TransportController::OnTransportReceivingState_w);
|
||||
transport->SignalCompleted.connect(
|
||||
this, &TransportController::OnTransportCompleted_w);
|
||||
transport->SignalFailed.connect(this,
|
||||
&TransportController::OnTransportFailed_w);
|
||||
transport->SignalGatheringState.connect(
|
||||
this, &TransportController::OnTransportGatheringState_w);
|
||||
transport->SignalCandidatesGathered.connect(
|
||||
this, &TransportController::OnTransportCandidatesGathered_w);
|
||||
transport->SignalRoleConflict.connect(
|
||||
this, &TransportController::OnTransportRoleConflict_w);
|
||||
transports_[transport_name] = transport;
|
||||
|
||||
return transport;
|
||||
}
|
||||
|
||||
void TransportController::DestroyTransport_w(
|
||||
const std::string& transport_name) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
auto iter = transports_.find(transport_name);
|
||||
if (iter != transports_.end()) {
|
||||
delete iter->second;
|
||||
transports_.erase(transport_name);
|
||||
}
|
||||
// Destroying a transport may cause aggregate state to change.
|
||||
UpdateAggregateStates_w();
|
||||
}
|
||||
|
||||
void TransportController::DestroyAllTransports_w() {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
for (const auto& kv : transports_) {
|
||||
delete kv.second;
|
||||
}
|
||||
transports_.clear();
|
||||
}
|
||||
|
||||
bool TransportController::SetSslMaxProtocolVersion_w(
|
||||
rtc::SSLProtocolVersion version) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
// Max SSL version can only be set before transports are created.
|
||||
if (!transports_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ssl_max_version_ = version;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransportController::SetIceConnectionReceivingTimeout_w(int timeout_ms) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
ice_receiving_timeout_ms_ = timeout_ms;
|
||||
for (const auto& kv : transports_) {
|
||||
kv.second->SetChannelReceivingTimeout(timeout_ms);
|
||||
}
|
||||
}
|
||||
|
||||
void TransportController::SetIceRole_w(IceRole ice_role) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
ice_role_ = ice_role;
|
||||
for (const auto& kv : transports_) {
|
||||
kv.second->SetIceRole(ice_role_);
|
||||
}
|
||||
}
|
||||
|
||||
bool TransportController::GetSslRole_w(rtc::SSLRole* role) {
|
||||
RTC_DCHECK(worker_thread()->IsCurrent());
|
||||
|
||||
if (transports_.empty()) {
|
||||
return false;
|
||||
}
|
||||
return transports_.begin()->second->GetSslRole(role);
|
||||
}
|
||||
|
||||
bool TransportController::SetLocalCertificate_w(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
if (certificate_) {
|
||||
return false;
|
||||
}
|
||||
if (!certificate) {
|
||||
return false;
|
||||
}
|
||||
certificate_ = certificate;
|
||||
|
||||
for (const auto& kv : transports_) {
|
||||
kv.second->SetLocalCertificate(certificate_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransportController::GetLocalCertificate_w(
|
||||
const std::string& transport_name,
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
Transport* t = GetTransport_w(transport_name);
|
||||
if (!t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return t->GetLocalCertificate(certificate);
|
||||
}
|
||||
|
||||
bool TransportController::GetRemoteSSLCertificate_w(
|
||||
const std::string& transport_name,
|
||||
rtc::SSLCertificate** cert) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
Transport* t = GetTransport_w(transport_name);
|
||||
if (!t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return t->GetRemoteSSLCertificate(cert);
|
||||
}
|
||||
|
||||
bool TransportController::SetLocalTransportDescription_w(
|
||||
const std::string& transport_name,
|
||||
const TransportDescription& tdesc,
|
||||
ContentAction action,
|
||||
std::string* err) {
|
||||
RTC_DCHECK(worker_thread()->IsCurrent());
|
||||
|
||||
Transport* transport = GetTransport_w(transport_name);
|
||||
if (!transport) {
|
||||
// If we didn't find a transport, that's not an error;
|
||||
// it could have been deleted as a result of bundling.
|
||||
// TODO(deadbeef): Make callers smarter so they won't attempt to set a
|
||||
// description on a deleted transport.
|
||||
return true;
|
||||
}
|
||||
|
||||
return transport->SetLocalTransportDescription(tdesc, action, err);
|
||||
}
|
||||
|
||||
bool TransportController::SetRemoteTransportDescription_w(
|
||||
const std::string& transport_name,
|
||||
const TransportDescription& tdesc,
|
||||
ContentAction action,
|
||||
std::string* err) {
|
||||
RTC_DCHECK(worker_thread()->IsCurrent());
|
||||
|
||||
Transport* transport = GetTransport_w(transport_name);
|
||||
if (!transport) {
|
||||
// If we didn't find a transport, that's not an error;
|
||||
// it could have been deleted as a result of bundling.
|
||||
// TODO(deadbeef): Make callers smarter so they won't attempt to set a
|
||||
// description on a deleted transport.
|
||||
return true;
|
||||
}
|
||||
|
||||
return transport->SetRemoteTransportDescription(tdesc, action, err);
|
||||
}
|
||||
|
||||
void TransportController::MaybeStartGathering_w() {
|
||||
for (const auto& kv : transports_) {
|
||||
kv.second->MaybeStartGathering();
|
||||
}
|
||||
}
|
||||
|
||||
bool TransportController::AddRemoteCandidates_w(
|
||||
const std::string& transport_name,
|
||||
const Candidates& candidates,
|
||||
std::string* err) {
|
||||
RTC_DCHECK(worker_thread()->IsCurrent());
|
||||
|
||||
Transport* transport = GetTransport_w(transport_name);
|
||||
if (!transport) {
|
||||
// If we didn't find a transport, that's not an error;
|
||||
// it could have been deleted as a result of bundling.
|
||||
return true;
|
||||
}
|
||||
|
||||
return transport->AddRemoteCandidates(candidates, err);
|
||||
}
|
||||
|
||||
bool TransportController::ReadyForRemoteCandidates_w(
|
||||
const std::string& transport_name) {
|
||||
RTC_DCHECK(worker_thread()->IsCurrent());
|
||||
|
||||
Transport* transport = GetTransport_w(transport_name);
|
||||
if (!transport) {
|
||||
return false;
|
||||
}
|
||||
return transport->ready_for_remote_candidates();
|
||||
}
|
||||
|
||||
bool TransportController::GetStats_w(const std::string& transport_name,
|
||||
TransportStats* stats) {
|
||||
RTC_DCHECK(worker_thread()->IsCurrent());
|
||||
|
||||
Transport* transport = GetTransport_w(transport_name);
|
||||
if (!transport) {
|
||||
return false;
|
||||
}
|
||||
return transport->GetStats(stats);
|
||||
}
|
||||
|
||||
void TransportController::OnTransportConnecting_w(Transport* transport) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
UpdateAggregateStates_w();
|
||||
}
|
||||
|
||||
void TransportController::OnTransportWritableState_w(Transport* transport) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
UpdateAggregateStates_w();
|
||||
}
|
||||
|
||||
void TransportController::OnTransportReceivingState_w(Transport* transport) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
UpdateAggregateStates_w();
|
||||
}
|
||||
|
||||
void TransportController::OnTransportCompleted_w(Transport* transport) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
UpdateAggregateStates_w();
|
||||
}
|
||||
|
||||
void TransportController::OnTransportFailed_w(Transport* transport) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
UpdateAggregateStates_w();
|
||||
}
|
||||
|
||||
void TransportController::OnTransportGatheringState_w(Transport* transport) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
UpdateAggregateStates_w();
|
||||
}
|
||||
|
||||
void TransportController::OnTransportCandidatesGathered_w(
|
||||
Transport* transport,
|
||||
const std::vector<Candidate>& candidates) {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
CandidatesData* data = new CandidatesData(transport->name(), candidates);
|
||||
signaling_thread_->Post(this, MSG_CANDIDATESGATHERED, data);
|
||||
}
|
||||
|
||||
void TransportController::OnTransportRoleConflict_w() {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
if (ice_role_switch_) {
|
||||
LOG(LS_WARNING) << "Repeat of role conflict signal from Transport.";
|
||||
return;
|
||||
}
|
||||
|
||||
ice_role_switch_ = true;
|
||||
IceRole reversed_role = (ice_role_ == ICEROLE_CONTROLLING)
|
||||
? ICEROLE_CONTROLLED
|
||||
: ICEROLE_CONTROLLING;
|
||||
for (const auto& kv : transports_) {
|
||||
kv.second->SetIceRole(reversed_role);
|
||||
}
|
||||
}
|
||||
|
||||
void TransportController::UpdateAggregateStates_w() {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
IceConnectionState new_connection_state = kIceConnectionConnecting;
|
||||
IceGatheringState new_gathering_state = kIceGatheringNew;
|
||||
bool any_receiving = false;
|
||||
bool any_failed = false;
|
||||
bool all_connected = HasChannels_w();
|
||||
bool all_completed = HasChannels_w();
|
||||
bool any_gathering = false;
|
||||
bool all_done_gathering = HasChannels_w();
|
||||
for (const auto& kv : transports_) {
|
||||
// Ignore transports without channels since they're about to be deleted,
|
||||
// and their state is meaningless.
|
||||
if (!kv.second->HasChannels()) {
|
||||
continue;
|
||||
}
|
||||
any_receiving = any_receiving || kv.second->any_channel_receiving();
|
||||
any_failed = any_failed || kv.second->AnyChannelFailed();
|
||||
all_connected = all_connected && kv.second->all_channels_writable();
|
||||
all_completed = all_completed && kv.second->AllChannelsCompleted();
|
||||
any_gathering =
|
||||
any_gathering || kv.second->gathering_state() != kIceGatheringNew;
|
||||
all_done_gathering = all_done_gathering &&
|
||||
kv.second->gathering_state() == kIceGatheringComplete;
|
||||
}
|
||||
|
||||
if (any_failed) {
|
||||
new_connection_state = kIceConnectionFailed;
|
||||
} else if (all_completed) {
|
||||
new_connection_state = kIceConnectionCompleted;
|
||||
} else if (all_connected) {
|
||||
new_connection_state = kIceConnectionConnected;
|
||||
}
|
||||
if (connection_state_ != new_connection_state) {
|
||||
connection_state_ = new_connection_state;
|
||||
signaling_thread_->Post(
|
||||
this, MSG_ICECONNECTIONSTATE,
|
||||
new rtc::TypedMessageData<IceConnectionState>(new_connection_state));
|
||||
}
|
||||
|
||||
if (receiving_ != any_receiving) {
|
||||
receiving_ = any_receiving;
|
||||
signaling_thread_->Post(this, MSG_RECEIVING,
|
||||
new rtc::TypedMessageData<bool>(any_receiving));
|
||||
}
|
||||
|
||||
if (all_done_gathering) {
|
||||
new_gathering_state = kIceGatheringComplete;
|
||||
} else if (any_gathering) {
|
||||
new_gathering_state = kIceGatheringGathering;
|
||||
}
|
||||
if (gathering_state_ != new_gathering_state) {
|
||||
gathering_state_ = new_gathering_state;
|
||||
signaling_thread_->Post(
|
||||
this, MSG_ICEGATHERINGSTATE,
|
||||
new rtc::TypedMessageData<IceGatheringState>(new_gathering_state));
|
||||
}
|
||||
}
|
||||
|
||||
bool TransportController::HasChannels_w() {
|
||||
for (const auto& kv : transports_) {
|
||||
if (kv.second->HasChannels()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
196
webrtc/p2p/base/transportcontroller.h
Normal file
196
webrtc/p2p/base/transportcontroller.h
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_P2P_BASE_TRANSPORTCONTROLLER_H_
|
||||
#define WEBRTC_P2P_BASE_TRANSPORTCONTROLLER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/base/sigslot.h"
|
||||
#include "webrtc/base/sslstreamadapter.h"
|
||||
#include "webrtc/p2p/base/candidate.h"
|
||||
#include "webrtc/p2p/base/transport.h"
|
||||
|
||||
namespace rtc {
|
||||
class Thread;
|
||||
}
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class TransportController : public sigslot::has_slots<>,
|
||||
public rtc::MessageHandler {
|
||||
public:
|
||||
TransportController(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
PortAllocator* port_allocator);
|
||||
|
||||
virtual ~TransportController();
|
||||
|
||||
rtc::Thread* signaling_thread() const { return signaling_thread_; }
|
||||
rtc::Thread* worker_thread() const { return worker_thread_; }
|
||||
|
||||
PortAllocator* port_allocator() const { return port_allocator_; }
|
||||
|
||||
// Can only be set before transports are created.
|
||||
// TODO(deadbeef): Make this an argument to the constructor once BaseSession
|
||||
// and WebRtcSession are combined
|
||||
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version);
|
||||
|
||||
void SetIceConnectionReceivingTimeout(int timeout_ms);
|
||||
void SetIceRole(IceRole ice_role);
|
||||
|
||||
// TODO(deadbeef) - Return role of each transport, as role may differ from
|
||||
// one another.
|
||||
// In current implementaion we just return the role of the first transport
|
||||
// alphabetically.
|
||||
bool GetSslRole(rtc::SSLRole* role);
|
||||
|
||||
// Specifies the identity to use in this session.
|
||||
// Can only be called once.
|
||||
bool SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
|
||||
bool GetLocalCertificate(
|
||||
const std::string& transport_name,
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate);
|
||||
// Caller owns returned certificate
|
||||
bool GetRemoteSSLCertificate(const std::string& transport_name,
|
||||
rtc::SSLCertificate** cert);
|
||||
bool SetLocalTransportDescription(const std::string& transport_name,
|
||||
const TransportDescription& tdesc,
|
||||
ContentAction action,
|
||||
std::string* err);
|
||||
bool SetRemoteTransportDescription(const std::string& transport_name,
|
||||
const TransportDescription& tdesc,
|
||||
ContentAction action,
|
||||
std::string* err);
|
||||
// Start gathering candidates for any new transports, or transports doing an
|
||||
// ICE restart.
|
||||
void MaybeStartGathering();
|
||||
bool AddRemoteCandidates(const std::string& transport_name,
|
||||
const Candidates& candidates,
|
||||
std::string* err);
|
||||
bool ReadyForRemoteCandidates(const std::string& transport_name);
|
||||
bool GetStats(const std::string& transport_name, TransportStats* stats);
|
||||
|
||||
virtual TransportChannel* CreateTransportChannel_w(
|
||||
const std::string& transport_name,
|
||||
int component);
|
||||
virtual void DestroyTransportChannel_w(const std::string& transport_name,
|
||||
int component);
|
||||
|
||||
// All of these signals are fired on the signalling thread.
|
||||
|
||||
// If any transport failed => failed,
|
||||
// Else if all completed => completed,
|
||||
// Else if all connected => connected,
|
||||
// Else => connecting
|
||||
sigslot::signal1<IceConnectionState> SignalConnectionState;
|
||||
|
||||
// Receiving if any transport is receiving
|
||||
sigslot::signal1<bool> SignalReceiving;
|
||||
|
||||
// If all transports done gathering => complete,
|
||||
// Else if any are gathering => gathering,
|
||||
// Else => new
|
||||
sigslot::signal1<IceGatheringState> SignalGatheringState;
|
||||
|
||||
// (transport_name, candidates)
|
||||
sigslot::signal2<const std::string&, const Candidates&>
|
||||
SignalCandidatesGathered;
|
||||
|
||||
// for unit test
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate_for_testing();
|
||||
|
||||
protected:
|
||||
// Protected and virtual so we can override it in unit tests.
|
||||
virtual Transport* CreateTransport_w(const std::string& transport_name);
|
||||
|
||||
// For unit tests
|
||||
const std::map<std::string, Transport*>& transports() { return transports_; }
|
||||
Transport* GetTransport_w(const std::string& transport_name);
|
||||
|
||||
private:
|
||||
void OnMessage(rtc::Message* pmsg) override;
|
||||
|
||||
Transport* GetOrCreateTransport_w(const std::string& transport_name);
|
||||
void DestroyTransport_w(const std::string& transport_name);
|
||||
void DestroyAllTransports_w();
|
||||
|
||||
bool SetSslMaxProtocolVersion_w(rtc::SSLProtocolVersion version);
|
||||
void SetIceConnectionReceivingTimeout_w(int timeout_ms);
|
||||
void SetIceRole_w(IceRole ice_role);
|
||||
bool GetSslRole_w(rtc::SSLRole* role);
|
||||
bool SetLocalCertificate_w(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
|
||||
bool GetLocalCertificate_w(
|
||||
const std::string& transport_name,
|
||||
rtc::scoped_refptr<rtc::RTCCertificate>* certificate);
|
||||
bool GetRemoteSSLCertificate_w(const std::string& transport_name,
|
||||
rtc::SSLCertificate** cert);
|
||||
bool SetLocalTransportDescription_w(const std::string& transport_name,
|
||||
const TransportDescription& tdesc,
|
||||
ContentAction action,
|
||||
std::string* err);
|
||||
bool SetRemoteTransportDescription_w(const std::string& transport_name,
|
||||
const TransportDescription& tdesc,
|
||||
ContentAction action,
|
||||
std::string* err);
|
||||
void MaybeStartGathering_w();
|
||||
bool AddRemoteCandidates_w(const std::string& transport_name,
|
||||
const Candidates& candidates,
|
||||
std::string* err);
|
||||
bool ReadyForRemoteCandidates_w(const std::string& transport_name);
|
||||
bool GetStats_w(const std::string& transport_name, TransportStats* stats);
|
||||
|
||||
// Handlers for signals from Transport.
|
||||
void OnTransportConnecting_w(Transport* transport);
|
||||
void OnTransportWritableState_w(Transport* transport);
|
||||
void OnTransportReceivingState_w(Transport* transport);
|
||||
void OnTransportCompleted_w(Transport* transport);
|
||||
void OnTransportFailed_w(Transport* transport);
|
||||
void OnTransportGatheringState_w(Transport* transport);
|
||||
void OnTransportCandidatesGathered_w(
|
||||
Transport* transport,
|
||||
const std::vector<Candidate>& candidates);
|
||||
void OnTransportRoleConflict_w();
|
||||
|
||||
void UpdateAggregateStates_w();
|
||||
bool HasChannels_w();
|
||||
|
||||
rtc::Thread* const signaling_thread_ = nullptr;
|
||||
rtc::Thread* const worker_thread_ = nullptr;
|
||||
typedef std::map<std::string, Transport*> TransportMap;
|
||||
TransportMap transports_;
|
||||
|
||||
PortAllocator* const port_allocator_ = nullptr;
|
||||
rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10;
|
||||
|
||||
// Aggregate state for Transports
|
||||
IceConnectionState connection_state_ = kIceConnectionConnecting;
|
||||
bool receiving_ = false;
|
||||
IceGatheringState gathering_state_ = kIceGatheringNew;
|
||||
|
||||
// TODO(deadbeef): Move the fields below down to the transports themselves
|
||||
|
||||
// Timeout value in milliseconds for which no ICE connection receives
|
||||
// any packets
|
||||
int ice_receiving_timeout_ms_ = -1;
|
||||
IceRole ice_role_ = ICEROLE_CONTROLLING;
|
||||
// Flag which will be set to true after the first role switch
|
||||
bool ice_role_switch_ = false;
|
||||
uint64 ice_tiebreaker_ = rtc::CreateRandomId64();
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // WEBRTC_P2P_BASE_TRANSPORTCONTROLLER_H_
|
||||
679
webrtc/p2p/base/transportcontroller_unittest.cc
Normal file
679
webrtc/p2p/base/transportcontroller_unittest.cc
Normal file
@ -0,0 +1,679 @@
|
||||
/*
|
||||
* Copyright 2015 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "webrtc/base/fakesslidentity.h"
|
||||
#include "webrtc/base/gunit.h"
|
||||
#include "webrtc/base/helpers.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/base/sslidentity.h"
|
||||
#include "webrtc/base/thread.h"
|
||||
#include "webrtc/p2p/base/dtlstransportchannel.h"
|
||||
#include "webrtc/p2p/base/faketransportcontroller.h"
|
||||
#include "webrtc/p2p/base/p2ptransportchannel.h"
|
||||
#include "webrtc/p2p/base/portallocator.h"
|
||||
#include "webrtc/p2p/base/transportcontroller.h"
|
||||
#include "webrtc/p2p/client/fakeportallocator.h"
|
||||
|
||||
static const int kTimeout = 100;
|
||||
static const char kIceUfrag1[] = "TESTICEUFRAG0001";
|
||||
static const char kIcePwd1[] = "TESTICEPWD00000000000001";
|
||||
static const char kIceUfrag2[] = "TESTICEUFRAG0002";
|
||||
static const char kIcePwd2[] = "TESTICEPWD00000000000002";
|
||||
|
||||
using cricket::Candidate;
|
||||
using cricket::Candidates;
|
||||
using cricket::FakeTransportChannel;
|
||||
using cricket::FakeTransportController;
|
||||
using cricket::IceConnectionState;
|
||||
using cricket::IceGatheringState;
|
||||
using cricket::TransportChannel;
|
||||
using cricket::TransportController;
|
||||
using cricket::TransportDescription;
|
||||
using cricket::TransportStats;
|
||||
|
||||
// Only subclassing from FakeTransportController because currently that's the
|
||||
// only way to have a TransportController with fake TransportChannels.
|
||||
//
|
||||
// TODO(deadbeef): Change this once the Transport/TransportChannel class
|
||||
// heirarchy is cleaned up, and we can pass a "TransportChannelFactory" or
|
||||
// something similar into TransportController.
|
||||
typedef FakeTransportController TransportControllerForTest;
|
||||
|
||||
class TransportControllerTest : public testing::Test,
|
||||
public sigslot::has_slots<> {
|
||||
public:
|
||||
TransportControllerTest()
|
||||
: transport_controller_(new TransportControllerForTest()),
|
||||
signaling_thread_(rtc::Thread::Current()) {
|
||||
ConnectTransportControllerSignals();
|
||||
}
|
||||
|
||||
void CreateTransportControllerWithWorkerThread() {
|
||||
if (!worker_thread_) {
|
||||
worker_thread_.reset(new rtc::Thread());
|
||||
worker_thread_->Start();
|
||||
}
|
||||
transport_controller_.reset(
|
||||
new TransportControllerForTest(worker_thread_.get()));
|
||||
ConnectTransportControllerSignals();
|
||||
}
|
||||
|
||||
void ConnectTransportControllerSignals() {
|
||||
transport_controller_->SignalConnectionState.connect(
|
||||
this, &TransportControllerTest::OnConnectionState);
|
||||
transport_controller_->SignalReceiving.connect(
|
||||
this, &TransportControllerTest::OnReceiving);
|
||||
transport_controller_->SignalGatheringState.connect(
|
||||
this, &TransportControllerTest::OnGatheringState);
|
||||
transport_controller_->SignalCandidatesGathered.connect(
|
||||
this, &TransportControllerTest::OnCandidatesGathered);
|
||||
}
|
||||
|
||||
FakeTransportChannel* CreateChannel(const std::string& content,
|
||||
int component) {
|
||||
TransportChannel* channel =
|
||||
transport_controller_->CreateTransportChannel_w(content, component);
|
||||
return static_cast<FakeTransportChannel*>(channel);
|
||||
}
|
||||
|
||||
void DestroyChannel(const std::string& content, int component) {
|
||||
transport_controller_->DestroyTransportChannel_w(content, component);
|
||||
}
|
||||
|
||||
Candidate CreateCandidate(int component) {
|
||||
Candidate c;
|
||||
c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
|
||||
c.set_component(1);
|
||||
c.set_protocol(cricket::UDP_PROTOCOL_NAME);
|
||||
c.set_priority(1);
|
||||
return c;
|
||||
}
|
||||
|
||||
// Used for thread hopping test.
|
||||
void CreateChannelsAndCompleteConnectionOnWorkerThread() {
|
||||
worker_thread_->Invoke<void>(rtc::Bind(
|
||||
&TransportControllerTest::CreateChannelsAndCompleteConnection_w, this));
|
||||
}
|
||||
|
||||
void CreateChannelsAndCompleteConnection_w() {
|
||||
transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
|
||||
TransportDescription local_desc(
|
||||
std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
|
||||
cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
|
||||
std::string err;
|
||||
transport_controller_->SetLocalTransportDescription(
|
||||
"audio", local_desc, cricket::CA_OFFER, &err);
|
||||
transport_controller_->SetLocalTransportDescription(
|
||||
"video", local_desc, cricket::CA_OFFER, &err);
|
||||
transport_controller_->MaybeStartGathering();
|
||||
channel1->SignalCandidateGathered(channel1, CreateCandidate(1));
|
||||
channel2->SignalCandidateGathered(channel2, CreateCandidate(1));
|
||||
channel1->SetCandidatesGatheringComplete();
|
||||
channel2->SetCandidatesGatheringComplete();
|
||||
channel1->SetConnectionCount(2);
|
||||
channel2->SetConnectionCount(2);
|
||||
channel1->SetReceiving(true);
|
||||
channel2->SetReceiving(true);
|
||||
channel1->SetWritable(true);
|
||||
channel2->SetWritable(true);
|
||||
channel1->SetConnectionCount(1);
|
||||
channel2->SetConnectionCount(1);
|
||||
}
|
||||
|
||||
protected:
|
||||
void OnConnectionState(IceConnectionState state) {
|
||||
if (!signaling_thread_->IsCurrent()) {
|
||||
signaled_on_non_signaling_thread_ = true;
|
||||
}
|
||||
connection_state_ = state;
|
||||
++connection_state_signal_count_;
|
||||
}
|
||||
|
||||
void OnReceiving(bool receiving) {
|
||||
if (!signaling_thread_->IsCurrent()) {
|
||||
signaled_on_non_signaling_thread_ = true;
|
||||
}
|
||||
receiving_ = receiving;
|
||||
++receiving_signal_count_;
|
||||
}
|
||||
|
||||
void OnGatheringState(IceGatheringState state) {
|
||||
if (!signaling_thread_->IsCurrent()) {
|
||||
signaled_on_non_signaling_thread_ = true;
|
||||
}
|
||||
gathering_state_ = state;
|
||||
++gathering_state_signal_count_;
|
||||
}
|
||||
|
||||
void OnCandidatesGathered(const std::string& transport_name,
|
||||
const Candidates& candidates) {
|
||||
if (!signaling_thread_->IsCurrent()) {
|
||||
signaled_on_non_signaling_thread_ = true;
|
||||
}
|
||||
candidates_[transport_name].insert(candidates_[transport_name].end(),
|
||||
candidates.begin(), candidates.end());
|
||||
++candidates_signal_count_;
|
||||
}
|
||||
|
||||
rtc::scoped_ptr<rtc::Thread> worker_thread_; // Not used for most tests.
|
||||
rtc::scoped_ptr<TransportControllerForTest> transport_controller_;
|
||||
|
||||
// Information received from signals from transport controller.
|
||||
IceConnectionState connection_state_ = cricket::kIceConnectionConnecting;
|
||||
bool receiving_ = false;
|
||||
IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
|
||||
// transport_name => candidates
|
||||
std::map<std::string, Candidates> candidates_;
|
||||
// Counts of each signal emitted.
|
||||
int connection_state_signal_count_ = 0;
|
||||
int receiving_signal_count_ = 0;
|
||||
int gathering_state_signal_count_ = 0;
|
||||
int candidates_signal_count_ = 0;
|
||||
|
||||
// Used to make sure signals only come on signaling thread.
|
||||
rtc::Thread* const signaling_thread_ = nullptr;
|
||||
bool signaled_on_non_signaling_thread_ = false;
|
||||
};
|
||||
|
||||
TEST_F(TransportControllerTest, TestSetIceReceivingTimeout) {
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
|
||||
transport_controller_->SetIceConnectionReceivingTimeout(1000);
|
||||
EXPECT_EQ(1000, channel1->receiving_timeout());
|
||||
|
||||
// Test that value stored in controller is applied to new channels.
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
EXPECT_EQ(1000, channel2->receiving_timeout());
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSetSslMaxProtocolVersion) {
|
||||
EXPECT_TRUE(transport_controller_->SetSslMaxProtocolVersion(
|
||||
rtc::SSL_PROTOCOL_DTLS_12));
|
||||
FakeTransportChannel* channel = CreateChannel("audio", 1);
|
||||
|
||||
ASSERT_NE(nullptr, channel);
|
||||
EXPECT_EQ(rtc::SSL_PROTOCOL_DTLS_12, channel->ssl_max_protocol_version());
|
||||
|
||||
// Setting max version after transport is created should fail.
|
||||
EXPECT_FALSE(transport_controller_->SetSslMaxProtocolVersion(
|
||||
rtc::SSL_PROTOCOL_DTLS_10));
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSetIceRole) {
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
|
||||
transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
|
||||
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole());
|
||||
transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLED);
|
||||
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel1->GetIceRole());
|
||||
|
||||
// Test that value stored in controller is applied to new channels.
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel2->GetIceRole());
|
||||
}
|
||||
|
||||
// Test that when one channel encounters a role conflict, the ICE role is
|
||||
// swapped on every channel.
|
||||
TEST_F(TransportControllerTest, TestIceRoleConflict) {
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
|
||||
transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
|
||||
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole());
|
||||
EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel2->GetIceRole());
|
||||
|
||||
channel1->SignalRoleConflict(channel1);
|
||||
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel1->GetIceRole());
|
||||
EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel2->GetIceRole());
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestGetSslRole) {
|
||||
FakeTransportChannel* channel = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel);
|
||||
ASSERT_TRUE(channel->SetSslRole(rtc::SSL_CLIENT));
|
||||
rtc::SSLRole role;
|
||||
EXPECT_TRUE(transport_controller_->GetSslRole(&role));
|
||||
EXPECT_EQ(rtc::SSL_CLIENT, role);
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSetAndGetLocalCertificate) {
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
|
||||
rtc::RTCCertificate::Create(
|
||||
rtc::scoped_ptr<rtc::SSLIdentity>(
|
||||
rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT))
|
||||
.Pass());
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
|
||||
rtc::RTCCertificate::Create(
|
||||
rtc::scoped_ptr<rtc::SSLIdentity>(
|
||||
rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT))
|
||||
.Pass());
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
|
||||
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
|
||||
EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
|
||||
EXPECT_TRUE(transport_controller_->GetLocalCertificate(
|
||||
"audio", &returned_certificate));
|
||||
EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
|
||||
returned_certificate->identity()->certificate().ToPEMString());
|
||||
|
||||
// Should fail if called for a nonexistant transport.
|
||||
EXPECT_FALSE(transport_controller_->GetLocalCertificate(
|
||||
"video", &returned_certificate));
|
||||
|
||||
// Test that identity stored in controller is applied to new channels.
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
EXPECT_TRUE(transport_controller_->GetLocalCertificate(
|
||||
"video", &returned_certificate));
|
||||
EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
|
||||
returned_certificate->identity()->certificate().ToPEMString());
|
||||
|
||||
// Shouldn't be able to change the identity once set.
|
||||
EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestGetRemoteSSLCertificate) {
|
||||
rtc::FakeSSLCertificate fake_certificate("fake_data");
|
||||
rtc::scoped_ptr<rtc::SSLCertificate> returned_certificate;
|
||||
|
||||
FakeTransportChannel* channel = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel);
|
||||
|
||||
channel->SetRemoteSSLCertificate(&fake_certificate);
|
||||
EXPECT_TRUE(transport_controller_->GetRemoteSSLCertificate(
|
||||
"audio", returned_certificate.accept()));
|
||||
EXPECT_EQ(fake_certificate.ToPEMString(),
|
||||
returned_certificate->ToPEMString());
|
||||
|
||||
// Should fail if called for a nonexistant transport.
|
||||
EXPECT_FALSE(transport_controller_->GetRemoteSSLCertificate(
|
||||
"video", returned_certificate.accept()));
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSetLocalTransportDescription) {
|
||||
FakeTransportChannel* channel = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel);
|
||||
TransportDescription local_desc(
|
||||
std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
|
||||
cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
|
||||
std::string err;
|
||||
EXPECT_TRUE(transport_controller_->SetLocalTransportDescription(
|
||||
"audio", local_desc, cricket::CA_OFFER, &err));
|
||||
// Check that ICE ufrag and pwd were propagated to channel.
|
||||
EXPECT_EQ(kIceUfrag1, channel->ice_ufrag());
|
||||
EXPECT_EQ(kIcePwd1, channel->ice_pwd());
|
||||
// After setting local description, we should be able to start gathering
|
||||
// candidates.
|
||||
transport_controller_->MaybeStartGathering();
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(1, gathering_state_signal_count_);
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSetRemoteTransportDescription) {
|
||||
FakeTransportChannel* channel = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel);
|
||||
TransportDescription remote_desc(
|
||||
std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
|
||||
cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
|
||||
std::string err;
|
||||
EXPECT_TRUE(transport_controller_->SetRemoteTransportDescription(
|
||||
"audio", remote_desc, cricket::CA_OFFER, &err));
|
||||
// Check that ICE ufrag and pwd were propagated to channel.
|
||||
EXPECT_EQ(kIceUfrag1, channel->remote_ice_ufrag());
|
||||
EXPECT_EQ(kIcePwd1, channel->remote_ice_pwd());
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestAddRemoteCandidates) {
|
||||
FakeTransportChannel* channel = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel);
|
||||
Candidates candidates;
|
||||
candidates.push_back(CreateCandidate(1));
|
||||
std::string err;
|
||||
EXPECT_TRUE(
|
||||
transport_controller_->AddRemoteCandidates("audio", candidates, &err));
|
||||
EXPECT_EQ(1U, channel->remote_candidates().size());
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestReadyForRemoteCandidates) {
|
||||
FakeTransportChannel* channel = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel);
|
||||
// We expect to be ready for remote candidates only after local and remote
|
||||
// descriptions are set.
|
||||
EXPECT_FALSE(transport_controller_->ReadyForRemoteCandidates("audio"));
|
||||
|
||||
std::string err;
|
||||
TransportDescription remote_desc(
|
||||
std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
|
||||
cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
|
||||
EXPECT_TRUE(transport_controller_->SetRemoteTransportDescription(
|
||||
"audio", remote_desc, cricket::CA_OFFER, &err));
|
||||
EXPECT_FALSE(transport_controller_->ReadyForRemoteCandidates("audio"));
|
||||
|
||||
TransportDescription local_desc(
|
||||
std::vector<std::string>(), kIceUfrag2, kIcePwd2, cricket::ICEMODE_FULL,
|
||||
cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
|
||||
EXPECT_TRUE(transport_controller_->SetLocalTransportDescription(
|
||||
"audio", local_desc, cricket::CA_ANSWER, &err));
|
||||
EXPECT_TRUE(transport_controller_->ReadyForRemoteCandidates("audio"));
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestGetStats) {
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
FakeTransportChannel* channel2 = CreateChannel("audio", 2);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
FakeTransportChannel* channel3 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel3);
|
||||
|
||||
TransportStats stats;
|
||||
EXPECT_TRUE(transport_controller_->GetStats("audio", &stats));
|
||||
EXPECT_EQ("audio", stats.transport_name);
|
||||
EXPECT_EQ(2U, stats.channel_stats.size());
|
||||
}
|
||||
|
||||
// Test that transport gets destroyed when it has no more channels.
|
||||
TEST_F(TransportControllerTest, TestCreateAndDestroyChannel) {
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
FakeTransportChannel* channel2 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
ASSERT_EQ(channel1, channel2);
|
||||
FakeTransportChannel* channel3 = CreateChannel("audio", 2);
|
||||
ASSERT_NE(nullptr, channel3);
|
||||
|
||||
// Using GetStats to check if transport is destroyed from an outside class's
|
||||
// perspective.
|
||||
TransportStats stats;
|
||||
EXPECT_TRUE(transport_controller_->GetStats("audio", &stats));
|
||||
DestroyChannel("audio", 2);
|
||||
DestroyChannel("audio", 1);
|
||||
EXPECT_TRUE(transport_controller_->GetStats("audio", &stats));
|
||||
DestroyChannel("audio", 1);
|
||||
EXPECT_FALSE(transport_controller_->GetStats("audio", &stats));
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSignalConnectionStateFailed) {
|
||||
// Need controlling ICE role to get in failed state.
|
||||
transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
|
||||
// Should signal "failed" if any channel failed; channel is considered failed
|
||||
// if it previously had a connection but now has none, and gathering is
|
||||
// complete.
|
||||
channel1->SetCandidatesGatheringComplete();
|
||||
channel1->SetConnectionCount(1);
|
||||
channel1->SetConnectionCount(0);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
|
||||
EXPECT_EQ(1, connection_state_signal_count_);
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSignalConnectionStateConnected) {
|
||||
transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
FakeTransportChannel* channel3 = CreateChannel("video", 2);
|
||||
ASSERT_NE(nullptr, channel3);
|
||||
|
||||
// First, have one channel connect, and another fail, to ensure that
|
||||
// the first channel connecting didn't trigger a "connected" state signal.
|
||||
// We should only get a signal when all are connected.
|
||||
channel1->SetConnectionCount(2);
|
||||
channel1->SetWritable(true);
|
||||
channel3->SetCandidatesGatheringComplete();
|
||||
channel3->SetConnectionCount(1);
|
||||
channel3->SetConnectionCount(0);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
|
||||
// Signal count of 1 means that the only signal emitted was "failed".
|
||||
EXPECT_EQ(1, connection_state_signal_count_);
|
||||
|
||||
// Destroy the failed channel to return to "connecting" state.
|
||||
DestroyChannel("video", 2);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
|
||||
kTimeout);
|
||||
EXPECT_EQ(2, connection_state_signal_count_);
|
||||
|
||||
// Make the remaining channel reach a connected state.
|
||||
channel2->SetConnectionCount(2);
|
||||
channel2->SetWritable(true);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
|
||||
EXPECT_EQ(3, connection_state_signal_count_);
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSignalConnectionStateComplete) {
|
||||
transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
FakeTransportChannel* channel3 = CreateChannel("video", 2);
|
||||
ASSERT_NE(nullptr, channel3);
|
||||
|
||||
// Similar to above test, but we're now reaching the completed state, which
|
||||
// means only one connection per FakeTransportChannel.
|
||||
channel1->SetCandidatesGatheringComplete();
|
||||
channel1->SetConnectionCount(1);
|
||||
channel1->SetWritable(true);
|
||||
channel3->SetCandidatesGatheringComplete();
|
||||
channel3->SetConnectionCount(1);
|
||||
channel3->SetConnectionCount(0);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
|
||||
// Signal count of 1 means that the only signal emitted was "failed".
|
||||
EXPECT_EQ(1, connection_state_signal_count_);
|
||||
|
||||
// Destroy the failed channel to return to "connecting" state.
|
||||
DestroyChannel("video", 2);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
|
||||
kTimeout);
|
||||
EXPECT_EQ(2, connection_state_signal_count_);
|
||||
|
||||
// Make the remaining channel reach a connected state.
|
||||
channel2->SetCandidatesGatheringComplete();
|
||||
channel2->SetConnectionCount(2);
|
||||
channel2->SetWritable(true);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
|
||||
EXPECT_EQ(3, connection_state_signal_count_);
|
||||
|
||||
// Finally, transition to completed state.
|
||||
channel2->SetConnectionCount(1);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
|
||||
EXPECT_EQ(4, connection_state_signal_count_);
|
||||
}
|
||||
|
||||
// Make sure that if we're "connected" and remove a transport, we stay in the
|
||||
// "connected" state.
|
||||
TEST_F(TransportControllerTest, TestDestroyTransportAndStayConnected) {
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
|
||||
channel1->SetCandidatesGatheringComplete();
|
||||
channel1->SetConnectionCount(2);
|
||||
channel1->SetWritable(true);
|
||||
channel2->SetCandidatesGatheringComplete();
|
||||
channel2->SetConnectionCount(2);
|
||||
channel2->SetWritable(true);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
|
||||
EXPECT_EQ(1, connection_state_signal_count_);
|
||||
|
||||
// Destroy one channel, then "complete" the other one, so we reach
|
||||
// a known state.
|
||||
DestroyChannel("video", 1);
|
||||
channel1->SetConnectionCount(1);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
|
||||
// Signal count of 2 means the deletion didn't cause any unexpected signals
|
||||
EXPECT_EQ(2, connection_state_signal_count_);
|
||||
}
|
||||
|
||||
// If we destroy the last/only transport, we should simply transition to
|
||||
// "connecting".
|
||||
TEST_F(TransportControllerTest, TestDestroyLastTransportWhileConnected) {
|
||||
FakeTransportChannel* channel = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel);
|
||||
|
||||
channel->SetCandidatesGatheringComplete();
|
||||
channel->SetConnectionCount(2);
|
||||
channel->SetWritable(true);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
|
||||
EXPECT_EQ(1, connection_state_signal_count_);
|
||||
|
||||
DestroyChannel("audio", 1);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
|
||||
kTimeout);
|
||||
// Signal count of 2 means the deletion didn't cause any unexpected signals
|
||||
EXPECT_EQ(2, connection_state_signal_count_);
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSignalReceiving) {
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
|
||||
// Should signal receiving as soon as any channel is receiving.
|
||||
channel1->SetReceiving(true);
|
||||
EXPECT_TRUE_WAIT(receiving_, kTimeout);
|
||||
EXPECT_EQ(1, receiving_signal_count_);
|
||||
|
||||
channel2->SetReceiving(true);
|
||||
channel1->SetReceiving(false);
|
||||
channel2->SetReceiving(false);
|
||||
EXPECT_TRUE_WAIT(!receiving_, kTimeout);
|
||||
EXPECT_EQ(2, receiving_signal_count_);
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSignalGatheringStateGathering) {
|
||||
FakeTransportChannel* channel = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel);
|
||||
channel->Connect();
|
||||
channel->MaybeStartGathering();
|
||||
// Should be in the gathering state as soon as any transport starts gathering.
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(1, gathering_state_signal_count_);
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSignalGatheringStateComplete) {
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
FakeTransportChannel* channel3 = CreateChannel("data", 1);
|
||||
ASSERT_NE(nullptr, channel3);
|
||||
|
||||
channel3->Connect();
|
||||
channel3->MaybeStartGathering();
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(1, gathering_state_signal_count_);
|
||||
|
||||
// Have one channel finish gathering, then destroy it, to make sure gathering
|
||||
// completion wasn't signalled if only one transport finished gathering.
|
||||
channel3->SetCandidatesGatheringComplete();
|
||||
DestroyChannel("data", 1);
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringNew, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(2, gathering_state_signal_count_);
|
||||
|
||||
// Make remaining channels start and then finish gathering.
|
||||
channel1->Connect();
|
||||
channel1->MaybeStartGathering();
|
||||
channel2->Connect();
|
||||
channel2->MaybeStartGathering();
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(3, gathering_state_signal_count_);
|
||||
|
||||
channel1->SetCandidatesGatheringComplete();
|
||||
channel2->SetCandidatesGatheringComplete();
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(4, gathering_state_signal_count_);
|
||||
}
|
||||
|
||||
// Test that when the last transport that hasn't finished connecting and/or
|
||||
// gathering is destroyed, the aggregate state jumps to "completed". This can
|
||||
// happen if, for example, we have an audio and video transport, the audio
|
||||
// transport completes, then we start bundling video on the audio transport.
|
||||
TEST_F(TransportControllerTest,
|
||||
TestSignalingWhenLastIncompleteTransportDestroyed) {
|
||||
transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
|
||||
FakeTransportChannel* channel1 = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel1);
|
||||
FakeTransportChannel* channel2 = CreateChannel("video", 1);
|
||||
ASSERT_NE(nullptr, channel2);
|
||||
|
||||
channel1->SetCandidatesGatheringComplete();
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(1, gathering_state_signal_count_);
|
||||
|
||||
channel1->SetConnectionCount(1);
|
||||
channel1->SetWritable(true);
|
||||
DestroyChannel("video", 1);
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
|
||||
EXPECT_EQ(1, connection_state_signal_count_);
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(2, gathering_state_signal_count_);
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSignalCandidatesGathered) {
|
||||
FakeTransportChannel* channel = CreateChannel("audio", 1);
|
||||
ASSERT_NE(nullptr, channel);
|
||||
|
||||
// Transport won't signal candidates until it has a local description.
|
||||
TransportDescription local_desc(
|
||||
std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
|
||||
cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
|
||||
std::string err;
|
||||
EXPECT_TRUE(transport_controller_->SetLocalTransportDescription(
|
||||
"audio", local_desc, cricket::CA_OFFER, &err));
|
||||
transport_controller_->MaybeStartGathering();
|
||||
|
||||
channel->SignalCandidateGathered(channel, CreateCandidate(1));
|
||||
EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
|
||||
EXPECT_EQ(1U, candidates_["audio"].size());
|
||||
}
|
||||
|
||||
TEST_F(TransportControllerTest, TestSignalingOccursOnSignalingThread) {
|
||||
CreateTransportControllerWithWorkerThread();
|
||||
CreateChannelsAndCompleteConnectionOnWorkerThread();
|
||||
|
||||
// connecting --> connected --> completed
|
||||
EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
|
||||
EXPECT_EQ(2, connection_state_signal_count_);
|
||||
|
||||
EXPECT_TRUE_WAIT(receiving_, kTimeout);
|
||||
EXPECT_EQ(1, receiving_signal_count_);
|
||||
|
||||
// new --> gathering --> complete
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(2, gathering_state_signal_count_);
|
||||
|
||||
EXPECT_EQ_WAIT(1U, candidates_["audio"].size(), kTimeout);
|
||||
EXPECT_EQ_WAIT(1U, candidates_["video"].size(), kTimeout);
|
||||
EXPECT_EQ(2, candidates_signal_count_);
|
||||
|
||||
EXPECT_TRUE(!signaled_on_non_signaling_thread_);
|
||||
}
|
||||
@ -14,7 +14,6 @@
|
||||
#include "webrtc/base/helpers.h"
|
||||
#include "webrtc/base/logging.h"
|
||||
#include "webrtc/base/messagedigest.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/base/sslfingerprint.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
@ -66,8 +66,8 @@
|
||||
'base/transportchannel.cc',
|
||||
'base/transportchannel.h',
|
||||
'base/transportchannelimpl.h',
|
||||
'base/transportchannelproxy.cc',
|
||||
'base/transportchannelproxy.h',
|
||||
'base/transportcontroller.cc',
|
||||
'base/transportcontroller.h',
|
||||
'base/transportdescription.cc',
|
||||
'base/transportdescription.h',
|
||||
'base/transportdescriptionfactory.cc',
|
||||
|
||||
@ -15,13 +15,12 @@
|
||||
'direct_dependent_settings': {
|
||||
'sources': [
|
||||
'base/dtlstransportchannel_unittest.cc',
|
||||
'base/fakesession.h',
|
||||
'base/faketransportcontroller.h',
|
||||
'base/p2ptransportchannel_unittest.cc',
|
||||
'base/port_unittest.cc',
|
||||
'base/pseudotcp_unittest.cc',
|
||||
'base/relayport_unittest.cc',
|
||||
'base/relayserver_unittest.cc',
|
||||
'base/session_unittest.cc',
|
||||
'base/stun_unittest.cc',
|
||||
'base/stunport_unittest.cc',
|
||||
'base/stunrequest_unittest.cc',
|
||||
@ -30,6 +29,7 @@
|
||||
'base/teststunserver.h',
|
||||
'base/testturnserver.h',
|
||||
'base/transport_unittest.cc',
|
||||
'base/transportcontroller_unittest.cc',
|
||||
'base/transportdescriptionfactory_unittest.cc',
|
||||
'base/turnport_unittest.cc',
|
||||
'client/fakeportallocator.h',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user