AVAssetImageGeneratorを使って立方体に動画を貼付けて再生してみる

AVAssetImageGenerator を使うと、動画から任意の時刻のフレームを静止画として切り出すことができます。そこで、切り出した静止画をテクスチャにして、立方体に貼付けて再生してみました。

なお、AVAssetImageGenerator はiOS4以降でしか使えませんが、僕はiOS4で動くデバイスを持っていないのでシミュレータ上でしか動作確認をしていません。実機で動かすとかなり重いんじゃないかという気がしてます。早くiPhone4Sを手に入れないと…


ソースコード: http://kuramo.ch/iosdev/GLMovie.zip

(動画ファイルを含んでいるので約19MBあります。実行すると音も出ますので注意してください)

できること

  • ピンチ操作で拡大縮小
  • パン操作で回転
  • シングルタップで一時停止/再開
  • ダブルタップで停止(最初に巻き戻る)
  • 画面の上端付近をタップすると2倍速、下端付近をタップすると1/2倍速


AVAssetImageGenerator について

AV Foundation Programming Guide (日本語PDF) には次のように書かれています。

AVURLAsset の初期化メソッドは、第2引数として options ディクショナリを取ります。このディクショナリで使用される唯一のキーは AVURLAssetPreferPreciseDurationAndTimingKey です。対応する値は、正確な再生時間を指定し、時間単位の正確なランダムアクセスを許可するようにアセットを準備する必要があるかどうかを示すブール値です。

しかし、そのようにして初期化した AVURLAsset から AVAssetImageGenerator を作成しても指定した時刻どおりにフレームを切り出すことはできませんでした。AVURLAssetPreferPreciseDurationAndTimingKey の値によらず、直前のキーフレームが切り出されるようです。これは困りました…

さて、上記の記述の少し先を見てみると、次のようなことも書かれています。

アセットをコンポジション(AVMutableComposition)に追加する場合は、通常、正確なランダムアクセスが必要です。次のように、AVURLAssetPreferPreciseDurationAndTimingKey キーとその値として(NSNumberオブジェクトに格納された)YESを含むディクショナリを渡します。

これはつまり、AVURLAssetPreferPreciseDurationAndTimingKey の値が意味をなすのは AVMutableComposition と共に使用した場合なのではないだろうかと考えて試してみたところ、うまくいきました。だいたい以下のような手順になります。

NSURL *assetURL = [[NSBundle mainBundle] URLForResource:@"movie.mp4" withExtension:nil];
NSDictionary *assetOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
                                            forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:assetURL options:assetOptions];

AVAssetTrack *srcTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVMutableComposition *comp = [AVMutableComposition composition];
AVMutableCompositionTrack *track = [comp addMutableTrackWithMediaType:AVMediaTypeVideo
                                          preferredTrackID:kCMPersistentTrackID_Invalid];
NSError *error = nil;
BOOL ok = [track insertTimeRange:srcTrack.timeRange ofTrack:srcTrack atTime:kCMTimeZero error:&error];
AVAssetImageGenerator *imageGen = [[AVAssetImageGenerator alloc] initWithAsset:comp];