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]

Reply via email to