精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
音频输出的主要目的是从一个或者几个解码器(也叫“输入流“)得到声音采样,然后混合它们并且写到输出设备(也叫“输出流”)。在这个过程中,转换可能需要,也可能被用户要求,由音频过滤器来完成这个过程。
以下为常用的音频术语:
采样:采样是音频信息的基本部分,包括所有信道的值。例如不管有多少信道被编码也不管系数的编码类型,流工作在44100HZ就是表明流每秒采样44100个音频元素。
系数:一采样元素对于每个信道都含有一个系数。例如一个立体声流的每一个元素里有两个系数。许多音频项(例如float32位的音频混音器)直接处理系数。由于一采样元素不能单独的在流里被事例化,因此一个没被解码的元素格式当然就没有系数的概念。
整个音频输出可以别看成以连续的步骤来把一种音频格式转换成另一种音频格式的管道,这对于理解音频采样格式非常重要。audio_sample_format_t结构被定义在incfude/audio_output.h里,它包括以下的成员:
(1)i_format:
定义了系数的格式。比如:’fl32’(float32),’fi32’(fixed32),’s16b’(有符号的16bit字节以big- endian方式存储),‘s161’(有符号的16bit字节以little-endian方式存储),AOUT_FMT_S16_NE(‘sl6b’ 或者’s16l’的截短),’u16b’,’u16l’,’s8’,’u8’,’ac3’,’spdi’(S/PDIF)。没被解码的采样格式包 括:’a52’,’dts’,’spdi’,’mpga’(MPEG音频I,II层),’mpg3’(MPEG音频III层)。音频过滤器(允许从一种格 式到另一种格式)被定义为转换器,有些转换器扮演着解码器的角色(比如a52tonoat32.c),但是实际上它是音频过滤器。
(2)i_rate:
定义了音频输出每秒要处理的采样数。通常这个值为22050,24000,44100,48000,单位为HZ。
(3)i_physical_channels:
定义在缓冲器里被自然编码的信道。它掩盖了在audio_output.h定义的比特值(比如 AOUT_CHAN_CENTER,AOUT_CHAN_LEFT等)。要注意的是:这个数字值并不代表每个采样含有多少个系数 (aout_FormatNbChannels()里有说明),由于对于混音器处理交叉存取的系数是比较容易的,因此每个信道的系数也总是交叉存取的,从 而一个来可以输出平面数据的解码器必须能实现交叉存取的功能。
(4)i_original_channels:
定义用于组成缓冲器的原始流的信道。比如说只有单一输出的插件但要输出的音频流是立体声时,那么可以同时使用流的信道(i_original_channels == AOUT_CHAN_LEFT| AOUT_CHAN_RIGHT)或者选择一个信道。
i_original_channels和i_physical_channels使用同一个比特掩码,并且表明了专用的比特流 AOUT_CHAN_DOLBYSTEREO(表明输入流是否下行混音成杜比环绕声)和AOUT_CHAN_DUALMONO(表明立体声流实际上是由两 个单一的流所组成的),这两个专用的比特流只能选择一个(就像在一个VCD上可以有两种语言)。
对于16位的整型格式的音频类型,可以区分开big-endian和little-endian的存储类型。但对于浮点型的音频类型(可以以 big-endian存储或者用little-endian存储)却无法分别开来。这是因为采样的数据是在文件里被强制储存成32位的浮点类型并且从一个 机器转移到另一个机器中,因此这32位的浮点类型数据以本地机器的endian来默认储存。然而采样的数据一般都是以16位的整型类型big- endian方式来储存的(比如DVD的LPCM格式),因此LPCM解码器分配’s16b’,给输入流,在little-endian机器上也是这 样,’s16b’->’s16l’的转换被输入管道自动的激活。(在大多数情况下,AOUT_FMT_S16_NE和 AOUT_FMT_U16_NE要被用到)。音频输出内核提供了宏来比较两种音频采样格式。AOUT_FMT_IDENTICAL)用来验证是否 i_format,i_rate,i_physical _channels和i_original_channels一样,AOUT_FMT_SIMILAR()用来验证i_rate和i_channels是 否一样(有利于写一个纯粹的转换过滤器)。
audio_sample_format_t结构包含两个额外的参数(除非处理没被解码的音频格式就不需要有)。对于PCM格式这两个参数被自动添充到aout_FormatPrepare()(必要时被内核模块所调用)。下面说明这两个额外的参数:
i_frame_lenth:定义普通帧采样的数目。对于A\52,1536个采样是压缩后在没被解码的缓冲器里的数目,那么i_frame_length=1536;对于PCM格式,帧的大小为1(在缓冲器里的每一个采样值都能独立的获得)。
i_bytes_per_frame:定义帧的大小(字节)。对于A\52它取决于输入流(同步读取)的比特率,例如对于32位浮点类型的立体声采样,i_bytes_per_frame == 8(i_frame_length=1l)。
输入派生了一个新的音频解码器(比如A\52解码器)。A\52解码器为格式信息解析同步信息并且用aout_InPutNew()来创造出输入流的新的音频输出,这个采样格式为:
i_format=’a52’
i_rate=48000
i_physical_channels=i_original_channels=AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT|AOUT_CHAN_CENTER|AOUT_CHAN_REARLEFT|AOUT_CHAN_REARRIGHT|AOUT_CHAN_LFEi_frame_length=1536
i_bytes_per_frame=24000
这个输入格式不能被修改且存储在aout_input_t structure里和p_aout->pp_input[0]->input这个输入流一致。既然这个是输入的第一个流,音频输出将会用这 个音频采样格式(p_aout->output.output),来配置输出设备以避免不必要的转换。
音频内核用通常的方式来探测输出模块,输出模块的行为取决于输出设备,如果输出设备有S/PDIF能力的话,那么就需要设置 p_aout->output,把output .i_format转换成’spdi’格式,如果只是PCM设备的话就需要知道本地的采样格式(比如用于Darwin CoreAudio的’fl32’或者用于OSS的AOUT_FMT_S16_NE)。输出设备也可能因信道的数目和流的速率不同而受到限制(例如 p_aout->output)。输出结构可以如下:
一旦获得了输出的格式,那么需要根据它来推出混音器的格式。除了i_format以外,禁止在混音器和输出之间来改变音频采样格式(所有的转换在输 入管道里发生),这是因为需要开发出三个混音器(内置设备的float32 and S/PDIF和plus fixed32),所有其它的类型必须转化成这三个中的一个。下面以p_aout->mixer为例,混音器的结构如下:
音频输出内核因此要分配音频过滤器来把’fl32’转换成AOUT_FMT_S16_NE,这是在输出管道里唯一的音频过滤器,同时也要分配一个 32位浮点类型的混音器。由于只是一个输入流,因此用一个很小的混音器就好了(只需从第一个输入流里复制采样数据即可),否则要用到更准确的32位浮点类 型的混音器。
初始化的最后一步是编译输入管道。当需要更改几个属性时,音频输出内核将会优先搜索音频过滤器可以更改的属性:
如果整个的转换不能用一个音频过滤器来完成的话,音频内核就需要分配第二个甚至第三个过滤器来处理剩余的转换。接着上面的例子,分配两个过滤器:
a52到float32(用来处理这种转换和下行混音)和重采样器。出于对效率的考虑,通常对于没被解码的格式转换器也要处理下行混音。
<.p>
当初始化完成后,解码器插件模块就会运行自己的主循环。通常解码器需要i_nb_samples大小的缓冲器并且复制没被解码的采样值到这个缓冲器 里(用到GetChunk()函数)。然后这个缓冲器和输入管道一起编码(为’fl32’格式),下行混音和重采样。如果输出层需要暂时的调节其快慢速率 以获得和输入流完美的同步(由每一个缓冲器的基层来确定)时就需要重采样。在输入管道的底端,这个缓冲器将放置到FIFO上,同时解码器线程运行音频混音 器。
这时音频混音器就会计算采样数是否足够多来编译一个新的输出缓冲器。如果足够的话,它就和输入流混合经过缓冲器到达输出层。为了输出设备缓冲器就顺 着输出管道(在上面的例子里只含有转换过滤器)到达输出FIFO。这时输出设备就会从输出FIFO上取出下一个缓冲器,取出的方法为通过回调音频子系统 (Mac OS X’CoreAudio,SDL)或者通过音频输出线程(OSS,ALSA…)来达到。这种机制用到了aout_OutputNextBuffer()函 数,并且给出了缓冲器大致的播放时间。如果计算出的播放时间和估计的播放时间不一致时(小的差别),输出层就会在音频输出模块上修改所有的缓冲器时间,当 下一个从解码器出来的缓冲到来的时候就会在输入管道的开始处触发再采样。用这种方法需要重同步音视频流,当缓冲播放时,就会最终释放它。