【蓝牙专题】bluez代码分析——音频流

我们来分析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插件;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值