前端实时AAC音频处理方案

2,836 阅读6分钟

前言

针对现如今移动端设备音频广泛的应用场景,我们在测试相应程序的过程中不可避免的需要对音频能力进行验证,为了能够在云真机测试平台中完成不同场景下的音频测试,就需要实时获取客户端设备中播放的音频,进而能够直接通过 Web 端平台进行测试。

根据应用场景,需要从移动端设备中读取音频数据,即获取到音频裸数据 PCM (Pulse Code Modulation) 数据,然后将 PCM 数据通过一系列方式,最终发送到 Web 端进行播放。

技术选型

编码技术

由于获取到的音频数据是裸数据(PCM数据),如果将其存储在本地磁盘中,音频文件的体积是可接受的,但是我们应用的场景是实时传输,直接传输 PCM 数据必然会有传输数据量过大的问题,因此需要使用压缩编码技术对裸数据进行压缩后传输。常见的编码技术有 AAC、MP3、WAV 和 WMA 等等。

通过参考 音频编码方案之间音质比较(AAC,MP3,WMA等) 文章可以看出,在码率较低等情况下,不同编码方案的音频的音质排序为:AAC+ > MP3PRO > AAC > RealAudio > WMA > MP3。

由于 AAC 是一种高压缩比的音频压缩算法,但它的压缩比要远超过较老的音频压缩算法,如 AC-3、MP3 等。并且其质量可以同未压缩的 CD 音质相媲美。因此最终选择 AAC 编码技术对 PCM 数据进行压缩。

前端解码方案

选择使用 jMuxer 作为解码器,是一个基于 MSE 技术的 JavaScript 开源库,允许 JavaScript 动态构建 <video><audio> 的媒体流,支持对接收到的 AAC 数据进行解码,并且有着接近原生的解码速度。

AAC音频编码技术

AAC是高级音频编码(Advanced Audio Coding)的缩写,出现于1997年,最初是基于MPEG-2的音频编码技术。由Fraunhofer IIS、Dolby Laboratories、AT&T、Sony等公司共同开发,目的是取代MP3格式。2000年,MPEG-4标准出台,AAC重新集成了其它技术(PS,SBR),为区别于传统的MPEG-2 AAC,故含有SBR或PS特性的AAC又称为MPEG-4 AAC。

AAC 是新一代的音频有损压缩技术,音频文件格式有 ADIF 和 ADTS:

  • ADIF:Audio Data Interchange Format 音频数据交换格式。这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,即它的解码必须在明确定义的开始处进行。故这种格式常用在磁盘文件中。
  • ADTS:Audio Data Transport Stream。是 AAC 音频的传输流格式。这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。

总的来说,ADTS 可以在任意帧解码,也就是说它每一帧都有头信息。ADIF 只有一个统一的头,所以必须得到所有的数据后解码。

对原始帧加上 ADTS 头进行 ADTS 的封装,就形成了 ADTS 帧。 AAC音频文件的每一帧由 ADTS Header和 AAC Audio Data组成。结构体如下: 1

而本次方案中由于实时传输,将会采用 ADTS 格式的数据帧进行数据传输。一个数据包 ADTS Header 分为:

  • 固定头(fixed header):数据每一帧都相同,可参考 AAC的ADTS头文件信息介绍
  • 可变头(variable header):在帧与帧之间可变;

有时候可能我们接收到的 AAC 数据包是不合法的,也就是 ADTS Header 不合法,可以将 Header 通过 P23工具 对数据进行验证。

JMuxer 解码

在通过 websocket 获取到 AAC 数据包时,其实拿到的是16进制的数据,在解码的过程中会转换成10进制数据,最后会转成二进制进行解析。

JMuxer 库 同时支持音视频解码。首先介绍以下 JMuxer 常用的参数和方法。

参数

属性属性值说明默认值
nodeTag IDvideo/audio 标签 ID-
modeaudio/video/both解码模式both
flushingTime时间(毫秒)缓存刷新频率1500
maxDelay时间(毫秒)最大延时500
clearBuffertrue/false清除Buffertrue
fps帧数视频帧数duration
onReady函数MSE 就绪后回调-
onError函数Buffer 异常回调-
debugtrue/false是否打印日志false

方法

函数参数说明
feeddata object对象参数包含 audio, video 和 duration,如果没有提供 duration,将根据 fps 计算。
createStream-写入缓冲区,nodeJS可用
reset-重置并重新开始 JMuxer
destroy-实例销毁

整体代码实现

整体 Demo 可参考:github.com/Lewage59/pr…

/**
 * 音频接受处理器
 */
import JMuxer from 'jmuxer';
import Socket from './socket';

const DEFAULT_WS_URL = 'ws://localhost:8080';

export default class AudioProcessor {
    constructor(options) {
        const wsUrl = options.wsUrl || DEFAULT_WS_URL
        /**
         * node: 'player',
         * mode: 'audio',
         * debug: true,
         * flushingTime: 0,
         * wsUrl
         */
        this.jmuxer = new JMuxer({
            mode: 'audio',
            flushingTime: 0,
            onReady() {
                console.log('Jmuxer audio init onReady!');
            },
            onError(data) {
                console.error('Buffer error encountered', data);
            },
            ...options
        });
        this.audioDom = document.getElementById(options.node)
        this.initWebSocket(wsUrl)
	}

    initWebSocket(url) {
        const that = this
        this.ws = new Socket({
            url,
            binaryType: 'arraybuffer',
            onmessage: function(event) {
                const data = that.parse(event.data);
                data && that.jmuxer.feed(data);
            }
        });
    }

    /**
	 * 音频解析
	 * @param {*} data AAC Buffer 视频流
	 * @returns 
	 */
    parse(data) {
        let input = new Uint8Array(data)
    
        return {
        	audio: input
        };
    }

    onPlay() {
        this.audioDom.load()
        const playPromise = this.audioDom.play()
        if (playPromise !== undefined) {
            playPromise.then(() => {
                this.audioDom.play()
            })
        }
    }

    onPause() {
    	this.audioDom.pause()
    }

    onReload() {
    	this.audioDom.load()
    }

    onDestroy() {
        this.ws.handleClose()
        this.audioDom.pause()
        this.jmuxer = null
    }
}

而有时候前端需要模拟音视频服务进行调试的情况,首先可以可以通过 FFmpeg 将 MP3 音频转换为 AAC 音频格式,再将音频文件分割成数据帧通过 node 服务启动 websocket 进行传输数据帧,具体案例可参考 Node服务端AAC音频传输

写在最后

音视频相关技术在之前其实一直没有接触过,在这次的预研和项目落地的过程中,也对该技术有了新的认识,因此本文主要是对应用层面的讲解,没有深入探讨每个技术的细节,但我相信后续还会对该音视频技术持续关注。

如果对UI自动化测试、远程控制等感兴趣的小伙伴可以关注一下 Sonic 云真机测试平台

Sonic,一站式开源分布式集群云真机测试平台,致力服务于中小企业的客户端UI测试(永久免费)

参考资料