我们来分析bluez audio相关的代码;笔者是以bluez4.955来分析的;
bluez/audio目录集中了audio相关的protocol和profile,但是并未给每个audio相关的profile建立子目录,BlueZ 5.x对此上做了改进,在bluez/profiles目录下给每个profile建立了子目录,但是5.x因为在D-Bus接口方面有较大改动,对于A2DP,会影响A2DP Sink和上层Audio server的接口,所以笔者还是以bluez4.9来分析;
前面分析avdtp协议时,我们提到avdtp协议中的src和snk端,分别是音源端和接收端,对于a2dp来说,就是A2DP Sink与A2DP Source,我们下面会从以下几方面来分析;
一:blueZ支持的蓝牙的profile都是用插件的形式提供支持的,我们想看下audio相关模块的加载过程,增加新的profile支持,只需编写一个插件就可以;
二:audio server的注册过程,包括headset server, gateway server, a2dp server, avrcp server, media server等;
三:分析A2DP Sink与A2DP source的实现,即音频传输播放的过程;
一、audio 模块加载
plugin接口的实现在bluez/audio/mian.c;
BLUETOOTH_PLUGIN_DEFINE(audio, VERSION,
BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, audio_init, audio_exit)
这里定义了一个audio插件,对于宏BLUETOOTH_PLUGIN_DEFINE可以看下其定义,第一个参数为plugin名称,参数2为版本号,参数3是优先级,参数4和参数5是插件初始化和退出函数;
下面我们来看函数audio_init和audio_exit;
static int audio_init(void)
{
GKeyFile *config;
gboolean enable_sco;
connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
if (connection == NULL)
return -EIO;
config = load_config_file(CONFIGDIR "/audio.conf");
if (audio_manager_init(connection, config, &enable_sco) < 0)
goto failed;
if (!enable_sco)
return 0;
sco_server = bt_io_listen(BT_IO_SCO, sco_server_cb, NULL, NULL,
NULL, NULL,
BT_IO_OPT_INVALID);
if (!sco_server) {
error("Unable to start SCO server socket");
goto failed;
}
return 0;
failed:
audio_manager_exit();
if (connection) {
dbus_connection_unref(connection);
connection = NULL;
}
return -EIO;
}
这个函数其实非常简单,我们分两步来看,首先看需要了解GLib's GKeyFile Parser,给大家提供个网址 http://www.gtkbook.com/tutorial.php?page=keyfile;GLib's GKeyFile Parser类似于windows下的ini文件操作,我们只需要大概了解下其接口函数就可以;
load_config_file函数就是利用GLib's GKeyFile Parser接口函数,读出配置文件内容到config变量中,我们简单看下其定义bluez/audio/main.c:
static GKeyFile *load_config_file(const char *file)
{
GError *err = NULL;
GKeyFile *keyfile;
keyfile = g_key_file_new();
g_key_file_set_list_separator(keyfile, ',');
if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
error("Parsing %s failed: %s", file, err->message);
g_error_free(err);
g_key_file_free(keyfile);
return NULL;
}
return keyfile;
}
load_config_file函数不懂的看就去看我前面发的网址,我们继续向下看,audio_manager_init函数定义如下bluez/audio/manager.c:
int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
gboolean *enable_sco)
{
char **list;
int i;
gboolean b;
GError *err = NULL;
connection = dbus_connection_ref(conn);
if (!conf)
goto proceed;
config = conf;
list = g_key_file_get_string_list(config, "General", "Enable",
NULL, NULL);
for (i = 0; list && list[i] != NULL; i++) {
if (g_str_equal(list[i], "Headset"))
enabled.headset = TRUE;
else if (g_str_equal(list[i], "Gateway"))
enabled.gateway = TRUE;
else if (g_str_equal(list[i], "Sink"))
enabled.sink = TRUE;
else if (g_str_equal(list[i], "Source"))
enabled.source = TRUE;
else if (g_str_equal(list[i], "Control"))
enabled.control = TRUE;
else if (g_str_equal(list[i], "Socket"))
enabled.socket = TRUE;
else if (g_str_equal(list[i], "Media"))
enabled.media = TRUE;
}
g_strfreev(list);
list = g_key_file_get_string_list(config, "General", "Disable",
NULL, NULL);
for (i = 0; list && list[i] != NULL; i++) {
if (g_str_equal(list[i], "Headset"))
enabled.headset = FALSE;
else if (g_str_equal(list[i], "Gateway"))
enabled.gateway = FALSE;
else if (g_str_equal(list[i], "Sink"))
enabled.sink = FALSE;
else if (g_str_equal(list[i], "Source"))
enabled.source = FALSE;
else if (g_str_equal(list[i], "Control"))
enabled.control = FALSE;
else if (g_str_equal(list[i], "Socket"))
enabled.socket = FALSE;
else if (g_str_equal(list[i], "Media"))
enabled.media = FALSE;
}
g_strfreev(list);
b = g_key_file_get_boolean(config, "General", "AutoConnect", &err);
if (err) {
DBG("audio.conf: %s", err->message);
g_clear_error(&err);
} else
auto_connect = b;
b = g_key_file_get_boolean(config, "Headset", "HFP",
&err);
if (err)
g_clear_error(&err);
else
enabled.hfp = b;
err = NULL;
i = g_key_file_get_integer(config, "Headset", "MaxConnected",
&err);
if (err) {
DBG("audio.conf: %s", err->message);
g_clear_error(&err);
} else
max_connected_headsets = i;
proceed:
if (enabled.socket)
unix_init();
if (enabled.media)
btd_register_adapter_driver(&media_server_driver);
if (enabled.headset)
btd_register_adapter_driver(&headset_server_driver);
if (enabled.gateway)
btd_register_adapter_driver(&gateway_server_driver);
if (enabled.source || enabled.sink)
btd_register_adapter_driver(&a2dp_server_driver);
if (enabled.control)
btd_register_adapter_driver(&avrcp_server_driver);
btd_register_device_driver(&audio_driver);
*enable_sco = (enabled.gateway || enabled.headset);
return 0;
}
audio_manager_init可以分三部分来看;
第一部分是linux dbus函数,即dbus_connection_ref函数,这里是增加一个dbus连接,对于dbus不想做过多说明,感兴趣的可以自己搜下资料;
第二部分是enabled这个结构体,我们来看下定义
static struct enabled_interfaces enabled = {
.hfp = TRUE,
.headset = TRUE,
.gateway = FALSE,
.sink = TRUE,
.source = FALSE,
.control = TRUE,
.socket = TRUE,
.media = FALSE
};
我们使用load_config_file读出了配置文件内容,这里做进一步解析,看哪些服务需要开启;
第三部分,就是使用btd_register_adapter_driver注册需要开启的服务;
二、如何注册服务
下面来看btd_register_adapter_driver函数的实现:
int btd_register_adapter_driver(struct btd_adapter_driver *driver)
{
adapter_drivers = g_slist_append(adapter_drivers, driver);
if (driver->probe == NULL)
return 0;
manager_foreach_adapter(probe_driver, driver);
return 0;
}
void manager_foreach_adapter(adapter_cb func, gpointer user_data)
{
g_slist_foreach(adapters, (GFunc) func, user_data);
}
g_slist_append和g_slist_foreach函数可以参考https://developer.gnome.org/glib/stable/glib-Singly-Linked-Lists.html#g-slist-append;
一个是在列表尾追加项,一个是遍历执行;
下面是probe_driver的定义:
static void probe_driver(struct btd_adapter *adapter, gpointer user_data)
{
struct btd_adapter_driver *driver = user_data;
int err;
if (!adapter->up)
return;
if (driver->probe == NULL)
return;
err = driver->probe(adapter);
if (err < 0) {
error("%s: %s (%d)", driver->name, strerror(-err), -err);
return;
}
adapter->loaded_drivers = g_slist_prepend(adapter->loaded_drivers,
driver);
}
这里看到执行了err = driver->probe(adapter);到这里就清楚了,回到audio_manager_init函数,以a2dp服务为例
if (enabled.source || enabled.sink)
btd_register_adapter_driver(&a2dp_server_driver);
这里实际是执行了a2dp_server_driver->probe,到这里我们先看一下这几个服务的定义:
static struct btd_device_driver audio_driver = {
.name = "audio",
.uuids = BTD_UUIDS(HSP_HS_UUID, HFP_HS_UUID, HSP_AG_UUID, HFP_AG_UUID,
ADVANCED_AUDIO_UUID, A2DP_SOURCE_UUID, A2DP_SINK_UUID,
AVRCP_TARGET_UUID, AVRCP_REMOTE_UUID),
.probe = audio_probe,
.remove = audio_remove,
};
static struct btd_adapter_driver headset_server_driver = {
.name = "audio-headset",
.probe = headset_server_probe,
.remove = headset_server_remove,
};
static struct btd_adapter_driver gateway_server_driver = {
.name = "audio-gateway",
.probe = gateway_server_probe,
.remove = gateway_server_remove,
};
static struct btd_adapter_driver a2dp_server_driver = {
.name = "audio-a2dp",
.probe = a2dp_server_probe,
.remove = a2dp_server_remove,
};
static struct btd_adapter_driver avrcp_server_driver = {
.name = "audio-control",
.probe = avrcp_server_probe,
.remove = avrcp_server_remove,
};
static struct btd_adapter_driver media_server_driver = {
.name = "media",
.probe = media_server_probe,
.remove = media_server_remove,
};
其中btd_adapter_driver 定义如下:
struct btd_adapter_driver {
const char *name;
int (*probe) (struct btd_adapter *adapter);
void (*remove) (struct btd_adapter *adapter);
};
好了,到这里我们已经分析完服务的注册过程,剩下的我们通过a2dp为例,来说明注册服务后是如何工作的;
三、a2dp服务工作流程分析
其入口是从这里开始的:
static struct btd_adapter_driver a2dp_server_driver = {
.name = "audio-a2dp",
.probe = a2dp_server_probe,
.remove = a2dp_server_remove,
};
我们看到有两个函数,分别是a2dp_server_probe和a2dp_server_remove,一个是服务入口,另一个是服务出口,我们先看a2dp_server_probe的实现;
static int a2dp_server_probe(struct btd_adapter *adapter)
{
struct audio_adapter *adp;
const gchar *path = adapter_get_path(adapter);
bdaddr_t src;
int err;
DBG("path %s", path);
adp = audio_adapter_get(adapter);
if (!adp)
return -EINVAL;
adapter_get_address(adapter, &src);
err = a2dp_register(connection, &src, config);
if (err < 0)
audio_adapter_unref(adp);
return err;
}
分析a2dp_server_probe函数之前,我们先看两个结构体:
struct btd_adapter {
uint16_t dev_id;
int up;
char *path; /* adapter object path */
bdaddr_t bdaddr; /* adapter Bluetooth Address */
uint32_t dev_class; /* Class of Device */
char name[MAX_NAME_LENGTH + 1]; /* adapter name */
gboolean allow_name_changes; /* whether the adapter name can be changed */
guint discov_timeout_id; /* discoverable timeout id */
guint stop_discov_id; /* stop inquiry/scanning id */
uint32_t discov_timeout; /* discoverable time(sec) */
guint pairable_timeout_id; /* pairable timeout id */
uint32_t pairable_timeout; /* pairable time(sec) */
uint8_t scan_mode; /* scan mode: SCAN_DISABLED, SCAN_PAGE,
* SCAN_INQUIRY */
uint8_t mode; /* off, connectable, discoverable,
* limited */
uint8_t global_mode; /* last valid global mode */
struct session_req *pending_mode;
int state; /* standard inq, periodic inq, name
* resolving, suspended discovery */
GSList *found_devices;
GSList *oor_devices; /* out of range device list */
struct agent *agent; /* For the new API */
guint auth_idle_id; /* Ongoing authorization */
GSList *connections; /* Connected devices */
GSList *devices; /* Devices structure pointers */
GSList *mode_sessions; /* Request Mode sessions */
GSList *disc_sessions; /* Discovery sessions */
guint scheduler_id; /* Scheduler handle */
sdp_list_t *services; /* Services associated to adapter */
gboolean pairable; /* pairable state */
gboolean initialized;
gboolean off_requested; /* DEVDOWN ioctl was called */
gint ref;
guint off_timer;
GSList *powered_callbacks;
GSList *loaded_drivers;
};
struct audio_adapter {
struct btd_adapter *btd_adapter;
gboolean powered;
uint32_t hsp_ag_record_id;
uint32_t hfp_ag_record_id;
uint32_t hfp_hs_record_id;
GIOChannel *hsp_ag_server;
GIOChannel *hfp_ag_server;
GIOChannel *hfp_hs_server;
gint ref;
};
上面两个结构体,btd_adapter包含了一个蓝牙适配器所需要的所有信息,蓝牙地址,配对设备,连接状态等等;
audio_adapter是audio适配器结构体;
我们回到a2dp_server_probe函数,首先看函数adapter_get_path,它是得到适配器对象的位置,这里是为了方便调试,我们暂且不管;
接下来函数audio_adapter_get,其定义如下:
static struct audio_adapter *audio_adapter_get(struct btd_adapter *adapter)
{
struct audio_adapter *adp;
adp = find_adapter(adapters, adapter);
if (!adp) {
adp = audio_adapter_create(adapter);
if (!adp)
return NULL;
adapters = g_slist_append(adapters, adp);
} else
audio_adapter_ref(adp);
return adp;
}
static struct audio_adapter *find_adapter(GSList *list,
struct btd_adapter *btd_adapter)
{
for (; list; list = list->next) {
struct audio_adapter *adapter = list->data;
if (adapter->btd_adapter == btd_adapter)
return adapter;
}
return NULL;
}
static struct audio_adapter *audio_adapter_create(struct btd_adapter *adapter)
{
struct audio_adapter *adp;
adp = g_new0(struct audio_adapter, 1);
adp->btd_adapter = btd_adapter_ref(adapter);
return audio_adapter_ref(adp);
}
static struct audio_adapter *audio_adapter_ref(struct audio_adapter *adp)
{
adp->ref++;
DBG("%p: ref=%d", adp, adp->ref);
return adp;
}
audio_adapter_get函数比较简单,首先find_adapter遍历一个全局的适配器链表adapters,如果已存在则返回该适配器,如果不存在返回NULL,对于第一次执行的代码肯定是不存在,如果不存在,则使用audio_adapter_create函数创建一个新的适配器,并用g_slist_append函数将新创建的适配器加入到全局适配器链表中;
回到audio_adapter_get函数,之后的adapter_get_address函数比较简单,只是将适配器中的蓝牙地址复制到局部变量中;
下面的a2dp_register函数,是这部分的核心函数,我们详细来看;
int a2dp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
{
int sbc_srcs = 1, sbc_sinks = 1;
int mpeg12_srcs = 0, mpeg12_sinks = 0;
gboolean source = TRUE, sink = FALSE, socket = TRUE;
gboolean delay_reporting = FALSE;
char *str;
GError *err = NULL;
int i;
struct a2dp_server *server;
if (!config)
goto proceed;
str = g_key_file_get_string(config, "General", "Enable", &err);
if (err) {
DBG("audio.conf: %s", err->message);
g_clear_error(&err);
} else {
if (strstr(str, "Sink"))
source = TRUE;
if (strstr(str, "Source"))
sink = TRUE;
g_free(str);
}
str = g_key_file_get_string(config, "General", "Disable", &err);
if (err) {
DBG("audio.conf: %s", err->message);
g_clear_error(&err);
} else {
if (strstr(str, "Sink"))
source = FALSE;
if (strstr(str, "Source"))
sink = FALSE;
if (strstr(str, "Socket"))
socket = FALSE;
g_free(str);
}
/* Don't register any local sep if Socket is disabled */
if (socket == FALSE) {
sbc_srcs = 0;
sbc_sinks = 0;
mpeg12_srcs = 0;
mpeg12_sinks = 0;
goto proceed;
}
str = g_key_file_get_string(config, "A2DP", "SBCSources", &err);
if (err) {
DBG("audio.conf: %s", err->message);
g_clear_error(&err);
} else {
sbc_srcs = atoi(str);
g_free(str);
}
str = g_key_file_get_string(config, "A2DP", "MPEG12Sources", &err);
if (err) {
DBG("audio.conf: %s", err->message);
g_clear_error(&err);
} else {
mpeg12_srcs = atoi(str);
g_free(str);
}
str = g_key_file_get_string(config, "A2DP", "SBCSinks", &err);
if (err) {
DBG("audio.conf: %s", err->message);
g_clear_error(&err);
} else {
sbc_sinks = atoi(str);
g_free(str);
}
str = g_key_file_get_string(config, "A2DP", "MPEG12Sinks", &err);
if (err) {
DBG("audio.conf: %s", err->message);
g_clear_error(&err);
} else {
mpeg12_sinks = atoi(str);
g_free(str);
}
proceed:
if (!connection)
connection = dbus_connection_ref(conn);
server = find_server(servers, src);
if (!server) {
int av_err;
server = g_new0(struct a2dp_server, 1);
if (!server)
return -ENOMEM;
av_err = avdtp_init(src, config, &server->version);
if (av_err < 0) {
g_free(server);
return av_err;
}
bacpy(&server->src, src);
servers = g_slist_append(servers, server);
}
if (config)
delay_reporting = g_key_file_get_boolean(config, "A2DP",
"DelayReporting", NULL);
if (delay_reporting)
server->version = 0x0103;
else
server->version = 0x0102;
server->source_enabled = source;
if (source) {
for (i = 0; i < sbc_srcs; i++)
a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE,
A2DP_CODEC_SBC, delay_reporting, NULL, NULL);
for (i = 0; i < mpeg12_srcs; i++)
a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE,
A2DP_CODEC_MPEG12, delay_reporting,
NULL, NULL);
}
server->sink_enabled = sink;
if (sink) {
for (i = 0; i < sbc_sinks; i++)
a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK,
A2DP_CODEC_SBC, delay_reporting, NULL, NULL);
for (i = 0; i < mpeg12_sinks; i++)
a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK,
A2DP_CODEC_MPEG12, delay_reporting,
NULL, NULL);
}
return 0;
}
a2dp_register函数可以分开来看,labal “process”之前的是解析配置文件,之后的是根据解析的配置文件运行相应的功能;我们只看后面部分,后面部分也拆开来看,先看92-107行,这里是先遍历是否有a2dp_server,如果没有则创建,创建后放到全局服务链表servers 中;
a2dp_server和avdtp_server的定义如下:
struct a2dp_server {
bdaddr_t src;
GSList *sinks;
GSList *sources;
uint32_t source_record_id;
uint32_t sink_record_id;
uint16_t version;
gboolean sink_enabled;
gboolean source_enabled;
};
struct avdtp_server {
bdaddr_t src;
uint16_t version;
GIOChannel *io;
GSList *seps;
GSList *sessions;
};
int avdtp_init(const bdaddr_t *src, GKeyFile *config, uint16_t *version)
{
GError *err = NULL;
gboolean tmp, master = TRUE;
struct avdtp_server *server;
uint16_t ver = 0x0102;
if (!config)
goto proceed;
tmp = g_key_file_get_boolean(config, "General",
"Master", &err);
if (err) {
DBG("audio.conf: %s", err->message);
g_clear_error(&err);
} else
master = tmp;
tmp = g_key_file_get_boolean(config, "General", "AutoConnect",
&err);
if (err)
g_clear_error(&err);
else
auto_connect = tmp;
if (g_key_file_get_boolean(config, "A2DP", "DelayReporting", NULL))
ver = 0x0103;
proceed:
server = g_new0(struct avdtp_server, 1);
if (!server)
return -ENOMEM;
server->version = ver;
if (version)
*version = server->version;
server->io = avdtp_server_socket(src, master);
if (!server->io) {
g_free(server);
return -1;
}
bacpy(&server->src, src);
servers = g_slist_append(servers, server);
return 0;
}
static GIOChannel *avdtp_server_socket(const bdaddr_t *src, gboolean master)
{
GError *err = NULL;
GIOChannel *io;
io = bt_io_listen(BT_IO_L2CAP, NULL, avdtp_confirm_cb,
NULL, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, src,
BT_IO_OPT_PSM, AVDTP_PSM,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_MASTER, master,
BT_IO_OPT_INVALID);
if (!io) {
error("%s", err->message);
g_error_free(err);
}
return io;
}
通过上面的代码可以看到,在创建a2dp server的同时,也对avctp进行了初始化,创建了avctp server,并最终在avdtp_server_socket中调用了bt_io_listen函数,bt_io_listen就是作为服务器端的监听函数,当avctp连接或有其他动作时,会调用avdtp_confirm_cb函数进行处理;
我们在前面分析协议时已经知道,avctp是一个框架协议,具体的通信格式在avdtp或avrcp中定义,而a2dp则是基于上述协议来封装数据流,所以这里我们进入avdtp_confirm_cb函数,来解析其如何为a2dp提供服务,定义如下:
static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
{
struct avdtp *session;
struct audio_device *dev;
char address[18];
bdaddr_t src, dst;
int perr;
GError *err = NULL;
bt_io_get(chan, BT_IO_L2CAP, &err,
BT_IO_OPT_SOURCE_BDADDR, &src,
BT_IO_OPT_DEST_BDADDR, &dst,
BT_IO_OPT_DEST, address,
BT_IO_OPT_INVALID);
if (err) {
error("%s", err->message);
g_error_free(err);
goto drop;
}
DBG("AVDTP: incoming connect from %s", address);
session = avdtp_get_internal(&src, &dst);
if (!session)
goto drop;
/* This state (ie, session is already *connecting*) happens when the
* device initiates a connect (really a config'd L2CAP channel) even
* though there is a connect we initiated in progress. In sink.c &
* source.c, this state is referred to as XCASE connect:connect.
* Abort the device's channel in favor of our own.
*/
if (session->state == AVDTP_SESSION_STATE_CONNECTING) {
DBG("connect already in progress (XCASE connect:connect)");
goto drop;
}
if (session->pending_open && session->pending_open->open_acp) {
if (!bt_io_accept(chan, avdtp_connect_cb, session, NULL, NULL))
goto drop;
return;
}
if (session->io) {
error("Refusing unexpected connect from %s", address);
goto drop;
}
dev = manager_get_device(&src, &dst, FALSE);
if (!dev) {
dev = manager_get_device(&src, &dst, TRUE);
if (!dev) {
error("Unable to get audio device object for %s",
address);
goto drop;
}
btd_device_add_uuid(dev->btd_dev, ADVANCED_AUDIO_UUID);
}
session->io = g_io_channel_ref(chan);
avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) session_cb, session);
perr = audio_device_request_authorization(dev, ADVANCED_AUDIO_UUID,
auth_cb, session);
if (perr < 0) {
avdtp_unref(session);
goto drop;
}
dev->auto_connect = auto_connect;
return;
drop:
g_io_channel_shutdown(chan, TRUE, NULL);
}
函数avdtp_confirm_cb可以从五个方面来分析,分析之前,我们先来看几个结构体:
/* Structure describing an AVDTP connection between two devices */
//这里可以看做avdtp的一个session
struct avdtp {
int ref;
int free_lock;
uint16_t version;
struct avdtp_server *server;
bdaddr_t dst;
avdtp_session_state_t state;
/* True if the session should be automatically disconnected */
gboolean auto_dc;
/* True if the entire device is being disconnected */
gboolean device_disconnect;
GIOChannel *io;
guint io_id;
GSList *seps; /* Elements of type struct avdtp_remote_sep * */
GSList *streams; /* Elements of type struct avdtp_stream * */
GSList *req_queue; /* Elements of type struct pending_req * */
GSList *prio_queue; /* Same as req_queue but is processed before it */
struct avdtp_stream *pending_open;
uint16_t imtu;
uint16_t omtu;
struct in_buf in;
char *buf;
avdtp_discover_cb_t discov_cb;
void *user_data;
struct pending_req *req;
guint dc_timer;
/* Attempt stream setup instead of disconnecting */
gboolean stream_setup;
DBusPendingCall *pending_auth;
};
struct audio_device {
struct btd_device *btd_dev;
DBusConnection *conn;
char *path;
bdaddr_t src;
bdaddr_t dst;
gboolean auto_connect;
struct headset *headset;
struct gateway *gateway;
struct sink *sink;
struct source *source;
struct control *control;
struct target *target;
guint hs_preauth_id;
struct dev_priv *priv;
};
走到函数avdtp_confirm_cb,说明两个设备已经要建立avdtp连接,此时我们接触到的结构体,包括server结构体、adapter结构体,device结构体、session结构体,这四个结构体都对应着一个全局链表保存已经存在信息,看似复杂的代码,其实都跑不出这四个结构体,我们只要抓住这四个结构体,就能一步步分析下去;
回到函数avdtp_confirm_cb,我们分五步来解析这个函数:
1、代码10-21行,这里需要先了解glib 中 IO Channels:
GUI系统都是基于事件驱动的,其中必有一个事件循环过程来获取和处理事件。gtk也一样,gtk的事件循环过程是由glib提供的,而iochannel是glib中把IO事件集成到事件的一种手段;
iochannel可以把开发者指定的发生在 文件描述符、管道和socket之上的事件转换为glib的内部事件,从而可以在程序中用统一的方法来处理IO事件和用户交互
iochannel支持的IO事件有 可读、可写、有紧急(urgent)数据到达、出错、挂断。由于iochannel是在 文件描述符、管道和socket 的基础上构建的,所以它提供的方法既包括这三者的共同点,也考虑到了这三者的不同之处;
这里的bt_io_get函数定义如下,我们看到,它利用va_start和va_end来拆分从GIOChannel得到的数据,放入指定个变量中,可以看到avdtp_confirm_cb调用的bt_io_get函数只有四个为变量,分别为&err,&src,&dst,&address,也就是下面需要用到的几个值;
gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
BtIOOption opt1, ...)
{
va_list args;
gboolean ret;
va_start(args, opt1);
ret = get_valist(io, type, err, opt1, args);
va_end(args);
return ret;
}
2、avdtp_confirm_cb代码23-47行,这里是得到一个session,前面说到了,调用avdtp_confirm_cb说明此时可能有建立连接的请求,关于session结构体我们前面已经看过了,里面包含两个设备建立的avdtp信息,连接状态等;
看下函数avdtp_get_internal 的定义,先找到server,通过server查询session是否已经建立,如果没有建立则new一个,并进行初始化,添加到session全局链表当中(定义在server结构体中);
static struct avdtp *avdtp_get_internal(const bdaddr_t *src, const bdaddr_t *dst)
{
struct avdtp_server *server;
struct avdtp *session;
assert(src != NULL);
assert(dst != NULL);
server = find_server(servers, src);
if (server == NULL)
return NULL;
session = find_session(server->sessions, dst);
if (session) {
if (session->pending_auth)
return NULL;
else
return session;
}
session = g_new0(struct avdtp, 1);
session->server = server;
bacpy(&session->dst, dst);
session->ref = 1;
/* We don't use avdtp_set_state() here since this isn't a state change
* but just setting of the initial state */
session->state = AVDTP_SESSION_STATE_DISCONNECTED;
session->auto_dc = TRUE;
session->version = get_version(session);
server->sessions = g_slist_append(server->sessions, session);
return session;
}
3、avdtp_confirm_cb代码39-61行,这里是否device的处理,因为此时如果建立了连接,需要保存device信息,这里主要看函数manager_get_device就可以,因为流程并不复杂,笔者增加了一些注释,很好理解;
struct audio_device *manager_get_device(const bdaddr_t *src,
const bdaddr_t *dst,
gboolean create)
{
struct audio_device *dev;
struct btd_adapter *adapter;
struct btd_device *device;
char addr[18];
const char *path;
dev = manager_find_device(NULL, src, dst, NULL, FALSE);//遍历设备是否已经存在
if (dev)
return dev;
if (!create)
return NULL;
ba2str(src, addr);
adapter = manager_find_adapter(src);//遍历adapter
if (!adapter) {
error("Unable to get a btd_adapter object for %s",
addr);
return NULL;
}
ba2str(dst, addr);
device = adapter_get_device(connection, adapter, addr);//在这里新建device的时候需要关联adapter
if (!device) {
error("Unable to get btd_device object for %s", addr);
return NULL;
}
path = device_get_path(device);
dev = audio_device_register(connection, device, path, src, dst);//这个函数很关键,具体分析见下面注释
if (!dev)
return NULL;
devices = g_slist_append(devices, dev);//将新增的device添加到devices全局链表中
return dev;
}
struct btd_device *adapter_get_device(DBusConnection *conn,
struct btd_adapter *adapter,
const gchar *address)
{
struct btd_device *device;
DBG("%s", address);
if (!adapter)
return NULL;
device = adapter_find_device(adapter, address);
if (device)
return device;
//创建一个新的device设备,关联了adapter
return adapter_create_device(conn, adapter, address,
DEVICE_TYPE_BREDR);
}
struct audio_device *audio_device_register(DBusConnection *conn,
struct btd_device *device,
const char *path, const bdaddr_t *src,
const bdaddr_t *dst)
{
struct audio_device *dev;
if (!conn || !path)
return NULL;
dev = g_new0(struct audio_device, 1);
dev->btd_dev = btd_device_ref(device);
dev->path = g_strdup(path);
bacpy(&dev->dst, dst);
bacpy(&dev->src, src);
dev->conn = dbus_connection_ref(conn);
dev->priv = g_new0(struct dev_priv, 1);
dev->priv->state = AUDIO_STATE_DISCONNECTED;
if (!g_dbus_register_interface(dev->conn, dev->path,
AUDIO_INTERFACE,
dev_methods, dev_signals, NULL,
dev, NULL)) {
error("Unable to register %s on %s", AUDIO_INTERFACE,
dev->path);
device_free(dev);
return NULL;
}
DBG("Registered interface %s on path %s", AUDIO_INTERFACE,
dev->path);
if (sink_callback_id == 0)
sink_callback_id = sink_add_state_cb(device_sink_cb, NULL);
if (avdtp_callback_id == 0)
avdtp_callback_id = avdtp_add_state_cb(device_avdtp_cb, NULL);
if (avctp_callback_id == 0)
avctp_callback_id = avctp_add_state_cb(device_avctp_cb, NULL);
if (headset_callback_id == 0)
headset_callback_id = headset_add_state_cb(device_headset_cb,
NULL);
return dev;
}
函数audio_device_register这里其实是注册了几个回调函数,这几个函数在执行sink_init后会被调用,这里先放一下,下一篇分析sink和source时再详细去分析这几个函数;
分析到这里,已经把a2dp的注册过程分析完了,具体注册后是怎么工作的,我们后面再看;
4、回到avdtp_confirm_cb函数,我们继续看代码63行,g_io_add_watch函数将把iochannel的指定事件加入到事件循环,g_io_add_watch函数的说明参见glib 中 IO Channels相关;
当有事件发生时,会回调函数session_cb,这里留下一个疑问,iochannel在哪里指定的触发事件,我们后面遇到再进行说明,这里先看下函数session_cb的定义:
static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
gpointer data)
{
struct avdtp *session = data;
struct avdtp_common_header *header;
ssize_t size;
int fd;
DBG("");
if (cond & G_IO_NVAL)
return FALSE;
header = (void *) session->buf;
if (cond & (G_IO_HUP | G_IO_ERR))
goto failed;
fd = g_io_channel_unix_get_fd(chan);
size = read(fd, session->buf, session->imtu);
if (size < 0) {
error("IO Channel read error");
goto failed;
}
if ((size_t) size < sizeof(struct avdtp_common_header)) {
error("Received too small packet (%zu bytes)", size);
goto failed;
}
switch (avdtp_parse_data(session, session->buf, size)) {
case PARSE_ERROR:
goto failed;
case PARSE_FRAGMENT:
return TRUE;
case PARSE_SUCCESS:
break;
}
if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
if (!avdtp_parse_cmd(session, session->in.transaction,
session->in.signal_id,
session->in.buf,
session->in.data_size)) {
error("Unable to handle command. Disconnecting");
goto failed;
}
if (session->ref == 1 && !session->streams && !session->req)
set_disconnect_timer(session);
if (session->streams && session->dc_timer)
remove_disconnect_timer(session);
return TRUE;
}
if (session->req == NULL) {
error("No pending request, ignoring message");
return TRUE;
}
if (header->transaction != session->req->transaction) {
error("Transaction label doesn't match");
return TRUE;
}
if (session->in.signal_id != session->req->signal_id) {
error("Response signal doesn't match");
return TRUE;
}
g_source_remove(session->req->timeout);
session->req->timeout = 0;
switch (header->message_type) {
case AVDTP_MSG_TYPE_ACCEPT:
if (!avdtp_parse_resp(session, session->req->stream,
session->in.transaction,
session->in.signal_id,
session->in.buf,
session->in.data_size)) {
error("Unable to parse accept response");
goto failed;
}
break;
case AVDTP_MSG_TYPE_REJECT:
if (!avdtp_parse_rej(session, session->req->stream,
session->in.transaction,
session->in.signal_id,
session->in.buf,
session->in.data_size)) {
error("Unable to parse reject response");
goto failed;
}
break;
case AVDTP_MSG_TYPE_GEN_REJECT:
error("Received a General Reject message");
break;
default:
error("Unknown message type 0x%02X", header->message_type);
break;
}
pending_req_free(session->req);
session->req = NULL;
process_queue(session);
return TRUE;
failed:
connection_lost(session, EIO);
return FALSE;
}
函数session_cb就是处理avdtp包了,这块我会找一个专题来写;
5、回到avdtp_confirm_cb函数,代码66行,这里发送了一个验证包;
好的,到这里,函数avdtp_confirm_cb就分析完了,我们需要回到函数a2dp_register继续分析;
a2dp_register中,不管是sink还是source端,都需要用到SEP(Stream End Point),前面我们看协议的时候已经讲过了,这里再回顾一下,SRC端发现SNK端的SEP后,通过SEPID获取SNK的服务能力,包括应用服务能力和传输服务能力。
对于A2DP,应用服务能力中包括了CODEC的能力和内容,传输服务由AVDTP提供;SEP相当于soket中的一个端口;
a2dp_register在a2dp_add_sep中创建了SEP,我们来看实现:
struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type,
uint8_t codec, gboolean delay_reporting,
struct media_endpoint *endpoint, int *err)
{
struct a2dp_server *server;
struct a2dp_sep *sep;
GSList **l;
uint32_t *record_id;
sdp_record_t *record;
struct avdtp_sep_ind *ind;
server = find_server(servers, src);
if (server == NULL) {
if (err)
*err = -EINVAL;
return NULL;
}
if (type == AVDTP_SEP_TYPE_SINK && !server->sink_enabled) {
if (err)
*err = -EPROTONOSUPPORT;
return NULL;
}
if (type == AVDTP_SEP_TYPE_SOURCE && !server->source_enabled) {
if (err)
*err = -EPROTONOSUPPORT;
return NULL;
}
sep = g_new0(struct a2dp_sep, 1);
if (endpoint) {
ind = &endpoint_ind;
goto proceed;
}
ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind;
proceed:
sep->lsep = avdtp_register_sep(&server->src, type,
AVDTP_MEDIA_TYPE_AUDIO, codec,
delay_reporting, ind, &cfm, sep);
if (sep->lsep == NULL) {
g_free(sep);
if (err)
*err = -EINVAL;
return NULL;
}
sep->server = server;
sep->endpoint = endpoint;
sep->codec = codec;
sep->type = type;
sep->delay_reporting = delay_reporting;
if (type == AVDTP_SEP_TYPE_SOURCE) {
l = &server->sources;
record_id = &server->source_record_id;
} else {
l = &server->sinks;
record_id = &server->sink_record_id;
}
if (*record_id != 0)
goto add;
record = a2dp_record(type, server->version);
if (!record) {
error("Unable to allocate new service record");
avdtp_unregister_sep(sep->lsep);
g_free(sep);
if (err)
*err = -EINVAL;
return NULL;
}
if (add_record_to_server(&server->src, record) < 0) {
error("Unable to register A2DP service record");\
sdp_record_free(record);
avdtp_unregister_sep(sep->lsep);
g_free(sep);
if (err)
*err = -EINVAL;
return NULL;
}
*record_id = record->handle;
add:
*l = g_slist_append(*l, sep);
if (err)
*err = 0;
return sep;
}
函数a2dp_add_sep其实只做了两件事,一是创建sink或source的SEP,二是将SEP信息放到SDP server上,下面我们分开来看;
1、创建sink或source SEP,代码在12-66行,代码比较多,但非常好理解,我们只需要了解两个结构体和函数add_record_to_server即可,定义如下:
//SEP 结构体也是和server结构体关联
struct a2dp_sep {
struct a2dp_server *server;
struct media_endpoint *endpoint;
uint8_t type;
uint8_t codec;
struct avdtp_local_sep *lsep;
struct avdtp *session;
struct avdtp_stream *stream;
guint suspend_timer;
gboolean delay_reporting;
gboolean locked;
gboolean suspending;
gboolean starting;
};
//这里也是SEP结构体,不过和a2dp_struct不同,这里是avdtp协议所关注的SEP,不同的协议对SEP的关注点不同
struct avdtp_local_sep {
avdtp_state_t state;
struct avdtp_stream *stream;
struct seid_info info;
uint8_t codec;
gboolean delay_reporting;
GSList *caps;
struct avdtp_sep_ind *ind;
struct avdtp_sep_cfm *cfm;
void *user_data;
struct avdtp_server *server;
};
//这个函数声明了一个avdtp_local_sep *sep,将流媒体格式,编解码格式等信息赋值后,关联到全局server链表中;
struct avdtp_local_sep *avdtp_register_sep(const bdaddr_t *src, uint8_t type,
uint8_t media_type,
uint8_t codec_type,
gboolean delay_reporting,
struct avdtp_sep_ind *ind,
struct avdtp_sep_cfm *cfm,
void *user_data)
{
struct avdtp_server *server;
struct avdtp_local_sep *sep;
server = find_server(servers, src);
if (!server)
return NULL;
if (g_slist_length(server->seps) > MAX_SEID)
return NULL;
sep = g_new0(struct avdtp_local_sep, 1);
sep->state = AVDTP_STATE_IDLE;
sep->info.seid = g_slist_length(server->seps) + 1;
sep->info.type = type;
sep->info.media_type = media_type;
sep->codec = codec_type;
sep->ind = ind;
sep->cfm = cfm;
sep->user_data = user_data;
sep->server = server;
sep->delay_reporting = TRUE;
DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
sep->info.type, sep->codec, sep->info.seid);
server->seps = g_slist_append(server->seps, sep);
return sep;
}
2、对于SEP信息放到SDP服务器上,代码为68-86,看懂这块需要对SDP协议有很好的了解,可以查看博主之前的博客;
函数a2dp_record打包SDP 服务器数据,格式可参照之前博客缩写;
函数add_record_to_server将SDP数据挂到服务器上,这样对端就可以访问了;
好了,到这里基本就分析完了audio插件的加载,服务的注册等,下一篇将从sink和source的功能上来继续分析audio插件;