音视频开发进阶指南(第四章)-OpenSL-ES播放PCM音频(下)

简介: 笔记

创建接口过程

创建接口有两种方法:

  1. CreateXXX,这种获取的都是通用类型的接口,需要实例化
  2. GetInterface,获取的是具体类型的接口,因为它需要传入接口类型ID,不需要实例化


实例化过程

实例化就是自己给自己实例化,所有类型的实例化是固定的方法:

//obj是通用类型
//第二个参数表示是否异步执行 一般为false
(*obj)->Realize(obj, SL_BOOLEAN_FALSE);

播放的初始化工作是比较麻烦的,参数非常多,关键参数一定要弄清楚,否则不知其所以然。


1.1 引擎对象

想要调用OpenSL的API,它有一个唯一的门口slCreateEngine,很多文章里叫它引擎,我就叫引擎门口,直观一点,门口里面还有其它的小门口。

SLObjectItf engineObj;  //API门口
//1.1获取引擎对象接口
SLresult result = slCreateEngine(&engineObj, 0, 0, 0, 0, 0);
//1.2 SLObjectItf 类型,需要实例化门口引擎对象接口
result = (*engineObj)->Realize(engineObj, SL_BOOLEAN_FALSE);

1.2 获取引擎管理接口

有了引擎对象,接下来就要获取需要的引擎管理接口了,OpenSL有多种引擎管理接口,通过ID区分,例如下面的SL_IID_ENGINE

SLEngineItf engineEngine;
//2.1获取SLEngineItf类型引擎接口,后续操作将会使用这个接口
result = (*engineObj)->GetInterface(engineObj, SL_IID_ENGINE, &engineEngine);
//SLEngineItf 是具体类型不需要实例化

1.3 音频混音

混音器用于将多个音频混合并且输出到喇叭

SLObjectItf outputMixObj;
const SLInterfaceID ids[] = {SL_IID_VOLUME};
const SLboolean req[] = {SL_BOOLEAN_FALSE};
 //3.1创建音频输出混音对象接口
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObj, 0, ids, req);
//3.2 SLObjectItf 类型,实例化音频输出混音对象接口
result = (*outputMixObj)->Realize(outputMixObj, SL_BOOLEAN_FALSE);
//3.3 配置输出管道
SLDataLocator_OutputMix outputMixLocator = {SL_DATALOCATOR_OUTPUTMIX, outputMixObj};
SLDataSink outputSink = {&outputMixLocator, NULL};
// 配置输出源
 //4.1配置缓冲区Buffer Queue参数
 outputLocator = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
 //4.2设置音频源的音频格式
 SLDataFormat_PCM outputFormat = {
                                  SL_DATAFORMAT_PCM,   //指定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  //字节序:小端
 };
 //4.3输出源
 SLDataSource outputSource = {&outputLocator, &outputFormat};

1.4 获取播放器对象门口

播放器门口不是具体执行播放的工具,而是管理播放相关的缓冲,音频格式,混音,输出等

//5.1获取播放器对象接口
SLObjectItf audioPlayerObj;
const SLInterfaceID outputInterfaces[1] = {SL_IID_BUFFERQUEUE};
const SLboolean requireds[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine,
                                   &audioPlayerObj,
                                   &outputSource,//输出源
                                   &outputSink,//输出管道
                                   1,//接口个数
                                   outputInterfaces,//输出接口
                                   requireds);  //接口配置
//看到了没,又是SLObjectItf 类型,还得实例化
//5.2实例化播放器对象接口
result = (*audioPlayerObj)->Realize(audioPlayerObj, SL_BOOLEAN_FALSE);

1.5 音频输出对象

音频输出对象就是音频数据本身,具体一点就是存放即将被播放的数据所在的缓冲区

//6.1获取具体音频输出对象接口
SLAndroidSimpleBufferQueueItf outputBufferQueueInterface;
 result = (*audioPlayerObj)->GetInterface(audioPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
                                 &outputBufferQueueInterface);
//SLAndroidSimpleBufferQueueItf 是具体类型,不用实例化

1.6 具体的播放器对象

它是用来执行播放功能的,其它的条件都给它准备好了

SLPlayItf audioPlayerPlay;
//7.1获取播放器播放对象接口
result = (*audioPlayerObj)->GetInterface(audioPlayerObj, SL_IID_PLAY, &audioPlayerPlay);
if (result != SL_RESULT_SUCCESS) {
    LOGD("audioPlayerObj SL_IID_PLAY GetInterface failed,result=%d", result);
    return result;
}
//具体类型,不用实例化

1.7 设置回调

回调函数的作用是:通知。

通知什么?在播放的时候,OpenSL不会一次性把所有数据都读到缓冲区,需要用一点,拷贝一点,这个函数就是播放器告诉你,缓存用光了,需要新的数据。

所以在回调函数中需要把新的数据拷贝到缓冲区。

//8.1设置回调
result = (*outputBufferQueueInterface)->RegisterCallback(outputBufferQueueInterface,
                                                PlayCallback,
                                                this);

二、开始播放


//9设置为播放状态
(*audioPlayerPlay)->SetPlayState(audioPlayerPlay, SL_PLAYSTATE_PLAYING);
LOGI("setPlayerState:SL_PLAYSTATE_PLAYING");
//10启动回调机制,开始播放
PlayCallback(outputBufferQueueInterface, this);


三、写数据


前面说了,回调函数中需要填充新的数据:

SLuint32 getPcmData(void **pcm, FILE *pcmFile, uint8_t *out_buffer) {
    while (!feof(pcmFile)) {
        //因为PCM采样率为44100,采样精度为16BIT,所以一次读取2秒钟的采样
        size_t size = fread(out_buffer, 1, 44100 * 2 * 2, pcmFile);
        *pcm = out_buffer;
        return size;
    }
    return 0;
}
//当outputBufferQueueInterface中的数据消耗完就会触发回调
void PlayCallback(SLAndroidSimpleBufferQueueItf bufferQueue, void *pContext) {
    LOGI("PlayCallback");
    //获取数据
    SLuint32 size = getPcmData(&readPCMBuffer, pcmFile, tempBuffer);
    LOGI("PlayCallback, size=%d", size);
    if (NULL != readPCMBuffer && size > 0) {
        SLresult result = (*outputBufferQueueInterface)->Enqueue(outputBufferQueueInterface,
                                                                 readPCMBuffer, size);
    }
}

停止播放


//11.停止播放
(*audioPlayerPlay)->SetPlayState(audioPlayerPlay, SL_PLAYSTATE_STOPPED);

释放OpenSL ES资源


只需要销毁OpenSL ES对象,接口不需要做Destroy处理

(*engineObj)->Destroy(engineObj);
(*outputMixObj)->Destroy(outputMixObj);
(*audioPlayerObj)->Destroy(audioPlayerObj);
目录
相关文章
|
机器学习/深度学习 编解码 语音技术
音频基础知识 2
音频基础知识
306 0
|
编解码 API 语音技术
Opus从入门到精通(七)Opus编码基础之认识声音
前面我们分析完Opus的编解码api使用,封装原理等,接下来我们准备分析Opus编码原理.Opus编码是一个复杂的工作,我们需要做一些基本铺垫,包括认识声音,压缩编码基础.认识音频有助于我们了解音频特征,不仅对语音有助于我们理解编码技术,同时在语音识别,TTS等场景提供帮助
564 0
Opus从入门到精通(七)Opus编码基础之认识声音
|
6月前
|
安全 数据处理 数据格式
深入浅出:FFmpeg 音频解码与处理AVFrame全解析(三)
深入浅出:FFmpeg 音频解码与处理AVFrame全解析
292 0
|
6月前
|
缓存 编解码
FFmpeg开发笔记(十四)FFmpeg音频重采样的缓存
FFmpeg在视频流重编码和音频重采样中使用缓存机制。在音频文件格式转换时,特别是对于帧长度不固定的格式如ogg、amr、wma,需处理重采样缓存。通过调用`swr_convert`,传入空输入和0大小来清空缓存。在`swrmp3.c`中,修改帧样本数处理,并在循环结束后添加代码以冲刷缓存。编译并运行程序,将ogg文件重采样为MP3,日志显示操作成功,播放转换后的文件确认功能正常。
120 7
FFmpeg开发笔记(十四)FFmpeg音频重采样的缓存
|
6月前
|
存储 缓存 算法
ffmpeg 音视频同步进阶 剖析:ffmpeg音视频同步中特殊情况处理策略
ffmpeg 音视频同步进阶 剖析:ffmpeg音视频同步中特殊情况处理策略
157 0
|
6月前
|
存储 编解码 算法
深入浅出:FFmpeg 音频解码与处理AVFrame全解析(一)
深入浅出:FFmpeg 音频解码与处理AVFrame全解析
1294 0
|
6月前
|
存储 编解码 数据处理
深入浅出:FFmpeg 音频解码与处理AVFrame全解析(二)
深入浅出:FFmpeg 音频解码与处理AVFrame全解析
480 0
|
6月前
|
人工智能 算法 物联网
声音的变奏:深入理解ffmpeg音频格式转换的奥秘与应用(二)
声音的变奏:深入理解ffmpeg音频格式转换的奥秘与应用
167 0
|
6月前
|
存储 编解码 算法
声音的变奏:深入理解ffmpeg音频格式转换的奥秘与应用(一)
声音的变奏:深入理解ffmpeg音频格式转换的奥秘与应用
279 0
|
6月前
|
数据采集 Windows
【音视频 ffmpeg 学习】麦克风数据采集,命令行播放 持续更新
【音视频 ffmpeg 学习】麦克风数据采集,命令行播放 持续更新