保存录制的视频太长时,应用崩溃 [英] When saving recorded video that is too long, app crashes

查看:244
本文介绍了保存录制的视频太长时,应用崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:



保存录制在我的应用中的视频时,如果视频大小/ ,我的应用程序崩溃,没有日志/异常。



我的设置



在我的应用中,我使用UIImagePickerController录制视频。现在我注意到,如果我让我的视频很长的长度(例如30分钟与UIImagePickerControllerQualityTypeMedium,或超过一分钟与UIImagePickerControllerQualityTypeIFrame1280x720),当保存视频,应用程序崩溃。有时有和没有警告。现在我开始调试,并注意到它与内存(malloc_error)有关。



我使用分析器来检查分配,并注意到,当它要保存视频,分配突然变得非常大(我猜,视频的内存使用)。以下是分析器的屏幕截图:



应用程序必须能够录制最长持续时间为1小时(指定的任何质量)的视频。



我尝试过的




  • picker.videoMaximumDuration更短/更长

  • 使用分析器/乐器调试

  • 检查是否有泄漏

  • 开放应用程式&已删除的设备上的应用(用于存储清理)获取更多内存



代码: $ b

   - (void)openCamera:(id)sender context:(NSManagedObjectContext *)context {
self.context =
//将自身设置为委托(UIImagePickerControllerDelegate)
[self.picker setDelegate:self];
//如果设备有一个摄像头
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
self.picker.sourceType = UIImagePickerControllerSourceTypeCamera;
self.picker.allowsEditing = YES;
self.picker.videoQuality = [Settings videoQualitySetting];
//如果摄像机可以录制视频
NSString * desired =(NSString *)kUTTypeMovie;
if([[UIImagePickerController availableMediaTypesForSourceType:self.picker.sourceType] containsObject:desired]){
//让用户录制视频
self.picker.mediaTypes = [NSArray arrayWithObject:desired] ;
self.picker.videoMaximumDuration = MaxDuration;
}
else {
NSLog(@无法使用此设备拍摄视频); // debug
}
//在弹出框中显示选择器全屏/
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(@没有摄像头); // debug
}
}

   - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info 
{
NSString * mediaType = [info objectForKey:UIImagePickerControllerMediaType];

//保存视频并关闭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]; //创建NSFileManager的实例

NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES); //创建一个数组并存储我们搜索文档目录的结果

NSString * documentsDirectory = [paths objectAtIndex:0]; //创建NSString对象,它保存我们文档目录的确切路径

NSString * fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@%@。MOV,videoName]]; //将我们的视频添加到路径

[fileManager createFileAtPath:fullPath contents:video attributes:nil]; //最后保存路径(视频)

NSLog(@视频已保存!
return fullPath;

}

我使用ARC与iOS 5.1.1



更新:
我在malloc_error_break上放了一个断点,在仪器中我可以看到它被调用:



#$地址类别时间戳活动大小负责任的调用者
0 0x10900000 Malloc 473,29 MB 02:08.951.332•496283648 Foundation - [NSData (NSData)initWithContentsOfFile:]

解决方案: & john.k.doe说,我试图从它的路径加载视频到一个NSData变量。这导致整个视频被加载到内存。而不是这样,我现在只是移动文件(& rename) copyItemAtPath

  NSError * error = nil; 
if(![fileManager copyItemAtPath:path toPath:fullPath error:& error])
NSLog(@Error:%@,error);


解决方案

您的问题是这一行:

  [NSData dataWithContentsOfPath:self.tempVideoPath] 


b $ b

你显然是试图将这个文件的内容一次加载到内存,但iOS永远不会让你一次加载那么多。您的 saveVideo 方法似乎只将文件从临时位置复制到文档目录。如果这是您需要做的唯一事情,请查看copyItemAtPath:toPath:error 方法。您可以更改 saveVideo 方法以将临时文件路径作为参数,而不是数据。


The problem:

When saving a video that is recorded in my app, if the video size/duration is too big/long, my app crashes without a log/exception.

My Setup:

In my App I use a UIImagePickerController to record videos. Now I have noticed that if I make my videos very long in length (for example 30minutes with UIImagePickerControllerQualityTypeMedium, or more than a minute with UIImagePickerControllerQualityTypeIFrame1280x720), when saving the video, the app crashes. Sometimes with and sometimes without a warning. Now I started to debug and noticed it had something to do with memory (malloc_error).

I used the profiler to check allocations live, and noticed that when it was going to save the video, the allocation suddenly became very big (I guess something to do with temporary memory usage for the video?) before it ultimately crashed. Here is a screenshot from the profiler:

The app must be able to record video with a maximum duration of 1 hour (in any quality specified).

What I have tried:

  • Setting the picker.videoMaximumDuration shorter/longer
  • Debug with profiler/instruments
  • Check for leaks
  • Closed all open apps & deleted app on device (for storage cleaning) to get more memory

Code:

- (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
    }    
}

And code when the image is picked:

- (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];  
    }
}

And finally, when it is saved:

+ (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;

}

I am using ARC with iOS 5.1.1

Update: I have put a breakpoint on malloc_error_break, and in instruments I can see it is called from:

#   Address Category    Timestamp   Live    Size    Responsible Library Responsible Caller
0   0x10900000  Malloc 473,29 MB    02:08.951.332   •   496283648   Foundation  -[NSData(NSData) initWithContentsOfFile:]

Solution: As lawicko & john.k.doe said, I tried to load the video from it's path into an NSData variable. This caused the whole video to be loaded into memory. Instead of doing that, I now just move the file (& rename) copyItemAtPath

NSError *error = nil;
if (![fileManager copyItemAtPath:path toPath:fullPath error:&error])
    NSLog(@"Error: %@", error);

解决方案

Your problem is this line:

[NSData dataWithContentsOfPath:self.tempVideoPath]

You are clearly trying to load the contents of this file to memory all at once, but iOS will never let you load that much at one time. Your saveVideo method seems to only copy the file from temporary location to the documents directory. If this is the only thing that you need to do there, then take a look at copyItemAtPath:toPath:error method of NSFileManager. Your could change the saveVideo method to take the temporary file path as a parameter, rather than the data.

这篇关于保存录制的视频太长时,应用崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆