Il problema:Quando si salva il video registrato che è troppo lungo, applicazione si blocca
Quando si salva un video che viene registrato nella mia app, se le dimensioni del video/durata è troppo grande/lungo, i miei arresti app senza un log/eccezione.
mio Setup:
nella mia app Io uso un UIImagePickerController per registrare video. Ora ho notato che se realizzo video molto lunghi (ad esempio 30 minuti con UIImagePickerControllerQualityTypeMedium o più di un minuto con UIImagePickerControllerQualityTypeIFrame1280x720), quando si salva il video, l'app si arresta in modo anomalo. A volte con e talvolta senza un avvertimento. Ora ho iniziato a eseguire il debug e ho notato che aveva qualcosa a che fare con la memoria (malloc_error).
Ho utilizzato il profiler per controllare le allocazioni dal vivo e ho notato che quando stava per salvare il video, l'allocazione è diventata improvvisamente molto grande (suppongo che abbia a che fare con l'utilizzo temporaneo della memoria per il video?) Prima che si arrestasse definitivamente . Ecco uno screenshot dal profiler:
L'app deve essere in grado di registrare video con una durata massima di 1 ora (in qualsiasi qualità specificata).
Quello che ho cercato:
- Impostazione del picker.videoMaximumDuration più breve/più
- Debug con profiler/strumenti
- controllare eventuali perdite
- chiuso tutte le applicazioni aperte & cancellati app dispositivo (per la pulizia di archiviazione) per ottenere più memoria
Codice:
- (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
}
}
E il codice quando l'immagine viene raccolto:
- (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];
}
}
E, infine, quando viene salvato:
+ (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;
}
Sto usando ARC con iOS 5.1 .1
Aggiornamento: Ho messo un punto di interruzione malloc_error_break, e in strumenti che può vedere che è richiamato da:
# Address Category Timestamp Live Size Responsible Library Responsible Caller
0 0x10900000 Malloc 473,29 MB 02:08.951.332 • 496283648 Foundation -[NSData(NSData) initWithContentsOfFile:]
Soluzione: Come detto lawicko & john.k.doe, ho cercato di caricare il video da è il percorso in una variabile NSData. Ciò ha causato l'intero video da caricare in memoria.Invece di fare questo, ora solo spostare il file (& rename) copyItemAtPath
NSError *error = nil;
if (![fileManager copyItemAtPath:path toPath:fullPath error:&error])
NSLog(@"Error: %@", error);
Riesci a trovare quale tipo di oggetto ha i byte live più grandi? – nhahtdh
@nhahtdh si dice che è chiamato: '# \t Indirizzo \t Categoria \t timestamp \t diretta \t Dimensioni \t Responsabile Biblioteca \t Responsabile chiamante 0x10900000 \t Malloc 473,29 MB \t 02: 08.951.332 \t • \t 496.283.648 \t Fondazione \t - [NSData (NSData) initWithContentsOfFile:] ' – Thermometer
'[LocalVideoStorage saveVideo: [NSData dataWithContentsOfPath: self.tempVideoPath] ...' si sta leggendo l'intero file temporaneo in un oggetto NSData solo per salvarlo nuovamente sul disco. Non puoi semplicemente copiare il file temporaneo nella cartella di archiviazione permanente scelta? – Rog