diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index 7963405e74..5ffd96477f 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -887,6 +887,9 @@ if (is_ios || is_mac) { "objc/api/peerconnection/RTCSessionDescription+Private.h", "objc/api/peerconnection/RTCSessionDescription.h", "objc/api/peerconnection/RTCSessionDescription.mm", + "objc/api/peerconnection/RTCStatisticsReport+Private.h", + "objc/api/peerconnection/RTCStatisticsReport.h", + "objc/api/peerconnection/RTCStatisticsReport.mm", "objc/api/peerconnection/RTCTracing.h", "objc/api/peerconnection/RTCTracing.mm", "objc/api/peerconnection/RTCVideoTrack+Private.h", @@ -917,6 +920,7 @@ if (is_ios || is_mac) { ":videotoolbox_objc", "../api:create_peerconnection_factory", "../api:libjingle_peerconnection_api", + "../api:rtc_stats_api", "../api:scoped_refptr", "../api/audio_codecs:audio_codecs_api", "../api/audio_codecs:builtin_audio_decoder_factory", diff --git a/sdk/objc/api/peerconnection/RTCPeerConnection+Stats.mm b/sdk/objc/api/peerconnection/RTCPeerConnection+Stats.mm index 72e8e5a522..e035caa2d5 100644 --- a/sdk/objc/api/peerconnection/RTCPeerConnection+Stats.mm +++ b/sdk/objc/api/peerconnection/RTCPeerConnection+Stats.mm @@ -12,12 +12,29 @@ #import "RTCLegacyStatsReport+Private.h" #import "RTCMediaStreamTrack+Private.h" +#import "RTCStatisticsReport+Private.h" #import "helpers/NSString+StdString.h" #include "rtc_base/checks.h" namespace webrtc { +class StatsCollectorCallbackAdapter : public RTCStatsCollectorCallback { + public: + StatsCollectorCallbackAdapter(RTCStatisticsCompletionHandler completion_handler) + : completion_handler_(completion_handler) {} + + void OnStatsDelivered(const rtc::scoped_refptr &report) override { + RTC_DCHECK(completion_handler_); + RTCStatisticsReport *statisticsReport = [[RTCStatisticsReport alloc] initWithReport:*report]; + completion_handler_(statisticsReport); + completion_handler_ = nil; + } + + private: + RTCStatisticsCompletionHandler completion_handler_; +}; + class StatsObserverAdapter : public StatsObserver { public: StatsObserverAdapter(void (^completionHandler) @@ -46,6 +63,12 @@ class StatsObserverAdapter : public StatsObserver { @implementation RTCPeerConnection (Stats) +- (void)statisticsWithCompletionHandler:(RTCStatisticsCompletionHandler)completionHandler { + rtc::scoped_refptr collector( + new rtc::RefCountedObject(completionHandler)); + self.nativePeerConnection->GetStats(collector); +} + - (void)statsForTrack:(RTCMediaStreamTrack *)mediaStreamTrack statsOutputLevel:(RTCStatsOutputLevel)statsOutputLevel completionHandler: diff --git a/sdk/objc/api/peerconnection/RTCPeerConnection.h b/sdk/objc/api/peerconnection/RTCPeerConnection.h index 393a50bc89..500a7ceaf5 100644 --- a/sdk/objc/api/peerconnection/RTCPeerConnection.h +++ b/sdk/objc/api/peerconnection/RTCPeerConnection.h @@ -25,6 +25,7 @@ @class RTCRtpTransceiver; @class RTCRtpTransceiverInit; @class RTCSessionDescription; +@class RTCStatisticsReport; @class RTCLegacyStatsReport; typedef NS_ENUM(NSInteger, RTCRtpMediaType); @@ -316,6 +317,8 @@ RTC_OBJC_EXPORT @end +typedef void (^RTCStatisticsCompletionHandler)(RTCStatisticsReport *); + @interface RTCPeerConnection (Stats) /** Gather stats for the given RTCMediaStreamTrack. If |mediaStreamTrack| is nil @@ -325,6 +328,9 @@ RTC_OBJC_EXPORT statsOutputLevel:(RTCStatsOutputLevel)statsOutputLevel completionHandler:(nullable void (^)(NSArray *stats))completionHandler; +/** Gather statistic through the v2 statistics API. */ +- (void)statisticsWithCompletionHandler:(RTCStatisticsCompletionHandler)completionHandler; + @end NS_ASSUME_NONNULL_END diff --git a/sdk/objc/api/peerconnection/RTCStatisticsReport+Private.h b/sdk/objc/api/peerconnection/RTCStatisticsReport+Private.h new file mode 100644 index 0000000000..0220d186b7 --- /dev/null +++ b/sdk/objc/api/peerconnection/RTCStatisticsReport+Private.h @@ -0,0 +1,19 @@ +/* + * Copyright 2019 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. + */ + +#import "RTCStatisticsReport.h" + +#include "api/stats/rtc_stats_report.h" + +@interface RTCStatisticsReport (Private) + +- (instancetype)initWithReport:(const webrtc::RTCStatsReport &)report; + +@end diff --git a/sdk/objc/api/peerconnection/RTCStatisticsReport.h b/sdk/objc/api/peerconnection/RTCStatisticsReport.h new file mode 100644 index 0000000000..6fbd59b112 --- /dev/null +++ b/sdk/objc/api/peerconnection/RTCStatisticsReport.h @@ -0,0 +1,51 @@ +/* + * Copyright 2019 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. + */ + +#import + +@class RTCStatistics; + +NS_ASSUME_NONNULL_BEGIN + +/** A statistics report. Encapsulates a number of RTCStatistics objects. */ +@interface RTCStatisticsReport : NSObject + +/** The timestamp of the report in microseconds since 1970-01-01T00:00:00Z. */ +@property(nonatomic, readonly) CFTimeInterval timestamp_us; + +/** RTCStatistics objects by id. */ +@property(nonatomic, readonly) NSDictionary *statistics; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +/** A part of a report (a subreport) covering a certain area. */ +@interface RTCStatistics : NSObject + +/** The id of this subreport, e.g. "RTCMediaStreamTrack_receiver_2". */ +@property(nonatomic, readonly) NSString *id; + +/** The timestamp of the subreport in microseconds since 1970-01-01T00:00:00Z. */ +@property(nonatomic, readonly) CFTimeInterval timestamp_us; + +/** The type of the subreport, e.g. "track", "codec". */ +@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. */ +@property(nonatomic, readonly) NSDictionary *values; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk/objc/api/peerconnection/RTCStatisticsReport.mm b/sdk/objc/api/peerconnection/RTCStatisticsReport.mm new file mode 100644 index 0000000000..526976707d --- /dev/null +++ b/sdk/objc/api/peerconnection/RTCStatisticsReport.mm @@ -0,0 +1,172 @@ +/* + * Copyright 2019 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. + */ + +#import "RTCStatisticsReport+Private.h" + +#include "helpers/NSString+StdString.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +/** Converts a single value to a suitable NSNumber, NSString or NSArray containing NSNumbers + or NSStrings.*/ +NSObject *ValueFromStatsMember(const RTCStatsMemberInterface *member) { + if (member->is_defined()) { + switch (member->type()) { + case RTCStatsMemberInterface::kBool: + return [NSNumber numberWithBool:*member->cast_to>()]; + case RTCStatsMemberInterface::kInt32: + return [NSNumber numberWithInt:*member->cast_to>()]; + case RTCStatsMemberInterface::kUint32: + return [NSNumber numberWithUnsignedInt:*member->cast_to>()]; + case RTCStatsMemberInterface::kInt64: + return [NSNumber numberWithLong:*member->cast_to>()]; + case RTCStatsMemberInterface::kUint64: + return [NSNumber numberWithUnsignedLong:*member->cast_to>()]; + case RTCStatsMemberInterface::kDouble: + return [NSNumber numberWithDouble:*member->cast_to>()]; + case RTCStatsMemberInterface::kString: + return [NSString stringForStdString:*member->cast_to>()]; + case RTCStatsMemberInterface::kSequenceBool: { + std::vector sequence = *member->cast_to>>(); + NSMutableArray *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSNumber numberWithBool:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceInt32: { + std::vector sequence = *member->cast_to>>(); + NSMutableArray *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSNumber numberWithInt:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceUint32: { + std::vector sequence = *member->cast_to>>(); + NSMutableArray *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSNumber numberWithUnsignedInt:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceInt64: { + std::vector sequence = *member->cast_to>>(); + NSMutableArray *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSNumber numberWithLong:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceUint64: { + std::vector sequence = *member->cast_to>>(); + NSMutableArray *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSNumber numberWithUnsignedLong:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceDouble: { + std::vector sequence = *member->cast_to>>(); + NSMutableArray *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSNumber numberWithDouble:item]]; + } + return [array copy]; + } + case RTCStatsMemberInterface::kSequenceString: { + std::vector sequence = + *member->cast_to>>(); + NSMutableArray *array = [NSMutableArray arrayWithCapacity:sequence.size()]; + for (const auto &item : sequence) { + [array addObject:[NSString stringForStdString:item]]; + } + return [array copy]; + } + default: + RTC_NOTREACHED(); + } + } + + return nil; +} +} // namespace webrtc + +@implementation RTCStatistics + +@synthesize id = _id; +@synthesize timestamp_us = _timestamp_us; +@synthesize type = _type; +@synthesize values = _values; + +- (instancetype)initWithStatistics:(const webrtc::RTCStats &)statistics { + if (self = [super init]) { + _id = [NSString stringForStdString:statistics.id()]; + _timestamp_us = statistics.timestamp_us(); + _type = [NSString stringWithCString:statistics.type() encoding:NSUTF8StringEncoding]; + + NSMutableDictionary *values = [NSMutableDictionary dictionary]; + for (const webrtc::RTCStatsMemberInterface *member : statistics.Members()) { + NSObject *value = ValueFromStatsMember(member); + if (value) { + NSString *name = [NSString stringWithCString:member->name() encoding:NSUTF8StringEncoding]; + RTC_DCHECK(name.length > 0); + RTC_DCHECK(!values[name]); + values[name] = value; + } + } + _values = [values copy]; + } + + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"id = %@, type = %@, timestamp = %.0f, values = %@", + self.id, + self.type, + self.timestamp_us, + self.values]; +} + +@end + +@implementation RTCStatisticsReport + +@synthesize timestamp_us = _timestamp_us; +@synthesize statistics = _statistics; + +- (NSString *)description { + return [NSString + stringWithFormat:@"timestamp = %.0f, statistics = %@", self.timestamp_us, self.statistics]; +} + +@end + +@implementation RTCStatisticsReport (Private) + +- (instancetype)initWithReport:(const webrtc::RTCStatsReport &)report { + if (self = [super init]) { + _timestamp_us = report.timestamp_us(); + + NSMutableDictionary *statisticsById = + [NSMutableDictionary dictionaryWithCapacity:report.size()]; + for (const auto &stat : report) { + RTCStatistics *statistics = [[RTCStatistics alloc] initWithStatistics:stat]; + statisticsById[statistics.id] = statistics; + } + _statistics = [statisticsById copy]; + } + + return self; +} + +@end