Loopback and audio only mode.

Adds a loopback button that will connect to itself by simulating another client connection to the web socket server.

Adds an audio only mode switch.

BUG=

Review URL: https://codereview.webrtc.org/1334003002

Cr-Commit-Position: refs/heads/master@{#10153}
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDAppClient+Internal.h b/webrtc/examples/objc/AppRTCDemo/ARDAppClient+Internal.h
index 5a7b387..c1a0376 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDAppClient+Internal.h
+++ b/webrtc/examples/objc/AppRTCDemo/ARDAppClient+Internal.h
@@ -25,6 +25,7 @@
 // All properties should only be mutated from the main queue.
 @property(nonatomic, strong) id<ARDRoomServerClient> roomServerClient;
 @property(nonatomic, strong) id<ARDSignalingChannel> channel;
+@property(nonatomic, strong) id<ARDSignalingChannel> loopbackChannel;
 @property(nonatomic, strong) id<ARDTURNClient> turnClient;
 
 @property(nonatomic, strong) RTCPeerConnection *peerConnection;
@@ -41,6 +42,8 @@
 @property(nonatomic, strong) NSMutableArray *iceServers;
 @property(nonatomic, strong) NSURL *webSocketURL;
 @property(nonatomic, strong) NSURL *webSocketRestURL;
+@property(nonatomic, readonly) BOOL isLoopback;
+@property(nonatomic, readonly) BOOL isAudioOnly;
 
 @property(nonatomic, strong)
     RTCMediaConstraints *defaultPeerConnectionConstraints;
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.h b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.h
index d591704..0745c16 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.h
+++ b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.h
@@ -61,11 +61,11 @@
 - (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate;
 
 // Establishes a connection with the AppRTC servers for the given room id.
-// TODO(tkchin): provide available keys/values for options. This will be used
-// for call configurations such as overriding server choice, specifying codecs
-// and so on.
+// If |isLoopback| is true, the call will connect to itself.
+// If |isAudioOnly| is true, video will be disabled for the call.
 - (void)connectToRoomWithId:(NSString *)roomId
-                    options:(NSDictionary *)options;
+                 isLoopback:(BOOL)isLoopback
+                isAudioOnly:(BOOL)isAudioOnly;
 
 // Disconnects from the AppRTC servers and any connected clients.
 - (void)disconnect;
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
index e53222e..33e00ed 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
+++ b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
@@ -99,6 +99,7 @@
 @synthesize delegate = _delegate;
 @synthesize roomServerClient = _roomServerClient;
 @synthesize channel = _channel;
+@synthesize loopbackChannel = _loopbackChannel;
 @synthesize turnClient = _turnClient;
 @synthesize peerConnection = _peerConnection;
 @synthesize factory = _factory;
@@ -113,6 +114,8 @@
 @synthesize webSocketRestURL = _websocketRestURL;
 @synthesize defaultPeerConnectionConstraints =
     _defaultPeerConnectionConstraints;
+@synthesize isLoopback = _isLoopback;
+@synthesize isAudioOnly = _isAudioOnly;
 
 - (instancetype)init {
   if (self = [super init]) {
@@ -198,9 +201,12 @@
 }
 
 - (void)connectToRoomWithId:(NSString *)roomId
-                    options:(NSDictionary *)options {
+                 isLoopback:(BOOL)isLoopback
+                isAudioOnly:(BOOL)isAudioOnly {
   NSParameterAssert(roomId.length);
   NSParameterAssert(_state == kARDAppClientStateDisconnected);
+  _isLoopback = isLoopback;
+  _isAudioOnly = isAudioOnly;
   self.state = kARDAppClientStateConnecting;
 
   // Request TURN.
@@ -219,6 +225,7 @@
 
   // Join room on room server.
   [_roomServerClient joinRoomWithRoomId:roomId
+                             isLoopback:isLoopback
       completionHandler:^(ARDJoinResponse *response, NSError *error) {
     ARDAppClient *strongSelf = weakSelf;
     if (error) {
@@ -579,14 +586,17 @@
   // TODO(tkchin): local video capture for OSX. See
   // https://code.google.com/p/webrtc/issues/detail?id=3417.
 #if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
-  RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints];
-  RTCAVFoundationVideoSource *source =
-      [[RTCAVFoundationVideoSource alloc] initWithFactory:_factory
-                                              constraints:mediaConstraints];
-  localVideoTrack =
-      [[RTCVideoTrack alloc] initWithFactory:_factory
-                                      source:source
-                                     trackId:@"ARDAMSv0"];
+  if (!_isAudioOnly) {
+    RTCMediaConstraints *mediaConstraints =
+        [self defaultMediaStreamConstraints];
+    RTCAVFoundationVideoSource *source =
+        [[RTCAVFoundationVideoSource alloc] initWithFactory:_factory
+                                                constraints:mediaConstraints];
+    localVideoTrack =
+        [[RTCVideoTrack alloc] initWithFactory:_factory
+                                        source:source
+                                       trackId:@"ARDAMSv0"];
+  }
 #endif
   return localVideoTrack;
 }
@@ -603,8 +613,16 @@
         [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
                                          restURL:_websocketRestURL
                                         delegate:self];
+    if (_isLoopback) {
+      _loopbackChannel =
+          [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
+                                                   restURL:_websocketRestURL];
+    }
   }
   [_channel registerForRoomId:_roomId clientId:_clientId];
+  if (_isLoopback) {
+    [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
+  }
 }
 
 #pragma mark - Defaults
@@ -637,8 +655,9 @@
   if (_defaultPeerConnectionConstraints) {
     return _defaultPeerConnectionConstraints;
   }
+  NSString *value = _isLoopback ? @"false" : @"true";
   NSArray *optionalConstraints = @[
-      [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"]
+      [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:value]
   ];
   RTCMediaConstraints* constraints =
       [[RTCMediaConstraints alloc]
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDAppEngineClient.m b/webrtc/examples/objc/AppRTCDemo/ARDAppEngineClient.m
index 4318e6b..cd0704a 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDAppEngineClient.m
+++ b/webrtc/examples/objc/AppRTCDemo/ARDAppEngineClient.m
@@ -22,6 +22,8 @@
     @"https://apprtc.appspot.com";
 static NSString * const kARDRoomServerJoinFormat =
     @"https://apprtc.appspot.com/join/%@";
+static NSString * const kARDRoomServerJoinFormatLoopback =
+    @"https://apprtc.appspot.com/join/%@?debug=loopback";
 static NSString * const kARDRoomServerMessageFormat =
     @"https://apprtc.appspot.com/message/%@/%@";
 static NSString * const kARDRoomServerLeaveFormat =
@@ -35,12 +37,20 @@
 #pragma mark - ARDRoomServerClient
 
 - (void)joinRoomWithRoomId:(NSString *)roomId
+                isLoopback:(BOOL)isLoopback
          completionHandler:(void (^)(ARDJoinResponse *response,
                                      NSError *error))completionHandler {
   NSParameterAssert(roomId.length);
 
-  NSString *urlString =
-      [NSString stringWithFormat:kARDRoomServerJoinFormat, roomId];
+  NSString *urlString = nil;
+  if (isLoopback) {
+    urlString =
+        [NSString stringWithFormat:kARDRoomServerJoinFormatLoopback, roomId];
+  } else {
+    urlString =
+        [NSString stringWithFormat:kARDRoomServerJoinFormat, roomId];
+  }
+
   NSURL *roomURL = [NSURL URLWithString:urlString];
   RTCLog(@"Joining room:%@ on room server.", roomId);
   NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:roomURL];
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDRoomServerClient.h b/webrtc/examples/objc/AppRTCDemo/ARDRoomServerClient.h
index a9ff825..70694a8 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDRoomServerClient.h
+++ b/webrtc/examples/objc/AppRTCDemo/ARDRoomServerClient.h
@@ -17,6 +17,7 @@
 @protocol ARDRoomServerClient <NSObject>
 
 - (void)joinRoomWithRoomId:(NSString *)roomId
+                isLoopback:(BOOL)isLoopback
          completionHandler:(void (^)(ARDJoinResponse *response,
                                      NSError *error))completionHandler;
 
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.h b/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.h
index 2bd6264..ffb0b72 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.h
+++ b/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.h
@@ -29,3 +29,13 @@
 - (void)sendMessage:(ARDSignalingMessage *)message;
 
 @end
+
+// Loopback mode is used to cause the client to connect to itself for testing.
+// A second web socket connection is established simulating the other client.
+// Any messages received are sent back to the WebSocket server after modifying
+// them as appropriate.
+@interface ARDLoopbackWebSocketChannel : ARDWebSocketChannel
+
+- (instancetype)initWithURL:(NSURL *)url restURL:(NSURL *)restURL;
+
+@end
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.m b/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.m
index 395a22b..90cadc6 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.m
+++ b/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.m
@@ -13,6 +13,7 @@
 #import "RTCLogging.h"
 #import "SRWebSocket.h"
 
+#import "ARDSignalingMessage.h"
 #import "ARDUtilities.h"
 
 // TODO(tkchin): move these to a configuration object.
@@ -197,3 +198,53 @@
 }
 
 @end
+
+@interface ARDLoopbackWebSocketChannel () <ARDSignalingChannelDelegate>
+@end
+
+@implementation ARDLoopbackWebSocketChannel
+
+- (instancetype)initWithURL:(NSURL *)url restURL:(NSURL *)restURL {
+  return [super initWithURL:url restURL:restURL delegate:self];
+}
+
+#pragma mark - ARDSignalingChannelDelegate
+
+- (void)channel:(id<ARDSignalingChannel>)channel
+    didReceiveMessage:(ARDSignalingMessage *)message {
+  switch (message.type) {
+    case kARDSignalingMessageTypeOffer: {
+      // Change message to answer, send back to server.
+      ARDSessionDescriptionMessage *sdpMessage =
+          (ARDSessionDescriptionMessage *)message;
+      RTCSessionDescription *description = sdpMessage.sessionDescription;
+      NSString *dsc = description.description;
+      dsc = [dsc stringByReplacingOccurrencesOfString:@"offer"
+                                           withString:@"answer"];
+      RTCSessionDescription *answerDescription =
+          [[RTCSessionDescription alloc] initWithType:@"answer" sdp:dsc];
+      ARDSignalingMessage *answer =
+          [[ARDSessionDescriptionMessage alloc]
+               initWithDescription:answerDescription];
+      [self sendMessage:answer];
+      break;
+    }
+    case kARDSignalingMessageTypeAnswer:
+      // Should not receive answer in loopback scenario.
+      break;
+    case kARDSignalingMessageTypeCandidate:
+      // Send back to server.
+      [self sendMessage:message];
+      break;
+    case kARDSignalingMessageTypeBye:
+      // Nothing to do.
+      return;
+  }
+}
+
+- (void)channel:(id<ARDSignalingChannel>)channel
+    didChangeState:(ARDSignalingChannelState)state {
+}
+
+@end
+
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
index f091ad0..a77cc8c 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
@@ -14,7 +14,10 @@
 
 @protocol ARDMainViewDelegate <NSObject>
 
-- (void)mainView:(ARDMainView *)mainView didInputRoom:(NSString *)room;
+- (void)mainView:(ARDMainView *)mainView
+    didInputRoom:(NSString *)room
+      isLoopback:(BOOL)isLoopback
+     isAudioOnly:(BOOL)isAudioOnly;
 
 @end
 
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
index 295b59c..3c9e46e 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
@@ -18,6 +18,7 @@
 static CGFloat const kRoomTextButtonSize = 40;
 static CGFloat const kRoomTextFieldHeight = 40;
 static CGFloat const kRoomTextFieldMargin = 8;
+static CGFloat const kCallControlMargin = 8;
 static CGFloat const kAppLabelHeight = 20;
 
 @class ARDRoomTextField;
@@ -29,6 +30,7 @@
 // Helper view that contains a text field and a clear button.
 @interface ARDRoomTextField : UIView <UITextFieldDelegate>
 @property(nonatomic, weak) id<ARDRoomTextFieldDelegate> delegate;
+@property(nonatomic, readonly) NSString *roomText;
 @end
 
 @implementation ARDRoomTextField {
@@ -88,6 +90,10 @@
   return size;
 }
 
+- (NSString *)roomText {
+  return _roomText.text;
+}
+
 #pragma mark - UITextFieldDelegate
 
 - (void)textFieldDidEndEditing:(UITextField *)textField {
@@ -125,6 +131,12 @@
 @implementation ARDMainView {
   UILabel *_appLabel;
   ARDRoomTextField *_roomText;
+  UILabel *_callOptionsLabel;
+  UISwitch *_audioOnlySwitch;
+  UILabel *_audioOnlyLabel;
+  UISwitch *_loopbackSwitch;
+  UILabel *_loopbackLabel;
+  UIButton *_startCallButton;
 }
 
 @synthesize delegate = _delegate;
@@ -142,6 +154,58 @@
     _roomText.delegate = self;
     [self addSubview:_roomText];
 
+    UIFont *controlFont = [UIFont fontWithName:@"Roboto" size:20];
+    UIColor *controlFontColor = [UIColor colorWithWhite:0 alpha:.6];
+
+    _callOptionsLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+    _callOptionsLabel.text = @"Call Options";
+    _callOptionsLabel.font = controlFont;
+    _callOptionsLabel.textColor = controlFontColor;
+    [_callOptionsLabel sizeToFit];
+    [self addSubview:_callOptionsLabel];
+
+    _audioOnlySwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
+    [_audioOnlySwitch sizeToFit];
+    [self addSubview:_audioOnlySwitch];
+
+    _audioOnlyLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+    _audioOnlyLabel.text = @"Audio only";
+    _audioOnlyLabel.font = controlFont;
+    _audioOnlyLabel.textColor = controlFontColor;
+    [_audioOnlyLabel sizeToFit];
+    [self addSubview:_audioOnlyLabel];
+
+    _loopbackSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
+    [_loopbackSwitch sizeToFit];
+    [self addSubview:_loopbackSwitch];
+
+    _loopbackLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+    _loopbackLabel.text = @"Loopback mode";
+    _loopbackLabel.font = controlFont;
+    _loopbackLabel.textColor = controlFontColor;
+    [_loopbackLabel sizeToFit];
+    [self addSubview:_loopbackLabel];
+
+    _startCallButton = [[UIButton alloc] initWithFrame:CGRectZero];
+
+    _startCallButton = [UIButton buttonWithType:UIButtonTypeSystem];
+    _startCallButton.backgroundColor = [UIColor blueColor];
+    _startCallButton.layer.cornerRadius = 10;
+    _startCallButton.clipsToBounds = YES;
+    _startCallButton.contentEdgeInsets = UIEdgeInsetsMake(5, 10, 5, 10);
+    [_startCallButton setTitle:@"Start call"
+                      forState:UIControlStateNormal];
+    _startCallButton.titleLabel.font = controlFont;
+    [_startCallButton setTitleColor:[UIColor whiteColor]
+                           forState:UIControlStateNormal];
+    [_startCallButton setTitleColor:[UIColor lightGrayColor]
+                           forState:UIControlStateSelected];
+    [_startCallButton sizeToFit];
+    [_startCallButton addTarget:self
+                         action:@selector(onStartCall:)
+               forControlEvents:UIControlEventTouchUpInside];
+    [self addSubview:_startCallButton];
+
     self.backgroundColor = [UIColor whiteColor];
   }
   return self;
@@ -156,13 +220,69 @@
                                roomTextWidth,
                                roomTextHeight);
   _appLabel.center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
+
+  CGFloat callOptionsLabelTop =
+      CGRectGetMaxY(_roomText.frame) + kCallControlMargin * 4;
+  _callOptionsLabel.frame = CGRectMake(kCallControlMargin,
+                                       callOptionsLabelTop,
+                                       _callOptionsLabel.frame.size.width,
+                                       _callOptionsLabel.frame.size.height);
+
+  CGFloat audioOnlyTop =
+      CGRectGetMaxY(_callOptionsLabel.frame) + kCallControlMargin * 2;
+  CGRect audioOnlyRect = CGRectMake(kCallControlMargin * 3,
+                                    audioOnlyTop,
+                                    _audioOnlySwitch.frame.size.width,
+                                    _audioOnlySwitch.frame.size.height);
+  _audioOnlySwitch.frame = audioOnlyRect;
+  CGFloat audioOnlyLabelCenterX = CGRectGetMaxX(audioOnlyRect) +
+      kCallControlMargin + _audioOnlyLabel.frame.size.width / 2;
+  _audioOnlyLabel.center = CGPointMake(audioOnlyLabelCenterX,
+                                       CGRectGetMidY(audioOnlyRect));
+
+  CGFloat loopbackModeTop =
+      CGRectGetMaxY(_audioOnlySwitch.frame) + kCallControlMargin;
+  CGRect loopbackModeRect = CGRectMake(kCallControlMargin * 3,
+                                       loopbackModeTop,
+                                       _loopbackSwitch.frame.size.width,
+                                       _loopbackSwitch.frame.size.height);
+  _loopbackSwitch.frame = loopbackModeRect;
+  CGFloat loopbackModeLabelCenterX = CGRectGetMaxX(loopbackModeRect) +
+      kCallControlMargin + _loopbackLabel.frame.size.width / 2;
+  _loopbackLabel.center = CGPointMake(loopbackModeLabelCenterX,
+                                      CGRectGetMidY(loopbackModeRect));
+
+  CGFloat startCallTop =
+     CGRectGetMaxY(loopbackModeRect) + kCallControlMargin * 3;
+  _startCallButton.frame = CGRectMake(kCallControlMargin,
+                                      startCallTop,
+                                      _startCallButton.frame.size.width,
+                                      _startCallButton.frame.size.height);
 }
 
 #pragma mark - ARDRoomTextFieldDelegate
 
 - (void)roomTextField:(ARDRoomTextField *)roomTextField
          didInputRoom:(NSString *)room {
-  [_delegate mainView:self didInputRoom:room];
+  [_delegate mainView:self
+         didInputRoom:room
+           isLoopback:NO
+          isAudioOnly:_audioOnlySwitch.isOn];
+}
+
+#pragma mark - Private
+
+- (void)onStartCall:(id)sender {
+  NSString *room = _roomText.roomText;
+  // If this is a loopback call, allow a generated room name.
+  if (!room.length && _loopbackSwitch.isOn) {
+    room = [[NSUUID UUID] UUIDString];
+  }
+  room = [room stringByReplacingOccurrencesOfString:@"-" withString:@""];
+  [_delegate mainView:self
+         didInputRoom:room
+           isLoopback:_loopbackSwitch.isOn
+          isAudioOnly:_audioOnlySwitch.isOn];
 }
 
 @end
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
index 3721fe9..8de6f6a1 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
@@ -32,8 +32,12 @@
 
 #pragma mark - ARDMainViewDelegate
 
-- (void)mainView:(ARDMainView *)mainView didInputRoom:(NSString *)room {
+- (void)mainView:(ARDMainView *)mainView
+    didInputRoom:(NSString *)room
+      isLoopback:(BOOL)isLoopback
+     isAudioOnly:(BOOL)isAudioOnly {
   if (!room.length) {
+    [self showAlertWithMessage:@"Missing room name."];
     return;
   }
   // Trim whitespaces.
@@ -63,7 +67,9 @@
 
   // Kick off the video call.
   ARDVideoCallViewController *videoCallViewController =
-      [[ARDVideoCallViewController alloc] initForRoom:trimmedRoom];
+      [[ARDVideoCallViewController alloc] initForRoom:trimmedRoom
+                                           isLoopback:isLoopback
+                                          isAudioOnly:isAudioOnly];
   videoCallViewController.modalTransitionStyle =
       UIModalTransitionStyleCrossDissolve;
   [self presentViewController:videoCallViewController
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.h b/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.h
index 9616da5..7dee2d8 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.h
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.h
@@ -12,6 +12,8 @@
 
 @interface ARDVideoCallViewController : UIViewController
 
-- (instancetype)initForRoom:(NSString *)room;
+- (instancetype)initForRoom:(NSString *)room
+                 isLoopback:(BOOL)isLoopback
+                isAudioOnly:(BOOL)isAudioOnly;
 
 @end
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m
index 674c97b..8de6b95 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m
@@ -31,10 +31,14 @@
 
 @synthesize videoCallView = _videoCallView;
 
-- (instancetype)initForRoom:(NSString *)room {
+- (instancetype)initForRoom:(NSString *)room
+                 isLoopback:(BOOL)isLoopback
+                isAudioOnly:(BOOL)isAudioOnly {
   if (self = [super init]) {
     _client = [[ARDAppClient alloc] initWithDelegate:self];
-    [_client connectToRoomWithId:room options:nil];
+    [_client connectToRoomWithId:room
+                      isLoopback:isLoopback
+                     isAudioOnly:isAudioOnly];
   }
   return self;
 }
diff --git a/webrtc/examples/objc/AppRTCDemo/mac/APPRTCViewController.m b/webrtc/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
index dec41ea..80b0691 100644
--- a/webrtc/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
+++ b/webrtc/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
@@ -280,7 +280,7 @@
         didEnterRoomId:(NSString*)roomId {
   [_client disconnect];
   ARDAppClient *client = [[ARDAppClient alloc] initWithDelegate:self];
-  [client connectToRoomWithId:roomId options:nil];
+  [client connectToRoomWithId:roomId isLoopback:NO isAudioOnly:NO];
   _client = client;
 }
 
diff --git a/webrtc/examples/objc/AppRTCDemo/tests/ARDAppClientTest.mm b/webrtc/examples/objc/AppRTCDemo/tests/ARDAppClientTest.mm
index b131931..2470e50 100644
--- a/webrtc/examples/objc/AppRTCDemo/tests/ARDAppClientTest.mm
+++ b/webrtc/examples/objc/AppRTCDemo/tests/ARDAppClientTest.mm
@@ -138,7 +138,7 @@
                                                   NSError *error);
     [invocation getArgument:&completionHandler atIndex:3];
     completionHandler(joinResponse, nil);
-  }] joinRoomWithRoomId:roomId completionHandler:[OCMArg any]];
+  }] joinRoomWithRoomId:roomId isLoopback:NO completionHandler:[OCMArg any]];
 
   // Return message response from above on join.
   [[[mockRoomServerClient stub] andDo:^(NSInvocation *invocation) {
@@ -278,8 +278,8 @@
   weakAnswerer = answerer;
 
   // Kick off connection.
-  [caller connectToRoomWithId:roomId options:nil];
-  [answerer connectToRoomWithId:roomId options:nil];
+  [caller connectToRoomWithId:roomId isLoopback:NO isAudioOnly:NO];
+  [answerer connectToRoomWithId:roomId isLoopback:NO isAudioOnly:NO];
   [self waitForExpectationsWithTimeout:20 handler:^(NSError *error) {
     if (error) {
       NSLog(@"Expectations error: %@", error);