Bug 1355220 add RTCRtpSender/Receiver.getStats;r?jib
MozReview-Commit-ID: LZ4ItjFYxmk
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -1528,17 +1528,17 @@ class PeerConnectionObserver {
onRemoveStream(stream) {
this.dispatchEvent(new this._dompc._win.MediaStreamEvent("removestream",
{ stream }));
}
onAddTrack(track, streams) {
let pc = this._dompc;
let receiver = pc._win.RTCRtpReceiver._create(pc._win,
- new RTCRtpReceiver(this,
+ new RTCRtpReceiver(pc,
track));
pc._receivers.push(receiver);
let ev = new pc._win.RTCTrackEvent("track", { receiver, track, streams });
this.dispatchEvent(ev);
// Fire legacy event as well for a little bit.
ev = new pc._win.MediaStreamTrackEvent("addtrack", { track });
this.dispatchEvent(ev);
@@ -1660,27 +1660,37 @@ class RTCRtpSender {
setParameters(parameters) {
return this._pc._win.Promise.resolve()
.then(() => this._pc._setParameters(this, parameters));
}
getParameters() {
return this._pc._getParameters(this);
}
+
+ getStats() {
+ return this._pc._async(
+ async () => this._pc._getStats(this.track));
+ }
}
setupPrototype(RTCRtpSender, {
classID: PC_SENDER_CID,
contractID: PC_SENDER_CONTRACT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
});
class RTCRtpReceiver {
constructor(pc, track) {
Object.assign(this, { _pc: pc, track });
}
+
+ getStats() {
+ return this._pc._async(
+ async () => this._pc.getStats(this.track));
+ }
}
setupPrototype(RTCRtpReceiver, {
classID: PC_RECEIVER_CID,
contractID: PC_RECEIVER_CONTRACT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports])
});
class CreateOfferRequest {
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -282,8 +282,10 @@ skip-if = (android_version == '18') # an
skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
[test_peerConnection_threeUnbundledConnections.html]
skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
[test_selftest.html]
# Bug 1227781: Crash with bogus TURN server.
[test_peerConnection_bug1227781.html]
[test_peerConnection_stats.html]
skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
+[test_peerConnection_sender_and_receiver_stats.html]
+skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -813,16 +813,33 @@ function PeerConnectionWrapper(label, co
this.dataChannels.push(wrapper);
});
createOneShotEventWrapper(this, this._pc, 'signalingstatechange');
createOneShotEventWrapper(this, this._pc, 'negotiationneeded');
}
PeerConnectionWrapper.prototype = {
+ /**
+ * Returns the senders
+ *
+ * @returns {sequence<RTCRtpSender>} the senders
+ */
+ getSenders: function() {
+ return this._pc.getSenders();
+ },
+
+ /**
+ * Returns the getters
+ *
+ * @returns {sequence<RTCRtpReceiver>} the receivers
+ */
+ getReceivers: function() {
+ return this._pc.getReceivers();
+ },
/**
* Returns the local description.
*
* @returns {object} The local description
*/
get localDescription() {
return this._pc.localDescription;
@@ -1476,16 +1493,57 @@ PeerConnectionWrapper.prototype = {
Object.keys(this.expectedRemoteTrackInfoById)
.map(id => this.remoteMediaElements
.find(e => e.srcObject.getTracks().some(t => t.id == id)))
.map(e => this.waitForMediaElementFlow(e)),
this._pc.getSenders().map(sender => this.waitForRtpFlow(sender.track)),
this._pc.getReceivers().map(receiver => this.waitForRtpFlow(receiver.track))));
},
+ async waitForSyncedRtcp() {
+ // Ensures that RTCP is present
+ let ensureSyncedRtcp = async () => {
+ let report = await this._pc.getStats();
+ for (let [k, v] of report) {
+ if (v.type.endsWith("bound-rtp") && !v.remoteId) {
+ info(v.id + " is missing remoteId: " + JSON.stringify(v));
+ return null;
+ }
+ if (v.type == "inbound-rtp" && v.isRemote == true
+ && v.roundTripTime === undefined) {
+ info(v.id + " is missing roundTripTime: " + JSON.stringify(v));
+ return null;
+ }
+ }
+ return report;
+ }
+ let attempts = 0;
+ // Time-units are MS
+ const waitPeriod = 500;
+ const maxTime = 15000;
+ for (let totalTime = maxTime; totalTime > 0; totalTime -= waitPeriod) {
+ try {
+ let syncedStats = await ensureSyncedRtcp();
+ if (syncedStats) {
+ return syncedStats;
+ }
+ } catch (e) {
+ info(e);
+ info(e.stack);
+ throw e;
+ }
+ attempts += 1;
+ info("waitForSyncedRtcp: no synced RTCP on attempt" + attempts
+ + ", retrying.\n");
+ await wait(waitPeriod);
+ }
+ throw Error("Waiting for synced RTCP timed out after at least "
+ + maxTime + "ms");
+ },
+
/**
* Check that correct audio (typically a flat tone) is flowing to this
* PeerConnection. Uses WebAudio AnalyserNodes to compare input and output
* audio data in the frequency domain.
*
* @param {object} from
* A PeerConnectionWrapper whose audio RTPSender we use as source for
* the audio flow check.
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_sender_and_receiver_stats.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+ createHTML({
+ bug: "1355220",
+ title: "RTCRtpSender.getStats() and RTCRtpReceiver.getStats()",
+ visible: true
+ });
+
+ var test;
+
+ var checkStats = (sndReport, rcvReport, mediaType) => {
+ // Returns SSRCs and checks that the tracks are of the correct mediaType
+ let getSsrcs = (report, kind) => {
+ return [...report.values()]
+ .filter(stat => stat.type.endsWith("bound-rtp")).map(stat =>{
+ is(stat.mediaType, kind, "mediaType of " + stat.id
+ + " is expected type " + kind);
+ return stat.ssrc;
+ }).sort().join("|");
+ };
+ let sndSsrcs = getSsrcs(sndReport, mediaType);
+ let rcvSsrcs = getSsrcs(rcvReport, mediaType);
+ ok(sndSsrcs, "sender SSRCs is not empty");
+ ok(rcvSsrcs, "receiver SSRCs is not empty");
+ is(sndSsrcs, rcvSsrcs, "sender SSRCs match receiver SSRCs");
+ };
+
+ // This MUST be run after PC_*_WAIT_FOR_MEDIA_FLOW to ensure that we have RTP
+ // before checking for RTCP.
+ // It will throw UnsyncedRtcpError if it times out waiting for sync.
+
+ var test;
+ runNetworkTest(function (options) {
+ test = new PeerConnectionTest(options);
+ test.chain.insertAfter("PC_REMOTE_WAIT_FOR_MEDIA_FLOW",
+ async function PC_LOCAL_AND_REMOTE_CHECK_SENDER_RECEIVER_STATS(test) {
+ return Promise.all([test.pcLocal.waitForSyncedRtcp(),
+ test.pcRemote.waitForSyncedRtcp()])
+ .then(async () => {
+ let senders = test.pcLocal.getSenders();
+ let receivers = test.pcRemote.getReceivers();
+ is(senders.length, 2, "Have exactly two senders.");
+ is(receivers.length, 2, "Have exactly two receivers.");
+ for(let kind of ["audio", "video"]) {
+ let senderStats =
+ await senders.find(s => s.track.kind == kind).getStats();
+ is(senders.filter(s => s.track.kind == kind).length, 1,
+ "Exactly 1 sender of kind " + kind);
+ let receiverStats =
+ await receivers.find(r => r.track.kind == kind).getStats();
+ is(receivers.filter(r => r.track.kind == kind).length, 1,
+ "Exactly 1 receiver of kind " + kind);
+
+ checkStats(senderStats, receiverStats, kind);
+ }
+ })
+ });
+ test.setMediaConstraints([{audio: true}, {video: true}], []);
+ test.run();
+ });
+
+</script>
+</pre>
+</body>
+</html>
\ No newline at end of file
--- a/dom/webidl/RTCRtpReceiver.webidl
+++ b/dom/webidl/RTCRtpReceiver.webidl
@@ -6,9 +6,10 @@
* The origin of this IDL file is
* http://lists.w3.org/Archives/Public/public-webrtc/2014May/0067.html
*/
[Pref="media.peerconnection.enabled",
JSImplementation="@mozilla.org/dom/rtpreceiver;1"]
interface RTCRtpReceiver {
readonly attribute MediaStreamTrack track;
+ Promise<RTCStatsReport> getStats();
};
--- a/dom/webidl/RTCRtpSender.webidl
+++ b/dom/webidl/RTCRtpSender.webidl
@@ -68,11 +68,12 @@ dictionary RTCRtpParameters {
[Pref="media.peerconnection.enabled",
JSImplementation="@mozilla.org/dom/rtpsender;1"]
interface RTCRtpSender {
readonly attribute MediaStreamTrack track;
Promise<void> setParameters (optional RTCRtpParameters parameters);
RTCRtpParameters getParameters();
Promise<void> replaceTrack(MediaStreamTrack track);
+ Promise<RTCStatsReport> getStats();
[Pref="media.peerconnection.dtmf.enabled"]
readonly attribute RTCDTMFSender? dtmf;
};