最近在做一个音频可视化的业务,网上有Java层的实现方法,但是业务需要用C实现,从原理出发其实很简单,先对音频进行解码,再计算分贝。这比把大象放进冰箱还简单。本文从音频可视化的业务为依托,以FFmpeg为基础实现解码,计算,绘制。

一、解码流程

解码流程大致分为以下三个部分,以FFmpge源码下的ffmpeg\doc\examples\decode_audio.c为参考。

1.1、解析音频信息

avformat_open_input负责打开需要解码的音频文件,如果文件打开成功的话会初始化AVFormatContext,avformat_find_stream_info开启音频流遍历,av_find_best_stream找到最合适解析数据的帧,解析完后我们可以通过返回的AVStream获取到我们需要用的解码器id、通道数、采样率、位深、音频时长、数据排列结构。拿到解码器id我们通过解码器id获取解码器avcodec_find_decoder,有些解码器并不是FFmpeg内置的,所以有些需要在编译时就加进去,我之前的文章也有讲过AAC和MP3编解码第三方库。如果找到了解码器,下一步就是avcodec_alloc_context3对解码器上下文AVCodecContext进行初始化,初始化完成后avcodec_parameters_to_context将解码器参数设置给解码器上下文,例如通道数,采样率,采样位深等等信息。如果未设置可能会出现invalid argument的错误,导致后续无法继续。最后通过avcodec_open2打开解码器,如果打开成功我们就可以开始对音频数据进行读取。

1.2、从原始数据packet到frame

我们解码的目的就是为了拿到底层播放器能播的PCM数据。既然我们已经获取到了解码器,那么下面就是一帧一帧的读取解码器解析出来的数据。首先我们需要av_packet_alloc初始化包对象AVPacket,包对象是未解码的数据,原始的音频数据被打包成一个一个的包,然后送给解码器去把包打开,变成帧对象,所以我们又需要通过av_frame_alloc初始化帧对象AVFrame,把它送给解码器,解码器用数据把它装满后返回回来。av_read_frame就是从打开的文件读取一个数据包,对于AAC/MP3来说他们是未解码的压缩数据。然后通过avcodec_send_packet把数据包送给解码器,返回0表示解码器解包成功,接下来就可以从解码器读数据,这时的数据就是以帧的形式存在,avcodec_receive_frame读取帧,因为一个包可能有几个帧,所以需要循环读取,当avcodec_receive_frame返回0时表示读取成功,可以进行下一步操作,当返回值是AVERROR_EOF表示读取完毕可以跳出循环了,返回AVERROR(EAGAIN)表示解码器输出已经是不可用的状态,必须向解码器送新包来激活输出,同样也可以跳出读取和解析帧的循环。

1.3、从frame到PCM byte

我们的PCM数据就在frame的data里,但是我们并不能直接拿,首先我们得知道拿多少,怎么拿。拿多少取决于采样位数,通道数和帧里面的样本数。例如44100HZ的话一秒就有44100*通道数个样本。那一个帧里面就一共有 采样位数/8*通道数*样本数个字节数据。怎么拿取决于音频数据的存储方式,音频存储方式有两种:

  • planar:音频左右声道数据分开放置,数据存储格式为

data[0]:LLLLLLLLLLLLLLLL

data[1]:RRRRRRRRRRRRR

  • packet:音频左右声道数据交替放置,数据存储格式为

data[0]:LRLRLRLRLRLRLRLR

最终拿到的数据都是以LRLRLRLRLR的方式排列,到这里我们可以把它送给播放器或者在送给播放器前加一些我们自己的音频算法,全部解码完成后,最后记得释放掉相关资源。在这里我们简单点,计算它的分贝,实现音频可视化的功能。

二、分贝计算

我们音频的分贝往往不需要计算每一个样本的分贝数,第一计算密度太大超出人眼感知对显示没有益处,二是计算量太大会导致我们的计算时间大大延长。因为声音具有一定的延续性,所以我们可以计算一个时间段内的平均值来获得一段音频范围的分布值,这样既减小了工作量又达到了合理可视化的效果。首先是获取平均值,假设我们每秒想获取10个分贝值,那么我们需要计算采样率*通道数*采样位数/8/10个字节数据的平均值,我们不妨自己把它叫dB采样区间样本数,一个16bit位深的音频每两个字节组成一个样本,将区间内样本相加再除以样本数取平均值即可。接下来就是dB的计算,dB其实并不特指分贝,它只是在音频描述领域。它描述的是音频的增益关系,如果想详细了解db是什么可以看看《什么是dB》。分贝的计算公式是

20*log10(value)

所以声音的分贝描述的并不是线性关系而是指数关系,例如70db比50db的声音大了20倍,例如16bit可以描述的音频范围为0-65535那么它的最大dB值在96.3左右,32bit可以描述音频范围在0-4294967296,那么它的最大dB值在192.6。把我们刚才计算的平均值带入value就能获得我们的区间的分贝,把它存起来解析完一起返回或者逐个回调都可以,看你的业务需求。下面是计算16bit采样位数的分贝的方法,32bit的处理方法类似,主要注意值的大小,和每次位移的byte步长。拿到了了分贝我们就可以将它们变成条变成块的绘制到屏幕了。

void getPcmDB16(const unsigned char *pcmdata, size_t size) {int db = 0;short int value = 0;double sum = 0;for(int i = 0; i < size; i += bit_format/8){memcpy(&value, pcmdata+i, bit_format/8); //获取2个字节的大小(值)sum += abs(value); //绝对值求和}sum = sum / (size / (bit_format/8)); //求平均值(2个字节表示一个振幅,所以振幅个数为:size/2个)if(sum > 0){db = (int)(20.0*log10(sum));}memcpy(wave_buffer+wave_index,(char*)&db,1);wave_index++;
}

需要注意的是我们在解码时ffmpeg的音频格式类型除了packet和planar两个大类外,对于32位的音频又区分了32位整形和32位浮点型。

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
};

浮点型的取值范围在-1到1的区间,所以我们在计算时需要乘以0x7fff来获得和16位同比例的数据,达到同样的显示效果。

void getPcmDBFloat(const unsigned char *pcmdata, size_t size) {int db = 0;float value = 0;double sum = 0;for(int i = 0; i < size; i += bit_format/8){memcpy(&value, pcmdata+i, bit_format/8); //获取4个字节的大小(值)sum += abs(value*0x7fff); //绝对值求和}sum = sum / (size / (bit_format/8)); if(sum > 0){db = (int)(20.0*log10(sum));}memcpy(wave_buffer+wave_index,(char*)&db,1);wave_index++;
}

三、实现效果

欢迎大家交流讨论,批评指正。完整代码我会阶段性的推到我的GitHub上Audio项目上,如果需要解析音频的单个C文件可以私聊我发送给大家。

更新:资源已经上传到CSDN,0积分下载,有兴趣的可以下载下来看一看。解码只针对ffmpeg,计算db的方法可通用。音频解码,分贝计算 资源下载https://download.csdn.net/download/qq_37841321/85203912?spm=1001.2014.3001.5503

FFmpeg音频解码-音频可视化相关推荐

  1. ffmpeg api解码音频,得到pcm数据

    ffmpeg api解码音频,得到pcm数据,程序如下: extern "C" { #include "libavutil/avutil.h" #include ...

  2. 深入浅出:FFmpeg 音频解码与处理AVFrame全解析

    深入浅出:FFmpeg 音频解码与处理全解析 一.FFmpeg 简介 1.1 FFmpeg 的历史与发展 1.2 FFmpeg 的主要组成部分 二.音频编解码基础 (Basics of Audio E ...

  3. 海思3516开发记录-AAC音频解码

    海思平台播放AAC文件 1.说明 2.音频解码 音频API 参考: 解码aac流程: 音频设置 音频解码相关错误码 1.说明 海思3516平台内置了aac解码库,可以直接解码aac音频文件,但要支持m ...

  4. FFmpeg 开发(04):FFmpeg + OpenGLES 实现音频可视化播放

    本文基于上一篇文章 FFmpeg + OpenSLES 实现音频解码播放 ,利用 FFmpeg 对一个 Mp4 文件的音频流进行解码,然后将解码后的 PCM 音频数据进行重采样,最后利用 OpenSL ...

  5. Android NDK开发之旅31 FFmpeg音频解码

    ###前言 #####基于Android NDK开发之旅30--FFmpeg视频播放这篇文章,我们已经学会视频解码基本过程.这篇文章就对音频解码进行分析. #####音频解码和视频解码的套路基本是一样 ...

  6. ffmpeg 解码音频(aac、mp3)输出pcm文件

    ffmpeg 解码音频(aac.mp3)输出pcm文件 播放pcm可以参考: ffplay -ar 48000 -ac 2 -f f32le out.pcm main.c #include <s ...

  7. 基于 FFMPEG 的音频编解码(二):音频解码

    音频解码 基于 FFMPEG 的音频编解码(一):Hello FFMPEG,安装与编译 基于 FFMPEG 的音频编解码(二):音频解码 基于 FFMPEG 的音频编解码(三):音频编码 在 Hell ...

  8. FFmpeg解码音频代码

    工程请访问DecodeTest.用c++写的,使用FFmpeg-4.1.0.比较完善的是OfficalDecodeAudio.cpp,它是我研究了一下ffplay.c的源码得来的.该代码按照统一的44 ...

  9. 音视频开发之旅(36) -FFmpeg +OpenSL ES实现音频解码和播放

    目录 OpenSL ES基本介绍 OpenSL ES播放音频流程 代码实现 遇到的问题 资料 收获 上一篇我们通过AudioTrack实现了FFmpeg解码后的PCM音频数据的播放,在Android上 ...

最新文章

  1. 如何一起破解图形化Python调试器
  2. excel函数FREQUENCY、COUNTIFS、COUNTIF
  3. 字符串整体赋值和比较问题
  4. linux crontab 管理,linux crontab 命令详解
  5. ABAP更改程序的请求包操作
  6. 0 RabbitMQ概念
  7. 【C语言进阶深度学习记录】七 C语言中的循环语句
  8. 江阳职高计算机应用教改实验,计算机应用课程教改模式
  9. Django学习目录
  10. 企业五大模式,多看两遍你就懂了
  11. 女人为什么必须学习会家庭财富管理?
  12. 路由器05---多拨
  13. 饥荒联机版加入服务器显示无应答,饥荒联机版水中木更新内容汇总 8月13日更新预览[多图]...
  14. 淘宝客是鸡肋还是熊掌
  15. 【Excel VBA】Len的有趣之处——获取的长度怎么不一致?
  16. [堆+贪心] CF596C. Wilbur and Points
  17. python固定效应模型_panel data做固定效应模型时有下列几种方法
  18. 《炬丰科技-半导体工艺》 玻璃薄化蚀刻
  19. mac xcode 开发C++
  20. python汇率换算程序_编写python程序,解决公里与英里转换问题,要求输入英里数,计算出相应的公里数...

热门文章

  1. java get/set方法好处
  2. MaratonIME plays Cîrokime
  3. 【摘自csdn】一场我没有看懂的相亲
  4. 长尾效应解析以及长尾效应在电商中的应用
  5. C++ 拉格朗日插值法优化 DP
  6. ARDUINO:控制两台步进电机同步运转
  7. windows.frames
  8. 北京进一步强化节能实施,能耗监测、余热回收等为数据中心重点
  9. 怎么理解——用户不是人
  10. 代码随想录Day48|198.打家劫舍、213.打家劫舍II、337.打家劫舍III