关于

Android使用FFmpeg(一)--编译ffmpeg
Android使用FFmpeg(二)--Android Studio配置ffmpeg
Android使用FFmpeg(三)--ffmpeg实现视频播放
Android使用FFmpeg(四)--ffmpeg实现音频播放(使用AudioTrack进行播放)
Android使用FFmpeg(五)--ffmpeg实现音频播放(使用openSL ES进行播放)
Android使用FFmpeg(六)--ffmpeg实现音视频同步播放
Android使用FFmpeg(七)--ffmpeg实现暂停、快退快进播放

准备工作

openSL ES了解

正文

实现整体思路:使用ffmpeg解封装、解码视频得到pcm数据-->数据添加到opensl es缓冲区中-->播放
实现具体思路:因为在上篇中详细讲解了将视频通过ffmpeg解码成pcm,所以在这篇中将详细讲解如何通过opensl es播放。

openSLes实现大概流程图.png
我们还是根据流程图来进行代码的编写,注释已经写好,代码不懂之处可以看注释:
1.创建引擎:

//创建引擎void createEngine(){    slCreateEngine(&engineObject,0,NULL,0,NULL,NULL);//创建引擎    (*engineObject)->Realize(engineObject,SL_BOOLEAN_FALSE);//实现engineObject接口对象    (*engineObject)->GetInterface(engineObject,SL_IID_ENGINE,&engineEngine);//通过引擎调用接口初始化SLEngineItf}

2.创建混音器:

//创建混音器void createMixVolume(){    (*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.创建播放器并播放:

void createPlayer(){    //初始化ffmpeg    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);    //播放    (*slPlayItf)->SetPlayState(slPlayItf,SL_PLAYSTATE_PLAYING);    //开始播放    getQueueCallBack(slBufferQueueItf,NULL);}//回调的函数void getQueueCallBack(SLAndroidSimpleBufferQueueItf  slBufferQueueItf, void* context){    buffersize=0;    getPcm(&buffer,&buffersize);    if(buffer!=NULL&&buffersize!=0){        //将得到的数据加入到队列中        (*slBufferQueueItf)->Enqueue(slBufferQueueItf,buffer,buffersize);    }}

4.释放资源,从下往上依次释放:

void realseResource(){    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;    }    realseFFmpeg();}

5.关于使用ffmpeg得到pcm,我们将其单独写入一个cpp文件中,分为三部分:创建,得到pcm,释放资源。方便在openSL ES这边进行一个调用。具体代码的注释和思路在上篇中已经讲解,不懂的请看上篇的内容。这里的代码只是将整体的代码进行了分类和模块化。

//// Created by david on 2017/9/25.//#include "FFmpegMusic.h"AVFormatContext *pFormatCtx;AVCodecContext *pCodecCtx;AVCodec *pCodex;AVPacket *packet;AVFrame *frame;SwrContext *swrContext;uint8_t *out_buffer;int out_channer_nb;int audio_stream_idx=-1;//opensl es调用 int * rate,int *channelint createFFmpeg(int *rate,int *channel){    av_register_all();    char *input = "/sdcard/input.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;    return 0;}//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;}void realseFFmpeg(){    av_free_packet(packet);    av_free(out_buffer);    av_frame_free(&frame);    swr_free(&swrContext);    avcodec_close(pCodecCtx);    avformat_close_input(&pFormatCtx);}

小结

按照流程图来编写代码清晰明朗,只是一些细节的地方注意就行。另外注意以下几点:
1.测试时请使用真机测试,因为没有使用x86的cpu的.so;
2.注意添加权限;
3.注意将music.cpp添加到CMakeLists.txt中;
4.测试时请在手机中存入input.xxx的音频文件。
源码地址

更多相关文章

  1. Android版本更新实现
  2. Android(安卓)4编程入门经典
  3. android使用http协议上传文件
  4. Android动画中Interpolator 加速器的使用
  5. Android之Telephony各文件解释
  6. Android常见问题总结(三)
  7. android:maxLines和android:ellipsize同时使用导致显示异常
  8. Android实现程序前后台切换效果
  9. Android(安卓)ScrollView 实现小说阅读界面自动滚屏

随机推荐

  1. android如何查看app的内存占用情况
  2. 【原创】Android学习历程..持续中
  3. Android中ContextMenu用法实例
  4. Android拓展系列(9)--Android视频录制scr
  5. android绘制几何图形
  6. EditText 光标不显示问题
  7. android拍照上传的效果是如何实现的
  8. Android开发问题 - Some projects cannot
  9. Android(安卓)生成不同服务器配置,不同APP
  10. Android 异常处理:java.lang.IllegalArgum