当前位置: 首页 > news >正文

深入解析 iOS 视频录制(一):录制管理核心MWRecordingController 类的设计与实现

深入解析 iOS 视频录制(一):录制管理核心MWRecordingController 类的设计与实现

深入解析iOS视频录制(二):自定义UI的实现

引言

在 iOS 应用开发中,视频录制功能越来越常见,尤其是在直播、短视频和社交应用中,用户经常需要录制高质量的视频内容。为了实现这个功能,我们不仅需要处理视频的输入和输出,还要控制摄像头的切换、录制的开始与结束,以及会话的管理等。而这些都需要通过一套清晰且高效的代码架构来实现。

在本系列博客中,我们将深入探讨如何实现 iOS 中的自定义视频录制功能。第一篇将重点介绍 MWRecordingController 类,它是整个视频录制管理的核心组件。我们将从它的设计原理、功能实现以及如何高效地管理录制会话入手,逐步揭开它在视频录制过程中的关键作用。

通过本篇博客的学习,您将能够更好地理解 iOS 视频录制的底层实现,并能在自己的项目中灵活地应用这些技术。

MWRecordingController 类的设计与实现

在自定义的视频录制功能中,MWRecordingController 类扮演着至关重要的角色。它是视频录制管理的核心,负责协调视频会话的启动与控制,确保录制过程的顺畅与高效。

MWRecordingController 类的作用与职责

具体来说,MWRecordingController 的主要职责包括:

  1. 管理录制会话的生命周期:MWRecordingController 类负责创建、启动和停止会话,确保视频录制从初始化到结束的整个过程可控。它为会话提供配置,控制录制的开始与停止,确保每一次录制都符合预期。
  2. 设置视频输入与输出:MWRecordingController还负责配置视频的输入源和输出目标。
  3. 切换摄像头:MWRecordingController 提供摄像头切换的功能,使用户能够在录制过程中自由选择前置或后置摄像头,增强录制体验。它确保每次切换都能快速且无缝地完成,以满足实时录制的需求。
  4. 录制控制:作为核心组件,MWRecordingController 控制着录制的开始与停止,确保视频文件的正确保存与后续处理。它负责处理录制过程中的状态变化,并通过回调通知外部应用,确保录制功能的顺利执行。
  5. 错误接结果的回调:MWRecordingController还负责将录制时产生的错误,及录制结果传递出去。

总结来说,MWRecordingController 类通过高效的会话管理、视频输入输出设置、摄像头控制及录制控制,承担了视频录制过程中的核心任务,是实现自定义视频录制功能的关键所在。

关键功能解析

接下来,我们将深入分析 MWRecordingController 类中的关键功能,从启动会话到停止录制的整个过程。通过这些功能的代码实现,我们能够更清楚地理解每个环节是如何协同工作,以确保视频录制的顺利进行。

1. 配置会话

在视频录制过程中,所有操作都依赖于 AVCaptureSession 实例。AVCaptureSession是 iOS 中用来协调视频和音频数据流的核心对象,它负责将输入设备(如摄像头、麦克风等)的数据与输出(如文件、屏幕显示等)进行连接。因此,第一步就是创建一个 AVCaptureSession 实例,并对其进行必要的配置。

    /// 视频录制会话
    private(set) var session: AVCaptureSession?
    /// 开始设置会话
    func setupSession() {
        session = AVCaptureSession()
        guard let session = session else {
            let error = NSError(domain: "com.mw.video", code: 0, userInfo: [NSLocalizedDescriptionKey: "创建会话失败"])
            delegate?.recordingController(self, didFailWithError: error)
            return
        }
        session.beginConfiguration()
        session.sessionPreset = .hd1280x720
        // 设置视频输入
        setupVideoInput()
        // 设置音频输入
        setupAudioInput()
        // 设置视频输出
        setupVideoOutput()
        session.commitConfiguration()
    }
  1. 创建一个AVCaptureSession的实例。
  2. 如果创建失败则直接回调出错误信息。
  3. 开启录制会话的配置。
  4. 设置录制的视频分辨率大小。
  5. 配置录制会话的视频输入。
  6. 配置录制会话的音频输入。
  7. 配置录制会话的视频输出。
  8. 提交录制会话配置。

2. 配置视频输入

配置视频输入是录制过程中至关重要的一步。视频输入源通常是设备的摄像头,iOS 提供了前置和后置摄像头可供选择。在这一过程中,我们需要确保选择正确的摄像头,并将其转换为 AVCaptureDeviceInput 以便能够将其添加到 AVCaptureSession 中。

    /// 摄像头
    private var camera: AVCaptureDevice?
    /// 视频输入
    private var videoInput: AVCaptureDeviceInput?
    /// 添加视频输入
    private func setupVideoInput(position:AVCaptureDevice.Position = .back) {
        // 默认获取后置
        guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: position) else {
            let error = NSError(domain: "com.mw.video", code: 0, userInfo: [NSLocalizedDescriptionKey: "获取摄像头失败"])
            delegate?.recordingController(self, didFailWithError: error)
            return
        }
        camera = device
        do {
            let input = try AVCaptureDeviceInput(device: device)
            videoInput = input
            if session?.canAddInput(input) == true {
                session?.addInput(input)
            }
        } catch {
            delegate?.recordingController(self, didFailWithError: error)
        }
    }
  1. 获取设备的视频输入源摄像头,默认为后置摄像头。
  2. 将AVCaptureDevice包装一层AVCaptureDeviceInput,并添加到AVCaptureSession中。
  3. 如果发生错误直接回调回对应的NSError。

3. 配置音频输入

视频录制时如果需要音频,那么我们就需要将音频输入也像视频一样配置上,步骤同上。

    /// 添加音频输入
    private func setupAudioInput() {
        guard let device = AVCaptureDevice.default(for: .audio) else {
            let error = NSError(domain: "com.mw.video", code: 0, userInfo: [NSLocalizedDescriptionKey: "获取麦克风失败"])
            delegate?.recordingController(self, didFailWithError: error)
            return
        }
        do {
            let input = try AVCaptureDeviceInput(device: device)
            if session?.canAddInput(input) == true {
                session?.addInput(input)
            }
        } catch {
            delegate?.recordingController(self, didFailWithError: error)
        }
    }
  1. 获取麦克风设备。
  2. 将AVCaptureDevice包装一层AVCaptureDeviceInput,并添加到AVCaptureSession中。
  3. 如果发生错误直接回调回对应的NSError。

4. 配置音视频输出

视频输出是录制过程中不可或缺的一部分,它决定了如何保存或处理录制的视频数据。在 iOS 中,我们通常使用AVCaptureMovieFileOutput 来输出录制的视频文件。它能够将视频流保存到指定的位置,通常是一个文件路径,之后可以进行播放或其他处理。

    /// 视频录制输出
    private var videoOutput: AVCaptureMovieFileOutput?
    /// 添加视频输出
    private func setupVideoOutput() {
        let output = AVCaptureMovieFileOutput()
        if session?.canAddOutput(output) == true {
            session?.addOutput(output)
            videoOutput = output
        } else {
            let error = NSError(domain: "com.mw.video", code: 0, userInfo: [NSLocalizedDescriptionKey: "添加视频输出失败"])
            delegate?.recordingController(self, didFailWithError: error)
        }
    }
  1. 使用AVCaptureMovieFileOutput会为我们直接生成录制好的音频数据,我们只需要设置数据存储路径。
  2. 创建音视频输出。
  3. 添加音视频输出。
  4. 如果发生错误直接回调回NSError。

5. 开启和停止会话

在进行视频和音频的采集之前,首先要做的就是启动会话,开启采集。

    /// 开始会话
    func startSession() {
        videoQueue.async {[weak self] in
            guard let self = self else { return }
            if self.session?.isRunning == false {
                self.session?.startRunning()
            }
        }
    }

当不需要使用时,手动调用结束会话。

    /// 停止会话
    func stopSession() {
        videoQueue.async {[weak self] in
            guard let self = self else { return }
            if self.session?.isRunning == true {
                self.session?.stopRunning()
            }
        }
    }

6. 开启录制结束录制

一切准备就绪之后,我们就可以使用视频的输出调用开始录制方法,同时设置视频的输出地址,以及代理。

    /// 开始录制
    func startRecording() {
        if isRecording {
            return
        }
        videoQueue.async {[weak self] in
            guard let self = self else { return }
            let videoConnection = self.videoOutput?.connection(with: .video)
            self.videoOutput?.startRecording(to: self.outputFileURL(), recordingDelegate: self)
        }
    }
    /// 结束录制
    func stopRecording() {
        if isRecording == false {
            return
        }
        videoQueue.async {[weak self] in
            guard let self = self else { return }
            self.videoOutput?.stopRecording()
        }
    }

7. 完成回调

录制完成之后,我们在AVCaptureFileOutputRecordingDelegate的协议方法中,可以直接获取到录制的视频地址URL,或者录制报错的错误信息。

    //MARK: - 结束录制
    func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: (any Error)?) {
        
        if let error = error {
            delegate?.recordingController(self, didFailWithError: error)
        } else {
            delegate?.recordingController(self, didFinishRecordingTo: outputFileURL)
        }
    }

8. 切换摄像头

在进行切换前,首先设置会话状态为开始配置,然后移除当前的输入设备,获取新的输入设备并添加,提交新的配置。

    /// 切换摄像头
    func switchCamera() {
        guard let session = session else {
            return
        }
        session.beginConfiguration()
        // 移除输入
        if let videoInput = videoInput {
            session.removeInput(videoInput)
        }
        // 切换摄像头
        if camera?.position == .back {
            setupVideoInput(position: .front)
        } else {
            setupVideoInput(position: .back)
        }
        session.commitConfiguration()
    }
  1. 开启会话配置。
  2. 移除原来的视频输出。
  3. 设置新的视频输入。
  4. 移交配置。

9. 定义代理

为了将结果和错误信息回调到调用的对象中,我们定义了一个 MWRecordingControllerDelegate 的代理,主要包含了录制发生错误的回调,以及录制完成的回调。

protocol MWRecordingControllerDelegate: AnyObject {
    /// 录制发生错误
    func recordingController(_ controller: MWRecordingController, didFailWithError error: Error)
    /// 录制完成
    func recordingController(_ controller: MWRecordingController, didFinishRecordingTo outputFileURL: URL)
   
}

结语

通过本篇博客,我们深入解析了 MWRecordingController 类在 iOS 视频录制中的核心作用,详细介绍了如何配置会话、设置视频输入输出,以及录制控制的实现。这些技术为实现高效的视频录制功能奠定了基础。希望通过本篇的分享,能够帮助大家更好地理解视频录制的底层实现,为自己项目中的视频录制功能提供参考与帮助。在接下来的博客中,我们将继续探索其他优化与扩展的技术,敬请期待!

相关文章:

  • 网络安全不分家 网络安全不涉及什么
  • el-input无法输入0.0001的小数,自动转换为0在vue3中的bug
  • 【SpringBoot注解失效】@注解为什么不生效?
  • home assistant ddns动态域名解析插件
  • AWS上Amazon Redshift用Zoominfo API验证公司基本信息数据正确性检查设计方案
  • Vue 前端开发中的路由知识:从入门到精通
  • STM32外设SPI FLASH应用实例
  • 2.16日学习总结
  • Flutter 记一次疑难杂症
  • 【HUSTOJ 判题机源码解读系列04】判题机常见技术选择方案
  • 响应式布局学习笔记
  • BT401双模音频蓝牙模块如何开启ble的透传,有什么注意事项
  • 从零到一:Spring Boot 与 RocketMQ 的完美集成指南
  • GPT-4o悄然升级:能力与个性双突破,AI竞技场再掀波澜
  • Go 模块管理工具 `go mod tidy` 和 `go.sum` 文件详解
  • 在 Android 上自定义编译 FFmpeg
  • 嵌入式Linux系统SPI驱动移植专题详解(3000+字图文实战指南)
  • 康耐视CAM-CIC-10MR-10-GC工业相机
  • 《TSP6K数据集进行交通场景解析》学习笔记
  • 计算机网络(4)TCP断开
  • 中国戏剧梅花奖终评结果公示,蓝天和朱洁静等15名演员入选
  • 半年不到再换岗:伊春市委常委、政法委书记方春彪任伊春森工集团党委书记
  • 巴基斯坦副总理兼外长达尔将访华
  • 无人机企业从科技园区搬到乡村后,村子里变得不一样了
  • 美国恶劣天气已造成至少28人死亡
  • 水果预包装带来的环境成本谁来分担?