iOS
swift4

Swift4で動画を撮影・保存する

swift4で動画を撮影し、保存する手順を説明します。

手順

  1. frameworkの追加
  2. AVCaptureSession, AVCaptureDeviceの設定
  3. プレビューの生成
  4. 録画開始、停止ボタンを追加
  5. 動画を保存

frameworkの追加

AVFoundationとPhotosをimportします。

AVCaptureSession, AVCaptureDeviceの設定

はじめに入力と出力を設定し、その情報をAVCaptureSessionに渡します。

let videoDevice = AVCaptureDevice.default(for: AVMediaType.video)
let audioDevice = AVCaptureDevice.default(for: AVMediaType.audio)

do {
    if videoDevice == nil || audioDevice == nil {
        throw NSError(domain: "device error", code: -1, userInfo: nil)
    }
    let captureSession = AVCaptureSession()

    // video inputを capture sessionに追加
    let videoInput = try AVCaptureDeviceInput(device: videoDevice!)
    captureSession.addInput(videoInput)

    // audio inputを capture sessionに追加
    let audioInput = try AVCaptureDeviceInput(device: audioDevice!)
    captureSession.addInput(audioInput)

    // 録画時間の上限を設けたい時は maxRecordedDurationを設定する
    fileOutput.maxRecordedDuration = CMTimeMake(30, 1)

    // 出力をsessionに追加
    captureSession.addOutput(fileOutput)

} catch {
    // エラー処理
}

プレビューの生成

AVCaptureVideoPreviewLayerを使い、画面全体にプレビューを表示します。

// プレビュー
let videoLayer : AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoLayer.frame = self.view.bounds
videoLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.view.layer.addSublayer(videoLayer)

captureSession.startRunning()

録画ボタンを追加

func setUpButton() {
    recordButton = UIButton(frame: CGRect(x: 0,y: 0,width: 120,height: 50))
    recordButton.backgroundColor = UIColor.gray
    recordButton.layer.masksToBounds = true
    recordButton.setTitle("録画開始", for: UIControlState.normal)
    recordButton.layer.cornerRadius = 20.0
    recordButton.layer.position = CGPoint(x: self.view.bounds.width/2, y:self.view.bounds.height-50)
    recordButton.addTarget(self, action: #selector(RecordViewController.onClickRecordButton(sender:)), for: .touchUpInside)

    self.view.addSubview(recordButton)
}

@objc func onClickRecordButton(sender: UIButton) {
    if !isRecording {
        // 録画開始
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        let documentsDirectory = paths[0] as String
        let filePath : String? = "\(documentsDirectory)/temp.mp4"
        let fileURL : NSURL = NSURL(fileURLWithPath: filePath!)
        fileOutput.startRecording(to: fileURL as URL, recordingDelegate: self)

        isRecording = true
        changeButtonColor(target: recordButton, color: UIColor.red)
        recordButton.setTitle("録画中", for: .normal)
    } else {
        // 録画終了
        fileOutput.stopRecording()

        isRecording = false
        changeButtonColor(target: recordButton, color: UIColor.gray)
        recordButton.setTitle("録画開始", for: .normal)
    }
}

func changeButtonColor(target: UIButton, color: UIColor) {
    target.backgroundColor = color
}

動画を保存

録画が停止されるとこのメソッドが呼び出されるので、この中で動画を保存する処理を行います。

func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
    // ライブラリへ保存
    PHPhotoLibrary.shared().performChanges({
        PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputFileURL)
    }) { completed, error in
        if completed {
            print("Video is saved!")
        }
    }
}

ソースコード

import UIKit
import AVFoundation
import Photos

class RecordViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
    let fileOutput = AVCaptureMovieFileOutput()

    var recordButton: UIButton!
    var isRecording = false

    override func viewDidLoad() {
        super.viewDidLoad()

        setUpPreview()
    }

    func setUpPreview() {
        let videoDevice = AVCaptureDevice.default(for: AVMediaType.video)
        let audioDevice = AVCaptureDevice.default(for: AVMediaType.audio)

        do {
            if self.videoDevice == nil || self.audioDevice == nil {
                throw NSError(domain: "device error", code: -1, userInfo: nil)
            }
            let captureSession = AVCaptureSession()

            // video inputを capture sessionに追加
            let videoInput = try AVCaptureDeviceInput(device: self.videoDevice!)
            captureSession.addInput(videoInput)

            // audio inputを capture sessionに追加
            let audioInput = try AVCaptureDeviceInput(device: self.audioDevice!)
            captureSession.addInput(audioInput)

            // max 30sec
            self.fileOutput.maxRecordedDuration = CMTimeMake(30, 1)
            captureSession.addOutput(fileOutput)

            // プレビュー
            let videoLayer : AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
            videoLayer.frame = self.view.bounds
            videoLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
            self.view.layer.addSublayer(videoLayer)

            captureSession.startRunning()

            setUpButton()
        } catch {
            // エラー処理
        }
    }

    func setUpButton() {
        recordButton = UIButton(frame: CGRect(x: 0,y: 0,width: 120,height: 50))
        recordButton.backgroundColor = UIColor.gray
        recordButton.layer.masksToBounds = true
        recordButton.setTitle("録画開始", for: UIControlState.normal)
        recordButton.layer.cornerRadius = 20.0
        recordButton.layer.position = CGPoint(x: self.view.bounds.width/2, y:self.view.bounds.height-50)
        recordButton.addTarget(self, action: #selector(RecordViewController.onClickRecordButton(sender:)), for: .touchUpInside)

        self.view.addSubview(recordButton)
    }

    @objc func onClickRecordButton(sender: UIButton) {
        if !isRecording {
            // 録画開始
            let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
            let documentsDirectory = paths[0] as String
            let filePath : String? = "\(documentsDirectory)/temp.mp4"
            let fileURL : NSURL = NSURL(fileURLWithPath: filePath!)
            fileOutput.startRecording(to: fileURL as URL, recordingDelegate: self)

            isRecording = true
            changeButtonColor(target: recordButton, color: UIColor.red)
            recordButton.setTitle("録画中", for: .normal)
        } else {
            // 録画終了
            fileOutput.stopRecording()

            isRecording = false
            changeButtonColor(target: recordButton, color: UIColor.gray)
            recordButton.setTitle("録画開始", for: .normal)
        }
    }

    func changeButtonColor(target: UIButton, color: UIColor) {
        target.backgroundColor = color
    }

    func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
        // ライブラリへ保存
        PHPhotoLibrary.shared().performChanges({
            PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputFileURL)
        }) { completed, error in
            if completed {
                print("Video is saved!")
            }
        }
    }
}