お使いのブラウザは、バージョンが古すぎます。

このサイトは、Internet Explore8・Internet Explore9には対応しておりません。
恐れ入りますが、お使いのブラウザをバージョンアップしていただきますよう宜しくお願いいたします。

iPhoneで動作する映像フィルタをつくる:GPUImage+iOSのAVFoundationフレームワークその2

こんにちは、andyです。

今回は、前回のプロジェクトをいじって映像部分の変形を行ってみたいと思います。



AVFondationには元々その機能があって、それを利用するのですが、最後にGPUImageでフィルタを掛けて表示するために、ちょっとだけGPUImageの部分のコードも変更します。



ここプロジェクトを実行すると、オリジナルの映像が70%縮小されてセンターに表示されます。また、コンポジションの画像サイズを640×480に設定しているために、縮小により画像がコンポジションよりも小さい場合には、バックグラウンドとして設定している青色の背景色が表示されます。

コード

それではコードから。

- (void)showFilteringMovie
{
    NSURL *fileURL1 = [[NSBundle mainBundle] URLForResource:@"<最初の映像>" withExtension:@"mp4"];
    NSURL *fileURL2 = [[NSBundle mainBundle] URLForResource:@"<2番目の映像>" withExtension:@"mp4"];
    
    AVMutableComposition* composition = [AVMutableComposition composition];
    AVMutableCompositionTrack* videoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    
    AVURLAsset* firstMovie = [AVURLAsset URLAssetWithURL:fileURL1 options:nil];
    AVURLAsset* secondMovie = [AVURLAsset URLAssetWithURL:fileURL2 options:nil];
    
    AVAssetTrack* firstMovieVideo = [[firstMovie tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    AVAssetTrack* secondMovieVideo = [[secondMovie tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    
    [videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, firstMovie.duration)
                        ofTrack:firstMovieVideo
                         atTime:kCMTimeZero
                          error:nil];
    [videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, secondMovie.duration)
                        ofTrack:secondMovieVideo
                         atTime:firstMovie.duration
                          error:nil];
    
    //解説-1
    AVMutableVideoComposition* transformVideoComposition = [AVMutableVideoComposition videoComposition];
    NSMutableArray *inst = [NSMutableArray array];
    
    CGSize frameSize = CGSizeMake(640.0f, 480.0f);
    float scale = 0.7f;
    
    //解説-2
    AVMutableVideoCompositionInstruction * firstInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
    firstInstruction.backgroundColor = [[UIColor blueColor] CGColor];
    firstInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, firstMovie.duration);
    
    //解説-3
    AVMutableVideoCompositionLayerInstruction *firstlayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:firstMovieVideo];
    CGAffineTransform firstLayerScale = CGAffineTransformMakeScale(scale, scale);
    CGAffineTransform firstLayerMove = CGAffineTransformMakeTranslation((frameSize.width - firstMovieVideo.naturalSize.width * scale) / 2,
                                                              (frameSize.height-firstMovieVideo.naturalSize.height * scale) / 2);
    [firstlayerInstruction setTransform:CGAffineTransformConcat(firstLayerScale, firstLayerMove) atTime:kCMTimeZero];
    
    //解説-4
    firstInstruction.layerInstructions = [NSArray arrayWithObjects:firstlayerInstruction,nil];;
    [inst addObject:firstInstruction];
    
    AVMutableVideoCompositionInstruction * secondInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
    secondInstruction.backgroundColor = [[UIColor blueColor] CGColor];
    secondInstruction.timeRange = CMTimeRangeMake(firstMovie.duration, secondMovie.duration);
    
    AVMutableVideoCompositionLayerInstruction *secondlayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:secondMovieVideo];
    CGAffineTransform secondLayerScale = CGAffineTransformMakeScale(scale, scale);
    CGAffineTransform secondLayerMove = CGAffineTransformMakeTranslation((frameSize.width - secondMovieVideo.naturalSize.width * scale) / 2,
                                                              (frameSize.height - secondMovieVideo.naturalSize.height * scale) / 2);
    [secondlayerInstruction setTransform:CGAffineTransformConcat(secondLayerScale, secondLayerMove) atTime:kCMTimeZero];
    
    secondInstruction.layerInstructions = [NSArray arrayWithObjects:secondlayerInstruction,nil];;
    [inst addObject:secondInstruction];
    
    //解説-5
    transformVideoComposition.instructions = inst;
    
    //解説-6
    transformVideoComposition.renderSize = frameSize;
    transformVideoComposition.frameDuration = CMTimeMake(1, 30);
    
    GPUImageView *filterView = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 240)];
    GPUImagePolkaDotFilter *filter = [[GPUImagePolkaDotFilter alloc] init];

    //movieFile = [[GPUImageMovie alloc] initWithAsset:composition];
    
    //解説-7
    movieFile = [[GPUImageMovieComposition alloc] initWithComposition:composition andVideoComposition:transformVideoComposition andAudioMix:nil];
    movieFile.playAtActualSpeed = YES;

    [movieFile addTarget:filter];
    [filter addTarget:filterView];
    [self.view addSubview:filterView];
    
    [NSTimer scheduledTimerWithTimeInterval:0.5f
                                     target:self
                                   selector: @selector(play:)
                                   userInfo:nil
                                    repeats:NO];
    
}

- (void)play:(id)sender
{
    [movieFile startProcessing];
}

コードの解説

解説-1

まずAVMutableVideoCompositionという映像を取り扱うためのコンポジションを作成します。このコンポジションで、全体のコンポジション(AVMutableComposition)の映像部分だけを最後に置き換えます。

また、このコンポジションに最終的に映像トラックを追加するための配列を用意しています。

その下の部分は、AVMutableVideoCompositionで使用するフレームサイズ(640 x 480)と、サイズ縮小を行うための比率(70%)を設定しています。


解説-2

AVMutableVideoCompositionInstructionというクラスで、最初の映像のレイヤー全体を取り扱う映像トラックを定義して、バックグラウンドの色を青色に指定しています。

また、この入れ物の再生時間を最初のムービーの再生時間に設定しています。


解説-3

AVMutableVideoCompositionLayerInstructionで、先ほど作成したfirstInstructionの中にいれるレイヤーを作成しています。ここでは最初の映像のビデオトラックです。

また、CGAffineTransformでサイズ縮小と、このレイヤーの画面内の位置を設定しています。

最後にこのCGAffineTransformの設定をレイヤーに適用しています。


解説-4

firstInstructionの全レイヤーの要素を、先ほど作成したfirstlayerInstructionのみが入った配列を作成して受け渡しています。複数レイヤーを使用する場合には、ここに追加していきます。

最後に、firstInstructionを解説-1で用意した配列に追加します。

解説-2から解説-4間での作業を2番目の映像でも行います。


解説-5

ここでは、映像トラックを保持している配列を、transformVideoCompositionに追加しています。


解説-6

ここでは、transformVideoCompositionのフレームサイズと1フレームの再生時間(ここでは1/30秒)を指定しています。


解説-7

ここでは、前回GPUImageMovieでAVMutableCompositionを渡していた部分をAVMutableVideoCompositionを渡せるようにするためにGPUImageMovieCompositionというクラスを使っています。

このクラス、実はGPUImage(フレームワーク内)の中には存在しているものの、そのままではヘッダーファイルに記載されていないので使えません。そのため、GPUImageのGPUImage.xcodeprojを開き、GPUImage.hの中に次のコードを追加します。

#import "GPUImageMovieComposition.h"

これで今回のプロジェクト内でこのクラスが使用できるようになります。



ただこのクラス、公開されていないクラスであるために使ってよいのか、はたまたバグがあるから公開していないのかわかりません。



とりあえず動いてはいます。



海外のサイトなどでもこのクラスを使ったコードが公開されていますので、大丈夫なのではないか・・・と思っていますが。



今このコードを元にして映像編集用のアプリを作ってますが、だんだん機能が肥大化してきてしまってどうしようと考えている最中です。何らかの形でリリースしたいと思いますので、その際には読んで頂いている方々の暖かいダウンロードよろしくお願いします。



次回は、何を書こうか考え中ですが、音の扱いにもちょっと触れたいかなと考えています。



それではまた。

コメントをどうぞ

メールアドレスは公開されません。* が付いている欄は必須項目です。


お気軽にお問い合わせください。

日本VTR実験室では、お仕事のご依頼、ブログ・コラムのご感想などを受け付けております。
アプリ開発・コンテンツ制作でお困りでしたら、お気軽にご相談ください。
ご連絡お待ちしております。

お問い合わせはこちらから

03-3541-1230

info@nvtrlab.jp

電話受付対応時間:平日AM9:30〜PM6:00