blob: fd0b16b5d2a29280acc22958d7b3db1498e4d98d [file] [log] [blame]
/*
* 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