diff options
Diffstat (limited to 'audio/pcm_bluetooth.c')
-rw-r--r-- | audio/pcm_bluetooth.c | 166 |
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); |