RTCStatsCollector::GetStatsReport() with optional selector argument.

This implements the stats selection algorithm[1] in RTCStatsCollector by
obtaining the selector's inbound-rtp/outbound-rtp stats and performing
the stats traversal algorithm (TakeReferencedStats)[2] on a copy of the
cached report with the rtps as starting point.

Changes:
- RTCStatsCollector.GetStatsReport() with selector arguments added.
  - RequestInfo added, "callbacks_" is replaced by "requests_".
- RTCStatsReport.Copy() added.
- New test for sender selector and receiver selector,
  RTCStatsCollectorTest.GetStatsWithSelector.

[1] https://w3c.github.io/webrtc-pc/#dfn-stats-selection-algorithm
[2] https://cs.chromium.org/chromium/src/third_party/webrtc/pc/rtcstatstraversal.h

Bug: chromium:680172
Change-Id: I9eff00738a1f24c94c9c8ecd13c1304452e962cf
Reviewed-on: https://webrtc-review.googlesource.com/62141
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22499}
This commit is contained in:
Henrik Boström
2018-03-19 13:52:56 +01:00
committed by Commit Bot
parent 4a73cd436c
commit 5b3541f9af
6 changed files with 431 additions and 42 deletions

View File

@ -57,6 +57,7 @@ class RTCStatsReport : public rtc::RefCountInterface {
explicit RTCStatsReport(int64_t timestamp_us); explicit RTCStatsReport(int64_t timestamp_us);
RTCStatsReport(const RTCStatsReport& other) = delete; RTCStatsReport(const RTCStatsReport& other) = delete;
rtc::scoped_refptr<RTCStatsReport> Copy() const;
int64_t timestamp_us() const { return timestamp_us_; } int64_t timestamp_us() const { return timestamp_us_; }
void AddStats(std::unique_ptr<const RTCStats> stats); void AddStats(std::unique_ptr<const RTCStats> stats);

View File

@ -23,6 +23,7 @@
#include "p2p/base/p2pconstants.h" #include "p2p/base/p2pconstants.h"
#include "p2p/base/port.h" #include "p2p/base/port.h"
#include "pc/peerconnection.h" #include "pc/peerconnection.h"
#include "pc/rtcstatstraversal.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
#include "rtc_base/ptr_util.h" #include "rtc_base/ptr_util.h"
#include "rtc_base/stringutils.h" #include "rtc_base/stringutils.h"
@ -606,8 +607,93 @@ void ProduceReceiverMediaTrackStats(
} }
} }
rtc::scoped_refptr<RTCStatsReport> CreateReportFilteredBySelector(
bool filter_by_sender_selector,
rtc::scoped_refptr<const RTCStatsReport> report,
rtc::scoped_refptr<RtpSenderInternal> sender_selector,
rtc::scoped_refptr<RtpReceiverInternal> receiver_selector) {
std::vector<std::string> rtpstream_ids;
if (filter_by_sender_selector) {
// Filter mode: RTCStatsCollector::RequestInfo::kSenderSelector
if (sender_selector) {
// Find outbound-rtp(s) of the sender, i.e. the outbound-rtp(s) that
// reference the sender stats.
// Because we do not implement sender stats, we look at outbound-rtp(s)
// that reference the track attachment stats for the sender instead.
std::string track_id =
RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
kSender, sender_selector->AttachmentId());
for (const auto& stats : *report) {
if (stats.type() != RTCOutboundRTPStreamStats::kType)
continue;
const auto& outbound_rtp = stats.cast_to<RTCOutboundRTPStreamStats>();
if (outbound_rtp.track_id.is_defined() &&
*outbound_rtp.track_id == track_id) {
rtpstream_ids.push_back(outbound_rtp.id());
}
}
}
} else {
// Filter mode: RTCStatsCollector::RequestInfo::kReceiverSelector
if (receiver_selector) {
// Find inbound-rtp(s) of the receiver, i.e. the inbound-rtp(s) that
// reference the receiver stats.
// Because we do not implement receiver stats, we look at inbound-rtp(s)
// that reference the track attachment stats for the receiver instead.
std::string track_id =
RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(
kReceiver, receiver_selector->AttachmentId());
for (const auto& stats : *report) {
if (stats.type() != RTCInboundRTPStreamStats::kType)
continue;
const auto& inbound_rtp = stats.cast_to<RTCInboundRTPStreamStats>();
if (inbound_rtp.track_id.is_defined() &&
*inbound_rtp.track_id == track_id) {
rtpstream_ids.push_back(inbound_rtp.id());
}
}
}
}
if (rtpstream_ids.empty())
return RTCStatsReport::Create(report->timestamp_us());
return TakeReferencedStats(report->Copy(), rtpstream_ids);
}
} // namespace } // namespace
RTCStatsCollector::RequestInfo::RequestInfo(
rtc::scoped_refptr<RTCStatsCollectorCallback> callback)
: RequestInfo(FilterMode::kAll, std::move(callback), nullptr, nullptr) {}
RTCStatsCollector::RequestInfo::RequestInfo(
rtc::scoped_refptr<RtpSenderInternal> selector,
rtc::scoped_refptr<RTCStatsCollectorCallback> callback)
: RequestInfo(FilterMode::kSenderSelector,
std::move(callback),
std::move(selector),
nullptr) {}
RTCStatsCollector::RequestInfo::RequestInfo(
rtc::scoped_refptr<RtpReceiverInternal> selector,
rtc::scoped_refptr<RTCStatsCollectorCallback> callback)
: RequestInfo(FilterMode::kReceiverSelector,
std::move(callback),
nullptr,
std::move(selector)) {}
RTCStatsCollector::RequestInfo::RequestInfo(
RTCStatsCollector::RequestInfo::FilterMode filter_mode,
rtc::scoped_refptr<RTCStatsCollectorCallback> callback,
rtc::scoped_refptr<RtpSenderInternal> sender_selector,
rtc::scoped_refptr<RtpReceiverInternal> receiver_selector)
: filter_mode_(filter_mode),
callback_(std::move(callback)),
sender_selector_(std::move(sender_selector)),
receiver_selector_(std::move(receiver_selector)) {
RTC_DCHECK(callback_);
RTC_DCHECK(!sender_selector_ || !receiver_selector_);
}
rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create( rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create(
PeerConnectionInternal* pc, PeerConnectionInternal* pc,
int64_t cache_lifetime_us) { int64_t cache_lifetime_us) {
@ -640,9 +726,25 @@ RTCStatsCollector::~RTCStatsCollector() {
void RTCStatsCollector::GetStatsReport( void RTCStatsCollector::GetStatsReport(
rtc::scoped_refptr<RTCStatsCollectorCallback> callback) { rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {
GetStatsReportInternal(RequestInfo(std::move(callback)));
}
void RTCStatsCollector::GetStatsReport(
rtc::scoped_refptr<RtpSenderInternal> selector,
rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {
GetStatsReportInternal(RequestInfo(std::move(selector), std::move(callback)));
}
void RTCStatsCollector::GetStatsReport(
rtc::scoped_refptr<RtpReceiverInternal> selector,
rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {
GetStatsReportInternal(RequestInfo(std::move(selector), std::move(callback)));
}
void RTCStatsCollector::GetStatsReportInternal(
RTCStatsCollector::RequestInfo request) {
RTC_DCHECK(signaling_thread_->IsCurrent()); RTC_DCHECK(signaling_thread_->IsCurrent());
RTC_DCHECK(callback); requests_.push_back(std::move(request));
callbacks_.push_back(callback);
// "Now" using a monotonically increasing timer. // "Now" using a monotonically increasing timer.
int64_t cache_now_us = rtc::TimeMicros(); int64_t cache_now_us = rtc::TimeMicros();
@ -651,13 +753,12 @@ void RTCStatsCollector::GetStatsReport(
// We have a fresh cached report to deliver. Deliver asynchronously, since // We have a fresh cached report to deliver. Deliver asynchronously, since
// the caller may not be expecting a synchronous callback, and it avoids // the caller may not be expecting a synchronous callback, and it avoids
// reentrancy problems. // reentrancy problems.
std::vector<rtc::scoped_refptr<RTCStatsCollectorCallback>> callbacks; std::vector<RequestInfo> requests;
callbacks.swap(callbacks_); requests.swap(requests_);
invoker_.AsyncInvoke<void>( invoker_.AsyncInvoke<void>(
RTC_FROM_HERE, signaling_thread_, RTC_FROM_HERE, signaling_thread_,
rtc::Bind(&RTCStatsCollector::DeliverCachedReport, this, cached_report_, rtc::Bind(&RTCStatsCollector::DeliverCachedReport, this, cached_report_,
std::move(callbacks))); std::move(requests)));
callbacks_.clear();
} else if (!num_pending_partial_reports_) { } else if (!num_pending_partial_reports_) {
// Only start gathering stats if we're not already gathering stats. In the // Only start gathering stats if we're not already gathering stats. In the
// case of already gathering stats, |callback_| will be invoked when there // case of already gathering stats, |callback_| will be invoked when there
@ -781,24 +882,40 @@ void RTCStatsCollector::AddPartialResults_s(
TRACE_EVENT_INSTANT1("webrtc_stats", "webrtc_stats", "report", TRACE_EVENT_INSTANT1("webrtc_stats", "webrtc_stats", "report",
cached_report_->ToJson()); cached_report_->ToJson());
// Swap the list of callbacks, in case one of them recursively calls // Deliver report and clear |requests_|.
// GetStatsReport again and modifies the callback list. std::vector<RequestInfo> requests;
std::vector<rtc::scoped_refptr<RTCStatsCollectorCallback>> callbacks; requests.swap(requests_);
callbacks.swap(callbacks_); DeliverCachedReport(cached_report_, std::move(requests));
DeliverCachedReport(cached_report_, std::move(callbacks));
} }
} }
void RTCStatsCollector::DeliverCachedReport( void RTCStatsCollector::DeliverCachedReport(
rtc::scoped_refptr<const RTCStatsReport> cached_report, rtc::scoped_refptr<const RTCStatsReport> cached_report,
std::vector<rtc::scoped_refptr<RTCStatsCollectorCallback>> callbacks) { std::vector<RTCStatsCollector::RequestInfo> requests) {
RTC_DCHECK(signaling_thread_->IsCurrent()); RTC_DCHECK(signaling_thread_->IsCurrent());
RTC_DCHECK(!callbacks.empty()); RTC_DCHECK(!requests.empty());
RTC_DCHECK(cached_report); RTC_DCHECK(cached_report);
for (const rtc::scoped_refptr<RTCStatsCollectorCallback>& callback : for (const RequestInfo& request : requests) {
callbacks) { if (request.filter_mode() == RequestInfo::FilterMode::kAll) {
callback->OnStatsDelivered(cached_report); request.callback()->OnStatsDelivered(cached_report);
} else {
bool filter_by_sender_selector;
rtc::scoped_refptr<RtpSenderInternal> sender_selector;
rtc::scoped_refptr<RtpReceiverInternal> receiver_selector;
if (request.filter_mode() == RequestInfo::FilterMode::kSenderSelector) {
filter_by_sender_selector = true;
sender_selector = request.sender_selector();
} else {
RTC_DCHECK(request.filter_mode() ==
RequestInfo::FilterMode::kReceiverSelector);
filter_by_sender_selector = false;
receiver_selector = request.receiver_selector();
}
request.callback()->OnStatsDelivered(CreateReportFilteredBySelector(
filter_by_sender_selector, cached_report, sender_selector,
receiver_selector));
}
} }
} }

View File

@ -35,6 +35,9 @@
namespace webrtc { namespace webrtc {
class RtpSenderInternal;
class RtpReceiverInternal;
// All public methods of the collector are to be called on the signaling thread. // All public methods of the collector are to be called on the signaling thread.
// Stats are gathered on the signaling, worker and network threads // Stats are gathered on the signaling, worker and network threads
// asynchronously. The callback is invoked on the signaling thread. Resulting // asynchronously. The callback is invoked on the signaling thread. Resulting
@ -50,7 +53,18 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface,
// it is returned, otherwise new stats are gathered and returned. A report is // it is returned, otherwise new stats are gathered and returned. A report is
// considered fresh for |cache_lifetime_| ms. const RTCStatsReports are safe // considered fresh for |cache_lifetime_| ms. const RTCStatsReports are safe
// to use across multiple threads and may be destructed on any thread. // to use across multiple threads and may be destructed on any thread.
// If the optional selector argument is used, stats are filtered according to
// stats selection algorithm before delivery.
// https://w3c.github.io/webrtc-pc/#dfn-stats-selection-algorithm
void GetStatsReport(rtc::scoped_refptr<RTCStatsCollectorCallback> callback); void GetStatsReport(rtc::scoped_refptr<RTCStatsCollectorCallback> callback);
// If |selector| is null the selection algorithm is still applied (interpreted
// as: no RTP streams are sent by selector). The result is empty.
void GetStatsReport(rtc::scoped_refptr<RtpSenderInternal> selector,
rtc::scoped_refptr<RTCStatsCollectorCallback> callback);
// If |selector| is null the selection algorithm is still applied (interpreted
// as: no RTP streams are received by selector). The result is empty.
void GetStatsReport(rtc::scoped_refptr<RtpReceiverInternal> selector,
rtc::scoped_refptr<RTCStatsCollectorCallback> callback);
// Clears the cache's reference to the most recent stats report. Subsequently // Clears the cache's reference to the most recent stats report. Subsequently
// calling |GetStatsReport| guarantees fresh stats. // calling |GetStatsReport| guarantees fresh stats.
void ClearCachedStatsReport(); void ClearCachedStatsReport();
@ -73,6 +87,49 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface,
const rtc::scoped_refptr<RTCStatsReport>& partial_report); const rtc::scoped_refptr<RTCStatsReport>& partial_report);
private: private:
class RequestInfo {
public:
enum class FilterMode { kAll, kSenderSelector, kReceiverSelector };
// Constructs with FilterMode::kAll.
explicit RequestInfo(
rtc::scoped_refptr<RTCStatsCollectorCallback> callback);
// Constructs with FilterMode::kSenderSelector. The selection algorithm is
// applied even if |selector| is null, resulting in an empty report.
RequestInfo(rtc::scoped_refptr<RtpSenderInternal> selector,
rtc::scoped_refptr<RTCStatsCollectorCallback> callback);
// Constructs with FilterMode::kReceiverSelector. The selection algorithm is
// applied even if |selector| is null, resulting in an empty report.
RequestInfo(rtc::scoped_refptr<RtpReceiverInternal> selector,
rtc::scoped_refptr<RTCStatsCollectorCallback> callback);
FilterMode filter_mode() const { return filter_mode_; }
rtc::scoped_refptr<RTCStatsCollectorCallback> callback() const {
return callback_;
}
rtc::scoped_refptr<RtpSenderInternal> sender_selector() const {
RTC_DCHECK(filter_mode_ == FilterMode::kSenderSelector);
return sender_selector_;
}
rtc::scoped_refptr<RtpReceiverInternal> receiver_selector() const {
RTC_DCHECK(filter_mode_ == FilterMode::kReceiverSelector);
return receiver_selector_;
}
private:
RequestInfo(FilterMode filter_mode,
rtc::scoped_refptr<RTCStatsCollectorCallback> callback,
rtc::scoped_refptr<RtpSenderInternal> sender_selector,
rtc::scoped_refptr<RtpReceiverInternal> receiver_selector);
FilterMode filter_mode_;
rtc::scoped_refptr<RTCStatsCollectorCallback> callback_;
rtc::scoped_refptr<RtpSenderInternal> sender_selector_;
rtc::scoped_refptr<RtpReceiverInternal> receiver_selector_;
};
void GetStatsReportInternal(RequestInfo request);
struct CertificateStatsPair { struct CertificateStatsPair {
std::unique_ptr<rtc::SSLCertificateStats> local; std::unique_ptr<rtc::SSLCertificateStats> local;
std::unique_ptr<rtc::SSLCertificateStats> remote; std::unique_ptr<rtc::SSLCertificateStats> remote;
@ -96,7 +153,7 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface,
void AddPartialResults_s(rtc::scoped_refptr<RTCStatsReport> partial_report); void AddPartialResults_s(rtc::scoped_refptr<RTCStatsReport> partial_report);
void DeliverCachedReport( void DeliverCachedReport(
rtc::scoped_refptr<const RTCStatsReport> cached_report, rtc::scoped_refptr<const RTCStatsReport> cached_report,
std::vector<rtc::scoped_refptr<RTCStatsCollectorCallback>> callbacks); std::vector<RequestInfo> requests);
// Produces |RTCCertificateStats|. // Produces |RTCCertificateStats|.
void ProduceCertificateStats_n( void ProduceCertificateStats_n(
@ -168,7 +225,7 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface,
int num_pending_partial_reports_; int num_pending_partial_reports_;
int64_t partial_report_timestamp_us_; int64_t partial_report_timestamp_us_;
rtc::scoped_refptr<RTCStatsReport> partial_report_; rtc::scoped_refptr<RTCStatsReport> partial_report_;
std::vector<rtc::scoped_refptr<RTCStatsCollectorCallback>> callbacks_; std::vector<RequestInfo> requests_;
// Set in |GetStatsReport|, read in |ProducePartialResultsOnNetworkThread| and // Set in |GetStatsReport|, read in |ProducePartialResultsOnNetworkThread| and
// |ProducePartialResultsOnSignalingThread|, reset after work is complete. Not // |ProducePartialResultsOnSignalingThread|, reset after work is complete. Not

View File

@ -299,26 +299,36 @@ class RTCStatsCollectorWrapper {
return stats_collector_; return stats_collector_;
} }
rtc::scoped_refptr<const RTCStatsReport> GetStatsReport() {
rtc::scoped_refptr<RTCStatsObtainer> callback = RTCStatsObtainer::Create();
stats_collector_->GetStatsReport(callback);
return WaitForReport(callback);
}
rtc::scoped_refptr<const RTCStatsReport> GetStatsReportWithSenderSelector(
rtc::scoped_refptr<RtpSenderInternal> selector) {
rtc::scoped_refptr<RTCStatsObtainer> callback = RTCStatsObtainer::Create();
stats_collector_->GetStatsReport(selector, callback);
return WaitForReport(callback);
}
rtc::scoped_refptr<const RTCStatsReport> GetStatsReportWithReceiverSelector(
rtc::scoped_refptr<RtpReceiverInternal> selector) {
rtc::scoped_refptr<RTCStatsObtainer> callback = RTCStatsObtainer::Create();
stats_collector_->GetStatsReport(selector, callback);
return WaitForReport(callback);
}
rtc::scoped_refptr<const RTCStatsReport> GetFreshStatsReport() { rtc::scoped_refptr<const RTCStatsReport> GetFreshStatsReport() {
stats_collector_->ClearCachedStatsReport(); stats_collector_->ClearCachedStatsReport();
return GetStatsReport(); return GetStatsReport();
} }
rtc::scoped_refptr<const RTCStatsReport> GetStatsReport() { rtc::scoped_refptr<MockRtpSenderInternal> SetupLocalTrackAndSender(
rtc::scoped_refptr<RTCStatsObtainer> callback = RTCStatsObtainer::Create(); cricket::MediaType media_type,
stats_collector_->GetStatsReport(callback); const std::string& track_id,
EXPECT_TRUE_WAIT(callback->report(), kGetStatsReportTimeoutMs); uint32_t ssrc,
int64_t after = rtc::TimeUTCMicros(); bool add_stream) {
for (const RTCStats& stats : *callback->report()) {
EXPECT_LE(stats.timestamp_us(), after);
}
return callback->report();
}
void SetupLocalTrackAndSender(cricket::MediaType media_type,
const std::string& track_id,
uint32_t ssrc,
bool add_stream) {
rtc::scoped_refptr<MediaStream> local_stream; rtc::scoped_refptr<MediaStream> local_stream;
if (add_stream) { if (add_stream) {
local_stream = MediaStream::Create("LocalStreamId"); local_stream = MediaStream::Create("LocalStreamId");
@ -343,13 +353,16 @@ class RTCStatsCollectorWrapper {
rtc::scoped_refptr<MockRtpSenderInternal> sender = rtc::scoped_refptr<MockRtpSenderInternal> sender =
CreateMockSender(track, ssrc, 50, {}); CreateMockSender(track, ssrc, 50, {});
pc_->AddSender(sender); pc_->AddSender(sender);
return sender;
} }
void SetupRemoteTrackAndReceiver(cricket::MediaType media_type, rtc::scoped_refptr<MockRtpReceiverInternal> SetupRemoteTrackAndReceiver(
const std::string& track_id, cricket::MediaType media_type,
uint32_t ssrc) { const std::string& track_id,
const std::string& stream_id,
uint32_t ssrc) {
rtc::scoped_refptr<MediaStream> remote_stream = rtc::scoped_refptr<MediaStream> remote_stream =
MediaStream::Create("RemoteStreamId"); MediaStream::Create(stream_id);
pc_->mutable_remote_streams()->AddStream(remote_stream); pc_->mutable_remote_streams()->AddStream(remote_stream);
rtc::scoped_refptr<MediaStreamTrackInterface> track; rtc::scoped_refptr<MediaStreamTrackInterface> track;
@ -370,6 +383,7 @@ class RTCStatsCollectorWrapper {
Return(std::vector<rtc::scoped_refptr<MediaStreamInterface>>( Return(std::vector<rtc::scoped_refptr<MediaStreamInterface>>(
{remote_stream}))); {remote_stream})));
pc_->AddReceiver(receiver); pc_->AddReceiver(receiver);
return receiver;
} }
// Attaches tracks to peer connections by configuring RTP senders and RTP // Attaches tracks to peer connections by configuring RTP senders and RTP
@ -471,6 +485,16 @@ class RTCStatsCollectorWrapper {
} }
private: private:
rtc::scoped_refptr<const RTCStatsReport> WaitForReport(
rtc::scoped_refptr<RTCStatsObtainer> callback) {
EXPECT_TRUE_WAIT(callback->report(), kGetStatsReportTimeoutMs);
int64_t after = rtc::TimeUTCMicros();
for (const RTCStats& stats : *callback->report()) {
EXPECT_LE(stats.timestamp_us(), after);
}
return callback->report();
}
rtc::scoped_refptr<FakePeerConnectionForStats> pc_; rtc::scoped_refptr<FakePeerConnectionForStats> pc_;
rtc::scoped_refptr<RTCStatsCollector> stats_collector_; rtc::scoped_refptr<RTCStatsCollector> stats_collector_;
}; };
@ -502,6 +526,115 @@ class RTCStatsCollectorTest : public testing::Test {
} }
} }
struct ExampleStatsGraph {
rtc::scoped_refptr<RtpSenderInternal> sender;
rtc::scoped_refptr<RtpReceiverInternal> receiver;
rtc::scoped_refptr<const RTCStatsReport> full_report;
std::string send_codec_id;
std::string recv_codec_id;
std::string outbound_rtp_id;
std::string inbound_rtp_id;
std::string transport_id;
std::string sender_track_id;
std::string receiver_track_id;
std::string remote_stream_id;
std::string peer_connection_id;
};
// Sets up the example stats graph (see ASCII art below) used for testing the
// stats selection algorithm,
// https://w3c.github.io/webrtc-pc/#dfn-stats-selection-algorithm.
// These tests test the integration of the stats traversal algorithm inside of
// RTCStatsCollector. See rtcstatstraveral_unittest.cc for more stats
// traversal tests.
ExampleStatsGraph SetupExampleStatsGraphForSelectorTests() {
ExampleStatsGraph graph;
// codec (send)
graph.send_codec_id = "RTCCodec_VideoMid_Outbound_1";
cricket::VideoMediaInfo video_media_info;
RtpCodecParameters send_codec;
send_codec.payload_type = 1;
send_codec.clock_rate = 0;
video_media_info.send_codecs.insert(
std::make_pair(send_codec.payload_type, send_codec));
// codec (recv)
graph.recv_codec_id = "RTCCodec_VideoMid_Inbound_2";
RtpCodecParameters recv_codec;
recv_codec.payload_type = 2;
recv_codec.clock_rate = 0;
video_media_info.receive_codecs.insert(
std::make_pair(recv_codec.payload_type, recv_codec));
// outbound-rtp
graph.outbound_rtp_id = "RTCOutboundRTPVideoStream_3";
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 = 3;
video_media_info.senders[0].codec_payload_type = send_codec.payload_type;
// inbound-rtp
graph.inbound_rtp_id = "RTCInboundRTPVideoStream_4";
video_media_info.receivers.push_back(cricket::VideoReceiverInfo());
video_media_info.receivers[0].local_stats.push_back(
cricket::SsrcReceiverInfo());
video_media_info.receivers[0].local_stats[0].ssrc = 4;
video_media_info.receivers[0].codec_payload_type = recv_codec.payload_type;
// transport
graph.transport_id = "RTCTransport_TransportName_1";
auto* video_media_channel =
pc_->AddVideoChannel("VideoMid", "TransportName");
video_media_channel->SetStats(video_media_info);
// track (sender)
graph.sender = stats_->SetupLocalTrackAndSender(
cricket::MEDIA_TYPE_VIDEO, "LocalVideoTrackID", 3, false);
graph.sender_track_id = "RTCMediaStreamTrack_sender_" +
rtc::ToString<>(graph.sender->AttachmentId());
// track (receiver) and stream (remote stream)
graph.receiver = stats_->SetupRemoteTrackAndReceiver(
cricket::MEDIA_TYPE_VIDEO, "RemoteVideoTrackID", "RemoteStreamId", 4);
graph.receiver_track_id = "RTCMediaStreamTrack_receiver_" +
rtc::ToString<>(graph.receiver->AttachmentId());
graph.remote_stream_id = "RTCMediaStream_RemoteStreamId";
// peer-connection
graph.peer_connection_id = "RTCPeerConnection";
// Expected stats graph:
//
// track (sender) stream (remote stream) ---> track (receiver)
// ^ ^
// | |
// outbound-rtp inbound-rtp ---------------+
// | | | |
// v v v v
// codec (send) transport codec (recv) peer-connection
// Verify the stats graph is set up correctly.
graph.full_report = stats_->GetStatsReport();
EXPECT_EQ(graph.full_report->size(), 9u);
EXPECT_TRUE(graph.full_report->Get(graph.send_codec_id));
EXPECT_TRUE(graph.full_report->Get(graph.recv_codec_id));
EXPECT_TRUE(graph.full_report->Get(graph.outbound_rtp_id));
EXPECT_TRUE(graph.full_report->Get(graph.inbound_rtp_id));
EXPECT_TRUE(graph.full_report->Get(graph.transport_id));
EXPECT_TRUE(graph.full_report->Get(graph.sender_track_id));
EXPECT_TRUE(graph.full_report->Get(graph.receiver_track_id));
EXPECT_TRUE(graph.full_report->Get(graph.remote_stream_id));
EXPECT_TRUE(graph.full_report->Get(graph.peer_connection_id));
const auto& outbound_rtp = graph.full_report->Get(graph.outbound_rtp_id)
->cast_to<RTCOutboundRTPStreamStats>();
EXPECT_EQ(*outbound_rtp.codec_id, graph.send_codec_id);
EXPECT_EQ(*outbound_rtp.track_id, graph.sender_track_id);
EXPECT_EQ(*outbound_rtp.transport_id, graph.transport_id);
const auto& inbound_rtp = graph.full_report->Get(graph.inbound_rtp_id)
->cast_to<RTCInboundRTPStreamStats>();
EXPECT_EQ(*inbound_rtp.codec_id, graph.recv_codec_id);
EXPECT_EQ(*inbound_rtp.track_id, graph.receiver_track_id);
EXPECT_EQ(*inbound_rtp.transport_id, graph.transport_id);
return graph;
}
protected: protected:
rtc::ScopedFakeClock fake_clock_; rtc::ScopedFakeClock fake_clock_;
rtc::scoped_refptr<FakePeerConnectionForStats> pc_; rtc::scoped_refptr<FakePeerConnectionForStats> pc_;
@ -1465,8 +1598,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) {
auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName"); auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName");
voice_media_channel->SetStats(voice_media_info); voice_media_channel->SetStats(voice_media_info);
stats_->SetupRemoteTrackAndReceiver(cricket::MEDIA_TYPE_AUDIO, stats_->SetupRemoteTrackAndReceiver(
"RemoteAudioTrackID", 1); cricket::MEDIA_TYPE_AUDIO, "RemoteAudioTrackID", "RemoteStreamId", 1);
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport(); rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
@ -1523,8 +1656,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) {
auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName");
video_media_channel->SetStats(video_media_info); video_media_channel->SetStats(video_media_info);
stats_->SetupRemoteTrackAndReceiver(cricket::MEDIA_TYPE_VIDEO, stats_->SetupRemoteTrackAndReceiver(
"RemoteVideoTrackID", 1); cricket::MEDIA_TYPE_VIDEO, "RemoteVideoTrackID", "RemoteStreamId", 1);
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport(); rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
@ -1872,6 +2005,78 @@ TEST_F(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRTPStreamStats_Audio) {
EXPECT_TRUE(report->Get(*expected_audio.codec_id)); EXPECT_TRUE(report->Get(*expected_audio.codec_id));
} }
TEST_F(RTCStatsCollectorTest, GetStatsWithSenderSelector) {
ExampleStatsGraph graph = SetupExampleStatsGraphForSelectorTests();
// Expected stats graph when filtered by sender:
//
// track (sender)
// ^
// |
// outbound-rtp
// | |
// v v
// codec (send) transport
rtc::scoped_refptr<const RTCStatsReport> sender_report =
stats_->GetStatsReportWithSenderSelector(graph.sender);
EXPECT_TRUE(sender_report);
EXPECT_EQ(sender_report->timestamp_us(), graph.full_report->timestamp_us());
EXPECT_EQ(sender_report->size(), 4u);
EXPECT_TRUE(sender_report->Get(graph.send_codec_id));
EXPECT_FALSE(sender_report->Get(graph.recv_codec_id));
EXPECT_TRUE(sender_report->Get(graph.outbound_rtp_id));
EXPECT_FALSE(sender_report->Get(graph.inbound_rtp_id));
EXPECT_TRUE(sender_report->Get(graph.transport_id));
EXPECT_TRUE(sender_report->Get(graph.sender_track_id));
EXPECT_FALSE(sender_report->Get(graph.receiver_track_id));
EXPECT_FALSE(sender_report->Get(graph.remote_stream_id));
EXPECT_FALSE(sender_report->Get(graph.peer_connection_id));
}
TEST_F(RTCStatsCollectorTest, GetStatsWithReceiverSelector) {
ExampleStatsGraph graph = SetupExampleStatsGraphForSelectorTests();
// Expected stats graph when filtered by receiver:
//
// track (receiver)
// ^
// |
// inbound-rtp ---------------+
// | |
// v v
// transport codec (recv)
rtc::scoped_refptr<const RTCStatsReport> receiver_report =
stats_->GetStatsReportWithReceiverSelector(graph.receiver);
EXPECT_TRUE(receiver_report);
EXPECT_EQ(receiver_report->size(), 4u);
EXPECT_EQ(receiver_report->timestamp_us(), graph.full_report->timestamp_us());
EXPECT_FALSE(receiver_report->Get(graph.send_codec_id));
EXPECT_TRUE(receiver_report->Get(graph.recv_codec_id));
EXPECT_FALSE(receiver_report->Get(graph.outbound_rtp_id));
EXPECT_TRUE(receiver_report->Get(graph.inbound_rtp_id));
EXPECT_TRUE(receiver_report->Get(graph.transport_id));
EXPECT_FALSE(receiver_report->Get(graph.sender_track_id));
EXPECT_TRUE(receiver_report->Get(graph.receiver_track_id));
EXPECT_FALSE(receiver_report->Get(graph.remote_stream_id));
EXPECT_FALSE(receiver_report->Get(graph.peer_connection_id));
}
TEST_F(RTCStatsCollectorTest, GetStatsWithNullSenderSelector) {
ExampleStatsGraph graph = SetupExampleStatsGraphForSelectorTests();
rtc::scoped_refptr<const RTCStatsReport> empty_report =
stats_->GetStatsReportWithSenderSelector(nullptr);
EXPECT_TRUE(empty_report);
EXPECT_EQ(empty_report->timestamp_us(), graph.full_report->timestamp_us());
EXPECT_EQ(empty_report->size(), 0u);
}
TEST_F(RTCStatsCollectorTest, GetStatsWithNullReceiverSelector) {
ExampleStatsGraph graph = SetupExampleStatsGraphForSelectorTests();
rtc::scoped_refptr<const RTCStatsReport> empty_report =
stats_->GetStatsReportWithReceiverSelector(nullptr);
EXPECT_TRUE(empty_report);
EXPECT_EQ(empty_report->timestamp_us(), graph.full_report->timestamp_us());
EXPECT_EQ(empty_report->size(), 0u);
}
// When the PC has not had SetLocalDescription done, tracks all have // When the PC has not had SetLocalDescription done, tracks all have
// SSRC 0, meaning "unconnected". // SSRC 0, meaning "unconnected".
// In this state, we report on track stats, but not RTP stats. // In this state, we report on track stats, but not RTP stats.

View File

@ -61,7 +61,8 @@ void AddIdsIfDefined(const RTCStatsMember<std::vector<std::string>>& ids,
rtc::scoped_refptr<RTCStatsReport> TakeReferencedStats( rtc::scoped_refptr<RTCStatsReport> TakeReferencedStats(
rtc::scoped_refptr<RTCStatsReport> report, rtc::scoped_refptr<RTCStatsReport> report,
const std::vector<std::string>& ids) { const std::vector<std::string>& ids) {
rtc::scoped_refptr<RTCStatsReport> result = RTCStatsReport::Create(); rtc::scoped_refptr<RTCStatsReport> result =
RTCStatsReport::Create(report->timestamp_us());
for (const auto& id : ids) { for (const auto& id : ids) {
TraverseAndTakeVisitedStats(report.get(), result.get(), id); TraverseAndTakeVisitedStats(report.get(), result.get(), id);
} }

View File

@ -69,6 +69,14 @@ RTCStatsReport::RTCStatsReport(int64_t timestamp_us)
RTCStatsReport::~RTCStatsReport() { RTCStatsReport::~RTCStatsReport() {
} }
rtc::scoped_refptr<RTCStatsReport> RTCStatsReport::Copy() const {
rtc::scoped_refptr<RTCStatsReport> copy = Create(timestamp_us_);
for (auto it = stats_.begin(); it != stats_.end(); ++it) {
copy->AddStats(it->second->copy());
}
return copy;
}
void RTCStatsReport::AddStats(std::unique_ptr<const RTCStats> stats) { void RTCStatsReport::AddStats(std::unique_ptr<const RTCStats> stats) {
auto result = stats_.insert(std::make_pair(std::string(stats->id()), auto result = stats_.insert(std::make_pair(std::string(stats->id()),
std::move(stats))); std::move(stats)));