summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-11-24 19:40:24 +0100
committerwm4 <wm4@nowhere>2014-11-24 19:44:26 +0100
commit2228d4737367fb0303bec263bc06ca90c9ff8757 (patch)
tree30231e75b7fedac3424711fb082c0b2f196f04c0
parent7561adb14da8ce5f2a742eea8506823500167ad5 (diff)
downloadmpv-2228d4737367fb0303bec263bc06ca90c9ff8757.tar.bz2
mpv-2228d4737367fb0303bec263bc06ca90c9ff8757.tar.xz
ao_alsa: try to use the channel map reported by ALSA
If ALSA reports a channel map, and it looks like it makes sense (i.e. could be converted to mpv channel map, and the channel count matches), then use that instead of the channel map we are assuming. This is based on code written by lachs0r (alsa_ng branch).
-rw-r--r--audio/chmap.h3
-rw-r--r--audio/out/ao_alsa.c64
2 files changed, 66 insertions, 1 deletions
diff --git a/audio/chmap.h b/audio/chmap.h
index 22cf6fb73b..c7851a8657 100644
--- a/audio/chmap.h
+++ b/audio/chmap.h
@@ -61,7 +61,8 @@ enum mp_speaker_id {
MP_SPEAKER_ID_UNKNOWN0 = 64,
MP_SPEAKER_ID_UNKNOWN_LAST = MP_SPEAKER_ID_UNKNOWN0 + MP_NUM_CHANNELS - 1,
- // Including the unassigned IDs in between. This is not a valid ID anymore.
+ // Including the unassigned IDs in between. This is not a valid ID anymore,
+ // but is still within uint8_t.
MP_SPEAKER_ID_COUNT,
};
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index b857c8fcf4..910ac98437 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -45,6 +45,9 @@
#include <alsa/asoundlib.h>
+#define HAVE_CHMAP_API \
+ (defined(SND_CHMAP_API_VERSION) && SND_CHMAP_API_VERSION >= (1 << 16))
+
#include "ao.h"
#include "internal.h"
#include "audio/format.h"
@@ -237,6 +240,45 @@ static int find_alsa_format(int af_format)
return SND_PCM_FORMAT_UNKNOWN;
}
+#if HAVE_CHMAP_API
+
+static const int alsa_to_mp_channels[][2] = {
+ {SND_CHMAP_FL, MP_SP(FL)},
+ {SND_CHMAP_FR, MP_SP(FR)},
+ {SND_CHMAP_RL, MP_SP(BL)},
+ {SND_CHMAP_RR, MP_SP(BR)},
+ {SND_CHMAP_FC, MP_SP(FC)},
+ {SND_CHMAP_LFE, MP_SP(LFE)},
+ {SND_CHMAP_SL, MP_SP(SL)},
+ {SND_CHMAP_SR, MP_SP(SR)},
+ {SND_CHMAP_RC, MP_SP(BC)},
+ {SND_CHMAP_FLC, MP_SP(FLC)},
+ {SND_CHMAP_FRC, MP_SP(FRC)},
+ {SND_CHMAP_FLW, MP_SP(WL)},
+ {SND_CHMAP_FRW, MP_SP(WR)},
+ {SND_CHMAP_TC, MP_SP(TC)},
+ {SND_CHMAP_TFL, MP_SP(TFL)},
+ {SND_CHMAP_TFR, MP_SP(TFR)},
+ {SND_CHMAP_TFC, MP_SP(TFC)},
+ {SND_CHMAP_TRL, MP_SP(TBL)},
+ {SND_CHMAP_TRR, MP_SP(TBR)},
+ {SND_CHMAP_TRC, MP_SP(TBC)},
+ {SND_CHMAP_MONO, MP_SP(FC)},
+ {SND_CHMAP_LAST, MP_SPEAKER_ID_COUNT}
+};
+
+static int find_mp_channel(int alsa_channel)
+{
+ for (int i = 0; alsa_to_mp_channels[i][1] != MP_SPEAKER_ID_COUNT; i++) {
+ if (alsa_to_mp_channels[i][0] == alsa_channel)
+ return alsa_to_mp_channels[i][1];
+ }
+
+ return MP_SPEAKER_ID_COUNT;
+}
+
+#endif /* HAVE_CHMAP_API */
+
// Lists device names and their implied channel map.
// The second item must be resolvable with mp_chmap_from_str().
// Source: http://www.alsa-project.org/main/index.php/DeviceNames
@@ -534,6 +576,28 @@ static int init(struct ao *ao)
p->can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams);
+#if HAVE_CHMAP_API
+ snd_pcm_chmap_t *alsa_chmap = snd_pcm_get_chmap(p->alsa);
+ if (alsa_chmap) {
+ char tmp[128];
+ if (snd_pcm_chmap_print(alsa_chmap, sizeof(tmp), tmp) > 0)
+ MP_VERBOSE(ao, "attempting to set ALSA channel map: %s\n", tmp);
+
+ struct mp_chmap chmap = {.num = alsa_chmap->channels};
+ for (int c = 0; c < chmap.num; c++)
+ chmap.speaker[c] = find_mp_channel(alsa_chmap->pos[c]);
+
+ char *mp_map_str = mp_chmap_to_str(&chmap);
+ MP_VERBOSE(ao, "which we understand as: %s\n", mp_map_str);
+ talloc_free(mp_map_str);
+
+ if (mp_chmap_is_valid(&chmap) && chmap.num == ao->channels.num) {
+ MP_VERBOSE(ao, "using the ALSA channel map.\n");
+ ao->channels = chmap;
+ }
+ }
+#endif
+
MP_VERBOSE(ao, "opened: %d Hz/%d channels/%d bps/%d samples buffer/%s\n",
ao->samplerate, ao->channels.num, af_fmt2bits(ao->format),
p->buffersize, snd_pcm_format_description(p->alsa_fmt));