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