diff --git a/.gitignore b/.gitignore
index edc082c..6e13704 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# Xcode
-build/*
+.DS_Store
+*/build/*
*.pbxuser
!default.pbxuser
*.mode1v3
@@ -8,8 +9,13 @@ build/*
!default.mode2v3
*.perspectivev3
!default.perspectivev3
-*.xcworkspace
-!default.xcworkspace
xcuserdata
profile
*.moved-aside
+DerivedData
+.idea/
+*.hmap
+*.xccheckout
+
+#CocoaPods
+Pods
diff --git a/MultiVidCam.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MultiVidCam.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..77bc7ff
--- /dev/null
+++ b/MultiVidCam.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/MultiVidCam/AVAssetStitcher.m b/MultiVidCam/AVAssetStitcher.m
index 0ae23cf..1c9da04 100644
--- a/MultiVidCam/AVAssetStitcher.m
+++ b/MultiVidCam/AVAssetStitcher.m
@@ -47,6 +47,12 @@ - (id)initWithOutputSize:(CGSize)outSize
- (void)addAsset:(AVURLAsset *)asset withTransform:(CGAffineTransform (^)(AVAssetTrack *videoTrack))transformToApply withErrorHandler:(void (^)(NSError *error))errorHandler
{
+ if ([[asset tracksWithMediaType:AVMediaTypeVideo] count] == 0 ||
+ [[asset tracksWithMediaType:AVMediaTypeAudio] count] == 0)
+ {
+ return;
+ }
+
AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
diff --git a/MultiVidCam/RecordingViewController.h b/MultiVidCam/RecordingViewController.h
index 69816f7..a51d4d0 100644
--- a/MultiVidCam/RecordingViewController.h
+++ b/MultiVidCam/RecordingViewController.h
@@ -36,5 +36,6 @@
- (IBAction)recordTouchUp:(id)sender;
- (IBAction)saveRecording:(id)sender;
- (IBAction)cancelAction:(id)sender;
+- (IBAction)reverseAction:(id)sender;
@end
diff --git a/MultiVidCam/RecordingViewController.m b/MultiVidCam/RecordingViewController.m
index 7054a5e..f8d89da 100644
--- a/MultiVidCam/RecordingViewController.m
+++ b/MultiVidCam/RecordingViewController.m
@@ -129,6 +129,9 @@ - (void)didReceiveMemoryWarning
}
#pragma mark Record button
+- (IBAction)reverseAction:(id)sender {
+ [videoCameraInputManager reverseCamera];
+}
- (IBAction)cancelAction:(id)sender
{
diff --git a/MultiVidCam/VideoCameraInputManager.h b/MultiVidCam/VideoCameraInputManager.h
index 055a2a2..af0ef2b 100644
--- a/MultiVidCam/VideoCameraInputManager.h
+++ b/MultiVidCam/VideoCameraInputManager.h
@@ -35,6 +35,8 @@ typedef void (^ErrorHandlingBlock)(NSError *error);
- (void)resumeRecording;
- (void)reset;
+- (void)reverseCamera;
+- (void)cleanTemporaryFiles;
- (void)finalizeRecordingToFile:(NSURL *)finalVideoLocationURL withVideoSize:(CGSize)videoSize withPreset:(NSString *)preset withCompletionHandler:(void (^)(NSError *error))completionHandler;
diff --git a/MultiVidCam/VideoCameraInputManager.m b/MultiVidCam/VideoCameraInputManager.m
index 3b9c684..01a6c54 100644
--- a/MultiVidCam/VideoCameraInputManager.m
+++ b/MultiVidCam/VideoCameraInputManager.m
@@ -22,7 +22,9 @@
#import
-@interface VideoCameraInputManager (Private)
+@interface VideoCameraInputManager ()
+
+@property (nonatomic, strong) NSMutableDictionary *segmentFileDict;
- (void)startNotificationObservers;
- (void)endNotificationObservers;
@@ -75,6 +77,7 @@ - (id)init
inFlightWrites = 0;
movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
+ self.segmentFileDict = [[NSMutableDictionary alloc] init];
[self startNotificationObservers];
}
@@ -83,6 +86,7 @@ - (id)init
- (void)dealloc
{
+ NSLog(@"Video Camera Input Manager dealloc");
[_captureSession removeOutput:movieFileOutput];
[self endNotificationObservers];
@@ -90,6 +94,7 @@ - (void)dealloc
- (void)setupSessionWithPreset:(NSString *)preset withCaptureDevice:(AVCaptureDevicePosition)cd withTorchMode:(AVCaptureTorchMode)tm withError:(NSError **)error
{
+ [self cleanTemporaryFiles];
if(setupComplete)
{
*error = [NSError errorWithDomain:@"Setup session already complete." code:102 userInfo:nil];
@@ -150,8 +155,6 @@ - (void)setupSessionWithPreset:(NSString *)preset withCaptureDevice:(AVCaptureDe
- (void)startRecording
{
- [temporaryFileURLs removeAllObjects];
-
uniqueTimestamp = [[NSDate date] timeIntervalSince1970];
currentRecordingSegment = 0;
_isPaused = NO;
@@ -163,13 +166,10 @@ - (void)startRecording
videoConnection.videoOrientation = orientation;
}
- NSURL *outputFileURL = [NSURL fileURLWithPath:[self constructCurrentTemporaryFilename]];
-
- [temporaryFileURLs addObject:outputFileURL];
-
+ NSString *filePath = [self constructCurrentTemporaryFilename];
+ [self.segmentFileDict setObject:@(0) forKey:filePath];
movieFileOutput.maxRecordedDuration = (_maxDuration > 0) ? CMTimeMakeWithSeconds(_maxDuration, 600) : kCMTimeInvalid;
-
- [movieFileOutput startRecordingToOutputFileURL:outputFileURL recordingDelegate:self];
+ [movieFileOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:filePath] recordingDelegate:self];
}
- (void)pauseRecording
@@ -182,33 +182,50 @@ - (void)pauseRecording
- (void)resumeRecording
{
- currentRecordingSegment++;
_isPaused = NO;
+ currentRecordingSegment++;
- NSURL *outputFileURL = [NSURL fileURLWithPath:[self constructCurrentTemporaryFilename]];
-
- [temporaryFileURLs addObject:outputFileURL];
-
- if(_maxDuration > 0)
- {
- movieFileOutput.maxRecordedDuration = CMTimeSubtract(CMTimeMakeWithSeconds(_maxDuration, 600), currentFinalDurration);
- }
- else
- {
- movieFileOutput.maxRecordedDuration = kCMTimeInvalid;
- }
-
- [movieFileOutput startRecordingToOutputFileURL:outputFileURL recordingDelegate:self];
+ NSString *filePath = [self constructCurrentTemporaryFilename];
+ [self.segmentFileDict setObject:@(0) forKey:filePath];
+ movieFileOutput.maxRecordedDuration = (_maxDuration > 0) ? CMTimeSubtract(CMTimeMakeWithSeconds(_maxDuration, 600), currentFinalDurration) : kCMTimeInvalid;
+ [movieFileOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:filePath] recordingDelegate:self];
}
- (void)reset
{
- if (movieFileOutput.isRecording)
- {
- [self pauseRecording];
- }
-
+ [self pauseRecording];
_isPaused = NO;
+ [_captureSession stopRunning];
+}
+
+- (void)reverseCamera {
+ NSArray *inputs = self.captureSession.inputs;
+ for ( AVCaptureDeviceInput *input in inputs ) {
+ AVCaptureDevice *device = input.device;
+ if ([device hasMediaType:AVMediaTypeVideo]) {
+ AVCaptureDevicePosition position = device.position;
+ AVCaptureDevice *newCamera = nil;
+ AVCaptureDeviceInput *newInput = nil;
+
+ if (position == AVCaptureDevicePositionFront) {
+ newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
+ }
+ else {
+ newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
+ }
+ newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
+
+ // beginConfiguration ensures that pending changes are not applied immediately
+ [self.captureSession beginConfiguration];
+
+ [self.captureSession removeInput:input];
+ [self.captureSession addInput:newInput];
+
+ // Changes take effect once the outermost commitConfiguration is invoked.
+ [self.captureSession commitConfiguration];
+ break;
+ }
+ }
}
- (void)finalizeRecordingToFile:(NSURL *)finalVideoLocationURL withVideoSize:(CGSize)videoSize withPreset:(NSString *)preset withCompletionHandler:(void (^)(NSError *error))completionHandler
@@ -218,13 +235,61 @@ - (void)finalizeRecordingToFile:(NSURL *)finalVideoLocationURL withVideoSize:(CG
NSError *error;
if([finalVideoLocationURL checkResourceIsReachableAndReturnError:&error])
{
- completionHandler([NSError errorWithDomain:@"Output file already exists." code:104 userInfo:nil]);
- return;
+ [[NSFileManager defaultManager] removeItemAtURL:finalVideoLocationURL error:nil];
}
- if(inFlightWrites != 0)
- {
- completionHandler([NSError errorWithDomain:@"Can't finalize recording unless all sub-recorings are finished." code:106 userInfo:nil]);
+ //----------filter the failed files---------
+ [temporaryFileURLs removeAllObjects];
+ NSArray *keys = [self.segmentFileDict allKeys];
+ NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
+ return [obj1 compare:obj2 options:NSNumericSearch];
+ }];
+
+ for (NSString *filePath in sortedArray) {
+ NSInteger fileValue = [self.segmentFileDict[filePath] integerValue];
+ if (fileValue == 0) {
+ NSLog(@"[0]not start and stop! filePath=%@", filePath);
+ if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
+ [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
+ }
+ }
+ else if (fileValue == 1) {
+ NSLog(@"[1]started but not stop! filePath=%@", filePath);
+ if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
+ [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
+ }
+ }
+ else if (fileValue == 2) {
+ NSLog(@"[2]stoped but not start! filePath=%@", filePath);
+ if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
+ [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
+ }
+ }
+ else if (fileValue == 3) {
+ NSLog(@"[3]OK start and stop success!!! filePath=%@", filePath);
+ if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
+ CGFloat fileSize = [self getFileSize:filePath];//KB
+ if (fileSize <= 10.0f) {
+ [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
+ }
+ else {
+ [temporaryFileURLs addObject:[NSURL fileURLWithPath:filePath]];
+ }
+ }
+ }
+ else {
+ NSLog(@"[%d]other error! filepath=%@", fileValue, filePath);
+ if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
+ [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
+ }
+ }
+ }
+ //------------------------------------------
+
+
+ if ([temporaryFileURLs count] == 0) {
+ NSError *error1 = [NSError errorWithDomain:@"have no files!" code:108 userInfo:nil];
+ completionHandler(error1);
return;
}
@@ -279,15 +344,25 @@ - (void)finalizeRecordingToFile:(NSURL *)finalVideoLocationURL withVideoSize:(CG
}
else
{
- [self cleanTemporaryFiles];
- [temporaryFileURLs removeAllObjects];
-
completionHandler(nil);
}
-
+ [self cleanTemporaryFiles];
}];
}
+- (CGFloat)getFileSize:(NSString *)filePath{
+ if ( ! [[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
+ return 0;
+ }
+ NSError *error=nil;
+ NSDictionary * fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];
+ if (fileAttributes == nil || error!=nil) {
+ return 0;
+ }
+ long long fileSizeByte = [fileAttributes fileSize];
+ return fileSizeByte / 1024.0f;
+}
+
- (CMTime)totalRecordingDuration
{
if(CMTimeCompare(kCMTimeZero, currentFinalDurration) == 0)
@@ -305,11 +380,17 @@ - (CMTime)totalRecordingDuration
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections
{
- inFlightWrites++;
+ NSInteger value = [[self.segmentFileDict objectForKey:[fileURL path]] integerValue];
+ value += 1;
+ self.segmentFileDict[[fileURL path]] = @(value);
}
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
+ NSInteger value = [[self.segmentFileDict objectForKey:[fileURL path]] integerValue];
+ value += 2;
+ self.segmentFileDict[[fileURL path]] = @(value);
+
if(error)
{
if(self.asyncErrorHandler)
@@ -321,8 +402,6 @@ - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToO
NSLog(@"Error capturing output: %@", error);
}
}
-
- inFlightWrites--;
}
#pragma mark - Observer start and stop
@@ -499,14 +578,21 @@ - (AVCaptureConnection *)connectionWithMediaType:(NSString *)mediaType fromConne
- (NSString *)constructCurrentTemporaryFilename
{
- return [NSString stringWithFormat:@"%@%@-%ld-%d.mov", NSTemporaryDirectory(), @"recordingsegment", uniqueTimestamp, currentRecordingSegment];
+ NSString *tempFolderPath = [NSString stringWithFormat:@"%@temprecordfiles",NSTemporaryDirectory()];
+ if ( ! [[NSFileManager defaultManager] fileExistsAtPath:tempFolderPath]) {
+ [[NSFileManager defaultManager] createDirectoryAtPath:tempFolderPath withIntermediateDirectories:YES attributes:nil error:NULL];
+ }
+ return [NSString stringWithFormat:@"%@/%@-%ld-%d.mov", tempFolderPath, @"recordingsegment", uniqueTimestamp, currentRecordingSegment];
}
- (void)cleanTemporaryFiles
{
- [temporaryFileURLs enumerateObjectsUsingBlock:^(NSURL *temporaryFiles, NSUInteger idx, BOOL *stop) {
- [[NSFileManager defaultManager] removeItemAtURL:temporaryFiles error:nil];
- }];
+ NSString *tempFolderPath = [NSString stringWithFormat:@"%@temprecordfiles",NSTemporaryDirectory()];
+ if ([[NSFileManager defaultManager] fileExistsAtPath:tempFolderPath]) {
+ [[NSFileManager defaultManager] removeItemAtPath:tempFolderPath error:nil];
+ }
+
+ [temporaryFileURLs removeAllObjects];
}
@end
diff --git a/MultiVidCam/en.lproj/MainStoryboard_iPad.storyboard b/MultiVidCam/en.lproj/MainStoryboard_iPad.storyboard
index 06c9edd..f678d7e 100644
--- a/MultiVidCam/en.lproj/MainStoryboard_iPad.storyboard
+++ b/MultiVidCam/en.lproj/MainStoryboard_iPad.storyboard
@@ -1,26 +1,34 @@
-
+
-
+
+
+
+
+
+
-
+
-
+
+
-
+
+
-
-
-
-
-
+
+
-
+
-
-
-
-
+
-
+
-
@@ -139,29 +118,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/MultiVidCam/en.lproj/MainStoryboard_iPhone.storyboard b/MultiVidCam/en.lproj/MainStoryboard_iPhone.storyboard
index d6d2b00..60615b1 100644
--- a/MultiVidCam/en.lproj/MainStoryboard_iPhone.storyboard
+++ b/MultiVidCam/en.lproj/MainStoryboard_iPhone.storyboard
@@ -1,22 +1,41 @@
-
+
-
+
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -34,6 +53,7 @@
+
@@ -52,30 +72,29 @@
-
+
+
-
-
-
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
@@ -138,27 +157,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-