| // 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 "third_party/blink/renderer/platform/peerconnection/rtc_stats.h" |
| |
| #include <algorithm> |
| #include <set> |
| #include <string> |
| |
| #include "base/check_op.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/time/time.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/webrtc/api/stats/rtc_stats.h" |
| #include "third_party/webrtc/api/stats/rtcstats_objects.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| class RTCStatsAllowlist { |
| public: |
| RTCStatsAllowlist() { |
| allowlisted_stats_types_.insert(webrtc::RTCCertificateStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCCodecStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCDataChannelStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCIceCandidatePairStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCIceCandidateStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCLocalIceCandidateStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCRemoteIceCandidateStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCMediaStreamStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCMediaStreamTrackStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCPeerConnectionStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCRTPStreamStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCInboundRTPStreamStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCOutboundRTPStreamStats::kType); |
| allowlisted_stats_types_.insert( |
| webrtc::RTCRemoteInboundRtpStreamStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCMediaSourceStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCAudioSourceStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCVideoSourceStats::kType); |
| allowlisted_stats_types_.insert(webrtc::RTCTransportStats::kType); |
| } |
| |
| bool IsAllowlisted(const webrtc::RTCStats& stats) { |
| return allowlisted_stats_types_.find(stats.type()) != |
| allowlisted_stats_types_.end(); |
| } |
| |
| void AllowStatsForTesting(const char* type) { |
| allowlisted_stats_types_.insert(type); |
| } |
| |
| private: |
| std::set<std::string> allowlisted_stats_types_; |
| }; |
| |
| RTCStatsAllowlist* GetStatsAllowlist() { |
| static RTCStatsAllowlist* list = new RTCStatsAllowlist(); |
| return list; |
| } |
| |
| bool IsAllowlistedStats(const webrtc::RTCStats& stats) { |
| return GetStatsAllowlist()->IsAllowlisted(stats); |
| } |
| |
| // Filters stats that should be surfaced to JS. Stats are surfaced if they're |
| // standardized or if there is an active origin trial that enables a stat by |
| // including one of its group IDs in |exposed_group_ids|. |
| std::vector<const webrtc::RTCStatsMemberInterface*> FilterMembers( |
| std::vector<const webrtc::RTCStatsMemberInterface*> stats_members, |
| const Vector<webrtc::NonStandardGroupId>& exposed_group_ids) { |
| // Note that using "is_standarized" avoids having to maintain an allowlist of |
| // every single standardized member, as we do at the "stats object" level |
| // with "RTCStatsAllowlist". |
| base::EraseIf( |
| stats_members, |
| [&exposed_group_ids](const webrtc::RTCStatsMemberInterface* member) { |
| if (member->is_standardized()) { |
| return false; |
| } |
| |
| const std::vector<webrtc::NonStandardGroupId>& ids = |
| member->group_ids(); |
| for (const webrtc::NonStandardGroupId& id : exposed_group_ids) { |
| if (std::find(ids.begin(), ids.end(), id) != ids.end()) { |
| return false; |
| } |
| } |
| return true; |
| }); |
| return stats_members; |
| } |
| |
| size_t CountAllowlistedStats( |
| const scoped_refptr<const webrtc::RTCStatsReport>& stats_report) { |
| size_t size = 0; |
| for (const auto& stats : *stats_report) { |
| if (IsAllowlistedStats(stats)) { |
| ++size; |
| } |
| } |
| return size; |
| } |
| |
| template <typename T> |
| Vector<T> ToWTFVector(const std::vector<T>& vector) { |
| Vector<T> wtf_vector(SafeCast<WTF::wtf_size_t>(vector.size())); |
| std::move(vector.begin(), vector.end(), wtf_vector.begin()); |
| return wtf_vector; |
| } |
| |
| } // namespace |
| |
| RTCStatsReportPlatform::RTCStatsReportPlatform( |
| const scoped_refptr<const webrtc::RTCStatsReport>& stats_report, |
| const Vector<webrtc::NonStandardGroupId>& exposed_group_ids) |
| : stats_report_(stats_report), |
| it_(stats_report_->begin()), |
| end_(stats_report_->end()), |
| exposed_group_ids_(exposed_group_ids), |
| size_(CountAllowlistedStats(stats_report)) { |
| 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 || !IsAllowlistedStats(*stats)) |
| return std::unique_ptr<RTCStats>(); |
| return std::make_unique<RTCStats>(stats_report_, stats, exposed_group_ids_); |
| } |
| |
| std::unique_ptr<RTCStats> RTCStatsReportPlatform::Next() { |
| while (it_ != end_) { |
| const webrtc::RTCStats& next = *it_; |
| ++it_; |
| if (IsAllowlistedStats(next)) { |
| return std::make_unique<RTCStats>(stats_report_, &next, |
| exposed_group_ids_); |
| } |
| } |
| 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) |
| : stats_owner_(stats_owner), |
| stats_(stats), |
| stats_members_(FilterMembers(stats->Members(), exposed_group_ids)) { |
| DCHECK(stats_owner_); |
| DCHECK(stats_); |
| DCHECK(stats_owner_->Get(stats_->id())); |
| } |
| |
| RTCStats::~RTCStats() {} |
| |
| String RTCStats::Id() const { |
| return String::FromUTF8(stats_->id()); |
| } |
| |
| String RTCStats::GetType() const { |
| return String::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<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() {} |
| |
| 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(sequence.size()); |
| for (size_t i = 0; i < sequence.size(); ++i) |
| wtf_sequence[i] = String::FromUTF8(sequence[i]); |
| return wtf_sequence; |
| } |
| |
| 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_)); |
| } |
| |
| void AllowStatsForTesting(const char* type) { |
| GetStatsAllowlist()->AllowStatsForTesting(type); |
| } |
| |
| } // namespace blink |