FFmpeg解码MP3用OpenSL ES播放

一、FFmpeg解码代码:

1、头文件

FFmpegMusic.h:

#ifndef FFMPEGAUDIO_FFMPEGMUSIC_H
#define FFMPEGAUDIO_FFMPEGMUSIC_H
#endif //FFMPEGAUDIO_FFMPEGMUSIC_H


extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"
#include <android/native_window_jni.h>
#include <unistd.h>
#include <android/log.h>
}
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"keke",FORMAT,##__VA_ARGS__);
//初始化FFmpeg
int createFFmpeg(int *rate,int *channal,const char* input);
//解码后的PCM数据
void getPCM(void **outBuffer,size_t *size);
//释放FFmpeg
void release();

2、实现

FFmpegMusic.cpp:

#include "FFmpegMusic.h"

AVFormatContext *pFormatCtx;
int audio_stream_idx = -1;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame * frame;
AVPacket * packet;

SwrContext *swrCtx;
uint8_t *out_buffer;

int createFFmpeg(int *rate,int *channal,const char *input){

    av_register_all();
    
    pFormatCtx = avformat_alloc_context();

   if(avformat_open_input(&pFormatCtx,input,NULL,NULL)!=0) {
       LOGI("打开失败");
       return -1 ;
   }

    if(avformat_find_stream_info(pFormatCtx,NULL)<0){
        LOGI("获取流信息失败")
        return -1;
    }

    for (int i = 0; i < pFormatCtx->nb_streams; ++i) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            audio_stream_idx = i;
            LOGI("找到音频流");
            break;
        }
    }

    pCodecCtx = pFormatCtx->streams[audio_stream_idx]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

    if(avcodec_open2(pCodecCtx,pCodec,NULL)<0) {
        LOGI("avcodec_open failed");
    }

    frame = av_frame_alloc();

    packet = (AVPacket *) av_malloc(sizeof(AVPacket));

    swrCtx = swr_alloc();

    swr_alloc_set_opts(swrCtx,
                       AV_CH_LAYOUT_STEREO,
                       AV_SAMPLE_FMT_S16,
                       pCodecCtx->sample_rate,
                       pCodecCtx->channel_layout,
                       pCodecCtx->sample_fmt,
                       pCodecCtx->sample_rate,0,NULL);

    swr_init(swrCtx);

    *rate = pCodecCtx->sample_rate;
    *channal = pCodecCtx->channels;
    out_buffer = (uint8_t *) av_malloc(44100 * 2);
    LOGI("初始刷完毕");
    return 0;
    
};

void getPCM(void **pcm,size_t *size){

    int got_frame;
    int out_channer_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
    while (av_read_frame(pFormatCtx, packet)>=0) {

        if (packet->stream_index == audio_stream_idx) {

            avcodec_decode_audio4(pCodecCtx, frame, & got_frame, packet);
            if (got_frame) {
                LOGI("解码");

                swr_convert(swrCtx, &out_buffer, 44100 * 2, (const uint8_t **) frame->data, frame->nb_samples);
                int out_buffer_size=av_samples_get_buffer_size(NULL, out_channer_nb, frame->nb_samples,AV_SAMPLE_FMT_S16, 1);
                *pcm =out_buffer;
                *size =out_buffer_size;
                break;
            }
        }
    }
};
void release(){
    av_free_packet(packet);
    av_frame_free(&frame);
    av_free(out_buffer);
    swr_free(&swrCtx);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
};

二、重点 OpenSL ES 部分

回头加下注释,感觉OpenSL调用有点反人类,有空再研究加深下理解。

#include <jni.h>
#include <string>
#include <android/log.h>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <assert.h>
#include "FFmpegMusic.h"

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"
#include <android/native_window_jni.h>
#include <unistd.h>
}
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"keke",FORMAT,##__VA_ARGS__);

SLObjectItf slObjectItf = NULL;
//混音器
SLObjectItf outputMixObject = NULL;
SLEngineItf engineEngine = NULL;

SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;

const SLEnvironmentalReverbSettings settings = SL_I3DL2_ENVIRONMENT_PRESET_DEFAULT;

SLObjectItf pPlayer;
SLPlayItf playerI;

//缓冲器队列接口
SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;

SLVolumeItf bqPlayerVolume;

size_t bufferSize;
void *buffer;
void playerCallBack( SLAndroidSimpleBufferQueueItf caller,void *pContext){
    bufferSize = 0;

    getPCM(&buffer,&bufferSize);
    if (buffer != NULL && bufferSize != 0) {
        SLresult result;

        result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue,buffer,bufferSize);

//        assert()
        LOGE("keke  bqPlayerCallback :%d", result);
    }

}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_ffmpegaudio_MyPlayer_sound_1sl(JNIEnv *env, jobject instance, jstring input_) {

    const char *input = env->GetStringUTFChars(input_, 0);

    SLresult sLresult;

    //初始化引擎
    slCreateEngine(&slObjectItf,0,NULL,0,NULL,NULL);

    (*slObjectItf)->Realize(slObjectItf, SL_BOOLEAN_FALSE);

    (*slObjectItf)->GetInterface(slObjectItf,SL_IID_ENGINE,&engineEngine);
    LOGE("地址 %p",engineEngine);

    (*engineEngine)->CreateOutputMix(engineEngine,&outputMixObject,0,0,0);

    (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);

    sLresult = (*outputMixObject)->GetInterface(outputMixObject,SL_IID_ENVIRONMENTALREVERB,&outputMixEnvironmentalReverb);

    if (SL_RESULT_SUCCESS == sLresult) {
        (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb,&settings);

    }

    int rate;
    int channels;
     createFFmpeg(&rate,&channels,input);
    LOGE(" 比特率%d  ,channels %d" ,rate,channels);

    SLDataLocator_AndroidSimpleBufferQueue andorid_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};

    SLDataFormat_PCM pcm = {SL_DATAFORMAT_PCM,2,SL_SAMPLINGRATE_44_1,
                            SL_PCMSAMPLEFORMAT_FIXED_16,
                            SL_PCMSAMPLEFORMAT_FIXED_16,
                            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
                            SL_BYTEORDER_LITTLEENDIAN};

    SLDataSource slDataSource = {&andorid_queue,&pcm};

    SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX,outputMixObject};

    SLDataSink slDataSink = {&outputMix,NULL};

    const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND,
            /*SL_IID_MUTESOLO,*/ SL_IID_VOLUME};
    const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,
            /*SL_BOOLEAN_TRUE,*/ SL_BOOLEAN_TRUE};

    sLresult =(*engineEngine)->CreateAudioPlayer(engineEngine,&pPlayer,&slDataSource,&slDataSink,3,ids,req);

    LOGE("初始化播放器是否成功  %d    bqPlayerObject  %d",sLresult,pPlayer);

    (*pPlayer)->Realize(pPlayer,SL_BOOLEAN_FALSE);

    (*pPlayer)->GetInterface(pPlayer,SL_IID_PLAY,&playerI);

    //注册回调缓冲区 //获取缓冲队列接口
    (*pPlayer)->GetInterface(pPlayer, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);
    LOGE("获取缓冲区数据");

    (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue,playerCallBack,NULL);

    //    获取音量接口
    (*pPlayer)->GetInterface(pPlayer, SL_IID_VOLUME, &bqPlayerVolume);

//    获取播放状态接口
    (*playerI)->SetPlayState(playerI, SL_PLAYSTATE_PLAYING);

    playerCallBack(bqPlayerBufferQueue, NULL);

    env->ReleaseStringUTFChars(input_, input);
}
void shutdown()
{
 if (pPlayer != NULL) {
        (*pPlayer)->Destroy(pPlayer);
        pPlayer = NULL;
        playerI = NULL;
        bqPlayerBufferQueue = NULL;
        bqPlayerVolume = NULL;
    }
    if (outputMixObject != NULL) {
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = NULL;
        outputMixEnvironmentalReverb = NULL;
    }
    if (slObjectItf != NULL) {
        (*slObjectItf)->Destroy(slObjectItf);
        slObjectItf = NULL;
        engineEngine = NULL;
    }
    release();
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_ffmpegaudio_MyPlayer_stop(JNIEnv *env, jobject instance) {

     shutdown();
}

三、调用

public class MyPlayer {
    static{
        System.loadLibrary("avcodec-56");
        System.loadLibrary("avdevice-56");
        System.loadLibrary("avfilter-5");
        System.loadLibrary("avformat-56");
        System.loadLibrary("avutil-54");
        System.loadLibrary("postproc-53");
        System.loadLibrary("swresample-1");
        System.loadLibrary("swscale-3");
        System.loadLibrary("native-lib");
    }

    public native void sound_sl(String input);
    public native void stop();
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容