/* * 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.
*/
// 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( constchar 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();
}
constchar* 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;
}
}
constchar* 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;
}
}
constchar* 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;
}
}
constchar* 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;
}
}
constchar* 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;
}
}
constchar* 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 {};
}
constchar* 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 (constauto& elem : durations_ms) {
result[QualityLimitationReasonToRTCQualityLimitationReason(elem.first)] =
elem.second / static_cast<double>(rtc::kNumMillisecsPerSec);
} return result;
}
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, constbool 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. constauto* transport_from_id = report.Get(transport_id); if (transport_from_id) { constauto& 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. constauto* 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; constauto& 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;
}
}
// "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_)));
} elseif (!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());
// 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::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();
}
// 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_;
}
// 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);
// 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::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());
// Produce local candidate stats. If a transport exists these will already // have been produced. for (constauto& candidate_stats :
channel_stats.ice_transport_stats.candidate_stats_list) { constauto& candidate = candidate_stats.candidate();
ProduceIceCandidateStats(timestamp, candidate, true, transport_id,
report);
}
}
}
}
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.