我一直在为 iOS 8 编写一个相机应用程序,它使用 AVFoundation 来设置和处理记录和保存(不是 ImagePickerController)。我正在尝试保存使用 AVCaptureMovieFileOutput 类的 maxRecordedFileSize 属性,以允许用户填满手机上的所有可用空间(减去留给苹果东西的 250MB 缓冲区)。
- (unsigned long long) availableFreespaceInMb {
unsigned long long freeSpace;
NSError *error = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSDictionary *dictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error: &error];
if (dictionary) {
NSNumber *fileSystemFreeSizeInBytes = [dictionary objectForKey: NSFileSystemFreeSize];
freeSpace = [fileSystemFreeSizeInBytes unsignedLongLongValue];
} else {
NSLog(@"Error getting free space");
//Handle error
}
//convert to MB
freeSpace = (freeSpace/1024ll)/1024ll;
freeSpace -= _recordSpaceBufferInMb; // 250 MB
NSLog(@"Remaining space in MB: %llu", freeSpace);
NSLog(@" Diff Since Last: %llu", (_prevRemSpaceMb - freeSpace));
_prevRemSpaceMb = freeSpace;
return freeSpace;
}
当可用空间(减去缓冲区)减少到零时抛出 AVErrorMaximumFileSizeReached,并且没有抛出保存错误,但是视频没有出现在相机胶卷中并且没有保存。当我设置 maxRecordedDuration 字段时,会抛出 AVErrorMaximumDurationReached 并保存视频。我从最大尺寸计算最大时间,但由于帧压缩,我总是有足够的空间。
- (void) toggleMovieRecording
{
double factor = 1.0;
if (_currentFramerate == _slowFPS) {
factor = _slowMotionFactor;
}
double availableRecordTimeInSeconds = [self remainingRecordTimeInSeconds] / factor;
unsigned long long remainingSpace = [self availableFreespaceInMb] * 1024 * 1024;
if (![[self movieFileOutput] isRecording]) {
if (availableSpaceInMb < 50) {
NSLog(@"TMR:Not enough space, can't record");
[AVViewController currentVideoOrientation];
[_previewView memoryAlert];
return;
}
}
if (![self enableRecording]) {
return;
}
[[self recordButton] setEnabled:NO];
dispatch_async([self sessionQueue], ^{
if (![[self movieFileOutput] isRecording])
{
if ([[UIDevice currentDevice] isMultitaskingSupported])
{
[self setBackgroundRecordingID:[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]];
}
// Update the orientation on the movie file output video connection before starting recording.
[[[self movieFileOutput] connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation: [AVViewController currentVideoOrientation]];//[[(AVCaptureVideoPreviewLayer *)[[self previewView] layer] connection] videoOrientation]];
// Start recording to a temporary file.
NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[@"movie" stringByAppendingPathExtension:@"mov"]];
// Is there already a file like this?
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:outputFilePath]) {
NSLog(@"filexists");
NSError *err;
if ([fileManager removeItemAtPath:outputFilePath error:&err] == NO) {
NSLog(@"Error, file exists at path");
}
}
[_previewView startRecording];
// Set the movie file output to stop recording a bit before the phone is full
[_movieFileOutput setMaxRecordedFileSize:remainingSpace]; // Less than the total remaining space
// [_movieFileOutput setMaxRecordedDuration:CMTimeMake(availableRecordTimeInSeconds, 1.0)];
[_movieFileOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:outputFilePath] recordingDelegate:self];
}
else
{
[_previewView stopRecording];
[[self movieFileOutput] stopRecording];
}
});
}
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
NSLog(@"AVViewController: didFinishRecordingToOutputFile");
if (error) {
NSLog(@"%@", error);
NSLog(@"Caught Error");
if ([error code] == AVErrorDiskFull) {
NSLog(@"Caught disk full error");
} else if ([error code] == AVErrorMaximumFileSizeReached) {
NSLog(@"Caught max file size error");
} else if ([error code] == AVErrorMaximumDurationReached) {
NSLog(@"Caught max duration error");
} else {
NSLog(@"Caught other error");
}
[self remainingRecordTimeInSeconds];
dispatch_async(dispatch_get_main_queue(), ^{
[_previewView stopRecording];
[_previewView memoryAlert];
});
}
// Note the backgroundRecordingID for use in the ALAssetsLibrary completion handler to end the background task associated with this recording. This allows a new recording to be started, associated with a new UIBackgroundTaskIdentifier, once the movie file output's -isRecording is back to NO — which happens sometime after this method returns.
UIBackgroundTaskIdentifier backgroundRecordingID = [self backgroundRecordingID];
[self setBackgroundRecordingID:UIBackgroundTaskInvalid];
[[[ALAssetsLibrary alloc] init] writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
NSLog(@"%@", error);
NSLog(@"Error during write");
} else {
NSLog(@"Writing to photos album");
}
[[NSFileManager defaultManager] removeItemAtURL:outputFileURL error:nil];
if (backgroundRecordingID != UIBackgroundTaskInvalid)
[[UIApplication sharedApplication] endBackgroundTask:backgroundRecordingID];
}];
if (error) {
[_session stopRunning];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), _sessionQueue, ^{
[_session startRunning];
});
}
抛出两个错误时会出现“正在写入相册”。我完全被这个难住了。任何iOS见解?