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 <sstream> | |
15 #include <utility> | |
16 #include <vector> | |
17 | |
18 #include "webrtc/api/peerconnection.h" | |
19 #include "webrtc/api/peerconnectioninterface.h" | |
20 #include "webrtc/api/mediastreaminterface.h" | |
21 #include "webrtc/api/webrtcsession.h" | |
22 #include "webrtc/base/checks.h" | |
23 #include "webrtc/base/timeutils.h" | |
24 #include "webrtc/media/base/mediachannel.h" | |
25 #include "webrtc/p2p/base/candidate.h" | |
26 #include "webrtc/p2p/base/p2pconstants.h" | |
27 #include "webrtc/p2p/base/port.h" | |
28 | |
29 namespace webrtc { | |
30 | |
31 namespace { | |
32 | |
33 std::string RTCCertificateIDFromFingerprint(const std::string& fingerprint) { | |
34 return "RTCCertificate_" + fingerprint; | |
35 } | |
36 | |
37 std::string RTCCodecStatsIDFromDirectionMediaAndPayload( | |
38 bool inbound, bool audio, uint32_t payload_type) { | |
39 // TODO(hbos): When we are able to handle multiple m= lines of the same media | |
40 // type (and multiple BaseChannels for the same type is possible?) this needs | |
41 // to be updated to differentiate the transport being used, and stats need to | |
42 // be collected for all of them. crbug.com/659117 | |
43 if (inbound) { | |
44 return audio ? "RTCCodec_InboundAudio_" + rtc::ToString<>(payload_type) | |
45 : "RTCCodec_InboundVideo_" + rtc::ToString<>(payload_type); | |
46 } | |
47 return audio ? "RTCCodec_OutboundAudio_" + rtc::ToString<>(payload_type) | |
48 : "RTCCodec_OutboundVideo_" + rtc::ToString<>(payload_type); | |
49 } | |
50 | |
51 std::string RTCIceCandidatePairStatsIDFromConnectionInfo( | |
52 const cricket::ConnectionInfo& info) { | |
53 return "RTCIceCandidatePair_" + info.local_candidate.id() + "_" + | |
54 info.remote_candidate.id(); | |
55 } | |
56 | |
57 std::string RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc( | |
58 bool is_local, const char* kind, const std::string& id, uint32_t ssrc) { | |
59 RTC_DCHECK(kind == MediaStreamTrackInterface::kAudioKind || | |
60 kind == MediaStreamTrackInterface::kVideoKind); | |
61 std::ostringstream oss; | |
62 oss << (is_local ? "RTCMediaStreamTrack_local_" | |
63 : "RTCMediaStreamTrack_remote_"); | |
64 oss << kind << "_"; | |
65 oss << id << "_"; | |
66 oss << ssrc; | |
67 return oss.str(); | |
68 } | |
69 | |
70 std::string RTCTransportStatsIDFromTransportChannel( | |
71 const std::string& transport_name, int channel_component) { | |
72 return "RTCTransport_" + transport_name + "_" + | |
73 rtc::ToString<>(channel_component); | |
74 } | |
75 | |
76 std::string RTCTransportStatsIDFromBaseChannel( | |
77 const ProxyTransportMap& proxy_to_transport, | |
78 const cricket::BaseChannel& base_channel) { | |
79 auto proxy_it = proxy_to_transport.find(base_channel.content_name()); | |
80 if (proxy_it == proxy_to_transport.cend()) | |
81 return ""; | |
82 return RTCTransportStatsIDFromTransportChannel( | |
83 proxy_it->second, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
84 } | |
85 | |
86 std::string RTCInboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) { | |
87 return audio ? "RTCInboundRTPAudioStream_" + rtc::ToString<>(ssrc) | |
88 : "RTCInboundRTPVideoStream_" + rtc::ToString<>(ssrc); | |
89 } | |
90 | |
91 std::string RTCOutboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) { | |
92 return audio ? "RTCOutboundRTPAudioStream_" + rtc::ToString<>(ssrc) | |
93 : "RTCOutboundRTPVideoStream_" + rtc::ToString<>(ssrc); | |
94 } | |
95 | |
96 const char* CandidateTypeToRTCIceCandidateType(const std::string& type) { | |
97 if (type == cricket::LOCAL_PORT_TYPE) | |
98 return RTCIceCandidateType::kHost; | |
99 if (type == cricket::STUN_PORT_TYPE) | |
100 return RTCIceCandidateType::kSrflx; | |
101 if (type == cricket::PRFLX_PORT_TYPE) | |
102 return RTCIceCandidateType::kPrflx; | |
103 if (type == cricket::RELAY_PORT_TYPE) | |
104 return RTCIceCandidateType::kRelay; | |
105 RTC_NOTREACHED(); | |
106 return nullptr; | |
107 } | |
108 | |
109 const char* DataStateToRTCDataChannelState( | |
110 DataChannelInterface::DataState state) { | |
111 switch (state) { | |
112 case DataChannelInterface::kConnecting: | |
113 return RTCDataChannelState::kConnecting; | |
114 case DataChannelInterface::kOpen: | |
115 return RTCDataChannelState::kOpen; | |
116 case DataChannelInterface::kClosing: | |
117 return RTCDataChannelState::kClosing; | |
118 case DataChannelInterface::kClosed: | |
119 return RTCDataChannelState::kClosed; | |
120 default: | |
121 RTC_NOTREACHED(); | |
122 return nullptr; | |
123 } | |
124 } | |
125 | |
126 const char* IceCandidatePairStateToRTCStatsIceCandidatePairState( | |
127 cricket::IceCandidatePairState state) { | |
128 switch (state) { | |
129 case cricket::IceCandidatePairState::WAITING: | |
130 return RTCStatsIceCandidatePairState::kWaiting; | |
131 case cricket::IceCandidatePairState::IN_PROGRESS: | |
132 return RTCStatsIceCandidatePairState::kInProgress; | |
133 case cricket::IceCandidatePairState::SUCCEEDED: | |
134 return RTCStatsIceCandidatePairState::kSucceeded; | |
135 case cricket::IceCandidatePairState::FAILED: | |
136 return RTCStatsIceCandidatePairState::kFailed; | |
137 default: | |
138 RTC_NOTREACHED(); | |
139 return nullptr; | |
140 } | |
141 } | |
142 | |
143 const char* DtlsTransportStateToRTCDtlsTransportState( | |
144 cricket::DtlsTransportState state) { | |
145 switch (state) { | |
146 case cricket::DTLS_TRANSPORT_NEW: | |
147 return RTCDtlsTransportState::kNew; | |
148 case cricket::DTLS_TRANSPORT_CONNECTING: | |
149 return RTCDtlsTransportState::kConnecting; | |
150 case cricket::DTLS_TRANSPORT_CONNECTED: | |
151 return RTCDtlsTransportState::kConnected; | |
152 case cricket::DTLS_TRANSPORT_CLOSED: | |
153 return RTCDtlsTransportState::kClosed; | |
154 case cricket::DTLS_TRANSPORT_FAILED: | |
155 return RTCDtlsTransportState::kFailed; | |
156 default: | |
157 RTC_NOTREACHED(); | |
158 return nullptr; | |
159 } | |
160 } | |
161 | |
162 double DoubleAudioLevelFromIntAudioLevel(int audio_level) { | |
163 RTC_DCHECK_GE(audio_level, 0); | |
164 RTC_DCHECK_LE(audio_level, 32767); | |
165 return audio_level / 32767.0; | |
166 } | |
167 | |
168 std::unique_ptr<RTCCodecStats> CodecStatsFromRtpCodecParameters( | |
169 uint64_t timestamp_us, bool inbound, bool audio, | |
170 const RtpCodecParameters& codec_params) { | |
171 RTC_DCHECK_GE(codec_params.payload_type, 0); | |
172 RTC_DCHECK_LE(codec_params.payload_type, 127); | |
173 uint32_t payload_type = static_cast<uint32_t>(codec_params.payload_type); | |
174 std::unique_ptr<RTCCodecStats> codec_stats(new RTCCodecStats( | |
175 RTCCodecStatsIDFromDirectionMediaAndPayload(inbound, audio, payload_type), | |
176 timestamp_us)); | |
177 codec_stats->payload_type = payload_type; | |
178 codec_stats->codec = (audio ? "audio/" : "video/") + codec_params.mime_type; | |
179 codec_stats->clock_rate = static_cast<uint32_t>(codec_params.clock_rate); | |
180 return codec_stats; | |
181 } | |
182 | |
183 void SetMediaStreamTrackStatsFromMediaStreamTrackInterface( | |
184 const MediaStreamTrackInterface& track, | |
185 RTCMediaStreamTrackStats* track_stats) { | |
186 track_stats->track_identifier = track.id(); | |
187 track_stats->ended = (track.state() == MediaStreamTrackInterface::kEnded); | |
188 } | |
189 | |
190 // Provides the media independent counters (both audio and video). | |
191 void SetInboundRTPStreamStatsFromMediaReceiverInfo( | |
192 const cricket::MediaReceiverInfo& media_receiver_info, | |
193 RTCInboundRTPStreamStats* inbound_stats) { | |
194 RTC_DCHECK(inbound_stats); | |
195 inbound_stats->ssrc = rtc::ToString<>(media_receiver_info.ssrc()); | |
196 // TODO(hbos): Support the remote case. crbug.com/657855 | |
197 inbound_stats->is_remote = false; | |
198 // TODO(hbos): Set |codec_id| when we have |RTCCodecStats|. Maybe relevant: | |
199 // |media_receiver_info.codec_name|. crbug.com/657854, 657855, 659117 | |
200 inbound_stats->packets_received = | |
201 static_cast<uint32_t>(media_receiver_info.packets_rcvd); | |
202 inbound_stats->bytes_received = | |
203 static_cast<uint64_t>(media_receiver_info.bytes_rcvd); | |
204 inbound_stats->packets_lost = | |
205 static_cast<uint32_t>(media_receiver_info.packets_lost); | |
206 inbound_stats->fraction_lost = | |
207 static_cast<double>(media_receiver_info.fraction_lost); | |
208 } | |
209 | |
210 void SetInboundRTPStreamStatsFromVoiceReceiverInfo( | |
211 const cricket::VoiceReceiverInfo& voice_receiver_info, | |
212 RTCInboundRTPStreamStats* inbound_audio) { | |
213 SetInboundRTPStreamStatsFromMediaReceiverInfo( | |
214 voice_receiver_info, inbound_audio); | |
215 inbound_audio->media_type = "audio"; | |
216 inbound_audio->jitter = | |
217 static_cast<double>(voice_receiver_info.jitter_ms) / | |
218 rtc::kNumMillisecsPerSec; | |
219 // |fir_count|, |pli_count| and |sli_count| are only valid for video and are | |
220 // purposefully left undefined for audio. | |
221 } | |
222 | |
223 void SetInboundRTPStreamStatsFromVideoReceiverInfo( | |
224 const cricket::VideoReceiverInfo& video_receiver_info, | |
225 RTCInboundRTPStreamStats* inbound_video) { | |
226 SetInboundRTPStreamStatsFromMediaReceiverInfo( | |
227 video_receiver_info, inbound_video); | |
228 inbound_video->media_type = "video"; | |
229 inbound_video->fir_count = | |
230 static_cast<uint32_t>(video_receiver_info.firs_sent); | |
231 inbound_video->pli_count = | |
232 static_cast<uint32_t>(video_receiver_info.plis_sent); | |
233 inbound_video->nack_count = | |
234 static_cast<uint32_t>(video_receiver_info.nacks_sent); | |
235 inbound_video->frames_decoded = video_receiver_info.frames_decoded; | |
236 } | |
237 | |
238 // Provides the media independent counters (both audio and video). | |
239 void SetOutboundRTPStreamStatsFromMediaSenderInfo( | |
240 const cricket::MediaSenderInfo& media_sender_info, | |
241 RTCOutboundRTPStreamStats* outbound_stats) { | |
242 RTC_DCHECK(outbound_stats); | |
243 outbound_stats->ssrc = rtc::ToString<>(media_sender_info.ssrc()); | |
244 // TODO(hbos): Support the remote case. crbug.com/657856 | |
245 outbound_stats->is_remote = false; | |
246 // TODO(hbos): Set |codec_id| when we have |RTCCodecStats|. Maybe relevant: | |
247 // |media_sender_info.codec_name|. crbug.com/657854, 657856, 659117 | |
248 outbound_stats->packets_sent = | |
249 static_cast<uint32_t>(media_sender_info.packets_sent); | |
250 outbound_stats->bytes_sent = | |
251 static_cast<uint64_t>(media_sender_info.bytes_sent); | |
252 if (media_sender_info.rtt_ms >= 0) { | |
253 outbound_stats->round_trip_time = static_cast<double>( | |
254 media_sender_info.rtt_ms) / rtc::kNumMillisecsPerSec; | |
255 } | |
256 } | |
257 | |
258 void SetOutboundRTPStreamStatsFromVoiceSenderInfo( | |
259 const cricket::VoiceSenderInfo& voice_sender_info, | |
260 RTCOutboundRTPStreamStats* outbound_audio) { | |
261 SetOutboundRTPStreamStatsFromMediaSenderInfo( | |
262 voice_sender_info, outbound_audio); | |
263 outbound_audio->media_type = "audio"; | |
264 // |fir_count|, |pli_count| and |sli_count| are only valid for video and are | |
265 // purposefully left undefined for audio. | |
266 } | |
267 | |
268 void SetOutboundRTPStreamStatsFromVideoSenderInfo( | |
269 const cricket::VideoSenderInfo& video_sender_info, | |
270 RTCOutboundRTPStreamStats* outbound_video) { | |
271 SetOutboundRTPStreamStatsFromMediaSenderInfo( | |
272 video_sender_info, outbound_video); | |
273 outbound_video->media_type = "video"; | |
274 outbound_video->fir_count = | |
275 static_cast<uint32_t>(video_sender_info.firs_rcvd); | |
276 outbound_video->pli_count = | |
277 static_cast<uint32_t>(video_sender_info.plis_rcvd); | |
278 outbound_video->nack_count = | |
279 static_cast<uint32_t>(video_sender_info.nacks_rcvd); | |
280 if (video_sender_info.qp_sum) | |
281 outbound_video->qp_sum = *video_sender_info.qp_sum; | |
282 outbound_video->frames_encoded = video_sender_info.frames_encoded; | |
283 } | |
284 | |
285 void ProduceCertificateStatsFromSSLCertificateStats( | |
286 int64_t timestamp_us, const rtc::SSLCertificateStats& certificate_stats, | |
287 RTCStatsReport* report) { | |
288 RTCCertificateStats* prev_certificate_stats = nullptr; | |
289 for (const rtc::SSLCertificateStats* s = &certificate_stats; s; | |
290 s = s->issuer.get()) { | |
291 std::string certificate_stats_id = | |
292 RTCCertificateIDFromFingerprint(s->fingerprint); | |
293 // It is possible for the same certificate to show up multiple times, e.g. | |
294 // if local and remote side use the same certificate in a loopback call. | |
295 // If the report already contains stats for this certificate, skip it. | |
296 if (report->Get(certificate_stats_id)) { | |
297 RTC_DCHECK_EQ(s, &certificate_stats); | |
298 break; | |
299 } | |
300 RTCCertificateStats* certificate_stats = new RTCCertificateStats( | |
301 certificate_stats_id, timestamp_us); | |
302 certificate_stats->fingerprint = s->fingerprint; | |
303 certificate_stats->fingerprint_algorithm = s->fingerprint_algorithm; | |
304 certificate_stats->base64_certificate = s->base64_certificate; | |
305 if (prev_certificate_stats) | |
306 prev_certificate_stats->issuer_certificate_id = certificate_stats->id(); | |
307 report->AddStats(std::unique_ptr<RTCCertificateStats>(certificate_stats)); | |
308 prev_certificate_stats = certificate_stats; | |
309 } | |
310 } | |
311 | |
312 const std::string& ProduceIceCandidateStats( | |
313 int64_t timestamp_us, const cricket::Candidate& candidate, bool is_local, | |
314 const std::string& transport_id, RTCStatsReport* report) { | |
315 const std::string& id = "RTCIceCandidate_" + candidate.id(); | |
316 const RTCStats* stats = report->Get(id); | |
317 if (!stats) { | |
318 std::unique_ptr<RTCIceCandidateStats> candidate_stats; | |
319 if (is_local) | |
320 candidate_stats.reset(new RTCLocalIceCandidateStats(id, timestamp_us)); | |
321 else | |
322 candidate_stats.reset(new RTCRemoteIceCandidateStats(id, timestamp_us)); | |
323 candidate_stats->transport_id = transport_id; | |
324 candidate_stats->ip = candidate.address().ipaddr().ToString(); | |
325 candidate_stats->port = static_cast<int32_t>(candidate.address().port()); | |
326 candidate_stats->protocol = candidate.protocol(); | |
327 candidate_stats->candidate_type = CandidateTypeToRTCIceCandidateType( | |
328 candidate.type()); | |
329 candidate_stats->priority = static_cast<int32_t>(candidate.priority()); | |
330 | |
331 stats = candidate_stats.get(); | |
332 report->AddStats(std::move(candidate_stats)); | |
333 } | |
334 RTC_DCHECK_EQ(stats->type(), is_local ? RTCLocalIceCandidateStats::kType | |
335 : RTCRemoteIceCandidateStats::kType); | |
336 return stats->id(); | |
337 } | |
338 | |
339 std::unique_ptr<RTCMediaStreamTrackStats> | |
340 ProduceMediaStreamTrackStatsFromVoiceSenderInfo( | |
341 int64_t timestamp_us, | |
342 const AudioTrackInterface& audio_track, | |
343 const cricket::VoiceSenderInfo& voice_sender_info) { | |
344 std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats( | |
345 new RTCMediaStreamTrackStats( | |
346 RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc( | |
347 true, MediaStreamTrackInterface::kAudioKind, audio_track.id(), | |
348 voice_sender_info.ssrc()), | |
349 timestamp_us, | |
350 RTCMediaStreamTrackKind::kAudio)); | |
351 SetMediaStreamTrackStatsFromMediaStreamTrackInterface( | |
352 audio_track, audio_track_stats.get()); | |
353 audio_track_stats->remote_source = false; | |
354 audio_track_stats->detached = false; | |
355 if (voice_sender_info.audio_level >= 0) { | |
356 audio_track_stats->audio_level = DoubleAudioLevelFromIntAudioLevel( | |
357 voice_sender_info.audio_level); | |
358 } | |
359 if (voice_sender_info.echo_return_loss != -100) { | |
360 audio_track_stats->echo_return_loss = static_cast<double>( | |
361 voice_sender_info.echo_return_loss); | |
362 } | |
363 if (voice_sender_info.echo_return_loss_enhancement != -100) { | |
364 audio_track_stats->echo_return_loss_enhancement = static_cast<double>( | |
365 voice_sender_info.echo_return_loss_enhancement); | |
366 } | |
367 return audio_track_stats; | |
368 } | |
369 | |
370 std::unique_ptr<RTCMediaStreamTrackStats> | |
371 ProduceMediaStreamTrackStatsFromVoiceReceiverInfo( | |
372 int64_t timestamp_us, | |
373 const AudioTrackInterface& audio_track, | |
374 const cricket::VoiceReceiverInfo& voice_receiver_info) { | |
375 std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats( | |
376 new RTCMediaStreamTrackStats( | |
377 RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc( | |
378 false, MediaStreamTrackInterface::kAudioKind, audio_track.id(), | |
379 voice_receiver_info.ssrc()), | |
380 timestamp_us, | |
381 RTCMediaStreamTrackKind::kAudio)); | |
382 SetMediaStreamTrackStatsFromMediaStreamTrackInterface( | |
383 audio_track, audio_track_stats.get()); | |
384 audio_track_stats->remote_source = true; | |
385 audio_track_stats->detached = false; | |
386 if (voice_receiver_info.audio_level >= 0) { | |
387 audio_track_stats->audio_level = DoubleAudioLevelFromIntAudioLevel( | |
388 voice_receiver_info.audio_level); | |
389 } | |
390 return audio_track_stats; | |
391 } | |
392 | |
393 std::unique_ptr<RTCMediaStreamTrackStats> | |
394 ProduceMediaStreamTrackStatsFromVideoSenderInfo( | |
395 int64_t timestamp_us, | |
396 const VideoTrackInterface& video_track, | |
397 const cricket::VideoSenderInfo& video_sender_info) { | |
398 std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats( | |
399 new RTCMediaStreamTrackStats( | |
400 RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc( | |
401 true, MediaStreamTrackInterface::kVideoKind, video_track.id(), | |
402 video_sender_info.ssrc()), | |
403 timestamp_us, | |
404 RTCMediaStreamTrackKind::kVideo)); | |
405 SetMediaStreamTrackStatsFromMediaStreamTrackInterface( | |
406 video_track, video_track_stats.get()); | |
407 video_track_stats->remote_source = false; | |
408 video_track_stats->detached = false; | |
409 video_track_stats->frame_width = static_cast<uint32_t>( | |
410 video_sender_info.send_frame_width); | |
411 video_track_stats->frame_height = static_cast<uint32_t>( | |
412 video_sender_info.send_frame_height); | |
413 // TODO(hbos): Will reduce this by frames dropped due to congestion control | |
414 // when available. crbug.com/659137 | |
415 video_track_stats->frames_sent = video_sender_info.frames_encoded; | |
416 return video_track_stats; | |
417 } | |
418 | |
419 std::unique_ptr<RTCMediaStreamTrackStats> | |
420 ProduceMediaStreamTrackStatsFromVideoReceiverInfo( | |
421 int64_t timestamp_us, | |
422 const VideoTrackInterface& video_track, | |
423 const cricket::VideoReceiverInfo& video_receiver_info) { | |
424 std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats( | |
425 new RTCMediaStreamTrackStats( | |
426 RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc( | |
427 false, MediaStreamTrackInterface::kVideoKind, video_track.id(), | |
428 video_receiver_info.ssrc()), | |
429 timestamp_us, | |
430 RTCMediaStreamTrackKind::kVideo)); | |
431 SetMediaStreamTrackStatsFromMediaStreamTrackInterface( | |
432 video_track, video_track_stats.get()); | |
433 video_track_stats->remote_source = true; | |
434 video_track_stats->detached = false; | |
435 if (video_receiver_info.frame_width > 0 && | |
436 video_receiver_info.frame_height > 0) { | |
437 video_track_stats->frame_width = static_cast<uint32_t>( | |
438 video_receiver_info.frame_width); | |
439 video_track_stats->frame_height = static_cast<uint32_t>( | |
440 video_receiver_info.frame_height); | |
441 } | |
442 video_track_stats->frames_received = video_receiver_info.frames_received; | |
443 // TODO(hbos): When we support receiving simulcast, this should be the total | |
444 // number of frames correctly decoded, independent of which SSRC it was | |
445 // received from. Since we don't support that, this is correct and is the same | |
446 // value as "RTCInboundRTPStreamStats.framesDecoded". crbug.com/659137 | |
447 video_track_stats->frames_decoded = video_receiver_info.frames_decoded; | |
448 return video_track_stats; | |
449 } | |
450 | |
451 void ProduceMediaStreamAndTrackStats( | |
452 int64_t timestamp_us, | |
453 const TrackMediaInfoMap& track_media_info_map, | |
454 rtc::scoped_refptr<StreamCollectionInterface> streams, | |
455 bool is_local, | |
456 RTCStatsReport* report) { | |
457 // TODO(hbos): When "AddTrack" is implemented we should iterate tracks to | |
458 // find which streams exist, not iterate streams to find tracks. | |
459 // crbug.com/659137 | |
460 // TODO(hbos): Return stats of detached tracks. We have to perform stats | |
461 // gathering at the time of detachment to get accurate stats and timestamps. | |
462 // crbug.com/659137 | |
463 if (!streams) | |
464 return; | |
465 for (size_t i = 0; i < streams->count(); ++i) { | |
466 MediaStreamInterface* stream = streams->at(i); | |
467 | |
468 std::unique_ptr<RTCMediaStreamStats> stream_stats( | |
469 new RTCMediaStreamStats( | |
470 (is_local ? "RTCMediaStream_local_" : "RTCMediaStream_remote_") + | |
471 stream->label(), timestamp_us)); | |
472 stream_stats->stream_identifier = stream->label(); | |
473 stream_stats->track_ids = std::vector<std::string>(); | |
474 // The track stats are per-attachment to the connection. There can be one | |
475 // for receiving (remote) tracks and multiple attachments for sending | |
476 // (local) tracks. | |
477 if (is_local) { | |
478 // Local Audio Tracks | |
479 for (const rtc::scoped_refptr<AudioTrackInterface>& audio_track : | |
480 stream->GetAudioTracks()) { | |
481 const std::vector<cricket::VoiceSenderInfo*>* voice_sender_infos = | |
482 track_media_info_map.GetVoiceSenderInfos(*audio_track); | |
483 if (!voice_sender_infos) { | |
484 continue; | |
485 } | |
486 for (const auto& voice_sender_info : *voice_sender_infos) { | |
487 std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats = | |
488 ProduceMediaStreamTrackStatsFromVoiceSenderInfo( | |
489 timestamp_us, *audio_track, *voice_sender_info); | |
490 stream_stats->track_ids->push_back(audio_track_stats->id()); | |
491 report->AddStats(std::move(audio_track_stats)); | |
492 } | |
493 } | |
494 // Local Video Tracks | |
495 for (const rtc::scoped_refptr<VideoTrackInterface>& video_track : | |
496 stream->GetVideoTracks()) { | |
497 const std::vector<cricket::VideoSenderInfo*>* video_sender_infos = | |
498 track_media_info_map.GetVideoSenderInfos(*video_track); | |
499 if (!video_sender_infos) { | |
500 continue; | |
501 } | |
502 for (const auto& video_sender_info : *video_sender_infos) { | |
503 std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats = | |
504 ProduceMediaStreamTrackStatsFromVideoSenderInfo( | |
505 timestamp_us, *video_track, *video_sender_info); | |
506 stream_stats->track_ids->push_back(video_track_stats->id()); | |
507 report->AddStats(std::move(video_track_stats)); | |
508 } | |
509 } | |
510 } else { | |
511 // Remote Audio Tracks | |
512 for (const rtc::scoped_refptr<AudioTrackInterface>& audio_track : | |
513 stream->GetAudioTracks()) { | |
514 const cricket::VoiceReceiverInfo* voice_receiver_info = | |
515 track_media_info_map.GetVoiceReceiverInfo(*audio_track); | |
516 if (!voice_receiver_info) { | |
517 continue; | |
518 } | |
519 std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats = | |
520 ProduceMediaStreamTrackStatsFromVoiceReceiverInfo( | |
521 timestamp_us, *audio_track, *voice_receiver_info); | |
522 stream_stats->track_ids->push_back(audio_track_stats->id()); | |
523 report->AddStats(std::move(audio_track_stats)); | |
524 } | |
525 // Remote Video Tracks | |
526 for (const rtc::scoped_refptr<VideoTrackInterface>& video_track : | |
527 stream->GetVideoTracks()) { | |
528 const cricket::VideoReceiverInfo* video_receiver_info = | |
529 track_media_info_map.GetVideoReceiverInfo(*video_track); | |
530 if (!video_receiver_info) { | |
531 continue; | |
532 } | |
533 std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats = | |
534 ProduceMediaStreamTrackStatsFromVideoReceiverInfo( | |
535 timestamp_us, *video_track, *video_receiver_info); | |
536 stream_stats->track_ids->push_back(video_track_stats->id()); | |
537 report->AddStats(std::move(video_track_stats)); | |
538 } | |
539 } | |
540 report->AddStats(std::move(stream_stats)); | |
541 } | |
542 } | |
543 | |
544 } // namespace | |
545 | |
546 rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create( | |
547 PeerConnection* pc, int64_t cache_lifetime_us) { | |
548 return rtc::scoped_refptr<RTCStatsCollector>( | |
549 new rtc::RefCountedObject<RTCStatsCollector>(pc, cache_lifetime_us)); | |
550 } | |
551 | |
552 RTCStatsCollector::RTCStatsCollector(PeerConnection* pc, | |
553 int64_t cache_lifetime_us) | |
554 : pc_(pc), | |
555 signaling_thread_(pc->session()->signaling_thread()), | |
556 worker_thread_(pc->session()->worker_thread()), | |
557 network_thread_(pc->session()->network_thread()), | |
558 num_pending_partial_reports_(0), | |
559 partial_report_timestamp_us_(0), | |
560 cache_timestamp_us_(0), | |
561 cache_lifetime_us_(cache_lifetime_us) { | |
562 RTC_DCHECK(pc_); | |
563 RTC_DCHECK(signaling_thread_); | |
564 RTC_DCHECK(worker_thread_); | |
565 RTC_DCHECK(network_thread_); | |
566 RTC_DCHECK_GE(cache_lifetime_us_, 0); | |
567 pc_->SignalDataChannelCreated.connect( | |
568 this, &RTCStatsCollector::OnDataChannelCreated); | |
569 } | |
570 | |
571 RTCStatsCollector::~RTCStatsCollector() { | |
572 RTC_DCHECK_EQ(num_pending_partial_reports_, 0); | |
573 } | |
574 | |
575 void RTCStatsCollector::GetStatsReport( | |
576 rtc::scoped_refptr<RTCStatsCollectorCallback> callback) { | |
577 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
578 RTC_DCHECK(callback); | |
579 callbacks_.push_back(callback); | |
580 | |
581 // "Now" using a monotonically increasing timer. | |
582 int64_t cache_now_us = rtc::TimeMicros(); | |
583 if (cached_report_ && | |
584 cache_now_us - cache_timestamp_us_ <= cache_lifetime_us_) { | |
585 // We have a fresh cached report to deliver. | |
586 DeliverCachedReport(); | |
587 } else if (!num_pending_partial_reports_) { | |
588 // Only start gathering stats if we're not already gathering stats. In the | |
589 // case of already gathering stats, |callback_| will be invoked when there | |
590 // are no more pending partial reports. | |
591 | |
592 // "Now" using a system clock, relative to the UNIX epoch (Jan 1, 1970, | |
593 // UTC), in microseconds. The system clock could be modified and is not | |
594 // necessarily monotonically increasing. | |
595 int64_t timestamp_us = rtc::TimeUTCMicros(); | |
596 | |
597 num_pending_partial_reports_ = 2; | |
598 partial_report_timestamp_us_ = cache_now_us; | |
599 | |
600 // Prepare |channel_name_pairs_| for use in | |
601 // |ProducePartialResultsOnNetworkThread|. | |
602 channel_name_pairs_.reset(new ChannelNamePairs()); | |
603 if (pc_->session()->voice_channel()) { | |
604 channel_name_pairs_->voice = rtc::Optional<ChannelNamePair>( | |
605 ChannelNamePair(pc_->session()->voice_channel()->content_name(), | |
606 pc_->session()->voice_channel()->transport_name())); | |
607 } | |
608 if (pc_->session()->video_channel()) { | |
609 channel_name_pairs_->video = rtc::Optional<ChannelNamePair>( | |
610 ChannelNamePair(pc_->session()->video_channel()->content_name(), | |
611 pc_->session()->video_channel()->transport_name())); | |
612 } | |
613 if (pc_->session()->rtp_data_channel()) { | |
614 channel_name_pairs_->data = | |
615 rtc::Optional<ChannelNamePair>(ChannelNamePair( | |
616 pc_->session()->rtp_data_channel()->content_name(), | |
617 pc_->session()->rtp_data_channel()->transport_name())); | |
618 } | |
619 if (pc_->session()->sctp_content_name()) { | |
620 channel_name_pairs_->data = rtc::Optional<ChannelNamePair>( | |
621 ChannelNamePair(*pc_->session()->sctp_content_name(), | |
622 *pc_->session()->sctp_transport_name())); | |
623 } | |
624 // Prepare |track_media_info_map_| for use in | |
625 // |ProducePartialResultsOnNetworkThread| and | |
626 // |ProducePartialResultsOnSignalingThread|. | |
627 track_media_info_map_.reset(PrepareTrackMediaInfoMap_s().release()); | |
628 // Prepare |track_to_id_| for use in |ProducePartialResultsOnNetworkThread|. | |
629 // This avoids a possible deadlock if |MediaStreamTrackInterface::id| is | |
630 // implemented to invoke on the signaling thread. | |
631 track_to_id_ = PrepareTrackToID_s(); | |
632 | |
633 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, network_thread_, | |
634 rtc::Bind(&RTCStatsCollector::ProducePartialResultsOnNetworkThread, | |
635 rtc::scoped_refptr<RTCStatsCollector>(this), timestamp_us)); | |
636 ProducePartialResultsOnSignalingThread(timestamp_us); | |
637 } | |
638 } | |
639 | |
640 void RTCStatsCollector::ClearCachedStatsReport() { | |
641 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
642 cached_report_ = nullptr; | |
643 } | |
644 | |
645 void RTCStatsCollector::WaitForPendingRequest() { | |
646 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
647 if (num_pending_partial_reports_) { | |
648 rtc::Thread::Current()->ProcessMessages(0); | |
649 while (num_pending_partial_reports_) { | |
650 rtc::Thread::Current()->SleepMs(1); | |
651 rtc::Thread::Current()->ProcessMessages(0); | |
652 } | |
653 } | |
654 } | |
655 | |
656 void RTCStatsCollector::ProducePartialResultsOnSignalingThread( | |
657 int64_t timestamp_us) { | |
658 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
659 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create( | |
660 timestamp_us); | |
661 | |
662 ProduceDataChannelStats_s(timestamp_us, report.get()); | |
663 ProduceMediaStreamAndTrackStats_s(timestamp_us, report.get()); | |
664 ProducePeerConnectionStats_s(timestamp_us, report.get()); | |
665 | |
666 AddPartialResults(report); | |
667 } | |
668 | |
669 void RTCStatsCollector::ProducePartialResultsOnNetworkThread( | |
670 int64_t timestamp_us) { | |
671 RTC_DCHECK(network_thread_->IsCurrent()); | |
672 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create( | |
673 timestamp_us); | |
674 | |
675 std::unique_ptr<SessionStats> session_stats = | |
676 pc_->session()->GetStats(*channel_name_pairs_); | |
677 if (session_stats) { | |
678 std::map<std::string, CertificateStatsPair> transport_cert_stats = | |
679 PrepareTransportCertificateStats_n(*session_stats); | |
680 | |
681 ProduceCertificateStats_n( | |
682 timestamp_us, transport_cert_stats, report.get()); | |
683 ProduceCodecStats_n( | |
684 timestamp_us, *track_media_info_map_, report.get()); | |
685 ProduceIceCandidateAndPairStats_n( | |
686 timestamp_us, *session_stats, report.get()); | |
687 ProduceRTPStreamStats_n( | |
688 timestamp_us, *session_stats, *track_media_info_map_, report.get()); | |
689 ProduceTransportStats_n( | |
690 timestamp_us, *session_stats, transport_cert_stats, report.get()); | |
691 } | |
692 | |
693 AddPartialResults(report); | |
694 } | |
695 | |
696 void RTCStatsCollector::AddPartialResults( | |
697 const rtc::scoped_refptr<RTCStatsReport>& partial_report) { | |
698 if (!signaling_thread_->IsCurrent()) { | |
699 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_, | |
700 rtc::Bind(&RTCStatsCollector::AddPartialResults_s, | |
701 rtc::scoped_refptr<RTCStatsCollector>(this), | |
702 partial_report)); | |
703 return; | |
704 } | |
705 AddPartialResults_s(partial_report); | |
706 } | |
707 | |
708 void RTCStatsCollector::AddPartialResults_s( | |
709 rtc::scoped_refptr<RTCStatsReport> partial_report) { | |
710 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
711 RTC_DCHECK_GT(num_pending_partial_reports_, 0); | |
712 if (!partial_report_) | |
713 partial_report_ = partial_report; | |
714 else | |
715 partial_report_->TakeMembersFrom(partial_report); | |
716 --num_pending_partial_reports_; | |
717 if (!num_pending_partial_reports_) { | |
718 cache_timestamp_us_ = partial_report_timestamp_us_; | |
719 cached_report_ = partial_report_; | |
720 partial_report_ = nullptr; | |
721 channel_name_pairs_.reset(); | |
722 track_media_info_map_.reset(); | |
723 track_to_id_.clear(); | |
724 DeliverCachedReport(); | |
725 } | |
726 } | |
727 | |
728 void RTCStatsCollector::DeliverCachedReport() { | |
729 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
730 RTC_DCHECK(!callbacks_.empty()); | |
731 RTC_DCHECK(cached_report_); | |
732 for (const rtc::scoped_refptr<RTCStatsCollectorCallback>& callback : | |
733 callbacks_) { | |
734 callback->OnStatsDelivered(cached_report_); | |
735 } | |
736 callbacks_.clear(); | |
737 } | |
738 | |
739 void RTCStatsCollector::ProduceCertificateStats_n( | |
740 int64_t timestamp_us, | |
741 const std::map<std::string, CertificateStatsPair>& transport_cert_stats, | |
742 RTCStatsReport* report) const { | |
743 RTC_DCHECK(network_thread_->IsCurrent()); | |
744 for (const auto& transport_cert_stats_pair : transport_cert_stats) { | |
745 if (transport_cert_stats_pair.second.local) { | |
746 ProduceCertificateStatsFromSSLCertificateStats( | |
747 timestamp_us, *transport_cert_stats_pair.second.local.get(), report); | |
748 } | |
749 if (transport_cert_stats_pair.second.remote) { | |
750 ProduceCertificateStatsFromSSLCertificateStats( | |
751 timestamp_us, *transport_cert_stats_pair.second.remote.get(), report); | |
752 } | |
753 } | |
754 } | |
755 | |
756 void RTCStatsCollector::ProduceCodecStats_n( | |
757 int64_t timestamp_us, const TrackMediaInfoMap& track_media_info_map, | |
758 RTCStatsReport* report) const { | |
759 RTC_DCHECK(network_thread_->IsCurrent()); | |
760 // Audio | |
761 if (track_media_info_map.voice_media_info()) { | |
762 // Inbound | |
763 for (const auto& pair : | |
764 track_media_info_map.voice_media_info()->receive_codecs) { | |
765 report->AddStats(CodecStatsFromRtpCodecParameters( | |
766 timestamp_us, true, true, pair.second)); | |
767 } | |
768 // Outbound | |
769 for (const auto& pair : | |
770 track_media_info_map.voice_media_info()->send_codecs) { | |
771 report->AddStats(CodecStatsFromRtpCodecParameters( | |
772 timestamp_us, false, true, pair.second)); | |
773 } | |
774 } | |
775 // Video | |
776 if (track_media_info_map.video_media_info()) { | |
777 // Inbound | |
778 for (const auto& pair : | |
779 track_media_info_map.video_media_info()->receive_codecs) { | |
780 report->AddStats(CodecStatsFromRtpCodecParameters( | |
781 timestamp_us, true, false, pair.second)); | |
782 } | |
783 // Outbound | |
784 for (const auto& pair : | |
785 track_media_info_map.video_media_info()->send_codecs) { | |
786 report->AddStats(CodecStatsFromRtpCodecParameters( | |
787 timestamp_us, false, false, pair.second)); | |
788 } | |
789 } | |
790 } | |
791 | |
792 void RTCStatsCollector::ProduceDataChannelStats_s( | |
793 int64_t timestamp_us, RTCStatsReport* report) const { | |
794 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
795 for (const rtc::scoped_refptr<DataChannel>& data_channel : | |
796 pc_->sctp_data_channels()) { | |
797 std::unique_ptr<RTCDataChannelStats> data_channel_stats( | |
798 new RTCDataChannelStats( | |
799 "RTCDataChannel_" + rtc::ToString<>(data_channel->id()), | |
800 timestamp_us)); | |
801 data_channel_stats->label = data_channel->label(); | |
802 data_channel_stats->protocol = data_channel->protocol(); | |
803 data_channel_stats->datachannelid = data_channel->id(); | |
804 data_channel_stats->state = | |
805 DataStateToRTCDataChannelState(data_channel->state()); | |
806 data_channel_stats->messages_sent = data_channel->messages_sent(); | |
807 data_channel_stats->bytes_sent = data_channel->bytes_sent(); | |
808 data_channel_stats->messages_received = data_channel->messages_received(); | |
809 data_channel_stats->bytes_received = data_channel->bytes_received(); | |
810 report->AddStats(std::move(data_channel_stats)); | |
811 } | |
812 } | |
813 | |
814 void RTCStatsCollector::ProduceIceCandidateAndPairStats_n( | |
815 int64_t timestamp_us, const SessionStats& session_stats, | |
816 RTCStatsReport* report) const { | |
817 RTC_DCHECK(network_thread_->IsCurrent()); | |
818 for (const auto& transport_stats : session_stats.transport_stats) { | |
819 for (const auto& channel_stats : transport_stats.second.channel_stats) { | |
820 std::string transport_id = RTCTransportStatsIDFromTransportChannel( | |
821 transport_stats.second.transport_name, channel_stats.component); | |
822 for (const cricket::ConnectionInfo& info : | |
823 channel_stats.connection_infos) { | |
824 std::unique_ptr<RTCIceCandidatePairStats> candidate_pair_stats( | |
825 new RTCIceCandidatePairStats( | |
826 RTCIceCandidatePairStatsIDFromConnectionInfo(info), | |
827 timestamp_us)); | |
828 | |
829 candidate_pair_stats->transport_id = transport_id; | |
830 // TODO(hbos): There could be other candidates that are not paired with | |
831 // anything. We don't have a complete list. Local candidates come from | |
832 // Port objects, and prflx candidates (both local and remote) are only | |
833 // stored in candidate pairs. crbug.com/632723 | |
834 candidate_pair_stats->local_candidate_id = ProduceIceCandidateStats( | |
835 timestamp_us, info.local_candidate, true, transport_id, report); | |
836 candidate_pair_stats->remote_candidate_id = ProduceIceCandidateStats( | |
837 timestamp_us, info.remote_candidate, false, transport_id, report); | |
838 candidate_pair_stats->state = | |
839 IceCandidatePairStateToRTCStatsIceCandidatePairState(info.state); | |
840 candidate_pair_stats->priority = info.priority; | |
841 // TODO(hbos): This writable is different than the spec. It goes to | |
842 // false after a certain amount of time without a response passes. | |
843 // crbug.com/633550 | |
844 candidate_pair_stats->writable = info.writable; | |
845 candidate_pair_stats->bytes_sent = | |
846 static_cast<uint64_t>(info.sent_total_bytes); | |
847 candidate_pair_stats->bytes_received = | |
848 static_cast<uint64_t>(info.recv_total_bytes); | |
849 // TODO(hbos): The |info.rtt| measurement is smoothed. It shouldn't be | |
850 // smoothed according to the spec. crbug.com/633550. See | |
851 // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-curr
entrtt | |
852 candidate_pair_stats->current_round_trip_time = | |
853 static_cast<double>(info.rtt) / rtc::kNumMillisecsPerSec; | |
854 candidate_pair_stats->requests_received = | |
855 static_cast<uint64_t>(info.recv_ping_requests); | |
856 candidate_pair_stats->requests_sent = static_cast<uint64_t>( | |
857 info.sent_ping_requests_before_first_response); | |
858 candidate_pair_stats->responses_received = | |
859 static_cast<uint64_t>(info.recv_ping_responses); | |
860 candidate_pair_stats->responses_sent = | |
861 static_cast<uint64_t>(info.sent_ping_responses); | |
862 RTC_DCHECK_GE(info.sent_ping_requests_total, | |
863 info.sent_ping_requests_before_first_response); | |
864 candidate_pair_stats->consent_requests_sent = static_cast<uint64_t>( | |
865 info.sent_ping_requests_total - | |
866 info.sent_ping_requests_before_first_response); | |
867 | |
868 report->AddStats(std::move(candidate_pair_stats)); | |
869 } | |
870 } | |
871 } | |
872 } | |
873 | |
874 void RTCStatsCollector::ProduceMediaStreamAndTrackStats_s( | |
875 int64_t timestamp_us, RTCStatsReport* report) const { | |
876 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
877 RTC_DCHECK(track_media_info_map_); | |
878 ProduceMediaStreamAndTrackStats(timestamp_us, | |
879 *track_media_info_map_, | |
880 pc_->local_streams(), | |
881 true, | |
882 report); | |
883 ProduceMediaStreamAndTrackStats(timestamp_us, | |
884 *track_media_info_map_, | |
885 pc_->remote_streams(), | |
886 false, | |
887 report); | |
888 } | |
889 | |
890 void RTCStatsCollector::ProducePeerConnectionStats_s( | |
891 int64_t timestamp_us, RTCStatsReport* report) const { | |
892 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
893 std::unique_ptr<RTCPeerConnectionStats> stats( | |
894 new RTCPeerConnectionStats("RTCPeerConnection", timestamp_us)); | |
895 stats->data_channels_opened = internal_record_.data_channels_opened; | |
896 stats->data_channels_closed = internal_record_.data_channels_closed; | |
897 report->AddStats(std::move(stats)); | |
898 } | |
899 | |
900 void RTCStatsCollector::ProduceRTPStreamStats_n( | |
901 int64_t timestamp_us, const SessionStats& session_stats, | |
902 const TrackMediaInfoMap& track_media_info_map, | |
903 RTCStatsReport* report) const { | |
904 RTC_DCHECK(network_thread_->IsCurrent()); | |
905 | |
906 // Audio | |
907 if (track_media_info_map.voice_media_info()) { | |
908 std::string transport_id = RTCTransportStatsIDFromBaseChannel( | |
909 session_stats.proxy_to_transport, *pc_->session()->voice_channel()); | |
910 RTC_DCHECK(!transport_id.empty()); | |
911 // Inbound | |
912 for (const cricket::VoiceReceiverInfo& voice_receiver_info : | |
913 track_media_info_map.voice_media_info()->receivers) { | |
914 // TODO(nisse): SSRC == 0 currently means none. Delete check when that | |
915 // is fixed. | |
916 if (voice_receiver_info.ssrc() == 0) | |
917 continue; | |
918 std::unique_ptr<RTCInboundRTPStreamStats> inbound_audio( | |
919 new RTCInboundRTPStreamStats( | |
920 RTCInboundRTPStreamStatsIDFromSSRC( | |
921 true, voice_receiver_info.ssrc()), | |
922 timestamp_us)); | |
923 SetInboundRTPStreamStatsFromVoiceReceiverInfo( | |
924 voice_receiver_info, inbound_audio.get()); | |
925 rtc::scoped_refptr<AudioTrackInterface> audio_track = | |
926 track_media_info_map_->GetAudioTrack(voice_receiver_info); | |
927 if (audio_track) { | |
928 RTC_DCHECK(track_to_id_.find(audio_track.get()) != track_to_id_.end()); | |
929 inbound_audio->media_track_id = | |
930 RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc( | |
931 false, | |
932 MediaStreamTrackInterface::kAudioKind, | |
933 track_to_id_.find(audio_track.get())->second, | |
934 voice_receiver_info.ssrc()); | |
935 } | |
936 inbound_audio->transport_id = transport_id; | |
937 if (voice_receiver_info.codec_payload_type) { | |
938 inbound_audio->codec_id = | |
939 RTCCodecStatsIDFromDirectionMediaAndPayload( | |
940 true, true, *voice_receiver_info.codec_payload_type); | |
941 } | |
942 report->AddStats(std::move(inbound_audio)); | |
943 } | |
944 // Outbound | |
945 for (const cricket::VoiceSenderInfo& voice_sender_info : | |
946 track_media_info_map.voice_media_info()->senders) { | |
947 // TODO(nisse): SSRC == 0 currently means none. Delete check when that | |
948 // is fixed. | |
949 if (voice_sender_info.ssrc() == 0) | |
950 continue; | |
951 std::unique_ptr<RTCOutboundRTPStreamStats> outbound_audio( | |
952 new RTCOutboundRTPStreamStats( | |
953 RTCOutboundRTPStreamStatsIDFromSSRC( | |
954 true, voice_sender_info.ssrc()), | |
955 timestamp_us)); | |
956 SetOutboundRTPStreamStatsFromVoiceSenderInfo( | |
957 voice_sender_info, outbound_audio.get()); | |
958 rtc::scoped_refptr<AudioTrackInterface> audio_track = | |
959 track_media_info_map_->GetAudioTrack(voice_sender_info); | |
960 if (audio_track) { | |
961 RTC_DCHECK(track_to_id_.find(audio_track.get()) != track_to_id_.end()); | |
962 outbound_audio->media_track_id = | |
963 RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc( | |
964 true, | |
965 MediaStreamTrackInterface::kAudioKind, | |
966 track_to_id_.find(audio_track.get())->second, | |
967 voice_sender_info.ssrc()); | |
968 } | |
969 outbound_audio->transport_id = transport_id; | |
970 if (voice_sender_info.codec_payload_type) { | |
971 outbound_audio->codec_id = | |
972 RTCCodecStatsIDFromDirectionMediaAndPayload( | |
973 false, true, *voice_sender_info.codec_payload_type); | |
974 } | |
975 report->AddStats(std::move(outbound_audio)); | |
976 } | |
977 } | |
978 // Video | |
979 if (track_media_info_map.video_media_info()) { | |
980 std::string transport_id = RTCTransportStatsIDFromBaseChannel( | |
981 session_stats.proxy_to_transport, *pc_->session()->video_channel()); | |
982 RTC_DCHECK(!transport_id.empty()); | |
983 // Inbound | |
984 for (const cricket::VideoReceiverInfo& video_receiver_info : | |
985 track_media_info_map.video_media_info()->receivers) { | |
986 // TODO(nisse): SSRC == 0 currently means none. Delete check when that | |
987 // is fixed. | |
988 if (video_receiver_info.ssrc() == 0) | |
989 continue; | |
990 std::unique_ptr<RTCInboundRTPStreamStats> inbound_video( | |
991 new RTCInboundRTPStreamStats( | |
992 RTCInboundRTPStreamStatsIDFromSSRC( | |
993 false, video_receiver_info.ssrc()), | |
994 timestamp_us)); | |
995 SetInboundRTPStreamStatsFromVideoReceiverInfo( | |
996 video_receiver_info, inbound_video.get()); | |
997 rtc::scoped_refptr<VideoTrackInterface> video_track = | |
998 track_media_info_map_->GetVideoTrack(video_receiver_info); | |
999 if (video_track) { | |
1000 RTC_DCHECK(track_to_id_.find(video_track.get()) != track_to_id_.end()); | |
1001 inbound_video->media_track_id = | |
1002 RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc( | |
1003 false, | |
1004 MediaStreamTrackInterface::kVideoKind, | |
1005 track_to_id_.find(video_track.get())->second, | |
1006 video_receiver_info.ssrc()); | |
1007 } | |
1008 inbound_video->transport_id = transport_id; | |
1009 if (video_receiver_info.codec_payload_type) { | |
1010 inbound_video->codec_id = | |
1011 RTCCodecStatsIDFromDirectionMediaAndPayload( | |
1012 true, false, *video_receiver_info.codec_payload_type); | |
1013 } | |
1014 report->AddStats(std::move(inbound_video)); | |
1015 } | |
1016 // Outbound | |
1017 for (const cricket::VideoSenderInfo& video_sender_info : | |
1018 track_media_info_map.video_media_info()->senders) { | |
1019 // TODO(nisse): SSRC == 0 currently means none. Delete check when that | |
1020 // is fixed. | |
1021 if (video_sender_info.ssrc() == 0) | |
1022 continue; | |
1023 std::unique_ptr<RTCOutboundRTPStreamStats> outbound_video( | |
1024 new RTCOutboundRTPStreamStats( | |
1025 RTCOutboundRTPStreamStatsIDFromSSRC( | |
1026 false, video_sender_info.ssrc()), | |
1027 timestamp_us)); | |
1028 SetOutboundRTPStreamStatsFromVideoSenderInfo( | |
1029 video_sender_info, outbound_video.get()); | |
1030 rtc::scoped_refptr<VideoTrackInterface> video_track = | |
1031 track_media_info_map_->GetVideoTrack(video_sender_info); | |
1032 if (video_track) { | |
1033 RTC_DCHECK(track_to_id_.find(video_track.get()) != track_to_id_.end()); | |
1034 outbound_video->media_track_id = | |
1035 RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc( | |
1036 true, | |
1037 MediaStreamTrackInterface::kVideoKind, | |
1038 track_to_id_.find(video_track.get())->second, | |
1039 video_sender_info.ssrc()); | |
1040 } | |
1041 outbound_video->transport_id = transport_id; | |
1042 if (video_sender_info.codec_payload_type) { | |
1043 outbound_video->codec_id = | |
1044 RTCCodecStatsIDFromDirectionMediaAndPayload( | |
1045 false, false, *video_sender_info.codec_payload_type); | |
1046 } | |
1047 report->AddStats(std::move(outbound_video)); | |
1048 } | |
1049 } | |
1050 } | |
1051 | |
1052 void RTCStatsCollector::ProduceTransportStats_n( | |
1053 int64_t timestamp_us, const SessionStats& session_stats, | |
1054 const std::map<std::string, CertificateStatsPair>& transport_cert_stats, | |
1055 RTCStatsReport* report) const { | |
1056 RTC_DCHECK(network_thread_->IsCurrent()); | |
1057 for (const auto& transport : session_stats.transport_stats) { | |
1058 // Get reference to RTCP channel, if it exists. | |
1059 std::string rtcp_transport_stats_id; | |
1060 for (const auto& channel_stats : transport.second.channel_stats) { | |
1061 if (channel_stats.component == | |
1062 cricket::ICE_CANDIDATE_COMPONENT_RTCP) { | |
1063 rtcp_transport_stats_id = RTCTransportStatsIDFromTransportChannel( | |
1064 transport.second.transport_name, channel_stats.component); | |
1065 break; | |
1066 } | |
1067 } | |
1068 | |
1069 // Get reference to local and remote certificates of this transport, if they | |
1070 // exist. | |
1071 const auto& certificate_stats_it = transport_cert_stats.find( | |
1072 transport.second.transport_name); | |
1073 RTC_DCHECK(certificate_stats_it != transport_cert_stats.cend()); | |
1074 std::string local_certificate_id; | |
1075 if (certificate_stats_it->second.local) { | |
1076 local_certificate_id = RTCCertificateIDFromFingerprint( | |
1077 certificate_stats_it->second.local->fingerprint); | |
1078 } | |
1079 std::string remote_certificate_id; | |
1080 if (certificate_stats_it->second.remote) { | |
1081 remote_certificate_id = RTCCertificateIDFromFingerprint( | |
1082 certificate_stats_it->second.remote->fingerprint); | |
1083 } | |
1084 | |
1085 // There is one transport stats for each channel. | |
1086 for (const auto& channel_stats : transport.second.channel_stats) { | |
1087 std::unique_ptr<RTCTransportStats> transport_stats( | |
1088 new RTCTransportStats( | |
1089 RTCTransportStatsIDFromTransportChannel( | |
1090 transport.second.transport_name, channel_stats.component), | |
1091 timestamp_us)); | |
1092 transport_stats->bytes_sent = 0; | |
1093 transport_stats->bytes_received = 0; | |
1094 transport_stats->dtls_state = DtlsTransportStateToRTCDtlsTransportState( | |
1095 channel_stats.dtls_state); | |
1096 for (const cricket::ConnectionInfo& info : | |
1097 channel_stats.connection_infos) { | |
1098 *transport_stats->bytes_sent += info.sent_total_bytes; | |
1099 *transport_stats->bytes_received += info.recv_total_bytes; | |
1100 if (info.best_connection) { | |
1101 transport_stats->selected_candidate_pair_id = | |
1102 RTCIceCandidatePairStatsIDFromConnectionInfo(info); | |
1103 } | |
1104 } | |
1105 if (channel_stats.component != cricket::ICE_CANDIDATE_COMPONENT_RTCP && | |
1106 !rtcp_transport_stats_id.empty()) { | |
1107 transport_stats->rtcp_transport_stats_id = rtcp_transport_stats_id; | |
1108 } | |
1109 if (!local_certificate_id.empty()) | |
1110 transport_stats->local_certificate_id = local_certificate_id; | |
1111 if (!remote_certificate_id.empty()) | |
1112 transport_stats->remote_certificate_id = remote_certificate_id; | |
1113 report->AddStats(std::move(transport_stats)); | |
1114 } | |
1115 } | |
1116 } | |
1117 | |
1118 std::map<std::string, RTCStatsCollector::CertificateStatsPair> | |
1119 RTCStatsCollector::PrepareTransportCertificateStats_n( | |
1120 const SessionStats& session_stats) const { | |
1121 RTC_DCHECK(network_thread_->IsCurrent()); | |
1122 std::map<std::string, CertificateStatsPair> transport_cert_stats; | |
1123 for (const auto& transport_stats : session_stats.transport_stats) { | |
1124 CertificateStatsPair certificate_stats_pair; | |
1125 rtc::scoped_refptr<rtc::RTCCertificate> local_certificate; | |
1126 if (pc_->session()->GetLocalCertificate( | |
1127 transport_stats.second.transport_name, &local_certificate)) { | |
1128 certificate_stats_pair.local = | |
1129 local_certificate->ssl_certificate().GetStats(); | |
1130 } | |
1131 std::unique_ptr<rtc::SSLCertificate> remote_certificate = | |
1132 pc_->session()->GetRemoteSSLCertificate( | |
1133 transport_stats.second.transport_name); | |
1134 if (remote_certificate) { | |
1135 certificate_stats_pair.remote = remote_certificate->GetStats(); | |
1136 } | |
1137 transport_cert_stats.insert( | |
1138 std::make_pair(transport_stats.second.transport_name, | |
1139 std::move(certificate_stats_pair))); | |
1140 } | |
1141 return transport_cert_stats; | |
1142 } | |
1143 | |
1144 std::unique_ptr<TrackMediaInfoMap> | |
1145 RTCStatsCollector::PrepareTrackMediaInfoMap_s() const { | |
1146 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
1147 std::unique_ptr<cricket::VoiceMediaInfo> voice_media_info; | |
1148 if (pc_->session()->voice_channel()) { | |
1149 voice_media_info.reset(new cricket::VoiceMediaInfo()); | |
1150 if (!pc_->session()->voice_channel()->GetStats(voice_media_info.get())) { | |
1151 voice_media_info.reset(); | |
1152 } | |
1153 } | |
1154 std::unique_ptr<cricket::VideoMediaInfo> video_media_info; | |
1155 if (pc_->session()->video_channel()) { | |
1156 video_media_info.reset(new cricket::VideoMediaInfo()); | |
1157 if (!pc_->session()->video_channel()->GetStats(video_media_info.get())) { | |
1158 video_media_info.reset(); | |
1159 } | |
1160 } | |
1161 std::unique_ptr<TrackMediaInfoMap> track_media_info_map( | |
1162 new TrackMediaInfoMap(std::move(voice_media_info), | |
1163 std::move(video_media_info), | |
1164 pc_->GetSenders(), | |
1165 pc_->GetReceivers())); | |
1166 return track_media_info_map; | |
1167 } | |
1168 | |
1169 std::map<MediaStreamTrackInterface*, std::string> | |
1170 RTCStatsCollector::PrepareTrackToID_s() const { | |
1171 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
1172 std::map<MediaStreamTrackInterface*, std::string> track_to_id; | |
1173 StreamCollectionInterface* local_and_remote_streams[] = | |
1174 { pc_->local_streams().get(), pc_->remote_streams().get() }; | |
1175 for (auto& streams : local_and_remote_streams) { | |
1176 if (streams) { | |
1177 for (size_t i = 0; i < streams->count(); ++i) { | |
1178 MediaStreamInterface* stream = streams->at(i); | |
1179 for (const rtc::scoped_refptr<AudioTrackInterface>& audio_track : | |
1180 stream->GetAudioTracks()) { | |
1181 track_to_id[audio_track.get()] = audio_track->id(); | |
1182 } | |
1183 for (const rtc::scoped_refptr<VideoTrackInterface>& video_track : | |
1184 stream->GetVideoTracks()) { | |
1185 track_to_id[video_track.get()] = video_track->id(); | |
1186 } | |
1187 } | |
1188 } | |
1189 } | |
1190 return track_to_id; | |
1191 } | |
1192 | |
1193 void RTCStatsCollector::OnDataChannelCreated(DataChannel* channel) { | |
1194 channel->SignalOpened.connect(this, &RTCStatsCollector::OnDataChannelOpened); | |
1195 channel->SignalClosed.connect(this, &RTCStatsCollector::OnDataChannelClosed); | |
1196 } | |
1197 | |
1198 void RTCStatsCollector::OnDataChannelOpened(DataChannel* channel) { | |
1199 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
1200 bool result = internal_record_.opened_data_channels.insert( | |
1201 reinterpret_cast<uintptr_t>(channel)).second; | |
1202 ++internal_record_.data_channels_opened; | |
1203 RTC_DCHECK(result); | |
1204 } | |
1205 | |
1206 void RTCStatsCollector::OnDataChannelClosed(DataChannel* channel) { | |
1207 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
1208 // Only channels that have been fully opened (and have increased the | |
1209 // |data_channels_opened_| counter) increase the closed counter. | |
1210 if (internal_record_.opened_data_channels.find( | |
1211 reinterpret_cast<uintptr_t>(channel)) != | |
1212 internal_record_.opened_data_channels.end()) { | |
1213 ++internal_record_.data_channels_closed; | |
1214 } | |
1215 } | |
1216 | |
1217 const char* CandidateTypeToRTCIceCandidateTypeForTesting( | |
1218 const std::string& type) { | |
1219 return CandidateTypeToRTCIceCandidateType(type); | |
1220 } | |
1221 | |
1222 const char* DataStateToRTCDataChannelStateForTesting( | |
1223 DataChannelInterface::DataState state) { | |
1224 return DataStateToRTCDataChannelState(state); | |
1225 } | |
1226 | |
1227 } // namespace webrtc | |
OLD | NEW |