Upstream version 5.34.104.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/base/base64.h"
34 #include "talk/base/scoped_ptr.h"
35 #include "talk/session/media/channel.h"
36
37 namespace webrtc {
38
39 // The items below are in alphabetical order.
40 const char StatsReport::kStatsValueNameActiveConnection[] =
41     "googActiveConnection";
42 const char StatsReport::kStatsValueNameActualEncBitrate[] =
43     "googActualEncBitrate";
44 const char StatsReport::kStatsValueNameAudioOutputLevel[] = "audioOutputLevel";
45 const char StatsReport::kStatsValueNameAudioInputLevel[] = "audioInputLevel";
46 const char StatsReport::kStatsValueNameAvailableReceiveBandwidth[] =
47     "googAvailableReceiveBandwidth";
48 const char StatsReport::kStatsValueNameAvailableSendBandwidth[] =
49     "googAvailableSendBandwidth";
50 const char StatsReport::kStatsValueNameAvgEncodeMs[] = "googAvgEncodeMs";
51 const char StatsReport::kStatsValueNameBucketDelay[] = "googBucketDelay";
52 const char StatsReport::kStatsValueNameBytesReceived[] = "bytesReceived";
53 const char StatsReport::kStatsValueNameBytesSent[] = "bytesSent";
54 const char StatsReport::kStatsValueNameBandwidthLimitedResolution[] =
55     "googBandwidthLimitedResolution";
56 const char StatsReport::kStatsValueNameCaptureJitterMs[] =
57     "googCaptureJitterMs";
58 const char StatsReport::kStatsValueNameCaptureQueueDelayMsPerS[] =
59     "googCaptureQueueDelayMsPerS";
60 const char StatsReport::kStatsValueNameChannelId[] = "googChannelId";
61 const char StatsReport::kStatsValueNameCodecName[] = "googCodecName";
62 const char StatsReport::kStatsValueNameComponent[] = "googComponent";
63 const char StatsReport::kStatsValueNameContentName[] = "googContentName";
64 const char StatsReport::kStatsValueNameCpuLimitedResolution[] =
65     "googCpuLimitedResolution";
66 const char StatsReport::kStatsValueNameDer[] = "googDerBase64";
67 // Echo metrics from the audio processing module.
68 const char StatsReport::kStatsValueNameEchoCancellationQualityMin[] =
69     "googEchoCancellationQualityMin";
70 const char StatsReport::kStatsValueNameEchoDelayMedian[] =
71     "googEchoCancellationEchoDelayMedian";
72 const char StatsReport::kStatsValueNameEchoDelayStdDev[] =
73     "googEchoCancellationEchoDelayStdDev";
74 const char StatsReport::kStatsValueNameEchoReturnLoss[] =
75     "googEchoCancellationReturnLoss";
76 const char StatsReport::kStatsValueNameEchoReturnLossEnhancement[] =
77     "googEchoCancellationReturnLossEnhancement";
78
79 const char StatsReport::kStatsValueNameEncodeUsagePercent[] =
80     "googEncodeUsagePercent";
81 const char StatsReport::kStatsValueNameExpandRate[] = "googExpandRate";
82 const char StatsReport::kStatsValueNameFingerprint[] = "googFingerprint";
83 const char StatsReport::kStatsValueNameFingerprintAlgorithm[] =
84     "googFingerprintAlgorithm";
85 const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived";
86 const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent";
87 const char StatsReport::kStatsValueNameFrameHeightInput[] =
88     "googFrameHeightInput";
89 const char StatsReport::kStatsValueNameFrameHeightReceived[] =
90     "googFrameHeightReceived";
91 const char StatsReport::kStatsValueNameFrameHeightSent[] =
92     "googFrameHeightSent";
93 const char StatsReport::kStatsValueNameFrameRateReceived[] =
94     "googFrameRateReceived";
95 const char StatsReport::kStatsValueNameFrameRateDecoded[] =
96     "googFrameRateDecoded";
97 const char StatsReport::kStatsValueNameFrameRateOutput[] =
98     "googFrameRateOutput";
99 const char StatsReport::kStatsValueNameDecodeMs[] = "googDecodeMs";
100 const char StatsReport::kStatsValueNameMaxDecodeMs[] = "googMaxDecodeMs";
101 const char StatsReport::kStatsValueNameCurrentDelayMs[] = "googCurrentDelayMs";
102 const char StatsReport::kStatsValueNameTargetDelayMs[] = "googTargetDelayMs";
103 const char StatsReport::kStatsValueNameJitterBufferMs[] = "googJitterBufferMs";
104 const char StatsReport::kStatsValueNameMinPlayoutDelayMs[] =
105     "googMinPlayoutDelayMs";
106 const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs";
107
108 const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput";
109 const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent";
110 const char StatsReport::kStatsValueNameFrameWidthInput[] =
111     "googFrameWidthInput";
112 const char StatsReport::kStatsValueNameFrameWidthReceived[] =
113     "googFrameWidthReceived";
114 const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent";
115 const char StatsReport::kStatsValueNameInitiator[] = "googInitiator";
116 const char StatsReport::kStatsValueNameIssuerId[] = "googIssuerId";
117 const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived";
118 const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress";
119 const char StatsReport::kStatsValueNameLocalCandidateType[] =
120     "googLocalCandidateType";
121 const char StatsReport::kStatsValueNameLocalCertificateId[] =
122     "googLocalCertificateId";
123 const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived";
124 const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent";
125 const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived";
126 const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent";
127 const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost";
128 const char StatsReport::kStatsValueNameReadable[] = "googReadable";
129 const char StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug[] =
130     "googReceivedPacketGroupArrivalTimeDebug";
131 const char StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug[] =
132     "googReceivedPacketGroupPropagationDeltaDebug";
133 const char
134 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug[] =
135     "googReceivedPacketGroupPropagationDeltaSumDebug";
136 const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress";
137 const char StatsReport::kStatsValueNameRemoteCandidateType[] =
138     "googRemoteCandidateType";
139 const char StatsReport::kStatsValueNameRemoteCertificateId[] =
140     "googRemoteCertificateId";
141 const char StatsReport::kStatsValueNameRetransmitBitrate[] =
142     "googRetransmitBitrate";
143 const char StatsReport::kStatsValueNameRtt[] = "googRtt";
144 const char StatsReport::kStatsValueNameSsrc[] = "ssrc";
145 const char StatsReport::kStatsValueNameTargetEncBitrate[] =
146     "googTargetEncBitrate";
147 const char StatsReport::kStatsValueNameTransmitBitrate[] =
148     "googTransmitBitrate";
149 const char StatsReport::kStatsValueNameTransportId[] = "transportId";
150 const char StatsReport::kStatsValueNameTransportType[] = "googTransportType";
151 const char StatsReport::kStatsValueNameTrackId[] = "googTrackId";
152 const char StatsReport::kStatsValueNameTypingNoiseState[] =
153     "googTypingNoiseState";
154 const char StatsReport::kStatsValueNameViewLimitedResolution[] =
155     "googViewLimitedResolution";
156 const char StatsReport::kStatsValueNameWritable[] = "googWritable";
157
158 const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession";
159 const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe";
160 const char StatsReport::kStatsReportTypeRemoteSsrc[] = "remoteSsrc";
161 const char StatsReport::kStatsReportTypeSsrc[] = "ssrc";
162 const char StatsReport::kStatsReportTypeTrack[] = "googTrack";
163 const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate";
164 const char StatsReport::kStatsReportTypeTransport[] = "googTransport";
165 const char StatsReport::kStatsReportTypeComponent[] = "googComponent";
166 const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair";
167 const char StatsReport::kStatsReportTypeCertificate[] = "googCertificate";
168
169 const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo";
170
171
172 // Implementations of functions in statstypes.h
173 void StatsReport::AddValue(const std::string& name, const std::string& value) {
174   Value temp;
175   temp.name = name;
176   temp.value = value;
177   values.push_back(temp);
178 }
179
180 void StatsReport::AddValue(const std::string& name, int64 value) {
181   AddValue(name, talk_base::ToString<int64>(value));
182 }
183
184 template <typename T>
185 void StatsReport::AddValue(const std::string& name,
186                            const std::vector<T>& value) {
187   std::ostringstream oss;
188   oss << "[";
189   for (size_t i = 0; i < value.size(); ++i) {
190     oss << talk_base::ToString<T>(value[i]);
191     if (i != value.size() - 1)
192       oss << ", ";
193   }
194   oss << "]";
195   AddValue(name, oss.str());
196 }
197
198 void StatsReport::AddBoolean(const std::string& name, bool value) {
199   AddValue(name, value ? "true" : "false");
200 }
201
202 namespace {
203 typedef std::map<std::string, StatsReport> StatsMap;
204
205 std::string StatsId(const std::string& type, const std::string& id) {
206   return type + "_" + id;
207 }
208
209 bool ExtractValueFromReport(
210     const StatsReport& report,
211     const std::string& name,
212     std::string* value) {
213   StatsReport::Values::const_iterator it = report.values.begin();
214   for (; it != report.values.end(); ++it) {
215     if (it->name == name) {
216       *value = it->value;
217       return true;
218     }
219   }
220   return false;
221 }
222
223 template <class TrackVector>
224 void CreateTrackReports(const TrackVector& tracks, StatsMap* reports) {
225   for (size_t j = 0; j < tracks.size(); ++j) {
226     webrtc::MediaStreamTrackInterface* track = tracks[j];
227     // Adds an empty track report.
228     StatsReport report;
229     report.type = StatsReport::kStatsReportTypeTrack;
230     report.id = StatsId(StatsReport::kStatsReportTypeTrack, track->id());
231     report.AddValue(StatsReport::kStatsValueNameTrackId,
232                     track->id());
233     (*reports)[report.id] = report;
234   }
235 }
236
237 void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
238   report->AddValue(StatsReport::kStatsValueNameAudioOutputLevel,
239                    info.audio_level);
240   report->AddValue(StatsReport::kStatsValueNameBytesReceived,
241                    info.bytes_rcvd);
242   report->AddValue(StatsReport::kStatsValueNameJitterReceived,
243                    info.jitter_ms);
244   report->AddValue(StatsReport::kStatsValueNameExpandRate,
245                    talk_base::ToString<float>(info.expand_rate));
246   report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
247                    info.packets_rcvd);
248   report->AddValue(StatsReport::kStatsValueNamePacketsLost,
249                    info.packets_lost);
250 }
251
252 void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
253   report->AddValue(StatsReport::kStatsValueNameAudioInputLevel,
254                    info.audio_level);
255   report->AddValue(StatsReport::kStatsValueNameBytesSent,
256                    info.bytes_sent);
257   report->AddValue(StatsReport::kStatsValueNamePacketsSent,
258                    info.packets_sent);
259   report->AddValue(StatsReport::kStatsValueNameJitterReceived,
260                    info.jitter_ms);
261   report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
262   report->AddValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
263                    talk_base::ToString<float>(info.aec_quality_min));
264   report->AddValue(StatsReport::kStatsValueNameEchoDelayMedian,
265                    info.echo_delay_median_ms);
266   report->AddValue(StatsReport::kStatsValueNameEchoDelayStdDev,
267                    info.echo_delay_std_ms);
268   report->AddValue(StatsReport::kStatsValueNameEchoReturnLoss,
269                    info.echo_return_loss);
270   report->AddValue(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
271                    info.echo_return_loss_enhancement);
272   report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
273   report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
274                      info.typing_noise_detected);
275 }
276
277 void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
278   report->AddValue(StatsReport::kStatsValueNameBytesReceived,
279                    info.bytes_rcvd);
280   report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
281                    info.packets_rcvd);
282   report->AddValue(StatsReport::kStatsValueNamePacketsLost,
283                    info.packets_lost);
284
285   report->AddValue(StatsReport::kStatsValueNameFirsSent,
286                    info.firs_sent);
287   report->AddValue(StatsReport::kStatsValueNameNacksSent,
288                    info.nacks_sent);
289   report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived,
290                    info.frame_width);
291   report->AddValue(StatsReport::kStatsValueNameFrameHeightReceived,
292                    info.frame_height);
293   report->AddValue(StatsReport::kStatsValueNameFrameRateReceived,
294                    info.framerate_rcvd);
295   report->AddValue(StatsReport::kStatsValueNameFrameRateDecoded,
296                    info.framerate_decoded);
297   report->AddValue(StatsReport::kStatsValueNameFrameRateOutput,
298                    info.framerate_output);
299
300   report->AddValue(StatsReport::kStatsValueNameDecodeMs,
301                    info.decode_ms);
302   report->AddValue(StatsReport::kStatsValueNameMaxDecodeMs,
303                    info.max_decode_ms);
304   report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs,
305                    info.current_delay_ms);
306   report->AddValue(StatsReport::kStatsValueNameTargetDelayMs,
307                    info.target_delay_ms);
308   report->AddValue(StatsReport::kStatsValueNameJitterBufferMs,
309                    info.jitter_buffer_ms);
310   report->AddValue(StatsReport::kStatsValueNameMinPlayoutDelayMs,
311                    info.min_playout_delay_ms);
312   report->AddValue(StatsReport::kStatsValueNameRenderDelayMs,
313                    info.render_delay_ms);
314 }
315
316 void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
317   report->AddValue(StatsReport::kStatsValueNameBytesSent,
318                    info.bytes_sent);
319   report->AddValue(StatsReport::kStatsValueNamePacketsSent,
320                    info.packets_sent);
321
322   report->AddValue(StatsReport::kStatsValueNameFirsReceived,
323                    info.firs_rcvd);
324   report->AddValue(StatsReport::kStatsValueNameNacksReceived,
325                    info.nacks_rcvd);
326   report->AddValue(StatsReport::kStatsValueNameFrameWidthInput,
327                    info.input_frame_width);
328   report->AddValue(StatsReport::kStatsValueNameFrameHeightInput,
329                    info.input_frame_height);
330   report->AddValue(StatsReport::kStatsValueNameFrameWidthSent,
331                    info.send_frame_width);
332   report->AddValue(StatsReport::kStatsValueNameFrameHeightSent,
333                    info.send_frame_height);
334   report->AddValue(StatsReport::kStatsValueNameFrameRateInput,
335                    info.framerate_input);
336   report->AddValue(StatsReport::kStatsValueNameFrameRateSent,
337                    info.framerate_sent);
338   report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
339   report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
340   report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution,
341                      (info.adapt_reason & 0x1) > 0);
342   report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution,
343                      (info.adapt_reason & 0x2) > 0);
344   report->AddBoolean(StatsReport::kStatsValueNameViewLimitedResolution,
345                      (info.adapt_reason & 0x4) > 0);
346   report->AddValue(StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms);
347   report->AddValue(StatsReport::kStatsValueNameCaptureJitterMs,
348                    info.capture_jitter_ms);
349   report->AddValue(StatsReport::kStatsValueNameCaptureQueueDelayMsPerS,
350                    info.capture_queue_delay_ms_per_s);
351   report->AddValue(StatsReport::kStatsValueNameEncodeUsagePercent,
352                    info.encode_usage_percent);
353 }
354
355 void ExtractStats(const cricket::BandwidthEstimationInfo& info,
356                   double stats_gathering_started,
357                   PeerConnectionInterface::StatsOutputLevel level,
358                   StatsReport* report) {
359   report->id = StatsReport::kStatsReportVideoBweId;
360   report->type = StatsReport::kStatsReportTypeBwe;
361
362   // Clear out stats from previous GatherStats calls if any.
363   if (report->timestamp != stats_gathering_started) {
364     report->values.clear();
365     report->timestamp = stats_gathering_started;
366   }
367
368   report->AddValue(StatsReport::kStatsValueNameAvailableSendBandwidth,
369                    info.available_send_bandwidth);
370   report->AddValue(StatsReport::kStatsValueNameAvailableReceiveBandwidth,
371                    info.available_recv_bandwidth);
372   report->AddValue(StatsReport::kStatsValueNameTargetEncBitrate,
373                    info.target_enc_bitrate);
374   report->AddValue(StatsReport::kStatsValueNameActualEncBitrate,
375                    info.actual_enc_bitrate);
376   report->AddValue(StatsReport::kStatsValueNameRetransmitBitrate,
377                    info.retransmit_bitrate);
378   report->AddValue(StatsReport::kStatsValueNameTransmitBitrate,
379                    info.transmit_bitrate);
380   report->AddValue(StatsReport::kStatsValueNameBucketDelay,
381                    info.bucket_delay);
382   if (level >= PeerConnectionInterface::kStatsOutputLevelDebug) {
383     report->AddValue(
384         StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug,
385         info.total_received_propagation_delta_ms);
386     if (info.recent_received_propagation_delta_ms.size() > 0) {
387       report->AddValue(
388           StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug,
389           info.recent_received_propagation_delta_ms);
390       report->AddValue(
391           StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug,
392           info.recent_received_packet_group_arrival_time_ms);
393     }
394   }
395 }
396
397 void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
398                         StatsReport* report) {
399   report->timestamp = info.remote_stats[0].timestamp;
400   // TODO(hta): Extract some stats here.
401 }
402
403 void ExtractRemoteStats(const cricket::MediaReceiverInfo& info,
404                         StatsReport* report) {
405   report->timestamp = info.remote_stats[0].timestamp;
406   // TODO(hta): Extract some stats here.
407 }
408
409 // Template to extract stats from a data vector.
410 // In order to use the template, the functions that are called from it,
411 // ExtractStats and ExtractRemoteStats, must be defined and overloaded
412 // for each type.
413 template<typename T>
414 void ExtractStatsFromList(const std::vector<T>& data,
415                           const std::string& transport_id,
416                           StatsCollector* collector) {
417   typename std::vector<T>::const_iterator it = data.begin();
418   for (; it != data.end(); ++it) {
419     std::string id;
420     uint32 ssrc = it->ssrc();
421     // Each object can result in 2 objects, a local and a remote object.
422     // TODO(hta): Handle the case of multiple SSRCs per object.
423     StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id);
424     if (!report) {
425       continue;
426     }
427     ExtractStats(*it, report);
428     if (it->remote_stats.size() > 0) {
429       report = collector->PrepareRemoteReport(ssrc, transport_id);
430       if (!report) {
431         continue;
432       }
433       ExtractRemoteStats(*it, report);
434     }
435   }
436 }
437
438 }  // namespace
439
440 StatsCollector::StatsCollector()
441     : session_(NULL), stats_gathering_started_(0) {
442 }
443
444 // Adds a MediaStream with tracks that can be used as a |selector| in a call
445 // to GetStats.
446 void StatsCollector::AddStream(MediaStreamInterface* stream) {
447   ASSERT(stream != NULL);
448
449   CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(),
450                                        &reports_);
451   CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(),
452                                        &reports_);
453 }
454
455 bool StatsCollector::GetStats(MediaStreamTrackInterface* track,
456                               StatsReports* reports) {
457   ASSERT(reports != NULL);
458   reports->clear();
459
460   StatsMap::iterator it;
461   if (!track) {
462     for (it = reports_.begin(); it != reports_.end(); ++it) {
463       reports->push_back(it->second);
464     }
465     return true;
466   }
467
468   it = reports_.find(StatsId(StatsReport::kStatsReportTypeSession,
469                              session_->id()));
470   if (it != reports_.end()) {
471     reports->push_back(it->second);
472   }
473
474   it = reports_.find(StatsId(StatsReport::kStatsReportTypeTrack, track->id()));
475
476   if (it == reports_.end()) {
477     LOG(LS_WARNING) << "No StatsReport is available for "<< track->id();
478     return false;
479   }
480
481   reports->push_back(it->second);
482
483   std::string track_id;
484   for (it = reports_.begin(); it != reports_.end(); ++it) {
485     if (it->second.type != StatsReport::kStatsReportTypeSsrc) {
486       continue;
487     }
488     if (ExtractValueFromReport(it->second,
489                                StatsReport::kStatsValueNameTrackId,
490                                &track_id)) {
491       if (track_id == track->id()) {
492         reports->push_back(it->second);
493       }
494     }
495   }
496
497   return true;
498 }
499
500 void
501 StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) {
502   double time_now = GetTimeNow();
503   // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
504   // ms apart will be ignored.
505   const double kMinGatherStatsPeriod = 50;
506   if (stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
507     return;
508   }
509   stats_gathering_started_ = time_now;
510
511   if (session_) {
512     ExtractSessionInfo();
513     ExtractVoiceInfo();
514     ExtractVideoInfo(level);
515   }
516 }
517
518 StatsReport* StatsCollector::PrepareLocalReport(
519     uint32 ssrc,
520     const std::string& transport_id) {
521   std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
522   StatsMap::iterator it = reports_.find(StatsId(
523       StatsReport::kStatsReportTypeSsrc, ssrc_id));
524
525   std::string track_id;
526   if (it == reports_.end()) {
527     if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
528       LOG(LS_WARNING) << "The SSRC " << ssrc
529                       << " is not associated with a track";
530       return NULL;
531     }
532   } else {
533     // Keeps the old track id since we want to report the stats for inactive
534     // tracks.
535     ExtractValueFromReport(it->second,
536                            StatsReport::kStatsValueNameTrackId,
537                            &track_id);
538   }
539
540   StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc,
541                                           ssrc_id);
542
543   // Clear out stats from previous GatherStats calls if any.
544   if (report->timestamp != stats_gathering_started_) {
545     report->values.clear();
546     report->timestamp = stats_gathering_started_;
547   }
548
549   report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
550   report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
551   // Add the mapping of SSRC to transport.
552   report->AddValue(StatsReport::kStatsValueNameTransportId,
553                    transport_id);
554   return report;
555 }
556
557 StatsReport* StatsCollector::PrepareRemoteReport(
558     uint32 ssrc,
559     const std::string& transport_id) {
560   std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
561   StatsMap::iterator it = reports_.find(StatsId(
562       StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id));
563
564   std::string track_id;
565   if (it == reports_.end()) {
566     if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
567       LOG(LS_WARNING) << "The SSRC " << ssrc
568                       << " is not associated with a track";
569       return NULL;
570     }
571   } else {
572     // Keeps the old track id since we want to report the stats for inactive
573     // tracks.
574     ExtractValueFromReport(it->second,
575                            StatsReport::kStatsValueNameTrackId,
576                            &track_id);
577   }
578
579   StatsReport* report = GetOrCreateReport(
580       StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id);
581
582   // Clear out stats from previous GatherStats calls if any.
583   // The timestamp will be added later. Zero it for debugging.
584   report->values.clear();
585   report->timestamp = 0;
586
587   report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
588   report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
589   // Add the mapping of SSRC to transport.
590   report->AddValue(StatsReport::kStatsValueNameTransportId,
591                    transport_id);
592   return report;
593 }
594
595 std::string StatsCollector::AddOneCertificateReport(
596     const talk_base::SSLCertificate* cert, const std::string& issuer_id) {
597   // TODO(bemasc): Move this computation to a helper class that caches these
598   // values to reduce CPU use in GetStats.  This will require adding a fast
599   // SSLCertificate::Equals() method to detect certificate changes.
600
601   std::string digest_algorithm;
602   if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm))
603     return std::string();
604
605   talk_base::scoped_ptr<talk_base::SSLFingerprint> ssl_fingerprint(
606       talk_base::SSLFingerprint::Create(digest_algorithm, cert));
607
608   // SSLFingerprint::Create can fail if the algorithm returned by
609   // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the
610   // implementation of SSLCertificate::ComputeDigest.  This currently happens
611   // with MD5- and SHA-224-signed certificates when linked to libNSS.
612   if (!ssl_fingerprint)
613     return std::string();
614
615   std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
616
617   talk_base::Buffer der_buffer;
618   cert->ToDER(&der_buffer);
619   std::string der_base64;
620   talk_base::Base64::EncodeFromArray(
621       der_buffer.data(), der_buffer.length(), &der_base64);
622
623   StatsReport report;
624   report.type = StatsReport::kStatsReportTypeCertificate;
625   report.id = StatsId(report.type, fingerprint);
626   report.timestamp = stats_gathering_started_;
627   report.AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint);
628   report.AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm,
629                   digest_algorithm);
630   report.AddValue(StatsReport::kStatsValueNameDer, der_base64);
631   if (!issuer_id.empty())
632     report.AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id);
633   reports_[report.id] = report;
634   return report.id;
635 }
636
637 std::string StatsCollector::AddCertificateReports(
638     const talk_base::SSLCertificate* cert) {
639   // Produces a chain of StatsReports representing this certificate and the rest
640   // of its chain, and adds those reports to |reports_|.  The return value is
641   // the id of the leaf report.  The provided cert must be non-null, so at least
642   // one report will always be provided and the returned string will never be
643   // empty.
644   ASSERT(cert != NULL);
645
646   std::string issuer_id;
647   talk_base::scoped_ptr<talk_base::SSLCertChain> chain;
648   if (cert->GetChain(chain.accept())) {
649     // This loop runs in reverse, i.e. from root to leaf, so that each
650     // certificate's issuer's report ID is known before the child certificate's
651     // report is generated.  The root certificate does not have an issuer ID
652     // value.
653     for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) {
654       const talk_base::SSLCertificate& cert_i = chain->Get(i);
655       issuer_id = AddOneCertificateReport(&cert_i, issuer_id);
656     }
657   }
658   // Add the leaf certificate.
659   return AddOneCertificateReport(cert, issuer_id);
660 }
661
662 void StatsCollector::ExtractSessionInfo() {
663   // Extract information from the base session.
664   StatsReport report;
665   report.id = StatsId(StatsReport::kStatsReportTypeSession, session_->id());
666   report.type = StatsReport::kStatsReportTypeSession;
667   report.timestamp = stats_gathering_started_;
668   report.values.clear();
669   report.AddBoolean(StatsReport::kStatsValueNameInitiator,
670                     session_->initiator());
671
672   reports_[report.id] = report;
673
674   cricket::SessionStats stats;
675   if (session_->GetStats(&stats)) {
676     // Store the proxy map away for use in SSRC reporting.
677     proxy_to_transport_ = stats.proxy_to_transport;
678
679     for (cricket::TransportStatsMap::iterator transport_iter
680              = stats.transport_stats.begin();
681          transport_iter != stats.transport_stats.end(); ++transport_iter) {
682       // Attempt to get a copy of the certificates from the transport and
683       // expose them in stats reports.  All channels in a transport share the
684       // same local and remote certificates.
685       std::string local_cert_report_id, remote_cert_report_id;
686       cricket::Transport* transport =
687           session_->GetTransport(transport_iter->second.content_name);
688       if (transport) {
689         talk_base::scoped_ptr<talk_base::SSLIdentity> identity;
690         if (transport->GetIdentity(identity.accept()))
691           local_cert_report_id = AddCertificateReports(
692               &(identity->certificate()));
693
694         talk_base::scoped_ptr<talk_base::SSLCertificate> cert;
695         if (transport->GetRemoteCertificate(cert.accept()))
696           remote_cert_report_id = AddCertificateReports(cert.get());
697       }
698       for (cricket::TransportChannelStatsList::iterator channel_iter
699                = transport_iter->second.channel_stats.begin();
700            channel_iter != transport_iter->second.channel_stats.end();
701            ++channel_iter) {
702         StatsReport channel_report;
703         std::ostringstream ostc;
704         ostc << "Channel-" << transport_iter->second.content_name
705              << "-" << channel_iter->component;
706         channel_report.id = ostc.str();
707         channel_report.type = StatsReport::kStatsReportTypeComponent;
708         channel_report.timestamp = stats_gathering_started_;
709         channel_report.AddValue(StatsReport::kStatsValueNameComponent,
710                                 channel_iter->component);
711         if (!local_cert_report_id.empty())
712           channel_report.AddValue(
713               StatsReport::kStatsValueNameLocalCertificateId,
714               local_cert_report_id);
715         if (!remote_cert_report_id.empty())
716           channel_report.AddValue(
717               StatsReport::kStatsValueNameRemoteCertificateId,
718               remote_cert_report_id);
719         reports_[channel_report.id] = channel_report;
720         for (size_t i = 0;
721              i < channel_iter->connection_infos.size();
722              ++i) {
723           StatsReport report;
724           const cricket::ConnectionInfo& info
725               = channel_iter->connection_infos[i];
726           std::ostringstream ost;
727           ost << "Conn-" << transport_iter->first << "-"
728               << channel_iter->component << "-" << i;
729           report.id = ost.str();
730           report.type = StatsReport::kStatsReportTypeCandidatePair;
731           report.timestamp = stats_gathering_started_;
732           // Link from connection to its containing channel.
733           report.AddValue(StatsReport::kStatsValueNameChannelId,
734                           channel_report.id);
735           report.AddValue(StatsReport::kStatsValueNameBytesSent,
736                           info.sent_total_bytes);
737           report.AddValue(StatsReport::kStatsValueNameBytesReceived,
738                           info.recv_total_bytes);
739           report.AddBoolean(StatsReport::kStatsValueNameWritable,
740                             info.writable);
741           report.AddBoolean(StatsReport::kStatsValueNameReadable,
742                             info.readable);
743           report.AddBoolean(StatsReport::kStatsValueNameActiveConnection,
744                             info.best_connection);
745           report.AddValue(StatsReport::kStatsValueNameLocalAddress,
746                           info.local_candidate.address().ToString());
747           report.AddValue(StatsReport::kStatsValueNameRemoteAddress,
748                           info.remote_candidate.address().ToString());
749           report.AddValue(StatsReport::kStatsValueNameRtt, info.rtt);
750           report.AddValue(StatsReport::kStatsValueNameTransportType,
751                           info.local_candidate.protocol());
752           report.AddValue(StatsReport::kStatsValueNameLocalCandidateType,
753                           info.local_candidate.type());
754           report.AddValue(StatsReport::kStatsValueNameRemoteCandidateType,
755                           info.remote_candidate.type());
756           reports_[report.id] = report;
757         }
758       }
759     }
760   }
761 }
762
763 void StatsCollector::ExtractVoiceInfo() {
764   if (!session_->voice_channel()) {
765     return;
766   }
767   cricket::VoiceMediaInfo voice_info;
768   if (!session_->voice_channel()->GetStats(&voice_info)) {
769     LOG(LS_ERROR) << "Failed to get voice channel stats.";
770     return;
771   }
772   std::string transport_id;
773   if (!GetTransportIdFromProxy(session_->voice_channel()->content_name(),
774                                &transport_id)) {
775     LOG(LS_ERROR) << "Failed to get transport name for proxy "
776                   << session_->voice_channel()->content_name();
777     return;
778   }
779   ExtractStatsFromList(voice_info.receivers, transport_id, this);
780   ExtractStatsFromList(voice_info.senders, transport_id, this);
781 }
782
783 void StatsCollector::ExtractVideoInfo(
784     PeerConnectionInterface::StatsOutputLevel level) {
785   if (!session_->video_channel()) {
786     return;
787   }
788   cricket::StatsOptions options;
789   options.include_received_propagation_stats =
790       (level >= PeerConnectionInterface::kStatsOutputLevelDebug) ?
791           true : false;
792   cricket::VideoMediaInfo video_info;
793   if (!session_->video_channel()->GetStats(options, &video_info)) {
794     LOG(LS_ERROR) << "Failed to get video channel stats.";
795     return;
796   }
797   std::string transport_id;
798   if (!GetTransportIdFromProxy(session_->video_channel()->content_name(),
799                                &transport_id)) {
800     LOG(LS_ERROR) << "Failed to get transport name for proxy "
801                   << session_->video_channel()->content_name();
802     return;
803   }
804   ExtractStatsFromList(video_info.receivers, transport_id, this);
805   ExtractStatsFromList(video_info.senders, transport_id, this);
806   if (video_info.bw_estimations.size() != 1) {
807     LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
808   } else {
809     StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId];
810     ExtractStats(
811         video_info.bw_estimations[0], stats_gathering_started_, level, report);
812   }
813 }
814
815 double StatsCollector::GetTimeNow() {
816   return timing_.WallTimeNow() * talk_base::kNumMillisecsPerSec;
817 }
818
819 bool StatsCollector::GetTransportIdFromProxy(const std::string& proxy,
820                                              std::string* transport) {
821   // TODO(hta): Remove handling of empty proxy name once tests do not use it.
822   if (proxy.empty()) {
823     transport->clear();
824     return true;
825   }
826   if (proxy_to_transport_.find(proxy) == proxy_to_transport_.end()) {
827     LOG(LS_ERROR) << "No transport ID mapping for " << proxy;
828     return false;
829   }
830   std::ostringstream ost;
831   // Component 1 is always used for RTP.
832   ost << "Channel-" << proxy_to_transport_[proxy] << "-1";
833   *transport = ost.str();
834   return true;
835 }
836
837 StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
838                                                const std::string& id) {
839   std::string statsid = StatsId(type, id);
840   StatsReport* report = NULL;
841   std::map<std::string, StatsReport>::iterator it = reports_.find(statsid);
842   if (it == reports_.end()) {
843     report = &reports_[statsid];  // Create new element.
844     report->id = statsid;
845     report->type = type;
846   } else {
847     report = &(it->second);
848   }
849   return report;
850 }
851
852 }  // namespace webrtc