1 /*
2 * Copyright 2012 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/legacy_stats_collector.h"
12
13 #include <stddef.h>
14 #include <stdint.h>
15
16 #include <algorithm>
17 #include <cmath>
18 #include <list>
19 #include <set>
20 #include <utility>
21 #include <vector>
22
23 #include "absl/strings/string_view.h"
24 #include "absl/types/optional.h"
25 #include "api/audio_codecs/audio_encoder.h"
26 #include "api/candidate.h"
27 #include "api/data_channel_interface.h"
28 #include "api/field_trials_view.h"
29 #include "api/media_types.h"
30 #include "api/rtp_sender_interface.h"
31 #include "api/scoped_refptr.h"
32 #include "api/sequence_checker.h"
33 #include "api/video/video_content_type.h"
34 #include "api/video/video_timing.h"
35 #include "call/call.h"
36 #include "media/base/media_channel.h"
37 #include "modules/audio_processing/include/audio_processing_statistics.h"
38 #include "p2p/base/ice_transport_internal.h"
39 #include "p2p/base/p2p_constants.h"
40 #include "pc/channel_interface.h"
41 #include "pc/data_channel_utils.h"
42 #include "pc/rtp_receiver.h"
43 #include "pc/rtp_receiver_proxy.h"
44 #include "pc/rtp_sender_proxy.h"
45 #include "pc/rtp_transceiver.h"
46 #include "pc/transport_stats.h"
47 #include "rtc_base/checks.h"
48 #include "rtc_base/ip_address.h"
49 #include "rtc_base/logging.h"
50 #include "rtc_base/rtc_certificate.h"
51 #include "rtc_base/socket_address.h"
52 #include "rtc_base/ssl_stream_adapter.h"
53 #include "rtc_base/string_encode.h"
54 #include "rtc_base/thread.h"
55 #include "rtc_base/time_utils.h"
56 #include "rtc_base/trace_event.h"
57
58 namespace webrtc {
59 namespace {
60
61 // Field trial which controls whether to report standard-compliant bytes
62 // sent/received per stream. If enabled, padding and headers are not included
63 // in bytes sent or received.
64 constexpr char kUseStandardBytesStats[] = "WebRTC-UseStandardBytesStats";
65
66 // The following is the enum RTCStatsIceCandidateType from
67 // http://w3c.github.io/webrtc-stats/#rtcstatsicecandidatetype-enum such that
68 // our stats report for ice candidate type could conform to that.
69 const char STATSREPORT_LOCAL_PORT_TYPE[] = "host";
70 const char STATSREPORT_STUN_PORT_TYPE[] = "serverreflexive";
71 const char STATSREPORT_PRFLX_PORT_TYPE[] = "peerreflexive";
72 const char STATSREPORT_RELAY_PORT_TYPE[] = "relayed";
73
74 // Strings used by the stats collector to report adapter types. This fits the
75 // general stype of http://w3c.github.io/webrtc-stats than what
76 // AdapterTypeToString does.
77 const char* STATSREPORT_ADAPTER_TYPE_ETHERNET = "lan";
78 const char* STATSREPORT_ADAPTER_TYPE_WIFI = "wlan";
79 const char* STATSREPORT_ADAPTER_TYPE_WWAN = "wwan";
80 const char* STATSREPORT_ADAPTER_TYPE_VPN = "vpn";
81 const char* STATSREPORT_ADAPTER_TYPE_LOOPBACK = "loopback";
82 const char* STATSREPORT_ADAPTER_TYPE_WILDCARD = "wildcard";
83
84 template <typename ValueType>
85 struct TypeForAdd {
86 const StatsReport::StatsValueName name;
87 const ValueType& value;
88 };
89
90 typedef TypeForAdd<bool> BoolForAdd;
91 typedef TypeForAdd<float> FloatForAdd;
92 typedef TypeForAdd<int64_t> Int64ForAdd;
93 typedef TypeForAdd<int> IntForAdd;
94
AddTrackReport(StatsCollection * reports,const std::string & track_id)95 StatsReport* AddTrackReport(StatsCollection* reports,
96 const std::string& track_id) {
97 // Adds an empty track report.
98 StatsReport::Id id(
99 StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack, track_id));
100 StatsReport* report = reports->ReplaceOrAddNew(id);
101 report->AddString(StatsReport::kStatsValueNameTrackId, track_id);
102 return report;
103 }
104
105 template <class Track>
CreateTrackReport(const Track * track,StatsCollection * reports,TrackIdMap * track_ids)106 void CreateTrackReport(const Track* track,
107 StatsCollection* reports,
108 TrackIdMap* track_ids) {
109 const std::string& track_id = track->id();
110 StatsReport* report = AddTrackReport(reports, track_id);
111 RTC_DCHECK(report != nullptr);
112 (*track_ids)[track_id] = report;
113 }
114
115 template <class TrackVector>
CreateTrackReports(const TrackVector & tracks,StatsCollection * reports,TrackIdMap * track_ids)116 void CreateTrackReports(const TrackVector& tracks,
117 StatsCollection* reports,
118 TrackIdMap* track_ids) {
119 for (const auto& track : tracks) {
120 CreateTrackReport(track.get(), reports, track_ids);
121 }
122 }
123
ExtractCommonSendProperties(const cricket::MediaSenderInfo & info,StatsReport * report,bool use_standard_bytes_stats)124 void ExtractCommonSendProperties(const cricket::MediaSenderInfo& info,
125 StatsReport* report,
126 bool use_standard_bytes_stats) {
127 report->AddString(StatsReport::kStatsValueNameCodecName, info.codec_name);
128 int64_t bytes_sent = info.payload_bytes_sent;
129 if (!use_standard_bytes_stats) {
130 bytes_sent += info.header_and_padding_bytes_sent;
131 }
132 report->AddInt64(StatsReport::kStatsValueNameBytesSent, bytes_sent);
133 if (info.rtt_ms >= 0) {
134 report->AddInt64(StatsReport::kStatsValueNameRtt, info.rtt_ms);
135 }
136 }
137
ExtractCommonReceiveProperties(const cricket::MediaReceiverInfo & info,StatsReport * report)138 void ExtractCommonReceiveProperties(const cricket::MediaReceiverInfo& info,
139 StatsReport* report) {
140 report->AddString(StatsReport::kStatsValueNameCodecName, info.codec_name);
141 }
142
SetAudioProcessingStats(StatsReport * report,const AudioProcessingStats & apm_stats)143 void SetAudioProcessingStats(StatsReport* report,
144 const AudioProcessingStats& apm_stats) {
145 if (apm_stats.delay_median_ms) {
146 report->AddInt(StatsReport::kStatsValueNameEchoDelayMedian,
147 *apm_stats.delay_median_ms);
148 }
149 if (apm_stats.delay_standard_deviation_ms) {
150 report->AddInt(StatsReport::kStatsValueNameEchoDelayStdDev,
151 *apm_stats.delay_standard_deviation_ms);
152 }
153 if (apm_stats.echo_return_loss) {
154 report->AddInt(StatsReport::kStatsValueNameEchoReturnLoss,
155 *apm_stats.echo_return_loss);
156 }
157 if (apm_stats.echo_return_loss_enhancement) {
158 report->AddInt(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
159 *apm_stats.echo_return_loss_enhancement);
160 }
161 if (apm_stats.residual_echo_likelihood) {
162 report->AddFloat(StatsReport::kStatsValueNameResidualEchoLikelihood,
163 static_cast<float>(*apm_stats.residual_echo_likelihood));
164 }
165 if (apm_stats.residual_echo_likelihood_recent_max) {
166 report->AddFloat(
167 StatsReport::kStatsValueNameResidualEchoLikelihoodRecentMax,
168 static_cast<float>(*apm_stats.residual_echo_likelihood_recent_max));
169 }
170 if (apm_stats.divergent_filter_fraction) {
171 report->AddFloat(StatsReport::kStatsValueNameAecDivergentFilterFraction,
172 static_cast<float>(*apm_stats.divergent_filter_fraction));
173 }
174 }
175
ExtractStats(const cricket::VoiceReceiverInfo & info,StatsReport * report,bool use_standard_bytes_stats)176 void ExtractStats(const cricket::VoiceReceiverInfo& info,
177 StatsReport* report,
178 bool use_standard_bytes_stats) {
179 ExtractCommonReceiveProperties(info, report);
180 const FloatForAdd floats[] = {
181 {StatsReport::kStatsValueNameExpandRate, info.expand_rate},
182 {StatsReport::kStatsValueNameSecondaryDecodedRate,
183 info.secondary_decoded_rate},
184 {StatsReport::kStatsValueNameSecondaryDiscardedRate,
185 info.secondary_discarded_rate},
186 {StatsReport::kStatsValueNameSpeechExpandRate, info.speech_expand_rate},
187 {StatsReport::kStatsValueNameAccelerateRate, info.accelerate_rate},
188 {StatsReport::kStatsValueNamePreemptiveExpandRate,
189 info.preemptive_expand_rate},
190 {StatsReport::kStatsValueNameTotalAudioEnergy,
191 static_cast<float>(info.total_output_energy)},
192 {StatsReport::kStatsValueNameTotalSamplesDuration,
193 static_cast<float>(info.total_output_duration)}};
194
195 const IntForAdd ints[] = {
196 {StatsReport::kStatsValueNameCurrentDelayMs, info.delay_estimate_ms},
197 {StatsReport::kStatsValueNameDecodingCNG, info.decoding_cng},
198 {StatsReport::kStatsValueNameDecodingCTN, info.decoding_calls_to_neteq},
199 {StatsReport::kStatsValueNameDecodingCTSG,
200 info.decoding_calls_to_silence_generator},
201 {StatsReport::kStatsValueNameDecodingMutedOutput,
202 info.decoding_muted_output},
203 {StatsReport::kStatsValueNameDecodingNormal, info.decoding_normal},
204 {StatsReport::kStatsValueNameDecodingPLC, info.decoding_plc},
205 {StatsReport::kStatsValueNameDecodingPLCCNG, info.decoding_plc_cng},
206 {StatsReport::kStatsValueNameJitterBufferMs, info.jitter_buffer_ms},
207 {StatsReport::kStatsValueNameJitterReceived, info.jitter_ms},
208 {StatsReport::kStatsValueNamePacketsLost, info.packets_lost},
209 {StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd},
210 {StatsReport::kStatsValueNamePreferredJitterBufferMs,
211 info.jitter_buffer_preferred_ms},
212 };
213
214 for (const auto& f : floats)
215 report->AddFloat(f.name, f.value);
216
217 for (const auto& i : ints)
218 report->AddInt(i.name, i.value);
219 if (info.audio_level >= 0) {
220 report->AddInt(StatsReport::kStatsValueNameAudioOutputLevel,
221 info.audio_level);
222 }
223 if (info.decoding_codec_plc)
224 report->AddInt(StatsReport::kStatsValueNameDecodingCodecPLC,
225 info.decoding_codec_plc);
226
227 int64_t bytes_rcvd = info.payload_bytes_rcvd;
228 if (!use_standard_bytes_stats) {
229 bytes_rcvd += info.header_and_padding_bytes_rcvd;
230 }
231 report->AddInt64(StatsReport::kStatsValueNameBytesReceived, bytes_rcvd);
232 if (info.capture_start_ntp_time_ms >= 0) {
233 report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
234 info.capture_start_ntp_time_ms);
235 }
236 report->AddString(StatsReport::kStatsValueNameMediaType, "audio");
237 }
238
ExtractStats(const cricket::VoiceSenderInfo & info,StatsReport * report,bool use_standard_bytes_stats)239 void ExtractStats(const cricket::VoiceSenderInfo& info,
240 StatsReport* report,
241 bool use_standard_bytes_stats) {
242 ExtractCommonSendProperties(info, report, use_standard_bytes_stats);
243
244 SetAudioProcessingStats(report, info.apm_statistics);
245
246 const FloatForAdd floats[] = {
247 {StatsReport::kStatsValueNameTotalAudioEnergy,
248 static_cast<float>(info.total_input_energy)},
249 {StatsReport::kStatsValueNameTotalSamplesDuration,
250 static_cast<float>(info.total_input_duration)}};
251
252 RTC_DCHECK_GE(info.audio_level, 0);
253 const IntForAdd ints[] = {
254 {StatsReport::kStatsValueNameAudioInputLevel, info.audio_level},
255 {StatsReport::kStatsValueNameJitterReceived, info.jitter_ms},
256 {StatsReport::kStatsValueNamePacketsLost, info.packets_lost},
257 {StatsReport::kStatsValueNamePacketsSent, info.packets_sent},
258 };
259
260 for (const auto& f : floats) {
261 report->AddFloat(f.name, f.value);
262 }
263
264 for (const auto& i : ints) {
265 if (i.value >= 0) {
266 report->AddInt(i.name, i.value);
267 }
268 }
269 report->AddString(StatsReport::kStatsValueNameMediaType, "audio");
270 if (info.ana_statistics.bitrate_action_counter) {
271 report->AddInt(StatsReport::kStatsValueNameAnaBitrateActionCounter,
272 *info.ana_statistics.bitrate_action_counter);
273 }
274 if (info.ana_statistics.channel_action_counter) {
275 report->AddInt(StatsReport::kStatsValueNameAnaChannelActionCounter,
276 *info.ana_statistics.channel_action_counter);
277 }
278 if (info.ana_statistics.dtx_action_counter) {
279 report->AddInt(StatsReport::kStatsValueNameAnaDtxActionCounter,
280 *info.ana_statistics.dtx_action_counter);
281 }
282 if (info.ana_statistics.fec_action_counter) {
283 report->AddInt(StatsReport::kStatsValueNameAnaFecActionCounter,
284 *info.ana_statistics.fec_action_counter);
285 }
286 if (info.ana_statistics.frame_length_increase_counter) {
287 report->AddInt(StatsReport::kStatsValueNameAnaFrameLengthIncreaseCounter,
288 *info.ana_statistics.frame_length_increase_counter);
289 }
290 if (info.ana_statistics.frame_length_decrease_counter) {
291 report->AddInt(StatsReport::kStatsValueNameAnaFrameLengthDecreaseCounter,
292 *info.ana_statistics.frame_length_decrease_counter);
293 }
294 if (info.ana_statistics.uplink_packet_loss_fraction) {
295 report->AddFloat(StatsReport::kStatsValueNameAnaUplinkPacketLossFraction,
296 *info.ana_statistics.uplink_packet_loss_fraction);
297 }
298 }
299
ExtractStats(const cricket::VideoReceiverInfo & info,StatsReport * report,bool use_standard_bytes_stats)300 void ExtractStats(const cricket::VideoReceiverInfo& info,
301 StatsReport* report,
302 bool use_standard_bytes_stats) {
303 ExtractCommonReceiveProperties(info, report);
304 report->AddString(StatsReport::kStatsValueNameCodecImplementationName,
305 info.decoder_implementation_name);
306 int64_t bytes_rcvd = info.payload_bytes_rcvd;
307 if (!use_standard_bytes_stats) {
308 bytes_rcvd += info.header_and_padding_bytes_rcvd;
309 }
310 report->AddInt64(StatsReport::kStatsValueNameBytesReceived, bytes_rcvd);
311 if (info.capture_start_ntp_time_ms >= 0) {
312 report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
313 info.capture_start_ntp_time_ms);
314 }
315 if (info.first_frame_received_to_decoded_ms >= 0) {
316 report->AddInt64(StatsReport::kStatsValueNameFirstFrameReceivedToDecodedMs,
317 info.first_frame_received_to_decoded_ms);
318 }
319 if (info.qp_sum)
320 report->AddInt64(StatsReport::kStatsValueNameQpSum, *info.qp_sum);
321
322 if (info.nacks_sent) {
323 report->AddInt(StatsReport::kStatsValueNameNacksSent, *info.nacks_sent);
324 }
325
326 const IntForAdd ints[] = {
327 {StatsReport::kStatsValueNameCurrentDelayMs, info.current_delay_ms},
328 {StatsReport::kStatsValueNameDecodeMs, info.decode_ms},
329 {StatsReport::kStatsValueNameFirsSent, info.firs_sent},
330 {StatsReport::kStatsValueNameFrameHeightReceived, info.frame_height},
331 {StatsReport::kStatsValueNameFrameRateDecoded, info.framerate_decoded},
332 {StatsReport::kStatsValueNameFrameRateOutput, info.framerate_output},
333 {StatsReport::kStatsValueNameFrameRateReceived, info.framerate_rcvd},
334 {StatsReport::kStatsValueNameFrameWidthReceived, info.frame_width},
335 {StatsReport::kStatsValueNameJitterBufferMs, info.jitter_buffer_ms},
336 {StatsReport::kStatsValueNameMaxDecodeMs, info.max_decode_ms},
337 {StatsReport::kStatsValueNameMinPlayoutDelayMs,
338 info.min_playout_delay_ms},
339 {StatsReport::kStatsValueNamePacketsLost, info.packets_lost},
340 {StatsReport::kStatsValueNamePacketsReceived, info.packets_rcvd},
341 {StatsReport::kStatsValueNamePlisSent, info.plis_sent},
342 {StatsReport::kStatsValueNameRenderDelayMs, info.render_delay_ms},
343 {StatsReport::kStatsValueNameTargetDelayMs, info.target_delay_ms},
344 {StatsReport::kStatsValueNameFramesDecoded,
345 static_cast<int>(info.frames_decoded)},
346 };
347
348 for (const auto& i : ints)
349 report->AddInt(i.name, i.value);
350 report->AddString(StatsReport::kStatsValueNameMediaType, "video");
351
352 if (info.timing_frame_info) {
353 report->AddString(StatsReport::kStatsValueNameTimingFrameInfo,
354 info.timing_frame_info->ToString());
355 }
356
357 report->AddInt64(StatsReport::kStatsValueNameInterframeDelayMaxMs,
358 info.interframe_delay_max_ms);
359
360 report->AddString(
361 StatsReport::kStatsValueNameContentType,
362 webrtc::videocontenttypehelpers::ToString(info.content_type));
363 }
364
ExtractStats(const cricket::VideoSenderInfo & info,StatsReport * report,bool use_standard_bytes_stats)365 void ExtractStats(const cricket::VideoSenderInfo& info,
366 StatsReport* report,
367 bool use_standard_bytes_stats) {
368 ExtractCommonSendProperties(info, report, use_standard_bytes_stats);
369
370 report->AddString(StatsReport::kStatsValueNameCodecImplementationName,
371 info.encoder_implementation_name);
372 report->AddBoolean(StatsReport::kStatsValueNameBandwidthLimitedResolution,
373 (info.adapt_reason & 0x2) > 0);
374 report->AddBoolean(StatsReport::kStatsValueNameCpuLimitedResolution,
375 (info.adapt_reason & 0x1) > 0);
376 report->AddBoolean(StatsReport::kStatsValueNameHasEnteredLowResolution,
377 info.has_entered_low_resolution);
378
379 if (info.qp_sum)
380 report->AddInt(StatsReport::kStatsValueNameQpSum, *info.qp_sum);
381
382 const IntForAdd ints[] = {
383 {StatsReport::kStatsValueNameAdaptationChanges, info.adapt_changes},
384 {StatsReport::kStatsValueNameAvgEncodeMs, info.avg_encode_ms},
385 {StatsReport::kStatsValueNameEncodeUsagePercent,
386 info.encode_usage_percent},
387 {StatsReport::kStatsValueNameFirsReceived, info.firs_rcvd},
388 {StatsReport::kStatsValueNameFrameHeightSent, info.send_frame_height},
389 {StatsReport::kStatsValueNameFrameRateInput,
390 static_cast<int>(round(info.framerate_input))},
391 {StatsReport::kStatsValueNameFrameRateSent, info.framerate_sent},
392 {StatsReport::kStatsValueNameFrameWidthSent, info.send_frame_width},
393 {StatsReport::kStatsValueNameNacksReceived,
394 static_cast<int>(info.nacks_rcvd)},
395 {StatsReport::kStatsValueNamePacketsLost, info.packets_lost},
396 {StatsReport::kStatsValueNamePacketsSent, info.packets_sent},
397 {StatsReport::kStatsValueNamePlisReceived, info.plis_rcvd},
398 {StatsReport::kStatsValueNameFramesEncoded,
399 static_cast<int>(info.frames_encoded)},
400 {StatsReport::kStatsValueNameHugeFramesSent,
401 static_cast<int>(info.huge_frames_sent)},
402 };
403
404 for (const auto& i : ints)
405 report->AddInt(i.name, i.value);
406 report->AddString(StatsReport::kStatsValueNameMediaType, "video");
407 report->AddString(
408 StatsReport::kStatsValueNameContentType,
409 webrtc::videocontenttypehelpers::ToString(info.content_type));
410 }
411
ExtractStats(const cricket::BandwidthEstimationInfo & info,double stats_gathering_started,StatsReport * report)412 void ExtractStats(const cricket::BandwidthEstimationInfo& info,
413 double stats_gathering_started,
414 StatsReport* report) {
415 RTC_DCHECK(report->type() == StatsReport::kStatsReportTypeBwe);
416
417 report->set_timestamp(stats_gathering_started);
418 const IntForAdd ints[] = {
419 {StatsReport::kStatsValueNameAvailableSendBandwidth,
420 info.available_send_bandwidth},
421 {StatsReport::kStatsValueNameAvailableReceiveBandwidth,
422 info.available_recv_bandwidth},
423 {StatsReport::kStatsValueNameTargetEncBitrate, info.target_enc_bitrate},
424 {StatsReport::kStatsValueNameActualEncBitrate, info.actual_enc_bitrate},
425 {StatsReport::kStatsValueNameRetransmitBitrate, info.retransmit_bitrate},
426 {StatsReport::kStatsValueNameTransmitBitrate, info.transmit_bitrate},
427 };
428 for (const auto& i : ints)
429 report->AddInt(i.name, i.value);
430 report->AddInt64(StatsReport::kStatsValueNameBucketDelay, info.bucket_delay);
431 }
432
ExtractRemoteStats(const cricket::MediaSenderInfo & info,StatsReport * report)433 void ExtractRemoteStats(const cricket::MediaSenderInfo& info,
434 StatsReport* report) {
435 report->set_timestamp(info.remote_stats[0].timestamp);
436 // TODO(hta): Extract some stats here.
437 }
438
ExtractRemoteStats(const cricket::MediaReceiverInfo & info,StatsReport * report)439 void ExtractRemoteStats(const cricket::MediaReceiverInfo& info,
440 StatsReport* report) {
441 report->set_timestamp(info.remote_stats[0].timestamp);
442 // TODO(hta): Extract some stats here.
443 }
444
GetTrackIdBySsrc(uint32_t ssrc,StatsReport::Direction direction,const std::map<uint32_t,std::string> & track_id_by_ssrc)445 std::string GetTrackIdBySsrc(
446 uint32_t ssrc,
447 StatsReport::Direction direction,
448 const std::map<uint32_t, std::string>& track_id_by_ssrc) {
449 auto it = track_id_by_ssrc.find(ssrc);
450 if (it != track_id_by_ssrc.end()) {
451 return it->second;
452 }
453 if (direction == StatsReport::kReceive) {
454 // If the track ID was not found, this might be an unsignaled receive
455 // SSRC, so try looking up by the special SSRC 0.
456 it = track_id_by_ssrc.find(0);
457 if (it != track_id_by_ssrc.end()) {
458 RTC_LOG(LS_INFO) << "Assuming SSRC=" << ssrc
459 << " is an unsignalled receive stream corresponding "
460 "to the RtpReceiver with track ID \""
461 << it->second << "\".";
462 return it->second;
463 }
464 }
465 return "";
466 }
467
468 // Template to extract stats from a data vector.
469 // In order to use the template, the functions that are called from it,
470 // ExtractStats and ExtractRemoteStats, must be defined and overloaded
471 // for each type.
472 template <typename T>
ExtractStatsFromList(const std::vector<T> & data,const StatsReport::Id & transport_id,LegacyStatsCollector * collector,StatsReport::Direction direction,const std::map<uint32_t,std::string> & track_id_by_ssrc)473 void ExtractStatsFromList(
474 const std::vector<T>& data,
475 const StatsReport::Id& transport_id,
476 LegacyStatsCollector* collector,
477 StatsReport::Direction direction,
478 const std::map<uint32_t, std::string>& track_id_by_ssrc) {
479 for (const auto& d : data) {
480 uint32_t ssrc = d.ssrc();
481 std::string track_id = GetTrackIdBySsrc(ssrc, direction, track_id_by_ssrc);
482 // Each track can have stats for both local and remote objects.
483 // TODO(hta): Handle the case of multiple SSRCs per object.
484 StatsReport* report =
485 collector->PrepareReport(true, ssrc, track_id, transport_id, direction);
486 if (report)
487 ExtractStats(d, report, collector->UseStandardBytesStats());
488
489 if (!d.remote_stats.empty()) {
490 report = collector->PrepareReport(false, ssrc, track_id, transport_id,
491 direction);
492 if (report)
493 ExtractRemoteStats(d, report);
494 }
495 }
496 }
497
498 } // namespace
499
IceCandidateTypeToStatsType(const std::string & candidate_type)500 const char* IceCandidateTypeToStatsType(const std::string& candidate_type) {
501 if (candidate_type == cricket::LOCAL_PORT_TYPE) {
502 return STATSREPORT_LOCAL_PORT_TYPE;
503 }
504 if (candidate_type == cricket::STUN_PORT_TYPE) {
505 return STATSREPORT_STUN_PORT_TYPE;
506 }
507 if (candidate_type == cricket::PRFLX_PORT_TYPE) {
508 return STATSREPORT_PRFLX_PORT_TYPE;
509 }
510 if (candidate_type == cricket::RELAY_PORT_TYPE) {
511 return STATSREPORT_RELAY_PORT_TYPE;
512 }
513 RTC_DCHECK_NOTREACHED();
514 return "unknown";
515 }
516
AdapterTypeToStatsType(rtc::AdapterType type)517 const char* AdapterTypeToStatsType(rtc::AdapterType type) {
518 switch (type) {
519 case rtc::ADAPTER_TYPE_UNKNOWN:
520 return "unknown";
521 case rtc::ADAPTER_TYPE_ETHERNET:
522 return STATSREPORT_ADAPTER_TYPE_ETHERNET;
523 case rtc::ADAPTER_TYPE_WIFI:
524 return STATSREPORT_ADAPTER_TYPE_WIFI;
525 case rtc::ADAPTER_TYPE_CELLULAR:
526 case rtc::ADAPTER_TYPE_CELLULAR_2G:
527 case rtc::ADAPTER_TYPE_CELLULAR_3G:
528 case rtc::ADAPTER_TYPE_CELLULAR_4G:
529 case rtc::ADAPTER_TYPE_CELLULAR_5G:
530 return STATSREPORT_ADAPTER_TYPE_WWAN;
531 case rtc::ADAPTER_TYPE_VPN:
532 return STATSREPORT_ADAPTER_TYPE_VPN;
533 case rtc::ADAPTER_TYPE_LOOPBACK:
534 return STATSREPORT_ADAPTER_TYPE_LOOPBACK;
535 case rtc::ADAPTER_TYPE_ANY:
536 return STATSREPORT_ADAPTER_TYPE_WILDCARD;
537 default:
538 RTC_DCHECK_NOTREACHED();
539 return "";
540 }
541 }
542
LegacyStatsCollector(PeerConnectionInternal * pc)543 LegacyStatsCollector::LegacyStatsCollector(PeerConnectionInternal* pc)
544 : pc_(pc),
545 stats_gathering_started_(0),
546 use_standard_bytes_stats_(
547 pc->trials().IsEnabled(kUseStandardBytesStats)) {
548 RTC_DCHECK(pc_);
549 }
550
~LegacyStatsCollector()551 LegacyStatsCollector::~LegacyStatsCollector() {
552 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
553 }
554
555 // Wallclock time in ms.
GetTimeNow()556 double LegacyStatsCollector::GetTimeNow() {
557 return static_cast<double>(rtc::TimeUTCMillis());
558 }
559
560 // Adds a MediaStream with tracks that can be used as a `selector` in a call
561 // to GetStats.
AddStream(MediaStreamInterface * stream)562 void LegacyStatsCollector::AddStream(MediaStreamInterface* stream) {
563 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
564 RTC_DCHECK(stream != NULL);
565
566 CreateTrackReports<AudioTrackVector>(stream->GetAudioTracks(), &reports_,
567 &track_ids_);
568 CreateTrackReports<VideoTrackVector>(stream->GetVideoTracks(), &reports_,
569 &track_ids_);
570 }
571
AddTrack(MediaStreamTrackInterface * track)572 void LegacyStatsCollector::AddTrack(MediaStreamTrackInterface* track) {
573 if (track->kind() == MediaStreamTrackInterface::kAudioKind) {
574 CreateTrackReport(static_cast<AudioTrackInterface*>(track), &reports_,
575 &track_ids_);
576 } else if (track->kind() == MediaStreamTrackInterface::kVideoKind) {
577 CreateTrackReport(static_cast<VideoTrackInterface*>(track), &reports_,
578 &track_ids_);
579 } else {
580 RTC_DCHECK_NOTREACHED() << "Illegal track kind";
581 }
582 }
583
AddLocalAudioTrack(AudioTrackInterface * audio_track,uint32_t ssrc)584 void LegacyStatsCollector::AddLocalAudioTrack(AudioTrackInterface* audio_track,
585 uint32_t ssrc) {
586 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
587 RTC_DCHECK(audio_track != NULL);
588 #if RTC_DCHECK_IS_ON
589 for (const auto& track : local_audio_tracks_)
590 RTC_DCHECK(track.first != audio_track || track.second != ssrc);
591 #endif
592
593 local_audio_tracks_.push_back(std::make_pair(audio_track, ssrc));
594
595 // Create the kStatsReportTypeTrack report for the new track if there is no
596 // report yet.
597 StatsReport::Id id(StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack,
598 audio_track->id()));
599 StatsReport* report = reports_.Find(id);
600 if (!report) {
601 report = reports_.InsertNew(id);
602 report->AddString(StatsReport::kStatsValueNameTrackId, audio_track->id());
603 }
604 }
605
RemoveLocalAudioTrack(AudioTrackInterface * audio_track,uint32_t ssrc)606 void LegacyStatsCollector::RemoveLocalAudioTrack(
607 AudioTrackInterface* audio_track,
608 uint32_t ssrc) {
609 RTC_DCHECK(audio_track != NULL);
610 local_audio_tracks_.erase(
611 std::remove_if(
612 local_audio_tracks_.begin(), local_audio_tracks_.end(),
613 [audio_track, ssrc](const LocalAudioTrackVector::value_type& track) {
614 return track.first == audio_track && track.second == ssrc;
615 }),
616 local_audio_tracks_.end());
617 }
618
GetStats(MediaStreamTrackInterface * track,StatsReports * reports)619 void LegacyStatsCollector::GetStats(MediaStreamTrackInterface* track,
620 StatsReports* reports) {
621 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
622 RTC_DCHECK(reports != NULL);
623 RTC_DCHECK(reports->empty());
624
625 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
626
627 if (!track) {
628 reports->reserve(reports_.size());
629 for (auto* r : reports_)
630 reports->push_back(r);
631 return;
632 }
633
634 StatsReport* report = reports_.Find(StatsReport::NewTypedId(
635 StatsReport::kStatsReportTypeSession, pc_->session_id()));
636 if (report)
637 reports->push_back(report);
638
639 report = reports_.Find(
640 StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack, track->id()));
641
642 if (!report)
643 return;
644
645 reports->push_back(report);
646
647 std::string track_id;
648 for (const auto* r : reports_) {
649 if (r->type() != StatsReport::kStatsReportTypeSsrc)
650 continue;
651
652 const StatsReport::Value* v =
653 r->FindValue(StatsReport::kStatsValueNameTrackId);
654 if (v && v->string_val() == track->id())
655 reports->push_back(r);
656 }
657 }
658
UpdateStats(PeerConnectionInterface::StatsOutputLevel level)659 void LegacyStatsCollector::UpdateStats(
660 PeerConnectionInterface::StatsOutputLevel level) {
661 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
662 // Calls to UpdateStats() that occur less than kMinGatherStatsPeriodMs apart
663 // will be ignored. Using a monotonic clock specifically for this, while using
664 // a UTC clock for the reports themselves.
665 const int64_t kMinGatherStatsPeriodMs = 50;
666 int64_t cache_now_ms = rtc::TimeMillis();
667 if (cache_timestamp_ms_ != 0 &&
668 cache_timestamp_ms_ + kMinGatherStatsPeriodMs > cache_now_ms) {
669 return;
670 }
671 cache_timestamp_ms_ = cache_now_ms;
672 stats_gathering_started_ = GetTimeNow();
673
674 // TODO(tommi): ExtractSessionInfo now has a single hop to the network thread
675 // to fetch stats, then applies them on the signaling thread. See if we need
676 // to do this synchronously or if updating the stats without blocking is safe.
677 std::map<std::string, std::string> transport_names_by_mid =
678 ExtractSessionInfo();
679
680 // TODO(tommi): All of these hop over to the worker thread to fetch
681 // information. We could post a task to run all of these and post
682 // the information back to the signaling thread where we can create and
683 // update stats reports. That would also clean up the threading story a bit
684 // since we'd be creating/updating the stats report objects consistently on
685 // the same thread (this class has no locks right now).
686 ExtractBweInfo();
687 ExtractMediaInfo(transport_names_by_mid);
688 ExtractSenderInfo();
689 ExtractDataInfo();
690 UpdateTrackReports();
691 }
692
PrepareReport(bool local,uint32_t ssrc,const std::string & track_id,const StatsReport::Id & transport_id,StatsReport::Direction direction)693 StatsReport* LegacyStatsCollector::PrepareReport(
694 bool local,
695 uint32_t ssrc,
696 const std::string& track_id,
697 const StatsReport::Id& transport_id,
698 StatsReport::Direction direction) {
699 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
700 StatsReport::Id id(StatsReport::NewIdWithDirection(
701 local ? StatsReport::kStatsReportTypeSsrc
702 : StatsReport::kStatsReportTypeRemoteSsrc,
703 rtc::ToString(ssrc), direction));
704 StatsReport* report = reports_.Find(id);
705 if (!report) {
706 report = reports_.InsertNew(id);
707 }
708
709 // FYI - for remote reports, the timestamp will be overwritten later.
710 report->set_timestamp(stats_gathering_started_);
711
712 report->AddInt64(StatsReport::kStatsValueNameSsrc, ssrc);
713 if (!track_id.empty()) {
714 report->AddString(StatsReport::kStatsValueNameTrackId, track_id);
715 }
716 // Add the mapping of SSRC to transport.
717 report->AddId(StatsReport::kStatsValueNameTransportId, transport_id);
718 return report;
719 }
720
PrepareADMReport()721 StatsReport* LegacyStatsCollector::PrepareADMReport() {
722 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
723 StatsReport::Id id(StatsReport::NewTypedId(
724 StatsReport::kStatsReportTypeSession, pc_->session_id()));
725 StatsReport* report = reports_.FindOrAddNew(id);
726 return report;
727 }
728
IsValidTrack(const std::string & track_id)729 bool LegacyStatsCollector::IsValidTrack(const std::string& track_id) {
730 return reports_.Find(StatsReport::NewTypedId(
731 StatsReport::kStatsReportTypeTrack, track_id)) != nullptr;
732 }
733
AddCertificateReports(std::unique_ptr<rtc::SSLCertificateStats> cert_stats)734 StatsReport* LegacyStatsCollector::AddCertificateReports(
735 std::unique_ptr<rtc::SSLCertificateStats> cert_stats) {
736 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
737
738 StatsReport* first_report = nullptr;
739 StatsReport* prev_report = nullptr;
740 for (rtc::SSLCertificateStats* stats = cert_stats.get(); stats;
741 stats = stats->issuer.get()) {
742 StatsReport::Id id(StatsReport::NewTypedId(
743 StatsReport::kStatsReportTypeCertificate, stats->fingerprint));
744
745 StatsReport* report = reports_.ReplaceOrAddNew(id);
746 report->set_timestamp(stats_gathering_started_);
747 report->AddString(StatsReport::kStatsValueNameFingerprint,
748 stats->fingerprint);
749 report->AddString(StatsReport::kStatsValueNameFingerprintAlgorithm,
750 stats->fingerprint_algorithm);
751 report->AddString(StatsReport::kStatsValueNameDer,
752 stats->base64_certificate);
753 if (!first_report)
754 first_report = report;
755 else
756 prev_report->AddId(StatsReport::kStatsValueNameIssuerId, id);
757 prev_report = report;
758 }
759 return first_report;
760 }
761
AddConnectionInfoReport(const std::string & content_name,int component,int connection_id,const StatsReport::Id & channel_report_id,const cricket::ConnectionInfo & info)762 StatsReport* LegacyStatsCollector::AddConnectionInfoReport(
763 const std::string& content_name,
764 int component,
765 int connection_id,
766 const StatsReport::Id& channel_report_id,
767 const cricket::ConnectionInfo& info) {
768 StatsReport::Id id(
769 StatsReport::NewCandidatePairId(content_name, component, connection_id));
770 StatsReport* report = reports_.ReplaceOrAddNew(id);
771 report->set_timestamp(stats_gathering_started_);
772
773 const BoolForAdd bools[] = {
774 {StatsReport::kStatsValueNameActiveConnection, info.best_connection},
775 {StatsReport::kStatsValueNameReceiving, info.receiving},
776 {StatsReport::kStatsValueNameWritable, info.writable},
777 };
778 for (const auto& b : bools)
779 report->AddBoolean(b.name, b.value);
780
781 report->AddId(StatsReport::kStatsValueNameChannelId, channel_report_id);
782 cricket::CandidateStats local_candidate_stats(info.local_candidate);
783 cricket::CandidateStats remote_candidate_stats(info.remote_candidate);
784 report->AddId(StatsReport::kStatsValueNameLocalCandidateId,
785 AddCandidateReport(local_candidate_stats, true)->id());
786 report->AddId(StatsReport::kStatsValueNameRemoteCandidateId,
787 AddCandidateReport(remote_candidate_stats, false)->id());
788
789 const Int64ForAdd int64s[] = {
790 {StatsReport::kStatsValueNameBytesReceived,
791 static_cast<int64_t>(info.recv_total_bytes)},
792 {StatsReport::kStatsValueNameBytesSent,
793 static_cast<int64_t>(info.sent_total_bytes)},
794 {StatsReport::kStatsValueNamePacketsSent,
795 static_cast<int64_t>(info.sent_total_packets)},
796 {StatsReport::kStatsValueNameRtt, static_cast<int64_t>(info.rtt)},
797 {StatsReport::kStatsValueNameSendPacketsDiscarded,
798 static_cast<int64_t>(info.sent_discarded_packets)},
799 {StatsReport::kStatsValueNameSentPingRequestsTotal,
800 static_cast<int64_t>(info.sent_ping_requests_total)},
801 {StatsReport::kStatsValueNameSentPingRequestsBeforeFirstResponse,
802 static_cast<int64_t>(info.sent_ping_requests_before_first_response)},
803 {StatsReport::kStatsValueNameSentPingResponses,
804 static_cast<int64_t>(info.sent_ping_responses)},
805 {StatsReport::kStatsValueNameRecvPingRequests,
806 static_cast<int64_t>(info.recv_ping_requests)},
807 {StatsReport::kStatsValueNameRecvPingResponses,
808 static_cast<int64_t>(info.recv_ping_responses)},
809 };
810 for (const auto& i : int64s)
811 report->AddInt64(i.name, i.value);
812
813 report->AddString(StatsReport::kStatsValueNameLocalAddress,
814 info.local_candidate.address().ToString());
815 report->AddString(StatsReport::kStatsValueNameLocalCandidateType,
816 info.local_candidate.type());
817 report->AddString(StatsReport::kStatsValueNameRemoteAddress,
818 info.remote_candidate.address().ToString());
819 report->AddString(StatsReport::kStatsValueNameRemoteCandidateType,
820 info.remote_candidate.type());
821 report->AddString(StatsReport::kStatsValueNameTransportType,
822 info.local_candidate.protocol());
823 report->AddString(StatsReport::kStatsValueNameLocalCandidateRelayProtocol,
824 info.local_candidate.relay_protocol());
825
826 return report;
827 }
828
AddCandidateReport(const cricket::CandidateStats & candidate_stats,bool local)829 StatsReport* LegacyStatsCollector::AddCandidateReport(
830 const cricket::CandidateStats& candidate_stats,
831 bool local) {
832 const auto& candidate = candidate_stats.candidate();
833 StatsReport::Id id(StatsReport::NewCandidateId(local, candidate.id()));
834 StatsReport* report = reports_.Find(id);
835 if (!report) {
836 report = reports_.InsertNew(id);
837 report->set_timestamp(stats_gathering_started_);
838 if (local) {
839 report->AddString(StatsReport::kStatsValueNameCandidateNetworkType,
840 AdapterTypeToStatsType(candidate.network_type()));
841 }
842 report->AddString(StatsReport::kStatsValueNameCandidateIPAddress,
843 candidate.address().ipaddr().ToString());
844 report->AddString(StatsReport::kStatsValueNameCandidatePortNumber,
845 candidate.address().PortAsString());
846 report->AddInt(StatsReport::kStatsValueNameCandidatePriority,
847 candidate.priority());
848 report->AddString(StatsReport::kStatsValueNameCandidateType,
849 IceCandidateTypeToStatsType(candidate.type()));
850 report->AddString(StatsReport::kStatsValueNameCandidateTransportType,
851 candidate.protocol());
852 }
853 report->set_timestamp(stats_gathering_started_);
854
855 if (local && candidate_stats.stun_stats().has_value()) {
856 const auto& stun_stats = candidate_stats.stun_stats().value();
857 report->AddInt64(StatsReport::kStatsValueNameSentStunKeepaliveRequests,
858 stun_stats.stun_binding_requests_sent);
859 report->AddInt64(StatsReport::kStatsValueNameRecvStunKeepaliveResponses,
860 stun_stats.stun_binding_responses_received);
861 report->AddFloat(StatsReport::kStatsValueNameStunKeepaliveRttTotal,
862 stun_stats.stun_binding_rtt_ms_total);
863 report->AddFloat(StatsReport::kStatsValueNameStunKeepaliveRttSquaredTotal,
864 stun_stats.stun_binding_rtt_ms_squared_total);
865 }
866
867 return report;
868 }
869
ExtractSessionInfo()870 std::map<std::string, std::string> LegacyStatsCollector::ExtractSessionInfo() {
871 TRACE_EVENT0("webrtc", "LegacyStatsCollector::ExtractSessionInfo");
872 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
873
874 SessionStats stats;
875 auto transceivers = pc_->GetTransceiversInternal();
876 pc_->network_thread()->BlockingCall(
877 [&, sctp_transport_name = pc_->sctp_transport_name(),
878 sctp_mid = pc_->sctp_mid()]() mutable {
879 stats = ExtractSessionInfo_n(
880 transceivers, std::move(sctp_transport_name), std::move(sctp_mid));
881 });
882
883 ExtractSessionInfo_s(stats);
884
885 return std::move(stats.transport_names_by_mid);
886 }
887
ExtractSessionInfo_n(const std::vector<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>> & transceivers,absl::optional<std::string> sctp_transport_name,absl::optional<std::string> sctp_mid)888 LegacyStatsCollector::SessionStats LegacyStatsCollector::ExtractSessionInfo_n(
889 const std::vector<rtc::scoped_refptr<
890 RtpTransceiverProxyWithInternal<RtpTransceiver>>>& transceivers,
891 absl::optional<std::string> sctp_transport_name,
892 absl::optional<std::string> sctp_mid) {
893 TRACE_EVENT0("webrtc", "LegacyStatsCollector::ExtractSessionInfo_n");
894 RTC_DCHECK_RUN_ON(pc_->network_thread());
895 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
896 SessionStats stats;
897 stats.candidate_stats = pc_->GetPooledCandidateStats();
898 for (auto& transceiver : transceivers) {
899 cricket::ChannelInterface* channel = transceiver->internal()->channel();
900 if (channel) {
901 stats.transport_names_by_mid[channel->mid()] =
902 std::string(channel->transport_name());
903 }
904 }
905
906 if (sctp_transport_name) {
907 RTC_DCHECK(sctp_mid);
908 stats.transport_names_by_mid[*sctp_mid] = *sctp_transport_name;
909 }
910
911 std::set<std::string> transport_names;
912 for (const auto& entry : stats.transport_names_by_mid) {
913 transport_names.insert(entry.second);
914 }
915
916 std::map<std::string, cricket::TransportStats> transport_stats_by_name =
917 pc_->GetTransportStatsByNames(transport_names);
918
919 for (auto& entry : transport_stats_by_name) {
920 stats.transport_stats.emplace_back(entry.first, std::move(entry.second));
921 TransportStats& transport = stats.transport_stats.back();
922
923 // Attempt to get a copy of the certificates from the transport and
924 // expose them in stats reports. All channels in a transport share the
925 // same local and remote certificates.
926 //
927 StatsReport::Id local_cert_report_id, remote_cert_report_id;
928 rtc::scoped_refptr<rtc::RTCCertificate> certificate;
929 if (pc_->GetLocalCertificate(transport.name, &certificate)) {
930 transport.local_cert_stats =
931 certificate->GetSSLCertificateChain().GetStats();
932 }
933
934 std::unique_ptr<rtc::SSLCertChain> remote_cert_chain =
935 pc_->GetRemoteSSLCertChain(transport.name);
936 if (remote_cert_chain) {
937 transport.remote_cert_stats = remote_cert_chain->GetStats();
938 }
939 }
940
941 return stats;
942 }
943
ExtractSessionInfo_s(SessionStats & session_stats)944 void LegacyStatsCollector::ExtractSessionInfo_s(SessionStats& session_stats) {
945 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
946 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
947
948 StatsReport::Id id(StatsReport::NewTypedId(
949 StatsReport::kStatsReportTypeSession, pc_->session_id()));
950 StatsReport* report = reports_.ReplaceOrAddNew(id);
951 report->set_timestamp(stats_gathering_started_);
952 report->AddBoolean(StatsReport::kStatsValueNameInitiator,
953 pc_->initial_offerer());
954
955 for (const cricket::CandidateStats& stats : session_stats.candidate_stats) {
956 AddCandidateReport(stats, true);
957 }
958
959 for (auto& transport : session_stats.transport_stats) {
960 // Attempt to get a copy of the certificates from the transport and
961 // expose them in stats reports. All channels in a transport share the
962 // same local and remote certificates.
963 //
964 StatsReport::Id local_cert_report_id, remote_cert_report_id;
965 if (transport.local_cert_stats) {
966 StatsReport* r =
967 AddCertificateReports(std::move(transport.local_cert_stats));
968 if (r)
969 local_cert_report_id = r->id();
970 }
971
972 if (transport.remote_cert_stats) {
973 StatsReport* r =
974 AddCertificateReports(std::move(transport.remote_cert_stats));
975 if (r)
976 remote_cert_report_id = r->id();
977 }
978
979 for (const auto& channel_iter : transport.stats.channel_stats) {
980 StatsReport::Id channel_stats_id(
981 StatsReport::NewComponentId(transport.name, channel_iter.component));
982 StatsReport* channel_report = reports_.ReplaceOrAddNew(channel_stats_id);
983 channel_report->set_timestamp(stats_gathering_started_);
984 channel_report->AddInt(StatsReport::kStatsValueNameComponent,
985 channel_iter.component);
986 if (local_cert_report_id.get()) {
987 channel_report->AddId(StatsReport::kStatsValueNameLocalCertificateId,
988 local_cert_report_id);
989 }
990 if (remote_cert_report_id.get()) {
991 channel_report->AddId(StatsReport::kStatsValueNameRemoteCertificateId,
992 remote_cert_report_id);
993 }
994 int srtp_crypto_suite = channel_iter.srtp_crypto_suite;
995 if (srtp_crypto_suite != rtc::kSrtpInvalidCryptoSuite &&
996 rtc::SrtpCryptoSuiteToName(srtp_crypto_suite).length()) {
997 channel_report->AddString(
998 StatsReport::kStatsValueNameSrtpCipher,
999 rtc::SrtpCryptoSuiteToName(srtp_crypto_suite));
1000 }
1001 int ssl_cipher_suite = channel_iter.ssl_cipher_suite;
1002 if (ssl_cipher_suite != rtc::kTlsNullWithNullNull &&
1003 rtc::SSLStreamAdapter::SslCipherSuiteToName(ssl_cipher_suite)
1004 .length()) {
1005 channel_report->AddString(
1006 StatsReport::kStatsValueNameDtlsCipher,
1007 rtc::SSLStreamAdapter::SslCipherSuiteToName(ssl_cipher_suite));
1008 }
1009
1010 // Collect stats for non-pooled candidates. Note that the reports
1011 // generated here supersedes the candidate reports generated in
1012 // AddConnectionInfoReport below, and they may report candidates that are
1013 // not paired. Also, the candidate report generated in
1014 // AddConnectionInfoReport do not report port stats like StunStats.
1015 for (const cricket::CandidateStats& stats :
1016 channel_iter.ice_transport_stats.candidate_stats_list) {
1017 AddCandidateReport(stats, true);
1018 }
1019
1020 int connection_id = 0;
1021 for (const cricket::ConnectionInfo& info :
1022 channel_iter.ice_transport_stats.connection_infos) {
1023 StatsReport* connection_report = AddConnectionInfoReport(
1024 transport.name, channel_iter.component, connection_id++,
1025 channel_report->id(), info);
1026 if (info.best_connection) {
1027 channel_report->AddId(
1028 StatsReport::kStatsValueNameSelectedCandidatePairId,
1029 connection_report->id());
1030 }
1031 }
1032 }
1033 }
1034 }
1035
ExtractBweInfo()1036 void LegacyStatsCollector::ExtractBweInfo() {
1037 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
1038
1039 if (pc_->signaling_state() == PeerConnectionInterface::kClosed)
1040 return;
1041
1042 webrtc::Call::Stats call_stats = pc_->GetCallStats();
1043 cricket::BandwidthEstimationInfo bwe_info;
1044 bwe_info.available_send_bandwidth = call_stats.send_bandwidth_bps;
1045 bwe_info.available_recv_bandwidth = call_stats.recv_bandwidth_bps;
1046 bwe_info.bucket_delay = call_stats.pacer_delay_ms;
1047
1048 // Fill in target encoder bitrate, actual encoder bitrate, rtx bitrate, etc.
1049 // TODO(holmer): Also fill this in for audio.
1050 auto transceivers = pc_->GetTransceiversInternal();
1051 std::vector<cricket::VideoMediaChannel*> video_media_channels;
1052 for (const auto& transceiver : transceivers) {
1053 if (transceiver->media_type() != cricket::MEDIA_TYPE_VIDEO) {
1054 continue;
1055 }
1056 auto* video_channel = transceiver->internal()->channel();
1057 if (video_channel) {
1058 video_media_channels.push_back(static_cast<cricket::VideoMediaChannel*>(
1059 video_channel->media_channel()));
1060 }
1061 }
1062
1063 if (!video_media_channels.empty()) {
1064 pc_->worker_thread()->BlockingCall([&] {
1065 for (const auto& channel : video_media_channels) {
1066 channel->FillBitrateInfo(&bwe_info);
1067 }
1068 });
1069 }
1070
1071 StatsReport::Id report_id(StatsReport::NewBandwidthEstimationId());
1072 StatsReport* report = reports_.FindOrAddNew(report_id);
1073 ExtractStats(bwe_info, stats_gathering_started_, report);
1074 }
1075
1076 namespace {
1077
1078 class MediaChannelStatsGatherer {
1079 public:
1080 virtual ~MediaChannelStatsGatherer() = default;
1081
1082 virtual bool GetStatsOnWorkerThread() = 0;
1083
1084 virtual void ExtractStats(LegacyStatsCollector* collector) const = 0;
1085
1086 virtual bool HasRemoteAudio() const = 0;
1087
1088 std::string mid;
1089 std::string transport_name;
1090 std::map<uint32_t, std::string> sender_track_id_by_ssrc;
1091 std::map<uint32_t, std::string> receiver_track_id_by_ssrc;
1092
1093 protected:
1094 template <typename ReceiverT, typename SenderT>
ExtractSenderReceiverStats(LegacyStatsCollector * collector,const std::vector<ReceiverT> & receiver_data,const std::vector<SenderT> & sender_data) const1095 void ExtractSenderReceiverStats(
1096 LegacyStatsCollector* collector,
1097 const std::vector<ReceiverT>& receiver_data,
1098 const std::vector<SenderT>& sender_data) const {
1099 RTC_DCHECK(collector);
1100 StatsReport::Id transport_id = StatsReport::NewComponentId(
1101 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
1102 ExtractStatsFromList(receiver_data, transport_id, collector,
1103 StatsReport::kReceive, receiver_track_id_by_ssrc);
1104 ExtractStatsFromList(sender_data, transport_id, collector,
1105 StatsReport::kSend, sender_track_id_by_ssrc);
1106 }
1107 };
1108
1109 class VoiceMediaChannelStatsGatherer final : public MediaChannelStatsGatherer {
1110 public:
VoiceMediaChannelStatsGatherer(cricket::VoiceMediaChannel * voice_media_channel)1111 VoiceMediaChannelStatsGatherer(
1112 cricket::VoiceMediaChannel* voice_media_channel)
1113 : voice_media_channel_(voice_media_channel) {
1114 RTC_DCHECK(voice_media_channel_);
1115 }
1116
GetStatsOnWorkerThread()1117 bool GetStatsOnWorkerThread() override {
1118 return voice_media_channel_->GetStats(&voice_media_info,
1119 /*get_and_clear_legacy_stats=*/true);
1120 }
1121
ExtractStats(LegacyStatsCollector * collector) const1122 void ExtractStats(LegacyStatsCollector* collector) const override {
1123 ExtractSenderReceiverStats(collector, voice_media_info.receivers,
1124 voice_media_info.senders);
1125 if (voice_media_info.device_underrun_count == -2 ||
1126 voice_media_info.device_underrun_count > 0) {
1127 StatsReport* report = collector->PrepareADMReport();
1128 report->AddInt(StatsReport::kStatsValueNameAudioDeviceUnderrunCounter,
1129 voice_media_info.device_underrun_count);
1130 }
1131 }
1132
HasRemoteAudio() const1133 bool HasRemoteAudio() const override {
1134 return !voice_media_info.receivers.empty();
1135 }
1136
1137 private:
1138 cricket::VoiceMediaChannel* voice_media_channel_;
1139 cricket::VoiceMediaInfo voice_media_info;
1140 };
1141
1142 class VideoMediaChannelStatsGatherer final : public MediaChannelStatsGatherer {
1143 public:
VideoMediaChannelStatsGatherer(cricket::VideoMediaChannel * video_media_channel)1144 VideoMediaChannelStatsGatherer(
1145 cricket::VideoMediaChannel* video_media_channel)
1146 : video_media_channel_(video_media_channel) {
1147 RTC_DCHECK(video_media_channel_);
1148 }
1149
GetStatsOnWorkerThread()1150 bool GetStatsOnWorkerThread() override {
1151 return video_media_channel_->GetStats(&video_media_info);
1152 }
1153
ExtractStats(LegacyStatsCollector * collector) const1154 void ExtractStats(LegacyStatsCollector* collector) const override {
1155 ExtractSenderReceiverStats(collector, video_media_info.receivers,
1156 video_media_info.aggregated_senders);
1157 }
1158
HasRemoteAudio() const1159 bool HasRemoteAudio() const override { return false; }
1160
1161 private:
1162 cricket::VideoMediaChannel* video_media_channel_;
1163 cricket::VideoMediaInfo video_media_info;
1164 };
1165
CreateMediaChannelStatsGatherer(cricket::MediaChannel * channel)1166 std::unique_ptr<MediaChannelStatsGatherer> CreateMediaChannelStatsGatherer(
1167 cricket::MediaChannel* channel) {
1168 RTC_DCHECK(channel);
1169 if (channel->media_type() == cricket::MEDIA_TYPE_AUDIO) {
1170 return std::make_unique<VoiceMediaChannelStatsGatherer>(
1171 static_cast<cricket::VoiceMediaChannel*>(channel));
1172 } else {
1173 RTC_DCHECK_EQ(channel->media_type(), cricket::MEDIA_TYPE_VIDEO);
1174 return std::make_unique<VideoMediaChannelStatsGatherer>(
1175 static_cast<cricket::VideoMediaChannel*>(channel));
1176 }
1177 }
1178
1179 } // namespace
1180
ExtractMediaInfo(const std::map<std::string,std::string> & transport_names_by_mid)1181 void LegacyStatsCollector::ExtractMediaInfo(
1182 const std::map<std::string, std::string>& transport_names_by_mid) {
1183 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
1184
1185 std::vector<std::unique_ptr<MediaChannelStatsGatherer>> gatherers;
1186
1187 auto transceivers = pc_->GetTransceiversInternal();
1188 {
1189 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1190 for (const auto& transceiver : transceivers) {
1191 cricket::ChannelInterface* channel = transceiver->internal()->channel();
1192 if (!channel) {
1193 continue;
1194 }
1195 std::unique_ptr<MediaChannelStatsGatherer> gatherer =
1196 CreateMediaChannelStatsGatherer(channel->media_channel());
1197 gatherer->mid = channel->mid();
1198 gatherer->transport_name = transport_names_by_mid.at(gatherer->mid);
1199
1200 for (const auto& sender : transceiver->internal()->senders()) {
1201 auto track = sender->track();
1202 std::string track_id = (track ? track->id() : "");
1203 gatherer->sender_track_id_by_ssrc.insert(
1204 std::make_pair(sender->ssrc(), track_id));
1205 }
1206
1207 // Populating `receiver_track_id_by_ssrc` will be done on the worker
1208 // thread as the `ssrc` property of the receiver needs to be accessed
1209 // there.
1210
1211 gatherers.push_back(std::move(gatherer));
1212 }
1213 }
1214
1215 pc_->worker_thread()->BlockingCall([&] {
1216 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1217 // Populate `receiver_track_id_by_ssrc` for the gatherers.
1218 int i = 0;
1219 for (const auto& transceiver : transceivers) {
1220 cricket::ChannelInterface* channel = transceiver->internal()->channel();
1221 if (!channel)
1222 continue;
1223 MediaChannelStatsGatherer* gatherer = gatherers[i++].get();
1224 RTC_DCHECK_EQ(gatherer->mid, channel->mid());
1225
1226 for (const auto& receiver : transceiver->internal()->receivers()) {
1227 gatherer->receiver_track_id_by_ssrc.insert(std::make_pair(
1228 receiver->internal()->ssrc(), receiver->track()->id()));
1229 }
1230 }
1231
1232 for (auto it = gatherers.begin(); it != gatherers.end();
1233 /* incremented manually */) {
1234 MediaChannelStatsGatherer* gatherer = it->get();
1235 if (!gatherer->GetStatsOnWorkerThread()) {
1236 RTC_LOG(LS_ERROR) << "Failed to get media channel stats for mid="
1237 << gatherer->mid;
1238 it = gatherers.erase(it);
1239 continue;
1240 }
1241 ++it;
1242 }
1243 });
1244
1245 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1246
1247 bool has_remote_audio = false;
1248 for (const auto& gatherer : gatherers) {
1249 gatherer->ExtractStats(this);
1250 has_remote_audio |= gatherer->HasRemoteAudio();
1251 }
1252
1253 UpdateStatsFromExistingLocalAudioTracks(has_remote_audio);
1254 }
1255
ExtractSenderInfo()1256 void LegacyStatsCollector::ExtractSenderInfo() {
1257 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
1258
1259 for (const auto& sender : pc_->GetSenders()) {
1260 // TODO(bugs.webrtc.org/8694): SSRC == 0 currently means none. Delete check
1261 // when that is fixed.
1262 if (!sender->ssrc()) {
1263 continue;
1264 }
1265 const rtc::scoped_refptr<MediaStreamTrackInterface> track(sender->track());
1266 if (!track || track->kind() != MediaStreamTrackInterface::kVideoKind) {
1267 continue;
1268 }
1269 // Safe, because kind() == kVideoKind implies a subclass of
1270 // VideoTrackInterface; see mediastreaminterface.h.
1271 VideoTrackSourceInterface* source =
1272 static_cast<VideoTrackInterface*>(track.get())->GetSource();
1273
1274 VideoTrackSourceInterface::Stats stats;
1275 if (!source->GetStats(&stats)) {
1276 continue;
1277 }
1278 const StatsReport::Id stats_id = StatsReport::NewIdWithDirection(
1279 StatsReport::kStatsReportTypeSsrc, rtc::ToString(sender->ssrc()),
1280 StatsReport::kSend);
1281 StatsReport* report = reports_.FindOrAddNew(stats_id);
1282 report->AddInt(StatsReport::kStatsValueNameFrameWidthInput,
1283 stats.input_width);
1284 report->AddInt(StatsReport::kStatsValueNameFrameHeightInput,
1285 stats.input_height);
1286 }
1287 }
1288
ExtractDataInfo()1289 void LegacyStatsCollector::ExtractDataInfo() {
1290 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
1291
1292 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1293
1294 std::vector<DataChannelStats> data_stats = pc_->GetDataChannelStats();
1295 for (const auto& stats : data_stats) {
1296 StatsReport::Id id(StatsReport::NewTypedIntId(
1297 StatsReport::kStatsReportTypeDataChannel, stats.id));
1298 StatsReport* report = reports_.ReplaceOrAddNew(id);
1299 report->set_timestamp(stats_gathering_started_);
1300 report->AddString(StatsReport::kStatsValueNameLabel, stats.label);
1301 // Filter out the initial id (-1).
1302 if (stats.id >= 0) {
1303 report->AddInt(StatsReport::kStatsValueNameDataChannelId, stats.id);
1304 }
1305 report->AddString(StatsReport::kStatsValueNameProtocol, stats.protocol);
1306 report->AddString(StatsReport::kStatsValueNameState,
1307 DataChannelInterface::DataStateString(stats.state));
1308 }
1309 }
1310
GetReport(const StatsReport::StatsType & type,const std::string & id,StatsReport::Direction direction)1311 StatsReport* LegacyStatsCollector::GetReport(const StatsReport::StatsType& type,
1312 const std::string& id,
1313 StatsReport::Direction direction) {
1314 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
1315 RTC_DCHECK(type == StatsReport::kStatsReportTypeSsrc ||
1316 type == StatsReport::kStatsReportTypeRemoteSsrc);
1317 return reports_.Find(StatsReport::NewIdWithDirection(type, id, direction));
1318 }
1319
UpdateStatsFromExistingLocalAudioTracks(bool has_remote_tracks)1320 void LegacyStatsCollector::UpdateStatsFromExistingLocalAudioTracks(
1321 bool has_remote_tracks) {
1322 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
1323 // Loop through the existing local audio tracks.
1324 for (const auto& it : local_audio_tracks_) {
1325 AudioTrackInterface* track = it.first;
1326 uint32_t ssrc = it.second;
1327 StatsReport* report = GetReport(StatsReport::kStatsReportTypeSsrc,
1328 rtc::ToString(ssrc), StatsReport::kSend);
1329 if (report == NULL) {
1330 // This can happen if a local audio track is added to a stream on the
1331 // fly and the report has not been set up yet. Do nothing in this case.
1332 RTC_LOG(LS_ERROR) << "Stats report does not exist for ssrc " << ssrc;
1333 continue;
1334 }
1335
1336 // The same ssrc can be used by both local and remote audio tracks.
1337 const StatsReport::Value* v =
1338 report->FindValue(StatsReport::kStatsValueNameTrackId);
1339 if (!v || v->string_val() != track->id())
1340 continue;
1341
1342 report->set_timestamp(stats_gathering_started_);
1343 UpdateReportFromAudioTrack(track, report, has_remote_tracks);
1344 }
1345 }
1346
UpdateReportFromAudioTrack(AudioTrackInterface * track,StatsReport * report,bool has_remote_tracks)1347 void LegacyStatsCollector::UpdateReportFromAudioTrack(
1348 AudioTrackInterface* track,
1349 StatsReport* report,
1350 bool has_remote_tracks) {
1351 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
1352 RTC_DCHECK(track != NULL);
1353
1354 // Don't overwrite report values if they're not available.
1355 int signal_level;
1356 if (track->GetSignalLevel(&signal_level)) {
1357 RTC_DCHECK_GE(signal_level, 0);
1358 report->AddInt(StatsReport::kStatsValueNameAudioInputLevel, signal_level);
1359 }
1360
1361 auto audio_processor(track->GetAudioProcessor());
1362
1363 if (audio_processor.get()) {
1364 AudioProcessorInterface::AudioProcessorStatistics stats =
1365 audio_processor->GetStats(has_remote_tracks);
1366
1367 SetAudioProcessingStats(report, stats.apm_statistics);
1368 }
1369 }
1370
UpdateTrackReports()1371 void LegacyStatsCollector::UpdateTrackReports() {
1372 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
1373
1374 rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
1375
1376 for (const auto& entry : track_ids_) {
1377 StatsReport* report = entry.second;
1378 report->set_timestamp(stats_gathering_started_);
1379 }
1380 }
1381
InvalidateCache()1382 void LegacyStatsCollector::InvalidateCache() {
1383 RTC_DCHECK_RUN_ON(pc_->signaling_thread());
1384 cache_timestamp_ms_ = 0;
1385 }
1386
1387 } // namespace webrtc
1388