From 19b1be159e13a5aa2ba03bc5eda7c67e50bcfb7d Mon Sep 17 00:00:00 2001 From: "tkchin@webrtc.org" Date: Tue, 22 Apr 2014 21:05:38 +0000 Subject: [PATCH] Provide GetStats method in RTCPeerConnection BUG=3144 R=fischman@webrtc.org Review URL: https://webrtc-codereview.appspot.com/12069006 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5960 4adac7df-926f-26a2-2b94-8c16560cd09d --- talk/app/webrtc/objc/RTCEnumConverter.h | 3 + talk/app/webrtc/objc/RTCEnumConverter.mm | 10 +++ talk/app/webrtc/objc/RTCPair.m | 4 ++ talk/app/webrtc/objc/RTCPeerConnection.mm | 39 +++++++++++ .../app/webrtc/objc/RTCStatsReport+Internal.h | 36 ++++++++++ talk/app/webrtc/objc/RTCStatsReport.mm | 69 +++++++++++++++++++ .../webrtc/objc/public/RTCPeerConnection.h | 9 ++- .../app/webrtc/objc/public/RTCStatsDelegate.h | 39 +++++++++++ talk/app/webrtc/objc/public/RTCStatsReport.h | 45 ++++++++++++ talk/app/webrtc/objc/public/RTCTypes.h | 6 ++ .../ios/AppRTCDemo/APPRTCAppDelegate.m | 35 +++++++++- talk/libjingle.gyp | 7 ++ talk/libjingle_tests.gyp | 3 + 13 files changed, 302 insertions(+), 3 deletions(-) create mode 100644 talk/app/webrtc/objc/RTCStatsReport+Internal.h create mode 100644 talk/app/webrtc/objc/RTCStatsReport.mm create mode 100644 talk/app/webrtc/objc/public/RTCStatsDelegate.h create mode 100644 talk/app/webrtc/objc/public/RTCStatsReport.h diff --git a/talk/app/webrtc/objc/RTCEnumConverter.h b/talk/app/webrtc/objc/RTCEnumConverter.h index 0e83719d5..d33709d30 100644 --- a/talk/app/webrtc/objc/RTCEnumConverter.h +++ b/talk/app/webrtc/objc/RTCEnumConverter.h @@ -42,6 +42,9 @@ + (RTCSignalingState)convertSignalingStateToObjC: (webrtc::PeerConnectionInterface::SignalingState)nativeState; ++ (webrtc::PeerConnectionInterface::StatsOutputLevel) + convertStatsOutputLevelToNative:(RTCStatsOutputLevel)statsOutputLevel; + + (RTCSourceState)convertSourceStateToObjC: (webrtc::MediaSourceInterface::SourceState)nativeState; diff --git a/talk/app/webrtc/objc/RTCEnumConverter.mm b/talk/app/webrtc/objc/RTCEnumConverter.mm index 7c81c8d85..9d019419c 100644 --- a/talk/app/webrtc/objc/RTCEnumConverter.mm +++ b/talk/app/webrtc/objc/RTCEnumConverter.mm @@ -81,6 +81,16 @@ } } ++ (webrtc::PeerConnectionInterface::StatsOutputLevel) + convertStatsOutputLevelToNative:(RTCStatsOutputLevel)statsOutputLevel { + switch (statsOutputLevel) { + case RTCStatsOutputLevelStandard: + return webrtc::PeerConnectionInterface::kStatsOutputLevelStandard; + case RTCStatsOutputLevelDebug: + return webrtc::PeerConnectionInterface::kStatsOutputLevelDebug; + } +} + + (RTCSourceState)convertSourceStateToObjC: (webrtc::MediaSourceInterface::SourceState)nativeState { switch (nativeState) { diff --git a/talk/app/webrtc/objc/RTCPair.m b/talk/app/webrtc/objc/RTCPair.m index 226484522..2b289f5cc 100644 --- a/talk/app/webrtc/objc/RTCPair.m +++ b/talk/app/webrtc/objc/RTCPair.m @@ -40,4 +40,8 @@ return self; } +- (NSString*)description { + return [NSString stringWithFormat:@"%@: %@", _key, _value]; +} + @end diff --git a/talk/app/webrtc/objc/RTCPeerConnection.mm b/talk/app/webrtc/objc/RTCPeerConnection.mm index 9188653e8..759c6be9e 100644 --- a/talk/app/webrtc/objc/RTCPeerConnection.mm +++ b/talk/app/webrtc/objc/RTCPeerConnection.mm @@ -36,9 +36,12 @@ #import "RTCICEServer+Internal.h" #import "RTCMediaConstraints+Internal.h" #import "RTCMediaStream+Internal.h" +#import "RTCMediaStreamTrack+Internal.h" #import "RTCSessionDescription+Internal.h" #import "RTCSessionDescriptionDelegate.h" #import "RTCSessionDescription.h" +#import "RTCStatsDelegate.h" +#import "RTCStatsReport+Internal.h" #include "talk/app/webrtc/jsep.h" @@ -108,6 +111,30 @@ class RTCSetSessionDescriptionObserver : public SetSessionDescriptionObserver { id _delegate; RTCPeerConnection* _peerConnection; }; + +class RTCStatsObserver : public StatsObserver { + public: + RTCStatsObserver(id delegate, + RTCPeerConnection* peerConnection) { + _delegate = delegate; + _peerConnection = peerConnection; + } + + virtual void OnComplete(const std::vector& reports) OVERRIDE { + NSMutableArray* stats = [NSMutableArray arrayWithCapacity:reports.size()]; + std::vector::const_iterator it = reports.begin(); + for (; it != reports.end(); ++it) { + RTCStatsReport* statsReport = + [[RTCStatsReport alloc] initWithStatsReport:*it]; + [stats addObject:statsReport]; + } + [_delegate peerConnection:_peerConnection didGetStats:stats]; + } + + private: + id _delegate; + RTCPeerConnection* _peerConnection; +}; } @implementation RTCPeerConnection { @@ -220,6 +247,18 @@ class RTCSetSessionDescriptionObserver : public SetSessionDescriptionObserver { self.peerConnection->Close(); } +- (BOOL)getStatsWithDelegate:(id)delegate + mediaStreamTrack:(RTCMediaStreamTrack*)mediaStreamTrack + statsOutputLevel:(RTCStatsOutputLevel)statsOutputLevel { + talk_base::scoped_refptr observer( + new talk_base::RefCountedObject(delegate, + self)); + webrtc::PeerConnectionInterface::StatsOutputLevel nativeOutputLevel = + [RTCEnumConverter convertStatsOutputLevelToNative:statsOutputLevel]; + return self.peerConnection->GetStats( + observer, mediaStreamTrack.mediaTrack, nativeOutputLevel); +} + @end @implementation RTCPeerConnection (Internal) diff --git a/talk/app/webrtc/objc/RTCStatsReport+Internal.h b/talk/app/webrtc/objc/RTCStatsReport+Internal.h new file mode 100644 index 000000000..b17b01a2f --- /dev/null +++ b/talk/app/webrtc/objc/RTCStatsReport+Internal.h @@ -0,0 +1,36 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "RTCStatsReport.h" + +#include "talk/app/webrtc/statstypes.h" + +@interface RTCStatsReport (Internal) + +- (instancetype)initWithStatsReport:(const webrtc::StatsReport&)statsReport; + +@end diff --git a/talk/app/webrtc/objc/RTCStatsReport.mm b/talk/app/webrtc/objc/RTCStatsReport.mm new file mode 100644 index 000000000..8da4b469a --- /dev/null +++ b/talk/app/webrtc/objc/RTCStatsReport.mm @@ -0,0 +1,69 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "RTCStatsReport+Internal.h" + +#import "RTCPair.h" + +@implementation RTCStatsReport + +- (NSString*)description { + NSString* format = @"id: %@, type: %@, timestamp: %f, values: %@"; + return [NSString stringWithFormat:format, + self.reportId, + self.type, + self.timestamp, + self.values]; +} + +@end + +@implementation RTCStatsReport (Internal) + +- (instancetype)initWithStatsReport:(const webrtc::StatsReport&)statsReport { + if (self = [super init]) { + _reportId = @(statsReport.id.c_str()); + _type = @(statsReport.type.c_str()); + _timestamp = statsReport.timestamp; + NSMutableArray* values = + [NSMutableArray arrayWithCapacity:statsReport.values.size()]; + webrtc::StatsReport::Values::const_iterator it = statsReport.values.begin(); + for (; it != statsReport.values.end(); ++it) { + RTCPair* pair = [[RTCPair alloc] initWithKey:@(it->name.c_str()) + value:@(it->value.c_str())]; + [values addObject:pair]; + } + _values = values; + } + return self; +} + +@end diff --git a/talk/app/webrtc/objc/public/RTCPeerConnection.h b/talk/app/webrtc/objc/public/RTCPeerConnection.h index 45e0a932c..b5b657c01 100644 --- a/talk/app/webrtc/objc/public/RTCPeerConnection.h +++ b/talk/app/webrtc/objc/public/RTCPeerConnection.h @@ -31,8 +31,10 @@ @class RTCICEServers; @class RTCMediaConstraints; @class RTCMediaStream; +@class RTCMediaStreamTrack; @class RTCSessionDescription; @protocol RTCSessionDescriptionDelegate; +@protocol RTCStatsDelegate; // RTCPeerConnection is an ObjectiveC friendly wrapper around a PeerConnection // object. See the documentation in talk/app/webrtc/peerconnectioninterface.h. @@ -99,7 +101,12 @@ // Terminates all media and closes the transport. - (void)close; -// TODO(hughv): Implement GetStats. +// Gets statistics for the media track. If |mediaStreamTrack| is nil statistics +// are gathered for all tracks. +// Statistics information will be reported via RTCStatsDelegate. +- (BOOL)getStatsWithDelegate:(id)delegate + mediaStreamTrack:(RTCMediaStreamTrack*)mediaStreamTrack + statsOutputLevel:(RTCStatsOutputLevel)statsOutputLevel; #ifndef DOXYGEN_SHOULD_SKIP_THIS // Disallow init and don't add to documentation diff --git a/talk/app/webrtc/objc/public/RTCStatsDelegate.h b/talk/app/webrtc/objc/public/RTCStatsDelegate.h new file mode 100644 index 000000000..3de3dfe96 --- /dev/null +++ b/talk/app/webrtc/objc/public/RTCStatsDelegate.h @@ -0,0 +1,39 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +@class RTCPeerConnection; + +// RTCSessionDescriptionDelegate is a protocol for receiving statistic +// reports from RTCPeerConnection. +@protocol RTCStatsDelegate + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didGetStats:(NSArray*)stats; // NSArray of RTCStatsReport*. + +@end diff --git a/talk/app/webrtc/objc/public/RTCStatsReport.h b/talk/app/webrtc/objc/public/RTCStatsReport.h new file mode 100644 index 000000000..784ce6741 --- /dev/null +++ b/talk/app/webrtc/objc/public/RTCStatsReport.h @@ -0,0 +1,45 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +// ObjectiveC friendly wrapper around a StatsReport object. +// See talk/app/webrtc/statsypes.h +@interface RTCStatsReport : NSObject + +@property(nonatomic, readonly) NSString* reportId; +@property(nonatomic, readonly) NSString* type; +@property(nonatomic, readonly) CFTimeInterval timestamp; +@property(nonatomic, readonly) NSArray* values; // NSArray of RTCPair*. + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// Disallow init and don't add to documentation +- (id)init __attribute__(( + unavailable("init is not a supported initializer for this class."))); +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +@end diff --git a/talk/app/webrtc/objc/public/RTCTypes.h b/talk/app/webrtc/objc/public/RTCTypes.h index 8ff8bf485..2a55200a6 100644 --- a/talk/app/webrtc/objc/public/RTCTypes.h +++ b/talk/app/webrtc/objc/public/RTCTypes.h @@ -55,6 +55,12 @@ typedef enum { RTCSignalingClosed, } RTCSignalingState; +// RTCStatsOutputLevel correspond to webrtc::StatsOutputLevel +typedef enum { + RTCStatsOutputLevelStandard, + RTCStatsOutputLevelDebug, +} RTCStatsOutputLevel; + // RTCSourceState corresponds to the states in webrtc::SourceState. typedef enum { RTCSourceStateInitializing, diff --git a/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m b/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m index e6add76de..2ed8ff23d 100644 --- a/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m +++ b/talk/examples/ios/AppRTCDemo/APPRTCAppDelegate.m @@ -39,6 +39,7 @@ #import "RTCPeerConnectionDelegate.h" #import "RTCPeerConnectionFactory.h" #import "RTCSessionDescription.h" +#import "RTCStatsDelegate.h" #import "RTCVideoRenderer.h" #import "RTCVideoCapturer.h" #import "RTCVideoTrack.h" @@ -63,6 +64,8 @@ return self; } +#pragma mark - RTCPeerConnectionDelegate + - (void)peerConnectionOnError:(RTCPeerConnection*)peerConnection { dispatch_async(dispatch_get_main_queue(), ^(void) { NSLog(@"PCO onError."); @@ -147,13 +150,15 @@ }); } +#pragma mark - Private + - (void)displayLogMessage:(NSString*)message { [_delegate displayLogMessage:message]; } @end -@interface APPRTCAppDelegate () +@interface APPRTCAppDelegate () @property(nonatomic, strong) APPRTCAppClient* client; @property(nonatomic, strong) PCObserver* pcObserver; @@ -163,7 +168,9 @@ @end -@implementation APPRTCAppDelegate +@implementation APPRTCAppDelegate { + NSTimer* _statsTimer; +} #pragma mark - UIApplicationDelegate methods @@ -175,6 +182,12 @@ [[APPRTCViewController alloc] initWithNibName:@"APPRTCViewController" bundle:nil]; self.window.rootViewController = self.viewController; + _statsTimer = + [NSTimer scheduledTimerWithTimeInterval:10 + target:self + selector:@selector(didFireStatsTimer:) + userInfo:nil + repeats:YES]; [self.window makeKeyAndVisible]; return YES; } @@ -488,6 +501,16 @@ }); } +#pragma mark - RTCStatsDelegate methods + +- (void)peerConnection:(RTCPeerConnection*)peerConnection + didGetStats:(NSArray*)stats { + dispatch_async(dispatch_get_main_queue(), ^{ + NSString* message = [NSString stringWithFormat:@"Stats:\n %@", stats]; + [self displayLogMessage:message]; + }); +} + #pragma mark - internal methods - (void)disconnect { @@ -531,6 +554,14 @@ return removeBackslash; } +- (void)didFireStatsTimer:(NSTimer *)timer { + if (self.peerConnection) { + [self.peerConnection getStatsWithDelegate:self + mediaStreamTrack:nil + statsOutputLevel:RTCStatsOutputLevelDebug]; + } +} + #pragma mark - public methods - (void)closeVideoUI { diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp index 3c64ded9c..03d39a39e 100755 --- a/talk/libjingle.gyp +++ b/talk/libjingle.gyp @@ -193,6 +193,8 @@ 'app/webrtc/objc/RTCPeerConnectionObserver.mm', 'app/webrtc/objc/RTCSessionDescription+Internal.h', 'app/webrtc/objc/RTCSessionDescription.mm', + 'app/webrtc/objc/RTCStatsReport+Internal.h', + 'app/webrtc/objc/RTCStatsReport.mm', 'app/webrtc/objc/RTCVideoCapturer+Internal.h', 'app/webrtc/objc/RTCVideoCapturer.mm', 'app/webrtc/objc/RTCVideoRenderer+Internal.h', @@ -216,6 +218,8 @@ 'app/webrtc/objc/public/RTCPeerConnectionFactory.h', 'app/webrtc/objc/public/RTCSessionDescription.h', 'app/webrtc/objc/public/RTCSessionDescriptionDelegate.h', + 'app/webrtc/objc/public/RTCStatsDelegate.h', + 'app/webrtc/objc/public/RTCStatsReport.h', 'app/webrtc/objc/public/RTCTypes.h', 'app/webrtc/objc/public/RTCVideoCapturer.h', 'app/webrtc/objc/public/RTCVideoRenderer.h', @@ -241,6 +245,9 @@ }, 'xcode_settings': { 'CLANG_ENABLE_OBJC_ARC': 'YES', + # common.gypi enables this for mac but we want this to be disabled + # like it is for ios. + 'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'NO', }, }, # target libjingle_peerconnection_objc ], diff --git a/talk/libjingle_tests.gyp b/talk/libjingle_tests.gyp index 31dc55171..781613700 100755 --- a/talk/libjingle_tests.gyp +++ b/talk/libjingle_tests.gyp @@ -508,6 +508,9 @@ ], 'xcode_settings': { 'CLANG_ENABLE_OBJC_ARC': 'YES', + # common.gypi enables this for mac but we want this to be disabled + # like it is for ios. + 'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'NO', 'INFOPLIST_FILE': '<(infoplist_file)', }, 'dependencies': [