iOS 音频流播(一)

作者: anyoptional | 来源:发表于2017-05-26 16:14 被阅读1666次

    前言

    学习AudioToolBox有一段时间了,期间有遇到不少坑(主要还是英文不够好,看官方文档不甚明了)。随着一路踩坑填坑,倒是积累了不少音频相关的知识。在这里分享一下心得,算是给后来的人一点帮助吧。
      这一篇是基础篇,主要介绍音频基础知识,顺便讲讲iOS上实现音频流播的几种方式和需要掌握的知识。
      开源项目DOUAudioStreamer对我帮助很大(感谢开源社区),而我最开始的知识来自这一系列博客,大家也可以去学习一下。很多知识我就直接引用了(其实是copy过来),这里先说明一下。

    音频基础知识

    音频文件的生成过程是将连续的离散信号(真实的声音信号)采样、量化再编码转化为离散的数字信号的过程,这一过程被称为脉冲编码调制(Pulse Code Modulation,PCM)。采样必须遵循奈奎斯特抽样定理(只有采样频率高于声音信号最高频率的两倍时,才能把数字信号表示的声音还原成为原来的声音),而人耳能听到的声音,频率最低从20Hz起到20KHz,所以音频文件的采样率一般在40KHz以上,比如最常见的44.1KHz和48KHz。

    采样(sampling)

    把模拟音频转成数字音频的过程,就称作采样。采样的过程实际上是将通常的模拟音频信号的电信号转换成二进制码0和1,这些0和1便构成了数字音频文件。要正确理解音频采样可以分为采样的频率和采样的位数。

    采样频率(sample rate)

    也称为采样速度或者采样率(单位Hz),定义了每秒从连续信号中提取并组成离散信号的采样个数。通俗的讲采样频率是指计算机每秒钟采集多少个信号样本,采样频率越高声音的还原就越真实越自然。

    采样位数

    也称为采样精度(单位bit),采样位数可以理解为采集卡处理声音的解析度。这个数值越大,解析度就越高,录制和回放的声音就越真实。移动端常见的采样位数为8或16。8位代表2的8次方--256,16位则代表2的16次方--64K。比较一下,一段相同的音乐信息,16位声卡能把它分为64K个精度单位进行处理,而8位声卡只能处理256个精度单位,造成了较大的信号损失,最终的采样效果自然是无法相提并论的。

    通道数(channel)

    分为单声道和立体声。当然还存在更多的通道数。举个列子,声道多,效果好,两个声道,说明只有左右两边有声音传过来, 四声道,说明前后左右都有声音传过来。

    样本(sample),帧(frame),包(packet)
    • 一个音频样本是单个音频通道的单一数值。
    • 一个音频帧是时间重合的样本的集合。例如,立体声文件每帧有两个样本,一个用于左声道,一个用于右声道。
    • 一个音频包是一个或多个连续的帧的集合。在线性PCM音频中,音频包始终是单个帧。在压缩格式中,一般是多个帧。在给定的音频格式中,音频包定义了最小且具有意义的一组音频帧的集合。
    比特率(bit rate)

    也叫码率,声音中的比特率是指将模拟声音信号转换成数字声音信号后,单位时间内的二进制数据量,是间接衡量音频质量的一个指标(比特率越高音质越好)。计算公式:比特率 = 采样率 x 采样位数 x 声道数,单位bps(Bit Per Second)。例如一个48KHz采样率,双声道,16位采样率,它的比特率为 48KHz * 2 * 16bit = 1536kbps(这里的k是1000)。下图是网易云音乐提供的音频码率。


    netease@2x.png
    音频压缩

    一个采样率为44.1KHz,采样位数为16bit/s,双声道的PCM编码的音频文件,它的数据速率则为 44.1K * 16 * 2=1411.2 kbps。 将码率除以8(进制转换,1Byte=8Bit),就可以得到数据速率,即176.4KB/s。这表示存储一秒钟采样率为44.1KHz,采样大小为16bit/s,双声道的PCM 编码的音频信号,需要176.4KB的空间,1分钟则约为10.34M,这对大部分用户是不可接受的,尤其是喜欢在电脑上听音乐的朋友,要降低磁盘占用, 只有2种方法,降低采样指标或者压缩,而降低指标是不可取的,就只能压缩了。对于原始音频数据,如PCM,每一个数据包只有一个音频帧,而对于压缩音频,如MP3,一个数据包往往对应着多个音频帧。

    audioUnitPlay.jpg
    编码方式VBR、CBR

    VBR(Variable Bitrate)动态比特率 也就是没有固定的比特率,压缩软件在压缩时根据音频数据即时确定使用什么比特率,这是以质量为前提兼顾文件大小的方式,推荐编码模式;
      CBR(Constant Bitrate),常数比特率 指文件从头到尾都是一种位速率。相对于VBR和ABR来讲,它压缩出来的文件体积很大,而且音质相对于VBR和ABR不会有明显的提高

    MP3格式

    MP3是目前最常用的音频格式。MP3格式中的数据通常由两部分组成,一部分为ID3用来存储歌名、演唱者、专辑、音轨数等信息,另一部分为音频数据。音频数据部分以帧(frame)为单位存储,每个音频都有自己的帧头。MP3中的每一个帧都有自己的帧头,其中存储了采样率等解码必须的信息,所以每一个帧都可以独立于文件存在和播放,这个特性加上高压缩比使得MP3文件成为了音频流播放的主流格式(也正是因为这个,于是出现了VBR技术,可以让MP3文件的每一段甚至每一帧都可以有单独的bitrate,这样做的好处就是在保证音质的前提下最大程度的限制了文件的大小)。帧头之后存储着音频数据,这些音频数据是若干个PCM数据帧经过压缩算法压缩得到的,对CBR的MP3数据来说每个帧中包含的PCM数据帧是固定的,而VBR是可变的。

    单个音频包的持续时间

      packetDuration = framesPerPacket / sampleRate * 1000,也就是说单个包的持续时间(毫秒) = 单个包的帧数 / 采样频率 * 1000
      例如,对于一个采样率44.1KHz的MP3文件,一个音频包有1152个音频帧(固定的),根据公式计算可得 packetDuration = 1152 / 44.1KHz * 1000 = 26.1224...(约等于26ms),即一个MP3音频包的持续时间是26ms。这个知识点在seek时会发挥比较大的作用,可以用于求出seek后对应的音频包位置。

    iOS音频播放总览

    了解了基础概念之后我们就可以列出一个经典的音频播放流程(以MP3为例):
    1、读取MP3文件
    2、解析采样率、码率、时长等信息,分离MP3中的音频帧
    3、对分离出来的音频帧解码得到PCM数据
    4、对PCM数据进行音效处理(均衡器、混响器等,非必须)
    5、把PCM数据解码成音频信号
    6、把音频信号交给硬件播放
    7、重复1-6步直到播放完成

    在iOS系统中apple对上述的流程进行了封装并提供了不同层次的接口,下面对其中的中高层接口进行功能说明:

    • Audio File Services:读写音频数据,可以完成播放流程中的第2步;
    • Audio File Stream Services:对音频进行解码,可以完成播放流程中的第2步;
    • Audio Converter services:音频数据转换,可以完成播放流程中的第3步;
    • Audio Processing Graph Services:音效处理模块,可以完成播放流程中的第4步;
    • Audio Unit Services:播放音频数据:可以完成播放流程中的第5步、第6步;
    • Extended Audio File Services:Audio File Services和Audio Converter services的结合体;
    • AVAudioPlayer/AVPlayer(AVFoundation):高级接口,可以完成整个音频播放的过程(包括本地文件和网络流播放,第4步除外);
    • Audio Queue Services:高级接口,可以进行录音和播放,可以完成播放流程中的第3、5、6步;

    我们可以自身需求,选择不同的接口:

    • 如果只是想实现音频的播放,没有其他需求AVFoundation会很好的满足你的需求。它的接口使用简单、不用关心其中的细节;
    • 如果想实现一个音频流播放器,有几种选择:
      • CFNetwork + AudioFileStream + AudioQueue。用CFReadStream读取音频数据并交给AudioFileStream或者AudioFile解析分离音频帧,分离出来的音频帧可以送给AudioQueue进行解码和播放。如果是本地文件用NSFileHandle直接读取文件解析即可。开源播放器AudioStreamer就是这种思路。
      • CFNetwork + AudioFile + AudioConverter + AudioUnit。同样用CFReadStream读取音频数据并交给AudioFile解析分离音频帧,分离出来的音频帧交给AudioConverter转换成PCM数据,再把PCM数据交给AudioUnit进行播放。这是开源播放器DOUAudioStreamer的搭建方式,也是这一系列文章要讲述的方式。

    为什么选择CFNetwork而不是NSURLSession?
      由于NSURLSession是异步请求,这样就会涉及到多线程之间的通信问题,比较复杂,并且读取到的数据大小不好控制,而使用CFNetwork则不一样,可以精确控制每次读取到的数据大小,并且读取数据是在本线程中,所以虽然说CFNetwork是底层网络,但是使用CFNetwork反而比使用NSURLSession更加简单。
      
      下篇将会介绍iOS中的音频小管家,AVAudioSession

    相关文章

      网友评论

      • 渐z:博主,你好,请教一个问题。使用CFReadStream读取音频数据时,音频数据还没读取完整,就回调EndEncountered了,这是为何?
      • 8ab85fad1fa1:做实时音视频传输 可以用 AudioFile + AudioConverter + AudioUnit么?接收的是AAC数据流 通过解码 之后 再用AudioUnit 播放PCM
      • wanna_dance:不错不错 给你点个赞 6666666 写的很好 有空详细看
      • Junheng: 牛逼的人都有相似之处,比如你。。。。。。。强逼

      本文标题:iOS 音频流播(一)

      本文链接:https://www.haomeiwen.com/subject/nfosxxtx.html