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