This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

The following commit(s) were added to refs/heads/master by this push:
     new 1f24840239 avdevice/alsa: auto-detect channel layout when capturing 
audio
1f24840239 is described below

commit 1f2484023936c4da95035459ebf11fc7730f18af
Author:     Romain Beauxis <[email protected]>
AuthorDate: Wed May 13 08:18:49 2026 -0400
Commit:     Romain Beauxis <[email protected]>
CommitDate: Sat Jul 11 08:45:54 2026 -0500

    avdevice/alsa: auto-detect channel layout when capturing audio
---
 doc/indevs.texi        |   6 ++-
 libavdevice/alsa.c     | 118 +++++++++++++++++++++++++++++++++++++++++++++++--
 libavdevice/alsa.h     |   5 ++-
 libavdevice/alsa_dec.c |   2 +-
 4 files changed, 123 insertions(+), 8 deletions(-)

diff --git a/doc/indevs.texi b/doc/indevs.texi
index 8822e070fe..7c3c7114a4 100644
--- a/doc/indevs.texi
+++ b/doc/indevs.texi
@@ -58,8 +58,10 @@ For more information see:
 @item sample_rate
 Set the sample rate in Hz. Default is 48000.
 
-@item channels
-Set the number of channels. Default is 2.
+@item ch_layout
+Set the channel layout. When unset, the layout defaults to stereo, or to the
+minimum channel count supported by the device, and is filled in from the ALSA
+channel map on a best-effort basis.
 
 @end table
 
diff --git a/libavdevice/alsa.c b/libavdevice/alsa.c
index 966eea519a..a18b532375 100644
--- a/libavdevice/alsa.c
+++ b/libavdevice/alsa.c
@@ -127,6 +127,88 @@ 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 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 av_cold int find_reorder_func(AlsaData *s, int codec_id,
                                      const AVChannelLayout *layout, int out)
 {
@@ -173,7 +255,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 +275,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 +320,24 @@ av_cold int ff_alsa_open(AVFormatContext *ctx, 
snd_pcm_stream_t mode,
         goto fail;
     }
 
+    // For output streams, the stream should already have a layout.
+    // Adding this check for clarity.
+    if (mode == SND_PCM_STREAM_CAPTURE && layout->nb_channels == 0) {
+        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 +345,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 +377,19 @@ 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 supersede the re-ordering function
+    // below.
+    if (mode == SND_PCM_STREAM_CAPTURE &&
+        layout->order == AV_CHANNEL_ORDER_UNSPEC) {
+        res = alsa_get_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 
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 f6a85616a8..a1fa667c04 100644
--- a/libavdevice/alsa.h
+++ b/libavdevice/alsa.h
@@ -69,7 +69,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
@@ -79,7 +80,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 e3f73911de..7411fcc5fb 100644
--- a/libavdevice/alsa_dec.c
+++ b/libavdevice/alsa_dec.c
@@ -150,7 +150,7 @@ static int audio_get_device_list(AVFormatContext *h, 
AVDeviceInfoList *device_li
 
 static const AVOption options[] = {
     { "sample_rate", "", offsetof(AlsaData, sample_rate), AV_OPT_TYPE_INT, 
{.i64 = 48000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
-    { "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 },
 };
 

_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to