こんにちは、nukkyです。
今回はメインのビューの右上とかに小窓の様なプレビューにカメラの映像を出して、動画撮影するという機能を作成したのですが、思ったより苦戦したので備忘録がてら記事にしたいと思います。
Xcode 9.1
iOS 11 Simulator
Swift 4.0
まずは以下の二つをインポートします。
「AVFoundation」は動画撮影に、「Photos」動画を保存する際に必要になってきます。
import AVFoundation import Photos
動画保存の為に「AVCaptureFileOutputRecordingDelegate」を設定します。
class RecordViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
以下のグローバル変数を宣言しておきます。
// カメラやマイクへのセッション管理用 var captureSession:AVCaptureSession! // 動画ファイルの出力管理用 var fileOutput:AVCaptureMovieFileOutput! // View上にカメラの映像を表示用 var videoLayer:AVCaptureVideoPreviewLayer! // 動画撮影状態管理用フラグ var isRecoding = false
AVCaptureSessionとAVCaptureMovieFileOutputの初期化を行います。
今回はフロントカメラを使用します。
// セッションのインスタンス生成 captureSession = AVCaptureSession() // 入力(前面カメラ) var videoDevice: AVCaptureDevice? // OSバージョンでカメラの指定が異なる if #available(iOS 10.0, *) { let discoverySession = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInDuoCamera], mediaType: AVMediaTypeVideo, position: .front) for device in (discoverySession?.devices)! { videoDevice = device } } else { let videoDevices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) for device in videoDevices!{ let device = device as! AVCaptureDevice if device.position == AVCaptureDevicePosition.front { videoDevice = device break } } } let videoInput = try! AVCaptureDeviceInput.init(device: videoDevice) captureSession.addInput(videoInput) // 入力(マイク)動画の音声いらない?もしくは動画保存中に録音もするから無いほうがいい? let audioDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio) let audioInput = try! AVCaptureDeviceInput.init(device: audioDevice) captureSession.addInput(audioInput); // 出力(動画ファイル) fileOutput = AVCaptureMovieFileOutput() captureSession.addOutput(fileOutput)
ここが今回の肝ですが、カメラの映像を小窓の様にして表示させます。
今回はViewの左上に240の正方形で表示させます。
// プレビュー表示 func previewInitialization(view:UIView, frame: CGRect = CGRect(x: 0, y: 60, width: 240, height: 240)) { if let videoLayer = AVCaptureVideoPreviewLayer.init(session: captureSession) { videoLayer.frame = frame videoLayer.videoGravity = AVLayerVideoGravityResizeAspectFill self.videoLayer = videoLayer view.layer.addSublayer(self.videoLayer) } }
録画の開始と終了は以下になります。
// 録画開始 func startRecord(fileURL:NSURL) { // toOutputFileURLにファイルのパスをファイル名と拡張子込みで渡す fileOutput?.startRecording(toOutputFileURL: fileURL as URL!, recordingDelegate: self) } // 録画終了 func stopRecord() { fileOutput.stopRecording() }
stopRecordingメソッドを呼ぶと以下のDelegateが呼ばれるので、ここでファイルを保存します。
// 録画終了時に動画を保存 public func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) { if error != nil { // エラー処理 return } // ライブラリへの保存 PHPhotoLibrary.shared().performChanges({ PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputFileURL) }) { completed, error in if completed { print("Video is saved!") } } }
駆け足になってしまいましたが、これで自由にカメラの映像をViewに重ねて動画の保存ができると思います。
個人的に、カメラの映像のframe自由に設定して画面表示する箇所がソースにすると大した事ないのですが調査も含めて苦戦したので、これが誰かのお役に立てれば報われます。