blob: b866b68b168636c91625d2d3ed4f20d87ae6e28f [file] [log] [blame]
hbosd565b732016-08-30 14:04:35 -07001/*
2 * Copyright 2016 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
hbos74e1a4f2016-09-15 23:33:01 -070011#include "webrtc/api/rtcstatscollector.h"
hbosd565b732016-08-30 14:04:35 -070012
13#include <memory>
14#include <utility>
15#include <vector>
16
17#include "webrtc/api/peerconnection.h"
hbos09bc1282016-11-08 06:29:22 -080018#include "webrtc/api/peerconnectioninterface.h"
19#include "webrtc/api/mediastreaminterface.h"
hbos6ab97ce2016-10-03 14:16:56 -070020#include "webrtc/api/webrtcsession.h"
hbosd565b732016-08-30 14:04:35 -070021#include "webrtc/base/checks.h"
hbos6ded1902016-11-01 01:50:46 -070022#include "webrtc/base/timeutils.h"
23#include "webrtc/media/base/mediachannel.h"
hbosab9f6e42016-10-07 02:18:47 -070024#include "webrtc/p2p/base/candidate.h"
hbos2fa7c672016-10-24 04:00:05 -070025#include "webrtc/p2p/base/p2pconstants.h"
hbosab9f6e42016-10-07 02:18:47 -070026#include "webrtc/p2p/base/port.h"
hbosd565b732016-08-30 14:04:35 -070027
28namespace webrtc {
29
hboscc555c52016-10-18 12:48:31 -070030namespace {
31
hbos2fa7c672016-10-24 04:00:05 -070032std::string RTCCertificateIDFromFingerprint(const std::string& fingerprint) {
33 return "RTCCertificate_" + fingerprint;
34}
35
36std::string RTCIceCandidatePairStatsIDFromConnectionInfo(
37 const cricket::ConnectionInfo& info) {
38 return "RTCIceCandidatePair_" + info.local_candidate.id() + "_" +
39 info.remote_candidate.id();
40}
41
hbos09bc1282016-11-08 06:29:22 -080042std::string RTCMediaStreamTrackStatsIDFromMediaStreamTrackInterface(
43 const MediaStreamTrackInterface& track) {
44 return "RTCMediaStreamTrack_" + track.id();
45}
46
hbos2fa7c672016-10-24 04:00:05 -070047std::string RTCTransportStatsIDFromTransportChannel(
48 const std::string& transport_name, int channel_component) {
49 return "RTCTransport_" + transport_name + "_" +
50 rtc::ToString<>(channel_component);
51}
52
hbos6ded1902016-11-01 01:50:46 -070053std::string RTCTransportStatsIDFromBaseChannel(
54 const ProxyTransportMap& proxy_to_transport,
55 const cricket::BaseChannel& base_channel) {
56 auto proxy_it = proxy_to_transport.find(base_channel.content_name());
57 if (proxy_it == proxy_to_transport.cend())
58 return "";
59 return RTCTransportStatsIDFromTransportChannel(
60 proxy_it->second, cricket::ICE_CANDIDATE_COMPONENT_RTP);
61}
62
hboseeafe942016-11-01 03:00:17 -070063std::string RTCInboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) {
64 return audio ? "RTCInboundRTPAudioStream_" + rtc::ToString<>(ssrc)
65 : "RTCInboundRTPVideoStream_" + rtc::ToString<>(ssrc);
66}
67
hbos6ded1902016-11-01 01:50:46 -070068std::string RTCOutboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) {
69 return audio ? "RTCOutboundRTPAudioStream_" + rtc::ToString<>(ssrc)
70 : "RTCOutboundRTPVideoStream_" + rtc::ToString<>(ssrc);
71}
72
hbosab9f6e42016-10-07 02:18:47 -070073const char* CandidateTypeToRTCIceCandidateType(const std::string& type) {
74 if (type == cricket::LOCAL_PORT_TYPE)
75 return RTCIceCandidateType::kHost;
76 if (type == cricket::STUN_PORT_TYPE)
77 return RTCIceCandidateType::kSrflx;
78 if (type == cricket::PRFLX_PORT_TYPE)
79 return RTCIceCandidateType::kPrflx;
80 if (type == cricket::RELAY_PORT_TYPE)
81 return RTCIceCandidateType::kRelay;
82 RTC_NOTREACHED();
83 return nullptr;
84}
85
hboscc555c52016-10-18 12:48:31 -070086const char* DataStateToRTCDataChannelState(
87 DataChannelInterface::DataState state) {
88 switch (state) {
89 case DataChannelInterface::kConnecting:
90 return RTCDataChannelState::kConnecting;
91 case DataChannelInterface::kOpen:
92 return RTCDataChannelState::kOpen;
93 case DataChannelInterface::kClosing:
94 return RTCDataChannelState::kClosing;
95 case DataChannelInterface::kClosed:
96 return RTCDataChannelState::kClosed;
97 default:
98 RTC_NOTREACHED();
99 return nullptr;
100 }
101}
102
hbos09bc1282016-11-08 06:29:22 -0800103void SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
104 const MediaStreamTrackInterface& track,
105 RTCMediaStreamTrackStats* track_stats) {
106 track_stats->track_identifier = track.id();
107 track_stats->ended = (track.state() == MediaStreamTrackInterface::kEnded);
108}
109
hbos820f5782016-11-22 03:16:50 -0800110// Provides the media independent counters (both audio and video).
hboseeafe942016-11-01 03:00:17 -0700111void SetInboundRTPStreamStatsFromMediaReceiverInfo(
112 const cricket::MediaReceiverInfo& media_receiver_info,
113 RTCInboundRTPStreamStats* inbound_stats) {
114 RTC_DCHECK(inbound_stats);
115 inbound_stats->ssrc = rtc::ToString<>(media_receiver_info.ssrc());
116 // TODO(hbos): Support the remote case. crbug.com/657855
117 inbound_stats->is_remote = false;
118 // TODO(hbos): Set |codec_id| when we have |RTCCodecStats|. Maybe relevant:
119 // |media_receiver_info.codec_name|. crbug.com/657854, 657855, 659117
120 inbound_stats->packets_received =
121 static_cast<uint32_t>(media_receiver_info.packets_rcvd);
122 inbound_stats->bytes_received =
123 static_cast<uint64_t>(media_receiver_info.bytes_rcvd);
124 inbound_stats->fraction_lost =
125 static_cast<double>(media_receiver_info.fraction_lost);
126}
127
128void SetInboundRTPStreamStatsFromVoiceReceiverInfo(
129 const cricket::VoiceReceiverInfo& voice_receiver_info,
hbos820f5782016-11-22 03:16:50 -0800130 RTCInboundRTPStreamStats* inbound_audio) {
hboseeafe942016-11-01 03:00:17 -0700131 SetInboundRTPStreamStatsFromMediaReceiverInfo(
hbos820f5782016-11-22 03:16:50 -0800132 voice_receiver_info, inbound_audio);
133 inbound_audio->media_type = "audio";
134 inbound_audio->jitter =
hboseeafe942016-11-01 03:00:17 -0700135 static_cast<double>(voice_receiver_info.jitter_ms) /
136 rtc::kNumMillisecsPerSec;
hbos820f5782016-11-22 03:16:50 -0800137 // |fir_count|, |pli_count| and |sli_count| are only valid for video and are
138 // purposefully left undefined for audio.
hboseeafe942016-11-01 03:00:17 -0700139}
140
141void SetInboundRTPStreamStatsFromVideoReceiverInfo(
142 const cricket::VideoReceiverInfo& video_receiver_info,
hbos820f5782016-11-22 03:16:50 -0800143 RTCInboundRTPStreamStats* inbound_video) {
hboseeafe942016-11-01 03:00:17 -0700144 SetInboundRTPStreamStatsFromMediaReceiverInfo(
hbos820f5782016-11-22 03:16:50 -0800145 video_receiver_info, inbound_video);
146 inbound_video->media_type = "video";
147 inbound_video->fir_count =
148 static_cast<uint32_t>(video_receiver_info.firs_sent);
149 inbound_video->pli_count =
150 static_cast<uint32_t>(video_receiver_info.plis_sent);
151 inbound_video->nack_count =
152 static_cast<uint32_t>(video_receiver_info.nacks_sent);
hboseeafe942016-11-01 03:00:17 -0700153}
154
hbos820f5782016-11-22 03:16:50 -0800155// Provides the media independent counters (both audio and video).
hbos6ded1902016-11-01 01:50:46 -0700156void SetOutboundRTPStreamStatsFromMediaSenderInfo(
157 const cricket::MediaSenderInfo& media_sender_info,
158 RTCOutboundRTPStreamStats* outbound_stats) {
159 RTC_DCHECK(outbound_stats);
160 outbound_stats->ssrc = rtc::ToString<>(media_sender_info.ssrc());
161 // TODO(hbos): Support the remote case. crbug.com/657856
162 outbound_stats->is_remote = false;
163 // TODO(hbos): Set |codec_id| when we have |RTCCodecStats|. Maybe relevant:
164 // |media_sender_info.codec_name|. crbug.com/657854, 657856, 659117
165 outbound_stats->packets_sent =
166 static_cast<uint32_t>(media_sender_info.packets_sent);
167 outbound_stats->bytes_sent =
168 static_cast<uint64_t>(media_sender_info.bytes_sent);
169 outbound_stats->round_trip_time =
170 static_cast<double>(media_sender_info.rtt_ms) / rtc::kNumMillisecsPerSec;
171}
172
173void SetOutboundRTPStreamStatsFromVoiceSenderInfo(
174 const cricket::VoiceSenderInfo& voice_sender_info,
175 RTCOutboundRTPStreamStats* outbound_audio) {
176 SetOutboundRTPStreamStatsFromMediaSenderInfo(
177 voice_sender_info, outbound_audio);
178 outbound_audio->media_type = "audio";
179 // |fir_count|, |pli_count| and |sli_count| are only valid for video and are
180 // purposefully left undefined for audio.
181}
182
183void SetOutboundRTPStreamStatsFromVideoSenderInfo(
184 const cricket::VideoSenderInfo& video_sender_info,
185 RTCOutboundRTPStreamStats* outbound_video) {
186 SetOutboundRTPStreamStatsFromMediaSenderInfo(
187 video_sender_info, outbound_video);
188 outbound_video->media_type = "video";
189 outbound_video->fir_count =
190 static_cast<uint32_t>(video_sender_info.firs_rcvd);
191 outbound_video->pli_count =
192 static_cast<uint32_t>(video_sender_info.plis_rcvd);
193 outbound_video->nack_count =
194 static_cast<uint32_t>(video_sender_info.nacks_rcvd);
195}
196
hbos02ba2112016-10-28 05:14:53 -0700197void ProduceCertificateStatsFromSSLCertificateStats(
198 int64_t timestamp_us, const rtc::SSLCertificateStats& certificate_stats,
199 RTCStatsReport* report) {
200 RTCCertificateStats* prev_certificate_stats = nullptr;
201 for (const rtc::SSLCertificateStats* s = &certificate_stats; s;
202 s = s->issuer.get()) {
203 RTCCertificateStats* certificate_stats = new RTCCertificateStats(
204 RTCCertificateIDFromFingerprint(s->fingerprint), timestamp_us);
205 certificate_stats->fingerprint = s->fingerprint;
206 certificate_stats->fingerprint_algorithm = s->fingerprint_algorithm;
207 certificate_stats->base64_certificate = s->base64_certificate;
208 if (prev_certificate_stats)
209 prev_certificate_stats->issuer_certificate_id = certificate_stats->id();
210 report->AddStats(std::unique_ptr<RTCCertificateStats>(certificate_stats));
211 prev_certificate_stats = certificate_stats;
212 }
213}
214
215const std::string& ProduceIceCandidateStats(
216 int64_t timestamp_us, const cricket::Candidate& candidate, bool is_local,
217 RTCStatsReport* report) {
218 const std::string& id = "RTCIceCandidate_" + candidate.id();
219 const RTCStats* stats = report->Get(id);
220 if (!stats) {
221 std::unique_ptr<RTCIceCandidateStats> candidate_stats;
222 if (is_local)
223 candidate_stats.reset(new RTCLocalIceCandidateStats(id, timestamp_us));
224 else
225 candidate_stats.reset(new RTCRemoteIceCandidateStats(id, timestamp_us));
226 candidate_stats->ip = candidate.address().ipaddr().ToString();
227 candidate_stats->port = static_cast<int32_t>(candidate.address().port());
228 candidate_stats->protocol = candidate.protocol();
229 candidate_stats->candidate_type = CandidateTypeToRTCIceCandidateType(
230 candidate.type());
231 candidate_stats->priority = static_cast<int32_t>(candidate.priority());
232
233 stats = candidate_stats.get();
234 report->AddStats(std::move(candidate_stats));
235 }
236 RTC_DCHECK_EQ(stats->type(), is_local ? RTCLocalIceCandidateStats::kType
237 : RTCRemoteIceCandidateStats::kType);
238 return stats->id();
239}
240
hbos09bc1282016-11-08 06:29:22 -0800241void ProduceMediaStreamAndTrackStats(
242 int64_t timestamp_us,
243 rtc::scoped_refptr<StreamCollectionInterface> streams,
244 bool is_local,
245 RTCStatsReport* report) {
246 // TODO(hbos): When "AddTrack" is implemented we should iterate tracks to
247 // find which streams exist, not iterate streams to find tracks.
248 // crbug.com/659137
249 // TODO(hbos): Return stats of detached tracks. We have to perform stats
250 // gathering at the time of detachment to get accurate stats and timestamps.
251 // crbug.com/659137
252 if (!streams)
253 return;
254 for (size_t i = 0; i < streams->count(); ++i) {
255 MediaStreamInterface* stream = streams->at(i);
256
257 std::unique_ptr<RTCMediaStreamStats> stream_stats(
258 new RTCMediaStreamStats("RTCMediaStream_" + stream->label(),
259 timestamp_us));
260 stream_stats->stream_identifier = stream->label();
261 stream_stats->track_ids = std::vector<std::string>();
262 // Audio Tracks
kwiberg1b35d4c2016-11-10 05:15:38 -0800263 for (const rtc::scoped_refptr<AudioTrackInterface>& audio_track :
hbos09bc1282016-11-08 06:29:22 -0800264 stream->GetAudioTracks()) {
265 std::string id = RTCMediaStreamTrackStatsIDFromMediaStreamTrackInterface(
266 *audio_track.get());
267 if (report->Get(id)) {
268 // Skip track, stats already exist for it.
269 continue;
270 }
271 std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats(
272 new RTCMediaStreamTrackStats(id, timestamp_us));
273 stream_stats->track_ids->push_back(audio_track_stats->id());
274 SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
275 *audio_track.get(),
276 audio_track_stats.get());
277 audio_track_stats->remote_source = !is_local;
278 audio_track_stats->detached = false;
279 int signal_level;
280 if (audio_track->GetSignalLevel(&signal_level)) {
281 // Convert signal level from [0,32767] int to [0,1] double.
282 RTC_DCHECK_GE(signal_level, 0);
283 RTC_DCHECK_LE(signal_level, 32767);
284 audio_track_stats->audio_level = signal_level / 32767.0;
285 }
286 if (audio_track->GetAudioProcessor()) {
287 AudioProcessorInterface::AudioProcessorStats audio_processor_stats;
288 audio_track->GetAudioProcessor()->GetStats(&audio_processor_stats);
289 audio_track_stats->echo_return_loss = static_cast<double>(
290 audio_processor_stats.echo_return_loss);
291 audio_track_stats->echo_return_loss_enhancement = static_cast<double>(
292 audio_processor_stats.echo_return_loss_enhancement);
293 }
294 report->AddStats(std::move(audio_track_stats));
295 }
296 // Video Tracks
kwiberg1b35d4c2016-11-10 05:15:38 -0800297 for (const rtc::scoped_refptr<VideoTrackInterface>& video_track :
hbos09bc1282016-11-08 06:29:22 -0800298 stream->GetVideoTracks()) {
299 std::string id = RTCMediaStreamTrackStatsIDFromMediaStreamTrackInterface(
300 *video_track.get());
301 if (report->Get(id)) {
302 // Skip track, stats already exist for it.
303 continue;
304 }
305 std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats(
306 new RTCMediaStreamTrackStats(id, timestamp_us));
307 stream_stats->track_ids->push_back(video_track_stats->id());
308 SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
309 *video_track.get(),
310 video_track_stats.get());
311 video_track_stats->remote_source = !is_local;
312 video_track_stats->detached = false;
313 if (video_track->GetSource()) {
314 VideoTrackSourceInterface::Stats video_track_source_stats;
315 if (video_track->GetSource()->GetStats(&video_track_source_stats)) {
316 video_track_stats->frame_width = static_cast<uint32_t>(
317 video_track_source_stats.input_width);
318 video_track_stats->frame_height = static_cast<uint32_t>(
319 video_track_source_stats.input_height);
320 }
321 }
322 report->AddStats(std::move(video_track_stats));
323 }
324 report->AddStats(std::move(stream_stats));
325 }
326}
327
hboscc555c52016-10-18 12:48:31 -0700328} // namespace
329
hbosc82f2e12016-09-05 01:36:50 -0700330rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create(
331 PeerConnection* pc, int64_t cache_lifetime_us) {
332 return rtc::scoped_refptr<RTCStatsCollector>(
333 new rtc::RefCountedObject<RTCStatsCollector>(pc, cache_lifetime_us));
334}
335
336RTCStatsCollector::RTCStatsCollector(PeerConnection* pc,
337 int64_t cache_lifetime_us)
hbosd565b732016-08-30 14:04:35 -0700338 : pc_(pc),
hbosc82f2e12016-09-05 01:36:50 -0700339 signaling_thread_(pc->session()->signaling_thread()),
340 worker_thread_(pc->session()->worker_thread()),
341 network_thread_(pc->session()->network_thread()),
342 num_pending_partial_reports_(0),
343 partial_report_timestamp_us_(0),
hbos0e6758d2016-08-31 07:57:36 -0700344 cache_timestamp_us_(0),
345 cache_lifetime_us_(cache_lifetime_us) {
hbosd565b732016-08-30 14:04:35 -0700346 RTC_DCHECK(pc_);
hbosc82f2e12016-09-05 01:36:50 -0700347 RTC_DCHECK(signaling_thread_);
348 RTC_DCHECK(worker_thread_);
349 RTC_DCHECK(network_thread_);
hbos0e6758d2016-08-31 07:57:36 -0700350 RTC_DCHECK_GE(cache_lifetime_us_, 0);
hbos82ebe022016-11-14 01:41:09 -0800351 pc_->SignalDataChannelCreated.connect(
352 this, &RTCStatsCollector::OnDataChannelCreated);
hbosd565b732016-08-30 14:04:35 -0700353}
354
hbosc82f2e12016-09-05 01:36:50 -0700355void RTCStatsCollector::GetStatsReport(
356 rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {
357 RTC_DCHECK(signaling_thread_->IsCurrent());
358 RTC_DCHECK(callback);
359 callbacks_.push_back(callback);
360
hbos0e6758d2016-08-31 07:57:36 -0700361 // "Now" using a monotonically increasing timer.
362 int64_t cache_now_us = rtc::TimeMicros();
363 if (cached_report_ &&
364 cache_now_us - cache_timestamp_us_ <= cache_lifetime_us_) {
hbosc82f2e12016-09-05 01:36:50 -0700365 // We have a fresh cached report to deliver.
366 DeliverCachedReport();
367 } else if (!num_pending_partial_reports_) {
368 // Only start gathering stats if we're not already gathering stats. In the
369 // case of already gathering stats, |callback_| will be invoked when there
370 // are no more pending partial reports.
371
372 // "Now" using a system clock, relative to the UNIX epoch (Jan 1, 1970,
373 // UTC), in microseconds. The system clock could be modified and is not
374 // necessarily monotonically increasing.
nissecdf37a92016-09-13 23:41:47 -0700375 int64_t timestamp_us = rtc::TimeUTCMicros();
hbosc82f2e12016-09-05 01:36:50 -0700376
377 num_pending_partial_reports_ = 3;
378 partial_report_timestamp_us_ = cache_now_us;
379 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
380 rtc::Bind(&RTCStatsCollector::ProducePartialResultsOnSignalingThread,
381 rtc::scoped_refptr<RTCStatsCollector>(this), timestamp_us));
382 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, worker_thread_,
383 rtc::Bind(&RTCStatsCollector::ProducePartialResultsOnWorkerThread,
384 rtc::scoped_refptr<RTCStatsCollector>(this), timestamp_us));
385 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, network_thread_,
386 rtc::Bind(&RTCStatsCollector::ProducePartialResultsOnNetworkThread,
387 rtc::scoped_refptr<RTCStatsCollector>(this), timestamp_us));
hbos0e6758d2016-08-31 07:57:36 -0700388 }
hbosd565b732016-08-30 14:04:35 -0700389}
390
391void RTCStatsCollector::ClearCachedStatsReport() {
hbosc82f2e12016-09-05 01:36:50 -0700392 RTC_DCHECK(signaling_thread_->IsCurrent());
hbosd565b732016-08-30 14:04:35 -0700393 cached_report_ = nullptr;
394}
395
hbosc82f2e12016-09-05 01:36:50 -0700396void RTCStatsCollector::ProducePartialResultsOnSignalingThread(
397 int64_t timestamp_us) {
398 RTC_DCHECK(signaling_thread_->IsCurrent());
hbos6ded1902016-11-01 01:50:46 -0700399 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create(
400 timestamp_us);
hbosc82f2e12016-09-05 01:36:50 -0700401
hbos6ab97ce2016-10-03 14:16:56 -0700402 SessionStats session_stats;
403 if (pc_->session()->GetTransportStats(&session_stats)) {
hbos2fa7c672016-10-24 04:00:05 -0700404 std::map<std::string, CertificateStatsPair> transport_cert_stats =
405 PrepareTransportCertificateStats_s(session_stats);
406
407 ProduceCertificateStats_s(
408 timestamp_us, transport_cert_stats, report.get());
409 ProduceIceCandidateAndPairStats_s(
410 timestamp_us, session_stats, report.get());
hbos6ded1902016-11-01 01:50:46 -0700411 ProduceRTPStreamStats_s(
412 timestamp_us, session_stats, report.get());
hbos2fa7c672016-10-24 04:00:05 -0700413 ProduceTransportStats_s(
414 timestamp_us, session_stats, transport_cert_stats, report.get());
hbos6ab97ce2016-10-03 14:16:56 -0700415 }
hboscc555c52016-10-18 12:48:31 -0700416 ProduceDataChannelStats_s(timestamp_us, report.get());
hbos09bc1282016-11-08 06:29:22 -0800417 ProduceMediaStreamAndTrackStats_s(timestamp_us, report.get());
hbos6ab97ce2016-10-03 14:16:56 -0700418 ProducePeerConnectionStats_s(timestamp_us, report.get());
hbosc82f2e12016-09-05 01:36:50 -0700419
420 AddPartialResults(report);
421}
422
423void RTCStatsCollector::ProducePartialResultsOnWorkerThread(
424 int64_t timestamp_us) {
425 RTC_DCHECK(worker_thread_->IsCurrent());
hbos6ded1902016-11-01 01:50:46 -0700426 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create(
427 timestamp_us);
hbosc82f2e12016-09-05 01:36:50 -0700428
429 // TODO(hbos): Gather stats on worker thread.
hbos6ded1902016-11-01 01:50:46 -0700430 // pc_->session()'s channels are owned by the signaling thread but there are
431 // some stats that are gathered on the worker thread. Instead of a synchronous
432 // invoke on "s->w" we could to the "w" work here asynchronously if it wasn't
433 // for the ownership issue. Synchronous invokes in other places makes it
434 // difficult to introduce locks without introducing deadlocks and the channels
435 // are not reference counted.
hbosc82f2e12016-09-05 01:36:50 -0700436
437 AddPartialResults(report);
438}
439
440void RTCStatsCollector::ProducePartialResultsOnNetworkThread(
441 int64_t timestamp_us) {
442 RTC_DCHECK(network_thread_->IsCurrent());
hbos6ded1902016-11-01 01:50:46 -0700443 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create(
444 timestamp_us);
hbosc82f2e12016-09-05 01:36:50 -0700445
446 // TODO(hbos): Gather stats on network thread.
hbos6ded1902016-11-01 01:50:46 -0700447 // pc_->session()'s channels are owned by the signaling thread but there are
448 // some stats that are gathered on the network thread. Instead of a
449 // synchronous invoke on "s->n" we could to the "n" work here asynchronously
450 // if it wasn't for the ownership issue. Synchronous invokes in other places
451 // makes it difficult to introduce locks without introducing deadlocks and the
452 // channels are not reference counted.
hbosc82f2e12016-09-05 01:36:50 -0700453
454 AddPartialResults(report);
455}
456
457void RTCStatsCollector::AddPartialResults(
458 const rtc::scoped_refptr<RTCStatsReport>& partial_report) {
459 if (!signaling_thread_->IsCurrent()) {
460 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
461 rtc::Bind(&RTCStatsCollector::AddPartialResults_s,
462 rtc::scoped_refptr<RTCStatsCollector>(this),
463 partial_report));
464 return;
465 }
466 AddPartialResults_s(partial_report);
467}
468
469void RTCStatsCollector::AddPartialResults_s(
470 rtc::scoped_refptr<RTCStatsReport> partial_report) {
471 RTC_DCHECK(signaling_thread_->IsCurrent());
472 RTC_DCHECK_GT(num_pending_partial_reports_, 0);
473 if (!partial_report_)
474 partial_report_ = partial_report;
475 else
476 partial_report_->TakeMembersFrom(partial_report);
477 --num_pending_partial_reports_;
478 if (!num_pending_partial_reports_) {
479 cache_timestamp_us_ = partial_report_timestamp_us_;
480 cached_report_ = partial_report_;
481 partial_report_ = nullptr;
482 DeliverCachedReport();
483 }
484}
485
486void RTCStatsCollector::DeliverCachedReport() {
487 RTC_DCHECK(signaling_thread_->IsCurrent());
488 RTC_DCHECK(!callbacks_.empty());
489 RTC_DCHECK(cached_report_);
490 for (const rtc::scoped_refptr<RTCStatsCollectorCallback>& callback :
491 callbacks_) {
492 callback->OnStatsDelivered(cached_report_);
493 }
494 callbacks_.clear();
hbosd565b732016-08-30 14:04:35 -0700495}
496
hbos6ab97ce2016-10-03 14:16:56 -0700497void RTCStatsCollector::ProduceCertificateStats_s(
hbos2fa7c672016-10-24 04:00:05 -0700498 int64_t timestamp_us,
499 const std::map<std::string, CertificateStatsPair>& transport_cert_stats,
hbos6ab97ce2016-10-03 14:16:56 -0700500 RTCStatsReport* report) const {
501 RTC_DCHECK(signaling_thread_->IsCurrent());
hbos02ba2112016-10-28 05:14:53 -0700502 for (const auto& transport_cert_stats_pair : transport_cert_stats) {
503 if (transport_cert_stats_pair.second.local) {
504 ProduceCertificateStatsFromSSLCertificateStats(
505 timestamp_us, *transport_cert_stats_pair.second.local.get(), report);
hbos6ab97ce2016-10-03 14:16:56 -0700506 }
hbos02ba2112016-10-28 05:14:53 -0700507 if (transport_cert_stats_pair.second.remote) {
508 ProduceCertificateStatsFromSSLCertificateStats(
509 timestamp_us, *transport_cert_stats_pair.second.remote.get(), report);
hbos6ab97ce2016-10-03 14:16:56 -0700510 }
511 }
512}
513
hboscc555c52016-10-18 12:48:31 -0700514void RTCStatsCollector::ProduceDataChannelStats_s(
515 int64_t timestamp_us, RTCStatsReport* report) const {
516 RTC_DCHECK(signaling_thread_->IsCurrent());
517 for (const rtc::scoped_refptr<DataChannel>& data_channel :
518 pc_->sctp_data_channels()) {
519 std::unique_ptr<RTCDataChannelStats> data_channel_stats(
520 new RTCDataChannelStats(
521 "RTCDataChannel_" + rtc::ToString<>(data_channel->id()),
522 timestamp_us));
523 data_channel_stats->label = data_channel->label();
524 data_channel_stats->protocol = data_channel->protocol();
525 data_channel_stats->datachannelid = data_channel->id();
526 data_channel_stats->state =
527 DataStateToRTCDataChannelState(data_channel->state());
528 data_channel_stats->messages_sent = data_channel->messages_sent();
529 data_channel_stats->bytes_sent = data_channel->bytes_sent();
530 data_channel_stats->messages_received = data_channel->messages_received();
531 data_channel_stats->bytes_received = data_channel->bytes_received();
532 report->AddStats(std::move(data_channel_stats));
533 }
534}
535
hbosab9f6e42016-10-07 02:18:47 -0700536void RTCStatsCollector::ProduceIceCandidateAndPairStats_s(
537 int64_t timestamp_us, const SessionStats& session_stats,
538 RTCStatsReport* report) const {
539 RTC_DCHECK(signaling_thread_->IsCurrent());
hbosc47a0c32016-10-11 14:54:49 -0700540 for (const auto& transport_stats : session_stats.transport_stats) {
541 for (const auto& channel_stats : transport_stats.second.channel_stats) {
542 for (const cricket::ConnectionInfo& info :
543 channel_stats.connection_infos) {
hbosc47a0c32016-10-11 14:54:49 -0700544 std::unique_ptr<RTCIceCandidatePairStats> candidate_pair_stats(
hbos2fa7c672016-10-24 04:00:05 -0700545 new RTCIceCandidatePairStats(
546 RTCIceCandidatePairStatsIDFromConnectionInfo(info),
547 timestamp_us));
hbosc47a0c32016-10-11 14:54:49 -0700548
hbosab9f6e42016-10-07 02:18:47 -0700549 // TODO(hbos): There could be other candidates that are not paired with
550 // anything. We don't have a complete list. Local candidates come from
551 // Port objects, and prflx candidates (both local and remote) are only
552 // stored in candidate pairs. crbug.com/632723
hbos02ba2112016-10-28 05:14:53 -0700553 candidate_pair_stats->local_candidate_id = ProduceIceCandidateStats(
hbosab9f6e42016-10-07 02:18:47 -0700554 timestamp_us, info.local_candidate, true, report);
hbos02ba2112016-10-28 05:14:53 -0700555 candidate_pair_stats->remote_candidate_id = ProduceIceCandidateStats(
hbosab9f6e42016-10-07 02:18:47 -0700556 timestamp_us, info.remote_candidate, false, report);
hbosc47a0c32016-10-11 14:54:49 -0700557
hbosc47a0c32016-10-11 14:54:49 -0700558 // TODO(hbos): This writable is different than the spec. It goes to
559 // false after a certain amount of time without a response passes.
560 // crbug.com/633550
561 candidate_pair_stats->writable = info.writable;
hbosc47a0c32016-10-11 14:54:49 -0700562 candidate_pair_stats->bytes_sent =
563 static_cast<uint64_t>(info.sent_total_bytes);
564 candidate_pair_stats->bytes_received =
565 static_cast<uint64_t>(info.recv_total_bytes);
hbosc47a0c32016-10-11 14:54:49 -0700566 // TODO(hbos): The |info.rtt| measurement is smoothed. It shouldn't be
567 // smoothed according to the spec. crbug.com/633550. See
568 // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentrtt
569 candidate_pair_stats->current_rtt =
hbos6ded1902016-11-01 01:50:46 -0700570 static_cast<double>(info.rtt) / rtc::kNumMillisecsPerSec;
hbosc47a0c32016-10-11 14:54:49 -0700571 candidate_pair_stats->requests_sent =
572 static_cast<uint64_t>(info.sent_ping_requests_total);
573 candidate_pair_stats->responses_received =
574 static_cast<uint64_t>(info.recv_ping_responses);
575 candidate_pair_stats->responses_sent =
576 static_cast<uint64_t>(info.sent_ping_responses);
hbosc47a0c32016-10-11 14:54:49 -0700577
578 report->AddStats(std::move(candidate_pair_stats));
hbosab9f6e42016-10-07 02:18:47 -0700579 }
580 }
581 }
582}
583
hbos09bc1282016-11-08 06:29:22 -0800584void RTCStatsCollector::ProduceMediaStreamAndTrackStats_s(
585 int64_t timestamp_us, RTCStatsReport* report) const {
586 RTC_DCHECK(signaling_thread_->IsCurrent());
587 ProduceMediaStreamAndTrackStats(
588 timestamp_us, pc_->local_streams(), true, report);
589 ProduceMediaStreamAndTrackStats(
590 timestamp_us, pc_->remote_streams(), false, report);
591}
592
hbos6ab97ce2016-10-03 14:16:56 -0700593void RTCStatsCollector::ProducePeerConnectionStats_s(
594 int64_t timestamp_us, RTCStatsReport* report) const {
hbosc82f2e12016-09-05 01:36:50 -0700595 RTC_DCHECK(signaling_thread_->IsCurrent());
hbosd565b732016-08-30 14:04:35 -0700596 std::unique_ptr<RTCPeerConnectionStats> stats(
hbos0e6758d2016-08-31 07:57:36 -0700597 new RTCPeerConnectionStats("RTCPeerConnection", timestamp_us));
hbos82ebe022016-11-14 01:41:09 -0800598 stats->data_channels_opened = internal_record_.data_channels_opened;
599 stats->data_channels_closed = internal_record_.data_channels_closed;
hbos6ab97ce2016-10-03 14:16:56 -0700600 report->AddStats(std::move(stats));
hbosd565b732016-08-30 14:04:35 -0700601}
602
hbos6ded1902016-11-01 01:50:46 -0700603void RTCStatsCollector::ProduceRTPStreamStats_s(
604 int64_t timestamp_us, const SessionStats& session_stats,
605 RTCStatsReport* report) const {
606 RTC_DCHECK(signaling_thread_->IsCurrent());
607
608 // Audio
609 if (pc_->session()->voice_channel()) {
610 cricket::VoiceMediaInfo voice_media_info;
611 if (pc_->session()->voice_channel()->GetStats(&voice_media_info)) {
612 std::string transport_id = RTCTransportStatsIDFromBaseChannel(
613 session_stats.proxy_to_transport, *pc_->session()->voice_channel());
hboseeafe942016-11-01 03:00:17 -0700614 RTC_DCHECK(!transport_id.empty());
615 // Inbound
616 for (const cricket::VoiceReceiverInfo& voice_receiver_info :
617 voice_media_info.receivers) {
618 // TODO(nisse): SSRC == 0 currently means none. Delete check when that
619 // is fixed.
620 if (voice_receiver_info.ssrc() == 0)
621 continue;
622 std::unique_ptr<RTCInboundRTPStreamStats> inbound_audio(
623 new RTCInboundRTPStreamStats(
624 RTCInboundRTPStreamStatsIDFromSSRC(
625 true, voice_receiver_info.ssrc()),
626 timestamp_us));
627 SetInboundRTPStreamStatsFromVoiceReceiverInfo(
628 voice_receiver_info, inbound_audio.get());
629 inbound_audio->transport_id = transport_id;
630 report->AddStats(std::move(inbound_audio));
631 }
632 // Outbound
hbos6ded1902016-11-01 01:50:46 -0700633 for (const cricket::VoiceSenderInfo& voice_sender_info :
634 voice_media_info.senders) {
635 // TODO(nisse): SSRC == 0 currently means none. Delete check when that
636 // is fixed.
637 if (voice_sender_info.ssrc() == 0)
638 continue;
639 std::unique_ptr<RTCOutboundRTPStreamStats> outbound_audio(
640 new RTCOutboundRTPStreamStats(
641 RTCOutboundRTPStreamStatsIDFromSSRC(
642 true, voice_sender_info.ssrc()),
643 timestamp_us));
644 SetOutboundRTPStreamStatsFromVoiceSenderInfo(
645 voice_sender_info, outbound_audio.get());
hboseeafe942016-11-01 03:00:17 -0700646 outbound_audio->transport_id = transport_id;
hbos6ded1902016-11-01 01:50:46 -0700647 report->AddStats(std::move(outbound_audio));
648 }
649 }
650 }
651 // Video
652 if (pc_->session()->video_channel()) {
653 cricket::VideoMediaInfo video_media_info;
654 if (pc_->session()->video_channel()->GetStats(&video_media_info)) {
655 std::string transport_id = RTCTransportStatsIDFromBaseChannel(
656 session_stats.proxy_to_transport, *pc_->session()->video_channel());
hboseeafe942016-11-01 03:00:17 -0700657 RTC_DCHECK(!transport_id.empty());
658 // Inbound
659 for (const cricket::VideoReceiverInfo& video_receiver_info :
660 video_media_info.receivers) {
661 // TODO(nisse): SSRC == 0 currently means none. Delete check when that
662 // is fixed.
663 if (video_receiver_info.ssrc() == 0)
664 continue;
665 std::unique_ptr<RTCInboundRTPStreamStats> inbound_video(
666 new RTCInboundRTPStreamStats(
667 RTCInboundRTPStreamStatsIDFromSSRC(
668 false, video_receiver_info.ssrc()),
669 timestamp_us));
670 SetInboundRTPStreamStatsFromVideoReceiverInfo(
671 video_receiver_info, inbound_video.get());
672 inbound_video->transport_id = transport_id;
673 report->AddStats(std::move(inbound_video));
674 }
675 // Outbound
hbos6ded1902016-11-01 01:50:46 -0700676 for (const cricket::VideoSenderInfo& video_sender_info :
677 video_media_info.senders) {
678 // TODO(nisse): SSRC == 0 currently means none. Delete check when that
679 // is fixed.
680 if (video_sender_info.ssrc() == 0)
681 continue;
682 std::unique_ptr<RTCOutboundRTPStreamStats> outbound_video(
683 new RTCOutboundRTPStreamStats(
684 RTCOutboundRTPStreamStatsIDFromSSRC(
685 false, video_sender_info.ssrc()),
686 timestamp_us));
687 SetOutboundRTPStreamStatsFromVideoSenderInfo(
688 video_sender_info, outbound_video.get());
hboseeafe942016-11-01 03:00:17 -0700689 outbound_video->transport_id = transport_id;
hbos6ded1902016-11-01 01:50:46 -0700690 report->AddStats(std::move(outbound_video));
691 }
692 }
693 }
694}
695
hbos2fa7c672016-10-24 04:00:05 -0700696void RTCStatsCollector::ProduceTransportStats_s(
697 int64_t timestamp_us, const SessionStats& session_stats,
698 const std::map<std::string, CertificateStatsPair>& transport_cert_stats,
699 RTCStatsReport* report) const {
700 RTC_DCHECK(signaling_thread_->IsCurrent());
701 for (const auto& transport : session_stats.transport_stats) {
702 // Get reference to RTCP channel, if it exists.
703 std::string rtcp_transport_stats_id;
704 for (const auto& channel_stats : transport.second.channel_stats) {
705 if (channel_stats.component ==
706 cricket::ICE_CANDIDATE_COMPONENT_RTCP) {
707 rtcp_transport_stats_id = RTCTransportStatsIDFromTransportChannel(
708 transport.second.transport_name, channel_stats.component);
709 break;
710 }
711 }
712
713 // Get reference to local and remote certificates of this transport, if they
714 // exist.
715 const auto& certificate_stats_it = transport_cert_stats.find(
716 transport.second.transport_name);
717 RTC_DCHECK(certificate_stats_it != transport_cert_stats.cend());
718 std::string local_certificate_id;
719 if (certificate_stats_it->second.local) {
720 local_certificate_id = RTCCertificateIDFromFingerprint(
721 certificate_stats_it->second.local->fingerprint);
722 }
723 std::string remote_certificate_id;
724 if (certificate_stats_it->second.remote) {
725 remote_certificate_id = RTCCertificateIDFromFingerprint(
726 certificate_stats_it->second.remote->fingerprint);
727 }
728
729 // There is one transport stats for each channel.
730 for (const auto& channel_stats : transport.second.channel_stats) {
731 std::unique_ptr<RTCTransportStats> transport_stats(
732 new RTCTransportStats(
733 RTCTransportStatsIDFromTransportChannel(
734 transport.second.transport_name, channel_stats.component),
735 timestamp_us));
736 transport_stats->bytes_sent = 0;
737 transport_stats->bytes_received = 0;
738 transport_stats->active_connection = false;
739 for (const cricket::ConnectionInfo& info :
740 channel_stats.connection_infos) {
741 *transport_stats->bytes_sent += info.sent_total_bytes;
742 *transport_stats->bytes_received += info.recv_total_bytes;
743 if (info.best_connection) {
744 transport_stats->active_connection = true;
745 transport_stats->selected_candidate_pair_id =
746 RTCIceCandidatePairStatsIDFromConnectionInfo(info);
747 }
748 }
749 if (channel_stats.component != cricket::ICE_CANDIDATE_COMPONENT_RTCP &&
750 !rtcp_transport_stats_id.empty()) {
751 transport_stats->rtcp_transport_stats_id = rtcp_transport_stats_id;
752 }
753 if (!local_certificate_id.empty())
754 transport_stats->local_certificate_id = local_certificate_id;
755 if (!remote_certificate_id.empty())
756 transport_stats->remote_certificate_id = remote_certificate_id;
757 report->AddStats(std::move(transport_stats));
758 }
759 }
760}
761
762std::map<std::string, RTCStatsCollector::CertificateStatsPair>
763RTCStatsCollector::PrepareTransportCertificateStats_s(
764 const SessionStats& session_stats) const {
765 RTC_DCHECK(signaling_thread_->IsCurrent());
766 std::map<std::string, CertificateStatsPair> transport_cert_stats;
767 for (const auto& transport_stats : session_stats.transport_stats) {
768 CertificateStatsPair certificate_stats_pair;
769 rtc::scoped_refptr<rtc::RTCCertificate> local_certificate;
770 if (pc_->session()->GetLocalCertificate(
771 transport_stats.second.transport_name, &local_certificate)) {
772 certificate_stats_pair.local =
773 local_certificate->ssl_certificate().GetStats();
774 }
775 std::unique_ptr<rtc::SSLCertificate> remote_certificate =
776 pc_->session()->GetRemoteSSLCertificate(
777 transport_stats.second.transport_name);
778 if (remote_certificate) {
779 certificate_stats_pair.remote = remote_certificate->GetStats();
780 }
781 transport_cert_stats.insert(
782 std::make_pair(transport_stats.second.transport_name,
783 std::move(certificate_stats_pair)));
784 }
785 return transport_cert_stats;
786}
787
hbos82ebe022016-11-14 01:41:09 -0800788void RTCStatsCollector::OnDataChannelCreated(DataChannel* channel) {
789 channel->SignalOpened.connect(this, &RTCStatsCollector::OnDataChannelOpened);
790 channel->SignalClosed.connect(this, &RTCStatsCollector::OnDataChannelClosed);
791}
792
793void RTCStatsCollector::OnDataChannelOpened(DataChannel* channel) {
794 RTC_DCHECK(signaling_thread_->IsCurrent());
795 bool result = internal_record_.opened_data_channels.insert(
796 reinterpret_cast<uintptr_t>(channel)).second;
797 ++internal_record_.data_channels_opened;
798 RTC_DCHECK(result);
799}
800
801void RTCStatsCollector::OnDataChannelClosed(DataChannel* channel) {
802 RTC_DCHECK(signaling_thread_->IsCurrent());
803 // Only channels that have been fully opened (and have increased the
804 // |data_channels_opened_| counter) increase the closed counter.
805 if (internal_record_.opened_data_channels.find(
806 reinterpret_cast<uintptr_t>(channel)) !=
807 internal_record_.opened_data_channels.end()) {
808 ++internal_record_.data_channels_closed;
809 }
810}
811
hboscc555c52016-10-18 12:48:31 -0700812const char* CandidateTypeToRTCIceCandidateTypeForTesting(
813 const std::string& type) {
814 return CandidateTypeToRTCIceCandidateType(type);
815}
816
817const char* DataStateToRTCDataChannelStateForTesting(
818 DataChannelInterface::DataState state) {
819 return DataStateToRTCDataChannelState(state);
820}
821
hbosd565b732016-08-30 14:04:35 -0700822} // namespace webrtc