メモ
カメラ入力を画面上にプレビューしつつ、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フレームごとの処理をここに書く")
}
}