OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ |
| 10 |
| 11 #import "ARDStatsBuilder.h" |
| 12 |
| 13 #import "RTCPair.h" |
| 14 #import "RTCStatsReport.h" |
| 15 |
| 16 #import "ARDBitrateTracker.h" |
| 17 #import "ARDUtilities.h" |
| 18 |
| 19 @implementation ARDStatsBuilder { |
| 20 // Connection stats. |
| 21 NSString *_connRecvBitrate; |
| 22 NSString *_connRtt; |
| 23 NSString *_connSendBitrate; |
| 24 NSString *_localCandType; |
| 25 NSString *_remoteCandType; |
| 26 NSString *_transportType; |
| 27 |
| 28 // BWE stats. |
| 29 NSString *_actualEncBitrate; |
| 30 NSString *_availableRecvBw; |
| 31 NSString *_availableSendBw; |
| 32 NSString *_targetEncBitrate; |
| 33 |
| 34 // Video send stats. |
| 35 NSString *_videoEncodeMs; |
| 36 NSString *_videoInputFps; |
| 37 NSString *_videoInputHeight; |
| 38 NSString *_videoInputWidth; |
| 39 NSString *_videoSendCodec; |
| 40 NSString *_videoSendBitrate; |
| 41 NSString *_videoSendFps; |
| 42 NSString *_videoSendHeight; |
| 43 NSString *_videoSendWidth; |
| 44 |
| 45 // Video receive stats. |
| 46 NSString *_videoDecodeMs; |
| 47 NSString *_videoDecodedFps; |
| 48 NSString *_videoOutputFps; |
| 49 NSString *_videoRecvBitrate; |
| 50 NSString *_videoRecvFps; |
| 51 NSString *_videoRecvHeight; |
| 52 NSString *_videoRecvWidth; |
| 53 |
| 54 // Audio send stats. |
| 55 NSString *_audioSendBitrate; |
| 56 NSString *_audioSendCodec; |
| 57 |
| 58 // Audio receive stats. |
| 59 NSString *_audioCurrentDelay; |
| 60 NSString *_audioExpandRate; |
| 61 NSString *_audioRecvBitrate; |
| 62 NSString *_audioRecvCodec; |
| 63 |
| 64 // Bitrate trackers. |
| 65 ARDBitrateTracker *_audioRecvBitrateTracker; |
| 66 ARDBitrateTracker *_audioSendBitrateTracker; |
| 67 ARDBitrateTracker *_connRecvBitrateTracker; |
| 68 ARDBitrateTracker *_connSendBitrateTracker; |
| 69 ARDBitrateTracker *_videoRecvBitrateTracker; |
| 70 ARDBitrateTracker *_videoSendBitrateTracker; |
| 71 } |
| 72 |
| 73 - (instancetype)init { |
| 74 if (self = [super init]) { |
| 75 _audioSendBitrateTracker = [[ARDBitrateTracker alloc] init]; |
| 76 _audioRecvBitrateTracker = [[ARDBitrateTracker alloc] init]; |
| 77 _connSendBitrateTracker = [[ARDBitrateTracker alloc] init]; |
| 78 _connRecvBitrateTracker = [[ARDBitrateTracker alloc] init]; |
| 79 _videoSendBitrateTracker = [[ARDBitrateTracker alloc] init]; |
| 80 _videoRecvBitrateTracker = [[ARDBitrateTracker alloc] init]; |
| 81 } |
| 82 return self; |
| 83 } |
| 84 |
| 85 - (NSString *)statsString { |
| 86 NSMutableString *result = [NSMutableString string]; |
| 87 NSString *systemStatsFormat = @"(cpu)%ld%%\n"; |
| 88 [result appendString:[NSString stringWithFormat:systemStatsFormat, |
| 89 (long)ARDGetCpuUsagePercentage()]]; |
| 90 |
| 91 // Connection stats. |
| 92 NSString *connStatsFormat = @"CN %@ms | %@->%@/%@ | (s)%@ | (r)%@\n"; |
| 93 [result appendString:[NSString stringWithFormat:connStatsFormat, |
| 94 _connRtt, |
| 95 _localCandType, _remoteCandType, _transportType, |
| 96 _connSendBitrate, _connRecvBitrate]]; |
| 97 |
| 98 // Video send stats. |
| 99 NSString *videoSendFormat = @"VS (input) %@x%@@%@fps | (sent) %@x%@@%@fps\n" |
| 100 "VS (enc) %@/%@ | (sent) %@/%@ | %@ms | %@\n"; |
| 101 [result appendString:[NSString stringWithFormat:videoSendFormat, |
| 102 _videoInputWidth, _videoInputHeight, _videoInputFps, |
| 103 _videoSendWidth, _videoSendHeight, _videoSendFps, |
| 104 _actualEncBitrate, _targetEncBitrate, |
| 105 _videoSendBitrate, _availableSendBw, |
| 106 _videoEncodeMs, |
| 107 _videoSendCodec]]; |
| 108 |
| 109 // Video receive stats. |
| 110 NSString *videoReceiveFormat = |
| 111 @"VR (recv) %@x%@@%@fps | (decoded)%@ | (output)%@fps | %@/%@ | %@ms\n"; |
| 112 [result appendString:[NSString stringWithFormat:videoReceiveFormat, |
| 113 _videoRecvWidth, _videoRecvHeight, _videoRecvFps, |
| 114 _videoDecodedFps, |
| 115 _videoOutputFps, |
| 116 _videoRecvBitrate, _availableRecvBw, |
| 117 _videoDecodeMs]]; |
| 118 |
| 119 // Audio send stats. |
| 120 NSString *audioSendFormat = @"AS %@ | %@\n"; |
| 121 [result appendString:[NSString stringWithFormat:audioSendFormat, |
| 122 _audioSendBitrate, _audioSendCodec]]; |
| 123 |
| 124 // Audio receive stats. |
| 125 NSString *audioReceiveFormat = @"AR %@ | %@ | %@ms | (expandrate)%@"; |
| 126 [result appendString:[NSString stringWithFormat:audioReceiveFormat, |
| 127 _audioRecvBitrate, _audioRecvCodec, _audioCurrentDelay, |
| 128 _audioExpandRate]]; |
| 129 |
| 130 return result; |
| 131 } |
| 132 |
| 133 - (void)parseStatsReport:(RTCStatsReport *)statsReport { |
| 134 NSString *reportType = statsReport.type; |
| 135 if ([reportType isEqualToString:@"ssrc"] && |
| 136 [statsReport.reportId rangeOfString:@"ssrc"].location != NSNotFound) { |
| 137 if ([statsReport.reportId rangeOfString:@"send"].location != NSNotFound) { |
| 138 [self parseSendSsrcStatsReport:statsReport]; |
| 139 } |
| 140 if ([statsReport.reportId rangeOfString:@"recv"].location != NSNotFound) { |
| 141 [self parseRecvSsrcStatsReport:statsReport]; |
| 142 } |
| 143 } else if ([reportType isEqualToString:@"VideoBwe"]) { |
| 144 [self parseBweStatsReport:statsReport]; |
| 145 } else if ([reportType isEqualToString:@"googCandidatePair"]) { |
| 146 [self parseConnectionStatsReport:statsReport]; |
| 147 } |
| 148 } |
| 149 |
| 150 #pragma mark - Private |
| 151 |
| 152 - (void)parseBweStatsReport:(RTCStatsReport *)statsReport { |
| 153 for (RTCPair *pair in statsReport.values) { |
| 154 NSString *key = pair.key; |
| 155 NSString *value = pair.value; |
| 156 if ([key isEqualToString:@"googAvailableSendBandwidth"]) { |
| 157 _availableSendBw = |
| 158 [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
| 159 } else if ([key isEqualToString:@"googAvailableReceiveBandwidth"]) { |
| 160 _availableRecvBw = |
| 161 [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
| 162 } else if ([key isEqualToString:@"googActualEncBitrate"]) { |
| 163 _actualEncBitrate = |
| 164 [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
| 165 } else if ([key isEqualToString:@"googTargetEncBitrate"]) { |
| 166 _targetEncBitrate = |
| 167 [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
| 168 } |
| 169 } |
| 170 } |
| 171 |
| 172 - (void)parseConnectionStatsReport:(RTCStatsReport *)statsReport { |
| 173 NSDictionary *values = [self dictionaryForReport:statsReport]; |
| 174 NSString *activeConnection = [values[@"googActiveConnection"] firstObject]; |
| 175 if (![activeConnection isEqualToString:@"true"]) { |
| 176 return; |
| 177 } |
| 178 for (RTCPair *pair in statsReport.values) { |
| 179 NSString *key = pair.key; |
| 180 NSString *value = pair.value; |
| 181 if ([key isEqualToString:@"googRtt"]) { |
| 182 _connRtt = value; |
| 183 } else if ([key isEqualToString:@"googLocalCandidateType"]) { |
| 184 _localCandType = value; |
| 185 } else if ([key isEqualToString:@"googRemoteCandidateType"]) { |
| 186 _remoteCandType = value; |
| 187 } else if ([key isEqualToString:@"googTransportType"]) { |
| 188 _transportType = value; |
| 189 } else if ([key isEqualToString:@"bytesReceived"]) { |
| 190 NSInteger byteCount = value.integerValue; |
| 191 [_connRecvBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 192 _connRecvBitrate = _connRecvBitrateTracker.bitrateString; |
| 193 } else if ([key isEqualToString:@"bytesSent"]) { |
| 194 NSInteger byteCount = value.integerValue; |
| 195 [_connSendBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 196 _connSendBitrate = _connSendBitrateTracker.bitrateString; |
| 197 } |
| 198 } |
| 199 } |
| 200 |
| 201 - (void)parseSendSsrcStatsReport:(RTCStatsReport *)statsReport { |
| 202 NSDictionary *values = [self dictionaryForReport:statsReport]; |
| 203 NSString *trackId = [values[@"googTrackId"] firstObject]; |
| 204 if (trackId.length && [trackId hasPrefix:@"ARDAMSv0"]) { |
| 205 // Video track. |
| 206 [self parseVideoSendStatsReport:statsReport]; |
| 207 } else { |
| 208 // Audio track. |
| 209 [self parseAudioSendStatsReport:statsReport]; |
| 210 } |
| 211 } |
| 212 |
| 213 - (void)parseAudioSendStatsReport:(RTCStatsReport *)statsReport { |
| 214 for (RTCPair *pair in statsReport.values) { |
| 215 NSString *key = pair.key; |
| 216 NSString *value = pair.value; |
| 217 if ([key isEqualToString:@"googCodecName"]) { |
| 218 _audioSendCodec = value; |
| 219 } else if ([key isEqualToString:@"bytesSent"]) { |
| 220 NSInteger byteCount = value.integerValue; |
| 221 [_audioSendBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 222 _audioSendBitrate = _audioSendBitrateTracker.bitrateString; |
| 223 } |
| 224 } |
| 225 } |
| 226 |
| 227 - (void)parseVideoSendStatsReport:(RTCStatsReport *)statsReport { |
| 228 for (RTCPair *pair in statsReport.values) { |
| 229 NSString *key = pair.key; |
| 230 NSString *value = pair.value; |
| 231 if ([key isEqualToString:@"googCodecName"]) { |
| 232 _videoSendCodec = value; |
| 233 } else if ([key isEqualToString:@"googFrameHeightInput"]) { |
| 234 _videoInputHeight = value; |
| 235 } else if ([key isEqualToString:@"googFrameWidthInput"]) { |
| 236 _videoInputWidth = value; |
| 237 } else if ([key isEqualToString:@"googFrameRateInput"]) { |
| 238 _videoInputFps = value; |
| 239 } else if ([key isEqualToString:@"googFrameHeightSent"]) { |
| 240 _videoSendHeight = value; |
| 241 } else if ([key isEqualToString:@"googFrameWidthSent"]) { |
| 242 _videoSendWidth = value; |
| 243 } else if ([key isEqualToString:@"googFrameRateSent"]) { |
| 244 _videoSendFps = value; |
| 245 } else if ([key isEqualToString:@"googAvgEncodeMs"]) { |
| 246 _videoEncodeMs = value; |
| 247 } else if ([key isEqualToString:@"bytesSent"]) { |
| 248 NSInteger byteCount = value.integerValue; |
| 249 [_videoSendBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 250 _videoSendBitrate = _videoSendBitrateTracker.bitrateString; |
| 251 } |
| 252 } |
| 253 } |
| 254 |
| 255 - (void)parseRecvSsrcStatsReport:(RTCStatsReport *)statsReport { |
| 256 NSDictionary *values = [self dictionaryForReport:statsReport]; |
| 257 NSString *transportId = [values[@"transportId"] firstObject]; |
| 258 if ([values[@"googFrameWidthReceived"] firstObject]) { |
| 259 [self parseVideoRecvStatsReport:statsReport]; |
| 260 } else { |
| 261 [self parseAudioRecvStatsReport:statsReport]; |
| 262 } |
| 263 } |
| 264 |
| 265 - (void)parseAudioRecvStatsReport:(RTCStatsReport *)statsReport { |
| 266 for (RTCPair *pair in statsReport.values) { |
| 267 NSString *key = pair.key; |
| 268 NSString *value = pair.value; |
| 269 if ([key isEqualToString:@"googCodecName"]) { |
| 270 _audioRecvCodec = value; |
| 271 } else if ([key isEqualToString:@"bytesReceived"]) { |
| 272 NSInteger byteCount = value.integerValue; |
| 273 [_audioRecvBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 274 _audioRecvBitrate = _audioRecvBitrateTracker.bitrateString; |
| 275 } else if ([key isEqualToString:@"googSpeechExpandRate"]) { |
| 276 _audioExpandRate = value; |
| 277 } else if ([key isEqualToString:@"googCurrentDelayMs"]) { |
| 278 _audioCurrentDelay = value; |
| 279 } |
| 280 } |
| 281 } |
| 282 |
| 283 - (void)parseVideoRecvStatsReport:(RTCStatsReport *)statsReport { |
| 284 for (RTCPair *pair in statsReport.values) { |
| 285 NSString *key = pair.key; |
| 286 NSString *value = pair.value; |
| 287 if ([key isEqualToString:@"googFrameHeightReceived"]) { |
| 288 _videoRecvHeight = value; |
| 289 } else if ([key isEqualToString:@"googFrameWidthReceived"]) { |
| 290 _videoRecvWidth = value; |
| 291 } else if ([key isEqualToString:@"googFrameRateReceived"]) { |
| 292 _videoRecvFps = value; |
| 293 } else if ([key isEqualToString:@"googFrameRateDecoded"]) { |
| 294 _videoDecodedFps = value; |
| 295 } else if ([key isEqualToString:@"googFrameRateOutput"]) { |
| 296 _videoOutputFps = value; |
| 297 } else if ([key isEqualToString:@"googDecodeMs"]) { |
| 298 _videoDecodeMs = value; |
| 299 } else if ([key isEqualToString:@"bytesReceived"]) { |
| 300 NSInteger byteCount = value.integerValue; |
| 301 [_videoRecvBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 302 _videoRecvBitrate = _videoRecvBitrateTracker.bitrateString; |
| 303 } |
| 304 } |
| 305 } |
| 306 |
| 307 - (NSDictionary *)dictionaryForReport:(RTCStatsReport *)statsReport { |
| 308 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; |
| 309 for (RTCPair *pair in statsReport.values) { |
| 310 NSMutableArray *values = dict[pair.key]; |
| 311 if (!values) { |
| 312 values = [NSMutableArray arrayWithCapacity:1]; |
| 313 dict[pair.key] = values; |
| 314 } |
| 315 [values addObject:pair.value]; |
| 316 } |
| 317 return dict; |
| 318 } |
| 319 |
| 320 @end |
| 321 |
OLD | NEW |