8000 Implementation StatsReport. · lineCode/flutter-webrtc@f27350e · GitHub
[go: up one dir, main page]

Skip to content

Commit f27350e

Browse files
committed
Implementation StatsReport.
1 parent 4fc6659 commit f27350e

File tree

5 files changed

+85
-135
lines changed

5 files changed

+85
-135
lines changed

android/src/main/java/com/cloudwebrtc/webrtc/PeerConnectionObserver.java

Lines changed: 30 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -187,73 +187,44 @@ void getStats(String trackId, final Result result) {
187187
new StatsObserver() {
188188
@Override
189189
public void onComplete(StatsReport[] reports) {
190-
result.success(statsToJSON(reports));
190+
191+
final int reportCount = reports.length;
192+
ConstraintsMap params = new ConstraintsMap();
193+
ConstraintsArray stats = new ConstraintsArray();
194+
195+
for (int i = 0; i < reportCount; ++i) {
196+
StatsReport report = reports[i];
197+
ConstraintsMap report_map = new ConstraintsMap();
198+
199+
report_map.putString("id", report.id);
200+
report_map.putString("type", report.type);
201+
report_map.putDouble("timestamp", report.timestamp);
202+
203+
StatsReport.Value[] values = report.values;
204+
ConstraintsMap v_map = new ConstraintsMap();
205+
final int valueCount = values.length;
206+
for (int j = 0; j < valueCount; ++j) {
207+
StatsReport.Value v = values[j];
208+
v_map.putString(v.name, v.value);
209+
}
210+
211+
report_map.putMap("values", v_map.toMap());
212+
stats.pushMap(report_map);
213+
}
214+
215+
params.putArray("stats", stats.toArrayList());
216+
result.success(params.toMap());
191217
}
192218
},
193219
track);
194220
} else {
195221
Log.e(TAG, "peerConnectionGetStats() MediaStreamTrack not found for id: " + trackId);
222+
result.error("peerConnectionGetStats",
223+
"peerConnectionGetStats() MediaStreamTrack not found for id: " + trackId,
224+
null);
196225
}
197226
}
198227

199-
/**
200-
* Constructs a JSON <tt>String</tt> representation of a specific array of
201-
* <tt>StatsReport</tt>s (produced by {@link PeerConnection#getStats}).
202-
* <p>
203-
* On Android it is faster to (1) construct a single JSON <tt>String</tt>
204-
* representation of an array of <tt>StatsReport</tt>s and (2) have it pass
205-
* through the React Native bridge rather than the array of
206-
* <tt>StatsReport</tt>s.
207-
*
208-
* @param reports the array of <tt>StatsReport</tt>s to represent in JSON
209-
* format
210-
* @return a <tt>String</tt> which represents the specified <tt>reports</tt>
211-
* in JSON format
212-
*/
213-
private String statsToJSON(StatsReport[] reports) {
214-
// If possible, reuse a single StringBuilder instance across multiple
215-
// getStats method calls in order to reduce the total number of
216-
// allocations.
217-
StringBuilder s = statsToJSONStringBuilder.get();
218-
if (s == null) {
219-
s = new StringBuilder();
220-
statsToJSONStringBuilder = new SoftReference(s);
221-
}
222-
223-
s.append('[');
224-
final int reportCount = reports.length;
225-
for (int i = 0; i < reportCount; ++i) {
226-
StatsReport report = reports[i];
227-
if (i != 0) {
228-
s.append(',');
229-
}
230-
s.append("{\"id\":\"").append(report.id)
231-
.append("\",\"type\":\"").append(report.type)
232-
.append("\",\"timestamp\":").append(report.timestamp)
233-
.append(",\"values\":[");
234-
StatsReport.Value[] values = report.values;
235-
final int valueCount = values.length;
236-
for (int j = 0; j < valueCount; ++j) {
237-
StatsReport.Value v = values[j];
238-
if (j != 0) {
239-
s.append(',');
240-
}
241-
s.append("{\"").append(v.name).append("\":\"").append(v.value)
242-
.append("\"}");
243-
}
244-
s.append("]}");
245-
}
246-
s.append("]");
247-
248-
String r = s.toString();
249-
// Prepare the StringBuilder instance for reuse (in order to reduce the
250-
// total number of allocations performed during multiple getStats method
251-
// calls).
252-
s.setLength(0);
253-
254-
return r;
255-
}
256-
257228
@Override
258229
public void onIceCandidate(final IceCandidate candidate) {
259230
Log.d(TAG, "onIceCandidate");

example/lib/main.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import 'package:webrtc/get_user_media.dart';
66
import 'package:webrtc/rtc_session_description.dart';
77
import 'package:webrtc/rtc_video_view.dart';
88
import 'package:webrtc/rtc_ice_candidate.dart';
9+
import 'package:webrtc/rtc_stats_report.dart';
910
import 'dart:core';
11+
import 'dart:async';
1012

1113
void main() => runApp(new MyApp());
1214

@@ -21,6 +23,7 @@ class _MyAppState extends State<MyApp> {
2123
final _localRenderer = new RTCVideoRenderer();
2224
final _remoteRenderer = new RTCVideoRenderer();
2325
bool incalling = false;
26+
Timer _timer;
2427

2528
@override
2629
initState() {
@@ -33,6 +36,24 @@ class _MyAppState extends State<MyApp> {
3336
await _remoteRenderer.initialize();
3437
}
3538

39+
void handleStatsReport(Timer timer) async {
40+
if (_peerConnection != null) {
41+
List<StatsReport> reports = await _peerConnection.getStats(null);
42+
reports.forEach((report) {
43+
print("report => { ");
44+
print(" id: " + report.id + ",");
45+
print(" type: " + report.type + ",");
46+
print(" timestamp: ${report.timestamp},");
47+
print(" values => {");
48+
report.values.forEach((key, value) {
49+
print(" " + key + " : " + value + ", ");
50+
});
51+
print(" }");
52+
print("}");
53+
});
54+
}
55+
}
56+
3657
_onSignalingState(RTCSignalingState state) {
3758
print(state);
3859
}
@@ -130,6 +151,8 @@ class _MyAppState extends State<MyApp> {
130151
}
131152
if (!mounted) return;
132153

154+
_timer = new Timer.periodic(Duration(seconds: 1), handleStatsReport);
155+
133156
setState(() {
134157
incalling = true;
135158
});

ios/Classes/FlutterRTCPeerConnection.m

Lines changed: 17 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -207,75 +207,27 @@ -(void) peerConnectionGetStats:(nonnull NSString *)trackID
207207
|| (track = peerConnection.remoteTracks[trackID])) {
208208
[peerConnection statsForTrack:track
209209
statsOutputLevel:RTCStatsOutputLevelStandard
210-
completionHandler:^(NSArray<RTCLegacyStatsReport *> *stats) {
211-
result(@[[self statsToJSON:stats]]);
210+
completionHandler:^(NSArray<RTCLegacyStatsReport *> *reports) {
211+
212+
NSMutableArray *stats = [NSMutableArray array];
213+
214+
for (RTCLegacyStatsReport *report in reports) {
215+
[stats addObject:@{@"id": report.reportId,
216+
@"type": report.type,
217+
@"timestamp": @(report.timestamp),
218+
@"values": report.values
219+
}];
220+
}
221+
222+
result(@{@"stats": stats});
212223
}];
224+
}else{
225+
result([FlutterError errorWithCode:@"GetStatsFailed"
226+
message:[NSString stringWithFormat:@"Error %@", @""]
227+
details:nil]);
213228
}
214229
}
215230

216-
/**
217-
* Constructs a JSON <tt>NSString</tt> representation of a specific array of
218-
* <tt>RTCLegacyStatsReport</tt>s.
219-
* <p>
220-
* On iOS it is faster to (1) construct a single JSON <tt>NSString</tt>
221-
* representation of an array of <tt>RTCLegacyStatsReport</tt>s and (2) have it
222-
* pass through the React Native bridge rather than the array of
223-
* <tt>RTCLegacyStatsReport</tt>s.
224-
*
225-
* @param reports the array of <tt>RTCLegacyStatsReport</tt>s to represent in
226-
* JSON format
227-
* @return an <tt>NSString</tt> which represents the specified <tt>stats</tt> in
228-
* JSON format
229-
*/
230-
- (NSString *)statsToJSON:(NSArray<RTCLegacyStatsReport *> *)reports
231-
{
232-
// XXX The initial capacity matters, of course, because it determines how many
233-
// times the NSMutableString will have grow. But walking through the reports
234-
// to compute an initial capacity which exactly matches the requirements of
235-
// the reports is too much work without real-world bang here. A better
236-
// approach is what the Android counterpart does i.e. cache the
237-
// NSMutableString and preferably with a Java-like soft reference. If that is
238-
// too much work, then an improvement should be caching the required capacity
239-
// from the previous invocation of the method and using it as the initial
240-
// capacity in the next invocation. As I didn't want to go even through that,
241-
// choosing just about any initial capacity is OK because NSMutableCopy
242-
// doesn't have too bad a strategy of growing.
243-
NSMutableString *s = [NSMutableString stringWithCapacity:8 * 1024];
244-
245-
[s appendString:@"["];
246-
BOOL firstReport = YES;
247-
for (RTCLegacyStatsReport *report in reports) {
248-
if (firstReport) {
249-
firstReport = NO;
250-
} else {
251-
[s appendString:@","];
252-
}
253-
[s appendString:@"{\"id\":\""]; [s appendString:report.reportId];
254-
[s appendString:@"\",\"type\":\""]; [s appendString:report.type];
255-
[s appendString:@"\",\"timestamp\":"];
256-
[s appendFormat:@"%f", report.timestamp];
257-
[s appendString:@",\"values\":["];
258-
__block BOOL firstValue = YES;
259-
[report.values enumerateKeysAndObjectsUsingBlock:^(
260-
NSString *key,
261-
NSString *value,
262-
BOOL *stop) {
263-
if (firstValue) {
264-
firstValue = NO;
265-
} else {
266-
[s appendString:@","];
267-
}
268-
[s appendString:@"{\""]; [s appendString:key];
269-
[s appendString:@"\":\""]; [s appendString:value];
270-
[s appendString:@"\"}"];
271-
}];
272-
[s appendString:@"]}"];
273-
}
274-
[s appendString:@"]"];
275-
276-
return s;
277-
}
278-
279231
- (NSString *)stringForICEConnectionState:(RTCIceConnectionState)state {
280232
switch (state) {
281233
case RTCIceConnectionStateNew: return @"new";

lib/rtc_peerconnection.dart

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,15 +253,19 @@ class RTCPeerConnection {
253253
});
254254
}
255255

256-
Future<StatsReport> getStats(MediaStreamTrack track) async {
256+
Future<List<StatsReport>> getStats(MediaStreamTrack track) async {
257257
try {
258-
final Map<dynamic, dynamic> response = await _channel.invokeMethod(
259-
'getStats', <String, dynamic>{
258+
final Map<dynamic, dynamic> response =
259+
await _channel.invokeMethod('getStats', <String, dynamic>{
260260
'peerConnectionId': this._peerConnectionId,
261-
'track': track.id
261+
'track': track != null ? track.id : null
262262
});
263-
Map<String, dynamic> stats = response["stats"];
264-
return new StatsReport(stats);
263+
List<dynamic> reports = response['stats'];
264+
List<StatsReport> stats = new List<StatsReport>();
265+
reports.forEach((report){
266+
stats.add(new StatsReport(report['id'], report['type'], report['timestamp'], report['values']));
267+
});
268+
return stats;
265269
} on PlatformException catch (e) {
266270
throw 'Unable to RTCPeerConnection::getStats: ${e.message}';
267271
}

lib/rtc_stats_report.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11

22
class StatsReport {
3-
Map<String,dynamic> _stats;
4-
StatsReport(this._stats);
5-
Map<String,dynamic> getStats(){
6-
return _stats;
7-
}
3+
String id;
4+
String type;
5+
double timestamp;
6+
Map<dynamic,dynamic> values;
7+
StatsReport(this.id, this.type, this.timestamp, this.values);
88
}

0 commit comments

Comments
 (0)
0