四.音视频编辑-音频混合-概述

引言

当我们在前两篇博客中成功地构建了一个媒体组合,并且略过了音频部分时,我们意识到了我们需要对这个项目进行更详细的探讨。在本篇博客中,我们将会展示如何创建一个包含视频轨道、配音音频轨道以及背景音频轨道的完整媒体组合。更进一步,我们将探讨当两个音频轨道同时竞争空间时的音频混合方案。

构建双音轨组合

在前面博客的基础上我们需要进行一些调整以构建新的媒体组合。

资源选择

首先是资源选择器的调整,原本的资源选择器只支持单一的视频媒体资源选择,现在我们将其分为三组,分别为视频资源,配音音频资源,背景音乐资源。

代码如下:

修改数据源为二维数组

    /// 数据var dataArray:[[PHResource]] = [[PHResource]]()

初始化数据

    override func initData() {super.initData()//添加视频资源var videoArray = [PHResource]()let resource_breckiehill = PHResource(resource_name: "01_nebula", resource_ext: "mp4", resource_type: .video)videoArray.append(resource_breckiehill)let resource_dkglitch = PHResource(resource_name: "04_quasar", resource_ext: "mp4", resource_type: .video)videoArray.append(resource_dkglitch)dataArray.append(videoArray)//添加音频资源var audioArray = [PHResource]()let resource_john_kennedy = PHResource(resource_name: "John F. Kennedy", resource_ext: "m4a", resource_type: .audio)audioArray.append(resource_john_kennedy)let resource_ronald_reagen = PHResource(resource_name: "Ronald Reagan", resource_ext: "m4a", resource_type: .audio)audioArray.append(resource_ronald_reagen)dataArray.append(audioArray)//添加背景音乐资源var bgmArray = [PHResource]()let resource_keep_going = PHResource(resource_name: "02 Keep Going", resource_ext: "m4a", resource_type: .music)bgmArray.append(resource_keep_going)let resource_star_gazing = PHResource(resource_name: "01 Star Gazing", resource_ext: "m4a", resource_type: .music)bgmArray.append(resource_star_gazing)dataArray.append(bgmArray)

列表改为分组样式,显示结果如下图:

资源选择器

资源编辑

资源编辑页面需要将三个轨道都显示到页面上,为此我们需要创建三个不同的横向列表来显示不同的媒体资源轨道。

    /// 数据var timeLine = PHTimeLine()/// 视频编辑视图var videoCollectionView:UICollectionView?/// 音频编辑视图var audioCollectionView:UICollectionView?/// 背景音乐编辑视图var musicCollectionView:UICollectionView?
    func addCollectionView() {addVideoCollectionView()addAudioCollectionView()addMusicCollectionView()}func addVideoCollectionView() {self.videoCollectionView = buildCollectionView(offsetY: 50.0)}func addAudioCollectionView() {let offsetY = CGRectGetMaxY(self.videoCollectionView?.frame ?? CGRect.zero) + 8.0self.audioCollectionView = buildCollectionView(offsetY: offsetY)}func addMusicCollectionView() {let offsetY = CGRectGetMaxY(self.audioCollectionView?.frame ?? CGRect.zero) + 8.0self.musicCollectionView = buildCollectionView(offsetY: offsetY)}/// 创建编辑视图/// - Parameter :/// - offsetY: 偏移量////// - Returns: UICollectionViewfunc buildCollectionView(offsetY:CGFloat) -> UICollectionView {let flowLayout = UICollectionViewFlowLayout()flowLayout.scrollDirection = .horizontalflowLayout.itemSize = item_sizeflowLayout.minimumInteritemSpacing = 0.0flowLayout.minimumLineSpacing = 0.0let collectionView = UICollectionView(frame: CGRect(x: 0.0, y: offsetY, width: self.bounds.size.width, height: item_size.height), collectionViewLayout: flowLayout)collectionView.contentInset = UIEdgeInsets(top: 0.0, left: self.bounds.width*0.5, bottom: 0.0, right: self.bounds.width*0.5)collectionView.delegate = selfcollectionView.dataSource = selfself.addSubview(collectionView)collectionView.register(PHEditorCell.self, forCellWithReuseIdentifier: NSStringFromClass(PHEditorCell.self))return collectionView}

每个collectionView加载timeline中的不同数据。

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {var cell:UICollectionViewCell?var mediaItem:PHMediaItem?var backViewColor:UIColor = .redif collectionView == self.videoCollectionView {mediaItem = timeLine.videoItmes[indexPath.section]backViewColor = .red}if collectionView == self.audioCollectionView {mediaItem = timeLine.audioItems[indexPath.section]backViewColor = .blue}if collectionView == self.musicCollectionView {mediaItem = timeLine.musicItembackViewColor = .green}if let editorCell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(PHEditorCell.self), for: indexPath) as? PHEditorCell {if let mediaItem = mediaItem {editorCell.titleLabel.text = mediaItem.titleeditorCell.backViewColor = backViewColorcell = editorCell}}if cell == nil {assert(false, "cell is nil")}return cell!}

最终的实现的页面显示效果如下:

混合音频

这次组合媒体看起来还不错,场景切换也十分清晰,但仔细感受我们会发现在音频上有一些小瑕疵,首先一个比较严重的问题是我们的两个音频轨道发生了冲突,在开始播放时几乎听不见画外音,背景音乐的音量已经完全覆盖了它。与其让着两个音频轨道发生冲突,倒不如使用一种名为闪避的处理方案,在画外音持续的时间内将背景音乐的音量调低,并保持这个音量直到画外音结束之后再恢复到原来的音量。

另外一个小问题时当播放结束时音乐戛然而止,如果声音可以渐渐减小,会带来更好的用户体验。

框架提供了一个AVAudioMix类处理上面的问题,它是用来在组合的音频轨道中进行自定义音频的处理。

AVAudioMix所具有的音频处理方法是由它的输入参数集定义的,它的参数是AVAudioMixInputParameters类型的对象。AVAudioMixInputParameters的实例关联组合中的单独音频轨道,并在添加到音频混合时定义基于轨道的处理方法。AVAudioMix和其相关联的AVAudioMixInputParameters集合都是不可变对象,意味着它们适用于为AVPlayerItem和AVAssetExportSession之类的客户端提供相关数据,不过它们不能操作其状态。当我们需要创建一个自定义音频混合时,需要改用它们在AVMutableAudioMix和AVMutableAudioMixInputParameters中的可变子类。

AVAudioMix及其相关类的示意图如下:

AVAudioMix及其相关类

自动调节音量

当一个组合资源播放或导出时,默认行为是以最大音量或正常音量。只有一个单音轨道时这样的方法才可能比较容易接受,不过当一个组合资源包含多个音频源时就会出现问题。对于多音频轨道的情况,每个声音都在争夺空间,这就不可避免会导致一些声音可能无法被听到。

AV Foundation把音量定义为了一个标准化的浮点型数值,数值范围从0.0~1.0。音频轨道的默认音量为1.0,不过可以使用AVMutableAudioMixInputParameters实例修改这个值。这个对象允许在一个指定时间点或给定的时间范围自动调节音量。

AVMutableAudioMixInputParameters提供了两个方法来调节音量:

1.在指定时间点立即调节音量。音量在音轨持续时间内会保持不变,直到有另一个音量调节出现。

setVolume(_ volume: Float, at time: CMTime)

volume:表示目标音量。

time:起始时间点。

2.在一个给定时间范围内平滑地将音量从一个值调节到另外一个值。当需要在一个时间范围内调整音量时,音量会立即变为指定值的初始值音量并在持续时间内逐渐调整为指定的结束值。

setVolumeRamp(fromStartVolume startVolume: Float, toEndVolume endVolume: Float, timeRange: CMTimeRange)

starVolume:起始音量。

endVolume:目标音量。

timeRange:变化持续时间范围。

简单示例

我们来使用上面的知识来实现一下下面的小示例,8秒的音频资源,开始播放时默认音量为1.0,当播放到2秒的时候开始设置音量平滑的减小到3秒时音量为0.4,而到第5秒音量直接调整为0.6。

        // 创建一个音频轨道let composition = AVMutableComposition()let compositionTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)// 设置时间let zeroSeconds = CMTime.zerolet twoSeconds = CMTime(value: 2, timescale: 1)let threeSeconds = CMTime(value: 3, timescale: 1)let fiveSeconds = CMTime(value: 5, timescale: 1)// 创建parameterslet parameters = AVMutableAudioMixInputParameters(track: compositionTrack)// 设置初始音量 (即使不设置默认也是最大音量1.0)parameters.setVolume(1.0, at: zeroSeconds)// 2s时音量开始平滑减小3s减小至0.4parameters.setVolumeRamp(fromStartVolume: 1.0, toEndVolume: 0.4, timeRange: CMTimeRange(start: twoSeconds, end: threeSeconds))// 5s时直接设置音量为0.6parameters.setVolume(0.6, at: fiveSeconds)// 创建audioMixlet audioMix = AVMutableAudioMix()audioMix.inputParameters = [parameters]
  1. 首先需要有一个音频轨道。
  2. 定义设置音量变化的时间。
  3. 创建一个新的与要操作的轨道关联的AVMutableAudioMixInputParameters实例。
  4. 默认音量为1.0,在2s时设置音量平滑过渡到0.4持续时间为1s,在5秒时直接设置音量为0.6。
  5. 定义好所有参数后就可以创建AVMutableAudioMix了,将参数添加到数组中,并将数组赋给音频混合对象的inputParameters属性。

示例中创建了一个全格式的音频混合,可以被设置为AVPlayerItem或AVAssetExportSession的audioMix属性进行播放或导出。

结语

在本文中,我们深入探讨了使用AVFoundation进行音频混合的相关类和方案。我们介绍了核心概念,包括AVAudioMix,AVMutableAudioMix,AVAudioMixInputParameters,AVMutableAudioMixInputParameters等类的使用场景。

通过提供一个简单的实例,我们演示了如何在实际项目中应用这些技术。

希望本文能为您提供清晰的指导,并激发您在自己的应用程序中尝试音频混合的想法。在下一篇博客中,我们将继续探讨如何将这些概念整合到实际项目中,为您展示更多高级技术和实用技巧。

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

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

相关文章

lua 环境安装

下载地址: https://luabinaries.sourceforge.net/download.html 安装环境变量 检查一下是否安装成功,有版本号,打印一句话,如下表示成功 idea 安装插件,方便编写lua脚本 配置一下idea 运行测试 local function m…

经典文献阅读之--Light-LOAM( 基于图匹配的轻量级激光雷达里程计和地图构建)

0. 简介 将SLAM应用于机器人应用中,可靠性和效率是两个最受重视的特性。本文《Light-LOAM: A Lightweight LiDAR Odometry and Mapping based on Graph-Matching》考虑在计算能力有限的平台上实现可靠的基于激光雷达的SLAM功能。首先与大多数选择点云配准的显著特征…

国税发票查验接口、电子增值税发票查验接口、数电票查验接口

翔云发票查验接口支持增值税发票管理系统开具发票的真伪,通过发票代码、号码、日期、金额、校验码四要素信息进行真伪的查验,支持返回全票面信息,API接口便于集成,可适用于多种应用场景。 发票查验接口python调用示例:…

正则表达式(Regular Expression)

正则表达式很重要,是一个合格攻城狮的必备利器,必须要学会!!! (参考视频)10分钟快速掌握正则表达式(奇乐编程学院)https://www.bilibili.com/video/BV1da4y1p7iZ在线测试…

分布式监控平台---Zabbix

一、Zabbix概述 作为一个运维,需要会使用监控系统查看服务器状态以及网站流量指标,利用监控系统的数据去了解上线发布的结果,和网站的健康状态。 利用一个优秀的监控软件,我们可以: 通过一个友好的界面进行浏览整个…

CSRF漏洞详解

目录 什么是同源策略 什么是csrf CSRF 攻击会产生什么影响? CSRF漏洞low等级复现 什么是同源策略 协议相同 域名相同 端口相同 什么是csrf 跨站请求伪造(也称为 CSRF)是一种 Web 安全漏洞,允许攻击者诱导用户执行他们不打…

华为各级OD薪资曝光。。

华为 OD 薪资 之前写过几篇华为 OD 的算法题,后来有不少同学问起,华为 OD 薪资到底怎么样。 华为 OD 的薪资待遇,网上信息不多,只找到一篇相对靠谱的爆料: 上述是月 base 的爆料,然后通常 OD 是 15-16 薪。…

如何连通私有子网中的 MSK / Kafka 集群?

MSK 集群通常都是建在私有子网中的,这给本地访问带来了很多麻烦,特别是需要在本地使用 Kafka GUI 客户端管理和读写 MSK 数据的时候。本文会给出一套解决方案。 我们这里讨论的问题有一点特殊性,那就是:由于 MSK 是托管服务&…

标准版uni-app移动端页面添加/开发操作流程

页面简介 uni-app项目中,一个页面就是一个符合Vue SFC规范的.vue文件或.nvue文件。 .vue页面和.nvue页面,均全平台支持,差异在于当uni-app发行到App平台时,.vue文件会使用webview进行渲染,.nvue会使用原生进行渲染。…

单元测试四大过程

单元测试四大过程(蓝桥课学习笔记) 单元测试过程 单元测试是软件测试过程中的一个关键环节,它与集成测试、系统测试一样,分为测试策划、测试设计、测试执行和测试总结几个阶段。 单元测试过程中每个阶段需要完成的主要工作如下&…

Ubuntu配置VScode的C++环境

在Ubuntu系统下配置C环境,并运行helloworld 1. 下载VScode 我这里使用的是星火应用商店,在商店里面可以直接下载安装 http://spark-app.store/ 2.创建文件夹 3.启动VScode并打开该文件夹 4.安装以下几个扩展 PS:Clang这个插件别安装&…

使用TomCat写Film前后端项目0414

使用TomCat写Film前后端项目源文件0414-CSDN博客 实现功能: 得到数据库所有电影数据在首页显示出来 添加 删除 修改 点击修改,获取编号id,传入到根据id编号查询数据的控制器转发数据到 修改的jsp页面。 获取修改数据传入到根据id修改数据的控…

【word2pdf】Springboot word转pdf(自学使用)

文章目录 概要整体介绍具体实现官网pom文件增加依赖 遇到的问题本地运行OK,发布到Linux报错还是本地OK,但是Linux能运行的,但是中文乱码 小结 概要 Springboot word 转 pdf 整体介绍 搜了一下,发现了能实现功能的方法有四种 U…

JDBC 数据库连接

文章目录 JDBC核心技术第1章:JDBC概述1.1 数据的持久化1.2 Java中的数据存储技术1.3 JDBC介绍1.4 JDBC体系结构1.5 JDBC程序编写步骤 第2章:获取数据库连接三要素2.1 要素一:Driver接口实现类2.1.1 Driver接口介绍2.1.2 加载与注册JDBC驱动 2…

SETR——Rethinking系列工作,展示使用纯transformer在语义分割任务上是可行的,但需要很强的训练技巧

题目:Rethinking Semantic Segmentation from a Sequence-to-Sequence Perspective with Transformers 作者: 开源:https://fudan-zvg.github.io/SETR 1.研究背景 1.1 为什么要研究这个问题? 自[ 36 ]的开创性工作以来,现有的语义分割模型主要是**基于全卷积网络( FCN )的…

windows网络驱动开发

基石:WFP 1、简介 Windows过滤平台(Windows Filtering Platform, WFP),是从Vista系统后新增的一套系统API和服务。开发者可以在WFP框架已划分的不同分层中进行过滤、重定向、修改网络数据包,以实现防火墙、入侵检测系…

GNU Radio Radar Toolbox编译及安装

文章目录 前言一、GNU Radio Radar Toolbox 介绍二、gr-radar 安装三、具体使用四、OFDM 雷达仿真 前言 GNU Radio Radar Toolbox(gr-radar)是一个开放源码的工具箱,用于 GNU Radio 生态系统,主要目的是为雷达信号处理提供必要的…

JDK自带的线程池有哪些?

1、Executors.newFixedThreadPool(4); // 核心线程 传几个就有几个核心线程和最大线程数 2、Executors.newCachedThreadPool(); // 核心线程0 ,全是临时工,最大线程数为21亿 3、Executors.newScheduledThreadPool(4); // 传几个就有几个核心线程&#xf…

OSPF 开放式最短路径优先协议

目录 技术产生原因:因为RIP存在不足 OSPF优点: RIPV2和OSPFV2比较: 相同点: 不同点: OSPF的结构化部署 --- 区域划分 区域划分的主要目的: 区域边界路由器 --- ABR : 区域划分的要求&am…

怎么做预约功能_让服务变得更便捷

在快节奏的现代生活中,时间成为了我们最宝贵的财富。无论是工作、学习还是休闲娱乐,我们都希望能够更加高效地利用每一分每一秒。而预约功能的出现,正是为了满足这一需求,让我们的生活变得更加便捷、高效。 工具/原料 微信小程序…