The officially official Devuan Forum!

You are not logged in.

#1 Yesterday 15:29:44

igorzwx
Member
Registered: 2024-05-06
Posts: 413  

Why Zoom Doesn’t Work with Firefox’s ALSA Backend

It’s not so much that Firefox can’t handle ALSA, but rather that its cubeb backend takes a somewhat… minimalist approach to device enumeration.

The issue lies in alsa_enumerate_devices(), which, rather than listing actual hardware, returns a single, rather fictional "default" device (cubeb_alsa.c:1408–1456). This isn’t so much discovery as polite suggestion.

The Present Situation

Currently, the backend reports a device with:

- A maximum of 10,000 channels (ambitious, if not grounded in reality),
- Zero latency (one can only dream),
- And a fixed sample rate of 48,000 Hz (cubeb_alsa.c:1430–1451).

Unsurprisingly, Zoom’s web client sees “internal speakers” and “internal mic” — but they’re rather like ghosts: visible, yet impossible to engage with (e.g., Zoom’s web client with Mozilla LibreWolf 145.0.1-2, cubeb's ALSA backend).

What snd_device_name_hint Actually Does

This is ALSA’s proper device discovery function. It returns real devices — hardware, plugins, virtuals — with actual capabilities. It’s what one might reasonably expect to be used.

Why a Complete Rewrite Is in Order

To fix this, the cubeb ALSA backend would need to:

1. Call snd_device_name_hint() — a modest step, but one currently overlooked.
2. Query real hardware parameters — using standard ALSA calls to learn actual channel counts, rates, and latencies (cubeb_alsa.c:1213–1221).
3. Distinguish input, output, and duplex devices — a basic courtesy to the user.
4. Expose the actual ALSA topology — including asymmetric setups using dmix and dsnoop, which many rely on.

Why This Hasn’t Happened

The developers appear to have treated ALSA as a fallback — a bare-bones option for when PulseAudio isn’t available. This overlooks that ALSA, with its default software mixing, is quite capable. The current implementation suffices for playing the odd YouTube video, but falters when full-duplex communication — such as a Zoom call — is required.

For Comparison

The OSS backend in cubeb does properly enumerate devices by probing /dev/sndstat (cubeb_oss.c:344–443). A similar effort for ALSA would be only reasonable.

In Summary

The fix amounts to implementing what the standalone Zoom app already does: using ALSA as it’s meant to be used. The current stub is so far from proper enumeration that a full rewrite — replacing fiction with fact — is the only sensible path forward.

Citations

File: src/cubeb_alsa.c (L73-78)

#define CUBEB_STREAM_MAX 16
#define CUBEB_WATCHDOG_MS 10000

#define CUBEB_ALSA_PCM_NAME "default"

#define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"

File: src/cubeb_alsa.c (L1213-1221)

 
  r = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
  if (r < 0) {
    return CUBEB_ERROR;
  }

  r = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
  if (r < 0) {
    return CUBEB_ERROR;
  }

 
File: src/cubeb_alsa.c (L1408-1456)

static int
alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
                       cubeb_device_collection * collection)
{
  cubeb_device_info * device = NULL;

  if (!context)
    return CUBEB_ERROR;

  uint32_t rate, max_channels;
  int r;

  r = alsa_get_preferred_sample_rate(context, &rate);
  if (r != CUBEB_OK) {
    return CUBEB_ERROR;
  }

  r = alsa_get_max_channel_count(context, &max_channels);
  if (r != CUBEB_OK) {
    return CUBEB_ERROR;
  }

  char const * a_name = "default";
  device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
  assert(device);
  if (!device)
    return CUBEB_ERROR;

  device->device_id = a_name;
  device->devid = (cubeb_devid)device->device_id;
  device->friendly_name = a_name;
  device->group_id = a_name;
  device->vendor_name = a_name;
  device->type = type;
  device->state = CUBEB_DEVICE_STATE_ENABLED;
  device->preferred = CUBEB_DEVICE_PREF_ALL;
  device->format = CUBEB_DEVICE_FMT_S16NE;
  device->default_format = CUBEB_DEVICE_FMT_S16NE;
  device->max_channels = max_channels;
  device->min_rate = rate;
  device->max_rate = rate;
  device->default_rate = rate;
  device->latency_lo = 0;
  device->latency_hi = 0;

  collection->device = device;
  collection->count = 1;

  return CUBEB_OK;

File: src/cubeb_oss.c (L344-443)

static int
oss_enumerate_devices(cubeb * context, cubeb_device_type type,
                      cubeb_device_collection * collection)
{
  cubeb_device_info * devinfop = NULL;
  char * line = NULL;
  size_t linecap = 0;
  FILE * sndstatfp = NULL;
  int collection_cnt = 0;
  int is_ud = 0;
  int skipall = 0;

  devinfop = calloc(1, sizeof(cubeb_device_info));
  if (devinfop == NULL)
    goto fail;

  sndstatfp = fopen("/dev/sndstat", "r");
  if (sndstatfp == NULL)
    goto fail;
  while (getline(&line, &linecap, sndstatfp) > 0) {
    const char * devid = NULL;
    struct sndstat_info sinfo;
    oss_audioinfo ai;

    if (!strncmp(line, SNDSTAT_FV_BEGIN_STR, strlen(SNDSTAT_FV_BEGIN_STR))) {
      skipall = 1;
      continue;
    }
    if (!strncmp(line, SNDSTAT_BEGIN_STR, strlen(SNDSTAT_BEGIN_STR))) {
      is_ud = 0;
      skipall = 0;
      continue;
    }
    if (!strncmp(line, SNDSTAT_USER_BEGIN_STR,
                 strlen(SNDSTAT_USER_BEGIN_STR))) {
      is_ud = 1;
      skipall = 0;
      continue;
    }
    if (skipall || isblank(line[0]))
      continue;

    if (oss_sndstat_line_parse(line, is_ud, &sinfo))
      continue;

    devinfop[collection_cnt].type = 0;
    switch (sinfo.type) {
    case CUBEB_DEVICE_TYPE_INPUT:
      if (type & CUBEB_DEVICE_TYPE_OUTPUT)
        continue;
      break;
    case CUBEB_DEVICE_TYPE_OUTPUT:
      if (type & CUBEB_DEVICE_TYPE_INPUT)
        continue;
      break;
    case 0:
      continue;
    }

    if (oss_probe_open(sinfo.devname, type, NULL, &ai))
      continue;

    devid = oss_cubeb_devid_intern(context, sinfo.devname);
    if (devid == NULL)
      continue;

    devinfop[collection_cnt].device_id = strdup(sinfo.devname);
    asprintf((char **)&devinfop[collection_cnt].friendly_name, "%s: %s",
             sinfo.devname, sinfo.desc);
    devinfop[collection_cnt].group_id = strdup(sinfo.devname);
    devinfop[collection_cnt].vendor_name = NULL;
    if (devinfop[collection_cnt].device_id == NULL ||
        devinfop[collection_cnt].friendly_name == NULL ||
        devinfop[collection_cnt].group_id == NULL) {
      oss_free_cubeb_device_info_strings(&devinfop[collection_cnt]);
      continue;
    }

    devinfop[collection_cnt].type = type;
    devinfop[collection_cnt].devid = devid;
    devinfop[collection_cnt].state = CUBEB_DEVICE_STATE_ENABLED;
    devinfop[collection_cnt].preferred =
        (sinfo.preferred) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
    devinfop[collection_cnt].format = CUBEB_DEVICE_FMT_S16NE;
    devinfop[collection_cnt].default_format = CUBEB_DEVICE_FMT_S16NE;
    devinfop[collection_cnt].max_channels = ai.max_channels;
    devinfop[collection_cnt].default_rate = OSS_PREFER_RATE;
    devinfop[collection_cnt].max_rate = ai.max_rate;
    devinfop[collection_cnt].min_rate = ai.min_rate;
    devinfop[collection_cnt].latency_lo = 0;
    devinfop[collection_cnt].latency_hi = 0;

    collection_cnt++;

    void * newp =
        reallocarray(devinfop, collection_cnt + 1, sizeof(cubeb_device_info));
    if (newp == NULL)
      goto fail;
    devinfop = newp;
  }

Last edited by igorzwx (Yesterday 15:41:44)

Offline

#2 Yesterday 16:03:18

Altoid
Member
Registered: 2017-05-07
Posts: 1,906  

Re: Why Zoom Doesn’t Work with Firefox’s ALSA Backend

Hello:

igorzwx wrote:

... ALSA as a fallback — a bare-bones option for when PulseAudio isn’t available.
... overlooks that ALSA, with its default software mixing, is quite capable.

Very practical of them.
How else will they get people to use Poettering's PulseAudio complication?

igorzwx wrote:

... a full rewrite — replacing fiction with fact — is the only sensible path forward.

I wonder what the odds on that happening are?

Maybe I'll call my bookie and ask.  8^°

Best,

A.

Offline

Board footer