| OLD | NEW | 
|---|
|  | (Empty) | 
| 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/app/webrtc/peerconnection.h" |  | 
| 34 #include "talk/session/media/channel.h" |  | 
| 35 #include "webrtc/base/base64.h" |  | 
| 36 #include "webrtc/base/checks.h" |  | 
| 37 #include "webrtc/base/scoped_ptr.h" |  | 
| 38 #include "webrtc/base/timing.h" |  | 
| 39 |  | 
| 40 using rtc::scoped_ptr; |  | 
| 41 |  | 
| 42 namespace webrtc { |  | 
| 43 namespace { |  | 
| 44 |  | 
| 45 // The following is the enum RTCStatsIceCandidateType from |  | 
| 46 // http://w3c.github.io/webrtc-stats/#rtcstatsicecandidatetype-enum such that |  | 
| 47 // our stats report for ice candidate type could conform to that. |  | 
| 48 const char STATSREPORT_LOCAL_PORT_TYPE[] = "host"; |  | 
| 49 const char STATSREPORT_STUN_PORT_TYPE[] = "serverreflexive"; |  | 
| 50 const char STATSREPORT_PRFLX_PORT_TYPE[] = "peerreflexive"; |  | 
| 51 const char STATSREPORT_RELAY_PORT_TYPE[] = "relayed"; |  | 
| 52 |  | 
| 53 // Strings used by the stats collector to report adapter types. This fits the |  | 
| 54 // general stype of http://w3c.github.io/webrtc-stats than what |  | 
| 55 // AdapterTypeToString does. |  | 
| 56 const char* STATSREPORT_ADAPTER_TYPE_ETHERNET = "lan"; |  | 
| 57 const char* STATSREPORT_ADAPTER_TYPE_WIFI = "wlan"; |  | 
| 58 const char* STATSREPORT_ADAPTER_TYPE_WWAN = "wwan"; |  | 
| 59 const char* STATSREPORT_ADAPTER_TYPE_VPN = "vpn"; |  | 
| 60 const char* STATSREPORT_ADAPTER_TYPE_LOOPBACK = "loopback"; |  | 
| 61 |  | 
| 62 template<typename ValueType> |  | 
| 63 struct TypeForAdd { |  | 
| 64   const StatsReport::StatsValueName name; |  | 
| 65   const ValueType& value; |  | 
| 66 }; |  | 
| 67 |  | 
| 68 typedef TypeForAdd<bool> BoolForAdd; |  | 
| 69 typedef TypeForAdd<float> FloatForAdd; |  | 
| 70 typedef TypeForAdd<int64_t> Int64ForAdd; |  | 
| 71 typedef TypeForAdd<int> IntForAdd; |  | 
| 72 |  | 
| 73 StatsReport::Id GetTransportIdFromProxy(const ProxyTransportMap& map, |  | 
| 74                                         const std::string& proxy) { |  | 
| 75   RTC_DCHECK(!proxy.empty()); |  | 
| 76   auto found = map.find(proxy); |  | 
| 77   if (found == map.end()) { |  | 
| 78     return StatsReport::Id(); |  | 
| 79   } |  | 
| 80 |  | 
| 81   return StatsReport::NewComponentId( |  | 
| 82       found->second, cricket::ICE_CANDIDATE_COMPONENT_RTP); |  | 
| 83 } |  | 
| 84 |  | 
| 85 StatsReport* AddTrackReport(StatsCollection* reports, |  | 
| 86                             const std::string& track_id) { |  | 
| 87   // Adds an empty track report. |  | 
| 88   StatsReport::Id id( |  | 
| 89       StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack, track_id)); |  | 
| 90   StatsReport* report = reports->ReplaceOrAddNew(id); |  | 
| 91   report->AddString(StatsReport::kStatsValueNameTrackId, track_id); |  | 
| 92   return report; |  | 
| 93 } |  | 
| 94 |  | 
| 95 template <class TrackVector> |  | 
| 96 void CreateTrackReports(const TrackVector& tracks, StatsCollection* reports, |  | 
| 97                         TrackIdMap& track_ids) { |  | 
| 98   for (const auto& track : tracks) { |  | 
| 99     const std::string& track_id = track->id(); |  | 
| 100     StatsReport* report = AddTrackReport(reports, track_id); |  | 
| 101     RTC_DCHECK(report != nullptr); |  | 
| 102     track_ids[track_id] = report; |  | 
| 103   } |  | 
| 104 } |  | 
| 105 |  | 
| 106 void ExtractCommonSendProperties(const cricket::MediaSenderInfo& info, |  | 
| 107                                  StatsReport* report) { |  | 
| 108   report->AddString(StatsReport::kStatsValueNameCodecName, info.codec_name); |  | 
| 109   report->AddInt64(StatsReport::kStatsValueNameBytesSent, info.bytes_sent); |  | 
| 110   report->AddInt64(StatsReport::kStatsValueNameRtt, info.rtt_ms); |  | 
| 111 } |  | 
| 112 |  | 
| 113 void ExtractCommonReceiveProperties(const cricket::MediaReceiverInfo& info, |  | 
| 114                                     StatsReport* report) { |  | 
| 115   report->AddString(StatsReport::kStatsValueNameCodecName, info.codec_name); |  | 
| 116 } |  | 
| 117 |  | 
| 118 void SetAudioProcessingStats(StatsReport* report, |  | 
| 119                              bool typing_noise_detected, |  | 
| 120                              int echo_return_loss, |  | 
| 121                              int echo_return_loss_enhancement, |  | 
| 122                              int echo_delay_median_ms, |  | 
| 123                              float aec_quality_min, |  | 
| 124                              int echo_delay_std_ms) { |  | 
| 125   report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState, |  | 
| 126                      typing_noise_detected); |  | 
| 127   report->AddFloat(StatsReport::kStatsValueNameEchoCancellationQualityMin, |  | 
| 128                    aec_quality_min); |  | 
| 129   const IntForAdd ints[] = { |  | 
| 130     { StatsReport::kStatsValueNameEchoReturnLoss, echo_return_loss }, |  | 
| 131     { StatsReport::kStatsValueNameEchoReturnLossEnhancement, |  | 
| 132       echo_return_loss_enhancement }, |  | 
| 133     { StatsReport::kStatsValueNameEchoDelayMedian, echo_delay_median_ms }, |  | 
| 134     { StatsReport::kStatsValueNameEchoDelayStdDev, echo_delay_std_ms }, |  | 
| 135   }; |  | 
| 136   for (const auto& i : ints) |  | 
| 137     report->AddInt(i.name, i.value); |  | 
| 138 } |  | 
| 139 |  | 
| 140 void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) { |  | 
| 141   ExtractCommonReceiveProperties(info, report); |  | 
| 142   const FloatForAdd floats[] = { |  | 
| 143     { StatsReport::kStatsValueNameExpandRate, info.expand_rate }, |  | 
| 144     { StatsReport::kStatsValueNameSecondaryDecodedRate, |  | 
| 145       info.secondary_decoded_rate }, |  | 
| 146     { StatsReport::kStatsValueNameSpeechExpandRate, info.speech_expand_rate }, |  | 
| 147     { StatsReport::kStatsValueNameAccelerateRate, info.accelerate_rate }, |  | 
| 148     { StatsReport::kStatsValueNamePreemptiveExpandRate, |  | 
| 149       info.preemptive_expand_rate }, |  | 
| 150   }; |  | 
| 151 |  | 
| 152   const IntForAdd ints[] = { |  | 
| 153     { StatsReport::kStatsValueNameAudioOutputLevel, info.audio_level }, |  | 
| 154     { StatsReport::kStatsValueNameCurrentDelayMs, info.delay_estimate_ms }, |  | 
| 155     { StatsReport::kStatsValueNameDecodingCNG, info.decoding_cng }, |  | 
| 156     { StatsReport::kStatsValueNameDecodingCTN, info.decoding_calls_to_neteq }, |  | 
| 157     { StatsReport::kStatsValueNameDecodingCTSG, |  | 
| 158       info.decoding_calls_to_silence_generator }, |  | 
| 159     { StatsReport::kStatsValueNameDecodingNormal, info.decoding_normal }, |  | 
| 160     { StatsReport::kStatsValueNameDecodingPLC, info.decoding_plc }, |  | 
| 161     { StatsReport::kStatsValueNameDecodingPLCCNG, info.decoding_plc_cng }, |  | 
| 162     { StatsReport::kStatsValueNameJitterBufferMs, info.jitter_buffer_ms }, |  | 
| 163     { StatsReport::kStatsValueNameJitterReceived, info.jitter_ms }, |  | 
| 164     { StatsReport::kStatsValueNamePacketsLost, info.packets_lost }, |  | 
| 165     { StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd }, |  | 
| 166     { StatsReport::kStatsValueNamePreferredJitterBufferMs, |  | 
| 167       info.jitter_buffer_preferred_ms }, |  | 
| 168   }; |  | 
| 169 |  | 
| 170   for (const auto& f : floats) |  | 
| 171     report->AddFloat(f.name, f.value); |  | 
| 172 |  | 
| 173   for (const auto& i : ints) |  | 
| 174     report->AddInt(i.name, i.value); |  | 
| 175 |  | 
| 176   report->AddInt64(StatsReport::kStatsValueNameBytesReceived, |  | 
| 177                    info.bytes_rcvd); |  | 
| 178   report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs, |  | 
| 179                    info.capture_start_ntp_time_ms); |  | 
| 180 } |  | 
| 181 |  | 
| 182 void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) { |  | 
| 183   ExtractCommonSendProperties(info, report); |  | 
| 184 |  | 
| 185   SetAudioProcessingStats( |  | 
| 186       report, info.typing_noise_detected, info.echo_return_loss, |  | 
| 187       info.echo_return_loss_enhancement, info.echo_delay_median_ms, |  | 
| 188       info.aec_quality_min, info.echo_delay_std_ms); |  | 
| 189 |  | 
| 190   RTC_DCHECK_GE(info.audio_level, 0); |  | 
| 191   const IntForAdd ints[] = { |  | 
| 192     { StatsReport::kStatsValueNameAudioInputLevel, info.audio_level}, |  | 
| 193     { StatsReport::kStatsValueNameJitterReceived, info.jitter_ms }, |  | 
| 194     { StatsReport::kStatsValueNamePacketsLost, info.packets_lost }, |  | 
| 195     { StatsReport::kStatsValueNamePacketsSent, info.packets_sent }, |  | 
| 196   }; |  | 
| 197 |  | 
| 198   for (const auto& i : ints) |  | 
| 199     report->AddInt(i.name, i.value); |  | 
| 200 } |  | 
| 201 |  | 
| 202 void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) { |  | 
| 203   ExtractCommonReceiveProperties(info, report); |  | 
| 204   report->AddString(StatsReport::kStatsValueNameCodecImplementationName, |  | 
| 205                     info.decoder_implementation_name); |  | 
| 206   report->AddInt64(StatsReport::kStatsValueNameBytesReceived, |  | 
| 207                    info.bytes_rcvd); |  | 
| 208   report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs, |  | 
| 209                    info.capture_start_ntp_time_ms); |  | 
| 210   const IntForAdd ints[] = { |  | 
| 211     { StatsReport::kStatsValueNameCurrentDelayMs, info.current_delay_ms }, |  | 
| 212     { StatsReport::kStatsValueNameDecodeMs, info.decode_ms }, |  | 
| 213     { StatsReport::kStatsValueNameFirsSent, info.firs_sent }, |  | 
| 214     { StatsReport::kStatsValueNameFrameHeightReceived, info.frame_height }, |  | 
| 215     { StatsReport::kStatsValueNameFrameRateDecoded, info.framerate_decoded }, |  | 
| 216     { StatsReport::kStatsValueNameFrameRateOutput, info.framerate_output }, |  | 
| 217     { StatsReport::kStatsValueNameFrameRateReceived, info.framerate_rcvd }, |  | 
| 218     { StatsReport::kStatsValueNameFrameWidthReceived, info.frame_width }, |  | 
| 219     { StatsReport::kStatsValueNameJitterBufferMs, info.jitter_buffer_ms }, |  | 
| 220     { StatsReport::kStatsValueNameMaxDecodeMs, info.max_decode_ms }, |  | 
| 221     { StatsReport::kStatsValueNameMinPlayoutDelayMs, |  | 
| 222       info.min_playout_delay_ms }, |  | 
| 223     { StatsReport::kStatsValueNameNacksSent, info.nacks_sent }, |  | 
| 224     { StatsReport::kStatsValueNamePacketsLost, info.packets_lost }, |  | 
| 225     { StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd }, |  | 
| 226     { StatsReport::kStatsValueNamePlisSent, info.plis_sent }, |  | 
| 227     { StatsReport::kStatsValueNameRenderDelayMs, info.render_delay_ms }, |  | 
| 228     { StatsReport::kStatsValueNameTargetDelayMs, info.target_delay_ms }, |  | 
| 229   }; |  | 
| 230 |  | 
| 231   for (const auto& i : ints) |  | 
| 232     report->AddInt(i.name, i.value); |  | 
| 233 } |  | 
| 234 |  | 
| 235 void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) { |  | 
| 236   ExtractCommonSendProperties(info, report); |  | 
| 237 |  | 
| 238   report->AddString(StatsReport::kStatsValueNameCodecImplementationName, |  | 
| 239                     info.encoder_implementation_name); |  | 
| 240   report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution, |  | 
| 241                      (info.adapt_reason & 0x2) > 0); |  | 
| 242   report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution, |  | 
| 243                      (info.adapt_reason & 0x1) > 0); |  | 
| 244   report->AddBoolean(StatsReport::kStatsValueNameViewLimitedResolution, |  | 
| 245                      (info.adapt_reason & 0x4) > 0); |  | 
| 246 |  | 
| 247   const IntForAdd ints[] = { |  | 
| 248     { StatsReport::kStatsValueNameAdaptationChanges, info.adapt_changes }, |  | 
| 249     { StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms }, |  | 
| 250     { StatsReport::kStatsValueNameEncodeUsagePercent, |  | 
| 251       info.encode_usage_percent }, |  | 
| 252     { StatsReport::kStatsValueNameFirsReceived, info.firs_rcvd }, |  | 
| 253     { StatsReport::kStatsValueNameFrameHeightInput, info.input_frame_height }, |  | 
| 254     { StatsReport::kStatsValueNameFrameHeightSent, info.send_frame_height }, |  | 
| 255     { StatsReport::kStatsValueNameFrameRateInput, info.framerate_input }, |  | 
| 256     { StatsReport::kStatsValueNameFrameRateSent, info.framerate_sent }, |  | 
| 257     { StatsReport::kStatsValueNameFrameWidthInput, info.input_frame_width }, |  | 
| 258     { StatsReport::kStatsValueNameFrameWidthSent, info.send_frame_width }, |  | 
| 259     { StatsReport::kStatsValueNameNacksReceived, info.nacks_rcvd }, |  | 
| 260     { StatsReport::kStatsValueNamePacketsLost, info.packets_lost }, |  | 
| 261     { StatsReport::kStatsValueNamePacketsSent, info.packets_sent }, |  | 
| 262     { StatsReport::kStatsValueNamePlisReceived, info.plis_rcvd }, |  | 
| 263   }; |  | 
| 264 |  | 
| 265   for (const auto& i : ints) |  | 
| 266     report->AddInt(i.name, i.value); |  | 
| 267 } |  | 
| 268 |  | 
| 269 void ExtractStats(const cricket::BandwidthEstimationInfo& info, |  | 
| 270                   double stats_gathering_started, |  | 
| 271                   PeerConnectionInterface::StatsOutputLevel level, |  | 
| 272                   StatsReport* report) { |  | 
| 273   RTC_DCHECK(report->type() == StatsReport::kStatsReportTypeBwe); |  | 
| 274 |  | 
| 275   report->set_timestamp(stats_gathering_started); |  | 
| 276   const IntForAdd ints[] = { |  | 
| 277     { StatsReport::kStatsValueNameAvailableSendBandwidth, |  | 
| 278       info.available_send_bandwidth }, |  | 
| 279     { StatsReport::kStatsValueNameAvailableReceiveBandwidth, |  | 
| 280       info.available_recv_bandwidth }, |  | 
| 281     { StatsReport::kStatsValueNameTargetEncBitrate, info.target_enc_bitrate }, |  | 
| 282     { StatsReport::kStatsValueNameActualEncBitrate, info.actual_enc_bitrate }, |  | 
| 283     { StatsReport::kStatsValueNameRetransmitBitrate, info.retransmit_bitrate }, |  | 
| 284     { StatsReport::kStatsValueNameTransmitBitrate, info.transmit_bitrate }, |  | 
| 285   }; |  | 
| 286   for (const auto& i : ints) |  | 
| 287     report->AddInt(i.name, i.value); |  | 
| 288   report->AddInt64(StatsReport::kStatsValueNameBucketDelay, info.bucket_delay); |  | 
| 289 } |  | 
| 290 |  | 
| 291 void ExtractRemoteStats(const cricket::MediaSenderInfo& info, |  | 
| 292                         StatsReport* report) { |  | 
| 293   report->set_timestamp(info.remote_stats[0].timestamp); |  | 
| 294   // TODO(hta): Extract some stats here. |  | 
| 295 } |  | 
| 296 |  | 
| 297 void ExtractRemoteStats(const cricket::MediaReceiverInfo& info, |  | 
| 298                         StatsReport* report) { |  | 
| 299   report->set_timestamp(info.remote_stats[0].timestamp); |  | 
| 300   // TODO(hta): Extract some stats here. |  | 
| 301 } |  | 
| 302 |  | 
| 303 // Template to extract stats from a data vector. |  | 
| 304 // In order to use the template, the functions that are called from it, |  | 
| 305 // ExtractStats and ExtractRemoteStats, must be defined and overloaded |  | 
| 306 // for each type. |  | 
| 307 template<typename T> |  | 
| 308 void ExtractStatsFromList(const std::vector<T>& data, |  | 
| 309                           const StatsReport::Id& transport_id, |  | 
| 310                           StatsCollector* collector, |  | 
| 311                           StatsReport::Direction direction) { |  | 
| 312   for (const auto& d : data) { |  | 
| 313     uint32_t ssrc = d.ssrc(); |  | 
| 314     // Each track can have stats for both local and remote objects. |  | 
| 315     // TODO(hta): Handle the case of multiple SSRCs per object. |  | 
| 316     StatsReport* report = collector->PrepareReport(true, ssrc, transport_id, |  | 
| 317                                                    direction); |  | 
| 318     if (report) |  | 
| 319       ExtractStats(d, report); |  | 
| 320 |  | 
| 321     if (!d.remote_stats.empty()) { |  | 
| 322       report = collector->PrepareReport(false, ssrc, transport_id, direction); |  | 
| 323       if (report) |  | 
| 324         ExtractRemoteStats(d, report); |  | 
| 325     } |  | 
| 326   } |  | 
| 327 } |  | 
| 328 |  | 
| 329 }  // namespace |  | 
| 330 |  | 
| 331 const char* IceCandidateTypeToStatsType(const std::string& candidate_type) { |  | 
| 332   if (candidate_type == cricket::LOCAL_PORT_TYPE) { |  | 
| 333     return STATSREPORT_LOCAL_PORT_TYPE; |  | 
| 334   } |  | 
| 335   if (candidate_type == cricket::STUN_PORT_TYPE) { |  | 
| 336     return STATSREPORT_STUN_PORT_TYPE; |  | 
| 337   } |  | 
| 338   if (candidate_type == cricket::PRFLX_PORT_TYPE) { |  | 
| 339     return STATSREPORT_PRFLX_PORT_TYPE; |  | 
| 340   } |  | 
| 341   if (candidate_type == cricket::RELAY_PORT_TYPE) { |  | 
| 342     return STATSREPORT_RELAY_PORT_TYPE; |  | 
| 343   } |  | 
| 344   RTC_DCHECK(false); |  | 
| 345   return "unknown"; |  | 
| 346 } |  | 
| 347 |  | 
| 348 const char* AdapterTypeToStatsType(rtc::AdapterType type) { |  | 
| 349   switch (type) { |  | 
| 350     case rtc::ADAPTER_TYPE_UNKNOWN: |  | 
| 351       return "unknown"; |  | 
| 352     case rtc::ADAPTER_TYPE_ETHERNET: |  | 
| 353       return STATSREPORT_ADAPTER_TYPE_ETHERNET; |  | 
| 354     case rtc::ADAPTER_TYPE_WIFI: |  | 
| 355       return STATSREPORT_ADAPTER_TYPE_WIFI; |  | 
| 356     case rtc::ADAPTER_TYPE_CELLULAR: |  | 
| 357       return STATSREPORT_ADAPTER_TYPE_WWAN; |  | 
| 358     case rtc::ADAPTER_TYPE_VPN: |  | 
| 359       return STATSREPORT_ADAPTER_TYPE_VPN; |  | 
| 360     case rtc::ADAPTER_TYPE_LOOPBACK: |  | 
| 361       return STATSREPORT_ADAPTER_TYPE_LOOPBACK; |  | 
| 362     default: |  | 
| 363       RTC_DCHECK(false); |  | 
| 364       return ""; |  | 
| 365   } |  | 
| 366 } |  | 
| 367 |  | 
| 368 StatsCollector::StatsCollector(PeerConnection* pc) |  | 
| 369     : pc_(pc), stats_gathering_started_(0) { |  | 
| 370   RTC_DCHECK(pc_); |  | 
| 371 } |  | 
| 372 |  | 
| 373 StatsCollector::~StatsCollector() { |  | 
| 374   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 375 } |  | 
| 376 |  | 
| 377 double StatsCollector::GetTimeNow() { |  | 
| 378   return rtc::Timing::WallTimeNow() * rtc::kNumMillisecsPerSec; |  | 
| 379 } |  | 
| 380 |  | 
| 381 // Adds a MediaStream with tracks that can be used as a |selector| in a call |  | 
| 382 // to GetStats. |  | 
| 383 void StatsCollector::AddStream(MediaStreamInterface* stream) { |  | 
| 384   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 385   RTC_DCHECK(stream != NULL); |  | 
| 386 |  | 
| 387   CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(), |  | 
| 388                                        &reports_, track_ids_); |  | 
| 389   CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(), |  | 
| 390                                        &reports_, track_ids_); |  | 
| 391 } |  | 
| 392 |  | 
| 393 void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track, |  | 
| 394                                         uint32_t ssrc) { |  | 
| 395   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 396   RTC_DCHECK(audio_track != NULL); |  | 
| 397 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) |  | 
| 398   for (const auto& track : local_audio_tracks_) |  | 
| 399     RTC_DCHECK(track.first != audio_track || track.second != ssrc); |  | 
| 400 #endif |  | 
| 401 |  | 
| 402   local_audio_tracks_.push_back(std::make_pair(audio_track, ssrc)); |  | 
| 403 |  | 
| 404   // Create the kStatsReportTypeTrack report for the new track if there is no |  | 
| 405   // report yet. |  | 
| 406   StatsReport::Id id(StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack, |  | 
| 407                                              audio_track->id())); |  | 
| 408   StatsReport* report = reports_.Find(id); |  | 
| 409   if (!report) { |  | 
| 410     report = reports_.InsertNew(id); |  | 
| 411     report->AddString(StatsReport::kStatsValueNameTrackId, audio_track->id()); |  | 
| 412   } |  | 
| 413 } |  | 
| 414 |  | 
| 415 void StatsCollector::RemoveLocalAudioTrack(AudioTrackInterface* audio_track, |  | 
| 416                                            uint32_t ssrc) { |  | 
| 417   RTC_DCHECK(audio_track != NULL); |  | 
| 418   local_audio_tracks_.erase(std::remove_if(local_audio_tracks_.begin(), |  | 
| 419       local_audio_tracks_.end(), |  | 
| 420       [audio_track, ssrc](const LocalAudioTrackVector::value_type& track) { |  | 
| 421         return track.first == audio_track && track.second == ssrc; |  | 
| 422       })); |  | 
| 423 } |  | 
| 424 |  | 
| 425 void StatsCollector::GetStats(MediaStreamTrackInterface* track, |  | 
| 426                               StatsReports* reports) { |  | 
| 427   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 428   RTC_DCHECK(reports != NULL); |  | 
| 429   RTC_DCHECK(reports->empty()); |  | 
| 430 |  | 
| 431   rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; |  | 
| 432 |  | 
| 433   if (!track) { |  | 
| 434     reports->reserve(reports_.size()); |  | 
| 435     for (auto* r : reports_) |  | 
| 436       reports->push_back(r); |  | 
| 437     return; |  | 
| 438   } |  | 
| 439 |  | 
| 440   StatsReport* report = reports_.Find(StatsReport::NewTypedId( |  | 
| 441       StatsReport::kStatsReportTypeSession, pc_->session()->id())); |  | 
| 442   if (report) |  | 
| 443     reports->push_back(report); |  | 
| 444 |  | 
| 445   report = reports_.Find(StatsReport::NewTypedId( |  | 
| 446       StatsReport::kStatsReportTypeTrack, track->id())); |  | 
| 447 |  | 
| 448   if (!report) |  | 
| 449     return; |  | 
| 450 |  | 
| 451   reports->push_back(report); |  | 
| 452 |  | 
| 453   std::string track_id; |  | 
| 454   for (const auto* r : reports_) { |  | 
| 455     if (r->type() != StatsReport::kStatsReportTypeSsrc) |  | 
| 456       continue; |  | 
| 457 |  | 
| 458     const StatsReport::Value* v = |  | 
| 459         r->FindValue(StatsReport::kStatsValueNameTrackId); |  | 
| 460     if (v && v->string_val() == track->id()) |  | 
| 461       reports->push_back(r); |  | 
| 462   } |  | 
| 463 } |  | 
| 464 |  | 
| 465 void |  | 
| 466 StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) { |  | 
| 467   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 468   double time_now = GetTimeNow(); |  | 
| 469   // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of |  | 
| 470   // ms apart will be ignored. |  | 
| 471   const double kMinGatherStatsPeriod = 50; |  | 
| 472   if (stats_gathering_started_ != 0 && |  | 
| 473       stats_gathering_started_ + kMinGatherStatsPeriod > time_now) { |  | 
| 474     return; |  | 
| 475   } |  | 
| 476   stats_gathering_started_ = time_now; |  | 
| 477 |  | 
| 478   if (pc_->session()) { |  | 
| 479     // TODO(tommi): All of these hop over to the worker thread to fetch |  | 
| 480     // information.  We could use an AsyncInvoker to run all of these and post |  | 
| 481     // the information back to the signaling thread where we can create and |  | 
| 482     // update stats reports.  That would also clean up the threading story a bit |  | 
| 483     // since we'd be creating/updating the stats report objects consistently on |  | 
| 484     // the same thread (this class has no locks right now). |  | 
| 485     ExtractSessionInfo(); |  | 
| 486     ExtractVoiceInfo(); |  | 
| 487     ExtractVideoInfo(level); |  | 
| 488     ExtractDataInfo(); |  | 
| 489     UpdateTrackReports(); |  | 
| 490   } |  | 
| 491 } |  | 
| 492 |  | 
| 493 StatsReport* StatsCollector::PrepareReport( |  | 
| 494     bool local, |  | 
| 495     uint32_t ssrc, |  | 
| 496     const StatsReport::Id& transport_id, |  | 
| 497     StatsReport::Direction direction) { |  | 
| 498   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 499   StatsReport::Id id(StatsReport::NewIdWithDirection( |  | 
| 500       local ? StatsReport::kStatsReportTypeSsrc |  | 
| 501             : StatsReport::kStatsReportTypeRemoteSsrc, |  | 
| 502       rtc::ToString<uint32_t>(ssrc), direction)); |  | 
| 503   StatsReport* report = reports_.Find(id); |  | 
| 504 |  | 
| 505   // Use the ID of the track that is currently mapped to the SSRC, if any. |  | 
| 506   std::string track_id; |  | 
| 507   if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) { |  | 
| 508     if (!report) { |  | 
| 509       // The ssrc is not used by any track or existing report, return NULL |  | 
| 510       // in such case to indicate no report is prepared for the ssrc. |  | 
| 511       return NULL; |  | 
| 512     } |  | 
| 513 |  | 
| 514     // The ssrc is not used by any existing track. Keeps the old track id |  | 
| 515     // since we want to report the stats for inactive ssrc. |  | 
| 516     const StatsReport::Value* v = |  | 
| 517         report->FindValue(StatsReport::kStatsValueNameTrackId); |  | 
| 518     if (v) |  | 
| 519       track_id = v->string_val(); |  | 
| 520   } |  | 
| 521 |  | 
| 522   if (!report) |  | 
| 523     report = reports_.InsertNew(id); |  | 
| 524 |  | 
| 525   // FYI - for remote reports, the timestamp will be overwritten later. |  | 
| 526   report->set_timestamp(stats_gathering_started_); |  | 
| 527 |  | 
| 528   report->AddInt64(StatsReport::kStatsValueNameSsrc, ssrc); |  | 
| 529   report->AddString(StatsReport::kStatsValueNameTrackId, track_id); |  | 
| 530   // Add the mapping of SSRC to transport. |  | 
| 531   report->AddId(StatsReport::kStatsValueNameTransportId, transport_id); |  | 
| 532   return report; |  | 
| 533 } |  | 
| 534 |  | 
| 535 StatsReport* StatsCollector::AddOneCertificateReport( |  | 
| 536     const rtc::SSLCertificate* cert, const StatsReport* issuer) { |  | 
| 537   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 538 |  | 
| 539   // TODO(bemasc): Move this computation to a helper class that caches these |  | 
| 540   // values to reduce CPU use in GetStats.  This will require adding a fast |  | 
| 541   // SSLCertificate::Equals() method to detect certificate changes. |  | 
| 542 |  | 
| 543   std::string digest_algorithm; |  | 
| 544   if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm)) |  | 
| 545     return nullptr; |  | 
| 546 |  | 
| 547   rtc::scoped_ptr<rtc::SSLFingerprint> ssl_fingerprint( |  | 
| 548       rtc::SSLFingerprint::Create(digest_algorithm, cert)); |  | 
| 549 |  | 
| 550   // SSLFingerprint::Create can fail if the algorithm returned by |  | 
| 551   // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the |  | 
| 552   // implementation of SSLCertificate::ComputeDigest.  This currently happens |  | 
| 553   // with MD5- and SHA-224-signed certificates when linked to libNSS. |  | 
| 554   if (!ssl_fingerprint) |  | 
| 555     return nullptr; |  | 
| 556 |  | 
| 557   std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint(); |  | 
| 558 |  | 
| 559   rtc::Buffer der_buffer; |  | 
| 560   cert->ToDER(&der_buffer); |  | 
| 561   std::string der_base64; |  | 
| 562   rtc::Base64::EncodeFromArray(der_buffer.data(), der_buffer.size(), |  | 
| 563                                &der_base64); |  | 
| 564 |  | 
| 565   StatsReport::Id id(StatsReport::NewTypedId( |  | 
| 566       StatsReport::kStatsReportTypeCertificate, fingerprint)); |  | 
| 567   StatsReport* report = reports_.ReplaceOrAddNew(id); |  | 
| 568   report->set_timestamp(stats_gathering_started_); |  | 
| 569   report->AddString(StatsReport::kStatsValueNameFingerprint, fingerprint); |  | 
| 570   report->AddString(StatsReport::kStatsValueNameFingerprintAlgorithm, |  | 
| 571                     digest_algorithm); |  | 
| 572   report->AddString(StatsReport::kStatsValueNameDer, der_base64); |  | 
| 573   if (issuer) |  | 
| 574     report->AddId(StatsReport::kStatsValueNameIssuerId, issuer->id()); |  | 
| 575   return report; |  | 
| 576 } |  | 
| 577 |  | 
| 578 StatsReport* StatsCollector::AddCertificateReports( |  | 
| 579     const rtc::SSLCertificate* cert) { |  | 
| 580   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 581   // Produces a chain of StatsReports representing this certificate and the rest |  | 
| 582   // of its chain, and adds those reports to |reports_|.  The return value is |  | 
| 583   // the id of the leaf report.  The provided cert must be non-null, so at least |  | 
| 584   // one report will always be provided and the returned string will never be |  | 
| 585   // empty. |  | 
| 586   RTC_DCHECK(cert != NULL); |  | 
| 587 |  | 
| 588   StatsReport* issuer = nullptr; |  | 
| 589   rtc::scoped_ptr<rtc::SSLCertChain> chain; |  | 
| 590   if (cert->GetChain(chain.accept())) { |  | 
| 591     // This loop runs in reverse, i.e. from root to leaf, so that each |  | 
| 592     // certificate's issuer's report ID is known before the child certificate's |  | 
| 593     // report is generated.  The root certificate does not have an issuer ID |  | 
| 594     // value. |  | 
| 595     for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) { |  | 
| 596       const rtc::SSLCertificate& cert_i = chain->Get(i); |  | 
| 597       issuer = AddOneCertificateReport(&cert_i, issuer); |  | 
| 598     } |  | 
| 599   } |  | 
| 600   // Add the leaf certificate. |  | 
| 601   return AddOneCertificateReport(cert, issuer); |  | 
| 602 } |  | 
| 603 |  | 
| 604 StatsReport* StatsCollector::AddConnectionInfoReport( |  | 
| 605     const std::string& content_name, int component, int connection_id, |  | 
| 606     const StatsReport::Id& channel_report_id, |  | 
| 607     const cricket::ConnectionInfo& info) { |  | 
| 608   StatsReport::Id id(StatsReport::NewCandidatePairId(content_name, component, |  | 
| 609                                                      connection_id)); |  | 
| 610   StatsReport* report = reports_.ReplaceOrAddNew(id); |  | 
| 611   report->set_timestamp(stats_gathering_started_); |  | 
| 612 |  | 
| 613   const BoolForAdd bools[] = { |  | 
| 614     {StatsReport::kStatsValueNameActiveConnection, info.best_connection}, |  | 
| 615     {StatsReport::kStatsValueNameReceiving, info.receiving}, |  | 
| 616     {StatsReport::kStatsValueNameWritable, info.writable}, |  | 
| 617   }; |  | 
| 618   for (const auto& b : bools) |  | 
| 619     report->AddBoolean(b.name, b.value); |  | 
| 620 |  | 
| 621   report->AddId(StatsReport::kStatsValueNameChannelId, channel_report_id); |  | 
| 622   report->AddId(StatsReport::kStatsValueNameLocalCandidateId, |  | 
| 623                 AddCandidateReport(info.local_candidate, true)->id()); |  | 
| 624   report->AddId(StatsReport::kStatsValueNameRemoteCandidateId, |  | 
| 625                 AddCandidateReport(info.remote_candidate, false)->id()); |  | 
| 626 |  | 
| 627   const Int64ForAdd int64s[] = { |  | 
| 628     { StatsReport::kStatsValueNameBytesReceived, info.recv_total_bytes }, |  | 
| 629     { StatsReport::kStatsValueNameBytesSent, info.sent_total_bytes }, |  | 
| 630     { StatsReport::kStatsValueNamePacketsSent, info.sent_total_packets }, |  | 
| 631     { StatsReport::kStatsValueNameRtt, info.rtt }, |  | 
| 632     { StatsReport::kStatsValueNameSendPacketsDiscarded, |  | 
| 633       info.sent_discarded_packets }, |  | 
| 634   }; |  | 
| 635   for (const auto& i : int64s) |  | 
| 636     report->AddInt64(i.name, i.value); |  | 
| 637 |  | 
| 638   report->AddString(StatsReport::kStatsValueNameLocalAddress, |  | 
| 639                     info.local_candidate.address().ToString()); |  | 
| 640   report->AddString(StatsReport::kStatsValueNameLocalCandidateType, |  | 
| 641                     info.local_candidate.type()); |  | 
| 642   report->AddString(StatsReport::kStatsValueNameRemoteAddress, |  | 
| 643                     info.remote_candidate.address().ToString()); |  | 
| 644   report->AddString(StatsReport::kStatsValueNameRemoteCandidateType, |  | 
| 645                     info.remote_candidate.type()); |  | 
| 646   report->AddString(StatsReport::kStatsValueNameTransportType, |  | 
| 647                     info.local_candidate.protocol()); |  | 
| 648 |  | 
| 649   return report; |  | 
| 650 } |  | 
| 651 |  | 
| 652 StatsReport* StatsCollector::AddCandidateReport( |  | 
| 653     const cricket::Candidate& candidate, |  | 
| 654     bool local) { |  | 
| 655   StatsReport::Id id(StatsReport::NewCandidateId(local, candidate.id())); |  | 
| 656   StatsReport* report = reports_.Find(id); |  | 
| 657   if (!report) { |  | 
| 658     report = reports_.InsertNew(id); |  | 
| 659     report->set_timestamp(stats_gathering_started_); |  | 
| 660     if (local) { |  | 
| 661       report->AddString(StatsReport::kStatsValueNameCandidateNetworkType, |  | 
| 662                         AdapterTypeToStatsType(candidate.network_type())); |  | 
| 663     } |  | 
| 664     report->AddString(StatsReport::kStatsValueNameCandidateIPAddress, |  | 
| 665                       candidate.address().ipaddr().ToString()); |  | 
| 666     report->AddString(StatsReport::kStatsValueNameCandidatePortNumber, |  | 
| 667                       candidate.address().PortAsString()); |  | 
| 668     report->AddInt(StatsReport::kStatsValueNameCandidatePriority, |  | 
| 669                    candidate.priority()); |  | 
| 670     report->AddString(StatsReport::kStatsValueNameCandidateType, |  | 
| 671                       IceCandidateTypeToStatsType(candidate.type())); |  | 
| 672     report->AddString(StatsReport::kStatsValueNameCandidateTransportType, |  | 
| 673                       candidate.protocol()); |  | 
| 674   } |  | 
| 675 |  | 
| 676   return report; |  | 
| 677 } |  | 
| 678 |  | 
| 679 void StatsCollector::ExtractSessionInfo() { |  | 
| 680   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 681 |  | 
| 682   // Extract information from the base session. |  | 
| 683   StatsReport::Id id(StatsReport::NewTypedId( |  | 
| 684       StatsReport::kStatsReportTypeSession, pc_->session()->id())); |  | 
| 685   StatsReport* report = reports_.ReplaceOrAddNew(id); |  | 
| 686   report->set_timestamp(stats_gathering_started_); |  | 
| 687   report->AddBoolean(StatsReport::kStatsValueNameInitiator, |  | 
| 688                      pc_->session()->initial_offerer()); |  | 
| 689 |  | 
| 690   SessionStats stats; |  | 
| 691   if (!pc_->session()->GetTransportStats(&stats)) { |  | 
| 692     return; |  | 
| 693   } |  | 
| 694 |  | 
| 695   // Store the proxy map away for use in SSRC reporting. |  | 
| 696   // TODO(tommi): This shouldn't be necessary if we post the stats back to the |  | 
| 697   // signaling thread after fetching them on the worker thread, then just use |  | 
| 698   // the proxy map directly from the session stats. |  | 
| 699   // As is, if GetStats() failed, we could be using old (incorrect?) proxy |  | 
| 700   // data. |  | 
| 701   proxy_to_transport_ = stats.proxy_to_transport; |  | 
| 702 |  | 
| 703   for (const auto& transport_iter : stats.transport_stats) { |  | 
| 704     // Attempt to get a copy of the certificates from the transport and |  | 
| 705     // expose them in stats reports.  All channels in a transport share the |  | 
| 706     // same local and remote certificates. |  | 
| 707     // |  | 
| 708     StatsReport::Id local_cert_report_id, remote_cert_report_id; |  | 
| 709     rtc::scoped_refptr<rtc::RTCCertificate> certificate; |  | 
| 710     if (pc_->session()->GetLocalCertificate( |  | 
| 711             transport_iter.second.transport_name, &certificate)) { |  | 
| 712       StatsReport* r = AddCertificateReports(&(certificate->ssl_certificate())); |  | 
| 713       if (r) |  | 
| 714         local_cert_report_id = r->id(); |  | 
| 715     } |  | 
| 716 |  | 
| 717     rtc::scoped_ptr<rtc::SSLCertificate> cert; |  | 
| 718     if (pc_->session()->GetRemoteSSLCertificate( |  | 
| 719             transport_iter.second.transport_name, cert.accept())) { |  | 
| 720       StatsReport* r = AddCertificateReports(cert.get()); |  | 
| 721       if (r) |  | 
| 722         remote_cert_report_id = r->id(); |  | 
| 723     } |  | 
| 724 |  | 
| 725     for (const auto& channel_iter : transport_iter.second.channel_stats) { |  | 
| 726       StatsReport::Id id(StatsReport::NewComponentId( |  | 
| 727           transport_iter.second.transport_name, channel_iter.component)); |  | 
| 728       StatsReport* channel_report = reports_.ReplaceOrAddNew(id); |  | 
| 729       channel_report->set_timestamp(stats_gathering_started_); |  | 
| 730       channel_report->AddInt(StatsReport::kStatsValueNameComponent, |  | 
| 731                              channel_iter.component); |  | 
| 732       if (local_cert_report_id.get()) { |  | 
| 733         channel_report->AddId(StatsReport::kStatsValueNameLocalCertificateId, |  | 
| 734                               local_cert_report_id); |  | 
| 735       } |  | 
| 736       if (remote_cert_report_id.get()) { |  | 
| 737         channel_report->AddId(StatsReport::kStatsValueNameRemoteCertificateId, |  | 
| 738                               remote_cert_report_id); |  | 
| 739       } |  | 
| 740       int srtp_crypto_suite = channel_iter.srtp_crypto_suite; |  | 
| 741       if (srtp_crypto_suite != rtc::SRTP_INVALID_CRYPTO_SUITE && |  | 
| 742           rtc::SrtpCryptoSuiteToName(srtp_crypto_suite).length()) { |  | 
| 743         channel_report->AddString( |  | 
| 744             StatsReport::kStatsValueNameSrtpCipher, |  | 
| 745             rtc::SrtpCryptoSuiteToName(srtp_crypto_suite)); |  | 
| 746       } |  | 
| 747       int ssl_cipher_suite = channel_iter.ssl_cipher_suite; |  | 
| 748       if (ssl_cipher_suite != rtc::TLS_NULL_WITH_NULL_NULL && |  | 
| 749           rtc::SSLStreamAdapter::SslCipherSuiteToName(ssl_cipher_suite) |  | 
| 750               .length()) { |  | 
| 751         channel_report->AddString( |  | 
| 752             StatsReport::kStatsValueNameDtlsCipher, |  | 
| 753             rtc::SSLStreamAdapter::SslCipherSuiteToName(ssl_cipher_suite)); |  | 
| 754       } |  | 
| 755 |  | 
| 756       int connection_id = 0; |  | 
| 757       for (const cricket::ConnectionInfo& info : |  | 
| 758                channel_iter.connection_infos) { |  | 
| 759         StatsReport* connection_report = AddConnectionInfoReport( |  | 
| 760             transport_iter.first, channel_iter.component, connection_id++, |  | 
| 761             channel_report->id(), info); |  | 
| 762         if (info.best_connection) { |  | 
| 763           channel_report->AddId( |  | 
| 764               StatsReport::kStatsValueNameSelectedCandidatePairId, |  | 
| 765               connection_report->id()); |  | 
| 766         } |  | 
| 767       } |  | 
| 768     } |  | 
| 769   } |  | 
| 770 } |  | 
| 771 |  | 
| 772 void StatsCollector::ExtractVoiceInfo() { |  | 
| 773   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 774 |  | 
| 775   if (!pc_->session()->voice_channel()) { |  | 
| 776     return; |  | 
| 777   } |  | 
| 778   cricket::VoiceMediaInfo voice_info; |  | 
| 779   if (!pc_->session()->voice_channel()->GetStats(&voice_info)) { |  | 
| 780     LOG(LS_ERROR) << "Failed to get voice channel stats."; |  | 
| 781     return; |  | 
| 782   } |  | 
| 783 |  | 
| 784   // TODO(tommi): The above code should run on the worker thread and post the |  | 
| 785   // results back to the signaling thread, where we can add data to the reports. |  | 
| 786   rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; |  | 
| 787 |  | 
| 788   StatsReport::Id transport_id(GetTransportIdFromProxy( |  | 
| 789       proxy_to_transport_, pc_->session()->voice_channel()->content_name())); |  | 
| 790   if (!transport_id.get()) { |  | 
| 791     LOG(LS_ERROR) << "Failed to get transport name for proxy " |  | 
| 792                   << pc_->session()->voice_channel()->content_name(); |  | 
| 793     return; |  | 
| 794   } |  | 
| 795 |  | 
| 796   ExtractStatsFromList(voice_info.receivers, transport_id, this, |  | 
| 797       StatsReport::kReceive); |  | 
| 798   ExtractStatsFromList(voice_info.senders, transport_id, this, |  | 
| 799       StatsReport::kSend); |  | 
| 800 |  | 
| 801   UpdateStatsFromExistingLocalAudioTracks(); |  | 
| 802 } |  | 
| 803 |  | 
| 804 void StatsCollector::ExtractVideoInfo( |  | 
| 805     PeerConnectionInterface::StatsOutputLevel level) { |  | 
| 806   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 807 |  | 
| 808   if (!pc_->session()->video_channel()) |  | 
| 809     return; |  | 
| 810 |  | 
| 811   cricket::VideoMediaInfo video_info; |  | 
| 812   if (!pc_->session()->video_channel()->GetStats(&video_info)) { |  | 
| 813     LOG(LS_ERROR) << "Failed to get video channel stats."; |  | 
| 814     return; |  | 
| 815   } |  | 
| 816 |  | 
| 817   // TODO(tommi): The above code should run on the worker thread and post the |  | 
| 818   // results back to the signaling thread, where we can add data to the reports. |  | 
| 819   rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; |  | 
| 820 |  | 
| 821   StatsReport::Id transport_id(GetTransportIdFromProxy( |  | 
| 822       proxy_to_transport_, pc_->session()->video_channel()->content_name())); |  | 
| 823   if (!transport_id.get()) { |  | 
| 824     LOG(LS_ERROR) << "Failed to get transport name for proxy " |  | 
| 825                   << pc_->session()->video_channel()->content_name(); |  | 
| 826     return; |  | 
| 827   } |  | 
| 828   ExtractStatsFromList(video_info.receivers, transport_id, this, |  | 
| 829       StatsReport::kReceive); |  | 
| 830   ExtractStatsFromList(video_info.senders, transport_id, this, |  | 
| 831       StatsReport::kSend); |  | 
| 832   if (video_info.bw_estimations.size() != 1) { |  | 
| 833     LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size(); |  | 
| 834   } else { |  | 
| 835     StatsReport::Id report_id(StatsReport::NewBandwidthEstimationId()); |  | 
| 836     StatsReport* report = reports_.FindOrAddNew(report_id); |  | 
| 837     ExtractStats( |  | 
| 838         video_info.bw_estimations[0], stats_gathering_started_, level, report); |  | 
| 839   } |  | 
| 840 } |  | 
| 841 |  | 
| 842 void StatsCollector::ExtractDataInfo() { |  | 
| 843   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 844 |  | 
| 845   rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; |  | 
| 846 |  | 
| 847   for (const auto& dc : pc_->sctp_data_channels()) { |  | 
| 848     StatsReport::Id id(StatsReport::NewTypedIntId( |  | 
| 849         StatsReport::kStatsReportTypeDataChannel, dc->id())); |  | 
| 850     StatsReport* report = reports_.ReplaceOrAddNew(id); |  | 
| 851     report->set_timestamp(stats_gathering_started_); |  | 
| 852     report->AddString(StatsReport::kStatsValueNameLabel, dc->label()); |  | 
| 853     report->AddInt(StatsReport::kStatsValueNameDataChannelId, dc->id()); |  | 
| 854     report->AddString(StatsReport::kStatsValueNameProtocol, dc->protocol()); |  | 
| 855     report->AddString(StatsReport::kStatsValueNameState, |  | 
| 856                       DataChannelInterface::DataStateString(dc->state())); |  | 
| 857   } |  | 
| 858 } |  | 
| 859 |  | 
| 860 StatsReport* StatsCollector::GetReport(const StatsReport::StatsType& type, |  | 
| 861                                        const std::string& id, |  | 
| 862                                        StatsReport::Direction direction) { |  | 
| 863   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 864   RTC_DCHECK(type == StatsReport::kStatsReportTypeSsrc || |  | 
| 865              type == StatsReport::kStatsReportTypeRemoteSsrc); |  | 
| 866   return reports_.Find(StatsReport::NewIdWithDirection(type, id, direction)); |  | 
| 867 } |  | 
| 868 |  | 
| 869 void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() { |  | 
| 870   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 871   // Loop through the existing local audio tracks. |  | 
| 872   for (const auto& it : local_audio_tracks_) { |  | 
| 873     AudioTrackInterface* track = it.first; |  | 
| 874     uint32_t ssrc = it.second; |  | 
| 875     StatsReport* report = |  | 
| 876         GetReport(StatsReport::kStatsReportTypeSsrc, |  | 
| 877                   rtc::ToString<uint32_t>(ssrc), StatsReport::kSend); |  | 
| 878     if (report == NULL) { |  | 
| 879       // This can happen if a local audio track is added to a stream on the |  | 
| 880       // fly and the report has not been set up yet. Do nothing in this case. |  | 
| 881       LOG(LS_ERROR) << "Stats report does not exist for ssrc " << ssrc; |  | 
| 882       continue; |  | 
| 883     } |  | 
| 884 |  | 
| 885     // The same ssrc can be used by both local and remote audio tracks. |  | 
| 886     const StatsReport::Value* v = |  | 
| 887         report->FindValue(StatsReport::kStatsValueNameTrackId); |  | 
| 888     if (!v || v->string_val() != track->id()) |  | 
| 889       continue; |  | 
| 890 |  | 
| 891     report->set_timestamp(stats_gathering_started_); |  | 
| 892     UpdateReportFromAudioTrack(track, report); |  | 
| 893   } |  | 
| 894 } |  | 
| 895 |  | 
| 896 void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track, |  | 
| 897                                                 StatsReport* report) { |  | 
| 898   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 899   RTC_DCHECK(track != NULL); |  | 
| 900 |  | 
| 901   // Don't overwrite report values if they're not available. |  | 
| 902   int signal_level; |  | 
| 903   if (track->GetSignalLevel(&signal_level)) { |  | 
| 904     RTC_DCHECK_GE(signal_level, 0); |  | 
| 905     report->AddInt(StatsReport::kStatsValueNameAudioInputLevel, signal_level); |  | 
| 906   } |  | 
| 907 |  | 
| 908   auto audio_processor(track->GetAudioProcessor()); |  | 
| 909 |  | 
| 910   if (audio_processor.get()) { |  | 
| 911     AudioProcessorInterface::AudioProcessorStats stats; |  | 
| 912     audio_processor->GetStats(&stats); |  | 
| 913 |  | 
| 914     SetAudioProcessingStats( |  | 
| 915         report, stats.typing_noise_detected, stats.echo_return_loss, |  | 
| 916         stats.echo_return_loss_enhancement, stats.echo_delay_median_ms, |  | 
| 917         stats.aec_quality_min, stats.echo_delay_std_ms); |  | 
| 918   } |  | 
| 919 } |  | 
| 920 |  | 
| 921 bool StatsCollector::GetTrackIdBySsrc(uint32_t ssrc, |  | 
| 922                                       std::string* track_id, |  | 
| 923                                       StatsReport::Direction direction) { |  | 
| 924   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 925   if (direction == StatsReport::kSend) { |  | 
| 926     if (!pc_->session()->GetLocalTrackIdBySsrc(ssrc, track_id)) { |  | 
| 927       LOG(LS_WARNING) << "The SSRC " << ssrc |  | 
| 928                       << " is not associated with a sending track"; |  | 
| 929       return false; |  | 
| 930     } |  | 
| 931   } else { |  | 
| 932     RTC_DCHECK(direction == StatsReport::kReceive); |  | 
| 933     if (!pc_->session()->GetRemoteTrackIdBySsrc(ssrc, track_id)) { |  | 
| 934       LOG(LS_WARNING) << "The SSRC " << ssrc |  | 
| 935                       << " is not associated with a receiving track"; |  | 
| 936       return false; |  | 
| 937     } |  | 
| 938   } |  | 
| 939 |  | 
| 940   return true; |  | 
| 941 } |  | 
| 942 |  | 
| 943 void StatsCollector::UpdateTrackReports() { |  | 
| 944   RTC_DCHECK(pc_->session()->signaling_thread()->IsCurrent()); |  | 
| 945 |  | 
| 946   rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; |  | 
| 947 |  | 
| 948   for (const auto& entry : track_ids_) { |  | 
| 949     StatsReport* report = entry.second; |  | 
| 950     report->set_timestamp(stats_gathering_started_); |  | 
| 951   } |  | 
| 952 } |  | 
| 953 |  | 
| 954 void StatsCollector::ClearUpdateStatsCacheForTest() { |  | 
| 955   stats_gathering_started_ = 0; |  | 
| 956 } |  | 
| 957 |  | 
| 958 }  // namespace webrtc |  | 
| OLD | NEW | 
|---|