| /* |
| * libjingle |
| * Copyright 2014 Google Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import "APPRTCViewController.h" |
| |
| #import <AVFoundation/AVFoundation.h> |
| #import "ARDAppClient.h" |
| #import "RTCNSGLVideoView.h" |
| #import "RTCVideoTrack.h" |
| |
| static NSUInteger const kContentWidth = 1280; |
| static NSUInteger const kContentHeight = 720; |
| static NSUInteger const kRoomFieldWidth = 80; |
| static NSUInteger const kLogViewHeight = 280; |
| |
| @class APPRTCMainView; |
| @protocol APPRTCMainViewDelegate |
| |
| - (void)appRTCMainView:(APPRTCMainView*)mainView |
| didEnterRoomId:(NSString*)roomId; |
| |
| @end |
| |
| @interface APPRTCMainView : NSView |
| |
| @property(nonatomic, weak) id<APPRTCMainViewDelegate> delegate; |
| @property(nonatomic, readonly) RTCNSGLVideoView* localVideoView; |
| @property(nonatomic, readonly) RTCNSGLVideoView* remoteVideoView; |
| |
| - (void)displayLogMessage:(NSString*)message; |
| |
| @end |
| |
| @interface APPRTCMainView () <NSTextFieldDelegate, RTCNSGLVideoViewDelegate> |
| @end |
| @implementation APPRTCMainView { |
| NSScrollView* _scrollView; |
| NSTextField* _roomLabel; |
| NSTextField* _roomField; |
| NSTextView* _logView; |
| RTCNSGLVideoView* _localVideoView; |
| RTCNSGLVideoView* _remoteVideoView; |
| CGSize _localVideoSize; |
| CGSize _remoteVideoSize; |
| } |
| |
| + (BOOL)requiresConstraintBasedLayout { |
| return YES; |
| } |
| |
| - (instancetype)initWithFrame:(NSRect)frame { |
| if (self = [super initWithFrame:frame]) { |
| [self setupViews]; |
| } |
| return self; |
| } |
| |
| - (void)updateConstraints { |
| NSParameterAssert( |
| _roomField != nil && _scrollView != nil && _remoteVideoView != nil); |
| [self removeConstraints:[self constraints]]; |
| NSDictionary* viewsDictionary = |
| NSDictionaryOfVariableBindings(_roomLabel, |
| _roomField, |
| _scrollView, |
| _remoteVideoView); |
| |
| NSSize remoteViewSize = [self remoteVideoViewSize]; |
| NSDictionary* metrics = @{ |
| @"kLogViewHeight" : @(kLogViewHeight), |
| @"kRoomFieldWidth" : @(kRoomFieldWidth), |
| @"remoteViewWidth" : @(remoteViewSize.width), |
| @"remoteViewHeight" : @(remoteViewSize.height), |
| }; |
| // Declare this separately to avoid compiler warning about splitting string |
| // within an NSArray expression. |
| NSString* verticalConstraint = |
| @"V:|-[_roomLabel]-[_roomField]-[_scrollView(kLogViewHeight)]" |
| "-[_remoteVideoView(remoteViewHeight)]-|"; |
| NSArray* constraintFormats = @[ |
| verticalConstraint, |
| @"|-[_roomLabel]", |
| @"|-[_roomField(kRoomFieldWidth)]", |
| @"|-[_scrollView(remoteViewWidth)]-|", |
| @"|-[_remoteVideoView(remoteViewWidth)]-|", |
| ]; |
| for (NSString* constraintFormat in constraintFormats) { |
| NSArray* constraints = |
| [NSLayoutConstraint constraintsWithVisualFormat:constraintFormat |
| options:0 |
| metrics:metrics |
| views:viewsDictionary]; |
| for (NSLayoutConstraint* constraint in constraints) { |
| [self addConstraint:constraint]; |
| } |
| } |
| [super updateConstraints]; |
| } |
| |
| - (void)displayLogMessage:(NSString*)message { |
| _logView.string = |
| [NSString stringWithFormat:@"%@%@\n", _logView.string, message]; |
| NSRange range = NSMakeRange([_logView.string length], 0); |
| [_logView scrollRangeToVisible:range]; |
| } |
| |
| #pragma mark - NSControl delegate |
| |
| - (void)controlTextDidEndEditing:(NSNotification*)notification { |
| NSDictionary* userInfo = [notification userInfo]; |
| NSInteger textMovement = [userInfo[@"NSTextMovement"] intValue]; |
| if (textMovement == NSReturnTextMovement) { |
| [self.delegate appRTCMainView:self didEnterRoomId:_roomField.stringValue]; |
| } |
| } |
| |
| #pragma mark - RTCNSGLVideoViewDelegate |
| |
| - (void)videoView:(RTCNSGLVideoView*)videoView |
| didChangeVideoSize:(NSSize)size { |
| if (videoView == _remoteVideoView) { |
| _remoteVideoSize = size; |
| } else if (videoView == _localVideoView) { |
| _localVideoSize = size; |
| } else { |
| return; |
| } |
| [self setNeedsUpdateConstraints:YES]; |
| } |
| |
| #pragma mark - Private |
| |
| - (void)setupViews { |
| NSParameterAssert([[self subviews] count] == 0); |
| |
| _roomLabel = [[NSTextField alloc] initWithFrame:NSZeroRect]; |
| [_roomLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| [_roomLabel setBezeled:NO]; |
| [_roomLabel setDrawsBackground:NO]; |
| [_roomLabel setEditable:NO]; |
| [_roomLabel setStringValue:@"Enter AppRTC room id:"]; |
| [self addSubview:_roomLabel]; |
| |
| _roomField = [[NSTextField alloc] initWithFrame:NSZeroRect]; |
| [_roomField setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| [self addSubview:_roomField]; |
| [_roomField setEditable:YES]; |
| [_roomField setDelegate:self]; |
| |
| _logView = [[NSTextView alloc] initWithFrame:NSZeroRect]; |
| [_logView setMinSize:NSMakeSize(0, kLogViewHeight)]; |
| [_logView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; |
| [_logView setVerticallyResizable:YES]; |
| [_logView setAutoresizingMask:NSViewWidthSizable]; |
| NSTextContainer* textContainer = [_logView textContainer]; |
| NSSize containerSize = NSMakeSize(kContentWidth, FLT_MAX); |
| [textContainer setContainerSize:containerSize]; |
| [textContainer setWidthTracksTextView:YES]; |
| [_logView setEditable:NO]; |
| |
| _scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect]; |
| [_scrollView setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| [_scrollView setHasVerticalScroller:YES]; |
| [_scrollView setDocumentView:_logView]; |
| [self addSubview:_scrollView]; |
| |
| NSOpenGLPixelFormatAttribute attributes[] = { |
| NSOpenGLPFADoubleBuffer, |
| NSOpenGLPFADepthSize, 24, |
| NSOpenGLPFAOpenGLProfile, |
| NSOpenGLProfileVersion3_2Core, |
| 0 |
| }; |
| NSOpenGLPixelFormat* pixelFormat = |
| [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; |
| _remoteVideoView = [[RTCNSGLVideoView alloc] initWithFrame:NSZeroRect |
| pixelFormat:pixelFormat]; |
| [_remoteVideoView setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| _remoteVideoView.delegate = self; |
| [self addSubview:_remoteVideoView]; |
| |
| // TODO(tkchin): create local video view. |
| // https://code.google.com/p/webrtc/issues/detail?id=3417. |
| } |
| |
| - (NSSize)remoteVideoViewSize { |
| if (_remoteVideoSize.width > 0 && _remoteVideoSize.height > 0) { |
| return _remoteVideoSize; |
| } else { |
| return NSMakeSize(kContentWidth, kContentHeight); |
| } |
| } |
| |
| - (NSSize)localVideoViewSize { |
| return NSZeroSize; |
| } |
| |
| @end |
| |
| @interface APPRTCViewController () |
| <ARDAppClientDelegate, APPRTCMainViewDelegate> |
| @property(nonatomic, readonly) APPRTCMainView* mainView; |
| @end |
| |
| @implementation APPRTCViewController { |
| ARDAppClient* _client; |
| RTCVideoTrack* _localVideoTrack; |
| RTCVideoTrack* _remoteVideoTrack; |
| } |
| |
| - (void)dealloc { |
| [self disconnect]; |
| } |
| |
| - (void)loadView { |
| APPRTCMainView* view = [[APPRTCMainView alloc] initWithFrame:NSZeroRect]; |
| [view setTranslatesAutoresizingMaskIntoConstraints:NO]; |
| view.delegate = self; |
| self.view = view; |
| } |
| |
| - (void)windowWillClose:(NSNotification*)notification { |
| [self disconnect]; |
| } |
| |
| #pragma mark - ARDAppClientDelegate |
| |
| - (void)appClient:(ARDAppClient *)client |
| didChangeState:(ARDAppClientState)state { |
| switch (state) { |
| case kARDAppClientStateConnected: |
| NSLog(@"Client connected."); |
| break; |
| case kARDAppClientStateConnecting: |
| NSLog(@"Client connecting."); |
| break; |
| case kARDAppClientStateDisconnected: |
| NSLog(@"Client disconnected."); |
| [self resetUI]; |
| _client = nil; |
| break; |
| } |
| } |
| |
| - (void)appClient:(ARDAppClient *)client |
| didChangeConnectionState:(RTCICEConnectionState)state { |
| } |
| |
| - (void)appClient:(ARDAppClient *)client |
| didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack { |
| _localVideoTrack = localVideoTrack; |
| } |
| |
| - (void)appClient:(ARDAppClient *)client |
| didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack { |
| _remoteVideoTrack = remoteVideoTrack; |
| [_remoteVideoTrack addRenderer:self.mainView.remoteVideoView]; |
| } |
| |
| - (void)appClient:(ARDAppClient *)client |
| didError:(NSError *)error { |
| [self showAlertWithMessage:[NSString stringWithFormat:@"%@", error]]; |
| [self disconnect]; |
| } |
| |
| #pragma mark - APPRTCMainViewDelegate |
| |
| - (void)appRTCMainView:(APPRTCMainView*)mainView |
| didEnterRoomId:(NSString*)roomId { |
| [_client disconnect]; |
| ARDAppClient *client = [[ARDAppClient alloc] initWithDelegate:self]; |
| [client connectToRoomWithId:roomId options:nil]; |
| _client = client; |
| } |
| |
| #pragma mark - Private |
| |
| - (APPRTCMainView*)mainView { |
| return (APPRTCMainView*)self.view; |
| } |
| |
| - (void)showAlertWithMessage:(NSString*)message { |
| NSAlert* alert = [[NSAlert alloc] init]; |
| [alert setMessageText:message]; |
| [alert runModal]; |
| } |
| |
| - (void)resetUI { |
| [_remoteVideoTrack removeRenderer:self.mainView.remoteVideoView]; |
| _remoteVideoTrack = nil; |
| [self.mainView.remoteVideoView renderFrame:nil]; |
| } |
| |
| - (void)disconnect { |
| [self resetUI]; |
| [_client disconnect]; |
| } |
| |
| @end |