2012-06-15 12 views
6

problem:Przy zapisywaniu nagrań wideo, który jest zbyt długi, aplikacja wywala

Podczas zapisywania wideo, które mamy w mojej aplikacji, czy rozmiar video/czas trwania jest zbyt duży/długi, moje awarie aplikacji bez dziennik/wyjątek.

Moja konfiguracja:

w moim app używam UIImagePickerController nagrywać filmy. Zauważyłem, że jeśli nagrywam wideo o bardzo długiej długości (na przykład 30 minut z UIImagePickerControllerQualityTypeMedium lub ponad minutę z UIImagePickerControllerQualityTypeIFrame1280x720), podczas zapisywania wideo aplikacja ulega awarii. Czasami z, a czasem bez ostrzeżenia. Teraz zacząłem debugować i zauważyłem, że ma to coś wspólnego z pamięcią (malloc_error).

Użyłem profilera do sprawdzenia przydziałów na żywo i zauważyłem, że kiedy miał zamiar zapisać wideo, przydzielenie nagle stało się bardzo duże (jak sądzę, że ma coś wspólnego z tymczasowym wykorzystaniem pamięci dla wideo?), Zanim ostatecznie się zawiesiło . Oto zrzut ekranu z profilu: Allocations screenshot

Aplikacja musi być w stanie nagrywać wideo maksymalnie przez 1 godzinę (w dowolnej określonej jakości).

Co próbowałem:

  • Ustawianie picker.videoMaximumDuration krótsze/dłuższe
  • Debug z Profiler/instrumentów
  • sprawdzić szczelność
  • Zamknięto wszystkie & usuniętych aplikacji na otwarte aplikacje urządzenie (do czyszczenia pamięci), aby uzyskać więcej pamięci:

Kod:

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

i kod gdy obraz jest odbierany:

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

I wreszcie, gdy jest zapisane:

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

} 

Używam ARC z iOS 5.1 .1

Aktualizacja: mam umieścić punkt przerwania na malloc_error_break i instrumentami widzę to się nazywa od:

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

Rozwiązanie: Jak lawicko & john.k.doe powiedział, starałem się załadować wideo z jest ścieżką do zmiennej NSData. Spowodowało to załadowanie całego filmu do pamięci.Zamiast robić to, że teraz po prostu przenieść plik (& zmiany nazwy) copyItemAtPath

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

Czy możesz znaleźć, który typ obiektu ma największe bajty na żywo? – nhahtdh

+0

@nhahtdh mówi, że nazywa się on: '# \t Adres \t Kategoria \t Datownik \t żywo \t Rozmiar \t Odpowiedzialny Biblioteka \t Odpowiedzialny rozmówcy 0x10900000 \t Malloc 473,29 MB \t 02: 08.951.332 \t • Fundacji \t - [NSData (NSData) initWithContentsOfFile:] ' – Thermometer

+1

'[LocalVideoStorage saveVideo: [NSData dataWithContentsOfPath: self.tempVideoPath] ..." czytasz cały plik tymczasowy do obiektu NSData tylko po to, aby zapisać go ponownie na dysku. Nie możesz po prostu skopiować pliku tymczasowego do wybranego folderu stałego magazynu? – Rog

Odpowiedz

10

Twój problem jest to linia:

[NSData dataWithContentsOfPath:self.tempVideoPath] 

Jesteś wyraźnie próbuje załadować zawartość tego pliku pamięć na raz, ale iOS nigdy nie pozwoli ci załadować tak dużo za jednym razem. Wydaje się, że twoja metoda saveVideo kopiuje tylko plik z tymczasowej lokalizacji do katalogu dokumentów. Jeśli jest to jedyna rzecz, którą musisz tam zrobić, spójrz na copyItemAtPath:toPath:error metodę NSFileManager. Twój może zmienić metodę saveVideo, aby pobrać ścieżkę pliku tymczasowego jako parametr, a nie dane.

+0

lepszą odpowiedź niż moja, ponieważ jest bardziej szczegółowy. przyznać nagrodę tutaj ... –

+0

Wielkie dzięki! Jedyne, co musiałem zmienić, to: '[fileManager createFileAtPath: treść fullPath: atrybuty wideo: nil];' to 'NSError * error = nil; if (! [FileManager copyItemAtPath: ścieżka do Path: fullPath error: & error]) NSLog (@ "Błąd:% @", błąd); ' – Thermometer

+0

Znakomity! Dziękuję bardzo. –

2

dlaczego przeszkadza wyciągając całą zawartość strumienia multimedialnego z
[[info objectForKey:UIImagePickerControllerMediaURL] path] się na NSData* a następnie zapisuje je z powrotem? Twój strumień mediów będzie ogromny!

powodem tego, że dzieje się to przy zapisie, jest to, że nagrywasz dane, to "dysk", a następnie odczytujesz je do pamięci, aby zapisać je z powrotem na inny plik na dysku.

Czy rozważałeś użycie NSFileManager, aby skopiować plik z [[info objectForKey:UIImagePickerControlMediaURL] path] na nazwę, którą utworzyłeś (fullPath)? powinno to uniknąć wciągnięcia całej rzeczy do pamięci; powinien to być transfer "plik do pliku".

Powiązane problemy