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:
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);
¿Puedes encontrar qué tipo de objeto tiene los bytes en vivo más grandes? – nhahtdh
@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
'[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