| 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 |