1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include <ostream>
6 #include <string>
7 #include <vector>
8
9 #include "CSFLog.h"
10
11 #include "nspr.h"
12
13 #include "nricectx.h"
14 #include "nricemediastream.h"
15 #include "MediaPipelineFilter.h"
16 #include "MediaPipeline.h"
17 #include "PeerConnectionImpl.h"
18 #include "PeerConnectionMedia.h"
19 #include "runnable_utils.h"
20 #include "transportlayerice.h"
21 #include "transportlayerdtls.h"
22 #include "signaling/src/jsep/JsepSession.h"
23 #include "signaling/src/jsep/JsepTransport.h"
24
25 #include "nsContentUtils.h"
26 #include "nsNetCID.h"
27 #include "nsNetUtil.h"
28 #include "nsIURI.h"
29 #include "nsIScriptSecurityManager.h"
30 #include "nsICancelable.h"
31 #include "nsILoadInfo.h"
32 #include "nsIContentPolicy.h"
33 #include "nsIProxyInfo.h"
34 #include "nsIProtocolProxyService.h"
35
36 #include "nsProxyRelease.h"
37
38 #include "nsIScriptGlobalObject.h"
39 #include "mozilla/Preferences.h"
40 #include "mozilla/Telemetry.h"
41 #include "MediaManager.h"
42 #include "WebrtcGmpVideoCodec.h"
43
44 namespace mozilla {
45 using namespace dom;
46
47 static const char* pcmLogTag = "PeerConnectionMedia";
48 #ifdef LOGTAG
49 #undef LOGTAG
50 #endif
51 #define LOGTAG pcmLogTag
52
OnProxyAvailable(nsICancelable * request,nsIChannel * aChannel,nsIProxyInfo * proxyinfo,nsresult result)53 NS_IMETHODIMP PeerConnectionMedia::ProtocolProxyQueryHandler::OnProxyAvailable(
54 nsICancelable* request, nsIChannel* aChannel, nsIProxyInfo* proxyinfo,
55 nsresult result) {
56 if (!pcm_->mProxyRequest) {
57 // PeerConnectionMedia is no longer waiting
58 return NS_OK;
59 }
60
61 CSFLogInfo(LOGTAG, "%s: Proxy Available: %d", __FUNCTION__, (int)result);
62
63 if (NS_SUCCEEDED(result) && proxyinfo) {
64 SetProxyOnPcm(*proxyinfo);
65 }
66
67 pcm_->mProxyResolveCompleted = true;
68 pcm_->mProxyRequest = nullptr;
69 pcm_->FlushIceCtxOperationQueueIfReady();
70
71 return NS_OK;
72 }
73
SetProxyOnPcm(nsIProxyInfo & proxyinfo)74 void PeerConnectionMedia::ProtocolProxyQueryHandler::SetProxyOnPcm(
75 nsIProxyInfo& proxyinfo) {
76 CSFLogInfo(LOGTAG, "%s: Had proxyinfo", __FUNCTION__);
77 nsresult rv;
78 nsCString httpsProxyHost;
79 int32_t httpsProxyPort;
80
81 rv = proxyinfo.GetHost(httpsProxyHost);
82 if (NS_FAILED(rv)) {
83 CSFLogError(LOGTAG, "%s: Failed to get proxy server host", __FUNCTION__);
84 return;
85 }
86
87 rv = proxyinfo.GetPort(&httpsProxyPort);
88 if (NS_FAILED(rv)) {
89 CSFLogError(LOGTAG, "%s: Failed to get proxy server port", __FUNCTION__);
90 return;
91 }
92
93 if (pcm_->mIceCtxHdlr.get()) {
94 assert(httpsProxyPort >= 0 && httpsProxyPort < (1 << 16));
95 // Note that this could check if PrivacyRequested() is set on the PC and
96 // remove "webrtc" from the ALPN list. But that would only work if the PC
97 // was constructed with a peerIdentity constraint, not when isolated
98 // streams are added. If we ever need to signal to the proxy that the
99 // media is isolated, then we would need to restructure this code.
100 pcm_->mProxyServer.reset(new NrIceProxyServer(
101 httpsProxyHost.get(), static_cast<uint16_t>(httpsProxyPort),
102 "webrtc,c-webrtc"));
103 } else {
104 CSFLogError(LOGTAG, "%s: Failed to set proxy server (ICE ctx unavailable)",
105 __FUNCTION__);
106 }
107 }
108
NS_IMPL_ISUPPORTS(PeerConnectionMedia::ProtocolProxyQueryHandler,nsIProtocolProxyCallback)109 NS_IMPL_ISUPPORTS(PeerConnectionMedia::ProtocolProxyQueryHandler,
110 nsIProtocolProxyCallback)
111
112 void PeerConnectionMedia::StunAddrsHandler::OnStunAddrsAvailable(
113 const mozilla::net::NrIceStunAddrArray& addrs) {
114 CSFLogInfo(LOGTAG, "%s: receiving (%d) stun addrs", __FUNCTION__,
115 (int)addrs.Length());
116 if (pcm_) {
117 pcm_->mStunAddrs = addrs;
118 pcm_->mLocalAddrsCompleted = true;
119 pcm_->mStunAddrsRequest = nullptr;
120 pcm_->FlushIceCtxOperationQueueIfReady();
121 // If parent process returns 0 STUN addresses, change ICE connection
122 // state to failed.
123 if (!pcm_->mStunAddrs.Length()) {
124 pcm_->SignalIceConnectionStateChange(pcm_->mIceCtxHdlr->ctx().get(),
125 NrIceCtx::ICE_CTX_FAILED);
126 }
127
128 pcm_ = nullptr;
129 }
130 }
131
PeerConnectionMedia(PeerConnectionImpl * parent)132 PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl* parent)
133 : mParent(parent),
134 mParentHandle(parent->GetHandle()),
135 mParentName(parent->GetName()),
136 mIceCtxHdlr(nullptr),
137 mDNSResolver(new NrIceResolver()),
138 mUuidGen(MakeUnique<PCUuidGenerator>()),
139 mMainThread(mParent->GetMainThread()),
140 mSTSThread(mParent->GetSTSThread()),
141 mProxyResolveCompleted(false),
142 mIceRestartState(ICE_RESTART_NONE),
143 mLocalAddrsCompleted(false) {}
144
~PeerConnectionMedia()145 PeerConnectionMedia::~PeerConnectionMedia() {
146 MOZ_RELEASE_ASSERT(!mMainThread);
147 }
148
InitLocalAddrs()149 void PeerConnectionMedia::InitLocalAddrs() {
150 if (XRE_IsContentProcess()) {
151 CSFLogDebug(LOGTAG, "%s: Get stun addresses via IPC",
152 mParentHandle.c_str());
153
154 nsCOMPtr<nsIEventTarget> target =
155 mParent->GetWindow()
156 ? mParent->GetWindow()->EventTargetFor(TaskCategory::Other)
157 : nullptr;
158
159 // We're in the content process, so send a request over IPC for the
160 // stun address discovery.
161 mStunAddrsRequest =
162 new StunAddrsRequestChild(new StunAddrsHandler(this), target);
163 mStunAddrsRequest->SendGetStunAddrs();
164 } else {
165 // No content process, so don't need to hold up the ice event queue
166 // until completion of stun address discovery. We can let the
167 // discovery of stun addresses happen in the same process.
168 mLocalAddrsCompleted = true;
169 }
170 }
171
InitProxy()172 nsresult PeerConnectionMedia::InitProxy() {
173 // Allow mochitests to disable this, since mochitest configures a fake proxy
174 // that serves up content.
175 bool disable =
176 Preferences::GetBool("media.peerconnection.disable_http_proxy", false);
177 if (disable) {
178 mProxyResolveCompleted = true;
179 return NS_OK;
180 }
181
182 nsresult rv;
183 nsCOMPtr<nsIProtocolProxyService> pps =
184 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
185 if (NS_FAILED(rv)) {
186 CSFLogError(LOGTAG, "%s: Failed to get proxy service: %d", __FUNCTION__,
187 (int)rv);
188 return NS_ERROR_FAILURE;
189 }
190
191 // We use the following URL to find the "default" proxy address for all HTTPS
192 // connections. We will only attempt one HTTP(S) CONNECT per peer connection.
193 // "example.com" is guaranteed to be unallocated and should return the best
194 // default.
195 nsCOMPtr<nsIURI> fakeHttpsLocation;
196 rv = NS_NewURI(getter_AddRefs(fakeHttpsLocation), "https://example.com");
197 if (NS_FAILED(rv)) {
198 CSFLogError(LOGTAG, "%s: Failed to set URI: %d", __FUNCTION__, (int)rv);
199 return NS_ERROR_FAILURE;
200 }
201
202 nsCOMPtr<nsIChannel> channel;
203 rv = NS_NewChannel(getter_AddRefs(channel), fakeHttpsLocation,
204 nsContentUtils::GetSystemPrincipal(),
205 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
206 nsIContentPolicy::TYPE_OTHER);
207
208 if (NS_FAILED(rv)) {
209 CSFLogError(LOGTAG, "%s: Failed to get channel from URI: %d", __FUNCTION__,
210 (int)rv);
211 return NS_ERROR_FAILURE;
212 }
213
214 nsCOMPtr<nsIEventTarget> target =
215 mParent->GetWindow()
216 ? mParent->GetWindow()->EventTargetFor(TaskCategory::Network)
217 : nullptr;
218 RefPtr<ProtocolProxyQueryHandler> handler =
219 new ProtocolProxyQueryHandler(this);
220 rv = pps->AsyncResolve(channel,
221 nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
222 nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
223 handler, target, getter_AddRefs(mProxyRequest));
224 if (NS_FAILED(rv)) {
225 CSFLogError(LOGTAG, "%s: Failed to resolve protocol proxy: %d",
226 __FUNCTION__, (int)rv);
227 return NS_ERROR_FAILURE;
228 }
229
230 return NS_OK;
231 }
232
Init(const std::vector<NrIceStunServer> & stun_servers,const std::vector<NrIceTurnServer> & turn_servers,NrIceCtx::Policy policy)233 nsresult PeerConnectionMedia::Init(
234 const std::vector<NrIceStunServer>& stun_servers,
235 const std::vector<NrIceTurnServer>& turn_servers, NrIceCtx::Policy policy) {
236 nsresult rv = InitProxy();
237 NS_ENSURE_SUCCESS(rv, rv);
238
239 bool ice_tcp = Preferences::GetBool("media.peerconnection.ice.tcp", false);
240
241 // setup the stun local addresses IPC async call
242 InitLocalAddrs();
243
244 // TODO(ekr@rtfm.com): need some way to set not offerer later
245 // Looks like a bug in the NrIceCtx API.
246 mIceCtxHdlr = NrIceCtxHandler::Create(
247 "PC:" + mParentName, mParent->GetAllowIceLoopback(), ice_tcp,
248 mParent->GetAllowIceLinkLocal(), policy);
249 if (!mIceCtxHdlr) {
250 CSFLogError(LOGTAG, "%s: Failed to create Ice Context", __FUNCTION__);
251 return NS_ERROR_FAILURE;
252 }
253
254 if (NS_FAILED(rv = mIceCtxHdlr->ctx()->SetStunServers(stun_servers))) {
255 CSFLogError(LOGTAG, "%s: Failed to set stun servers", __FUNCTION__);
256 return rv;
257 }
258 // Give us a way to globally turn off TURN support
259 bool disabled =
260 Preferences::GetBool("media.peerconnection.turn.disable", false);
261 if (!disabled) {
262 if (NS_FAILED(rv = mIceCtxHdlr->ctx()->SetTurnServers(turn_servers))) {
263 CSFLogError(LOGTAG, "%s: Failed to set turn servers", __FUNCTION__);
264 return rv;
265 }
266 } else if (!turn_servers.empty()) {
267 CSFLogError(LOGTAG, "%s: Setting turn servers disabled", __FUNCTION__);
268 }
269 if (NS_FAILED(rv = mDNSResolver->Init())) {
270 CSFLogError(LOGTAG, "%s: Failed to initialize dns resolver", __FUNCTION__);
271 return rv;
272 }
273 if (NS_FAILED(rv = mIceCtxHdlr->ctx()->SetResolver(
274 mDNSResolver->AllocateResolver()))) {
275 CSFLogError(LOGTAG, "%s: Failed to get dns resolver", __FUNCTION__);
276 return rv;
277 }
278 ConnectSignals(mIceCtxHdlr->ctx().get());
279
280 return NS_OK;
281 }
282
EnsureTransports(const JsepSession & aSession)283 void PeerConnectionMedia::EnsureTransports(const JsepSession& aSession) {
284 for (const auto& transceiver : aSession.GetTransceivers()) {
285 if (!transceiver->HasLevel()) {
286 continue;
287 }
288
289 RefPtr<JsepTransport> transport = transceiver->mTransport;
290 RUN_ON_THREAD(GetSTSThread(),
291 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
292 &PeerConnectionMedia::EnsureTransport_s,
293 transceiver->GetLevel(), transport->mComponents),
294 NS_DISPATCH_NORMAL);
295 }
296
297 GatherIfReady();
298 }
299
EnsureTransport_s(size_t aLevel,size_t aComponentCount)300 void PeerConnectionMedia::EnsureTransport_s(size_t aLevel,
301 size_t aComponentCount) {
302 RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->GetStream(aLevel));
303 if (!stream) {
304 CSFLogDebug(LOGTAG, "%s: Creating ICE media stream=%u components=%u",
305 mParentHandle.c_str(), static_cast<unsigned>(aLevel),
306 static_cast<unsigned>(aComponentCount));
307
308 std::ostringstream os;
309 os << mParentName << " aLevel=" << aLevel;
310 RefPtr<NrIceMediaStream> stream =
311 mIceCtxHdlr->CreateStream(os.str(), aComponentCount);
312
313 if (!stream) {
314 CSFLogError(LOGTAG, "Failed to create ICE stream.");
315 return;
316 }
317
318 stream->SetLevel(aLevel);
319 stream->SignalReady.connect(this, &PeerConnectionMedia::IceStreamReady_s);
320 stream->SignalCandidate.connect(this,
321 &PeerConnectionMedia::OnCandidateFound_s);
322 mIceCtxHdlr->ctx()->SetStream(aLevel, stream);
323 }
324 }
325
ActivateOrRemoveTransports(const JsepSession & aSession,const bool forceIceTcp)326 nsresult PeerConnectionMedia::ActivateOrRemoveTransports(
327 const JsepSession& aSession, const bool forceIceTcp) {
328 for (const auto& transceiver : aSession.GetTransceivers()) {
329 if (!transceiver->HasLevel()) {
330 continue;
331 }
332
333 std::string ufrag;
334 std::string pwd;
335 std::vector<std::string> candidates;
336 size_t components = 0;
337
338 RefPtr<JsepTransport> transport = transceiver->mTransport;
339 unsigned level = transceiver->GetLevel();
340
341 if (transport->mComponents && (!transceiver->HasBundleLevel() ||
342 (transceiver->BundleLevel() == level))) {
343 CSFLogDebug(LOGTAG,
344 "ACTIVATING TRANSPORT! - PC %s: level=%u components=%u",
345 mParentHandle.c_str(), (unsigned)level,
346 (unsigned)transport->mComponents);
347
348 ufrag = transport->mIce->GetUfrag();
349 pwd = transport->mIce->GetPassword();
350 candidates = transport->mIce->GetCandidates();
351 components = transport->mComponents;
352 if (forceIceTcp) {
353 candidates.erase(
354 std::remove_if(candidates.begin(), candidates.end(),
355 [](const std::string& s) {
356 return s.find(" UDP ") != std::string::npos ||
357 s.find(" udp ") != std::string::npos;
358 }),
359 candidates.end());
360 }
361 }
362
363 RUN_ON_THREAD(
364 GetSTSThread(),
365 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
366 &PeerConnectionMedia::ActivateOrRemoveTransport_s,
367 transceiver->GetLevel(), components, ufrag, pwd,
368 candidates),
369 NS_DISPATCH_NORMAL);
370 }
371
372 // We can have more streams than m-lines due to rollback.
373 RUN_ON_THREAD(GetSTSThread(),
374 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
375 &PeerConnectionMedia::RemoveTransportsAtOrAfter_s,
376 aSession.GetTransceivers().size()),
377 NS_DISPATCH_NORMAL);
378
379 return NS_OK;
380 }
381
UpdateTransceiverTransports(const JsepSession & aSession)382 nsresult PeerConnectionMedia::UpdateTransceiverTransports(
383 const JsepSession& aSession) {
384 for (const auto& transceiver : aSession.GetTransceivers()) {
385 nsresult rv = UpdateTransportFlows(*transceiver);
386 if (NS_FAILED(rv)) {
387 return rv;
388 }
389 }
390
391 for (const auto& transceiverImpl : mTransceivers) {
392 transceiverImpl->UpdateTransport(*this);
393 }
394
395 return NS_OK;
396 }
397
ActivateOrRemoveTransport_s(size_t aMLine,size_t aComponentCount,const std::string & aUfrag,const std::string & aPassword,const std::vector<std::string> & aCandidateList)398 void PeerConnectionMedia::ActivateOrRemoveTransport_s(
399 size_t aMLine, size_t aComponentCount, const std::string& aUfrag,
400 const std::string& aPassword,
401 const std::vector<std::string>& aCandidateList) {
402 if (!aComponentCount) {
403 CSFLogDebug(LOGTAG, "%s: Removing ICE media stream=%u",
404 mParentHandle.c_str(), static_cast<unsigned>(aMLine));
405 mIceCtxHdlr->ctx()->SetStream(aMLine, nullptr);
406 return;
407 }
408
409 RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->GetStream(aMLine));
410 if (!stream) {
411 MOZ_ASSERT(false);
412 return;
413 }
414
415 if (!stream->HasParsedAttributes()) {
416 CSFLogDebug(LOGTAG, "%s: Activating ICE media stream=%u components=%u",
417 mParentHandle.c_str(), static_cast<unsigned>(aMLine),
418 static_cast<unsigned>(aComponentCount));
419
420 std::vector<std::string> attrs;
421 attrs.reserve(aCandidateList.size() + 2 /* ufrag + pwd */);
422 for (const auto& candidate : aCandidateList) {
423 attrs.push_back("candidate:" + candidate);
424 }
425 attrs.push_back("ice-ufrag:" + aUfrag);
426 attrs.push_back("ice-pwd:" + aPassword);
427
428 nsresult rv = stream->ParseAttributes(attrs);
429 if (NS_FAILED(rv)) {
430 CSFLogError(LOGTAG, "Couldn't parse ICE attributes, rv=%u",
431 static_cast<unsigned>(rv));
432 }
433
434 for (size_t c = aComponentCount; c < stream->components(); ++c) {
435 // components are 1-indexed
436 stream->DisableComponent(c + 1);
437 }
438 }
439 }
440
RemoveTransportsAtOrAfter_s(size_t aMLine)441 void PeerConnectionMedia::RemoveTransportsAtOrAfter_s(size_t aMLine) {
442 for (size_t i = aMLine; i < mIceCtxHdlr->ctx()->GetStreamCount(); ++i) {
443 mIceCtxHdlr->ctx()->SetStream(i, nullptr);
444 }
445 }
446
UpdateMediaPipelines()447 nsresult PeerConnectionMedia::UpdateMediaPipelines() {
448 // The GMP code is all the way on the other side of webrtc.org, and it is not
449 // feasible to plumb error information all the way back. So, we set up a
450 // handle to the PC (for the duration of this call) in a global variable.
451 // This allows the GMP code to report errors to the PC.
452 WebrtcGmpPCHandleSetter setter(mParentHandle);
453
454 for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
455 nsresult rv = transceiver->UpdateConduit();
456 if (NS_FAILED(rv)) {
457 return rv;
458 }
459
460 if (!transceiver->IsVideo()) {
461 rv = transceiver->SyncWithMatchingVideoConduits(mTransceivers);
462 if (NS_FAILED(rv)) {
463 return rv;
464 }
465 // TODO: If there is no audio, we should probably de-sync. However, this
466 // has never been done before, and it is unclear whether it is safe...
467 }
468 }
469
470 return NS_OK;
471 }
472
UpdateTransportFlows(const JsepTransceiver & aTransceiver)473 nsresult PeerConnectionMedia::UpdateTransportFlows(
474 const JsepTransceiver& aTransceiver) {
475 if (!aTransceiver.HasLevel()) {
476 // Nothing to do
477 return NS_OK;
478 }
479
480 size_t transportLevel = aTransceiver.GetTransportLevel();
481
482 nsresult rv =
483 UpdateTransportFlow(transportLevel, false, *aTransceiver.mTransport);
484 if (NS_FAILED(rv)) {
485 return rv;
486 }
487
488 return UpdateTransportFlow(transportLevel, true, *aTransceiver.mTransport);
489 }
490
491 // Accessing the PCMedia should be safe here because we shouldn't
492 // have enqueued this function unless it was still active and
493 // the ICE data is destroyed on the STS.
FinalizeTransportFlow_s(RefPtr<PeerConnectionMedia> aPCMedia,RefPtr<TransportFlow> aFlow,size_t aLevel,bool aIsRtcp,nsAutoPtr<PtrVector<TransportLayer>> aLayerList)494 static void FinalizeTransportFlow_s(
495 RefPtr<PeerConnectionMedia> aPCMedia, RefPtr<TransportFlow> aFlow,
496 size_t aLevel, bool aIsRtcp,
497 nsAutoPtr<PtrVector<TransportLayer>> aLayerList) {
498 TransportLayerIce* ice =
499 static_cast<TransportLayerIce*>(aLayerList->values.front());
500 ice->SetParameters(aPCMedia->ice_media_stream(aLevel), aIsRtcp ? 2 : 1);
501 nsAutoPtr<std::queue<TransportLayer*>> layerQueue(
502 new std::queue<TransportLayer*>);
503 for (auto& value : aLayerList->values) {
504 layerQueue->push(value);
505 }
506 aLayerList->values.clear();
507 (void)aFlow->PushLayers(layerQueue); // TODO(bug 854518): Process errors.
508 }
509
AddNewIceStreamForRestart_s(RefPtr<PeerConnectionMedia> aPCMedia,RefPtr<TransportFlow> aFlow,size_t aLevel,bool aIsRtcp)510 static void AddNewIceStreamForRestart_s(RefPtr<PeerConnectionMedia> aPCMedia,
511 RefPtr<TransportFlow> aFlow,
512 size_t aLevel, bool aIsRtcp) {
513 TransportLayerIce* ice =
514 static_cast<TransportLayerIce*>(aFlow->GetLayer("ice"));
515 ice->SetParameters(aPCMedia->ice_media_stream(aLevel), aIsRtcp ? 2 : 1);
516 }
517
UpdateTransportFlow(size_t aLevel,bool aIsRtcp,const JsepTransport & aTransport)518 nsresult PeerConnectionMedia::UpdateTransportFlow(
519 size_t aLevel, bool aIsRtcp, const JsepTransport& aTransport) {
520 if (aIsRtcp && aTransport.mComponents < 2) {
521 RemoveTransportFlow(aLevel, aIsRtcp);
522 return NS_OK;
523 }
524
525 if (!aIsRtcp && !aTransport.mComponents) {
526 RemoveTransportFlow(aLevel, aIsRtcp);
527 return NS_OK;
528 }
529
530 nsresult rv;
531
532 RefPtr<TransportFlow> flow = GetTransportFlow(aLevel, aIsRtcp);
533 if (flow) {
534 if (IsIceRestarting()) {
535 CSFLogInfo(LOGTAG, "Flow[%s]: detected ICE restart - level: %u rtcp: %d",
536 flow->id().c_str(), (unsigned)aLevel, aIsRtcp);
537
538 RefPtr<PeerConnectionMedia> pcMedia(this);
539 rv = GetSTSThread()->Dispatch(
540 WrapRunnableNM(AddNewIceStreamForRestart_s, pcMedia, flow, aLevel,
541 aIsRtcp),
542 NS_DISPATCH_NORMAL);
543 if (NS_FAILED(rv)) {
544 CSFLogError(LOGTAG, "Failed to dispatch AddNewIceStreamForRestart_s");
545 return rv;
546 }
547 }
548
549 return NS_OK;
550 }
551
552 std::ostringstream osId;
553 osId << mParentHandle << ":" << aLevel << "," << (aIsRtcp ? "rtcp" : "rtp");
554 flow = new TransportFlow(osId.str());
555
556 // The media streams are made on STS so we need to defer setup.
557 auto ice = MakeUnique<TransportLayerIce>();
558 auto dtls = MakeUnique<TransportLayerDtls>();
559 dtls->SetRole(aTransport.mDtls->GetRole() ==
560 JsepDtlsTransport::kJsepDtlsClient
561 ? TransportLayerDtls::CLIENT
562 : TransportLayerDtls::SERVER);
563
564 RefPtr<DtlsIdentity> pcid = mParent->Identity();
565 if (!pcid) {
566 CSFLogError(LOGTAG, "Failed to get DTLS identity.");
567 return NS_ERROR_FAILURE;
568 }
569 dtls->SetIdentity(pcid);
570
571 const SdpFingerprintAttributeList& fingerprints =
572 aTransport.mDtls->GetFingerprints();
573 for (const auto& fingerprint : fingerprints.mFingerprints) {
574 std::ostringstream ss;
575 ss << fingerprint.hashFunc;
576 rv = dtls->SetVerificationDigest(ss.str(), &fingerprint.fingerprint[0],
577 fingerprint.fingerprint.size());
578 if (NS_FAILED(rv)) {
579 CSFLogError(LOGTAG, "Could not set fingerprint");
580 return rv;
581 }
582 }
583
584 std::vector<uint16_t> srtpCiphers;
585 srtpCiphers.push_back(SRTP_AES128_CM_HMAC_SHA1_80);
586 srtpCiphers.push_back(SRTP_AES128_CM_HMAC_SHA1_32);
587
588 rv = dtls->SetSrtpCiphers(srtpCiphers);
589 if (NS_FAILED(rv)) {
590 CSFLogError(LOGTAG, "Couldn't set SRTP ciphers");
591 return rv;
592 }
593
594 // Always permits negotiation of the confidential mode.
595 // Only allow non-confidential (which is an allowed default),
596 // if we aren't confidential.
597 std::set<std::string> alpn;
598 std::string alpnDefault = "";
599 alpn.insert("c-webrtc");
600 if (!mParent->PrivacyRequested()) {
601 alpnDefault = "webrtc";
602 alpn.insert(alpnDefault);
603 }
604 rv = dtls->SetAlpn(alpn, alpnDefault);
605 if (NS_FAILED(rv)) {
606 CSFLogError(LOGTAG, "Couldn't set ALPN");
607 return rv;
608 }
609
610 nsAutoPtr<PtrVector<TransportLayer>> layers(new PtrVector<TransportLayer>);
611 layers->values.push_back(ice.release());
612 layers->values.push_back(dtls.release());
613
614 RefPtr<PeerConnectionMedia> pcMedia(this);
615 rv = GetSTSThread()->Dispatch(WrapRunnableNM(FinalizeTransportFlow_s, pcMedia,
616 flow, aLevel, aIsRtcp, layers),
617 NS_DISPATCH_NORMAL);
618 if (NS_FAILED(rv)) {
619 CSFLogError(LOGTAG, "Failed to dispatch FinalizeTransportFlow_s");
620 return rv;
621 }
622
623 AddTransportFlow(aLevel, aIsRtcp, flow);
624
625 return NS_OK;
626 }
627
StartIceChecks(const JsepSession & aSession)628 void PeerConnectionMedia::StartIceChecks(const JsepSession& aSession) {
629 nsCOMPtr<nsIRunnable> runnable(WrapRunnable(
630 RefPtr<PeerConnectionMedia>(this), &PeerConnectionMedia::StartIceChecks_s,
631 aSession.IsIceControlling(), aSession.IsOfferer(),
632 aSession.RemoteIsIceLite(),
633 // Copy, just in case API changes to return a ref
634 std::vector<std::string>(aSession.GetIceOptions())));
635
636 PerformOrEnqueueIceCtxOperation(runnable);
637 }
638
StartIceChecks_s(bool aIsControlling,bool aIsOfferer,bool aIsIceLite,const std::vector<std::string> & aIceOptionsList)639 void PeerConnectionMedia::StartIceChecks_s(
640 bool aIsControlling, bool aIsOfferer, bool aIsIceLite,
641 const std::vector<std::string>& aIceOptionsList) {
642 CSFLogDebug(LOGTAG, "Starting ICE Checking");
643
644 std::vector<std::string> attributes;
645 if (aIsIceLite) {
646 attributes.push_back("ice-lite");
647 }
648
649 if (!aIceOptionsList.empty()) {
650 attributes.push_back("ice-options:");
651 for (const auto& option : aIceOptionsList) {
652 attributes.back() += option + ' ';
653 }
654 }
655
656 nsresult rv = mIceCtxHdlr->ctx()->ParseGlobalAttributes(attributes);
657 if (NS_FAILED(rv)) {
658 CSFLogError(LOGTAG, "%s: couldn't parse global parameters", __FUNCTION__);
659 }
660
661 mIceCtxHdlr->ctx()->SetControlling(aIsControlling ? NrIceCtx::ICE_CONTROLLING
662 : NrIceCtx::ICE_CONTROLLED);
663
664 mIceCtxHdlr->ctx()->StartChecks(aIsOfferer);
665 }
666
IsIceRestarting() const667 bool PeerConnectionMedia::IsIceRestarting() const {
668 ASSERT_ON_THREAD(mMainThread);
669
670 return (mIceRestartState != ICE_RESTART_NONE);
671 }
672
GetIceRestartState() const673 PeerConnectionMedia::IceRestartState PeerConnectionMedia::GetIceRestartState()
674 const {
675 ASSERT_ON_THREAD(mMainThread);
676
677 return mIceRestartState;
678 }
679
BeginIceRestart(const std::string & ufrag,const std::string & pwd)680 void PeerConnectionMedia::BeginIceRestart(const std::string& ufrag,
681 const std::string& pwd) {
682 ASSERT_ON_THREAD(mMainThread);
683 if (IsIceRestarting()) {
684 return;
685 }
686
687 RefPtr<NrIceCtx> new_ctx = mIceCtxHdlr->CreateCtx(ufrag, pwd);
688
689 RUN_ON_THREAD(GetSTSThread(),
690 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
691 &PeerConnectionMedia::BeginIceRestart_s, new_ctx),
692 NS_DISPATCH_NORMAL);
693
694 mIceRestartState = ICE_RESTART_PROVISIONAL;
695 }
696
BeginIceRestart_s(RefPtr<NrIceCtx> new_ctx)697 void PeerConnectionMedia::BeginIceRestart_s(RefPtr<NrIceCtx> new_ctx) {
698 ASSERT_ON_THREAD(mSTSThread);
699
700 // hold the original context so we can disconnect signals if needed
701 RefPtr<NrIceCtx> originalCtx = mIceCtxHdlr->ctx();
702
703 if (mIceCtxHdlr->BeginIceRestart(new_ctx)) {
704 ConnectSignals(mIceCtxHdlr->ctx().get(), originalCtx.get());
705 }
706 }
707
CommitIceRestart()708 void PeerConnectionMedia::CommitIceRestart() {
709 ASSERT_ON_THREAD(mMainThread);
710 if (mIceRestartState != ICE_RESTART_PROVISIONAL) {
711 return;
712 }
713
714 mIceRestartState = ICE_RESTART_COMMITTED;
715 }
716
FinalizeIceRestart()717 void PeerConnectionMedia::FinalizeIceRestart() {
718 ASSERT_ON_THREAD(mMainThread);
719 if (!IsIceRestarting()) {
720 return;
721 }
722
723 RUN_ON_THREAD(GetSTSThread(),
724 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
725 &PeerConnectionMedia::FinalizeIceRestart_s),
726 NS_DISPATCH_NORMAL);
727
728 mIceRestartState = ICE_RESTART_NONE;
729 }
730
FinalizeIceRestart_s()731 void PeerConnectionMedia::FinalizeIceRestart_s() {
732 ASSERT_ON_THREAD(mSTSThread);
733
734 // reset old streams since we don't need them anymore
735 for (auto& transportFlow : mTransportFlows) {
736 RefPtr<TransportFlow> aFlow = transportFlow.second;
737 if (!aFlow) continue;
738 TransportLayerIce* ice = static_cast<TransportLayerIce*>(
739 aFlow->GetLayer(TransportLayerIce::ID()));
740 ice->ResetOldStream();
741 }
742
743 mIceCtxHdlr->FinalizeIceRestart();
744 }
745
RollbackIceRestart()746 void PeerConnectionMedia::RollbackIceRestart() {
747 ASSERT_ON_THREAD(mMainThread);
748 if (mIceRestartState != ICE_RESTART_PROVISIONAL) {
749 return;
750 }
751
752 RUN_ON_THREAD(GetSTSThread(),
753 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
754 &PeerConnectionMedia::RollbackIceRestart_s),
755 NS_DISPATCH_NORMAL);
756
757 mIceRestartState = ICE_RESTART_NONE;
758 }
759
RollbackIceRestart_s()760 void PeerConnectionMedia::RollbackIceRestart_s() {
761 ASSERT_ON_THREAD(mSTSThread);
762
763 // hold the restart context so we can disconnect signals
764 RefPtr<NrIceCtx> restartCtx = mIceCtxHdlr->ctx();
765
766 // restore old streams since we're rolling back
767 for (auto& transportFlow : mTransportFlows) {
768 RefPtr<TransportFlow> aFlow = transportFlow.second;
769 if (!aFlow) continue;
770 TransportLayerIce* ice = static_cast<TransportLayerIce*>(
771 aFlow->GetLayer(TransportLayerIce::ID()));
772 ice->RestoreOldStream();
773 }
774
775 mIceCtxHdlr->RollbackIceRestart();
776 ConnectSignals(mIceCtxHdlr->ctx().get(), restartCtx.get());
777
778 // Fixup the telemetry by transferring abandoned ctx stats to current ctx.
779 NrIceStats stats = restartCtx->Destroy();
780 restartCtx = nullptr;
781 mIceCtxHdlr->ctx()->AccumulateStats(stats);
782 }
783
GetPrefDefaultAddressOnly() const784 bool PeerConnectionMedia::GetPrefDefaultAddressOnly() const {
785 ASSERT_ON_THREAD(mMainThread); // will crash on STS thread
786
787 uint64_t winId = mParent->GetWindow()->WindowID();
788
789 bool default_address_only = Preferences::GetBool(
790 "media.peerconnection.ice.default_address_only", false);
791 default_address_only |=
792 !MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId);
793 return default_address_only;
794 }
795
GetPrefProxyOnly() const796 bool PeerConnectionMedia::GetPrefProxyOnly() const {
797 ASSERT_ON_THREAD(mMainThread); // will crash on STS thread
798
799 return Preferences::GetBool("media.peerconnection.ice.proxy_only", false);
800 }
801
ConnectSignals(NrIceCtx * aCtx,NrIceCtx * aOldCtx)802 void PeerConnectionMedia::ConnectSignals(NrIceCtx* aCtx, NrIceCtx* aOldCtx) {
803 aCtx->SignalGatheringStateChange.connect(
804 this, &PeerConnectionMedia::IceGatheringStateChange_s);
805 aCtx->SignalConnectionStateChange.connect(
806 this, &PeerConnectionMedia::IceConnectionStateChange_s);
807
808 if (aOldCtx) {
809 MOZ_ASSERT(aCtx != aOldCtx);
810 aOldCtx->SignalGatheringStateChange.disconnect(this);
811 aOldCtx->SignalConnectionStateChange.disconnect(this);
812
813 // if the old and new connection state and/or gathering state is
814 // different fire the state update. Note: we don't fire the update
815 // if the state is *INIT since updates for the INIT state aren't
816 // sent during the normal flow. (mjf)
817 if (aOldCtx->connection_state() != aCtx->connection_state() &&
818 aCtx->connection_state() != NrIceCtx::ICE_CTX_INIT) {
819 aCtx->SignalConnectionStateChange(aCtx, aCtx->connection_state());
820 }
821
822 if (aOldCtx->gathering_state() != aCtx->gathering_state() &&
823 aCtx->gathering_state() != NrIceCtx::ICE_CTX_GATHER_INIT) {
824 aCtx->SignalGatheringStateChange(aCtx, aCtx->gathering_state());
825 }
826 }
827 }
828
AddIceCandidate(const std::string & candidate,const std::string & mid,uint32_t aMLine)829 void PeerConnectionMedia::AddIceCandidate(const std::string& candidate,
830 const std::string& mid,
831 uint32_t aMLine) {
832 RUN_ON_THREAD(GetSTSThread(),
833 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
834 &PeerConnectionMedia::AddIceCandidate_s,
835 std::string(candidate), // Make copies.
836 std::string(mid), aMLine),
837 NS_DISPATCH_NORMAL);
838 }
839
AddIceCandidate_s(const std::string & aCandidate,const std::string & aMid,uint32_t aMLine)840 void PeerConnectionMedia::AddIceCandidate_s(const std::string& aCandidate,
841 const std::string& aMid,
842 uint32_t aMLine) {
843 RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->GetStream(aMLine));
844 if (!stream) {
845 CSFLogError(LOGTAG, "No ICE stream for candidate at level %u: %s",
846 static_cast<unsigned>(aMLine), aCandidate.c_str());
847 return;
848 }
849
850 nsresult rv = stream->ParseTrickleCandidate(aCandidate);
851 if (NS_FAILED(rv)) {
852 CSFLogError(LOGTAG, "Couldn't process ICE candidate at level %u",
853 static_cast<unsigned>(aMLine));
854 return;
855 }
856 }
857
UpdateNetworkState(bool online)858 void PeerConnectionMedia::UpdateNetworkState(bool online) {
859 RUN_ON_THREAD(
860 GetSTSThread(),
861 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
862 &PeerConnectionMedia::UpdateNetworkState_s, online),
863 NS_DISPATCH_NORMAL);
864 }
865
UpdateNetworkState_s(bool online)866 void PeerConnectionMedia::UpdateNetworkState_s(bool online) {
867 mIceCtxHdlr->ctx()->UpdateNetworkState(online);
868 }
869
FlushIceCtxOperationQueueIfReady()870 void PeerConnectionMedia::FlushIceCtxOperationQueueIfReady() {
871 ASSERT_ON_THREAD(mMainThread);
872
873 if (IsIceCtxReady()) {
874 for (auto& mQueuedIceCtxOperation : mQueuedIceCtxOperations) {
875 GetSTSThread()->Dispatch(mQueuedIceCtxOperation, NS_DISPATCH_NORMAL);
876 }
877 mQueuedIceCtxOperations.clear();
878 }
879 }
880
PerformOrEnqueueIceCtxOperation(nsIRunnable * runnable)881 void PeerConnectionMedia::PerformOrEnqueueIceCtxOperation(
882 nsIRunnable* runnable) {
883 ASSERT_ON_THREAD(mMainThread);
884
885 if (IsIceCtxReady()) {
886 GetSTSThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
887 } else {
888 mQueuedIceCtxOperations.push_back(runnable);
889 }
890 }
891
GatherIfReady()892 void PeerConnectionMedia::GatherIfReady() {
893 ASSERT_ON_THREAD(mMainThread);
894
895 nsCOMPtr<nsIRunnable> runnable(
896 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
897 &PeerConnectionMedia::EnsureIceGathering_s,
898 GetPrefDefaultAddressOnly(), GetPrefProxyOnly()));
899
900 PerformOrEnqueueIceCtxOperation(runnable);
901 }
902
EnsureIceGathering_s(bool aDefaultRouteOnly,bool aProxyOnly)903 void PeerConnectionMedia::EnsureIceGathering_s(bool aDefaultRouteOnly,
904 bool aProxyOnly) {
905 if (mProxyServer) {
906 mIceCtxHdlr->ctx()->SetProxyServer(*mProxyServer);
907 } else if (aProxyOnly) {
908 IceGatheringStateChange_s(mIceCtxHdlr->ctx().get(),
909 NrIceCtx::ICE_CTX_GATHER_COMPLETE);
910 return;
911 }
912
913 // Make sure we don't call NrIceCtx::StartGathering if we're in e10s mode
914 // and we received no STUN addresses from the parent process. In the
915 // absence of previously provided STUN addresses, StartGathering will
916 // attempt to gather them (as in non-e10s mode), and this will cause a
917 // sandboxing exception in e10s mode.
918 if (!mStunAddrs.Length() && XRE_IsContentProcess()) {
919 CSFLogInfo(LOGTAG, "%s: No STUN addresses returned from parent process",
920 __FUNCTION__);
921 return;
922 }
923
924 // Belt and suspenders - in e10s mode, the call below to SetStunAddrs
925 // needs to have the proper flags set on ice ctx. For non-e10s,
926 // setting those flags happens in StartGathering. We could probably
927 // just set them here, and only do it here.
928 mIceCtxHdlr->ctx()->SetCtxFlags(aDefaultRouteOnly, aProxyOnly);
929
930 if (mStunAddrs.Length()) {
931 mIceCtxHdlr->ctx()->SetStunAddrs(mStunAddrs);
932 }
933
934 // Start gathering, but only if there are streams
935 for (size_t i = 0; i < mIceCtxHdlr->ctx()->GetStreamCount(); ++i) {
936 if (mIceCtxHdlr->ctx()->GetStream(i)) {
937 mIceCtxHdlr->ctx()->StartGathering(aDefaultRouteOnly, aProxyOnly);
938 return;
939 }
940 }
941
942 // If there are no streams, we're probably in a situation where we've rolled
943 // back while still waiting for our proxy configuration to come back. Make
944 // sure content knows that the rollback has stuck wrt gathering.
945 IceGatheringStateChange_s(mIceCtxHdlr->ctx().get(),
946 NrIceCtx::ICE_CTX_GATHER_COMPLETE);
947 }
948
SelfDestruct()949 void PeerConnectionMedia::SelfDestruct() {
950 ASSERT_ON_THREAD(mMainThread);
951
952 CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__);
953
954 if (mStunAddrsRequest) {
955 mStunAddrsRequest->Cancel();
956 mStunAddrsRequest = nullptr;
957 }
958
959 if (mProxyRequest) {
960 mProxyRequest->Cancel(NS_ERROR_ABORT);
961 mProxyRequest = nullptr;
962 }
963
964 for (auto& transceiver : mTransceivers) {
965 // transceivers are garbage-collected, so we need to poke them to perform
966 // cleanup right now so the appropriate events fire.
967 transceiver->Shutdown_m();
968 }
969
970 mTransceivers.clear();
971
972 mQueuedIceCtxOperations.clear();
973
974 // Shutdown the transport (async)
975 RUN_ON_THREAD(
976 mSTSThread,
977 WrapRunnable(this, &PeerConnectionMedia::ShutdownMediaTransport_s),
978 NS_DISPATCH_NORMAL);
979
980 CSFLogDebug(LOGTAG, "%s: Media shut down", __FUNCTION__);
981 }
982
SelfDestruct_m()983 void PeerConnectionMedia::SelfDestruct_m() {
984 CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__);
985
986 ASSERT_ON_THREAD(mMainThread);
987
988 mMainThread = nullptr;
989
990 // Final self-destruct.
991 this->Release();
992 }
993
ShutdownMediaTransport_s()994 void PeerConnectionMedia::ShutdownMediaTransport_s() {
995 ASSERT_ON_THREAD(mSTSThread);
996
997 CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__);
998
999 disconnect_all();
1000 mTransportFlows.clear();
1001
1002 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
1003 NrIceStats stats = mIceCtxHdlr->Destroy();
1004
1005 CSFLogDebug(LOGTAG,
1006 "Ice Telemetry: stun (retransmits: %d)"
1007 " turn (401s: %d 403s: %d 438s: %d)",
1008 stats.stun_retransmits, stats.turn_401s, stats.turn_403s,
1009 stats.turn_438s);
1010
1011 Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_STUN_RETRANSMITS,
1012 stats.stun_retransmits);
1013 Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_401S,
1014 stats.turn_401s);
1015 Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_403S,
1016 stats.turn_403s);
1017 Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_438S,
1018 stats.turn_438s);
1019 #endif
1020
1021 mIceCtxHdlr = nullptr;
1022
1023 // we're holding a ref to 'this' that's released by SelfDestruct_m
1024 mMainThread->Dispatch(
1025 WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m),
1026 NS_DISPATCH_NORMAL);
1027 }
1028
AddTransceiver(JsepTransceiver * aJsepTransceiver,dom::MediaStreamTrack & aReceiveTrack,dom::MediaStreamTrack * aSendTrack,RefPtr<TransceiverImpl> * aTransceiverImpl)1029 nsresult PeerConnectionMedia::AddTransceiver(
1030 JsepTransceiver* aJsepTransceiver, dom::MediaStreamTrack& aReceiveTrack,
1031 dom::MediaStreamTrack* aSendTrack,
1032 RefPtr<TransceiverImpl>* aTransceiverImpl) {
1033 if (!mCall) {
1034 mCall = WebRtcCallWrapper::Create();
1035 }
1036
1037 RefPtr<TransceiverImpl> transceiver = new TransceiverImpl(
1038 mParent->GetHandle(), aJsepTransceiver, mMainThread.get(),
1039 mSTSThread.get(), &aReceiveTrack, aSendTrack, mCall.get());
1040
1041 if (!transceiver->IsValid()) {
1042 return NS_ERROR_FAILURE;
1043 }
1044
1045 if (aSendTrack) {
1046 // implement checking for peerIdentity (where failure == black/silence)
1047 nsIDocument* doc = mParent->GetWindow()->GetExtantDoc();
1048 if (doc) {
1049 transceiver->UpdateSinkIdentity(nullptr, doc->NodePrincipal(),
1050 mParent->GetPeerIdentity());
1051 } else {
1052 MOZ_CRASH();
1053 return NS_ERROR_FAILURE; // Don't remove this till we know it's safe.
1054 }
1055 }
1056
1057 mTransceivers.push_back(transceiver);
1058 *aTransceiverImpl = transceiver;
1059
1060 return NS_OK;
1061 }
1062
GetTransmitPipelinesMatching(MediaStreamTrack * aTrack,nsTArray<RefPtr<MediaPipeline>> * aPipelines)1063 void PeerConnectionMedia::GetTransmitPipelinesMatching(
1064 MediaStreamTrack* aTrack, nsTArray<RefPtr<MediaPipeline>>* aPipelines) {
1065 for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1066 if (transceiver->HasSendTrack(aTrack)) {
1067 aPipelines->AppendElement(transceiver->GetSendPipeline());
1068 }
1069 }
1070
1071 if (!aPipelines->Length()) {
1072 CSFLogWarn(LOGTAG, "%s: none found for %p", __FUNCTION__, aTrack);
1073 }
1074 }
1075
GetReceivePipelinesMatching(MediaStreamTrack * aTrack,nsTArray<RefPtr<MediaPipeline>> * aPipelines)1076 void PeerConnectionMedia::GetReceivePipelinesMatching(
1077 MediaStreamTrack* aTrack, nsTArray<RefPtr<MediaPipeline>>* aPipelines) {
1078 for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1079 if (transceiver->HasReceiveTrack(aTrack)) {
1080 aPipelines->AppendElement(transceiver->GetReceivePipeline());
1081 }
1082 }
1083
1084 if (!aPipelines->Length()) {
1085 CSFLogWarn(LOGTAG, "%s: none found for %p", __FUNCTION__, aTrack);
1086 }
1087 }
1088
AddRIDExtension(MediaStreamTrack & aRecvTrack,unsigned short aExtensionId)1089 nsresult PeerConnectionMedia::AddRIDExtension(MediaStreamTrack& aRecvTrack,
1090 unsigned short aExtensionId) {
1091 for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1092 if (transceiver->HasReceiveTrack(&aRecvTrack)) {
1093 transceiver->AddRIDExtension(aExtensionId);
1094 }
1095 }
1096 return NS_OK;
1097 }
1098
AddRIDFilter(MediaStreamTrack & aRecvTrack,const nsAString & aRid)1099 nsresult PeerConnectionMedia::AddRIDFilter(MediaStreamTrack& aRecvTrack,
1100 const nsAString& aRid) {
1101 for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1102 if (transceiver->HasReceiveTrack(&aRecvTrack)) {
1103 transceiver->AddRIDFilter(aRid);
1104 }
1105 }
1106 return NS_OK;
1107 }
1108
IceGatheringStateChange_s(NrIceCtx * ctx,NrIceCtx::GatheringState state)1109 void PeerConnectionMedia::IceGatheringStateChange_s(
1110 NrIceCtx* ctx, NrIceCtx::GatheringState state) {
1111 ASSERT_ON_THREAD(mSTSThread);
1112
1113 if (state == NrIceCtx::ICE_CTX_GATHER_COMPLETE) {
1114 // Fire off EndOfLocalCandidates for each stream
1115 for (size_t i = 0;; ++i) {
1116 RefPtr<NrIceMediaStream> stream(ctx->GetStream(i));
1117 if (!stream) {
1118 break;
1119 }
1120
1121 NrIceCandidate candidate;
1122 NrIceCandidate rtcpCandidate;
1123 GetDefaultCandidates(*stream, &candidate, &rtcpCandidate);
1124 EndOfLocalCandidates(candidate.cand_addr.host, candidate.cand_addr.port,
1125 rtcpCandidate.cand_addr.host,
1126 rtcpCandidate.cand_addr.port, i);
1127 }
1128 }
1129
1130 // ShutdownMediaTransport_s has not run yet because it unhooks this function
1131 // from its signal, which means that SelfDestruct_m has not been dispatched
1132 // yet either, so this PCMedia will still be around when this dispatch reaches
1133 // main.
1134 GetMainThread()->Dispatch(
1135 WrapRunnable(this, &PeerConnectionMedia::IceGatheringStateChange_m, ctx,
1136 state),
1137 NS_DISPATCH_NORMAL);
1138 }
1139
IceConnectionStateChange_s(NrIceCtx * ctx,NrIceCtx::ConnectionState state)1140 void PeerConnectionMedia::IceConnectionStateChange_s(
1141 NrIceCtx* ctx, NrIceCtx::ConnectionState state) {
1142 ASSERT_ON_THREAD(mSTSThread);
1143 // ShutdownMediaTransport_s has not run yet because it unhooks this function
1144 // from its signal, which means that SelfDestruct_m has not been dispatched
1145 // yet either, so this PCMedia will still be around when this dispatch reaches
1146 // main.
1147 GetMainThread()->Dispatch(
1148 WrapRunnable(this, &PeerConnectionMedia::IceConnectionStateChange_m, ctx,
1149 state),
1150 NS_DISPATCH_NORMAL);
1151 }
1152
OnCandidateFound_s(NrIceMediaStream * aStream,const std::string & aCandidateLine)1153 void PeerConnectionMedia::OnCandidateFound_s(
1154 NrIceMediaStream* aStream, const std::string& aCandidateLine) {
1155 ASSERT_ON_THREAD(mSTSThread);
1156 MOZ_ASSERT(aStream);
1157 MOZ_RELEASE_ASSERT(mIceCtxHdlr);
1158
1159 CSFLogDebug(LOGTAG, "%s: %s", __FUNCTION__, aStream->name().c_str());
1160
1161 NrIceCandidate candidate;
1162 NrIceCandidate rtcpCandidate;
1163 GetDefaultCandidates(*aStream, &candidate, &rtcpCandidate);
1164
1165 // ShutdownMediaTransport_s has not run yet because it unhooks this function
1166 // from its signal, which means that SelfDestruct_m has not been dispatched
1167 // yet either, so this PCMedia will still be around when this dispatch reaches
1168 // main.
1169 GetMainThread()->Dispatch(
1170 WrapRunnable(this, &PeerConnectionMedia::OnCandidateFound_m,
1171 aCandidateLine, candidate.cand_addr.host,
1172 candidate.cand_addr.port, rtcpCandidate.cand_addr.host,
1173 rtcpCandidate.cand_addr.port, aStream->GetLevel()),
1174 NS_DISPATCH_NORMAL);
1175 }
1176
EndOfLocalCandidates(const std::string & aDefaultAddr,uint16_t aDefaultPort,const std::string & aDefaultRtcpAddr,uint16_t aDefaultRtcpPort,uint16_t aMLine)1177 void PeerConnectionMedia::EndOfLocalCandidates(
1178 const std::string& aDefaultAddr, uint16_t aDefaultPort,
1179 const std::string& aDefaultRtcpAddr, uint16_t aDefaultRtcpPort,
1180 uint16_t aMLine) {
1181 GetMainThread()->Dispatch(
1182 WrapRunnable(this, &PeerConnectionMedia::EndOfLocalCandidates_m,
1183 aDefaultAddr, aDefaultPort, aDefaultRtcpAddr,
1184 aDefaultRtcpPort, aMLine),
1185 NS_DISPATCH_NORMAL);
1186 }
1187
GetDefaultCandidates(const NrIceMediaStream & aStream,NrIceCandidate * aCandidate,NrIceCandidate * aRtcpCandidate)1188 void PeerConnectionMedia::GetDefaultCandidates(const NrIceMediaStream& aStream,
1189 NrIceCandidate* aCandidate,
1190 NrIceCandidate* aRtcpCandidate) {
1191 nsresult res = aStream.GetDefaultCandidate(1, aCandidate);
1192 // Optional; component won't exist if doing rtcp-mux
1193 if (NS_FAILED(aStream.GetDefaultCandidate(2, aRtcpCandidate))) {
1194 aRtcpCandidate->cand_addr.host.clear();
1195 aRtcpCandidate->cand_addr.port = 0;
1196 }
1197 if (NS_FAILED(res)) {
1198 aCandidate->cand_addr.host.clear();
1199 aCandidate->cand_addr.port = 0;
1200 CSFLogError(LOGTAG,
1201 "%s: GetDefaultCandidates failed for level %u, "
1202 "res=%u",
1203 __FUNCTION__, static_cast<unsigned>(aStream.GetLevel()),
1204 static_cast<unsigned>(res));
1205 }
1206 }
1207
IceGatheringStateChange_m(NrIceCtx * ctx,NrIceCtx::GatheringState state)1208 void PeerConnectionMedia::IceGatheringStateChange_m(
1209 NrIceCtx* ctx, NrIceCtx::GatheringState state) {
1210 ASSERT_ON_THREAD(mMainThread);
1211 SignalIceGatheringStateChange(ctx, state);
1212 }
1213
IceConnectionStateChange_m(NrIceCtx * ctx,NrIceCtx::ConnectionState state)1214 void PeerConnectionMedia::IceConnectionStateChange_m(
1215 NrIceCtx* ctx, NrIceCtx::ConnectionState state) {
1216 ASSERT_ON_THREAD(mMainThread);
1217 SignalIceConnectionStateChange(ctx, state);
1218 }
1219
IceStreamReady_s(NrIceMediaStream * aStream)1220 void PeerConnectionMedia::IceStreamReady_s(NrIceMediaStream* aStream) {
1221 MOZ_ASSERT(aStream);
1222
1223 CSFLogDebug(LOGTAG, "%s: %s", __FUNCTION__, aStream->name().c_str());
1224 }
1225
OnCandidateFound_m(const std::string & aCandidateLine,const std::string & aDefaultAddr,uint16_t aDefaultPort,const std::string & aDefaultRtcpAddr,uint16_t aDefaultRtcpPort,uint16_t aMLine)1226 void PeerConnectionMedia::OnCandidateFound_m(
1227 const std::string& aCandidateLine, const std::string& aDefaultAddr,
1228 uint16_t aDefaultPort, const std::string& aDefaultRtcpAddr,
1229 uint16_t aDefaultRtcpPort, uint16_t aMLine) {
1230 ASSERT_ON_THREAD(mMainThread);
1231 if (!aDefaultAddr.empty()) {
1232 SignalUpdateDefaultCandidate(aDefaultAddr, aDefaultPort, aDefaultRtcpAddr,
1233 aDefaultRtcpPort, aMLine);
1234 }
1235 SignalCandidate(aCandidateLine, aMLine);
1236 }
1237
EndOfLocalCandidates_m(const std::string & aDefaultAddr,uint16_t aDefaultPort,const std::string & aDefaultRtcpAddr,uint16_t aDefaultRtcpPort,uint16_t aMLine)1238 void PeerConnectionMedia::EndOfLocalCandidates_m(
1239 const std::string& aDefaultAddr, uint16_t aDefaultPort,
1240 const std::string& aDefaultRtcpAddr, uint16_t aDefaultRtcpPort,
1241 uint16_t aMLine) {
1242 ASSERT_ON_THREAD(mMainThread);
1243 if (!aDefaultAddr.empty()) {
1244 SignalUpdateDefaultCandidate(aDefaultAddr, aDefaultPort, aDefaultRtcpAddr,
1245 aDefaultRtcpPort, aMLine);
1246 }
1247 SignalEndOfLocalCandidates(aMLine);
1248 }
1249
DtlsConnected_s(TransportLayer * layer,TransportLayer::State state)1250 void PeerConnectionMedia::DtlsConnected_s(TransportLayer* layer,
1251 TransportLayer::State state) {
1252 MOZ_ASSERT(layer->id() == "dtls");
1253 TransportLayerDtls* dtlsLayer = static_cast<TransportLayerDtls*>(layer);
1254 dtlsLayer->SignalStateChange.disconnect(this);
1255
1256 bool privacyRequested = (dtlsLayer->GetNegotiatedAlpn() == "c-webrtc");
1257 GetMainThread()->Dispatch(
1258 WrapRunnableNM(&PeerConnectionMedia::DtlsConnected_m, mParentHandle,
1259 privacyRequested),
1260 NS_DISPATCH_NORMAL);
1261 }
1262
DtlsConnected_m(const std::string & aParentHandle,bool aPrivacyRequested)1263 void PeerConnectionMedia::DtlsConnected_m(const std::string& aParentHandle,
1264 bool aPrivacyRequested) {
1265 PeerConnectionWrapper pcWrapper(aParentHandle);
1266 PeerConnectionImpl* pc = pcWrapper.impl();
1267 if (pc) {
1268 pc->SetDtlsConnected(aPrivacyRequested);
1269 }
1270 }
1271
AddTransportFlow(int aIndex,bool aRtcp,const RefPtr<TransportFlow> & aFlow)1272 void PeerConnectionMedia::AddTransportFlow(int aIndex, bool aRtcp,
1273 const RefPtr<TransportFlow>& aFlow) {
1274 int index_inner = GetTransportFlowIndex(aIndex, aRtcp);
1275
1276 MOZ_ASSERT(!mTransportFlows[index_inner]);
1277 mTransportFlows[index_inner] = aFlow;
1278
1279 GetSTSThread()->Dispatch(
1280 WrapRunnable(this, &PeerConnectionMedia::ConnectDtlsListener_s, aFlow),
1281 NS_DISPATCH_NORMAL);
1282 }
1283
RemoveTransportFlow(int aIndex,bool aRtcp)1284 void PeerConnectionMedia::RemoveTransportFlow(int aIndex, bool aRtcp) {
1285 int index_inner = GetTransportFlowIndex(aIndex, aRtcp);
1286 NS_ProxyRelease("PeerConnectionMedia::mTransportFlows", GetSTSThread(),
1287 mTransportFlows[index_inner].forget());
1288 }
1289
ConnectDtlsListener_s(const RefPtr<TransportFlow> & aFlow)1290 void PeerConnectionMedia::ConnectDtlsListener_s(
1291 const RefPtr<TransportFlow>& aFlow) {
1292 TransportLayer* dtls = aFlow->GetLayer(TransportLayerDtls::ID());
1293 if (dtls) {
1294 dtls->SignalStateChange.connect(this,
1295 &PeerConnectionMedia::DtlsConnected_s);
1296 }
1297 }
1298
1299 /**
1300 * Tells you if any local track is isolated to a specific peer identity.
1301 * Obviously, we want all the tracks to be isolated equally so that they can
1302 * all be sent or not. We check once when we are setting a local description
1303 * and that determines if we flip the "privacy requested" bit on. Once the bit
1304 * is on, all media originating from this peer connection is isolated.
1305 *
1306 * @returns true if any track has a peerIdentity set on it
1307 */
AnyLocalTrackHasPeerIdentity() const1308 bool PeerConnectionMedia::AnyLocalTrackHasPeerIdentity() const {
1309 ASSERT_ON_THREAD(mMainThread);
1310
1311 for (const RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1312 if (transceiver->GetSendTrack() &&
1313 transceiver->GetSendTrack()->GetPeerIdentity()) {
1314 return true;
1315 }
1316 }
1317 return false;
1318 }
1319
UpdateRemoteStreamPrincipals_m(nsIPrincipal * aPrincipal)1320 void PeerConnectionMedia::UpdateRemoteStreamPrincipals_m(
1321 nsIPrincipal* aPrincipal) {
1322 ASSERT_ON_THREAD(mMainThread);
1323
1324 for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1325 transceiver->UpdatePrincipal(aPrincipal);
1326 }
1327 }
1328
UpdateSinkIdentity_m(const MediaStreamTrack * aTrack,nsIPrincipal * aPrincipal,const PeerIdentity * aSinkIdentity)1329 void PeerConnectionMedia::UpdateSinkIdentity_m(
1330 const MediaStreamTrack* aTrack, nsIPrincipal* aPrincipal,
1331 const PeerIdentity* aSinkIdentity) {
1332 ASSERT_ON_THREAD(mMainThread);
1333
1334 for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1335 transceiver->UpdateSinkIdentity(aTrack, aPrincipal, aSinkIdentity);
1336 }
1337 }
1338
AnyCodecHasPluginID(uint64_t aPluginID)1339 bool PeerConnectionMedia::AnyCodecHasPluginID(uint64_t aPluginID) {
1340 for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1341 if (transceiver->ConduitHasPluginID(aPluginID)) {
1342 return true;
1343 }
1344 }
1345 return false;
1346 }
1347
1348 } // namespace mozilla
1349