media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial