Implement mac version of AppRTCDemo.

- Refactored and moved AppRTCDemo to support sharing AppRTC connection code between iOS and mac counterparts.
- Refactored OpenGL rendering code to be shared between iOS and mac counterparts.
- iOS AppRTCDemo now respects video aspect ratio.

BUG=2168
R=fischman@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/17589004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6291 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m b/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
new file mode 100644
index 0000000..cf5b836
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
@@ -0,0 +1,312 @@
+/*
+ * libjingle
+ * Copyright 2014, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "APPRTCViewController.h"
+
+#import <AVFoundation/AVFoundation.h>
+#import "APPRTCConnectionManager.h"
+#import "RTCNSGLVideoView.h"
+
+static NSUInteger const kContentWidth = 1280;
+static NSUInteger const kContentHeight = 720;
+static NSUInteger const kRoomFieldWidth = 80;
+static NSUInteger const kLogViewHeight = 280;
+
+@class APPRTCMainView;
+@protocol APPRTCMainViewDelegate
+
+- (void)appRTCMainView:(APPRTCMainView*)mainView
+        didEnterRoomId:(NSString*)roomId;
+
+@end
+
+@interface APPRTCMainView : NSView
+
+@property(nonatomic, weak) id<APPRTCMainViewDelegate> delegate;
+@property(nonatomic, readonly) RTCNSGLVideoView* localVideoView;
+@property(nonatomic, readonly) RTCNSGLVideoView* remoteVideoView;
+
+- (void)displayLogMessage:(NSString*)message;
+
+@end
+
+@interface APPRTCMainView () <NSTextFieldDelegate, RTCNSGLVideoViewDelegate>
+@end
+@implementation APPRTCMainView  {
+  NSScrollView* _scrollView;
+  NSTextField* _roomLabel;
+  NSTextField* _roomField;
+  NSTextView* _logView;
+  RTCNSGLVideoView* _localVideoView;
+  RTCNSGLVideoView* _remoteVideoView;
+  CGSize _localVideoSize;
+  CGSize _remoteVideoSize;
+}
+
++ (BOOL)requiresConstraintBasedLayout {
+  return YES;
+}
+
+- (instancetype)initWithFrame:(NSRect)frame {
+  if (self = [super initWithFrame:frame]) {
+    [self setupViews];
+  }
+  return self;
+}
+
+- (void)updateConstraints {
+  NSParameterAssert(
+      _roomField != nil && _scrollView != nil && _remoteVideoView != nil);
+  [self removeConstraints:[self constraints]];
+  NSDictionary* viewsDictionary =
+      NSDictionaryOfVariableBindings(_roomLabel,
+                                     _roomField,
+                                     _scrollView,
+                                     _remoteVideoView);
+
+  NSSize remoteViewSize = [self remoteVideoViewSize];
+  NSDictionary* metrics = @{
+    @"kLogViewHeight" : @(kLogViewHeight),
+    @"kRoomFieldWidth" : @(kRoomFieldWidth),
+    @"remoteViewWidth" : @(remoteViewSize.width),
+    @"remoteViewHeight" : @(remoteViewSize.height),
+  };
+  // Declare this separately to avoid compiler warning about splitting string
+  // within an NSArray expression.
+  NSString* verticalConstraint =
+      @"V:|-[_roomLabel]-[_roomField]-[_scrollView(kLogViewHeight)]"
+       "-[_remoteVideoView(remoteViewHeight)]-|";
+  NSArray* constraintFormats = @[
+      verticalConstraint,
+      @"|-[_roomLabel]",
+      @"|-[_roomField(kRoomFieldWidth)]",
+      @"|-[_scrollView(remoteViewWidth)]-|",
+      @"|-[_remoteVideoView(remoteViewWidth)]-|",
+  ];
+  for (NSString* constraintFormat in constraintFormats) {
+    NSArray* constraints =
+        [NSLayoutConstraint constraintsWithVisualFormat:constraintFormat
+                                                options:0
+                                                metrics:metrics
+                                                  views:viewsDictionary];
+    for (NSLayoutConstraint* constraint in constraints) {
+      [self addConstraint:constraint];
+    }
+  }
+  [super updateConstraints];
+}
+
+- (void)displayLogMessage:(NSString*)message {
+  _logView.string =
+      [NSString stringWithFormat:@"%@%@\n", _logView.string, message];
+  NSRange range = NSMakeRange([_logView.string length], 0);
+  [_logView scrollRangeToVisible:range];
+}
+
+#pragma mark - NSControl delegate
+
+- (void)controlTextDidEndEditing:(NSNotification*)notification {
+  NSDictionary* userInfo = [notification userInfo];
+  NSInteger textMovement = [userInfo[@"NSTextMovement"] intValue];
+  if (textMovement == NSReturnTextMovement) {
+    [self.delegate appRTCMainView:self didEnterRoomId:_roomField.stringValue];
+  }
+}
+
+#pragma mark - RTCNSGLVideoViewDelegate
+
+- (void)videoView:(RTCNSGLVideoView*)videoView
+    didChangeVideoSize:(NSSize)size {
+  if (videoView == _remoteVideoView) {
+    _remoteVideoSize = size;
+  } else if (videoView == _localVideoView) {
+    _localVideoSize = size;
+  } else {
+    return;
+  }
+  [self setNeedsUpdateConstraints:YES];
+}
+
+#pragma mark - Private
+
+- (void)setupViews {
+  NSParameterAssert([[self subviews] count] == 0);
+
+  _roomLabel = [[NSTextField alloc] initWithFrame:NSZeroRect];
+  [_roomLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
+  [_roomLabel setBezeled:NO];
+  [_roomLabel setDrawsBackground:NO];
+  [_roomLabel setEditable:NO];
+  [_roomLabel setStringValue:@"Enter AppRTC room id:"];
+  [self addSubview:_roomLabel];
+
+  _roomField = [[NSTextField alloc] initWithFrame:NSZeroRect];
+  [_roomField setTranslatesAutoresizingMaskIntoConstraints:NO];
+  [self addSubview:_roomField];
+  [_roomField setEditable:YES];
+  [_roomField setDelegate:self];
+
+  _logView = [[NSTextView alloc] initWithFrame:NSZeroRect];
+  [_logView setMinSize:NSMakeSize(0, kLogViewHeight)];
+  [_logView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+  [_logView setVerticallyResizable:YES];
+  [_logView setAutoresizingMask:NSViewWidthSizable];
+  NSTextContainer* textContainer = [_logView textContainer];
+  NSSize containerSize = NSMakeSize(kContentWidth, FLT_MAX);
+  [textContainer setContainerSize:containerSize];
+  [textContainer setWidthTracksTextView:YES];
+  [_logView setEditable:NO];
+
+  _scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect];
+  [_scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
+  [_scrollView setHasVerticalScroller:YES];
+  [_scrollView setDocumentView:_logView];
+  [self addSubview:_scrollView];
+
+  NSOpenGLPixelFormatAttribute attributes[] = {
+    NSOpenGLPFADoubleBuffer,
+    NSOpenGLPFADepthSize, 24,
+    NSOpenGLPFAOpenGLProfile,
+    NSOpenGLProfileVersion3_2Core,
+    0
+  };
+  NSOpenGLPixelFormat* pixelFormat =
+      [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
+  _remoteVideoView = [[RTCNSGLVideoView alloc] initWithFrame:NSZeroRect
+                                                 pixelFormat:pixelFormat];
+  [_remoteVideoView setTranslatesAutoresizingMaskIntoConstraints:NO];
+  _remoteVideoView.delegate = self;
+  [self addSubview:_remoteVideoView];
+
+  // TODO(tkchin): create local video view.
+  // https://code.google.com/p/webrtc/issues/detail?id=3417.
+}
+
+- (NSSize)remoteVideoViewSize {
+  if (_remoteVideoSize.width > 0 && _remoteVideoSize.height > 0) {
+    return _remoteVideoSize;
+  } else {
+    return NSMakeSize(kContentWidth, kContentHeight);
+  }
+}
+
+- (NSSize)localVideoViewSize {
+  return NSZeroSize;
+}
+
+@end
+
+@interface APPRTCViewController ()
+    <APPRTCConnectionManagerDelegate, APPRTCMainViewDelegate, APPRTCLogger>
+@property(nonatomic, readonly) APPRTCMainView* mainView;
+@end
+
+@implementation APPRTCViewController {
+  APPRTCConnectionManager* _connectionManager;
+}
+
+- (instancetype)initWithNibName:(NSString*)nibName
+                         bundle:(NSBundle*)bundle {
+  if (self = [super initWithNibName:nibName bundle:bundle]) {
+    _connectionManager =
+        [[APPRTCConnectionManager alloc] initWithDelegate:self
+                                                   logger:self];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [self disconnect];
+}
+
+- (void)loadView {
+  APPRTCMainView* view = [[APPRTCMainView alloc] initWithFrame:NSZeroRect];
+  [view setTranslatesAutoresizingMaskIntoConstraints:NO];
+  view.delegate = self;
+  self.view = view;
+}
+
+- (void)windowWillClose:(NSNotification*)notification {
+  [self disconnect];
+}
+
+#pragma mark - APPRTCConnectionManagerDelegate
+
+- (void)connectionManager:(APPRTCConnectionManager*)manager
+    didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack {
+  self.mainView.localVideoView.videoTrack = localVideoTrack;
+}
+
+- (void)connectionManager:(APPRTCConnectionManager*)manager
+    didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack {
+  self.mainView.remoteVideoView.videoTrack = remoteVideoTrack;
+}
+
+- (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager {
+  [self showAlertWithMessage:@"Remote closed connection"];
+  [self disconnect];
+}
+
+- (void)connectionManager:(APPRTCConnectionManager*)manager
+      didErrorWithMessage:(NSString*)message {
+  [self showAlertWithMessage:message];
+  [self disconnect];
+}
+
+#pragma mark - APPRTCLogger
+
+- (void)logMessage:(NSString*)message {
+  [self.mainView displayLogMessage:message];
+}
+
+#pragma mark - APPRTCMainViewDelegate
+
+- (void)appRTCMainView:(APPRTCMainView*)mainView
+        didEnterRoomId:(NSString*)roomId {
+  NSString* urlString =
+      [NSString stringWithFormat:@"https://apprtc.appspot.com/?r=%@", roomId];
+  [_connectionManager connectToRoomWithURL:[NSURL URLWithString:urlString]];
+}
+
+#pragma mark - Private
+
+- (APPRTCMainView*)mainView {
+  return (APPRTCMainView*)self.view;
+}
+
+- (void)showAlertWithMessage:(NSString*)message {
+  NSAlert* alert = [[NSAlert alloc] init];
+  [alert setMessageText:message];
+  [alert runModal];
+}
+
+- (void)disconnect {
+  self.mainView.remoteVideoView.videoTrack = nil;
+  [_connectionManager disconnect];
+}
+
+@end