blob: 4c1996755ba12d167204f031f85616f166d7998f [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
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000033#include "talk/session/media/channel.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000034#include "webrtc/base/base64.h"
35#include "webrtc/base/scoped_ptr.h"
36#include "webrtc/base/timing.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000037
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
wu@webrtc.org9caf2762013-12-11 18:25:07 +000092const char StatsReport::kStatsValueNameEncodeUsagePercent[] =
93 "googEncodeUsagePercent";
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +000094const char StatsReport::kStatsValueNameExpandRate[] = "googExpandRate";
wu@webrtc.org4551b792013-10-09 15:37:36 +000095const char StatsReport::kStatsValueNameFingerprint[] = "googFingerprint";
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +000096const char StatsReport::kStatsValueNameFingerprintAlgorithm[] =
97 "googFingerprintAlgorithm";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000098const char StatsReport::kStatsValueNameFirsReceived[] = "googFirsReceived";
99const char StatsReport::kStatsValueNameFirsSent[] = "googFirsSent";
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000100const char StatsReport::kStatsValueNameFrameHeightInput[] =
101 "googFrameHeightInput";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000102const char StatsReport::kStatsValueNameFrameHeightReceived[] =
103 "googFrameHeightReceived";
104const char StatsReport::kStatsValueNameFrameHeightSent[] =
105 "googFrameHeightSent";
106const char StatsReport::kStatsValueNameFrameRateReceived[] =
107 "googFrameRateReceived";
108const char StatsReport::kStatsValueNameFrameRateDecoded[] =
109 "googFrameRateDecoded";
110const char StatsReport::kStatsValueNameFrameRateOutput[] =
111 "googFrameRateOutput";
wu@webrtc.org97077a32013-10-25 21:18:33 +0000112const char StatsReport::kStatsValueNameDecodeMs[] = "googDecodeMs";
113const char StatsReport::kStatsValueNameMaxDecodeMs[] = "googMaxDecodeMs";
114const char StatsReport::kStatsValueNameCurrentDelayMs[] = "googCurrentDelayMs";
115const char StatsReport::kStatsValueNameTargetDelayMs[] = "googTargetDelayMs";
116const char StatsReport::kStatsValueNameJitterBufferMs[] = "googJitterBufferMs";
117const char StatsReport::kStatsValueNameMinPlayoutDelayMs[] =
118 "googMinPlayoutDelayMs";
119const char StatsReport::kStatsValueNameRenderDelayMs[] = "googRenderDelayMs";
120
buildbot@webrtc.org0581f0b2014-05-06 21:36:31 +0000121const char StatsReport::kStatsValueNameCaptureStartNtpTimeMs[] =
122 "googCaptureStartNtpTimeMs";
123
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000124const char StatsReport::kStatsValueNameFrameRateInput[] = "googFrameRateInput";
125const char StatsReport::kStatsValueNameFrameRateSent[] = "googFrameRateSent";
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000126const char StatsReport::kStatsValueNameFrameWidthInput[] =
127 "googFrameWidthInput";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000128const char StatsReport::kStatsValueNameFrameWidthReceived[] =
129 "googFrameWidthReceived";
130const char StatsReport::kStatsValueNameFrameWidthSent[] = "googFrameWidthSent";
131const char StatsReport::kStatsValueNameInitiator[] = "googInitiator";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000132const char StatsReport::kStatsValueNameIssuerId[] = "googIssuerId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000133const char StatsReport::kStatsValueNameJitterReceived[] = "googJitterReceived";
134const char StatsReport::kStatsValueNameLocalAddress[] = "googLocalAddress";
wu@webrtc.org364f2042013-11-20 21:49:41 +0000135const char StatsReport::kStatsValueNameLocalCandidateType[] =
136 "googLocalCandidateType";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000137const char StatsReport::kStatsValueNameLocalCertificateId[] =
138 "googLocalCertificateId";
buildbot@webrtc.org71dffb72014-06-24 07:24:49 +0000139const char StatsReport::kStatsValueNameAdaptationChanges[] =
140 "googAdaptationChanges";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000141const char StatsReport::kStatsValueNameNacksReceived[] = "googNacksReceived";
142const char StatsReport::kStatsValueNameNacksSent[] = "googNacksSent";
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000143const char StatsReport::kStatsValueNamePlisReceived[] = "googPlisReceived";
144const char StatsReport::kStatsValueNamePlisSent[] = "googPlisSent";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000145const char StatsReport::kStatsValueNamePacketsReceived[] = "packetsReceived";
146const char StatsReport::kStatsValueNamePacketsSent[] = "packetsSent";
147const char StatsReport::kStatsValueNamePacketsLost[] = "packetsLost";
jiayl@webrtc.org11aab0e2014-03-07 18:56:26 +0000148const char StatsReport::kStatsValueNamePreferredJitterBufferMs[] =
149 "googPreferredJitterBufferMs";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000150const char StatsReport::kStatsValueNameReadable[] = "googReadable";
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000151const char StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug[] =
152 "googReceivedPacketGroupArrivalTimeDebug";
153const char StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug[] =
154 "googReceivedPacketGroupPropagationDeltaDebug";
155const char
156StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug[] =
157 "googReceivedPacketGroupPropagationDeltaSumDebug";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000158const char StatsReport::kStatsValueNameRemoteAddress[] = "googRemoteAddress";
wu@webrtc.org364f2042013-11-20 21:49:41 +0000159const char StatsReport::kStatsValueNameRemoteCandidateType[] =
160 "googRemoteCandidateType";
wu@webrtc.org4551b792013-10-09 15:37:36 +0000161const char StatsReport::kStatsValueNameRemoteCertificateId[] =
162 "googRemoteCertificateId";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000163const char StatsReport::kStatsValueNameRetransmitBitrate[] =
164 "googRetransmitBitrate";
165const char StatsReport::kStatsValueNameRtt[] = "googRtt";
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000166const char StatsReport::kStatsValueNameSsrc[] = "ssrc";
guoweis@webrtc.org930e0042014-11-17 19:42:14 +0000167const char StatsReport::kStatsValueNameSendPacketsDiscarded[] =
168 "packetsDiscardedOnSend";
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
tommi@webrtc.org242068d2014-07-14 20:19:56 +0000196void StatsReport::AddValue(StatsReport::StatsValueName name,
197 const std::string& value) {
198 values.push_back(Value(name, value));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000199}
200
tommi@webrtc.org242068d2014-07-14 20:19:56 +0000201void StatsReport::AddValue(StatsReport::StatsValueName name, int64 value) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000202 AddValue(name, rtc::ToString<int64>(value));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000203}
204
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000205template <typename T>
tommi@webrtc.org242068d2014-07-14 20:19:56 +0000206void StatsReport::AddValue(StatsReport::StatsValueName name,
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000207 const std::vector<T>& value) {
208 std::ostringstream oss;
209 oss << "[";
210 for (size_t i = 0; i < value.size(); ++i) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000211 oss << rtc::ToString<T>(value[i]);
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000212 if (i != value.size() - 1)
213 oss << ", ";
214 }
215 oss << "]";
216 AddValue(name, oss.str());
217}
218
tommi@webrtc.org242068d2014-07-14 20:19:56 +0000219void StatsReport::AddBoolean(StatsReport::StatsValueName name, bool value) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220 AddValue(name, value ? "true" : "false");
221}
222
tommi@webrtc.org242068d2014-07-14 20:19:56 +0000223void StatsReport::ReplaceValue(StatsReport::StatsValueName name,
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000224 const std::string& value) {
225 for (Values::iterator it = values.begin(); it != values.end(); ++it) {
226 if ((*it).name == name) {
227 it->value = value;
228 return;
229 }
230 }
231 // It is not reachable here, add an ASSERT to make sure the overwriting is
232 // always a success.
233 ASSERT(false);
234}
235
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000236namespace {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000237
tommi@webrtc.org47218952014-07-15 19:22:37 +0000238double GetTimeNow() {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000239 return rtc::Timing::WallTimeNow() * rtc::kNumMillisecsPerSec;
tommi@webrtc.org47218952014-07-15 19:22:37 +0000240}
241
242bool GetTransportIdFromProxy(const cricket::ProxyTransportMap& map,
243 const std::string& proxy,
244 std::string* transport) {
245 // TODO(hta): Remove handling of empty proxy name once tests do not use it.
246 if (proxy.empty()) {
247 transport->clear();
248 return true;
249 }
250
251 cricket::ProxyTransportMap::const_iterator found = map.find(proxy);
252 if (found == map.end()) {
253 LOG(LS_ERROR) << "No transport ID mapping for " << proxy;
254 return false;
255 }
256
257 std::ostringstream ost;
258 // Component 1 is always used for RTP.
259 ost << "Channel-" << found->second << "-1";
260 *transport = ost.str();
261 return true;
262}
263
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000264std::string StatsId(const std::string& type, const std::string& id) {
265 return type + "_" + id;
266}
267
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000268std::string StatsId(const std::string& type, const std::string& id,
269 StatsCollector::TrackDirection direction) {
270 ASSERT(direction == StatsCollector::kSending ||
271 direction == StatsCollector::kReceiving);
272
273 // Strings for the direction of the track.
274 const char kSendDirection[] = "send";
275 const char kRecvDirection[] = "recv";
276
277 const std::string direction_id = (direction == StatsCollector::kSending) ?
278 kSendDirection : kRecvDirection;
279 return type + "_" + id + "_" + direction_id;
280}
281
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000282bool ExtractValueFromReport(
283 const StatsReport& report,
tommi@webrtc.org242068d2014-07-14 20:19:56 +0000284 StatsReport::StatsValueName name,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000285 std::string* value) {
286 StatsReport::Values::const_iterator it = report.values.begin();
287 for (; it != report.values.end(); ++it) {
288 if (it->name == name) {
289 *value = it->value;
290 return true;
291 }
292 }
293 return false;
294}
295
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000296void AddTrackReport(StatsSet* reports, const std::string& track_id) {
xians@webrtc.org01bda202014-07-09 07:38:38 +0000297 // Adds an empty track report.
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000298 StatsReport* report = reports->ReplaceOrAddNew(
299 StatsId(StatsReport::kStatsReportTypeTrack, track_id));
300 report->type = StatsReport::kStatsReportTypeTrack;
301 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
xians@webrtc.org01bda202014-07-09 07:38:38 +0000302}
303
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000304template <class TrackVector>
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000305void CreateTrackReports(const TrackVector& tracks, StatsSet* reports) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000306 for (size_t j = 0; j < tracks.size(); ++j) {
307 webrtc::MediaStreamTrackInterface* track = tracks[j];
xians@webrtc.org01bda202014-07-09 07:38:38 +0000308 AddTrackReport(reports, track->id());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000309 }
310}
311
312void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
313 report->AddValue(StatsReport::kStatsValueNameAudioOutputLevel,
314 info.audio_level);
315 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
316 info.bytes_rcvd);
317 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
318 info.jitter_ms);
jiayl@webrtc.org11aab0e2014-03-07 18:56:26 +0000319 report->AddValue(StatsReport::kStatsValueNameJitterBufferMs,
320 info.jitter_buffer_ms);
321 report->AddValue(StatsReport::kStatsValueNamePreferredJitterBufferMs,
322 info.jitter_buffer_preferred_ms);
323 report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs,
324 info.delay_estimate_ms);
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000325 report->AddValue(StatsReport::kStatsValueNameExpandRate,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000326 rtc::ToString<float>(info.expand_rate));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000327 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
328 info.packets_rcvd);
329 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
330 info.packets_lost);
buildbot@webrtc.org3e01e0b2014-05-13 17:54:10 +0000331 report->AddValue(StatsReport::kStatsValueNameDecodingCTSG,
332 info.decoding_calls_to_silence_generator);
333 report->AddValue(StatsReport::kStatsValueNameDecodingCTN,
334 info.decoding_calls_to_neteq);
335 report->AddValue(StatsReport::kStatsValueNameDecodingNormal,
336 info.decoding_normal);
337 report->AddValue(StatsReport::kStatsValueNameDecodingPLC,
338 info.decoding_plc);
339 report->AddValue(StatsReport::kStatsValueNameDecodingCNG,
340 info.decoding_cng);
341 report->AddValue(StatsReport::kStatsValueNameDecodingPLCCNG,
342 info.decoding_plc_cng);
buildbot@webrtc.orgb525a9d2014-06-03 09:42:15 +0000343 report->AddValue(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
344 info.capture_start_ntp_time_ms);
buildbot@webrtc.org7e71b772014-06-13 01:14:01 +0000345 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000346}
347
348void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
349 report->AddValue(StatsReport::kStatsValueNameAudioInputLevel,
350 info.audio_level);
351 report->AddValue(StatsReport::kStatsValueNameBytesSent,
352 info.bytes_sent);
353 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
354 info.packets_sent);
henrike@webrtc.orgffe26202014-03-19 22:20:10 +0000355 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
356 info.packets_lost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000357 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
358 info.jitter_ms);
359 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
360 report->AddValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000361 rtc::ToString<float>(info.aec_quality_min));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000362 report->AddValue(StatsReport::kStatsValueNameEchoDelayMedian,
363 info.echo_delay_median_ms);
364 report->AddValue(StatsReport::kStatsValueNameEchoDelayStdDev,
365 info.echo_delay_std_ms);
366 report->AddValue(StatsReport::kStatsValueNameEchoReturnLoss,
367 info.echo_return_loss);
368 report->AddValue(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
369 info.echo_return_loss_enhancement);
370 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000371 report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
372 info.typing_noise_detected);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000373}
374
375void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
376 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
377 info.bytes_rcvd);
378 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
379 info.packets_rcvd);
380 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
381 info.packets_lost);
382
383 report->AddValue(StatsReport::kStatsValueNameFirsSent,
384 info.firs_sent);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000385 report->AddValue(StatsReport::kStatsValueNamePlisSent,
386 info.plis_sent);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000387 report->AddValue(StatsReport::kStatsValueNameNacksSent,
388 info.nacks_sent);
389 report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived,
390 info.frame_width);
391 report->AddValue(StatsReport::kStatsValueNameFrameHeightReceived,
392 info.frame_height);
393 report->AddValue(StatsReport::kStatsValueNameFrameRateReceived,
394 info.framerate_rcvd);
395 report->AddValue(StatsReport::kStatsValueNameFrameRateDecoded,
396 info.framerate_decoded);
397 report->AddValue(StatsReport::kStatsValueNameFrameRateOutput,
398 info.framerate_output);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000399
400 report->AddValue(StatsReport::kStatsValueNameDecodeMs,
401 info.decode_ms);
402 report->AddValue(StatsReport::kStatsValueNameMaxDecodeMs,
403 info.max_decode_ms);
404 report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs,
405 info.current_delay_ms);
406 report->AddValue(StatsReport::kStatsValueNameTargetDelayMs,
407 info.target_delay_ms);
408 report->AddValue(StatsReport::kStatsValueNameJitterBufferMs,
409 info.jitter_buffer_ms);
410 report->AddValue(StatsReport::kStatsValueNameMinPlayoutDelayMs,
411 info.min_playout_delay_ms);
412 report->AddValue(StatsReport::kStatsValueNameRenderDelayMs,
413 info.render_delay_ms);
buildbot@webrtc.org0581f0b2014-05-06 21:36:31 +0000414
415 report->AddValue(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
416 info.capture_start_ntp_time_ms);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000417}
418
419void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
420 report->AddValue(StatsReport::kStatsValueNameBytesSent,
421 info.bytes_sent);
422 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
423 info.packets_sent);
henrike@webrtc.orgffe26202014-03-19 22:20:10 +0000424 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
425 info.packets_lost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000426
427 report->AddValue(StatsReport::kStatsValueNameFirsReceived,
428 info.firs_rcvd);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000429 report->AddValue(StatsReport::kStatsValueNamePlisReceived,
430 info.plis_rcvd);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000431 report->AddValue(StatsReport::kStatsValueNameNacksReceived,
432 info.nacks_rcvd);
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000433 report->AddValue(StatsReport::kStatsValueNameFrameWidthInput,
434 info.input_frame_width);
435 report->AddValue(StatsReport::kStatsValueNameFrameHeightInput,
436 info.input_frame_height);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000437 report->AddValue(StatsReport::kStatsValueNameFrameWidthSent,
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000438 info.send_frame_width);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000439 report->AddValue(StatsReport::kStatsValueNameFrameHeightSent,
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000440 info.send_frame_height);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000441 report->AddValue(StatsReport::kStatsValueNameFrameRateInput,
442 info.framerate_input);
443 report->AddValue(StatsReport::kStatsValueNameFrameRateSent,
444 info.framerate_sent);
445 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
446 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +0000447 report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution,
448 (info.adapt_reason & 0x1) > 0);
449 report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution,
450 (info.adapt_reason & 0x2) > 0);
451 report->AddBoolean(StatsReport::kStatsValueNameViewLimitedResolution,
452 (info.adapt_reason & 0x4) > 0);
buildbot@webrtc.org71dffb72014-06-24 07:24:49 +0000453 report->AddValue(StatsReport::kStatsValueNameAdaptationChanges,
454 info.adapt_changes);
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000455 report->AddValue(StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms);
456 report->AddValue(StatsReport::kStatsValueNameCaptureJitterMs,
457 info.capture_jitter_ms);
wu@webrtc.org9caf2762013-12-11 18:25:07 +0000458 report->AddValue(StatsReport::kStatsValueNameCaptureQueueDelayMsPerS,
459 info.capture_queue_delay_ms_per_s);
460 report->AddValue(StatsReport::kStatsValueNameEncodeUsagePercent,
461 info.encode_usage_percent);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000462}
463
464void ExtractStats(const cricket::BandwidthEstimationInfo& info,
465 double stats_gathering_started,
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000466 PeerConnectionInterface::StatsOutputLevel level,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000467 StatsReport* report) {
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000468 ASSERT(report->id == StatsReport::kStatsReportVideoBweId);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000469 report->type = StatsReport::kStatsReportTypeBwe;
470
471 // Clear out stats from previous GatherStats calls if any.
472 if (report->timestamp != stats_gathering_started) {
473 report->values.clear();
474 report->timestamp = stats_gathering_started;
475 }
476
477 report->AddValue(StatsReport::kStatsValueNameAvailableSendBandwidth,
478 info.available_send_bandwidth);
479 report->AddValue(StatsReport::kStatsValueNameAvailableReceiveBandwidth,
480 info.available_recv_bandwidth);
481 report->AddValue(StatsReport::kStatsValueNameTargetEncBitrate,
482 info.target_enc_bitrate);
483 report->AddValue(StatsReport::kStatsValueNameActualEncBitrate,
484 info.actual_enc_bitrate);
485 report->AddValue(StatsReport::kStatsValueNameRetransmitBitrate,
486 info.retransmit_bitrate);
487 report->AddValue(StatsReport::kStatsValueNameTransmitBitrate,
488 info.transmit_bitrate);
489 report->AddValue(StatsReport::kStatsValueNameBucketDelay,
490 info.bucket_delay);
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000491 if (level >= PeerConnectionInterface::kStatsOutputLevelDebug) {
492 report->AddValue(
493 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug,
494 info.total_received_propagation_delta_ms);
495 if (info.recent_received_propagation_delta_ms.size() > 0) {
496 report->AddValue(
497 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug,
498 info.recent_received_propagation_delta_ms);
499 report->AddValue(
500 StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug,
501 info.recent_received_packet_group_arrival_time_ms);
502 }
503 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000504}
505
wu@webrtc.org97077a32013-10-25 21:18:33 +0000506void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
507 StatsReport* report) {
508 report->timestamp = info.remote_stats[0].timestamp;
509 // TODO(hta): Extract some stats here.
510}
511
512void ExtractRemoteStats(const cricket::MediaReceiverInfo& info,
513 StatsReport* report) {
514 report->timestamp = info.remote_stats[0].timestamp;
515 // TODO(hta): Extract some stats here.
516}
517
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000518// Template to extract stats from a data vector.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000519// In order to use the template, the functions that are called from it,
520// ExtractStats and ExtractRemoteStats, must be defined and overloaded
521// for each type.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000522template<typename T>
523void ExtractStatsFromList(const std::vector<T>& data,
524 const std::string& transport_id,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000525 StatsCollector* collector,
526 StatsCollector::TrackDirection direction) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000527 typename std::vector<T>::const_iterator it = data.begin();
528 for (; it != data.end(); ++it) {
529 std::string id;
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000530 uint32 ssrc = it->ssrc();
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000531 // Each track can have stats for both local and remote objects.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000532 // TODO(hta): Handle the case of multiple SSRCs per object.
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000533 StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id,
534 direction);
535 if (report)
536 ExtractStats(*it, report);
537
wu@webrtc.org97077a32013-10-25 21:18:33 +0000538 if (it->remote_stats.size() > 0) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000539 report = collector->PrepareRemoteReport(ssrc, transport_id,
540 direction);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000541 if (!report) {
542 continue;
543 }
544 ExtractRemoteStats(*it, report);
545 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000546 }
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000547}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000548
549} // namespace
550
tommi@webrtc.org03505bc2014-07-14 20:15:26 +0000551StatsCollector::StatsCollector(WebRtcSession* session)
552 : session_(session), stats_gathering_started_(0) {
553 ASSERT(session_);
554}
555
556StatsCollector::~StatsCollector() {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000557}
558
559// Adds a MediaStream with tracks that can be used as a |selector| in a call
560// to GetStats.
561void StatsCollector::AddStream(MediaStreamInterface* stream) {
562 ASSERT(stream != NULL);
563
564 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(),
565 &reports_);
566 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(),
567 &reports_);
568}
569
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000570void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track,
571 uint32 ssrc) {
572 ASSERT(audio_track != NULL);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000573 for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin();
574 it != local_audio_tracks_.end(); ++it) {
575 ASSERT(it->first != audio_track || it->second != ssrc);
576 }
xians@webrtc.org01bda202014-07-09 07:38:38 +0000577
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000578 local_audio_tracks_.push_back(std::make_pair(audio_track, ssrc));
xians@webrtc.org01bda202014-07-09 07:38:38 +0000579
580 // Create the kStatsReportTypeTrack report for the new track if there is no
581 // report yet.
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000582 StatsReport* found = reports_.Find(
xians@webrtc.org01bda202014-07-09 07:38:38 +0000583 StatsId(StatsReport::kStatsReportTypeTrack, audio_track->id()));
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000584 if (!found)
xians@webrtc.org01bda202014-07-09 07:38:38 +0000585 AddTrackReport(&reports_, audio_track->id());
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000586}
587
588void StatsCollector::RemoveLocalAudioTrack(AudioTrackInterface* audio_track,
589 uint32 ssrc) {
590 ASSERT(audio_track != NULL);
591 for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin();
592 it != local_audio_tracks_.end(); ++it) {
593 if (it->first == audio_track && it->second == ssrc) {
594 local_audio_tracks_.erase(it);
595 return;
596 }
597 }
598
599 ASSERT(false);
600}
601
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000602void StatsCollector::GetStats(MediaStreamTrackInterface* track,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000603 StatsReports* reports) {
604 ASSERT(reports != NULL);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000605 ASSERT(reports->empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000606
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000607 if (!track) {
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000608 StatsSet::const_iterator it;
609 for (it = reports_.begin(); it != reports_.end(); ++it)
610 reports->push_back(&(*it));
611 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000612 }
613
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000614 StatsReport* report =
615 reports_.Find(StatsId(StatsReport::kStatsReportTypeSession,
616 session_->id()));
617 if (report)
618 reports->push_back(report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000619
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000620 report = reports_.Find(
621 StatsId(StatsReport::kStatsReportTypeTrack, track->id()));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000622
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000623 if (!report)
624 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000625
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000626 reports->push_back(report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000627
628 std::string track_id;
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000629 for (StatsSet::const_iterator it = reports_.begin(); it != reports_.end();
630 ++it) {
631 if (it->type != StatsReport::kStatsReportTypeSsrc)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000632 continue;
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000633
634 if (ExtractValueFromReport(*it,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000635 StatsReport::kStatsValueNameTrackId,
636 &track_id)) {
637 if (track_id == track->id()) {
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000638 reports->push_back(&(*it));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000639 }
640 }
641 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000642}
643
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000644void
645StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000646 double time_now = GetTimeNow();
647 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
648 // ms apart will be ignored.
649 const double kMinGatherStatsPeriod = 50;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000650 if (stats_gathering_started_ != 0 &&
651 stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000652 return;
653 }
654 stats_gathering_started_ = time_now;
655
656 if (session_) {
657 ExtractSessionInfo();
658 ExtractVoiceInfo();
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000659 ExtractVideoInfo(level);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000660 }
661}
662
wu@webrtc.org97077a32013-10-25 21:18:33 +0000663StatsReport* StatsCollector::PrepareLocalReport(
664 uint32 ssrc,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000665 const std::string& transport_id,
666 TrackDirection direction) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000667 const std::string ssrc_id = rtc::ToString<uint32>(ssrc);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000668 StatsReport* report = reports_.Find(
669 StatsId(StatsReport::kStatsReportTypeSsrc, ssrc_id, direction));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000670
xians@webrtc.org01bda202014-07-09 07:38:38 +0000671 // Use the ID of the track that is currently mapped to the SSRC, if any.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000672 std::string track_id;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000673 if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000674 if (!report) {
xians@webrtc.org01bda202014-07-09 07:38:38 +0000675 // The ssrc is not used by any track or existing report, return NULL
676 // in such case to indicate no report is prepared for the ssrc.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000677 return NULL;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000678 }
679
680 // The ssrc is not used by any existing track. Keeps the old track id
681 // since we want to report the stats for inactive ssrc.
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000682 ExtractValueFromReport(*report,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000683 StatsReport::kStatsValueNameTrackId,
684 &track_id);
685 }
686
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000687 report = GetOrCreateReport(
688 StatsReport::kStatsReportTypeSsrc, ssrc_id, direction);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000689
690 // Clear out stats from previous GatherStats calls if any.
xians@webrtc.org01bda202014-07-09 07:38:38 +0000691 // This is required since the report will be returned for the new values.
692 // Having the old values in the report will lead to multiple values with
693 // the same name.
694 // TODO(xians): Consider changing StatsReport to use map instead of vector.
695 report->values.clear();
696 report->timestamp = stats_gathering_started_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000697
698 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
699 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
700 // Add the mapping of SSRC to transport.
701 report->AddValue(StatsReport::kStatsValueNameTransportId,
702 transport_id);
703 return report;
704}
705
wu@webrtc.org97077a32013-10-25 21:18:33 +0000706StatsReport* StatsCollector::PrepareRemoteReport(
707 uint32 ssrc,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000708 const std::string& transport_id,
709 TrackDirection direction) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000710 const std::string ssrc_id = rtc::ToString<uint32>(ssrc);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000711 StatsReport* report = reports_.Find(
712 StatsId(StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction));
wu@webrtc.org97077a32013-10-25 21:18:33 +0000713
xians@webrtc.org01bda202014-07-09 07:38:38 +0000714 // Use the ID of the track that is currently mapped to the SSRC, if any.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000715 std::string track_id;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000716 if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000717 if (!report) {
xians@webrtc.org01bda202014-07-09 07:38:38 +0000718 // The ssrc is not used by any track or existing report, return NULL
719 // in such case to indicate no report is prepared for the ssrc.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000720 return NULL;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000721 }
722
723 // The ssrc is not used by any existing track. Keeps the old track id
724 // since we want to report the stats for inactive ssrc.
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000725 ExtractValueFromReport(*report,
wu@webrtc.org97077a32013-10-25 21:18:33 +0000726 StatsReport::kStatsValueNameTrackId,
727 &track_id);
728 }
729
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000730 report = GetOrCreateReport(
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000731 StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000732
733 // Clear out stats from previous GatherStats calls if any.
734 // The timestamp will be added later. Zero it for debugging.
735 report->values.clear();
736 report->timestamp = 0;
737
738 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
739 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
740 // Add the mapping of SSRC to transport.
741 report->AddValue(StatsReport::kStatsValueNameTransportId,
742 transport_id);
743 return report;
744}
745
wu@webrtc.org4551b792013-10-09 15:37:36 +0000746std::string StatsCollector::AddOneCertificateReport(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000747 const rtc::SSLCertificate* cert, const std::string& issuer_id) {
wu@webrtc.org4551b792013-10-09 15:37:36 +0000748 // TODO(bemasc): Move this computation to a helper class that caches these
749 // values to reduce CPU use in GetStats. This will require adding a fast
750 // SSLCertificate::Equals() method to detect certificate changes.
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000751
752 std::string digest_algorithm;
753 if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm))
754 return std::string();
755
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000756 rtc::scoped_ptr<rtc::SSLFingerprint> ssl_fingerprint(
757 rtc::SSLFingerprint::Create(digest_algorithm, cert));
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000758
759 // SSLFingerprint::Create can fail if the algorithm returned by
760 // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the
761 // implementation of SSLCertificate::ComputeDigest. This currently happens
762 // with MD5- and SHA-224-signed certificates when linked to libNSS.
763 if (!ssl_fingerprint)
764 return std::string();
765
wu@webrtc.org4551b792013-10-09 15:37:36 +0000766 std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
767
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000768 rtc::Buffer der_buffer;
wu@webrtc.org4551b792013-10-09 15:37:36 +0000769 cert->ToDER(&der_buffer);
770 std::string der_base64;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000771 rtc::Base64::EncodeFromArray(
wu@webrtc.org4551b792013-10-09 15:37:36 +0000772 der_buffer.data(), der_buffer.length(), &der_base64);
773
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000774 StatsReport* report = reports_.ReplaceOrAddNew(
775 StatsId(StatsReport::kStatsReportTypeCertificate, fingerprint));
776 report->type = StatsReport::kStatsReportTypeCertificate;
777 report->timestamp = stats_gathering_started_;
778 report->AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint);
779 report->AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm,
780 digest_algorithm);
781 report->AddValue(StatsReport::kStatsValueNameDer, der_base64);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000782 if (!issuer_id.empty())
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000783 report->AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id);
784 return report->id;
wu@webrtc.org4551b792013-10-09 15:37:36 +0000785}
786
787std::string StatsCollector::AddCertificateReports(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000788 const rtc::SSLCertificate* cert) {
wu@webrtc.org4551b792013-10-09 15:37:36 +0000789 // Produces a chain of StatsReports representing this certificate and the rest
790 // of its chain, and adds those reports to |reports_|. The return value is
791 // the id of the leaf report. The provided cert must be non-null, so at least
792 // one report will always be provided and the returned string will never be
793 // empty.
794 ASSERT(cert != NULL);
795
796 std::string issuer_id;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000797 rtc::scoped_ptr<rtc::SSLCertChain> chain;
wu@webrtc.org4551b792013-10-09 15:37:36 +0000798 if (cert->GetChain(chain.accept())) {
799 // This loop runs in reverse, i.e. from root to leaf, so that each
800 // certificate's issuer's report ID is known before the child certificate's
801 // report is generated. The root certificate does not have an issuer ID
802 // value.
803 for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000804 const rtc::SSLCertificate& cert_i = chain->Get(i);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000805 issuer_id = AddOneCertificateReport(&cert_i, issuer_id);
806 }
807 }
808 // Add the leaf certificate.
809 return AddOneCertificateReport(cert, issuer_id);
810}
811
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000812void StatsCollector::ExtractSessionInfo() {
813 // Extract information from the base session.
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000814 StatsReport* report = reports_.ReplaceOrAddNew(
815 StatsId(StatsReport::kStatsReportTypeSession, session_->id()));
816 report->type = StatsReport::kStatsReportTypeSession;
817 report->timestamp = stats_gathering_started_;
818 report->values.clear();
819 report->AddBoolean(StatsReport::kStatsValueNameInitiator,
820 session_->initiator());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000821
822 cricket::SessionStats stats;
823 if (session_->GetStats(&stats)) {
824 // Store the proxy map away for use in SSRC reporting.
825 proxy_to_transport_ = stats.proxy_to_transport;
826
827 for (cricket::TransportStatsMap::iterator transport_iter
828 = stats.transport_stats.begin();
829 transport_iter != stats.transport_stats.end(); ++transport_iter) {
wu@webrtc.org4551b792013-10-09 15:37:36 +0000830 // Attempt to get a copy of the certificates from the transport and
831 // expose them in stats reports. All channels in a transport share the
832 // same local and remote certificates.
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000833 //
834 // Note that Transport::GetIdentity and Transport::GetRemoteCertificate
835 // invoke method calls on the worker thread and block this thread, but
836 // messages are still processed on this thread, which may blow way the
837 // existing transports. So we cannot reuse |transport| after these calls.
wu@webrtc.org4551b792013-10-09 15:37:36 +0000838 std::string local_cert_report_id, remote_cert_report_id;
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000839
wu@webrtc.org4551b792013-10-09 15:37:36 +0000840 cricket::Transport* transport =
841 session_->GetTransport(transport_iter->second.content_name);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000842 rtc::scoped_ptr<rtc::SSLIdentity> identity;
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000843 if (transport && transport->GetIdentity(identity.accept())) {
844 local_cert_report_id =
845 AddCertificateReports(&(identity->certificate()));
wu@webrtc.org4551b792013-10-09 15:37:36 +0000846 }
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000847
848 transport = session_->GetTransport(transport_iter->second.content_name);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000849 rtc::scoped_ptr<rtc::SSLCertificate> cert;
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000850 if (transport && transport->GetRemoteCertificate(cert.accept())) {
851 remote_cert_report_id = AddCertificateReports(cert.get());
852 }
853
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000854 for (cricket::TransportChannelStatsList::iterator channel_iter
855 = transport_iter->second.channel_stats.begin();
856 channel_iter != transport_iter->second.channel_stats.end();
857 ++channel_iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000858 std::ostringstream ostc;
859 ostc << "Channel-" << transport_iter->second.content_name
860 << "-" << channel_iter->component;
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000861 StatsReport* channel_report = reports_.ReplaceOrAddNew(ostc.str());
862 channel_report->type = StatsReport::kStatsReportTypeComponent;
863 channel_report->timestamp = stats_gathering_started_;
864 channel_report->AddValue(StatsReport::kStatsValueNameComponent,
865 channel_iter->component);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000866 if (!local_cert_report_id.empty())
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000867 channel_report->AddValue(
wu@webrtc.org4551b792013-10-09 15:37:36 +0000868 StatsReport::kStatsValueNameLocalCertificateId,
869 local_cert_report_id);
870 if (!remote_cert_report_id.empty())
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000871 channel_report->AddValue(
wu@webrtc.org4551b792013-10-09 15:37:36 +0000872 StatsReport::kStatsValueNameRemoteCertificateId,
873 remote_cert_report_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000874 for (size_t i = 0;
875 i < channel_iter->connection_infos.size();
876 ++i) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000877 std::ostringstream ost;
878 ost << "Conn-" << transport_iter->first << "-"
879 << channel_iter->component << "-" << i;
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000880 StatsReport* report = reports_.ReplaceOrAddNew(ost.str());
881 report->type = StatsReport::kStatsReportTypeCandidatePair;
882 report->timestamp = stats_gathering_started_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000883 // Link from connection to its containing channel.
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000884 report->AddValue(StatsReport::kStatsValueNameChannelId,
885 channel_report->id);
886
887 const cricket::ConnectionInfo& info =
888 channel_iter->connection_infos[i];
889 report->AddValue(StatsReport::kStatsValueNameBytesSent,
890 info.sent_total_bytes);
guoweis@webrtc.org930e0042014-11-17 19:42:14 +0000891 report->AddValue(StatsReport::kStatsValueNameSendPacketsDiscarded,
892 info.sent_discarded_packets);
893 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
894 info.sent_total_packets);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000895 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
896 info.recv_total_bytes);
897 report->AddBoolean(StatsReport::kStatsValueNameWritable,
898 info.writable);
899 report->AddBoolean(StatsReport::kStatsValueNameReadable,
900 info.readable);
901 report->AddBoolean(StatsReport::kStatsValueNameActiveConnection,
902 info.best_connection);
903 report->AddValue(StatsReport::kStatsValueNameLocalAddress,
904 info.local_candidate.address().ToString());
905 report->AddValue(StatsReport::kStatsValueNameRemoteAddress,
906 info.remote_candidate.address().ToString());
907 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt);
908 report->AddValue(StatsReport::kStatsValueNameTransportType,
909 info.local_candidate.protocol());
910 report->AddValue(StatsReport::kStatsValueNameLocalCandidateType,
911 info.local_candidate.type());
912 report->AddValue(StatsReport::kStatsValueNameRemoteCandidateType,
913 info.remote_candidate.type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000914 }
915 }
916 }
917 }
918}
919
920void StatsCollector::ExtractVoiceInfo() {
921 if (!session_->voice_channel()) {
922 return;
923 }
924 cricket::VoiceMediaInfo voice_info;
925 if (!session_->voice_channel()->GetStats(&voice_info)) {
926 LOG(LS_ERROR) << "Failed to get voice channel stats.";
927 return;
928 }
929 std::string transport_id;
tommi@webrtc.org47218952014-07-15 19:22:37 +0000930 if (!GetTransportIdFromProxy(proxy_to_transport_,
931 session_->voice_channel()->content_name(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000932 &transport_id)) {
933 LOG(LS_ERROR) << "Failed to get transport name for proxy "
934 << session_->voice_channel()->content_name();
935 return;
936 }
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000937 ExtractStatsFromList(voice_info.receivers, transport_id, this, kReceiving);
938 ExtractStatsFromList(voice_info.senders, transport_id, this, kSending);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000939
940 UpdateStatsFromExistingLocalAudioTracks();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000941}
942
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000943void StatsCollector::ExtractVideoInfo(
944 PeerConnectionInterface::StatsOutputLevel level) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000945 if (!session_->video_channel()) {
946 return;
947 }
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000948 cricket::StatsOptions options;
949 options.include_received_propagation_stats =
950 (level >= PeerConnectionInterface::kStatsOutputLevelDebug) ?
951 true : false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000952 cricket::VideoMediaInfo video_info;
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000953 if (!session_->video_channel()->GetStats(options, &video_info)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000954 LOG(LS_ERROR) << "Failed to get video channel stats.";
955 return;
956 }
957 std::string transport_id;
tommi@webrtc.org47218952014-07-15 19:22:37 +0000958 if (!GetTransportIdFromProxy(proxy_to_transport_,
959 session_->video_channel()->content_name(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000960 &transport_id)) {
961 LOG(LS_ERROR) << "Failed to get transport name for proxy "
962 << session_->video_channel()->content_name();
963 return;
964 }
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000965 ExtractStatsFromList(video_info.receivers, transport_id, this, kReceiving);
966 ExtractStatsFromList(video_info.senders, transport_id, this, kSending);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000967 if (video_info.bw_estimations.size() != 1) {
968 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
969 } else {
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000970 StatsReport* report =
971 reports_.FindOrAddNew(StatsReport::kStatsReportVideoBweId);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000972 ExtractStats(
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000973 video_info.bw_estimations[0], stats_gathering_started_, level, report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000974 }
975}
976
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000977StatsReport* StatsCollector::GetReport(const std::string& type,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000978 const std::string& id,
979 TrackDirection direction) {
980 ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
981 type == StatsReport::kStatsReportTypeRemoteSsrc);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000982 return reports_.Find(StatsId(type, id, direction));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000983}
984
985StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000986 const std::string& id,
987 TrackDirection direction) {
988 ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
989 type == StatsReport::kStatsReportTypeRemoteSsrc);
990 StatsReport* report = GetReport(type, id, direction);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000991 if (report == NULL) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000992 std::string statsid = StatsId(type, id, direction);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000993 report = reports_.FindOrAddNew(statsid);
994 ASSERT(report->id == statsid);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000995 report->type = type;
wu@webrtc.org97077a32013-10-25 21:18:33 +0000996 }
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000997
wu@webrtc.org97077a32013-10-25 21:18:33 +0000998 return report;
999}
1000
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001001void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() {
1002 // Loop through the existing local audio tracks.
1003 for (LocalAudioTrackVector::const_iterator it = local_audio_tracks_.begin();
1004 it != local_audio_tracks_.end(); ++it) {
1005 AudioTrackInterface* track = it->first;
1006 uint32 ssrc = it->second;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001007 std::string ssrc_id = rtc::ToString<uint32>(ssrc);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001008 StatsReport* report = GetReport(StatsReport::kStatsReportTypeSsrc,
xians@webrtc.org4cb01282014-06-12 14:57:05 +00001009 ssrc_id,
1010 kSending);
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +00001011 if (report == NULL) {
1012 // This can happen if a local audio track is added to a stream on the
1013 // fly and the report has not been set up yet. Do nothing in this case.
1014 LOG(LS_ERROR) << "Stats report does not exist for ssrc " << ssrc;
1015 continue;
1016 }
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001017
1018 // The same ssrc can be used by both local and remote audio tracks.
1019 std::string track_id;
1020 if (!ExtractValueFromReport(*report,
1021 StatsReport::kStatsValueNameTrackId,
1022 &track_id) ||
1023 track_id != track->id()) {
1024 continue;
1025 }
1026
1027 UpdateReportFromAudioTrack(track, report);
1028 }
1029}
1030
1031void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track,
1032 StatsReport* report) {
1033 ASSERT(track != NULL);
1034 if (report == NULL)
1035 return;
1036
1037 int signal_level = 0;
1038 if (track->GetSignalLevel(&signal_level)) {
1039 report->ReplaceValue(StatsReport::kStatsValueNameAudioInputLevel,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001040 rtc::ToString<int>(signal_level));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001041 }
1042
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001043 rtc::scoped_refptr<AudioProcessorInterface> audio_processor(
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001044 track->GetAudioProcessor());
1045 if (audio_processor.get() == NULL)
1046 return;
1047
1048 AudioProcessorInterface::AudioProcessorStats stats;
1049 audio_processor->GetStats(&stats);
1050 report->ReplaceValue(StatsReport::kStatsValueNameTypingNoiseState,
1051 stats.typing_noise_detected ? "true" : "false");
1052 report->ReplaceValue(StatsReport::kStatsValueNameEchoReturnLoss,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001053 rtc::ToString<int>(stats.echo_return_loss));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001054 report->ReplaceValue(
1055 StatsReport::kStatsValueNameEchoReturnLossEnhancement,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001056 rtc::ToString<int>(stats.echo_return_loss_enhancement));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001057 report->ReplaceValue(StatsReport::kStatsValueNameEchoDelayMedian,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001058 rtc::ToString<int>(stats.echo_delay_median_ms));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001059 report->ReplaceValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001060 rtc::ToString<float>(stats.aec_quality_min));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001061 report->ReplaceValue(StatsReport::kStatsValueNameEchoDelayStdDev,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001062 rtc::ToString<int>(stats.echo_delay_std_ms));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +00001063}
1064
xians@webrtc.org4cb01282014-06-12 14:57:05 +00001065bool StatsCollector::GetTrackIdBySsrc(uint32 ssrc, std::string* track_id,
1066 TrackDirection direction) {
1067 if (direction == kSending) {
tommi@webrtc.org03505bc2014-07-14 20:15:26 +00001068 if (!session_->GetLocalTrackIdBySsrc(ssrc, track_id)) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +00001069 LOG(LS_WARNING) << "The SSRC " << ssrc
1070 << " is not associated with a sending track";
1071 return false;
1072 }
1073 } else {
1074 ASSERT(direction == kReceiving);
tommi@webrtc.org03505bc2014-07-14 20:15:26 +00001075 if (!session_->GetRemoteTrackIdBySsrc(ssrc, track_id)) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +00001076 LOG(LS_WARNING) << "The SSRC " << ssrc
1077 << " is not associated with a receiving track";
1078 return false;
1079 }
1080 }
1081
1082 return true;
1083}
1084
xians@webrtc.org01bda202014-07-09 07:38:38 +00001085void StatsCollector::ClearUpdateStatsCache() {
1086 stats_gathering_started_ = 0;
1087}
1088
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001089} // namespace webrtc