Reland of TransportController refactoring. (patchset #1 id:1 of https://codereview.webrtc.org/1358413003/ )
Reason for revert: This CL just landed: https://codereview.chromium.org/1323243006/ Which fixes the FYI bots for the original CL, and breaks them for this revert. Original issue's description: > Revert of TransportController refactoring. (patchset #6 id:100001 of https://codereview.webrtc.org/1350523003/ ) > > Reason for revert: > This CL causes problems with the WebRTC-in-Chromium FYI bots. Presumably it needs to be done in several steps, where removed files are emptied instead of removed in the first step. > > Original issue's description: > > 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. > > > > Committed: https://crrev.com/47ee2f3b9f33e8938948c482c921d4e13a3acd83 > > Cr-Commit-Position: refs/heads/master@{#10022} > > TBR=pthatcher@webrtc.org,deadbeef@webrtc.org > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > > Committed: https://crrev.com/a81a42f584baa0d93a4b93da9632415e8922450c > Cr-Commit-Position: refs/heads/master@{#10024} TBR=pthatcher@webrtc.org,torbjorng@webrtc.org NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true Review URL: https://codereview.webrtc.org/1361773005 Cr-Commit-Position: refs/heads/master@{#10036}
This commit is contained in:
parent
d0b5b091e4
commit
cbecd358e0
@ -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.
|
||||
|
||||
@ -35,7 +35,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;
|
||||
|
||||
@ -104,11 +104,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);
|
||||
@ -118,15 +117,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);
|
||||
|
||||
@ -241,21 +241,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);
|
||||
|
||||
@ -31,11 +31,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 {
|
||||
|
||||
@ -57,14 +57,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();
|
||||
@ -75,7 +81,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");
|
||||
@ -90,7 +97,7 @@ class ChannelManagerTest : public testing::Test {
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete session_;
|
||||
delete transport_controller_;
|
||||
delete cm_;
|
||||
cm_ = NULL;
|
||||
fdm_ = NULL;
|
||||
@ -107,7 +114,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.
|
||||
@ -138,15 +145,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);
|
||||
@ -159,17 +167,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);
|
||||
@ -181,21 +191,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