该文章首发于微信公众号:字节流动
FFmpeg 开发系列连载:
字节流动:FFmpeg 开发(01):FFmpeg 编译和集成zhuanlan.zhihu.com
字节流动:FFmpeg 开发(02):FFmpeg + ANativeWindow 实现视频解码播放zhuanlan.zhihu.com
字节流动:FFmpeg 开发(03):FFmpeg + OpenSLES 实现音频解码播放zhuanlan.zhihu.com
本文基于上一篇文章 FFmpeg + OpenSLES 实现音频解码播放 ,利用 FFmpeg 对一个 Mp4 文件的音频流进行解码,然后将解码后的 PCM 音频数据进行重采样,最后利用 OpenSLES 进行播放的同时,将 PCM 音频一个通道的数据实时渲染成柱状图。
关于音频的可视化,在旧文中,我们曾经实现过将 Android AudioRecorder 采集的实时音频单通道 PCM 数据用 OpenGL 渲染成柱状图。具体的渲染过程和细节,请移步这篇文章,代码已开源:
OpenGL ES 实现可视化实时音频
提取一个通道的音频数据
在上一篇文章,我们构建 OpenSLES 播放器时,对数据格式的定义如下:
SLDataFormat_PCM
从上面代码中可以看出,音频驱动接收的 PCM 数据的采样率是 44.1kHz,双通道,采样大小 2 字节。由于我们要渲染的是一个通道的 PCM 数据,所以需要对双通道的数据做一个提取。
如上图所示,解码后的 PCM 数据是 2 个通道的数据交叉存储,当使用指针偏移提取某一通道的数据时,每次偏移的步长是 2 字节 X 通道数 = 4 个字节。
提取某一通道的 PCM 数据方式如下,通过该方式我们可以将一帧音频数据每个通道的数据进行分离。
//小端序存储的音频数据
另外需要注意的是,数据的存储方式分为大端序和小端序,小端序指低地址存放低位、高地址存放高位,大端序与小端序相反,即低地址存放高位,分离通道数据需要注意。
//大端序存储的音频数据
OpenGL ES 渲染音频数据
OpenGLES 全称 OpenGL for Embedded Systems ,是三维图形应用程序接口 OpenGL 的子集,本质上是一个跨编程语言、跨平台的编程接口规范,主要应用于嵌入式设备,如手机、平板等。
由于前期已经系统地阐述了 OpenGL ES 相关知识点,这里就不做展开叙述,详细内容请参考:
Android OpenGL ES 从入门到精通系统性学习教程
利用 OpenGL 渲染音频数据,本质上就是根据音频数据的值去构建一组如下图所示的网格,最终渲染成条状图。
接下来就是代码实现过程,首先在 Java 层创建 GLSurfaceView 的 Render ,FFMediaPlayer 中增加对应 Native 函数:
private
对应 Java 层接口的 JNI :
//可视化音频的渲染接口
Native 层实现音频渲染的类:
#include
最后只需要在 OpenSLES 播放器的回调函数(见上篇文章)中调用下面函数即可:
AudioFrame