APP下载

基于Android平台的音频播放处理研究与实现

2020-12-23王强洪蕾

软件 2020年10期
关键词:解码

王强 洪蕾

摘  要: 随着Android终端设备的普及,基于Android平台的音频应用大批涌现。对于音频的处理,Android提供了MediaPlayer来满足开发者对音频的处理,MediaPlayer在音频采集、解码和播放,需要将音频数据从Java层拷贝到native层,对系统资源的消耗是巨大的。为了减少数据的拷贝,开发更加高效的Android音频应用,能够直接在native层处理音频数据显得尤为重要。本文介绍将FFmpeg与OpenSL ES的数据结构,在native层使用FFmpeg的解码过程,及使用OpenSL ES对音频数据的播放处理研究。

关键词: Android;Ffmpeg;OpenSLES;解码;音频播放

中图分类号: TP311.52    文献标识码: A    DOI:10.3969/j.issn.1003-6970.2020.10.008

本文著录格式:王强,洪蕾. 基于Android平台的音频播放处理研究与实现[J]. 软件,2020,41(10):3133

【Abstract】: With the popularity of Android terminal devices, a large number of audio applications based on the Android platform have emerged. For audio processing, Android provides MediaPlayer to meet the needs of developers for audio processing. MediaPlayer needs to copy audio data from the Java layer to the native layer for audio acquisition, decoding and playing, which consumes a lot of system resources. In order to reduce the copy of data, to develop more efficient Android audio applications, it is particularly important to be able to directly process audio data in the native layer. This paper introduces the data structure of FFmpeg and OpenSL ES, the decoding process of FFmpeg in the native layer, and the playback processing of audio data using OpenSL ES.

【Key words】: Android; FFmpeg; OpenSLES; Decoded; Audio playback

0  引言

Android[1-4]終端设备的普及,让人们对安卓应用的体验有着越来越高的需求。音频的处理包含着许多方面,如音乐播放[5],音频录制等。手机性能的局限导致对安卓应用在控制性能消耗有着高的需求,所以开发人员在完成音频应用开发的时候选择一个合适的方案是必要的。

Android提供了MediaPlayer对音频进行播放处理,而MediaPlayer在处理音频上对系统资源有着巨大的消耗。采用FFmpeg与OpenSLES,可以让应用层传递目标音频的资源地址,使得FFmpeg直接在native层对资源进行访问,解码音频,然后将数据传递给OpenSLES进行处理。降低性能消耗。

1  关键技术研究

1.1  FFmpeg

FFmpeg[6]是一套用来记录,转换数字音频、视频,并能将其转化为流的开源程序。提供了转换、录制与音视频流化的完整解决方案,不仅包含了编解码、流化音频文件的功能,还可以对音频文件进行录制、转换等工作[7]。

通过对FFmpeg的研究,给出其对音频数据的解码流程,如图1所示。

1.2  OpenSL ES

OpenSL ES[8]是针对嵌入式系统调整的无授权费,跨平台,硬件加速的音频API。它为嵌入式移动多媒体设备上的应用程序开发人员提供了一套标准化,高性能,低延迟的音频处理方案,从而实现了硬件和软件音频功能的直接跨平台部署。

OpenSL ES作为音频开发的API,它相较于Android提供的Java层API,如MediaPlayer,减少了数据在Java层和native层的拷贝,提高效率,并配合FFmpeg,播放解码转码后的PCM音频数据。

通过对OpenSL ES的研究,给出其对音频数据的播放流程,如图2所示。

2  程序结构与方案设计

2.1  程序结构设计

对于程序的结构设计,给出其结构图,如图3所示。

为了方便开发者的调用,在对native层的调用上封装了一层代码,即程序结构图中的AudioPlayer类,向外提供操作接口,实现了播放,暂停,停止与循环等功能。

在AudioPlayer类中,使用native关键字声明与native层交互的函数,并一一对应在native-lib类中进行实现。通过Java的JNI机制让Java层与底层C++进行交互,即对native-lib类函数进行调用,具体表现为,向native-lib类传递音频的资源地址,通过对AudioPlayer类提供的音频控制函数,调用native-lib类中对应的控制方法,对音频的播放暂停,占用资源释放与循环进行控制。

在native-lib类中,通过对Decoder类的调用完成解码,播放,暂停,资源释放的功能。

在Decoder類中,首先通过FFmpeg完成对音频信息的采集,解码器的初始化与音频解码的工作,并将解码完成的音频数据缓存入SafeQueue类中,再通过调用Audio类进行对音频的处理工作。

通过Decoder类的调用,Audio类会进行对OpenSL ES的相关初始化,并等待FFmpeg解码完成的数据,与音频编码转码,然后进行播放。

2.2  方案设计

对于FFmpeg的解码[9]设计,给出其结构图,如图4所示。

通过FFmpeg进行对数据源的解码。这里涉及两个重要的结构体。AVFormatContext,用于存储音频格式中的信息,AVCodecContext,用于存储音频解码器信息[10]。由于解码是个耗时操作,需要开启子线程进行解码,由于Android系统是基于Linux内核,而Linux又是遵循POSIX线程标准的,所以采用POSIX线程创建子线程。完成解码需要执行以下几个操作:①FFmpeg通过av_register_all()注册编解码器,avformat_network_ init()进行网络初始化,以便FFmpeg可以直接访问网络地址。②通过avformat_open_input(),打开输入文件流,读取数据并判断文件编码格式,将格式信息存入AVFormatContext结构体中。③通过avformat_find_ stream_info(),获得文件的编码信息,将信息存入AVFormatContext结构体中。④遍历AVFormatContext结构体中的数据流,根据类型判断,找到音频流,保存音频流的索引,并保存至音频类对象Audio中。⑤通过avcodec_find_decoder()函数与AVFormatContext中保存的文件编码格式找到相应的解码器⑥通过avcodec_parameters_to_context(),将音频流信息保存至AVCodecContext结构体中⑦通过avcodec_open2()打开解码器。⑧通过av_read_frame()读取原始音频数据帧AVPacket,如果读取失败,则回调AudioPlayer类,通知调用者,反之,如果成功,则将数据存入SafeQueue类的帧队列中。由于解码的速度往往远大于音频播放的速度,所以需要对解码完成后的数据进行缓存,先解码好的先播放,利用队列这个数据结构。实现队列的存、取、获取队列长度与清空队列操作。因为音频数据是边解码边播放的,在对数据的存与取时可能会产生冲突,所以对于队列的存与取需要进行同步操作,这里通过POSIX线程,进行加锁,实现对队列的同步。

由于利用OpenSL ES进行播放,在播放之前需要对OpenSL ES进行初始化,在循环解码原始数据帧的同时,进行OpenSL ES初始化,并启用回调函数。

对于播放的配置回调给出具体流程图,如图5所示。

通过接口对象的创建,设置播放数据类型为PCM数据,16位量化位数,双声道,立体声与采样率,播放状态为播放,并设置播放回调,监测数据的传递。

由于音频编码格式多样,需要对原始音频帧AVPacket进行重采样,生成PCM数据,采样标准为,每秒采样音频个数44100 HZ,采样位数16 bit,输出声道为双声道。

给出每个采样点数据大小的计算公式:

size(数据长度) = 采样个数 * 声道数 * 单个采样点大小

对于重采样并实现播放的具体流程图,如图6所示。

从SafeQueue类的缓冲队列中通过popAVPacket()函数获得原始音频数据AVPacket,通过avcodec_ send_packet()函数进行解封装,得到音频帧,并将其保存在AVCodecContext中,通过avcodec_receive_frame()函数获得音频帧,利用swr_alloc_set_opts()函数设置转码后的PCM音频数据参数,最后通过swr_convert()函数从音频帧中获得转码后的一帧PCM数据,这时OpenSL ES监测到回调,将PCM数据通过Enqueue()函数加入播放队列,完成播放。

3  对比MediaPlayer

3.1  音频数据加载时间对比

MediaPlayer的加载时间,与本文方案(AudioPlayer)加载时间(单位:秒)对比如表1所示。

经过试验测算,在对同一资源地址进行播放时,AudioPlayer所需要的时间约为0.2 S,MediaPlayer所需的时间约为0.45 s。

3.2  播放音频时内存增量对比

MediaPlayer与本文方案(AudioPlayer)的播放音频时内存的增量对比如图7所示。

经过试验测算,在对同一资源地址进行播放时,AudioPlayer所占用的内存均值约为2.5 MB,MediaPlayer所占用的内容存均值约为4.6 MB。

4  结论

采用FFmpeg与OpenSL ES实现了对音频数据的播放处理,提供了一个较好的解决方案。相比与MediaPlayer不仅提高了数据的加载效率,还减少了Java层和native层之间的数据拷贝,符合了手机性能的需求,提升了用户的体验性。

参考文献

[1]王翠香, 邵星. 基于安卓的大学生掌上论坛系统设计[J]. 软件, 2015, 36(10): 33-35.

[2]何艳江, 吕鹏, 颜溯, 等. 基于安卓平台的复合地基处理软件开发[J]. 软件, 2015, 36(12): 42-44.

[3]姚永明, 梅雨凯, 章香, 等. 基于安卓的南邮通达掌上校园 APP 的需求分析[J]. 软件, 2018, 39(8): 45-47.

[4]姚永明, 梅雨凯, 章香, 等. 基于安卓的南邮通达掌上校园 APP 的实现[J]. 软件, 2018, 39(8): 48-51.

[5]张小琴, 张庚. 基于 Android 平台的音乐播放器设计与实现[J]. 软件, 2018, 39(9): 113-116.

[6]邓正良. 基于FFmpeg和SDL的视频流播放存储研究综述[J]. 现代计算机, 2019(22): 47-50.

[7]石佩青. 基于Android系统在线音乐播放器的设计与实现[D]. 北京邮电大学, 2017.

[8]张希龙. 基于Android平台的助听器系统设计和实现[D]. 东南大学, 2017.

[9]罗潇. Android多媒体平台下基于FFMPEG的音视频处理方案研究[D]. 暨南大学, 2016.

[10]Research and Implementation of Video Codec Based on FFmpeg. ZENG Hao, ZHANG Zhi-yong, SHI Lul-in. 2016 International Conference on Network and Information Systems for Computers. 2016.

猜你喜欢

解码
《解码万吨站》
《解码万吨站》
《解码万吨站》
解码eUCP2.0
NAD C368解码/放大器一体机
Quad(国都)Vena解码/放大器一体机