1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2443 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include <cstdlib> 1.9 +#include <cerrno> 1.10 +#include <deque> 1.11 +#include <sstream> 1.12 + 1.13 +#include "base/histogram.h" 1.14 +#include "vcm.h" 1.15 +#include "CSFLog.h" 1.16 +#include "timecard.h" 1.17 +#include "ccapi_call_info.h" 1.18 +#include "CC_SIPCCCallInfo.h" 1.19 +#include "ccapi_device_info.h" 1.20 +#include "CC_SIPCCDeviceInfo.h" 1.21 +#include "cpr_string.h" 1.22 +#include "cpr_stdlib.h" 1.23 + 1.24 +#include "jsapi.h" 1.25 +#include "nspr.h" 1.26 +#include "nss.h" 1.27 +#include "pk11pub.h" 1.28 + 1.29 +#include "nsNetCID.h" 1.30 +#include "nsIProperty.h" 1.31 +#include "nsIPropertyBag2.h" 1.32 +#include "nsIServiceManager.h" 1.33 +#include "nsISimpleEnumerator.h" 1.34 +#include "nsServiceManagerUtils.h" 1.35 +#include "nsISocketTransportService.h" 1.36 +#include "nsIConsoleService.h" 1.37 +#include "nsThreadUtils.h" 1.38 +#include "nsProxyRelease.h" 1.39 +#include "prtime.h" 1.40 + 1.41 +#include "AudioConduit.h" 1.42 +#include "VideoConduit.h" 1.43 +#include "runnable_utils.h" 1.44 +#include "PeerConnectionCtx.h" 1.45 +#include "PeerConnectionImpl.h" 1.46 +#include "PeerConnectionMedia.h" 1.47 +#include "nsDOMDataChannelDeclarations.h" 1.48 +#include "dtlsidentity.h" 1.49 + 1.50 +#ifdef MOZILLA_INTERNAL_API 1.51 +#include "nsPerformance.h" 1.52 +#include "nsGlobalWindow.h" 1.53 +#include "nsDOMDataChannel.h" 1.54 +#include "mozilla/TimeStamp.h" 1.55 +#include "mozilla/Telemetry.h" 1.56 +#include "mozilla/Preferences.h" 1.57 +#include "mozilla/PublicSSL.h" 1.58 +#include "nsXULAppAPI.h" 1.59 +#include "nsContentUtils.h" 1.60 +#include "nsDOMJSUtils.h" 1.61 +#include "nsIDocument.h" 1.62 +#include "nsIScriptError.h" 1.63 +#include "nsPrintfCString.h" 1.64 +#include "nsURLHelper.h" 1.65 +#include "nsNetUtil.h" 1.66 +#include "nsIDOMDataChannel.h" 1.67 +#include "nsIDOMLocation.h" 1.68 +#include "mozilla/dom/RTCConfigurationBinding.h" 1.69 +#include "mozilla/dom/RTCStatsReportBinding.h" 1.70 +#include "mozilla/dom/RTCPeerConnectionBinding.h" 1.71 +#include "mozilla/dom/PeerConnectionImplBinding.h" 1.72 +#include "mozilla/dom/DataChannelBinding.h" 1.73 +#include "MediaStreamList.h" 1.74 +#include "MediaStreamTrack.h" 1.75 +#include "AudioStreamTrack.h" 1.76 +#include "VideoStreamTrack.h" 1.77 +#include "nsIScriptGlobalObject.h" 1.78 +#include "DOMMediaStream.h" 1.79 +#include "rlogringbuffer.h" 1.80 +#include "WebrtcGlobalInformation.h" 1.81 +#endif 1.82 + 1.83 +#ifndef USE_FAKE_MEDIA_STREAMS 1.84 +#include "MediaSegment.h" 1.85 +#endif 1.86 + 1.87 +#ifdef USE_FAKE_PCOBSERVER 1.88 +#include "FakePCObserver.h" 1.89 +#else 1.90 +#include "mozilla/dom/PeerConnectionObserverBinding.h" 1.91 +#endif 1.92 +#include "mozilla/dom/PeerConnectionObserverEnumsBinding.h" 1.93 + 1.94 +#define ICE_PARSING "In RTCConfiguration passed to RTCPeerConnection constructor" 1.95 + 1.96 +using namespace mozilla; 1.97 +using namespace mozilla::dom; 1.98 + 1.99 +typedef PCObserverString ObString; 1.100 + 1.101 +static const char* logTag = "PeerConnectionImpl"; 1.102 + 1.103 +#ifdef MOZILLA_INTERNAL_API 1.104 +static nsresult InitNSSInContent() 1.105 +{ 1.106 + NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD); 1.107 + 1.108 + if (XRE_GetProcessType() != GeckoProcessType_Content) { 1.109 + MOZ_ASSUME_UNREACHABLE("Must be called in content process"); 1.110 + } 1.111 + 1.112 + static bool nssStarted = false; 1.113 + if (nssStarted) { 1.114 + return NS_OK; 1.115 + } 1.116 + 1.117 + if (NSS_NoDB_Init(nullptr) != SECSuccess) { 1.118 + CSFLogError(logTag, "NSS_NoDB_Init failed."); 1.119 + return NS_ERROR_FAILURE; 1.120 + } 1.121 + 1.122 + if (NS_FAILED(mozilla::psm::InitializeCipherSuite())) { 1.123 + CSFLogError(logTag, "Fail to set up nss cipher suite."); 1.124 + return NS_ERROR_FAILURE; 1.125 + } 1.126 + 1.127 + mozilla::psm::DisableMD5(); 1.128 + 1.129 + nssStarted = true; 1.130 + 1.131 + return NS_OK; 1.132 +} 1.133 +#endif // MOZILLA_INTERNAL_API 1.134 + 1.135 +namespace mozilla { 1.136 + class DataChannel; 1.137 +} 1.138 + 1.139 +class nsIDOMDataChannel; 1.140 + 1.141 +static const int MEDIA_STREAM_MUTE = 0x80; 1.142 + 1.143 +PRLogModuleInfo *signalingLogInfo() { 1.144 + static PRLogModuleInfo *logModuleInfo = nullptr; 1.145 + if (!logModuleInfo) { 1.146 + logModuleInfo = PR_NewLogModule("signaling"); 1.147 + } 1.148 + return logModuleInfo; 1.149 +} 1.150 + 1.151 + 1.152 +namespace sipcc { 1.153 + 1.154 +#ifdef MOZILLA_INTERNAL_API 1.155 +RTCStatsQuery::RTCStatsQuery(bool internal) : internalStats(internal) { 1.156 +} 1.157 + 1.158 +RTCStatsQuery::~RTCStatsQuery() { 1.159 + MOZ_ASSERT(NS_IsMainThread()); 1.160 +} 1.161 + 1.162 +#endif 1.163 + 1.164 +// Getting exceptions back down from PCObserver is generally not harmful. 1.165 +namespace { 1.166 +class JSErrorResult : public ErrorResult 1.167 +{ 1.168 +public: 1.169 + ~JSErrorResult() 1.170 + { 1.171 +#ifdef MOZILLA_INTERNAL_API 1.172 + WouldReportJSException(); 1.173 + if (IsJSException()) { 1.174 + MOZ_ASSERT(NS_IsMainThread()); 1.175 + AutoJSContext cx; 1.176 + Optional<JS::Handle<JS::Value> > value(cx); 1.177 + StealJSException(cx, &value.Value()); 1.178 + } 1.179 +#endif 1.180 + } 1.181 +}; 1.182 + 1.183 +// The WrapRunnable() macros copy passed-in args and passes them to the function 1.184 +// later on the other thread. ErrorResult cannot be passed like this because it 1.185 +// disallows copy-semantics. 1.186 +// 1.187 +// This WrappableJSErrorResult hack solves this by not actually copying the 1.188 +// ErrorResult, but creating a new one instead, which works because we don't 1.189 +// care about the result. 1.190 +// 1.191 +// Since this is for JS-calls, these can only be dispatched to the main thread. 1.192 + 1.193 +class WrappableJSErrorResult { 1.194 +public: 1.195 + WrappableJSErrorResult() : isCopy(false) {} 1.196 + WrappableJSErrorResult(WrappableJSErrorResult &other) : mRv(), isCopy(true) {} 1.197 + ~WrappableJSErrorResult() { 1.198 + if (isCopy) { 1.199 +#ifdef MOZILLA_INTERNAL_API 1.200 + MOZ_ASSERT(NS_IsMainThread()); 1.201 +#endif 1.202 + } 1.203 + } 1.204 + operator JSErrorResult &() { return mRv; } 1.205 +private: 1.206 + JSErrorResult mRv; 1.207 + bool isCopy; 1.208 +}; 1.209 +} 1.210 + 1.211 +class PeerConnectionObserverDispatch : public nsRunnable { 1.212 + 1.213 +public: 1.214 + PeerConnectionObserverDispatch(CSF::CC_CallInfoPtr aInfo, 1.215 + nsRefPtr<PeerConnectionImpl> aPC, 1.216 + PeerConnectionObserver* aObserver) 1.217 + : mPC(aPC), 1.218 + mObserver(aObserver), 1.219 + mCode(static_cast<PeerConnectionImpl::Error>(aInfo->getStatusCode())), 1.220 + mReason(aInfo->getStatus()), 1.221 + mSdpStr(), 1.222 + mCandidateStr(), 1.223 + mCallState(aInfo->getCallState()), 1.224 + mFsmState(aInfo->getFsmState()), 1.225 + mStateStr(aInfo->callStateToString(mCallState)), 1.226 + mFsmStateStr(aInfo->fsmStateToString(mFsmState)) { 1.227 + if (mCallState == REMOTESTREAMADD) { 1.228 + MediaStreamTable *streams = nullptr; 1.229 + streams = aInfo->getMediaStreams(); 1.230 + mRemoteStream = mPC->media()->GetRemoteStream(streams->media_stream_id); 1.231 + MOZ_ASSERT(mRemoteStream); 1.232 + } else if (mCallState == FOUNDICECANDIDATE) { 1.233 + mCandidateStr = aInfo->getCandidate(); 1.234 + } else if ((mCallState == CREATEOFFERSUCCESS) || 1.235 + (mCallState == CREATEANSWERSUCCESS)) { 1.236 + mSdpStr = aInfo->getSDP(); 1.237 + } 1.238 + } 1.239 + 1.240 + ~PeerConnectionObserverDispatch(){} 1.241 + 1.242 +#ifdef MOZILLA_INTERNAL_API 1.243 + class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback 1.244 + { 1.245 + public: 1.246 + TracksAvailableCallback(DOMMediaStream::TrackTypeHints aTrackTypeHints, 1.247 + nsRefPtr<PeerConnectionObserver> aObserver) 1.248 + : DOMMediaStream::OnTracksAvailableCallback(aTrackTypeHints) 1.249 + , mObserver(aObserver) {} 1.250 + 1.251 + virtual void NotifyTracksAvailable(DOMMediaStream* aStream) MOZ_OVERRIDE 1.252 + { 1.253 + MOZ_ASSERT(NS_IsMainThread()); 1.254 + 1.255 + // Start currentTime from the point where this stream was successfully 1.256 + // returned. 1.257 + aStream->SetLogicalStreamStartTime(aStream->GetStream()->GetCurrentTime()); 1.258 + 1.259 + CSFLogInfo(logTag, "Returning success for OnAddStream()"); 1.260 + // We are running on main thread here so we shouldn't have a race 1.261 + // on this callback 1.262 + JSErrorResult rv; 1.263 + mObserver->OnAddStream(*aStream, rv); 1.264 + if (rv.Failed()) { 1.265 + CSFLogError(logTag, ": OnAddStream() failed! Error: %d", rv.ErrorCode()); 1.266 + } 1.267 + } 1.268 + private: 1.269 + nsRefPtr<PeerConnectionObserver> mObserver; 1.270 + }; 1.271 +#endif 1.272 + 1.273 + NS_IMETHOD Run() { 1.274 + 1.275 + CSFLogInfo(logTag, "PeerConnectionObserverDispatch processing " 1.276 + "mCallState = %d (%s), mFsmState = %d (%s)", 1.277 + mCallState, mStateStr.c_str(), mFsmState, mFsmStateStr.c_str()); 1.278 + 1.279 + if (mCallState == SETLOCALDESCERROR || mCallState == SETREMOTEDESCERROR) { 1.280 + const std::vector<std::string> &errors = mPC->GetSdpParseErrors(); 1.281 + std::vector<std::string>::const_iterator i; 1.282 + for (i = errors.begin(); i != errors.end(); ++i) { 1.283 + mReason += " | SDP Parsing Error: " + *i; 1.284 + } 1.285 + if (errors.size()) { 1.286 + mCode = PeerConnectionImpl::kInvalidSessionDescription; 1.287 + } 1.288 + mPC->ClearSdpParseErrorMessages(); 1.289 + } 1.290 + 1.291 + if (mReason.length()) { 1.292 + CSFLogInfo(logTag, "Message contains error: %d: %s", 1.293 + mCode, mReason.c_str()); 1.294 + } 1.295 + 1.296 + /* 1.297 + * While the fsm_states_t (FSM_DEF_*) constants are a proper superset 1.298 + * of SignalingState, and the order in which the SignalingState values 1.299 + * appear matches the order they appear in fsm_states_t, their underlying 1.300 + * numeric representation is different. Hence, we need to perform an 1.301 + * offset calculation to map from one to the other. 1.302 + */ 1.303 + 1.304 + if (mFsmState >= FSMDEF_S_STABLE && mFsmState <= FSMDEF_S_CLOSED) { 1.305 + int offset = FSMDEF_S_STABLE - int(PCImplSignalingState::SignalingStable); 1.306 + mPC->SetSignalingState_m(static_cast<PCImplSignalingState>(mFsmState - offset)); 1.307 + } else { 1.308 + CSFLogError(logTag, ": **** UNHANDLED SIGNALING STATE : %d (%s)", 1.309 + mFsmState, mFsmStateStr.c_str()); 1.310 + } 1.311 + 1.312 + JSErrorResult rv; 1.313 + 1.314 + switch (mCallState) { 1.315 + case CREATEOFFERSUCCESS: 1.316 + mObserver->OnCreateOfferSuccess(ObString(mSdpStr.c_str()), rv); 1.317 + break; 1.318 + 1.319 + case CREATEANSWERSUCCESS: 1.320 + mObserver->OnCreateAnswerSuccess(ObString(mSdpStr.c_str()), rv); 1.321 + break; 1.322 + 1.323 + case CREATEOFFERERROR: 1.324 + mObserver->OnCreateOfferError(mCode, ObString(mReason.c_str()), rv); 1.325 + break; 1.326 + 1.327 + case CREATEANSWERERROR: 1.328 + mObserver->OnCreateAnswerError(mCode, ObString(mReason.c_str()), rv); 1.329 + break; 1.330 + 1.331 + case SETLOCALDESCSUCCESS: 1.332 + // TODO: The SDP Parse error list should be copied out and sent up 1.333 + // to the Javascript layer before being cleared here. Even though 1.334 + // there was not a failure, it is possible that the SDP parse generated 1.335 + // warnings. The WebRTC spec does not currently have a mechanism for 1.336 + // providing non-fatal warnings. 1.337 + mPC->ClearSdpParseErrorMessages(); 1.338 + mObserver->OnSetLocalDescriptionSuccess(rv); 1.339 + break; 1.340 + 1.341 + case SETREMOTEDESCSUCCESS: 1.342 + // TODO: The SDP Parse error list should be copied out and sent up 1.343 + // to the Javascript layer before being cleared here. Even though 1.344 + // there was not a failure, it is possible that the SDP parse generated 1.345 + // warnings. The WebRTC spec does not currently have a mechanism for 1.346 + // providing non-fatal warnings. 1.347 + mPC->ClearSdpParseErrorMessages(); 1.348 + mObserver->OnSetRemoteDescriptionSuccess(rv); 1.349 +#ifdef MOZILLA_INTERNAL_API 1.350 + mPC->startCallTelem(); 1.351 +#endif 1.352 + break; 1.353 + 1.354 + case SETLOCALDESCERROR: 1.355 + mObserver->OnSetLocalDescriptionError(mCode, 1.356 + ObString(mReason.c_str()), rv); 1.357 + break; 1.358 + 1.359 + case SETREMOTEDESCERROR: 1.360 + mObserver->OnSetRemoteDescriptionError(mCode, 1.361 + ObString(mReason.c_str()), rv); 1.362 + break; 1.363 + 1.364 + case ADDICECANDIDATE: 1.365 + mObserver->OnAddIceCandidateSuccess(rv); 1.366 + break; 1.367 + 1.368 + case ADDICECANDIDATEERROR: 1.369 + mObserver->OnAddIceCandidateError(mCode, ObString(mReason.c_str()), rv); 1.370 + break; 1.371 + 1.372 + case FOUNDICECANDIDATE: 1.373 + { 1.374 + size_t end_of_level = mCandidateStr.find('\t'); 1.375 + if (end_of_level == std::string::npos) { 1.376 + MOZ_ASSERT(false); 1.377 + return NS_OK; 1.378 + } 1.379 + std::string level = mCandidateStr.substr(0, end_of_level); 1.380 + if (!level.size()) { 1.381 + MOZ_ASSERT(false); 1.382 + return NS_OK; 1.383 + } 1.384 + char *endptr; 1.385 + errno = 0; 1.386 + unsigned long level_long = 1.387 + strtoul(level.c_str(), &endptr, 10); 1.388 + if (errno || *endptr != 0 || level_long > 65535) { 1.389 + /* Conversion failure */ 1.390 + MOZ_ASSERT(false); 1.391 + return NS_OK; 1.392 + } 1.393 + size_t end_of_mid = mCandidateStr.find('\t', end_of_level + 1); 1.394 + if (end_of_mid == std::string::npos) { 1.395 + MOZ_ASSERT(false); 1.396 + return NS_OK; 1.397 + } 1.398 + 1.399 + std::string mid = mCandidateStr.substr(end_of_level + 1, 1.400 + end_of_mid - (end_of_level + 1)); 1.401 + 1.402 + std::string candidate = mCandidateStr.substr(end_of_mid + 1); 1.403 + 1.404 + mObserver->OnIceCandidate(level_long & 0xffff, 1.405 + ObString(mid.c_str()), 1.406 + ObString(candidate.c_str()), rv); 1.407 + } 1.408 + break; 1.409 + case REMOTESTREAMADD: 1.410 + { 1.411 + DOMMediaStream* stream = nullptr; 1.412 + 1.413 + if (!mRemoteStream) { 1.414 + CSFLogError(logTag, "%s: GetRemoteStream returned NULL", __FUNCTION__); 1.415 + } else { 1.416 + stream = mRemoteStream->GetMediaStream(); 1.417 + } 1.418 + 1.419 + if (!stream) { 1.420 + CSFLogError(logTag, "%s: GetMediaStream returned NULL", __FUNCTION__); 1.421 + } else { 1.422 +#ifdef MOZILLA_INTERNAL_API 1.423 + TracksAvailableCallback* tracksAvailableCallback = 1.424 + new TracksAvailableCallback(mRemoteStream->mTrackTypeHints, mObserver); 1.425 + 1.426 + stream->OnTracksAvailable(tracksAvailableCallback); 1.427 +#else 1.428 + mObserver->OnAddStream(stream, rv); 1.429 +#endif 1.430 + } 1.431 + break; 1.432 + } 1.433 + 1.434 + case UPDATELOCALDESC: 1.435 + /* No action necessary */ 1.436 + break; 1.437 + 1.438 + default: 1.439 + CSFLogError(logTag, ": **** UNHANDLED CALL STATE : %d (%s)", 1.440 + mCallState, mStateStr.c_str()); 1.441 + break; 1.442 + } 1.443 + return NS_OK; 1.444 + } 1.445 + 1.446 +private: 1.447 + nsRefPtr<PeerConnectionImpl> mPC; 1.448 + nsRefPtr<PeerConnectionObserver> mObserver; 1.449 + PeerConnectionImpl::Error mCode; 1.450 + std::string mReason; 1.451 + std::string mSdpStr; 1.452 + std::string mCandidateStr; 1.453 + cc_call_state_t mCallState; 1.454 + fsmdef_states_t mFsmState; 1.455 + std::string mStateStr; 1.456 + std::string mFsmStateStr; 1.457 + nsRefPtr<RemoteSourceStreamInfo> mRemoteStream; 1.458 +}; 1.459 + 1.460 +NS_IMPL_ISUPPORTS0(PeerConnectionImpl) 1.461 + 1.462 +#ifdef MOZILLA_INTERNAL_API 1.463 +JSObject* 1.464 +PeerConnectionImpl::WrapObject(JSContext* aCx) 1.465 +{ 1.466 + return PeerConnectionImplBinding::Wrap(aCx, this); 1.467 +} 1.468 +#endif 1.469 + 1.470 +struct PeerConnectionImpl::Internal { 1.471 + CSF::CC_CallPtr mCall; 1.472 +}; 1.473 + 1.474 +PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal) 1.475 +: mTimeCard(PR_LOG_TEST(signalingLogInfo(),PR_LOG_ERROR) ? 1.476 + create_timecard() : nullptr) 1.477 + , mInternal(new Internal()) 1.478 + , mReadyState(PCImplReadyState::New) 1.479 + , mSignalingState(PCImplSignalingState::SignalingStable) 1.480 + , mIceConnectionState(PCImplIceConnectionState::New) 1.481 + , mIceGatheringState(PCImplIceGatheringState::New) 1.482 + , mWindow(nullptr) 1.483 + , mIdentity(nullptr) 1.484 + , mSTSThread(nullptr) 1.485 + , mLoadManager(nullptr) 1.486 + , mMedia(nullptr) 1.487 + , mNumAudioStreams(0) 1.488 + , mNumVideoStreams(0) 1.489 + , mHaveDataStream(false) 1.490 + , mTrickle(true) // TODO(ekr@rtfm.com): Use pref 1.491 +{ 1.492 +#ifdef MOZILLA_INTERNAL_API 1.493 + MOZ_ASSERT(NS_IsMainThread()); 1.494 + if (aGlobal) { 1.495 + mWindow = do_QueryInterface(aGlobal->GetAsSupports()); 1.496 + } 1.497 +#endif 1.498 + MOZ_ASSERT(mInternal); 1.499 + CSFLogInfo(logTag, "%s: PeerConnectionImpl constructor for %s", 1.500 + __FUNCTION__, mHandle.c_str()); 1.501 + STAMP_TIMECARD(mTimeCard, "Constructor Completed"); 1.502 +} 1.503 + 1.504 +PeerConnectionImpl::~PeerConnectionImpl() 1.505 +{ 1.506 + if (mTimeCard) { 1.507 + STAMP_TIMECARD(mTimeCard, "Destructor Invoked"); 1.508 + print_timecard(mTimeCard); 1.509 + destroy_timecard(mTimeCard); 1.510 + mTimeCard = nullptr; 1.511 + } 1.512 + // This aborts if not on main thread (in Debug builds) 1.513 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.514 + if (PeerConnectionCtx::isActive()) { 1.515 + PeerConnectionCtx::GetInstance()->mPeerConnections.erase(mHandle); 1.516 + } else { 1.517 + CSFLogError(logTag, "PeerConnectionCtx is already gone. Ignoring..."); 1.518 + } 1.519 + 1.520 + CSFLogInfo(logTag, "%s: PeerConnectionImpl destructor invoked for %s", 1.521 + __FUNCTION__, mHandle.c_str()); 1.522 + CloseInt(); 1.523 + 1.524 +#ifdef MOZILLA_INTERNAL_API 1.525 + { 1.526 + // Deregister as an NSS Shutdown Object 1.527 + nsNSSShutDownPreventionLock locker; 1.528 + if (!isAlreadyShutDown()) { 1.529 + destructorSafeDestroyNSSReference(); 1.530 + shutdown(calledFromObject); 1.531 + } 1.532 + } 1.533 + if (mLoadManager) { 1.534 + mozilla::LoadManagerDestroy(mLoadManager); 1.535 + mLoadManager = nullptr; 1.536 + } 1.537 +#endif 1.538 + 1.539 + // Since this and Initialize() occur on MainThread, they can't both be 1.540 + // running at once 1.541 + 1.542 + // Right now, we delete PeerConnectionCtx at XPCOM shutdown only, but we 1.543 + // probably want to shut it down more aggressively to save memory. We 1.544 + // could shut down here when there are no uses. It might be more optimal 1.545 + // to release off a timer (and XPCOM Shutdown) to avoid churn 1.546 +} 1.547 + 1.548 +already_AddRefed<DOMMediaStream> 1.549 +PeerConnectionImpl::MakeMediaStream(nsPIDOMWindow* aWindow, 1.550 + uint32_t aHint) 1.551 +{ 1.552 + nsRefPtr<DOMMediaStream> stream = 1.553 + DOMMediaStream::CreateSourceStream(aWindow, aHint); 1.554 +#ifdef MOZILLA_INTERNAL_API 1.555 + nsIDocument* doc = aWindow->GetExtantDoc(); 1.556 + if (!doc) { 1.557 + return nullptr; 1.558 + } 1.559 + // Make the stream data (audio/video samples) accessible to the receiving page. 1.560 + stream->CombineWithPrincipal(doc->NodePrincipal()); 1.561 +#endif 1.562 + 1.563 + CSFLogDebug(logTag, "Created media stream %p, inner: %p", stream.get(), stream->GetStream()); 1.564 + 1.565 + return stream.forget(); 1.566 +} 1.567 + 1.568 +nsresult 1.569 +PeerConnectionImpl::CreateRemoteSourceStreamInfo(nsRefPtr<RemoteSourceStreamInfo>* 1.570 + aInfo) 1.571 +{ 1.572 + MOZ_ASSERT(aInfo); 1.573 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.574 + 1.575 + // We need to pass a dummy hint here because FakeMediaStream currently 1.576 + // needs to actually propagate a hint for local streams. 1.577 + // TODO(ekr@rtfm.com): Clean up when we have explicit track lists. 1.578 + // See bug 834835. 1.579 + nsRefPtr<DOMMediaStream> stream = MakeMediaStream(mWindow, 0); 1.580 + if (!stream) { 1.581 + return NS_ERROR_FAILURE; 1.582 + } 1.583 + 1.584 + static_cast<SourceMediaStream*>(stream->GetStream())->SetPullEnabled(true); 1.585 + 1.586 + nsRefPtr<RemoteSourceStreamInfo> remote; 1.587 + remote = new RemoteSourceStreamInfo(stream.forget(), mMedia); 1.588 + *aInfo = remote; 1.589 + 1.590 + return NS_OK; 1.591 +} 1.592 + 1.593 +/** 1.594 + * In JS, an RTCConfiguration looks like this: 1.595 + * 1.596 + * { "iceServers": [ { url:"stun:stun.example.org" }, 1.597 + * { url:"turn:turn.example.org?transport=udp", 1.598 + * username: "jib", credential:"mypass"} ] } 1.599 + * 1.600 + * This function converts that into an internal IceConfiguration object. 1.601 + */ 1.602 +nsresult 1.603 +PeerConnectionImpl::ConvertRTCConfiguration(const RTCConfiguration& aSrc, 1.604 + IceConfiguration *aDst) 1.605 +{ 1.606 +#ifdef MOZILLA_INTERNAL_API 1.607 + if (!aSrc.mIceServers.WasPassed()) { 1.608 + return NS_OK; 1.609 + } 1.610 + for (uint32_t i = 0; i < aSrc.mIceServers.Value().Length(); i++) { 1.611 + const RTCIceServer& server = aSrc.mIceServers.Value()[i]; 1.612 + NS_ENSURE_TRUE(server.mUrl.WasPassed(), NS_ERROR_UNEXPECTED); 1.613 + 1.614 + // Without STUN/TURN handlers, NS_NewURI returns nsSimpleURI rather than 1.615 + // nsStandardURL. To parse STUN/TURN URI's to spec 1.616 + // http://tools.ietf.org/html/draft-nandakumar-rtcweb-stun-uri-02#section-3 1.617 + // http://tools.ietf.org/html/draft-petithuguenin-behave-turn-uri-03#section-3 1.618 + // we parse out the query-string, and use ParseAuthority() on the rest 1.619 + nsRefPtr<nsIURI> url; 1.620 + nsresult rv = NS_NewURI(getter_AddRefs(url), server.mUrl.Value()); 1.621 + NS_ENSURE_SUCCESS(rv, rv); 1.622 + bool isStun = false, isStuns = false, isTurn = false, isTurns = false; 1.623 + url->SchemeIs("stun", &isStun); 1.624 + url->SchemeIs("stuns", &isStuns); 1.625 + url->SchemeIs("turn", &isTurn); 1.626 + url->SchemeIs("turns", &isTurns); 1.627 + if (!(isStun || isStuns || isTurn || isTurns)) { 1.628 + return NS_ERROR_FAILURE; 1.629 + } 1.630 + nsAutoCString spec; 1.631 + rv = url->GetSpec(spec); 1.632 + NS_ENSURE_SUCCESS(rv, rv); 1.633 + 1.634 + // TODO(jib@mozilla.com): Revisit once nsURI supports STUN/TURN (Bug 833509) 1.635 + int32_t port; 1.636 + nsAutoCString host; 1.637 + nsAutoCString transport; 1.638 + { 1.639 + uint32_t hostPos; 1.640 + int32_t hostLen; 1.641 + nsAutoCString path; 1.642 + rv = url->GetPath(path); 1.643 + NS_ENSURE_SUCCESS(rv, rv); 1.644 + 1.645 + // Tolerate query-string + parse 'transport=[udp|tcp]' by hand. 1.646 + int32_t questionmark = path.FindChar('?'); 1.647 + if (questionmark >= 0) { 1.648 + const nsCString match = NS_LITERAL_CSTRING("transport="); 1.649 + 1.650 + for (int32_t i = questionmark, endPos; i >= 0; i = endPos) { 1.651 + endPos = path.FindCharInSet("&", i + 1); 1.652 + const nsDependentCSubstring fieldvaluepair = Substring(path, i + 1, 1.653 + endPos); 1.654 + if (StringBeginsWith(fieldvaluepair, match)) { 1.655 + transport = Substring(fieldvaluepair, match.Length()); 1.656 + ToLowerCase(transport); 1.657 + } 1.658 + } 1.659 + path.SetLength(questionmark); 1.660 + } 1.661 + 1.662 + rv = net_GetAuthURLParser()->ParseAuthority(path.get(), path.Length(), 1.663 + nullptr, nullptr, 1.664 + nullptr, nullptr, 1.665 + &hostPos, &hostLen, &port); 1.666 + NS_ENSURE_SUCCESS(rv, rv); 1.667 + if (!hostLen) { 1.668 + return NS_ERROR_FAILURE; 1.669 + } 1.670 + if (hostPos > 1) /* The username was removed */ 1.671 + return NS_ERROR_FAILURE; 1.672 + path.Mid(host, hostPos, hostLen); 1.673 + } 1.674 + if (port == -1) 1.675 + port = (isStuns || isTurns)? 5349 : 3478; 1.676 + 1.677 + if (isTurn || isTurns) { 1.678 + NS_ConvertUTF16toUTF8 credential(server.mCredential); 1.679 + NS_ConvertUTF16toUTF8 username(server.mUsername); 1.680 + 1.681 +#ifdef MOZ_WIDGET_GONK 1.682 + if (transport == kNrIceTransportTcp) 1.683 + continue; 1.684 +#endif 1.685 + if (!aDst->addTurnServer(host.get(), port, 1.686 + username.get(), 1.687 + credential.get(), 1.688 + (transport.IsEmpty() ? 1.689 + kNrIceTransportUdp : transport.get()))) { 1.690 + return NS_ERROR_FAILURE; 1.691 + } 1.692 + } else { 1.693 + if (!aDst->addStunServer(host.get(), port)) { 1.694 + return NS_ERROR_FAILURE; 1.695 + } 1.696 + } 1.697 + } 1.698 +#endif 1.699 + return NS_OK; 1.700 +} 1.701 + 1.702 +NS_IMETHODIMP 1.703 +PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver, 1.704 + nsGlobalWindow* aWindow, 1.705 + const IceConfiguration* aConfiguration, 1.706 + const RTCConfiguration* aRTCConfiguration, 1.707 + nsISupports* aThread) 1.708 +{ 1.709 + nsresult res; 1.710 + 1.711 + // Invariant: we receive configuration one way or the other but not both (XOR) 1.712 + MOZ_ASSERT(!aConfiguration != !aRTCConfiguration); 1.713 +#ifdef MOZILLA_INTERNAL_API 1.714 + MOZ_ASSERT(NS_IsMainThread()); 1.715 +#endif 1.716 + MOZ_ASSERT(aThread); 1.717 + mThread = do_QueryInterface(aThread); 1.718 + 1.719 + mPCObserver = do_GetWeakReference(&aObserver); 1.720 + 1.721 + // Find the STS thread 1.722 + 1.723 + mSTSThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res); 1.724 + MOZ_ASSERT(mSTSThread); 1.725 +#ifdef MOZILLA_INTERNAL_API 1.726 + 1.727 + // Initialize NSS if we are in content process. For chrome process, NSS should already 1.728 + // been initialized. 1.729 + if (XRE_GetProcessType() == GeckoProcessType_Default) { 1.730 + // This code interferes with the C++ unit test startup code. 1.731 + nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &res); 1.732 + NS_ENSURE_SUCCESS(res, res); 1.733 + } else { 1.734 + NS_ENSURE_SUCCESS(res = InitNSSInContent(), res); 1.735 + } 1.736 + 1.737 + // Currently no standalone unit tests for DataChannel, 1.738 + // which is the user of mWindow 1.739 + MOZ_ASSERT(aWindow); 1.740 + mWindow = aWindow; 1.741 + NS_ENSURE_STATE(mWindow); 1.742 + 1.743 +#endif // MOZILLA_INTERNAL_API 1.744 + 1.745 + PRTime timestamp = PR_Now(); 1.746 + // Ok if we truncate this. 1.747 + char temp[128]; 1.748 + 1.749 +#ifdef MOZILLA_INTERNAL_API 1.750 + nsAutoCString locationCStr; 1.751 + nsIDOMLocation* location; 1.752 + res = mWindow->GetLocation(&location); 1.753 + 1.754 + if (location && NS_SUCCEEDED(res)) { 1.755 + nsAutoString locationAStr; 1.756 + location->ToString(locationAStr); 1.757 + location->Release(); 1.758 + 1.759 + CopyUTF16toUTF8(locationAStr, locationCStr); 1.760 + } 1.761 + 1.762 + PR_snprintf( 1.763 + temp, 1.764 + sizeof(temp), 1.765 + "%llu (id=%llu url=%s)", 1.766 + static_cast<unsigned long long>(timestamp), 1.767 + static_cast<unsigned long long>(mWindow ? mWindow->WindowID() : 0), 1.768 + locationCStr.get() ? locationCStr.get() : "NULL"); 1.769 + 1.770 +#else 1.771 + PR_snprintf(temp, sizeof(temp), "%llu", (unsigned long long)timestamp); 1.772 +#endif // MOZILLA_INTERNAL_API 1.773 + 1.774 + mName = temp; 1.775 + 1.776 + // Generate a random handle 1.777 + unsigned char handle_bin[8]; 1.778 + SECStatus rv; 1.779 + rv = PK11_GenerateRandom(handle_bin, sizeof(handle_bin)); 1.780 + if (rv != SECSuccess) { 1.781 + MOZ_CRASH(); 1.782 + return NS_ERROR_UNEXPECTED; 1.783 + } 1.784 + 1.785 + char hex[17]; 1.786 + PR_snprintf(hex,sizeof(hex),"%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", 1.787 + handle_bin[0], 1.788 + handle_bin[1], 1.789 + handle_bin[2], 1.790 + handle_bin[3], 1.791 + handle_bin[4], 1.792 + handle_bin[5], 1.793 + handle_bin[6], 1.794 + handle_bin[7]); 1.795 + 1.796 + mHandle = hex; 1.797 + 1.798 + STAMP_TIMECARD(mTimeCard, "Initializing PC Ctx"); 1.799 + res = PeerConnectionCtx::InitializeGlobal(mThread, mSTSThread); 1.800 + NS_ENSURE_SUCCESS(res, res); 1.801 + 1.802 + PeerConnectionCtx *pcctx = PeerConnectionCtx::GetInstance(); 1.803 + MOZ_ASSERT(pcctx); 1.804 + STAMP_TIMECARD(mTimeCard, "Done Initializing PC Ctx"); 1.805 + 1.806 + mInternal->mCall = pcctx->createCall(); 1.807 + if (!mInternal->mCall.get()) { 1.808 + CSFLogError(logTag, "%s: Couldn't Create Call Object", __FUNCTION__); 1.809 + return NS_ERROR_FAILURE; 1.810 + } 1.811 + 1.812 + IceConfiguration converted; 1.813 + if (aRTCConfiguration) { 1.814 + res = ConvertRTCConfiguration(*aRTCConfiguration, &converted); 1.815 + if (NS_FAILED(res)) { 1.816 + CSFLogError(logTag, "%s: Invalid RTCConfiguration", __FUNCTION__); 1.817 + return res; 1.818 + } 1.819 + aConfiguration = &converted; 1.820 + } 1.821 + 1.822 + mMedia = new PeerConnectionMedia(this); 1.823 + 1.824 + // Connect ICE slots. 1.825 + mMedia->SignalIceGatheringStateChange.connect( 1.826 + this, 1.827 + &PeerConnectionImpl::IceGatheringStateChange); 1.828 + mMedia->SignalIceConnectionStateChange.connect( 1.829 + this, 1.830 + &PeerConnectionImpl::IceConnectionStateChange); 1.831 + 1.832 + // Initialize the media object. 1.833 + res = mMedia->Init(aConfiguration->getStunServers(), 1.834 + aConfiguration->getTurnServers()); 1.835 + if (NS_FAILED(res)) { 1.836 + CSFLogError(logTag, "%s: Couldn't initialize media object", __FUNCTION__); 1.837 + return res; 1.838 + } 1.839 + 1.840 + // Store under mHandle 1.841 + mInternal->mCall->setPeerConnection(mHandle); 1.842 + PeerConnectionCtx::GetInstance()->mPeerConnections[mHandle] = this; 1.843 + 1.844 + STAMP_TIMECARD(mTimeCard, "Generating DTLS Identity"); 1.845 + // Create the DTLS Identity 1.846 + mIdentity = DtlsIdentity::Generate(); 1.847 + STAMP_TIMECARD(mTimeCard, "Done Generating DTLS Identity"); 1.848 + 1.849 + if (!mIdentity) { 1.850 + CSFLogError(logTag, "%s: Generate returned NULL", __FUNCTION__); 1.851 + return NS_ERROR_FAILURE; 1.852 + } 1.853 + 1.854 + mFingerprint = mIdentity->GetFormattedFingerprint(); 1.855 + if (mFingerprint.empty()) { 1.856 + CSFLogError(logTag, "%s: unable to get fingerprint", __FUNCTION__); 1.857 + return res; 1.858 + } 1.859 + 1.860 +#ifdef MOZILLA_INTERNAL_API 1.861 + if (mozilla::Preferences::GetBool("media.navigator.load_adapt", false)) { 1.862 + mLoadManager = mozilla::LoadManagerBuild(); 1.863 + } 1.864 +#endif 1.865 + 1.866 + return NS_OK; 1.867 +} 1.868 + 1.869 +RefPtr<DtlsIdentity> const 1.870 +PeerConnectionImpl::GetIdentity() const 1.871 +{ 1.872 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.873 + return mIdentity; 1.874 +} 1.875 + 1.876 +std::string 1.877 +PeerConnectionImpl::GetFingerprint() const 1.878 +{ 1.879 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.880 + return mFingerprint; 1.881 +} 1.882 + 1.883 +NS_IMETHODIMP 1.884 +PeerConnectionImpl::FingerprintSplitHelper(std::string& fingerprint, 1.885 + size_t& spaceIdx) const 1.886 +{ 1.887 + fingerprint = GetFingerprint(); 1.888 + spaceIdx = fingerprint.find_first_of(' '); 1.889 + if (spaceIdx == std::string::npos) { 1.890 + CSFLogError(logTag, "%s: fingerprint is messed up: %s", 1.891 + __FUNCTION__, fingerprint.c_str()); 1.892 + return NS_ERROR_FAILURE; 1.893 + } 1.894 + return NS_OK; 1.895 +} 1.896 + 1.897 +std::string 1.898 +PeerConnectionImpl::GetFingerprintAlgorithm() const 1.899 +{ 1.900 + std::string fp; 1.901 + size_t spc; 1.902 + if (NS_SUCCEEDED(FingerprintSplitHelper(fp, spc))) { 1.903 + return fp.substr(0, spc); 1.904 + } 1.905 + return ""; 1.906 +} 1.907 + 1.908 +std::string 1.909 +PeerConnectionImpl::GetFingerprintHexValue() const 1.910 +{ 1.911 + std::string fp; 1.912 + size_t spc; 1.913 + if (NS_SUCCEEDED(FingerprintSplitHelper(fp, spc))) { 1.914 + return fp.substr(spc + 1); 1.915 + } 1.916 + return ""; 1.917 +} 1.918 + 1.919 + 1.920 +nsresult 1.921 +PeerConnectionImpl::CreateFakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aRetval) 1.922 +{ 1.923 + MOZ_ASSERT(aRetval); 1.924 + PC_AUTO_ENTER_API_CALL(false); 1.925 + 1.926 + bool mute = false; 1.927 + 1.928 + // Hack to allow you to mute the stream 1.929 + if (aHint & MEDIA_STREAM_MUTE) { 1.930 + mute = true; 1.931 + aHint &= ~MEDIA_STREAM_MUTE; 1.932 + } 1.933 + 1.934 + nsRefPtr<DOMMediaStream> stream = MakeMediaStream(mWindow, aHint); 1.935 + if (!stream) { 1.936 + return NS_ERROR_FAILURE; 1.937 + } 1.938 + 1.939 + if (!mute) { 1.940 + if (aHint & DOMMediaStream::HINT_CONTENTS_AUDIO) { 1.941 + new Fake_AudioGenerator(stream); 1.942 + } else { 1.943 +#ifdef MOZILLA_INTERNAL_API 1.944 + new Fake_VideoGenerator(stream); 1.945 +#endif 1.946 + } 1.947 + } 1.948 + 1.949 + stream.forget(aRetval); 1.950 + return NS_OK; 1.951 +} 1.952 + 1.953 +// Stubbing this call out for now. 1.954 +// We can remove it when we are confident of datachannels being started 1.955 +// correctly on SDP negotiation (bug 852908) 1.956 +NS_IMETHODIMP 1.957 +PeerConnectionImpl::ConnectDataConnection(uint16_t aLocalport, 1.958 + uint16_t aRemoteport, 1.959 + uint16_t aNumstreams) 1.960 +{ 1.961 + return NS_OK; // InitializeDataChannel(aLocalport, aRemoteport, aNumstreams); 1.962 +} 1.963 + 1.964 +// Data channels won't work without a window, so in order for the C++ unit 1.965 +// tests to work (it doesn't have a window available) we ifdef the following 1.966 +// two implementations. 1.967 +NS_IMETHODIMP 1.968 +PeerConnectionImpl::EnsureDataConnection(uint16_t aNumstreams) 1.969 +{ 1.970 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.971 + 1.972 +#ifdef MOZILLA_INTERNAL_API 1.973 + if (mDataConnection) { 1.974 + CSFLogDebug(logTag,"%s DataConnection already connected",__FUNCTION__); 1.975 + // Ignore the request to connect when already connected. This entire 1.976 + // implementation is temporary. Ignore aNumstreams as it's merely advisory 1.977 + // and we increase the number of streams dynamically as needed. 1.978 + return NS_OK; 1.979 + } 1.980 + mDataConnection = new DataChannelConnection(this); 1.981 + if (!mDataConnection->Init(5000, aNumstreams, true)) { 1.982 + CSFLogError(logTag,"%s DataConnection Init Failed",__FUNCTION__); 1.983 + return NS_ERROR_FAILURE; 1.984 + } 1.985 + CSFLogDebug(logTag,"%s DataChannelConnection %p attached to %s", 1.986 + __FUNCTION__, (void*) mDataConnection.get(), mHandle.c_str()); 1.987 +#endif 1.988 + return NS_OK; 1.989 +} 1.990 + 1.991 +nsresult 1.992 +PeerConnectionImpl::InitializeDataChannel(int track_id, 1.993 + uint16_t aLocalport, 1.994 + uint16_t aRemoteport, 1.995 + uint16_t aNumstreams) 1.996 +{ 1.997 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.998 + 1.999 +#ifdef MOZILLA_INTERNAL_API 1.1000 + nsresult rv = EnsureDataConnection(aNumstreams); 1.1001 + if (NS_SUCCEEDED(rv)) { 1.1002 + // use the specified TransportFlow 1.1003 + nsRefPtr<TransportFlow> flow = mMedia->GetTransportFlow(track_id, false).get(); 1.1004 + CSFLogDebug(logTag, "Transportflow[%d] = %p", track_id, flow.get()); 1.1005 + if (flow) { 1.1006 + if (mDataConnection->ConnectViaTransportFlow(flow, aLocalport, aRemoteport)) { 1.1007 + return NS_OK; 1.1008 + } 1.1009 + } 1.1010 + // If we inited the DataConnection, call Destroy() before releasing it 1.1011 + mDataConnection->Destroy(); 1.1012 + } 1.1013 + mDataConnection = nullptr; 1.1014 +#endif 1.1015 + return NS_ERROR_FAILURE; 1.1016 +} 1.1017 + 1.1018 +already_AddRefed<nsDOMDataChannel> 1.1019 +PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel, 1.1020 + const nsAString& aProtocol, 1.1021 + uint16_t aType, 1.1022 + bool outOfOrderAllowed, 1.1023 + uint16_t aMaxTime, 1.1024 + uint16_t aMaxNum, 1.1025 + bool aExternalNegotiated, 1.1026 + uint16_t aStream, 1.1027 + ErrorResult &rv) 1.1028 +{ 1.1029 +#ifdef MOZILLA_INTERNAL_API 1.1030 + nsRefPtr<nsDOMDataChannel> result; 1.1031 + rv = CreateDataChannel(aLabel, aProtocol, aType, outOfOrderAllowed, 1.1032 + aMaxTime, aMaxNum, aExternalNegotiated, 1.1033 + aStream, getter_AddRefs(result)); 1.1034 + return result.forget(); 1.1035 +#else 1.1036 + return nullptr; 1.1037 +#endif 1.1038 +} 1.1039 + 1.1040 +NS_IMETHODIMP 1.1041 +PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel, 1.1042 + const nsAString& aProtocol, 1.1043 + uint16_t aType, 1.1044 + bool outOfOrderAllowed, 1.1045 + uint16_t aMaxTime, 1.1046 + uint16_t aMaxNum, 1.1047 + bool aExternalNegotiated, 1.1048 + uint16_t aStream, 1.1049 + nsDOMDataChannel** aRetval) 1.1050 +{ 1.1051 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1052 + MOZ_ASSERT(aRetval); 1.1053 + 1.1054 +#ifdef MOZILLA_INTERNAL_API 1.1055 + nsRefPtr<DataChannel> dataChannel; 1.1056 + DataChannelConnection::Type theType = 1.1057 + static_cast<DataChannelConnection::Type>(aType); 1.1058 + 1.1059 + nsresult rv = EnsureDataConnection(WEBRTC_DATACHANNEL_STREAMS_DEFAULT); 1.1060 + if (NS_FAILED(rv)) { 1.1061 + return rv; 1.1062 + } 1.1063 + dataChannel = mDataConnection->Open( 1.1064 + NS_ConvertUTF16toUTF8(aLabel), NS_ConvertUTF16toUTF8(aProtocol), theType, 1.1065 + !outOfOrderAllowed, 1.1066 + aType == DataChannelConnection::PARTIAL_RELIABLE_REXMIT ? aMaxNum : 1.1067 + (aType == DataChannelConnection::PARTIAL_RELIABLE_TIMED ? aMaxTime : 0), 1.1068 + nullptr, nullptr, aExternalNegotiated, aStream 1.1069 + ); 1.1070 + NS_ENSURE_TRUE(dataChannel,NS_ERROR_FAILURE); 1.1071 + 1.1072 + CSFLogDebug(logTag, "%s: making DOMDataChannel", __FUNCTION__); 1.1073 + 1.1074 + if (!mHaveDataStream) { 1.1075 + // XXX stream_id of 0 might confuse things... 1.1076 + mInternal->mCall->addStream(0, 2, DATA, 0); 1.1077 + mHaveDataStream = true; 1.1078 + } 1.1079 + nsIDOMDataChannel *retval; 1.1080 + rv = NS_NewDOMDataChannel(dataChannel.forget(), mWindow, &retval); 1.1081 + if (NS_FAILED(rv)) { 1.1082 + return rv; 1.1083 + } 1.1084 + *aRetval = static_cast<nsDOMDataChannel*>(retval); 1.1085 +#endif 1.1086 + return NS_OK; 1.1087 +} 1.1088 + 1.1089 +// do_QueryObjectReferent() - Helps get PeerConnectionObserver from nsWeakPtr. 1.1090 +// 1.1091 +// nsWeakPtr deals in XPCOM interfaces, while webidl bindings are concrete objs. 1.1092 +// TODO: Turn this into a central (template) function somewhere (Bug 939178) 1.1093 +// 1.1094 +// Without it, each weak-ref call in this file would look like this: 1.1095 +// 1.1096 +// nsCOMPtr<nsISupportsWeakReference> tmp = do_QueryReferent(mPCObserver); 1.1097 +// if (!tmp) { 1.1098 +// return; 1.1099 +// } 1.1100 +// nsRefPtr<nsSupportsWeakReference> tmp2 = do_QueryObject(tmp); 1.1101 +// nsRefPtr<PeerConnectionObserver> pco = static_cast<PeerConnectionObserver*>(&*tmp2); 1.1102 + 1.1103 +static already_AddRefed<PeerConnectionObserver> 1.1104 +do_QueryObjectReferent(nsIWeakReference* aRawPtr) { 1.1105 + nsCOMPtr<nsISupportsWeakReference> tmp = do_QueryReferent(aRawPtr); 1.1106 + if (!tmp) { 1.1107 + return nullptr; 1.1108 + } 1.1109 + nsRefPtr<nsSupportsWeakReference> tmp2 = do_QueryObject(tmp); 1.1110 + nsRefPtr<PeerConnectionObserver> tmp3 = static_cast<PeerConnectionObserver*>(&*tmp2); 1.1111 + return tmp3.forget(); 1.1112 +} 1.1113 + 1.1114 +void 1.1115 +PeerConnectionImpl::NotifyConnection() 1.1116 +{ 1.1117 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1118 + 1.1119 + CSFLogDebug(logTag, "%s", __FUNCTION__); 1.1120 + 1.1121 +#ifdef MOZILLA_INTERNAL_API 1.1122 + nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); 1.1123 + if (!pco) { 1.1124 + return; 1.1125 + } 1.1126 + WrappableJSErrorResult rv; 1.1127 + RUN_ON_THREAD(mThread, 1.1128 + WrapRunnable(pco, 1.1129 + &PeerConnectionObserver::NotifyConnection, 1.1130 + rv, static_cast<JSCompartment*>(nullptr)), 1.1131 + NS_DISPATCH_NORMAL); 1.1132 +#endif 1.1133 +} 1.1134 + 1.1135 +void 1.1136 +PeerConnectionImpl::NotifyClosedConnection() 1.1137 +{ 1.1138 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1139 + 1.1140 + CSFLogDebug(logTag, "%s", __FUNCTION__); 1.1141 + 1.1142 +#ifdef MOZILLA_INTERNAL_API 1.1143 + nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); 1.1144 + if (!pco) { 1.1145 + return; 1.1146 + } 1.1147 + WrappableJSErrorResult rv; 1.1148 + RUN_ON_THREAD(mThread, 1.1149 + WrapRunnable(pco, &PeerConnectionObserver::NotifyClosedConnection, 1.1150 + rv, static_cast<JSCompartment*>(nullptr)), 1.1151 + NS_DISPATCH_NORMAL); 1.1152 +#endif 1.1153 +} 1.1154 + 1.1155 + 1.1156 +#ifdef MOZILLA_INTERNAL_API 1.1157 +// Not a member function so that we don't need to keep the PC live. 1.1158 +static void NotifyDataChannel_m(nsRefPtr<nsIDOMDataChannel> aChannel, 1.1159 + nsRefPtr<PeerConnectionObserver> aObserver) 1.1160 +{ 1.1161 + MOZ_ASSERT(NS_IsMainThread()); 1.1162 + JSErrorResult rv; 1.1163 + nsRefPtr<nsDOMDataChannel> channel = static_cast<nsDOMDataChannel*>(&*aChannel); 1.1164 + aObserver->NotifyDataChannel(*channel, rv); 1.1165 + NS_DataChannelAppReady(aChannel); 1.1166 +} 1.1167 +#endif 1.1168 + 1.1169 +void 1.1170 +PeerConnectionImpl::NotifyDataChannel(already_AddRefed<DataChannel> aChannel) 1.1171 +{ 1.1172 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1173 + 1.1174 + // XXXkhuey this is completely fucked up. We can't use nsRefPtr<DataChannel> 1.1175 + // here because DataChannel's AddRef/Release are non-virtual and not visible 1.1176 + // if !MOZILLA_INTERNAL_API, but this function leaks the DataChannel if 1.1177 + // !MOZILLA_INTERNAL_API because it never transfers the ref to 1.1178 + // NS_NewDOMDataChannel. 1.1179 + DataChannel* channel = aChannel.take(); 1.1180 + MOZ_ASSERT(channel); 1.1181 + 1.1182 + CSFLogDebug(logTag, "%s: channel: %p", __FUNCTION__, channel); 1.1183 + 1.1184 +#ifdef MOZILLA_INTERNAL_API 1.1185 + nsCOMPtr<nsIDOMDataChannel> domchannel; 1.1186 + nsresult rv = NS_NewDOMDataChannel(already_AddRefed<DataChannel>(channel), 1.1187 + mWindow, getter_AddRefs(domchannel)); 1.1188 + NS_ENSURE_SUCCESS_VOID(rv); 1.1189 + 1.1190 + nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); 1.1191 + if (!pco) { 1.1192 + return; 1.1193 + } 1.1194 + 1.1195 + RUN_ON_THREAD(mThread, 1.1196 + WrapRunnableNM(NotifyDataChannel_m, 1.1197 + domchannel.get(), 1.1198 + pco), 1.1199 + NS_DISPATCH_NORMAL); 1.1200 +#endif 1.1201 +} 1.1202 + 1.1203 +NS_IMETHODIMP 1.1204 +PeerConnectionImpl::CreateOffer(const MediaConstraintsInternal& aConstraints) 1.1205 +{ 1.1206 + return CreateOffer(MediaConstraintsExternal (aConstraints)); 1.1207 +} 1.1208 + 1.1209 +// Used by unit tests and the IDL CreateOffer. 1.1210 +NS_IMETHODIMP 1.1211 +PeerConnectionImpl::CreateOffer(const MediaConstraintsExternal& aConstraints) 1.1212 +{ 1.1213 + PC_AUTO_ENTER_API_CALL(true); 1.1214 + 1.1215 + Timecard *tc = mTimeCard; 1.1216 + mTimeCard = nullptr; 1.1217 + STAMP_TIMECARD(tc, "Create Offer"); 1.1218 + 1.1219 + cc_media_constraints_t* cc_constraints = aConstraints.build(); 1.1220 + NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED); 1.1221 + mInternal->mCall->createOffer(cc_constraints, tc); 1.1222 + return NS_OK; 1.1223 +} 1.1224 + 1.1225 +NS_IMETHODIMP 1.1226 +PeerConnectionImpl::CreateAnswer(const MediaConstraintsInternal& aConstraints) 1.1227 +{ 1.1228 + return CreateAnswer(MediaConstraintsExternal (aConstraints)); 1.1229 +} 1.1230 + 1.1231 +NS_IMETHODIMP 1.1232 +PeerConnectionImpl::CreateAnswer(const MediaConstraintsExternal& aConstraints) 1.1233 +{ 1.1234 + PC_AUTO_ENTER_API_CALL(true); 1.1235 + 1.1236 + Timecard *tc = mTimeCard; 1.1237 + mTimeCard = nullptr; 1.1238 + STAMP_TIMECARD(tc, "Create Answer"); 1.1239 + 1.1240 + cc_media_constraints_t* cc_constraints = aConstraints.build(); 1.1241 + NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED); 1.1242 + mInternal->mCall->createAnswer(cc_constraints, tc); 1.1243 + return NS_OK; 1.1244 +} 1.1245 + 1.1246 +NS_IMETHODIMP 1.1247 +PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP) 1.1248 +{ 1.1249 + PC_AUTO_ENTER_API_CALL(true); 1.1250 + 1.1251 + if (!aSDP) { 1.1252 + CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__); 1.1253 + return NS_ERROR_FAILURE; 1.1254 + } 1.1255 + 1.1256 + Timecard *tc = mTimeCard; 1.1257 + mTimeCard = nullptr; 1.1258 + STAMP_TIMECARD(tc, "Set Local Description"); 1.1259 + 1.1260 + mLocalRequestedSDP = aSDP; 1.1261 + mInternal->mCall->setLocalDescription((cc_jsep_action_t)aAction, 1.1262 + mLocalRequestedSDP, tc); 1.1263 + return NS_OK; 1.1264 +} 1.1265 + 1.1266 +NS_IMETHODIMP 1.1267 +PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP) 1.1268 +{ 1.1269 + PC_AUTO_ENTER_API_CALL(true); 1.1270 + 1.1271 + if (!aSDP) { 1.1272 + CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__); 1.1273 + return NS_ERROR_FAILURE; 1.1274 + } 1.1275 + 1.1276 + Timecard *tc = mTimeCard; 1.1277 + mTimeCard = nullptr; 1.1278 + STAMP_TIMECARD(tc, "Set Remote Description"); 1.1279 + 1.1280 + mRemoteRequestedSDP = aSDP; 1.1281 + mInternal->mCall->setRemoteDescription((cc_jsep_action_t)action, 1.1282 + mRemoteRequestedSDP, tc); 1.1283 + return NS_OK; 1.1284 +} 1.1285 + 1.1286 +// WebRTC uses highres time relative to the UNIX epoch (Jan 1, 1970, UTC). 1.1287 + 1.1288 +#ifdef MOZILLA_INTERNAL_API 1.1289 +nsresult 1.1290 +PeerConnectionImpl::GetTimeSinceEpoch(DOMHighResTimeStamp *result) { 1.1291 + MOZ_ASSERT(NS_IsMainThread()); 1.1292 + nsPerformance *perf = mWindow->GetPerformance(); 1.1293 + NS_ENSURE_TRUE(perf && perf->Timing(), NS_ERROR_UNEXPECTED); 1.1294 + *result = perf->Now() + perf->Timing()->NavigationStart(); 1.1295 + return NS_OK; 1.1296 +} 1.1297 + 1.1298 +class RTCStatsReportInternalConstruct : public RTCStatsReportInternal { 1.1299 +public: 1.1300 + RTCStatsReportInternalConstruct(const nsString &pcid, DOMHighResTimeStamp now) { 1.1301 + mPcid = pcid; 1.1302 + mInboundRTPStreamStats.Construct(); 1.1303 + mOutboundRTPStreamStats.Construct(); 1.1304 + mMediaStreamTrackStats.Construct(); 1.1305 + mMediaStreamStats.Construct(); 1.1306 + mTransportStats.Construct(); 1.1307 + mIceComponentStats.Construct(); 1.1308 + mIceCandidatePairStats.Construct(); 1.1309 + mIceCandidateStats.Construct(); 1.1310 + mCodecStats.Construct(); 1.1311 + } 1.1312 +}; 1.1313 + 1.1314 +// Specialized helper - push map[key] if specified or all map values onto array 1.1315 + 1.1316 +static void 1.1317 +PushBackSelect(nsTArray<RefPtr<MediaPipeline>>& aDst, 1.1318 + const std::map<TrackID, RefPtr<mozilla::MediaPipeline>> & aSrc, 1.1319 + TrackID aKey = 0) { 1.1320 + auto begin = aKey ? aSrc.find(aKey) : aSrc.begin(), it = begin; 1.1321 + for (auto end = (aKey && begin != aSrc.end())? ++begin : aSrc.end(); 1.1322 + it != end; ++it) { 1.1323 + aDst.AppendElement(it->second); 1.1324 + } 1.1325 +} 1.1326 +#endif 1.1327 + 1.1328 +NS_IMETHODIMP 1.1329 +PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector) { 1.1330 + PC_AUTO_ENTER_API_CALL(true); 1.1331 + 1.1332 +#ifdef MOZILLA_INTERNAL_API 1.1333 + if (!mMedia) { 1.1334 + // Since we zero this out before the d'tor, we should check. 1.1335 + return NS_ERROR_UNEXPECTED; 1.1336 + } 1.1337 + 1.1338 + nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(false)); 1.1339 + 1.1340 + nsresult rv = BuildStatsQuery_m(aSelector, query.get()); 1.1341 + 1.1342 + NS_ENSURE_SUCCESS(rv, rv); 1.1343 + 1.1344 + RUN_ON_THREAD(mSTSThread, 1.1345 + WrapRunnableNM(&PeerConnectionImpl::GetStatsForPCObserver_s, 1.1346 + mHandle, 1.1347 + query), 1.1348 + NS_DISPATCH_NORMAL); 1.1349 +#endif 1.1350 + return NS_OK; 1.1351 +} 1.1352 + 1.1353 +NS_IMETHODIMP 1.1354 +PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel) { 1.1355 + PC_AUTO_ENTER_API_CALL(true); 1.1356 + 1.1357 + Timecard *tc = mTimeCard; 1.1358 + mTimeCard = nullptr; 1.1359 + STAMP_TIMECARD(tc, "Add Ice Candidate"); 1.1360 + 1.1361 +#ifdef MOZILLA_INTERNAL_API 1.1362 + // When remote candidates are added before our ICE ctx is up and running 1.1363 + // (the transition to New is async through STS, so this is not impossible), 1.1364 + // we won't record them as trickle candidates. Is this what we want? 1.1365 + if(!mIceStartTime.IsNull()) { 1.1366 + TimeDuration timeDelta = TimeStamp::Now() - mIceStartTime; 1.1367 + if (mIceConnectionState == PCImplIceConnectionState::Failed) { 1.1368 + Telemetry::Accumulate(Telemetry::WEBRTC_ICE_LATE_TRICKLE_ARRIVAL_TIME, 1.1369 + timeDelta.ToMilliseconds()); 1.1370 + } else { 1.1371 + Telemetry::Accumulate(Telemetry::WEBRTC_ICE_ON_TIME_TRICKLE_ARRIVAL_TIME, 1.1372 + timeDelta.ToMilliseconds()); 1.1373 + } 1.1374 + } 1.1375 +#endif 1.1376 + 1.1377 + mInternal->mCall->addICECandidate(aCandidate, aMid, aLevel, tc); 1.1378 + return NS_OK; 1.1379 +} 1.1380 + 1.1381 +NS_IMETHODIMP 1.1382 +PeerConnectionImpl::CloseStreams() { 1.1383 + PC_AUTO_ENTER_API_CALL(false); 1.1384 + 1.1385 + if (mReadyState != PCImplReadyState::Closed) { 1.1386 + ChangeReadyState(PCImplReadyState::Closing); 1.1387 + } 1.1388 + 1.1389 + CSFLogInfo(logTag, "%s: Ending associated call", __FUNCTION__); 1.1390 + 1.1391 + mInternal->mCall->endCall(); 1.1392 + return NS_OK; 1.1393 +} 1.1394 + 1.1395 +NS_IMETHODIMP 1.1396 +PeerConnectionImpl::AddStream(DOMMediaStream &aMediaStream, 1.1397 + const MediaConstraintsInternal& aConstraints) 1.1398 +{ 1.1399 + return AddStream(aMediaStream, MediaConstraintsExternal(aConstraints)); 1.1400 +} 1.1401 + 1.1402 +NS_IMETHODIMP 1.1403 +PeerConnectionImpl::AddStream(DOMMediaStream& aMediaStream, 1.1404 + const MediaConstraintsExternal& aConstraints) { 1.1405 + PC_AUTO_ENTER_API_CALL(true); 1.1406 + 1.1407 + uint32_t hints = aMediaStream.GetHintContents(); 1.1408 + 1.1409 + // XXX Remove this check once addStream has an error callback 1.1410 + // available and/or we have plumbing to handle multiple 1.1411 + // local audio streams. 1.1412 + if ((hints & DOMMediaStream::HINT_CONTENTS_AUDIO) && 1.1413 + mNumAudioStreams > 0) { 1.1414 + CSFLogError(logTag, "%s: Only one local audio stream is supported for now", 1.1415 + __FUNCTION__); 1.1416 + return NS_ERROR_FAILURE; 1.1417 + } 1.1418 + 1.1419 + // XXX Remove this check once addStream has an error callback 1.1420 + // available and/or we have plumbing to handle multiple 1.1421 + // local video streams. 1.1422 + if ((hints & DOMMediaStream::HINT_CONTENTS_VIDEO) && 1.1423 + mNumVideoStreams > 0) { 1.1424 + CSFLogError(logTag, "%s: Only one local video stream is supported for now", 1.1425 + __FUNCTION__); 1.1426 + return NS_ERROR_FAILURE; 1.1427 + } 1.1428 + 1.1429 + uint32_t stream_id; 1.1430 + nsresult res = mMedia->AddStream(&aMediaStream, &stream_id); 1.1431 + if (NS_FAILED(res)) 1.1432 + return res; 1.1433 + 1.1434 + // TODO(ekr@rtfm.com): these integers should be the track IDs 1.1435 + if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { 1.1436 + cc_media_constraints_t* cc_constraints = aConstraints.build(); 1.1437 + NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED); 1.1438 + mInternal->mCall->addStream(stream_id, 0, AUDIO, cc_constraints); 1.1439 + mNumAudioStreams++; 1.1440 + } 1.1441 + 1.1442 + if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { 1.1443 + cc_media_constraints_t* cc_constraints = aConstraints.build(); 1.1444 + NS_ENSURE_TRUE(cc_constraints, NS_ERROR_UNEXPECTED); 1.1445 + mInternal->mCall->addStream(stream_id, 1, VIDEO, cc_constraints); 1.1446 + mNumVideoStreams++; 1.1447 + } 1.1448 + 1.1449 + return NS_OK; 1.1450 +} 1.1451 + 1.1452 +NS_IMETHODIMP 1.1453 +PeerConnectionImpl::RemoveStream(DOMMediaStream& aMediaStream) { 1.1454 + PC_AUTO_ENTER_API_CALL(true); 1.1455 + 1.1456 + uint32_t stream_id; 1.1457 + nsresult res = mMedia->RemoveStream(&aMediaStream, &stream_id); 1.1458 + 1.1459 + if (NS_FAILED(res)) 1.1460 + return res; 1.1461 + 1.1462 + uint32_t hints = aMediaStream.GetHintContents(); 1.1463 + 1.1464 + if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { 1.1465 + mInternal->mCall->removeStream(stream_id, 0, AUDIO); 1.1466 + MOZ_ASSERT(mNumAudioStreams > 0); 1.1467 + mNumAudioStreams--; 1.1468 + } 1.1469 + 1.1470 + if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { 1.1471 + mInternal->mCall->removeStream(stream_id, 1, VIDEO); 1.1472 + MOZ_ASSERT(mNumVideoStreams > 0); 1.1473 + mNumVideoStreams--; 1.1474 + } 1.1475 + 1.1476 + return NS_OK; 1.1477 +} 1.1478 + 1.1479 +/* 1.1480 +NS_IMETHODIMP 1.1481 +PeerConnectionImpl::SetRemoteFingerprint(const char* hash, const char* fingerprint) 1.1482 +{ 1.1483 + MOZ_ASSERT(hash); 1.1484 + MOZ_ASSERT(fingerprint); 1.1485 + 1.1486 + if (fingerprint != nullptr && (strcmp(hash, "sha-1") == 0)) { 1.1487 + mRemoteFingerprint = std::string(fingerprint); 1.1488 + CSFLogDebug(logTag, "Setting remote fingerprint to %s", mRemoteFingerprint.c_str()); 1.1489 + return NS_OK; 1.1490 + } else { 1.1491 + CSFLogError(logTag, "%s: Invalid Remote Finger Print", __FUNCTION__); 1.1492 + return NS_ERROR_FAILURE; 1.1493 + } 1.1494 +} 1.1495 +*/ 1.1496 + 1.1497 +NS_IMETHODIMP 1.1498 +PeerConnectionImpl::GetFingerprint(char** fingerprint) 1.1499 +{ 1.1500 + MOZ_ASSERT(fingerprint); 1.1501 + 1.1502 + if (!mIdentity) { 1.1503 + return NS_ERROR_FAILURE; 1.1504 + } 1.1505 + 1.1506 + char* tmp = new char[mFingerprint.size() + 1]; 1.1507 + std::copy(mFingerprint.begin(), mFingerprint.end(), tmp); 1.1508 + tmp[mFingerprint.size()] = '\0'; 1.1509 + 1.1510 + *fingerprint = tmp; 1.1511 + return NS_OK; 1.1512 +} 1.1513 + 1.1514 +NS_IMETHODIMP 1.1515 +PeerConnectionImpl::GetLocalDescription(char** aSDP) 1.1516 +{ 1.1517 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1518 + MOZ_ASSERT(aSDP); 1.1519 + 1.1520 + char* tmp = new char[mLocalSDP.size() + 1]; 1.1521 + std::copy(mLocalSDP.begin(), mLocalSDP.end(), tmp); 1.1522 + tmp[mLocalSDP.size()] = '\0'; 1.1523 + 1.1524 + *aSDP = tmp; 1.1525 + return NS_OK; 1.1526 +} 1.1527 + 1.1528 +NS_IMETHODIMP 1.1529 +PeerConnectionImpl::GetRemoteDescription(char** aSDP) 1.1530 +{ 1.1531 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1532 + MOZ_ASSERT(aSDP); 1.1533 + 1.1534 + char* tmp = new char[mRemoteSDP.size() + 1]; 1.1535 + std::copy(mRemoteSDP.begin(), mRemoteSDP.end(), tmp); 1.1536 + tmp[mRemoteSDP.size()] = '\0'; 1.1537 + 1.1538 + *aSDP = tmp; 1.1539 + return NS_OK; 1.1540 +} 1.1541 + 1.1542 +NS_IMETHODIMP 1.1543 +PeerConnectionImpl::ReadyState(PCImplReadyState* aState) 1.1544 +{ 1.1545 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1546 + MOZ_ASSERT(aState); 1.1547 + 1.1548 + *aState = mReadyState; 1.1549 + return NS_OK; 1.1550 +} 1.1551 + 1.1552 +NS_IMETHODIMP 1.1553 +PeerConnectionImpl::SignalingState(PCImplSignalingState* aState) 1.1554 +{ 1.1555 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1556 + MOZ_ASSERT(aState); 1.1557 + 1.1558 + *aState = mSignalingState; 1.1559 + return NS_OK; 1.1560 +} 1.1561 + 1.1562 +NS_IMETHODIMP 1.1563 +PeerConnectionImpl::SipccState(PCImplSipccState* aState) 1.1564 +{ 1.1565 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1566 + MOZ_ASSERT(aState); 1.1567 + 1.1568 + PeerConnectionCtx* pcctx = PeerConnectionCtx::GetInstance(); 1.1569 + // Avoid B2G error: operands to ?: have different types 1.1570 + // 'mozilla::dom::PCImplSipccState' and 'mozilla::dom::PCImplSipccState::Enum' 1.1571 + if (pcctx) { 1.1572 + *aState = pcctx->sipcc_state(); 1.1573 + } else { 1.1574 + *aState = PCImplSipccState::Idle; 1.1575 + } 1.1576 + return NS_OK; 1.1577 +} 1.1578 + 1.1579 +NS_IMETHODIMP 1.1580 +PeerConnectionImpl::IceConnectionState(PCImplIceConnectionState* aState) 1.1581 +{ 1.1582 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1583 + MOZ_ASSERT(aState); 1.1584 + 1.1585 + *aState = mIceConnectionState; 1.1586 + return NS_OK; 1.1587 +} 1.1588 + 1.1589 +NS_IMETHODIMP 1.1590 +PeerConnectionImpl::IceGatheringState(PCImplIceGatheringState* aState) 1.1591 +{ 1.1592 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1593 + MOZ_ASSERT(aState); 1.1594 + 1.1595 + *aState = mIceGatheringState; 1.1596 + return NS_OK; 1.1597 +} 1.1598 + 1.1599 +nsresult 1.1600 +PeerConnectionImpl::CheckApiState(bool assert_ice_ready) const 1.1601 +{ 1.1602 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1603 + MOZ_ASSERT(mTrickle || !assert_ice_ready || 1.1604 + (mIceGatheringState == PCImplIceGatheringState::Complete)); 1.1605 + 1.1606 + if (mReadyState == PCImplReadyState::Closed) { 1.1607 + CSFLogError(logTag, "%s: called API while closed", __FUNCTION__); 1.1608 + return NS_ERROR_FAILURE; 1.1609 + } 1.1610 + if (!mMedia) { 1.1611 + CSFLogError(logTag, "%s: called API with disposed mMedia", __FUNCTION__); 1.1612 + return NS_ERROR_FAILURE; 1.1613 + } 1.1614 + return NS_OK; 1.1615 +} 1.1616 + 1.1617 +NS_IMETHODIMP 1.1618 +PeerConnectionImpl::Close() 1.1619 +{ 1.1620 + CSFLogDebug(logTag, "%s: for %s", __FUNCTION__, mHandle.c_str()); 1.1621 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1622 + 1.1623 + nsresult res = CloseInt(); 1.1624 + 1.1625 + SetSignalingState_m(PCImplSignalingState::SignalingClosed); 1.1626 + 1.1627 + return res; 1.1628 +} 1.1629 + 1.1630 + 1.1631 +nsresult 1.1632 +PeerConnectionImpl::CloseInt() 1.1633 +{ 1.1634 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1635 + 1.1636 + // We do this at the end of the call because we want to make sure we've waited 1.1637 + // for all trickle ICE candidates to come in; this can happen well after we've 1.1638 + // transitioned to connected. As a bonus, this allows us to detect race 1.1639 + // conditions where a stats dispatch happens right as the PC closes. 1.1640 + if (!IsClosed()) { 1.1641 + RecordLongtermICEStatistics(); 1.1642 + } 1.1643 + 1.1644 + if (mInternal->mCall) { 1.1645 + CSFLogInfo(logTag, "%s: Closing PeerConnectionImpl %s; " 1.1646 + "ending call", __FUNCTION__, mHandle.c_str()); 1.1647 + mInternal->mCall->endCall(); 1.1648 + } 1.1649 +#ifdef MOZILLA_INTERNAL_API 1.1650 + if (mDataConnection) { 1.1651 + CSFLogInfo(logTag, "%s: Destroying DataChannelConnection %p for %s", 1.1652 + __FUNCTION__, (void *) mDataConnection.get(), mHandle.c_str()); 1.1653 + mDataConnection->Destroy(); 1.1654 + mDataConnection = nullptr; // it may not go away until the runnables are dead 1.1655 + } 1.1656 +#endif 1.1657 + 1.1658 + ShutdownMedia(); 1.1659 + 1.1660 + // DataConnection will need to stay alive until all threads/runnables exit 1.1661 + 1.1662 + return NS_OK; 1.1663 +} 1.1664 + 1.1665 +void 1.1666 +PeerConnectionImpl::ShutdownMedia() 1.1667 +{ 1.1668 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1669 + 1.1670 + if (!mMedia) 1.1671 + return; 1.1672 + 1.1673 +#ifdef MOZILLA_INTERNAL_API 1.1674 + // End of call to be recorded in Telemetry 1.1675 + if (!mStartTime.IsNull()){ 1.1676 + TimeDuration timeDelta = TimeStamp::Now() - mStartTime; 1.1677 + Telemetry::Accumulate(Telemetry::WEBRTC_CALL_DURATION, timeDelta.ToSeconds()); 1.1678 + } 1.1679 +#endif 1.1680 + 1.1681 + // Forget the reference so that we can transfer it to 1.1682 + // SelfDestruct(). 1.1683 + mMedia.forget().take()->SelfDestruct(); 1.1684 +} 1.1685 + 1.1686 +#ifdef MOZILLA_INTERNAL_API 1.1687 +// If NSS is shutting down, then we need to get rid of the DTLS 1.1688 +// identity right now; otherwise, we'll cause wreckage when we do 1.1689 +// finally deallocate it in our destructor. 1.1690 +void 1.1691 +PeerConnectionImpl::virtualDestroyNSSReference() 1.1692 +{ 1.1693 + destructorSafeDestroyNSSReference(); 1.1694 +} 1.1695 + 1.1696 +void 1.1697 +PeerConnectionImpl::destructorSafeDestroyNSSReference() 1.1698 +{ 1.1699 + MOZ_ASSERT(NS_IsMainThread()); 1.1700 + CSFLogDebug(logTag, "%s: NSS shutting down; freeing our DtlsIdentity.", __FUNCTION__); 1.1701 + mIdentity = nullptr; 1.1702 +} 1.1703 +#endif 1.1704 + 1.1705 +void 1.1706 +PeerConnectionImpl::onCallEvent(const OnCallEventArgs& args) 1.1707 +{ 1.1708 + const ccapi_call_event_e &aCallEvent = args.mCallEvent; 1.1709 + const CSF::CC_CallInfoPtr &aInfo = args.mInfo; 1.1710 + 1.1711 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1712 + MOZ_ASSERT(aInfo.get()); 1.1713 + 1.1714 + cc_call_state_t event = aInfo->getCallState(); 1.1715 + std::string statestr = aInfo->callStateToString(event); 1.1716 + Timecard *timecard = aInfo->takeTimecard(); 1.1717 + 1.1718 + if (timecard) { 1.1719 + mTimeCard = timecard; 1.1720 + STAMP_TIMECARD(mTimeCard, "Operation Completed"); 1.1721 + } 1.1722 + 1.1723 + if (CCAPI_CALL_EV_CREATED != aCallEvent && CCAPI_CALL_EV_STATE != aCallEvent) { 1.1724 + CSFLogDebug(logTag, "%s: **** CALL HANDLE IS: %s, **** CALL STATE IS: %s", 1.1725 + __FUNCTION__, mHandle.c_str(), statestr.c_str()); 1.1726 + return; 1.1727 + } 1.1728 + 1.1729 + switch (event) { 1.1730 + case SETLOCALDESCSUCCESS: 1.1731 + case UPDATELOCALDESC: 1.1732 + mLocalSDP = aInfo->getSDP(); 1.1733 + break; 1.1734 + 1.1735 + case SETREMOTEDESCSUCCESS: 1.1736 + case ADDICECANDIDATE: 1.1737 + mRemoteSDP = aInfo->getSDP(); 1.1738 + break; 1.1739 + 1.1740 + case CONNECTED: 1.1741 + CSFLogDebug(logTag, "Setting PeerConnnection state to kActive"); 1.1742 + ChangeReadyState(PCImplReadyState::Active); 1.1743 + break; 1.1744 + default: 1.1745 + break; 1.1746 + } 1.1747 + 1.1748 + nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); 1.1749 + if (!pco) { 1.1750 + return; 1.1751 + } 1.1752 + 1.1753 + PeerConnectionObserverDispatch* runnable = 1.1754 + new PeerConnectionObserverDispatch(aInfo, this, pco); 1.1755 + 1.1756 + if (mThread) { 1.1757 + mThread->Dispatch(runnable, NS_DISPATCH_NORMAL); 1.1758 + return; 1.1759 + } 1.1760 + runnable->Run(); 1.1761 + delete runnable; 1.1762 +} 1.1763 + 1.1764 +void 1.1765 +PeerConnectionImpl::ChangeReadyState(PCImplReadyState aReadyState) 1.1766 +{ 1.1767 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1768 + mReadyState = aReadyState; 1.1769 + 1.1770 + // Note that we are passing an nsRefPtr which keeps the observer live. 1.1771 + nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); 1.1772 + if (!pco) { 1.1773 + return; 1.1774 + } 1.1775 + WrappableJSErrorResult rv; 1.1776 + RUN_ON_THREAD(mThread, 1.1777 + WrapRunnable(pco, 1.1778 + &PeerConnectionObserver::OnStateChange, 1.1779 + PCObserverStateType::ReadyState, 1.1780 + rv, static_cast<JSCompartment*>(nullptr)), 1.1781 + NS_DISPATCH_NORMAL); 1.1782 +} 1.1783 + 1.1784 +void 1.1785 +PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState) 1.1786 +{ 1.1787 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1788 + if (mSignalingState == aSignalingState || 1.1789 + mSignalingState == PCImplSignalingState::SignalingClosed) { 1.1790 + return; 1.1791 + } 1.1792 + 1.1793 + mSignalingState = aSignalingState; 1.1794 + nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); 1.1795 + if (!pco) { 1.1796 + return; 1.1797 + } 1.1798 + JSErrorResult rv; 1.1799 + pco->OnStateChange(PCObserverStateType::SignalingState, rv); 1.1800 + MOZ_ASSERT(!rv.Failed()); 1.1801 +} 1.1802 + 1.1803 +bool 1.1804 +PeerConnectionImpl::IsClosed() const 1.1805 +{ 1.1806 + return mSignalingState == PCImplSignalingState::SignalingClosed; 1.1807 +} 1.1808 + 1.1809 +bool 1.1810 +PeerConnectionImpl::HasMedia() const 1.1811 +{ 1.1812 + return mMedia; 1.1813 +} 1.1814 + 1.1815 +PeerConnectionWrapper::PeerConnectionWrapper(const std::string& handle) 1.1816 + : impl_(nullptr) { 1.1817 + if (PeerConnectionCtx::GetInstance()->mPeerConnections.find(handle) == 1.1818 + PeerConnectionCtx::GetInstance()->mPeerConnections.end()) { 1.1819 + return; 1.1820 + } 1.1821 + 1.1822 + PeerConnectionImpl *impl = PeerConnectionCtx::GetInstance()->mPeerConnections[handle]; 1.1823 + 1.1824 + if (!impl->media()) 1.1825 + return; 1.1826 + 1.1827 + impl_ = impl; 1.1828 +} 1.1829 + 1.1830 +const std::string& 1.1831 +PeerConnectionImpl::GetHandle() 1.1832 +{ 1.1833 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1834 + return mHandle; 1.1835 +} 1.1836 + 1.1837 +const std::string& 1.1838 +PeerConnectionImpl::GetName() 1.1839 +{ 1.1840 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.1841 + return mName; 1.1842 +} 1.1843 + 1.1844 +static mozilla::dom::PCImplIceConnectionState 1.1845 +toDomIceConnectionState(NrIceCtx::ConnectionState state) { 1.1846 + switch (state) { 1.1847 + case NrIceCtx::ICE_CTX_INIT: 1.1848 + return PCImplIceConnectionState::New; 1.1849 + case NrIceCtx::ICE_CTX_CHECKING: 1.1850 + return PCImplIceConnectionState::Checking; 1.1851 + case NrIceCtx::ICE_CTX_OPEN: 1.1852 + return PCImplIceConnectionState::Connected; 1.1853 + case NrIceCtx::ICE_CTX_FAILED: 1.1854 + return PCImplIceConnectionState::Failed; 1.1855 + } 1.1856 + MOZ_CRASH(); 1.1857 +} 1.1858 + 1.1859 +static mozilla::dom::PCImplIceGatheringState 1.1860 +toDomIceGatheringState(NrIceCtx::GatheringState state) { 1.1861 + switch (state) { 1.1862 + case NrIceCtx::ICE_CTX_GATHER_INIT: 1.1863 + return PCImplIceGatheringState::New; 1.1864 + case NrIceCtx::ICE_CTX_GATHER_STARTED: 1.1865 + return PCImplIceGatheringState::Gathering; 1.1866 + case NrIceCtx::ICE_CTX_GATHER_COMPLETE: 1.1867 + return PCImplIceGatheringState::Complete; 1.1868 + } 1.1869 + MOZ_CRASH(); 1.1870 +} 1.1871 + 1.1872 +#ifdef MOZILLA_INTERNAL_API 1.1873 +static bool isDone(PCImplIceConnectionState state) { 1.1874 + return state != PCImplIceConnectionState::Checking && 1.1875 + state != PCImplIceConnectionState::New; 1.1876 +} 1.1877 + 1.1878 +static bool isSucceeded(PCImplIceConnectionState state) { 1.1879 + return state == PCImplIceConnectionState::Connected || 1.1880 + state == PCImplIceConnectionState::Completed; 1.1881 +} 1.1882 + 1.1883 +static bool isFailed(PCImplIceConnectionState state) { 1.1884 + return state == PCImplIceConnectionState::Failed || 1.1885 + state == PCImplIceConnectionState::Disconnected; 1.1886 +} 1.1887 +#endif 1.1888 + 1.1889 +void PeerConnectionImpl::IceConnectionStateChange( 1.1890 + NrIceCtx* ctx, 1.1891 + NrIceCtx::ConnectionState state) { 1.1892 + PC_AUTO_ENTER_API_CALL_VOID_RETURN(false); 1.1893 + 1.1894 + CSFLogDebug(logTag, "%s", __FUNCTION__); 1.1895 + 1.1896 + auto domState = toDomIceConnectionState(state); 1.1897 + 1.1898 +#ifdef MOZILLA_INTERNAL_API 1.1899 + if (!isDone(mIceConnectionState) && isDone(domState)) { 1.1900 + // mIceStartTime can be null if going directly from New to Closed, in which 1.1901 + // case we don't count it as a success or a failure. 1.1902 + if (!mIceStartTime.IsNull()){ 1.1903 + TimeDuration timeDelta = TimeStamp::Now() - mIceStartTime; 1.1904 + if (isSucceeded(domState)) { 1.1905 + Telemetry::Accumulate(Telemetry::WEBRTC_ICE_SUCCESS_TIME, 1.1906 + timeDelta.ToMilliseconds()); 1.1907 + } else if (isFailed(domState)) { 1.1908 + Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FAILURE_TIME, 1.1909 + timeDelta.ToMilliseconds()); 1.1910 + } 1.1911 + } 1.1912 + } 1.1913 +#endif 1.1914 + 1.1915 + mIceConnectionState = domState; 1.1916 + 1.1917 + // Would be nice if we had a means of converting one of these dom enums 1.1918 + // to a string that wasn't almost as much text as this switch statement... 1.1919 + switch (mIceConnectionState) { 1.1920 + case PCImplIceConnectionState::New: 1.1921 + STAMP_TIMECARD(mTimeCard, "Ice state: new"); 1.1922 + break; 1.1923 + case PCImplIceConnectionState::Checking: 1.1924 +#ifdef MOZILLA_INTERNAL_API 1.1925 + // For telemetry 1.1926 + mIceStartTime = TimeStamp::Now(); 1.1927 +#endif 1.1928 + STAMP_TIMECARD(mTimeCard, "Ice state: checking"); 1.1929 + break; 1.1930 + case PCImplIceConnectionState::Connected: 1.1931 + STAMP_TIMECARD(mTimeCard, "Ice state: connected"); 1.1932 + break; 1.1933 + case PCImplIceConnectionState::Completed: 1.1934 + STAMP_TIMECARD(mTimeCard, "Ice state: completed"); 1.1935 + break; 1.1936 + case PCImplIceConnectionState::Failed: 1.1937 + STAMP_TIMECARD(mTimeCard, "Ice state: failed"); 1.1938 + break; 1.1939 + case PCImplIceConnectionState::Disconnected: 1.1940 + STAMP_TIMECARD(mTimeCard, "Ice state: disconnected"); 1.1941 + break; 1.1942 + case PCImplIceConnectionState::Closed: 1.1943 + STAMP_TIMECARD(mTimeCard, "Ice state: closed"); 1.1944 + break; 1.1945 + } 1.1946 + 1.1947 + nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); 1.1948 + if (!pco) { 1.1949 + return; 1.1950 + } 1.1951 + WrappableJSErrorResult rv; 1.1952 + RUN_ON_THREAD(mThread, 1.1953 + WrapRunnable(pco, 1.1954 + &PeerConnectionObserver::OnStateChange, 1.1955 + PCObserverStateType::IceConnectionState, 1.1956 + rv, static_cast<JSCompartment*>(nullptr)), 1.1957 + NS_DISPATCH_NORMAL); 1.1958 +} 1.1959 + 1.1960 +void 1.1961 +PeerConnectionImpl::IceGatheringStateChange( 1.1962 + NrIceCtx* ctx, 1.1963 + NrIceCtx::GatheringState state) 1.1964 +{ 1.1965 + PC_AUTO_ENTER_API_CALL_VOID_RETURN(false); 1.1966 + 1.1967 + CSFLogDebug(logTag, "%s", __FUNCTION__); 1.1968 + 1.1969 + mIceGatheringState = toDomIceGatheringState(state); 1.1970 + 1.1971 + // Would be nice if we had a means of converting one of these dom enums 1.1972 + // to a string that wasn't almost as much text as this switch statement... 1.1973 + switch (mIceGatheringState) { 1.1974 + case PCImplIceGatheringState::New: 1.1975 + STAMP_TIMECARD(mTimeCard, "Ice gathering state: new"); 1.1976 + break; 1.1977 + case PCImplIceGatheringState::Gathering: 1.1978 + STAMP_TIMECARD(mTimeCard, "Ice gathering state: gathering"); 1.1979 + break; 1.1980 + case PCImplIceGatheringState::Complete: 1.1981 + STAMP_TIMECARD(mTimeCard, "Ice state: complete"); 1.1982 + break; 1.1983 + } 1.1984 + 1.1985 + nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); 1.1986 + if (!pco) { 1.1987 + return; 1.1988 + } 1.1989 + WrappableJSErrorResult rv; 1.1990 + RUN_ON_THREAD(mThread, 1.1991 + WrapRunnable(pco, 1.1992 + &PeerConnectionObserver::OnStateChange, 1.1993 + PCObserverStateType::IceGatheringState, 1.1994 + rv, static_cast<JSCompartment*>(nullptr)), 1.1995 + NS_DISPATCH_NORMAL); 1.1996 +} 1.1997 + 1.1998 +#ifdef MOZILLA_INTERNAL_API 1.1999 +nsresult 1.2000 +PeerConnectionImpl::BuildStatsQuery_m( 1.2001 + mozilla::dom::MediaStreamTrack *aSelector, 1.2002 + RTCStatsQuery *query) { 1.2003 + 1.2004 + if (!HasMedia()) { 1.2005 + return NS_OK; 1.2006 + } 1.2007 + 1.2008 + if (!mMedia->ice_ctx() || !mThread) { 1.2009 + CSFLogError(logTag, "Could not build stats query, critical components of " 1.2010 + "PeerConnectionImpl not set."); 1.2011 + return NS_ERROR_UNEXPECTED; 1.2012 + } 1.2013 + 1.2014 + nsresult rv = GetTimeSinceEpoch(&(query->now)); 1.2015 + 1.2016 + if (NS_FAILED(rv)) { 1.2017 + CSFLogError(logTag, "Could not build stats query, could not get timestamp"); 1.2018 + return rv; 1.2019 + } 1.2020 + 1.2021 + // We do not use the pcHandle here, since that's risky to expose to content. 1.2022 + query->report = RTCStatsReportInternalConstruct( 1.2023 + NS_ConvertASCIItoUTF16(mName.c_str()), 1.2024 + query->now); 1.2025 + 1.2026 + // Gather up pipelines from mMedia so they may be inspected on STS 1.2027 + TrackID trackId = aSelector ? aSelector->GetTrackID() : 0; 1.2028 + 1.2029 + for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) { 1.2030 + PushBackSelect(query->pipelines, 1.2031 + mMedia->GetLocalStream(i)->GetPipelines(), 1.2032 + trackId); 1.2033 + } 1.2034 + 1.2035 + for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) { 1.2036 + PushBackSelect(query->pipelines, 1.2037 + mMedia->GetRemoteStream(i)->GetPipelines(), 1.2038 + trackId); 1.2039 + } 1.2040 + 1.2041 + query->iceCtx = mMedia->ice_ctx(); 1.2042 + 1.2043 + // From the list of MediaPipelines, determine the set of NrIceMediaStreams 1.2044 + // we are interested in. 1.2045 + std::set<size_t> levelsToGrab; 1.2046 + if (trackId) { 1.2047 + for (size_t p = 0; p < query->pipelines.Length(); ++p) { 1.2048 + size_t level = query->pipelines[p]->level(); 1.2049 + MOZ_ASSERT(level); 1.2050 + levelsToGrab.insert(level); 1.2051 + } 1.2052 + } else { 1.2053 + // We want to grab all streams, so ignore the pipelines (this also ends up 1.2054 + // grabbing DataChannel streams, which is what we want) 1.2055 + for (size_t s = 0; s < mMedia->num_ice_media_streams(); ++s) { 1.2056 + levelsToGrab.insert(s + 1); // mIceStreams is 0-indexed 1.2057 + } 1.2058 + } 1.2059 + 1.2060 + for (auto s = levelsToGrab.begin(); s != levelsToGrab.end(); ++s) { 1.2061 + // TODO(bcampen@mozilla.com): I may need to revisit this for bundle. 1.2062 + // (Bug 786234) 1.2063 + RefPtr<NrIceMediaStream> temp(mMedia->ice_media_stream(*s - 1)); 1.2064 + RefPtr<TransportFlow> flow(mMedia->GetTransportFlow(*s, false)); 1.2065 + // flow can be null for unused levels, such as unused DataChannels 1.2066 + if (temp && flow) { 1.2067 + query->streams.AppendElement(temp); 1.2068 + } 1.2069 + } 1.2070 + 1.2071 + return rv; 1.2072 +} 1.2073 + 1.2074 +static void ToRTCIceCandidateStats( 1.2075 + const std::vector<NrIceCandidate>& candidates, 1.2076 + RTCStatsType candidateType, 1.2077 + const nsString& componentId, 1.2078 + DOMHighResTimeStamp now, 1.2079 + RTCStatsReportInternal* report) { 1.2080 + 1.2081 + MOZ_ASSERT(report); 1.2082 + for (auto c = candidates.begin(); c != candidates.end(); ++c) { 1.2083 + RTCIceCandidateStats cand; 1.2084 + cand.mType.Construct(candidateType); 1.2085 + NS_ConvertASCIItoUTF16 codeword(c->codeword.c_str()); 1.2086 + cand.mComponentId.Construct(componentId); 1.2087 + cand.mId.Construct(codeword); 1.2088 + cand.mTimestamp.Construct(now); 1.2089 + cand.mCandidateType.Construct( 1.2090 + RTCStatsIceCandidateType(c->type)); 1.2091 + cand.mIpAddress.Construct( 1.2092 + NS_ConvertASCIItoUTF16(c->cand_addr.host.c_str())); 1.2093 + cand.mPortNumber.Construct(c->cand_addr.port); 1.2094 + cand.mTransport.Construct( 1.2095 + NS_ConvertASCIItoUTF16(c->cand_addr.transport.c_str())); 1.2096 + if (candidateType == RTCStatsType::Localcandidate) { 1.2097 + cand.mMozLocalTransport.Construct( 1.2098 + NS_ConvertASCIItoUTF16(c->local_addr.transport.c_str())); 1.2099 + } 1.2100 + report->mIceCandidateStats.Value().AppendElement(cand); 1.2101 + } 1.2102 +} 1.2103 + 1.2104 +static void RecordIceStats_s( 1.2105 + NrIceMediaStream& mediaStream, 1.2106 + bool internalStats, 1.2107 + DOMHighResTimeStamp now, 1.2108 + RTCStatsReportInternal* report) { 1.2109 + 1.2110 + NS_ConvertASCIItoUTF16 componentId(mediaStream.name().c_str()); 1.2111 + if (internalStats) { 1.2112 + std::vector<NrIceCandidatePair> candPairs; 1.2113 + nsresult res = mediaStream.GetCandidatePairs(&candPairs); 1.2114 + if (NS_FAILED(res)) { 1.2115 + CSFLogError(logTag, "%s: Error getting candidate pairs", __FUNCTION__); 1.2116 + return; 1.2117 + } 1.2118 + 1.2119 + for (auto p = candPairs.begin(); p != candPairs.end(); ++p) { 1.2120 + NS_ConvertASCIItoUTF16 codeword(p->codeword.c_str()); 1.2121 + NS_ConvertASCIItoUTF16 localCodeword(p->local.codeword.c_str()); 1.2122 + NS_ConvertASCIItoUTF16 remoteCodeword(p->remote.codeword.c_str()); 1.2123 + // Only expose candidate-pair statistics to chrome, until we've thought 1.2124 + // through the implications of exposing it to content. 1.2125 + 1.2126 + RTCIceCandidatePairStats s; 1.2127 + s.mId.Construct(codeword); 1.2128 + s.mComponentId.Construct(componentId); 1.2129 + s.mTimestamp.Construct(now); 1.2130 + s.mType.Construct(RTCStatsType::Candidatepair); 1.2131 + s.mLocalCandidateId.Construct(localCodeword); 1.2132 + s.mRemoteCandidateId.Construct(remoteCodeword); 1.2133 + s.mNominated.Construct(p->nominated); 1.2134 + s.mMozPriority.Construct(p->priority); 1.2135 + s.mSelected.Construct(p->selected); 1.2136 + s.mState.Construct(RTCStatsIceCandidatePairState(p->state)); 1.2137 + report->mIceCandidatePairStats.Value().AppendElement(s); 1.2138 + } 1.2139 + } 1.2140 + 1.2141 + std::vector<NrIceCandidate> candidates; 1.2142 + if (NS_SUCCEEDED(mediaStream.GetLocalCandidates(&candidates))) { 1.2143 + ToRTCIceCandidateStats(candidates, 1.2144 + RTCStatsType::Localcandidate, 1.2145 + componentId, 1.2146 + now, 1.2147 + report); 1.2148 + } 1.2149 + candidates.clear(); 1.2150 + 1.2151 + if (NS_SUCCEEDED(mediaStream.GetRemoteCandidates(&candidates))) { 1.2152 + ToRTCIceCandidateStats(candidates, 1.2153 + RTCStatsType::Remotecandidate, 1.2154 + componentId, 1.2155 + now, 1.2156 + report); 1.2157 + } 1.2158 +} 1.2159 + 1.2160 +nsresult 1.2161 +PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) { 1.2162 + 1.2163 + ASSERT_ON_THREAD(query->iceCtx->thread()); 1.2164 + 1.2165 + // Gather stats from pipelines provided (can't touch mMedia + stream on STS) 1.2166 + 1.2167 + for (size_t p = 0; p < query->pipelines.Length(); ++p) { 1.2168 + const MediaPipeline& mp = *query->pipelines[p]; 1.2169 + bool isAudio = (mp.Conduit()->type() == MediaSessionConduit::AUDIO); 1.2170 + nsString idstr = isAudio ? 1.2171 + NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_"); 1.2172 + idstr.AppendInt(mp.trackid()); 1.2173 + 1.2174 + switch (mp.direction()) { 1.2175 + case MediaPipeline::TRANSMIT: { 1.2176 + nsString localId = NS_LITERAL_STRING("outbound_rtp_") + idstr; 1.2177 + nsString remoteId; 1.2178 + nsString ssrc; 1.2179 + unsigned int ssrcval; 1.2180 + if (mp.Conduit()->GetLocalSSRC(&ssrcval)) { 1.2181 + ssrc.AppendInt(ssrcval); 1.2182 + } 1.2183 + { 1.2184 + // First, fill in remote stat with rtcp receiver data, if present. 1.2185 + // ReceiverReports have less information than SenderReports, 1.2186 + // so fill in what we can. 1.2187 + DOMHighResTimeStamp timestamp; 1.2188 + uint32_t jitterMs; 1.2189 + uint32_t packetsReceived; 1.2190 + uint64_t bytesReceived; 1.2191 + uint32_t packetsLost; 1.2192 + int32_t rtt; 1.2193 + if (mp.Conduit()->GetRTCPReceiverReport(×tamp, &jitterMs, 1.2194 + &packetsReceived, 1.2195 + &bytesReceived, 1.2196 + &packetsLost, 1.2197 + &rtt)) { 1.2198 + remoteId = NS_LITERAL_STRING("outbound_rtcp_") + idstr; 1.2199 + RTCInboundRTPStreamStats s; 1.2200 + s.mTimestamp.Construct(timestamp); 1.2201 + s.mId.Construct(remoteId); 1.2202 + s.mType.Construct(RTCStatsType::Inboundrtp); 1.2203 + if (ssrc.Length()) { 1.2204 + s.mSsrc.Construct(ssrc); 1.2205 + } 1.2206 + s.mJitter.Construct(double(jitterMs)/1000); 1.2207 + s.mRemoteId.Construct(localId); 1.2208 + s.mIsRemote = true; 1.2209 + s.mPacketsReceived.Construct(packetsReceived); 1.2210 + s.mBytesReceived.Construct(bytesReceived); 1.2211 + s.mPacketsLost.Construct(packetsLost); 1.2212 + s.mMozRtt.Construct(rtt); 1.2213 + query->report.mInboundRTPStreamStats.Value().AppendElement(s); 1.2214 + } 1.2215 + } 1.2216 + // Then, fill in local side (with cross-link to remote only if present) 1.2217 + { 1.2218 + RTCOutboundRTPStreamStats s; 1.2219 + s.mTimestamp.Construct(query->now); 1.2220 + s.mId.Construct(localId); 1.2221 + s.mType.Construct(RTCStatsType::Outboundrtp); 1.2222 + if (ssrc.Length()) { 1.2223 + s.mSsrc.Construct(ssrc); 1.2224 + } 1.2225 + s.mRemoteId.Construct(remoteId); 1.2226 + s.mIsRemote = false; 1.2227 + s.mPacketsSent.Construct(mp.rtp_packets_sent()); 1.2228 + s.mBytesSent.Construct(mp.rtp_bytes_sent()); 1.2229 + query->report.mOutboundRTPStreamStats.Value().AppendElement(s); 1.2230 + } 1.2231 + break; 1.2232 + } 1.2233 + case MediaPipeline::RECEIVE: { 1.2234 + nsString localId = NS_LITERAL_STRING("inbound_rtp_") + idstr; 1.2235 + nsString remoteId; 1.2236 + nsString ssrc; 1.2237 + unsigned int ssrcval; 1.2238 + if (mp.Conduit()->GetRemoteSSRC(&ssrcval)) { 1.2239 + ssrc.AppendInt(ssrcval); 1.2240 + } 1.2241 + { 1.2242 + // First, fill in remote stat with rtcp sender data, if present. 1.2243 + DOMHighResTimeStamp timestamp; 1.2244 + uint32_t packetsSent; 1.2245 + uint64_t bytesSent; 1.2246 + if (mp.Conduit()->GetRTCPSenderReport(×tamp, 1.2247 + &packetsSent, &bytesSent)) { 1.2248 + remoteId = NS_LITERAL_STRING("inbound_rtcp_") + idstr; 1.2249 + RTCOutboundRTPStreamStats s; 1.2250 + s.mTimestamp.Construct(timestamp); 1.2251 + s.mId.Construct(remoteId); 1.2252 + s.mType.Construct(RTCStatsType::Outboundrtp); 1.2253 + if (ssrc.Length()) { 1.2254 + s.mSsrc.Construct(ssrc); 1.2255 + } 1.2256 + s.mRemoteId.Construct(localId); 1.2257 + s.mIsRemote = true; 1.2258 + s.mPacketsSent.Construct(packetsSent); 1.2259 + s.mBytesSent.Construct(bytesSent); 1.2260 + query->report.mOutboundRTPStreamStats.Value().AppendElement(s); 1.2261 + } 1.2262 + } 1.2263 + // Then, fill in local side (with cross-link to remote only if present) 1.2264 + RTCInboundRTPStreamStats s; 1.2265 + s.mTimestamp.Construct(query->now); 1.2266 + s.mId.Construct(localId); 1.2267 + s.mType.Construct(RTCStatsType::Inboundrtp); 1.2268 + if (ssrc.Length()) { 1.2269 + s.mSsrc.Construct(ssrc); 1.2270 + } 1.2271 + unsigned int jitterMs, packetsLost; 1.2272 + if (mp.Conduit()->GetRTPStats(&jitterMs, &packetsLost)) { 1.2273 + s.mJitter.Construct(double(jitterMs)/1000); 1.2274 + s.mPacketsLost.Construct(packetsLost); 1.2275 + } 1.2276 + if (remoteId.Length()) { 1.2277 + s.mRemoteId.Construct(remoteId); 1.2278 + } 1.2279 + s.mIsRemote = false; 1.2280 + s.mPacketsReceived.Construct(mp.rtp_packets_received()); 1.2281 + s.mBytesReceived.Construct(mp.rtp_bytes_received()); 1.2282 + 1.2283 + if (query->internalStats && isAudio) { 1.2284 + int32_t jitterBufferDelay; 1.2285 + int32_t playoutBufferDelay; 1.2286 + int32_t avSyncDelta; 1.2287 + if (mp.Conduit()->GetAVStats(&jitterBufferDelay, 1.2288 + &playoutBufferDelay, 1.2289 + &avSyncDelta)) { 1.2290 + s.mMozJitterBufferDelay.Construct(jitterBufferDelay); 1.2291 + s.mMozAvSyncDelay.Construct(avSyncDelta); 1.2292 + } 1.2293 + } 1.2294 + query->report.mInboundRTPStreamStats.Value().AppendElement(s); 1.2295 + break; 1.2296 + } 1.2297 + } 1.2298 + } 1.2299 + 1.2300 + // Gather stats from ICE 1.2301 + for (size_t s = 0; s != query->streams.Length(); ++s) { 1.2302 + RecordIceStats_s(*query->streams[s], 1.2303 + query->internalStats, 1.2304 + query->now, 1.2305 + &(query->report)); 1.2306 + } 1.2307 + 1.2308 + // NrIceCtx and NrIceMediaStream must be destroyed on STS, so it is not safe 1.2309 + // to dispatch them back to main. 1.2310 + // We clear streams first to maintain destruction order 1.2311 + query->streams.Clear(); 1.2312 + query->iceCtx = nullptr; 1.2313 + return NS_OK; 1.2314 +} 1.2315 + 1.2316 +void PeerConnectionImpl::GetStatsForPCObserver_s( 1.2317 + const std::string& pcHandle, // The Runnable holds the memory 1.2318 + nsAutoPtr<RTCStatsQuery> query) { 1.2319 + 1.2320 + MOZ_ASSERT(query); 1.2321 + MOZ_ASSERT(query->iceCtx); 1.2322 + ASSERT_ON_THREAD(query->iceCtx->thread()); 1.2323 + 1.2324 + nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get()); 1.2325 + 1.2326 + NS_DispatchToMainThread( 1.2327 + WrapRunnableNM( 1.2328 + &PeerConnectionImpl::DeliverStatsReportToPCObserver_m, 1.2329 + pcHandle, 1.2330 + rv, 1.2331 + query), 1.2332 + NS_DISPATCH_NORMAL); 1.2333 +} 1.2334 + 1.2335 +void PeerConnectionImpl::DeliverStatsReportToPCObserver_m( 1.2336 + const std::string& pcHandle, 1.2337 + nsresult result, 1.2338 + nsAutoPtr<RTCStatsQuery> query) { 1.2339 + 1.2340 + // Is the PeerConnectionImpl still around? 1.2341 + PeerConnectionWrapper pcw(pcHandle); 1.2342 + if (pcw.impl()) { 1.2343 + nsRefPtr<PeerConnectionObserver> pco = 1.2344 + do_QueryObjectReferent(pcw.impl()->mPCObserver); 1.2345 + if (pco) { 1.2346 + JSErrorResult rv; 1.2347 + if (NS_SUCCEEDED(result)) { 1.2348 + pco->OnGetStatsSuccess(query->report, rv); 1.2349 + } else { 1.2350 + pco->OnGetStatsError(kInternalError, 1.2351 + ObString("Failed to fetch statistics"), 1.2352 + rv); 1.2353 + } 1.2354 + 1.2355 + if (rv.Failed()) { 1.2356 + CSFLogError(logTag, "Error firing stats observer callback"); 1.2357 + } 1.2358 + } 1.2359 + } 1.2360 +} 1.2361 + 1.2362 +#endif 1.2363 + 1.2364 +void 1.2365 +PeerConnectionImpl::RecordLongtermICEStatistics() { 1.2366 +#ifdef MOZILLA_INTERNAL_API 1.2367 + WebrtcGlobalInformation::StoreLongTermICEStatistics(*this); 1.2368 +#endif 1.2369 +} 1.2370 + 1.2371 +void 1.2372 +PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream) 1.2373 +{ 1.2374 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.2375 + MOZ_ASSERT(aStream); 1.2376 + 1.2377 + CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str()); 1.2378 +} 1.2379 + 1.2380 +void 1.2381 +PeerConnectionImpl::OnSdpParseError(const char *message) { 1.2382 + CSFLogError(logTag, "%s SDP Parse Error: %s", __FUNCTION__, message); 1.2383 + // Save the parsing errors in the PC to be delivered with OnSuccess or OnError 1.2384 + mSDPParseErrorMessages.push_back(message); 1.2385 +} 1.2386 + 1.2387 +void 1.2388 +PeerConnectionImpl::ClearSdpParseErrorMessages() { 1.2389 + mSDPParseErrorMessages.clear(); 1.2390 +} 1.2391 + 1.2392 +const std::vector<std::string> & 1.2393 +PeerConnectionImpl::GetSdpParseErrors() { 1.2394 + return mSDPParseErrorMessages; 1.2395 +} 1.2396 + 1.2397 +#ifdef MOZILLA_INTERNAL_API 1.2398 +//Telemetry for when calls start 1.2399 +void 1.2400 +PeerConnectionImpl::startCallTelem() { 1.2401 + // Start time for calls 1.2402 + mStartTime = TimeStamp::Now(); 1.2403 + 1.2404 + // Increment session call counter 1.2405 +#ifdef MOZILLA_INTERNAL_API 1.2406 + int &cnt = PeerConnectionCtx::GetInstance()->mConnectionCounter; 1.2407 + Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Subtract(cnt); 1.2408 + cnt++; 1.2409 + Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Add(cnt); 1.2410 +#endif 1.2411 +} 1.2412 +#endif 1.2413 + 1.2414 +NS_IMETHODIMP 1.2415 +PeerConnectionImpl::GetLocalStreams(nsTArray<nsRefPtr<DOMMediaStream > >& result) 1.2416 +{ 1.2417 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.2418 +#ifdef MOZILLA_INTERNAL_API 1.2419 + for(uint32_t i=0; i < media()->LocalStreamsLength(); i++) { 1.2420 + LocalSourceStreamInfo *info = media()->GetLocalStream(i); 1.2421 + NS_ENSURE_TRUE(info, NS_ERROR_UNEXPECTED); 1.2422 + result.AppendElement(info->GetMediaStream()); 1.2423 + } 1.2424 + return NS_OK; 1.2425 +#else 1.2426 + return NS_ERROR_FAILURE; 1.2427 +#endif 1.2428 +} 1.2429 + 1.2430 +NS_IMETHODIMP 1.2431 +PeerConnectionImpl::GetRemoteStreams(nsTArray<nsRefPtr<DOMMediaStream > >& result) 1.2432 +{ 1.2433 + PC_AUTO_ENTER_API_CALL_NO_CHECK(); 1.2434 +#ifdef MOZILLA_INTERNAL_API 1.2435 + for(uint32_t i=0; i < media()->RemoteStreamsLength(); i++) { 1.2436 + RemoteSourceStreamInfo *info = media()->GetRemoteStream(i); 1.2437 + NS_ENSURE_TRUE(info, NS_ERROR_UNEXPECTED); 1.2438 + result.AppendElement(info->GetMediaStream()); 1.2439 + } 1.2440 + return NS_OK; 1.2441 +#else 1.2442 + return NS_ERROR_FAILURE; 1.2443 +#endif 1.2444 +} 1.2445 + 1.2446 +} // end sipcc namespace