blob: 5a4f4b8f5af3042e60d3e8010a309e4ffad3e4f8 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h"
#include <set>
#include <string>
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/containers/cxx20_erase.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_piece.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/platform/peerconnection/rtc_scoped_refptr_cross_thread_copier.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
#include "third_party/webrtc/api/stats/rtc_stats.h"
#include "third_party/webrtc/api/stats/rtcstats_objects.h"
namespace blink {
// TODO(https://crbug.com/webrtc/14554): When there exists a flag in WebRTC to
// not collect deprecated stats in the first place, make use of that flag and
// unship the filtering mechanism controlled by `WebRtcUnshipDeprecatedStats`.
BASE_FEATURE(WebRtcUnshipDeprecatedStats,
"WebRtcUnshipDeprecatedStats",
base::FEATURE_DISABLED_BY_DEFAULT);
namespace {
bool MemberIsReferenceToDeprecated(
const webrtc::RTCStatsMemberInterface* member) {
// ID references are strings with a defined value.
if (member->type() != webrtc::RTCStatsMemberInterface::Type::kString ||
!member->is_defined()) {
return false;
}
const char* member_name = member->name();
size_t len = strlen(member_name);
// ID referenced end with "Id" by naming convention.
if (len < 2 || member_name[len - 2] != 'I' || member_name[len - 1] != 'd')
return false;
const std::string& id_reference =
*member->cast_to<webrtc::RTCStatsMember<std::string>>();
// starts_with()
return id_reference.rfind("DEPRECATED_", 0) == 0;
}
// Members are surfaced if one of the following is true:
// - They're standardized and if `unship_deprecated_stats` is true they aren't
// references to a deprecated object.
// - There is an active origin trial exposing that particular member.
// - There is an active feature exposing non-standard stats.
std::vector<const webrtc::RTCStatsMemberInterface*> FilterMembers(
std::vector<const webrtc::RTCStatsMemberInterface*> stats_members,
const Vector<webrtc::NonStandardGroupId>& exposed_group_ids,
bool unship_deprecated_stats) {
if (base::FeatureList::IsEnabled(
blink::features::kWebRtcExposeNonStandardStats)) {
return stats_members;
}
base::EraseIf(
stats_members, [&exposed_group_ids, &unship_deprecated_stats](
const webrtc::RTCStatsMemberInterface* member) {
if (member->is_standardized()) {
// Standard members are only erased when filtering out "DEPRECATED_"
// ID references.
return unship_deprecated_stats &&
MemberIsReferenceToDeprecated(member);
}
// Non-standard members are erased unless part of the exposed groups.
const std::vector<webrtc::NonStandardGroupId>& ids =
member->group_ids();
for (const webrtc::NonStandardGroupId& id : exposed_group_ids) {
if (base::Contains(ids, id)) {
return false;
}
}
return true;
});
return stats_members;
}
template <typename T>
Vector<T> ToWTFVector(const std::vector<T>& vector) {
Vector<T> wtf_vector(base::checked_cast<WTF::wtf_size_t>(vector.size()));
std::move(vector.begin(), vector.end(), wtf_vector.begin());
return wtf_vector;
}
bool ShouldExposeStatsObject(const webrtc::RTCStats& stats,
bool unship_deprecated_stats) {
if (!unship_deprecated_stats)
return true;
// !starts_with()
return stats.id().rfind("DEPRECATED_", 0) != 0;
}
size_t CountExposedStatsObjects(
const scoped_refptr<const webrtc::RTCStatsReport>& stats_report,
bool unship_deprecated_stats) {
if (!unship_deprecated_stats)
return stats_report->size();
size_t count = 0u;
for (const auto& stats : *stats_report) {
if (ShouldExposeStatsObject(stats, unship_deprecated_stats))
++count;
}
return count;
}
} // namespace
RTCStatsReportPlatform::RTCStatsReportPlatform(
const scoped_refptr<const webrtc::RTCStatsReport>& stats_report,
const Vector<webrtc::NonStandardGroupId>& exposed_group_ids)
: unship_deprecated_stats_(
base::FeatureList::IsEnabled(WebRtcUnshipDeprecatedStats)),
stats_report_(stats_report),
it_(stats_report_->begin()),
end_(stats_report_->end()),
exposed_group_ids_(exposed_group_ids),
size_(CountExposedStatsObjects(stats_report, unship_deprecated_stats_)) {
DCHECK(stats_report_);
}
RTCStatsReportPlatform::~RTCStatsReportPlatform() {}
std::unique_ptr<RTCStatsReportPlatform> RTCStatsReportPlatform::CopyHandle()
const {
return std::make_unique<RTCStatsReportPlatform>(stats_report_,
exposed_group_ids_);
}
std::unique_ptr<RTCStats> RTCStatsReportPlatform::GetStats(
const String& id) const {
const webrtc::RTCStats* stats = stats_report_->Get(id.Utf8());
if (!stats || !ShouldExposeStatsObject(*stats, unship_deprecated_stats_))
return std::unique_ptr<RTCStats>();
return std::make_unique<RTCStats>(stats_report_, stats, exposed_group_ids_,
unship_deprecated_stats_);
}
std::unique_ptr<RTCStats> RTCStatsReportPlatform::Next() {
while (it_ != end_) {
const webrtc::RTCStats& next = *it_;
++it_;
if (ShouldExposeStatsObject(next, unship_deprecated_stats_)) {
return std::make_unique<RTCStats>(
stats_report_, &next, exposed_group_ids_, unship_deprecated_stats_);
}
}
return std::unique_ptr<RTCStats>();
}
size_t RTCStatsReportPlatform::Size() const {
return size_;
}
RTCStats::RTCStats(
const scoped_refptr<const webrtc::RTCStatsReport>& stats_owner,
const webrtc::RTCStats* stats,
const Vector<webrtc::NonStandardGroupId>& exposed_group_ids,
bool unship_deprecated_stats)
: stats_owner_(stats_owner),
stats_(stats),
stats_members_(FilterMembers(stats->Members(),
exposed_group_ids,
unship_deprecated_stats)) {
DCHECK(stats_owner_);
DCHECK(stats_);
DCHECK(stats_owner_->Get(stats_->id()));
}
RTCStats::~RTCStats() = default;
String RTCStats::Id() const {
return String::FromUTF8(stats_->id());
}
String RTCStats::GetType() const {
return String::FromUTF8(stats_->type());
}
double RTCStats::TimestampMs() const {
// The timestamp unit is milliseconds but we want decimal
// precision so we convert ourselves.
return stats_->timestamp().us() /
static_cast<double>(base::Time::kMicrosecondsPerMillisecond);
}
size_t RTCStats::MembersCount() const {
return stats_members_.size();
}
std::unique_ptr<RTCStatsMember> RTCStats::GetMember(size_t i) const {
DCHECK_LT(i, stats_members_.size());
return std::make_unique<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() = default;
String RTCStatsMember::GetName() const {
return String::FromUTF8(member_->name());
}
webrtc::RTCStatsMemberInterface::Type RTCStatsMember::GetType() const {
return member_->type();
}
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>>();
}
String RTCStatsMember::ValueString() const {
DCHECK(IsDefined());
return String::FromUTF8(
*member_->cast_to<webrtc::RTCStatsMember<std::string>>());
}
Vector<bool> RTCStatsMember::ValueSequenceBool() const {
DCHECK(IsDefined());
const std::vector<bool> vector =
*member_->cast_to<webrtc::RTCStatsMember<std::vector<bool>>>();
return ToWTFVector(vector);
}
Vector<int32_t> RTCStatsMember::ValueSequenceInt32() const {
DCHECK(IsDefined());
const std::vector<int32_t> vector =
*member_->cast_to<webrtc::RTCStatsMember<std::vector<int32_t>>>();
return ToWTFVector(vector);
}
Vector<uint32_t> RTCStatsMember::ValueSequenceUint32() const {
DCHECK(IsDefined());
const std::vector<uint32_t> vector =
*member_->cast_to<webrtc::RTCStatsMember<std::vector<uint32_t>>>();
return ToWTFVector(vector);
}
Vector<int64_t> RTCStatsMember::ValueSequenceInt64() const {
DCHECK(IsDefined());
const std::vector<int64_t> vector =
*member_->cast_to<webrtc::RTCStatsMember<std::vector<int64_t>>>();
return ToWTFVector(vector);
}
Vector<uint64_t> RTCStatsMember::ValueSequenceUint64() const {
DCHECK(IsDefined());
const std::vector<uint64_t> vector =
*member_->cast_to<webrtc::RTCStatsMember<std::vector<uint64_t>>>();
return ToWTFVector(vector);
}
Vector<double> RTCStatsMember::ValueSequenceDouble() const {
DCHECK(IsDefined());
const std::vector<double> vector =
*member_->cast_to<webrtc::RTCStatsMember<std::vector<double>>>();
return ToWTFVector(vector);
}
Vector<String> RTCStatsMember::ValueSequenceString() const {
DCHECK(IsDefined());
const std::vector<std::string>& sequence =
*member_->cast_to<webrtc::RTCStatsMember<std::vector<std::string>>>();
Vector<String> wtf_sequence(base::checked_cast<wtf_size_t>(sequence.size()));
for (wtf_size_t i = 0; i < wtf_sequence.size(); ++i)
wtf_sequence[i] = String::FromUTF8(sequence[i]);
return wtf_sequence;
}
HashMap<String, uint64_t> RTCStatsMember::ValueMapStringUint64() const {
DCHECK(IsDefined());
const std::map<std::string, uint64_t>& map =
*member_
->cast_to<webrtc::RTCStatsMember<std::map<std::string, uint64_t>>>();
HashMap<String, uint64_t> wtf_map;
wtf_map.ReserveCapacityForSize(base::checked_cast<unsigned>(map.size()));
for (auto& elem : map) {
wtf_map.insert(String::FromUTF8(elem.first), elem.second);
}
return wtf_map;
}
HashMap<String, double> RTCStatsMember::ValueMapStringDouble() const {
DCHECK(IsDefined());
const std::map<std::string, double>& map =
*member_
->cast_to<webrtc::RTCStatsMember<std::map<std::string, double>>>();
HashMap<String, double> wtf_map;
wtf_map.ReserveCapacityForSize(base::checked_cast<unsigned>(map.size()));
for (auto& elem : map) {
wtf_map.insert(String::FromUTF8(elem.first), elem.second);
}
return wtf_map;
}
RTCStatsMember::ExposureRestriction RTCStatsMember::Restriction() const {
switch (member_->exposure_criteria()) {
case webrtc::StatExposureCriteria::kHardwareCapability:
return ExposureRestriction::kHardwareCapability;
case webrtc::StatExposureCriteria::kAlways:
default:
return ExposureRestriction::kNone;
}
}
rtc::scoped_refptr<webrtc::RTCStatsCollectorCallback>
CreateRTCStatsCollectorCallback(
scoped_refptr<base::SingleThreadTaskRunner> main_thread,
RTCStatsReportCallback callback,
const Vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
return rtc::scoped_refptr<RTCStatsCollectorCallbackImpl>(
new rtc::RefCountedObject<RTCStatsCollectorCallbackImpl>(
std::move(main_thread), std::move(callback), exposed_group_ids));
}
RTCStatsCollectorCallbackImpl::RTCStatsCollectorCallbackImpl(
scoped_refptr<base::SingleThreadTaskRunner> main_thread,
RTCStatsReportCallback callback,
const Vector<webrtc::NonStandardGroupId>& exposed_group_ids)
: main_thread_(std::move(main_thread)),
callback_(std::move(callback)),
exposed_group_ids_(exposed_group_ids) {}
RTCStatsCollectorCallbackImpl::~RTCStatsCollectorCallbackImpl() {
DCHECK(!callback_);
}
void RTCStatsCollectorCallbackImpl::OnStatsDelivered(
const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report) {
PostCrossThreadTask(
*main_thread_.get(), FROM_HERE,
CrossThreadBindOnce(
&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_);
// Make sure the callback is destroyed in the main thread as well.
std::move(callback_).Run(std::make_unique<RTCStatsReportPlatform>(
base::WrapRefCounted(report.get()), exposed_group_ids_));
}
} // namespace blink