项目地址
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;  pcmSLuint32         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);

转载于:https://blog.51cto.com/13591594/2073043

Android 音视频深入 十四 FFmpeg与OpenSL ES 播放mp3音乐,能暂停(附源码相关推荐

  1. Android 音视频深入 十四 FFmpeg与OpenSL ES 播放mp3音乐,能暂停(附源码下载)

    项目地址 https://github.com/979451341/FFmpegOpenslES 这次说的是FFmpeg解码mp3,数据给OpenSL ES播放,并且能够暂停. 1.创建引擎 slCr ...

  2. 音视频系列四 ffmpeg配合opensl es播放音频

    文章目录 一 解码成PCM 二 opensl es创建引擎,混音器 创建引擎 创建混音器 创建播放器,注册回调函数 一 解码成PCM 流程其实和视频差不多,参考播放视频的代码,稍作增改 先定义后面需要 ...

  3. Android 音视频深入 十五 FFmpeg 推流mp4文件(附源码下载)

    源码地址 https://github.com/979451341/Rtmp 1.配置RTMP服务器 这个我不多说贴两个博客分别是在mac和windows环境上的,大家跟着弄 MAC搭建RTMP服务器 ...

  4. Android 音视频深入 十六 FFmpeg 推流手机摄像头,实现直播 (附源码下载)

    源码地址 https://github.com/979451341/RtmpCamera/tree/master 配置RMTP服务器,虽然之前说了,这里就直接粘贴过来吧 1.配置RTMP服务器 这个我 ...

  5. Android 音视频深入 十八 FFmpeg播放视频,有声音(附源码下载)

    项目地址 https://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpegv%E6%92%AD%E6%94%BE%E8%A7 ...

  6. android音视频【十】音频mp3剪切

    人间观察 为了等你,我错过了等我的人. 介绍 Android中在一些短视频的制作app软件上,会有给视频增加背景音乐的功能,而背景音乐/歌曲(一般是mp3)是从服务器上下载后,然后本地解码,往往用户会 ...

  7. Android 音视频入门之音频采集、编码、播放

    今天我们学习音频的采集.编码.生成文件.转码等操作,我们生成三种格式的文件格式,pcm.wav.aac 三种格式,并且我们用 AudioStack 来播放音频,最后我们播放这个音频. 本篇文章你将学到 ...

  8. 【Java十大热门游戏合集】Java经典游戏项目(附源码课件

    家人们,我又来啦,今天我们来看一下学Java必练的10款游戏项目! 大家都知道学习编程必须要做的就是敲代码和做项目练手了,那项目有难有易,很多小伙伴不知道从哪里找项目来练习,今日我们来看一下初级项目中 ...

  9. android 键编译,Android 音视频学习系列 (四) 一键编译 32/64 位 FFmpeg 4.2.2

    前言 2020/5/20 增加了硬件解码编译脚本 编译环境 Centos + NDK20b + FFmpeg4.2.2 + Android-21/16 2020/4/26 更新了编译 64 位脚本 编 ...

最新文章

  1. window.open html打开一个新页面
  2. DS博客大作业--树
  3. android 反编译apktool工具
  4. TSM12M触摸芯片注意事项
  5. shell批量监控网站状态码
  6. Scala可以将Java中的接口当作特质来使用
  7. 图解Fiddler如何抓手机APP数据包过滤抓取
  8. 如何使用ASP.NET Core Web API实现短链接服务
  9. 【Elasticsearch】es CPU热点线程 HotThreads 源码解析
  10. kettlejava脚本的api_[翻译]KETTLE JAVA API :编程定制自己的Kettle转换(transformation)...
  11. vue 多点触控手势_手写 Vue 手势组件__Vue.js
  12. 对变量移位顺序读写_Java多线程并发读写锁ReadWriteLock实现原理剖析
  13. Android开发的消消乐游戏
  14. Hyperledger Avalon启动笔记
  15. 纯前端实现xls表格下载
  16. hdu2448 费用流
  17. HBuilderX编译微信小程序时报错:WebAssembly.instantiate():
  18. GIS开发:mapbox gl几种底图的加载
  19. [dEw] 简单文件系统设计笔记
  20. 深圳学区房购买-查查吧深圳学区地图

热门文章

  1. XMPP核心协议客户端
  2. node.js中net网络模块TCP服务端与客户端的使用
  3. Oracle数据库merge into的使用,存在则更新,不存在则插入
  4. 招兵买马,亚马逊再次补强人工智能
  5. 2017-2018-1 20179209《Linux内核原理与分析》第六周作业
  6. Centos6.3搭建cactinagios
  7. 修改路由器mac地址_你知道吗:路由器转发报文时,会剥掉MAC地址,重新封装
  8. Google Map 附近查询
  9. 关闭IE窗口时执行事件
  10. java9学习之模块化