Index: webrtc/examples/objc/AppRTCDemo/ARDStatsBuilder.m |
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDStatsBuilder.m b/webrtc/examples/objc/AppRTCDemo/ARDStatsBuilder.m |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0643b428273cc89c3d661d11423577fac07105a5 |
--- /dev/null |
+++ b/webrtc/examples/objc/AppRTCDemo/ARDStatsBuilder.m |
@@ -0,0 +1,300 @@ |
+/* |
+ * Copyright 2015 The WebRTC Project Authors. All rights reserved. |
+ * |
+ * Use of this source code is governed by a BSD-style license |
+ * that can be found in the LICENSE file in the root of the source |
+ * tree. An additional intellectual property rights grant can be found |
+ * in the file PATENTS. All contributing project authors may |
+ * be found in the AUTHORS file in the root of the source tree. |
+ */ |
+ |
+#import "ARDStatsBuilder.h" |
+ |
+#import "WebRTC/RTCLegacyStatsReport.h" |
+ |
+#import "ARDBitrateTracker.h" |
+#import "ARDUtilities.h" |
+ |
+@implementation ARDStatsBuilder { |
+ // Connection stats. |
+ NSString *_connRecvBitrate; |
+ NSString *_connRtt; |
+ NSString *_connSendBitrate; |
+ NSString *_localCandType; |
+ NSString *_remoteCandType; |
+ NSString *_transportType; |
+ |
+ // BWE stats. |
+ NSString *_actualEncBitrate; |
+ NSString *_availableRecvBw; |
+ NSString *_availableSendBw; |
+ NSString *_targetEncBitrate; |
+ |
+ // Video send stats. |
+ NSString *_videoEncodeMs; |
+ NSString *_videoInputFps; |
+ NSString *_videoInputHeight; |
+ NSString *_videoInputWidth; |
+ NSString *_videoSendCodec; |
+ NSString *_videoSendBitrate; |
+ NSString *_videoSendFps; |
+ NSString *_videoSendHeight; |
+ NSString *_videoSendWidth; |
+ |
+ // Video receive stats. |
+ NSString *_videoDecodeMs; |
+ NSString *_videoDecodedFps; |
+ NSString *_videoOutputFps; |
+ NSString *_videoRecvBitrate; |
+ NSString *_videoRecvFps; |
+ NSString *_videoRecvHeight; |
+ NSString *_videoRecvWidth; |
+ |
+ // Audio send stats. |
+ NSString *_audioSendBitrate; |
+ NSString *_audioSendCodec; |
+ |
+ // Audio receive stats. |
+ NSString *_audioCurrentDelay; |
+ NSString *_audioExpandRate; |
+ NSString *_audioRecvBitrate; |
+ NSString *_audioRecvCodec; |
+ |
+ // Bitrate trackers. |
+ ARDBitrateTracker *_audioRecvBitrateTracker; |
+ ARDBitrateTracker *_audioSendBitrateTracker; |
+ ARDBitrateTracker *_connRecvBitrateTracker; |
+ ARDBitrateTracker *_connSendBitrateTracker; |
+ ARDBitrateTracker *_videoRecvBitrateTracker; |
+ ARDBitrateTracker *_videoSendBitrateTracker; |
+} |
+ |
+- (instancetype)init { |
+ if (self = [super init]) { |
+ _audioSendBitrateTracker = [[ARDBitrateTracker alloc] init]; |
+ _audioRecvBitrateTracker = [[ARDBitrateTracker alloc] init]; |
+ _connSendBitrateTracker = [[ARDBitrateTracker alloc] init]; |
+ _connRecvBitrateTracker = [[ARDBitrateTracker alloc] init]; |
+ _videoSendBitrateTracker = [[ARDBitrateTracker alloc] init]; |
+ _videoRecvBitrateTracker = [[ARDBitrateTracker alloc] init]; |
+ } |
+ return self; |
+} |
+ |
+- (NSString *)statsString { |
+ NSMutableString *result = [NSMutableString string]; |
+ NSString *systemStatsFormat = @"(cpu)%ld%%\n"; |
+ [result appendString:[NSString stringWithFormat:systemStatsFormat, |
+ (long)ARDGetCpuUsagePercentage()]]; |
+ |
+ // Connection stats. |
+ NSString *connStatsFormat = @"CN %@ms | %@->%@/%@ | (s)%@ | (r)%@\n"; |
+ [result appendString:[NSString stringWithFormat:connStatsFormat, |
+ _connRtt, |
+ _localCandType, _remoteCandType, _transportType, |
+ _connSendBitrate, _connRecvBitrate]]; |
+ |
+ // Video send stats. |
+ NSString *videoSendFormat = @"VS (input) %@x%@@%@fps | (sent) %@x%@@%@fps\n" |
+ "VS (enc) %@/%@ | (sent) %@/%@ | %@ms | %@\n"; |
+ [result appendString:[NSString stringWithFormat:videoSendFormat, |
+ _videoInputWidth, _videoInputHeight, _videoInputFps, |
+ _videoSendWidth, _videoSendHeight, _videoSendFps, |
+ _actualEncBitrate, _targetEncBitrate, |
+ _videoSendBitrate, _availableSendBw, |
+ _videoEncodeMs, |
+ _videoSendCodec]]; |
+ |
+ // Video receive stats. |
+ NSString *videoReceiveFormat = |
+ @"VR (recv) %@x%@@%@fps | (decoded)%@ | (output)%@fps | %@/%@ | %@ms\n"; |
+ [result appendString:[NSString stringWithFormat:videoReceiveFormat, |
+ _videoRecvWidth, _videoRecvHeight, _videoRecvFps, |
+ _videoDecodedFps, |
+ _videoOutputFps, |
+ _videoRecvBitrate, _availableRecvBw, |
+ _videoDecodeMs]]; |
+ |
+ // Audio send stats. |
+ NSString *audioSendFormat = @"AS %@ | %@\n"; |
+ [result appendString:[NSString stringWithFormat:audioSendFormat, |
+ _audioSendBitrate, _audioSendCodec]]; |
+ |
+ // Audio receive stats. |
+ NSString *audioReceiveFormat = @"AR %@ | %@ | %@ms | (expandrate)%@"; |
+ [result appendString:[NSString stringWithFormat:audioReceiveFormat, |
+ _audioRecvBitrate, _audioRecvCodec, _audioCurrentDelay, |
+ _audioExpandRate]]; |
+ |
+ return result; |
+} |
+ |
+- (void)parseStatsReport:(RTCLegacyStatsReport *)statsReport { |
+ NSString *reportType = statsReport.type; |
+ if ([reportType isEqualToString:@"ssrc"] && |
+ [statsReport.reportId rangeOfString:@"ssrc"].location != NSNotFound) { |
+ if ([statsReport.reportId rangeOfString:@"send"].location != NSNotFound) { |
+ [self parseSendSsrcStatsReport:statsReport]; |
+ } |
+ if ([statsReport.reportId rangeOfString:@"recv"].location != NSNotFound) { |
+ [self parseRecvSsrcStatsReport:statsReport]; |
+ } |
+ } else if ([reportType isEqualToString:@"VideoBwe"]) { |
+ [self parseBweStatsReport:statsReport]; |
+ } else if ([reportType isEqualToString:@"googCandidatePair"]) { |
+ [self parseConnectionStatsReport:statsReport]; |
+ } |
+} |
+ |
+#pragma mark - Private |
+ |
+- (void)parseBweStatsReport:(RTCLegacyStatsReport *)statsReport { |
+ [statsReport.values enumerateKeysAndObjectsUsingBlock:^( |
+ NSString *key, NSString *value, BOOL *stop) { |
+ if ([key isEqualToString:@"googAvailableSendBandwidth"]) { |
+ _availableSendBw = |
+ [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
+ } else if ([key isEqualToString:@"googAvailableReceiveBandwidth"]) { |
+ _availableRecvBw = |
+ [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
+ } else if ([key isEqualToString:@"googActualEncBitrate"]) { |
+ _actualEncBitrate = |
+ [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
+ } else if ([key isEqualToString:@"googTargetEncBitrate"]) { |
+ _targetEncBitrate = |
+ [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
+ } |
+ }]; |
+} |
+ |
+- (void)parseConnectionStatsReport:(RTCLegacyStatsReport *)statsReport { |
+ NSString *activeConnection = statsReport.values[@"googActiveConnection"]; |
+ if (![activeConnection isEqualToString:@"true"]) { |
+ return; |
+ } |
+ [statsReport.values enumerateKeysAndObjectsUsingBlock:^( |
+ NSString *key, NSString *value, BOOL *stop) { |
+ if ([key isEqualToString:@"googRtt"]) { |
+ _connRtt = value; |
+ } else if ([key isEqualToString:@"googLocalCandidateType"]) { |
+ _localCandType = value; |
+ } else if ([key isEqualToString:@"googRemoteCandidateType"]) { |
+ _remoteCandType = value; |
+ } else if ([key isEqualToString:@"googTransportType"]) { |
+ _transportType = value; |
+ } else if ([key isEqualToString:@"bytesReceived"]) { |
+ NSInteger byteCount = value.integerValue; |
+ [_connRecvBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
+ _connRecvBitrate = _connRecvBitrateTracker.bitrateString; |
+ } else if ([key isEqualToString:@"bytesSent"]) { |
+ NSInteger byteCount = value.integerValue; |
+ [_connSendBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
+ _connSendBitrate = _connSendBitrateTracker.bitrateString; |
+ } |
+ }]; |
+} |
+ |
+- (void)parseSendSsrcStatsReport:(RTCLegacyStatsReport *)statsReport { |
+ NSDictionary *values = statsReport.values; |
+ if ([values objectForKey:@"googFrameRateSent"]) { |
+ // Video track. |
+ [self parseVideoSendStatsReport:statsReport]; |
+ } else if ([values objectForKey:@"audioInputLevel"]) { |
+ // Audio track. |
+ [self parseAudioSendStatsReport:statsReport]; |
+ } |
+} |
+ |
+- (void)parseAudioSendStatsReport:(RTCLegacyStatsReport *)statsReport { |
+ [statsReport.values enumerateKeysAndObjectsUsingBlock:^( |
+ NSString *key, NSString *value, BOOL *stop) { |
+ if ([key isEqualToString:@"googCodecName"]) { |
+ _audioSendCodec = value; |
+ } else if ([key isEqualToString:@"bytesSent"]) { |
+ NSInteger byteCount = value.integerValue; |
+ [_audioSendBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
+ _audioSendBitrate = _audioSendBitrateTracker.bitrateString; |
+ } |
+ }]; |
+} |
+ |
+- (void)parseVideoSendStatsReport:(RTCLegacyStatsReport *)statsReport { |
+ [statsReport.values enumerateKeysAndObjectsUsingBlock:^( |
+ NSString *key, NSString *value, BOOL *stop) { |
+ if ([key isEqualToString:@"googCodecName"]) { |
+ _videoSendCodec = value; |
+ } else if ([key isEqualToString:@"googFrameHeightInput"]) { |
+ _videoInputHeight = value; |
+ } else if ([key isEqualToString:@"googFrameWidthInput"]) { |
+ _videoInputWidth = value; |
+ } else if ([key isEqualToString:@"googFrameRateInput"]) { |
+ _videoInputFps = value; |
+ } else if ([key isEqualToString:@"googFrameHeightSent"]) { |
+ _videoSendHeight = value; |
+ } else if ([key isEqualToString:@"googFrameWidthSent"]) { |
+ _videoSendWidth = value; |
+ } else if ([key isEqualToString:@"googFrameRateSent"]) { |
+ _videoSendFps = value; |
+ } else if ([key isEqualToString:@"googAvgEncodeMs"]) { |
+ _videoEncodeMs = value; |
+ } else if ([key isEqualToString:@"bytesSent"]) { |
+ NSInteger byteCount = value.integerValue; |
+ [_videoSendBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
+ _videoSendBitrate = _videoSendBitrateTracker.bitrateString; |
+ } |
+ }]; |
+} |
+ |
+- (void)parseRecvSsrcStatsReport:(RTCLegacyStatsReport *)statsReport { |
+ NSDictionary *values = statsReport.values; |
+ if ([values objectForKey:@"googFrameWidthReceived"]) { |
+ // Video track. |
+ [self parseVideoRecvStatsReport:statsReport]; |
+ } else if ([values objectForKey:@"audioOutputLevel"]) { |
+ // Audio track. |
+ [self parseAudioRecvStatsReport:statsReport]; |
+ } |
+} |
+ |
+- (void)parseAudioRecvStatsReport:(RTCLegacyStatsReport *)statsReport { |
+ [statsReport.values enumerateKeysAndObjectsUsingBlock:^( |
+ NSString *key, NSString *value, BOOL *stop) { |
+ if ([key isEqualToString:@"googCodecName"]) { |
+ _audioRecvCodec = value; |
+ } else if ([key isEqualToString:@"bytesReceived"]) { |
+ NSInteger byteCount = value.integerValue; |
+ [_audioRecvBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
+ _audioRecvBitrate = _audioRecvBitrateTracker.bitrateString; |
+ } else if ([key isEqualToString:@"googSpeechExpandRate"]) { |
+ _audioExpandRate = value; |
+ } else if ([key isEqualToString:@"googCurrentDelayMs"]) { |
+ _audioCurrentDelay = value; |
+ } |
+ }]; |
+} |
+ |
+- (void)parseVideoRecvStatsReport:(RTCLegacyStatsReport *)statsReport { |
+ [statsReport.values enumerateKeysAndObjectsUsingBlock:^( |
+ NSString *key, NSString *value, BOOL *stop) { |
+ if ([key isEqualToString:@"googFrameHeightReceived"]) { |
+ _videoRecvHeight = value; |
+ } else if ([key isEqualToString:@"googFrameWidthReceived"]) { |
+ _videoRecvWidth = value; |
+ } else if ([key isEqualToString:@"googFrameRateReceived"]) { |
+ _videoRecvFps = value; |
+ } else if ([key isEqualToString:@"googFrameRateDecoded"]) { |
+ _videoDecodedFps = value; |
+ } else if ([key isEqualToString:@"googFrameRateOutput"]) { |
+ _videoOutputFps = value; |
+ } else if ([key isEqualToString:@"googDecodeMs"]) { |
+ _videoDecodeMs = value; |
+ } else if ([key isEqualToString:@"bytesReceived"]) { |
+ NSInteger byteCount = value.integerValue; |
+ [_videoRecvBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
+ _videoRecvBitrate = _videoRecvBitrateTracker.bitrateString; |
+ } |
+ }]; |
+} |
+ |
+@end |
+ |