diff options
-rw-r--r-- | audio/out/ao_alsa.c | 153 |
1 files changed, 125 insertions, 28 deletions
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c index c918363873..2cee7170a5 100644 --- a/audio/out/ao_alsa.c +++ b/audio/out/ao_alsa.c @@ -266,7 +266,60 @@ static int find_mp_channel(int alsa_channel) return MP_SPEAKER_ID_COUNT; } -#endif /* HAVE_CHMAP_API */ +static int find_alsa_channel(int mp_channel) +{ + for (int i = 0; alsa_to_mp_channels[i][1] != MP_SPEAKER_ID_COUNT; i++) { + if (alsa_to_mp_channels[i][1] == mp_channel) + return alsa_to_mp_channels[i][0]; + } + + return SND_CHMAP_UNKNOWN; +} + +static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap) +{ + struct priv *p = ao->priv; + struct mp_chmap_sel chmap_sel = {0}; + + snd_pcm_chmap_query_t **maps = snd_pcm_query_chmaps(p->alsa); + if (!maps) + return false; + + for (int i = 0; maps[i] != NULL; i++) { + if (maps[i]->map.channels > MP_NUM_CHANNELS) { + MP_VERBOSE(ao, "skipping ALSA channel map with too many channels.\n"); + continue; + } + + struct mp_chmap entry = {.num = maps[i]->map.channels}; + for (int c = 0; c < entry.num; c++) + entry.speaker[c] = find_mp_channel(maps[i]->map.pos[c]); + + if (mp_chmap_is_valid(&entry)) { + MP_VERBOSE(ao, "Got supported channel map: %s (type %s)\n", + mp_chmap_to_str(&entry), + snd_pcm_chmap_type_name(maps[i]->type)); + mp_chmap_sel_add_map(&chmap_sel, &entry); + } else { + char tmp[128]; + if (snd_pcm_chmap_print(&maps[i]->map, sizeof(tmp), tmp) > 0) + MP_VERBOSE(ao, "skipping unknown ALSA channel map: %s\n", tmp); + } + } + + snd_pcm_free_chmaps(maps); + + return ao_chmap_sel_adjust(ao, &chmap_sel, chmap); +} + +#else /* HAVE_CHMAP_API */ + +static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap) +{ + return false; +} + +#endif /* else HAVE_CHMAP_API */ // Lists device names and their implied channel map. // The second item must be resolvable with mp_chmap_from_str(). @@ -287,7 +340,7 @@ static const char *const device_channel_layouts[][2] = { #define NUM_ALSA_CHMAPS MP_ARRAY_SIZE(device_channel_layouts) -static const char *select_chmap(struct ao *ao) +static const char *select_chmap(struct ao *ao, struct mp_chmap *chmap) { struct mp_chmap_sel sel = {0}; struct mp_chmap maps[NUM_ALSA_CHMAPS]; @@ -296,16 +349,16 @@ static const char *select_chmap(struct ao *ao) mp_chmap_sel_add_map(&sel, &maps[n]); }; - if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) + if (!ao_chmap_sel_adjust(ao, &sel, chmap)) return "default"; for (int n = 0; n < NUM_ALSA_CHMAPS; n++) { - if (mp_chmap_equals(&ao->channels, &maps[n])) + if (mp_chmap_equals(chmap, &maps[n])) return device_channel_layouts[n][0]; } MP_ERR(ao, "channel layout %s (%d ch) not supported.\n", - mp_chmap_to_str(&ao->channels), ao->channels.num); + mp_chmap_to_str(chmap), chmap->num); return "default"; } @@ -396,13 +449,14 @@ static int init(struct ao *ao) if (!p->cfg_ni) ao->format = af_fmt_from_planar(ao->format); + struct mp_chmap implied_chmap = ao->channels; const char *device; if (AF_FORMAT_IS_IEC61937(ao->format)) { device = "iec958"; MP_VERBOSE(ao, "playing AC3/iec61937/iec958, %i channels\n", ao->channels.num); } else { - device = select_chmap(ao); + device = select_chmap(ao, &implied_chmap); if (strcmp(device, "default") != 0 && (ao->format & AF_FORMAT_F)) { // hack - use the converter plugin (why the heck?) device = talloc_asprintf(ao, "plug:%s", device); @@ -480,11 +534,23 @@ static int init(struct ao *ao) } CHECK_ALSA_ERROR("Unable to set access type"); + struct mp_chmap dev_chmap = ao->channels; + if (query_chmaps(ao, &dev_chmap)) { + ao->channels = dev_chmap; + } else { + dev_chmap.num = 0; + } + int num_channels = ao->channels.num; err = snd_pcm_hw_params_set_channels_near (p->alsa, alsa_hwparams, &num_channels); CHECK_ALSA_ERROR("Unable to set channels"); + if (num_channels > MP_NUM_CHANNELS) { + MP_FATAL(ao, "Too many audio channels (%d).\n", num_channels); + goto alsa_error; + } + if (num_channels != ao->channels.num) { MP_ERR(ao, "Couldn't get requested number of channels.\n"); mp_chmap_from_channels_alsa(&ao->channels, num_channels); @@ -515,6 +581,59 @@ static int init(struct ao *ao) /* end setting hw-params */ +#if HAVE_CHMAP_API + if (mp_chmap_is_valid(&dev_chmap)) { + snd_pcm_chmap_t *alsa_chmap = + calloc(1, sizeof(*alsa_chmap) + + sizeof(alsa_chmap->pos[0]) * dev_chmap.num); + if (!alsa_chmap) + goto alsa_error; + + alsa_chmap->channels = dev_chmap.num; + for (int c = 0; c < dev_chmap.num; c++) + alsa_chmap->pos[c] = find_alsa_channel(dev_chmap.speaker[c]); + + char tmp[128]; + if (snd_pcm_chmap_print(alsa_chmap, sizeof(tmp), tmp) > 0) + MP_VERBOSE(ao, "trying to set ALSA channel map: %s\n", tmp); + + err = snd_pcm_set_chmap(p->alsa, alsa_chmap); + if (err == -ENXIO) { + MP_WARN(ao, "Device does not support requested channel map\n"); + } else { + CHECK_ALSA_WARN("Channel map setup failed"); + } + } + + 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, "channel map reported by ALSA: %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]); + + MP_VERBOSE(ao, "which we understand as: %s\n", mp_chmap_to_str(&chmap)); + + if (mp_chmap_is_valid(&chmap)) { + if (mp_chmap_equals(&chmap, &ao->channels)) { + MP_VERBOSE(ao, "which is what we requested.\n"); + } else if (chmap.num == ao->channels.num) { + MP_VERBOSE(ao, "using the ALSA channel map.\n"); + ao->channels = chmap; + } else { + MP_WARN(ao, "ALSA channel map conflicts with channel count!\n"); + } + } else { + MP_WARN(ao, "Got unknown channel map from ALSA.\n"); + } + + free(alsa_chmap); + } +#endif + snd_pcm_uframes_t bufsize; err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize); CHECK_ALSA_ERROR("Unable to get buffersize"); @@ -559,28 +678,6 @@ 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, "channel map reported by ALSA: %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]); - - MP_VERBOSE(ao, "which we understand as: %s\n", mp_chmap_to_str(&chmap)); - - if (mp_chmap_is_valid(&chmap) && chmap.num == ao->channels.num) { - MP_VERBOSE(ao, "using the ALSA channel map.\n"); - ao->channels = chmap; - } - - free(alsa_chmap); - } -#endif - return 0; alsa_error: |