OLD | NEW |
| (Empty) |
1 /* | |
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 | |
11 #include "webrtc/api/rtcstatscollector.h" | |
12 | |
13 #include <memory> | |
14 #include <utility> | |
15 #include <vector> | |
16 | |
17 #include "webrtc/api/peerconnection.h" | |
18 #include "webrtc/api/peerconnectioninterface.h" | |
19 #include "webrtc/api/mediastreaminterface.h" | |
20 #include "webrtc/api/webrtcsession.h" | |
21 #include "webrtc/base/checks.h" | |
22 #include "webrtc/base/timeutils.h" | |
23 #include "webrtc/media/base/mediachannel.h" | |
24 #include "webrtc/p2p/base/candidate.h" | |
25 #include "webrtc/p2p/base/p2pconstants.h" | |
26 #include "webrtc/p2p/base/port.h" | |
27 | |
28 namespace webrtc { | |
29 | |
30 namespace { | |
31 | |
32 std::string RTCCertificateIDFromFingerprint(const std::string& fingerprint) { | |
33 return "RTCCertificate_" + fingerprint; | |
34 } | |
35 | |
36 std::string RTCIceCandidatePairStatsIDFromConnectionInfo( | |
37 const cricket::ConnectionInfo& info) { | |
38 return "RTCIceCandidatePair_" + info.local_candidate.id() + "_" + | |
39 info.remote_candidate.id(); | |
40 } | |
41 | |
42 std::string RTCMediaStreamTrackStatsIDFromMediaStreamTrackInterface( | |
43 const MediaStreamTrackInterface& track) { | |
44 return "RTCMediaStreamTrack_" + track.id(); | |
45 } | |
46 | |
47 std::string RTCTransportStatsIDFromTransportChannel( | |
48 const std::string& transport_name, int channel_component) { | |
49 return "RTCTransport_" + transport_name + "_" + | |
50 rtc::ToString<>(channel_component); | |
51 } | |
52 | |
53 std::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 | |
63 std::string RTCInboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) { | |
64 return audio ? "RTCInboundRTPAudioStream_" + rtc::ToString<>(ssrc) | |
65 : "RTCInboundRTPVideoStream_" + rtc::ToString<>(ssrc); | |
66 } | |
67 | |
68 std::string RTCOutboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) { | |
69 return audio ? "RTCOutboundRTPAudioStream_" + rtc::ToString<>(ssrc) | |
70 : "RTCOutboundRTPVideoStream_" + rtc::ToString<>(ssrc); | |
71 } | |
72 | |
73 const 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 | |
86 const 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 | |
103 void 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 | |
110 void SetInboundRTPStreamStatsFromMediaReceiverInfo( | |
111 const cricket::MediaReceiverInfo& media_receiver_info, | |
112 RTCInboundRTPStreamStats* inbound_stats) { | |
113 RTC_DCHECK(inbound_stats); | |
114 inbound_stats->ssrc = rtc::ToString<>(media_receiver_info.ssrc()); | |
115 // TODO(hbos): Support the remote case. crbug.com/657855 | |
116 inbound_stats->is_remote = false; | |
117 // TODO(hbos): Set |codec_id| when we have |RTCCodecStats|. Maybe relevant: | |
118 // |media_receiver_info.codec_name|. crbug.com/657854, 657855, 659117 | |
119 inbound_stats->packets_received = | |
120 static_cast<uint32_t>(media_receiver_info.packets_rcvd); | |
121 inbound_stats->bytes_received = | |
122 static_cast<uint64_t>(media_receiver_info.bytes_rcvd); | |
123 inbound_stats->fraction_lost = | |
124 static_cast<double>(media_receiver_info.fraction_lost); | |
125 } | |
126 | |
127 void SetInboundRTPStreamStatsFromVoiceReceiverInfo( | |
128 const cricket::VoiceReceiverInfo& voice_receiver_info, | |
129 RTCInboundRTPStreamStats* inbound_stats) { | |
130 SetInboundRTPStreamStatsFromMediaReceiverInfo( | |
131 voice_receiver_info, inbound_stats); | |
132 inbound_stats->media_type = "audio"; | |
133 inbound_stats->jitter = | |
134 static_cast<double>(voice_receiver_info.jitter_ms) / | |
135 rtc::kNumMillisecsPerSec; | |
136 } | |
137 | |
138 void SetInboundRTPStreamStatsFromVideoReceiverInfo( | |
139 const cricket::VideoReceiverInfo& video_receiver_info, | |
140 RTCInboundRTPStreamStats* inbound_stats) { | |
141 SetInboundRTPStreamStatsFromMediaReceiverInfo( | |
142 video_receiver_info, inbound_stats); | |
143 inbound_stats->media_type = "video"; | |
144 } | |
145 | |
146 void SetOutboundRTPStreamStatsFromMediaSenderInfo( | |
147 const cricket::MediaSenderInfo& media_sender_info, | |
148 RTCOutboundRTPStreamStats* outbound_stats) { | |
149 RTC_DCHECK(outbound_stats); | |
150 outbound_stats->ssrc = rtc::ToString<>(media_sender_info.ssrc()); | |
151 // TODO(hbos): Support the remote case. crbug.com/657856 | |
152 outbound_stats->is_remote = false; | |
153 // TODO(hbos): Set |codec_id| when we have |RTCCodecStats|. Maybe relevant: | |
154 // |media_sender_info.codec_name|. crbug.com/657854, 657856, 659117 | |
155 outbound_stats->packets_sent = | |
156 static_cast<uint32_t>(media_sender_info.packets_sent); | |
157 outbound_stats->bytes_sent = | |
158 static_cast<uint64_t>(media_sender_info.bytes_sent); | |
159 outbound_stats->round_trip_time = | |
160 static_cast<double>(media_sender_info.rtt_ms) / rtc::kNumMillisecsPerSec; | |
161 } | |
162 | |
163 void SetOutboundRTPStreamStatsFromVoiceSenderInfo( | |
164 const cricket::VoiceSenderInfo& voice_sender_info, | |
165 RTCOutboundRTPStreamStats* outbound_audio) { | |
166 SetOutboundRTPStreamStatsFromMediaSenderInfo( | |
167 voice_sender_info, outbound_audio); | |
168 outbound_audio->media_type = "audio"; | |
169 // |fir_count|, |pli_count| and |sli_count| are only valid for video and are | |
170 // purposefully left undefined for audio. | |
171 } | |
172 | |
173 void SetOutboundRTPStreamStatsFromVideoSenderInfo( | |
174 const cricket::VideoSenderInfo& video_sender_info, | |
175 RTCOutboundRTPStreamStats* outbound_video) { | |
176 SetOutboundRTPStreamStatsFromMediaSenderInfo( | |
177 video_sender_info, outbound_video); | |
178 outbound_video->media_type = "video"; | |
179 outbound_video->fir_count = | |
180 static_cast<uint32_t>(video_sender_info.firs_rcvd); | |
181 outbound_video->pli_count = | |
182 static_cast<uint32_t>(video_sender_info.plis_rcvd); | |
183 outbound_video->nack_count = | |
184 static_cast<uint32_t>(video_sender_info.nacks_rcvd); | |
185 } | |
186 | |
187 void ProduceCertificateStatsFromSSLCertificateStats( | |
188 int64_t timestamp_us, const rtc::SSLCertificateStats& certificate_stats, | |
189 RTCStatsReport* report) { | |
190 RTCCertificateStats* prev_certificate_stats = nullptr; | |
191 for (const rtc::SSLCertificateStats* s = &certificate_stats; s; | |
192 s = s->issuer.get()) { | |
193 RTCCertificateStats* certificate_stats = new RTCCertificateStats( | |
194 RTCCertificateIDFromFingerprint(s->fingerprint), timestamp_us); | |
195 certificate_stats->fingerprint = s->fingerprint; | |
196 certificate_stats->fingerprint_algorithm = s->fingerprint_algorithm; | |
197 certificate_stats->base64_certificate = s->base64_certificate; | |
198 if (prev_certificate_stats) | |
199 prev_certificate_stats->issuer_certificate_id = certificate_stats->id(); | |
200 report->AddStats(std::unique_ptr<RTCCertificateStats>(certificate_stats)); | |
201 prev_certificate_stats = certificate_stats; | |
202 } | |
203 } | |
204 | |
205 const std::string& ProduceIceCandidateStats( | |
206 int64_t timestamp_us, const cricket::Candidate& candidate, bool is_local, | |
207 RTCStatsReport* report) { | |
208 const std::string& id = "RTCIceCandidate_" + candidate.id(); | |
209 const RTCStats* stats = report->Get(id); | |
210 if (!stats) { | |
211 std::unique_ptr<RTCIceCandidateStats> candidate_stats; | |
212 if (is_local) | |
213 candidate_stats.reset(new RTCLocalIceCandidateStats(id, timestamp_us)); | |
214 else | |
215 candidate_stats.reset(new RTCRemoteIceCandidateStats(id, timestamp_us)); | |
216 candidate_stats->ip = candidate.address().ipaddr().ToString(); | |
217 candidate_stats->port = static_cast<int32_t>(candidate.address().port()); | |
218 candidate_stats->protocol = candidate.protocol(); | |
219 candidate_stats->candidate_type = CandidateTypeToRTCIceCandidateType( | |
220 candidate.type()); | |
221 candidate_stats->priority = static_cast<int32_t>(candidate.priority()); | |
222 | |
223 stats = candidate_stats.get(); | |
224 report->AddStats(std::move(candidate_stats)); | |
225 } | |
226 RTC_DCHECK_EQ(stats->type(), is_local ? RTCLocalIceCandidateStats::kType | |
227 : RTCRemoteIceCandidateStats::kType); | |
228 return stats->id(); | |
229 } | |
230 | |
231 void ProduceMediaStreamAndTrackStats( | |
232 int64_t timestamp_us, | |
233 rtc::scoped_refptr<StreamCollectionInterface> streams, | |
234 bool is_local, | |
235 RTCStatsReport* report) { | |
236 // TODO(hbos): When "AddTrack" is implemented we should iterate tracks to | |
237 // find which streams exist, not iterate streams to find tracks. | |
238 // crbug.com/659137 | |
239 // TODO(hbos): Return stats of detached tracks. We have to perform stats | |
240 // gathering at the time of detachment to get accurate stats and timestamps. | |
241 // crbug.com/659137 | |
242 if (!streams) | |
243 return; | |
244 for (size_t i = 0; i < streams->count(); ++i) { | |
245 MediaStreamInterface* stream = streams->at(i); | |
246 | |
247 std::unique_ptr<RTCMediaStreamStats> stream_stats( | |
248 new RTCMediaStreamStats("RTCMediaStream_" + stream->label(), | |
249 timestamp_us)); | |
250 stream_stats->stream_identifier = stream->label(); | |
251 stream_stats->track_ids = std::vector<std::string>(); | |
252 // Audio Tracks | |
253 for (const rtc::scoped_refptr<AudioTrackInterface>& audio_track : | |
254 stream->GetAudioTracks()) { | |
255 std::string id = RTCMediaStreamTrackStatsIDFromMediaStreamTrackInterface( | |
256 *audio_track.get()); | |
257 if (report->Get(id)) { | |
258 // Skip track, stats already exist for it. | |
259 continue; | |
260 } | |
261 std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats( | |
262 new RTCMediaStreamTrackStats(id, timestamp_us)); | |
263 stream_stats->track_ids->push_back(audio_track_stats->id()); | |
264 SetMediaStreamTrackStatsFromMediaStreamTrackInterface( | |
265 *audio_track.get(), | |
266 audio_track_stats.get()); | |
267 audio_track_stats->remote_source = !is_local; | |
268 audio_track_stats->detached = false; | |
269 int signal_level; | |
270 if (audio_track->GetSignalLevel(&signal_level)) { | |
271 // Convert signal level from [0,32767] int to [0,1] double. | |
272 RTC_DCHECK_GE(signal_level, 0); | |
273 RTC_DCHECK_LE(signal_level, 32767); | |
274 audio_track_stats->audio_level = signal_level / 32767.0; | |
275 } | |
276 if (audio_track->GetAudioProcessor()) { | |
277 AudioProcessorInterface::AudioProcessorStats audio_processor_stats; | |
278 audio_track->GetAudioProcessor()->GetStats(&audio_processor_stats); | |
279 audio_track_stats->echo_return_loss = static_cast<double>( | |
280 audio_processor_stats.echo_return_loss); | |
281 audio_track_stats->echo_return_loss_enhancement = static_cast<double>( | |
282 audio_processor_stats.echo_return_loss_enhancement); | |
283 } | |
284 report->AddStats(std::move(audio_track_stats)); | |
285 } | |
286 // Video Tracks | |
287 for (const rtc::scoped_refptr<VideoTrackInterface>& video_track : | |
288 stream->GetVideoTracks()) { | |
289 std::string id = RTCMediaStreamTrackStatsIDFromMediaStreamTrackInterface( | |
290 *video_track.get()); | |
291 if (report->Get(id)) { | |
292 // Skip track, stats already exist for it. | |
293 continue; | |
294 } | |
295 std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats( | |
296 new RTCMediaStreamTrackStats(id, timestamp_us)); | |
297 stream_stats->track_ids->push_back(video_track_stats->id()); | |
298 SetMediaStreamTrackStatsFromMediaStreamTrackInterface( | |
299 *video_track.get(), | |
300 video_track_stats.get()); | |
301 video_track_stats->remote_source = !is_local; | |
302 video_track_stats->detached = false; | |
303 if (video_track->GetSource()) { | |
304 VideoTrackSourceInterface::Stats video_track_source_stats; | |
305 if (video_track->GetSource()->GetStats(&video_track_source_stats)) { | |
306 video_track_stats->frame_width = static_cast<uint32_t>( | |
307 video_track_source_stats.input_width); | |
308 video_track_stats->frame_height = static_cast<uint32_t>( | |
309 video_track_source_stats.input_height); | |
310 } | |
311 } | |
312 report->AddStats(std::move(video_track_stats)); | |
313 } | |
314 report->AddStats(std::move(stream_stats)); | |
315 } | |
316 } | |
317 | |
318 } // namespace | |
319 | |
320 rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create( | |
321 PeerConnection* pc, int64_t cache_lifetime_us) { | |
322 return rtc::scoped_refptr<RTCStatsCollector>( | |
323 new rtc::RefCountedObject<RTCStatsCollector>(pc, cache_lifetime_us)); | |
324 } | |
325 | |
326 RTCStatsCollector::RTCStatsCollector(PeerConnection* pc, | |
327 int64_t cache_lifetime_us) | |
328 : pc_(pc), | |
329 signaling_thread_(pc->session()->signaling_thread()), | |
330 worker_thread_(pc->session()->worker_thread()), | |
331 network_thread_(pc->session()->network_thread()), | |
332 num_pending_partial_reports_(0), | |
333 partial_report_timestamp_us_(0), | |
334 cache_timestamp_us_(0), | |
335 cache_lifetime_us_(cache_lifetime_us) { | |
336 RTC_DCHECK(pc_); | |
337 RTC_DCHECK(signaling_thread_); | |
338 RTC_DCHECK(worker_thread_); | |
339 RTC_DCHECK(network_thread_); | |
340 RTC_DCHECK_GE(cache_lifetime_us_, 0); | |
341 pc_->SignalDataChannelCreated.connect( | |
342 this, &RTCStatsCollector::OnDataChannelCreated); | |
343 } | |
344 | |
345 void RTCStatsCollector::GetStatsReport( | |
346 rtc::scoped_refptr<RTCStatsCollectorCallback> callback) { | |
347 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
348 RTC_DCHECK(callback); | |
349 callbacks_.push_back(callback); | |
350 | |
351 // "Now" using a monotonically increasing timer. | |
352 int64_t cache_now_us = rtc::TimeMicros(); | |
353 if (cached_report_ && | |
354 cache_now_us - cache_timestamp_us_ <= cache_lifetime_us_) { | |
355 // We have a fresh cached report to deliver. | |
356 DeliverCachedReport(); | |
357 } else if (!num_pending_partial_reports_) { | |
358 // Only start gathering stats if we're not already gathering stats. In the | |
359 // case of already gathering stats, |callback_| will be invoked when there | |
360 // are no more pending partial reports. | |
361 | |
362 // "Now" using a system clock, relative to the UNIX epoch (Jan 1, 1970, | |
363 // UTC), in microseconds. The system clock could be modified and is not | |
364 // necessarily monotonically increasing. | |
365 int64_t timestamp_us = rtc::TimeUTCMicros(); | |
366 | |
367 num_pending_partial_reports_ = 3; | |
368 partial_report_timestamp_us_ = cache_now_us; | |
369 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_, | |
370 rtc::Bind(&RTCStatsCollector::ProducePartialResultsOnSignalingThread, | |
371 rtc::scoped_refptr<RTCStatsCollector>(this), timestamp_us)); | |
372 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, worker_thread_, | |
373 rtc::Bind(&RTCStatsCollector::ProducePartialResultsOnWorkerThread, | |
374 rtc::scoped_refptr<RTCStatsCollector>(this), timestamp_us)); | |
375 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, network_thread_, | |
376 rtc::Bind(&RTCStatsCollector::ProducePartialResultsOnNetworkThread, | |
377 rtc::scoped_refptr<RTCStatsCollector>(this), timestamp_us)); | |
378 } | |
379 } | |
380 | |
381 void RTCStatsCollector::ClearCachedStatsReport() { | |
382 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
383 cached_report_ = nullptr; | |
384 } | |
385 | |
386 void RTCStatsCollector::ProducePartialResultsOnSignalingThread( | |
387 int64_t timestamp_us) { | |
388 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
389 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create( | |
390 timestamp_us); | |
391 | |
392 SessionStats session_stats; | |
393 if (pc_->session()->GetTransportStats(&session_stats)) { | |
394 std::map<std::string, CertificateStatsPair> transport_cert_stats = | |
395 PrepareTransportCertificateStats_s(session_stats); | |
396 | |
397 ProduceCertificateStats_s( | |
398 timestamp_us, transport_cert_stats, report.get()); | |
399 ProduceIceCandidateAndPairStats_s( | |
400 timestamp_us, session_stats, report.get()); | |
401 ProduceRTPStreamStats_s( | |
402 timestamp_us, session_stats, report.get()); | |
403 ProduceTransportStats_s( | |
404 timestamp_us, session_stats, transport_cert_stats, report.get()); | |
405 } | |
406 ProduceDataChannelStats_s(timestamp_us, report.get()); | |
407 ProduceMediaStreamAndTrackStats_s(timestamp_us, report.get()); | |
408 ProducePeerConnectionStats_s(timestamp_us, report.get()); | |
409 | |
410 AddPartialResults(report); | |
411 } | |
412 | |
413 void RTCStatsCollector::ProducePartialResultsOnWorkerThread( | |
414 int64_t timestamp_us) { | |
415 RTC_DCHECK(worker_thread_->IsCurrent()); | |
416 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create( | |
417 timestamp_us); | |
418 | |
419 // TODO(hbos): Gather stats on worker thread. | |
420 // pc_->session()'s channels are owned by the signaling thread but there are | |
421 // some stats that are gathered on the worker thread. Instead of a synchronous | |
422 // invoke on "s->w" we could to the "w" work here asynchronously if it wasn't | |
423 // for the ownership issue. Synchronous invokes in other places makes it | |
424 // difficult to introduce locks without introducing deadlocks and the channels | |
425 // are not reference counted. | |
426 | |
427 AddPartialResults(report); | |
428 } | |
429 | |
430 void RTCStatsCollector::ProducePartialResultsOnNetworkThread( | |
431 int64_t timestamp_us) { | |
432 RTC_DCHECK(network_thread_->IsCurrent()); | |
433 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create( | |
434 timestamp_us); | |
435 | |
436 // TODO(hbos): Gather stats on network thread. | |
437 // pc_->session()'s channels are owned by the signaling thread but there are | |
438 // some stats that are gathered on the network thread. Instead of a | |
439 // synchronous invoke on "s->n" we could to the "n" work here asynchronously | |
440 // if it wasn't for the ownership issue. Synchronous invokes in other places | |
441 // makes it difficult to introduce locks without introducing deadlocks and the | |
442 // channels are not reference counted. | |
443 | |
444 AddPartialResults(report); | |
445 } | |
446 | |
447 void RTCStatsCollector::AddPartialResults( | |
448 const rtc::scoped_refptr<RTCStatsReport>& partial_report) { | |
449 if (!signaling_thread_->IsCurrent()) { | |
450 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_, | |
451 rtc::Bind(&RTCStatsCollector::AddPartialResults_s, | |
452 rtc::scoped_refptr<RTCStatsCollector>(this), | |
453 partial_report)); | |
454 return; | |
455 } | |
456 AddPartialResults_s(partial_report); | |
457 } | |
458 | |
459 void RTCStatsCollector::AddPartialResults_s( | |
460 rtc::scoped_refptr<RTCStatsReport> partial_report) { | |
461 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
462 RTC_DCHECK_GT(num_pending_partial_reports_, 0); | |
463 if (!partial_report_) | |
464 partial_report_ = partial_report; | |
465 else | |
466 partial_report_->TakeMembersFrom(partial_report); | |
467 --num_pending_partial_reports_; | |
468 if (!num_pending_partial_reports_) { | |
469 cache_timestamp_us_ = partial_report_timestamp_us_; | |
470 cached_report_ = partial_report_; | |
471 partial_report_ = nullptr; | |
472 DeliverCachedReport(); | |
473 } | |
474 } | |
475 | |
476 void RTCStatsCollector::DeliverCachedReport() { | |
477 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
478 RTC_DCHECK(!callbacks_.empty()); | |
479 RTC_DCHECK(cached_report_); | |
480 for (const rtc::scoped_refptr<RTCStatsCollectorCallback>& callback : | |
481 callbacks_) { | |
482 callback->OnStatsDelivered(cached_report_); | |
483 } | |
484 callbacks_.clear(); | |
485 } | |
486 | |
487 void RTCStatsCollector::ProduceCertificateStats_s( | |
488 int64_t timestamp_us, | |
489 const std::map<std::string, CertificateStatsPair>& transport_cert_stats, | |
490 RTCStatsReport* report) const { | |
491 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
492 for (const auto& transport_cert_stats_pair : transport_cert_stats) { | |
493 if (transport_cert_stats_pair.second.local) { | |
494 ProduceCertificateStatsFromSSLCertificateStats( | |
495 timestamp_us, *transport_cert_stats_pair.second.local.get(), report); | |
496 } | |
497 if (transport_cert_stats_pair.second.remote) { | |
498 ProduceCertificateStatsFromSSLCertificateStats( | |
499 timestamp_us, *transport_cert_stats_pair.second.remote.get(), report); | |
500 } | |
501 } | |
502 } | |
503 | |
504 void RTCStatsCollector::ProduceDataChannelStats_s( | |
505 int64_t timestamp_us, RTCStatsReport* report) const { | |
506 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
507 for (const rtc::scoped_refptr<DataChannel>& data_channel : | |
508 pc_->sctp_data_channels()) { | |
509 std::unique_ptr<RTCDataChannelStats> data_channel_stats( | |
510 new RTCDataChannelStats( | |
511 "RTCDataChannel_" + rtc::ToString<>(data_channel->id()), | |
512 timestamp_us)); | |
513 data_channel_stats->label = data_channel->label(); | |
514 data_channel_stats->protocol = data_channel->protocol(); | |
515 data_channel_stats->datachannelid = data_channel->id(); | |
516 data_channel_stats->state = | |
517 DataStateToRTCDataChannelState(data_channel->state()); | |
518 data_channel_stats->messages_sent = data_channel->messages_sent(); | |
519 data_channel_stats->bytes_sent = data_channel->bytes_sent(); | |
520 data_channel_stats->messages_received = data_channel->messages_received(); | |
521 data_channel_stats->bytes_received = data_channel->bytes_received(); | |
522 report->AddStats(std::move(data_channel_stats)); | |
523 } | |
524 } | |
525 | |
526 void RTCStatsCollector::ProduceIceCandidateAndPairStats_s( | |
527 int64_t timestamp_us, const SessionStats& session_stats, | |
528 RTCStatsReport* report) const { | |
529 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
530 for (const auto& transport_stats : session_stats.transport_stats) { | |
531 for (const auto& channel_stats : transport_stats.second.channel_stats) { | |
532 for (const cricket::ConnectionInfo& info : | |
533 channel_stats.connection_infos) { | |
534 std::unique_ptr<RTCIceCandidatePairStats> candidate_pair_stats( | |
535 new RTCIceCandidatePairStats( | |
536 RTCIceCandidatePairStatsIDFromConnectionInfo(info), | |
537 timestamp_us)); | |
538 | |
539 // TODO(hbos): There could be other candidates that are not paired with | |
540 // anything. We don't have a complete list. Local candidates come from | |
541 // Port objects, and prflx candidates (both local and remote) are only | |
542 // stored in candidate pairs. crbug.com/632723 | |
543 candidate_pair_stats->local_candidate_id = ProduceIceCandidateStats( | |
544 timestamp_us, info.local_candidate, true, report); | |
545 candidate_pair_stats->remote_candidate_id = ProduceIceCandidateStats( | |
546 timestamp_us, info.remote_candidate, false, report); | |
547 | |
548 // TODO(hbos): This writable is different than the spec. It goes to | |
549 // false after a certain amount of time without a response passes. | |
550 // crbug.com/633550 | |
551 candidate_pair_stats->writable = info.writable; | |
552 candidate_pair_stats->bytes_sent = | |
553 static_cast<uint64_t>(info.sent_total_bytes); | |
554 candidate_pair_stats->bytes_received = | |
555 static_cast<uint64_t>(info.recv_total_bytes); | |
556 // TODO(hbos): The |info.rtt| measurement is smoothed. It shouldn't be | |
557 // smoothed according to the spec. crbug.com/633550. See | |
558 // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-curr
entrtt | |
559 candidate_pair_stats->current_rtt = | |
560 static_cast<double>(info.rtt) / rtc::kNumMillisecsPerSec; | |
561 candidate_pair_stats->requests_sent = | |
562 static_cast<uint64_t>(info.sent_ping_requests_total); | |
563 candidate_pair_stats->responses_received = | |
564 static_cast<uint64_t>(info.recv_ping_responses); | |
565 candidate_pair_stats->responses_sent = | |
566 static_cast<uint64_t>(info.sent_ping_responses); | |
567 | |
568 report->AddStats(std::move(candidate_pair_stats)); | |
569 } | |
570 } | |
571 } | |
572 } | |
573 | |
574 void RTCStatsCollector::ProduceMediaStreamAndTrackStats_s( | |
575 int64_t timestamp_us, RTCStatsReport* report) const { | |
576 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
577 ProduceMediaStreamAndTrackStats( | |
578 timestamp_us, pc_->local_streams(), true, report); | |
579 ProduceMediaStreamAndTrackStats( | |
580 timestamp_us, pc_->remote_streams(), false, report); | |
581 } | |
582 | |
583 void RTCStatsCollector::ProducePeerConnectionStats_s( | |
584 int64_t timestamp_us, RTCStatsReport* report) const { | |
585 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
586 std::unique_ptr<RTCPeerConnectionStats> stats( | |
587 new RTCPeerConnectionStats("RTCPeerConnection", timestamp_us)); | |
588 stats->data_channels_opened = internal_record_.data_channels_opened; | |
589 stats->data_channels_closed = internal_record_.data_channels_closed; | |
590 report->AddStats(std::move(stats)); | |
591 } | |
592 | |
593 void RTCStatsCollector::ProduceRTPStreamStats_s( | |
594 int64_t timestamp_us, const SessionStats& session_stats, | |
595 RTCStatsReport* report) const { | |
596 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
597 | |
598 // Audio | |
599 if (pc_->session()->voice_channel()) { | |
600 cricket::VoiceMediaInfo voice_media_info; | |
601 if (pc_->session()->voice_channel()->GetStats(&voice_media_info)) { | |
602 std::string transport_id = RTCTransportStatsIDFromBaseChannel( | |
603 session_stats.proxy_to_transport, *pc_->session()->voice_channel()); | |
604 RTC_DCHECK(!transport_id.empty()); | |
605 // Inbound | |
606 for (const cricket::VoiceReceiverInfo& voice_receiver_info : | |
607 voice_media_info.receivers) { | |
608 // TODO(nisse): SSRC == 0 currently means none. Delete check when that | |
609 // is fixed. | |
610 if (voice_receiver_info.ssrc() == 0) | |
611 continue; | |
612 std::unique_ptr<RTCInboundRTPStreamStats> inbound_audio( | |
613 new RTCInboundRTPStreamStats( | |
614 RTCInboundRTPStreamStatsIDFromSSRC( | |
615 true, voice_receiver_info.ssrc()), | |
616 timestamp_us)); | |
617 SetInboundRTPStreamStatsFromVoiceReceiverInfo( | |
618 voice_receiver_info, inbound_audio.get()); | |
619 inbound_audio->transport_id = transport_id; | |
620 report->AddStats(std::move(inbound_audio)); | |
621 } | |
622 // Outbound | |
623 for (const cricket::VoiceSenderInfo& voice_sender_info : | |
624 voice_media_info.senders) { | |
625 // TODO(nisse): SSRC == 0 currently means none. Delete check when that | |
626 // is fixed. | |
627 if (voice_sender_info.ssrc() == 0) | |
628 continue; | |
629 std::unique_ptr<RTCOutboundRTPStreamStats> outbound_audio( | |
630 new RTCOutboundRTPStreamStats( | |
631 RTCOutboundRTPStreamStatsIDFromSSRC( | |
632 true, voice_sender_info.ssrc()), | |
633 timestamp_us)); | |
634 SetOutboundRTPStreamStatsFromVoiceSenderInfo( | |
635 voice_sender_info, outbound_audio.get()); | |
636 outbound_audio->transport_id = transport_id; | |
637 report->AddStats(std::move(outbound_audio)); | |
638 } | |
639 } | |
640 } | |
641 // Video | |
642 if (pc_->session()->video_channel()) { | |
643 cricket::VideoMediaInfo video_media_info; | |
644 if (pc_->session()->video_channel()->GetStats(&video_media_info)) { | |
645 std::string transport_id = RTCTransportStatsIDFromBaseChannel( | |
646 session_stats.proxy_to_transport, *pc_->session()->video_channel()); | |
647 RTC_DCHECK(!transport_id.empty()); | |
648 // Inbound | |
649 for (const cricket::VideoReceiverInfo& video_receiver_info : | |
650 video_media_info.receivers) { | |
651 // TODO(nisse): SSRC == 0 currently means none. Delete check when that | |
652 // is fixed. | |
653 if (video_receiver_info.ssrc() == 0) | |
654 continue; | |
655 std::unique_ptr<RTCInboundRTPStreamStats> inbound_video( | |
656 new RTCInboundRTPStreamStats( | |
657 RTCInboundRTPStreamStatsIDFromSSRC( | |
658 false, video_receiver_info.ssrc()), | |
659 timestamp_us)); | |
660 SetInboundRTPStreamStatsFromVideoReceiverInfo( | |
661 video_receiver_info, inbound_video.get()); | |
662 inbound_video->transport_id = transport_id; | |
663 report->AddStats(std::move(inbound_video)); | |
664 } | |
665 // Outbound | |
666 for (const cricket::VideoSenderInfo& video_sender_info : | |
667 video_media_info.senders) { | |
668 // TODO(nisse): SSRC == 0 currently means none. Delete check when that | |
669 // is fixed. | |
670 if (video_sender_info.ssrc() == 0) | |
671 continue; | |
672 std::unique_ptr<RTCOutboundRTPStreamStats> outbound_video( | |
673 new RTCOutboundRTPStreamStats( | |
674 RTCOutboundRTPStreamStatsIDFromSSRC( | |
675 false, video_sender_info.ssrc()), | |
676 timestamp_us)); | |
677 SetOutboundRTPStreamStatsFromVideoSenderInfo( | |
678 video_sender_info, outbound_video.get()); | |
679 outbound_video->transport_id = transport_id; | |
680 report->AddStats(std::move(outbound_video)); | |
681 } | |
682 } | |
683 } | |
684 } | |
685 | |
686 void RTCStatsCollector::ProduceTransportStats_s( | |
687 int64_t timestamp_us, const SessionStats& session_stats, | |
688 const std::map<std::string, CertificateStatsPair>& transport_cert_stats, | |
689 RTCStatsReport* report) const { | |
690 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
691 for (const auto& transport : session_stats.transport_stats) { | |
692 // Get reference to RTCP channel, if it exists. | |
693 std::string rtcp_transport_stats_id; | |
694 for (const auto& channel_stats : transport.second.channel_stats) { | |
695 if (channel_stats.component == | |
696 cricket::ICE_CANDIDATE_COMPONENT_RTCP) { | |
697 rtcp_transport_stats_id = RTCTransportStatsIDFromTransportChannel( | |
698 transport.second.transport_name, channel_stats.component); | |
699 break; | |
700 } | |
701 } | |
702 | |
703 // Get reference to local and remote certificates of this transport, if they | |
704 // exist. | |
705 const auto& certificate_stats_it = transport_cert_stats.find( | |
706 transport.second.transport_name); | |
707 RTC_DCHECK(certificate_stats_it != transport_cert_stats.cend()); | |
708 std::string local_certificate_id; | |
709 if (certificate_stats_it->second.local) { | |
710 local_certificate_id = RTCCertificateIDFromFingerprint( | |
711 certificate_stats_it->second.local->fingerprint); | |
712 } | |
713 std::string remote_certificate_id; | |
714 if (certificate_stats_it->second.remote) { | |
715 remote_certificate_id = RTCCertificateIDFromFingerprint( | |
716 certificate_stats_it->second.remote->fingerprint); | |
717 } | |
718 | |
719 // There is one transport stats for each channel. | |
720 for (const auto& channel_stats : transport.second.channel_stats) { | |
721 std::unique_ptr<RTCTransportStats> transport_stats( | |
722 new RTCTransportStats( | |
723 RTCTransportStatsIDFromTransportChannel( | |
724 transport.second.transport_name, channel_stats.component), | |
725 timestamp_us)); | |
726 transport_stats->bytes_sent = 0; | |
727 transport_stats->bytes_received = 0; | |
728 transport_stats->active_connection = false; | |
729 for (const cricket::ConnectionInfo& info : | |
730 channel_stats.connection_infos) { | |
731 *transport_stats->bytes_sent += info.sent_total_bytes; | |
732 *transport_stats->bytes_received += info.recv_total_bytes; | |
733 if (info.best_connection) { | |
734 transport_stats->active_connection = true; | |
735 transport_stats->selected_candidate_pair_id = | |
736 RTCIceCandidatePairStatsIDFromConnectionInfo(info); | |
737 } | |
738 } | |
739 if (channel_stats.component != cricket::ICE_CANDIDATE_COMPONENT_RTCP && | |
740 !rtcp_transport_stats_id.empty()) { | |
741 transport_stats->rtcp_transport_stats_id = rtcp_transport_stats_id; | |
742 } | |
743 if (!local_certificate_id.empty()) | |
744 transport_stats->local_certificate_id = local_certificate_id; | |
745 if (!remote_certificate_id.empty()) | |
746 transport_stats->remote_certificate_id = remote_certificate_id; | |
747 report->AddStats(std::move(transport_stats)); | |
748 } | |
749 } | |
750 } | |
751 | |
752 std::map<std::string, RTCStatsCollector::CertificateStatsPair> | |
753 RTCStatsCollector::PrepareTransportCertificateStats_s( | |
754 const SessionStats& session_stats) const { | |
755 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
756 std::map<std::string, CertificateStatsPair> transport_cert_stats; | |
757 for (const auto& transport_stats : session_stats.transport_stats) { | |
758 CertificateStatsPair certificate_stats_pair; | |
759 rtc::scoped_refptr<rtc::RTCCertificate> local_certificate; | |
760 if (pc_->session()->GetLocalCertificate( | |
761 transport_stats.second.transport_name, &local_certificate)) { | |
762 certificate_stats_pair.local = | |
763 local_certificate->ssl_certificate().GetStats(); | |
764 } | |
765 std::unique_ptr<rtc::SSLCertificate> remote_certificate = | |
766 pc_->session()->GetRemoteSSLCertificate( | |
767 transport_stats.second.transport_name); | |
768 if (remote_certificate) { | |
769 certificate_stats_pair.remote = remote_certificate->GetStats(); | |
770 } | |
771 transport_cert_stats.insert( | |
772 std::make_pair(transport_stats.second.transport_name, | |
773 std::move(certificate_stats_pair))); | |
774 } | |
775 return transport_cert_stats; | |
776 } | |
777 | |
778 void RTCStatsCollector::OnDataChannelCreated(DataChannel* channel) { | |
779 channel->SignalOpened.connect(this, &RTCStatsCollector::OnDataChannelOpened); | |
780 channel->SignalClosed.connect(this, &RTCStatsCollector::OnDataChannelClosed); | |
781 } | |
782 | |
783 void RTCStatsCollector::OnDataChannelOpened(DataChannel* channel) { | |
784 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
785 bool result = internal_record_.opened_data_channels.insert( | |
786 reinterpret_cast<uintptr_t>(channel)).second; | |
787 ++internal_record_.data_channels_opened; | |
788 RTC_DCHECK(result); | |
789 } | |
790 | |
791 void RTCStatsCollector::OnDataChannelClosed(DataChannel* channel) { | |
792 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
793 // Only channels that have been fully opened (and have increased the | |
794 // |data_channels_opened_| counter) increase the closed counter. | |
795 if (internal_record_.opened_data_channels.find( | |
796 reinterpret_cast<uintptr_t>(channel)) != | |
797 internal_record_.opened_data_channels.end()) { | |
798 ++internal_record_.data_channels_closed; | |
799 } | |
800 } | |
801 | |
802 const char* CandidateTypeToRTCIceCandidateTypeForTesting( | |
803 const std::string& type) { | |
804 return CandidateTypeToRTCIceCandidateType(type); | |
805 } | |
806 | |
807 const char* DataStateToRTCDataChannelStateForTesting( | |
808 DataChannelInterface::DataState state) { | |
809 return DataStateToRTCDataChannelState(state); | |
810 } | |
811 | |
812 } // namespace webrtc | |
OLD | NEW |