Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 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 | |
Anders Carlsson | 7bca8ca | 2018-08-30 09:30:29 +0200 | [diff] [blame] | 13 | #import <WebRTC/RTCLegacyStatsReport.h> |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 14 | |
| 15 | #import "ARDBitrateTracker.h" |
| 16 | #import "ARDUtilities.h" |
| 17 | |
| 18 | @implementation ARDStatsBuilder { |
| 19 | // Connection stats. |
| 20 | NSString *_connRecvBitrate; |
| 21 | NSString *_connRtt; |
| 22 | NSString *_connSendBitrate; |
| 23 | NSString *_localCandType; |
| 24 | NSString *_remoteCandType; |
| 25 | NSString *_transportType; |
| 26 | |
| 27 | // BWE stats. |
| 28 | NSString *_actualEncBitrate; |
| 29 | NSString *_availableRecvBw; |
| 30 | NSString *_availableSendBw; |
| 31 | NSString *_targetEncBitrate; |
| 32 | |
| 33 | // Video send stats. |
| 34 | NSString *_videoEncodeMs; |
| 35 | NSString *_videoInputFps; |
| 36 | NSString *_videoInputHeight; |
| 37 | NSString *_videoInputWidth; |
| 38 | NSString *_videoSendCodec; |
| 39 | NSString *_videoSendBitrate; |
| 40 | NSString *_videoSendFps; |
| 41 | NSString *_videoSendHeight; |
| 42 | NSString *_videoSendWidth; |
| 43 | |
denicija | b6c456b | 2016-12-19 02:14:08 -0800 | [diff] [blame] | 44 | // QP stats. |
| 45 | int _videoQPSum; |
| 46 | int _framesEncoded; |
| 47 | int _oldVideoQPSum; |
| 48 | int _oldFramesEncoded; |
| 49 | |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 50 | // Video receive stats. |
| 51 | NSString *_videoDecodeMs; |
| 52 | NSString *_videoDecodedFps; |
| 53 | NSString *_videoOutputFps; |
| 54 | NSString *_videoRecvBitrate; |
| 55 | NSString *_videoRecvFps; |
| 56 | NSString *_videoRecvHeight; |
| 57 | NSString *_videoRecvWidth; |
| 58 | |
| 59 | // Audio send stats. |
| 60 | NSString *_audioSendBitrate; |
| 61 | NSString *_audioSendCodec; |
| 62 | |
| 63 | // Audio receive stats. |
| 64 | NSString *_audioCurrentDelay; |
| 65 | NSString *_audioExpandRate; |
| 66 | NSString *_audioRecvBitrate; |
| 67 | NSString *_audioRecvCodec; |
| 68 | |
| 69 | // Bitrate trackers. |
| 70 | ARDBitrateTracker *_audioRecvBitrateTracker; |
| 71 | ARDBitrateTracker *_audioSendBitrateTracker; |
| 72 | ARDBitrateTracker *_connRecvBitrateTracker; |
| 73 | ARDBitrateTracker *_connSendBitrateTracker; |
| 74 | ARDBitrateTracker *_videoRecvBitrateTracker; |
| 75 | ARDBitrateTracker *_videoSendBitrateTracker; |
| 76 | } |
| 77 | |
| 78 | - (instancetype)init { |
| 79 | if (self = [super init]) { |
| 80 | _audioSendBitrateTracker = [[ARDBitrateTracker alloc] init]; |
| 81 | _audioRecvBitrateTracker = [[ARDBitrateTracker alloc] init]; |
| 82 | _connSendBitrateTracker = [[ARDBitrateTracker alloc] init]; |
| 83 | _connRecvBitrateTracker = [[ARDBitrateTracker alloc] init]; |
| 84 | _videoSendBitrateTracker = [[ARDBitrateTracker alloc] init]; |
| 85 | _videoRecvBitrateTracker = [[ARDBitrateTracker alloc] init]; |
denicija | b6c456b | 2016-12-19 02:14:08 -0800 | [diff] [blame] | 86 | _videoQPSum = 0; |
| 87 | _framesEncoded = 0; |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 88 | } |
| 89 | return self; |
| 90 | } |
| 91 | |
| 92 | - (NSString *)statsString { |
| 93 | NSMutableString *result = [NSMutableString string]; |
| 94 | NSString *systemStatsFormat = @"(cpu)%ld%%\n"; |
| 95 | [result appendString:[NSString stringWithFormat:systemStatsFormat, |
| 96 | (long)ARDGetCpuUsagePercentage()]]; |
| 97 | |
| 98 | // Connection stats. |
| 99 | NSString *connStatsFormat = @"CN %@ms | %@->%@/%@ | (s)%@ | (r)%@\n"; |
| 100 | [result appendString:[NSString stringWithFormat:connStatsFormat, |
| 101 | _connRtt, |
| 102 | _localCandType, _remoteCandType, _transportType, |
| 103 | _connSendBitrate, _connRecvBitrate]]; |
| 104 | |
| 105 | // Video send stats. |
| 106 | NSString *videoSendFormat = @"VS (input) %@x%@@%@fps | (sent) %@x%@@%@fps\n" |
denicija | b6c456b | 2016-12-19 02:14:08 -0800 | [diff] [blame] | 107 | "VS (enc) %@/%@ | (sent) %@/%@ | %@ms | %@\n" |
| 108 | "AvgQP (past %d encoded frames) = %d\n "; |
| 109 | int avgqp = [self calculateAvgQP]; |
| 110 | |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 111 | [result appendString:[NSString stringWithFormat:videoSendFormat, |
| 112 | _videoInputWidth, _videoInputHeight, _videoInputFps, |
| 113 | _videoSendWidth, _videoSendHeight, _videoSendFps, |
| 114 | _actualEncBitrate, _targetEncBitrate, |
| 115 | _videoSendBitrate, _availableSendBw, |
| 116 | _videoEncodeMs, |
denicija | b6c456b | 2016-12-19 02:14:08 -0800 | [diff] [blame] | 117 | _videoSendCodec, |
| 118 | _framesEncoded - _oldFramesEncoded, avgqp]]; |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 119 | |
| 120 | // Video receive stats. |
| 121 | NSString *videoReceiveFormat = |
| 122 | @"VR (recv) %@x%@@%@fps | (decoded)%@ | (output)%@fps | %@/%@ | %@ms\n"; |
| 123 | [result appendString:[NSString stringWithFormat:videoReceiveFormat, |
| 124 | _videoRecvWidth, _videoRecvHeight, _videoRecvFps, |
| 125 | _videoDecodedFps, |
| 126 | _videoOutputFps, |
| 127 | _videoRecvBitrate, _availableRecvBw, |
| 128 | _videoDecodeMs]]; |
| 129 | |
| 130 | // Audio send stats. |
| 131 | NSString *audioSendFormat = @"AS %@ | %@\n"; |
| 132 | [result appendString:[NSString stringWithFormat:audioSendFormat, |
| 133 | _audioSendBitrate, _audioSendCodec]]; |
| 134 | |
| 135 | // Audio receive stats. |
| 136 | NSString *audioReceiveFormat = @"AR %@ | %@ | %@ms | (expandrate)%@"; |
| 137 | [result appendString:[NSString stringWithFormat:audioReceiveFormat, |
| 138 | _audioRecvBitrate, _audioRecvCodec, _audioCurrentDelay, |
| 139 | _audioExpandRate]]; |
| 140 | |
| 141 | return result; |
| 142 | } |
| 143 | |
hbos | bd3dda6 | 2016-09-09 01:36:28 -0700 | [diff] [blame] | 144 | - (void)parseStatsReport:(RTCLegacyStatsReport *)statsReport { |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 145 | NSString *reportType = statsReport.type; |
| 146 | if ([reportType isEqualToString:@"ssrc"] && |
| 147 | [statsReport.reportId rangeOfString:@"ssrc"].location != NSNotFound) { |
| 148 | if ([statsReport.reportId rangeOfString:@"send"].location != NSNotFound) { |
| 149 | [self parseSendSsrcStatsReport:statsReport]; |
| 150 | } |
| 151 | if ([statsReport.reportId rangeOfString:@"recv"].location != NSNotFound) { |
| 152 | [self parseRecvSsrcStatsReport:statsReport]; |
| 153 | } |
| 154 | } else if ([reportType isEqualToString:@"VideoBwe"]) { |
| 155 | [self parseBweStatsReport:statsReport]; |
| 156 | } else if ([reportType isEqualToString:@"googCandidatePair"]) { |
| 157 | [self parseConnectionStatsReport:statsReport]; |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | #pragma mark - Private |
| 162 | |
denicija | b6c456b | 2016-12-19 02:14:08 -0800 | [diff] [blame] | 163 | - (int)calculateAvgQP { |
| 164 | int deltaFramesEncoded = _framesEncoded - _oldFramesEncoded; |
| 165 | int deltaQPSum = _videoQPSum - _oldVideoQPSum; |
| 166 | |
| 167 | return deltaFramesEncoded != 0 ? deltaQPSum / deltaFramesEncoded : 0; |
| 168 | } |
| 169 | |
Jiawei Ou | 4aeb35b | 2018-11-09 13:55:45 -0800 | [diff] [blame] | 170 | - (void)updateBweStatOfKey:(NSString *)key value:(NSString *)value { |
| 171 | if ([key isEqualToString:@"googAvailableSendBandwidth"]) { |
| 172 | _availableSendBw = [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
| 173 | } else if ([key isEqualToString:@"googAvailableReceiveBandwidth"]) { |
| 174 | _availableRecvBw = [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
| 175 | } else if ([key isEqualToString:@"googActualEncBitrate"]) { |
| 176 | _actualEncBitrate = [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
| 177 | } else if ([key isEqualToString:@"googTargetEncBitrate"]) { |
| 178 | _targetEncBitrate = [ARDBitrateTracker bitrateStringForBitrate:value.doubleValue]; |
| 179 | } |
| 180 | } |
| 181 | |
hbos | bd3dda6 | 2016-09-09 01:36:28 -0700 | [diff] [blame] | 182 | - (void)parseBweStatsReport:(RTCLegacyStatsReport *)statsReport { |
Jiawei Ou | 4aeb35b | 2018-11-09 13:55:45 -0800 | [diff] [blame] | 183 | [statsReport.values |
| 184 | enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) { |
| 185 | [self updateBweStatOfKey:key value:value]; |
| 186 | }]; |
| 187 | } |
| 188 | |
| 189 | - (void)updateConnectionStatOfKey:(NSString *)key value:(NSString *)value { |
| 190 | if ([key isEqualToString:@"googRtt"]) { |
| 191 | _connRtt = value; |
| 192 | } else if ([key isEqualToString:@"googLocalCandidateType"]) { |
| 193 | _localCandType = value; |
| 194 | } else if ([key isEqualToString:@"googRemoteCandidateType"]) { |
| 195 | _remoteCandType = value; |
| 196 | } else if ([key isEqualToString:@"googTransportType"]) { |
| 197 | _transportType = value; |
| 198 | } else if ([key isEqualToString:@"bytesReceived"]) { |
| 199 | NSInteger byteCount = value.integerValue; |
| 200 | [_connRecvBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 201 | _connRecvBitrate = _connRecvBitrateTracker.bitrateString; |
| 202 | } else if ([key isEqualToString:@"bytesSent"]) { |
| 203 | NSInteger byteCount = value.integerValue; |
| 204 | [_connSendBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 205 | _connSendBitrate = _connSendBitrateTracker.bitrateString; |
| 206 | } |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 207 | } |
| 208 | |
hbos | bd3dda6 | 2016-09-09 01:36:28 -0700 | [diff] [blame] | 209 | - (void)parseConnectionStatsReport:(RTCLegacyStatsReport *)statsReport { |
hjon | 79858f8 | 2016-03-13 22:08:26 -0700 | [diff] [blame] | 210 | NSString *activeConnection = statsReport.values[@"googActiveConnection"]; |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 211 | if (![activeConnection isEqualToString:@"true"]) { |
| 212 | return; |
| 213 | } |
Jiawei Ou | 4aeb35b | 2018-11-09 13:55:45 -0800 | [diff] [blame] | 214 | [statsReport.values |
| 215 | enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) { |
| 216 | [self updateConnectionStatOfKey:key value:value]; |
| 217 | }]; |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 218 | } |
| 219 | |
hbos | bd3dda6 | 2016-09-09 01:36:28 -0700 | [diff] [blame] | 220 | - (void)parseSendSsrcStatsReport:(RTCLegacyStatsReport *)statsReport { |
hjon | 79858f8 | 2016-03-13 22:08:26 -0700 | [diff] [blame] | 221 | NSDictionary *values = statsReport.values; |
stefan | ff4def7 | 2016-06-26 12:08:39 -0700 | [diff] [blame] | 222 | if ([values objectForKey:@"googFrameRateSent"]) { |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 223 | // Video track. |
| 224 | [self parseVideoSendStatsReport:statsReport]; |
stefan | ff4def7 | 2016-06-26 12:08:39 -0700 | [diff] [blame] | 225 | } else if ([values objectForKey:@"audioInputLevel"]) { |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 226 | // Audio track. |
| 227 | [self parseAudioSendStatsReport:statsReport]; |
| 228 | } |
| 229 | } |
| 230 | |
Jiawei Ou | 4aeb35b | 2018-11-09 13:55:45 -0800 | [diff] [blame] | 231 | - (void)updateAudioSendStatOfKey:(NSString *)key value:(NSString *)value { |
| 232 | if ([key isEqualToString:@"googCodecName"]) { |
| 233 | _audioSendCodec = value; |
| 234 | } else if ([key isEqualToString:@"bytesSent"]) { |
| 235 | NSInteger byteCount = value.integerValue; |
| 236 | [_audioSendBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 237 | _audioSendBitrate = _audioSendBitrateTracker.bitrateString; |
| 238 | } |
| 239 | } |
| 240 | |
hbos | bd3dda6 | 2016-09-09 01:36:28 -0700 | [diff] [blame] | 241 | - (void)parseAudioSendStatsReport:(RTCLegacyStatsReport *)statsReport { |
Jiawei Ou | 4aeb35b | 2018-11-09 13:55:45 -0800 | [diff] [blame] | 242 | [statsReport.values |
| 243 | enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) { |
| 244 | [self updateAudioSendStatOfKey:key value:value]; |
| 245 | }]; |
| 246 | } |
| 247 | |
| 248 | - (void)updateVideoSendStatOfKey:(NSString *)key value:(NSString *)value { |
| 249 | if ([key isEqualToString:@"googCodecName"]) { |
| 250 | _videoSendCodec = value; |
| 251 | } else if ([key isEqualToString:@"googFrameHeightInput"]) { |
| 252 | _videoInputHeight = value; |
| 253 | } else if ([key isEqualToString:@"googFrameWidthInput"]) { |
| 254 | _videoInputWidth = value; |
| 255 | } else if ([key isEqualToString:@"googFrameRateInput"]) { |
| 256 | _videoInputFps = value; |
| 257 | } else if ([key isEqualToString:@"googFrameHeightSent"]) { |
| 258 | _videoSendHeight = value; |
| 259 | } else if ([key isEqualToString:@"googFrameWidthSent"]) { |
| 260 | _videoSendWidth = value; |
| 261 | } else if ([key isEqualToString:@"googFrameRateSent"]) { |
| 262 | _videoSendFps = value; |
| 263 | } else if ([key isEqualToString:@"googAvgEncodeMs"]) { |
| 264 | _videoEncodeMs = value; |
| 265 | } else if ([key isEqualToString:@"bytesSent"]) { |
| 266 | NSInteger byteCount = value.integerValue; |
| 267 | [_videoSendBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 268 | _videoSendBitrate = _videoSendBitrateTracker.bitrateString; |
| 269 | } else if ([key isEqualToString:@"qpSum"]) { |
| 270 | _oldVideoQPSum = _videoQPSum; |
| 271 | _videoQPSum = value.integerValue; |
| 272 | } else if ([key isEqualToString:@"framesEncoded"]) { |
| 273 | _oldFramesEncoded = _framesEncoded; |
| 274 | _framesEncoded = value.integerValue; |
| 275 | } |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 276 | } |
| 277 | |
hbos | bd3dda6 | 2016-09-09 01:36:28 -0700 | [diff] [blame] | 278 | - (void)parseVideoSendStatsReport:(RTCLegacyStatsReport *)statsReport { |
Jiawei Ou | 4aeb35b | 2018-11-09 13:55:45 -0800 | [diff] [blame] | 279 | [statsReport.values |
| 280 | enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) { |
| 281 | [self updateVideoSendStatOfKey:key value:value]; |
| 282 | }]; |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 283 | } |
| 284 | |
hbos | bd3dda6 | 2016-09-09 01:36:28 -0700 | [diff] [blame] | 285 | - (void)parseRecvSsrcStatsReport:(RTCLegacyStatsReport *)statsReport { |
hjon | 79858f8 | 2016-03-13 22:08:26 -0700 | [diff] [blame] | 286 | NSDictionary *values = statsReport.values; |
stefan | ff4def7 | 2016-06-26 12:08:39 -0700 | [diff] [blame] | 287 | if ([values objectForKey:@"googFrameWidthReceived"]) { |
| 288 | // Video track. |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 289 | [self parseVideoRecvStatsReport:statsReport]; |
stefan | ff4def7 | 2016-06-26 12:08:39 -0700 | [diff] [blame] | 290 | } else if ([values objectForKey:@"audioOutputLevel"]) { |
| 291 | // Audio track. |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 292 | [self parseAudioRecvStatsReport:statsReport]; |
| 293 | } |
| 294 | } |
| 295 | |
Jiawei Ou | 4aeb35b | 2018-11-09 13:55:45 -0800 | [diff] [blame] | 296 | - (void)updateAudioRecvStatOfKey:(NSString *)key value:(NSString *)value { |
| 297 | if ([key isEqualToString:@"googCodecName"]) { |
| 298 | _audioRecvCodec = value; |
| 299 | } else if ([key isEqualToString:@"bytesReceived"]) { |
| 300 | NSInteger byteCount = value.integerValue; |
| 301 | [_audioRecvBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 302 | _audioRecvBitrate = _audioRecvBitrateTracker.bitrateString; |
| 303 | } else if ([key isEqualToString:@"googSpeechExpandRate"]) { |
| 304 | _audioExpandRate = value; |
| 305 | } else if ([key isEqualToString:@"googCurrentDelayMs"]) { |
| 306 | _audioCurrentDelay = value; |
| 307 | } |
| 308 | } |
| 309 | |
hbos | bd3dda6 | 2016-09-09 01:36:28 -0700 | [diff] [blame] | 310 | - (void)parseAudioRecvStatsReport:(RTCLegacyStatsReport *)statsReport { |
Jiawei Ou | 4aeb35b | 2018-11-09 13:55:45 -0800 | [diff] [blame] | 311 | [statsReport.values |
| 312 | enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) { |
| 313 | [self updateAudioRecvStatOfKey:key value:value]; |
| 314 | }]; |
| 315 | } |
| 316 | |
| 317 | - (void)updateVideoRecvStatOfKey:(NSString *)key value:(NSString *)value { |
| 318 | if ([key isEqualToString:@"googFrameHeightReceived"]) { |
| 319 | _videoRecvHeight = value; |
| 320 | } else if ([key isEqualToString:@"googFrameWidthReceived"]) { |
| 321 | _videoRecvWidth = value; |
| 322 | } else if ([key isEqualToString:@"googFrameRateReceived"]) { |
| 323 | _videoRecvFps = value; |
| 324 | } else if ([key isEqualToString:@"googFrameRateDecoded"]) { |
| 325 | _videoDecodedFps = value; |
| 326 | } else if ([key isEqualToString:@"googFrameRateOutput"]) { |
| 327 | _videoOutputFps = value; |
| 328 | } else if ([key isEqualToString:@"googDecodeMs"]) { |
| 329 | _videoDecodeMs = value; |
| 330 | } else if ([key isEqualToString:@"bytesReceived"]) { |
| 331 | NSInteger byteCount = value.integerValue; |
| 332 | [_videoRecvBitrateTracker updateBitrateWithCurrentByteCount:byteCount]; |
| 333 | _videoRecvBitrate = _videoRecvBitrateTracker.bitrateString; |
| 334 | } |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 335 | } |
| 336 | |
hbos | bd3dda6 | 2016-09-09 01:36:28 -0700 | [diff] [blame] | 337 | - (void)parseVideoRecvStatsReport:(RTCLegacyStatsReport *)statsReport { |
Jiawei Ou | 4aeb35b | 2018-11-09 13:55:45 -0800 | [diff] [blame] | 338 | [statsReport.values |
| 339 | enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) { |
| 340 | [self updateVideoRecvStatOfKey:key value:value]; |
| 341 | }]; |
Zeke Chin | d332580 | 2015-08-14 11:00:02 -0700 | [diff] [blame] | 342 | } |
| 343 | |
| 344 | @end |
| 345 | |