diff options
Diffstat (limited to 'audio/gsta2dpsink.c')
| -rw-r--r-- | audio/gsta2dpsink.c | 746 | 
1 files changed, 426 insertions, 320 deletions
diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index e605158f..535f6c5e 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -41,14 +41,6 @@  #include "gsta2dpsink.h" -enum { -        NOT_CONFIGURED, -        CONFIGURING_INIT, -        CONFIGURING_SENT_CONF, -        CONFIGURING_RCVD_DEV_CONF, -        CONFIGURED -}; -  GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug);  #define GST_CAT_DEFAULT a2dp_sink_debug @@ -62,24 +54,10 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug);  		g_mutex_unlock (s->sink_lock);		\  	} G_STMT_END -#define GST_A2DP_SINK_WAIT_CON_END(s) G_STMT_START {			\ -		s->waiting_con_conf = TRUE;				\ -		g_cond_wait (s->con_conf_end, s->sink_lock);		\ -		s->waiting_con_conf = FALSE;				\ -	} G_STMT_END - -#define GST_A2DP_SINK_CONFIGURATION_FAIL(s) G_STMT_START {		\ -		s->con_state = NOT_CONFIGURED;				\ -		g_cond_signal (s->con_conf_end);			\ -	} G_STMT_END - -#define GST_A2DP_SINK_CONFIGURATION_SUCCESS(s) G_STMT_START {		\ -		s->con_state = CONFIGURED;				\ -		g_cond_signal (s->con_conf_end);			\ -	} G_STMT_END  struct bluetooth_data { -	struct ipc_data_cfg cfg;	/* Bluetooth device config */ +	struct bt_getcapabilities_rsp cfg; /* Bluetooth device config */ +	gint link_mtu;  	int samples;			/* Number of encoded samples */  	gchar buffer[BUFFER_SIZE];	/* Codec transfer buffer */  	gsize count;			/* Codec transfer buffer counter */ @@ -121,6 +99,13 @@ static GstStaticPadTemplate a2dp_sink_factory =  				"rate = (int) { 16000, 22050, 24000, 32000, 44100, 48000 }, "  				"channels = (int) [ 1, 2 ]")); +static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self, +					const bt_audio_msg_header_t *msg); +static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self, +				bt_audio_msg_header_t *outmsg, +				int expected_type); + +  static void gst_a2dp_sink_base_init(gpointer g_class)  {  	GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); @@ -137,8 +122,6 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink)  	GST_INFO_OBJECT(self, "stop"); -	self->con_state = NOT_CONFIGURED; -  	if (self->watch_id != 0) {  		g_source_remove(self->watch_id);  		self->watch_id = 0; @@ -152,7 +135,7 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink)  	}  	if (self->server) { -		g_io_channel_close(self->server); +		bt_audio_service_close(g_io_channel_unix_get_fd(self->server));  		g_io_channel_unref(self->server);  		self->server = NULL;  	} @@ -162,9 +145,9 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink)  		self->data = NULL;  	} -	if (self->sbc) { -		g_free(self->sbc); -		self->sbc = NULL; +	if (self->stream_caps) { +		gst_caps_unref(self->stream_caps); +		self->stream_caps = NULL;  	}  	if (self->dev_caps) { @@ -185,12 +168,6 @@ static void gst_a2dp_sink_finalize(GObject *object)  	if (self->device)  		g_free(self->device); -	/* unlock any thread waiting for this signal */ -	GST_A2DP_SINK_MUTEX_LOCK(self); -	GST_A2DP_SINK_CONFIGURATION_FAIL(self); -	GST_A2DP_SINK_MUTEX_UNLOCK(self); - -	g_cond_free(self->con_conf_end);  	g_mutex_free(self->sink_lock);  	G_OBJECT_CLASS(parent_class)->finalize(object); @@ -232,19 +209,10 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,  static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink)  { -	char cmsg_b[CMSG_SPACE(sizeof(int))], m; -	int err, ret, stream_fd; -	struct iovec iov = { &m, sizeof(m) }; -	struct msghdr msgh; -	struct cmsghdr *cmsg; - -	memset(&msgh, 0, sizeof(msgh)); -	msgh.msg_iov = &iov; -	msgh.msg_iovlen = 1; -	msgh.msg_control = &cmsg_b; -	msgh.msg_controllen = CMSG_LEN(sizeof(int)); - -	ret = recvmsg(g_io_channel_unix_get_fd(sink->server), &msgh, 0); +	int err, ret; + +	ret = bt_audio_service_get_data_fd(g_io_channel_unix_get_fd(sink->server)); +  	if (ret < 0) {  		err = errno;  		GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", @@ -252,76 +220,20 @@ static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink)  		return -err;  	} -	/* Receive auxiliary data in msgh */ -	for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; -			cmsg = CMSG_NXTHDR(&msgh, cmsg)) { -		if (cmsg->cmsg_level == SOL_SOCKET -				&& cmsg->cmsg_type == SCM_RIGHTS) { -			stream_fd = (*(int *) CMSG_DATA(cmsg)); -			sink->stream = g_io_channel_unix_new(stream_fd); - -			GST_DEBUG_OBJECT(sink, "stream_fd=%d", stream_fd); -			return 0; -		} -	} - -	return -EINVAL; -} - -static void gst_a2dp_sink_check_dev_caps(GstA2dpSink *self) -{ -	GstStructure *structure; -	GstCaps *dev_caps; -	gint channels; - -	structure = gst_caps_get_structure(self->dev_caps, 0); -	if (!gst_structure_get_int(structure, "channels", &channels)) -		channels = 2; /* FIXME how to get channels */ -	dev_caps = gst_sbc_caps_from_sbc(&(self->data->cfg), self->sbc, -			channels); - -	self->new_dev_caps = TRUE; -	gst_caps_unref(self->dev_caps); -	self->dev_caps = gst_caps_ref(dev_caps); - - -} - -static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *self, -			struct ipc_codec_sbc *sbc) -{ -	struct bluetooth_data *data = self->data; -	struct ipc_data_cfg *cfg = &data->cfg; - -	if (cfg == NULL) { -		GST_ERROR_OBJECT(self, "Error getting codec parameters"); -		return -1; -	} - -	if (cfg->codec != CFG_CODEC_SBC) -		return -1; - -	data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); - -	GST_DEBUG_OBJECT(self, "Codec parameters: " -				"\tallocation=%u\n\tsubbands=%u\n " -				"\tblocks=%u\n\tbitpool=%u\n", -				sbc->allocation, sbc->subbands, -				sbc->blocks, sbc->bitpool); +	sink->stream = g_io_channel_unix_new(ret); +	GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret);  	return 0;  }  static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,  					GstCaps *caps, -					struct ipc_packet *pkt) +					sbc_capabilities_t *pkt)  { - -	struct ipc_data_cfg *cfg = (void *) pkt->data; -	struct ipc_codec_sbc *sbc = (void *) cfg->data; +	sbc_capabilities_t *cfg = &sink->data->cfg.sbc_capabilities;  	const GValue *value = NULL;  	const char *pref, *name; -	GstStructure *structure = gst_caps_get_structure(caps,0); +	GstStructure *structure = gst_caps_get_structure(caps, 0);  	name = gst_structure_get_name(structure);  	/* FIXME only sbc supported here, should suport mp3 */ @@ -330,26 +242,21 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,  		return FALSE;  	} -	if (sink->device) -		strncpy(pkt->device, sink->device, 18); - -	pkt->role = PKT_ROLE_HIFI; -  	value = gst_structure_get_value(structure, "rate"); -	cfg->rate = g_value_get_int(value); +	cfg->frequency = g_value_get_int(value);  	value = gst_structure_get_value(structure, "mode");  	pref = g_value_get_string(value);  	if (strcmp(pref, "auto") == 0) -		cfg->mode = CFG_MODE_AUTO; +		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO;  	else if (strcmp(pref, "mono") == 0) -		cfg->mode = CFG_MODE_MONO; +		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;  	else if (strcmp(pref, "dual") == 0) -		cfg->mode = CFG_MODE_DUAL_CHANNEL; +		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;  	else if (strcmp(pref, "stereo") == 0) -		cfg->mode = CFG_MODE_STEREO; +		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;  	else if (strcmp(pref, "joint") == 0) -		cfg->mode = CFG_MODE_JOINT_STEREO; +		cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;  	else {  		GST_ERROR_OBJECT(sink, "Invalid mode %s", pref);  		return FALSE; @@ -358,107 +265,27 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,  	value = gst_structure_get_value(structure, "allocation");  	pref = g_value_get_string(value);  	if (strcmp(pref, "auto") == 0) -		sbc->allocation = CFG_ALLOCATION_AUTO; +		cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO;  	else if (strcmp(pref, "loudness") == 0) -		sbc->allocation = CFG_ALLOCATION_LOUDNESS; +		cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;  	else if (strcmp(pref, "snr") == 0) -		sbc->allocation = CFG_ALLOCATION_SNR; +		cfg->allocation_method = BT_A2DP_ALLOCATION_SNR;  	else {  		GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref);  		return FALSE;  	}  	value = gst_structure_get_value(structure, "subbands"); -	sbc->subbands = g_value_get_int(value); +	cfg->subbands = g_value_get_int(value);  	value = gst_structure_get_value(structure, "blocks"); -	sbc->blocks = g_value_get_int(value); +	cfg->block_length = g_value_get_int(value); +	/* FIXME min and max ??? */  	value = gst_structure_get_value(structure, "bitpool"); -	sbc->bitpool = g_value_get_int(value); - -	pkt->length = sizeof(*cfg) + sizeof(*sbc); -	pkt->type = PKT_TYPE_CFG_REQ; -	pkt->error = PKT_ERROR_NONE; - -	return TRUE; -} - -static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) -{ -	gchar buf[IPC_MTU]; -	GIOError io_error; -	gsize ret; -	gint total; -	struct ipc_packet *pkt = (void *) buf; -	struct ipc_data_cfg *cfg = (void *) pkt->data; -	struct ipc_codec_sbc *sbc = (void *) cfg->data; - -	memset(buf, 0, sizeof(buf)); - -	io_error = g_io_channel_read(sink->server, (gchar *) buf, -			sizeof(*pkt) + sizeof(*cfg), &ret); -	if (io_error != G_IO_ERROR_NONE && ret > 0) { -		GST_ERROR_OBJECT(sink, "Error ocurred while receiving " -					"configurarion packet answer"); -		return FALSE; -	} - -	total = ret; -	if (pkt->type != PKT_TYPE_CFG_RSP) { -		GST_ERROR_OBJECT(sink, "Unexpected packet type %d " -					"received", pkt->type); -		return FALSE; -	} - -	if (pkt->error != PKT_ERROR_NONE) { -		GST_ERROR_OBJECT(sink, "Error %d while configuring " -					"device", pkt->error); -		return FALSE; -	} - -	if (cfg->codec != CFG_CODEC_SBC) { -		GST_ERROR_OBJECT(sink, "Unsupported format"); -		return FALSE; -	} - -	io_error = g_io_channel_read(sink->server, (gchar *) sbc, -					sizeof(*sbc), &ret); -	if (io_error != G_IO_ERROR_NONE) { -		GST_ERROR_OBJECT(sink, "Error while reading data from socket " -				"%s (%d)", strerror(errno), errno); -		return FALSE; -	} else if (ret == 0) { -		GST_ERROR_OBJECT(sink, "Read 0 bytes from socket"); -		return FALSE; -	} - -	total += ret; -	GST_DEBUG_OBJECT(sink, "OK - %d bytes received", total); - -	if (pkt->length != (total - sizeof(struct ipc_packet))) { -		GST_ERROR_OBJECT(sink, "Error while configuring device: " -			"packet size doesn't match"); -		return FALSE; -	} - -	memcpy(&sink->data->cfg, cfg, sizeof(*cfg)); -	memcpy(sink->sbc, sbc, sizeof(struct ipc_codec_sbc)); +	cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value); -	gst_a2dp_sink_check_dev_caps(sink); - -	GST_DEBUG_OBJECT(sink, "Device configuration:\n\tchannel=%p\n\t" -			"fd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", -			sink->stream, sink->data->cfg.fd_opt, -			sink->data->cfg.pkt_len, sink->data->cfg.sample_size, -			sink->data->cfg.rate); - -	if (sink->data->cfg.codec == CFG_CODEC_SBC) { -		/* FIXME is this necessary? */ -		ret = gst_a2dp_sink_bluetooth_a2dp_init(sink, sbc); -		if (ret < 0) -			return FALSE; -	} +	memcpy(pkt, cfg, sizeof(*pkt));  	return TRUE;  } @@ -504,7 +331,7 @@ static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self)  	GST_LOG_OBJECT(self, "emptying stream pipe");  	while (1) {  		err = g_io_channel_read(self->stream, data->buffer, -					(gsize) data->cfg.pkt_len, +					(gsize) data->cfg.link_mtu,  					&read);  		if (err != G_IO_ERROR_NONE || read <= 0)  			break; @@ -531,33 +358,6 @@ static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self)  	return TRUE;  } -static void gst_a2dp_sink_conf_recv_data(GstA2dpSink *sink) -{ -	/* -	 * We hold the lock, since we can send a signal. -	 * It is a good practice, according to the glib api. -	 */ -	GST_A2DP_SINK_MUTEX_LOCK(sink); - -	switch (sink->con_state) { -	case CONFIGURING_SENT_CONF: -		if (gst_a2dp_sink_conf_resp(sink)) -			sink->con_state = CONFIGURING_RCVD_DEV_CONF; -		else -			GST_A2DP_SINK_CONFIGURATION_FAIL(sink); -		break; -	case CONFIGURING_RCVD_DEV_CONF: -		if (gst_a2dp_sink_conf_recv_stream_fd(sink)) -			GST_A2DP_SINK_CONFIGURATION_SUCCESS(sink); -		else -			GST_A2DP_SINK_CONFIGURATION_FAIL(sink); -		break; -	} - -	GST_A2DP_SINK_MUTEX_UNLOCK(sink); -} - -  static gboolean server_callback(GIOChannel *chan,  					GIOCondition cond, gpointer data)  { @@ -568,16 +368,217 @@ static gboolean server_callback(GIOChannel *chan,  	else if (cond & G_IO_ERR) {  		sink = GST_A2DP_SINK(data);  		GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); -	} else if (cond & G_IO_IN) { -		sink = GST_A2DP_SINK(data); -		if (sink->con_state != NOT_CONFIGURED && -			sink->con_state != CONFIGURED) -			gst_a2dp_sink_conf_recv_data(sink); -		else -			GST_WARNING_OBJECT(sink, "Unexpected data received"); +	} + +	return TRUE; +} + +static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self) +{ +	sbc_capabilities_t *sbc = &self->data->cfg.sbc_capabilities; +	GstStructure *structure; +	GValue *value; +	GValue *list; +	gchar *tmp; + +	GST_LOG_OBJECT(self, "updating device caps"); + +	structure = gst_structure_empty_new("audio/x-sbc"); +	value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); + +	/* mode */ +	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); +	if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { +		g_value_set_static_string(value, "joint"); +		gst_value_list_prepend_value(list, value); +		g_value_set_static_string(value, "stereo"); +		gst_value_list_prepend_value(list, value); +		g_value_set_static_string(value, "mono"); +		gst_value_list_prepend_value(list, value); +		g_value_set_static_string(value, "dual"); +		gst_value_list_prepend_value(list, value);  	} else { -		sink = GST_A2DP_SINK(data); -		GST_WARNING_OBJECT(sink, "Unexpected callback call"); +		if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { +			g_value_set_static_string(value, "mono"); +			gst_value_list_prepend_value(list, value); +		} +		if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { +			g_value_set_static_string(value, "stereo"); +			gst_value_list_prepend_value(list, value); +		} +		if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { +			g_value_set_static_string(value, "dual"); +			gst_value_list_prepend_value(list, value); +		} +		if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { +			g_value_set_static_string(value, "joint"); +			gst_value_list_prepend_value(list, value); +		} +	} +	g_value_unset(value); +	if (list) { +		gst_structure_set_value(structure, "mode", list); +		g_free(list); +		list = NULL; +	} + +	/* subbands */ +	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); +	value = g_value_init(value, G_TYPE_INT); +	if (sbc->subbands & BT_A2DP_SUBBANDS_4) { +		g_value_set_int(value, 4); +		gst_value_list_prepend_value(list, value); +	} +	if (sbc->subbands & BT_A2DP_SUBBANDS_8) { +		g_value_set_int(value, 8); +		gst_value_list_prepend_value(list, value); +	} +	g_value_unset(value); +	if (list) { +		gst_structure_set_value(structure, "subbands", list); +		g_free(list); +		list = NULL; +	} + +	/* blocks */ +	value = g_value_init(value, G_TYPE_INT); +	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); +	if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) { +		g_value_set_int(value, 16); +		gst_value_list_prepend_value(list, value); +	} +	if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) { +		g_value_set_int(value, 12); +		gst_value_list_prepend_value(list, value); +	} +	if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) { +		g_value_set_int(value, 8); +		gst_value_list_prepend_value(list, value); +	} +	if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) { +		g_value_set_int(value, 4); +		gst_value_list_prepend_value(list, value); +	} +	g_value_unset(value); +	if (list) { +		gst_structure_set_value(structure, "blocks", list); +		g_free(list); +		list = NULL; +	} + +	/* allocation */ +	g_value_init(value, G_TYPE_STRING); +	list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); +	if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) { +		g_value_set_static_string(value, "loudness"); +		gst_value_list_prepend_value(list, value); +		g_value_set_static_string(value, "snr"); +		gst_value_list_prepend_value(list, value); +	} else { +		if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { +			g_value_set_static_string(value, "loudness"); +			gst_value_list_prepend_value(list, value); +		} +		if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { +			g_value_set_static_string(value, "snr"); +			gst_value_list_prepend_value(list, value); +		} +	} +	g_value_unset(value); +	if (list) { +		gst_structure_set_value(structure, "allocation", list); +		g_free(list); +		list = NULL; +	} + +	/* rate */ +	g_value_init(value, G_TYPE_INT); +	list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); +	if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) { +		g_value_set_int(value, 48000); +		gst_value_list_prepend_value(list, value); +	} +	if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) { +		g_value_set_int(value, 44100); +		gst_value_list_prepend_value(list, value); +	} +	if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) { +		g_value_set_int(value, 32000); +		gst_value_list_prepend_value(list, value); +	} +	if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_16000) { +		g_value_set_int(value, 16000); +		gst_value_list_prepend_value(list, value); +	} +	g_value_unset(value); +	if (list) { +		gst_structure_set_value(structure, "rate", list); +		g_free(list); +		list = NULL; +	} + +	/* bitpool */ +	value = g_value_init(value, GST_TYPE_INT_RANGE); +	gst_value_set_int_range(value, sbc->min_bitpool, sbc->max_bitpool); +	gst_structure_set_value(structure, "bitpool", value); + +	/* channels */ +	gst_value_set_int_range(value, 1, 2); +	gst_structure_set_value(structure, "channels", value); + +	g_free(value); + +	self->dev_caps = gst_caps_new_full(structure, NULL); + +	tmp = gst_caps_to_string(self->dev_caps); +	GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); +	g_free(tmp); + +	return TRUE; +} + +static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self) +{ +	gchar *buf[BT_AUDIO_IPC_PACKET_SIZE]; +	struct bt_getcapabilities_req *req = (void *) buf; +	struct bt_getcapabilities_rsp *rsp = (void *) buf; +	GIOError io_error; + +	memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE); + +	req->h.msg_type = BT_GETCAPABILITIES_REQ; +	strncpy(req->device, self->device, 18); + +	req->transport = BT_CAPABILITIES_TRANSPORT_A2DP; +	req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; +	io_error = gst_a2dp_sink_audioservice_send(self, &req->h); +	if (io_error != G_IO_ERROR_NONE) { +		GST_ERROR_OBJECT(self, "Error while asking device caps"); +	} + +	io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, +			BT_GETCAPABILITIES_RSP); +	if (io_error != G_IO_ERROR_NONE) { +		GST_ERROR_OBJECT(self, "Error while getting device caps"); +		return FALSE; +	} + +	if (rsp->posix_errno != 0) { +		GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)", +					strerror(rsp->posix_errno), +					rsp->posix_errno); +		return FALSE; +	} + +	if (rsp->transport != BT_CAPABILITIES_TRANSPORT_A2DP) { +		GST_ERROR_OBJECT(self, "Non a2dp answer from device"); +		return FALSE; +	} + +	memcpy(&self->data->cfg, rsp, sizeof(*rsp)); +	if (!gst_a2dp_sink_update_caps(self)) { +		GST_WARNING_OBJECT(self, "failed to update capabilities"); +		return FALSE;  	}  	return TRUE; @@ -586,7 +587,6 @@ static gboolean server_callback(GIOChannel *chan,  static gboolean gst_a2dp_sink_start(GstBaseSink *basesink)  {  	GstA2dpSink *self = GST_A2DP_SINK(basesink); -	struct sockaddr_un addr = { AF_UNIX, IPC_SOCKET_NAME };  	gint sk;  	gint err; @@ -594,107 +594,146 @@ static gboolean gst_a2dp_sink_start(GstBaseSink *basesink)  	self->watch_id = 0; -	sk = socket(PF_LOCAL, SOCK_STREAM, 0); -	if (sk < 0) { +	sk = bt_audio_service_open(); +	if (sk <= 0) {  		err = errno; -		GST_ERROR_OBJECT(self, "Cannot open socket: %s (%d)", -			strerror(err), err); -		return FALSE; -	} - -	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { -		err = errno; -		GST_ERROR_OBJECT(self, "Connection fail %s (%d)", -			strerror(err), err); -		close(sk); -		return FALSE; +		GST_ERROR_OBJECT(self, "Cannot open connection to bt " +			"audio service: %s %d", strerror(err), err); +		goto failed;  	}  	self->server = g_io_channel_unix_new(sk); - -	self->watch_id = g_io_add_watch(self->server, G_IO_IN | G_IO_HUP | -			G_IO_ERR | G_IO_NVAL, server_callback, self); +	self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR | +					G_IO_NVAL, server_callback, self);  	self->data = g_new0(struct bluetooth_data, 1);  	memset(self->data, 0, sizeof(struct bluetooth_data)); -	self->sbc = g_new0(struct ipc_codec_sbc, 1); -  	self->stream = NULL; -	self->con_state = NOT_CONFIGURED; -	self->new_dev_caps = FALSE; -	self->dev_caps = NULL; +	self->stream_caps = NULL; -	self->waiting_con_conf = FALSE; +	if (!gst_a2dp_sink_get_capabilities(self)) +		goto failed;  	return TRUE; + +failed: +	bt_audio_service_close(sk); +	return FALSE;  } -static gboolean gst_a2dp_sink_send_conf_pkt(GstA2dpSink *sink, GstCaps *caps) +static gboolean gst_a2dp_sink_stream_start(GstA2dpSink *self)  { -	gchar buf[IPC_MTU]; -	struct ipc_packet *pkt = (void *) buf; -	gboolean ret; -	gsize bytes_sent; +	gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; +	struct bt_streamstart_req *req = (void *) buf; +	struct bt_streamstart_rsp *rsp = (void *) buf; +	struct bt_datafd_ind *ind = (void*) buf;  	GIOError io_error; -	g_assert(sink->con_state == NOT_CONFIGURED); +	GST_DEBUG_OBJECT(self, "stream start"); -	memset (pkt, 0, sizeof(buf)); -	ret = gst_a2dp_sink_init_pkt_conf(sink, caps, pkt); -	if (!ret) { -		GST_ERROR_OBJECT(sink, "Couldn't initialize parse caps " -				"to packet configuration"); +	memset (req, 0, sizeof(buf)); +	req->h.msg_type = BT_STREAMSTART_REQ; + +	io_error = gst_a2dp_sink_audioservice_send(self, &req->h); +	if (io_error != G_IO_ERROR_NONE) { +		GST_ERROR_OBJECT(self, "Error ocurred while sending " +					"start packet");  		return FALSE;  	} -	sink->con_state = CONFIGURING_INIT; +	GST_DEBUG_OBJECT(self, "stream start packet sent"); -	io_error = g_io_channel_write(sink->server, (gchar *) pkt, -			sizeof(*pkt) + pkt->length, &bytes_sent); +	io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, +			BT_STREAMSTART_RSP);  	if (io_error != G_IO_ERROR_NONE) { -		GST_ERROR_OBJECT(sink, "Error ocurred while sending " -					"configurarion packet"); -		sink->con_state = NOT_CONFIGURED; +		GST_ERROR_OBJECT(self, "Error while stream start confirmation"); +		return FALSE; +	} + +	if (rsp->posix_errno != 0) { +		GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)", +					strerror(rsp->posix_errno), +					rsp->posix_errno); +		return FALSE; +	} + +	GST_DEBUG_OBJECT(self, "stream started"); + +	io_error = gst_a2dp_sink_audioservice_expect(self, &ind->h, +			BT_STREAMFD_IND); +	if (io_error != G_IO_ERROR_NONE) { +		GST_ERROR_OBJECT(self, "Error while receiving stream fd");  		return FALSE;  	} -	GST_DEBUG_OBJECT(sink, "%d bytes sent", bytes_sent); -	sink->con_state = CONFIGURING_SENT_CONF; +	if (!gst_a2dp_sink_conf_recv_stream_fd(self)) +		return FALSE;  	return TRUE;  } -static gboolean gst_a2dp_sink_start_dev_conf(GstA2dpSink *sink, GstCaps *caps) +static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps)  { +	gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; +	struct bt_setconfiguration_req *req = (void *) buf; +	struct bt_setconfiguration_rsp *rsp = (void *) buf;  	gboolean ret; +	GIOError io_error; + +	GST_DEBUG_OBJECT(self, "configuring device"); + +	memset (req, 0, sizeof(buf)); +	req->h.msg_type = BT_SETCONFIGURATION_REQ; +	strncpy(req->device, self->device, 18); +	ret = gst_a2dp_sink_init_pkt_conf(self, caps, &req->sbc_capabilities); +	if (!ret) { +		GST_ERROR_OBJECT(self, "Couldn't parse caps " +				"to packet configuration"); +		return FALSE; +	} -	g_assert(sink->con_state == NOT_CONFIGURED); +	io_error = gst_a2dp_sink_audioservice_send(self, &req->h); +	if (io_error != G_IO_ERROR_NONE) { +		GST_ERROR_OBJECT(self, "Error ocurred while sending " +					"configurarion packet"); +		return FALSE; +	} -	GST_DEBUG_OBJECT(sink, "starting device configuration"); +	GST_DEBUG_OBJECT(self, "configuration packet sent"); -	ret = gst_a2dp_sink_send_conf_pkt(sink, caps); +	io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, +			BT_SETCONFIGURATION_RSP); +	if (io_error != G_IO_ERROR_NONE) { +		GST_ERROR_OBJECT(self, "Error while receiving device confirmation"); +		return FALSE; +	} -	return ret; +	if (rsp->posix_errno != 0) { +		GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : %s(%d)", +					strerror(rsp->posix_errno), +					rsp->posix_errno); +		return FALSE; +	} + +	GST_DEBUG_OBJECT(self, "configuration set"); + +	return TRUE;  }  static GstFlowReturn gst_a2dp_sink_preroll(GstBaseSink *basesink,  					GstBuffer *buffer)  {  	GstA2dpSink *sink = GST_A2DP_SINK(basesink); +	gboolean ret;  	GST_A2DP_SINK_MUTEX_LOCK(sink); -	if (sink->con_state == NOT_CONFIGURED) -		gst_a2dp_sink_start_dev_conf(sink, GST_BUFFER_CAPS(buffer)); - -	/* wait for the connection process to finish */ -	if (sink->con_state != CONFIGURED) -		GST_A2DP_SINK_WAIT_CON_END(sink); +	ret = gst_a2dp_sink_stream_start(sink);  	GST_A2DP_SINK_MUTEX_UNLOCK(sink); -	if (sink->con_state != CONFIGURED) +	if (!ret)  		return GST_FLOW_ERROR;  	return GST_FLOW_OK; @@ -745,7 +784,7 @@ static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink,  	encoded = GST_BUFFER_SIZE(buffer); -	if (data->count + encoded >= data->cfg.pkt_len) { +	if (data->count + encoded >= data->cfg.link_mtu) {  		ret = gst_a2dp_sink_avdtp_write(self);  		if (ret < 0)  			return GST_FLOW_ERROR; @@ -758,24 +797,28 @@ static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink,  	return GST_FLOW_OK;  } +static GstCaps* gst_a2dp_sink_get_caps(GstBaseSink *basesink) +{ +	GstA2dpSink *self = GST_A2DP_SINK(basesink); + +	return self->dev_caps ? gst_caps_ref(self->dev_caps): NULL; +} +  static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps)  {  	GstA2dpSink *self = GST_A2DP_SINK(basesink); +	gboolean ret;  	GST_A2DP_SINK_MUTEX_LOCK(self); -	if (self->con_state == NOT_CONFIGURED) { -		gst_a2dp_sink_start_dev_conf(self, caps); +	ret = gst_a2dp_sink_configure(self, caps); -		if (self->dev_caps) -			gst_caps_unref(self->dev_caps); -		self->dev_caps = gst_caps_ref(caps); +	if (self->stream_caps) +		gst_caps_unref(self->stream_caps); +	self->stream_caps = gst_caps_ref(caps); -		/* we suppose the device will accept this caps */ -		self->new_dev_caps = FALSE; -	}  	GST_A2DP_SINK_MUTEX_UNLOCK(self); -	return TRUE; +	return ret;  }  static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink) @@ -800,12 +843,7 @@ static GstFlowReturn gst_a2dp_sink_buffer_alloc(GstBaseSink *basesink,  		return GST_FLOW_ERROR;  	} -	if (self->new_dev_caps && self->dev_caps) { -		GST_INFO_OBJECT(self, "new caps from device"); -		gst_buffer_set_caps(*buf, self->dev_caps); -		self->new_dev_caps = FALSE; -	} else -		gst_buffer_set_caps(*buf, caps); +	gst_buffer_set_caps(*buf, caps);  	GST_BUFFER_OFFSET(*buf) = offset; @@ -830,6 +868,7 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)  	basesink_class->render = GST_DEBUG_FUNCPTR(gst_a2dp_sink_render);  	basesink_class->preroll = GST_DEBUG_FUNCPTR(gst_a2dp_sink_preroll);  	basesink_class->set_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps); +	basesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_caps);  	basesink_class->unlock = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unlock);  	basesink_class->buffer_alloc =  		GST_DEBUG_FUNCPTR(gst_a2dp_sink_buffer_alloc); @@ -847,12 +886,79 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass)  {  	self->device = NULL;  	self->data = NULL; -	self->sbc = NULL;  	self->stream = NULL; -	self->con_state = NOT_CONFIGURED; -	self->con_conf_end = g_cond_new(); -	self->waiting_con_conf = FALSE; +	self->dev_caps = NULL; +  	self->sink_lock = g_mutex_new();  } + +static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self, +					const bt_audio_msg_header_t *msg) +{ +	gint err; +	GIOError error; +	gsize written; + +	GST_DEBUG_OBJECT(self, "sending %s", bt_audio_strmsg(msg->msg_type)); + +	error = g_io_channel_write(self->server, (const gchar*) msg, +			BT_AUDIO_IPC_PACKET_SIZE, &written); +	if (error != G_IO_ERROR_NONE) { +		err = errno; +		GST_ERROR_OBJECT(self, "Error sending data to audio service:" +			" %s(%d)", strerror(err), err); +	} + +	return error; +} + +static GIOError gst_a2dp_sink_audioservice_recv(GstA2dpSink *self, +					bt_audio_msg_header_t *inmsg) +{ +	GIOError status; +	gsize bytes_read; +	const char *type; + +	status = g_io_channel_read(self->server, (gchar*) inmsg, +			BT_AUDIO_IPC_PACKET_SIZE, &bytes_read); +	if (status != G_IO_ERROR_NONE) { +		GST_ERROR_OBJECT(self, "Error receiving data from service"); +		return status; +	} + +	type = bt_audio_strmsg(inmsg->msg_type); +	if (!type) { +		GST_ERROR_OBJECT(self, "Bogus message type %d " +				"received from audio service", +				inmsg->msg_type); +		return G_IO_ERROR_INVAL; +	} + +	GST_DEBUG_OBJECT(self, "Received %s", type); + +	return status; +} + +static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self, +					bt_audio_msg_header_t *outmsg, +					int expected_type) +{ +	GIOError status; + +	status = gst_a2dp_sink_audioservice_recv(self, outmsg); +	if (status != G_IO_ERROR_NONE) +		return status; + +	if (outmsg->msg_type != expected_type) { +		GST_ERROR_OBJECT(self, "Bogus message %s " +				"received while %s was expected", +				bt_audio_strmsg(outmsg->msg_type), +				bt_audio_strmsg(expected_type)); +		return G_IO_ERROR_INVAL; +	} + +	return status; +} +  | 
