FFMpeg笔记(三) 音频处理基本概念及音频重采样
Android放音的采样率固定为44.1KHz,录音的采样率固定为8KHz,因此底层的音频设备驱动需要设置好这两个固定的采样率。如果上层传过来的采样率不符的话,需要进行resample重采样处理。
几个名词:
1. 采样率
采样设备每秒抽取样本的次数
2. 音频格式及量化精度(位宽)
每种音频格式有不同的量化精度(位宽),位数越多,表示值就越精确,声音表现自然就越精准。FFMpeg中音频格式有以下几种,每种格式有其占用的字节数信息:
enum AVSampleFormat {
AV_SAMPLE_FMT_NONE = -,
AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
AV_SAMPLE_FMT_S16, ///< signed 16 bits
AV_SAMPLE_FMT_S32, ///< signed 32 bits
AV_SAMPLE_FMT_FLT, ///< float
AV_SAMPLE_FMT_DBL, ///< double
AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP, ///< float, planar
AV_SAMPLE_FMT_DBLP, ///< double, planar
AV_SAMPLE_FMT_S64, ///< signed 64 bits
AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar
AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
};
3. 分片(plane)和打包(packed)
以双声道为例,带P(plane)的数据格式在存储时,其左声道和右声道的数据是分开存储的,左声道的数据存储在data[0],右声道的数据存储在data[1],每个声道的所占用的字节数为linesize[0]和linesize[1];
不带P(packed)的音频数据在存储时,是按照LRLRLR...的格式交替存储在data[0]中,linesize[0]表示总的数据量。
4. 声道分布(channel_layout)
声道分布在FFmpeg\libavutil\channel_layout.h中有定义,一般来说用的比较多的是AV_CH_LAYOUT_STEREO(双声道)和AV_CH_LAYOUT_SURROUND(三声道),这两者的定义如下:
#define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
#define AV_CH_LAYOUT_SURROUND (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)
5. 音频帧的数据量计算
一帧音频的数据量=channel数 * nb_samples样本数 * 每个样本占用的字节数
如果该音频帧是FLTP格式的PCM数据,包含1024个样本,双声道,那么该音频帧包含的音频数据量是2*1024*4=8192字节。
6. 音频播放时间计算
以采样率44100Hz来计算,每秒44100个sample,而正常一帧为1024个sample,可知每帧播放时间/1024=1000ms/44100,得到每帧播放时间=1024*1000/44100=23.2ms。
7. 音频重采样(resample)
FFMpeg自带的resample例子:FFmpeg\doc\examples\resampling_audio.c,这里把最核心的resample代码贴一下,在工程中使用时,注意设置的各种参数,给定的输入数据都不能错。
int main(int argc, char **argv)
{
// 设置数据源src和dst声道布局
int64_t src_ch_layout = AV_CH_LAYOUT_STEREO, dst_ch_layout = AV_CH_LAYOUT_SURROUND;
// 设置src和dst采样率
int src_rate = , dst_rate = ;
uint8_t **src_data = NULL, **dst_data = NULL;
int src_nb_channels = , dst_nb_channels = ;
int src_linesize, dst_linesize;
int src_nb_samples = , dst_nb_samples, max_dst_nb_samples;
// 设置src和dst音频格式
enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_DBL, dst_sample_fmt = AV_SAMPLE_FMT_S16;
const char *dst_filename = NULL;
FILE *dst_file;
int dst_bufsize;
const char *fmt;
// 重采样上下文,包含resample信息
struct SwrContext *swr_ctx;
double t;
int ret; if (argc != ) {
fprintf(stderr, "Usage: %s output_file\n"
"API example program to show how to resample an audio stream with libswresample.\n"
"This program generates a series of audio frames, resamples them to a specified "
"output format and rate and saves them to an output file named output_file.\n",
argv[]);
exit();
}
// resample后的数据保存到本地文件
dst_filename = argv[]; dst_file = fopen(dst_filename, "wb");
if (!dst_file) {
fprintf(stderr, "Could not open destination file %s\n", dst_filename);
exit();
} /* create resampler context */
swr_ctx = swr_alloc();
if (!swr_ctx) {
fprintf(stderr, "Could not allocate resampler context\n");
ret = AVERROR(ENOMEM);
goto end;
} /* set options */
// 将resample信息写入resample上下文
av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, );
av_opt_set_int(swr_ctx, "in_sample_rate", src_rate, );
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_sample_fmt, ); av_opt_set_int(swr_ctx, "out_channel_layout", dst_ch_layout, );
av_opt_set_int(swr_ctx, "out_sample_rate", dst_rate, );
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, ); /* initialize the resampling context */
if ((ret = swr_init(swr_ctx)) < ) {
fprintf(stderr, "Failed to initialize the resampling context\n");
goto end;
} /* allocate source and destination samples buffers */ src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout);
ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize, src_nb_channels,
src_nb_samples, src_sample_fmt, );
if (ret < ) {
fprintf(stderr, "Could not allocate source samples\n");
goto end;
} /* compute the number of converted samples: buffering is avoided
* ensuring that the output buffer will contain at least all the
* converted input samples */
max_dst_nb_samples = dst_nb_samples =
av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP); /* buffer is going to be directly written to a rawaudio file, no alignment */
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, );
if (ret < ) {
fprintf(stderr, "Could not allocate destination samples\n");
goto end;
} t = ;
do {
/* generate synthetic audio */
// 这里是自行生成源数据帧,实际工程中应该将解码后的PCM数据填入src_data中
fill_samples((double *)src_data[], src_nb_samples, src_nb_channels, src_rate, &t); /* compute destination number of samples */
dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, src_rate) +
src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
if (dst_nb_samples > max_dst_nb_samples) {
av_freep(&dst_data[]);
ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,
dst_nb_samples, dst_sample_fmt, );
if (ret < )
break;
max_dst_nb_samples = dst_nb_samples;
} /* convert to destination format */
// 重采样操作
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)src_data, src_nb_samples);
if (ret < ) {
fprintf(stderr, "Error while converting\n");
goto end;
}
dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,
ret, dst_sample_fmt, );
if (dst_bufsize < ) {
fprintf(stderr, "Could not get sample buffer size\n");
goto end;
}
printf("t:%f in:%d out:%d\n", t, src_nb_samples, ret);
fwrite(dst_data[], , dst_bufsize, dst_file);
} while (t < ); if ((ret = get_format_from_sample_fmt(&fmt, dst_sample_fmt)) < )
goto end;
fprintf(stderr, "Resampling succeeded. Play the output file with the command:\n"
"ffplay -f %s -channel_layout %"PRId64" -channels %d -ar %d %s\n",
fmt, dst_ch_layout, dst_nb_channels, dst_rate, dst_filename); end:
fclose(dst_file); if (src_data)
av_freep(&src_data[]);
av_freep(&src_data); if (dst_data)
av_freep(&dst_data[]);
av_freep(&dst_data); swr_free(&swr_ctx);
return ret < ;
}
FFMpeg笔记(三) 音频处理基本概念及音频重采样的更多相关文章
- 音频相关基本概念,音频处理及编解码基本框架和原理以及音、重采样、3A等音频处理(了解概念为主)
视频笔记:音频专业级分析软件(Cooledit) 音质定义以语音带宽来区分,采样率越高,带宽越大,则保真度越高,音质越好.窄带(8khz采样),宽带(16khz采样),CD音质(44.1khz采样) ...
- iOS音频学习笔记三:音频会话管理
使用Audio Session API ,可以指定App需要的音频行为,比如,当播放音频时,使得其他应用App静音或者混和在一起,也可以指定当App的音频被中断(例如被电话)时的行为,还 ...
- FFMpeg笔记(五) 录制小视频时几个问题解决
1. YUV数据在使用avfilter scale时在特定的分辨率下UV分量不对 由于是小视频,那么分辨率不需要太高,但是有的视频源是1080p,甚至有的是4K的,所以对视频源进行scale非常有必要 ...
- java之jvm学习笔记三(Class文件检验器)
java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...
- NumPy学习笔记 三 股票价格
NumPy学习笔记 三 股票价格 <NumPy学习笔记>系列将记录学习NumPy过程中的动手笔记,前期的参考书是<Python数据分析基础教程 NumPy学习指南>第二版.&l ...
- 学习笔记(三)--->《Java 8编程官方参考教程(第9版).pdf》:第十章到十二章学习笔记
回到顶部 注:本文声明事项. 本博文整理者:刘军 本博文出自于: <Java8 编程官方参考教程>一书 声明:1:转载请标注出处.本文不得作为商业活动.若有违本之,则本人不负法律责任.违法 ...
- ES6学习笔记<三> 生成器函数与yield
为什么要把这个内容拿出来单独做一篇学习笔记? 生成器函数比较重要,相对不是很容易理解,单独做一篇笔记详细聊一聊生成器函数. 标题为什么是生成器函数与yield? 生成器函数类似其他服务器端语音中的接口 ...
- angular学习笔记(三十)-指令(7)-compile和link(2)
继续上一篇:angular学习笔记(三十)-指令(7)-compile和link(1) 上一篇讲了compile函数的基本概念,接下来详细讲解compile和link的执行顺序. 看一段三个指令嵌套的 ...
- FFmpeg + SDL2 实现的视频播放器「视音频同步」
文章转自:http://blog.csdn.net/i_scream_/article/details/52760033 日期:2016.10.8 作者:isshe github:github.com ...
随机推荐
- Excel2010取消隐藏的工作簿
背景 Excel 2010文件,其中包含针对业务需要涉及的计算器等,其中一个Worksheet用于存放计算器用到的常量,针对业务人员(即此Excel文件的用户)是隐藏的,并有密码保护. 现象 因业务变 ...
- react解决roadhog buildDll 【转】
本地删了 node module 目录,重新安装的时候,提示 找了找,可如下解决 ------- 转自: https://www.cnblogs.com/huhanhaha/p/7605722.htm ...
- 在RecyclerView列表滚动的时候显示或者隐藏Toolbar
先看一下效果: 本文将讲解如何实现类似于Google+应用中,当列表滚动的时候,ToolBar(以及悬浮操作按钮)的显示与隐藏(向下滚动隐藏,向上滚动显示),这种效果在Material Design ...
- 【日常记录】【unity3d】 获取手柄轴的输入
参考 https://blogs.msdn.microsoft.com/nathalievangelist/2014/12/16/joystick-input-in-unity-using-xbox3 ...
- 解决国外模板h1、h2、h3...不显示中文文章标题的问题
如果你经常用国外好看的网页模版时候,会遇到不显示中文文章标题的情况,显示英文标题却正常.遇到这个情况很多人认为应该修改CSS的font-family的字体,其实这是错误的,与CSS无关. 出现这种情况 ...
- npm私有仓库搭建
背景 Node.js开发本地项目,有时不同项目之间存在依赖,如果不想把项目发布到npm社区的仓库,则需要有自己本地的仓库. 有些公司采用的是内网开发,很多npm资源无法从内网去下载. sinopia( ...
- layui和bootstrap对比
layui和bootstrap 对比 这两个都属于UI渲染框架. layui是国人开发的一套框架,2016年出来的,现在已更新到2.X版本了.比较新,轻量级,样式简单好看. bootstrap 相对来 ...
- flask的g对象
故名思议我们可以理解这个g对象是一个全局的对象,这个对象存储的是我们这一次请求的所有的信息,只是存储这一次的请求 g:global 1. g对象是专门用来保存用户的数据的. 2. g对象在一次请求中 ...
- [翻译] SCViewShaker
SCViewShaker https://github.com/rFlex/SCViewShaker About A highly configurable UIView category for s ...
- [翻译] DoImagePickerController
DoImagePickerController An image picker controller with single selection and multiple selection. Sup ...