LoginSignup
14
10

More than 5 years have passed since last update.

AVCaptureVideoDataOutputの使い方

Posted at

メモ

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

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フレームごとの処理をここに書く")
    }
}
14
10
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
10