1

私のプロジェクトでは、ビデオの各フレームのチャンクを1つの一意の結果の画像にコピーする必要があります。

ビデオフレームのキャプチャは大したことではありません。それは次のようなものになります:

// duration is the movie lenght in s.
// frameDuration is 1/fps. (or 24fps, frameDuration = 1/24)
// player is a MPMoviePlayerController
for (NSTimeInterval i=0; i < duration; i += frameDuration) {
    UIImage * image = [player thumbnailImageAtTime:i timeOption:MPMovieTimeOptionExact];

    CGRect destinationRect = [self getDestinationRect:i];
    [self drawImage:image inRect:destinationRect fromRect:originRect];

    // UI feedback
    [self performSelectorOnMainThread:@selector(setProgressValue:) withObject:[NSNumber numberWithFloat:x/totalFrames] waitUntilDone:NO];
}

drawImage:inRect:fromRect:メソッドを実装しようとすると問題が発生します。私はこのコード
を試しました:

  1. CGImageCreateWithImageInRectビデオフレームから新しいCGImageを作成して、画像のチャンクを抽出します。
  2. ImageContextにCGContextDrawImageを作成して、チャンクを描画します

しかし、ビデオが12〜14秒に達すると、私のiPhone 4Sは3回目のメモリ警告を通知し、クラッシュします。リークツールを使用してアプリのプロファイルを作成しましたが、リークはまったく見つかりませんでした...

私はクォーツにはあまり強くありません。これを達成するためのより良い最適化された方法はありますか?

4

2 に答える 2

1

最後に、コードの Quartz 部分を保持し、画像を取得する方法を変更しました。

今では、はるかに高速なソリューションである AVFoundation を使用しています。

// Creating the tools : 1/ the video asset, 2/ the image generator, 3/ the composition, which helps to retrieve video properties.
AVURLAsset *asset = [[[AVURLAsset alloc] initWithURL:moviePathURL
                                             options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], AVURLAssetPreferPreciseDurationAndTimingKey, nil]] autorelease];
AVAssetImageGenerator *generator = [[[AVAssetImageGenerator alloc] initWithAsset:asset] autorelease];
generator.appliesPreferredTrackTransform = YES; // if I omit this, the frames are rotated 90° (didn't try in landscape)
AVVideoComposition * composition = [AVVideoComposition videoCompositionWithPropertiesOfAsset:asset];

// Retrieving the video properties
NSTimeInterval duration = CMTimeGetSeconds(asset.duration);
frameDuration = CMTimeGetSeconds(composition.frameDuration);
CGSize renderSize = composition.renderSize;
CGFloat totalFrames = round(duration/frameDuration);

// Selecting each frame we want to extract : all of them.
NSMutableArray * times = [NSMutableArray arrayWithCapacity:round(duration/frameDuration)];
for (int i=0; i<totalFrames; i++) {
    NSValue *time = [NSValue valueWithCMTime:CMTimeMakeWithSeconds(i*frameDuration, composition.frameDuration.timescale)];
    [times addObject:time];
}

__block int i = 0;
AVAssetImageGeneratorCompletionHandler handler = ^(CMTime requestedTime, CGImageRef im, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error){
    if (result == AVAssetImageGeneratorSucceeded) {
        int x = round(CMTimeGetSeconds(requestedTime)/frameDuration);
        CGRect destinationStrip = CGRectMake(x, 0, 1, renderSize.height);
        [self drawImage:im inRect:destinationStrip fromRect:originStrip inContext:context];
    }
    else
        NSLog(@"Ouch: %@", error.description);
    i++;
    [self performSelectorOnMainThread:@selector(setProgressValue:) withObject:[NSNumber numberWithFloat:i/totalFrames] waitUntilDone:NO];
    if(i == totalFrames) {
        [self performSelectorOnMainThread:@selector(performVideoDidFinish) withObject:nil waitUntilDone:NO];
    }
};

// Launching the process...
generator.requestedTimeToleranceBefore = kCMTimeZero;
generator.requestedTimeToleranceAfter = kCMTimeZero;
generator.maximumSize = renderSize;
[generator generateCGImagesAsynchronouslyForTimes:times completionHandler:handler];

非常に長いビデオでも、時間はかかりますが、クラッシュすることはありません。

于 2013-02-26T20:32:29.387 に答える