Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  rtc_stats_collector.cc   Sprache: C

 
/*
 *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */


#include "pc/rtc_stats_collector.h"

#include <stdint.h>
#include <stdio.h>

#include <cstdint>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include "absl/functional/bind_front.h"
#include "absl/strings/string_view.h"
#include "api/array_view.h"
#include "api/audio/audio_device.h"
#include "api/audio/audio_processing_statistics.h"
#include "api/candidate.h"
#include "api/dtls_transport_interface.h"
#include "api/media_stream_interface.h"
#include "api/media_types.h"
#include "api/rtp_parameters.h"
#include "api/sequence_checker.h"
#include "api/stats/rtc_stats.h"
#include "api/stats/rtcstats_objects.h"
#include "api/units/time_delta.h"
#include "api/video/video_content_type.h"
#include "api/video_codecs/scalability_mode.h"
#include "common_video/include/quality_limitation_reason.h"
#include "media/base/media_channel.h"
#include "media/base/media_channel_impl.h"
#include "modules/rtp_rtcp/include/report_block_data.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "p2p/base/connection_info.h"
#include "p2p/base/ice_transport_internal.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/port.h"
#include "pc/channel_interface.h"
#include "pc/data_channel_utils.h"
#include "pc/rtc_stats_traversal.h"
#include "pc/rtp_receiver_proxy.h"
#include "pc/rtp_sender_proxy.h"
#include "pc/webrtc_sdp.h"
#include "rtc_base/checks.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/logging.h"
#include "rtc_base/network_constants.h"
#include "rtc_base/rtc_certificate.h"
#include "rtc_base/socket_address.h"
#include "rtc_base/ssl_stream_adapter.h"
#include "rtc_base/string_encode.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/time_utils.h"
#include "rtc_base/trace_event.h"

namespace webrtc {

namespace {

const char kDirectionInbound = 'I';
const char kDirectionOutbound = 'O';

static constexpr char kAudioPlayoutSingletonId[] = "AP";

// TODO(https://crbug.com/webrtc/10656): Consider making IDs less predictable.
std::string RTCCertificateIDFromFingerprint(const std::string& fingerprint) {
  return "CF" + fingerprint;
}

// `direction` is either kDirectionInbound or kDirectionOutbound.
std::string RTCCodecStatsIDFromTransportAndCodecParameters(
    const char direction,
    const std::string& transport_id,
    const RtpCodecParameters& codec_params) {
  char buf[1024];
  rtc::SimpleStringBuilder sb(buf);
  sb << 'C' << direction << transport_id << '_' << codec_params.payload_type;
  // TODO(https://crbug.com/webrtc/14420): If we stop supporting different FMTP
  // lines for the same PT and transport, which should be illegal SDP, then we
  // wouldn't need `fmtp` to be part of the ID here.
  rtc::StringBuilder fmtp;
  if (WriteFmtpParameters(codec_params.parameters, &fmtp)) {
    sb << '_' << fmtp.Release();
  }
  return sb.str();
}

std::string RTCIceCandidatePairStatsIDFromConnectionInfo(
    const cricket::ConnectionInfo& info) {
  char buf[4096];
  rtc::SimpleStringBuilder sb(buf);
  sb << "CP" << info.local_candidate.id() << "_" << info.remote_candidate.id();
  return sb.str();
}

std::string RTCTransportStatsIDFromTransportChannel(
    const std::string& transport_name,
    int channel_component) {
  char buf[1024];
  rtc::SimpleStringBuilder sb(buf);
  sb << 'T' << transport_name << channel_component;
  return sb.str();
}

std::string RTCInboundRtpStreamStatsIDFromSSRC(const std::string& transport_id,
                                               cricket::MediaType media_type,
                                               uint32_t ssrc) {
  char buf[1024];
  rtc::SimpleStringBuilder sb(buf);
  sb << 'I' << transport_id
     << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V') << ssrc;
  return sb.str();
}

std::string RTCOutboundRtpStreamStatsIDFromSSRC(const std::string& transport_id,
                                                cricket::MediaType media_type,
                                                uint32_t ssrc) {
  char buf[1024];
  rtc::SimpleStringBuilder sb(buf);
  sb << 'O' << transport_id
     << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V') << ssrc;
  return sb.str();
}

std::string RTCRemoteInboundRtpStreamStatsIdFromSourceSsrc(
    cricket::MediaType media_type,
    uint32_t source_ssrc) {
  char buf[1024];
  rtc::SimpleStringBuilder sb(buf);
  sb << "RI" << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V')
     << source_ssrc;
  return sb.str();
}

std::string RTCRemoteOutboundRTPStreamStatsIDFromSSRC(
    cricket::MediaType media_type,
    uint32_t source_ssrc) {
  char buf[1024];
  rtc::SimpleStringBuilder sb(buf);
  sb << "RO" << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V')
     << source_ssrc;
  return sb.str();
}

std::string RTCMediaSourceStatsIDFromKindAndAttachment(
    cricket::MediaType media_type,
    int attachment_id) {
  char buf[1024];
  rtc::SimpleStringBuilder sb(buf);
  sb << 'S' << (media_type == cricket::MEDIA_TYPE_AUDIO ? 'A' : 'V')
     << attachment_id;
  return sb.str();
}

const char* DataStateToRTCDataChannelState(
    DataChannelInterface::DataState state) {
  switch (state) {
    case DataChannelInterface::kConnecting:
      return "connecting";
    case DataChannelInterface::kOpen:
      return "open";
    case DataChannelInterface::kClosing:
      return "closing";
    case DataChannelInterface::kClosed:
      return "closed";
    default:
      RTC_DCHECK_NOTREACHED();
      return nullptr;
  }
}

const char* IceCandidatePairStateToRTCStatsIceCandidatePairState(
    cricket::IceCandidatePairState state) {
  switch (state) {
    case cricket::IceCandidatePairState::WAITING:
      return "waiting";
    case cricket::IceCandidatePairState::IN_PROGRESS:
      return "in-progress";
    case cricket::IceCandidatePairState::SUCCEEDED:
      return "succeeded";
    case cricket::IceCandidatePairState::FAILED:
      return "failed";
    default:
      RTC_DCHECK_NOTREACHED();
      return nullptr;
  }
}

const char* IceRoleToRTCIceRole(cricket::IceRole role) {
  switch (role) {
    case cricket::IceRole::ICEROLE_UNKNOWN:
      return "unknown";
    case cricket::IceRole::ICEROLE_CONTROLLED:
      return "controlled";
    case cricket::IceRole::ICEROLE_CONTROLLING:
      return "controlling";
    default:
      RTC_DCHECK_NOTREACHED();
      return nullptr;
  }
}

const char* DtlsTransportStateToRTCDtlsTransportState(
    DtlsTransportState state) {
  switch (state) {
    case DtlsTransportState::kNew:
      return "new";
    case DtlsTransportState::kConnecting:
      return "connecting";
    case DtlsTransportState::kConnected:
      return "connected";
    case DtlsTransportState::kClosed:
      return "closed";
    case DtlsTransportState::kFailed:
      return "failed";
    default:
      RTC_CHECK_NOTREACHED();
      return nullptr;
  }
}

const char* IceTransportStateToRTCIceTransportState(IceTransportState state) {
  switch (state) {
    case IceTransportState::kNew:
      return "new";
    case IceTransportState::kChecking:
      return "checking";
    case IceTransportState::kConnected:
      return "connected";
    case IceTransportState::kCompleted:
      return "completed";
    case IceTransportState::kFailed:
      return "failed";
    case IceTransportState::kDisconnected:
      return "disconnected";
    case IceTransportState::kClosed:
      return "closed";
    default:
      RTC_CHECK_NOTREACHED();
      return nullptr;
  }
}

const char* NetworkTypeToStatsType(rtc::AdapterType type) {
  switch (type) {
    case rtc::ADAPTER_TYPE_CELLULAR:
    case rtc::ADAPTER_TYPE_CELLULAR_2G:
    case rtc::ADAPTER_TYPE_CELLULAR_3G:
    case rtc::ADAPTER_TYPE_CELLULAR_4G:
    case rtc::ADAPTER_TYPE_CELLULAR_5G:
      return "cellular";
    case rtc::ADAPTER_TYPE_ETHERNET:
      return "ethernet";
    case rtc::ADAPTER_TYPE_WIFI:
      return "wifi";
    case rtc::ADAPTER_TYPE_VPN:
      return "vpn";
    case rtc::ADAPTER_TYPE_UNKNOWN:
    case rtc::ADAPTER_TYPE_LOOPBACK:
    case rtc::ADAPTER_TYPE_ANY:
      return "unknown";
  }
  RTC_DCHECK_NOTREACHED();
  return nullptr;
}

absl::string_view NetworkTypeToStatsNetworkAdapterType(rtc::AdapterType type) {
  switch (type) {
    case rtc::ADAPTER_TYPE_CELLULAR:
      return "cellular";
    case rtc::ADAPTER_TYPE_CELLULAR_2G:
      return "cellular2g";
    case rtc::ADAPTER_TYPE_CELLULAR_3G:
      return "cellular3g";
    case rtc::ADAPTER_TYPE_CELLULAR_4G:
      return "cellular4g";
    case rtc::ADAPTER_TYPE_CELLULAR_5G:
      return "cellular5g";
    case rtc::ADAPTER_TYPE_ETHERNET:
      return "ethernet";
    case rtc::ADAPTER_TYPE_WIFI:
      return "wifi";
    case rtc::ADAPTER_TYPE_UNKNOWN:
      return "unknown";
    case rtc::ADAPTER_TYPE_LOOPBACK:
      return "loopback";
    case rtc::ADAPTER_TYPE_ANY:
      return "any";
    case rtc::ADAPTER_TYPE_VPN:
      /* should not be handled here. Vpn is modelled as a bool */
      break;
  }
  RTC_DCHECK_NOTREACHED();
  return {};
}

const char* QualityLimitationReasonToRTCQualityLimitationReason(
    QualityLimitationReason reason) {
  switch (reason) {
    case QualityLimitationReason::kNone:
      return "none";
    case QualityLimitationReason::kCpu:
      return "cpu";
    case QualityLimitationReason::kBandwidth:
      return "bandwidth";
    case QualityLimitationReason::kOther:
      return "other";
  }
  RTC_CHECK_NOTREACHED();
}

std::map<std::string, double>
QualityLimitationDurationToRTCQualityLimitationDuration(
    std::map<QualityLimitationReason, int64_t> durations_ms) {
  std::map<std::string, double> result;
  // The internal duration is defined in milliseconds while the spec defines
  // the value in seconds:
  // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationdurations
  for (const auto& elem : durations_ms) {
    result[QualityLimitationReasonToRTCQualityLimitationReason(elem.first)] =
        elem.second / static_cast<double>(rtc::kNumMillisecsPerSec);
  }
  return result;
}

double DoubleAudioLevelFromIntAudioLevel(int audio_level) {
  RTC_DCHECK_GE(audio_level, 0);
  RTC_DCHECK_LE(audio_level, 32767);
  return audio_level / 32767.0;
}

// Gets the `codecId` identified by `transport_id` and `codec_params`. If no
// such `RTCCodecStats` exist yet, create it and add it to `report`.
std::string GetCodecIdAndMaybeCreateCodecStats(
    Timestamp timestamp,
    const char direction,
    const std::string& transport_id,
    const RtpCodecParameters& codec_params,
    RTCStatsReport* report) {
  RTC_DCHECK_GE(codec_params.payload_type, 0);
  RTC_DCHECK_LE(codec_params.payload_type, 127);
  RTC_DCHECK(codec_params.clock_rate);
  uint32_t payload_type = static_cast<uint32_t>(codec_params.payload_type);
  std::string codec_id = RTCCodecStatsIDFromTransportAndCodecParameters(
      direction, transport_id, codec_params);
  if (report->Get(codec_id) != nullptr) {
    // The `RTCCodecStats` already exists.
    return codec_id;
  }
  // Create the `RTCCodecStats` that we want to reference.
  auto codec_stats = std::make_unique<RTCCodecStats>(codec_id, timestamp);
  codec_stats->payload_type = payload_type;
  codec_stats->mime_type = codec_params.mime_type();
  if (codec_params.clock_rate.has_value()) {
    codec_stats->clock_rate = static_cast<uint32_t>(*codec_params.clock_rate);
  }
  if (codec_params.num_channels) {
    codec_stats->channels = *codec_params.num_channels;
  }

  rtc::StringBuilder fmtp;
  if (WriteFmtpParameters(codec_params.parameters, &fmtp)) {
    codec_stats->sdp_fmtp_line = fmtp.Release();
  }
  codec_stats->transport_id = transport_id;
  report->AddStats(std::move(codec_stats));
  return codec_id;
}

// Provides the media independent counters (both audio and video).
void SetInboundRTPStreamStatsFromMediaReceiverInfo(
    const cricket::MediaReceiverInfo& media_receiver_info,
    RTCInboundRtpStreamStats* inbound_stats) {
  RTC_DCHECK(inbound_stats);
  inbound_stats->ssrc = media_receiver_info.ssrc();
  inbound_stats->packets_received =
      static_cast<uint32_t>(media_receiver_info.packets_received);
  inbound_stats->bytes_received =
      static_cast<uint64_t>(media_receiver_info.payload_bytes_received);
  inbound_stats->header_bytes_received = static_cast<uint64_t>(
      media_receiver_info.header_and_padding_bytes_received);
  if (media_receiver_info.retransmitted_bytes_received.has_value()) {
    inbound_stats->retransmitted_bytes_received =
        *media_receiver_info.retransmitted_bytes_received;
  }
  if (media_receiver_info.retransmitted_packets_received.has_value()) {
    inbound_stats->retransmitted_packets_received =
        *media_receiver_info.retransmitted_packets_received;
  }
  inbound_stats->packets_lost =
      static_cast<int32_t>(media_receiver_info.packets_lost);
  inbound_stats->jitter_buffer_delay =
      media_receiver_info.jitter_buffer_delay_seconds;
  inbound_stats->jitter_buffer_target_delay =
      media_receiver_info.jitter_buffer_target_delay_seconds;
  inbound_stats->jitter_buffer_minimum_delay =
      media_receiver_info.jitter_buffer_minimum_delay_seconds;
  inbound_stats->jitter_buffer_emitted_count =
      media_receiver_info.jitter_buffer_emitted_count;
  if (media_receiver_info.nacks_sent.has_value()) {
    inbound_stats->nack_count = *media_receiver_info.nacks_sent;
  }
  if (media_receiver_info.fec_packets_received.has_value()) {
    inbound_stats->fec_packets_received =
        *media_receiver_info.fec_packets_received;
  }
  if (media_receiver_info.fec_packets_discarded.has_value()) {
    inbound_stats->fec_packets_discarded =
        *media_receiver_info.fec_packets_discarded;
  }
  if (media_receiver_info.fec_bytes_received.has_value()) {
    inbound_stats->fec_bytes_received = *media_receiver_info.fec_bytes_received;
  }
  inbound_stats->total_processing_delay =
      media_receiver_info.total_processing_delay_seconds;
}

std::unique_ptr<RTCInboundRtpStreamStats> CreateInboundAudioStreamStats(
    const cricket::VoiceMediaInfo& voice_media_info,
    const cricket::VoiceReceiverInfo& voice_receiver_info,
    const std::string& transport_id,
    const std::string& mid,
    Timestamp timestamp,
    RTCStatsReport* report) {
  auto inbound_audio = std::make_unique<RTCInboundRtpStreamStats>(
      /*id=*/RTCInboundRtpStreamStatsIDFromSSRC(
          transport_id, cricket::MEDIA_TYPE_AUDIO, voice_receiver_info.ssrc()),
      timestamp);
  SetInboundRTPStreamStatsFromMediaReceiverInfo(voice_receiver_info,
                                                inbound_audio.get());
  inbound_audio->transport_id = transport_id;
  inbound_audio->mid = mid;
  inbound_audio->kind = "audio";
  if (voice_receiver_info.codec_payload_type.has_value()) {
    auto codec_param_it = voice_media_info.receive_codecs.find(
        *voice_receiver_info.codec_payload_type);
    RTC_DCHECK(codec_param_it != voice_media_info.receive_codecs.end());
    if (codec_param_it != voice_media_info.receive_codecs.end()) {
      inbound_audio->codec_id = GetCodecIdAndMaybeCreateCodecStats(
          inbound_audio->timestamp(), kDirectionInbound, transport_id,
          codec_param_it->second, report);
    }
  }
  inbound_audio->jitter = static_cast<double>(voice_receiver_info.jitter_ms) /
                          rtc::kNumMillisecsPerSec;
  inbound_audio->total_samples_received =
      voice_receiver_info.total_samples_received;
  inbound_audio->concealed_samples = voice_receiver_info.concealed_samples;
  inbound_audio->silent_concealed_samples =
      voice_receiver_info.silent_concealed_samples;
  inbound_audio->concealment_events = voice_receiver_info.concealment_events;
  inbound_audio->inserted_samples_for_deceleration =
      voice_receiver_info.inserted_samples_for_deceleration;
  inbound_audio->removed_samples_for_acceleration =
      voice_receiver_info.removed_samples_for_acceleration;
  if (voice_receiver_info.audio_level >= 0) {
    inbound_audio->audio_level =
        DoubleAudioLevelFromIntAudioLevel(voice_receiver_info.audio_level);
  }
  inbound_audio->total_audio_energy = voice_receiver_info.total_output_energy;
  inbound_audio->total_samples_duration =
      voice_receiver_info.total_output_duration;
  // `fir_count` and `pli_count` are only valid for video and are
  // purposefully left undefined for audio.
  if (voice_receiver_info.last_packet_received.has_value()) {
    inbound_audio->last_packet_received_timestamp =
        voice_receiver_info.last_packet_received->ms<double>();
  }
  if (voice_receiver_info.estimated_playout_ntp_timestamp_ms.has_value()) {
    // TODO(bugs.webrtc.org/10529): Fix time origin.
    inbound_audio->estimated_playout_timestamp = static_cast<double>(
        *voice_receiver_info.estimated_playout_ntp_timestamp_ms);
  }
  inbound_audio->packets_discarded = voice_receiver_info.packets_discarded;
  inbound_audio->jitter_buffer_flushes =
      voice_receiver_info.jitter_buffer_flushes;
  inbound_audio->delayed_packet_outage_samples =
      voice_receiver_info.delayed_packet_outage_samples;
  inbound_audio->relative_packet_arrival_delay =
      voice_receiver_info.relative_packet_arrival_delay_seconds;
  inbound_audio->interruption_count =
      voice_receiver_info.interruption_count >= 0
          ? voice_receiver_info.interruption_count
          : 0;
  inbound_audio->total_interruption_duration =
      static_cast<double>(voice_receiver_info.total_interruption_duration_ms) /
      rtc::kNumMillisecsPerSec;
  return inbound_audio;
}

std::unique_ptr<RTCAudioPlayoutStats> CreateAudioPlayoutStats(
    const AudioDeviceModule::Stats& audio_device_stats,
    Timestamp timestamp) {
  auto stats = std::make_unique<RTCAudioPlayoutStats>(
      /*id=*/kAudioPlayoutSingletonId, timestamp);
  stats->synthesized_samples_duration =
      audio_device_stats.synthesized_samples_duration_s;
  stats->synthesized_samples_events =
      audio_device_stats.synthesized_samples_events;
  stats->total_samples_count = audio_device_stats.total_samples_count;
  stats->total_samples_duration = audio_device_stats.total_samples_duration_s;
  stats->total_playout_delay = audio_device_stats.total_playout_delay_s;
  return stats;
}

std::unique_ptr<RTCRemoteOutboundRtpStreamStats>
CreateRemoteOutboundMediaStreamStats(
    const cricket::MediaReceiverInfo& media_receiver_info,
    const std::string& mid,
    cricket::MediaType media_type,
    const RTCInboundRtpStreamStats& inbound_audio_stats,
    const std::string& transport_id,
    const bool stats_timestamp_with_environment_clock) {
  std::optional<Timestamp> last_sender_report_timestamp =
      stats_timestamp_with_environment_clock
          ? media_receiver_info.last_sender_report_timestamp
          : media_receiver_info.last_sender_report_utc_timestamp;
  if (!last_sender_report_timestamp.has_value()) {
    // Cannot create `RTCRemoteOutboundRtpStreamStats` when the RTCP SR arrival
    // timestamp is not available - i.e., until the first sender report is
    // received.
    return nullptr;
  }
  RTC_DCHECK_GT(media_receiver_info.sender_reports_reports_count, 0);

  // Create.
  auto stats = std::make_unique<RTCRemoteOutboundRtpStreamStats>(
      /*id=*/RTCRemoteOutboundRTPStreamStatsIDFromSSRC(
          media_type, media_receiver_info.ssrc()),
      *last_sender_report_timestamp);

  // Populate.
  // - RTCRtpStreamStats.
  stats->ssrc = media_receiver_info.ssrc();
  stats->kind = cricket::MediaTypeToString(media_type);
  stats->transport_id = transport_id;
  if (inbound_audio_stats.codec_id.has_value()) {
    stats->codec_id = *inbound_audio_stats.codec_id;
  }
  // - RTCSentRtpStreamStats.
  stats->packets_sent = media_receiver_info.sender_reports_packets_sent;
  stats->bytes_sent = media_receiver_info.sender_reports_bytes_sent;
  // - RTCRemoteOutboundRtpStreamStats.
  stats->local_id = inbound_audio_stats.id();
  // last_sender_report_remote_utc_timestamp_ms is set together with
  // last_sender_report_utc_timestamp_ms.
  RTC_DCHECK(
      media_receiver_info.last_sender_report_remote_utc_timestamp.has_value());
  stats->remote_timestamp =
      media_receiver_info.last_sender_report_remote_utc_timestamp->ms<double>();
  stats->reports_sent = media_receiver_info.sender_reports_reports_count;
  if (media_receiver_info.round_trip_time.has_value()) {
    stats->round_trip_time =
        media_receiver_info.round_trip_time->seconds<double>();
  }
  stats->round_trip_time_measurements =
      media_receiver_info.round_trip_time_measurements;
  stats->total_round_trip_time =
      media_receiver_info.total_round_trip_time.seconds<double>();

  return stats;
}

std::unique_ptr<RTCInboundRtpStreamStats>
CreateInboundRTPStreamStatsFromVideoReceiverInfo(
    const std::string& transport_id,
    const std::string& mid,
    const cricket::VideoMediaInfo& video_media_info,
    const cricket::VideoReceiverInfo& video_receiver_info,
    Timestamp timestamp,
    RTCStatsReport* report) {
  auto inbound_video = std::make_unique<RTCInboundRtpStreamStats>(
      RTCInboundRtpStreamStatsIDFromSSRC(
          transport_id, cricket::MEDIA_TYPE_VIDEO, video_receiver_info.ssrc()),
      timestamp);
  SetInboundRTPStreamStatsFromMediaReceiverInfo(video_receiver_info,
                                                inbound_video.get());
  inbound_video->transport_id = transport_id;
  inbound_video->mid = mid;
  inbound_video->kind = "video";
  if (video_receiver_info.codec_payload_type.has_value()) {
    auto codec_param_it = video_media_info.receive_codecs.find(
        *video_receiver_info.codec_payload_type);
    RTC_DCHECK(codec_param_it != video_media_info.receive_codecs.end());
    if (codec_param_it != video_media_info.receive_codecs.end()) {
      inbound_video->codec_id = GetCodecIdAndMaybeCreateCodecStats(
          inbound_video->timestamp(), kDirectionInbound, transport_id,
          codec_param_it->second, report);
    }
  }
  inbound_video->jitter = static_cast<double>(video_receiver_info.jitter_ms) /
                          rtc::kNumMillisecsPerSec;
  inbound_video->fir_count =
      static_cast<uint32_t>(video_receiver_info.firs_sent);
  inbound_video->pli_count =
      static_cast<uint32_t>(video_receiver_info.plis_sent);
  inbound_video->frames_received = video_receiver_info.frames_received;
  inbound_video->frames_decoded = video_receiver_info.frames_decoded;
  inbound_video->frames_dropped = video_receiver_info.frames_dropped;
  inbound_video->key_frames_decoded = video_receiver_info.key_frames_decoded;
  if (video_receiver_info.frame_width > 0) {
    inbound_video->frame_width =
        static_cast<uint32_t>(video_receiver_info.frame_width);
  }
  if (video_receiver_info.frame_height > 0) {
    inbound_video->frame_height =
        static_cast<uint32_t>(video_receiver_info.frame_height);
  }
  if (video_receiver_info.framerate_decoded > 0) {
    inbound_video->frames_per_second = video_receiver_info.framerate_decoded;
  }
  if (video_receiver_info.qp_sum.has_value()) {
    inbound_video->qp_sum = *video_receiver_info.qp_sum;
  }
  if (video_receiver_info.corruption_score_sum.has_value()) {
    RTC_CHECK(video_receiver_info.corruption_score_squared_sum.has_value());
    RTC_CHECK_GT(video_receiver_info.corruption_score_count, 0);
    inbound_video->total_corruption_probability =
        *video_receiver_info.corruption_score_sum;
    inbound_video->total_squared_corruption_probability =
        *video_receiver_info.corruption_score_squared_sum;
    inbound_video->corruption_measurements =
        video_receiver_info.corruption_score_count;
  }
  if (video_receiver_info.timing_frame_info.has_value()) {
    inbound_video->goog_timing_frame_info =
        video_receiver_info.timing_frame_info->ToString();
  }
  inbound_video->total_decode_time =
      video_receiver_info.total_decode_time.seconds<double>();
  inbound_video->total_processing_delay =
      video_receiver_info.total_processing_delay.seconds<double>();
  inbound_video->total_assembly_time =
      video_receiver_info.total_assembly_time.seconds<double>();
  inbound_video->frames_assembled_from_multiple_packets =
      video_receiver_info.frames_assembled_from_multiple_packets;
  inbound_video->total_inter_frame_delay =
      video_receiver_info.total_inter_frame_delay;
  inbound_video->total_squared_inter_frame_delay =
      video_receiver_info.total_squared_inter_frame_delay;
  inbound_video->pause_count = video_receiver_info.pause_count;
  inbound_video->total_pauses_duration =
      static_cast<double>(video_receiver_info.total_pauses_duration_ms) /
      rtc::kNumMillisecsPerSec;
  inbound_video->freeze_count = video_receiver_info.freeze_count;
  inbound_video->total_freezes_duration =
      static_cast<double>(video_receiver_info.total_freezes_duration_ms) /
      rtc::kNumMillisecsPerSec;
  inbound_video->min_playout_delay =
      static_cast<double>(video_receiver_info.min_playout_delay_ms) /
      rtc::kNumMillisecsPerSec;
  if (video_receiver_info.last_packet_received.has_value()) {
    inbound_video->last_packet_received_timestamp =
        video_receiver_info.last_packet_received->ms<double>();
  }
  if (video_receiver_info.estimated_playout_ntp_timestamp_ms.has_value()) {
    // TODO(bugs.webrtc.org/10529): Fix time origin if needed.
    inbound_video->estimated_playout_timestamp = static_cast<double>(
        *video_receiver_info.estimated_playout_ntp_timestamp_ms);
  }
  // TODO(bugs.webrtc.org/10529): When info's `content_info` is optional
  // support the "unspecified" value.
  if (videocontenttypehelpers::IsScreenshare(video_receiver_info.content_type))
    inbound_video->content_type = "screenshare";
  if (video_receiver_info.decoder_implementation_name.has_value()) {
    inbound_video->decoder_implementation =
        *video_receiver_info.decoder_implementation_name;
  }
  if (video_receiver_info.power_efficient_decoder.has_value()) {
    inbound_video->power_efficient_decoder =
        *video_receiver_info.power_efficient_decoder;
  }
  for (const auto& ssrc_group : video_receiver_info.ssrc_groups) {
    if (ssrc_group.semantics == cricket::kFidSsrcGroupSemantics &&
        ssrc_group.ssrcs.size() == 2) {
      inbound_video->rtx_ssrc = ssrc_group.ssrcs[1];
    } else if (ssrc_group.semantics == cricket::kFecFrSsrcGroupSemantics &&
               ssrc_group.ssrcs.size() == 2) {
      // TODO(bugs.webrtc.org/15002): the ssrc-group might be >= 2 with
      // multistream support.
      inbound_video->fec_ssrc = ssrc_group.ssrcs[1];
    }
  }

  return inbound_video;
}

// Provides the media independent counters and information (both audio and
// video).
void SetOutboundRTPStreamStatsFromMediaSenderInfo(
    const cricket::MediaSenderInfo& media_sender_info,
    RTCOutboundRtpStreamStats* outbound_stats) {
  RTC_DCHECK(outbound_stats);
  outbound_stats->ssrc = media_sender_info.ssrc();
  outbound_stats->packets_sent =
      static_cast<uint32_t>(media_sender_info.packets_sent);
  outbound_stats->total_packet_send_delay =
      media_sender_info.total_packet_send_delay.seconds<double>();
  outbound_stats->retransmitted_packets_sent =
      media_sender_info.retransmitted_packets_sent;
  outbound_stats->bytes_sent =
      static_cast<uint64_t>(media_sender_info.payload_bytes_sent);
  outbound_stats->header_bytes_sent =
      static_cast<uint64_t>(media_sender_info.header_and_padding_bytes_sent);
  outbound_stats->retransmitted_bytes_sent =
      media_sender_info.retransmitted_bytes_sent;
  outbound_stats->nack_count = media_sender_info.nacks_received;
  if (media_sender_info.active.has_value()) {
    outbound_stats->active = *media_sender_info.active;
  }
}

std::unique_ptr<RTCOutboundRtpStreamStats>
CreateOutboundRTPStreamStatsFromVoiceSenderInfo(
    const std::string& transport_id,
    const std::string& mid,
    const cricket::VoiceMediaInfo& voice_media_info,
    const cricket::VoiceSenderInfo& voice_sender_info,
    Timestamp timestamp,
    RTCStatsReport* report) {
  auto outbound_audio = std::make_unique<RTCOutboundRtpStreamStats>(
      RTCOutboundRtpStreamStatsIDFromSSRC(
          transport_id, cricket::MEDIA_TYPE_AUDIO, voice_sender_info.ssrc()),
      timestamp);
  SetOutboundRTPStreamStatsFromMediaSenderInfo(voice_sender_info,
                                               outbound_audio.get());
  outbound_audio->transport_id = transport_id;
  outbound_audio->mid = mid;
  outbound_audio->kind = "audio";
  if (voice_sender_info.target_bitrate.has_value() &&
      *voice_sender_info.target_bitrate > 0) {
    outbound_audio->target_bitrate = *voice_sender_info.target_bitrate;
  }
  if (voice_sender_info.codec_payload_type.has_value()) {
    auto codec_param_it = voice_media_info.send_codecs.find(
        *voice_sender_info.codec_payload_type);
    RTC_DCHECK(codec_param_it != voice_media_info.send_codecs.end());
    if (codec_param_it != voice_media_info.send_codecs.end()) {
      outbound_audio->codec_id = GetCodecIdAndMaybeCreateCodecStats(
          outbound_audio->timestamp(), kDirectionOutbound, transport_id,
          codec_param_it->second, report);
    }
  }
  // `fir_count` and `pli_count` are only valid for video and are
  // purposefully left undefined for audio.
  return outbound_audio;
}

std::unique_ptr<RTCOutboundRtpStreamStats>
CreateOutboundRTPStreamStatsFromVideoSenderInfo(
    const std::string& transport_id,
    const std::string& mid,
    const cricket::VideoMediaInfo& video_media_info,
    const cricket::VideoSenderInfo& video_sender_info,
    Timestamp timestamp,
    RTCStatsReport* report) {
  auto outbound_video = std::make_unique<RTCOutboundRtpStreamStats>(
      RTCOutboundRtpStreamStatsIDFromSSRC(
          transport_id, cricket::MEDIA_TYPE_VIDEO, video_sender_info.ssrc()),
      timestamp);
  SetOutboundRTPStreamStatsFromMediaSenderInfo(video_sender_info,
                                               outbound_video.get());
  outbound_video->transport_id = transport_id;
  outbound_video->mid = mid;
  outbound_video->kind = "video";
  if (video_sender_info.codec_payload_type.has_value()) {
    auto codec_param_it = video_media_info.send_codecs.find(
        *video_sender_info.codec_payload_type);
    RTC_DCHECK(codec_param_it != video_media_info.send_codecs.end());
    if (codec_param_it != video_media_info.send_codecs.end()) {
      outbound_video->codec_id = GetCodecIdAndMaybeCreateCodecStats(
          outbound_video->timestamp(), kDirectionOutbound, transport_id,
          codec_param_it->second, report);
    }
  }
  outbound_video->fir_count =
      static_cast<uint32_t>(video_sender_info.firs_received);
  outbound_video->pli_count =
      static_cast<uint32_t>(video_sender_info.plis_received);
  if (video_sender_info.qp_sum.has_value())
    outbound_video->qp_sum = *video_sender_info.qp_sum;
  if (video_sender_info.target_bitrate.has_value() &&
      *video_sender_info.target_bitrate > 0) {
    outbound_video->target_bitrate = *video_sender_info.target_bitrate;
  }
  outbound_video->frames_encoded = video_sender_info.frames_encoded;
  outbound_video->key_frames_encoded = video_sender_info.key_frames_encoded;
  outbound_video->total_encode_time =
      static_cast<double>(video_sender_info.total_encode_time_ms) /
      rtc::kNumMillisecsPerSec;
  outbound_video->total_encoded_bytes_target =
      video_sender_info.total_encoded_bytes_target;
  if (video_sender_info.send_frame_width > 0) {
    outbound_video->frame_width =
        static_cast<uint32_t>(video_sender_info.send_frame_width);
  }
  if (video_sender_info.send_frame_height > 0) {
    outbound_video->frame_height =
        static_cast<uint32_t>(video_sender_info.send_frame_height);
  }
  if (video_sender_info.framerate_sent > 0) {
    outbound_video->frames_per_second = video_sender_info.framerate_sent;
  }
  outbound_video->frames_sent = video_sender_info.frames_sent;
  outbound_video->huge_frames_sent = video_sender_info.huge_frames_sent;
  outbound_video->quality_limitation_reason =
      QualityLimitationReasonToRTCQualityLimitationReason(
          video_sender_info.quality_limitation_reason);
  outbound_video->quality_limitation_durations =
      QualityLimitationDurationToRTCQualityLimitationDuration(
          video_sender_info.quality_limitation_durations_ms);
  outbound_video->quality_limitation_resolution_changes =
      video_sender_info.quality_limitation_resolution_changes;
  // TODO(https://crbug.com/webrtc/10529): When info's `content_info` is
  // optional, support the "unspecified" value.
  if (videocontenttypehelpers::IsScreenshare(video_sender_info.content_type))
    outbound_video->content_type = "screenshare";
  if (video_sender_info.encoder_implementation_name.has_value()) {
    outbound_video->encoder_implementation =
        *video_sender_info.encoder_implementation_name;
  }
  if (video_sender_info.rid.has_value()) {
    outbound_video->rid = *video_sender_info.rid;
  }
  if (video_sender_info.power_efficient_encoder.has_value()) {
    outbound_video->power_efficient_encoder =
        *video_sender_info.power_efficient_encoder;
  }
  if (video_sender_info.scalability_mode) {
    outbound_video->scalability_mode = std::string(
        ScalabilityModeToString(*video_sender_info.scalability_mode));
  }
  for (const auto& ssrc_group : video_sender_info.ssrc_groups) {
    if (ssrc_group.semantics == cricket::kFidSsrcGroupSemantics &&
        ssrc_group.ssrcs.size() == 2 &&
        video_sender_info.ssrc() == ssrc_group.ssrcs[0]) {
      outbound_video->rtx_ssrc = ssrc_group.ssrcs[1];
    }
  }
  return outbound_video;
}

std::unique_ptr<RTCRemoteInboundRtpStreamStats>
ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
    const std::string& transport_id,
    const ReportBlockData& report_block,
    cricket::MediaType media_type,
    const std::map<std::string, RTCOutboundRtpStreamStats*>& outbound_rtps,
    const RTCStatsReport& report,
    const bool stats_timestamp_with_environment_clock) {
  // RTCStats' timestamp generally refers to when the metric was sampled, but
  // for "remote-[outbound/inbound]-rtp" it refers to the local time when the
  // Report Block was received.
  Timestamp arrival_timestamp = stats_timestamp_with_environment_clock
                                    ? report_block.report_block_timestamp()
                                    : report_block.report_block_timestamp_utc();
  auto remote_inbound = std::make_unique<RTCRemoteInboundRtpStreamStats>(
      RTCRemoteInboundRtpStreamStatsIdFromSourceSsrc(
          media_type, report_block.source_ssrc()),
      arrival_timestamp);
  remote_inbound->ssrc = report_block.source_ssrc();
  remote_inbound->kind =
      media_type == cricket::MEDIA_TYPE_AUDIO ? "audio" : "video";
  remote_inbound->packets_lost = report_block.cumulative_lost();
  remote_inbound->fraction_lost = report_block.fraction_lost();
  if (report_block.num_rtts() > 0) {
    remote_inbound->round_trip_time = report_block.last_rtt().seconds<double>();
  }
  remote_inbound->total_round_trip_time =
      report_block.sum_rtts().seconds<double>();
  remote_inbound->round_trip_time_measurements = report_block.num_rtts();

  std::string local_id = RTCOutboundRtpStreamStatsIDFromSSRC(
      transport_id, media_type, report_block.source_ssrc());
  // Look up local stat from `outbound_rtps` where the pointers are non-const.
  auto local_id_it = outbound_rtps.find(local_id);
  if (local_id_it != outbound_rtps.end()) {
    remote_inbound->local_id = local_id;
    auto& outbound_rtp = *local_id_it->second;
    outbound_rtp.remote_id = remote_inbound->id();
    // The RTP/RTCP transport is obtained from the
    // RTCOutboundRtpStreamStats's transport.
    const auto* transport_from_id = report.Get(transport_id);
    if (transport_from_id) {
      const auto& transport = transport_from_id->cast_to<RTCTransportStats>();
      // If RTP and RTCP are not multiplexed, there is a separate RTCP
      // transport paired with the RTP transport, otherwise the same
      // transport is used for RTCP and RTP.
      remote_inbound->transport_id =
          transport.rtcp_transport_stats_id.has_value()
              ? *transport.rtcp_transport_stats_id
              : *outbound_rtp.transport_id;
    }
    // We're assuming the same codec is used on both ends. However if the
    // codec is switched out on the fly we may have received a Report Block
    // based on the previous codec and there is no way to tell which point in
    // time the codec changed for the remote end.
    const auto* codec_from_id = outbound_rtp.codec_id.has_value()
                                    ? report.Get(*outbound_rtp.codec_id)
                                    : nullptr;
    if (codec_from_id) {
      remote_inbound->codec_id = *outbound_rtp.codec_id;
      const auto& codec = codec_from_id->cast_to<RTCCodecStats>();
      if (codec.clock_rate.has_value()) {
        remote_inbound->jitter =
            report_block.jitter(*codec.clock_rate).seconds<double>();
      }
    }
  }
  return remote_inbound;
}

void ProduceCertificateStatsFromSSLCertificateStats(
    Timestamp timestamp,
    const rtc::SSLCertificateStats& certificate_stats,
    RTCStatsReport* report) {
  RTCCertificateStats* prev_certificate_stats = nullptr;
  for (const rtc::SSLCertificateStats* s = &certificate_stats; s;
       s = s->issuer.get()) {
    std::string certificate_stats_id =
        RTCCertificateIDFromFingerprint(s->fingerprint);
    // It is possible for the same certificate to show up multiple times, e.g.
    // if local and remote side use the same certificate in a loopback call.
    // If the report already contains stats for this certificate, skip it.
    if (report->Get(certificate_stats_id)) {
      RTC_DCHECK_EQ(s, &certificate_stats);
      break;
    }
    RTCCertificateStats* certificate_stats =
        new RTCCertificateStats(certificate_stats_id, timestamp);
    certificate_stats->fingerprint = s->fingerprint;
    certificate_stats->fingerprint_algorithm = s->fingerprint_algorithm;
    certificate_stats->base64_certificate = s->base64_certificate;
    if (prev_certificate_stats)
      prev_certificate_stats->issuer_certificate_id = certificate_stats->id();
    report->AddStats(std::unique_ptr<RTCCertificateStats>(certificate_stats));
    prev_certificate_stats = certificate_stats;
  }
}

const std::string& ProduceIceCandidateStats(Timestamp timestamp,
                                            const cricket::Candidate& candidate,
                                            bool is_local,
                                            const std::string& transport_id,
                                            RTCStatsReport* report) {
  std::string id = "I" + candidate.id();
  const RTCStats* stats = report->Get(id);
  if (!stats) {
    std::unique_ptr<RTCIceCandidateStats> candidate_stats;
    if (is_local) {
      candidate_stats =
          std::make_unique<RTCLocalIceCandidateStats>(std::move(id), timestamp);
    } else {
      candidate_stats = std::make_unique<RTCRemoteIceCandidateStats>(
          std::move(id), timestamp);
    }
    candidate_stats->transport_id = transport_id;
    if (is_local) {
      candidate_stats->network_type =
          NetworkTypeToStatsType(candidate.network_type());
      const std::string& relay_protocol = candidate.relay_protocol();
      const std::string& url = candidate.url();
      if (candidate.is_relay() ||
          (candidate.is_prflx() && !relay_protocol.empty())) {
        RTC_DCHECK(relay_protocol.compare("udp") == 0 ||
                   relay_protocol.compare("tcp") == 0 ||
                   relay_protocol.compare("tls") == 0);
        candidate_stats->relay_protocol = relay_protocol;
        if (!url.empty()) {
          candidate_stats->url = url;
        }
      } else if (candidate.is_stun()) {
        if (!url.empty()) {
          candidate_stats->url = url;
        }
      }
      if (candidate.network_type() == rtc::ADAPTER_TYPE_VPN) {
        candidate_stats->vpn = true;
        candidate_stats->network_adapter_type =
            std::string(NetworkTypeToStatsNetworkAdapterType(
                candidate.underlying_type_for_vpn()));
      } else {
        candidate_stats->vpn = false;
        candidate_stats->network_adapter_type = std::string(
            NetworkTypeToStatsNetworkAdapterType(candidate.network_type()));
      }
    } else {
      // We don't expect to know the adapter type of remote candidates.
      RTC_DCHECK_EQ(rtc::ADAPTER_TYPE_UNKNOWN, candidate.network_type());
      RTC_DCHECK_EQ(0, candidate.relay_protocol().compare(""));
      RTC_DCHECK_EQ(rtc::ADAPTER_TYPE_UNKNOWN,
                    candidate.underlying_type_for_vpn());
    }
    candidate_stats->ip = candidate.address().ipaddr().ToString();
    candidate_stats->address = candidate.address().ipaddr().ToString();
    candidate_stats->port = static_cast<int32_t>(candidate.address().port());
    candidate_stats->protocol = candidate.protocol();
    candidate_stats->candidate_type = candidate.type_name();
    candidate_stats->priority = static_cast<int32_t>(candidate.priority());
    candidate_stats->foundation = candidate.foundation();
    auto related_address = candidate.related_address();
    if (related_address.port() != 0) {
      candidate_stats->related_address = related_address.ipaddr().ToString();
      candidate_stats->related_port =
          static_cast<int32_t>(related_address.port());
    }
    candidate_stats->username_fragment = candidate.username();
    if (candidate.protocol() == "tcp") {
      candidate_stats->tcp_type = candidate.tcptype();
    }

    stats = candidate_stats.get();
    report->AddStats(std::move(candidate_stats));
  }
  RTC_DCHECK_EQ(stats->type(), is_local ? RTCLocalIceCandidateStats::kType
                                        : RTCRemoteIceCandidateStats::kType);
  return stats->id();
}

template <typename StatsType>
void SetAudioProcessingStats(StatsType* stats,
                             const AudioProcessingStats& apm_stats) {
  if (apm_stats.echo_return_loss.has_value()) {
    stats->echo_return_loss = *apm_stats.echo_return_loss;
  }
  if (apm_stats.echo_return_loss_enhancement.has_value()) {
    stats->echo_return_loss_enhancement =
        *apm_stats.echo_return_loss_enhancement;
  }
}

}  // namespace

rtc::scoped_refptr<RTCStatsReport>
RTCStatsCollector::CreateReportFilteredBySelector(
    bool filter_by_sender_selector,
    rtc::scoped_refptr<const RTCStatsReport> report,
    rtc::scoped_refptr<RtpSenderInternal> sender_selector,
    rtc::scoped_refptr<RtpReceiverInternal> receiver_selector) {
  std::vector<std::string> rtpstream_ids;
  if (filter_by_sender_selector) {
    // Filter mode: RTCStatsCollector::RequestInfo::kSenderSelector
    if (sender_selector) {
      // Find outbound-rtp(s) of the sender using ssrc lookup.
      auto encodings = sender_selector->GetParametersInternal().encodings;
      for (const auto* outbound_rtp :
           report->GetStatsOfType<RTCOutboundRtpStreamStats>()) {
        RTC_DCHECK(outbound_rtp->ssrc.has_value());
        auto it = std::find_if(encodings.begin(), encodings.end(),
                               [ssrc = *outbound_rtp->ssrc](
                                   const RtpEncodingParameters& encoding) {
                                 return encoding.ssrc == ssrc;
                               });
        if (it != encodings.end()) {
          rtpstream_ids.push_back(outbound_rtp->id());
        }
      }
    }
  } else {
    // Filter mode: RTCStatsCollector::RequestInfo::kReceiverSelector
    if (receiver_selector) {
      // Find the inbound-rtp of the receiver using ssrc lookup.
      std::optional<uint32_t> ssrc;
      worker_thread_->BlockingCall([&] { ssrc = receiver_selector->ssrc(); });
      if (ssrc.has_value()) {
        for (const auto* inbound_rtp :
             report->GetStatsOfType<RTCInboundRtpStreamStats>()) {
          RTC_DCHECK(inbound_rtp->ssrc.has_value());
          if (*inbound_rtp->ssrc == *ssrc) {
            rtpstream_ids.push_back(inbound_rtp->id());
          }
        }
      }
    }
  }
  if (rtpstream_ids.empty())
    return RTCStatsReport::Create(report->timestamp());
  return TakeReferencedStats(report->Copy(), rtpstream_ids);
}

RTCStatsCollector::CertificateStatsPair
RTCStatsCollector::CertificateStatsPair::Copy() const {
  CertificateStatsPair copy;
  copy.local = local ? local->Copy() : nullptr;
  copy.remote = remote ? remote->Copy() : nullptr;
  return copy;
}

RTCStatsCollector::RequestInfo::RequestInfo(
    rtc::scoped_refptr<RTCStatsCollectorCallback> callback)
    : RequestInfo(FilterMode::kAll, std::move(callback), nullptr, nullptr) {}

RTCStatsCollector::RequestInfo::RequestInfo(
    rtc::scoped_refptr<RtpSenderInternal> selector,
    rtc::scoped_refptr<RTCStatsCollectorCallback> callback)
    : RequestInfo(FilterMode::kSenderSelector,
                  std::move(callback),
                  std::move(selector),
                  nullptr) {}

RTCStatsCollector::RequestInfo::RequestInfo(
    rtc::scoped_refptr<RtpReceiverInternal> selector,
    rtc::scoped_refptr<RTCStatsCollectorCallback> callback)
    : RequestInfo(FilterMode::kReceiverSelector,
                  std::move(callback),
                  nullptr,
                  std::move(selector)) {}

RTCStatsCollector::RequestInfo::RequestInfo(
    RTCStatsCollector::RequestInfo::FilterMode filter_mode,
    rtc::scoped_refptr<RTCStatsCollectorCallback> callback,
    rtc::scoped_refptr<RtpSenderInternal> sender_selector,
    rtc::scoped_refptr<RtpReceiverInternal> receiver_selector)
    : filter_mode_(filter_mode),
      callback_(std::move(callback)),
      sender_selector_(std::move(sender_selector)),
      receiver_selector_(std::move(receiver_selector)) {
  RTC_DCHECK(callback_);
  RTC_DCHECK(!sender_selector_ || !receiver_selector_);
}

rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create(
    PeerConnectionInternal* pc,
    const Environment& env,
    int64_t cache_lifetime_us) {
  return rtc::make_ref_counted<RTCStatsCollector>(pc, env, cache_lifetime_us);
}

RTCStatsCollector::RTCStatsCollector(PeerConnectionInternal* pc,
                                     const Environment& env,
                                     int64_t cache_lifetime_us)
    : pc_(pc),
      env_(env),
      stats_timestamp_with_environment_clock_(
          pc->GetConfiguration().stats_timestamp_with_environment_clock()),
      signaling_thread_(pc->signaling_thread()),
      worker_thread_(pc->worker_thread()),
      network_thread_(pc->network_thread()),
      num_pending_partial_reports_(0),
      partial_report_timestamp_us_(0),
      network_report_event_(true /* manual_reset */,
                            true /* initially_signaled */),
      cache_timestamp_us_(0),
      cache_lifetime_us_(cache_lifetime_us) {
  RTC_DCHECK(pc_);
  RTC_DCHECK(signaling_thread_);
  RTC_DCHECK(worker_thread_);
  RTC_DCHECK(network_thread_);
  RTC_DCHECK_GE(cache_lifetime_us_, 0);
}

RTCStatsCollector::~RTCStatsCollector() {
  RTC_DCHECK_EQ(num_pending_partial_reports_, 0);
}

void RTCStatsCollector::GetStatsReport(
    rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {
  GetStatsReportInternal(RequestInfo(std::move(callback)));
}

void RTCStatsCollector::GetStatsReport(
    rtc::scoped_refptr<RtpSenderInternal> selector,
    rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {
  GetStatsReportInternal(RequestInfo(std::move(selector), std::move(callback)));
}

void RTCStatsCollector::GetStatsReport(
    rtc::scoped_refptr<RtpReceiverInternal> selector,
    rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {
  GetStatsReportInternal(RequestInfo(std::move(selector), std::move(callback)));
}

void RTCStatsCollector::GetStatsReportInternal(
    RTCStatsCollector::RequestInfo request) {
  RTC_DCHECK_RUN_ON(signaling_thread_);
  requests_.push_back(std::move(request));

  // "Now" using a monotonically increasing timer.
  int64_t cache_now_us = rtc::TimeMicros();
  if (cached_report_ &&
      cache_now_us - cache_timestamp_us_ <= cache_lifetime_us_) {
    // We have a fresh cached report to deliver. Deliver asynchronously, since
    // the caller may not be expecting a synchronous callback, and it avoids
    // reentrancy problems.
    signaling_thread_->PostTask(
        absl::bind_front(&RTCStatsCollector::DeliverCachedReport,
                         rtc::scoped_refptr<RTCStatsCollector>(this),
                         cached_report_, std::move(requests_)));
  } else if (!num_pending_partial_reports_) {
    // Only start gathering stats if we're not already gathering stats. In the
    // case of already gathering stats, `callback_` will be invoked when there
    // are no more pending partial reports.

    Timestamp timestamp =
        stats_timestamp_with_environment_clock_
            ?
            // "Now" using a monotonically increasing timer.
            env_.clock().CurrentTime()
            :
            // "Now" using a system clock, relative to the UNIX epoch (Jan 1,
            // 1970, UTC), in microseconds. The system clock could be modified
            // and is not necessarily monotonically increasing.
            Timestamp::Micros(rtc::TimeUTCMicros());

    num_pending_partial_reports_ = 2;
    partial_report_timestamp_us_ = cache_now_us;

    // Prepare `transceiver_stats_infos_` and `call_stats_` for use in
    // `ProducePartialResultsOnNetworkThread` and
    // `ProducePartialResultsOnSignalingThread`.
    PrepareTransceiverStatsInfosAndCallStats_s_w_n();
    // Don't touch `network_report_` on the signaling thread until
    // ProducePartialResultsOnNetworkThread() has signaled the
    // `network_report_event_`.
    network_report_event_.Reset();
    rtc::scoped_refptr<RTCStatsCollector> collector(this);
    network_thread_->PostTask([collector,
                               sctp_transport_name = pc_->sctp_transport_name(),
                               timestamp]() mutable {
      collector->ProducePartialResultsOnNetworkThread(
          timestamp, std::move(sctp_transport_name));
    });
    ProducePartialResultsOnSignalingThread(timestamp);
  }
}

void RTCStatsCollector::ClearCachedStatsReport() {
  RTC_DCHECK_RUN_ON(signaling_thread_);
  cached_report_ = nullptr;
  MutexLock lock(&cached_certificates_mutex_);
  cached_certificates_by_transport_.clear();
}

void RTCStatsCollector::WaitForPendingRequest() {
  RTC_DCHECK_RUN_ON(signaling_thread_);
  // If a request is pending, blocks until the `network_report_event_` is
  // signaled and then delivers the result. Otherwise this is a NO-OP.
  MergeNetworkReport_s();
}

void RTCStatsCollector::ProducePartialResultsOnSignalingThread(
    Timestamp timestamp) {
  RTC_DCHECK_RUN_ON(signaling_thread_);
  rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;

  partial_report_ = RTCStatsReport::Create(timestamp);

  ProducePartialResultsOnSignalingThreadImpl(timestamp, partial_report_.get());

  // ProducePartialResultsOnSignalingThread() is running synchronously on the
  // signaling thread, so it is always the first partial result delivered on the
  // signaling thread. The request is not complete until MergeNetworkReport_s()
  // happens; we don't have to do anything here.
  RTC_DCHECK_GT(num_pending_partial_reports_, 1);
  --num_pending_partial_reports_;
}

void RTCStatsCollector::ProducePartialResultsOnSignalingThreadImpl(
    Timestamp timestamp,
    RTCStatsReport* partial_report) {
  RTC_DCHECK_RUN_ON(signaling_thread_);
  rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;

  ProduceMediaSourceStats_s(timestamp, partial_report);
  ProducePeerConnectionStats_s(timestamp, partial_report);
  ProduceAudioPlayoutStats_s(timestamp, partial_report);
}

void RTCStatsCollector::ProducePartialResultsOnNetworkThread(
    Timestamp timestamp,
    std::optional<std::string> sctp_transport_name) {
  TRACE_EVENT0("webrtc",
               "RTCStatsCollector::ProducePartialResultsOnNetworkThread");
  RTC_DCHECK_RUN_ON(network_thread_);
  rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;

  // Touching `network_report_` on this thread is safe by this method because
  // `network_report_event_` is reset before this method is invoked.
  network_report_ = RTCStatsReport::Create(timestamp);

  ProduceDataChannelStats_n(timestamp, network_report_.get());

  std::set<std::string> transport_names;
  if (sctp_transport_name) {
    transport_names.emplace(std::move(*sctp_transport_name));
  }

  for (const auto& info : transceiver_stats_infos_) {
    if (info.transport_name)
      transport_names.insert(*info.transport_name);
  }

  std::map<std::string, cricket::TransportStats> transport_stats_by_name =
      pc_->GetTransportStatsByNames(transport_names);
  std::map<std::string, CertificateStatsPair> transport_cert_stats =
      PrepareTransportCertificateStats_n(transport_stats_by_name);

  ProducePartialResultsOnNetworkThreadImpl(timestamp, transport_stats_by_name,
                                           transport_cert_stats,
                                           network_report_.get());

  // Signal that it is now safe to touch `network_report_` on the signaling
  // thread, and post a task to merge it into the final results.
  network_report_event_.Set();
  rtc::scoped_refptr<RTCStatsCollector> collector(this);
  signaling_thread_->PostTask(
      [collector] { collector->MergeNetworkReport_s(); });
}

void RTCStatsCollector::ProducePartialResultsOnNetworkThreadImpl(
    Timestamp timestamp,
    const std::map<std::string, cricket::TransportStats>&
        transport_stats_by_name,
    const std::map<std::string, CertificateStatsPair>& transport_cert_stats,
    RTCStatsReport* partial_report) {
  RTC_DCHECK_RUN_ON(network_thread_);
  rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;

  ProduceCertificateStats_n(timestamp, transport_cert_stats, partial_report);
  ProduceIceCandidateAndPairStats_n(timestamp, transport_stats_by_name,
                                    call_stats_, partial_report);
  ProduceTransportStats_n(timestamp, transport_stats_by_name,
                          transport_cert_stats, partial_report);
  ProduceRTPStreamStats_n(timestamp, transceiver_stats_infos_, partial_report);
}

void RTCStatsCollector::MergeNetworkReport_s() {
  RTC_DCHECK_RUN_ON(signaling_thread_);
  // The `network_report_event_` must be signaled for it to be safe to touch
  // `network_report_`. This is normally not blocking, but if
  // WaitForPendingRequest() is called while a request is pending, we might have
  // to wait until the network thread is done touching `network_report_`.
  network_report_event_.Wait(rtc::Event::kForever);
  if (!network_report_) {
    // Normally, MergeNetworkReport_s() is executed because it is posted from
    // the network thread. But if WaitForPendingRequest() is called while a
    // request is pending, an early call to MergeNetworkReport_s() is made,
    // merging the report and setting `network_report_` to null. If so, when the
    // previously posted MergeNetworkReport_s() is later executed, the report is
    // already null and nothing needs to be done here.
    return;
  }
  RTC_DCHECK_GT(num_pending_partial_reports_, 0);
  RTC_DCHECK(partial_report_);
  partial_report_->TakeMembersFrom(network_report_);
  network_report_ = nullptr;
  --num_pending_partial_reports_;
  // `network_report_` is currently the only partial report collected
  // asynchronously, so `num_pending_partial_reports_` must now be 0 and we are
  // ready to deliver the result.
  RTC_DCHECK_EQ(num_pending_partial_reports_, 0);
  cache_timestamp_us_ = partial_report_timestamp_us_;
  cached_report_ = partial_report_;
  partial_report_ = nullptr;
  transceiver_stats_infos_.clear();
  // Trace WebRTC Stats when getStats is called on Javascript.
  // This allows access to WebRTC stats from trace logs. To enable them,
  // select the "webrtc_stats" category when recording traces.
  TRACE_EVENT_INSTANT1("webrtc_stats""webrtc_stats", TRACE_EVENT_SCOPE_GLOBAL,
                       "report", cached_report_->ToJson());

  // Deliver report and clear `requests_`.
  std::vector<RequestInfo> requests;
  requests.swap(requests_);
  DeliverCachedReport(cached_report_, std::move(requests));
}

void RTCStatsCollector::DeliverCachedReport(
    rtc::scoped_refptr<const RTCStatsReport> cached_report,
    std::vector<RTCStatsCollector::RequestInfo> requests) {
  RTC_DCHECK_RUN_ON(signaling_thread_);
  RTC_DCHECK(!requests.empty());
  RTC_DCHECK(cached_report);

  for (const RequestInfo& request : requests) {
    if (request.filter_mode() == RequestInfo::FilterMode::kAll) {
      request.callback()->OnStatsDelivered(cached_report);
    } else {
      bool filter_by_sender_selector;
      rtc::scoped_refptr<RtpSenderInternal> sender_selector;
      rtc::scoped_refptr<RtpReceiverInternal> receiver_selector;
      if (request.filter_mode() == RequestInfo::FilterMode::kSenderSelector) {
        filter_by_sender_selector = true;
        sender_selector = request.sender_selector();
      } else {
        RTC_DCHECK(request.filter_mode() ==
                   RequestInfo::FilterMode::kReceiverSelector);
        filter_by_sender_selector = false;
        receiver_selector = request.receiver_selector();
      }
      request.callback()->OnStatsDelivered(CreateReportFilteredBySelector(
          filter_by_sender_selector, cached_report, sender_selector,
          receiver_selector));
    }
  }
}

void RTCStatsCollector::ProduceCertificateStats_n(
    Timestamp timestamp,
    const std::map<std::string, CertificateStatsPair>& transport_cert_stats,
    RTCStatsReport* report) const {
  RTC_DCHECK_RUN_ON(network_thread_);
  rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;

  for (const auto& transport_cert_stats_pair : transport_cert_stats) {
    if (transport_cert_stats_pair.second.local) {
      ProduceCertificateStatsFromSSLCertificateStats(
          timestamp, *transport_cert_stats_pair.second.local.get(), report);
    }
    if (transport_cert_stats_pair.second.remote) {
      ProduceCertificateStatsFromSSLCertificateStats(
          timestamp, *transport_cert_stats_pair.second.remote.get(), report);
    }
  }
}

void RTCStatsCollector::ProduceDataChannelStats_n(
    Timestamp timestamp,
    RTCStatsReport* report) const {
  RTC_DCHECK_RUN_ON(network_thread_);
  rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
  std::vector<DataChannelStats> data_stats = pc_->GetDataChannelStats();
  for (const auto& stats : data_stats) {
    auto data_channel_stats = std::make_unique<RTCDataChannelStats>(
        "D" + rtc::ToString(stats.internal_id), timestamp);
    data_channel_stats->label = std::move(stats.label);
    data_channel_stats->protocol = std::move(stats.protocol);
    if (stats.id >= 0) {
      // Do not set this value before the DTLS handshake is finished
      // and filter out the magic value -1.
      data_channel_stats->data_channel_identifier = stats.id;
    }
    data_channel_stats->state = DataStateToRTCDataChannelState(stats.state);
    data_channel_stats->messages_sent = stats.messages_sent;
    data_channel_stats->bytes_sent = stats.bytes_sent;
    data_channel_stats->messages_received = stats.messages_received;
    data_channel_stats->bytes_received = stats.bytes_received;
    report->AddStats(std::move(data_channel_stats));
  }
}

void RTCStatsCollector::ProduceIceCandidateAndPairStats_n(
    Timestamp timestamp,
    const std::map<std::string, cricket::TransportStats>&
        transport_stats_by_name,
    const Call::Stats& call_stats,
    RTCStatsReport* report) const {
  RTC_DCHECK_RUN_ON(network_thread_);
  rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;

  for (const auto& entry : transport_stats_by_name) {
    const std::string& transport_name = entry.first;
    const cricket::TransportStats& transport_stats = entry.second;
    for (const auto& channel_stats : transport_stats.channel_stats) {
      std::string transport_id = RTCTransportStatsIDFromTransportChannel(
          transport_name, channel_stats.component);
      for (const auto& info :
           channel_stats.ice_transport_stats.connection_infos) {
        auto candidate_pair_stats = std::make_unique<RTCIceCandidatePairStats>(
            RTCIceCandidatePairStatsIDFromConnectionInfo(info), timestamp);

        candidate_pair_stats->transport_id = transport_id;
        candidate_pair_stats->local_candidate_id = ProduceIceCandidateStats(
            timestamp, info.local_candidate, true, transport_id, report);
        candidate_pair_stats->remote_candidate_id = ProduceIceCandidateStats(
            timestamp, info.remote_candidate, false, transport_id, report);
        candidate_pair_stats->state =
            IceCandidatePairStateToRTCStatsIceCandidatePairState(info.state);
        candidate_pair_stats->priority = info.priority;
        candidate_pair_stats->nominated = info.nominated;
        // TODO(hbos): This writable is different than the spec. It goes to
        // false after a certain amount of time without a response passes.
        // https://crbug.com/633550
        candidate_pair_stats->writable = info.writable;
        // Note that sent_total_packets includes discarded packets but
        // sent_total_bytes does not.
        candidate_pair_stats->packets_sent = static_cast<uint64_t>(
            info.sent_total_packets - info.sent_discarded_packets);
        candidate_pair_stats->packets_discarded_on_send =
            static_cast<uint64_t>(info.sent_discarded_packets);
        candidate_pair_stats->packets_received =
            static_cast<uint64_t>(info.packets_received);
        candidate_pair_stats->bytes_sent =
            static_cast<uint64_t>(info.sent_total_bytes);
        candidate_pair_stats->bytes_discarded_on_send =
            static_cast<uint64_t>(info.sent_discarded_bytes);
        candidate_pair_stats->bytes_received =
            static_cast<uint64_t>(info.recv_total_bytes);
        candidate_pair_stats->total_round_trip_time =
            static_cast<double>(info.total_round_trip_time_ms) /
            rtc::kNumMillisecsPerSec;
        if (info.current_round_trip_time_ms.has_value()) {
          candidate_pair_stats->current_round_trip_time =
              static_cast<double>(*info.current_round_trip_time_ms) /
              rtc::kNumMillisecsPerSec;
        }
        if (info.best_connection) {
          // The bandwidth estimations we have are for the selected candidate
          // pair ("info.best_connection").
          RTC_DCHECK_GE(call_stats.send_bandwidth_bps, 0);
          RTC_DCHECK_GE(call_stats.recv_bandwidth_bps, 0);
          if (call_stats.send_bandwidth_bps > 0) {
            candidate_pair_stats->available_outgoing_bitrate =
                static_cast<double>(call_stats.send_bandwidth_bps);
          }
          if (call_stats.recv_bandwidth_bps > 0) {
            candidate_pair_stats->available_incoming_bitrate =
                static_cast<double>(call_stats.recv_bandwidth_bps);
          }
        }
        candidate_pair_stats->requests_received =
            static_cast<uint64_t>(info.recv_ping_requests);
        candidate_pair_stats->requests_sent =
            static_cast<uint64_t>(info.sent_ping_requests_total);
        candidate_pair_stats->responses_received =
            static_cast<uint64_t>(info.recv_ping_responses);
        candidate_pair_stats->responses_sent =
            static_cast<uint64_t>(info.sent_ping_responses);
        RTC_DCHECK_GE(info.sent_ping_requests_total,
                      info.sent_ping_requests_before_first_response);
        candidate_pair_stats->consent_requests_sent = static_cast<uint64_t>(
            info.sent_ping_requests_total -
            info.sent_ping_requests_before_first_response);

        if (info.last_data_received.has_value()) {
          candidate_pair_stats->last_packet_received_timestamp =
              static_cast<double>(info.last_data_received->ms());
        }
        if (info.last_data_sent) {
          candidate_pair_stats->last_packet_sent_timestamp =
              static_cast<double>(info.last_data_sent->ms());
        }

        report->AddStats(std::move(candidate_pair_stats));
      }

      // Produce local candidate stats. If a transport exists these will already
      // have been produced.
      for (const auto& candidate_stats :
           channel_stats.ice_transport_stats.candidate_stats_list) {
        const auto& candidate = candidate_stats.candidate();
        ProduceIceCandidateStats(timestamp, candidate, true, transport_id,
                                 report);
      }
    }
  }
}

void RTCStatsCollector::ProduceMediaSourceStats_s(
    Timestamp timestamp,
    RTCStatsReport* report) const {
  RTC_DCHECK_RUN_ON(signaling_thread_);
  rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;

  for (const RtpTransceiverStatsInfo& transceiver_stats_info :
       transceiver_stats_infos_) {
    const auto& track_media_info_map =
        transceiver_stats_info.track_media_info_map;
    for (const auto& sender : transceiver_stats_info.transceiver->senders()) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=96 G=93

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge