这样,一个channel_layout就是上述channelmask的组合,部分定义如下:
#defineAV_CH_LAYOUT_MONO(AV_CH_FRONT_CENTER)
#defineAV_CH_LAYOUT_STEREO(AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
#defineAV_CH_LAYOUT_2POINT1(AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY)
#defineAV_CH_LAYOUT_2_1(AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER)
#defineAV_CH_LAYOUT_SURROUND(AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)
#defineAV_CH_LAYOUT_3POINT1(AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY)
#defineAV_CH_LAYOUT_4POINT0(AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER)
#defineAV_CH_LAYOUT_4POINT1(AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY)
#defineAV_CH_LAYOUT_2_2(AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
#defineAV_CH_LAYOUT_QUAD(AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
#defineAV_CH_LAYOUT_5POINT0(AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
#defineAV_CH_LAYOUT_5POINT1(AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY)
...
AV_CH_LAYOUT_STEREO是立体声(2通道),其通道的存放顺序为LEFT|RIGHT;
AV_CH_LAYOUT_4POINT0是4通道,其通道的存放顺序为
LEFT|RIGHT|FRONT-CENTER|BACK-CENTER;其它数量的声道与此类似。
下面列举一些和channel_layout相关的函数
uint64_tav_get_channel_layout(constchar*name)根据传入的字符
串,返回相对应的channel_layout。
传入的参数可以是:
常用的channellayout的名称:
mono,stereo,4.0,quad,5.0,5.0(side),
5.1等。
一个单通道的名称:
FL,FR,FC,BL,BR,FLC,FRC等
通道的数量
channel_layoutmask,以"0x"开头的十六进制串。
更多详细的说明,参见该函数的文档。
intav_get_channel_layout_nb_channels(uint64_tchannel_layou
t)根据通道的layout返回通道的个数
int64_tav_get_default_channel_layout(intnb_channels)根据通道
的个数返回默认的layout
intav_get_channel_layout_channel_index(uint64_tchannel_layout,
uint64_tchannel);返回通道在layout中的index,也就是某一通道
在layout的存储位置。
av_get_channel_layout_channel_index的实现如下:
intav_get_channel_layout_channel_index(uint64_tchannel_layo
ut,
uint64_tchannel),{
if(!
(channel_layout&channel)||
av_get_channel_layout_nb_channels(channel)!
=1),returnAVERROR(EINVAL);
channel_layout&=channel-1;
returnav_get_channel_layout_nb_channels(channel_layout);
}
首先判断传入的layout包含该通道,并且保证该传入的通道是一个单通道。
以4通道AV_CH_LAYOUT_4POINT0为例,说明下计算方法。
AV_CH_LAYOUT_4POIN
T0=AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|
AV_CH_BACK_CENTER
其二进制表示为0001,0000,0111,假如想找AV_CH_BACK_CENTER在该layout中
的index。
AV_CH_BACK_CENTER的十六进制为0x0100,二进制为0001,0000,000
0,那么
AV_CH_BACK_CENTER-1=1111,1111。
0001,0000,0111&0000,1111,111
1=0111,函数av_get_channel_layout_nb_channels是获取某个layout对应
的通道的数量,
前面提到,layout中值为1的位的个数和通道的数量相等,所以AV_CH_BACK_CENT
ER在layoutAV_CH_LAYOUT_4POINT0的index为3。
Audio格式转换
在FFmpeg中进行音频的格式转换主要有三个步骤
1.实例化SwrContext,并设置转换所需的参数:
通道数量、channellayout、samplerate
有以下两种方式来实例SwrContext,并设置参数:
使用swr_alloc
SwrContext*swr=swr_alloc();
av_opt_set_channel_layout(swr,"in_channel_layout",AV_CH_L
AYOUT_5POINT1,0);
av_opt_set_channel_layout(swr,"out_channel_layout",AV_CH_L
AYOUT_STEREO,0);
av_opt_set_int(swr,"in_sample_rate",48000,
0);
av_opt_set_int(swr,"out_sample_rate",44100,
0);
av_opt_set_sample_fmt(swr,"in_sample_fmt",AV_SAMPLE_FMT_F
LTP,0);
av_opt_set_sample_fmt(swr,"out_sample_fmt",AV_SAMPLE_FMT_S16,
0);
使用swr_alloc_set_opts
SwrContext*swr=swr_alloc_set_opts(NULL,//we'reallocat
inganewcontext
AV_CH_LAYOUT_STEREO,//out_ch_layout,AV_SAMPLE_FMT_S16,//out_sample_fmt,44100,//out_sample_rate,AV_CH_LAYOUT_5POINT1,//in_ch_layout
AV_SAMPLE_FMT_FLTP,//in_sample_fmt,48000,//in_sample_rate,0,//log_offset
NULL);//log_ctx
上述两种方法设置那个的参数是将5.1声道,channellayout为AV_CH_LAYOUT
_5POINT1,采样率为48KHz转换为2声道,channel_layout为AV_SAMPLE_F
MT_S16,采样率为44.1KHz。
1.计算转换后的sample个数
转后后的sample个数的计算公式为:
src_nb_samples*dst_sample_rate/src_sample_rate,其计算如下:
intdst_nb_samples=av_rescale_rnd(swr_get_delay(swr_ctx,
frame->sample_rate)+frame->nb_samples,frame->sample_rate,
frame->sample_rate,AVRounding
(1));
函数av_rescale_rnd是按照指定的舍入方式计算a*b/c。
函数swr_get_delay得到输入sample和输出sample之间的延迟,并且其返回值的根据传入的第二个参数不同而不同。
如果是输入的采样率,则返回值是输入sample个数;如果输入的是输出采样率,则返回值是输出sample个数。
2.调用swr_convert进行转换
intnb=swr_convert(swr_ctx,&audio_buf,dst_nb_samples,(constuint8_t**)frame->data,frame->nb_samples);
其返回值为转换的sample个数。
SDL播放音频时的格式转换
首先使用avcodec_send_packet和avcodec_receive_frame获取解码后的原
始数据
intret=avcodec_send_packet(aCodecCtx,&pkt);
if(ret<0&&ret!
=AVERROR(EAGAIN)&&ret!
=AVERROR_EOF),return-1;
ret=avcodec_receive_frame(aCodecCtx,frame);
if(ret<0&&ret!
=AVERROR_EOF)
return-1;
这里不再使用avcodec_decode_audio4进行音频的解码,在FFmpeg3中该函数已
被废弃,使用avcodec_send_packet和avcodec_receive_frame替代。
新的解
码API使用更为方便,
设置通道数量和channellayout
在编码的时候有可能丢失通道数量或者channellayout,这里根据获取的参数设置
其默认值
if(frame->channels>0&&frame->channel_layout==0)
frame->channel_layout=av_get_default_channel_layout(fra
me->channels);
elseif(frame->channels==0&&frame->channel_layout>0)
frame->channels=av_get_channel_layout_nb_channels(frame->ch
annel_layout);
如果channellayout未知(channel_layout=0),根据通道数量获取其默认的c
hannellayout;如同通道的数量未知,则根据其channellayout得到其通道数量。
设置输出格式
由于SDL2的sample格式不支持浮点型(FFmpeg中是支持的浮点型的),这里简
单的设置输出格式为AV_SAMPLE_FMT_S16(16位有符号整型),输出的channell
ayout也
根据通道数量设置为默认值dst_layout=av_get_default_channel_layout
(frame->channels)(SDL2不支持planar格式)。
实例化SwrContext
swr_ctx=swr_alloc_set_opts(nullptr,dst_layout,dst_format,
frame->sample_rate,
frame->channel_layout,(AVSampleFormat)frame->format,fra
me->sample_rate,0,nullptr);
if(!
swr_ctx||swr_init(swr_ctx)<0)
return-1;
在设置完参数后,一定要调用swr_init进行初始化。
转换
//计算转换后的sample个数a*b/c
intdst_nb_samples=av_rescale_rnd(swr_get_delay(swr_ctx,fr
ame->sample_rate)+frame->nb_samples,frame->sample_rate,frame
->sample_rate,AVRounding
(1));
//转换,返回值为转换后的sample个数
intnb=swr_convert(swr_ctx,&audio_buf,dst_nb_samples,(co
nstuint8_t**)frame->data,frame->nb_samples);
data_size=frame->channels*nb*av_get_bytes_per_sample(dst_fo