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:
Henrik Boström
2019-05-27 13:40:25 +02:00
committed by Commit Bot
parent 6e436d1cc0
commit 883eefc59e
8 changed files with 475 additions and 2 deletions

View File

@ -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:

View File

@ -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",

View File

@ -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(

View File

@ -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,

View File

@ -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;

View File

@ -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);

View File

@ -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.

View File

@ -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,