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笔记(三) 音频处理基本概念及音频重采样的更多相关文章

  1. 音频相关基本概念,音频处理及编解码基本框架和原理以及音、重采样、3A等音频处理(了解概念为主)

    视频笔记:音频专业级分析软件(Cooledit) 音质定义以语音带宽来区分,采样率越高,带宽越大,则保真度越高,音质越好.窄带(8khz采样),宽带(16khz采样),CD音质(44.1khz采样) ...

  2. iOS音频学习笔记三:音频会话管理

    ​      使用Audio Session API ,可以指定App需要的音频行为,比如,当播放音频时,使得其他应用App静音或者混和在一起,也可以指定当App的音频被中断(例如被电话)时的行为,还 ...

  3. FFMpeg笔记(五) 录制小视频时几个问题解决

    1. YUV数据在使用avfilter scale时在特定的分辨率下UV分量不对 由于是小视频,那么分辨率不需要太高,但是有的视频源是1080p,甚至有的是4K的,所以对视频源进行scale非常有必要 ...

  4. java之jvm学习笔记三(Class文件检验器)

    java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...

  5. NumPy学习笔记 三 股票价格

    NumPy学习笔记 三 股票价格 <NumPy学习笔记>系列将记录学习NumPy过程中的动手笔记,前期的参考书是<Python数据分析基础教程 NumPy学习指南>第二版.&l ...

  6. 学习笔记(三)--->《Java 8编程官方参考教程(第9版).pdf》:第十章到十二章学习笔记

    回到顶部 注:本文声明事项. 本博文整理者:刘军 本博文出自于: <Java8 编程官方参考教程>一书 声明:1:转载请标注出处.本文不得作为商业活动.若有违本之,则本人不负法律责任.违法 ...

  7. ES6学习笔记<三> 生成器函数与yield

    为什么要把这个内容拿出来单独做一篇学习笔记? 生成器函数比较重要,相对不是很容易理解,单独做一篇笔记详细聊一聊生成器函数. 标题为什么是生成器函数与yield? 生成器函数类似其他服务器端语音中的接口 ...

  8. angular学习笔记(三十)-指令(7)-compile和link(2)

    继续上一篇:angular学习笔记(三十)-指令(7)-compile和link(1) 上一篇讲了compile函数的基本概念,接下来详细讲解compile和link的执行顺序. 看一段三个指令嵌套的 ...

  9. FFmpeg + SDL2 实现的视频播放器「视音频同步」

    文章转自:http://blog.csdn.net/i_scream_/article/details/52760033 日期:2016.10.8 作者:isshe github:github.com ...

随机推荐

  1. 【PyQt5 学习记录】009:批量创建组件并查找

    这里需要在创建组件时使用 setObjectName() 为其添加 objectName 属性,再通过 findChild() 或者 findChildren() 函数来查找. 举个栗子,批量创建10 ...

  2. FineReport中如何用JavaScript解决控件值刷新不及时

    我们经常利用按钮进行一些页面值的处理工作,但是默认的逻辑造成,每次新填报的值,需要点击下空白区域或是执行某个其他操作才可以被正确读取,那么我们如何处理呢? 例:当我们用常规取值的时候,虽然B3单元格录 ...

  3. ExpandableListView控件实现二级列表

    效果图如下: 二级列表附有点击事件. 1.布局文件: 此处加了一个自定义的导航RelativeLayout,记得注activity的时候添加 android:theme="@style/Th ...

  4. Object equals 方法

    package com.mydemo.controller; public class TestEquals { public static void main(String[] args) { Do ...

  5. leetCode题解之First Missing Positive

    1.问题描述 2.题解思路 本题的思路是对于数组中每个正的元素,应该将其放到数组中对应的位置,比如元素1 ,应该放在数组的第一个位置.以此类推,最后检查数组中元素值和下标不匹配的情况. 3.代码 in ...

  6. SSL Certificates深入理解

    http://www.littlewhitedog.com/content-71.html https://www.verisign.com/en_US/website-presence/websit ...

  7. 【Redis】命令学习笔记——字符串(String)(23个超全字典版)

    Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合). 本篇基于redis 4.0.11版本,学习字符串( ...

  8. 关于easyUI一些标签的使用

    ①table: 1.class="easyui-datagrid":指定为easyUI的表格布局 2.pagination="true":带分页的表格 3.ro ...

  9. Mysql使用binlog恢复数据解决误操作问题的两种方法

    为保证没有其他参数配置影响,重新安装配置了一台最小化安装的CentOS7虚拟机 1. 基础知识
 安装mysql5.6数据库Mysql binlog初步理解 2. 配置mysql 开启binlog.修 ...

  10. 更新tableView的某个cell

    更新tableView的某个cell 异步加载完数据后更新某个cell,这应该是非常常见的使用方法了,我们经常会用reloadData. 效果: 源码: // // RootViewControlle ...