前に UIImagePickerControllerやAVCapturePhotoOutput でカメラを扱いましたが
AVCaptureVideoDataOutput でも静止画を撮影できます。ただし基本的には動画撮影用です。
画面タップでシャッターを切る例です。余談ですがプレビュー無しの無音カメラができてしまいます。
Swift 5.1
Xcode 11.3.1
Xcode 11.3.1
AVCaptureVideoDataOutput
iPhoneでは写真の静止画フォーマットが「HEIF」、動画は「HEVC(H.265)」と新しいフォーマット(形式)になっています。
ちょっと忘れがちでハマることがありますので
Camera使用上の許可
Cameraを扱う注意点として、
- 実機で試す
- simulatorではデバッグできません、実機で試してください。
オーディオおよびビデオ入力(カメラとマイクロフォン)はサポートされていません。
- simulatorではデバッグできません、実機で試してください。
- Info.plistの編集
- 以下の設定をInfo.plistに記述しないとエラーになります。
UIImagePickerController による Camera撮影1234567...<dict><key>NSCameraUsageDescription</key><string>許可せんとcamera使えまへんで〜</string><key>NSPhotoLibraryAddUsageDescription</key><string>写真を保存しますので許可してください</string>...
- 以下の設定をInfo.plistに記述しないとエラーになります。
設定
カメラ設定について抜粋ですが、
背面、前面カメラの切り替え
position: .back
position: .front
1 2 3 4 5 |
// 背面・前面カメラの選択 camera = AVCaptureDevice.default( AVCaptureDevice.DeviceType.builtInWideAngleCamera, for: AVMediaType.video, position: .back) |
キャプチャ・クオリティの設定は
VideResolution = AVCaptureSessionPresetHigh
としていますが、その他の設定もできます
- AVCaptureSessionPresetPhoto
- 写真用の最大解像度, ビデオ出力ではサポートされない
- AVCaptureSessionPresetHigh
- 最高の録画品質
- AVCaptureSessionPresetMedium
- WiFi共有用
- AVCaptureSessionPresetLow
- 3G共有
- AVCaptureSessionPreset352x288
- CIF
- AVCaptureSessionPreset640x480
- VGA
- AVCaptureSessionPreset1280x720
- HD, 720p
- AVCaptureSessionPresetiFrame960x540
- 960×540のiFrame ビデオ
- AVCaptureSessionPresetiFrame1280x720
- 1280×720iFrame ビデオ
サンプルコード
カメラを起動し、キャプチャー画面をタップして写真を保存する基本的なコードです。
Tap を受け取るために、UIGestureRecognizerDelegate をdelegateで設定します
ViewController.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
import UIKit // AVFoundationのインポート import AVFoundation class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate, UIGestureRecognizerDelegate { var input:AVCaptureDeviceInput! var output:AVCaptureVideoDataOutput! var session:AVCaptureSession! var camera:AVCaptureDevice! var imageView:UIImageView! override func viewDidLoad() { super.viewDidLoad() // 画面タップでシャッターを切るための設定 let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer( target: self, action: #selector(ViewController.tapped(_:))) // デリゲートをセット tapGesture.delegate = self; // Viewに追加. self.view.addGestureRecognizer(tapGesture) } // SafeAreaのサイズを取得するためviewDidAppearまで待つ override func viewDidAppear(_ animated: Bool) { // スクリーン設定 setupDisplay() // カメラの設定 setupCamera() } func setupDisplay(){ //スクリーンの幅 let screenWidth = UIScreen.main.bounds.size.width; //スクリーンの高さ let screenHeight = UIScreen.main.bounds.size.height; // プレビュー用のビューを生成 imageView = UIImageView() var topPadding:CGFloat = 0 //var bottomPadding:CGFloat = 0 var leftPadding:CGFloat = 0 var rightPadding:CGFloat = 0 // iPhone X , X以外は0となる if #available(iOS 11.0, *) { let window = UIApplication.shared.keyWindow topPadding = window!.safeAreaInsets.top //bottomPadding = window!.safeAreaInsets.bottom leftPadding = window!.safeAreaInsets.left rightPadding = window!.safeAreaInsets.right } // portrait let safeAreaWidth = screenWidth - leftPadding - rightPadding //let safeAreaHeight = (screenHeight) - topPadding - bottomPadding // カメラ画像サイズはsessionPresetによって変わる // とりあえず16:9のportraitとして設定 let rect = CGRect(x: leftPadding, y: topPadding, width: safeAreaWidth, height: safeAreaWidth/9*16) // frame をCGRectで作った矩形に合わせる imageView.frame = rect imageView.center = CGPoint(x: screenWidth/2, y: screenHeight/2) } func setupCamera(){ // AVCaptureSession: キャプチャに関する入力と出力の管理 session = AVCaptureSession() // sessionPreset: キャプチャ・クオリティの設定 session.sessionPreset = AVCaptureSession.Preset.high // session.sessionPreset = AVCaptureSessionPresetPhoto // session.sessionPreset = AVCaptureSessionPresetHigh // session.sessionPreset = AVCaptureSessionPresetMedium // session.sessionPreset = AVCaptureSessionPresetLow // 背面・前面カメラの選択 camera = AVCaptureDevice.default( AVCaptureDevice.DeviceType.builtInWideAngleCamera, for: AVMediaType.video, position: .back) // position: .front // カメラからの入力データ do { input = try AVCaptureDeviceInput(device: camera) as AVCaptureDeviceInput } catch let error as NSError { print(error) } // 入力をセッションに追加 if(session.canAddInput(input)) { session.addInput(input) } // AVCaptureStillImageOutput:静止画 // AVCaptureMovieFileOutput:動画ファイル // AVCaptureAudioFileOutput:音声ファイル // AVCaptureVideoDataOutput:動画フレームデータ // AVCaptureAudioDataOutput:音声データ // AVCaptureVideoDataOutput:動画フレームデータを出力に設定 output = AVCaptureVideoDataOutput() // 出力をセッションに追加 if(session.canAddOutput(output)) { session.addOutput(output) } // ピクセルフォーマットを 32bit BGR + A とする output.videoSettings = [kCVPixelBufferPixelFormatTypeKey as AnyHashable as! String : Int(kCVPixelFormatType_32BGRA)] // フレームをキャプチャするためのサブスレッド用のシリアルキューを用意 output.setSampleBufferDelegate(self, queue: DispatchQueue.main) output.alwaysDiscardsLateVideoFrames = true // ビデオ出力に接続 //let connection = output.connection(with: AVMediaType.video) session.startRunning() // deviceをロックして設定 // swift 2.0 do { try camera.lockForConfiguration() // フレームレート camera.activeVideoMinFrameDuration = CMTimeMake(value: 1, timescale: 30) camera.unlockForConfiguration() } catch _ { } } // 新しいキャプチャの追加で呼ばれる func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { // キャプチャしたsampleBufferからUIImageを作成 let image:UIImage = self.captureImage(sampleBuffer) // 画像を画面に表示 DispatchQueue.main.async { self.imageView.image = image // UIImageViewをビューに追加 self.view.addSubview(self.imageView) } } // sampleBufferからUIImageを作成 func captureImage(_ sampleBuffer:CMSampleBuffer) -> UIImage{ // Sampling Bufferから画像を取得 let imageBuffer:CVImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)! // pixel buffer のベースアドレスをロック CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: CVOptionFlags(0))) let baseAddress:UnsafeMutableRawPointer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0)! let bytesPerRow:Int = CVPixelBufferGetBytesPerRow(imageBuffer) let width:Int = CVPixelBufferGetWidth(imageBuffer) let height:Int = CVPixelBufferGetHeight(imageBuffer) // 色空間 let colorSpace:CGColorSpace = CGColorSpaceCreateDeviceRGB() //let bitsPerCompornent:Int = 8 // swift 2.0 let newContext:CGContext = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue|CGBitmapInfo.byteOrder32Little.rawValue)! let imageRef:CGImage = newContext.makeImage()! let resultImage = UIImage(cgImage: imageRef, scale: 1.0, orientation: UIImage.Orientation.right) return resultImage } // タップイベント. @objc func tapped(_ sender: UITapGestureRecognizer){ print("タップ") takeStillPicture() } func takeStillPicture(){ if var _:AVCaptureConnection = output.connection(with: AVMediaType.video){ // アルバムに追加 UIImageWriteToSavedPhotosAlbum(self.imageView.image!, self, nil, nil) } } } |
これで実機でカメラを起動させて、画面タップで写真が保存されているか確認してください。
関連: