При сохранении слишком длинного записанного видео происходит сбой приложения.

Проблема:

При сохранении видео, записанного в моем приложении, если размер/продолжительность видео слишком велики/длинны, мое приложение аварийно завершает работу без журнал/исключение.

Мои настройки:

В своем приложении я использую UIImagePickerController для записи видео. Теперь я заметил, что если я делаю свои видео очень длинными (например, 30 минут с UIImagePickerControllerQualityTypeMedium или более минуты с UIImagePickerControllerQualityTypeIFrame1280x720), при сохранении видео происходит сбой приложения. Иногда с предупреждением, а иногда и без. Теперь я начал отлаживать и заметил, что это как-то связано с памятью (malloc_error).

Я использовал профилировщик, чтобы проверить выделение памяти в реальном времени, и заметил, что когда он собирался сохранить видео, выделение памяти внезапно стало очень большим (я думаю, это связано с временным использованием памяти для видео?), прежде чем оно в конечном итоге вышло из строя. Вот скриншот из профилировщика: Allocations screenshot

Приложение должно иметь возможность записывать видео максимальной продолжительностью 1 час (в любом указанном качестве).

Что я пробовал:

  • Настройка сборщика.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;

}

Я использую ARC с iOS 5.1.1

Обновление: Я поставил точку останова на 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);
6
задан Thermometer 26 June 2012 в 14:16
поделиться