2012-06-15 25 views
6

Vấn đề:Khi lưu video ghi lại đó là quá dài, ứng dụng bị treo

Khi lưu một đoạn video được ghi lại trong ứng dụng của tôi, nếu kích thước video/thời gian là quá lớn/dài, treo ứng dụng của mình mà không nhật ký/ngoại lệ.

Cài đặt của tôi:

Trong App của tôi, tôi sử dụng một UIImagePickerController để ghi lại video. Bây giờ tôi đã nhận thấy rằng nếu tôi làm cho video của tôi rất dài (ví dụ 30 phút với UIImagePickerControllerQualityTypeMedium, hoặc hơn một phút với UIImagePickerControllerQualityTypeIFrame1280x720), khi lưu video, ứng dụng gặp sự cố. Đôi khi có và đôi khi không có cảnh báo. Bây giờ tôi bắt đầu gỡ lỗi và nhận thấy nó có liên quan đến bộ nhớ (malloc_error).

Tôi đã sử dụng trình hồ sơ để kiểm tra phân bổ trực tiếp và nhận thấy rằng khi nó lưu video, phân bổ đột nhiên trở nên rất lớn (tôi đoán có điều gì đó liên quan đến việc sử dụng bộ nhớ tạm thời cho video?) Trước khi nó bị hỏng . Đây là ảnh chụp màn hình từ profiler: Allocations screenshot

Ứng dụng phải có thể quay video với thời lượng tối đa 1 giờ (theo bất kỳ chất lượng nào được chỉ định).

Những gì tôi đã cố gắng:

  • Thiết lập picker.videoMaximumDuration ngắn/dài
  • Debug với hồ sơ/công cụ
  • Kiểm tra rò rỉ
  • đóng tất cả các ứng dụng mở & xóa ứng dụng trên thiết bị (để làm sạch bộ nhớ) để có thêm bộ nhớ

Code:

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

Và mã khi hình ảnh được chọn:

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

Và cuối cùng, khi nó được lưu:

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

} 

Tôi đang sử dụng ARC với iOS 5.1 .1

Cập nhật: Tôi đã đặt một breakpoint trên malloc_error_break, và trong các văn bản tôi có thể thấy nó được gọi là từ:

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

Giải pháp: Như lawicko & john.k.doe nói, tôi đã cố gắng để tải các video từ đó là đường dẫn vào biến NSData. Điều này làm cho toàn bộ video được tải vào bộ nhớ.Thay vì làm điều đó, tôi bây giờ chỉ cần di chuyển các tập tin (& đổi tên) copyItemAtPath

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

Bạn có thể tìm thấy loại đối tượng nào có byte trực tiếp lớn nhất không? – nhahtdh

+0

@nhahtdh nó nói nó được gọi vào lúc: '# \t Địa chỉ \t Thể loại \t Timestamp \t Sống \t Kích \t Phụ trách Thư viện \t Caller Chịu trách nhiệm 0x10900000 \t Malloc 473,29 MB \t 02: 08.951.332 \t • \t 496.283.648 \t Foundation \t - [NSData (NSData) initWithContentsOfFile:] ' – Thermometer

+1

'[LocalVideoStorage saveVideo: [NSData dataWithContentsOfPath: self.tempVideoPath] ...' bạn đang đọc toàn bộ tệp tạm thời vào đối tượng NSData chỉ để lưu lại vào đĩa. Bạn không thể sao chép tệp tạm thời vào thư mục lưu trữ vĩnh viễn đã chọn của mình? – Rog

Trả lời

10

Vấn đề của bạn là dòng này:

[NSData dataWithContentsOfPath:self.tempVideoPath] 

Bạn đang cố gắng rõ ràng để tải nội dung của tập tin này để bộ nhớ cùng một lúc, nhưng iOS sẽ không bao giờ cho phép bạn tải nhiều dữ liệu cùng một lúc. Phương thức saveVideo của bạn dường như chỉ sao chép tệp từ vị trí tạm thời vào thư mục tài liệu. Nếu đây là điều duy nhất bạn cần làm ở đó, hãy xem phương thức copyItemAtPath:toPath:error của NSFileManager. Bạn có thể thay đổi phương thức saveVideo để lấy đường dẫn tệp tạm thời làm tham số thay vì dữ liệu.

+0

câu trả lời hay hơn tôi vì nó cụ thể hơn. giải thưởng tiền thưởng ở đây ... –

+0

Cảm ơn rất nhiều !!! điều duy nhất tôi cần thay đổi là '[fileManager createFileAtPath: nội dung fullPath: thuộc tính video: nil];' đến 'NSError * error = nil; nếu (! [FileManager copyItemAtPath: đường dẫn đếnPath: lỗi fullPath: & lỗi]) NSLog (@ "Lỗi:% @", lỗi); ' – Thermometer

+0

Tuyệt vời! Cảm ơn bạn rất nhiều. –

2

lý do tại sao bận tâm kéo toàn bộ nội dung của luồng phương tiện từ
[[info objectForKey:UIImagePickerControllerMediaURL] path] vào NSData* rồi viết lại chúng? luồng truyền thông của bạn sẽ rất lớn!

lý do điều này xảy ra khi lưu là vì bạn đang ghi dữ liệu, nó sẽ là "đĩa", và sau đó bạn đọc nó vào bộ nhớ để ghi nó ra tệp đĩa khác.

bạn có cân nhắc sử dụng NSFileManager để sao chép tệp từ [[info objectForKey:UIImagePickerControlMediaURL] path] vào tên bạn tạo (fullPath) không? điều này nên tránh kéo toàn bộ điều vào bộ nhớ; nó phải là một chuyển tập tin "thành tập tin".

Các vấn đề liên quan