FFmpeg进阶: 采用音频滤镜对音频进行转码
文章目录
- 采样位数
- 采样率
- 声道布局
- 码率
- 使用FFmpeg音频滤镜进行转码
- 参考链接
很多时候为了让视频文件适应不同的播放领域,我们需要对音频文件进行转码操作,转码操作其实主要就是修改音频文件的各种参数包括:采样位数、采样率、音频布局、码率等等。下面分别介绍一下各个参数的意义和作用。
采样位数
采样位数也称为位深度、分辨率, 它是指声音的连续强度被数字表示后可以分为多少级。N-bit的意思声音的强度被均分为2^N级。16位的就是65535级。这是一个很大的数了,人可能也分辨不出1/65536的音强差别。也可以说是声卡的分辨率,它的数值越大,分辨率也就越高,所发出声音的能力越强。这里的采样倍数主要针对的是信号的强度特性,采样率针对的是信号的时间(频率)特性这是两个不一样的概念。
ffmpeg常用的采样位数对应的格式如下所示:
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
};
采样率
音频采样,是把声音从模拟信号转换为数字信号。采样率,就是每秒对声音进行采集的次数,同样也是所得的数字信号的每秒样本数。在对声音进行采样时,常用的采样率有:
8,000 Hz - 电话所用采样率, 对于人的说话已经足够
11,025 Hz - AM调幅广播所用采样率
22,050 Hz~24,000 Hz - FM调频广播所用采样率
32,000 Hz - miniDV 数码视频 camcorder、DAT (LP mode)所用采样率
44,100 Hz - 音频 CD, 也常用于 MPEG-1 音频(VCD, SVCD, MP3)所用采样率
47,250 Hz - 商用PCM录音机所用采样率
48,000 Hz - miniDV、数字电视、DVD、DAT、电影和专业音频所用的数字声音所用采样率
50,000 Hz - 商用数字录音机所用采样率
96,000 或者192,000 Hz - DVD-Audio、一些 LPCM DVD 音轨、BD-ROM(蓝光盘)音轨、和 HD-DVD (高清晰度 DVD)音轨所用所用采样率
2.8224 MHz - Direct Stream Digital 的 1 位 sigma-delta modulation 过程所用采样率。
采样越高,声音的还原就越真实越自然,人对频率的识别范围是20HZ - 20000HZ, 如果每秒钟能对声音做 20000 个采样, 回放时就足可以满足人耳的需求.所以 22050 的采样频率是常用的, 44100已是CD音质, 超过48000的采样对人耳已经没有意义。这和电影的每秒 24 帧图片的道理差不多。
声道布局
当人听到声音时,能对声源进行定位,那么通过在不同的位置设置声源,就可以造就出更好的听觉感受。常见的声道有:
- 单声道, mono
- 双声道, stereo, 最常见的类型,包含左声道以及右声道
- 2.1声道,在双声道基础上加入一个低音声道
- 5.1声道,包含一个正面声道、左前方声道、右前方声道、左环绕声道、右环绕声道、一个低音声道,最早应用于早期的电影院
- 7.1声道,在5.1声道的基础上,把左右的环绕声道拆分为左右环绕声道以及左右后置声道,主要应用于BD以及现代的电影院
码率
码率也就是每秒的传输速率(也叫比特率),压缩的音频文件常用倍速来表示,比如达到CD音质的MP3是128kbps/44100HZ。注意这里的单位是bit而不是Byte,一个Byte等于8个bit(位),bit是最小的单位,一般用于网络速度的描述和各种通信速度,Byte则用于计算硬盘,内存的大小。
使用FFmpeg音频滤镜进行转码
不同领域对音频的播放要求是不一样的,所以需要针对不同的领域对音频参数进行调整,这里介绍一下如何通过音频滤镜调整音频数据的相关参数,对应的实现如下:
#include "../audio_filter.h"extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavfilter/avfilter.h>
#include <libswresample/swresample.h>
}#include <string>/**@brief 转换音频数据的格式
* @param[in] output_filename 输出文件名称
* @param[in] input_filename 输入文件名称
* @param[in] sample_fmt 采样格式
* @param[in] sample_rate 采样率
* @param[in] channel_layout 通道布局
* @param[in] bitrate 码率
* @return 函数执行结果
* - 0 成功
* - 其它 失败
*/
int transcode_audio(const char *output_filename, const char *input_filename, AVSampleFormat sample_fmt,int sample_rate, uint64_t channel_layout, uint64_t bitrate)
{//输入输出格式AVFormatContext *inFmtCtx = nullptr;AVFormatContext *outFmtCtx = nullptr;//解码器和编码器AVCodecContext *aDecCtx = nullptr;AVCodecContext *aEncCtx = nullptr;//输出流AVStream *aOutStream = nullptr;int ret;// open input fileret = avformat_open_input(&inFmtCtx, input_filename, nullptr, nullptr);ret = avformat_find_stream_info(inFmtCtx, nullptr);// open output fileavformat_alloc_output_context2(&outFmtCtx, nullptr, nullptr, output_filename);for (int i = 0; i < inFmtCtx->nb_streams; ++i){AVStream *inStream = inFmtCtx->streams[i];if (inStream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){//输入流的解码器AVCodec *decoder = avcodec_find_decoder(inStream->codecpar->codec_id);aDecCtx = avcodec_alloc_context3(decoder);ret = avcodec_parameters_to_context(aDecCtx, inStream->codecpar);ret = avcodec_open2(aDecCtx, decoder, nullptr);//输出流的编码器AVCodec *encoder = avcodec_find_encoder(outFmtCtx->oformat->audio_codec);aOutStream = avformat_new_stream(outFmtCtx, encoder);aOutStream->id = outFmtCtx->nb_streams - 1;aEncCtx = avcodec_alloc_context3(encoder);aEncCtx->codec_id = encoder->id;aEncCtx->sample_fmt = sample_fmt ? sample_fmt : aDecCtx->sample_fmt;aEncCtx->sample_rate = sample_rate ? sample_rate : aDecCtx->sample_rate;aEncCtx->channel_layout = channel_layout;aEncCtx->channels = av_get_channel_layout_nb_channels(channel_layout);aEncCtx->bit_rate = bitrate ? bitrate : aDecCtx->bit_rate;aEncCtx->time_base = { 1, aEncCtx->sample_rate };aOutStream->time_base = aEncCtx->time_base;if (outFmtCtx->oformat->flags & AVFMT_GLOBALHEADER)aEncCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;avcodec_open2(aEncCtx, encoder, nullptr);ret = avcodec_parameters_from_context(aOutStream->codecpar, aEncCtx);av_dict_copy(&aOutStream->metadata, inStream->metadata, 0);break;}}if (!(outFmtCtx->oformat->flags & AVFMT_NOFILE)) {ret = avio_open(&outFmtCtx->pb, output_filename, AVIO_FLAG_WRITE);if (ret < 0) {return -1;}}ret = avformat_write_header(outFmtCtx, nullptr);if (ret < 0) {return -1;}AVFrame *inAudioFrame = av_frame_alloc();AVFrame *outAudioFrame = av_frame_alloc();outAudioFrame->format = aEncCtx->sample_fmt;outAudioFrame->sample_rate = aEncCtx->sample_rate;outAudioFrame->channel_layout = aEncCtx->channel_layout;outAudioFrame->nb_samples = aEncCtx->frame_size;ret = av_frame_get_buffer(outAudioFrame, 0);int64_t audio_pts = 0;//修改音频数据包格式的滤镜AudioFilter filter;char description[512];AudioConfig inConfig(aDecCtx->sample_fmt, aDecCtx->sample_rate, aDecCtx->channel_layout, aDecCtx->time_base);AudioConfig outConfig(aEncCtx->sample_fmt, aEncCtx->sample_rate, aEncCtx->channel_layout, aEncCtx->time_base);char ch_layout[64];av_get_channel_layout_string(ch_layout, sizeof(ch_layout),av_get_channel_layout_nb_channels(aEncCtx->channel_layout), aEncCtx->channel_layout);snprintf(description, sizeof(description),"[in]aresample=sample_rate=%d[res];[res]aformat=sample_fmts=%s:sample_rates=%d:channel_layouts=%s[out]",aEncCtx->sample_rate,av_get_sample_fmt_name(aEncCtx->sample_fmt),aEncCtx->sample_rate,ch_layout);filter.create(description, &inConfig, &outConfig);filter.dumpGraph();while (true) {//解析音频帧并通过滤镜进行处理AVPacket inPacket{ nullptr };av_init_packet(&inPacket);ret = av_read_frame(inFmtCtx, &inPacket);if (ret == AVERROR_EOF) {break;}else if (ret < 0) {return -1;}if (inPacket.stream_index == aOutStream->index) {ret = avcodec_send_packet(aDecCtx, &inPacket);if (ret != 0) {printf("send packet error\n");}ret = avcodec_receive_frame(aDecCtx, inAudioFrame);if (ret == 0) {ret = filter.addInput1(inAudioFrame);av_frame_unref(inAudioFrame);if (ret < 0){printf("add filter input1 error\n");}do {outAudioFrame->nb_samples = aEncCtx->frame_size;ret = filter.getFrame(outAudioFrame);if (ret == 0) {outAudioFrame->pts = audio_pts;audio_pts += outAudioFrame->nb_samples;ret = avcodec_send_frame(aEncCtx, outAudioFrame);if (ret < 0) {printf("unable to send frame: %s\n");}}else {printf("unable to get filter audio frame: %s\n");break;}do {AVPacket outPacket{ nullptr };av_init_packet(&outPacket);ret = avcodec_receive_packet(aEncCtx, &outPacket);if (ret == 0) {av_packet_rescale_ts(&outPacket, aEncCtx->time_base, aOutStream->time_base);outPacket.stream_index = aOutStream->index;ret = av_interleaved_write_frame(outFmtCtx, &outPacket);if (ret < 0) {printf("unable to write packet\n");break;}}else {printf("unable to receive packet\n");break;}} while (true);} while (true);}else {printf("unable to receive frame\n");}}}//清理数据缓存int eof = 0;do {ret = filter.getFrame(outAudioFrame);if (ret == 0) {outAudioFrame->pts = audio_pts;audio_pts += outAudioFrame->nb_samples;}else {printf("filter queue finished\n");}ret = avcodec_send_frame(aEncCtx, ret == 0 ? outAudioFrame : nullptr);do {AVPacket outPacket{ nullptr };ret = avcodec_receive_packet(aEncCtx, &outPacket);if (ret == 0) {av_packet_rescale_ts(&outPacket, aEncCtx->time_base, aOutStream->time_base);outPacket.stream_index = aOutStream->index;ret = av_interleaved_write_frame(outFmtCtx, &outPacket);if (ret < 0) {eof = 1;break;}}else if (ret == AVERROR_EOF) {eof = 1;break;}else {break;}} while (true);} while (!eof);//释放对应的资源filter.destroy();av_write_trailer(outFmtCtx);avformat_close_input(&inFmtCtx);av_frame_free(&inAudioFrame);av_frame_free(&outAudioFrame);avcodec_free_context(&aDecCtx);avcodec_free_context(&aEncCtx);avformat_free_context(inFmtCtx);avformat_free_context(outFmtCtx);return 0;
}
这里我们将音频文件的采样格式修改为AV_SAMPLE_FMT_FLTP,同时我们将采样率降低为22050,码率修改为80kbps。
int main(int argc, char* argv[])
{if (argc != 3){printf("usage:%1 input filepath %2 outputfilepath");return -1;}//输入文件地址、输出文件地址std::string fileInput = std::string(argv[1]);std::string fileOutput = std::string(argv[2]);transcode_audio(fileOutput.c_str(), fileInput.c_str(),(AVSampleFormat)AV_SAMPLE_FMT_FLTP,22050, AV_CH_LAYOUT_STEREO,80000);
}
参考链接
参考链接:https://www.cnblogs.com/yongdaimi/p/10722355.html
相关文章:
FFmpeg进阶: 采用音频滤镜对音频进行转码
文章目录采样位数采样率声道布局码率使用FFmpeg音频滤镜进行转码参考链接很多时候为了让视频文件适应不同的播放领域,我们需要对音频文件进行转码操作,转码操作其实主要就是修改音频文件的各种参数包括:采样位数、采样率、音频布局、码率等等。下面分别介…...

C++:AVL树
AVL树的概念 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下,时间复杂度为O(N); 两位俄罗斯的数学家G.M.Ade…...

Docker中安装Oracle-12c
前言 MySQL和Oracle是开发中常用到的两个关系型数据库管理系统,接上一期内容,这一期在Docker中完成oracle-12c的安装和配置。 安装oracle-12c 1、拉取oracle-12c镜像 启动Docker Desktop后在cmd窗口中执行docker search oracle命令,搜索O…...

教你如何用Python分析出选注双色球号码
前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 数据集介绍 找从19年到现在的开奖历史数据,我们首先要把这个历史数据拿到, 拿到我们再进行做分析,分析每个号码出现的频率是多少, 哪个多&#x…...

elasticsearch映射及字段类型
查询映射关系类型上对字段的类型进行映射,我们前面知道可以通过get方法请求_mapping查询指定类型的映射关系:此语句可以查询get-together索引下的group类型的映射关系更新映射关系使用put方法可以更新类型的映射这里指定了new-events类型的字段映射关系&…...
1493围圈报数(队列)
题目描述 有n个人依次围成一圈,从第1个人开始报数,数到第m个人出列,然后从出列的下一个人开始报数,数到第m个人又出列,…,如此反复到所有的人全部出列为止。…...

【ArcGIS Pro二次开发】(2):创建一个Add-in项目
Add-In即模块加载项,是一种能够快速扩展桌面应用程序功能的全新扩展方式。 一、创建新项目 1、打开VS2002,选择创建新项目。 2、在搜索框中输入“arcgis pro”,在搜索结果中选择【ArcGIS Pro 模块加载项】创建项目,注意选择语言应…...

浏览器缓存是如何提升网站访问速度的
提升速度,降低负载 浏览器访问一个页面时,会请求加载HTML、CSS和JS等静态资源,并把这些内容渲染到屏幕上。 对浏览器来说,如果页面没有更新,每次都去请求服务器是没有必要的。所以,把下载的资源缓存起来&…...

Linux中几个在终端中有趣的命令
uhh…最近我不知道该更新些什么,所以就更新Linux几个很有趣的命令 文章目录前言1.命令:sl安装 sl输出2. 命令:telnet命令:fortune安装fortune4.命令:rev(反转)安装rev5. 命令:factor…...
快来来试试SpringBoot3 中的新玩意~
你还在用OpenFeign嘛?快来试试 SpringBoot3 中的这个新玩意!声明式HTTP调用 1、由来 Spring Boot3 去年底就已经正式发布,我也尝了一把鲜,最近有空会和小伙伴们慢慢聊聊 Spring Boot3 都给我们带来了哪些新东西。 今天我们就先…...

【寻人启事】达坦科技持续招人ing
❤️一起来探索前沿科技,做有意思的事情~ 我们是谁 达坦科技(DatenLord)专注于打造新一代开源跨云存储平台。通过软硬件深度融合的方式打通云云壁垒,实现无限制跨云存储、跨云联通,建立海量异地、异构…...

【C/C++基础练习题】简单函数练习题
🍉内容专栏:【C/C要打好基础啊】 🍉本文内容:简单函数使用练习题(复习之前写过的实验报告) 🍉本文作者:Melon西西 🍉发布时间 :2023.2.11 目录 1.给定某个年…...
【代码随想录训练营】【Day11】第五章|栈与队列|20. 有效的括号|1047. 删除字符串中的所有相邻重复项|150. 逆波兰表达式求值
20. 有效的括号 题目详细:LeetCode.20 由题可知,有效字符串需满足: 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括号。 那么,我们可以利用栈后进先出的特点&#x…...

基于云原生分布式存储ceph实现k8s数据持久化
文章目录1、初始化集群1.1 集群机器配置1.2 配置主机名1.3 配置hosts文件1.4、配置互信1.5、关闭防火墙1.6、关闭selinux1.7、配置Ceph安装源1.8、配置时间同步1.9、安装基础软件包2、安装ceph集群2.1 安装ceph-deploy2.2 创建monitor节点2.3 安装ceph-monitor2.4 部署osd服务2…...
SpringMVC获取请求参数
SpringMVC获取请求参数 通过ServletAPI获取 将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的报文对象。 RequestMapping("/testServletAPI") // request表示当前请求 public String testServletAPI(H…...
详解浏览器从输入URL到页面展示的过程
用户发出 URL 请求到页面开始解析的这个过程,就叫做导航。 1. 用户输入 当用户在地址栏中输入一个查询关键字时,地址栏会判断输入的关键字是搜索内容,还是请求的 URL。 当用户输入关键字并键入回车之后,这意味着当前页面即将要…...

【吉先生的Java全栈之路】
吉士先生Java全栈学习路线🧡第一阶段Java基础: 在第一阶段:我们要认真听讲,因为基础很重要!基础很重要!基础很重要!!! 重要的事情说三遍。在这里我们先学JavaSE路线;学完之后我们要去学第一个可视化组件编程《GUI》;然后写个《贪吃蛇》游戏耍…...

第二章 Opencv图像处理基本操作
目录1.读取图像1-1.imread()方法2.显示图像2-1.imshow()方法2-2.waitKey()方法2-3.destroyAllWindows()方法2-4.小总结3.保存图像3-1.imwrite()方法4.查看图像属性4-1.常见的三个图像属性1.读取图像 要对一幅图像进行处理,第一件事就是要读取这幅图像。 1-1.imread(…...

字节一面:在浏览器地址栏输入一个 URL 后回车,背后发生了什么?
近段时间,有小伙伴面试字节,说遇到一个面试题: 在浏览器地址栏输入一个 URL 后回车,背后发生了什么? 这里尼恩给大家做一下系统化、体系化的梳理,使得大家可以充分展示一下大家雄厚的 “技术肌肉”…...

推荐3dMax三维设计十大插件
3dMax是一款功能非常强大的三维设计软件,但无论它的功能多么强大,也不可能包含所有三维方面的功能,这时候,第三方插件可以很好的弥补和增强3dMax的基本功能,下面就给大家介绍十款非常不错的3dMax插件。 森林包…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...