diff options
Diffstat (limited to 'audio/out/ao_alsa.c')
-rw-r--r-- | audio/out/ao_alsa.c | 78 |
1 files changed, 69 insertions, 9 deletions
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c index 9ffd4792ee..d09f5fc499 100644 --- a/audio/out/ao_alsa.c +++ b/audio/out/ao_alsa.c @@ -58,6 +58,8 @@ struct priv { snd_pcm_uframes_t buffersize; snd_pcm_uframes_t outburst; + snd_output_t *output; + char *cfg_device; char *cfg_mixer_device; char *cfg_mixer_name; @@ -477,6 +479,22 @@ static int set_chmap(struct ao *ao, struct mp_chmap *dev_chmap, int num_channels #endif /* else HAVE_CHMAP_API */ +static void dump_hw_params(struct ao *ao, int msglevel, const char *msg, + snd_pcm_hw_params_t *hw_params) +{ + struct priv *p = ao->priv; + int err; + + err = snd_pcm_hw_params_dump(hw_params, p->output); + CHECK_ALSA_WARN("Dump hwparams error"); + + char *tmp = NULL; + size_t tmp_s = snd_output_buffer_string(p->output, &tmp); + if (tmp) + mp_msg(ao->log, msglevel, "%s---\n%.*s---\n", msg, (int)tmp_s, tmp); + snd_output_flush(p->output); +} + static int map_iec958_srate(int srate) { switch (srate) { @@ -520,7 +538,7 @@ static char *append_params(void *ta_parent, const char *device, const char *p) abort(); } -static int try_open_device(struct ao *ao, const char *device) +static int try_open_device(struct ao *ao, const char *device, int mode) { struct priv *p = ao->priv; int err; @@ -534,7 +552,7 @@ static int try_open_device(struct ao *ao, const char *device) map_iec958_srate(ao->samplerate)); const char *ac3_device = append_params(tmp, device, params); MP_VERBOSE(ao, "opening device '%s' => '%s'\n", device, ac3_device); - err = snd_pcm_open(&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, 0); + err = snd_pcm_open(&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, mode); if (err < 0) { // Some spdif-capable devices do not accept the AES0 parameter, // and instead require the iec958 pseudo-device (they will play @@ -547,13 +565,13 @@ static int try_open_device(struct ao *ao, const char *device) MP_VERBOSE(ao, "got error %d; opening iec fallback device '%s'\n", err, ac3_device); err = snd_pcm_open - (&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, 0); + (&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, mode); } } talloc_free(tmp); } else { MP_VERBOSE(ao, "opening device '%s'\n", device); - err = snd_pcm_open(&p->alsa, device, SND_PCM_STREAM_PLAYBACK, 0); + err = snd_pcm_open(&p->alsa, device, SND_PCM_STREAM_PLAYBACK, mode); } return err; @@ -563,6 +581,10 @@ static void uninit(struct ao *ao) { struct priv *p = ao->priv; + if (p->output) + snd_output_close(p->output); + p->output = NULL; + if (p->alsa) { int err; @@ -574,20 +596,35 @@ static void uninit(struct ao *ao) alsa_error: ; } -static int init_device(struct ao *ao) +#define INIT_DEVICE_ERR_GENERIC -1 +#define INIT_DEVICE_ERR_HWPARAMS -2 +static int init_device(struct ao *ao, int mode) { struct priv *p = ao->priv; + int ret = INIT_DEVICE_ERR_GENERIC; + char *tmp; + size_t tmp_s; int err; + err = snd_output_buffer_open(&p->output); + CHECK_ALSA_ERROR("Unable to create output buffer"); + const char *device = "default"; if (ao->device) device = ao->device; if (p->cfg_device && p->cfg_device[0]) device = p->cfg_device; - err = try_open_device(ao, device); + err = try_open_device(ao, device, mode); CHECK_ALSA_ERROR("Playback open error"); + err = snd_pcm_dump(p->alsa, p->output); + CHECK_ALSA_WARN("Dump PCM error"); + tmp_s = snd_output_buffer_string(p->output, &tmp); + if (tmp) + MP_DBG(ao, "PCM setup:\n---\n%.*s---\n", (int)tmp_s, tmp); + snd_output_flush(p->output); + err = snd_pcm_nonblock(p->alsa, 0); CHECK_ALSA_WARN("Unable to set blocking mode"); @@ -597,12 +634,15 @@ static int init_device(struct ao *ao) err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams); CHECK_ALSA_ERROR("Unable to get initial parameters"); + dump_hw_params(ao, MSGL_DEBUG, "Start HW params:\n", alsa_hwparams); + // Some ALSA drivers have broken delay reporting, so disable the ALSA // resampling plugin by default. if (!p->cfg_resample) { err = snd_pcm_hw_params_set_rate_resample(p->alsa, alsa_hwparams, 0); CHECK_ALSA_ERROR("Unable to disable resampling"); } + dump_hw_params(ao, MSGL_DEBUG, "HW params after rate:\n", alsa_hwparams); snd_pcm_access_t access = af_fmt_is_planar(ao->format) ? SND_PCM_ACCESS_RW_NONINTERLEAVED @@ -614,6 +654,7 @@ static int init_device(struct ao *ao) err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access); } CHECK_ALSA_ERROR("Unable to set access type"); + dump_hw_params(ao, MSGL_DEBUG, "HW params after access:\n", alsa_hwparams); bool found_format = false; int try_formats[AF_FORMAT_COUNT]; @@ -637,6 +678,7 @@ static int init_device(struct ao *ao) err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt); CHECK_ALSA_ERROR("Unable to set format"); + dump_hw_params(ao, MSGL_DEBUG, "HW params after format:\n", alsa_hwparams); struct mp_chmap dev_chmap = ao->channels; if (af_fmt_is_spdif(ao->format) || p->cfg_ignore_chmap) { @@ -656,6 +698,7 @@ static int init_device(struct ao *ao) err = snd_pcm_hw_params_set_channels_near (p->alsa, alsa_hwparams, &num_channels); CHECK_ALSA_ERROR("Unable to set channels"); + dump_hw_params(ao, MSGL_DEBUG, "HW params after channels:\n", alsa_hwparams); if (num_channels > MP_NUM_CHANNELS) { MP_FATAL(ao, "Too many audio channels (%d).\n", num_channels); @@ -665,6 +708,7 @@ static int init_device(struct ao *ao) err = snd_pcm_hw_params_set_rate_near (p->alsa, alsa_hwparams, &ao->samplerate, NULL); CHECK_ALSA_ERROR("Unable to set samplerate-2"); + dump_hw_params(ao, MSGL_DEBUG, "HW params after rate-2:\n", alsa_hwparams); snd_pcm_hw_params_t *hwparams_backup; snd_pcm_hw_params_alloca(&hwparams_backup); @@ -682,9 +726,14 @@ static int init_device(struct ao *ao) if (err < 0) snd_pcm_hw_params_copy(alsa_hwparams, hwparams_backup); + dump_hw_params(ao, MSGL_V, "Going to set final HW params:\n", alsa_hwparams); + /* finally install hardware parameters */ err = snd_pcm_hw_params(p->alsa, alsa_hwparams); + ret = INIT_DEVICE_ERR_HWPARAMS; CHECK_ALSA_ERROR("Unable to set hw-parameters"); + ret = INIT_DEVICE_ERR_GENERIC; + dump_hw_params(ao, MSGL_DEBUG, "Final HW params:\n", alsa_hwparams); if (set_chmap(ao, &dev_chmap, num_channels) < 0) goto alsa_error; @@ -740,7 +789,7 @@ static int init_device(struct ao *ao) alsa_error: uninit(ao); - return -1; + return ret; } static int init(struct ao *ao) @@ -751,7 +800,18 @@ static int init(struct ao *ao) MP_VERBOSE(ao, "using ALSA version: %s\n", snd_asoundlib_version()); - int r = init_device(ao); + int mode = 0; + int r = init_device(ao, mode); + if (r == INIT_DEVICE_ERR_HWPARAMS) { + // With some drivers, ALSA appears to be unable to set valid hwparams, + // but they work if at least SND_PCM_NO_AUTO_FORMAT is set. Also, it + // appears you can set this flag only on opening a device, thus there + // is the need to retry opening the device. + MP_WARN(ao, "Attempting to work around even more ALSA bugs...\n"); + mode |= SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT | + SND_PCM_NO_AUTO_RESAMPLE; + r = init_device(ao, mode); + } // Sometimes, ALSA will advertise certain chmaps, but it's not possible to // set them. This can happen with dmix: as of alsa 1.0.29, dmix can do @@ -773,7 +833,7 @@ static int init(struct ao *ao) MP_VERBOSE(ao, "Working around braindead dmix multichannel behavior.\n"); uninit(ao); ao->channels = without_na; - r = init_device(ao); + r = init_device(ao, mode); } } |