上一篇文章介绍了btif层中A2DP角色管理以及状态机,本文将介绍A2DP音频相关的内容,包括音频流、解码等。
概述
音频流向如下图:
建立AVDTP协议连接之后,当Source端需要播放时会通过AVDTP协议发送通过RTP格式封装的音频数据包,收到数据包之后协议栈中选用连接时约定的编码器以及参数进行解码,解码成PCM数据之后写入到音频模块进行播放。A2DP Profile连接建立过程如下:
- Source端会获取Sink端支持几个解码器(SEP, Stream End Point)。
- Source端获取每个SEP的配置(Capabilites)。
- 根据Source端支持的配置情况选择一个配置设置给Sink端。
此时,已经为后面传输音频流做好了准备,需要播放时即可发送音频流进行播放。
编解码器
管理
在Fluoride协议栈中,解码器配置、解码等的实现在stack/a2dp目录中。A2DP Profile中规定蓝牙播放必须支持SBC编解码器,其他的可以选,现在常见的有编解码器有:SBC、AAC、APTx、LDAC、LHDC等,除SBC和AAC在profile协议中有定义,其他都都是自定义编解码器。不同编解码器由不同的人/厂商实现,因此在A2DP协议上使用以下类型区分不同的编解码器:
// SBC codec
#define A2DP_MEDIA_CT_SBC 0x00
// AAC codec
#define A2DP_MEDIA_CT_AAC 0x02
// 自定义codec
#define A2DP_MEDIA_CT_NON_A2DP 0xFF
所有自定义的codec都是FF,具体细分在自定义的capabilities中用vendor id和codec id区分,capabilities结构中,前面两个字段为vendor id和codec id,如下图:
其中每个厂商的vendor id不能重复,一个厂商多个codec的codec id也不能重复,如LDAC:
static const tA2DP_LDAC_CIE a2dp_ldac_source_caps = {
A2DP_LDAC_VENDOR_ID, // vendorId
A2DP_LDAC_CODEC_ID, // codecId
...
};
在代码逻辑中为了区分不同角色的编解码器,使用以下枚举表示不同的codec(每个codec的index的值应该是不同的):
typedef enum {
BTAV_A2DP_CODEC_INDEX_SOURCE_MIN = 0,
// Add an entry for each source codec here.
// NOTE: The values should be same as those listed in the following file:
// BluetoothCodecConfig.java
BTAV_A2DP_CODEC_INDEX_SOURCE_SBC = 0,
BTAV_A2DP_CODEC_INDEX_SOURCE_AAC,
BTAV_A2DP_CODEC_INDEX_SOURCE_APTX,
BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD,
BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC,
BTAV_A2DP_CODEC_INDEX_SOURCE_MAX,
BTAV_A2DP_CODEC_INDEX_SINK_MIN = BTAV_A2DP_CODEC_INDEX_SOURCE_MAX,
// Add an entry for each sink codec here
BTAV_A2DP_CODEC_INDEX_SINK_SBC = BTAV_A2DP_CODEC_INDEX_SINK_MIN,
BTAV_A2DP_CODEC_INDEX_SINK_AAC,
BTAV_A2DP_CODEC_INDEX_SINK_LDAC,
BTAV_A2DP_CODEC_INDEX_SINK_MAX,
BTAV_A2DP_CODEC_INDEX_MIN = BTAV_A2DP_CODEC_INDEX_SOURCE_MIN,
BTAV_A2DP_CODEC_INDEX_MAX = BTAV_A2DP_CODEC_INDEX_SINK_MAX
} btav_a2dp_codec_index_t;
核心的类之间的关系如下:
A2dpCodecs
是管理编解码器的核心数据结构,管理所有编辑器的配置,初始化A2dpCodecs
时,根据btav_a2dp_codec_index_t
中定义的枚举调用A2dpCodecConfig
中的工厂方法createCodec
创建各个编解码器的配置,然后添加列表中,其中indexed_codecs_
是所有未被禁用的编解码器的配置,ordered_source_codecs_
是所有source使用的编码器的配置,ordered_sink_codecs_
是所有sink使用的解码器的配置。
API接口
不同编解码器的接口都不相同,为了调用方的接口统一,fluoride中定义了编解码器的API,有新的编解码器需要适配时只需要实现这些API即可,API原型定义如下:
// 编码器API
typedef struct {
// 编码器初始化
void (*encoder_init)(const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, A2dpCodecConfig* a2dp_codec_config, a2dp_source_read_callback_t read_callback, a2dp_source_enqueue_callback_t enqueue_callback);
// 清理编码器资源
void (*encoder_cleanup)(void);