Implement RTCRemoteInboundRtpStreamStats for both audio and video.
This implements the essentials of RTCRemoteInboundRtpStreamStats. This includes: - ssrc - transportId - codecId - packetsLost - jitter - localId - roundTripTime https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict* The following members are not implemented because they require more work... - From RTCReceivedRtpStreamStats: packetsReceived, packetsDiscarded, packetsRepaired, burstPacketsLost, burstPacketsDiscarded, burstLossCount, burstDiscardCount, burstLossRate, burstDiscardRate, gapLossRate and gapDiscardRate. - From RTCRemoteInboundRtpStreamStats: fractionLost. Bug: webrtc:10455, webrtc:10456 Change-Id: If2ab0da7105d8c93bba58e14aa93bd22ffe57f1d Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/138067 Commit-Queue: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/master@{#28073}
This commit is contained in:
committed by
Commit Bot
parent
6e436d1cc0
commit
883eefc59e
@ -468,6 +468,46 @@ class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats {
|
||||
RTCStatsMember<std::string> content_type;
|
||||
};
|
||||
|
||||
// TODO(https://crbug.com/webrtc/10671): Refactor the stats dictionaries to have
|
||||
// the same hierarchy as in the spec; implement RTCReceivedRtpStreamStats.
|
||||
// Several metrics are shared between "outbound-rtp", "remote-inbound-rtp",
|
||||
// "inbound-rtp" and "remote-outbound-rtp". In the spec there is a hierarchy of
|
||||
// dictionaries that minimizes defining the same metrics in multiple places.
|
||||
// From JavaScript this hierarchy is not observable and the spec's hierarchy is
|
||||
// purely editorial. In C++ non-final classes in the hierarchy could be used to
|
||||
// refer to different stats objects within the hierarchy.
|
||||
// https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*
|
||||
class RTC_EXPORT RTCRemoteInboundRtpStreamStats final : public RTCStats {
|
||||
public:
|
||||
WEBRTC_RTCSTATS_DECL();
|
||||
|
||||
RTCRemoteInboundRtpStreamStats(const std::string& id, int64_t timestamp_us);
|
||||
RTCRemoteInboundRtpStreamStats(std::string&& id, int64_t timestamp_us);
|
||||
RTCRemoteInboundRtpStreamStats(const RTCRemoteInboundRtpStreamStats& other);
|
||||
~RTCRemoteInboundRtpStreamStats() override;
|
||||
|
||||
// In the spec RTCRemoteInboundRtpStreamStats inherits from RTCRtpStreamStats
|
||||
// and RTCReceivedRtpStreamStats. The members here are listed based on where
|
||||
// they are defined in the spec.
|
||||
// RTCRtpStreamStats
|
||||
RTCStatsMember<uint32_t> ssrc;
|
||||
RTCStatsMember<std::string> kind;
|
||||
RTCStatsMember<std::string> transport_id;
|
||||
RTCStatsMember<std::string> codec_id;
|
||||
// RTCReceivedRtpStreamStats
|
||||
RTCStatsMember<int32_t> packets_lost;
|
||||
RTCStatsMember<double> jitter;
|
||||
// TODO(hbos): The following RTCReceivedRtpStreamStats metrics should also be
|
||||
// implemented: packetsReceived, packetsDiscarded, packetsRepaired,
|
||||
// burstPacketsLost, burstPacketsDiscarded, burstLossCount, burstDiscardCount,
|
||||
// burstLossRate, burstDiscardRate, gapLossRate and gapDiscardRate.
|
||||
// RTCRemoteInboundRtpStreamStats
|
||||
RTCStatsMember<std::string> local_id;
|
||||
RTCStatsMember<double> round_trip_time;
|
||||
// TODO(hbos): The following RTCRemoteInboundRtpStreamStats metric should also
|
||||
// be implemented: fractionLost.
|
||||
};
|
||||
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcmediasourcestats
|
||||
class RTC_EXPORT RTCMediaSourceStats : public RTCStats {
|
||||
public:
|
||||
|
||||
@ -523,6 +523,7 @@ if (rtc_include_tests) {
|
||||
"../media:rtc_media_config",
|
||||
"../modules/audio_device:audio_device_api",
|
||||
"../modules/audio_processing:audio_processing_statistics",
|
||||
"../modules/rtp_rtcp:rtp_rtcp_format",
|
||||
"../p2p:fake_port_allocator",
|
||||
"../rtc_base:checks",
|
||||
"../rtc_base:gunit_helpers",
|
||||
|
||||
@ -92,6 +92,18 @@ std::string RTCOutboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) {
|
||||
return sb.str();
|
||||
}
|
||||
|
||||
std::string RTCRemoteInboundRtpStreamStatsIdFromSsrcs(
|
||||
cricket::MediaType media_type,
|
||||
uint32_t sender_ssrc,
|
||||
uint32_t source_ssrc) {
|
||||
char buf[1024];
|
||||
rtc::SimpleStringBuilder sb(buf);
|
||||
sb << "RTCRemoteInboundRtp"
|
||||
<< (media_type == cricket::MEDIA_TYPE_AUDIO ? "Audio" : "Video")
|
||||
<< "Stream_" << sender_ssrc << "_" << source_ssrc;
|
||||
return sb.str();
|
||||
}
|
||||
|
||||
std::string RTCMediaSourceStatsIDFromKindAndAttachment(
|
||||
cricket::MediaType media_type,
|
||||
int attachment_id) {
|
||||
@ -369,6 +381,71 @@ void SetOutboundRTPStreamStatsFromVideoSenderInfo(
|
||||
outbound_video->content_type = RTCContentType::kScreenshare;
|
||||
}
|
||||
|
||||
std::unique_ptr<RTCRemoteInboundRtpStreamStats>
|
||||
ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
|
||||
const ReportBlockData& report_block_data,
|
||||
cricket::MediaType media_type,
|
||||
const RTCStatsReport& report) {
|
||||
const auto& report_block = report_block_data.report_block();
|
||||
// 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.
|
||||
auto remote_inbound = absl::make_unique<RTCRemoteInboundRtpStreamStats>(
|
||||
RTCRemoteInboundRtpStreamStatsIdFromSsrcs(
|
||||
media_type, report_block.sender_ssrc, report_block.source_ssrc),
|
||||
/*timestamp=*/report_block_data.report_block_timestamp_utc_us());
|
||||
remote_inbound->ssrc = report_block.sender_ssrc;
|
||||
remote_inbound->kind =
|
||||
media_type == cricket::MEDIA_TYPE_AUDIO ? "audio" : "video";
|
||||
remote_inbound->packets_lost = report_block.packets_lost;
|
||||
remote_inbound->round_trip_time =
|
||||
static_cast<double>(report_block_data.last_rtt_ms()) /
|
||||
rtc::kNumMillisecsPerSec;
|
||||
|
||||
std::string local_id = RTCOutboundRTPStreamStatsIDFromSSRC(
|
||||
media_type == cricket::MEDIA_TYPE_AUDIO, report_block.source_ssrc);
|
||||
const auto* local_id_stat = report.Get(local_id);
|
||||
if (local_id_stat) {
|
||||
remote_inbound->local_id = local_id;
|
||||
const auto& outbound_rtp =
|
||||
local_id_stat->cast_to<RTCOutboundRTPStreamStats>();
|
||||
// The RTP/RTCP transport is obtained from the
|
||||
// RTCOutboundRtpStreamStats's transport.
|
||||
const auto* transport_from_id = outbound_rtp.transport_id.is_defined()
|
||||
? report.Get(*outbound_rtp.transport_id)
|
||||
: nullptr;
|
||||
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.is_defined()
|
||||
? *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.is_defined()
|
||||
? 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.is_defined()) {
|
||||
// The Report Block jitter is expressed in RTP timestamp units
|
||||
// (https://tools.ietf.org/html/rfc3550#section-6.4.1). To convert this
|
||||
// to seconds we divide by the codec's clock rate.
|
||||
remote_inbound->jitter =
|
||||
static_cast<double>(report_block.jitter) / *codec.clock_rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
return remote_inbound;
|
||||
}
|
||||
|
||||
void ProduceCertificateStatsFromSSLCertificateStats(
|
||||
int64_t timestamp_us, const rtc::SSLCertificateStats& certificate_stats,
|
||||
RTCStatsReport* report) {
|
||||
@ -984,10 +1061,10 @@ void RTCStatsCollector::ProducePartialResultsOnNetworkThreadImpl(
|
||||
ProduceCodecStats_n(timestamp_us, transceiver_stats_infos_, partial_report);
|
||||
ProduceIceCandidateAndPairStats_n(timestamp_us, transport_stats_by_name,
|
||||
call_stats_, partial_report);
|
||||
ProduceRTPStreamStats_n(timestamp_us, transceiver_stats_infos_,
|
||||
partial_report);
|
||||
ProduceTransportStats_n(timestamp_us, transport_stats_by_name,
|
||||
transport_cert_stats, partial_report);
|
||||
ProduceRTPStreamStats_n(timestamp_us, transceiver_stats_infos_,
|
||||
partial_report);
|
||||
}
|
||||
|
||||
void RTCStatsCollector::MergeNetworkReport_s() {
|
||||
@ -1427,6 +1504,18 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n(
|
||||
outbound_audio->transport_id = transport_id;
|
||||
report->AddStats(std::move(outbound_audio));
|
||||
}
|
||||
// Remote-inbound
|
||||
// These are Report Block-based, information sent from the remote endpoint,
|
||||
// providing metrics about our Outbound streams. We take advantage of the fact
|
||||
// that RTCOutboundRtpStreamStats, RTCCodecStats and RTCTransport have already
|
||||
// been added to the report.
|
||||
for (const cricket::VoiceSenderInfo& voice_sender_info :
|
||||
track_media_info_map.voice_media_info()->senders) {
|
||||
for (const auto& report_block_data : voice_sender_info.report_block_datas) {
|
||||
report->AddStats(ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
|
||||
report_block_data, cricket::MEDIA_TYPE_AUDIO, *report));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RTCStatsCollector::ProduceVideoRTPStreamStats_n(
|
||||
@ -1488,6 +1577,18 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n(
|
||||
outbound_video->transport_id = transport_id;
|
||||
report->AddStats(std::move(outbound_video));
|
||||
}
|
||||
// Remote-inbound
|
||||
// These are Report Block-based, information sent from the remote endpoint,
|
||||
// providing metrics about our Outbound streams. We take advantage of the fact
|
||||
// that RTCOutboundRtpStreamStats, RTCCodecStats and RTCTransport have already
|
||||
// been added to the report.
|
||||
for (const cricket::VideoSenderInfo& video_sender_info :
|
||||
track_media_info_map.video_media_info()->senders) {
|
||||
for (const auto& report_block_data : video_sender_info.report_block_datas) {
|
||||
report->AddStats(ProduceRemoteInboundRtpStreamStatsFromReportBlockData(
|
||||
report_block_data, cricket::MEDIA_TYPE_VIDEO, *report));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RTCStatsCollector::ProduceTransportStats_n(
|
||||
|
||||
@ -190,6 +190,8 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface,
|
||||
void ProducePeerConnectionStats_s(int64_t timestamp_us,
|
||||
RTCStatsReport* report) const;
|
||||
// Produces |RTCInboundRTPStreamStats| and |RTCOutboundRTPStreamStats|.
|
||||
// This has to be invoked after codecs and transport stats have been created
|
||||
// because some metrics are calculated through lookup of other metrics.
|
||||
void ProduceRTPStreamStats_n(
|
||||
int64_t timestamp_us,
|
||||
const std::vector<RtpTransceiverStatsInfo>& transceiver_stats_infos,
|
||||
|
||||
@ -8,6 +8,9 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
@ -21,6 +24,8 @@
|
||||
#include "api/stats/rtc_stats_report.h"
|
||||
#include "api/stats/rtcstats_objects.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "modules/rtp_rtcp/include/report_block_data.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "pc/media_stream.h"
|
||||
@ -89,6 +94,10 @@ void PrintTo(const RTCOutboundRTPStreamStats& stats, ::std::ostream* os) {
|
||||
*os << stats.ToJson();
|
||||
}
|
||||
|
||||
void PrintTo(const RTCRemoteInboundRtpStreamStats& stats, ::std::ostream* os) {
|
||||
*os << stats.ToJson();
|
||||
}
|
||||
|
||||
void PrintTo(const RTCAudioSourceStats& stats, ::std::ostream* os) {
|
||||
*os << stats.ToJson();
|
||||
}
|
||||
@ -2342,6 +2351,252 @@ TEST_F(RTCStatsCollectorTest,
|
||||
EXPECT_FALSE(report->Get("RTCAudioSource_42"));
|
||||
}
|
||||
|
||||
// Parameterized tests on cricket::MediaType (audio or video).
|
||||
class RTCStatsCollectorTestWithParamKind
|
||||
: public RTCStatsCollectorTest,
|
||||
public ::testing::WithParamInterface<cricket::MediaType> {
|
||||
public:
|
||||
RTCStatsCollectorTestWithParamKind() : media_type_(GetParam()) {
|
||||
RTC_DCHECK(media_type_ == cricket::MEDIA_TYPE_AUDIO ||
|
||||
media_type_ == cricket::MEDIA_TYPE_VIDEO);
|
||||
}
|
||||
|
||||
std::string MediaTypeUpperCase() const {
|
||||
switch (media_type_) {
|
||||
case cricket::MEDIA_TYPE_AUDIO:
|
||||
return "Audio";
|
||||
case cricket::MEDIA_TYPE_VIDEO:
|
||||
return "Video";
|
||||
case cricket::MEDIA_TYPE_DATA:
|
||||
RTC_NOTREACHED();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string MediaTypeLowerCase() const {
|
||||
std::string str = MediaTypeUpperCase();
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
return str;
|
||||
}
|
||||
|
||||
// Adds a sender and channel of the appropriate kind, creating a sender info
|
||||
// with the report block's |source_ssrc| and report block data.
|
||||
void AddSenderInfoAndMediaChannel(std::string transport_name,
|
||||
ReportBlockData report_block_data,
|
||||
absl::optional<RtpCodecParameters> codec) {
|
||||
switch (media_type_) {
|
||||
case cricket::MEDIA_TYPE_AUDIO: {
|
||||
cricket::VoiceMediaInfo voice_media_info;
|
||||
voice_media_info.senders.push_back(cricket::VoiceSenderInfo());
|
||||
voice_media_info.senders[0].local_stats.push_back(
|
||||
cricket::SsrcSenderInfo());
|
||||
voice_media_info.senders[0].local_stats[0].ssrc =
|
||||
report_block_data.report_block().source_ssrc;
|
||||
if (codec.has_value()) {
|
||||
voice_media_info.senders[0].codec_payload_type = codec->payload_type;
|
||||
voice_media_info.send_codecs.insert(
|
||||
std::make_pair(codec->payload_type, *codec));
|
||||
}
|
||||
voice_media_info.senders[0].report_block_datas.push_back(
|
||||
report_block_data);
|
||||
auto* voice_media_channel = pc_->AddVoiceChannel("mid", transport_name);
|
||||
voice_media_channel->SetStats(voice_media_info);
|
||||
return;
|
||||
}
|
||||
case cricket::MEDIA_TYPE_VIDEO: {
|
||||
cricket::VideoMediaInfo video_media_info;
|
||||
video_media_info.senders.push_back(cricket::VideoSenderInfo());
|
||||
video_media_info.senders[0].local_stats.push_back(
|
||||
cricket::SsrcSenderInfo());
|
||||
video_media_info.senders[0].local_stats[0].ssrc =
|
||||
report_block_data.report_block().source_ssrc;
|
||||
if (codec.has_value()) {
|
||||
video_media_info.senders[0].codec_payload_type = codec->payload_type;
|
||||
video_media_info.send_codecs.insert(
|
||||
std::make_pair(codec->payload_type, *codec));
|
||||
}
|
||||
video_media_info.senders[0].report_block_datas.push_back(
|
||||
report_block_data);
|
||||
auto* video_media_channel = pc_->AddVideoChannel("mid", transport_name);
|
||||
video_media_channel->SetStats(video_media_info);
|
||||
return;
|
||||
}
|
||||
case cricket::MEDIA_TYPE_DATA:
|
||||
RTC_NOTREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
cricket::MediaType media_type_;
|
||||
};
|
||||
|
||||
// Verifies RTCRemoteInboundRtpStreamStats members that don't require
|
||||
// RTCCodecStats (codecId, jitter) and without setting up an RTCP transport.
|
||||
TEST_P(RTCStatsCollectorTestWithParamKind,
|
||||
RTCRemoteInboundRtpStreamStatsCollectedFromReportBlock) {
|
||||
const int64_t kReportBlockTimestampUtcUs = 123456789;
|
||||
const int64_t kRoundTripTimeMs = 13000;
|
||||
const double kRoundTripTimeSeconds = 13.0;
|
||||
|
||||
// The report block's timestamp cannot be from the future, set the fake clock
|
||||
// to match.
|
||||
fake_clock_.SetTime(Timestamp::us(kReportBlockTimestampUtcUs));
|
||||
|
||||
RTCPReportBlock report_block;
|
||||
// The remote-inbound-rtp SSRC, "SSRC of sender of this report".
|
||||
report_block.sender_ssrc = 8;
|
||||
// The outbound-rtp SSRC, "SSRC of the RTP packet sender".
|
||||
report_block.source_ssrc = 12;
|
||||
report_block.packets_lost = 7;
|
||||
ReportBlockData report_block_data;
|
||||
report_block_data.SetReportBlock(report_block, kReportBlockTimestampUtcUs);
|
||||
report_block_data.AddRoundTripTimeSample(1234);
|
||||
// Only the last sample should be exposed as the
|
||||
// |RTCRemoteInboundRtpStreamStats::round_trip_time|.
|
||||
report_block_data.AddRoundTripTimeSample(kRoundTripTimeMs);
|
||||
|
||||
AddSenderInfoAndMediaChannel("TransportName", report_block_data,
|
||||
absl::nullopt);
|
||||
|
||||
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
|
||||
|
||||
RTCRemoteInboundRtpStreamStats expected_remote_inbound_rtp(
|
||||
"RTCRemoteInboundRtp" + MediaTypeUpperCase() + "Stream_8_12",
|
||||
kReportBlockTimestampUtcUs);
|
||||
expected_remote_inbound_rtp.ssrc = 8;
|
||||
expected_remote_inbound_rtp.kind = MediaTypeLowerCase();
|
||||
expected_remote_inbound_rtp.transport_id =
|
||||
"RTCTransport_TransportName_1"; // 1 for RTP (we have no RTCP transport)
|
||||
expected_remote_inbound_rtp.packets_lost = 7;
|
||||
expected_remote_inbound_rtp.local_id =
|
||||
"RTCOutboundRTP" + MediaTypeUpperCase() + "Stream_12";
|
||||
expected_remote_inbound_rtp.round_trip_time = kRoundTripTimeSeconds;
|
||||
// This test does not set up RTCCodecStats, so |codec_id| and |jitter| are
|
||||
// expected to be missing. These are tested separately.
|
||||
|
||||
ASSERT_TRUE(report->Get(expected_remote_inbound_rtp.id()));
|
||||
EXPECT_EQ(report->Get(expected_remote_inbound_rtp.id())
|
||||
->cast_to<RTCRemoteInboundRtpStreamStats>(),
|
||||
expected_remote_inbound_rtp);
|
||||
EXPECT_TRUE(report->Get(*expected_remote_inbound_rtp.transport_id));
|
||||
EXPECT_TRUE(report->Get(*expected_remote_inbound_rtp.local_id));
|
||||
}
|
||||
|
||||
TEST_P(RTCStatsCollectorTestWithParamKind,
|
||||
RTCRemoteInboundRtpStreamStatsWithTimestampFromReportBlock) {
|
||||
const int64_t kReportBlockTimestampUtcUs = 123456789;
|
||||
fake_clock_.SetTime(Timestamp::us(kReportBlockTimestampUtcUs));
|
||||
|
||||
RTCPReportBlock report_block;
|
||||
// The remote-inbound-rtp SSRC, "SSRC of sender of this report".
|
||||
report_block.sender_ssrc = 8;
|
||||
// The outbound-rtp SSRC, "SSRC of the RTP packet sender".
|
||||
report_block.source_ssrc = 12;
|
||||
ReportBlockData report_block_data;
|
||||
report_block_data.SetReportBlock(report_block, kReportBlockTimestampUtcUs);
|
||||
|
||||
AddSenderInfoAndMediaChannel("TransportName", report_block_data,
|
||||
absl::nullopt);
|
||||
|
||||
// Advance time, it should be OK to have fresher reports than report blocks.
|
||||
fake_clock_.AdvanceTime(TimeDelta::us(1234));
|
||||
|
||||
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
|
||||
|
||||
std::string remote_inbound_rtp_id =
|
||||
"RTCRemoteInboundRtp" + MediaTypeUpperCase() + "Stream_8_12";
|
||||
ASSERT_TRUE(report->Get(remote_inbound_rtp_id));
|
||||
auto& remote_inbound_rtp = report->Get(remote_inbound_rtp_id)
|
||||
->cast_to<RTCRemoteInboundRtpStreamStats>();
|
||||
|
||||
// Even though the report time is different, the remote-inbound-rtp timestamp
|
||||
// is of the time that the report block was received.
|
||||
EXPECT_EQ(kReportBlockTimestampUtcUs + 1234, report->timestamp_us());
|
||||
EXPECT_EQ(kReportBlockTimestampUtcUs, remote_inbound_rtp.timestamp_us());
|
||||
}
|
||||
|
||||
TEST_P(RTCStatsCollectorTestWithParamKind,
|
||||
RTCRemoteInboundRtpStreamStatsWithCodecBasedMembers) {
|
||||
const int64_t kReportBlockTimestampUtcUs = 123456789;
|
||||
fake_clock_.SetTime(Timestamp::us(kReportBlockTimestampUtcUs));
|
||||
|
||||
RTCPReportBlock report_block;
|
||||
// The remote-inbound-rtp SSRC, "SSRC of sender of this report".
|
||||
report_block.sender_ssrc = 8;
|
||||
// The outbound-rtp SSRC, "SSRC of the RTP packet sender".
|
||||
report_block.source_ssrc = 12;
|
||||
report_block.jitter = 5000;
|
||||
ReportBlockData report_block_data;
|
||||
report_block_data.SetReportBlock(report_block, kReportBlockTimestampUtcUs);
|
||||
|
||||
RtpCodecParameters codec;
|
||||
codec.payload_type = 3;
|
||||
codec.kind = media_type_;
|
||||
codec.clock_rate = 1000;
|
||||
|
||||
AddSenderInfoAndMediaChannel("TransportName", report_block_data, codec);
|
||||
|
||||
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
|
||||
|
||||
std::string remote_inbound_rtp_id =
|
||||
"RTCRemoteInboundRtp" + MediaTypeUpperCase() + "Stream_8_12";
|
||||
ASSERT_TRUE(report->Get(remote_inbound_rtp_id));
|
||||
auto& remote_inbound_rtp = report->Get(remote_inbound_rtp_id)
|
||||
->cast_to<RTCRemoteInboundRtpStreamStats>();
|
||||
|
||||
EXPECT_TRUE(remote_inbound_rtp.codec_id.is_defined());
|
||||
EXPECT_TRUE(report->Get(*remote_inbound_rtp.codec_id));
|
||||
|
||||
EXPECT_TRUE(remote_inbound_rtp.jitter.is_defined());
|
||||
// The jitter (in seconds) is the report block's jitter divided by the codec's
|
||||
// clock rate.
|
||||
EXPECT_EQ(5.0, *remote_inbound_rtp.jitter);
|
||||
}
|
||||
|
||||
TEST_P(RTCStatsCollectorTestWithParamKind,
|
||||
RTCRemoteInboundRtpStreamStatsWithRtcpTransport) {
|
||||
const int64_t kReportBlockTimestampUtcUs = 123456789;
|
||||
fake_clock_.SetTime(Timestamp::us(kReportBlockTimestampUtcUs));
|
||||
|
||||
RTCPReportBlock report_block;
|
||||
// The remote-inbound-rtp SSRC, "SSRC of sender of this report".
|
||||
report_block.sender_ssrc = 8;
|
||||
// The outbound-rtp SSRC, "SSRC of the RTP packet sender".
|
||||
report_block.source_ssrc = 12;
|
||||
ReportBlockData report_block_data;
|
||||
report_block_data.SetReportBlock(report_block, kReportBlockTimestampUtcUs);
|
||||
|
||||
cricket::TransportChannelStats rtp_transport_channel_stats;
|
||||
rtp_transport_channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP;
|
||||
rtp_transport_channel_stats.dtls_state = cricket::DTLS_TRANSPORT_NEW;
|
||||
cricket::TransportChannelStats rtcp_transport_channel_stats;
|
||||
rtcp_transport_channel_stats.component =
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTCP;
|
||||
rtcp_transport_channel_stats.dtls_state = cricket::DTLS_TRANSPORT_NEW;
|
||||
pc_->SetTransportStats("TransportName", {rtp_transport_channel_stats,
|
||||
rtcp_transport_channel_stats});
|
||||
AddSenderInfoAndMediaChannel("TransportName", report_block_data,
|
||||
absl::nullopt);
|
||||
|
||||
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
|
||||
|
||||
std::string remote_inbound_rtp_id =
|
||||
"RTCRemoteInboundRtp" + MediaTypeUpperCase() + "Stream_8_12";
|
||||
ASSERT_TRUE(report->Get(remote_inbound_rtp_id));
|
||||
auto& remote_inbound_rtp = report->Get(remote_inbound_rtp_id)
|
||||
->cast_to<RTCRemoteInboundRtpStreamStats>();
|
||||
|
||||
EXPECT_TRUE(remote_inbound_rtp.transport_id.is_defined());
|
||||
EXPECT_EQ("RTCTransport_TransportName_2", // 2 for RTCP
|
||||
*remote_inbound_rtp.transport_id);
|
||||
EXPECT_TRUE(report->Get(*remote_inbound_rtp.transport_id));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(,
|
||||
RTCStatsCollectorTestWithParamKind,
|
||||
::testing::Values(cricket::MEDIA_TYPE_AUDIO, // "/0"
|
||||
cricket::MEDIA_TYPE_VIDEO)); // "/1"
|
||||
|
||||
TEST_F(RTCStatsCollectorTest,
|
||||
RTCVideoSourceStatsNotCollectedForSenderWithoutTrack) {
|
||||
const uint32_t kSsrc = 4;
|
||||
|
||||
@ -394,6 +394,9 @@ class RTCStatsReportVerifier {
|
||||
} else if (stats.type() == RTCOutboundRTPStreamStats::kType) {
|
||||
verify_successful &= VerifyRTCOutboundRTPStreamStats(
|
||||
stats.cast_to<RTCOutboundRTPStreamStats>());
|
||||
} else if (stats.type() == RTCRemoteInboundRtpStreamStats::kType) {
|
||||
verify_successful &= VerifyRTCRemoteInboundRtpStreamStats(
|
||||
stats.cast_to<RTCRemoteInboundRtpStreamStats>());
|
||||
} else if (stats.type() == RTCAudioSourceStats::kType) {
|
||||
// RTCAudioSourceStats::kType and RTCVideoSourceStats::kType both have
|
||||
// the value "media-source", but they are distinguishable with pointer
|
||||
@ -846,6 +849,26 @@ class RTCStatsReportVerifier {
|
||||
return verifier.ExpectAllMembersSuccessfullyTested();
|
||||
}
|
||||
|
||||
bool VerifyRTCRemoteInboundRtpStreamStats(
|
||||
const RTCRemoteInboundRtpStreamStats& remote_inbound_stream) {
|
||||
RTCStatsVerifier verifier(report_, &remote_inbound_stream);
|
||||
verifier.TestMemberIsDefined(remote_inbound_stream.ssrc);
|
||||
verifier.TestMemberIsDefined(remote_inbound_stream.kind);
|
||||
verifier.TestMemberIsIDReference(remote_inbound_stream.transport_id,
|
||||
RTCTransportStats::kType);
|
||||
verifier.TestMemberIsIDReference(remote_inbound_stream.codec_id,
|
||||
RTCCodecStats::kType);
|
||||
verifier.TestMemberIsDefined(remote_inbound_stream.packets_lost);
|
||||
// Note that the existance of RTCCodecStats is needed for |codec_id| and
|
||||
// |jitter| to be present.
|
||||
verifier.TestMemberIsNonNegative<double>(remote_inbound_stream.jitter);
|
||||
verifier.TestMemberIsIDReference(remote_inbound_stream.local_id,
|
||||
RTCOutboundRTPStreamStats::kType);
|
||||
verifier.TestMemberIsNonNegative<double>(
|
||||
remote_inbound_stream.round_trip_time);
|
||||
return verifier.ExpectAllMembersSuccessfullyTested();
|
||||
}
|
||||
|
||||
void VerifyRTCMediaSourceStats(const RTCMediaSourceStats& media_source,
|
||||
RTCStatsVerifier* verifier) {
|
||||
verifier->TestMemberIsDefined(media_source.track_identifier);
|
||||
|
||||
@ -110,6 +110,12 @@ std::vector<const std::string*> GetStatsReferencedIds(const RTCStats& stats) {
|
||||
static_cast<const RTCOutboundRTPStreamStats&>(stats);
|
||||
AddIdIfDefined(outbound_rtp.media_source_id, &neighbor_ids);
|
||||
}
|
||||
} else if (type == RTCRemoteInboundRtpStreamStats::kType) {
|
||||
const auto& remote_inbound_rtp =
|
||||
static_cast<const RTCRemoteInboundRtpStreamStats&>(stats);
|
||||
AddIdIfDefined(remote_inbound_rtp.transport_id, &neighbor_ids);
|
||||
AddIdIfDefined(remote_inbound_rtp.codec_id, &neighbor_ids);
|
||||
AddIdIfDefined(remote_inbound_rtp.local_id, &neighbor_ids);
|
||||
} else if (type == RTCAudioSourceStats::kType ||
|
||||
type == RTCVideoSourceStats::kType) {
|
||||
// RTC[Audio/Video]SourceStats does not have any neighbor references.
|
||||
|
||||
@ -720,6 +720,51 @@ RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats(
|
||||
|
||||
RTCOutboundRTPStreamStats::~RTCOutboundRTPStreamStats() {}
|
||||
|
||||
// clang-format off
|
||||
WEBRTC_RTCSTATS_IMPL(
|
||||
RTCRemoteInboundRtpStreamStats, RTCStats, "remote-inbound-rtp",
|
||||
&ssrc,
|
||||
&kind,
|
||||
&transport_id,
|
||||
&codec_id,
|
||||
&packets_lost,
|
||||
&jitter,
|
||||
&local_id,
|
||||
&round_trip_time)
|
||||
// clang-format on
|
||||
|
||||
RTCRemoteInboundRtpStreamStats::RTCRemoteInboundRtpStreamStats(
|
||||
const std::string& id,
|
||||
int64_t timestamp_us)
|
||||
: RTCRemoteInboundRtpStreamStats(std::string(id), timestamp_us) {}
|
||||
|
||||
RTCRemoteInboundRtpStreamStats::RTCRemoteInboundRtpStreamStats(
|
||||
std::string&& id,
|
||||
int64_t timestamp_us)
|
||||
: RTCStats(std::move(id), timestamp_us),
|
||||
ssrc("ssrc"),
|
||||
kind("kind"),
|
||||
transport_id("transportId"),
|
||||
codec_id("codecId"),
|
||||
packets_lost("packetsLost"),
|
||||
jitter("jitter"),
|
||||
local_id("localId"),
|
||||
round_trip_time("roundTripTime") {}
|
||||
|
||||
RTCRemoteInboundRtpStreamStats::RTCRemoteInboundRtpStreamStats(
|
||||
const RTCRemoteInboundRtpStreamStats& other)
|
||||
: RTCStats(other),
|
||||
ssrc(other.ssrc),
|
||||
kind(other.kind),
|
||||
transport_id(other.transport_id),
|
||||
codec_id(other.codec_id),
|
||||
packets_lost(other.packets_lost),
|
||||
jitter(other.jitter),
|
||||
local_id(other.local_id),
|
||||
round_trip_time(other.round_trip_time) {}
|
||||
|
||||
RTCRemoteInboundRtpStreamStats::~RTCRemoteInboundRtpStreamStats() {}
|
||||
|
||||
// clang-format off
|
||||
WEBRTC_RTCSTATS_IMPL(RTCMediaSourceStats, RTCStats, "parent-media-source",
|
||||
&track_identifier,
|
||||
|
||||
Reference in New Issue
Block a user