author | Byron Campen [:bwc] <docfaraday@gmail.com> |
Thu, 20 Feb 2014 09:35:35 -0800 | |
changeset 172100 | 5cdd9dc14f1e3c45621180df060ea06bd22fd71f |
parent 172099 | 95e8301cb71e00e271cb9461fd79736df70e2d12 |
child 172101 | 4ac5c87912d059a8803937f31c8447f8ecec04c8 |
push id | 26349 |
push user | kwierso@gmail.com |
push date | Thu, 06 Mar 2014 02:08:58 +0000 |
treeherder | mozilla-central@c7d401d189e0 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jib |
bugs | 958221 |
milestone | 30.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1415,16 +1415,23 @@ DOMInterfaces = { 'wrapperCache': False }, 'WebGLVertexArray': { 'nativeType': 'mozilla::WebGLVertexArray', 'headerFile': 'WebGLVertexArray.h' }, +'WebrtcGlobalInformation': { + 'nativeType': 'mozilla::dom::WebrtcGlobalInformation', + 'headerFile': 'WebrtcGlobalInformation.h', + 'wrapperCache': False, + 'concrete': False, +}, + 'WebSocket': { 'headerFile': 'WebSocket.h', }, 'Window': { 'nativeType': 'nsGlobalWindow', # When turning on Window, remember to drop the "'register': False" # from ChromeWindow.
--- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -9,26 +9,24 @@ const {classes: Cc, interfaces: Ci, util Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PopupNotifications.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PeerConnectionIdp", "resource://gre/modules/media/PeerConnectionIdp.jsm"); const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1"; -const WEBRTC_GLOBAL_CONTRACT = "@mozilla.org/dom/webrtcglobalinformation1"; const PC_OBS_CONTRACT = "@mozilla.org/dom/peerconnectionobserver;1"; const PC_ICE_CONTRACT = "@mozilla.org/dom/rtcicecandidate;1"; const PC_SESSION_CONTRACT = "@mozilla.org/dom/rtcsessiondescription;1"; const PC_MANAGER_CONTRACT = "@mozilla.org/dom/peerconnectionmanager;1"; const PC_STATS_CONTRACT = "@mozilla.org/dom/rtcstatsreport;1"; const PC_IDENTITY_CONTRACT = "@mozilla.org/dom/rtcidentityassertion;1"; const PC_CID = Components.ID("{00e0e20d-1494-4776-8e0e-0f0acbea3c79}"); -const WEBRTC_GLOBAL_CID = Components.ID("{f6063d11-f467-49ad-9765-e7923050dc08}"); const PC_OBS_CID = Components.ID("{d1748d4c-7f6a-4dc5-add6-d55b7678537e}"); const PC_ICE_CID = Components.ID("{02b9970c-433d-4cc2-923d-f7028ac66073}"); const PC_SESSION_CID = Components.ID("{1775081b-b62d-4954-8ffe-a067bbf508a7}"); const PC_MANAGER_CID = Components.ID("{7293e901-2be3-4c02-b4bd-cbef6fc24f78}"); const PC_STATS_CID = Components.ID("{7fe6e18b-0da3-4056-bf3b-440ef3809e06}"); const PC_IDENTITY_CID = Components.ID("{1abc7499-3c54-43e0-bd60-686e2703f072}"); // Global list of PeerConnection objects, so they can be cleaned up when @@ -119,82 +117,19 @@ GlobalPCList.prototype = { // this._list shold be empty here this._networkdown = true; } else if (data == "online") { this._networkdown = false; } } }, - getStatsForEachPC: function(callback, errorCallback) { - function getStatsFromPC(pcref) { - try { - pcref.get().getStatsInternal(null, callback, errorCallback); - } catch (e) { - errorCallback("Some error getting stats from PC: " + e.toString()); - } - } - - for (let winId in this._list) { - if (this._list.hasOwnProperty(winId)) { - this.removeNullRefs(winId); - if (this._list[winId]) { - this._list[winId].forEach(getStatsFromPC); - } - } - } - }, - - // TODO(bcampen@mozilla.com): Handle this with a global object in c++ - // (Bug 958221) - getLoggingFromFirstPC: function(pattern, callback, errorCallback) { - for (let winId in this._list) { - this.removeNullRefs(winId); - if (this._list[winId]) { - // We expect removeNullRefs to not leave us with an empty array here - let pcref = this._list[winId][0]; - pcref.get().getLogging(pattern, callback, errorCallback); - return; - } - } - }, }; let _globalPCList = new GlobalPCList(); -function WebrtcGlobalInformation() { -} -WebrtcGlobalInformation.prototype = { - classDescription: "WebrtcGlobalInformation", - classID: WEBRTC_GLOBAL_CID, - contractID: WEBRTC_GLOBAL_CONTRACT, - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]), - - getAllStats: function(successCallback, failureCallback) { - // TODO(bcampen@mozilla.com): Move the work of fanout into c++, and - // only callback once. (Bug 958221) - if (_globalPCList) { - _globalPCList.getStatsForEachPC(successCallback, failureCallback); - } else { - failureCallback("No global PeerConnection list"); - } - }, - - getLogs: function(pattern, callback, errorCallback) { - if (_globalPCList) { - _globalPCList.getLoggingFromFirstPC(pattern, callback, errorCallback); - } else { - errorCallback("No global PeerConnection list"); - } - }, - - getCandPairLogs: function(candPairId, callback, errorCallback) { - this.getLogs('CAND-PAIR(' + candPairId + ')', callback, errorCallback); - }, -}; - function RTCIceCandidate() { this.candidate = this.sdpMid = this.sdpMLineIndex = null; } RTCIceCandidate.prototype = { classDescription: "mozRTCIceCandidate", classID: PC_ICE_CID, contractID: PC_ICE_CONTRACT, QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, @@ -320,18 +255,16 @@ function RTCPeerConnection() { this._closed = false; this._onCreateOfferSuccess = null; this._onCreateOfferFailure = null; this._onCreateAnswerSuccess = null; this._onCreateAnswerFailure = null; this._onGetStatsSuccess = null; this._onGetStatsFailure = null; - this._onGetLoggingSuccess = null; - this._onGetLoggingFailure = null; this._pendingType = null; this._localType = null; this._remoteType = null; this._trickleIce = false; this._peerIdentity = null; /** @@ -917,49 +850,26 @@ RTCPeerConnection.prototype = { changeIceConnectionState: function(state) { this._iceConnectionState = state; this.dispatchEvent(new this._win.Event("iceconnectionstatechange")); }, getStats: function(selector, onSuccess, onError) { this._queueOrRun({ func: this._getStats, - args: [selector, onSuccess, onError, false], - wait: true - }); - }, - - getStatsInternal: function(selector, onSuccess, onError) { - this._queueOrRun({ - func: this._getStats, - args: [selector, onSuccess, onError, true], + args: [selector, onSuccess, onError], wait: true }); }, - _getStats: function(selector, onSuccess, onError, internal) { + _getStats: function(selector, onSuccess, onError) { this._onGetStatsSuccess = onSuccess; this._onGetStatsFailure = onError; - this._impl.getStats(selector, internal); - }, - - getLogging: function(pattern, onSuccess, onError) { - this._queueOrRun({ - func: this._getLogging, - args: [pattern, onSuccess, onError], - wait: true - }); - }, - - _getLogging: function(pattern, onSuccess, onError) { - this._onGetLoggingSuccess = onSuccess; - this._onGetLoggingFailure = onError; - - this._impl.getLogging(pattern); + this._impl.getStats(selector); }, createDataChannel: function(label, dict) { this._checkClosed(); if (dict == undefined) { dict = {}; } if (dict.maxRetransmitNum != undefined) { @@ -1301,26 +1211,16 @@ PeerConnectionObserver.prototype = { this._dompc._executeNext(); }, onGetStatsError: function(code, message) { this.callCB(this._dompc._onGetStatsFailure, new RTCError(code, message)); this._dompc._executeNext(); }, - onGetLoggingSuccess: function(logs) { - this.callCB(this._dompc._onGetLoggingSuccess, logs); - this._dompc._executeNext(); - }, - - onGetLoggingError: function(code, message) { - this.callCB(this._dompc._onGetLoggingFailure, new RTCError(code, message)); - this._dompc._executeNext(); - }, - onAddStream: function(stream) { this.dispatchEvent(new this._dompc._win.MediaStreamEvent("addstream", { stream: stream })); }, onRemoveStream: function(stream, type) { this.dispatchEvent(new this._dompc._win.MediaStreamEvent("removestream", { stream: stream })); @@ -1351,11 +1251,10 @@ PeerConnectionObserver.prototype = { this.NSGetFactory = XPCOMUtils.generateNSGetFactory( [GlobalPCList, RTCIceCandidate, RTCSessionDescription, RTCPeerConnection, RTCStatsReport, RTCIdentityAssertion, - PeerConnectionObserver, - WebrtcGlobalInformation] + PeerConnectionObserver] );
--- a/dom/media/PeerConnection.manifest +++ b/dom/media/PeerConnection.manifest @@ -1,17 +1,15 @@ component {00e0e20d-1494-4776-8e0e-0f0acbea3c79} PeerConnection.js -component {f6063d11-f467-49ad-9765-e7923050dc08} PeerConnection.js component {d1748d4c-7f6a-4dc5-add6-d55b7678537e} PeerConnection.js component {02b9970c-433d-4cc2-923d-f7028ac66073} PeerConnection.js component {1775081b-b62d-4954-8ffe-a067bbf508a7} PeerConnection.js component {7293e901-2be3-4c02-b4bd-cbef6fc24f78} PeerConnection.js component {7fe6e18b-0da3-4056-bf3b-440ef3809e06} PeerConnection.js component {1abc7499-3c54-43e0-bd60-686e2703f072} PeerConnection.js contract @mozilla.org/dom/peerconnection;1 {00e0e20d-1494-4776-8e0e-0f0acbea3c79} -contract @mozilla.org/dom/webrtcglobalinformation;1 {f6063d11-f467-49ad-9765-e7923050dc08} contract @mozilla.org/dom/peerconnectionobserver;1 {d1748d4c-7f6a-4dc5-add6-d55b7678537e} contract @mozilla.org/dom/rtcicecandidate;1 {02b9970c-433d-4cc2-923d-f7028ac66073} contract @mozilla.org/dom/rtcsessiondescription;1 {1775081b-b62d-4954-8ffe-a067bbf508a7} contract @mozilla.org/dom/peerconnectionmanager;1 {7293e901-2be3-4c02-b4bd-cbef6fc24f78} contract @mozilla.org/dom/rtcstatsreport;1 {7fe6e18b-0da3-4056-bf3b-440ef3809e06} contract @mozilla.org/dom/rtcidentityassertion;1 {1abc7499-3c54-43e0-bd60-686e2703f072}
--- a/dom/webidl/PeerConnectionImpl.webidl +++ b/dom/webidl/PeerConnectionImpl.webidl @@ -31,23 +31,17 @@ interface PeerConnectionImpl { [Throws] void setLocalDescription(long action, DOMString sdp); [Throws] void setRemoteDescription(long action, DOMString sdp); /* Stats call, calls either |onGetStatsSuccess| or |onGetStatsError| on our observer. (see the |PeerConnectionObserver| interface) */ [Throws] - void getStats(MediaStreamTrack? selector, boolean internalStats); - - /* Scrapes the RLogRingbuffer, and calls either |onGetLoggingSuccess| - or |onGetLoggingError| on our observer. - (see the |PeerConnectionObserver| interface) */ - [Throws] - void getLogging(DOMString pattern); + void getStats(MediaStreamTrack? selector); /* Adds the stream created by GetUserMedia */ [Throws] void addStream(MediaStream stream, optional MediaConstraintsInternal constraints); [Throws] void removeStream(MediaStream stream); [Throws]
--- a/dom/webidl/PeerConnectionObserver.webidl +++ b/dom/webidl/PeerConnectionObserver.webidl @@ -23,20 +23,16 @@ interface PeerConnectionObserver void onAddIceCandidateSuccess(); void onAddIceCandidateError(unsigned long name, DOMString message); void onIceCandidate(unsigned short level, DOMString mid, DOMString candidate); /* Stats callbacks */ void onGetStatsSuccess(optional RTCStatsReportInternal report); void onGetStatsError(unsigned long name, DOMString message); - /* Logging callbacks */ - void onGetLoggingSuccess(sequence<DOMString> logs); - void onGetLoggingError(unsigned long name, DOMString message); - /* Data channel callbacks */ void notifyDataChannel(DataChannel channel); void notifyConnection(); void notifyClosedConnection(); /* Notification of one of several types of state changed */ void onStateChange(PCObserverStateType state);
--- a/dom/webidl/RTCPeerConnection.webidl +++ b/dom/webidl/RTCPeerConnection.webidl @@ -130,40 +130,20 @@ interface mozRTCPeerConnection : EventTa attribute EventHandler onaddstream; attribute EventHandler onremovestream; attribute EventHandler oniceconnectionstatechange; void getStats (MediaStreamTrack? selector, RTCStatsCallback successCallback, RTCPeerConnectionErrorCallback failureCallback); - [ChromeOnly] - void getStatsInternal (MediaStreamTrack? selector, - RTCStatsCallback successCallback, - RTCPeerConnectionErrorCallback failureCallback); - // Data channel. RTCDataChannel createDataChannel (DOMString label, optional RTCDataChannelInit dataChannelDict); attribute EventHandler ondatachannel; attribute EventHandler onconnection; attribute EventHandler onclosedconnection; [Pref="media.peerconnection.identity.enabled"] attribute EventHandler onidentityresult; [Pref="media.peerconnection.identity.enabled"] attribute EventHandler onpeeridentity; }; -callback RTCLogCallback = void (sequence<DOMString> logMessages); - -[JSImplementation="@mozilla.org/dom/webrtcglobalinformation;1", - ChromeOnly, - Constructor ()] -interface WebrtcGlobalInformation { - void getAllStats(RTCStatsCallback callback, - RTCPeerConnectionErrorCallback errorCallback); - void getCandPairLogs(DOMString candPairId, - RTCLogCallback callback, - RTCPeerConnectionErrorCallback errorCallback); - void getLogs(DOMString pattern, - RTCLogCallback callback, - RTCPeerConnectionErrorCallback errorCallback); -};
new file mode 100644 --- /dev/null +++ b/dom/webidl/WebrtcGlobalInformation.webidl @@ -0,0 +1,25 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +dictionary WebrtcGlobalStatisticsReport { + sequence<RTCStatsReportInternal> reports; +}; + +callback WebrtcGlobalStatisticsCallback = void (WebrtcGlobalStatisticsReport reports); +callback WebrtcGlobalLoggingCallback = void (sequence<DOMString> logMessages); + +[ChromeOnly] +interface WebrtcGlobalInformation { + + [Throws] + static void getAllStats(WebrtcGlobalStatisticsCallback callback); + + [Throws] + static void getLogging(DOMString pattern, + WebrtcGlobalLoggingCallback callback); +}; + +
--- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -464,16 +464,17 @@ if CONFIG['MOZ_WEBGL']: if CONFIG['MOZ_WEBRTC']: WEBIDL_FILES += [ 'DataChannel.webidl', 'MediaStreamList.webidl', 'PeerConnectionImpl.webidl', 'PeerConnectionImplEnums.webidl', 'PeerConnectionObserver.webidl', 'PeerConnectionObserverEnums.webidl', + 'WebrtcGlobalInformation.webidl', ] if CONFIG['MOZ_WEBSPEECH']: WEBIDL_FILES += [ 'SpeechGrammar.webidl', 'SpeechGrammarList.webidl', 'SpeechRecognition.webidl', 'SpeechRecognitionAlternative.webidl',
--- a/media/webrtc/moz.build +++ b/media/webrtc/moz.build @@ -64,16 +64,17 @@ if CONFIG['MOZ_WEBRTC_SIGNALING']: 'signaling/src/media/CSFVideoControlWrapper.cpp', 'signaling/src/media/VcmSIPCCBinding.cpp', 'signaling/src/mediapipeline/MediaPipeline.cpp', 'signaling/src/mediapipeline/SrtpFlow.cpp', 'signaling/src/peerconnection/MediaStreamList.cpp', 'signaling/src/peerconnection/PeerConnectionCtx.cpp', 'signaling/src/peerconnection/PeerConnectionImpl.cpp', 'signaling/src/peerconnection/PeerConnectionMedia.cpp', + 'signaling/src/peerconnection/WebrtcGlobalInformation.cpp', 'signaling/src/sipcc/core/ccapp/call_logger.c', 'signaling/src/sipcc/core/ccapp/capability_set.c', 'signaling/src/sipcc/core/ccapp/cc_call_feature.c', 'signaling/src/sipcc/core/ccapp/cc_config.c', 'signaling/src/sipcc/core/ccapp/cc_device_feature.c', 'signaling/src/sipcc/core/ccapp/cc_device_manager.c', 'signaling/src/sipcc/core/ccapp/cc_info.c', 'signaling/src/sipcc/core/ccapp/cc_service.c',
--- a/media/webrtc/signaling/signaling.gyp +++ b/media/webrtc/signaling/signaling.gyp @@ -188,16 +188,20 @@ # # Conditionals # 'conditions': [ ['build_for_test==0', { 'defines' : [ 'MOZILLA_INTERNAL_API' ], + 'sources': [ + './src/peerconnection/WebrtcGlobalInformation.cpp', + './src/peerconnection/WebrtcGlobalInformation.h', + ], }], ['build_for_test!=0', { 'include_dirs': [ './test' ], 'defines' : [ 'NO_CHROMIUM_LOGGING', 'USE_FAKE_MEDIA_STREAMS',
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h @@ -20,16 +20,20 @@ #include "cpr_stdlib.h" #include "StaticPtr.h" #include "PeerConnectionImpl.h" namespace mozilla { class PeerConnectionCtxShutdown; +namespace dom { +class WebrtcGlobalInformation; +} + // Unit-test helper, because cc_media_constraints_t is hard to forward-declare class MediaConstraintsExternal { public: MediaConstraintsExternal(); MediaConstraintsExternal(const dom::MediaConstraintsInternal &aOther); cc_media_constraints_t* build() const; protected: @@ -71,16 +75,17 @@ class PeerConnectionCtx : public CSF::CC // Create a SIPCC Call CSF::CC_CallPtr createCall(); mozilla::dom::PCImplSipccState sipcc_state() { return mSipccState; } // Make these classes friend so that they can access mPeerconnections. friend class PeerConnectionImpl; friend class PeerConnectionWrapper; + friend class mozilla::dom::WebrtcGlobalInformation; private: // We could make these available only via accessors but it's too much trouble. std::map<const std::string, PeerConnectionImpl *> mPeerConnections; PeerConnectionCtx() : mSipccState(mozilla::dom::PCImplSipccState::Idle), mCCM(nullptr), mDevice(nullptr) {} // This is a singleton, so don't copy construct it, etc.
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -142,16 +142,26 @@ PRLogModuleInfo *signalingLogInfo() { logModuleInfo = PR_NewLogModule("signaling"); } return logModuleInfo; } namespace sipcc { +#ifdef MOZILLA_INTERNAL_API +RTCStatsQuery::RTCStatsQuery(bool internal) : internalStats(internal) { +} + +RTCStatsQuery::~RTCStatsQuery() { + MOZ_ASSERT(NS_IsMainThread()); +} + +#endif + // Getting exceptions back down from PCObserver is generally not harmful. namespace { class JSErrorResult : public ErrorResult { public: ~JSErrorResult() { #ifdef MOZILLA_INTERNAL_API @@ -1277,70 +1287,53 @@ public: mIceCandidateStats.Construct(); mCodecStats.Construct(); } }; // Specialized helper - push map[key] if specified or all map values onto array static void -PushBackSelect(std::vector<RefPtr<MediaPipeline>>& aDst, +PushBackSelect(nsTArray<RefPtr<MediaPipeline>>& aDst, const std::map<TrackID, RefPtr<mozilla::MediaPipeline>> & aSrc, TrackID aKey = 0) { auto begin = aKey ? aSrc.find(aKey) : aSrc.begin(), it = begin; for (auto end = (aKey && begin != aSrc.end())? ++begin : aSrc.end(); it != end; ++it) { - aDst.push_back(it->second); + aDst.AppendElement(it->second); } } #endif NS_IMETHODIMP -PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector, bool internalStats) { +PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector) { PC_AUTO_ENTER_API_CALL(true); #ifdef MOZILLA_INTERNAL_API if (!mMedia) { // Since we zero this out before the d'tor, we should check. return NS_ERROR_UNEXPECTED; } - nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery); + nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(false)); - nsresult rv = BuildStatsQuery_m(aSelector, internalStats, query.get()); + nsresult rv = BuildStatsQuery_m(aSelector, query.get()); NS_ENSURE_SUCCESS(rv, rv); RUN_ON_THREAD(mSTSThread, WrapRunnableNM(&PeerConnectionImpl::GetStatsForPCObserver_s, mHandle, query), NS_DISPATCH_NORMAL); #endif return NS_OK; } NS_IMETHODIMP -PeerConnectionImpl::GetLogging(const nsAString& aPattern) { - PC_AUTO_ENTER_API_CALL_NO_CHECK(); - -#ifdef MOZILLA_INTERNAL_API - std::string pattern(NS_ConvertUTF16toUTF8(aPattern).get()); - RUN_ON_THREAD(mSTSThread, - WrapRunnableNM(&PeerConnectionImpl::GetLogging_s, - mHandle, - mThread, - pattern), - NS_DISPATCH_NORMAL); - -#endif - return NS_OK; -} - -NS_IMETHODIMP PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel) { PC_AUTO_ENTER_API_CALL(true); Timecard *tc = mTimeCard; mTimeCard = nullptr; STAMP_TIMECARD(tc, "Add Ice Candidate"); mInternal->mCall->addICECandidate(aCandidate, aMid, aLevel, tc); @@ -1751,16 +1744,22 @@ PeerConnectionImpl::SetSignalingState_m( if (!pco) { return; } JSErrorResult rv; pco->OnStateChange(PCObserverStateType::SignalingState, rv); MOZ_ASSERT(!rv.Failed()); } +bool +PeerConnectionImpl::IsClosed() const +{ + return !mMedia; +} + PeerConnectionWrapper::PeerConnectionWrapper(const std::string& handle) : impl_(nullptr) { if (PeerConnectionCtx::GetInstance()->mPeerConnections.find(handle) == PeerConnectionCtx::GetInstance()->mPeerConnections.end()) { return; } PeerConnectionImpl *impl = PeerConnectionCtx::GetInstance()->mPeerConnections[handle]; @@ -1928,68 +1927,85 @@ PeerConnectionImpl::IceGatheringStateCha NS_DISPATCH_NORMAL); return NS_OK; } #ifdef MOZILLA_INTERNAL_API nsresult PeerConnectionImpl::BuildStatsQuery_m( mozilla::dom::MediaStreamTrack *aSelector, - bool internalStats, RTCStatsQuery *query) { - if (!mMedia || !mMedia->ice_ctx() || !mThread) { - MOZ_CRASH(); // Crash if debug build, return immediately if not + if (IsClosed()) { + return NS_OK; + } + + if (!mMedia->ice_ctx() || !mThread) { + CSFLogError(logTag, "Could not build stats query, critical components of " + "PeerConnectionImpl not set."); return NS_ERROR_UNEXPECTED; } + nsresult rv = GetTimeSinceEpoch(&(query->now)); + + if (NS_FAILED(rv)) { + CSFLogError(logTag, "Could not build stats query, could not get timestamp"); + return rv; + } + // We do not use the pcHandle here, since that's risky to expose to content. query->report = RTCStatsReportInternalConstruct( NS_ConvertASCIItoUTF16(mName.c_str()), query->now); - // Gather up pipelines from mMedia and dispatch them to STS for inspection + // Gather up pipelines from mMedia so they may be inspected on STS TrackID trackId = aSelector ? aSelector->GetTrackID() : 0; for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) { PushBackSelect(query->pipelines, mMedia->GetLocalStream(i)->GetPipelines(), trackId); } + for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) { PushBackSelect(query->pipelines, mMedia->GetRemoteStream(i)->GetPipelines(), trackId); } query->iceCtx = mMedia->ice_ctx(); // From the list of MediaPipelines, determine the set of NrIceMediaStreams // we are interested in. - for (auto p = query->pipelines.begin(); p != query->pipelines.end(); ++p) { - size_t level = p->get()->level(); + std::set<size_t> streamsGrabbed; + for (size_t p = 0; p < query->pipelines.Length(); ++p) { + + size_t level = query->pipelines[p]->level(); + + // Don't grab the same stream twice, since that causes duplication + // of the ICE stats. + if (streamsGrabbed.count(level)) { + continue; + } + + streamsGrabbed.insert(level); // TODO(bcampen@mozilla.com): I may need to revisit this for bundle. // (Bug 786234) RefPtr<NrIceMediaStream> temp(mMedia->ice_media_stream(level-1)); if (temp.get()) { - query->streams.push_back(temp); + query->streams.AppendElement(temp); } else { CSFLogError(logTag, "Failed to get NrIceMediaStream for level %u " "in %s: %s", uint32_t(level), __FUNCTION__, mHandle.c_str()); MOZ_CRASH(); } } - query->mainThread = mThread; - - nsresult rv = GetTimeSinceEpoch(&(query->now)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; + return rv; } static void ToRTCIceCandidateStats( const std::vector<NrIceCandidate>& candidates, RTCStatsType candidateType, const nsString& componentId, DOMHighResTimeStamp now, RTCStatsReportInternal* report) { @@ -2067,20 +2083,25 @@ static void RecordIceStats_s( } } nsresult PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) { ASSERT_ON_THREAD(query->iceCtx->thread()); + // NrIceCtx must be destroyed on STS, so it is not safe to dispatch it back + // to main. + RefPtr<NrIceCtx> iceCtxTmp(query->iceCtx); + query->iceCtx = nullptr; + // Gather stats from pipelines provided (can't touch mMedia + stream on STS) - for (auto it = query->pipelines.begin(); it != query->pipelines.end(); ++it) { - const MediaPipeline& mp = **it; + for (size_t p = 0; p < query->pipelines.Length(); ++p) { + const MediaPipeline& mp = *query->pipelines[p]; nsString idstr = (mp.Conduit()->type() == MediaSessionConduit::AUDIO) ? NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_"); idstr.AppendInt(mp.trackid()); switch (mp.direction()) { case MediaPipeline::TRANSMIT: { nsString localId = NS_LITERAL_STRING("outbound_rtp_") + idstr; nsString remoteId; @@ -2187,44 +2208,43 @@ PeerConnectionImpl::ExecuteStatsQuery_s( s.mBytesReceived.Construct(mp.rtp_bytes_received()); query->report.mInboundRTPStreamStats.Value().AppendElement(s); break; } } } // Gather stats from ICE - for (auto s = query->streams.begin(); s != query->streams.end(); ++s) { - RecordIceStats_s(**s, query->internalStats, query->now, &(query->report)); + for (size_t s = 0; s != query->streams.Length(); ++s) { + RecordIceStats_s(*query->streams[s], + query->internalStats, + query->now, + &(query->report)); } return NS_OK; } void PeerConnectionImpl::GetStatsForPCObserver_s( const std::string& pcHandle, // The Runnable holds the memory nsAutoPtr<RTCStatsQuery> query) { MOZ_ASSERT(query); MOZ_ASSERT(query->iceCtx); - MOZ_ASSERT(query->mainThread); ASSERT_ON_THREAD(query->iceCtx->thread()); nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get()); - // Some platforms will init the WrapRunnable (thus nulling out query) before - // query->mainThread is evaluated if placed in the RUN_ON_THREAD macro. - nsCOMPtr<nsIThread> mainThread(query->mainThread); - RUN_ON_THREAD(mainThread, - WrapRunnableNM( - &PeerConnectionImpl::DeliverStatsReportToPCObserver_m, - pcHandle, - rv, - query), - NS_DISPATCH_NORMAL); + NS_DispatchToMainThread( + WrapRunnableNM( + &PeerConnectionImpl::DeliverStatsReportToPCObserver_m, + pcHandle, + rv, + query), + NS_DISPATCH_NORMAL); } void PeerConnectionImpl::DeliverStatsReportToPCObserver_m( const std::string& pcHandle, nsresult result, nsAutoPtr<RTCStatsQuery> query) { // Is the PeerConnectionImpl still around? @@ -2244,59 +2264,16 @@ void PeerConnectionImpl::DeliverStatsRep if (rv.Failed()) { CSFLogError(logTag, "Error firing stats observer callback"); } } } } -void PeerConnectionImpl::GetLogging_s(const std::string& pcHandle, - nsCOMPtr<nsIThread> callbackThread, - const std::string& pattern) { - RLogRingBuffer* logs = RLogRingBuffer::GetInstance(); - nsAutoPtr<std::deque<std::string>> result(new std::deque<std::string>); - logs->Filter(pattern, 0, result); - RUN_ON_THREAD(callbackThread, - WrapRunnableNM(&PeerConnectionImpl::OnGetLogging_m, - pcHandle, - pattern, - result), - NS_DISPATCH_NORMAL); -} - -void PeerConnectionImpl::OnGetLogging_m( - const std::string& pcHandle, - const std::string& pattern, - nsAutoPtr<std::deque<std::string>> logging) { - - // Is the PeerConnectionImpl still around? - PeerConnectionWrapper pcw(pcHandle); - if (pcw.impl()) { - nsRefPtr<PeerConnectionObserver> pco = - do_QueryObjectReferent(pcw.impl()->mPCObserver); - if (pco) { - JSErrorResult rv; - if (!logging->empty()) { - Sequence<nsString> nsLogs; - for (auto l = logging->begin(); l != logging->end(); ++l) { - nsLogs.AppendElement(ObString(l->c_str())); - } - pco->OnGetLoggingSuccess(nsLogs, rv); - } else { - pco->OnGetLoggingError(kInternalError, - ObString(("No logging matching pattern " + pattern).c_str()), rv); - } - - if (rv.Failed()) { - CSFLogError(logTag, "Error firing stats observer callback"); - } - } - } -} #endif void PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream) { PC_AUTO_ENTER_API_CALL_NO_CHECK(); MOZ_ASSERT(aStream);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -155,24 +155,30 @@ private: std::vector<NrIceStunServer> mStunServers; std::vector<NrIceTurnServer> mTurnServers; }; #ifdef MOZILLA_INTERNAL_API // Not an inner class so we can forward declare. class RTCStatsQuery { public: + explicit RTCStatsQuery(bool internalStats); + ~RTCStatsQuery(); + + mozilla::dom::RTCStatsReportInternal report; + std::string error; + + private: + friend class PeerConnectionImpl; std::string pcName; bool internalStats; - std::vector<mozilla::RefPtr<mozilla::MediaPipeline>> pipelines; + nsTArray<mozilla::RefPtr<mozilla::MediaPipeline>> pipelines; mozilla::RefPtr<NrIceCtx> iceCtx; - std::vector<mozilla::RefPtr<NrIceMediaStream>> streams; + nsTArray<mozilla::RefPtr<NrIceMediaStream>> streams; DOMHighResTimeStamp now; - mozilla::dom::RTCStatsReportInternal report; - nsCOMPtr<nsIThread> mainThread; }; #endif // MOZILLA_INTERNAL_API // Enter an API call and check that the state is OK, // the PC isn't closed, etc. #define PC_AUTO_ENTER_API_CALL(assert_ice_ready) \ do { \ /* do/while prevents res from conflicting with locals */ \ @@ -325,26 +331,19 @@ public: NS_IMETHODIMP SetRemoteDescription (int32_t aAction, const char* aSDP); void SetRemoteDescription (int32_t aAction, const nsAString& aSDP, ErrorResult &rv) { rv = SetRemoteDescription(aAction, NS_ConvertUTF16toUTF8(aSDP).get()); } NS_IMETHODIMP_TO_ERRORRESULT(GetStats, ErrorResult &rv, - mozilla::dom::MediaStreamTrack *aSelector, - bool internalStats) + mozilla::dom::MediaStreamTrack *aSelector) { - rv = GetStats(aSelector, internalStats); - } - - NS_IMETHODIMP_TO_ERRORRESULT(GetLogging, ErrorResult &rv, - const nsAString& pattern) - { - rv = GetLogging(pattern); + rv = GetStats(aSelector); } NS_IMETHODIMP AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel); void AddIceCandidate(const nsAString& aCandidate, const nsAString& aMid, unsigned short aLevel, ErrorResult &rv) { @@ -502,25 +501,24 @@ public: void ClearSdpParseErrorMessages(); // Called to retreive the list of parsing errors. const std::vector<std::string> &GetSdpParseErrors(); // Sets the RTC Signaling State void SetSignalingState_m(mozilla::dom::PCImplSignalingState aSignalingState); + bool IsClosed() const; + #ifdef MOZILLA_INTERNAL_API // initialize telemetry for when calls start void startCallTelem(); - // This is an intermediate form, to make this refactoring easier to review. - // Please forgive the use of & for out-params. nsresult BuildStatsQuery_m( mozilla::dom::MediaStreamTrack *aSelector, - bool internalStats, RTCStatsQuery *query); static nsresult ExecuteStatsQuery_s(RTCStatsQuery *query); #endif private: PeerConnectionImpl(const PeerConnectionImpl&rhs); PeerConnectionImpl& operator=(PeerConnectionImpl); @@ -574,26 +572,16 @@ private: const std::string& pcHandle, nsAutoPtr<RTCStatsQuery> query); // Sends an RTCStatsReport to JS. Must run on main thread. static void DeliverStatsReportToPCObserver_m( const std::string& pcHandle, nsresult result, nsAutoPtr<RTCStatsQuery> query); - - // Fetches logs matching pattern from RLogRingBuffer. Must be run on STS. - static void GetLogging_s(const std::string& pcHandle, - nsCOMPtr<nsIThread> callbackThread, - const std::string& pattern); - - // Sends logging to JS. Must run on main thread. - static void OnGetLogging_m(const std::string& pcHandle, - const std::string& pattern, - nsAutoPtr<std::deque<std::string>> logging); #endif // Timecard used to measure processing time. This should be the first class // attribute so that we accurately measure the time required to instantiate // any other attributes of this class. Timecard *mTimeCard; // The call
new file mode 100644 --- /dev/null +++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp @@ -0,0 +1,216 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WebrtcGlobalInformation.h" + +#include <deque> +#include <string> + +#include "CSFLog.h" + +#include "mozilla/dom/WebrtcGlobalInformationBinding.h" + +#include "nsAutoPtr.h" +#include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID +#include "nsServiceManagerUtils.h" // do_GetService +#include "mozilla/ErrorResult.h" +#include "mozilla/Vector.h" +#include "nsProxyRelease.h" + +#include "rlogringbuffer.h" +#include "runnable_utils.h" +#include "PeerConnectionCtx.h" +#include "PeerConnectionImpl.h" + +using sipcc::PeerConnectionImpl; +using sipcc::PeerConnectionCtx; +using sipcc::RTCStatsQuery; + +static const char* logTag = "WebrtcGlobalInformation"; + +namespace mozilla { +namespace dom { + +typedef Vector<nsAutoPtr<RTCStatsQuery>> RTCStatsQueries; + +static void OnStatsReport_m( + nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> aStatsCallback, + nsAutoPtr<RTCStatsQueries> aQueryList) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aQueryList); + + WebrtcGlobalStatisticsReport report; + report.mReports.Construct(); + for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) { + MOZ_ASSERT(*q); + report.mReports.Value().AppendElement((*q)->report); + } + + ErrorResult rv; + aStatsCallback.get()->Call(report, rv); + + if (rv.Failed()) { + CSFLogError(logTag, "Error firing stats observer callback"); + } +} + +static void GetAllStats_s( + nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> aStatsCallback, + nsAutoPtr<RTCStatsQueries> aQueryList) +{ + MOZ_ASSERT(aQueryList); + + for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) { + MOZ_ASSERT(*q); + PeerConnectionImpl::ExecuteStatsQuery_s(*q); + } + + NS_DispatchToMainThread(WrapRunnableNM(&OnStatsReport_m, + aStatsCallback, + aQueryList), + NS_DISPATCH_NORMAL); +} + +static void OnGetLogging_m( + nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> aLoggingCallback, + const std::string& aPattern, + nsAutoPtr<std::deque<std::string>> aLogList) +{ + ErrorResult rv; + if (!aLogList->empty()) { + Sequence<nsString> nsLogs; + for (auto l = aLogList->begin(); l != aLogList->end(); ++l) { + nsLogs.AppendElement(NS_ConvertUTF8toUTF16(l->c_str())); + } + aLoggingCallback.get()->Call(nsLogs, rv); + } + + if (rv.Failed()) { + CSFLogError(logTag, "Error firing logging observer callback"); + } +} + +static void GetLogging_s( + nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> aLoggingCallback, + const std::string& aPattern) +{ + RLogRingBuffer* logs = RLogRingBuffer::GetInstance(); + nsAutoPtr<std::deque<std::string>> result(new std::deque<std::string>); + // Might not exist yet. + if (logs) { + logs->Filter(aPattern, 0, result); + } + NS_DispatchToMainThread(WrapRunnableNM(&OnGetLogging_m, + aLoggingCallback, + aPattern, + result), + NS_DISPATCH_NORMAL); +} + + +void +WebrtcGlobalInformation::GetAllStats( + const GlobalObject& aGlobal, + WebrtcGlobalStatisticsCallback& aStatsCallback, + ErrorResult& aRv) +{ + if (!NS_IsMainThread()) { + aRv.Throw(NS_ERROR_NOT_SAME_THREAD); + return; + } + + nsresult rv; + nsCOMPtr<nsIEventTarget> stsThread = + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + if (!stsThread) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + nsAutoPtr<RTCStatsQueries> queries(new RTCStatsQueries); + + // If there is no PeerConnectionCtx, go through the same motions, since + // the API consumer doesn't care why there are no PeerConnectionImpl. + if (PeerConnectionCtx::isActive()) { + PeerConnectionCtx *ctx = PeerConnectionCtx::GetInstance(); + MOZ_ASSERT(ctx); + for (auto p = ctx->mPeerConnections.begin(); + p != ctx->mPeerConnections.end(); + ++p) { + MOZ_ASSERT(p->second); + + if (!p->second->IsClosed()) { + queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true))); + p->second->BuildStatsQuery_m(nullptr, // all tracks + queries->back()); + } + } + } + + // CallbackObject does not support threadsafe refcounting, and must be + // destroyed on main. + nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> callbackHandle( + new nsMainThreadPtrHolder<WebrtcGlobalStatisticsCallback>(&aStatsCallback)); + + rv = RUN_ON_THREAD(stsThread, + WrapRunnableNM(&GetAllStats_s, callbackHandle, queries), + NS_DISPATCH_NORMAL); + + aRv = rv; +} + +void +WebrtcGlobalInformation::GetLogging( + const GlobalObject& aGlobal, + const nsAString& aPattern, + WebrtcGlobalLoggingCallback& aLoggingCallback, + ErrorResult& aRv) +{ + if (!NS_IsMainThread()) { + aRv.Throw(NS_ERROR_NOT_SAME_THREAD); + return; + } + + nsresult rv; + nsCOMPtr<nsIEventTarget> stsThread = + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + if (!stsThread) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + std::string pattern(NS_ConvertUTF16toUTF8(aPattern).get()); + + // CallbackObject does not support threadsafe refcounting, and must be + // destroyed on main. + nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> callbackHandle( + new nsMainThreadPtrHolder<WebrtcGlobalLoggingCallback>(&aLoggingCallback)); + + rv = RUN_ON_THREAD(stsThread, + WrapRunnableNM(&GetLogging_s, callbackHandle, pattern), + NS_DISPATCH_NORMAL); + + if (NS_FAILED(rv)) { + aLoggingCallback.Release(); + } + + aRv = rv; +} + +} // namespace dom +} // namespace mozilla +
new file mode 100644 --- /dev/null +++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.h @@ -0,0 +1,41 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _WEBRTC_GLOBAL_INFORMATION_H_ +#define _WEBRTC_GLOBAL_INFORMATION_H_ + +#include "nsString.h" + +namespace mozilla { +class ErrorResult; + +namespace dom { +class GlobalObject; +class WebrtcGlobalStatisticsCallback; +class WebrtcGlobalLoggingCallback; + +class WebrtcGlobalInformation +{ +public: + static void GetAllStats(const GlobalObject& aGlobal, + WebrtcGlobalStatisticsCallback& aStatsCallback, + ErrorResult& aRv); + + static void GetLogging(const GlobalObject& aGlobal, + const nsAString& aPattern, + WebrtcGlobalLoggingCallback& aLoggingCallback, + ErrorResult& aRv); + +private: + WebrtcGlobalInformation() MOZ_DELETE; + WebrtcGlobalInformation(const WebrtcGlobalInformation& aOrig) MOZ_DELETE; + WebrtcGlobalInformation& operator=( + const WebrtcGlobalInformation& aRhs) MOZ_DELETE; +}; + +} // namespace dom +} // namespace mozilla + +#endif // _WEBRTC_GLOBAL_INFORMATION_H_ +
--- a/toolkit/content/aboutWebrtc.xhtml +++ b/toolkit/content/aboutWebrtc.xhtml @@ -10,72 +10,46 @@ <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Webrtc Internals</title> </head> <script> -function getCandPairLogs(id) { - try { - var wg = new WebrtcGlobalInformation(); - wg.getCandPairLogs(id, displayLogs, console.log); - } catch (e) { - console.log("Exception while creating WebrtcGlobalInformation" + e.toString()); - } -} - -function getAllLogs() { - try { - var wg = new WebrtcGlobalInformation(); - wg.getLogs('', displayLogs, console.log); - } catch(e) { - console.log("Exception while creating WebrtcGlobalInformation" + e.toString()); - } -} - function displayLogs(logs) { var logsDiv = document.getElementById('logs'); while (logsDiv.lastChild) { logsDiv.removeChild(logsDiv.lastChild); } logsDiv.appendChild(document.createElement('h3')) .appendChild(document.createTextNode('Logging:')); logs.forEach(function(logLine){ logsDiv.appendChild(document.createElement('div')) .appendChild(document.createTextNode(logLine)); }); } -function buildCandPairTableRow(candPair, stats) { +function buildCandPairTableRow(candPair, localCand, remoteCand) { var row = document.createElement('tr'); row.onclick = function() { - getCandPairLogs(candPair.id); + WebrtcGlobalInformation.getLogging("CAND-PAIR(" + row.id, displayLogs); } - if (stats.has(candPair.localCandidateId)) { - // TODO: Might have collisions; we either need to be able to supply - // desired type to get(), or do the ugly business of putting the type - // in the id as well as the type field. - var localCand = stats.get(candPair.localCandidateId); + if (localCand) { row.appendChild(document.createElement('td')) .appendChild(document.createTextNode(localCand.ipAddress + ':' + localCand.portNumber + '/' + localCand.candidateType)); } else { row.appendChild(document.createElement('td')) .appendChild(document.createTextNode(candPair.localCandidateId)); } - if (stats.has(candPair.remoteCandidateId)) { - // TODO: Might have collisions; we either need to be able to supply - // desired type to get(), or do the ugly business of putting the type - // in the id as well as the type field. - var remoteCand = stats.get(candPair.remoteCandidateId); + if (remoteCand) { row.appendChild(document.createElement('td')) .appendChild(document.createTextNode(remoteCand.ipAddress + ':' + remoteCand.portNumber + '/' + remoteCand.candidateType)); } else { row.appendChild(document.createElement('td')) .appendChild(document.createTextNode(candPair.remoteCandidateId)); } @@ -87,17 +61,17 @@ function buildCandPairTableRow(candPair, row.appendChild(document.createElement('td')) .appendChild(document.createTextNode(candPair.nominated ? '*' : '')); row.appendChild(document.createElement('td')) .appendChild(document.createTextNode(candPair.selected ? '*' : '')); return row; } -function buildCandTableRow(cand, stats) { +function buildCandTableRow(cand) { var row = document.createElement('tr'); row.appendChild(document.createElement('td')) .appendChild(document.createTextNode(cand.ipAddress + ':' + cand.portNumber)); row.appendChild(document.createElement('td')) .appendChild(document.createTextNode(cand.candidateType)); @@ -163,102 +137,171 @@ function dumpStat(stat, label) { if (stat.bytesSent !== undefined) { statsString += " (" + round00(stat.bytesSent/1024) + " Kb)"; } } div.appendChild(document.createTextNode(statsString)); return div; } -function buildPcDiv(stats, pcDivId) { +function buildPcDiv(stats, pcDivHeading) { var newPcDiv = document.createElement('div'); var heading = document.createElement('h3'); - heading.appendChild(document.createTextNode(pcDivId)); + heading.appendChild(document.createTextNode(pcDivHeading)); newPcDiv.appendChild(heading); - var subDivs = {}; + // First, ICE stats + var iceHeading = document.createElement('h4'); + iceHeading.appendChild(document.createTextNode("ICE statistics")); + newPcDiv.appendChild(iceHeading); + + var iceTablesByComponent = {}; - stats.forEach(function(stat) { - if (!stat.componentId) { - if (!stat.isRemote) { - newPcDiv.appendChild(document.createElement('h4')) - .appendChild(document.createTextNode(stat.id)); - if (stat.remoteId) { - newPcDiv.appendChild(dumpStat(stat, "Local: ")); - newPcDiv.appendChild(dumpStat(stats.get(stat.remoteId), "Remote: ")); - } else { - newPcDiv.appendChild(dumpStat(stat, "")); - } - } - return; - } - - if (!subDivs[stat.componentId]) { - subDivs[stat.componentId] = { - candPairTable: buildEmptyCandPairTable(), - localCandTable: buildEmptyCandTable(true), - remoteCandTable: buildEmptyCandTable(false) + function getIceTables(componentId) { + if (!iceTablesByComponent[componentId]) { + iceTablesByComponent[componentId] = { + candidatePairTable: buildEmptyCandPairTable(), + localCandidateTable: buildEmptyCandTable(true), + remoteCandidateTable: buildEmptyCandTable(false) }; } + return iceTablesByComponent[componentId]; + } - var subDiv = subDivs[stat.componentId]; + // Candidates + var candidateMap = {}; // Used later to speed up recording of candidate pairs + + if (stats.iceCandidateStats) { + stats.iceCandidateStats.forEach(function(cand) { + var tables = getIceTables(cand.componentId); + + candidateMap[cand.id] = cand; - if (stat.type == 'candidatepair') { - subDiv.candPairTable.appendChild(buildCandPairTableRow(stat, stats)); - } else if (stat.type == 'localcandidate') { - subDiv.localCandTable.appendChild(buildCandTableRow(stat, stats)); - } else if (stat.type == 'remotecandidate') { - subDiv.remoteCandTable.appendChild(buildCandTableRow(stat, stats)); - } - }); + if (cand.type == "localcandidate") { + tables.localCandidateTable.appendChild(buildCandTableRow(cand)); + } else { + tables.remoteCandidateTable.appendChild(buildCandTableRow(cand)); + } + }); + } - for (var cid in subDivs) { - if (subDivs.hasOwnProperty(cid)) { - var subDiv = subDivs[cid]; + // Candidate pairs + if (stats.iceCandidatePairStats) { + stats.iceCandidatePairStats.forEach(function(candPair) { + var candPairTable = + getIceTables(candPair.componentId).candidatePairTable; + candPairTable.appendChild( + buildCandPairTableRow(candPair, + candidateMap[candPair.localCandidateId], + candidateMap[candPair.remoteCandidateId])); + }); + } + + // Now that tables are completely built, put them on the page. + for (var cid in iceTablesByComponent) { + if (iceTablesByComponent.hasOwnProperty(cid)) { + var tables = iceTablesByComponent[cid]; newPcDiv.appendChild(document.createElement('h4')) .appendChild(document.createTextNode(cid)); - newPcDiv.appendChild(subDiv.candPairTable); - newPcDiv.appendChild(subDiv.localCandTable); - newPcDiv.appendChild(subDiv.remoteCandTable); + newPcDiv.appendChild(tables.candidatePairTable); + newPcDiv.appendChild(tables.localCandidateTable); + newPcDiv.appendChild(tables.remoteCandidateTable); + } + } + + // end of ICE stats + + // Now, RTP stats + var rtpHeading = document.createElement('h4'); + rtpHeading.appendChild(document.createTextNode("RTP statistics")); + newPcDiv.appendChild(rtpHeading); + + // Build map from id -> remote RTP stats (ie; stats obtained from RTCP + // from the other end). This allows us to pair up local/remote stats for + // the same stream more easily. + var remoteRtpStatsMap = {}; + + var addRemoteStatToMap = function (rtpStat) { + if (rtpStat.isRemote) { + remoteRtpStatsMap[rtpStat.id] = rtpStat; } } + if (stats.inboundRTPStreamStats) { + stats.inboundRTPStreamStats.forEach(addRemoteStatToMap); + } + + if (stats.outboundRTPStreamStats) { + stats.outboundRTPStreamStats.forEach(addRemoteStatToMap); + } + + var addRtpStatPairToDocument = function (rtpStat) { + if (!rtpStat.isRemote) { + newPcDiv.appendChild(document.createElement('h5')) + .appendChild(document.createTextNode(rtpStat.id)); + newPcDiv.appendChild(dumpStat(rtpStat, "Local: ")); + + // Might not be receiving RTCP, so we have no idea what the + // statistics look like from the perspective of the other end. + if (rtpStat.remoteId) { + var remoteRtpStat = remoteRtpStatsMap[rtpStat.remoteId]; + newPcDiv.appendChild(dumpStat(remoteRtpStat, "Remote: ")); + } + } + } + + if (stats.outboundRTPStreamStats) { + stats.outboundRTPStreamStats.forEach(addRtpStatPairToDocument); + } + + if (stats.inboundRTPStreamStats) { + stats.inboundRTPStreamStats.forEach(addRtpStatPairToDocument); + } + return newPcDiv; } -function displayStats(obj) { +function displayStats(globalReport) { console.log("Got stats callback."); - var pcid = obj.mozPcid; - var pcDivId = 'PeerConnection:' + pcid; + globalReport.reports.forEach(function (report) { + var pcDivHeading = 'PeerConnection:' + report.pcid; - var pcDiv = document.getElementById(pcDivId); - var newPcDiv = buildPcDiv(obj, pcDivId); - newPcDiv.id = pcDivId; + var pcDiv = document.getElementById(pcDivHeading); + var newPcDiv = buildPcDiv(report, pcDivHeading); + newPcDiv.id = pcDivHeading; - if (!pcDiv) { - document.getElementById('stats').appendChild(newPcDiv); - } else { - document.getElementById('stats').replaceChild(newPcDiv, pcDiv); - } -} + if (!pcDiv) { + document.getElementById('stats').appendChild(newPcDiv); + } else { + document.getElementById('stats').replaceChild(newPcDiv, pcDiv); + } + }); + + globalReport.errors.forEach(function (error) { + var pcDivHeading = 'PeerConnection:' + error.pcid; -function refreshStats() { - try { - var wg = new WebrtcGlobalInformation(); - wg.getAllStats(displayStats, console.log); - } catch(e) { - console.log("Exception while creating WebrtcGlobalInformation:" + e.toString()); - } + var pcDiv = document.getElementById(pcDivHeading); + var newPcDiv = buildPcDiv(error, pcDivHeading); + newPcDiv.id = pcDivHeading; + + if (pcDiv) { + document.getElementById('stats').replaceChild(newPcDiv, pcDiv); + } else { + document.getElementById('stats').appendChild(newPcDiv); + } + }); } </script> - <body id="body" onload="refreshStats()"> + <body id="body" onload="WebrtcGlobalInformation.getAllStats(displayStats)"> <div id="stats"> </div> - <button onclick="getAllLogs()">Show/refresh logging</button> + <button onclick="WebrtcGlobalInformation.getLogging('', displayLogs)"> + Show/refresh logging + </button> <div id="logs"> </div> </body> </html> <!-- vim: softtabstop=2:shiftwidth=2:expandtab -->