保存过长的录制视频时,应用程序崩溃
Posted
技术标签:
【中文标题】保存过长的录制视频时,应用程序崩溃【英文标题】:When saving recorded video that is too long, app crashes 【发布时间】:2012-06-18 13:18:58 【问题描述】:问题:
保存在我的应用中录制的视频时,如果视频大小/持续时间太大/太长,我的应用会崩溃而没有日志/异常。
我的设置:
在我的应用程序中,我使用 UIImagePickerController 来录制视频。现在我注意到,如果我制作的视频长度很长(例如,使用 UIImagePickerControllerQualityTypeMedium 时为 30 分钟,或者使用 UIImagePickerControllerQualityTypeIFrame1280x720 时超过一分钟),则在保存视频时,应用程序会崩溃。有时有警告,有时没有警告。现在我开始调试并注意到它与内存有关(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;
我在 ios 5.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);
【问题讨论】:
你能找出哪种类型的对象拥有最大的存活字节数吗? @nhahtdh 它说它被调用:# Address Category Timestamp Live Size Responsible Library Responsible Caller 0 0x10900000 Malloc 473,29 MB 02:08.951.332 • 496283648 Foundation -[NSData(NSData) initWithContentsOfFile:]
'[LocalVideoStorage saveVideo:[NSData dataWithContentsOfPath:self.tempVideoPath]...' 您正在将整个临时文件读入 NSData 对象,只是为了将其再次保存到磁盘。您不能直接将临时文件复制到您选择的永久存储文件夹中吗?
【参考方案1】:
你的问题是这一行:
[NSData dataWithContentsOfPath:self.tempVideoPath]
您显然是想一次将这个文件的内容全部加载到内存中,但 iOS 绝不会让您一次加载那么多。您的 saveVideo
方法似乎只将文件从临时位置复制到文档目录。如果这是您唯一需要做的事情,请查看 NSFileManager 的 copyItemAtPath:toPath:error 方法。您可以更改saveVideo
方法以将临时文件路径作为参数,而不是数据。
【讨论】:
非常感谢!!!我唯一需要改变的是[fileManager createFileAtPath:fullPath contents:video attributes:nil];
到 NSError *error = nil; if (![fileManager copyItemAtPath:path toPath:fullPath error:&error]) NSLog(@"Error: %@", error);
【参考方案2】:
为什么要将媒体流的全部内容从[[info objectForKey:UIImagePickerControllerMediaURL] path]
拉到NSData*
中,然后再写回?您的媒体流将是巨大的!
在保存时发生这种情况的原因是因为您正在记录数据,它将进入“磁盘”,然后您将其读入内存以将其写回另一个磁盘文件。
您是否考虑过使用NSFileManager
将文件从[[info objectForKey:UIImagePickerControlMediaURL] path]
复制到您创建的名称(fullPath)?这应该避免将整个事情拉入内存;它应该是“文件到文件”的传输。
【讨论】:
以上是关于保存过长的录制视频时,应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章