Skip to content

Commit

Permalink
Support for map of string keys to uint64_t / double values in RTCStats
Browse files Browse the repository at this point in the history
Bug: webrtc:10685
Change-Id: I047d784bd20c3fca8b96391653f90fd8803140d8
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/219141
Reviewed-by: Henrik Boström <[email protected]>
Reviewed-by: Kári Helgason <[email protected]>
Reviewed-by: Xavier Lepaul‎ <[email protected]>
Commit-Queue: Henrik Boström <[email protected]>
Cr-Commit-Position: refs/heads/master@{#34121}
  • Loading branch information
bc-lee authored and WebRTC LUCI CQ committed May 25, 2021
1 parent cbeff55 commit 0a52ede
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 7 deletions.
17 changes: 17 additions & 0 deletions api/stats/rtc_stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <stddef.h>
#include <stdint.h>

#include <map>
#include <memory>
#include <string>
#include <utility>
Expand Down Expand Up @@ -237,6 +238,9 @@ class RTCStatsMemberInterface {
kSequenceUint64, // std::vector<uint64_t>
kSequenceDouble, // std::vector<double>
kSequenceString, // std::vector<std::string>

kMapStringUint64, // std::map<std::string, uint64_t>
kMapStringDouble, // std::map<std::string, double>
};

virtual ~RTCStatsMemberInterface() {}
Expand Down Expand Up @@ -363,6 +367,13 @@ class RTCStatsMember : public RTCStatsMemberInterface {
T value_;
};

namespace rtc_stats_internal {

typedef std::map<std::string, uint64_t> MapStringUint64;
typedef std::map<std::string, double> MapStringDouble;

} // namespace rtc_stats_internal

#define WEBRTC_DECLARE_RTCSTATSMEMBER(T) \
template <> \
RTC_EXPORT RTCStatsMemberInterface::Type RTCStatsMember<T>::StaticType(); \
Expand Down Expand Up @@ -391,6 +402,8 @@ WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<int64_t>);
WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<uint64_t>);
WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<double>);
WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<std::string>);
WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringUint64);
WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringDouble);

// Using inheritance just so that it's obvious from the member's declaration
// whether it's standardized or not.
Expand Down Expand Up @@ -455,6 +468,10 @@ extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT)
RTCNonStandardStatsMember<std::vector<double>>;
extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT)
RTCNonStandardStatsMember<std::vector<std::string>>;
extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT)
RTCNonStandardStatsMember<std::map<std::string, uint64_t>>;
extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT)
RTCNonStandardStatsMember<std::map<std::string, double>>;

} // namespace webrtc

Expand Down
1 change: 1 addition & 0 deletions sdk/android/api/org/webrtc/RTCStats.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public String getId() {
* - Double
* - String
* - The array form of any of the above (e.g., Integer[])
* - Map of String keys to BigInteger / Double values
*/
public Map<String, Object> getMembers() {
return members;
Expand Down
17 changes: 17 additions & 0 deletions sdk/android/src/jni/pc/rtc_stats_collector_callback_wrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,23 @@ ScopedJavaLocalRef<jobject> MemberToJava(
case RTCStatsMemberInterface::kSequenceString:
return NativeToJavaStringArray(
env, *member.cast_to<RTCStatsMember<std::vector<std::string>>>());

case RTCStatsMemberInterface::kMapStringUint64:
return NativeToJavaMap(
env,
*member.cast_to<RTCStatsMember<std::map<std::string, uint64_t>>>(),
[](JNIEnv* env, const auto& entry) {
return std::make_pair(NativeToJavaString(env, entry.first),
NativeToJavaBigInteger(env, entry.second));
});

case RTCStatsMemberInterface::kMapStringDouble:
return NativeToJavaMap(
env, *member.cast_to<RTCStatsMember<std::map<std::string, double>>>(),
[](JNIEnv* env, const auto& entry) {
return std::make_pair(NativeToJavaString(env, entry.first),
NativeToJavaDouble(env, entry.second));
});
}
RTC_NOTREACHED();
return nullptr;
Expand Down
4 changes: 2 additions & 2 deletions sdk/objc/api/peerconnection/RTCStatisticsReport.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ RTC_OBJC_EXPORT
@property(nonatomic, readonly) NSString *type;

/** The keys and values of the subreport, e.g. "totalFramesDuration = 5.551".
The values are either NSNumbers or NSStrings, or NSArrays encapsulating NSNumbers
or NSStrings. */
The values are either NSNumbers or NSStrings or NSArrays encapsulating NSNumbers
or NSStrings, or NSDictionary of NSString keys to NSNumber values. */
@property(nonatomic, readonly) NSDictionary<NSString *, NSObject *> *values;

- (instancetype)init NS_UNAVAILABLE;
Expand Down
22 changes: 21 additions & 1 deletion sdk/objc/api/peerconnection/RTCStatisticsReport.mm
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
namespace webrtc {

/** Converts a single value to a suitable NSNumber, NSString or NSArray containing NSNumbers
or NSStrings.*/
or NSStrings, or NSDictionary of NSString keys to NSNumber values.*/
NSObject *ValueFromStatsMember(const RTCStatsMemberInterface *member) {
if (member->is_defined()) {
switch (member->type()) {
Expand Down Expand Up @@ -91,6 +91,26 @@
}
return [array copy];
}
case RTCStatsMemberInterface::kMapStringUint64: {
std::map<std::string, uint64_t> map =
*member->cast_to<RTCStatsMember<std::map<std::string, uint64_t>>>();
NSMutableDictionary<NSString *, NSNumber *> *dictionary =
[NSMutableDictionary dictionaryWithCapacity:map.size()];
for (const auto &item : map) {
dictionary[[NSString stringForStdString:item.first]] = @(item.second);
}
return [dictionary copy];
}
case RTCStatsMemberInterface::kMapStringDouble: {
std::map<std::string, double> map =
*member->cast_to<RTCStatsMember<std::map<std::string, double>>>();
NSMutableDictionary<NSString *, NSNumber *> *dictionary =
[NSMutableDictionary dictionaryWithCapacity:map.size()];
for (const auto &item : map) {
dictionary[[NSString stringForStdString:item.first]] = @(item.second);
}
return [dictionary copy];
}
default:
RTC_NOTREACHED();
}
Expand Down
40 changes: 40 additions & 0 deletions stats/rtc_stats.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ std::string VectorOfStringsToString(const std::vector<T>& strings) {
return sb.Release();
}

template <typename T>
std::string MapToString(const std::map<std::string, T>& map) {
rtc::StringBuilder sb;
sb << "{";
const char* separator = "";
for (const auto& element : map) {
sb << separator << rtc::ToString(element.first) << ":"
<< rtc::ToString(element.second);
separator = ",";
}
sb << "}";
return sb.Release();
}

template <typename T>
std::string ToStringAsDouble(const T value) {
// JSON represents numbers as floating point numbers with about 15 decimal
Expand All @@ -88,6 +102,20 @@ std::string VectorToStringAsDouble(const std::vector<T>& vector) {
return sb.Release();
}

template <typename T>
std::string MapToStringAsDouble(const std::map<std::string, T>& map) {
rtc::StringBuilder sb;
sb << "{";
const char* separator = "";
for (const auto& element : map) {
sb << separator << "\"" << rtc::ToString(element.first)
<< "\":" << ToStringAsDouble(element.second);
separator = ",";
}
sb << "}";
return sb.Release();
}

} // namespace

bool RTCStats::operator==(const RTCStats& other) const {
Expand Down Expand Up @@ -248,6 +276,18 @@ WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<std::string>,
false,
VectorOfStringsToString(value_),
VectorOfStringsToString(value_));
WEBRTC_DEFINE_RTCSTATSMEMBER(rtc_stats_internal::MapStringUint64,
kMapStringUint64,
false,
false,
MapToString(value_),
MapToStringAsDouble(value_));
WEBRTC_DEFINE_RTCSTATSMEMBER(rtc_stats_internal::MapStringDouble,
kMapStringDouble,
false,
false,
MapToString(value_),
MapToStringAsDouble(value_));

template class RTC_EXPORT_TEMPLATE_DEFINE(RTC_EXPORT)
RTCNonStandardStatsMember<bool>;
Expand Down
51 changes: 50 additions & 1 deletion stats/rtc_stats_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ TEST(RTCStatsTest, RTCStatsAndMembers) {
EXPECT_EQ(stats.id(), "testId");
EXPECT_EQ(stats.timestamp_us(), static_cast<int64_t>(42));
std::vector<const RTCStatsMemberInterface*> members = stats.Members();
EXPECT_EQ(members.size(), static_cast<size_t>(14));
EXPECT_EQ(members.size(), static_cast<size_t>(16));
for (const RTCStatsMemberInterface* member : members) {
EXPECT_FALSE(member->is_defined());
}
Expand All @@ -98,6 +98,9 @@ TEST(RTCStatsTest, RTCStatsAndMembers) {
std::vector<std::string> sequence_string;
sequence_string.push_back(std::string("six"));

std::map<std::string, uint64_t> map_string_uint64{{"seven", 8}};
std::map<std::string, double> map_string_double{{"nine", 10.0}};

stats.m_sequence_bool = sequence_bool;
stats.m_sequence_int32 = sequence_int32;
stats.m_sequence_uint32 = sequence_uint32;
Expand All @@ -106,6 +109,8 @@ TEST(RTCStatsTest, RTCStatsAndMembers) {
stats.m_sequence_uint64 = sequence_uint64;
stats.m_sequence_double = sequence_double;
stats.m_sequence_string = sequence_string;
stats.m_map_string_uint64 = map_string_uint64;
stats.m_map_string_double = map_string_double;
for (const RTCStatsMemberInterface* member : members) {
EXPECT_TRUE(member->is_defined());
}
Expand All @@ -123,6 +128,8 @@ TEST(RTCStatsTest, RTCStatsAndMembers) {
EXPECT_EQ(*stats.m_sequence_uint64, sequence_uint64);
EXPECT_EQ(*stats.m_sequence_double, sequence_double);
EXPECT_EQ(*stats.m_sequence_string, sequence_string);
EXPECT_EQ(*stats.m_map_string_uint64, map_string_uint64);
EXPECT_EQ(*stats.m_map_string_double, map_string_double);

int32_t numbers[] = {4, 8, 15, 16, 23, 42};
std::vector<int32_t> numbers_sequence(&numbers[0], &numbers[6]);
Expand Down Expand Up @@ -152,6 +159,8 @@ TEST(RTCStatsTest, EqualityOperator) {
stats_with_all_values.m_sequence_uint64 = std::vector<uint64_t>();
stats_with_all_values.m_sequence_double = std::vector<double>();
stats_with_all_values.m_sequence_string = std::vector<std::string>();
stats_with_all_values.m_map_string_uint64 = std::map<std::string, uint64_t>();
stats_with_all_values.m_map_string_double = std::map<std::string, double>();
EXPECT_NE(stats_with_all_values, empty_stats);
EXPECT_EQ(stats_with_all_values, stats_with_all_values);
EXPECT_NE(stats_with_all_values.m_int32, stats_with_all_values.m_uint32);
Expand Down Expand Up @@ -180,6 +189,8 @@ TEST(RTCStatsTest, EqualityOperator) {
one_member_different[11].m_sequence_uint64->push_back(321);
one_member_different[12].m_sequence_double->push_back(321.0);
one_member_different[13].m_sequence_string->push_back("321");
(*one_member_different[13].m_map_string_uint64)["321"] = 321;
(*one_member_different[13].m_map_string_double)["321"] = 321.0;
for (size_t i = 0; i < 14; ++i) {
EXPECT_NE(stats_with_all_values, one_member_different[i]);
}
Expand Down Expand Up @@ -238,6 +249,11 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
std::vector<std::string> sequence_string;
sequence_string.push_back(std::string("four"));

std::map<std::string, uint64_t> map_string_uint64{
{"long", static_cast<uint64_t>(1234567890123456499L)}};
std::map<std::string, double> map_string_double{
{"three", 123.4567890123456499}, {"thirteen", 123.4567890123456499}};

RTCTestStats stats(id, timestamp);
stats.m_bool = m_bool;
stats.m_int32 = m_int32;
Expand All @@ -249,6 +265,8 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
stats.m_sequence_int64 = sequence_int64;
stats.m_sequence_double = sequence_double;
stats.m_sequence_string = sequence_string;
stats.m_map_string_uint64 = map_string_uint64;
stats.m_map_string_double = map_string_double;

Json::Value json_output;
EXPECT_TRUE(Json::Reader().parse(stats.ToJson(), json_output));
Expand Down Expand Up @@ -278,6 +296,16 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
rtc::GetValueFromJsonObject(json_output, "mSequenceString", &json_array));
EXPECT_TRUE(rtc::JsonArrayToStringVector(json_array, &sequence_string));

Json::Value json_map;
EXPECT_TRUE(
rtc::GetValueFromJsonObject(json_output, "mMapStringDouble", &json_map));
for (const auto& entry : map_string_double) {
double double_output = 0.0;
EXPECT_TRUE(
rtc::GetDoubleFromJsonObject(json_map, entry.first, &double_output));
EXPECT_NEAR(double_output, entry.second, GetExpectedError(entry.second));
}

EXPECT_EQ(id, stats.id());
EXPECT_EQ(timestamp, stats.timestamp_us());
EXPECT_EQ(m_bool, *stats.m_bool);
Expand All @@ -286,6 +314,7 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
EXPECT_EQ(sequence_bool, *stats.m_sequence_bool);
EXPECT_EQ(sequence_int32, *stats.m_sequence_int32);
EXPECT_EQ(sequence_string, *stats.m_sequence_string);
EXPECT_EQ(map_string_double, *stats.m_map_string_double);

EXPECT_NEAR(m_double, *stats.m_double, GetExpectedError(*stats.m_double));

Expand All @@ -295,6 +324,13 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
GetExpectedError(stats.m_sequence_double->at(i)));
}

EXPECT_EQ(map_string_double.size(), stats.m_map_string_double->size());
for (const auto& entry : map_string_double) {
auto it = stats.m_map_string_double->find(entry.first);
EXPECT_NE(it, stats.m_map_string_double->end());
EXPECT_NEAR(entry.second, it->second, GetExpectedError(it->second));
}

// We read mInt64 as double since JSON stores all numbers as doubles, so there
// is not enough precision to represent large numbers.
double m_int64_as_double;
Expand All @@ -320,6 +356,19 @@ TEST(RTCStatsTest, RTCStatsPrintsValidJson) {
GetExpectedError(stats_value_as_double));
}

// Similarly, read Uint64 as double
EXPECT_TRUE(
rtc::GetValueFromJsonObject(json_output, "mMapStringUint64", &json_map));
for (const auto& entry : map_string_uint64) {
const double stats_value_as_double =
static_cast<double>((*stats.m_map_string_uint64)[entry.first]);
double double_output = 0.0;
EXPECT_TRUE(
rtc::GetDoubleFromJsonObject(json_map, entry.first, &double_output));
EXPECT_NEAR(double_output, stats_value_as_double,
GetExpectedError(stats_value_as_double));
}

// Neither stats.m_uint32 nor stats.m_uint64 are defined, so "mUint64" and
// "mUint32" should not be part of the generated JSON object.
int m_uint32;
Expand Down
12 changes: 9 additions & 3 deletions stats/test/rtc_test_stats.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ WEBRTC_RTCSTATS_IMPL(RTCTestStats,
&m_sequence_int64,
&m_sequence_uint64,
&m_sequence_double,
&m_sequence_string)
&m_sequence_string,
&m_map_string_uint64,
&m_map_string_double)

RTCTestStats::RTCTestStats(const std::string& id, int64_t timestamp_us)
: RTCStats(id, timestamp_us),
Expand All @@ -47,7 +49,9 @@ RTCTestStats::RTCTestStats(const std::string& id, int64_t timestamp_us)
m_sequence_int64("mSequenceInt64"),
m_sequence_uint64("mSequenceUint64"),
m_sequence_double("mSequenceDouble"),
m_sequence_string("mSequenceString") {}
m_sequence_string("mSequenceString"),
m_map_string_uint64("mMapStringUint64"),
m_map_string_double("mMapStringDouble") {}

RTCTestStats::RTCTestStats(const RTCTestStats& other)
: RTCStats(other.id(), other.timestamp_us()),
Expand All @@ -64,7 +68,9 @@ RTCTestStats::RTCTestStats(const RTCTestStats& other)
m_sequence_int64(other.m_sequence_int64),
m_sequence_uint64(other.m_sequence_uint64),
m_sequence_double(other.m_sequence_double),
m_sequence_string(other.m_sequence_string) {}
m_sequence_string(other.m_sequence_string),
m_map_string_uint64(other.m_map_string_uint64),
m_map_string_double(other.m_map_string_double) {}

RTCTestStats::~RTCTestStats() {}

Expand Down
3 changes: 3 additions & 0 deletions stats/test/rtc_test_stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define STATS_TEST_RTC_TEST_STATS_H_

#include <cstdint>
#include <map>
#include <string>
#include <vector>

Expand Down Expand Up @@ -42,6 +43,8 @@ class RTC_EXPORT RTCTestStats : public RTCStats {
RTCStatsMember<std::vector<uint64_t>> m_sequence_uint64;
RTCStatsMember<std::vector<double>> m_sequence_double;
RTCStatsMember<std::vector<std::string>> m_sequence_string;
RTCStatsMember<std::map<std::string, uint64_t>> m_map_string_uint64;
RTCStatsMember<std::map<std::string, double>> m_map_string_double;
};

} // namespace webrtc
Expand Down

0 comments on commit 0a52ede

Please sign in to comment.