【蓝牙专题】bluez代码分析——音频流二(a2dp的source与sink代码分析)

之前我们说过,BlueZ从4.46的版本之后实现了对A2DP Sink的支持,而之前的版本只支持A2DP Source,在BlueZ4.9版本中,A2DP Sink实现的源文件是source.c(注意:source.c实现了A2DP Sink而不是sink.c)。a2dp.c也做了一些支持A2DP sink的相应改进,比如增加了对AVDTP_SEP_TYPE_SOURCE的判断,然后调用相应的source或sink函数。结合文档和protocol analyzer tool可以比较容易追踪音频数据流的处理过程。

上图是我们上一篇分析的思路,我们分析了a2dp服务是如何注册了,经过上一篇文档,我们理清了一个audio插件,是如何创建server、device、adapter等;上一篇我们分析到了audio_manager_init--->btd_register_device_driver(&audio_driver),下面我们接着通过这个函数分析audio数据流是怎样发送和接收的,也就是a2dp的source与sink实现;其中audio_driver定义如下,我们仍假设config文件中只打开了source和sink配置,所以这里只需要看a2dp_server_driver函数,函数a2dp_server_driver涉及两个函数,audio_probe和audio_remove,下面我们分别来看;

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 int audio_probe(struct btd_device *device, GSList *uuids)
{
	struct btd_adapter *adapter = device_get_adapter(device);//通过device得到adapter
	bdaddr_t src, dst;
	struct audio_device *audio_dev;

	adapter_get_address(adapter, &src);//通过adapter得到src地址
	device_get_address(device, &dst);//通过adapter得到dst地址

	audio_dev = manager_get_device(&src, &dst, TRUE);
	if (!audio_dev) {
		DBG("unable to get a device object");
		return -1;
	}

	g_slist_foreach(uuids, (GFunc) handle_uuid, audio_dev);

	return 0;
}

audio_probe可以分两部分来看,第一部分是manager_get_device函数,第二部分是g_slist_foreach函数,g_slist_foreach本身没有值得深入研究的,其执行函数handle_uuid函数需要我们分析下,下面先看manager_get_device函数的实现:

</pre><p></p><p><span style="white-space:pre"></span><pre name="code" class="objc">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);//通过src和dst地址在列表中查找是否已经创建了device
	if (dev)
		return dev;

	if (!create)//因为create在调用时使用了ture,所以当没有遍历到device后会重新创建一个
		return NULL;

	ba2str(src, addr);

	adapter = manager_find_adapter(src);//首先获取前面创建的adapter,没有会在manager_find_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);//使用adapter来创建device
	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);//这个函数中更新了device和audio_device,我们需要看下里面的回调函数

	if (!dev)return NULL;devices = g_slist_append(devices, dev);//将device放到devices全局列表当中

	return dev;
}

 

分析函数前,我们先看结构体audio_device的定义(前一篇已经看过),它包含了作为一个audio设备应该具备的属性

struct audio_device {
	struct btd_device *btd_dev;//关联device结构体

	DBusConnection *conn;//dbus结构体,后面有专题讨论linux下的Dbus总线,一种进程间通信方式
	char *path;
	bdaddr_t src;//源地址
	bdaddr_t dst;//目标地址

	gboolean auto_connect;//是否自动连接,config文件中可以设置

	struct headset *headset;//config开启headset模式时才有意义
	struct gateway *gateway;//config开始作为网关时才使用
	struct sink *sink;//config中配置了sink时有意义
	struct source *source;//config中配置了source时有意义
	struct control *control;
	struct target *target;

	guint hs_preauth_id;

	struct dev_priv *priv;
};

函数manager_get_device就是打包了一个audio_device返回,因为我们在上一篇分析audio插件注册流程时已经创建了各种server、device、adapter,并放到了全局列表中,函数manager_get_device就是把之前的信息拿出来,如果没有创建的话需要创建,具体见上面的代码注释,这里需要说明的是,函数audio_device_register做的工作是针对device的,如果我们没有新建device,而是遍历到了之前创建的device,这几个回调函数在之前创建device时,就已经被声明过了,打包完device后,会关联到audio_device结构体,并将audio_device返回;

在manager_get_device函数中,我们看下新创建的audio_device涉及的回调函数:

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时已经见过了这几个回调函数,但当时没有分析,这里我们需要展开看一下,因为我们假设config文件中没有配置headset模式,所以这里只需要看sink_callback_id、avdtp_callback_id和avctp_callback_id,其实就是封装了三个回调函数,我们一个个来看:

1、device_sink_cb

BlueZ/audio/Device.c

static void device_sink_cb(struct audio_device *dev,
				sink_state_t old_state,
				sink_state_t new_state,
				void *user_data)
{
	struct dev_priv *priv = dev->priv;

	if (!dev->sink)
		return;

	priv->sink_state = new_state;

	switch (new_state) {
	case SINK_STATE_DISCONNECTED:
		if (dev->control) {
			device_remove_control_timer(dev);
			avrcp_disconnect(dev);
		}
		if (priv->hs_state != HEADSET_STATE_DISCONNECTED &&
				(priv->dc_req || priv->disconnecting)) {
			headset_shutdown(dev);
			break;
		}
		if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
			device_set_state(dev, AUDIO_STATE_DISCONNECTED);
		else if (old_state == SINK_STATE_CONNECTING) {
			switch (priv->hs_state) {
			case HEADSET_STATE_CONNECTED:
			case HEADSET_STATE_PLAY_IN_PROGRESS:
			case HEADSET_STATE_PLAYING:
				device_set_state(dev, AUDIO_STATE_CONNECTED);
			default:
				break;
			}
		}
		break;
	case SINK_STATE_CONNECTING:
		device_remove_avdtp_timer(dev);
		if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
			device_set_state(dev, AUDIO_STATE_CONNECTING);
		break;
	case SINK_STATE_CONNECTED:
		if (old_state == SINK_STATE_PLAYING)
			break;
		if (dev->auto_connect) {
			if (!dev->headset)
				device_set_state(dev, AUDIO_STATE_CONNECTED);
			else if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
				device_set_headset_timer(dev);
			else if (priv->hs_state == HEADSET_STATE_CONNECTED ||
					priv->hs_state == HEADSET_STATE_PLAY_IN_PROGRESS ||
					priv->hs_state == HEADSET_STATE_PLAYING)
				device_set_state(dev, AUDIO_STATE_CONNECTED);
		} else if (priv->hs_state == HEADSET_STATE_DISCONNECTED ||
				priv->hs_state == HEADSET_STATE_CONNECTING)
			device_set_state(dev, AUDIO_STATE_CONNECTED);
		break;
	case SINK_STATE_PLAYING:
		break;
	}
}


函数device_sink_cb比较简单,楼主只关心SINK_STATE_CONNECTED和SINK_STATE_DISCONNECTED到底会做什么操作,除了在SINK_STATE_DISCONNECTED时会删除一个定时器外,剩下的操作就是函数device_set_state,我们来看其定义

static void device_set_state(struct audio_device *dev, audio_state_t new_state)
{
	struct dev_priv *priv = dev->priv;
	const char *state_str;
	DBusMessage *reply = NULL;

	state_str = state2str(new_state);
	if (!state_str)
		return;

	if (new_state == AUDIO_STATE_DISCONNECTED) {
		priv->authorized = FALSE;

		if (priv->dc_id) {
			device_remove_disconnect_watch(dev->btd_dev,
							priv->dc_id);
			priv->dc_id = 0;
		}
	} else if (new_state == AUDIO_STATE_CONNECTING)
		priv->dc_id = device_add_disconnect_watch(dev->btd_dev,
						disconnect_cb, dev, NULL);

	if (dev->priv->state == new_state) {
		DBG("state change attempted from %s to %s",
							state_str, state_str);
		return;
	}

	dev->priv->state = new_state;

	if (new_state == AUDIO_STATE_DISCONNECTED) {
		if (priv->dc_req) {
			reply = dbus_message_new_method_return(priv->dc_req);
			dbus_message_unref(priv->dc_req);
			priv->dc_req = NULL;
			g_dbus_send_message(dev->conn, reply);
		}
		priv->disconnecting = FALSE;
	}

	if (priv->conn_req && new_state != AUDIO_STATE_CONNECTING) {
		if (new_state == AUDIO_STATE_CONNECTED)
			reply = dbus_message_new_method_return(priv->conn_req);
		else
			reply = btd_error_failed(priv->conn_req,
							"Connect Failed");

		dbus_message_unref(priv->conn_req);
		priv->conn_req = NULL;
		g_dbus_send_message(dev->conn, reply);
	}

	emit_property_changed(dev->conn, dev->path,
				AUDIO_INTERFACE, "State",
				DBUS_TYPE_STRING, &state_str);
}

分析函数device_set_state我们分两条线,一是new_state=SINK_STATE_CONNECTED,二是new_state=SINK_STATE_DISCONNECTED;

当new_state=SINK_STATE_CONNECTED时,我们可以直接跳到函数device_set_state的41行,会执行dbus_message_new_method_return(priv->conn_req);其中priv->conn_req是创建device时通过函数g_dbus_register_interface生成的,这里dbus_message_new_method_return是Dbus方法调用返回结果的方式,也就是在其他地方通过Dbus的方法调用了回调函数device_sink_cb,在这里需要有一个结果返回,其中dbus_message_new_method_return函数打包返回参数,g_dbus_send_message中的dbus_connection_send函数将Dbus消息发送出去,那么是在哪里调用的回调?这里先放一下,我们分析完回调函数后再来分析;

当new_state=SINK_STATE_DISCONNECTED时,会执行函数avrcp_disconnect和device_set_state,对于avrcp的操作我们这里暂且不分析,后面会有专门章节;而device_set_state对new_state=SINK_STATE_DISCONNECTED的处理,处理也发送了一个DBUS方法调用返回,还执行了avrcp_disconnect函数,断开avrcp;

2、avdtp_add_state_cb函数,avdtp是音视频分发传输协议(avctp是音视频控制传输协议,是一个框架协议)

static void device_avdtp_cb(struct audio_device *dev, struct avdtp *session,
				avdtp_session_state_t old_state,
				avdtp_session_state_t new_state,
				void *user_data)
{
	if (!dev->sink || !dev->control)
		return;

	if (new_state == AVDTP_SESSION_STATE_CONNECTED) {
		if (avdtp_stream_setup_active(session))
			device_set_control_timer(dev);
		else
			avrcp_connect(dev);
	}
}

这个回调函数其实就是做了avrcp_connect这一项工作,要不加定时器延时启动,要不直接调用avrcp_connect,里面调用了bt_io_connect->l2cap_connect->connect,这里的connect是GLib socket中的标准API,由此看出BlueZ的一些监听是通过socket套接字来实现的;

3、device_avctp_cb,avctp是音视频控制传输协议,是一个框架协议;我们看到这个回调函数非常简单,只有在disconnected时,会删除一个Control_timer,猜测肯定是控制命令超时之类的作用,等分析avctp时我们再详细去看

static void device_avctp_cb(struct audio_device *dev,
				avctp_state_t old_state,
				avctp_state_t new_state,
				void *user_data)
{
	if (!dev->control)
		return;

	dev->priv->avctp_state = new_state;

	switch (new_state) {
	case AVCTP_STATE_DISCONNECTED:
		break;
	case AVCTP_STATE_CONNECTING:
		device_remove_control_timer(dev);
		break;
	case AVCTP_STATE_CONNECTED:
		break;
	}
}

好了,上面我们分析完了这几个回调函数,分别是device_sink_cb、avdtp_add_state_cb、device_avctp_cb操作非常简单,最终的目的都是修改自身状态,包括device_state,avctp_state和avdtp_state,那么是那里调用的呢?

具体的调用流程需要看a2dp部分,我们下一篇会详细叙述;

在分析三个回调函数之前,我们先要了解在什么情况下会去回调这里,在BlueZ的API说明中摘取了下面一段

Audio hierarchy ===============
(Service->org.bluez) (Interface->org.bluez.Audio)  (Object path->[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX)
This is a generic audio interface that abstracts the different audio profiles.
Methods		void Connect()
			Connect all supported audio profiles on the device.
		void Disconnect()
			Disconnect all audio profiles on the device
		dict GetProperties()
			Returns all properties for the interface. See the
			properties section for available properties.
Signals		void PropertyChanged(string name, variant value)
			This signal indicates a changed value of the given
			property.
Properties	string State
			Possible values: "disconnected", "connecting",
			"connected"
			"disconnected" -> "connecting" Either an incoming or outgoing connection
				attempt ongoing.
			"connecting" -> "disconnected" Connection attempt failed
			"connecting" -> "connected" Successfully connected
			"connected" -> "disconnected" Disconnected from the remote device

上面这段说明告诉了我们在audio中(没有关注headset等应用场景)Dbus方法有三个,分别是Connect(),Disconnect(),GetProperties(),有一个信号,PropertyChanged(),这些方法已经

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值