使用OpenSL直接播放mp3

article/2025/7/29 2:17:22

使用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


http://chatgpt.dhexx.cn/article/fdYbGn6K.shtml

相关文章

音视频开发之旅(36) -FFmpeg +OpenSL ES实现音频解码和播放

目录 OpenSL ES基本介绍OpenSL ES播放音频流程代码实现遇到的问题资料收获 上一篇我们通过AudioTrack实现了FFmpeg解码后的PCM音频数据的播放&#xff0c;在Android上还有一种播放音频的方式即OpenSL ES, 什么是OpenSL ES&#xff0c;这个我们平时接触的很少&#xff0c;原因…

Android OpenSL ES 开发:Android OpenSL 介绍和开发流程说明

一、Android OpenSL ES 介绍 OpenSL ES (Open Sound Library for Embedded Systems)是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API。它为嵌入式移动多媒体设备上的本地应用程序开发者提供标准化, 高性能,低响应时间的音频功能实现方法&#xff0c;并实现软/硬件…

Xcode7 网络请求报错:The resource could not be loaded because the App Transport Security policy requir...

Google后查证&#xff0c;iOS9引入了新特性App Transport Security (ATS)。详情&#xff1a;App Transport Security (ATS)新特性要求App内访问的网络必须使用HTTPS协议。但是现在公司的项目使用的是HTTP协议&#xff0c;使用私有加密方式保证数据安全。现在也不能马上改成HTTP…

【用户自定义气象站】

自定义气象站系统由数据采集器、传感器、总线模块、网络模块、供电模块等组成&#xff0c;可实现野外无人看守的情况下长期监测。用户可根据自己的需求&#xff0c;自定义测量指标&#xff0c;以达到观测目的。 自定义气象站测量指标 自定义气象站测量参数指标 总辐射、光合…

自动气象站设备的防雷要点

自动气象站设备&#xff0c;其工作场所几乎都是在户外&#xff0c;雨打风吹、电闪雷鸣、日晒雨淋&#xff0c;由于是雷雨天气&#xff0c;气象站也要连续工作。如何科学防雷&#xff0c;直接影响到气象站的运行与使用寿命。 自动气象站设备的工作要点是获取气象环境参数&#x…

12,桥接模式-露娜的召唤师技能

一,前言 7种结构型设计模式:桥接模式,适配器模式,装饰模式,组合模式,享元模式,外观模式,代理模式上篇我们说了装饰模式:动态地将责任附加到对象上,在不修改任何底层代码的情况下,为对象赋予新的职责开发中,我们经常会遇到一个类有两个或两个以上的维度经常在变化 如果我们使用…

浮标水质监测站是什么

浮标水质监测站是设立在河流、湖泊、水库、近岸海域等流域内进行现场水质自动监测的监测仪器&#xff0c;是以水质监测仪为核心&#xff0c;运用传感器技术&#xff0c;结合浮标体、电源供电系统、数据传输设备组成的放置于水域内的小型水质监测系统。用于连续自动监测被测水体…

【赛纳斯】EC Raman电化学拉曼光谱检测系统推动科研新突破

【前言】   近日,Angew在线发表了厦门大学李剑锋教授团队在设计用于氧还原反应的先进材料及改进催化剂的设计最 新综述文章。该论文综述了双金属纳米催化剂有序度对氧还原反应的影响。论文第 一作者为&#xff1a;Heng-Quan Chen,Huajie Ze,Mu-Fei Yue,论文共同通讯作者为&am…

Ethercat学习-从站源码移植

文章目录 简介移植源码1.源码结构2.GD32硬件接口准备1.SPI接口2.PDI中断配置3.Sync0中断配置4.Sync1中断配置5.定时器中断配置 3.移植准备4.源码移植1.修改头文件名2.ecatport.c文件修改1.SPI部分修改2.中断部分3.修改HW_Init()4.报错修改 3.myapp.c文件修改 5.其他 简介 移植…

小说php 站点源码下载,PTCMS小说站源码

必装环境&#xff1a;nginx(apache.iis 也可)&#xff0c;mysql,php5.6,memcached php5.6 安装扩展 memcache 新建站点&#xff0c;注意新建时&#xff0c;PHP 版本必须选择 PHP5.6&#xff0c;不然程序会报错 1.上传网站文件到网站目录&#xff0c;新建网站伪静态选择 thinkph…

PHP是什么

PHP 是服务器端脚本语言。 您应当具备的基础知识 在继续学习之前&#xff0c;您需要对以下知识有基本的了解&#xff1a; HTMLCSS 如果您希望首先学习这些项目&#xff0c;请在我们的 首页 访问这些教程。 PHP 是什么&#xff1f; PHP 代表 PHP: Hypertext PreprocessorPHP 是一…

国家地表水水质自动监测站坐标每四小时数据(共1952个监测站,含省份、城市、河流、流域、断面名称、监测时间、水温、pH、DO、CODMn、TP、TN、NH3-N、浊度等)

1.监测范围 国家地表水水质自动监测网1952 个水质自动监测站。2.监测项目 监测项目为国家水质自动监测站配备的监测指标&#xff0c;主要包括五参数(水温、pH、溶解氧、电导率和浊度)、氨氮、高锰酸盐指数、总氮、总磷&#xff0c;部分水站增测总有机碳、叶绿素a、藻密度、VOCs…

php 跨站脚本,Piwigo register.php页面多个跨站脚本漏洞

发布日期&#xff1a;2010-05-06 更新日期&#xff1a;2010-05-11 受影响系统&#xff1a; Piwigo project Piwigo 2.0.9 描述&#xff1a; -------------------------------------------------------------------------------- BUGTRAQ ID: 39958 CVE(CAN) ID: CVE-2010-1707…

基于Modbus RTU 485通信协议实现对PH、溶解氧传感器的数据采集

modbus rtu 485协议采用的是一主多从方式通信&#xff0c;主机是普中的stm32f103zet6开发板&#xff0c;从机是传感器。代码已经在实物上测试通过&#xff0c;并且也用modbus精灵测试通过了。如果你没有stm32基础的话&#xff0c;建议先去B站搜索“正点原子”了解一下485串口通…

3、基于51单片机的智能水箱控制系统-温度-PH值-水位(仿真+程序+原理图)

目录 基于51单片机的智能水箱控制系统1、主要功能2、实验结果3、仿真工程4、原理图5、程序源码6、资源获取 基于51单片机的智能水箱控制系统 1、主要功能 51单片机检测水箱内温度&#xff0c;ph值&#xff1b;使用pid算法控制温度到设置值&#xff1b;普通控制ph值到设定值&a…

如何下载y站视频

今天看到了一篇B站视频的下载方法&#xff0c;学习了下&#xff0c;然后去看了下y站是不是也能下下来&#xff0c;居然被我试出来了&#xff0c;嘿嘿 B站文章链接&#xff1a;https://blog.csdn.net/Enderman_xiaohei/article/details/94718494 然后看一下y站的&#xff0c;打…

基于STM32(HAL库)的水质检测(浑浊度、PH值、温度、手机APP显示、wifi上云)

本系统由通过wifi将浑浊度、PH值、温度采集的数据发送到手机APP&#xff0c;超过设定的阈值报警。 一、硬件材料清单&#xff1a; 1、STM32C8T6&#xff1a;控制器 2、OLED显示屏&#xff1a;显示传感器采集的数据 3、PH传感器&#xff1a;检测PH值 4、TDS传感器&#xff1…

JEB动态调试与篡改攻防世界Ph0en1x-100

文章目录 题目APK静态分析jadx反编译IDA反汇编 JEB动态调试工具的使用操作内存值 AndroidKiller工具的使用篡改软件包 总结 题目 攻防世界 Mobile 新手区题目链接 Ph0en1x-100&#xff0c;如下&#xff1a; 下载附件得到一个 apk&#xff0c;安装后如下&#xff1a; 要求输入…

php 抓站,如何跨站抓取别的站点的页面的补充

如何跨站抓取别的站点的页面的补充 更新时间&#xff1a;2006年10月09日 00:00:00 作者&#xff1a; 在实际的应用中&#xff0c;经常会遇到一些特殊的情况&#xff0c;比如需要新闻&#xff0c;天气预报&#xff0c;等等&#xff0c;但是作为个人站点或者实力小的站点 我们不…

用python计算ph,用python下载PH上的学习视频

努力学习&#xff0c;天天向上 闲来无事&#xff0c;用python写个脚本下载PH上的学习视频 环境&#xff1a;python3 用法&#xff1a;python ph_downloader.py viewkey viewkey是PH上的一串字符 代码在 https://paste.ubuntu.com/p/jXVYD3NGP9 多线程下载&#xff0c;网络不好时…