blob: 4d5b324188ff301b3d56aa798b9b812d74b264a7 [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.orgb9a088b2014-02-13 23:18:49 +000081const char StatsReport::kStatsValueNameExpandRate[] = "googExpandRate";
wu@webrtc.org4551b792013-10-09 15:37:36 +000082const char StatsReport::kStatsValueNameFingerprint[] = "googFingerprint";
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +000083const char StatsReport::kStatsValueNameFingerprintAlgorithm[] =
84 "googFingerprintAlgorithm";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000085const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived";
86const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent";
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +000087const char StatsReport::kStatsValueNameFrameHeightInput[] =
88 "googFrameHeightInput";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000089const char StatsReport::kStatsValueNameFrameHeightReceived[] =
90 "googFrameHeightReceived";
91const char StatsReport::kStatsValueNameFrameHeightSent[] =
92 "googFrameHeightSent";
93const char StatsReport::kStatsValueNameFrameRateReceived[] =
94 "googFrameRateReceived";
95const char StatsReport::kStatsValueNameFrameRateDecoded[] =
96 "googFrameRateDecoded";
97const char StatsReport::kStatsValueNameFrameRateOutput[] =
98 "googFrameRateOutput";
wu@webrtc.org97077a32013-10-25 21:18:33 +000099const char StatsReport::kStatsValueNameDecodeMs[] = "googDecodeMs";
100const char StatsReport::kStatsValueNameMaxDecodeMs[] = "googMaxDecodeMs";
101const char StatsReport::kStatsValueNameCurrentDelayMs[] = "googCurrentDelayMs";
102const char StatsReport::kStatsValueNameTargetDelayMs[] = "googTargetDelayMs";
103const char StatsReport::kStatsValueNameJitterBufferMs[] = "googJitterBufferMs";
104const char StatsReport::kStatsValueNameMinPlayoutDelayMs[] =
105 "googMinPlayoutDelayMs";
106const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs";
107
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000108const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput";
109const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent";
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000110const char StatsReport::kStatsValueNameFrameWidthInput[] =
111 "googFrameWidthInput";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000112const char StatsReport::kStatsValueNameFrameWidthReceived[] =
113 "googFrameWidthReceived";
114const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent";
115const char StatsReport::kStatsValueNameInitiator[] = "googInitiator";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000116const char StatsReport::kStatsValueNameIssuerId[] = "googIssuerId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000117const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived";
118const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress";
wu@webrtc.org364f2042013-11-20 21:49:41 +0000119const char StatsReport::kStatsValueNameLocalCandidateType[] =
120 "googLocalCandidateType";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000121const char StatsReport::kStatsValueNameLocalCertificateId[] =
122 "googLocalCertificateId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000123const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived";
124const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent";
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000125const char StatsReport::kStatsValueNamePlisReceived[] = "googPlisReceived";
126const char StatsReport::kStatsValueNamePlisSent[] = "googPlisSent";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000127const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived";
128const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent";
129const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost";
130const char StatsReport::kStatsValueNameReadable[] = "googReadable";
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000131const char StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug[] =
132 "googReceivedPacketGroupArrivalTimeDebug";
133const char StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug[] =
134 "googReceivedPacketGroupPropagationDeltaDebug";
135const char
136StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug[] =
137 "googReceivedPacketGroupPropagationDeltaSumDebug";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000138const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress";
wu@webrtc.org364f2042013-11-20 21:49:41 +0000139const char StatsReport::kStatsValueNameRemoteCandidateType[] =
140 "googRemoteCandidateType";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000141const char StatsReport::kStatsValueNameRemoteCertificateId[] =
142 "googRemoteCertificateId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000143const char StatsReport::kStatsValueNameRetransmitBitrate[] =
144 "googRetransmitBitrate";
145const char StatsReport::kStatsValueNameRtt[] = "googRtt";
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000146const char StatsReport::kStatsValueNameSsrc[] = "ssrc";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000147const char StatsReport::kStatsValueNameTargetEncBitrate[] =
148 "googTargetEncBitrate";
149const char StatsReport::kStatsValueNameTransmitBitrate[] =
150 "googTransmitBitrate";
151const char StatsReport::kStatsValueNameTransportId[] = "transportId";
152const char StatsReport::kStatsValueNameTransportType[] = "googTransportType";
153const char StatsReport::kStatsValueNameTrackId[] = "googTrackId";
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000154const char StatsReport::kStatsValueNameTypingNoiseState[] =
155 "googTypingNoiseState";
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +0000156const char StatsReport::kStatsValueNameViewLimitedResolution[] =
157 "googViewLimitedResolution";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000158const char StatsReport::kStatsValueNameWritable[] = "googWritable";
159
160const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession";
161const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe";
wu@webrtc.org97077a32013-10-25 21:18:33 +0000162const char StatsReport::kStatsReportTypeRemoteSsrc[] = "remoteSsrc";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000163const char StatsReport::kStatsReportTypeSsrc[] = "ssrc";
164const char StatsReport::kStatsReportTypeTrack[] = "googTrack";
165const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate";
166const char StatsReport::kStatsReportTypeTransport[] = "googTransport";
167const char StatsReport::kStatsReportTypeComponent[] = "googComponent";
168const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000169const char StatsReport::kStatsReportTypeCertificate[] = "googCertificate";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000170
171const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo";
172
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000173
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000174// Implementations of functions in statstypes.h
175void StatsReport::AddValue(const std::string& name, const std::string& value) {
176 Value temp;
177 temp.name = name;
178 temp.value = value;
179 values.push_back(temp);
180}
181
182void StatsReport::AddValue(const std::string& name, int64 value) {
183 AddValue(name, talk_base::ToString<int64>(value));
184}
185
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000186template <typename T>
187void StatsReport::AddValue(const std::string& name,
188 const std::vector<T>& value) {
189 std::ostringstream oss;
190 oss << "[";
191 for (size_t i = 0; i < value.size(); ++i) {
192 oss << talk_base::ToString<T>(value[i]);
193 if (i != value.size() - 1)
194 oss << ", ";
195 }
196 oss << "]";
197 AddValue(name, oss.str());
198}
199
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000200void StatsReport::AddBoolean(const std::string& name, bool value) {
201 AddValue(name, value ? "true" : "false");
202}
203
204namespace {
205typedef std::map<std::string, StatsReport> StatsMap;
206
207std::string StatsId(const std::string& type, const std::string& id) {
208 return type + "_" + id;
209}
210
211bool ExtractValueFromReport(
212 const StatsReport& report,
213 const std::string& name,
214 std::string* value) {
215 StatsReport::Values::const_iterator it = report.values.begin();
216 for (; it != report.values.end(); ++it) {
217 if (it->name == name) {
218 *value = it->value;
219 return true;
220 }
221 }
222 return false;
223}
224
225template <class TrackVector>
226void CreateTrackReports(const TrackVector& tracks, StatsMap* reports) {
227 for (size_t j = 0; j < tracks.size(); ++j) {
228 webrtc::MediaStreamTrackInterface* track = tracks[j];
229 // Adds an empty track report.
230 StatsReport report;
231 report.type = StatsReport::kStatsReportTypeTrack;
232 report.id = StatsId(StatsReport::kStatsReportTypeTrack, track->id());
233 report.AddValue(StatsReport::kStatsValueNameTrackId,
234 track->id());
235 (*reports)[report.id] = report;
236 }
237}
238
239void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
240 report->AddValue(StatsReport::kStatsValueNameAudioOutputLevel,
241 info.audio_level);
242 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
243 info.bytes_rcvd);
244 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
245 info.jitter_ms);
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000246 report->AddValue(StatsReport::kStatsValueNameExpandRate,
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000247 talk_base::ToString<float>(info.expand_rate));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000248 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
249 info.packets_rcvd);
250 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
251 info.packets_lost);
252}
253
254void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
255 report->AddValue(StatsReport::kStatsValueNameAudioInputLevel,
256 info.audio_level);
257 report->AddValue(StatsReport::kStatsValueNameBytesSent,
258 info.bytes_sent);
259 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
260 info.packets_sent);
261 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
262 info.jitter_ms);
263 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
264 report->AddValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
265 talk_base::ToString<float>(info.aec_quality_min));
266 report->AddValue(StatsReport::kStatsValueNameEchoDelayMedian,
267 info.echo_delay_median_ms);
268 report->AddValue(StatsReport::kStatsValueNameEchoDelayStdDev,
269 info.echo_delay_std_ms);
270 report->AddValue(StatsReport::kStatsValueNameEchoReturnLoss,
271 info.echo_return_loss);
272 report->AddValue(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
273 info.echo_return_loss_enhancement);
274 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000275 report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
276 info.typing_noise_detected);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000277}
278
279void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
280 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
281 info.bytes_rcvd);
282 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
283 info.packets_rcvd);
284 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
285 info.packets_lost);
286
287 report->AddValue(StatsReport::kStatsValueNameFirsSent,
288 info.firs_sent);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000289 report->AddValue(StatsReport::kStatsValueNamePlisSent,
290 info.plis_sent);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000291 report->AddValue(StatsReport::kStatsValueNameNacksSent,
292 info.nacks_sent);
293 report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived,
294 info.frame_width);
295 report->AddValue(StatsReport::kStatsValueNameFrameHeightReceived,
296 info.frame_height);
297 report->AddValue(StatsReport::kStatsValueNameFrameRateReceived,
298 info.framerate_rcvd);
299 report->AddValue(StatsReport::kStatsValueNameFrameRateDecoded,
300 info.framerate_decoded);
301 report->AddValue(StatsReport::kStatsValueNameFrameRateOutput,
302 info.framerate_output);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000303
304 report->AddValue(StatsReport::kStatsValueNameDecodeMs,
305 info.decode_ms);
306 report->AddValue(StatsReport::kStatsValueNameMaxDecodeMs,
307 info.max_decode_ms);
308 report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs,
309 info.current_delay_ms);
310 report->AddValue(StatsReport::kStatsValueNameTargetDelayMs,
311 info.target_delay_ms);
312 report->AddValue(StatsReport::kStatsValueNameJitterBufferMs,
313 info.jitter_buffer_ms);
314 report->AddValue(StatsReport::kStatsValueNameMinPlayoutDelayMs,
315 info.min_playout_delay_ms);
316 report->AddValue(StatsReport::kStatsValueNameRenderDelayMs,
317 info.render_delay_ms);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000318}
319
320void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
321 report->AddValue(StatsReport::kStatsValueNameBytesSent,
322 info.bytes_sent);
323 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
324 info.packets_sent);
325
326 report->AddValue(StatsReport::kStatsValueNameFirsReceived,
327 info.firs_rcvd);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000328 report->AddValue(StatsReport::kStatsValueNamePlisReceived,
329 info.plis_rcvd);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000330 report->AddValue(StatsReport::kStatsValueNameNacksReceived,
331 info.nacks_rcvd);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000332 report->AddValue(StatsReport::kStatsValueNameFrameWidthInput,
333 info.input_frame_width);
334 report->AddValue(StatsReport::kStatsValueNameFrameHeightInput,
335 info.input_frame_height);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000336 report->AddValue(StatsReport::kStatsValueNameFrameWidthSent,
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000337 info.send_frame_width);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000338 report->AddValue(StatsReport::kStatsValueNameFrameHeightSent,
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000339 info.send_frame_height);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000340 report->AddValue(StatsReport::kStatsValueNameFrameRateInput,
341 info.framerate_input);
342 report->AddValue(StatsReport::kStatsValueNameFrameRateSent,
343 info.framerate_sent);
344 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
345 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +0000346 report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution,
347 (info.adapt_reason & 0x1) > 0);
348 report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution,
349 (info.adapt_reason & 0x2) > 0);
350 report->AddBoolean(StatsReport::kStatsValueNameViewLimitedResolution,
351 (info.adapt_reason & 0x4) > 0);
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000352 report->AddValue(StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms);
353 report->AddValue(StatsReport::kStatsValueNameCaptureJitterMs,
354 info.capture_jitter_ms);
wu@webrtc.org9caf2762013-12-11 18:25:07 +0000355 report->AddValue(StatsReport::kStatsValueNameCaptureQueueDelayMsPerS,
356 info.capture_queue_delay_ms_per_s);
357 report->AddValue(StatsReport::kStatsValueNameEncodeUsagePercent,
358 info.encode_usage_percent);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000359}
360
361void ExtractStats(const cricket::BandwidthEstimationInfo& info,
362 double stats_gathering_started,
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000363 PeerConnectionInterface::StatsOutputLevel level,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000364 StatsReport* report) {
365 report->id = StatsReport::kStatsReportVideoBweId;
366 report->type = StatsReport::kStatsReportTypeBwe;
367
368 // Clear out stats from previous GatherStats calls if any.
369 if (report->timestamp != stats_gathering_started) {
370 report->values.clear();
371 report->timestamp = stats_gathering_started;
372 }
373
374 report->AddValue(StatsReport::kStatsValueNameAvailableSendBandwidth,
375 info.available_send_bandwidth);
376 report->AddValue(StatsReport::kStatsValueNameAvailableReceiveBandwidth,
377 info.available_recv_bandwidth);
378 report->AddValue(StatsReport::kStatsValueNameTargetEncBitrate,
379 info.target_enc_bitrate);
380 report->AddValue(StatsReport::kStatsValueNameActualEncBitrate,
381 info.actual_enc_bitrate);
382 report->AddValue(StatsReport::kStatsValueNameRetransmitBitrate,
383 info.retransmit_bitrate);
384 report->AddValue(StatsReport::kStatsValueNameTransmitBitrate,
385 info.transmit_bitrate);
386 report->AddValue(StatsReport::kStatsValueNameBucketDelay,
387 info.bucket_delay);
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000388 if (level >= PeerConnectionInterface::kStatsOutputLevelDebug) {
389 report->AddValue(
390 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug,
391 info.total_received_propagation_delta_ms);
392 if (info.recent_received_propagation_delta_ms.size() > 0) {
393 report->AddValue(
394 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug,
395 info.recent_received_propagation_delta_ms);
396 report->AddValue(
397 StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug,
398 info.recent_received_packet_group_arrival_time_ms);
399 }
400 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000401}
402
wu@webrtc.org97077a32013-10-25 21:18:33 +0000403void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
404 StatsReport* report) {
405 report->timestamp = info.remote_stats[0].timestamp;
406 // TODO(hta): Extract some stats here.
407}
408
409void ExtractRemoteStats(const cricket::MediaReceiverInfo& info,
410 StatsReport* report) {
411 report->timestamp = info.remote_stats[0].timestamp;
412 // TODO(hta): Extract some stats here.
413}
414
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000415// Template to extract stats from a data vector.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000416// In order to use the template, the functions that are called from it,
417// ExtractStats and ExtractRemoteStats, must be defined and overloaded
418// for each type.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000419template<typename T>
420void ExtractStatsFromList(const std::vector<T>& data,
421 const std::string& transport_id,
422 StatsCollector* collector) {
423 typename std::vector<T>::const_iterator it = data.begin();
424 for (; it != data.end(); ++it) {
425 std::string id;
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000426 uint32 ssrc = it->ssrc();
wu@webrtc.org97077a32013-10-25 21:18:33 +0000427 // Each object can result in 2 objects, a local and a remote object.
428 // TODO(hta): Handle the case of multiple SSRCs per object.
429 StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000430 if (!report) {
431 continue;
432 }
433 ExtractStats(*it, report);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000434 if (it->remote_stats.size() > 0) {
435 report = collector->PrepareRemoteReport(ssrc, transport_id);
436 if (!report) {
437 continue;
438 }
439 ExtractRemoteStats(*it, report);
440 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000441 }
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000442}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000443
444} // namespace
445
446StatsCollector::StatsCollector()
447 : session_(NULL), stats_gathering_started_(0) {
448}
449
450// Adds a MediaStream with tracks that can be used as a |selector| in a call
451// to GetStats.
452void StatsCollector::AddStream(MediaStreamInterface* stream) {
453 ASSERT(stream != NULL);
454
455 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(),
456 &reports_);
457 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(),
458 &reports_);
459}
460
461bool StatsCollector::GetStats(MediaStreamTrackInterface* track,
462 StatsReports* reports) {
463 ASSERT(reports != NULL);
464 reports->clear();
465
466 StatsMap::iterator it;
467 if (!track) {
468 for (it = reports_.begin(); it != reports_.end(); ++it) {
469 reports->push_back(it->second);
470 }
471 return true;
472 }
473
474 it = reports_.find(StatsId(StatsReport::kStatsReportTypeSession,
475 session_->id()));
476 if (it != reports_.end()) {
477 reports->push_back(it->second);
478 }
479
480 it = reports_.find(StatsId(StatsReport::kStatsReportTypeTrack, track->id()));
481
482 if (it == reports_.end()) {
483 LOG(LS_WARNING) << "No StatsReport is available for "<< track->id();
484 return false;
485 }
486
487 reports->push_back(it->second);
488
489 std::string track_id;
490 for (it = reports_.begin(); it != reports_.end(); ++it) {
491 if (it->second.type != StatsReport::kStatsReportTypeSsrc) {
492 continue;
493 }
494 if (ExtractValueFromReport(it->second,
495 StatsReport::kStatsValueNameTrackId,
496 &track_id)) {
497 if (track_id == track->id()) {
498 reports->push_back(it->second);
499 }
500 }
501 }
502
503 return true;
504}
505
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000506void
507StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000508 double time_now = GetTimeNow();
509 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
510 // ms apart will be ignored.
511 const double kMinGatherStatsPeriod = 50;
512 if (stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
513 return;
514 }
515 stats_gathering_started_ = time_now;
516
517 if (session_) {
518 ExtractSessionInfo();
519 ExtractVoiceInfo();
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000520 ExtractVideoInfo(level);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000521 }
522}
523
wu@webrtc.org97077a32013-10-25 21:18:33 +0000524StatsReport* StatsCollector::PrepareLocalReport(
525 uint32 ssrc,
526 const std::string& transport_id) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000527 std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
528 StatsMap::iterator it = reports_.find(StatsId(
529 StatsReport::kStatsReportTypeSsrc, ssrc_id));
530
531 std::string track_id;
532 if (it == reports_.end()) {
533 if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000534 LOG(LS_WARNING) << "The SSRC " << ssrc
535 << " is not associated with a track";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000536 return NULL;
537 }
538 } else {
539 // Keeps the old track id since we want to report the stats for inactive
540 // tracks.
541 ExtractValueFromReport(it->second,
542 StatsReport::kStatsValueNameTrackId,
543 &track_id);
544 }
545
wu@webrtc.org97077a32013-10-25 21:18:33 +0000546 StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc,
547 ssrc_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000548
549 // Clear out stats from previous GatherStats calls if any.
550 if (report->timestamp != stats_gathering_started_) {
551 report->values.clear();
552 report->timestamp = stats_gathering_started_;
553 }
554
555 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
556 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
557 // Add the mapping of SSRC to transport.
558 report->AddValue(StatsReport::kStatsValueNameTransportId,
559 transport_id);
560 return report;
561}
562
wu@webrtc.org97077a32013-10-25 21:18:33 +0000563StatsReport* StatsCollector::PrepareRemoteReport(
564 uint32 ssrc,
565 const std::string& transport_id) {
566 std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
567 StatsMap::iterator it = reports_.find(StatsId(
568 StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id));
569
570 std::string track_id;
571 if (it == reports_.end()) {
572 if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
573 LOG(LS_WARNING) << "The SSRC " << ssrc
574 << " is not associated with a track";
575 return NULL;
576 }
577 } else {
578 // Keeps the old track id since we want to report the stats for inactive
579 // tracks.
580 ExtractValueFromReport(it->second,
581 StatsReport::kStatsValueNameTrackId,
582 &track_id);
583 }
584
585 StatsReport* report = GetOrCreateReport(
586 StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id);
587
588 // Clear out stats from previous GatherStats calls if any.
589 // The timestamp will be added later. Zero it for debugging.
590 report->values.clear();
591 report->timestamp = 0;
592
593 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
594 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
595 // Add the mapping of SSRC to transport.
596 report->AddValue(StatsReport::kStatsValueNameTransportId,
597 transport_id);
598 return report;
599}
600
wu@webrtc.org4551b792013-10-09 15:37:36 +0000601std::string StatsCollector::AddOneCertificateReport(
602 const talk_base::SSLCertificate* cert, const std::string& issuer_id) {
603 // TODO(bemasc): Move this computation to a helper class that caches these
604 // values to reduce CPU use in GetStats. This will require adding a fast
605 // SSLCertificate::Equals() method to detect certificate changes.
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000606
607 std::string digest_algorithm;
608 if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm))
609 return std::string();
610
wu@webrtc.org4551b792013-10-09 15:37:36 +0000611 talk_base::scoped_ptr<talk_base::SSLFingerprint> ssl_fingerprint(
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000612 talk_base::SSLFingerprint::Create(digest_algorithm, cert));
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000613
614 // SSLFingerprint::Create can fail if the algorithm returned by
615 // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the
616 // implementation of SSLCertificate::ComputeDigest. This currently happens
617 // with MD5- and SHA-224-signed certificates when linked to libNSS.
618 if (!ssl_fingerprint)
619 return std::string();
620
wu@webrtc.org4551b792013-10-09 15:37:36 +0000621 std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
622
623 talk_base::Buffer der_buffer;
624 cert->ToDER(&der_buffer);
625 std::string der_base64;
626 talk_base::Base64::EncodeFromArray(
627 der_buffer.data(), der_buffer.length(), &der_base64);
628
629 StatsReport report;
630 report.type = StatsReport::kStatsReportTypeCertificate;
631 report.id = StatsId(report.type, fingerprint);
632 report.timestamp = stats_gathering_started_;
633 report.AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint);
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000634 report.AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm,
635 digest_algorithm);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000636 report.AddValue(StatsReport::kStatsValueNameDer, der_base64);
637 if (!issuer_id.empty())
638 report.AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id);
639 reports_[report.id] = report;
640 return report.id;
641}
642
643std::string StatsCollector::AddCertificateReports(
644 const talk_base::SSLCertificate* cert) {
645 // Produces a chain of StatsReports representing this certificate and the rest
646 // of its chain, and adds those reports to |reports_|. The return value is
647 // the id of the leaf report. The provided cert must be non-null, so at least
648 // one report will always be provided and the returned string will never be
649 // empty.
650 ASSERT(cert != NULL);
651
652 std::string issuer_id;
653 talk_base::scoped_ptr<talk_base::SSLCertChain> chain;
654 if (cert->GetChain(chain.accept())) {
655 // This loop runs in reverse, i.e. from root to leaf, so that each
656 // certificate's issuer's report ID is known before the child certificate's
657 // report is generated. The root certificate does not have an issuer ID
658 // value.
659 for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) {
660 const talk_base::SSLCertificate& cert_i = chain->Get(i);
661 issuer_id = AddOneCertificateReport(&cert_i, issuer_id);
662 }
663 }
664 // Add the leaf certificate.
665 return AddOneCertificateReport(cert, issuer_id);
666}
667
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000668void StatsCollector::ExtractSessionInfo() {
669 // Extract information from the base session.
670 StatsReport report;
671 report.id = StatsId(StatsReport::kStatsReportTypeSession, session_->id());
672 report.type = StatsReport::kStatsReportTypeSession;
673 report.timestamp = stats_gathering_started_;
674 report.values.clear();
675 report.AddBoolean(StatsReport::kStatsValueNameInitiator,
676 session_->initiator());
677
678 reports_[report.id] = report;
679
680 cricket::SessionStats stats;
681 if (session_->GetStats(&stats)) {
682 // Store the proxy map away for use in SSRC reporting.
683 proxy_to_transport_ = stats.proxy_to_transport;
684
685 for (cricket::TransportStatsMap::iterator transport_iter
686 = stats.transport_stats.begin();
687 transport_iter != stats.transport_stats.end(); ++transport_iter) {
wu@webrtc.org4551b792013-10-09 15:37:36 +0000688 // Attempt to get a copy of the certificates from the transport and
689 // expose them in stats reports. All channels in a transport share the
690 // same local and remote certificates.
691 std::string local_cert_report_id, remote_cert_report_id;
692 cricket::Transport* transport =
693 session_->GetTransport(transport_iter->second.content_name);
694 if (transport) {
695 talk_base::scoped_ptr<talk_base::SSLIdentity> identity;
696 if (transport->GetIdentity(identity.accept()))
697 local_cert_report_id = AddCertificateReports(
698 &(identity->certificate()));
699
700 talk_base::scoped_ptr<talk_base::SSLCertificate> cert;
701 if (transport->GetRemoteCertificate(cert.accept()))
702 remote_cert_report_id = AddCertificateReports(cert.get());
703 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000704 for (cricket::TransportChannelStatsList::iterator channel_iter
705 = transport_iter->second.channel_stats.begin();
706 channel_iter != transport_iter->second.channel_stats.end();
707 ++channel_iter) {
708 StatsReport channel_report;
709 std::ostringstream ostc;
710 ostc << "Channel-" << transport_iter->second.content_name
711 << "-" << channel_iter->component;
712 channel_report.id = ostc.str();
713 channel_report.type = StatsReport::kStatsReportTypeComponent;
714 channel_report.timestamp = stats_gathering_started_;
715 channel_report.AddValue(StatsReport::kStatsValueNameComponent,
716 channel_iter->component);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000717 if (!local_cert_report_id.empty())
718 channel_report.AddValue(
719 StatsReport::kStatsValueNameLocalCertificateId,
720 local_cert_report_id);
721 if (!remote_cert_report_id.empty())
722 channel_report.AddValue(
723 StatsReport::kStatsValueNameRemoteCertificateId,
724 remote_cert_report_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000725 reports_[channel_report.id] = channel_report;
726 for (size_t i = 0;
727 i < channel_iter->connection_infos.size();
728 ++i) {
729 StatsReport report;
730 const cricket::ConnectionInfo& info
731 = channel_iter->connection_infos[i];
732 std::ostringstream ost;
733 ost << "Conn-" << transport_iter->first << "-"
734 << channel_iter->component << "-" << i;
735 report.id = ost.str();
736 report.type = StatsReport::kStatsReportTypeCandidatePair;
737 report.timestamp = stats_gathering_started_;
738 // Link from connection to its containing channel.
739 report.AddValue(StatsReport::kStatsValueNameChannelId,
740 channel_report.id);
741 report.AddValue(StatsReport::kStatsValueNameBytesSent,
742 info.sent_total_bytes);
743 report.AddValue(StatsReport::kStatsValueNameBytesReceived,
744 info.recv_total_bytes);
745 report.AddBoolean(StatsReport::kStatsValueNameWritable,
746 info.writable);
747 report.AddBoolean(StatsReport::kStatsValueNameReadable,
748 info.readable);
749 report.AddBoolean(StatsReport::kStatsValueNameActiveConnection,
750 info.best_connection);
751 report.AddValue(StatsReport::kStatsValueNameLocalAddress,
752 info.local_candidate.address().ToString());
753 report.AddValue(StatsReport::kStatsValueNameRemoteAddress,
754 info.remote_candidate.address().ToString());
wu@webrtc.org97077a32013-10-25 21:18:33 +0000755 report.AddValue(StatsReport::kStatsValueNameRtt, info.rtt);
756 report.AddValue(StatsReport::kStatsValueNameTransportType,
757 info.local_candidate.protocol());
wu@webrtc.org364f2042013-11-20 21:49:41 +0000758 report.AddValue(StatsReport::kStatsValueNameLocalCandidateType,
759 info.local_candidate.type());
760 report.AddValue(StatsReport::kStatsValueNameRemoteCandidateType,
761 info.remote_candidate.type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000762 reports_[report.id] = report;
763 }
764 }
765 }
766 }
767}
768
769void StatsCollector::ExtractVoiceInfo() {
770 if (!session_->voice_channel()) {
771 return;
772 }
773 cricket::VoiceMediaInfo voice_info;
774 if (!session_->voice_channel()->GetStats(&voice_info)) {
775 LOG(LS_ERROR) << "Failed to get voice channel stats.";
776 return;
777 }
778 std::string transport_id;
779 if (!GetTransportIdFromProxy(session_->voice_channel()->content_name(),
780 &transport_id)) {
781 LOG(LS_ERROR) << "Failed to get transport name for proxy "
782 << session_->voice_channel()->content_name();
783 return;
784 }
785 ExtractStatsFromList(voice_info.receivers, transport_id, this);
786 ExtractStatsFromList(voice_info.senders, transport_id, this);
787}
788
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000789void StatsCollector::ExtractVideoInfo(
790 PeerConnectionInterface::StatsOutputLevel level) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000791 if (!session_->video_channel()) {
792 return;
793 }
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000794 cricket::StatsOptions options;
795 options.include_received_propagation_stats =
796 (level >= PeerConnectionInterface::kStatsOutputLevelDebug) ?
797 true : false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000798 cricket::VideoMediaInfo video_info;
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000799 if (!session_->video_channel()->GetStats(options, &video_info)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000800 LOG(LS_ERROR) << "Failed to get video channel stats.";
801 return;
802 }
803 std::string transport_id;
804 if (!GetTransportIdFromProxy(session_->video_channel()->content_name(),
805 &transport_id)) {
806 LOG(LS_ERROR) << "Failed to get transport name for proxy "
807 << session_->video_channel()->content_name();
808 return;
809 }
810 ExtractStatsFromList(video_info.receivers, transport_id, this);
811 ExtractStatsFromList(video_info.senders, transport_id, this);
812 if (video_info.bw_estimations.size() != 1) {
813 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
814 } else {
815 StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId];
816 ExtractStats(
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000817 video_info.bw_estimations[0], stats_gathering_started_, level, report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000818 }
819}
820
821double StatsCollector::GetTimeNow() {
822 return timing_.WallTimeNow() * talk_base::kNumMillisecsPerSec;
823}
824
825bool StatsCollector::GetTransportIdFromProxy(const std::string& proxy,
826 std::string* transport) {
827 // TODO(hta): Remove handling of empty proxy name once tests do not use it.
828 if (proxy.empty()) {
829 transport->clear();
830 return true;
831 }
832 if (proxy_to_transport_.find(proxy) == proxy_to_transport_.end()) {
833 LOG(LS_ERROR) << "No transport ID mapping for " << proxy;
834 return false;
835 }
836 std::ostringstream ost;
837 // Component 1 is always used for RTP.
838 ost << "Channel-" << proxy_to_transport_[proxy] << "-1";
839 *transport = ost.str();
840 return true;
841}
842
wu@webrtc.org97077a32013-10-25 21:18:33 +0000843StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
844 const std::string& id) {
845 std::string statsid = StatsId(type, id);
846 StatsReport* report = NULL;
847 std::map<std::string, StatsReport>::iterator it = reports_.find(statsid);
848 if (it == reports_.end()) {
849 report = &reports_[statsid]; // Create new element.
850 report->id = statsid;
851 report->type = type;
852 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000853 report = &(it->second);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000854 }
855 return report;
856}
857
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000858} // namespace webrtc