summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-12-15 16:40:17 +0100
committerwm4 <wm4@nowhere>2014-12-15 16:40:23 +0100
commitae5fd4a809bcba8eae934787da01d7f801a64560 (patch)
treeb3ca798ff4ed2a59f6022369cb74c04cf780060a
parent756adee999bbc24b79d02fea34f8512239de016c (diff)
downloadmpv-ae5fd4a809bcba8eae934787da01d7f801a64560.tar.bz2
mpv-ae5fd4a809bcba8eae934787da01d7f801a64560.tar.xz
ao_alsa: add ridiculous hack to deal with braindead ALSA behavior
dmix reports channel layouts it doesn't support. The rest of the technical part of the story is in the code comment. This seems to be the only reasonable way to fallback from trying to initialize certain devices (like dmix) with multichannel audio. We could probably add support for such padding channels to our audio chain or to ao_alsa itself, but this would probably be much more work than this commit. What dmix does is probably a bug. I've tried to report it to ALSA. Thay have a link on their website to a bug tracker, but it's a dead link, and has been for years. I've posted to alsa-devel, but received no reply. I'm thus assuming this absolutely retarded behavior is by design, and nothing will happen to improve upon it. I'm considering sending Lennart Poettering a "thank you" email, because with PulseAudio, multichannel audio just works (although some other things just don't work).
-rw-r--r--audio/out/ao_alsa.c45
1 files changed, 42 insertions, 3 deletions
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index c7407b0890..af051bed4f 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -440,7 +440,10 @@ static void uninit(struct ao *ao)
alsa_error: ;
}
-static int init(struct ao *ao)
+#define INIT_OK 0
+#define INIT_ERROR -1
+#define INIT_BRAINDEATH -2
+static int init_device(struct ao *ao)
{
struct priv *p = ao->priv;
int err;
@@ -621,6 +624,34 @@ static int init(struct ao *ao)
MP_WARN(ao, "ALSA channel map conflicts with channel count!\n");
}
} else {
+ // Is it one that contains NA channels?
+ struct mp_chmap chmap2 = {0};
+ for (int c = 0; c < alsa_chmap->channels; c++) {
+ int alsa_ch = alsa_chmap->pos[c];
+ if (alsa_ch != SND_CHMAP_NA)
+ chmap2.speaker[chmap2.num++] = find_mp_channel(alsa_ch);
+ }
+
+ if (mp_chmap_is_valid(&chmap2)) {
+ // Sometimes, ALSA will advertise certain chmaps, but it's not
+ // possible to set them. This can happen with dmix: as of
+ // alsa 1.0.28, dmix can do stereo only, but advertises the
+ // surround chmaps of the underlying device. In this case,
+ // requesting e.g. 5.1 will fail, but it will still allow
+ // setting 6 channels. Then it will return something like
+ // "FL FR NA NA NA NA" as channel map. This means we would
+ // have to pad stereo output to 6 channels with silence, which
+ // is way too complicated in the general case. You can't change
+ // the number of channels to 2 either, because the hw params
+ // are already set! So just fuck it and reopen the device with
+ // the chmap "cleaned out" of NA entries.
+ err = snd_pcm_close(p->alsa);
+ p->alsa = NULL;
+ CHECK_ALSA_ERROR("pcm close error");
+ ao->channels = chmap2;
+ return INIT_BRAINDEATH;
+ }
+
MP_WARN(ao, "Got unknown channel map from ALSA.\n");
}
@@ -675,11 +706,19 @@ static int init(struct ao *ao)
p->can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams);
- return 0;
+ return INIT_OK;
alsa_error:
uninit(ao);
- return -1;
+ return INIT_ERROR;
+}
+
+static int init(struct ao *ao)
+{
+ int r = init_device(ao);
+ if (r == INIT_BRAINDEATH)
+ r = init_device(ao); // retry with normalized channel layout
+ return r == INIT_OK ? 0 : -1;
}
static void drain(struct ao *ao)