在FFmpeg中进行音频的格式转换主要有三个步骤。
1、实例化SwrContext
,并设置转换所需的参数:通道数量、channel layout、sample rate
两种方式实例化。
- 使用
swr_alloc
SwrContext *swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", AV_CH_LAYOUT_5POINT1, 0);
av_opt_set_channel_layout(swr, "out_channel_layout", AV_CH_LAYOUT_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_FLTP, 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're allocating a new context
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
2、计算转换后的sample个数
计算公式为:src_nb_samples * dst_sample_rate / src_sample_rate
int dst_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个数。
3、调用 swr_convert
进行转换
int nb = swr_convert(swr_ctx, &audio_buf, dst_nb_samples, (const uint8_t**)frame->data, frame->nb_samples);
SDL播放音频时的格式转换
1、首先使用avcodec_send_packet
和avcodec_receive_frame
获取解码后的原始数据
int ret = avcodec_send_packet(aCodecCtx, &pkt);
ret = avcodec_receive_frame(aCodecCtx, frame);
2、设置通道数量和channel layout
在编码的时候有可能丢失通道数量或者channel layout ,这里根据获取的参数设置其默认值
if (frame->channels > 0 && frame->channel_layout == 0)
frame->channel_layout = av_get_default_channel_layout(frame->channels);
else if (frame->channels == 0 && frame->channel_layout > 0)
frame->channels = av_get_channel_layout_nb_channels(frame->channel_layout)
如果channel layout未知(channel_layout = 0),根据通道数量获取其默认的channel layout;如同通道的数量未知,则根据其channel layout得到其通道数量。
3、设置输出格式
由于SDL2的sample格式不支持浮点型(FFmpeg中是支持的浮点型的),这里简单的设置输出格式为AV_SAMPLE_FMT_S16
(16位有符号整型),输出的channel layout也
根据通道数量设置为默认值 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, frame->sample_rate, 0, nullptr);
if (!swr_ctx || swr_init(swr_ctx) < 0)
return -1;
在设置完参数后,一定要调用swr_init
进行初始化。
4、转换
// 计算转换后的sample个数 a * b / c
int dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, frame->sample_rate) + frame->nb_samples, frame->sample_rate, frame->sample_rate, AVRounding(1));
// 转换,返回值为转换后的sample个数
int nb = swr_convert(swr_ctx, &audio_buf, dst_nb_samples, (const uint8_t**)frame->data, frame->nb_samples);
data_size = frame->channels * nb * av_get_bytes_per_sample(dst_format);
最后data_size
中保存的是转换的数据的字节数:通道数 * sample个数 * 每个sample的字节数。
https://github.com/brookicv/FFMPEG-study/blob/master/FFmpeg-playAudio.cpp