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