使用OpenSL直接播放mp3

前言

通过使用OpenSL来播放一个mp3文件来学习openSL的使用方式。

设计

在android平台播放mp3方式有多种方式入使用MediaPlayer、AudioTrack、OpenSL、oboe等。根据使用MediaPlayer,AudioTrack的经验一个播放器需要有的基础功能有加载数据、开始、暂停、停止、销毁等。
我们可以设计一个播放器它具有开始播放、暂停、停止、调整进度等方式。
那么我们需要设计一个类它应该支持以下功能:

  1. 初始化:初始化SLEngine、构建OpenSL层的SLAudioPlayer、设置输入uri、设置输出。
  2. 开始播放
  3. 暂停播放
  4. 停止播放
  5. 获取当前的播放进度/设置当前的进度
  6. 释放资源

JNI层定义

所以我们的JNI层提供的接口为:

    private native long initialize(String filePath);private native int start(long cPtr);private native int pause(long cPtr);private native int stop(long cPtr);private native int destroy(long cPtr);private native long getDuration(long cPtr);private native long getPosition(long cPtr);private native int seek(long cPtr, long position);

OpenSL API的使用

OpenSL的API提供的是C API,但是它的设计是面向对象的。它定义了对象(object),每个对象也会有一组接口。

  • 接口分为3类:显示的、隐式的、和动态的。显示需要在创建对象指明,如果对象不支持这个接口的feature那么创建对象将失败。隐式接口则不需要,可以直接获取。还有动态接口,动态接口可以使用SLDynamicInterfaceManagementItf来进行添加或删除
  • 每个接口都有一个ID
  • 对象在使用时需要初始化

    套用官方文档的图,这张图描述了一个对象的状态转换。对象一开始是Unrealized可以由用户侧使用Realize初始化,初始化是为了获取资源。对象也有可能被系统转换为Suspended的状态,这是因为它持有的资源有可能被其他高优先级的对象偷走。用户可以通过Resume以及设置自身更高的优先级转会为Realized。用户在使用完对象后可以调用destroy销毁对象系统会进行资源释放。

在来看一张图:
这种图中矩形AudioPlayer、Output Mix、Engine 就属于OpenSL中的对象,他们身上伸出来的小火柴就属于接口,接口的命名都是xxxxItf这种。我们操作一个对象,都是创建一个对象,然后获取它的某个接口,这种接口中会有一组方法可以供我们使用。
另外,这个图也正好可以表示我们用OpenSL播放一个本地mp3文件的设计图。

好了,我们来看代码如何操作。

  1. 创建SLEngine并获取EngineItf接口
// 创建SLEnginueObject
SLresult result = slCreateEngine(&mp3Engine->slObjectItf_,0, nullptr,0, nullptr,0);
// 初始化
(*mp3Engine->slObjectItf_)->Realize(mp3Engine->slObjectItf_, SL_BOOLEAN_FALSE);// 获取接口
(*mp3Engine->slObjectItf_)->GetInterface(mp3Engine->slObjectItf_, SL_IID_ENGINE,&mp3Engine->slEngineItf_);

slCreateEnginue 这个API参数的定位为:

我这里第5,6个参数传的0,null,意思是不需要指明需要那些接口。但是后面的代码仍通过(*mp3Engine->slObjectItf_)->GetInterface(mp3Engine->slObjectItf_, SL_IID_ENGINE, &mp3Engine->slEngineItf_);来获取到了SLEngineItf,这也说明这个接口是engine的隐式接口。
隐式接口定义的原文:

An object’s type determines the set of interfaces that will always exist, regardless of whether the application requests them or not. These interfaces are called implicit

显示接口定义的原文:

Every object type also defines a set of interfaces that are available on objects of this type, but will not be exposed by an object unless explicitly requested by the application during the object’s creation. These explicitly requested interfaces are called explicit interfaces

  1. 有了SLEnginueItf 我们就可以创建AudioPlayer,创建的同时我们需要为这个AudioPlayer构建输入和输出,它的输入就是本地Uri,输出OutputMix.
// 构建输入SLDataLocator_URI_ dataSourceUrl = {.locatorType = SL_DATALOCATOR_URI,.URI = (SLchar *) mp3Engine->uri_};SLDataFormat_MIME inputFormat = {.formatType = SL_DATAFORMAT_MIME,.mimeType = (SLchar *) "audio/mpeg",.containerType = SL_CONTAINERTYPE_MP3};SLDataSource slDataSource{.pLocator = &dataSourceUrl,.pFormat = &inputFormat,};
// 构建输入结束
// 开始构建输出SLObjectItf outputMix;(*mp3Engine->slEngineItf_)->CreateOutputMix(mp3Engine->slEngineItf_,&outputMix,0,nullptr,0);(*outputMix)->Realize(outputMix, SL_BOOLEAN_FALSE);SLDataLocator_OutputMix outputMixLocator = {.locatorType = SL_DATALOCATOR_OUTPUTMIX,.outputMix = outputMix,};
// 输出构建结束    SLDataSink slDataSink = {.pLocator = &outputMixLocator,.pFormat = nullptr};
// 设置必须支持的接口,SL_IID_PLAY 我们用来控制开始/暂停/停止播放等等
// SL_IID_SEEK 用来控制播放的进度SLInterfaceID interfaces[2]{SL_IID_PLAY,SL_IID_SEEK,};SLboolean itfResult[2]{SL_BOOLEAN_TRUE,SL_BOOLEAN_TRUE,};
// 创建播放器(*mp3Engine->slEngineItf_)->CreateAudioPlayer(mp3Engine->slEngineItf_,&mp3Engine->slAudioPlayer_,&slDataSource,&slDataSink,2,interfaces,itfResult);(*mp3Engine->slAudioPlayer_)->Realize(mp3Engine->slAudioPlayer_, SL_BOOLEAN_FALSE);
  1. 播放器object创建成功之后,我们需要获取这个播放器的播放控制接口、控制进度的接口
 (*mp3Engine->slAudioPlayer_)->GetInterface(mp3Engine->slAudioPlayer_, SL_IID_PLAY,&mp3Engine->slPlayItf_);result = (*mp3Engine->slAudioPlayer_)->GetInterface(mp3Engine->slAudioPlayer_,SL_IID_SEEK, &mp3Engine->slSeekItf_);
  1. 获取了播放器相关接口后,后续的开始/暂停/停止等就非常的简单,只需要改变播放器的状态就好了
// 开始播放
NIEXPORT jint JNICALL
Java_com_blueberry_videoplayer_SLPlayMp3JniLib_start(JNIEnv *env, jobject thiz, jlong cptr) {auto *mp3EnginePtr = reinterpret_cast<Mp3Engine *>(cptr);auto mp3Engine = *mp3EnginePtr;SLuint32 state;(*mp3Engine.slPlayItf_)->GetPlayState(mp3Engine.slPlayItf_, &state);if (state == SL_PLAYSTATE_PLAYING) {return 0;}(*mp3Engine.slPlayItf_)->SetPlayState(mp3Engine.slPlayItf_, SL_PLAYSTATE_PLAYING);return 1;
}
// 暂停播放
JNIEXPORT jint JNICALL
Java_com_blueberry_videoplayer_SLPlayMp3JniLib_pause(JNIEnv *env, jobject thiz, jlong cptr) {auto *mp3EnginePtr = reinterpret_cast<Mp3Engine *>(cptr);auto mp3Engine = *mp3EnginePtr;SLuint32 state;(*mp3Engine.slPlayItf_)->GetPlayState(mp3Engine.slPlayItf_, &state);if (state == SL_PLAYSTATE_STOPPED) {return 0;}if (state == SL_PLAYSTATE_PAUSED) {(*mp3Engine.slPlayItf_)->SetPlayState(mp3Engine.slPlayItf_, SL_PLAYSTATE_PLAYING);} else {(*mp3Engine.slPlayItf_)->SetPlayState(mp3Engine.slPlayItf_, SL_PLAYSTATE_PAUSED);}return 1;
}
// 停止播放
JNIEXPORT jint JNICALL
Java_com_blueberry_videoplayer_SLPlayMp3JniLib_stop(JNIEnv *env, jobject thiz, jlong cptr) {auto *mp3EnginePtr = reinterpret_cast<Mp3Engine *>(cptr);auto mp3Engine = *mp3EnginePtr;SLuint32 state;(*mp3Engine.slPlayItf_)->GetPlayState(mp3Engine.slPlayItf_, &state);if (state == SL_PLAYSTATE_STOPPED) {return 0;}(*mp3Engine.slPlayItf_)->SetPlayState(mp3Engine.slPlayItf_, SL_PLAYSTATE_STOPPED);return 1;
}// 获取播放总时长
JNIEXPORT jlong JNICALL
Java_com_blueberry_videoplayer_SLPlayMp3JniLib_getDuration(JNIEnv *env, jobject thiz, jlong c_ptr) {auto *mp3EnginePtr = reinterpret_cast<Mp3Engine *>(c_ptr);auto mp3Engine = *mp3EnginePtr;SLmillisecond duration;(*mp3Engine.slPlayItf_)->GetDuration(mp3Engine.slPlayItf_, &duration);return duration;
}
// 设置播放进度
JNIEXPORT jint JNICALL
Java_com_blueberry_videoplayer_SLPlayMp3JniLib_seek(JNIEnv *env, jobject thiz, jlong c_ptr,jlong position) {auto *mp3EnginePtr = reinterpret_cast<Mp3Engine *>(c_ptr);auto mp3Engine = *mp3EnginePtr;(*mp3EnginePtr->slSeekItf_)->SetPosition(mp3Engine.slSeekItf_, position, SL_SEEKMODE_FAST);return 1;
}
// 获取当前的播放进度
JNIEXPORT jlong JNICALL
Java_com_blueberry_videoplayer_SLPlayMp3JniLib_getPosition(JNIEnv *env, jobject thiz, jlong c_ptr) {auto *mp3EnginePtr = reinterpret_cast<Mp3Engine *>(c_ptr);auto mp3Engine = *mp3EnginePtr;SLmillisecond position;(*mp3Engine.slPlayItf_)->GetPosition(mp3Engine.slPlayItf_, &position);return position;
}
  1. 释放资源
JNIEXPORT jint JNICALL
Java_com_blueberry_videoplayer_SLPlayMp3JniLib_destroy(JNIEnv *env, jobject thiz, jlong cptr) {auto *mp3EnginePtr = reinterpret_cast<Mp3Engine *>(cptr);auto mp3Engine = *mp3EnginePtr;(*mp3EnginePtr->slPlayItf_)->SetPlayState(mp3Engine.slPlayItf_, SL_PLAYSTATE_STOPPED);(*mp3Engine.slObjectItf_)->Destroy(mp3Engine.slObjectItf_);delete mp3EnginePtr;return 1;
}

参考

http://supercurio.project-voodoo.org/ndk-docs/docs/opensles/
https://www.khronos.org/registry/OpenSL-ES/specs/OpenSL_ES_Specification_1.1.pdf

使用OpenSL直接播放mp3相关推荐

  1. Android 音视频深入 十四 FFmpeg与OpenSL ES 播放mp3音乐,能暂停(附源码

    项目地址 https://github.com/979451341/FFmpegOpenslES 这次说的是FFmpeg解码mp3,数据给OpenSL ES播放,并且能够暂停. 1.创建引擎 slCr ...

  2. Android 音视频深入 十四 FFmpeg与OpenSL ES 播放mp3音乐,能暂停(附源码下载)

    项目地址 https://github.com/979451341/FFmpegOpenslES 这次说的是FFmpeg解码mp3,数据给OpenSL ES播放,并且能够暂停. 1.创建引擎 slCr ...

  3. CentOS 6.x 播放 mp3 音乐 —— 成功

    CentOS 6.x 播放 mp3 音乐 -- 成功 参考:http://blog.chinaunix.net/uid-14735472-id-3472898.html centos 6.x  添加 ...

  4. 使用go语言GUI库实现对mp3文件的播放1(简单的播放mp3文件)

    使用go语言GUI库实现对mp3文件的播放1(简单的播放mp3文件) 使用beep播放mp3文件(10num) 使用go语言GUI库fyne实现音乐播放器 要是想使用go语言实现播放mp3需要借助be ...

  5. ubuntu 13.10 Rhythmbox不能播放mp3 和中文乱码的问题

    1.ubuntu 13.10 Rhythmbox不能播放mp3的解决方法 软件中心搜索(ubuntu额外的版权受限软件)不带括号 2.中文乱码问题解决方法: 终端顺序操作 : 1.  sudo ged ...

  6. android 播放MP3实例

    在android中播放mp3非常简单,也是项目中经常使用的,比如说要做项目的背景音乐,应用中某些功能的提示音等的.应用非常广泛,下面提供一个简单的使用实例: layout文件的配置: <?xml ...

  7. Android实现mp3分析,Android编程实现播放MP3功能示例

    本文实例讲述了Android编程实现播放MP3功能.分享给大家供大家参考,具体如下: 在android中播放mp3非常简单,也是项目中经常使用的,比如说要做项目的背景音乐,应用中某些功能的提示音等的. ...

  8. python怎么播放mp3_python上播放mp3歌曲

    试试这个.它过于简单但可能不是最好的方法. from pygame import mixer # Load the required library mixer.init() mixer.music. ...

  9. android audiotrack mp3,播放mp3数据压缩由JLayer和Audiotrack latin mp3在android

    我跟着这个example转换来自AudioRecord原始音频数据为MP3,并且它成功发生,如果我将这个数据存储在一个文件的MP3文件和播放与音乐播放器,然后它是可以听到的.播放mp3数据压缩由JLa ...

最新文章

  1. WEP保护帧移除工具airdecloak-ng
  2. 《Java编程思想》学习笔记4——集合容器
  3. for循环中new的对象什么时候被回收_你真的了解JS里的 new 吗?
  4. 多人协作代码--公共库的引用与业务约定
  5. mysql 排他,mysql 共享锁 排他锁 防插入锁
  6. ThreadLocal到底是什么,尚硅谷docker高级
  7. fastdfs 报错 err: TrackerTask RecvHeader recv resp status 28 != 0
  8. mybatis查询树形数据的两种方法
  9. 计算机考研数据结构参考书,2016计算机考研:数据结构参考书及其复习重点
  10. AI学习教程:AI(Adobe lliustrator)快速入门
  11. gg修改器修改数值没有用怎么办_gg修改器怎么用教学 gg修改器修改游戏方法介绍...
  12. 基于F340 实现Bridge功能(二):上位机应用程序编写
  13. FFmpeg获取视频正确的宽高比
  14. 如何降低网站跳出率方法
  15. 安装CHD走过的一些坑
  16. Win10系统下怎么开启管理员administrator权限?
  17. docker docker安装app
  18. 国产Linux系统再添一员猛将,颜值完全不输苹果!
  19. 你想拥有一台可以水下通话的手机吗?
  20. CSS3D导航栏翻转效果

热门文章

  1. 宏基因组专题生信分析学习总结
  2. 中国羊毛针织纱行业市场供需与战略研究报告
  3. 文本的换行与包裹 之我都忘了我写过 break-word
  4. Linux之uniq命令详解
  5. 计算机linux试题及答案,Linux_期末考试试题(含答案)
  6. bWAPP 玩法总结
  7. 短信猫 java 开发包,程序员福音!BAT企业联合出品《Java开发手册》强势来袭
  8. 复选框选中状态html,javascript中如何判断checkbox是否选中?
  9. Spring i18n 国际化样例
  10. string头文件详解