1.背景
音频 IPC相对于普通IPC相比,支持语音的输入、输出的功能以及语音对讲功能。音频IPC的视频部分与普通IPC对比,几乎是完全一致,只是添加了音频的部分,因此本文从音频的角度出发,介绍音频的产生、音频数据的传输、以及音频的处理等方面内容。
1.2.术语定义
AI:Audio Input音频输入模块
AO:Audio Output音频输出模块
AENC:Audio Encode音频编码模块
ADEC:Audio Decode音频解码模块
BQ:IPC相关缓存模块;
PCM:Pulse Code Modulation脉冲编码调制
RIFF:Resources Interchange File Format 资源互换文件格式(windows环境下大部分多媒体文件遵循的一种文件结构)
Ffmpeg:一种多媒体视音频处理工具,可以用来记录、转换数字音频、视频,并能将其转化为流的程序。
PTS:Presentation Time Stamp 显示时间戳
TS:Transport Stream 传输流,用于HTTP方式传输音频数据的数据流
Live555:是一个为流媒体提供解决方案的跨平台的开源项目,实现了多种音视频编码格式的音视频数据的流化、接收和处理等功能
2.音频数据流图
2.1.IPC音频流图
音频数据部分主要可以分为音频产生、音频传输和音频解码播放三部分,该过程实现和视频数据流一致。上图为大致的流程框架,使用HTTP/RTSP进行不同的连接。
2.2.音频数据采样、编码流程
2.2.1.脉冲编码调制
音频从Line In/Mic In端采集后,经过PCM(脉冲编码调制)把一个时间连续,取值连续的模拟信号变换成时间离散,取值离散的数字信号后在信道中传输。模拟信号->采样->量化->编码->数字信号。
1、抽样:连续的时间信号进行周期性扫描,变成时间上离散的瞬时信号,抽样遵循抽样定理。
(Nyquist采样定律:采样率大于或等于连续信号最高频率分量的2倍时,采样信号可以用来完美重构原始连续信号)
2、量化:用一组规定的电平,把瞬时值用最接近的电平值表示,比如可以分为量化值A0~A15。
3、编码:用一组二进制码组来表示量化值,比如用0011来表示上面提到的量化值A3。通常,可以使用8bit或者16bit来表示量化值。编码的目的一方面是为了用计算机语言去描述声音数据,另外一方面是为了用各种算法(可还原)去压缩数据,减小传输带宽的压力。因此Encode阶段不同的音频格式对应着不同的编码、压缩方式,不同的音频格式使用的编码器也不一样,主要分为硬件编码器编码与软件编码两种;
硬件编码:以G.711a、G.711u(PCMA、PCMU)格式为例是将PCM数据送入AENC(音频编码模块),这个部分是专门的一个编码器(属于硬件);
软件编码:以AAC等音频格式为例,需要ffmpeg将PCM转化编码成AAC格式;
4、存入缓存:编码完成后,相关进程将码流存入缓存。当RTSP Client或者 HTTP Client需要预览发出请求的时候,再对数据帧然后进行一定的封装,RTSP方式将音频数据封装成RTP包、HTTP方式则加上Wave Head通过TS流封装,分发所有HTTP连接。
2.2.2.音频帧、PTS概念
视频数据存在“帧”的概念,也就是一个图像,常见IPC一般都是25帧,也就是40ms一帧。音频数据在产生的过程中,也有“帧”的概念,这个帧可以理解为音频的打包周期。音频帧差时长取决于音频采样率、量化精度(位宽)、AI中音频buf大小有关。
帧率 = 码率/buf_len = 采样率 * 位宽 / buf_len
不同的平台IPC帧率不太一样,由于这些参数都是供应商设定好的,没有提供接口去修改。
确定音视频帧的一个很重要的事情就是为了确定每个音视频包的PTS,也就是显示时间戳,对于后续客户端进行正确顺序播放、音视频同步的播放有很大的意义。首先选择一个参考时钟(要求参考时钟上的时间是线性递增的);生成数据流时依据参考时钟上的时间给每个数据块都打上时间戳(一般包括开始时间和结束时间)
例如:视频流40ms/1帧,音频35ms/1帧,则基于IPC时钟系统打上相应的时间戳。
3.音频传输
底层获取到音频数据后放到bq模块中,然后通知接收者去发送bq模块中的音频数据。IPC支持RTSP、HTTP两种连接方式,下面分别介绍:
3.1.RTSP
rtsp server使用live555开源程序,其工作流程如下:
1.rtsp server接收到客户端请求,创建一个处理线程,并注册信号的处理函数,用于监听bq模块中是否有新的帧。
2.aenc没编码出足够的音频数据放入bq的同时,向rtsp server的所有处理线程发送信号(有新音频数据后只向请求音频的线程发信号)。
3.处理线程收到信号后,就去bq模块中获取帧,通过相应socket发送到客户端。
3.1.1. 音频RTSP传输原理
最开始的IPC产品通过RTSP只传输视频流,在这个基础上如何增加音频流的传输,需要首先分析一下live555对于媒体文件的管理方式。
LIVE555使用MediaSession管理一个媒体文件,每个MediaSession使用文件名唯一标识。使用SubSession管理MediaSession中的一个音频流或视频流,因此一个MediaSession可以有多个MediaSubsession,一个管理音频流一个管理视频流。
客户端在给服务器发送DESCRIBE查询某个文件的SDP信息时,服务器会给客户端返回媒体文件所包含的多个媒体流信息。并为每个媒体流分配一个TrackID。如视频流分配为Track1,音频流分配为Track2。此后RTSP Client必须在URL指定要为哪个Track发送SETUP命令。 因此我们可以认为MediaSubsession代表Server端媒体文件的一个Track,也即对应一个媒体流。MediaSession代表Server 端一个媒体文件。
搞清楚live555如何管理媒体文件后,添加音频流的传输也即对现有的MediaSession增加一个SubSession,原来的MediaSession只有一个SubSession(管理h264/h265视频),新增的SubSession用来管理音频流。
3.1.2. 音频RTSP传输过程
在了解完音频RTSP传输的原理后,看看我司NVR与音频IPC交互的流程,在进行RTSP交互之前NVR与IPC会在Onvif阶段Capability数据包中声明IPC是否支持音频,具体字段为AudioSource、AudioOutputs分别代表着IPC是否支持音频的输入、输出:0代表这不支持、1代表支持。
1、DESCRIBE
客户端发送请求:
DESCRIBE rtsp://192.168.1.60:554/stream1 RTSP/1.0
解析:该方法是客户端像服务端请求描述媒体的详细信息。包中说明需要描述的媒体文件具体目录和名称,定义客户端能理解的描述类型,要求服务端以SDP包方式来描述媒体信息
服务端响应请求:
服务器回复的SDP数据包中包含两部分,具体如下:
第一部分解析:这是服务端响应DESCRIBE请求所发回的报文。以上内容说明描述的媒体文件具体路径和名称,以及所采用的描述类型(sdp),并定义了SDP包内容的长度。以下的第二部分是SDP包的内容。
第二部分解析:该部分是SDP包内容(图中红色框为SDP必选项),包括媒体的所有初始化信息,申明自己支持的媒体流信息,并且为每个不同的媒体流分配了不同track ID。
v (protocol version) :【0】
//SDP版本号
o (owner/creator and session identifier) :【34826504 1 IN IP4 192.168.1.60】
//<用户名> <会话版本> <网络类型><地址类型> <服务器地址> {其中session id在整个会话过程中必须是唯一的}
s (session name):【Session stream by “TP-LINK RTSP Server”】
//会话名(唯一),服务器信息。
t(time the session is active):【0 0】
//媒体开始时间和结束时间,默认0 0表示持续会话
m(media name and transport address):【audio 0 RTP/AVP 8】
//媒体类型+端口+传输协议+格式列表 {其中每种格式对应不同的序号}
a(media attribute):control:【track1】
//session轨道 {与之前声明的媒体类型对应,后续客户端根据轨道进行不同数据类型的传输}
*其他可选信息大多都是默认值或者对于传输无较大含义,如b(bandwidth information)NVR不予理睬,故不一一详解。
2、SETUP
客户端发送请求:
若IPC回复中有音频、视频,则分别建立一个Session进行数据传输,音频、视频彼此独立,请求的url会申明不同的track id。
解析:客户端向服务端发送SETUP请求,要求服务端设置会话属性和流媒体传输方式以建立会话。包内容包含所需要的传输协议(RTP),传播方式和客户端用来接收数据的端口号。
注意:
1、此处不同于主子码流是两个TCP连接,NVR请求的虽然是两个不同的track id,但是都是在同一个TCP的连接下。由于有不同的track,NVR端在Setup阶段已经可以区分视音频数据。
2、由于主、子码流的音频部分相差不大,我司实现的时候NVR与IPC在SETUP阶段只建立一个码流的音频Session的会话。
服务端响应请求:
解析:服务端接收到SETUP请求后建立会话,向客户端返回会话详细信息以及会话标识。会话标识是唯一的。至此一个会话建立完成。
3、PLAY
客户端发送请求:
PLAY rtsp://192.168.1.60:554/stream1 RTSP/1.0
解析:会话建立后,客户端发出PLAY请求播放所申请的流媒体。传输机制按照SETUP命令所设置的进行。
服务器响应请求:
RTSP/1.0 200 OK
解析:服务器返回确认报文并开始传输流媒体数据。
4、RTSP传输数据
真正音频数据传输如图,wireshark可以直接解析出来音频数据的type。
(RTP timestamp与IPC本身的PTS不是一个概念,但是我们依然可以看到RTP包的顺序关系,过滤这个type后可以发现每个音频的RTP包在RTP的timestamp相差320byte,由于我司音频采样频率为8KHz,量化精度为8bit,所以计算【帧差 = 8000Hz * 8bit/320byte = 40ms】与我们之前说的IPC大约25的帧率吻合)
3.2.HTTP传输原理
当客户端发起一个带音频的预览请求时,会向该media_source注册一个media_receiver,用于记录特定于该连接的信息。该media_source负责管理(添加、删除)音频通道的所有receivers,所有请求音频的客户端以链表的形式保存在该media_source中。
底层每获取到足够的音频数据(与AI通道设置的bufferLen大小有关),就将其传给media_source,media_source将该帧分发给所有的media_receiver,media_receiver找到对应特定http连接的发包函数将帧通过socket发送出去。
下图说明了音频数据的http发送过程:
3.2.1.名词解释
ES流(Elementary Stream):基本码流,不分段的音频、视频或其他信息的连续码流。
PES流:把基本流ES根据需要分成长度不等的数据包,并加上相应头文件打包形成的打包基本码流。
TS流(Transport Stream):传输流,将具有共同时间基准或独立时间基准的一个或多个PES(固定长度为188字节的包)复合而成的单一数据流(用于数据传输)。
3.2.2.TS流
TS(Transport Stream)传输流,顾名思义主要被用来实时传送数据,IPC与Web端的音视频数据传输就是通过TS流:IPC取到音视频基本码流后打包成TS流,并通过HTTP协议传到Web端,由插件进行TS流的解析后进行播放。
另外因为在打包TS流的过程中封装进了时间标记等同步信息,并且TS流的包结构是长度是固定的,所以当传输误码破坏了某一TS包的同步信息时,接收端可以在固定的位置检测它后面的包中的同步信息,从而恢复同步,避免信息丢失。
3.2.3.TS流形成
从上图可以看出,视频ES和音频ES通过打包器和共同或独立的系统时间基准形成一个个PES,通过TS复用器复用形成的传输流。
3.2.4.TS流格式
TS流是基于Packet的位流格式,每个包是188个字节(或204个字节,在188个字节后加上了6字节的CRC校验数据,其他格式一样)。整个TS流组成形式如下:
4个字节的包头表明了该Packet的属性,其中13bits为PID,即Packet ID号码,PID是TS流中唯一识别标志,Packet Data是什么媒体类型的内容就是由PID决定的。(不过这些packet都已经被http封装,wireshark抓包无法看到对应字段)。
4.音频处理机制
4.1.NVR端同步机制
数据流通过RTSP传输到NVR端后,分别存入NVR的音频缓存和视频缓存里(由于RTSP视音频通过不同的Track传输,故NVR建立RTSP连接时已经可以区分视音频数据流),此后音视频分别送入解码器解码,最后通过AO模块进行播放。除此之外,在播放之前NVR会进行视音频,NVR还会对视音频数据做同步处理,保证音视频同步播放。避免音视频不同步现象有两个关键:一是在生成数据流时要打上正确的时间戳。如果数据块上打的时间戳本身就有问题,那么播放时再怎么调整也于事无补。第二个关键的地方,就是在播放时基于时间戳对数据流的控制,也就是对数据块早到或晚到采取不同的处理方法。下面我们分别简单介绍:
4.1.1.PTS时间戳
从技术上来说,解决音视频同步问题的最佳方案就是时间戳:首先选择一个参考时钟(要求参考时钟上的时间是线性递增的);生成数据流时依据参考时钟上的时间给每个数据块都打上时间戳(一般包括开始时间和结束时间)。
打时间戳时,视频流和音频流都是参考参考时钟的时间,而数据流之间不会发生参考关系;也就是说,视频流和音频流是通过一个中立的第三方(也就是参考时钟)来实现同步的。
在播放时,读取数据块上的时间戳,同时参考当前参考时钟上的时间来安排播放。如果数据块的开始时间晚于当前参考时钟上的时间,则不急于播放该数据块,直到参考时钟达到数据块的开始时间;如果数据块的开始时间早于当前参考时钟上的时间,则“尽快”播放这块数据或者索性将这块数据“丢弃”,以使播放进度追上参考时钟。
4.1.2.NVR数据流控制进程
基于时间戳的播放过程中,仅仅对早到的或晚到的数据块进行等待或快速处理,有时候是不够的。如果想要更加主动并且有效地调节播放性能,需要引入一个反馈机制,也就是要将当前数据流速度太快或太慢的状态反馈给“源”,让源去放慢或加快数据流的速度。理论来说,视音频数据每帧间隔的时间一致,因此控制进程每过相同的帧差时间将视音频数据流送入解码器即可。但因网络传输原因,到达NVR端的数据时间上并不是那么间隔均匀的,因此NVR会有相应的控制进程做处理,保证预览实时性(注意:回放由于实时性不高,没有该机制)。处理机制可以简单的用“视频看队列,音频看视频”概括,也就是说优先排序视频队列,再将音频按照视频的顺序匹配解码。
4.2.WEB端处理机制
4.2.1.Wave head头部标志
音频数据在进行打包的时候,已经加了相应的Wave head。Wave head是目前网页端接收到的音频数据包的格式约定,在把TS包组成音频帧后,每一个音频帧之前都会带有这个标志。
研究过程中发现wireshark抓取的数据包中唯一可识别代表是音频数据的就是Wave_head中标识的“RIFF”字段,RIFF文件是windows环境下大部分多媒体文件遵循的一种文件结构,RIFF文件所包含的数据类型由该文件的扩展名来标识,RIFF后面跟随的就是音频数据内容。
WEB Plugin根据PID区分出音频数据后,根据Wave_head解析相关音频数据,然后再调用相应的Filter(滤波器)对音频、视频部分解码、外加一定的渲染,最后播放出来。
Wave_head头部结构如下:头部主要声明协议、Wave format(包含编码格式、采样率、传输率、音频数据长度等)、头部之后就是真正的音频数据内容。
5.语音对讲
5.1.语音对讲音频流图
语音对讲音频数据流图每个过程与IPC发出音频的数据流图相似,两个流图几乎是镜像的,WEB端到IPC端音频流程图大致如下:
5.2.语音对讲传输
5.2.1.语音对讲交互过程
语音对讲IPC到Client数据流与音频CET功能一致,所以在这里只要再实现对讲Client到IPC数据流部分。另外,RTSP(NVR)对讲未实现,所以主要介绍WebIPC的语音对讲。
Web与IPC通过http协议连接交互,Web–>IPC数据可以采用HTTP POST的实现,直接将语音数据推送到IPC端。Client到IPC端的语音数据没有时间戳(PTS)的概念,因此Client即插件把音频数据编码后打包成TS流直接Post到IPC端即可,WEB在语音对讲时会将自己切换为半双工模式,此时WEB端不会对IPC传来的音频数据做播放处理。
设备端收到请求后会回复对讲的会话ID(session_id),WEB按照这个会话id进行后续语音对讲的通讯。IPC在收到音频数据后会逐帧进行TS解包,然后将每一帧放置到bq队列中,调用底层send接口播放线程进行依次取帧播放;