Swift IOS 使用 AVFoundation 录制视频和音频

Posted

技术标签:

【中文标题】Swift IOS 使用 AVFoundation 录制视频和音频【英文标题】:Swift IOS Record Video and Audio with AVFoundation 【发布时间】:2016-03-05 04:38:05 【问题描述】:

按照这个问题,我能够成功抓取录制的视频 here

基本上

    AVCaptureFileOutputRecordingDelegate原型继承 循环访问可用设备 使用相机创建会话 开始录制 停止录制 通过实现上述原型的方法获取录制视频

但该文件不附带音频。

根据this的问题,我必须单独录制音频并使用提到的类合并视频和音频

但我不知道如何同时实现视频和音频录制。

for device in devices 
    // Make sure this particular device supports video
    if (device.hasMediaType(AVMediaTypeVideo)) 
        // Finally check the position and confirm we've got the back camera
        if(device.position == AVCaptureDevicePosition.Back) 
            captureDevice = device as? AVCaptureDevice
            if captureDevice != nil 
                print("Capture device found")

                beginSession()
            
        
    

在这个循环中,只有可用的设备类型是 .Front 和 .Back

【问题讨论】:

【参考方案1】:

以下是使用 AVFoundation 框架录制带音频的视频的方法。步骤是:

1.准备会议:

self.captureSession = AVCaptureSession()

2。准备可用的视频和音频设备:

let session = AVCaptureDevice.DiscoverySession.init(deviceTypes:[.builtInWideAngleCamera, .builtInMicrophone], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified)
        
let cameras = (session.devices.compactMap$0)
        
for camera in cameras 
    if camera.position == .front 
        self.frontCamera = camera
    
    if camera.position == .back 
        self.rearCamera = camera

        try camera.lockForConfiguration()
        camera.focusMode = .continuousAutoFocus
        camera.unlockForConfiguration()
    

3.准备会话输入:

guard let captureSession = self.captureSession else 
    throw CameraControllerError.captureSessionIsMissing


if let rearCamera = self.rearCamera 
    self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
    if captureSession.canAddInput(self.rearCameraInput!) 
        captureSession.addInput(self.rearCameraInput!)
        self.currentCameraPosition = .rear
     else 
        throw CameraControllerError.inputsAreInvalid
    
 else if let frontCamera = self.frontCamera 
    self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
    if captureSession.canAddInput(self.frontCameraInput!) 
        captureSession.addInput(self.frontCameraInput!)
        self.currentCameraPosition = .front
     else 
        throw CameraControllerError.inputsAreInvalid
    
 else 
    throw CameraControllerError.noCamerasAvailable


// Add audio input
if let audioDevice = self.audioDevice 
    self.audioInput = try AVCaptureDeviceInput(device: audioDevice)
    if captureSession.canAddInput(self.audioInput!) 
        captureSession.addInput(self.audioInput!)
     else 
        throw CameraControllerError.inputsAreInvalid
    

4.准备输出:

self.videoOutput = AVCaptureMovieFileOutput()
if captureSession.canAddOutput(self.videoOutput!) 
    captureSession.addOutput(self.videoOutput!)

captureSession.startRunning()

5.开始录制:

func recordVideo(completion: @escaping (URL?, Error?) -> Void) 
    guard let captureSession = self.captureSession, captureSession.isRunning else 
        completion(nil, CameraControllerError.captureSessionIsMissing)
        return
    
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let fileUrl = paths[0].appendingPathComponent("output.mp4")
    try? FileManager.default.removeItem(at: fileUrl)
    videoOutput!.startRecording(to: fileUrl, recordingDelegate: self)
    self.videoRecordCompletionBlock = completion

6.停止录制:

func stopRecording(completion: @escaping (Error?) -> Void) 
    guard let captureSession = self.captureSession, captureSession.isRunning else 
        completion(CameraControllerError.captureSessionIsMissing)
        return
    
    self.videoOutput?.stopRecording()

7.实现委托:

func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) 
    if error == nil 
        //do something
     else 
        //do something
    

我从这里得到想法:https://www.appcoda.com/avfoundation-swift-guide/

这是完整的项目https://github.com/rubaiyat6370/ios-Tutorial/

【讨论】:

【参考方案2】:

找到答案了,这个答案是this代码

这可以简单地完成

    声明另一个捕获设备变量 遍历设备并初始化摄像头和音频捕获设备变量 向会话添加音频输入

代码

var captureDevice : AVCaptureDevice?
var captureAudio :AVCaptureDevice?

循环设备并初始化捕获设备

var captureDeviceVideoFound: Bool = false
var captureDeviceAudioFound:Bool = false

// Loop through all the capture devices on this phone
for device in devices 
// Make sure this particular device supports video
    if (device.hasMediaType(AVMediaTypeVideo)) 
// Finally check the position and confirm we've got the front camera
        if(device.position == AVCaptureDevicePosition.Front) 

            captureDevice = device as? AVCaptureDevice //initialize video
            if captureDevice != nil 
                print("Capture device found")
                captureDeviceVideoFound = true; 
            
        
    
    if(device.hasMediaType(AVMediaTypeAudio))
        print("Capture device audio init")
        captureAudio = device as? AVCaptureDevice //initialize audio
        captureDeviceAudioFound = true
    

if(captureDeviceAudioFound && captureDeviceVideoFound)
    beginSession() 

内部会议

try captureSession.addInput(AVCaptureDeviceInput(device: captureDevice))
try captureSession.addInput(AVCaptureDeviceInput(device: captureAudio))

这将输出带有音频的视频文件。无需合并音频或做任何事情。

这个苹果文档helps

【讨论】:

【参考方案3】:

遵循@Mumu 的回答,但它对我不起作用,因为对 AVCaptureDevice.DiscoverySession.init 的调用仅返回视频设备。

这是我在 iOS 14 Swift 5 上运行的版本:

var captureSession: AVCaptureSession? = nil
var camera: AVCaptureDevice? = nil
var microphone: AVCaptureDevice? = nil
var videoOutput: AVCaptureFileOutput? = nil
var previewLayer: AVCaptureVideoPreviewLayer? = nil

func findDevices() 
    camera = nil
    microphone = nil

    //Search for video media type and we need back camera only
    let session = AVCaptureDevice.DiscoverySession.init(deviceTypes:[.builtInWideAngleCamera],
            mediaType: AVMediaType.video, position: AVCaptureDevice.Position.back)
    var devices = (session.devices.compactMap$0)
    //Search for microphone
    let asession = AVCaptureDevice.DiscoverySession.init(deviceTypes:[.builtInMicrophone],
            mediaType: AVMediaType.audio, position: AVCaptureDevice.Position.unspecified)
    //Combine all devices into one list
    devices.append(contentsOf: asession.devices.compactMap$0)
    for device in devices 
        if device.position == .back 
            do 
                try device.lockForConfiguration()
                device.focusMode = .continuousAutoFocus
                device.flashMode = .off
                device.whiteBalanceMode = .continuousAutoWhiteBalance
                device.unlockForConfiguration()
                camera = device
             catch 
            
        
        if device.hasMediaType(.audio) 
            microphone = device
        
    


func initVideoRecorder()->Bool 
    captureSession = AVCaptureSession()
    guard let captureSession = captureSession else return false

    captureSession.sessionPreset = .hd4K3840x2160
    findDevices()

    guard let camera = camera else  return false
    do 
        let cameraInput = try AVCaptureDeviceInput(device: camera)
        captureSession.addInput(cameraInput)
     catch 
        self.camera = nil
        return false
    

    if let audio = microphone 
        do 
            let audioInput = try AVCaptureDeviceInput(device: audio)
            captureSession.addInput(audioInput)
         catch 
        
    

    videoOutput = AVCaptureMovieFileOutput()
    if captureSession.canAddOutput(videoOutput!) 
        captureSession.addOutput(videoOutput!)
        captureSession.startRunning()
        videoOutput?.connection(with: .video)?.videoOrientation = .landscapeRight
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer?.videoGravity = .resizeAspect
        previewLayer?.connection?.videoOrientation = .landscapeRight
        return true
    

    return false


func startRecording()->Bool 
    guard let captureSession = captureSession, captureSession.isRunning else return false
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let fileUrl = paths[0].appendingPathComponent(getVideoName())
    try? FileManager.default.removeItem(at: fileUrl)
    videoOutput?.startRecording(to: fileUrl, recordingDelegate: self)
    return true

【讨论】:

【参考方案4】:

我也遇到了这个问题,但是当我分组添加视频输入和声音输入之后,音频工作了。这是我添加输入的代码。

if (cameraSession.canAddInput(deviceInput) == true && cameraSession.canAddInput(audioDeviceInput) == true) //detects if devices can be added
    cameraSession.addInput(deviceInput)//adds video
    cameraSession.addInput(audioDeviceInput)//adds audio

另外我发现你必须先有视频输入,否则不会有音频。我最初将它们放在两个 if 语句中,但我发现将它们放在一个中可以将视频和音频记录在一起。希望这会有所帮助。

【讨论】:

【参考方案5】:

用音频录制视频

//获取视频设备

if let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as? [AVCaptureDevice] 
    for device in devices 
        if device.hasMediaType(AVMediaTypeVideo) 
            if device.position == AVCaptureDevicePosition.back 
                videoCaptureDevice = device
            
        
    
    if videoCaptureDevice != nil 
        do 
            // Add Video Input
            try self.captureSession.addInput(AVCaptureDeviceInput(device: videoCaptureDevice))
            // Get Audio Device
            let audioInput = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio)
            //Add Audio Input
            try self.captureSession.addInput(AVCaptureDeviceInput(device: audioInput))
            self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
            previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
            previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.portrait
            self.videoView.layer.addSublayer(self.previewLayer)
            //Add File Output
            self.captureSession.addOutput(self.movieOutput)
            captureSession.startRunning()
         catch 
            print(error)
        
    

更多详情请参考此链接:

https://medium.com/@santhosh3386/ios-avcapturesession-record-video-with-audio-23c8f8c9a8f8

【讨论】:

以上是关于Swift IOS 使用 AVFoundation 录制视频和音频的主要内容,如果未能解决你的问题,请参考以下文章

iOS Swift 3:导入 AVFoundation 产生了不需要的下划线按钮文本

Swift:使用系统AVFoundation实现二维码扫描和生成

Swift AVFoundation 二维码扫描和生成

使用 AVFoundation 裁剪 AVAsset 视频不工作 iOS 8

将音频文件保存到用户友好的位置 swift 3 avfoundation

Swift实现iOS录音与播放音频功能