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