# 测试仓库 **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; } ```