3 * Copyright 2013, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
28 #if !defined(__has_feature) || !__has_feature(objc_arc)
29 #error "This file requires ARC support."
32 #import "APPRTCViewController.h"
34 #import <AVFoundation/AVFoundation.h>
35 #import "APPRTCConnectionManager.h"
36 #import "RTCEAGLVideoView.h"
38 // Padding space for local video view with its parent.
39 static CGFloat const kLocalViewPadding = 20;
41 @interface APPRTCViewController ()
42 <APPRTCConnectionManagerDelegate, APPRTCLogger, RTCEAGLVideoViewDelegate>
43 @property(nonatomic, assign) UIInterfaceOrientation statusBarOrientation;
44 @property(nonatomic, strong) RTCEAGLVideoView* localVideoView;
45 @property(nonatomic, strong) RTCEAGLVideoView* remoteVideoView;
48 @implementation APPRTCViewController {
49 APPRTCConnectionManager* _connectionManager;
50 CGSize _localVideoSize;
51 CGSize _remoteVideoSize;
54 - (instancetype)initWithNibName:(NSString*)nibName
55 bundle:(NSBundle*)bundle {
56 if (self = [super initWithNibName:nibName bundle:bundle]) {
58 [[APPRTCConnectionManager alloc] initWithDelegate:self
67 self.remoteVideoView =
68 [[RTCEAGLVideoView alloc] initWithFrame:self.blackView.bounds];
69 self.remoteVideoView.delegate = self;
70 self.remoteVideoView.transform = CGAffineTransformMakeScale(-1, 1);
71 [self.blackView addSubview:self.remoteVideoView];
74 [[RTCEAGLVideoView alloc] initWithFrame:self.blackView.bounds];
75 self.localVideoView.delegate = self;
76 [self.blackView addSubview:self.localVideoView];
78 self.statusBarOrientation =
79 [UIApplication sharedApplication].statusBarOrientation;
80 self.roomInput.delegate = self;
81 [self.roomInput becomeFirstResponder];
84 - (void)viewDidLayoutSubviews {
85 if (self.statusBarOrientation !=
86 [UIApplication sharedApplication].statusBarOrientation) {
87 self.statusBarOrientation =
88 [UIApplication sharedApplication].statusBarOrientation;
89 [[NSNotificationCenter defaultCenter]
90 postNotificationName:@"StatusBarOrientationDidChange"
95 - (void)applicationWillResignActive:(UIApplication*)application {
96 [self logMessage:@"Application lost focus, connection broken."];
100 #pragma mark - APPRTCConnectionManagerDelegate
102 - (void)connectionManager:(APPRTCConnectionManager*)manager
103 didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack {
104 self.localVideoView.hidden = NO;
105 self.localVideoView.videoTrack = localVideoTrack;
108 - (void)connectionManager:(APPRTCConnectionManager*)manager
109 didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack {
110 self.remoteVideoView.videoTrack = remoteVideoTrack;
113 - (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager {
114 [self showAlertWithMessage:@"Remote hung up."];
118 - (void)connectionManager:(APPRTCConnectionManager*)manager
119 didErrorWithMessage:(NSString*)message {
120 [self showAlertWithMessage:message];
124 #pragma mark - APPRTCLogger
126 - (void)logMessage:(NSString*)message {
127 dispatch_async(dispatch_get_main_queue(), ^{
129 [NSString stringWithFormat:@"%@\n%@", self.logView.text, message];
130 self.logView.text = output;
132 scrollRangeToVisible:NSMakeRange([self.logView.text length], 0)];
136 #pragma mark - RTCEAGLVideoViewDelegate
138 - (void)videoView:(RTCEAGLVideoView*)videoView
139 didChangeVideoSize:(CGSize)size {
140 if (videoView == self.localVideoView) {
141 _localVideoSize = size;
142 } else if (videoView == self.remoteVideoView) {
143 _remoteVideoSize = size;
145 NSParameterAssert(NO);
147 [self updateVideoViewLayout];
150 #pragma mark - UITextFieldDelegate
152 - (void)textFieldDidEndEditing:(UITextField*)textField {
153 NSString* room = textField.text;
154 if ([room length] == 0) {
157 textField.hidden = YES;
158 self.instructionsView.hidden = YES;
159 self.logView.hidden = NO;
161 [NSString stringWithFormat:@"https://apprtc.appspot.com/?r=%@", room];
162 [_connectionManager connectToRoomWithURL:[NSURL URLWithString:url]];
163 [self setupCaptureSession];
166 - (BOOL)textFieldShouldReturn:(UITextField*)textField {
167 // There is no other control that can take focus, so manually resign focus
168 // when return (Join) is pressed to trigger |textFieldDidEndEditing|.
169 [textField resignFirstResponder];
173 #pragma mark - Private
177 [_connectionManager disconnect];
180 - (void)showAlertWithMessage:(NSString*)message {
181 UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:nil
184 cancelButtonTitle:@"OK"
185 otherButtonTitles:nil];
190 [self.roomInput resignFirstResponder];
191 self.roomInput.text = nil;
192 self.roomInput.hidden = NO;
193 self.instructionsView.hidden = NO;
194 self.logView.hidden = YES;
195 self.logView.text = nil;
196 self.localVideoView.videoTrack = nil;
197 self.remoteVideoView.videoTrack = nil;
198 self.blackView.hidden = YES;
201 - (void)setupCaptureSession {
202 self.blackView.hidden = NO;
203 [self updateVideoViewLayout];
206 - (void)updateVideoViewLayout {
207 // TODO(tkchin): handle rotation.
208 CGSize defaultAspectRatio = CGSizeMake(4, 3);
209 CGSize localAspectRatio = CGSizeEqualToSize(_localVideoSize, CGSizeZero) ?
210 defaultAspectRatio : _localVideoSize;
211 CGSize remoteAspectRatio = CGSizeEqualToSize(_remoteVideoSize, CGSizeZero) ?
212 defaultAspectRatio : _remoteVideoSize;
214 CGRect remoteVideoFrame =
215 AVMakeRectWithAspectRatioInsideRect(remoteAspectRatio,
216 self.blackView.bounds);
217 self.remoteVideoView.frame = remoteVideoFrame;
219 CGRect localVideoFrame =
220 AVMakeRectWithAspectRatioInsideRect(localAspectRatio,
221 self.blackView.bounds);
222 localVideoFrame.size.width = localVideoFrame.size.width / 3;
223 localVideoFrame.size.height = localVideoFrame.size.height / 3;
224 localVideoFrame.origin.x = CGRectGetMaxX(self.blackView.bounds)
225 - localVideoFrame.size.width - kLocalViewPadding;
226 localVideoFrame.origin.y = CGRectGetMaxY(self.blackView.bounds)
227 - localVideoFrame.size.height - kLocalViewPadding;
228 self.localVideoView.frame = localVideoFrame;