很多时候在播放音频时,音频的采样率、通道数等并不能满足我们播放得需求,这就需要我们对音频进行重采样。
FFmpeg提供的方法libswresample.a库为我们很好的解决重采样的问题,下面就对如何使用这个库进行具体说明。
#pragma mark -- 重采样
- (int)AudioResamplingWithAVCodecContext:(AVCodecContext *)audio_dec_ctx andAVFrame:(AVFrame *)pAudioDecodeFrame andOutfmt:(int)out_sample_fmt andOutchannels:(int)out_channels andOutRate:(int)out_sample_rate andOutbuf:(uint8_t *)out_buf
{
//
SwrContext * swr_ctx = NULL;
int data_size = 0;
int ret = 0;
int64_t src_ch_layout = AV_CH_LAYOUT_STEREO; //初始化这样根据不同文件做调整
int64_t dst_ch_layout = AV_CH_LAYOUT_STEREO; //这里设定ok
int dst_nb_channels = 0;
int dst_linesize = 0;
int src_nb_samples = 0;
int dst_nb_samples = 0;
int max_dst_nb_samples = 0;
uint8_t **dst_data = NULL;
int resampled_data_size = 0;
//重新采样
if (swr_ctx)
{
swr_free(&swr_ctx);
}
swr_ctx = swr_alloc();//为重采样上下文申请空间
if (!swr_ctx)
{
printf("swr_alloc error \n");
return -1;
}
src_ch_layout = (audio_dec_ctx->channel_layout &&
audio_dec_ctx->channels ==
av_get_channel_layout_nb_channels(audio_dec_ctx->channel_layout)) ?
audio_dec_ctx->channel_layout :
av_get_default_channel_layout(audio_dec_ctx->channels);
if (out_channels == 1)//输出单声道
{
dst_ch_layout = AV_CH_LAYOUT_MONO;
}
else if(out_channels == 2)//输出双声道
{
dst_ch_layout = AV_CH_LAYOUT_STEREO;
}
else
{
//可扩展
}
if (src_ch_layout <= 0)
{
printf("src_ch_layout error \n");
return -1;
}
// src_nb_samples的值是固定的 aac:1024 opus:960 mp3:1152
src_nb_samples = pAudioDecodeFrame->nb_samples;
if (src_nb_samples <= 0)
{
printf("src_nb_samples error \n");
return -1;
}
/* 设置参数 */
av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0);//设置输入源的通道布局
av_opt_set_int(swr_ctx, "in_sample_rate", audio_dec_ctx->sample_rate, 0);//设置输入源的采样率
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", audio_dec_ctx->sample_fmt, 0);//设置输入源的采样格式
av_opt_set_int(swr_ctx, "out_channel_layout", dst_ch_layout, 0);//设置输出源的通道布局
av_opt_set_int(swr_ctx, "out_sample_rate", out_sample_rate, 0);//设置输出源的采样率
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", (enum AVSampleFormat)out_sample_fmt, 0);//设置输出源的采样格式
swr_init(swr_ctx);
/**
av_rescale_rnd作用:
将以audio_dec_ctx->sample_rate表示的src_nb_samples转换成out_sample_rate。
AV_ROUND_UP:取整方式选择趋于更大的整数
*/
//计算转换后的采样数,要避免溢出
max_dst_nb_samples = dst_nb_samples = (int)av_rescale_rnd(src_nb_samples, out_sample_rate, audio_dec_ctx->sample_rate, AV_ROUND_UP);
if (max_dst_nb_samples <= 0)
{
printf("av_rescale_rnd error \n");
return -1;
}
//分配目标采样存储空间
dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, dst_nb_channels,
dst_nb_samples, (enum AVSampleFormat)out_sample_fmt, 0);
if (ret < 0)
{
printf("av_samples_alloc_array_and_samples error \n");
return -1;
}
//计算方式为:a*b/c ,先转换成64位再除y,避免溢出
dst_nb_samples = (int)av_rescale_rnd(swr_get_delay(swr_ctx, audio_dec_ctx->sample_rate) +
src_nb_samples, out_sample_rate, audio_dec_ctx->sample_rate,AV_ROUND_UP);
if (dst_nb_samples <= 0)
{
printf("av_rescale_rnd error \n");
return -1;
}
if (dst_nb_samples > max_dst_nb_samples)
{
av_free(dst_data[0]);
ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,
dst_nb_samples, (enum AVSampleFormat)out_sample_fmt, 1);
max_dst_nb_samples = dst_nb_samples;
}
data_size = av_samples_get_buffer_size(NULL, audio_dec_ctx->channels,
pAudioDecodeFrame->nb_samples,
audio_dec_ctx->sample_fmt, 1);
if (data_size <= 0)
{
printf("av_samples_get_buffer_size error \n");
return -1;
}
resampled_data_size = data_size;
if (swr_ctx)
{
// 主要是将FLT和FLTP格式转换成S16或S16P格式
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples,
(const uint8_t **)pAudioDecodeFrame->data, pAudioDecodeFrame->nb_samples);
if (ret <= 0)
{
printf("swr_convert error \n");
return -1;
}
// 获取重采样后的数据大小
resampled_data_size = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,
ret, (enum AVSampleFormat)out_sample_fmt, 1);
if (resampled_data_size <= 0)
{
printf("av_samples_get_buffer_size error \n");
return -1;
}
}
else
{
printf("swr_ctx null error \n");
return -1;
}
//将值返回去
memcpy(out_buf,dst_data[0],resampled_data_size);
if (dst_data)
{
av_freep(&dst_data[0]);
}
av_freep(&dst_data);
dst_data = NULL;
if (swr_ctx)
{
swr_free(&swr_ctx);
}
return resampled_data_size;
}