提供一个可以运行的ffmpeg工程:https://gitee.com/qiuguolu1108/ffmpeg-study
ffmpeg -i Jasmine.mp4 -vn -ar 48000 -ac 2 -f f32le Jasmine.pcm
进入player目录,使用上述命令生成待处理的pcm文件。源pcm文件的采样率为48000,采样格式为float,经过重采样后,pcm文件的采样率为44100,采样格式为s16。
#include "spdlog/spdlog.h"
extern "C" {
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "libswresample/swresample.h"
}
char error_buf[1024] = {0};
char* av_get_err(int err_num) {
av_strerror(err_num, error_buf, sizeof(error_buf));
return error_buf;
}
int get_format_from_sample_fmt(const char** fmt,
enum AVSampleFormat sample_fmt) {
struct sample_fmt_entry {
enum AVSampleFormat sample_fmt;
const char *fmt_be, *fmt_le;
} sample_fmt_entries[] = {
{AV_SAMPLE_FMT_U8, "u8", "u8"},
{AV_SAMPLE_FMT_S16, "s16be", "s16le"},
{AV_SAMPLE_FMT_S32, "s32be", "s32le"},
{AV_SAMPLE_FMT_FLT, "f32be", "f32le"},
{AV_SAMPLE_FMT_DBL, "f64be", "f64le"},
};
*fmt = NULL;
for (int i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {
struct sample_fmt_entry* entry = &sample_fmt_entries[i];
if (sample_fmt == entry->sample_fmt) {
*fmt = AV_NE(entry->fmt_be, entry->fmt_le);
return 0;
}
}
SPDLOG_ERROR("Sample format {} not supported as output format.",
av_get_sample_fmt_name(sample_fmt));
return AVERROR(EINVAL);
}
int main() {
const char* src_file_name = "src.pcm";
FILE* src_fd = fopen(src_file_name, "rb");
if (!src_fd) {
SPDLOG_ERROR("Could not open {}", src_file_name);
}
const char* dst_file_name = "dst.pcm";
FILE* dst_fd = fopen(dst_file_name, "wb");
if (!dst_fd) {
SPDLOG_ERROR("Could not open {}", dst_file_name);
}
//输入参数
int64_t src_ch_layout = AV_CH_LAYOUT_STEREO; //声道布局
int src_rate = 48000; //采样率
enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_FLT; //采样格式
int src_nb_channels = 0; //声道数
uint8_t** src_data = NULL; //音频数据
int src_linesize; //每个line中的数据大小
int src_nb_samples = 1024; //一帧数据的采样点数
//输出参数
int64_t dst_ch_layout = AV_CH_LAYOUT_STEREO;
int dst_rate = 44100;
enum AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;
int dst_nb_channels = 0;
uint8_t** dst_data = NULL;
int dst_linesize;
int dst_nb_samples;
int max_dst_nb_samples;
#if 1
// 1、2、创建重采样实例,并设置参数,NULL表示内部申请SwrContext。
struct SwrContext* swr_ctx =
swr_alloc_set_opts(NULL, dst_ch_layout, dst_sample_fmt, dst_rate,
src_ch_layout, src_sample_fmt, src_rate, 0, NULL);
#else
// 1、创建重采样实例
struct SwrContext* swr_ctx = swr_alloc();
if (!swr_ctx) {
SPDLOG_ERROR("Could not allocate resampler context");
}
// 2、设置重采样参数
//输入参数
av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0);
av_opt_set_int(swr_ctx, "in_sample_rate", src_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_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", dst_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0);
#endif
// 3、初始化重采样
int ret = swr_init(swr_ctx);
if (ret < 0) {
SPDLOG_ERROR("Failed to initialize the resampling context");
}
//通过声道布局,计算声道数。
src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout);
//根据音频参数,申请空间,并计算linesize大小。
ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize,
src_nb_channels, src_nb_samples,
src_sample_fmt, 0);
if (ret < 0) {
SPDLOG_ERROR("Could not allocate source samples");
}
//计算转换后一帧音频的采样点数
max_dst_nb_samples = dst_nb_samples =
av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
//计算声道数
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,
dst_sample_fmt, 0);
if (ret < 0) {
SPDLOG_ERROR("Could not allocate destination samples");
}
while (!feof(src_fd)) {
//读取一帧音频数据
fread(src_data[0], 1024 * 4 * 2, 1, src_fd);
//计算swr中没有被取走的采样点数
int64_t delay = swr_get_delay(swr_ctx, src_rate);
//重新计算输出一帧的采样点数
dst_nb_samples =
av_rescale_rnd(delay + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
if (dst_nb_samples > max_dst_nb_samples) {
av_freep(&dst_data[0]);
ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,
dst_nb_samples, dst_sample_fmt, 1);
if (ret < 0) {
break;
}
max_dst_nb_samples = dst_nb_samples;
}
// 4、重采样转换 返回采样点数
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples,
(const uint8_t**)src_data, src_nb_samples);
if (ret < 0) {
SPDLOG_ERROR("Error while converting");
}
//计算输出buffer的大小
int dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,
ret, dst_sample_fmt, 1);
if (dst_bufsize < 0) {
SPDLOG_ERROR("Could not get sample buffer size");
}
SPDLOG_INFO("in: {}, out: {}", src_nb_samples, ret);
fwrite(dst_data[0], 1, dst_bufsize, dst_fd);
}
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, NULL, 0);
if (ret < 0) {
SPDLOG_ERROR("Error while converting");
}
int dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,
ret, dst_sample_fmt, 1);
if (dst_bufsize < 0) {
SPDLOG_ERROR("Could not get sample buffer size");
}
SPDLOG_INFO("flush in: {}, out: {}", src_nb_samples, ret);
fwrite(dst_data[0], 1, dst_bufsize, dst_fd);
const char* fmt;
ret = get_format_from_sample_fmt(&fmt, dst_sample_fmt);
if (ret < 0) {
SPDLOG_ERROR("Could not find sample format.");
}
SPDLOG_INFO(
"Resampling succeeded. Play the output file with the command:\n ffplay "
"-f "
"{} -channel_layout {} -channels {} -ar {} {}",
fmt, dst_ch_layout, dst_nb_channels, dst_rate, dst_file_name);
fclose(dst_fd);
av_freep(&src_data[0]);
av_freep(&src_data);
av_freep(&dst_data[0]);
av_freep(&dst_data);
swr_free(&swr_ctx);
return 0;
}
...
[2021-09-14 21:17:22.776] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.776] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.776] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 940
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 940
[2021-09-14 21:17:22.777] [info] [main.cpp:180] flush in: 1024, out: 17
[2021-09-14 21:17:22.777] [info] [main.cpp:193] Resampling succeeded. Play the output file with the command:
ffplay -f s16le -channel_layout 3 -channels 2 -ar 44100 dst.pcm
使用如下命令播放重采样后的音频文件:
ffplay -f s16le -channel_layout 3 -channels 2 -ar 44100 dst.pcm