2012-06-15 12 views
6

el problema:Al guardar el vídeo grabado que es demasiado largo, aplicación falla

Al guardar un vídeo que se grabó en mi aplicación, si el tamaño de vídeo/duración es demasiado grande/larga, mis aplicación se bloquea sin un registro/excepción.

mi configuración:

En mi aplicación Me utilizan un UIImagePickerController para grabar vídeos. Ahora he notado que si hago que mis videos tengan una duración muy larga (por ejemplo, 30 minutos con UIImagePickerControllerQualityTypeMedium, o más de un minuto con UIImagePickerControllerQualityTypeIFrame1280x720), al guardar el video, la aplicación falla. A veces con y a veces sin una advertencia. Ahora comencé a depurar y noté que tenía algo que ver con la memoria (malloc_error).

Utilicé el generador de perfiles para comprobar las asignaciones en vivo, y noté que cuando iba a guardar el video, la asignación repentinamente se hizo muy grande (¿algo relacionado con el uso temporal de memoria para el video?) Antes de que se bloqueara . Aquí hay una captura de pantalla del generador de perfiles: Allocations screenshot

La aplicación debe poder grabar video con una duración máxima de 1 hora (en cualquier calidad especificada).

Lo que he intentado:

  • Ajuste del picker.videoMaximumDuration corto/más largo
  • de depuración con el perfil del/instrumentos
  • comprobar si hay fugas
  • cerrado todas las aplicaciones abiertas & suprimen aplicación en dispositivo (para limpieza de almacenamiento) para obtener más memoria

Código:

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

Y código cuando se tomó la imagen:

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

Y, por último, cuando se guarda:

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

} 

estoy usando ARC con IOS 5.1 .1

Actualización: me han puesto un punto de interrupción en malloc_error_break, y en los instrumentos que puedo ver que es llamado desde:

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

Solución: Como dijo lawicko & john.k.doe, traté de cargar el vídeo desde es camino a una variable NSData. Esto causó que todo el video se cargue en la memoria.En vez de hacer eso, ahora sólo mover el archivo (& de cambio de nombre) copyItemAtPath

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

¿Puedes encontrar qué tipo de objeto tiene los bytes en vivo más grandes? – nhahtdh

+0

@nhahtdh que dice que se llama en: '# \t Dirección \t \t Categoría Marca de tiempo \t vivo \t Tamaño \t Biblioteca Responsable Responsable \t de llamadas 0x10900000 \t Malloc 473,29 MB \t 02: 08.951.332 • \t Fundación \t - [NSData (NSData) initWithContentsOfFile:] ' – Thermometer

+1

'[LocalVideoStorage saveVideo: [NSData dataWithContentsOfPath: self.tempVideoPath] ...' está leyendo todo el archivo temp en un objeto NSData solo para guardarlo nuevamente en el disco. ¿No puedes simplemente copiar el archivo temporal en la carpeta de almacenamiento permanente que elijas? – Rog

Respuesta

10

Su problema es esta línea:

[NSData dataWithContentsOfPath:self.tempVideoPath] 

Usted está tratando con claridad para cargar el contenido de este archivo para memoria a la vez, pero iOS nunca te permitirá cargar tanto al mismo tiempo. Su método saveVideo parece copiar solo el archivo desde una ubicación temporal al directorio de documentos. Si esto es lo único que tiene que hacer allí, entonces eche un vistazo al método copyItemAtPath:toPath:error de NSFileManager. Su podría cambiar el método saveVideo para tomar la ruta del archivo temporal como un parámetro, en lugar de los datos.

+0

una mejor respuesta que la mía porque es más específica. otorgar la recompensa aquí ... –

+0

Muchas gracias! Lo único que necesitaba cambiar era '[fileManager createFileAtPath: fullPath contents: video attributes: nil];' to 'NSError * error = nil; if (! [FileManager copyItemAtPath: ruta de acceso a Path: error de FullPath: & error]) NSLog (@ "Error:% @", error); ' – Thermometer

+0

¡Excelente! Muchas gracias. –

2

qué molestarse tirando de todo el contenido de la secuencia de medios de
[[info objectForKey:UIImagePickerControllerMediaURL] path] en un NSData* y luego escribirlos de vuelta? ¡tu transmisión de medios va a ser enorme!

la razón por la que esto sucede al guardar es porque está grabando los datos, va a "disco" y luego lo lee en la memoria para volver a escribirlo en otro archivo de disco.

¿Ha considerado usar NSFileManager para copiar el archivo de [[info objectForKey:UIImagePickerControlMediaURL] path] al nombre que crea (fullPath)? esto debería evitar poner todo en la memoria; debe ser una transferencia de "archivo a archivo".

Cuestiones relacionadas