Sound: Recover broken audio connection by closing and reopening the device

This commit is contained in:
Andreas Eversberg 2020-07-26 13:13:01 +02:00
parent ed72e29dbb
commit 8929b332cb
2 changed files with 75 additions and 51 deletions

View File

@ -347,7 +347,7 @@ void process_sender_audio(sender_t *sender, int *quit, int latspl)
#endif #endif
count = sender->audio_get_tosend(sender->audio, latspl); count = sender->audio_get_tosend(sender->audio, latspl);
if (count < 0) { if (count < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to get number of samples in buffer (rc = %d)!\n", count); PDEBUG_CHAN(DSENDER, DEBUG_ERROR, "Failed to get number of samples in buffer (rc = %d)!\n", count);
if (count == -EPIPE) { if (count == -EPIPE) {
if (cant_recover) { if (cant_recover) {
cant_recover: cant_recover:

View File

@ -28,6 +28,9 @@
typedef struct sound { typedef struct sound {
snd_pcm_t *phandle, *chandle; snd_pcm_t *phandle, *chandle;
int pchannels, cchannels; int pchannels, cchannels;
int channels; /* required number of channels */
int samplerate; /* required sample rate */
char *audiodev; /* required device */
double spl_deviation; /* how much deviation is one sample step */ double spl_deviation; /* how much deviation is one sample step */
double paging_phaseshift; /* phase to shift every sample */ double paging_phaseshift; /* phase to shift every sample */
double paging_phase; /* current phase */ double paging_phase; /* current phase */
@ -112,9 +115,48 @@ error:
return rc; return rc;
} }
static int sound_prepare(sound_t *sound) static int dev_open(sound_t *sound)
{ {
int rc; int rc, rc_rec, rc_play;
rc_play = snd_pcm_open(&sound->phandle, sound->audiodev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
rc_rec = snd_pcm_open(&sound->chandle, sound->audiodev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if (rc_play < 0 && rc_rec < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s'! (%s)\n", sound->audiodev, snd_strerror(rc_play));
PDEBUG(DSOUND, DEBUG_ERROR, "Run 'aplay -l' to get a list of available cards and devices.\n");
PDEBUG(DSOUND, DEBUG_ERROR, "Then use 'hw:<card>:<device>' for audio device.\n");
return rc_play;
}
if (rc_play < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for playback! (%s) Please select a device that supports both direction audio.\n", sound->audiodev, snd_strerror(rc_play));
return rc_play;
}
if (rc_rec < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for capture! (%s) Please select a device that supports both direction audio.\n", sound->audiodev, snd_strerror(rc_rec));
return rc_rec;
}
rc = set_hw_params(sound->phandle, sound->samplerate, &sound->pchannels);
if (rc < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to set playback hw params\n");
return rc;
}
if (sound->pchannels < sound->channels) {
PDEBUG(DSOUND, DEBUG_ERROR, "Sound card only supports %d channel for playback.\n", sound->pchannels);
return rc;
}
PDEBUG(DSOUND, DEBUG_DEBUG, "Playback with %d channels.\n", sound->pchannels);
rc = set_hw_params(sound->chandle, sound->samplerate, &sound->cchannels);
if (rc < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to set capture hw params\n");
return rc;
}
if (sound->cchannels < sound->channels) {
PDEBUG(DSOUND, DEBUG_ERROR, "Sound card only supports %d channel for capture.\n", sound->cchannels);
return -EIO;
}
PDEBUG(DSOUND, DEBUG_DEBUG, "Capture with %d channels.\n", sound->cchannels);
rc = snd_pcm_prepare(sound->phandle); rc = snd_pcm_prepare(sound->phandle);
if (rc < 0) { if (rc < 0) {
@ -131,10 +173,18 @@ static int sound_prepare(sound_t *sound)
return 0; return 0;
} }
static void dev_close(sound_t *sound)
{
if (sound->phandle != NULL)
snd_pcm_close(sound->phandle);
if (sound->chandle != NULL)
snd_pcm_close(sound->chandle);
}
void *sound_open(const char *audiodev, double __attribute__((unused)) *tx_frequency, double __attribute__((unused)) *rx_frequency, int __attribute__((unused)) *am, int channels, double __attribute__((unused)) paging_frequency, int samplerate, int __attribute((unused)) latspl, double max_deviation, double __attribute__((unused)) max_modulation, double __attribute__((unused)) modulation_index) void *sound_open(const char *audiodev, double __attribute__((unused)) *tx_frequency, double __attribute__((unused)) *rx_frequency, int __attribute__((unused)) *am, int channels, double __attribute__((unused)) paging_frequency, int samplerate, int __attribute((unused)) latspl, double max_deviation, double __attribute__((unused)) max_modulation, double __attribute__((unused)) modulation_index)
{ {
sound_t *sound; sound_t *sound;
int rc, rc_rec, rc_play; int rc;
if (channels < 1 || channels > 2) { if (channels < 1 || channels > 2) {
PDEBUG(DSOUND, DEBUG_ERROR, "Cannot use more than two channels with the same sound card!\n"); PDEBUG(DSOUND, DEBUG_ERROR, "Cannot use more than two channels with the same sound card!\n");
@ -147,49 +197,13 @@ void *sound_open(const char *audiodev, double __attribute__((unused)) *tx_freque
return NULL; return NULL;
} }
sound->audiodev = strdup(audiodev);
sound->channels = channels;
sound->samplerate = samplerate;
sound->spl_deviation = max_deviation / 32767.0; sound->spl_deviation = max_deviation / 32767.0;
sound->paging_phaseshift = 1.0 / ((double)samplerate / 1000.0); sound->paging_phaseshift = 1.0 / ((double)samplerate / 1000.0);
rc_play = snd_pcm_open(&sound->phandle, audiodev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); rc = dev_open(sound);
rc_rec = snd_pcm_open(&sound->chandle, audiodev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if (rc_play < 0 && rc_rec < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s'! (%s)\n", audiodev, snd_strerror(rc_play));
PDEBUG(DSOUND, DEBUG_ERROR, "Run 'aplay -l' to get a list of available cards and devices.\n");
PDEBUG(DSOUND, DEBUG_ERROR, "Then use 'hw:<card>:<device>' for audio device.\n");
goto error;
}
if (rc_play < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for playback! (%s) Please select a device that supports both direction audio.\n", audiodev, snd_strerror(rc_play));
goto error;
}
if (rc_rec < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for capture! (%s) Please select a device that supports both direction audio.\n", audiodev, snd_strerror(rc_rec));
goto error;
}
rc = set_hw_params(sound->phandle, samplerate, &sound->pchannels);
if (rc < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to set playback hw params\n");
goto error;
}
if (sound->pchannels < channels) {
PDEBUG(DSOUND, DEBUG_ERROR, "Sound card only supports %d channel for playback.\n", sound->pchannels);
goto error;
}
PDEBUG(DSOUND, DEBUG_DEBUG, "Playback with %d channels.\n", sound->pchannels);
rc = set_hw_params(sound->chandle, samplerate, &sound->cchannels);
if (rc < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to set capture hw params\n");
goto error;
}
if (sound->cchannels < channels) {
PDEBUG(DSOUND, DEBUG_ERROR, "Sound card only supports %d channel for capture.\n", sound->cchannels);
goto error;
}
PDEBUG(DSOUND, DEBUG_DEBUG, "Capture with %d channels.\n", sound->cchannels);
rc = sound_prepare(sound);
if (rc < 0) if (rc < 0)
goto error; goto error;
@ -228,10 +242,8 @@ void sound_close(void *inst)
{ {
sound_t *sound = (sound_t *)inst; sound_t *sound = (sound_t *)inst;
if (sound->phandle != NULL) dev_close(sound);
snd_pcm_close(sound->phandle); free(sound->audiodev);
if (sound->chandle != NULL)
snd_pcm_close(sound->chandle);
free(sound); free(sound);
} }
@ -344,8 +356,12 @@ int sound_write(void *inst, sample_t **samples, uint8_t __attribute__((unused))
if (rc < 0) { if (rc < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "failed to write audio to interface (%s)\n", snd_strerror(rc)); PDEBUG(DSOUND, DEBUG_ERROR, "failed to write audio to interface (%s)\n", snd_strerror(rc));
if (rc == -EPIPE) { if (rc == -EPIPE) {
sound_prepare(sound); dev_close(sound);
rc = dev_open(sound);
if (rc < 0)
return rc;
sound_start(sound); sound_start(sound);
return -EPIPE; /* indicate what happened */
} }
return rc; return rc;
} }
@ -390,8 +406,12 @@ int sound_read(void *inst, sample_t **samples, int num, int channels, double *rf
PDEBUG(DSOUND, DEBUG_ERROR, "failed to read audio from interface (%s)\n", snd_strerror(rc)); PDEBUG(DSOUND, DEBUG_ERROR, "failed to read audio from interface (%s)\n", snd_strerror(rc));
/* recover read */ /* recover read */
if (rc == -EPIPE) { if (rc == -EPIPE) {
sound_prepare(sound); dev_close(sound);
rc = dev_open(sound);
if (rc < 0)
return rc;
sound_start(sound); sound_start(sound);
return -EPIPE; /* indicate what happened */
} }
return rc; return rc;
} }
@ -464,8 +484,12 @@ int sound_get_tosend(void *inst, int latspl)
else else
PDEBUG(DSOUND, DEBUG_ERROR, "failed to get delay from interface (%s)\n", snd_strerror(rc)); PDEBUG(DSOUND, DEBUG_ERROR, "failed to get delay from interface (%s)\n", snd_strerror(rc));
if (rc == -EPIPE) { if (rc == -EPIPE) {
sound_prepare(sound); dev_close(sound);
rc = dev_open(sound);
if (rc < 0)
return rc;
sound_start(sound); sound_start(sound);
return -EPIPE; /* indicate what happened */
} }
return rc; return rc;
} }