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:
Ứ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);
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
@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
'[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