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);//实现混音器接口对象
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;  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);

原文地址:http://blog.51cto.com/13591594/2073043

时间: 2024-10-28 21:11:02

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

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

源码地址https://github.com/979451341/RtmpCamera/tree/master 配置RMTP服务器,虽然之前说了,这里就直接粘贴过来吧 1.配置RTMP服务器 这个我不多说贴两个博客分别是在mac和windows环境上的,大家跟着弄MAC搭建RTMP服务器https://www.jianshu.com/p/6fcec3b9d644这个是在windows上的,RTMP服务器搭建(crtmpserver和nginx) https://www.jianshu.com/p

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

项目地址https://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpegv%E6%92%AD%E6%94%BE%E8%A7%86%E9%A2%91%E6%9C%89%E5%A3%B0%E9%9F%B3%EF%BC%8C%E6%9A%82%E5%81%9C%EF%BC%8C%E9%87%8A%E6%94%BE%E3%80%81%E5%BF%AB%E8%BF%9B%E3%80%81%E9%80%80%E5%90%8E 这个项

7个播放器效果展示(附源码)(一,二,三,四)

1.  HTML5+CSS3自定义视频播放器实现物理效果 源码下载/  在线演示 2.  html5触发式音频播放 这个插件集成了一些非常好的 JavaScript 库,提供一个方便使用的文本动画插件. 源码下载 /  在线演示 3. html5+css3酷炫音频播放器 源码下载/  在线演示 4.  css3迷你播放器面板 能在支持 FireFox.Chrome.Safari.傲游.搜狗.360浏览器. 源码下载/  在线演示 7个播放器效果展示(附源码)(一,二,三,四)

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

项目地址https://github.com/979451341/Myijkplayer 前段时候我觉得FFmpeg做个视频播放器好难,虽然播放上没问题,但暂停还有通过拖动进度条来设置播放进度,这些都即便做得到,可以那个延缓..... 现在学习一下目前移动端最知名的视频播放器的框架ijkplayer,这个框架他是基于FFmpeg.SDL.还有安卓原生API MediaCodec之类的.他是没有播放界面的,这个需要我们去做,所以这个里我就做个基于ijkplayer的视频播放器,随便浅显的说一下ij

Android 音视频深入 十 FFmpeg给视频加特效(附源码下载)

项目地址,求starhttps://github.com/979451341/Audio-and-video-learning-materials/tree/master/FFmpeg(AVfilter%E8%BF%87%E6%BB%A4%EF%BC%89 1.AVfilter结构体成员 这个特效要靠AVfilter来实现,首先说一下说AVfilter这个结构体的成员 / *过滤器定义.这定义了一个过滤器包含的垫,以及所有的 *用于与筛选器交互的回调函数. /typedef struct AVF

Android 音视频深入 六 使用FFmpeg播放视频(附源码下载)

本篇项目地址,求starhttps://github.com/979451341/Audio-and-video-learning-materials/tree/master/FFmpeg%E6%92%AD%E6%94%BE%E8%A7%86%E9%A2%91 首先FFmpeg是c语言写的,所以我们需要NDK的技术,然后我使用的NDK使用Cmake的,一开始就是说如何将FFmpeg导入项目,使用我的方法导入FFmpeg不用一分钟. 这个需要大家先在上面的代码地址里下载项目代码因为FFmpeg这个

Android 音视频深入 二十一 FFmpeg视频剪切

视频剪切我意外的发现上一次的视频压缩的代码能够运行FFmpeg视频剪切的命令,但是不能做视频合并的命令,因为不能读取记录了几个视频的路径的txt文件. 这里我就说直说视频剪切的过程,不说代码,只说log,毕竟我也不清楚代码往哪运行了?上一次的项目地址https://github.com/979451341/FFmpegCompress 首先我们需要将项目代码里改一下,将runCommand函数传入的参数改成下面这样 int ret = FFmpegNativeBridge.runCommand(

android音视频点/直播模块开发

前言 随着音视频领域的火热,在很多领域(教育,游戏,娱乐,体育,跑步,餐饮,音乐等)尝试做音视频直播/点播功能,那么作为开发一个小白,如何快速学习音视频基础知识,了解音视频编解码的传输协议,编解码方式,以及如何技术选型,如何解决遇到的坑,本文抛砖引玉,欢迎大咖交流. 一. 音视频的基础知识 1.1 基本概念 视频是什么 静止的画面叫图像(picture).连续的图像变化每秒超过24帧(frame)画面以上时,根椐视觉暂留原理, 人眼无法辨别每付单独的静态画面,看上去是平滑连续的视觉效果.这样的连

Android音视频即时通讯软件怎样通过JNI快速实现

Android音视频即时通讯软件怎样通过JNI快速实现 音视频通信 作为独立开发者或想缩短音视频开发周期的公司来说,想要在Android平台下实现音视频通信,最快捷的方法是寻找开源项目或调用其他公司API.之所以这么说是因为音视频通信技术涉及到底层音视频采集.解码. FFmpeg(音视频处理解决方案).媒体流传输协议等太多太多相关技术知识点.试了几个开源项目,视频差强人意,语音与视频不同步等不稳定因素.因此我把目光放到其他公司的API上(点击下载demo程序).demo程序API提供了一系列纯J