本文目录一览:
- 1、Android音视频【十二】使用OpenSLES和AudioTrack进行播放PCM
- 2、哔哩哔哩上缓到本地的视频没有声音是怎么回事?
- 3、如何使用OpenSL ES在c4droid中进行声音编程
- 4、Android使用FFmpeg播放视频(二):音频播放
Android音视频【十二】使用OpenSLES和AudioTrack进行播放PCM
本节我们学习下如何播放pcm数据,在Android中有两种方法:一种是使用java层的 AudioTrack 方法,一种是使用底层的 OpenSLES 直接在 jni 层调用系统的 OpenSLES的c方法 实现。
两种使用场景不一样:
AudioTrack 一般用于 比如本地播放一个pcm文件/流,又或者播放解码后的音频的pcm流,API较简单。
OpenSLES 一般用于一些播放器中开发中,比如音频/视频播放器,声音/音频的播放采用的OpenSLES,一是播放器一般是c/c++实现,便于直接在c层调用OpenSLES的API,二也是如果用AudioTrack进行播放,务必会带来java和jni层的反射调用的开销,API较复杂。-opensles源文件
可以根据业务自行决定来进行选择。
AudioTrack的方式使用较简单,直接在java层。
指定采样率,采样位数,声道数进行创建。
其中44100是采样率, AudioFormat.CHANNEL_OUT_STEREO 为双声道,还有 CHANNEL_OUT_MONO 单声道。 AudioFormat.ENCODING_PCM_16BIT 为采样位数16位,还有 ENCODING_PCM_8BIT 8位。 minBufferSize 是播放器缓冲的大小,也是根据采样率和采样位数,声道数 进行获取,只有满足最小的buffer才去操作底层进程播放。-opensles源文件
最后一个参数mode。可以指定的值有 AudioTrack.MODE_STREAM 和 AudioTrack.MODE_STATIC 。
MODE_STREAM 适用于大多数的场景,比如动态的处理audio buffer,或者播放很长的音频文件,它是将audio buffers从java层传递到native层。音频播放时音频数据从Java流式传输到native层的创建模式。-opensles源文件
MODE_STATIC 适用场景,比如播放很短的音频,它是一次性将全部的音频资源从java传递到native层。音频数据在音频开始播放前仅从Java传输到native层的创建模式。
是的,就这么一个方法。注意此方法是同步方法,是个耗时方法,一般是开启一个线程循环调用 write 方法进行写入。
注意在调用 write 方法前需要调用 audioTrack.play() 方法开始播放。
因为是pcm裸数据,无法像mediaplayer一样提供了API。所以需要自己处理下。可以利用 getPlaybackHeadPosition 方法。
getPlaybackHeadPosition() 的意思是返回以帧为单位表示的播放头位置
getPlaybackRate() 的意思是返回以Hz为单位返回当前播放采样率。
所以当前播放时间可以通过如下方式获取
OpenSLES:(Open Sound Library for Embedded Systems).
OpenSLES是跨平台是针对嵌入式系统精心优化的硬件音频加速API。使用OpenSLES进行音频播放的好处是可以不依赖第三方。比如一些音频或者视频播放器中都是用OpenSLES进行播放解码后的pcm的,这样免去了和java层的交互。-opensles源文件
在Android中使用OpenSLES首先需要把Android 系统提供的so链接到外面自己的so。在CMakeLists.txt脚本中添加链接库OpenSLES。库的名字可以在 类似如下目录中
需要去掉lib
然后导入头文件即可使用了OpenSLES提供的底层方法了。
创建使用的步骤大致分为:
一个 SLObjectItf 里面可能包含了多个Interface,获取Interface通过 GetInterface 方法,而 GetInterface 方法的地2个参数 SLInterfaceID 参数来指定到的需要获取Object里面的那个Interface。比如通过指定 SL_IID_ENGINE 的类型来获取 SLEngineItf 。我们可以通过 SLEngineItf 去创建各种Object,例如播放器、录音器、混音器的Object,然后在用这些Object去获取各种Interface去实现各种功能。-opensles源文件
如上所说,SLEngineItf可以创建混音器的Object。
在创建播放器前需要创建音频的配置信息(比如采样率,声道数,每个采样的位数等)
开始播放后会不断的回调这个 pcmBufferCallBack 函数将音频数据压入队列
(*pcmBufferQueue)-RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);
如果想要暂停播放参数直接设置为SL_PLAYSTATE_PAUSED,若暂停后继续播放设置参数为SL_PLAYSTATE_PLAYING即可。若想要停止播放参数设置为SL_PLAYSTATE_STOPPED即可。-opensles源文件
首先获取播放器的用于控制音量的接口SLVolumeItf pcmVolumePlay
然后动态设置
首先也是获取播放器的用于控制音量的接口SLMuteSoloItf pcmMutePlay
然后动态设置
看起来控制还是蛮简单的哈。先熟悉这么多,OpenSLES还是蛮强大的。
哔哩哔哩上缓到本地的视频没有声音是怎么回事?
一般来说,只要自己缓存好的视频不管什么时候看都是可以的,就算最后作者将源视频删除,哔哩哔哩软件也不会将你缓存的内容删除,所以我认为问题应该是缓存本身。如果你设置的缓存路径是外置内存卡(内存卡等等),建议重新插拔再尝试。如果路径设置的是内置储存卡,建议重启手机或软件,或者仔细回想一下最近有没有进行相关的文件清理。(文件清理不当很容易导致缓存数据丢失,进而导致视频无法观看)-opensles源文件
找到哔哩哔哩上想缓存的视频(以下是我的心酸吐槽⊙﹏⊙缓存视频倒是不麻烦,但是找到手机中的缓存文件就费了好长时间,然后把文件后缀改成mp4,发现视频没声音,傻乎乎的我还把媒体音量开到了最大5555……大晚上的还被自己吓了一跳,然鹅,我发现这视频根本就没啥子声音嘛,遂,重新缓存视频,走迷宫似的找到文件,把媒体音量开到最大,再试一哈!!!然鹅,还是没卵用[摊手]。上网一搜才知道!原来!!哔哩哔哩新版的缓存是音频和视频分开的!!!然后就各种扒拉音视频合成的软件教程,一晚下了好多软件(剪映,小影)但是别的软件找不到帮同事下载的讲课视频,就很心塞╯△╰最后一把鼻涕一把泪的,无意中从原始浏览器的网页上找到这个网址,真的好希望大家都能看到我的回答啊!能省去好多好多时间!希望这个网址可以一直有用!!!嘿嘿嘿)-opensles源文件
你是用Android吗?我对象用iOS给我看,有声音。 客服说得Android 10才能录其他APP的。然后客服推荐我用影视厅的投屏功能,只是看视频的话,比同屏好用,可以把b站的视频投过来两个人一起看。如果是本地视频,直接在同屏库打开就行,有声音。-opensles源文件
如何使用OpenSL ES在c4droid中进行声音编程
使用opensl es 的前提条件是在源代码中:
#include SLES/OpenSLES.h, 包含 opensl es的头文件
在gcc中:
-lOpenSLES 链接 opensl es的库文件
大休步骤如下:
1.创建声音引擎
2.创建声音播放器
3.设置播放缓冲
opensl es 主要操作的是 介面(interface),大体来说有如下几个介面:
SLObjectItf : 对象介面,
SLEngineItf :引擎介面
SLPlayItf:播放介面
SLBufferQueueItf :缓冲队列介面
SLEffectSendItf:音效发送介面
SLMuteSoloItf : 声道关闭 /单声道介面
SLVolumeItf : 声量介面
除了 slCreateEngine 这一条函数是用来创建声音引擎对象介面以外,其它的所有操作都是通过介面的成员函数完成的,现在我们来研究如何初始化声音引擎
初始化声音引擎需要3个介面,我们要将它们声明为全局变量:
SLObjectItf _aud;/* 声音引擎的对象介面 */
SLEngineItf _aud_eng;/* 声音引擎 */
SLObjectItf _aud_mix;/* 混音器对象介面 */
首先我们创建声音引擎的对象介面 :
slCreateEngine(_aud, 0, NULL, 0, NULL, NULL);
创建之后的介面并不能立即使用,我们首先要通过介面的子函数实现(Realize)它:
(*_aud)-Realize(_aud, SL_BOOLEAN_FALSE);/* 通过_aud的Realize子函数实现声音引擎的对象介面 */
实现之后,我们从声音引擎的对象中抓取声音引擎,在这里我们通过介面的子函数抓取介面 (GetInterface),抓取对像是 _aud, 抓取类型是引擎,抓取后存放的内存位置是我们先前声明的引擎介面_aud_eng的指针位置。指令如下:-opensles源文件
(*_aud)-GetInterface(_aud, SL_IID_ENGINE, _aud_eng);
这样声音引擎就初始化了
第三步我们要做的是从声音引擎中创建 "输出混音器" 对象,这样我们才可以将声音播放出来。
注意,同 声音引擎的对象一样, 由于 "输出混音器" 是对象,我们必须创建后在实现(Realize)它。
由于"输出混音器"创建的同时需要两个音效参数,所以我们先准备好这两个参数:第一个参数类型是声音介面编号(SLInterfaceID),是一个数组,我们可以将其看成一个音效配置列表,在其中放置不同的音效编号。第二个参数是一个逻辑数组:是否强制实现声音介面编号中对应音效,如果某个音效的逻辑为真(SL_BOOLEAN_TRUE),则在无法实现该音效时视为"输出混音器"创建失败,否则将忽略该音效。因为安卓的声硬件对音效的支持不同,所以最好不要强制实现,所以我们在第二个数组中全部填写SL_BOOLEAN_FALSE 。-opensles源文件
现在我们开始创建"输出混音器",环境回响(SL_IID_ENVIRONMENTALREVERB )是比较常见的音效,我们将非强制性的使用该音效。
SLInterfaceID effect[1] = {SL_IID_ENVIRONMENTALREVERB}; /*环境回响音效 */
SLboolean effect_bool[1] = {SL_BOOLEAN_FALSE}; /* 回响音效强制实现逻辑 */
(*_aud_eng)-CreateOutputMix(_aud_eng, _aud_mix, 1, effect, effect_bool);/* 从声音引擎中创建“输出混音器” */
(*_aud_mix)-Realize(_aud_mix, SL_BOOLEAN_FALSE); /* 实现刚创建的“输出混音器” */
样一来,所有的初始化工作就全部完成了。我们获得如下完整代码:
SLObjectItf _aud;/* 声音引擎对象 */
SLEngineItf _aud_eng;/* 声音引擎 */
SLObjectItf _aud_mix;/* 输出混音器对象 */
/* audio_init: 初始化opensl es */
int audio_init ()
{
SLInterfaceID effect[1] = {SL_IID_ENVIRONMENTALREVERB}; /* 音效 */
SLboolean effect_bool[1] = {SL_BOOLEAN_FALSE}; /*音效强制实现逻辑 */
slCreateEngine(_aud, 0, NULL, 0, NULL, NULL);/* 创建声音引擎对象 */
(*_aud)-Realize(_aud, SL_BOOLEAN_FALSE);/* 实现声音引擎对象 */
(*_aud)-GetInterface(_aud, SL_IID_ENGINE, _aud_eng);/* 从声音引擎对象中抓取声音引擎 */
(*_aud_eng)-CreateOutputMix(_aud_eng, _aud_mix, 1, effect, effect_bool);/* 通过声音引擎创建输出混音器对象,并且非强制性的开启环境混响效果 */-opensles源文件
(*_aud_mix)-Realize(_aud_mix, SL_BOOLEAN_FALSE);/* 实现混音器对象 */
}
初始化引擎之后, 我们还要创建一个播放器对象,就可以在播放器中输出声音了
播放器对象和输出混音器一样,是对象,创建后需要进行实现:
SLObjectItf _aud_plyobj; /*播放器对象 */
(*_aud_eng)-CreateAudioPlayer(_aud_eng, _aud_plyobj, sndsrc, sndsnk, 3, ids, req);
/* 这是创建声音播放器对象aud_plyobj的函数 */
前两个参数分别把 声音引擎 和 播放器引擎的指针位置填上就可以了
我们真正需要填写的是后面4个参数 :
sndsrc: 声音数据源
sndsnk:声音输出池(data sink),也就是声音输出设备
3:功能清单数目,我们要让播放器支持3种功能
ids: 功能清单,我们要让播放器支持的功能
req:功能强制实现逻辑
Android使用FFmpeg播放视频(二):音频播放
Android使用FFmpeg播放视频(一):视频播放
Android NDK开发:利用OpenSL ES实现声音播放
这里我创建了两个JNI函数,一个是播放的,一个是释放的如下:
这里我在用于播放的JNI函数中依次初始化了FFmpeg和OpenSLES
其中初始化FFmpeg的函数中的逻辑其实和使用FFmpeg播放视频画面中的逻辑差不多,主要区别就是要找到音频的索引以及后面对于解析音频的一些配置;而初始化OpenSLES基本就和之前使用OpenSLES播放PCM数据是一样的,具体如下:-opensles源文件
最后再加入释放资源的逻辑即可
这里的案例源码是和之前播放视频画面的分开了