zoukankan      html  css  js  c++  java
  • OpenSL ES: OpenSL ES 简介

    1. OpenSL ES 是什么

    OpenSL ES (Open Sound Library for Embedded Systems)是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API。它为嵌入式移动多媒体设备上的本地应用程序开发者提供标准化, 高性能,低响应时间的音频功能实现方法,并实现软/硬件音频性能的直接跨平台部署,降低执行难度,促进高级音频市场的发展。简单来说OpenSL ES是一个嵌入式跨平台免费的音频处理库。
    Android的OpenSL ES库是在NDK的platforms文件夹对应android平台先相应cpu类型里面,如:

    2. OpenSL ES 与 Android的关系

    Android 2.3 (API 9) 即开始支持 OpenSL ES 标准了,通过 NDK 提供相应的 API 开发接口,下图是 Android 官方给出的关系图(参考:https://source.android.com/devices/audio/latency_app.html):

    由该图可以看出,Android 实现的 OpenSL ES 只是 OpenSL 1.0.1 的子集,并且进行了扩展,因此,对于 OpenSL ES API 的使用,我们还需要特别留意哪些是 Android 支持的,哪些是不支持的,具体相关文档的地址位于 NDK docs 目录下:

    NDKroot/docs/Additional_library_docs/opensles/index.html
    NDKroot/docs/Additional_library_docs/opensles/OpenSL_ES_Specification_1.0.1.pdf

    3. 与Java层的AudioRecord, AudioTrack的关系

    利用 Android 提供的 AudioRecord 采集音频,利用 AudioTrack 播放音频,利用 MediaCodec 来编解码,这些 API 均是 Android 提供的 Java 层 API,无论是采集、播放还是编解码,这些 API 接口都需要将音频数据从 Java 拷贝到 native 层,或者从 native 层拷贝到 Java,如果希望减少拷贝,开发更加高效的 Android 音频应用,则建议使用 Android NDK 提供的 OpenSL ES API 接口,它支持在 native 层直接处理音频数据。

    4. OpenSL ES的特性以及优劣势

    特性:

    1)C 语言接口,兼容 C++,需要在 NDK 下开发,能更好地集成在 native 应用中
    (2)运行于 native 层,需要自己管理资源的申请与释放,没有 Dalvik 虚拟机的垃圾回收机制
    (3)支持 PCM 数据的采集,支持的配置:16bit 位宽,16000 Hz采样率,单通道。(其他的配置不能保证兼容所有平台)
    (4)支持 PCM 数据的播放,支持的配置:8bit/16bit 位宽,单通道/双通道,小端模式,采样率(8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 Hz)
    (5)支持播放的音频数据来源:res 文件夹下的音频、assets 文件夹下的音频、sdcard 目录下的音频、在线网络音频、代码中定义的音频二进制数据等等

    优势:

    1)避免音频数据频繁在 native 层和 Java 层拷贝,提高效率
    (2)相比于 Java API,可以更灵活地控制参数
    (3)由于是 C 代码,因此可以做深度优化,比如采用 NEON 优化
    (4)代码细节更难被反编译

    劣势:

    1)不支持版本低于 Android 2.3 (API 9) 的设备
    (2)没有全部实现 OpenSL ES 定义的特性和功能
    (3)不支持 MIDI 
    (4)不支持直接播放 DRM 或者 加密的内容
    (5)不支持音频数据的编解码,如需编解码,需要使用 MediaCodec API 或者第三方库
    (6)在音频延时方面,相比于上层 API,并没有特别明显地改进

    5. OpenSL ES的一些基本概念

    Objects和Interfaces

    OpenSL ES 有两个必须理解的概念,就是 Object 和 Interface,Object 可以想象成 Java 的 Object 类,Interface 可以想象成 Java 的 Interface,但它们并不完全相同,下面进一步解释他们的关系:
    (1) 每个 Object 可能会存在一个或者多个 Interface,官方为每一种 Object 都定义了一系列的 Interface
    (2)每个 Object 对象都提供了一些最基础的操作,比如:Realize,Resume,GetState,Destroy 等等,如果希望使用该对象支持的功能函数,则必须通过其 GetInterface 函数拿到 Interface 接口,然后通过 Interface 来访问功能函数
    (3)并不是每个系统上都实现了 OpenSL ES 为 Object 定义的所有 Interface,所以在获取 Interface 的时候需要做一些选择和判断

    所有的Object在OpenSL里面我们拿到的都是一个SLObjectItf:

    struct SLObjectItf_ {
        SLresult (*Realize) (SLObjectItf self,SLboolean async);
    
        SLresult (*Resume) (SLObjectItf self,SLboolean async);
    
        SLresult (*GetState) (SLObjectItf self,SLuint32 * pState);
    
        SLresult (*GetInterface) (SLObjectItf self, const SLInterfaceID iid, void * pInterface);
    
        SLresult (*RegisterCallback) (SLObjectItf self, slObjectCallback callback, void * pContext);
    
        void (*AbortAsyncOperation) (SLObjectItf self);
    
        void (*Destroy) (SLObjectItf self);
    
        SLresult (*SetPriority) (SLObjectItf self, SLint32 priority, SLboolean preemptable);
    
        SLresult (*GetPriority) (SLObjectItf self, SLint32 *pPriority, SLboolean *pPreemptable);
    
        SLresult (*SetLossOfControlInterfaces) (SLObjectItf self, SLint16 numInterfaces, SLInterfaceID * pInterfaceIDs, SLboolean enabled);
    };
    
    typedef const struct SLObjectItf_ * const * SLObjectItf;

    这里的SLObjectItf是一个二级指针,它指向的是一个结构体指针。任何创建出来的Object都必须调用 Realize 方法做初始化,在不需要的时候可以使用 Destroy 方法来释放资源。

    Interface则是方法的集合,例如SLRecordItf里面包含了和录音相关的方法,SLPlayItf包含了和播放相关的方法。我们功能都是通过调用Interfaces的方法去实现的,如 SLEngineItf 这个interface,我们可以通过它去创建各种Object,例如播放器、录音器、混音器的Object,然后在用这些Object去获取各种Interface去实现各种功能。

    struct SLEngineItf_ {
        SLresult (*CreateAudioPlayer) (SLEngineItf self, SLObjectItf * pPlayer, SLDataSource *pAudioSrc, SLDataSink *pAudioSnk, SLuint32 numInterfaces, const SLInterfaceID * pInterfaceIds, const SLboolean * pInterfaceRequired);
    
        SLresult (*CreateAudioRecorder) (SLEngineItf self, SLObjectItf * pRecorder, SLDataSource *pAudioSrc, SLDataSink *pAudioSnk, SLuint32 numInterfaces, const SLInterfaceID * pInterfaceIds, const SLboolean * pInterfaceRequired);
    
        SLresult (*CreateOutputMix) (SLEngineItf self, SLObjectItf * pMix, SLuint32 numInterfaces, const SLInterfaceID * pInterfaceIds, const SLboolean * pInterfaceRequired);
    
        ...
    };

    6. OpenSL ES的基本开发流程

    这里以使用OpenSL ES 来播放一个PCM文件为例,主要包括:

    1、 创建接口对象
    2、设置混音器
    3、创建播放器(录音器)
    4、设置缓冲队列和回调函数
    5、设置播放状态
    6、启动回调函数

    由于OpenSL ES是Native层提供的API,在使用前须注意添加头文件和链接选项,在需要用到OpenSL ES API的C文件中添加:

    #include <SLES/OpenSLES.h>
    #include <SLES/OpenSLES_Android.h>

    链接库文件:

    如果是:Android.mk

    LOCAL_LDLIBS += -lOepnSLES

    如果是:CMakeLists.txt

     在 target_link_libraries 括号中添加 OpenSLES 

    示例代码: 《OpenSL ES: 利用OpenSL ES播放一个存在于SDcard上的PCM文件

    参考链接:

    1. Android音频开发(7):使用 OpenSL ES API(下)

    2. OpenSL ES 学习笔记

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

  • 相关阅读:
    第二次作业循环语句
    c语言01次作业分支,顺序结构
    PAT 1027. Colors in Mars
    PAT 1026 Table Tennis
    PAT 1035 Password
    PAT 1038. Recover the Smallest Number
    PAT 1028 List Sorting (25)
    PAT 1041 Be Unique (20)
    PAT 1025 PAT Ranking
    1037. Magic Coupon
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/11097909.html
Copyright © 2011-2022 走看看