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