1 /* 2 * libjingle 3 * Copyright 2012, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "talk/app/webrtc/statscollector.h" 29 30 #include <utility> 31 #include <vector> 32 33 #include "talk/base/base64.h" 34 #include "talk/base/scoped_ptr.h" 35 #include "talk/session/media/channel.h" 36 37 namespace webrtc { 38 39 // The items below are in alphabetical order. 40 const char StatsReport::kStatsValueNameActiveConnection[] = 41 "googActiveConnection"; 42 const char StatsReport::kStatsValueNameActualEncBitrate[] = 43 "googActualEncBitrate"; 44 const char StatsReport::kStatsValueNameAudioOutputLevel[] = "audioOutputLevel"; 45 const char StatsReport::kStatsValueNameAudioInputLevel[] = "audioInputLevel"; 46 const char StatsReport::kStatsValueNameAvailableReceiveBandwidth[] = 47 "googAvailableReceiveBandwidth"; 48 const char StatsReport::kStatsValueNameAvailableSendBandwidth[] = 49 "googAvailableSendBandwidth"; 50 const char StatsReport::kStatsValueNameAvgEncodeMs[] = "googAvgEncodeMs"; 51 const char StatsReport::kStatsValueNameBucketDelay[] = "googBucketDelay"; 52 const char StatsReport::kStatsValueNameBytesReceived[] = "bytesReceived"; 53 const char StatsReport::kStatsValueNameBytesSent[] = "bytesSent"; 54 const char StatsReport::kStatsValueNameBandwidthLimitedResolution[] = 55 "googBandwidthLimitedResolution"; 56 const char StatsReport::kStatsValueNameCaptureJitterMs[] = 57 "googCaptureJitterMs"; 58 const char StatsReport::kStatsValueNameCaptureQueueDelayMsPerS[] = 59 "googCaptureQueueDelayMsPerS"; 60 const char StatsReport::kStatsValueNameChannelId[] = "googChannelId"; 61 const char StatsReport::kStatsValueNameCodecName[] = "googCodecName"; 62 const char StatsReport::kStatsValueNameComponent[] = "googComponent"; 63 const char StatsReport::kStatsValueNameContentName[] = "googContentName"; 64 const char StatsReport::kStatsValueNameCpuLimitedResolution[] = 65 "googCpuLimitedResolution"; 66 const char StatsReport::kStatsValueNameDecodingCTSG[] = 67 "googDecodingCTSG"; 68 const char StatsReport::kStatsValueNameDecodingCTN[] = 69 "googDecodingCTN"; 70 const char StatsReport::kStatsValueNameDecodingNormal[] = 71 "googDecodingNormal"; 72 const char StatsReport::kStatsValueNameDecodingPLC[] = 73 "googDecodingPLC"; 74 const char StatsReport::kStatsValueNameDecodingCNG[] = 75 "googDecodingCNG"; 76 const char StatsReport::kStatsValueNameDecodingPLCCNG[] = 77 "googDecodingPLCCNG"; 78 const char StatsReport::kStatsValueNameDer[] = "googDerBase64"; 79 // Echo metrics from the audio processing module. 80 const char StatsReport::kStatsValueNameEchoCancellationQualityMin[] = 81 "googEchoCancellationQualityMin"; 82 const char StatsReport::kStatsValueNameEchoDelayMedian[] = 83 "googEchoCancellationEchoDelayMedian"; 84 const char StatsReport::kStatsValueNameEchoDelayStdDev[] = 85 "googEchoCancellationEchoDelayStdDev"; 86 const char StatsReport::kStatsValueNameEchoReturnLoss[] = 87 "googEchoCancellationReturnLoss"; 88 const char StatsReport::kStatsValueNameEchoReturnLossEnhancement[] = 89 "googEchoCancellationReturnLossEnhancement"; 90 91 const char StatsReport::kStatsValueNameEncodeRelStdDev[] = 92 "googEncodeRelStdDev"; 93 const char StatsReport::kStatsValueNameEncodeUsagePercent[] = 94 "googEncodeUsagePercent"; 95 const char StatsReport::kStatsValueNameExpandRate[] = "googExpandRate"; 96 const char StatsReport::kStatsValueNameFingerprint[] = "googFingerprint"; 97 const char StatsReport::kStatsValueNameFingerprintAlgorithm[] = 98 "googFingerprintAlgorithm"; 99 const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived"; 100 const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent"; 101 const char StatsReport::kStatsValueNameFrameHeightInput[] = 102 "googFrameHeightInput"; 103 const char StatsReport::kStatsValueNameFrameHeightReceived[] = 104 "googFrameHeightReceived"; 105 const char StatsReport::kStatsValueNameFrameHeightSent[] = 106 "googFrameHeightSent"; 107 const char StatsReport::kStatsValueNameFrameRateReceived[] = 108 "googFrameRateReceived"; 109 const char StatsReport::kStatsValueNameFrameRateDecoded[] = 110 "googFrameRateDecoded"; 111 const char StatsReport::kStatsValueNameFrameRateOutput[] = 112 "googFrameRateOutput"; 113 const char StatsReport::kStatsValueNameDecodeMs[] = "googDecodeMs"; 114 const char StatsReport::kStatsValueNameMaxDecodeMs[] = "googMaxDecodeMs"; 115 const char StatsReport::kStatsValueNameCurrentDelayMs[] = "googCurrentDelayMs"; 116 const char StatsReport::kStatsValueNameTargetDelayMs[] = "googTargetDelayMs"; 117 const char StatsReport::kStatsValueNameJitterBufferMs[] = "googJitterBufferMs"; 118 const char StatsReport::kStatsValueNameMinPlayoutDelayMs[] = 119 "googMinPlayoutDelayMs"; 120 const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs"; 121 122 const char StatsReport::kStatsValueNameCaptureStartNtpTimeMs[] = 123 "googCaptureStartNtpTimeMs"; 124 125 const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput"; 126 const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent"; 127 const char StatsReport::kStatsValueNameFrameWidthInput[] = 128 "googFrameWidthInput"; 129 const char StatsReport::kStatsValueNameFrameWidthReceived[] = 130 "googFrameWidthReceived"; 131 const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent"; 132 const char StatsReport::kStatsValueNameInitiator[] = "googInitiator"; 133 const char StatsReport::kStatsValueNameIssuerId[] = "googIssuerId"; 134 const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived"; 135 const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress"; 136 const char StatsReport::kStatsValueNameLocalCandidateType[] = 137 "googLocalCandidateType"; 138 const char StatsReport::kStatsValueNameLocalCertificateId[] = 139 "googLocalCertificateId"; 140 const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived"; 141 const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent"; 142 const char StatsReport::kStatsValueNamePlisReceived[] = "googPlisReceived"; 143 const char StatsReport::kStatsValueNamePlisSent[] = "googPlisSent"; 144 const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived"; 145 const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent"; 146 const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost"; 147 const char StatsReport::kStatsValueNamePreferredJitterBufferMs[] = 148 "googPreferredJitterBufferMs"; 149 const char StatsReport::kStatsValueNameReadable[] = "googReadable"; 150 const char StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug[] = 151 "googReceivedPacketGroupArrivalTimeDebug"; 152 const char StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug[] = 153 "googReceivedPacketGroupPropagationDeltaDebug"; 154 const char 155 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug[] = 156 "googReceivedPacketGroupPropagationDeltaSumDebug"; 157 const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress"; 158 const char StatsReport::kStatsValueNameRemoteCandidateType[] = 159 "googRemoteCandidateType"; 160 const char StatsReport::kStatsValueNameRemoteCertificateId[] = 161 "googRemoteCertificateId"; 162 const char StatsReport::kStatsValueNameRetransmitBitrate[] = 163 "googRetransmitBitrate"; 164 const char StatsReport::kStatsValueNameRtt[] = "googRtt"; 165 const char StatsReport::kStatsValueNameSsrc[] = "ssrc"; 166 const char StatsReport::kStatsValueNameTargetEncBitrate[] = 167 "googTargetEncBitrate"; 168 const char StatsReport::kStatsValueNameTransmitBitrate[] = 169 "googTransmitBitrate"; 170 const char StatsReport::kStatsValueNameTransportId[] = "transportId"; 171 const char StatsReport::kStatsValueNameTransportType[] = "googTransportType"; 172 const char StatsReport::kStatsValueNameTrackId[] = "googTrackId"; 173 const char StatsReport::kStatsValueNameTypingNoiseState[] = 174 "googTypingNoiseState"; 175 const char StatsReport::kStatsValueNameViewLimitedResolution[] = 176 "googViewLimitedResolution"; 177 const char StatsReport::kStatsValueNameWritable[] = "googWritable"; 178 179 const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession"; 180 const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe"; 181 const char StatsReport::kStatsReportTypeRemoteSsrc[] = "remoteSsrc"; 182 const char StatsReport::kStatsReportTypeSsrc[] = "ssrc"; 183 const char StatsReport::kStatsReportTypeTrack[] = "googTrack"; 184 const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate"; 185 const char StatsReport::kStatsReportTypeTransport[] = "googTransport"; 186 const char StatsReport::kStatsReportTypeComponent[] = "googComponent"; 187 const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair"; 188 const char StatsReport::kStatsReportTypeCertificate[] = "googCertificate"; 189 190 const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo"; 191 192 // Implementations of functions in statstypes.h 193 void StatsReport::AddValue(const std::string& name, const std::string& value) { 194 Value temp; 195 temp.name = name; 196 temp.value = value; 197 values.push_back(temp); 198 } 199 200 void StatsReport::AddValue(const std::string& name, int64 value) { 201 AddValue(name, talk_base::ToString<int64>(value)); 202 } 203 204 template <typename T> 205 void StatsReport::AddValue(const std::string& name, 206 const std::vector<T>& value) { 207 std::ostringstream oss; 208 oss << "["; 209 for (size_t i = 0; i < value.size(); ++i) { 210 oss << talk_base::ToString<T>(value[i]); 211 if (i != value.size() - 1) 212 oss << ", "; 213 } 214 oss << "]"; 215 AddValue(name, oss.str()); 216 } 217 218 void StatsReport::AddBoolean(const std::string& name, bool value) { 219 AddValue(name, value ? "true" : "false"); 220 } 221 222 void StatsReport::ReplaceValue(const std::string& name, 223 const std::string& value) { 224 for (Values::iterator it = values.begin(); it != values.end(); ++it) { 225 if ((*it).name == name) { 226 it->value = value; 227 return; 228 } 229 } 230 // It is not reachable here, add an ASSERT to make sure the overwriting is 231 // always a success. 232 ASSERT(false); 233 } 234 235 namespace { 236 typedef std::map<std::string, StatsReport> StatsMap; 237 238 std::string StatsId(const std::string& type, const std::string& id) { 239 return type + "_" + id; 240 } 241 242 std::string StatsId(const std::string& type, const std::string& id, 243 StatsCollector::TrackDirection direction) { 244 ASSERT(direction == StatsCollector::kSending || 245 direction == StatsCollector::kReceiving); 246 247 // Strings for the direction of the track. 248 const char kSendDirection[] = "send"; 249 const char kRecvDirection[] = "recv"; 250 251 const std::string direction_id = (direction == StatsCollector::kSending) ? 252 kSendDirection : kRecvDirection; 253 return type + "_" + id + "_" + direction_id; 254 } 255 256 bool ExtractValueFromReport( 257 const StatsReport& report, 258 const std::string& name, 259 std::string* value) { 260 StatsReport::Values::const_iterator it = report.values.begin(); 261 for (; it != report.values.end(); ++it) { 262 if (it->name == name) { 263 *value = it->value; 264 return true; 265 } 266 } 267 return false; 268 } 269 270 template <class TrackVector> 271 void CreateTrackReports(const TrackVector& tracks, StatsMap* reports) { 272 for (size_t j = 0; j < tracks.size(); ++j) { 273 webrtc::MediaStreamTrackInterface* track = tracks[j]; 274 // Adds an empty track report. 275 StatsReport report; 276 report.type = StatsReport::kStatsReportTypeTrack; 277 report.id = StatsId(StatsReport::kStatsReportTypeTrack, track->id()); 278 report.AddValue(StatsReport::kStatsValueNameTrackId, 279 track->id()); 280 (*reports)[report.id] = report; 281 } 282 } 283 284 void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) { 285 report->AddValue(StatsReport::kStatsValueNameAudioOutputLevel, 286 info.audio_level); 287 report->AddValue(StatsReport::kStatsValueNameBytesReceived, 288 info.bytes_rcvd); 289 report->AddValue(StatsReport::kStatsValueNameJitterReceived, 290 info.jitter_ms); 291 report->AddValue(StatsReport::kStatsValueNameJitterBufferMs, 292 info.jitter_buffer_ms); 293 report->AddValue(StatsReport::kStatsValueNamePreferredJitterBufferMs, 294 info.jitter_buffer_preferred_ms); 295 report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs, 296 info.delay_estimate_ms); 297 report->AddValue(StatsReport::kStatsValueNameExpandRate, 298 talk_base::ToString<float>(info.expand_rate)); 299 report->AddValue(StatsReport::kStatsValueNamePacketsReceived, 300 info.packets_rcvd); 301 report->AddValue(StatsReport::kStatsValueNamePacketsLost, 302 info.packets_lost); 303 report->AddValue(StatsReport::kStatsValueNameDecodingCTSG, 304 info.decoding_calls_to_silence_generator); 305 report->AddValue(StatsReport::kStatsValueNameDecodingCTN, 306 info.decoding_calls_to_neteq); 307 report->AddValue(StatsReport::kStatsValueNameDecodingNormal, 308 info.decoding_normal); 309 report->AddValue(StatsReport::kStatsValueNameDecodingPLC, 310 info.decoding_plc); 311 report->AddValue(StatsReport::kStatsValueNameDecodingCNG, 312 info.decoding_cng); 313 report->AddValue(StatsReport::kStatsValueNameDecodingPLCCNG, 314 info.decoding_plc_cng); 315 report->AddValue(StatsReport::kStatsValueNameCaptureStartNtpTimeMs, 316 info.capture_start_ntp_time_ms); 317 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name); 318 } 319 320 void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) { 321 report->AddValue(StatsReport::kStatsValueNameAudioInputLevel, 322 info.audio_level); 323 report->AddValue(StatsReport::kStatsValueNameBytesSent, 324 info.bytes_sent); 325 report->AddValue(StatsReport::kStatsValueNamePacketsSent, 326 info.packets_sent); 327 report->AddValue(StatsReport::kStatsValueNamePacketsLost, 328 info.packets_lost); 329 report->AddValue(StatsReport::kStatsValueNameJitterReceived, 330 info.jitter_ms); 331 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms); 332 report->AddValue(StatsReport::kStatsValueNameEchoCancellationQualityMin, 333 talk_base::ToString<float>(info.aec_quality_min)); 334 report->AddValue(StatsReport::kStatsValueNameEchoDelayMedian, 335 info.echo_delay_median_ms); 336 report->AddValue(StatsReport::kStatsValueNameEchoDelayStdDev, 337 info.echo_delay_std_ms); 338 report->AddValue(StatsReport::kStatsValueNameEchoReturnLoss, 339 info.echo_return_loss); 340 report->AddValue(StatsReport::kStatsValueNameEchoReturnLossEnhancement, 341 info.echo_return_loss_enhancement); 342 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name); 343 report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState, 344 info.typing_noise_detected); 345 } 346 347 void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) { 348 report->AddValue(StatsReport::kStatsValueNameBytesReceived, 349 info.bytes_rcvd); 350 report->AddValue(StatsReport::kStatsValueNamePacketsReceived, 351 info.packets_rcvd); 352 report->AddValue(StatsReport::kStatsValueNamePacketsLost, 353 info.packets_lost); 354 355 report->AddValue(StatsReport::kStatsValueNameFirsSent, 356 info.firs_sent); 357 report->AddValue(StatsReport::kStatsValueNamePlisSent, 358 info.plis_sent); 359 report->AddValue(StatsReport::kStatsValueNameNacksSent, 360 info.nacks_sent); 361 report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived, 362 info.frame_width); 363 report->AddValue(StatsReport::kStatsValueNameFrameHeightReceived, 364 info.frame_height); 365 report->AddValue(StatsReport::kStatsValueNameFrameRateReceived, 366 info.framerate_rcvd); 367 report->AddValue(StatsReport::kStatsValueNameFrameRateDecoded, 368 info.framerate_decoded); 369 report->AddValue(StatsReport::kStatsValueNameFrameRateOutput, 370 info.framerate_output); 371 372 report->AddValue(StatsReport::kStatsValueNameDecodeMs, 373 info.decode_ms); 374 report->AddValue(StatsReport::kStatsValueNameMaxDecodeMs, 375 info.max_decode_ms); 376 report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs, 377 info.current_delay_ms); 378 report->AddValue(StatsReport::kStatsValueNameTargetDelayMs, 379 info.target_delay_ms); 380 report->AddValue(StatsReport::kStatsValueNameJitterBufferMs, 381 info.jitter_buffer_ms); 382 report->AddValue(StatsReport::kStatsValueNameMinPlayoutDelayMs, 383 info.min_playout_delay_ms); 384 report->AddValue(StatsReport::kStatsValueNameRenderDelayMs, 385 info.render_delay_ms); 386 387 report->AddValue(StatsReport::kStatsValueNameCaptureStartNtpTimeMs, 388 info.capture_start_ntp_time_ms); 389 } 390 391 void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { 392 report->AddValue(StatsReport::kStatsValueNameBytesSent, 393 info.bytes_sent); 394 report->AddValue(StatsReport::kStatsValueNamePacketsSent, 395 info.packets_sent); 396 report->AddValue(StatsReport::kStatsValueNamePacketsLost, 397 info.packets_lost); 398 399 report->AddValue(StatsReport::kStatsValueNameFirsReceived, 400 info.firs_rcvd); 401 report->AddValue(StatsReport::kStatsValueNamePlisReceived, 402 info.plis_rcvd); 403 report->AddValue(StatsReport::kStatsValueNameNacksReceived, 404 info.nacks_rcvd); 405 report->AddValue(StatsReport::kStatsValueNameFrameWidthInput, 406 info.input_frame_width); 407 report->AddValue(StatsReport::kStatsValueNameFrameHeightInput, 408 info.input_frame_height); 409 report->AddValue(StatsReport::kStatsValueNameFrameWidthSent, 410 info.send_frame_width); 411 report->AddValue(StatsReport::kStatsValueNameFrameHeightSent, 412 info.send_frame_height); 413 report->AddValue(StatsReport::kStatsValueNameFrameRateInput, 414 info.framerate_input); 415 report->AddValue(StatsReport::kStatsValueNameFrameRateSent, 416 info.framerate_sent); 417 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms); 418 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name); 419 report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution, 420 (info.adapt_reason & 0x1) > 0); 421 report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution, 422 (info.adapt_reason & 0x2) > 0); 423 report->AddBoolean(StatsReport::kStatsValueNameViewLimitedResolution, 424 (info.adapt_reason & 0x4) > 0); 425 report->AddValue(StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms); 426 report->AddValue(StatsReport::kStatsValueNameCaptureJitterMs, 427 info.capture_jitter_ms); 428 report->AddValue(StatsReport::kStatsValueNameCaptureQueueDelayMsPerS, 429 info.capture_queue_delay_ms_per_s); 430 report->AddValue(StatsReport::kStatsValueNameEncodeUsagePercent, 431 info.encode_usage_percent); 432 report->AddValue(StatsReport::kStatsValueNameEncodeRelStdDev, 433 info.encode_rsd); 434 } 435 436 void ExtractStats(const cricket::BandwidthEstimationInfo& info, 437 double stats_gathering_started, 438 PeerConnectionInterface::StatsOutputLevel level, 439 StatsReport* report) { 440 report->id = StatsReport::kStatsReportVideoBweId; 441 report->type = StatsReport::kStatsReportTypeBwe; 442 443 // Clear out stats from previous GatherStats calls if any. 444 if (report->timestamp != stats_gathering_started) { 445 report->values.clear(); 446 report->timestamp = stats_gathering_started; 447 } 448 449 report->AddValue(StatsReport::kStatsValueNameAvailableSendBandwidth, 450 info.available_send_bandwidth); 451 report->AddValue(StatsReport::kStatsValueNameAvailableReceiveBandwidth, 452 info.available_recv_bandwidth); 453 report->AddValue(StatsReport::kStatsValueNameTargetEncBitrate, 454 info.target_enc_bitrate); 455 report->AddValue(StatsReport::kStatsValueNameActualEncBitrate, 456 info.actual_enc_bitrate); 457 report->AddValue(StatsReport::kStatsValueNameRetransmitBitrate, 458 info.retransmit_bitrate); 459 report->AddValue(StatsReport::kStatsValueNameTransmitBitrate, 460 info.transmit_bitrate); 461 report->AddValue(StatsReport::kStatsValueNameBucketDelay, 462 info.bucket_delay); 463 if (level >= PeerConnectionInterface::kStatsOutputLevelDebug) { 464 report->AddValue( 465 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug, 466 info.total_received_propagation_delta_ms); 467 if (info.recent_received_propagation_delta_ms.size() > 0) { 468 report->AddValue( 469 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug, 470 info.recent_received_propagation_delta_ms); 471 report->AddValue( 472 StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug, 473 info.recent_received_packet_group_arrival_time_ms); 474 } 475 } 476 } 477 478 void ExtractRemoteStats(const cricket::MediaSenderInfo& info, 479 StatsReport* report) { 480 report->timestamp = info.remote_stats[0].timestamp; 481 // TODO(hta): Extract some stats here. 482 } 483 484 void ExtractRemoteStats(const cricket::MediaReceiverInfo& info, 485 StatsReport* report) { 486 report->timestamp = info.remote_stats[0].timestamp; 487 // TODO(hta): Extract some stats here. 488 } 489 490 // Template to extract stats from a data vector. 491 // In order to use the template, the functions that are called from it, 492 // ExtractStats and ExtractRemoteStats, must be defined and overloaded 493 // for each type. 494 template<typename T> 495 void ExtractStatsFromList(const std::vector<T>& data, 496 const std::string& transport_id, 497 StatsCollector* collector, 498 StatsCollector::TrackDirection direction) { 499 typename std::vector<T>::const_iterator it = data.begin(); 500 for (; it != data.end(); ++it) { 501 std::string id; 502 uint32 ssrc = it->ssrc(); 503 // Each track can have stats for both local and remote objects. 504 // TODO(hta): Handle the case of multiple SSRCs per object. 505 StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id, 506 direction); 507 if (report) 508 ExtractStats(*it, report); 509 510 if (it->remote_stats.size() > 0) { 511 report = collector->PrepareRemoteReport(ssrc, transport_id, 512 direction); 513 if (!report) { 514 continue; 515 } 516 ExtractRemoteStats(*it, report); 517 } 518 } 519 } 520 521 } // namespace 522 523 StatsCollector::StatsCollector() 524 : session_(NULL), stats_gathering_started_(0) { 525 } 526 527 // Adds a MediaStream with tracks that can be used as a |selector| in a call 528 // to GetStats. 529 void StatsCollector::AddStream(MediaStreamInterface* stream) { 530 ASSERT(stream != NULL); 531 532 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(), 533 &reports_); 534 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(), 535 &reports_); 536 } 537 538 void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track, 539 uint32 ssrc) { 540 ASSERT(audio_track != NULL); 541 #ifdef _DEBUG 542 for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin(); 543 it != local_audio_tracks_.end(); ++it) { 544 ASSERT(it->first != audio_track || it->second != ssrc); 545 } 546 #endif 547 local_audio_tracks_.push_back(std::make_pair(audio_track, ssrc)); 548 } 549 550 void StatsCollector::RemoveLocalAudioTrack(AudioTrackInterface* audio_track, 551 uint32 ssrc) { 552 ASSERT(audio_track != NULL); 553 for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin(); 554 it != local_audio_tracks_.end(); ++it) { 555 if (it->first == audio_track && it->second == ssrc) { 556 local_audio_tracks_.erase(it); 557 return; 558 } 559 } 560 561 ASSERT(false); 562 } 563 564 bool StatsCollector::GetStats(MediaStreamTrackInterface* track, 565 StatsReports* reports) { 566 ASSERT(reports != NULL); 567 reports->clear(); 568 569 StatsMap::iterator it; 570 if (!track) { 571 for (it = reports_.begin(); it != reports_.end(); ++it) { 572 reports->push_back(it->second); 573 } 574 return true; 575 } 576 577 it = reports_.find(StatsId(StatsReport::kStatsReportTypeSession, 578 session_->id())); 579 if (it != reports_.end()) { 580 reports->push_back(it->second); 581 } 582 583 it = reports_.find(StatsId(StatsReport::kStatsReportTypeTrack, track->id())); 584 585 if (it == reports_.end()) { 586 LOG(LS_WARNING) << "No StatsReport is available for "<< track->id(); 587 return false; 588 } 589 590 reports->push_back(it->second); 591 592 std::string track_id; 593 for (it = reports_.begin(); it != reports_.end(); ++it) { 594 if (it->second.type != StatsReport::kStatsReportTypeSsrc) { 595 continue; 596 } 597 if (ExtractValueFromReport(it->second, 598 StatsReport::kStatsValueNameTrackId, 599 &track_id)) { 600 if (track_id == track->id()) { 601 reports->push_back(it->second); 602 } 603 } 604 } 605 606 return true; 607 } 608 609 void 610 StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) { 611 double time_now = GetTimeNow(); 612 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of 613 // ms apart will be ignored. 614 const double kMinGatherStatsPeriod = 50; 615 if (stats_gathering_started_ + kMinGatherStatsPeriod > time_now) { 616 return; 617 } 618 stats_gathering_started_ = time_now; 619 620 if (session_) { 621 ExtractSessionInfo(); 622 ExtractVoiceInfo(); 623 ExtractVideoInfo(level); 624 } 625 } 626 627 StatsReport* StatsCollector::PrepareLocalReport( 628 uint32 ssrc, 629 const std::string& transport_id, 630 TrackDirection direction) { 631 const std::string ssrc_id = talk_base::ToString<uint32>(ssrc); 632 StatsMap::iterator it = reports_.find(StatsId( 633 StatsReport::kStatsReportTypeSsrc, ssrc_id, direction)); 634 635 std::string track_id; 636 if (it == reports_.end()) { 637 if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) 638 return NULL; 639 } else { 640 // Keeps the old track id since we want to report the stats for inactive 641 // tracks. 642 ExtractValueFromReport(it->second, 643 StatsReport::kStatsValueNameTrackId, 644 &track_id); 645 } 646 647 StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc, 648 ssrc_id, direction); 649 650 // Clear out stats from previous GatherStats calls if any. 651 if (report->timestamp != stats_gathering_started_) { 652 report->values.clear(); 653 report->timestamp = stats_gathering_started_; 654 } 655 656 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id); 657 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id); 658 // Add the mapping of SSRC to transport. 659 report->AddValue(StatsReport::kStatsValueNameTransportId, 660 transport_id); 661 return report; 662 } 663 664 StatsReport* StatsCollector::PrepareRemoteReport( 665 uint32 ssrc, 666 const std::string& transport_id, 667 TrackDirection direction) { 668 const std::string ssrc_id = talk_base::ToString<uint32>(ssrc); 669 StatsMap::iterator it = reports_.find(StatsId( 670 StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction)); 671 672 std::string track_id; 673 if (it == reports_.end()) { 674 if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) 675 return NULL; 676 } else { 677 // Keeps the old track id since we want to report the stats for inactive 678 // tracks. 679 ExtractValueFromReport(it->second, 680 StatsReport::kStatsValueNameTrackId, 681 &track_id); 682 } 683 684 StatsReport* report = GetOrCreateReport( 685 StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction); 686 687 // Clear out stats from previous GatherStats calls if any. 688 // The timestamp will be added later. Zero it for debugging. 689 report->values.clear(); 690 report->timestamp = 0; 691 692 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id); 693 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id); 694 // Add the mapping of SSRC to transport. 695 report->AddValue(StatsReport::kStatsValueNameTransportId, 696 transport_id); 697 return report; 698 } 699 700 std::string StatsCollector::AddOneCertificateReport( 701 const talk_base::SSLCertificate* cert, const std::string& issuer_id) { 702 // TODO(bemasc): Move this computation to a helper class that caches these 703 // values to reduce CPU use in GetStats. This will require adding a fast 704 // SSLCertificate::Equals() method to detect certificate changes. 705 706 std::string digest_algorithm; 707 if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm)) 708 return std::string(); 709 710 talk_base::scoped_ptr<talk_base::SSLFingerprint> ssl_fingerprint( 711 talk_base::SSLFingerprint::Create(digest_algorithm, cert)); 712 713 // SSLFingerprint::Create can fail if the algorithm returned by 714 // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the 715 // implementation of SSLCertificate::ComputeDigest. This currently happens 716 // with MD5- and SHA-224-signed certificates when linked to libNSS. 717 if (!ssl_fingerprint) 718 return std::string(); 719 720 std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint(); 721 722 talk_base::Buffer der_buffer; 723 cert->ToDER(&der_buffer); 724 std::string der_base64; 725 talk_base::Base64::EncodeFromArray( 726 der_buffer.data(), der_buffer.length(), &der_base64); 727 728 StatsReport report; 729 report.type = StatsReport::kStatsReportTypeCertificate; 730 report.id = StatsId(report.type, fingerprint); 731 report.timestamp = stats_gathering_started_; 732 report.AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint); 733 report.AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm, 734 digest_algorithm); 735 report.AddValue(StatsReport::kStatsValueNameDer, der_base64); 736 if (!issuer_id.empty()) 737 report.AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id); 738 reports_[report.id] = report; 739 return report.id; 740 } 741 742 std::string StatsCollector::AddCertificateReports( 743 const talk_base::SSLCertificate* cert) { 744 // Produces a chain of StatsReports representing this certificate and the rest 745 // of its chain, and adds those reports to |reports_|. The return value is 746 // the id of the leaf report. The provided cert must be non-null, so at least 747 // one report will always be provided and the returned string will never be 748 // empty. 749 ASSERT(cert != NULL); 750 751 std::string issuer_id; 752 talk_base::scoped_ptr<talk_base::SSLCertChain> chain; 753 if (cert->GetChain(chain.accept())) { 754 // This loop runs in reverse, i.e. from root to leaf, so that each 755 // certificate's issuer's report ID is known before the child certificate's 756 // report is generated. The root certificate does not have an issuer ID 757 // value. 758 for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) { 759 const talk_base::SSLCertificate& cert_i = chain->Get(i); 760 issuer_id = AddOneCertificateReport(&cert_i, issuer_id); 761 } 762 } 763 // Add the leaf certificate. 764 return AddOneCertificateReport(cert, issuer_id); 765 } 766 767 void StatsCollector::ExtractSessionInfo() { 768 // Extract information from the base session. 769 StatsReport report; 770 report.id = StatsId(StatsReport::kStatsReportTypeSession, session_->id()); 771 report.type = StatsReport::kStatsReportTypeSession; 772 report.timestamp = stats_gathering_started_; 773 report.values.clear(); 774 report.AddBoolean(StatsReport::kStatsValueNameInitiator, 775 session_->initiator()); 776 777 reports_[report.id] = report; 778 779 cricket::SessionStats stats; 780 if (session_->GetStats(&stats)) { 781 // Store the proxy map away for use in SSRC reporting. 782 proxy_to_transport_ = stats.proxy_to_transport; 783 784 for (cricket::TransportStatsMap::iterator transport_iter 785 = stats.transport_stats.begin(); 786 transport_iter != stats.transport_stats.end(); ++transport_iter) { 787 // Attempt to get a copy of the certificates from the transport and 788 // expose them in stats reports. All channels in a transport share the 789 // same local and remote certificates. 790 std::string local_cert_report_id, remote_cert_report_id; 791 cricket::Transport* transport = 792 session_->GetTransport(transport_iter->second.content_name); 793 if (transport) { 794 talk_base::scoped_ptr<talk_base::SSLIdentity> identity; 795 if (transport->GetIdentity(identity.accept())) 796 local_cert_report_id = AddCertificateReports( 797 &(identity->certificate())); 798 799 talk_base::scoped_ptr<talk_base::SSLCertificate> cert; 800 if (transport->GetRemoteCertificate(cert.accept())) 801 remote_cert_report_id = AddCertificateReports(cert.get()); 802 } 803 for (cricket::TransportChannelStatsList::iterator channel_iter 804 = transport_iter->second.channel_stats.begin(); 805 channel_iter != transport_iter->second.channel_stats.end(); 806 ++channel_iter) { 807 StatsReport channel_report; 808 std::ostringstream ostc; 809 ostc << "Channel-" << transport_iter->second.content_name 810 << "-" << channel_iter->component; 811 channel_report.id = ostc.str(); 812 channel_report.type = StatsReport::kStatsReportTypeComponent; 813 channel_report.timestamp = stats_gathering_started_; 814 channel_report.AddValue(StatsReport::kStatsValueNameComponent, 815 channel_iter->component); 816 if (!local_cert_report_id.empty()) 817 channel_report.AddValue( 818 StatsReport::kStatsValueNameLocalCertificateId, 819 local_cert_report_id); 820 if (!remote_cert_report_id.empty()) 821 channel_report.AddValue( 822 StatsReport::kStatsValueNameRemoteCertificateId, 823 remote_cert_report_id); 824 reports_[channel_report.id] = channel_report; 825 for (size_t i = 0; 826 i < channel_iter->connection_infos.size(); 827 ++i) { 828 StatsReport report; 829 const cricket::ConnectionInfo& info 830 = channel_iter->connection_infos[i]; 831 std::ostringstream ost; 832 ost << "Conn-" << transport_iter->first << "-" 833 << channel_iter->component << "-" << i; 834 report.id = ost.str(); 835 report.type = StatsReport::kStatsReportTypeCandidatePair; 836 report.timestamp = stats_gathering_started_; 837 // Link from connection to its containing channel. 838 report.AddValue(StatsReport::kStatsValueNameChannelId, 839 channel_report.id); 840 report.AddValue(StatsReport::kStatsValueNameBytesSent, 841 info.sent_total_bytes); 842 report.AddValue(StatsReport::kStatsValueNameBytesReceived, 843 info.recv_total_bytes); 844 report.AddBoolean(StatsReport::kStatsValueNameWritable, 845 info.writable); 846 report.AddBoolean(StatsReport::kStatsValueNameReadable, 847 info.readable); 848 report.AddBoolean(StatsReport::kStatsValueNameActiveConnection, 849 info.best_connection); 850 report.AddValue(StatsReport::kStatsValueNameLocalAddress, 851 info.local_candidate.address().ToString()); 852 report.AddValue(StatsReport::kStatsValueNameRemoteAddress, 853 info.remote_candidate.address().ToString()); 854 report.AddValue(StatsReport::kStatsValueNameRtt, info.rtt); 855 report.AddValue(StatsReport::kStatsValueNameTransportType, 856 info.local_candidate.protocol()); 857 report.AddValue(StatsReport::kStatsValueNameLocalCandidateType, 858 info.local_candidate.type()); 859 report.AddValue(StatsReport::kStatsValueNameRemoteCandidateType, 860 info.remote_candidate.type()); 861 reports_[report.id] = report; 862 } 863 } 864 } 865 } 866 } 867 868 void StatsCollector::ExtractVoiceInfo() { 869 if (!session_->voice_channel()) { 870 return; 871 } 872 cricket::VoiceMediaInfo voice_info; 873 if (!session_->voice_channel()->GetStats(&voice_info)) { 874 LOG(LS_ERROR) << "Failed to get voice channel stats."; 875 return; 876 } 877 std::string transport_id; 878 if (!GetTransportIdFromProxy(session_->voice_channel()->content_name(), 879 &transport_id)) { 880 LOG(LS_ERROR) << "Failed to get transport name for proxy " 881 << session_->voice_channel()->content_name(); 882 return; 883 } 884 ExtractStatsFromList(voice_info.receivers, transport_id, this, kReceiving); 885 ExtractStatsFromList(voice_info.senders, transport_id, this, kSending); 886 887 UpdateStatsFromExistingLocalAudioTracks(); 888 } 889 890 void StatsCollector::ExtractVideoInfo( 891 PeerConnectionInterface::StatsOutputLevel level) { 892 if (!session_->video_channel()) { 893 return; 894 } 895 cricket::StatsOptions options; 896 options.include_received_propagation_stats = 897 (level >= PeerConnectionInterface::kStatsOutputLevelDebug) ? 898 true : false; 899 cricket::VideoMediaInfo video_info; 900 if (!session_->video_channel()->GetStats(options, &video_info)) { 901 LOG(LS_ERROR) << "Failed to get video channel stats."; 902 return; 903 } 904 std::string transport_id; 905 if (!GetTransportIdFromProxy(session_->video_channel()->content_name(), 906 &transport_id)) { 907 LOG(LS_ERROR) << "Failed to get transport name for proxy " 908 << session_->video_channel()->content_name(); 909 return; 910 } 911 ExtractStatsFromList(video_info.receivers, transport_id, this, kReceiving); 912 ExtractStatsFromList(video_info.senders, transport_id, this, kSending); 913 if (video_info.bw_estimations.size() != 1) { 914 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size(); 915 } else { 916 StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId]; 917 ExtractStats( 918 video_info.bw_estimations[0], stats_gathering_started_, level, report); 919 } 920 } 921 922 double StatsCollector::GetTimeNow() { 923 return timing_.WallTimeNow() * talk_base::kNumMillisecsPerSec; 924 } 925 926 bool StatsCollector::GetTransportIdFromProxy(const std::string& proxy, 927 std::string* transport) { 928 // TODO(hta): Remove handling of empty proxy name once tests do not use it. 929 if (proxy.empty()) { 930 transport->clear(); 931 return true; 932 } 933 if (proxy_to_transport_.find(proxy) == proxy_to_transport_.end()) { 934 LOG(LS_ERROR) << "No transport ID mapping for " << proxy; 935 return false; 936 } 937 std::ostringstream ost; 938 // Component 1 is always used for RTP. 939 ost << "Channel-" << proxy_to_transport_[proxy] << "-1"; 940 *transport = ost.str(); 941 return true; 942 } 943 944 StatsReport* StatsCollector::GetReport(const std::string& type, 945 const std::string& id, 946 TrackDirection direction) { 947 ASSERT(type == StatsReport::kStatsReportTypeSsrc || 948 type == StatsReport::kStatsReportTypeRemoteSsrc); 949 std::string statsid = StatsId(type, id, direction); 950 StatsReport* report = NULL; 951 std::map<std::string, StatsReport>::iterator it = reports_.find(statsid); 952 if (it != reports_.end()) 953 report = &(it->second); 954 955 return report; 956 } 957 958 StatsReport* StatsCollector::GetOrCreateReport(const std::string& type, 959 const std::string& id, 960 TrackDirection direction) { 961 ASSERT(type == StatsReport::kStatsReportTypeSsrc || 962 type == StatsReport::kStatsReportTypeRemoteSsrc); 963 StatsReport* report = GetReport(type, id, direction); 964 if (report == NULL) { 965 std::string statsid = StatsId(type, id, direction); 966 report = &reports_[statsid]; // Create new element. 967 report->id = statsid; 968 report->type = type; 969 } 970 971 return report; 972 } 973 974 void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() { 975 // Loop through the existing local audio tracks. 976 for (LocalAudioTrackVector::const_iterator it = local_audio_tracks_.begin(); 977 it != local_audio_tracks_.end(); ++it) { 978 AudioTrackInterface* track = it->first; 979 uint32 ssrc = it->second; 980 std::string ssrc_id = talk_base::ToString<uint32>(ssrc); 981 StatsReport* report = GetReport(StatsReport::kStatsReportTypeSsrc, 982 ssrc_id, 983 kSending); 984 if (report == NULL) { 985 // This can happen if a local audio track is added to a stream on the 986 // fly and the report has not been set up yet. Do nothing in this case. 987 LOG(LS_ERROR) << "Stats report does not exist for ssrc " << ssrc; 988 continue; 989 } 990 991 // The same ssrc can be used by both local and remote audio tracks. 992 std::string track_id; 993 if (!ExtractValueFromReport(*report, 994 StatsReport::kStatsValueNameTrackId, 995 &track_id) || 996 track_id != track->id()) { 997 continue; 998 } 999 1000 UpdateReportFromAudioTrack(track, report); 1001 } 1002 } 1003 1004 void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track, 1005 StatsReport* report) { 1006 ASSERT(track != NULL); 1007 if (report == NULL) 1008 return; 1009 1010 int signal_level = 0; 1011 if (track->GetSignalLevel(&signal_level)) { 1012 report->ReplaceValue(StatsReport::kStatsValueNameAudioInputLevel, 1013 talk_base::ToString<int>(signal_level)); 1014 } 1015 1016 talk_base::scoped_refptr<AudioProcessorInterface> audio_processor( 1017 track->GetAudioProcessor()); 1018 if (audio_processor.get() == NULL) 1019 return; 1020 1021 AudioProcessorInterface::AudioProcessorStats stats; 1022 audio_processor->GetStats(&stats); 1023 report->ReplaceValue(StatsReport::kStatsValueNameTypingNoiseState, 1024 stats.typing_noise_detected ? "true" : "false"); 1025 report->ReplaceValue(StatsReport::kStatsValueNameEchoReturnLoss, 1026 talk_base::ToString<int>(stats.echo_return_loss)); 1027 report->ReplaceValue( 1028 StatsReport::kStatsValueNameEchoReturnLossEnhancement, 1029 talk_base::ToString<int>(stats.echo_return_loss_enhancement)); 1030 report->ReplaceValue(StatsReport::kStatsValueNameEchoDelayMedian, 1031 talk_base::ToString<int>(stats.echo_delay_median_ms)); 1032 report->ReplaceValue(StatsReport::kStatsValueNameEchoCancellationQualityMin, 1033 talk_base::ToString<float>(stats.aec_quality_min)); 1034 report->ReplaceValue(StatsReport::kStatsValueNameEchoDelayStdDev, 1035 talk_base::ToString<int>(stats.echo_delay_std_ms)); 1036 } 1037 1038 bool StatsCollector::GetTrackIdBySsrc(uint32 ssrc, std::string* track_id, 1039 TrackDirection direction) { 1040 if (direction == kSending) { 1041 if (!session()->GetLocalTrackIdBySsrc(ssrc, track_id)) { 1042 LOG(LS_WARNING) << "The SSRC " << ssrc 1043 << " is not associated with a sending track"; 1044 return false; 1045 } 1046 } else { 1047 ASSERT(direction == kReceiving); 1048 if (!session()->GetRemoteTrackIdBySsrc(ssrc, track_id)) { 1049 LOG(LS_WARNING) << "The SSRC " << ssrc 1050 << " is not associated with a receiving track"; 1051 return false; 1052 } 1053 } 1054 1055 return true; 1056 } 1057 1058 } // namespace webrtc 1059