# 测试仓库
**Repository Path**: iot-solution_admin/test-warehouse
## Basic Information
- **Project Name**: 测试仓库
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-03-15
- **Last Updated**: 2022-03-15
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# AV框架
## 简介
AV框架是一个轻量级的多媒体开发框架,是[AV组件](README)的一部分。其采用典型的4层多媒体模型设计及面向对象的思想开发,使得用户在此基础上易于复用与扩展。若用户需要增加媒体取流格式、扩展媒体解封装格式或增加解码器类型,请参考[AV组件适配对接](av_adaptor)文档。
当前AV组件中提供了wav、mp3、m4a、amrnb、amrwb、flac、adts等音频格式的支持。框架本身的设计向后提供视频支持。若有需要,可基于此AV框架进行扩展。
### AV框架分层设计
AV框架主要抽象为四个层次:
**媒体接入层:**access层,负责媒体数据的来源,可能是file、http、fifo、mem等。
**解复用层:**demux层,负责把容器里的音视频数据剥离出来,然后分别送给audio/video decoder。
**解码层:**decoder层,将解码完成后的数据(yuv、pcm)送给audio/video output输出。
**输出层:**output层,负责将decoder过来的数据呈现/播放出来。
如果把数据想象成流水的话,每层的功能虽然不同,但是他们大致抽象的功能都是接收上个模块过来的数据,然后加工并把加工后的数据送到下一个模块。把上述这些层通过某种方式连接起来,就形成了一个音频播放器。
### 媒体接入层(stream)
UML设计如下图所示:
- stream_ops_http、stream_ops_mem、stream_ops_file、stream_ops_fifo分别对应于网络流、内存流、本地文件流、fifo流取流播放。
- 其中语音合成(TTS)流的播放可基于stream_ops_fifo实现。
- 后续对于新增的媒体接入类型,可根据struct stream_ops结构中定义的类型,实现对应的接口即可扩展。具体如何扩展,请参考[AV组件适配对接](av_adaptor)文档。
- 主要类型定义详见stream_cls.h
**解复用层(avformat)**
UML设计如下图所示:
- 其中demux_ops_wav、demux_ops_mp3、demux_ops_m4a分别对应于wav、mp3、m4a音频复用格式解复用。解复用出来的一帧音频数据会被送到对应的解码器进行解码。
- 当前支持wav、mp3、mp4、adts、flac、asf、amr等格式的解复用
- 后续对于新增的解复用格式,可根据struct demux_ops结构中定义的类型,实现对应的接口即可扩展
- 主要类型定义详见demux_cls.h
### **解码层(avcodec)**
- 该层将demux解复用后出来的一帧帧编码数据解码成音视频裸数据(pcm/yuv)。
- 在某些情况下,某些编码类型解码时可能占用很高的主频(如在ck803ef上,HE-AAC解码主频需求在240M左右),此时可能需要通过核间通信(mailbox/IPC)将解码工作放到另一核上执行。
- 核间解码接口使用请参考[此处](adicore)介绍。开发者可略过AV框架,直接使用核间解码功能。
- 解码层为考虑扩展性,支持本地(核内)与跨核(核间)解码。其中核间解码适配层请参考av/avcodec/ad_ipc.c使用。
核间解码结构如下图所示:
核间解码时序图如下所示:
核内/核间解码框架设计如下图所示:
解码器UML设计如下图所示:
- 其中ad_ops_pvmp3、ad_ops_rawaudio、ad_ops_opus、ad_ops_fdk分别对应于mp3、裸pcm(解码透传)、opus、aac音频编码格式解码
- 当前支持mp3、aac、adpcm_ms、flac、amrnb、amrwb等格式的解码
- 后续对于新增的解码类型,可根据struct ad_ops结构中定义的类型,实现对应的接口即可扩展
- 主要类型定义详见ad_cls.h
### **输出层(output)**
UML设计如下图所示:
- 其中ao_ops_alsa对应于采用alsa标准音频输出接口实现。通过alsa层来屏蔽各产品不同codec的实现
- 后续对于新增的输出类型,可根据struct ao_ops结构中定义的类型或在alsa/sound驱动层,实现对应的接口即可扩展
- 主要类型定义详见ao_cls.h
音频输出链路如下图所示:
## 媒体接入层接口
#### 流类型注册
##### 注册AV框架中stream类型
```c
static inline int stream_register_all();
```
该接口将AV框架中实现的内存流、文件流、网络流、队列流都注册使能。这四种流的注册接口如下所示:
```c
int stream_register_mem(); ///< 内存流
int stream_register_file(); ///< 文件流
int stream_register_http(); ///< http/https网络流
int stream_register_fifo(); ///< 文件流
```
播放器当前默认将这四种流都注册进去。用户可根据需要分别注册所需流类型。
返回值:
> 调用成功时返回0,否则返回-1。
##### 注册stream子类型
```c
int stream_ops_register(const struct stream_ops *ops);
```
通过该接口,用户可以将自己扩展定义的ops子类型注册到系统中。具体如何扩展,请参考[AV组件适配对接](av_adaptor)文档。
返回值:
> 调用成功时返回0,否则返回-1。
注意事项:
> 当前stream子类型最大支持注册16个,定义在stream.h中的STREAM_OPS_MAX。用户可根据需要修改
#### 打开流
##### 流配置参数初始化
```c
int stream_conf_init(stm_conf_t *stm_cnf);
```
初始化流配置参数,用于stream的打开。
用户通过该接口获取stream默认的配置参数后,可根据需要修改相关参数。配置参数由结构体stm_conf_t表示,其包括缓存配置、解密回调、取流超时时间等,详细定义如下:
```c
typedef struct stream_conf {
enum stream_mode mode; ///< 打开流的模式
irq_av_t irq; ///< 取流中断回调。使用者通知stream,防止长时间阻塞
uint32_t rcv_timeout; ///< 取流超时时间(ms)。AOS_WAIT_FOREVER表示一直等,0表示使用默认配置
uint32_t cache_size; ///< 取流缓存大小(byte)
uint32_t cache_start_threshold; ///< 当码流缓存到cache_size的指定百分比时才开始播放,防止网络状态不好时播放时断时续,优化播放效果。取值范围是0~100
get_decrypt_cb_t get_dec_cb; ///< 解密回调接口,用于从上层获取秘钥(若流是加密的)
} stm_conf_t;
```
对于get_dec_cb,用户需实现下面类型的定义。该接口在从码流中解析出特殊key时,可回调到上层获取秘钥。具体可由用户扩展实现自行定义。
```c
typedef int (*get_decrypt_cb_t)(const void *in, size_t ilen, void *out, size_t *olen);
```
返回值:
> 调用成功时返回0,否则返回-1。
注意事项:
> 对于流的模式,当前仅支持STREAM_READ读模式
##### 打开流
```c
stream_cls_t* stream_open(const char *url, const stm_conf_t *stm_cnf);
```
根据配置参数stm_cnf,打开指定url的流。该接口调用前,必须保证对应url的stream已经成功注册。其会根据url的前缀找到应的stream_ops打开流。stream_cls_t结构体中的主要成员如下:
```c
typedef struct stream_cls stream_cls_t;
struct stream_cls {
int32_t buf_pos; ///< 当前读指针在buf数组中的偏移量
int32_t buf_len; ///< buf数组有效数据长度
int32_t pos; ///< 当前读进buf数组中的位置
int32_t size; ///< 流的大小
char *url;
uint8_t eof; ///< 流是否结束
uint8_t quit;
uint8_t seekable; ///< 流是否支持seek操作
irq_av_t irq;
get_decrypt_cb_t get_dec_cb; ///< 解密接口,同stm_conf_t中定义
uint32_t rcv_timeout; ///< 读超时时间,同stm_conf_t中定义
aos_event_t cache_quit; ///< 用户缓存线程事件
uint32_t cache_size; ///< 同stm_conf_t中定义
uint32_t cache_start_threshold; ///< 同stm_conf_t中定义
uint8_t cache_start_upto; ///< 用于缓存是否达到阈值标记
uint8_t cache_status; ///< 缓存的状态,停止态、运行态
int32_t cache_pos; ///< cache position. used when cache enable
void *priv; ///< 指向stream子类结构
const struct stream_ops *ops; ///< 子类ops
aos_mutex_t lock;
sfifo_t *fifo; ///< 用于缓冲线程的队列
uint8_t buf[STREAM_BUF_SIZE_MAX];///< 内部buf块(比如说流来自sd卡,为效率考虑按块读取)
};
```
该接口对于网络流(实际依赖于stream_ops结构中的enable_cache字段),会根据stm_cnf中的cache_size创建缓冲fifo和缓冲线程,以用于处理网络抖动带来的播放卡顿问题。
url格式定义规则如下表所示:
| 流类型 | URL前缀 | URL格式 |
| ------------ | ---------- | :----------------------------------------------------------- |
| 网络流 | http(s):// | http(s)://ip:port/xx.mp3 |
| 文件流(SD卡) | file:// | file:///fatfs0/xx.mp3?avformat=%s&avcodec=%u&channel=%u&rate=%u |
| 内存流 | mem:// | mem://addr=%u&size=%u&avformat=%u&avcodec=%u&channel=%u&rate=%u |
| fifo流 | fifo:// | fifo://tts/1?avformat=%s&avcodec=%u&channel=%u&rate=%u |
- 对于能够探测到媒体信息的码流,url格式中的avformat、avcodec、channel、rate字段不是必须的。
- 这些配置项一般用于raw pcm的播放(通过这些参数传入裸流的具体格式)。
- avformat字段可选有rawaudio/wav/mp3/m4a。
- avcodec字段可选有`pcm_s16be/pcm_s32be/pcm_s16le/pcm_s32le/pcm_s8/pcm_u16be/pcm_u32be/pcm_u16le/pcm_u32le/pcm_u8`。
url格式具体示例如下表所示:
| 流类型 | 示例 |
| ------------ | ------------------------------------------------------------ |
| 网络流 | http://www.baidu.com/1.mp3 |
| 文件流(SD卡) | file:///fatfs0/test.MP3 |
| 内存流 | mem://addr=765432&size=1024&avformat=rawaudio&avcodec=pcm_s16le &channel=1&rate=16000 |
| fifo流 | fifo://tts/1 |
返回值:
> 调用失败时,返回NULL。
#### 读写、控制流
##### 输入流
```c
int stream_read(stream_cls_t *o, uint8_t *buf, size_t count);
```
从指定stream中读取count个字节的数据存储到buf中。
返回值:
> 大于0表示实际读取流字节数,等于0表示读空,失败返回-1
##### 单变量读取
```c
int stream_r8(stream_cls_t *o);
uint16_t stream_r16be(stream_cls_t *o);
uint32_t stream_r24be(stream_cls_t *o);
uint32_t stream_r32be(stream_cls_t *o);
uint64_t stream_r64be(stream_cls_t *o);
uint16_t stream_r16le(stream_cls_t *o);
uint32_t stream_r24le(stream_cls_t *o);
uint32_t stream_r32le(stream_cls_t *o);
uint64_t stream_r64le(stream_cls_t *o);
```
按照1/2/3/4/8字节(大小端)读取流
##### 输出流
```c
int stream_write(stream_cls_t *o, const uint8_t *buf, size_t count);
```
将指定字节大小count的buf写入到stream中。该接口尚未实现。
返回值:
> 大于0表示实际写入流字节数,等于0表示写满,失败返回-1
##### 跳转流
```c
int stream_seek(stream_cls_t *o, int32_t offset, int whence);
```
跳转到流的指定位置处。whence表示偏移基准,有SEEK_SET/SEEK_CUR/SEEK_END这三个取值。通过offset和whence可以计算出跳转的绝对位置。
当要跳转的绝对问题不在当前缓存范围内,对于网络流来说底层会退出缓存线程并重新根据新的位置打开流、创建相关缓存资源。
返回值:
> 调用成功时返回0,否则返回-1。
##### 跳过流
```c
int stream_skip(stream_cls_t *o, int32_t offset);
```
从stream当前位置跳过指定偏移量。
当要跳转的绝对问题不在当前缓存范围内,对于网络流来说底层会退出缓存线程并重新根据新的位置打开流、创建相关缓存资源。
返回值:
> 调用成功时返回0,否则返回-1。
##### 控制流
```c
int stream_control(stream_cls_t *o, int cmd, void *arg, size_t *arg_size);
```
通过控制命令字cmd控制流。该接口尚未实现,预留。
返回值:
> 调用成功时返回0,否则返回-1。
#### 获取流状态、信息
##### 获取流位置
```c
int stream_tell(stream_cls_t *o);
```
获取流当前读写位置
- 返回值:
> 调用成功时大于等于0,否则返回-1。
##### 流是否支持跳转
```c
int stream_is_seekable(stream_cls_t *o);
```
获取流是否支持跳转
返回值:
> 支持流跳转返回1,否则返回0。
##### 流是否结束
```c
int stream_is_eof(stream_cls_t *o);
```
获取流是否读结束
返回值:
> 已结束返回1,否则返回0。
##### 获取流大小
```c
int stream_get_size(stream_cls_t *o);
```
获取流的大小。
返回值:
> 调用失败返回-1。
注意事项:
> 对于直播流或fifo流等,流的大小可能不是固定的或获取不到。
##### 获取流路径
```c
const char* stream_get_url(stream_cls_t *o);
```
获取当前流的url地址
返回值:
> 调用失败返回NULL。
#### 关闭流
```c
int stream_close(stream_cls_t *o);
```
关闭流,销毁相关资源。
返回值:
> 调用成功时返回0,否则返回-1。
## 解复用层接口
#### 解复用类型注册
##### 注册AV框架中的解复用类型
```c
static inline int demux_register_all();
```
该接口将AV框架中实现的wav、mp3、mp4、adts、裸流、flac、asf、amr都注册使能。这几种解复用的注册接口如下所示:
```c
int demux_register_wav(); ///< wav格式解复用
int demux_register_mp3(); ///< mp3格式解复用
int demux_register_mp4(); ///< mp4格式解复用
int demux_register_adts(); ///< adts格式解复用
int demux_register_rawaudio(); ///< pcm裸流格式解复用
int demux_register_flac(); ///< flac格式解复用
int demux_register_asf(); ///< asf格式解复用
int demux_register_amr(); ///< amr格式解复用
```
播放器当前默认将这几种解复用类型都注册进去。用户可根据需要分别注册所需类型。
返回值:
> 调用成功时返回0,否则返回-1。
##### 注册解复用子类型
```c
int demux_ops_register(const struct demux_ops *ops);
```
通过该接口,用户可以将自己扩展定义的ops子类型注册到系统中。具体如何扩展,请参考[AV组件适配对接](av_adaptor)文档。
返回值:
> 调用成功时返回0,否则返回-1。
注意事项:
> 当前demux子类型最大支持注册16个,定义在demux.h中的DEMUX_OPS_MAX。用户可根据需要修改
#### 打开解复用
```c
demux_cls_t* demux_open(stream_cls_t *s);
```
根据流打开解复用器。其会根据url中的avformat参数或者对流的内容探测,找到最匹配的demux_ops。
函数返回后,将会获取到码流的编解码、采样率等信息。用户可根据这些信息创建相应的解码器解码。demux_cls_t结构体中的主要成员如下:
```c
typedef struct demux_cls demux_cls_t;
struct demux_cls {
stream_cls_t *s;
sh_audio_t ash; ///< 解复用出的音频采样率、位率等
aos_mutex_t lock;
avpacket_t fpkt; ///< 媒体第一个编码帧,用于编解码信息获取,同时减少不必要的seek
size_t id3v2size; ///< id3信息的大小
uint64_t bps; ///< 码率
size_t time_scale; ///< 时基大小
uint64_t duration; ///< 音频总时长,ms
track_info_t *tracks; ///< 媒体轨道信息
void *priv; ///< 指向解复用器子类结构
const struct demux_ops *ops; ///< 解复用器子类接口
};
```
返回值:
> 调用失败时,返回NULL。
#### 解出音频包及控制
##### 解出一帧编码数据
```c
int demux_read_packet(demux_cls_t *o, avpacket_t *pkt);
```
调用找到的demux_ops解复用器,解出一帧编码包。包的数据内容和大小存储在pkt结构中。avpacket_t结构体中的主要成员如下:
```c
typedef struct avpacket {
uint8_t *data;
int32_t size; ///< data的总大小
int32_t len; ///< 编码帧data的有效长度
int64_t pts; ///< 当前帧的present timestamp
} avpacket_t;
```
返回值:
> 大于0表示实际读取编码帧字节数,等于0表示流已经结束,失败返回-1
##### 解复用器跳转
```c
int demux_seek(demux_cls_t *o, uint64_t timestamp);
```
解复用器跳转到指定时间点(ms),后续可从该时间点开始读取一帧编码帧数据解码播放。
返回值:
> 调用成功时返回0,否则返回-1。
##### 解复用器控制
```c
int demux_control(demux_cls_t *o, int cmd, void *arg, size_t *arg_size);
```
通过控制命令字cmd控制流。该接口尚未实现,预留。
返回值:
> 调用成功时返回0,否则返回-1。
#### 关闭解复用
```c
int demux_close(demux_cls_t *o);
```
关闭解复用器,销毁相关资源。
返回值:
> 调用成功时返回0,否则返回-1。
## 解码层接口
#### 解码器类型注册
##### 注册AV框架中的解码器类型
```c
static inline int ad_register_all();
```
解码器的注册提供了一个总接口,但具体注册了哪些解码器类型依赖于解决方案下package.yaml中的CONFIG_DECODER_XXX等配置项。如solutions/pangu_demo/package.yaml中,以下配置项默认可将pcm裸流、mp3解码器(pvmp3)、aac解码器(fdk-aac)注册进去。
```
CONFIG_DECODER_PCM: 1
CONFIG_DECODER_PVMP3: 1
CONFIG_DECODER_FLAC: 0
CONFIG_DECODER_FDK: 1
CONFIG_DECODER_ADPCM_MS: 0
CONFIG_DECODER_AMRNB: 0
CONFIG_DECODER_AMRWB: 0
CONFIG_DECODER_IPC: 0
```
AV框架中当前提供了如下几种解码器的注册接口:
```c
int ad_register_pcm(); ///< 用于裸流解码(透传)
int ad_register_pvmp3(); ///< 用于mp3解码,使用pvmp3解码库
int ad_register_fdk(); ///< 用于aac解码,使用fdkaac解码库
int ad_register_adpcm_ms(); ///< 用于adpcm_ms解码
int ad_register_flac(); ///< 用于flac解码
int ad_register_amrnb(); ///< 用于amrnb解码
int ad_register_amrwb(); ///< 用于amrwb解码
int ad_register_ipc(); ///< 用于核间解码(具体的解码运算工作跑在其他核上)
```
用户若不想通过宏定义形式使用ad_register_all接口,可以分别调用这些接口。
对于核间解码ad_register_ipc,若具体mp3解码算法运行在其他核(此处称为DSP核)上,则本地无需调用ad_register_pvmp3接口。同时在DSP核上,将ad_register_pvmp3注册进去,编译成固件。核间解码具体如何使用,请参考[av_cp](av_cp)组件说明。
返回值:
> 调用成功时返回0,否则返回-1。
##### 注册解码器子类型
```c
int ad_ops_register(const struct ad_ops *ops);
```
通过该接口,用户可以将自己扩展定义的ops子类型注册到系统中。具体如何扩展,请参考[AV组件适配对接](av_adaptor)说明。
返回值:
> 调用成功时返回0,否则返回-1。
注意事项:
> 当前音频解码器子类型最大支持注册16个,定义在ad.h中的AD_OPS_MAX。用户可根据需要修改
#### 打开解码器
##### 解码器参数初始化
```c
int ad_conf_init(ad_conf_t *ad_cnf);
```
初始化解码器配置参数,用于打开解码器。
用户通过该接口获取默认的配置参数后,可根据需要修改相关参数。配置参数由结构体ad_conf_t表示,其包括采样格式、额外解码配置参数等,详细定义如下:
```c
typedef struct ad_conf {
sf_t sf; ///< 音频采样格式,如果有需要
uint8_t *extradata; ///< Audio Specific Config.额外编码参数,如果存在。如mp4
int32_t extradata_size;
uint32_t block_align; ///< 块大小,如果需要
uint32_t bps; ///< 比特率, if needed
} ad_conf_t;
```
返回值:
> 调用成功时返回0,否则返回-1。
注意事项:
> - 对于裸流解码(透传),需要将sf传递给pcm解码器
> - 对于extradata和extradata_size,如果从封装格式中拆出核心解码信息是单独存储的,则需要传入。如mp4
> - 对于block_align,则在adpcm_ms解码时需要从wav封装格式中拆出传入
##### 打开解码器
```c
ad_cls_t* ad_open(avcodec_id_t id, const ad_conf_t *ad_cnf);
```
根据解码配置参数ad_cnf打开指定codec id的解码器。ad_cls_t结构体中的主要成员如下:
```c
struct ad_cls {
sh_audio_t ash; ///< 音频采样等信息
aos_mutex_t lock;
void *priv; ///< 解码器子类结构
const struct ad_ops *ops; ///< 解码器子类接口
};
```
返回值:
> 调用失败时,返回NULL。
注意事项:
> 解码器打开后,获得的音频采样格式(sf)可能与解复用出来的有差异(与相应解码库的配置或实现有关系)。后续打开音频输出时,以解码器的sf为准(从sh_audio_t结构中获取)
#### 音频解码及控制
##### 解码音频帧
```c
int ad_decode(ad_cls_t *o, avframe_t *frame, int *got_frame, const avpacket_t *pkt);
```
将一帧音频包pkt解码,解码数据存储在frame结构中。got_frame表明本次是否可以解出数据(可能需要向解码器传入多个音频包才能解出来一帧)。avframe_t结构体中的主要成员如下:
```c
typedef struct avframe {
#define AV_DATA_POINTERS_MAX (8)
avmedia_type_t type; ///< 媒体类型
uint8_t *data[AV_DATA_POINTERS_MAX]; ///< 存储帧数据指针。帧的申请来源于内存池或malloc申请
int linesize[AV_DATA_POINTERS_MAX]; ///< 帧的有效数据大小
int capsize[AV_DATA_POINTERS_MAX]; ///< data指针指向的数据总大小,linesize 需<= capsize
uint8_t *mpool; ///< 表明data指针指向的内存来源于此内存池
size_t msize; ///< 内存池的大小
size_t moffset; ///< 内存池当前已使用的偏移量
/* 音频信息 */
sf_t sf; ///< 音频采样格式
int nb_samples; ///< 每个声道的采样个数
} avframe_t;
```
返回值:
> 调用失败时,返回-1,大于0表示解码一帧消耗的原始编码帧字节数
##### 重置解码器
```c
int ad_reset(ad_cls_t *o);
```
重置解码器。当音频跳转到新的时间点播放时,需要重置解码器。防止可能出现爆音现象。
返回值:
> 调用成功时返回0,否则返回-1。
##### 控制解码器
```c
int ad_control(ad_cls_t *o, int cmd, void *arg, size_t *arg_size);
```
通过控制命令字cmd控制流。该接口尚未实现,预留。
返回值:
> 调用成功时返回0,否则返回-1。
#### 关闭解码器
```c
int ad_close(ad_cls_t *o);
```
关闭解码器,销毁相关资源
返回值:
> 调用成功时返回0,否则返回-1。
## 音频输出接口
#### 音频输出类型注册
##### 注册AV框架中的音频输出类型
AV框架中当前提供了类似alsa的音频输出注册接口:
```c
int ao_register_alsa();
```
返回值:
> 调用成功时返回0,否则返回-1。
##### 注册音频输出子类型
```c
int ao_ops_register(const struct ao_ops *ops);
```
通过该接口,用户可以将自己扩展定义的ops子类型注册到系统中。具体如何扩展,请参考[AV组件适配对接](av_adaptor)说明。
返回值:
> 调用成功时返回0,否则返回-1。
注意事项:
> 当前音频解码器子类型最大支持注册6个,定义在ao.h中的AO_OPS_MAX。用户可根据需要修改
#### 打开音频输出
##### 音频输出参数初始化
```c
int ao_conf_init(ao_conf_t *ao_cnf);
```
初始化音频输出配置参数,用于打开音频输出。
用户通过该接口获取默认的配置参数后,可根据需要修改相关参数。配置参数由结构体ao_conf_t表示,其包括音频输出名称、重采样输出配置、软件音量等,详细定义如下:
```c
typedef struct ao_conf {
char *name; ///< 音频输出名
uint32_t period_ms; ///< 音频输出每消耗多少ms的数据量来一次中断
uint32_t period_num; ///< period_ms的个数. 通过period这两个参数可以计算出ao的缓存大小为(period_num * period_ms * (rate / 1000) * 2 * (16/8))
uint8_t eq_segments; ///< 量化器的段数(默认不支持,用户根据需要扩展)
uint8_t *aef_conf; ///< 音效配置参数(默认为索那音效,需商务合作或扩展)
size_t aef_conf_size; ///< 音效配置大小
uint32_t resample_rate; ///< 非0表示重采样输出到该rate,否则原样输出
uint8_t vol_en; ///< 软件音量使能配置
uint8_t vol_index; ///< 软件音量大小(不同于硬件音量调节),范围为0~255
} ao_conf_t;
```
对于vol_index,当配置过大时,对于某些码流可能播放存在爆音。用户需根据需要确定音频曲线范围。
返回值:
> 调用成功时返回0,否则返回-1。
##### 打开音频输出
```c
ao_cls_t* ao_open(sf_t sf, const ao_conf_t *ao_cnf);
```
根据配置参数ao_cnf和音频采样格式sf打开音频输出,创建相关资源。此时会根据配置参数,创建相应的过滤器,并将多个过滤器链接起来。ao_cls_t结构体中的主要成员如下:
```c
struct ao_cls {
sf_t sf; ///< 最终配置到实际音频输出的sample format
void *priv; ///< 指向音频输出子类
uint8_t start; ///< 音频输出开始标志,用于内部状态控制
uint8_t interrupt;
sf_t ori_sf; ///< 音频输出打开时,传入的sample format
uint32_t period_ms; ///< 同ao_conf_t中定义
uint32_t period_num; ///< 同ao_conf_t中定义
uint32_t resample_rate; ///< 重采样,同ao_conf_t中定义
avfilter_t *avfc; ///< 过滤器链头(多个过滤器会串联起来)
avfilter_t *avf_vol; ///< 软件音量过滤器
uint8_t vol_en; ///< 同ao_conf_t中定义
uint8_t vol_index; ///< 同ao_conf_t中定义
uint8_t *aef_conf; ///< 同ao_conf_t中定义
size_t aef_conf_size; ///< 同ao_conf_t中定义
uint8_t eq_en; ///< eq使能开关
uint8_t eq_segments; ///< 量化器段数,同ao_conf_t中定义
eqfp_t *eq_params; ///< 量化器的配置参数数组
avfilter_t *avf_eq; ///< equalizer过滤器
avframe_t *oframe; ///< 用于过滤器使用
const struct ao_ops *ops; ///< 指向音频输出子类接口
aos_mutex_t lock;
};
```
返回值:
> 调用失败时,返回NULL。
注意事项:
> - 音频最终输出格式为16bits,stereo
> - 音频输出可以打开多个。单需要重采样输出到同一采样率。由于当前尚不支持混音功能,多个ao不能同时写。
#### 音频输出操作
##### 开始音频输出
```c
int ao_start(ao_cls_t *o);
```
启动音频输出。
返回值:
> 调用成功时返回0,否则返回-1。
##### 停止音频输出
```c
int ao_stop(ao_cls_t *o);
```
停止音频输出。
返回值:
> 调用成功时返回0,否则返回-1。
##### 写入数据
```c
int ao_write(ao_cls_t *o, const uint8_t *buf, size_t count);
```
向音频输出中写入指定count字节的pcm数据。该接口是一个阻塞操作。当出错或将buf中的数据完全写到音频输出后才返回。调用该接口时,音频输出内部会根据相关配置参数使用创建的过滤器将音频buf处理后,才会写到音频输出。
返回值:
> 大于等于0表示实际写入pcm字节数,否则返回-1。
注意事项:
> 当调用ao_start后才能调用此接口,否则数据写不成功
##### 播尽ao内部缓冲
```c
int ao_drain(ao_cls_t *o);
```
关闭音频输出前,将已写入到音频输出的全部pcm完全播放完。当通过ao_write接口将pcm数据写完后,若该接口没有调用就直接关闭音频输出,可能导致音频尾部数据得不到播放。
返回值:
> 调用成功时返回0,否则返回-1。
##### 控制音频输出
```c
int ao_control(ao_cls_t *o, int cmd, void *arg, size_t *arg_size);
```
通过控制命令字cmd控制音频输出。
具体的音频输出控制命令字如下表所示:
| 状态 | 对应参数类型 | 描述 |
| :------------------ | :----------- | ------------ |
| AO_CMD_UNKNOWN | —— | 未定义 |
| AO_CMD_EQ_ENABLE | oeq_seten_t* | EQ使能配置 |
| AO_CMD_EQ_SET_PARAM | oeq_setpa_t* | EQ参数配置 |
| AO_CMD_VOL_SET | ovol_set_t* | 软件音量配置 |
其中对于AO_CMD_EQ_ENABLE和AO_CMD_EQ_SET_PARAM作为预留,需要自行扩展实现相应EQ接口。
对于AO_CMD_VOL_SET,只有当音频输出创建时vol_en使能才会生效。
返回值:
> 调用成功时返回0,否则返回-1。
#### 关闭音频输出
```c
int ao_close(ao_cls_t *o);
```
关闭音频输出,销毁相关资源。
返回值:
> 调用成功时返回0,否则返回-1。
## AV框架使用示例
### **注册stream、demux、decoder、ao等**
```c
//核心代码片段
int player_init()
{
static int inited = 0;
if (!inited) {
resample_register(); ///< 注册重采样功能, 选择使用哪种重采样算法
eqx_register(); ///< 注册量化器功能
aefx_register(); ///< 注册音效处理功能
stream_register_mem(); ///< 注册内存流
stream_register_file(); ///< 注册文件流
stream_register_http(); ///< 注册http网络流
stream_register_fifo(); ///< 注册队列流
demux_register_wav(); ///< 注册wav格式解复用
demux_register_mp3(); ///< 注册mp3格式解复用
demux_register_mp4(); ///< 注册mp4格式解复用
demux_register_adts(); ///< 注册adts格式解复用
demux_register_rawaudio(); ///< 注册裸流解复用(透传)
ad_register_pcm(); ///< 注册裸流解码(透传)
ad_register_pvmp3(); ///< 注册pvmp3库用于解码mp3
ad_register_ipc(); ///< 注册核间解码器
ao_register_alsa(); ///< 注册alsa音频输出
inited = 1;
}
return 0;
}
```