[alsa-devel] bluetooth headset doesn't work well(Motorola HS820)
Hi,
I have a blue-tooth headset connected to Ubuntu 8.04 but it does't work well. I've created ~/.asoundrc as below:
pcm.bluetooth { type bluetooth device 20:04:07:80:BC:89 profile voice }
ctl.bluetooth { type bluetooth }
now I can use command `aplay -Dbluetooth -v 2.wav` to play sound to the headset, but the sound is little bit wired due to inaccurate rate.
Playing WAVE '2.wav' : Signed 16 bit Little Endian, Rate 16000 Hz, Mono Warning: rate is not accurate (requested = 16000Hz, got = 8000Hz) please, try the plug plugin (-Dplug:bluetooth) Bluetooth Audio Device Its setup is: stream : PLAYBACK access : RW_INTERLEAVED format : S16_LE subformat : STD channels : 1 rate : 8000 exact rate : 8000 (8000/1) msbits : 16 buffer_size : 4000 period_size : 1000 period_time : 125000 tstamp_mode : NONE period_step : 1 avail_min : 1000 period_event : 0 start_threshold : 4000 stop_threshold : 4000 silence_threshold: 0 silence_size : 0 boundary : 2097152000
So I input command according to the aplay's recommendation `aplay -Dplug:bluetooth -v 2.wav`. The problem comes out.
aplay -Dplug:bluetooth -v 2.wav Playing WAVE '2.wav' : Signed 16 bit Little Endian, Rate 16000 Hz, Mono Plug PCM: Rate conversion PCM (8000, sformat=S16_LE) Its setup is: stream : PLAYBACK access : RW_INTERLEAVED format : S16_LE subformat : STD channels : 1 rate : 16000 exact rate : 16000 (16000/1) msbits : 16 buffer_size : 8000 period_size : 2000 period_time : 125000 tstamp_mode : NONE period_step : 1 avail_min : 2000 period_event : 0 start_threshold : 8000 stop_threshold : 8000 silence_threshold: 0 silence_size : 0 boundary : 2097152000 Slave: Bluetooth Audio Device Its setup is: stream : PLAYBACK access : MMAP_INTERLEAVED format : S16_LE subformat : STD channels : 1 rate : 8000 exact rate : 8000 (8000/1) msbits : 16 buffer_size : 4000 period_size : 1000 period_time : 125000 tstamp_mode : NONE period_step : 1 avail_min : 1000 period_event : 0 start_threshold : 4000 stop_threshold : 4000 silence_threshold: 0 silence_size : 0 boundary : 2097152000
It seems that ALSA plugs rate plugin. But I hear NOTHING from my headset and aplay seems suspended. I turn on bluez debug flag and rebuild it. I find the bluez suspending at 'bluetooth_playback_poll_descriptors'.
Sep 7 23:18:59 alex-laptop aplay: DEBUG: pcm_bluetooth.c 930 bluetooth_hsp_write: count=0 frames_to_read=24 Sep 7 23:18:59 alex-laptop aplay: DEBUG: pcm_bluetooth.c 993 bluetooth_hsp_write: returning 24 Sep 7 23:18:59 alex-laptop aplay: DEBUG: pcm_bluetooth.c 912 bluetooth_hsp_write: areas->step=16 areas->first=0 offset=0, size=1000 io->nonblock=0 io->hw_ptr=0 io->appl_ptr=0 stream.fd=8 link_mtu=48 data->count=0 Sep 7 23:18:59 alex-laptop aplay: DEBUG: pcm_bluetooth.c 930 bluetooth_hsp_write: count=0 frames_to_read=24 Sep 7 23:18:59 alex-laptop aplay: DEBUG: pcm_bluetooth.c 993 bluetooth_hsp_write: returning 24 Sep 7 23:18:59 alex-laptop aplay: DEBUG: pcm_bluetooth.c 802 bluetooth_playback_poll_descriptors: Space =2 pfd[0].fd=5 pfd[1].fd=6 Sep 7 23:19:01 alex-laptop audio[7979]: Received AT+VGS=11
Now I go deep into ALSA then I find ALSA suspending at: int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout) { struct pollfd *pfd; unsigned short *revents; int i, npfds, pollio, err, err_poll; npfds = snd_pcm_poll_descriptors_count(pcm); if (npfds <= 0 || npfds >= 16) { SNDERR("Invalid poll_fds %d\n", npfds); return -EIO; } pfd = alloca(sizeof(*pfd) * npfds); revents = alloca(sizeof(*revents) * npfds); syslog( LOG_INFO, "%s", __FUNCTION__ ); err = snd_pcm_poll_descriptors(pcm, pfd, npfds); if (err < 0) return err; if (err != npfds) { SNDMSG("invalid poll descriptors %d\n", err); return -EIO; } do { pollio = 0; err_poll = poll(pfd, npfds, timeout); <-------------
It seems the bluez doesn't feed ALSA some signals. But why? I notice 'bluetooth_playback_start' haven't been invoked. Is it the point? Then I keep tracing ALSA. I find in snd_pcm_rate_commit_area
result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, slave_size); if (result < (snd_pcm_sframes_t)slave_size) { if (result < 0) return result; result = snd_pcm_rewind(rate->gen.slave, result); <----------- if (result < 0) return result; return 0; }
The snd_pcm_mmap_commit returns with 24 which indicates bluetooth SCO link mtu(48bytes). And it causes snd_pcm_rewind being called after every package sent by bluetooth SCO link. Look at this piece of code in snd_pcm_write_areas: if (state == SND_PCM_STATE_PREPARED) { snd_pcm_sframes_t hw_avail = pcm->buffer_size - avail; hw_avail += frames; /* some plugins might automatically start the stream */ state = snd_pcm_state(pcm);
if (state == SND_PCM_STATE_PREPARED && hw_avail >= (snd_pcm_sframes_t) pcm->start_threshold) { err = snd_pcm_start(pcm); if (err < 0) goto _end; } }
the 'avail' will be equal to pcm->buffer due to snd_pcm_rewind, therefore the condition hw_avail>=pcm->start_threshold will not be satisfied forever. That's why snd_pcm_start can't be called.
I don't know the mechanism of ALSA and I just assume ALSA wanna bluez plugin sending whole period_size instead of only one 48bytes package. I revise the bluez plugin:
#if 0 // this section is original bluez frame_size = areas->step / 8; if ((data->count + size * frame_size) <= data->link_mtu) frames_to_read = size; else frames_to_read = (data->link_mtu - data->count) / frame_size;
DBG("count=%d frames_to_read=%lu", data->count, frames_to_read);
/* 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);
/* Remember we have some frames in the pipe now */ data->count += frames_to_read * frame_size; if (data->count != data->link_mtu) { ret = frames_to_read; goto done; }
rsend = send(data->stream.fd, data->buffer, data->link_mtu, io->nonblock ? MSG_DONTWAIT : 0); if (rsend > 0) { /* Reset count pointer */ data->count = 0;
ret = frames_to_read; } else if (rsend < 0) ret = (errno == EPIPE) ? -EIO : -errno; else ret = -EIO; #else frame_size = areas->step / 8;
buff = (uint8_t *) areas->addr + (areas->first + areas->step * offset) / 8;
tobesent = frame_size*size;
while( rsend<tobesent ) { if( tobesent-rsend>data->link_mtu ) sent = send(data->stream.fd, buff +counter*data->link_mtu, data->link_mtu, io->nonblock ? MSG_DONTWAIT : 0); else sent = send(data->stream.fd, buff +counter*data->link_mtu, tobesent-rsend, io->nonblock ? MSG_DONTWAIT : 0);
if( sent > 0 ) { if( sent != data->link_mtu && tobesent-rsend > data->link_mtu ) DBG("got problem!"); counter++; rsend+=sent; } else if (rsend < 0) { ret = (errno == EPIPE) ? -EIO : -errno; break; } else { ret = -EIO; break; } } ret = size; #endif
After this revision, the headset works but the performance isn't as good as playing MONO 8000Hz wav directly to headset with 'aplay -Dbluetooth 1.wav'. Any hints would be appreciated.
Regards, Galaha
participants (1)
-
Galaha Shine