Android音視頻學習第7章:使用OpenSL ES進行音頻解碼

來源:互聯網
上載者:User

標籤:jni   span   asi   proc   rate   thread   include   imp   結構體   


/*
*
*這裡使用了transcode-1.1.7對wav檔案進行解碼,然後使用opensl es進行播放
*
*/

//用到的變數和結構體

WAV wav; //wav檔案指標SLObjectItf engineObject; //引擎對象SLEngineItf engineInterface; //引擎介面SLObjectItf outputMixObject; //混音器SLObjectItf audioPlayerObject; //播放器對象SLAndroidSimpleBufferQueueItf andioPlayerBufferQueueItf;    //緩衝器隊列介面SLPlayItf audioPlayInterface;   //播放介面unsigned char *buffer; //緩衝區size_t bufferSize;     //緩衝區大小//上下文struct PlayerContext{    WAV wav;    unsigned char *buffer;    size_t bufferSize;    PlayerContext(WAV wav,            unsigned char *buffer,            size_t bufferSize){        this->wav = wav;        this->buffer = buffer;        this->bufferSize = bufferSize;    }};//實現對象void RealizeObject(SLObjectItf object){    //非非同步(阻塞)    (*object)->Realize(object,SL_BOOLEAN_FALSE);}
具體實現流程:1.開啟檔案
WAV wav = OpenWaveFile(env,jFileName);
//開啟檔案WAV OpenWaveFile(JNIEnv *env,jstring jFileName){    const char *cFileName = env->GetStringUTFChars(jFileName,JNI_FALSE);    WAVError err;    WAV wav = wav_open(cFileName,WAV_READ,&err);    LOGI("%d",wav_get_bitrate(wav));    env->ReleaseStringUTFChars(jFileName,cFileName);    if(wav == 0){        LOGE("%s",wav_strerror(err));    }    return wav;}
2.建立OpenSL ES引擎
//OpenSL ES在Android平台下預設是安全執行緒的,這樣設定是為了為了相容其他平台    SLEngineOption options[] = {        {(SLuint32)SL_ENGINEOPTION_THREADSAFE, (SLuint32)SL_BOOLEAN_TRUE}    };    slCreateEngine(&engineObject,ARRAY_LEN(engineObject),options,0,0,0); //沒有介面//執行個體化對象    //對象建立之後,處於未執行個體化狀態,對象雖然存在但未分配任何資源,使用前先執行個體化(使用完之後destroy)    RealizeObject(engineObject);
3.擷取引擎介面
(*engineObject)->GetInterface(engineObject,SL_IID_ENGINE,&engineInterface);
4.建立輸出混音器
(*engineInterface)->CreateOutputMix(engineInterface,&outputMixObject,0,0,0); //沒有介面    //執行個體化混音器    RealizeObject(outputMixObject);
5.建立緩衝區儲存讀取到的音頻資料庫
//緩衝區的大小    bufferSize = wav_get_channels(wav) * wav_get_rate(wav) * wav_get_bits(wav);    buffer = new unsigned char[bufferSize];
6.建立帶有緩衝區隊列的音頻播放器
CreateBufferQueueAudioPlayer(wav,engineInterface,outputMixObject,audioPlayerObject);    //執行個體化音頻播放器    RealizeObject(audioPlayerObject);

CreateBufferQueueAudioPlayer.cpp

extern "C" {#include "wavlib.h"}#include <SLES/OpenSLES.h>#include <SLES/OpenSLES_Android.h>#include <android/log.h>#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))//建立音頻播放對象void CreateBufferQueueAudioPlayer(        WAV wav,        SLEngineItf engineEngine,        SLObjectItf outputMixObject,        SLObjectItf &audioPlayerObject){    // Android針對資料來源的簡單緩衝區隊列定位器    SLDataLocator_AndroidSimpleBufferQueue dataSourceLocator = {        SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // 定位器類型        1                                        // 緩衝區數    };    // PCM資料來源格式    SLDataFormat_PCM dataSourceFormat = {        SL_DATAFORMAT_PCM,        // 格式類型        wav_get_channels(wav),    // 通道數        wav_get_rate(wav) * 1000, // 毫赫茲/秒的樣本數        wav_get_bits(wav),        // 每個樣本的位元        wav_get_bits(wav),        // 容器大小        SL_SPEAKER_FRONT_CENTER,  // 通道屏蔽        SL_BYTEORDER_LITTLEENDIAN // 位元組順序    };    // 資料來源是含有PCM格式的簡單緩衝區隊列    SLDataSource dataSource = {        &dataSourceLocator, // 資料定位器        &dataSourceFormat   // 資料格式    };    // 針對資料接收器的輸出混合定位器    SLDataLocator_OutputMix dataSinkLocator = {        SL_DATALOCATOR_OUTPUTMIX, // 定位器類型        outputMixObject           // 輸出混合    };    // 資料定位器是一個輸出混合    SLDataSink dataSink = {        &dataSinkLocator, // 定位器        0                 // 格式    };    // 需要的介面    SLInterfaceID interfaceIds[] = {        SL_IID_BUFFERQUEUE    };    // 需要的介面,如果所需要的介面不要用,請求將失敗    SLboolean requiredInterfaces[] = {        SL_BOOLEAN_TRUE // for SL_IID_BUFFERQUEUE    };    // 建立音頻播放器對象    SLresult result = (*engineEngine)->CreateAudioPlayer(            engineEngine,            &audioPlayerObject,            &dataSource,            &dataSink,            ARRAY_LEN(interfaceIds),            interfaceIds,            requiredInterfaces);}
7.獲得緩衝區隊列介面Buffer Queue Interface
//通過緩衝區隊列介面對緩衝區進行排序播放    (*audioPlayerObject)->GetInterface(audioPlayerObject,SL_IID_BUFFERQUEUE,&andioPlayerBufferQueueItf);
8.註冊音頻播放器回呼函數
//當播放器完成對前一個緩衝區隊列的播放時,回呼函數會被調用,然後我們又繼續讀取音頻資料,直到結束    //上下文,包裹參數方便再回呼函數中使用    PlayerContext *ctx = new PlayerContext(wav,buffer,bufferSize);    (*andioPlayerBufferQueueItf)->RegisterCallback(andioPlayerBufferQueueItf,PlayerCallBack,ctx);
//回呼函數void PlayerCallBack(SLAndroidSimpleBufferQueueItf andioPlayerBufferQueue,void *context){    PlayerContext* ctx = (PlayerContext*)context;    //讀取資料    ssize_t readSize = wav_read_data(ctx->wav,ctx->buffer,ctx->bufferSize);    if(0 < readSize){        (*andioPlayerBufferQueue)->Enqueue(andioPlayerBufferQueue,ctx->buffer,readSize);    }else{        //destroy context        CloseWaveFile(ctx->wav); //關閉檔案        delete ctx->buffer; //釋放緩衝    }}
9.擷取Play Interface通過對SetPlayState函數來啟動播放音樂
//一旦播放器被設定為播放狀態,該音頻播放器開始等待緩衝區排隊就緒    (*audioPlayerObject)->GetInterface(audioPlayerObject,SL_IID_PLAY,&audioPlayInterface);    //設定播放狀態    (*audioPlayInterface)->SetPlayState(audioPlayInterface,SL_PLAYSTATE_PLAYING);
10.開始,讓第一個緩衝區入隊

PlayerCallBack(andioPlayerBufferQueueItf,ctx);
完整代碼
#include "com_dongnaoedu_jasonaudioplayer_AudioPlayer.h"extern "C" {#include "wavlib.h"}#include <SLES/OpenSLES.h>#include <SLES/OpenSLES_Android.h>#include <android/log.h>#include "CreateBufferQueueAudioPlayer.cpp"#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"jason",FORMAT,##__VA_ARGS__);#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"jason",FORMAT,##__VA_ARGS__);#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))WAV wav; //wav檔案指標SLObjectItf engineObject; //引擎對象SLEngineItf engineInterface; //引擎介面SLObjectItf outputMixObject; //混音器SLObjectItf audioPlayerObject; //播放器對象SLAndroidSimpleBufferQueueItf andioPlayerBufferQueueItf;    //緩衝器隊列介面SLPlayItf audioPlayInterface;   //播放介面unsigned char *buffer; //緩衝區size_t bufferSize;     //緩衝區大小//上下文struct PlayerContext{    WAV wav;    unsigned char *buffer;    size_t bufferSize;    PlayerContext(WAV wav,            unsigned char *buffer,            size_t bufferSize){        this->wav = wav;        this->buffer = buffer;        this->bufferSize = bufferSize;    }};//開啟檔案WAV OpenWaveFile(JNIEnv *env,jstring jFileName){    const char *cFileName = env->GetStringUTFChars(jFileName,JNI_FALSE);    WAVError err;    WAV wav = wav_open(cFileName,WAV_READ,&err);    LOGI("%d",wav_get_bitrate(wav));    env->ReleaseStringUTFChars(jFileName,cFileName);    if(wav == 0){        LOGE("%s",wav_strerror(err));    }    return wav;}//關閉檔案void CloseWaveFile(WAV wav){    wav_close(wav);}//實現對象void RealizeObject(SLObjectItf object){    //非非同步(阻塞)    (*object)->Realize(object,SL_BOOLEAN_FALSE);}//回呼函數void PlayerCallBack(SLAndroidSimpleBufferQueueItf andioPlayerBufferQueue,void *context){    PlayerContext* ctx = (PlayerContext*)context;    //讀取資料    ssize_t readSize = wav_read_data(ctx->wav,ctx->buffer,ctx->bufferSize);    if(0 < readSize){        (*andioPlayerBufferQueue)->Enqueue(andioPlayerBufferQueue,ctx->buffer,readSize);    }else{        //destroy context        CloseWaveFile(ctx->wav); //關閉檔案        delete ctx->buffer; //釋放緩衝    }}JNIEXPORT void JNICALL Java_com_dongnaoedu_jasonaudioplayer_AudioPlayer_play  (JNIEnv *env, jclass jthiz, jstring jFileName){    //1.開啟檔案    WAV wav = OpenWaveFile(env,jFileName);    //2.建立OpenSL ES引擎    //OpenSL ES在Android平台下預設是安全執行緒的,這樣設定是為了為了相容其他平台    SLEngineOption options[] = {        {(SLuint32)SL_ENGINEOPTION_THREADSAFE, (SLuint32)SL_BOOLEAN_TRUE}    };    slCreateEngine(&engineObject,ARRAY_LEN(engineObject),options,0,0,0); //沒有介面    //執行個體化對象    //對象建立之後,處於未執行個體化狀態,對象雖然存在但未分配任何資源,使用前先執行個體化(使用完之後destroy)    RealizeObject(engineObject);    //3.擷取引擎介面    (*engineObject)->GetInterface(engineObject,SL_IID_ENGINE,&engineInterface);    //4.建立輸出混音器    (*engineInterface)->CreateOutputMix(engineInterface,&outputMixObject,0,0,0); //沒有介面    //執行個體化混音器    RealizeObject(outputMixObject);    //5.建立緩衝區儲存讀取到的音頻資料庫    //緩衝區的大小    bufferSize = wav_get_channels(wav) * wav_get_rate(wav) * wav_get_bits(wav);    buffer = new unsigned char[bufferSize];    //6.建立帶有緩衝區隊列的音頻播放器    CreateBufferQueueAudioPlayer(wav,engineInterface,outputMixObject,audioPlayerObject);    //執行個體化音頻播放器    RealizeObject(audioPlayerObject);    //7.獲得緩衝區隊列介面Buffer Queue Interface    //通過緩衝區隊列介面對緩衝區進行排序播放    (*audioPlayerObject)->GetInterface(audioPlayerObject,SL_IID_BUFFERQUEUE,&andioPlayerBufferQueueItf);    //8.註冊音頻播放器回呼函數    //當播放器完成對前一個緩衝區隊列的播放時,回呼函數會被調用,然後我們又繼續讀取音頻資料,直到結束    //上下文,包裹參數方便再回呼函數中使用    PlayerContext *ctx = new PlayerContext(wav,buffer,bufferSize);    (*andioPlayerBufferQueueItf)->RegisterCallback(andioPlayerBufferQueueItf,PlayerCallBack,ctx);    //9.擷取Play Interface通過對SetPlayState函數來啟動播放音樂    //一旦播放器被設定為播放狀態,該音頻播放器開始等待緩衝區排隊就緒    (*audioPlayerObject)->GetInterface(audioPlayerObject,SL_IID_PLAY,&audioPlayInterface);    //設定播放狀態    (*audioPlayInterface)->SetPlayState(audioPlayInterface,SL_PLAYSTATE_PLAYING);    //10.開始,讓第一個緩衝區入隊    PlayerCallBack(andioPlayerBufferQueueItf,ctx);    //關閉檔案    //CloseWaveFile(wav);}

Android音視頻學習第7章:使用OpenSL ES進行音頻解碼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.