iOS-音视屏采集
2018-07-27 本文已影响0人
三月望龙
概述
- 音视屏采集分音频采集和视频采集
- 在iOS中,可以同步采集音频和视频
- 采集的API在AVFoundation框架中
采集流程
1、初始化输入输出源
- 设置采集视屏的输入和输出源
private func setupVideo() {
// 获取视屏input所需的设备,本例取的是前置摄像头
let devices = AVCaptureDevice.devices(for: AVMediaType.video)
guard let device = devices.filter({ $0.position == .front}).first else {return}
// input
guard let input = try? AVCaptureDeviceInput(device: device) else { return }
self.videoInput = input
// output
let output = AVCaptureVideoDataOutput()
output.setSampleBufferDelegate(self, queue: DispatchQueue.global())
self.videoOutput = output
// add session
session.beginConfiguration()
if session.canAddInput(input) {
session.addInput(input)
}
if session.canAddOutput(output) {
session.addOutput(output)
}
session.commitConfiguration()
}
- 设置采集的音频的输入和输出源
private func setupAudio() {
// input
guard let device = AVCaptureDevice.default(for: AVMediaType.audio) else { return }
guard let input = try? AVCaptureDeviceInput(device: device) else { return }
// output
let output = AVCaptureAudioDataOutput()
output.setSampleBufferDelegate(self, queue: DispatchQueue.global())
// add session
session.beginConfiguration()
if session.canAddInput(input) {
session.addInput(input)
}
if session.canAddOutput(output) {
session.addOutput(output)
}
session.commitConfiguration()
}
- 设置预览图层
实时显示采集的画面,可以添加此预览图层
不添加该图层也可以正常采集
private func setupPreviewLayer() {
let preview = AVCaptureVideoPreviewLayer(session: session)
preview.frame = view.bounds
view.layer.insertSublayer(preview, at: 0)
self.previewLayer = preview
}
- 采集音视屏回调
音频回调遵循
AVCaptureAudioDataOutputSampleBufferDelegate
视屏回调遵循AVCaptureVideoDataOutputSampleBufferDelegate
注意: 音频回调方法和视屏回调方法是同一个方法
extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate {
func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// 丢弃采集
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// sampleBuffer 采集到的每一帧结果
// 通过connection判断采集到的数据是视屏或音频信息
if self.videoOutput?.connection(with: AVMediaType.video) == connection {
print("采集到视屏")
}else {
print("采集到音频")
}
}
}
2、设置采集结果的保存
如果需要保存采集到的音视屏,可以添加此实例
- 初始化
AVCaptureMovieFileOutput
private func setupOutputFile () {
if movieOutputFile != nil {
session.removeOutput(movieOutputFile!)
}
let fileOutput = AVCaptureMovieFileOutput()
let connection = fileOutput.connection(with: AVMediaType.video)
connection?.automaticallyAdjustsVideoMirroring = true
if session.canAddOutput(fileOutput) {
session.addOutput(fileOutput)
}
self.movieOutputFile = fileOutput
}
- 设置保存路径
使用计算属性
private var urlPath: URL? {
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first! + "/video.mp4"
return URL(fileURLWithPath: path)
}
- 遵守文件写入代理
AVCaptureFileOutputRecordingDelegate
extension ViewController: AVCaptureFileOutputRecordingDelegate {
func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {
print("开始写入")
}
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
print("结束写入")
}
}
3、开始采集以及结束采集
- 开始采集
@IBAction func startCapture(_ sender: Any) {
// 每次开始采集前, 设置预览图层
setupPreviewLayer()
// 开始录制,保存采集结果
if let url = urlPath {
self.movieOutputFile?.startRecording(to: url, recordingDelegate: self)
}
// 开始采集
session.startRunning()
}
- 结束采集
@IBAction func stopCapture(_ sender: Any) {
// 结束录制
self.movieOutputFile?.stopRecording()
// 结束采集
session.stopRunning()
// 移出预览图层
previewLayer?.removeFromSuperlayer()
}
4、旋转采集摄像头
@IBAction func rotateCamera(_ sender: Any) {
guard let videoInput = videoInput else { return }
// 前置 -> 后置, 后置 -> 前置
let position: AVCaptureDevice.Position = videoInput.device.position == .front ? .back : .front
// 输入源重新添加设备
let devices = AVCaptureDevice.devices(for: AVMediaType.video)
guard let device = devices.filter({$0.position == position }).first else { return }
guard let input = try? AVCaptureDeviceInput(device: device) else { return }
// session重新添加输入源
session.beginConfiguration()
session.removeInput(videoInput)
if session.canAddInput(input) {
session.addInput(input)
}
session.commitConfiguration()
// 保存最新输入源
self.videoInput = input
}