| 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..fd0b16b5d2a29280acc22958d7b3db1498e4d98d
|
| --- /dev/null
|
| +++ b/webrtc/examples/objc/AppRTCDemo/ARDStatsBuilder.m
|
| @@ -0,0 +1,321 @@
|
| +/*
|
| + * 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 "RTCPair.h"
|
| +#import "RTCStatsReport.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:(RTCStatsReport *)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:(RTCStatsReport *)statsReport {
|
| + for (RTCPair *pair in statsReport.values) {
|
| + NSString *key = pair.key;
|
| + NSString *value = pair.value;
|
| + 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:(RTCStatsReport *)statsReport {
|
| + NSDictionary *values = [self dictionaryForReport:statsReport];
|
| + NSString *activeConnection = [values[@"googActiveConnection"] firstObject];
|
| + if (![activeConnection isEqualToString:@"true"]) {
|
| + return;
|
| + }
|
| + for (RTCPair *pair in statsReport.values) {
|
| + NSString *key = pair.key;
|
| + NSString *value = pair.value;
|
| + 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:(RTCStatsReport *)statsReport {
|
| + NSDictionary *values = [self dictionaryForReport:statsReport];
|
| + NSString *trackId = [values[@"googTrackId"] firstObject];
|
| + if (trackId.length && [trackId hasPrefix:@"ARDAMSv0"]) {
|
| + // Video track.
|
| + [self parseVideoSendStatsReport:statsReport];
|
| + } else {
|
| + // Audio track.
|
| + [self parseAudioSendStatsReport:statsReport];
|
| + }
|
| +}
|
| +
|
| +- (void)parseAudioSendStatsReport:(RTCStatsReport *)statsReport {
|
| + for (RTCPair *pair in statsReport.values) {
|
| + NSString *key = pair.key;
|
| + NSString *value = pair.value;
|
| + if ([key isEqualToString:@"googCodecName"]) {
|
| + _audioSendCodec = value;
|
| + } else if ([key isEqualToString:@"bytesSent"]) {
|
| + NSInteger byteCount = value.integerValue;
|
| + [_audioSendBitrateTracker updateBitrateWithCurrentByteCount:byteCount];
|
| + _audioSendBitrate = _audioSendBitrateTracker.bitrateString;
|
| + }
|
| + }
|
| +}
|
| +
|
| +- (void)parseVideoSendStatsReport:(RTCStatsReport *)statsReport {
|
| + for (RTCPair *pair in statsReport.values) {
|
| + NSString *key = pair.key;
|
| + NSString *value = pair.value;
|
| + 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:(RTCStatsReport *)statsReport {
|
| + NSDictionary *values = [self dictionaryForReport:statsReport];
|
| + NSString *transportId = [values[@"transportId"] firstObject];
|
| + if ([values[@"googFrameWidthReceived"] firstObject]) {
|
| + [self parseVideoRecvStatsReport:statsReport];
|
| + } else {
|
| + [self parseAudioRecvStatsReport:statsReport];
|
| + }
|
| +}
|
| +
|
| +- (void)parseAudioRecvStatsReport:(RTCStatsReport *)statsReport {
|
| + for (RTCPair *pair in statsReport.values) {
|
| + NSString *key = pair.key;
|
| + NSString *value = pair.value;
|
| + 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:(RTCStatsReport *)statsReport {
|
| + for (RTCPair *pair in statsReport.values) {
|
| + NSString *key = pair.key;
|
| + NSString *value = pair.value;
|
| + 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;
|
| + }
|
| + }
|
| +}
|
| +
|
| +- (NSDictionary *)dictionaryForReport:(RTCStatsReport *)statsReport {
|
| + NSMutableDictionary *dict = [NSMutableDictionary dictionary];
|
| + for (RTCPair *pair in statsReport.values) {
|
| + NSMutableArray *values = dict[pair.key];
|
| + if (!values) {
|
| + values = [NSMutableArray arrayWithCapacity:1];
|
| + dict[pair.key] = values;
|
| + }
|
| + [values addObject:pair.value];
|
| + }
|
| + return dict;
|
| +}
|
| +
|
| +@end
|
| +
|
|
|