iOS11から本体機能にスクリーンレコーディングがついたということで、ここではあえてMacOSでスクリーンレコーディングする方法を紹介してみたいと思います。
MacOSの場合標準のAVFoundation
を使用することでカメラやマイク、画面のレコーディングを行うことができます。
基本的には
セッションの作成
-> 入力元の指定
-> 出力先の指定
-> 開始
のステップとなっており、それぞれ入力元
と出力先
をカスタマイズすることで任意のデバイス(カメラ、マイク・画面等)から好きな形式で出力することができます。
では、実際にMacのメインディスプレイをレコーディングしてMP4形式で出力する方法を紹介したいと思います。
/*** 画面を10秒録画して.mp4で出力 ***/
import Cocoa
final class ViewController: NSViewController {
private let recorder = ScreenRecorder()
override func viewDidLoad() {
super.viewDidLoad()
// 録画開始
recorder.start()
// 10秒後に録画停止
DispatchQueue.main.asyncAfter(deadline: .now() + 10) { [weak self] in
self?.recorder.stop()
}
}
}
/*** 画面を10秒録画して.mp4で出力 ***/
import Foundation
import AVFoundation
import Cocoa
final class ScreenRecorder: NSObject, AVCaptureFileOutputRecordingDelegate {
private let session = AVCaptureSession()
// 出力形式をファイル保存にする
private let output = AVCaptureMovieFileOutput()
// 保存ファイル名
private let fileName = "sample.mp4"
// 保存先
private var savePath: URL {
return URL(fileURLWithPath: "/Users/hogehoge/Desktop/" + fileName)
}
override init() {
super.init()
// 録画の画質を指定
session.sessionPreset = AVCaptureSessionPresetHigh
// メインディスプレイのIDを取得
let displayID = CGDirectDisplayID(CGMainDisplayID())
// 入力ソースをメインディスプレイに設定
let input: AVCaptureScreenInput = AVCaptureScreenInput(displayID: displayID)
// カーソルは録画対象から外す
input.capturesCursor = false
// クリックは録画する
input.capturesMouseClicks = true
// セッションにInputソースを渡す
if session.canAddInput(input) {
session.addInput(input)
}
// 出力ソースを設定
if session.canAddOutput(output) {
session.addOutput(output)
}
}
// 録画開始
func start() {
session.startRunning()
output.startRecording(toOutputFileURL: savePath, recordingDelegate: self)
}
// 録画停止
func stop() {
output.stopRecording()
}
func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
session.stopRunning()
}
}
またAVCaptureSession
を利用してカメラの動画や画面から取得されるサンプルバッファを取得したい場合は次のように書くことできます。
/*** 入力ソースからの画素情報にアクセスしたい場合 ***/
import Foundation
import AVFoundation
// AVCaptureFileOutputRecordingDelegateに準拠
final class ScreenRecorder: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
private let session = AVCaptureSession()
// ビデオアウトプットを指定
private let output = AVCaptureVideoDataOutput()
private var callbackQueue: DispatchQueue!
override init() {
super.init()
session.sessionPreset = AVCaptureSessionPresetHigh
let displayID = CGDirectDisplayID(CGMainDisplayID())
let input: AVCaptureScreenInput = AVCaptureScreenInput(displayID: displayID)
// 60FPSに設定
input.minFrameDuration = CMTime(seconds: 1, preferredTimescale: 60)
if session.canAddInput(input) {
session.addInput(input)
}
// サンプルバッファが蓄積されるQueue. 必ずSerialなQueueを指定してください
callbackQueue = DispatchQueue(label: "jp.rinov.screenRecord_example")
// 処理の遅れたフレームは無視する
output.alwaysDiscardsLateVideoFrames = true
// DelegateとcallbackQueueを指定
output.setSampleBufferDelegate(self, queue: callbackQueue)
if session.canAddOutput(output) {
session.addOutput(output)
}
}
func start() {
session.startRunning()
}
func stop() {
session.stopRunning()
}
// MARK: - AVCaptureVideoDataOutputSampleBufferDelegate
public func captureOutput(_ captureOutput: AVCaptureOutput!, didDrop sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
// 処理落ちによりバッファが取得できない場合
}
public func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
// SampleBufferが更新された場合
}
}
上記サンプルではバッファが更新されるごとにデリゲートメソッドに通知が来ますが、minFrameDuration
を60FPS相当にしていも実際にデリゲートメソッドが呼ばれる感覚は5FPS相当です。つまりリアルタイムに画素に対する処理を行いたい場合にはほとんど使用できません。動画編集などを行いたい場合などは一度AVCaptureMovieFileOutput
でファイル出力後に当該ファイルを読み込んで処理を行うほうが良さそうです。
参考:
https://developer.apple.com/documentation/avfoundation/avcapturemoviefileoutput
https://developer.apple.com/documentation/avfoundation/avcapturescreeninput