2012-06-15 11 views
6

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: Allocations screenshot

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); 
+0

Riesci a trovare quale tipo di oggetto ha i byte live più grandi? – nhahtdh

+0

@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

+1

'[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

risposta

10

Il tuo problema è questa linea:

[NSData dataWithContentsOfPath:self.tempVideoPath] 

Si sta chiaramente tentando di caricare i contenuti di questo file memoria tutto in una volta, ma iOS non ti lascerà mai caricare così tanto in una volta. Il tuo metodo saveVideo sembra copiare solo il file dalla posizione temporanea alla directory dei documenti. Se questa è l'unica cosa che devi fare lì, dai un'occhiata al metodo copyItemAtPath:toPath:error di NSFileManager. È possibile modificare il metodo saveVideo per prendere il percorso del file temporaneo come parametro, anziché i dati.

+0

una risposta migliore della mia perché è più specifica. premi la taglia qui ... –

+0

Grazie mille !!! l'unica cosa che dovevo cambiare era '[fileManager createFileAtPath: contenuto fullPath: attributi video: nil];' a 'NSError * error = nil; if (! [FileManager copyItemAtPath: percorso toPath: errore FullPath: & errore]) NSLog (@ "Errore:% @", errore); ' – Thermometer

+0

Superbo! Grazie mille. –

2

perché preoccuparsi tirando l'intero contenuto del flusso multimediale da
[[info objectForKey:UIImagePickerControllerMediaURL] path] in un NSData* e poi scrivere di nuovo fuori? il tuo flusso multimediale sarà enorme!

la ragione per cui questo sta accadendo al salvataggio è perché si stanno registrando i dati, sta per "disco" e quindi li si legge in memoria per riscriverli su un altro file del disco.

hai considerato l'utilizzo di NSFileManager per copiare il file da [[info objectForKey:UIImagePickerControlMediaURL] path] al nome creato (fullPath)? questo dovrebbe evitare di trascinare l'intera cosa in memoria; dovrebbe essere un trasferimento "file-to-file".

Problemi correlati