問題:
アプリに記録されたビデオを保存するときに、ビデオのサイズ/長さが大きすぎる/長すぎると、ログ/例外なしでアプリがクラッシュします。
私のセットアップ:
私のアプリでは、UIImagePickerControllerを使用してビデオを録画しています。ビデオの長さを非常に長くすると(たとえば、UIImagePickerControllerQualityTypeMediumで30分、UIImagePickerControllerQualityTypeIFrame1280x720で1分以上)、ビデオを保存するとアプリがクラッシュすることに気付きました。警告がある場合とない場合があります。今、私はデバッグを開始し、それがメモリ(malloc_error)と関係があることに気づきました。
プロファイラーを使用して割り当てをライブで確認しましたが、ビデオを保存しようとすると、最終的にクラッシュする前に、割り当てが突然非常に大きくなることに気付きました(ビデオの一時的なメモリ使用量と関係があると思いますか?)。プロファイラーのスクリーンショットは次のとおりです。
アプリは、最大1時間(指定された品質で)のビデオを録画できる必要があります。
私が試したこと:
- picker.videoMaximumDurationを短く/長く設定する
- プロファイラー/機器を使用してデバッグする
- 漏れがないか確認してください
- より多くのメモリを取得するために、デバイスで開いているすべてのアプリと削除されたアプリを閉じました(ストレージのクリーニング用)
コード:
- (void)openCamera:(id)sender context:(NSManagedObjectContext*)context {
self.context = context;
//Set self as delegate (UIImagePickerControllerDelegate)
[self.picker setDelegate:self];
//If the device has a camera
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
self.picker.sourceType = UIImagePickerControllerSourceTypeCamera;
self.picker.allowsEditing = YES;
self.picker.videoQuality = [Settings videoQualitySetting];
//If the camera can record video
NSString *desired = (NSString *)kUTTypeMovie;
if ([[UIImagePickerController availableMediaTypesForSourceType:self.picker.sourceType] containsObject:desired]) {
//Let the user record video
self.picker.mediaTypes = [NSArray arrayWithObject:desired];
self.picker.videoMaximumDuration = MaxDuration;
}
else {
NSLog(@"Can't take videos with this device"); //debug
}
//Present the picker fullscreen/in popover
if ([Settings shouldDisplayFullScreenCamera]){
[self presentModalViewController:self.picker animated:YES];
[self.masterPopoverController dismissPopoverAnimated:YES];
}
else {
if (!_popover){
_popover = [[UIPopoverController alloc] initWithContentViewController:self.picker];
}
[_popover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
}
else {
NSLog(@"Does not have a camera"); //debug
}
}
そして、画像が選択されたときのコード:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
// Save the video, and close the overlay
if (CFStringCompare ((__bridge CFStringRef) mediaType, kUTTypeMovie, 0)
== kCFCompareEqualTo) {
self.tempVideoPath = [[info objectForKey:
UIImagePickerControllerMediaURL] path];
[LocalVideoStorage saveVideo:[NSData dataWithContentsOfPath:self.tempVideoPath name:self.videoName];
[_picker dismissModalViewControllerAnimated: YES];
[[_picker parentViewController] dismissModalViewControllerAnimated:YES];
[_popover dismissPopoverAnimated:YES];
}
}
そして最後に、それが保存されたとき:
+ (NSString*)saveVideo:(NSData*)video:(NSString*)videoName {
NSFileManager *fileManager = [NSFileManager defaultManager];//create instance of NSFileManager
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //create an array and store result of our search for the documents directory in it
NSString *documentsDirectory = [paths objectAtIndex:0]; //create NSString object, that holds our exact path to the documents directory
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.MOV", videoName]]; //add our video to the path
[fileManager createFileAtPath:fullPath contents:video attributes:nil]; //finally save the path (video)
NSLog(@"Video saved!");
return fullPath;
}
iOS5.1.1でARCを使用しています
更新: malloc_error_breakにブレークポイントを設定しました。インストゥルメントでは、次の場所から呼び出されていることがわかります。
# Address Category Timestamp Live Size Responsible Library Responsible Caller
0 0x10900000 Malloc 473,29 MB 02:08.951.332 • 496283648 Foundation -[NSData(NSData) initWithContentsOfFile:]
解決策: lawicko&john.k.doeが言ったように、私はビデオをそのパスからNSData変数にロードしようとしました。これにより、ビデオ全体がメモリにロードされました。その代わりに、ファイルを移動(および名前変更)するだけです。copyItemAtPath
NSError *error = nil;
if (![fileManager copyItemAtPath:path toPath:fullPath error:&error])
NSLog(@"Error: %@", error);