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