blob: 2efc11b1cb026a32cce7836c19fbafd6428dde8d [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";
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +000086const char StatsReport::kStatsValueNameFrameHeightInput[] =
87 "googFrameHeightInput";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000088const char StatsReport::kStatsValueNameFrameHeightReceived[] =
89 "googFrameHeightReceived";
90const char StatsReport::kStatsValueNameFrameHeightSent[] =
91 "googFrameHeightSent";
92const char StatsReport::kStatsValueNameFrameRateReceived[] =
93 "googFrameRateReceived";
94const char StatsReport::kStatsValueNameFrameRateDecoded[] =
95 "googFrameRateDecoded";
96const char StatsReport::kStatsValueNameFrameRateOutput[] =
97 "googFrameRateOutput";
wu@webrtc.org97077a32013-10-25 21:18:33 +000098const char StatsReport::kStatsValueNameDecodeMs[] = "googDecodeMs";
99const char StatsReport::kStatsValueNameMaxDecodeMs[] = "googMaxDecodeMs";
100const char StatsReport::kStatsValueNameCurrentDelayMs[] = "googCurrentDelayMs";
101const char StatsReport::kStatsValueNameTargetDelayMs[] = "googTargetDelayMs";
102const char StatsReport::kStatsValueNameJitterBufferMs[] = "googJitterBufferMs";
103const char StatsReport::kStatsValueNameMinPlayoutDelayMs[] =
104 "googMinPlayoutDelayMs";
105const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs";
106
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000107const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput";
108const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent";
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000109const char StatsReport::kStatsValueNameFrameWidthInput[] =
110 "googFrameWidthInput";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000111const char StatsReport::kStatsValueNameFrameWidthReceived[] =
112 "googFrameWidthReceived";
113const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent";
114const char StatsReport::kStatsValueNameInitiator[] = "googInitiator";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000115const char StatsReport::kStatsValueNameIssuerId[] = "googIssuerId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000116const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived";
117const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress";
wu@webrtc.org364f2042013-11-20 21:49:41 +0000118const char StatsReport::kStatsValueNameLocalCandidateType[] =
119 "googLocalCandidateType";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000120const char StatsReport::kStatsValueNameLocalCertificateId[] =
121 "googLocalCertificateId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived";
123const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent";
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000124const char StatsReport::kStatsValueNameNetEqExpandRate[] =
125 "googNetEqExpandRate";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000126const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived";
127const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent";
128const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost";
129const char StatsReport::kStatsValueNameReadable[] = "googReadable";
130const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress";
wu@webrtc.org364f2042013-11-20 21:49:41 +0000131const char StatsReport::kStatsValueNameRemoteCandidateType[] =
132 "googRemoteCandidateType";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000133const char StatsReport::kStatsValueNameRemoteCertificateId[] =
134 "googRemoteCertificateId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000135const char StatsReport::kStatsValueNameRetransmitBitrate[] =
136 "googRetransmitBitrate";
137const char StatsReport::kStatsValueNameRtt[] = "googRtt";
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000138const char StatsReport::kStatsValueNameSsrc[] = "ssrc";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000139const char StatsReport::kStatsValueNameTargetEncBitrate[] =
140 "googTargetEncBitrate";
141const char StatsReport::kStatsValueNameTransmitBitrate[] =
142 "googTransmitBitrate";
143const char StatsReport::kStatsValueNameTransportId[] = "transportId";
144const char StatsReport::kStatsValueNameTransportType[] = "googTransportType";
145const char StatsReport::kStatsValueNameTrackId[] = "googTrackId";
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000146const char StatsReport::kStatsValueNameTypingNoiseState[] =
147 "googTypingNoiseState";
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +0000148const char StatsReport::kStatsValueNameViewLimitedResolution[] =
149 "googViewLimitedResolution";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000150const char StatsReport::kStatsValueNameWritable[] = "googWritable";
151
152const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession";
153const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe";
wu@webrtc.org97077a32013-10-25 21:18:33 +0000154const char StatsReport::kStatsReportTypeRemoteSsrc[] = "remoteSsrc";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000155const char StatsReport::kStatsReportTypeSsrc[] = "ssrc";
156const char StatsReport::kStatsReportTypeTrack[] = "googTrack";
157const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate";
158const char StatsReport::kStatsReportTypeTransport[] = "googTransport";
159const char StatsReport::kStatsReportTypeComponent[] = "googComponent";
160const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000161const char StatsReport::kStatsReportTypeCertificate[] = "googCertificate";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000162
163const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo";
164
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000165
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000166// Implementations of functions in statstypes.h
167void StatsReport::AddValue(const std::string& name, const std::string& value) {
168 Value temp;
169 temp.name = name;
170 temp.value = value;
171 values.push_back(temp);
172}
173
174void StatsReport::AddValue(const std::string& name, int64 value) {
175 AddValue(name, talk_base::ToString<int64>(value));
176}
177
178void StatsReport::AddBoolean(const std::string& name, bool value) {
179 AddValue(name, value ? "true" : "false");
180}
181
182namespace {
183typedef std::map<std::string, StatsReport> StatsMap;
184
185std::string StatsId(const std::string& type, const std::string& id) {
186 return type + "_" + id;
187}
188
189bool ExtractValueFromReport(
190 const StatsReport& report,
191 const std::string& name,
192 std::string* value) {
193 StatsReport::Values::const_iterator it = report.values.begin();
194 for (; it != report.values.end(); ++it) {
195 if (it->name == name) {
196 *value = it->value;
197 return true;
198 }
199 }
200 return false;
201}
202
203template <class TrackVector>
204void CreateTrackReports(const TrackVector& tracks, StatsMap* reports) {
205 for (size_t j = 0; j < tracks.size(); ++j) {
206 webrtc::MediaStreamTrackInterface* track = tracks[j];
207 // Adds an empty track report.
208 StatsReport report;
209 report.type = StatsReport::kStatsReportTypeTrack;
210 report.id = StatsId(StatsReport::kStatsReportTypeTrack, track->id());
211 report.AddValue(StatsReport::kStatsValueNameTrackId,
212 track->id());
213 (*reports)[report.id] = report;
214 }
215}
216
217void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
218 report->AddValue(StatsReport::kStatsValueNameAudioOutputLevel,
219 info.audio_level);
220 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
221 info.bytes_rcvd);
222 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
223 info.jitter_ms);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000224 report->AddValue(StatsReport::kStatsValueNameNetEqExpandRate,
225 talk_base::ToString<float>(info.expand_rate));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000226 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
227 info.packets_rcvd);
228 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
229 info.packets_lost);
230}
231
232void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
233 report->AddValue(StatsReport::kStatsValueNameAudioInputLevel,
234 info.audio_level);
235 report->AddValue(StatsReport::kStatsValueNameBytesSent,
236 info.bytes_sent);
237 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
238 info.packets_sent);
239 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
240 info.jitter_ms);
241 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
242 report->AddValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
243 talk_base::ToString<float>(info.aec_quality_min));
244 report->AddValue(StatsReport::kStatsValueNameEchoDelayMedian,
245 info.echo_delay_median_ms);
246 report->AddValue(StatsReport::kStatsValueNameEchoDelayStdDev,
247 info.echo_delay_std_ms);
248 report->AddValue(StatsReport::kStatsValueNameEchoReturnLoss,
249 info.echo_return_loss);
250 report->AddValue(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
251 info.echo_return_loss_enhancement);
252 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000253 report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
254 info.typing_noise_detected);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000255}
256
257void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
258 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
259 info.bytes_rcvd);
260 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
261 info.packets_rcvd);
262 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
263 info.packets_lost);
264
265 report->AddValue(StatsReport::kStatsValueNameFirsSent,
266 info.firs_sent);
267 report->AddValue(StatsReport::kStatsValueNameNacksSent,
268 info.nacks_sent);
269 report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived,
270 info.frame_width);
271 report->AddValue(StatsReport::kStatsValueNameFrameHeightReceived,
272 info.frame_height);
273 report->AddValue(StatsReport::kStatsValueNameFrameRateReceived,
274 info.framerate_rcvd);
275 report->AddValue(StatsReport::kStatsValueNameFrameRateDecoded,
276 info.framerate_decoded);
277 report->AddValue(StatsReport::kStatsValueNameFrameRateOutput,
278 info.framerate_output);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000279
280 report->AddValue(StatsReport::kStatsValueNameDecodeMs,
281 info.decode_ms);
282 report->AddValue(StatsReport::kStatsValueNameMaxDecodeMs,
283 info.max_decode_ms);
284 report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs,
285 info.current_delay_ms);
286 report->AddValue(StatsReport::kStatsValueNameTargetDelayMs,
287 info.target_delay_ms);
288 report->AddValue(StatsReport::kStatsValueNameJitterBufferMs,
289 info.jitter_buffer_ms);
290 report->AddValue(StatsReport::kStatsValueNameMinPlayoutDelayMs,
291 info.min_playout_delay_ms);
292 report->AddValue(StatsReport::kStatsValueNameRenderDelayMs,
293 info.render_delay_ms);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000294}
295
296void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
297 report->AddValue(StatsReport::kStatsValueNameBytesSent,
298 info.bytes_sent);
299 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
300 info.packets_sent);
301
302 report->AddValue(StatsReport::kStatsValueNameFirsReceived,
303 info.firs_rcvd);
304 report->AddValue(StatsReport::kStatsValueNameNacksReceived,
305 info.nacks_rcvd);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000306 report->AddValue(StatsReport::kStatsValueNameFrameWidthInput,
307 info.input_frame_width);
308 report->AddValue(StatsReport::kStatsValueNameFrameHeightInput,
309 info.input_frame_height);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000310 report->AddValue(StatsReport::kStatsValueNameFrameWidthSent,
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000311 info.send_frame_width);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000312 report->AddValue(StatsReport::kStatsValueNameFrameHeightSent,
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000313 info.send_frame_height);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000314 report->AddValue(StatsReport::kStatsValueNameFrameRateInput,
315 info.framerate_input);
316 report->AddValue(StatsReport::kStatsValueNameFrameRateSent,
317 info.framerate_sent);
318 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
319 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +0000320 report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution,
321 (info.adapt_reason & 0x1) > 0);
322 report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution,
323 (info.adapt_reason & 0x2) > 0);
324 report->AddBoolean(StatsReport::kStatsValueNameViewLimitedResolution,
325 (info.adapt_reason & 0x4) > 0);
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000326 report->AddValue(StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms);
327 report->AddValue(StatsReport::kStatsValueNameCaptureJitterMs,
328 info.capture_jitter_ms);
wu@webrtc.org9caf2762013-12-11 18:25:07 +0000329 report->AddValue(StatsReport::kStatsValueNameCaptureQueueDelayMsPerS,
330 info.capture_queue_delay_ms_per_s);
331 report->AddValue(StatsReport::kStatsValueNameEncodeUsagePercent,
332 info.encode_usage_percent);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000333}
334
335void ExtractStats(const cricket::BandwidthEstimationInfo& info,
336 double stats_gathering_started,
337 StatsReport* report) {
338 report->id = StatsReport::kStatsReportVideoBweId;
339 report->type = StatsReport::kStatsReportTypeBwe;
340
341 // Clear out stats from previous GatherStats calls if any.
342 if (report->timestamp != stats_gathering_started) {
343 report->values.clear();
344 report->timestamp = stats_gathering_started;
345 }
346
347 report->AddValue(StatsReport::kStatsValueNameAvailableSendBandwidth,
348 info.available_send_bandwidth);
349 report->AddValue(StatsReport::kStatsValueNameAvailableReceiveBandwidth,
350 info.available_recv_bandwidth);
351 report->AddValue(StatsReport::kStatsValueNameTargetEncBitrate,
352 info.target_enc_bitrate);
353 report->AddValue(StatsReport::kStatsValueNameActualEncBitrate,
354 info.actual_enc_bitrate);
355 report->AddValue(StatsReport::kStatsValueNameRetransmitBitrate,
356 info.retransmit_bitrate);
357 report->AddValue(StatsReport::kStatsValueNameTransmitBitrate,
358 info.transmit_bitrate);
359 report->AddValue(StatsReport::kStatsValueNameBucketDelay,
360 info.bucket_delay);
361}
362
wu@webrtc.org97077a32013-10-25 21:18:33 +0000363void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
364 StatsReport* report) {
365 report->timestamp = info.remote_stats[0].timestamp;
366 // TODO(hta): Extract some stats here.
367}
368
369void ExtractRemoteStats(const cricket::MediaReceiverInfo& info,
370 StatsReport* report) {
371 report->timestamp = info.remote_stats[0].timestamp;
372 // TODO(hta): Extract some stats here.
373}
374
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000375// Template to extract stats from a data vector.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000376// In order to use the template, the functions that are called from it,
377// ExtractStats and ExtractRemoteStats, must be defined and overloaded
378// for each type.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000379template<typename T>
380void ExtractStatsFromList(const std::vector<T>& data,
381 const std::string& transport_id,
382 StatsCollector* collector) {
383 typename std::vector<T>::const_iterator it = data.begin();
384 for (; it != data.end(); ++it) {
385 std::string id;
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000386 uint32 ssrc = it->ssrc();
wu@webrtc.org97077a32013-10-25 21:18:33 +0000387 // Each object can result in 2 objects, a local and a remote object.
388 // TODO(hta): Handle the case of multiple SSRCs per object.
389 StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000390 if (!report) {
391 continue;
392 }
393 ExtractStats(*it, report);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000394 if (it->remote_stats.size() > 0) {
395 report = collector->PrepareRemoteReport(ssrc, transport_id);
396 if (!report) {
397 continue;
398 }
399 ExtractRemoteStats(*it, report);
400 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000401 }
402};
403
404} // namespace
405
406StatsCollector::StatsCollector()
407 : session_(NULL), stats_gathering_started_(0) {
408}
409
410// Adds a MediaStream with tracks that can be used as a |selector| in a call
411// to GetStats.
412void StatsCollector::AddStream(MediaStreamInterface* stream) {
413 ASSERT(stream != NULL);
414
415 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(),
416 &reports_);
417 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(),
418 &reports_);
419}
420
421bool StatsCollector::GetStats(MediaStreamTrackInterface* track,
422 StatsReports* reports) {
423 ASSERT(reports != NULL);
424 reports->clear();
425
426 StatsMap::iterator it;
427 if (!track) {
428 for (it = reports_.begin(); it != reports_.end(); ++it) {
429 reports->push_back(it->second);
430 }
431 return true;
432 }
433
434 it = reports_.find(StatsId(StatsReport::kStatsReportTypeSession,
435 session_->id()));
436 if (it != reports_.end()) {
437 reports->push_back(it->second);
438 }
439
440 it = reports_.find(StatsId(StatsReport::kStatsReportTypeTrack, track->id()));
441
442 if (it == reports_.end()) {
443 LOG(LS_WARNING) << "No StatsReport is available for "<< track->id();
444 return false;
445 }
446
447 reports->push_back(it->second);
448
449 std::string track_id;
450 for (it = reports_.begin(); it != reports_.end(); ++it) {
451 if (it->second.type != StatsReport::kStatsReportTypeSsrc) {
452 continue;
453 }
454 if (ExtractValueFromReport(it->second,
455 StatsReport::kStatsValueNameTrackId,
456 &track_id)) {
457 if (track_id == track->id()) {
458 reports->push_back(it->second);
459 }
460 }
461 }
462
463 return true;
464}
465
466void StatsCollector::UpdateStats() {
467 double time_now = GetTimeNow();
468 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
469 // ms apart will be ignored.
470 const double kMinGatherStatsPeriod = 50;
471 if (stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
472 return;
473 }
474 stats_gathering_started_ = time_now;
475
476 if (session_) {
477 ExtractSessionInfo();
478 ExtractVoiceInfo();
479 ExtractVideoInfo();
480 }
481}
482
wu@webrtc.org97077a32013-10-25 21:18:33 +0000483StatsReport* StatsCollector::PrepareLocalReport(
484 uint32 ssrc,
485 const std::string& transport_id) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000486 std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
487 StatsMap::iterator it = reports_.find(StatsId(
488 StatsReport::kStatsReportTypeSsrc, ssrc_id));
489
490 std::string track_id;
491 if (it == reports_.end()) {
492 if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000493 LOG(LS_WARNING) << "The SSRC " << ssrc
494 << " is not associated with a track";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000495 return NULL;
496 }
497 } else {
498 // Keeps the old track id since we want to report the stats for inactive
499 // tracks.
500 ExtractValueFromReport(it->second,
501 StatsReport::kStatsValueNameTrackId,
502 &track_id);
503 }
504
wu@webrtc.org97077a32013-10-25 21:18:33 +0000505 StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc,
506 ssrc_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000507
508 // Clear out stats from previous GatherStats calls if any.
509 if (report->timestamp != stats_gathering_started_) {
510 report->values.clear();
511 report->timestamp = stats_gathering_started_;
512 }
513
514 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
515 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
516 // Add the mapping of SSRC to transport.
517 report->AddValue(StatsReport::kStatsValueNameTransportId,
518 transport_id);
519 return report;
520}
521
wu@webrtc.org97077a32013-10-25 21:18:33 +0000522StatsReport* StatsCollector::PrepareRemoteReport(
523 uint32 ssrc,
524 const std::string& transport_id) {
525 std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
526 StatsMap::iterator it = reports_.find(StatsId(
527 StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id));
528
529 std::string track_id;
530 if (it == reports_.end()) {
531 if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
532 LOG(LS_WARNING) << "The SSRC " << ssrc
533 << " is not associated with a track";
534 return NULL;
535 }
536 } else {
537 // Keeps the old track id since we want to report the stats for inactive
538 // tracks.
539 ExtractValueFromReport(it->second,
540 StatsReport::kStatsValueNameTrackId,
541 &track_id);
542 }
543
544 StatsReport* report = GetOrCreateReport(
545 StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id);
546
547 // Clear out stats from previous GatherStats calls if any.
548 // The timestamp will be added later. Zero it for debugging.
549 report->values.clear();
550 report->timestamp = 0;
551
552 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
553 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
554 // Add the mapping of SSRC to transport.
555 report->AddValue(StatsReport::kStatsValueNameTransportId,
556 transport_id);
557 return report;
558}
559
wu@webrtc.org4551b792013-10-09 15:37:36 +0000560std::string StatsCollector::AddOneCertificateReport(
561 const talk_base::SSLCertificate* cert, const std::string& issuer_id) {
562 // TODO(bemasc): Move this computation to a helper class that caches these
563 // values to reduce CPU use in GetStats. This will require adding a fast
564 // SSLCertificate::Equals() method to detect certificate changes.
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000565
566 std::string digest_algorithm;
567 if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm))
568 return std::string();
569
wu@webrtc.org4551b792013-10-09 15:37:36 +0000570 talk_base::scoped_ptr<talk_base::SSLFingerprint> ssl_fingerprint(
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000571 talk_base::SSLFingerprint::Create(digest_algorithm, cert));
wu@webrtc.org4551b792013-10-09 15:37:36 +0000572 std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
573
574 talk_base::Buffer der_buffer;
575 cert->ToDER(&der_buffer);
576 std::string der_base64;
577 talk_base::Base64::EncodeFromArray(
578 der_buffer.data(), der_buffer.length(), &der_base64);
579
580 StatsReport report;
581 report.type = StatsReport::kStatsReportTypeCertificate;
582 report.id = StatsId(report.type, fingerprint);
583 report.timestamp = stats_gathering_started_;
584 report.AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint);
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000585 report.AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm,
586 digest_algorithm);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000587 report.AddValue(StatsReport::kStatsValueNameDer, der_base64);
588 if (!issuer_id.empty())
589 report.AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id);
590 reports_[report.id] = report;
591 return report.id;
592}
593
594std::string StatsCollector::AddCertificateReports(
595 const talk_base::SSLCertificate* cert) {
596 // Produces a chain of StatsReports representing this certificate and the rest
597 // of its chain, and adds those reports to |reports_|. The return value is
598 // the id of the leaf report. The provided cert must be non-null, so at least
599 // one report will always be provided and the returned string will never be
600 // empty.
601 ASSERT(cert != NULL);
602
603 std::string issuer_id;
604 talk_base::scoped_ptr<talk_base::SSLCertChain> chain;
605 if (cert->GetChain(chain.accept())) {
606 // This loop runs in reverse, i.e. from root to leaf, so that each
607 // certificate's issuer's report ID is known before the child certificate's
608 // report is generated. The root certificate does not have an issuer ID
609 // value.
610 for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) {
611 const talk_base::SSLCertificate& cert_i = chain->Get(i);
612 issuer_id = AddOneCertificateReport(&cert_i, issuer_id);
613 }
614 }
615 // Add the leaf certificate.
616 return AddOneCertificateReport(cert, issuer_id);
617}
618
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000619void StatsCollector::ExtractSessionInfo() {
620 // Extract information from the base session.
621 StatsReport report;
622 report.id = StatsId(StatsReport::kStatsReportTypeSession, session_->id());
623 report.type = StatsReport::kStatsReportTypeSession;
624 report.timestamp = stats_gathering_started_;
625 report.values.clear();
626 report.AddBoolean(StatsReport::kStatsValueNameInitiator,
627 session_->initiator());
628
629 reports_[report.id] = report;
630
631 cricket::SessionStats stats;
632 if (session_->GetStats(&stats)) {
633 // Store the proxy map away for use in SSRC reporting.
634 proxy_to_transport_ = stats.proxy_to_transport;
635
636 for (cricket::TransportStatsMap::iterator transport_iter
637 = stats.transport_stats.begin();
638 transport_iter != stats.transport_stats.end(); ++transport_iter) {
wu@webrtc.org4551b792013-10-09 15:37:36 +0000639 // Attempt to get a copy of the certificates from the transport and
640 // expose them in stats reports. All channels in a transport share the
641 // same local and remote certificates.
642 std::string local_cert_report_id, remote_cert_report_id;
643 cricket::Transport* transport =
644 session_->GetTransport(transport_iter->second.content_name);
645 if (transport) {
646 talk_base::scoped_ptr<talk_base::SSLIdentity> identity;
647 if (transport->GetIdentity(identity.accept()))
648 local_cert_report_id = AddCertificateReports(
649 &(identity->certificate()));
650
651 talk_base::scoped_ptr<talk_base::SSLCertificate> cert;
652 if (transport->GetRemoteCertificate(cert.accept()))
653 remote_cert_report_id = AddCertificateReports(cert.get());
654 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000655 for (cricket::TransportChannelStatsList::iterator channel_iter
656 = transport_iter->second.channel_stats.begin();
657 channel_iter != transport_iter->second.channel_stats.end();
658 ++channel_iter) {
659 StatsReport channel_report;
660 std::ostringstream ostc;
661 ostc << "Channel-" << transport_iter->second.content_name
662 << "-" << channel_iter->component;
663 channel_report.id = ostc.str();
664 channel_report.type = StatsReport::kStatsReportTypeComponent;
665 channel_report.timestamp = stats_gathering_started_;
666 channel_report.AddValue(StatsReport::kStatsValueNameComponent,
667 channel_iter->component);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000668 if (!local_cert_report_id.empty())
669 channel_report.AddValue(
670 StatsReport::kStatsValueNameLocalCertificateId,
671 local_cert_report_id);
672 if (!remote_cert_report_id.empty())
673 channel_report.AddValue(
674 StatsReport::kStatsValueNameRemoteCertificateId,
675 remote_cert_report_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000676 reports_[channel_report.id] = channel_report;
677 for (size_t i = 0;
678 i < channel_iter->connection_infos.size();
679 ++i) {
680 StatsReport report;
681 const cricket::ConnectionInfo& info
682 = channel_iter->connection_infos[i];
683 std::ostringstream ost;
684 ost << "Conn-" << transport_iter->first << "-"
685 << channel_iter->component << "-" << i;
686 report.id = ost.str();
687 report.type = StatsReport::kStatsReportTypeCandidatePair;
688 report.timestamp = stats_gathering_started_;
689 // Link from connection to its containing channel.
690 report.AddValue(StatsReport::kStatsValueNameChannelId,
691 channel_report.id);
692 report.AddValue(StatsReport::kStatsValueNameBytesSent,
693 info.sent_total_bytes);
694 report.AddValue(StatsReport::kStatsValueNameBytesReceived,
695 info.recv_total_bytes);
696 report.AddBoolean(StatsReport::kStatsValueNameWritable,
697 info.writable);
698 report.AddBoolean(StatsReport::kStatsValueNameReadable,
699 info.readable);
700 report.AddBoolean(StatsReport::kStatsValueNameActiveConnection,
701 info.best_connection);
702 report.AddValue(StatsReport::kStatsValueNameLocalAddress,
703 info.local_candidate.address().ToString());
704 report.AddValue(StatsReport::kStatsValueNameRemoteAddress,
705 info.remote_candidate.address().ToString());
wu@webrtc.org97077a32013-10-25 21:18:33 +0000706 report.AddValue(StatsReport::kStatsValueNameRtt, info.rtt);
707 report.AddValue(StatsReport::kStatsValueNameTransportType,
708 info.local_candidate.protocol());
wu@webrtc.org364f2042013-11-20 21:49:41 +0000709 report.AddValue(StatsReport::kStatsValueNameLocalCandidateType,
710 info.local_candidate.type());
711 report.AddValue(StatsReport::kStatsValueNameRemoteCandidateType,
712 info.remote_candidate.type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000713 reports_[report.id] = report;
714 }
715 }
716 }
717 }
718}
719
720void StatsCollector::ExtractVoiceInfo() {
721 if (!session_->voice_channel()) {
722 return;
723 }
724 cricket::VoiceMediaInfo voice_info;
725 if (!session_->voice_channel()->GetStats(&voice_info)) {
726 LOG(LS_ERROR) << "Failed to get voice channel stats.";
727 return;
728 }
729 std::string transport_id;
730 if (!GetTransportIdFromProxy(session_->voice_channel()->content_name(),
731 &transport_id)) {
732 LOG(LS_ERROR) << "Failed to get transport name for proxy "
733 << session_->voice_channel()->content_name();
734 return;
735 }
736 ExtractStatsFromList(voice_info.receivers, transport_id, this);
737 ExtractStatsFromList(voice_info.senders, transport_id, this);
738}
739
740void StatsCollector::ExtractVideoInfo() {
741 if (!session_->video_channel()) {
742 return;
743 }
744 cricket::VideoMediaInfo video_info;
745 if (!session_->video_channel()->GetStats(&video_info)) {
746 LOG(LS_ERROR) << "Failed to get video channel stats.";
747 return;
748 }
749 std::string transport_id;
750 if (!GetTransportIdFromProxy(session_->video_channel()->content_name(),
751 &transport_id)) {
752 LOG(LS_ERROR) << "Failed to get transport name for proxy "
753 << session_->video_channel()->content_name();
754 return;
755 }
756 ExtractStatsFromList(video_info.receivers, transport_id, this);
757 ExtractStatsFromList(video_info.senders, transport_id, this);
758 if (video_info.bw_estimations.size() != 1) {
759 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
760 } else {
761 StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId];
762 ExtractStats(
763 video_info.bw_estimations[0], stats_gathering_started_, report);
764 }
765}
766
767double StatsCollector::GetTimeNow() {
768 return timing_.WallTimeNow() * talk_base::kNumMillisecsPerSec;
769}
770
771bool StatsCollector::GetTransportIdFromProxy(const std::string& proxy,
772 std::string* transport) {
773 // TODO(hta): Remove handling of empty proxy name once tests do not use it.
774 if (proxy.empty()) {
775 transport->clear();
776 return true;
777 }
778 if (proxy_to_transport_.find(proxy) == proxy_to_transport_.end()) {
779 LOG(LS_ERROR) << "No transport ID mapping for " << proxy;
780 return false;
781 }
782 std::ostringstream ost;
783 // Component 1 is always used for RTP.
784 ost << "Channel-" << proxy_to_transport_[proxy] << "-1";
785 *transport = ost.str();
786 return true;
787}
788
wu@webrtc.org97077a32013-10-25 21:18:33 +0000789StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
790 const std::string& id) {
791 std::string statsid = StatsId(type, id);
792 StatsReport* report = NULL;
793 std::map<std::string, StatsReport>::iterator it = reports_.find(statsid);
794 if (it == reports_.end()) {
795 report = &reports_[statsid]; // Create new element.
796 report->id = statsid;
797 report->type = type;
798 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000799 report = &(it->second);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000800 }
801 return report;
802}
803
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000804} // namespace webrtc