Reland "Add file capturer to AppRTCMobile on simulator."

This is a reland of 5adcd198752b651f7b7e9199a91f9b873b7d7237
Original change's description:
> Add file capturer to AppRTCMobile on simulator.
> 
> To achieve this, the CL does the following
> - Adds sample mp4 video
> - Refactors the existing RTCFileVideoCapturer to achieve continious
> capture and adds tests.
> 
> Bug: webrtc:8406
> Change-Id: Ibc0891176c58ec9053b42e340d2113036e7199ec
> Reviewed-on: https://webrtc-review.googlesource.com/12180
> Reviewed-by: Anders Carlsson <andersc@webrtc.org>
> Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
> Commit-Queue: Daniela Jovanoska Petrenko <denicija@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#20598}

Bug: webrtc:8406
Change-Id: I93be89b86e342a9a8195e19ebaf4aef1410d2c20
Reviewed-on: https://webrtc-review.googlesource.com/23200
Reviewed-by: Daniela Jovanoska Petrenko <denicija@webrtc.org>
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Commit-Queue: Daniela Jovanoska Petrenko <denicija@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20870}
diff --git a/examples/BUILD.gn b/examples/BUILD.gn
index 740ebe5..eef20d4 100644
--- a/examples/BUILD.gn
+++ b/examples/BUILD.gn
@@ -249,6 +249,8 @@
       testonly = true
       sources = [
         "objc/AppRTCMobile/ios/ARDAppDelegate.m",
+        "objc/AppRTCMobile/ios/ARDFileCaptureController.h",
+        "objc/AppRTCMobile/ios/ARDFileCaptureController.m",
         "objc/AppRTCMobile/ios/ARDMainView.h",
         "objc/AppRTCMobile/ios/ARDMainView.m",
         "objc/AppRTCMobile/ios/ARDMainViewController.h",
@@ -311,6 +313,9 @@
     bundle_data("AppRTCMobile_ios_bundle_data") {
       sources = [
         "objc/AppRTCMobile/ios/resources/Roboto-Regular.ttf",
+
+        # Sample video taken from https://media.xiph.org/video/derf/
+        "objc/AppRTCMobile/ios/resources/foreman.mp4",
         "objc/AppRTCMobile/ios/resources/iPhone5@2x.png",
         "objc/AppRTCMobile/ios/resources/iPhone6@2x.png",
         "objc/AppRTCMobile/ios/resources/iPhone6p@3x.png",
@@ -420,6 +425,7 @@
         testonly = true
         sources = [
           "objc/AppRTCMobile/tests/ARDAppClient_xctest.mm",
+          "objc/AppRTCMobile/tests/ARDFileCaptureController_xctest.mm",
           "objc/AppRTCMobile/tests/ARDSettingsModel_xctest.mm",
         ]
         deps = [
diff --git a/examples/objc/AppRTCMobile/ARDAppClient.h b/examples/objc/AppRTCMobile/ARDAppClient.h
index 8c27b34..5054c28 100644
--- a/examples/objc/AppRTCMobile/ARDAppClient.h
+++ b/examples/objc/AppRTCMobile/ARDAppClient.h
@@ -9,8 +9,6 @@
  */
 
 #import <Foundation/Foundation.h>
-
-#import "WebRTC/RTCCameraVideoCapturer.h"
 #import "WebRTC/RTCPeerConnection.h"
 #import "WebRTC/RTCVideoTrack.h"
 
@@ -26,6 +24,8 @@
 @class ARDAppClient;
 @class ARDSettingsModel;
 @class RTCMediaConstraints;
+@class RTCCameraVideoCapturer;
+@class RTCFileVideoCapturer;
 
 // The delegate is informed of pertinent events and will be called on the
 // main queue.
@@ -52,6 +52,10 @@
 - (void)appClient:(ARDAppClient *)client
       didGetStats:(NSArray *)stats;
 
+@optional
+- (void)appClient:(ARDAppClient *)client
+didCreateLocalFileCapturer:(RTCFileVideoCapturer *)fileCapturer;
+
 @end
 
 // Handles connections to the AppRTC server for a given room. Methods on this
diff --git a/examples/objc/AppRTCMobile/ARDAppClient.m b/examples/objc/AppRTCMobile/ARDAppClient.m
index f753828..10be40a 100644
--- a/examples/objc/AppRTCMobile/ARDAppClient.m
+++ b/examples/objc/AppRTCMobile/ARDAppClient.m
@@ -15,6 +15,7 @@
 #import "WebRTC/RTCCameraVideoCapturer.h"
 #import "WebRTC/RTCConfiguration.h"
 #import "WebRTC/RTCFileLogger.h"
+#import "WebRTC/RTCFileVideoCapturer.h"
 #import "WebRTC/RTCIceServer.h"
 #import "WebRTC/RTCLogging.h"
 #import "WebRTC/RTCMediaConstraints.h"
@@ -690,21 +691,26 @@
 }
 
 - (RTCVideoTrack *)createLocalVideoTrack {
-  RTCVideoTrack* localVideoTrack = nil;
-  // The iOS simulator doesn't provide any sort of camera capture
-  // support or emulation (http://goo.gl/rHAnC1) so don't bother
-  // trying to open a local stream.
+  if ([_settings currentAudioOnlySettingFromStore]) {
+    return nil;
+  }
+
+  RTCVideoSource *source = [_factory videoSource];
+
 #if !TARGET_IPHONE_SIMULATOR
-  if (![_settings currentAudioOnlySettingFromStore]) {
-    RTCVideoSource *source = [_factory videoSource];
-    RTCCameraVideoCapturer *capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:source];
-    [_delegate appClient:self didCreateLocalCapturer:capturer];
-    localVideoTrack =
-        [_factory videoTrackWithSource:source
-                               trackId:kARDVideoTrackId];
+  RTCCameraVideoCapturer *capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:source];
+  [_delegate appClient:self didCreateLocalCapturer:capturer];
+
+#else
+#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
+  if (@available(iOS 10, *)) {
+    RTCFileVideoCapturer *fileCapturer = [[RTCFileVideoCapturer alloc] initWithDelegate:source];
+    [_delegate appClient:self didCreateLocalFileCapturer:fileCapturer];
   }
 #endif
-  return localVideoTrack;
+#endif
+
+  return [_factory videoTrackWithSource:source trackId:kARDVideoTrackId];
 }
 
 #pragma mark - Collider methods
diff --git a/examples/objc/AppRTCMobile/ARDSettingsModel.m b/examples/objc/AppRTCMobile/ARDSettingsModel.m
index 0a2bee6..a4eea73 100644
--- a/examples/objc/AppRTCMobile/ARDSettingsModel.m
+++ b/examples/objc/AppRTCMobile/ARDSettingsModel.m
@@ -169,20 +169,12 @@
 
 - (void)registerStoreDefaults {
   NSString *defaultVideoResolutionSetting = [self defaultVideoResolutionSetting];
-  BOOL audioOnly = (defaultVideoResolutionSetting.length == 0);
-
-// The iOS simulator doesn't provide any sort of camera capture
-// support or emulation (http://goo.gl/rHAnC1) so don't bother
-// trying to open a local stream.
-#if TARGET_IPHONE_SIMULATOR
-  audioOnly = YES;
-#endif
 
   NSData *codecData = [NSKeyedArchiver archivedDataWithRootObject:[self defaultVideoCodecSetting]];
   [ARDSettingsStore setDefaultsForVideoResolution:[self defaultVideoResolutionSetting]
                                        videoCodec:codecData
                                           bitrate:nil
-                                        audioOnly:audioOnly
+                                        audioOnly:NO
                                     createAecDump:NO
                                useLevelController:NO
                              useManualAudioConfig:YES];
diff --git a/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.h b/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.h
new file mode 100644
index 0000000..7e0387d
--- /dev/null
+++ b/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright 2017 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#import <Foundation/Foundation.h>
+
+@class RTCFileVideoCapturer;
+
+/**
+ * Controls a file capturer.
+ */
+NS_CLASS_AVAILABLE_IOS(10)
+@interface ARDFileCaptureController : NSObject
+
+/**
+ * Creates instance of the controller.
+ *
+ * @param capturer The capturer to be controlled.
+ */
+- (instancetype)initWithCapturer:(RTCFileVideoCapturer *)capturer;
+
+/**
+ * Starts the file capturer.
+ *
+ * Possible errors produced by the capturer will be logged.
+ */
+- (void)startCapture;
+
+/**
+ * Immediately stops capturer.
+ */
+- (void)stopCapture;
+
+@end
diff --git a/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.m b/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.m
new file mode 100644
index 0000000..d61bfe2
--- /dev/null
+++ b/examples/objc/AppRTCMobile/ios/ARDFileCaptureController.m
@@ -0,0 +1,45 @@
+/*
+ *  Copyright 2017 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#import "ARDFileCaptureController.h"
+
+#import "WebRTC/RTCFileVideoCapturer.h"
+
+@interface ARDFileCaptureController ()
+
+@property(nonatomic, strong) RTCFileVideoCapturer *fileCapturer;
+
+@end
+
+@implementation ARDFileCaptureController
+@synthesize fileCapturer = _fileCapturer;
+
+- (instancetype)initWithCapturer:(RTCFileVideoCapturer *)capturer {
+  if (self = [super init]) {
+    _fileCapturer = capturer;
+  }
+  return self;
+}
+
+- (void)startCapture {
+  [self startFileCapture];
+}
+
+- (void)startFileCapture {
+  [self.fileCapturer startCapturingFromFileNamed:@"foreman.mp4"
+                                         onError:^(NSError *_Nonnull error) {
+                                           NSLog(@"Error %@", error.userInfo);
+                                         }];
+}
+
+- (void)stopCapture {
+  [self.fileCapturer stopCapture];
+}
+@end
diff --git a/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m b/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m
index 3deb067..bf349ce 100644
--- a/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m
+++ b/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m
@@ -11,9 +11,11 @@
 #import "ARDVideoCallViewController.h"
 
 #import "WebRTC/RTCAudioSession.h"
+#import "WebRTC/RTCCameraVideoCapturer.h"
 
 #import "ARDAppClient.h"
 #import "ARDCaptureController.h"
+#import "ARDFileCaptureController.h"
 #import "ARDSettingsModel.h"
 #import "ARDVideoCallView.h"
 #import "WebRTC/RTCAVFoundationVideoSource.h"
@@ -32,6 +34,7 @@
   ARDAppClient *_client;
   RTCVideoTrack *_remoteVideoTrack;
   ARDCaptureController *_captureController;
+  ARDFileCaptureController *_fileCaptureController NS_AVAILABLE_IOS(10);
   AVAudioSessionPortOverride _portOverride;
 }
 
@@ -106,6 +109,16 @@
 }
 
 - (void)appClient:(ARDAppClient *)client
+    didCreateLocalFileCapturer:(RTCFileVideoCapturer *)fileCapturer {
+#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
+  if (@available(iOS 10, *)) {
+    _fileCaptureController = [[ARDFileCaptureController alloc] initWithCapturer:fileCapturer];
+    [_fileCaptureController startCapture];
+  }
+#endif
+}
+
+- (void)appClient:(ARDAppClient *)client
     didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack {
 }
 
@@ -191,6 +204,8 @@
   _videoCallView.localVideoView.captureSession = nil;
   [_captureController stopCapture];
   _captureController = nil;
+  [_fileCaptureController stopCapture];
+  _fileCaptureController = nil;
   [_client disconnect];
   [_delegate viewControllerDidFinish:self];
 }
diff --git a/examples/objc/AppRTCMobile/ios/resources/foreman.mp4 b/examples/objc/AppRTCMobile/ios/resources/foreman.mp4
new file mode 100644
index 0000000..ccffbf4
--- /dev/null
+++ b/examples/objc/AppRTCMobile/ios/resources/foreman.mp4
Binary files differ
diff --git a/examples/objc/AppRTCMobile/tests/ARDFileCaptureController_xctest.mm b/examples/objc/AppRTCMobile/tests/ARDFileCaptureController_xctest.mm
new file mode 100644
index 0000000..e734f10
--- /dev/null
+++ b/examples/objc/AppRTCMobile/tests/ARDFileCaptureController_xctest.mm
@@ -0,0 +1,62 @@
+/*
+ *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#import <Foundation/Foundation.h>
+#import <OCMock/OCMock.h>
+#import <XCTest/XCTest.h>
+
+#import "ARDFileCaptureController.h"
+
+#import "WebRTC/RTCFileVideoCapturer.h"
+
+NS_CLASS_AVAILABLE_IOS(10)
+@interface ARDFileCaptureControllerTests : XCTestCase
+
+@property(nonatomic, strong) ARDFileCaptureController *fileCaptureController;
+@property(nonatomic, strong) id fileCapturerMock;
+
+@end
+
+@implementation ARDFileCaptureControllerTests
+
+@synthesize fileCaptureController = _fileCaptureController;
+@synthesize fileCapturerMock = _fileCapturerMock;
+
+- (void)setUp {
+  [super setUp];
+  self.fileCapturerMock = OCMClassMock([RTCFileVideoCapturer class]);
+  self.fileCaptureController =
+      [[ARDFileCaptureController alloc] initWithCapturer:self.fileCapturerMock];
+}
+
+- (void)tearDown {
+  self.fileCaptureController = nil;
+  [self.fileCapturerMock stopMocking];
+  self.fileCapturerMock = nil;
+  [super tearDown];
+}
+
+- (void)testCaptureIsStarted {
+  [[self.fileCapturerMock expect] startCapturingFromFileNamed:[OCMArg any] onError:[OCMArg any]];
+
+  [self.fileCaptureController startCapture];
+
+  [self.fileCapturerMock verify];
+}
+
+- (void)testCaptureIsStoped {
+  [[self.fileCapturerMock expect] stopCapture];
+
+  [self.fileCaptureController stopCapture];
+
+  [self.fileCapturerMock verify];
+}
+
+@end
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index 3d68dd9..0d03dc2 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -282,8 +282,8 @@
       ]
       if (is_ios) {
         sources += [
-          "objc/Framework/Classes/PeerConnection/RTCFileVideoCapturer.h",
           "objc/Framework/Classes/PeerConnection/RTCFileVideoCapturer.m",
+          "objc/Framework/Headers/WebRTC/RTCFileVideoCapturer.h",
         ]
       }
       libs = [ "AVFoundation.framework" ]
@@ -529,20 +529,22 @@
     }
 
     if (rtc_include_tests) {
-      # TODO(denicija):remove second part of this check.
-      if (is_ios && (current_cpu == "arm64" || use_ios_simulator)) {
+      if (is_ios) {
         rtc_source_set("sdk_unittests_sources") {
           testonly = true
           include_dirs = [
             "objc/Framework/Headers",
             "objc/Framework/Classes",
           ]
+
           sources = [
-            # TODO(denicija): Once more sources are included,
-            # move the second part of the check on line 516 here
-            # when adding this file to the sources
-            "objc/Framework/UnitTests/RTCMTLVideoView_xctest.mm",
+            "objc/Framework/UnitTests/RTCFileVideoCapturer_xctest.mm",
           ]
+
+          if (current_cpu == "arm64" || use_ios_simulator) {
+            sources += [ "objc/Framework/UnitTests/RTCMTLVideoView_xctest.mm" ]
+          }
+
           if (use_ios_simulator) {
             # Only include this file on simulator, as it's already
             # included in device builds.
@@ -568,14 +570,26 @@
           ]
         }
 
+        bundle_data("sdk_unittests_bundle_data") {
+          # Sample video taken from https://media.xiph.org/video/derf/
+          sources = [
+            "objc/Framework/UnitTests/foreman.mp4",
+          ]
+          outputs = [
+            "{{bundle_resources_dir}}/{{source_file_part}}",
+          ]
+        }
+
         rtc_ios_xctest_test("sdk_unittests") {
           info_plist = "//test/ios/Info.plist"
           sources = [
             "objc/Framework/UnitTests/main.m",
           ]
+
           _bundle_id_suffix = ios_generic_test_bundle_id_suffix
           extra_substitutions = [ "GTEST_BUNDLE_ID_SUFFIX=$_bundle_id_suffix" ]
           deps = [
+            ":sdk_unittests_bundle_data",
             ":sdk_unittests_sources",
           ]
           ldflags = [ "-all_load" ]
@@ -666,6 +680,7 @@
           "objc/Framework/Headers/WebRTC/RTCDispatcher.h",
           "objc/Framework/Headers/WebRTC/RTCEAGLVideoView.h",
           "objc/Framework/Headers/WebRTC/RTCFieldTrials.h",
+          "objc/Framework/Headers/WebRTC/RTCFileVideoCapturer.h",
           "objc/Framework/Headers/WebRTC/RTCIceCandidate.h",
           "objc/Framework/Headers/WebRTC/RTCIceServer.h",
           "objc/Framework/Headers/WebRTC/RTCIntervalRange.h",
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCFileVideoCapturer.h b/sdk/objc/Framework/Classes/PeerConnection/RTCFileVideoCapturer.h
deleted file mode 100644
index c8793ca..0000000
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCFileVideoCapturer.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- *  Copyright 2017 The WebRTC Project Authors. All rights reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-#import <Foundation/Foundation.h>
-#import <WebRTC/RTCVideoCapturer.h>
-
-/**
- * RTCVideoCapturer that reads buffers from file.
- *
- * Per design, the file capturer can only be run once and once stopped it cannot run again.
- * To run another file capture session, create new instance of the class.
- */
-NS_CLASS_AVAILABLE_IOS(10)
-@interface RTCFileVideoCapturer : RTCVideoCapturer
-
-- (void)startCapturingFromFileNamed:(NSString *)nameOfFile;
-- (void)stopCapture;
-
-@end
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCFileVideoCapturer.m b/sdk/objc/Framework/Classes/PeerConnection/RTCFileVideoCapturer.m
index 178a958..07cb2c6 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCFileVideoCapturer.m
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCFileVideoCapturer.m
@@ -8,60 +8,91 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#import "RTCFileVideoCapturer.h"
+#import "WebRTC/RTCFileVideoCapturer.h"
 
 #import "WebRTC/RTCLogging.h"
 #import "WebRTC/RTCVideoFrameBuffer.h"
 
+NSString *const kRTCFileVideoCapturerErrorDomain = @"org.webrtc.RTCFileVideoCapturer";
+
+typedef NS_ENUM(NSInteger, RTCFileVideoCapturerErrorCode) {
+  RTCFileVideoCapturerErrorCode_CapturerRunning = 2000,
+  RTCFileVideoCapturerErrorCode_FileNotFound
+};
+
+typedef NS_ENUM(NSInteger, RTCFileVideoCapturerStatus) {
+  RTCFileVideoCapturerStatusNotInitialized,
+  RTCFileVideoCapturerStatusStarted,
+  RTCFileVideoCapturerStatusStopped
+};
+
 @implementation RTCFileVideoCapturer {
   AVAssetReader *_reader;
   AVAssetReaderTrackOutput *_outTrack;
-  BOOL _capturerStopped;
+  RTCFileVideoCapturerStatus _status;
   CMTime _lastPresentationTime;
   dispatch_queue_t _frameQueue;
+  NSURL *_fileURL;
 }
 
-- (void)startCapturingFromFileNamed:(NSString *)nameOfFile {
+- (void)startCapturingFromFileNamed:(NSString *)nameOfFile
+                            onError:(RTCFileVideoCapturerErrorBlock)errorBlock {
+  if (_status == RTCFileVideoCapturerStatusStarted) {
+    NSError *error =
+        [NSError errorWithDomain:kRTCFileVideoCapturerErrorDomain
+                            code:RTCFileVideoCapturerErrorCode_CapturerRunning
+                        userInfo:@{NSUnderlyingErrorKey : @"Capturer has been started."}];
+
+    errorBlock(error);
+    return;
+  } else {
+    _status = RTCFileVideoCapturerStatusStarted;
+  }
+
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-    if (_reader && _reader.status == AVAssetReaderStatusReading) {
-      RTCLog("Capturer exists and reads another file. Start capture request failed.");
-      return;
-    }
     NSString *pathForFile = [self pathForFileName:nameOfFile];
     if (!pathForFile) {
-      RTCLog("File %@ not found in bundle", nameOfFile);
+      NSString *errorString =
+          [NSString stringWithFormat:@"File %@ not found in bundle", nameOfFile];
+      NSError *error = [NSError errorWithDomain:kRTCFileVideoCapturerErrorDomain
+                                           code:RTCFileVideoCapturerErrorCode_FileNotFound
+                                       userInfo:@{NSUnderlyingErrorKey : errorString}];
+      errorBlock(error);
       return;
     }
 
     _lastPresentationTime = CMTimeMake(0, 0);
 
-    NSURL *URLForFile = [NSURL fileURLWithPath:pathForFile];
-    AVURLAsset *asset = [AVURLAsset URLAssetWithURL:URLForFile options:nil];
-
-    NSArray *allTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
-    NSError *error = nil;
-    _reader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
-    if (error) {
-      RTCLog("File reader failed with error: %@", error);
-      return;
-    }
-
-    NSDictionary *options = @{
-      (NSString *)
-      kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
-    };
-    _outTrack = [[AVAssetReaderTrackOutput alloc] initWithTrack:allTracks.firstObject
-                                                 outputSettings:options];
-    [_reader addOutput:_outTrack];
-
-    [_reader startReading];
-    RTCLog(@"File capturer started reading");
-    [self readNextBuffer];
+    _fileURL = [NSURL fileURLWithPath:pathForFile];
+    [self setupReaderOnError:errorBlock];
   });
 }
 
+- (void)setupReaderOnError:(RTCFileVideoCapturerErrorBlock)errorBlock {
+  AVURLAsset *asset = [AVURLAsset URLAssetWithURL:_fileURL options:nil];
+
+  NSArray *allTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
+  NSError *error = nil;
+
+  _reader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
+  if (error) {
+    errorBlock(error);
+    return;
+  }
+
+  NSDictionary *options = @{
+    (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)
+  };
+  _outTrack =
+      [[AVAssetReaderTrackOutput alloc] initWithTrack:allTracks.firstObject outputSettings:options];
+  [_reader addOutput:_outTrack];
+
+  [_reader startReading];
+  RTCLog(@"File capturer started reading");
+  [self readNextBuffer];
+}
 - (void)stopCapture {
-  _capturerStopped = YES;
+  _status = RTCFileVideoCapturerStatusStopped;
   RTCLog(@"File capturer stopped.");
 }
 
@@ -88,12 +119,19 @@
 }
 
 - (void)readNextBuffer {
-  if (_reader.status != AVAssetReaderStatusReading || _capturerStopped) {
+  if (_status == RTCFileVideoCapturerStatusStopped) {
     [_reader cancelReading];
     _reader = nil;
     return;
   }
 
+  if (_reader.status == AVAssetReaderStatusCompleted) {
+    [_reader cancelReading];
+    _reader = nil;
+    [self setupReaderOnError:nil];
+    return;
+  }
+
   CMSampleBufferRef sampleBuffer = [_outTrack copyNextSampleBuffer];
   if (!sampleBuffer) {
     [self readNextBuffer];
diff --git a/sdk/objc/Framework/Headers/WebRTC/RTCFileVideoCapturer.h b/sdk/objc/Framework/Headers/WebRTC/RTCFileVideoCapturer.h
new file mode 100644
index 0000000..7b898e4
--- /dev/null
+++ b/sdk/objc/Framework/Headers/WebRTC/RTCFileVideoCapturer.h
@@ -0,0 +1,50 @@
+/*
+ *  Copyright 2017 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#import <Foundation/Foundation.h>
+#import <WebRTC/RTCVideoCapturer.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Error passing block.
+ */
+typedef void (^RTCFileVideoCapturerErrorBlock)(NSError *error);
+
+/**
+ * Captures buffers from bundled video file.
+ *
+ * See @c RTCVideoCapturer for more info on capturers.
+ */
+RTC_EXPORT
+
+NS_CLASS_AVAILABLE_IOS(10)
+@interface RTCFileVideoCapturer : RTCVideoCapturer
+
+/**
+ * Starts asynchronous capture of frames from video file.
+ *
+ * Capturing is not started if error occurs. Underlying error will be
+ * relayed in the errorBlock if one is provided.
+ * Successfully captured video frames will be passed to the delegate.
+ *
+ * @param nameOfFile The name of the bundled video file to be read.
+ * @errorBlock block to be executed upon error.
+ */
+- (void)startCapturingFromFileNamed:(NSString *)nameOfFile
+                            onError:(__nullable RTCFileVideoCapturerErrorBlock)errorBlock;
+
+/**
+ * Immediately stops capture.
+ */
+- (void)stopCapture;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/sdk/objc/Framework/UnitTests/RTCFileVideoCapturer_xctest.mm b/sdk/objc/Framework/UnitTests/RTCFileVideoCapturer_xctest.mm
new file mode 100644
index 0000000..f0c1739
--- /dev/null
+++ b/sdk/objc/Framework/UnitTests/RTCFileVideoCapturer_xctest.mm
@@ -0,0 +1,113 @@
+/*
+ *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#import "WebRTC/RTCFileVideoCapturer.h"
+
+#import <XCTest/XCTest.h>
+
+#include "rtc_base/gunit.h"
+
+NSString *const kTestFileName = @"foreman.mp4";
+static const int kTestTimeoutMs = 5 * 1000;  // 5secs.
+
+@interface MockCapturerDelegate : NSObject <RTCVideoCapturerDelegate>
+
+@property(nonatomic, assign) NSInteger capturedFramesCount;
+
+@end
+
+@implementation MockCapturerDelegate
+@synthesize capturedFramesCount = _capturedFramesCount;
+
+- (void)capturer:(RTCVideoCapturer *)capturer didCaptureVideoFrame:(RTCVideoFrame *)frame {
+  self.capturedFramesCount++;
+}
+
+@end
+
+NS_CLASS_AVAILABLE_IOS(10)
+@interface RTCFileVideoCapturerTests : XCTestCase
+
+@property(nonatomic, strong) RTCFileVideoCapturer *capturer;
+@property(nonatomic, strong) MockCapturerDelegate *mockDelegate;
+
+@end
+
+@implementation RTCFileVideoCapturerTests
+@synthesize capturer = _capturer;
+@synthesize mockDelegate = _mockDelegate;
+
+- (void)setUp {
+  self.mockDelegate = [[MockCapturerDelegate alloc] init];
+  self.capturer = [[RTCFileVideoCapturer alloc] initWithDelegate:self.mockDelegate];
+}
+
+- (void)tearDown {
+  self.capturer = nil;
+  self.mockDelegate = nil;
+}
+
+- (void)testCaptureWhenFileNotInBundle {
+  __block BOOL errorOccured = NO;
+
+  RTCFileVideoCapturerErrorBlock errorBlock = ^void(NSError *error) {
+    errorOccured = YES;
+  };
+
+  [self.capturer startCapturingFromFileNamed:@"not_in_bundle.mov" onError:errorBlock];
+  ASSERT_TRUE_WAIT(errorOccured, kTestTimeoutMs);
+}
+
+- (void)testSecondStartCaptureCallFails {
+  __block BOOL secondError = NO;
+
+  RTCFileVideoCapturerErrorBlock firstErrorBlock = ^void(NSError *error) {
+    // This block should never be called.
+    NSLog(@"Error: %@", [error userInfo]);
+    ASSERT_TRUE(false);
+  };
+
+  RTCFileVideoCapturerErrorBlock secondErrorBlock = ^void(NSError *error) {
+    secondError = YES;
+  };
+
+  [self.capturer startCapturingFromFileNamed:kTestFileName onError:firstErrorBlock];
+  [self.capturer startCapturingFromFileNamed:kTestFileName onError:secondErrorBlock];
+
+  ASSERT_TRUE_WAIT(secondError, kTestTimeoutMs);
+}
+
+- (void)testStartStopCapturer {
+#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
+  if (@available(iOS 10, *)) {
+    [self.capturer startCapturingFromFileNamed:kTestFileName onError:nil];
+
+    __block BOOL done = NO;
+    __block NSInteger capturedFrames = -1;
+    NSInteger capturedFramesAfterStop = -1;
+
+    // We're dispatching the `stopCapture` with delay to ensure the capturer has
+    // had the chance to capture several frames.
+    dispatch_time_t captureDelay = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);  // 2secs.
+    dispatch_after(captureDelay, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+      capturedFrames = self.mockDelegate.capturedFramesCount;
+      [self.capturer stopCapture];
+      done = YES;
+    });
+    WAIT(done, kTestTimeoutMs);
+
+    capturedFramesAfterStop = self.mockDelegate.capturedFramesCount;
+    ASSERT_TRUE(capturedFrames != -1);
+    ASSERT_EQ(capturedFrames, capturedFramesAfterStop);
+  }
+#endif
+}
+
+@end
diff --git a/sdk/objc/Framework/UnitTests/foreman.mp4 b/sdk/objc/Framework/UnitTests/foreman.mp4
new file mode 100644
index 0000000..ccffbf4
--- /dev/null
+++ b/sdk/objc/Framework/UnitTests/foreman.mp4
Binary files differ