diff options
| -rw-r--r-- | audio/pcm_bluetooth.c | 411 | 
1 files changed, 147 insertions, 264 deletions
| diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index a7c90661..c75f8af3 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -28,7 +28,6 @@  #include <sys/socket.h>  #include <sys/un.h>  #include <sys/time.h> -#include <pthread.h>  #include <netinet/in.h> @@ -41,7 +40,7 @@  #include "ipc.h"  #include "sbc.h" -/*#define ENABLE_DEBUG*/ +//#define ENABLE_DEBUG  #define BUFFER_SIZE 2048 @@ -59,9 +58,6 @@  #define SCO_RXBUFS 0x04  #endif -#define PERIOD_TIME_USECS(data) (1000000.0 * \ -				((data)->io.period_size) / \ -				(data)->io.rate)  struct rtp_header {  	uint8_t cc:4;  	uint8_t x:1; @@ -97,13 +93,13 @@ struct bluetooth_a2dp {  	uint16_t seq_num;		/* */  	int frame_count;		/* */ -	pthread_t hw_thread;		/* Makes virtual hw pointer move */	 -	int pipefd[2];			/* Inter thread communication */ +	int bandwithcount; +	struct timeval bandwithtimestamp;  };  struct bluetooth_data {  	snd_pcm_ioplug_t io; -	volatile snd_pcm_sframes_t hw_ptr; +	snd_pcm_sframes_t hw_ptr;  	struct ipc_data_cfg cfg;	/* Bluetooth device config */  	int stream_fd;			/* Audio stream filedescriptor */  	int sock;			/* Daemon unix socket */ @@ -117,9 +113,9 @@ void memcpy_changeendian(void *dst, const void *src, int size)  	int i;  	const uint16_t *ptrsrc = src;  	uint16_t *ptrdst = dst; - -	for (i = 0; i < size / 2; i++) +	for (i = 0; i < size / 2; i++) {  		*ptrdst++ = htons(*ptrsrc++); +	}  }  static int bluetooth_start(snd_pcm_ioplug_t *io) @@ -136,85 +132,6 @@ static int bluetooth_stop(snd_pcm_ioplug_t *io)  	return 0;  } -static void *a2dp_playback_hw_thread(void *param) -{ -	struct bluetooth_data* data = (struct bluetooth_data *)param; -	unsigned int num_period_elapsed = 0; -	unsigned long long starttime; /* in usecs */ -	struct timeval tv; -	int ret; - -	gettimeofday(&tv, 0); -	starttime = tv.tv_sec * 1000000 + tv.tv_usec; - -	for(;;) { -		unsigned long long curtime; -		unsigned int ntimes; - -		gettimeofday(&tv, 0); - -		/* How much time period_time has elapsed since the thread started ? */ -		curtime = tv.tv_sec * 1000000 + tv.tv_usec; -		ntimes = (1.0 * (curtime - starttime)) / PERIOD_TIME_USECS(data); - -		if (ntimes > num_period_elapsed) { -			char c = 'w'; -			data->hw_ptr = (data->hw_ptr + -					(ntimes - num_period_elapsed) -					* data->io.period_size) -					% data->io.buffer_size; -			DBG("pointer = %ld", data->hw_ptr); -			/* Notify user that hardware pointer has moved */ -			ret = write(data->a2dp.pipefd[1], &c, 1); -			assert(ret == 1); -			num_period_elapsed = ntimes; -		}		 -		/* Period time is usually no shorter that 1 ms, -		   no need to sleep for a shorter amount of time */ -		usleep(1000); -		/* Offer opportunity to be canceled by main thread */ -		pthread_testcancel();	 -	} -} -static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) -{ -	struct bluetooth_data *data = io->private_data; -	struct bluetooth_a2dp *a2dp_data = &data->a2dp; -	int ret = 0; - -	DBG("%p", io); - -	assert(a2dp_data->hw_thread == 0); -	ret = -pthread_create(&a2dp_data->hw_thread, 0, a2dp_playback_hw_thread, data); - -	DBG(" - return %d", ret); - -	return ret; -} - -static int bluetooth_a2dp_playback_stop(snd_pcm_ioplug_t *io) -{ -	struct bluetooth_data *data = io->private_data; -	struct bluetooth_a2dp *a2dp_data = &data->a2dp;	 -	int ret = 0; - -	DBG("%p", io); - -	/* Beware - We can be called more than once */ -	if (a2dp_data->hw_thread != 0) { -		ret = -pthread_cancel(a2dp_data->hw_thread); -		if (ret != 0) -			goto done; - -		ret = -pthread_join(a2dp_data->hw_thread, 0); -	} - -done: -	a2dp_data->hw_thread = 0; -	DBG(" - return %d", ret); -	return ret; -} -  static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io)  {  	struct bluetooth_data *data = io->private_data; @@ -237,12 +154,6 @@ static void bluetooth_exit(struct bluetooth_data *data)  	if (data->cfg.codec == CFG_CODEC_SBC)  		sbc_finish(&data->a2dp.sbc); -	if(data->a2dp.pipefd[0] > 0) -		close(data->a2dp.pipefd[0]); -	 -	if(data->a2dp.pipefd[1] > 0) -		close(data->a2dp.pipefd[1]); -	  	free(data);  } @@ -260,7 +171,6 @@ static int bluetooth_close(snd_pcm_ioplug_t *io)  static int bluetooth_prepare(snd_pcm_ioplug_t *io)  {  	struct bluetooth_data *data = io->private_data; -	char c = 'w';  	DBG("Preparing with io->period_size = %lu, io->buffer_size = %lu",  			io->period_size, io->buffer_size); @@ -274,8 +184,7 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io)  		 * If it is, capture won't start */  		data->hw_ptr = io->period_size; -	/* a2dp : wake up any client polling at us */	 -	return write(data->a2dp.pipefd[1], &c, 1); +	return 0;  }  static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, @@ -325,80 +234,11 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,  		return 0;  	err = errno; -  	SNDERR("%s (%d)", strerror(err), err);  	return -err;  } -static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io, -					struct pollfd *pfd, -					unsigned int space) -{ -	assert(io); -	assert(space >= 1); - -	pfd[0].fd = ((struct bluetooth_data *)io->private_data)->stream_fd; -	pfd[0].events = POLLIN; -	pfd[0].revents = 0; - -	return 1; -} - -static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED, -					struct pollfd *pfds, unsigned int nfds, -					unsigned short *revents) -{ -	assert(pfds && nfds == 1 && revents); - -	*revents = pfds[0].revents; - -	return 0; -} - -static int bluetooth_a2dp_playback_poll_descriptors(snd_pcm_ioplug_t *io,  -							struct pollfd *pfd, -							unsigned int space) -{ -	struct bluetooth_data *data = io->private_data; - -	DBG(""); - -	assert(io); -	assert(space >= 1); -	assert(data->a2dp.pipefd[0] != 0); - -	pfd[0].fd = data->a2dp.pipefd[0]; -	pfd[0].events = POLLIN; -	pfd[0].revents = 0; - -	return 1; -} - -static int bluetooth_a2dp_playback_poll_revents(snd_pcm_ioplug_t *io, -						struct pollfd *pfds, -						unsigned int nfds, -						unsigned short *revents) -{ -	static char buf[1]; -	int ret = 0;	 - -	DBG(""); - -	assert(pfds); -	assert(nfds == 1); -	assert(revents); -	assert(pfds[0].fd != 0); - -	if (io->state != SND_PCM_STATE_PREPARED) -		ret = read(pfds[0].fd, buf, 1); - -	*revents = (pfds[0].revents & ~POLLIN) | POLLOUT; - -	return 0; -} - -  static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io,  						const snd_pcm_channel_area_t *areas,  						snd_pcm_uframes_t offset, @@ -524,14 +364,19 @@ static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io,  	return ret;  } -static int avdtp_write(struct bluetooth_data *data) +static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock)  { -	int ret = 0; +	int count = 0, written = 0, ret = 0;  	struct rtp_header *header;  	struct rtp_payload *payload;  	struct bluetooth_a2dp *a2dp = &data->a2dp; +#ifdef ENABLE_DEBUG +	static struct timeval send_date = { 0, 0 }; +	static struct timeval prev_date = { 0, 0 }; +	struct timeval send_delay = { 0, 0 }; +	struct timeval sendz_delay = { 0, 0 }; +#endif -	DBG("");  	header = (void *) a2dp->buffer;  	payload = (void *) (a2dp->buffer + sizeof(*header)); @@ -544,24 +389,98 @@ static int avdtp_write(struct bluetooth_data *data)  	header->timestamp = htonl(a2dp->nsamples);  	header->ssrc = htonl(1); -	ret = send(data->stream_fd, a2dp->buffer, a2dp->count, -				MSG_DONTWAIT); -	if(ret == -1) -		ret = -errno; +	while (count++ < 10) { +#ifdef ENABLE_DEBUG +		gettimeofday(&send_date, NULL); +#endif +		ret = send(data->stream_fd, a2dp->buffer, a2dp->count, +				nonblock ? MSG_DONTWAIT : 0); +		if (ret < 0) { +			ret = -errno; +			if (errno == EAGAIN) +				goto retry; +			fprintf(stderr, "send: %s (%d)\n", strerror(errno), +					errno); +			goto done; +		} + +		written += ret; + +#ifdef ENABLE_DEBUG +		if ((written >= 0 || errno == EAGAIN) && prev_date.tv_sec != 0) { +			long delay, real, theo, delta; + +			delay = (long) (send_delay.tv_sec * 1000 + +						send_delay.tv_usec / 1000), +			real = (long) (sendz_delay.tv_sec * 1000 + +						sendz_delay.tv_usec / 1000); +			theo = (long) (((float) a2dp->nsamples) / +						data->cfg.rate * 1000.0); +			delta = (long) (sendz_delay.tv_sec * 1000 + +						sendz_delay.tv_usec / 1000) - +					(long) (((float) a2dp->nsamples) / +							data->cfg.rate * 1000.0); + +			timersub(&send_date, &prev_date, &send_delay); +			timersub(&send_date, &a2dp->ntimestamp, &sendz_delay); + +			printf("send %d (cumul=%d) samples (delay=%ld ms," +					" real=%ld ms, theo=%ld ms," +					" delta=%ld ms).\n", a2dp->samples, +					a2dp->nsamples, delay, real, theo, +					delta); +		} +#endif +		if (written == a2dp->count) +			break; + +		a2dp->count -= written; -	/* Kernel side l2cap socket layer makes sure either everything  -	   is buffered for sending, or nothing is buffered. -	   This assertion is to remind people of this fact (and be noticed -	   the day that changes) - 	   */ -	assert(ret < 0 || ret == a2dp->count); +retry: +		DBG("send (retry)."); +		usleep(150000); +	} + +#ifdef ENABLE_DEBUG +	prev_date = send_date; +#endif + +	if (written != a2dp->count) +		printf("Wrote %d not %d bytes\n", written, a2dp->count); + +#ifdef ENABLE_DEBUG +	else { +		/* Measure bandwith usage */ +		struct timeval now = { 0, 0 }; +		struct timeval interval = { 0, 0 }; + +		if(a2dp->bandwithtimestamp.tv_sec == 0) +			gettimeofday(&a2dp->bandwithtimestamp, NULL); + +		/* See if we must wait again */ +		gettimeofday(&now, NULL); +		timersub(&now, &a2dp->bandwithtimestamp, &interval); +		if(interval.tv_sec > 0) +			printf("Bandwith: %d (%d kbps)\n", a2dp->bandwithcount, +				a2dp->bandwithcount / 128); +		a2dp->bandwithtimestamp = now; +		a2dp->bandwithcount = 0; +	} + +	a2dp->bandwithcount += written; +#endif + +done:  	/* Reset buffer of data to send */  	a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);  	a2dp->frame_count = 0;  	a2dp->samples = 0;  	a2dp->seq_num++; +	if (written > 0) +		return written; +  	return ret;  } @@ -578,35 +497,9 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io,  	uint8_t *buff;  	static int codesize = 0; -	DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu" -			, areas->step, areas->first, offset, size); -	DBG("hw_ptr = %lu, appl_ptr = %lu" -			, io->hw_ptr, io->appl_ptr); - -	if(io->hw_ptr > io->appl_ptr) { -		ret = bluetooth_a2dp_playback_stop(io); -		if(ret == 0)		 -			ret = -EPIPE; -		goto done;	 -	}		 - -	/* Check if we should autostart */ -	if(io->state == SND_PCM_STATE_PREPARED) { -		snd_pcm_sw_params_t *swparams; -		snd_pcm_uframes_t threshold; -	 -		snd_pcm_sw_params_malloc(&swparams); -		if(!snd_pcm_sw_params_current(io->pcm, swparams) -		   && !snd_pcm_sw_params_get_start_threshold(swparams, -		       &threshold) ) { -			if(io->appl_ptr >= threshold) { -				ret = snd_pcm_start(io->pcm); -				if(ret != 0) -					goto done;			 -			} -		} -		snd_pcm_sw_params_free(swparams);	 -	} +	DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," +			"io->nonblock=%u", areas->step, areas->first, +			offset, size, io->nonblock);  	if (codesize == 0) {  		/* How much data can be encoded by sbc at a time? */ @@ -655,7 +548,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io,  	DBG("encoded = %d  a2dp.sbc.len= %d", encoded, a2dp->sbc.len);  	if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) { -		ret = avdtp_write(data); +		ret = avdtp_write(data, io->nonblock);  		if (ret < 0) {  			if (-ret == EPIPE)  				ret = -EIO; @@ -668,60 +561,55 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io,  	a2dp->frame_count++;  	a2dp->samples += encoded / frame_size;  	a2dp->nsamples += encoded / frame_size; +	/* Increment hardware transmition pointer */ +	data->hw_ptr = (data->hw_ptr + encoded / frame_size) +			% io->buffer_size;  	ret = frames_to_read;  done: -	DBG("returning %ld", ret); +	DBG("returning %lu", ret);  	return ret;  }  static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { -	.start			= bluetooth_start, -	.stop			= bluetooth_stop, -	.pointer		= bluetooth_pointer, -	.close			= bluetooth_close, -	.hw_params		= bluetooth_hsp_hw_params, -	.prepare		= bluetooth_prepare, -	.transfer		= bluetooth_hsp_write, -	.poll_descriptors	= bluetooth_poll_descriptors, -	.poll_revents		= bluetooth_poll_revents, +	.start		= bluetooth_start, +	.stop		= bluetooth_stop, +	.pointer	= bluetooth_pointer, +	.close		= bluetooth_close, +	.hw_params	= bluetooth_hsp_hw_params, +	.prepare	= bluetooth_prepare, +	.transfer	= bluetooth_hsp_write,  };  static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { -	.start			= bluetooth_start, -	.stop			= bluetooth_stop, -	.pointer		= bluetooth_pointer, -	.close			= bluetooth_close, -	.hw_params		= bluetooth_hsp_hw_params, -	.prepare		= bluetooth_prepare, -	.transfer		= bluetooth_hsp_read, -	.poll_descriptors	= bluetooth_poll_descriptors, -	.poll_revents		= bluetooth_poll_revents, +	.start		= bluetooth_start, +	.stop		= bluetooth_stop, +	.pointer	= bluetooth_pointer, +	.close		= bluetooth_close, +	.hw_params	= bluetooth_hsp_hw_params, +	.prepare	= bluetooth_prepare, +	.transfer	= bluetooth_hsp_read,  };  static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { -	.start			= bluetooth_a2dp_playback_start, -	.stop			= bluetooth_a2dp_playback_stop, -	.pointer		= bluetooth_pointer, -	.close			= bluetooth_close, -	.hw_params		= bluetooth_a2dp_hw_params, -	.prepare		= bluetooth_prepare, -	.transfer		= bluetooth_a2dp_write, -	.poll_descriptors	= bluetooth_a2dp_playback_poll_descriptors, -	.poll_revents		= bluetooth_a2dp_playback_poll_revents, +	.start		= bluetooth_start, +	.stop		= bluetooth_stop, +	.pointer	= bluetooth_pointer, +	.close		= bluetooth_close, +	.hw_params	= bluetooth_a2dp_hw_params, +	.prepare	= bluetooth_prepare, +	.transfer	= bluetooth_a2dp_write,  };  static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { -	.start			= bluetooth_start, -	.stop			= bluetooth_stop, -	.pointer		= bluetooth_pointer, -	.close			= bluetooth_close, -	.hw_params		= bluetooth_a2dp_hw_params, -	.prepare		= bluetooth_prepare, -	.transfer		= bluetooth_a2dp_read, -	.poll_descriptors	= bluetooth_poll_descriptors, -	.poll_revents		= bluetooth_poll_revents, +	.start		= bluetooth_start, +	.stop		= bluetooth_stop, +	.pointer	= bluetooth_pointer, +	.close		= bluetooth_close, +	.hw_params	= bluetooth_a2dp_hw_params, +	.prepare	= bluetooth_prepare, +	.transfer	= bluetooth_a2dp_read,  };  #define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) @@ -773,7 +661,7 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io)  		return err;  	err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, -					2, 50); +					2, 200);  	if (err < 0)  		return err; @@ -790,13 +678,13 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data)  		.iov_len  = sizeof(pkt)          };  	struct msghdr msgh = { -		.msg_name	= 0, -		.msg_namelen	= 0, -		.msg_iov	= &iov, -		.msg_iovlen	= 1, -		.msg_control	= &cmsg_b, -		.msg_controllen	= CMSG_LEN(sizeof(int)), -		.msg_flags	= 0 +		.msg_name       = 0, +		.msg_namelen    = 0, +		.msg_iov        = &iov, +		.msg_iovlen     = 1, +		.msg_control    = &cmsg_b, +		.msg_controllen = CMSG_LEN(sizeof(int)), +		.msg_flags      = 0  	};  	ret = recvmsg(data->sock, &msgh, 0); @@ -850,14 +738,6 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data,  	a2dp->sbc.blocks = sbc->blocks;  	a2dp->sbc.bitpool = sbc->bitpool; - -	if(pipe(a2dp->pipefd) != 0) -		return -errno; -	if(fcntl(a2dp->pipefd[0], F_SETFL, O_NONBLOCK) != 0) -		return -errno;	 -	if(fcntl(a2dp->pipefd[1], F_SETFL, O_NONBLOCK) != 0) -		return -errno; -  	return 0;  } @@ -1008,7 +888,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)  	DBG("Bluetooth PCM plugin (%s)",  		stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); -	data = calloc(1, sizeof(struct bluetooth_data)); +	data = malloc(sizeof(struct bluetooth_data));  	if (!data) {  		err = -ENOMEM;  		goto error; @@ -1021,6 +901,9 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)  	data->io.version = SND_PCM_IOPLUG_VERSION;  	data->io.name = "Bluetooth Audio Device";  	data->io.mmap_rw = 0; /* No direct mmap communication */ +	data->io.poll_fd = data->stream_fd; +	data->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? +					POLLOUT : POLLIN;  	data->io.private_data = data;  	if (data->cfg.codec == CFG_CODEC_SBC) | 
