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/session/media/channel.h" 34 35 namespace webrtc { 36 37 // The items below are in alphabetical order. 38 const char StatsReport::kStatsValueNameActiveConnection[] = 39 "googActiveConnection"; 40 const char StatsReport::kStatsValueNameActualEncBitrate[] = 41 "googActualEncBitrate"; 42 const char StatsReport::kStatsValueNameAudioOutputLevel[] = "audioOutputLevel"; 43 const char StatsReport::kStatsValueNameAudioInputLevel[] = "audioInputLevel"; 44 const char StatsReport::kStatsValueNameAvailableReceiveBandwidth[] = 45 "googAvailableReceiveBandwidth"; 46 const char StatsReport::kStatsValueNameAvailableSendBandwidth[] = 47 "googAvailableSendBandwidth"; 48 const char StatsReport::kStatsValueNameBucketDelay[] = "googBucketDelay"; 49 const char StatsReport::kStatsValueNameBytesReceived[] = "bytesReceived"; 50 const char StatsReport::kStatsValueNameBytesSent[] = "bytesSent"; 51 const char StatsReport::kStatsValueNameChannelId[] = "googChannelId"; 52 const char StatsReport::kStatsValueNameCodecName[] = "googCodecName"; 53 const char StatsReport::kStatsValueNameComponent[] = "googComponent"; 54 const char StatsReport::kStatsValueNameContentName[] = "googContentName"; 55 // Echo metrics from the audio processing module. 56 const char StatsReport::kStatsValueNameEchoCancellationQualityMin[] = 57 "googEchoCancellationQualityMin"; 58 const char StatsReport::kStatsValueNameEchoDelayMedian[] = 59 "googEchoCancellationEchoDelayMedian"; 60 const char StatsReport::kStatsValueNameEchoDelayStdDev[] = 61 "googEchoCancellationEchoDelayStdDev"; 62 const char StatsReport::kStatsValueNameEchoReturnLoss[] = 63 "googEchoCancellationReturnLoss"; 64 const char StatsReport::kStatsValueNameEchoReturnLossEnhancement[] = 65 "googEchoCancellationReturnLossEnhancement"; 66 67 const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived"; 68 const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent"; 69 const char StatsReport::kStatsValueNameFrameHeightReceived[] = 70 "googFrameHeightReceived"; 71 const char StatsReport::kStatsValueNameFrameHeightSent[] = 72 "googFrameHeightSent"; 73 const char StatsReport::kStatsValueNameFrameRateReceived[] = 74 "googFrameRateReceived"; 75 const char StatsReport::kStatsValueNameFrameRateDecoded[] = 76 "googFrameRateDecoded"; 77 const char StatsReport::kStatsValueNameFrameRateOutput[] = 78 "googFrameRateOutput"; 79 const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput"; 80 const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent"; 81 const char StatsReport::kStatsValueNameFrameWidthReceived[] = 82 "googFrameWidthReceived"; 83 const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent"; 84 const char StatsReport::kStatsValueNameInitiator[] = "googInitiator"; 85 const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived"; 86 const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress"; 87 const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived"; 88 const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent"; 89 const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived"; 90 const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent"; 91 const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost"; 92 const char StatsReport::kStatsValueNameReadable[] = "googReadable"; 93 const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress"; 94 const char StatsReport::kStatsValueNameRetransmitBitrate[] = 95 "googRetransmitBitrate"; 96 const char StatsReport::kStatsValueNameRtt[] = "googRtt"; 97 const char StatsReport::kStatsValueNameTargetEncBitrate[] = 98 "googTargetEncBitrate"; 99 const char StatsReport::kStatsValueNameTransmitBitrate[] = 100 "googTransmitBitrate"; 101 const char StatsReport::kStatsValueNameTransportId[] = "transportId"; 102 const char StatsReport::kStatsValueNameTransportType[] = "googTransportType"; 103 const char StatsReport::kStatsValueNameTrackId[] = "googTrackId"; 104 const char StatsReport::kStatsValueNameSsrc[] = "ssrc"; 105 const char StatsReport::kStatsValueNameWritable[] = "googWritable"; 106 107 const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession"; 108 const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe"; 109 const char StatsReport::kStatsReportTypeSsrc[] = "ssrc"; 110 const char StatsReport::kStatsReportTypeTrack[] = "googTrack"; 111 const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate"; 112 const char StatsReport::kStatsReportTypeTransport[] = "googTransport"; 113 const char StatsReport::kStatsReportTypeComponent[] = "googComponent"; 114 const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair"; 115 116 const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo"; 117 118 // Implementations of functions in statstypes.h 119 void StatsReport::AddValue(const std::string& name, const std::string& value) { 120 Value temp; 121 temp.name = name; 122 temp.value = value; 123 values.push_back(temp); 124 } 125 126 void StatsReport::AddValue(const std::string& name, int64 value) { 127 AddValue(name, talk_base::ToString<int64>(value)); 128 } 129 130 void StatsReport::AddBoolean(const std::string& name, bool value) { 131 AddValue(name, value ? "true" : "false"); 132 } 133 134 namespace { 135 typedef std::map<std::string, StatsReport> StatsMap; 136 137 std::string StatsId(const std::string& type, const std::string& id) { 138 return type + "_" + id; 139 } 140 141 bool ExtractValueFromReport( 142 const StatsReport& report, 143 const std::string& name, 144 std::string* value) { 145 StatsReport::Values::const_iterator it = report.values.begin(); 146 for (; it != report.values.end(); ++it) { 147 if (it->name == name) { 148 *value = it->value; 149 return true; 150 } 151 } 152 return false; 153 } 154 155 template <class TrackVector> 156 void CreateTrackReports(const TrackVector& tracks, StatsMap* reports) { 157 for (size_t j = 0; j < tracks.size(); ++j) { 158 webrtc::MediaStreamTrackInterface* track = tracks[j]; 159 // Adds an empty track report. 160 StatsReport report; 161 report.type = StatsReport::kStatsReportTypeTrack; 162 report.id = StatsId(StatsReport::kStatsReportTypeTrack, track->id()); 163 report.AddValue(StatsReport::kStatsValueNameTrackId, 164 track->id()); 165 (*reports)[report.id] = report; 166 } 167 } 168 169 void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) { 170 report->AddValue(StatsReport::kStatsValueNameAudioOutputLevel, 171 info.audio_level); 172 report->AddValue(StatsReport::kStatsValueNameBytesReceived, 173 info.bytes_rcvd); 174 report->AddValue(StatsReport::kStatsValueNameJitterReceived, 175 info.jitter_ms); 176 report->AddValue(StatsReport::kStatsValueNamePacketsReceived, 177 info.packets_rcvd); 178 report->AddValue(StatsReport::kStatsValueNamePacketsLost, 179 info.packets_lost); 180 } 181 182 void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) { 183 report->AddValue(StatsReport::kStatsValueNameAudioInputLevel, 184 info.audio_level); 185 report->AddValue(StatsReport::kStatsValueNameBytesSent, 186 info.bytes_sent); 187 report->AddValue(StatsReport::kStatsValueNamePacketsSent, 188 info.packets_sent); 189 report->AddValue(StatsReport::kStatsValueNameJitterReceived, 190 info.jitter_ms); 191 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms); 192 report->AddValue(StatsReport::kStatsValueNameEchoCancellationQualityMin, 193 talk_base::ToString<float>(info.aec_quality_min)); 194 report->AddValue(StatsReport::kStatsValueNameEchoDelayMedian, 195 info.echo_delay_median_ms); 196 report->AddValue(StatsReport::kStatsValueNameEchoDelayStdDev, 197 info.echo_delay_std_ms); 198 report->AddValue(StatsReport::kStatsValueNameEchoReturnLoss, 199 info.echo_return_loss); 200 report->AddValue(StatsReport::kStatsValueNameEchoReturnLossEnhancement, 201 info.echo_return_loss_enhancement); 202 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name); 203 } 204 205 void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) { 206 report->AddValue(StatsReport::kStatsValueNameBytesReceived, 207 info.bytes_rcvd); 208 report->AddValue(StatsReport::kStatsValueNamePacketsReceived, 209 info.packets_rcvd); 210 report->AddValue(StatsReport::kStatsValueNamePacketsLost, 211 info.packets_lost); 212 213 report->AddValue(StatsReport::kStatsValueNameFirsSent, 214 info.firs_sent); 215 report->AddValue(StatsReport::kStatsValueNameNacksSent, 216 info.nacks_sent); 217 report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived, 218 info.frame_width); 219 report->AddValue(StatsReport::kStatsValueNameFrameHeightReceived, 220 info.frame_height); 221 report->AddValue(StatsReport::kStatsValueNameFrameRateReceived, 222 info.framerate_rcvd); 223 report->AddValue(StatsReport::kStatsValueNameFrameRateDecoded, 224 info.framerate_decoded); 225 report->AddValue(StatsReport::kStatsValueNameFrameRateOutput, 226 info.framerate_output); 227 } 228 229 void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { 230 report->AddValue(StatsReport::kStatsValueNameBytesSent, 231 info.bytes_sent); 232 report->AddValue(StatsReport::kStatsValueNamePacketsSent, 233 info.packets_sent); 234 235 report->AddValue(StatsReport::kStatsValueNameFirsReceived, 236 info.firs_rcvd); 237 report->AddValue(StatsReport::kStatsValueNameNacksReceived, 238 info.nacks_rcvd); 239 report->AddValue(StatsReport::kStatsValueNameFrameWidthSent, 240 info.frame_width); 241 report->AddValue(StatsReport::kStatsValueNameFrameHeightSent, 242 info.frame_height); 243 report->AddValue(StatsReport::kStatsValueNameFrameRateInput, 244 info.framerate_input); 245 report->AddValue(StatsReport::kStatsValueNameFrameRateSent, 246 info.framerate_sent); 247 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms); 248 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name); 249 } 250 251 void ExtractStats(const cricket::BandwidthEstimationInfo& info, 252 double stats_gathering_started, 253 StatsReport* report) { 254 report->id = StatsReport::kStatsReportVideoBweId; 255 report->type = StatsReport::kStatsReportTypeBwe; 256 257 // Clear out stats from previous GatherStats calls if any. 258 if (report->timestamp != stats_gathering_started) { 259 report->values.clear(); 260 report->timestamp = stats_gathering_started; 261 } 262 263 report->AddValue(StatsReport::kStatsValueNameAvailableSendBandwidth, 264 info.available_send_bandwidth); 265 report->AddValue(StatsReport::kStatsValueNameAvailableReceiveBandwidth, 266 info.available_recv_bandwidth); 267 report->AddValue(StatsReport::kStatsValueNameTargetEncBitrate, 268 info.target_enc_bitrate); 269 report->AddValue(StatsReport::kStatsValueNameActualEncBitrate, 270 info.actual_enc_bitrate); 271 report->AddValue(StatsReport::kStatsValueNameRetransmitBitrate, 272 info.retransmit_bitrate); 273 report->AddValue(StatsReport::kStatsValueNameTransmitBitrate, 274 info.transmit_bitrate); 275 report->AddValue(StatsReport::kStatsValueNameBucketDelay, 276 info.bucket_delay); 277 } 278 279 uint32 ExtractSsrc(const cricket::VoiceReceiverInfo& info) { 280 return info.ssrc; 281 } 282 283 uint32 ExtractSsrc(const cricket::VoiceSenderInfo& info) { 284 return info.ssrc; 285 } 286 287 uint32 ExtractSsrc(const cricket::VideoReceiverInfo& info) { 288 return info.ssrcs[0]; 289 } 290 291 uint32 ExtractSsrc(const cricket::VideoSenderInfo& info) { 292 return info.ssrcs[0]; 293 } 294 295 // Template to extract stats from a data vector. 296 // ExtractSsrc and ExtractStats must be defined and overloaded for each type. 297 template<typename T> 298 void ExtractStatsFromList(const std::vector<T>& data, 299 const std::string& transport_id, 300 StatsCollector* collector) { 301 typename std::vector<T>::const_iterator it = data.begin(); 302 for (; it != data.end(); ++it) { 303 std::string id; 304 uint32 ssrc = ExtractSsrc(*it); 305 StatsReport* report = collector->PrepareReport(ssrc, transport_id); 306 if (!report) { 307 continue; 308 } 309 ExtractStats(*it, report); 310 } 311 }; 312 313 } // namespace 314 315 StatsCollector::StatsCollector() 316 : session_(NULL), stats_gathering_started_(0) { 317 } 318 319 // Adds a MediaStream with tracks that can be used as a |selector| in a call 320 // to GetStats. 321 void StatsCollector::AddStream(MediaStreamInterface* stream) { 322 ASSERT(stream != NULL); 323 324 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(), 325 &reports_); 326 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(), 327 &reports_); 328 } 329 330 bool StatsCollector::GetStats(MediaStreamTrackInterface* track, 331 StatsReports* reports) { 332 ASSERT(reports != NULL); 333 reports->clear(); 334 335 StatsMap::iterator it; 336 if (!track) { 337 for (it = reports_.begin(); it != reports_.end(); ++it) { 338 reports->push_back(it->second); 339 } 340 return true; 341 } 342 343 it = reports_.find(StatsId(StatsReport::kStatsReportTypeSession, 344 session_->id())); 345 if (it != reports_.end()) { 346 reports->push_back(it->second); 347 } 348 349 it = reports_.find(StatsId(StatsReport::kStatsReportTypeTrack, track->id())); 350 351 if (it == reports_.end()) { 352 LOG(LS_WARNING) << "No StatsReport is available for "<< track->id(); 353 return false; 354 } 355 356 reports->push_back(it->second); 357 358 std::string track_id; 359 for (it = reports_.begin(); it != reports_.end(); ++it) { 360 if (it->second.type != StatsReport::kStatsReportTypeSsrc) { 361 continue; 362 } 363 if (ExtractValueFromReport(it->second, 364 StatsReport::kStatsValueNameTrackId, 365 &track_id)) { 366 if (track_id == track->id()) { 367 reports->push_back(it->second); 368 } 369 } 370 } 371 372 return true; 373 } 374 375 void StatsCollector::UpdateStats() { 376 double time_now = GetTimeNow(); 377 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of 378 // ms apart will be ignored. 379 const double kMinGatherStatsPeriod = 50; 380 if (stats_gathering_started_ + kMinGatherStatsPeriod > time_now) { 381 return; 382 } 383 stats_gathering_started_ = time_now; 384 385 if (session_) { 386 ExtractSessionInfo(); 387 ExtractVoiceInfo(); 388 ExtractVideoInfo(); 389 } 390 } 391 392 StatsReport* StatsCollector::PrepareReport(uint32 ssrc, 393 const std::string& transport_id) { 394 std::string ssrc_id = talk_base::ToString<uint32>(ssrc); 395 StatsMap::iterator it = reports_.find(StatsId( 396 StatsReport::kStatsReportTypeSsrc, ssrc_id)); 397 398 std::string track_id; 399 if (it == reports_.end()) { 400 if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) { 401 LOG(LS_ERROR) << "The SSRC " << ssrc 402 << " is not associated with a track"; 403 return NULL; 404 } 405 } else { 406 // Keeps the old track id since we want to report the stats for inactive 407 // tracks. 408 ExtractValueFromReport(it->second, 409 StatsReport::kStatsValueNameTrackId, 410 &track_id); 411 } 412 413 StatsReport* report = &reports_[ 414 StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id)]; 415 report->id = StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id); 416 report->type = StatsReport::kStatsReportTypeSsrc; 417 418 // Clear out stats from previous GatherStats calls if any. 419 if (report->timestamp != stats_gathering_started_) { 420 report->values.clear(); 421 report->timestamp = stats_gathering_started_; 422 } 423 424 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id); 425 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id); 426 // Add the mapping of SSRC to transport. 427 report->AddValue(StatsReport::kStatsValueNameTransportId, 428 transport_id); 429 return report; 430 } 431 432 void StatsCollector::ExtractSessionInfo() { 433 // Extract information from the base session. 434 StatsReport report; 435 report.id = StatsId(StatsReport::kStatsReportTypeSession, session_->id()); 436 report.type = StatsReport::kStatsReportTypeSession; 437 report.timestamp = stats_gathering_started_; 438 report.values.clear(); 439 report.AddBoolean(StatsReport::kStatsValueNameInitiator, 440 session_->initiator()); 441 442 reports_[report.id] = report; 443 444 cricket::SessionStats stats; 445 if (session_->GetStats(&stats)) { 446 // Store the proxy map away for use in SSRC reporting. 447 proxy_to_transport_ = stats.proxy_to_transport; 448 449 for (cricket::TransportStatsMap::iterator transport_iter 450 = stats.transport_stats.begin(); 451 transport_iter != stats.transport_stats.end(); ++transport_iter) { 452 for (cricket::TransportChannelStatsList::iterator channel_iter 453 = transport_iter->second.channel_stats.begin(); 454 channel_iter != transport_iter->second.channel_stats.end(); 455 ++channel_iter) { 456 StatsReport channel_report; 457 std::ostringstream ostc; 458 ostc << "Channel-" << transport_iter->second.content_name 459 << "-" << channel_iter->component; 460 channel_report.id = ostc.str(); 461 channel_report.type = StatsReport::kStatsReportTypeComponent; 462 channel_report.timestamp = stats_gathering_started_; 463 channel_report.AddValue(StatsReport::kStatsValueNameComponent, 464 channel_iter->component); 465 reports_[channel_report.id] = channel_report; 466 for (size_t i = 0; 467 i < channel_iter->connection_infos.size(); 468 ++i) { 469 StatsReport report; 470 const cricket::ConnectionInfo& info 471 = channel_iter->connection_infos[i]; 472 std::ostringstream ost; 473 ost << "Conn-" << transport_iter->first << "-" 474 << channel_iter->component << "-" << i; 475 report.id = ost.str(); 476 report.type = StatsReport::kStatsReportTypeCandidatePair; 477 report.timestamp = stats_gathering_started_; 478 // Link from connection to its containing channel. 479 report.AddValue(StatsReport::kStatsValueNameChannelId, 480 channel_report.id); 481 report.AddValue(StatsReport::kStatsValueNameBytesSent, 482 info.sent_total_bytes); 483 report.AddValue(StatsReport::kStatsValueNameBytesReceived, 484 info.recv_total_bytes); 485 report.AddBoolean(StatsReport::kStatsValueNameWritable, 486 info.writable); 487 report.AddBoolean(StatsReport::kStatsValueNameReadable, 488 info.readable); 489 report.AddBoolean(StatsReport::kStatsValueNameActiveConnection, 490 info.best_connection); 491 report.AddValue(StatsReport::kStatsValueNameLocalAddress, 492 info.local_candidate.address().ToString()); 493 report.AddValue(StatsReport::kStatsValueNameRemoteAddress, 494 info.remote_candidate.address().ToString()); 495 reports_[report.id] = report; 496 } 497 } 498 } 499 } 500 } 501 502 void StatsCollector::ExtractVoiceInfo() { 503 if (!session_->voice_channel()) { 504 return; 505 } 506 cricket::VoiceMediaInfo voice_info; 507 if (!session_->voice_channel()->GetStats(&voice_info)) { 508 LOG(LS_ERROR) << "Failed to get voice channel stats."; 509 return; 510 } 511 std::string transport_id; 512 if (!GetTransportIdFromProxy(session_->voice_channel()->content_name(), 513 &transport_id)) { 514 LOG(LS_ERROR) << "Failed to get transport name for proxy " 515 << session_->voice_channel()->content_name(); 516 return; 517 } 518 ExtractStatsFromList(voice_info.receivers, transport_id, this); 519 ExtractStatsFromList(voice_info.senders, transport_id, this); 520 } 521 522 void StatsCollector::ExtractVideoInfo() { 523 if (!session_->video_channel()) { 524 return; 525 } 526 cricket::VideoMediaInfo video_info; 527 if (!session_->video_channel()->GetStats(&video_info)) { 528 LOG(LS_ERROR) << "Failed to get video channel stats."; 529 return; 530 } 531 std::string transport_id; 532 if (!GetTransportIdFromProxy(session_->video_channel()->content_name(), 533 &transport_id)) { 534 LOG(LS_ERROR) << "Failed to get transport name for proxy " 535 << session_->video_channel()->content_name(); 536 return; 537 } 538 ExtractStatsFromList(video_info.receivers, transport_id, this); 539 ExtractStatsFromList(video_info.senders, transport_id, this); 540 if (video_info.bw_estimations.size() != 1) { 541 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size(); 542 } else { 543 StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId]; 544 ExtractStats( 545 video_info.bw_estimations[0], stats_gathering_started_, report); 546 } 547 } 548 549 double StatsCollector::GetTimeNow() { 550 return timing_.WallTimeNow() * talk_base::kNumMillisecsPerSec; 551 } 552 553 bool StatsCollector::GetTransportIdFromProxy(const std::string& proxy, 554 std::string* transport) { 555 // TODO(hta): Remove handling of empty proxy name once tests do not use it. 556 if (proxy.empty()) { 557 transport->clear(); 558 return true; 559 } 560 if (proxy_to_transport_.find(proxy) == proxy_to_transport_.end()) { 561 LOG(LS_ERROR) << "No transport ID mapping for " << proxy; 562 return false; 563 } 564 std::ostringstream ost; 565 // Component 1 is always used for RTP. 566 ost << "Channel-" << proxy_to_transport_[proxy] << "-1"; 567 *transport = ost.str(); 568 return true; 569 } 570 571 } // namespace webrtc 572