blob: 176ea19da826c65261c2157f153473381d9be8a0 [file] [log] [blame]
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +00001/*
Donald E Curtisa8736442015-08-05 15:48:13 -07002 * Copyright 2014 The WebRTC Project Authors. All rights reserved.
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +00003 *
Donald E Curtisa8736442015-08-05 15:48:13 -07004 * 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.
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +00009 */
10
11#import "APPRTCViewController.h"
12
13#import <AVFoundation/AVFoundation.h>
tkchin9eeb6242016-04-27 01:54:20 -070014
15#import "WebRTC/RTCNSGLVideoView.h"
16#import "WebRTC/RTCVideoTrack.h"
hjon79858f82016-03-13 22:08:26 -070017
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000018#import "ARDAppClient.h"
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000019
denicija647915f2016-10-20 01:46:41 -070020static NSUInteger const kContentWidth = 900;
21static NSUInteger const kRoomFieldWidth = 200;
22static NSUInteger const kActionItemHeight = 30;
23static NSUInteger const kBottomViewHeight = 200;
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000024
25@class APPRTCMainView;
26@protocol APPRTCMainViewDelegate
27
28- (void)appRTCMainView:(APPRTCMainView*)mainView
denicija647915f2016-10-20 01:46:41 -070029 didEnterRoomId:(NSString*)roomId
30 loopback:(BOOL)isLoopback;
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000031
32@end
33
34@interface APPRTCMainView : NSView
35
36@property(nonatomic, weak) id<APPRTCMainViewDelegate> delegate;
37@property(nonatomic, readonly) RTCNSGLVideoView* localVideoView;
38@property(nonatomic, readonly) RTCNSGLVideoView* remoteVideoView;
39
40- (void)displayLogMessage:(NSString*)message;
41
42@end
43
44@interface APPRTCMainView () <NSTextFieldDelegate, RTCNSGLVideoViewDelegate>
45@end
46@implementation APPRTCMainView {
47 NSScrollView* _scrollView;
denicija647915f2016-10-20 01:46:41 -070048 NSView* _actionItemsView;
49 NSButton* _connectButton;
50 NSButton* _loopbackButton;
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000051 NSTextField* _roomField;
52 NSTextView* _logView;
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000053 CGSize _localVideoSize;
54 CGSize _remoteVideoSize;
55}
56
tkchin2ddfdba2016-08-07 21:37:45 -070057@synthesize delegate = _delegate;
58@synthesize localVideoView = _localVideoView;
59@synthesize remoteVideoView = _remoteVideoView;
60
denicija647915f2016-10-20 01:46:41 -070061
62- (void)displayLogMessage:(NSString *)message {
63 _logView.string =
64 [NSString stringWithFormat:@"%@%@\n", _logView.string, message];
65 NSRange range = NSMakeRange(_logView.string.length, 0);
66 [_logView scrollRangeToVisible:range];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000067}
68
denicija647915f2016-10-20 01:46:41 -070069#pragma mark - Private
70
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000071- (instancetype)initWithFrame:(NSRect)frame {
72 if (self = [super initWithFrame:frame]) {
73 [self setupViews];
74 }
75 return self;
76}
77
denicija647915f2016-10-20 01:46:41 -070078+ (BOOL)requiresConstraintBasedLayout {
79 return YES;
80}
81
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000082- (void)updateConstraints {
83 NSParameterAssert(
denicija647915f2016-10-20 01:46:41 -070084 _roomField != nil &&
85 _scrollView != nil &&
86 _remoteVideoView != nil &&
87 _localVideoView != nil &&
88 _actionItemsView!= nil &&
89 _connectButton != nil &&
90 _loopbackButton != nil);
91
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000092 [self removeConstraints:[self constraints]];
93 NSDictionary* viewsDictionary =
denicija647915f2016-10-20 01:46:41 -070094 NSDictionaryOfVariableBindings(_roomField,
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000095 _scrollView,
kthelgason314bc5f2016-08-31 10:23:27 -070096 _remoteVideoView,
denicija647915f2016-10-20 01:46:41 -070097 _localVideoView,
98 _actionItemsView,
99 _connectButton,
100 _loopbackButton);
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000101
102 NSSize remoteViewSize = [self remoteVideoViewSize];
103 NSDictionary* metrics = @{
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000104 @"remoteViewWidth" : @(remoteViewSize.width),
105 @"remoteViewHeight" : @(remoteViewSize.height),
denicija647915f2016-10-20 01:46:41 -0700106 @"kBottomViewHeight" : @(kBottomViewHeight),
107 @"localViewHeight" : @(remoteViewSize.height / 3),
108 @"localViewWidth" : @(remoteViewSize.width / 3),
109 @"kRoomFieldWidth" : @(kRoomFieldWidth),
110 @"kActionItemHeight" : @(kActionItemHeight)
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000111 };
112 // Declare this separately to avoid compiler warning about splitting string
113 // within an NSArray expression.
denicija647915f2016-10-20 01:46:41 -0700114 NSString* verticalConstraintLeft =
115 @"V:|-[_remoteVideoView(remoteViewHeight)]-[_scrollView(kBottomViewHeight)]-|";
116 NSString* verticalConstraintRight =
117 @"V:|-[_remoteVideoView(remoteViewHeight)]-[_actionItemsView(kBottomViewHeight)]-|";
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000118 NSArray* constraintFormats = @[
denicija647915f2016-10-20 01:46:41 -0700119 verticalConstraintLeft,
120 verticalConstraintRight,
121 @"H:|-[_remoteVideoView(remoteViewWidth)]-|",
122 @"V:|-[_localVideoView(localViewHeight)]",
123 @"H:|-[_localVideoView(localViewWidth)]",
124 @"H:|-[_scrollView(==_actionItemsView)]-[_actionItemsView]-|"
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000125 ];
denicija647915f2016-10-20 01:46:41 -0700126
127 NSArray* actionItemsConstraints = @[
128 @"H:|-[_roomField(kRoomFieldWidth)]-[_loopbackButton(kRoomFieldWidth)]",
129 @"H:|-[_connectButton(kRoomFieldWidth)]",
130 @"V:|-[_roomField(kActionItemHeight)]-[_connectButton(kActionItemHeight)]",
131 @"V:|-[_loopbackButton(kActionItemHeight)]",
132 ];
133
134 [APPRTCMainView addConstraints:constraintFormats
135 toView:self
136 viewsDictionary:viewsDictionary
137 metrics:metrics];
138 [APPRTCMainView addConstraints:actionItemsConstraints
139 toView:_actionItemsView
140 viewsDictionary:viewsDictionary
141 metrics:metrics];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000142 [super updateConstraints];
143}
144
denicija647915f2016-10-20 01:46:41 -0700145#pragma mark - Constraints helper
146
147+ (void)addConstraints:(NSArray*)constraints toView:(NSView*)view
148 viewsDictionary:(NSDictionary*)viewsDictionary
149 metrics:(NSDictionary*)metrics {
150 for (NSString* constraintFormat in constraints) {
151 NSArray* constraints =
152 [NSLayoutConstraint constraintsWithVisualFormat:constraintFormat
153 options:0
154 metrics:metrics
155 views:viewsDictionary];
156 for (NSLayoutConstraint* constraint in constraints) {
157 [view addConstraint:constraint];
158 }
159 }
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000160}
161
denicija647915f2016-10-20 01:46:41 -0700162#pragma mark - Control actions
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000163
denicija647915f2016-10-20 01:46:41 -0700164- (void)startCall:(id)sender {
165 NSString* roomString = _roomField.stringValue;
166 // Generate room id for loopback options.
167 if (_loopbackButton.intValue && [roomString isEqualToString:@""]) {
168 roomString = [NSUUID UUID].UUIDString;
169 roomString = [roomString stringByReplacingOccurrencesOfString:@"-" withString:@""];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000170 }
denicija647915f2016-10-20 01:46:41 -0700171
172 [self.delegate appRTCMainView:self
173 didEnterRoomId:roomString
174 loopback:_loopbackButton.intValue];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000175}
176
177#pragma mark - RTCNSGLVideoViewDelegate
178
179- (void)videoView:(RTCNSGLVideoView*)videoView
180 didChangeVideoSize:(NSSize)size {
181 if (videoView == _remoteVideoView) {
182 _remoteVideoSize = size;
183 } else if (videoView == _localVideoView) {
184 _localVideoSize = size;
185 } else {
186 return;
187 }
denicija647915f2016-10-20 01:46:41 -0700188
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000189 [self setNeedsUpdateConstraints:YES];
190}
191
192#pragma mark - Private
193
194- (void)setupViews {
195 NSParameterAssert([[self subviews] count] == 0);
196
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000197 _logView = [[NSTextView alloc] initWithFrame:NSZeroRect];
denicija647915f2016-10-20 01:46:41 -0700198 [_logView setMinSize:NSMakeSize(0, kBottomViewHeight)];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000199 [_logView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
200 [_logView setVerticallyResizable:YES];
201 [_logView setAutoresizingMask:NSViewWidthSizable];
202 NSTextContainer* textContainer = [_logView textContainer];
203 NSSize containerSize = NSMakeSize(kContentWidth, FLT_MAX);
204 [textContainer setContainerSize:containerSize];
205 [textContainer setWidthTracksTextView:YES];
206 [_logView setEditable:NO];
207
denicija647915f2016-10-20 01:46:41 -0700208 [self setupActionItemsView];
209
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000210 _scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect];
211 [_scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
212 [_scrollView setHasVerticalScroller:YES];
213 [_scrollView setDocumentView:_logView];
214 [self addSubview:_scrollView];
215
216 NSOpenGLPixelFormatAttribute attributes[] = {
217 NSOpenGLPFADoubleBuffer,
218 NSOpenGLPFADepthSize, 24,
219 NSOpenGLPFAOpenGLProfile,
220 NSOpenGLProfileVersion3_2Core,
221 0
222 };
223 NSOpenGLPixelFormat* pixelFormat =
224 [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
225 _remoteVideoView = [[RTCNSGLVideoView alloc] initWithFrame:NSZeroRect
226 pixelFormat:pixelFormat];
227 [_remoteVideoView setTranslatesAutoresizingMaskIntoConstraints:NO];
228 _remoteVideoView.delegate = self;
229 [self addSubview:_remoteVideoView];
230
kthelgason314bc5f2016-08-31 10:23:27 -0700231 _localVideoView = [[RTCNSGLVideoView alloc] initWithFrame:NSZeroRect
232 pixelFormat:pixelFormat];
233 [_localVideoView setTranslatesAutoresizingMaskIntoConstraints:NO];
234 _localVideoView.delegate = self;
235 [self addSubview:_localVideoView];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000236}
237
denicija647915f2016-10-20 01:46:41 -0700238- (void)setupActionItemsView {
239 _actionItemsView = [[NSView alloc] initWithFrame:NSZeroRect];
240 [_actionItemsView setTranslatesAutoresizingMaskIntoConstraints:NO];
241 [self addSubview:_actionItemsView];
242
243 _roomField = [[NSTextField alloc] initWithFrame:NSZeroRect];
244 [_roomField setTranslatesAutoresizingMaskIntoConstraints:NO];
magjeda445b9b2017-02-20 07:56:53 -0800245 [[_roomField cell] setPlaceholderString: @"Enter AppRTC room id"];
denicija647915f2016-10-20 01:46:41 -0700246 [_actionItemsView addSubview:_roomField];
247 [_roomField setEditable:YES];
248
249 _connectButton = [[NSButton alloc] initWithFrame:NSZeroRect];
250 [_connectButton setTranslatesAutoresizingMaskIntoConstraints:NO];
251 _connectButton.title = @"Start call";
252 _connectButton.bezelStyle = NSRoundedBezelStyle;
253 _connectButton.target = self;
254 _connectButton.action = @selector(startCall:);
255 [_actionItemsView addSubview:_connectButton];
256
257 _loopbackButton = [[NSButton alloc] initWithFrame:NSZeroRect];
258 [_loopbackButton setTranslatesAutoresizingMaskIntoConstraints:NO];
259 _loopbackButton.title = @"Loopback";
260 [_loopbackButton setButtonType:NSSwitchButton];
261 [_actionItemsView addSubview:_loopbackButton];
262}
263
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000264- (NSSize)remoteVideoViewSize {
denicija647915f2016-10-20 01:46:41 -0700265 if (!_remoteVideoView.bounds.size.width) {
266 return NSMakeSize(kContentWidth, 0);
267 }
268 NSInteger width = MAX(_remoteVideoView.bounds.size.width, kContentWidth);
kthelgason314bc5f2016-08-31 10:23:27 -0700269 NSInteger height = (width/16) * 9;
270 return NSMakeSize(width, height);
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000271}
272
273@end
274
275@interface APPRTCViewController ()
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000276 <ARDAppClientDelegate, APPRTCMainViewDelegate>
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000277@property(nonatomic, readonly) APPRTCMainView* mainView;
278@end
279
280@implementation APPRTCViewController {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000281 ARDAppClient* _client;
tkchin@webrtc.org81257442014-11-04 23:06:15 +0000282 RTCVideoTrack* _localVideoTrack;
283 RTCVideoTrack* _remoteVideoTrack;
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000284}
285
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000286- (void)dealloc {
287 [self disconnect];
288}
289
denicija647915f2016-10-20 01:46:41 -0700290- (void)viewDidAppear {
291 [super viewDidAppear];
292 [self displayUsageInstructions];
293}
294
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000295- (void)loadView {
296 APPRTCMainView* view = [[APPRTCMainView alloc] initWithFrame:NSZeroRect];
297 [view setTranslatesAutoresizingMaskIntoConstraints:NO];
298 view.delegate = self;
299 self.view = view;
300}
301
302- (void)windowWillClose:(NSNotification*)notification {
303 [self disconnect];
304}
305
denicija647915f2016-10-20 01:46:41 -0700306#pragma mark - Usage
307
308- (void)displayUsageInstructions {
309 [self.mainView displayLogMessage:
310 @"To start call:\n"
311 @"• Enter AppRTC room id (not neccessary for loopback)\n"
312 @"• Start call"];
313}
314
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000315#pragma mark - ARDAppClientDelegate
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000316
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000317- (void)appClient:(ARDAppClient *)client
318 didChangeState:(ARDAppClientState)state {
319 switch (state) {
320 case kARDAppClientStateConnected:
denicija647915f2016-10-20 01:46:41 -0700321 [self.mainView displayLogMessage:@"Client connected."];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000322 break;
323 case kARDAppClientStateConnecting:
denicija647915f2016-10-20 01:46:41 -0700324 [self.mainView displayLogMessage:@"Client connecting."];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000325 break;
326 case kARDAppClientStateDisconnected:
denicija647915f2016-10-20 01:46:41 -0700327 [self.mainView displayLogMessage:@"Client disconnected."];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000328 [self resetUI];
329 _client = nil;
330 break;
331 }
332}
333
334- (void)appClient:(ARDAppClient *)client
hjon79858f82016-03-13 22:08:26 -0700335 didChangeConnectionState:(RTCIceConnectionState)state {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000336}
337
338- (void)appClient:(ARDAppClient *)client
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000339 didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack {
tkchin@webrtc.org81257442014-11-04 23:06:15 +0000340 _localVideoTrack = localVideoTrack;
kthelgason314bc5f2016-08-31 10:23:27 -0700341 [_localVideoTrack addRenderer:self.mainView.localVideoView];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000342}
343
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000344- (void)appClient:(ARDAppClient *)client
345 didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack {
tkchin@webrtc.org81257442014-11-04 23:06:15 +0000346 _remoteVideoTrack = remoteVideoTrack;
347 [_remoteVideoTrack addRenderer:self.mainView.remoteVideoView];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000348}
349
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000350- (void)appClient:(ARDAppClient *)client
351 didError:(NSError *)error {
352 [self showAlertWithMessage:[NSString stringWithFormat:@"%@", error]];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000353 [self disconnect];
354}
355
Zeke Chind3325802015-08-14 11:00:02 -0700356- (void)appClient:(ARDAppClient *)client
357 didGetStats:(NSArray *)stats {
358}
359
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000360#pragma mark - APPRTCMainViewDelegate
361
362- (void)appRTCMainView:(APPRTCMainView*)mainView
denicija647915f2016-10-20 01:46:41 -0700363 didEnterRoomId:(NSString*)roomId
364 loopback:(BOOL)isLoopback {
365
366 if ([roomId isEqualToString:@""]) {
367 [self.mainView displayLogMessage:@"Missing room id"];
368 return;
369 }
370
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000371 [_client disconnect];
372 ARDAppClient *client = [[ARDAppClient alloc] initWithDelegate:self];
tkchinab1293a2016-08-30 12:35:05 -0700373 [client connectToRoomWithId:roomId
denicija647915f2016-10-20 01:46:41 -0700374 isLoopback:isLoopback
tkchinab1293a2016-08-30 12:35:05 -0700375 isAudioOnly:NO
376 shouldMakeAecDump:NO
377 shouldUseLevelControl:NO];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000378 _client = client;
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000379}
380
381#pragma mark - Private
382
383- (APPRTCMainView*)mainView {
384 return (APPRTCMainView*)self.view;
385}
386
387- (void)showAlertWithMessage:(NSString*)message {
388 NSAlert* alert = [[NSAlert alloc] init];
389 [alert setMessageText:message];
390 [alert runModal];
391}
392
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000393- (void)resetUI {
tkchin@webrtc.org81257442014-11-04 23:06:15 +0000394 [_remoteVideoTrack removeRenderer:self.mainView.remoteVideoView];
kthelgason314bc5f2016-08-31 10:23:27 -0700395 [_localVideoTrack removeRenderer:self.mainView.localVideoView];
tkchin@webrtc.org81257442014-11-04 23:06:15 +0000396 _remoteVideoTrack = nil;
kthelgason314bc5f2016-08-31 10:23:27 -0700397 _localVideoTrack = nil;
tkchin@webrtc.org81257442014-11-04 23:06:15 +0000398 [self.mainView.remoteVideoView renderFrame:nil];
kthelgason314bc5f2016-08-31 10:23:27 -0700399 [self.mainView.localVideoView renderFrame:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000400}
401
402- (void)disconnect {
403 [self resetUI];
404 [_client disconnect];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000405}
406
407@end