6

問題:

アプリに記録されたビデオを保存するときに、ビデオのサイズ/長さが大きすぎる/長すぎると、ログ/例外なしでアプリがクラッシュします。

私のセットアップ:

私のアプリでは、UIImagePickerControllerを使用してビデオを録画しています。ビデオの長さを非常に長くすると(たとえば、UIImagePickerControllerQualityTypeMediumで30分、UIImagePickerControllerQualityTypeIFrame1280x720で1分以上)、ビデオを保存するとアプリがクラッシュすることに気付きました。警告がある場合とない場合があります。今、私はデバッグを開始し、それがメモリ(malloc_error)と関係があることに気づきました。

プロファイラーを使用して割り当てをライブで確認しましたが、ビデオを保存しようとすると、最終的にクラッシュする前に、割り当てが突然非常に大きくなることに気付きました(ビデオの一時的なメモリ使用量と関係があると思いますか?)。プロファイラーのスクリーンショットは次のとおりです。 割り当てのスクリーンショット

アプリは、最大1時間(指定された品質で)のビデオを録画できる必要があります。

私が試したこと:

  • picker.videoMaximumDurationを短く/長く設定する
  • プロファイラー/機器を使用してデバッグする
  • 漏れがないか確認してください
  • より多くのメモリを取得するために、デバイスで開いているすべてのアプリと削除されたアプリを閉じました(ストレージのクリーニング用)

コード:

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

そして、画像が選択されたときのコード:

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

そして最後に、それが保存されたとき:

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

}

iOS5.1.1でARCを使用しています

更新: malloc_error_breakにブレークポイントを設定しました。インストゥルメントでは、次の場所から呼び出されていることがわかります。

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

解決策: lawicko&john.k.doeが言ったように、私はビデオをそのパスからNSData変数にロードしようとしました。これにより、ビデオ全体がメモリにロードされました。その代わりに、ファイルを移動(および名前変更)するだけです。copyItemAtPath

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

2 に答える 2

10

あなたの問題はこの行です:

[NSData dataWithContentsOfPath:self.tempVideoPath]

このファイルの内容を一度にすべてメモリにロードしようとしているのは明らかですが、iOS では一度に多くをロードすることはできません。あなたのsaveVideo方法は、ファイルを一時的な場所からドキュメントディレクトリにコピーするだけのようです。これだけを行う必要がある場合は、 NSFileManager のcopyItemAtPath:toPath:errorメソッドを参照してください。saveVideoデータではなく、一時ファイルのパスをパラメーターとして受け取るようにメソッドを変更できます。

于 2012-06-26T11:26:56.297 に答える
2

メディア ストリームのコンテンツ全体を から
[[info objectForKey:UIImagePickerControllerMediaURL] path]取り出しNSData*てから書き戻す必要があるのはなぜですか? あなたのメディアストリームは巨大になるでしょう!

保存時にこれが発生する理由は、データを記録して「ディスク」に移動し、それをメモリに読み取って別のディスク ファイルに書き戻すためです。

NSFileManagerファイル[[info objectForKey:UIImagePickerControlMediaURL] path]を作成した名前(fullPath)にコピーするために使用することを検討しましたか?これにより、全体がメモリに読み込まれるのを回避できます。「ファイルからファイルへ」の転送である必要があります。

于 2012-06-26T11:27:53.230 に答える