AV框架
简介
AV框架是一个轻量级的多媒体开发框架,是AV组件的一部分。其采用典型的4层多媒体模型设计及面向对象的思想开发,使得用户在此基础上易于复用与扩展。若用户需要增加媒体取流格式、扩展媒体解封装格式或增加解码器类型,请参考AV组件适配对接文档。
当前AV组件中提供了wav、mp3、m4a、amrnb、amrwb、flac、adts、opus、speex、adpcm_ms、alaw、mulaw等音频格式的支持。框架本身的设计向后提供视频支持。若有需要,可基于此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组件适配对接文档。
- 主要类型定义详见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)将解码工作放到另一核上执行。
- 核间解码接口使用请参考此处介绍。开发者可略过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类型
static inline int stream_register_all();
流的注册提供了一个总接口,但具体注册了哪些流类型可依赖于解决方案下package.yaml中的CONFIG_STREAMER_XXX等配置项。如solutions/pangu_demo/package.yaml中,以下配置项默认可将内存流、文件流、http网络流、队列流、加密流、hls(http live stream)注册进去。
CONFIG_STREAMER_MEM: 1
CONFIG_STREAMER_FILE: 1
CONFIG_STREAMER_HTTP: 1
CONFIG_STREAMER_FIFO: 1
CONFIG_STREAMER_CRYPTO: 1
CONFIG_STREAMER_HLS: 1
AV框架中当前提供了如下几种流类型的注册接口::
int stream_register_mem(); ///< 内存流
int stream_register_file(); ///< 文件流
int stream_register_http(); ///< http/https网络流
int stream_register_fifo(); ///< 队列流
int stream_register_crypto();///< 加密流
int stream_register_hls(); ///< http live stream(直播/点播均支持)
av组件当前默认将这几种流都注册进去。用户可根据需要分别注册所需流类型。
返回值:
调用成功时返回0,否则返回-1。
注册stream子类型
int stream_ops_register(const struct stream_ops *ops);
通过该接口,用户可以将自己扩展定义的ops子类型注册到系统中。具体如何扩展,请参考AV组件适配对接文档。
返回值:
调用成功时返回0,否则返回-1。
注意事项:
当前stream子类型最大支持注册16个,定义在stream.h中的STREAM_OPS_MAX。用户可根据需要修改
打开流
流配置参数初始化
int stream_conf_init(stm_conf_t *stm_cnf);
初始化流配置参数,用于stream的打开。
用户通过该接口获取stream默认的配置参数后,可根据需要修改相关参数。配置参数由结构体stm_conf_t表示,其包括缓存配置、解密回调、取流超时时间等,详细定义如下:
typedef struct stream_conf {
enum stream_mode mode; ///< 打开流的模式
irq_av_t irq; ///< 取流中断回调。使用者通知stream,防止长时间阻塞
uint8_t need_parse; ///< 1是默认值,表示需要解析URL。为0表示直接前缀获取流。
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; ///< 解密回调接口,用于从上层获取秘钥(若流是加密的)
void *opaque; ///<流事件
stream_event_t stream_event_cb;///<用于取流事件上载
} stm_conf_t;
对于get_dec_cb,用户需实现下面类型的定义。该接口在从码流中解析出特殊key时,可回调到上层获取秘钥。具体可由用户扩展实现自行定义。
typedef int (*get_decrypt_cb_t)(const void *in, size_t ilen, void *out, size_t *olen);
返回值:
调用成功时返回0,否则返回-1。
注意事项:
对于流的模式,当前仅支持STREAM_READ读模式
打开流
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结构体中的主要成员如下:
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 live; ///<直播还是视频点播
uint8_t eof; ///< 流是否结束
uint8_t quit;
uint8_t seekable; ///< 流是否支持seek操作
irq_av_t irq;
get_decrypt_cb_t get_dec_cb; ///< 解密接口,同stm_conf_t中定义
void *opaque;
stream_event_t stream_event_cb;
uint32_t rcv_timeout; ///< 读超时时间,同stm_conf_t中定义
aos_task_t cache_task;
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=%s&channel=%u&rate=%u |
内存流 | mem:// | mem://addr=%u&size=%u&avformat=%s&avcodec=%s&channel=%u&rate=%u |
fifo流 | fifo:// | fifo://tts/1?avformat=%s&avcodec=%s&channel=%u&rate=%u |
加密流 | crypto:// | crypto://http://ip:port/xx.mp3?key=%s&iv=%s |
hls流 | http(s):// | http(s)://ip:port/xx.m3u8 |
- 对于能够探测到媒体信息的码流,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 |
加密流 | crypto://http://www.baidu.com/xx.mp3?key=965582bcbff7da4c3590bf1640c94f06&iv=0123456789abcdef0123456789abcdef |
hls流 | http://www.baidu.com/1.m3u8 |
返回值:
调用失败时,返回NULL。
注意事项:
- 对于加密流,其是通过ffmpeg crypto协议加密的,详情请自行查阅。
- 加密命令示例:ffmpeg -i input.mp3 -c copy -key "855582bceff7de4c3590bf1640c94f05" -iv "0123456789ABCDEF0123456789ABCDEF" crypto:out_enc.mp3
读写、控制流
输入流
int stream_read(stream_cls_t *o, uint8_t *buf, size_t count);
从指定stream中读取count个字节的数据存储到buf中。
返回值:
大于0表示实际读取流字节数,等于0表示读空,失败返回-1。
单变量读取
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字节(大小端)读取流
输出流
int stream_write(stream_cls_t *o, const uint8_t *buf, size_t count);
将指定字节大小count的buf写入到stream中。该接口尚未实现。
返回值:
大于0表示实际写入流字节数,等于0表示写满,失败返回-1
跳转流
int stream_seek(stream_cls_t *o, int32_t offset, int whence);
跳转到流的指定位置处。whence表示偏移基准,有SEEK_SET/SEEK_CUR/SEEK_END这三个取值。通过offset和whence可以计算出跳转的绝对位置。
当要跳转的绝对问题不在当前缓存范围内,对于网络流来说底层会退出缓存线程并重新根据新的位置打开流、创建相关缓存资源。
返回值:
调用成功时返回0,否则返回-1。
跳过流
int stream_skip(stream_cls_t *o, int32_t offset);
从stream当前位置跳过指定偏移量。
当要跳转的绝对问题不在当前缓存范围内,对于网络流来说底层会退出缓存线程并重新根据新的位置打开流、创建相关缓存资源。
返回值:
调用成功时返回0,否则返回-1。
控制流
int stream_control(stream_cls_t *o, int cmd, void *arg, size_t *arg_size);
通过控制命令字cmd控制流。该接口尚未实现,预留。
返回值:
调用成功时返回0,否则返回-1。
获取流状态、信息
获取流位置
int stream_tell(stream_cls_t *o);
获取流当前读写位置
返回值:
调用成功时大于等于0,否则返回-1。
流是否支持跳转
int stream_is_seekable(stream_cls_t *o);
获取流是否支持跳转
返回值:
支持流跳转返回1,否则返回0。
流是否结束
int stream_is_eof(stream_cls_t *o);
获取流是否读结束
返回值:
已结束返回1,否则返回0。
流为直播还是视频点播
int stream_is_live(stream_cls_t *o);
流为直播还是视频点播
返回值:
已结束返回1,否则返回0。
流是否打断
int stream_is_interrupt(stream_cls_t *o);
获取流的获取等是否被上层打断,可用于上层快速退出返回。
返回值:
已打断返回1,否则返回0。
获取流大小
int stream_get_size(stream_cls_t *o);
获取流的大小。
返回值:
调用失败返回-1。
注意事项:
对于直播流或fifo流等,流的大小可能不是固定的或获取不到。
获取流路径
const char* stream_get_url(stream_cls_t *o);
获取当前流的url地址
返回值:
调用失败返回NULL。
关闭流
int stream_close(stream_cls_t *o);
关闭流,销毁相关资源。
返回值:
调用成功时返回0,否则返回-1。
解复用层接口
解复用类型注册
注册AV框架中的解复用类型
static inline int demux_register_all();
解复用器的注册提供了一个总接口,但具体注册了哪些解复用器类型依赖于解决方案下package.yaml中的CONFIG_DEMUXER_XXX等配置项。如solutions/pangu_demo/package.yaml中,以下配置项默认可将wav、mp3、mp4、adts、裸流、flac、asf、amr、ts、ogg注册进去。
CONFIG_DEMUXER_WAV: 1
CONFIG_DEMUXER_MP3: 1
CONFIG_DEMUXER_MP4: 1
CONFIG_DEMUXER_ADTS: 1
CONFIG_DEMUXER_RAWAUDIO: 1
CONFIG_DEMUXER_FLAC: 1
CONFIG_DEMUXER_ASF: 1
CONFIG_DEMUXER_AMR: 1
CONFIG_DEMUXER_TS: 1
CONFIG_DEMUXER_OGG: 1
AV框架中当前提供了如下几种解复用器的注册接口:
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格式解复用
int demux_register_ts(); ///< ts格式解复用
int demux_register_ogg(); ///< ogg格式解复用
播放器当前默认将这几种解复用类型都注册进去。用户可根据需要分别注册所需类型。
返回值:
调用成功时返回0,否则返回-1。
注册解复用子类型
int demux_ops_register(const struct demux_ops *ops);
通过该接口,用户可以将自己扩展定义的ops子类型注册到系统中。具体如何扩展,请参考AV组件适配对接文档。
返回值:
调用成功时返回0,否则返回-1。
注意事项:
当前demux子类型最大支持注册16个,定义在demux.h中的DEMUX_OPS_MAX。用户可根据需要修改
打开解复用
demux_cls_t* demux_open(stream_cls_t *s);
根据流打开解复用器。其会根据url中的avformat参数或者对流的内容探测,找到最匹配的demux_ops。
函数返回后,将会获取到码流的编解码、采样率等信息。用户可根据这些信息创建相应的解码器解码。demux_cls_t结构体中的主要成员如下:
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; ///< 媒体轨道信息
avparser_t *psr;
slist_t list_free;
slist_t list_ready; ///<准备好avpacket_list
void *priv; ///< 指向解复用器子类结构
const struct demux_ops *ops; ///< 解复用器子类接口
};
返回值:
调用失败时,返回NULL。
解出音频包及控制
解出一帧编码数据
int demux_read_packet(demux_cls_t *o, avpacket_t *pkt);
调用找到的demux_ops解复用器,解出一帧编码包。包的数据内容和大小存储在pkt结构中。avpacket_t结构体中的主要成员如下:
typedef struct avpacket {
uint8_t *data;
int32_t size; ///< data的总大小
int32_t len; ///< 编码帧data的有效长度
int64_t pts; ///< 当前帧的present timestamp
slist_t node;
} avpacket_t;
返回值:
大于0表示实际读取编码帧字节数,等于0表示流已经结束,失败返回-1。
解复用器跳转
int demux_seek(demux_cls_t *o, uint64_t timestamp);
解复用器跳转到指定时间点(ms),后续可从该时间点开始读取一帧编码帧数据解码播放。
返回值:
调用成功时返回0,否则返回-1。
解复用器控制
int demux_control(demux_cls_t *o, int cmd, void *arg, size_t *arg_size);
通过控制命令字cmd控制流。该接口尚未实现,预留。
返回值:
调用成功时返回0,否则返回-1。
关闭解复用
int demux_close(demux_cls_t *o);
关闭解复用器,销毁相关资源。
返回值:
调用成功时返回0,否则返回-1。
解码层接口
解码器类型注册
注册AV框架中的解码器类型
static inline int ad_register_all();
解码器的注册提供了一个总接口,但具体注册了哪些解码器类型可依赖于解决方案下package.yaml中的CONFIG_DECODER_XXX等配置项。如solutions/pangu_demo/package.yaml中,以下配置项默认可将pcm裸流、mp3解码器(pvmp3)注册进去。
CONFIG_DECODER_PCM: 1
CONFIG_DECODER_PVMP3: 1
CONFIG_DECODER_FLAC: 0
CONFIG_DECODER_ADPCM_MS: 0
CONFIG_DECODER_AMRNB: 0
CONFIG_DECODER_AMRWB: 0
CONFIG_DECODER_OPUS: 0
CONFIG_DECODER_SPEEX: 0
CONFIG_DECODER_ALAW: 0
CONFIG_DECODER_MULAW: 0
CONFIG_DECODER_IPC: 0
AV框架中当前提供了如下几种解码器的注册接口:
int ad_register_pcm(); ///< 用于裸流解码(透传)
int ad_register_pvmp3(); ///< 用于mp3解码,使用pvmp3解码库
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_opus(); ///< 用于opus解码
int ad_register_speex(); ///< 用于speex解码
int ad_register_alaw(); ///< 用于a律解码
int ad_register_mulaw(); ///< 用于u律解码
int ad_register_ipc(); ///< 用于核间解码(具体的解码运算工作跑在其他核上)
用户若不想通过宏定义形式使用ad_register_all接口,可以分别调用这些接口。
对于核间解码ad_register_ipc,若具体mp3解码算法运行在其他核(此处称为DSP核)上,则本地无需调用ad_register_pvmp3接口。同时在DSP核上,将ad_register_pvmp3注册进去,编译成固件。核间解码具体如何使用,请参考av_cp组件说明。
返回值:
调用成功时返回0,否则返回-1。
注册解码器子类型
int ad_ops_register(const struct ad_ops *ops);
通过该接口,用户可以将自己扩展定义的ops子类型注册到系统中。具体如何扩展,请参考AV组件适配对接说明。
返回值:
调用成功时返回0,否则返回-1。
注意事项:
当前音频解码器子类型最大支持注册16个,定义在ad.h中的AD_OPS_MAX。用户可根据需要修改
打开解码器
解码器参数初始化
int ad_conf_init(ad_conf_t *ad_cnf);
初始化解码器配置参数,用于打开解码器。
用户通过该接口获取默认的配置参数后,可根据需要修改相关参数。配置参数由结构体ad_conf_t表示,其包括采样格式、额外解码配置参数等,详细定义如下:
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封装格式中拆出传入
打开解码器
ad_cls_t* ad_open(avcodec_id_t id, const ad_conf_t *ad_cnf);
根据解码配置参数ad_cnf打开指定codec id的解码器。ad_cls_t结构体中的主要成员如下:
struct ad_cls {
sh_audio_t ash; ///< 音频采样等信息
aos_mutex_t lock;
void *priv; ///< 解码器子类结构
const struct ad_ops *ops; ///< 解码器子类接口
};
返回值:
调用失败时,返回NULL。
注意事项:
解码器打开后,获得的音频采样格式(sf)可能与解复用出来的有差异(与相应解码库的配置或实现有关系)。后续打开音频输出时,以解码器的sf为准(从sh_audio_t结构中获取)
音频解码及控制
解码音频帧
int ad_decode(ad_cls_t *o, avframe_t *frame, int *got_frame, const avpacket_t *pkt);
将一帧音频包pkt解码,解码数据存储在frame结构中。got_frame表明本次是否可以解出数据(可能需要向解码器传入多个音频包才能解出来一帧)。avframe_t结构体中的主要成员如下:
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表示解码一帧消耗的原始编码帧字节数。
重置解码器
int ad_reset(ad_cls_t *o);
重置解码器。当音频跳转到新的时间点播放时,需要重置解码器。防止可能出现爆音现象。
返回值:
调用成功时返回0,否则返回-1。
控制解码器
int ad_control(ad_cls_t *o, int cmd, void *arg, size_t *arg_size);
通过控制命令字cmd控制流。该接口尚未实现,预留。
返回值:
调用成功时返回0,否则返回-1。
关闭解码器
int ad_close(ad_cls_t *o);
关闭解码器,销毁相关资源
返回值:
调用成功时返回0,否则返回-1。
音频输出接口
音频输出类型注册
注册AV框架中的音频输出类型
static inline int ao_register_all()
音频输出的注册提供了一个总接口,但具体注册了哪些音频输出类型依赖于package.yaml中的CONFIG_AV_AO_XXX等配置项。以下配置项可将alsa音频输出注册进去。
CONFIG_AV_AO_ALSA: 1
AV框架中当前默认仅提供了类似alsa的音频输出注册接口:
int ao_register_alsa();
返回值:
用成功时返回0,否则返回-1。
注册音频输出子类型
int ao_ops_register(const struct ao_ops *ops);
通过该接口,用户可以将自己扩展定义的ops子类型注册到系统中。具体如何扩展,请参考AV组件适配对接说明。
返回值:
调用成功时返回0,否则返回-1。
注意事项:
当前音频解码器子类型最大支持注册6个,定义在ao.h中的AO_OPS_MAX。用户可根据需要修改
打开音频输出
音频输出参数初始化
int ao_conf_init(ao_conf_t *ao_cnf);
初始化音频输出配置参数,用于打开音频输出。
用户通过该接口获取默认的配置参数后,可根据需要修改相关参数。配置参数由结构体ao_conf_t表示,其包括音频输出名称、重采样输出配置、软件音量等,详细定义如下:
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
uint8_t atempo_play_en;///< 播放启用
float speed; ///< 播放速度,建议0.5 ~ 2.0;
int32_t db_min; ///< 最小db
int32_t db_max; ///<最大db
} ao_conf_t;
对于vol_index,当配置过大时,对于某些码流可能播放存在爆音。用户需根据需要确定音频曲线范围。
返回值:
调用成功时返回0,否则返回-1。
打开音频输出
ao_cls_t* ao_open(sf_t sf, const ao_conf_t *ao_cnf);
根据配置参数ao_cnf和音频采样格式sf打开音频输出,创建相关资源。此时会根据配置参数,创建相应的过滤器,并将多个过滤器链接起来。ao_cls_t结构体中的主要成员如下:
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; ///< 软件音量过滤器
avfilter_t *avf_atempo; ///< 用于播放速度配置
avfilter_t *avf_aef; ///< 用于sona aef配置
uint8_t vol_en; ///< 同ao_conf_t中定义
uint8_t vol_index; ///< 同ao_conf_t中定义
uint8_t atempo_play_en;///< 播放启用
float speed; ///< 播放速度。建议:0.5~2.0
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不能同时写。
音频输出操作
开始音频输出
int ao_start(ao_cls_t *o);
启动音频输出。
返回值:
调用成功时返回0,否则返回-1。
停止音频输出
int ao_stop(ao_cls_t *o);
停止音频输出。
返回值:
调用成功时返回0,否则返回-1。
写入数据
int ao_write(ao_cls_t *o, const uint8_t *buf, size_t count);
向音频输出中写入指定count字节的pcm数据。该接口是一个阻塞操作。当出错或将buf中的数据完全写到音频输出后才返回。调用该接口时,音频输出内部会根据相关配置参数使用创建的过滤器将音频buf处理后,才会写到音频输出。
返回值:
大于等于0表示实际写入pcm字节数,否则返回-1。
注意事项:
当调用ao_start后才能调用此接口,否则数据写不成功
播尽ao内部缓冲
int ao_drain(ao_cls_t *o);
关闭音频输出前,将已写入到音频输出的全部pcm完全播放完。当通过ao_write接口将pcm数据写完后,若该接口没有调用就直接关闭音频输出,可能导致音频尾部数据得不到播放。
返回值:
调用成功时返回0,否则返回-1。
控制音频输出
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。
关闭音频输出
int ao_close(ao_cls_t *o);
关闭音频输出,销毁相关资源。
返回值:
调用成功时返回0,否则返回-1。
功能配置与裁剪
鉴于某些产品硬件资源如ram、flash等比较受限,而av组件又提供了很多的功能。所以不太可能将av组件提供的所有功能都能够包含进去。此时就需要开发者根据具体产品需要开启或配置相关功能或资源大小使用。
av组件中提供了如流、解复用器、解码器、音频输出器、播放任务栈/网络缓冲任务栈默认大小、倍速播放、重采样器、单双声道输出等配置等,方便开发者根据需要来裁剪。
av组件package.yaml中的def_config字段下提供了默认组件默认打开的功能和配置。开发者在solution或板级组件中重新配置即可。
若开发者想修改默认配置的话,可在具体solution/板级组件中重新配置这些宏。在编译时,其会覆盖av组件中的默认配置。
Linux开发环境,可通过打开/编辑对应的package.yaml中的def_config选项来查看/修改相关宏配置。Windows下CDK集成开发环境,可右键查看/编辑对应组件的define。
宏配置说明
av组件默认提供了多种配置。其中CONFIG_XXX_IPC用于核间处理,仅适用于多核处理器,其表示具体的操作实际在协处理器上工作。本地仅是该功能的核间适配器,需要搭配相应的协处理器固件使用,具体可参考av_cp组件说明。下述所介绍的配置是以yaml格式介绍的。
av组件中的配置项大概分为如下几类:
媒体接入层配置
CONFIG_STREAMER_CRYPTO: 0 #加密流
CONFIG_STREAMER_FIFO: 0 #队列流
CONFIG_STREAMER_FILE: 0 #文件流
CONFIG_STREAMER_HLS: 0 #http live stream
CONFIG_STREAMER_HTTP: 1 #http网络流
CONFIG_STREAMER_MEM: 1 #内存流
CONFIG_WEB_CACHE_TASK_STACK_SIZE: 2048 #网络流缓冲任务栈大小
CONFIG_AV_STREAM_CACHE_SIZE_DEFAULT: 8192#网络流缓冲大小
CONFIG_AV_STREAM_INNER_BUF_SIZE: 256 #stream内部buf大小,用于性能优化
CONFIG_STREAMER_XXX表示需要支持的媒体接入类型,在媒体接入层接口小节已介绍过。
CONFIG_WEB_CACHE_TASK_STACK_SIZE表示在获取http、hls等网络音频流时默认的任务栈大小。对于不同的取流类型,该值可根据需要来调整。
CONFIG_AV_STREAM_CACHE_SIZE_DEFAULT表示获取网络音频流时的默认环形buffer缓冲大小,默认80KB。该值可控制网络播放时的抖动,优化网络播放的效果。开发者可使用ffprobe等工具探测具体音频流的码率,结合产品的硬件配置,应用场景等计算出合适的网络缓冲大小。同时网络缓冲大小也可以在创建播放器时动态配置,具体请参考播放器中的说明。
CONFIG_AV_STREAM_INNER_BUF_SIZE用于所有流内部小buf的大小配置,不管流是来自网络或是本地文件。合适的大小配置可提升性能,默认2KB。
解复用器配置
CONFIG_DEMUXER_ADTS: 0 #adts解复用
CONFIG_DEMUXER_AMR: 0 #amr解复用
CONFIG_DEMUXER_ASF: 0 #asf解复用
CONFIG_DEMUXER_FLAC: 0 #flac解复用
CONFIG_DEMUXER_MP3: 1 #mp3解复用
CONFIG_DEMUXER_MP4: 0 #mp4解复用
CONFIG_DEMUXER_OGG: 0 #ogg解复用
CONFIG_DEMUXER_RAWAUDIO: 0 #rawaudio解复用
CONFIG_DEMUXER_TS: 0 #ts解复用
CONFIG_DEMUXER_WAV: 0 #wav解复用
CONFIG_AV_PROBE_SIZE_MAX: 1024 #音频格式探测最大长度
CONFIG_AV_SAMPLE_NUM_PER_FRAME_MAX: 80 #控制wav音频帧的最大采样数
CONFIG_AV_MP4_IDX_OPT: 1 #m4a解复用内存优化
CONFIG_DEMUXER_xxx表示需要支持的解复用器类型,在解复用层接口小节已介绍过。
CONFIG_AV_PROBE_SIZE_MAX用于在解复用阶段探测音频格式具体是哪种类型。该配置项可提高音频格式探测的准确性,默认2KB。在网络播放时,建议该值小于或等于CONFIG_AV_STREAM_INNER_BUF_SIZE,可加快首播时间。否则可能会引起seek,导致首播时间慢。
CONFIG_AV_SAMPLE_NUM_PER_FRAME_MAX表示控制rawaudio或某些wav音频帧的最大采样数。可用于降低小内存芯片的最大内存占用,如ch2601、w800等。
CONFIG_AV_MP4_IDX_OPT表示m4a解复用内存优化是否开启。由于m4a文件播放时,需要先获取所有索引信息才能播放,内存开销较大。该优化开启后,可以在播放时分段建立索引,降低最大内存开销。但开启后,对于网络播放,可能会引起卡顿。
解码器配置
CONFIG_DECODER_ADPCM_MS: 0 #adpcm_ms解码
CONFIG_DECODER_ALAW: 0 #alaw解码
CONFIG_DECODER_AMRNB: 0 #amrnb解码
CONFIG_DECODER_AMRWB: 0 #amrwb解码
CONFIG_DECODER_FLAC: 0 #flac解码
CONFIG_DECODER_IPC: 0 #核间解码
CONFIG_DECODER_MULAW: 0 #ulaw解码
CONFIG_DECODER_OPUS: 0 #opus解码
CONFIG_DECODER_PCM: 1 #pcm裸流解码
CONFIG_DECODER_PVMP3: 1 #mp3解码
CONFIG_DECODER_SPEEX: 0 #speex解码
CONFIG_DECODER_xxx表示需要支持的解码器类型,在解码层接口小节已介绍过。
音频输出配置
CONFIG_AV_AO_ALSA: 1 #音频输出采用alsa
CONFIG_AV_AO_CHANNEL_NUM: 1 #音频输出通道,仅单声道或双声道输出
CONFIG_AO_MIXER_SUPPORT: 0 #混音播放
CONFIG_AV_AO_xxx表示需要支持的音频输出类型,在音频输出接口小节已介绍过。
CONFIG_AV_AO_CHANNEL_NUM表示音频输出通道数,其决定音频流最终送到codec输出采用单声道还是双声道。对于某些低成本芯片,可仅采用单声道输出,降低最大内存开销。
CONFIG_AO_MIXER_SUPPORT表示是否支持混音播放输出,及允许创建两个播放器同时输出。若关闭,播放器需要将之前的播放stop后才能正常播放,否则可能会引起异常。若开启,其不会有关闭情况下的功能限制,但会有较多的内存开销。
解析器配置
CONFIG_AVPARSER_MP3: 1 #mp3解析器
CONFIG_AVPARSER_ADTS: 1 #adts解析器
某些格式,如ts等码流格式解复用后,可能会出现多个音频编码帧粘连的情况。CONFIG_AVPARSER_MP3和CONFIG_AVPARSER_ADTS可用于将mp3和adts粘连帧解析成一个个单独的音频帧。同时CONFIG_AVPARSER_ADTS可将m4a等容器格式解复用出来的带有Audio Special codec参数的aac转换为adts帧(当前av中的aac解码器仅支持adts帧解码)。
建议在开启mp3或aac解码时,同时配置对应的解析器。
播放控制配置等
CONFIG_AEFXER_IPC: 0 #核间音效处理
CONFIG_AEFXER_SONA: 0 #索那音效处理
CONFIG_ATEMPOER_IPC: 0 #核间变速播放
CONFIG_ATEMPOER_SONIC: 1 #变速播放
CONFIG_EQXER_IPC: 0 #量化器
CONFIG_EQXER_SILAN: 0 #量化器
CONFIG_FFTXER_IPC: 0 #fft变换
CONFIG_FFTXER_SPEEX: 0 #fft变换
CONFIG_RESAMPLER_IPC: 0 #核间音频重采样
CONFIG_RESAMPLER_SPEEX: 0 #speex重采样
CONFIG_PLAYER_TASK_STACK_SIZE: 2048 #播放器任务栈大小
CONFIG_AEFXER_xxx用于音效功能的处理,在某些芯片上功能可能不存在,开发者可根据现有接口定义扩展实现。
CONFIG_ATEMPOER_xxx用于倍速播放,采用sonic开源倍速处理算法。
CONFIG_PLAYER_TASK_STACK_SIZE用于控制播放任务栈大小。当前播放任务涵盖解复用、解码、解码后处理、音频输出等功能。这些功能随着解码器等配置的不同所要求的栈空间大小不一样。
配置依赖
要完成一个特定功能时,可能要求某些配置项要同时开启。如本地内存中一个mp4容器格式文件,其内部的音频编码格式可能是mp3的,最终通过alsa接口输出。则必要的配置项如下:
CONFIG_STREAMER_MEM: 1
CONFIG_DEMUXER_MP4: 1
CONFIG_DECODER_PVMP3: 1
CONFIG_OUTPUTER_ALSA: 1
外部影响配置
CONFIG_SAL
CONFIG_TCPIP
CONFIG_USING_TLS
av组件中的功能同时会受到外部其他配置项的影响。
CONFIG_SAL表示网络协议栈使用了at网卡模组等,其网络协议栈跑在模组上。CONFIG_TCPIP表示本地使用lwip网络协议栈。若av组件支持http等网络流的播放,CONFIG_SAL和CONFIG_TCPIP需要配置一个,否则不支持网络播放。当前CONFIG_SAL和CONFIG_TCPIP无法共存。
CONFIG_USING_TLS用于https网络流、加密流等播放,其依赖mbedtls组件。
解复用器和解码器
关于容器格式和编码格式的详细关系请自行查阅,这里不再赘述。此处仅介绍av组件中解复用器支持哪几种音频编码格式,如下表所示:
解复用器 | 解码器 |
---|---|
CONFIG_DEMUXER_AMR | CONFIG_DECODER_AMRNB、CONFIG_DECODER_AMRWB |
CONFIG_DEMUXER_FLAC | CONFIG_DECODER_FLAC |
CONFIG_DEMUXER_MP3 | CONFIG_DECODER_PVMP3 |
CONFIG_DEMUXER_MP4 | CONFIG_DECODER_PVMP3 |
CONFIG_DEMUXER_OGG | CONFIG_DECODER_OPUS、CONFIG_DECODER_SPEEX |
CONFIG_DEMUXER_RAWAUDIO | CONFIG_DECODER_PCM |
CONFIG_DEMUXER_TS | CONFIG_DECODER_PVMP3 |
CONFIG_DEMUXER_WAV | CONFIG_DECODER_ADPCM_MS、CONFIG_DECODER_PCM、CONFIG_DECODER_ALAW、CONFIG_DECODER_MULAW |
典型应用场景配置
本地mp3文件音频播放
必须打开的宏配置如下:
CONFIG_STREAMER_FILE: 1
CONFIG_DEMUXER_MP3: 1
CONFIG_DECODER_PVMP3: 1
CONFIG_OUTPUTER_ALSA: 1
播放控制等配置项根据需要开启。如需要支持重采样功能,则需要配置CONFIG_RESAMPLER_SPEEX或CONFIG_RESAMPLER_IPC,下述类同。
内存中某wav音频文件adpcm_ms编码格式播放
必须打开的宏配置如下:
CONFIG_STREAMER_MEM: 1
CONFIG_DEMUXER_WAV: 1
CONFIG_DECODER_ADPCM_MS: 1
CONFIG_OUTPUTER_ALSA: 1
网络(https)mp3音频播放
必须打开的宏配置如下:
CONFIG_STREAMER_HTTP: 1
CONFIG_DEMUXER_MP3: 1
CONFIG_DECODER_PVMP3: 1
CONFIG_OUTPUTER_ALSA: 1
同时CONFIG_USING_TLS需要配置。
CONFIG_SAL和CONFIG_TCPIP根据产品网络类型配置一个。
hls直播流播放,采用核间mp3解码
必须打开的宏配置如下:
CONFIG_STREAMER_HTTP: 1
CONFIG_STREAMER_HLS: 1 #hls内部会使用CONFIG_STREAMER_HTTP
CONFIG_DEMUXER_TS: 1
CONFIG_DECODER_IPC: 1
CONFIG_OUTPUTER_ALSA: 1
CONFIG_AVPARSER_MP3: 1
hls(http live stream)实际音视频数据是采用ts容器格式的,所以需要配置CONFIG_DEMUXER_TS。同时ts容器格式中解复用出来的mp3帧可能是黏连帧,所以需要配置CONFIG_AVPARSER_MP3。
核间解码固件编译时,请配置CONFIG_DECODER_PVMP3。具体请参考av_cp组件。
若hls采用ssl加密传输,CONFIG_USING_TLS也需要配置。
CONFIG_SAL和CONFIG_TCPIP根据产品网络类型配置一个。
AV框架使用
注册stream、demux、decoder、ao等
//核心代码片段
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;
}