Skip to content

Commit

Permalink
RTCStatsCollector::GetStatsReport() with optional selector argument.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-by: Taylor Brandstetter <[email protected]>
Commit-Queue: Henrik Boström <[email protected]>
Cr-Commit-Position: refs/heads/master@{#22499}
  • Loading branch information
henbos authored and Commit Bot committed Mar 19, 2018
1 parent 4a73cd4 commit 5b3541f
Show file tree
Hide file tree
Showing 6 changed files with 431 additions and 42 deletions.
1 change: 1 addition & 0 deletions api/stats/rtcstatsreport.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class RTCStatsReport : public rtc::RefCountInterface {

explicit RTCStatsReport(int64_t timestamp_us);
RTCStatsReport(const RTCStatsReport& other) = delete;
rtc::scoped_refptr<RTCStatsReport> Copy() const;

int64_t timestamp_us() const { return timestamp_us_; }
void AddStats(std::unique_ptr<const RTCStats> stats);
Expand Down
149 changes: 133 additions & 16 deletions pc/rtcstatscollector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "p2p/base/p2pconstants.h"
#include "p2p/base/port.h"
#include "pc/peerconnection.h"
#include "pc/rtcstatstraversal.h"
#include "rtc_base/checks.h"
#include "rtc_base/ptr_util.h"
#include "rtc_base/stringutils.h"
Expand Down Expand Up @@ -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

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(
PeerConnectionInternal* pc,
int64_t cache_lifetime_us) {
Expand Down Expand Up @@ -640,9 +726,25 @@ RTCStatsCollector::~RTCStatsCollector() {

void RTCStatsCollector::GetStatsReport(
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(callback);
callbacks_.push_back(callback);
requests_.push_back(std::move(request));

// "Now" using a monotonically increasing timer.
int64_t cache_now_us = rtc::TimeMicros();
Expand All @@ -651,13 +753,12 @@ void RTCStatsCollector::GetStatsReport(
// We have a fresh cached report to deliver. Deliver asynchronously, since
// the caller may not be expecting a synchronous callback, and it avoids
// reentrancy problems.
std::vector<rtc::scoped_refptr<RTCStatsCollectorCallback>> callbacks;
callbacks.swap(callbacks_);
std::vector<RequestInfo> requests;
requests.swap(requests_);
invoker_.AsyncInvoke<void>(
RTC_FROM_HERE, signaling_thread_,
rtc::Bind(&RTCStatsCollector::DeliverCachedReport, this, cached_report_,
std::move(callbacks)));
callbacks_.clear();
std::move(requests)));
} 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
Expand Down Expand Up @@ -781,24 +882,40 @@ void RTCStatsCollector::AddPartialResults_s(
TRACE_EVENT_INSTANT1("webrtc_stats", "webrtc_stats", "report",
cached_report_->ToJson());

// Swap the list of callbacks, in case one of them recursively calls
// GetStatsReport again and modifies the callback list.
std::vector<rtc::scoped_refptr<RTCStatsCollectorCallback>> callbacks;
callbacks.swap(callbacks_);
DeliverCachedReport(cached_report_, std::move(callbacks));
// Deliver report and clear |requests_|.
std::vector<RequestInfo> requests;
requests.swap(requests_);
DeliverCachedReport(cached_report_, std::move(requests));
}
}

void RTCStatsCollector::DeliverCachedReport(
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(!callbacks.empty());
RTC_DCHECK(!requests.empty());
RTC_DCHECK(cached_report);

for (const rtc::scoped_refptr<RTCStatsCollectorCallback>& callback :
callbacks) {
callback->OnStatsDelivered(cached_report);
for (const RequestInfo& request : requests) {
if (request.filter_mode() == RequestInfo::FilterMode::kAll) {
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));
}
}
}

Expand Down
61 changes: 59 additions & 2 deletions pc/rtcstatscollector.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@

namespace webrtc {

class RtpSenderInternal;
class RtpReceiverInternal;

// All public methods of the collector are to be called on the signaling thread.
// Stats are gathered on the signaling, worker and network threads
// asynchronously. The callback is invoked on the signaling thread. Resulting
Expand All @@ -50,7 +53,18 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface,
// it is returned, otherwise new stats are gathered and returned. A report is
// considered fresh for |cache_lifetime_| ms. const RTCStatsReports are safe
// 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);
// 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
// calling |GetStatsReport| guarantees fresh stats.
void ClearCachedStatsReport();
Expand All @@ -73,6 +87,49 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface,
const rtc::scoped_refptr<RTCStatsReport>& partial_report);

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 {
std::unique_ptr<rtc::SSLCertificateStats> local;
std::unique_ptr<rtc::SSLCertificateStats> remote;
Expand All @@ -96,7 +153,7 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface,
void AddPartialResults_s(rtc::scoped_refptr<RTCStatsReport> partial_report);
void DeliverCachedReport(
rtc::scoped_refptr<const RTCStatsReport> cached_report,
std::vector<rtc::scoped_refptr<RTCStatsCollectorCallback>> callbacks);
std::vector<RequestInfo> requests);

// Produces |RTCCertificateStats|.
void ProduceCertificateStats_n(
Expand Down Expand Up @@ -168,7 +225,7 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface,
int num_pending_partial_reports_;
int64_t partial_report_timestamp_us_;
rtc::scoped_refptr<RTCStatsReport> partial_report_;
std::vector<rtc::scoped_refptr<RTCStatsCollectorCallback>> callbacks_;
std::vector<RequestInfo> requests_;

// Set in |GetStatsReport|, read in |ProducePartialResultsOnNetworkThread| and
// |ProducePartialResultsOnSignalingThread|, reset after work is complete. Not
Expand Down
Loading

0 comments on commit 5b3541f

Please sign in to comment.