ALSA driver improvements:

* alsa hotplug thread is low priority
* give a chance for other threads to catch up when audio playback is not progressing
* use nonblocking for alsa audio capture
  There is a bug with SDL hanging when an audio capture USB device is removed, because poll never returns
Sam Lantinga 2017-03-14 07:20:14 -07:00
parent c4d54504fa
commit 6814f5dbc0
1 changed files with 49 additions and 25 deletions

View File

@ -364,6 +364,13 @@ ALSA_PlayDevice(_THIS)
}
continue;
}
else if (status == 0) {
/* No frames were written (no available space in pcm device).
Allow other threads to catch up. */
Uint32 delay = (frames_left / 2 * 1000) / this->spec.freq;
SDL_Delay(delay);
}
sample_buf += status * frame_size;
frames_left -= status;
}
@ -383,23 +390,22 @@ ALSA_CaptureFromDevice(_THIS, void *buffer, int buflen)
this->spec.channels;
const int total_frames = buflen / frame_size;
snd_pcm_uframes_t frames_left = total_frames;
snd_pcm_uframes_t wait_time = frame_size / 2;
SDL_assert((buflen % frame_size) == 0);
while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) {
/* !!! FIXME: This works, but needs more testing before going live */
/* ALSA_snd_pcm_wait(this->hidden->pcm_handle, -1); */
int status = ALSA_snd_pcm_readi(this->hidden->pcm_handle,
int status;
status = ALSA_snd_pcm_readi(this->hidden->pcm_handle,
sample_buf, frames_left);
if (status < 0) {
/*printf("ALSA: capture error %d\n", status);*/
if (status == -EAGAIN) {
/* Apparently snd_pcm_recover() doesn't handle this case -
does it assume snd_pcm_wait() above? */
SDL_Delay(1);
continue;
ALSA_snd_pcm_wait(this->hidden->pcm_handle, wait_time);
status = 0;
}
else if (status < 0) {
/*printf("ALSA: capture error %d\n", status);*/
status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
if (status < 0) {
/* Hmm, not much we can do - abort */
@ -745,8 +751,9 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
}
/* Switch to blocking mode for playback */
if (!iscapture) {
ALSA_snd_pcm_nonblock(pcm_handle, 0);
}
/* We're ready to rock and roll. :-) */
return 0;
@ -763,16 +770,26 @@ static void
add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen)
{
ALSA_Device *dev = SDL_malloc(sizeof (ALSA_Device));
char *desc = ALSA_snd_device_name_get_hint(hint, "DESC");
char *desc;
char *handle = NULL;
char *ptr;
if (!dev) {
return;
}
/* Not all alsa devices are enumerable via snd_device_name_get_hint
(i.e. bluetooth devices). Therefore if hint is passed in to this
function as NULL, assume name contains desc.
Make sure not to free the storage associated with desc in this case */
if (hint) {
desc = ALSA_snd_device_name_get_hint(hint, "DESC");
if (!desc) {
SDL_free(dev);
return;
} else if (!dev) {
free(desc);
return;
}
} else {
desc = (char *) name;
}
SDL_assert(name != NULL);
@ -788,14 +805,16 @@ add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSee
handle = SDL_strdup(name);
if (!handle) {
if (hint) {
free(desc);
}
SDL_free(dev);
return;
}
SDL_AddAudioDevice(iscapture, desc, handle);
if (hint)
free(desc);
dev->name = handle;
dev->iscapture = iscapture;
dev->next = *pSeen;
@ -815,12 +834,15 @@ ALSA_HotplugThread(void *arg)
ALSA_Device *dev;
Uint32 ticks;
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
void **hints = NULL;
if (ALSA_snd_device_name_hint(-1, "pcm", &hints) != -1) {
ALSA_Device *unseen = devices;
ALSA_Device *seen = NULL;
ALSA_Device *unseen;
ALSA_Device *seen;
ALSA_Device *prev;
if (ALSA_snd_device_name_hint(-1, "pcm", &hints) != -1) {
int i, j;
const char *match = NULL;
int bestmatch = 0xFFFF;
@ -830,6 +852,8 @@ ALSA_HotplugThread(void *arg)
"hw:", "sysdefault:", "default:", NULL
};
unseen = devices;
seen = NULL;
/* Apparently there are several different ways that ALSA lists
actual hardware. It could be prefixed with "hw:" or "default:"
or "sysdefault:" and maybe others. Go through the list and see
@ -924,7 +948,7 @@ ALSA_HotplugThread(void *arg)
/* report anything still in unseen as removed. */
for (dev = unseen; dev; dev = next) {
/*printf("ALSA: removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
/*printf("ALSA: removing usb %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
next = dev->next;
SDL_RemoveAudioDevice(dev->iscapture, dev->name);
SDL_free(dev->name);