iOS利用AVFoundation实现视频音频的输入&输出,切换摄像头并保存
程序员文章站
2022-03-04 23:27:34
...
iOS中AVFoundation是一个专门的音频,视频处理库,很强大的一个库,配合GPUImage能够实现很多音视频和图片的处理,如:美颜效果等等
采集步骤
采集步骤文字描述
- 导入框架
- 相关API主要在AVFoundation框架中,因此需要先导入框架
- 创建捕捉会话(AVCaptureSession)
- 该会话用于连接之后的输入源&输出源
- 输入源:摄像头&话筒
- 输出源:拿到对应的音频&视频数据的出口
- 会话:用于将输入源&输出源连接起来
- 设置视频输入源&输出源
- 输入源(AVCaptureDeviceInput):从摄像头输入
- 输出源(AVCaptureVideoDataOutput):可以设置代理,在代理方法中拿到数据
- 将输入&输出添加到会话中
- 设置音频输入源&输出源
- 输入源(AVCaptureDeviceInput):从话筒输入
- 输出源(AVCaptureAudioDataOutput):可以设置代理,在代理方法中拿到数据
- 将输入&输出添加到会话中
- 添加预览图层(可选)
- 如果希望用户看到采集的画面,可以添加预览图层
- 该预览图层不是必须的,及时没有添加也可以正常采集数据
- 开始采集即可
- 调用会话(AVCaptureSession)的startRunning方法即可开始采集
import UIKit
import AVFoundation
class ViewController: UIViewController {
fileprivate lazy var session: AVCaptureSession = AVCaptureSession()
fileprivate var videoOutput: AVCaptureVideoDataOutput?
fileprivate var videoInput: AVCaptureDeviceInput?
fileprivate var previewLayer: AVCaptureVideoPreviewLayer?
fileprivate var movieOutput: AVCaptureMovieFileOutput?
override func viewDidLoad() {
super.viewDidLoad()
// 初始化视频的输入&输出
setupVideoInputOutput()
// 初始化音频的输入&输出
setupAudioInputOutput()
}
// 开始采集音视频
@IBAction func startCapturing(_ sender: UIButton) {
// 初始化一个预览图层
setupPreviewLayer()
session.startRunning()
// 录制视频, 并且写入文件
setupMovieFileOutput()
}
// 停止录制
@IBAction func stopCapturing(_ sender: UIButton) {
session.stopRunning()
movieOutput?.stopRecording()
previewLayer?.removeFromSuperlayer()
}
// 切换摄像头
@IBAction func switchCamera(_ sender: UIButton) {
// 取出之前镜头的方向
guard let videoInput = self.videoInput else { return }
let position: AVCaptureDevice.Position = videoInput.device.position == AVCaptureDevice.Position.front ? .back : .front
let devices = AVCaptureDevice.devices()
guard let device = devices.filter({ $0.position == position}).first else { return
}
guard let newInput = try? AVCaptureDeviceInput(device: device) else { return }
// 移除之前的input, 添加新的input
session.beginConfiguration()
session.removeInput(videoInput)
if session.canAddInput(newInput) {
session.addInput(newInput)
}
session.commitConfiguration()
// 保存最新的input
self.videoInput = newInput
}
}
// MARK:- 初始化方法
extension ViewController {
// 初始化视频的输入输出
fileprivate func setupVideoInputOutput() {
// 添加视频的输入
let devices = AVCaptureDevice.devices()
guard let device = devices.filter({ $0.position == .front }).first else { return }
guard let input = try? AVCaptureDeviceInput(device: device) else { return }
videoInput = input
// 添加视频的输出
let output = AVCaptureVideoDataOutput()
let queue = DispatchQueue.global()
output.setSampleBufferDelegate(self, queue: queue)
videoOutput = output
// 添加输入输出
session.beginConfiguration()
if session.canAddInput(input) {
session.addInput(input)
}
if session.canAddOutput(output) {
session.addOutput(output)
}
session.commitConfiguration()
}
// 初始化音频的输入输出
fileprivate func setupAudioInputOutput() {
// 添加音频的输入
guard let device = AVCaptureDevice.default(for: AVMediaType.audio) else { return }
guard let input = try? AVCaptureDeviceInput(device: device) else { return }
// 创建输出
let output = AVCaptureAudioDataOutput()
let queue = DispatchQueue.global()
output.setSampleBufferDelegate(self, queue: queue)
//添加输入&输出
session.beginConfiguration()
if session.canAddOutput(output) {
session.addOutput(output)
}
if session.canAddInput(input) {
session.addInput(input)
}
session.commitConfiguration()
}
// 初始化预览层
fileprivate func setupPreviewLayer() {
// 创建预览图层
let previewLayer = AVCaptureVideoPreviewLayer(session: session)
// 设置previewLayer属性
previewLayer.frame = view.bounds
// 图层添加到控制器的View的layer中
view.layer.insertSublayer(previewLayer, at: 0)
self.previewLayer = previewLayer
}
// 录制视频, 并且写入文件
fileprivate func setupMovieFileOutput() {
if let output = movieOutput {
session.removeOutput(output)
}
// 创建写入文件的输出
let fileOutput = AVCaptureMovieFileOutput()
movieOutput = fileOutput
let connection = fileOutput.connection(with: .video)
connection?.automaticallyAdjustsVideoMirroring = true
session.beginConfiguration()
if session.canAddOutput(fileOutput) {
session.addOutput(fileOutput)
}
session.commitConfiguration()
// 直接开始写入文件
let filePath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/video.mp4"
let fileUrl = URL(fileURLWithPath: filePath)
fileOutput.startRecording(to: fileUrl, recordingDelegate: self)
}
}
// MARK:- <AVCaptureVideoDataOutputSampleBufferDelegate>
extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate {
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
if videoOutput?.connection(with: AVMediaType.video) == connection {
print("采集视频数据")
} else {
print("采集音频数据")
}
}
}
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("完成写入文件")
}
}
上一篇: 使用Oracle Text进行全文检索
下一篇: windows如何删除php