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