我一直在为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的见解?
解决方法
您提供的代码示例很难测试,因为缺少一个属性和方法.虽然我无法编译你的代码,但肯定会有一些红旗可能导致这个问题.以下问题发现在:
captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:错误:
captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:错误:
问题1:该方法正在处理传递的错误,但是继续执行该方法.而应该是:
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error { if (error) { // handle error then bail return; } // continue on }
问题2:您要实例化的ALAssetsLibrary对象不会存储到一个属性中,因此一旦captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:finish对象将被释放(可能永远不会触发完成块).相反应该是:
// hold onto the assets library beyond this scope self.assetsLibrary = [[ALAssetsLibrary alloc] init]; // get weak reference to self for later removal of the assets library __weak typeof(self) weakSelf = self; [self.assetsLibrary writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock:^(NSURL *assetURL,NSError *error) { // handle error // handle cleanup // cleanup new property weakSelf.assetsLibrary = nil; }];