blob: 66c745d018a9a13aee351fb8c1624316c4824167 [file] [log] [blame]
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/media/webrtc/rtc_stats.h"
#include <algorithm>
#include <set>
#include <string>
#include "base/bind.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/time/time.h"
#include "third_party/webrtc/api/stats/rtcstats_objects.h"
namespace content {
namespace {
class RTCStatsWhitelist {
public:
RTCStatsWhitelist() {
whitelisted_stats_types_.insert(webrtc::RTCCertificateStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCCodecStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCDataChannelStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCIceCandidatePairStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCIceCandidateStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCLocalIceCandidateStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCRemoteIceCandidateStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCMediaStreamStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCMediaStreamTrackStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCPeerConnectionStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCRTPStreamStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCInboundRTPStreamStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCOutboundRTPStreamStats::kType);
whitelisted_stats_types_.insert(webrtc::RTCTransportStats::kType);
}
bool IsWhitelisted(const webrtc::RTCStats& stats) {
return whitelisted_stats_types_.find(stats.type()) !=
whitelisted_stats_types_.end();
}
void WhitelistStatsForTesting(const char* type) {
whitelisted_stats_types_.insert(type);
}
private:
std::set<std::string> whitelisted_stats_types_;
};
RTCStatsWhitelist* GetStatsWhitelist() {
static RTCStatsWhitelist* whitelist = new RTCStatsWhitelist();
return whitelist;
}
bool IsWhitelistedStats(const webrtc::RTCStats& stats) {
return GetStatsWhitelist()->IsWhitelisted(stats);
}
// Filters out any unstandardized members; stats should only be surfaced to JS
// if they're standardized.
std::vector<const webrtc::RTCStatsMemberInterface*> StandardizedMembers(
std::vector<const webrtc::RTCStatsMemberInterface*> stats_members) {
// Note that using "is_standarized" avoids having to maintain a whitelist of
// every single standardized member, as we do at the "stats object" level
// with "RTCStatsWhitelist".
base::EraseIf(stats_members,
[](const webrtc::RTCStatsMemberInterface* member) {
return !member->is_standardized();
});
return stats_members;
}
} // namespace
RTCStatsReport::RTCStatsReport(
const scoped_refptr<const webrtc::RTCStatsReport>& stats_report,
blink::RTCStatsFilter filter)
: stats_report_(stats_report),
it_(stats_report_->begin()),
end_(stats_report_->end()),
filter_(filter) {
DCHECK(stats_report_);
}
RTCStatsReport::~RTCStatsReport() {
}
std::unique_ptr<blink::WebRTCStatsReport> RTCStatsReport::CopyHandle() const {
return std::unique_ptr<blink::WebRTCStatsReport>(
new RTCStatsReport(stats_report_, filter_));
}
std::unique_ptr<blink::WebRTCStats> RTCStatsReport::GetStats(
blink::WebString id) const {
const webrtc::RTCStats* stats = stats_report_->Get(id.Utf8());
if (!stats || !IsWhitelistedStats(*stats))
return std::unique_ptr<blink::WebRTCStats>();
return std::unique_ptr<blink::WebRTCStats>(
new RTCStats(stats_report_, stats, filter_));
}
std::unique_ptr<blink::WebRTCStats> RTCStatsReport::Next() {
while (it_ != end_) {
const webrtc::RTCStats& next = *it_;
++it_;
if (IsWhitelistedStats(next)) {
return std::unique_ptr<blink::WebRTCStats>(
new RTCStats(stats_report_, &next, filter_));
}
}
return std::unique_ptr<blink::WebRTCStats>();
}
size_t RTCStatsReport::Size() const {
// TODO(crbug.com/908072): If there are non-whitelisted stats objects in the
// report, this would return the wrong thing; DCHECK that all objects are
// whitelisted or make this method return the whitelisted count
return stats_report_->size();
}
RTCStats::RTCStats(
const scoped_refptr<const webrtc::RTCStatsReport>& stats_owner,
const webrtc::RTCStats* stats,
blink::RTCStatsFilter filter)
: stats_owner_(stats_owner),
stats_(stats),
stats_members_(filter == blink::RTCStatsFilter::kIncludeNonStandardMembers
? stats->Members()
: StandardizedMembers(stats->Members())) {
DCHECK(stats_owner_);
DCHECK(stats_);
DCHECK(stats_owner_->Get(stats_->id()));
}
RTCStats::~RTCStats() {
}
blink::WebString RTCStats::Id() const {
return blink::WebString::FromUTF8(stats_->id());
}
blink::WebString RTCStats::GetType() const {
return blink::WebString::FromUTF8(stats_->type());
}
double RTCStats::Timestamp() const {
return stats_->timestamp_us() / static_cast<double>(
base::Time::kMicrosecondsPerMillisecond);
}
size_t RTCStats::MembersCount() const {
return stats_members_.size();
}
std::unique_ptr<blink::WebRTCStatsMember> RTCStats::GetMember(size_t i) const {
DCHECK_LT(i, stats_members_.size());
return std::unique_ptr<blink::WebRTCStatsMember>(
new RTCStatsMember(stats_owner_, stats_members_[i]));
}
RTCStatsMember::RTCStatsMember(
const scoped_refptr<const webrtc::RTCStatsReport>& stats_owner,
const webrtc::RTCStatsMemberInterface* member)
: stats_owner_(stats_owner),
member_(member) {
DCHECK(stats_owner_);
DCHECK(member_);
}
RTCStatsMember::~RTCStatsMember() {
}
blink::WebString RTCStatsMember::GetName() const {
return blink::WebString::FromUTF8(member_->name());
}
blink::WebRTCStatsMemberType RTCStatsMember::GetType() const {
switch (member_->type()) {
case webrtc::RTCStatsMemberInterface::kBool:
return blink::kWebRTCStatsMemberTypeBool;
case webrtc::RTCStatsMemberInterface::kInt32:
return blink::kWebRTCStatsMemberTypeInt32;
case webrtc::RTCStatsMemberInterface::kUint32:
return blink::kWebRTCStatsMemberTypeUint32;
case webrtc::RTCStatsMemberInterface::kInt64:
return blink::kWebRTCStatsMemberTypeInt64;
case webrtc::RTCStatsMemberInterface::kUint64:
return blink::kWebRTCStatsMemberTypeUint64;
case webrtc::RTCStatsMemberInterface::kDouble:
return blink::kWebRTCStatsMemberTypeDouble;
case webrtc::RTCStatsMemberInterface::kString:
return blink::kWebRTCStatsMemberTypeString;
case webrtc::RTCStatsMemberInterface::kSequenceBool:
return blink::kWebRTCStatsMemberTypeSequenceBool;
case webrtc::RTCStatsMemberInterface::kSequenceInt32:
return blink::kWebRTCStatsMemberTypeSequenceInt32;
case webrtc::RTCStatsMemberInterface::kSequenceUint32:
return blink::kWebRTCStatsMemberTypeSequenceUint32;
case webrtc::RTCStatsMemberInterface::kSequenceInt64:
return blink::kWebRTCStatsMemberTypeSequenceInt64;
case webrtc::RTCStatsMemberInterface::kSequenceUint64:
return blink::kWebRTCStatsMemberTypeSequenceUint64;
case webrtc::RTCStatsMemberInterface::kSequenceDouble:
return blink::kWebRTCStatsMemberTypeSequenceDouble;
case webrtc::RTCStatsMemberInterface::kSequenceString:
return blink::kWebRTCStatsMemberTypeSequenceString;
default:
NOTREACHED();
return blink::kWebRTCStatsMemberTypeSequenceInt32;
}
}
bool RTCStatsMember::IsDefined() const {
return member_->is_defined();
}
bool RTCStatsMember::ValueBool() const {
DCHECK(IsDefined());
return *member_->cast_to<webrtc::RTCStatsMember<bool>>();
}
int32_t RTCStatsMember::ValueInt32() const {
DCHECK(IsDefined());
return *member_->cast_to<webrtc::RTCStatsMember<int32_t>>();
}
uint32_t RTCStatsMember::ValueUint32() const {
DCHECK(IsDefined());
return *member_->cast_to<webrtc::RTCStatsMember<uint32_t>>();
}
int64_t RTCStatsMember::ValueInt64() const {
DCHECK(IsDefined());
return *member_->cast_to<webrtc::RTCStatsMember<int64_t>>();
}
uint64_t RTCStatsMember::ValueUint64() const {
DCHECK(IsDefined());
return *member_->cast_to<webrtc::RTCStatsMember<uint64_t>>();
}
double RTCStatsMember::ValueDouble() const {
DCHECK(IsDefined());
return *member_->cast_to<webrtc::RTCStatsMember<double>>();
}
blink::WebString RTCStatsMember::ValueString() const {
DCHECK(IsDefined());
return blink::WebString::FromUTF8(
*member_->cast_to<webrtc::RTCStatsMember<std::string>>());
}
blink::WebVector<int> RTCStatsMember::ValueSequenceBool() const {
DCHECK(IsDefined());
const std::vector<bool>& vector =
*member_->cast_to<webrtc::RTCStatsMember<std::vector<bool>>>();
std::vector<int> uint32_vector;
uint32_vector.reserve(vector.size());
for (size_t i = 0; i < vector.size(); ++i) {
uint32_vector.push_back(vector[i] ? 1 : 0);
}
return blink::WebVector<int>(uint32_vector);
}
blink::WebVector<int32_t> RTCStatsMember::ValueSequenceInt32() const {
DCHECK(IsDefined());
return blink::WebVector<int32_t>(
*member_->cast_to<webrtc::RTCStatsMember<std::vector<int32_t>>>());
}
blink::WebVector<uint32_t> RTCStatsMember::ValueSequenceUint32() const {
DCHECK(IsDefined());
return blink::WebVector<uint32_t>(
*member_->cast_to<webrtc::RTCStatsMember<std::vector<uint32_t>>>());
}
blink::WebVector<int64_t> RTCStatsMember::ValueSequenceInt64() const {
DCHECK(IsDefined());
return blink::WebVector<int64_t>(
*member_->cast_to<webrtc::RTCStatsMember<std::vector<int64_t>>>());
}
blink::WebVector<uint64_t> RTCStatsMember::ValueSequenceUint64() const {
DCHECK(IsDefined());
return blink::WebVector<uint64_t>(
*member_->cast_to<webrtc::RTCStatsMember<std::vector<uint64_t>>>());
}
blink::WebVector<double> RTCStatsMember::ValueSequenceDouble() const {
DCHECK(IsDefined());
return blink::WebVector<double>(
*member_->cast_to<webrtc::RTCStatsMember<std::vector<double>>>());
}
blink::WebVector<blink::WebString> RTCStatsMember::ValueSequenceString() const {
DCHECK(IsDefined());
const std::vector<std::string>& sequence =
*member_->cast_to<webrtc::RTCStatsMember<std::vector<std::string>>>();
blink::WebVector<blink::WebString> web_sequence(sequence.size());
for (size_t i = 0; i < sequence.size(); ++i)
web_sequence[i] = blink::WebString::FromUTF8(sequence[i]);
return web_sequence;
}
// static
rtc::scoped_refptr<RTCStatsCollectorCallbackImpl>
RTCStatsCollectorCallbackImpl::Create(
scoped_refptr<base::SingleThreadTaskRunner> main_thread,
std::unique_ptr<blink::WebRTCStatsReportCallback> callback,
blink::RTCStatsFilter filter) {
return rtc::scoped_refptr<RTCStatsCollectorCallbackImpl>(
new rtc::RefCountedObject<RTCStatsCollectorCallbackImpl>(
std::move(main_thread), callback.release(), filter));
}
RTCStatsCollectorCallbackImpl::RTCStatsCollectorCallbackImpl(
scoped_refptr<base::SingleThreadTaskRunner> main_thread,
blink::WebRTCStatsReportCallback* callback,
blink::RTCStatsFilter filter)
: main_thread_(std::move(main_thread)),
callback_(callback),
filter_(filter) {}
RTCStatsCollectorCallbackImpl::~RTCStatsCollectorCallbackImpl() {
DCHECK(!callback_);
}
void RTCStatsCollectorCallbackImpl::OnStatsDelivered(
const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report) {
main_thread_->PostTask(
FROM_HERE,
base::BindOnce(
&RTCStatsCollectorCallbackImpl::OnStatsDeliveredOnMainThread,
rtc::scoped_refptr<RTCStatsCollectorCallbackImpl>(this), report));
}
void RTCStatsCollectorCallbackImpl::OnStatsDeliveredOnMainThread(
rtc::scoped_refptr<const webrtc::RTCStatsReport> report) {
DCHECK(main_thread_->BelongsToCurrentThread());
DCHECK(report);
DCHECK(callback_);
callback_->OnStatsDelivered(std::make_unique<RTCStatsReport>(
base::WrapRefCounted(report.get()), filter_));
// Make sure the callback is destroyed in the main thread as well.
callback_.reset();
}
void WhitelistStatsForTesting(const char* type) {
GetStatsWhitelist()->WhitelistStatsForTesting(type);
}
} // namespace content