Le jeu. 4 juin 2026 à 22:39, toots via ffmpeg-devel
<[email protected]> a écrit :
>
> PR #23356 opened by toots
> URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23356
> Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23356.patch
>
>
> >From 48b8d19af32592b1e7c5a75e74a61b0f0fce096e Mon Sep 17 00:00:00 2001
> From: Romain Beauxis <[email protected]>
> Date: Wed, 13 May 2026 08:18:49 -0400
> Subject: [PATCH] avdevice/alsa: auto-detect channel layout.
Something is broken with the agit workflow here.
This happened after git push got stuck several times earlier today.
Next, I was able to push but each push kept opening a new PR instead
of updating the existing one..
-- Romain
> ---
> doc/outdevs.texi | 7 ++
> libavdevice/alsa.c | 154 ++++++++++++++++++++++++++++++++++++++++-
> libavdevice/alsa.h | 5 +-
> libavdevice/alsa_dec.c | 2 +-
> 4 files changed, 162 insertions(+), 6 deletions(-)
>
> diff --git a/doc/outdevs.texi b/doc/outdevs.texi
> index 86c78f31b7..6bd0e880e7 100644
> --- a/doc/outdevs.texi
> +++ b/doc/outdevs.texi
> @@ -22,6 +22,13 @@ A description of the currently available output devices
> follows.
>
> ALSA (Advanced Linux Sound Architecture) output device.
>
> +The channel layout is taken from the output stream. If no channel layout is
> +set, the device will attempt to use stereo (2 channels), falling back to
> +the minimum channel count supported by the device. When a channel layout is
> +specified, the device will attempt to configure the ALSA channel map
> +accordingly, though support for channel layout in ALSA drivers is still
> +sparse and this is done on a best-effort basis.
> +
> @subsection Examples
>
> @itemize
> diff --git a/libavdevice/alsa.c b/libavdevice/alsa.c
> index 966eea519a..4acbcd71a2 100644
> --- a/libavdevice/alsa.c
> +++ b/libavdevice/alsa.c
> @@ -127,6 +127,123 @@ switch(format) {\
> case FORMAT_F32: s->reorder_func = alsa_reorder_f32_out_ ##layout;
> break;\
> }
>
> +static const struct {
> + unsigned int pos;
> + enum AVChannel ch;
> +} alsa_chmap_table[] = {
> + { SND_CHMAP_MONO, AV_CHAN_FRONT_CENTER },
> + { SND_CHMAP_FL, AV_CHAN_FRONT_LEFT },
> + { SND_CHMAP_FR, AV_CHAN_FRONT_RIGHT },
> + { SND_CHMAP_RL, AV_CHAN_BACK_LEFT },
> + { SND_CHMAP_RR, AV_CHAN_BACK_RIGHT },
> + { SND_CHMAP_FC, AV_CHAN_FRONT_CENTER },
> + { SND_CHMAP_LFE, AV_CHAN_LOW_FREQUENCY },
> + { SND_CHMAP_SL, AV_CHAN_SIDE_LEFT },
> + { SND_CHMAP_SR, AV_CHAN_SIDE_RIGHT },
> + { SND_CHMAP_RC, AV_CHAN_BACK_CENTER },
> + { SND_CHMAP_FLC, AV_CHAN_FRONT_LEFT_OF_CENTER },
> + { SND_CHMAP_FRC, AV_CHAN_FRONT_RIGHT_OF_CENTER},
> + { SND_CHMAP_FLW, AV_CHAN_WIDE_LEFT },
> + { SND_CHMAP_FRW, AV_CHAN_WIDE_RIGHT },
> + { SND_CHMAP_TC, AV_CHAN_TOP_CENTER },
> + { SND_CHMAP_TFL, AV_CHAN_TOP_FRONT_LEFT },
> + { SND_CHMAP_TFR, AV_CHAN_TOP_FRONT_RIGHT },
> + { SND_CHMAP_TFC, AV_CHAN_TOP_FRONT_CENTER },
> + { SND_CHMAP_TRL, AV_CHAN_TOP_BACK_LEFT },
> + { SND_CHMAP_TRR, AV_CHAN_TOP_BACK_RIGHT },
> + { SND_CHMAP_TRC, AV_CHAN_TOP_BACK_CENTER },
> + { SND_CHMAP_TSL, AV_CHAN_TOP_SIDE_LEFT },
> + { SND_CHMAP_TSR, AV_CHAN_TOP_SIDE_RIGHT },
> + { SND_CHMAP_LLFE, AV_CHAN_LOW_FREQUENCY },
> + { SND_CHMAP_RLFE, AV_CHAN_LOW_FREQUENCY_2 },
> + { SND_CHMAP_BC, AV_CHAN_BOTTOM_FRONT_CENTER },
> + { SND_CHMAP_BLC, AV_CHAN_BOTTOM_FRONT_LEFT },
> + { SND_CHMAP_BRC, AV_CHAN_BOTTOM_FRONT_RIGHT },
> + { SND_CHMAP_UNKNOWN, AV_CHAN_UNKNOWN },
> + { SND_CHMAP_NA, AV_CHAN_UNUSED },
> +};
> +
> +static enum AVChannel alsa_chmap_pos_to_av_chan(unsigned int pos)
> +{
> + for (unsigned i = 0; i < FF_ARRAY_ELEMS(alsa_chmap_table); i++)
> + if (alsa_chmap_table[i].pos == pos)
> + return alsa_chmap_table[i].ch;
> + return AV_CHAN_NONE;
> +}
> +
> +static unsigned int alsa_av_chan_to_chmap_pos(enum AVChannel ch)
> +{
> + for (unsigned i = 0; i < FF_ARRAY_ELEMS(alsa_chmap_table); i++)
> + if (alsa_chmap_table[i].ch == ch)
> + return alsa_chmap_table[i].pos;
> + return SND_CHMAP_UNKNOWN;
> +}
> +
> +static int alsa_get_chmap(AVFormatContext *ctx, snd_pcm_t *h,
> AVChannelLayout *layout)
> +{
> + snd_pcm_chmap_t *chmap = snd_pcm_get_chmap(h);
> + int ret = 0;
> +
> + if (!chmap)
> + return 0;
> +
> + if (layout->nb_channels != chmap->channels) {
> + av_log(ctx, AV_LOG_ERROR, "Inconsistent channel layout
> requested!\n");
> + ret = AVERROR(EINVAL);
> + goto out;
> + }
> +
> + av_channel_layout_uninit(layout);
> + ret = av_channel_layout_custom_init(layout, chmap->channels);
> + if (ret < 0)
> + goto out;
> +
> + for (unsigned i = 0; i < chmap->channels; i++) {
> + enum AVChannel ch = alsa_chmap_pos_to_av_chan(chmap->pos[i] &
> +
> SND_CHMAP_POSITION_MASK);
> + if (ch == AV_CHAN_NONE) {
> + av_log(ctx, AV_LOG_WARNING,
> + "unknown ALSA channel position %u.\n",
> + chmap->pos[i] & SND_CHMAP_POSITION_MASK);
> + continue;
> + }
> + layout->u.map[i].id = ch;
> + }
> + ret = av_channel_layout_retype(layout, 0,
> + AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL);
> +
> +out:
> + free(chmap);
> + return ret;
> +}
> +
> +static int alsa_set_chmap(AVFormatContext *ctx, snd_pcm_t *h,
> + const AVChannelLayout *layout)
> +{
> + snd_pcm_chmap_t *chmap;
> +
> + chmap = av_malloc(sizeof(*chmap) + layout->nb_channels *
> sizeof(chmap->pos[0]));
> + if (!chmap)
> + return AVERROR(ENOMEM);
> +
> + chmap->channels = layout->nb_channels;
> + for (int i = 0; i < layout->nb_channels; i++) {
> + enum AVChannel ch = av_channel_layout_channel_from_index(layout, i);
> + chmap->pos[i] = alsa_av_chan_to_chmap_pos(ch);
> + }
> +
> + if (snd_pcm_set_chmap(h, chmap) < 0)
> + // channels count still match the layout and not many drivers
> currently
> + // support explicit channel layout setting so not erroring for now.
> + av_log(ctx, AV_LOG_WARNING,
> + "ALSA driver does not implement the requested channel layout "
> + "configuration!\n");
> +
> + av_free(chmap);
> +
> + return 0;
> +}
> +
> static av_cold int find_reorder_func(AlsaData *s, int codec_id,
> const AVChannelLayout *layout, int out)
> {
> @@ -173,7 +290,7 @@ static av_cold int find_reorder_func(AlsaData *s, int
> codec_id,
>
> av_cold int ff_alsa_open(AVFormatContext *ctx, snd_pcm_stream_t mode,
> unsigned int *sample_rate,
> - const AVChannelLayout *layout, enum AVCodecID
> *codec_id)
> + AVChannelLayout *layout, enum AVCodecID *codec_id)
> {
> AlsaData *s = ctx->priv_data;
> const char *audio_device;
> @@ -193,8 +310,6 @@ av_cold int ff_alsa_open(AVFormatContext *ctx,
> snd_pcm_stream_t mode,
> av_log(ctx, AV_LOG_ERROR, "sample format 0x%04x is not supported\n",
> *codec_id);
> return AVERROR(ENOSYS);
> }
> - s->frame_size = av_get_bits_per_sample(*codec_id) / 8 *
> layout->nb_channels;
> -
> if (ctx->flags & AVFMT_FLAG_NONBLOCK) {
> flags = SND_PCM_NONBLOCK;
> }
> @@ -240,6 +355,22 @@ av_cold int ff_alsa_open(AVFormatContext *ctx,
> snd_pcm_stream_t mode,
> goto fail;
> }
>
> + if (layout->nb_channels == 0 && mode == SND_PCM_STREAM_CAPTURE)) {
> + unsigned int channels = 2;
> + if (snd_pcm_hw_params_test_channels(h, hw_params, channels) < 0) {
> + res = snd_pcm_hw_params_get_channels_min(hw_params, &channels);
> + if (res < 0) {
> + av_log(ctx, AV_LOG_ERROR, "cannot get minimum channel count
> (%s)\n",
> + snd_strerror(res));
> + goto fail;
> + }
> + av_log(ctx, AV_LOG_VERBOSE,
> + "stereo not supported, falling back to %u channel(s)\n",
> channels);
> + }
> + layout->order = AV_CHANNEL_ORDER_UNSPEC;
> + layout->nb_channels = channels;
> + }
> +
> res = snd_pcm_hw_params_set_channels(h, hw_params, layout->nb_channels);
> if (res < 0) {
> av_log(ctx, AV_LOG_ERROR, "cannot set channel count to %d (%s)\n",
> @@ -247,6 +378,8 @@ av_cold int ff_alsa_open(AVFormatContext *ctx,
> snd_pcm_stream_t mode,
> goto fail;
> }
>
> + s->frame_size = av_get_bits_per_sample(*codec_id) / 8 *
> layout->nb_channels;
> +
> snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size);
> buffer_size = FFMIN(buffer_size, ALSA_BUFFER_SIZE_MAX);
> /* TODO: maybe use ctx->max_picture_buffer somehow */
> @@ -277,7 +410,22 @@ av_cold int ff_alsa_open(AVFormatContext *ctx,
> snd_pcm_stream_t mode,
>
> snd_pcm_hw_params_free(hw_params);
>
> + // TODO: when support for channel layout gets more widespread in alsa
> + // drivers, this might be used to superseed the re-ordering function
> + // below.
> + if (mode != SND_PCM_STREAM_PLAYBACK) {
> + if (layout->order == AV_CHANNEL_ORDER_UNSPEC)
> + res = alsa_get_chmap(ctx, h, layout);
> + else
> + res = alsa_set_chmap(ctx, h, layout);
> +
> + if (res < 0)
> + goto fail1;
> + }
> +
> if (layout->nb_channels > 2 && layout->order != AV_CHANNEL_ORDER_UNSPEC)
> {
> + // See comment above. find_reorder_func is currently only
> implemented for
> + // in playback mode.
> if (find_reorder_func(s, *codec_id, layout, mode ==
> SND_PCM_STREAM_PLAYBACK) < 0) {
> char name[128];
> av_channel_layout_describe(layout, name, sizeof(name));
> diff --git a/libavdevice/alsa.h b/libavdevice/alsa.h
> index d3dfa478c5..ed2ac66e93 100644
> --- a/libavdevice/alsa.h
> +++ b/libavdevice/alsa.h
> @@ -72,7 +72,8 @@ typedef struct AlsaData {
> * @param mode either SND_PCM_STREAM_CAPTURE or SND_PCM_STREAM_PLAYBACK
> * @param sample_rate in: requested sample rate;
> * out: actually selected sample rate
> - * @param layout channel layout
> + * @param layout in: requested channel layout, or nb_channels=0 for
> auto-detect;
> + * out: actually selected channel layout
> * @param codec_id in: requested AVCodecID or AV_CODEC_ID_NONE;
> * out: actually selected AVCodecID, changed only if
> * AV_CODEC_ID_NONE was requested
> @@ -82,7 +83,7 @@ typedef struct AlsaData {
> av_warn_unused_result
> int ff_alsa_open(AVFormatContext *s, snd_pcm_stream_t mode,
> unsigned int *sample_rate,
> - const AVChannelLayout *layout, enum AVCodecID *codec_id);
> + AVChannelLayout *layout, enum AVCodecID *codec_id);
>
> /**
> * Close the ALSA PCM.
> diff --git a/libavdevice/alsa_dec.c b/libavdevice/alsa_dec.c
> index 63409a7785..54d9b3a783 100644
> --- a/libavdevice/alsa_dec.c
> +++ b/libavdevice/alsa_dec.c
> @@ -160,7 +160,7 @@ static const AVOption options[] = {
> #if FF_API_ALSA_CHANNELS
> { "channels", "", offsetof(AlsaData, channels), AV_OPT_TYPE_INT,
> {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM |
> AV_OPT_FLAG_DEPRECATED },
> #endif
> - { "ch_layout", "", offsetof(AlsaData, ch_layout),
> AV_OPT_TYPE_CHLAYOUT, {.str = "2C"}, INT_MIN, INT_MAX,
> AV_OPT_FLAG_DECODING_PARAM },
> + { "ch_layout", "", offsetof(AlsaData, ch_layout),
> AV_OPT_TYPE_CHLAYOUT, {.str = NULL}, INT_MIN, INT_MAX,
> AV_OPT_FLAG_DECODING_PARAM },
> { NULL },
> };
>
> --
> 2.52.0
>
> _______________________________________________
> ffmpeg-devel mailing list -- [email protected]
> To unsubscribe send an email to [email protected]
_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]