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 "pc/rtc_stats_collector.h"
12
13 #include <stdint.h>
14 #include <stdio.h>
15
16 #include <cstdint>
17 #include <map>
18 #include <memory>
19 #include <string>
20 #include <type_traits>
21 #include <utility>
22 #include <vector>
23
24 #include "absl/functional/bind_front.h"
25 #include "absl/strings/string_view.h"
26 #include "api/array_view.h"
27 #include "api/candidate.h"
28 #include "api/dtls_transport_interface.h"
29 #include "api/media_stream_interface.h"
30 #include "api/rtp_parameters.h"
31 #include "api/sequence_checker.h"
32 #include "api/stats/rtc_stats.h"
33 #include "api/stats/rtcstats_objects.h"
34 #include "api/units/time_delta.h"
35 #include "api/video/video_content_type.h"
36 #include "common_video/include/quality_limitation_reason.h"
37 #include "media/base/media_channel.h"
38 #include "modules/audio_processing/include/audio_processing_statistics.h"
39 #include "modules/rtp_rtcp/include/report_block_data.h"
40 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
41 #include "p2p/base/connection_info.h"
42 #include "p2p/base/ice_transport_internal.h"
43 #include "p2p/base/p2p_constants.h"
44 #include "p2p/base/port.h"
45 #include "pc/channel_interface.h"
46 #include "pc/data_channel_utils.h"
47 #include "pc/rtc_stats_traversal.h"
48 #include "pc/rtp_receiver_proxy.h"
49 #include "pc/rtp_sender_proxy.h"
50 #include "pc/webrtc_sdp.h"
51 #include "rtc_base/checks.h"
52 #include "rtc_base/ip_address.h"
53 #include "rtc_base/logging.h"
54 #include "rtc_base/network_constants.h"
55 #include "rtc_base/rtc_certificate.h"
56 #include "rtc_base/socket_address.h"
57 #include "rtc_base/ssl_stream_adapter.h"
58 #include "rtc_base/string_encode.h"
59 #include "rtc_base/strings/string_builder.h"
60 #include "rtc_base/time_utils.h"
61 #include "rtc_base/trace_event.h"
62
63 namespace webrtc {
64
65 namespace {
66
67 const char kDirectionInbound = 'I';
68 const char kDirectionOutbound = 'O';
69
70 // TODO(https://crbug.com/webrtc/10656): Consider making IDs less predictable.
RTCCertificateIDFromFingerprint(const std::string & fingerprint)71 std::string RTCCertificateIDFromFingerprint(const std::string& fingerprint) {
72 return "CF" + fingerprint;
73 }
74
75 // `direction` is either kDirectionInbound or kDirectionOutbound.
RTCCodecStatsIDFromTransportAndCodecParameters(const char direction,const std::string & transport_id,const RtpCodecParameters & codec_params)76 std::string RTCCodecStatsIDFromTransportAndCodecParameters(
77 const char direction,
78 const std::string& transport_id,
79 const RtpCodecParameters& codec_params) {
80 char buf[1024];
81 rtc::SimpleStringBuilder sb(buf);
82 sb << 'C' << direction << transport_id << '_' << codec_params.payload_type;
83 // TODO(https://crbug.com/webrtc/14420): If we stop supporting different FMTP
84 // lines for the same PT and transport, which should be illegal SDP, then we
85 // wouldn't need `fmtp` to be part of the ID here.
86 rtc::StringBuilder fmtp;
87 if (WriteFmtpParameters(codec_params.parameters, &fmtp)) {
88 sb << '_' << fmtp.Release();
89 }
90 return sb.str();
91 }
92
RTCIceCandidatePairStatsIDFromConnectionInfo(const cricket::ConnectionInfo & info)93 std::string RTCIceCandidatePairStatsIDFromConnectionInfo(
94 const cricket::ConnectionInfo& info) {
95 char buf[4096];
96 rtc::SimpleStringBuilder sb(buf);
97 sb << "CP" << info.local_candidate.id() << "_" << info.remote_candidate.id();
98 return sb.str();
99 }
100
101 // `direction` is either kDirectionInbound or kDirectionOutbound.
DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(const char direction,int attachment_id)102 std::string DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
103 const char direction,
104 int attachment_id) {
105 char buf[1024];
106 rtc::SimpleStringBuilder sb(buf);
107 sb << "DEPRECATED_T" << direction << attachment_id;
108 return sb.str();
109 }
110
RTCTransportStatsIDFromTransportChannel(const std::string & transport_name,int channel_component)111 std::string RTCTransportStatsIDFromTransportChannel(
112 const std::string& transport_name,
113 int channel_component) {
114 char buf[1024];
115 rtc::SimpleStringBuilder sb(buf);
116 sb << 'T' << transport_name << channel_component;
117 return sb.str();
118 }
119
RTCInboundRTPStreamStatsIDFromSSRC(const std::string & transport_id,cricket::MediaType media_type,uint32_t ssrc)120 std::string RTCInboundRTPStreamStatsIDFromSSRC(const std::string& transport_id,
121 cricket::MediaType media_type,
122 uint32_t ssrc) {
123 char buf[1024];
124 rtc::SimpleStringBuilder sb(buf);
125 sb << 'I' << transport_id
126 << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V') << ssrc;
127 return sb.str();
128 }
129
RTCOutboundRTPStreamStatsIDFromSSRC(const std::string & transport_id,cricket::MediaType media_type,uint32_t ssrc)130 std::string RTCOutboundRTPStreamStatsIDFromSSRC(const std::string& transport_id,
131 cricket::MediaType media_type,
132 uint32_t ssrc) {
133 char buf[1024];
134 rtc::SimpleStringBuilder sb(buf);
135 sb << 'O' << transport_id
136 << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V') << ssrc;
137 return sb.str();
138 }
139
RTCRemoteInboundRtpStreamStatsIdFromSourceSsrc(cricket::MediaType media_type,uint32_t source_ssrc)140 std::string RTCRemoteInboundRtpStreamStatsIdFromSourceSsrc(
141 cricket::MediaType media_type,
142 uint32_t source_ssrc) {
143 char buf[1024];
144 rtc::SimpleStringBuilder sb(buf);
145 sb << "RI" << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V')
146 << source_ssrc;
147 return sb.str();
148 }
149
RTCRemoteOutboundRTPStreamStatsIDFromSSRC(cricket::MediaType media_type,uint32_t source_ssrc)150 std::string RTCRemoteOutboundRTPStreamStatsIDFromSSRC(
151 cricket::MediaType media_type,
152 uint32_t source_ssrc) {
153 char buf[1024];
154 rtc::SimpleStringBuilder sb(buf);
155 sb << "RO" << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V')
156 << source_ssrc;
157 return sb.str();
158 }
159
RTCMediaSourceStatsIDFromKindAndAttachment(cricket::MediaType media_type,int attachment_id)160 std::string RTCMediaSourceStatsIDFromKindAndAttachment(
161 cricket::MediaType media_type,
162 int attachment_id) {
163 char buf[1024];
164 rtc::SimpleStringBuilder sb(buf);
165 sb << 'S' << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V')
166 << attachment_id;
167 return sb.str();
168 }
169
CandidateTypeToRTCIceCandidateType(const std::string & type)170 const char* CandidateTypeToRTCIceCandidateType(const std::string& type) {
171 if (type == cricket::LOCAL_PORT_TYPE)
172 return RTCIceCandidateType::kHost;
173 if (type == cricket::STUN_PORT_TYPE)
174 return RTCIceCandidateType::kSrflx;
175 if (type == cricket::PRFLX_PORT_TYPE)
176 return RTCIceCandidateType::kPrflx;
177 if (type == cricket::RELAY_PORT_TYPE)
178 return RTCIceCandidateType::kRelay;
179 RTC_DCHECK_NOTREACHED();
180 return nullptr;
181 }
182
DataStateToRTCDataChannelState(DataChannelInterface::DataState state)183 const char* DataStateToRTCDataChannelState(
184 DataChannelInterface::DataState state) {
185 switch (state) {
186 case DataChannelInterface::kConnecting:
187 return RTCDataChannelState::kConnecting;
188 case DataChannelInterface::kOpen:
189 return RTCDataChannelState::kOpen;
190 case DataChannelInterface::kClosing:
191 return RTCDataChannelState::kClosing;
192 case DataChannelInterface::kClosed:
193 return RTCDataChannelState::kClosed;
194 default:
195 RTC_DCHECK_NOTREACHED();
196 return nullptr;
197 }
198 }
199
IceCandidatePairStateToRTCStatsIceCandidatePairState(cricket::IceCandidatePairState state)200 const char* IceCandidatePairStateToRTCStatsIceCandidatePairState(
201 cricket::IceCandidatePairState state) {
202 switch (state) {
203 case cricket::IceCandidatePairState::WAITING:
204 return RTCStatsIceCandidatePairState::kWaiting;
205 case cricket::IceCandidatePairState::IN_PROGRESS:
206 return RTCStatsIceCandidatePairState::kInProgress;
207 case cricket::IceCandidatePairState::SUCCEEDED:
208 return RTCStatsIceCandidatePairState::kSucceeded;
209 case cricket::IceCandidatePairState::FAILED:
210 return RTCStatsIceCandidatePairState::kFailed;
211 default:
212 RTC_DCHECK_NOTREACHED();
213 return nullptr;
214 }
215 }
216
IceRoleToRTCIceRole(cricket::IceRole role)217 const char* IceRoleToRTCIceRole(cricket::IceRole role) {
218 switch (role) {
219 case cricket::IceRole::ICEROLE_UNKNOWN:
220 return RTCIceRole::kUnknown;
221 case cricket::IceRole::ICEROLE_CONTROLLED:
222 return RTCIceRole::kControlled;
223 case cricket::IceRole::ICEROLE_CONTROLLING:
224 return RTCIceRole::kControlling;
225 default:
226 RTC_DCHECK_NOTREACHED();
227 return nullptr;
228 }
229 }
230
DtlsTransportStateToRTCDtlsTransportState(DtlsTransportState state)231 const char* DtlsTransportStateToRTCDtlsTransportState(
232 DtlsTransportState state) {
233 switch (state) {
234 case DtlsTransportState::kNew:
235 return RTCDtlsTransportState::kNew;
236 case DtlsTransportState::kConnecting:
237 return RTCDtlsTransportState::kConnecting;
238 case DtlsTransportState::kConnected:
239 return RTCDtlsTransportState::kConnected;
240 case DtlsTransportState::kClosed:
241 return RTCDtlsTransportState::kClosed;
242 case DtlsTransportState::kFailed:
243 return RTCDtlsTransportState::kFailed;
244 default:
245 RTC_CHECK_NOTREACHED();
246 return nullptr;
247 }
248 }
249
IceTransportStateToRTCIceTransportState(IceTransportState state)250 const char* IceTransportStateToRTCIceTransportState(IceTransportState state) {
251 switch (state) {
252 case IceTransportState::kNew:
253 return RTCIceTransportState::kNew;
254 case IceTransportState::kChecking:
255 return RTCIceTransportState::kChecking;
256 case IceTransportState::kConnected:
257 return RTCIceTransportState::kConnected;
258 case IceTransportState::kCompleted:
259 return RTCIceTransportState::kCompleted;
260 case IceTransportState::kFailed:
261 return RTCIceTransportState::kFailed;
262 case IceTransportState::kDisconnected:
263 return RTCIceTransportState::kDisconnected;
264 case IceTransportState::kClosed:
265 return RTCIceTransportState::kClosed;
266 default:
267 RTC_CHECK_NOTREACHED();
268 return nullptr;
269 }
270 }
271
NetworkTypeToStatsType(rtc::AdapterType type)272 const char* NetworkTypeToStatsType(rtc::AdapterType type) {
273 switch (type) {
274 case rtc::ADAPTER_TYPE_CELLULAR:
275 case rtc::ADAPTER_TYPE_CELLULAR_2G:
276 case rtc::ADAPTER_TYPE_CELLULAR_3G:
277 case rtc::ADAPTER_TYPE_CELLULAR_4G:
278 case rtc::ADAPTER_TYPE_CELLULAR_5G:
279 return RTCNetworkType::kCellular;
280 case rtc::ADAPTER_TYPE_ETHERNET:
281 return RTCNetworkType::kEthernet;
282 case rtc::ADAPTER_TYPE_WIFI:
283 return RTCNetworkType::kWifi;
284 case rtc::ADAPTER_TYPE_VPN:
285 return RTCNetworkType::kVpn;
286 case rtc::ADAPTER_TYPE_UNKNOWN:
287 case rtc::ADAPTER_TYPE_LOOPBACK:
288 case rtc::ADAPTER_TYPE_ANY:
289 return RTCNetworkType::kUnknown;
290 }
291 RTC_DCHECK_NOTREACHED();
292 return nullptr;
293 }
294
NetworkTypeToStatsNetworkAdapterType(rtc::AdapterType type)295 absl::string_view NetworkTypeToStatsNetworkAdapterType(rtc::AdapterType type) {
296 switch (type) {
297 case rtc::ADAPTER_TYPE_CELLULAR:
298 return RTCNetworkAdapterType::kCellular;
299 case rtc::ADAPTER_TYPE_CELLULAR_2G:
300 return RTCNetworkAdapterType::kCellular2g;
301 case rtc::ADAPTER_TYPE_CELLULAR_3G:
302 return RTCNetworkAdapterType::kCellular3g;
303 case rtc::ADAPTER_TYPE_CELLULAR_4G:
304 return RTCNetworkAdapterType::kCellular4g;
305 case rtc::ADAPTER_TYPE_CELLULAR_5G:
306 return RTCNetworkAdapterType::kCellular5g;
307 case rtc::ADAPTER_TYPE_ETHERNET:
308 return RTCNetworkAdapterType::kEthernet;
309 case rtc::ADAPTER_TYPE_WIFI:
310 return RTCNetworkAdapterType::kWifi;
311 case rtc::ADAPTER_TYPE_UNKNOWN:
312 return RTCNetworkAdapterType::kUnknown;
313 case rtc::ADAPTER_TYPE_LOOPBACK:
314 return RTCNetworkAdapterType::kLoopback;
315 case rtc::ADAPTER_TYPE_ANY:
316 return RTCNetworkAdapterType::kAny;
317 case rtc::ADAPTER_TYPE_VPN:
318 /* should not be handled here. Vpn is modelled as a bool */
319 break;
320 }
321 RTC_DCHECK_NOTREACHED();
322 return {};
323 }
324
QualityLimitationReasonToRTCQualityLimitationReason(QualityLimitationReason reason)325 const char* QualityLimitationReasonToRTCQualityLimitationReason(
326 QualityLimitationReason reason) {
327 switch (reason) {
328 case QualityLimitationReason::kNone:
329 return RTCQualityLimitationReason::kNone;
330 case QualityLimitationReason::kCpu:
331 return RTCQualityLimitationReason::kCpu;
332 case QualityLimitationReason::kBandwidth:
333 return RTCQualityLimitationReason::kBandwidth;
334 case QualityLimitationReason::kOther:
335 return RTCQualityLimitationReason::kOther;
336 }
337 RTC_CHECK_NOTREACHED();
338 }
339
340 std::map<std::string, double>
QualityLimitationDurationToRTCQualityLimitationDuration(std::map<webrtc::QualityLimitationReason,int64_t> durations_ms)341 QualityLimitationDurationToRTCQualityLimitationDuration(
342 std::map<webrtc::QualityLimitationReason, int64_t> durations_ms) {
343 std::map<std::string, double> result;
344 // The internal duration is defined in milliseconds while the spec defines
345 // the value in seconds:
346 // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationdurations
347 for (const auto& elem : durations_ms) {
348 result[QualityLimitationReasonToRTCQualityLimitationReason(elem.first)] =
349 elem.second / static_cast<double>(rtc::kNumMillisecsPerSec);
350 }
351 return result;
352 }
353
DoubleAudioLevelFromIntAudioLevel(int audio_level)354 double DoubleAudioLevelFromIntAudioLevel(int audio_level) {
355 RTC_DCHECK_GE(audio_level, 0);
356 RTC_DCHECK_LE(audio_level, 32767);
357 return audio_level / 32767.0;
358 }
359
360 // Gets the `codecId` identified by `transport_id` and `codec_params`. If no
361 // such `RTCCodecStats` exist yet, create it and add it to `report`.
GetCodecIdAndMaybeCreateCodecStats(uint64_t timestamp_us,const char direction,const std::string & transport_id,const RtpCodecParameters & codec_params,RTCStatsReport * report)362 std::string GetCodecIdAndMaybeCreateCodecStats(
363 uint64_t timestamp_us,
364 const char direction,
365 const std::string& transport_id,
366 const RtpCodecParameters& codec_params,
367 RTCStatsReport* report) {
368 RTC_DCHECK_GE(codec_params.payload_type, 0);
369 RTC_DCHECK_LE(codec_params.payload_type, 127);
370 RTC_DCHECK(codec_params.clock_rate);
371 uint32_t payload_type = static_cast<uint32_t>(codec_params.payload_type);
372 std::string codec_id = RTCCodecStatsIDFromTransportAndCodecParameters(
373 direction, transport_id, codec_params);
374 if (report->Get(codec_id) != nullptr) {
375 // The `RTCCodecStats` already exists.
376 return codec_id;
377 }
378 // Create the `RTCCodecStats` that we want to reference.
379 std::unique_ptr<RTCCodecStats> codec_stats(
380 std::make_unique<RTCCodecStats>(codec_id, timestamp_us));
381 codec_stats->payload_type = payload_type;
382 codec_stats->mime_type = codec_params.mime_type();
383 if (codec_params.clock_rate.has_value()) {
384 codec_stats->clock_rate = static_cast<uint32_t>(*codec_params.clock_rate);
385 }
386 if (codec_params.num_channels) {
387 codec_stats->channels = *codec_params.num_channels;
388 }
389
390 rtc::StringBuilder fmtp;
391 if (WriteFmtpParameters(codec_params.parameters, &fmtp)) {
392 codec_stats->sdp_fmtp_line = fmtp.Release();
393 }
394 codec_stats->transport_id = transport_id;
395 report->AddStats(std::move(codec_stats));
396 return codec_id;
397 }
398
SetMediaStreamTrackStatsFromMediaStreamTrackInterface(const MediaStreamTrackInterface & track,DEPRECATED_RTCMediaStreamTrackStats * track_stats)399 void SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
400 const MediaStreamTrackInterface& track,
401 DEPRECATED_RTCMediaStreamTrackStats* track_stats) {
402 track_stats->track_identifier = track.id();
403 track_stats->ended = (track.state() == MediaStreamTrackInterface::kEnded);
404 }
405
406 // Provides the media independent counters (both audio and video).
SetInboundRTPStreamStatsFromMediaReceiverInfo(const cricket::MediaReceiverInfo & media_receiver_info,RTCInboundRTPStreamStats * inbound_stats)407 void SetInboundRTPStreamStatsFromMediaReceiverInfo(
408 const cricket::MediaReceiverInfo& media_receiver_info,
409 RTCInboundRTPStreamStats* inbound_stats) {
410 RTC_DCHECK(inbound_stats);
411 inbound_stats->ssrc = media_receiver_info.ssrc();
412 inbound_stats->packets_received =
413 static_cast<uint32_t>(media_receiver_info.packets_rcvd);
414 inbound_stats->bytes_received =
415 static_cast<uint64_t>(media_receiver_info.payload_bytes_rcvd);
416 inbound_stats->header_bytes_received =
417 static_cast<uint64_t>(media_receiver_info.header_and_padding_bytes_rcvd);
418 inbound_stats->packets_lost =
419 static_cast<int32_t>(media_receiver_info.packets_lost);
420 inbound_stats->jitter_buffer_delay =
421 media_receiver_info.jitter_buffer_delay_seconds;
422 if (media_receiver_info.jitter_buffer_target_delay_seconds.has_value()) {
423 inbound_stats->jitter_buffer_target_delay =
424 *media_receiver_info.jitter_buffer_target_delay_seconds;
425 }
426 if (media_receiver_info.jitter_buffer_minimum_delay_seconds.has_value()) {
427 inbound_stats->jitter_buffer_minimum_delay =
428 *media_receiver_info.jitter_buffer_minimum_delay_seconds;
429 }
430 inbound_stats->jitter_buffer_emitted_count =
431 media_receiver_info.jitter_buffer_emitted_count;
432 if (media_receiver_info.nacks_sent.has_value()) {
433 inbound_stats->nack_count = *media_receiver_info.nacks_sent;
434 }
435 }
436
CreateInboundAudioStreamStats(const cricket::VoiceMediaInfo & voice_media_info,const cricket::VoiceReceiverInfo & voice_receiver_info,const std::string & transport_id,const std::string & mid,int64_t timestamp_us,RTCStatsReport * report)437 std::unique_ptr<RTCInboundRTPStreamStats> CreateInboundAudioStreamStats(
438 const cricket::VoiceMediaInfo& voice_media_info,
439 const cricket::VoiceReceiverInfo& voice_receiver_info,
440 const std::string& transport_id,
441 const std::string& mid,
442 int64_t timestamp_us,
443 RTCStatsReport* report) {
444 auto inbound_audio = std::make_unique<RTCInboundRTPStreamStats>(
445 /*id=*/RTCInboundRTPStreamStatsIDFromSSRC(
446 transport_id, cricket::MEDIA_TYPE_AUDIO, voice_receiver_info.ssrc()),
447 timestamp_us);
448 SetInboundRTPStreamStatsFromMediaReceiverInfo(voice_receiver_info,
449 inbound_audio.get());
450 inbound_audio->transport_id = transport_id;
451 inbound_audio->mid = mid;
452 inbound_audio->media_type = "audio";
453 inbound_audio->kind = "audio";
454 if (voice_receiver_info.codec_payload_type.has_value()) {
455 auto codec_param_it = voice_media_info.receive_codecs.find(
456 voice_receiver_info.codec_payload_type.value());
457 RTC_DCHECK(codec_param_it != voice_media_info.receive_codecs.end());
458 if (codec_param_it != voice_media_info.receive_codecs.end()) {
459 inbound_audio->codec_id = GetCodecIdAndMaybeCreateCodecStats(
460 inbound_audio->timestamp_us(), kDirectionInbound, transport_id,
461 codec_param_it->second, report);
462 }
463 }
464 inbound_audio->jitter = static_cast<double>(voice_receiver_info.jitter_ms) /
465 rtc::kNumMillisecsPerSec;
466 inbound_audio->total_samples_received =
467 voice_receiver_info.total_samples_received;
468 inbound_audio->concealed_samples = voice_receiver_info.concealed_samples;
469 inbound_audio->silent_concealed_samples =
470 voice_receiver_info.silent_concealed_samples;
471 inbound_audio->concealment_events = voice_receiver_info.concealment_events;
472 inbound_audio->inserted_samples_for_deceleration =
473 voice_receiver_info.inserted_samples_for_deceleration;
474 inbound_audio->removed_samples_for_acceleration =
475 voice_receiver_info.removed_samples_for_acceleration;
476 if (voice_receiver_info.audio_level >= 0) {
477 inbound_audio->audio_level =
478 DoubleAudioLevelFromIntAudioLevel(voice_receiver_info.audio_level);
479 }
480 inbound_audio->total_audio_energy = voice_receiver_info.total_output_energy;
481 inbound_audio->total_samples_duration =
482 voice_receiver_info.total_output_duration;
483 // `fir_count`, `pli_count` and `sli_count` are only valid for video and are
484 // purposefully left undefined for audio.
485 if (voice_receiver_info.last_packet_received_timestamp_ms.has_value()) {
486 inbound_audio->last_packet_received_timestamp = static_cast<double>(
487 *voice_receiver_info.last_packet_received_timestamp_ms);
488 }
489 if (voice_receiver_info.estimated_playout_ntp_timestamp_ms.has_value()) {
490 // TODO(bugs.webrtc.org/10529): Fix time origin.
491 inbound_audio->estimated_playout_timestamp = static_cast<double>(
492 *voice_receiver_info.estimated_playout_ntp_timestamp_ms);
493 }
494 inbound_audio->fec_packets_received =
495 voice_receiver_info.fec_packets_received;
496 inbound_audio->fec_packets_discarded =
497 voice_receiver_info.fec_packets_discarded;
498 inbound_audio->packets_discarded = voice_receiver_info.packets_discarded;
499 inbound_audio->jitter_buffer_flushes =
500 voice_receiver_info.jitter_buffer_flushes;
501 inbound_audio->delayed_packet_outage_samples =
502 voice_receiver_info.delayed_packet_outage_samples;
503 inbound_audio->relative_packet_arrival_delay =
504 voice_receiver_info.relative_packet_arrival_delay_seconds;
505 inbound_audio->interruption_count =
506 voice_receiver_info.interruption_count >= 0
507 ? voice_receiver_info.interruption_count
508 : 0;
509 inbound_audio->total_interruption_duration =
510 static_cast<double>(voice_receiver_info.total_interruption_duration_ms) /
511 rtc::kNumMillisecsPerSec;
512 return inbound_audio;
513 }
514
515 std::unique_ptr<RTCRemoteOutboundRtpStreamStats>
CreateRemoteOutboundAudioStreamStats(const cricket::VoiceReceiverInfo & voice_receiver_info,const std::string & mid,const RTCInboundRTPStreamStats & inbound_audio_stats,const std::string & transport_id)516 CreateRemoteOutboundAudioStreamStats(
517 const cricket::VoiceReceiverInfo& voice_receiver_info,
518 const std::string& mid,
519 const RTCInboundRTPStreamStats& inbound_audio_stats,
520 const std::string& transport_id) {
521 if (!voice_receiver_info.last_sender_report_timestamp_ms.has_value()) {
522 // Cannot create `RTCRemoteOutboundRtpStreamStats` when the RTCP SR arrival
523 // timestamp is not available - i.e., until the first sender report is
524 // received.
525 return nullptr;
526 }
527 RTC_DCHECK_GT(voice_receiver_info.sender_reports_reports_count, 0);
528
529 // Create.
530 auto stats = std::make_unique<RTCRemoteOutboundRtpStreamStats>(
531 /*id=*/RTCRemoteOutboundRTPStreamStatsIDFromSSRC(
532 cricket::MEDIA_TYPE_AUDIO, voice_receiver_info.ssrc()),
533 /*timestamp_us=*/rtc::kNumMicrosecsPerMillisec *
534 voice_receiver_info.last_sender_report_timestamp_ms.value());
535
536 // Populate.
537 // - RTCRtpStreamStats.
538 stats->ssrc = voice_receiver_info.ssrc();
539 stats->kind = "audio";
540 stats->transport_id = transport_id;
541 if (inbound_audio_stats.codec_id.is_defined()) {
542 stats->codec_id = *inbound_audio_stats.codec_id;
543 }
544 // - RTCSentRtpStreamStats.
545 stats->packets_sent = voice_receiver_info.sender_reports_packets_sent;
546 stats->bytes_sent = voice_receiver_info.sender_reports_bytes_sent;
547 // - RTCRemoteOutboundRtpStreamStats.
548 stats->local_id = inbound_audio_stats.id();
549 RTC_DCHECK(
550 voice_receiver_info.last_sender_report_remote_timestamp_ms.has_value());
551 stats->remote_timestamp = static_cast<double>(
552 voice_receiver_info.last_sender_report_remote_timestamp_ms.value());
553 stats->reports_sent = voice_receiver_info.sender_reports_reports_count;
554 if (voice_receiver_info.round_trip_time.has_value()) {
555 stats->round_trip_time =
556 voice_receiver_info.round_trip_time->seconds<double>();
557 }
558 stats->round_trip_time_measurements =
559 voice_receiver_info.round_trip_time_measurements;
560 stats->total_round_trip_time =
561 voice_receiver_info.total_round_trip_time.seconds<double>();
562
563 return stats;
564 }
565
SetInboundRTPStreamStatsFromVideoReceiverInfo(const std::string & transport_id,const std::string & mid,const cricket::VideoMediaInfo & video_media_info,const cricket::VideoReceiverInfo & video_receiver_info,RTCInboundRTPStreamStats * inbound_video,RTCStatsReport * report)566 void SetInboundRTPStreamStatsFromVideoReceiverInfo(
567 const std::string& transport_id,
568 const std::string& mid,
569 const cricket::VideoMediaInfo& video_media_info,
570 const cricket::VideoReceiverInfo& video_receiver_info,
571 RTCInboundRTPStreamStats* inbound_video,
572 RTCStatsReport* report) {
573 SetInboundRTPStreamStatsFromMediaReceiverInfo(video_receiver_info,
574 inbound_video);
575 inbound_video->transport_id = transport_id;
576 inbound_video->mid = mid;
577 inbound_video->media_type = "video";
578 inbound_video->kind = "video";
579 if (video_receiver_info.codec_payload_type.has_value()) {
580 auto codec_param_it = video_media_info.receive_codecs.find(
581 video_receiver_info.codec_payload_type.value());
582 RTC_DCHECK(codec_param_it != video_media_info.receive_codecs.end());
583 if (codec_param_it != video_media_info.receive_codecs.end()) {
584 inbound_video->codec_id = GetCodecIdAndMaybeCreateCodecStats(
585 inbound_video->timestamp_us(), kDirectionInbound, transport_id,
586 codec_param_it->second, report);
587 }
588 }
589 inbound_video->jitter = static_cast<double>(video_receiver_info.jitter_ms) /
590 rtc::kNumMillisecsPerSec;
591 inbound_video->fir_count =
592 static_cast<uint32_t>(video_receiver_info.firs_sent);
593 inbound_video->pli_count =
594 static_cast<uint32_t>(video_receiver_info.plis_sent);
595 inbound_video->frames_received = video_receiver_info.frames_received;
596 inbound_video->frames_decoded = video_receiver_info.frames_decoded;
597 inbound_video->frames_dropped = video_receiver_info.frames_dropped;
598 inbound_video->key_frames_decoded = video_receiver_info.key_frames_decoded;
599 if (video_receiver_info.frame_width > 0) {
600 inbound_video->frame_width =
601 static_cast<uint32_t>(video_receiver_info.frame_width);
602 }
603 if (video_receiver_info.frame_height > 0) {
604 inbound_video->frame_height =
605 static_cast<uint32_t>(video_receiver_info.frame_height);
606 }
607 if (video_receiver_info.framerate_decoded > 0) {
608 inbound_video->frames_per_second = video_receiver_info.framerate_decoded;
609 }
610 if (video_receiver_info.qp_sum.has_value()) {
611 inbound_video->qp_sum = *video_receiver_info.qp_sum;
612 }
613 if (video_receiver_info.timing_frame_info.has_value()) {
614 inbound_video->goog_timing_frame_info =
615 video_receiver_info.timing_frame_info->ToString();
616 }
617 inbound_video->total_decode_time =
618 video_receiver_info.total_decode_time.seconds<double>();
619 inbound_video->total_processing_delay =
620 video_receiver_info.total_processing_delay.seconds<double>();
621 inbound_video->total_assembly_time =
622 video_receiver_info.total_assembly_time.seconds<double>();
623 inbound_video->frames_assembled_from_multiple_packets =
624 video_receiver_info.frames_assembled_from_multiple_packets;
625 inbound_video->total_inter_frame_delay =
626 video_receiver_info.total_inter_frame_delay;
627 inbound_video->total_squared_inter_frame_delay =
628 video_receiver_info.total_squared_inter_frame_delay;
629 inbound_video->pause_count = video_receiver_info.pause_count;
630 inbound_video->total_pauses_duration =
631 static_cast<double>(video_receiver_info.total_pauses_duration_ms) /
632 rtc::kNumMillisecsPerSec;
633 inbound_video->freeze_count = video_receiver_info.freeze_count;
634 inbound_video->total_freezes_duration =
635 static_cast<double>(video_receiver_info.total_freezes_duration_ms) /
636 rtc::kNumMillisecsPerSec;
637 inbound_video->min_playout_delay =
638 static_cast<double>(video_receiver_info.min_playout_delay_ms) /
639 rtc::kNumMillisecsPerSec;
640 if (video_receiver_info.last_packet_received_timestamp_ms.has_value()) {
641 inbound_video->last_packet_received_timestamp = static_cast<double>(
642 *video_receiver_info.last_packet_received_timestamp_ms);
643 }
644 if (video_receiver_info.estimated_playout_ntp_timestamp_ms.has_value()) {
645 // TODO(bugs.webrtc.org/10529): Fix time origin if needed.
646 inbound_video->estimated_playout_timestamp = static_cast<double>(
647 *video_receiver_info.estimated_playout_ntp_timestamp_ms);
648 }
649 // TODO(bugs.webrtc.org/10529): When info's `content_info` is optional
650 // support the "unspecified" value.
651 if (video_receiver_info.content_type == VideoContentType::SCREENSHARE)
652 inbound_video->content_type = RTCContentType::kScreenshare;
653 if (!video_receiver_info.decoder_implementation_name.empty()) {
654 inbound_video->decoder_implementation =
655 video_receiver_info.decoder_implementation_name;
656 }
657 if (video_receiver_info.power_efficient_decoder.has_value()) {
658 inbound_video->power_efficient_decoder =
659 video_receiver_info.power_efficient_decoder.value();
660 }
661 }
662
663 // Provides the media independent counters and information (both audio and
664 // video).
SetOutboundRTPStreamStatsFromMediaSenderInfo(const cricket::MediaSenderInfo & media_sender_info,RTCOutboundRTPStreamStats * outbound_stats)665 void SetOutboundRTPStreamStatsFromMediaSenderInfo(
666 const cricket::MediaSenderInfo& media_sender_info,
667 RTCOutboundRTPStreamStats* outbound_stats) {
668 RTC_DCHECK(outbound_stats);
669 outbound_stats->ssrc = media_sender_info.ssrc();
670 outbound_stats->packets_sent =
671 static_cast<uint32_t>(media_sender_info.packets_sent);
672 outbound_stats->total_packet_send_delay =
673 media_sender_info.total_packet_send_delay.seconds<double>();
674 outbound_stats->retransmitted_packets_sent =
675 media_sender_info.retransmitted_packets_sent;
676 outbound_stats->bytes_sent =
677 static_cast<uint64_t>(media_sender_info.payload_bytes_sent);
678 outbound_stats->header_bytes_sent =
679 static_cast<uint64_t>(media_sender_info.header_and_padding_bytes_sent);
680 outbound_stats->retransmitted_bytes_sent =
681 media_sender_info.retransmitted_bytes_sent;
682 outbound_stats->nack_count = media_sender_info.nacks_rcvd;
683 if (media_sender_info.active.has_value()) {
684 outbound_stats->active = *media_sender_info.active;
685 }
686 }
687
SetOutboundRTPStreamStatsFromVoiceSenderInfo(const std::string & transport_id,const std::string & mid,const cricket::VoiceMediaInfo & voice_media_info,const cricket::VoiceSenderInfo & voice_sender_info,RTCOutboundRTPStreamStats * outbound_audio,RTCStatsReport * report)688 void SetOutboundRTPStreamStatsFromVoiceSenderInfo(
689 const std::string& transport_id,
690 const std::string& mid,
691 const cricket::VoiceMediaInfo& voice_media_info,
692 const cricket::VoiceSenderInfo& voice_sender_info,
693 RTCOutboundRTPStreamStats* outbound_audio,
694 RTCStatsReport* report) {
695 SetOutboundRTPStreamStatsFromMediaSenderInfo(voice_sender_info,
696 outbound_audio);
697 outbound_audio->transport_id = transport_id;
698 outbound_audio->mid = mid;
699 outbound_audio->media_type = "audio";
700 outbound_audio->kind = "audio";
701 if (voice_sender_info.target_bitrate.has_value() &&
702 *voice_sender_info.target_bitrate > 0) {
703 outbound_audio->target_bitrate = *voice_sender_info.target_bitrate;
704 }
705 if (voice_sender_info.codec_payload_type.has_value()) {
706 auto codec_param_it = voice_media_info.send_codecs.find(
707 voice_sender_info.codec_payload_type.value());
708 RTC_DCHECK(codec_param_it != voice_media_info.send_codecs.end());
709 if (codec_param_it != voice_media_info.send_codecs.end()) {
710 outbound_audio->codec_id = GetCodecIdAndMaybeCreateCodecStats(
711 outbound_audio->timestamp_us(), kDirectionOutbound, transport_id,
712 codec_param_it->second, report);
713 }
714 }
715 // `fir_count`, `pli_count` and `sli_count` are only valid for video and are
716 // purposefully left undefined for audio.
717 }
718
SetOutboundRTPStreamStatsFromVideoSenderInfo(const std::string & transport_id,const std::string & mid,const cricket::VideoMediaInfo & video_media_info,const cricket::VideoSenderInfo & video_sender_info,RTCOutboundRTPStreamStats * outbound_video,RTCStatsReport * report)719 void SetOutboundRTPStreamStatsFromVideoSenderInfo(
720 const std::string& transport_id,
721 const std::string& mid,
722 const cricket::VideoMediaInfo& video_media_info,
723 const cricket::VideoSenderInfo& video_sender_info,
724 RTCOutboundRTPStreamStats* outbound_video,
725 RTCStatsReport* report) {
726 SetOutboundRTPStreamStatsFromMediaSenderInfo(video_sender_info,
727 outbound_video);
728 outbound_video->transport_id = transport_id;
729 outbound_video->mid = mid;
730 outbound_video->media_type = "video";
731 outbound_video->kind = "video";
732 if (video_sender_info.codec_payload_type.has_value()) {
733 auto codec_param_it = video_media_info.send_codecs.find(
734 video_sender_info.codec_payload_type.value());
735 RTC_DCHECK(codec_param_it != video_media_info.send_codecs.end());
736 if (codec_param_it != video_media_info.send_codecs.end()) {
737 outbound_video->codec_id = GetCodecIdAndMaybeCreateCodecStats(
738 outbound_video->timestamp_us(), kDirectionOutbound, transport_id,
739 codec_param_it->second, report);
740 }
741 }
742 outbound_video->fir_count =
743 static_cast<uint32_t>(video_sender_info.firs_rcvd);
744 outbound_video->pli_count =
745 static_cast<uint32_t>(video_sender_info.plis_rcvd);
746 if (video_sender_info.qp_sum.has_value())
747 outbound_video->qp_sum = *video_sender_info.qp_sum;
748 if (video_sender_info.target_bitrate.has_value() &&
749 *video_sender_info.target_bitrate > 0) {
750 outbound_video->target_bitrate = *video_sender_info.target_bitrate;
751 }
752 outbound_video->frames_encoded = video_sender_info.frames_encoded;
753 outbound_video->key_frames_encoded = video_sender_info.key_frames_encoded;
754 outbound_video->total_encode_time =
755 static_cast<double>(video_sender_info.total_encode_time_ms) /
756 rtc::kNumMillisecsPerSec;
757 outbound_video->total_encoded_bytes_target =
758 video_sender_info.total_encoded_bytes_target;
759 if (video_sender_info.send_frame_width > 0) {
760 outbound_video->frame_width =
761 static_cast<uint32_t>(video_sender_info.send_frame_width);
762 }
763 if (video_sender_info.send_frame_height > 0) {
764 outbound_video->frame_height =
765 static_cast<uint32_t>(video_sender_info.send_frame_height);
766 }
767 if (video_sender_info.framerate_sent > 0) {
768 outbound_video->frames_per_second = video_sender_info.framerate_sent;
769 }
770 outbound_video->frames_sent = video_sender_info.frames_sent;
771 outbound_video->huge_frames_sent = video_sender_info.huge_frames_sent;
772 outbound_video->quality_limitation_reason =
773 QualityLimitationReasonToRTCQualityLimitationReason(
774 video_sender_info.quality_limitation_reason);
775 outbound_video->quality_limitation_durations =
776 QualityLimitationDurationToRTCQualityLimitationDuration(
777 video_sender_info.quality_limitation_durations_ms);
778 outbound_video->quality_limitation_resolution_changes =
779 video_sender_info.quality_limitation_resolution_changes;
780 // TODO(https://crbug.com/webrtc/10529): When info's `content_info` is
781 // optional, support the "unspecified" value.
782 if (video_sender_info.content_type == VideoContentType::SCREENSHARE)
783 outbound_video->content_type = RTCContentType::kScreenshare;
784 if (!video_sender_info.encoder_implementation_name.empty()) {
785 outbound_video->encoder_implementation =
786 video_sender_info.encoder_implementation_name;
787 }
788 if (video_sender_info.rid.has_value()) {
789 outbound_video->rid = *video_sender_info.rid;
790 }
791 if (video_sender_info.power_efficient_encoder.has_value()) {
792 outbound_video->power_efficient_encoder =
793 video_sender_info.power_efficient_encoder.value();
794 }
795 }
796
797 std::unique_ptr<RTCRemoteInboundRtpStreamStats>
ProduceRemoteInboundRtpStreamStatsFromReportBlockData(const std::string & transport_id,const ReportBlockData & report_block_data,cricket::MediaType media_type,const std::map<std::string,RTCOutboundRTPStreamStats * > & outbound_rtps,const RTCStatsReport & report)798 ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
799 const std::string& transport_id,
800 const ReportBlockData& report_block_data,
801 cricket::MediaType media_type,
802 const std::map<std::string, RTCOutboundRTPStreamStats*>& outbound_rtps,
803 const RTCStatsReport& report) {
804 const auto& report_block = report_block_data.report_block();
805 // RTCStats' timestamp generally refers to when the metric was sampled, but
806 // for "remote-[outbound/inbound]-rtp" it refers to the local time when the
807 // Report Block was received.
808 auto remote_inbound = std::make_unique<RTCRemoteInboundRtpStreamStats>(
809 RTCRemoteInboundRtpStreamStatsIdFromSourceSsrc(media_type,
810 report_block.source_ssrc),
811 /*timestamp=*/report_block_data.report_block_timestamp_utc_us());
812 remote_inbound->ssrc = report_block.source_ssrc;
813 remote_inbound->kind =
814 media_type == cricket::MEDIA_TYPE_AUDIO ? "audio" : "video";
815 remote_inbound->packets_lost = report_block.packets_lost;
816 remote_inbound->fraction_lost =
817 static_cast<double>(report_block.fraction_lost) / (1 << 8);
818 if (report_block_data.num_rtts() > 0) {
819 remote_inbound->round_trip_time =
820 static_cast<double>(report_block_data.last_rtt_ms()) /
821 rtc::kNumMillisecsPerSec;
822 }
823 remote_inbound->total_round_trip_time =
824 static_cast<double>(report_block_data.sum_rtt_ms()) /
825 rtc::kNumMillisecsPerSec;
826 remote_inbound->round_trip_time_measurements =
827 report_block_data.num_rtts();
828
829 std::string local_id = RTCOutboundRTPStreamStatsIDFromSSRC(
830 transport_id, media_type, report_block.source_ssrc);
831 // Look up local stat from `outbound_rtps` where the pointers are non-const.
832 auto local_id_it = outbound_rtps.find(local_id);
833 if (local_id_it != outbound_rtps.end()) {
834 remote_inbound->local_id = local_id;
835 auto& outbound_rtp = *local_id_it->second;
836 outbound_rtp.remote_id = remote_inbound->id();
837 // The RTP/RTCP transport is obtained from the
838 // RTCOutboundRtpStreamStats's transport.
839 const auto* transport_from_id = report.Get(transport_id);
840 if (transport_from_id) {
841 const auto& transport = transport_from_id->cast_to<RTCTransportStats>();
842 // If RTP and RTCP are not multiplexed, there is a separate RTCP
843 // transport paired with the RTP transport, otherwise the same
844 // transport is used for RTCP and RTP.
845 remote_inbound->transport_id =
846 transport.rtcp_transport_stats_id.is_defined()
847 ? *transport.rtcp_transport_stats_id
848 : *outbound_rtp.transport_id;
849 }
850 // We're assuming the same codec is used on both ends. However if the
851 // codec is switched out on the fly we may have received a Report Block
852 // based on the previous codec and there is no way to tell which point in
853 // time the codec changed for the remote end.
854 const auto* codec_from_id = outbound_rtp.codec_id.is_defined()
855 ? report.Get(*outbound_rtp.codec_id)
856 : nullptr;
857 if (codec_from_id) {
858 remote_inbound->codec_id = *outbound_rtp.codec_id;
859 const auto& codec = codec_from_id->cast_to<RTCCodecStats>();
860 if (codec.clock_rate.is_defined()) {
861 // The Report Block jitter is expressed in RTP timestamp units
862 // (https://tools.ietf.org/html/rfc3550#section-6.4.1). To convert this
863 // to seconds we divide by the codec's clock rate.
864 remote_inbound->jitter =
865 static_cast<double>(report_block.jitter) / *codec.clock_rate;
866 }
867 }
868 }
869 return remote_inbound;
870 }
871
ProduceCertificateStatsFromSSLCertificateStats(int64_t timestamp_us,const rtc::SSLCertificateStats & certificate_stats,RTCStatsReport * report)872 void ProduceCertificateStatsFromSSLCertificateStats(
873 int64_t timestamp_us,
874 const rtc::SSLCertificateStats& certificate_stats,
875 RTCStatsReport* report) {
876 RTCCertificateStats* prev_certificate_stats = nullptr;
877 for (const rtc::SSLCertificateStats* s = &certificate_stats; s;
878 s = s->issuer.get()) {
879 std::string certificate_stats_id =
880 RTCCertificateIDFromFingerprint(s->fingerprint);
881 // It is possible for the same certificate to show up multiple times, e.g.
882 // if local and remote side use the same certificate in a loopback call.
883 // If the report already contains stats for this certificate, skip it.
884 if (report->Get(certificate_stats_id)) {
885 RTC_DCHECK_EQ(s, &certificate_stats);
886 break;
887 }
888 RTCCertificateStats* certificate_stats =
889 new RTCCertificateStats(certificate_stats_id, timestamp_us);
890 certificate_stats->fingerprint = s->fingerprint;
891 certificate_stats->fingerprint_algorithm = s->fingerprint_algorithm;
892 certificate_stats->base64_certificate = s->base64_certificate;
893 if (prev_certificate_stats)
894 prev_certificate_stats->issuer_certificate_id = certificate_stats->id();
895 report->AddStats(std::unique_ptr<RTCCertificateStats>(certificate_stats));
896 prev_certificate_stats = certificate_stats;
897 }
898 }
899
ProduceIceCandidateStats(int64_t timestamp_us,const cricket::Candidate & candidate,bool is_local,const std::string & transport_id,RTCStatsReport * report)900 const std::string& ProduceIceCandidateStats(int64_t timestamp_us,
901 const cricket::Candidate& candidate,
902 bool is_local,
903 const std::string& transport_id,
904 RTCStatsReport* report) {
905 const std::string& id = "I" + candidate.id();
906 const RTCStats* stats = report->Get(id);
907 if (!stats) {
908 std::unique_ptr<RTCIceCandidateStats> candidate_stats;
909 if (is_local)
910 candidate_stats =
911 std::make_unique<RTCLocalIceCandidateStats>(id, timestamp_us);
912 else
913 candidate_stats =
914 std::make_unique<RTCRemoteIceCandidateStats>(id, timestamp_us);
915 candidate_stats->transport_id = transport_id;
916 if (is_local) {
917 candidate_stats->network_type =
918 NetworkTypeToStatsType(candidate.network_type());
919 const std::string& candidate_type = candidate.type();
920 const std::string& relay_protocol = candidate.relay_protocol();
921 const std::string& url = candidate.url();
922 if (candidate_type == cricket::RELAY_PORT_TYPE ||
923 (candidate_type == cricket::PRFLX_PORT_TYPE &&
924 !relay_protocol.empty())) {
925 RTC_DCHECK(relay_protocol.compare("udp") == 0 ||
926 relay_protocol.compare("tcp") == 0 ||
927 relay_protocol.compare("tls") == 0);
928 candidate_stats->relay_protocol = relay_protocol;
929 if (!url.empty()) {
930 candidate_stats->url = url;
931 }
932 } else if (candidate_type == cricket::STUN_PORT_TYPE) {
933 if (!url.empty()) {
934 candidate_stats->url = url;
935 }
936 }
937 if (candidate.network_type() == rtc::ADAPTER_TYPE_VPN) {
938 candidate_stats->vpn = true;
939 candidate_stats->network_adapter_type =
940 std::string(NetworkTypeToStatsNetworkAdapterType(
941 candidate.underlying_type_for_vpn()));
942 } else {
943 candidate_stats->vpn = false;
944 candidate_stats->network_adapter_type = std::string(
945 NetworkTypeToStatsNetworkAdapterType(candidate.network_type()));
946 }
947 } else {
948 // We don't expect to know the adapter type of remote candidates.
949 RTC_DCHECK_EQ(rtc::ADAPTER_TYPE_UNKNOWN, candidate.network_type());
950 RTC_DCHECK_EQ(0, candidate.relay_protocol().compare(""));
951 RTC_DCHECK_EQ(rtc::ADAPTER_TYPE_UNKNOWN,
952 candidate.underlying_type_for_vpn());
953 }
954 candidate_stats->ip = candidate.address().ipaddr().ToString();
955 candidate_stats->address = candidate.address().ipaddr().ToString();
956 candidate_stats->port = static_cast<int32_t>(candidate.address().port());
957 candidate_stats->protocol = candidate.protocol();
958 candidate_stats->candidate_type =
959 CandidateTypeToRTCIceCandidateType(candidate.type());
960 candidate_stats->priority = static_cast<int32_t>(candidate.priority());
961 candidate_stats->foundation = candidate.foundation();
962 auto related_address = candidate.related_address();
963 if (related_address.port() != 0) {
964 candidate_stats->related_address = related_address.ipaddr().ToString();
965 candidate_stats->related_port =
966 static_cast<int32_t>(related_address.port());
967 }
968 candidate_stats->username_fragment = candidate.username();
969 if (candidate.protocol() == "tcp") {
970 candidate_stats->tcp_type = candidate.tcptype();
971 }
972
973 stats = candidate_stats.get();
974 report->AddStats(std::move(candidate_stats));
975 }
976 RTC_DCHECK_EQ(stats->type(), is_local ? RTCLocalIceCandidateStats::kType
977 : RTCRemoteIceCandidateStats::kType);
978 return stats->id();
979 }
980
981 template <typename StatsType>
SetAudioProcessingStats(StatsType * stats,const AudioProcessingStats & apm_stats)982 void SetAudioProcessingStats(StatsType* stats,
983 const AudioProcessingStats& apm_stats) {
984 if (apm_stats.echo_return_loss.has_value()) {
985 stats->echo_return_loss = *apm_stats.echo_return_loss;
986 }
987 if (apm_stats.echo_return_loss_enhancement.has_value()) {
988 stats->echo_return_loss_enhancement =
989 *apm_stats.echo_return_loss_enhancement;
990 }
991 }
992
993 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats>
ProduceMediaStreamTrackStatsFromVoiceSenderInfo(int64_t timestamp_us,AudioTrackInterface & audio_track,const cricket::VoiceSenderInfo & voice_sender_info,int attachment_id)994 ProduceMediaStreamTrackStatsFromVoiceSenderInfo(
995 int64_t timestamp_us,
996 AudioTrackInterface& audio_track,
997 const cricket::VoiceSenderInfo& voice_sender_info,
998 int attachment_id) {
999 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats> audio_track_stats(
1000 std::make_unique<DEPRECATED_RTCMediaStreamTrackStats>(
1001 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
1002 kDirectionOutbound, attachment_id),
1003 timestamp_us, RTCMediaStreamTrackKind::kAudio));
1004 SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
1005 audio_track, audio_track_stats.get());
1006 audio_track_stats->media_source_id =
1007 RTCMediaSourceStatsIDFromKindAndAttachment(cricket::MEDIA_TYPE_AUDIO,
1008 attachment_id);
1009 audio_track_stats->remote_source = false;
1010 audio_track_stats->detached = false;
1011 // Audio processor may be attached to either the track or the send
1012 // stream, so look in both places.
1013 SetAudioProcessingStats(audio_track_stats.get(),
1014 voice_sender_info.apm_statistics);
1015 auto audio_processor(audio_track.GetAudioProcessor());
1016 if (audio_processor.get()) {
1017 // The `has_remote_tracks` argument is obsolete; makes no difference if it's
1018 // set to true or false.
1019 AudioProcessorInterface::AudioProcessorStatistics ap_stats =
1020 audio_processor->GetStats(/*has_remote_tracks=*/false);
1021 SetAudioProcessingStats(audio_track_stats.get(), ap_stats.apm_statistics);
1022 }
1023 return audio_track_stats;
1024 }
1025
1026 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats>
ProduceMediaStreamTrackStatsFromVoiceReceiverInfo(int64_t timestamp_us,const AudioTrackInterface & audio_track,const cricket::VoiceReceiverInfo & voice_receiver_info,int attachment_id)1027 ProduceMediaStreamTrackStatsFromVoiceReceiverInfo(
1028 int64_t timestamp_us,
1029 const AudioTrackInterface& audio_track,
1030 const cricket::VoiceReceiverInfo& voice_receiver_info,
1031 int attachment_id) {
1032 // Since receiver tracks can't be reattached, we use the SSRC as
1033 // an attachment identifier.
1034 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats> audio_track_stats(
1035 std::make_unique<DEPRECATED_RTCMediaStreamTrackStats>(
1036 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
1037 kDirectionInbound, attachment_id),
1038 timestamp_us, RTCMediaStreamTrackKind::kAudio));
1039 SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
1040 audio_track, audio_track_stats.get());
1041 audio_track_stats->remote_source = true;
1042 audio_track_stats->detached = false;
1043 if (voice_receiver_info.audio_level >= 0) {
1044 audio_track_stats->audio_level =
1045 DoubleAudioLevelFromIntAudioLevel(voice_receiver_info.audio_level);
1046 }
1047 audio_track_stats->jitter_buffer_delay =
1048 voice_receiver_info.jitter_buffer_delay_seconds;
1049 audio_track_stats->jitter_buffer_emitted_count =
1050 voice_receiver_info.jitter_buffer_emitted_count;
1051 audio_track_stats->inserted_samples_for_deceleration =
1052 voice_receiver_info.inserted_samples_for_deceleration;
1053 audio_track_stats->removed_samples_for_acceleration =
1054 voice_receiver_info.removed_samples_for_acceleration;
1055 audio_track_stats->total_audio_energy =
1056 voice_receiver_info.total_output_energy;
1057 audio_track_stats->total_samples_received =
1058 voice_receiver_info.total_samples_received;
1059 audio_track_stats->total_samples_duration =
1060 voice_receiver_info.total_output_duration;
1061 audio_track_stats->concealed_samples = voice_receiver_info.concealed_samples;
1062 audio_track_stats->silent_concealed_samples =
1063 voice_receiver_info.silent_concealed_samples;
1064 audio_track_stats->concealment_events =
1065 voice_receiver_info.concealment_events;
1066
1067 return audio_track_stats;
1068 }
1069
1070 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats>
ProduceMediaStreamTrackStatsFromVideoSenderInfo(int64_t timestamp_us,const VideoTrackInterface & video_track,const cricket::VideoSenderInfo & video_sender_info,int attachment_id)1071 ProduceMediaStreamTrackStatsFromVideoSenderInfo(
1072 int64_t timestamp_us,
1073 const VideoTrackInterface& video_track,
1074 const cricket::VideoSenderInfo& video_sender_info,
1075 int attachment_id) {
1076 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats> video_track_stats(
1077 std::make_unique<DEPRECATED_RTCMediaStreamTrackStats>(
1078 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
1079 kDirectionOutbound, attachment_id),
1080 timestamp_us, RTCMediaStreamTrackKind::kVideo));
1081 SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
1082 video_track, video_track_stats.get());
1083 video_track_stats->media_source_id =
1084 RTCMediaSourceStatsIDFromKindAndAttachment(cricket::MEDIA_TYPE_VIDEO,
1085 attachment_id);
1086 video_track_stats->remote_source = false;
1087 video_track_stats->detached = false;
1088 video_track_stats->frame_width =
1089 static_cast<uint32_t>(video_sender_info.send_frame_width);
1090 video_track_stats->frame_height =
1091 static_cast<uint32_t>(video_sender_info.send_frame_height);
1092 // TODO(hbos): Will reduce this by frames dropped due to congestion control
1093 // when available. https://crbug.com/659137
1094 video_track_stats->frames_sent = video_sender_info.frames_encoded;
1095 video_track_stats->huge_frames_sent = video_sender_info.huge_frames_sent;
1096 return video_track_stats;
1097 }
1098
1099 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats>
ProduceMediaStreamTrackStatsFromVideoReceiverInfo(int64_t timestamp_us,const VideoTrackInterface & video_track,const cricket::VideoReceiverInfo & video_receiver_info,int attachment_id)1100 ProduceMediaStreamTrackStatsFromVideoReceiverInfo(
1101 int64_t timestamp_us,
1102 const VideoTrackInterface& video_track,
1103 const cricket::VideoReceiverInfo& video_receiver_info,
1104 int attachment_id) {
1105 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats> video_track_stats(
1106 std::make_unique<DEPRECATED_RTCMediaStreamTrackStats>(
1107 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
1108 kDirectionInbound, attachment_id),
1109 timestamp_us, RTCMediaStreamTrackKind::kVideo));
1110 SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
1111 video_track, video_track_stats.get());
1112 video_track_stats->remote_source = true;
1113 video_track_stats->detached = false;
1114 if (video_receiver_info.frame_width > 0 &&
1115 video_receiver_info.frame_height > 0) {
1116 video_track_stats->frame_width =
1117 static_cast<uint32_t>(video_receiver_info.frame_width);
1118 video_track_stats->frame_height =
1119 static_cast<uint32_t>(video_receiver_info.frame_height);
1120 }
1121 video_track_stats->jitter_buffer_delay =
1122 video_receiver_info.jitter_buffer_delay_seconds;
1123 video_track_stats->jitter_buffer_emitted_count =
1124 video_receiver_info.jitter_buffer_emitted_count;
1125 video_track_stats->frames_received = video_receiver_info.frames_received;
1126 // TODO(hbos): When we support receiving simulcast, this should be the total
1127 // number of frames correctly decoded, independent of which SSRC it was
1128 // received from. Since we don't support that, this is correct and is the same
1129 // value as "RTCInboundRTPStreamStats.framesDecoded". https://crbug.com/659137
1130 video_track_stats->frames_decoded = video_receiver_info.frames_decoded;
1131 video_track_stats->frames_dropped = video_receiver_info.frames_dropped;
1132
1133 return video_track_stats;
1134 }
1135
ProduceSenderMediaTrackStats(int64_t timestamp_us,const TrackMediaInfoMap & track_media_info_map,std::vector<rtc::scoped_refptr<RtpSenderInternal>> senders,RTCStatsReport * report)1136 void ProduceSenderMediaTrackStats(
1137 int64_t timestamp_us,
1138 const TrackMediaInfoMap& track_media_info_map,
1139 std::vector<rtc::scoped_refptr<RtpSenderInternal>> senders,
1140 RTCStatsReport* report) {
1141 // This function iterates over the senders to generate outgoing track stats.
1142
1143 // TODO(https://crbug.com/webrtc/14175): Stop collecting "track" stats,
1144 // they're deprecated.
1145 for (const auto& sender : senders) {
1146 if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) {
1147 AudioTrackInterface* track =
1148 static_cast<AudioTrackInterface*>(sender->track().get());
1149 if (!track)
1150 continue;
1151 cricket::VoiceSenderInfo null_sender_info;
1152 const cricket::VoiceSenderInfo* voice_sender_info = &null_sender_info;
1153 // TODO(hta): Checking on ssrc is not proper. There should be a way
1154 // to see from a sender whether it's connected or not.
1155 // Related to https://crbug.com/8694 (using ssrc 0 to indicate "none")
1156 if (sender->ssrc() != 0) {
1157 // When pc.close is called, sender info is discarded, so
1158 // we generate zeroes instead. Bug: It should be retained.
1159 // https://crbug.com/807174
1160 const cricket::VoiceSenderInfo* sender_info =
1161 track_media_info_map.GetVoiceSenderInfoBySsrc(sender->ssrc());
1162 if (sender_info) {
1163 voice_sender_info = sender_info;
1164 } else {
1165 RTC_DLOG(LS_INFO)
1166 << "RTCStatsCollector: No voice sender info for sender with ssrc "
1167 << sender->ssrc();
1168 }
1169 }
1170 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats> audio_track_stats =
1171 ProduceMediaStreamTrackStatsFromVoiceSenderInfo(
1172 timestamp_us, *track, *voice_sender_info, sender->AttachmentId());
1173 report->AddStats(std::move(audio_track_stats));
1174 } else if (sender->media_type() == cricket::MEDIA_TYPE_VIDEO) {
1175 VideoTrackInterface* track =
1176 static_cast<VideoTrackInterface*>(sender->track().get());
1177 if (!track)
1178 continue;
1179 cricket::VideoSenderInfo null_sender_info;
1180 const cricket::VideoSenderInfo* video_sender_info = &null_sender_info;
1181 // TODO(hta): Check on state not ssrc when state is available
1182 // Related to https://bugs.webrtc.org/8694 (using ssrc 0 to indicate
1183 // "none")
1184 if (sender->ssrc() != 0) {
1185 // When pc.close is called, sender info is discarded, so
1186 // we generate zeroes instead. Bug: It should be retained.
1187 // https://crbug.com/807174
1188 const cricket::VideoSenderInfo* sender_info =
1189 track_media_info_map.GetVideoSenderInfoBySsrc(sender->ssrc());
1190 if (sender_info) {
1191 video_sender_info = sender_info;
1192 } else {
1193 RTC_DLOG(LS_INFO)
1194 << "No video sender info for sender with ssrc " << sender->ssrc();
1195 }
1196 }
1197 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats> video_track_stats =
1198 ProduceMediaStreamTrackStatsFromVideoSenderInfo(
1199 timestamp_us, *track, *video_sender_info, sender->AttachmentId());
1200 report->AddStats(std::move(video_track_stats));
1201 } else {
1202 RTC_DCHECK_NOTREACHED();
1203 }
1204 }
1205 }
1206
ProduceReceiverMediaTrackStats(int64_t timestamp_us,const TrackMediaInfoMap & track_media_info_map,std::vector<rtc::scoped_refptr<RtpReceiverInternal>> receivers,RTCStatsReport * report)1207 void ProduceReceiverMediaTrackStats(
1208 int64_t timestamp_us,
1209 const TrackMediaInfoMap& track_media_info_map,
1210 std::vector<rtc::scoped_refptr<RtpReceiverInternal>> receivers,
1211 RTCStatsReport* report) {
1212 // This function iterates over the receivers to find the remote tracks.
1213 for (const auto& receiver : receivers) {
1214 if (receiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
1215 AudioTrackInterface* track =
1216 static_cast<AudioTrackInterface*>(receiver->track().get());
1217 const cricket::VoiceReceiverInfo* voice_receiver_info =
1218 track_media_info_map.GetVoiceReceiverInfo(*track);
1219 if (!voice_receiver_info) {
1220 continue;
1221 }
1222 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats> audio_track_stats =
1223 ProduceMediaStreamTrackStatsFromVoiceReceiverInfo(
1224 timestamp_us, *track, *voice_receiver_info,
1225 receiver->AttachmentId());
1226 report->AddStats(std::move(audio_track_stats));
1227 } else if (receiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
1228 VideoTrackInterface* track =
1229 static_cast<VideoTrackInterface*>(receiver->track().get());
1230 const cricket::VideoReceiverInfo* video_receiver_info =
1231 track_media_info_map.GetVideoReceiverInfo(*track);
1232 if (!video_receiver_info) {
1233 continue;
1234 }
1235 std::unique_ptr<DEPRECATED_RTCMediaStreamTrackStats> video_track_stats =
1236 ProduceMediaStreamTrackStatsFromVideoReceiverInfo(
1237 timestamp_us, *track, *video_receiver_info,
1238 receiver->AttachmentId());
1239 report->AddStats(std::move(video_track_stats));
1240 } else {
1241 RTC_DCHECK_NOTREACHED();
1242 }
1243 }
1244 }
1245
CreateReportFilteredBySelector(bool filter_by_sender_selector,rtc::scoped_refptr<const RTCStatsReport> report,rtc::scoped_refptr<RtpSenderInternal> sender_selector,rtc::scoped_refptr<RtpReceiverInternal> receiver_selector)1246 rtc::scoped_refptr<RTCStatsReport> CreateReportFilteredBySelector(
1247 bool filter_by_sender_selector,
1248 rtc::scoped_refptr<const RTCStatsReport> report,
1249 rtc::scoped_refptr<RtpSenderInternal> sender_selector,
1250 rtc::scoped_refptr<RtpReceiverInternal> receiver_selector) {
1251 std::vector<std::string> rtpstream_ids;
1252 if (filter_by_sender_selector) {
1253 // Filter mode: RTCStatsCollector::RequestInfo::kSenderSelector
1254 if (sender_selector) {
1255 // Find outbound-rtp(s) of the sender, i.e. the outbound-rtp(s) that
1256 // reference the sender stats.
1257 // Because we do not implement sender stats, we look at outbound-rtp(s)
1258 // that reference the track attachment stats for the sender instead.
1259 std::string track_id =
1260 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
1261 kDirectionOutbound, sender_selector->AttachmentId());
1262 for (const auto& stats : *report) {
1263 if (stats.type() != RTCOutboundRTPStreamStats::kType)
1264 continue;
1265 const auto& outbound_rtp = stats.cast_to<RTCOutboundRTPStreamStats>();
1266 if (outbound_rtp.track_id.is_defined() &&
1267 *outbound_rtp.track_id == track_id) {
1268 rtpstream_ids.push_back(outbound_rtp.id());
1269 }
1270 }
1271 }
1272 } else {
1273 // Filter mode: RTCStatsCollector::RequestInfo::kReceiverSelector
1274 if (receiver_selector) {
1275 // Find inbound-rtp(s) of the receiver, i.e. the inbound-rtp(s) that
1276 // reference the receiver stats.
1277 // Because we do not implement receiver stats, we look at inbound-rtp(s)
1278 // that reference the track attachment stats for the receiver instead.
1279 std::string track_id =
1280 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
1281 kDirectionInbound, receiver_selector->AttachmentId());
1282 for (const auto& stats : *report) {
1283 if (stats.type() != RTCInboundRTPStreamStats::kType)
1284 continue;
1285 const auto& inbound_rtp = stats.cast_to<RTCInboundRTPStreamStats>();
1286 if (inbound_rtp.track_id.is_defined() &&
1287 *inbound_rtp.track_id == track_id) {
1288 rtpstream_ids.push_back(inbound_rtp.id());
1289 }
1290 }
1291 }
1292 }
1293 if (rtpstream_ids.empty())
1294 return RTCStatsReport::Create(report->timestamp());
1295 return TakeReferencedStats(report->Copy(), rtpstream_ids);
1296 }
1297
1298 } // namespace
1299
1300 RTCStatsCollector::CertificateStatsPair
Copy() const1301 RTCStatsCollector::CertificateStatsPair::Copy() const {
1302 CertificateStatsPair copy;
1303 copy.local = local ? local->Copy() : nullptr;
1304 copy.remote = remote ? remote->Copy() : nullptr;
1305 return copy;
1306 }
1307
RequestInfo(rtc::scoped_refptr<RTCStatsCollectorCallback> callback)1308 RTCStatsCollector::RequestInfo::RequestInfo(
1309 rtc::scoped_refptr<RTCStatsCollectorCallback> callback)
1310 : RequestInfo(FilterMode::kAll, std::move(callback), nullptr, nullptr) {}
1311
RequestInfo(rtc::scoped_refptr<RtpSenderInternal> selector,rtc::scoped_refptr<RTCStatsCollectorCallback> callback)1312 RTCStatsCollector::RequestInfo::RequestInfo(
1313 rtc::scoped_refptr<RtpSenderInternal> selector,
1314 rtc::scoped_refptr<RTCStatsCollectorCallback> callback)
1315 : RequestInfo(FilterMode::kSenderSelector,
1316 std::move(callback),
1317 std::move(selector),
1318 nullptr) {}
1319
RequestInfo(rtc::scoped_refptr<RtpReceiverInternal> selector,rtc::scoped_refptr<RTCStatsCollectorCallback> callback)1320 RTCStatsCollector::RequestInfo::RequestInfo(
1321 rtc::scoped_refptr<RtpReceiverInternal> selector,
1322 rtc::scoped_refptr<RTCStatsCollectorCallback> callback)
1323 : RequestInfo(FilterMode::kReceiverSelector,
1324 std::move(callback),
1325 nullptr,
1326 std::move(selector)) {}
1327
RequestInfo(RTCStatsCollector::RequestInfo::FilterMode filter_mode,rtc::scoped_refptr<RTCStatsCollectorCallback> callback,rtc::scoped_refptr<RtpSenderInternal> sender_selector,rtc::scoped_refptr<RtpReceiverInternal> receiver_selector)1328 RTCStatsCollector::RequestInfo::RequestInfo(
1329 RTCStatsCollector::RequestInfo::FilterMode filter_mode,
1330 rtc::scoped_refptr<RTCStatsCollectorCallback> callback,
1331 rtc::scoped_refptr<RtpSenderInternal> sender_selector,
1332 rtc::scoped_refptr<RtpReceiverInternal> receiver_selector)
1333 : filter_mode_(filter_mode),
1334 callback_(std::move(callback)),
1335 sender_selector_(std::move(sender_selector)),
1336 receiver_selector_(std::move(receiver_selector)) {
1337 RTC_DCHECK(callback_);
1338 RTC_DCHECK(!sender_selector_ || !receiver_selector_);
1339 }
1340
Create(PeerConnectionInternal * pc,int64_t cache_lifetime_us)1341 rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create(
1342 PeerConnectionInternal* pc,
1343 int64_t cache_lifetime_us) {
1344 return rtc::make_ref_counted<RTCStatsCollector>(pc, cache_lifetime_us);
1345 }
1346
RTCStatsCollector(PeerConnectionInternal * pc,int64_t cache_lifetime_us)1347 RTCStatsCollector::RTCStatsCollector(PeerConnectionInternal* pc,
1348 int64_t cache_lifetime_us)
1349 : pc_(pc),
1350 signaling_thread_(pc->signaling_thread()),
1351 worker_thread_(pc->worker_thread()),
1352 network_thread_(pc->network_thread()),
1353 num_pending_partial_reports_(0),
1354 partial_report_timestamp_us_(0),
1355 network_report_event_(true /* manual_reset */,
1356 true /* initially_signaled */),
1357 cache_timestamp_us_(0),
1358 cache_lifetime_us_(cache_lifetime_us) {
1359 RTC_DCHECK(pc_);
1360 RTC_DCHECK(signaling_thread_);
1361 RTC_DCHECK(worker_thread_);
1362 RTC_DCHECK(network_thread_);
1363 RTC_DCHECK_GE(cache_lifetime_us_, 0);
1364 pc_->SignalSctpDataChannelCreated().connect(
1365 this, &RTCStatsCollector::OnSctpDataChannelCreated);
1366 }
1367
~RTCStatsCollector()1368 RTCStatsCollector::~RTCStatsCollector() {
1369 RTC_DCHECK_EQ(num_pending_partial_reports_, 0);
1370 }
1371
GetStatsReport(rtc::scoped_refptr<RTCStatsCollectorCallback> callback)1372 void RTCStatsCollector::GetStatsReport(
1373 rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {
1374 GetStatsReportInternal(RequestInfo(std::move(callback)));
1375 }
1376
GetStatsReport(rtc::scoped_refptr<RtpSenderInternal> selector,rtc::scoped_refptr<RTCStatsCollectorCallback> callback)1377 void RTCStatsCollector::GetStatsReport(
1378 rtc::scoped_refptr<RtpSenderInternal> selector,
1379 rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {
1380 GetStatsReportInternal(RequestInfo(std::move(selector), std::move(callback)));
1381 }
1382
GetStatsReport(rtc::scoped_refptr<RtpReceiverInternal> selector,rtc::scoped_refptr<RTCStatsCollectorCallback> callback)1383 void RTCStatsCollector::GetStatsReport(
1384 rtc::scoped_refptr<RtpReceiverInternal> selector,
1385 rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {
1386 GetStatsReportInternal(RequestInfo(std::move(selector), std::move(callback)));
1387 }
1388
GetStatsReportInternal(RTCStatsCollector::RequestInfo request)1389 void RTCStatsCollector::GetStatsReportInternal(
1390 RTCStatsCollector::RequestInfo request) {
1391 RTC_DCHECK_RUN_ON(signaling_thread_);
1392 requests_.push_back(std::move(request));
1393
1394 // "Now" using a monotonically increasing timer.
1395 int64_t cache_now_us = rtc::TimeMicros();
1396 if (cached_report_ &&
1397 cache_now_us - cache_timestamp_us_ <= cache_lifetime_us_) {
1398 // We have a fresh cached report to deliver. Deliver asynchronously, since
1399 // the caller may not be expecting a synchronous callback, and it avoids
1400 // reentrancy problems.
1401 signaling_thread_->PostTask(
1402 absl::bind_front(&RTCStatsCollector::DeliverCachedReport,
1403 rtc::scoped_refptr<RTCStatsCollector>(this),
1404 cached_report_, std::move(requests_)));
1405 } else if (!num_pending_partial_reports_) {
1406 // Only start gathering stats if we're not already gathering stats. In the
1407 // case of already gathering stats, `callback_` will be invoked when there
1408 // are no more pending partial reports.
1409
1410 // "Now" using a system clock, relative to the UNIX epoch (Jan 1, 1970,
1411 // UTC), in microseconds. The system clock could be modified and is not
1412 // necessarily monotonically increasing.
1413 int64_t timestamp_us = rtc::TimeUTCMicros();
1414
1415 num_pending_partial_reports_ = 2;
1416 partial_report_timestamp_us_ = cache_now_us;
1417
1418 // Prepare `transceiver_stats_infos_` and `call_stats_` for use in
1419 // `ProducePartialResultsOnNetworkThread` and
1420 // `ProducePartialResultsOnSignalingThread`.
1421 PrepareTransceiverStatsInfosAndCallStats_s_w_n();
1422 // Don't touch `network_report_` on the signaling thread until
1423 // ProducePartialResultsOnNetworkThread() has signaled the
1424 // `network_report_event_`.
1425 network_report_event_.Reset();
1426 rtc::scoped_refptr<RTCStatsCollector> collector(this);
1427 network_thread_->PostTask(
1428 [collector, sctp_transport_name = pc_->sctp_transport_name(),
1429 timestamp_us]() mutable {
1430 collector->ProducePartialResultsOnNetworkThread(
1431 timestamp_us, std::move(sctp_transport_name));
1432 });
1433 ProducePartialResultsOnSignalingThread(timestamp_us);
1434 }
1435 }
1436
ClearCachedStatsReport()1437 void RTCStatsCollector::ClearCachedStatsReport() {
1438 RTC_DCHECK_RUN_ON(signaling_thread_);
1439 cached_report_ = nullptr;
1440 MutexLock lock(&cached_certificates_mutex_);
1441 cached_certificates_by_transport_.clear();
1442 }
1443
WaitForPendingRequest()1444 void RTCStatsCollector::WaitForPendingRequest() {
1445 RTC_DCHECK_RUN_ON(signaling_thread_);
1446 // If a request is pending, blocks until the `network_report_event_` is
1447 // signaled and then delivers the result. Otherwise this is a NO-OP.
1448 MergeNetworkReport_s();
1449 }
1450
ProducePartialResultsOnSignalingThread(int64_t timestamp_us)1451 void RTCStatsCollector::ProducePartialResultsOnSignalingThread(
1452 int64_t timestamp_us) {
1453 RTC_DCHECK_RUN_ON(signaling_thread_);
1454 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1455
1456 partial_report_ = RTCStatsReport::Create(Timestamp::Micros(timestamp_us));
1457
1458 ProducePartialResultsOnSignalingThreadImpl(timestamp_us,
1459 partial_report_.get());
1460
1461 // ProducePartialResultsOnSignalingThread() is running synchronously on the
1462 // signaling thread, so it is always the first partial result delivered on the
1463 // signaling thread. The request is not complete until MergeNetworkReport_s()
1464 // happens; we don't have to do anything here.
1465 RTC_DCHECK_GT(num_pending_partial_reports_, 1);
1466 --num_pending_partial_reports_;
1467 }
1468
ProducePartialResultsOnSignalingThreadImpl(int64_t timestamp_us,RTCStatsReport * partial_report)1469 void RTCStatsCollector::ProducePartialResultsOnSignalingThreadImpl(
1470 int64_t timestamp_us,
1471 RTCStatsReport* partial_report) {
1472 RTC_DCHECK_RUN_ON(signaling_thread_);
1473 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1474
1475 ProduceDataChannelStats_s(timestamp_us, partial_report);
1476 ProduceMediaStreamStats_s(timestamp_us, partial_report);
1477 ProduceMediaStreamTrackStats_s(timestamp_us, partial_report);
1478 ProduceMediaSourceStats_s(timestamp_us, partial_report);
1479 ProducePeerConnectionStats_s(timestamp_us, partial_report);
1480 }
1481
ProducePartialResultsOnNetworkThread(int64_t timestamp_us,absl::optional<std::string> sctp_transport_name)1482 void RTCStatsCollector::ProducePartialResultsOnNetworkThread(
1483 int64_t timestamp_us,
1484 absl::optional<std::string> sctp_transport_name) {
1485 TRACE_EVENT0("webrtc",
1486 "RTCStatsCollector::ProducePartialResultsOnNetworkThread");
1487 RTC_DCHECK_RUN_ON(network_thread_);
1488 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1489
1490 // Touching `network_report_` on this thread is safe by this method because
1491 // `network_report_event_` is reset before this method is invoked.
1492 network_report_ = RTCStatsReport::Create(Timestamp::Micros(timestamp_us));
1493
1494 std::set<std::string> transport_names;
1495 if (sctp_transport_name) {
1496 transport_names.emplace(std::move(*sctp_transport_name));
1497 }
1498
1499 for (const auto& info : transceiver_stats_infos_) {
1500 if (info.transport_name)
1501 transport_names.insert(*info.transport_name);
1502 }
1503
1504 std::map<std::string, cricket::TransportStats> transport_stats_by_name =
1505 pc_->GetTransportStatsByNames(transport_names);
1506 std::map<std::string, CertificateStatsPair> transport_cert_stats =
1507 PrepareTransportCertificateStats_n(transport_stats_by_name);
1508
1509 ProducePartialResultsOnNetworkThreadImpl(
1510 timestamp_us, transport_stats_by_name, transport_cert_stats,
1511 network_report_.get());
1512
1513 // Signal that it is now safe to touch `network_report_` on the signaling
1514 // thread, and post a task to merge it into the final results.
1515 network_report_event_.Set();
1516 rtc::scoped_refptr<RTCStatsCollector> collector(this);
1517 signaling_thread_->PostTask(
1518 [collector] { collector->MergeNetworkReport_s(); });
1519 }
1520
ProducePartialResultsOnNetworkThreadImpl(int64_t timestamp_us,const std::map<std::string,cricket::TransportStats> & transport_stats_by_name,const std::map<std::string,CertificateStatsPair> & transport_cert_stats,RTCStatsReport * partial_report)1521 void RTCStatsCollector::ProducePartialResultsOnNetworkThreadImpl(
1522 int64_t timestamp_us,
1523 const std::map<std::string, cricket::TransportStats>&
1524 transport_stats_by_name,
1525 const std::map<std::string, CertificateStatsPair>& transport_cert_stats,
1526 RTCStatsReport* partial_report) {
1527 RTC_DCHECK_RUN_ON(network_thread_);
1528 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1529
1530 ProduceCertificateStats_n(timestamp_us, transport_cert_stats, partial_report);
1531 ProduceIceCandidateAndPairStats_n(timestamp_us, transport_stats_by_name,
1532 call_stats_, partial_report);
1533 ProduceTransportStats_n(timestamp_us, transport_stats_by_name,
1534 transport_cert_stats, partial_report);
1535 ProduceRTPStreamStats_n(timestamp_us, transceiver_stats_infos_,
1536 partial_report);
1537 }
1538
MergeNetworkReport_s()1539 void RTCStatsCollector::MergeNetworkReport_s() {
1540 RTC_DCHECK_RUN_ON(signaling_thread_);
1541 // The `network_report_event_` must be signaled for it to be safe to touch
1542 // `network_report_`. This is normally not blocking, but if
1543 // WaitForPendingRequest() is called while a request is pending, we might have
1544 // to wait until the network thread is done touching `network_report_`.
1545 network_report_event_.Wait(rtc::Event::kForever);
1546 if (!network_report_) {
1547 // Normally, MergeNetworkReport_s() is executed because it is posted from
1548 // the network thread. But if WaitForPendingRequest() is called while a
1549 // request is pending, an early call to MergeNetworkReport_s() is made,
1550 // merging the report and setting `network_report_` to null. If so, when the
1551 // previously posted MergeNetworkReport_s() is later executed, the report is
1552 // already null and nothing needs to be done here.
1553 return;
1554 }
1555 RTC_DCHECK_GT(num_pending_partial_reports_, 0);
1556 RTC_DCHECK(partial_report_);
1557 partial_report_->TakeMembersFrom(network_report_);
1558 network_report_ = nullptr;
1559 --num_pending_partial_reports_;
1560 // `network_report_` is currently the only partial report collected
1561 // asynchronously, so `num_pending_partial_reports_` must now be 0 and we are
1562 // ready to deliver the result.
1563 RTC_DCHECK_EQ(num_pending_partial_reports_, 0);
1564 cache_timestamp_us_ = partial_report_timestamp_us_;
1565 cached_report_ = partial_report_;
1566 partial_report_ = nullptr;
1567 transceiver_stats_infos_.clear();
1568 // Trace WebRTC Stats when getStats is called on Javascript.
1569 // This allows access to WebRTC stats from trace logs. To enable them,
1570 // select the "webrtc_stats" category when recording traces.
1571 TRACE_EVENT_INSTANT1("webrtc_stats", "webrtc_stats", "report",
1572 cached_report_->ToJson());
1573
1574 // Deliver report and clear `requests_`.
1575 std::vector<RequestInfo> requests;
1576 requests.swap(requests_);
1577 DeliverCachedReport(cached_report_, std::move(requests));
1578 }
1579
DeliverCachedReport(rtc::scoped_refptr<const RTCStatsReport> cached_report,std::vector<RTCStatsCollector::RequestInfo> requests)1580 void RTCStatsCollector::DeliverCachedReport(
1581 rtc::scoped_refptr<const RTCStatsReport> cached_report,
1582 std::vector<RTCStatsCollector::RequestInfo> requests) {
1583 RTC_DCHECK_RUN_ON(signaling_thread_);
1584 RTC_DCHECK(!requests.empty());
1585 RTC_DCHECK(cached_report);
1586
1587 for (const RequestInfo& request : requests) {
1588 if (request.filter_mode() == RequestInfo::FilterMode::kAll) {
1589 request.callback()->OnStatsDelivered(cached_report);
1590 } else {
1591 bool filter_by_sender_selector;
1592 rtc::scoped_refptr<RtpSenderInternal> sender_selector;
1593 rtc::scoped_refptr<RtpReceiverInternal> receiver_selector;
1594 if (request.filter_mode() == RequestInfo::FilterMode::kSenderSelector) {
1595 filter_by_sender_selector = true;
1596 sender_selector = request.sender_selector();
1597 } else {
1598 RTC_DCHECK(request.filter_mode() ==
1599 RequestInfo::FilterMode::kReceiverSelector);
1600 filter_by_sender_selector = false;
1601 receiver_selector = request.receiver_selector();
1602 }
1603 request.callback()->OnStatsDelivered(CreateReportFilteredBySelector(
1604 filter_by_sender_selector, cached_report, sender_selector,
1605 receiver_selector));
1606 }
1607 }
1608 }
1609
ProduceCertificateStats_n(int64_t timestamp_us,const std::map<std::string,CertificateStatsPair> & transport_cert_stats,RTCStatsReport * report) const1610 void RTCStatsCollector::ProduceCertificateStats_n(
1611 int64_t timestamp_us,
1612 const std::map<std::string, CertificateStatsPair>& transport_cert_stats,
1613 RTCStatsReport* report) const {
1614 RTC_DCHECK_RUN_ON(network_thread_);
1615 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1616
1617 for (const auto& transport_cert_stats_pair : transport_cert_stats) {
1618 if (transport_cert_stats_pair.second.local) {
1619 ProduceCertificateStatsFromSSLCertificateStats(
1620 timestamp_us, *transport_cert_stats_pair.second.local.get(), report);
1621 }
1622 if (transport_cert_stats_pair.second.remote) {
1623 ProduceCertificateStatsFromSSLCertificateStats(
1624 timestamp_us, *transport_cert_stats_pair.second.remote.get(), report);
1625 }
1626 }
1627 }
1628
ProduceDataChannelStats_s(int64_t timestamp_us,RTCStatsReport * report) const1629 void RTCStatsCollector::ProduceDataChannelStats_s(
1630 int64_t timestamp_us,
1631 RTCStatsReport* report) const {
1632 RTC_DCHECK_RUN_ON(signaling_thread_);
1633 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1634 std::vector<DataChannelStats> data_stats = pc_->GetDataChannelStats();
1635 for (const auto& stats : data_stats) {
1636 std::unique_ptr<RTCDataChannelStats> data_channel_stats(
1637 std::make_unique<RTCDataChannelStats>(
1638 "D" + rtc::ToString(stats.internal_id), timestamp_us));
1639 data_channel_stats->label = std::move(stats.label);
1640 data_channel_stats->protocol = std::move(stats.protocol);
1641 data_channel_stats->data_channel_identifier = stats.id;
1642 data_channel_stats->state = DataStateToRTCDataChannelState(stats.state);
1643 data_channel_stats->messages_sent = stats.messages_sent;
1644 data_channel_stats->bytes_sent = stats.bytes_sent;
1645 data_channel_stats->messages_received = stats.messages_received;
1646 data_channel_stats->bytes_received = stats.bytes_received;
1647 report->AddStats(std::move(data_channel_stats));
1648 }
1649 }
1650
ProduceIceCandidateAndPairStats_n(int64_t timestamp_us,const std::map<std::string,cricket::TransportStats> & transport_stats_by_name,const Call::Stats & call_stats,RTCStatsReport * report) const1651 void RTCStatsCollector::ProduceIceCandidateAndPairStats_n(
1652 int64_t timestamp_us,
1653 const std::map<std::string, cricket::TransportStats>&
1654 transport_stats_by_name,
1655 const Call::Stats& call_stats,
1656 RTCStatsReport* report) const {
1657 RTC_DCHECK_RUN_ON(network_thread_);
1658 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1659
1660 for (const auto& entry : transport_stats_by_name) {
1661 const std::string& transport_name = entry.first;
1662 const cricket::TransportStats& transport_stats = entry.second;
1663 for (const auto& channel_stats : transport_stats.channel_stats) {
1664 std::string transport_id = RTCTransportStatsIDFromTransportChannel(
1665 transport_name, channel_stats.component);
1666 for (const auto& info :
1667 channel_stats.ice_transport_stats.connection_infos) {
1668 std::unique_ptr<RTCIceCandidatePairStats> candidate_pair_stats(
1669 std::make_unique<RTCIceCandidatePairStats>(
1670 RTCIceCandidatePairStatsIDFromConnectionInfo(info),
1671 timestamp_us));
1672
1673 candidate_pair_stats->transport_id = transport_id;
1674 // TODO(hbos): There could be other candidates that are not paired with
1675 // anything. We don't have a complete list. Local candidates come from
1676 // Port objects, and prflx candidates (both local and remote) are only
1677 // stored in candidate pairs. https://crbug.com/632723
1678 candidate_pair_stats->local_candidate_id = ProduceIceCandidateStats(
1679 timestamp_us, info.local_candidate, true, transport_id, report);
1680 candidate_pair_stats->remote_candidate_id = ProduceIceCandidateStats(
1681 timestamp_us, info.remote_candidate, false, transport_id, report);
1682 candidate_pair_stats->state =
1683 IceCandidatePairStateToRTCStatsIceCandidatePairState(info.state);
1684 candidate_pair_stats->priority = info.priority;
1685 candidate_pair_stats->nominated = info.nominated;
1686 // TODO(hbos): This writable is different than the spec. It goes to
1687 // false after a certain amount of time without a response passes.
1688 // https://crbug.com/633550
1689 candidate_pair_stats->writable = info.writable;
1690 // Note that sent_total_packets includes discarded packets but
1691 // sent_total_bytes does not.
1692 candidate_pair_stats->packets_sent = static_cast<uint64_t>(
1693 info.sent_total_packets - info.sent_discarded_packets);
1694 candidate_pair_stats->packets_discarded_on_send =
1695 static_cast<uint64_t>(info.sent_discarded_packets);
1696 candidate_pair_stats->packets_received =
1697 static_cast<uint64_t>(info.packets_received);
1698 candidate_pair_stats->bytes_sent =
1699 static_cast<uint64_t>(info.sent_total_bytes);
1700 candidate_pair_stats->bytes_discarded_on_send =
1701 static_cast<uint64_t>(info.sent_discarded_bytes);
1702 candidate_pair_stats->bytes_received =
1703 static_cast<uint64_t>(info.recv_total_bytes);
1704 candidate_pair_stats->total_round_trip_time =
1705 static_cast<double>(info.total_round_trip_time_ms) /
1706 rtc::kNumMillisecsPerSec;
1707 if (info.current_round_trip_time_ms.has_value()) {
1708 candidate_pair_stats->current_round_trip_time =
1709 static_cast<double>(*info.current_round_trip_time_ms) /
1710 rtc::kNumMillisecsPerSec;
1711 }
1712 if (info.best_connection) {
1713 // The bandwidth estimations we have are for the selected candidate
1714 // pair ("info.best_connection").
1715 RTC_DCHECK_GE(call_stats.send_bandwidth_bps, 0);
1716 RTC_DCHECK_GE(call_stats.recv_bandwidth_bps, 0);
1717 if (call_stats.send_bandwidth_bps > 0) {
1718 candidate_pair_stats->available_outgoing_bitrate =
1719 static_cast<double>(call_stats.send_bandwidth_bps);
1720 }
1721 if (call_stats.recv_bandwidth_bps > 0) {
1722 candidate_pair_stats->available_incoming_bitrate =
1723 static_cast<double>(call_stats.recv_bandwidth_bps);
1724 }
1725 }
1726 candidate_pair_stats->requests_received =
1727 static_cast<uint64_t>(info.recv_ping_requests);
1728 candidate_pair_stats->requests_sent =
1729 static_cast<uint64_t>(info.sent_ping_requests_total);
1730 candidate_pair_stats->responses_received =
1731 static_cast<uint64_t>(info.recv_ping_responses);
1732 candidate_pair_stats->responses_sent =
1733 static_cast<uint64_t>(info.sent_ping_responses);
1734 RTC_DCHECK_GE(info.sent_ping_requests_total,
1735 info.sent_ping_requests_before_first_response);
1736 candidate_pair_stats->consent_requests_sent = static_cast<uint64_t>(
1737 info.sent_ping_requests_total -
1738 info.sent_ping_requests_before_first_response);
1739
1740 if (info.last_data_received.has_value()) {
1741 candidate_pair_stats->last_packet_received_timestamp =
1742 static_cast<double>(info.last_data_received->ms());
1743 }
1744 if (info.last_data_sent) {
1745 candidate_pair_stats->last_packet_sent_timestamp =
1746 static_cast<double>(info.last_data_sent->ms());
1747 }
1748
1749 report->AddStats(std::move(candidate_pair_stats));
1750 }
1751
1752 // Produce local candidate stats. If a transport exists these will already
1753 // have been produced.
1754 for (const auto& candidate_stats :
1755 channel_stats.ice_transport_stats.candidate_stats_list) {
1756 const auto& candidate = candidate_stats.candidate();
1757 ProduceIceCandidateStats(timestamp_us, candidate, true, transport_id,
1758 report);
1759 }
1760 }
1761 }
1762 }
1763
ProduceMediaStreamStats_s(int64_t timestamp_us,RTCStatsReport * report) const1764 void RTCStatsCollector::ProduceMediaStreamStats_s(
1765 int64_t timestamp_us,
1766 RTCStatsReport* report) const {
1767 RTC_DCHECK_RUN_ON(signaling_thread_);
1768 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1769
1770 std::map<std::string, std::vector<std::string>> track_ids;
1771
1772 for (const auto& stats : transceiver_stats_infos_) {
1773 for (const auto& sender : stats.transceiver->senders()) {
1774 std::string track_id =
1775 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
1776 kDirectionOutbound, sender->internal()->AttachmentId());
1777 for (auto& stream_id : sender->stream_ids()) {
1778 track_ids[stream_id].push_back(track_id);
1779 }
1780 }
1781 for (const auto& receiver : stats.transceiver->receivers()) {
1782 std::string track_id =
1783 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
1784 kDirectionInbound, receiver->internal()->AttachmentId());
1785 for (auto& stream : receiver->streams()) {
1786 track_ids[stream->id()].push_back(track_id);
1787 }
1788 }
1789 }
1790
1791 // Build stats for each stream ID known.
1792 for (auto& it : track_ids) {
1793 std::unique_ptr<DEPRECATED_RTCMediaStreamStats> stream_stats(
1794 std::make_unique<DEPRECATED_RTCMediaStreamStats>(
1795 "DEPRECATED_S" + it.first, timestamp_us));
1796 stream_stats->stream_identifier = it.first;
1797 stream_stats->track_ids = it.second;
1798 report->AddStats(std::move(stream_stats));
1799 }
1800 }
1801
ProduceMediaStreamTrackStats_s(int64_t timestamp_us,RTCStatsReport * report) const1802 void RTCStatsCollector::ProduceMediaStreamTrackStats_s(
1803 int64_t timestamp_us,
1804 RTCStatsReport* report) const {
1805 RTC_DCHECK_RUN_ON(signaling_thread_);
1806 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1807
1808 for (const RtpTransceiverStatsInfo& stats : transceiver_stats_infos_) {
1809 std::vector<rtc::scoped_refptr<RtpSenderInternal>> senders;
1810 for (const auto& sender : stats.transceiver->senders()) {
1811 senders.push_back(
1812 rtc::scoped_refptr<RtpSenderInternal>(sender->internal()));
1813 }
1814 ProduceSenderMediaTrackStats(timestamp_us, stats.track_media_info_map,
1815 senders, report);
1816
1817 std::vector<rtc::scoped_refptr<RtpReceiverInternal>> receivers;
1818 for (const auto& receiver : stats.transceiver->receivers()) {
1819 receivers.push_back(
1820 rtc::scoped_refptr<RtpReceiverInternal>(receiver->internal()));
1821 }
1822 ProduceReceiverMediaTrackStats(timestamp_us, stats.track_media_info_map,
1823 receivers, report);
1824 }
1825 }
1826
ProduceMediaSourceStats_s(int64_t timestamp_us,RTCStatsReport * report) const1827 void RTCStatsCollector::ProduceMediaSourceStats_s(
1828 int64_t timestamp_us,
1829 RTCStatsReport* report) const {
1830 RTC_DCHECK_RUN_ON(signaling_thread_);
1831 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1832
1833 for (const RtpTransceiverStatsInfo& transceiver_stats_info :
1834 transceiver_stats_infos_) {
1835 const auto& track_media_info_map =
1836 transceiver_stats_info.track_media_info_map;
1837 for (const auto& sender : transceiver_stats_info.transceiver->senders()) {
1838 const auto& sender_internal = sender->internal();
1839 const auto& track = sender_internal->track();
1840 if (!track)
1841 continue;
1842 // TODO(https://crbug.com/webrtc/10771): The same track could be attached
1843 // to multiple senders which should result in multiple senders referencing
1844 // the same media-source stats. When all media source related metrics are
1845 // moved to the track's source (e.g. input frame rate is moved from
1846 // cricket::VideoSenderInfo to VideoTrackSourceInterface::Stats and audio
1847 // levels are moved to the corresponding audio track/source object), don't
1848 // create separate media source stats objects on a per-attachment basis.
1849 std::unique_ptr<RTCMediaSourceStats> media_source_stats;
1850 if (track->kind() == MediaStreamTrackInterface::kAudioKind) {
1851 AudioTrackInterface* audio_track =
1852 static_cast<AudioTrackInterface*>(track.get());
1853 auto audio_source_stats = std::make_unique<RTCAudioSourceStats>(
1854 RTCMediaSourceStatsIDFromKindAndAttachment(
1855 cricket::MEDIA_TYPE_AUDIO, sender_internal->AttachmentId()),
1856 timestamp_us);
1857 // TODO(https://crbug.com/webrtc/10771): We shouldn't need to have an
1858 // SSRC assigned (there shouldn't need to exist a send-stream, created
1859 // by an O/A exchange) in order to read audio media-source stats.
1860 // TODO(https://crbug.com/webrtc/8694): SSRC 0 shouldn't be a magic
1861 // value indicating no SSRC.
1862 if (sender_internal->ssrc() != 0) {
1863 auto* voice_sender_info =
1864 track_media_info_map.GetVoiceSenderInfoBySsrc(
1865 sender_internal->ssrc());
1866 if (voice_sender_info) {
1867 audio_source_stats->audio_level = DoubleAudioLevelFromIntAudioLevel(
1868 voice_sender_info->audio_level);
1869 audio_source_stats->total_audio_energy =
1870 voice_sender_info->total_input_energy;
1871 audio_source_stats->total_samples_duration =
1872 voice_sender_info->total_input_duration;
1873 SetAudioProcessingStats(audio_source_stats.get(),
1874 voice_sender_info->apm_statistics);
1875 }
1876 }
1877 // Audio processor may be attached to either the track or the send
1878 // stream, so look in both places.
1879 auto audio_processor(audio_track->GetAudioProcessor());
1880 if (audio_processor.get()) {
1881 // The `has_remote_tracks` argument is obsolete; makes no difference
1882 // if it's set to true or false.
1883 AudioProcessorInterface::AudioProcessorStatistics ap_stats =
1884 audio_processor->GetStats(/*has_remote_tracks=*/false);
1885 SetAudioProcessingStats(audio_source_stats.get(),
1886 ap_stats.apm_statistics);
1887 }
1888 media_source_stats = std::move(audio_source_stats);
1889 } else {
1890 RTC_DCHECK_EQ(MediaStreamTrackInterface::kVideoKind, track->kind());
1891 auto video_source_stats = std::make_unique<RTCVideoSourceStats>(
1892 RTCMediaSourceStatsIDFromKindAndAttachment(
1893 cricket::MEDIA_TYPE_VIDEO, sender_internal->AttachmentId()),
1894 timestamp_us);
1895 auto* video_track = static_cast<VideoTrackInterface*>(track.get());
1896 auto* video_source = video_track->GetSource();
1897 VideoTrackSourceInterface::Stats source_stats;
1898 if (video_source && video_source->GetStats(&source_stats)) {
1899 video_source_stats->width = source_stats.input_width;
1900 video_source_stats->height = source_stats.input_height;
1901 }
1902 // TODO(https://crbug.com/webrtc/10771): We shouldn't need to have an
1903 // SSRC assigned (there shouldn't need to exist a send-stream, created
1904 // by an O/A exchange) in order to get framesPerSecond.
1905 // TODO(https://crbug.com/webrtc/8694): SSRC 0 shouldn't be a magic
1906 // value indicating no SSRC.
1907 if (sender_internal->ssrc() != 0) {
1908 auto* video_sender_info =
1909 track_media_info_map.GetVideoSenderInfoBySsrc(
1910 sender_internal->ssrc());
1911 if (video_sender_info) {
1912 video_source_stats->frames_per_second =
1913 video_sender_info->framerate_input;
1914 video_source_stats->frames = video_sender_info->frames;
1915 }
1916 }
1917 media_source_stats = std::move(video_source_stats);
1918 }
1919 media_source_stats->track_identifier = track->id();
1920 media_source_stats->kind = track->kind();
1921 report->AddStats(std::move(media_source_stats));
1922 }
1923 }
1924 }
1925
ProducePeerConnectionStats_s(int64_t timestamp_us,RTCStatsReport * report) const1926 void RTCStatsCollector::ProducePeerConnectionStats_s(
1927 int64_t timestamp_us,
1928 RTCStatsReport* report) const {
1929 RTC_DCHECK_RUN_ON(signaling_thread_);
1930 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1931
1932 std::unique_ptr<RTCPeerConnectionStats> stats(
1933 std::make_unique<RTCPeerConnectionStats>("P", timestamp_us));
1934 stats->data_channels_opened = internal_record_.data_channels_opened;
1935 stats->data_channels_closed = internal_record_.data_channels_closed;
1936 report->AddStats(std::move(stats));
1937 }
1938
ProduceRTPStreamStats_n(int64_t timestamp_us,const std::vector<RtpTransceiverStatsInfo> & transceiver_stats_infos,RTCStatsReport * report) const1939 void RTCStatsCollector::ProduceRTPStreamStats_n(
1940 int64_t timestamp_us,
1941 const std::vector<RtpTransceiverStatsInfo>& transceiver_stats_infos,
1942 RTCStatsReport* report) const {
1943 RTC_DCHECK_RUN_ON(network_thread_);
1944 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1945
1946 for (const RtpTransceiverStatsInfo& stats : transceiver_stats_infos) {
1947 if (stats.media_type == cricket::MEDIA_TYPE_AUDIO) {
1948 ProduceAudioRTPStreamStats_n(timestamp_us, stats, report);
1949 } else if (stats.media_type == cricket::MEDIA_TYPE_VIDEO) {
1950 ProduceVideoRTPStreamStats_n(timestamp_us, stats, report);
1951 } else {
1952 RTC_DCHECK_NOTREACHED();
1953 }
1954 }
1955 }
1956
ProduceAudioRTPStreamStats_n(int64_t timestamp_us,const RtpTransceiverStatsInfo & stats,RTCStatsReport * report) const1957 void RTCStatsCollector::ProduceAudioRTPStreamStats_n(
1958 int64_t timestamp_us,
1959 const RtpTransceiverStatsInfo& stats,
1960 RTCStatsReport* report) const {
1961 RTC_DCHECK_RUN_ON(network_thread_);
1962 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1963
1964 if (!stats.mid || !stats.transport_name) {
1965 return;
1966 }
1967 RTC_DCHECK(stats.track_media_info_map.voice_media_info().has_value());
1968 std::string mid = *stats.mid;
1969 std::string transport_id = RTCTransportStatsIDFromTransportChannel(
1970 *stats.transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
1971 // Inbound and remote-outbound.
1972 // The remote-outbound stats are based on RTCP sender reports sent from the
1973 // remote endpoint providing metrics about the remote outbound streams.
1974 for (const cricket::VoiceReceiverInfo& voice_receiver_info :
1975 stats.track_media_info_map.voice_media_info()->receivers) {
1976 if (!voice_receiver_info.connected())
1977 continue;
1978 // Inbound.
1979 auto inbound_audio = CreateInboundAudioStreamStats(
1980 stats.track_media_info_map.voice_media_info().value(),
1981 voice_receiver_info, transport_id, mid, timestamp_us, report);
1982 // TODO(hta): This lookup should look for the sender, not the track.
1983 rtc::scoped_refptr<AudioTrackInterface> audio_track =
1984 stats.track_media_info_map.GetAudioTrack(voice_receiver_info);
1985 if (audio_track) {
1986 inbound_audio->track_id =
1987 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
1988 kDirectionInbound, stats.track_media_info_map
1989 .GetAttachmentIdByTrack(audio_track.get())
1990 .value());
1991 inbound_audio->track_identifier = audio_track->id();
1992 }
1993 auto* inbound_audio_ptr = report->TryAddStats(std::move(inbound_audio));
1994 if (!inbound_audio_ptr) {
1995 RTC_LOG(LS_ERROR)
1996 << "Unable to add audio 'inbound-rtp' to report, ID is not unique.";
1997 continue;
1998 }
1999 // Remote-outbound.
2000 auto remote_outbound_audio = CreateRemoteOutboundAudioStreamStats(
2001 voice_receiver_info, mid, *inbound_audio_ptr, transport_id);
2002 // Add stats.
2003 if (remote_outbound_audio) {
2004 // When the remote outbound stats are available, the remote ID for the
2005 // local inbound stats is set.
2006 auto* remote_outbound_audio_ptr =
2007 report->TryAddStats(std::move(remote_outbound_audio));
2008 if (remote_outbound_audio_ptr) {
2009 inbound_audio_ptr->remote_id = remote_outbound_audio_ptr->id();
2010 } else {
2011 RTC_LOG(LS_ERROR) << "Unable to add audio 'remote-outbound-rtp' to "
2012 << "report, ID is not unique.";
2013 }
2014 }
2015 }
2016 // Outbound.
2017 std::map<std::string, RTCOutboundRTPStreamStats*> audio_outbound_rtps;
2018 for (const cricket::VoiceSenderInfo& voice_sender_info :
2019 stats.track_media_info_map.voice_media_info()->senders) {
2020 if (!voice_sender_info.connected())
2021 continue;
2022 auto outbound_audio = std::make_unique<RTCOutboundRTPStreamStats>(
2023 RTCOutboundRTPStreamStatsIDFromSSRC(
2024 transport_id, cricket::MEDIA_TYPE_AUDIO, voice_sender_info.ssrc()),
2025 timestamp_us);
2026 SetOutboundRTPStreamStatsFromVoiceSenderInfo(
2027 transport_id, mid,
2028 stats.track_media_info_map.voice_media_info().value(),
2029 voice_sender_info, outbound_audio.get(), report);
2030 rtc::scoped_refptr<AudioTrackInterface> audio_track =
2031 stats.track_media_info_map.GetAudioTrack(voice_sender_info);
2032 if (audio_track) {
2033 int attachment_id =
2034 stats.track_media_info_map.GetAttachmentIdByTrack(audio_track.get())
2035 .value();
2036 outbound_audio->track_id =
2037 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
2038 kDirectionOutbound, attachment_id);
2039 outbound_audio->media_source_id =
2040 RTCMediaSourceStatsIDFromKindAndAttachment(cricket::MEDIA_TYPE_AUDIO,
2041 attachment_id);
2042 }
2043 auto audio_outbound_pair =
2044 std::make_pair(outbound_audio->id(), outbound_audio.get());
2045 if (report->TryAddStats(std::move(outbound_audio))) {
2046 audio_outbound_rtps.insert(std::move(audio_outbound_pair));
2047 } else {
2048 RTC_LOG(LS_ERROR)
2049 << "Unable to add audio 'outbound-rtp' to report, ID is not unique.";
2050 }
2051 }
2052 // Remote-inbound.
2053 // These are Report Block-based, information sent from the remote endpoint,
2054 // providing metrics about our Outbound streams. We take advantage of the fact
2055 // that RTCOutboundRtpStreamStats, RTCCodecStats and RTCTransport have already
2056 // been added to the report.
2057 for (const cricket::VoiceSenderInfo& voice_sender_info :
2058 stats.track_media_info_map.voice_media_info()->senders) {
2059 for (const auto& report_block_data : voice_sender_info.report_block_datas) {
2060 report->AddStats(ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
2061 transport_id, report_block_data, cricket::MEDIA_TYPE_AUDIO,
2062 audio_outbound_rtps, *report));
2063 }
2064 }
2065 }
2066
ProduceVideoRTPStreamStats_n(int64_t timestamp_us,const RtpTransceiverStatsInfo & stats,RTCStatsReport * report) const2067 void RTCStatsCollector::ProduceVideoRTPStreamStats_n(
2068 int64_t timestamp_us,
2069 const RtpTransceiverStatsInfo& stats,
2070 RTCStatsReport* report) const {
2071 RTC_DCHECK_RUN_ON(network_thread_);
2072 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
2073
2074 if (!stats.mid || !stats.transport_name) {
2075 return;
2076 }
2077 RTC_DCHECK(stats.track_media_info_map.video_media_info().has_value());
2078 std::string mid = *stats.mid;
2079 std::string transport_id = RTCTransportStatsIDFromTransportChannel(
2080 *stats.transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
2081 // Inbound
2082 for (const cricket::VideoReceiverInfo& video_receiver_info :
2083 stats.track_media_info_map.video_media_info()->receivers) {
2084 if (!video_receiver_info.connected())
2085 continue;
2086 auto inbound_video = std::make_unique<RTCInboundRTPStreamStats>(
2087 RTCInboundRTPStreamStatsIDFromSSRC(transport_id,
2088 cricket::MEDIA_TYPE_VIDEO,
2089 video_receiver_info.ssrc()),
2090 timestamp_us);
2091 SetInboundRTPStreamStatsFromVideoReceiverInfo(
2092 transport_id, mid,
2093 stats.track_media_info_map.video_media_info().value(),
2094 video_receiver_info, inbound_video.get(), report);
2095 rtc::scoped_refptr<VideoTrackInterface> video_track =
2096 stats.track_media_info_map.GetVideoTrack(video_receiver_info);
2097 if (video_track) {
2098 inbound_video->track_id =
2099 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
2100 kDirectionInbound, stats.track_media_info_map
2101 .GetAttachmentIdByTrack(video_track.get())
2102 .value());
2103 inbound_video->track_identifier = video_track->id();
2104 }
2105 if (!report->TryAddStats(std::move(inbound_video))) {
2106 RTC_LOG(LS_ERROR)
2107 << "Unable to add video 'inbound-rtp' to report, ID is not unique.";
2108 }
2109 }
2110 // Outbound
2111 std::map<std::string, RTCOutboundRTPStreamStats*> video_outbound_rtps;
2112 for (const cricket::VideoSenderInfo& video_sender_info :
2113 stats.track_media_info_map.video_media_info()->senders) {
2114 if (!video_sender_info.connected())
2115 continue;
2116 auto outbound_video = std::make_unique<RTCOutboundRTPStreamStats>(
2117 RTCOutboundRTPStreamStatsIDFromSSRC(
2118 transport_id, cricket::MEDIA_TYPE_VIDEO, video_sender_info.ssrc()),
2119 timestamp_us);
2120 SetOutboundRTPStreamStatsFromVideoSenderInfo(
2121 transport_id, mid,
2122 stats.track_media_info_map.video_media_info().value(),
2123 video_sender_info, outbound_video.get(), report);
2124 rtc::scoped_refptr<VideoTrackInterface> video_track =
2125 stats.track_media_info_map.GetVideoTrack(video_sender_info);
2126 if (video_track) {
2127 int attachment_id =
2128 stats.track_media_info_map.GetAttachmentIdByTrack(video_track.get())
2129 .value();
2130 outbound_video->track_id =
2131 DEPRECATED_RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
2132 kDirectionOutbound, attachment_id);
2133 outbound_video->media_source_id =
2134 RTCMediaSourceStatsIDFromKindAndAttachment(cricket::MEDIA_TYPE_VIDEO,
2135 attachment_id);
2136 }
2137 auto video_outbound_pair =
2138 std::make_pair(outbound_video->id(), outbound_video.get());
2139 if (report->TryAddStats(std::move(outbound_video))) {
2140 video_outbound_rtps.insert(std::move(video_outbound_pair));
2141 } else {
2142 RTC_LOG(LS_ERROR)
2143 << "Unable to add video 'outbound-rtp' to report, ID is not unique.";
2144 }
2145 }
2146 // Remote-inbound
2147 // These are Report Block-based, information sent from the remote endpoint,
2148 // providing metrics about our Outbound streams. We take advantage of the fact
2149 // that RTCOutboundRtpStreamStats, RTCCodecStats and RTCTransport have already
2150 // been added to the report.
2151 for (const cricket::VideoSenderInfo& video_sender_info :
2152 stats.track_media_info_map.video_media_info()->senders) {
2153 for (const auto& report_block_data : video_sender_info.report_block_datas) {
2154 report->AddStats(ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
2155 transport_id, report_block_data, cricket::MEDIA_TYPE_VIDEO,
2156 video_outbound_rtps, *report));
2157 }
2158 }
2159 }
2160
ProduceTransportStats_n(int64_t timestamp_us,const std::map<std::string,cricket::TransportStats> & transport_stats_by_name,const std::map<std::string,CertificateStatsPair> & transport_cert_stats,RTCStatsReport * report) const2161 void RTCStatsCollector::ProduceTransportStats_n(
2162 int64_t timestamp_us,
2163 const std::map<std::string, cricket::TransportStats>&
2164 transport_stats_by_name,
2165 const std::map<std::string, CertificateStatsPair>& transport_cert_stats,
2166 RTCStatsReport* report) const {
2167 RTC_DCHECK_RUN_ON(network_thread_);
2168 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
2169
2170 for (const auto& entry : transport_stats_by_name) {
2171 const std::string& transport_name = entry.first;
2172 const cricket::TransportStats& transport_stats = entry.second;
2173
2174 // Get reference to RTCP channel, if it exists.
2175 std::string rtcp_transport_stats_id;
2176 for (const cricket::TransportChannelStats& channel_stats :
2177 transport_stats.channel_stats) {
2178 if (channel_stats.component == cricket::ICE_CANDIDATE_COMPONENT_RTCP) {
2179 rtcp_transport_stats_id = RTCTransportStatsIDFromTransportChannel(
2180 transport_name, channel_stats.component);
2181 break;
2182 }
2183 }
2184
2185 // Get reference to local and remote certificates of this transport, if they
2186 // exist.
2187 const auto& certificate_stats_it =
2188 transport_cert_stats.find(transport_name);
2189 RTC_DCHECK(certificate_stats_it != transport_cert_stats.cend());
2190 std::string local_certificate_id;
2191 if (certificate_stats_it->second.local) {
2192 local_certificate_id = RTCCertificateIDFromFingerprint(
2193 certificate_stats_it->second.local->fingerprint);
2194 }
2195 std::string remote_certificate_id;
2196 if (certificate_stats_it->second.remote) {
2197 remote_certificate_id = RTCCertificateIDFromFingerprint(
2198 certificate_stats_it->second.remote->fingerprint);
2199 }
2200
2201 // There is one transport stats for each channel.
2202 for (const cricket::TransportChannelStats& channel_stats :
2203 transport_stats.channel_stats) {
2204 std::unique_ptr<RTCTransportStats> transport_stats(
2205 std::make_unique<RTCTransportStats>(
2206 RTCTransportStatsIDFromTransportChannel(transport_name,
2207 channel_stats.component),
2208 timestamp_us));
2209 transport_stats->packets_sent =
2210 channel_stats.ice_transport_stats.packets_sent;
2211 transport_stats->packets_received =
2212 channel_stats.ice_transport_stats.packets_received;
2213 transport_stats->bytes_sent =
2214 channel_stats.ice_transport_stats.bytes_sent;
2215 transport_stats->bytes_received =
2216 channel_stats.ice_transport_stats.bytes_received;
2217 transport_stats->dtls_state =
2218 DtlsTransportStateToRTCDtlsTransportState(channel_stats.dtls_state);
2219 transport_stats->selected_candidate_pair_changes =
2220 channel_stats.ice_transport_stats.selected_candidate_pair_changes;
2221 transport_stats->ice_role =
2222 IceRoleToRTCIceRole(channel_stats.ice_transport_stats.ice_role);
2223 transport_stats->ice_local_username_fragment =
2224 channel_stats.ice_transport_stats.ice_local_username_fragment;
2225 transport_stats->ice_state = IceTransportStateToRTCIceTransportState(
2226 channel_stats.ice_transport_stats.ice_state);
2227 for (const cricket::ConnectionInfo& info :
2228 channel_stats.ice_transport_stats.connection_infos) {
2229 if (info.best_connection) {
2230 transport_stats->selected_candidate_pair_id =
2231 RTCIceCandidatePairStatsIDFromConnectionInfo(info);
2232 }
2233 }
2234 if (channel_stats.component != cricket::ICE_CANDIDATE_COMPONENT_RTCP &&
2235 !rtcp_transport_stats_id.empty()) {
2236 transport_stats->rtcp_transport_stats_id = rtcp_transport_stats_id;
2237 }
2238 if (!local_certificate_id.empty())
2239 transport_stats->local_certificate_id = local_certificate_id;
2240 if (!remote_certificate_id.empty())
2241 transport_stats->remote_certificate_id = remote_certificate_id;
2242 // Crypto information
2243 if (channel_stats.ssl_version_bytes) {
2244 char bytes[5];
2245 snprintf(bytes, sizeof(bytes), "%04X", channel_stats.ssl_version_bytes);
2246 transport_stats->tls_version = bytes;
2247 }
2248
2249 if (channel_stats.dtls_role) {
2250 transport_stats->dtls_role = *channel_stats.dtls_role == rtc::SSL_CLIENT
2251 ? webrtc::RTCDtlsRole::kClient
2252 : webrtc::RTCDtlsRole::kServer;
2253 } else {
2254 transport_stats->dtls_role = webrtc::RTCDtlsRole::kUnknown;
2255 }
2256
2257 if (channel_stats.ssl_cipher_suite != rtc::kTlsNullWithNullNull &&
2258 rtc::SSLStreamAdapter::SslCipherSuiteToName(
2259 channel_stats.ssl_cipher_suite)
2260 .length()) {
2261 transport_stats->dtls_cipher =
2262 rtc::SSLStreamAdapter::SslCipherSuiteToName(
2263 channel_stats.ssl_cipher_suite);
2264 }
2265 if (channel_stats.srtp_crypto_suite != rtc::kSrtpInvalidCryptoSuite &&
2266 rtc::SrtpCryptoSuiteToName(channel_stats.srtp_crypto_suite)
2267 .length()) {
2268 transport_stats->srtp_cipher =
2269 rtc::SrtpCryptoSuiteToName(channel_stats.srtp_crypto_suite);
2270 }
2271 report->AddStats(std::move(transport_stats));
2272 }
2273 }
2274 }
2275
2276 std::map<std::string, RTCStatsCollector::CertificateStatsPair>
PrepareTransportCertificateStats_n(const std::map<std::string,cricket::TransportStats> & transport_stats_by_name)2277 RTCStatsCollector::PrepareTransportCertificateStats_n(
2278 const std::map<std::string, cricket::TransportStats>&
2279 transport_stats_by_name) {
2280 RTC_DCHECK_RUN_ON(network_thread_);
2281 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
2282
2283 std::map<std::string, CertificateStatsPair> transport_cert_stats;
2284 {
2285 MutexLock lock(&cached_certificates_mutex_);
2286 // Copy the certificate info from the cache, avoiding expensive
2287 // rtc::SSLCertChain::GetStats() calls.
2288 for (const auto& pair : cached_certificates_by_transport_) {
2289 transport_cert_stats.insert(
2290 std::make_pair(pair.first, pair.second.Copy()));
2291 }
2292 }
2293 if (transport_cert_stats.empty()) {
2294 // Collect certificate info.
2295 for (const auto& entry : transport_stats_by_name) {
2296 const std::string& transport_name = entry.first;
2297
2298 CertificateStatsPair certificate_stats_pair;
2299 rtc::scoped_refptr<rtc::RTCCertificate> local_certificate;
2300 if (pc_->GetLocalCertificate(transport_name, &local_certificate)) {
2301 certificate_stats_pair.local =
2302 local_certificate->GetSSLCertificateChain().GetStats();
2303 }
2304
2305 std::unique_ptr<rtc::SSLCertChain> remote_cert_chain =
2306 pc_->GetRemoteSSLCertChain(transport_name);
2307 if (remote_cert_chain) {
2308 certificate_stats_pair.remote = remote_cert_chain->GetStats();
2309 }
2310
2311 transport_cert_stats.insert(
2312 std::make_pair(transport_name, std::move(certificate_stats_pair)));
2313 }
2314 // Copy the result into the certificate cache for future reference.
2315 MutexLock lock(&cached_certificates_mutex_);
2316 for (const auto& pair : transport_cert_stats) {
2317 cached_certificates_by_transport_.insert(
2318 std::make_pair(pair.first, pair.second.Copy()));
2319 }
2320 }
2321 return transport_cert_stats;
2322 }
2323
PrepareTransceiverStatsInfosAndCallStats_s_w_n()2324 void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() {
2325 RTC_DCHECK_RUN_ON(signaling_thread_);
2326
2327 transceiver_stats_infos_.clear();
2328 // These are used to invoke GetStats for all the media channels together in
2329 // one worker thread hop.
2330 std::map<cricket::VoiceMediaChannel*, cricket::VoiceMediaInfo> voice_stats;
2331 std::map<cricket::VideoMediaChannel*, cricket::VideoMediaInfo> video_stats;
2332
2333 auto transceivers = pc_->GetTransceiversInternal();
2334
2335 // TODO(tommi): See if we can avoid synchronously blocking the signaling
2336 // thread while we do this (or avoid the BlockingCall at all).
2337 network_thread_->BlockingCall([this, &transceivers, &voice_stats,
2338 &video_stats] {
2339 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
2340
2341 for (const auto& transceiver_proxy : transceivers) {
2342 RtpTransceiver* transceiver = transceiver_proxy->internal();
2343 cricket::MediaType media_type = transceiver->media_type();
2344
2345 // Prepare stats entry. The TrackMediaInfoMap will be filled in after the
2346 // stats have been fetched on the worker thread.
2347 transceiver_stats_infos_.emplace_back();
2348 RtpTransceiverStatsInfo& stats = transceiver_stats_infos_.back();
2349 stats.transceiver = transceiver;
2350 stats.media_type = media_type;
2351
2352 cricket::ChannelInterface* channel = transceiver->channel();
2353 if (!channel) {
2354 // The remaining fields require a BaseChannel.
2355 continue;
2356 }
2357
2358 stats.mid = channel->mid();
2359 stats.transport_name = std::string(channel->transport_name());
2360
2361 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2362 cricket::VoiceMediaChannel* voice_channel =
2363 static_cast<cricket::VoiceMediaChannel*>(channel->media_channel());
2364 RTC_DCHECK(voice_stats.find(voice_channel) == voice_stats.end());
2365 voice_stats.insert(
2366 std::make_pair(voice_channel, cricket::VoiceMediaInfo()));
2367 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
2368 cricket::VideoMediaChannel* video_channel =
2369 static_cast<cricket::VideoMediaChannel*>(channel->media_channel());
2370 RTC_DCHECK(video_stats.find(video_channel) == video_stats.end());
2371 video_stats.insert(
2372 std::make_pair(video_channel, cricket::VideoMediaInfo()));
2373 } else {
2374 RTC_DCHECK_NOTREACHED();
2375 }
2376 }
2377 });
2378
2379 // We jump to the worker thread and call GetStats() on each media channel as
2380 // well as GetCallStats(). At the same time we construct the
2381 // TrackMediaInfoMaps, which also needs info from the worker thread. This
2382 // minimizes the number of thread jumps.
2383 worker_thread_->BlockingCall([&] {
2384 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
2385
2386 for (auto& pair : voice_stats) {
2387 if (!pair.first->GetStats(&pair.second,
2388 /*get_and_clear_legacy_stats=*/false)) {
2389 RTC_LOG(LS_WARNING) << "Failed to get voice stats.";
2390 }
2391 }
2392 for (auto& pair : video_stats) {
2393 if (!pair.first->GetStats(&pair.second)) {
2394 RTC_LOG(LS_WARNING) << "Failed to get video stats.";
2395 }
2396 }
2397
2398 // Create the TrackMediaInfoMap for each transceiver stats object.
2399 for (auto& stats : transceiver_stats_infos_) {
2400 auto transceiver = stats.transceiver;
2401 absl::optional<cricket::VoiceMediaInfo> voice_media_info;
2402 absl::optional<cricket::VideoMediaInfo> video_media_info;
2403 auto channel = transceiver->channel();
2404 if (channel) {
2405 cricket::MediaType media_type = transceiver->media_type();
2406 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2407 cricket::VoiceMediaChannel* voice_channel =
2408 static_cast<cricket::VoiceMediaChannel*>(
2409 channel->media_channel());
2410 RTC_DCHECK(voice_stats.find(voice_channel) != voice_stats.end());
2411 voice_media_info = std::move(voice_stats[voice_channel]);
2412 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
2413 cricket::VideoMediaChannel* video_channel =
2414 static_cast<cricket::VideoMediaChannel*>(
2415 channel->media_channel());
2416 RTC_DCHECK(video_stats.find(video_channel) != video_stats.end());
2417 video_media_info = std::move(video_stats[video_channel]);
2418 }
2419 }
2420 std::vector<rtc::scoped_refptr<RtpSenderInternal>> senders;
2421 for (const auto& sender : transceiver->senders()) {
2422 senders.push_back(
2423 rtc::scoped_refptr<RtpSenderInternal>(sender->internal()));
2424 }
2425 std::vector<rtc::scoped_refptr<RtpReceiverInternal>> receivers;
2426 for (const auto& receiver : transceiver->receivers()) {
2427 receivers.push_back(
2428 rtc::scoped_refptr<RtpReceiverInternal>(receiver->internal()));
2429 }
2430 stats.track_media_info_map.Initialize(std::move(voice_media_info),
2431 std::move(video_media_info),
2432 senders, receivers);
2433 }
2434
2435 call_stats_ = pc_->GetCallStats();
2436 });
2437 }
2438
OnSctpDataChannelCreated(SctpDataChannel * channel)2439 void RTCStatsCollector::OnSctpDataChannelCreated(SctpDataChannel* channel) {
2440 channel->SignalOpened.connect(this, &RTCStatsCollector::OnDataChannelOpened);
2441 channel->SignalClosed.connect(this, &RTCStatsCollector::OnDataChannelClosed);
2442 }
2443
OnDataChannelOpened(DataChannelInterface * channel)2444 void RTCStatsCollector::OnDataChannelOpened(DataChannelInterface* channel) {
2445 RTC_DCHECK_RUN_ON(signaling_thread_);
2446 bool result = internal_record_.opened_data_channels
2447 .insert(reinterpret_cast<uintptr_t>(channel))
2448 .second;
2449 ++internal_record_.data_channels_opened;
2450 RTC_DCHECK(result);
2451 }
2452
OnDataChannelClosed(DataChannelInterface * channel)2453 void RTCStatsCollector::OnDataChannelClosed(DataChannelInterface* channel) {
2454 RTC_DCHECK_RUN_ON(signaling_thread_);
2455 // Only channels that have been fully opened (and have increased the
2456 // `data_channels_opened_` counter) increase the closed counter.
2457 if (internal_record_.opened_data_channels.erase(
2458 reinterpret_cast<uintptr_t>(channel))) {
2459 ++internal_record_.data_channels_closed;
2460 }
2461 }
2462
CandidateTypeToRTCIceCandidateTypeForTesting(const std::string & type)2463 const char* CandidateTypeToRTCIceCandidateTypeForTesting(
2464 const std::string& type) {
2465 return CandidateTypeToRTCIceCandidateType(type);
2466 }
2467
DataStateToRTCDataChannelStateForTesting(DataChannelInterface::DataState state)2468 const char* DataStateToRTCDataChannelStateForTesting(
2469 DataChannelInterface::DataState state) {
2470 return DataStateToRTCDataChannelState(state);
2471 }
2472
2473 } // namespace webrtc
2474