Bug 1494301: Single API for mtransport. r=mjf

Differential Revision: https://phabricator.services.mozilla.com/D7212

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Byron Campen [:bwc] 2018-10-26 00:39:07 +00:00
parent 6f57bf69b4
commit 41541e64f6
29 changed files with 1728 additions and 1811 deletions

View File

@ -22,5 +22,60 @@ MediaPacket::Copy(const uint8_t* data, size_t len, size_t capacity)
memcpy(data_.get(), data, len);
}
static bool IsRtp(const uint8_t* data, size_t len)
{
if (len < 2)
return false;
// Check if this is a RTCP packet. Logic based on the types listed in
// media/webrtc/trunk/src/modules/rtp_rtcp/source/rtp_utility.cc
// Anything outside this range is RTP.
if ((data[1] < 192) || (data[1] > 207))
return true;
if (data[1] == 192) // FIR
return false;
if (data[1] == 193) // NACK, but could also be RTP. This makes us sad
return true; // but it's how webrtc.org behaves.
if (data[1] == 194)
return true;
if (data[1] == 195) // IJ.
return false;
if ((data[1] > 195) && (data[1] < 200)) // the > 195 is redundant
return true;
if ((data[1] >= 200) && (data[1] <= 207)) // SR, RR, SDES, BYE,
return false; // APP, RTPFB, PSFB, XR
MOZ_ASSERT(false); // Not reached, belt and suspenders.
return true;
}
void
MediaPacket::Categorize()
{
SetType(MediaPacket::UNCLASSIFIED);
if (!data_ || len_ < 4) {
return;
}
if (data_[0] >= 20 && data_[0] <= 63) {
// DTLS per RFC 7983
SetType(MediaPacket::DTLS);
} else if (data_[0] > 127 && data_[0] < 192) {
// RTP/RTCP per RFC 7983
if (IsRtp(data_.get(), len_)) {
SetType(MediaPacket::SRTP);
} else {
SetType(MediaPacket::SRTCP);
}
}
}
} // namespace mozilla

View File

@ -86,11 +86,16 @@ class MediaPacket {
enum Type {
UNCLASSIFIED,
SRTP,
SRTCP,
DTLS,
RTP,
RTCP,
SCTP
};
void Categorize();
void SetType(Type type)
{
type_ = type;

View File

@ -786,7 +786,6 @@ NrIceStats NrIceCtx::Destroy() {
}
NrIceCtx::~NrIceCtx() {
Destroy();
}
void NrIceCtx::destroy_peer_ctx() {

View File

@ -477,6 +477,10 @@ nsresult NrIceMediaStream::GetDefaultCandidate(
int component,
NrIceCandidate* candidate) const {
if (!stream_) {
return NS_ERROR_NOT_AVAILABLE;
}
nr_ice_candidate *cand;
int r = nr_ice_media_stream_get_default_candidate(stream_, component, &cand);

View File

@ -979,6 +979,7 @@ class IceTestPeer : public sigslot::has_slots<> {
}
}
ice_ctx_->Destroy();
ice_ctx_ = nullptr;
if (remote_) {

View File

@ -482,6 +482,7 @@ class TransportTestPeer : public sigslot::has_slots<> {
loopback_->Disconnect();
flow_ = nullptr;
}
ice_ctx_->Destroy();
ice_ctx_ = nullptr;
streams_.clear();
}

View File

@ -12,6 +12,8 @@
#include "transportflow.h"
#include "transportlayer.h"
#include "mozilla/DebugOnly.h"
namespace mozilla {
NS_IMPL_ISUPPORTS0(TransportFlow)
@ -26,12 +28,12 @@ TransportFlow::~TransportFlow() {
// destroy it simultaneously. The conversion to an nsAutoPtr
// ensures automatic destruction of the queue at exit of
// DestroyFinal.
if (target_) {
nsAutoPtr<std::deque<TransportLayer*>> layers_tmp(layers_.release());
RUN_ON_THREAD(target_,
WrapRunnableNM(&TransportFlow::DestroyFinal, layers_tmp),
NS_DISPATCH_NORMAL);
}
MOZ_RELEASE_ASSERT(target_);
nsAutoPtr<std::deque<TransportLayer*>> layers_tmp(layers_.release());
DebugOnly<nsresult> rv = target_->Dispatch(
WrapRunnableNM(&TransportFlow::DestroyFinal, layers_tmp),
NS_DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
void TransportFlow::DestroyFinal(nsAutoPtr<std::deque<TransportLayer *> > layers) {

View File

@ -100,6 +100,7 @@ int32_t TransportLayerNSPRAdapter::Write(const void *buf, int32_t length) {
MediaPacket packet;
// Copies. Oh well.
packet.Copy(static_cast<const uint8_t*>(buf), static_cast<size_t>(length));
packet.SetType(MediaPacket::DTLS);
TransportResult r = output_->SendPacket(packet);
if (r >= 0) {
@ -1017,8 +1018,7 @@ void TransportLayerDtls::PacketReceived(TransportLayer* layer,
return;
}
// not DTLS per RFC 7983
if (packet.data()[0] < 20 || packet.data()[0] > 63) {
if (packet.type() != MediaPacket::DTLS) {
return;
}
@ -1048,6 +1048,7 @@ TransportLayerDtls::GetDecryptedPackets()
// We have data
MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS");
MediaPacket packet;
packet.SetType(MediaPacket::SCTP);
packet.Take(std::move(buffer), static_cast<size_t>(rv));
SignalPacketReceived(this, packet);
} else if (rv == 0) {

View File

@ -125,6 +125,7 @@ void TransportLayerIce::PostSetup() {
TransportResult TransportLayerIce::SendPacket(MediaPacket& packet) {
CheckThread();
SignalPacketSending(this, packet);
nsresult res = stream_->SendPacket(component_,
packet.data(),
packet.len());
@ -182,6 +183,8 @@ void TransportLayerIce::IcePacketReceived(NrIceMediaStream *stream, int componen
// footgun though with MediaPackets that end up on the heap.
MediaPacket packet;
packet.Copy(data, len);
packet.Categorize();
SignalPacketReceived(this, packet);
}

View File

@ -50,6 +50,9 @@ class TransportLayerIce : public TransportLayer {
void IcePacketReceived(NrIceMediaStream *stream, int component,
const unsigned char *data, int len);
// Useful for capturing encrypted packets
sigslot::signal2<TransportLayer*, MediaPacket&> SignalPacketSending;
TRANSPORT_LAYER_ID("ice")
private:

View File

@ -56,40 +56,6 @@ TransportLayerSrtp::Setup()
return true;
}
static bool IsRtp(const unsigned char* data, size_t len)
{
if (len < 2)
return false;
// Check if this is a RTCP packet. Logic based on the types listed in
// media/webrtc/trunk/src/modules/rtp_rtcp/source/rtp_utility.cc
// Anything outside this range is RTP.
if ((data[1] < 192) || (data[1] > 207))
return true;
if (data[1] == 192) // FIR
return false;
if (data[1] == 193) // NACK, but could also be RTP. This makes us sad
return true; // but it's how webrtc.org behaves.
if (data[1] == 194)
return true;
if (data[1] == 195) // IJ.
return false;
if ((data[1] > 195) && (data[1] < 200)) // the > 195 is redundant
return true;
if ((data[1] >= 200) && (data[1] <= 207)) // SR, RR, SDES, BYE,
return false; // APP, RTPFB, PSFB, XR
MOZ_ASSERT(false); // Not reached, belt and suspenders.
return true;
}
TransportResult
TransportLayerSrtp::SendPacket(MediaPacket& packet)
{
@ -105,9 +71,11 @@ TransportLayerSrtp::SendPacket(MediaPacket& packet)
switch (packet.type()) {
case MediaPacket::RTP:
res = mSendSrtp->ProtectRtp(packet.data(), packet.len(), packet.capacity(), &out_len);
packet.SetType(MediaPacket::SRTP);
break;
case MediaPacket::RTCP:
res = mSendSrtp->ProtectRtcp(packet.data(), packet.len(), packet.capacity(), &out_len);
packet.SetType(MediaPacket::SRTCP);
break;
default:
MOZ_CRASH("SRTP layer asked to send packet that is neither RTP or RTCP");
@ -227,12 +195,8 @@ TransportLayerSrtp::PacketReceived(TransportLayer* layer, MediaPacket& packet)
return;
}
if (packet.len() < 4) {
return;
}
// not RTP/RTCP per RFC 7983
if (packet.data()[0] <= 127 || packet.data()[0] >= 192) {
if (packet.type() != MediaPacket::SRTP &&
packet.type() != MediaPacket::SRTCP) {
return;
}
@ -241,7 +205,7 @@ TransportLayerSrtp::PacketReceived(TransportLayer* layer, MediaPacket& packet)
int outLen;
nsresult res;
if (IsRtp(packet.data(), packet.len())) {
if (packet.type() == MediaPacket::SRTP) {
packet.SetType(MediaPacket::RTP);
res = mRecvSrtp->UnprotectRtp(packet.data(), packet.len(), packet.len(), &outLen);
} else {

View File

@ -26,6 +26,7 @@
#include "mozilla/SyncRunnable.h"
#include "mtransport_test_utils.h"
#include "SharedBuffer.h"
#include "MediaTransportHandler.h"
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
@ -164,63 +165,71 @@ public:
}
};
class TransportInfo {
class LoopbackTransport : public MediaTransportBase {
public:
TransportInfo() :
flow_(nullptr),
loopback_(nullptr) {}
LoopbackTransport()
{
SetState("mux", TransportLayer::TS_INIT, false);
SetState("mux", TransportLayer::TS_INIT, true);
SetState("non-mux", TransportLayer::TS_INIT, false);
SetState("non-mux", TransportLayer::TS_INIT, true);
}
static void InitAndConnect(TransportInfo &client, TransportInfo &server) {
client.Init(true);
server.Init(false);
static void InitAndConnect(LoopbackTransport &client, LoopbackTransport &server) {
client.Connect(&server);
server.Connect(&client);
}
void Init(bool client) {
UniquePtr<TransportLayerLoopback> loopback(new TransportLayerLoopback);
UniquePtr<TransportLayerDtls> dtls(new TransportLayerDtls);
UniquePtr<TransportLayerSrtp> srtp(new TransportLayerSrtp(*dtls));
std::vector<uint16_t> ciphers;
ciphers.push_back(kDtlsSrtpAeadAes256Gcm);
dtls->SetSrtpCiphers(ciphers);
dtls->SetIdentity(DtlsIdentity::Generate());
dtls->SetRole(client ? TransportLayerDtls::CLIENT :
TransportLayerDtls::SERVER);
dtls->SetVerificationAllowAll();
ASSERT_EQ(NS_OK, loopback->Init());
ASSERT_EQ(NS_OK, dtls->Init());
ASSERT_EQ(NS_OK, srtp->Init());
dtls->Chain(loopback.get());
srtp->Chain(loopback.get());
flow_ = new TransportFlow();
loopback_ = loopback.release();
flow_->PushLayer(loopback_);
flow_->PushLayer(dtls.release());
flow_->PushLayer(srtp.release());
}
void Connect(TransportInfo* peer) {
MOZ_ASSERT(loopback_);
MOZ_ASSERT(peer->loopback_);
loopback_->Connect(peer->loopback_);
void Connect(LoopbackTransport* peer) {
peer_ = peer;
}
void Shutdown() {
if (loopback_) {
loopback_->Disconnect();
}
loopback_ = nullptr;
flow_ = nullptr;
peer_ = nullptr;
}
RefPtr<TransportFlow> flow_;
TransportLayerLoopback *loopback_;
nsresult SendPacket(const std::string& aTransportId,
MediaPacket& aPacket) override
{
peer_->SignalPacketReceived(aTransportId, aPacket);
return NS_OK;
}
TransportLayer::State GetState(const std::string& aTransportId,
bool aRtcp) const override
{
if (aRtcp) {
auto it = mRtcpStates.find(aTransportId);
if (it != mRtcpStates.end()) {
return it->second;
}
} else {
auto it = mRtpStates.find(aTransportId);
if (it != mRtpStates.end()) {
return it->second;
}
}
return TransportLayer::TS_NONE;
}
void SetState(const std::string& aTransportId,
TransportLayer::State aState,
bool aRtcp)
{
if (aRtcp) {
mRtcpStates[aTransportId] = aState;
SignalRtcpStateChange(aTransportId, aState);
} else {
mRtpStates[aTransportId] = aState;
SignalStateChange(aTransportId, aState);
}
}
private:
RefPtr<MediaTransportBase> peer_;
std::map<std::string, TransportLayer::State> mRtpStates;
std::map<std::string, TransportLayer::State> mRtcpStates;
};
class TestAgent {
@ -229,26 +238,35 @@ class TestAgent {
audio_config_(109, "opus", 48000, 960, 2, 64000, false),
audio_conduit_(mozilla::AudioSessionConduit::Create()),
audio_pipeline_(),
use_bundle_(false) {
transport_(new LoopbackTransport) {
}
static void ConnectRtp(TestAgent *client, TestAgent *server) {
TransportInfo::InitAndConnect(client->audio_rtp_transport_,
server->audio_rtp_transport_);
static void Connect(TestAgent *client, TestAgent *server) {
LoopbackTransport::InitAndConnect(*client->transport_,
*server->transport_);
}
static void ConnectRtcp(TestAgent *client, TestAgent *server) {
TransportInfo::InitAndConnect(client->audio_rtcp_transport_,
server->audio_rtcp_transport_);
virtual void CreatePipeline(const std::string& aTransportId) = 0;
void SetState(const std::string& aTransportId,
TransportLayer::State aState,
bool aRtcp)
{
mozilla::SyncRunnable::DispatchToThread(
test_utils->sts_target(),
WrapRunnable(transport_,
&LoopbackTransport::SetState, aTransportId, aState, aRtcp));
}
static void ConnectBundle(TestAgent *client, TestAgent *server) {
TransportInfo::InitAndConnect(client->bundle_transport_,
server->bundle_transport_);
void UpdateTransport(const std::string& aTransportId,
nsAutoPtr<MediaPipelineFilter> aFilter)
{
mozilla::SyncRunnable::DispatchToThread(
test_utils->sts_target(),
WrapRunnable(audio_pipeline_,
&MediaPipeline::UpdateTransport_s, aTransportId, aFilter));
}
virtual void CreatePipeline(bool aIsRtcpMux) = 0;
void Stop() {
MOZ_MTLOG(ML_DEBUG, "Stopping");
@ -257,9 +275,7 @@ class TestAgent {
}
void Shutdown_s() {
audio_rtp_transport_.Shutdown();
audio_rtcp_transport_.Shutdown();
bundle_transport_.Shutdown();
transport_->Shutdown();
}
void Shutdown() {
@ -301,11 +317,6 @@ class TestAgent {
return audio_pipeline_->RtcpPacketsReceived();
}
void SetUsingBundle(bool use_bundle) {
use_bundle_ = use_bundle;
}
protected:
mozilla::AudioCodecConfig audio_config_;
RefPtr<mozilla::MediaSessionConduit> audio_conduit_;
@ -314,10 +325,7 @@ class TestAgent {
// both directions; only the sender's RTCP is sent, but the receiver should
// be sending it too.
RefPtr<mozilla::MediaPipeline> audio_pipeline_;
TransportInfo audio_rtp_transport_;
TransportInfo audio_rtcp_transport_;
TransportInfo bundle_transport_;
bool use_bundle_;
RefPtr<LoopbackTransport> transport_;
};
class TestAgentSend : public TestAgent {
@ -331,17 +339,14 @@ class TestAgentSend : public TestAgent {
audio_stream_track_ = new FakeAudioStreamTrack();
}
virtual void CreatePipeline(bool aIsRtcpMux) {
virtual void CreatePipeline(const std::string& aTransportId) {
std::string test_pc;
if (aIsRtcpMux) {
ASSERT_FALSE(audio_rtcp_transport_.flow_);
}
RefPtr<MediaPipelineTransmit> audio_pipeline =
new mozilla::MediaPipelineTransmit(
test_pc,
transport_,
nullptr,
test_utils->sts_target(),
false,
@ -352,16 +357,8 @@ class TestAgentSend : public TestAgent {
audio_pipeline_ = audio_pipeline;
RefPtr<TransportFlow> rtp(audio_rtp_transport_.flow_);
RefPtr<TransportFlow> rtcp(audio_rtcp_transport_.flow_);
if (use_bundle_) {
rtp = bundle_transport_.flow_;
rtcp = nullptr;
}
audio_pipeline_->UpdateTransport_m(
rtp, rtcp, nsAutoPtr<MediaPipelineFilter>(nullptr));
audio_pipeline_->UpdateTransport_m(aTransportId,
nsAutoPtr<MediaPipelineFilter>(nullptr));
}
};
@ -379,15 +376,12 @@ class TestAgentReceive : public TestAgent {
EXPECT_EQ(mozilla::kMediaConduitNoError, err);
}
virtual void CreatePipeline(bool aIsRtcpMux) {
virtual void CreatePipeline(const std::string& aTransportId) {
std::string test_pc;
if (aIsRtcpMux) {
ASSERT_FALSE(audio_rtcp_transport_.flow_);
}
audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio(
test_pc,
transport_,
nullptr,
test_utils->sts_target(),
static_cast<mozilla::AudioSessionConduit *>(audio_conduit_.get()),
@ -395,26 +389,16 @@ class TestAgentReceive : public TestAgent {
audio_pipeline_->Start();
RefPtr<TransportFlow> rtp(audio_rtp_transport_.flow_);
RefPtr<TransportFlow> rtcp(audio_rtcp_transport_.flow_);
if (use_bundle_) {
rtp = bundle_transport_.flow_;
rtcp = nullptr;
}
audio_pipeline_->UpdateTransport_m(rtp, rtcp, bundle_filter_);
audio_pipeline_->UpdateTransport_m(aTransportId, bundle_filter_);
}
void SetBundleFilter(nsAutoPtr<MediaPipelineFilter> filter) {
bundle_filter_ = filter;
}
void UpdateFilter_s(
void UpdateTransport_s(const std::string& aTransportId,
nsAutoPtr<MediaPipelineFilter> filter) {
audio_pipeline_->UpdateTransport_s(audio_rtp_transport_.flow_,
audio_rtcp_transport_.flow_,
filter);
audio_pipeline_->UpdateTransport_s(aTransportId, filter);
}
private:
@ -436,24 +420,10 @@ class MediaPipelineTest : public ::testing::Test {
}
// Setup transport.
void InitTransports(bool aIsRtcpMux) {
// RTP, p1_ is server, p2_ is client
void InitTransports() {
mozilla::SyncRunnable::DispatchToThread(
test_utils->sts_target(),
WrapRunnableNM(&TestAgent::ConnectRtp, &p2_, &p1_));
// Create RTCP flows separately if we are not muxing them.
if(!aIsRtcpMux) {
// RTCP, p1_ is server, p2_ is client
mozilla::SyncRunnable::DispatchToThread(
test_utils->sts_target(),
WrapRunnableNM(&TestAgent::ConnectRtcp, &p2_, &p1_));
}
// BUNDLE, p1_ is server, p2_ is client
mozilla::SyncRunnable::DispatchToThread(
test_utils->sts_target(),
WrapRunnableNM(&TestAgent::ConnectBundle, &p2_, &p1_));
WrapRunnableNM(&TestAgent::Connect, &p2_, &p1_));
}
// Verify RTP and RTCP
@ -473,10 +443,28 @@ class MediaPipelineTest : public ::testing::Test {
p2_.SetBundleFilter(initialFilter);
// Setup transport flows
InitTransports(aIsRtcpMux);
InitTransports();
p1_.CreatePipeline(aIsRtcpMux);
p2_.CreatePipeline(aIsRtcpMux);
std::string transportId = aIsRtcpMux ? "mux" : "non-mux";
p1_.CreatePipeline(transportId);
p2_.CreatePipeline(transportId);
// Set state of transports to CONNECTING. MediaPipeline doesn't really care
// about this transition, but we're trying to simluate what happens in a
// real case.
p1_.SetState(transportId, TransportLayer::TS_CONNECTING, false);
p1_.SetState(transportId, TransportLayer::TS_CONNECTING, true);
p2_.SetState(transportId, TransportLayer::TS_CONNECTING, false);
p2_.SetState(transportId, TransportLayer::TS_CONNECTING, true);
PR_Sleep(10);
// Set state of transports to OPEN (ie; connected). This should result in
// media flowing.
p1_.SetState(transportId, TransportLayer::TS_OPEN, false);
p1_.SetState(transportId, TransportLayer::TS_OPEN, true);
p2_.SetState(transportId, TransportLayer::TS_OPEN, false);
p2_.SetState(transportId, TransportLayer::TS_OPEN, true);
if (bundle) {
PR_Sleep(ms_until_filter_update);
@ -489,11 +477,7 @@ class MediaPipelineTest : public ::testing::Test {
refinedFilter->AddRemoteSSRC(p1_.GetLocalSSRC());
}
mozilla::SyncRunnable::DispatchToThread(
test_utils->sts_target(),
WrapRunnable(&p2_,
&TestAgentReceive::UpdateFilter_s,
refinedFilter));
p2_.UpdateTransport(transportId, refinedFilter);
}
// wait for some RTP/RTCP tx and rx to happen

View File

@ -44,13 +44,8 @@
#include "mozilla/gfx/Types.h"
#include "nsError.h"
#include "nsThreadUtils.h"
#include "nspr.h"
#include "runnable_utils.h"
#include "srtp.h"
#include "transportflow.h"
#include "transportlayer.h"
#include "transportlayerdtls.h"
#include "transportlayerice.h"
#include "signaling/src/peerconnection/MediaTransportHandler.h"
#include "Tracing.h"
#include "webrtc/base/bind.h"
@ -602,15 +597,15 @@ protected:
};
MediaPipeline::MediaPipeline(const std::string& aPc,
MediaTransportBase* aTransportHandler,
DirectionType aDirection,
nsCOMPtr<nsIEventTarget> aMainThread,
nsCOMPtr<nsIEventTarget> aStsThread,
RefPtr<MediaSessionConduit> aConduit)
: mDirection(aDirection)
, mLevel(0)
, mTransportHandler(aTransportHandler)
, mConduit(aConduit)
, mRtp(nullptr, RTP)
, mRtcp(nullptr, RTCP)
, mMainThread(aMainThread)
, mStsThread(aStsThread)
, mTransport(new PipelineTransport(aStsThread))
@ -660,74 +655,49 @@ MediaPipeline::DetachTransport_s()
("%s in %s", mDescription.c_str(), __FUNCTION__));
disconnect_all();
mRtpState = TransportLayer::TS_NONE;
mRtcpState = TransportLayer::TS_NONE;
mTransportId.clear();
mTransport->Detach();
mRtp.Detach();
mRtcp.Detach();
// Make sure any cycles are broken
mPacketDumper = nullptr;
}
nsresult
MediaPipeline::AttachTransport_s()
{
ASSERT_ON_THREAD(mStsThread);
nsresult res;
MOZ_ASSERT(mRtp.mTransport);
MOZ_ASSERT(mRtcp.mTransport);
res = ConnectTransport_s(mRtp);
if (NS_FAILED(res)) {
return res;
}
if (mRtcp.mTransport != mRtp.mTransport) {
res = ConnectTransport_s(mRtcp);
if (NS_FAILED(res)) {
return res;
}
}
mTransport->Attach(this);
return NS_OK;
}
void
MediaPipeline::UpdateTransport_m(RefPtr<TransportFlow> aRtpTransport,
RefPtr<TransportFlow> aRtcpTransport,
MediaPipeline::UpdateTransport_m(const std::string& aTransportId,
nsAutoPtr<MediaPipelineFilter> aFilter)
{
RUN_ON_THREAD(mStsThread,
WrapRunnable(RefPtr<MediaPipeline>(this),
&MediaPipeline::UpdateTransport_s,
aRtpTransport,
aRtcpTransport,
aTransportId,
aFilter),
NS_DISPATCH_NORMAL);
}
void
MediaPipeline::UpdateTransport_s(RefPtr<TransportFlow> aRtpTransport,
RefPtr<TransportFlow> aRtcpTransport,
MediaPipeline::UpdateTransport_s(const std::string& aTransportId,
nsAutoPtr<MediaPipelineFilter> aFilter)
{
bool rtcp_mux = false;
if (!aRtcpTransport) {
aRtcpTransport = aRtpTransport;
rtcp_mux = true;
ASSERT_ON_THREAD(mStsThread);
if (!mSignalsConnected) {
mTransportHandler->SignalStateChange.connect(
this, &MediaPipeline::RtpStateChange);
mTransportHandler->SignalRtcpStateChange.connect(
this, &MediaPipeline::RtcpStateChange);
mTransportHandler->SignalEncryptedSending.connect(
this, &MediaPipeline::EncryptedPacketSending);
mTransportHandler->SignalPacketReceived.connect(
this, &MediaPipeline::PacketReceived);
mSignalsConnected = true;
}
if ((aRtpTransport != mRtp.mTransport) ||
(aRtcpTransport != mRtcp.mTransport)) {
disconnect_all();
mTransport->Detach();
mRtp.Detach();
mRtcp.Detach();
if (aRtpTransport && aRtcpTransport) {
mRtp = TransportInfo(aRtpTransport, rtcp_mux ? MUX : RTP);
mRtcp = TransportInfo(aRtcpTransport, rtcp_mux ? MUX : RTCP);
AttachTransport_s();
}
if (aTransportId != mTransportId) {
mTransportId = aTransportId;
mRtpState = mTransportHandler->GetState(mTransportId, false);
mRtcpState = mTransportHandler->GetState(mTransportId, true);
CheckTransportStates();
}
if (mFilter && aFilter) {
@ -790,121 +760,80 @@ MediaPipeline::GetContributingSourceStats(
}
void
MediaPipeline::StateChange(TransportLayer* aLayer, TransportLayer::State aState)
MediaPipeline::RtpStateChange(const std::string& aTransportId,
TransportLayer::State aState)
{
TransportInfo* info = GetTransportInfo_s(aLayer);
MOZ_ASSERT(info);
if (aState == TransportLayer::TS_OPEN) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Info, ("Flow is ready"));
TransportReady_s(*info);
} else if (aState == TransportLayer::TS_CLOSED ||
aState == TransportLayer::TS_ERROR) {
TransportFailed_s(*info);
if (mTransportId != aTransportId) {
return;
}
}
static bool
MakeRtpTypeToStringArray(const char** aArray)
{
static const char* RTP_str = "RTP";
static const char* RTCP_str = "RTCP";
static const char* MUX_str = "RTP/RTCP mux";
aArray[MediaPipeline::RTP] = RTP_str;
aArray[MediaPipeline::RTCP] = RTCP_str;
aArray[MediaPipeline::MUX] = MUX_str;
return true;
}
static const char*
ToString(MediaPipeline::RtpType type)
{
static const char* array[(int)MediaPipeline::MAX_RTP_TYPE] = { nullptr };
// Dummy variable to cause init to happen only on first call
static bool dummy = MakeRtpTypeToStringArray(array);
(void)dummy;
return array[type];
}
nsresult
MediaPipeline::TransportReady_s(TransportInfo& aInfo)
{
// TODO(ekr@rtfm.com): implement some kind of notification on
// failure. bug 852665.
if (aInfo.mState != StateType::MP_CONNECTING) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Error,
("Transport ready for flow in wrong state:%s :%s",
mDescription.c_str(),
ToString(aInfo.mType)));
return NS_ERROR_FAILURE;
}
MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
("Transport ready for pipeline %p flow %s: %s",
this,
mDescription.c_str(),
ToString(aInfo.mType)));
if (mDirection == DirectionType::RECEIVE) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
("Listening for %s packets received on %p",
ToString(aInfo.mType),
aInfo.mSrtp));
aInfo.mSrtp->SignalPacketReceived.connect(
this, &MediaPipeline::PacketReceived);
}
aInfo.mState = StateType::MP_OPEN;
UpdateRtcpMuxState(aInfo);
return NS_OK;
}
nsresult
MediaPipeline::TransportFailed_s(TransportInfo& aInfo)
{
ASSERT_ON_THREAD(mStsThread);
aInfo.mState = StateType::MP_CLOSED;
UpdateRtcpMuxState(aInfo);
MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
("Transport closed for flow %s", ToString(aInfo.mType)));
NS_WARNING(
"MediaPipeline Transport failed. This is not properly cleaned up yet");
// TODO(ekr@rtfm.com): SECURITY: Figure out how to clean up if the
// connection was good and now it is bad.
// TODO(ekr@rtfm.com): Report up so that the PC knows we
// have experienced an error.
return NS_OK;
mRtpState = aState;
CheckTransportStates();
}
void
MediaPipeline::UpdateRtcpMuxState(TransportInfo& aInfo)
MediaPipeline::RtcpStateChange(const std::string& aTransportId,
TransportLayer::State aState)
{
if (aInfo.mType == MUX) {
if (aInfo.mTransport == mRtcp.mTransport) {
mRtcp.mState = aInfo.mState;
}
if (mTransportId != aTransportId) {
return;
}
mRtcpState = aState;
CheckTransportStates();
}
void
MediaPipeline::CheckTransportStates()
{
ASSERT_ON_THREAD(mStsThread);
if (mRtpState == TransportLayer::TS_CLOSED ||
mRtpState == TransportLayer::TS_ERROR ||
mRtcpState == TransportLayer::TS_CLOSED ||
mRtcpState == TransportLayer::TS_ERROR) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Warning,
("RTP Transport failed for pipeline %p flow %s",
this,
mDescription.c_str()));
NS_WARNING(
"MediaPipeline Transport failed. This is not properly cleaned up yet");
// TODO(ekr@rtfm.com): SECURITY: Figure out how to clean up if the
// connection was good and now it is bad.
// TODO(ekr@rtfm.com): Report up so that the PC knows we
// have experienced an error.
mTransport->Detach();
return;
}
if (mRtpState == TransportLayer::TS_OPEN) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
("RTP Transport ready for pipeline %p flow %s",
this,
mDescription.c_str()));
}
if (mRtcpState == TransportLayer::TS_OPEN) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
("RTCP Transport ready for pipeline %p flow %s",
this,
mDescription.c_str()));
}
if (mRtpState == TransportLayer::TS_OPEN && mRtcpState == mRtpState) {
mTransport->Attach(this);
TransportReady_s();
}
}
nsresult
MediaPipeline::SendPacket(TransportLayer* aLayer, MediaPacket& packet)
MediaPipeline::SendPacket(MediaPacket& packet)
{
ASSERT_ON_THREAD(mStsThread);
MOZ_ASSERT(mRtpState == TransportLayer::TS_OPEN);
MOZ_ASSERT(!mTransportId.empty());
nsresult rv = mTransportHandler->SendPacket(mTransportId, packet);
int len = packet.len();
TransportResult res = aLayer->SendPacket(packet);
if (res != len) {
// Ignore blocking indications
if (res == TE_WOULDBLOCK)
return NS_OK;
if (NS_FAILED(rv)) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Error,
("Failed write on stream %s", mDescription.c_str()));
return NS_BASE_STREAM_CLOSED;
@ -921,11 +850,10 @@ MediaPipeline::IncrementRtpPacketsSent(int32_t aBytes)
if (!(mRtpPacketsSent % 100)) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
("RTP sent packet count for %s Pipeline %p Flow: %p: %u (%" PRId64
("RTP sent packet count for %s Pipeline %p: %u (%" PRId64
" bytes)",
mDescription.c_str(),
this,
static_cast<void*>(mRtp.mTransport),
mRtpPacketsSent,
mRtpBytesSent));
}
@ -937,10 +865,9 @@ MediaPipeline::IncrementRtcpPacketsSent()
++mRtcpPacketsSent;
if (!(mRtcpPacketsSent % 100)) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
("RTCP sent packet count for %s Pipeline %p Flow: %p: %u",
("RTCP sent packet count for %s Pipeline %p: %u",
mDescription.c_str(),
this,
static_cast<void*>(mRtp.mTransport),
mRtcpPacketsSent));
}
}
@ -952,11 +879,10 @@ MediaPipeline::IncrementRtpPacketsReceived(int32_t aBytes)
mRtpBytesReceived += aBytes;
if (!(mRtpPacketsReceived % 100)) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
("RTP received packet count for %s Pipeline %p Flow: %p: %u (%"
("RTP received packet count for %s Pipeline %p: %u (%"
PRId64 " bytes)",
mDescription.c_str(),
this,
static_cast<void*>(mRtp.mTransport),
mRtpPacketsReceived,
mRtpBytesReceived));
}
@ -968,16 +894,15 @@ MediaPipeline::IncrementRtcpPacketsReceived()
++mRtcpPacketsReceived;
if (!(mRtcpPacketsReceived % 100)) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
("RTCP received packet count for %s Pipeline %p Flow: %p: %u",
("RTCP received packet count for %s Pipeline %p: %u",
mDescription.c_str(),
this,
static_cast<void*>(mRtp.mTransport),
mRtcpPacketsReceived));
}
}
void
MediaPipeline::RtpPacketReceived(TransportLayer* aLayer, MediaPacket& packet)
MediaPipeline::RtpPacketReceived(MediaPacket& packet)
{
if (mDirection == DirectionType::TRANSMIT) {
return;
@ -995,18 +920,6 @@ MediaPipeline::RtpPacketReceived(TransportLayer* aLayer, MediaPacket& packet)
return;
}
if (mRtp.mState != StateType::MP_OPEN) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Error,
("Discarding incoming packet; pipeline not open"));
return;
}
if (mRtp.mSrtp->state() != TransportLayer::TS_OPEN) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Error,
("Discarding incoming packet; transport not open"));
return;
}
if (!packet.len()) {
return;
}
@ -1078,7 +991,7 @@ MediaPipeline::RtpPacketReceived(TransportLayer* aLayer, MediaPacket& packet)
}
void
MediaPipeline::RtcpPacketReceived(TransportLayer* aLayer, MediaPacket& packet)
MediaPipeline::RtcpPacketReceived(MediaPacket& packet)
{
if (!mTransport->Pipeline()) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
@ -1092,18 +1005,6 @@ MediaPipeline::RtcpPacketReceived(TransportLayer* aLayer, MediaPacket& packet)
return;
}
if (mRtcp.mState != StateType::MP_OPEN) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
("Discarding incoming packet; pipeline not open"));
return;
}
if (mRtcp.mSrtp->state() != TransportLayer::TS_OPEN) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Error,
("Discarding incoming packet; transport not open"));
return;
}
if (!packet.len()) {
return;
}
@ -1131,8 +1032,13 @@ MediaPipeline::RtcpPacketReceived(TransportLayer* aLayer, MediaPacket& packet)
}
void
MediaPipeline::PacketReceived(TransportLayer* aLayer, MediaPacket& packet)
MediaPipeline::PacketReceived(const std::string& aTransportId,
MediaPacket& packet)
{
if (mTransportId != aTransportId) {
return;
}
if (!mTransport->Pipeline()) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
("Discarding incoming packet; transport disconnected"));
@ -1141,13 +1047,34 @@ MediaPipeline::PacketReceived(TransportLayer* aLayer, MediaPacket& packet)
switch (packet.type()) {
case MediaPacket::RTP:
RtpPacketReceived(aLayer, packet);
RtpPacketReceived(packet);
break;
case MediaPacket::RTCP:
RtcpPacketReceived(aLayer, packet);
RtcpPacketReceived(packet);
break;
default:
MOZ_CRASH("TransportLayerSrtp let something other than RTP/RTCP through");
;
}
}
void
MediaPipeline::EncryptedPacketSending(const std::string& aTransportId,
MediaPacket& aPacket)
{
if (mTransportId == aTransportId) {
dom::mozPacketDumpType type;
if (aPacket.type() == MediaPacket::SRTP) {
type = dom::mozPacketDumpType::Srtp;
} else if (aPacket.type() == MediaPacket::SRTCP) {
type = dom::mozPacketDumpType::Srtcp;
} else if (aPacket.type() == MediaPacket::DTLS) {
// TODO(bug 1497936): Implement packet dump for DTLS
return;
} else {
MOZ_ASSERT(false);
return;
}
mPacketDumper->Dump(Level(), type, true, aPacket.data(), aPacket.len());
}
}
@ -1273,11 +1200,13 @@ protected:
MediaPipelineTransmit::MediaPipelineTransmit(
const std::string& aPc,
MediaTransportBase* aTransportHandler,
nsCOMPtr<nsIEventTarget> aMainThread,
nsCOMPtr<nsIEventTarget> aStsThread,
bool aIsVideo,
RefPtr<MediaSessionConduit> aConduit)
: MediaPipeline(aPc,
aTransportHandler,
DirectionType::TRANSMIT,
aMainThread,
aStsThread,
@ -1468,19 +1397,13 @@ MediaPipelineTransmit::DetachMedia()
// Let the listener be destroyed with the pipeline (or later).
}
nsresult
MediaPipelineTransmit::TransportReady_s(TransportInfo& aInfo)
void
MediaPipelineTransmit::TransportReady_s()
{
ASSERT_ON_THREAD(mStsThread);
// Call base ready function.
MediaPipeline::TransportReady_s(aInfo);
// Should not be set for a transmitter
if (&aInfo == &mRtp) {
mListener->SetActive(true);
}
return NS_OK;
MediaPipeline::TransportReady_s();
mListener->SetActive(true);
}
nsresult
@ -1510,51 +1433,6 @@ MediaPipelineTransmit::SetTrack(MediaStreamTrack* aDomTrack)
return NS_OK;
}
nsresult
MediaPipeline::ConnectTransport_s(TransportInfo& aInfo)
{
MOZ_ASSERT(aInfo.mTransport);
MOZ_ASSERT(aInfo.mSrtp);
ASSERT_ON_THREAD(mStsThread);
// Look to see if the transport is ready
if (aInfo.mSrtp->state() == TransportLayer::TS_OPEN) {
nsresult res = TransportReady_s(aInfo);
if (NS_FAILED(res)) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Error,
("Error calling TransportReady(); res=%u in %s",
static_cast<uint32_t>(res),
__FUNCTION__));
return res;
}
} else if (aInfo.mSrtp->state() == TransportLayer::TS_ERROR) {
MOZ_LOG(gMediaPipelineLog, LogLevel::Error,
("%s transport is already in error state",
ToString(aInfo.mType)));
TransportFailed_s(aInfo);
return NS_ERROR_FAILURE;
}
aInfo.mSrtp->SignalStateChange.connect(this, &MediaPipeline::StateChange);
return NS_OK;
}
MediaPipeline::TransportInfo*
MediaPipeline::GetTransportInfo_s(TransportLayer* aLayer)
{
ASSERT_ON_THREAD(mStsThread);
if (aLayer == mRtp.mSrtp) {
return &mRtp;
}
if (aLayer == mRtcp.mSrtp) {
return &mRtcp;
}
return nullptr;
}
nsresult
MediaPipeline::PipelineTransport::SendRtpPacket(const uint8_t* aData, size_t aLen)
{
@ -1583,15 +1461,13 @@ MediaPipeline::PipelineTransport::SendRtpRtcpPacket_s(
return NS_OK; // Detached
}
TransportInfo& transport = isRtp ? mPipeline->mRtp : mPipeline->mRtcp;
if (transport.mSrtp->state() != TransportLayer::TS_OPEN) {
// SRTP not ready yet.
if (isRtp && mPipeline->mRtpState != TransportLayer::TS_OPEN) {
return NS_OK;
}
MOZ_ASSERT(transport.mTransport);
NS_ENSURE_TRUE(transport.mTransport, NS_ERROR_NULL_POINTER);
if (!isRtp && mPipeline->mRtcpState != TransportLayer::TS_OPEN) {
return NS_OK;
}
MediaPacket packet(std::move(*aPacket));
packet.sdp_level() = Some(mPipeline->Level());
@ -1621,7 +1497,7 @@ MediaPipeline::PipelineTransport::SendRtpRtcpPacket_s(
mPipeline->mDescription.c_str(),
(isRtp ? "RTP" : "RTCP")));
return mPipeline->SendPacket(transport.mSrtp, packet);
return mPipeline->SendPacket(packet);
}
nsresult
@ -1898,11 +1774,14 @@ protected:
Atomic<bool> mMaybeTrackNeedsUnmute;
};
MediaPipelineReceive::MediaPipelineReceive(const std::string& aPc,
nsCOMPtr<nsIEventTarget> aMainThread,
nsCOMPtr<nsIEventTarget> aStsThread,
RefPtr<MediaSessionConduit> aConduit)
MediaPipelineReceive::MediaPipelineReceive(
const std::string& aPc,
MediaTransportBase* aTransportHandler,
nsCOMPtr<nsIEventTarget> aMainThread,
nsCOMPtr<nsIEventTarget> aStsThread,
RefPtr<MediaSessionConduit> aConduit)
: MediaPipeline(aPc,
aTransportHandler,
DirectionType::RECEIVE,
aMainThread,
aStsThread,
@ -2048,11 +1927,16 @@ private:
MediaPipelineReceiveAudio::MediaPipelineReceiveAudio(
const std::string& aPc,
MediaTransportBase* aTransportHandler,
nsCOMPtr<nsIEventTarget> aMainThread,
nsCOMPtr<nsIEventTarget> aStsThread,
RefPtr<AudioSessionConduit> aConduit,
dom::MediaStreamTrack* aTrack)
: MediaPipelineReceive(aPc, aMainThread, aStsThread, aConduit)
: MediaPipelineReceive(aPc,
aTransportHandler,
aMainThread,
aStsThread,
aConduit)
, mListener(aTrack ? new PipelineListener(aTrack, mConduit) : nullptr)
{
mDescription = mPc + "| Receive audio";
@ -2236,11 +2120,16 @@ private:
MediaPipelineReceiveVideo::MediaPipelineReceiveVideo(
const std::string& aPc,
MediaTransportBase* aTransportHandler,
nsCOMPtr<nsIEventTarget> aMainThread,
nsCOMPtr<nsIEventTarget> aStsThread,
RefPtr<VideoSessionConduit> aConduit,
dom::MediaStreamTrack* aTrack)
: MediaPipelineReceive(aPc, aMainThread, aStsThread, aConduit)
: MediaPipelineReceive(aPc,
aTransportHandler,
aMainThread,
aStsThread,
aConduit)
, mRenderer(new PipelineRenderer(this))
, mListener(aTrack ? new PipelineListener(aTrack) : nullptr)
{

View File

@ -11,14 +11,14 @@
#include <map>
#include "sigslot.h"
#include "transportlayer.h" // For TransportLayer::State
#include "signaling/src/media-conduit/MediaConduitInterface.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Atomics.h"
#include "SrtpFlow.h"
#include "SrtpFlow.h" // For SRTP_MAX_EXPANSION
#include "mediapacket.h"
#include "mtransport/runnable_utils.h"
#include "mtransport/transportflow.h"
#include "AudioPacketizer.h"
#include "StreamTracks.h"
#include "signaling/src/peerconnection/PacketDumper.h"
@ -33,6 +33,7 @@ class nsIPrincipal;
namespace mozilla {
class MediaPipelineFilter;
class MediaTransportBase;
class PeerIdentity;
class AudioProxyThread;
class VideoFrameConverter;
@ -83,13 +84,8 @@ public:
TRANSMIT,
RECEIVE
};
enum class StateType
{
MP_CONNECTING,
MP_OPEN,
MP_CLOSED
};
MediaPipeline(const std::string& aPc,
MediaTransportBase* aTransportHandler,
DirectionType aDirection,
nsCOMPtr<nsIEventTarget> aMainThread,
nsCOMPtr<nsIEventTarget> aStsThread,
@ -104,12 +100,10 @@ public:
// Must be called on the main thread.
void Shutdown_m();
void UpdateTransport_m(RefPtr<TransportFlow> aRtpTransport,
RefPtr<TransportFlow> aRtcpTransport,
void UpdateTransport_m(const std::string& aTransportId,
nsAutoPtr<MediaPipelineFilter> aFilter);
void UpdateTransport_s(RefPtr<TransportFlow> aRtpTransport,
RefPtr<TransportFlow> aRtcpTransport,
void UpdateTransport_s(const std::string& aTransportId,
nsAutoPtr<MediaPipelineFilter> aFilter);
// Used only for testing; adds RTP header extension for RTP Stream Id with
@ -125,8 +119,6 @@ public:
int Level() const { return mLevel; }
virtual bool IsVideo() const = 0;
bool IsDoingRtcpMux() const { return mRtp.mType == MUX; }
class RtpCSRCStats
{
public:
@ -178,8 +170,6 @@ public:
// Thread counting
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPipeline)
typedef enum { RTP, RTCP, MUX, MAX_RTP_TYPE } RtpType;
// Separate class to allow ref counting
class PipelineTransport : public TransportInterface
{
@ -208,40 +198,10 @@ public:
protected:
virtual ~MediaPipeline();
nsresult AttachTransport_s();
friend class PipelineTransport;
struct TransportInfo
{
TransportInfo(RefPtr<TransportFlow> aFlow, RtpType aType)
: mTransport(aFlow)
, mSrtp(mTransport ? mTransport->GetLayer("srtp") : nullptr)
, mState(StateType::MP_CONNECTING)
, mType(aType)
{
}
void Detach()
{
mTransport = nullptr;
mSrtp = nullptr;
}
RefPtr<TransportFlow> mTransport;
TransportLayer* mSrtp;
StateType mState;
RtpType mType;
};
// The transport is down
virtual nsresult TransportFailed_s(TransportInfo& aInfo);
// The transport is ready
virtual nsresult TransportReady_s(TransportInfo& aInfo);
void UpdateRtcpMuxState(TransportInfo& aInfo);
nsresult ConnectTransport_s(TransportInfo& aInfo);
TransportInfo* GetTransportInfo_s(TransportLayer* aLayer);
virtual void TransportReady_s() {}
void IncrementRtpPacketsSent(int aBytes);
void IncrementRtcpPacketsSent();
@ -249,25 +209,32 @@ protected:
virtual void OnRtpPacketReceived() {};
void IncrementRtcpPacketsReceived();
virtual nsresult SendPacket(TransportLayer* aLayer,
MediaPacket& packet);
virtual nsresult SendPacket(MediaPacket& packet);
// Process slots on transports
void StateChange(TransportLayer* aLayer, TransportLayer::State);
void RtpPacketReceived(TransportLayer* aLayer, MediaPacket& packet);
void RtcpPacketReceived(TransportLayer* aLayer, MediaPacket& packet);
void PacketReceived(TransportLayer* aLayer, MediaPacket& packet);
void RtpStateChange(const std::string& aTransportId, TransportLayer::State);
void RtcpStateChange(const std::string& aTransportId, TransportLayer::State);
virtual void CheckTransportStates();
void PacketReceived(const std::string& aTransportId, MediaPacket& packet);
void RtpPacketReceived(MediaPacket& packet);
void RtcpPacketReceived(MediaPacket& packet);
void EncryptedPacketSending(const std::string& aTransportId,
MediaPacket& aPacket);
void SetDescription_s(const std::string& description);
const DirectionType mDirection;
size_t mLevel;
std::string mTransportId;
RefPtr<MediaTransportBase> mTransportHandler;
RefPtr<MediaSessionConduit> mConduit; // Our conduit. Written on the main
// thread. Read on STS thread.
// The transport objects. Read/written on STS thread.
TransportInfo mRtp;
TransportInfo mRtcp;
TransportLayer::State mRtpState = TransportLayer::TS_NONE;
TransportLayer::State mRtcpState = TransportLayer::TS_NONE;
bool mSignalsConnected = false;
// Pointers to the threads we need. Initialized at creation
// and used all over the place.
@ -278,7 +245,6 @@ protected:
RefPtr<PipelineTransport> mTransport;
// Only safe to access from STS thread.
// Build into TransportInfo?
int32_t mRtpPacketsSent;
int32_t mRtcpPacketsSent;
int32_t mRtpPacketsReceived;
@ -315,6 +281,7 @@ class MediaPipelineTransmit : public MediaPipeline
public:
// Set aRtcpTransport to nullptr to use rtcp-mux
MediaPipelineTransmit(const std::string& aPc,
MediaTransportBase* aTransportHandler,
nsCOMPtr<nsIEventTarget> aMainThread,
nsCOMPtr<nsIEventTarget> aStsThread,
bool aIsVideo,
@ -338,8 +305,8 @@ public:
// Called on the main thread.
void DetachMedia() override;
// Override MediaPipeline::TransportReady.
nsresult TransportReady_s(TransportInfo& aInfo) override;
// Override MediaPipeline::TransportReady_s.
void TransportReady_s() override;
// Replace a track with a different one
// In non-compliance with the likely final spec, allow the new
@ -373,6 +340,7 @@ class MediaPipelineReceive : public MediaPipeline
public:
// Set aRtcpTransport to nullptr to use rtcp-mux
MediaPipelineReceive(const std::string& aPc,
MediaTransportBase* aTransportHandler,
nsCOMPtr<nsIEventTarget> aMainThread,
nsCOMPtr<nsIEventTarget> aStsThread,
RefPtr<MediaSessionConduit> aConduit);
@ -392,6 +360,7 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive
{
public:
MediaPipelineReceiveAudio(const std::string& aPc,
MediaTransportBase* aTransportHandler,
nsCOMPtr<nsIEventTarget> aMainThread,
nsCOMPtr<nsIEventTarget> aStsThread,
RefPtr<AudioSessionConduit> aConduit,
@ -421,6 +390,7 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive
{
public:
MediaPipelineReceiveVideo(const std::string& aPc,
MediaTransportBase* aTransportHandler,
nsCOMPtr<nsIEventTarget> aMainThread,
nsCOMPtr<nsIEventTarget> aStsThread,
RefPtr<VideoSessionConduit> aConduit,

View File

@ -1,77 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Original author: ekr@rtfm.com
#include "TransportLayerPacketDumper.h"
#include "logging.h"
#include "nsError.h"
#include "mozilla/Assertions.h"
namespace mozilla {
MOZ_MTLOG_MODULE("mtransport")
TransportLayerPacketDumper::TransportLayerPacketDumper(
nsAutoPtr<PacketDumper>&& aPacketDumper, dom::mozPacketDumpType aType) :
mPacketDumper(std::move(aPacketDumper)),
mType(aType)
{}
void
TransportLayerPacketDumper::WasInserted()
{
CheckThread();
if (!downward_) {
MOZ_MTLOG(ML_ERROR, "Packet dumper with nothing below. This is useless");
TL_SET_STATE(TS_ERROR);
}
downward_->SignalStateChange.connect(this,
&TransportLayerPacketDumper::StateChange);
downward_->SignalPacketReceived.connect(this,
&TransportLayerPacketDumper::PacketReceived);
}
TransportResult
TransportLayerPacketDumper::SendPacket(MediaPacket& packet)
{
if (packet.sdp_level().isSome()) {
dom::mozPacketDumpType dumpType = mType;
if (mType == dom::mozPacketDumpType::Srtp &&
packet.type() == MediaPacket::RTCP) {
dumpType = dom::mozPacketDumpType::Srtcp;
}
mPacketDumper->Dump(*packet.sdp_level(),
dumpType,
true,
packet.data(),
packet.len());
}
return downward_->SendPacket(packet);
}
void
TransportLayerPacketDumper::StateChange(TransportLayer* aLayer, State aState)
{
TL_SET_STATE(aState);
}
void
TransportLayerPacketDumper::PacketReceived(TransportLayer* aLayer,
MediaPacket& packet)
{
// There's no way to know the level yet, so we can't use the packet dumper
// yet. We rely on the SRTP layer saving the encrypted packet in
// MediaPacket::encrypted_, to allow MediaPipeline to dump it later.
SignalPacketReceived(this, packet);
}
} // namespace mozilla

View File

@ -1,40 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef transportlayerpacketdumper_h__
#define transportlayerpacketdumper_h__
#include "transportlayer.h"
#include "signaling/src/peerconnection/PacketDumper.h"
#include "mozilla/dom/RTCPeerConnectionBinding.h"
namespace mozilla {
class TransportLayerPacketDumper final : public TransportLayer {
public:
explicit TransportLayerPacketDumper(nsAutoPtr<PacketDumper>&& aPacketDumper,
dom::mozPacketDumpType aType);
virtual ~TransportLayerPacketDumper() {};
// Transport layer overrides.
void WasInserted() override;
TransportResult SendPacket(MediaPacket& packet) override;
// Signals
void StateChange(TransportLayer *aLayer, State state);
void PacketReceived(TransportLayer* aLayer, MediaPacket& packet);
TRANSPORT_LAYER_ID("packet-dumper")
private:
DISALLOW_COPY_ASSIGN(TransportLayerPacketDumper);
nsAutoPtr<PacketDumper> mPacketDumper;
dom::mozPacketDumpType mType;
};
} // close namespace
#endif

View File

@ -22,7 +22,6 @@ UNIFIED_SOURCES += [
'MediaPipeline.cpp',
'MediaPipelineFilter.cpp',
'RtpLogger.cpp',
'TransportLayerPacketDumper.cpp',
]
DEFINES['TRACING'] = True

View File

@ -0,0 +1,906 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MediaTransportHandler.h"
#include "nricemediastream.h"
#include "nriceresolver.h"
#include "transportflow.h"
#include "transportlayerice.h"
#include "transportlayerdtls.h"
#include "transportlayersrtp.h"
// Config stuff
#include "nsIPrefService.h"
#include "mozilla/dom/RTCConfigurationBinding.h"
// Parsing STUN/TURN URIs
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsURLHelper.h"
#include "nsIURLParser.h"
// Logging stuff
#include "CSFLog.h"
// DTLS
#include "signaling/src/sdp/SdpAttribute.h"
#include "mozilla/Telemetry.h"
#include "mozilla/dom/RTCStatsReportBinding.h"
#include <string>
#include <vector>
namespace mozilla {
static const char* mthLogTag = "MediaTransportHandler";
#ifdef LOGTAG
#undef LOGTAG
#endif
#define LOGTAG mthLogTag
MediaTransportHandler::MediaTransportHandler()
{}
MediaTransportHandler::~MediaTransportHandler()
{}
static NrIceCtx::Policy toNrIcePolicy(dom::RTCIceTransportPolicy aPolicy)
{
switch (aPolicy) {
case dom::RTCIceTransportPolicy::Relay:
return NrIceCtx::ICE_POLICY_RELAY;
case dom::RTCIceTransportPolicy::All:
if (Preferences::GetBool("media.peerconnection.ice.no_host", false)) {
return NrIceCtx::ICE_POLICY_NO_HOST;
} else {
return NrIceCtx::ICE_POLICY_ALL;
}
default:
MOZ_CRASH();
}
return NrIceCtx::ICE_POLICY_ALL;
}
static nsresult addNrIceServer(const nsString& aIceUrl,
const dom::RTCIceServer& aIceServer,
std::vector<NrIceStunServer>* aStunServersOut,
std::vector<NrIceTurnServer>* aTurnServersOut)
{
// Without STUN/TURN handlers, NS_NewURI returns nsSimpleURI rather than
// nsStandardURL. To parse STUN/TURN URI's to spec
// http://tools.ietf.org/html/draft-nandakumar-rtcweb-stun-uri-02#section-3
// http://tools.ietf.org/html/draft-petithuguenin-behave-turn-uri-03#section-3
// we parse out the query-string, and use ParseAuthority() on the rest
RefPtr<nsIURI> url;
nsresult rv = NS_NewURI(getter_AddRefs(url), aIceUrl);
NS_ENSURE_SUCCESS(rv, rv);
bool isStun = false, isStuns = false, isTurn = false, isTurns = false;
url->SchemeIs("stun", &isStun);
url->SchemeIs("stuns", &isStuns);
url->SchemeIs("turn", &isTurn);
url->SchemeIs("turns", &isTurns);
if (!(isStun || isStuns || isTurn || isTurns)) {
return NS_ERROR_FAILURE;
}
if (isStuns) {
return NS_OK; // TODO: Support STUNS (Bug 1056934)
}
nsAutoCString spec;
rv = url->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
// TODO(jib@mozilla.com): Revisit once nsURI supports STUN/TURN (Bug 833509)
int32_t port;
nsAutoCString host;
nsAutoCString transport;
{
uint32_t hostPos;
int32_t hostLen;
nsAutoCString path;
rv = url->GetPathQueryRef(path);
NS_ENSURE_SUCCESS(rv, rv);
// Tolerate query-string + parse 'transport=[udp|tcp]' by hand.
int32_t questionmark = path.FindChar('?');
if (questionmark >= 0) {
const nsCString match = NS_LITERAL_CSTRING("transport=");
for (int32_t i = questionmark, endPos; i >= 0; i = endPos) {
endPos = path.FindCharInSet("&", i + 1);
const nsDependentCSubstring fieldvaluepair = Substring(path, i + 1,
endPos);
if (StringBeginsWith(fieldvaluepair, match)) {
transport = Substring(fieldvaluepair, match.Length());
ToLowerCase(transport);
}
}
path.SetLength(questionmark);
}
rv = net_GetAuthURLParser()->ParseAuthority(path.get(), path.Length(),
nullptr, nullptr,
nullptr, nullptr,
&hostPos, &hostLen, &port);
NS_ENSURE_SUCCESS(rv, rv);
if (!hostLen) {
return NS_ERROR_FAILURE;
}
if (hostPos > 1) /* The username was removed */
return NS_ERROR_FAILURE;
path.Mid(host, hostPos, hostLen);
}
if (port == -1)
port = (isStuns || isTurns)? 5349 : 3478;
if (isStuns || isTurns) {
// Should we barf if transport is set to udp or something?
transport = kNrIceTransportTls;
}
if (transport.IsEmpty()) {
transport = kNrIceTransportUdp;
}
if (isTurn || isTurns) {
std::string pwd(NS_ConvertUTF16toUTF8(aIceServer.mCredential.Value()).get());
std::string username(NS_ConvertUTF16toUTF8(aIceServer.mUsername.Value()).get());
std::vector<unsigned char> password(pwd.begin(), pwd.end());
UniquePtr<NrIceTurnServer> server(NrIceTurnServer::Create(host.get(), port, username, password, transport.get()));
if (!server) {
return NS_ERROR_FAILURE;
}
aTurnServersOut->emplace_back(std::move(*server));
} else {
UniquePtr<NrIceStunServer> server(NrIceStunServer::Create(host.get(), port, transport.get()));
if (!server) {
return NS_ERROR_FAILURE;
}
aStunServersOut->emplace_back(std::move(*server));
}
return NS_OK;
}
nsresult
MediaTransportHandler::Init(const std::string& aName,
const dom::RTCConfiguration& aConfiguration)
{
bool allowIceLoopback = Preferences::GetBool(
"media.peerconnection.ice.loopback", false);
bool iceTcp = Preferences::GetBool("media.peerconnection.ice.tcp", false);
bool allowIceLinkLocal = Preferences::GetBool(
"media.peerconnection.ice.link_local", false);
NrIceCtx::InitializeGlobals(allowIceLoopback, iceTcp, allowIceLinkLocal);
bool allowLoopback = Preferences::GetBool(
"media.peerconnection.ice.loopback", false);
bool tcpEnabled = Preferences::GetBool(
"media.peerconnection.ice.tcp", false);
bool allowLinkLocal = Preferences::GetBool(
"media.peerconnection.ice.link_local", false);
mIceCtx = NrIceCtx::Create(aName, allowLoopback, tcpEnabled, allowLinkLocal,
toNrIcePolicy(aConfiguration.mIceTransportPolicy));
if (!mIceCtx) {
return NS_ERROR_FAILURE;
}
mProxyOnly = Preferences::GetBool(
"media.peerconnection.ice.proxy_only", false);
mIceCtx->SignalGatheringStateChange.connect(
this, &MediaTransportHandler::OnGatheringStateChange);
mIceCtx->SignalConnectionStateChange.connect(
this, &MediaTransportHandler::OnConnectionStateChange);
std::vector<NrIceStunServer> stunServers;
std::vector<NrIceTurnServer> turnServers;
nsresult rv;
if (aConfiguration.mIceServers.WasPassed()) {
for (const auto& iceServer : aConfiguration.mIceServers.Value()) {
NS_ENSURE_STATE(iceServer.mUrls.WasPassed());
NS_ENSURE_STATE(iceServer.mUrls.Value().IsStringSequence());
for (const auto& iceUrl : iceServer.mUrls.Value().GetAsStringSequence()) {
rv = addNrIceServer(iceUrl, iceServer, &stunServers, &turnServers);
if (NS_FAILED(rv)) {
CSFLogError(LOGTAG, "%s: invalid STUN/TURN server: %s",
__FUNCTION__, NS_ConvertUTF16toUTF8(iceUrl).get());
return rv;
}
}
}
}
if (NS_FAILED(rv = mIceCtx->SetStunServers(stunServers))) {
CSFLogError(LOGTAG, "%s: Failed to set stun servers", __FUNCTION__);
return rv;
}
// Give us a way to globally turn off TURN support
bool disabled = Preferences::GetBool("media.peerconnection.turn.disable", false);
if (!disabled) {
if (NS_FAILED(rv = mIceCtx->SetTurnServers(turnServers))) {
CSFLogError(LOGTAG, "%s: Failed to set turn servers", __FUNCTION__);
return rv;
}
} else if (!turnServers.empty()) {
CSFLogError(LOGTAG, "%s: Setting turn servers disabled", __FUNCTION__);
}
mDNSResolver = new NrIceResolver;
if (NS_FAILED(rv = mDNSResolver->Init())) {
CSFLogError(LOGTAG, "%s: Failed to initialize dns resolver", __FUNCTION__);
return rv;
}
if (NS_FAILED(rv = mIceCtx->SetResolver(mDNSResolver->AllocateResolver()))) {
CSFLogError(LOGTAG, "%s: Failed to get dns resolver", __FUNCTION__);
return rv;
}
return NS_OK;
}
void
MediaTransportHandler::Destroy()
{
disconnect_all();
NrIceStats stats = mIceCtx->Destroy();
CSFLogDebug(LOGTAG, "Ice Telemetry: stun (retransmits: %d)"
" turn (401s: %d 403s: %d 438s: %d)",
stats.stun_retransmits, stats.turn_401s, stats.turn_403s,
stats.turn_438s);
Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_STUN_RETRANSMITS,
stats.stun_retransmits);
Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_401S,
stats.turn_401s);
Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_403S,
stats.turn_403s);
Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_438S,
stats.turn_438s);
}
nsresult
MediaTransportHandler::SetProxyServer(const std::string& aProxyHost,
uint16_t aProxyPort,
const std::string& aAlpnProtocols)
{
NrIceProxyServer proxyServer(aProxyHost, aProxyPort, aAlpnProtocols);
return mIceCtx->SetProxyServer(proxyServer);
}
void
MediaTransportHandler::EnsureProvisionalTransport(
const std::string& aTransportId,
const std::string& aUfrag,
const std::string& aPwd,
size_t aComponentCount)
{
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId));
if (!stream) {
CSFLogDebug(LOGTAG, "%s: Creating ICE media stream=%s components=%u",
mIceCtx->name().c_str(),
aTransportId.c_str(),
static_cast<unsigned>(aComponentCount));
std::ostringstream os;
os << mIceCtx->name() << " transport-id=" << aTransportId;
stream = mIceCtx->CreateStream(aTransportId,
os.str(),
aComponentCount);
if (!stream) {
CSFLogError(LOGTAG, "Failed to create ICE stream.");
return;
}
stream->SignalCandidate.connect(this,
&MediaTransportHandler::OnCandidateFound);
}
// Begins an ICE restart if this stream has a different ufrag/pwd
stream->SetIceCredentials(aUfrag, aPwd);
// Make sure there's an entry in mTransports
mTransports[aTransportId];
}
nsresult
MediaTransportHandler::ActivateTransport(
const std::string& aTransportId,
const std::string& aLocalUfrag,
const std::string& aLocalPwd,
size_t aComponentCount,
const std::string& aUfrag,
const std::string& aPassword,
const std::vector<std::string>& aCandidateList,
RefPtr<DtlsIdentity> aDtlsIdentity,
bool aDtlsClient,
const SdpFingerprintAttributeList& aFingerprints,
bool aPrivacyRequested)
{
MOZ_ASSERT(aComponentCount);
MOZ_ASSERT(aDtlsIdentity);
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId));
if (!stream) {
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
CSFLogDebug(LOGTAG, "%s: Activating ICE media stream=%s components=%u",
mIceCtx->name().c_str(),
aTransportId.c_str(),
static_cast<unsigned>(aComponentCount));
std::vector<std::string> attrs;
attrs.reserve(aCandidateList.size() + 2 /* ufrag + pwd */);
for (const auto& candidate : aCandidateList) {
attrs.push_back("candidate:" + candidate);
}
attrs.push_back("ice-ufrag:" + aUfrag);
attrs.push_back("ice-pwd:" + aPassword);
// If we started an ICE restart in EnsureProvisionalTransport, this is where
// we decide whether to commit or rollback.
nsresult rv = stream->ConnectToPeer(aLocalUfrag, aLocalPwd, attrs);
if (NS_FAILED(rv)) {
CSFLogError(LOGTAG, "Couldn't parse ICE attributes, rv=%u",
static_cast<unsigned>(rv));
MOZ_ASSERT(false);
return rv;
}
Transport transport = mTransports[aTransportId];
if (!transport.mFlow) {
transport.mFlow = CreateTransportFlow(aTransportId, false, aDtlsIdentity,
aDtlsClient, aFingerprints, aPrivacyRequested);
if (!transport.mFlow) {
return NS_ERROR_FAILURE;
}
TransportLayer* dtls = transport.mFlow->GetLayer(TransportLayerDtls::ID());
dtls->SignalStateChange.connect(
this, &MediaTransportHandler::OnStateChange);
if (aComponentCount < 2) {
dtls->SignalStateChange.connect(
this, &MediaTransportHandler::OnRtcpStateChange);
}
}
if (aComponentCount == 2) {
if (!transport.mRtcpFlow) {
transport.mRtcpFlow = CreateTransportFlow(aTransportId, true,
aDtlsIdentity, aDtlsClient, aFingerprints, aPrivacyRequested);
if (!transport.mRtcpFlow) {
return NS_ERROR_FAILURE;
}
TransportLayer* dtls = transport.mRtcpFlow->GetLayer(
TransportLayerDtls::ID());
dtls->SignalStateChange.connect(
this, &MediaTransportHandler::OnRtcpStateChange);
}
} else {
transport.mRtcpFlow = nullptr;
// components are 1-indexed
stream->DisableComponent(2);
}
mTransports[aTransportId] = transport;
return NS_OK;
}
void
MediaTransportHandler::StartIceGathering(
bool aDefaultRouteOnly,
const nsTArray<NrIceStunAddr>& aStunAddrs)
{
// Belt and suspenders - in e10s mode, the call below to SetStunAddrs
// needs to have the proper flags set on ice ctx. For non-e10s,
// setting those flags happens in StartGathering. We could probably
// just set them here, and only do it here.
mIceCtx->SetCtxFlags(aDefaultRouteOnly, mProxyOnly);
if (aStunAddrs.Length()) {
mIceCtx->SetStunAddrs(aStunAddrs);
}
// Start gathering, but only if there are streams
if (!mIceCtx->GetStreams().empty()) {
mIceCtx->StartGathering(aDefaultRouteOnly, mProxyOnly);
return;
}
CSFLogWarn(LOGTAG,
"%s: No streams to start gathering on. Can happen with rollback",
__FUNCTION__);
// If there are no streams, we're probably in a situation where we've rolled
// back while still waiting for our proxy configuration to come back. Make
// sure content knows that the rollback has stuck wrt gathering.
SignalGatheringStateChange(dom::PCImplIceGatheringState::Complete);
}
nsresult
MediaTransportHandler::StartIceChecks(
bool aIsControlling,
bool aIsOfferer,
const std::vector<std::string>& aIceOptions)
{
nsresult rv = mIceCtx->ParseGlobalAttributes(aIceOptions);
if (NS_FAILED(rv)) {
CSFLogError(LOGTAG, "%s: couldn't parse global parameters", __FUNCTION__ );
return rv;
}
rv = mIceCtx->SetControlling(aIsControlling ? NrIceCtx::ICE_CONTROLLING :
NrIceCtx::ICE_CONTROLLED);
if (NS_FAILED(rv)) {
CSFLogError(LOGTAG, "%s: couldn't set controlling to %d",
__FUNCTION__, aIsControlling);
return rv;
}
rv = mIceCtx->StartChecks(aIsOfferer);
if (NS_FAILED(rv)) {
CSFLogError(LOGTAG, "%s: couldn't start checks", __FUNCTION__);
return rv;
}
return NS_OK;
}
nsresult
MediaTransportHandler::AddIceCandidate(const std::string& aTransportId,
const std::string& aCandidate)
{
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId));
if (!stream) {
CSFLogError(LOGTAG, "No ICE stream for candidate with transport id %s: %s",
aTransportId.c_str(), aCandidate.c_str());
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv = stream->ParseTrickleCandidate(aCandidate);
if (NS_FAILED(rv)) {
CSFLogError(LOGTAG, "Couldn't process ICE candidate with transport id %s: "
"%s",
aTransportId.c_str(), aCandidate.c_str());
return NS_ERROR_FAILURE;
}
return NS_OK;
}
void
MediaTransportHandler::UpdateNetworkState(bool aOnline)
{
mIceCtx->UpdateNetworkState(aOnline);
}
void
MediaTransportHandler::RemoveTransportsExcept(
const std::set<std::string>& aTransportIds)
{
for (auto it = mTransports.begin(); it != mTransports.end();) {
if (!aTransportIds.count(it->first)) {
if (it->second.mFlow) {
SignalStateChange(it->first, TransportLayer::TS_NONE);
SignalRtcpStateChange(it->first, TransportLayer::TS_NONE);
}
mIceCtx->DestroyStream(it->first);
it = mTransports.erase(it);
} else {
MOZ_ASSERT(it->second.mFlow);
++it;
}
}
}
nsresult
MediaTransportHandler::SendPacket(const std::string& aTransportId,
MediaPacket& aPacket)
{
MOZ_ASSERT(aPacket.type() != MediaPacket::UNCLASSIFIED);
RefPtr<TransportFlow> flow = GetTransportFlow(
aTransportId, aPacket.type() == MediaPacket::RTCP);
if (!flow) {
CSFLogError(LOGTAG, "%s: No such transport flow (%s) for outgoing packet",
mIceCtx->name().c_str(), aTransportId.c_str());
MOZ_ASSERT(false);
return NS_ERROR_NOT_AVAILABLE;
}
TransportLayer* layer = nullptr;
switch (aPacket.type()) {
case MediaPacket::SCTP:
layer = flow->GetLayer(TransportLayerDtls::ID());
break;
case MediaPacket::RTP:
case MediaPacket::RTCP:
layer = flow->GetLayer(TransportLayerSrtp::ID());
break;
default:
// Maybe it would be useful to allow the injection of other packet types
// for testing?
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(layer);
if (layer->SendPacket(aPacket) < 0) {
CSFLogError(LOGTAG, "%s: Transport flow (%s) failed to send packet",
mIceCtx->name().c_str(), aTransportId.c_str());
return NS_ERROR_FAILURE;
}
return NS_OK;
}
TransportLayer::State
MediaTransportHandler::GetState(const std::string& aTransportId,
bool aRtcp) const
{
RefPtr<TransportFlow> flow = GetTransportFlow(aTransportId, aRtcp);
if (flow) {
return flow->GetLayer(TransportLayerDtls::ID())->state();
}
return TransportLayer::TS_NONE;
}
void
MediaTransportHandler::GetAllIceStats(bool aInternalStats,
DOMHighResTimeStamp aNow,
dom::RTCStatsReportInternal* aReport)
{
for (const auto& stream : mIceCtx->GetStreams()) {
GetIceStats(*stream, aInternalStats, aNow, aReport);
}
}
void
MediaTransportHandler::GetIceStats(const std::string& aTransportId,
bool aInternalStats,
DOMHighResTimeStamp aNow,
dom::RTCStatsReportInternal* aReport)
{
auto stream = mIceCtx->GetStream(aTransportId);
if (stream) {
GetIceStats(*stream, aInternalStats, aNow, aReport);
}
}
static void ToRTCIceCandidateStats(
const std::vector<NrIceCandidate>& candidates,
dom::RTCStatsType candidateType,
const nsString& componentId,
DOMHighResTimeStamp now,
dom::RTCStatsReportInternal* report) {
MOZ_ASSERT(report);
for (const auto& candidate : candidates) {
dom::RTCIceCandidateStats cand;
cand.mType.Construct(candidateType);
NS_ConvertASCIItoUTF16 codeword(candidate.codeword.c_str());
cand.mComponentId.Construct(componentId);
cand.mId.Construct(codeword);
cand.mTimestamp.Construct(now);
cand.mCandidateType.Construct(
dom::RTCStatsIceCandidateType(candidate.type));
cand.mIpAddress.Construct(
NS_ConvertASCIItoUTF16(candidate.cand_addr.host.c_str()));
cand.mPortNumber.Construct(candidate.cand_addr.port);
cand.mTransport.Construct(
NS_ConvertASCIItoUTF16(candidate.cand_addr.transport.c_str()));
if (candidateType == dom::RTCStatsType::Local_candidate) {
cand.mMozLocalTransport.Construct(
NS_ConvertASCIItoUTF16(candidate.local_addr.transport.c_str()));
if (dom::RTCStatsIceCandidateType(candidate.type) ==
dom::RTCStatsIceCandidateType::Relayed) {
cand.mRelayProtocol.Construct(
NS_ConvertASCIItoUTF16(candidate.local_addr.transport.c_str()));
}
}
report->mIceCandidateStats.Value().AppendElement(cand, fallible);
if (candidate.trickled) {
report->mTrickledIceCandidateStats.Value().AppendElement(cand, fallible);
}
}
}
void
MediaTransportHandler::GetIceStats(
const NrIceMediaStream& aStream,
bool aInternalStats,
DOMHighResTimeStamp aNow,
dom::RTCStatsReportInternal* aReport) const
{
NS_ConvertASCIItoUTF16 transportId(aStream.GetId().c_str());
std::vector<NrIceCandidatePair> candPairs;
nsresult res = aStream.GetCandidatePairs(&candPairs);
if (NS_FAILED(res)) {
CSFLogError(LOGTAG,
"%s: Error getting candidate pairs for transport id \"%s\"",
__FUNCTION__, aStream.GetId().c_str());
return;
}
for (auto& candPair : candPairs) {
NS_ConvertASCIItoUTF16 codeword(candPair.codeword.c_str());
NS_ConvertASCIItoUTF16 localCodeword(candPair.local.codeword.c_str());
NS_ConvertASCIItoUTF16 remoteCodeword(candPair.remote.codeword.c_str());
// Only expose candidate-pair statistics to chrome, until we've thought
// through the implications of exposing it to content.
dom::RTCIceCandidatePairStats s;
s.mId.Construct(codeword);
s.mTransportId.Construct(transportId);
s.mTimestamp.Construct(aNow);
s.mType.Construct(dom::RTCStatsType::Candidate_pair);
s.mLocalCandidateId.Construct(localCodeword);
s.mRemoteCandidateId.Construct(remoteCodeword);
s.mNominated.Construct(candPair.nominated);
s.mWritable.Construct(candPair.writable);
s.mReadable.Construct(candPair.readable);
s.mPriority.Construct(candPair.priority);
s.mSelected.Construct(candPair.selected);
s.mBytesSent.Construct(candPair.bytes_sent);
s.mBytesReceived.Construct(candPair.bytes_recvd);
s.mLastPacketSentTimestamp.Construct(candPair.ms_since_last_send);
s.mLastPacketReceivedTimestamp.Construct(candPair.ms_since_last_recv);
s.mState.Construct(dom::RTCStatsIceCandidatePairState(candPair.state));
s.mComponentId.Construct(candPair.component_id);
aReport->mIceCandidatePairStats.Value().AppendElement(s, fallible);
}
std::vector<NrIceCandidate> candidates;
if (NS_SUCCEEDED(aStream.GetLocalCandidates(&candidates))) {
ToRTCIceCandidateStats(candidates,
dom::RTCStatsType::Local_candidate,
transportId,
aNow,
aReport);
// add the local candidates unparsed string to a sequence
for (const auto& candidate : candidates) {
aReport->mRawLocalCandidates.Value().AppendElement(
NS_ConvertASCIItoUTF16(candidate.label.c_str()), fallible);
}
}
candidates.clear();
if (NS_SUCCEEDED(aStream.GetRemoteCandidates(&candidates))) {
ToRTCIceCandidateStats(candidates,
dom::RTCStatsType::Remote_candidate,
transportId,
aNow,
aReport);
// add the remote candidates unparsed string to a sequence
for (const auto& candidate : candidates) {
aReport->mRawRemoteCandidates.Value().AppendElement(
NS_ConvertASCIItoUTF16(candidate.label.c_str()), fallible);
}
}
}
RefPtr<TransportFlow>
MediaTransportHandler::GetTransportFlow(const std::string& aTransportId,
bool aIsRtcp) const
{
auto it = mTransports.find(aTransportId);
if (it == mTransports.end()) {
return nullptr;
}
if (aIsRtcp) {
return it->second.mRtcpFlow ? it->second.mRtcpFlow : it->second.mFlow;;
}
return it->second.mFlow;
}
RefPtr<TransportFlow>
MediaTransportHandler::CreateTransportFlow(
const std::string& aTransportId,
bool aIsRtcp,
RefPtr<DtlsIdentity> aDtlsIdentity,
bool aDtlsClient,
const SdpFingerprintAttributeList& aFingerprints,
bool aPrivacyRequested)
{
nsresult rv;
RefPtr<TransportFlow> flow = new TransportFlow(aTransportId);
// The media streams are made on STS so we need to defer setup.
auto ice = MakeUnique<TransportLayerIce>();
auto dtls = MakeUnique<TransportLayerDtls>();
auto srtp = MakeUnique<TransportLayerSrtp>(*dtls);
dtls->SetRole(aDtlsClient
? TransportLayerDtls::CLIENT
: TransportLayerDtls::SERVER);
dtls->SetIdentity(aDtlsIdentity);
for (const auto& fingerprint : aFingerprints.mFingerprints) {
std::ostringstream ss;
ss << fingerprint.hashFunc;
rv = dtls->SetVerificationDigest(ss.str(), &fingerprint.fingerprint[0],
fingerprint.fingerprint.size());
if (NS_FAILED(rv)) {
CSFLogError(LOGTAG, "Could not set fingerprint");
return nullptr;
}
}
std::vector<uint16_t> srtpCiphers = TransportLayerDtls::GetDefaultSrtpCiphers();
rv = dtls->SetSrtpCiphers(srtpCiphers);
if (NS_FAILED(rv)) {
CSFLogError(LOGTAG, "Couldn't set SRTP ciphers");
return nullptr;
}
// Always permits negotiation of the confidential mode.
// Only allow non-confidential (which is an allowed default),
// if we aren't confidential.
std::set<std::string> alpn;
std::string alpnDefault = "";
alpn.insert("c-webrtc");
if (!aPrivacyRequested) {
alpnDefault = "webrtc";
alpn.insert(alpnDefault);
}
rv = dtls->SetAlpn(alpn, alpnDefault);
if (NS_FAILED(rv)) {
CSFLogError(LOGTAG, "Couldn't set ALPN");
return nullptr;
}
ice->SetParameters(mIceCtx->GetStream(aTransportId), aIsRtcp ? 2 : 1);
NS_ENSURE_SUCCESS(ice->Init(), nullptr);
NS_ENSURE_SUCCESS(dtls->Init(), nullptr);
NS_ENSURE_SUCCESS(srtp->Init(), nullptr);
dtls->Chain(ice.get());
srtp->Chain(ice.get());
dtls->SignalPacketReceived.connect(
this, &MediaTransportHandler::PacketReceived);
srtp->SignalPacketReceived.connect(
this, &MediaTransportHandler::PacketReceived);
ice->SignalPacketSending.connect(
this, &MediaTransportHandler::EncryptedPacketSending);
flow->PushLayer(ice.release());
flow->PushLayer(dtls.release());
flow->PushLayer(srtp.release());
return flow;
}
static mozilla::dom::PCImplIceGatheringState
toDomIceGatheringState(NrIceCtx::GatheringState aState)
{
switch (aState) {
case NrIceCtx::ICE_CTX_GATHER_INIT:
return dom::PCImplIceGatheringState::New;
case NrIceCtx::ICE_CTX_GATHER_STARTED:
return dom::PCImplIceGatheringState::Gathering;
case NrIceCtx::ICE_CTX_GATHER_COMPLETE:
return dom::PCImplIceGatheringState::Complete;
}
MOZ_CRASH();
}
void
MediaTransportHandler::OnGatheringStateChange(NrIceCtx* aIceCtx,
NrIceCtx::GatheringState aState)
{
if (aState == NrIceCtx::ICE_CTX_GATHER_COMPLETE) {
for (const auto& stream : mIceCtx->GetStreams()) {
OnCandidateFound(stream, "");
}
}
SignalGatheringStateChange(toDomIceGatheringState(aState));
}
static mozilla::dom::PCImplIceConnectionState
toDomIceConnectionState(NrIceCtx::ConnectionState aState)
{
switch (aState) {
case NrIceCtx::ICE_CTX_INIT:
return dom::PCImplIceConnectionState::New;
case NrIceCtx::ICE_CTX_CHECKING:
return dom::PCImplIceConnectionState::Checking;
case NrIceCtx::ICE_CTX_CONNECTED:
return dom::PCImplIceConnectionState::Connected;
case NrIceCtx::ICE_CTX_COMPLETED:
return dom::PCImplIceConnectionState::Completed;
case NrIceCtx::ICE_CTX_FAILED:
return dom::PCImplIceConnectionState::Failed;
case NrIceCtx::ICE_CTX_DISCONNECTED:
return dom::PCImplIceConnectionState::Disconnected;
case NrIceCtx::ICE_CTX_CLOSED:
return dom::PCImplIceConnectionState::Closed;
}
MOZ_CRASH();
}
void
MediaTransportHandler::OnConnectionStateChange(NrIceCtx* aIceCtx,
NrIceCtx::ConnectionState aState)
{
SignalConnectionStateChange(toDomIceConnectionState(aState));
}
// The stuff below here will eventually go into the MediaTransportChild class
void
MediaTransportHandler::OnCandidateFound(NrIceMediaStream* aStream,
const std::string& aCandidate)
{
CandidateInfo info;
info.mCandidate = aCandidate;
NrIceCandidate defaultRtpCandidate;
NrIceCandidate defaultRtcpCandidate;
nsresult rv = aStream->GetDefaultCandidate(1, &defaultRtpCandidate);
if (NS_SUCCEEDED(rv)) {
info.mDefaultHostRtp = defaultRtpCandidate.cand_addr.host;
info.mDefaultPortRtp = defaultRtpCandidate.cand_addr.port;
} else {
CSFLogError(LOGTAG, "%s: GetDefaultCandidates failed for transport id %s, "
"res=%u",
__FUNCTION__,
aStream->GetId().c_str(),
static_cast<unsigned>(rv));
}
// Optional; component won't exist if doing rtcp-mux
if (NS_SUCCEEDED(aStream->GetDefaultCandidate(2, &defaultRtcpCandidate))) {
info.mDefaultHostRtcp = defaultRtcpCandidate.cand_addr.host;
info.mDefaultPortRtcp = defaultRtcpCandidate.cand_addr.port;
}
SignalCandidate(aStream->GetId(), info);
}
void
MediaTransportHandler::OnStateChange(TransportLayer* aLayer,
TransportLayer::State aState)
{
if (aState == TransportLayer::TS_OPEN) {
MOZ_ASSERT(aLayer->id() == TransportLayerDtls::ID());
TransportLayerDtls* dtlsLayer = static_cast<TransportLayerDtls*>(aLayer);
SignalAlpnNegotiated(dtlsLayer->GetNegotiatedAlpn());
}
// DTLS state indicates the readiness of the transport as a whole, because
// SRTP uses the keys from the DTLS handshake.
SignalStateChange(aLayer->flow_id(), aState);
}
void
MediaTransportHandler::OnRtcpStateChange(TransportLayer* aLayer,
TransportLayer::State aState)
{
SignalRtcpStateChange(aLayer->flow_id(), aState);
}
void
MediaTransportHandler::PacketReceived(TransportLayer* aLayer,
MediaPacket& aPacket)
{
SignalPacketReceived(aLayer->flow_id(), aPacket);
}
void
MediaTransportHandler::EncryptedPacketSending(TransportLayer* aLayer,
MediaPacket& aPacket)
{
SignalEncryptedSending(aLayer->flow_id(), aPacket);
}
} // namespace mozilla

View File

@ -0,0 +1,183 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _MTRANSPORTHANDLER_H__
#define _MTRANSPORTHANDLER_H__
#include "mozilla/RefPtr.h"
#include "nsISupportsImpl.h"
#include "sigslot.h"
#include "transportlayer.h" // Need the State enum
#include "mozilla/dom/PeerConnectionImplEnumsBinding.h"
#include "nricectx.h" // Need some enums
#include "nsDOMNavigationTiming.h" // DOMHighResTimeStamp
#include <map>
#include <string>
#include <set>
#include <vector>
namespace mozilla {
class DtlsIdentity; // TODO(bug 1494311) Use IPC type
class NrIceCtx;
class NrIceMediaStream;
class NrIceResolver;
class SdpFingerprintAttributeList; // TODO(bug 1494311) Use IPC type
class TransportFlow;
namespace dom {
struct RTCConfiguration;
struct RTCStatsReportInternal;
}
// Base-class, makes some testing easier
class MediaTransportBase {
public:
virtual nsresult SendPacket(const std::string& aTransportId,
MediaPacket& aPacket) = 0;
virtual TransportLayer::State GetState(const std::string& aTransportId,
bool aRtcp) const = 0;
sigslot::signal2<const std::string&, MediaPacket&> SignalPacketReceived;
sigslot::signal2<const std::string&, MediaPacket&> SignalEncryptedSending;
sigslot::signal2<const std::string&, TransportLayer::State>
SignalStateChange;
sigslot::signal2<const std::string&, TransportLayer::State>
SignalRtcpStateChange;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaTransportBase)
protected:
virtual ~MediaTransportBase() {}
};
class MediaTransportHandler : public MediaTransportBase,
public sigslot::has_slots<> {
public:
MediaTransportHandler();
nsresult Init(const std::string& aName,
const dom::RTCConfiguration& aConfiguration);
void Destroy();
// We will probably be able to move the proxy lookup stuff into
// this class once we move mtransport to its own process.
nsresult SetProxyServer(const std::string& aProxyHost,
uint16_t aProxyPort,
const std::string& aAlpnProtocols);
void EnsureProvisionalTransport(const std::string& aTransportId,
const std::string& aLocalUfrag,
const std::string& aLocalPwd,
size_t aComponentCount);
// We set default-route-only as late as possible because it depends on what
// capture permissions have been granted on the window, which could easily
// change between Init (ie; when the PC is created) and StartIceGathering
// (ie; when we set the local description).
void StartIceGathering(bool aDefaultRouteOnly,
// This will go away once mtransport moves to its
// own process, because we won't need to get this
// via IPC anymore
const nsTArray<NrIceStunAddr>& aStunAddrs);
nsresult ActivateTransport(const std::string& aTransportId,
const std::string& aLocalUfrag,
const std::string& aLocalPwd,
size_t aComponentCount,
const std::string& aUfrag,
const std::string& aPassword,
const std::vector<std::string>& aCandidateList,
// TODO(bug 1494311): Use an IPC type.
RefPtr<DtlsIdentity> aDtlsIdentity,
bool aDtlsClient,
// TODO(bug 1494311): Use IPC type
const SdpFingerprintAttributeList& aFingerprints,
bool aPrivacyRequested);
void RemoveTransportsExcept(const std::set<std::string>& aTransportIds);
nsresult StartIceChecks(bool aIsControlling,
bool aIsOfferer,
const std::vector<std::string>& aIceOptions);
nsresult AddIceCandidate(const std::string& aTransportId,
const std::string& aCandidate);
void UpdateNetworkState(bool aOnline);
nsresult SendPacket(const std::string& aTransportId,
MediaPacket& aPacket) override;
// TODO(bug 1494312): Figure out how this fits with an async API. Maybe we
// cache on the content process.
TransportLayer::State GetState(const std::string& aTransportId,
bool aRtcp) const override;
// TODO(bug 1494312): Stats stuff needs to be async.
void GetAllIceStats(bool internalStats,
DOMHighResTimeStamp now,
dom::RTCStatsReportInternal* report);
// TODO(bug 1494312): Stats stuff needs to be async.
void GetIceStats(const std::string& aTransportId,
bool internalStats,
DOMHighResTimeStamp now,
dom::RTCStatsReportInternal* report);
// TODO(bug 1494311) Use IPC type
struct CandidateInfo {
std::string mCandidate;
std::string mDefaultHostRtp;
uint16_t mDefaultPortRtp = 0;
std::string mDefaultHostRtcp;
uint16_t mDefaultPortRtcp = 0;
};
sigslot::signal2<const std::string&, const CandidateInfo&> SignalCandidate;
sigslot::signal1<const std::string&> SignalAlpnNegotiated;
sigslot::signal1<dom::PCImplIceGatheringState> SignalGatheringStateChange;
sigslot::signal1<dom::PCImplIceConnectionState> SignalConnectionStateChange;
private:
RefPtr<TransportFlow> CreateTransportFlow(
const std::string& aTransportId,
bool aIsRtcp,
RefPtr<DtlsIdentity> aDtlsIdentity,
bool aDtlsClient,
// TODO(bug 1494312) Use IPC type
const SdpFingerprintAttributeList& aFingerprints,
bool aPrivacyRequested);
struct Transport {
RefPtr<TransportFlow> mFlow;
RefPtr<TransportFlow> mRtcpFlow;
};
void OnGatheringStateChange(NrIceCtx* aIceCtx,
NrIceCtx::GatheringState aState);
void OnConnectionStateChange(NrIceCtx* aIceCtx,
NrIceCtx::ConnectionState aState);
void OnCandidateFound(NrIceMediaStream* aStream,
const std::string& aCandidate);
void OnStateChange(TransportLayer* aLayer, TransportLayer::State);
void OnRtcpStateChange(TransportLayer* aLayer, TransportLayer::State);
void PacketReceived(TransportLayer* aLayer, MediaPacket& aPacket);
void EncryptedPacketSending(TransportLayer* aLayer, MediaPacket& aPacket);
RefPtr<TransportFlow> GetTransportFlow(const std::string& aId,
bool aIsRtcp) const;
void GetIceStats(const NrIceMediaStream& aStream,
bool aInternalStats,
DOMHighResTimeStamp aNow,
dom::RTCStatsReportInternal* aReport) const;
~MediaTransportHandler() override;
RefPtr<NrIceCtx> mIceCtx;
RefPtr<NrIceResolver> mDNSResolver;
std::map<std::string, Transport> mTransports;
bool mProxyOnly = false;
};
}
#endif //_MTRANSPORTHANDLER_H__

View File

@ -319,13 +319,9 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
, mSignalingState(PCImplSignalingState::SignalingStable)
, mIceConnectionState(PCImplIceConnectionState::New)
, mIceGatheringState(PCImplIceGatheringState::New)
, mDtlsConnected(false)
, mWindow(nullptr)
, mCertificate(nullptr)
, mPrivacyRequested(false)
, mSTSThread(nullptr)
, mAllowIceLoopback(false)
, mAllowIceLinkLocal(false)
, mForceIceTcp(false)
, mMedia(nullptr)
, mUuidGen(MakeUnique<PCUuidGenerator>())
@ -356,10 +352,6 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
CSFLogInfo(LOGTAG, "%s: PeerConnectionImpl constructor for %s",
__FUNCTION__, mHandle.c_str());
STAMP_TIMECARD(mTimeCard, "Constructor Completed");
mAllowIceLoopback = Preferences::GetBool(
"media.peerconnection.ice.loopback", false);
mAllowIceLinkLocal = Preferences::GetBool(
"media.peerconnection.ice.link_local", false);
mForceIceTcp = Preferences::GetBool(
"media.peerconnection.ice.force_ice_tcp", false);
memset(mMaxReceiving, 0, sizeof(mMaxReceiving));
@ -507,9 +499,6 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
mMedia->SignalUpdateDefaultCandidate.connect(
this,
&PeerConnectionImpl::UpdateDefaultCandidate);
mMedia->SignalEndOfLocalCandidates.connect(
this,
&PeerConnectionImpl::EndOfLocalCandidates);
mMedia->SignalIceConnectionStateChange.connect(
this,
&PeerConnectionImpl::IceConnectionStateChange);
@ -582,7 +571,7 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
if (!aConfiguration.mPeerIdentity.IsEmpty()) {
mPeerIdentity = new PeerIdentity(aConfiguration.mPeerIdentity);
mPrivacyRequested = true;
mPrivacyRequested = Some(true);
}
}
@ -922,7 +911,8 @@ PeerConnectionImpl::EnsureDataConnection(uint16_t aLocalPort,
nsCOMPtr<nsIEventTarget> target = mWindow
? mWindow->EventTargetFor(TaskCategory::Other)
: nullptr;
mDataConnection = new DataChannelConnection(this, target);
mDataConnection = new DataChannelConnection(
this, target, mMedia->mTransportHandler);
if (!mDataConnection->Init(aLocalPort, aNumstreams, aMMSSet, aMaxMessageSize)) {
CSFLogError(LOGTAG,"%s DataConnection Init Failed",__FUNCTION__);
return NS_ERROR_FAILURE;
@ -939,7 +929,8 @@ PeerConnectionImpl::GetDatachannelParameters(
uint16_t* remoteport,
uint32_t* remotemaxmessagesize,
bool* mmsset,
std::string* transportId) const {
std::string* transportId,
bool* client) const {
for (const auto& transceiver : mJsepSession->GetTransceivers()) {
bool dataChannel =
@ -990,6 +981,9 @@ PeerConnectionImpl::GetDatachannelParameters(
(codec)->mRemoteMMSSet;
MOZ_ASSERT(!transceiver->mTransport.mTransportId.empty());
*transportId = transceiver->mTransport.mTransportId;
*client =
transceiver->mTransport.mDtls->GetRole() ==
JsepDtlsTransport::kJsepDtlsClient;
return NS_OK;
}
}
@ -1115,8 +1109,9 @@ PeerConnectionImpl::InitializeDataChannel()
uint32_t remotemaxmessagesize = 0;
bool mmsset = false;
std::string transportId;
bool client = false;
nsresult rv = GetDatachannelParameters(&channels, &localport, &remoteport,
&remotemaxmessagesize, &mmsset, &transportId);
&remotemaxmessagesize, &mmsset, &transportId, &client);
if (NS_FAILED(rv)) {
CSFLogDebug(LOGTAG, "%s: We did not negotiate datachannel", __FUNCTION__);
@ -1129,16 +1124,9 @@ PeerConnectionImpl::InitializeDataChannel()
rv = EnsureDataConnection(localport, channels, remotemaxmessagesize, mmsset);
if (NS_SUCCEEDED(rv)) {
// use the specified TransportFlow
RefPtr<TransportFlow> flow = mMedia->GetTransportFlow(transportId, false).get();
CSFLogDebug(LOGTAG, "Transportflow[%s] = %p",
transportId.c_str(), flow.get());
if (flow) {
if (mDataConnection->ConnectViaTransportFlow(flow,
localport,
remoteport)) {
return NS_OK;
}
if (mDataConnection->ConnectToTransport(
transportId, client, localport, remoteport)) {
return NS_OK;
}
// If we inited the DataConnection, call Destroy() before releasing it
mDataConnection->Destroy();
@ -1443,8 +1431,9 @@ PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
STAMP_TIMECARD(mTimeCard, "Set Local Description");
bool isolated = mMedia->AnyLocalTrackHasPeerIdentity();
mPrivacyRequested = mPrivacyRequested || isolated;
if (mMedia->AnyLocalTrackHasPeerIdentity()) {
mPrivacyRequested = Some(true);
}
mLocalRequestedSDP = aSDP;
@ -1820,16 +1809,21 @@ PeerConnectionImpl::SetPeerIdentity(const nsAString& aPeerIdentity)
}
nsresult
PeerConnectionImpl::SetDtlsConnected(bool aPrivacyRequested)
PeerConnectionImpl::OnAlpnNegotiated(const std::string& aAlpn)
{
PC_AUTO_ENTER_API_CALL(false);
if (mPrivacyRequested.isSome()) {
return NS_OK;
}
mPrivacyRequested = Some(aAlpn == "c-webrtc");
// For this, as with mPrivacyRequested, once we've connected to a peer, we
// fixate on that peer. Dealing with multiple peers or connections is more
// than this run-down wreck of an object can handle.
// Besides, this is only used to say if we have been connected ever.
if (!mPrivacyRequested && !aPrivacyRequested && !mDtlsConnected) {
// now we know that privacy isn't needed for sure
if (!*mPrivacyRequested) {
// Neither side wants privacy
nsIDocument* doc = GetWindow()->GetExtantDoc();
if (!doc) {
CSFLogInfo(LOGTAG, "Can't update principal on streams; document gone");
@ -1837,8 +1831,7 @@ PeerConnectionImpl::SetDtlsConnected(bool aPrivacyRequested)
}
mMedia->UpdateRemoteStreamPrincipals_m(doc->NodePrincipal());
}
mDtlsConnected = true;
mPrivacyRequested = mPrivacyRequested || aPrivacyRequested;
return NS_OK;
}
@ -2069,7 +2062,7 @@ PeerConnectionImpl::CreateReceiveTrack(SdpMediaSection::MediaType type)
nsCOMPtr<nsIPrincipal> principal;
nsIDocument* doc = GetWindow()->GetExtantDoc();
MOZ_ASSERT(doc);
if (mDtlsConnected && !PrivacyRequested()) {
if (mPrivacyRequested.isSome() && !*mPrivacyRequested) {
principal = doc->NodePrincipal();
} else {
// we're either certain that we need isolation for the streams, OR
@ -2669,6 +2662,11 @@ PeerConnectionImpl::CandidateReady(const std::string& candidate,
const std::string& transportId) {
PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);
if (candidate.empty()) {
mJsepSession->EndOfLocalCandidates(transportId);
return;
}
if (mForceIceTcp && std::string::npos != candidate.find(" UDP ")) {
CSFLogWarn(LOGTAG, "Blocking local UDP candidate: %s", candidate.c_str());
return;
@ -2886,12 +2884,6 @@ PeerConnectionImpl::UpdateDefaultCandidate(const std::string& defaultAddr,
transportId);
}
void
PeerConnectionImpl::EndOfLocalCandidates(const std::string& transportId) {
CSFLogDebug(LOGTAG, "%s", __FUNCTION__);
mJsepSession->EndOfLocalCandidates(transportId);
}
nsresult
PeerConnectionImpl::BuildStatsQuery_m(
mozilla::dom::MediaStreamTrack *aSelector,
@ -2912,10 +2904,6 @@ PeerConnectionImpl::BuildStatsQuery_m(
return rv;
}
// Note: mMedia->ice_ctx() is deleted on STS thread; so make sure we grab and hold
// a ref instead of making multiple calls. NrIceCtx uses threadsafe refcounting.
// NOTE: Do this after all other failure tests, to ensure we don't
// accidentally release the Ctx on Mainthread.
query->media = mMedia;
if (!query->media) {
CSFLogError(LOGTAG, "Could not build stats query, no ice_ctx");
@ -3199,15 +3187,17 @@ PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) {
}
}
if (query->grabAllLevels) {
query->media->GetAllIceStats_s(query->internalStats,
query->now,
query->report);
} else {
query->media->GetIceStats_s(query->transportId,
query->internalStats,
query->now,
query->report);
if (query->media->mTransportHandler) {
if (query->grabAllLevels) {
query->media->mTransportHandler->GetAllIceStats(query->internalStats,
query->now,
query->report);
} else {
query->media->mTransportHandler->GetIceStats(query->transportId,
query->internalStats,
query->now,
query->report);
}
}
return NS_OK;

View File

@ -211,14 +211,6 @@ public:
return mMedia;
}
// Configure the ability to use localhost.
void SetAllowIceLoopback(bool val) { mAllowIceLoopback = val; }
bool GetAllowIceLoopback() const { return mAllowIceLoopback; }
// Configure the ability to use IPV6 link-local addresses.
void SetAllowIceLinkLocal(bool val) { mAllowIceLinkLocal = val; }
bool GetAllowIceLinkLocal() const { return mAllowIceLinkLocal; }
// Handle system to allow weak references to be passed through C code
virtual const std::string& GetHandle();
@ -233,7 +225,6 @@ public:
const std::string& defaultRtcpAddr,
uint16_t defaultRtcpPort,
const std::string& transportId);
void EndOfLocalCandidates(const std::string& transportId);
static void ListenThread(void *aData);
static void ConnectThread(void *aData);
@ -447,7 +438,10 @@ public:
}
// this method checks to see if we've made a promise to protect media.
bool PrivacyRequested() const { return mPrivacyRequested; }
bool PrivacyRequested() const
{
return mPrivacyRequested.isSome() && *mPrivacyRequested;
}
NS_IMETHODIMP GetFingerprint(char** fingerprint);
void GetFingerprint(nsAString& fingerprint)
@ -541,7 +535,7 @@ public:
bool IsClosed() const;
// called when DTLS connects; we only need this once
nsresult SetDtlsConnected(bool aPrivacyRequested);
nsresult OnAlpnNegotiated(const std::string& aAlpn);
bool HasMedia() const;
@ -611,7 +605,8 @@ private:
uint16_t* remoteport,
uint32_t* maxmessagesize,
bool* mmsset,
std::string* transportId) const;
std::string* transportId,
bool* client) const;
nsresult AddRtpTransceiverToJsepSession(RefPtr<JsepTransceiver>& transceiver);
already_AddRefed<TransceiverImpl> CreateTransceiverImpl(
@ -649,10 +644,6 @@ private:
mozilla::dom::PCImplIceConnectionState mIceConnectionState;
mozilla::dom::PCImplIceGatheringState mIceGatheringState;
// DTLS
// this is true if we have been connected ever, see SetDtlsConnected
bool mDtlsConnected;
nsCOMPtr<nsIThread> mThread;
// TODO: Remove if we ever properly wire PeerConnection for cycle-collection.
nsWeakPtr mPCObserver;
@ -679,7 +670,7 @@ private:
//
// This can be false if mPeerIdentity is set, in the case where identity is
// provided, but the media is not protected from the app on either side
bool mPrivacyRequested;
Maybe<bool> mPrivacyRequested;
// A handle to refer to this PC with
std::string mHandle;
@ -693,8 +684,6 @@ private:
// DataConnection that's used to get all the DataChannels
RefPtr<mozilla::DataChannelConnection> mDataConnection;
bool mAllowIceLoopback;
bool mAllowIceLinkLocal;
bool mForceIceTcp;
RefPtr<PeerConnectionMedia> mMedia;

View File

@ -13,6 +13,7 @@
#include "mozilla/UniquePtr.h"
#include "mozilla/net/StunAddrsRequestChild.h"
#include "nsIProtocolProxyCallback.h"
#include "MediaTransportHandler.h"
#include "TransceiverImpl.h"
@ -22,8 +23,6 @@ namespace mozilla {
class DataChannel;
class PeerIdentity;
namespace dom {
struct RTCInboundRTPStreamStats;
struct RTCOutboundRTPStreamStats;
class MediaStreamTrack;
}
}
@ -49,19 +48,10 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
public:
explicit PeerConnectionMedia(PeerConnectionImpl *parent);
PeerConnectionImpl* GetPC() { return mParent; }
nsresult Init(const dom::RTCConfiguration& aConfiguration);
// WARNING: This destroys the object!
void SelfDestruct();
void GetIceStats_s(const std::string& aTransportId,
bool internalStats,
DOMHighResTimeStamp now,
RTCStatsReportInternal* report) const;
void GetAllIceStats_s(bool internalStats,
DOMHighResTimeStamp now,
RTCStatsReportInternal* report) const;
// Ensure ICE transports exist that we might need when offer/answer concludes
void EnsureTransports(const JsepSession& aSession);
@ -129,19 +119,6 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
const nsCOMPtr<nsIThread>& GetMainThread() const { return mMainThread; }
const nsCOMPtr<nsIEventTarget>& GetSTSThread() const { return mSTSThread; }
// Get a transport flow either RTP/RTCP for a particular stream
// A stream can be of audio/video/datachannel/budled(?) types
RefPtr<TransportFlow> GetTransportFlow(const std::string& aId,
bool aIsRtcp) {
auto& flows = aIsRtcp ? mRtcpTransportFlows : mTransportFlows;
auto it = flows.find(aId);
if (it == flows.end()) {
return nullptr;
}
return it->second;
}
// Used by PCImpl in a couple of places. Might be good to move that code in
// here.
std::vector<RefPtr<TransceiverImpl>>& GetTransceivers()
@ -149,15 +126,9 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
return mTransceivers;
}
// Add a transport flow
void AddTransportFlow(const std::string& aId, bool aRtcp,
const RefPtr<TransportFlow> &aFlow);
void RemoveTransportFlow(const std::string& aId, bool aRtcp);
void ConnectDtlsListener_s(const RefPtr<TransportFlow>& aFlow);
void DtlsConnected_s(TransportLayer* aFlow,
TransportLayer::State state);
static void DtlsConnected_m(const std::string& aParentHandle,
bool aPrivacyRequested);
void AlpnNegotiated_s(const std::string& aAlpn);
static void AlpnNegotiated_m(const std::string& aParentHandle,
const std::string& aAlpn);
// ICE state signals
sigslot::signal1<mozilla::dom::PCImplIceGatheringState>
@ -165,17 +136,19 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
sigslot::signal1<mozilla::dom::PCImplIceConnectionState>
SignalIceConnectionStateChange;
// This passes a candidate:... attribute and transport id
// end-of-candidates is signaled with the empty string
sigslot::signal2<const std::string&, const std::string&> SignalCandidate;
// This passes address, port, transport id of the default candidate.
sigslot::signal5<const std::string&, uint16_t,
const std::string&, uint16_t, const std::string&>
SignalUpdateDefaultCandidate;
sigslot::signal1<const std::string&>
SignalEndOfLocalCandidates;
// TODO: Move to PeerConnectionImpl
RefPtr<WebRtcCallWrapper> mCall;
// mtransport objects
RefPtr<MediaTransportHandler> mTransportHandler;
private:
void InitLocalAddrs(); // for stun local address IPC request
nsresult InitProxy();
@ -215,86 +188,39 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
void SelfDestruct_m();
// Manage ICE transports.
nsresult UpdateTransport(const JsepTransceiver& aTransceiver,
bool aForceIceTcp);
void EnsureTransport_s(const std::string& aTransportId,
const std::string& aUfrag,
const std::string& aPwd,
size_t aComponentCount);
void ActivateTransport_s(const std::string& aTransportId,
const std::string& aLocalUfrag,
const std::string& aLocalPwd,
size_t aComponentCount,
const std::string& aUfrag,
const std::string& aPassword,
const std::vector<std::string>& aCandidateList);
void RemoveTransportsExcept_s(const std::set<std::string>& aTransportIds);
nsresult UpdateTransportFlows(const JsepTransceiver& transceiver);
nsresult UpdateTransportFlow(bool aIsRtcp,
const JsepTransport& aTransport);
void UpdateTransport(const JsepTransceiver& aTransceiver,
bool aForceIceTcp);
void GatherIfReady();
void FlushIceCtxOperationQueueIfReady();
void PerformOrEnqueueIceCtxOperation(nsIRunnable* runnable);
void EnsureIceGathering_s(bool aDefaultRouteOnly, bool aProxyOnly);
void EnsureIceGathering_s(bool aDefaultRouteOnly);
void StartIceChecks_s(bool aIsControlling,
bool aIsOfferer,
bool aIsIceLite,
const std::vector<std::string>& aIceOptionsList);
bool GetPrefDefaultAddressOnly() const;
bool GetPrefProxyOnly() const;
void ConnectSignals(NrIceCtx *aCtx, NrIceCtx *aOldCtx=nullptr);
// Process a trickle ICE candidate.
void AddIceCandidate_s(const std::string& aCandidate,
const std::string& aTransportId);
void UpdateNetworkState_s(bool online);
void ConnectSignals();
// ICE events
void IceGatheringStateChange_s(NrIceCtx* ctx,
NrIceCtx::GatheringState state);
void IceConnectionStateChange_s(NrIceCtx* ctx,
NrIceCtx::ConnectionState state);
void IceStreamReady_s(NrIceMediaStream *aStream);
void OnCandidateFound_s(NrIceMediaStream *aStream,
const std::string& aCandidate);
void EndOfLocalCandidates(const std::string& aDefaultAddr,
uint16_t aDefaultPort,
const std::string& aDefaultRtcpAddr,
uint16_t aDefaultRtcpPort,
const std::string& aTransportId);
void GetDefaultCandidates(const NrIceMediaStream& aStream,
NrIceCandidate* aCandidate,
NrIceCandidate* aRtcpCandidate);
void IceGatheringStateChange_s(dom::PCImplIceGatheringState aState);
void IceConnectionStateChange_s(dom::PCImplIceConnectionState aState);
void OnCandidateFound_s(
const std::string& aTransportId,
const MediaTransportHandler::CandidateInfo& aCandidateInfo);
void IceGatheringStateChange_m(dom::PCImplIceGatheringState aState);
void IceConnectionStateChange_m(dom::PCImplIceConnectionState aState);
void OnCandidateFound_m(
const std::string& aTransportId,
const MediaTransportHandler::CandidateInfo& aCandidateInfo);
void IceGatheringStateChange_m(NrIceCtx* ctx,
NrIceCtx::GatheringState state);
void IceConnectionStateChange_m(NrIceCtx* ctx,
NrIceCtx::ConnectionState state);
void OnCandidateFound_m(const std::string& aCandidateLine,
const std::string& aDefaultAddr,
uint16_t aDefaultPort,
const std::string& aDefaultRtcpAddr,
uint16_t aDefaultRtcpPort,
const std::string& aTransportId);
void EndOfLocalCandidates_m(const std::string& aDefaultAddr,
uint16_t aDefaultPort,
const std::string& aDefaultRtcpAddr,
uint16_t aDefaultRtcpPort,
const std::string& aTransportId);
bool IsIceCtxReady() const {
return mProxyResolveCompleted && mLocalAddrsCompleted;
}
void GetIceStats_s(const NrIceMediaStream& aStream,
bool internalStats,
DOMHighResTimeStamp now,
RTCStatsReportInternal* report) const;
// The parent PC
PeerConnectionImpl *mParent;
// and a loose handle on it for event driven stuff
@ -303,21 +229,6 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
std::vector<RefPtr<TransceiverImpl>> mTransceivers;
// ICE objects
RefPtr<NrIceCtx> mIceCtx;
// DNS
RefPtr<NrIceResolver> mDNSResolver;
// Transport flows for RTP and RTP/RTCP mux
std::map<std::string, RefPtr<TransportFlow> > mTransportFlows;
// Transport flows for standalone RTCP (rarely used)
std::map<std::string, RefPtr<TransportFlow> > mRtcpTransportFlows;
// UUID Generator
UniquePtr<PCUuidGenerator> mUuidGen;
// The main thread.
nsCOMPtr<nsIThread> mMainThread;
@ -337,7 +248,8 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
bool mProxyResolveCompleted;
// Used to store the result of the request.
UniquePtr<NrIceProxyServer> mProxyServer;
std::string mProxyHost;
uint16_t mProxyPort;
// Used to cancel incoming stun addrs response
RefPtr<net::StunAddrsRequestChild> mStunAddrsRequest;

View File

@ -3,12 +3,9 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TransceiverImpl.h"
#include "mtransport/runnable_utils.h"
#include "mozilla/UniquePtr.h"
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include "AudioConduit.h"
#include "VideoConduit.h"
#include "MediaStreamGraph.h"
@ -22,7 +19,7 @@
#include "MediaSegment.h"
#include "RemoteTrackSource.h"
#include "MediaConduitInterface.h"
#include "PeerConnectionMedia.h"
#include "MediaTransportHandler.h"
#include "mozilla/dom/RTCRtpReceiverBinding.h"
#include "mozilla/dom/RTCRtpSenderBinding.h"
#include "mozilla/dom/RTCRtpTransceiverBinding.h"
@ -36,6 +33,7 @@ using LocalDirection = MediaSessionConduitLocalDirection;
TransceiverImpl::TransceiverImpl(
const std::string& aPCHandle,
MediaTransportHandler* aTransportHandler,
JsepTransceiver* aJsepTransceiver,
nsIEventTarget* aMainThread,
nsIEventTarget* aStsThread,
@ -43,6 +41,7 @@ TransceiverImpl::TransceiverImpl(
dom::MediaStreamTrack* aSendTrack,
WebRtcCallWrapper* aCallWrapper) :
mPCHandle(aPCHandle),
mTransportHandler(aTransportHandler),
mJsepTransceiver(aJsepTransceiver),
mHaveStartedReceiving(false),
mHaveSetupTransport(false),
@ -66,6 +65,7 @@ TransceiverImpl::TransceiverImpl(
mTransmitPipeline = new MediaPipelineTransmit(
mPCHandle,
mTransportHandler,
mMainThread.get(),
mStsThread.get(),
IsVideo(),
@ -93,6 +93,7 @@ TransceiverImpl::InitAudio()
mReceivePipeline = new MediaPipelineReceiveAudio(
mPCHandle,
mTransportHandler,
mMainThread.get(),
mStsThread.get(),
static_cast<AudioSessionConduit*>(mConduit.get()),
@ -114,6 +115,7 @@ TransceiverImpl::InitVideo()
mReceivePipeline = new MediaPipelineReceiveVideo(
mPCHandle,
mTransportHandler,
mMainThread.get(),
mStsThread.get(),
static_cast<VideoSessionConduit*>(mConduit.get()),
@ -140,13 +142,12 @@ TransceiverImpl::Shutdown_m()
mTransmitPipeline->Shutdown_m();
mReceivePipeline = nullptr;
mTransmitPipeline = nullptr;
mTransportHandler = nullptr;
mSendTrack = nullptr;
if (mConduit) {
mConduit->DeleteStreams();
}
mConduit = nullptr;
RUN_ON_THREAD(mStsThread, WrapRelease(mRtpFlow.forget()), NS_DISPATCH_NORMAL);
RUN_ON_THREAD(mStsThread, WrapRelease(mRtcpFlow.forget()), NS_DISPATCH_NORMAL);
}
nsresult
@ -163,7 +164,7 @@ TransceiverImpl::UpdateSendTrack(dom::MediaStreamTrack* aSendTrack)
}
nsresult
TransceiverImpl::UpdateTransport(PeerConnectionMedia& aTransportManager)
TransceiverImpl::UpdateTransport()
{
if (!mJsepTransceiver->HasLevel()) {
return NS_OK;
@ -178,9 +179,6 @@ TransceiverImpl::UpdateTransport(PeerConnectionMedia& aTransportManager)
ASSERT_ON_THREAD(mMainThread);
nsAutoPtr<MediaPipelineFilter> filter;
mRtpFlow = aTransportManager.GetTransportFlow(GetTransportId(), false);
mRtcpFlow = aTransportManager.GetTransportFlow(GetTransportId(), true);
if (mJsepTransceiver->HasBundleLevel() &&
mJsepTransceiver->mRecvTrack.GetNegotiatedDetails()) {
filter = new MediaPipelineFilter;
@ -201,8 +199,10 @@ TransceiverImpl::UpdateTransport(PeerConnectionMedia& aTransportManager)
}
}
mReceivePipeline->UpdateTransport_m(mRtpFlow, mRtcpFlow, filter);
mTransmitPipeline->UpdateTransport_m(mRtpFlow, mRtcpFlow, nsAutoPtr<MediaPipelineFilter>());
mReceivePipeline->UpdateTransport_m(
mJsepTransceiver->mTransport.mTransportId, filter);
mTransmitPipeline->UpdateTransport_m(
mJsepTransceiver->mTransport.mTransportId, filter);
return NS_OK;
}

View File

@ -9,17 +9,14 @@
#include "nsCOMPtr.h"
#include "nsIEventTarget.h"
#include "nsTArray.h"
#include "mozilla/OwningNonNull.h"
#include "mozilla/dom/MediaStreamTrack.h"
#include "ErrorList.h"
#include "mtransport/transportflow.h"
#include "signaling/src/jsep/JsepTransceiver.h"
class nsIPrincipal;
namespace mozilla {
class PeerIdentity;
class PeerConnectionMedia;
class JsepTransceiver;
enum class MediaSessionConduitLocalDirection : int;
class MediaSessionConduit;
@ -29,6 +26,7 @@ class MediaPipelineReceive;
class MediaPipelineTransmit;
class MediaPipeline;
class MediaPipelineFilter;
class MediaTransportHandler;
class WebRtcCallWrapper;
class JsepTrackNegotiatedDetails;
@ -41,7 +39,7 @@ struct RTCRtpSourceEntry;
* This is what ties all the various pieces that make up a transceiver
* together. This includes:
* MediaStreamTrack for rendering and capture
* TransportFlow for RTP transmission/reception
* MediaTransportHandler for RTP transmission/reception
* Audio/VideoConduit for feeding RTP/RTCP into webrtc.org for decoding, and
* feeding audio/video frames into webrtc.org for encoding into RTP/RTCP.
*/
@ -53,6 +51,7 @@ public:
* set.
*/
TransceiverImpl(const std::string& aPCHandle,
MediaTransportHandler* aTransportHandler,
JsepTransceiver* aJsepTransceiver,
nsIEventTarget* aMainThread,
nsIEventTarget* aStsThread,
@ -71,7 +70,7 @@ public:
nsIPrincipal* aPrincipal,
const PeerIdentity* aSinkIdentity);
nsresult UpdateTransport(PeerConnectionMedia& aTransportManager);
nsresult UpdateTransport();
nsresult UpdateConduit();
@ -144,6 +143,7 @@ private:
void Stop();
const std::string mPCHandle;
RefPtr<MediaTransportHandler> mTransportHandler;
RefPtr<JsepTransceiver> mJsepTransceiver;
std::string mMid;
bool mHaveStartedReceiving;
@ -154,8 +154,6 @@ private:
RefPtr<dom::MediaStreamTrack> mSendTrack;
// state for webrtc.org that is shared between all transceivers
RefPtr<WebRtcCallWrapper> mCallWrapper;
RefPtr<TransportFlow> mRtpFlow;
RefPtr<TransportFlow> mRtcpFlow;
RefPtr<MediaSessionConduit> mConduit;
RefPtr<MediaPipelineReceive> mReceivePipeline;
RefPtr<MediaPipelineTransmit> mTransmitPipeline;

View File

@ -22,6 +22,7 @@ LOCAL_INCLUDES += [
]
UNIFIED_SOURCES += [
'MediaTransportHandler.cpp',
'PacketDumper.cpp',
'PeerConnectionCtx.cpp',
'PeerConnectionImpl.cpp',

View File

@ -50,6 +50,8 @@
#include "mozilla/Unused.h"
#ifdef MOZ_PEERCONNECTION
#include "mtransport/runnable_utils.h"
#include "signaling/src/peerconnection/MediaTransportHandler.h"
#include "mediapacket.h"
#endif
#define DATACHANNEL_LOG(args) LOG(args)
@ -304,7 +306,8 @@ debug_printf(const char *format, ...)
}
DataChannelConnection::DataChannelConnection(DataConnectionListener *listener,
nsIEventTarget *aTarget)
nsIEventTarget *aTarget,
MediaTransportHandler* aHandler)
: NeckoTargetHolder(aTarget)
, mLock("netwerk::sctp::DataChannelConnection")
, mSendInterleaved(false)
@ -312,13 +315,13 @@ DataChannelConnection::DataChannelConnection(DataConnectionListener *listener,
, mMaxMessageSizeSet(false)
, mMaxMessageSize(0)
, mAllocateEven(false)
, mTransportHandler(aHandler)
{
mCurrentStream = 0;
mState = CLOSED;
mSocket = nullptr;
mMasterSocket = nullptr;
mListener = listener;
mDtls = nullptr;
mLocalPort = 0;
mRemotePort = 0;
mPendingType = PENDING_NONE;
@ -336,10 +339,7 @@ DataChannelConnection::~DataChannelConnection()
ASSERT_WEBRTC(mState == CLOSED);
MOZ_ASSERT(!mMasterSocket);
MOZ_ASSERT(mPending.GetSize() == 0);
MOZ_ASSERT(!mDtls);
// Already disconnected from sigslot/mTransportFlow
// TransportFlows must be released from the STS thread
if (!IsSTSThread()) {
ASSERT_WEBRTC(NS_IsMainThread());
@ -421,8 +421,7 @@ void DataChannelConnection::DestroyOnSTS(struct socket *aMasterSocket,
void DataChannelConnection::DestroyOnSTSFinal()
{
mTransportFlow = nullptr;
mDtls = nullptr;
mTransportHandler = nullptr;
sDataChannelShutdown->CreateConnectionShutdown(this);
}
@ -686,60 +685,71 @@ DataChannelConnection::GetMaxMessageSize()
}
#ifdef MOZ_PEERCONNECTION
void
DataChannelConnection::SetEvenOdd()
{
ASSERT_WEBRTC(IsSTSThread());
MOZ_ASSERT(mDtls); // DTLS is mandatory
mAllocateEven = (mDtls->role() == TransportLayerDtls::CLIENT);
}
bool
DataChannelConnection::ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport)
DataChannelConnection::ConnectToTransport(
const std::string& aTransportId,
bool aClient,
uint16_t localport, uint16_t remoteport)
{
LOG(("Connect DTLS local %u, remote %u", localport, remoteport));
MOZ_ASSERT(mMasterSocket, "SCTP wasn't initialized before ConnectViaTransportFlow!");
if (NS_WARN_IF(!aFlow)) {
MOZ_ASSERT(mMasterSocket, "SCTP wasn't initialized before ConnectToTransport!");
if (NS_WARN_IF(aTransportId.empty())) {
return false;
}
mTransportFlow = aFlow;
mLocalPort = localport;
mRemotePort = remoteport;
mState = CONNECTING;
RUN_ON_THREAD(mSTS, WrapRunnable(RefPtr<DataChannelConnection>(this),
&DataChannelConnection::SetSignals),
&DataChannelConnection::SetSignals,
aTransportId,
aClient),
NS_DISPATCH_NORMAL);
return true;
}
void
DataChannelConnection::SetSignals()
DataChannelConnection::SetSignals(const std::string& aTransportId,
bool aClient)
{
ASSERT_WEBRTC(IsSTSThread());
mDtls = static_cast<TransportLayerDtls*>(mTransportFlow->GetLayer("dtls"));
ASSERT_WEBRTC(mDtls);
LOG(("Setting transport signals, state: %d", mDtls->state()));
mDtls->SignalPacketReceived.connect(this, &DataChannelConnection::SctpDtlsInput);
mTransportId = aTransportId;
mAllocateEven = aClient;
mTransportHandler->SignalPacketReceived.connect(
this, &DataChannelConnection::SctpDtlsInput);
// SignalStateChange() doesn't call you with the initial state
mDtls->SignalStateChange.connect(this, &DataChannelConnection::CompleteConnect);
CompleteConnect(mDtls, mDtls->state());
if (mTransportHandler->GetState(mTransportId, false)
== TransportLayer::TS_OPEN) {
LOG(("Setting transport signals, dtls already open"));
CompleteConnect();
} else {
LOG(("Setting transport signals, dtls not open yet"));
mTransportHandler->SignalStateChange.connect(
this, &DataChannelConnection::TransportStateChange);
}
}
void
DataChannelConnection::CompleteConnect(TransportLayer *layer, TransportLayer::State state)
DataChannelConnection::TransportStateChange(const std::string& aTransportId,
TransportLayer::State aState)
{
LOG(("Data transport state: %d", state));
if (aState == TransportLayer::TS_OPEN) {
CompleteConnect();
}
}
void
DataChannelConnection::CompleteConnect()
{
LOG(("dtls open"));
MutexAutoLock lock(mLock);
ASSERT_WEBRTC(IsSTSThread());
// We should abort connection on TS_ERROR.
// Note however that the association will also fail (perhaps with a delay) and
// notify us in that way
if (state != TransportLayer::TS_OPEN || !mMasterSocket)
if (!mMasterSocket) {
return;
}
struct sockaddr_conn addr;
memset(&addr, 0, sizeof(addr));
@ -795,8 +805,7 @@ DataChannelConnection::CompleteConnect(TransportLayer *layer, TransportLayer::St
LOG(("usrsctp_connect failed: %d", errno));
mState = CLOSED;
} else {
// We set Even/Odd and fire ON_CONNECTION via SCTP_COMM_UP when we get that
// This also avoids issues with calling TransportFlow stuff on Mainthread
// We fire ON_CONNECTION via SCTP_COMM_UP when we get that
return;
}
}
@ -838,8 +847,13 @@ DataChannelConnection::ProcessQueuedOpens()
}
void
DataChannelConnection::SctpDtlsInput(TransportLayer *layer, MediaPacket& packet)
DataChannelConnection::SctpDtlsInput(const std::string& aTransportId,
MediaPacket& packet)
{
if ((packet.type() != MediaPacket::SCTP) || (mTransportId != aTransportId)) {
return;
}
if (MOZ_LOG_TEST(gSCTPLog, LogLevel::Debug)) {
char *buf;
@ -859,8 +873,9 @@ int
DataChannelConnection::SendPacket(nsAutoPtr<MediaPacket> packet)
{
//LOG(("%p: SCTP/DTLS sent %ld bytes", this, len));
if (mDtls) {
return mDtls->SendPacket(*packet) < 0 ? 1 : 0;
if (!mTransportId.empty()) {
nsresult rv = mTransportHandler->SendPacket(mTransportId, *packet);
return NS_FAILED(rv) ? 1 : 0;
}
return 0;
}
@ -888,6 +903,7 @@ DataChannelConnection::SctpDtlsOutput(void *addr, void *buffer, size_t length,
// one of the locks before calling a packet output routine; with changes to
// the underlying SCTP stack this might remove the need to use an async proxy.
nsAutoPtr<MediaPacket> packet(new MediaPacket);
packet->SetType(MediaPacket::SCTP);
packet->Copy(static_cast<const uint8_t*>(buffer), length);
// XXX It might be worthwhile to add an assertion against the thread
@ -906,8 +922,6 @@ DataChannelConnection::SctpDtlsOutput(void *addr, void *buffer, size_t length,
// listen for incoming associations
// Blocks! - Don't call this from main thread!
#error This code will not work as-is since SetEvenOdd() runs on Mainthread
bool
DataChannelConnection::Listen(unsigned short port)
{
@ -952,8 +966,6 @@ DataChannelConnection::Listen(unsigned short port)
LOG(("Couldn't set SO_LINGER on SCTP socket"));
}
SetEvenOdd();
// Notify Connection open
// XXX We need to make sure connection sticks around until the message is delivered
LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this));
@ -1031,8 +1043,6 @@ DataChannelConnection::Connect(const char *addr, unsigned short port)
LOG(("connect() succeeded! Entering connected mode"));
mState = OPEN;
SetEvenOdd();
// Notify Connection open
// XXX We need to make sure connection sticks around until the message is delivered
LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this));
@ -1810,8 +1820,6 @@ DataChannelConnection::HandleAssociationChangeEvent(const struct sctp_assoc_chan
mPpidFragmentation = true;
}
SetEvenOdd();
Dispatch(do_AddRef(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CONNECTION,
this)));

View File

@ -27,9 +27,7 @@
#include "mozilla/net/NeckoTargetHolder.h"
#ifdef SCTP_DTLS_SUPPORTED
#include "mtransport/sigslot.h"
#include "mtransport/transportflow.h"
#include "mtransport/transportlayer.h"
#include "mtransport/transportlayerdtls.h"
#include "mtransport/transportlayer.h" // For TransportLayer::State
#endif
#ifndef DATACHANNEL_LOG
@ -50,6 +48,8 @@ namespace mozilla {
class DataChannelConnection;
class DataChannel;
class DataChannelOnMessageAvailable;
class MediaPacket;
class MediaTransportHandler;
// For sending outgoing messages.
// This class only holds a reference to the data and the info structure but does
@ -145,7 +145,8 @@ public:
};
DataChannelConnection(DataConnectionListener *listener,
nsIEventTarget *aTarget);
nsIEventTarget *aTarget,
MediaTransportHandler* aTransportHandler);
bool Init(unsigned short aPort, uint16_t aNumStreams, bool aMaxMessageSizeSet,
uint64_t aMaxMessageSize);
@ -168,11 +169,11 @@ public:
#endif
#ifdef SCTP_DTLS_SUPPORTED
// Connect using a TransportFlow (DTLS) channel
void SetEvenOdd();
bool ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport);
void CompleteConnect(TransportLayer *layer, TransportLayer::State state);
void SetSignals();
bool ConnectToTransport(const std::string& aTransportId, bool aClient, uint16_t localport, uint16_t remoteport);
void TransportStateChange(const std::string& aTransportId,
TransportLayer::State aState);
void CompleteConnect();
void SetSignals(const std::string& aTransportId, bool aClient);
#endif
typedef enum {
@ -247,7 +248,7 @@ private:
#ifdef SCTP_DTLS_SUPPORTED
static void DTLSConnectThread(void *data);
int SendPacket(nsAutoPtr<MediaPacket> packet);
void SctpDtlsInput(TransportLayer *layer, MediaPacket& packet);
void SctpDtlsInput(const std::string& aTransportId, MediaPacket& packet);
static int SctpDtlsOutput(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df);
#endif
DataChannel* FindChannelByStream(uint16_t stream);
@ -310,9 +311,6 @@ private:
}
#endif
// Exists solely for proxying release of the TransportFlow to the STS thread
static void ReleaseTransportFlow(const RefPtr<TransportFlow>& aFlow) {}
bool mSendInterleaved;
bool mPpidFragmentation;
bool mMaxMessageSizeSet;
@ -338,8 +336,8 @@ private:
uint16_t mState; // Protected with mLock
#ifdef SCTP_DTLS_SUPPORTED
RefPtr<TransportFlow> mTransportFlow;
TransportLayerDtls* mDtls;
std::string mTransportId;
RefPtr<MediaTransportHandler> mTransportHandler;
nsCOMPtr<nsIEventTarget> mSTS;
#endif
uint16_t mLocalPort; // Accessed from connect thread

View File

@ -20,6 +20,7 @@ FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/media/mtransport',
'/media/webrtc',
'/netwerk/sctp/src',
]