文章目录

  • 为什么要重采样
  • 完整代码
  • 运行结果

为什么要重采样

从设备采集的音频数据与编码器要求的数据不一致
扬声器要求的音频数据与要播放的音频数据不一致
更方便运算(回音消除须使用单声道,需要先转换)

比如说语音识别,需要很低的采样率就可以了,高了增加了数据量,毫无用处,这时候就需要进行音频重采样,重采样可以改变音频采样值或采样格式。

完整代码

代码参考了FFmpeg示例,利用 fill_samples() 函数生成正弦波音频数据,然后,实现将48000采样值转换成44100的功能。代码一开始做了最基本的初始化,然后分配空间,计算采样值,最后进行采样值的转换,将转换后的数据写入本地文件。代码中的关键函数是 swr_convert(),采样值的转换就是靠他完成。
以下代码在Qt5.14.0中验证OK,代码如下:

#ifdef __cplusplus
extern "C"
{#endif
#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.h>
#ifdef __cplusplus
}
#endif#define IN_RATE  48000  // 输入采样率
#define OUT_RATE 44100  // 输出采样率// 获取采样格式
static int get_format_from_sample_fmt(const char **fmt,enum AVSampleFormat sample_fmt)
{char *lettle_end = NULL;char *big_end    = NULL;switch ((int)sample_fmt) {  // 根据枚值获取音频编码格式字符串case AV_SAMPLE_FMT_U8:  big_end = "u8";    lettle_end = "u8";    break; // 大端模式和小端模式case AV_SAMPLE_FMT_S16: big_end = "s16be"; lettle_end = "s16le"; break;case AV_SAMPLE_FMT_S32: big_end = "s32be"; lettle_end = "s32le"; break;case AV_SAMPLE_FMT_FLT: big_end = "f32be"; lettle_end = "f32le"; break;case AV_SAMPLE_FMT_DBL: big_end = "f64be"; lettle_end = "f64le"; break;default: return AVERROR(EINVAL);}*fmt = AV_NE(big_end, lettle_end);  // 一般计算机都是小端模式,宏展开为 *fmt = lettle_endreturn 0;
}// 交错模式生成输入源
static void fill_samples(double *dst, int nb_samples, int nb_channels, int sample_rate, double *t)
{int i, j;double tincr = 1.0 / sample_rate, *dstp = dst;const double c = 2 * M_PI * 440.0; // 440Hz// 产生440Hz重复通道的正弦信号for (i = 0; i < nb_samples; i++) {*dstp = sin(c * *t);for (j = 1; j < nb_channels; j++)dstp[j] = dstp[0];dstp += nb_channels;*t += tincr;}
}int main()
{// 输入参数struct SwrContext   *swr_ctx        = swr_alloc();               // 重采样上下文enum AVSampleFormat src_sample_fmt  = AV_SAMPLE_FMT_DBL;         // 交错采样模式enum AVSampleFormat dst_sample_fmt  = AV_SAMPLE_FMT_S16;         // 有符号16位uint8_t             **src_data      = NULL;                      // 输入音频数据缓冲区uint8_t             **dst_data      = NULL;                      // 输出音频数据缓冲区int                 src_nb_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO); // 输入通道数int                 dst_nb_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO); // 输出通道数int                 src_linesize;                                // 输入音频行数据大小,取决于声道数和采样格式是否是交错模式int                 dst_linesize;                                // 输出音频行数据大小int                 src_nb_samples  = 1024;                      // 输入固定按1024个采样点int                 dst_nb_samples;                              // 当前输出数据包的真实采样数量int                 max_dst_nb_samples;                          // 最大的采样数量int                 dst_bufsize;                                 // 当前数据包采样值大小const char          *dst_filename   = "out.pcm";                 // 保存输出的pcm到本地,然后播放验证FILE                *dst_file       = fopen(dst_filename, "wb");const char          *fmt;double              t = 0.0;int                 ret = 0;if (!dst_file) { exit(1);  } // 输出文件打开失败if (!swr_ctx)  { goto end; } // 重采样上下文创建失败// 输入参数av_opt_set_int(swr_ctx, "in_channel_layout",  AV_CH_LAYOUT_STEREO, 0); // 输入立体声av_opt_set_int(swr_ctx, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); // 输出立体声av_opt_set_int(swr_ctx, "in_sample_rate",     IN_RATE, 0);             // 输入采样率48000av_opt_set_int(swr_ctx, "out_sample_rate",    OUT_RATE, 0);            // 输出采样率44100av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt",  src_sample_fmt, 0);av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0);// 初始化重采样if (swr_init(swr_ctx) < 0) { goto end; }// 给输入源分配内存空间ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize, src_nb_channels, src_nb_samples, src_sample_fmt, 0);if (ret < 0) { goto end; }// 计算输出采样数量max_dst_nb_samples = dst_nb_samples = av_rescale_rnd(src_nb_samples, OUT_RATE, IN_RATE, AV_ROUND_UP);// 分配输出缓存内存ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, dst_nb_channels, dst_nb_samples, dst_sample_fmt, 0);if (ret < 0) { goto end; }do {fill_samples((double *)src_data[0], src_nb_samples, src_nb_channels, IN_RATE, &t); // 生成输入源// 计算采样数量int64_t delay = swr_get_delay(swr_ctx, IN_RATE); // swr_convert可能有没转换的数据,这里需要获取缓存// 计算新的采样数量 dst_nb_samples = (delay + src_nb_samples) * OUT_RATE / IN_RATE,结果向上取整dst_nb_samples = av_rescale_rnd(delay + src_nb_samples, OUT_RATE, IN_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; // 更新最大采样数量}// 音频重采样if ((ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)src_data, src_nb_samples)) < 0) { goto end; }if ((dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels, ret, dst_sample_fmt, 1)) < 0) { goto end; }printf("t:%f in:%d out:%d\n", t, src_nb_samples, ret);fwrite(dst_data[0], 1, dst_bufsize, dst_file);  // 将pcm数据写入本地文件} while (t < 10);// ========== 刷新最后几个样本 ==========if (swr_convert(swr_ctx, dst_data, dst_nb_samples, NULL, 0) < 0) { goto end; }if ((dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels, ret, dst_sample_fmt, 1)) < 0) { goto end; }printf("flush in:%d out:%d\n", 0, ret);  // 最后有几个样本,非常小fwrite(dst_data[0], 1, dst_bufsize, dst_file);  // 将最后一点pcm数据写入本地文件if (get_format_from_sample_fmt(&fmt, dst_sample_fmt) < 0) { goto end; }fprintf(stderr, "ffplay -f %s -channel_layout %"PRId64" -channels %d -ar %d %s\n",   // 转换成功,打印音频播放命令fmt, (int64_t)AV_CH_LAYOUT_STEREO, dst_nb_channels, OUT_RATE, dst_filename);end:  // 结束并释放空间fclose(dst_file);if (src_data)av_freep(&src_data[0]);av_freep(&src_data);if (dst_data)av_freep(&dst_data[0]);av_freep(&dst_data);swr_free(&swr_ctx);return 0;
}

运行结果

编译运行后会在输出目录看到一个out.pcm文件,使用ffplay -f s16le -channel_layout 3 -channels 2 -ar 44100 out.pcm命令即可播放,声音为一个连续的但音调。

FFmpeg系列(五)—— 音频重采样相关推荐

  1. 音视频从入门到精通——FFmpeg之swr_convert音频重采样函数分析

    文章目录 音频重采样 swr_alloc函数 swr_alloc_set_opts函数 swr_init函数 swr_convert函数 音频基础 音频开发主要应用有 音频开发具体内容有 音频应用的难 ...

  2. iOS 音视频开发:Audio Unit播放FFmpeg解码的音频

    本文档描述了iOS播放经FFmpeg解码的音频数据的编程步骤,具体基于Audio Toolbox框架的Audio Session和Audio Unit框架提供的接口实现.在iOS 7及以上平台Audi ...

  3. Android音视频学习系列(五) — 掌握音频基础知识并使用AudioTrack、OpenSL ES渲染PCM数据

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  4. 【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )

    文章目录 I . FFMPEG 播放视频流程 II . FFMPEG 音频重采样流程 III . FFMPEG 音频重采样 IV . FFMPEG 初始化音频重采样上下文 SwrContext V . ...

  5. 音频重采样ffmpeg(九)

    前言 广义的音频重采样包括: 1.采样格式转化:比如采样格式从16位整形变为浮点型 2.采样率的转换:降采样和升采样,比如44100采样率降为2000采样率 3.存放方式转化:音频数据从packet方 ...

  6. 【ffmpeg】音频重采样

    [ffmpeg]音频重采样 前言 什么是音频重采样 实现音频重采样 创建重采样上下文 初始化重采样 进行重采样 ffplay播放 参考资料 个人简介

  7. ffmpeg音频重采样

    音频重采样 重采样就是改变音频的采样率.sample format(采样格式).声道数(channel)等参数 为什么需要重采样? 1.音频重采样是为了将音频统一格式 比如直播: 麦克风采集 44.1 ...

  8. FFmpeg开发(五)——Qt视频播放器之封装FFmpeg类(参考了暴风影音、迅雷影音)

    FFmpeg开发(五)--Qt视频播放器之封装FFmpeg类(参考了暴风影音.迅雷影音) 上一篇介绍了,使用Qt和FFmpeg写的播放器.页面大家可以点进去查看和下载. FFmpeg开发(四)--Qt ...

  9. 音频通道数、采样频率、采样位数、采样个数(样本数)的概念及计算一帧音频的大小、每秒播放的音频字节大小、一帧的播放时长、音频重采样

    对于下面data和linesize的解释(参考下面3.4中的av_samples_alloc_array_and_samples函数说明): 1)data是通道的意思,例如双通道,data[0]代表左 ...

最新文章

  1. Spark2.1.0分布式集群安装
  2. svn 403 Forbidden
  3. HTTP和HTTPS协议及工作原理分析
  4. 没有计算机的一天英语作文带翻译,初一英语作文我的一天带翻译
  5. 原生js实现一个随机点餐的小效果
  6. STM32电机库5.4开源注释单电阻霍尔 有感霍尔读取电角度 单电阻采样
  7. SeaweedFS基本介绍
  8. kodi资源_Kodi——支持全平台的看片神器
  9. 初探:使用Jest进行React单元测试
  10. 第三章:密码学基本理论
  11. html文件如何创建文件,怎么创建html文件
  12. java web 下拉列表_关于web中下拉列表的几种实现方法
  13. swagger 2.9.2
  14. 数据可视化echarts介绍
  15. UC浏览器不让访问网站的解决办法
  16. excel(排序、合并单元格、合并后求和)
  17. 三星S5PV210的启动
  18. 每一刻都是全新的自己
  19. anki公司即将出品Vector家庭机器人(正在预售)
  20. 咖啡汪日志——JAVA导出pdf文件加水印 文字+图片、文字

热门文章

  1. 【分享】一个查看无线网络密钥的小方法(查看 wife密码,热点密码)
  2. HTML图像和超链接及文字颜色与排版
  3. 好用的电脑备份软件推荐
  4. 手机通讯录、联系人的备份、恢复经验
  5. NBS-Predict:基于脑网络的机器学习预测
  6. 我为SEO代言,屌丝为我代言
  7. Geany的所有主题文件
  8. SAS初学者笔记---003---利用数据步读取数据--逻辑库
  9. (转)TensorFlow--实现人脸识别实验精讲 (Face Recognition using Tensorflow)
  10. 线性代数之从线性方程组看线性组合