|
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 <sstream> |
|
9 |
|
10 #include "base/histogram.h" |
|
11 #include "vcm.h" |
|
12 #include "CSFLog.h" |
|
13 #include "timecard.h" |
|
14 #include "ccapi_call_info.h" |
|
15 #include "CC_SIPCCCallInfo.h" |
|
16 #include "ccapi_device_info.h" |
|
17 #include "CC_SIPCCDeviceInfo.h" |
|
18 #include "cpr_string.h" |
|
19 #include "cpr_stdlib.h" |
|
20 |
|
21 #include "jsapi.h" |
|
22 #include "nspr.h" |
|
23 #include "nss.h" |
|
24 #include "pk11pub.h" |
|
25 |
|
26 #include "nsNetCID.h" |
|
27 #include "nsIProperty.h" |
|
28 #include "nsIPropertyBag2.h" |
|
29 #include "nsIServiceManager.h" |
|
30 #include "nsISimpleEnumerator.h" |
|
31 #include "nsServiceManagerUtils.h" |
|
32 #include "nsISocketTransportService.h" |
|
33 #include "nsIConsoleService.h" |
|
34 #include "nsThreadUtils.h" |
|
35 #include "nsProxyRelease.h" |
|
36 #include "prtime.h" |
|
37 |
|
38 #include "AudioConduit.h" |
|
39 #include "VideoConduit.h" |
|
40 #include "runnable_utils.h" |
|
41 #include "PeerConnectionCtx.h" |
|
42 #include "PeerConnectionImpl.h" |
|
43 #include "PeerConnectionMedia.h" |
|
44 #include "nsDOMDataChannelDeclarations.h" |
|
45 #include "dtlsidentity.h" |
|
46 |
|
47 #ifdef MOZILLA_INTERNAL_API |
|
48 #include "nsPerformance.h" |
|
49 #include "nsGlobalWindow.h" |
|
50 #include "nsDOMDataChannel.h" |
|
51 #include "mozilla/TimeStamp.h" |
|
52 #include "mozilla/Telemetry.h" |
|
53 #include "mozilla/Preferences.h" |
|
54 #include "mozilla/PublicSSL.h" |
|
55 #include "nsXULAppAPI.h" |
|
56 #include "nsContentUtils.h" |
|
57 #include "nsDOMJSUtils.h" |
|
58 #include "nsIDocument.h" |
|
59 #include "nsIScriptError.h" |
|
60 #include "nsPrintfCString.h" |
|
61 #include "nsURLHelper.h" |
|
62 #include "nsNetUtil.h" |
|
63 #include "nsIDOMDataChannel.h" |
|
64 #include "nsIDOMLocation.h" |
|
65 #include "mozilla/dom/RTCConfigurationBinding.h" |
|
66 #include "mozilla/dom/RTCStatsReportBinding.h" |
|
67 #include "mozilla/dom/RTCPeerConnectionBinding.h" |
|
68 #include "mozilla/dom/PeerConnectionImplBinding.h" |
|
69 #include "mozilla/dom/DataChannelBinding.h" |
|
70 #include "MediaStreamList.h" |
|
71 #include "MediaStreamTrack.h" |
|
72 #include "AudioStreamTrack.h" |
|
73 #include "VideoStreamTrack.h" |
|
74 #include "nsIScriptGlobalObject.h" |
|
75 #include "DOMMediaStream.h" |
|
76 #include "rlogringbuffer.h" |
|
77 #include "WebrtcGlobalInformation.h" |
|
78 #endif |
|
79 |
|
80 #ifndef USE_FAKE_MEDIA_STREAMS |
|
81 #include "MediaSegment.h" |
|
82 #endif |
|
83 |
|
84 #ifdef USE_FAKE_PCOBSERVER |
|
85 #include "FakePCObserver.h" |
|
86 #else |
|
87 #include "mozilla/dom/PeerConnectionObserverBinding.h" |
|
88 #endif |
|
89 #include "mozilla/dom/PeerConnectionObserverEnumsBinding.h" |
|
90 |
|
91 #define ICE_PARSING "In RTCConfiguration passed to RTCPeerConnection constructor" |
|
92 |
|
93 using namespace mozilla; |
|
94 using namespace mozilla::dom; |
|
95 |
|
96 typedef PCObserverString ObString; |
|
97 |
|
98 static const char* logTag = "PeerConnectionImpl"; |
|
99 |
|
100 #ifdef MOZILLA_INTERNAL_API |
|
101 static nsresult InitNSSInContent() |
|
102 { |
|
103 NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD); |
|
104 |
|
105 if (XRE_GetProcessType() != GeckoProcessType_Content) { |
|
106 MOZ_ASSUME_UNREACHABLE("Must be called in content process"); |
|
107 } |
|
108 |
|
109 static bool nssStarted = false; |
|
110 if (nssStarted) { |
|
111 return NS_OK; |
|
112 } |
|
113 |
|
114 if (NSS_NoDB_Init(nullptr) != SECSuccess) { |
|
115 CSFLogError(logTag, "NSS_NoDB_Init failed."); |
|
116 return NS_ERROR_FAILURE; |
|
117 } |
|
118 |
|
119 if (NS_FAILED(mozilla::psm::InitializeCipherSuite())) { |
|
120 CSFLogError(logTag, "Fail to set up nss cipher suite."); |
|
121 return NS_ERROR_FAILURE; |
|
122 } |
|
123 |
|
124 mozilla::psm::DisableMD5(); |
|
125 |
|
126 nssStarted = true; |
|
127 |
|
128 return NS_OK; |
|
129 } |
|
130 #endif // MOZILLA_INTERNAL_API |
|
131 |
|
132 namespace mozilla { |
|
133 class DataChannel; |
|
134 } |
|
135 |
|
136 class nsIDOMDataChannel; |
|
137 |
|
138 static const int MEDIA_STREAM_MUTE = 0x80; |
|
139 |
|
140 PRLogModuleInfo *signalingLogInfo() { |
|
141 static PRLogModuleInfo *logModuleInfo = nullptr; |
|
142 if (!logModuleInfo) { |
|
143 logModuleInfo = PR_NewLogModule("signaling"); |
|
144 } |
|
145 return logModuleInfo; |
|
146 } |
|
147 |
|
148 |
|
149 namespace sipcc { |
|
150 |
|
151 #ifdef MOZILLA_INTERNAL_API |
|
152 RTCStatsQuery::RTCStatsQuery(bool internal) : internalStats(internal) { |
|
153 } |
|
154 |
|
155 RTCStatsQuery::~RTCStatsQuery() { |
|
156 MOZ_ASSERT(NS_IsMainThread()); |
|
157 } |
|
158 |
|
159 #endif |
|
160 |
|
161 // Getting exceptions back down from PCObserver is generally not harmful. |
|
162 namespace { |
|
163 class JSErrorResult : public ErrorResult |
|
164 { |
|
165 public: |
|
166 ~JSErrorResult() |
|
167 { |
|
168 #ifdef MOZILLA_INTERNAL_API |
|
169 WouldReportJSException(); |
|
170 if (IsJSException()) { |
|
171 MOZ_ASSERT(NS_IsMainThread()); |
|
172 AutoJSContext cx; |
|
173 Optional<JS::Handle<JS::Value> > value(cx); |
|
174 StealJSException(cx, &value.Value()); |
|
175 } |
|
176 #endif |
|
177 } |
|
178 }; |
|
179 |
|
180 // The WrapRunnable() macros copy passed-in args and passes them to the function |
|
181 // later on the other thread. ErrorResult cannot be passed like this because it |
|
182 // disallows copy-semantics. |
|
183 // |
|
184 // This WrappableJSErrorResult hack solves this by not actually copying the |
|
185 // ErrorResult, but creating a new one instead, which works because we don't |
|
186 // care about the result. |
|
187 // |
|
188 // Since this is for JS-calls, these can only be dispatched to the main thread. |
|
189 |
|
190 class WrappableJSErrorResult { |
|
191 public: |
|
192 WrappableJSErrorResult() : isCopy(false) {} |
|
193 WrappableJSErrorResult(WrappableJSErrorResult &other) : mRv(), isCopy(true) {} |
|
194 ~WrappableJSErrorResult() { |
|
195 if (isCopy) { |
|
196 #ifdef MOZILLA_INTERNAL_API |
|
197 MOZ_ASSERT(NS_IsMainThread()); |
|
198 #endif |
|
199 } |
|
200 } |
|
201 operator JSErrorResult &() { return mRv; } |
|
202 private: |
|
203 JSErrorResult mRv; |
|
204 bool isCopy; |
|
205 }; |
|
206 } |
|
207 |
|
208 class PeerConnectionObserverDispatch : public nsRunnable { |
|
209 |
|
210 public: |
|
211 PeerConnectionObserverDispatch(CSF::CC_CallInfoPtr aInfo, |
|
212 nsRefPtr<PeerConnectionImpl> aPC, |
|
213 PeerConnectionObserver* aObserver) |
|
214 : mPC(aPC), |
|
215 mObserver(aObserver), |
|
216 mCode(static_cast<PeerConnectionImpl::Error>(aInfo->getStatusCode())), |
|
217 mReason(aInfo->getStatus()), |
|
218 mSdpStr(), |
|
219 mCandidateStr(), |
|
220 mCallState(aInfo->getCallState()), |
|
221 mFsmState(aInfo->getFsmState()), |
|
222 mStateStr(aInfo->callStateToString(mCallState)), |
|
223 mFsmStateStr(aInfo->fsmStateToString(mFsmState)) { |
|
224 if (mCallState == REMOTESTREAMADD) { |
|
225 MediaStreamTable *streams = nullptr; |
|
226 streams = aInfo->getMediaStreams(); |
|
227 mRemoteStream = mPC->media()->GetRemoteStream(streams->media_stream_id); |
|
228 MOZ_ASSERT(mRemoteStream); |
|
229 } else if (mCallState == FOUNDICECANDIDATE) { |
|
230 mCandidateStr = aInfo->getCandidate(); |
|
231 } else if ((mCallState == CREATEOFFERSUCCESS) || |
|
232 (mCallState == CREATEANSWERSUCCESS)) { |
|
233 mSdpStr = aInfo->getSDP(); |
|
234 } |
|
235 } |
|
236 |
|
237 ~PeerConnectionObserverDispatch(){} |
|
238 |
|
239 #ifdef MOZILLA_INTERNAL_API |
|
240 class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback |
|
241 { |
|
242 public: |
|
243 TracksAvailableCallback(DOMMediaStream::TrackTypeHints aTrackTypeHints, |
|
244 nsRefPtr<PeerConnectionObserver> aObserver) |
|
245 : DOMMediaStream::OnTracksAvailableCallback(aTrackTypeHints) |
|
246 , mObserver(aObserver) {} |
|
247 |
|
248 virtual void NotifyTracksAvailable(DOMMediaStream* aStream) MOZ_OVERRIDE |
|
249 { |
|
250 MOZ_ASSERT(NS_IsMainThread()); |
|
251 |
|
252 // Start currentTime from the point where this stream was successfully |
|
253 // returned. |
|
254 aStream->SetLogicalStreamStartTime(aStream->GetStream()->GetCurrentTime()); |
|
255 |
|
256 CSFLogInfo(logTag, "Returning success for OnAddStream()"); |
|
257 // We are running on main thread here so we shouldn't have a race |
|
258 // on this callback |
|
259 JSErrorResult rv; |
|
260 mObserver->OnAddStream(*aStream, rv); |
|
261 if (rv.Failed()) { |
|
262 CSFLogError(logTag, ": OnAddStream() failed! Error: %d", rv.ErrorCode()); |
|
263 } |
|
264 } |
|
265 private: |
|
266 nsRefPtr<PeerConnectionObserver> mObserver; |
|
267 }; |
|
268 #endif |
|
269 |
|
270 NS_IMETHOD Run() { |
|
271 |
|
272 CSFLogInfo(logTag, "PeerConnectionObserverDispatch processing " |
|
273 "mCallState = %d (%s), mFsmState = %d (%s)", |
|
274 mCallState, mStateStr.c_str(), mFsmState, mFsmStateStr.c_str()); |
|
275 |
|
276 if (mCallState == SETLOCALDESCERROR || mCallState == SETREMOTEDESCERROR) { |
|
277 const std::vector<std::string> &errors = mPC->GetSdpParseErrors(); |
|
278 std::vector<std::string>::const_iterator i; |
|
279 for (i = errors.begin(); i != errors.end(); ++i) { |
|
280 mReason += " | SDP Parsing Error: " + *i; |
|
281 } |
|
282 if (errors.size()) { |
|
283 mCode = PeerConnectionImpl::kInvalidSessionDescription; |
|
284 } |
|
285 mPC->ClearSdpParseErrorMessages(); |
|
286 } |
|
287 |
|
288 if (mReason.length()) { |
|
289 CSFLogInfo(logTag, "Message contains error: %d: %s", |
|
290 mCode, mReason.c_str()); |
|
291 } |
|
292 |
|
293 /* |
|
294 * While the fsm_states_t (FSM_DEF_*) constants are a proper superset |
|
295 * of SignalingState, and the order in which the SignalingState values |
|
296 * appear matches the order they appear in fsm_states_t, their underlying |
|
297 * numeric representation is different. Hence, we need to perform an |
|
298 * offset calculation to map from one to the other. |
|
299 */ |
|
300 |
|
301 if (mFsmState >= FSMDEF_S_STABLE && mFsmState <= FSMDEF_S_CLOSED) { |
|
302 int offset = FSMDEF_S_STABLE - int(PCImplSignalingState::SignalingStable); |
|
303 mPC->SetSignalingState_m(static_cast<PCImplSignalingState>(mFsmState - offset)); |
|
304 } else { |
|
305 CSFLogError(logTag, ": **** UNHANDLED SIGNALING STATE : %d (%s)", |
|
306 mFsmState, mFsmStateStr.c_str()); |
|
307 } |
|
308 |
|
309 JSErrorResult rv; |
|
310 |
|
311 switch (mCallState) { |
|
312 case CREATEOFFERSUCCESS: |
|
313 mObserver->OnCreateOfferSuccess(ObString(mSdpStr.c_str()), rv); |
|
314 break; |
|
315 |
|
316 case CREATEANSWERSUCCESS: |
|
317 mObserver->OnCreateAnswerSuccess(ObString(mSdpStr.c_str()), rv); |
|
318 break; |
|
319 |
|
320 case CREATEOFFERERROR: |
|
321 mObserver->OnCreateOfferError(mCode, ObString(mReason.c_str()), rv); |
|
322 break; |
|
323 |
|
324 case CREATEANSWERERROR: |
|
325 mObserver->OnCreateAnswerError(mCode, ObString(mReason.c_str()), rv); |
|
326 break; |
|
327 |
|
328 case SETLOCALDESCSUCCESS: |
|
329 // TODO: The SDP Parse error list should be copied out and sent up |
|
330 // to the Javascript layer before being cleared here. Even though |
|
331 // there was not a failure, it is possible that the SDP parse generated |
|
332 // warnings. The WebRTC spec does not currently have a mechanism for |
|
333 // providing non-fatal warnings. |
|
334 mPC->ClearSdpParseErrorMessages(); |
|
335 mObserver->OnSetLocalDescriptionSuccess(rv); |
|
336 break; |
|
337 |
|
338 case SETREMOTEDESCSUCCESS: |
|
339 // TODO: The SDP Parse error list should be copied out and sent up |
|
340 // to the Javascript layer before being cleared here. Even though |
|
341 // there was not a failure, it is possible that the SDP parse generated |
|
342 // warnings. The WebRTC spec does not currently have a mechanism for |
|
343 // providing non-fatal warnings. |
|
344 mPC->ClearSdpParseErrorMessages(); |
|
345 mObserver->OnSetRemoteDescriptionSuccess(rv); |
|
346 #ifdef MOZILLA_INTERNAL_API |
|
347 mPC->startCallTelem(); |
|
348 #endif |
|
349 break; |
|
350 |
|
351 case SETLOCALDESCERROR: |
|
352 mObserver->OnSetLocalDescriptionError(mCode, |
|
353 ObString(mReason.c_str()), rv); |
|
354 break; |
|
355 |
|
356 case SETREMOTEDESCERROR: |
|
357 mObserver->OnSetRemoteDescriptionError(mCode, |
|
358 ObString(mReason.c_str()), rv); |
|
359 break; |
|
360 |
|
361 case ADDICECANDIDATE: |
|
362 mObserver->OnAddIceCandidateSuccess(rv); |
|
363 break; |
|
364 |
|
365 case ADDICECANDIDATEERROR: |
|
366 mObserver->OnAddIceCandidateError(mCode, ObString(mReason.c_str()), rv); |
|
367 break; |
|
368 |
|
369 case FOUNDICECANDIDATE: |
|
370 { |
|
371 size_t end_of_level = mCandidateStr.find('\t'); |
|
372 if (end_of_level == std::string::npos) { |
|
373 MOZ_ASSERT(false); |
|
374 return NS_OK; |
|
375 } |
|
376 std::string level = mCandidateStr.substr(0, end_of_level); |
|
377 if (!level.size()) { |
|
378 MOZ_ASSERT(false); |
|
379 return NS_OK; |
|
380 } |
|
381 char *endptr; |
|
382 errno = 0; |
|
383 unsigned long level_long = |
|
384 strtoul(level.c_str(), &endptr, 10); |
|
385 if (errno || *endptr != 0 || level_long > 65535) { |
|
386 /* Conversion failure */ |
|
387 MOZ_ASSERT(false); |
|
388 return NS_OK; |
|
389 } |
|
390 size_t end_of_mid = mCandidateStr.find('\t', end_of_level + 1); |
|
391 if (end_of_mid == std::string::npos) { |
|
392 MOZ_ASSERT(false); |
|
393 return NS_OK; |
|
394 } |
|
395 |
|
396 std::string mid = mCandidateStr.substr(end_of_level + 1, |
|
397 end_of_mid - (end_of_level + 1)); |
|
398 |
|
399 std::string candidate = mCandidateStr.substr(end_of_mid + 1); |
|
400 |
|
401 mObserver->OnIceCandidate(level_long & 0xffff, |
|
402 ObString(mid.c_str()), |
|
403 ObString(candidate.c_str()), rv); |
|
404 } |
|
405 break; |
|
406 case REMOTESTREAMADD: |
|
407 { |
|
408 DOMMediaStream* stream = nullptr; |
|
409 |
|
410 if (!mRemoteStream) { |
|
411 CSFLogError(logTag, "%s: GetRemoteStream returned NULL", __FUNCTION__); |
|
412 } else { |
|
413 stream = mRemoteStream->GetMediaStream(); |
|
414 } |
|
415 |
|
416 if (!stream) { |
|
417 CSFLogError(logTag, "%s: GetMediaStream returned NULL", __FUNCTION__); |
|
418 } else { |
|
419 #ifdef MOZILLA_INTERNAL_API |
|
420 TracksAvailableCallback* tracksAvailableCallback = |
|
421 new TracksAvailableCallback(mRemoteStream->mTrackTypeHints, mObserver); |
|
422 |
|
423 stream->OnTracksAvailable(tracksAvailableCallback); |
|
424 #else |
|
425 mObserver->OnAddStream(stream, rv); |
|
426 #endif |
|
427 } |
|
428 break; |
|
429 } |
|
430 |
|
431 case UPDATELOCALDESC: |
|
432 /* No action necessary */ |
|
433 break; |
|
434 |
|
435 default: |
|
436 CSFLogError(logTag, ": **** UNHANDLED CALL STATE : %d (%s)", |
|
437 mCallState, mStateStr.c_str()); |
|
438 break; |
|
439 } |
|
440 return NS_OK; |
|
441 } |
|
442 |
|
443 private: |
|
444 nsRefPtr<PeerConnectionImpl> mPC; |
|
445 nsRefPtr<PeerConnectionObserver> mObserver; |
|
446 PeerConnectionImpl::Error mCode; |
|
447 std::string mReason; |
|
448 std::string mSdpStr; |
|
449 std::string mCandidateStr; |
|
450 cc_call_state_t mCallState; |
|
451 fsmdef_states_t mFsmState; |
|
452 std::string mStateStr; |
|
453 std::string mFsmStateStr; |
|
454 nsRefPtr<RemoteSourceStreamInfo> mRemoteStream; |
|
455 }; |
|
456 |
|
457 NS_IMPL_ISUPPORTS0(PeerConnectionImpl) |
|
458 |
|
459 #ifdef MOZILLA_INTERNAL_API |
|
460 JSObject* |
|
461 PeerConnectionImpl::WrapObject(JSContext* aCx) |
|
462 { |
|
463 return PeerConnectionImplBinding::Wrap(aCx, this); |
|
464 } |
|
465 #endif |
|
466 |
|
467 struct PeerConnectionImpl::Internal { |
|
468 CSF::CC_CallPtr mCall; |
|
469 }; |
|
470 |
|
471 PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal) |
|
472 : mTimeCard(PR_LOG_TEST(signalingLogInfo(),PR_LOG_ERROR) ? |
|
473 create_timecard() : nullptr) |
|
474 , mInternal(new Internal()) |
|
475 , mReadyState(PCImplReadyState::New) |
|
476 , mSignalingState(PCImplSignalingState::SignalingStable) |
|
477 , mIceConnectionState(PCImplIceConnectionState::New) |
|
478 , mIceGatheringState(PCImplIceGatheringState::New) |
|
479 , mWindow(nullptr) |
|
480 , mIdentity(nullptr) |
|
481 , mSTSThread(nullptr) |
|
482 , mLoadManager(nullptr) |
|
483 , mMedia(nullptr) |
|
484 , mNumAudioStreams(0) |
|
485 , mNumVideoStreams(0) |
|
486 , mHaveDataStream(false) |
|
487 , mTrickle(true) // TODO(ekr@rtfm.com): Use pref |
|
488 { |
|
489 #ifdef MOZILLA_INTERNAL_API |
|
490 MOZ_ASSERT(NS_IsMainThread()); |
|
491 if (aGlobal) { |
|
492 mWindow = do_QueryInterface(aGlobal->GetAsSupports()); |
|
493 } |
|
494 #endif |
|
495 MOZ_ASSERT(mInternal); |
|
496 CSFLogInfo(logTag, "%s: PeerConnectionImpl constructor for %s", |
|
497 __FUNCTION__, mHandle.c_str()); |
|
498 STAMP_TIMECARD(mTimeCard, "Constructor Completed"); |
|
499 } |
|
500 |
|
501 PeerConnectionImpl::~PeerConnectionImpl() |
|
502 { |
|
503 if (mTimeCard) { |
|
504 STAMP_TIMECARD(mTimeCard, "Destructor Invoked"); |
|
505 print_timecard(mTimeCard); |
|
506 destroy_timecard(mTimeCard); |
|
507 mTimeCard = nullptr; |
|
508 } |
|
509 // This aborts if not on main thread (in Debug builds) |
|
510 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
511 if (PeerConnectionCtx::isActive()) { |
|
512 PeerConnectionCtx::GetInstance()->mPeerConnections.erase(mHandle); |
|
513 } else { |
|
514 CSFLogError(logTag, "PeerConnectionCtx is already gone. Ignoring..."); |
|
515 } |
|
516 |
|
517 CSFLogInfo(logTag, "%s: PeerConnectionImpl destructor invoked for %s", |
|
518 __FUNCTION__, mHandle.c_str()); |
|
519 CloseInt(); |
|
520 |
|
521 #ifdef MOZILLA_INTERNAL_API |
|
522 { |
|
523 // Deregister as an NSS Shutdown Object |
|
524 nsNSSShutDownPreventionLock locker; |
|
525 if (!isAlreadyShutDown()) { |
|
526 destructorSafeDestroyNSSReference(); |
|
527 shutdown(calledFromObject); |
|
528 } |
|
529 } |
|
530 if (mLoadManager) { |
|
531 mozilla::LoadManagerDestroy(mLoadManager); |
|
532 mLoadManager = nullptr; |
|
533 } |
|
534 #endif |
|
535 |
|
536 // Since this and Initialize() occur on MainThread, they can't both be |
|
537 // running at once |
|
538 |
|
539 // Right now, we delete PeerConnectionCtx at XPCOM shutdown only, but we |
|
540 // probably want to shut it down more aggressively to save memory. We |
|
541 // could shut down here when there are no uses. It might be more optimal |
|
542 // to release off a timer (and XPCOM Shutdown) to avoid churn |
|
543 } |
|
544 |
|
545 already_AddRefed<DOMMediaStream> |
|
546 PeerConnectionImpl::MakeMediaStream(nsPIDOMWindow* aWindow, |
|
547 uint32_t aHint) |
|
548 { |
|
549 nsRefPtr<DOMMediaStream> stream = |
|
550 DOMMediaStream::CreateSourceStream(aWindow, aHint); |
|
551 #ifdef MOZILLA_INTERNAL_API |
|
552 nsIDocument* doc = aWindow->GetExtantDoc(); |
|
553 if (!doc) { |
|
554 return nullptr; |
|
555 } |
|
556 // Make the stream data (audio/video samples) accessible to the receiving page. |
|
557 stream->CombineWithPrincipal(doc->NodePrincipal()); |
|
558 #endif |
|
559 |
|
560 CSFLogDebug(logTag, "Created media stream %p, inner: %p", stream.get(), stream->GetStream()); |
|
561 |
|
562 return stream.forget(); |
|
563 } |
|
564 |
|
565 nsresult |
|
566 PeerConnectionImpl::CreateRemoteSourceStreamInfo(nsRefPtr<RemoteSourceStreamInfo>* |
|
567 aInfo) |
|
568 { |
|
569 MOZ_ASSERT(aInfo); |
|
570 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
571 |
|
572 // We need to pass a dummy hint here because FakeMediaStream currently |
|
573 // needs to actually propagate a hint for local streams. |
|
574 // TODO(ekr@rtfm.com): Clean up when we have explicit track lists. |
|
575 // See bug 834835. |
|
576 nsRefPtr<DOMMediaStream> stream = MakeMediaStream(mWindow, 0); |
|
577 if (!stream) { |
|
578 return NS_ERROR_FAILURE; |
|
579 } |
|
580 |
|
581 static_cast<SourceMediaStream*>(stream->GetStream())->SetPullEnabled(true); |
|
582 |
|
583 nsRefPtr<RemoteSourceStreamInfo> remote; |
|
584 remote = new RemoteSourceStreamInfo(stream.forget(), mMedia); |
|
585 *aInfo = remote; |
|
586 |
|
587 return NS_OK; |
|
588 } |
|
589 |
|
590 /** |
|
591 * In JS, an RTCConfiguration looks like this: |
|
592 * |
|
593 * { "iceServers": [ { url:"stun:stun.example.org" }, |
|
594 * { url:"turn:turn.example.org?transport=udp", |
|
595 * username: "jib", credential:"mypass"} ] } |
|
596 * |
|
597 * This function converts that into an internal IceConfiguration object. |
|
598 */ |
|
599 nsresult |
|
600 PeerConnectionImpl::ConvertRTCConfiguration(const RTCConfiguration& aSrc, |
|
601 IceConfiguration *aDst) |
|
602 { |
|
603 #ifdef MOZILLA_INTERNAL_API |
|
604 if (!aSrc.mIceServers.WasPassed()) { |
|
605 return NS_OK; |
|
606 } |
|
607 for (uint32_t i = 0; i < aSrc.mIceServers.Value().Length(); i++) { |
|
608 const RTCIceServer& server = aSrc.mIceServers.Value()[i]; |
|
609 NS_ENSURE_TRUE(server.mUrl.WasPassed(), NS_ERROR_UNEXPECTED); |
|
610 |
|
611 // Without STUN/TURN handlers, NS_NewURI returns nsSimpleURI rather than |
|
612 // nsStandardURL. To parse STUN/TURN URI's to spec |
|
613 // http://tools.ietf.org/html/draft-nandakumar-rtcweb-stun-uri-02#section-3 |
|
614 // http://tools.ietf.org/html/draft-petithuguenin-behave-turn-uri-03#section-3 |
|
615 // we parse out the query-string, and use ParseAuthority() on the rest |
|
616 nsRefPtr<nsIURI> url; |
|
617 nsresult rv = NS_NewURI(getter_AddRefs(url), server.mUrl.Value()); |
|
618 NS_ENSURE_SUCCESS(rv, rv); |
|
619 bool isStun = false, isStuns = false, isTurn = false, isTurns = false; |
|
620 url->SchemeIs("stun", &isStun); |
|
621 url->SchemeIs("stuns", &isStuns); |
|
622 url->SchemeIs("turn", &isTurn); |
|
623 url->SchemeIs("turns", &isTurns); |
|
624 if (!(isStun || isStuns || isTurn || isTurns)) { |
|
625 return NS_ERROR_FAILURE; |
|
626 } |
|
627 nsAutoCString spec; |
|
628 rv = url->GetSpec(spec); |
|
629 NS_ENSURE_SUCCESS(rv, rv); |
|
630 |
|
631 // TODO(jib@mozilla.com): Revisit once nsURI supports STUN/TURN (Bug 833509) |
|
632 int32_t port; |
|
633 nsAutoCString host; |
|
634 nsAutoCString transport; |
|
635 { |
|
636 uint32_t hostPos; |
|
637 int32_t hostLen; |
|
638 nsAutoCString path; |
|
639 rv = url->GetPath(path); |
|
640 NS_ENSURE_SUCCESS(rv, rv); |
|
641 |
|
642 // Tolerate query-string + parse 'transport=[udp|tcp]' by hand. |
|
643 int32_t questionmark = path.FindChar('?'); |
|
644 if (questionmark >= 0) { |
|
645 const nsCString match = NS_LITERAL_CSTRING("transport="); |
|
646 |
|
647 for (int32_t i = questionmark, endPos; i >= 0; i = endPos) { |
|
648 endPos = path.FindCharInSet("&", i + 1); |
|
649 const nsDependentCSubstring fieldvaluepair = Substring(path, i + 1, |
|
650 endPos); |
|
651 if (StringBeginsWith(fieldvaluepair, match)) { |
|
652 transport = Substring(fieldvaluepair, match.Length()); |
|
653 ToLowerCase(transport); |
|
654 } |
|
655 } |
|
656 path.SetLength(questionmark); |
|
657 } |
|
658 |
|
659 rv = net_GetAuthURLParser()->ParseAuthority(path.get(), path.Length(), |
|
660 nullptr, nullptr, |
|
661 nullptr, nullptr, |
|
662 &hostPos, &hostLen, &port); |
|
663 NS_ENSURE_SUCCESS(rv, rv); |
|
664 if (!hostLen) { |
|
665 return NS_ERROR_FAILURE; |
|
666 } |
|
667 if (hostPos > 1) /* The username was removed */ |
|
668 return NS_ERROR_FAILURE; |
|
669 path.Mid(host, hostPos, hostLen); |
|
670 } |
|
671 if (port == -1) |
|
672 port = (isStuns || isTurns)? 5349 : 3478; |
|
673 |
|
674 if (isTurn || isTurns) { |
|
675 NS_ConvertUTF16toUTF8 credential(server.mCredential); |
|
676 NS_ConvertUTF16toUTF8 username(server.mUsername); |
|
677 |
|
678 #ifdef MOZ_WIDGET_GONK |
|
679 if (transport == kNrIceTransportTcp) |
|
680 continue; |
|
681 #endif |
|
682 if (!aDst->addTurnServer(host.get(), port, |
|
683 username.get(), |
|
684 credential.get(), |
|
685 (transport.IsEmpty() ? |
|
686 kNrIceTransportUdp : transport.get()))) { |
|
687 return NS_ERROR_FAILURE; |
|
688 } |
|
689 } else { |
|
690 if (!aDst->addStunServer(host.get(), port)) { |
|
691 return NS_ERROR_FAILURE; |
|
692 } |
|
693 } |
|
694 } |
|
695 #endif |
|
696 return NS_OK; |
|
697 } |
|
698 |
|
699 NS_IMETHODIMP |
|
700 PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver, |
|
701 nsGlobalWindow* aWindow, |
|
702 const IceConfiguration* aConfiguration, |
|
703 const RTCConfiguration* aRTCConfiguration, |
|
704 nsISupports* aThread) |
|
705 { |
|
706 nsresult res; |
|
707 |
|
708 // Invariant: we receive configuration one way or the other but not both (XOR) |
|
709 MOZ_ASSERT(!aConfiguration != !aRTCConfiguration); |
|
710 #ifdef MOZILLA_INTERNAL_API |
|
711 MOZ_ASSERT(NS_IsMainThread()); |
|
712 #endif |
|
713 MOZ_ASSERT(aThread); |
|
714 mThread = do_QueryInterface(aThread); |
|
715 |
|
716 mPCObserver = do_GetWeakReference(&aObserver); |
|
717 |
|
718 // Find the STS thread |
|
719 |
|
720 mSTSThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res); |
|
721 MOZ_ASSERT(mSTSThread); |
|
722 #ifdef MOZILLA_INTERNAL_API |
|
723 |
|
724 // Initialize NSS if we are in content process. For chrome process, NSS should already |
|
725 // been initialized. |
|
726 if (XRE_GetProcessType() == GeckoProcessType_Default) { |
|
727 // This code interferes with the C++ unit test startup code. |
|
728 nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &res); |
|
729 NS_ENSURE_SUCCESS(res, res); |
|
730 } else { |
|
731 NS_ENSURE_SUCCESS(res = InitNSSInContent(), res); |
|
732 } |
|
733 |
|
734 // Currently no standalone unit tests for DataChannel, |
|
735 // which is the user of mWindow |
|
736 MOZ_ASSERT(aWindow); |
|
737 mWindow = aWindow; |
|
738 NS_ENSURE_STATE(mWindow); |
|
739 |
|
740 #endif // MOZILLA_INTERNAL_API |
|
741 |
|
742 PRTime timestamp = PR_Now(); |
|
743 // Ok if we truncate this. |
|
744 char temp[128]; |
|
745 |
|
746 #ifdef MOZILLA_INTERNAL_API |
|
747 nsAutoCString locationCStr; |
|
748 nsIDOMLocation* location; |
|
749 res = mWindow->GetLocation(&location); |
|
750 |
|
751 if (location && NS_SUCCEEDED(res)) { |
|
752 nsAutoString locationAStr; |
|
753 location->ToString(locationAStr); |
|
754 location->Release(); |
|
755 |
|
756 CopyUTF16toUTF8(locationAStr, locationCStr); |
|
757 } |
|
758 |
|
759 PR_snprintf( |
|
760 temp, |
|
761 sizeof(temp), |
|
762 "%llu (id=%llu url=%s)", |
|
763 static_cast<unsigned long long>(timestamp), |
|
764 static_cast<unsigned long long>(mWindow ? mWindow->WindowID() : 0), |
|
765 locationCStr.get() ? locationCStr.get() : "NULL"); |
|
766 |
|
767 #else |
|
768 PR_snprintf(temp, sizeof(temp), "%llu", (unsigned long long)timestamp); |
|
769 #endif // MOZILLA_INTERNAL_API |
|
770 |
|
771 mName = temp; |
|
772 |
|
773 // Generate a random handle |
|
774 unsigned char handle_bin[8]; |
|
775 SECStatus rv; |
|
776 rv = PK11_GenerateRandom(handle_bin, sizeof(handle_bin)); |
|
777 if (rv != SECSuccess) { |
|
778 MOZ_CRASH(); |
|
779 return NS_ERROR_UNEXPECTED; |
|
780 } |
|
781 |
|
782 char hex[17]; |
|
783 PR_snprintf(hex,sizeof(hex),"%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", |
|
784 handle_bin[0], |
|
785 handle_bin[1], |
|
786 handle_bin[2], |
|
787 handle_bin[3], |
|
788 handle_bin[4], |
|
789 handle_bin[5], |
|
790 handle_bin[6], |
|
791 handle_bin[7]); |
|
792 |
|
793 mHandle = hex; |
|
794 |
|
795 STAMP_TIMECARD(mTimeCard, "Initializing PC Ctx"); |
|
796 res = PeerConnectionCtx::InitializeGlobal(mThread, mSTSThread); |
|
797 NS_ENSURE_SUCCESS(res, res); |
|
798 |
|
799 PeerConnectionCtx *pcctx = PeerConnectionCtx::GetInstance(); |
|
800 MOZ_ASSERT(pcctx); |
|
801 STAMP_TIMECARD(mTimeCard, "Done Initializing PC Ctx"); |
|
802 |
|
803 mInternal->mCall = pcctx->createCall(); |
|
804 if (!mInternal->mCall.get()) { |
|
805 CSFLogError(logTag, "%s: Couldn't Create Call Object", __FUNCTION__); |
|
806 return NS_ERROR_FAILURE; |
|
807 } |
|
808 |
|
809 IceConfiguration converted; |
|
810 if (aRTCConfiguration) { |
|
811 res = ConvertRTCConfiguration(*aRTCConfiguration, &converted); |
|
812 if (NS_FAILED(res)) { |
|
813 CSFLogError(logTag, "%s: Invalid RTCConfiguration", __FUNCTION__); |
|
814 return res; |
|
815 } |
|
816 aConfiguration = &converted; |
|
817 } |
|
818 |
|
819 mMedia = new PeerConnectionMedia(this); |
|
820 |
|
821 // Connect ICE slots. |
|
822 mMedia->SignalIceGatheringStateChange.connect( |
|
823 this, |
|
824 &PeerConnectionImpl::IceGatheringStateChange); |
|
825 mMedia->SignalIceConnectionStateChange.connect( |
|
826 this, |
|
827 &PeerConnectionImpl::IceConnectionStateChange); |
|
828 |
|
829 // Initialize the media object. |
|
830 res = mMedia->Init(aConfiguration->getStunServers(), |
|
831 aConfiguration->getTurnServers()); |
|
832 if (NS_FAILED(res)) { |
|
833 CSFLogError(logTag, "%s: Couldn't initialize media object", __FUNCTION__); |
|
834 return res; |
|
835 } |
|
836 |
|
837 // Store under mHandle |
|
838 mInternal->mCall->setPeerConnection(mHandle); |
|
839 PeerConnectionCtx::GetInstance()->mPeerConnections[mHandle] = this; |
|
840 |
|
841 STAMP_TIMECARD(mTimeCard, "Generating DTLS Identity"); |
|
842 // Create the DTLS Identity |
|
843 mIdentity = DtlsIdentity::Generate(); |
|
844 STAMP_TIMECARD(mTimeCard, "Done Generating DTLS Identity"); |
|
845 |
|
846 if (!mIdentity) { |
|
847 CSFLogError(logTag, "%s: Generate returned NULL", __FUNCTION__); |
|
848 return NS_ERROR_FAILURE; |
|
849 } |
|
850 |
|
851 mFingerprint = mIdentity->GetFormattedFingerprint(); |
|
852 if (mFingerprint.empty()) { |
|
853 CSFLogError(logTag, "%s: unable to get fingerprint", __FUNCTION__); |
|
854 return res; |
|
855 } |
|
856 |
|
857 #ifdef MOZILLA_INTERNAL_API |
|
858 if (mozilla::Preferences::GetBool("media.navigator.load_adapt", false)) { |
|
859 mLoadManager = mozilla::LoadManagerBuild(); |
|
860 } |
|
861 #endif |
|
862 |
|
863 return NS_OK; |
|
864 } |
|
865 |
|
866 RefPtr<DtlsIdentity> const |
|
867 PeerConnectionImpl::GetIdentity() const |
|
868 { |
|
869 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
870 return mIdentity; |
|
871 } |
|
872 |
|
873 std::string |
|
874 PeerConnectionImpl::GetFingerprint() const |
|
875 { |
|
876 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
877 return mFingerprint; |
|
878 } |
|
879 |
|
880 NS_IMETHODIMP |
|
881 PeerConnectionImpl::FingerprintSplitHelper(std::string& fingerprint, |
|
882 size_t& spaceIdx) const |
|
883 { |
|
884 fingerprint = GetFingerprint(); |
|
885 spaceIdx = fingerprint.find_first_of(' '); |
|
886 if (spaceIdx == std::string::npos) { |
|
887 CSFLogError(logTag, "%s: fingerprint is messed up: %s", |
|
888 __FUNCTION__, fingerprint.c_str()); |
|
889 return NS_ERROR_FAILURE; |
|
890 } |
|
891 return NS_OK; |
|
892 } |
|
893 |
|
894 std::string |
|
895 PeerConnectionImpl::GetFingerprintAlgorithm() const |
|
896 { |
|
897 std::string fp; |
|
898 size_t spc; |
|
899 if (NS_SUCCEEDED(FingerprintSplitHelper(fp, spc))) { |
|
900 return fp.substr(0, spc); |
|
901 } |
|
902 return ""; |
|
903 } |
|
904 |
|
905 std::string |
|
906 PeerConnectionImpl::GetFingerprintHexValue() const |
|
907 { |
|
908 std::string fp; |
|
909 size_t spc; |
|
910 if (NS_SUCCEEDED(FingerprintSplitHelper(fp, spc))) { |
|
911 return fp.substr(spc + 1); |
|
912 } |
|
913 return ""; |
|
914 } |
|
915 |
|
916 |
|
917 nsresult |
|
918 PeerConnectionImpl::CreateFakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aRetval) |
|
919 { |
|
920 MOZ_ASSERT(aRetval); |
|
921 PC_AUTO_ENTER_API_CALL(false); |
|
922 |
|
923 bool mute = false; |
|
924 |
|
925 // Hack to allow you to mute the stream |
|
926 if (aHint & MEDIA_STREAM_MUTE) { |
|
927 mute = true; |
|
928 aHint &= ~MEDIA_STREAM_MUTE; |
|
929 } |
|
930 |
|
931 nsRefPtr<DOMMediaStream> stream = MakeMediaStream(mWindow, aHint); |
|
932 if (!stream) { |
|
933 return NS_ERROR_FAILURE; |
|
934 } |
|
935 |
|
936 if (!mute) { |
|
937 if (aHint & DOMMediaStream::HINT_CONTENTS_AUDIO) { |
|
938 new Fake_AudioGenerator(stream); |
|
939 } else { |
|
940 #ifdef MOZILLA_INTERNAL_API |
|
941 new Fake_VideoGenerator(stream); |
|
942 #endif |
|
943 } |
|
944 } |
|
945 |
|
946 stream.forget(aRetval); |
|
947 return NS_OK; |
|
948 } |
|
949 |
|
950 // Stubbing this call out for now. |
|
951 // We can remove it when we are confident of datachannels being started |
|
952 // correctly on SDP negotiation (bug 852908) |
|
953 NS_IMETHODIMP |
|
954 PeerConnectionImpl::ConnectDataConnection(uint16_t aLocalport, |
|
955 uint16_t aRemoteport, |
|
956 uint16_t aNumstreams) |
|
957 { |
|
958 return NS_OK; // InitializeDataChannel(aLocalport, aRemoteport, aNumstreams); |
|
959 } |
|
960 |
|
961 // Data channels won't work without a window, so in order for the C++ unit |
|
962 // tests to work (it doesn't have a window available) we ifdef the following |
|
963 // two implementations. |
|
964 NS_IMETHODIMP |
|
965 PeerConnectionImpl::EnsureDataConnection(uint16_t aNumstreams) |
|
966 { |
|
967 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
968 |
|
969 #ifdef MOZILLA_INTERNAL_API |
|
970 if (mDataConnection) { |
|
971 CSFLogDebug(logTag,"%s DataConnection already connected",__FUNCTION__); |
|
972 // Ignore the request to connect when already connected. This entire |
|
973 // implementation is temporary. Ignore aNumstreams as it's merely advisory |
|
974 // and we increase the number of streams dynamically as needed. |
|
975 return NS_OK; |
|
976 } |
|
977 mDataConnection = new DataChannelConnection(this); |
|
978 if (!mDataConnection->Init(5000, aNumstreams, true)) { |
|
979 CSFLogError(logTag,"%s DataConnection Init Failed",__FUNCTION__); |
|
980 return NS_ERROR_FAILURE; |
|
981 } |
|
982 CSFLogDebug(logTag,"%s DataChannelConnection %p attached to %s", |
|
983 __FUNCTION__, (void*) mDataConnection.get(), mHandle.c_str()); |
|
984 #endif |
|
985 return NS_OK; |
|
986 } |
|
987 |
|
988 nsresult |
|
989 PeerConnectionImpl::InitializeDataChannel(int track_id, |
|
990 uint16_t aLocalport, |
|
991 uint16_t aRemoteport, |
|
992 uint16_t aNumstreams) |
|
993 { |
|
994 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
995 |
|
996 #ifdef MOZILLA_INTERNAL_API |
|
997 nsresult rv = EnsureDataConnection(aNumstreams); |
|
998 if (NS_SUCCEEDED(rv)) { |
|
999 // use the specified TransportFlow |
|
1000 nsRefPtr<TransportFlow> flow = mMedia->GetTransportFlow(track_id, false).get(); |
|
1001 CSFLogDebug(logTag, "Transportflow[%d] = %p", track_id, flow.get()); |
|
1002 if (flow) { |
|
1003 if (mDataConnection->ConnectViaTransportFlow(flow, aLocalport, aRemoteport)) { |
|
1004 return NS_OK; |
|
1005 } |
|
1006 } |
|
1007 // If we inited the DataConnection, call Destroy() before releasing it |
|
1008 mDataConnection->Destroy(); |
|
1009 } |
|
1010 mDataConnection = nullptr; |
|
1011 #endif |
|
1012 return NS_ERROR_FAILURE; |
|
1013 } |
|
1014 |
|
1015 already_AddRefed<nsDOMDataChannel> |
|
1016 PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel, |
|
1017 const nsAString& aProtocol, |
|
1018 uint16_t aType, |
|
1019 bool outOfOrderAllowed, |
|
1020 uint16_t aMaxTime, |
|
1021 uint16_t aMaxNum, |
|
1022 bool aExternalNegotiated, |
|
1023 uint16_t aStream, |
|
1024 ErrorResult &rv) |
|
1025 { |
|
1026 #ifdef MOZILLA_INTERNAL_API |
|
1027 nsRefPtr<nsDOMDataChannel> result; |
|
1028 rv = CreateDataChannel(aLabel, aProtocol, aType, outOfOrderAllowed, |
|
1029 aMaxTime, aMaxNum, aExternalNegotiated, |
|
1030 aStream, getter_AddRefs(result)); |
|
1031 return result.forget(); |
|
1032 #else |
|
1033 return nullptr; |
|
1034 #endif |
|
1035 } |
|
1036 |
|
1037 NS_IMETHODIMP |
|
1038 PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel, |
|
1039 const nsAString& aProtocol, |
|
1040 uint16_t aType, |
|
1041 bool outOfOrderAllowed, |
|
1042 uint16_t aMaxTime, |
|
1043 uint16_t aMaxNum, |
|
1044 bool aExternalNegotiated, |
|
1045 uint16_t aStream, |
|
1046 nsDOMDataChannel** aRetval) |
|
1047 { |
|
1048 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1049 MOZ_ASSERT(aRetval); |
|
1050 |
|
1051 #ifdef MOZILLA_INTERNAL_API |
|
1052 nsRefPtr<DataChannel> dataChannel; |
|
1053 DataChannelConnection::Type theType = |
|
1054 static_cast<DataChannelConnection::Type>(aType); |
|
1055 |
|
1056 nsresult rv = EnsureDataConnection(WEBRTC_DATACHANNEL_STREAMS_DEFAULT); |
|
1057 if (NS_FAILED(rv)) { |
|
1058 return rv; |
|
1059 } |
|
1060 dataChannel = mDataConnection->Open( |
|
1061 NS_ConvertUTF16toUTF8(aLabel), NS_ConvertUTF16toUTF8(aProtocol), theType, |
|
1062 !outOfOrderAllowed, |
|
1063 aType == DataChannelConnection::PARTIAL_RELIABLE_REXMIT ? aMaxNum : |
|
1064 (aType == DataChannelConnection::PARTIAL_RELIABLE_TIMED ? aMaxTime : 0), |
|
1065 nullptr, nullptr, aExternalNegotiated, aStream |
|
1066 ); |
|
1067 NS_ENSURE_TRUE(dataChannel,NS_ERROR_FAILURE); |
|
1068 |
|
1069 CSFLogDebug(logTag, "%s: making DOMDataChannel", __FUNCTION__); |
|
1070 |
|
1071 if (!mHaveDataStream) { |
|
1072 // XXX stream_id of 0 might confuse things... |
|
1073 mInternal->mCall->addStream(0, 2, DATA, 0); |
|
1074 mHaveDataStream = true; |
|
1075 } |
|
1076 nsIDOMDataChannel *retval; |
|
1077 rv = NS_NewDOMDataChannel(dataChannel.forget(), mWindow, &retval); |
|
1078 if (NS_FAILED(rv)) { |
|
1079 return rv; |
|
1080 } |
|
1081 *aRetval = static_cast<nsDOMDataChannel*>(retval); |
|
1082 #endif |
|
1083 return NS_OK; |
|
1084 } |
|
1085 |
|
1086 // do_QueryObjectReferent() - Helps get PeerConnectionObserver from nsWeakPtr. |
|
1087 // |
|
1088 // nsWeakPtr deals in XPCOM interfaces, while webidl bindings are concrete objs. |
|
1089 // TODO: Turn this into a central (template) function somewhere (Bug 939178) |
|
1090 // |
|
1091 // Without it, each weak-ref call in this file would look like this: |
|
1092 // |
|
1093 // nsCOMPtr<nsISupportsWeakReference> tmp = do_QueryReferent(mPCObserver); |
|
1094 // if (!tmp) { |
|
1095 // return; |
|
1096 // } |
|
1097 // nsRefPtr<nsSupportsWeakReference> tmp2 = do_QueryObject(tmp); |
|
1098 // nsRefPtr<PeerConnectionObserver> pco = static_cast<PeerConnectionObserver*>(&*tmp2); |
|
1099 |
|
1100 static already_AddRefed<PeerConnectionObserver> |
|
1101 do_QueryObjectReferent(nsIWeakReference* aRawPtr) { |
|
1102 nsCOMPtr<nsISupportsWeakReference> tmp = do_QueryReferent(aRawPtr); |
|
1103 if (!tmp) { |
|
1104 return nullptr; |
|
1105 } |
|
1106 nsRefPtr<nsSupportsWeakReference> tmp2 = do_QueryObject(tmp); |
|
1107 nsRefPtr<PeerConnectionObserver> tmp3 = static_cast<PeerConnectionObserver*>(&*tmp2); |
|
1108 return tmp3.forget(); |
|
1109 } |
|
1110 |
|
1111 void |
|
1112 PeerConnectionImpl::NotifyConnection() |
|
1113 { |
|
1114 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1115 |
|
1116 CSFLogDebug(logTag, "%s", __FUNCTION__); |
|
1117 |
|
1118 #ifdef MOZILLA_INTERNAL_API |
|
1119 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); |
|
1120 if (!pco) { |
|
1121 return; |
|
1122 } |
|
1123 WrappableJSErrorResult rv; |
|
1124 RUN_ON_THREAD(mThread, |
|
1125 WrapRunnable(pco, |
|
1126 &PeerConnectionObserver::NotifyConnection, |
|
1127 rv, static_cast<JSCompartment*>(nullptr)), |
|
1128 NS_DISPATCH_NORMAL); |
|
1129 #endif |
|
1130 } |
|
1131 |
|
1132 void |
|
1133 PeerConnectionImpl::NotifyClosedConnection() |
|
1134 { |
|
1135 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1136 |
|
1137 CSFLogDebug(logTag, "%s", __FUNCTION__); |
|
1138 |
|
1139 #ifdef MOZILLA_INTERNAL_API |
|
1140 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); |
|
1141 if (!pco) { |
|
1142 return; |
|
1143 } |
|
1144 WrappableJSErrorResult rv; |
|
1145 RUN_ON_THREAD(mThread, |
|
1146 WrapRunnable(pco, &PeerConnectionObserver::NotifyClosedConnection, |
|
1147 rv, static_cast<JSCompartment*>(nullptr)), |
|
1148 NS_DISPATCH_NORMAL); |
|
1149 #endif |
|
1150 } |
|
1151 |
|
1152 |
|
1153 #ifdef MOZILLA_INTERNAL_API |
|
1154 // Not a member function so that we don't need to keep the PC live. |
|
1155 static void NotifyDataChannel_m(nsRefPtr<nsIDOMDataChannel> aChannel, |
|
1156 nsRefPtr<PeerConnectionObserver> aObserver) |
|
1157 { |
|
1158 MOZ_ASSERT(NS_IsMainThread()); |
|
1159 JSErrorResult rv; |
|
1160 nsRefPtr<nsDOMDataChannel> channel = static_cast<nsDOMDataChannel*>(&*aChannel); |
|
1161 aObserver->NotifyDataChannel(*channel, rv); |
|
1162 NS_DataChannelAppReady(aChannel); |
|
1163 } |
|
1164 #endif |
|
1165 |
|
1166 void |
|
1167 PeerConnectionImpl::NotifyDataChannel(already_AddRefed<DataChannel> aChannel) |
|
1168 { |
|
1169 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1170 |
|
1171 // XXXkhuey this is completely fucked up. We can't use nsRefPtr<DataChannel> |
|
1172 // here because DataChannel's AddRef/Release are non-virtual and not visible |
|
1173 // if !MOZILLA_INTERNAL_API, but this function leaks the DataChannel if |
|
1174 // !MOZILLA_INTERNAL_API because it never transfers the ref to |
|
1175 // NS_NewDOMDataChannel. |
|
1176 DataChannel* channel = aChannel.take(); |
|
1177 MOZ_ASSERT(channel); |
|
1178 |
|
1179 CSFLogDebug(logTag, "%s: channel: %p", __FUNCTION__, channel); |
|
1180 |
|
1181 #ifdef MOZILLA_INTERNAL_API |
|
1182 nsCOMPtr<nsIDOMDataChannel> domchannel; |
|
1183 nsresult rv = NS_NewDOMDataChannel(already_AddRefed<DataChannel>(channel), |
|
1184 mWindow, getter_AddRefs(domchannel)); |
|
1185 NS_ENSURE_SUCCESS_VOID(rv); |
|
1186 |
|
1187 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); |
|
1188 if (!pco) { |
|
1189 return; |
|
1190 } |
|
1191 |
|
1192 RUN_ON_THREAD(mThread, |
|
1193 WrapRunnableNM(NotifyDataChannel_m, |
|
1194 domchannel.get(), |
|
1195 pco), |
|
1196 NS_DISPATCH_NORMAL); |
|
1197 #endif |
|
1198 } |
|
1199 |
|
1200 NS_IMETHODIMP |
|
1201 PeerConnectionImpl::CreateOffer(const MediaConstraintsInternal& aConstraints) |
|
1202 { |
|
1203 return CreateOffer(MediaConstraintsExternal (aConstraints)); |
|
1204 } |
|
1205 |
|
1206 // Used by unit tests and the IDL CreateOffer. |
|
1207 NS_IMETHODIMP |
|
1208 PeerConnectionImpl::CreateOffer(const MediaConstraintsExternal& aConstraints) |
|
1209 { |
|
1210 PC_AUTO_ENTER_API_CALL(true); |
|
1211 |
|
1212 Timecard *tc = mTimeCard; |
|
1213 mTimeCard = nullptr; |
|
1214 STAMP_TIMECARD(tc, "Create Offer"); |
|
1215 |
|
1216 cc_media_constraints_t* cc_constraints = aConstraints.build(); |
|
1217 NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED); |
|
1218 mInternal->mCall->createOffer(cc_constraints, tc); |
|
1219 return NS_OK; |
|
1220 } |
|
1221 |
|
1222 NS_IMETHODIMP |
|
1223 PeerConnectionImpl::CreateAnswer(const MediaConstraintsInternal& aConstraints) |
|
1224 { |
|
1225 return CreateAnswer(MediaConstraintsExternal (aConstraints)); |
|
1226 } |
|
1227 |
|
1228 NS_IMETHODIMP |
|
1229 PeerConnectionImpl::CreateAnswer(const MediaConstraintsExternal& aConstraints) |
|
1230 { |
|
1231 PC_AUTO_ENTER_API_CALL(true); |
|
1232 |
|
1233 Timecard *tc = mTimeCard; |
|
1234 mTimeCard = nullptr; |
|
1235 STAMP_TIMECARD(tc, "Create Answer"); |
|
1236 |
|
1237 cc_media_constraints_t* cc_constraints = aConstraints.build(); |
|
1238 NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED); |
|
1239 mInternal->mCall->createAnswer(cc_constraints, tc); |
|
1240 return NS_OK; |
|
1241 } |
|
1242 |
|
1243 NS_IMETHODIMP |
|
1244 PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP) |
|
1245 { |
|
1246 PC_AUTO_ENTER_API_CALL(true); |
|
1247 |
|
1248 if (!aSDP) { |
|
1249 CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__); |
|
1250 return NS_ERROR_FAILURE; |
|
1251 } |
|
1252 |
|
1253 Timecard *tc = mTimeCard; |
|
1254 mTimeCard = nullptr; |
|
1255 STAMP_TIMECARD(tc, "Set Local Description"); |
|
1256 |
|
1257 mLocalRequestedSDP = aSDP; |
|
1258 mInternal->mCall->setLocalDescription((cc_jsep_action_t)aAction, |
|
1259 mLocalRequestedSDP, tc); |
|
1260 return NS_OK; |
|
1261 } |
|
1262 |
|
1263 NS_IMETHODIMP |
|
1264 PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP) |
|
1265 { |
|
1266 PC_AUTO_ENTER_API_CALL(true); |
|
1267 |
|
1268 if (!aSDP) { |
|
1269 CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__); |
|
1270 return NS_ERROR_FAILURE; |
|
1271 } |
|
1272 |
|
1273 Timecard *tc = mTimeCard; |
|
1274 mTimeCard = nullptr; |
|
1275 STAMP_TIMECARD(tc, "Set Remote Description"); |
|
1276 |
|
1277 mRemoteRequestedSDP = aSDP; |
|
1278 mInternal->mCall->setRemoteDescription((cc_jsep_action_t)action, |
|
1279 mRemoteRequestedSDP, tc); |
|
1280 return NS_OK; |
|
1281 } |
|
1282 |
|
1283 // WebRTC uses highres time relative to the UNIX epoch (Jan 1, 1970, UTC). |
|
1284 |
|
1285 #ifdef MOZILLA_INTERNAL_API |
|
1286 nsresult |
|
1287 PeerConnectionImpl::GetTimeSinceEpoch(DOMHighResTimeStamp *result) { |
|
1288 MOZ_ASSERT(NS_IsMainThread()); |
|
1289 nsPerformance *perf = mWindow->GetPerformance(); |
|
1290 NS_ENSURE_TRUE(perf && perf->Timing(), NS_ERROR_UNEXPECTED); |
|
1291 *result = perf->Now() + perf->Timing()->NavigationStart(); |
|
1292 return NS_OK; |
|
1293 } |
|
1294 |
|
1295 class RTCStatsReportInternalConstruct : public RTCStatsReportInternal { |
|
1296 public: |
|
1297 RTCStatsReportInternalConstruct(const nsString &pcid, DOMHighResTimeStamp now) { |
|
1298 mPcid = pcid; |
|
1299 mInboundRTPStreamStats.Construct(); |
|
1300 mOutboundRTPStreamStats.Construct(); |
|
1301 mMediaStreamTrackStats.Construct(); |
|
1302 mMediaStreamStats.Construct(); |
|
1303 mTransportStats.Construct(); |
|
1304 mIceComponentStats.Construct(); |
|
1305 mIceCandidatePairStats.Construct(); |
|
1306 mIceCandidateStats.Construct(); |
|
1307 mCodecStats.Construct(); |
|
1308 } |
|
1309 }; |
|
1310 |
|
1311 // Specialized helper - push map[key] if specified or all map values onto array |
|
1312 |
|
1313 static void |
|
1314 PushBackSelect(nsTArray<RefPtr<MediaPipeline>>& aDst, |
|
1315 const std::map<TrackID, RefPtr<mozilla::MediaPipeline>> & aSrc, |
|
1316 TrackID aKey = 0) { |
|
1317 auto begin = aKey ? aSrc.find(aKey) : aSrc.begin(), it = begin; |
|
1318 for (auto end = (aKey && begin != aSrc.end())? ++begin : aSrc.end(); |
|
1319 it != end; ++it) { |
|
1320 aDst.AppendElement(it->second); |
|
1321 } |
|
1322 } |
|
1323 #endif |
|
1324 |
|
1325 NS_IMETHODIMP |
|
1326 PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector) { |
|
1327 PC_AUTO_ENTER_API_CALL(true); |
|
1328 |
|
1329 #ifdef MOZILLA_INTERNAL_API |
|
1330 if (!mMedia) { |
|
1331 // Since we zero this out before the d'tor, we should check. |
|
1332 return NS_ERROR_UNEXPECTED; |
|
1333 } |
|
1334 |
|
1335 nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(false)); |
|
1336 |
|
1337 nsresult rv = BuildStatsQuery_m(aSelector, query.get()); |
|
1338 |
|
1339 NS_ENSURE_SUCCESS(rv, rv); |
|
1340 |
|
1341 RUN_ON_THREAD(mSTSThread, |
|
1342 WrapRunnableNM(&PeerConnectionImpl::GetStatsForPCObserver_s, |
|
1343 mHandle, |
|
1344 query), |
|
1345 NS_DISPATCH_NORMAL); |
|
1346 #endif |
|
1347 return NS_OK; |
|
1348 } |
|
1349 |
|
1350 NS_IMETHODIMP |
|
1351 PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel) { |
|
1352 PC_AUTO_ENTER_API_CALL(true); |
|
1353 |
|
1354 Timecard *tc = mTimeCard; |
|
1355 mTimeCard = nullptr; |
|
1356 STAMP_TIMECARD(tc, "Add Ice Candidate"); |
|
1357 |
|
1358 #ifdef MOZILLA_INTERNAL_API |
|
1359 // When remote candidates are added before our ICE ctx is up and running |
|
1360 // (the transition to New is async through STS, so this is not impossible), |
|
1361 // we won't record them as trickle candidates. Is this what we want? |
|
1362 if(!mIceStartTime.IsNull()) { |
|
1363 TimeDuration timeDelta = TimeStamp::Now() - mIceStartTime; |
|
1364 if (mIceConnectionState == PCImplIceConnectionState::Failed) { |
|
1365 Telemetry::Accumulate(Telemetry::WEBRTC_ICE_LATE_TRICKLE_ARRIVAL_TIME, |
|
1366 timeDelta.ToMilliseconds()); |
|
1367 } else { |
|
1368 Telemetry::Accumulate(Telemetry::WEBRTC_ICE_ON_TIME_TRICKLE_ARRIVAL_TIME, |
|
1369 timeDelta.ToMilliseconds()); |
|
1370 } |
|
1371 } |
|
1372 #endif |
|
1373 |
|
1374 mInternal->mCall->addICECandidate(aCandidate, aMid, aLevel, tc); |
|
1375 return NS_OK; |
|
1376 } |
|
1377 |
|
1378 NS_IMETHODIMP |
|
1379 PeerConnectionImpl::CloseStreams() { |
|
1380 PC_AUTO_ENTER_API_CALL(false); |
|
1381 |
|
1382 if (mReadyState != PCImplReadyState::Closed) { |
|
1383 ChangeReadyState(PCImplReadyState::Closing); |
|
1384 } |
|
1385 |
|
1386 CSFLogInfo(logTag, "%s: Ending associated call", __FUNCTION__); |
|
1387 |
|
1388 mInternal->mCall->endCall(); |
|
1389 return NS_OK; |
|
1390 } |
|
1391 |
|
1392 NS_IMETHODIMP |
|
1393 PeerConnectionImpl::AddStream(DOMMediaStream &aMediaStream, |
|
1394 const MediaConstraintsInternal& aConstraints) |
|
1395 { |
|
1396 return AddStream(aMediaStream, MediaConstraintsExternal(aConstraints)); |
|
1397 } |
|
1398 |
|
1399 NS_IMETHODIMP |
|
1400 PeerConnectionImpl::AddStream(DOMMediaStream& aMediaStream, |
|
1401 const MediaConstraintsExternal& aConstraints) { |
|
1402 PC_AUTO_ENTER_API_CALL(true); |
|
1403 |
|
1404 uint32_t hints = aMediaStream.GetHintContents(); |
|
1405 |
|
1406 // XXX Remove this check once addStream has an error callback |
|
1407 // available and/or we have plumbing to handle multiple |
|
1408 // local audio streams. |
|
1409 if ((hints & DOMMediaStream::HINT_CONTENTS_AUDIO) && |
|
1410 mNumAudioStreams > 0) { |
|
1411 CSFLogError(logTag, "%s: Only one local audio stream is supported for now", |
|
1412 __FUNCTION__); |
|
1413 return NS_ERROR_FAILURE; |
|
1414 } |
|
1415 |
|
1416 // XXX Remove this check once addStream has an error callback |
|
1417 // available and/or we have plumbing to handle multiple |
|
1418 // local video streams. |
|
1419 if ((hints & DOMMediaStream::HINT_CONTENTS_VIDEO) && |
|
1420 mNumVideoStreams > 0) { |
|
1421 CSFLogError(logTag, "%s: Only one local video stream is supported for now", |
|
1422 __FUNCTION__); |
|
1423 return NS_ERROR_FAILURE; |
|
1424 } |
|
1425 |
|
1426 uint32_t stream_id; |
|
1427 nsresult res = mMedia->AddStream(&aMediaStream, &stream_id); |
|
1428 if (NS_FAILED(res)) |
|
1429 return res; |
|
1430 |
|
1431 // TODO(ekr@rtfm.com): these integers should be the track IDs |
|
1432 if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { |
|
1433 cc_media_constraints_t* cc_constraints = aConstraints.build(); |
|
1434 NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED); |
|
1435 mInternal->mCall->addStream(stream_id, 0, AUDIO, cc_constraints); |
|
1436 mNumAudioStreams++; |
|
1437 } |
|
1438 |
|
1439 if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { |
|
1440 cc_media_constraints_t* cc_constraints = aConstraints.build(); |
|
1441 NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED); |
|
1442 mInternal->mCall->addStream(stream_id, 1, VIDEO, cc_constraints); |
|
1443 mNumVideoStreams++; |
|
1444 } |
|
1445 |
|
1446 return NS_OK; |
|
1447 } |
|
1448 |
|
1449 NS_IMETHODIMP |
|
1450 PeerConnectionImpl::RemoveStream(DOMMediaStream& aMediaStream) { |
|
1451 PC_AUTO_ENTER_API_CALL(true); |
|
1452 |
|
1453 uint32_t stream_id; |
|
1454 nsresult res = mMedia->RemoveStream(&aMediaStream, &stream_id); |
|
1455 |
|
1456 if (NS_FAILED(res)) |
|
1457 return res; |
|
1458 |
|
1459 uint32_t hints = aMediaStream.GetHintContents(); |
|
1460 |
|
1461 if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { |
|
1462 mInternal->mCall->removeStream(stream_id, 0, AUDIO); |
|
1463 MOZ_ASSERT(mNumAudioStreams > 0); |
|
1464 mNumAudioStreams--; |
|
1465 } |
|
1466 |
|
1467 if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { |
|
1468 mInternal->mCall->removeStream(stream_id, 1, VIDEO); |
|
1469 MOZ_ASSERT(mNumVideoStreams > 0); |
|
1470 mNumVideoStreams--; |
|
1471 } |
|
1472 |
|
1473 return NS_OK; |
|
1474 } |
|
1475 |
|
1476 /* |
|
1477 NS_IMETHODIMP |
|
1478 PeerConnectionImpl::SetRemoteFingerprint(const char* hash, const char* fingerprint) |
|
1479 { |
|
1480 MOZ_ASSERT(hash); |
|
1481 MOZ_ASSERT(fingerprint); |
|
1482 |
|
1483 if (fingerprint != nullptr && (strcmp(hash, "sha-1") == 0)) { |
|
1484 mRemoteFingerprint = std::string(fingerprint); |
|
1485 CSFLogDebug(logTag, "Setting remote fingerprint to %s", mRemoteFingerprint.c_str()); |
|
1486 return NS_OK; |
|
1487 } else { |
|
1488 CSFLogError(logTag, "%s: Invalid Remote Finger Print", __FUNCTION__); |
|
1489 return NS_ERROR_FAILURE; |
|
1490 } |
|
1491 } |
|
1492 */ |
|
1493 |
|
1494 NS_IMETHODIMP |
|
1495 PeerConnectionImpl::GetFingerprint(char** fingerprint) |
|
1496 { |
|
1497 MOZ_ASSERT(fingerprint); |
|
1498 |
|
1499 if (!mIdentity) { |
|
1500 return NS_ERROR_FAILURE; |
|
1501 } |
|
1502 |
|
1503 char* tmp = new char[mFingerprint.size() + 1]; |
|
1504 std::copy(mFingerprint.begin(), mFingerprint.end(), tmp); |
|
1505 tmp[mFingerprint.size()] = '\0'; |
|
1506 |
|
1507 *fingerprint = tmp; |
|
1508 return NS_OK; |
|
1509 } |
|
1510 |
|
1511 NS_IMETHODIMP |
|
1512 PeerConnectionImpl::GetLocalDescription(char** aSDP) |
|
1513 { |
|
1514 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1515 MOZ_ASSERT(aSDP); |
|
1516 |
|
1517 char* tmp = new char[mLocalSDP.size() + 1]; |
|
1518 std::copy(mLocalSDP.begin(), mLocalSDP.end(), tmp); |
|
1519 tmp[mLocalSDP.size()] = '\0'; |
|
1520 |
|
1521 *aSDP = tmp; |
|
1522 return NS_OK; |
|
1523 } |
|
1524 |
|
1525 NS_IMETHODIMP |
|
1526 PeerConnectionImpl::GetRemoteDescription(char** aSDP) |
|
1527 { |
|
1528 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1529 MOZ_ASSERT(aSDP); |
|
1530 |
|
1531 char* tmp = new char[mRemoteSDP.size() + 1]; |
|
1532 std::copy(mRemoteSDP.begin(), mRemoteSDP.end(), tmp); |
|
1533 tmp[mRemoteSDP.size()] = '\0'; |
|
1534 |
|
1535 *aSDP = tmp; |
|
1536 return NS_OK; |
|
1537 } |
|
1538 |
|
1539 NS_IMETHODIMP |
|
1540 PeerConnectionImpl::ReadyState(PCImplReadyState* aState) |
|
1541 { |
|
1542 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1543 MOZ_ASSERT(aState); |
|
1544 |
|
1545 *aState = mReadyState; |
|
1546 return NS_OK; |
|
1547 } |
|
1548 |
|
1549 NS_IMETHODIMP |
|
1550 PeerConnectionImpl::SignalingState(PCImplSignalingState* aState) |
|
1551 { |
|
1552 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1553 MOZ_ASSERT(aState); |
|
1554 |
|
1555 *aState = mSignalingState; |
|
1556 return NS_OK; |
|
1557 } |
|
1558 |
|
1559 NS_IMETHODIMP |
|
1560 PeerConnectionImpl::SipccState(PCImplSipccState* aState) |
|
1561 { |
|
1562 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1563 MOZ_ASSERT(aState); |
|
1564 |
|
1565 PeerConnectionCtx* pcctx = PeerConnectionCtx::GetInstance(); |
|
1566 // Avoid B2G error: operands to ?: have different types |
|
1567 // 'mozilla::dom::PCImplSipccState' and 'mozilla::dom::PCImplSipccState::Enum' |
|
1568 if (pcctx) { |
|
1569 *aState = pcctx->sipcc_state(); |
|
1570 } else { |
|
1571 *aState = PCImplSipccState::Idle; |
|
1572 } |
|
1573 return NS_OK; |
|
1574 } |
|
1575 |
|
1576 NS_IMETHODIMP |
|
1577 PeerConnectionImpl::IceConnectionState(PCImplIceConnectionState* aState) |
|
1578 { |
|
1579 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1580 MOZ_ASSERT(aState); |
|
1581 |
|
1582 *aState = mIceConnectionState; |
|
1583 return NS_OK; |
|
1584 } |
|
1585 |
|
1586 NS_IMETHODIMP |
|
1587 PeerConnectionImpl::IceGatheringState(PCImplIceGatheringState* aState) |
|
1588 { |
|
1589 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1590 MOZ_ASSERT(aState); |
|
1591 |
|
1592 *aState = mIceGatheringState; |
|
1593 return NS_OK; |
|
1594 } |
|
1595 |
|
1596 nsresult |
|
1597 PeerConnectionImpl::CheckApiState(bool assert_ice_ready) const |
|
1598 { |
|
1599 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1600 MOZ_ASSERT(mTrickle || !assert_ice_ready || |
|
1601 (mIceGatheringState == PCImplIceGatheringState::Complete)); |
|
1602 |
|
1603 if (mReadyState == PCImplReadyState::Closed) { |
|
1604 CSFLogError(logTag, "%s: called API while closed", __FUNCTION__); |
|
1605 return NS_ERROR_FAILURE; |
|
1606 } |
|
1607 if (!mMedia) { |
|
1608 CSFLogError(logTag, "%s: called API with disposed mMedia", __FUNCTION__); |
|
1609 return NS_ERROR_FAILURE; |
|
1610 } |
|
1611 return NS_OK; |
|
1612 } |
|
1613 |
|
1614 NS_IMETHODIMP |
|
1615 PeerConnectionImpl::Close() |
|
1616 { |
|
1617 CSFLogDebug(logTag, "%s: for %s", __FUNCTION__, mHandle.c_str()); |
|
1618 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1619 |
|
1620 nsresult res = CloseInt(); |
|
1621 |
|
1622 SetSignalingState_m(PCImplSignalingState::SignalingClosed); |
|
1623 |
|
1624 return res; |
|
1625 } |
|
1626 |
|
1627 |
|
1628 nsresult |
|
1629 PeerConnectionImpl::CloseInt() |
|
1630 { |
|
1631 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1632 |
|
1633 // We do this at the end of the call because we want to make sure we've waited |
|
1634 // for all trickle ICE candidates to come in; this can happen well after we've |
|
1635 // transitioned to connected. As a bonus, this allows us to detect race |
|
1636 // conditions where a stats dispatch happens right as the PC closes. |
|
1637 if (!IsClosed()) { |
|
1638 RecordLongtermICEStatistics(); |
|
1639 } |
|
1640 |
|
1641 if (mInternal->mCall) { |
|
1642 CSFLogInfo(logTag, "%s: Closing PeerConnectionImpl %s; " |
|
1643 "ending call", __FUNCTION__, mHandle.c_str()); |
|
1644 mInternal->mCall->endCall(); |
|
1645 } |
|
1646 #ifdef MOZILLA_INTERNAL_API |
|
1647 if (mDataConnection) { |
|
1648 CSFLogInfo(logTag, "%s: Destroying DataChannelConnection %p for %s", |
|
1649 __FUNCTION__, (void *) mDataConnection.get(), mHandle.c_str()); |
|
1650 mDataConnection->Destroy(); |
|
1651 mDataConnection = nullptr; // it may not go away until the runnables are dead |
|
1652 } |
|
1653 #endif |
|
1654 |
|
1655 ShutdownMedia(); |
|
1656 |
|
1657 // DataConnection will need to stay alive until all threads/runnables exit |
|
1658 |
|
1659 return NS_OK; |
|
1660 } |
|
1661 |
|
1662 void |
|
1663 PeerConnectionImpl::ShutdownMedia() |
|
1664 { |
|
1665 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1666 |
|
1667 if (!mMedia) |
|
1668 return; |
|
1669 |
|
1670 #ifdef MOZILLA_INTERNAL_API |
|
1671 // End of call to be recorded in Telemetry |
|
1672 if (!mStartTime.IsNull()){ |
|
1673 TimeDuration timeDelta = TimeStamp::Now() - mStartTime; |
|
1674 Telemetry::Accumulate(Telemetry::WEBRTC_CALL_DURATION, timeDelta.ToSeconds()); |
|
1675 } |
|
1676 #endif |
|
1677 |
|
1678 // Forget the reference so that we can transfer it to |
|
1679 // SelfDestruct(). |
|
1680 mMedia.forget().take()->SelfDestruct(); |
|
1681 } |
|
1682 |
|
1683 #ifdef MOZILLA_INTERNAL_API |
|
1684 // If NSS is shutting down, then we need to get rid of the DTLS |
|
1685 // identity right now; otherwise, we'll cause wreckage when we do |
|
1686 // finally deallocate it in our destructor. |
|
1687 void |
|
1688 PeerConnectionImpl::virtualDestroyNSSReference() |
|
1689 { |
|
1690 destructorSafeDestroyNSSReference(); |
|
1691 } |
|
1692 |
|
1693 void |
|
1694 PeerConnectionImpl::destructorSafeDestroyNSSReference() |
|
1695 { |
|
1696 MOZ_ASSERT(NS_IsMainThread()); |
|
1697 CSFLogDebug(logTag, "%s: NSS shutting down; freeing our DtlsIdentity.", __FUNCTION__); |
|
1698 mIdentity = nullptr; |
|
1699 } |
|
1700 #endif |
|
1701 |
|
1702 void |
|
1703 PeerConnectionImpl::onCallEvent(const OnCallEventArgs& args) |
|
1704 { |
|
1705 const ccapi_call_event_e &aCallEvent = args.mCallEvent; |
|
1706 const CSF::CC_CallInfoPtr &aInfo = args.mInfo; |
|
1707 |
|
1708 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1709 MOZ_ASSERT(aInfo.get()); |
|
1710 |
|
1711 cc_call_state_t event = aInfo->getCallState(); |
|
1712 std::string statestr = aInfo->callStateToString(event); |
|
1713 Timecard *timecard = aInfo->takeTimecard(); |
|
1714 |
|
1715 if (timecard) { |
|
1716 mTimeCard = timecard; |
|
1717 STAMP_TIMECARD(mTimeCard, "Operation Completed"); |
|
1718 } |
|
1719 |
|
1720 if (CCAPI_CALL_EV_CREATED != aCallEvent && CCAPI_CALL_EV_STATE != aCallEvent) { |
|
1721 CSFLogDebug(logTag, "%s: **** CALL HANDLE IS: %s, **** CALL STATE IS: %s", |
|
1722 __FUNCTION__, mHandle.c_str(), statestr.c_str()); |
|
1723 return; |
|
1724 } |
|
1725 |
|
1726 switch (event) { |
|
1727 case SETLOCALDESCSUCCESS: |
|
1728 case UPDATELOCALDESC: |
|
1729 mLocalSDP = aInfo->getSDP(); |
|
1730 break; |
|
1731 |
|
1732 case SETREMOTEDESCSUCCESS: |
|
1733 case ADDICECANDIDATE: |
|
1734 mRemoteSDP = aInfo->getSDP(); |
|
1735 break; |
|
1736 |
|
1737 case CONNECTED: |
|
1738 CSFLogDebug(logTag, "Setting PeerConnnection state to kActive"); |
|
1739 ChangeReadyState(PCImplReadyState::Active); |
|
1740 break; |
|
1741 default: |
|
1742 break; |
|
1743 } |
|
1744 |
|
1745 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); |
|
1746 if (!pco) { |
|
1747 return; |
|
1748 } |
|
1749 |
|
1750 PeerConnectionObserverDispatch* runnable = |
|
1751 new PeerConnectionObserverDispatch(aInfo, this, pco); |
|
1752 |
|
1753 if (mThread) { |
|
1754 mThread->Dispatch(runnable, NS_DISPATCH_NORMAL); |
|
1755 return; |
|
1756 } |
|
1757 runnable->Run(); |
|
1758 delete runnable; |
|
1759 } |
|
1760 |
|
1761 void |
|
1762 PeerConnectionImpl::ChangeReadyState(PCImplReadyState aReadyState) |
|
1763 { |
|
1764 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1765 mReadyState = aReadyState; |
|
1766 |
|
1767 // Note that we are passing an nsRefPtr which keeps the observer live. |
|
1768 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); |
|
1769 if (!pco) { |
|
1770 return; |
|
1771 } |
|
1772 WrappableJSErrorResult rv; |
|
1773 RUN_ON_THREAD(mThread, |
|
1774 WrapRunnable(pco, |
|
1775 &PeerConnectionObserver::OnStateChange, |
|
1776 PCObserverStateType::ReadyState, |
|
1777 rv, static_cast<JSCompartment*>(nullptr)), |
|
1778 NS_DISPATCH_NORMAL); |
|
1779 } |
|
1780 |
|
1781 void |
|
1782 PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState) |
|
1783 { |
|
1784 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1785 if (mSignalingState == aSignalingState || |
|
1786 mSignalingState == PCImplSignalingState::SignalingClosed) { |
|
1787 return; |
|
1788 } |
|
1789 |
|
1790 mSignalingState = aSignalingState; |
|
1791 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); |
|
1792 if (!pco) { |
|
1793 return; |
|
1794 } |
|
1795 JSErrorResult rv; |
|
1796 pco->OnStateChange(PCObserverStateType::SignalingState, rv); |
|
1797 MOZ_ASSERT(!rv.Failed()); |
|
1798 } |
|
1799 |
|
1800 bool |
|
1801 PeerConnectionImpl::IsClosed() const |
|
1802 { |
|
1803 return mSignalingState == PCImplSignalingState::SignalingClosed; |
|
1804 } |
|
1805 |
|
1806 bool |
|
1807 PeerConnectionImpl::HasMedia() const |
|
1808 { |
|
1809 return mMedia; |
|
1810 } |
|
1811 |
|
1812 PeerConnectionWrapper::PeerConnectionWrapper(const std::string& handle) |
|
1813 : impl_(nullptr) { |
|
1814 if (PeerConnectionCtx::GetInstance()->mPeerConnections.find(handle) == |
|
1815 PeerConnectionCtx::GetInstance()->mPeerConnections.end()) { |
|
1816 return; |
|
1817 } |
|
1818 |
|
1819 PeerConnectionImpl *impl = PeerConnectionCtx::GetInstance()->mPeerConnections[handle]; |
|
1820 |
|
1821 if (!impl->media()) |
|
1822 return; |
|
1823 |
|
1824 impl_ = impl; |
|
1825 } |
|
1826 |
|
1827 const std::string& |
|
1828 PeerConnectionImpl::GetHandle() |
|
1829 { |
|
1830 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1831 return mHandle; |
|
1832 } |
|
1833 |
|
1834 const std::string& |
|
1835 PeerConnectionImpl::GetName() |
|
1836 { |
|
1837 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
1838 return mName; |
|
1839 } |
|
1840 |
|
1841 static mozilla::dom::PCImplIceConnectionState |
|
1842 toDomIceConnectionState(NrIceCtx::ConnectionState state) { |
|
1843 switch (state) { |
|
1844 case NrIceCtx::ICE_CTX_INIT: |
|
1845 return PCImplIceConnectionState::New; |
|
1846 case NrIceCtx::ICE_CTX_CHECKING: |
|
1847 return PCImplIceConnectionState::Checking; |
|
1848 case NrIceCtx::ICE_CTX_OPEN: |
|
1849 return PCImplIceConnectionState::Connected; |
|
1850 case NrIceCtx::ICE_CTX_FAILED: |
|
1851 return PCImplIceConnectionState::Failed; |
|
1852 } |
|
1853 MOZ_CRASH(); |
|
1854 } |
|
1855 |
|
1856 static mozilla::dom::PCImplIceGatheringState |
|
1857 toDomIceGatheringState(NrIceCtx::GatheringState state) { |
|
1858 switch (state) { |
|
1859 case NrIceCtx::ICE_CTX_GATHER_INIT: |
|
1860 return PCImplIceGatheringState::New; |
|
1861 case NrIceCtx::ICE_CTX_GATHER_STARTED: |
|
1862 return PCImplIceGatheringState::Gathering; |
|
1863 case NrIceCtx::ICE_CTX_GATHER_COMPLETE: |
|
1864 return PCImplIceGatheringState::Complete; |
|
1865 } |
|
1866 MOZ_CRASH(); |
|
1867 } |
|
1868 |
|
1869 #ifdef MOZILLA_INTERNAL_API |
|
1870 static bool isDone(PCImplIceConnectionState state) { |
|
1871 return state != PCImplIceConnectionState::Checking && |
|
1872 state != PCImplIceConnectionState::New; |
|
1873 } |
|
1874 |
|
1875 static bool isSucceeded(PCImplIceConnectionState state) { |
|
1876 return state == PCImplIceConnectionState::Connected || |
|
1877 state == PCImplIceConnectionState::Completed; |
|
1878 } |
|
1879 |
|
1880 static bool isFailed(PCImplIceConnectionState state) { |
|
1881 return state == PCImplIceConnectionState::Failed || |
|
1882 state == PCImplIceConnectionState::Disconnected; |
|
1883 } |
|
1884 #endif |
|
1885 |
|
1886 void PeerConnectionImpl::IceConnectionStateChange( |
|
1887 NrIceCtx* ctx, |
|
1888 NrIceCtx::ConnectionState state) { |
|
1889 PC_AUTO_ENTER_API_CALL_VOID_RETURN(false); |
|
1890 |
|
1891 CSFLogDebug(logTag, "%s", __FUNCTION__); |
|
1892 |
|
1893 auto domState = toDomIceConnectionState(state); |
|
1894 |
|
1895 #ifdef MOZILLA_INTERNAL_API |
|
1896 if (!isDone(mIceConnectionState) && isDone(domState)) { |
|
1897 // mIceStartTime can be null if going directly from New to Closed, in which |
|
1898 // case we don't count it as a success or a failure. |
|
1899 if (!mIceStartTime.IsNull()){ |
|
1900 TimeDuration timeDelta = TimeStamp::Now() - mIceStartTime; |
|
1901 if (isSucceeded(domState)) { |
|
1902 Telemetry::Accumulate(Telemetry::WEBRTC_ICE_SUCCESS_TIME, |
|
1903 timeDelta.ToMilliseconds()); |
|
1904 } else if (isFailed(domState)) { |
|
1905 Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FAILURE_TIME, |
|
1906 timeDelta.ToMilliseconds()); |
|
1907 } |
|
1908 } |
|
1909 } |
|
1910 #endif |
|
1911 |
|
1912 mIceConnectionState = domState; |
|
1913 |
|
1914 // Would be nice if we had a means of converting one of these dom enums |
|
1915 // to a string that wasn't almost as much text as this switch statement... |
|
1916 switch (mIceConnectionState) { |
|
1917 case PCImplIceConnectionState::New: |
|
1918 STAMP_TIMECARD(mTimeCard, "Ice state: new"); |
|
1919 break; |
|
1920 case PCImplIceConnectionState::Checking: |
|
1921 #ifdef MOZILLA_INTERNAL_API |
|
1922 // For telemetry |
|
1923 mIceStartTime = TimeStamp::Now(); |
|
1924 #endif |
|
1925 STAMP_TIMECARD(mTimeCard, "Ice state: checking"); |
|
1926 break; |
|
1927 case PCImplIceConnectionState::Connected: |
|
1928 STAMP_TIMECARD(mTimeCard, "Ice state: connected"); |
|
1929 break; |
|
1930 case PCImplIceConnectionState::Completed: |
|
1931 STAMP_TIMECARD(mTimeCard, "Ice state: completed"); |
|
1932 break; |
|
1933 case PCImplIceConnectionState::Failed: |
|
1934 STAMP_TIMECARD(mTimeCard, "Ice state: failed"); |
|
1935 break; |
|
1936 case PCImplIceConnectionState::Disconnected: |
|
1937 STAMP_TIMECARD(mTimeCard, "Ice state: disconnected"); |
|
1938 break; |
|
1939 case PCImplIceConnectionState::Closed: |
|
1940 STAMP_TIMECARD(mTimeCard, "Ice state: closed"); |
|
1941 break; |
|
1942 } |
|
1943 |
|
1944 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); |
|
1945 if (!pco) { |
|
1946 return; |
|
1947 } |
|
1948 WrappableJSErrorResult rv; |
|
1949 RUN_ON_THREAD(mThread, |
|
1950 WrapRunnable(pco, |
|
1951 &PeerConnectionObserver::OnStateChange, |
|
1952 PCObserverStateType::IceConnectionState, |
|
1953 rv, static_cast<JSCompartment*>(nullptr)), |
|
1954 NS_DISPATCH_NORMAL); |
|
1955 } |
|
1956 |
|
1957 void |
|
1958 PeerConnectionImpl::IceGatheringStateChange( |
|
1959 NrIceCtx* ctx, |
|
1960 NrIceCtx::GatheringState state) |
|
1961 { |
|
1962 PC_AUTO_ENTER_API_CALL_VOID_RETURN(false); |
|
1963 |
|
1964 CSFLogDebug(logTag, "%s", __FUNCTION__); |
|
1965 |
|
1966 mIceGatheringState = toDomIceGatheringState(state); |
|
1967 |
|
1968 // Would be nice if we had a means of converting one of these dom enums |
|
1969 // to a string that wasn't almost as much text as this switch statement... |
|
1970 switch (mIceGatheringState) { |
|
1971 case PCImplIceGatheringState::New: |
|
1972 STAMP_TIMECARD(mTimeCard, "Ice gathering state: new"); |
|
1973 break; |
|
1974 case PCImplIceGatheringState::Gathering: |
|
1975 STAMP_TIMECARD(mTimeCard, "Ice gathering state: gathering"); |
|
1976 break; |
|
1977 case PCImplIceGatheringState::Complete: |
|
1978 STAMP_TIMECARD(mTimeCard, "Ice state: complete"); |
|
1979 break; |
|
1980 } |
|
1981 |
|
1982 nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); |
|
1983 if (!pco) { |
|
1984 return; |
|
1985 } |
|
1986 WrappableJSErrorResult rv; |
|
1987 RUN_ON_THREAD(mThread, |
|
1988 WrapRunnable(pco, |
|
1989 &PeerConnectionObserver::OnStateChange, |
|
1990 PCObserverStateType::IceGatheringState, |
|
1991 rv, static_cast<JSCompartment*>(nullptr)), |
|
1992 NS_DISPATCH_NORMAL); |
|
1993 } |
|
1994 |
|
1995 #ifdef MOZILLA_INTERNAL_API |
|
1996 nsresult |
|
1997 PeerConnectionImpl::BuildStatsQuery_m( |
|
1998 mozilla::dom::MediaStreamTrack *aSelector, |
|
1999 RTCStatsQuery *query) { |
|
2000 |
|
2001 if (!HasMedia()) { |
|
2002 return NS_OK; |
|
2003 } |
|
2004 |
|
2005 if (!mMedia->ice_ctx() || !mThread) { |
|
2006 CSFLogError(logTag, "Could not build stats query, critical components of " |
|
2007 "PeerConnectionImpl not set."); |
|
2008 return NS_ERROR_UNEXPECTED; |
|
2009 } |
|
2010 |
|
2011 nsresult rv = GetTimeSinceEpoch(&(query->now)); |
|
2012 |
|
2013 if (NS_FAILED(rv)) { |
|
2014 CSFLogError(logTag, "Could not build stats query, could not get timestamp"); |
|
2015 return rv; |
|
2016 } |
|
2017 |
|
2018 // We do not use the pcHandle here, since that's risky to expose to content. |
|
2019 query->report = RTCStatsReportInternalConstruct( |
|
2020 NS_ConvertASCIItoUTF16(mName.c_str()), |
|
2021 query->now); |
|
2022 |
|
2023 // Gather up pipelines from mMedia so they may be inspected on STS |
|
2024 TrackID trackId = aSelector ? aSelector->GetTrackID() : 0; |
|
2025 |
|
2026 for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) { |
|
2027 PushBackSelect(query->pipelines, |
|
2028 mMedia->GetLocalStream(i)->GetPipelines(), |
|
2029 trackId); |
|
2030 } |
|
2031 |
|
2032 for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) { |
|
2033 PushBackSelect(query->pipelines, |
|
2034 mMedia->GetRemoteStream(i)->GetPipelines(), |
|
2035 trackId); |
|
2036 } |
|
2037 |
|
2038 query->iceCtx = mMedia->ice_ctx(); |
|
2039 |
|
2040 // From the list of MediaPipelines, determine the set of NrIceMediaStreams |
|
2041 // we are interested in. |
|
2042 std::set<size_t> levelsToGrab; |
|
2043 if (trackId) { |
|
2044 for (size_t p = 0; p < query->pipelines.Length(); ++p) { |
|
2045 size_t level = query->pipelines[p]->level(); |
|
2046 MOZ_ASSERT(level); |
|
2047 levelsToGrab.insert(level); |
|
2048 } |
|
2049 } else { |
|
2050 // We want to grab all streams, so ignore the pipelines (this also ends up |
|
2051 // grabbing DataChannel streams, which is what we want) |
|
2052 for (size_t s = 0; s < mMedia->num_ice_media_streams(); ++s) { |
|
2053 levelsToGrab.insert(s + 1); // mIceStreams is 0-indexed |
|
2054 } |
|
2055 } |
|
2056 |
|
2057 for (auto s = levelsToGrab.begin(); s != levelsToGrab.end(); ++s) { |
|
2058 // TODO(bcampen@mozilla.com): I may need to revisit this for bundle. |
|
2059 // (Bug 786234) |
|
2060 RefPtr<NrIceMediaStream> temp(mMedia->ice_media_stream(*s - 1)); |
|
2061 RefPtr<TransportFlow> flow(mMedia->GetTransportFlow(*s, false)); |
|
2062 // flow can be null for unused levels, such as unused DataChannels |
|
2063 if (temp && flow) { |
|
2064 query->streams.AppendElement(temp); |
|
2065 } |
|
2066 } |
|
2067 |
|
2068 return rv; |
|
2069 } |
|
2070 |
|
2071 static void ToRTCIceCandidateStats( |
|
2072 const std::vector<NrIceCandidate>& candidates, |
|
2073 RTCStatsType candidateType, |
|
2074 const nsString& componentId, |
|
2075 DOMHighResTimeStamp now, |
|
2076 RTCStatsReportInternal* report) { |
|
2077 |
|
2078 MOZ_ASSERT(report); |
|
2079 for (auto c = candidates.begin(); c != candidates.end(); ++c) { |
|
2080 RTCIceCandidateStats cand; |
|
2081 cand.mType.Construct(candidateType); |
|
2082 NS_ConvertASCIItoUTF16 codeword(c->codeword.c_str()); |
|
2083 cand.mComponentId.Construct(componentId); |
|
2084 cand.mId.Construct(codeword); |
|
2085 cand.mTimestamp.Construct(now); |
|
2086 cand.mCandidateType.Construct( |
|
2087 RTCStatsIceCandidateType(c->type)); |
|
2088 cand.mIpAddress.Construct( |
|
2089 NS_ConvertASCIItoUTF16(c->cand_addr.host.c_str())); |
|
2090 cand.mPortNumber.Construct(c->cand_addr.port); |
|
2091 cand.mTransport.Construct( |
|
2092 NS_ConvertASCIItoUTF16(c->cand_addr.transport.c_str())); |
|
2093 if (candidateType == RTCStatsType::Localcandidate) { |
|
2094 cand.mMozLocalTransport.Construct( |
|
2095 NS_ConvertASCIItoUTF16(c->local_addr.transport.c_str())); |
|
2096 } |
|
2097 report->mIceCandidateStats.Value().AppendElement(cand); |
|
2098 } |
|
2099 } |
|
2100 |
|
2101 static void RecordIceStats_s( |
|
2102 NrIceMediaStream& mediaStream, |
|
2103 bool internalStats, |
|
2104 DOMHighResTimeStamp now, |
|
2105 RTCStatsReportInternal* report) { |
|
2106 |
|
2107 NS_ConvertASCIItoUTF16 componentId(mediaStream.name().c_str()); |
|
2108 if (internalStats) { |
|
2109 std::vector<NrIceCandidatePair> candPairs; |
|
2110 nsresult res = mediaStream.GetCandidatePairs(&candPairs); |
|
2111 if (NS_FAILED(res)) { |
|
2112 CSFLogError(logTag, "%s: Error getting candidate pairs", __FUNCTION__); |
|
2113 return; |
|
2114 } |
|
2115 |
|
2116 for (auto p = candPairs.begin(); p != candPairs.end(); ++p) { |
|
2117 NS_ConvertASCIItoUTF16 codeword(p->codeword.c_str()); |
|
2118 NS_ConvertASCIItoUTF16 localCodeword(p->local.codeword.c_str()); |
|
2119 NS_ConvertASCIItoUTF16 remoteCodeword(p->remote.codeword.c_str()); |
|
2120 // Only expose candidate-pair statistics to chrome, until we've thought |
|
2121 // through the implications of exposing it to content. |
|
2122 |
|
2123 RTCIceCandidatePairStats s; |
|
2124 s.mId.Construct(codeword); |
|
2125 s.mComponentId.Construct(componentId); |
|
2126 s.mTimestamp.Construct(now); |
|
2127 s.mType.Construct(RTCStatsType::Candidatepair); |
|
2128 s.mLocalCandidateId.Construct(localCodeword); |
|
2129 s.mRemoteCandidateId.Construct(remoteCodeword); |
|
2130 s.mNominated.Construct(p->nominated); |
|
2131 s.mMozPriority.Construct(p->priority); |
|
2132 s.mSelected.Construct(p->selected); |
|
2133 s.mState.Construct(RTCStatsIceCandidatePairState(p->state)); |
|
2134 report->mIceCandidatePairStats.Value().AppendElement(s); |
|
2135 } |
|
2136 } |
|
2137 |
|
2138 std::vector<NrIceCandidate> candidates; |
|
2139 if (NS_SUCCEEDED(mediaStream.GetLocalCandidates(&candidates))) { |
|
2140 ToRTCIceCandidateStats(candidates, |
|
2141 RTCStatsType::Localcandidate, |
|
2142 componentId, |
|
2143 now, |
|
2144 report); |
|
2145 } |
|
2146 candidates.clear(); |
|
2147 |
|
2148 if (NS_SUCCEEDED(mediaStream.GetRemoteCandidates(&candidates))) { |
|
2149 ToRTCIceCandidateStats(candidates, |
|
2150 RTCStatsType::Remotecandidate, |
|
2151 componentId, |
|
2152 now, |
|
2153 report); |
|
2154 } |
|
2155 } |
|
2156 |
|
2157 nsresult |
|
2158 PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) { |
|
2159 |
|
2160 ASSERT_ON_THREAD(query->iceCtx->thread()); |
|
2161 |
|
2162 // Gather stats from pipelines provided (can't touch mMedia + stream on STS) |
|
2163 |
|
2164 for (size_t p = 0; p < query->pipelines.Length(); ++p) { |
|
2165 const MediaPipeline& mp = *query->pipelines[p]; |
|
2166 bool isAudio = (mp.Conduit()->type() == MediaSessionConduit::AUDIO); |
|
2167 nsString idstr = isAudio ? |
|
2168 NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_"); |
|
2169 idstr.AppendInt(mp.trackid()); |
|
2170 |
|
2171 switch (mp.direction()) { |
|
2172 case MediaPipeline::TRANSMIT: { |
|
2173 nsString localId = NS_LITERAL_STRING("outbound_rtp_") + idstr; |
|
2174 nsString remoteId; |
|
2175 nsString ssrc; |
|
2176 unsigned int ssrcval; |
|
2177 if (mp.Conduit()->GetLocalSSRC(&ssrcval)) { |
|
2178 ssrc.AppendInt(ssrcval); |
|
2179 } |
|
2180 { |
|
2181 // First, fill in remote stat with rtcp receiver data, if present. |
|
2182 // ReceiverReports have less information than SenderReports, |
|
2183 // so fill in what we can. |
|
2184 DOMHighResTimeStamp timestamp; |
|
2185 uint32_t jitterMs; |
|
2186 uint32_t packetsReceived; |
|
2187 uint64_t bytesReceived; |
|
2188 uint32_t packetsLost; |
|
2189 int32_t rtt; |
|
2190 if (mp.Conduit()->GetRTCPReceiverReport(×tamp, &jitterMs, |
|
2191 &packetsReceived, |
|
2192 &bytesReceived, |
|
2193 &packetsLost, |
|
2194 &rtt)) { |
|
2195 remoteId = NS_LITERAL_STRING("outbound_rtcp_") + idstr; |
|
2196 RTCInboundRTPStreamStats s; |
|
2197 s.mTimestamp.Construct(timestamp); |
|
2198 s.mId.Construct(remoteId); |
|
2199 s.mType.Construct(RTCStatsType::Inboundrtp); |
|
2200 if (ssrc.Length()) { |
|
2201 s.mSsrc.Construct(ssrc); |
|
2202 } |
|
2203 s.mJitter.Construct(double(jitterMs)/1000); |
|
2204 s.mRemoteId.Construct(localId); |
|
2205 s.mIsRemote = true; |
|
2206 s.mPacketsReceived.Construct(packetsReceived); |
|
2207 s.mBytesReceived.Construct(bytesReceived); |
|
2208 s.mPacketsLost.Construct(packetsLost); |
|
2209 s.mMozRtt.Construct(rtt); |
|
2210 query->report.mInboundRTPStreamStats.Value().AppendElement(s); |
|
2211 } |
|
2212 } |
|
2213 // Then, fill in local side (with cross-link to remote only if present) |
|
2214 { |
|
2215 RTCOutboundRTPStreamStats s; |
|
2216 s.mTimestamp.Construct(query->now); |
|
2217 s.mId.Construct(localId); |
|
2218 s.mType.Construct(RTCStatsType::Outboundrtp); |
|
2219 if (ssrc.Length()) { |
|
2220 s.mSsrc.Construct(ssrc); |
|
2221 } |
|
2222 s.mRemoteId.Construct(remoteId); |
|
2223 s.mIsRemote = false; |
|
2224 s.mPacketsSent.Construct(mp.rtp_packets_sent()); |
|
2225 s.mBytesSent.Construct(mp.rtp_bytes_sent()); |
|
2226 query->report.mOutboundRTPStreamStats.Value().AppendElement(s); |
|
2227 } |
|
2228 break; |
|
2229 } |
|
2230 case MediaPipeline::RECEIVE: { |
|
2231 nsString localId = NS_LITERAL_STRING("inbound_rtp_") + idstr; |
|
2232 nsString remoteId; |
|
2233 nsString ssrc; |
|
2234 unsigned int ssrcval; |
|
2235 if (mp.Conduit()->GetRemoteSSRC(&ssrcval)) { |
|
2236 ssrc.AppendInt(ssrcval); |
|
2237 } |
|
2238 { |
|
2239 // First, fill in remote stat with rtcp sender data, if present. |
|
2240 DOMHighResTimeStamp timestamp; |
|
2241 uint32_t packetsSent; |
|
2242 uint64_t bytesSent; |
|
2243 if (mp.Conduit()->GetRTCPSenderReport(×tamp, |
|
2244 &packetsSent, &bytesSent)) { |
|
2245 remoteId = NS_LITERAL_STRING("inbound_rtcp_") + idstr; |
|
2246 RTCOutboundRTPStreamStats s; |
|
2247 s.mTimestamp.Construct(timestamp); |
|
2248 s.mId.Construct(remoteId); |
|
2249 s.mType.Construct(RTCStatsType::Outboundrtp); |
|
2250 if (ssrc.Length()) { |
|
2251 s.mSsrc.Construct(ssrc); |
|
2252 } |
|
2253 s.mRemoteId.Construct(localId); |
|
2254 s.mIsRemote = true; |
|
2255 s.mPacketsSent.Construct(packetsSent); |
|
2256 s.mBytesSent.Construct(bytesSent); |
|
2257 query->report.mOutboundRTPStreamStats.Value().AppendElement(s); |
|
2258 } |
|
2259 } |
|
2260 // Then, fill in local side (with cross-link to remote only if present) |
|
2261 RTCInboundRTPStreamStats s; |
|
2262 s.mTimestamp.Construct(query->now); |
|
2263 s.mId.Construct(localId); |
|
2264 s.mType.Construct(RTCStatsType::Inboundrtp); |
|
2265 if (ssrc.Length()) { |
|
2266 s.mSsrc.Construct(ssrc); |
|
2267 } |
|
2268 unsigned int jitterMs, packetsLost; |
|
2269 if (mp.Conduit()->GetRTPStats(&jitterMs, &packetsLost)) { |
|
2270 s.mJitter.Construct(double(jitterMs)/1000); |
|
2271 s.mPacketsLost.Construct(packetsLost); |
|
2272 } |
|
2273 if (remoteId.Length()) { |
|
2274 s.mRemoteId.Construct(remoteId); |
|
2275 } |
|
2276 s.mIsRemote = false; |
|
2277 s.mPacketsReceived.Construct(mp.rtp_packets_received()); |
|
2278 s.mBytesReceived.Construct(mp.rtp_bytes_received()); |
|
2279 |
|
2280 if (query->internalStats && isAudio) { |
|
2281 int32_t jitterBufferDelay; |
|
2282 int32_t playoutBufferDelay; |
|
2283 int32_t avSyncDelta; |
|
2284 if (mp.Conduit()->GetAVStats(&jitterBufferDelay, |
|
2285 &playoutBufferDelay, |
|
2286 &avSyncDelta)) { |
|
2287 s.mMozJitterBufferDelay.Construct(jitterBufferDelay); |
|
2288 s.mMozAvSyncDelay.Construct(avSyncDelta); |
|
2289 } |
|
2290 } |
|
2291 query->report.mInboundRTPStreamStats.Value().AppendElement(s); |
|
2292 break; |
|
2293 } |
|
2294 } |
|
2295 } |
|
2296 |
|
2297 // Gather stats from ICE |
|
2298 for (size_t s = 0; s != query->streams.Length(); ++s) { |
|
2299 RecordIceStats_s(*query->streams[s], |
|
2300 query->internalStats, |
|
2301 query->now, |
|
2302 &(query->report)); |
|
2303 } |
|
2304 |
|
2305 // NrIceCtx and NrIceMediaStream must be destroyed on STS, so it is not safe |
|
2306 // to dispatch them back to main. |
|
2307 // We clear streams first to maintain destruction order |
|
2308 query->streams.Clear(); |
|
2309 query->iceCtx = nullptr; |
|
2310 return NS_OK; |
|
2311 } |
|
2312 |
|
2313 void PeerConnectionImpl::GetStatsForPCObserver_s( |
|
2314 const std::string& pcHandle, // The Runnable holds the memory |
|
2315 nsAutoPtr<RTCStatsQuery> query) { |
|
2316 |
|
2317 MOZ_ASSERT(query); |
|
2318 MOZ_ASSERT(query->iceCtx); |
|
2319 ASSERT_ON_THREAD(query->iceCtx->thread()); |
|
2320 |
|
2321 nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get()); |
|
2322 |
|
2323 NS_DispatchToMainThread( |
|
2324 WrapRunnableNM( |
|
2325 &PeerConnectionImpl::DeliverStatsReportToPCObserver_m, |
|
2326 pcHandle, |
|
2327 rv, |
|
2328 query), |
|
2329 NS_DISPATCH_NORMAL); |
|
2330 } |
|
2331 |
|
2332 void PeerConnectionImpl::DeliverStatsReportToPCObserver_m( |
|
2333 const std::string& pcHandle, |
|
2334 nsresult result, |
|
2335 nsAutoPtr<RTCStatsQuery> query) { |
|
2336 |
|
2337 // Is the PeerConnectionImpl still around? |
|
2338 PeerConnectionWrapper pcw(pcHandle); |
|
2339 if (pcw.impl()) { |
|
2340 nsRefPtr<PeerConnectionObserver> pco = |
|
2341 do_QueryObjectReferent(pcw.impl()->mPCObserver); |
|
2342 if (pco) { |
|
2343 JSErrorResult rv; |
|
2344 if (NS_SUCCEEDED(result)) { |
|
2345 pco->OnGetStatsSuccess(query->report, rv); |
|
2346 } else { |
|
2347 pco->OnGetStatsError(kInternalError, |
|
2348 ObString("Failed to fetch statistics"), |
|
2349 rv); |
|
2350 } |
|
2351 |
|
2352 if (rv.Failed()) { |
|
2353 CSFLogError(logTag, "Error firing stats observer callback"); |
|
2354 } |
|
2355 } |
|
2356 } |
|
2357 } |
|
2358 |
|
2359 #endif |
|
2360 |
|
2361 void |
|
2362 PeerConnectionImpl::RecordLongtermICEStatistics() { |
|
2363 #ifdef MOZILLA_INTERNAL_API |
|
2364 WebrtcGlobalInformation::StoreLongTermICEStatistics(*this); |
|
2365 #endif |
|
2366 } |
|
2367 |
|
2368 void |
|
2369 PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream) |
|
2370 { |
|
2371 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
2372 MOZ_ASSERT(aStream); |
|
2373 |
|
2374 CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str()); |
|
2375 } |
|
2376 |
|
2377 void |
|
2378 PeerConnectionImpl::OnSdpParseError(const char *message) { |
|
2379 CSFLogError(logTag, "%s SDP Parse Error: %s", __FUNCTION__, message); |
|
2380 // Save the parsing errors in the PC to be delivered with OnSuccess or OnError |
|
2381 mSDPParseErrorMessages.push_back(message); |
|
2382 } |
|
2383 |
|
2384 void |
|
2385 PeerConnectionImpl::ClearSdpParseErrorMessages() { |
|
2386 mSDPParseErrorMessages.clear(); |
|
2387 } |
|
2388 |
|
2389 const std::vector<std::string> & |
|
2390 PeerConnectionImpl::GetSdpParseErrors() { |
|
2391 return mSDPParseErrorMessages; |
|
2392 } |
|
2393 |
|
2394 #ifdef MOZILLA_INTERNAL_API |
|
2395 //Telemetry for when calls start |
|
2396 void |
|
2397 PeerConnectionImpl::startCallTelem() { |
|
2398 // Start time for calls |
|
2399 mStartTime = TimeStamp::Now(); |
|
2400 |
|
2401 // Increment session call counter |
|
2402 #ifdef MOZILLA_INTERNAL_API |
|
2403 int &cnt = PeerConnectionCtx::GetInstance()->mConnectionCounter; |
|
2404 Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Subtract(cnt); |
|
2405 cnt++; |
|
2406 Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Add(cnt); |
|
2407 #endif |
|
2408 } |
|
2409 #endif |
|
2410 |
|
2411 NS_IMETHODIMP |
|
2412 PeerConnectionImpl::GetLocalStreams(nsTArray<nsRefPtr<DOMMediaStream > >& result) |
|
2413 { |
|
2414 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
2415 #ifdef MOZILLA_INTERNAL_API |
|
2416 for(uint32_t i=0; i < media()->LocalStreamsLength(); i++) { |
|
2417 LocalSourceStreamInfo *info = media()->GetLocalStream(i); |
|
2418 NS_ENSURE_TRUE(info, NS_ERROR_UNEXPECTED); |
|
2419 result.AppendElement(info->GetMediaStream()); |
|
2420 } |
|
2421 return NS_OK; |
|
2422 #else |
|
2423 return NS_ERROR_FAILURE; |
|
2424 #endif |
|
2425 } |
|
2426 |
|
2427 NS_IMETHODIMP |
|
2428 PeerConnectionImpl::GetRemoteStreams(nsTArray<nsRefPtr<DOMMediaStream > >& result) |
|
2429 { |
|
2430 PC_AUTO_ENTER_API_CALL_NO_CHECK(); |
|
2431 #ifdef MOZILLA_INTERNAL_API |
|
2432 for(uint32_t i=0; i < media()->RemoteStreamsLength(); i++) { |
|
2433 RemoteSourceStreamInfo *info = media()->GetRemoteStream(i); |
|
2434 NS_ENSURE_TRUE(info, NS_ERROR_UNEXPECTED); |
|
2435 result.AppendElement(info->GetMediaStream()); |
|
2436 } |
|
2437 return NS_OK; |
|
2438 #else |
|
2439 return NS_ERROR_FAILURE; |
|
2440 #endif |
|
2441 } |
|
2442 |
|
2443 } // end sipcc namespace |