diff options
author | Chih-hung Hsieh <chh@google.com> | 2016-01-20 17:50:13 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2016-01-20 17:50:13 +0000 |
commit | b3cb8ab4ede8bb77f0bdef2715efc2c1e6267072 (patch) | |
tree | 28c4cf735dd5bd9cc8f1ccd06fff8a173b20d1cb /webrtc/api | |
parent | a4acd9d6bc9b3b033d7d274316e75ee067df8d20 (diff) | |
parent | 9a337512d97e37afc142dee4fd50a41b741a87d2 (diff) | |
download | webrtc-nougat-mr1.5-release.tar.gz |
Merge "Merge upstream SHA 04cb763"android-cts_7.1_r1android-cts-7.1_r9android-cts-7.1_r8android-cts-7.1_r7android-cts-7.1_r6android-cts-7.1_r5android-cts-7.1_r4android-cts-7.1_r3android-cts-7.1_r29android-cts-7.1_r28android-cts-7.1_r27android-cts-7.1_r26android-cts-7.1_r25android-cts-7.1_r24android-cts-7.1_r23android-cts-7.1_r22android-cts-7.1_r21android-cts-7.1_r20android-cts-7.1_r2android-cts-7.1_r19android-cts-7.1_r18android-cts-7.1_r17android-cts-7.1_r16android-cts-7.1_r15android-cts-7.1_r14android-cts-7.1_r13android-cts-7.1_r12android-cts-7.1_r11android-cts-7.1_r10android-cts-7.1_r1android-cts-7.0_r9android-cts-7.0_r8android-cts-7.0_r7android-cts-7.0_r6android-cts-7.0_r5android-cts-7.0_r4android-cts-7.0_r33android-cts-7.0_r32android-cts-7.0_r31android-cts-7.0_r30android-cts-7.0_r3android-cts-7.0_r29android-cts-7.0_r28android-cts-7.0_r27android-cts-7.0_r26android-cts-7.0_r25android-cts-7.0_r24android-cts-7.0_r23android-cts-7.0_r22android-cts-7.0_r21android-cts-7.0_r20android-cts-7.0_r2android-cts-7.0_r19android-cts-7.0_r18android-cts-7.0_r17android-cts-7.0_r16android-cts-7.0_r15android-cts-7.0_r14android-cts-7.0_r13android-cts-7.0_r12android-cts-7.0_r11android-cts-7.0_r10android-cts-7.0_r1android-7.1.2_r9android-7.1.2_r8android-7.1.2_r6android-7.1.2_r5android-7.1.2_r4android-7.1.2_r39android-7.1.2_r38android-7.1.2_r37android-7.1.2_r36android-7.1.2_r33android-7.1.2_r32android-7.1.2_r30android-7.1.2_r3android-7.1.2_r29android-7.1.2_r28android-7.1.2_r27android-7.1.2_r25android-7.1.2_r24android-7.1.2_r23android-7.1.2_r2android-7.1.2_r19android-7.1.2_r18android-7.1.2_r17android-7.1.2_r16android-7.1.2_r15android-7.1.2_r14android-7.1.2_r13android-7.1.2_r12android-7.1.2_r11android-7.1.2_r10android-7.1.2_r1android-7.1.1_r9android-7.1.1_r8android-7.1.1_r7android-7.1.1_r61android-7.1.1_r60android-7.1.1_r6android-7.1.1_r59android-7.1.1_r58android-7.1.1_r57android-7.1.1_r56android-7.1.1_r55android-7.1.1_r54android-7.1.1_r53android-7.1.1_r52android-7.1.1_r51android-7.1.1_r50android-7.1.1_r49android-7.1.1_r48android-7.1.1_r47android-7.1.1_r46android-7.1.1_r45android-7.1.1_r44android-7.1.1_r43android-7.1.1_r42android-7.1.1_r41android-7.1.1_r40android-7.1.1_r4android-7.1.1_r39android-7.1.1_r38android-7.1.1_r35android-7.1.1_r33android-7.1.1_r32android-7.1.1_r31android-7.1.1_r3android-7.1.1_r28android-7.1.1_r27android-7.1.1_r26android-7.1.1_r25android-7.1.1_r24android-7.1.1_r23android-7.1.1_r22android-7.1.1_r21android-7.1.1_r20android-7.1.1_r2android-7.1.1_r17android-7.1.1_r16android-7.1.1_r15android-7.1.1_r14android-7.1.1_r13android-7.1.1_r12android-7.1.1_r11android-7.1.1_r10android-7.1.1_r1android-7.1.0_r7android-7.1.0_r6android-7.1.0_r5android-7.1.0_r4android-7.1.0_r3android-7.1.0_r2android-7.1.0_r1android-7.0.0_r9android-7.0.0_r8android-7.0.0_r7android-7.0.0_r6android-7.0.0_r5android-7.0.0_r4android-7.0.0_r36android-7.0.0_r35android-7.0.0_r34android-7.0.0_r33android-7.0.0_r32android-7.0.0_r31android-7.0.0_r30android-7.0.0_r3android-7.0.0_r29android-7.0.0_r28android-7.0.0_r27android-7.0.0_r24android-7.0.0_r21android-7.0.0_r19android-7.0.0_r17android-7.0.0_r15android-7.0.0_r14android-7.0.0_r13android-7.0.0_r12android-7.0.0_r11android-7.0.0_r10android-7.0.0_r1nougat-releasenougat-mr2.3-releasenougat-mr2.2-releasenougat-mr2.1-releasenougat-mr2-security-releasenougat-mr2-releasenougat-mr2-pixel-releasenougat-mr2-devnougat-mr1.8-releasenougat-mr1.7-releasenougat-mr1.6-releasenougat-mr1.5-releasenougat-mr1.4-releasenougat-mr1.3-releasenougat-mr1.2-releasenougat-mr1.1-releasenougat-mr1-volantis-releasenougat-mr1-security-releasenougat-mr1-releasenougat-mr1-flounder-releasenougat-mr1-devnougat-mr1-cts-releasenougat-mr0.5-releasenougat-dr1-releasenougat-devnougat-cts-releasenougat-bugfix-release
am: 9a337512d9
* commit '9a337512d97e37afc142dee4fd50a41b741a87d2': (797 commits)
Add tests for verifying transport feedback for audio and video.
Eliminate defines in talk/
Revert of Update with new default boringssl no-aes cipher suites. Re-enable tests. (patchset #3 id:40001 of https://codereview.webrtc.org/1550773002/ )
Remove assert which was incorrectly added to TcpPort::OnSentPacket.
Reland Connect TurnPort and TCPPort to AsyncPacketSocket::SignalSentPacket.
Update with new default boringssl no-aes cipher suites. Re-enable tests.
Revert of Connect TurnPort and TCPPort to AsyncPacketSocket::SignalSentPacket. (patchset #3 id:40001 of https://codereview.webrtc.org/1577873003/ )
Re-land: "Use an explicit identifier in Config"
Connect TurnPort and TCPPort to AsyncPacketSocket::SignalSentPacket.
Revert of Delete remnants of non-square pixel support from cricket::VideoFrame. (patchset #1 id:1 of https://codereview.webrtc.org/1586613002/ )
Remove libfuzzer trybot from default trybot set.
Add ramp-up tests for transport sequence number with and w/o audio.
Delete remnants of non-square pixel support from cricket::VideoFrame.
Fix IPAddress::ToSensitiveString() to avoid dependency on inet_ntop().
Revert of Storing raw audio sink for default audio track. (patchset #7 id:120001 of https://codereview.chromium.org/1551813002/ )
Re-enable tests that failed under Linux_Msan.
Revert of Use an explicit identifier in Config (patchset #4 id:60001 of https://codereview.webrtc.org/1538643004/ )
Roll chromium_revision 346fea9..099be58 (369082:369139)
Disable WebRtcVideoChannel2BaseTest.SendManyResizeOnce for TSan
Add build_protobuf variable.
...
Diffstat (limited to 'webrtc/api')
42 files changed, 2871 insertions, 0 deletions
diff --git a/webrtc/api/BUILD.gn b/webrtc/api/BUILD.gn new file mode 100644 index 0000000000..7cfa083a6b --- /dev/null +++ b/webrtc/api/BUILD.gn @@ -0,0 +1,76 @@ +# Copyright (c) 2015 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("../build/webrtc.gni") + +config("ios_config") { + libs = [ + "CoreGraphics.framework", + "GLKit.framework", + "OpenGLES.framework", + "QuartzCore.framework", + ] +} + +if (is_ios) { + source_set("rtc_api_objc") { + deps = [ + "//webrtc/base:rtc_base_objc", + #"//talk/libjingle:libjingle_peerconnection", + ] + cflags = [ + "-fobjc-arc", + "-Wobjc-missing-property-synthesis", + ] + sources = [ + # Add these when there's a BUILD.gn for peer connection APIs + #"objc/RTCIceCandidate+Private.h", + #"objc/RTCIceCandidate.h", + #"objc/RTCIceCandidate.mm", + #"objc/RTCMediaSource+Private.h", + #"objc/RTCMediaSource.h", + #"objc/RTCMediaSource.mm", + #"objc/RTCMediaStreamTrack+Private.h", + #"objc/RTCMediaStreamTrack.h", + #"objc/RTCMediaStreamTrack.mm", + "objc/RTCIceServer+Private.h", + "objc/RTCIceServer.h", + "objc/RTCIceServer.mm", + "objc/RTCMediaConstraints+Private.h", + "objc/RTCMediaConstraints.h", + "objc/RTCMediaConstraints.mm", + "objc/RTCOpenGLVideoRenderer.h", + "objc/RTCOpenGLVideoRenderer.mm", + "objc/RTCSessionDescription+Private.h", + "objc/RTCSessionDescription.h", + "objc/RTCSessionDescription.mm", + "objc/RTCStatsReport+Private.h", + "objc/RTCStatsReport.h", + "objc/RTCStatsReport.mm", + "objc/RTCVideoFrame+Private.h", + "objc/RTCVideoFrame.h", + "objc/RTCVideoFrame.mm", + "objc/RTCVideoRenderer.h", + "objc/WebRTC-Prefix.pch", + ] + + if (is_ios) { + sources += [ + "objc/RTCEAGLVideoView.h", + "objc/RTCEAGLVideoView.m", + ] + } + + if (is_mac) { + sources += [ + "objc/RTCNSGLVideoView.h", + "objc/RTCNSGLVideoView.m", + ] + } + } +} diff --git a/webrtc/api/OWNERS b/webrtc/api/OWNERS new file mode 100644 index 0000000000..cd06158b7f --- /dev/null +++ b/webrtc/api/OWNERS @@ -0,0 +1 @@ +tkchin@webrtc.org diff --git a/webrtc/api/api.gyp b/webrtc/api/api.gyp new file mode 100644 index 0000000000..ba3fe8d0bd --- /dev/null +++ b/webrtc/api/api.gyp @@ -0,0 +1,83 @@ +# Copyright (c) 2015 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. + +{ + 'includes': [ '../build/common.gypi', ], + 'conditions': [ + ['OS=="ios"', { + 'targets': [ + { + 'target_name': 'rtc_api_objc', + 'type': 'static_library', + 'dependencies': [ + '<(webrtc_root)/base/base.gyp:rtc_base_objc', + '../../talk/libjingle.gyp:libjingle_peerconnection', + ], + 'sources': [ + 'objc/RTCIceCandidate+Private.h', + 'objc/RTCIceCandidate.h', + 'objc/RTCIceCandidate.mm', + 'objc/RTCIceServer+Private.h', + 'objc/RTCIceServer.h', + 'objc/RTCIceServer.mm', + 'objc/RTCMediaConstraints+Private.h', + 'objc/RTCMediaConstraints.h', + 'objc/RTCMediaConstraints.mm', + 'objc/RTCMediaSource+Private.h', + 'objc/RTCMediaSource.h', + 'objc/RTCMediaSource.mm', + 'objc/RTCMediaStreamTrack+Private.h', + 'objc/RTCMediaStreamTrack.h', + 'objc/RTCMediaStreamTrack.mm', + 'objc/RTCOpenGLVideoRenderer.h', + 'objc/RTCOpenGLVideoRenderer.mm', + 'objc/RTCSessionDescription+Private.h', + 'objc/RTCSessionDescription.h', + 'objc/RTCSessionDescription.mm', + 'objc/RTCStatsReport+Private.h', + 'objc/RTCStatsReport.h', + 'objc/RTCStatsReport.mm', + 'objc/RTCVideoFrame+Private.h', + 'objc/RTCVideoFrame.h', + 'objc/RTCVideoFrame.mm', + 'objc/RTCVideoRenderer.h', + ], + 'conditions': [ + ['OS=="ios"', { + 'sources': [ + 'objc/RTCEAGLVideoView.h', + 'objc/RTCEAGLVideoView.m', + ], + 'all_dependent_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework CoreGraphics', + '-framework GLKit', + '-framework OpenGLES', + '-framework QuartzCore', + ] + } + } + }], + ['OS=="mac"', { + 'sources': [ + 'objc/RTCNSGLVideoView.h', + 'objc/RTCNSGLVideoView.m', + ], + }], + ], + 'xcode_settings': { + 'CLANG_ENABLE_OBJC_ARC': 'YES', + 'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'YES', + 'GCC_PREFIX_HEADER': 'objc/WebRTC-Prefix.pch', + }, + } + ], + }], # OS=="ios" + ], +} diff --git a/webrtc/api/api_tests.gyp b/webrtc/api/api_tests.gyp new file mode 100644 index 0000000000..c2c18bc693 --- /dev/null +++ b/webrtc/api/api_tests.gyp @@ -0,0 +1,40 @@ +# Copyright (c) 2015 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. + +{ + 'includes': [ '../build/common.gypi', ], + 'conditions': [ + ['OS=="ios"', { + 'targets': [ + { + 'target_name': 'rtc_api_objc_test', + 'type': 'executable', + 'dependencies': [ + '<(webrtc_root)/api/api.gyp:rtc_api_objc', + '<(webrtc_root)/base/base_tests.gyp:rtc_base_tests_utils', + ], + 'sources': [ + 'objctests/RTCIceCandidateTest.mm', + 'objctests/RTCIceServerTest.mm', + 'objctests/RTCMediaConstraintsTest.mm', + 'objctests/RTCSessionDescriptionTest.mm', + ], + 'xcode_settings': { + 'CLANG_ENABLE_OBJC_ARC': 'YES', + 'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'YES', + 'GCC_PREFIX_HEADER': 'objc/WebRTC-Prefix.pch', + # |-ObjC| flag needed to make sure category method implementations + # are included: + # https://developer.apple.com/library/mac/qa/qa1490/_index.html + 'OTHER_LDFLAGS': ['-ObjC'], + }, + } + ], + }], # OS=="ios" + ], +} diff --git a/webrtc/api/objc/OWNERS b/webrtc/api/objc/OWNERS new file mode 100644 index 0000000000..cd06158b7f --- /dev/null +++ b/webrtc/api/objc/OWNERS @@ -0,0 +1 @@ +tkchin@webrtc.org diff --git a/webrtc/api/objc/README b/webrtc/api/objc/README new file mode 100644 index 0000000000..bd33e61921 --- /dev/null +++ b/webrtc/api/objc/README @@ -0,0 +1,3 @@ +This is a work-in-progress to update the Objective-C API according to the W3C +specification. The Objective-C API located at talk/app/webrtc/objc is +deprecated, but will remain for the time being. diff --git a/webrtc/api/objc/RTCEAGLVideoView.h b/webrtc/api/objc/RTCEAGLVideoView.h new file mode 100644 index 0000000000..1a57df76bb --- /dev/null +++ b/webrtc/api/objc/RTCEAGLVideoView.h @@ -0,0 +1,35 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +#import "RTCVideoRenderer.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTCEAGLVideoView; +@protocol RTCEAGLVideoViewDelegate + +- (void)videoView:(RTCEAGLVideoView *)videoView didChangeVideoSize:(CGSize)size; + +@end + +/** + * RTCEAGLVideoView is an RTCVideoRenderer which renders video frames in its + * bounds using OpenGLES 2.0. + */ +@interface RTCEAGLVideoView : UIView <RTCVideoRenderer> + +@property(nonatomic, weak) id<RTCEAGLVideoViewDelegate> delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCEAGLVideoView.m b/webrtc/api/objc/RTCEAGLVideoView.m new file mode 100644 index 0000000000..e664ede455 --- /dev/null +++ b/webrtc/api/objc/RTCEAGLVideoView.m @@ -0,0 +1,259 @@ +/* + * Copyright 2015 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 "RTCEAGLVideoView.h" + +#import <GLKit/GLKit.h> + +#import "RTCVideoFrame.h" +#import "RTCOpenGLVideoRenderer.h" + +// RTCDisplayLinkTimer wraps a CADisplayLink and is set to fire every two screen +// refreshes, which should be 30fps. We wrap the display link in order to avoid +// a retain cycle since CADisplayLink takes a strong reference onto its target. +// The timer is paused by default. +@interface RTCDisplayLinkTimer : NSObject + +@property(nonatomic) BOOL isPaused; + +- (instancetype)initWithTimerHandler:(void (^)(void))timerHandler; +- (void)invalidate; + +@end + +@implementation RTCDisplayLinkTimer { + CADisplayLink *_displayLink; + void (^_timerHandler)(void); +} + +- (instancetype)initWithTimerHandler:(void (^)(void))timerHandler { + NSParameterAssert(timerHandler); + if (self = [super init]) { + _timerHandler = timerHandler; + _displayLink = + [CADisplayLink displayLinkWithTarget:self + selector:@selector(displayLinkDidFire:)]; + _displayLink.paused = YES; + // Set to half of screen refresh, which should be 30fps. + [_displayLink setFrameInterval:2]; + [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] + forMode:NSRunLoopCommonModes]; + } + return self; +} + +- (void)dealloc { + [self invalidate]; +} + +- (BOOL)isPaused { + return _displayLink.paused; +} + +- (void)setIsPaused:(BOOL)isPaused { + _displayLink.paused = isPaused; +} + +- (void)invalidate { + [_displayLink invalidate]; +} + +- (void)displayLinkDidFire:(CADisplayLink *)displayLink { + _timerHandler(); +} + +@end + +// RTCEAGLVideoView wraps a GLKView which is setup with +// enableSetNeedsDisplay = NO for the purpose of gaining control of +// exactly when to call -[GLKView display]. This need for extra +// control is required to avoid triggering method calls on GLKView +// that results in attempting to bind the underlying render buffer +// when the drawable size would be empty which would result in the +// error GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT. -[GLKView display] is +// the method that will trigger the binding of the render +// buffer. Because the standard behaviour of -[UIView setNeedsDisplay] +// is disabled for the reasons above, the RTCEAGLVideoView maintains +// its own |isDirty| flag. + +@interface RTCEAGLVideoView () <GLKViewDelegate> +// |videoFrame| is set when we receive a frame from a worker thread and is read +// from the display link callback so atomicity is required. +@property(atomic, strong) RTCVideoFrame *videoFrame; +@property(nonatomic, readonly) GLKView *glkView; +@property(nonatomic, readonly) RTCOpenGLVideoRenderer *glRenderer; +@end + +@implementation RTCEAGLVideoView { + RTCDisplayLinkTimer *_timer; + // This flag should only be set and read on the main thread (e.g. by + // setNeedsDisplay) + BOOL _isDirty; +} + +@synthesize delegate = _delegate; +@synthesize videoFrame = _videoFrame; +@synthesize glkView = _glkView; +@synthesize glRenderer = _glRenderer; + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + [self configure]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + if (self = [super initWithCoder:aDecoder]) { + [self configure]; + } + return self; +} + +- (void)configure { + EAGLContext *glContext = + [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + if (!glContext) { + glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + } + _glRenderer = [[RTCOpenGLVideoRenderer alloc] initWithContext:glContext]; + + // GLKView manages a framebuffer for us. + _glkView = [[GLKView alloc] initWithFrame:CGRectZero + context:glContext]; + _glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; + _glkView.drawableDepthFormat = GLKViewDrawableDepthFormatNone; + _glkView.drawableStencilFormat = GLKViewDrawableStencilFormatNone; + _glkView.drawableMultisample = GLKViewDrawableMultisampleNone; + _glkView.delegate = self; + _glkView.layer.masksToBounds = YES; + _glkView.enableSetNeedsDisplay = NO; + [self addSubview:_glkView]; + + // Listen to application state in order to clean up OpenGL before app goes + // away. + NSNotificationCenter *notificationCenter = + [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self + selector:@selector(willResignActive) + name:UIApplicationWillResignActiveNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(didBecomeActive) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + + // Frames are received on a separate thread, so we poll for current frame + // using a refresh rate proportional to screen refresh frequency. This + // occurs on the main thread. + __weak RTCEAGLVideoView *weakSelf = self; + _timer = [[RTCDisplayLinkTimer alloc] initWithTimerHandler:^{ + RTCEAGLVideoView *strongSelf = weakSelf; + [strongSelf displayLinkTimerDidFire]; + }]; + [self setupGL]; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + UIApplicationState appState = + [UIApplication sharedApplication].applicationState; + if (appState == UIApplicationStateActive) { + [self teardownGL]; + } + [_timer invalidate]; +} + +#pragma mark - UIView + +- (void)setNeedsDisplay { + [super setNeedsDisplay]; + _isDirty = YES; +} + +- (void)setNeedsDisplayInRect:(CGRect)rect { + [super setNeedsDisplayInRect:rect]; + _isDirty = YES; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + _glkView.frame = self.bounds; +} + +#pragma mark - GLKViewDelegate + +// This method is called when the GLKView's content is dirty and needs to be +// redrawn. This occurs on main thread. +- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { + // The renderer will draw the frame to the framebuffer corresponding to the + // one used by |view|. + [_glRenderer drawFrame:self.videoFrame]; +} + +#pragma mark - RTCVideoRenderer + +// These methods may be called on non-main thread. +- (void)setSize:(CGSize)size { + __weak RTCEAGLVideoView *weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + RTCEAGLVideoView *strongSelf = weakSelf; + [strongSelf.delegate videoView:strongSelf didChangeVideoSize:size]; + }); +} + +- (void)renderFrame:(RTCVideoFrame *)frame { + self.videoFrame = frame; +} + +#pragma mark - Private + +- (void)displayLinkTimerDidFire { + // Don't render unless video frame have changed or the view content + // has explicitly been marked dirty. + if (!_isDirty && _glRenderer.lastDrawnFrame == self.videoFrame) { + return; + } + + // Always reset isDirty at this point, even if -[GLKView display] + // won't be called in the case the drawable size is empty. + _isDirty = NO; + + // Only call -[GLKView display] if the drawable size is + // non-empty. Calling display will make the GLKView setup its + // render buffer if necessary, but that will fail with error + // GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT if size is empty. + if (self.bounds.size.width > 0 && self.bounds.size.height > 0) { + [_glkView display]; + } +} + +- (void)setupGL { + self.videoFrame = nil; + [_glRenderer setupGL]; + _timer.isPaused = NO; +} + +- (void)teardownGL { + self.videoFrame = nil; + _timer.isPaused = YES; + [_glkView deleteDrawable]; + [_glRenderer teardownGL]; +} + +- (void)didBecomeActive { + [self setupGL]; +} + +- (void)willResignActive { + [self teardownGL]; +} + +@end diff --git a/webrtc/api/objc/RTCIceCandidate+Private.h b/webrtc/api/objc/RTCIceCandidate+Private.h new file mode 100644 index 0000000000..ca95a43e3a --- /dev/null +++ b/webrtc/api/objc/RTCIceCandidate+Private.h @@ -0,0 +1,36 @@ +/* + * Copyright 2015 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 "RTCIceCandidate.h" + +#include "talk/app/webrtc/jsep.h" +#include "webrtc/base/scoped_ptr.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCIceCandidate () + +/** + * The native IceCandidateInterface representation of this RTCIceCandidate + * object. This is needed to pass to the underlying C++ APIs. + */ +@property(nonatomic, readonly) + rtc::scoped_ptr<webrtc::IceCandidateInterface> nativeCandidate; + +/** + * Initialize an RTCIceCandidate from a native IceCandidateInterface. No + * ownership is taken of the native candidate. + */ +- (instancetype)initWithNativeCandidate: + (webrtc::IceCandidateInterface *)candidate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCIceCandidate.h b/webrtc/api/objc/RTCIceCandidate.h new file mode 100644 index 0000000000..41ea69e991 --- /dev/null +++ b/webrtc/api/objc/RTCIceCandidate.h @@ -0,0 +1,44 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCIceCandidate : NSObject + +/** + * If present, the identifier of the "media stream identification" for the media + * component this candidate is associated with. + */ +@property(nonatomic, readonly, nullable) NSString *sdpMid; + +/** + * The index (starting at zero) of the media description this candidate is + * associated with in the SDP. + */ +@property(nonatomic, readonly) NSInteger sdpMLineIndex; + +/** The SDP string for this candidate. */ +@property(nonatomic, readonly) NSString *sdp; + +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initialize an RTCIceCandidate from SDP. + */ +- (instancetype)initWithSdp:(NSString *)sdp + sdpMLineIndex:(NSInteger)sdpMLineIndex + sdpMid:(nullable NSString *)sdpMid + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCIceCandidate.mm b/webrtc/api/objc/RTCIceCandidate.mm new file mode 100644 index 0000000000..9e094f6f06 --- /dev/null +++ b/webrtc/api/objc/RTCIceCandidate.mm @@ -0,0 +1,70 @@ +/* + * Copyright 2015 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 "RTCIceCandidate.h" + +#import "webrtc/api/objc/RTCIceCandidate+Private.h" +#import "webrtc/base/objc/NSString+StdString.h" +#import "webrtc/base/objc/RTCLogging.h" + +@implementation RTCIceCandidate + +@synthesize sdpMid = _sdpMid; +@synthesize sdpMLineIndex = _sdpMLineIndex; +@synthesize sdp = _sdp; + +- (instancetype)initWithSdp:(NSString *)sdp + sdpMLineIndex:(NSInteger)sdpMLineIndex + sdpMid:(NSString *)sdpMid { + NSParameterAssert(sdp.length); + if (self = [super init]) { + _sdpMid = [sdpMid copy]; + _sdpMLineIndex = sdpMLineIndex; + _sdp = [sdp copy]; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"RTCIceCandidate:\n%@\n%ld\n%@", + _sdpMid, + (long)_sdpMLineIndex, + _sdp]; +} + +#pragma mark - Private + +- (instancetype)initWithNativeCandidate: + (webrtc::IceCandidateInterface *)candidate { + NSParameterAssert(candidate); + std::string sdp; + candidate->ToString(&sdp); + + return [self initWithSdp:[NSString stringForStdString:sdp] + sdpMLineIndex:candidate->sdp_mline_index() + sdpMid:[NSString stringForStdString:candidate->sdp_mid()]]; +} + +- (rtc::scoped_ptr<webrtc::IceCandidateInterface>)nativeCandidate { + webrtc::SdpParseError error; + + webrtc::IceCandidateInterface *candidate = webrtc::CreateIceCandidate( + _sdpMid.stdString, _sdpMLineIndex, _sdp.stdString, &error); + + if (!candidate) { + RTCLog(@"Failed to create ICE candidate: %s\nline: %s", + error.description.c_str(), + error.line.c_str()); + } + + return rtc::scoped_ptr<webrtc::IceCandidateInterface>(candidate); +} + +@end diff --git a/webrtc/api/objc/RTCIceServer+Private.h b/webrtc/api/objc/RTCIceServer+Private.h new file mode 100644 index 0000000000..59f5a92dff --- /dev/null +++ b/webrtc/api/objc/RTCIceServer+Private.h @@ -0,0 +1,28 @@ +/* + * Copyright 2015 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 "RTCIceServer.h" + +#include "talk/app/webrtc/peerconnectioninterface.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCIceServer () + +/** + * IceServer struct representation of this RTCIceServer object's data. + * This is needed to pass to the underlying C++ APIs. + */ +@property(nonatomic, readonly) + webrtc::PeerConnectionInterface::IceServer iceServer; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCIceServer.h b/webrtc/api/objc/RTCIceServer.h new file mode 100644 index 0000000000..be4e0d7b6e --- /dev/null +++ b/webrtc/api/objc/RTCIceServer.h @@ -0,0 +1,42 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCIceServer : NSObject + +/** URI(s) for this server represented as NSStrings. */ +@property(nonatomic, copy, readonly) NSArray<NSString *> *urlStrings; + +/** Username to use if this RTCIceServer object is a TURN server. */ +@property(nonatomic, copy, readonly, nullable) NSString *username; + +/** Credential to use if this RTCIceServer object is a TURN server. */ +@property(nonatomic, copy, readonly, nullable) NSString *credential; + +- (instancetype)init NS_UNAVAILABLE; + +/** Convenience initializer for a server with no authentication (e.g. STUN). */ +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings; + +/** + * Initialize an RTCIceServer with its associated URLs, optional username, + * optional credential, and credentialType. + */ +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(nullable NSString *)username + credential:(nullable NSString *)credential + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCIceServer.mm b/webrtc/api/objc/RTCIceServer.mm new file mode 100644 index 0000000000..7a898e06d5 --- /dev/null +++ b/webrtc/api/objc/RTCIceServer.mm @@ -0,0 +1,64 @@ +/* + * Copyright 2015 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 "RTCIceServer.h" + +#import "webrtc/api/objc/RTCIceServer+Private.h" +#import "webrtc/base/objc/NSString+StdString.h" + +@implementation RTCIceServer + +@synthesize urlStrings = _urlStrings; +@synthesize username = _username; +@synthesize credential = _credential; + +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings { + NSParameterAssert(urlStrings.count); + return [self initWithURLStrings:urlStrings + username:nil + credential:nil]; +} + +- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings + username:(NSString *)username + credential:(NSString *)credential { + NSParameterAssert(urlStrings.count); + if (self = [super init]) { + _urlStrings = [[NSArray alloc] initWithArray:urlStrings copyItems:YES]; + _username = [username copy]; + _credential = [credential copy]; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"RTCIceServer:\n%@\n%@\n%@", + _urlStrings, + _username, + _credential]; +} + +#pragma mark - Private + +- (webrtc::PeerConnectionInterface::IceServer)iceServer { + __block webrtc::PeerConnectionInterface::IceServer iceServer; + + iceServer.username = [NSString stdStringForString:_username]; + iceServer.password = [NSString stdStringForString:_credential]; + + [_urlStrings enumerateObjectsUsingBlock:^(NSString *url, + NSUInteger idx, + BOOL *stop) { + iceServer.urls.push_back(url.stdString); + }]; + return iceServer; +} + +@end diff --git a/webrtc/api/objc/RTCMediaConstraints+Private.h b/webrtc/api/objc/RTCMediaConstraints+Private.h new file mode 100644 index 0000000000..2c4b722104 --- /dev/null +++ b/webrtc/api/objc/RTCMediaConstraints+Private.h @@ -0,0 +1,53 @@ +/* + * Copyright 2015 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 "RTCMediaConstraints.h" + +#include "talk/app/webrtc/mediaconstraintsinterface.h" +#include "webrtc/base/scoped_ptr.h" + +namespace webrtc { + +class MediaConstraints : public MediaConstraintsInterface { + public: + virtual ~MediaConstraints(); + MediaConstraints(); + MediaConstraints( + const MediaConstraintsInterface::Constraints& mandatory, + const MediaConstraintsInterface::Constraints& optional); + virtual const Constraints& GetMandatory() const; + virtual const Constraints& GetOptional() const; + + private: + MediaConstraintsInterface::Constraints mandatory_; + MediaConstraintsInterface::Constraints optional_; +}; + +} // namespace webrtc + + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCMediaConstraints () + +/** + * A MediaConstraints representation of this RTCMediaConstraints object. This is + * needed to pass to the underlying C++ APIs. + */ +- (rtc::scoped_ptr<webrtc::MediaConstraints>)nativeConstraints; + +/** Return a native Constraints object representing these constraints */ ++ (webrtc::MediaConstraintsInterface::Constraints) + nativeConstraintsForConstraints: + (NSDictionary<NSString *, NSString *> *)constraints; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCMediaConstraints.h b/webrtc/api/objc/RTCMediaConstraints.h new file mode 100644 index 0000000000..a8ad39142e --- /dev/null +++ b/webrtc/api/objc/RTCMediaConstraints.h @@ -0,0 +1,28 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCMediaConstraints : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** Initialize with mandatory and/or optional constraints. */ +- (instancetype)initWithMandatoryConstraints: + (nullable NSDictionary<NSString *, NSString *> *)mandatory + optionalConstraints: + (nullable NSDictionary<NSString *, NSString *> *)optional + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCMediaConstraints.mm b/webrtc/api/objc/RTCMediaConstraints.mm new file mode 100644 index 0000000000..a53a517747 --- /dev/null +++ b/webrtc/api/objc/RTCMediaConstraints.mm @@ -0,0 +1,92 @@ +/* + * Copyright 2015 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 "RTCMediaConstraints.h" + +#import "webrtc/api/objc/RTCMediaConstraints+Private.h" +#import "webrtc/base/objc/NSString+StdString.h" + +namespace webrtc { + +MediaConstraints::~MediaConstraints() {} + +MediaConstraints::MediaConstraints() {} + +MediaConstraints::MediaConstraints( + const MediaConstraintsInterface::Constraints& mandatory, + const MediaConstraintsInterface::Constraints& optional) + : mandatory_(mandatory), optional_(optional) {} + +const MediaConstraintsInterface::Constraints& +MediaConstraints::GetMandatory() const { + return mandatory_; +} + +const MediaConstraintsInterface::Constraints& +MediaConstraints::GetOptional() const { + return optional_; +} + +} // namespace webrtc + + +@implementation RTCMediaConstraints { + NSDictionary<NSString *, NSString *> *_mandatory; + NSDictionary<NSString *, NSString *> *_optional; +} + +- (instancetype)initWithMandatoryConstraints: + (NSDictionary<NSString *, NSString *> *)mandatory + optionalConstraints: + (NSDictionary<NSString *, NSString *> *)optional { + if (self = [super init]) { + _mandatory = [[NSDictionary alloc] initWithDictionary:mandatory + copyItems:YES]; + _optional = [[NSDictionary alloc] initWithDictionary:optional + copyItems:YES]; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"RTCMediaConstraints:\n%@\n%@", + _mandatory, + _optional]; +} + +#pragma mark - Private + +- (rtc::scoped_ptr<webrtc::MediaConstraints>)nativeConstraints { + webrtc::MediaConstraintsInterface::Constraints mandatory = + [[self class] nativeConstraintsForConstraints:_mandatory]; + webrtc::MediaConstraintsInterface::Constraints optional = + [[self class] nativeConstraintsForConstraints:_optional]; + + webrtc::MediaConstraints *nativeConstraints = + new webrtc::MediaConstraints(mandatory, optional); + return rtc::scoped_ptr<webrtc::MediaConstraints>(nativeConstraints); +} + ++ (webrtc::MediaConstraintsInterface::Constraints) + nativeConstraintsForConstraints: + (NSDictionary<NSString *, NSString *> *)constraints { + webrtc::MediaConstraintsInterface::Constraints nativeConstraints; + for (NSString *key in constraints) { + NSAssert([key isKindOfClass:[NSString class]], + @"%@ is not an NSString.", key); + NSAssert([constraints[key] isKindOfClass:[NSString class]], + @"%@ is not an NSString.", constraints[key]); + nativeConstraints.push_back(webrtc::MediaConstraintsInterface::Constraint( + key.stdString, constraints[key].stdString)); + } + return nativeConstraints; +} + +@end diff --git a/webrtc/api/objc/RTCMediaSource+Private.h b/webrtc/api/objc/RTCMediaSource+Private.h new file mode 100644 index 0000000000..fcbaad8e45 --- /dev/null +++ b/webrtc/api/objc/RTCMediaSource+Private.h @@ -0,0 +1,41 @@ +/* + * Copyright 2015 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 "RTCMediaSource.h" + +#include "talk/app/webrtc/mediastreaminterface.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCMediaSource () + +/** + * The MediaSourceInterface object passed to this RTCMediaSource during + * construction. + */ +@property(nonatomic, readonly) + rtc::scoped_refptr<webrtc::MediaSourceInterface> nativeMediaSource; + +/** Initialize an RTCMediaSource from a native MediaSourceInterface. */ +- (instancetype)initWithNativeMediaSource: + (rtc::scoped_refptr<webrtc::MediaSourceInterface>)nativeMediaSource + NS_DESIGNATED_INITIALIZER; + ++ (webrtc::MediaSourceInterface::SourceState)nativeSourceStateForState: + (RTCSourceState)state; + ++ (RTCSourceState)sourceStateForNativeState: + (webrtc::MediaSourceInterface::SourceState)nativeState; + ++ (NSString *)stringForState:(RTCSourceState)state; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCMediaSource.h b/webrtc/api/objc/RTCMediaSource.h new file mode 100644 index 0000000000..0b36b8d709 --- /dev/null +++ b/webrtc/api/objc/RTCMediaSource.h @@ -0,0 +1,31 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +typedef NS_ENUM(NSInteger, RTCSourceState) { + RTCSourceStateInitializing, + RTCSourceStateLive, + RTCSourceStateEnded, + RTCSourceStateMuted, +}; + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCMediaSource : NSObject + +/** The current state of the RTCMediaSource. */ +@property(nonatomic, readonly) RTCSourceState state; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCMediaSource.mm b/webrtc/api/objc/RTCMediaSource.mm new file mode 100644 index 0000000000..5f46ab8318 --- /dev/null +++ b/webrtc/api/objc/RTCMediaSource.mm @@ -0,0 +1,84 @@ +/* + * Copyright 2015 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 "RTCMediaSource.h" + +#import "webrtc/api/objc/RTCMediaSource+Private.h" + +@implementation RTCMediaSource { + rtc::scoped_refptr<webrtc::MediaSourceInterface> _nativeMediaSource; +} + +- (RTCSourceState)state { + return [[self class] sourceStateForNativeState:_nativeMediaSource->state()]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"RTCMediaSource:\n%@", + [[self class] stringForState:self.state]]; +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::MediaSourceInterface>)nativeMediaSource { + return _nativeMediaSource; +} + +- (instancetype)initWithNativeMediaSource: + (rtc::scoped_refptr<webrtc::MediaSourceInterface>)nativeMediaSource { + NSParameterAssert(nativeMediaSource); + if (self = [super init]) { + _nativeMediaSource = nativeMediaSource; + } + return self; +} + ++ (webrtc::MediaSourceInterface::SourceState)nativeSourceStateForState: + (RTCSourceState)state { + switch (state) { + case RTCSourceStateInitializing: + return webrtc::MediaSourceInterface::kInitializing; + case RTCSourceStateLive: + return webrtc::MediaSourceInterface::kLive; + case RTCSourceStateEnded: + return webrtc::MediaSourceInterface::kEnded; + case RTCSourceStateMuted: + return webrtc::MediaSourceInterface::kMuted; + } +} + ++ (RTCSourceState)sourceStateForNativeState: + (webrtc::MediaSourceInterface::SourceState)nativeState { + switch (nativeState) { + case webrtc::MediaSourceInterface::kInitializing: + return RTCSourceStateInitializing; + case webrtc::MediaSourceInterface::kLive: + return RTCSourceStateLive; + case webrtc::MediaSourceInterface::kEnded: + return RTCSourceStateEnded; + case webrtc::MediaSourceInterface::kMuted: + return RTCSourceStateMuted; + } +} + ++ (NSString *)stringForState:(RTCSourceState)state { + switch (state) { + case RTCSourceStateInitializing: + return @"Initializing"; + case RTCSourceStateLive: + return @"Live"; + case RTCSourceStateEnded: + return @"Ended"; + case RTCSourceStateMuted: + return @"Muted"; + } +} + +@end diff --git a/webrtc/api/objc/RTCMediaStreamTrack+Private.h b/webrtc/api/objc/RTCMediaStreamTrack+Private.h new file mode 100644 index 0000000000..3e17e63cd3 --- /dev/null +++ b/webrtc/api/objc/RTCMediaStreamTrack+Private.h @@ -0,0 +1,45 @@ +/* + * Copyright 2015 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 "RTCMediaStreamTrack.h" + +#include "talk/app/webrtc/mediastreaminterface.h" +#include "webrtc/base/scoped_ptr.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCMediaStreamTrack () + +/** + * The native MediaStreamTrackInterface representation of this + * RTCMediaStreamTrack object. This is needed to pass to the underlying C++ + * APIs. + */ +@property(nonatomic, readonly) + rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> nativeTrack; + +/** + * Initialize an RTCMediaStreamTrack from a native MediaStreamTrackInterface. + */ +- (instancetype)initWithNativeTrack: + (rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeTrack + NS_DESIGNATED_INITIALIZER; + ++ (webrtc::MediaStreamTrackInterface::TrackState)nativeTrackStateForState: + (RTCMediaStreamTrackState)state; + ++ (RTCMediaStreamTrackState)trackStateForNativeState: + (webrtc::MediaStreamTrackInterface::TrackState)nativeState; + ++ (NSString *)stringForState:(RTCMediaStreamTrackState)state; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCMediaStreamTrack.h b/webrtc/api/objc/RTCMediaStreamTrack.h new file mode 100644 index 0000000000..beb48d3b6f --- /dev/null +++ b/webrtc/api/objc/RTCMediaStreamTrack.h @@ -0,0 +1,47 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +/** + * Represents the state of the track. This exposes the same states in C++, + * which include two more states than are in the W3C spec. + */ +typedef NS_ENUM(NSInteger, RTCMediaStreamTrackState) { + RTCMediaStreamTrackStateInitializing, + RTCMediaStreamTrackStateLive, + RTCMediaStreamTrackStateEnded, + RTCMediaStreamTrackStateFailed, +}; + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCMediaStreamTrack : NSObject + +/** + * The kind of track. For example, "audio" if this track represents an audio + * track and "video" if this track represents a video track. + */ +@property(nonatomic, readonly) NSString *kind; + +/** An identifier string. */ +@property(nonatomic, readonly) NSString *trackId; + +/** The enabled state of the track. */ +@property(nonatomic) BOOL isEnabled; + +/** The state of the track. */ +@property(nonatomic, readonly) RTCMediaStreamTrackState readyState; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCMediaStreamTrack.mm b/webrtc/api/objc/RTCMediaStreamTrack.mm new file mode 100644 index 0000000000..e5751b0746 --- /dev/null +++ b/webrtc/api/objc/RTCMediaStreamTrack.mm @@ -0,0 +1,105 @@ +/* + * Copyright 2015 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 "RTCMediaStreamTrack.h" + +#import "webrtc/api/objc/RTCMediaStreamTrack+Private.h" +#import "webrtc/base/objc/NSString+StdString.h" + +@implementation RTCMediaStreamTrack { + rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> _nativeTrack; +} + +- (NSString *)kind { + return [NSString stringForStdString:_nativeTrack->kind()]; +} + +- (NSString *)trackId { + return [NSString stringForStdString:_nativeTrack->id()]; +} + +- (BOOL)isEnabled { + return _nativeTrack->enabled(); +} + +- (void)setIsEnabled:(BOOL)isEnabled { + _nativeTrack->set_enabled(isEnabled); +} + +- (RTCMediaStreamTrackState)readyState { + return [[self class] trackStateForNativeState:_nativeTrack->state()]; +} + +- (NSString *)description { + NSString *readyState = [[self class] stringForState:self.readyState]; + return [NSString stringWithFormat:@"RTCMediaStreamTrack:\n%@\n%@\n%@\n%@", + self.kind, + self.trackId, + self.isEnabled ? @"enabled" : @"disabled", + readyState]; +} + +#pragma mark - Private + +- (rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeTrack { + return _nativeTrack; +} + +- (instancetype)initWithNativeTrack: + (rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>)nativeTrack { + NSParameterAssert(nativeTrack); + if (self = [super init]) { + _nativeTrack = nativeTrack; + } + return self; +} + ++ (webrtc::MediaStreamTrackInterface::TrackState)nativeTrackStateForState: + (RTCMediaStreamTrackState)state { + switch (state) { + case RTCMediaStreamTrackStateInitializing: + return webrtc::MediaStreamTrackInterface::kInitializing; + case RTCMediaStreamTrackStateLive: + return webrtc::MediaStreamTrackInterface::kLive; + case RTCMediaStreamTrackStateEnded: + return webrtc::MediaStreamTrackInterface::kEnded; + case RTCMediaStreamTrackStateFailed: + return webrtc::MediaStreamTrackInterface::kFailed; + } +} + ++ (RTCMediaStreamTrackState)trackStateForNativeState: + (webrtc::MediaStreamTrackInterface::TrackState)nativeState { + switch (nativeState) { + case webrtc::MediaStreamTrackInterface::kInitializing: + return RTCMediaStreamTrackStateInitializing; + case webrtc::MediaStreamTrackInterface::kLive: + return RTCMediaStreamTrackStateLive; + case webrtc::MediaStreamTrackInterface::kEnded: + return RTCMediaStreamTrackStateEnded; + case webrtc::MediaStreamTrackInterface::kFailed: + return RTCMediaStreamTrackStateFailed; + } +} + ++ (NSString *)stringForState:(RTCMediaStreamTrackState)state { + switch (state) { + case RTCMediaStreamTrackStateInitializing: + return @"Initializing"; + case RTCMediaStreamTrackStateLive: + return @"Live"; + case RTCMediaStreamTrackStateEnded: + return @"Ended"; + case RTCMediaStreamTrackStateFailed: + return @"Failed"; + } +} + +@end diff --git a/webrtc/api/objc/RTCNSGLVideoView.h b/webrtc/api/objc/RTCNSGLVideoView.h new file mode 100644 index 0000000000..27eb31e9af --- /dev/null +++ b/webrtc/api/objc/RTCNSGLVideoView.h @@ -0,0 +1,34 @@ +/* + * Copyright 2015 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. + */ + +#if TARGET_OS_IPHONE +#error "This file targets OSX." +#endif + +#import <AppKit/NSOpenGLView.h> + +#import "RTCVideoRenderer.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RTCNSGLVideoView; +@protocol RTCNSGLVideoViewDelegate + +- (void)videoView:(RTCNSGLVideoView *)videoView didChangeVideoSize:(CGSize)size; + +@end + +@interface RTCNSGLVideoView : NSOpenGLView <RTCVideoRenderer> + +@property(nonatomic, weak) id<RTCNSGLVideoViewDelegate> delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCNSGLVideoView.m b/webrtc/api/objc/RTCNSGLVideoView.m new file mode 100644 index 0000000000..063e6f1330 --- /dev/null +++ b/webrtc/api/objc/RTCNSGLVideoView.m @@ -0,0 +1,141 @@ +/* + * Copyright 2015 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 "RTCNSGLVideoView.h" + +#import <CoreVideo/CVDisplayLink.h> +#import <OpenGL/gl3.h> +#import "RTCVideoFrame.h" +#import "RTCOpenGLVideoRenderer.h" + +@interface RTCNSGLVideoView () +// |videoFrame| is set when we receive a frame from a worker thread and is read +// from the display link callback so atomicity is required. +@property(atomic, strong) RTCVideoFrame *videoFrame; +@property(atomic, strong) RTCOpenGLVideoRenderer *glRenderer; +- (void)drawFrame; +@end + +static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink, + const CVTimeStamp *now, + const CVTimeStamp *outputTime, + CVOptionFlags flagsIn, + CVOptionFlags *flagsOut, + void *displayLinkContext) { + RTCNSGLVideoView *view = (__bridge RTCNSGLVideoView *)displayLinkContext; + [view drawFrame]; + return kCVReturnSuccess; +} + +@implementation RTCNSGLVideoView { + CVDisplayLinkRef _displayLink; +} + +@synthesize delegate = _delegate; +@synthesize videoFrame = _videoFrame; +@synthesize glRenderer = _glRenderer; + +- (void)dealloc { + [self teardownDisplayLink]; +} + +- (void)drawRect:(NSRect)rect { + [self drawFrame]; +} + +- (void)reshape { + [super reshape]; + NSRect frame = [self frame]; + CGLLockContext([[self openGLContext] CGLContextObj]); + glViewport(0, 0, frame.size.width, frame.size.height); + CGLUnlockContext([[self openGLContext] CGLContextObj]); +} + +- (void)lockFocus { + NSOpenGLContext *context = [self openGLContext]; + [super lockFocus]; + if ([context view] != self) { + [context setView:self]; + } + [context makeCurrentContext]; +} + +- (void)prepareOpenGL { + [super prepareOpenGL]; + if (!self.glRenderer) { + self.glRenderer = + [[RTCOpenGLVideoRenderer alloc] initWithContext:[self openGLContext]]; + } + [self.glRenderer setupGL]; + [self setupDisplayLink]; +} + +- (void)clearGLContext { + [self.glRenderer teardownGL]; + self.glRenderer = nil; + [super clearGLContext]; +} + +#pragma mark - RTCVideoRenderer + +// These methods may be called on non-main thread. +- (void)setSize:(CGSize)size { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate videoView:self didChangeVideoSize:size]; + }); +} + +- (void)renderFrame:(RTCVideoFrame *)frame { + self.videoFrame = frame; +} + +#pragma mark - Private + +- (void)drawFrame { + RTCVideoFrame *videoFrame = self.videoFrame; + if (self.glRenderer.lastDrawnFrame != videoFrame) { + // This method may be called from CVDisplayLink callback which isn't on the + // main thread so we have to lock the GL context before drawing. + CGLLockContext([[self openGLContext] CGLContextObj]); + [self.glRenderer drawFrame:videoFrame]; + CGLUnlockContext([[self openGLContext] CGLContextObj]); + } +} + +- (void)setupDisplayLink { + if (_displayLink) { + return; + } + // Synchronize buffer swaps with vertical refresh rate. + GLint swapInt = 1; + [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + + // Create display link. + CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); + CVDisplayLinkSetOutputCallback(_displayLink, + &OnDisplayLinkFired, + (__bridge void *)self); + // Set the display link for the current renderer. + CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; + CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; + CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext( + _displayLink, cglContext, cglPixelFormat); + CVDisplayLinkStart(_displayLink); +} + +- (void)teardownDisplayLink { + if (!_displayLink) { + return; + } + CVDisplayLinkRelease(_displayLink); + _displayLink = NULL; +} + +@end diff --git a/webrtc/api/objc/RTCOpenGLVideoRenderer.h b/webrtc/api/objc/RTCOpenGLVideoRenderer.h new file mode 100644 index 0000000000..729839c6a3 --- /dev/null +++ b/webrtc/api/objc/RTCOpenGLVideoRenderer.h @@ -0,0 +1,58 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> +#if TARGET_OS_IPHONE +#import <GLKit/GLKit.h> +#else +#import <AppKit/NSOpenGL.h> +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class RTCVideoFrame; + +// RTCOpenGLVideoRenderer issues appropriate OpenGL commands to draw a frame to +// the currently bound framebuffer. Supports OpenGL 3.2 and OpenGLES 2.0. OpenGL +// framebuffer creation and management should be handled elsewhere using the +// same context used to initialize this class. +@interface RTCOpenGLVideoRenderer : NSObject + +// The last successfully drawn frame. Used to avoid drawing frames unnecessarily +// hence saving battery life by reducing load. +@property(nonatomic, readonly) RTCVideoFrame *lastDrawnFrame; + +#if TARGET_OS_IPHONE +- (instancetype)initWithContext:(EAGLContext *)context + NS_DESIGNATED_INITIALIZER; +#else +- (instancetype)initWithContext:(NSOpenGLContext *)context + NS_DESIGNATED_INITIALIZER; +#endif + +// Draws |frame| onto the currently bound OpenGL framebuffer. |setupGL| must be +// called before this function will succeed. +- (BOOL)drawFrame:(RTCVideoFrame *)frame; + +// The following methods are used to manage OpenGL resources. On iOS +// applications should release resources when placed in background for use in +// the foreground application. In fact, attempting to call OpenGLES commands +// while in background will result in application termination. + +// Sets up the OpenGL state needed for rendering. +- (void)setupGL; +// Tears down the OpenGL state created by |setupGL|. +- (void)teardownGL; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCOpenGLVideoRenderer.mm b/webrtc/api/objc/RTCOpenGLVideoRenderer.mm new file mode 100644 index 0000000000..56a6431ffa --- /dev/null +++ b/webrtc/api/objc/RTCOpenGLVideoRenderer.mm @@ -0,0 +1,485 @@ +/* + * Copyright 2015 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 "RTCOpenGLVideoRenderer.h" + +#include <string.h> + +#include "webrtc/base/scoped_ptr.h" + +#if TARGET_OS_IPHONE +#import <OpenGLES/ES3/gl.h> +#else +#import <OpenGL/gl3.h> +#endif + +#import "RTCVideoFrame.h" + +// TODO(tkchin): check and log openGL errors. Methods here return BOOLs in +// anticipation of that happening in the future. + +#if TARGET_OS_IPHONE +#define RTC_PIXEL_FORMAT GL_LUMINANCE +#define SHADER_VERSION +#define VERTEX_SHADER_IN "attribute" +#define VERTEX_SHADER_OUT "varying" +#define FRAGMENT_SHADER_IN "varying" +#define FRAGMENT_SHADER_OUT +#define FRAGMENT_SHADER_COLOR "gl_FragColor" +#define FRAGMENT_SHADER_TEXTURE "texture2D" +#else +#define RTC_PIXEL_FORMAT GL_RED +#define SHADER_VERSION "#version 150\n" +#define VERTEX_SHADER_IN "in" +#define VERTEX_SHADER_OUT "out" +#define FRAGMENT_SHADER_IN "in" +#define FRAGMENT_SHADER_OUT "out vec4 fragColor;\n" +#define FRAGMENT_SHADER_COLOR "fragColor" +#define FRAGMENT_SHADER_TEXTURE "texture" +#endif + +// Vertex shader doesn't do anything except pass coordinates through. +static const char kVertexShaderSource[] = + SHADER_VERSION + VERTEX_SHADER_IN " vec2 position;\n" + VERTEX_SHADER_IN " vec2 texcoord;\n" + VERTEX_SHADER_OUT " vec2 v_texcoord;\n" + "void main() {\n" + " gl_Position = vec4(position.x, position.y, 0.0, 1.0);\n" + " v_texcoord = texcoord;\n" + "}\n"; + +// Fragment shader converts YUV values from input textures into a final RGB +// pixel. The conversion formula is from http://www.fourcc.org/fccyvrgb.php. +static const char kFragmentShaderSource[] = + SHADER_VERSION + "precision highp float;" + FRAGMENT_SHADER_IN " vec2 v_texcoord;\n" + "uniform lowp sampler2D s_textureY;\n" + "uniform lowp sampler2D s_textureU;\n" + "uniform lowp sampler2D s_textureV;\n" + FRAGMENT_SHADER_OUT + "void main() {\n" + " float y, u, v, r, g, b;\n" + " y = " FRAGMENT_SHADER_TEXTURE "(s_textureY, v_texcoord).r;\n" + " u = " FRAGMENT_SHADER_TEXTURE "(s_textureU, v_texcoord).r;\n" + " v = " FRAGMENT_SHADER_TEXTURE "(s_textureV, v_texcoord).r;\n" + " u = u - 0.5;\n" + " v = v - 0.5;\n" + " r = y + 1.403 * v;\n" + " g = y - 0.344 * u - 0.714 * v;\n" + " b = y + 1.770 * u;\n" + " " FRAGMENT_SHADER_COLOR " = vec4(r, g, b, 1.0);\n" + " }\n"; + +// Compiles a shader of the given |type| with GLSL source |source| and returns +// the shader handle or 0 on error. +GLuint CreateShader(GLenum type, const GLchar *source) { + GLuint shader = glCreateShader(type); + if (!shader) { + return 0; + } + glShaderSource(shader, 1, &source, NULL); + glCompileShader(shader); + GLint compileStatus = GL_FALSE; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); + if (compileStatus == GL_FALSE) { + glDeleteShader(shader); + shader = 0; + } + return shader; +} + +// Links a shader program with the given vertex and fragment shaders and +// returns the program handle or 0 on error. +GLuint CreateProgram(GLuint vertexShader, GLuint fragmentShader) { + if (vertexShader == 0 || fragmentShader == 0) { + return 0; + } + GLuint program = glCreateProgram(); + if (!program) { + return 0; + } + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + glLinkProgram(program); + GLint linkStatus = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); + if (linkStatus == GL_FALSE) { + glDeleteProgram(program); + program = 0; + } + return program; +} + +// When modelview and projection matrices are identity (default) the world is +// contained in the square around origin with unit size 2. Drawing to these +// coordinates is equivalent to drawing to the entire screen. The texture is +// stretched over that square using texture coordinates (u, v) that range +// from (0, 0) to (1, 1) inclusive. Texture coordinates are flipped vertically +// here because the incoming frame has origin in upper left hand corner but +// OpenGL expects origin in bottom left corner. +const GLfloat gVertices[] = { + // X, Y, U, V. + -1, -1, 0, 1, // Bottom left. + 1, -1, 1, 1, // Bottom right. + 1, 1, 1, 0, // Top right. + -1, 1, 0, 0, // Top left. +}; + +// |kNumTextures| must not exceed 8, which is the limit in OpenGLES2. Two sets +// of 3 textures are used here, one for each of the Y, U and V planes. Having +// two sets alleviates CPU blockage in the event that the GPU is asked to render +// to a texture that is already in use. +static const GLsizei kNumTextureSets = 2; +static const GLsizei kNumTextures = 3 * kNumTextureSets; + +@implementation RTCOpenGLVideoRenderer { +#if TARGET_OS_IPHONE + EAGLContext *_context; +#else + NSOpenGLContext *_context; +#endif + BOOL _isInitialized; + NSUInteger _currentTextureSet; + // Handles for OpenGL constructs. + GLuint _textures[kNumTextures]; + GLuint _program; +#if !TARGET_OS_IPHONE + GLuint _vertexArray; +#endif + GLuint _vertexBuffer; + GLint _position; + GLint _texcoord; + GLint _ySampler; + GLint _uSampler; + GLint _vSampler; + // Used to create a non-padded plane for GPU upload when we receive padded + // frames. + rtc::scoped_ptr<uint8_t[]> _planeBuffer; +} + +@synthesize lastDrawnFrame = _lastDrawnFrame; + ++ (void)initialize { + // Disable dithering for performance. + glDisable(GL_DITHER); +} + +#if TARGET_OS_IPHONE +- (instancetype)initWithContext:(EAGLContext *)context { +#else +- (instancetype)initWithContext:(NSOpenGLContext *)context { +#endif + NSAssert(context != nil, @"context cannot be nil"); + if (self = [super init]) { + _context = context; + } + return self; +} + +- (BOOL)drawFrame:(RTCVideoFrame *)frame { + if (!_isInitialized) { + return NO; + } + if (_lastDrawnFrame == frame) { + return NO; + } + [self ensureGLContext]; + glClear(GL_COLOR_BUFFER_BIT); + if (frame) { + if (![self updateTextureSizesForFrame:frame] || + ![self updateTextureDataForFrame:frame]) { + return NO; + } +#if !TARGET_OS_IPHONE + glBindVertexArray(_vertexArray); +#endif + glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } +#if !TARGET_OS_IPHONE + [_context flushBuffer]; +#endif + _lastDrawnFrame = frame; + return YES; +} + +- (void)setupGL { + if (_isInitialized) { + return; + } + [self ensureGLContext]; + if (![self setupProgram]) { + return; + } + if (![self setupTextures]) { + return; + } + if (![self setupVertices]) { + return; + } + glUseProgram(_program); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + _isInitialized = YES; +} + +- (void)teardownGL { + if (!_isInitialized) { + return; + } + [self ensureGLContext]; + glDeleteProgram(_program); + _program = 0; + glDeleteTextures(kNumTextures, _textures); + glDeleteBuffers(1, &_vertexBuffer); + _vertexBuffer = 0; +#if !TARGET_OS_IPHONE + glDeleteVertexArrays(1, &_vertexArray); +#endif + _isInitialized = NO; +} + +#pragma mark - Private + +- (void)ensureGLContext { + NSAssert(_context, @"context shouldn't be nil"); +#if TARGET_OS_IPHONE + if ([EAGLContext currentContext] != _context) { + [EAGLContext setCurrentContext:_context]; + } +#else + if ([NSOpenGLContext currentContext] != _context) { + [_context makeCurrentContext]; + } +#endif +} + +- (BOOL)setupProgram { + NSAssert(!_program, @"program already set up"); + GLuint vertexShader = CreateShader(GL_VERTEX_SHADER, kVertexShaderSource); + NSAssert(vertexShader, @"failed to create vertex shader"); + GLuint fragmentShader = + CreateShader(GL_FRAGMENT_SHADER, kFragmentShaderSource); + NSAssert(fragmentShader, @"failed to create fragment shader"); + _program = CreateProgram(vertexShader, fragmentShader); + // Shaders are created only to generate program. + if (vertexShader) { + glDeleteShader(vertexShader); + } + if (fragmentShader) { + glDeleteShader(fragmentShader); + } + if (!_program) { + return NO; + } + _position = glGetAttribLocation(_program, "position"); + _texcoord = glGetAttribLocation(_program, "texcoord"); + _ySampler = glGetUniformLocation(_program, "s_textureY"); + _uSampler = glGetUniformLocation(_program, "s_textureU"); + _vSampler = glGetUniformLocation(_program, "s_textureV"); + if (_position < 0 || _texcoord < 0 || _ySampler < 0 || _uSampler < 0 || + _vSampler < 0) { + return NO; + } + return YES; +} + +- (BOOL)setupTextures { + glGenTextures(kNumTextures, _textures); + // Set parameters for each of the textures we created. + for (GLsizei i = 0; i < kNumTextures; i++) { + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, _textures[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + return YES; +} + +- (BOOL)updateTextureSizesForFrame:(RTCVideoFrame *)frame { + if (frame.height == _lastDrawnFrame.height && + frame.width == _lastDrawnFrame.width && + frame.chromaWidth == _lastDrawnFrame.chromaWidth && + frame.chromaHeight == _lastDrawnFrame.chromaHeight) { + return YES; + } + GLsizei lumaWidth = frame.width; + GLsizei lumaHeight = frame.height; + GLsizei chromaWidth = frame.chromaWidth; + GLsizei chromaHeight = frame.chromaHeight; + for (GLint i = 0; i < kNumTextureSets; i++) { + glActiveTexture(GL_TEXTURE0 + i * 3); + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + lumaWidth, + lumaHeight, + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + 0); + glActiveTexture(GL_TEXTURE0 + i * 3 + 1); + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + chromaWidth, + chromaHeight, + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + 0); + glActiveTexture(GL_TEXTURE0 + i * 3 + 2); + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + chromaWidth, + chromaHeight, + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + 0); + } + if ((NSUInteger)frame.yPitch != frame.width || + (NSUInteger)frame.uPitch != frame.chromaWidth || + (NSUInteger)frame.vPitch != frame.chromaWidth) { + _planeBuffer.reset(new uint8_t[frame.width * frame.height]); + } else { + _planeBuffer.reset(); + } + return YES; +} + +- (void)uploadPlane:(const uint8_t *)plane + sampler:(GLint)sampler + offset:(NSUInteger)offset + width:(size_t)width + height:(size_t)height + stride:(int32_t)stride { + glActiveTexture(GL_TEXTURE0 + offset); + // When setting texture sampler uniforms, the texture index is used not + // the texture handle. + glUniform1i(sampler, offset); +#if TARGET_OS_IPHONE + BOOL hasUnpackRowLength = _context.API == kEAGLRenderingAPIOpenGLES3; +#else + BOOL hasUnpackRowLength = YES; +#endif + const uint8_t *uploadPlane = plane; + if ((size_t)stride != width) { + if (hasUnpackRowLength) { + // GLES3 allows us to specify stride. + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + width, + height, + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + uploadPlane); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + return; + } else { + // Make an unpadded copy and upload that instead. Quick profiling showed + // that this is faster than uploading row by row using glTexSubImage2D. + uint8_t *unpaddedPlane = _planeBuffer.get(); + for (size_t y = 0; y < height; ++y) { + memcpy(unpaddedPlane + y * width, plane + y * stride, width); + } + uploadPlane = unpaddedPlane; + } + } + glTexImage2D(GL_TEXTURE_2D, + 0, + RTC_PIXEL_FORMAT, + width, + height, + 0, + RTC_PIXEL_FORMAT, + GL_UNSIGNED_BYTE, + uploadPlane); +} + +- (BOOL)updateTextureDataForFrame:(RTCVideoFrame *)frame { + NSUInteger textureOffset = _currentTextureSet * 3; + NSAssert(textureOffset + 3 <= kNumTextures, @"invalid offset"); + + [self uploadPlane:frame.yPlane + sampler:_ySampler + offset:textureOffset + width:frame.width + height:frame.height + stride:frame.yPitch]; + + [self uploadPlane:frame.uPlane + sampler:_uSampler + offset:textureOffset + 1 + width:frame.chromaWidth + height:frame.chromaHeight + stride:frame.uPitch]; + + [self uploadPlane:frame.vPlane + sampler:_vSampler + offset:textureOffset + 2 + width:frame.chromaWidth + height:frame.chromaHeight + stride:frame.vPitch]; + + _currentTextureSet = (_currentTextureSet + 1) % kNumTextureSets; + return YES; +} + +- (BOOL)setupVertices { +#if !TARGET_OS_IPHONE + NSAssert(!_vertexArray, @"vertex array already set up"); + glGenVertexArrays(1, &_vertexArray); + if (!_vertexArray) { + return NO; + } + glBindVertexArray(_vertexArray); +#endif + NSAssert(!_vertexBuffer, @"vertex buffer already set up"); + glGenBuffers(1, &_vertexBuffer); + if (!_vertexBuffer) { +#if !TARGET_OS_IPHONE + glDeleteVertexArrays(1, &_vertexArray); + _vertexArray = 0; +#endif + return NO; + } + glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(gVertices), gVertices, GL_DYNAMIC_DRAW); + + // Read position attribute from |gVertices| with size of 2 and stride of 4 + // beginning at the start of the array. The last argument indicates offset + // of data within |gVertices| as supplied to the vertex buffer. + glVertexAttribPointer( + _position, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void *)0); + glEnableVertexAttribArray(_position); + + // Read texcoord attribute from |gVertices| with size of 2 and stride of 4 + // beginning at the first texcoord in the array. The last argument indicates + // offset of data within |gVertices| as supplied to the vertex buffer. + glVertexAttribPointer(_texcoord, + 2, + GL_FLOAT, + GL_FALSE, + 4 * sizeof(GLfloat), + (void *)(2 * sizeof(GLfloat))); + glEnableVertexAttribArray(_texcoord); + + return YES; +} + +@end diff --git a/webrtc/api/objc/RTCSessionDescription+Private.h b/webrtc/api/objc/RTCSessionDescription+Private.h new file mode 100644 index 0000000000..aa0314d3f3 --- /dev/null +++ b/webrtc/api/objc/RTCSessionDescription+Private.h @@ -0,0 +1,41 @@ +/* + * Copyright 2015 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 "RTCSessionDescription.h" + +#include "talk/app/webrtc/jsep.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCSessionDescription () + +/** + * The native SessionDescriptionInterface representation of this + * RTCSessionDescription object. This is needed to pass to the underlying C++ + * APIs. + */ +@property(nonatomic, readonly) + webrtc::SessionDescriptionInterface *nativeDescription; + +/** + * Initialize an RTCSessionDescription from a native + * SessionDescriptionInterface. No ownership is taken of the native session + * description. + */ +- (instancetype)initWithNativeDescription: + (webrtc::SessionDescriptionInterface *)nativeDescription; + ++ (std::string)stringForType:(RTCSdpType)type; + ++ (RTCSdpType)typeForString:(const std::string &)string; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCSessionDescription.h b/webrtc/api/objc/RTCSessionDescription.h new file mode 100644 index 0000000000..5f00b1c9f4 --- /dev/null +++ b/webrtc/api/objc/RTCSessionDescription.h @@ -0,0 +1,41 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +/** + * Represents the session description type. This exposes the same types that are + * in C++, which doesn't include the rollback type that is in the W3C spec. + */ +typedef NS_ENUM(NSInteger, RTCSdpType) { + RTCSdpTypeOffer, + RTCSdpTypePrAnswer, + RTCSdpTypeAnswer, +}; + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCSessionDescription : NSObject + +/** The type of session description. */ +@property(nonatomic, readonly) RTCSdpType type; + +/** The SDP string representation of this session description. */ +@property(nonatomic, readonly) NSString *sdp; + +- (instancetype)init NS_UNAVAILABLE; + +/** Initialize a session description with a type and SDP string. */ +- (instancetype)initWithType:(RTCSdpType)type sdp:(NSString *)sdp + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCSessionDescription.mm b/webrtc/api/objc/RTCSessionDescription.mm new file mode 100644 index 0000000000..7ed0760158 --- /dev/null +++ b/webrtc/api/objc/RTCSessionDescription.mm @@ -0,0 +1,92 @@ +/* + * Copyright 2015 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 "RTCSessionDescription.h" + +#include "webrtc/base/checks.h" + +#import "webrtc/api/objc/RTCSessionDescription+Private.h" +#import "webrtc/base/objc/NSString+StdString.h" +#import "webrtc/base/objc/RTCLogging.h" + +@implementation RTCSessionDescription + +@synthesize type = _type; +@synthesize sdp = _sdp; + +- (instancetype)initWithType:(RTCSdpType)type sdp:(NSString *)sdp { + NSParameterAssert(sdp.length); + if (self = [super init]) { + _type = type; + _sdp = [sdp copy]; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"RTCSessionDescription:\n%s\n%@", + [[self class] stringForType:_type].c_str(), + _sdp]; +} + +#pragma mark - Private + +- (webrtc::SessionDescriptionInterface *)nativeDescription { + webrtc::SdpParseError error; + + webrtc::SessionDescriptionInterface *description = + webrtc::CreateSessionDescription([[self class] stringForType:_type], + _sdp.stdString, + &error); + + if (!description) { + RTCLogError(@"Failed to create session description: %s\nline: %s", + error.description.c_str(), + error.line.c_str()); + } + + return description; +} + +- (instancetype)initWithNativeDescription: + (webrtc::SessionDescriptionInterface *)nativeDescription { + NSParameterAssert(nativeDescription); + std::string sdp; + nativeDescription->ToString(&sdp); + RTCSdpType type = [[self class] typeForString:nativeDescription->type()]; + + return [self initWithType:type + sdp:[NSString stringForStdString:sdp]]; +} + ++ (std::string)stringForType:(RTCSdpType)type { + switch (type) { + case RTCSdpTypeOffer: + return webrtc::SessionDescriptionInterface::kOffer; + case RTCSdpTypePrAnswer: + return webrtc::SessionDescriptionInterface::kPrAnswer; + case RTCSdpTypeAnswer: + return webrtc::SessionDescriptionInterface::kAnswer; + } +} + ++ (RTCSdpType)typeForString:(const std::string &)string { + if (string == webrtc::SessionDescriptionInterface::kOffer) { + return RTCSdpTypeOffer; + } else if (string == webrtc::SessionDescriptionInterface::kPrAnswer) { + return RTCSdpTypePrAnswer; + } else if (string == webrtc::SessionDescriptionInterface::kAnswer) { + return RTCSdpTypeAnswer; + } else { + RTC_NOTREACHED(); + } +} + +@end diff --git a/webrtc/api/objc/RTCStatsReport+Private.h b/webrtc/api/objc/RTCStatsReport+Private.h new file mode 100644 index 0000000000..5b7dc32a74 --- /dev/null +++ b/webrtc/api/objc/RTCStatsReport+Private.h @@ -0,0 +1,24 @@ +/* + * Copyright 2015 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 "RTCStatsReport.h" + +#include "talk/app/webrtc/statstypes.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCStatsReport () + +/** Initialize an RTCStatsReport object from a native StatsReport. */ +- (instancetype)initWithNativeReport:(const webrtc::StatsReport &)nativeReport; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCStatsReport.h b/webrtc/api/objc/RTCStatsReport.h new file mode 100644 index 0000000000..fc66faf2cf --- /dev/null +++ b/webrtc/api/objc/RTCStatsReport.h @@ -0,0 +1,34 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** This does not currently conform to the spec. */ +@interface RTCStatsReport : NSObject + +/** Time since 1970-01-01T00:00:00Z in milliseconds. */ +@property(nonatomic, readonly) CFTimeInterval timestamp; + +/** The type of stats held by this object. */ +@property(nonatomic, readonly) NSString *type; + +/** The identifier for this object. */ +@property(nonatomic, readonly) NSString *statsId; + +/** A dictionary holding the actual stats. */ +@property(nonatomic, readonly) NSDictionary<NSString *, NSString *> *values; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCStatsReport.mm b/webrtc/api/objc/RTCStatsReport.mm new file mode 100644 index 0000000000..35a5229014 --- /dev/null +++ b/webrtc/api/objc/RTCStatsReport.mm @@ -0,0 +1,62 @@ +/* + * Copyright 2015 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 "RTCStatsReport.h" + +#include "webrtc/base/checks.h" + +#import "webrtc/api/objc/RTCStatsReport+Private.h" +#import "webrtc/base/objc/NSString+StdString.h" +#import "webrtc/base/objc/RTCLogging.h" + +@implementation RTCStatsReport + +@synthesize timestamp = _timestamp; +@synthesize type = _type; +@synthesize statsId = _statsId; +@synthesize values = _values; + +- (NSString *)description { + return [NSString stringWithFormat:@"RTCStatsReport:\n%@\n%@\n%f\n%@", + _statsId, + _type, + _timestamp, + _values]; +} + +#pragma mark - Private + +- (instancetype)initWithNativeReport:(const webrtc::StatsReport &)nativeReport { + if (self = [super init]) { + _timestamp = nativeReport.timestamp(); + _type = [NSString stringForStdString:nativeReport.TypeToString()]; + _statsId = [NSString stringForStdString: + nativeReport.id()->ToString()]; + + NSUInteger capacity = nativeReport.values().size(); + NSMutableDictionary *values = + [NSMutableDictionary dictionaryWithCapacity:capacity]; + for (auto const &valuePair : nativeReport.values()) { + NSString *key = [NSString stringForStdString: + valuePair.second->display_name()]; + NSString *value = [NSString stringForStdString: + valuePair.second->ToString()]; + + // Not expecting duplicate keys. + RTC_DCHECK(values[key]); + + values[key] = value; + } + _values = values; + } + return self; +} + +@end diff --git a/webrtc/api/objc/RTCVideoFrame+Private.h b/webrtc/api/objc/RTCVideoFrame+Private.h new file mode 100644 index 0000000000..954344aee1 --- /dev/null +++ b/webrtc/api/objc/RTCVideoFrame+Private.h @@ -0,0 +1,24 @@ +/* + * Copyright 2015 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 "RTCVideoFrame.h" + +#include "talk/media/base/videoframe.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCVideoFrame () + +- (instancetype)initWithNativeFrame:(const cricket::VideoFrame *)nativeFrame + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCVideoFrame.h b/webrtc/api/objc/RTCVideoFrame.h new file mode 100644 index 0000000000..8ed23ba82c --- /dev/null +++ b/webrtc/api/objc/RTCVideoFrame.h @@ -0,0 +1,37 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface RTCVideoFrame : NSObject + +/** Width without rotation applied. */ +@property(nonatomic, readonly) size_t width; + +/** Height without rotation applied. */ +@property(nonatomic, readonly) size_t height; +@property(nonatomic, readonly) size_t chromaWidth; +@property(nonatomic, readonly) size_t chromaHeight; +@property(nonatomic, readonly) size_t chromaSize; +// These can return NULL if the object is not backed by a buffer. +@property(nonatomic, readonly, nullable) const uint8_t *yPlane; +@property(nonatomic, readonly, nullable) const uint8_t *uPlane; +@property(nonatomic, readonly, nullable) const uint8_t *vPlane; +@property(nonatomic, readonly) int32_t yPitch; +@property(nonatomic, readonly) int32_t uPitch; +@property(nonatomic, readonly) int32_t vPitch; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/RTCVideoFrame.mm b/webrtc/api/objc/RTCVideoFrame.mm new file mode 100644 index 0000000000..db2d07ba31 --- /dev/null +++ b/webrtc/api/objc/RTCVideoFrame.mm @@ -0,0 +1,79 @@ +/* + * Copyright 2015 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 "RTCVideoFrame.h" + +#include "webrtc/base/scoped_ptr.h" + +#import "webrtc/api/objc/RTCVideoFrame+Private.h" + +@implementation RTCVideoFrame { + rtc::scoped_ptr<cricket::VideoFrame> _videoFrame; +} + +- (size_t)width { + return _videoFrame->GetWidth(); +} + +- (size_t)height { + return _videoFrame->GetHeight(); +} + +- (size_t)chromaWidth { + return _videoFrame->GetChromaWidth(); +} + +- (size_t)chromaHeight { + return _videoFrame->GetChromaHeight(); +} + +- (size_t)chromaSize { + return _videoFrame->GetChromaSize(); +} + +- (const uint8_t *)yPlane { + const cricket::VideoFrame *const_frame = _videoFrame.get(); + return const_frame->GetYPlane(); +} + +- (const uint8_t *)uPlane { + const cricket::VideoFrame *const_frame = _videoFrame.get(); + return const_frame->GetUPlane(); +} + +- (const uint8_t *)vPlane { + const cricket::VideoFrame *const_frame = _videoFrame.get(); + return const_frame->GetVPlane(); +} + +- (int32_t)yPitch { + return _videoFrame->GetYPitch(); +} + +- (int32_t)uPitch { + return _videoFrame->GetUPitch(); +} + +- (int32_t)vPitch { + return _videoFrame->GetVPitch(); +} + +#pragma mark - Private + +- (instancetype)initWithNativeFrame:(const cricket::VideoFrame *)nativeFrame { + if (self = [super init]) { + // Keep a shallow copy of the video frame. The underlying frame buffer is + // not copied. + _videoFrame.reset(nativeFrame->Copy()); + } + return self; +} + +@end diff --git a/webrtc/api/objc/RTCVideoRenderer.h b/webrtc/api/objc/RTCVideoRenderer.h new file mode 100644 index 0000000000..a97456275a --- /dev/null +++ b/webrtc/api/objc/RTCVideoRenderer.h @@ -0,0 +1,30 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> +#if TARGET_OS_IPHONE +#import <UIKit/UIKit.h> +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class RTCVideoFrame; + +@protocol RTCVideoRenderer <NSObject> + +/** The size of the frame. */ +- (void)setSize:(CGSize)size; + +/** The frame to be displayed. */ +- (void)renderFrame:(RTCVideoFrame *)frame; + +@end + +NS_ASSUME_NONNULL_END diff --git a/webrtc/api/objc/WebRTC-Prefix.pch b/webrtc/api/objc/WebRTC-Prefix.pch new file mode 100644 index 0000000000..990b1602da --- /dev/null +++ b/webrtc/api/objc/WebRTC-Prefix.pch @@ -0,0 +1,13 @@ +/* + * Copyright 2015 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. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif diff --git a/webrtc/api/objctests/RTCIceCandidateTest.mm b/webrtc/api/objctests/RTCIceCandidateTest.mm new file mode 100644 index 0000000000..391db44ae1 --- /dev/null +++ b/webrtc/api/objctests/RTCIceCandidateTest.mm @@ -0,0 +1,74 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +#include "webrtc/base/gunit.h" + +#import "webrtc/api/objc/RTCIceCandidate.h" +#import "webrtc/api/objc/RTCIceCandidate+Private.h" +#import "webrtc/base/objc/NSString+StdString.h" + +@interface RTCIceCandidateTest : NSObject +- (void)testCandidate; +- (void)testInitFromNativeCandidate; +@end + +@implementation RTCIceCandidateTest + +- (void)testCandidate { + NSString *sdp = @"candidate:4025901590 1 udp 2122265343 " + "fdff:2642:12a6:fe38:c001:beda:fcf9:51aa " + "59052 typ host generation 0"; + + RTCIceCandidate *candidate = [[RTCIceCandidate alloc] initWithSdp:sdp + sdpMLineIndex:0 + sdpMid:@"audio"]; + + rtc::scoped_ptr<webrtc::IceCandidateInterface> nativeCandidate = + candidate.nativeCandidate; + EXPECT_EQ("audio", nativeCandidate->sdp_mid()); + EXPECT_EQ(0, nativeCandidate->sdp_mline_index()); + + std::string sdpString; + nativeCandidate->ToString(&sdpString); + EXPECT_EQ(sdp.stdString, sdpString); +} + +- (void)testInitFromNativeCandidate { + std::string sdp("candidate:4025901590 1 udp 2122265343 " + "fdff:2642:12a6:fe38:c001:beda:fcf9:51aa " + "59052 typ host generation 0"); + webrtc::IceCandidateInterface *nativeCandidate = + webrtc::CreateIceCandidate("audio", 0, sdp, nullptr); + + RTCIceCandidate *iceCandidate = + [[RTCIceCandidate alloc] initWithNativeCandidate:nativeCandidate]; + EXPECT_TRUE([@"audio" isEqualToString:iceCandidate.sdpMid]); + EXPECT_EQ(0, iceCandidate.sdpMLineIndex); + + EXPECT_EQ(sdp, iceCandidate.sdp.stdString); +} + +@end + +TEST(RTCIceCandidateTest, CandidateTest) { + @autoreleasepool { + RTCIceCandidateTest *test = [[RTCIceCandidateTest alloc] init]; + [test testCandidate]; + } +} + +TEST(RTCIceCandidateTest, InitFromCandidateTest) { + @autoreleasepool { + RTCIceCandidateTest *test = [[RTCIceCandidateTest alloc] init]; + [test testInitFromNativeCandidate]; + } +} diff --git a/webrtc/api/objctests/RTCIceServerTest.mm b/webrtc/api/objctests/RTCIceServerTest.mm new file mode 100644 index 0000000000..5fa43f8447 --- /dev/null +++ b/webrtc/api/objctests/RTCIceServerTest.mm @@ -0,0 +1,84 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +#include <vector> + +#include "webrtc/base/gunit.h" + +#import "webrtc/api/objc/RTCIceServer.h" +#import "webrtc/api/objc/RTCIceServer+Private.h" + +@interface RTCIceServerTest : NSObject +- (void)testOneURLServer; +- (void)testTwoURLServer; +- (void)testPasswordCredential; +@end + +@implementation RTCIceServerTest + +- (void)testOneURLServer { + RTCIceServer *server = [[RTCIceServer alloc] initWithURLStrings:@[ + @"stun:stun1.example.net" ]]; + + webrtc::PeerConnectionInterface::IceServer iceStruct = server.iceServer; + EXPECT_EQ((size_t)1, iceStruct.urls.size()); + EXPECT_EQ("stun:stun1.example.net", iceStruct.urls.front()); + EXPECT_EQ("", iceStruct.username); + EXPECT_EQ("", iceStruct.password); +} + +- (void)testTwoURLServer { + RTCIceServer *server = [[RTCIceServer alloc] initWithURLStrings:@[ + @"turn1:turn1.example.net", @"turn2:turn2.example.net" ]]; + + webrtc::PeerConnectionInterface::IceServer iceStruct = server.iceServer; + EXPECT_EQ((size_t)2, iceStruct.urls.size()); + EXPECT_EQ("turn1:turn1.example.net", iceStruct.urls.front()); + EXPECT_EQ("turn2:turn2.example.net", iceStruct.urls.back()); + EXPECT_EQ("", iceStruct.username); + EXPECT_EQ("", iceStruct.password); +} + +- (void)testPasswordCredential { + RTCIceServer *server = [[RTCIceServer alloc] + initWithURLStrings:@[ @"turn1:turn1.example.net" ] + username:@"username" + credential:@"credential"]; + webrtc::PeerConnectionInterface::IceServer iceStruct = server.iceServer; + EXPECT_EQ((size_t)1, iceStruct.urls.size()); + EXPECT_EQ("turn1:turn1.example.net", iceStruct.urls.front()); + EXPECT_EQ("username", iceStruct.username); + EXPECT_EQ("credential", iceStruct.password); +} + +@end + +TEST(RTCIceServerTest, OneURLTest) { + @autoreleasepool { + RTCIceServerTest *test = [[RTCIceServerTest alloc] init]; + [test testOneURLServer]; + } +} + +TEST(RTCIceServerTest, TwoURLTest) { + @autoreleasepool { + RTCIceServerTest *test = [[RTCIceServerTest alloc] init]; + [test testTwoURLServer]; + } +} + +TEST(RTCIceServerTest, PasswordCredentialTest) { + @autoreleasepool { + RTCIceServerTest *test = [[RTCIceServerTest alloc] init]; + [test testPasswordCredential]; + } +} diff --git a/webrtc/api/objctests/RTCMediaConstraintsTest.mm b/webrtc/api/objctests/RTCMediaConstraintsTest.mm new file mode 100644 index 0000000000..44ffe3d033 --- /dev/null +++ b/webrtc/api/objctests/RTCMediaConstraintsTest.mm @@ -0,0 +1,66 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +#include "webrtc/base/gunit.h" + +#import "webrtc/api/objc/RTCMediaConstraints.h" +#import "webrtc/api/objc/RTCMediaConstraints+Private.h" +#import "webrtc/base/objc/NSString+StdString.h" + +@interface RTCMediaConstraintsTest : NSObject +- (void)testMediaConstraints; +@end + +@implementation RTCMediaConstraintsTest + +- (void)testMediaConstraints { + NSDictionary *mandatory = @{@"key1": @"value1", @"key2": @"value2"}; + NSDictionary *optional = @{@"key3": @"value3", @"key4": @"value4"}; + + RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] + initWithMandatoryConstraints:mandatory + optionalConstraints:optional]; + rtc::scoped_ptr<webrtc::MediaConstraints> nativeConstraints = + [constraints nativeConstraints]; + + webrtc::MediaConstraintsInterface::Constraints nativeMandatory = + nativeConstraints->GetMandatory(); + [self expectConstraints:mandatory inNativeConstraints:nativeMandatory]; + + webrtc::MediaConstraintsInterface::Constraints nativeOptional = + nativeConstraints->GetOptional(); + [self expectConstraints:optional inNativeConstraints:nativeOptional]; +} + +- (void)expectConstraints:(NSDictionary *)constraints + inNativeConstraints: + (webrtc::MediaConstraintsInterface::Constraints)nativeConstraints { + EXPECT_EQ(constraints.count, nativeConstraints.size()); + + for (NSString *key in constraints) { + NSString *value = constraints[key]; + + std::string nativeValue; + bool found = nativeConstraints.FindFirst(key.stdString, &nativeValue); + EXPECT_TRUE(found); + EXPECT_EQ(value.stdString, nativeValue); + } +} + +@end + +TEST(RTCMediaConstraintsTest, MediaConstraintsTest) { + @autoreleasepool { + RTCMediaConstraintsTest *test = [[RTCMediaConstraintsTest alloc] init]; + [test testMediaConstraints]; + } +} diff --git a/webrtc/api/objctests/RTCSessionDescriptionTest.mm b/webrtc/api/objctests/RTCSessionDescriptionTest.mm new file mode 100644 index 0000000000..2404dedd3a --- /dev/null +++ b/webrtc/api/objctests/RTCSessionDescriptionTest.mm @@ -0,0 +1,144 @@ +/* + * Copyright 2015 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 <Foundation/Foundation.h> + +#include "webrtc/base/gunit.h" + +#import "webrtc/api/objc/RTCSessionDescription.h" +#import "webrtc/api/objc/RTCSessionDescription+Private.h" +#import "webrtc/base/objc/NSString+StdString.h" + +@interface RTCSessionDescriptionTest : NSObject +- (void)testSessionDescriptionConversion; +- (void)testInitFromNativeSessionDescription; +@end + +@implementation RTCSessionDescriptionTest + +/** + * Test conversion of an Objective-C RTCSessionDescription to a native + * SessionDescriptionInterface (based on the types and SDP strings being equal). + */ +- (void)testSessionDescriptionConversion { + RTCSessionDescription *description = + [[RTCSessionDescription alloc] initWithType:RTCSdpTypeAnswer + sdp:[self sdp]]; + + webrtc::SessionDescriptionInterface *nativeDescription = + description.nativeDescription; + + EXPECT_EQ(RTCSdpTypeAnswer, + [RTCSessionDescription typeForString:nativeDescription->type()]); + + std::string sdp; + nativeDescription->ToString(&sdp); + EXPECT_EQ([self sdp].stdString, sdp); +} + +- (void)testInitFromNativeSessionDescription { + webrtc::SessionDescriptionInterface *nativeDescription; + + nativeDescription = webrtc::CreateSessionDescription( + webrtc::SessionDescriptionInterface::kAnswer, + [self sdp].stdString, + nullptr); + + RTCSessionDescription *description = + [[RTCSessionDescription alloc] initWithNativeDescription: + nativeDescription]; + EXPECT_EQ(webrtc::SessionDescriptionInterface::kAnswer, + [RTCSessionDescription stringForType:description.type]); + EXPECT_TRUE([[self sdp] isEqualToString:description.sdp]); +} + +- (NSString *)sdp { + return @"v=0\r\n" + "o=- 5319989746393411314 2 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "a=group:BUNDLE audio video\r\n" + "a=msid-semantic: WMS ARDAMS\r\n" + "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 9 0 8 126\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=ice-ufrag:f3o+0HG7l9nwIWFY\r\n" + "a=ice-pwd:VDctmJNCptR2TB7+meDpw7w5\r\n" + "a=fingerprint:sha-256 A9:D5:8D:A8:69:22:39:60:92:AD:94:1A:22:2D:5E:" + "A5:4A:A9:18:C2:35:5D:46:5E:59:BD:1C:AF:38:9F:E6:E1\r\n" + "a=setup:active\r\n" + "a=mid:audio\r\n" + "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" + "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/" + "abs-send-time\r\n" + "a=sendrecv\r\n" + "a=rtcp-mux\r\n" + "a=rtpmap:111 opus/48000/2\r\n" + "a=fmtp:111 minptime=10; useinbandfec=1\r\n" + "a=rtpmap:103 ISAC/16000\r\n" + "a=rtpmap:9 G722/8000\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:126 telephone-event/8000\r\n" + "a=maxptime:60\r\n" + "a=ssrc:1504474588 cname:V+FdIC5AJpxLhdYQ\r\n" + "a=ssrc:1504474588 msid:ARDAMS ARDAMSa0\r\n" + "a=ssrc:1504474588 mslabel:ARDAMS\r\n" + "a=ssrc:1504474588 label:ARDAMSa0\r\n" + "m=video 9 UDP/TLS/RTP/SAVPF 100 116 117 96\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=ice-ufrag:f3o+0HG7l9nwIWFY\r\n" + "a=ice-pwd:VDctmJNCptR2TB7+meDpw7w5\r\n" + "a=fingerprint:sha-256 A9:D5:8D:A8:69:22:39:60:92:AD:94:1A:22:2D:5E:" + "A5:4A:A9:18:C2:35:5D:46:5E:59:BD:1C:AF:38:9F:E6:E1\r\n" + "a=setup:active\r\n" + "a=mid:video\r\n" + "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n" + "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/" + "abs-send-time\r\n" + "a=extmap:4 urn:3gpp:video-orientation\r\n" + "a=sendrecv\r\n" + "a=rtcp-mux\r\n" + "a=rtpmap:100 VP8/90000\r\n" + "a=rtcp-fb:100 ccm fir\r\n" + "a=rtcp-fb:100 nack\r\n" + "a=rtcp-fb:100 nack pli\r\n" + "a=rtcp-fb:100 goog-remb\r\n" + "a=rtpmap:116 red/90000\r\n" + "a=rtpmap:117 ulpfec/90000\r\n" + "a=rtpmap:96 rtx/90000\r\n" + "a=fmtp:96 apt=100\r\n" + "a=ssrc-group:FID 498297514 1644357692\r\n" + "a=ssrc:498297514 cname:V+FdIC5AJpxLhdYQ\r\n" + "a=ssrc:498297514 msid:ARDAMS ARDAMSv0\r\n" + "a=ssrc:498297514 mslabel:ARDAMS\r\n" + "a=ssrc:498297514 label:ARDAMSv0\r\n" + "a=ssrc:1644357692 cname:V+FdIC5AJpxLhdYQ\r\n" + "a=ssrc:1644357692 msid:ARDAMS ARDAMSv0\r\n" + "a=ssrc:1644357692 mslabel:ARDAMS\r\n" + "a=ssrc:1644357692 label:ARDAMSv0\r\n"; +} + +@end + +TEST(RTCSessionDescriptionTest, SessionDescriptionConversionTest) { + @autoreleasepool { + RTCSessionDescriptionTest *test = [[RTCSessionDescriptionTest alloc] init]; + [test testSessionDescriptionConversion]; + } +} + +TEST(RTCSessionDescriptionTest, InitFromSessionDescriptionTest) { + @autoreleasepool { + RTCSessionDescriptionTest *test = [[RTCSessionDescriptionTest alloc] init]; + [test testInitFromNativeSessionDescription]; + } +} |