深入解析 iOS 视频录制(三):完整录制流程的实现与整合

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

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

深入解析 iOS 视频录制(三):完整录制流程的实现与整合

引言

在之前的两篇博客中,我们详细探讨了如何实现 iOS 视频录制功能的核心部分。第一篇博客《深入解析 iOS 视频录制(一):录制管理核心MWRecordingController 类的设计与实现》介绍了如何设计并实现一个录制管理类MWRecordingController,该类负责管理视频录制的核心功能,包括启动会话、设置输入输出、切换摄像头以及控制录制的开始与停止。第二篇《深入解析 iOS 视频录制(二):自定义 UI 的实现》中,我们详细探讨了如何通过自定义 UI 提供更流畅的用户体验,包括录制按钮、预览视图、控制视图等组件的实现与布局。

本篇博客将以这些基础为依托,重点讲解如何在一个 ViewController 中实现完整的视频录制流程。我们将把前两篇博客中的内容整合,展示如何在单一视图控制器中实现从视频预览到录制控制、从开始录制到保存视频文件的全过程。通过这篇博客,你将了解如何将这些不同模块融合成一个完整、可交互的视频录制应用。

希望通过这篇博客,你能深入理解如何实现一个简单而强大的视频录制功能,并为未来的应用开发打下坚实的基础。

准备工作

在开始实现视频录制功能之前,我们需要做好一些必要的准备工作。确保项目配置正确,依赖库安装完毕,以及设备权限已正确设置,才能顺利进行开发和测试。

项目设置与依赖

首先,确保你的 Xcode 项目已经设置好并配置了正确的依赖项。在本项目中,我们主要依赖 AVFoundation 框架来实现视频录制功能。

import AVFoundation

配置 Info.plist 权限请求

iOS 中,应用访问设备的硬件(如摄像头和麦克风)时需要请求响应的权限。为了确保应用能顺利进行示例录制,我们需要在 Info.plist 文件中添加以下权限描述:

<key>NSCameraUsageDescription</key>
<string>需要访问摄像头来进行视频录制</string>
<key>NSMicrophoneUsageDescription</key>
<string>需要访问麦克风来录制音频</string>

这些描述会在应用首次请求权限时,向用户展示权限请求弹窗。确保根据你应用的功能,调整描述内容,提供清晰且易懂的理由,以提升用户体验。

设备要求与测试

为了保证视频录制功能能够顺利运行,请确保测试设备支持摄像头和麦克风,在模拟器中一般无法使用真实摄像头进行录制,因此必须使用真实设备进行开发和调试。

录制完整流程代码实现

在这一部分,我们将会结合代码一步一步实现录制视频的完整流程。

录制管理与UI组件集成

MWRecordingViewController是本次实现的核心视图控制,它将 MWRecordingController 作为录制的核心控制器,并通过自定义的视图组件(如MWRecordingPreview、MWRecordingControlView、MWRecordingNavgationView)构建了一个完成的视频录制界面。

MWRecordingController

录制控制器(MWRecordingController):负责启动会话、开始和停止录制、切换摄像头。

    /// 录制控制器private let recordingController = MWRecordingController()
  public override func viewDidLoad() {super.viewDidLoad()setupRecording()addPreviewView()addNavgationView()addControlView()}private func setupRecording() {recordingController.recordingSource = self.recordingSource// 设置recordingController.setupSession()// 开始会话recordingController.startSession()recordingController.delegate = self}

MWRecordingPreview

预览视图(MWRecordingPreview):负责展示视频录制过程中的实时画面。

    /// 预览视图private let previewView = MWRecordingPreview()

MWRecordingControlView

控制器视图(MWRecordingControlView):提供开始/暂停录制、重新录制、完成录制等按钮。

    /// 底部控制视图private let controlView = MWRecordingControlView()
    // 控制视图private func addControlView() {self.view.addSubview(controlView)controlView.snp.makeConstraints { make inmake.leading.trailing.equalToSuperview()make.height.equalTo(103.0)make.bottom.equalToSuperview().offset(-MW_BOTTOM_SAFE_HEIGHT - 14.0)}// 开始/暂停 录制controlView.recordButtonClickBlock = { [weak self] inguard let self = self else { return }....}// 重新录制controlView.reRecordButtonClickBlock = { [weak self] inguard let self = self else { return }.....}// 完成controlView.finishButtonClickBlock = { [weak self] inguard let self = self else { return }...}}

MWRecordingNavgationView

导航栏(MWRecordingNavgationView):提供切换摄像头和返回的按钮。

  /// 导航栏private let navgationView = MWRecordingNavgationView()
   // 导航private func addNavgationView() {self.view.addSubview(navgationView)navgationView.snp.makeConstraints { make inmake.top.equalToSuperview().offset(MW_TOP_SAFE_HEIGHT)make.leading.trailing.equalToSuperview()make.height.equalTo(56.0)}// 返回navgationView.backButtonClickBlock = { [weak self] inguard let self = self else { return }self.exit()}// 切换摄像头navgationView.switchCameraButtonClickBlock = { [weak self] inguard let self = self else { return }self.recordingController.switchCamera()}}

实现录制的核心逻辑

在这一部分将会重点讲解如何在MWRecordingViewController中实现视频录制的核心功能,就是UI组件与用操作的交互,以及UI组件与录制核心类MWRecordingController的交互。

在创建MWRecordingController时,我们已经设置并启动了会话,那么第一步我们来实现控制视图的开始/暂停录制的功能,在MWRecordingControlView中我们创建了多个闭包。

录制/暂停

其中recordButtonClickBlock负责回调中间录制/暂停按钮的事件。

        // 开始/暂停 录制controlView.recordButtonClickBlock = { [weak self] inguard let self = self else { return }if self.recordingState == .normal {// 正常状态 开始录制self.recordingState = .recordingself.recordingController.startRecording()} else if self.recordingState == .recording {// 录制状态 暂停self.recordingState = .finishself.recordingController.stopRecording()} else if self.recordingState == .finish {// 完成状态 重新录制self.recordingState = .recordingself.recordingController.startRecording()}if self.recordingState == .recording {self.currentDuration = 0self.startTimer()} else {self.stopTimer()}// 隐藏/显示 切换按钮self.navgationView.isHiddenSwitchCameraButton = self.recordingState != .normalself.controlView.setRecordingState(state: self.recordingState)}

该按钮的点击事件会根据当前的状态来执行不同的操作。

  1. normal:当状态为normal,调用MWRecordingController的开始录制startRecording()方法,并修改状态为recording。
  2. recording:当状态为recording,调用MWRecordingController的结束录制stopRecording()方法,并修改状态为finish。
  3. finish:当状态为finish时,直接重新录制,调用开始录制方法。
  4. 切换状态后,如果状态为recording,重新开启定时器,定时读取录制时长,否则停止定时器。
  5. 根据切换后的状态,更新MWRecordingControlView以及MWRecordingNavgationView视图的状态。

重新录制

点击重新录制按钮,直接切换状态为录制状态,并执行开始录制,启动定时器开始读取录制时长。

        // 重新录制controlView.reRecordButtonClickBlock = { [weak self] inguard let self = self else { return }self.stopPlay()self.recordingState = .recordingself.controlView.setRecordingState(state: self.recordingState)self.recordingController.startRecording()self.startTimer()}

完成事件

点击完成事件,首先判断录制时长是否符合要求,然后将录制视频的URL传递回调用的视图控制器,并隐藏当前录制的视图控制器。

        // 完成controlView.finishButtonClickBlock = { [weak self] inguard let self = self else { return }// 判断时长if self.currentDuration < self.minDuration {MWToast.showToast("Video at least \(self.minDuration) seconds")return}self.recordingCompletionHandler?(self.coverImage,self.videoURL)self.exit()}

录制的代理

在MWRecordingController中我们定义了两个代理方法,用于回调录制的错误信息以及录制完成的视频地址。

    //MARK: MWRecordingControllerDelegate/// 录制报错func recordingController(_ controller: MWRecordingController, didFailWithError error: any Error) {let image = UIImage(named: "login_toast_left_icon")MWToast.showTopToast(error.localizedDescription, icon: image)}/// 录制完成func recordingController(_ controller: MWRecordingController, didFinishRecordingTo outputFileURL: URL) {videoURL = outputFileURLlet image = generateCoverImage()coverImage = imageif currentDuration < minDuration {MWToast.showToast("Video at least \(minDuration) seconds")return}}
  1. 错误信息直接显示Toast。
  2. 录制完成后,根据视频地址读取视频封面。

计时器

开启和关闭计时器,在定时事件中更新录制时长,如果达到录制的最大时长自动结束录制。

    /// 开启定时器private func startTimer() {if timer != nil {return}MWLogHelper.debug("开启定时器", context: "MWRecordingViewController")timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)}/// 结束定时器private func stopTimer() {MWLogHelper.debug("结束定时器", context: "MWRecordingViewController")timer?.invalidate()timer = nil}/// 定时器事件@objc private func timerAction() {// 读取录制时长let duration = recordingController.durationcurrentDuration = CMTimeGetSeconds(duration)if currentDuration >= maxDuration {// 结束录制recordingController.stopRecording()recordingState = .finishcontrolView.setRecordingState(state: recordingState)stopTimer()} else {controlView.updateTime(time: currentDuration)}}

结语

在本篇博客中,我们详细介绍了如何在 ViewController 中实现完整的 iOS 视频录制功能。通过整合之前讨论的录制管理核心 MWRecordingController 和自定义的 UI 组件,我们构建了一个可交互的视频录制界面,涵盖了录制的各个方面:从视频预览、录制控制到视频保存与封面生成,提供了一个完整且流畅的用户体验。

通过这次实现,大家可以了解到如何使用 AVFoundation 框架来处理视频录制,同时也掌握了如何结合自定义 UI 和交互设计,提升应用的易用性与功能性。

在未来,视频录制功能的扩展性非常强,可以根据需求加入更多的特性,如视频特效、实时滤镜、录制过程中的实时预览调整等。随着技术的不断发展,如何优化视频录制的性能、降低资源消耗、提高录制质量等,仍然是值得我们不断探索的方向。

希望本系列博客能够帮助你更好地理解 iOS 视频录制的实现方式,也期待你能将这些知识应用到自己的项目中,打造出更丰富、更有趣的用户体验!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/71404.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于豆瓣2025电影数据可视化分析系统的设计与实现

✔️本项目旨在通过对豆瓣电影数据进行综合分析与可视化展示&#xff0c;构建一个基于Python的大数据可视化系统。通过数据爬取收集、清洗、分析豆瓣电影数据&#xff0c;我们提供了一个全面的电影信息平台&#xff0c;为用户提供深入了解电影产业趋势、影片评价与演员表现的工…

tcp协议连接,和传输数据

1、连接 这个是通用的 2、传送数据 当连接建立后&#xff0c;客户端和服务器都可以主动发送数据&#xff0c;分别如下 1》客户端先发送数据 这里是单向的&#xff0c;服务器没有对客户端的数据内容进行应答&#xff0c;只是单纯的对报文应答ack 2》服务器先发送数据

2024年国赛高教杯数学建模C题农作物的种植策略解题全过程文档及程序

2024年国赛高教杯数学建模 C题 农作物的种植策略 原题再现 根据乡村的实际情况&#xff0c;充分利用有限的耕地资源&#xff0c;因地制宜&#xff0c;发展有机种植产业&#xff0c;对乡村经济的可持续发展具有重要的现实意义。选择适宜的农作物&#xff0c;优化种植策略&…

鸿蒙开发:V2版本装饰器之@Monitor装饰器

前言 本文代码案例基于Api13。 随着官方的迭代&#xff0c;在新的Api中&#xff0c;对于新的应用开发&#xff0c;官方已经建议直接使用V2所属的装饰器进行开发了&#xff0c;所以&#xff0c;能上手V2的尽量上手V2吧&#xff0c;毕竟&#xff0c;V2是V1的增强版本&#xff0c;…

国产编辑器EverEdit - 独门暗器:自动监视剪贴板内容

1 监视剪贴板 1.1 应用场景 如果需要对剪贴板的所有历史进行记录&#xff0c;并进行分析和回顾&#xff0c;则可以使用监视剪贴板功能&#xff0c;不仅在EverEdit中的复制会记录&#xff0c;在其他应用的复制也会记录。 1.2 使用方法 新建一个空文档(重要&#xff1a;防止扰乱…

pdf转换成word在线 简单好用 支持批量转换 效率高 100%还原

pdf转换成word在线 简单好用 支持批量转换 效率高 100%还原 在数字化办公的浪潮中&#xff0c;文档格式转换常常让人头疼不已&#xff0c;尤其是 PDF 转 Word 的需求极为常见。PDF 格式虽然方便阅读和传输&#xff0c;但难以编辑&#xff0c;而 Word 格式却能灵活地进行内容修…

深入探索C语言中的字符串处理函数:strstr与strtok

在C语言的字符串处理领域&#xff0c; strstr 和 strtok 是两个非常重要的函数&#xff0c;它们各自承担着独特的功能&#xff0c;为开发者处理字符串提供了强大的支持。 一、strstr函数&#xff1a;字符串查找的利器 strstr 函数用于在一个字符串中查找另一个字符串的首次出现…

AIGC(生成式AI)试用 21 -- Python调用deepseek API

1. 安装openai pip3 install openai########################## Collecting openaiUsing cached openai-1.61.1-py3-none-any.whl.metadata (27 kB) Collecting anyio<5,>3.5.0 (from openai)Using cached anyio-4.8.0-py3-none-any.whl.metadata (4.6 kB) Collecting d…

关于使用雪花算法生成唯一ID,返回给前端ID不一致的问题

问题 在某个项目中,使用雪花算法生成的唯一ID,从数据库查询到数据后返回给前端,但是前端接受到的数据ID和数据库原先生成的不一致 但是前端展示的数据: 原因 原因是后端使用Long类型来存储雪花算法生成的ID,但是这个数值已经超过前端数值类型的范围,导致前端在存储这个数值…

Windows 启动 SSH 服务

Windows 启动 SSH 服务 一、OpenSSH Server 安装 以 Win10 系统为例 打开设置 -> 系统 -> 可选功能 在 添加的功能 查看是否安装了 OpenSSH 服务 或者 OpenSSH Server 如果没有安装&#xff0c;找到 系统->添加可选功能 -> 查看功能->搜索 OpenSSH 服务 ->…

C#功能测试

List 内部元素为引用 src[0]为"11" List<Source> src new List<Source>(); src.Add(new Source() { Name "1", Age 1, Description "1" }); src.Add(new Source() { Name "2", Age 2, Description "2"…

大数据SQL调优专题——Flink执行原理

引入 上一篇我们了解了Spark&#xff0c;相比起MapReduce来说&#xff0c;它确实已经快了超级多了&#xff0c;但是人类的欲望是没有止境的&#xff0c;这也是推动人类进步的动力。 Flink就是为了满足实时响应的场景需求诞生的。 其实在Flink之前&#xff0c;实时处理其实已…

计算机视觉:神经网络实战之手势识别(附代码)

第一章&#xff1a;计算机视觉中图像的基础认知 第二章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(一) 第三章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(二) 第四章&#xff1a;搭建一个经典的LeNet5神经网络(附代码) 第五章&#xff1…

win11安装wsl报错:无法解析服务器的名称或地址(启用wsl2)

1. 启用wsl报错如下 # 查看可安装的 wsl --install wsl --list --online此原因是因为没有开启DNS的原因&#xff0c;所以需要我们手动开启DNS。 2. 按照如下配置即可 Google的DNS&#xff08;8.8.8.8和8.8.4.4) 全国通用DNS地址 (114.114.114.114) 3. 运行以下命令来重启 WSL…

开源模型应用落地-DeepSeek-R1-Distill-Qwen-7B-LoRA微调-LLaMA-Factory-单机单卡-V100(一)

一、前言 如今&#xff0c;大语言模型领域热闹非凡&#xff0c;各种模型不断涌现。DeepSeek-R1-Distill-Qwen-7B 模型凭借其出色的效果和性能&#xff0c;吸引了众多开发者的目光。而 LLaMa-Factory 作为强大的微调工具&#xff0c;能让模型更好地满足个性化需求。 在本篇中&am…

k8s-对接NFS存储

一、前提条件 1、NFS_Server 部署好了。 2、网络可达。 二、 使用方式 1、CSI **项目地址 https://github.com/kubernetes-csi/csi-driver-nfs#readme Install NFS CSI driver v4.10.0 version on a kubernetes cluster If you have already installed Helm, you can a…

【动态路由】系统Web URL资源整合系列(后端技术实现)【nodejs实现】

需求说明 软件功能需求&#xff1a;反向代理功能&#xff08;描述&#xff1a;apollo、eureka控、apisix、sentinel、普米、kibana、timetask、grafana、hbase、skywalking-ui、pinpoint、cmak界面、kafka-map、nacos、gateway、elasticsearch、 oa-portal 业务应用等多个web资…

Git 修改或删除某次提交信息

Git 修改或删除某次提交信息 情况一&#xff1a;未推送到远程仓库修改提交信息删除提交信息&#xff08;替换为空信息&#xff09;修改历史提交信息删除历史提交信息 情况二&#xff1a;已推送到远程仓库修改最新提交信息并推送到远程仓库修改历史提交信息并推送到远程仓库 情况…

DeepSeek崛起:如何在云端快速部署你的专属AI助手

在2025年春节的科技盛宴上&#xff0c;DeepSeek因其在AI领域的卓越表现成为焦点&#xff0c;其开源的推理模型DeepSeek-R1擅长处理多种复杂任务&#xff0c;支持多语言处理&#xff0c;并通过搜索引擎获取实时信息。DeepSeek因其先进的自然语言处理技术、广泛的知识库和高性价比…

DeepSeek部署到本地(解决ollama模型下载失败问题)

一、下载ollama软件安装 1、下载ollama软件 Ollama 下载完成后可以直接进行安装&#xff08;外网&#xff0c;速度可能会有点慢&#xff09; 2、修改安装目录 进去下载的目录&#xff0c;使用cmd打开终端输入OllamaSetup.exe /DIRE:\MySoftware\Ollama 输入完成后会自动打开…