summaryrefslogtreecommitdiffstats
path: root/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp')
-rw-r--r--media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp1672
1 files changed, 1672 insertions, 0 deletions
diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
new file mode 100644
index 000000000..4f42b0bb7
--- /dev/null
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -0,0 +1,1672 @@
+/* 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 <ostream>
+#include <string>
+#include <vector>
+
+#include "CSFLog.h"
+
+#include "nspr.h"
+
+#include "nricectx.h"
+#include "nricemediastream.h"
+#include "MediaPipelineFactory.h"
+#include "PeerConnectionImpl.h"
+#include "PeerConnectionMedia.h"
+#include "AudioConduit.h"
+#include "VideoConduit.h"
+#include "runnable_utils.h"
+#include "transportlayerice.h"
+#include "transportlayerdtls.h"
+#include "signaling/src/jsep/JsepSession.h"
+#include "signaling/src/jsep/JsepTransport.h"
+
+#ifdef USE_FAKE_STREAMS
+#include "DOMMediaStream.h"
+#include "FakeMediaStreams.h"
+#else
+#include "MediaSegment.h"
+#ifdef MOZILLA_INTERNAL_API
+#include "MediaStreamGraph.h"
+#endif
+#endif
+
+#ifndef USE_FAKE_MEDIA_STREAMS
+#include "MediaStreamGraphImpl.h"
+#endif
+
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsIURI.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsICancelable.h"
+#include "nsILoadInfo.h"
+#include "nsIContentPolicy.h"
+#include "nsIProxyInfo.h"
+#include "nsIProtocolProxyService.h"
+
+#include "nsProxyRelease.h"
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+#include "MediaStreamList.h"
+#include "nsIScriptGlobalObject.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/RTCStatsReportBinding.h"
+#include "MediaStreamTrack.h"
+#include "VideoStreamTrack.h"
+#include "MediaStreamError.h"
+#include "MediaManager.h"
+#endif
+
+
+
+namespace mozilla {
+using namespace dom;
+
+static const char* logTag = "PeerConnectionMedia";
+
+nsresult
+PeerConnectionMedia::ReplaceTrack(const std::string& aOldStreamId,
+ const std::string& aOldTrackId,
+ MediaStreamTrack& aNewTrack,
+ const std::string& aNewStreamId,
+ const std::string& aNewTrackId)
+{
+ RefPtr<LocalSourceStreamInfo> oldInfo(GetLocalStreamById(aOldStreamId));
+
+ if (!oldInfo) {
+ CSFLogError(logTag, "Failed to find stream id %s", aOldStreamId.c_str());
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsresult rv = AddTrack(*aNewTrack.mOwningStream, aNewStreamId,
+ aNewTrack, aNewTrackId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<LocalSourceStreamInfo> newInfo(GetLocalStreamById(aNewStreamId));
+
+ if (!newInfo) {
+ CSFLogError(logTag, "Failed to add track id %s", aNewTrackId.c_str());
+ MOZ_ASSERT(false);
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = newInfo->TakePipelineFrom(oldInfo, aOldTrackId, aNewTrack, aNewTrackId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return RemoveLocalTrack(aOldStreamId, aOldTrackId);
+}
+
+static void
+PipelineReleaseRef_m(RefPtr<MediaPipeline> pipeline)
+{}
+
+static void
+PipelineDetachTransport_s(RefPtr<MediaPipeline> pipeline,
+ nsCOMPtr<nsIThread> mainThread)
+{
+ pipeline->DetachTransport_s();
+ mainThread->Dispatch(
+ // Make sure we let go of our reference before dispatching
+ // If the dispatch fails, well, we're hosed anyway.
+ WrapRunnableNM(PipelineReleaseRef_m, pipeline.forget()),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+SourceStreamInfo::EndTrack(MediaStream* stream, dom::MediaStreamTrack* track)
+{
+ if (!stream || !stream->AsSourceStream()) {
+ return;
+ }
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ class Message : public ControlMessage {
+ public:
+ Message(MediaStream* stream, TrackID track)
+ : ControlMessage(stream),
+ track_id_(track) {}
+
+ virtual void Run() override {
+ mStream->AsSourceStream()->EndTrack(track_id_);
+ }
+ private:
+ TrackID track_id_;
+ };
+
+ stream->GraphImpl()->AppendMessage(
+ MakeUnique<Message>(stream, track->mTrackID));
+#endif
+
+}
+
+void
+SourceStreamInfo::RemoveTrack(const std::string& trackId)
+{
+ mTracks.erase(trackId);
+
+ RefPtr<MediaPipeline> pipeline = GetPipelineByTrackId_m(trackId);
+ if (pipeline) {
+ mPipelines.erase(trackId);
+ pipeline->ShutdownMedia_m();
+ mParent->GetSTSThread()->Dispatch(
+ WrapRunnableNM(PipelineDetachTransport_s,
+ pipeline.forget(),
+ mParent->GetMainThread()),
+ NS_DISPATCH_NORMAL);
+ }
+}
+
+void SourceStreamInfo::DetachTransport_s()
+{
+ ASSERT_ON_THREAD(mParent->GetSTSThread());
+ // walk through all the MediaPipelines and call the shutdown
+ // transport functions. Must be on the STS thread.
+ for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) {
+ it->second->DetachTransport_s();
+ }
+}
+
+void SourceStreamInfo::DetachMedia_m()
+{
+ ASSERT_ON_THREAD(mParent->GetMainThread());
+
+ // walk through all the MediaPipelines and call the shutdown
+ // media functions. Must be on the main thread.
+ for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) {
+ it->second->ShutdownMedia_m();
+ }
+ mMediaStream = nullptr;
+}
+
+already_AddRefed<PeerConnectionImpl>
+PeerConnectionImpl::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& rv)
+{
+ RefPtr<PeerConnectionImpl> pc = new PeerConnectionImpl(&aGlobal);
+
+ CSFLogDebug(logTag, "Created PeerConnection: %p", pc.get());
+
+ return pc.forget();
+}
+
+PeerConnectionImpl* PeerConnectionImpl::CreatePeerConnection()
+{
+ PeerConnectionImpl *pc = new PeerConnectionImpl();
+
+ CSFLogDebug(logTag, "Created PeerConnection: %p", pc);
+
+ return pc;
+}
+
+NS_IMETHODIMP PeerConnectionMedia::ProtocolProxyQueryHandler::
+OnProxyAvailable(nsICancelable *request,
+ nsIChannel *aChannel,
+ nsIProxyInfo *proxyinfo,
+ nsresult result) {
+
+ if (!pcm_->mProxyRequest) {
+ // PeerConnectionMedia is no longer waiting
+ return NS_OK;
+ }
+
+ CSFLogInfo(logTag, "%s: Proxy Available: %d", __FUNCTION__, (int)result);
+
+ if (NS_SUCCEEDED(result) && proxyinfo) {
+ SetProxyOnPcm(*proxyinfo);
+ }
+
+ pcm_->mProxyResolveCompleted = true;
+ pcm_->mProxyRequest = nullptr;
+ pcm_->FlushIceCtxOperationQueueIfReady();
+
+ return NS_OK;
+}
+
+void
+PeerConnectionMedia::ProtocolProxyQueryHandler::SetProxyOnPcm(
+ nsIProxyInfo& proxyinfo)
+{
+ CSFLogInfo(logTag, "%s: Had proxyinfo", __FUNCTION__);
+ nsresult rv;
+ nsCString httpsProxyHost;
+ int32_t httpsProxyPort;
+
+ rv = proxyinfo.GetHost(httpsProxyHost);
+ if (NS_FAILED(rv)) {
+ CSFLogError(logTag, "%s: Failed to get proxy server host", __FUNCTION__);
+ return;
+ }
+
+ rv = proxyinfo.GetPort(&httpsProxyPort);
+ if (NS_FAILED(rv)) {
+ CSFLogError(logTag, "%s: Failed to get proxy server port", __FUNCTION__);
+ return;
+ }
+
+ if (pcm_->mIceCtxHdlr.get()) {
+ assert(httpsProxyPort >= 0 && httpsProxyPort < (1 << 16));
+ // Note that this could check if PrivacyRequested() is set on the PC and
+ // remove "webrtc" from the ALPN list. But that would only work if the PC
+ // was constructed with a peerIdentity constraint, not when isolated
+ // streams are added. If we ever need to signal to the proxy that the
+ // media is isolated, then we would need to restructure this code.
+ pcm_->mProxyServer.reset(
+ new NrIceProxyServer(httpsProxyHost.get(),
+ static_cast<uint16_t>(httpsProxyPort),
+ "webrtc,c-webrtc"));
+ } else {
+ CSFLogError(logTag, "%s: Failed to set proxy server (ICE ctx unavailable)",
+ __FUNCTION__);
+ }
+}
+
+NS_IMPL_ISUPPORTS(PeerConnectionMedia::ProtocolProxyQueryHandler, nsIProtocolProxyCallback)
+
+PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl *parent)
+ : mParent(parent),
+ mParentHandle(parent->GetHandle()),
+ mParentName(parent->GetName()),
+ mIceCtxHdlr(nullptr),
+ mDNSResolver(new NrIceResolver()),
+ mUuidGen(MakeUnique<PCUuidGenerator>()),
+ mMainThread(mParent->GetMainThread()),
+ mSTSThread(mParent->GetSTSThread()),
+ mProxyResolveCompleted(false),
+ mIceRestartState(ICE_RESTART_NONE) {
+}
+
+nsresult
+PeerConnectionMedia::InitProxy()
+{
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ // Allow mochitests to disable this, since mochitest configures a fake proxy
+ // that serves up content.
+ bool disable = Preferences::GetBool("media.peerconnection.disable_http_proxy",
+ false);
+ if (disable) {
+ mProxyResolveCompleted = true;
+ return NS_OK;
+ }
+#endif
+
+ nsresult rv;
+ nsCOMPtr<nsIProtocolProxyService> pps =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ CSFLogError(logTag, "%s: Failed to get proxy service: %d", __FUNCTION__, (int)rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ // We use the following URL to find the "default" proxy address for all HTTPS
+ // connections. We will only attempt one HTTP(S) CONNECT per peer connection.
+ // "example.com" is guaranteed to be unallocated and should return the best default.
+ nsCOMPtr<nsIURI> fakeHttpsLocation;
+ rv = NS_NewURI(getter_AddRefs(fakeHttpsLocation), "https://example.com");
+ if (NS_FAILED(rv)) {
+ CSFLogError(logTag, "%s: Failed to set URI: %d", __FUNCTION__, (int)rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIScriptSecurityManager> secMan(
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ CSFLogError(logTag, "%s: Failed to get IOService: %d",
+ __FUNCTION__, (int)rv);
+ CSFLogError(logTag, "%s: Failed to get securityManager: %d", __FUNCTION__, (int)rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIPrincipal> systemPrincipal;
+ rv = secMan->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
+ if (NS_FAILED(rv)) {
+ CSFLogError(logTag, "%s: Failed to get systemPrincipal: %d", __FUNCTION__, (int)rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ fakeHttpsLocation,
+ systemPrincipal,
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER);
+
+ if (NS_FAILED(rv)) {
+ CSFLogError(logTag, "%s: Failed to get channel from URI: %d",
+ __FUNCTION__, (int)rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<ProtocolProxyQueryHandler> handler = new ProtocolProxyQueryHandler(this);
+ rv = pps->AsyncResolve(channel,
+ nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
+ nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
+ handler, getter_AddRefs(mProxyRequest));
+ if (NS_FAILED(rv)) {
+ CSFLogError(logTag, "%s: Failed to resolve protocol proxy: %d", __FUNCTION__, (int)rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_servers,
+ const std::vector<NrIceTurnServer>& turn_servers,
+ NrIceCtx::Policy policy)
+{
+ nsresult rv = InitProxy();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ bool ice_tcp = Preferences::GetBool("media.peerconnection.ice.tcp", false);
+#else
+ bool ice_tcp = false;
+#endif
+
+ // TODO(ekr@rtfm.com): need some way to set not offerer later
+ // Looks like a bug in the NrIceCtx API.
+ mIceCtxHdlr = NrIceCtxHandler::Create("PC:" + mParentName,
+ true, // Offerer
+ mParent->GetAllowIceLoopback(),
+ ice_tcp,
+ mParent->GetAllowIceLinkLocal(),
+ policy);
+ if(!mIceCtxHdlr) {
+ CSFLogError(logTag, "%s: Failed to create Ice Context", __FUNCTION__);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (NS_FAILED(rv = mIceCtxHdlr->ctx()->SetStunServers(stun_servers))) {
+ CSFLogError(logTag, "%s: Failed to set stun servers", __FUNCTION__);
+ return rv;
+ }
+ // Give us a way to globally turn off TURN support
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ bool disabled = Preferences::GetBool("media.peerconnection.turn.disable", false);
+#else
+ bool disabled = false;
+#endif
+ if (!disabled) {
+ if (NS_FAILED(rv = mIceCtxHdlr->ctx()->SetTurnServers(turn_servers))) {
+ CSFLogError(logTag, "%s: Failed to set turn servers", __FUNCTION__);
+ return rv;
+ }
+ } else if (turn_servers.size() != 0) {
+ CSFLogError(logTag, "%s: Setting turn servers disabled", __FUNCTION__);
+ }
+ if (NS_FAILED(rv = mDNSResolver->Init())) {
+ CSFLogError(logTag, "%s: Failed to initialize dns resolver", __FUNCTION__);
+ return rv;
+ }
+ if (NS_FAILED(rv =
+ mIceCtxHdlr->ctx()->SetResolver(mDNSResolver->AllocateResolver()))) {
+ CSFLogError(logTag, "%s: Failed to get dns resolver", __FUNCTION__);
+ return rv;
+ }
+ ConnectSignals(mIceCtxHdlr->ctx().get());
+
+ return NS_OK;
+}
+
+void
+PeerConnectionMedia::EnsureTransports(const JsepSession& aSession)
+{
+ auto transports = aSession.GetTransports();
+ for (size_t i = 0; i < transports.size(); ++i) {
+ RefPtr<JsepTransport> transport = transports[i];
+ RUN_ON_THREAD(
+ GetSTSThread(),
+ WrapRunnable(RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::EnsureTransport_s,
+ i,
+ transport->mComponents),
+ NS_DISPATCH_NORMAL);
+ }
+
+ GatherIfReady();
+}
+
+void
+PeerConnectionMedia::EnsureTransport_s(size_t aLevel, size_t aComponentCount)
+{
+ RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->GetStream(aLevel));
+ if (!stream) {
+ CSFLogDebug(logTag, "%s: Creating ICE media stream=%u components=%u",
+ mParentHandle.c_str(),
+ static_cast<unsigned>(aLevel),
+ static_cast<unsigned>(aComponentCount));
+
+ std::ostringstream os;
+ os << mParentName << " aLevel=" << aLevel;
+ RefPtr<NrIceMediaStream> stream =
+ mIceCtxHdlr->CreateStream(os.str().c_str(),
+ aComponentCount);
+
+ if (!stream) {
+ CSFLogError(logTag, "Failed to create ICE stream.");
+ return;
+ }
+
+ stream->SetLevel(aLevel);
+ stream->SignalReady.connect(this, &PeerConnectionMedia::IceStreamReady_s);
+ stream->SignalCandidate.connect(this,
+ &PeerConnectionMedia::OnCandidateFound_s);
+ mIceCtxHdlr->ctx()->SetStream(aLevel, stream);
+ }
+}
+
+void
+PeerConnectionMedia::ActivateOrRemoveTransports(const JsepSession& aSession)
+{
+ auto transports = aSession.GetTransports();
+ for (size_t i = 0; i < transports.size(); ++i) {
+ RefPtr<JsepTransport> transport = transports[i];
+
+ std::string ufrag;
+ std::string pwd;
+ std::vector<std::string> candidates;
+
+ if (transport->mComponents) {
+ MOZ_ASSERT(transport->mIce);
+ CSFLogDebug(logTag, "Transport %u is active", static_cast<unsigned>(i));
+ ufrag = transport->mIce->GetUfrag();
+ pwd = transport->mIce->GetPassword();
+ candidates = transport->mIce->GetCandidates();
+ } else {
+ CSFLogDebug(logTag, "Transport %u is disabled", static_cast<unsigned>(i));
+ // Make sure the MediaPipelineFactory doesn't try to use these.
+ RemoveTransportFlow(i, false);
+ RemoveTransportFlow(i, true);
+ }
+
+ RUN_ON_THREAD(
+ GetSTSThread(),
+ WrapRunnable(RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::ActivateOrRemoveTransport_s,
+ i,
+ transport->mComponents,
+ ufrag,
+ pwd,
+ candidates),
+ NS_DISPATCH_NORMAL);
+ }
+
+ // We can have more streams than m-lines due to rollback.
+ RUN_ON_THREAD(
+ GetSTSThread(),
+ WrapRunnable(RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::RemoveTransportsAtOrAfter_s,
+ transports.size()),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionMedia::ActivateOrRemoveTransport_s(
+ size_t aMLine,
+ size_t aComponentCount,
+ const std::string& aUfrag,
+ const std::string& aPassword,
+ const std::vector<std::string>& aCandidateList) {
+
+ if (!aComponentCount) {
+ CSFLogDebug(logTag, "%s: Removing ICE media stream=%u",
+ mParentHandle.c_str(),
+ static_cast<unsigned>(aMLine));
+ mIceCtxHdlr->ctx()->SetStream(aMLine, nullptr);
+ return;
+ }
+
+ RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->GetStream(aMLine));
+ if (!stream) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ if (!stream->HasParsedAttributes()) {
+ CSFLogDebug(logTag, "%s: Activating ICE media stream=%u components=%u",
+ mParentHandle.c_str(),
+ static_cast<unsigned>(aMLine),
+ static_cast<unsigned>(aComponentCount));
+
+ std::vector<std::string> attrs;
+ for (auto i = aCandidateList.begin(); i != aCandidateList.end(); ++i) {
+ attrs.push_back("candidate:" + *i);
+ }
+ attrs.push_back("ice-ufrag:" + aUfrag);
+ attrs.push_back("ice-pwd:" + aPassword);
+
+ nsresult rv = stream->ParseAttributes(attrs);
+ if (NS_FAILED(rv)) {
+ CSFLogError(logTag, "Couldn't parse ICE attributes, rv=%u",
+ static_cast<unsigned>(rv));
+ }
+
+ for (size_t c = aComponentCount; c < stream->components(); ++c) {
+ // components are 1-indexed
+ stream->DisableComponent(c + 1);
+ }
+ }
+}
+
+void
+PeerConnectionMedia::RemoveTransportsAtOrAfter_s(size_t aMLine)
+{
+ for (size_t i = aMLine; i < mIceCtxHdlr->ctx()->GetStreamCount(); ++i) {
+ mIceCtxHdlr->ctx()->SetStream(i, nullptr);
+ }
+}
+
+nsresult PeerConnectionMedia::UpdateMediaPipelines(
+ const JsepSession& session) {
+ auto trackPairs = session.GetNegotiatedTrackPairs();
+ MediaPipelineFactory factory(this);
+ nsresult rv;
+
+ for (auto i = trackPairs.begin(); i != trackPairs.end(); ++i) {
+ JsepTrackPair pair = *i;
+
+ if (pair.mReceiving) {
+ rv = factory.CreateOrUpdateMediaPipeline(pair, *pair.mReceiving);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ if (pair.mSending) {
+ rv = factory.CreateOrUpdateMediaPipeline(pair, *pair.mSending);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+
+ for (auto& stream : mRemoteSourceStreams) {
+ stream->StartReceiving();
+ }
+
+ return NS_OK;
+}
+
+void
+PeerConnectionMedia::StartIceChecks(const JsepSession& aSession)
+{
+ nsCOMPtr<nsIRunnable> runnable(
+ WrapRunnable(
+ RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::StartIceChecks_s,
+ aSession.IsIceControlling(),
+ aSession.RemoteIsIceLite(),
+ // Copy, just in case API changes to return a ref
+ std::vector<std::string>(aSession.GetIceOptions())));
+
+ PerformOrEnqueueIceCtxOperation(runnable);
+}
+
+void
+PeerConnectionMedia::StartIceChecks_s(
+ bool aIsControlling,
+ bool aIsIceLite,
+ const std::vector<std::string>& aIceOptionsList) {
+
+ CSFLogDebug(logTag, "Starting ICE Checking");
+
+ std::vector<std::string> attributes;
+ if (aIsIceLite) {
+ attributes.push_back("ice-lite");
+ }
+
+ if (!aIceOptionsList.empty()) {
+ attributes.push_back("ice-options:");
+ for (auto i = aIceOptionsList.begin(); i != aIceOptionsList.end(); ++i) {
+ attributes.back() += *i + ' ';
+ }
+ }
+
+ nsresult rv = mIceCtxHdlr->ctx()->ParseGlobalAttributes(attributes);
+ if (NS_FAILED(rv)) {
+ CSFLogError(logTag, "%s: couldn't parse global parameters", __FUNCTION__ );
+ }
+
+ mIceCtxHdlr->ctx()->SetControlling(aIsControlling ?
+ NrIceCtx::ICE_CONTROLLING :
+ NrIceCtx::ICE_CONTROLLED);
+
+ mIceCtxHdlr->ctx()->StartChecks();
+}
+
+bool
+PeerConnectionMedia::IsIceRestarting() const
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ return (mIceRestartState != ICE_RESTART_NONE);
+}
+
+PeerConnectionMedia::IceRestartState
+PeerConnectionMedia::GetIceRestartState() const
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ return mIceRestartState;
+}
+
+void
+PeerConnectionMedia::BeginIceRestart(const std::string& ufrag,
+ const std::string& pwd)
+{
+ ASSERT_ON_THREAD(mMainThread);
+ if (IsIceRestarting()) {
+ return;
+ }
+
+ RefPtr<NrIceCtx> new_ctx = mIceCtxHdlr->CreateCtx(ufrag, pwd);
+
+ RUN_ON_THREAD(GetSTSThread(),
+ WrapRunnable(
+ RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::BeginIceRestart_s,
+ new_ctx),
+ NS_DISPATCH_NORMAL);
+
+ mIceRestartState = ICE_RESTART_PROVISIONAL;
+}
+
+void
+PeerConnectionMedia::BeginIceRestart_s(RefPtr<NrIceCtx> new_ctx)
+{
+ ASSERT_ON_THREAD(mSTSThread);
+
+ // hold the original context so we can disconnect signals if needed
+ RefPtr<NrIceCtx> originalCtx = mIceCtxHdlr->ctx();
+
+ if (mIceCtxHdlr->BeginIceRestart(new_ctx)) {
+ ConnectSignals(mIceCtxHdlr->ctx().get(), originalCtx.get());
+ }
+}
+
+void
+PeerConnectionMedia::CommitIceRestart()
+{
+ ASSERT_ON_THREAD(mMainThread);
+ if (mIceRestartState != ICE_RESTART_PROVISIONAL) {
+ return;
+ }
+
+ mIceRestartState = ICE_RESTART_COMMITTED;
+}
+
+void
+PeerConnectionMedia::FinalizeIceRestart()
+{
+ ASSERT_ON_THREAD(mMainThread);
+ if (!IsIceRestarting()) {
+ return;
+ }
+
+ RUN_ON_THREAD(GetSTSThread(),
+ WrapRunnable(
+ RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::FinalizeIceRestart_s),
+ NS_DISPATCH_NORMAL);
+
+ mIceRestartState = ICE_RESTART_NONE;
+}
+
+void
+PeerConnectionMedia::FinalizeIceRestart_s()
+{
+ ASSERT_ON_THREAD(mSTSThread);
+
+ // reset old streams since we don't need them anymore
+ for (auto i = mTransportFlows.begin();
+ i != mTransportFlows.end();
+ ++i) {
+ RefPtr<TransportFlow> aFlow = i->second;
+ if (!aFlow) continue;
+ TransportLayerIce* ice =
+ static_cast<TransportLayerIce*>(aFlow->GetLayer(TransportLayerIce::ID()));
+ ice->ResetOldStream();
+ }
+
+ mIceCtxHdlr->FinalizeIceRestart();
+}
+
+void
+PeerConnectionMedia::RollbackIceRestart()
+{
+ ASSERT_ON_THREAD(mMainThread);
+ if (mIceRestartState != ICE_RESTART_PROVISIONAL) {
+ return;
+ }
+
+ RUN_ON_THREAD(GetSTSThread(),
+ WrapRunnable(
+ RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::RollbackIceRestart_s),
+ NS_DISPATCH_NORMAL);
+
+ mIceRestartState = ICE_RESTART_NONE;
+}
+
+void
+PeerConnectionMedia::RollbackIceRestart_s()
+{
+ ASSERT_ON_THREAD(mSTSThread);
+
+ // hold the restart context so we can disconnect signals
+ RefPtr<NrIceCtx> restartCtx = mIceCtxHdlr->ctx();
+
+ // restore old streams since we're rolling back
+ for (auto i = mTransportFlows.begin();
+ i != mTransportFlows.end();
+ ++i) {
+ RefPtr<TransportFlow> aFlow = i->second;
+ if (!aFlow) continue;
+ TransportLayerIce* ice =
+ static_cast<TransportLayerIce*>(aFlow->GetLayer(TransportLayerIce::ID()));
+ ice->RestoreOldStream();
+ }
+
+ mIceCtxHdlr->RollbackIceRestart();
+ ConnectSignals(mIceCtxHdlr->ctx().get(), restartCtx.get());
+}
+
+bool
+PeerConnectionMedia::GetPrefDefaultAddressOnly() const
+{
+ ASSERT_ON_THREAD(mMainThread); // will crash on STS thread
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ uint64_t winId = mParent->GetWindow()->WindowID();
+
+ bool default_address_only = Preferences::GetBool(
+ "media.peerconnection.ice.default_address_only", false);
+ default_address_only |=
+ !MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId);
+#else
+ bool default_address_only = true;
+#endif
+ return default_address_only;
+}
+
+bool
+PeerConnectionMedia::GetPrefProxyOnly() const
+{
+ ASSERT_ON_THREAD(mMainThread); // will crash on STS thread
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ return Preferences::GetBool("media.peerconnection.ice.proxy_only", false);
+#else
+ return false;
+#endif
+}
+
+void
+PeerConnectionMedia::ConnectSignals(NrIceCtx *aCtx, NrIceCtx *aOldCtx)
+{
+ aCtx->SignalGatheringStateChange.connect(
+ this,
+ &PeerConnectionMedia::IceGatheringStateChange_s);
+ aCtx->SignalConnectionStateChange.connect(
+ this,
+ &PeerConnectionMedia::IceConnectionStateChange_s);
+
+ if (aOldCtx) {
+ MOZ_ASSERT(aCtx != aOldCtx);
+ aOldCtx->SignalGatheringStateChange.disconnect(this);
+ aOldCtx->SignalConnectionStateChange.disconnect(this);
+
+ // if the old and new connection state and/or gathering state is
+ // different fire the state update. Note: we don't fire the update
+ // if the state is *INIT since updates for the INIT state aren't
+ // sent during the normal flow. (mjf)
+ if (aOldCtx->connection_state() != aCtx->connection_state() &&
+ aCtx->connection_state() != NrIceCtx::ICE_CTX_INIT) {
+ aCtx->SignalConnectionStateChange(aCtx, aCtx->connection_state());
+ }
+
+ if (aOldCtx->gathering_state() != aCtx->gathering_state() &&
+ aCtx->gathering_state() != NrIceCtx::ICE_CTX_GATHER_INIT) {
+ aCtx->SignalGatheringStateChange(aCtx, aCtx->gathering_state());
+ }
+ }
+}
+
+void
+PeerConnectionMedia::AddIceCandidate(const std::string& candidate,
+ const std::string& mid,
+ uint32_t aMLine) {
+ RUN_ON_THREAD(GetSTSThread(),
+ WrapRunnable(
+ RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::AddIceCandidate_s,
+ std::string(candidate), // Make copies.
+ std::string(mid),
+ aMLine),
+ NS_DISPATCH_NORMAL);
+}
+void
+PeerConnectionMedia::AddIceCandidate_s(const std::string& aCandidate,
+ const std::string& aMid,
+ uint32_t aMLine) {
+ RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->GetStream(aMLine));
+ if (!stream) {
+ CSFLogError(logTag, "No ICE stream for candidate at level %u: %s",
+ static_cast<unsigned>(aMLine), aCandidate.c_str());
+ return;
+ }
+
+ nsresult rv = stream->ParseTrickleCandidate(aCandidate);
+ if (NS_FAILED(rv)) {
+ CSFLogError(logTag, "Couldn't process ICE candidate at level %u",
+ static_cast<unsigned>(aMLine));
+ return;
+ }
+}
+
+void
+PeerConnectionMedia::FlushIceCtxOperationQueueIfReady()
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (IsIceCtxReady()) {
+ for (auto i = mQueuedIceCtxOperations.begin();
+ i != mQueuedIceCtxOperations.end();
+ ++i) {
+ GetSTSThread()->Dispatch(*i, NS_DISPATCH_NORMAL);
+ }
+ mQueuedIceCtxOperations.clear();
+ }
+}
+
+void
+PeerConnectionMedia::PerformOrEnqueueIceCtxOperation(nsIRunnable* runnable)
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (IsIceCtxReady()) {
+ GetSTSThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
+ } else {
+ mQueuedIceCtxOperations.push_back(runnable);
+ }
+}
+
+void
+PeerConnectionMedia::GatherIfReady() {
+ ASSERT_ON_THREAD(mMainThread);
+
+ nsCOMPtr<nsIRunnable> runnable(WrapRunnable(
+ RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::EnsureIceGathering_s,
+ GetPrefDefaultAddressOnly(),
+ GetPrefProxyOnly()));
+
+ PerformOrEnqueueIceCtxOperation(runnable);
+}
+
+void
+PeerConnectionMedia::EnsureIceGathering_s(bool aDefaultRouteOnly,
+ bool aProxyOnly) {
+ if (mProxyServer) {
+ mIceCtxHdlr->ctx()->SetProxyServer(*mProxyServer);
+ } else if (aProxyOnly) {
+ IceGatheringStateChange_s(mIceCtxHdlr->ctx().get(),
+ NrIceCtx::ICE_CTX_GATHER_COMPLETE);
+ return;
+ }
+
+ // Start gathering, but only if there are streams
+ for (size_t i = 0; i < mIceCtxHdlr->ctx()->GetStreamCount(); ++i) {
+ if (mIceCtxHdlr->ctx()->GetStream(i)) {
+ mIceCtxHdlr->ctx()->StartGathering(aDefaultRouteOnly, aProxyOnly);
+ return;
+ }
+ }
+
+ // 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.
+ IceGatheringStateChange_s(mIceCtxHdlr->ctx().get(),
+ NrIceCtx::ICE_CTX_GATHER_COMPLETE);
+}
+
+nsresult
+PeerConnectionMedia::AddTrack(DOMMediaStream& aMediaStream,
+ const std::string& streamId,
+ MediaStreamTrack& aTrack,
+ const std::string& trackId)
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ CSFLogDebug(logTag, "%s: MediaStream: %p", __FUNCTION__, &aMediaStream);
+
+ RefPtr<LocalSourceStreamInfo> localSourceStream =
+ GetLocalStreamById(streamId);
+
+ if (!localSourceStream) {
+ localSourceStream = new LocalSourceStreamInfo(&aMediaStream, this, streamId);
+ mLocalSourceStreams.AppendElement(localSourceStream);
+ }
+
+ localSourceStream->AddTrack(trackId, &aTrack);
+ return NS_OK;
+}
+
+nsresult
+PeerConnectionMedia::RemoveLocalTrack(const std::string& streamId,
+ const std::string& trackId)
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ CSFLogDebug(logTag, "%s: stream: %s track: %s", __FUNCTION__,
+ streamId.c_str(), trackId.c_str());
+
+ RefPtr<LocalSourceStreamInfo> localSourceStream =
+ GetLocalStreamById(streamId);
+ if (!localSourceStream) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ localSourceStream->RemoveTrack(trackId);
+ if (!localSourceStream->GetTrackCount()) {
+ mLocalSourceStreams.RemoveElement(localSourceStream);
+ }
+ return NS_OK;
+}
+
+nsresult
+PeerConnectionMedia::RemoveRemoteTrack(const std::string& streamId,
+ const std::string& trackId)
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ CSFLogDebug(logTag, "%s: stream: %s track: %s", __FUNCTION__,
+ streamId.c_str(), trackId.c_str());
+
+ RefPtr<RemoteSourceStreamInfo> remoteSourceStream =
+ GetRemoteStreamById(streamId);
+ if (!remoteSourceStream) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ remoteSourceStream->RemoveTrack(trackId);
+ if (!remoteSourceStream->GetTrackCount()) {
+ mRemoteSourceStreams.RemoveElement(remoteSourceStream);
+ }
+ return NS_OK;
+}
+
+void
+PeerConnectionMedia::SelfDestruct()
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ CSFLogDebug(logTag, "%s: ", __FUNCTION__);
+
+ // Shut down the media
+ for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) {
+ mLocalSourceStreams[i]->DetachMedia_m();
+ }
+
+ for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) {
+ mRemoteSourceStreams[i]->DetachMedia_m();
+ }
+
+ if (mProxyRequest) {
+ mProxyRequest->Cancel(NS_ERROR_ABORT);
+ mProxyRequest = nullptr;
+ }
+
+ // Shutdown the transport (async)
+ RUN_ON_THREAD(mSTSThread, WrapRunnable(
+ this, &PeerConnectionMedia::ShutdownMediaTransport_s),
+ NS_DISPATCH_NORMAL);
+
+ CSFLogDebug(logTag, "%s: Media shut down", __FUNCTION__);
+}
+
+void
+PeerConnectionMedia::SelfDestruct_m()
+{
+ CSFLogDebug(logTag, "%s: ", __FUNCTION__);
+
+ ASSERT_ON_THREAD(mMainThread);
+
+ mLocalSourceStreams.Clear();
+ mRemoteSourceStreams.Clear();
+
+ mMainThread = nullptr;
+
+ // Final self-destruct.
+ this->Release();
+}
+
+void
+PeerConnectionMedia::ShutdownMediaTransport_s()
+{
+ ASSERT_ON_THREAD(mSTSThread);
+
+ CSFLogDebug(logTag, "%s: ", __FUNCTION__);
+
+ // Here we access m{Local|Remote}SourceStreams off the main thread.
+ // That's OK because by here PeerConnectionImpl has forgotten about us,
+ // so there is no chance of getting a call in here from outside.
+ // The dispatches from SelfDestruct() and to SelfDestruct_m() provide
+ // memory barriers that protect us from badness.
+ for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) {
+ mLocalSourceStreams[i]->DetachTransport_s();
+ }
+
+ for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) {
+ mRemoteSourceStreams[i]->DetachTransport_s();
+ }
+
+ disconnect_all();
+ mTransportFlows.clear();
+ mIceCtxHdlr = nullptr;
+
+ mMainThread->Dispatch(WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m),
+ NS_DISPATCH_NORMAL);
+}
+
+LocalSourceStreamInfo*
+PeerConnectionMedia::GetLocalStreamByIndex(int aIndex)
+{
+ ASSERT_ON_THREAD(mMainThread);
+ if(aIndex < 0 || aIndex >= (int) mLocalSourceStreams.Length()) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(mLocalSourceStreams[aIndex]);
+ return mLocalSourceStreams[aIndex];
+}
+
+LocalSourceStreamInfo*
+PeerConnectionMedia::GetLocalStreamById(const std::string& id)
+{
+ ASSERT_ON_THREAD(mMainThread);
+ for (size_t i = 0; i < mLocalSourceStreams.Length(); ++i) {
+ if (id == mLocalSourceStreams[i]->GetId()) {
+ return mLocalSourceStreams[i];
+ }
+ }
+
+ return nullptr;
+}
+
+LocalSourceStreamInfo*
+PeerConnectionMedia::GetLocalStreamByTrackId(const std::string& id)
+{
+ ASSERT_ON_THREAD(mMainThread);
+ for (RefPtr<LocalSourceStreamInfo>& info : mLocalSourceStreams) {
+ if (info->HasTrack(id)) {
+ return info;
+ }
+ }
+
+ return nullptr;
+}
+
+RemoteSourceStreamInfo*
+PeerConnectionMedia::GetRemoteStreamByIndex(size_t aIndex)
+{
+ ASSERT_ON_THREAD(mMainThread);
+ MOZ_ASSERT(mRemoteSourceStreams.SafeElementAt(aIndex));
+ return mRemoteSourceStreams.SafeElementAt(aIndex);
+}
+
+RemoteSourceStreamInfo*
+PeerConnectionMedia::GetRemoteStreamById(const std::string& id)
+{
+ ASSERT_ON_THREAD(mMainThread);
+ for (size_t i = 0; i < mRemoteSourceStreams.Length(); ++i) {
+ if (id == mRemoteSourceStreams[i]->GetId()) {
+ return mRemoteSourceStreams[i];
+ }
+ }
+
+ return nullptr;
+}
+
+RemoteSourceStreamInfo*
+PeerConnectionMedia::GetRemoteStreamByTrackId(const std::string& id)
+{
+ ASSERT_ON_THREAD(mMainThread);
+ for (RefPtr<RemoteSourceStreamInfo>& info : mRemoteSourceStreams) {
+ if (info->HasTrack(id)) {
+ return info;
+ }
+ }
+
+ return nullptr;
+}
+
+
+nsresult
+PeerConnectionMedia::AddRemoteStream(RefPtr<RemoteSourceStreamInfo> aInfo)
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ mRemoteSourceStreams.AppendElement(aInfo);
+
+ return NS_OK;
+}
+
+void
+PeerConnectionMedia::IceGatheringStateChange_s(NrIceCtx* ctx,
+ NrIceCtx::GatheringState state)
+{
+ ASSERT_ON_THREAD(mSTSThread);
+
+ if (state == NrIceCtx::ICE_CTX_GATHER_COMPLETE) {
+ // Fire off EndOfLocalCandidates for each stream
+ for (size_t i = 0; ; ++i) {
+ RefPtr<NrIceMediaStream> stream(ctx->GetStream(i));
+ if (!stream) {
+ break;
+ }
+
+ NrIceCandidate candidate;
+ NrIceCandidate rtcpCandidate;
+ GetDefaultCandidates(*stream, &candidate, &rtcpCandidate);
+ EndOfLocalCandidates(candidate.cand_addr.host,
+ candidate.cand_addr.port,
+ rtcpCandidate.cand_addr.host,
+ rtcpCandidate.cand_addr.port,
+ i);
+ }
+ }
+
+ // ShutdownMediaTransport_s has not run yet because it unhooks this function
+ // from its signal, which means that SelfDestruct_m has not been dispatched
+ // yet either, so this PCMedia will still be around when this dispatch reaches
+ // main.
+ GetMainThread()->Dispatch(
+ WrapRunnable(this,
+ &PeerConnectionMedia::IceGatheringStateChange_m,
+ ctx,
+ state),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionMedia::IceConnectionStateChange_s(NrIceCtx* ctx,
+ NrIceCtx::ConnectionState state)
+{
+ ASSERT_ON_THREAD(mSTSThread);
+ // ShutdownMediaTransport_s has not run yet because it unhooks this function
+ // from its signal, which means that SelfDestruct_m has not been dispatched
+ // yet either, so this PCMedia will still be around when this dispatch reaches
+ // main.
+ GetMainThread()->Dispatch(
+ WrapRunnable(this,
+ &PeerConnectionMedia::IceConnectionStateChange_m,
+ ctx,
+ state),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionMedia::OnCandidateFound_s(NrIceMediaStream *aStream,
+ const std::string &aCandidateLine)
+{
+ ASSERT_ON_THREAD(mSTSThread);
+ MOZ_ASSERT(aStream);
+ MOZ_RELEASE_ASSERT(mIceCtxHdlr);
+
+ CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str());
+
+ NrIceCandidate candidate;
+ NrIceCandidate rtcpCandidate;
+ GetDefaultCandidates(*aStream, &candidate, &rtcpCandidate);
+
+ // ShutdownMediaTransport_s has not run yet because it unhooks this function
+ // from its signal, which means that SelfDestruct_m has not been dispatched
+ // yet either, so this PCMedia will still be around when this dispatch reaches
+ // main.
+ GetMainThread()->Dispatch(
+ WrapRunnable(this,
+ &PeerConnectionMedia::OnCandidateFound_m,
+ aCandidateLine,
+ candidate.cand_addr.host,
+ candidate.cand_addr.port,
+ rtcpCandidate.cand_addr.host,
+ rtcpCandidate.cand_addr.port,
+ aStream->GetLevel()),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionMedia::EndOfLocalCandidates(const std::string& aDefaultAddr,
+ uint16_t aDefaultPort,
+ const std::string& aDefaultRtcpAddr,
+ uint16_t aDefaultRtcpPort,
+ uint16_t aMLine)
+{
+ GetMainThread()->Dispatch(
+ WrapRunnable(this,
+ &PeerConnectionMedia::EndOfLocalCandidates_m,
+ aDefaultAddr,
+ aDefaultPort,
+ aDefaultRtcpAddr,
+ aDefaultRtcpPort,
+ aMLine),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionMedia::GetDefaultCandidates(const NrIceMediaStream& aStream,
+ NrIceCandidate* aCandidate,
+ NrIceCandidate* aRtcpCandidate)
+{
+ nsresult res = aStream.GetDefaultCandidate(1, aCandidate);
+ // Optional; component won't exist if doing rtcp-mux
+ if (NS_FAILED(aStream.GetDefaultCandidate(2, aRtcpCandidate))) {
+ aRtcpCandidate->cand_addr.host.clear();
+ aRtcpCandidate->cand_addr.port = 0;
+ }
+ if (NS_FAILED(res)) {
+ aCandidate->cand_addr.host.clear();
+ aCandidate->cand_addr.port = 0;
+ CSFLogError(logTag, "%s: GetDefaultCandidates failed for level %u, "
+ "res=%u",
+ __FUNCTION__,
+ static_cast<unsigned>(aStream.GetLevel()),
+ static_cast<unsigned>(res));
+ }
+}
+
+void
+PeerConnectionMedia::IceGatheringStateChange_m(NrIceCtx* ctx,
+ NrIceCtx::GatheringState state)
+{
+ ASSERT_ON_THREAD(mMainThread);
+ SignalIceGatheringStateChange(ctx, state);
+}
+
+void
+PeerConnectionMedia::IceConnectionStateChange_m(NrIceCtx* ctx,
+ NrIceCtx::ConnectionState state)
+{
+ ASSERT_ON_THREAD(mMainThread);
+ SignalIceConnectionStateChange(ctx, state);
+}
+
+void
+PeerConnectionMedia::IceStreamReady_s(NrIceMediaStream *aStream)
+{
+ MOZ_ASSERT(aStream);
+
+ CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str());
+}
+
+void
+PeerConnectionMedia::OnCandidateFound_m(const std::string& aCandidateLine,
+ const std::string& aDefaultAddr,
+ uint16_t aDefaultPort,
+ const std::string& aDefaultRtcpAddr,
+ uint16_t aDefaultRtcpPort,
+ uint16_t aMLine)
+{
+ ASSERT_ON_THREAD(mMainThread);
+ if (!aDefaultAddr.empty()) {
+ SignalUpdateDefaultCandidate(aDefaultAddr,
+ aDefaultPort,
+ aDefaultRtcpAddr,
+ aDefaultRtcpPort,
+ aMLine);
+ }
+ SignalCandidate(aCandidateLine, aMLine);
+}
+
+void
+PeerConnectionMedia::EndOfLocalCandidates_m(const std::string& aDefaultAddr,
+ uint16_t aDefaultPort,
+ const std::string& aDefaultRtcpAddr,
+ uint16_t aDefaultRtcpPort,
+ uint16_t aMLine) {
+ ASSERT_ON_THREAD(mMainThread);
+ if (!aDefaultAddr.empty()) {
+ SignalUpdateDefaultCandidate(aDefaultAddr,
+ aDefaultPort,
+ aDefaultRtcpAddr,
+ aDefaultRtcpPort,
+ aMLine);
+ }
+ SignalEndOfLocalCandidates(aMLine);
+}
+
+void
+PeerConnectionMedia::DtlsConnected_s(TransportLayer *layer,
+ TransportLayer::State state)
+{
+ MOZ_ASSERT(layer->id() == "dtls");
+ TransportLayerDtls* dtlsLayer = static_cast<TransportLayerDtls*>(layer);
+ dtlsLayer->SignalStateChange.disconnect(this);
+
+ bool privacyRequested = (dtlsLayer->GetNegotiatedAlpn() == "c-webrtc");
+ GetMainThread()->Dispatch(
+ WrapRunnableNM(&PeerConnectionMedia::DtlsConnected_m,
+ mParentHandle, privacyRequested),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionMedia::DtlsConnected_m(const std::string& aParentHandle,
+ bool aPrivacyRequested)
+{
+ PeerConnectionWrapper pcWrapper(aParentHandle);
+ PeerConnectionImpl* pc = pcWrapper.impl();
+ if (pc) {
+ pc->SetDtlsConnected(aPrivacyRequested);
+ }
+}
+
+void
+PeerConnectionMedia::AddTransportFlow(int aIndex, bool aRtcp,
+ const RefPtr<TransportFlow> &aFlow)
+{
+ int index_inner = GetTransportFlowIndex(aIndex, aRtcp);
+
+ MOZ_ASSERT(!mTransportFlows[index_inner]);
+ mTransportFlows[index_inner] = aFlow;
+
+ GetSTSThread()->Dispatch(
+ WrapRunnable(this, &PeerConnectionMedia::ConnectDtlsListener_s, aFlow),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionMedia::RemoveTransportFlow(int aIndex, bool aRtcp)
+{
+ int index_inner = GetTransportFlowIndex(aIndex, aRtcp);
+ NS_ProxyRelease(GetSTSThread(), mTransportFlows[index_inner].forget());
+}
+
+void
+PeerConnectionMedia::ConnectDtlsListener_s(const RefPtr<TransportFlow>& aFlow)
+{
+ TransportLayer* dtls = aFlow->GetLayer(TransportLayerDtls::ID());
+ if (dtls) {
+ dtls->SignalStateChange.connect(this, &PeerConnectionMedia::DtlsConnected_s);
+ }
+}
+
+nsresult
+LocalSourceStreamInfo::TakePipelineFrom(RefPtr<LocalSourceStreamInfo>& info,
+ const std::string& oldTrackId,
+ MediaStreamTrack& aNewTrack,
+ const std::string& newTrackId)
+{
+ if (mPipelines.count(newTrackId)) {
+ CSFLogError(logTag, "%s: Pipeline already exists for %s/%s",
+ __FUNCTION__, mId.c_str(), newTrackId.c_str());
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ RefPtr<MediaPipeline> pipeline(info->ForgetPipelineByTrackId_m(oldTrackId));
+
+ if (!pipeline) {
+ // Replacetrack can potentially happen in the middle of offer/answer, before
+ // the pipeline has been created.
+ CSFLogInfo(logTag, "%s: Replacing track before the pipeline has been "
+ "created, nothing to do.", __FUNCTION__);
+ return NS_OK;
+ }
+
+ nsresult rv =
+ static_cast<MediaPipelineTransmit*>(pipeline.get())->ReplaceTrack(aNewTrack);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mPipelines[newTrackId] = pipeline;
+
+ return NS_OK;
+}
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+/**
+ * Tells you if any local track is isolated to a specific peer identity.
+ * Obviously, we want all the tracks to be isolated equally so that they can
+ * all be sent or not. We check once when we are setting a local description
+ * and that determines if we flip the "privacy requested" bit on. Once the bit
+ * is on, all media originating from this peer connection is isolated.
+ *
+ * @returns true if any track has a peerIdentity set on it
+ */
+bool
+PeerConnectionMedia::AnyLocalTrackHasPeerIdentity() const
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) {
+ for (auto pair : mLocalSourceStreams[u]->GetMediaStreamTracks()) {
+ if (pair.second->GetPeerIdentity() != nullptr) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void
+PeerConnectionMedia::UpdateRemoteStreamPrincipals_m(nsIPrincipal* aPrincipal)
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ for (uint32_t u = 0; u < mRemoteSourceStreams.Length(); u++) {
+ mRemoteSourceStreams[u]->UpdatePrincipal_m(aPrincipal);
+ }
+}
+
+void
+PeerConnectionMedia::UpdateSinkIdentity_m(MediaStreamTrack* aTrack,
+ nsIPrincipal* aPrincipal,
+ const PeerIdentity* aSinkIdentity)
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) {
+ mLocalSourceStreams[u]->UpdateSinkIdentity_m(aTrack, aPrincipal,
+ aSinkIdentity);
+ }
+}
+
+void
+LocalSourceStreamInfo::UpdateSinkIdentity_m(MediaStreamTrack* aTrack,
+ nsIPrincipal* aPrincipal,
+ const PeerIdentity* aSinkIdentity)
+{
+ for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) {
+ MediaPipelineTransmit* pipeline =
+ static_cast<MediaPipelineTransmit*>((*it).second.get());
+ pipeline->UpdateSinkIdentity_m(aTrack, aPrincipal, aSinkIdentity);
+ }
+}
+
+void RemoteSourceStreamInfo::UpdatePrincipal_m(nsIPrincipal* aPrincipal)
+{
+ // This blasts away the existing principal.
+ // We only do this when we become certain that the all tracks are safe to make
+ // accessible to the script principal.
+ for (auto& trackPair : mTracks) {
+ MOZ_RELEASE_ASSERT(trackPair.second);
+ RemoteTrackSource& source =
+ static_cast<RemoteTrackSource&>(trackPair.second->GetSource());
+ source.SetPrincipal(aPrincipal);
+
+ RefPtr<MediaPipeline> pipeline = GetPipelineByTrackId_m(trackPair.first);
+ if (pipeline) {
+ MOZ_ASSERT(pipeline->direction() == MediaPipeline::RECEIVE);
+ static_cast<MediaPipelineReceive*>(pipeline.get())
+ ->SetPrincipalHandle_m(MakePrincipalHandle(aPrincipal));
+ }
+ }
+}
+#endif // MOZILLA_INTERNAL_API
+
+bool
+PeerConnectionMedia::AnyCodecHasPluginID(uint64_t aPluginID)
+{
+ for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) {
+ if (mLocalSourceStreams[i]->AnyCodecHasPluginID(aPluginID)) {
+ return true;
+ }
+ }
+ for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) {
+ if (mRemoteSourceStreams[i]->AnyCodecHasPluginID(aPluginID)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+SourceStreamInfo::AnyCodecHasPluginID(uint64_t aPluginID)
+{
+ // Scan the videoConduits for this plugin ID
+ for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) {
+ if (it->second->Conduit()->CodecPluginID() == aPluginID) {
+ return true;
+ }
+ }
+ return false;
+}
+
+nsresult
+SourceStreamInfo::StorePipeline(
+ const std::string& trackId,
+ const RefPtr<mozilla::MediaPipeline>& aPipeline)
+{
+ MOZ_ASSERT(mPipelines.find(trackId) == mPipelines.end());
+ if (mPipelines.find(trackId) != mPipelines.end()) {
+ CSFLogError(logTag, "%s: Storing duplicate track", __FUNCTION__);
+ return NS_ERROR_FAILURE;
+ }
+
+ mPipelines[trackId] = aPipeline;
+ return NS_OK;
+}
+
+void
+RemoteSourceStreamInfo::DetachMedia_m()
+{
+ for (auto& webrtcIdAndTrack : mTracks) {
+ EndTrack(mMediaStream->GetInputStream(), webrtcIdAndTrack.second);
+ }
+ SourceStreamInfo::DetachMedia_m();
+}
+
+void
+RemoteSourceStreamInfo::RemoveTrack(const std::string& trackId)
+{
+ auto it = mTracks.find(trackId);
+ if (it != mTracks.end()) {
+ EndTrack(mMediaStream->GetInputStream(), it->second);
+ }
+
+ SourceStreamInfo::RemoveTrack(trackId);
+}
+
+void
+RemoteSourceStreamInfo::SyncPipeline(
+ RefPtr<MediaPipelineReceive> aPipeline)
+{
+ // See if we have both audio and video here, and if so cross the streams and
+ // sync them
+ // TODO: Do we need to prevent multiple syncs if there is more than one audio
+ // or video track in a single media stream? What are we supposed to do in this
+ // case?
+ for (auto i = mPipelines.begin(); i != mPipelines.end(); ++i) {
+ if (i->second->IsVideo() != aPipeline->IsVideo()) {
+ // Ok, we have one video, one non-video - cross the streams!
+ WebrtcAudioConduit *audio_conduit =
+ static_cast<WebrtcAudioConduit*>(aPipeline->IsVideo() ?
+ i->second->Conduit() :
+ aPipeline->Conduit());
+ WebrtcVideoConduit *video_conduit =
+ static_cast<WebrtcVideoConduit*>(aPipeline->IsVideo() ?
+ aPipeline->Conduit() :
+ i->second->Conduit());
+ video_conduit->SyncTo(audio_conduit);
+ CSFLogDebug(logTag, "Syncing %p to %p, %s to %s",
+ video_conduit, audio_conduit,
+ i->first.c_str(), aPipeline->trackid().c_str());
+ }
+ }
+}
+
+void
+RemoteSourceStreamInfo::StartReceiving()
+{
+ if (mReceiving || mPipelines.empty()) {
+ return;
+ }
+
+ mReceiving = true;
+
+ SourceMediaStream* source = GetMediaStream()->GetInputStream()->AsSourceStream();
+ source->SetPullEnabled(true);
+ // AdvanceKnownTracksTicksTime(HEAT_DEATH_OF_UNIVERSE) means that in
+ // theory per the API, we can't add more tracks before that
+ // time. However, the impl actually allows it, and it avoids a whole
+ // bunch of locking that would be required (and potential blocking)
+ // if we used smaller values and updated them on each NotifyPull.
+ source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
+ CSFLogDebug(logTag, "Finished adding tracks to MediaStream %p", source);
+}
+
+RefPtr<MediaPipeline> SourceStreamInfo::GetPipelineByTrackId_m(
+ const std::string& trackId) {
+ ASSERT_ON_THREAD(mParent->GetMainThread());
+
+ // Refuse to hand out references if we're tearing down.
+ // (Since teardown involves a dispatch to and from STS before MediaPipelines
+ // are released, it is safe to start other dispatches to and from STS with a
+ // RefPtr<MediaPipeline>, since that reference won't be the last one
+ // standing)
+ if (mMediaStream) {
+ if (mPipelines.count(trackId)) {
+ return mPipelines[trackId];
+ }
+ }
+
+ return nullptr;
+}
+
+already_AddRefed<MediaPipeline>
+LocalSourceStreamInfo::ForgetPipelineByTrackId_m(const std::string& trackId)
+{
+ ASSERT_ON_THREAD(mParent->GetMainThread());
+
+ // Refuse to hand out references if we're tearing down.
+ // (Since teardown involves a dispatch to and from STS before MediaPipelines
+ // are released, it is safe to start other dispatches to and from STS with a
+ // RefPtr<MediaPipeline>, since that reference won't be the last one
+ // standing)
+ if (mMediaStream) {
+ if (mPipelines.count(trackId)) {
+ RefPtr<MediaPipeline> pipeline(mPipelines[trackId]);
+ mPipelines.erase(trackId);
+ return pipeline.forget();
+ }
+ }
+
+ return nullptr;
+}
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+auto
+RemoteTrackSource::ApplyConstraints(
+ nsPIDOMWindowInner* aWindow,
+ const dom::MediaTrackConstraints& aConstraints) -> already_AddRefed<PledgeVoid>
+{
+ RefPtr<PledgeVoid> p = new PledgeVoid();
+ p->Reject(new dom::MediaStreamError(aWindow,
+ NS_LITERAL_STRING("OverconstrainedError"),
+ NS_LITERAL_STRING("")));
+ return p.forget();
+}
+#endif
+
+} // namespace mozilla