Home | History | Annotate | Download | only in webrtc
      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