summaryrefslogtreecommitdiffstats
path: root/audio/pcm_bluetooth.c
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2007-11-09 08:45:57 +0000
committerJohan Hedberg <johan.hedberg@nokia.com>2007-11-09 08:45:57 +0000
commitdff420207b69768a305ed014f7c7648d027d6b38 (patch)
treee0e60a8c10350f87be8208b71a43d8571eeb495f /audio/pcm_bluetooth.c
parentf4051e4e1bc3a5a00fc74b5b5eb7eb3794f67310 (diff)
A2DP wakeup refactoring & cleanup (patch from Kai Vehmanen <kai.vehmanen@nokia.com>)
Diffstat (limited to 'audio/pcm_bluetooth.c')
-rw-r--r--audio/pcm_bluetooth.c166
1 files changed, 102 insertions, 64 deletions
diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c
index 6f9bbe0b..b3c05cf8 100644
--- a/audio/pcm_bluetooth.c
+++ b/audio/pcm_bluetooth.c
@@ -30,6 +30,7 @@
#include <sys/un.h>
#include <sys/time.h>
#include <pthread.h>
+#include <signal.h>
#include <netinet/in.h>
@@ -91,6 +92,7 @@ struct bluetooth_data {
pthread_t hw_thread; /* Makes virtual hw pointer move */
int pipefd[2]; /* Inter thread communication */
int stopped;
+ sig_atomic_t reset; /* Request XRUN handling */
};
static int bluetooth_start(snd_pcm_ioplug_t *io)
@@ -116,6 +118,9 @@ static void *playback_hw_thread(void *param)
struct pollfd fds[2];
int poll_timeout;
+ data->server.events = POLLIN;
+ /* note: only errors for data->stream.events */
+
fds[0] = data->server;
fds[1] = data->stream;
@@ -136,6 +141,13 @@ static void *playback_hw_thread(void *param)
if (data->stopped)
goto iter_sleep;
+ if (data->reset) {
+ DBG("Handle XRUN in hw-thread.");
+ data->reset = 0;
+ gettimeofday(&start, 0);
+ prev_periods = 0;
+ }
+
gettimeofday(&cur, 0);
timersub(&cur, &start, &delta);
@@ -145,14 +157,17 @@ static void *playback_hw_thread(void *param)
if (periods > prev_periods) {
char c = 'w';
+ int frags = periods - prev_periods, n;
- data->hw_ptr += (periods - prev_periods) *
- data->io.period_size;
+ data->hw_ptr += frags * data->io.period_size;
data->hw_ptr %= data->io.buffer_size;
- /* Notify user that hardware pointer has moved */
- if (write(data->pipefd[1], &c, 1) < 0)
- pthread_testcancel();
+ for (n = 0; n < frags; n++) {
+ /* Notify user that hardware pointer
+ * has moved * */
+ if (write(data->pipefd[1], &c, 1) < 0)
+ pthread_testcancel();
+ }
/* Reset point of reference to avoid too big values
* that wont fit an unsigned int */
@@ -167,6 +182,7 @@ static void *playback_hw_thread(void *param)
iter_sleep:
/* sleep up to one period interval */
ret = poll(fds, 2, poll_timeout);
+
if (ret < 0) {
SNDERR("poll error: %s (%d)", strerror(errno), errno);
if (errno != EINTR)
@@ -329,6 +345,8 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io)
DBG("Preparing with io->period_size=%lu io->buffer_size=%lu",
io->period_size, io->buffer_size);
+ data->reset = 0;
+
if (io->stream == SND_PCM_STREAM_PLAYBACK)
/* If not null for playback, xmms doesn't display time
* correctly */
@@ -416,7 +434,7 @@ static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io,
static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED,
struct pollfd *pfds, unsigned int nfds,
- unsigned short *revents)
+ unsigned short *revents)
{
assert(pfds && nfds == 1 && revents);
@@ -446,7 +464,7 @@ static int bluetooth_playback_poll_descriptors(snd_pcm_ioplug_t *io,
static int bluetooth_playback_poll_revents(snd_pcm_ioplug_t *io,
struct pollfd *pfds, unsigned int nfds,
- unsigned short *revents)
+ unsigned short *revents)
{
static char buf[1];
int ret;
@@ -469,7 +487,8 @@ static int bluetooth_playback_poll_revents(snd_pcm_ioplug_t *io,
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, snd_pcm_uframes_t size)
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
{
struct bluetooth_data *data = io->private_data;
struct ipc_data_cfg cfg = data->cfg;
@@ -526,7 +545,8 @@ done:
static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io,
const snd_pcm_channel_area_t *areas,
- snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
{
struct bluetooth_data *data = io->private_data;
struct ipc_data_cfg cfg = data->cfg;
@@ -584,7 +604,8 @@ done:
static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io,
const snd_pcm_channel_area_t *areas,
- snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
{
snd_pcm_uframes_t ret = 0;
return ret;
@@ -609,9 +630,11 @@ 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 = send(data->stream.fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT);
+ if (ret < 0) {
+ DBG("send returned %d errno %s.", ret, strerror(errno));
ret = -errno;
+ }
/* Reset buffer of data to send */
a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
@@ -629,18 +652,20 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io,
struct bluetooth_data *data = io->private_data;
struct bluetooth_a2dp *a2dp = &data->a2dp;
snd_pcm_sframes_t ret = 0;
- snd_pcm_uframes_t frames_to_read;
+ snd_pcm_uframes_t frames_to_read, frames_left = size;
int frame_size, encoded;
uint8_t *buff;
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);
+ DBG("hw_ptr=%lu appl_ptr=%lu diff=%lu", io->hw_ptr, io->appl_ptr,
+ io->appl_ptr - io->hw_ptr);
if (io->hw_ptr > io->appl_ptr) {
ret = bluetooth_playback_stop(io);
if (ret == 0)
ret = -EPIPE;
+ data->reset = 1;
goto done;
}
@@ -651,66 +676,76 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io,
snd_pcm_sw_params_malloc(&swparams);
if (!snd_pcm_sw_params_current(io->pcm, swparams) &&
- !snd_pcm_sw_params_get_start_threshold(swparams, &threshold)) {
+ !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);
}
- frame_size = areas->step / 8;
- if ((data->count + size * frame_size) <= a2dp->codesize)
- frames_to_read = size;
- else
- frames_to_read = (a2dp->codesize - data->count) / frame_size;
+ while (frames_left > 0) {
+ frame_size = areas->step / 8;
- DBG("count=%d frames_to_read=%lu", data->count, frames_to_read);
- DBG("a2dp.count=%d cfg.pkt_len=%d", a2dp->count, data->cfg.pkt_len);
+ if ((data->count + frames_left * frame_size) <= a2dp->codesize)
+ frames_to_read = frames_left;
+ else
+ frames_to_read = (a2dp->codesize - data->count) / frame_size;
- /* FIXME: If state is not streaming then return */
+ DBG("count=%d frames_to_read=%lu", data->count, frames_to_read);
+ DBG("a2dp.count=%d cfg.pkt_len=%d", a2dp->count, data->cfg.pkt_len);
- /* Ready for more data */
- buff = (uint8_t *) areas->addr +
- (areas->first + areas->step * offset) / 8;
- memcpy(data->buffer + data->count, buff, frame_size * frames_to_read);
+ /* FIXME: If state is not streaming then return */
- /* Remember we have some frames in the pipe now */
- data->count += frames_to_read * frame_size;
- if (data->count != a2dp->codesize) {
- ret = frames_to_read;
- goto done;
- }
+ /* Ready for more data */
+ buff = (uint8_t *) areas->addr +
+ (areas->first + areas->step * (offset + ret)) / 8;
+ memcpy(data->buffer + data->count, buff,
+ frame_size * frames_to_read);
- /* Enough data to encode (sbc wants 1k blocks) */
- encoded = sbc_encode(&(a2dp->sbc), data->buffer, a2dp->codesize);
- if (encoded <= 0) {
- DBG("Encoding error %d", encoded);
- goto done;
- }
+ /* Remember we have some frames in the pipe now */
+ data->count += frames_to_read * frame_size;
+ if (data->count != a2dp->codesize) {
+ ret = frames_to_read;
+ goto done;
+ }
- data->count -= encoded;
+ /* Enough data to encode (sbc wants 1k blocks) */
+ encoded = sbc_encode(&(a2dp->sbc), data->buffer, a2dp->codesize);
+ if (encoded <= 0) {
+ DBG("Encoding error %d", encoded);
+ goto done;
+ }
- DBG("encoded=%d a2dp.sbc.len=%d", encoded, a2dp->sbc.len);
+ data->count -= encoded;
- if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) {
- ret = avdtp_write(data);
- if (ret < 0) {
- if (-ret == EPIPE)
- ret = -EIO;
- goto done;
+ DBG("encoded=%d a2dp.sbc.len=%d count=%d", encoded,
+ a2dp->sbc.len, a2dp->count);
+
+ /* Send previously encoded buffer */
+ if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) {
+ avdtp_write(data);
+ DBG("sending packet %d, count %d, pkt_len %u", c,
+ old_count, data->cfg.pkt_len);
}
- }
- memcpy(a2dp->buffer + a2dp->count, a2dp->sbc.data, a2dp->sbc.len);
- a2dp->count += a2dp->sbc.len;
- a2dp->frame_count++;
- a2dp->samples += encoded / frame_size;
- a2dp->nsamples += encoded / frame_size;
+ memcpy(a2dp->buffer + a2dp->count, a2dp->sbc.data, a2dp->sbc.len);
+ a2dp->count += a2dp->sbc.len;
+ a2dp->frame_count++;
+ a2dp->samples += encoded / frame_size;
+ a2dp->nsamples += encoded / frame_size;
+
+ ret += frames_to_read;
+ frames_left -= frames_to_read;
+ }
- ret = frames_to_read;
+ /* note: some ALSA apps will get confused otherwise */
+ if (ret > size)
+ ret = size;
done:
DBG("returning %ld", ret);
@@ -847,7 +882,6 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io)
static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io)
{
struct bluetooth_data *data = io->private_data;
- struct bluetooth_a2dp *a2dp = &data->a2dp;
struct ipc_data_cfg cfg = data->cfg;
snd_pcm_access_t access_list[] = {
SND_PCM_ACCESS_RW_INTERLEAVED,
@@ -859,6 +893,13 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io)
unsigned int format_list[] = {
SND_PCM_FORMAT_S16_LE
};
+ unsigned int period_list[] = {
+ 512, /* 3/6ms (mono/stereo 16bit at 44.1kHz) */
+ 1024, /* 6/12ms */
+ 2048, /* 12/23ms */
+ 4096, /* 23/46ms */
+ 8192, /* 46/93ms */
+ };
int err, channels;
/* access type */
@@ -886,18 +927,15 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io)
if (err < 0)
return err;
- /* supported block sizes:
- * - lower limit is A2DP codec size
- * - total buffer size is the upper limit (with two periods) */
- err = snd_pcm_ioplug_set_param_minmax(io,
- SND_PCM_IOPLUG_HW_PERIOD_BYTES,
- a2dp->codesize,
- a2dp->codesize);
+ /* supported block sizes: */
+ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
+ ARRAY_NELEMS(period_list), period_list);
if (err < 0)
return err;
+ /* period count fixed to 3 as we don't support prefilling */
err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS,
- 2, 50);
+ 3, 3);
if (err < 0)
return err;
@@ -1184,7 +1222,7 @@ done:
DBG("\n\tfd=%d\n\tfd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u",
data->stream.fd, data->cfg.fd_opt, data->cfg.pkt_len,
- data->cfg.sample_size, data->cfg.rate);
+ data->cfg.sample_size, data->cfg.rate);
if (data->cfg.codec == CFG_CODEC_SBC) {
ret = bluetooth_a2dp_init(data, sbc);