音视频开发23 FFmpeg 音频重采样

1.重采样

1.1 为什么要重采样?

为什么要重采样?当然是原有的⾳频参数不满⾜我们的需求。
⽐如在FFmpeg解码⾳频的时候,不同的⾳源有不同的格式,采样率等,在解码后的数据中的这些参数也会不⼀致。
(最新FFmpeg 解码⾳频后,⾳频格 式为AV_SAMPLE_FMT_FLTP,这个参数应该是⼀致的),
如果我们接下来需要使⽤解码后的⾳频数据做 其他操作,⽽这些参数的不⼀致导致会有很多额外⼯作,此时直接对其进⾏重采样,获取我们制定的⾳频 参数,这样就会⽅便很多。
再⽐如在将⾳频进⾏SDL播放时候,因为当前的SDL2.0不⽀持planar格式,也不⽀持浮点型的,⽽最新的 FFMPEG 16年会将⾳频解码为AV_SAMPLE_FMT_FLTP格式,因此此时就需要我们对其重采样,使之可 以在SDL2.0上进⾏播放。

1.2 什么是重采样

所谓的重采样,就是改变⾳频的 采样率、sample format、声道数 等参数,使之按照我们期望的参数输出。这里的改变是指 只要 上述三者的其中一个变化,就叫做重采样。

1.3 可调节的参数

通过重采样,我们可以对:
1. sample rate(采样率)
2. sample format(采样格式)
3. channel layout(通道布局,可以通过此参数获取声道数

2 对应参数解析

2.1 采样率

采样设备每秒抽取样本的次数  例如48000,44100,对应的是 AVFrame 结构体中的 sample_rate

2.2 采样格式及量化精度(位宽)

每种⾳频格式有不同的量化精度(位宽),位数越多,表示值就越精确,声⾳表现⾃然就越精准。
FFMpeg中⾳频格式有以下⼏种,每种格式有其占⽤的字节数信息(libavutil/samplefmt.h):
enum AVSampleFormat {AV_SAMPLE_FMT_NONE = -1,AV_SAMPLE_FMT_U8,          ///< unsigned 8 bitsAV_SAMPLE_FMT_S16,         ///< signed 16 bitsAV_SAMPLE_FMT_S32,         ///< signed 32 bitsAV_SAMPLE_FMT_FLT,         ///< floatAV_SAMPLE_FMT_DBL,         ///< doubleAV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planarAV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planarAV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planarAV_SAMPLE_FMT_FLTP,        ///< float, planarAV_SAMPLE_FMT_DBLP,        ///< double, planarAV_SAMPLE_FMT_S64,         ///< signed 64 bitsAV_SAMPLE_FMT_S64P,        ///< signed 64 bits, planarAV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
};

2.3 平面模式和交错模式 - 分⽚(plane)和打包(packed

以双声道为例,
带P(plane)的数据格式在存储时,其左声道和右声道的数据是分开存储的,左声道的 数据存储在data[0],右声道的数据存储在data[1],每个声道的所占⽤的字节数为linesize[0]和 linesize[1];
不带P(packed)的⾳频数据在存储时,是按照LRLRLR...的格式交替存储在data[0]中,linesize[0]
表示总的数据量。

2.4 声道分布(channel_layout)

声道分布在FFmpeg\libavutil\channel_layout.h中有定义,⼀般来说⽤的⽐较多的是
AV_CH_LAYOUT_MONO  (单声道)            
AV_CH_LAYOUT_STEREO(双声道)
AV_CH_LAYOUT_SURROUND(三声道)
#define AV_CH_LAYOUT_MONO              (AV_CH_FRONT_CENTER)
#define AV_CH_LAYOUT_STEREO            (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
#define AV_CH_LAYOUT_2POINT1           (AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY)
#define AV_CH_LAYOUT_2_1               (AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER)
#define AV_CH_LAYOUT_SURROUND          (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)

2.5 ⾳频帧的数据量计算

⼀帧⾳频的数据量(字节)= channel数 * 每个channel的样本数 * 每个样本占⽤的字节数
对应从AVFrame 中的数据为
avframe->ch_layout.nb_channels * avframe->nb_samples * av_get_bytes_per_sample((AVSampleFormat)avframe->format);
如果该⾳频帧是FLTP格式的PCM数据,包含1024个样本,双声道,那么该⾳频帧包含的⾳频数据量是  2*1024*4=8192字节。
FLTP 对应的数据大小可以通过 ffmpeg -sample_fmts 查看:
u8        8
s16      16
s32      32
flt      32
dbl      64
u8p       8
s16p     16
s32p     32
fltp     32
dblp     64
s64      64
s64p     64

如果是  AV_SAMPLE_FMT_DBL ,大小为: 2*1024*8 = 16384

2.6 ⾳频播放时间计算

以采样率44100Hz来计算,也就是每秒44100个样本,
因此 播放一个样本的时间为 1÷44100 秒 。
⽽aac正常⼀帧为1024个sample,可知aac每帧播放时间:
1÷44100 x 1024 秒 = 1÷44100 x 1024 x 1000 毫秒 = 23.21995464852608毫秒
即:⼀帧播放时间(毫秒) = nb_samples样本数 *1000 ÷ 采样率
(1)1024*1000/44100=23.21995464852608ms ->约等于 23 .2ms,精度损失了
0.011995464852608ms,如果累计10万帧,误差>1199毫秒,如果有视频⼀起的就会有⾳视频同步的问 题。 如果按着23.2去计算pts(0 23.2 46.4 )就会有累积误差。
(2)1024*1000/48000= 21.3 3333333333333ms

3 FFmpeg重采样API

3.1 分配⾳频重采样的上下⽂

struct SwrContext *swr _alloc (void);
/*** Allocate SwrContext.** If you use this function you will need to set the parameters (manually or* with swr_alloc_set_opts2()) before calling swr_init().** @see swr_alloc_set_opts2(), swr_init(), swr_free()* @return NULL on error, allocated context otherwise*/
struct SwrContext *swr_alloc(void);

3.2 给音频重采样上下文设置参数

    /* set options */// 输入参数int64_t src_ch_layout = AV_CH_LAYOUT_STEREO;int src_rate = 48000;enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_DBL;// 设置输入参数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);// 输出参数int64_t dst_ch_layout = AV_CH_LAYOUT_STEREO;int dst_rate = 44100;enum AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;// 设置输出参数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);

3.1.2 上述两步可以直接弄成一步

/*** Allocate SwrContext if needed and set/reset common parameters.** This function does not require *ps to be allocated with swr_alloc(). On the* other hand, swr_alloc() can use swr_alloc_set_opts2() to set the parameters* on the allocated context.** @param ps              Pointer to an existing Swr context if available, or to NULL if not.*                        On success, *ps will be set to the allocated context.* @param out_ch_layout   output channel layout (e.g. AV_CHANNEL_LAYOUT_*)* @param out_sample_fmt  output sample format (AV_SAMPLE_FMT_*).* @param out_sample_rate output sample rate (frequency in Hz)* @param in_ch_layout    input channel layout (e.g. AV_CHANNEL_LAYOUT_*)* @param in_sample_fmt   input sample format (AV_SAMPLE_FMT_*).* @param in_sample_rate  input sample rate (frequency in Hz)* @param log_offset      logging level offset* @param log_ctx         parent logging context, can be NULL** @see swr_init(), swr_free()* @return 0 on success, a negative AVERROR code on error.*         On error, the Swr context is freed and *ps set to NULL.*/
int swr_alloc_set_opts2(struct SwrContext **ps,const AVChannelLayout *out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,const AVChannelLayout *in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,int log_offset, void *log_ctx);

3.3 当设置好相关的参数后,使⽤此函数来初始化SwrContext结构体

int swr_init(struct SwrContext *s);
 * Initialize context after user parameters have been set.* @note The context must be configured using the AVOption API.** @see av_opt_set_int()* @see av_opt_set_dict()** @param[in,out]   s Swr context to initialize* @return AVERROR error code in case of failure.*/
int swr_init(struct SwrContext *s);

3.4 这时候理论上是就要通过SwrContext  转化了,那么这里就有一个问题了,转化的数据应该放在哪里呢?--- 因此这一步 是 创建输入缓冲区

如何创建这个输入缓冲区呢?又根据哪些参数创建这个输入缓冲区呢?

很显然,输入缓冲区是要根据 输入的音频的三要素 来创建的。创建出来的缓冲区放在哪里呢?

            int av_samples_alloc_array_and_samples(uint8_t ***audio_data,
                                int *linesize,
                                int nb_channels,
                                int nb_samples,
                                enum AVSampleFormat sample_fmt,
                                int align);

第一个参数audio_data为:输入缓冲区的首地址,是个三级指针,本质上是对于 一个二级指针的 取地址,out参数

这里要说明一下为什么 audio_data 是个三级指针,首先是一个输出参数,那么意味着,我们传递进来的要改动的就是二级指针,这个二级指针确切来说应该是一个 uint8_t * audiodata[8], 每一个audiodata[i] 都是指向的 每个planar的具体数据。实际上这里就是为了兼容planar才弄了个三级指针。如果不考虑planar 的,二级指针就够了。

第二个参数linesize为:输入缓冲区对齐的音频缓冲区大小,可能为 NULL,out参数

第三个参数nb_channels为:要改动的输入源的 声道数

第四个参数nb_samples为:输入源每个声道的样本数,aac 为1024

第五个参数sample_fmt为:输入源的AVSampleFormat -- 类似AV_SAMPLE_FMT_DBL

第六个参数align为:是否要字节对齐,0为对齐,1为不对齐,一般都要对齐

/*** Allocate a samples buffer for nb_samples samples, and fill data pointers and* linesize accordingly.* The allocated samples buffer can be freed by using av_freep(&audio_data[0])* Allocated data will be initialized to silence.** @see enum AVSampleFormat* The documentation for AVSampleFormat describes the data layout.** @param[out] audio_data  array to be filled with the pointer for each channel* @param[out] linesize    aligned size for audio buffer(s), may be NULL* @param nb_channels      number of audio channels* @param nb_samples       number of samples per channel* @param sample_fmt       the sample format* @param align            buffer size alignment (0 = default, 1 = no alignment)* @return                 >=0 on success or a negative error code on failure* @todo return the size of the allocated buffer in case of success at the next bump* @see av_samples_fill_arrays()* @see av_samples_alloc_array_and_samples()*/
int av_samples_alloc(uint8_t **audio_data, int *linesize, int nb_channels,int nb_samples, enum AVSampleFormat sample_fmt, int align);/*** Allocate a data pointers array, samples buffer for nb_samples* samples, and fill data pointers and linesize accordingly.** This is the same as av_samples_alloc(), but also allocates the data* pointers array.** @see av_samples_alloc()*/
int av_samples_alloc_array_and_samples(uint8_t ***audio_data, int *linesize, int nb_channels,int nb_samples, enum AVSampleFormat sample_fmt, int align);

3.5 根据 输入缓冲区的样本数量 计算 缓冲区的样本数量 --创建输出缓冲区

3.6 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/849489.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

外贸自动化脚本编写会用到的源代码!

随着全球化的加速推进&#xff0c;外贸行业正迎来前所未有的发展机遇&#xff0c;为了提高工作效率、减少人为错误&#xff0c;并更好地把握市场机遇&#xff0c;越来越多的外贸企业开始关注自动化脚本的编写与应用。 自动化脚本不仅可以帮助企业实现业务流程的自动化&#xf…

语言模型解构——手搓BPE算法

1. BPE简介 BPE 的全称是 Byte Pair Encoding&#xff0c;原本是一种数据压缩算法&#xff0c;现已被广泛应用于自然语言处理中的分词任务。它通过统计高频字符序列来构建词表&#xff0c;并将词汇拆分为更小的、可重用的子词单元&#xff0c;例如&#xff1a;highest-> [h…

凸函数的局部最优也是全局最优的证明

这个性质早就知道了&#xff0c;但并不太清楚严谨的证明是什么。这也是《Introduction to linear optimization》书中第三章课后题的第一题。这篇博客给出严谨的证明。 Exercise 3.1 (Local minimum of convex functions) Let f : R n → R f: \mathcal{R}^n \rightarrow \m…

css 清除伪类active,hover效果

来源 :hover伪类可以让我们设置鼠标移入时的样式。 鼠标移入并选择后&#xff0c;我们就不想让这个元素触发active以及hover的效果该怎么整呢&#xff1f; 解决 使用pointer-events:none: 注意&#xff1a;pointer-event会阻挡所有的事件&#xff0c;包括js的click之类的事…

View 关于文本框可以输入e,表单验证不正确的问题

【问题原因】&#xff1a;使用的组件为<input typenumber/>&#xff0c;这个时候按道理只能输入数字&#xff0c;但是字母e确可以确认&#xff0c;原因是e代表数字2.71828&#xff0c;他也是个数字&#xff0c;所以能输入 【解决方案】&#xff1a; 1、使用组件<Input…

派单软件,改变服务业未来的神秘武器!

随着人们生活质量的提升&#xff0c;对于日常生活、工作中的售后维修服务响应时间、服务质量十分的在意。即使现在信息化时代快速发展&#xff0c;但还是有不少人们面临着以下问题。 你是否曾经因为等待维修服务而焦急万分&#xff1f; 你是否曾经因为繁琐的报修流程而倍感烦恼…

苍穹外卖笔记-08-套餐管理-增加,删除,修改,查询和起售停售套餐(上)

套餐管理 1 任务2 新增套餐2.1 需求分析和设计页面原型和业务规则接口设计setmeal和setmeal_dish表设计 2.2 代码开发2.2.1 根据分类id查询菜品DishControllerDishServiceDishServiceImplDishMapperDishMapper.xml 2.2.2 新增套餐接口SetmealControllerSetmealServiceSetmealSe…

【自定义View】Android圆饼进度条

源码 自定义属性 <?xml version"1.0" encoding"utf-8"?> <resources><declare-styleable name"ArcProgressView"><attr name"android:textSize" /><attr name"bgBorderWidth" format"d…

计算机毕业设计基于YOLOv8的头盔检测系统

1、安装Anaconda 官网下载或者哔哩哔哩有的up分享 https://www.anaconda.com/download 版本无所谓&#xff0c;安装位置不要有中文就行 2、创建环境yolov8 winR打开命令行 conda create -n yolov8 python3.9 3、打开源码 下载下来放到你想放的目录&#xff0c;直接用pyCharm或者…

【香橙派】Orange Pi AIpro体验——国产AI赋能

文章目录 &#x1f354;开箱&#x1f6f8;烧录镜像⭐启动系统&#x1f388;本机登录&#x1f388;远程登陆 &#x1f386;AI功能体验&#x1f50e;总结 &#x1f354;开箱 可以看到是很精美的开发组件 这里是香橙派官网 http://www.orangepi.cn/ 我们找到下面图片的内容&#…

【C/C++】C语言如何实现类似C++的智能指针?

在C中&#xff0c;智能指针是为了自动化资源管理而引入的工具。比如std::unique_ptr和std::shared_ptr等&#xff0c;它们管理着所持有对象的生命周期&#xff0c;可以在智能指针被销毁时自动释放其所持有的资源。在C语言中&#xff0c;虽然没有直接的智能指针概念&#xff0c;…

“冻干”凭什么好吃不肥喵?既能当零食又可做主食的冻干分享

近年来&#xff0c;冻干猫粮因其高品质而备受喜爱&#xff0c;吸引了无数猫主人的目光&#xff0c;像我这样的资深养猫人早已开始选择冻干喂养。但新手养猫的人&#xff0c;可能会感到迷茫&#xff1a;冻干猫粮到底是什么&#xff1f;冻干可以一直当主食喂吗&#xff1f; 一、…

爬虫技术笔记

一. Selenium技术 遇到安装并调用ChromeDriver问题&#xff1a; webdriver.Chrome DeprecationWarning: executable_path has been deprecated, please pass in a Service object 在 Selenium 4 中&#xff0c;executable_path 参数用于指定 ChromeDriver 的路径已经在 webdr…

SEO 中域权限和页面权限之间的区别

域权限 &#xff08;DA&#xff09; 和页面权限 &#xff08;PA&#xff09; 是 SEO&#xff08;搜索引擎优化&#xff09;中的两个重要概念&#xff0c;有助于评估网站和单个网页的强度和相关性。 目录 什么是域权限&#xff1f;什么是页面权限&#xff1f;主要区别结论 什么…

算法003:快乐数

这道题采用快慢双指针的方法。 为了弄清楚这个题到底是要我们干嘛&#xff0c;我们把整个过程类比一下&#xff1a; 不管是n19还是n2&#xff0c;我们都把它当成一种判断链表是否有环的方式。 对于n19&#xff0c;题干是这样解释的&#xff1a; 我们把它当成链表&#xff0c…

【STL源码剖析】priority_queue 优先队列的简单实现

水到绝处是风景 人到绝境是重生 目录 priority_queue的模拟实现 源码剖析&#xff1a; 代码测试&#xff1a; 契子✨ 我们之前不仅讲过 队列queue 还有 双端队列deque 而我们今天所讲的依旧是队列家族的成员 -- 优先队列priority_queue 顾名思义&#xff0c;priority_queue是…

空间搜索geohash概述

概述 通常在一些2C业务场景中会根据用户的位置来搜索一些内容。通常提供位置搜索的都是直接通过redis/mongodb/es等中间件实现的。 但是这些中间件又是怎么实现位置搜索的呢&#xff1b; 查了一番资料&#xff0c;发现背后一个公共的算法Geohash。 Geohash 经度和纬度是2个…

Amesim示例篇-案例2:液体循环回路

前文已完成流体库常用的元件参数与使用方法简单的介绍。本文将对液体回路系统管路的压降标定仿真方法与注意事项进行讨论。首先&#xff0c;本案例应用到的元件有膨胀水壶、水泵、阻力管、常规管路等元件。将上述元件进行串联组成液冷循环回路。 图1 膨胀水壶 图2 水泵 1…

如何让tracert命令的显示信息显示*星号

tracert命令如果在中间某一个节点超时&#xff0c;只会在显示信息中标识此节点信息超时“ * * * ”&#xff0c;不影响整个tracert命令操作。 如上图所示&#xff0c;在DeviceA上执行tracert 10.1.2.2命令&#xff0c;缺省情况下&#xff0c;DeviceA上的显示信息为&#xff1a;…

拼接屏处理器

拼接屏系统由三大部分组成&#xff0c;即拼接墙、液晶拼接处理器和信号源。其中液晶拼接处理器是关键技术的核心&#xff0c;支持不同像素的图像在大屏显示墙上显示以及在大屏显示墙上任意开窗口、BSV画面叠加、窗口放大缩小、跨屏漫游显示等。液晶拼接处理器一般分为两种&…