⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 a2dp.c

📁 Linux的蓝牙操作工具。配合bluez-lib使用
💻 C
📖 第 1 页 / 共 2 页
字号:
		struct avdtp_error start_err;		error("avdtp_start failed");		avdtp_error_init(&start_err, AVDTP_ERROR_ERRNO, EIO);		setup->err = err;		finalize_suspend(setup);	}}static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,				struct avdtp_stream *stream, uint8_t *err,				void *user_data){	struct a2dp_sep *a2dp_sep = user_data;	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)		debug("SBC Sink: Close_Ind");	else		debug("SBC Source: Close_Ind");	return TRUE;}static gboolean reconfigure(gpointer data){	struct a2dp_setup *setup = data;	struct avdtp_local_sep *lsep;	struct avdtp_remote_sep *rsep;	int posix_err;	posix_err = avdtp_get_seps(setup->session, AVDTP_SEP_TYPE_SINK,					AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC,					&lsep, &rsep);	if (posix_err < 0) {		error("No matching ACP and INT SEPs found");		finalize_config_errno(setup, posix_err);	}	posix_err = avdtp_set_configuration(setup->session, rsep, lsep,						setup->client_caps,						&setup->stream);	if (posix_err < 0) {		error("avdtp_set_configuration: %s",			strerror(-posix_err));		finalize_config_errno(setup, posix_err);	}	return FALSE;}static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,			struct avdtp_stream *stream, struct avdtp_error *err,			void *user_data){	struct a2dp_sep *a2dp_sep = user_data;	struct a2dp_setup *setup;	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)		debug("SBC Sink: Close_Cfm");	else		debug("SBC Source: Close_Cfm");	setup = find_setup_by_session(session);	if (!setup)		return;	if (setup->canceled) {		setup_unref(setup);		return;	}	if (err) {		setup->stream = NULL;		setup->err = err;		finalize_config(setup);		return;	}	if (setup->reconfigure)		g_timeout_add(RECONFIGURE_TIMEOUT, reconfigure, setup);}static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,				struct avdtp_stream *stream, uint8_t *err,				void *user_data){	struct a2dp_sep *a2dp_sep = user_data;	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)		debug("SBC Sink: Abort_Ind");	else		debug("SBC Source: Abort_Ind");	a2dp_sep->stream = NULL;	return TRUE;}static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,			struct avdtp_stream *stream, struct avdtp_error *err,			void *user_data){	struct a2dp_sep *a2dp_sep = user_data;	struct a2dp_setup *setup;	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)		debug("SBC Sink: Abort_Cfm");	else		debug("SBC Source: Abort_Cfm");	setup = find_setup_by_session(session);	if (!setup)		return;	setup_unref(setup);}static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,				uint8_t *err, void *user_data){	struct a2dp_sep *a2dp_sep = user_data;	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)		debug("SBC Sink: ReConfigure_Ind");	else		debug("SBC Source: ReConfigure_Ind");	return TRUE;}static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,			struct avdtp_stream *stream, struct avdtp_error *err,			void *user_data){	struct a2dp_sep *a2dp_sep = user_data;	struct a2dp_setup *setup;	if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)		debug("SBC Sink: ReConfigure_Cfm");	else		debug("SBC Source: ReConfigure_Cfm");	setup = find_setup_by_session(session);	if (!setup)		return;	if (setup->canceled) {		if (!err)			avdtp_close(session, stream);		setup_unref(setup);		return;	}	if (err) {		setup->stream = NULL;		setup->err = err;		finalize_config(setup);	}	else		finalize_config_errno(setup, 0);}static struct avdtp_sep_cfm cfm = {	.set_configuration	= setconf_cfm,	.get_configuration	= getconf_cfm,	.open			= open_cfm,	.start			= start_cfm,	.suspend		= suspend_cfm,	.close			= close_cfm,	.abort			= abort_cfm,	.reconfigure		= reconf_cfm};static struct avdtp_sep_ind ind = {	.get_capability		= getcap_ind,	.set_configuration	= setconf_ind,	.get_configuration	= getconf_ind,	.open			= open_ind,	.start			= start_ind,	.suspend		= suspend_ind,	.close			= close_ind,	.abort			= abort_ind,	.reconfigure		= reconf_ind};static int a2dp_source_record(sdp_buf_t *buf){	sdp_list_t *svclass_id, *pfseq, *apseq, *root;	uuid_t root_uuid, l2cap, avdtp, a2src;	sdp_profile_desc_t profile[1];	sdp_list_t *aproto, *proto[2];	sdp_record_t record;	sdp_data_t *psm, *version, *features;	uint16_t lp = AVDTP_UUID, ver = 0x0100, feat = 0x000F;	int ret = 0;	memset(&record, 0, sizeof(sdp_record_t));	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);	root = sdp_list_append(0, &root_uuid);	sdp_set_browse_groups(&record, root);	sdp_uuid16_create(&a2src, AUDIO_SOURCE_SVCLASS_ID);	svclass_id = sdp_list_append(0, &a2src);	sdp_set_service_classes(&record, svclass_id);	sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);	profile[0].version = 0x0100;	pfseq = sdp_list_append(0, &profile[0]);	sdp_set_profile_descs(&record, pfseq);	sdp_uuid16_create(&l2cap, L2CAP_UUID);	proto[0] = sdp_list_append(0, &l2cap);	psm = sdp_data_alloc(SDP_UINT16, &lp);	proto[0] = sdp_list_append(proto[0], psm);	apseq = sdp_list_append(0, proto[0]);	sdp_uuid16_create(&avdtp, AVDTP_UUID);	proto[1] = sdp_list_append(0, &avdtp);	version = sdp_data_alloc(SDP_UINT16, &ver);	proto[1] = sdp_list_append(proto[1], version);	apseq = sdp_list_append(apseq, proto[1]);	aproto = sdp_list_append(0, apseq);	sdp_set_access_protos(&record, aproto);	features = sdp_data_alloc(SDP_UINT16, &feat);	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);	sdp_set_info_attr(&record, "Audio Source", 0, 0);	if (sdp_gen_record_pdu(&record, buf) < 0)		ret = -1;	else		ret = 0;	free(psm);	free(version);	sdp_list_free(proto[0], 0);	sdp_list_free(proto[1], 0);	sdp_list_free(apseq, 0);	sdp_list_free(pfseq, 0);	sdp_list_free(aproto, 0);	sdp_list_free(root, 0);	sdp_list_free(svclass_id, 0);	sdp_list_free(record.attrlist, (sdp_free_func_t) sdp_data_free);	sdp_list_free(record.pattern, free);	return ret;}static int a2dp_sink_record(sdp_buf_t *buf){	return 0;}static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type){	struct a2dp_sep *sep;	GSList **l;	int (*create_record)(sdp_buf_t *buf);	uint32_t *record_id;	sdp_buf_t buf;	sep = g_new0(struct a2dp_sep, 1);	sep->sep = avdtp_register_sep(type, AVDTP_MEDIA_TYPE_AUDIO,					&ind, &cfm, sep);	if (sep->sep == NULL) {		g_free(sep);		return NULL;	}	sep->type = type;	if (type == AVDTP_SEP_TYPE_SOURCE) {		l = &sources;		create_record = a2dp_source_record;		record_id = &source_record_id;	} else {		l = &sinks;		create_record = a2dp_sink_record;		record_id = &sink_record_id;	}	if (*record_id != 0)		goto add;	if (create_record(&buf) < 0) {		error("Unable to allocate new service record");		avdtp_unregister_sep(sep->sep);		g_free(sep);		return NULL;	}	*record_id = add_service_record(conn, &buf);	free(buf.data);	if (!*record_id) {		error("Unable to register A2DP service record");		avdtp_unregister_sep(sep->sep);		g_free(sep);		return NULL;	}add:	*l = g_slist_append(*l, sep);	return sep;}int a2dp_init(DBusConnection *conn, int sources, int sinks){	int i;	if (!sources && !sinks)		return 0;	connection = dbus_connection_ref(conn);	avdtp_init();	for (i = 0; i < sources; i++)		a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE);	for (i = 0; i < sinks; i++)		a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK);	return 0;}static void a2dp_unregister_sep(struct a2dp_sep *sep){	avdtp_unregister_sep(sep->sep);	g_free(sep);}void a2dp_exit(){	g_slist_foreach(sinks, (GFunc) a2dp_unregister_sep, NULL);	g_slist_free(sinks);	sinks = NULL;	g_slist_foreach(sources, (GFunc) a2dp_unregister_sep, NULL);	g_slist_free(sources);	sources = NULL;	if (source_record_id) {		remove_service_record(connection, source_record_id);		source_record_id = 0;	}	if (sink_record_id) {		remove_service_record(connection, sink_record_id);		sink_record_id = 0;	}	dbus_connection_unref(connection);}gboolean a2dp_source_cancel(struct device *dev, unsigned int id){	struct a2dp_setup_cb *cb_data;	struct a2dp_setup *setup;	GSList *l;	setup = find_setup_by_dev(dev);	if (!setup)		return FALSE;	for (cb_data = NULL, l = setup->cb; l != NULL; l = g_slist_next(l)) {		struct a2dp_setup_cb *cb = l->data;		if (cb->id == id) {			cb_data = cb;			break;		}	}	if (!cb_data)		return FALSE;	setup->cb = g_slist_remove(setup->cb, cb_data);	g_free(cb_data);	if (!setup->cb) {		setup->canceled = TRUE;		setup->sep = NULL;	}	return TRUE;}unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb,				GSList *caps, void *user_data){	struct a2dp_setup_cb *cb_data;	GSList *l;	struct a2dp_setup *setup;	struct a2dp_sep *sep = NULL;	struct avdtp_local_sep *lsep;	struct avdtp_remote_sep *rsep;	int posix_err;	for (l = sources; l != NULL; l = l->next) {		struct a2dp_sep *tmp = l->data;		if (tmp->locked)			continue;		if (!tmp->stream || avdtp_has_stream(session, tmp->stream)) {			sep = tmp;			break;		}	}	if (!sep) {		error("a2dp_source_cfg: no available SEP found");		return 0;	}	debug("a2dp_source_config: selected SEP %p", sep);	cb_data = g_new0(struct a2dp_setup_cb, 1);	cb_data->config_cb = cb;	cb_data->user_data = user_data;	cb_data->id = ++cb_id;	setup = find_setup_by_session(session);	if (!setup) {		setup = g_new0(struct a2dp_setup, 1);		setup->session = avdtp_ref(session);		setups = g_slist_append(setups, setup);	}	setup_ref(setup);	setup->cb = g_slist_append(setup->cb, cb_data);	setup->sep = sep;	setup->stream = sep->stream;	setup->client_caps = caps;	switch (avdtp_sep_get_state(sep->sep)) {	case AVDTP_STATE_IDLE:		if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK,				AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC,				&lsep, &rsep) < 0) {			error("No matching ACP and INT SEPs found");			goto failed;		}		posix_err = avdtp_set_configuration(session, rsep, lsep,							caps, &setup->stream);		if (posix_err < 0) {			error("avdtp_set_configuration: %s",				strerror(-posix_err));			goto failed;		}		break;	case AVDTP_STATE_OPEN:	case AVDTP_STATE_STREAMING:		if (avdtp_stream_has_capabilities(setup->stream, caps))			g_idle_add((GSourceFunc) finalize_config, setup);		else if (!setup->reconfigure) {			setup->reconfigure = TRUE;			if (avdtp_close(session, sep->stream) < 0) {				error("avdtp_close failed");				goto failed;			}		}		break;	default:		error("SEP in bad state for requesting a new stream");		goto failed;	}	return cb_data->id;failed:	setup_unref(setup);	cb_id--;	return 0;}unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep,				a2dp_stream_cb_t cb, void *user_data){	struct a2dp_setup_cb *cb_data;	struct a2dp_setup *setup;	cb_data = g_new0(struct a2dp_setup_cb, 1);	cb_data->resume_cb = cb;	cb_data->user_data = user_data;	cb_data->id = ++cb_id;	setup = find_setup_by_session(session);	if (!setup) {		setup = g_new0(struct a2dp_setup, 1);		setup->session = avdtp_ref(session);		setups = g_slist_append(setups, setup);	}	setup_ref(setup);	setup->cb = g_slist_append(setup->cb, cb_data);	setup->sep = sep;	setup->stream = sep->stream;	switch (avdtp_sep_get_state(sep->sep)) {	case AVDTP_STATE_IDLE:		goto failed;		break;	case AVDTP_STATE_OPEN:		if (avdtp_start(session, sep->stream) < 0) {			error("avdtp_start failed");			goto failed;		}		break;	case AVDTP_STATE_STREAMING:		if (!sep->suspending && sep->suspend_timer) {			g_source_remove(sep->suspend_timer);			sep->suspend_timer = 0;			avdtp_unref(sep->session);			sep->session = NULL;		}		if (sep->suspending)			setup->start = TRUE;		else			g_idle_add((GSourceFunc) finalize_resume, setup);		break;	default:		error("SEP in bad state");		goto failed;	}	return cb_data->id;failed:	setup_unref(setup);	cb_id--;	return 0;}unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep,				a2dp_stream_cb_t cb, void *user_data){	struct a2dp_setup_cb *cb_data;	struct a2dp_setup *setup;	cb_data = g_new0(struct a2dp_setup_cb, 1);	cb_data->suspend_cb = cb;	cb_data->user_data = user_data;	cb_data->id = ++cb_id;	setup = find_setup_by_session(session);	if (!setup) {		setup = g_new0(struct a2dp_setup, 1);		setup->session = avdtp_ref(session);		setups = g_slist_append(setups, setup);	}	setup_ref(setup);	setup->cb = g_slist_append(setup->cb, cb_data);	setup->sep = sep;	setup->stream = sep->stream;	switch (avdtp_sep_get_state(sep->sep)) {	case AVDTP_STATE_IDLE:		error("a2dp_source_suspend: no stream to suspend");		goto failed;		break;	case AVDTP_STATE_OPEN:		g_idle_add((GSourceFunc) finalize_suspend, setup);		break;	case AVDTP_STATE_STREAMING:		if (avdtp_suspend(session, sep->stream) < 0) {			error("avdtp_suspend failed");			goto failed;		}		break;	default:		error("SEP in bad state for resume");		goto failed;	}	return cb_data->id;failed:	setup_unref(setup);	cb_id--;	return 0;}gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session){	if (sep->locked)		return FALSE;	debug("SBC Source SEP %p locked", sep);	sep->locked = TRUE;	return TRUE;}gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session){	avdtp_state_t state;	state = avdtp_sep_get_state(sep->sep);	sep->locked = FALSE;	debug("SBC Source SEP %p unlocked", sep);	if (!sep->stream || state == AVDTP_STATE_IDLE)		return TRUE;	switch (state) {	case AVDTP_STATE_OPEN:		/* Set timer here */		break;	case AVDTP_STATE_STREAMING:		if (avdtp_suspend(session, sep->stream) == 0)			sep->suspending = TRUE;		break;	default:		break;	}	return TRUE;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -