diff --git a/api/BUILD.gn b/api/BUILD.gn index f09b353eef..6af4fa5517 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn @@ -771,6 +771,7 @@ rtc_source_set("rtc_stats_api") { visibility = [ "*" ] cflags = [] sources = [ + "stats/attribute.h", "stats/rtc_stats.h", "stats/rtc_stats_collector_callback.h", "stats/rtc_stats_member.h", @@ -789,7 +790,10 @@ rtc_source_set("rtc_stats_api") { "units:timestamp", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/types:optional", + "//third_party/abseil-cpp/absl/types:variant", + ] } rtc_library("audio_options_api") { diff --git a/api/stats/attribute.h b/api/stats/attribute.h new file mode 100644 index 0000000000..3f6d058b0e --- /dev/null +++ b/api/stats/attribute.h @@ -0,0 +1,79 @@ +/* + * Copyright 2024 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_STATS_ATTRIBUTE_H_ +#define API_STATS_ATTRIBUTE_H_ + +#include +#include +#include +#include + +#include "absl/types/variant.h" +#include "api/stats/rtc_stats_member.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// A light-weight wrapper of an RTCStats attribute (an individual metric). +class RTC_EXPORT Attribute : public RTCStatsMemberInterface { + public: + // TODO(https://crbug.com/webrtc/15164): Replace uses of RTCStatsMember + // with absl::optional and update these pointer types. + typedef absl::variant*, + const RTCStatsMember*, + const RTCStatsMember*, + const RTCStatsMember*, + const RTCStatsMember*, + const RTCStatsMember*, + const RTCStatsMember*, + const RTCStatsMember>*, + const RTCStatsMember>*, + const RTCStatsMember>*, + const RTCStatsMember>*, + const RTCStatsMember>*, + const RTCStatsMember>*, + const RTCStatsMember>*, + const RTCStatsMember>*, + const RTCStatsMember>*> + StatVariant; + + template + explicit Attribute(const RTCStatsMember* attribute) + : RTCStatsMemberInterface(attribute->name()), attribute_(attribute) {} + ~Attribute() override; + + const char* name() const; + const StatVariant& as_variant() const; + + static Attribute FromMemberInterface(const RTCStatsMemberInterface* member); + // RTCStatsMemberInterface implementation. + // TODO(https://crbug.com/webrtc/15164): Delete RTCStatsMemberInterface in + // favor of absl::optional. + RTCStatsMemberInterface::Type type() const override; + bool is_sequence() const override; + bool is_string() const override; + bool is_defined() const override; + std::string ValueToString() const override; + std::string ValueToJson() const override; + + protected: + const RTCStatsMemberInterface* member_ptr() const override; + bool IsEqual(const RTCStatsMemberInterface& other) const override; + + private: + StatVariant attribute_; +}; + +Attribute MemberToAttribute(const RTCStatsMemberInterface* member); + +} // namespace webrtc + +#endif // API_STATS_ATTRIBUTE_H_ diff --git a/api/stats/rtc_stats.h b/api/stats/rtc_stats.h index d71b10d8e7..f49c1ea05d 100644 --- a/api/stats/rtc_stats.h +++ b/api/stats/rtc_stats.h @@ -20,6 +20,7 @@ #include #include +#include "api/stats/attribute.h" #include "api/stats/rtc_stats_member.h" #include "api/units/timestamp.h" #include "rtc_base/checks.h" @@ -56,8 +57,8 @@ class RTC_EXPORT RTCStats { public: RTCStats(const std::string& id, Timestamp timestamp) : id_(id), timestamp_(timestamp) {} - - virtual ~RTCStats() {} + RTCStats(const RTCStats& other); + virtual ~RTCStats(); virtual std::unique_ptr copy() const = 0; @@ -67,9 +68,12 @@ class RTC_EXPORT RTCStats { // Returns the static member variable `kType` of the implementing class. virtual const char* type() const = 0; - // Returns a vector of pointers to all the `RTCStatsMemberInterface` members - // of this class. This allows for iteration of members. For a given class, - // `Members` always returns the same members in the same order. + // Returns all attributes of this stats object, i.e. a list of its individual + // metrics as viewed via the Attribute wrapper. + std::vector Attributes() const; + // Returns Attributes() as `RTCStatsMemberInterface` pointers. + // TODO(https://crbug.com/webrtc/15164): Update callers to use Attributes() + // instead and delete this method as well as the RTCStatsMemberInterface. std::vector Members() const; // Checks if the two stats objects are of the same type and have the same // member values. Timestamps are not compared. These operators are exposed for @@ -90,15 +94,18 @@ class RTC_EXPORT RTCStats { } protected: - // Gets a vector of all members of this `RTCStats` object, including members - // derived from parent classes. `additional_capacity` is how many more members - // shall be reserved in the vector (so that subclasses can allocate a vector - // with room for both parent and child members without it having to resize). - virtual std::vector - MembersOfThisObjectAndAncestors(size_t additional_capacity) const; + virtual std::vector AttributesImpl( + size_t additional_capacity) const; std::string const id_; Timestamp timestamp_; + + // Because Members() return a raw pointers we need to cache attributes to + // ensure the pointers are still valid after the method has returned. Mutable + // to allow lazy instantiation the first time the method is called. + // TODO(https://crbug.com/webrtc/15164): Migrate all uses of Members() to + // Attributes() and delete Members() and `cached_attributes_`. + mutable std::vector cached_attributes_; }; // All `RTCStats` classes should use these macros. @@ -142,16 +149,15 @@ class RTC_EXPORT RTCStats { // bar("bar") { // } // -#define WEBRTC_RTCSTATS_DECL() \ - protected: \ - std::vector \ - MembersOfThisObjectAndAncestors(size_t local_var_additional_capacity) \ - const override; \ - \ - public: \ - static const char kType[]; \ - \ - std::unique_ptr copy() const override; \ +#define WEBRTC_RTCSTATS_DECL() \ + protected: \ + std::vector AttributesImpl(size_t additional_capacity) \ + const override; \ + \ + public: \ + static const char kType[]; \ + \ + std::unique_ptr copy() const override; \ const char* type() const override #define WEBRTC_RTCSTATS_IMPL(this_class, parent_class, type_str, ...) \ @@ -165,23 +171,17 @@ class RTC_EXPORT RTCStats { return this_class::kType; \ } \ \ - std::vector \ - this_class::MembersOfThisObjectAndAncestors( \ - size_t local_var_additional_capacity) const { \ - const webrtc::RTCStatsMemberInterface* local_var_members[] = { \ - __VA_ARGS__}; \ - size_t local_var_members_count = \ - sizeof(local_var_members) / sizeof(local_var_members[0]); \ - std::vector \ - local_var_members_vec = parent_class::MembersOfThisObjectAndAncestors( \ - local_var_members_count + local_var_additional_capacity); \ - RTC_DCHECK_GE( \ - local_var_members_vec.capacity() - local_var_members_vec.size(), \ - local_var_members_count + local_var_additional_capacity); \ - local_var_members_vec.insert(local_var_members_vec.end(), \ - &local_var_members[0], \ - &local_var_members[local_var_members_count]); \ - return local_var_members_vec; \ + std::vector this_class::AttributesImpl( \ + size_t additional_capacity) const { \ + const webrtc::RTCStatsMemberInterface* this_members[] = {__VA_ARGS__}; \ + size_t this_members_size = sizeof(this_members) / sizeof(this_members[0]); \ + std::vector attributes = \ + parent_class::AttributesImpl(this_members_size + additional_capacity); \ + for (size_t i = 0; i < this_members_size; ++i) { \ + attributes.push_back( \ + webrtc::Attribute::FromMemberInterface(this_members[i])); \ + } \ + return attributes; \ } // A version of WEBRTC_RTCSTATS_IMPL() where "..." is omitted, used to avoid a @@ -198,10 +198,9 @@ class RTC_EXPORT RTCStats { return this_class::kType; \ } \ \ - std::vector \ - this_class::MembersOfThisObjectAndAncestors( \ - size_t local_var_additional_capacity) const { \ - return parent_class::MembersOfThisObjectAndAncestors(0); \ + std::vector this_class::AttributesImpl( \ + size_t additional_capacity) const { \ + return parent_class::AttributesImpl(0); \ } } // namespace webrtc diff --git a/api/stats/rtc_stats_member.h b/api/stats/rtc_stats_member.h index 09e3836359..b39afa2c08 100644 --- a/api/stats/rtc_stats_member.h +++ b/api/stats/rtc_stats_member.h @@ -74,10 +74,11 @@ class RTCStatsMemberInterface { // instead. virtual std::string ValueToJson() const = 0; + virtual const RTCStatsMemberInterface* member_ptr() const { return this; } template const T& cast_to() const { RTC_DCHECK_EQ(type(), T::StaticType()); - return static_cast(*this); + return static_cast(*member_ptr()); } protected: @@ -151,7 +152,6 @@ class RTCStatsMember : public RTCStatsMemberInterface { return &(*value_); } - protected: bool IsEqual(const RTCStatsMemberInterface& other) const override { if (type() != other.type()) return false; diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 00ac124901..4ee0527743 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -29,6 +29,7 @@ #include "api/media_stream_track.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" +#include "api/stats/attribute.h" #include "api/stats/rtc_stats.h" #include "api/stats/rtc_stats_report.h" #include "api/stats/rtcstats_objects.h" diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index 648efab69a..643c0a52c0 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -207,13 +207,13 @@ class RTCStatsVerifier { RTC_CHECK(report_); RTC_CHECK(stats_); for (const RTCStatsMemberInterface* member : stats_->Members()) { - untested_members_.insert(member); + untested_members_.insert(member->member_ptr()); } } void MarkMemberTested(const RTCStatsMemberInterface& member, bool test_successful) { - untested_members_.erase(&member); + untested_members_.erase(member.member_ptr()); all_tests_successful_ &= test_successful; } diff --git a/stats/BUILD.gn b/stats/BUILD.gn index 5890149477..8993272921 100644 --- a/stats/BUILD.gn +++ b/stats/BUILD.gn @@ -16,6 +16,7 @@ rtc_library("rtc_stats") { visibility = [ "*" ] cflags = [] sources = [ + "attribute.cc", "rtc_stats.cc", "rtc_stats_member.cc", "rtc_stats_report.cc", @@ -28,6 +29,7 @@ rtc_library("rtc_stats") { "../rtc_base:macromagic", "../rtc_base:stringutils", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:variant" ] } rtc_library("rtc_stats_test_utils") { diff --git a/stats/attribute.cc b/stats/attribute.cc new file mode 100644 index 0000000000..36cdc454f4 --- /dev/null +++ b/stats/attribute.cc @@ -0,0 +1,142 @@ +/* + * Copyright 2024 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "api/stats/attribute.h" + +#include "absl/types/variant.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +namespace { + +struct VisitIsSequence { + // Any type of vector is a sequence. + template + bool operator()(const RTCStatsMember>* attribute) { + return true; + } + // Any other type is not. + template + bool operator()(const RTCStatsMember* attribute) { + return false; + } +}; + +struct VisitIsEqual { + template + bool operator()(const RTCStatsMember* attribute) { + return attribute->IsEqual(other); + } + + const RTCStatsMemberInterface& other; +}; + +} // namespace + +Attribute::~Attribute() {} + +// static +Attribute Attribute::FromMemberInterface( + const RTCStatsMemberInterface* member) { + switch (member->type()) { + case RTCStatsMemberInterface::Type::kBool: + return Attribute(&member->cast_to>()); + case RTCStatsMemberInterface::Type::kInt32: + return Attribute(&member->cast_to>()); + case RTCStatsMemberInterface::Type::kUint32: + return Attribute(&member->cast_to>()); + case RTCStatsMemberInterface::Type::kInt64: + return Attribute(&member->cast_to>()); + case RTCStatsMemberInterface::Type::kUint64: + return Attribute(&member->cast_to>()); + case RTCStatsMemberInterface::Type::kDouble: + return Attribute(&member->cast_to>()); + case RTCStatsMemberInterface::Type::kString: + return Attribute(&member->cast_to>()); + case RTCStatsMemberInterface::Type::kSequenceBool: + return Attribute(&member->cast_to>>()); + case RTCStatsMemberInterface::Type::kSequenceInt32: + return Attribute( + &member->cast_to>>()); + case RTCStatsMemberInterface::Type::kSequenceUint32: + return Attribute( + &member->cast_to>>()); + case RTCStatsMemberInterface::Type::kSequenceInt64: + return Attribute( + &member->cast_to>>()); + case RTCStatsMemberInterface::Type::kSequenceUint64: + return Attribute( + &member->cast_to>>()); + case RTCStatsMemberInterface::Type::kSequenceDouble: + return Attribute(&member->cast_to>>()); + case RTCStatsMemberInterface::Type::kSequenceString: + return Attribute( + &member->cast_to>>()); + case RTCStatsMemberInterface::Type::kMapStringUint64: + return Attribute( + &member->cast_to>>()); + case RTCStatsMemberInterface::Type::kMapStringDouble: + return Attribute( + &member->cast_to>>()); + default: + RTC_CHECK_NOTREACHED(); + } +} + +const char* Attribute::name() const { + return absl::visit([](const auto* attr) { return attr->name(); }, attribute_); +} + +const Attribute::StatVariant& Attribute::as_variant() const { + return attribute_; +} + +RTCStatsMemberInterface::Type Attribute::type() const { + return absl::visit([](const auto* attr) { return attr->type(); }, attribute_); +} + +const RTCStatsMemberInterface* Attribute::member_ptr() const { + return absl::visit( + [](const auto* attr) { + return static_cast(attr); + }, + attribute_); +} + +bool Attribute::is_sequence() const { + return absl::visit(VisitIsSequence(), attribute_); +} + +bool Attribute::is_string() const { + return absl::holds_alternative*>( + attribute_); +} + +bool Attribute::is_defined() const { + return absl::visit([](const auto* attr) { return attr->is_defined(); }, + attribute_); +} + +std::string Attribute::ValueToString() const { + return absl::visit([](const auto* attr) { return attr->ValueToString(); }, + attribute_); +} + +std::string Attribute::ValueToJson() const { + return absl::visit([](const auto* attr) { return attr->ValueToJson(); }, + attribute_); +} + +bool Attribute::IsEqual(const RTCStatsMemberInterface& other) const { + return absl::visit(VisitIsEqual{.other = *other.member_ptr()}, attribute_); +} + +} // namespace webrtc diff --git a/stats/rtc_stats.cc b/stats/rtc_stats.cc index 21e5d05f43..398987fd7b 100644 --- a/stats/rtc_stats.cc +++ b/stats/rtc_stats.cc @@ -16,6 +16,11 @@ namespace webrtc { +RTCStats::RTCStats(const RTCStats& other) + : RTCStats(other.id_, other.timestamp_) {} + +RTCStats::~RTCStats() {} + bool RTCStats::operator==(const RTCStats& other) const { if (type() != other.type() || id() != other.id()) return false; @@ -60,14 +65,26 @@ std::string RTCStats::ToJson() const { } std::vector RTCStats::Members() const { - return MembersOfThisObjectAndAncestors(0); -} - -std::vector -RTCStats::MembersOfThisObjectAndAncestors(size_t additional_capacity) const { + if (cached_attributes_.empty()) { + cached_attributes_ = Attributes(); + } std::vector members; - members.reserve(additional_capacity); + members.reserve(cached_attributes_.size()); + for (const auto& attribute : cached_attributes_) { + members.push_back(&attribute); + } return members; } +std::vector RTCStats::Attributes() const { + return AttributesImpl(0); +} + +std::vector RTCStats::AttributesImpl( + size_t additional_capacity) const { + std::vector attributes; + attributes.reserve(additional_capacity); + return attributes; +} + } // namespace webrtc diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index 77feaf87ba..18f3e6f394 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc @@ -12,6 +12,7 @@ #include +#include "api/stats/attribute.h" #include "api/stats/rtc_stats.h" #include "rtc_base/checks.h"