
使用AVFoundation处理视频
使用AVAssetReader、AVAssetWriter编解码视频
之前的两篇文章浅略讲了iOS音视频开发相关代码实现;
在编码时关于音视频的相关参数比较多,这些参数不是随便什么数值就能行的;如果不理解缘由,而填写了不合适的参数,容易导致音视频处理过程中出现各种奇怪的问题;
只有明白了音视频相关的原理,才能理解各种参数的含义,才能更好的实现开发;
现在,就从音频入手,总结下音频相关的理论知识;
声音是如何产生的?
声音是由物体振动而产生的,一切正在发声的物体都在振动

当小球撞击到音叉的时候,音叉会发生振动,对周围的空气产生挤压,然后导致更大范围的空气跟着一起振动,最后我们耳朵旁边的空气也开始振动;这是因为空气产生了疏密变化,形成疏密相间的纵波,由此就产生了声波,声波一直延续到振动消失为止; 声波一直传入我们耳朵,就听到了声音;
我们说话时的声音,也是声带振动的结果;
声音的本质就是声波;
我们听到声音的过程:
声波 --> 耳廓(收集声波)--> 外耳道(传递声波) --> 鼓膜(将声波转换成振动) --> 听小骨(放大振动) --> 耳蜗(将振动转换成电信号) --> 听觉神经(传递电信号) --> 大脑(形成听觉)

声波的三要素是频率、振幅和波形;频率代表音阶的高低,振幅代表响度,波形代表音色。

横坐标为时间,纵坐标为受振动的物体分子来回振动产生的位移;随着时间推移,分子的来回振动的轨迹,就是一个正弦或余弦函数的波形图;
受振动的物体分子每秒来回振动的次数,叫做频率;单位是秒分之一(1/s),也称为赫兹(Hz);频率用来表示振动的快慢
(如441Hz代表每秒来回振动441次)
频率越高,波长就越短。反之频率越低波长则越长,低频率可以更容易地绕过障碍物,因此能量衰减就小,声音就会传得更远。
人类耳朵的听力有一个频率范围,大约是20Hz~20kHz,不过,即使是在这个频率范围内,不同的频率,听力的感觉也会不一样

物体未受到振动影响时的位置(横轴上)称为平衡位置;
从平衡位置到最大位移位置之间的距离,就叫做振幅;
振幅代表响度,振幅越大表示响度越大能量越大,我们听到的声音就越大;
上面我们说声波是正弦或余弦函数的图;但这是在单一频率的声波的情况下的;
事实上,声源的振动产生的并不是单一频率的声波,而是由基音和不同频率的泛音组成的复合声音;当声源的主体振动时会发出一个基音;同时其余各部分也有复合的声源,这些声源组合产生泛音(其实就是物理学上的谐波)
泛音决定了不同的音色,不同的声源由于其材料、结构不同,泛音不同,则发出声音的音色也不同;

最后用一张图总结三要素

上面讲到声音的本质是声波的形式,声音属于模拟信号,但便于计算机处理和存储的是数字信号;所以需要将模拟信号(转成数字信号后进行存储。这一过程,即为音频数字化。
我们在互联网上听到的声音,都是先经过录制后转为了数字音频,再传输到互联网上的;
音频数字化的常见技术方案是脉冲编码调制(PCM,Pulse Code Modulation);
主要过程:采样 、量化 、编码。
所谓采样就是 在时间轴上对信号进行数字化
模拟信号的波形是无限光滑的,可以看成由无数个点组成,由于存储空间是相对有限的,数字编码过程中,必须要对波形的点进行采样。采样就是每隔一段时间采集一次模拟信号的样本,在时间上将模拟信号离散化的过程。
根据采样定理(奈奎斯特–香农采样定理),只有当采样率高于声音信号最高频率的2倍时,才能把采集的声音信号唯一地还原成原来的声音;因此要按比声音最高频率高2倍以上的频率对声音进行采样;人耳能够听到的频率范围是20Hz~20kHz,所以采样频率一般为 44.1kHz,这样就可以保证采样声音达到20kHz也能被数字化,从而使得经过数字化处理之后,人耳听到的声音质量不会被降低。采样率表示每秒采集的样本数量,44.1kHz就是代表1秒会采样44100次;

量化是指在幅度轴上对信号进行数字化,将每一个采样点的样本值数字化
比如用16比特的二进制信号来表示声音的一个采样,而16比特所表示的 范围是[-32768,32767],共有2^16=625536个可能取值,因此最终模拟的音频信号在幅度上也分为了65536层

这里的16bit即为位深度(采样精度/采样大小):使用多少个二进制位来存储一个采样点的样本值;位深度越高,表示的振幅越精确;
所谓编码,就是按照一定的格式记录采样和量化后的数字数据,比如顺序存储或压缩存储,等等。
编码涉及了很多种格式,通常说的音频裸数据格式就是PCM(脉冲编码调制)数据。PCM需要以下几个概念:采样格式(sampleFormat)、采样率 (sampleRate)、声道数(channel)。
采样格式:包含采样位深度、大小端模式、数据排列方式等;
以FFmpeg定义的sampleFormat为例:
enum AVSampleFormat {
AV_SAMPLE_FMT_NONE = -1,
AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
AV_SAMPLE_FMT_S16, ///< signed 16 bits
AV_SAMPLE_FMT_S32, ///< signed 32 bits
AV_SAMPLE_FMT_FLT, ///< float
AV_SAMPLE_FMT_DBL, ///< double
AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP, ///< float, planar
AV_SAMPLE_FMT_DBLP, ///< double, planar
AV_SAMPLE_FMT_S64, ///< signed 64 bits
AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar
AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
};�����������������
其中U,S,F,D表示存储的类型,对应unsigned、signed、float和double类型;
数值表示位深度;
P表示声道数据排列方式为Planar,还有排列方式为Packed:
对于双声道音频来说,Packed表示两个声道的数据交错存储,交织在一起,即:
LRLRLRLR 的存储方式;
Planar 表示两个声道分开存储,也就是平铺分开,即:
LLLLRRRR 的存储方式;
以Packed存储方式为例 大端模式不同位深度数据存储如下:

声道:单声道产生一组声波数据,双声道(立体声)产生两组声波数据。
对于声音格式,还有一个概念用来描述它的大小,称为比特率(byteRate),即指单位时间内传输或处理的比特数量;单位是:比特每秒(bps),还有:千比特每秒(Kbps)、兆比特每秒(Mbps)等等;
以CD的音质为例:位深度为16比特(2字节),采样率为44.1kHZ,声道数为2,这些信息就描述了CD的音质。对于1分钟CD音质的数据,比特率为:
44100 * 16 * 2 = 1378.125 Kbps
存储空间为:
1378.125 * 60 / 8 / 1024 = 10.09MB
通常,采样率、位深度越高,数字化音频的质量就越好。从比特率的特性可以看得出来:比特率越高,数字化音频的质量也越好;我们所说的无损音乐,就是采样率、位深度都很高的,没有进行压缩处理的数字化声音;
最后还是用一张图总结数字化过程:

前面计算了每分钟CD音质的数据采样格式,需要的存储空间约为10.1MB;在网络中传播的话,数据量太大了;
为了更便于存储和传输,一般都会使用某种音频编码对它进行编码压缩,然后再存成某种音频文件格式。
压缩分为无损压缩和有损压缩。
无损压缩:解压后可以完全还原出原始数据;
有损压缩:解压后不能完全还原出原始数据,会丢失一部分信息;一般是压缩掉冗余信号(冗余信号是指不能被人耳感知到的信号,包含人耳听觉范围之外的音频信号以及被掩蔽掉的音频信号等),不进行编码处理。
当需要播放音频时,得先解码(解压缩)出PCM数据,然后再进行播放。

WAV编码
WAV(Waveform Audio File Format),是由IBM和Microsoft开发的音频文件格式,扩展名是.wav,通常采用PCM编码,常用于Windows系统中;编码的一种实现就是在PCM数据格式的前面加上44字节,分别用来描述PCM的采样率、声道数、数据格式等信息。
MP3编码
MP3(MPEG Audio Layer III)具有不错的压缩比,使用LAME编码的中高码率的MP3文件,听感上非常接近源WAV文件。
AAC编码
AAC(Advanced Audio Coding)是新一代的音频有损压缩技术;
AAC编码的文件扩展名主要有3种:
// -----解码----
// AVAssetReader
do {
reader = try AVAssetReader(asset: composition)
} catch let e {
callback(false, e)
return
}
reader.timeRange = CMTimeRange(start: .zero, duration: composition.duration)
// AVAssetReaderOutput
audioOutput = AVAssetReaderAudioMixOutput(audioTracks: audioTracks, audioSettings: nil)
audioOutput.alwaysCopiesSampleData = false
audioOutput.audioMix = audioMix
if reader.canAdd(audioOutput) {
reader.add(audioOutput)
}
if !reader.startReading() {
callback(false, reader.error)
return
}
// -----编码----
// AVAssetWriter
do {
writer = try AVAssetWriter(outputURL: outputUrl, fileType: .mp3)
} catch let e {
callback(false, e)
return
}
writer.shouldOptimizeForNetworkUse = true
let audioOutputSettings: [String : Any] = [
AVFormatIDKey: NSNumber(value: kAudioFormatMPEGLayer3),
AVNumberOfChannelsKey: NSNumber(value: 2),
AVSampleRateKey: NSNumber(value: 44100),
AVEncoderBitRateKey: NSNumber(value: 128000)
]
// AVAssetWriterInput
audioInput = AVAssetWriterInput(mediaType: .audio, outputSettings: audioOutputSettings)
if writer.canAdd(audioInput) {
writer.add(audioInput)
}
writer.startWriting()
writer.startSession(atSourceTime: .zero)
// 准备写入数据
videoInput.requestMediaDataWhenReady(on: inputQueue) { [weak self] in
...
}
其中audioOutputSettings的4项就对应上面分析过的: 编码格式,声道数,采样率,比特率;这些设置最终决定了编码后音频的格式、音频的存储空间及音质;
而且这些设置都是固定组合的,不同编码格式Format所需的Key有所不一样;
wav/pcm 格式设置(需要pcm相关设置):
let audioOutputSettings: [String : Any] = [
AVFormatIDKey: NSNumber(value: kAudioFormatLinearPCM),
AVNumberOfChannelsKey: NSNumber(value: 2),
AVSampleRateKey: NSNumber(value: 44100),
AVLinearPCMBitDepthKey: NSNumber(value: 16),
AVLinearPCMIsBigEndianKey: NSNumber(value: false),
AVLinearPCMIsFloatKey: NSNumber(value: false),
AVLinearPCMIsNonInterleaved: NSNumber(value: false)
]
// 解码 mp3-->pcm
ffmpeg -i test.mp3 -acodec pcm_s16le -f s16le -ac 2 -ar 44100 test.pcm
// 编码 pcm-->mp3
ffmpeg -f s16le -ac 2 -ar 44100 -acodec pcm_s16le -i test test_new.mp3
播放pcm
ffplay -i crop.pcm -ar 44100 -ac 2 -f s16le
续篇:
音视频开发基础理论-视频篇
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,
Ⅰ软件测试基础一、软件测试基础理论1、软件测试的必要性所有的产品或者服务上线都需要测试2、测试的发展过程3、什么是软件测试找bug,发现缺陷4、测试的定义使用人工或自动的手段来运行或者测试某个系统的过程。目的在于检测它是否满足规定的需求。弄清预期结果和实际结果的差别。5、测试的目的以最小的人力、物力和时间找出软件中潜在的错误和缺陷6、测试的原则28原则:20%的主要功能要重点测(eg:支付宝的支付功能,其他功能都是次要的)80%的错误存在于20%的代码中7、测试标准8、测试的基本要求功能测试性能测试安全性测试兼容性测试易用性测试外观界面测试可靠性测试二、质量模型衡量一个优秀软件的维度①功能性功
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现