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