Note that there are some explanatory texts on larger screens.

plurals
  1. POWhen saving recorded video that is too long, app crashes
    primarykey
    data
    text
    <p><strong>The problem:</strong></p> <p>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. </p> <p><strong>My Setup:</strong></p> <p>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). </p> <p>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: <img src="https://i.stack.imgur.com/c2Be7.png" alt="Allocations screenshot"></p> <p>The app must be able to record video with a maximum duration of 1 hour (in any quality specified). </p> <p><strong>What I have tried:</strong></p> <ul> <li>Setting the picker.videoMaximumDuration shorter/longer</li> <li>Debug with profiler/instruments</li> <li>Check for leaks</li> <li>Closed all open apps &amp; deleted app on device (for storage cleaning) to get more memory </li> </ul> <p><strong>Code:</strong></p> <pre><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 } } </code></pre> <p>And code when the image is picked:</p> <pre><code>- (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]; } } </code></pre> <p>And finally, when it is saved:</p> <pre><code>+ (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; } </code></pre> <p>I am using ARC with iOS 5.1.1</p> <p><strong>Update:</strong> I have put a breakpoint on malloc_error_break, and in instruments I can see it is called from:</p> <pre><code># Address Category Timestamp Live Size Responsible Library Responsible Caller 0 0x10900000 Malloc 473,29 MB 02:08.951.332 • 496283648 Foundation -[NSData(NSData) initWithContentsOfFile:] </code></pre> <p><strong>Solution:</strong> As lawicko &amp; 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 (&amp; rename) <strong>copyItemAtPath</strong></p> <pre><code>NSError *error = nil; if (![fileManager copyItemAtPath:path toPath:fullPath error:&amp;error]) NSLog(@"Error: %@", error); </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload