blob: b80da2303640060ad14be63abf935af56f295fad [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"
tommi@webrtc.orgecb87232014-07-08 12:48:29 +000035#include "talk/base/timing.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000036#include "talk/session/media/channel.h"
37
38namespace webrtc {
39
40// The items below are in alphabetical order.
41const char StatsReport::kStatsValueNameActiveConnection[] =
42 "googActiveConnection";
43const char StatsReport::kStatsValueNameActualEncBitrate[] =
44 "googActualEncBitrate";
45const char StatsReport::kStatsValueNameAudioOutputLevel[] = "audioOutputLevel";
46const char StatsReport::kStatsValueNameAudioInputLevel[] = "audioInputLevel";
47const char StatsReport::kStatsValueNameAvailableReceiveBandwidth[] =
48 "googAvailableReceiveBandwidth";
49const char StatsReport::kStatsValueNameAvailableSendBandwidth[] =
50 "googAvailableSendBandwidth";
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +000051const char StatsReport::kStatsValueNameAvgEncodeMs[] = "googAvgEncodeMs";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000052const char StatsReport::kStatsValueNameBucketDelay[] = "googBucketDelay";
53const char StatsReport::kStatsValueNameBytesReceived[] = "bytesReceived";
54const char StatsReport::kStatsValueNameBytesSent[] = "bytesSent";
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +000055const char StatsReport::kStatsValueNameBandwidthLimitedResolution[] =
56 "googBandwidthLimitedResolution";
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +000057const char StatsReport::kStatsValueNameCaptureJitterMs[] =
58 "googCaptureJitterMs";
wu@webrtc.org9caf2762013-12-11 18:25:07 +000059const char StatsReport::kStatsValueNameCaptureQueueDelayMsPerS[] =
60 "googCaptureQueueDelayMsPerS";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000061const char StatsReport::kStatsValueNameChannelId[] = "googChannelId";
62const char StatsReport::kStatsValueNameCodecName[] = "googCodecName";
63const char StatsReport::kStatsValueNameComponent[] = "googComponent";
64const char StatsReport::kStatsValueNameContentName[] = "googContentName";
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +000065const char StatsReport::kStatsValueNameCpuLimitedResolution[] =
66 "googCpuLimitedResolution";
buildbot@webrtc.org3e01e0b2014-05-13 17:54:10 +000067const char StatsReport::kStatsValueNameDecodingCTSG[] =
68 "googDecodingCTSG";
69const char StatsReport::kStatsValueNameDecodingCTN[] =
70 "googDecodingCTN";
71const char StatsReport::kStatsValueNameDecodingNormal[] =
72 "googDecodingNormal";
73const char StatsReport::kStatsValueNameDecodingPLC[] =
74 "googDecodingPLC";
75const char StatsReport::kStatsValueNameDecodingCNG[] =
76 "googDecodingCNG";
77const char StatsReport::kStatsValueNameDecodingPLCCNG[] =
78 "googDecodingPLCCNG";
wu@webrtc.org4551b792013-10-09 15:37:36 +000079const char StatsReport::kStatsValueNameDer[] = "googDerBase64";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000080// Echo metrics from the audio processing module.
81const char StatsReport::kStatsValueNameEchoCancellationQualityMin[] =
82 "googEchoCancellationQualityMin";
83const char StatsReport::kStatsValueNameEchoDelayMedian[] =
84 "googEchoCancellationEchoDelayMedian";
85const char StatsReport::kStatsValueNameEchoDelayStdDev[] =
86 "googEchoCancellationEchoDelayStdDev";
87const char StatsReport::kStatsValueNameEchoReturnLoss[] =
88 "googEchoCancellationReturnLoss";
89const char StatsReport::kStatsValueNameEchoReturnLossEnhancement[] =
90 "googEchoCancellationReturnLossEnhancement";
91
buildbot@webrtc.orgc800c1c2014-06-13 07:56:17 +000092const char StatsReport::kStatsValueNameEncodeRelStdDev[] =
93 "googEncodeRelStdDev";
wu@webrtc.org9caf2762013-12-11 18:25:07 +000094const char StatsReport::kStatsValueNameEncodeUsagePercent[] =
95 "googEncodeUsagePercent";
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +000096const char StatsReport::kStatsValueNameExpandRate[] = "googExpandRate";
wu@webrtc.org4551b792013-10-09 15:37:36 +000097const char StatsReport::kStatsValueNameFingerprint[] = "googFingerprint";
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +000098const char StatsReport::kStatsValueNameFingerprintAlgorithm[] =
99 "googFingerprintAlgorithm";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000100const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived";
101const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent";
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000102const char StatsReport::kStatsValueNameFrameHeightInput[] =
103 "googFrameHeightInput";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000104const char StatsReport::kStatsValueNameFrameHeightReceived[] =
105 "googFrameHeightReceived";
106const char StatsReport::kStatsValueNameFrameHeightSent[] =
107 "googFrameHeightSent";
108const char StatsReport::kStatsValueNameFrameRateReceived[] =
109 "googFrameRateReceived";
110const char StatsReport::kStatsValueNameFrameRateDecoded[] =
111 "googFrameRateDecoded";
112const char StatsReport::kStatsValueNameFrameRateOutput[] =
113 "googFrameRateOutput";
wu@webrtc.org97077a32013-10-25 21:18:33 +0000114const char StatsReport::kStatsValueNameDecodeMs[] = "googDecodeMs";
115const char StatsReport::kStatsValueNameMaxDecodeMs[] = "googMaxDecodeMs";
116const char StatsReport::kStatsValueNameCurrentDelayMs[] = "googCurrentDelayMs";
117const char StatsReport::kStatsValueNameTargetDelayMs[] = "googTargetDelayMs";
118const char StatsReport::kStatsValueNameJitterBufferMs[] = "googJitterBufferMs";
119const char StatsReport::kStatsValueNameMinPlayoutDelayMs[] =
120 "googMinPlayoutDelayMs";
121const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs";
122
buildbot@webrtc.org0581f0b2014-05-06 21:36:31 +0000123const char StatsReport::kStatsValueNameCaptureStartNtpTimeMs[] =
124 "googCaptureStartNtpTimeMs";
125
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000126const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput";
127const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent";
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000128const char StatsReport::kStatsValueNameFrameWidthInput[] =
129 "googFrameWidthInput";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000130const char StatsReport::kStatsValueNameFrameWidthReceived[] =
131 "googFrameWidthReceived";
132const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent";
133const char StatsReport::kStatsValueNameInitiator[] = "googInitiator";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000134const char StatsReport::kStatsValueNameIssuerId[] = "googIssuerId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000135const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived";
136const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress";
wu@webrtc.org364f2042013-11-20 21:49:41 +0000137const char StatsReport::kStatsValueNameLocalCandidateType[] =
138 "googLocalCandidateType";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000139const char StatsReport::kStatsValueNameLocalCertificateId[] =
140 "googLocalCertificateId";
buildbot@webrtc.org71dffb72014-06-24 07:24:49 +0000141const char StatsReport::kStatsValueNameAdaptationChanges[] =
142 "googAdaptationChanges";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000143const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived";
144const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent";
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000145const char StatsReport::kStatsValueNamePlisReceived[] = "googPlisReceived";
146const char StatsReport::kStatsValueNamePlisSent[] = "googPlisSent";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000147const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived";
148const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent";
149const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost";
jiayl@webrtc.org11aab0e2014-03-07 18:56:26 +0000150const char StatsReport::kStatsValueNamePreferredJitterBufferMs[] =
151 "googPreferredJitterBufferMs";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000152const char StatsReport::kStatsValueNameReadable[] = "googReadable";
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000153const char StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug[] =
154 "googReceivedPacketGroupArrivalTimeDebug";
155const char StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug[] =
156 "googReceivedPacketGroupPropagationDeltaDebug";
157const char
158StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug[] =
159 "googReceivedPacketGroupPropagationDeltaSumDebug";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000160const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress";
wu@webrtc.org364f2042013-11-20 21:49:41 +0000161const char StatsReport::kStatsValueNameRemoteCandidateType[] =
162 "googRemoteCandidateType";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000163const char StatsReport::kStatsValueNameRemoteCertificateId[] =
164 "googRemoteCertificateId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165const char StatsReport::kStatsValueNameRetransmitBitrate[] =
166 "googRetransmitBitrate";
167const char StatsReport::kStatsValueNameRtt[] = "googRtt";
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000168const char StatsReport::kStatsValueNameSsrc[] = "ssrc";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000169const char StatsReport::kStatsValueNameTargetEncBitrate[] =
170 "googTargetEncBitrate";
171const char StatsReport::kStatsValueNameTransmitBitrate[] =
172 "googTransmitBitrate";
173const char StatsReport::kStatsValueNameTransportId[] = "transportId";
174const char StatsReport::kStatsValueNameTransportType[] = "googTransportType";
175const char StatsReport::kStatsValueNameTrackId[] = "googTrackId";
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000176const char StatsReport::kStatsValueNameTypingNoiseState[] =
177 "googTypingNoiseState";
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +0000178const char StatsReport::kStatsValueNameViewLimitedResolution[] =
179 "googViewLimitedResolution";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000180const char StatsReport::kStatsValueNameWritable[] = "googWritable";
181
182const char StatsReport::kStatsReportTypeSession[] = "googLibjingleSession";
183const char StatsReport::kStatsReportTypeBwe[] = "VideoBwe";
wu@webrtc.org97077a32013-10-25 21:18:33 +0000184const char StatsReport::kStatsReportTypeRemoteSsrc[] = "remoteSsrc";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000185const char StatsReport::kStatsReportTypeSsrc[] = "ssrc";
186const char StatsReport::kStatsReportTypeTrack[] = "googTrack";
187const char StatsReport::kStatsReportTypeIceCandidate[] = "iceCandidate";
188const char StatsReport::kStatsReportTypeTransport[] = "googTransport";
189const char StatsReport::kStatsReportTypeComponent[] = "googComponent";
190const char StatsReport::kStatsReportTypeCandidatePair[] = "googCandidatePair";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000191const char StatsReport::kStatsReportTypeCertificate[] = "googCertificate";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000192
193const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo";
194
195// Implementations of functions in statstypes.h
196void StatsReport::AddValue(const std::string& name, const std::string& value) {
197 Value temp;
198 temp.name = name;
199 temp.value = value;
200 values.push_back(temp);
201}
202
203void StatsReport::AddValue(const std::string& name, int64 value) {
204 AddValue(name, talk_base::ToString<int64>(value));
205}
206
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000207template <typename T>
208void StatsReport::AddValue(const std::string& name,
209 const std::vector<T>& value) {
210 std::ostringstream oss;
211 oss << "[";
212 for (size_t i = 0; i < value.size(); ++i) {
213 oss << talk_base::ToString<T>(value[i]);
214 if (i != value.size() - 1)
215 oss << ", ";
216 }
217 oss << "]";
218 AddValue(name, oss.str());
219}
220
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000221void StatsReport::AddBoolean(const std::string& name, bool value) {
222 AddValue(name, value ? "true" : "false");
223}
224
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000225void StatsReport::ReplaceValue(const std::string& name,
226 const std::string& value) {
227 for (Values::iterator it = values.begin(); it != values.end(); ++it) {
228 if ((*it).name == name) {
229 it->value = value;
230 return;
231 }
232 }
233 // It is not reachable here, add an ASSERT to make sure the overwriting is
234 // always a success.
235 ASSERT(false);
236}
237
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000238namespace {
239typedef std::map<std::string, StatsReport> StatsMap;
240
241std::string StatsId(const std::string& type, const std::string& id) {
242 return type + "_" + id;
243}
244
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000245std::string StatsId(const std::string& type, const std::string& id,
246 StatsCollector::TrackDirection direction) {
247 ASSERT(direction == StatsCollector::kSending ||
248 direction == StatsCollector::kReceiving);
249
250 // Strings for the direction of the track.
251 const char kSendDirection[] = "send";
252 const char kRecvDirection[] = "recv";
253
254 const std::string direction_id = (direction == StatsCollector::kSending) ?
255 kSendDirection : kRecvDirection;
256 return type + "_" + id + "_" + direction_id;
257}
258
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000259bool ExtractValueFromReport(
260 const StatsReport& report,
261 const std::string& name,
262 std::string* value) {
263 StatsReport::Values::const_iterator it = report.values.begin();
264 for (; it != report.values.end(); ++it) {
265 if (it->name == name) {
266 *value = it->value;
267 return true;
268 }
269 }
270 return false;
271}
272
xians@webrtc.org01bda202014-07-09 07:38:38 +0000273void AddTrackReport(StatsMap* reports, const std::string& track_id) {
274 // Adds an empty track report.
275 StatsReport report;
276 report.type = StatsReport::kStatsReportTypeTrack;
277 report.id = StatsId(StatsReport::kStatsReportTypeTrack, track_id);
278 report.AddValue(StatsReport::kStatsValueNameTrackId, track_id);
279 (*reports)[report.id] = report;
280}
281
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000282template <class TrackVector>
283void CreateTrackReports(const TrackVector& tracks, StatsMap* reports) {
284 for (size_t j = 0; j < tracks.size(); ++j) {
285 webrtc::MediaStreamTrackInterface* track = tracks[j];
xians@webrtc.org01bda202014-07-09 07:38:38 +0000286 AddTrackReport(reports, track->id());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000287 }
288}
289
290void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
291 report->AddValue(StatsReport::kStatsValueNameAudioOutputLevel,
292 info.audio_level);
293 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
294 info.bytes_rcvd);
295 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
296 info.jitter_ms);
jiayl@webrtc.org11aab0e2014-03-07 18:56:26 +0000297 report->AddValue(StatsReport::kStatsValueNameJitterBufferMs,
298 info.jitter_buffer_ms);
299 report->AddValue(StatsReport::kStatsValueNamePreferredJitterBufferMs,
300 info.jitter_buffer_preferred_ms);
301 report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs,
302 info.delay_estimate_ms);
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000303 report->AddValue(StatsReport::kStatsValueNameExpandRate,
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000304 talk_base::ToString<float>(info.expand_rate));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000305 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
306 info.packets_rcvd);
307 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
308 info.packets_lost);
buildbot@webrtc.org3e01e0b2014-05-13 17:54:10 +0000309 report->AddValue(StatsReport::kStatsValueNameDecodingCTSG,
310 info.decoding_calls_to_silence_generator);
311 report->AddValue(StatsReport::kStatsValueNameDecodingCTN,
312 info.decoding_calls_to_neteq);
313 report->AddValue(StatsReport::kStatsValueNameDecodingNormal,
314 info.decoding_normal);
315 report->AddValue(StatsReport::kStatsValueNameDecodingPLC,
316 info.decoding_plc);
317 report->AddValue(StatsReport::kStatsValueNameDecodingCNG,
318 info.decoding_cng);
319 report->AddValue(StatsReport::kStatsValueNameDecodingPLCCNG,
320 info.decoding_plc_cng);
buildbot@webrtc.orgb525a9d2014-06-03 09:42:15 +0000321 report->AddValue(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
322 info.capture_start_ntp_time_ms);
buildbot@webrtc.org7e71b772014-06-13 01:14:01 +0000323 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000324}
325
326void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
327 report->AddValue(StatsReport::kStatsValueNameAudioInputLevel,
328 info.audio_level);
329 report->AddValue(StatsReport::kStatsValueNameBytesSent,
330 info.bytes_sent);
331 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
332 info.packets_sent);
henrike@webrtc.orgffe26202014-03-19 22:20:10 +0000333 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
334 info.packets_lost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000335 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
336 info.jitter_ms);
337 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
338 report->AddValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
339 talk_base::ToString<float>(info.aec_quality_min));
340 report->AddValue(StatsReport::kStatsValueNameEchoDelayMedian,
341 info.echo_delay_median_ms);
342 report->AddValue(StatsReport::kStatsValueNameEchoDelayStdDev,
343 info.echo_delay_std_ms);
344 report->AddValue(StatsReport::kStatsValueNameEchoReturnLoss,
345 info.echo_return_loss);
346 report->AddValue(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
347 info.echo_return_loss_enhancement);
348 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000349 report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
350 info.typing_noise_detected);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000351}
352
353void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
354 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
355 info.bytes_rcvd);
356 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
357 info.packets_rcvd);
358 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
359 info.packets_lost);
360
361 report->AddValue(StatsReport::kStatsValueNameFirsSent,
362 info.firs_sent);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000363 report->AddValue(StatsReport::kStatsValueNamePlisSent,
364 info.plis_sent);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000365 report->AddValue(StatsReport::kStatsValueNameNacksSent,
366 info.nacks_sent);
367 report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived,
368 info.frame_width);
369 report->AddValue(StatsReport::kStatsValueNameFrameHeightReceived,
370 info.frame_height);
371 report->AddValue(StatsReport::kStatsValueNameFrameRateReceived,
372 info.framerate_rcvd);
373 report->AddValue(StatsReport::kStatsValueNameFrameRateDecoded,
374 info.framerate_decoded);
375 report->AddValue(StatsReport::kStatsValueNameFrameRateOutput,
376 info.framerate_output);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000377
378 report->AddValue(StatsReport::kStatsValueNameDecodeMs,
379 info.decode_ms);
380 report->AddValue(StatsReport::kStatsValueNameMaxDecodeMs,
381 info.max_decode_ms);
382 report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs,
383 info.current_delay_ms);
384 report->AddValue(StatsReport::kStatsValueNameTargetDelayMs,
385 info.target_delay_ms);
386 report->AddValue(StatsReport::kStatsValueNameJitterBufferMs,
387 info.jitter_buffer_ms);
388 report->AddValue(StatsReport::kStatsValueNameMinPlayoutDelayMs,
389 info.min_playout_delay_ms);
390 report->AddValue(StatsReport::kStatsValueNameRenderDelayMs,
391 info.render_delay_ms);
buildbot@webrtc.org0581f0b2014-05-06 21:36:31 +0000392
393 report->AddValue(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
394 info.capture_start_ntp_time_ms);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000395}
396
397void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
398 report->AddValue(StatsReport::kStatsValueNameBytesSent,
399 info.bytes_sent);
400 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
401 info.packets_sent);
henrike@webrtc.orgffe26202014-03-19 22:20:10 +0000402 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
403 info.packets_lost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000404
405 report->AddValue(StatsReport::kStatsValueNameFirsReceived,
406 info.firs_rcvd);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000407 report->AddValue(StatsReport::kStatsValueNamePlisReceived,
408 info.plis_rcvd);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000409 report->AddValue(StatsReport::kStatsValueNameNacksReceived,
410 info.nacks_rcvd);
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000411 report->AddValue(StatsReport::kStatsValueNameFrameWidthInput,
412 info.input_frame_width);
413 report->AddValue(StatsReport::kStatsValueNameFrameHeightInput,
414 info.input_frame_height);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000415 report->AddValue(StatsReport::kStatsValueNameFrameWidthSent,
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000416 info.send_frame_width);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000417 report->AddValue(StatsReport::kStatsValueNameFrameHeightSent,
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000418 info.send_frame_height);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000419 report->AddValue(StatsReport::kStatsValueNameFrameRateInput,
420 info.framerate_input);
421 report->AddValue(StatsReport::kStatsValueNameFrameRateSent,
422 info.framerate_sent);
423 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
424 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +0000425 report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution,
426 (info.adapt_reason & 0x1) > 0);
427 report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution,
428 (info.adapt_reason & 0x2) > 0);
429 report->AddBoolean(StatsReport::kStatsValueNameViewLimitedResolution,
430 (info.adapt_reason & 0x4) > 0);
buildbot@webrtc.org71dffb72014-06-24 07:24:49 +0000431 report->AddValue(StatsReport::kStatsValueNameAdaptationChanges,
432 info.adapt_changes);
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000433 report->AddValue(StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms);
434 report->AddValue(StatsReport::kStatsValueNameCaptureJitterMs,
435 info.capture_jitter_ms);
wu@webrtc.org9caf2762013-12-11 18:25:07 +0000436 report->AddValue(StatsReport::kStatsValueNameCaptureQueueDelayMsPerS,
437 info.capture_queue_delay_ms_per_s);
438 report->AddValue(StatsReport::kStatsValueNameEncodeUsagePercent,
439 info.encode_usage_percent);
buildbot@webrtc.orgc800c1c2014-06-13 07:56:17 +0000440 report->AddValue(StatsReport::kStatsValueNameEncodeRelStdDev,
441 info.encode_rsd);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000442}
443
444void ExtractStats(const cricket::BandwidthEstimationInfo& info,
445 double stats_gathering_started,
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000446 PeerConnectionInterface::StatsOutputLevel level,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000447 StatsReport* report) {
448 report->id = StatsReport::kStatsReportVideoBweId;
449 report->type = StatsReport::kStatsReportTypeBwe;
450
451 // Clear out stats from previous GatherStats calls if any.
452 if (report->timestamp != stats_gathering_started) {
453 report->values.clear();
454 report->timestamp = stats_gathering_started;
455 }
456
457 report->AddValue(StatsReport::kStatsValueNameAvailableSendBandwidth,
458 info.available_send_bandwidth);
459 report->AddValue(StatsReport::kStatsValueNameAvailableReceiveBandwidth,
460 info.available_recv_bandwidth);
461 report->AddValue(StatsReport::kStatsValueNameTargetEncBitrate,
462 info.target_enc_bitrate);
463 report->AddValue(StatsReport::kStatsValueNameActualEncBitrate,
464 info.actual_enc_bitrate);
465 report->AddValue(StatsReport::kStatsValueNameRetransmitBitrate,
466 info.retransmit_bitrate);
467 report->AddValue(StatsReport::kStatsValueNameTransmitBitrate,
468 info.transmit_bitrate);
469 report->AddValue(StatsReport::kStatsValueNameBucketDelay,
470 info.bucket_delay);
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000471 if (level >= PeerConnectionInterface::kStatsOutputLevelDebug) {
472 report->AddValue(
473 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug,
474 info.total_received_propagation_delta_ms);
475 if (info.recent_received_propagation_delta_ms.size() > 0) {
476 report->AddValue(
477 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug,
478 info.recent_received_propagation_delta_ms);
479 report->AddValue(
480 StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug,
481 info.recent_received_packet_group_arrival_time_ms);
482 }
483 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000484}
485
wu@webrtc.org97077a32013-10-25 21:18:33 +0000486void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
487 StatsReport* report) {
488 report->timestamp = info.remote_stats[0].timestamp;
489 // TODO(hta): Extract some stats here.
490}
491
492void ExtractRemoteStats(const cricket::MediaReceiverInfo& info,
493 StatsReport* report) {
494 report->timestamp = info.remote_stats[0].timestamp;
495 // TODO(hta): Extract some stats here.
496}
497
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000498// Template to extract stats from a data vector.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000499// In order to use the template, the functions that are called from it,
500// ExtractStats and ExtractRemoteStats, must be defined and overloaded
501// for each type.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000502template<typename T>
503void ExtractStatsFromList(const std::vector<T>& data,
504 const std::string& transport_id,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000505 StatsCollector* collector,
506 StatsCollector::TrackDirection direction) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000507 typename std::vector<T>::const_iterator it = data.begin();
508 for (; it != data.end(); ++it) {
509 std::string id;
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000510 uint32 ssrc = it->ssrc();
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000511 // Each track can have stats for both local and remote objects.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000512 // TODO(hta): Handle the case of multiple SSRCs per object.
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000513 StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id,
514 direction);
515 if (report)
516 ExtractStats(*it, report);
517
wu@webrtc.org97077a32013-10-25 21:18:33 +0000518 if (it->remote_stats.size() > 0) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000519 report = collector->PrepareRemoteReport(ssrc, transport_id,
520 direction);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000521 if (!report) {
522 continue;
523 }
524 ExtractRemoteStats(*it, report);
525 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000526 }
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000527}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000528
529} // namespace
530
tommi@webrtc.org03505bc2014-07-14 20:15:26 +0000531StatsCollector::StatsCollector(WebRtcSession* session)
532 : session_(session), stats_gathering_started_(0) {
533 ASSERT(session_);
534}
535
536StatsCollector::~StatsCollector() {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000537}
538
539// Adds a MediaStream with tracks that can be used as a |selector| in a call
540// to GetStats.
541void StatsCollector::AddStream(MediaStreamInterface* stream) {
542 ASSERT(stream != NULL);
543
544 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(),
545 &reports_);
546 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(),
547 &reports_);
548}
549
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000550void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track,
551 uint32 ssrc) {
552 ASSERT(audio_track != NULL);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000553 for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin();
554 it != local_audio_tracks_.end(); ++it) {
555 ASSERT(it->first != audio_track || it->second != ssrc);
556 }
xians@webrtc.org01bda202014-07-09 07:38:38 +0000557
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000558 local_audio_tracks_.push_back(std::make_pair(audio_track, ssrc));
xians@webrtc.org01bda202014-07-09 07:38:38 +0000559
560 // Create the kStatsReportTypeTrack report for the new track if there is no
561 // report yet.
562 StatsMap::iterator it = reports_.find(
563 StatsId(StatsReport::kStatsReportTypeTrack, audio_track->id()));
564 if (it == reports_.end())
565 AddTrackReport(&reports_, audio_track->id());
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000566}
567
568void StatsCollector::RemoveLocalAudioTrack(AudioTrackInterface* audio_track,
569 uint32 ssrc) {
570 ASSERT(audio_track != NULL);
571 for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin();
572 it != local_audio_tracks_.end(); ++it) {
573 if (it->first == audio_track && it->second == ssrc) {
574 local_audio_tracks_.erase(it);
575 return;
576 }
577 }
578
579 ASSERT(false);
580}
581
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000582bool StatsCollector::GetStats(MediaStreamTrackInterface* track,
583 StatsReports* reports) {
584 ASSERT(reports != NULL);
585 reports->clear();
586
587 StatsMap::iterator it;
588 if (!track) {
589 for (it = reports_.begin(); it != reports_.end(); ++it) {
590 reports->push_back(it->second);
591 }
592 return true;
593 }
594
595 it = reports_.find(StatsId(StatsReport::kStatsReportTypeSession,
596 session_->id()));
597 if (it != reports_.end()) {
598 reports->push_back(it->second);
599 }
600
601 it = reports_.find(StatsId(StatsReport::kStatsReportTypeTrack, track->id()));
602
603 if (it == reports_.end()) {
604 LOG(LS_WARNING) << "No StatsReport is available for "<< track->id();
605 return false;
606 }
607
608 reports->push_back(it->second);
609
610 std::string track_id;
611 for (it = reports_.begin(); it != reports_.end(); ++it) {
612 if (it->second.type != StatsReport::kStatsReportTypeSsrc) {
613 continue;
614 }
615 if (ExtractValueFromReport(it->second,
616 StatsReport::kStatsValueNameTrackId,
617 &track_id)) {
618 if (track_id == track->id()) {
619 reports->push_back(it->second);
620 }
621 }
622 }
623
624 return true;
625}
626
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000627void
628StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000629 double time_now = GetTimeNow();
630 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
631 // ms apart will be ignored.
632 const double kMinGatherStatsPeriod = 50;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000633 if (stats_gathering_started_ != 0 &&
634 stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000635 return;
636 }
637 stats_gathering_started_ = time_now;
638
639 if (session_) {
640 ExtractSessionInfo();
641 ExtractVoiceInfo();
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000642 ExtractVideoInfo(level);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000643 }
644}
645
wu@webrtc.org97077a32013-10-25 21:18:33 +0000646StatsReport* StatsCollector::PrepareLocalReport(
647 uint32 ssrc,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000648 const std::string& transport_id,
649 TrackDirection direction) {
650 const std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000651 StatsMap::iterator it = reports_.find(StatsId(
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000652 StatsReport::kStatsReportTypeSsrc, ssrc_id, direction));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000653
xians@webrtc.org01bda202014-07-09 07:38:38 +0000654 // Use the ID of the track that is currently mapped to the SSRC, if any.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000655 std::string track_id;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000656 if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
657 if (it == reports_.end()) {
658 // The ssrc is not used by any track or existing report, return NULL
659 // in such case to indicate no report is prepared for the ssrc.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000660 return NULL;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000661 }
662
663 // The ssrc is not used by any existing track. Keeps the old track id
664 // since we want to report the stats for inactive ssrc.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000665 ExtractValueFromReport(it->second,
666 StatsReport::kStatsValueNameTrackId,
667 &track_id);
668 }
669
wu@webrtc.org97077a32013-10-25 21:18:33 +0000670 StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000671 ssrc_id, direction);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000672
673 // Clear out stats from previous GatherStats calls if any.
xians@webrtc.org01bda202014-07-09 07:38:38 +0000674 // This is required since the report will be returned for the new values.
675 // Having the old values in the report will lead to multiple values with
676 // the same name.
677 // TODO(xians): Consider changing StatsReport to use map instead of vector.
678 report->values.clear();
679 report->timestamp = stats_gathering_started_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000680
681 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
682 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
683 // Add the mapping of SSRC to transport.
684 report->AddValue(StatsReport::kStatsValueNameTransportId,
685 transport_id);
686 return report;
687}
688
wu@webrtc.org97077a32013-10-25 21:18:33 +0000689StatsReport* StatsCollector::PrepareRemoteReport(
690 uint32 ssrc,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000691 const std::string& transport_id,
692 TrackDirection direction) {
693 const std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000694 StatsMap::iterator it = reports_.find(StatsId(
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000695 StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction));
wu@webrtc.org97077a32013-10-25 21:18:33 +0000696
xians@webrtc.org01bda202014-07-09 07:38:38 +0000697 // Use the ID of the track that is currently mapped to the SSRC, if any.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000698 std::string track_id;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000699 if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
700 if (it == reports_.end()) {
701 // The ssrc is not used by any track or existing report, return NULL
702 // in such case to indicate no report is prepared for the ssrc.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000703 return NULL;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000704 }
705
706 // The ssrc is not used by any existing track. Keeps the old track id
707 // since we want to report the stats for inactive ssrc.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000708 ExtractValueFromReport(it->second,
709 StatsReport::kStatsValueNameTrackId,
710 &track_id);
711 }
712
713 StatsReport* report = GetOrCreateReport(
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000714 StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000715
716 // Clear out stats from previous GatherStats calls if any.
717 // The timestamp will be added later. Zero it for debugging.
718 report->values.clear();
719 report->timestamp = 0;
720
721 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
722 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
723 // Add the mapping of SSRC to transport.
724 report->AddValue(StatsReport::kStatsValueNameTransportId,
725 transport_id);
726 return report;
727}
728
wu@webrtc.org4551b792013-10-09 15:37:36 +0000729std::string StatsCollector::AddOneCertificateReport(
730 const talk_base::SSLCertificate* cert, const std::string& issuer_id) {
731 // TODO(bemasc): Move this computation to a helper class that caches these
732 // values to reduce CPU use in GetStats. This will require adding a fast
733 // SSLCertificate::Equals() method to detect certificate changes.
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000734
735 std::string digest_algorithm;
736 if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm))
737 return std::string();
738
wu@webrtc.org4551b792013-10-09 15:37:36 +0000739 talk_base::scoped_ptr<talk_base::SSLFingerprint> ssl_fingerprint(
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000740 talk_base::SSLFingerprint::Create(digest_algorithm, cert));
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000741
742 // SSLFingerprint::Create can fail if the algorithm returned by
743 // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the
744 // implementation of SSLCertificate::ComputeDigest. This currently happens
745 // with MD5- and SHA-224-signed certificates when linked to libNSS.
746 if (!ssl_fingerprint)
747 return std::string();
748
wu@webrtc.org4551b792013-10-09 15:37:36 +0000749 std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
750
751 talk_base::Buffer der_buffer;
752 cert->ToDER(&der_buffer);
753 std::string der_base64;
754 talk_base::Base64::EncodeFromArray(
755 der_buffer.data(), der_buffer.length(), &der_base64);
756
757 StatsReport report;
758 report.type = StatsReport::kStatsReportTypeCertificate;
759 report.id = StatsId(report.type, fingerprint);
760 report.timestamp = stats_gathering_started_;
761 report.AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint);
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000762 report.AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm,
763 digest_algorithm);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000764 report.AddValue(StatsReport::kStatsValueNameDer, der_base64);
765 if (!issuer_id.empty())
766 report.AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id);
767 reports_[report.id] = report;
768 return report.id;
769}
770
771std::string StatsCollector::AddCertificateReports(
772 const talk_base::SSLCertificate* cert) {
773 // Produces a chain of StatsReports representing this certificate and the rest
774 // of its chain, and adds those reports to |reports_|. The return value is
775 // the id of the leaf report. The provided cert must be non-null, so at least
776 // one report will always be provided and the returned string will never be
777 // empty.
778 ASSERT(cert != NULL);
779
780 std::string issuer_id;
781 talk_base::scoped_ptr<talk_base::SSLCertChain> chain;
782 if (cert->GetChain(chain.accept())) {
783 // This loop runs in reverse, i.e. from root to leaf, so that each
784 // certificate's issuer's report ID is known before the child certificate's
785 // report is generated. The root certificate does not have an issuer ID
786 // value.
787 for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) {
788 const talk_base::SSLCertificate& cert_i = chain->Get(i);
789 issuer_id = AddOneCertificateReport(&cert_i, issuer_id);
790 }
791 }
792 // Add the leaf certificate.
793 return AddOneCertificateReport(cert, issuer_id);
794}
795
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000796void StatsCollector::ExtractSessionInfo() {
797 // Extract information from the base session.
798 StatsReport report;
799 report.id = StatsId(StatsReport::kStatsReportTypeSession, session_->id());
800 report.type = StatsReport::kStatsReportTypeSession;
801 report.timestamp = stats_gathering_started_;
802 report.values.clear();
803 report.AddBoolean(StatsReport::kStatsValueNameInitiator,
804 session_->initiator());
805
806 reports_[report.id] = report;
807
808 cricket::SessionStats stats;
809 if (session_->GetStats(&stats)) {
810 // Store the proxy map away for use in SSRC reporting.
811 proxy_to_transport_ = stats.proxy_to_transport;
812
813 for (cricket::TransportStatsMap::iterator transport_iter
814 = stats.transport_stats.begin();
815 transport_iter != stats.transport_stats.end(); ++transport_iter) {
wu@webrtc.org4551b792013-10-09 15:37:36 +0000816 // Attempt to get a copy of the certificates from the transport and
817 // expose them in stats reports. All channels in a transport share the
818 // same local and remote certificates.
819 std::string local_cert_report_id, remote_cert_report_id;
820 cricket::Transport* transport =
821 session_->GetTransport(transport_iter->second.content_name);
822 if (transport) {
823 talk_base::scoped_ptr<talk_base::SSLIdentity> identity;
824 if (transport->GetIdentity(identity.accept()))
825 local_cert_report_id = AddCertificateReports(
826 &(identity->certificate()));
827
828 talk_base::scoped_ptr<talk_base::SSLCertificate> cert;
829 if (transport->GetRemoteCertificate(cert.accept()))
830 remote_cert_report_id = AddCertificateReports(cert.get());
831 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000832 for (cricket::TransportChannelStatsList::iterator channel_iter
833 = transport_iter->second.channel_stats.begin();
834 channel_iter != transport_iter->second.channel_stats.end();
835 ++channel_iter) {
836 StatsReport channel_report;
837 std::ostringstream ostc;
838 ostc << "Channel-" << transport_iter->second.content_name
839 << "-" << channel_iter->component;
840 channel_report.id = ostc.str();
841 channel_report.type = StatsReport::kStatsReportTypeComponent;
842 channel_report.timestamp = stats_gathering_started_;
843 channel_report.AddValue(StatsReport::kStatsValueNameComponent,
844 channel_iter->component);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000845 if (!local_cert_report_id.empty())
846 channel_report.AddValue(
847 StatsReport::kStatsValueNameLocalCertificateId,
848 local_cert_report_id);
849 if (!remote_cert_report_id.empty())
850 channel_report.AddValue(
851 StatsReport::kStatsValueNameRemoteCertificateId,
852 remote_cert_report_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000853 reports_[channel_report.id] = channel_report;
854 for (size_t i = 0;
855 i < channel_iter->connection_infos.size();
856 ++i) {
857 StatsReport report;
858 const cricket::ConnectionInfo& info
859 = channel_iter->connection_infos[i];
860 std::ostringstream ost;
861 ost << "Conn-" << transport_iter->first << "-"
862 << channel_iter->component << "-" << i;
863 report.id = ost.str();
864 report.type = StatsReport::kStatsReportTypeCandidatePair;
865 report.timestamp = stats_gathering_started_;
866 // Link from connection to its containing channel.
867 report.AddValue(StatsReport::kStatsValueNameChannelId,
868 channel_report.id);
869 report.AddValue(StatsReport::kStatsValueNameBytesSent,
870 info.sent_total_bytes);
871 report.AddValue(StatsReport::kStatsValueNameBytesReceived,
872 info.recv_total_bytes);
873 report.AddBoolean(StatsReport::kStatsValueNameWritable,
874 info.writable);
875 report.AddBoolean(StatsReport::kStatsValueNameReadable,
876 info.readable);
877 report.AddBoolean(StatsReport::kStatsValueNameActiveConnection,
878 info.best_connection);
879 report.AddValue(StatsReport::kStatsValueNameLocalAddress,
880 info.local_candidate.address().ToString());
881 report.AddValue(StatsReport::kStatsValueNameRemoteAddress,
882 info.remote_candidate.address().ToString());
wu@webrtc.org97077a32013-10-25 21:18:33 +0000883 report.AddValue(StatsReport::kStatsValueNameRtt, info.rtt);
884 report.AddValue(StatsReport::kStatsValueNameTransportType,
885 info.local_candidate.protocol());
wu@webrtc.org364f2042013-11-20 21:49:41 +0000886 report.AddValue(StatsReport::kStatsValueNameLocalCandidateType,
887 info.local_candidate.type());
888 report.AddValue(StatsReport::kStatsValueNameRemoteCandidateType,
889 info.remote_candidate.type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000890 reports_[report.id] = report;
891 }
892 }
893 }
894 }
895}
896
897void StatsCollector::ExtractVoiceInfo() {
898 if (!session_->voice_channel()) {
899 return;
900 }
901 cricket::VoiceMediaInfo voice_info;
902 if (!session_->voice_channel()->GetStats(&voice_info)) {
903 LOG(LS_ERROR) << "Failed to get voice channel stats.";
904 return;
905 }
906 std::string transport_id;
907 if (!GetTransportIdFromProxy(session_->voice_channel()->content_name(),
908 &transport_id)) {
909 LOG(LS_ERROR) << "Failed to get transport name for proxy "
910 << session_->voice_channel()->content_name();
911 return;
912 }
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000913 ExtractStatsFromList(voice_info.receivers, transport_id, this, kReceiving);
914 ExtractStatsFromList(voice_info.senders, transport_id, this, kSending);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000915
916 UpdateStatsFromExistingLocalAudioTracks();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000917}
918
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000919void StatsCollector::ExtractVideoInfo(
920 PeerConnectionInterface::StatsOutputLevel level) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000921 if (!session_->video_channel()) {
922 return;
923 }
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000924 cricket::StatsOptions options;
925 options.include_received_propagation_stats =
926 (level >= PeerConnectionInterface::kStatsOutputLevelDebug) ?
927 true : false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000928 cricket::VideoMediaInfo video_info;
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000929 if (!session_->video_channel()->GetStats(options, &video_info)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000930 LOG(LS_ERROR) << "Failed to get video channel stats.";
931 return;
932 }
933 std::string transport_id;
934 if (!GetTransportIdFromProxy(session_->video_channel()->content_name(),
935 &transport_id)) {
936 LOG(LS_ERROR) << "Failed to get transport name for proxy "
937 << session_->video_channel()->content_name();
938 return;
939 }
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000940 ExtractStatsFromList(video_info.receivers, transport_id, this, kReceiving);
941 ExtractStatsFromList(video_info.senders, transport_id, this, kSending);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000942 if (video_info.bw_estimations.size() != 1) {
943 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
944 } else {
945 StatsReport* report = &reports_[StatsReport::kStatsReportVideoBweId];
946 ExtractStats(
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000947 video_info.bw_estimations[0], stats_gathering_started_, level, report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000948 }
949}
950
951double StatsCollector::GetTimeNow() {
tommi@webrtc.orgecb87232014-07-08 12:48:29 +0000952 return talk_base::Timing::WallTimeNow() * talk_base::kNumMillisecsPerSec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000953}
954
955bool StatsCollector::GetTransportIdFromProxy(const std::string& proxy,
956 std::string* transport) {
957 // TODO(hta): Remove handling of empty proxy name once tests do not use it.
958 if (proxy.empty()) {
959 transport->clear();
960 return true;
961 }
962 if (proxy_to_transport_.find(proxy) == proxy_to_transport_.end()) {
963 LOG(LS_ERROR) << "No transport ID mapping for " << proxy;
964 return false;
965 }
966 std::ostringstream ost;
967 // Component 1 is always used for RTP.
968 ost << "Channel-" << proxy_to_transport_[proxy] << "-1";
969 *transport = ost.str();
970 return true;
971}
972
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000973StatsReport* StatsCollector::GetReport(const std::string& type,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000974 const std::string& id,
975 TrackDirection direction) {
976 ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
977 type == StatsReport::kStatsReportTypeRemoteSsrc);
978 std::string statsid = StatsId(type, id, direction);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000979 StatsReport* report = NULL;
980 std::map<std::string, StatsReport>::iterator it = reports_.find(statsid);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000981 if (it != reports_.end())
982 report = &(it->second);
983
984 return report;
985}
986
987StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000988 const std::string& id,
989 TrackDirection direction) {
990 ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
991 type == StatsReport::kStatsReportTypeRemoteSsrc);
992 StatsReport* report = GetReport(type, id, direction);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000993 if (report == NULL) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000994 std::string statsid = StatsId(type, id, direction);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000995 report = &reports_[statsid]; // Create new element.
996 report->id = statsid;
997 report->type = type;
wu@webrtc.org97077a32013-10-25 21:18:33 +0000998 }
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000999
wu@webrtc.org97077a32013-10-25 21:18:33 +00001000 return report;
1001}
1002
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001003void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() {
1004 // Loop through the existing local audio tracks.
1005 for (LocalAudioTrackVector::const_iterator it = local_audio_tracks_.begin();
1006 it != local_audio_tracks_.end(); ++it) {
1007 AudioTrackInterface* track = it->first;
1008 uint32 ssrc = it->second;
1009 std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
1010 StatsReport* report = GetReport(StatsReport::kStatsReportTypeSsrc,
xians@webrtc.org4cb01282014-06-12 14:57:05 +00001011 ssrc_id,
1012 kSending);
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +00001013 if (report == NULL) {
1014 // This can happen if a local audio track is added to a stream on the
1015 // fly and the report has not been set up yet. Do nothing in this case.
1016 LOG(LS_ERROR) << "Stats report does not exist for ssrc " << ssrc;
1017 continue;
1018 }
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001019
1020 // The same ssrc can be used by both local and remote audio tracks.
1021 std::string track_id;
1022 if (!ExtractValueFromReport(*report,
1023 StatsReport::kStatsValueNameTrackId,
1024 &track_id) ||
1025 track_id != track->id()) {
1026 continue;
1027 }
1028
1029 UpdateReportFromAudioTrack(track, report);
1030 }
1031}
1032
1033void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track,
1034 StatsReport* report) {
1035 ASSERT(track != NULL);
1036 if (report == NULL)
1037 return;
1038
1039 int signal_level = 0;
1040 if (track->GetSignalLevel(&signal_level)) {
1041 report->ReplaceValue(StatsReport::kStatsValueNameAudioInputLevel,
1042 talk_base::ToString<int>(signal_level));
1043 }
1044
1045 talk_base::scoped_refptr<AudioProcessorInterface> audio_processor(
1046 track->GetAudioProcessor());
1047 if (audio_processor.get() == NULL)
1048 return;
1049
1050 AudioProcessorInterface::AudioProcessorStats stats;
1051 audio_processor->GetStats(&stats);
1052 report->ReplaceValue(StatsReport::kStatsValueNameTypingNoiseState,
1053 stats.typing_noise_detected ? "true" : "false");
1054 report->ReplaceValue(StatsReport::kStatsValueNameEchoReturnLoss,
1055 talk_base::ToString<int>(stats.echo_return_loss));
1056 report->ReplaceValue(
1057 StatsReport::kStatsValueNameEchoReturnLossEnhancement,
1058 talk_base::ToString<int>(stats.echo_return_loss_enhancement));
1059 report->ReplaceValue(StatsReport::kStatsValueNameEchoDelayMedian,
1060 talk_base::ToString<int>(stats.echo_delay_median_ms));
1061 report->ReplaceValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
1062 talk_base::ToString<float>(stats.aec_quality_min));
1063 report->ReplaceValue(StatsReport::kStatsValueNameEchoDelayStdDev,
1064 talk_base::ToString<int>(stats.echo_delay_std_ms));
1065}
1066
xians@webrtc.org4cb01282014-06-12 14:57:05 +00001067bool StatsCollector::GetTrackIdBySsrc(uint32 ssrc, std::string* track_id,
1068 TrackDirection direction) {
1069 if (direction == kSending) {
tommi@webrtc.org03505bc2014-07-14 20:15:26 +00001070 if (!session_->GetLocalTrackIdBySsrc(ssrc, track_id)) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +00001071 LOG(LS_WARNING) << "The SSRC " << ssrc
1072 << " is not associated with a sending track";
1073 return false;
1074 }
1075 } else {
1076 ASSERT(direction == kReceiving);
tommi@webrtc.org03505bc2014-07-14 20:15:26 +00001077 if (!session_->GetRemoteTrackIdBySsrc(ssrc, track_id)) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +00001078 LOG(LS_WARNING) << "The SSRC " << ssrc
1079 << " is not associated with a receiving track";
1080 return false;
1081 }
1082 }
1083
1084 return true;
1085}
1086
xians@webrtc.org01bda202014-07-09 07:38:38 +00001087void StatsCollector::ClearUpdateStatsCache() {
1088 stats_gathering_started_ = 0;
1089}
1090
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001091} // namespace webrtc