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