| /* |
| * 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 |
| |