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:
committed by
Commit Bot
parent
4a73cd436c
commit
5b3541f9af
@ -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);
|
||||||
|
|||||||
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)));
|
||||||
|
|||||||
Reference in New Issue
Block a user