blob: badc6091b867c0bd9bfe385287638d857d278252 [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";
phoglund@webrtc.org006521d2015-02-12 09:23:59 +000058const char* STATSREPORT_ADAPTER_TYPE_LOOPBACK = "loopback";
guoweis@webrtc.org950c5182014-12-16 23:01:31 +000059
tommi@webrtc.org92f40182015-03-04 15:25:19 +000060struct FloatForAdd {
61 const StatsReport::StatsValueName name;
62 const float& value;
63};
64
65struct IntForAdd {
66 const StatsReport::StatsValueName name;
67 const int value;
68};
69
tommi@webrtc.org47218952014-07-15 19:22:37 +000070bool GetTransportIdFromProxy(const cricket::ProxyTransportMap& map,
71 const std::string& proxy,
72 std::string* transport) {
73 // TODO(hta): Remove handling of empty proxy name once tests do not use it.
74 if (proxy.empty()) {
75 transport->clear();
76 return true;
77 }
78
79 cricket::ProxyTransportMap::const_iterator found = map.find(proxy);
80 if (found == map.end()) {
81 LOG(LS_ERROR) << "No transport ID mapping for " << proxy;
82 return false;
83 }
84
tommi@webrtc.org47218952014-07-15 19:22:37 +000085 // Component 1 is always used for RTP.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +000086 scoped_ptr<StatsReport::Id> id(
87 StatsReport::NewComponentId(found->second, 1));
88 // TODO(tommi): Should |transport| simply be of type StatsReport::Id?
89 // When we support more value types than string (e.g. int, double, vector etc)
90 // we should also support a value type for Id.
91 *transport = id->ToString();
tommi@webrtc.org47218952014-07-15 19:22:37 +000092 return true;
93}
94
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +000095void AddTrackReport(StatsCollection* reports, const std::string& track_id) {
xians@webrtc.org01bda202014-07-09 07:38:38 +000096 // Adds an empty track report.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +000097 rtc::scoped_ptr<StatsReport::Id> id(
98 StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack, track_id));
99 StatsReport* report = reports->ReplaceOrAddNew(id.Pass());
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000100 report->AddString(StatsReport::kStatsValueNameTrackId, track_id);
xians@webrtc.org01bda202014-07-09 07:38:38 +0000101}
102
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000103template <class TrackVector>
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000104void CreateTrackReports(const TrackVector& tracks, StatsCollection* reports) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000105 for (size_t j = 0; j < tracks.size(); ++j) {
106 webrtc::MediaStreamTrackInterface* track = tracks[j];
xians@webrtc.org01bda202014-07-09 07:38:38 +0000107 AddTrackReport(reports, track->id());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000108 }
109}
110
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000111void ExtractCommonSendProperties(const cricket::MediaSenderInfo& info,
112 StatsReport* report) {
113 report->AddString(StatsReport::kStatsValueNameCodecName, info.codec_name);
114 report->AddInt64(StatsReport::kStatsValueNameBytesSent, info.bytes_sent);
115 report->AddInt64(StatsReport::kStatsValueNameRtt, info.rtt_ms);
116}
117
118void SetAudioProcessingStats(StatsReport* report, int signal_level,
119 bool typing_noise_detected, int echo_return_loss,
120 int echo_return_loss_enhancement, int echo_delay_median_ms,
121 float aec_quality_min, int echo_delay_std_ms) {
122 report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
123 typing_noise_detected);
124 report->AddFloat(StatsReport::kStatsValueNameEchoCancellationQualityMin,
125 aec_quality_min);
126 // Don't overwrite the previous signal level if it's not available now.
127 if (signal_level >= 0)
128 report->AddInt(StatsReport::kStatsValueNameAudioInputLevel, signal_level);
129 const IntForAdd ints[] = {
130 { StatsReport::kStatsValueNameEchoReturnLoss, echo_return_loss },
131 { StatsReport::kStatsValueNameEchoReturnLossEnhancement,
132 echo_return_loss_enhancement },
133 { StatsReport::kStatsValueNameEchoDelayMedian, echo_delay_median_ms },
134 { StatsReport::kStatsValueNameEchoDelayStdDev, echo_delay_std_ms },
135 };
136 for (const auto& i : ints)
137 report->AddInt(i.name, i.value);
138}
139
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000140void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000141 const FloatForAdd floats[] = {
142 { StatsReport::kStatsValueNameExpandRate, info.expand_rate },
143 { StatsReport::kStatsValueNameSecondaryDecodedRate,
144 info.secondary_decoded_rate },
145 { StatsReport::kStatsValueNameSpeechExpandRate, info.speech_expand_rate },
146 };
147
148 const IntForAdd ints[] = {
149 { StatsReport::kStatsValueNameAudioOutputLevel, info.audio_level },
150 { StatsReport::kStatsValueNameCurrentDelayMs, info.delay_estimate_ms },
151 { StatsReport::kStatsValueNameDecodingCNG, info.decoding_cng },
152 { StatsReport::kStatsValueNameDecodingCTN, info.decoding_calls_to_neteq },
153 { StatsReport::kStatsValueNameDecodingCTSG,
154 info.decoding_calls_to_silence_generator },
155 { StatsReport::kStatsValueNameDecodingNormal, info.decoding_normal },
156 { StatsReport::kStatsValueNameDecodingPLC, info.decoding_plc },
157 { StatsReport::kStatsValueNameDecodingPLCCNG, info.decoding_plc_cng },
158 { StatsReport::kStatsValueNameJitterBufferMs, info.jitter_buffer_ms },
159 { StatsReport::kStatsValueNameJitterReceived, info.jitter_ms },
160 { StatsReport::kStatsValueNamePacketsLost, info.packets_lost },
161 { StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd },
162 { StatsReport::kStatsValueNamePreferredJitterBufferMs,
163 info.jitter_buffer_preferred_ms },
164 };
165
166 for (const auto& f : floats)
167 report->AddFloat(f.name, f.value);
168
169 for (const auto& i : ints)
170 report->AddInt(i.name, i.value);
171
172 report->AddInt64(StatsReport::kStatsValueNameBytesReceived,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000173 info.bytes_rcvd);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000174 report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
buildbot@webrtc.orgb525a9d2014-06-03 09:42:15 +0000175 info.capture_start_ntp_time_ms);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000176
177 report->AddString(StatsReport::kStatsValueNameCodecName, info.codec_name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000178}
179
180void ExtractStats(const cricket::VoiceSenderInfo& info, StatsReport* report) {
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000181 ExtractCommonSendProperties(info, report);
182
183 SetAudioProcessingStats(report, info.audio_level, info.typing_noise_detected,
184 info.echo_return_loss, info.echo_return_loss_enhancement,
185 info.echo_delay_median_ms, info.aec_quality_min, info.echo_delay_std_ms);
186
187 const IntForAdd ints[] = {
188 { StatsReport::kStatsValueNameJitterReceived, info.jitter_ms },
189 { StatsReport::kStatsValueNamePacketsLost, info.packets_lost },
190 { StatsReport::kStatsValueNamePacketsSent, info.packets_sent },
191 };
192
193 for (const auto& i : ints)
194 report->AddInt(i.name, i.value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000195}
196
197void ExtractStats(const cricket::VideoReceiverInfo& info, StatsReport* report) {
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000198 report->AddInt64(StatsReport::kStatsValueNameBytesReceived,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000199 info.bytes_rcvd);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000200 report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
buildbot@webrtc.org0581f0b2014-05-06 21:36:31 +0000201 info.capture_start_ntp_time_ms);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000202 const IntForAdd ints[] = {
203 { StatsReport::kStatsValueNameCurrentDelayMs, info.current_delay_ms },
204 { StatsReport::kStatsValueNameDecodeMs, info.decode_ms },
205 { StatsReport::kStatsValueNameFirsSent, info.firs_sent },
206 { StatsReport::kStatsValueNameFrameHeightReceived, info.frame_height },
207 { StatsReport::kStatsValueNameFrameRateDecoded, info.framerate_decoded },
208 { StatsReport::kStatsValueNameFrameRateOutput, info.framerate_output },
209 { StatsReport::kStatsValueNameFrameRateReceived, info.framerate_rcvd },
210 { StatsReport::kStatsValueNameFrameWidthReceived, info.frame_width },
211 { StatsReport::kStatsValueNameJitterBufferMs, info.jitter_buffer_ms },
212 { StatsReport::kStatsValueNameMaxDecodeMs, info.max_decode_ms },
213 { StatsReport::kStatsValueNameMinPlayoutDelayMs,
214 info.min_playout_delay_ms },
215 { StatsReport::kStatsValueNameNacksSent, info.nacks_sent },
216 { StatsReport::kStatsValueNamePacketsLost, info.packets_lost },
217 { StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd },
218 { StatsReport::kStatsValueNamePlisSent, info.plis_sent },
219 { StatsReport::kStatsValueNameRenderDelayMs, info.render_delay_ms },
220 { StatsReport::kStatsValueNameTargetDelayMs, info.target_delay_ms },
221 };
222
223 for (const auto& i : ints)
224 report->AddInt(i.name, i.value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000225}
226
227void ExtractStats(const cricket::VideoSenderInfo& info, StatsReport* report) {
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000228 ExtractCommonSendProperties(info, report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000229
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +0000230 report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution,
231 (info.adapt_reason & 0x2) > 0);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000232 report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution,
233 (info.adapt_reason & 0x1) > 0);
mallinath@webrtc.org62451dc2013-12-13 12:29:34 +0000234 report->AddBoolean(StatsReport::kStatsValueNameViewLimitedResolution,
235 (info.adapt_reason & 0x4) > 0);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000236
237 const IntForAdd ints[] = {
238 { StatsReport::kStatsValueNameAdaptationChanges, info.adapt_changes },
239 { StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms },
240 { StatsReport::kStatsValueNameCaptureJitterMs, info.capture_jitter_ms },
241 { StatsReport::kStatsValueNameCaptureQueueDelayMsPerS,
242 info.capture_queue_delay_ms_per_s },
243 { StatsReport::kStatsValueNameEncodeUsagePercent,
244 info.encode_usage_percent },
245 { StatsReport::kStatsValueNameFirsReceived, info.firs_rcvd },
246 { StatsReport::kStatsValueNameFrameHeightInput, info.input_frame_height },
247 { StatsReport::kStatsValueNameFrameHeightSent, info.send_frame_height },
248 { StatsReport::kStatsValueNameFrameRateInput, info.framerate_input },
249 { StatsReport::kStatsValueNameFrameRateSent, info.framerate_sent },
250 { StatsReport::kStatsValueNameFrameWidthInput, info.input_frame_width },
251 { StatsReport::kStatsValueNameFrameWidthSent, info.send_frame_width },
252 { StatsReport::kStatsValueNameNacksReceived, info.nacks_rcvd },
253 { StatsReport::kStatsValueNamePacketsLost, info.packets_lost },
254 { StatsReport::kStatsValueNamePacketsSent, info.packets_sent },
255 { StatsReport::kStatsValueNamePlisReceived, info.plis_rcvd },
256 };
257
258 for (const auto& i : ints)
259 report->AddInt(i.name, i.value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000260}
261
262void ExtractStats(const cricket::BandwidthEstimationInfo& info,
263 double stats_gathering_started,
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000264 PeerConnectionInterface::StatsOutputLevel level,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000265 StatsReport* report) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000266 ASSERT(report->type() == StatsReport::kStatsReportTypeBwe);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000267
268 // Clear out stats from previous GatherStats calls if any.
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000269 if (report->timestamp() != stats_gathering_started) {
270 report->ResetValues();
271 report->set_timestamp(stats_gathering_started);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000272 }
273
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000274 report->AddInt(StatsReport::kStatsValueNameAvailableSendBandwidth,
275 info.available_send_bandwidth);
276 report->AddInt(StatsReport::kStatsValueNameAvailableReceiveBandwidth,
277 info.available_recv_bandwidth);
278 report->AddInt(StatsReport::kStatsValueNameTargetEncBitrate,
279 info.target_enc_bitrate);
280 report->AddInt(StatsReport::kStatsValueNameActualEncBitrate,
281 info.actual_enc_bitrate);
282 report->AddInt(StatsReport::kStatsValueNameRetransmitBitrate,
283 info.retransmit_bitrate);
284 report->AddInt(StatsReport::kStatsValueNameTransmitBitrate,
285 info.transmit_bitrate);
286 report->AddInt64(StatsReport::kStatsValueNameBucketDelay,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000287 info.bucket_delay);
288}
289
wu@webrtc.org97077a32013-10-25 21:18:33 +0000290void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
291 StatsReport* report) {
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000292 report->set_timestamp(info.remote_stats[0].timestamp);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000293 // TODO(hta): Extract some stats here.
294}
295
296void ExtractRemoteStats(const cricket::MediaReceiverInfo& info,
297 StatsReport* report) {
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000298 report->set_timestamp(info.remote_stats[0].timestamp);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000299 // TODO(hta): Extract some stats here.
300}
301
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000302// Template to extract stats from a data vector.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000303// In order to use the template, the functions that are called from it,
304// ExtractStats and ExtractRemoteStats, must be defined and overloaded
305// for each type.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000306template<typename T>
307void ExtractStatsFromList(const std::vector<T>& data,
308 const std::string& transport_id,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000309 StatsCollector* collector,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000310 StatsReport::Direction direction) {
311 for (const auto& d : data) {
312 uint32 ssrc = d.ssrc();
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000313 // Each track can have stats for both local and remote objects.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000314 // TODO(hta): Handle the case of multiple SSRCs per object.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000315 StatsReport* report = collector->PrepareReport(true, ssrc, transport_id,
316 direction);
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000317 if (report)
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000318 ExtractStats(d, report);
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000319
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000320 if (!d.remote_stats.empty()) {
321 report = collector->PrepareReport(false, ssrc, transport_id, direction);
322 if (report)
323 ExtractRemoteStats(d, report);
wu@webrtc.org97077a32013-10-25 21:18:33 +0000324 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000325 }
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000326}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000327
328} // namespace
329
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000330const char* IceCandidateTypeToStatsType(const std::string& candidate_type) {
331 if (candidate_type == cricket::LOCAL_PORT_TYPE) {
332 return STATSREPORT_LOCAL_PORT_TYPE;
333 }
334 if (candidate_type == cricket::STUN_PORT_TYPE) {
335 return STATSREPORT_STUN_PORT_TYPE;
336 }
337 if (candidate_type == cricket::PRFLX_PORT_TYPE) {
338 return STATSREPORT_PRFLX_PORT_TYPE;
339 }
340 if (candidate_type == cricket::RELAY_PORT_TYPE) {
341 return STATSREPORT_RELAY_PORT_TYPE;
342 }
343 ASSERT(false);
344 return "unknown";
345}
346
347const char* AdapterTypeToStatsType(rtc::AdapterType type) {
348 switch (type) {
349 case rtc::ADAPTER_TYPE_UNKNOWN:
350 return "unknown";
351 case rtc::ADAPTER_TYPE_ETHERNET:
352 return STATSREPORT_ADAPTER_TYPE_ETHERNET;
353 case rtc::ADAPTER_TYPE_WIFI:
354 return STATSREPORT_ADAPTER_TYPE_WIFI;
355 case rtc::ADAPTER_TYPE_CELLULAR:
356 return STATSREPORT_ADAPTER_TYPE_WWAN;
357 case rtc::ADAPTER_TYPE_VPN:
358 return STATSREPORT_ADAPTER_TYPE_VPN;
phoglund@webrtc.org006521d2015-02-12 09:23:59 +0000359 case rtc::ADAPTER_TYPE_LOOPBACK:
360 return STATSREPORT_ADAPTER_TYPE_LOOPBACK;
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000361 default:
362 ASSERT(false);
363 return "";
364 }
365}
366
tommi@webrtc.org03505bc2014-07-14 20:15:26 +0000367StatsCollector::StatsCollector(WebRtcSession* session)
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000368 : session_(session),
369 stats_gathering_started_(0) {
tommi@webrtc.org03505bc2014-07-14 20:15:26 +0000370 ASSERT(session_);
371}
372
373StatsCollector::~StatsCollector() {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000374 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000375}
376
decurtis@webrtc.org322a5642015-02-03 22:09:37 +0000377double StatsCollector::GetTimeNow() {
378 return rtc::Timing::WallTimeNow() * rtc::kNumMillisecsPerSec;
379}
380
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000381// Adds a MediaStream with tracks that can be used as a |selector| in a call
382// to GetStats.
383void StatsCollector::AddStream(MediaStreamInterface* stream) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000384 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000385 ASSERT(stream != NULL);
386
387 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(),
388 &reports_);
389 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(),
390 &reports_);
391}
392
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000393void StatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track,
394 uint32 ssrc) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000395 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000396 ASSERT(audio_track != NULL);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000397 for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin();
398 it != local_audio_tracks_.end(); ++it) {
399 ASSERT(it->first != audio_track || it->second != ssrc);
400 }
xians@webrtc.org01bda202014-07-09 07:38:38 +0000401
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000402 local_audio_tracks_.push_back(std::make_pair(audio_track, ssrc));
xians@webrtc.org01bda202014-07-09 07:38:38 +0000403
404 // Create the kStatsReportTypeTrack report for the new track if there is no
405 // report yet.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000406 rtc::scoped_ptr<StatsReport::Id> id(
407 StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack,
408 audio_track->id()));
409 StatsReport* report = reports_.Find(*id.get());
410 if (!report) {
411 report = reports_.InsertNew(id.Pass());
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000412 report->AddString(StatsReport::kStatsValueNameTrackId, audio_track->id());
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000413 }
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000414}
415
416void StatsCollector::RemoveLocalAudioTrack(AudioTrackInterface* audio_track,
417 uint32 ssrc) {
418 ASSERT(audio_track != NULL);
419 for (LocalAudioTrackVector::iterator it = local_audio_tracks_.begin();
420 it != local_audio_tracks_.end(); ++it) {
421 if (it->first == audio_track && it->second == ssrc) {
422 local_audio_tracks_.erase(it);
423 return;
424 }
425 }
426
427 ASSERT(false);
428}
429
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000430void StatsCollector::GetStats(MediaStreamTrackInterface* track,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000431 StatsReports* reports) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000432 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000433 ASSERT(reports != NULL);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000434 ASSERT(reports->empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000435
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000436 if (!track) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000437 reports->reserve(reports_.size());
438 for (auto* r : reports_)
439 reports->push_back(r);
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000440 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000441 }
442
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000443 StatsReport* report = reports_.Find(StatsReport::NewTypedId(
444 StatsReport::kStatsReportTypeSession, session_->id()));
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000445 if (report)
446 reports->push_back(report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000447
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000448 report = reports_.Find(StatsReport::NewTypedId(
449 StatsReport::kStatsReportTypeTrack, track->id()));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000450
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000451 if (!report)
452 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000453
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000454 reports->push_back(report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000455
456 std::string track_id;
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000457 for (const auto* r : reports_) {
458 if (r->type() != StatsReport::kStatsReportTypeSsrc)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000459 continue;
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000460
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000461 const StatsReport::Value* v =
462 r->FindValue(StatsReport::kStatsValueNameTrackId);
463 if (v && v->value == track->id())
464 reports->push_back(r);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000465 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000466}
467
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000468void
469StatsCollector::UpdateStats(PeerConnectionInterface::StatsOutputLevel level) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000470 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000471 double time_now = GetTimeNow();
472 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriod number of
473 // ms apart will be ignored.
474 const double kMinGatherStatsPeriod = 50;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000475 if (stats_gathering_started_ != 0 &&
476 stats_gathering_started_ + kMinGatherStatsPeriod > time_now) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000477 return;
478 }
479 stats_gathering_started_ = time_now;
480
481 if (session_) {
482 ExtractSessionInfo();
483 ExtractVoiceInfo();
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000484 ExtractVideoInfo(level);
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000485 ExtractDataInfo();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000486 }
487}
488
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000489StatsReport* StatsCollector::PrepareReport(
490 bool local,
wu@webrtc.org97077a32013-10-25 21:18:33 +0000491 uint32 ssrc,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000492 const std::string& transport_id,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000493 StatsReport::Direction direction) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000494 ASSERT(session_->signaling_thread()->IsCurrent());
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000495 const std::string ssrc_id = rtc::ToString<uint32>(ssrc);
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000496 rtc::scoped_ptr<StatsReport::Id> id(StatsReport::NewIdWithDirection(
497 local ? StatsReport::kStatsReportTypeSsrc :
498 StatsReport::kStatsReportTypeRemoteSsrc,
499 ssrc_id, direction));
500 StatsReport* report = reports_.Find(*id.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000501
xians@webrtc.org01bda202014-07-09 07:38:38 +0000502 // Use the ID of the track that is currently mapped to the SSRC, if any.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000503 std::string track_id;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000504 if (!GetTrackIdBySsrc(ssrc, &track_id, direction)) {
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000505 if (!report) {
xians@webrtc.org01bda202014-07-09 07:38:38 +0000506 // The ssrc is not used by any track or existing report, return NULL
507 // in such case to indicate no report is prepared for the ssrc.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000508 return NULL;
xians@webrtc.org01bda202014-07-09 07:38:38 +0000509 }
510
511 // The ssrc is not used by any existing track. Keeps the old track id
512 // since we want to report the stats for inactive ssrc.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000513 const StatsReport::Value* v =
514 report->FindValue(StatsReport::kStatsValueNameTrackId);
515 if (v)
516 track_id = v->value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000517 }
518
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000519 if (!report) {
520 report = reports_.InsertNew(id.Pass());
521 } else {
522 // Clear out stats from previous GatherStats calls if any.
523 // This is required since the report will be returned for the new values.
524 // Having the old values in the report will lead to multiple values with
525 // the same name.
526 // TODO(tommi): This seems to be pretty wasteful if some of these values
527 // have not changed (we basically throw them away just to recreate them).
528 // Figure out a way to not have to do this while not breaking the existing
529 // functionality.
530 report->ResetValues();
531 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000532
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000533 ASSERT(report->empty());
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000534 // FYI - for remote reports, the timestamp will be overwritten later.
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000535 report->set_timestamp(stats_gathering_started_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000536
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000537 report->AddString(StatsReport::kStatsValueNameSsrc, ssrc_id);
538 report->AddString(StatsReport::kStatsValueNameTrackId, track_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000539 // Add the mapping of SSRC to transport.
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000540 report->AddString(StatsReport::kStatsValueNameTransportId, transport_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000541 return report;
542}
543
wu@webrtc.org4551b792013-10-09 15:37:36 +0000544std::string StatsCollector::AddOneCertificateReport(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000545 const rtc::SSLCertificate* cert, const std::string& issuer_id) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000546 ASSERT(session_->signaling_thread()->IsCurrent());
547
wu@webrtc.org4551b792013-10-09 15:37:36 +0000548 // TODO(bemasc): Move this computation to a helper class that caches these
549 // values to reduce CPU use in GetStats. This will require adding a fast
550 // SSLCertificate::Equals() method to detect certificate changes.
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000551
552 std::string digest_algorithm;
553 if (!cert->GetSignatureDigestAlgorithm(&digest_algorithm))
554 return std::string();
555
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000556 rtc::scoped_ptr<rtc::SSLFingerprint> ssl_fingerprint(
557 rtc::SSLFingerprint::Create(digest_algorithm, cert));
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000558
559 // SSLFingerprint::Create can fail if the algorithm returned by
560 // SSLCertificate::GetSignatureDigestAlgorithm is not supported by the
561 // implementation of SSLCertificate::ComputeDigest. This currently happens
562 // with MD5- and SHA-224-signed certificates when linked to libNSS.
563 if (!ssl_fingerprint)
564 return std::string();
565
wu@webrtc.org4551b792013-10-09 15:37:36 +0000566 std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
567
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000568 rtc::Buffer der_buffer;
wu@webrtc.org4551b792013-10-09 15:37:36 +0000569 cert->ToDER(&der_buffer);
570 std::string der_base64;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000571 rtc::Base64::EncodeFromArray(
wu@webrtc.org4551b792013-10-09 15:37:36 +0000572 der_buffer.data(), der_buffer.length(), &der_base64);
573
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000574 rtc::scoped_ptr<StatsReport::Id> id(
575 StatsReport::NewTypedId(
576 StatsReport::kStatsReportTypeCertificate, fingerprint));
577 StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000578 report->set_timestamp(stats_gathering_started_);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000579 report->AddString(StatsReport::kStatsValueNameFingerprint, fingerprint);
580 report->AddString(StatsReport::kStatsValueNameFingerprintAlgorithm,
581 digest_algorithm);
582 report->AddString(StatsReport::kStatsValueNameDer, der_base64);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000583 if (!issuer_id.empty())
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000584 report->AddString(StatsReport::kStatsValueNameIssuerId, issuer_id);
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000585 // TODO(tommi): Can we avoid this?
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000586 return report->id().ToString();
wu@webrtc.org4551b792013-10-09 15:37:36 +0000587}
588
589std::string StatsCollector::AddCertificateReports(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000590 const rtc::SSLCertificate* cert) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000591 ASSERT(session_->signaling_thread()->IsCurrent());
wu@webrtc.org4551b792013-10-09 15:37:36 +0000592 // Produces a chain of StatsReports representing this certificate and the rest
593 // of its chain, and adds those reports to |reports_|. The return value is
594 // the id of the leaf report. The provided cert must be non-null, so at least
595 // one report will always be provided and the returned string will never be
596 // empty.
597 ASSERT(cert != NULL);
598
599 std::string issuer_id;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000600 rtc::scoped_ptr<rtc::SSLCertChain> chain;
wu@webrtc.org4551b792013-10-09 15:37:36 +0000601 if (cert->GetChain(chain.accept())) {
602 // This loop runs in reverse, i.e. from root to leaf, so that each
603 // certificate's issuer's report ID is known before the child certificate's
604 // report is generated. The root certificate does not have an issuer ID
605 // value.
606 for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000607 const rtc::SSLCertificate& cert_i = chain->Get(i);
wu@webrtc.org4551b792013-10-09 15:37:36 +0000608 issuer_id = AddOneCertificateReport(&cert_i, issuer_id);
609 }
610 }
611 // Add the leaf certificate.
612 return AddOneCertificateReport(cert, issuer_id);
613}
614
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000615std::string StatsCollector::AddCandidateReport(
616 const cricket::Candidate& candidate,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000617 bool local) {
618 scoped_ptr<StatsReport::Id> id(
619 StatsReport::NewCandidateId(local, candidate.id()));
620 StatsReport* report = reports_.Find(*id.get());
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000621 if (!report) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000622 report = reports_.InsertNew(id.Pass());
623 report->set_timestamp(stats_gathering_started_);
624 if (local) {
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000625 report->AddString(StatsReport::kStatsValueNameCandidateNetworkType,
626 AdapterTypeToStatsType(candidate.network_type()));
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000627 }
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000628 report->AddString(StatsReport::kStatsValueNameCandidateIPAddress,
629 candidate.address().ipaddr().ToString());
630 report->AddString(StatsReport::kStatsValueNameCandidatePortNumber,
631 candidate.address().PortAsString());
632 report->AddInt(StatsReport::kStatsValueNameCandidatePriority,
633 candidate.priority());
634 report->AddString(StatsReport::kStatsValueNameCandidateType,
635 IceCandidateTypeToStatsType(candidate.type()));
636 report->AddString(StatsReport::kStatsValueNameCandidateTransportType,
637 candidate.protocol());
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000638 }
639
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000640 // TODO(tommi): Necessary?
641 return report->id().ToString();
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000642}
643
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000644void StatsCollector::ExtractSessionInfo() {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000645 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000646 // Extract information from the base session.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000647 rtc::scoped_ptr<StatsReport::Id> id(
648 StatsReport::NewTypedId(
649 StatsReport::kStatsReportTypeSession, session_->id()));
650 StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000651 report->set_timestamp(stats_gathering_started_);
652 report->ResetValues();
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000653 report->AddBoolean(StatsReport::kStatsValueNameInitiator,
654 session_->initiator());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000655
656 cricket::SessionStats stats;
657 if (session_->GetStats(&stats)) {
658 // Store the proxy map away for use in SSRC reporting.
659 proxy_to_transport_ = stats.proxy_to_transport;
660
661 for (cricket::TransportStatsMap::iterator transport_iter
662 = stats.transport_stats.begin();
663 transport_iter != stats.transport_stats.end(); ++transport_iter) {
wu@webrtc.org4551b792013-10-09 15:37:36 +0000664 // Attempt to get a copy of the certificates from the transport and
665 // expose them in stats reports. All channels in a transport share the
666 // same local and remote certificates.
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000667 //
668 // Note that Transport::GetIdentity and Transport::GetRemoteCertificate
669 // invoke method calls on the worker thread and block this thread, but
670 // messages are still processed on this thread, which may blow way the
671 // existing transports. So we cannot reuse |transport| after these calls.
wu@webrtc.org4551b792013-10-09 15:37:36 +0000672 std::string local_cert_report_id, remote_cert_report_id;
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000673
wu@webrtc.org4551b792013-10-09 15:37:36 +0000674 cricket::Transport* transport =
675 session_->GetTransport(transport_iter->second.content_name);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000676 rtc::scoped_ptr<rtc::SSLIdentity> identity;
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000677 if (transport && transport->GetIdentity(identity.accept())) {
678 local_cert_report_id =
679 AddCertificateReports(&(identity->certificate()));
wu@webrtc.org4551b792013-10-09 15:37:36 +0000680 }
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000681
682 transport = session_->GetTransport(transport_iter->second.content_name);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000683 rtc::scoped_ptr<rtc::SSLCertificate> cert;
jiayl@webrtc.org06b04ec2014-07-24 20:41:20 +0000684 if (transport && transport->GetRemoteCertificate(cert.accept())) {
685 remote_cert_report_id = AddCertificateReports(cert.get());
686 }
687
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000688 for (cricket::TransportChannelStatsList::iterator channel_iter
689 = transport_iter->second.channel_stats.begin();
690 channel_iter != transport_iter->second.channel_stats.end();
691 ++channel_iter) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000692 rtc::scoped_ptr<StatsReport::Id> id(
693 StatsReport::NewComponentId(transport_iter->second.content_name,
694 channel_iter->component));
695 StatsReport* channel_report = reports_.ReplaceOrAddNew(id.Pass());
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000696 channel_report->set_timestamp(stats_gathering_started_);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000697 channel_report->AddInt(StatsReport::kStatsValueNameComponent,
698 channel_iter->component);
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000699 if (!local_cert_report_id.empty()) {
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000700 channel_report->AddString(
wu@webrtc.org4551b792013-10-09 15:37:36 +0000701 StatsReport::kStatsValueNameLocalCertificateId,
702 local_cert_report_id);
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000703 }
704 if (!remote_cert_report_id.empty()) {
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000705 channel_report->AddString(
wu@webrtc.org4551b792013-10-09 15:37:36 +0000706 StatsReport::kStatsValueNameRemoteCertificateId,
707 remote_cert_report_id);
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000708 }
pthatcher@webrtc.org7bea1ff2015-03-04 01:38:30 +0000709 const std::string& srtp_cipher = channel_iter->srtp_cipher;
710 if (!srtp_cipher.empty()) {
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000711 channel_report->AddString(
pthatcher@webrtc.org7bea1ff2015-03-04 01:38:30 +0000712 StatsReport::kStatsValueNameSrtpCipher,
713 srtp_cipher);
714 }
715 const std::string& ssl_cipher = channel_iter->ssl_cipher;
716 if (!ssl_cipher.empty()) {
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000717 channel_report->AddString(
pthatcher@webrtc.org7bea1ff2015-03-04 01:38:30 +0000718 StatsReport::kStatsValueNameDtlsCipher,
719 ssl_cipher);
720 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000721 for (size_t i = 0;
722 i < channel_iter->connection_infos.size();
723 ++i) {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000724 rtc::scoped_ptr<StatsReport::Id> id(
725 StatsReport::NewCandidatePairId(transport_iter->first,
726 channel_iter->component, static_cast<int>(i)));
727 StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
tommi@webrtc.org8e327c42015-01-19 20:41:26 +0000728 report->set_timestamp(stats_gathering_started_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000729 // Link from connection to its containing channel.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000730 // TODO(tommi): Any way to avoid ToString here?
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000731 report->AddString(StatsReport::kStatsValueNameChannelId,
732 channel_report->id().ToString());
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000733
734 const cricket::ConnectionInfo& info =
735 channel_iter->connection_infos[i];
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000736 report->AddInt64(StatsReport::kStatsValueNameBytesSent,
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000737 info.sent_total_bytes);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000738 report->AddInt64(StatsReport::kStatsValueNameSendPacketsDiscarded,
guoweis@webrtc.org930e0042014-11-17 19:42:14 +0000739 info.sent_discarded_packets);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000740 report->AddInt64(StatsReport::kStatsValueNamePacketsSent,
guoweis@webrtc.org930e0042014-11-17 19:42:14 +0000741 info.sent_total_packets);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000742 report->AddInt64(StatsReport::kStatsValueNameBytesReceived,
tommi@webrtc.org5b06b062014-08-15 08:38:30 +0000743 info.recv_total_bytes);
744 report->AddBoolean(StatsReport::kStatsValueNameWritable,
745 info.writable);
746 report->AddBoolean(StatsReport::kStatsValueNameReadable,
747 info.readable);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000748 report->AddString(StatsReport::kStatsValueNameLocalCandidateId,
749 AddCandidateReport(info.local_candidate, true));
750 report->AddString(
guoweis@webrtc.org950c5182014-12-16 23:01:31 +0000751 StatsReport::kStatsValueNameRemoteCandidateId,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000752 AddCandidateReport(info.remote_candidate, false));
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000753 report->AddString(StatsReport::kStatsValueNameLocalAddress,
754 info.local_candidate.address().ToString());
755 report->AddString(StatsReport::kStatsValueNameRemoteAddress,
756 info.remote_candidate.address().ToString());
757 report->AddInt64(StatsReport::kStatsValueNameRtt, info.rtt);
758 report->AddString(StatsReport::kStatsValueNameTransportType,
759 info.local_candidate.protocol());
760 report->AddString(StatsReport::kStatsValueNameLocalCandidateType,
761 info.local_candidate.type());
762 report->AddString(StatsReport::kStatsValueNameRemoteCandidateType,
763 info.remote_candidate.type());
pthatcher@webrtc.org7bea1ff2015-03-04 01:38:30 +0000764 report->AddBoolean(StatsReport::kStatsValueNameActiveConnection,
765 info.best_connection);
766 if (info.best_connection) {
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000767 channel_report->AddString(
pthatcher@webrtc.org7bea1ff2015-03-04 01:38:30 +0000768 StatsReport::kStatsValueNameSelectedCandidatePairId,
769 report->id().ToString());
770 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000771 }
772 }
773 }
774 }
775}
776
777void StatsCollector::ExtractVoiceInfo() {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000778 ASSERT(session_->signaling_thread()->IsCurrent());
779
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000780 if (!session_->voice_channel()) {
781 return;
782 }
783 cricket::VoiceMediaInfo voice_info;
784 if (!session_->voice_channel()->GetStats(&voice_info)) {
785 LOG(LS_ERROR) << "Failed to get voice channel stats.";
786 return;
787 }
788 std::string transport_id;
tommi@webrtc.org47218952014-07-15 19:22:37 +0000789 if (!GetTransportIdFromProxy(proxy_to_transport_,
790 session_->voice_channel()->content_name(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000791 &transport_id)) {
792 LOG(LS_ERROR) << "Failed to get transport name for proxy "
793 << session_->voice_channel()->content_name();
794 return;
795 }
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000796 ExtractStatsFromList(voice_info.receivers, transport_id, this,
797 StatsReport::kReceive);
798 ExtractStatsFromList(voice_info.senders, transport_id, this,
799 StatsReport::kSend);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000800
801 UpdateStatsFromExistingLocalAudioTracks();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000802}
803
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000804void StatsCollector::ExtractVideoInfo(
805 PeerConnectionInterface::StatsOutputLevel level) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000806 ASSERT(session_->signaling_thread()->IsCurrent());
807
808 if (!session_->video_channel())
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000809 return;
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000810
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000811 cricket::VideoMediaInfo video_info;
pbos@webrtc.org058b1f12015-03-04 08:54:32 +0000812 if (!session_->video_channel()->GetStats(&video_info)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000813 LOG(LS_ERROR) << "Failed to get video channel stats.";
814 return;
815 }
816 std::string transport_id;
tommi@webrtc.org47218952014-07-15 19:22:37 +0000817 if (!GetTransportIdFromProxy(proxy_to_transport_,
818 session_->video_channel()->content_name(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000819 &transport_id)) {
820 LOG(LS_ERROR) << "Failed to get transport name for proxy "
821 << session_->video_channel()->content_name();
822 return;
823 }
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000824 ExtractStatsFromList(video_info.receivers, transport_id, this,
825 StatsReport::kReceive);
826 ExtractStatsFromList(video_info.senders, transport_id, this,
827 StatsReport::kSend);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000828 if (video_info.bw_estimations.size() != 1) {
829 LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
830 } else {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000831 rtc::scoped_ptr<StatsReport::Id> report_id(
832 StatsReport::NewBandwidthEstimationId());
833 StatsReport* report = reports_.FindOrAddNew(report_id.Pass());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000834 ExtractStats(
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000835 video_info.bw_estimations[0], stats_gathering_started_, level, report);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000836 }
837}
838
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000839void StatsCollector::ExtractDataInfo() {
840 ASSERT(session_->signaling_thread()->IsCurrent());
841
842 for (const auto& dc :
843 session_->mediastream_signaling()->sctp_data_channels()) {
decurtis@webrtc.org322a5642015-02-03 22:09:37 +0000844 rtc::scoped_ptr<StatsReport::Id> id(StatsReport::NewTypedIntId(
845 StatsReport::kStatsReportTypeDataChannel, dc->id()));
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000846 StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
decurtis@webrtc.org322a5642015-02-03 22:09:37 +0000847 report->set_timestamp(stats_gathering_started_);
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000848 report->AddString(StatsReport::kStatsValueNameLabel, dc->label());
849 report->AddInt(StatsReport::kStatsValueNameDataChannelId, dc->id());
850 report->AddString(StatsReport::kStatsValueNameProtocol, dc->protocol());
851 report->AddString(StatsReport::kStatsValueNameState,
852 DataChannelInterface::DataStateString(dc->state()));
decurtis@webrtc.org487a4442015-01-15 22:55:07 +0000853 }
854}
855
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000856StatsReport* StatsCollector::GetReport(const StatsReport::StatsType& type,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000857 const std::string& id,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000858 StatsReport::Direction direction) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000859 ASSERT(session_->signaling_thread()->IsCurrent());
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000860 ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
861 type == StatsReport::kStatsReportTypeRemoteSsrc);
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000862 return reports_.Find(StatsReport::NewIdWithDirection(type, id, direction));
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000863}
864
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000865void StatsCollector::UpdateStatsFromExistingLocalAudioTracks() {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000866 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000867 // Loop through the existing local audio tracks.
868 for (LocalAudioTrackVector::const_iterator it = local_audio_tracks_.begin();
869 it != local_audio_tracks_.end(); ++it) {
870 AudioTrackInterface* track = it->first;
871 uint32 ssrc = it->second;
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000872 const std::string ssrc_id = rtc::ToString<uint32>(ssrc);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000873 StatsReport* report = GetReport(StatsReport::kStatsReportTypeSsrc,
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000874 ssrc_id,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000875 StatsReport::kSend);
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000876 if (report == NULL) {
877 // This can happen if a local audio track is added to a stream on the
878 // fly and the report has not been set up yet. Do nothing in this case.
879 LOG(LS_ERROR) << "Stats report does not exist for ssrc " << ssrc;
880 continue;
881 }
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000882
883 // The same ssrc can be used by both local and remote audio tracks.
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000884 const StatsReport::Value* v =
885 report->FindValue(StatsReport::kStatsValueNameTrackId);
886 if (!v || v->value != track->id())
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000887 continue;
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000888
889 UpdateReportFromAudioTrack(track, report);
890 }
891}
892
893void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track,
894 StatsReport* report) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000895 ASSERT(session_->signaling_thread()->IsCurrent());
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000896 ASSERT(track != NULL);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000897
898 int signal_level = 0;
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000899 if (!track->GetSignalLevel(&signal_level))
900 signal_level = -1;
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000901
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000902 rtc::scoped_refptr<AudioProcessorInterface> audio_processor(
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000903 track->GetAudioProcessor());
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000904
905 AudioProcessorInterface::AudioProcessorStats stats;
tommi@webrtc.org92f40182015-03-04 15:25:19 +0000906 if (audio_processor.get())
907 audio_processor->GetStats(&stats);
908
909 SetAudioProcessingStats(report, signal_level, stats.typing_noise_detected,
910 stats.echo_return_loss, stats.echo_return_loss_enhancement,
911 stats.echo_delay_median_ms, stats.aec_quality_min,
912 stats.echo_delay_std_ms);
henrike@webrtc.org40b3b682014-03-03 18:30:11 +0000913}
914
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000915bool StatsCollector::GetTrackIdBySsrc(uint32 ssrc, std::string* track_id,
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000916 StatsReport::Direction direction) {
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000917 ASSERT(session_->signaling_thread()->IsCurrent());
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000918 if (direction == StatsReport::kSend) {
tommi@webrtc.org03505bc2014-07-14 20:15:26 +0000919 if (!session_->GetLocalTrackIdBySsrc(ssrc, track_id)) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000920 LOG(LS_WARNING) << "The SSRC " << ssrc
921 << " is not associated with a sending track";
922 return false;
923 }
924 } else {
tommi@webrtc.org4fb7e252015-01-21 11:36:18 +0000925 ASSERT(direction == StatsReport::kReceive);
tommi@webrtc.org03505bc2014-07-14 20:15:26 +0000926 if (!session_->GetRemoteTrackIdBySsrc(ssrc, track_id)) {
xians@webrtc.org4cb01282014-06-12 14:57:05 +0000927 LOG(LS_WARNING) << "The SSRC " << ssrc
928 << " is not associated with a receiving track";
929 return false;
930 }
931 }
932
933 return true;
934}
935
tommi@webrtc.org69bc5a32014-12-15 09:44:48 +0000936void StatsCollector::ClearUpdateStatsCacheForTest() {
xians@webrtc.org01bda202014-07-09 07:38:38 +0000937 stats_gathering_started_ = 0;
938}
939
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000940} // namespace webrtc