OpenSLES 播放音频

一、播放流程

流程

二、步骤

1、初始化引擎

• SLObjectItf engineObject = NULL;
• SLEngineItf engineEngine = NULL;
• slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
• (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
• (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);

2、slCreateEngine参数

• SL_API SLresult SLAPIENTRY slCreateEngine(
• SLObjectItf *pEngine,
• SLuint32 numOptions
• const SLEngineOption *pEngineOptions, //选择项目 默认参数
• SLuint32 numInterfaces,
• const SLInterfaceID *pInterfaceIds, //支持的接口
• const SLboolean * pInterfaceRequired //接口标识数值,是否支持
• )

3、SLObjectItf 创建

• SLresult (*Realize) (
• SLObjectItf self,
• SLboolean async
• );
• 对象已实现状态 (false阻塞)

4、GetInterface 获取接口

• SLresult (*GetInterface) (
• SLObjectItf self,
• const SLInterfaceID iid,
• void * pInterface
• );

5、创建输出设备

• SLEngineItf engineEngine = NULL;
• SLObjectItf outputMixObject = NULL;
• (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
• (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
• SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX,
outputMixObject};
• SLDataSink audioSnk = {&outputMix, NULL};

6、配置PCM格式信息

• SLDataLocator_AndroidSimpleBufferQueue
android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};
• SLDataFormat_PCM pcm={
• SL_DATAFORMAT_PCM,//播放pcm格式的数据
• 2,//2个声道(立体声)
• SL_SAMPLINGRATE_44_1,//44100hz的频率
• SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位
• SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行
• SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声(前左前右)
• SL_BYTEORDER_LITTLEENDIAN//小端模式
• };
• SLDataSource slDataSource = {&android_queue, &pcm};

7、初始化播放器

• const SLInterfaceID ids[1] = {SL_IID_BUFFERQUEUE};
• const SLboolean req[1] = {SL_BOOLEAN_TRUE};
• (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource,
&audioSnk, 1, ids, req);
• //初始化播放器
• (*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);
• // 得到接口后调用 获取Player接口
• (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);

8、播放和缓冲队列

• 注册回调缓冲区 获取缓冲队列接口(*pcmPlayerObject)-
>GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
• (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack,
NULL);
• (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
• (*pcmBufferQueue)->Enqueue(pcmBufferQueue, "", 1);

三、源码

#include <jni.h>
#include <string>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <android/log.h>
#define LOGD(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"ywl5320",FORMAT,##__VA_ARGS__);


static SLObjectItf  engineSL = NULL;
SLEngineItf CreateSL()
{
    SLresult re;
    SLEngineItf en;
    re = slCreateEngine(&engineSL,0,0,0,0,0);
    if(re != SL_RESULT_SUCCESS) return NULL;
    re = (*engineSL)->Realize(engineSL,SL_BOOLEAN_FALSE);
    if(re != SL_RESULT_SUCCESS) return NULL;
    re = (*engineSL)->GetInterface(engineSL,SL_IID_ENGINE,&en);
    if(re != SL_RESULT_SUCCESS) return NULL;
    return en;
}

void PcmCall(SLAndroidSimpleBufferQueueItf bf,void *content){
    LOGD("PcmCall");
    static FILE *fp = NULL;
    static char *buf = NULL;
    if (!buf){
        buf = new char[1024*1024];
    }
    if (!fp){
        fp = fopen("/sdcard/test.pcm","rb");
        LOGD("fp failed 1 ");
    }
    if (!fp){
        LOGD("fp failed 2 ");
        return;
    }
    if (feof(fp) == 0){ //没到结尾
        int len = (int) fread(buf, 1, 1024, fp);
        LOGD("fp len ",len);
        if (len > 0){
            // 加入队列
            (*bf)->Enqueue(bf,buf,len);
        }
    }
}

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_phj_testopensl_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";

    //1 创建引擎
    SLEngineItf eng = CreateSL();
    if(eng)
    {
        LOGD("CreateSL success! ");
    }
    else
    {
        LOGD("CreateSL failed! ");
    }

    //2 创建混音器
    SLObjectItf mix = NULL;
    SLresult re = 0;
    re = (*eng)->CreateOutputMix(eng,&mix,0,0,0);
    if(re !=SL_RESULT_SUCCESS )
    {
        LOGD("SL_RESULT_SUCCESS failed!");
    }
    re = (*mix)->Realize(mix,SL_BOOLEAN_FALSE);
    if(re !=SL_RESULT_SUCCESS )
    {
        LOGD("(*mix)->Realize failed!");
    }
    SLDataLocator_OutputMix outmix = {SL_DATALOCATOR_OUTPUTMIX,mix};
    SLDataSink audioSink= {&outmix,0};


    //3、配置音频信息
    // 缓存队列
    SLDataLocator_AndroidSimpleBufferQueue que = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,10};
    //音频格式
    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 //字节序 小段
    };

    SLDataSource ds = {&que,&pcm};

    // 4 创建播放器
    SLObjectItf player = NULL;
    SLPlayItf iplayer = NULL;
    SLAndroidSimpleBufferQueueItf pcmQue = NULL;
    const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE};
    const SLboolean req[] = {SL_BOOLEAN_TRUE};
    re = (*eng)->CreateAudioPlayer(eng,&player,&ds,&audioSink, sizeof(ids)/ sizeof(SLInterfaceID),ids,req);
    if (re != SL_RESULT_SUCCESS){
        LOGD("CreateAudioPlayer failed")
    } else {
        LOGD("CreateAudioPlayer SUCCESS")
    }

    (*player)->Realize(player,SL_BOOLEAN_FALSE);
    // 获取player接口
    re = (*player)->GetInterface(player,SL_IID_PLAY,&iplayer);

    if (re != SL_RESULT_SUCCESS){
        LOGD("GetInterface failed")
    } else {
        LOGD("GetInterface SUCCESS")
    }

    re = (*player)->GetInterface(player,SL_IID_BUFFERQUEUE,&pcmQue);
    if (re != SL_RESULT_SUCCESS){
        LOGD("GetInterface failed")
    } else {
        LOGD("GetInterface SUCCESS")
    }

    // 设置回调函数 播放回调为空时候使用
    (*pcmQue)->RegisterCallback(pcmQue,PcmCall,0);
    (*iplayer)->SetPlayState(iplayer,SL_PLAYSTATE_PLAYING);

    // 启动队列回调
    (*pcmQue)->Enqueue(pcmQue,"",1);

    return env->NewStringUTF(hello.c_str());
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容