【Android FFmpeg】第三节:移动端使用 OpenSL ES播放pcm音频

一、OpenSL ES是什么?

OpenSL ES ( 嵌入式音频加速标准), 它是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API。它为嵌入式移动多媒体设备上的本地应用程序开发者提供标准化, 高性能,低响应时间的音频功能实现方法,并实现软/硬件音频性能的直接跨平台部署,降低执行难度,促进高级音频市场的发展。


     简单来说:OpenSL ES是一个嵌入式、跨平台、免费的、音频 处理库。

 二、PCM文件是什么?

PCM(Pulse Code Modulation)脉冲编码调制是数字通信的编码方式之一。主要过程是将话音、图像等模拟信号每隔一定时间进行取样,使其离散化,同时将抽样值按分层单位四舍五入取整量化,同时将抽样值按一组二进制码来表示抽样脉冲的幅值。


什么是WAV和PCM?

WAV:wav是一种无损的音频文件格式,WAV符合 PIFF(Resource Interchange File Format)规范。所有的WAV都有一个文件头,这个文件头音频流的编码参数。WAV对音频流的编码没有硬性规定,除了PCM之外,还有几乎所有支持ACM规范的编码都可以为WAV的音频流进行编码。
PCM:PCM(Pulse Code Modulation----脉码调制录音)。所谓PCM录音就是将声音等模拟信号变成符号化的脉冲列,再予以记录。PCM信号是由[1]、[0]等符号构成的数字信号,而未经过任何编码和压缩处理。与模拟信号比,它不易受传送系统的杂波及失真的影响。动态范围宽,可得到音质相当好的影响效果。


简单来说:wav是一种无损的音频文件格式,pcm是没有压缩的编码方式。
============================================================================================

WAV和PCM的关系 :

WAV可以使用多种音频编码来压缩其音频流,不过我们常见的都是音频流被PCM编码处理的WAV,但这不表示WAV只能使用PCM编码,MP3编码同样也可以运用在WAV中,和AVI一样,只要安装好了相应的Decode,就可以欣赏这些WAV了。在Windows平台下,基于PCM编码的WAV是被支持得最好的音频格式,所有音频软件都能完美支持,由于本身可以达到较高的音质的要求,因此,WAV也是音乐编辑创作的首选格式,适合保存音乐素材。因此,基于PCM编码的WAV被作为了一种中介的格式,常常使用在其他编码的相互转换之中,例如MP3转换成WMA。


简单来说:pcm是无损wav文件中音频数据的一种编码方式,但wav还可以用其它方式编码。

 三、使用OpenSL ES播放一个pcm文件的流程

  1. cmake配置
    cmake_minimum_required(VERSION3.4.1)
    
    
    
    set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS}-std=c99-Wall")
    
    
    
    add_library(
    
               native-lib
    
                SHARED
    
               native-lib.cpp)
    
    
    
    find_library(
    
                log-lib
    
                log)
    
    
    
    target_link_libraries(
    
                     native-lib
    
                      android
    
                      OpenSLES
    
                      ${log-lib})

  2. 创建native方法
    public class MainActivity extends AppCompatActivity{
    
    
    
          static{
    
               System.loadLibrary("native-lib");
    
          }
    
    
    
          @Override
    
          protected void onCreate(Bundle savedInstanceState){
    
               super.onCreate(savedInstanceState);
    
                setContentView(R.layout.activity_main);
    
          }
    
    
    
          public native void playpcm(String url);
    
          //点击事件
    
          public void onButtonClick(View view){
    
               File file = new File(Environment.getExternalStorageDirectory(),"mm.pcm");
    
                playpcm(file.getAbsolutePath());
    
          }
    
    }

  3. 代码
    #include<jni.h>
    
    #include<string>
    
    #include<SLES/OpenSLES.h>
    
    #include<SLES/OpenSLES_Android.h>
    
    #include"AndroidLog.h"
    
    
    
    SLObjectItf engineObject = NULL;
    
    SLEngineItf engineEngin = NULL;
    
    
    
    SLObjectItf outputMixObject =NULL;
    
    SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;
    
    SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;
    
    SLObjectItf pcmPlayObject = NULL;
    
    SLPlayItf pclPlayerPlay = NULL;
    
    SLAndroidSimpleBufferQueueItf pcmBufferQueue;
    
    
    
    FILE *pcmFile;
    
    void *buffer;
    
    uint8_t *out_buffer;
    
    
    
          int getPcmData(void **pcm){
    
               int size=0;
    
               while(!feof(pcmFile)){//是否读到结尾
    
               size=fread(out_buffer,1,44100*2*2,pcmFile);
    
                if(out_buffer==NULL){
    
                    LOGD("%s","读取结束");
    
                     break;//读取完毕
    
               }else{
    
                      LOGD("%s","读取ing");
    
                }
    
                      *pcm=out_buffer;
    
                    break;
    
                }
    
                return size;
    
          }
    
    
    
          void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf,void *context){
    
                int size = getPcmData(&buffer);
    
                if(buffer!=NULL){
    
                      (*pcmBufferQueue)->Enqueue(pcmBufferQueue,buffer,size);
    
                }
    
          }
    
    
    
    extern"C"
    
    JNIEXPORT void JNICALL
    
    Java_com_qy_ffmpeg_102_MainActivity_playpcm
    (JNIEnv *env,jobject instance,jstring url_){
    
          const char *url = env->GetStringUTFChars(url_ , 0);
    
    
    
          pcmFile=fopen(url,"r");//打开文件
    
          if(pcmFile==NULL){
    
                return;
    
          }
    
          //读一秒钟的数据
    
          out_buffer=(uint8_t*)malloc(44100*2*2);
    
    
    
          SLresult result;
    
          //1创建引擎接口(参数:)
    
          result=slCreateEngine(&engineObject,0,NULL,0,NULL,NULL);
    
          //判断是否成功
    
          assert(SL_RESULT_SUCCESS==result);
    
          (void)result;
    
    
    
          //2实例化引擎
    
          result=(*engineObject)->Realize(engineObject,SL_BOOLEAN_FALSE);
    
          assert(SL_RESULT_SUCCESS==result);
    
          (void)result;
    
    
    
          //3获取引擎接口(其他对象需要这个),
    
          result=(*engineObject)
                 ->GetInterface(engineObject,SL_IID_ENGINE,&engineEngin);
    
          assert(SL_RESULT_SUCCESS==result);
    
          (void)result;
    
    
    
          //4创建混音器
    
          const SLInterfaceID mids[1]={SL_IID_ENVIRONMENTALREVERB};
    
          const SLboolean mreq[1]={SL_BOOLEAN_FALSE};
    
          result=(*engineEngin)
          ->CreateOutputMix(engineEngin,&outputMixObject,1,mids,mreq);
    
          assert(SL_RESULT_SUCCESS==result);
    
          (void)result;
    
    
    
          //实现输出组合
    
          result=(*outputMixObject)
                 ->Realize(outputMixObject,SL_BOOLEAN_FALSE);
    
          assert(SL_RESULT_SUCCESS==result);
    
          (void)result;
    
    
    
          //获得环境混响接口
    
          result=(*outputMixObject)
    
               ->GetInterface(outputMixObject,SL_IID_ENVIRONMENTALREVERB,
                &outputMixEnvironmentalReverb);
    
          获取环境混响界面
    
          ///如果环境混响效果不可用,则可能会失败,
    
          //原因是该功能不存在,CPU负载过大或未请求并授予所需的MODIFY_AUDIO_SETTINGS权限
    
          if(SL_RESULT_SUCCESS==result){
    
                result=(*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
    
                      outputMixEnvironmentalReverb,&reverbSettings);
    
                (void)result;
    
          }
    
    
    
          //5创建缓冲队列(配置音频源)
    
          SLDataLocator_AndroidBufferQueue android_queue= 
               {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE , 2};
    
          //设置要播放的pcm格式的内容
    
          SLDataFormat_PCM pcm={
    
                      SL_DATAFORMAT_PCM,//格式类型
    
                      2,//声道(单声道\双声道)
    
                      SL_SAMPLINGRATE_44_1,//采样率
    
                      SL_PCMSAMPLEFORMAT_FIXED_16,//采样格式
    
                      SL_PCMSAMPLEFORMAT_FIXED_16,//采样容器大小
    
                      SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT,//声道相关
    
                      SL_BYTEORDER_LITTLEENDIAN//块的字节顺序从16--32位
    
          };
    
    
    
          //配置音频接收器
    
          SLDataLocator_OutputMix outputMix={SL_DATALOCATOR_OUTPUTMIX,outputMixObject};
    
          SLDataSource slDataSource={&android_queue,&pcm};
    
          SLDataSink audioSnk={&outputMix,NULL,};
    
    
    
          //创建音频播放器:
    
          const SLInterfaceIDids[2]={SL_IID_BUFFERQUEUE};
    
          const SLbooleanreq[2]={SL_BOOLEAN_TRUE};
    
          result=(*engineEngin)->
                 CreateAudioPlayer(engineEngin,&pcmPlayObject,
                 &slDataSource,&audioSnk,1,ids,req);
    
          assert(SL_RESULT_SUCCESS==result);
    
          (void)result;
    
    
    
          //实例化播放器
    
          result=(*pcmPlayObject)->Realize(pcmPlayObject,SL_BOOLEAN_FALSE);
    
          assert(SL_RESULT_SUCCESS==result);
    
          (void)result;
    
    
    
          //得到播放接口
    
          result=(*pcmPlayObject)->GetInterface(pcmPlayObject,SL_IID_PLAY,&pclPlayerPlay);
    
          assert(SL_RESULT_SUCCESS==result);
    
          (void)result;
    
    
    
          //设置缓冲
    
          result=(*pcmPlayObject)->GetInterface(pcmPlayObject,SL_IID_BUFFERQUEUE,&pcmBufferQueue);
    
          assert(SL_RESULT_SUCCESS==result);
    
          (void)result;
    
    
    
          //设置回调
    
          (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue,pcmBufferCallBack,NULL);
    
          //设置播放状态
    
          (*pclPlayerPlay)->SetPlayState(pclPlayerPlay,SL_PLAYSTATE_PLAYING);
    
          //
    
          pcmBufferCallBack(pcmBufferQueue,NULL);
    
          //========
    
          env->ReleaseStringUTFChars(url_,url);
    
    }

PCM文件下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值