Line data Source code
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 <cstdlib>
6 : #include <cerrno>
7 : #include <deque>
8 : #include <set>
9 : #include <sstream>
10 : #include <vector>
11 :
12 : #include "CSFLog.h"
13 : #include "timecard.h"
14 :
15 : #include "jsapi.h"
16 : #include "nspr.h"
17 : #include "nss.h"
18 : #include "pk11pub.h"
19 :
20 : #include "nsNetCID.h"
21 : #include "nsILoadContext.h"
22 : #include "nsIProperty.h"
23 : #include "nsIPropertyBag2.h"
24 : #include "nsIServiceManager.h"
25 : #include "nsISimpleEnumerator.h"
26 : #include "nsServiceManagerUtils.h"
27 : #include "nsISocketTransportService.h"
28 : #include "nsIConsoleService.h"
29 : #include "nsThreadUtils.h"
30 : #include "nsIPrefService.h"
31 : #include "nsIPrefBranch.h"
32 : #include "nsProxyRelease.h"
33 : #include "nsQueryObject.h"
34 : #include "prtime.h"
35 :
36 : #include "AudioConduit.h"
37 : #include "VideoConduit.h"
38 : #include "runnable_utils.h"
39 : #include "PeerConnectionCtx.h"
40 : #include "PeerConnectionImpl.h"
41 : #include "PeerConnectionMedia.h"
42 : #include "nsDOMDataChannelDeclarations.h"
43 : #include "dtlsidentity.h"
44 : #include "signaling/src/sdp/SdpAttribute.h"
45 :
46 : #include "signaling/src/jsep/JsepTrack.h"
47 : #include "signaling/src/jsep/JsepSession.h"
48 : #include "signaling/src/jsep/JsepSessionImpl.h"
49 :
50 : #include "mozilla/IntegerPrintfMacros.h"
51 : #include "mozilla/Sprintf.h"
52 :
53 : #ifdef XP_WIN
54 : // We need to undef the MS macro for nsIDocument::CreateEvent
55 : #ifdef CreateEvent
56 : #undef CreateEvent
57 : #endif
58 : #endif // XP_WIN
59 :
60 : #include "nsIDocument.h"
61 : #include "nsGlobalWindow.h"
62 : #include "nsDOMDataChannel.h"
63 : #include "mozilla/dom/Location.h"
64 : #include "mozilla/dom/Performance.h"
65 : #include "mozilla/TimeStamp.h"
66 : #include "mozilla/Telemetry.h"
67 : #include "mozilla/Preferences.h"
68 : #include "mozilla/PublicSSL.h"
69 : #include "nsXULAppAPI.h"
70 : #include "nsContentUtils.h"
71 : #include "nsDOMJSUtils.h"
72 : #include "nsIScriptError.h"
73 : #include "nsPrintfCString.h"
74 : #include "nsURLHelper.h"
75 : #include "nsNetUtil.h"
76 : #include "nsIURLParser.h"
77 : #include "nsIDOMDataChannel.h"
78 : #include "NullPrincipal.h"
79 : #include "mozilla/PeerIdentity.h"
80 : #include "mozilla/dom/RTCCertificate.h"
81 : #include "mozilla/dom/RTCConfigurationBinding.h"
82 : #include "mozilla/dom/RTCDTMFSenderBinding.h"
83 : #include "mozilla/dom/RTCDTMFToneChangeEvent.h"
84 : #include "mozilla/dom/RTCRtpSenderBinding.h"
85 : #include "mozilla/dom/RTCStatsReportBinding.h"
86 : #include "mozilla/dom/RTCPeerConnectionBinding.h"
87 : #include "mozilla/dom/PeerConnectionImplBinding.h"
88 : #include "mozilla/dom/DataChannelBinding.h"
89 : #include "mozilla/dom/PerformanceTiming.h"
90 : #include "mozilla/dom/PluginCrashedEvent.h"
91 : #include "MediaStreamList.h"
92 : #include "MediaStreamTrack.h"
93 : #include "AudioStreamTrack.h"
94 : #include "VideoStreamTrack.h"
95 : #include "nsIScriptGlobalObject.h"
96 : #include "MediaStreamGraph.h"
97 : #include "DOMMediaStream.h"
98 : #include "rlogconnector.h"
99 : #include "WebrtcGlobalInformation.h"
100 : #include "mozilla/dom/Event.h"
101 : #include "nsIDOMCustomEvent.h"
102 : #include "mozilla/EventDispatcher.h"
103 : #include "mozilla/net/DataChannelProtocol.h"
104 :
105 : #include "MediaStreamGraphImpl.h"
106 :
107 : #ifdef XP_WIN
108 : // We need to undef the MS macro again in case the windows include file
109 : // got imported after we included nsIDocument.h
110 : #ifdef CreateEvent
111 : #undef CreateEvent
112 : #endif
113 : #endif // XP_WIN
114 :
115 : #include "MediaSegment.h"
116 :
117 : #ifdef USE_FAKE_PCOBSERVER
118 : #include "FakePCObserver.h"
119 : #else
120 : #include "mozilla/dom/PeerConnectionObserverBinding.h"
121 : #endif
122 : #include "mozilla/dom/PeerConnectionObserverEnumsBinding.h"
123 :
124 : #ifdef MOZ_WEBRTC_OMX
125 : #include "OMXVideoCodec.h"
126 : #include "OMXCodecWrapper.h"
127 : #endif
128 :
129 : #define ICE_PARSING "In RTCConfiguration passed to RTCPeerConnection constructor"
130 :
131 : using namespace mozilla;
132 : using namespace mozilla::dom;
133 :
134 : typedef PCObserverString ObString;
135 :
136 : static const char* logTag = "PeerConnectionImpl";
137 : static mozilla::LazyLogModule logModuleInfo("signaling");
138 :
139 : // Getting exceptions back down from PCObserver is generally not harmful.
140 : namespace {
141 : // This is a terrible hack. The problem is that SuppressException is not
142 : // inline, and we link this file without libxul in some cases (e.g. for our test
143 : // setup). So we can't use ErrorResult or IgnoredErrorResult because those call
144 : // SuppressException... And we can't use FastErrorResult because we can't
145 : // include BindingUtils.h, because our linking is completely fucked up. Use
146 : // BaseErrorResult directly. Please do not let me see _anyone_ doing this
147 : // without really careful review from someone who knows what they are doing.
148 0 : class JSErrorResult :
149 : public binding_danger::TErrorResult<binding_danger::JustAssertCleanupPolicy>
150 : {
151 : public:
152 0 : ~JSErrorResult()
153 0 : {
154 0 : SuppressException();
155 0 : }
156 : };
157 :
158 : // The WrapRunnable() macros copy passed-in args and passes them to the function
159 : // later on the other thread. ErrorResult cannot be passed like this because it
160 : // disallows copy-semantics.
161 : //
162 : // This WrappableJSErrorResult hack solves this by not actually copying the
163 : // ErrorResult, but creating a new one instead, which works because we don't
164 : // care about the result.
165 : //
166 : // Since this is for JS-calls, these can only be dispatched to the main thread.
167 :
168 : class WrappableJSErrorResult {
169 : public:
170 0 : WrappableJSErrorResult()
171 0 : : mRv(MakeUnique<JSErrorResult>()),
172 0 : isCopy(false) {}
173 0 : WrappableJSErrorResult(const WrappableJSErrorResult &other)
174 0 : : mRv(MakeUnique<JSErrorResult>()),
175 0 : isCopy(true) {}
176 0 : ~WrappableJSErrorResult() {
177 0 : if (isCopy) {
178 0 : MOZ_ASSERT(NS_IsMainThread());
179 : }
180 0 : }
181 0 : operator ErrorResult &() { return *mRv; }
182 : private:
183 : mozilla::UniquePtr<JSErrorResult> mRv;
184 : bool isCopy;
185 : };
186 :
187 : }
188 :
189 0 : static nsresult InitNSSInContent()
190 : {
191 0 : NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
192 :
193 0 : if (!XRE_IsContentProcess()) {
194 0 : MOZ_ASSERT_UNREACHABLE("Must be called in content process");
195 : return NS_ERROR_FAILURE;
196 : }
197 :
198 : static bool nssStarted = false;
199 0 : if (nssStarted) {
200 0 : return NS_OK;
201 : }
202 :
203 0 : if (NSS_NoDB_Init(nullptr) != SECSuccess) {
204 0 : CSFLogError(logTag, "NSS_NoDB_Init failed.");
205 0 : return NS_ERROR_FAILURE;
206 : }
207 :
208 0 : if (NS_FAILED(mozilla::psm::InitializeCipherSuite())) {
209 0 : CSFLogError(logTag, "Fail to set up nss cipher suite.");
210 0 : return NS_ERROR_FAILURE;
211 : }
212 :
213 0 : mozilla::psm::DisableMD5();
214 :
215 0 : nssStarted = true;
216 :
217 0 : return NS_OK;
218 : }
219 :
220 : namespace mozilla {
221 : class DataChannel;
222 : }
223 :
224 : class nsIDOMDataChannel;
225 :
226 : // XXX Workaround for bug 998092 to maintain the existing broken semantics
227 : template<>
228 : struct nsISupportsWeakReference::COMTypeInfo<nsSupportsWeakReference, void> {
229 : static const nsIID kIID;
230 : };
231 : const nsIID nsISupportsWeakReference::COMTypeInfo<nsSupportsWeakReference, void>::kIID = NS_ISUPPORTSWEAKREFERENCE_IID;
232 :
233 : namespace mozilla {
234 :
235 0 : RTCStatsQuery::RTCStatsQuery(bool internal) :
236 : failed(false),
237 : internalStats(internal),
238 0 : grabAllLevels(false) {
239 0 : }
240 :
241 0 : RTCStatsQuery::~RTCStatsQuery() {
242 0 : MOZ_ASSERT(NS_IsMainThread());
243 0 : }
244 :
245 :
246 0 : NS_IMPL_ISUPPORTS0(PeerConnectionImpl)
247 :
248 : bool
249 0 : PeerConnectionImpl::WrapObject(JSContext* aCx,
250 : JS::Handle<JSObject*> aGivenProto,
251 : JS::MutableHandle<JSObject*> aReflector)
252 : {
253 0 : return PeerConnectionImplBinding::Wrap(aCx, this, aGivenProto, aReflector);
254 : }
255 :
256 0 : bool PCUuidGenerator::Generate(std::string* idp) {
257 : nsresult rv;
258 :
259 0 : if(!mGenerator) {
260 0 : mGenerator = do_GetService("@mozilla.org/uuid-generator;1", &rv);
261 0 : if (NS_FAILED(rv)) {
262 0 : return false;
263 : }
264 0 : if (!mGenerator) {
265 0 : return false;
266 : }
267 : }
268 :
269 : nsID id;
270 0 : rv = mGenerator->GenerateUUIDInPlace(&id);
271 0 : if (NS_FAILED(rv)) {
272 0 : return false;
273 : }
274 : char buffer[NSID_LENGTH];
275 0 : id.ToProvidedString(buffer);
276 0 : idp->assign(buffer);
277 :
278 0 : return true;
279 : }
280 :
281 0 : bool IsPrivateBrowsing(nsPIDOMWindowInner* aWindow)
282 : {
283 0 : if (!aWindow) {
284 0 : return false;
285 : }
286 :
287 0 : nsIDocument *doc = aWindow->GetExtantDoc();
288 0 : if (!doc) {
289 0 : return false;
290 : }
291 :
292 0 : nsILoadContext *loadContext = doc->GetLoadContext();
293 0 : return loadContext && loadContext->UsePrivateBrowsing();
294 : }
295 :
296 0 : PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
297 0 : : mTimeCard(MOZ_LOG_TEST(logModuleInfo,LogLevel::Error) ?
298 : create_timecard() : nullptr)
299 : , mSignalingState(PCImplSignalingState::SignalingStable)
300 : , mIceConnectionState(PCImplIceConnectionState::New)
301 : , mIceGatheringState(PCImplIceGatheringState::New)
302 : , mDtlsConnected(false)
303 : , mWindow(nullptr)
304 : , mCertificate(nullptr)
305 : , mPrivacyRequested(false)
306 : , mSTSThread(nullptr)
307 : , mAllowIceLoopback(false)
308 : , mAllowIceLinkLocal(false)
309 : , mForceIceTcp(false)
310 : , mMedia(nullptr)
311 : , mUuidGen(MakeUnique<PCUuidGenerator>())
312 : , mHaveConfiguredCodecs(false)
313 : , mHaveDataStream(false)
314 : , mAddCandidateErrorCount(0)
315 : , mTrickle(true) // TODO(ekr@rtfm.com): Use pref
316 : , mNegotiationNeeded(false)
317 0 : , mPrivateWindow(false)
318 : {
319 0 : MOZ_ASSERT(NS_IsMainThread());
320 0 : auto log = RLogConnector::CreateInstance();
321 0 : if (aGlobal) {
322 0 : mWindow = do_QueryInterface(aGlobal->GetAsSupports());
323 0 : if (IsPrivateBrowsing(mWindow)) {
324 0 : mPrivateWindow = true;
325 0 : log->EnterPrivateMode();
326 : }
327 : }
328 0 : CSFLogInfo(logTag, "%s: PeerConnectionImpl constructor for %s",
329 0 : __FUNCTION__, mHandle.c_str());
330 0 : STAMP_TIMECARD(mTimeCard, "Constructor Completed");
331 0 : mAllowIceLoopback = Preferences::GetBool(
332 : "media.peerconnection.ice.loopback", false);
333 0 : mAllowIceLinkLocal = Preferences::GetBool(
334 : "media.peerconnection.ice.link_local", false);
335 0 : mForceIceTcp = Preferences::GetBool(
336 : "media.peerconnection.ice.force_ice_tcp", false);
337 0 : memset(mMaxReceiving, 0, sizeof(mMaxReceiving));
338 0 : memset(mMaxSending, 0, sizeof(mMaxSending));
339 0 : }
340 :
341 0 : PeerConnectionImpl::~PeerConnectionImpl()
342 : {
343 0 : if (mTimeCard) {
344 0 : STAMP_TIMECARD(mTimeCard, "Destructor Invoked");
345 0 : print_timecard(mTimeCard);
346 0 : destroy_timecard(mTimeCard);
347 0 : mTimeCard = nullptr;
348 : }
349 : // This aborts if not on main thread (in Debug builds)
350 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
351 0 : if (mPrivateWindow) {
352 0 : auto * log = RLogConnector::GetInstance();
353 0 : if (log) {
354 0 : log->ExitPrivateMode();
355 : }
356 0 : mPrivateWindow = false;
357 : }
358 0 : if (PeerConnectionCtx::isActive()) {
359 0 : PeerConnectionCtx::GetInstance()->mPeerConnections.erase(mHandle);
360 : } else {
361 0 : CSFLogError(logTag, "PeerConnectionCtx is already gone. Ignoring...");
362 : }
363 :
364 0 : CSFLogInfo(logTag, "%s: PeerConnectionImpl destructor invoked for %s",
365 0 : __FUNCTION__, mHandle.c_str());
366 :
367 0 : Close();
368 :
369 : // Since this and Initialize() occur on MainThread, they can't both be
370 : // running at once
371 :
372 : // Right now, we delete PeerConnectionCtx at XPCOM shutdown only, but we
373 : // probably want to shut it down more aggressively to save memory. We
374 : // could shut down here when there are no uses. It might be more optimal
375 : // to release off a timer (and XPCOM Shutdown) to avoid churn
376 0 : }
377 :
378 : already_AddRefed<DOMMediaStream>
379 0 : PeerConnectionImpl::MakeMediaStream()
380 : {
381 : MediaStreamGraph* graph =
382 0 : MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
383 0 : AudioChannel::Normal, GetWindow());
384 :
385 : RefPtr<DOMMediaStream> stream =
386 0 : DOMMediaStream::CreateSourceStreamAsInput(GetWindow(), graph);
387 :
388 0 : CSFLogDebug(logTag, "Created media stream %p, inner: %p", stream.get(), stream->GetInputStream());
389 :
390 0 : return stream.forget();
391 : }
392 :
393 : nsresult
394 0 : PeerConnectionImpl::CreateRemoteSourceStreamInfo(RefPtr<RemoteSourceStreamInfo>*
395 : aInfo,
396 : const std::string& aStreamID)
397 : {
398 0 : MOZ_ASSERT(aInfo);
399 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
400 :
401 0 : RefPtr<DOMMediaStream> stream = MakeMediaStream();
402 0 : if (!stream) {
403 0 : return NS_ERROR_FAILURE;
404 : }
405 :
406 0 : RefPtr<RemoteSourceStreamInfo> remote;
407 0 : remote = new RemoteSourceStreamInfo(stream.forget(), mMedia, aStreamID);
408 0 : *aInfo = remote;
409 :
410 0 : return NS_OK;
411 : }
412 :
413 : /**
414 : * In JS, an RTCConfiguration looks like this:
415 : *
416 : * { "iceServers": [ { url:"stun:stun.example.org" },
417 : * { url:"turn:turn.example.org?transport=udp",
418 : * username: "jib", credential:"mypass"} ] }
419 : *
420 : * This function converts that into an internal PeerConnectionConfiguration
421 : * object.
422 : */
423 : nsresult
424 0 : PeerConnectionConfiguration::Init(const RTCConfiguration& aSrc)
425 : {
426 0 : if (aSrc.mIceServers.WasPassed()) {
427 0 : for (size_t i = 0; i < aSrc.mIceServers.Value().Length(); i++) {
428 0 : nsresult rv = AddIceServer(aSrc.mIceServers.Value()[i]);
429 0 : NS_ENSURE_SUCCESS(rv, rv);
430 : }
431 : }
432 :
433 0 : switch (aSrc.mBundlePolicy) {
434 : case dom::RTCBundlePolicy::Balanced:
435 0 : setBundlePolicy(kBundleBalanced);
436 0 : break;
437 : case dom::RTCBundlePolicy::Max_compat:
438 0 : setBundlePolicy(kBundleMaxCompat);
439 0 : break;
440 : case dom::RTCBundlePolicy::Max_bundle:
441 0 : setBundlePolicy(kBundleMaxBundle);
442 0 : break;
443 : default:
444 0 : MOZ_CRASH();
445 : }
446 :
447 0 : switch (aSrc.mIceTransportPolicy) {
448 : case dom::RTCIceTransportPolicy::Relay:
449 0 : setIceTransportPolicy(NrIceCtx::ICE_POLICY_RELAY);
450 0 : break;
451 : case dom::RTCIceTransportPolicy::All:
452 0 : if (Preferences::GetBool("media.peerconnection.ice.no_host", false)) {
453 0 : setIceTransportPolicy(NrIceCtx::ICE_POLICY_NO_HOST);
454 : } else {
455 0 : setIceTransportPolicy(NrIceCtx::ICE_POLICY_ALL);
456 : }
457 0 : break;
458 : default:
459 0 : MOZ_CRASH();
460 : }
461 0 : return NS_OK;
462 : }
463 :
464 : nsresult
465 0 : PeerConnectionConfiguration::AddIceServer(const RTCIceServer &aServer)
466 : {
467 0 : NS_ENSURE_STATE(aServer.mUrls.WasPassed());
468 0 : NS_ENSURE_STATE(aServer.mUrls.Value().IsStringSequence());
469 0 : auto &urls = aServer.mUrls.Value().GetAsStringSequence();
470 0 : for (size_t i = 0; i < urls.Length(); i++) {
471 : // Without STUN/TURN handlers, NS_NewURI returns nsSimpleURI rather than
472 : // nsStandardURL. To parse STUN/TURN URI's to spec
473 : // http://tools.ietf.org/html/draft-nandakumar-rtcweb-stun-uri-02#section-3
474 : // http://tools.ietf.org/html/draft-petithuguenin-behave-turn-uri-03#section-3
475 : // we parse out the query-string, and use ParseAuthority() on the rest
476 0 : RefPtr<nsIURI> url;
477 0 : nsresult rv = NS_NewURI(getter_AddRefs(url), urls[i]);
478 0 : NS_ENSURE_SUCCESS(rv, rv);
479 0 : bool isStun = false, isStuns = false, isTurn = false, isTurns = false;
480 0 : url->SchemeIs("stun", &isStun);
481 0 : url->SchemeIs("stuns", &isStuns);
482 0 : url->SchemeIs("turn", &isTurn);
483 0 : url->SchemeIs("turns", &isTurns);
484 0 : if (!(isStun || isStuns || isTurn || isTurns)) {
485 0 : return NS_ERROR_FAILURE;
486 : }
487 0 : if (isStuns) {
488 0 : continue; // TODO: Support STUNS (Bug 1056934)
489 : }
490 0 : nsAutoCString spec;
491 0 : rv = url->GetSpec(spec);
492 0 : NS_ENSURE_SUCCESS(rv, rv);
493 :
494 : // TODO(jib@mozilla.com): Revisit once nsURI supports STUN/TURN (Bug 833509)
495 : int32_t port;
496 0 : nsAutoCString host;
497 0 : nsAutoCString transport;
498 : {
499 : uint32_t hostPos;
500 : int32_t hostLen;
501 0 : nsAutoCString path;
502 0 : rv = url->GetPath(path);
503 0 : NS_ENSURE_SUCCESS(rv, rv);
504 :
505 : // Tolerate query-string + parse 'transport=[udp|tcp]' by hand.
506 0 : int32_t questionmark = path.FindChar('?');
507 0 : if (questionmark >= 0) {
508 0 : const nsCString match = NS_LITERAL_CSTRING("transport=");
509 :
510 0 : for (int32_t i = questionmark, endPos; i >= 0; i = endPos) {
511 0 : endPos = path.FindCharInSet("&", i + 1);
512 0 : const nsDependentCSubstring fieldvaluepair = Substring(path, i + 1,
513 0 : endPos);
514 0 : if (StringBeginsWith(fieldvaluepair, match)) {
515 0 : transport = Substring(fieldvaluepair, match.Length());
516 0 : ToLowerCase(transport);
517 : }
518 : }
519 0 : path.SetLength(questionmark);
520 : }
521 :
522 0 : rv = net_GetAuthURLParser()->ParseAuthority(path.get(), path.Length(),
523 : nullptr, nullptr,
524 : nullptr, nullptr,
525 0 : &hostPos, &hostLen, &port);
526 0 : NS_ENSURE_SUCCESS(rv, rv);
527 0 : if (!hostLen) {
528 0 : return NS_ERROR_FAILURE;
529 : }
530 0 : if (hostPos > 1) /* The username was removed */
531 0 : return NS_ERROR_FAILURE;
532 0 : path.Mid(host, hostPos, hostLen);
533 : }
534 0 : if (port == -1)
535 0 : port = (isStuns || isTurns)? 5349 : 3478;
536 :
537 0 : if (isStuns || isTurns) {
538 : // Should we barf if transport is set to udp or something?
539 0 : transport = kNrIceTransportTls;
540 : }
541 :
542 0 : if (transport.IsEmpty()) {
543 0 : transport = kNrIceTransportUdp;
544 : }
545 :
546 0 : if (isTurn || isTurns) {
547 0 : NS_ConvertUTF16toUTF8 credential(aServer.mCredential.Value());
548 0 : NS_ConvertUTF16toUTF8 username(aServer.mUsername.Value());
549 :
550 0 : if (!addTurnServer(host.get(), port,
551 : username.get(),
552 : credential.get(),
553 : transport.get())) {
554 0 : return NS_ERROR_FAILURE;
555 0 : }
556 : } else {
557 0 : if (!addStunServer(host.get(), port, transport.get())) {
558 0 : return NS_ERROR_FAILURE;
559 : }
560 : }
561 : }
562 0 : return NS_OK;
563 : }
564 :
565 : nsresult
566 0 : PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
567 : nsGlobalWindow* aWindow,
568 : const PeerConnectionConfiguration& aConfiguration,
569 : nsISupports* aThread)
570 : {
571 : nsresult res;
572 :
573 0 : MOZ_ASSERT(NS_IsMainThread());
574 0 : MOZ_ASSERT(aThread);
575 0 : if (!mThread) {
576 0 : mThread = do_QueryInterface(aThread);
577 0 : MOZ_ASSERT(mThread);
578 : }
579 0 : CheckThread();
580 :
581 0 : mPCObserver = do_GetWeakReference(&aObserver);
582 :
583 : // Find the STS thread
584 :
585 0 : mSTSThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res);
586 0 : MOZ_ASSERT(mSTSThread);
587 :
588 : // Initialize NSS if we are in content process. For chrome process, NSS should already
589 : // been initialized.
590 0 : if (XRE_IsParentProcess()) {
591 : // This code interferes with the C++ unit test startup code.
592 0 : nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &res);
593 0 : NS_ENSURE_SUCCESS(res, res);
594 : } else {
595 0 : NS_ENSURE_SUCCESS(res = InitNSSInContent(), res);
596 : }
597 :
598 : // Currently no standalone unit tests for DataChannel,
599 : // which is the user of mWindow
600 0 : MOZ_ASSERT(aWindow);
601 0 : mWindow = aWindow->AsInner();
602 0 : NS_ENSURE_STATE(mWindow);
603 :
604 0 : PRTime timestamp = PR_Now();
605 : // Ok if we truncate this.
606 : char temp[128];
607 :
608 0 : nsAutoCString locationCStr;
609 :
610 0 : if (RefPtr<Location> location = mWindow->GetLocation()) {
611 0 : nsAutoString locationAStr;
612 0 : res = location->ToString(locationAStr);
613 0 : NS_ENSURE_SUCCESS(res, res);
614 :
615 0 : CopyUTF16toUTF8(locationAStr, locationCStr);
616 : }
617 :
618 0 : SprintfLiteral(temp,
619 : "%" PRIu64 " (id=%" PRIu64 " url=%s)",
620 : static_cast<uint64_t>(timestamp),
621 0 : static_cast<uint64_t>(mWindow ? mWindow->WindowID() : 0),
622 0 : locationCStr.get() ? locationCStr.get() : "NULL");
623 :
624 0 : mName = temp;
625 :
626 : // Generate a random handle
627 : unsigned char handle_bin[8];
628 : SECStatus rv;
629 0 : rv = PK11_GenerateRandom(handle_bin, sizeof(handle_bin));
630 0 : if (rv != SECSuccess) {
631 0 : MOZ_CRASH();
632 : return NS_ERROR_UNEXPECTED;
633 : }
634 :
635 : char hex[17];
636 0 : SprintfLiteral(hex, "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
637 0 : handle_bin[0],
638 0 : handle_bin[1],
639 0 : handle_bin[2],
640 0 : handle_bin[3],
641 0 : handle_bin[4],
642 0 : handle_bin[5],
643 0 : handle_bin[6],
644 0 : handle_bin[7]);
645 :
646 0 : mHandle = hex;
647 :
648 0 : STAMP_TIMECARD(mTimeCard, "Initializing PC Ctx");
649 0 : res = PeerConnectionCtx::InitializeGlobal(mThread, mSTSThread);
650 0 : NS_ENSURE_SUCCESS(res, res);
651 :
652 0 : mMedia = new PeerConnectionMedia(this);
653 :
654 : // Connect ICE slots.
655 0 : mMedia->SignalIceGatheringStateChange.connect(
656 : this,
657 0 : &PeerConnectionImpl::IceGatheringStateChange);
658 0 : mMedia->SignalUpdateDefaultCandidate.connect(
659 : this,
660 0 : &PeerConnectionImpl::UpdateDefaultCandidate);
661 0 : mMedia->SignalEndOfLocalCandidates.connect(
662 : this,
663 0 : &PeerConnectionImpl::EndOfLocalCandidates);
664 0 : mMedia->SignalIceConnectionStateChange.connect(
665 : this,
666 0 : &PeerConnectionImpl::IceConnectionStateChange);
667 :
668 0 : mMedia->SignalCandidate.connect(this, &PeerConnectionImpl::CandidateReady);
669 :
670 : // Initialize the media object.
671 0 : res = mMedia->Init(aConfiguration.getStunServers(),
672 : aConfiguration.getTurnServers(),
673 : aConfiguration.getIceTransportPolicy());
674 0 : if (NS_FAILED(res)) {
675 0 : CSFLogError(logTag, "%s: Couldn't initialize media object", __FUNCTION__);
676 0 : return res;
677 : }
678 :
679 0 : PeerConnectionCtx::GetInstance()->mPeerConnections[mHandle] = this;
680 :
681 0 : mJsepSession = MakeUnique<JsepSessionImpl>(mName,
682 0 : MakeUnique<PCUuidGenerator>());
683 :
684 0 : res = mJsepSession->Init();
685 0 : if (NS_FAILED(res)) {
686 0 : CSFLogError(logTag, "%s: Couldn't init JSEP Session, res=%u",
687 : __FUNCTION__,
688 0 : static_cast<unsigned>(res));
689 0 : return res;
690 : }
691 :
692 0 : res = mJsepSession->SetIceCredentials(mMedia->ice_ctx()->ufrag(),
693 0 : mMedia->ice_ctx()->pwd());
694 0 : if (NS_FAILED(res)) {
695 0 : CSFLogError(logTag, "%s: Couldn't set ICE credentials, res=%u",
696 : __FUNCTION__,
697 0 : static_cast<unsigned>(res));
698 0 : return res;
699 : }
700 :
701 0 : res = mJsepSession->SetBundlePolicy(aConfiguration.getBundlePolicy());
702 0 : if (NS_FAILED(res)) {
703 0 : CSFLogError(logTag, "%s: Couldn't set bundle policy, res=%u, error=%s",
704 : __FUNCTION__,
705 : static_cast<unsigned>(res),
706 0 : mJsepSession->GetLastError().c_str());
707 0 : return res;
708 : }
709 :
710 0 : return NS_OK;
711 : }
712 :
713 : void
714 0 : PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
715 : nsGlobalWindow& aWindow,
716 : const RTCConfiguration& aConfiguration,
717 : nsISupports* aThread,
718 : ErrorResult &rv)
719 : {
720 0 : MOZ_ASSERT(NS_IsMainThread());
721 0 : MOZ_ASSERT(aThread);
722 0 : mThread = do_QueryInterface(aThread);
723 :
724 0 : PeerConnectionConfiguration converted;
725 0 : nsresult res = converted.Init(aConfiguration);
726 0 : if (NS_FAILED(res)) {
727 0 : CSFLogError(logTag, "%s: Invalid RTCConfiguration", __FUNCTION__);
728 0 : rv.Throw(res);
729 0 : return;
730 : }
731 :
732 0 : res = Initialize(aObserver, &aWindow, converted, aThread);
733 0 : if (NS_FAILED(res)) {
734 0 : rv.Throw(res);
735 0 : return;
736 : }
737 :
738 0 : if (!aConfiguration.mPeerIdentity.IsEmpty()) {
739 0 : mPeerIdentity = new PeerIdentity(aConfiguration.mPeerIdentity);
740 0 : mPrivacyRequested = true;
741 : }
742 : }
743 :
744 : void
745 0 : PeerConnectionImpl::SetCertificate(mozilla::dom::RTCCertificate& aCertificate)
746 : {
747 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
748 0 : MOZ_ASSERT(!mCertificate, "This can only be called once");
749 0 : mCertificate = &aCertificate;
750 :
751 0 : std::vector<uint8_t> fingerprint;
752 : nsresult rv = CalculateFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM,
753 0 : &fingerprint);
754 0 : if (NS_FAILED(rv)) {
755 : CSFLogError(logTag, "%s: Couldn't calculate fingerprint, rv=%u",
756 0 : __FUNCTION__, static_cast<unsigned>(rv));
757 0 : mCertificate = nullptr;
758 0 : return;
759 : }
760 0 : rv = mJsepSession->AddDtlsFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM,
761 0 : fingerprint);
762 0 : if (NS_FAILED(rv)) {
763 : CSFLogError(logTag, "%s: Couldn't set DTLS credentials, rv=%u",
764 0 : __FUNCTION__, static_cast<unsigned>(rv));
765 0 : mCertificate = nullptr;
766 : }
767 : }
768 :
769 : const RefPtr<mozilla::dom::RTCCertificate>&
770 0 : PeerConnectionImpl::Certificate() const
771 : {
772 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
773 0 : return mCertificate;
774 : }
775 :
776 : RefPtr<DtlsIdentity>
777 0 : PeerConnectionImpl::Identity() const
778 : {
779 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
780 0 : MOZ_ASSERT(mCertificate);
781 0 : return mCertificate->CreateDtlsIdentity();
782 : }
783 :
784 0 : class CompareCodecPriority {
785 : public:
786 0 : void SetPreferredCodec(int32_t preferredCodec) {
787 : // This pref really ought to be a string, preferably something like
788 : // "H264" or "VP8" instead of a payload type.
789 : // Bug 1101259.
790 0 : std::ostringstream os;
791 0 : os << preferredCodec;
792 0 : mPreferredCodec = os.str();
793 0 : }
794 :
795 0 : bool operator()(JsepCodecDescription* lhs,
796 : JsepCodecDescription* rhs) const {
797 0 : if (!mPreferredCodec.empty() &&
798 0 : lhs->mDefaultPt == mPreferredCodec &&
799 0 : rhs->mDefaultPt != mPreferredCodec) {
800 0 : return true;
801 : }
802 :
803 0 : if (lhs->mStronglyPreferred && !rhs->mStronglyPreferred) {
804 0 : return true;
805 : }
806 :
807 0 : return false;
808 : }
809 :
810 : private:
811 : std::string mPreferredCodec;
812 : };
813 :
814 : class ConfigureCodec {
815 : public:
816 0 : explicit ConfigureCodec(nsCOMPtr<nsIPrefBranch>& branch) :
817 : mHardwareH264Enabled(false),
818 : mHardwareH264Supported(false),
819 : mSoftwareH264Enabled(false),
820 : mH264Enabled(false),
821 : mVP9Enabled(false),
822 : mH264Level(13), // minimum suggested for WebRTC spec
823 : mH264MaxBr(0), // Unlimited
824 : mH264MaxMbps(0), // Unlimited
825 : mVP8MaxFs(0),
826 : mVP8MaxFr(0),
827 : mUseTmmbr(false),
828 : mUseRemb(false),
829 : mUseAudioFec(false),
830 : mRedUlpfecEnabled(false),
831 0 : mDtmfEnabled(false)
832 : {
833 : #ifdef MOZ_WEBRTC_OMX
834 : // Check to see if what HW codecs are available (not in use) at this moment.
835 : // Note that streaming video decode can reserve a decoder
836 :
837 : // XXX See bug 1018791 Implement W3 codec reservation policy
838 : // Note that currently, OMXCodecReservation needs to be held by an sp<> because it puts
839 : // 'this' into an sp<EventListener> to talk to the resource reservation code
840 :
841 : // This pref is a misnomer; it is solely for h264 _hardware_ support.
842 : branch->GetBoolPref("media.peerconnection.video.h264_enabled",
843 : &mHardwareH264Enabled);
844 :
845 : if (mHardwareH264Enabled) {
846 : // Ok, it is preffed on. Can we actually do it?
847 : android::sp<android::OMXCodecReservation> encode = new android::OMXCodecReservation(true);
848 : android::sp<android::OMXCodecReservation> decode = new android::OMXCodecReservation(false);
849 :
850 : // Currently we just check if they're available right now, which will fail if we're
851 : // trying to call ourself, for example. It will work for most real-world cases, like
852 : // if we try to add a person to a 2-way call to make a 3-way mesh call
853 : if (encode->ReserveOMXCodec() && decode->ReserveOMXCodec()) {
854 : CSFLogDebug( logTag, "%s: H264 hardware codec available", __FUNCTION__);
855 : mHardwareH264Supported = true;
856 : }
857 : }
858 :
859 : #endif // MOZ_WEBRTC_OMX
860 :
861 0 : mSoftwareH264Enabled = PeerConnectionCtx::GetInstance()->gmpHasH264();
862 :
863 0 : mH264Enabled = mHardwareH264Supported || mSoftwareH264Enabled;
864 :
865 0 : branch->GetIntPref("media.navigator.video.h264.level", &mH264Level);
866 0 : mH264Level &= 0xFF;
867 :
868 0 : branch->GetIntPref("media.navigator.video.h264.max_br", &mH264MaxBr);
869 :
870 : #ifdef MOZ_WEBRTC_OMX
871 : // Level 1.2; but let's allow CIF@30 or QVGA@30+ by default
872 : mH264MaxMbps = 11880;
873 : #endif
874 :
875 0 : branch->GetIntPref("media.navigator.video.h264.max_mbps", &mH264MaxMbps);
876 :
877 0 : branch->GetBoolPref("media.peerconnection.video.vp9_enabled",
878 0 : &mVP9Enabled);
879 :
880 0 : branch->GetIntPref("media.navigator.video.max_fs", &mVP8MaxFs);
881 0 : if (mVP8MaxFs <= 0) {
882 0 : mVP8MaxFs = 12288; // We must specify something other than 0
883 : }
884 :
885 0 : branch->GetIntPref("media.navigator.video.max_fr", &mVP8MaxFr);
886 0 : if (mVP8MaxFr <= 0) {
887 0 : mVP8MaxFr = 60; // We must specify something other than 0
888 : }
889 :
890 : // TMMBR is enabled from a pref in about:config
891 0 : branch->GetBoolPref("media.navigator.video.use_tmmbr", &mUseTmmbr);
892 :
893 : // REMB is enabled by default, but can be disabled from about:config
894 0 : branch->GetBoolPref("media.navigator.video.use_remb", &mUseRemb);
895 :
896 0 : branch->GetBoolPref("media.navigator.audio.use_fec", &mUseAudioFec);
897 :
898 0 : branch->GetBoolPref("media.navigator.video.red_ulpfec_enabled",
899 0 : &mRedUlpfecEnabled);
900 :
901 : // media.peerconnection.dtmf.enabled controls both sdp generation for
902 : // DTMF support as well as DTMF exposure to DOM
903 0 : branch->GetBoolPref("media.peerconnection.dtmf.enabled", &mDtmfEnabled);
904 0 : }
905 :
906 0 : void operator()(JsepCodecDescription* codec) const
907 : {
908 0 : switch (codec->mType) {
909 : case SdpMediaSection::kAudio:
910 : {
911 : JsepAudioCodecDescription& audioCodec =
912 0 : static_cast<JsepAudioCodecDescription&>(*codec);
913 0 : if (audioCodec.mName == "opus") {
914 0 : audioCodec.mFECEnabled = mUseAudioFec;
915 0 : } else if (audioCodec.mName == "telephone-event") {
916 0 : audioCodec.mEnabled = mDtmfEnabled;
917 : }
918 : }
919 0 : break;
920 : case SdpMediaSection::kVideo:
921 : {
922 : JsepVideoCodecDescription& videoCodec =
923 0 : static_cast<JsepVideoCodecDescription&>(*codec);
924 :
925 0 : if (videoCodec.mName == "H264") {
926 : // Override level
927 0 : videoCodec.mProfileLevelId &= 0xFFFF00;
928 0 : videoCodec.mProfileLevelId |= mH264Level;
929 :
930 0 : videoCodec.mConstraints.maxBr = mH264MaxBr;
931 :
932 0 : videoCodec.mConstraints.maxMbps = mH264MaxMbps;
933 :
934 : // Might disable it, but we set up other params anyway
935 0 : videoCodec.mEnabled = mH264Enabled;
936 :
937 0 : if (videoCodec.mPacketizationMode == 0 && !mSoftwareH264Enabled) {
938 : // We're assuming packetization mode 0 is unsupported by
939 : // hardware.
940 0 : videoCodec.mEnabled = false;
941 : }
942 :
943 0 : if (mHardwareH264Supported) {
944 0 : videoCodec.mStronglyPreferred = true;
945 : }
946 0 : } else if (videoCodec.mName == "red") {
947 0 : videoCodec.mEnabled = mRedUlpfecEnabled;
948 0 : } else if (videoCodec.mName == "ulpfec") {
949 0 : videoCodec.mEnabled = mRedUlpfecEnabled;
950 0 : } else if (videoCodec.mName == "VP8" || videoCodec.mName == "VP9") {
951 0 : if (videoCodec.mName == "VP9" && !mVP9Enabled) {
952 0 : videoCodec.mEnabled = false;
953 0 : break;
954 : }
955 0 : videoCodec.mConstraints.maxFs = mVP8MaxFs;
956 0 : videoCodec.mConstraints.maxFps = mVP8MaxFr;
957 : }
958 :
959 0 : if (mUseTmmbr) {
960 0 : videoCodec.EnableTmmbr();
961 : }
962 0 : if (mUseRemb) {
963 0 : videoCodec.EnableRemb();
964 : }
965 : }
966 0 : break;
967 : case SdpMediaSection::kText:
968 : case SdpMediaSection::kApplication:
969 : case SdpMediaSection::kMessage:
970 : {} // Nothing to configure for these.
971 : }
972 0 : }
973 :
974 : private:
975 : bool mHardwareH264Enabled;
976 : bool mHardwareH264Supported;
977 : bool mSoftwareH264Enabled;
978 : bool mH264Enabled;
979 : bool mVP9Enabled;
980 : int32_t mH264Level;
981 : int32_t mH264MaxBr;
982 : int32_t mH264MaxMbps;
983 : int32_t mVP8MaxFs;
984 : int32_t mVP8MaxFr;
985 : bool mUseTmmbr;
986 : bool mUseRemb;
987 : bool mUseAudioFec;
988 : bool mRedUlpfecEnabled;
989 : bool mDtmfEnabled;
990 : };
991 :
992 : class ConfigureRedCodec {
993 : public:
994 0 : explicit ConfigureRedCodec(nsCOMPtr<nsIPrefBranch>& branch,
995 0 : std::vector<uint8_t>* redundantEncodings) :
996 0 : mRedundantEncodings(redundantEncodings)
997 : {
998 : // if we wanted to override or modify which encodings are considered
999 : // for redundant encodings, we'd probably want to handle it here by
1000 : // checking prefs modifying the operator() code below
1001 0 : }
1002 :
1003 0 : void operator()(JsepCodecDescription* codec) const
1004 : {
1005 0 : if (codec->mType == SdpMediaSection::kVideo &&
1006 0 : codec->mEnabled == false) {
1007 0 : uint8_t pt = (uint8_t)strtoul(codec->mDefaultPt.c_str(), nullptr, 10);
1008 : // don't search for the codec payload type unless we have a valid
1009 : // conversion (non-zero)
1010 0 : if (pt != 0) {
1011 : std::vector<uint8_t>::iterator it =
1012 0 : std::find(mRedundantEncodings->begin(),
1013 0 : mRedundantEncodings->end(),
1014 0 : pt);
1015 0 : if (it != mRedundantEncodings->end()) {
1016 0 : mRedundantEncodings->erase(it);
1017 : }
1018 : }
1019 : }
1020 0 : }
1021 :
1022 : private:
1023 : std::vector<uint8_t>* mRedundantEncodings;
1024 : };
1025 :
1026 : nsresult
1027 0 : PeerConnectionImpl::ConfigureJsepSessionCodecs() {
1028 : nsresult res;
1029 : nsCOMPtr<nsIPrefService> prefs =
1030 0 : do_GetService("@mozilla.org/preferences-service;1", &res);
1031 :
1032 0 : if (NS_FAILED(res)) {
1033 0 : CSFLogError(logTag, "%s: Couldn't get prefs service, res=%u",
1034 : __FUNCTION__,
1035 0 : static_cast<unsigned>(res));
1036 0 : return res;
1037 : }
1038 :
1039 0 : nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
1040 0 : if (!branch) {
1041 0 : CSFLogError(logTag, "%s: Couldn't get prefs branch", __FUNCTION__);
1042 0 : return NS_ERROR_FAILURE;
1043 : }
1044 :
1045 0 : ConfigureCodec configurer(branch);
1046 0 : mJsepSession->ForEachCodec(configurer);
1047 :
1048 : // first find the red codec description
1049 0 : std::vector<JsepCodecDescription*>& codecs = mJsepSession->Codecs();
1050 0 : JsepVideoCodecDescription* redCodec = nullptr;
1051 0 : for (auto codec : codecs) {
1052 : // we only really care about finding the RED codec if it is
1053 : // enabled
1054 0 : if (codec->mName == "red" && codec->mEnabled) {
1055 0 : redCodec = static_cast<JsepVideoCodecDescription*>(codec);
1056 0 : break;
1057 : }
1058 : }
1059 : // if red codec was found, configure it for the other enabled codecs
1060 0 : if (redCodec) {
1061 0 : ConfigureRedCodec configureRed(branch, &(redCodec->mRedundantEncodings));
1062 0 : mJsepSession->ForEachCodec(configureRed);
1063 : }
1064 :
1065 : // We use this to sort the list of codecs once everything is configured
1066 0 : CompareCodecPriority comparator;
1067 :
1068 : // Sort by priority
1069 0 : int32_t preferredCodec = 0;
1070 0 : branch->GetIntPref("media.navigator.video.preferred_codec",
1071 0 : &preferredCodec);
1072 :
1073 0 : if (preferredCodec) {
1074 0 : comparator.SetPreferredCodec(preferredCodec);
1075 : }
1076 :
1077 0 : mJsepSession->SortCodecs(comparator);
1078 0 : return NS_OK;
1079 : }
1080 :
1081 : // Data channels won't work without a window, so in order for the C++ unit
1082 : // tests to work (it doesn't have a window available) we ifdef the following
1083 : // two implementations.
1084 : NS_IMETHODIMP
1085 0 : PeerConnectionImpl::EnsureDataConnection(uint16_t aLocalPort,
1086 : uint16_t aNumstreams,
1087 : uint32_t aMaxMessageSize,
1088 : bool aMMSSet)
1089 : {
1090 0 : PC_AUTO_ENTER_API_CALL(false);
1091 :
1092 0 : if (mDataConnection) {
1093 0 : CSFLogDebug(logTag,"%s DataConnection already connected",__FUNCTION__);
1094 : // Ignore the request to connect when already connected. This entire
1095 : // implementation is temporary. Ignore aNumstreams as it's merely advisory
1096 : // and we increase the number of streams dynamically as needed.
1097 0 : return NS_OK;
1098 : }
1099 :
1100 : nsCOMPtr<nsIEventTarget> target = mWindow
1101 0 : ? mWindow->EventTargetFor(TaskCategory::Other)
1102 0 : : nullptr;
1103 0 : mDataConnection = new DataChannelConnection(this, target);
1104 0 : if (!mDataConnection->Init(aLocalPort, aNumstreams, true)) {
1105 0 : CSFLogError(logTag,"%s DataConnection Init Failed",__FUNCTION__);
1106 0 : return NS_ERROR_FAILURE;
1107 : }
1108 0 : CSFLogDebug(logTag,"%s DataChannelConnection %p attached to %s",
1109 0 : __FUNCTION__, (void*) mDataConnection.get(), mHandle.c_str());
1110 0 : return NS_OK;
1111 : }
1112 :
1113 : nsresult
1114 0 : PeerConnectionImpl::GetDatachannelParameters(
1115 : uint32_t* channels,
1116 : uint16_t* localport,
1117 : uint16_t* remoteport,
1118 : uint32_t* remotemaxmessagesize,
1119 : bool* mmsset,
1120 : uint16_t* level) const {
1121 :
1122 0 : auto trackPairs = mJsepSession->GetNegotiatedTrackPairs();
1123 0 : for (auto& trackPair : trackPairs) {
1124 : bool sendDataChannel =
1125 0 : trackPair.mSending &&
1126 0 : trackPair.mSending->GetMediaType() == SdpMediaSection::kApplication;
1127 : bool recvDataChannel =
1128 0 : trackPair.mReceiving &&
1129 0 : trackPair.mReceiving->GetMediaType() == SdpMediaSection::kApplication;
1130 : (void)recvDataChannel;
1131 0 : MOZ_ASSERT(sendDataChannel == recvDataChannel);
1132 :
1133 0 : if (sendDataChannel) {
1134 : // This will release assert if there is no such index, and that's ok
1135 : const JsepTrackEncoding& encoding =
1136 0 : trackPair.mSending->GetNegotiatedDetails()->GetEncoding(0);
1137 :
1138 0 : if (encoding.GetCodecs().empty()) {
1139 : CSFLogError(logTag, "%s: Negotiated m=application with no codec. "
1140 : "This is likely to be broken.",
1141 0 : __FUNCTION__);
1142 0 : return NS_ERROR_FAILURE;
1143 : }
1144 :
1145 0 : for (const JsepCodecDescription* codec : encoding.GetCodecs()) {
1146 0 : if (codec->mType != SdpMediaSection::kApplication) {
1147 0 : CSFLogError(logTag, "%s: Codec type for m=application was %u, this "
1148 : "is a bug.",
1149 : __FUNCTION__,
1150 0 : static_cast<unsigned>(codec->mType));
1151 0 : MOZ_ASSERT(false, "Codec for m=application was not \"application\"");
1152 0 : return NS_ERROR_FAILURE;
1153 : }
1154 :
1155 0 : if (codec->mName != "webrtc-datachannel") {
1156 0 : CSFLogWarn(logTag, "%s: Codec for m=application was not "
1157 : "webrtc-datachannel (was instead %s). ",
1158 : __FUNCTION__,
1159 0 : codec->mName.c_str());
1160 0 : continue;
1161 : }
1162 :
1163 0 : if (codec->mChannels) {
1164 0 : *channels = codec->mChannels;
1165 : } else {
1166 0 : *channels = WEBRTC_DATACHANNEL_STREAMS_DEFAULT;
1167 : }
1168 0 : *localport =
1169 0 : static_cast<const JsepApplicationCodecDescription*>(codec)->mLocalPort;
1170 0 : *remoteport =
1171 0 : static_cast<const JsepApplicationCodecDescription*>(codec)->mRemotePort;
1172 0 : *remotemaxmessagesize = static_cast<const JsepApplicationCodecDescription*>
1173 0 : (codec)->mRemoteMaxMessageSize;
1174 0 : *mmsset = static_cast<const JsepApplicationCodecDescription*>
1175 0 : (codec)->mRemoteMMSSet;
1176 0 : if (trackPair.HasBundleLevel()) {
1177 0 : *level = static_cast<uint16_t>(trackPair.BundleLevel());
1178 : } else {
1179 0 : *level = static_cast<uint16_t>(trackPair.mLevel);
1180 : }
1181 0 : return NS_OK;
1182 : }
1183 : }
1184 : }
1185 :
1186 0 : *channels = 0;
1187 0 : *localport = 0;
1188 0 : *remoteport = 0;
1189 0 : *remotemaxmessagesize = 0;
1190 0 : *mmsset = false;
1191 0 : *level = 0;
1192 0 : return NS_ERROR_FAILURE;
1193 : }
1194 :
1195 : /* static */
1196 : void
1197 0 : PeerConnectionImpl::DeferredAddTrackToJsepSession(
1198 : const std::string& pcHandle,
1199 : SdpMediaSection::MediaType type,
1200 : const std::string& streamId,
1201 : const std::string& trackId)
1202 : {
1203 0 : PeerConnectionWrapper wrapper(pcHandle);
1204 :
1205 0 : if (wrapper.impl()) {
1206 0 : if (!PeerConnectionCtx::GetInstance()->isReady()) {
1207 0 : MOZ_CRASH("Why is DeferredAddTrackToJsepSession being executed when the "
1208 : "PeerConnectionCtx isn't ready?");
1209 : }
1210 0 : wrapper.impl()->AddTrackToJsepSession(type, streamId, trackId);
1211 : }
1212 0 : }
1213 :
1214 : nsresult
1215 0 : PeerConnectionImpl::AddTrackToJsepSession(SdpMediaSection::MediaType type,
1216 : const std::string& streamId,
1217 : const std::string& trackId)
1218 : {
1219 0 : nsresult res = ConfigureJsepSessionCodecs();
1220 0 : if (NS_FAILED(res)) {
1221 0 : CSFLogError(logTag, "Failed to configure codecs");
1222 0 : return res;
1223 : }
1224 :
1225 0 : res = mJsepSession->AddTrack(
1226 0 : new JsepTrack(type, streamId, trackId, sdp::kSend));
1227 :
1228 0 : if (NS_FAILED(res)) {
1229 0 : std::string errorString = mJsepSession->GetLastError();
1230 0 : CSFLogError(logTag, "%s (%s) : pc = %s, error = %s",
1231 : __FUNCTION__,
1232 : type == SdpMediaSection::kAudio ? "audio" : "video",
1233 : mHandle.c_str(),
1234 0 : errorString.c_str());
1235 0 : return NS_ERROR_FAILURE;
1236 : }
1237 :
1238 0 : return NS_OK;
1239 : }
1240 :
1241 : nsresult
1242 0 : PeerConnectionImpl::InitializeDataChannel()
1243 : {
1244 0 : PC_AUTO_ENTER_API_CALL(false);
1245 0 : CSFLogDebug(logTag, "%s", __FUNCTION__);
1246 :
1247 0 : uint32_t channels = 0;
1248 0 : uint16_t localport = 0;
1249 0 : uint16_t remoteport = 0;
1250 0 : uint32_t remotemaxmessagesize = 0;
1251 0 : bool mmsset = false;
1252 0 : uint16_t level = 0;
1253 : nsresult rv = GetDatachannelParameters(&channels, &localport, &remoteport,
1254 0 : &remotemaxmessagesize, &mmsset, &level);
1255 :
1256 0 : if (NS_FAILED(rv)) {
1257 0 : CSFLogDebug(logTag, "%s: We did not negotiate datachannel", __FUNCTION__);
1258 0 : return NS_OK;
1259 : }
1260 :
1261 0 : if (channels > MAX_NUM_STREAMS) {
1262 0 : channels = MAX_NUM_STREAMS;
1263 : }
1264 :
1265 0 : rv = EnsureDataConnection(localport, channels, remotemaxmessagesize, mmsset);
1266 0 : if (NS_SUCCEEDED(rv)) {
1267 : // use the specified TransportFlow
1268 0 : RefPtr<TransportFlow> flow = mMedia->GetTransportFlow(level, false).get();
1269 0 : CSFLogDebug(logTag, "Transportflow[%u] = %p",
1270 0 : static_cast<unsigned>(level), flow.get());
1271 0 : if (flow) {
1272 0 : if (mDataConnection->ConnectViaTransportFlow(flow,
1273 : localport,
1274 : remoteport)) {
1275 0 : return NS_OK;
1276 : }
1277 : }
1278 : // If we inited the DataConnection, call Destroy() before releasing it
1279 0 : mDataConnection->Destroy();
1280 : }
1281 0 : mDataConnection = nullptr;
1282 0 : return NS_ERROR_FAILURE;
1283 : }
1284 :
1285 : already_AddRefed<nsDOMDataChannel>
1286 0 : PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel,
1287 : const nsAString& aProtocol,
1288 : uint16_t aType,
1289 : bool ordered,
1290 : uint16_t aMaxTime,
1291 : uint16_t aMaxNum,
1292 : bool aExternalNegotiated,
1293 : uint16_t aStream,
1294 : ErrorResult &rv)
1295 : {
1296 0 : RefPtr<nsDOMDataChannel> result;
1297 0 : rv = CreateDataChannel(aLabel, aProtocol, aType, ordered,
1298 : aMaxTime, aMaxNum, aExternalNegotiated,
1299 0 : aStream, getter_AddRefs(result));
1300 0 : return result.forget();
1301 : }
1302 :
1303 : NS_IMETHODIMP
1304 0 : PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel,
1305 : const nsAString& aProtocol,
1306 : uint16_t aType,
1307 : bool ordered,
1308 : uint16_t aMaxTime,
1309 : uint16_t aMaxNum,
1310 : bool aExternalNegotiated,
1311 : uint16_t aStream,
1312 : nsDOMDataChannel** aRetval)
1313 : {
1314 0 : PC_AUTO_ENTER_API_CALL(false);
1315 0 : MOZ_ASSERT(aRetval);
1316 :
1317 0 : RefPtr<DataChannel> dataChannel;
1318 : DataChannelConnection::Type theType =
1319 0 : static_cast<DataChannelConnection::Type>(aType);
1320 :
1321 : nsresult rv = EnsureDataConnection(WEBRTC_DATACHANNEL_PORT_DEFAULT,
1322 : WEBRTC_DATACHANNEL_STREAMS_DEFAULT,
1323 : WEBRTC_DATACHANELL_MAX_MESSAGE_SIZE_DEFAULT,
1324 0 : false);
1325 0 : if (NS_FAILED(rv)) {
1326 0 : return rv;
1327 : }
1328 0 : dataChannel = mDataConnection->Open(
1329 0 : NS_ConvertUTF16toUTF8(aLabel), NS_ConvertUTF16toUTF8(aProtocol), theType,
1330 : ordered,
1331 0 : aType == DataChannelConnection::PARTIAL_RELIABLE_REXMIT ? aMaxNum :
1332 : (aType == DataChannelConnection::PARTIAL_RELIABLE_TIMED ? aMaxTime : 0),
1333 : nullptr, nullptr, aExternalNegotiated, aStream
1334 0 : );
1335 0 : NS_ENSURE_TRUE(dataChannel,NS_ERROR_FAILURE);
1336 :
1337 0 : CSFLogDebug(logTag, "%s: making DOMDataChannel", __FUNCTION__);
1338 :
1339 0 : if (!mHaveDataStream) {
1340 :
1341 0 : std::string streamId;
1342 0 : std::string trackId;
1343 :
1344 : // Generate random ids because these aren't linked to any local streams.
1345 0 : if (!mUuidGen->Generate(&streamId)) {
1346 0 : return NS_ERROR_FAILURE;
1347 : }
1348 0 : if (!mUuidGen->Generate(&trackId)) {
1349 0 : return NS_ERROR_FAILURE;
1350 : }
1351 :
1352 : RefPtr<JsepTrack> track(new JsepTrack(
1353 : mozilla::SdpMediaSection::kApplication,
1354 : streamId,
1355 : trackId,
1356 0 : sdp::kSend));
1357 :
1358 0 : rv = mJsepSession->AddTrack(track);
1359 0 : if (NS_FAILED(rv)) {
1360 : CSFLogError(logTag, "%s: Failed to add application track.",
1361 0 : __FUNCTION__);
1362 0 : return rv;
1363 : }
1364 0 : mHaveDataStream = true;
1365 0 : OnNegotiationNeeded();
1366 : }
1367 : nsIDOMDataChannel *retval;
1368 0 : rv = NS_NewDOMDataChannel(dataChannel.forget(), mWindow, &retval);
1369 0 : if (NS_FAILED(rv)) {
1370 0 : return rv;
1371 : }
1372 0 : *aRetval = static_cast<nsDOMDataChannel*>(retval);
1373 0 : return NS_OK;
1374 : }
1375 :
1376 : // do_QueryObjectReferent() - Helps get PeerConnectionObserver from nsWeakPtr.
1377 : //
1378 : // nsWeakPtr deals in XPCOM interfaces, while webidl bindings are concrete objs.
1379 : // TODO: Turn this into a central (template) function somewhere (Bug 939178)
1380 : //
1381 : // Without it, each weak-ref call in this file would look like this:
1382 : //
1383 : // nsCOMPtr<nsISupportsWeakReference> tmp = do_QueryReferent(mPCObserver);
1384 : // if (!tmp) {
1385 : // return;
1386 : // }
1387 : // RefPtr<nsSupportsWeakReference> tmp2 = do_QueryObject(tmp);
1388 : // RefPtr<PeerConnectionObserver> pco = static_cast<PeerConnectionObserver*>(&*tmp2);
1389 :
1390 : static already_AddRefed<PeerConnectionObserver>
1391 0 : do_QueryObjectReferent(nsIWeakReference* aRawPtr) {
1392 0 : nsCOMPtr<nsISupportsWeakReference> tmp = do_QueryReferent(aRawPtr);
1393 0 : if (!tmp) {
1394 0 : return nullptr;
1395 : }
1396 0 : RefPtr<nsSupportsWeakReference> tmp2 = do_QueryObject(tmp);
1397 0 : RefPtr<PeerConnectionObserver> tmp3 = static_cast<PeerConnectionObserver*>(&*tmp2);
1398 0 : return tmp3.forget();
1399 : }
1400 :
1401 :
1402 : // Not a member function so that we don't need to keep the PC live.
1403 0 : static void NotifyDataChannel_m(RefPtr<nsIDOMDataChannel> aChannel,
1404 : RefPtr<PeerConnectionObserver> aObserver)
1405 : {
1406 0 : MOZ_ASSERT(NS_IsMainThread());
1407 0 : JSErrorResult rv;
1408 0 : RefPtr<nsDOMDataChannel> channel = static_cast<nsDOMDataChannel*>(&*aChannel);
1409 0 : aObserver->NotifyDataChannel(*channel, rv);
1410 0 : NS_DataChannelAppReady(aChannel);
1411 0 : }
1412 :
1413 : void
1414 0 : PeerConnectionImpl::NotifyDataChannel(already_AddRefed<DataChannel> aChannel)
1415 : {
1416 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
1417 :
1418 : // XXXkhuey this is completely fucked up. We can't use RefPtr<DataChannel>
1419 : // here because DataChannel's AddRef/Release are non-virtual and not visible
1420 : // if !MOZILLA_INTERNAL_API, but this function leaks the DataChannel if
1421 : // !MOZILLA_INTERNAL_API because it never transfers the ref to
1422 : // NS_NewDOMDataChannel.
1423 0 : DataChannel* channel = aChannel.take();
1424 0 : MOZ_ASSERT(channel);
1425 :
1426 0 : CSFLogDebug(logTag, "%s: channel: %p", __FUNCTION__, channel);
1427 :
1428 0 : nsCOMPtr<nsIDOMDataChannel> domchannel;
1429 0 : nsresult rv = NS_NewDOMDataChannel(already_AddRefed<DataChannel>(channel),
1430 0 : mWindow, getter_AddRefs(domchannel));
1431 0 : NS_ENSURE_SUCCESS_VOID(rv);
1432 :
1433 0 : mHaveDataStream = true;
1434 :
1435 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1436 0 : if (!pco) {
1437 0 : return;
1438 : }
1439 :
1440 : RUN_ON_THREAD(mThread,
1441 0 : WrapRunnableNM(NotifyDataChannel_m,
1442 0 : domchannel.get(),
1443 : pco),
1444 0 : NS_DISPATCH_NORMAL);
1445 : }
1446 :
1447 : NS_IMETHODIMP
1448 0 : PeerConnectionImpl::CreateOffer(const RTCOfferOptions& aOptions)
1449 : {
1450 0 : JsepOfferOptions options;
1451 : // convert the RTCOfferOptions to JsepOfferOptions
1452 0 : if (aOptions.mOfferToReceiveAudio.WasPassed()) {
1453 : options.mOfferToReceiveAudio =
1454 0 : mozilla::Some(size_t(aOptions.mOfferToReceiveAudio.Value()));
1455 : }
1456 :
1457 0 : if (aOptions.mOfferToReceiveVideo.WasPassed()) {
1458 : options.mOfferToReceiveVideo =
1459 0 : mozilla::Some(size_t(aOptions.mOfferToReceiveVideo.Value()));
1460 : }
1461 :
1462 0 : options.mIceRestart = mozilla::Some(aOptions.mIceRestart);
1463 :
1464 0 : if (aOptions.mMozDontOfferDataChannel.WasPassed()) {
1465 : options.mDontOfferDataChannel =
1466 0 : mozilla::Some(aOptions.mMozDontOfferDataChannel.Value());
1467 : }
1468 0 : return CreateOffer(options);
1469 : }
1470 :
1471 0 : static void DeferredCreateOffer(const std::string& aPcHandle,
1472 : const JsepOfferOptions& aOptions) {
1473 0 : PeerConnectionWrapper wrapper(aPcHandle);
1474 :
1475 0 : if (wrapper.impl()) {
1476 0 : if (!PeerConnectionCtx::GetInstance()->isReady()) {
1477 0 : MOZ_CRASH("Why is DeferredCreateOffer being executed when the "
1478 : "PeerConnectionCtx isn't ready?");
1479 : }
1480 0 : wrapper.impl()->CreateOffer(aOptions);
1481 : }
1482 0 : }
1483 :
1484 : // Used by unit tests and the IDL CreateOffer.
1485 : NS_IMETHODIMP
1486 0 : PeerConnectionImpl::CreateOffer(const JsepOfferOptions& aOptions)
1487 : {
1488 0 : PC_AUTO_ENTER_API_CALL(true);
1489 0 : bool restartIce = aOptions.mIceRestart.isSome() && *(aOptions.mIceRestart);
1490 0 : if (!restartIce &&
1491 0 : mMedia->GetIceRestartState() ==
1492 : PeerConnectionMedia::ICE_RESTART_PROVISIONAL) {
1493 0 : RollbackIceRestart();
1494 : }
1495 :
1496 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1497 0 : if (!pco) {
1498 0 : return NS_OK;
1499 : }
1500 :
1501 0 : if (!PeerConnectionCtx::GetInstance()->isReady()) {
1502 : // Uh oh. We're not ready yet. Enqueue this operation.
1503 0 : PeerConnectionCtx::GetInstance()->queueJSEPOperation(
1504 0 : WrapRunnableNM(DeferredCreateOffer, mHandle, aOptions));
1505 0 : STAMP_TIMECARD(mTimeCard, "Deferring CreateOffer (not ready)");
1506 0 : return NS_OK;
1507 : }
1508 :
1509 0 : CSFLogDebug(logTag, "CreateOffer()");
1510 :
1511 : nsresult nrv;
1512 0 : if (restartIce &&
1513 0 : !mJsepSession->GetLocalDescription(kJsepDescriptionCurrent).empty()) {
1514 : // If restart is requested and a restart is already in progress, we
1515 : // need to make room for the restart request so we either rollback
1516 : // or finalize to "clear" the previous restart.
1517 0 : if (mMedia->GetIceRestartState() ==
1518 : PeerConnectionMedia::ICE_RESTART_PROVISIONAL) {
1519 : // we're mid-restart and can rollback
1520 0 : RollbackIceRestart();
1521 0 : } else if (mMedia->GetIceRestartState() ==
1522 : PeerConnectionMedia::ICE_RESTART_COMMITTED) {
1523 : // we're mid-restart and can't rollback, finalize restart even
1524 : // though we're not really ready yet
1525 0 : FinalizeIceRestart();
1526 : }
1527 :
1528 0 : CSFLogInfo(logTag, "Offerer restarting ice");
1529 0 : nrv = SetupIceRestart();
1530 0 : if (NS_FAILED(nrv)) {
1531 : CSFLogError(logTag, "%s: SetupIceRestart failed, res=%u",
1532 : __FUNCTION__,
1533 0 : static_cast<unsigned>(nrv));
1534 0 : return nrv;
1535 : }
1536 : }
1537 :
1538 0 : nrv = ConfigureJsepSessionCodecs();
1539 0 : if (NS_FAILED(nrv)) {
1540 0 : CSFLogError(logTag, "Failed to configure codecs");
1541 0 : return nrv;
1542 : }
1543 :
1544 0 : STAMP_TIMECARD(mTimeCard, "Create Offer");
1545 :
1546 0 : std::string offer;
1547 :
1548 0 : nrv = mJsepSession->CreateOffer(aOptions, &offer);
1549 0 : JSErrorResult rv;
1550 0 : if (NS_FAILED(nrv)) {
1551 : Error error;
1552 0 : switch (nrv) {
1553 : case NS_ERROR_UNEXPECTED:
1554 0 : error = kInvalidState;
1555 0 : break;
1556 : default:
1557 0 : error = kInternalError;
1558 : }
1559 0 : std::string errorString = mJsepSession->GetLastError();
1560 :
1561 0 : CSFLogError(logTag, "%s: pc = %s, error = %s",
1562 0 : __FUNCTION__, mHandle.c_str(), errorString.c_str());
1563 0 : pco->OnCreateOfferError(error, ObString(errorString.c_str()), rv);
1564 : } else {
1565 0 : pco->OnCreateOfferSuccess(ObString(offer.c_str()), rv);
1566 : }
1567 :
1568 0 : UpdateSignalingState();
1569 0 : return NS_OK;
1570 : }
1571 :
1572 : NS_IMETHODIMP
1573 0 : PeerConnectionImpl::CreateAnswer()
1574 : {
1575 0 : PC_AUTO_ENTER_API_CALL(true);
1576 :
1577 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1578 0 : if (!pco) {
1579 0 : return NS_OK;
1580 : }
1581 :
1582 0 : CSFLogDebug(logTag, "CreateAnswer()");
1583 :
1584 : nsresult nrv;
1585 0 : if (mJsepSession->RemoteIceIsRestarting()) {
1586 0 : if (mMedia->GetIceRestartState() ==
1587 : PeerConnectionMedia::ICE_RESTART_COMMITTED) {
1588 0 : FinalizeIceRestart();
1589 0 : } else if (!mMedia->IsIceRestarting()) {
1590 0 : CSFLogInfo(logTag, "Answerer restarting ice");
1591 0 : nrv = SetupIceRestart();
1592 0 : if (NS_FAILED(nrv)) {
1593 : CSFLogError(logTag, "%s: SetupIceRestart failed, res=%u",
1594 : __FUNCTION__,
1595 0 : static_cast<unsigned>(nrv));
1596 0 : return nrv;
1597 : }
1598 : }
1599 : }
1600 :
1601 0 : STAMP_TIMECARD(mTimeCard, "Create Answer");
1602 : // TODO(bug 1098015): Once RTCAnswerOptions is standardized, we'll need to
1603 : // add it as a param to CreateAnswer, and convert it here.
1604 : JsepAnswerOptions options;
1605 0 : std::string answer;
1606 :
1607 0 : nrv = mJsepSession->CreateAnswer(options, &answer);
1608 0 : JSErrorResult rv;
1609 0 : if (NS_FAILED(nrv)) {
1610 : Error error;
1611 0 : switch (nrv) {
1612 : case NS_ERROR_UNEXPECTED:
1613 0 : error = kInvalidState;
1614 0 : break;
1615 : default:
1616 0 : error = kInternalError;
1617 : }
1618 0 : std::string errorString = mJsepSession->GetLastError();
1619 :
1620 0 : CSFLogError(logTag, "%s: pc = %s, error = %s",
1621 0 : __FUNCTION__, mHandle.c_str(), errorString.c_str());
1622 0 : pco->OnCreateAnswerError(error, ObString(errorString.c_str()), rv);
1623 : } else {
1624 0 : pco->OnCreateAnswerSuccess(ObString(answer.c_str()), rv);
1625 : }
1626 :
1627 0 : UpdateSignalingState();
1628 :
1629 0 : return NS_OK;
1630 : }
1631 :
1632 : nsresult
1633 0 : PeerConnectionImpl::SetupIceRestart()
1634 : {
1635 0 : if (mMedia->IsIceRestarting()) {
1636 : CSFLogError(logTag, "%s: ICE already restarting",
1637 0 : __FUNCTION__);
1638 0 : return NS_ERROR_UNEXPECTED;
1639 : }
1640 :
1641 0 : std::string ufrag = mMedia->ice_ctx()->GetNewUfrag();
1642 0 : std::string pwd = mMedia->ice_ctx()->GetNewPwd();
1643 0 : if (ufrag.empty() || pwd.empty()) {
1644 0 : CSFLogError(logTag, "%s: Bad ICE credentials (ufrag:'%s'/pwd:'%s')",
1645 : __FUNCTION__,
1646 0 : ufrag.c_str(), pwd.c_str());
1647 0 : return NS_ERROR_UNEXPECTED;
1648 : }
1649 :
1650 : // hold on to the current ice creds in case of rollback
1651 0 : mPreviousIceUfrag = mJsepSession->GetUfrag();
1652 0 : mPreviousIcePwd = mJsepSession->GetPwd();
1653 0 : mMedia->BeginIceRestart(ufrag, pwd);
1654 :
1655 0 : nsresult nrv = mJsepSession->SetIceCredentials(ufrag, pwd);
1656 0 : if (NS_FAILED(nrv)) {
1657 : CSFLogError(logTag, "%s: Couldn't set ICE credentials, res=%u",
1658 : __FUNCTION__,
1659 0 : static_cast<unsigned>(nrv));
1660 0 : return nrv;
1661 : }
1662 :
1663 0 : return NS_OK;
1664 : }
1665 :
1666 : nsresult
1667 0 : PeerConnectionImpl::RollbackIceRestart()
1668 : {
1669 0 : mMedia->RollbackIceRestart();
1670 : // put back the previous ice creds
1671 0 : nsresult nrv = mJsepSession->SetIceCredentials(mPreviousIceUfrag,
1672 0 : mPreviousIcePwd);
1673 0 : if (NS_FAILED(nrv)) {
1674 : CSFLogError(logTag, "%s: Couldn't set ICE credentials, res=%u",
1675 : __FUNCTION__,
1676 0 : static_cast<unsigned>(nrv));
1677 0 : return nrv;
1678 : }
1679 0 : mPreviousIceUfrag = "";
1680 0 : mPreviousIcePwd = "";
1681 :
1682 0 : return NS_OK;
1683 : }
1684 :
1685 : void
1686 0 : PeerConnectionImpl::FinalizeIceRestart()
1687 : {
1688 0 : mMedia->FinalizeIceRestart();
1689 : // clear the previous ice creds since they are no longer needed
1690 0 : mPreviousIceUfrag = "";
1691 0 : mPreviousIcePwd = "";
1692 0 : }
1693 :
1694 : NS_IMETHODIMP
1695 0 : PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
1696 : {
1697 0 : PC_AUTO_ENTER_API_CALL(true);
1698 :
1699 0 : if (!aSDP) {
1700 0 : CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
1701 0 : return NS_ERROR_FAILURE;
1702 : }
1703 :
1704 0 : JSErrorResult rv;
1705 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
1706 0 : if (!pco) {
1707 0 : return NS_OK;
1708 : }
1709 :
1710 0 : STAMP_TIMECARD(mTimeCard, "Set Local Description");
1711 :
1712 0 : bool isolated = mMedia->AnyLocalTrackHasPeerIdentity();
1713 0 : mPrivacyRequested = mPrivacyRequested || isolated;
1714 :
1715 0 : mLocalRequestedSDP = aSDP;
1716 :
1717 : JsepSdpType sdpType;
1718 0 : switch (aAction) {
1719 : case IPeerConnection::kActionOffer:
1720 0 : sdpType = mozilla::kJsepSdpOffer;
1721 0 : break;
1722 : case IPeerConnection::kActionAnswer:
1723 0 : sdpType = mozilla::kJsepSdpAnswer;
1724 0 : break;
1725 : case IPeerConnection::kActionPRAnswer:
1726 0 : sdpType = mozilla::kJsepSdpPranswer;
1727 0 : break;
1728 : case IPeerConnection::kActionRollback:
1729 0 : sdpType = mozilla::kJsepSdpRollback;
1730 0 : break;
1731 : default:
1732 0 : MOZ_ASSERT(false);
1733 : return NS_ERROR_FAILURE;
1734 :
1735 : }
1736 0 : nsresult nrv = mJsepSession->SetLocalDescription(sdpType,
1737 0 : mLocalRequestedSDP);
1738 0 : if (NS_FAILED(nrv)) {
1739 : Error error;
1740 0 : switch (nrv) {
1741 : case NS_ERROR_INVALID_ARG:
1742 0 : error = kInvalidSessionDescription;
1743 0 : break;
1744 : case NS_ERROR_UNEXPECTED:
1745 0 : error = kInvalidState;
1746 0 : break;
1747 : default:
1748 0 : error = kInternalError;
1749 : }
1750 :
1751 0 : std::string errorString = mJsepSession->GetLastError();
1752 0 : CSFLogError(logTag, "%s: pc = %s, error = %s",
1753 0 : __FUNCTION__, mHandle.c_str(), errorString.c_str());
1754 0 : pco->OnSetLocalDescriptionError(error, ObString(errorString.c_str()), rv);
1755 : } else {
1756 0 : pco->OnSetLocalDescriptionSuccess(rv);
1757 : }
1758 :
1759 0 : UpdateSignalingState(sdpType == mozilla::kJsepSdpRollback);
1760 0 : return NS_OK;
1761 : }
1762 :
1763 0 : static void DeferredSetRemote(const std::string& aPcHandle,
1764 : int32_t aAction,
1765 : const std::string& aSdp) {
1766 0 : PeerConnectionWrapper wrapper(aPcHandle);
1767 :
1768 0 : if (wrapper.impl()) {
1769 0 : if (!PeerConnectionCtx::GetInstance()->isReady()) {
1770 0 : MOZ_CRASH("Why is DeferredSetRemote being executed when the "
1771 : "PeerConnectionCtx isn't ready?");
1772 : }
1773 0 : wrapper.impl()->SetRemoteDescription(aAction, aSdp.c_str());
1774 : }
1775 0 : }
1776 :
1777 0 : static void StartTrack(MediaStream* aSource,
1778 : TrackID aTrackId,
1779 : nsAutoPtr<MediaSegment>&& aSegment) {
1780 0 : class Message : public ControlMessage {
1781 : public:
1782 0 : Message(MediaStream* aStream,
1783 : TrackID aTrack,
1784 : nsAutoPtr<MediaSegment>&& aSegment)
1785 0 : : ControlMessage(aStream),
1786 : track_id_(aTrack),
1787 0 : segment_(aSegment) {}
1788 :
1789 0 : virtual void Run() override {
1790 0 : TrackRate track_rate = segment_->GetType() == MediaSegment::AUDIO ?
1791 0 : WEBRTC_DEFAULT_SAMPLE_RATE : mStream->GraphRate();
1792 0 : StreamTime current_end = mStream->GetTracksEnd();
1793 : TrackTicks current_ticks =
1794 0 : mStream->TimeToTicksRoundUp(track_rate, current_end);
1795 :
1796 : // Add a track 'now' to avoid possible underrun, especially if we add
1797 : // a track "later".
1798 :
1799 0 : if (current_end != 0L) {
1800 0 : CSFLogDebug(logTag, "added track @ %u -> %f",
1801 : static_cast<unsigned>(current_end),
1802 0 : mStream->StreamTimeToSeconds(current_end));
1803 : }
1804 :
1805 : // To avoid assertions, we need to insert a dummy segment that covers up
1806 : // to the "start" time for the track
1807 0 : segment_->AppendNullData(current_ticks);
1808 0 : if (segment_->GetType() == MediaSegment::AUDIO) {
1809 0 : mStream->AsSourceStream()->AddAudioTrack(
1810 : track_id_,
1811 : WEBRTC_DEFAULT_SAMPLE_RATE,
1812 : 0,
1813 0 : static_cast<AudioSegment*>(segment_.forget()));
1814 : } else {
1815 0 : mStream->AsSourceStream()->AddTrack(track_id_, 0, segment_.forget());
1816 : }
1817 0 : }
1818 : private:
1819 : TrackID track_id_;
1820 : nsAutoPtr<MediaSegment> segment_;
1821 : };
1822 :
1823 0 : aSource->GraphImpl()->AppendMessage(
1824 0 : MakeUnique<Message>(aSource, aTrackId, Move(aSegment)));
1825 : CSFLogInfo(logTag, "Dispatched track-add for track id %u on stream %p",
1826 0 : aTrackId, aSource);
1827 0 : }
1828 :
1829 :
1830 : nsresult
1831 0 : PeerConnectionImpl::CreateNewRemoteTracks(RefPtr<PeerConnectionObserver>& aPco)
1832 : {
1833 0 : JSErrorResult jrv;
1834 :
1835 : std::vector<RefPtr<JsepTrack>> newTracks =
1836 0 : mJsepSession->GetRemoteTracksAdded();
1837 :
1838 : // Group new tracks by stream id
1839 0 : std::map<std::string, std::vector<RefPtr<JsepTrack>>> tracksByStreamId;
1840 0 : for (auto track : newTracks) {
1841 0 : if (track->GetMediaType() == mozilla::SdpMediaSection::kApplication) {
1842 : // Ignore datachannel
1843 0 : continue;
1844 : }
1845 :
1846 0 : tracksByStreamId[track->GetStreamId()].push_back(track);
1847 : }
1848 :
1849 0 : for (auto& id : tracksByStreamId) {
1850 0 : std::string streamId = id.first;
1851 0 : std::vector<RefPtr<JsepTrack>>& tracks = id.second;
1852 :
1853 0 : bool newStream = false;
1854 : RefPtr<RemoteSourceStreamInfo> info =
1855 0 : mMedia->GetRemoteStreamById(streamId);
1856 0 : if (!info) {
1857 0 : newStream = true;
1858 0 : nsresult nrv = CreateRemoteSourceStreamInfo(&info, streamId);
1859 0 : if (NS_FAILED(nrv)) {
1860 0 : aPco->OnSetRemoteDescriptionError(
1861 : kInternalError,
1862 0 : ObString("CreateRemoteSourceStreamInfo failed"),
1863 0 : jrv);
1864 0 : return nrv;
1865 : }
1866 :
1867 0 : nrv = mMedia->AddRemoteStream(info);
1868 0 : if (NS_FAILED(nrv)) {
1869 0 : aPco->OnSetRemoteDescriptionError(
1870 : kInternalError,
1871 0 : ObString("AddRemoteStream failed"),
1872 0 : jrv);
1873 0 : return nrv;
1874 : }
1875 :
1876 0 : CSFLogDebug(logTag, "Added remote stream %s", info->GetId().c_str());
1877 :
1878 0 : info->GetMediaStream()->AssignId(NS_ConvertUTF8toUTF16(streamId.c_str()));
1879 0 : info->GetMediaStream()->SetLogicalStreamStartTime(
1880 0 : info->GetMediaStream()->GetPlaybackStream()->GetCurrentTime());
1881 : }
1882 :
1883 0 : Sequence<OwningNonNull<DOMMediaStream>> streams;
1884 0 : if (!streams.AppendElement(OwningNonNull<DOMMediaStream>(
1885 0 : *info->GetMediaStream()),
1886 : fallible)) {
1887 0 : MOZ_ASSERT(false);
1888 : return NS_ERROR_FAILURE;
1889 : }
1890 :
1891 : // Set the principal used for creating the tracks. This makes the stream
1892 : // data (audio/video samples) accessible to the receiving page. We're
1893 : // only certain that privacy hasn't been requested if we're connected.
1894 0 : nsCOMPtr<nsIPrincipal> principal;
1895 0 : nsIDocument* doc = GetWindow()->GetExtantDoc();
1896 0 : MOZ_ASSERT(doc);
1897 0 : if (mDtlsConnected && !PrivacyRequested()) {
1898 0 : principal = doc->NodePrincipal();
1899 : } else {
1900 : // we're either certain that we need isolation for the streams, OR
1901 : // we're not sure and we can fix the stream in SetDtlsConnected
1902 0 : principal = NullPrincipal::CreateWithInheritedAttributes(doc->NodePrincipal());
1903 : }
1904 :
1905 : // We need to select unique ids, just use max + 1
1906 0 : TrackID maxTrackId = 0;
1907 : {
1908 0 : nsTArray<RefPtr<dom::MediaStreamTrack>> domTracks;
1909 0 : info->GetMediaStream()->GetTracks(domTracks);
1910 0 : for (auto& track : domTracks) {
1911 0 : maxTrackId = std::max(maxTrackId, track->mTrackID);
1912 : }
1913 : }
1914 :
1915 0 : for (RefPtr<JsepTrack>& track : tracks) {
1916 0 : std::string webrtcTrackId(track->GetTrackId());
1917 0 : if (!info->HasTrack(webrtcTrackId)) {
1918 : RefPtr<RemoteTrackSource> source =
1919 0 : new RemoteTrackSource(principal, nsString());
1920 0 : TrackID trackID = ++maxTrackId;
1921 0 : RefPtr<MediaStreamTrack> domTrack;
1922 0 : nsAutoPtr<MediaSegment> segment;
1923 0 : if (track->GetMediaType() == SdpMediaSection::kAudio) {
1924 : domTrack =
1925 0 : info->GetMediaStream()->CreateDOMTrack(trackID,
1926 : MediaSegment::AUDIO,
1927 0 : source);
1928 0 : info->GetMediaStream()->AddTrackInternal(domTrack);
1929 0 : segment = new AudioSegment;
1930 : } else {
1931 : domTrack =
1932 0 : info->GetMediaStream()->CreateDOMTrack(trackID,
1933 : MediaSegment::VIDEO,
1934 0 : source);
1935 0 : info->GetMediaStream()->AddTrackInternal(domTrack);
1936 0 : segment = new VideoSegment;
1937 : }
1938 :
1939 0 : StartTrack(info->GetMediaStream()->GetInputStream()->AsSourceStream(),
1940 0 : trackID, Move(segment));
1941 0 : info->AddTrack(webrtcTrackId, domTrack);
1942 0 : CSFLogDebug(logTag, "Added remote track %s/%s",
1943 0 : info->GetId().c_str(), webrtcTrackId.c_str());
1944 :
1945 0 : domTrack->AssignId(NS_ConvertUTF8toUTF16(webrtcTrackId.c_str()));
1946 0 : aPco->OnAddTrack(*domTrack, streams, jrv);
1947 0 : if (jrv.Failed()) {
1948 0 : CSFLogError(logTag, ": OnAddTrack(%s) failed! Error: %u",
1949 : webrtcTrackId.c_str(),
1950 0 : jrv.ErrorCodeAsInt());
1951 : }
1952 : }
1953 : }
1954 :
1955 0 : if (newStream) {
1956 0 : aPco->OnAddStream(*info->GetMediaStream(), jrv);
1957 0 : if (jrv.Failed()) {
1958 0 : CSFLogError(logTag, ": OnAddStream() failed! Error: %u",
1959 0 : jrv.ErrorCodeAsInt());
1960 : }
1961 : }
1962 : }
1963 0 : return NS_OK;
1964 : }
1965 :
1966 : void
1967 0 : PeerConnectionImpl::RemoveOldRemoteTracks(RefPtr<PeerConnectionObserver>& aPco)
1968 : {
1969 0 : JSErrorResult jrv;
1970 :
1971 : std::vector<RefPtr<JsepTrack>> removedTracks =
1972 0 : mJsepSession->GetRemoteTracksRemoved();
1973 :
1974 0 : for (auto& removedTrack : removedTracks) {
1975 0 : const std::string& streamId = removedTrack->GetStreamId();
1976 0 : const std::string& trackId = removedTrack->GetTrackId();
1977 :
1978 0 : RefPtr<RemoteSourceStreamInfo> info = mMedia->GetRemoteStreamById(streamId);
1979 0 : if (!info) {
1980 0 : MOZ_ASSERT(false, "A stream/track was removed that wasn't in PCMedia. "
1981 : "This is a bug.");
1982 : continue;
1983 : }
1984 :
1985 0 : mMedia->RemoveRemoteTrack(streamId, trackId);
1986 :
1987 0 : DOMMediaStream* stream = info->GetMediaStream();
1988 0 : nsTArray<RefPtr<MediaStreamTrack>> tracks;
1989 0 : stream->GetTracks(tracks);
1990 0 : for (auto& track : tracks) {
1991 0 : if (PeerConnectionImpl::GetTrackId(*track) == trackId) {
1992 0 : aPco->OnRemoveTrack(*track, jrv);
1993 0 : break;
1994 : }
1995 : }
1996 :
1997 : // We might be holding the last ref, but that's ok.
1998 0 : if (!info->GetTrackCount()) {
1999 0 : aPco->OnRemoveStream(*stream, jrv);
2000 : }
2001 : }
2002 0 : }
2003 :
2004 : NS_IMETHODIMP
2005 0 : PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
2006 : {
2007 0 : PC_AUTO_ENTER_API_CALL(true);
2008 :
2009 0 : if (!aSDP) {
2010 0 : CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
2011 0 : return NS_ERROR_FAILURE;
2012 : }
2013 :
2014 0 : JSErrorResult jrv;
2015 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
2016 0 : if (!pco) {
2017 0 : return NS_OK;
2018 : }
2019 :
2020 0 : if (action == IPeerConnection::kActionOffer) {
2021 0 : if (!PeerConnectionCtx::GetInstance()->isReady()) {
2022 : // Uh oh. We're not ready yet. Enqueue this operation. (This must be a
2023 : // remote offer, or else we would not have gotten this far)
2024 0 : PeerConnectionCtx::GetInstance()->queueJSEPOperation(
2025 0 : WrapRunnableNM(DeferredSetRemote,
2026 : mHandle,
2027 : action,
2028 0 : std::string(aSDP)));
2029 0 : STAMP_TIMECARD(mTimeCard, "Deferring SetRemote (not ready)");
2030 0 : return NS_OK;
2031 : }
2032 :
2033 0 : nsresult nrv = ConfigureJsepSessionCodecs();
2034 0 : if (NS_FAILED(nrv)) {
2035 0 : CSFLogError(logTag, "Failed to configure codecs");
2036 0 : return nrv;
2037 : }
2038 : }
2039 :
2040 0 : STAMP_TIMECARD(mTimeCard, "Set Remote Description");
2041 :
2042 0 : mRemoteRequestedSDP = aSDP;
2043 : JsepSdpType sdpType;
2044 0 : switch (action) {
2045 : case IPeerConnection::kActionOffer:
2046 0 : sdpType = mozilla::kJsepSdpOffer;
2047 0 : break;
2048 : case IPeerConnection::kActionAnswer:
2049 0 : sdpType = mozilla::kJsepSdpAnswer;
2050 0 : break;
2051 : case IPeerConnection::kActionPRAnswer:
2052 0 : sdpType = mozilla::kJsepSdpPranswer;
2053 0 : break;
2054 : case IPeerConnection::kActionRollback:
2055 0 : sdpType = mozilla::kJsepSdpRollback;
2056 0 : break;
2057 : default:
2058 0 : MOZ_ASSERT(false);
2059 : return NS_ERROR_FAILURE;
2060 : }
2061 :
2062 0 : nsresult nrv = mJsepSession->SetRemoteDescription(sdpType,
2063 0 : mRemoteRequestedSDP);
2064 0 : if (NS_FAILED(nrv)) {
2065 : Error error;
2066 0 : switch (nrv) {
2067 : case NS_ERROR_INVALID_ARG:
2068 0 : error = kInvalidSessionDescription;
2069 0 : break;
2070 : case NS_ERROR_UNEXPECTED:
2071 0 : error = kInvalidState;
2072 0 : break;
2073 : default:
2074 0 : error = kInternalError;
2075 : }
2076 :
2077 0 : std::string errorString = mJsepSession->GetLastError();
2078 0 : CSFLogError(logTag, "%s: pc = %s, error = %s",
2079 0 : __FUNCTION__, mHandle.c_str(), errorString.c_str());
2080 0 : pco->OnSetRemoteDescriptionError(error, ObString(errorString.c_str()), jrv);
2081 : } else {
2082 0 : nrv = CreateNewRemoteTracks(pco);
2083 0 : if (NS_FAILED(nrv)) {
2084 : // aPco was already notified, just return early.
2085 0 : return NS_OK;
2086 : }
2087 :
2088 0 : RemoveOldRemoteTracks(pco);
2089 :
2090 0 : pco->OnSetRemoteDescriptionSuccess(jrv);
2091 0 : startCallTelem();
2092 : }
2093 :
2094 0 : UpdateSignalingState(sdpType == mozilla::kJsepSdpRollback);
2095 0 : return NS_OK;
2096 : }
2097 :
2098 : // WebRTC uses highres time relative to the UNIX epoch (Jan 1, 1970, UTC).
2099 :
2100 : nsresult
2101 0 : PeerConnectionImpl::GetTimeSinceEpoch(DOMHighResTimeStamp *result) {
2102 0 : MOZ_ASSERT(NS_IsMainThread());
2103 0 : Performance *perf = mWindow->GetPerformance();
2104 0 : NS_ENSURE_TRUE(perf && perf->Timing(), NS_ERROR_UNEXPECTED);
2105 0 : *result = perf->Now() + perf->Timing()->NavigationStart();
2106 0 : return NS_OK;
2107 : }
2108 :
2109 : class RTCStatsReportInternalConstruct : public RTCStatsReportInternal {
2110 : public:
2111 0 : RTCStatsReportInternalConstruct(const nsString &pcid, DOMHighResTimeStamp now) {
2112 0 : mPcid = pcid;
2113 0 : mRtpContributingSourceStats.Construct();
2114 0 : mInboundRTPStreamStats.Construct();
2115 0 : mOutboundRTPStreamStats.Construct();
2116 0 : mMediaStreamTrackStats.Construct();
2117 0 : mMediaStreamStats.Construct();
2118 0 : mTransportStats.Construct();
2119 0 : mIceComponentStats.Construct();
2120 0 : mIceCandidatePairStats.Construct();
2121 0 : mIceCandidateStats.Construct();
2122 0 : mCodecStats.Construct();
2123 0 : mTimestamp.Construct(now);
2124 0 : }
2125 : };
2126 :
2127 : NS_IMETHODIMP
2128 0 : PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector) {
2129 0 : PC_AUTO_ENTER_API_CALL(true);
2130 :
2131 0 : if (!mMedia) {
2132 : // Since we zero this out before the d'tor, we should check.
2133 0 : return NS_ERROR_UNEXPECTED;
2134 : }
2135 :
2136 0 : nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(false));
2137 :
2138 0 : nsresult rv = BuildStatsQuery_m(aSelector, query.get());
2139 :
2140 0 : NS_ENSURE_SUCCESS(rv, rv);
2141 :
2142 0 : RUN_ON_THREAD(mSTSThread,
2143 0 : WrapRunnableNM(&PeerConnectionImpl::GetStatsForPCObserver_s,
2144 : mHandle,
2145 : query),
2146 0 : NS_DISPATCH_NORMAL);
2147 0 : return NS_OK;
2148 : }
2149 :
2150 : NS_IMETHODIMP
2151 0 : PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel) {
2152 0 : PC_AUTO_ENTER_API_CALL(true);
2153 :
2154 0 : if (mForceIceTcp && std::string::npos != std::string(aCandidate).find(" UDP ")) {
2155 0 : CSFLogError(logTag, "Blocking remote UDP candidate: %s", aCandidate);
2156 0 : return NS_OK;
2157 : }
2158 :
2159 0 : JSErrorResult rv;
2160 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
2161 0 : if (!pco) {
2162 0 : return NS_OK;
2163 : }
2164 :
2165 0 : STAMP_TIMECARD(mTimeCard, "Add Ice Candidate");
2166 :
2167 0 : CSFLogDebug(logTag, "AddIceCandidate: %s", aCandidate);
2168 :
2169 : // When remote candidates are added before our ICE ctx is up and running
2170 : // (the transition to New is async through STS, so this is not impossible),
2171 : // we won't record them as trickle candidates. Is this what we want?
2172 0 : if(!mIceStartTime.IsNull()) {
2173 0 : TimeDuration timeDelta = TimeStamp::Now() - mIceStartTime;
2174 0 : if (mIceConnectionState == PCImplIceConnectionState::Failed) {
2175 0 : Telemetry::Accumulate(Telemetry::WEBRTC_ICE_LATE_TRICKLE_ARRIVAL_TIME,
2176 0 : timeDelta.ToMilliseconds());
2177 : } else {
2178 0 : Telemetry::Accumulate(Telemetry::WEBRTC_ICE_ON_TIME_TRICKLE_ARRIVAL_TIME,
2179 0 : timeDelta.ToMilliseconds());
2180 : }
2181 : }
2182 :
2183 0 : nsresult res = mJsepSession->AddRemoteIceCandidate(aCandidate, aMid, aLevel);
2184 :
2185 0 : if (NS_SUCCEEDED(res)) {
2186 : // We do not bother PCMedia about this before offer/answer concludes.
2187 : // Once offer/answer concludes, PCMedia will extract these candidates from
2188 : // the remote SDP.
2189 0 : if (mSignalingState == PCImplSignalingState::SignalingStable) {
2190 0 : mMedia->AddIceCandidate(aCandidate, aMid, aLevel);
2191 : }
2192 0 : pco->OnAddIceCandidateSuccess(rv);
2193 : } else {
2194 0 : ++mAddCandidateErrorCount;
2195 : Error error;
2196 0 : switch (res) {
2197 : case NS_ERROR_UNEXPECTED:
2198 0 : error = kInvalidState;
2199 0 : break;
2200 : case NS_ERROR_INVALID_ARG:
2201 0 : error = kInvalidCandidate;
2202 0 : break;
2203 : default:
2204 0 : error = kInternalError;
2205 : }
2206 :
2207 0 : std::string errorString = mJsepSession->GetLastError();
2208 :
2209 0 : CSFLogError(logTag, "Failed to incorporate remote candidate into SDP:"
2210 : " res = %u, candidate = %s, level = %u, error = %s",
2211 : static_cast<unsigned>(res),
2212 : aCandidate,
2213 : static_cast<unsigned>(aLevel),
2214 0 : errorString.c_str());
2215 :
2216 0 : pco->OnAddIceCandidateError(error, ObString(errorString.c_str()), rv);
2217 : }
2218 :
2219 0 : return NS_OK;
2220 : }
2221 :
2222 : void
2223 0 : PeerConnectionImpl::UpdateNetworkState(bool online) {
2224 0 : if (!mMedia) {
2225 0 : return;
2226 : }
2227 0 : mMedia->UpdateNetworkState(online);
2228 : }
2229 :
2230 : NS_IMETHODIMP
2231 0 : PeerConnectionImpl::CloseStreams() {
2232 0 : PC_AUTO_ENTER_API_CALL(false);
2233 :
2234 0 : return NS_OK;
2235 : }
2236 :
2237 : nsresult
2238 0 : PeerConnectionImpl::SetPeerIdentity(const nsAString& aPeerIdentity)
2239 : {
2240 0 : PC_AUTO_ENTER_API_CALL(true);
2241 0 : MOZ_ASSERT(!aPeerIdentity.IsEmpty());
2242 :
2243 : // once set, this can't be changed
2244 0 : if (mPeerIdentity) {
2245 0 : if (!mPeerIdentity->Equals(aPeerIdentity)) {
2246 0 : return NS_ERROR_FAILURE;
2247 : }
2248 : } else {
2249 0 : mPeerIdentity = new PeerIdentity(aPeerIdentity);
2250 0 : nsIDocument* doc = GetWindow()->GetExtantDoc();
2251 0 : if (!doc) {
2252 0 : CSFLogInfo(logTag, "Can't update principal on streams; document gone");
2253 0 : return NS_ERROR_FAILURE;
2254 : }
2255 0 : MediaStreamTrack* allTracks = nullptr;
2256 0 : mMedia->UpdateSinkIdentity_m(allTracks, doc->NodePrincipal(), mPeerIdentity);
2257 : }
2258 0 : return NS_OK;
2259 : }
2260 :
2261 : nsresult
2262 0 : PeerConnectionImpl::SetDtlsConnected(bool aPrivacyRequested)
2263 : {
2264 0 : PC_AUTO_ENTER_API_CALL(false);
2265 :
2266 : // For this, as with mPrivacyRequested, once we've connected to a peer, we
2267 : // fixate on that peer. Dealing with multiple peers or connections is more
2268 : // than this run-down wreck of an object can handle.
2269 : // Besides, this is only used to say if we have been connected ever.
2270 0 : if (!mPrivacyRequested && !aPrivacyRequested && !mDtlsConnected) {
2271 : // now we know that privacy isn't needed for sure
2272 0 : nsIDocument* doc = GetWindow()->GetExtantDoc();
2273 0 : if (!doc) {
2274 0 : CSFLogInfo(logTag, "Can't update principal on streams; document gone");
2275 0 : return NS_ERROR_FAILURE;
2276 : }
2277 0 : mMedia->UpdateRemoteStreamPrincipals_m(doc->NodePrincipal());
2278 : }
2279 0 : mDtlsConnected = true;
2280 0 : mPrivacyRequested = mPrivacyRequested || aPrivacyRequested;
2281 0 : return NS_OK;
2282 : }
2283 :
2284 : void
2285 0 : PeerConnectionImpl::PrincipalChanged(MediaStreamTrack* aTrack) {
2286 0 : nsIDocument* doc = GetWindow()->GetExtantDoc();
2287 0 : if (doc) {
2288 0 : mMedia->UpdateSinkIdentity_m(aTrack, doc->NodePrincipal(), mPeerIdentity);
2289 : } else {
2290 0 : CSFLogInfo(logTag, "Can't update sink principal; document gone");
2291 : }
2292 0 : }
2293 :
2294 : std::string
2295 0 : PeerConnectionImpl::GetTrackId(const MediaStreamTrack& aTrack)
2296 : {
2297 0 : nsString wideTrackId;
2298 0 : aTrack.GetId(wideTrackId);
2299 0 : return NS_ConvertUTF16toUTF8(wideTrackId).get();
2300 : }
2301 :
2302 : std::string
2303 0 : PeerConnectionImpl::GetStreamId(const DOMMediaStream& aStream)
2304 : {
2305 0 : nsString wideStreamId;
2306 0 : aStream.GetId(wideStreamId);
2307 0 : return NS_ConvertUTF16toUTF8(wideStreamId).get();
2308 : }
2309 :
2310 : void
2311 0 : PeerConnectionImpl::OnMediaError(const std::string& aError)
2312 : {
2313 0 : CSFLogError(logTag, "Encountered media error! %s", aError.c_str());
2314 : // TODO: Let content know about this somehow.
2315 0 : }
2316 :
2317 : nsresult
2318 0 : PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack,
2319 : const Sequence<OwningNonNull<DOMMediaStream>>& aStreams)
2320 : {
2321 0 : PC_AUTO_ENTER_API_CALL(true);
2322 :
2323 0 : if (!aStreams.Length()) {
2324 0 : CSFLogError(logTag, "%s: At least one stream arg required", __FUNCTION__);
2325 0 : return NS_ERROR_FAILURE;
2326 : }
2327 :
2328 0 : return AddTrack(aTrack, aStreams[0]);
2329 : }
2330 :
2331 : nsresult
2332 0 : PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack,
2333 : DOMMediaStream& aMediaStream)
2334 : {
2335 0 : std::string streamId = PeerConnectionImpl::GetStreamId(aMediaStream);
2336 0 : std::string trackId = PeerConnectionImpl::GetTrackId(aTrack);
2337 0 : nsresult res = mMedia->AddTrack(aMediaStream, streamId, aTrack, trackId);
2338 0 : if (NS_FAILED(res)) {
2339 0 : return res;
2340 : }
2341 :
2342 0 : CSFLogDebug(logTag, "Added track (%s) to stream %s",
2343 0 : trackId.c_str(), streamId.c_str());
2344 :
2345 0 : aTrack.AddPrincipalChangeObserver(this);
2346 0 : PrincipalChanged(&aTrack);
2347 :
2348 0 : if (aTrack.AsAudioStreamTrack()) {
2349 0 : res = AddTrackToJsepSession(SdpMediaSection::kAudio, streamId, trackId);
2350 0 : if (NS_FAILED(res)) {
2351 0 : return res;
2352 : }
2353 : }
2354 :
2355 0 : if (aTrack.AsVideoStreamTrack()) {
2356 0 : if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) {
2357 : // Before this code was moved, this would silently ignore just like it
2358 : // does now. Is this actually what we want to do?
2359 0 : return NS_OK;
2360 : }
2361 :
2362 0 : res = AddTrackToJsepSession(SdpMediaSection::kVideo, streamId, trackId);
2363 0 : if (NS_FAILED(res)) {
2364 0 : return res;
2365 : }
2366 : }
2367 0 : OnNegotiationNeeded();
2368 0 : return NS_OK;
2369 : }
2370 :
2371 : RefPtr<MediaPipeline>
2372 0 : PeerConnectionImpl::GetMediaPipelineForTrack(MediaStreamTrack& aRecvTrack)
2373 : {
2374 0 : for (size_t i = 0; i < mMedia->RemoteStreamsLength(); ++i) {
2375 0 : if (mMedia->GetRemoteStreamByIndex(i)->GetMediaStream()->
2376 0 : HasTrack(aRecvTrack)) {
2377 0 : auto& pipelines = mMedia->GetRemoteStreamByIndex(i)->GetPipelines();
2378 0 : std::string trackId = PeerConnectionImpl::GetTrackId(aRecvTrack);
2379 0 : auto it = pipelines.find(trackId);
2380 0 : if (it != pipelines.end()) {
2381 0 : return it->second;
2382 : }
2383 : }
2384 : }
2385 :
2386 0 : return nullptr;
2387 : }
2388 :
2389 : nsresult
2390 0 : PeerConnectionImpl::AddRIDExtension(MediaStreamTrack& aRecvTrack,
2391 : unsigned short aExtensionId)
2392 : {
2393 0 : RefPtr<MediaPipeline> pipeline = GetMediaPipelineForTrack(aRecvTrack);
2394 0 : if (pipeline) {
2395 0 : pipeline->AddRIDExtension_m(aExtensionId);
2396 : }
2397 0 : return NS_OK;
2398 : }
2399 :
2400 : nsresult
2401 0 : PeerConnectionImpl::AddRIDFilter(MediaStreamTrack& aRecvTrack,
2402 : const nsAString& aRid)
2403 : {
2404 0 : RefPtr<MediaPipeline> pipeline = GetMediaPipelineForTrack(aRecvTrack);
2405 0 : if (pipeline) {
2406 0 : pipeline->AddRIDFilter_m(NS_ConvertUTF16toUTF8(aRid).get());
2407 : }
2408 0 : return NS_OK;
2409 : }
2410 :
2411 : NS_IMETHODIMP
2412 0 : PeerConnectionImpl::RemoveTrack(MediaStreamTrack& aTrack) {
2413 0 : PC_AUTO_ENTER_API_CALL(true);
2414 :
2415 0 : std::string trackId = PeerConnectionImpl::GetTrackId(aTrack);
2416 :
2417 0 : nsString wideTrackId;
2418 0 : aTrack.GetId(wideTrackId);
2419 0 : for (size_t i = 0; i < mDTMFStates.Length(); ++i) {
2420 0 : if (mDTMFStates[i].mTrackId == wideTrackId) {
2421 0 : mDTMFStates[i].mSendTimer->Cancel();
2422 0 : mDTMFStates.RemoveElementAt(i);
2423 0 : break;
2424 : }
2425 : }
2426 :
2427 0 : RefPtr<LocalSourceStreamInfo> info = media()->GetLocalStreamByTrackId(trackId);
2428 :
2429 0 : if (!info) {
2430 0 : CSFLogError(logTag, "%s: Unknown stream", __FUNCTION__);
2431 0 : return NS_ERROR_INVALID_ARG;
2432 : }
2433 :
2434 : nsresult rv =
2435 0 : mJsepSession->RemoveTrack(info->GetId(), trackId);
2436 :
2437 0 : if (NS_FAILED(rv)) {
2438 0 : CSFLogError(logTag, "%s: Unknown stream/track ids %s %s",
2439 : __FUNCTION__,
2440 : info->GetId().c_str(),
2441 0 : trackId.c_str());
2442 0 : return rv;
2443 : }
2444 :
2445 0 : media()->RemoveLocalTrack(info->GetId(), trackId);
2446 :
2447 0 : aTrack.RemovePrincipalChangeObserver(this);
2448 :
2449 0 : OnNegotiationNeeded();
2450 :
2451 0 : return NS_OK;
2452 : }
2453 :
2454 0 : static int GetDTMFToneCode(uint16_t c)
2455 : {
2456 0 : const char* DTMF_TONECODES = "0123456789*#ABCD";
2457 :
2458 0 : if (c == ',') {
2459 : // , is a special character indicating a 2 second delay
2460 0 : return -1;
2461 : }
2462 :
2463 0 : const char* i = strchr(DTMF_TONECODES, c);
2464 0 : MOZ_ASSERT(i);
2465 0 : return i - DTMF_TONECODES;
2466 : }
2467 :
2468 : NS_IMETHODIMP
2469 0 : PeerConnectionImpl::InsertDTMF(mozilla::dom::RTCRtpSender& sender,
2470 : const nsAString& tones, uint32_t duration,
2471 : uint32_t interToneGap) {
2472 0 : PC_AUTO_ENTER_API_CALL(false);
2473 :
2474 : // Check values passed in from PeerConnection.js
2475 0 : MOZ_ASSERT(duration >= 40, "duration must be at least 40");
2476 0 : MOZ_ASSERT(duration <= 6000, "duration must be at most 6000");
2477 0 : MOZ_ASSERT(interToneGap >= 30, "interToneGap must be at least 30");
2478 :
2479 0 : JSErrorResult jrv;
2480 :
2481 : // Retrieve track
2482 0 : RefPtr<MediaStreamTrack> mst = sender.GetTrack(jrv);
2483 0 : if (jrv.Failed()) {
2484 0 : NS_WARNING("Failed to retrieve track for RTCRtpSender!");
2485 0 : return jrv.StealNSResult();
2486 : }
2487 :
2488 0 : nsString senderTrackId;
2489 0 : mst->GetId(senderTrackId);
2490 :
2491 : // Attempt to locate state for the DTMFSender
2492 0 : DTMFState* state = nullptr;
2493 0 : for (auto& dtmfState : mDTMFStates) {
2494 0 : if (dtmfState.mTrackId == senderTrackId) {
2495 0 : state = &dtmfState;
2496 0 : break;
2497 : }
2498 : }
2499 :
2500 : // No state yet, create a new one
2501 0 : if (!state) {
2502 0 : state = mDTMFStates.AppendElement();
2503 0 : state->mPeerConnectionImpl = this;
2504 0 : state->mTrackId = senderTrackId;
2505 0 : state->mSendTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
2506 0 : MOZ_ASSERT(state->mSendTimer);
2507 : }
2508 0 : MOZ_ASSERT(state);
2509 :
2510 0 : auto trackPairs = mJsepSession->GetNegotiatedTrackPairs();
2511 0 : state->mLevel = -1;
2512 0 : for (auto& trackPair : trackPairs) {
2513 0 : if (state->mTrackId.EqualsASCII(trackPair.mSending->GetTrackId().c_str())) {
2514 0 : if (trackPair.HasBundleLevel()) {
2515 0 : state->mLevel = trackPair.BundleLevel();
2516 : } else {
2517 0 : state->mLevel = trackPair.mLevel;
2518 : }
2519 0 : break;
2520 : }
2521 : }
2522 :
2523 0 : state->mTones = tones;
2524 0 : state->mDuration = duration;
2525 0 : state->mInterToneGap = interToneGap;
2526 0 : if (!state->mTones.IsEmpty()) {
2527 0 : state->mSendTimer->InitWithNamedFuncCallback(DTMFSendTimerCallback_m, state, 0,
2528 : nsITimer::TYPE_ONE_SHOT,
2529 0 : "DTMFSendTimerCallback_m");
2530 : }
2531 0 : return NS_OK;
2532 : }
2533 :
2534 : NS_IMETHODIMP
2535 0 : PeerConnectionImpl::GetDTMFToneBuffer(mozilla::dom::RTCRtpSender& sender,
2536 : nsAString& outToneBuffer) {
2537 0 : PC_AUTO_ENTER_API_CALL(false);
2538 :
2539 0 : JSErrorResult jrv;
2540 :
2541 : // Retrieve track
2542 0 : RefPtr<MediaStreamTrack> mst = sender.GetTrack(jrv);
2543 0 : if (jrv.Failed()) {
2544 0 : NS_WARNING("Failed to retrieve track for RTCRtpSender!");
2545 0 : return jrv.StealNSResult();
2546 : }
2547 :
2548 0 : nsString senderTrackId;
2549 0 : mst->GetId(senderTrackId);
2550 :
2551 : // Attempt to locate state for the DTMFSender
2552 0 : for (auto& dtmfState : mDTMFStates) {
2553 0 : if (dtmfState.mTrackId == senderTrackId) {
2554 0 : outToneBuffer = dtmfState.mTones;
2555 0 : break;
2556 : }
2557 : }
2558 :
2559 0 : return NS_OK;
2560 : }
2561 :
2562 : NS_IMETHODIMP
2563 0 : PeerConnectionImpl::ReplaceTrack(MediaStreamTrack& aThisTrack,
2564 : MediaStreamTrack& aWithTrack) {
2565 0 : PC_AUTO_ENTER_API_CALL(true);
2566 :
2567 0 : nsString trackId;
2568 0 : aThisTrack.GetId(trackId);
2569 :
2570 0 : for (size_t i = 0; i < mDTMFStates.Length(); ++i) {
2571 0 : if (mDTMFStates[i].mTrackId == trackId) {
2572 0 : mDTMFStates[i].mSendTimer->Cancel();
2573 0 : mDTMFStates.RemoveElementAt(i);
2574 0 : break;
2575 : }
2576 : }
2577 :
2578 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
2579 0 : if (!pco) {
2580 0 : return NS_ERROR_UNEXPECTED;
2581 : }
2582 0 : JSErrorResult jrv;
2583 :
2584 0 : if (&aThisTrack == &aWithTrack) {
2585 0 : pco->OnReplaceTrackSuccess(jrv);
2586 0 : if (jrv.Failed()) {
2587 0 : CSFLogError(logTag, "Error firing replaceTrack success callback");
2588 0 : return NS_ERROR_UNEXPECTED;
2589 : }
2590 0 : return NS_OK;
2591 : }
2592 :
2593 0 : nsString thisKind;
2594 0 : aThisTrack.GetKind(thisKind);
2595 0 : nsString withKind;
2596 0 : aWithTrack.GetKind(withKind);
2597 :
2598 0 : if (thisKind != withKind) {
2599 0 : pco->OnReplaceTrackError(kIncompatibleMediaStreamTrack,
2600 0 : ObString(mJsepSession->GetLastError().c_str()),
2601 0 : jrv);
2602 0 : if (jrv.Failed()) {
2603 0 : CSFLogError(logTag, "Error firing replaceTrack success callback");
2604 0 : return NS_ERROR_UNEXPECTED;
2605 : }
2606 0 : return NS_OK;
2607 : }
2608 0 : std::string origTrackId = PeerConnectionImpl::GetTrackId(aThisTrack);
2609 0 : std::string newTrackId = PeerConnectionImpl::GetTrackId(aWithTrack);
2610 :
2611 : RefPtr<LocalSourceStreamInfo> info =
2612 0 : media()->GetLocalStreamByTrackId(origTrackId);
2613 0 : if (!info) {
2614 0 : CSFLogError(logTag, "Could not find stream from trackId");
2615 0 : return NS_ERROR_UNEXPECTED;
2616 : }
2617 :
2618 0 : std::string origStreamId = info->GetId();
2619 : std::string newStreamId =
2620 0 : PeerConnectionImpl::GetStreamId(*aWithTrack.mOwningStream);
2621 :
2622 0 : nsresult rv = mJsepSession->ReplaceTrack(origStreamId,
2623 : origTrackId,
2624 : newStreamId,
2625 0 : newTrackId);
2626 0 : if (NS_FAILED(rv)) {
2627 0 : pco->OnReplaceTrackError(kInvalidMediastreamTrack,
2628 0 : ObString(mJsepSession->GetLastError().c_str()),
2629 0 : jrv);
2630 0 : if (jrv.Failed()) {
2631 0 : CSFLogError(logTag, "Error firing replaceTrack error callback");
2632 0 : return NS_ERROR_UNEXPECTED;
2633 : }
2634 0 : return NS_OK;
2635 : }
2636 :
2637 0 : rv = media()->ReplaceTrack(origStreamId,
2638 : origTrackId,
2639 : aWithTrack,
2640 : newStreamId,
2641 0 : newTrackId);
2642 :
2643 0 : if (NS_FAILED(rv)) {
2644 0 : CSFLogError(logTag, "Unexpected error in ReplaceTrack: %d",
2645 0 : static_cast<int>(rv));
2646 0 : pco->OnReplaceTrackError(kInvalidMediastreamTrack,
2647 0 : ObString("Failed to replace track"),
2648 0 : jrv);
2649 0 : if (jrv.Failed()) {
2650 0 : CSFLogError(logTag, "Error firing replaceTrack error callback");
2651 0 : return NS_ERROR_UNEXPECTED;
2652 : }
2653 0 : return NS_OK;
2654 : }
2655 0 : aThisTrack.RemovePrincipalChangeObserver(this);
2656 0 : aWithTrack.AddPrincipalChangeObserver(this);
2657 0 : PrincipalChanged(&aWithTrack);
2658 :
2659 : // We update the media pipelines here so we can apply different codec
2660 : // settings for different sources (e.g. screensharing as opposed to camera.)
2661 : // TODO: We should probably only do this if the source has in fact changed.
2662 :
2663 0 : if (NS_FAILED((rv = mMedia->UpdateMediaPipelines(*mJsepSession)))) {
2664 0 : CSFLogError(logTag, "Error Updating MediaPipelines");
2665 0 : return rv;
2666 : }
2667 :
2668 0 : pco->OnReplaceTrackSuccess(jrv);
2669 0 : if (jrv.Failed()) {
2670 0 : CSFLogError(logTag, "Error firing replaceTrack success callback");
2671 0 : return NS_ERROR_UNEXPECTED;
2672 : }
2673 :
2674 0 : return NS_OK;
2675 : }
2676 :
2677 : NS_IMETHODIMP
2678 0 : PeerConnectionImpl::SetParameters(MediaStreamTrack& aTrack,
2679 : const RTCRtpParameters& aParameters) {
2680 0 : PC_AUTO_ENTER_API_CALL(true);
2681 :
2682 0 : std::vector<JsepTrack::JsConstraints> constraints;
2683 0 : if (aParameters.mEncodings.WasPassed()) {
2684 0 : for (auto& encoding : aParameters.mEncodings.Value()) {
2685 0 : JsepTrack::JsConstraints constraint;
2686 0 : if (encoding.mRid.WasPassed()) {
2687 0 : constraint.rid = NS_ConvertUTF16toUTF8(encoding.mRid.Value()).get();
2688 : }
2689 0 : if (encoding.mMaxBitrate.WasPassed()) {
2690 0 : constraint.constraints.maxBr = encoding.mMaxBitrate.Value();
2691 : }
2692 0 : constraint.constraints.scaleDownBy = encoding.mScaleResolutionDownBy;
2693 0 : constraints.push_back(constraint);
2694 : }
2695 : }
2696 0 : return SetParameters(aTrack, constraints);
2697 : }
2698 :
2699 : nsresult
2700 0 : PeerConnectionImpl::SetParameters(
2701 : MediaStreamTrack& aTrack,
2702 : const std::vector<JsepTrack::JsConstraints>& aConstraints)
2703 : {
2704 0 : std::string trackId = PeerConnectionImpl::GetTrackId(aTrack);
2705 0 : RefPtr<LocalSourceStreamInfo> info = media()->GetLocalStreamByTrackId(trackId);
2706 0 : if (!info) {
2707 0 : CSFLogError(logTag, "%s: Unknown stream", __FUNCTION__);
2708 0 : return NS_ERROR_INVALID_ARG;
2709 : }
2710 0 : std::string streamId = info->GetId();
2711 :
2712 0 : return mJsepSession->SetParameters(streamId, trackId, aConstraints);
2713 : }
2714 :
2715 : NS_IMETHODIMP
2716 0 : PeerConnectionImpl::GetParameters(MediaStreamTrack& aTrack,
2717 : RTCRtpParameters& aOutParameters) {
2718 0 : PC_AUTO_ENTER_API_CALL(true);
2719 :
2720 0 : std::vector<JsepTrack::JsConstraints> constraints;
2721 0 : nsresult rv = GetParameters(aTrack, &constraints);
2722 0 : if (NS_FAILED(rv)) {
2723 0 : return rv;
2724 : }
2725 0 : aOutParameters.mEncodings.Construct();
2726 0 : for (auto& constraint : constraints) {
2727 0 : RTCRtpEncodingParameters encoding;
2728 0 : encoding.mRid.Construct(NS_ConvertASCIItoUTF16(constraint.rid.c_str()));
2729 0 : encoding.mMaxBitrate.Construct(constraint.constraints.maxBr);
2730 0 : encoding.mScaleResolutionDownBy = constraint.constraints.scaleDownBy;
2731 0 : aOutParameters.mEncodings.Value().AppendElement(Move(encoding), fallible);
2732 : }
2733 0 : return NS_OK;
2734 : }
2735 :
2736 : nsresult
2737 0 : PeerConnectionImpl::GetParameters(
2738 : MediaStreamTrack& aTrack,
2739 : std::vector<JsepTrack::JsConstraints>* aOutConstraints)
2740 : {
2741 0 : std::string trackId = PeerConnectionImpl::GetTrackId(aTrack);
2742 0 : RefPtr<LocalSourceStreamInfo> info = media()->GetLocalStreamByTrackId(trackId);
2743 0 : if (!info) {
2744 0 : CSFLogError(logTag, "%s: Unknown stream", __FUNCTION__);
2745 0 : return NS_ERROR_INVALID_ARG;
2746 : }
2747 0 : std::string streamId = info->GetId();
2748 :
2749 0 : return mJsepSession->GetParameters(streamId, trackId, aOutConstraints);
2750 : }
2751 :
2752 : nsresult
2753 0 : PeerConnectionImpl::CalculateFingerprint(
2754 : const std::string& algorithm,
2755 : std::vector<uint8_t>* fingerprint) const {
2756 : uint8_t buf[DtlsIdentity::HASH_ALGORITHM_MAX_LENGTH];
2757 0 : size_t len = 0;
2758 :
2759 0 : MOZ_ASSERT(fingerprint);
2760 0 : const UniqueCERTCertificate& cert = mCertificate->Certificate();
2761 0 : nsresult rv = DtlsIdentity::ComputeFingerprint(cert, algorithm,
2762 : &buf[0], sizeof(buf),
2763 0 : &len);
2764 0 : if (NS_FAILED(rv)) {
2765 : CSFLogError(logTag, "Unable to calculate certificate fingerprint, rv=%u",
2766 0 : static_cast<unsigned>(rv));
2767 0 : return rv;
2768 : }
2769 0 : MOZ_ASSERT(len > 0 && len <= DtlsIdentity::HASH_ALGORITHM_MAX_LENGTH);
2770 0 : fingerprint->assign(buf, buf + len);
2771 0 : return NS_OK;
2772 : }
2773 :
2774 : NS_IMETHODIMP
2775 0 : PeerConnectionImpl::GetFingerprint(char** fingerprint)
2776 : {
2777 0 : MOZ_ASSERT(fingerprint);
2778 0 : MOZ_ASSERT(mCertificate);
2779 0 : std::vector<uint8_t> fp;
2780 0 : nsresult rv = CalculateFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM, &fp);
2781 0 : NS_ENSURE_SUCCESS(rv, rv);
2782 0 : std::ostringstream os;
2783 : os << DtlsIdentity::DEFAULT_HASH_ALGORITHM << ' '
2784 0 : << SdpFingerprintAttributeList::FormatFingerprint(fp);
2785 0 : std::string fpStr = os.str();
2786 :
2787 0 : char* tmp = new char[fpStr.size() + 1];
2788 0 : std::copy(fpStr.begin(), fpStr.end(), tmp);
2789 0 : tmp[fpStr.size()] = '\0';
2790 :
2791 0 : *fingerprint = tmp;
2792 0 : return NS_OK;
2793 : }
2794 :
2795 : NS_IMETHODIMP
2796 0 : PeerConnectionImpl::GetLocalDescription(nsAString& aSDP)
2797 : {
2798 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
2799 :
2800 0 : std::string localSdp = mJsepSession->GetLocalDescription(
2801 0 : kJsepDescriptionPendingOrCurrent);
2802 0 : aSDP = NS_ConvertASCIItoUTF16(localSdp.c_str());
2803 :
2804 0 : return NS_OK;
2805 : }
2806 :
2807 : NS_IMETHODIMP
2808 0 : PeerConnectionImpl::GetCurrentLocalDescription(nsAString& aSDP)
2809 : {
2810 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
2811 :
2812 0 : std::string localSdp = mJsepSession->GetLocalDescription(kJsepDescriptionCurrent);
2813 0 : aSDP = NS_ConvertASCIItoUTF16(localSdp.c_str());
2814 :
2815 0 : return NS_OK;
2816 : }
2817 :
2818 : NS_IMETHODIMP
2819 0 : PeerConnectionImpl::GetPendingLocalDescription(nsAString& aSDP)
2820 : {
2821 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
2822 :
2823 0 : std::string localSdp = mJsepSession->GetLocalDescription(kJsepDescriptionPending);
2824 0 : aSDP = NS_ConvertASCIItoUTF16(localSdp.c_str());
2825 :
2826 0 : return NS_OK;
2827 : }
2828 :
2829 : NS_IMETHODIMP
2830 0 : PeerConnectionImpl::GetRemoteDescription(nsAString& aSDP)
2831 : {
2832 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
2833 :
2834 0 : std::string remoteSdp = mJsepSession->GetRemoteDescription(
2835 0 : kJsepDescriptionPendingOrCurrent);
2836 0 : aSDP = NS_ConvertASCIItoUTF16(remoteSdp.c_str());
2837 :
2838 0 : return NS_OK;
2839 : }
2840 :
2841 : NS_IMETHODIMP
2842 0 : PeerConnectionImpl::GetCurrentRemoteDescription(nsAString& aSDP)
2843 : {
2844 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
2845 :
2846 0 : std::string remoteSdp = mJsepSession->GetRemoteDescription(kJsepDescriptionCurrent);
2847 0 : aSDP = NS_ConvertASCIItoUTF16(remoteSdp.c_str());
2848 :
2849 0 : return NS_OK;
2850 : }
2851 :
2852 : NS_IMETHODIMP
2853 0 : PeerConnectionImpl::GetPendingRemoteDescription(nsAString& aSDP)
2854 : {
2855 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
2856 :
2857 0 : std::string remoteSdp = mJsepSession->GetRemoteDescription(kJsepDescriptionPending);
2858 0 : aSDP = NS_ConvertASCIItoUTF16(remoteSdp.c_str());
2859 :
2860 0 : return NS_OK;
2861 : }
2862 :
2863 : NS_IMETHODIMP
2864 0 : PeerConnectionImpl::SignalingState(PCImplSignalingState* aState)
2865 : {
2866 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
2867 0 : MOZ_ASSERT(aState);
2868 :
2869 0 : *aState = mSignalingState;
2870 0 : return NS_OK;
2871 : }
2872 :
2873 : NS_IMETHODIMP
2874 0 : PeerConnectionImpl::IceConnectionState(PCImplIceConnectionState* aState)
2875 : {
2876 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
2877 0 : MOZ_ASSERT(aState);
2878 :
2879 0 : *aState = mIceConnectionState;
2880 0 : return NS_OK;
2881 : }
2882 :
2883 : NS_IMETHODIMP
2884 0 : PeerConnectionImpl::IceGatheringState(PCImplIceGatheringState* aState)
2885 : {
2886 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
2887 0 : MOZ_ASSERT(aState);
2888 :
2889 0 : *aState = mIceGatheringState;
2890 0 : return NS_OK;
2891 : }
2892 :
2893 : nsresult
2894 0 : PeerConnectionImpl::CheckApiState(bool assert_ice_ready) const
2895 : {
2896 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
2897 0 : MOZ_ASSERT(mTrickle || !assert_ice_ready ||
2898 : (mIceGatheringState == PCImplIceGatheringState::Complete));
2899 :
2900 0 : if (IsClosed()) {
2901 0 : CSFLogError(logTag, "%s: called API while closed", __FUNCTION__);
2902 0 : return NS_ERROR_FAILURE;
2903 : }
2904 0 : if (!mMedia) {
2905 0 : CSFLogError(logTag, "%s: called API with disposed mMedia", __FUNCTION__);
2906 0 : return NS_ERROR_FAILURE;
2907 : }
2908 0 : return NS_OK;
2909 : }
2910 :
2911 : NS_IMETHODIMP
2912 0 : PeerConnectionImpl::Close()
2913 : {
2914 0 : CSFLogDebug(logTag, "%s: for %s", __FUNCTION__, mHandle.c_str());
2915 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
2916 :
2917 0 : SetSignalingState_m(PCImplSignalingState::SignalingClosed);
2918 :
2919 0 : return NS_OK;
2920 : }
2921 :
2922 : bool
2923 0 : PeerConnectionImpl::PluginCrash(uint32_t aPluginID,
2924 : const nsAString& aPluginName)
2925 : {
2926 : // fire an event to the DOM window if this is "ours"
2927 0 : bool result = mMedia ? mMedia->AnyCodecHasPluginID(aPluginID) : false;
2928 0 : if (!result) {
2929 0 : return false;
2930 : }
2931 :
2932 0 : CSFLogError(logTag, "%s: Our plugin %llu crashed", __FUNCTION__, static_cast<unsigned long long>(aPluginID));
2933 :
2934 0 : nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
2935 0 : if (!doc) {
2936 0 : NS_WARNING("Couldn't get document for PluginCrashed event!");
2937 0 : return true;
2938 : }
2939 :
2940 0 : PluginCrashedEventInit init;
2941 0 : init.mPluginID = aPluginID;
2942 0 : init.mPluginName = aPluginName;
2943 0 : init.mSubmittedCrashReport = false;
2944 0 : init.mGmpPlugin = true;
2945 0 : init.mBubbles = true;
2946 0 : init.mCancelable = true;
2947 :
2948 : RefPtr<PluginCrashedEvent> event =
2949 0 : PluginCrashedEvent::Constructor(doc, NS_LITERAL_STRING("PluginCrashed"), init);
2950 :
2951 0 : event->SetTrusted(true);
2952 0 : event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
2953 :
2954 0 : EventDispatcher::DispatchDOMEvent(mWindow, nullptr, event, nullptr, nullptr);
2955 :
2956 0 : return true;
2957 : }
2958 :
2959 : void
2960 0 : PeerConnectionImpl::RecordEndOfCallTelemetry() const
2961 : {
2962 0 : if (!mJsepSession) {
2963 0 : return;
2964 : }
2965 :
2966 : // Exit early if no connection information was ever exchanged,
2967 : // This prevents distortion of telemetry data.
2968 0 : if (mLocalRequestedSDP.empty() && mRemoteRequestedSDP.empty()) {
2969 0 : return;
2970 : }
2971 :
2972 : // Bitmask used for WEBRTC/LOOP_CALL_TYPE telemetry reporting
2973 : static const uint32_t kAudioTypeMask = 1;
2974 : static const uint32_t kVideoTypeMask = 2;
2975 : static const uint32_t kDataChannelTypeMask = 4;
2976 :
2977 : // Report end-of-call Telemetry
2978 0 : if (mJsepSession->GetNegotiations() > 0) {
2979 0 : Telemetry::Accumulate(Telemetry::WEBRTC_RENEGOTIATIONS,
2980 0 : mJsepSession->GetNegotiations()-1);
2981 : }
2982 0 : Telemetry::Accumulate(Telemetry::WEBRTC_MAX_VIDEO_SEND_TRACK,
2983 0 : mMaxSending[SdpMediaSection::MediaType::kVideo]);
2984 0 : Telemetry::Accumulate(Telemetry::WEBRTC_MAX_VIDEO_RECEIVE_TRACK,
2985 0 : mMaxReceiving[SdpMediaSection::MediaType::kVideo]);
2986 0 : Telemetry::Accumulate(Telemetry::WEBRTC_MAX_AUDIO_SEND_TRACK,
2987 0 : mMaxSending[SdpMediaSection::MediaType::kAudio]);
2988 0 : Telemetry::Accumulate(Telemetry::WEBRTC_MAX_AUDIO_RECEIVE_TRACK,
2989 0 : mMaxReceiving[SdpMediaSection::MediaType::kAudio]);
2990 : // DataChannels appear in both Sending and Receiving
2991 0 : Telemetry::Accumulate(Telemetry::WEBRTC_DATACHANNEL_NEGOTIATED,
2992 0 : mMaxSending[SdpMediaSection::MediaType::kApplication]);
2993 : // Enumerated/bitmask: 1 = Audio, 2 = Video, 4 = DataChannel
2994 : // A/V = 3, A/V/D = 7, etc
2995 0 : uint32_t type = 0;
2996 0 : if (mMaxSending[SdpMediaSection::MediaType::kAudio] ||
2997 0 : mMaxReceiving[SdpMediaSection::MediaType::kAudio]) {
2998 0 : type = kAudioTypeMask;
2999 : }
3000 0 : if (mMaxSending[SdpMediaSection::MediaType::kVideo] ||
3001 0 : mMaxReceiving[SdpMediaSection::MediaType::kVideo]) {
3002 0 : type |= kVideoTypeMask;
3003 : }
3004 0 : if (mMaxSending[SdpMediaSection::MediaType::kApplication]) {
3005 0 : type |= kDataChannelTypeMask;
3006 : }
3007 : Telemetry::Accumulate(Telemetry::WEBRTC_CALL_TYPE,
3008 0 : type);
3009 : }
3010 :
3011 : nsresult
3012 0 : PeerConnectionImpl::CloseInt()
3013 : {
3014 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
3015 :
3016 0 : for (auto& dtmfState : mDTMFStates) {
3017 0 : dtmfState.mSendTimer->Cancel();
3018 : }
3019 :
3020 : // We do this at the end of the call because we want to make sure we've waited
3021 : // for all trickle ICE candidates to come in; this can happen well after we've
3022 : // transitioned to connected. As a bonus, this allows us to detect race
3023 : // conditions where a stats dispatch happens right as the PC closes.
3024 0 : if (!mPrivateWindow) {
3025 0 : RecordLongtermICEStatistics();
3026 : }
3027 0 : RecordEndOfCallTelemetry();
3028 0 : CSFLogInfo(logTag, "%s: Closing PeerConnectionImpl %s; "
3029 0 : "ending call", __FUNCTION__, mHandle.c_str());
3030 0 : if (mJsepSession) {
3031 0 : mJsepSession->Close();
3032 : }
3033 0 : if (mDataConnection) {
3034 0 : CSFLogInfo(logTag, "%s: Destroying DataChannelConnection %p for %s",
3035 0 : __FUNCTION__, (void *) mDataConnection.get(), mHandle.c_str());
3036 0 : mDataConnection->Destroy();
3037 0 : mDataConnection = nullptr; // it may not go away until the runnables are dead
3038 : }
3039 0 : ShutdownMedia();
3040 :
3041 : // DataConnection will need to stay alive until all threads/runnables exit
3042 :
3043 0 : return NS_OK;
3044 : }
3045 :
3046 : void
3047 0 : PeerConnectionImpl::ShutdownMedia()
3048 : {
3049 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
3050 :
3051 0 : if (!mMedia)
3052 0 : return;
3053 :
3054 : // before we destroy references to local tracks, detach from them
3055 0 : for(uint32_t i = 0; i < media()->LocalStreamsLength(); ++i) {
3056 0 : LocalSourceStreamInfo *info = media()->GetLocalStreamByIndex(i);
3057 0 : for (const auto& pair : info->GetMediaStreamTracks()) {
3058 0 : pair.second->RemovePrincipalChangeObserver(this);
3059 : }
3060 : }
3061 :
3062 : // End of call to be recorded in Telemetry
3063 0 : if (!mStartTime.IsNull()){
3064 0 : TimeDuration timeDelta = TimeStamp::Now() - mStartTime;
3065 0 : Telemetry::Accumulate(Telemetry::WEBRTC_CALL_DURATION,
3066 0 : timeDelta.ToSeconds());
3067 : }
3068 :
3069 : // Forget the reference so that we can transfer it to
3070 : // SelfDestruct().
3071 0 : mMedia.forget().take()->SelfDestruct();
3072 : }
3073 :
3074 : void
3075 0 : PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState,
3076 : bool rollback)
3077 : {
3078 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
3079 0 : if (mSignalingState == aSignalingState ||
3080 0 : mSignalingState == PCImplSignalingState::SignalingClosed) {
3081 0 : return;
3082 : }
3083 :
3084 0 : if (aSignalingState == PCImplSignalingState::SignalingHaveLocalOffer ||
3085 0 : (aSignalingState == PCImplSignalingState::SignalingStable &&
3086 0 : mSignalingState == PCImplSignalingState::SignalingHaveRemoteOffer &&
3087 0 : !rollback)) {
3088 0 : mMedia->EnsureTransports(*mJsepSession);
3089 : }
3090 :
3091 0 : mSignalingState = aSignalingState;
3092 :
3093 0 : bool fireNegotiationNeeded = false;
3094 0 : if (mSignalingState == PCImplSignalingState::SignalingStable) {
3095 0 : if (mMedia->GetIceRestartState() ==
3096 : PeerConnectionMedia::ICE_RESTART_PROVISIONAL) {
3097 0 : if (rollback) {
3098 0 : RollbackIceRestart();
3099 : } else {
3100 0 : mMedia->CommitIceRestart();
3101 : }
3102 : }
3103 :
3104 : // Either negotiation is done, or we've rolled back. In either case, we
3105 : // need to re-evaluate whether further negotiation is required.
3106 0 : mNegotiationNeeded = false;
3107 : // If we're rolling back a local offer, we might need to remove some
3108 : // transports, but nothing further needs to be done.
3109 0 : mMedia->ActivateOrRemoveTransports(*mJsepSession, mForceIceTcp);
3110 0 : if (!rollback) {
3111 0 : if (NS_FAILED(mMedia->UpdateMediaPipelines(*mJsepSession))) {
3112 0 : CSFLogError(logTag, "Error Updating MediaPipelines");
3113 0 : NS_ASSERTION(false, "Error Updating MediaPipelines in SetSignalingState_m()");
3114 : // XXX what now? Not much we can do but keep going, without major restructuring
3115 : }
3116 0 : InitializeDataChannel();
3117 0 : mMedia->StartIceChecks(*mJsepSession);
3118 : }
3119 :
3120 0 : if (!mJsepSession->AllLocalTracksAreAssigned()) {
3121 : CSFLogInfo(logTag, "Not all local tracks were assigned to an "
3122 : "m-section, either because the offerer did not offer"
3123 : " to receive enough tracks, or because tracks were "
3124 : "added after CreateOffer/Answer, but before "
3125 : "offer/answer completed. This requires "
3126 0 : "renegotiation.");
3127 0 : fireNegotiationNeeded = true;
3128 : }
3129 :
3130 : // Telemetry: record info on the current state of streams/renegotiations/etc
3131 : // Note: this code gets run on rollbacks as well!
3132 :
3133 : // Update the max channels used with each direction for each type
3134 : uint16_t receiving[SdpMediaSection::kMediaTypes];
3135 : uint16_t sending[SdpMediaSection::kMediaTypes];
3136 0 : mJsepSession->CountTracks(receiving, sending);
3137 0 : for (size_t i = 0; i < SdpMediaSection::kMediaTypes; i++) {
3138 0 : if (mMaxReceiving[i] < receiving[i]) {
3139 0 : mMaxReceiving[i] = receiving[i];
3140 : }
3141 0 : if (mMaxSending[i] < sending[i]) {
3142 0 : mMaxSending[i] = sending[i];
3143 : }
3144 : }
3145 : }
3146 :
3147 0 : if (mSignalingState == PCImplSignalingState::SignalingClosed) {
3148 0 : CloseInt();
3149 : }
3150 :
3151 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
3152 0 : if (!pco) {
3153 0 : return;
3154 : }
3155 0 : JSErrorResult rv;
3156 0 : pco->OnStateChange(PCObserverStateType::SignalingState, rv);
3157 :
3158 0 : if (fireNegotiationNeeded) {
3159 : // We don't use MaybeFireNegotiationNeeded here, since content might have
3160 : // already cased a transition from stable.
3161 0 : OnNegotiationNeeded();
3162 : }
3163 : }
3164 :
3165 : void
3166 0 : PeerConnectionImpl::UpdateSignalingState(bool rollback) {
3167 : mozilla::JsepSignalingState state =
3168 0 : mJsepSession->GetState();
3169 :
3170 : PCImplSignalingState newState;
3171 :
3172 0 : switch(state) {
3173 : case kJsepStateStable:
3174 0 : newState = PCImplSignalingState::SignalingStable;
3175 0 : break;
3176 : case kJsepStateHaveLocalOffer:
3177 0 : newState = PCImplSignalingState::SignalingHaveLocalOffer;
3178 0 : break;
3179 : case kJsepStateHaveRemoteOffer:
3180 0 : newState = PCImplSignalingState::SignalingHaveRemoteOffer;
3181 0 : break;
3182 : case kJsepStateHaveLocalPranswer:
3183 0 : newState = PCImplSignalingState::SignalingHaveLocalPranswer;
3184 0 : break;
3185 : case kJsepStateHaveRemotePranswer:
3186 0 : newState = PCImplSignalingState::SignalingHaveRemotePranswer;
3187 0 : break;
3188 : case kJsepStateClosed:
3189 0 : newState = PCImplSignalingState::SignalingClosed;
3190 0 : break;
3191 : default:
3192 0 : MOZ_CRASH();
3193 : }
3194 :
3195 0 : SetSignalingState_m(newState, rollback);
3196 0 : }
3197 :
3198 : bool
3199 0 : PeerConnectionImpl::IsClosed() const
3200 : {
3201 0 : return mSignalingState == PCImplSignalingState::SignalingClosed;
3202 : }
3203 :
3204 : bool
3205 0 : PeerConnectionImpl::HasMedia() const
3206 : {
3207 0 : return mMedia;
3208 : }
3209 :
3210 0 : PeerConnectionWrapper::PeerConnectionWrapper(const std::string& handle)
3211 0 : : impl_(nullptr) {
3212 0 : if (PeerConnectionCtx::GetInstance()->mPeerConnections.find(handle) ==
3213 0 : PeerConnectionCtx::GetInstance()->mPeerConnections.end()) {
3214 0 : return;
3215 : }
3216 :
3217 0 : PeerConnectionImpl *impl = PeerConnectionCtx::GetInstance()->mPeerConnections[handle];
3218 :
3219 0 : if (!impl->media())
3220 0 : return;
3221 :
3222 0 : impl_ = impl;
3223 : }
3224 :
3225 : const std::string&
3226 0 : PeerConnectionImpl::GetHandle()
3227 : {
3228 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
3229 0 : return mHandle;
3230 : }
3231 :
3232 : const std::string&
3233 0 : PeerConnectionImpl::GetName()
3234 : {
3235 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
3236 0 : return mName;
3237 : }
3238 :
3239 : static mozilla::dom::PCImplIceConnectionState
3240 0 : toDomIceConnectionState(NrIceCtx::ConnectionState state) {
3241 0 : switch (state) {
3242 : case NrIceCtx::ICE_CTX_INIT:
3243 0 : return PCImplIceConnectionState::New;
3244 : case NrIceCtx::ICE_CTX_CHECKING:
3245 0 : return PCImplIceConnectionState::Checking;
3246 : case NrIceCtx::ICE_CTX_CONNECTED:
3247 0 : return PCImplIceConnectionState::Connected;
3248 : case NrIceCtx::ICE_CTX_COMPLETED:
3249 0 : return PCImplIceConnectionState::Completed;
3250 : case NrIceCtx::ICE_CTX_FAILED:
3251 0 : return PCImplIceConnectionState::Failed;
3252 : case NrIceCtx::ICE_CTX_DISCONNECTED:
3253 0 : return PCImplIceConnectionState::Disconnected;
3254 : case NrIceCtx::ICE_CTX_CLOSED:
3255 0 : return PCImplIceConnectionState::Closed;
3256 : }
3257 0 : MOZ_CRASH();
3258 : }
3259 :
3260 : static mozilla::dom::PCImplIceGatheringState
3261 0 : toDomIceGatheringState(NrIceCtx::GatheringState state) {
3262 0 : switch (state) {
3263 : case NrIceCtx::ICE_CTX_GATHER_INIT:
3264 0 : return PCImplIceGatheringState::New;
3265 : case NrIceCtx::ICE_CTX_GATHER_STARTED:
3266 0 : return PCImplIceGatheringState::Gathering;
3267 : case NrIceCtx::ICE_CTX_GATHER_COMPLETE:
3268 0 : return PCImplIceGatheringState::Complete;
3269 : }
3270 0 : MOZ_CRASH();
3271 : }
3272 :
3273 : void
3274 0 : PeerConnectionImpl::CandidateReady(const std::string& candidate,
3275 : uint16_t level) {
3276 0 : PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);
3277 :
3278 0 : if (mForceIceTcp && std::string::npos != candidate.find(" UDP ")) {
3279 0 : CSFLogError(logTag, "Blocking local UDP candidate: %s", candidate.c_str());
3280 0 : return;
3281 : }
3282 :
3283 0 : std::string mid;
3284 0 : bool skipped = false;
3285 0 : nsresult res = mJsepSession->AddLocalIceCandidate(candidate,
3286 : level,
3287 : &mid,
3288 0 : &skipped);
3289 :
3290 0 : if (NS_FAILED(res)) {
3291 0 : std::string errorString = mJsepSession->GetLastError();
3292 :
3293 0 : CSFLogError(logTag, "Failed to incorporate local candidate into SDP:"
3294 : " res = %u, candidate = %s, level = %u, error = %s",
3295 : static_cast<unsigned>(res),
3296 : candidate.c_str(),
3297 : static_cast<unsigned>(level),
3298 0 : errorString.c_str());
3299 0 : return;
3300 : }
3301 :
3302 0 : if (skipped) {
3303 0 : CSFLogDebug(logTag, "Skipped adding local candidate %s (level %u) to SDP, "
3304 : "this typically happens because the m-section is "
3305 : "bundled, which means it doesn't make sense for it to "
3306 : "have its own transport-related attributes.",
3307 : candidate.c_str(),
3308 0 : static_cast<unsigned>(level));
3309 0 : return;
3310 : }
3311 :
3312 0 : CSFLogDebug(logTag, "Passing local candidate to content: %s",
3313 0 : candidate.c_str());
3314 0 : SendLocalIceCandidateToContent(level, mid, candidate);
3315 : }
3316 :
3317 : static void
3318 0 : SendLocalIceCandidateToContentImpl(nsWeakPtr weakPCObserver,
3319 : uint16_t level,
3320 : const std::string& mid,
3321 : const std::string& candidate) {
3322 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(weakPCObserver);
3323 0 : if (!pco) {
3324 0 : return;
3325 : }
3326 :
3327 0 : JSErrorResult rv;
3328 0 : pco->OnIceCandidate(level,
3329 0 : ObString(mid.c_str()),
3330 0 : ObString(candidate.c_str()),
3331 0 : rv);
3332 : }
3333 :
3334 : void
3335 0 : PeerConnectionImpl::SendLocalIceCandidateToContent(
3336 : uint16_t level,
3337 : const std::string& mid,
3338 : const std::string& candidate) {
3339 : // We dispatch this because OnSetLocalDescriptionSuccess does a setTimeout(0)
3340 : // to unwind the stack, but the event handlers don't. We need to ensure that
3341 : // the candidates do not skip ahead of the callback.
3342 : NS_DispatchToMainThread(
3343 0 : WrapRunnableNM(&SendLocalIceCandidateToContentImpl,
3344 : mPCObserver,
3345 : level,
3346 : mid,
3347 : candidate),
3348 0 : NS_DISPATCH_NORMAL);
3349 0 : }
3350 :
3351 0 : static bool isDone(PCImplIceConnectionState state) {
3352 0 : return state != PCImplIceConnectionState::Checking &&
3353 0 : state != PCImplIceConnectionState::New;
3354 : }
3355 :
3356 0 : static bool isSucceeded(PCImplIceConnectionState state) {
3357 0 : return state == PCImplIceConnectionState::Connected ||
3358 0 : state == PCImplIceConnectionState::Completed;
3359 : }
3360 :
3361 0 : static bool isFailed(PCImplIceConnectionState state) {
3362 0 : return state == PCImplIceConnectionState::Failed;
3363 : }
3364 :
3365 0 : void PeerConnectionImpl::IceConnectionStateChange(
3366 : NrIceCtx* ctx,
3367 : NrIceCtx::ConnectionState state) {
3368 0 : PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);
3369 :
3370 0 : CSFLogDebug(logTag, "%s", __FUNCTION__);
3371 :
3372 0 : auto domState = toDomIceConnectionState(state);
3373 0 : if (domState == mIceConnectionState) {
3374 : // no work to be done since the states are the same.
3375 : // this can happen during ICE rollback situations.
3376 0 : return;
3377 : }
3378 :
3379 0 : if (!isDone(mIceConnectionState) && isDone(domState)) {
3380 0 : if (isSucceeded(domState)) {
3381 0 : Telemetry::Accumulate(
3382 : Telemetry::WEBRTC_ICE_ADD_CANDIDATE_ERRORS_GIVEN_SUCCESS,
3383 0 : mAddCandidateErrorCount);
3384 0 : } else if (isFailed(domState)) {
3385 0 : Telemetry::Accumulate(
3386 : Telemetry::WEBRTC_ICE_ADD_CANDIDATE_ERRORS_GIVEN_FAILURE,
3387 0 : mAddCandidateErrorCount);
3388 : }
3389 : }
3390 :
3391 0 : mIceConnectionState = domState;
3392 :
3393 0 : if (mIceConnectionState == PCImplIceConnectionState::Connected ||
3394 0 : mIceConnectionState == PCImplIceConnectionState::Completed ||
3395 0 : mIceConnectionState == PCImplIceConnectionState::Failed) {
3396 0 : if (mMedia->IsIceRestarting()) {
3397 0 : FinalizeIceRestart();
3398 : }
3399 : }
3400 :
3401 : // Would be nice if we had a means of converting one of these dom enums
3402 : // to a string that wasn't almost as much text as this switch statement...
3403 0 : switch (mIceConnectionState) {
3404 : case PCImplIceConnectionState::New:
3405 0 : STAMP_TIMECARD(mTimeCard, "Ice state: new");
3406 0 : break;
3407 : case PCImplIceConnectionState::Checking:
3408 : // For telemetry
3409 0 : mIceStartTime = TimeStamp::Now();
3410 0 : STAMP_TIMECARD(mTimeCard, "Ice state: checking");
3411 0 : break;
3412 : case PCImplIceConnectionState::Connected:
3413 0 : STAMP_TIMECARD(mTimeCard, "Ice state: connected");
3414 0 : break;
3415 : case PCImplIceConnectionState::Completed:
3416 0 : STAMP_TIMECARD(mTimeCard, "Ice state: completed");
3417 0 : break;
3418 : case PCImplIceConnectionState::Failed:
3419 0 : STAMP_TIMECARD(mTimeCard, "Ice state: failed");
3420 0 : break;
3421 : case PCImplIceConnectionState::Disconnected:
3422 0 : STAMP_TIMECARD(mTimeCard, "Ice state: disconnected");
3423 0 : break;
3424 : case PCImplIceConnectionState::Closed:
3425 0 : STAMP_TIMECARD(mTimeCard, "Ice state: closed");
3426 0 : break;
3427 : default:
3428 0 : MOZ_ASSERT_UNREACHABLE("Unexpected mIceConnectionState!");
3429 : }
3430 :
3431 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
3432 0 : if (!pco) {
3433 0 : return;
3434 : }
3435 0 : WrappableJSErrorResult rv;
3436 0 : RUN_ON_THREAD(mThread,
3437 0 : WrapRunnable(pco,
3438 : &PeerConnectionObserver::OnStateChange,
3439 : PCObserverStateType::IceConnectionState,
3440 : rv, static_cast<JSCompartment*>(nullptr)),
3441 0 : NS_DISPATCH_NORMAL);
3442 : }
3443 :
3444 : void
3445 0 : PeerConnectionImpl::IceGatheringStateChange(
3446 : NrIceCtx* ctx,
3447 : NrIceCtx::GatheringState state)
3448 : {
3449 0 : PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);
3450 :
3451 0 : CSFLogDebug(logTag, "%s", __FUNCTION__);
3452 :
3453 0 : mIceGatheringState = toDomIceGatheringState(state);
3454 :
3455 : // Would be nice if we had a means of converting one of these dom enums
3456 : // to a string that wasn't almost as much text as this switch statement...
3457 0 : switch (mIceGatheringState) {
3458 : case PCImplIceGatheringState::New:
3459 0 : STAMP_TIMECARD(mTimeCard, "Ice gathering state: new");
3460 0 : break;
3461 : case PCImplIceGatheringState::Gathering:
3462 0 : STAMP_TIMECARD(mTimeCard, "Ice gathering state: gathering");
3463 0 : break;
3464 : case PCImplIceGatheringState::Complete:
3465 0 : STAMP_TIMECARD(mTimeCard, "Ice gathering state: complete");
3466 0 : break;
3467 : default:
3468 0 : MOZ_ASSERT_UNREACHABLE("Unexpected mIceGatheringState!");
3469 : }
3470 :
3471 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
3472 0 : if (!pco) {
3473 0 : return;
3474 : }
3475 0 : WrappableJSErrorResult rv;
3476 0 : RUN_ON_THREAD(mThread,
3477 0 : WrapRunnable(pco,
3478 : &PeerConnectionObserver::OnStateChange,
3479 : PCObserverStateType::IceGatheringState,
3480 : rv, static_cast<JSCompartment*>(nullptr)),
3481 0 : NS_DISPATCH_NORMAL);
3482 :
3483 0 : if (mIceGatheringState == PCImplIceGatheringState::Complete) {
3484 0 : SendLocalIceCandidateToContent(0, "", "");
3485 : }
3486 : }
3487 :
3488 : void
3489 0 : PeerConnectionImpl::UpdateDefaultCandidate(const std::string& defaultAddr,
3490 : uint16_t defaultPort,
3491 : const std::string& defaultRtcpAddr,
3492 : uint16_t defaultRtcpPort,
3493 : uint16_t level) {
3494 0 : CSFLogDebug(logTag, "%s", __FUNCTION__);
3495 0 : mJsepSession->UpdateDefaultCandidate(defaultAddr,
3496 : defaultPort,
3497 : defaultRtcpAddr,
3498 : defaultRtcpPort,
3499 0 : level);
3500 0 : }
3501 :
3502 : void
3503 0 : PeerConnectionImpl::EndOfLocalCandidates(uint16_t level) {
3504 0 : CSFLogDebug(logTag, "%s", __FUNCTION__);
3505 0 : mJsepSession->EndOfLocalCandidates(level);
3506 0 : }
3507 :
3508 : nsresult
3509 0 : PeerConnectionImpl::BuildStatsQuery_m(
3510 : mozilla::dom::MediaStreamTrack *aSelector,
3511 : RTCStatsQuery *query) {
3512 :
3513 0 : if (!HasMedia()) {
3514 0 : return NS_ERROR_UNEXPECTED;
3515 : }
3516 :
3517 0 : if (!mThread) {
3518 0 : CSFLogError(logTag, "Could not build stats query, no MainThread");
3519 0 : return NS_ERROR_UNEXPECTED;
3520 : }
3521 :
3522 0 : nsresult rv = GetTimeSinceEpoch(&(query->now));
3523 0 : if (NS_FAILED(rv)) {
3524 0 : CSFLogError(logTag, "Could not build stats query, could not get timestamp");
3525 0 : return rv;
3526 : }
3527 :
3528 : // Note: mMedia->ice_ctx() is deleted on STS thread; so make sure we grab and hold
3529 : // a ref instead of making multiple calls. NrIceCtx uses threadsafe refcounting.
3530 : // NOTE: Do this after all other failure tests, to ensure we don't
3531 : // accidentally release the Ctx on Mainthread.
3532 0 : query->iceCtx = mMedia->ice_ctx();
3533 0 : if (!query->iceCtx) {
3534 0 : CSFLogError(logTag, "Could not build stats query, no ice_ctx");
3535 0 : return NS_ERROR_UNEXPECTED;
3536 : }
3537 :
3538 : // We do not use the pcHandle here, since that's risky to expose to content.
3539 : query->report = new RTCStatsReportInternalConstruct(
3540 0 : NS_ConvertASCIItoUTF16(mName.c_str()),
3541 0 : query->now);
3542 :
3543 0 : query->iceStartTime = mIceStartTime;
3544 0 : query->failed = isFailed(mIceConnectionState);
3545 :
3546 : // Populate SDP on main
3547 0 : if (query->internalStats) {
3548 0 : if (mJsepSession) {
3549 : // TODO we probably should report Current and Pending SDPs here
3550 : // separately. Plus the raw SDP we got from JS (mLocalRequestedSDP).
3551 : // And if it's the offer or answer would also be nice.
3552 0 : std::string localDescription = mJsepSession->GetLocalDescription(
3553 0 : kJsepDescriptionPendingOrCurrent);
3554 0 : std::string remoteDescription = mJsepSession->GetRemoteDescription(
3555 0 : kJsepDescriptionPendingOrCurrent);
3556 0 : query->report->mLocalSdp.Construct(
3557 0 : NS_ConvertASCIItoUTF16(localDescription.c_str()));
3558 0 : query->report->mRemoteSdp.Construct(
3559 0 : NS_ConvertASCIItoUTF16(remoteDescription.c_str()));
3560 : }
3561 : }
3562 :
3563 : // Gather up pipelines from mMedia so they may be inspected on STS
3564 :
3565 0 : std::string trackId;
3566 0 : if (aSelector) {
3567 0 : trackId = PeerConnectionImpl::GetTrackId(*aSelector);
3568 : }
3569 :
3570 0 : for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) {
3571 0 : for (auto pipeline : mMedia->GetLocalStreamByIndex(i)->GetPipelines()) {
3572 0 : if (!aSelector || pipeline.second->trackid() == trackId) {
3573 0 : query->pipelines.AppendElement(pipeline.second);
3574 : }
3575 : }
3576 : }
3577 0 : for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) {
3578 0 : for (auto pipeline : mMedia->GetRemoteStreamByIndex(i)->GetPipelines()) {
3579 0 : if (!aSelector || pipeline.second->trackid() == trackId) {
3580 0 : query->pipelines.AppendElement(pipeline.second);
3581 : }
3582 : }
3583 : }
3584 :
3585 0 : if (!aSelector) {
3586 0 : query->grabAllLevels = true;
3587 : }
3588 :
3589 0 : return rv;
3590 : }
3591 :
3592 0 : static void ToRTCIceCandidateStats(
3593 : const std::vector<NrIceCandidate>& candidates,
3594 : RTCStatsType candidateType,
3595 : const nsString& componentId,
3596 : DOMHighResTimeStamp now,
3597 : RTCStatsReportInternal* report) {
3598 :
3599 0 : MOZ_ASSERT(report);
3600 0 : for (const auto& candidate : candidates) {
3601 0 : RTCIceCandidateStats cand;
3602 0 : cand.mType.Construct(candidateType);
3603 0 : NS_ConvertASCIItoUTF16 codeword(candidate.codeword.c_str());
3604 0 : cand.mComponentId.Construct(componentId);
3605 0 : cand.mId.Construct(codeword);
3606 0 : cand.mTimestamp.Construct(now);
3607 : cand.mCandidateType.Construct(
3608 0 : RTCStatsIceCandidateType(candidate.type));
3609 : cand.mIpAddress.Construct(
3610 0 : NS_ConvertASCIItoUTF16(candidate.cand_addr.host.c_str()));
3611 0 : cand.mPortNumber.Construct(candidate.cand_addr.port);
3612 : cand.mTransport.Construct(
3613 0 : NS_ConvertASCIItoUTF16(candidate.cand_addr.transport.c_str()));
3614 0 : if (candidateType == RTCStatsType::Local_candidate) {
3615 : cand.mMozLocalTransport.Construct(
3616 0 : NS_ConvertASCIItoUTF16(candidate.local_addr.transport.c_str()));
3617 : }
3618 0 : report->mIceCandidateStats.Value().AppendElement(cand, fallible);
3619 : }
3620 0 : }
3621 :
3622 0 : static void RecordIceStats_s(
3623 : NrIceMediaStream& mediaStream,
3624 : bool internalStats,
3625 : DOMHighResTimeStamp now,
3626 : RTCStatsReportInternal* report) {
3627 :
3628 0 : NS_ConvertASCIItoUTF16 transportId(mediaStream.name().c_str());
3629 :
3630 0 : std::vector<NrIceCandidatePair> candPairs;
3631 0 : nsresult res = mediaStream.GetCandidatePairs(&candPairs);
3632 0 : if (NS_FAILED(res)) {
3633 0 : CSFLogError(logTag, "%s: Error getting candidate pairs", __FUNCTION__);
3634 0 : return;
3635 : }
3636 :
3637 0 : for (auto& candPair : candPairs) {
3638 0 : NS_ConvertASCIItoUTF16 codeword(candPair.codeword.c_str());
3639 0 : NS_ConvertASCIItoUTF16 localCodeword(candPair.local.codeword.c_str());
3640 0 : NS_ConvertASCIItoUTF16 remoteCodeword(candPair.remote.codeword.c_str());
3641 : // Only expose candidate-pair statistics to chrome, until we've thought
3642 : // through the implications of exposing it to content.
3643 :
3644 0 : RTCIceCandidatePairStats s;
3645 0 : s.mId.Construct(codeword);
3646 0 : s.mTransportId.Construct(transportId);
3647 0 : s.mTimestamp.Construct(now);
3648 0 : s.mType.Construct(RTCStatsType::Candidate_pair);
3649 0 : s.mLocalCandidateId.Construct(localCodeword);
3650 0 : s.mRemoteCandidateId.Construct(remoteCodeword);
3651 0 : s.mNominated.Construct(candPair.nominated);
3652 0 : s.mWritable.Construct(candPair.writable);
3653 0 : s.mReadable.Construct(candPair.readable);
3654 0 : s.mPriority.Construct(candPair.priority);
3655 0 : s.mSelected.Construct(candPair.selected);
3656 0 : s.mBytesSent.Construct(candPair.bytes_sent);
3657 0 : s.mBytesReceived.Construct(candPair.bytes_recvd);
3658 0 : s.mLastPacketSentTimestamp.Construct(candPair.ms_since_last_send);
3659 0 : s.mLastPacketReceivedTimestamp.Construct(candPair.ms_since_last_recv);
3660 0 : s.mState.Construct(RTCStatsIceCandidatePairState(candPair.state));
3661 0 : report->mIceCandidatePairStats.Value().AppendElement(s, fallible);
3662 : }
3663 :
3664 0 : std::vector<NrIceCandidate> candidates;
3665 0 : if (NS_SUCCEEDED(mediaStream.GetLocalCandidates(&candidates))) {
3666 : ToRTCIceCandidateStats(candidates,
3667 : RTCStatsType::Local_candidate,
3668 : transportId,
3669 : now,
3670 0 : report);
3671 : }
3672 0 : candidates.clear();
3673 :
3674 0 : if (NS_SUCCEEDED(mediaStream.GetRemoteCandidates(&candidates))) {
3675 : ToRTCIceCandidateStats(candidates,
3676 : RTCStatsType::Remote_candidate,
3677 : transportId,
3678 : now,
3679 0 : report);
3680 : }
3681 : }
3682 :
3683 : nsresult
3684 0 : PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) {
3685 :
3686 0 : ASSERT_ON_THREAD(query->iceCtx->thread());
3687 :
3688 : // Gather stats from pipelines provided (can't touch mMedia + stream on STS)
3689 :
3690 0 : for (size_t p = 0; p < query->pipelines.Length(); ++p) {
3691 0 : const MediaPipeline& mp = *query->pipelines[p];
3692 0 : bool isAudio = (mp.Conduit()->type() == MediaSessionConduit::AUDIO);
3693 : nsString mediaType = isAudio ?
3694 0 : NS_LITERAL_STRING("audio") : NS_LITERAL_STRING("video");
3695 0 : nsString idstr = mediaType;
3696 0 : idstr.AppendLiteral("_");
3697 0 : idstr.AppendInt(mp.level());
3698 :
3699 : // TODO(@@NG):ssrcs handle Conduits having multiple stats at the same level
3700 : // This is pending spec work
3701 : // Gather pipeline stats.
3702 0 : switch (mp.direction()) {
3703 : case MediaPipeline::TRANSMIT: {
3704 0 : nsString localId = NS_LITERAL_STRING("outbound_rtp_") + idstr;
3705 0 : nsString remoteId;
3706 0 : nsString ssrc;
3707 0 : std::vector<unsigned int> ssrcvals = mp.Conduit()->GetLocalSSRCs();
3708 0 : if (!ssrcvals.empty()) {
3709 0 : ssrc.AppendInt(ssrcvals[0]);
3710 : }
3711 : {
3712 : // First, fill in remote stat with rtcp receiver data, if present.
3713 : // ReceiverReports have less information than SenderReports,
3714 : // so fill in what we can.
3715 : DOMHighResTimeStamp timestamp;
3716 : uint32_t jitterMs;
3717 : uint32_t packetsReceived;
3718 : uint64_t bytesReceived;
3719 : uint32_t packetsLost;
3720 : int32_t rtt;
3721 0 : if (mp.Conduit()->GetRTCPReceiverReport(×tamp, &jitterMs,
3722 : &packetsReceived,
3723 : &bytesReceived,
3724 : &packetsLost,
3725 0 : &rtt)) {
3726 0 : remoteId = NS_LITERAL_STRING("outbound_rtcp_") + idstr;
3727 0 : RTCInboundRTPStreamStats s;
3728 0 : s.mTimestamp.Construct(timestamp);
3729 0 : s.mId.Construct(remoteId);
3730 0 : s.mType.Construct(RTCStatsType::Inbound_rtp);
3731 0 : if (ssrc.Length()) {
3732 0 : s.mSsrc.Construct(ssrc);
3733 : }
3734 0 : s.mMediaType.Construct(mediaType);
3735 0 : s.mJitter.Construct(double(jitterMs)/1000);
3736 0 : s.mRemoteId.Construct(localId);
3737 0 : s.mIsRemote = true;
3738 0 : s.mPacketsReceived.Construct(packetsReceived);
3739 0 : s.mBytesReceived.Construct(bytesReceived);
3740 0 : s.mPacketsLost.Construct(packetsLost);
3741 0 : if (rtt > 0) {
3742 0 : s.mRoundTripTime.Construct(rtt);
3743 : }
3744 0 : query->report->mInboundRTPStreamStats.Value().AppendElement(s,
3745 0 : fallible);
3746 : }
3747 : }
3748 : // Then, fill in local side (with cross-link to remote only if present)
3749 : {
3750 0 : RTCOutboundRTPStreamStats s;
3751 0 : s.mTimestamp.Construct(query->now);
3752 0 : s.mId.Construct(localId);
3753 0 : s.mType.Construct(RTCStatsType::Outbound_rtp);
3754 0 : if (ssrc.Length()) {
3755 0 : s.mSsrc.Construct(ssrc);
3756 : }
3757 0 : s.mMediaType.Construct(mediaType);
3758 0 : s.mRemoteId.Construct(remoteId);
3759 0 : s.mIsRemote = false;
3760 0 : s.mPacketsSent.Construct(mp.rtp_packets_sent());
3761 0 : s.mBytesSent.Construct(mp.rtp_bytes_sent());
3762 :
3763 : // Fill in packet type statistics
3764 0 : webrtc::RtcpPacketTypeCounter counters;
3765 0 : if (mp.Conduit()->GetSendPacketTypeStats(&counters)) {
3766 0 : s.mNackCount.Construct(counters.nack_packets);
3767 : // Fill in video only packet type stats
3768 0 : if (!isAudio) {
3769 0 : s.mFirCount.Construct(counters.fir_packets);
3770 0 : s.mPliCount.Construct(counters.pli_packets);
3771 : }
3772 : }
3773 :
3774 : // Lastly, fill in video encoder stats if this is video
3775 0 : if (!isAudio) {
3776 : double framerateMean;
3777 : double framerateStdDev;
3778 : double bitrateMean;
3779 : double bitrateStdDev;
3780 : uint32_t droppedFrames;
3781 : uint32_t framesEncoded;
3782 0 : if (mp.Conduit()->GetVideoEncoderStats(&framerateMean,
3783 : &framerateStdDev,
3784 : &bitrateMean,
3785 : &bitrateStdDev,
3786 : &droppedFrames,
3787 0 : &framesEncoded)) {
3788 0 : s.mFramerateMean.Construct(framerateMean);
3789 0 : s.mFramerateStdDev.Construct(framerateStdDev);
3790 0 : s.mBitrateMean.Construct(bitrateMean);
3791 0 : s.mBitrateStdDev.Construct(bitrateStdDev);
3792 0 : s.mDroppedFrames.Construct(droppedFrames);
3793 0 : s.mFramesEncoded.Construct(framesEncoded);
3794 : }
3795 : }
3796 0 : query->report->mOutboundRTPStreamStats.Value().AppendElement(s,
3797 0 : fallible);
3798 : }
3799 0 : break;
3800 : }
3801 : case MediaPipeline::RECEIVE: {
3802 0 : nsString localId = NS_LITERAL_STRING("inbound_rtp_") + idstr;
3803 0 : nsString remoteId;
3804 0 : nsString ssrc;
3805 : unsigned int ssrcval;
3806 0 : if (mp.Conduit()->GetRemoteSSRC(&ssrcval)) {
3807 0 : ssrc.AppendInt(ssrcval);
3808 : }
3809 : {
3810 : // First, fill in remote stat with rtcp sender data, if present.
3811 : DOMHighResTimeStamp timestamp;
3812 : uint32_t packetsSent;
3813 : uint64_t bytesSent;
3814 0 : if (mp.Conduit()->GetRTCPSenderReport(×tamp,
3815 0 : &packetsSent, &bytesSent)) {
3816 0 : remoteId = NS_LITERAL_STRING("inbound_rtcp_") + idstr;
3817 0 : RTCOutboundRTPStreamStats s;
3818 0 : s.mTimestamp.Construct(timestamp);
3819 0 : s.mId.Construct(remoteId);
3820 0 : s.mType.Construct(RTCStatsType::Outbound_rtp);
3821 0 : if (ssrc.Length()) {
3822 0 : s.mSsrc.Construct(ssrc);
3823 : }
3824 0 : s.mMediaType.Construct(mediaType);
3825 0 : s.mRemoteId.Construct(localId);
3826 0 : s.mIsRemote = true;
3827 0 : s.mPacketsSent.Construct(packetsSent);
3828 0 : s.mBytesSent.Construct(bytesSent);
3829 0 : query->report->mOutboundRTPStreamStats.Value().AppendElement(s,
3830 0 : fallible);
3831 : }
3832 : }
3833 : // Then, fill in local side (with cross-link to remote only if present)
3834 0 : RTCInboundRTPStreamStats s;
3835 0 : s.mTimestamp.Construct(query->now);
3836 0 : s.mId.Construct(localId);
3837 0 : s.mType.Construct(RTCStatsType::Inbound_rtp);
3838 0 : if (ssrc.Length()) {
3839 0 : s.mSsrc.Construct(ssrc);
3840 : }
3841 0 : s.mMediaType.Construct(mediaType);
3842 : unsigned int jitterMs, packetsLost;
3843 0 : if (mp.Conduit()->GetRTPStats(&jitterMs, &packetsLost)) {
3844 0 : s.mJitter.Construct(double(jitterMs)/1000);
3845 0 : s.mPacketsLost.Construct(packetsLost);
3846 : }
3847 0 : if (remoteId.Length()) {
3848 0 : s.mRemoteId.Construct(remoteId);
3849 : }
3850 0 : s.mIsRemote = false;
3851 0 : s.mPacketsReceived.Construct(mp.rtp_packets_received());
3852 0 : s.mBytesReceived.Construct(mp.rtp_bytes_received());
3853 :
3854 0 : if (query->internalStats && isAudio) {
3855 : int32_t jitterBufferDelay;
3856 : int32_t playoutBufferDelay;
3857 : int32_t avSyncDelta;
3858 0 : if (mp.Conduit()->GetAVStats(&jitterBufferDelay,
3859 : &playoutBufferDelay,
3860 0 : &avSyncDelta)) {
3861 0 : s.mMozJitterBufferDelay.Construct(jitterBufferDelay);
3862 0 : s.mMozAvSyncDelay.Construct(avSyncDelta);
3863 : }
3864 : }
3865 : // Fill in packet type statistics
3866 0 : webrtc::RtcpPacketTypeCounter counters;
3867 0 : if (mp.Conduit()->GetRecvPacketTypeStats(&counters)) {
3868 0 : s.mNackCount.Construct(counters.nack_packets);
3869 : // Fill in video only packet type stats
3870 0 : if (!isAudio) {
3871 0 : s.mFirCount.Construct(counters.fir_packets);
3872 0 : s.mPliCount.Construct(counters.pli_packets);
3873 : }
3874 : }
3875 : // Lastly, fill in video decoder stats if this is video
3876 0 : if (!isAudio) {
3877 : double framerateMean;
3878 : double framerateStdDev;
3879 : double bitrateMean;
3880 : double bitrateStdDev;
3881 : uint32_t discardedPackets;
3882 : uint32_t framesDecoded;
3883 0 : if (mp.Conduit()->GetVideoDecoderStats(&framerateMean,
3884 : &framerateStdDev,
3885 : &bitrateMean,
3886 : &bitrateStdDev,
3887 : &discardedPackets,
3888 0 : &framesDecoded)) {
3889 0 : s.mFramerateMean.Construct(framerateMean);
3890 0 : s.mFramerateStdDev.Construct(framerateStdDev);
3891 0 : s.mBitrateMean.Construct(bitrateMean);
3892 0 : s.mBitrateStdDev.Construct(bitrateStdDev);
3893 0 : s.mDiscardedPackets.Construct(discardedPackets);
3894 0 : s.mFramesDecoded.Construct(framesDecoded);
3895 : }
3896 : }
3897 0 : query->report->mInboundRTPStreamStats.Value().AppendElement(s,
3898 0 : fallible);
3899 : // Fill in Contributing Source statistics
3900 0 : mp.GetContributingSourceStats(localId,
3901 0 : query->report->mRtpContributingSourceStats.Value());
3902 0 : break;
3903 : }
3904 : }
3905 :
3906 0 : if (!query->grabAllLevels) {
3907 : // If we're grabbing all levels, that means we want datachannels too,
3908 : // which don't have pipelines.
3909 0 : if (query->iceCtx->GetStream(p)) {
3910 0 : RecordIceStats_s(*query->iceCtx->GetStream(p),
3911 0 : query->internalStats,
3912 : query->now,
3913 0 : query->report);
3914 : }
3915 : }
3916 : }
3917 :
3918 0 : if (query->grabAllLevels) {
3919 0 : for (size_t i = 0; i < query->iceCtx->GetStreamCount(); ++i) {
3920 0 : if (query->iceCtx->GetStream(i)) {
3921 0 : RecordIceStats_s(*query->iceCtx->GetStream(i),
3922 0 : query->internalStats,
3923 : query->now,
3924 0 : query->report);
3925 : }
3926 : }
3927 : }
3928 :
3929 : // NrIceCtx must be destroyed on STS, so it is not safe
3930 : // to dispatch it back to main.
3931 0 : query->iceCtx = nullptr;
3932 0 : return NS_OK;
3933 : }
3934 :
3935 0 : void PeerConnectionImpl::GetStatsForPCObserver_s(
3936 : const std::string& pcHandle, // The Runnable holds the memory
3937 : nsAutoPtr<RTCStatsQuery> query) {
3938 :
3939 0 : MOZ_ASSERT(query);
3940 0 : MOZ_ASSERT(query->iceCtx);
3941 0 : ASSERT_ON_THREAD(query->iceCtx->thread());
3942 :
3943 0 : nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get());
3944 :
3945 : NS_DispatchToMainThread(
3946 0 : WrapRunnableNM(
3947 : &PeerConnectionImpl::DeliverStatsReportToPCObserver_m,
3948 : pcHandle,
3949 : rv,
3950 : query),
3951 0 : NS_DISPATCH_NORMAL);
3952 0 : }
3953 :
3954 0 : void PeerConnectionImpl::DeliverStatsReportToPCObserver_m(
3955 : const std::string& pcHandle,
3956 : nsresult result,
3957 : nsAutoPtr<RTCStatsQuery> query) {
3958 :
3959 : // Is the PeerConnectionImpl still around?
3960 0 : PeerConnectionWrapper pcw(pcHandle);
3961 0 : if (pcw.impl()) {
3962 : RefPtr<PeerConnectionObserver> pco =
3963 0 : do_QueryObjectReferent(pcw.impl()->mPCObserver);
3964 0 : if (pco) {
3965 0 : JSErrorResult rv;
3966 0 : if (NS_SUCCEEDED(result)) {
3967 0 : pco->OnGetStatsSuccess(*query->report, rv);
3968 : } else {
3969 0 : pco->OnGetStatsError(kInternalError,
3970 0 : ObString("Failed to fetch statistics"),
3971 0 : rv);
3972 : }
3973 :
3974 0 : if (rv.Failed()) {
3975 0 : CSFLogError(logTag, "Error firing stats observer callback");
3976 : }
3977 : }
3978 : }
3979 0 : }
3980 :
3981 : void
3982 0 : PeerConnectionImpl::RecordLongtermICEStatistics() {
3983 0 : WebrtcGlobalInformation::StoreLongTermICEStatistics(*this);
3984 0 : }
3985 :
3986 : void
3987 0 : PeerConnectionImpl::OnNegotiationNeeded()
3988 : {
3989 0 : if (mSignalingState != PCImplSignalingState::SignalingStable) {
3990 : // We will check whether we need to renegotiate when we reach stable again
3991 0 : return;
3992 : }
3993 :
3994 0 : if (mNegotiationNeeded) {
3995 0 : return;
3996 : }
3997 :
3998 0 : mNegotiationNeeded = true;
3999 :
4000 : RUN_ON_THREAD(mThread,
4001 0 : WrapRunnableNM(&MaybeFireNegotiationNeeded_static, mHandle),
4002 0 : NS_DISPATCH_NORMAL);
4003 : }
4004 :
4005 : /* static */
4006 : void
4007 0 : PeerConnectionImpl::MaybeFireNegotiationNeeded_static(
4008 : const std::string& pcHandle)
4009 : {
4010 0 : PeerConnectionWrapper wrapper(pcHandle);
4011 0 : if (!wrapper.impl()) {
4012 0 : return;
4013 : }
4014 :
4015 0 : wrapper.impl()->MaybeFireNegotiationNeeded();
4016 : }
4017 :
4018 : void
4019 0 : PeerConnectionImpl::MaybeFireNegotiationNeeded()
4020 : {
4021 0 : if (!mNegotiationNeeded) {
4022 0 : return;
4023 : }
4024 :
4025 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
4026 0 : if (!pco) {
4027 0 : return;
4028 : }
4029 :
4030 0 : JSErrorResult rv;
4031 0 : pco->OnNegotiationNeeded(rv);
4032 : }
4033 :
4034 : void
4035 0 : PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream)
4036 : {
4037 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
4038 0 : MOZ_ASSERT(aStream);
4039 :
4040 0 : CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str());
4041 0 : }
4042 :
4043 : //Telemetry for when calls start
4044 : void
4045 0 : PeerConnectionImpl::startCallTelem() {
4046 0 : if (!mStartTime.IsNull()) {
4047 0 : return;
4048 : }
4049 :
4050 : // Start time for calls
4051 0 : mStartTime = TimeStamp::Now();
4052 :
4053 : // Increment session call counter
4054 : // If we want to track Loop calls independently here, we need two histograms.
4055 0 : Telemetry::Accumulate(Telemetry::WEBRTC_CALL_COUNT_2, 1);
4056 : }
4057 :
4058 : NS_IMETHODIMP
4059 0 : PeerConnectionImpl::GetLocalStreams(nsTArray<RefPtr<DOMMediaStream > >& result)
4060 : {
4061 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
4062 0 : for(uint32_t i=0; i < media()->LocalStreamsLength(); i++) {
4063 0 : LocalSourceStreamInfo *info = media()->GetLocalStreamByIndex(i);
4064 0 : NS_ENSURE_TRUE(info, NS_ERROR_UNEXPECTED);
4065 0 : result.AppendElement(info->GetMediaStream());
4066 : }
4067 0 : return NS_OK;
4068 : }
4069 :
4070 : NS_IMETHODIMP
4071 0 : PeerConnectionImpl::GetRemoteStreams(nsTArray<RefPtr<DOMMediaStream > >& result)
4072 : {
4073 0 : PC_AUTO_ENTER_API_CALL_NO_CHECK();
4074 0 : for(uint32_t i=0; i < media()->RemoteStreamsLength(); i++) {
4075 0 : RemoteSourceStreamInfo *info = media()->GetRemoteStreamByIndex(i);
4076 0 : NS_ENSURE_TRUE(info, NS_ERROR_UNEXPECTED);
4077 0 : result.AppendElement(info->GetMediaStream());
4078 : }
4079 0 : return NS_OK;
4080 : }
4081 :
4082 : void
4083 0 : PeerConnectionImpl::DTMFSendTimerCallback_m(nsITimer* timer, void* closure)
4084 : {
4085 0 : MOZ_ASSERT(NS_IsMainThread());
4086 :
4087 0 : auto state = static_cast<DTMFState*>(closure);
4088 :
4089 0 : nsString eventTone;
4090 0 : if (!state->mTones.IsEmpty()) {
4091 0 : uint16_t toneChar = state->mTones.CharAt(0);
4092 0 : int tone = GetDTMFToneCode(toneChar);
4093 :
4094 0 : eventTone.Assign(toneChar);
4095 :
4096 0 : state->mTones.Cut(0, 1);
4097 :
4098 0 : if (tone == -1) {
4099 0 : state->mSendTimer->InitWithNamedFuncCallback(DTMFSendTimerCallback_m, state,
4100 : 2000, nsITimer::TYPE_ONE_SHOT,
4101 0 : "DTMFSendTimerCallback_m");
4102 : } else {
4103 : // Reset delay if necessary
4104 0 : state->mSendTimer->InitWithNamedFuncCallback(DTMFSendTimerCallback_m, state,
4105 0 : state->mDuration + state->mInterToneGap,
4106 : nsITimer::TYPE_ONE_SHOT,
4107 0 : "DTMFSendTimerCallback_m");
4108 :
4109 : RefPtr<AudioSessionConduit> conduit =
4110 0 : state->mPeerConnectionImpl->mMedia->GetAudioConduit(state->mLevel);
4111 :
4112 0 : if (conduit) {
4113 0 : uint32_t duration = state->mDuration;
4114 0 : state->mPeerConnectionImpl->mSTSThread->Dispatch(WrapRunnableNM([conduit, tone, duration] () {
4115 : //Note: We default to channel 0, not inband, and 6dB attenuation.
4116 : // here. We might want to revisit these choices in the future.
4117 0 : conduit->InsertDTMFTone(0, tone, true, duration, 6);
4118 0 : }), NS_DISPATCH_NORMAL);
4119 : }
4120 :
4121 : }
4122 : } else {
4123 0 : state->mSendTimer->Cancel();
4124 : }
4125 :
4126 0 : RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(state->mPeerConnectionImpl->mPCObserver);
4127 0 : if (!pco) {
4128 0 : NS_WARNING("Failed to dispatch the RTCDTMFToneChange event!");
4129 0 : return;
4130 : }
4131 :
4132 0 : JSErrorResult jrv;
4133 0 : pco->OnDTMFToneChange(state->mTrackId, eventTone, jrv);
4134 :
4135 0 : if (jrv.Failed()) {
4136 0 : NS_WARNING("Failed to dispatch the RTCDTMFToneChange event!");
4137 0 : return;
4138 : }
4139 : }
4140 :
4141 : } // end mozilla namespace
|