Home | History | Annotate | Download | only in mac
      1 /*
      2  * libjingle
      3  * Copyright 2014, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #import "APPRTCViewController.h"
     29 
     30 #import <AVFoundation/AVFoundation.h>
     31 #import "APPRTCConnectionManager.h"
     32 #import "RTCNSGLVideoView.h"
     33 
     34 static NSUInteger const kContentWidth = 1280;
     35 static NSUInteger const kContentHeight = 720;
     36 static NSUInteger const kRoomFieldWidth = 80;
     37 static NSUInteger const kLogViewHeight = 280;
     38 
     39 @class APPRTCMainView;
     40 @protocol APPRTCMainViewDelegate
     41 
     42 - (void)appRTCMainView:(APPRTCMainView*)mainView
     43         didEnterRoomId:(NSString*)roomId;
     44 
     45 @end
     46 
     47 @interface APPRTCMainView : NSView
     48 
     49 @property(nonatomic, weak) id<APPRTCMainViewDelegate> delegate;
     50 @property(nonatomic, readonly) RTCNSGLVideoView* localVideoView;
     51 @property(nonatomic, readonly) RTCNSGLVideoView* remoteVideoView;
     52 
     53 - (void)displayLogMessage:(NSString*)message;
     54 
     55 @end
     56 
     57 @interface APPRTCMainView () <NSTextFieldDelegate, RTCNSGLVideoViewDelegate>
     58 @end
     59 @implementation APPRTCMainView  {
     60   NSScrollView* _scrollView;
     61   NSTextField* _roomLabel;
     62   NSTextField* _roomField;
     63   NSTextView* _logView;
     64   RTCNSGLVideoView* _localVideoView;
     65   RTCNSGLVideoView* _remoteVideoView;
     66   CGSize _localVideoSize;
     67   CGSize _remoteVideoSize;
     68 }
     69 
     70 + (BOOL)requiresConstraintBasedLayout {
     71   return YES;
     72 }
     73 
     74 - (instancetype)initWithFrame:(NSRect)frame {
     75   if (self = [super initWithFrame:frame]) {
     76     [self setupViews];
     77   }
     78   return self;
     79 }
     80 
     81 - (void)updateConstraints {
     82   NSParameterAssert(
     83       _roomField != nil && _scrollView != nil && _remoteVideoView != nil);
     84   [self removeConstraints:[self constraints]];
     85   NSDictionary* viewsDictionary =
     86       NSDictionaryOfVariableBindings(_roomLabel,
     87                                      _roomField,
     88                                      _scrollView,
     89                                      _remoteVideoView);
     90 
     91   NSSize remoteViewSize = [self remoteVideoViewSize];
     92   NSDictionary* metrics = @{
     93     @"kLogViewHeight" : @(kLogViewHeight),
     94     @"kRoomFieldWidth" : @(kRoomFieldWidth),
     95     @"remoteViewWidth" : @(remoteViewSize.width),
     96     @"remoteViewHeight" : @(remoteViewSize.height),
     97   };
     98   // Declare this separately to avoid compiler warning about splitting string
     99   // within an NSArray expression.
    100   NSString* verticalConstraint =
    101       @"V:|-[_roomLabel]-[_roomField]-[_scrollView(kLogViewHeight)]"
    102        "-[_remoteVideoView(remoteViewHeight)]-|";
    103   NSArray* constraintFormats = @[
    104       verticalConstraint,
    105       @"|-[_roomLabel]",
    106       @"|-[_roomField(kRoomFieldWidth)]",
    107       @"|-[_scrollView(remoteViewWidth)]-|",
    108       @"|-[_remoteVideoView(remoteViewWidth)]-|",
    109   ];
    110   for (NSString* constraintFormat in constraintFormats) {
    111     NSArray* constraints =
    112         [NSLayoutConstraint constraintsWithVisualFormat:constraintFormat
    113                                                 options:0
    114                                                 metrics:metrics
    115                                                   views:viewsDictionary];
    116     for (NSLayoutConstraint* constraint in constraints) {
    117       [self addConstraint:constraint];
    118     }
    119   }
    120   [super updateConstraints];
    121 }
    122 
    123 - (void)displayLogMessage:(NSString*)message {
    124   _logView.string =
    125       [NSString stringWithFormat:@"%@%@\n", _logView.string, message];
    126   NSRange range = NSMakeRange([_logView.string length], 0);
    127   [_logView scrollRangeToVisible:range];
    128 }
    129 
    130 #pragma mark - NSControl delegate
    131 
    132 - (void)controlTextDidEndEditing:(NSNotification*)notification {
    133   NSDictionary* userInfo = [notification userInfo];
    134   NSInteger textMovement = [userInfo[@"NSTextMovement"] intValue];
    135   if (textMovement == NSReturnTextMovement) {
    136     [self.delegate appRTCMainView:self didEnterRoomId:_roomField.stringValue];
    137   }
    138 }
    139 
    140 #pragma mark - RTCNSGLVideoViewDelegate
    141 
    142 - (void)videoView:(RTCNSGLVideoView*)videoView
    143     didChangeVideoSize:(NSSize)size {
    144   if (videoView == _remoteVideoView) {
    145     _remoteVideoSize = size;
    146   } else if (videoView == _localVideoView) {
    147     _localVideoSize = size;
    148   } else {
    149     return;
    150   }
    151   [self setNeedsUpdateConstraints:YES];
    152 }
    153 
    154 #pragma mark - Private
    155 
    156 - (void)setupViews {
    157   NSParameterAssert([[self subviews] count] == 0);
    158 
    159   _roomLabel = [[NSTextField alloc] initWithFrame:NSZeroRect];
    160   [_roomLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
    161   [_roomLabel setBezeled:NO];
    162   [_roomLabel setDrawsBackground:NO];
    163   [_roomLabel setEditable:NO];
    164   [_roomLabel setStringValue:@"Enter AppRTC room id:"];
    165   [self addSubview:_roomLabel];
    166 
    167   _roomField = [[NSTextField alloc] initWithFrame:NSZeroRect];
    168   [_roomField setTranslatesAutoresizingMaskIntoConstraints:NO];
    169   [self addSubview:_roomField];
    170   [_roomField setEditable:YES];
    171   [_roomField setDelegate:self];
    172 
    173   _logView = [[NSTextView alloc] initWithFrame:NSZeroRect];
    174   [_logView setMinSize:NSMakeSize(0, kLogViewHeight)];
    175   [_logView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
    176   [_logView setVerticallyResizable:YES];
    177   [_logView setAutoresizingMask:NSViewWidthSizable];
    178   NSTextContainer* textContainer = [_logView textContainer];
    179   NSSize containerSize = NSMakeSize(kContentWidth, FLT_MAX);
    180   [textContainer setContainerSize:containerSize];
    181   [textContainer setWidthTracksTextView:YES];
    182   [_logView setEditable:NO];
    183 
    184   _scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect];
    185   [_scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
    186   [_scrollView setHasVerticalScroller:YES];
    187   [_scrollView setDocumentView:_logView];
    188   [self addSubview:_scrollView];
    189 
    190   NSOpenGLPixelFormatAttribute attributes[] = {
    191     NSOpenGLPFADoubleBuffer,
    192     NSOpenGLPFADepthSize, 24,
    193     NSOpenGLPFAOpenGLProfile,
    194     NSOpenGLProfileVersion3_2Core,
    195     0
    196   };
    197   NSOpenGLPixelFormat* pixelFormat =
    198       [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
    199   _remoteVideoView = [[RTCNSGLVideoView alloc] initWithFrame:NSZeroRect
    200                                                  pixelFormat:pixelFormat];
    201   [_remoteVideoView setTranslatesAutoresizingMaskIntoConstraints:NO];
    202   _remoteVideoView.delegate = self;
    203   [self addSubview:_remoteVideoView];
    204 
    205   // TODO(tkchin): create local video view.
    206   // https://code.google.com/p/webrtc/issues/detail?id=3417.
    207 }
    208 
    209 - (NSSize)remoteVideoViewSize {
    210   if (_remoteVideoSize.width > 0 && _remoteVideoSize.height > 0) {
    211     return _remoteVideoSize;
    212   } else {
    213     return NSMakeSize(kContentWidth, kContentHeight);
    214   }
    215 }
    216 
    217 - (NSSize)localVideoViewSize {
    218   return NSZeroSize;
    219 }
    220 
    221 @end
    222 
    223 @interface APPRTCViewController ()
    224     <APPRTCConnectionManagerDelegate, APPRTCMainViewDelegate, APPRTCLogger>
    225 @property(nonatomic, readonly) APPRTCMainView* mainView;
    226 @end
    227 
    228 @implementation APPRTCViewController {
    229   APPRTCConnectionManager* _connectionManager;
    230 }
    231 
    232 - (instancetype)initWithNibName:(NSString*)nibName
    233                          bundle:(NSBundle*)bundle {
    234   if (self = [super initWithNibName:nibName bundle:bundle]) {
    235     _connectionManager =
    236         [[APPRTCConnectionManager alloc] initWithDelegate:self
    237                                                    logger:self];
    238   }
    239   return self;
    240 }
    241 
    242 - (void)dealloc {
    243   [self disconnect];
    244 }
    245 
    246 - (void)loadView {
    247   APPRTCMainView* view = [[APPRTCMainView alloc] initWithFrame:NSZeroRect];
    248   [view setTranslatesAutoresizingMaskIntoConstraints:NO];
    249   view.delegate = self;
    250   self.view = view;
    251 }
    252 
    253 - (void)windowWillClose:(NSNotification*)notification {
    254   [self disconnect];
    255 }
    256 
    257 #pragma mark - APPRTCConnectionManagerDelegate
    258 
    259 - (void)connectionManager:(APPRTCConnectionManager*)manager
    260     didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack {
    261   self.mainView.localVideoView.videoTrack = localVideoTrack;
    262 }
    263 
    264 - (void)connectionManager:(APPRTCConnectionManager*)manager
    265     didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack {
    266   self.mainView.remoteVideoView.videoTrack = remoteVideoTrack;
    267 }
    268 
    269 - (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager {
    270   [self showAlertWithMessage:@"Remote closed connection"];
    271   [self disconnect];
    272 }
    273 
    274 - (void)connectionManager:(APPRTCConnectionManager*)manager
    275       didErrorWithMessage:(NSString*)message {
    276   [self showAlertWithMessage:message];
    277   [self disconnect];
    278 }
    279 
    280 #pragma mark - APPRTCLogger
    281 
    282 - (void)logMessage:(NSString*)message {
    283   [self.mainView displayLogMessage:message];
    284 }
    285 
    286 #pragma mark - APPRTCMainViewDelegate
    287 
    288 - (void)appRTCMainView:(APPRTCMainView*)mainView
    289         didEnterRoomId:(NSString*)roomId {
    290   NSString* urlString =
    291       [NSString stringWithFormat:@"https://apprtc.appspot.com/?r=%@", roomId];
    292   [_connectionManager connectToRoomWithURL:[NSURL URLWithString:urlString]];
    293 }
    294 
    295 #pragma mark - Private
    296 
    297 - (APPRTCMainView*)mainView {
    298   return (APPRTCMainView*)self.view;
    299 }
    300 
    301 - (void)showAlertWithMessage:(NSString*)message {
    302   NSAlert* alert = [[NSAlert alloc] init];
    303   [alert setMessageText:message];
    304   [alert runModal];
    305 }
    306 
    307 - (void)disconnect {
    308   self.mainView.remoteVideoView.videoTrack = nil;
    309   [_connectionManager disconnect];
    310 }
    311 
    312 @end
    313