Files
platform-external-webrtc/pc/rtcstatscollector.cc
Steve Anton d5585ca956 Move almost all references from WebRtcSession to PeerConnection
WebRtcSession is being merged into PeerConnection, and to make the
code review easier this is the first step towards achieving that.

Bug: webrtc:8323
Change-Id: I33778e46f20cb14089dff4328947868e207476bd
Reviewed-on: https://webrtc-review.googlesource.com/8760
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20413}
2017-10-24 17:59:20 +00:00

1269 lines
52 KiB
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/rtcstatscollector.h"
#include <memory>
#include <sstream>
#include <utility>
#include <vector>
#include "api/candidate.h"
#include "api/mediastreaminterface.h"
#include "api/peerconnectioninterface.h"
#include "media/base/mediachannel.h"
#include "p2p/base/p2pconstants.h"
#include "p2p/base/port.h"
#include "pc/peerconnection.h"
#include "rtc_base/checks.h"
#include "rtc_base/stringutils.h"
#include "rtc_base/timeutils.h"
#include "rtc_base/trace_event.h"
namespace webrtc {
namespace {
std::string RTCCertificateIDFromFingerprint(const std::string& fingerprint) {
return "RTCCertificate_" + fingerprint;
}
std::string RTCCodecStatsIDFromDirectionMediaAndPayload(
bool inbound, bool audio, uint32_t payload_type) {
// TODO(hbos): The present codec ID assignment is not sufficient to support
// Unified Plan or unbundled connections in all cases. When we are able to
// handle multiple m= lines of the same media type (and multiple BaseChannels
// for the same type is possible?) this needs to be updated to differentiate
// the transport being used, and stats need to be collected for all of them.
if (inbound) {
return audio ? "RTCCodec_InboundAudio_" + rtc::ToString<>(payload_type)
: "RTCCodec_InboundVideo_" + rtc::ToString<>(payload_type);
}
return audio ? "RTCCodec_OutboundAudio_" + rtc::ToString<>(payload_type)
: "RTCCodec_OutboundVideo_" + rtc::ToString<>(payload_type);
}
std::string RTCIceCandidatePairStatsIDFromConnectionInfo(
const cricket::ConnectionInfo& info) {
return "RTCIceCandidatePair_" + info.local_candidate.id() + "_" +
info.remote_candidate.id();
}
std::string RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
bool is_local, const char* kind, const std::string& id, uint32_t ssrc) {
RTC_DCHECK(kind == MediaStreamTrackInterface::kAudioKind ||
kind == MediaStreamTrackInterface::kVideoKind);
std::ostringstream oss;
oss << (is_local ? "RTCMediaStreamTrack_local_"
: "RTCMediaStreamTrack_remote_");
oss << kind << "_";
oss << id << "_";
oss << ssrc;
return oss.str();
}
std::string RTCTransportStatsIDFromTransportChannel(
const std::string& transport_name, int channel_component) {
return "RTCTransport_" + transport_name + "_" +
rtc::ToString<>(channel_component);
}
std::string RTCTransportStatsIDFromBaseChannel(
const std::map<std::string, std::string>& proxy_to_transport,
const cricket::BaseChannel& base_channel) {
auto proxy_it = proxy_to_transport.find(base_channel.content_name());
if (proxy_it == proxy_to_transport.cend())
return "";
return RTCTransportStatsIDFromTransportChannel(
proxy_it->second, cricket::ICE_CANDIDATE_COMPONENT_RTP);
}
std::string RTCInboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) {
return audio ? "RTCInboundRTPAudioStream_" + rtc::ToString<>(ssrc)
: "RTCInboundRTPVideoStream_" + rtc::ToString<>(ssrc);
}
std::string RTCOutboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) {
return audio ? "RTCOutboundRTPAudioStream_" + rtc::ToString<>(ssrc)
: "RTCOutboundRTPVideoStream_" + rtc::ToString<>(ssrc);
}
const char* CandidateTypeToRTCIceCandidateType(const std::string& type) {
if (type == cricket::LOCAL_PORT_TYPE)
return RTCIceCandidateType::kHost;
if (type == cricket::STUN_PORT_TYPE)
return RTCIceCandidateType::kSrflx;
if (type == cricket::PRFLX_PORT_TYPE)
return RTCIceCandidateType::kPrflx;
if (type == cricket::RELAY_PORT_TYPE)
return RTCIceCandidateType::kRelay;
RTC_NOTREACHED();
return nullptr;
}
const char* DataStateToRTCDataChannelState(
DataChannelInterface::DataState state) {
switch (state) {
case DataChannelInterface::kConnecting:
return RTCDataChannelState::kConnecting;
case DataChannelInterface::kOpen:
return RTCDataChannelState::kOpen;
case DataChannelInterface::kClosing:
return RTCDataChannelState::kClosing;
case DataChannelInterface::kClosed:
return RTCDataChannelState::kClosed;
default:
RTC_NOTREACHED();
return nullptr;
}
}
const char* IceCandidatePairStateToRTCStatsIceCandidatePairState(
cricket::IceCandidatePairState state) {
switch (state) {
case cricket::IceCandidatePairState::WAITING:
return RTCStatsIceCandidatePairState::kWaiting;
case cricket::IceCandidatePairState::IN_PROGRESS:
return RTCStatsIceCandidatePairState::kInProgress;
case cricket::IceCandidatePairState::SUCCEEDED:
return RTCStatsIceCandidatePairState::kSucceeded;
case cricket::IceCandidatePairState::FAILED:
return RTCStatsIceCandidatePairState::kFailed;
default:
RTC_NOTREACHED();
return nullptr;
}
}
const char* DtlsTransportStateToRTCDtlsTransportState(
cricket::DtlsTransportState state) {
switch (state) {
case cricket::DTLS_TRANSPORT_NEW:
return RTCDtlsTransportState::kNew;
case cricket::DTLS_TRANSPORT_CONNECTING:
return RTCDtlsTransportState::kConnecting;
case cricket::DTLS_TRANSPORT_CONNECTED:
return RTCDtlsTransportState::kConnected;
case cricket::DTLS_TRANSPORT_CLOSED:
return RTCDtlsTransportState::kClosed;
case cricket::DTLS_TRANSPORT_FAILED:
return RTCDtlsTransportState::kFailed;
default:
RTC_NOTREACHED();
return nullptr;
}
}
double DoubleAudioLevelFromIntAudioLevel(int audio_level) {
RTC_DCHECK_GE(audio_level, 0);
RTC_DCHECK_LE(audio_level, 32767);
return audio_level / 32767.0;
}
std::unique_ptr<RTCCodecStats> CodecStatsFromRtpCodecParameters(
uint64_t timestamp_us, bool inbound, bool audio,
const RtpCodecParameters& codec_params) {
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::unique_ptr<RTCCodecStats> codec_stats(new RTCCodecStats(
RTCCodecStatsIDFromDirectionMediaAndPayload(inbound, audio, payload_type),
timestamp_us));
codec_stats->payload_type = payload_type;
codec_stats->mime_type = codec_params.mime_type();
if (codec_params.clock_rate) {
codec_stats->clock_rate = static_cast<uint32_t>(*codec_params.clock_rate);
}
return codec_stats;
}
void SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
const MediaStreamTrackInterface& track,
RTCMediaStreamTrackStats* track_stats) {
track_stats->track_identifier = track.id();
track_stats->ended = (track.state() == MediaStreamTrackInterface::kEnded);
}
// 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();
// TODO(hbos): Support the remote case. crbug.com/657855
inbound_stats->is_remote = false;
inbound_stats->packets_received =
static_cast<uint32_t>(media_receiver_info.packets_rcvd);
inbound_stats->bytes_received =
static_cast<uint64_t>(media_receiver_info.bytes_rcvd);
inbound_stats->packets_lost =
static_cast<uint32_t>(media_receiver_info.packets_lost);
inbound_stats->fraction_lost =
static_cast<double>(media_receiver_info.fraction_lost);
}
void SetInboundRTPStreamStatsFromVoiceReceiverInfo(
const cricket::VoiceReceiverInfo& voice_receiver_info,
RTCInboundRTPStreamStats* inbound_audio) {
SetInboundRTPStreamStatsFromMediaReceiverInfo(
voice_receiver_info, inbound_audio);
inbound_audio->media_type = "audio";
if (voice_receiver_info.codec_payload_type) {
inbound_audio->codec_id =
RTCCodecStatsIDFromDirectionMediaAndPayload(
true, true, *voice_receiver_info.codec_payload_type);
}
inbound_audio->jitter =
static_cast<double>(voice_receiver_info.jitter_ms) /
rtc::kNumMillisecsPerSec;
// |fir_count|, |pli_count| and |sli_count| are only valid for video and are
// purposefully left undefined for audio.
}
void SetInboundRTPStreamStatsFromVideoReceiverInfo(
const cricket::VideoReceiverInfo& video_receiver_info,
RTCInboundRTPStreamStats* inbound_video) {
SetInboundRTPStreamStatsFromMediaReceiverInfo(
video_receiver_info, inbound_video);
inbound_video->media_type = "video";
if (video_receiver_info.codec_payload_type) {
inbound_video->codec_id =
RTCCodecStatsIDFromDirectionMediaAndPayload(
true, false, *video_receiver_info.codec_payload_type);
}
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->nack_count =
static_cast<uint32_t>(video_receiver_info.nacks_sent);
inbound_video->frames_decoded = video_receiver_info.frames_decoded;
if (video_receiver_info.qp_sum)
inbound_video->qp_sum = *video_receiver_info.qp_sum;
}
// Provides the media independent counters (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();
// TODO(hbos): Support the remote case. crbug.com/657856
outbound_stats->is_remote = false;
outbound_stats->packets_sent =
static_cast<uint32_t>(media_sender_info.packets_sent);
outbound_stats->bytes_sent =
static_cast<uint64_t>(media_sender_info.bytes_sent);
}
void SetOutboundRTPStreamStatsFromVoiceSenderInfo(
const cricket::VoiceSenderInfo& voice_sender_info,
RTCOutboundRTPStreamStats* outbound_audio) {
SetOutboundRTPStreamStatsFromMediaSenderInfo(
voice_sender_info, outbound_audio);
outbound_audio->media_type = "audio";
if (voice_sender_info.codec_payload_type) {
outbound_audio->codec_id =
RTCCodecStatsIDFromDirectionMediaAndPayload(
false, true, *voice_sender_info.codec_payload_type);
}
// |fir_count|, |pli_count| and |sli_count| are only valid for video and are
// purposefully left undefined for audio.
}
void SetOutboundRTPStreamStatsFromVideoSenderInfo(
const cricket::VideoSenderInfo& video_sender_info,
RTCOutboundRTPStreamStats* outbound_video) {
SetOutboundRTPStreamStatsFromMediaSenderInfo(
video_sender_info, outbound_video);
outbound_video->media_type = "video";
if (video_sender_info.codec_payload_type) {
outbound_video->codec_id =
RTCCodecStatsIDFromDirectionMediaAndPayload(
false, false, *video_sender_info.codec_payload_type);
}
outbound_video->fir_count =
static_cast<uint32_t>(video_sender_info.firs_rcvd);
outbound_video->pli_count =
static_cast<uint32_t>(video_sender_info.plis_rcvd);
outbound_video->nack_count =
static_cast<uint32_t>(video_sender_info.nacks_rcvd);
if (video_sender_info.qp_sum)
outbound_video->qp_sum = *video_sender_info.qp_sum;
outbound_video->frames_encoded = video_sender_info.frames_encoded;
}
void ProduceCertificateStatsFromSSLCertificateStats(
int64_t timestamp_us, 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_us);
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(
int64_t timestamp_us, const cricket::Candidate& candidate, bool is_local,
const std::string& transport_id, RTCStatsReport* report) {
const std::string& id = "RTCIceCandidate_" + candidate.id();
const RTCStats* stats = report->Get(id);
if (!stats) {
std::unique_ptr<RTCIceCandidateStats> candidate_stats;
if (is_local)
candidate_stats.reset(new RTCLocalIceCandidateStats(id, timestamp_us));
else
candidate_stats.reset(new RTCRemoteIceCandidateStats(id, timestamp_us));
candidate_stats->transport_id = transport_id;
candidate_stats->ip = candidate.address().ipaddr().ToString();
candidate_stats->port = static_cast<int32_t>(candidate.address().port());
candidate_stats->protocol = candidate.protocol();
candidate_stats->candidate_type = CandidateTypeToRTCIceCandidateType(
candidate.type());
candidate_stats->priority = static_cast<int32_t>(candidate.priority());
stats = candidate_stats.get();
report->AddStats(std::move(candidate_stats));
}
RTC_DCHECK_EQ(stats->type(), is_local ? RTCLocalIceCandidateStats::kType
: RTCRemoteIceCandidateStats::kType);
return stats->id();
}
std::unique_ptr<RTCMediaStreamTrackStats>
ProduceMediaStreamTrackStatsFromVoiceSenderInfo(
int64_t timestamp_us,
const AudioTrackInterface& audio_track,
const cricket::VoiceSenderInfo& voice_sender_info) {
std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats(
new RTCMediaStreamTrackStats(
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
true, MediaStreamTrackInterface::kAudioKind, audio_track.id(),
voice_sender_info.ssrc()),
timestamp_us,
RTCMediaStreamTrackKind::kAudio));
SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
audio_track, audio_track_stats.get());
audio_track_stats->remote_source = false;
audio_track_stats->detached = false;
if (voice_sender_info.audio_level >= 0) {
audio_track_stats->audio_level = DoubleAudioLevelFromIntAudioLevel(
voice_sender_info.audio_level);
}
audio_track_stats->total_audio_energy = voice_sender_info.total_input_energy;
audio_track_stats->total_samples_duration =
voice_sender_info.total_input_duration;
if (voice_sender_info.echo_return_loss != -100) {
audio_track_stats->echo_return_loss = static_cast<double>(
voice_sender_info.echo_return_loss);
}
if (voice_sender_info.echo_return_loss_enhancement != -100) {
audio_track_stats->echo_return_loss_enhancement = static_cast<double>(
voice_sender_info.echo_return_loss_enhancement);
}
return audio_track_stats;
}
std::unique_ptr<RTCMediaStreamTrackStats>
ProduceMediaStreamTrackStatsFromVoiceReceiverInfo(
int64_t timestamp_us,
const AudioTrackInterface& audio_track,
const cricket::VoiceReceiverInfo& voice_receiver_info) {
std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats(
new RTCMediaStreamTrackStats(
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
false, MediaStreamTrackInterface::kAudioKind, audio_track.id(),
voice_receiver_info.ssrc()),
timestamp_us,
RTCMediaStreamTrackKind::kAudio));
SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
audio_track, audio_track_stats.get());
audio_track_stats->remote_source = true;
audio_track_stats->detached = false;
if (voice_receiver_info.audio_level >= 0) {
audio_track_stats->audio_level = DoubleAudioLevelFromIntAudioLevel(
voice_receiver_info.audio_level);
}
audio_track_stats->jitter_buffer_delay =
voice_receiver_info.jitter_buffer_delay_seconds;
audio_track_stats->total_audio_energy =
voice_receiver_info.total_output_energy;
audio_track_stats->total_samples_received =
voice_receiver_info.total_samples_received;
audio_track_stats->total_samples_duration =
voice_receiver_info.total_output_duration;
audio_track_stats->concealed_samples = voice_receiver_info.concealed_samples;
audio_track_stats->concealment_events =
voice_receiver_info.concealment_events;
return audio_track_stats;
}
std::unique_ptr<RTCMediaStreamTrackStats>
ProduceMediaStreamTrackStatsFromVideoSenderInfo(
int64_t timestamp_us,
const VideoTrackInterface& video_track,
const cricket::VideoSenderInfo& video_sender_info) {
std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats(
new RTCMediaStreamTrackStats(
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
true, MediaStreamTrackInterface::kVideoKind, video_track.id(),
video_sender_info.ssrc()),
timestamp_us,
RTCMediaStreamTrackKind::kVideo));
SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
video_track, video_track_stats.get());
video_track_stats->remote_source = false;
video_track_stats->detached = false;
video_track_stats->frame_width = static_cast<uint32_t>(
video_sender_info.send_frame_width);
video_track_stats->frame_height = static_cast<uint32_t>(
video_sender_info.send_frame_height);
// TODO(hbos): Will reduce this by frames dropped due to congestion control
// when available. crbug.com/659137
video_track_stats->frames_sent = video_sender_info.frames_encoded;
return video_track_stats;
}
std::unique_ptr<RTCMediaStreamTrackStats>
ProduceMediaStreamTrackStatsFromVideoReceiverInfo(
int64_t timestamp_us,
const VideoTrackInterface& video_track,
const cricket::VideoReceiverInfo& video_receiver_info) {
std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats(
new RTCMediaStreamTrackStats(
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
false, MediaStreamTrackInterface::kVideoKind, video_track.id(),
video_receiver_info.ssrc()),
timestamp_us,
RTCMediaStreamTrackKind::kVideo));
SetMediaStreamTrackStatsFromMediaStreamTrackInterface(
video_track, video_track_stats.get());
video_track_stats->remote_source = true;
video_track_stats->detached = false;
if (video_receiver_info.frame_width > 0 &&
video_receiver_info.frame_height > 0) {
video_track_stats->frame_width = static_cast<uint32_t>(
video_receiver_info.frame_width);
video_track_stats->frame_height = static_cast<uint32_t>(
video_receiver_info.frame_height);
}
video_track_stats->frames_received = video_receiver_info.frames_received;
// TODO(hbos): When we support receiving simulcast, this should be the total
// number of frames correctly decoded, independent of which SSRC it was
// received from. Since we don't support that, this is correct and is the same
// value as "RTCInboundRTPStreamStats.framesDecoded". crbug.com/659137
video_track_stats->frames_decoded = video_receiver_info.frames_decoded;
RTC_DCHECK_GE(video_receiver_info.frames_received,
video_receiver_info.frames_rendered);
video_track_stats->frames_dropped = video_receiver_info.frames_received -
video_receiver_info.frames_rendered;
return video_track_stats;
}
void ProduceMediaStreamAndTrackStats(
int64_t timestamp_us,
const TrackMediaInfoMap& track_media_info_map,
rtc::scoped_refptr<StreamCollectionInterface> streams,
bool is_local,
RTCStatsReport* report) {
// TODO(hbos): When "AddTrack" is implemented we should iterate tracks to
// find which streams exist, not iterate streams to find tracks.
// crbug.com/659137
// TODO(hbos): Return stats of detached tracks. We have to perform stats
// gathering at the time of detachment to get accurate stats and timestamps.
// crbug.com/659137
if (!streams)
return;
for (size_t i = 0; i < streams->count(); ++i) {
MediaStreamInterface* stream = streams->at(i);
std::unique_ptr<RTCMediaStreamStats> stream_stats(
new RTCMediaStreamStats(
(is_local ? "RTCMediaStream_local_" : "RTCMediaStream_remote_") +
stream->label(), timestamp_us));
stream_stats->stream_identifier = stream->label();
stream_stats->track_ids = std::vector<std::string>();
// The track stats are per-attachment to the connection. There can be one
// for receiving (remote) tracks and multiple attachments for sending
// (local) tracks.
if (is_local) {
// Local Audio Tracks
for (const rtc::scoped_refptr<AudioTrackInterface>& audio_track :
stream->GetAudioTracks()) {
const std::vector<cricket::VoiceSenderInfo*>* voice_sender_infos =
track_media_info_map.GetVoiceSenderInfos(*audio_track);
if (!voice_sender_infos) {
continue;
}
for (const auto& voice_sender_info : *voice_sender_infos) {
std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats =
ProduceMediaStreamTrackStatsFromVoiceSenderInfo(
timestamp_us, *audio_track, *voice_sender_info);
stream_stats->track_ids->push_back(audio_track_stats->id());
report->AddStats(std::move(audio_track_stats));
}
}
// Local Video Tracks
for (const rtc::scoped_refptr<VideoTrackInterface>& video_track :
stream->GetVideoTracks()) {
const std::vector<cricket::VideoSenderInfo*>* video_sender_infos =
track_media_info_map.GetVideoSenderInfos(*video_track);
if (!video_sender_infos) {
continue;
}
for (const auto& video_sender_info : *video_sender_infos) {
std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats =
ProduceMediaStreamTrackStatsFromVideoSenderInfo(
timestamp_us, *video_track, *video_sender_info);
stream_stats->track_ids->push_back(video_track_stats->id());
report->AddStats(std::move(video_track_stats));
}
}
} else {
// Remote Audio Tracks
for (const rtc::scoped_refptr<AudioTrackInterface>& audio_track :
stream->GetAudioTracks()) {
const cricket::VoiceReceiverInfo* voice_receiver_info =
track_media_info_map.GetVoiceReceiverInfo(*audio_track);
if (!voice_receiver_info) {
continue;
}
std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats =
ProduceMediaStreamTrackStatsFromVoiceReceiverInfo(
timestamp_us, *audio_track, *voice_receiver_info);
stream_stats->track_ids->push_back(audio_track_stats->id());
report->AddStats(std::move(audio_track_stats));
}
// Remote Video Tracks
for (const rtc::scoped_refptr<VideoTrackInterface>& video_track :
stream->GetVideoTracks()) {
const cricket::VideoReceiverInfo* video_receiver_info =
track_media_info_map.GetVideoReceiverInfo(*video_track);
if (!video_receiver_info) {
continue;
}
std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats =
ProduceMediaStreamTrackStatsFromVideoReceiverInfo(
timestamp_us, *video_track, *video_receiver_info);
stream_stats->track_ids->push_back(video_track_stats->id());
report->AddStats(std::move(video_track_stats));
}
}
report->AddStats(std::move(stream_stats));
}
}
} // namespace
rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create(
PeerConnection* pc, int64_t cache_lifetime_us) {
return rtc::scoped_refptr<RTCStatsCollector>(
new rtc::RefCountedObject<RTCStatsCollector>(pc, cache_lifetime_us));
}
RTCStatsCollector::RTCStatsCollector(PeerConnection* pc,
int64_t cache_lifetime_us)
: pc_(pc),
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),
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);
pc_->SignalDataChannelCreated.connect(
this, &RTCStatsCollector::OnDataChannelCreated);
}
RTCStatsCollector::~RTCStatsCollector() {
RTC_DCHECK_EQ(num_pending_partial_reports_, 0);
}
void RTCStatsCollector::GetStatsReport(
rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {
RTC_DCHECK(signaling_thread_->IsCurrent());
RTC_DCHECK(callback);
callbacks_.push_back(callback);
// "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.
DeliverCachedReport();
} 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.
// "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.
int64_t timestamp_us = rtc::TimeUTCMicros();
num_pending_partial_reports_ = 2;
partial_report_timestamp_us_ = cache_now_us;
// Prepare |channel_name_pairs_| for use in
// |ProducePartialResultsOnNetworkThread|.
channel_name_pairs_.reset(new ChannelNamePairs());
if (pc_->voice_channel()) {
channel_name_pairs_->voice = rtc::Optional<ChannelNamePair>(
ChannelNamePair(pc_->voice_channel()->content_name(),
pc_->voice_channel()->transport_name()));
}
if (pc_->video_channel()) {
channel_name_pairs_->video = rtc::Optional<ChannelNamePair>(
ChannelNamePair(pc_->video_channel()->content_name(),
pc_->video_channel()->transport_name()));
}
if (pc_->rtp_data_channel()) {
channel_name_pairs_->data = rtc::Optional<ChannelNamePair>(
ChannelNamePair(pc_->rtp_data_channel()->content_name(),
pc_->rtp_data_channel()->transport_name()));
}
if (pc_->sctp_content_name()) {
channel_name_pairs_->data =
rtc::Optional<ChannelNamePair>(ChannelNamePair(
*pc_->sctp_content_name(), *pc_->sctp_transport_name()));
}
// Prepare |track_media_info_map_| for use in
// |ProducePartialResultsOnNetworkThread| and
// |ProducePartialResultsOnSignalingThread|.
track_media_info_map_.reset(PrepareTrackMediaInfoMap_s().release());
// Prepare |track_to_id_| for use in |ProducePartialResultsOnNetworkThread|.
// This avoids a possible deadlock if |MediaStreamTrackInterface::id| is
// implemented to invoke on the signaling thread.
track_to_id_ = PrepareTrackToID_s();
// Prepare |call_stats_| here since GetCallStats() will hop to the worker
// thread.
// TODO(holmer): To avoid the hop we could move BWE and BWE stats to the
// network thread, where it more naturally belongs.
call_stats_ = pc_->GetCallStats();
invoker_.AsyncInvoke<void>(
RTC_FROM_HERE, network_thread_,
rtc::Bind(&RTCStatsCollector::ProducePartialResultsOnNetworkThread,
rtc::scoped_refptr<RTCStatsCollector>(this), timestamp_us));
ProducePartialResultsOnSignalingThread(timestamp_us);
}
}
void RTCStatsCollector::ClearCachedStatsReport() {
RTC_DCHECK(signaling_thread_->IsCurrent());
cached_report_ = nullptr;
}
void RTCStatsCollector::WaitForPendingRequest() {
RTC_DCHECK(signaling_thread_->IsCurrent());
if (num_pending_partial_reports_) {
rtc::Thread::Current()->ProcessMessages(0);
while (num_pending_partial_reports_) {
rtc::Thread::Current()->SleepMs(1);
rtc::Thread::Current()->ProcessMessages(0);
}
}
}
void RTCStatsCollector::ProducePartialResultsOnSignalingThread(
int64_t timestamp_us) {
RTC_DCHECK(signaling_thread_->IsCurrent());
rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create(
timestamp_us);
ProduceDataChannelStats_s(timestamp_us, report.get());
ProduceMediaStreamAndTrackStats_s(timestamp_us, report.get());
ProducePeerConnectionStats_s(timestamp_us, report.get());
AddPartialResults(report);
}
void RTCStatsCollector::ProducePartialResultsOnNetworkThread(
int64_t timestamp_us) {
RTC_DCHECK(network_thread_->IsCurrent());
rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create(
timestamp_us);
std::unique_ptr<SessionStats> session_stats =
pc_->GetSessionStats(*channel_name_pairs_);
if (session_stats) {
std::map<std::string, CertificateStatsPair> transport_cert_stats =
PrepareTransportCertificateStats_n(*session_stats);
ProduceCertificateStats_n(
timestamp_us, transport_cert_stats, report.get());
ProduceCodecStats_n(
timestamp_us, *track_media_info_map_, report.get());
ProduceIceCandidateAndPairStats_n(timestamp_us, *session_stats,
track_media_info_map_->video_media_info(),
call_stats_, report.get());
ProduceRTPStreamStats_n(
timestamp_us, *session_stats, *track_media_info_map_, report.get());
ProduceTransportStats_n(
timestamp_us, *session_stats, transport_cert_stats, report.get());
}
AddPartialResults(report);
}
void RTCStatsCollector::AddPartialResults(
const rtc::scoped_refptr<RTCStatsReport>& partial_report) {
if (!signaling_thread_->IsCurrent()) {
invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
rtc::Bind(&RTCStatsCollector::AddPartialResults_s,
rtc::scoped_refptr<RTCStatsCollector>(this),
partial_report));
return;
}
AddPartialResults_s(partial_report);
}
void RTCStatsCollector::AddPartialResults_s(
rtc::scoped_refptr<RTCStatsReport> partial_report) {
RTC_DCHECK(signaling_thread_->IsCurrent());
RTC_DCHECK_GT(num_pending_partial_reports_, 0);
if (!partial_report_)
partial_report_ = partial_report;
else
partial_report_->TakeMembersFrom(partial_report);
--num_pending_partial_reports_;
if (!num_pending_partial_reports_) {
cache_timestamp_us_ = partial_report_timestamp_us_;
cached_report_ = partial_report_;
partial_report_ = nullptr;
channel_name_pairs_.reset();
track_media_info_map_.reset();
track_to_id_.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", "report",
cached_report_->ToJson());
DeliverCachedReport();
}
}
void RTCStatsCollector::DeliverCachedReport() {
RTC_DCHECK(signaling_thread_->IsCurrent());
RTC_DCHECK(!callbacks_.empty());
RTC_DCHECK(cached_report_);
for (const rtc::scoped_refptr<RTCStatsCollectorCallback>& callback :
callbacks_) {
callback->OnStatsDelivered(cached_report_);
}
callbacks_.clear();
}
void RTCStatsCollector::ProduceCertificateStats_n(
int64_t timestamp_us,
const std::map<std::string, CertificateStatsPair>& transport_cert_stats,
RTCStatsReport* report) const {
RTC_DCHECK(network_thread_->IsCurrent());
for (const auto& transport_cert_stats_pair : transport_cert_stats) {
if (transport_cert_stats_pair.second.local) {
ProduceCertificateStatsFromSSLCertificateStats(
timestamp_us, *transport_cert_stats_pair.second.local.get(), report);
}
if (transport_cert_stats_pair.second.remote) {
ProduceCertificateStatsFromSSLCertificateStats(
timestamp_us, *transport_cert_stats_pair.second.remote.get(), report);
}
}
}
void RTCStatsCollector::ProduceCodecStats_n(
int64_t timestamp_us, const TrackMediaInfoMap& track_media_info_map,
RTCStatsReport* report) const {
RTC_DCHECK(network_thread_->IsCurrent());
// Audio
if (track_media_info_map.voice_media_info()) {
// Inbound
for (const auto& pair :
track_media_info_map.voice_media_info()->receive_codecs) {
report->AddStats(CodecStatsFromRtpCodecParameters(
timestamp_us, true, true, pair.second));
}
// Outbound
for (const auto& pair :
track_media_info_map.voice_media_info()->send_codecs) {
report->AddStats(CodecStatsFromRtpCodecParameters(
timestamp_us, false, true, pair.second));
}
}
// Video
if (track_media_info_map.video_media_info()) {
// Inbound
for (const auto& pair :
track_media_info_map.video_media_info()->receive_codecs) {
report->AddStats(CodecStatsFromRtpCodecParameters(
timestamp_us, true, false, pair.second));
}
// Outbound
for (const auto& pair :
track_media_info_map.video_media_info()->send_codecs) {
report->AddStats(CodecStatsFromRtpCodecParameters(
timestamp_us, false, false, pair.second));
}
}
}
void RTCStatsCollector::ProduceDataChannelStats_s(
int64_t timestamp_us, RTCStatsReport* report) const {
RTC_DCHECK(signaling_thread_->IsCurrent());
for (const rtc::scoped_refptr<DataChannel>& data_channel :
pc_->sctp_data_channels()) {
std::unique_ptr<RTCDataChannelStats> data_channel_stats(
new RTCDataChannelStats(
"RTCDataChannel_" + rtc::ToString<>(data_channel->id()),
timestamp_us));
data_channel_stats->label = data_channel->label();
data_channel_stats->protocol = data_channel->protocol();
data_channel_stats->datachannelid = data_channel->id();
data_channel_stats->state =
DataStateToRTCDataChannelState(data_channel->state());
data_channel_stats->messages_sent = data_channel->messages_sent();
data_channel_stats->bytes_sent = data_channel->bytes_sent();
data_channel_stats->messages_received = data_channel->messages_received();
data_channel_stats->bytes_received = data_channel->bytes_received();
report->AddStats(std::move(data_channel_stats));
}
}
void RTCStatsCollector::ProduceIceCandidateAndPairStats_n(
int64_t timestamp_us,
const SessionStats& session_stats,
const cricket::VideoMediaInfo* video_media_info,
const Call::Stats& call_stats,
RTCStatsReport* report) const {
RTC_DCHECK(network_thread_->IsCurrent());
for (const auto& transport_stats : session_stats.transport_stats) {
for (const auto& channel_stats : transport_stats.second.channel_stats) {
std::string transport_id = RTCTransportStatsIDFromTransportChannel(
transport_stats.second.transport_name, channel_stats.component);
for (const cricket::ConnectionInfo& info :
channel_stats.connection_infos) {
std::unique_ptr<RTCIceCandidatePairStats> candidate_pair_stats(
new RTCIceCandidatePairStats(
RTCIceCandidatePairStatsIDFromConnectionInfo(info),
timestamp_us));
candidate_pair_stats->transport_id = transport_id;
// TODO(hbos): There could be other candidates that are not paired with
// anything. We don't have a complete list. Local candidates come from
// Port objects, and prflx candidates (both local and remote) are only
// stored in candidate pairs. crbug.com/632723
candidate_pair_stats->local_candidate_id = ProduceIceCandidateStats(
timestamp_us, info.local_candidate, true, transport_id, report);
candidate_pair_stats->remote_candidate_id = ProduceIceCandidateStats(
timestamp_us, 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.
// crbug.com/633550
candidate_pair_stats->writable = info.writable;
candidate_pair_stats->bytes_sent =
static_cast<uint64_t>(info.sent_total_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) {
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_before_first_response);
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);
report->AddStats(std::move(candidate_pair_stats));
}
}
}
}
void RTCStatsCollector::ProduceMediaStreamAndTrackStats_s(
int64_t timestamp_us, RTCStatsReport* report) const {
RTC_DCHECK(signaling_thread_->IsCurrent());
RTC_DCHECK(track_media_info_map_);
ProduceMediaStreamAndTrackStats(timestamp_us,
*track_media_info_map_,
pc_->local_streams(),
true,
report);
ProduceMediaStreamAndTrackStats(timestamp_us,
*track_media_info_map_,
pc_->remote_streams(),
false,
report);
}
void RTCStatsCollector::ProducePeerConnectionStats_s(
int64_t timestamp_us, RTCStatsReport* report) const {
RTC_DCHECK(signaling_thread_->IsCurrent());
std::unique_ptr<RTCPeerConnectionStats> stats(
new RTCPeerConnectionStats("RTCPeerConnection", timestamp_us));
stats->data_channels_opened = internal_record_.data_channels_opened;
stats->data_channels_closed = internal_record_.data_channels_closed;
report->AddStats(std::move(stats));
}
void RTCStatsCollector::ProduceRTPStreamStats_n(
int64_t timestamp_us, const SessionStats& session_stats,
const TrackMediaInfoMap& track_media_info_map,
RTCStatsReport* report) const {
RTC_DCHECK(network_thread_->IsCurrent());
// Audio
if (track_media_info_map.voice_media_info()) {
std::string transport_id = RTCTransportStatsIDFromBaseChannel(
session_stats.proxy_to_transport, *pc_->voice_channel());
RTC_DCHECK(!transport_id.empty());
// Inbound
for (const cricket::VoiceReceiverInfo& voice_receiver_info :
track_media_info_map.voice_media_info()->receivers) {
// TODO(nisse): SSRC == 0 currently means none. Delete check when that
// is fixed.
if (voice_receiver_info.ssrc() == 0)
continue;
std::unique_ptr<RTCInboundRTPStreamStats> inbound_audio(
new RTCInboundRTPStreamStats(
RTCInboundRTPStreamStatsIDFromSSRC(
true, voice_receiver_info.ssrc()),
timestamp_us));
SetInboundRTPStreamStatsFromVoiceReceiverInfo(
voice_receiver_info, inbound_audio.get());
rtc::scoped_refptr<AudioTrackInterface> audio_track =
track_media_info_map_->GetAudioTrack(voice_receiver_info);
if (audio_track) {
RTC_DCHECK(track_to_id_.find(audio_track.get()) != track_to_id_.end());
inbound_audio->track_id =
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
false,
MediaStreamTrackInterface::kAudioKind,
track_to_id_.find(audio_track.get())->second,
voice_receiver_info.ssrc());
}
inbound_audio->transport_id = transport_id;
report->AddStats(std::move(inbound_audio));
}
// Outbound
for (const cricket::VoiceSenderInfo& voice_sender_info :
track_media_info_map.voice_media_info()->senders) {
// TODO(nisse): SSRC == 0 currently means none. Delete check when that
// is fixed.
if (voice_sender_info.ssrc() == 0)
continue;
std::unique_ptr<RTCOutboundRTPStreamStats> outbound_audio(
new RTCOutboundRTPStreamStats(
RTCOutboundRTPStreamStatsIDFromSSRC(
true, voice_sender_info.ssrc()),
timestamp_us));
SetOutboundRTPStreamStatsFromVoiceSenderInfo(
voice_sender_info, outbound_audio.get());
rtc::scoped_refptr<AudioTrackInterface> audio_track =
track_media_info_map_->GetAudioTrack(voice_sender_info);
if (audio_track) {
RTC_DCHECK(track_to_id_.find(audio_track.get()) != track_to_id_.end());
outbound_audio->track_id =
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
true,
MediaStreamTrackInterface::kAudioKind,
track_to_id_.find(audio_track.get())->second,
voice_sender_info.ssrc());
}
outbound_audio->transport_id = transport_id;
report->AddStats(std::move(outbound_audio));
}
}
// Video
if (track_media_info_map.video_media_info()) {
std::string transport_id = RTCTransportStatsIDFromBaseChannel(
session_stats.proxy_to_transport, *pc_->video_channel());
RTC_DCHECK(!transport_id.empty());
// Inbound
for (const cricket::VideoReceiverInfo& video_receiver_info :
track_media_info_map.video_media_info()->receivers) {
// TODO(nisse): SSRC == 0 currently means none. Delete check when that
// is fixed.
if (video_receiver_info.ssrc() == 0)
continue;
std::unique_ptr<RTCInboundRTPStreamStats> inbound_video(
new RTCInboundRTPStreamStats(
RTCInboundRTPStreamStatsIDFromSSRC(
false, video_receiver_info.ssrc()),
timestamp_us));
SetInboundRTPStreamStatsFromVideoReceiverInfo(
video_receiver_info, inbound_video.get());
rtc::scoped_refptr<VideoTrackInterface> video_track =
track_media_info_map_->GetVideoTrack(video_receiver_info);
if (video_track) {
RTC_DCHECK(track_to_id_.find(video_track.get()) != track_to_id_.end());
inbound_video->track_id =
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
false,
MediaStreamTrackInterface::kVideoKind,
track_to_id_.find(video_track.get())->second,
video_receiver_info.ssrc());
}
inbound_video->transport_id = transport_id;
report->AddStats(std::move(inbound_video));
}
// Outbound
for (const cricket::VideoSenderInfo& video_sender_info :
track_media_info_map.video_media_info()->senders) {
// TODO(nisse): SSRC == 0 currently means none. Delete check when that
// is fixed.
if (video_sender_info.ssrc() == 0)
continue;
std::unique_ptr<RTCOutboundRTPStreamStats> outbound_video(
new RTCOutboundRTPStreamStats(
RTCOutboundRTPStreamStatsIDFromSSRC(
false, video_sender_info.ssrc()),
timestamp_us));
SetOutboundRTPStreamStatsFromVideoSenderInfo(
video_sender_info, outbound_video.get());
rtc::scoped_refptr<VideoTrackInterface> video_track =
track_media_info_map_->GetVideoTrack(video_sender_info);
if (video_track) {
RTC_DCHECK(track_to_id_.find(video_track.get()) != track_to_id_.end());
outbound_video->track_id =
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
true,
MediaStreamTrackInterface::kVideoKind,
track_to_id_.find(video_track.get())->second,
video_sender_info.ssrc());
}
outbound_video->transport_id = transport_id;
report->AddStats(std::move(outbound_video));
}
}
}
void RTCStatsCollector::ProduceTransportStats_n(
int64_t timestamp_us, const SessionStats& session_stats,
const std::map<std::string, CertificateStatsPair>& transport_cert_stats,
RTCStatsReport* report) const {
RTC_DCHECK(network_thread_->IsCurrent());
for (const auto& transport : session_stats.transport_stats) {
// Get reference to RTCP channel, if it exists.
std::string rtcp_transport_stats_id;
for (const auto& channel_stats : transport.second.channel_stats) {
if (channel_stats.component ==
cricket::ICE_CANDIDATE_COMPONENT_RTCP) {
rtcp_transport_stats_id = RTCTransportStatsIDFromTransportChannel(
transport.second.transport_name, channel_stats.component);
break;
}
}
// Get reference to local and remote certificates of this transport, if they
// exist.
const auto& certificate_stats_it = transport_cert_stats.find(
transport.second.transport_name);
RTC_DCHECK(certificate_stats_it != transport_cert_stats.cend());
std::string local_certificate_id;
if (certificate_stats_it->second.local) {
local_certificate_id = RTCCertificateIDFromFingerprint(
certificate_stats_it->second.local->fingerprint);
}
std::string remote_certificate_id;
if (certificate_stats_it->second.remote) {
remote_certificate_id = RTCCertificateIDFromFingerprint(
certificate_stats_it->second.remote->fingerprint);
}
// There is one transport stats for each channel.
for (const auto& channel_stats : transport.second.channel_stats) {
std::unique_ptr<RTCTransportStats> transport_stats(
new RTCTransportStats(
RTCTransportStatsIDFromTransportChannel(
transport.second.transport_name, channel_stats.component),
timestamp_us));
transport_stats->bytes_sent = 0;
transport_stats->bytes_received = 0;
transport_stats->dtls_state = DtlsTransportStateToRTCDtlsTransportState(
channel_stats.dtls_state);
for (const cricket::ConnectionInfo& info :
channel_stats.connection_infos) {
*transport_stats->bytes_sent += info.sent_total_bytes;
*transport_stats->bytes_received += info.recv_total_bytes;
if (info.best_connection) {
transport_stats->selected_candidate_pair_id =
RTCIceCandidatePairStatsIDFromConnectionInfo(info);
}
}
if (channel_stats.component != cricket::ICE_CANDIDATE_COMPONENT_RTCP &&
!rtcp_transport_stats_id.empty()) {
transport_stats->rtcp_transport_stats_id = rtcp_transport_stats_id;
}
if (!local_certificate_id.empty())
transport_stats->local_certificate_id = local_certificate_id;
if (!remote_certificate_id.empty())
transport_stats->remote_certificate_id = remote_certificate_id;
report->AddStats(std::move(transport_stats));
}
}
}
std::map<std::string, RTCStatsCollector::CertificateStatsPair>
RTCStatsCollector::PrepareTransportCertificateStats_n(
const SessionStats& session_stats) const {
RTC_DCHECK(network_thread_->IsCurrent());
std::map<std::string, CertificateStatsPair> transport_cert_stats;
for (const auto& transport_stats : session_stats.transport_stats) {
CertificateStatsPair certificate_stats_pair;
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate;
if (pc_->GetLocalCertificate(transport_stats.second.transport_name,
&local_certificate)) {
certificate_stats_pair.local =
local_certificate->ssl_certificate().GetStats();
}
std::unique_ptr<rtc::SSLCertificate> remote_certificate =
pc_->GetRemoteSSLCertificate(transport_stats.second.transport_name);
if (remote_certificate) {
certificate_stats_pair.remote = remote_certificate->GetStats();
}
transport_cert_stats.insert(
std::make_pair(transport_stats.second.transport_name,
std::move(certificate_stats_pair)));
}
return transport_cert_stats;
}
std::unique_ptr<TrackMediaInfoMap>
RTCStatsCollector::PrepareTrackMediaInfoMap_s() const {
RTC_DCHECK(signaling_thread_->IsCurrent());
std::unique_ptr<cricket::VoiceMediaInfo> voice_media_info;
if (pc_->voice_channel()) {
voice_media_info.reset(new cricket::VoiceMediaInfo());
if (!pc_->voice_channel()->GetStats(voice_media_info.get())) {
voice_media_info.reset();
}
}
std::unique_ptr<cricket::VideoMediaInfo> video_media_info;
if (pc_->video_channel()) {
video_media_info.reset(new cricket::VideoMediaInfo());
if (!pc_->video_channel()->GetStats(video_media_info.get())) {
video_media_info.reset();
}
}
std::unique_ptr<TrackMediaInfoMap> track_media_info_map(
new TrackMediaInfoMap(std::move(voice_media_info),
std::move(video_media_info),
pc_->GetSenders(),
pc_->GetReceivers()));
return track_media_info_map;
}
std::map<MediaStreamTrackInterface*, std::string>
RTCStatsCollector::PrepareTrackToID_s() const {
RTC_DCHECK(signaling_thread_->IsCurrent());
std::map<MediaStreamTrackInterface*, std::string> track_to_id;
for (auto sender : pc_->GetSenders()) {
auto track = sender->track();
if (track)
track_to_id[track.get()] = track->id();
}
for (auto receiver : pc_->GetReceivers()) {
auto track = receiver->track();
if (track)
track_to_id[track.get()] = track->id();
}
return track_to_id;
}
void RTCStatsCollector::OnDataChannelCreated(DataChannel* channel) {
channel->SignalOpened.connect(this, &RTCStatsCollector::OnDataChannelOpened);
channel->SignalClosed.connect(this, &RTCStatsCollector::OnDataChannelClosed);
}
void RTCStatsCollector::OnDataChannelOpened(DataChannel* channel) {
RTC_DCHECK(signaling_thread_->IsCurrent());
bool result = internal_record_.opened_data_channels.insert(
reinterpret_cast<uintptr_t>(channel)).second;
++internal_record_.data_channels_opened;
RTC_DCHECK(result);
}
void RTCStatsCollector::OnDataChannelClosed(DataChannel* channel) {
RTC_DCHECK(signaling_thread_->IsCurrent());
// Only channels that have been fully opened (and have increased the
// |data_channels_opened_| counter) increase the closed counter.
if (internal_record_.opened_data_channels.erase(
reinterpret_cast<uintptr_t>(channel))) {
++internal_record_.data_channels_closed;
}
}
const char* CandidateTypeToRTCIceCandidateTypeForTesting(
const std::string& type) {
return CandidateTypeToRTCIceCandidateType(type);
}
const char* DataStateToRTCDataChannelStateForTesting(
DataChannelInterface::DataState state) {
return DataStateToRTCDataChannelState(state);
}
} // namespace webrtc