Android 音視頻深入 十四 FFmpeg與OpenSL ES 播放mp3音樂,能暫停(附源碼

來源:互聯網
上載者:User

標籤:FFmpeg   OpenSL ES   

項目地址
https://github.com/979451341/FFmpegOpenslES

這次說的是FFmpeg解碼mp3,資料給OpenSL ES播放,並且能夠暫停。
1.建立引擎

slCreateEngine(&engineObject,0,NULL,0,NULL,NULL);//建立引擎(*engineObject)->Realize(engineObject,SL_BOOLEAN_FALSE);//實現engineObject介面對象(*engineObject)->GetInterface(engineObject,SL_IID_ENGINE,&engineEngine);//通過引擎調用介面初始化SLEngineItf

2.建立混音器

(*engineEngine)->CreateOutputMix(engineEngine,&outputMixObject,0,0,0);//用引擎對象建立混音器介面對象(*outputMixObject)->Realize(outputMixObject,SL_BOOLEAN_FALSE);//實現混音器介面對象SLresult   sLresult = (*outputMixObject)->GetInterface(outputMixObject,SL_IID_ENVIRONMENTALREVERB,&outputMixEnvironmentalReverb);//利用混音器執行個體對象介面初始化具體的混音器對象//設定if (SL_RESULT_SUCCESS == sLresult) {    (*outputMixEnvironmentalReverb)->            SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &settings);}

3.FFmpeg解碼mp3準備工作

av_register_all();char *input = "/storage/emulated/0/pauseRecordDemo/video/a.mp3";pFormatCtx = avformat_alloc_context();LOGE("Lujng %s",input);LOGE("xxx %p",pFormatCtx);int error;char buf[] = "";//開啟視頻地址並擷取裡面的內容(解鎖裝)if (error = avformat_open_input(&pFormatCtx, input, NULL, NULL) < 0) {    av_strerror(error, buf, 1024);    // LOGE("%s" ,inputPath)    LOGE("Couldn‘t open file %s: %d(%s)", input, error, buf);    // LOGE("%d",error)    LOGE("開啟視頻失敗")}//3.擷取視頻資訊if(avformat_find_stream_info(pFormatCtx,NULL) < 0){    LOGE("%s","擷取視頻資訊失敗");    return -1;}int i=0;for (int i = 0; i < pFormatCtx->nb_streams; ++i) {    if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {        LOGE("  找到音頻id %d", pFormatCtx->streams[i]->codec->codec_type);        audio_stream_idx=i;        break;    }}

// mp3的解碼器

// 擷取音訊轉碼器
pCodecCtx=pFormatCtx->streams[audio_stream_idx]->codec;
LOGE("擷取視頻編碼器上下文 %p ",pCodecCtx);

pCodex = avcodec_find_decoder(pCodecCtx->codec_id);LOGE("擷取視頻編碼 %p",pCodex);if (avcodec_open2(pCodecCtx, pCodex, NULL)<0) {}packet = (AVPacket *)av_malloc(sizeof(AVPacket));

// av_init_packet(packet);
// 音頻資料

frame = av_frame_alloc();

// mp3 裡面所包含的編碼格式 轉換成 pcm SwcContext
swrContext = swr_alloc();

int length=0;int got_frame;

// 441002
out_buffer = (uint8_t
) av_malloc(44100 * 2);
uint64_t out_ch_layout=AV_CH_LAYOUT_STEREO;
// 輸出採樣位元 16位
enum AVSampleFormat out_formart=AV_SAMPLE_FMT_S16;
//輸出的採樣率必須與輸入相同
int out_sample_rate = pCodecCtx->sample_rate;

swr_alloc_set_opts(swrContext, out_ch_layout, out_formart, out_sample_rate,                   pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0,                   NULL);swr_init(swrContext);

// 擷取通道數 2
out_channer_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
rate = pCodecCtx->sample_rate;
channel = pCodecCtx->channels;

4.緩衝隊列設定

int rate;int channels;createFFmpeg(&rate,&channels);LOGE("RATE %d",rate);LOGE("channels %d",channels);/* * typedef struct SLDataLocator_AndroidBufferQueue_ {SLuint32    locatorType;//緩衝區隊列類型SLuint32    numBuffers;//buffer位元

} */

SLDataLocator_AndroidBufferQueue android_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};/**typedef struct SLDataFormat_PCM_ {    SLuint32         formatType;  pcm    SLuint32         numChannels;  通道數    SLuint32         samplesPerSec;  採樣率    SLuint32         bitsPerSample;  採樣位元    SLuint32         containerSize;  包含位元    SLuint32         channelMask;     立體聲    SLuint32        endianness;    end標誌位} SLDataFormat_PCM; */SLDataFormat_PCM pcm = {SL_DATAFORMAT_PCM,channels,rate*1000        ,SL_PCMSAMPLEFORMAT_FIXED_16        ,SL_PCMSAMPLEFORMAT_FIXED_16        ,SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT,SL_BYTEORDER_LITTLEENDIAN};/* * typedef struct SLDataSource_ {        void *pLocator;//緩衝區隊列        void *pFormat;//資料樣式,配置資訊    } SLDataSource; * */SLDataSource dataSource = {&android_queue,&pcm};SLDataLocator_OutputMix slDataLocator_outputMix={SL_DATALOCATOR_OUTPUTMIX,outputMixObject};SLDataSink slDataSink = {&slDataLocator_outputMix,NULL};const SLInterfaceID ids[3]={SL_IID_BUFFERQUEUE,SL_IID_EFFECTSEND,SL_IID_VOLUME};const SLboolean req[3]={SL_BOOLEAN_FALSE,SL_BOOLEAN_FALSE,SL_BOOLEAN_FALSE};/* * SLresult (*CreateAudioPlayer) (    SLEngineItf self,    SLObjectItf * pPlayer,    SLDataSource *pAudioSrc,//資料設定    SLDataSink *pAudioSnk,//關聯混音器    SLuint32 numInterfaces,    const SLInterfaceID * pInterfaceIds,    const SLboolean * pInterfaceRequired); * */LOGE("執行到此處")(*engineEngine)->CreateAudioPlayer(engineEngine,&audioplayer,&dataSource,&slDataSink,3,ids,req);(*audioplayer)->Realize(audioplayer,SL_BOOLEAN_FALSE);LOGE("執行到此處2")(*audioplayer)->GetInterface(audioplayer,SL_IID_PLAY,&slPlayItf);//初始化播放器//註冊緩衝區,通過緩衝區裡面 的資料進行播放(*audioplayer)->GetInterface(audioplayer,SL_IID_BUFFERQUEUE,&slBufferQueueItf);//設定回調介面(*slBufferQueueItf)->RegisterCallback(slBufferQueueItf,getQueueCallBack,NULL);

最後還要給這個緩衝回呼函數賦予參數,這個回呼函數主要負責提供FFmpeg解碼出的資料

//開始播放getQueueCallBack(slBufferQueueItf,NULL);

我們再來看看這個函數說的啥,靠Enqueue函數把資料放入隊列裡,這個資料則是從getPcm函數得到的

void getQueueCallBack(SLAndroidSimpleBufferQueueItf slBufferQueueItf, void context){
buffersize=0;
getPcm(&buffer,&buffersize);
if(buffer!=NULL&&buffersize!=0){
//將得到的資料加入到隊列中
(
slBufferQueueItf)->Enqueue(slBufferQueueItf,buffer,buffersize);
}
}

這個FFmpeg解碼mp3得到Pcm資料,這個主要是每解碼出一個packet資料,就跳出迴圈,將資料給上層函數壓入隊列,當隊列的資料讀取完了,又會調用getQueueCallBack函數再來擷取FFmpeg解碼出的pcm資料

int getPcm(void *pcm,size_t pcm_size){
int frameCount=0;
int got_frame;
while (av_read_frame(pFormatCtx, packet) >= 0) {
if (packet->stream_index == audio_stream_idx) {
// 解碼 mp3 編碼格式frame----pcm frame
avcodec_decode_audio4(pCodecCtx, frame, &got_frame, packet);
if (got_frame) {
LOGE("解碼");
/**

  • int swr_convert(struct SwrContext s, uint8_t out, int out_count,
    const uint8_t
    in , int in_count);
    /
    swr_convert(swrContext, &out_buffer, 44100 * 2, (const uint8_t *) frame->data, frame->nb_samples);
    // 緩衝區的大小
    int size = av_samples_get_buffer_size(NULL, out_channer_nb, frame->nb_samples,
    AV_SAMPLE_FMT_S16, 1);
    pcm = out_buffer;
    *pcm_size = size;
    break;
    }
    }
    }
    return 0;
    }

5.播放音樂

(*slPlayItf)->SetPlayState(slPlayItf,SL_PLAYSTATE_PLAYING);

6.暫停音樂

(*slPlayItf)->SetPlayState(slPlayItf, SL_PLAYSTATE_PAUSED);

7.釋放資源

首先釋放關於OpenSL ES的實體

if(audioplayer!=NULL){    (*audioplayer)->Destroy(audioplayer);    audioplayer=NULL;    slBufferQueueItf=NULL;    slPlayItf=NULL;}if(outputMixObject!=NULL){    (*outputMixObject)->Destroy(outputMixObject);    outputMixObject=NULL;    outputMixEnvironmentalReverb=NULL;}if(engineObject!=NULL){    (*engineObject)->Destroy(engineObject);    engineObject=NULL;    engineEngine=NULL;}

然後釋放FFmpeg佔用的資源

av_free_packet(packet);av_free(out_buffer);av_frame_free(&frame);swr_free(&swrContext);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);

Android 音視頻深入 十四 FFmpeg與OpenSL ES 播放mp3音樂,能暫停(附源碼

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.