blob: 125e52cc2e901ebf8adb3d257281a292f4f3b8d6 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00003 * Copyright 2012 Google Inc.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00004 *
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
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +000038using rtc::scoped_ptr;
39
henrike@webrtc.org28e20752013-07-10 00:45:36 +000040namespace webrtc {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000041namespace {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000042
guoweis@webrtc.org950c5182014-12-16 23:01:31 +000043// The following is the enum RTCStatsIceCandidateType from
44// http://w3c.github.io/webrtc-stats/#rtcstatsicecandidatetype-enum such that
45// our stats report for ice candidate type could conform to that.
46const char STATSREPORT_LOCAL_PORT_TYPE[] = "host";
47const char STATSREPORT_STUN_PORT_TYPE[] = "serverreflexive";
48const char STATSREPORT_PRFLX_PORT_TYPE[] = "peerreflexive";
49const char STATSREPORT_RELAY_PORT_TYPE[] = "relayed";
50
51// Strings used by the stats collector to report adapter types. This fits the
52// general stype of http://w3c.github.io/webrtc-stats than what
53// AdapterTypeToString does.
54const char* STATSREPORT_ADAPTER_TYPE_ETHERNET = "lan";
55const char* STATSREPORT_ADAPTER_TYPE_WIFI = "wlan";
56const char* STATSREPORT_ADAPTER_TYPE_WWAN = "wwan";
57const char* STATSREPORT_ADAPTER_TYPE_VPN = "vpn";
58
tommi@webrtc.org47218952014-07-15 19:22:37 +000059double GetTimeNow() {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000060 return rtc::Timing::WallTimeNow() * rtc::kNumMillisecsPerSec;
tommi@webrtc.org47218952014-07-15 19:22:37 +000061}
62
63bool GetTransportIdFromProxy(const cricket::ProxyTransportMap& map,
64 const std::string& proxy,
65 std::string* transport) {
66 // TODO(hta): Remove handling of empty proxy name once tests do not use it.
67 if (proxy.empty()) {
68 transport->clear();
69 return true;
70 }
71
72 cricket::ProxyTransportMap::const_iterator found = map.find(proxy);
73 if (found == map.end()) {
74 LOG(LS_ERROR) << "No transport ID mapping for " << proxy;
75 return false;
76 }
77
tommi@webrtc.org47218952014-07-15 19:22:37 +000078 // Component 1 is always used for RTP.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +000079 scoped_ptr<StatsReport::Id> id(
80 StatsReport::NewComponentId(found->second, 1));
81 // TODO(tommi): Should |transport| simply be of type StatsReport::Id?
82 // When we support more value types than string (e.g. int, double, vector etc)
83 // we should also support a value type for Id.
84 *transport = id->ToString();
tommi@webrtc.org47218952014-07-15 19:22:37 +000085 return true;
86}
87
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +000088void AddTrackReport(StatsCollection* reports, const std::string& track_id) {
xians@webrtc.org01bda202014-07-09 07:38:38 +000089 // Adds an empty track report.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +000090 rtc::scoped_ptr<StatsReport::Id> id(
91 StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack, track_id));
92 StatsReport* report = reports->ReplaceOrAddNew(id.Pass());
tommi@webrtc.org5b06b062014-08-15 08:38:30 +000093 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
xians@webrtc.org01bda202014-07-09 07:38:38 +000094}
95
henrike@webrtc.org28e20752013-07-10 00:45:36 +000096template <class TrackVector>
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +000097void CreateTrackReports(const TrackVector& tracks, StatsCollection* reports) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000098 for (size_t j = 0; j < tracks.size(); ++j) {
99 webrtc::MediaStreamTrackInterface* track = tracks[j];
xians@webrtc.org01bda202014-07-09 07:38:38 +0000100 AddTrackReport(reports, track->id());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000101 }
102}
103
104void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
105 report->AddValue(StatsReport::kStatsValueNameAudioOutputLevel,
106 info.audio_level);
107 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
108 info.bytes_rcvd);
109 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
110 info.jitter_ms);
jiayl@webrtc.org11aab0e2014-03-07 18:56:26 +0000111 report->AddValue(StatsReport::kStatsValueNameJitterBufferMs,
112 info.jitter_buffer_ms);
113 report->AddValue(StatsReport::kStatsValueNamePreferredJitterBufferMs,
114 info.jitter_buffer_preferred_ms);
115 report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs,
116 info.delay_estimate_ms);
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000117 report->AddValue(StatsReport::kStatsValueNameExpandRate,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000118 rtc::ToString<float>(info.expand_rate));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000119 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
120 info.packets_rcvd);
121 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
122 info.packets_lost);
buildbot@webrtc.org3e01e0b2014-05-13 17:54:10 +0000123 report->AddValue(StatsReport::kStatsValueNameDecodingCTSG,
124 info.decoding_calls_to_silence_generator);
125 report->AddValue(StatsReport::kStatsValueNameDecodingCTN,
126 info.decoding_calls_to_neteq);
127 report->AddValue(StatsReport::kStatsValueNameDecodingNormal,
128 info.decoding_normal);
129 report->AddValue(StatsReport::kStatsValueNameDecodingPLC,
130 info.decoding_plc);
131 report->AddValue(StatsReport::kStatsValueNameDecodingCNG,
132 info.decoding_cng);
133 report->AddValue(StatsReport::kStatsValueNameDecodingPLCCNG,
134 info.decoding_plc_cng);
buildbot@webrtc.orgb525a9d2014-06-03 09:42:15 +0000135 report->AddValue(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
136 info.capture_start_ntp_time_ms);
buildbot@webrtc.org7e71b772014-06-13 01:14:01 +0000137 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000138}
139
140void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
141 report->AddValue(StatsReport::kStatsValueNameAudioInputLevel,
142 info.audio_level);
143 report->AddValue(StatsReport::kStatsValueNameBytesSent,
144 info.bytes_sent);
145 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
146 info.packets_sent);
henrike@webrtc.orgffe26202014-03-19 22:20:10 +0000147 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
148 info.packets_lost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000149 report->AddValue(StatsReport::kStatsValueNameJitterReceived,
150 info.jitter_ms);
151 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
152 report->AddValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000153 rtc::ToString<float>(info.aec_quality_min));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000154 report->AddValue(StatsReport::kStatsValueNameEchoDelayMedian,
155 info.echo_delay_median_ms);
156 report->AddValue(StatsReport::kStatsValueNameEchoDelayStdDev,
157 info.echo_delay_std_ms);
158 report->AddValue(StatsReport::kStatsValueNameEchoReturnLoss,
159 info.echo_return_loss);
160 report->AddValue(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
161 info.echo_return_loss_enhancement);
162 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
wu@webrtc.org967bfff2013-09-19 05:49:50 +0000163 report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
164 info.typing_noise_detected);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165}
166
167void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
168 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
169 info.bytes_rcvd);
170 report->AddValue(StatsReport::kStatsValueNamePacketsReceived,
171 info.packets_rcvd);
172 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
173 info.packets_lost);
174
175 report->AddValue(StatsReport::kStatsValueNameFirsSent,
176 info.firs_sent);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000177 report->AddValue(StatsReport::kStatsValueNamePlisSent,
178 info.plis_sent);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000179 report->AddValue(StatsReport::kStatsValueNameNacksSent,
180 info.nacks_sent);
181 report->AddValue(StatsReport::kStatsValueNameFrameWidthReceived,
182 info.frame_width);
183 report->AddValue(StatsReport::kStatsValueNameFrameHeightReceived,
184 info.frame_height);
185 report->AddValue(StatsReport::kStatsValueNameFrameRateReceived,
186 info.framerate_rcvd);
187 report->AddValue(StatsReport::kStatsValueNameFrameRateDecoded,
188 info.framerate_decoded);
189 report->AddValue(StatsReport::kStatsValueNameFrameRateOutput,
190 info.framerate_output);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000191
192 report->AddValue(StatsReport::kStatsValueNameDecodeMs,
193 info.decode_ms);
194 report->AddValue(StatsReport::kStatsValueNameMaxDecodeMs,
195 info.max_decode_ms);
196 report->AddValue(StatsReport::kStatsValueNameCurrentDelayMs,
197 info.current_delay_ms);
198 report->AddValue(StatsReport::kStatsValueNameTargetDelayMs,
199 info.target_delay_ms);
200 report->AddValue(StatsReport::kStatsValueNameJitterBufferMs,
201 info.jitter_buffer_ms);
202 report->AddValue(StatsReport::kStatsValueNameMinPlayoutDelayMs,
203 info.min_playout_delay_ms);
204 report->AddValue(StatsReport::kStatsValueNameRenderDelayMs,
205 info.render_delay_ms);
buildbot@webrtc.org0581f0b2014-05-06 21:36:31 +0000206
207 report->AddValue(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
208 info.capture_start_ntp_time_ms);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000209}
210
211void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
212 report->AddValue(StatsReport::kStatsValueNameBytesSent,
213 info.bytes_sent);
214 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
215 info.packets_sent);
henrike@webrtc.orgffe26202014-03-19 22:20:10 +0000216 report->AddValue(StatsReport::kStatsValueNamePacketsLost,
217 info.packets_lost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000218
219 report->AddValue(StatsReport::kStatsValueNameFirsReceived,
220 info.firs_rcvd);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000221 report->AddValue(StatsReport::kStatsValueNamePlisReceived,
222 info.plis_rcvd);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000223 report->AddValue(StatsReport::kStatsValueNameNacksReceived,
224 info.nacks_rcvd);
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000225 report->AddValue(StatsReport::kStatsValueNameFrameWidthInput,
226 info.input_frame_width);
227 report->AddValue(StatsReport::kStatsValueNameFrameHeightInput,
228 info.input_frame_height);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000229 report->AddValue(StatsReport::kStatsValueNameFrameWidthSent,
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000230 info.send_frame_width);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000231 report->AddValue(StatsReport::kStatsValueNameFrameHeightSent,
wu@webrtc.org987f2c92014-03-28 16:22:19 +0000232 info.send_frame_height);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000233 report->AddValue(StatsReport::kStatsValueNameFrameRateInput,
234 info.framerate_input);
235 report->AddValue(StatsReport::kStatsValueNameFrameRateSent,
236 info.framerate_sent);
237 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt_ms);
238 report->AddValue(StatsReport::kStatsValueNameCodecName, info.codec_name);
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +0000239 report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution,
240 (info.adapt_reason & 0x1) > 0);
241 report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution,
242 (info.adapt_reason & 0x2) > 0);
243 report->AddBoolean(StatsReport::kStatsValueNameViewLimitedResolution,
244 (info.adapt_reason & 0x4) > 0);
buildbot@webrtc.org71dffb72014-06-24 07:24:49 +0000245 report->AddValue(StatsReport::kStatsValueNameAdaptationChanges,
246 info.adapt_changes);
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000247 report->AddValue(StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms);
248 report->AddValue(StatsReport::kStatsValueNameCaptureJitterMs,
249 info.capture_jitter_ms);
wu@webrtc.org9caf2762013-12-11 18:25:07 +0000250 report->AddValue(StatsReport::kStatsValueNameCaptureQueueDelayMsPerS,
251 info.capture_queue_delay_ms_per_s);
252 report->AddValue(StatsReport::kStatsValueNameEncodeUsagePercent,
253 info.encode_usage_percent);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254}
255
256void ExtractStats(const cricket::BandwidthEstimationInfo& info,
257 double stats_gathering_started,
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000258 PeerConnectionInterface::StatsOutputLevel level,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000259 StatsReport* report) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000260 ASSERT(report->type() == StatsReport::kStatsReportTypeBwe);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000261
262 // Clear out stats from previous GatherStats calls if any.
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000263 if (report->timestamp() != stats_gathering_started) {
264 report->ResetValues();
265 report->set_timestamp(stats_gathering_started);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000266 }
267
268 report->AddValue(StatsReport::kStatsValueNameAvailableSendBandwidth,
269 info.available_send_bandwidth);
270 report->AddValue(StatsReport::kStatsValueNameAvailableReceiveBandwidth,
271 info.available_recv_bandwidth);
272 report->AddValue(StatsReport::kStatsValueNameTargetEncBitrate,
273 info.target_enc_bitrate);
274 report->AddValue(StatsReport::kStatsValueNameActualEncBitrate,
275 info.actual_enc_bitrate);
276 report->AddValue(StatsReport::kStatsValueNameRetransmitBitrate,
277 info.retransmit_bitrate);
278 report->AddValue(StatsReport::kStatsValueNameTransmitBitrate,
279 info.transmit_bitrate);
280 report->AddValue(StatsReport::kStatsValueNameBucketDelay,
281 info.bucket_delay);
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000282 if (level >= PeerConnectionInterface::kStatsOutputLevelDebug) {
283 report->AddValue(
284 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaSumDebug,
285 info.total_received_propagation_delta_ms);
286 if (info.recent_received_propagation_delta_ms.size() > 0) {
287 report->AddValue(
288 StatsReport::kStatsValueNameRecvPacketGroupPropagationDeltaDebug,
289 info.recent_received_propagation_delta_ms);
290 report->AddValue(
291 StatsReport::kStatsValueNameRecvPacketGroupArrivalTimeDebug,
292 info.recent_received_packet_group_arrival_time_ms);
293 }
294 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000295}
296
wu@webrtc.org97077a32013-10-25 21:18:33 +0000297void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
298 StatsReport* report) {
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000299 report->set_timestamp(info.remote_stats[0].timestamp);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000300 // TODO(hta): Extract some stats here.
301}
302
303void ExtractRemoteStats(const cricket::MediaReceiverInfo& info,
304 StatsReport* report) {
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000305 report->set_timestamp(info.remote_stats[0].timestamp);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000306 // TODO(hta): Extract some stats here.
307}
308
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000309// Template to extract stats from a data vector.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000310// In order to use the template, the functions that are called from it,
311// ExtractStats and ExtractRemoteStats, must be defined and overloaded
312// for each type.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000313template<typename T>
314void ExtractStatsFromList(const std::vector<T>& data,
315 const std::string& transport_id,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000316 StatsCollector* collector,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000317 StatsReport::Direction direction) {
318 for (const auto& d : data) {
319 uint32 ssrc = d.ssrc();
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000320 // Each track can have stats for both local and remote objects.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000321 // TODO(hta): Handle the case of multiple SSRCs per object.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000322 StatsReport* report = collector->PrepareReport(true, ssrc, transport_id,
323 direction);
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000324 if (report)
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000325 ExtractStats(d, report);
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000326
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000327 if (!d.remote_stats.empty()) {
328 report = collector->PrepareReport(false, ssrc, transport_id, direction);
329 if (report)
330 ExtractRemoteStats(d, report);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000331 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000332 }
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000333}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000334
335} // namespace
336
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000337const char* IceCandidateTypeToStatsType(const std::string& candidate_type) {
338 if (candidate_type == cricket::LOCAL_PORT_TYPE) {
339 return STATSREPORT_LOCAL_PORT_TYPE;
340 }
341 if (candidate_type == cricket::STUN_PORT_TYPE) {
342 return STATSREPORT_STUN_PORT_TYPE;
343 }
344 if (candidate_type == cricket::PRFLX_PORT_TYPE) {
345 return STATSREPORT_PRFLX_PORT_TYPE;
346 }
347 if (candidate_type == cricket::RELAY_PORT_TYPE) {
348 return STATSREPORT_RELAY_PORT_TYPE;
349 }
350 ASSERT(false);
351 return "unknown";
352}
353
354const char* AdapterTypeToStatsType(rtc::AdapterType type) {
355 switch (type) {
356 case rtc::ADAPTER_TYPE_UNKNOWN:
357 return "unknown";
358 case rtc::ADAPTER_TYPE_ETHERNET:
359 return STATSREPORT_ADAPTER_TYPE_ETHERNET;
360 case rtc::ADAPTER_TYPE_WIFI:
361 return STATSREPORT_ADAPTER_TYPE_WIFI;
362 case rtc::ADAPTER_TYPE_CELLULAR:
363 return STATSREPORT_ADAPTER_TYPE_WWAN;
364 case rtc::ADAPTER_TYPE_VPN:
365 return STATSREPORT_ADAPTER_TYPE_VPN;
366 default:
367 ASSERT(false);
368 return "";
369 }
370}
371
tommi@webrtc.org03505bc2014-07-14 20:15:26 +0000372StatsCollector::StatsCollector(WebRtcSession* session)
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000373 : session_(session),
374 stats_gathering_started_(0) {
tommi@webrtc.org03505bc2014-07-14 20:15:26 +0000375 ASSERT(session_);
376}
377
378StatsCollector::~StatsCollector() {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000379 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000380}
381
382// Adds a MediaStream with tracks that can be used as a |selector| in a call
383// to GetStats.
384void StatsCollector::AddStream(MediaStreamInterface* stream) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000385 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000386 ASSERT(stream != NULL);
387
388 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(),
389 &reports_);
390 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(),
391 &reports_);
392}
393
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000394void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track,
395 uint32 ssrc) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000396 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000397 ASSERT(audio_track != NULL);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000398 for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin();
399 it != local_audio_tracks_.end(); ++it) {
400 ASSERT(it->first != audio_track || it->second != ssrc);
401 }
xians@webrtc.org01bda202014-07-09 07:38:38 +0000402
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000403 local_audio_tracks_.push_back(std::make_pair(audio_track, ssrc));
xians@webrtc.org01bda202014-07-09 07:38:38 +0000404
405 // Create the kStatsReportTypeTrack report for the new track if there is no
406 // report yet.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000407 rtc::scoped_ptr<StatsReport::Id> id(
408 StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack,
409 audio_track->id()));
410 StatsReport* report = reports_.Find(*id.get());
411 if (!report) {
412 report = reports_.InsertNew(id.Pass());
413 report->AddValue(StatsReport::kStatsValueNameTrackId, audio_track->id());
414 }
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000415}
416
417void StatsCollector::RemoveLocalAudioTrack(AudioTrackInterface* audio_track,
418 uint32 ssrc) {
419 ASSERT(audio_track != NULL);
420 for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin();
421 it != local_audio_tracks_.end(); ++it) {
422 if (it->first == audio_track && it->second == ssrc) {
423 local_audio_tracks_.erase(it);
424 return;
425 }
426 }
427
428 ASSERT(false);
429}
430
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000431void StatsCollector::GetStats(MediaStreamTrackInterface* track,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000432 StatsReports* reports) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000433 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000434 ASSERT(reports != NULL);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000435 ASSERT(reports->empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000436
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000437 if (!track) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000438 reports->reserve(reports_.size());
439 for (auto* r : reports_)
440 reports->push_back(r);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000441 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000442 }
443
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000444 StatsReport* report = reports_.Find(StatsReport::NewTypedId(
445 StatsReport::kStatsReportTypeSession, session_->id()));
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000446 if (report)
447 reports->push_back(report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000448
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000449 report = reports_.Find(StatsReport::NewTypedId(
450 StatsReport::kStatsReportTypeTrack, track->id()));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000451
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000452 if (!report)
453 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000454
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000455 reports->push_back(report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000456
457 std::string track_id;
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000458 for (const auto* r : reports_) {
459 if (r->type() != StatsReport::kStatsReportTypeSsrc)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000460 continue;
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000461
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000462 const StatsReport::Value* v =
463 r->FindValue(StatsReport::kStatsValueNameTrackId);
464 if (v && v->value == track->id())
465 reports->push_back(r);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000466 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000467}
468
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000469void
470StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000471 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000472 double time_now = GetTimeNow();
473 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
474 // ms apart will be ignored.
475 const double kMinGatherStatsPeriod = 50;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000476 if (stats_gathering_started_ != 0 &&
477 stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000478 return;
479 }
480 stats_gathering_started_ = time_now;
481
482 if (session_) {
483 ExtractSessionInfo();
484 ExtractVoiceInfo();
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000485 ExtractVideoInfo(level);
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000486 ExtractDataInfo();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000487 }
488}
489
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000490StatsReport* StatsCollector::PrepareReport(
491 bool local,
wu@webrtc.org97077a32013-10-25 21:18:33 +0000492 uint32 ssrc,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000493 const std::string& transport_id,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000494 StatsReport::Direction direction) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000495 ASSERT(session_->signaling_thread()->IsCurrent());
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000496 const std::string ssrc_id = rtc::ToString<uint32>(ssrc);
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000497 rtc::scoped_ptr<StatsReport::Id> id(StatsReport::NewIdWithDirection(
498 local ? StatsReport::kStatsReportTypeSsrc :
499 StatsReport::kStatsReportTypeRemoteSsrc,
500 ssrc_id, direction));
501 StatsReport* report = reports_.Find(*id.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000502
xians@webrtc.org01bda202014-07-09 07:38:38 +0000503 // Use the ID of the track that is currently mapped to the SSRC, if any.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000504 std::string track_id;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000505 if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000506 if (!report) {
xians@webrtc.org01bda202014-07-09 07:38:38 +0000507 // The ssrc is not used by any track or existing report, return NULL
508 // in such case to indicate no report is prepared for the ssrc.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000509 return NULL;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000510 }
511
512 // The ssrc is not used by any existing track. Keeps the old track id
513 // since we want to report the stats for inactive ssrc.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000514 const StatsReport::Value* v =
515 report->FindValue(StatsReport::kStatsValueNameTrackId);
516 if (v)
517 track_id = v->value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000518 }
519
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000520 if (!report) {
521 report = reports_.InsertNew(id.Pass());
522 } else {
523 // Clear out stats from previous GatherStats calls if any.
524 // This is required since the report will be returned for the new values.
525 // Having the old values in the report will lead to multiple values with
526 // the same name.
527 // TODO(tommi): This seems to be pretty wasteful if some of these values
528 // have not changed (we basically throw them away just to recreate them).
529 // Figure out a way to not have to do this while not breaking the existing
530 // functionality.
531 report->ResetValues();
532 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000533
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000534 ASSERT(report->values().empty());
535 // FYI - for remote reports, the timestamp will be overwritten later.
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000536 report->set_timestamp(stats_gathering_started_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000537
538 report->AddValue(StatsReport::kStatsValueNameSsrc, ssrc_id);
539 report->AddValue(StatsReport::kStatsValueNameTrackId, track_id);
540 // Add the mapping of SSRC to transport.
541 report->AddValue(StatsReport::kStatsValueNameTransportId,
542 transport_id);
543 return report;
544}
545
wu@webrtc.org4551b792013-10-09 15:37:36 +0000546std::string StatsCollector::AddOneCertificateReport(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000547 const rtc::SSLCertificate* cert, const std::string& issuer_id) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000548 ASSERT(session_->signaling_thread()->IsCurrent());
549
wu@webrtc.org4551b792013-10-09 15:37:36 +0000550 // TODO(bemasc): Move this computation to a helper class that caches these
551 // values to reduce CPU use in GetStats. This will require adding a fast
552 // SSLCertificate::Equals() method to detect certificate changes.
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000553
554 std::string digest_algorithm;
555 if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm))
556 return std::string();
557
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000558 rtc::scoped_ptr<rtc::SSLFingerprint> ssl_fingerprint(
559 rtc::SSLFingerprint::Create(digest_algorithm, cert));
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000560
561 // SSLFingerprint::Create can fail if the algorithm returned by
562 // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the
563 // implementation of SSLCertificate::ComputeDigest. This currently happens
564 // with MD5- and SHA-224-signed certificates when linked to libNSS.
565 if (!ssl_fingerprint)
566 return std::string();
567
wu@webrtc.org4551b792013-10-09 15:37:36 +0000568 std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
569
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000570 rtc::Buffer der_buffer;
wu@webrtc.org4551b792013-10-09 15:37:36 +0000571 cert->ToDER(&der_buffer);
572 std::string der_base64;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000573 rtc::Base64::EncodeFromArray(
wu@webrtc.org4551b792013-10-09 15:37:36 +0000574 der_buffer.data(), der_buffer.length(), &der_base64);
575
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000576 rtc::scoped_ptr<StatsReport::Id> id(
577 StatsReport::NewTypedId(
578 StatsReport::kStatsReportTypeCertificate, fingerprint));
579 StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000580 report->set_timestamp(stats_gathering_started_);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000581 report->AddValue(StatsReport::kStatsValueNameFingerprint, fingerprint);
582 report->AddValue(StatsReport::kStatsValueNameFingerprintAlgorithm,
583 digest_algorithm);
584 report->AddValue(StatsReport::kStatsValueNameDer, der_base64);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000585 if (!issuer_id.empty())
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000586 report->AddValue(StatsReport::kStatsValueNameIssuerId, issuer_id);
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000587 // TODO(tommi): Can we avoid this?
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000588 return report->id().ToString();
wu@webrtc.org4551b792013-10-09 15:37:36 +0000589}
590
591std::string StatsCollector::AddCertificateReports(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000592 const rtc::SSLCertificate* cert) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000593 ASSERT(session_->signaling_thread()->IsCurrent());
wu@webrtc.org4551b792013-10-09 15:37:36 +0000594 // Produces a chain of StatsReports representing this certificate and the rest
595 // of its chain, and adds those reports to |reports_|. The return value is
596 // the id of the leaf report. The provided cert must be non-null, so at least
597 // one report will always be provided and the returned string will never be
598 // empty.
599 ASSERT(cert != NULL);
600
601 std::string issuer_id;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000602 rtc::scoped_ptr<rtc::SSLCertChain> chain;
wu@webrtc.org4551b792013-10-09 15:37:36 +0000603 if (cert->GetChain(chain.accept())) {
604 // This loop runs in reverse, i.e. from root to leaf, so that each
605 // certificate's issuer's report ID is known before the child certificate's
606 // report is generated. The root certificate does not have an issuer ID
607 // value.
608 for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000609 const rtc::SSLCertificate& cert_i = chain->Get(i);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000610 issuer_id = AddOneCertificateReport(&cert_i, issuer_id);
611 }
612 }
613 // Add the leaf certificate.
614 return AddOneCertificateReport(cert, issuer_id);
615}
616
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000617std::string StatsCollector::AddCandidateReport(
618 const cricket::Candidate& candidate,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000619 bool local) {
620 scoped_ptr<StatsReport::Id> id(
621 StatsReport::NewCandidateId(local, candidate.id()));
622 StatsReport* report = reports_.Find(*id.get());
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000623 if (!report) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000624 report = reports_.InsertNew(id.Pass());
625 report->set_timestamp(stats_gathering_started_);
626 if (local) {
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000627 report->AddValue(StatsReport::kStatsValueNameCandidateNetworkType,
628 AdapterTypeToStatsType(candidate.network_type()));
629 }
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000630 report->AddValue(StatsReport::kStatsValueNameCandidateIPAddress,
631 candidate.address().ipaddr().ToString());
632 report->AddValue(StatsReport::kStatsValueNameCandidatePortNumber,
633 candidate.address().PortAsString());
634 report->AddValue(StatsReport::kStatsValueNameCandidatePriority,
635 candidate.priority());
636 report->AddValue(StatsReport::kStatsValueNameCandidateType,
637 IceCandidateTypeToStatsType(candidate.type()));
638 report->AddValue(StatsReport::kStatsValueNameCandidateTransportType,
639 candidate.protocol());
640 }
641
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000642 // TODO(tommi): Necessary?
643 return report->id().ToString();
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000644}
645
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000646void StatsCollector::ExtractSessionInfo() {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000647 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000648 // Extract information from the base session.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000649 rtc::scoped_ptr<StatsReport::Id> id(
650 StatsReport::NewTypedId(
651 StatsReport::kStatsReportTypeSession, session_->id()));
652 StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000653 report->set_timestamp(stats_gathering_started_);
654 report->ResetValues();
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000655 report->AddBoolean(StatsReport::kStatsValueNameInitiator,
656 session_->initiator());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000657
658 cricket::SessionStats stats;
659 if (session_->GetStats(&stats)) {
660 // Store the proxy map away for use in SSRC reporting.
661 proxy_to_transport_ = stats.proxy_to_transport;
662
663 for (cricket::TransportStatsMap::iterator transport_iter
664 = stats.transport_stats.begin();
665 transport_iter != stats.transport_stats.end(); ++transport_iter) {
wu@webrtc.org4551b792013-10-09 15:37:36 +0000666 // Attempt to get a copy of the certificates from the transport and
667 // expose them in stats reports. All channels in a transport share the
668 // same local and remote certificates.
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000669 //
670 // Note that Transport::GetIdentity and Transport::GetRemoteCertificate
671 // invoke method calls on the worker thread and block this thread, but
672 // messages are still processed on this thread, which may blow way the
673 // existing transports. So we cannot reuse |transport| after these calls.
wu@webrtc.org4551b792013-10-09 15:37:36 +0000674 std::string local_cert_report_id, remote_cert_report_id;
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000675
wu@webrtc.org4551b792013-10-09 15:37:36 +0000676 cricket::Transport* transport =
677 session_->GetTransport(transport_iter->second.content_name);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000678 rtc::scoped_ptr<rtc::SSLIdentity> identity;
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000679 if (transport && transport->GetIdentity(identity.accept())) {
680 local_cert_report_id =
681 AddCertificateReports(&(identity->certificate()));
wu@webrtc.org4551b792013-10-09 15:37:36 +0000682 }
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000683
684 transport = session_->GetTransport(transport_iter->second.content_name);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000685 rtc::scoped_ptr<rtc::SSLCertificate> cert;
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000686 if (transport && transport->GetRemoteCertificate(cert.accept())) {
687 remote_cert_report_id = AddCertificateReports(cert.get());
688 }
689
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000690 for (cricket::TransportChannelStatsList::iterator channel_iter
691 = transport_iter->second.channel_stats.begin();
692 channel_iter != transport_iter->second.channel_stats.end();
693 ++channel_iter) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000694 rtc::scoped_ptr<StatsReport::Id> id(
695 StatsReport::NewComponentId(transport_iter->second.content_name,
696 channel_iter->component));
697 StatsReport* channel_report = reports_.ReplaceOrAddNew(id.Pass());
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000698 channel_report->set_timestamp(stats_gathering_started_);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000699 channel_report->AddValue(StatsReport::kStatsValueNameComponent,
700 channel_iter->component);
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000701 if (!local_cert_report_id.empty()) {
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000702 channel_report->AddValue(
wu@webrtc.org4551b792013-10-09 15:37:36 +0000703 StatsReport::kStatsValueNameLocalCertificateId,
704 local_cert_report_id);
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000705 }
706 if (!remote_cert_report_id.empty()) {
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000707 channel_report->AddValue(
wu@webrtc.org4551b792013-10-09 15:37:36 +0000708 StatsReport::kStatsValueNameRemoteCertificateId,
709 remote_cert_report_id);
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000710 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000711 for (size_t i = 0;
712 i < channel_iter->connection_infos.size();
713 ++i) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000714 rtc::scoped_ptr<StatsReport::Id> id(
715 StatsReport::NewCandidatePairId(transport_iter->first,
716 channel_iter->component, static_cast<int>(i)));
717 StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000718 report->set_timestamp(stats_gathering_started_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000719 // Link from connection to its containing channel.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000720 // TODO(tommi): Any way to avoid ToString here?
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000721 report->AddValue(StatsReport::kStatsValueNameChannelId,
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000722 channel_report->id().ToString());
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000723
724 const cricket::ConnectionInfo& info =
725 channel_iter->connection_infos[i];
726 report->AddValue(StatsReport::kStatsValueNameBytesSent,
727 info.sent_total_bytes);
guoweis@webrtc.org930e0042014-11-17 19:42:14 +0000728 report->AddValue(StatsReport::kStatsValueNameSendPacketsDiscarded,
729 info.sent_discarded_packets);
730 report->AddValue(StatsReport::kStatsValueNamePacketsSent,
731 info.sent_total_packets);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000732 report->AddValue(StatsReport::kStatsValueNameBytesReceived,
733 info.recv_total_bytes);
734 report->AddBoolean(StatsReport::kStatsValueNameWritable,
735 info.writable);
736 report->AddBoolean(StatsReport::kStatsValueNameReadable,
737 info.readable);
738 report->AddBoolean(StatsReport::kStatsValueNameActiveConnection,
739 info.best_connection);
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000740 report->AddValue(StatsReport::kStatsValueNameLocalCandidateId,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000741 AddCandidateReport(info.local_candidate, true));
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000742 report->AddValue(
743 StatsReport::kStatsValueNameRemoteCandidateId,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000744 AddCandidateReport(info.remote_candidate, false));
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000745 report->AddValue(StatsReport::kStatsValueNameLocalAddress,
746 info.local_candidate.address().ToString());
747 report->AddValue(StatsReport::kStatsValueNameRemoteAddress,
748 info.remote_candidate.address().ToString());
749 report->AddValue(StatsReport::kStatsValueNameRtt, info.rtt);
750 report->AddValue(StatsReport::kStatsValueNameTransportType,
751 info.local_candidate.protocol());
752 report->AddValue(StatsReport::kStatsValueNameLocalCandidateType,
753 info.local_candidate.type());
754 report->AddValue(StatsReport::kStatsValueNameRemoteCandidateType,
755 info.remote_candidate.type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000756 }
757 }
758 }
759 }
760}
761
762void StatsCollector::ExtractVoiceInfo() {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000763 ASSERT(session_->signaling_thread()->IsCurrent());
764
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000765 if (!session_->voice_channel()) {
766 return;
767 }
768 cricket::VoiceMediaInfo voice_info;
769 if (!session_->voice_channel()->GetStats(&voice_info)) {
770 LOG(LS_ERROR) << "Failed to get voice channel stats.";
771 return;
772 }
773 std::string transport_id;
tommi@webrtc.org47218952014-07-15 19:22:37 +0000774 if (!GetTransportIdFromProxy(proxy_to_transport_,
775 session_->voice_channel()->content_name(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000776 &transport_id)) {
777 LOG(LS_ERROR) << "Failed to get transport name for proxy "
778 << session_->voice_channel()->content_name();
779 return;
780 }
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000781 ExtractStatsFromList(voice_info.receivers, transport_id, this,
782 StatsReport::kReceive);
783 ExtractStatsFromList(voice_info.senders, transport_id, this,
784 StatsReport::kSend);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000785
786 UpdateStatsFromExistingLocalAudioTracks();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000787}
788
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000789void StatsCollector::ExtractVideoInfo(
790 PeerConnectionInterface::StatsOutputLevel level) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000791 ASSERT(session_->signaling_thread()->IsCurrent());
792
793 if (!session_->video_channel())
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000794 return;
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000795
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000796 cricket::StatsOptions options;
797 options.include_received_propagation_stats =
798 (level >= PeerConnectionInterface::kStatsOutputLevelDebug) ?
799 true : false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000800 cricket::VideoMediaInfo video_info;
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000801 if (!session_->video_channel()->GetStats(options, &video_info)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000802 LOG(LS_ERROR) << "Failed to get video channel stats.";
803 return;
804 }
805 std::string transport_id;
tommi@webrtc.org47218952014-07-15 19:22:37 +0000806 if (!GetTransportIdFromProxy(proxy_to_transport_,
807 session_->video_channel()->content_name(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000808 &transport_id)) {
809 LOG(LS_ERROR) << "Failed to get transport name for proxy "
810 << session_->video_channel()->content_name();
811 return;
812 }
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000813 ExtractStatsFromList(video_info.receivers, transport_id, this,
814 StatsReport::kReceive);
815 ExtractStatsFromList(video_info.senders, transport_id, this,
816 StatsReport::kSend);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000817 if (video_info.bw_estimations.size() != 1) {
818 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
819 } else {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000820 rtc::scoped_ptr<StatsReport::Id> report_id(
821 StatsReport::NewBandwidthEstimationId());
822 StatsReport* report = reports_.FindOrAddNew(report_id.Pass());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000823 ExtractStats(
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000824 video_info.bw_estimations[0], stats_gathering_started_, level, report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000825 }
826}
827
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000828void StatsCollector::ExtractDataInfo() {
829 ASSERT(session_->signaling_thread()->IsCurrent());
830
831 for (const auto& dc :
832 session_->mediastream_signaling()->sctp_data_channels()) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000833 rtc::scoped_ptr<StatsReport::Id> id(
834 StatsReport::NewTypedId(StatsReport::kStatsReportTypeDataChannel,
835 dc->label()));
836 StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000837 report->AddValue(StatsReport::kStatsValueNameLabel, dc->label());
838 report->AddValue(StatsReport::kStatsValueNameDataChannelId, dc->id());
839 report->AddValue(StatsReport::kStatsValueNameProtocol, dc->protocol());
840 report->AddValue(StatsReport::kStatsValueNameState,
841 DataChannelInterface::DataStateString(dc->state()));
842 }
843}
844
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000845StatsReport* StatsCollector::GetReport(const StatsReport::StatsType& type,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000846 const std::string& id,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000847 StatsReport::Direction direction) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000848 ASSERT(session_->signaling_thread()->IsCurrent());
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000849 ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
850 type == StatsReport::kStatsReportTypeRemoteSsrc);
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000851 return reports_.Find(StatsReport::NewIdWithDirection(type, id, direction));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000852}
853
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000854StatsReport* StatsCollector::GetOrCreateReport(
855 const StatsReport::StatsType& type,
856 const std::string& id,
857 StatsReport::Direction direction) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000858 ASSERT(session_->signaling_thread()->IsCurrent());
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000859 ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
860 type == StatsReport::kStatsReportTypeRemoteSsrc);
861 StatsReport* report = GetReport(type, id, direction);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000862 if (report == NULL) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000863 rtc::scoped_ptr<StatsReport::Id> report_id(
864 StatsReport::NewIdWithDirection(type, id, direction));
865 report = reports_.InsertNew(report_id.Pass());
wu@webrtc.org97077a32013-10-25 21:18:33 +0000866 }
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000867
wu@webrtc.org97077a32013-10-25 21:18:33 +0000868 return report;
869}
870
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000871void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000872 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000873 // Loop through the existing local audio tracks.
874 for (LocalAudioTrackVector::const_iterator it = local_audio_tracks_.begin();
875 it != local_audio_tracks_.end(); ++it) {
876 AudioTrackInterface* track = it->first;
877 uint32 ssrc = it->second;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000878 std::string ssrc_id = rtc::ToString<uint32>(ssrc);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000879 StatsReport* report = GetReport(StatsReport::kStatsReportTypeSsrc,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000880 ssrc_id,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000881 StatsReport::kSend);
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000882 if (report == NULL) {
883 // This can happen if a local audio track is added to a stream on the
884 // fly and the report has not been set up yet. Do nothing in this case.
885 LOG(LS_ERROR) << "Stats report does not exist for ssrc " << ssrc;
886 continue;
887 }
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000888
889 // The same ssrc can be used by both local and remote audio tracks.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000890 const StatsReport::Value* v =
891 report->FindValue(StatsReport::kStatsValueNameTrackId);
892 if (!v || v->value != track->id())
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000893 continue;
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000894
895 UpdateReportFromAudioTrack(track, report);
896 }
897}
898
899void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track,
900 StatsReport* report) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000901 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000902 ASSERT(track != NULL);
903 if (report == NULL)
904 return;
905
906 int signal_level = 0;
907 if (track->GetSignalLevel(&signal_level)) {
908 report->ReplaceValue(StatsReport::kStatsValueNameAudioInputLevel,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000909 rtc::ToString<int>(signal_level));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000910 }
911
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000912 rtc::scoped_refptr<AudioProcessorInterface> audio_processor(
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000913 track->GetAudioProcessor());
914 if (audio_processor.get() == NULL)
915 return;
916
917 AudioProcessorInterface::AudioProcessorStats stats;
918 audio_processor->GetStats(&stats);
919 report->ReplaceValue(StatsReport::kStatsValueNameTypingNoiseState,
920 stats.typing_noise_detected ? "true" : "false");
921 report->ReplaceValue(StatsReport::kStatsValueNameEchoReturnLoss,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000922 rtc::ToString<int>(stats.echo_return_loss));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000923 report->ReplaceValue(
924 StatsReport::kStatsValueNameEchoReturnLossEnhancement,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000925 rtc::ToString<int>(stats.echo_return_loss_enhancement));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000926 report->ReplaceValue(StatsReport::kStatsValueNameEchoDelayMedian,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000927 rtc::ToString<int>(stats.echo_delay_median_ms));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000928 report->ReplaceValue(StatsReport::kStatsValueNameEchoCancellationQualityMin,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000929 rtc::ToString<float>(stats.aec_quality_min));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000930 report->ReplaceValue(StatsReport::kStatsValueNameEchoDelayStdDev,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000931 rtc::ToString<int>(stats.echo_delay_std_ms));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000932}
933
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000934bool StatsCollector::GetTrackIdBySsrc(uint32 ssrc, std::string* track_id,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000935 StatsReport::Direction direction) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000936 ASSERT(session_->signaling_thread()->IsCurrent());
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000937 if (direction == StatsReport::kSend) {
tommi@webrtc.org03505bc2014-07-14 20:15:26 +0000938 if (!session_->GetLocalTrackIdBySsrc(ssrc, track_id)) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000939 LOG(LS_WARNING) << "The SSRC " << ssrc
940 << " is not associated with a sending track";
941 return false;
942 }
943 } else {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000944 ASSERT(direction == StatsReport::kReceive);
tommi@webrtc.org03505bc2014-07-14 20:15:26 +0000945 if (!session_->GetRemoteTrackIdBySsrc(ssrc, track_id)) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000946 LOG(LS_WARNING) << "The SSRC " << ssrc
947 << " is not associated with a receiving track";
948 return false;
949 }
950 }
951
952 return true;
953}
954
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000955void StatsCollector::ClearUpdateStatsCacheForTest() {
xians@webrtc.org01bda202014-07-09 07:38:38 +0000956 stats_gathering_started_ = 0;
957}
958
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000959} // namespace webrtc