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

(二十一)深入了解AVFoundation-编辑:导出视频与格式转换的全流程

博客专栏链接:AVFoundation架构与实践

博客专栏源码链接:AVFoundation-建议转存大量实战源码

一. 引言

在前面的系列博客中,我们主要聚焦于音视频的播放与合成技术,完成了视频的剪辑、水印叠加等功能,但并未深入讲解如何将编辑好的内容导出成新的视频文件。

本篇博客将详细介绍 AVFoundation 中视频导出的实现,重点讲解:

  • 参数化导出配置:如何灵活设置导出质量、格式等参数,满足不同需求;
  • 导出进度监听与取消:利用定时器轮询实现进度反馈,并支持用户主动取消导出;
  • 错误处理:针对导出失败的常见情况给出解决思路;
  • 保存到相册:导出完成后如何将视频保存至系统相册,及权限相关处理。

通过这篇文章,你将掌握一个可复用的导出组件设计思路,实战中可以直接使用并根据需求灵活调整。

二. 导出功能详解

在前面的博客中,我们使用了比较简陋的导出方式,参数固定且缺少对导出进度、错误和取消的处理。实际开发中,这些功能至关重要,能极大提升用户体验和程序稳定性。

本节将围绕我们封装的 PHCompositionExporter 类,详细介绍如何实现:

  1. 参数化导出配置
  2. 导出进度监听
  3. 取消导出
  4. 错误处理
  5. 保存到相册

2.1 参数化导出配置

AVFoundation 中,AVAssetExportSession 允许我们通过不同的预设(preset)控制导出质量、分辨率等,同时也支持多种输出格式。

在 PHCompositionExporter 里,beginExport 方法支持传入三个参数:

func beginExport(presetName: String = AVAssetExportPresetHighestQuality,outputFileType: AVFileType = .mp4,saveToPhotoLibrary: Bool = true)

presetName:导出预设名称,常用的有

  • AVAssetExportPresetLowQuality
  • AVAssetExportPresetMediumQuality
  • AVAssetExportPresetHighestQuality(默认)
  • 还有专门针对HEVC编码的预设,如 AVAssetExportPresetHEVCHighestQuality

outputFileType:输出文件格式,常见有 .mp4, .mov, .m4v 等,需与预设兼容,否则导出会失败。

saveToPhotoLibrary:导出完成后是否自动保存到相册,默认为 true。

通过参数化配置,开发者可以根据具体场景灵活调整导出效果和格式,满足从快速预览到高质量成片的不同需求。

2.2 导出进度监听

AVAssetExportSession 自身不支持通过 KVO 监听导出进度,因此我们通过一个定时器每 0.1 秒轮询 exportSession.progress。

在类中我们通过 progressTimer 实现:

self.progressTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ inguard let self = self, let session = self.exportSession else { return }self.onProgress?(session.progress)
}

然后在外部通过设置 onProgress 闭包,就能实时拿到进度数据,比如更新进度条。

2.3 取消导出

用户可能在导出过程中主动取消操作,我们需要提供接口取消导出,并正确处理回调。

PHCompositionExporter 提供了 cancelExport() 方法,内部调用 exportSession.cancelExport(),并且停止进度定时器:

func cancelExport() {exportSession?.cancelExport()progressTimer?.invalidate()progressTimer = nil
}

导出取消后,exportSession.status 会变成 .cancelled,我们在回调中判断并返回对应错误。

2.4 错误处理

导出失败的情况较多,常见包括:

  • 磁盘空间不足
  • 导出格式和预设不兼容
  • 权限问题(写入沙盒或相册失败)
  • 导出过程异常中断

在导出完成的回调中,我们会判断 exportSession.status,如果是 .failed,则将错误信息通过 onCompletion 回调返回。

case .failed:let error = exportSession.error ?? NSError(domain: "Export", code: -6, userInfo: [NSLocalizedDescriptionKey: "Export failed with unknown error."])self.onCompletion?(.failure(error))

这样调用方就可以据此做出对应的 UI 提示或重试逻辑。

2.5 保存到相册

导出视频后,通常需要保存到系统相册方便用户查看。

PHCompositionExporter 提供了 saveToPhotoLibrary 方法,利用 Photos 框架实现保存:

PHPhotoLibrary.shared().performChanges({PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputURL)
}, completionHandler: { success, error in// 回调保存结果
})

注意,iOS 14+ 需要用户授权访问相册,若用户拒绝则保存失败。

三. Demo:导出功能完整示例

接下来我们以实际代码演示如何结合前面封装好的导出组件,实现完整的导出流程,包括显示导出进度视图、响应取消和完成操作。

/// 执行导出
private func export() {let timeLine = timeLineViewController.timeLinelet compositionBuilder = PHCompositionBuilderFactory.buildComositionBuilder(timeLine: timeLine)guard let composition = compositionBuilder.buildComposition() else {print("Failed to build composition for export")return}// 初始化导出助手exportHelper = PHCompositionExporter(composition: composition)// 显示导出进度视图showExportView()// 绑定导出进度和完成回调exportHelper?.onProgress = { [weak self] progress in// 更新导出视图进度guard let self = self else { return }self.exportView?.updateState(progress: progress, result: nil)}exportHelper?.onCompletion = { [weak self] result in// 导出完成,更新视图状态guard let self = self else { return }switch result {case .success(let url):print("导出成功,文件路径:\(url)")DispatchQueue.main.async {// 在主线程更新 UIself.exportView?.updateState(progress: 1.0, result: true)}// 可根据需求处理导出成功后的逻辑,比如保存、分享case .failure(let error):print("导出失败:\(error.localizedDescription)")DispatchQueue.main.async {// 在主线程更新 UIself.exportView?.updateState(progress: 0.0, result: false)}}}// 开始导出exportHelper?.beginExport()print("开始导出视频")
}/// 显示导出视图
/// Show the export progress view, keep a weak reference, and handle user actions.
private func showExportView() {// 若已有导出视图,先移除if let existView = exportView {existView.removeFromSuperview()}// 初始化导出视图let exportView = PHExportView()// 设置视图样式及布局exportView.layer.masksToBounds = trueexportView.layer.cornerRadius = 20.0self.view.addSubview(exportView)exportView.snp.makeConstraints { make inmake.width.height.equalTo(250.0)make.center.equalToSuperview()}// 弱引用保存self.exportView = exportView// 绑定取消按钮回调exportView.onCancel = { [weak self] in// 取消导出self?.exportHelper?.cancelExport()// 移除导出视图并清理资源self?.exportView?.removeFromSuperview()self?.exportView = nilself?.exportHelper = nil}// 绑定完成按钮回调exportView.onComplete = { [weak self] in// 移除导出视图并清理资源self?.exportView?.removeFromSuperview()self?.exportView = nilself?.exportHelper = nil}
}

四. 结语

本篇博客我们详细介绍了 AVFoundation 中视频导出的全流程实现,从参数化配置、进度监听、取消导出,到错误处理与保存相册,都做了深入讲解和实战示范。

通过封装 PHCompositionExporter 和自定义的导出视图,我们实现了一个易用、灵活且用户体验友好的导出模块,方便在实际项目中直接集成。

接下来,你可以在此基础上继续扩展更多功能,比如导出格式支持更多编码参数,或者结合后台任务实现更稳定的长时间导出。

希望这篇文章对你的开发有所帮助,欢迎留言交流!

http://www.dtcms.com/a/342931.html

相关文章:

  • 全文 part1 - DGEMM Using Tensor Cores, and Its Accurate and Reproducible Versions
  • DeepSeek-V3.1 发布,迈向 Agent 时代的第一步
  • 0821 sqlite3_get_table函数(数据库函数的补充)
  • Nacos-9--认识Nacos中的Distro协议(Nacos高可用的实现原理)
  • visual studio编译的软件查找所依赖的运行库方法
  • 基于单片机智能路灯控制
  • 学习嵌入式第三十四天
  • 杂记 07
  • BGP高级特性
  • AI论文速读 | 多模态能否助力时间序列预测?时序预测中融合文本的边界与条件
  • Oracle CLOB类型转换
  • 数据分析三剑客
  • 如何解读京东按图搜索(拍立淘)API(jd.item_search_img)的返回值
  • AI大模型支持下的:CMIP6数据分析与可视化、降尺度技术与气候变化的区域影响、极端气候分析
  • JVM-(7)堆内存逻辑分区
  • 3个脱节,5大特征,1套方法:破解AI落地难题
  • 37、需求预测与库存优化 (快消品) - /供应链管理组件/fmcg-inventory-optimization
  • 【互动屏幕】大屏拼接在数字展厅展示上有哪些优势?
  • (CVPR-2025)通过频率分解实现身份保持的文本到视频生成
  • 【音视频】闭合GOP和开放GOP
  • 旅游小程序开发指南
  • 第三阶段数据库-5:数据库的主键,索引,约束,表间关系的图形化操作
  • 8.Shell脚本修炼手册---sed工具的基本使用
  • HarmonyOS 实战:6 种实现实时数据更新的方案全解析(含完整 Demo)
  • JavaScript中的深浅拷贝
  • Llama-Factory微调 Qwen2.5-VL-3B 模型
  • 人工智能未来趋势如何?
  • 【秋招笔试】2025.08.19百度秋招机考第一套
  • 算法训练营day57 图论⑦ prim算法精讲、kruskal算法精讲
  • 前端无感刷新 Token 的 Axios 封装方案