AVCaptureVideoDataOutputの使い方

# メモ
カメラ入力を画面上にプレビューしつつ、1フレームごとに処理を書く

```swift:ViewController.swift
//
//  ViewController.swift
//
//  Created by Utree on 2019/03/22.
//

import UIKit
import AVFoundation

class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
    // カメラからの入出力データをまとめるセッション
    var session: AVCaptureSession!
    // プレビューレイヤ
    var videoPreviewLayer: AVCaptureVideoPreviewLayer!
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        // カメラを初期化
        self.initCamera()
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        // Viewが閉じられたとき、セッションを終了
        self.session.stopRunning()
        // メモリ解放
        for output in self.session.outputs {
            self.session.removeOutput(output as AVCaptureOutput)
        }
        for input in self.session.inputs {
            self.session.removeInput(input as AVCaptureInput)
        }
        self.session = nil
    }
    
    private func initCamera() {
        // バックカメラを取得
        guard let caputureDevice = AVCaptureDevice.default(for: AVMediaType.video)
            else {
                print("バックカメラにアクセスできません")
                return
        }

        // FPSの設定
        caputureDevice.activeVideoMinFrameDuration = CMTimeMake(value: 1, timescale: 30)
        
        do {
            // 入力設定
            let input = try AVCaptureDeviceInput(device: caputureDevice)
            // 出力設定
            let output: AVCaptureVideoDataOutput = AVCaptureVideoDataOutput()
            // 出力設定: カラーチャンネル
            output.videoSettings = [kCVPixelBufferPixelFormatTypeKey : kCVPixelFormatType_32BGRA] as [String : Any]
            // 出力設定: デリゲート、画像をキャプチャするキュー
            output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
            // 出力設定: キューがブロックされているときに新しいフレームが来たら削除
            output.alwaysDiscardsLateVideoFrames = true
            
            // セッションの作成
            self.session = AVCaptureSession()
            // 解像度を設定
            self.session.sessionPreset = .medium
            
            // セッションに追加.
            if self.session.canAddInput(input) && self.session.canAddOutput(output) {
                self.session.addInput(input)
                self.session.addOutput(output)
                
                // プレビュー開始
                self.startPreview()
            }
        }
        catch _ {
            print("error occurd")
        }
    }
    
    private func startPreview() {
        // 画像を表示するレイヤーを生成
        self.videoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session)
        // カメラ入力の縦横比を維持したまま、レイヤーいっぱいに表示
        self.videoPreviewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        // 縦向きで固定
        self.videoPreviewLayer.connection?.videoOrientation = .portrait
        
        // previewViewに追加
        self.view.layer.addSublayer(videoPreviewLayer)
        
        // startRunningメソッドはブロッキングメソッドなので、非同期に並列処理を行う
        // qos引数は処理の優先順位
        DispatchQueue.global(qos: .userInitiated).async {
            // セッション開始
            self.session.startRunning()
            // 上記処理の終了後、下記処理をメインスレッドで実行
            DispatchQueue.main.async {
                // 'previewView'の大きさに'videoPreviewLayer'をリサイズ
                self.videoPreviewLayer.frame = self.view.bounds
            }
        }
    }
    
    // delegateメソッド
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        print("1フレームごとの処理をここに書く")
    }
}
```