青春须早为,岂能长少年。这篇文章主要讲述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);
//实现混音器接口对象
SLresultsLresult = (*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里面所包含的编码格式转换成pcmSwcContext
swrContext = swr_alloc();
int length=0;
int got_frame;
//441002
out_buffer = (uint8_t ) av_malloc(44100 * 2);
uint64_tout_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_ {
SLuint32locatorType;
//缓冲区队列类型
SLuint32numBuffers;
//buffer位数
} */
SLDataLocator_AndroidBufferQueue android_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};
/**
typedef struct SLDataFormat_PCM_ {
SLuint32formatType;
pcm
SLuint32numChannels;
通道数
SLuint32samplesPerSec;
采样率
SLuint32bitsPerSample;
采样位数
SLuint32containerSize;
包含位数
SLuint32channelMask;
立体声
SLuint32endianness;
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(SLAndroidSimpleBufferQueueItfslBufferQueueItf, 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----pcmframe
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;
}
(*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;
}
【Android 音视频深入 十四FFmpeg与OpenSL ES 播放mp3音乐,能暂停(附源码】然后释放FFmpeg占用的资源
av_free_packet(packet);
av_free(out_buffer);
av_frame_free(&
frame);
swr_free(&
swrContext);
avcodec_close(pCodecCtx);
avformat_close_input(&
pFormatCtx);
推荐阅读
- [Android FrameWork 6.0源码学习] View的重绘ViewRootImpl的setView方法
- Android内存优化1 了解Android是如何管理App内存
- RobotFramework(App滑动屏幕)
- Android时间轴效果,直接使用在你的项目中
- Android项目实战(四十一)(游戏和视频类型应用 状态栏沉浸式效果)
- TCP-Wrappers访问控制
- 43.mapping的理解
- Android内存优化1 了解java GC 垃圾回收机制2 GC执行finalize的过程
- MATLAB 函数fill()