Android 音視頻深入 十四 FFmpeg與OpenSL ES 播放mp3音樂,能暫停(附源碼
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音樂,能暫停(附源碼