Adding AecDump functionality to AppRTCDemo for iOS

BUG=webrtc:6229

Review-Url: https://codereview.webrtc.org/2253013006
Cr-Commit-Position: refs/heads/master@{#13927}
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDAppClient+Internal.h b/webrtc/examples/objc/AppRTCDemo/ARDAppClient+Internal.h
index f2da413..cdcac96 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDAppClient+Internal.h
+++ b/webrtc/examples/objc/AppRTCDemo/ARDAppClient+Internal.h
@@ -43,6 +43,8 @@
 @property(nonatomic, strong) NSURL *webSocketRestURL;
 @property(nonatomic, readonly) BOOL isLoopback;
 @property(nonatomic, readonly) BOOL isAudioOnly;
+@property(nonatomic, readonly) BOOL shouldMakeAecDump;
+@property(nonatomic, assign) BOOL isAecDumpActive;
 
 @property(nonatomic, strong)
     RTCMediaConstraints *defaultPeerConnectionConstraints;
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.h b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.h
index b7f7546..95e20d4 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.h
+++ b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.h
@@ -64,9 +64,11 @@
 // Establishes a connection with the AppRTC servers for the given room id.
 // If |isLoopback| is true, the call will connect to itself.
 // If |isAudioOnly| is true, video will be disabled for the call.
+// If |shouldMakeAecDump| is true, an aecdump will be created for the call.
 - (void)connectToRoomWithId:(NSString *)roomId
                  isLoopback:(BOOL)isLoopback
-                isAudioOnly:(BOOL)isAudioOnly;
+                isAudioOnly:(BOOL)isAudioOnly
+          shouldMakeAecDump:(BOOL)shouldMakeAecDump;
 
 // 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 43eb6f4..a9dd8b1 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
+++ b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
@@ -129,6 +129,8 @@
     _defaultPeerConnectionConstraints;
 @synthesize isLoopback = _isLoopback;
 @synthesize isAudioOnly = _isAudioOnly;
+@synthesize shouldMakeAecDump = _shouldMakeAecDump;
+@synthesize isAecDumpActive = _isAecDumpActive;
 
 - (instancetype)init {
   if (self = [super init]) {
@@ -220,11 +222,13 @@
 
 - (void)connectToRoomWithId:(NSString *)roomId
                  isLoopback:(BOOL)isLoopback
-                isAudioOnly:(BOOL)isAudioOnly {
+                isAudioOnly:(BOOL)isAudioOnly
+          shouldMakeAecDump:(BOOL)shouldMakeAecDump {
   NSParameterAssert(roomId.length);
   NSParameterAssert(_state == kARDAppClientStateDisconnected);
   _isLoopback = isLoopback;
   _isAudioOnly = isAudioOnly;
+  _shouldMakeAecDump = shouldMakeAecDump;
   self.state = kARDAppClientStateConnecting;
 
 #if defined(WEBRTC_IOS)
@@ -309,6 +313,10 @@
   _hasReceivedSdp = NO;
   _messageQueue = [NSMutableArray array];
 #if defined(WEBRTC_IOS)
+  if (_isAecDumpActive) {
+    [_factory stopAecDump];
+    _isAecDumpActive = NO;
+  }
   [_peerConnection stopRtcEventLog];
 #endif
   _peerConnection = nil;
@@ -562,6 +570,22 @@
       RTCLogError(@"Failed to start event logging.");
     }
   }
+
+  // Start aecdump diagnostic recording.
+  if (_shouldMakeAecDump) {
+    _isAecDumpActive = YES;
+    NSString *filePath = [self documentsFilePathForFileName:@"audio.aecdump"];
+    int fd = open(filePath.UTF8String, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+    if (fd < 0) {
+      RTCLogError(@"Failed to create the aecdump file!");
+      _isAecDumpActive = NO;
+    } else {
+      if (![_factory startAecDumpWithFileDescriptor:fd maxFileSizeInBytes:-1]) {
+        RTCLogError(@"Failed to create aecdump.");
+        _isAecDumpActive = NO;
+      }
+    }
+  }
 #endif
 }
 
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
index a783ca1..e9f4991 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
@@ -15,10 +15,11 @@
 @protocol ARDMainViewDelegate <NSObject>
 
 - (void)mainView:(ARDMainView *)mainView
-      didInputRoom:(NSString *)room
-        isLoopback:(BOOL)isLoopback
-       isAudioOnly:(BOOL)isAudioOnly
-    useManualAudio:(BOOL)useManualAudio;
+         didInputRoom:(NSString *)room
+           isLoopback:(BOOL)isLoopback
+          isAudioOnly:(BOOL)isAudioOnly
+    shouldMakeAecDump:(BOOL)shouldMakeAecDump
+       useManualAudio:(BOOL)useManualAudio;
 
 - (void)mainViewDidToggleAudioLoop:(ARDMainView *)mainView;
 
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
index ef55555..59b428a 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
@@ -119,6 +119,8 @@
   UILabel *_callOptionsLabel;
   UISwitch *_audioOnlySwitch;
   UILabel *_audioOnlyLabel;
+  UISwitch *_aecdumpSwitch;
+  UILabel *_aecdumpLabel;
   UISwitch *_loopbackSwitch;
   UILabel *_loopbackLabel;
   UISwitch *_useManualAudioSwitch;
@@ -174,6 +176,17 @@
     [_loopbackLabel sizeToFit];
     [self addSubview:_loopbackLabel];
 
+    _aecdumpSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
+    [_aecdumpSwitch sizeToFit];
+    [self addSubview:_aecdumpSwitch];
+
+    _aecdumpLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+    _aecdumpLabel.text = @"Create AecDump";
+    _aecdumpLabel.font = controlFont;
+    _aecdumpLabel.textColor = controlFontColor;
+    [_aecdumpLabel sizeToFit];
+    [self addSubview:_aecdumpLabel];
+
     _useManualAudioSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
     [_useManualAudioSwitch sizeToFit];
     _useManualAudioSwitch.on = YES;
@@ -274,8 +287,21 @@
   _loopbackLabel.center = CGPointMake(loopbackModeLabelCenterX,
                                       CGRectGetMidY(loopbackModeRect));
 
-  CGFloat useManualAudioTop =
+  CGFloat aecdumpModeTop =
       CGRectGetMaxY(_loopbackSwitch.frame) + kCallControlMargin;
+  CGRect aecdumpModeRect = CGRectMake(kCallControlMargin * 3,
+                                      aecdumpModeTop,
+                                      _aecdumpSwitch.frame.size.width,
+                                      _aecdumpSwitch.frame.size.height);
+  _aecdumpSwitch.frame = aecdumpModeRect;
+  CGFloat aecdumpModeLabelCenterX = CGRectGetMaxX(aecdumpModeRect) +
+      kCallControlMargin + _aecdumpLabel.frame.size.width / 2;
+  _aecdumpLabel.center = CGPointMake(aecdumpModeLabelCenterX,
+                                     CGRectGetMidY(aecdumpModeRect));
+
+
+  CGFloat useManualAudioTop =
+      CGRectGetMaxY(_aecdumpSwitch.frame) + kCallControlMargin;
   CGRect useManualAudioRect =
       CGRectMake(kCallControlMargin * 3,
                  useManualAudioTop,
@@ -334,6 +360,7 @@
                 didInputRoom:room
                   isLoopback:_loopbackSwitch.isOn
                  isAudioOnly:_audioOnlySwitch.isOn
+           shouldMakeAecDump:_aecdumpSwitch.isOn
               useManualAudio:_useManualAudioSwitch.isOn];
 }
 
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
index a684ba3..21f00cb 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
@@ -54,9 +54,10 @@
 #pragma mark - ARDMainViewDelegate
 
 - (void)mainView:(ARDMainView *)mainView
-      didInputRoom:(NSString *)room
-        isLoopback:(BOOL)isLoopback
-       isAudioOnly:(BOOL)isAudioOnly
+         didInputRoom:(NSString *)room
+           isLoopback:(BOOL)isLoopback
+          isAudioOnly:(BOOL)isAudioOnly
+    shouldMakeAecDump:(BOOL)shouldMakeAecDump
     useManualAudio:(BOOL)useManualAudio {
   if (!room.length) {
     [self showAlertWithMessage:@"Missing room name."];
@@ -96,6 +97,7 @@
       [[ARDVideoCallViewController alloc] initForRoom:trimmedRoom
                                            isLoopback:isLoopback
                                           isAudioOnly:isAudioOnly
+                                    shouldMakeAecDump:shouldMakeAecDump
                                              delegate:self];
   videoCallViewController.modalTransitionStyle =
       UIModalTransitionStyleCrossDissolve;
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.h b/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.h
index f3a9554..ffffd12 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.h
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.h
@@ -24,6 +24,7 @@
 - (instancetype)initForRoom:(NSString *)room
                  isLoopback:(BOOL)isLoopback
                 isAudioOnly:(BOOL)isAudioOnly
+          shouldMakeAecDump:(BOOL)shouldMakeAecDump
                    delegate:(id<ARDVideoCallViewControllerDelegate>)delegate;
 
 @end
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m
index d35e1e6..bb49ca4 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDVideoCallViewController.m
@@ -39,13 +39,15 @@
 - (instancetype)initForRoom:(NSString *)room
                  isLoopback:(BOOL)isLoopback
                 isAudioOnly:(BOOL)isAudioOnly
+          shouldMakeAecDump:(BOOL)shouldMakeAecDump
                    delegate:(id<ARDVideoCallViewControllerDelegate>)delegate {
   if (self = [super init]) {
     _delegate = delegate;
     _client = [[ARDAppClient alloc] initWithDelegate:self];
     [_client connectToRoomWithId:room
                       isLoopback:isLoopback
-                     isAudioOnly:isAudioOnly];
+                     isAudioOnly:isAudioOnly
+               shouldMakeAecDump:shouldMakeAecDump];
   }
   return self;
 }
diff --git a/webrtc/examples/objc/AppRTCDemo/mac/APPRTCViewController.m b/webrtc/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
index 9144036..0f4a54e 100644
--- a/webrtc/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
+++ b/webrtc/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
@@ -284,7 +284,7 @@
         didEnterRoomId:(NSString*)roomId {
   [_client disconnect];
   ARDAppClient *client = [[ARDAppClient alloc] initWithDelegate:self];
-  [client connectToRoomWithId:roomId isLoopback:NO isAudioOnly:NO];
+  [client connectToRoomWithId:roomId isLoopback:NO isAudioOnly:NO shouldMakeAecDump:NO];
   _client = client;
 }
 
diff --git a/webrtc/examples/objc/AppRTCDemo/tests/ARDAppClientTest.mm b/webrtc/examples/objc/AppRTCDemo/tests/ARDAppClientTest.mm
index 169f999..355031e 100644
--- a/webrtc/examples/objc/AppRTCDemo/tests/ARDAppClientTest.mm
+++ b/webrtc/examples/objc/AppRTCDemo/tests/ARDAppClientTest.mm
@@ -283,8 +283,8 @@
   weakAnswerer = answerer;
 
   // Kick off connection.
-  [caller connectToRoomWithId:roomId isLoopback:NO isAudioOnly:NO];
-  [answerer connectToRoomWithId:roomId isLoopback:NO isAudioOnly:NO];
+  [caller connectToRoomWithId:roomId isLoopback:NO isAudioOnly:NO shouldMakeAecDump:NO];
+  [answerer connectToRoomWithId:roomId isLoopback:NO isAudioOnly:NO shouldMakeAecDump:NO];
   [self waitForExpectationsWithTimeout:20 handler:^(NSError *error) {
     if (error) {
       NSLog(@"Expectations error: %@", error);
diff --git a/webrtc/sdk/objc/Framework/Classes/RTCPeerConnectionFactory.mm b/webrtc/sdk/objc/Framework/Classes/RTCPeerConnectionFactory.mm
index 9c9fd75..0ac2ec4 100644
--- a/webrtc/sdk/objc/Framework/Classes/RTCPeerConnectionFactory.mm
+++ b/webrtc/sdk/objc/Framework/Classes/RTCPeerConnectionFactory.mm
@@ -48,6 +48,19 @@
   return self;
 }
 
+- (BOOL)startAecDumpWithFileDescriptor:(int)fileDescriptor
+                    maxFileSizeInBytes:(int)maxFileSizeInBytes {
+  // Pass the file to the recorder. The file ownership
+  // is passed to the recorder, and the recorder
+  // closes the file when needed.
+  return _nativeFactory->StartAecDump(fileDescriptor, maxFileSizeInBytes);
+}
+
+- (void)stopAecDump {
+  // The file is closed by the call below.
+  _nativeFactory->StopAecDump();
+}
+
 - (RTCAVFoundationVideoSource *)avFoundationVideoSourceWithConstraints:
     (nullable RTCMediaConstraints *)constraints {
   return [[RTCAVFoundationVideoSource alloc] initWithFactory:self
diff --git a/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCPeerConnectionFactory.h b/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCPeerConnectionFactory.h
index f21c107..abf2373 100644
--- a/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCPeerConnectionFactory.h
+++ b/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCPeerConnectionFactory.h
@@ -53,6 +53,16 @@
                                               delegate:
     (nullable id<RTCPeerConnectionDelegate>)delegate;
 
+/** Start an aecdump recording with a file descriptor and
+    a specified maximum size limit (-1 specifies that no
+    limit should be used).
+    This API call will likely change in the future */
+- (BOOL)startAecDumpWithFileDescriptor:(int)fileDescriptor
+                    maxFileSizeInBytes:(int)maxFileSizeInBytes;
+
+/* Stop an active aecdump recording */
+- (void)stopAecDump;
+
 @end
 
 NS_ASSUME_NONNULL_END