How to get movie with AVFoundation

iPhoneのアプリを作成していてカメラから取得した動画を表示したい。
その時に方法。

## 必要なFramework
* AVFoundation
* CoreMedia
* CoreMovie

## sessionの取得
カメラから動画を取得するにはsessionというものを作成する必要がある。Unixとかでいうstreamみたいなものだと思っている。sessionにはカメラからのInput、ユーザアプリケーションへのOutputの２つの口がある。この２つを弄って、アプリを作ることになる。まず自分の任意のクラスに以下のようにしてsessionをもたせる

```
@interface VideoCameraViewController (){
    AVCaptureSession *_session;
}
```

このクラスの初期化の際に以下のようにしてsessionを作成する。

```
-(void)viewDidLoad{
    AVCaptureDevice*    device;
    device = [AVCaptureDevice
defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    // デバイス入力の取得
    // カメラからのデータはここから入る
    AVCaptureDeviceInput*   deviceInput;
    deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:NULL];
    
    // ビデオデータ出力の作成
    //　アプリからはここからデータを取得する
    NSMutableDictionary*        settings;
    AVCaptureVideoDataOutput*   dataOutput;
    settings = [NSMutableDictionary dictionary];
    [settings setObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA]
                 forKey:(id)kCVPixelBufferPixelFormatTypeKey];
    dataOutput = [[AVCaptureVideoDataOutput alloc] init];
    dataOutput.videoSettings = settings;
    [dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
    
    // セッションの作成
    _session = [[AVCaptureSession alloc] init];
    [_session addInput:deviceInput];
    [_session addOutput:dataOutput];
    
    // セッションの開始
    // ここから撮影が始まる感じ
    [_session startRunning];
}
```

## delegateメソッドの実装
AVCaptureSessionを使うなら`AVCaptureAudioDataOutputSampleBufferDelegate`プロトコルを実装する必要がある。そしてデータがsessionから送られるたびに呼び出されるのが`- (void)captureOutput:(AVCaptureOutput*)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection*)connection`だ

* `captureOutput`: さっきのOutputのインスタンス
* `sampleBuffer`: キャプチャされたデータが入っている
* `connection`: セッションの状態

さてこのメソッドは好きなように実装して大丈夫だけれど、簡単に画面にUIImageとして表示させたい場合は以下のようになる。

```
    // イメージバッファの取得
    CVImageBufferRef    buffer;
    buffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    
    // イメージバッファのロック
	// これをしないbufferがカメラから送られてくるデータが書き換えられていってしまう
    CVPixelBufferLockBaseAddress(buffer, 0);
    
    // イメージバッファ情報の取得
	// 画像の情報を取得する。これが得られればCoreGraphicsを使ってUIImageに変換することができる。
    uint8_t*    base;
    size_t      width, height, bytesPerRow;
    base = CVPixelBufferGetBaseAddress(buffer);
    width = CVPixelBufferGetWidth(buffer);
    height = CVPixelBufferGetHeight(buffer);
    bytesPerRow = CVPixelBufferGetBytesPerRow(buffer);
    
    // ビットマップコンテキストの作成
    CGColorSpaceRef colorSpace;
    CGContextRef    cgContext;
    colorSpace = CGColorSpaceCreateDeviceRGB();
    cgContext = CGBitmapContextCreate(
                                      base, width, height, 8, bytesPerRow, colorSpace,
                                      kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
    CGColorSpaceRelease(colorSpace);
    
    // 画像の作成
    CGImageRef  cgImage;
    UIImage*    image;
    cgImage = CGBitmapContextCreateImage(cgContext);
    image = [UIImage imageWithCGImage:cgImage scale:1.0f
                          orientation:UIImageOrientationRight];
    CGImageRelease(cgImage);
    CGContextRelease(cgContext);
    
    // イメージバッファのアンロック
	// カメラからまた送られてくるようにするため、忘れずに
    CVPixelBufferUnlockBaseAddress(buffer, 0);
    
    // 画像の表示
	// 適当に貼っつけたUIImageViewで表示
    imageView.image = image;

```

これでカメラを通した動画をリアルタイムにみることができる。