Skip to content

Commit

Permalink
Add support for RTCPeerConnection.getStats
Browse files Browse the repository at this point in the history
  • Loading branch information
oNaiPs authored and saghul committed Nov 21, 2016
1 parent ec00e2a commit d40e7da
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 5 deletions.
1 change: 0 additions & 1 deletion docs/iosrtc.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ All the methods are implemented in both fashions: the deprecated callbacks based
*TODO:*

* `updateIce()` method.
* `getStats()` method.
* Can not use `id` value greater than 1023 in the config object for `createDataChannel()` (see [issue #4618](https://code.google.com/p/webrtc/issues/detail?id=4618)).


Expand Down
81 changes: 81 additions & 0 deletions js/RTCPeerConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ var
RTCIceCandidate = require('./RTCIceCandidate'),
RTCDataChannel = require('./RTCDataChannel'),
RTCDTMFSender = require('./RTCDTMFSender'),
RTCStatsResponse = require('./RTCStatsResponse'),
RTCStatsReport = require('./RTCStatsReport'),
MediaStream = require('./MediaStream'),
MediaStreamTrack = require('./MediaStreamTrack'),
Errors = require('./Errors');


Expand Down Expand Up @@ -596,6 +599,84 @@ RTCPeerConnection.prototype.createDTMFSender = function (track) {
return new RTCDTMFSender(this, track);
};

RTCPeerConnection.prototype.getStats = function () {
var self = this,
isPromise,
selector,
callback, errback;

if (typeof arguments[0] !== 'function') {
isPromise = true;
selector = arguments[0];
} else {
isPromise = false;
callback = arguments[0];
selector = arguments[1];
errback = arguments[2];
}

if (selector && !(selector instanceof MediaStreamTrack)) {
throw new Error('getStats() must be called with null or a valid MediaStreamTrack instance as argument');
}

if (isClosed.call(this)) {
throw new Errors.InvalidStateError('peerconnection is closed');
}

debug('getStats() [selector:%o]', selector);

if (isPromise) {
return new Promise(function (resolve, reject) {
function onResultOK(array) {
if (isClosed.call(self)) {
return;
}

var res = [];
array.forEach(function (stat) {
res.push(new RTCStatsReport(stat));
});
resolve(new RTCStatsResponse(res));
}

function onResultError(error) {
if (isClosed.call(self)) {
return;
}

debugerror('getStats() | failure: %s', error);
reject(new global.DOMError(error));
}

exec(onResultOK, onResultError, 'iosrtcPlugin', 'RTCPeerConnection_getStats', [this.pcId, selector ? selector.id : null]);
});
}

function onResultOK(array) {
if (isClosed.call(self)) {
return;
}

var res = [];
array.forEach(function (stat) {
res.push(new RTCStatsReport(stat));
});
callback(new RTCStatsResponse(res));
}

function onResultError(error) {
if (isClosed.call(self)) {
return;
}

debugerror('getStats() | failure: %s', error);
if (typeof errback === 'function') {
errback(new global.DOMError(error));
}
}

exec(onResultOK, onResultError, 'iosrtcPlugin', 'RTCPeerConnection_getStats', [this.pcId, selector ? selector.id : null]);
};

RTCPeerConnection.prototype.close = function () {
if (isClosed.call(this)) {
Expand Down
20 changes: 20 additions & 0 deletions js/RTCStatsReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Expose the RTCStatsReport class.
*/
module.exports = RTCStatsReport;

function RTCStatsReport(data) {
data = data || [];

this.id = data.reportId;
this.timestamp = data.timestamp;
this.type = data.type;

this.names = function () {
return Object.keys(data.values);
};

this.stat = function (key) {
return data.values[key] || '';
};
}
16 changes: 16 additions & 0 deletions js/RTCStatsResponse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Expose the RTCStatsResponse class.
*/
module.exports = RTCStatsResponse;

function RTCStatsResponse(data) {
data = data || [];

this.result = function () {
return data;
};

this.namedItem = function () {
return null;
};
}
48 changes: 46 additions & 2 deletions src/PluginRTCPeerConnection.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation


class PluginRTCPeerConnection : NSObject, RTCPeerConnectionDelegate, RTCSessionDescriptionDelegate {
class PluginRTCPeerConnection : NSObject, RTCPeerConnectionDelegate, RTCSessionDescriptionDelegate, RTCStatsDelegate {
var rtcPeerConnectionFactory: RTCPeerConnectionFactory
var rtcPeerConnection: RTCPeerConnection!
var pluginRTCPeerConnectionConfig: PluginRTCPeerConnectionConfig
Expand All @@ -17,7 +17,7 @@ class PluginRTCPeerConnection : NSObject, RTCPeerConnectionDelegate, RTCSessionD
var onCreateDescriptionFailureCallback: ((error: NSError) -> Void)!
var onSetDescriptionSuccessCallback: (() -> Void)!
var onSetDescriptionFailureCallback: ((error: NSError) -> Void)!

var onGetStatsCallback: ((array: NSArray) -> Void)!

init(
rtcPeerConnectionFactory: RTCPeerConnectionFactory,
Expand Down Expand Up @@ -340,6 +340,27 @@ class PluginRTCPeerConnection : NSObject, RTCPeerConnectionDelegate, RTCSessionD
pluginRTCDTMFSender.run()
}

func getStats(
pluginMediaStreamTrack: PluginMediaStreamTrack?,
callback: (data: NSArray) -> Void,
errback: (error: NSError) -> Void
) {
NSLog("PluginRTCPeerConnection#getStats()")

if self.rtcPeerConnection.signalingState.rawValue == RTCSignalingClosed.rawValue {
return
}

self.onGetStatsCallback = { (array: NSArray) -> Void in
callback(data: array)
}

if !self.rtcPeerConnection.getStatsWithDelegate(self,
mediaStreamTrack: pluginMediaStreamTrack?.rtcMediaStreamTrack,
statsOutputLevel: RTCStatsOutputLevelStandard) {
errback(error: NSError(domain: "Cannot get peer connection stats.", code: -1, userInfo: nil))
}
}

func close() {
NSLog("PluginRTCPeerConnection#close()")
Expand Down Expand Up @@ -619,4 +640,27 @@ class PluginRTCPeerConnection : NSObject, RTCPeerConnectionDelegate, RTCSessionD
self.onSetDescriptionFailureCallback(error: error)
}
}

/**
* Methods inherited from RTCStatsDelegate
*/

func peerConnection(peerConnection: RTCPeerConnection!,
didGetStats stats: [AnyObject]!) {

var jsStats = [NSDictionary]()

for stat in stats as NSArray {
var jsValues = Dictionary<String,String>()

for pair in stat.values as! [RTCPair] {
jsValues[pair.key] = pair.value;
}

jsStats.append(["reportId": stat.reportId, "type": stat.type, "timestamp": stat.timestamp, "values": jsValues]);

}

self.onGetStatsCallback(array: jsStats);
}
}
4 changes: 2 additions & 2 deletions src/cordova-plugin-iosrtc-Bridging-Header.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
#import "RTCPeerConnectionFactory.h"
#import "RTCSessionDescription.h"
#import "RTCSessionDescriptionDelegate.h"
// #import "RTCStatsDelegate.h"
// #import "RTCStatsReport.h"
#import "RTCStatsDelegate.h"
#import "RTCStatsReport.h"
#import "RTCTypes.h"
#import "RTCVideoCapturer.h"
#import "RTCVideoRenderer.h"
Expand Down
38 changes: 38 additions & 0 deletions src/iosrtcPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,44 @@ class iosrtcPlugin : CDVPlugin {
}
}

func RTCPeerConnection_getStats(command: CDVInvokedUrlCommand) {
NSLog("iosrtcPlugin#RTCPeerConnection_getStats()")

let pcId = command.argumentAtIndex(0) as! Int
let pluginRTCPeerConnection = self.pluginRTCPeerConnections[pcId]

if pluginRTCPeerConnection == nil {
NSLog("iosrtcPlugin#RTCPeerConnection_getStats() | ERROR: pluginRTCPeerConnection with pcId=\(pcId) does not exist")
return;
}

var pluginMediaStreamTrack: PluginMediaStreamTrack?

if command.argumentAtIndex(1) != nil {
let trackId = command.argumentAtIndex(1) as! String
pluginMediaStreamTrack = self.pluginMediaStreamTracks[trackId]

if pluginMediaStreamTrack == nil {
NSLog("iosrtcPlugin#RTCPeerConnection_getStats() | ERROR: pluginMediaStreamTrack with id=\(trackId) does not exist")
return;
}
}

dispatch_async(self.queue) { [weak pluginRTCPeerConnection, weak pluginMediaStreamTrack] in
pluginRTCPeerConnection?.getStats(pluginMediaStreamTrack,
callback: { (array: NSArray) -> Void in
self.emit(command.callbackId,
result: CDVPluginResult(status: CDVCommandStatus_OK, messageAsArray: array as [AnyObject])
)
},
errback: { (error: NSError) -> Void in
self.emit(command.callbackId,
result: CDVPluginResult(status: CDVCommandStatus_ERROR, messageAsString: error.localizedDescription)
)
}
)
}
}

func RTCPeerConnection_close(command: CDVInvokedUrlCommand) {
NSLog("iosrtcPlugin#RTCPeerConnection_close()")
Expand Down

0 comments on commit d40e7da

Please sign in to comment.