项目地址
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;
// 44100*2
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音乐,能暂停(附源码下载)的更多相关文章

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

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

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

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

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

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

  4. Android 音视频深入 十二 FFmpeg视频替换声音(附源码下载)

    项目地址,求starhttps://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpeg%E7%BB%99%E8%A7%86%E ...

  5. Android中Canvas绘图基础详解(附源码下载) (转)

    Android中Canvas绘图基础详解(附源码下载) 原文链接  http://blog.csdn.net/iispring/article/details/49770651   AndroidCa ...

  6. Android仿今日头条和知乎等App顶部滑动导航实现代码分析及源码下载

    一.本文所涉及到的知识点 源码下载 二.目标 通过利用ViewPager+FragmentStatePagerAdapter+TabLayout 实现顶部滑动效果,如图: 三.知识点讲解 1.View ...

  7. Android 音视频深入 二十 FFmpeg视频压缩(附源码下载)

    项目源码https://github.com/979451341/FFmpegCompress 这个视频压缩是通过类似在mac终端上输入FFmpeg命令来完成,意思是我们需要在Android上达到能够 ...

  8. Android 音视频深入 十九 使用ijkplayer做个视频播放器(附源码下载)

    项目地址https://github.com/979451341/Myijkplayer 前段时候我觉得FFmpeg做个视频播放器好难,虽然播放上没问题,但暂停还有通过拖动进度条来设置播放进度,这些都 ...

  9. Android 音视频深入 十一 FFmpeg和AudioTrack播放声音(附源码下载)

    项目地址,求starhttps://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpeg%E6%92%AD%E6%94%BE%E ...

随机推荐

  1. 第二章 函数和window对象

    1.什么是函数函数相当于Java中的方法,每一个函数可以做一件事情,但是不属于某一个类 2.使用函数的好处:使代码模块化,功能分工明确,方便调用,思路功能清晰 3.函数的分类:(1)系统函数:系统提前 ...

  2. JAVA 多线程之volatile的介绍

    volatile的介绍 volatile的主要作用是:提示编译器该对象的值有可能在编译器未监测的情况下被改变. volatile类似于大家所熟知的const也是一个类型修饰符.volatile是给编译 ...

  3. python locust 性能测试:locust 关联---提取返回数据并使用

    from locust import HttpLocust, TaskSet, taskimport jsonfrom common import readConfig class UserBehav ...

  4. Internet spirit

    互联网思维精髓总结为 :1.用户思维:2.简约思维:3.极致思维:4.迭代思维:5.流量思维:6.社会化思维:7.大数据思维:8.平台思维:9.跨界思维.

  5. 大话npm,cnpm和yarn

    npm:基于node.js的包管理工具; 常用命令 npm install 包名; 缺点:因服务器在国外,所以下载包的速度超级慢,所以出现了cnpm和yarn cnpm:跟npm是一样的,这是淘宝出的 ...

  6. axios的封装

    function axios(options){ var promise = new Promise((resolve,reject)=>{ var xhr = null; if(window. ...

  7. python传参是传值还是传引用

    在此之前先来看看变量和对象的关系:Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是对象.而变量是对象的一个引用(又称为名字或者标签),对象的操作都是通过引用来完成的.例 ...

  8. CSS基础【1】:体验CSS

    CSS起源 web的衰落:在 web 早期(1990-1993),html是一个很局限的语言.几乎完全由用于描述段落,超链接,列表和标题的结构化元素组成.随着万维网的出现(用户交互体验的加强),对 h ...

  9. iOS关于直播的链接

    iOS关于直播集成的链接 http://www.jianshu.com/p/7b2f1df74420 https://www.cnblogs.com/graveliang/p/5683617.html ...

  10. ssm整合(方案一 引入jar)

    最近为了研究redis整合,基本都是ssm+redis 好吧 我是老古董了,以前都是ssi,那就折腾一下ssm先,具体方案: 方案一:基本引入jar版.方案二:maven版(后续继续整) 这篇主要是通 ...