blob: 41bfd35c727efed7deae9a9afec2c2f24519305b [file] [log] [blame]
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +00001/*
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>
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000031#import "ARDAppClient.h"
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000032#import "RTCNSGLVideoView.h"
tkchin@webrtc.org81257442014-11-04 23:06:15 +000033#import "RTCVideoTrack.h"
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +000034
35static NSUInteger const kContentWidth = 1280;
36static NSUInteger const kContentHeight = 720;
37static NSUInteger const kRoomFieldWidth = 80;
38static NSUInteger const kLogViewHeight = 280;
39
40@class APPRTCMainView;
41@protocol APPRTCMainViewDelegate
42
43- (void)appRTCMainView:(APPRTCMainView*)mainView
44 didEnterRoomId:(NSString*)roomId;
45
46@end
47
48@interface APPRTCMainView : NSView
49
50@property(nonatomic, weak) id<APPRTCMainViewDelegate> delegate;
51@property(nonatomic, readonly) RTCNSGLVideoView* localVideoView;
52@property(nonatomic, readonly) RTCNSGLVideoView* remoteVideoView;
53
54- (void)displayLogMessage:(NSString*)message;
55
56@end
57
58@interface APPRTCMainView () <NSTextFieldDelegate, RTCNSGLVideoViewDelegate>
59@end
60@implementation APPRTCMainView {
61 NSScrollView* _scrollView;
62 NSTextField* _roomLabel;
63 NSTextField* _roomField;
64 NSTextView* _logView;
65 RTCNSGLVideoView* _localVideoView;
66 RTCNSGLVideoView* _remoteVideoView;
67 CGSize _localVideoSize;
68 CGSize _remoteVideoSize;
69}
70
71+ (BOOL)requiresConstraintBasedLayout {
72 return YES;
73}
74
75- (instancetype)initWithFrame:(NSRect)frame {
76 if (self = [super initWithFrame:frame]) {
77 [self setupViews];
78 }
79 return self;
80}
81
82- (void)updateConstraints {
83 NSParameterAssert(
84 _roomField != nil && _scrollView != nil && _remoteVideoView != nil);
85 [self removeConstraints:[self constraints]];
86 NSDictionary* viewsDictionary =
87 NSDictionaryOfVariableBindings(_roomLabel,
88 _roomField,
89 _scrollView,
90 _remoteVideoView);
91
92 NSSize remoteViewSize = [self remoteVideoViewSize];
93 NSDictionary* metrics = @{
94 @"kLogViewHeight" : @(kLogViewHeight),
95 @"kRoomFieldWidth" : @(kRoomFieldWidth),
96 @"remoteViewWidth" : @(remoteViewSize.width),
97 @"remoteViewHeight" : @(remoteViewSize.height),
98 };
99 // Declare this separately to avoid compiler warning about splitting string
100 // within an NSArray expression.
101 NSString* verticalConstraint =
102 @"V:|-[_roomLabel]-[_roomField]-[_scrollView(kLogViewHeight)]"
103 "-[_remoteVideoView(remoteViewHeight)]-|";
104 NSArray* constraintFormats = @[
105 verticalConstraint,
106 @"|-[_roomLabel]",
107 @"|-[_roomField(kRoomFieldWidth)]",
108 @"|-[_scrollView(remoteViewWidth)]-|",
109 @"|-[_remoteVideoView(remoteViewWidth)]-|",
110 ];
111 for (NSString* constraintFormat in constraintFormats) {
112 NSArray* constraints =
113 [NSLayoutConstraint constraintsWithVisualFormat:constraintFormat
114 options:0
115 metrics:metrics
116 views:viewsDictionary];
117 for (NSLayoutConstraint* constraint in constraints) {
118 [self addConstraint:constraint];
119 }
120 }
121 [super updateConstraints];
122}
123
124- (void)displayLogMessage:(NSString*)message {
125 _logView.string =
126 [NSString stringWithFormat:@"%@%@\n", _logView.string, message];
127 NSRange range = NSMakeRange([_logView.string length], 0);
128 [_logView scrollRangeToVisible:range];
129}
130
131#pragma mark - NSControl delegate
132
133- (void)controlTextDidEndEditing:(NSNotification*)notification {
134 NSDictionary* userInfo = [notification userInfo];
135 NSInteger textMovement = [userInfo[@"NSTextMovement"] intValue];
136 if (textMovement == NSReturnTextMovement) {
137 [self.delegate appRTCMainView:self didEnterRoomId:_roomField.stringValue];
138 }
139}
140
141#pragma mark - RTCNSGLVideoViewDelegate
142
143- (void)videoView:(RTCNSGLVideoView*)videoView
144 didChangeVideoSize:(NSSize)size {
145 if (videoView == _remoteVideoView) {
146 _remoteVideoSize = size;
147 } else if (videoView == _localVideoView) {
148 _localVideoSize = size;
149 } else {
150 return;
151 }
152 [self setNeedsUpdateConstraints:YES];
153}
154
155#pragma mark - Private
156
157- (void)setupViews {
158 NSParameterAssert([[self subviews] count] == 0);
159
160 _roomLabel = [[NSTextField alloc] initWithFrame:NSZeroRect];
161 [_roomLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
162 [_roomLabel setBezeled:NO];
163 [_roomLabel setDrawsBackground:NO];
164 [_roomLabel setEditable:NO];
165 [_roomLabel setStringValue:@"Enter AppRTC room id:"];
166 [self addSubview:_roomLabel];
167
168 _roomField = [[NSTextField alloc] initWithFrame:NSZeroRect];
169 [_roomField setTranslatesAutoresizingMaskIntoConstraints:NO];
170 [self addSubview:_roomField];
171 [_roomField setEditable:YES];
172 [_roomField setDelegate:self];
173
174 _logView = [[NSTextView alloc] initWithFrame:NSZeroRect];
175 [_logView setMinSize:NSMakeSize(0, kLogViewHeight)];
176 [_logView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
177 [_logView setVerticallyResizable:YES];
178 [_logView setAutoresizingMask:NSViewWidthSizable];
179 NSTextContainer* textContainer = [_logView textContainer];
180 NSSize containerSize = NSMakeSize(kContentWidth, FLT_MAX);
181 [textContainer setContainerSize:containerSize];
182 [textContainer setWidthTracksTextView:YES];
183 [_logView setEditable:NO];
184
185 _scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect];
186 [_scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
187 [_scrollView setHasVerticalScroller:YES];
188 [_scrollView setDocumentView:_logView];
189 [self addSubview:_scrollView];
190
191 NSOpenGLPixelFormatAttribute attributes[] = {
192 NSOpenGLPFADoubleBuffer,
193 NSOpenGLPFADepthSize, 24,
194 NSOpenGLPFAOpenGLProfile,
195 NSOpenGLProfileVersion3_2Core,
196 0
197 };
198 NSOpenGLPixelFormat* pixelFormat =
199 [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
200 _remoteVideoView = [[RTCNSGLVideoView alloc] initWithFrame:NSZeroRect
201 pixelFormat:pixelFormat];
202 [_remoteVideoView setTranslatesAutoresizingMaskIntoConstraints:NO];
203 _remoteVideoView.delegate = self;
204 [self addSubview:_remoteVideoView];
205
206 // TODO(tkchin): create local video view.
207 // https://code.google.com/p/webrtc/issues/detail?id=3417.
208}
209
210- (NSSize)remoteVideoViewSize {
211 if (_remoteVideoSize.width > 0 && _remoteVideoSize.height > 0) {
212 return _remoteVideoSize;
213 } else {
214 return NSMakeSize(kContentWidth, kContentHeight);
215 }
216}
217
218- (NSSize)localVideoViewSize {
219 return NSZeroSize;
220}
221
222@end
223
224@interface APPRTCViewController ()
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000225 <ARDAppClientDelegate, APPRTCMainViewDelegate>
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000226@property(nonatomic, readonly) APPRTCMainView* mainView;
227@end
228
229@implementation APPRTCViewController {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000230 ARDAppClient* _client;
tkchin@webrtc.org81257442014-11-04 23:06:15 +0000231 RTCVideoTrack* _localVideoTrack;
232 RTCVideoTrack* _remoteVideoTrack;
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000233}
234
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000235- (void)dealloc {
236 [self disconnect];
237}
238
239- (void)loadView {
240 APPRTCMainView* view = [[APPRTCMainView alloc] initWithFrame:NSZeroRect];
241 [view setTranslatesAutoresizingMaskIntoConstraints:NO];
242 view.delegate = self;
243 self.view = view;
244}
245
246- (void)windowWillClose:(NSNotification*)notification {
247 [self disconnect];
248}
249
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000250#pragma mark - ARDAppClientDelegate
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000251
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000252- (void)appClient:(ARDAppClient *)client
253 didChangeState:(ARDAppClientState)state {
254 switch (state) {
255 case kARDAppClientStateConnected:
256 NSLog(@"Client connected.");
257 break;
258 case kARDAppClientStateConnecting:
259 NSLog(@"Client connecting.");
260 break;
261 case kARDAppClientStateDisconnected:
262 NSLog(@"Client disconnected.");
263 [self resetUI];
264 _client = nil;
265 break;
266 }
267}
268
269- (void)appClient:(ARDAppClient *)client
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000270 didChangeConnectionState:(RTCICEConnectionState)state {
271}
272
273- (void)appClient:(ARDAppClient *)client
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000274 didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack {
tkchin@webrtc.org81257442014-11-04 23:06:15 +0000275 _localVideoTrack = localVideoTrack;
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000276}
277
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000278- (void)appClient:(ARDAppClient *)client
279 didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack {
tkchin@webrtc.org81257442014-11-04 23:06:15 +0000280 _remoteVideoTrack = remoteVideoTrack;
281 [_remoteVideoTrack addRenderer:self.mainView.remoteVideoView];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000282}
283
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000284- (void)appClient:(ARDAppClient *)client
285 didError:(NSError *)error {
286 [self showAlertWithMessage:[NSString stringWithFormat:@"%@", error]];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000287 [self disconnect];
288}
289
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000290#pragma mark - APPRTCMainViewDelegate
291
292- (void)appRTCMainView:(APPRTCMainView*)mainView
293 didEnterRoomId:(NSString*)roomId {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000294 [_client disconnect];
295 ARDAppClient *client = [[ARDAppClient alloc] initWithDelegate:self];
296 [client connectToRoomWithId:roomId options:nil];
297 _client = client;
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000298}
299
300#pragma mark - Private
301
302- (APPRTCMainView*)mainView {
303 return (APPRTCMainView*)self.view;
304}
305
306- (void)showAlertWithMessage:(NSString*)message {
307 NSAlert* alert = [[NSAlert alloc] init];
308 [alert setMessageText:message];
309 [alert runModal];
310}
311
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000312- (void)resetUI {
tkchin@webrtc.org81257442014-11-04 23:06:15 +0000313 [_remoteVideoTrack removeRenderer:self.mainView.remoteVideoView];
314 _remoteVideoTrack = nil;
315 [self.mainView.remoteVideoView renderFrame:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000316}
317
318- (void)disconnect {
319 [self resetUI];
320 [_client disconnect];
tkchin@webrtc.orgacca6752014-05-30 22:26:06 +0000321}
322
323@end