1/* 2 * Copyright 2014 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#import "APPRTCViewController.h" 12 13#import <AVFoundation/AVFoundation.h> 14#import "ARDAppClient.h" 15#import "RTCNSGLVideoView.h" 16#import "RTCVideoTrack.h" 17 18static NSUInteger const kContentWidth = 1280; 19static NSUInteger const kContentHeight = 720; 20static NSUInteger const kRoomFieldWidth = 80; 21static NSUInteger const kLogViewHeight = 280; 22 23@class APPRTCMainView; 24@protocol APPRTCMainViewDelegate 25 26- (void)appRTCMainView:(APPRTCMainView*)mainView 27 didEnterRoomId:(NSString*)roomId; 28 29@end 30 31@interface APPRTCMainView : NSView 32 33@property(nonatomic, weak) id<APPRTCMainViewDelegate> delegate; 34@property(nonatomic, readonly) RTCNSGLVideoView* localVideoView; 35@property(nonatomic, readonly) RTCNSGLVideoView* remoteVideoView; 36 37- (void)displayLogMessage:(NSString*)message; 38 39@end 40 41@interface APPRTCMainView () <NSTextFieldDelegate, RTCNSGLVideoViewDelegate> 42@end 43@implementation APPRTCMainView { 44 NSScrollView* _scrollView; 45 NSTextField* _roomLabel; 46 NSTextField* _roomField; 47 NSTextView* _logView; 48 RTCNSGLVideoView* _localVideoView; 49 RTCNSGLVideoView* _remoteVideoView; 50 CGSize _localVideoSize; 51 CGSize _remoteVideoSize; 52} 53 54+ (BOOL)requiresConstraintBasedLayout { 55 return YES; 56} 57 58- (instancetype)initWithFrame:(NSRect)frame { 59 if (self = [super initWithFrame:frame]) { 60 [self setupViews]; 61 } 62 return self; 63} 64 65- (void)updateConstraints { 66 NSParameterAssert( 67 _roomField != nil && _scrollView != nil && _remoteVideoView != nil); 68 [self removeConstraints:[self constraints]]; 69 NSDictionary* viewsDictionary = 70 NSDictionaryOfVariableBindings(_roomLabel, 71 _roomField, 72 _scrollView, 73 _remoteVideoView); 74 75 NSSize remoteViewSize = [self remoteVideoViewSize]; 76 NSDictionary* metrics = @{ 77 @"kLogViewHeight" : @(kLogViewHeight), 78 @"kRoomFieldWidth" : @(kRoomFieldWidth), 79 @"remoteViewWidth" : @(remoteViewSize.width), 80 @"remoteViewHeight" : @(remoteViewSize.height), 81 }; 82 // Declare this separately to avoid compiler warning about splitting string 83 // within an NSArray expression. 84 NSString* verticalConstraint = 85 @"V:|-[_roomLabel]-[_roomField]-[_scrollView(kLogViewHeight)]" 86 "-[_remoteVideoView(remoteViewHeight)]-|"; 87 NSArray* constraintFormats = @[ 88 verticalConstraint, 89 @"|-[_roomLabel]", 90 @"|-[_roomField(kRoomFieldWidth)]", 91 @"|-[_scrollView(remoteViewWidth)]-|", 92 @"|-[_remoteVideoView(remoteViewWidth)]-|", 93 ]; 94 for (NSString* constraintFormat in constraintFormats) { 95 NSArray* constraints = 96 [NSLayoutConstraint constraintsWithVisualFormat:constraintFormat 97 options:0 98 metrics:metrics 99 views:viewsDictionary]; 100 for (NSLayoutConstraint* constraint in constraints) { 101 [self addConstraint:constraint]; 102 } 103 } 104 [super updateConstraints]; 105} 106 107- (void)displayLogMessage:(NSString*)message { 108 _logView.string = 109 [NSString stringWithFormat:@"%@%@\n", _logView.string, message]; 110 NSRange range = NSMakeRange([_logView.string length], 0); 111 [_logView scrollRangeToVisible:range]; 112} 113 114#pragma mark - NSControl delegate 115 116- (void)controlTextDidEndEditing:(NSNotification*)notification { 117 NSDictionary* userInfo = [notification userInfo]; 118 NSInteger textMovement = [userInfo[@"NSTextMovement"] intValue]; 119 if (textMovement == NSReturnTextMovement) { 120 [self.delegate appRTCMainView:self didEnterRoomId:_roomField.stringValue]; 121 } 122} 123 124#pragma mark - RTCNSGLVideoViewDelegate 125 126- (void)videoView:(RTCNSGLVideoView*)videoView 127 didChangeVideoSize:(NSSize)size { 128 if (videoView == _remoteVideoView) { 129 _remoteVideoSize = size; 130 } else if (videoView == _localVideoView) { 131 _localVideoSize = size; 132 } else { 133 return; 134 } 135 [self setNeedsUpdateConstraints:YES]; 136} 137 138#pragma mark - Private 139 140- (void)setupViews { 141 NSParameterAssert([[self subviews] count] == 0); 142 143 _roomLabel = [[NSTextField alloc] initWithFrame:NSZeroRect]; 144 [_roomLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; 145 [_roomLabel setBezeled:NO]; 146 [_roomLabel setDrawsBackground:NO]; 147 [_roomLabel setEditable:NO]; 148 [_roomLabel setStringValue:@"Enter AppRTC room id:"]; 149 [self addSubview:_roomLabel]; 150 151 _roomField = [[NSTextField alloc] initWithFrame:NSZeroRect]; 152 [_roomField setTranslatesAutoresizingMaskIntoConstraints:NO]; 153 [self addSubview:_roomField]; 154 [_roomField setEditable:YES]; 155 [_roomField setDelegate:self]; 156 157 _logView = [[NSTextView alloc] initWithFrame:NSZeroRect]; 158 [_logView setMinSize:NSMakeSize(0, kLogViewHeight)]; 159 [_logView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; 160 [_logView setVerticallyResizable:YES]; 161 [_logView setAutoresizingMask:NSViewWidthSizable]; 162 NSTextContainer* textContainer = [_logView textContainer]; 163 NSSize containerSize = NSMakeSize(kContentWidth, FLT_MAX); 164 [textContainer setContainerSize:containerSize]; 165 [textContainer setWidthTracksTextView:YES]; 166 [_logView setEditable:NO]; 167 168 _scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect]; 169 [_scrollView setTranslatesAutoresizingMaskIntoConstraints:NO]; 170 [_scrollView setHasVerticalScroller:YES]; 171 [_scrollView setDocumentView:_logView]; 172 [self addSubview:_scrollView]; 173 174 NSOpenGLPixelFormatAttribute attributes[] = { 175 NSOpenGLPFADoubleBuffer, 176 NSOpenGLPFADepthSize, 24, 177 NSOpenGLPFAOpenGLProfile, 178 NSOpenGLProfileVersion3_2Core, 179 0 180 }; 181 NSOpenGLPixelFormat* pixelFormat = 182 [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; 183 _remoteVideoView = [[RTCNSGLVideoView alloc] initWithFrame:NSZeroRect 184 pixelFormat:pixelFormat]; 185 [_remoteVideoView setTranslatesAutoresizingMaskIntoConstraints:NO]; 186 _remoteVideoView.delegate = self; 187 [self addSubview:_remoteVideoView]; 188 189 // TODO(tkchin): create local video view. 190 // https://code.google.com/p/webrtc/issues/detail?id=3417. 191} 192 193- (NSSize)remoteVideoViewSize { 194 if (_remoteVideoSize.width > 0 && _remoteVideoSize.height > 0) { 195 return _remoteVideoSize; 196 } else { 197 return NSMakeSize(kContentWidth, kContentHeight); 198 } 199} 200 201- (NSSize)localVideoViewSize { 202 return NSZeroSize; 203} 204 205@end 206 207@interface APPRTCViewController () 208 <ARDAppClientDelegate, APPRTCMainViewDelegate> 209@property(nonatomic, readonly) APPRTCMainView* mainView; 210@end 211 212@implementation APPRTCViewController { 213 ARDAppClient* _client; 214 RTCVideoTrack* _localVideoTrack; 215 RTCVideoTrack* _remoteVideoTrack; 216} 217 218- (void)dealloc { 219 [self disconnect]; 220} 221 222- (void)loadView { 223 APPRTCMainView* view = [[APPRTCMainView alloc] initWithFrame:NSZeroRect]; 224 [view setTranslatesAutoresizingMaskIntoConstraints:NO]; 225 view.delegate = self; 226 self.view = view; 227} 228 229- (void)windowWillClose:(NSNotification*)notification { 230 [self disconnect]; 231} 232 233#pragma mark - ARDAppClientDelegate 234 235- (void)appClient:(ARDAppClient *)client 236 didChangeState:(ARDAppClientState)state { 237 switch (state) { 238 case kARDAppClientStateConnected: 239 NSLog(@"Client connected."); 240 break; 241 case kARDAppClientStateConnecting: 242 NSLog(@"Client connecting."); 243 break; 244 case kARDAppClientStateDisconnected: 245 NSLog(@"Client disconnected."); 246 [self resetUI]; 247 _client = nil; 248 break; 249 } 250} 251 252- (void)appClient:(ARDAppClient *)client 253 didChangeConnectionState:(RTCICEConnectionState)state { 254} 255 256- (void)appClient:(ARDAppClient *)client 257 didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack { 258 _localVideoTrack = localVideoTrack; 259} 260 261- (void)appClient:(ARDAppClient *)client 262 didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack { 263 _remoteVideoTrack = remoteVideoTrack; 264 [_remoteVideoTrack addRenderer:self.mainView.remoteVideoView]; 265} 266 267- (void)appClient:(ARDAppClient *)client 268 didError:(NSError *)error { 269 [self showAlertWithMessage:[NSString stringWithFormat:@"%@", error]]; 270 [self disconnect]; 271} 272 273- (void)appClient:(ARDAppClient *)client 274 didGetStats:(NSArray *)stats { 275} 276 277#pragma mark - APPRTCMainViewDelegate 278 279- (void)appRTCMainView:(APPRTCMainView*)mainView 280 didEnterRoomId:(NSString*)roomId { 281 [_client disconnect]; 282 ARDAppClient *client = [[ARDAppClient alloc] initWithDelegate:self]; 283 [client connectToRoomWithId:roomId isLoopback:NO isAudioOnly:NO]; 284 _client = client; 285} 286 287#pragma mark - Private 288 289- (APPRTCMainView*)mainView { 290 return (APPRTCMainView*)self.view; 291} 292 293- (void)showAlertWithMessage:(NSString*)message { 294 NSAlert* alert = [[NSAlert alloc] init]; 295 [alert setMessageText:message]; 296 [alert runModal]; 297} 298 299- (void)resetUI { 300 [_remoteVideoTrack removeRenderer:self.mainView.remoteVideoView]; 301 _remoteVideoTrack = nil; 302 [self.mainView.remoteVideoView renderFrame:nil]; 303} 304 305- (void)disconnect { 306 [self resetUI]; 307 [_client disconnect]; 308} 309 310@end 311