You are not logged in.
about:support
Name Firefox Developer Edition
Version 154.0a1
Update Channel aurora
User Agent Mozilla/5.0 (X11; Linux x86_64; rv:154.0) Gecko/20100101 Firefox/154.0
Profile Directory ~/.firefox-dev/qxls80vg.dev-edition-default-1If User Agent is not set correctly with:
imply_option("MOZ_APP_UA_NAME", "Firefox")You can set it with override in about:config
general.useragent.override Mozilla/5.0 (X11; Linux x86_64; rv:154.0) Gecko/20100101 Firefox/154.0 imply_option("MOZ_APP_PROFILE", "firefox-dev")MOZ_APP_PROFILE=firefox-dev means User Profile Directory
$HOME/.firefox-devfor user settings.
firefox/browser/moz.configure is a secret configuration file.
does the firefox develper auto-update?
Yes, it does (by default). You can disable/enable this feature is about:config, or disable and lock in a secret config.
$ cat /etc/firefox-dev/defaults/pref/firefox-dev.cfg3-2.js
// Debian system-wide preferences for Firefox Developer Edition
// This file is intended for system administrators to configure default settings.
// Use 'pref("preference.name", value);' for defaults.
// Use 'pref("preference.name", value, locked);' to enforce immutable settings.
// Enable Remote Improvements - Nimbus rollouts system
pref("nimbus.rollouts.enabled", true);
pref("services.sync.prefs.sync.nimbus.rollouts.enabled", false, locked);
// Enable automatic updates
pref("app.update.auto", true);
pref("app.update.enabled", true);
pref("app.update.background.enabled", true);
// Disable Remote Improvements - Nimbus rollouts system
// pref("nimbus.rollouts.enabled", false, locked);
// pref("services.sync.prefs.sync.nimbus.rollouts.enabled", false, locked);
// Disable automatic updates
// pref("app.update.auto", false, locked);
// pref("app.update.enabled", false, locked);
// pref("app.update.background.enabled", false, locked); With plughw:0,0 or default, the audio path is:
Application → ALSA Library → Plugin Layer → Kernel PCM Core → Hardware Driver → Hardware The ALSA path with hw:0,0 is:
Application → ALSA Library → Kernel PCM Core → Hardware Driver → Hardware When using hw:0,0, the ALSA library bypasses the plugin layer (software format/rate/channel conversion) but still goes through the kernel PCM core for buffer management, period handling, and synchronization before reaching the hardware driver.
Usually, "exclusive mode" refers to exclusive device access control by one applications (this is what the word exclusive is supposed to mean). Such exclusive device access prevents multiple applications from simultaneously using the same hardware resource. In the Linux kernel's technical documentation, "exclusive" has a specific meaning: preventing multiple applications from simultaneously using the same hardware resource.
ALSA permits "exclusive full duplex":
$ arecord -f cd -D hw:0,0 | aplay -V mono -D hw:0,0
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
##################### + | 46%
$ arecord -f cd -D hw:1,0 | aplay -V mono -D hw:1,0
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
##################################################+| MAX
$ arecord -f cd -D hw:PCH,0 | aplay -V mono -D hw:PCH,0
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
#################### + | 97% arecord opens hw:0,0 for capture
aplay opens hw:0,0 for playback
Both processes have the the same device open simultaneously in "exclusive mode".
The active meter indicates that the full duplex works and audio data is actively flowing through the pipeline in real-time.
Thus, two applications, arecord and aplay, have exclusive access to the same device simultaneously. It works with Intel HDA codecs, USB soundcards, etc.
OSS4 with software mixer vmix disabled does not support "exclusive full duplex" for Intel HDA codecs
$ ./fulldup
Using audio engine 0=HD Audio play front for duplex
/dev/dsp doesn't support one device based full duplex scheme
Please use the two device scheme.
$ file /dev/dsp
/dev/dsp: symbolic link to /dev/oss/oss_hdaudio0/pcm0 ALSA is more advanced in this sense. In advanced Monty Python reality, the word exclusive has a flexible meaning. It may mean anything you want.
Patch 1 & 2
Sourcedir: firefox
FILE: firefox/browser/moz.configure
imply_option("MOZ_DEVTOOLS", "all")
imply_option("BROWSER_CHROME_URL", "chrome://browser/content/browser.xhtml")
+imply_option("MOZ_APP_UA_NAME", "Firefox")
+imply_option("MOZ_APP_PROFILE", "firefox-dev") You should simply add two lines:
imply_option("MOZ_APP_UA_NAME", "Firefox")
imply_option("MOZ_APP_PROFILE", "firefox-dev")after other "imply_option" lines.
$ cat firefox/browser/moz.configure
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
imply_option("MOZ_PLACES", True)
imply_option("MOZ_SERVICES_HEALTHREPORT", True)
imply_option("MOZ_SERVICES_SYNC", True)
imply_option("MOZ_DEDICATED_PROFILES", True)
imply_option("MOZ_BLOCK_PROFILE_DOWNGRADE", True)
imply_option("MOZ_NORMANDY", True)
imply_option("MOZ_PROFILE_MIGRATOR", True)
imply_option("MOZ_APP_VENDOR", "Mozilla")
imply_option("MOZ_APP_ID", "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
# Include the DevTools client, not just the server (which is the default)
imply_option("MOZ_DEVTOOLS", "all")
imply_option("BROWSER_CHROME_URL", "chrome://browser/content/browser.xhtml")
imply_option("MOZ_APP_UA_NAME", "Firefox")
imply_option("MOZ_APP_PROFILE", "firefox-dev")
...It works with Zoom.
Package Details: libasound2-plugin-fftrate 1.6.3-1
_https://aur.archlinux.org/packages/libasound2-plugin-fftrate
The Arch Linux patch for fftrate (gcc-12+):
prepare() {
cd "$srcdir/$pkgbase-$pkgver"
find . -type f \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) -exec sed -i -e 's/min(/MIN(/g' -e 's/max(/MAX(/g' {} +
} It does fix the problem, but creates a new one: a sort of inconsistency.
For example:
The upstream: PetrovSE fftrate/src/lib/mathmac.h
//================================================================
// Min & max computing
//----------------------------------------------------------------
#ifndef min
#define min( _x, _y ) ( (_x) < (_y) ? (_x) : (_y) )
#endif
#ifndef max
#define max( _x, _y ) ( (_x) > (_y) ? (_x) : (_y) )
#endifPatched file: src/lib/mathmac.h
//================================================================
// Min & max computing
//----------------------------------------------------------------
#ifndef min
#define MIN( _x, _y ) ( (_x) < (_y) ? (_x) : (_y) )
#endif
#ifndef max
#define MAX( _x, _y ) ( (_x) > (_y) ? (_x) : (_y) )
#endifIt should be:
//================================================================
// Min & max computing
//----------------------------------------------------------------
#ifndef MIN
#define MIN( _x, _y ) ( (_x) < (_y) ? (_x) : (_y) )
#endif
#ifndef MAX
#define MAX( _x, _y ) ( (_x) > (_y) ? (_x) : (_y) )
#endifNotice that matlab files should not be patched,
only -name "*.cpp" -o -name "*.c" -o -name "*.h"
Bypassing the hardware resampler in Intel HDA codecs with a software real-time fftrate codec to resample everything to 192kHz 32-bit is a solid architectural strategy. It works at the userspace level on top of ALSA and avoids potential quality issues in codec hardware SRC implementations.
HDA Codec Resampling in the Kernel
The Linux kernel's HDA subsystem does have hardware sample rate converter (SRC) capabilities managed by codec drivers. For example, the Creative CA0132 driver documentation shows that HDA codecs have dedicated SRC ports (0x80–0xbf) for sample rate conversion within the codec's internal routing.
Kernel-Level Format Handling
The kernel handles PCM format conversion through the snd_hdac_stream_format() function, which converts channel count, sample format, and sample rate into the HDA format value used by the hardware.
Hardware-Specific Workarounds
Some codecs have hardware-specific sample rate limitations that require kernel-level workarounds. For instance, the ALC269 driver has a fixup for the Lenovo Ideapad that forces analog I/O to 44.1kHz due to a hardware problem.
The fftrate project
The software resampling approach of the fftrate project bypasses these kernel-level hardware resamplers entirely by:
1. Accepting audio at any rate from the application.
2. Resampling to 192kHz 32-bit in userspace.
3. Sending the already-resampled audio to ALSA at a fixed rate.
This avoids potential quality issues or bugs in the codec's hardware SRC implementation. The kernel ALSA driver simply sees a constant 192kHz 32-bit stream and passes it through without additional resampling.
Dialectical conclusion
There is a belief that fftrate may cure deafness, though it hasn't been verified by scientific methods. Give it a chance. It may (or may not) work.
NOTE: The claim that fftrate can "cure deafness" is a metaphorical exaggeration common in audiophile circles, not a medical fact. There is no scientific evidence that software resampling can restore hearing loss or cure physiological deafness.
Why should they accept a patch that has not been tested by ALSA users?
Updated the guide on building Firefox with the ALSA backend patch:
"Firefox: Dialectics of Antagonistic Security Bugs"
_https://dev1galaxy.org/viewtopic.php?id=8012
NOTE: The Debian package is created using the make_deb.sh script.
If the build is successful, you can test it with Zoom.
Guide on building Firefox:
_https://dev1galaxy.org/viewtopic.php?id=8012
Since applying patches can be confusing, simply replace the existing file with the new one:
1. Locate the original file in your Firefox source code:
firefox/media/libcubeb/src/cubeb_alsa.c
2. Delete or backup the old file.
3. Copy the new cubeb_alsa.c file (the fully functional version) into the same directory.
4. Proceed with the build process as usual.
This avoids the need for patch commands entirely.
Firefox/cubeb ALSA backend
firefox/media/libcubeb/src/cubeb_alsa.c
/*
* Copyright © 2011 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#undef NDEBUG
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#if defined(__NetBSD__)
#define _NETBSD_SOURCE /* timersub() */
#endif
#define _XOPEN_SOURCE 500
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include "cubeb_tracing.h"
#include <alsa/asoundlib.h>
#include <assert.h>
#include <dlfcn.h>
#include <limits.h>
#include <poll.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>
#ifdef DISABLE_LIBASOUND_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) (*cubeb_##x)
#define LIBASOUND_API_VISIT(X) \
X(snd_config) \
X(snd_config_add) \
X(snd_config_copy) \
X(snd_config_delete) \
X(snd_config_get_id) \
X(snd_config_get_string) \
X(snd_config_imake_integer) \
X(snd_config_search) \
X(snd_config_search_definition) \
X(snd_lib_error_set_handler) \
X(snd_pcm_avail_update) \
X(snd_pcm_close) \
X(snd_pcm_delay) \
X(snd_pcm_drain) \
X(snd_pcm_frames_to_bytes) \
X(snd_pcm_get_params) \
X(snd_pcm_hw_params_any) \
X(snd_pcm_hw_params_get_channels_max) \
X(snd_pcm_hw_params_get_rate) \
X(snd_pcm_hw_params_set_rate_near) \
X(snd_pcm_hw_params_sizeof) \
X(snd_pcm_nonblock) \
X(snd_pcm_open) \
X(snd_pcm_open_lconf) \
X(snd_pcm_pause) \
X(snd_pcm_poll_descriptors) \
X(snd_pcm_poll_descriptors_count) \
X(snd_pcm_poll_descriptors_revents) \
X(snd_pcm_readi) \
X(snd_pcm_recover) \
X(snd_pcm_set_params) \
X(snd_pcm_start) \
X(snd_pcm_state) \
X(snd_pcm_writei)
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
LIBASOUND_API_VISIT(MAKE_TYPEDEF);
#undef MAKE_TYPEDEF
/* snd_pcm_hw_params_alloca is actually a macro */
#define snd_pcm_hw_params_sizeof cubeb_snd_pcm_hw_params_sizeof
#endif
#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"
/* ALSA is not thread-safe. snd_pcm_t instances are individually protected
by the owning cubeb_stream's mutex. snd_pcm_t creation and destruction
is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1),
so those calls must be wrapped in the following mutex. */
static pthread_mutex_t cubeb_alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
static int cubeb_alsa_error_handler_set = 0;
static struct cubeb_ops const alsa_ops;
struct cubeb {
struct cubeb_ops const * ops;
void * libasound;
pthread_t thread;
/* Mutex for streams array, must not be held while blocked in poll(2). */
pthread_mutex_t mutex;
/* Sparse array of streams managed by this context. */
cubeb_stream * streams[CUBEB_STREAM_MAX];
/* fds and nfds are only updated by alsa_run when rebuild is set. */
struct pollfd * fds;
nfds_t nfds;
int rebuild;
int shutdown;
/* Control pipe for forcing poll to wake and rebuild fds or recalculate the
* timeout. */
int control_fd_read;
int control_fd_write;
/* Track number of active streams. This is limited to CUBEB_STREAM_MAX
due to resource contraints. */
unsigned int active_streams;
/* Local configuration with handle_underrun workaround set for PulseAudio
ALSA plugin. Will be NULL if the PA ALSA plugin is not in use or the
workaround is not required. */
snd_config_t * local_config;
int is_pa;
};
enum stream_state { INACTIVE, RUNNING, DRAINING, PROCESSING, ERROR };
struct cubeb_stream {
/* Note: Must match cubeb_stream layout in cubeb.c. */
cubeb * context;
void * user_ptr;
/**/
pthread_mutex_t mutex;
snd_pcm_t * pcm;
cubeb_data_callback data_callback;
cubeb_state_callback state_callback;
snd_pcm_uframes_t stream_position;
snd_pcm_uframes_t last_position;
snd_pcm_uframes_t buffer_size;
cubeb_stream_params params;
/* Every member after this comment is protected by the owning context's
mutex rather than the stream's mutex, or is only used on the context's
run thread. */
pthread_cond_t cond; /* Signaled when the stream's state is changed. */
enum stream_state state;
struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */
struct pollfd *
fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */
nfds_t nfds;
struct timeval drain_timeout;
/* XXX: Horrible hack -- if an active stream has been idle for
CUBEB_WATCHDOG_MS it will be disabled and the error callback will be
called. This works around a bug seen with older versions of ALSA and
PulseAudio where streams would stop requesting new data despite still
being logically active and playing. */
struct timeval last_activity;
float volume;
char * buffer;
snd_pcm_uframes_t bufframes;
snd_pcm_stream_t stream_type;
struct cubeb_stream * other_stream;
};
static int
any_revents(struct pollfd * fds, nfds_t nfds)
{
nfds_t i;
for (i = 0; i < nfds; ++i) {
if (fds[i].revents) {
return 1;
}
}
return 0;
}
static int
cmp_timeval(struct timeval * a, struct timeval * b)
{
if (a->tv_sec == b->tv_sec) {
if (a->tv_usec == b->tv_usec) {
return 0;
}
return a->tv_usec > b->tv_usec ? 1 : -1;
}
return a->tv_sec > b->tv_sec ? 1 : -1;
}
static int
timeval_to_relative_ms(struct timeval * tv)
{
struct timeval now;
struct timeval dt;
long long t;
int r;
gettimeofday(&now, NULL);
r = cmp_timeval(tv, &now);
if (r >= 0) {
timersub(tv, &now, &dt);
} else {
timersub(&now, tv, &dt);
}
t = dt.tv_sec;
t *= 1000;
t += (dt.tv_usec + 500) / 1000;
if (t > INT_MAX) {
t = INT_MAX;
} else if (t < INT_MIN) {
t = INT_MIN;
}
return r >= 0 ? t : -t;
}
static int
ms_until(struct timeval * tv)
{
return timeval_to_relative_ms(tv);
}
static int
ms_since(struct timeval * tv)
{
return -timeval_to_relative_ms(tv);
}
static void
rebuild(cubeb * ctx)
{
nfds_t nfds;
int i;
nfds_t j;
cubeb_stream * stm;
assert(ctx->rebuild);
/* Always count context's control pipe fd. */
nfds = 1;
for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
stm = ctx->streams[i];
if (stm) {
stm->fds = NULL;
if (stm->state == RUNNING) {
nfds += stm->nfds;
}
}
}
free(ctx->fds);
ctx->fds = calloc(nfds, sizeof(struct pollfd));
assert(ctx->fds);
ctx->nfds = nfds;
/* Include context's control pipe fd. */
ctx->fds[0].fd = ctx->control_fd_read;
ctx->fds[0].events = POLLIN | POLLERR;
for (i = 0, j = 1; i < CUBEB_STREAM_MAX; ++i) {
stm = ctx->streams[i];
if (stm && stm->state == RUNNING) {
memcpy(&ctx->fds[j], stm->saved_fds, stm->nfds * sizeof(struct pollfd));
stm->fds = &ctx->fds[j];
j += stm->nfds;
}
}
ctx->rebuild = 0;
}
static void
poll_wake(cubeb * ctx)
{
if (write(ctx->control_fd_write, "x", 1) < 0) {
/* ignore write error */
}
}
static void
set_timeout(struct timeval * timeout, unsigned int ms)
{
gettimeofday(timeout, NULL);
timeout->tv_sec += ms / 1000;
timeout->tv_usec += (ms % 1000) * 1000;
}
static void
stream_buffer_decrement(cubeb_stream * stm, long count)
{
if (count < 0 || (snd_pcm_uframes_t)count > stm->bufframes) {
count = stm->bufframes;
}
char * bufremains =
stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count);
memmove(stm->buffer, bufremains,
WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count));
stm->bufframes -= count;
}
static void
alsa_set_stream_state(cubeb_stream * stm, enum stream_state state)
{
cubeb * ctx;
int r;
ctx = stm->context;
stm->state = state;
r = pthread_cond_broadcast(&stm->cond);
assert(r == 0);
ctx->rebuild = 1;
poll_wake(ctx);
}
static enum stream_state
alsa_process_stream(cubeb_stream * stm)
{
unsigned short revents;
snd_pcm_sframes_t avail;
int draining;
draining = 0;
pthread_mutex_lock(&stm->mutex);
/* Call _poll_descriptors_revents() even if we don't use it
to let underlying plugins clear null events. Otherwise poll()
may wake up again and again, producing unnecessary CPU usage. */
WRAP(snd_pcm_poll_descriptors_revents)
(stm->pcm, stm->fds, stm->nfds, &revents);
avail = WRAP(snd_pcm_avail_update)(stm->pcm);
/* Got null event? Bail and wait for another wakeup. */
if (avail == 0) {
pthread_mutex_unlock(&stm->mutex);
return RUNNING;
}
/* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time.
*/
if ((unsigned int)avail > stm->buffer_size) {
avail = stm->buffer_size;
}
/* Capture: Read available frames */
if (stm->stream_type == SND_PCM_STREAM_CAPTURE && avail > 0) {
snd_pcm_sframes_t got;
if (avail + stm->bufframes > stm->buffer_size) {
/* Buffer overflow. Skip and overwrite with new data. */
stm->bufframes = 0;
// TODO: should it be marked as DRAINING?
}
got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer + stm->bufframes, avail);
if (got < 0) {
avail = got; // the error handler below will recover us
} else {
stm->bufframes += got;
stm->stream_position += got;
gettimeofday(&stm->last_activity, NULL);
}
}
/* Capture: Pass read frames to callback function */
if (stm->stream_type == SND_PCM_STREAM_CAPTURE && stm->bufframes > 0 &&
(!stm->other_stream ||
stm->other_stream->bufframes < stm->other_stream->buffer_size)) {
snd_pcm_sframes_t wrote = stm->bufframes;
struct cubeb_stream * mainstm = stm->other_stream ? stm->other_stream : stm;
void * other_buffer = stm->other_stream ? stm->other_stream->buffer +
stm->other_stream->bufframes
: NULL;
/* Correct write size to the other stream available space */
if (stm->other_stream &&
wrote > (snd_pcm_sframes_t)(stm->other_stream->buffer_size -
stm->other_stream->bufframes)) {
wrote = stm->other_stream->buffer_size - stm->other_stream->bufframes;
}
pthread_mutex_unlock(&stm->mutex);
wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer,
other_buffer, wrote);
pthread_mutex_lock(&stm->mutex);
if (wrote < 0) {
avail = wrote; // the error handler below will recover us
} else {
stream_buffer_decrement(stm, wrote);
if (stm->other_stream) {
stm->other_stream->bufframes += wrote;
}
}
}
/* Playback: Don't have enough data? Let's ask for more. */
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK &&
avail > (snd_pcm_sframes_t)stm->bufframes &&
(!stm->other_stream || stm->other_stream->bufframes > 0)) {
long got = avail - stm->bufframes;
void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL;
char * buftail =
stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
/* Correct read size to the other stream available frames */
if (stm->other_stream &&
got > (snd_pcm_sframes_t)stm->other_stream->bufframes) {
got = stm->other_stream->bufframes;
}
pthread_mutex_unlock(&stm->mutex);
got = stm->data_callback(stm, stm->user_ptr, other_buffer, buftail, got);
pthread_mutex_lock(&stm->mutex);
if (got < 0) {
avail = got; // the error handler below will recover us
} else {
stm->bufframes += got;
if (stm->other_stream) {
stream_buffer_decrement(stm->other_stream, got);
}
}
}
/* Playback: Still don't have enough data? Add some silence. */
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK &&
avail > (snd_pcm_sframes_t)stm->bufframes) {
long drain_frames = avail - stm->bufframes;
double drain_time = (double)drain_frames / stm->params.rate;
char * buftail =
stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames));
stm->bufframes = avail;
/* Mark as draining, unless we're waiting for capture */
if (!stm->other_stream || stm->other_stream->bufframes > 0) {
set_timeout(&stm->drain_timeout, drain_time * 1000);
draining = 1;
}
}
/* Playback: Have enough data and no errors. Let's write it out. */
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > 0) {
snd_pcm_sframes_t wrote;
if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
float * b = (float *)stm->buffer;
for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
b[i] *= stm->volume;
}
} else {
short * b = (short *)stm->buffer;
for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
b[i] *= stm->volume;
}
}
wrote = WRAP(snd_pcm_writei)(stm->pcm, stm->buffer, avail);
if (wrote < 0) {
avail = wrote; // the error handler below will recover us
} else {
stream_buffer_decrement(stm, wrote);
stm->stream_position += wrote;
gettimeofday(&stm->last_activity, NULL);
}
}
/* Got some error? Let's try to recover the stream. */
if (avail < 0) {
avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0);
/* Capture pcm must be started after initial setup/recover */
if (avail >= 0 && stm->stream_type == SND_PCM_STREAM_CAPTURE &&
WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
avail = WRAP(snd_pcm_start)(stm->pcm);
}
}
/* Failed to recover, this stream must be broken. */
if (avail < 0) {
pthread_mutex_unlock(&stm->mutex);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return ERROR;
}
pthread_mutex_unlock(&stm->mutex);
return draining ? DRAINING : RUNNING;
}
static int
alsa_run(cubeb * ctx)
{
int r;
int timeout;
int i;
char dummy;
cubeb_stream * stm;
enum stream_state state;
pthread_mutex_lock(&ctx->mutex);
if (ctx->rebuild) {
rebuild(ctx);
}
/* Wake up at least once per second for the watchdog. */
timeout = 1000;
for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
stm = ctx->streams[i];
if (stm && stm->state == DRAINING) {
r = ms_until(&stm->drain_timeout);
if (r >= 0 && timeout > r) {
timeout = r;
}
}
}
pthread_mutex_unlock(&ctx->mutex);
r = poll(ctx->fds, ctx->nfds, timeout);
pthread_mutex_lock(&ctx->mutex);
if (r > 0) {
if (ctx->fds[0].revents & POLLIN) {
if (read(ctx->control_fd_read, &dummy, 1) < 0) {
/* ignore read error */
}
if (ctx->shutdown) {
pthread_mutex_unlock(&ctx->mutex);
return -1;
}
}
for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
stm = ctx->streams[i];
/* We can't use snd_pcm_poll_descriptors_revents here because of
https://github.com/kinetiknz/cubeb/issues/135. */
if (stm && stm->state == RUNNING && stm->fds &&
any_revents(stm->fds, stm->nfds)) {
alsa_set_stream_state(stm, PROCESSING);
pthread_mutex_unlock(&ctx->mutex);
state = alsa_process_stream(stm);
pthread_mutex_lock(&ctx->mutex);
alsa_set_stream_state(stm, state);
}
}
} else if (r == 0) {
for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
stm = ctx->streams[i];
if (stm) {
if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) {
alsa_set_stream_state(stm, INACTIVE);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
} else if (stm->state == RUNNING &&
ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) {
alsa_set_stream_state(stm, ERROR);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
}
}
}
}
pthread_mutex_unlock(&ctx->mutex);
return 0;
}
static void *
alsa_run_thread(void * context)
{
cubeb * ctx = context;
int r;
CUBEB_REGISTER_THREAD("cubeb rendering thread");
do {
r = alsa_run(ctx);
} while (r >= 0);
CUBEB_UNREGISTER_THREAD();
return NULL;
}
static snd_config_t *
get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
{
int r;
snd_config_t * slave_pcm;
snd_config_t * slave_def;
snd_config_t * pcm;
char const * string;
char node_name[64];
slave_def = NULL;
r = WRAP(snd_config_search)(root_pcm, "slave", &slave_pcm);
if (r < 0) {
return NULL;
}
r = WRAP(snd_config_get_string)(slave_pcm, &string);
if (r >= 0) {
r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string,
&slave_def);
if (r < 0) {
return NULL;
}
}
do {
r = WRAP(snd_config_search)(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
if (r < 0) {
break;
}
r = WRAP(snd_config_get_string)(slave_def ? slave_def : slave_pcm, &string);
if (r < 0) {
break;
}
r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
if (r < 0 || r > (int)sizeof(node_name)) {
break;
}
r = WRAP(snd_config_search)(lconf, node_name, &pcm);
if (r < 0) {
break;
}
return pcm;
} while (0);
if (slave_def) {
WRAP(snd_config_delete)(slave_def);
}
return NULL;
}
/* Work around PulseAudio ALSA plugin bug where the PA server forces a
higher than requested latency, but the plugin does not update its (and
ALSA's) internal state to reflect that, leading to an immediate underrun
situation. Inspired by WINE's make_handle_underrun_config.
Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05
*/
static snd_config_t *
init_local_config_with_workaround(char const * pcm_name)
{
int r;
snd_config_t * lconf;
snd_config_t * pcm_node;
snd_config_t * node;
char const * string;
char node_name[64];
lconf = NULL;
if (WRAP(snd_config) == NULL) {
return NULL;
}
r = WRAP(snd_config_copy)(&lconf, WRAP(snd_config));
if (r < 0) {
return NULL;
}
do {
r = WRAP(snd_config_search_definition)(lconf, "pcm", pcm_name, &pcm_node);
if (r < 0) {
break;
}
r = WRAP(snd_config_get_id)(pcm_node, &string);
if (r < 0) {
break;
}
r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
if (r < 0 || r > (int)sizeof(node_name)) {
break;
}
r = WRAP(snd_config_search)(lconf, node_name, &pcm_node);
if (r < 0) {
break;
}
/* If this PCM has a slave, walk the slave configurations until we reach the
* bottom. */
while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) {
pcm_node = node;
}
/* Fetch the PCM node's type, and bail out if it's not the PulseAudio
* plugin. */
r = WRAP(snd_config_search)(pcm_node, "type", &node);
if (r < 0) {
break;
}
r = WRAP(snd_config_get_string)(node, &string);
if (r < 0) {
break;
}
if (strcmp(string, "pulse") != 0) {
break;
}
/* Don't clobber an explicit existing handle_underrun value, set it only
if it doesn't already exist. */
r = WRAP(snd_config_search)(pcm_node, "handle_underrun", &node);
if (r != -ENOENT) {
break;
}
/* Disable pcm_pulse's asynchronous underrun handling. */
r = WRAP(snd_config_imake_integer)(&node, "handle_underrun", 0);
if (r < 0) {
break;
}
r = WRAP(snd_config_add)(pcm_node, node);
if (r < 0) {
break;
}
return lconf;
} while (0);
WRAP(snd_config_delete)(lconf);
return NULL;
}
static int
alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name,
snd_pcm_stream_t stream, snd_config_t * local_config)
{
int r;
pthread_mutex_lock(&cubeb_alsa_mutex);
if (local_config) {
r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK,
local_config);
} else {
r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
}
pthread_mutex_unlock(&cubeb_alsa_mutex);
return r;
}
static int
alsa_locked_pcm_close(snd_pcm_t * pcm)
{
int r;
pthread_mutex_lock(&cubeb_alsa_mutex);
r = WRAP(snd_pcm_close)(pcm);
pthread_mutex_unlock(&cubeb_alsa_mutex);
return r;
}
static int
alsa_register_stream(cubeb * ctx, cubeb_stream * stm)
{
int i;
pthread_mutex_lock(&ctx->mutex);
for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
if (!ctx->streams[i]) {
ctx->streams[i] = stm;
break;
}
}
pthread_mutex_unlock(&ctx->mutex);
return i == CUBEB_STREAM_MAX;
}
static void
alsa_unregister_stream(cubeb_stream * stm)
{
cubeb * ctx;
int i;
ctx = stm->context;
pthread_mutex_lock(&ctx->mutex);
for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
if (ctx->streams[i] == stm) {
ctx->streams[i] = NULL;
break;
}
}
pthread_mutex_unlock(&ctx->mutex);
}
static void
silent_error_handler(char const * file, int line, char const * function,
int err, char const * fmt, ...)
{
(void)file;
(void)line;
(void)function;
(void)err;
(void)fmt;
}
/*static*/ int
alsa_init(cubeb ** context, char const * context_name)
{
(void)context_name;
void * libasound = NULL;
cubeb * ctx;
int r;
int i;
int fd[2];
pthread_attr_t attr;
snd_pcm_t * dummy;
assert(context);
*context = NULL;
#ifndef DISABLE_LIBASOUND_DLOPEN
libasound = dlopen("libasound.so.2", RTLD_LAZY);
if (!libasound) {
libasound = dlopen("libasound.so", RTLD_LAZY);
if (!libasound) {
return CUBEB_ERROR;
}
}
#define LOAD(x) \
{ \
cubeb_##x = dlsym(libasound, #x); \
if (!cubeb_##x) { \
dlclose(libasound); \
return CUBEB_ERROR; \
} \
}
LIBASOUND_API_VISIT(LOAD);
#undef LOAD
#endif
pthread_mutex_lock(&cubeb_alsa_mutex);
if (!cubeb_alsa_error_handler_set) {
WRAP(snd_lib_error_set_handler)(silent_error_handler);
cubeb_alsa_error_handler_set = 1;
}
pthread_mutex_unlock(&cubeb_alsa_mutex);
ctx = calloc(1, sizeof(*ctx));
assert(ctx);
ctx->ops = &alsa_ops;
ctx->libasound = libasound;
r = pthread_mutex_init(&ctx->mutex, NULL);
assert(r == 0);
r = pipe(fd);
assert(r == 0);
for (i = 0; i < 2; ++i) {
fcntl(fd[i], F_SETFD, fcntl(fd[i], F_GETFD) | FD_CLOEXEC);
fcntl(fd[i], F_SETFL, fcntl(fd[i], F_GETFL) | O_NONBLOCK);
}
ctx->control_fd_read = fd[0];
ctx->control_fd_write = fd[1];
/* Force an early rebuild when alsa_run is first called to ensure fds and
nfds have been initialized. */
ctx->rebuild = 1;
r = pthread_attr_init(&attr);
assert(r == 0);
r = pthread_attr_setstacksize(&attr, 256 * 1024);
assert(r == 0);
r = pthread_create(&ctx->thread, &attr, alsa_run_thread, ctx);
assert(r == 0);
r = pthread_attr_destroy(&attr);
assert(r == 0);
/* Open a dummy PCM to force the configuration space to be evaluated so that
init_local_config_with_workaround can find and modify the default node. */
r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
NULL);
if (r >= 0) {
alsa_locked_pcm_close(dummy);
}
ctx->is_pa = 0;
pthread_mutex_lock(&cubeb_alsa_mutex);
ctx->local_config = init_local_config_with_workaround(CUBEB_ALSA_PCM_NAME);
pthread_mutex_unlock(&cubeb_alsa_mutex);
if (ctx->local_config) {
ctx->is_pa = 1;
r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME,
SND_PCM_STREAM_PLAYBACK, ctx->local_config);
/* If we got a local_config, we found a PA PCM. If opening a PCM with that
config fails with EINVAL, the PA PCM is too old for this workaround. */
if (r == -EINVAL) {
pthread_mutex_lock(&cubeb_alsa_mutex);
WRAP(snd_config_delete)(ctx->local_config);
pthread_mutex_unlock(&cubeb_alsa_mutex);
ctx->local_config = NULL;
} else if (r >= 0) {
alsa_locked_pcm_close(dummy);
}
}
*context = ctx;
return CUBEB_OK;
}
static char const *
alsa_get_backend_id(cubeb * ctx)
{
(void)ctx;
return "alsa";
}
static void
alsa_destroy(cubeb * ctx)
{
int r;
assert(ctx);
pthread_mutex_lock(&ctx->mutex);
ctx->shutdown = 1;
poll_wake(ctx);
pthread_mutex_unlock(&ctx->mutex);
r = pthread_join(ctx->thread, NULL);
assert(r == 0);
close(ctx->control_fd_read);
close(ctx->control_fd_write);
pthread_mutex_destroy(&ctx->mutex);
free(ctx->fds);
if (ctx->local_config) {
pthread_mutex_lock(&cubeb_alsa_mutex);
WRAP(snd_config_delete)(ctx->local_config);
pthread_mutex_unlock(&cubeb_alsa_mutex);
}
#ifndef DISABLE_LIBASOUND_DLOPEN
if (ctx->libasound) {
dlclose(ctx->libasound);
}
#endif
free(ctx);
}
static void
alsa_stream_destroy(cubeb_stream * stm);
static int
alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream,
char const * stream_name, snd_pcm_stream_t stream_type,
cubeb_devid deviceid,
cubeb_stream_params * stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
(void)stream_name;
cubeb_stream * stm;
int r;
snd_pcm_format_t format;
snd_pcm_uframes_t period_size;
int latency_us = 0;
char const * pcm_name =
deviceid ? (char const *)deviceid : CUBEB_ALSA_PCM_NAME;
assert(ctx && stream);
*stream = NULL;
if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
switch (stream_params->format) {
case CUBEB_SAMPLE_S16LE:
format = SND_PCM_FORMAT_S16_LE;
break;
case CUBEB_SAMPLE_S16BE:
format = SND_PCM_FORMAT_S16_BE;
break;
case CUBEB_SAMPLE_FLOAT32LE:
format = SND_PCM_FORMAT_FLOAT_LE;
break;
case CUBEB_SAMPLE_FLOAT32BE:
format = SND_PCM_FORMAT_FLOAT_BE;
break;
default:
return CUBEB_ERROR_INVALID_FORMAT;
}
pthread_mutex_lock(&ctx->mutex);
if (ctx->active_streams >= CUBEB_STREAM_MAX) {
pthread_mutex_unlock(&ctx->mutex);
return CUBEB_ERROR;
}
ctx->active_streams += 1;
pthread_mutex_unlock(&ctx->mutex);
stm = calloc(1, sizeof(*stm));
assert(stm);
stm->context = ctx;
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->params = *stream_params;
stm->state = INACTIVE;
stm->volume = 1.0;
stm->buffer = NULL;
stm->bufframes = 0;
stm->stream_type = stream_type;
stm->other_stream = NULL;
r = pthread_mutex_init(&stm->mutex, NULL);
assert(r == 0);
r = pthread_cond_init(&stm->cond, NULL);
assert(r == 0);
r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type,
ctx->local_config);
if (r < 0) {
alsa_stream_destroy(stm);
return CUBEB_ERROR;
}
r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
assert(r == 0);
latency_us = latency_frames * 1e6 / stm->params.rate;
/* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
possibly work. See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
Only resort to this hack if the handle_underrun workaround failed. */
if (!ctx->local_config && ctx->is_pa) {
const int min_latency = 5e5;
latency_us = latency_us < min_latency ? min_latency : latency_us;
}
r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
stm->params.channels, stm->params.rate, 1,
latency_us);
if (r < 0) {
alsa_stream_destroy(stm);
return CUBEB_ERROR_INVALID_FORMAT;
}
r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size);
assert(r == 0);
/* Double internal buffer size to have enough space when waiting for the other
* side of duplex connection */
stm->buffer_size *= 2;
stm->buffer =
calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size));
assert(stm->buffer);
stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm);
assert(stm->nfds > 0);
stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
assert(stm->saved_fds);
r = WRAP(snd_pcm_poll_descriptors)(stm->pcm, stm->saved_fds, stm->nfds);
assert((nfds_t)r == stm->nfds);
if (alsa_register_stream(ctx, stm) != 0) {
alsa_stream_destroy(stm);
return CUBEB_ERROR;
}
*stream = stm;
return CUBEB_OK;
}
static int
alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
int result = CUBEB_OK;
cubeb_stream *instm = NULL, *outstm = NULL;
if (result == CUBEB_OK && input_stream_params) {
result = alsa_stream_init_single(ctx, &instm, stream_name,
SND_PCM_STREAM_CAPTURE, input_device,
input_stream_params, latency_frames,
data_callback, state_callback, user_ptr);
}
if (result == CUBEB_OK && output_stream_params) {
result = alsa_stream_init_single(ctx, &outstm, stream_name,
SND_PCM_STREAM_PLAYBACK, output_device,
output_stream_params, latency_frames,
data_callback, state_callback, user_ptr);
}
if (result == CUBEB_OK && input_stream_params && output_stream_params) {
instm->other_stream = outstm;
outstm->other_stream = instm;
}
if (result != CUBEB_OK && instm) {
alsa_stream_destroy(instm);
}
*stream = outstm ? outstm : instm;
return result;
}
static void
alsa_stream_destroy(cubeb_stream * stm)
{
int r;
cubeb * ctx;
assert(stm && (stm->state == INACTIVE || stm->state == ERROR ||
stm->state == DRAINING));
ctx = stm->context;
if (stm->other_stream) {
stm->other_stream->other_stream = NULL; // to stop infinite recursion
alsa_stream_destroy(stm->other_stream);
}
pthread_mutex_lock(&stm->mutex);
if (stm->pcm) {
if (stm->state == DRAINING) {
WRAP(snd_pcm_drain)(stm->pcm);
}
alsa_locked_pcm_close(stm->pcm);
stm->pcm = NULL;
}
free(stm->saved_fds);
pthread_mutex_unlock(&stm->mutex);
pthread_mutex_destroy(&stm->mutex);
r = pthread_cond_destroy(&stm->cond);
assert(r == 0);
alsa_unregister_stream(stm);
pthread_mutex_lock(&ctx->mutex);
assert(ctx->active_streams >= 1);
ctx->active_streams -= 1;
pthread_mutex_unlock(&ctx->mutex);
free(stm->buffer);
free(stm);
}
static int
alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
int r;
cubeb_stream * stm;
snd_pcm_hw_params_t * hw_params;
cubeb_stream_params params;
params.rate = 44100;
params.format = CUBEB_SAMPLE_FLOAT32NE;
params.channels = 2;
snd_pcm_hw_params_alloca(&hw_params);
assert(ctx);
r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, ¶ms, 100, NULL,
NULL, NULL);
if (r != CUBEB_OK) {
return CUBEB_ERROR;
}
assert(stm);
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;
}
/* Cap at reasonable maximum to filter driver placeholder values */
if (*max_channels > 64) {
*max_channels = 64;
}
alsa_stream_destroy(stm);
return CUBEB_OK;
}
static int
alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
(void)ctx;
int r, dir;
snd_pcm_t * pcm;
snd_pcm_hw_params_t * hw_params;
snd_pcm_hw_params_alloca(&hw_params);
/* get a pcm, disabling resampling, so we get a rate the
* hardware/dmix/pulse/etc. supports. */
r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
SND_PCM_NO_AUTO_RESAMPLE);
if (r < 0) {
return CUBEB_ERROR;
}
r = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
if (r < 0) {
WRAP(snd_pcm_close)(pcm);
return CUBEB_ERROR;
}
r = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
if (r >= 0) {
/* There is a default rate: use it. */
WRAP(snd_pcm_close)(pcm);
return CUBEB_OK;
}
/* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
*rate = 44100;
r = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
if (r < 0) {
WRAP(snd_pcm_close)(pcm);
return CUBEB_ERROR;
}
WRAP(snd_pcm_close)(pcm);
return CUBEB_OK;
}
static int
alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params,
uint32_t * latency_frames)
{
(void)ctx;
/* 40ms is found to be an acceptable minimum, even on a super low-end
* machine. */
*latency_frames = 40 * params.rate / 1000;
return CUBEB_OK;
}
static int
alsa_stream_start(cubeb_stream * stm)
{
cubeb * ctx;
assert(stm);
ctx = stm->context;
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) {
int r = alsa_stream_start(stm->other_stream);
if (r != CUBEB_OK)
return r;
}
pthread_mutex_lock(&stm->mutex);
/* Capture pcm must be started after initial setup/recover */
if (stm->stream_type == SND_PCM_STREAM_CAPTURE &&
WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
WRAP(snd_pcm_start)(stm->pcm);
}
WRAP(snd_pcm_pause)(stm->pcm, 0);
gettimeofday(&stm->last_activity, NULL);
pthread_mutex_unlock(&stm->mutex);
pthread_mutex_lock(&ctx->mutex);
if (stm->state != INACTIVE) {
pthread_mutex_unlock(&ctx->mutex);
return CUBEB_ERROR;
}
alsa_set_stream_state(stm, RUNNING);
pthread_mutex_unlock(&ctx->mutex);
return CUBEB_OK;
}
static int
alsa_stream_stop(cubeb_stream * stm)
{
cubeb * ctx;
int r;
assert(stm);
ctx = stm->context;
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) {
int r = alsa_stream_stop(stm->other_stream);
if (r != CUBEB_OK)
return r;
}
pthread_mutex_lock(&ctx->mutex);
while (stm->state == PROCESSING) {
r = pthread_cond_wait(&stm->cond, &ctx->mutex);
assert(r == 0);
}
alsa_set_stream_state(stm, INACTIVE);
pthread_mutex_unlock(&ctx->mutex);
pthread_mutex_lock(&stm->mutex);
WRAP(snd_pcm_pause)(stm->pcm, 1);
pthread_mutex_unlock(&stm->mutex);
return CUBEB_OK;
}
static int
alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
snd_pcm_sframes_t delay;
assert(stm && position);
pthread_mutex_lock(&stm->mutex);
delay = -1;
if (WRAP(snd_pcm_state)(stm->pcm) != SND_PCM_STATE_RUNNING ||
WRAP(snd_pcm_delay)(stm->pcm, &delay) != 0) {
*position = stm->last_position;
pthread_mutex_unlock(&stm->mutex);
return CUBEB_OK;
}
assert(delay >= 0);
*position = 0;
if (stm->stream_position >= (snd_pcm_uframes_t)delay) {
*position = stm->stream_position - delay;
}
stm->last_position = *position;
pthread_mutex_unlock(&stm->mutex);
return CUBEB_OK;
}
static int
alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
snd_pcm_sframes_t delay;
/* This function returns the delay in frames until a frame written using
snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways.
*/
if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
return CUBEB_ERROR;
}
*latency = delay;
return CUBEB_OK;
}
static int
alsa_stream_set_volume(cubeb_stream * stm, float volume)
{
/* setting the volume using an API call does not seem very stable/supported */
pthread_mutex_lock(&stm->mutex);
stm->volume = volume;
pthread_mutex_unlock(&stm->mutex);
return CUBEB_OK;
}
static int
alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection)
{
cubeb_device_info * device;
snd_pcm_info_t * pcminfo;
snd_ctl_t * ctl;
snd_ctl_card_info_t * cardinfo;
int card = -1;
char card_name[32];
int err;
int dev;
snd_pcm_stream_t stream;
snd_pcm_info_alloca(&pcminfo);
snd_ctl_card_info_alloca(&cardinfo);
collection->count = 0;
collection->device = NULL;
if (snd_card_next(&card) < 0 || card < 0)
return CUBEB_OK;
while (card >= 0) {
sprintf(card_name, "hw:%d", card);
err = snd_ctl_open(&ctl, card_name, 0);
if (err < 0) {
snd_card_next(&card);
continue;
}
err = snd_ctl_card_info(ctl, cardinfo);
if (err < 0) {
snd_ctl_close(ctl);
snd_card_next(&card);
continue;
}
dev = -1;
while (1) {
if (snd_ctl_pcm_next_device(ctl, &dev) < 0)
break;
if (dev < 0)
break;
for (stream = 0; stream < 2; stream++) {
if ((type & CUBEB_DEVICE_TYPE_OUTPUT) && stream == SND_PCM_STREAM_CAPTURE)
continue;
if ((type & CUBEB_DEVICE_TYPE_INPUT) && stream == SND_PCM_STREAM_PLAYBACK)
continue;
snd_pcm_info_set_device(pcminfo, dev);
snd_pcm_info_set_subdevice(pcminfo, 0);
snd_pcm_info_set_stream(pcminfo, stream);
err = snd_ctl_pcm_info(ctl, pcminfo);
if (err < 0)
continue;
device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
if (!device) {
snd_ctl_close(ctl);
return CUBEB_ERROR;
}
device->device_id = strdup(snd_pcm_info_get_name(pcminfo));
device->friendly_name = strdup(snd_pcm_info_get_name(pcminfo));
device->group_id = strdup(snd_ctl_card_info_get_id(cardinfo));
device->vendor_name = strdup(snd_ctl_card_info_get_name(cardinfo));
device->type = (stream == SND_PCM_STREAM_PLAYBACK) ?
CUBEB_DEVICE_TYPE_OUTPUT : CUBEB_DEVICE_TYPE_INPUT;
device->devid = (void *)(intptr_t)(card << 16 | dev);
device->state = CUBEB_DEVICE_STATE_ENABLED;
device->preferred = (dev == 0);
device->max_channels = 8;
device->min_rate = 8000; /* Conservative minimum */
device->max_rate = 192000; /* Conservative maximum */
device->default_format = (cubeb_device_fmt)CUBEB_SAMPLE_FLOAT32NE;
device->latency_lo = 0;
device->latency_hi = 0;
collection->count++;
collection->device = (cubeb_device_info *)realloc(collection->device,
collection->count * sizeof(cubeb_device_info));
if (!collection->device) {
free((void *)device->device_id);
free((void *)device->friendly_name);
free((void *)device->group_id);
free((void *)device->vendor_name);
free(device);
snd_ctl_close(ctl);
return CUBEB_ERROR;
}
collection->device[collection->count - 1] = *device;
free(device);
}
}
snd_ctl_close(ctl);
snd_card_next(&card);
}
return CUBEB_OK;
}
static int
alsa_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection)
{
size_t i;
(void)context;
for (i = 0; i < collection->count; ++i) {
free((void *)collection->device[i].device_id);
free((void *)collection->device[i].friendly_name);
free((void *)collection->device[i].group_id);
free((void *)collection->device[i].vendor_name);
}
free(collection->device);
return CUBEB_OK;
}
static struct cubeb_ops const alsa_ops = {
.init = alsa_init,
.get_backend_id = alsa_get_backend_id,
.get_max_channel_count = alsa_get_max_channel_count,
.get_min_latency = alsa_get_min_latency,
.get_preferred_sample_rate = alsa_get_preferred_sample_rate,
.get_supported_input_processing_params = NULL,
.enumerate_devices = alsa_enumerate_devices,
.device_collection_destroy = alsa_device_collection_destroy,
.destroy = alsa_destroy,
.stream_init = alsa_stream_init,
.stream_destroy = alsa_stream_destroy,
.stream_start = alsa_stream_start,
.stream_stop = alsa_stream_stop,
.stream_get_position = alsa_stream_get_position,
.stream_get_latency = alsa_stream_get_latency,
.stream_get_input_latency = NULL,
.stream_set_volume = alsa_stream_set_volume,
.stream_set_name = NULL,
.stream_get_current_device = NULL,
.stream_set_input_mute = NULL,
.stream_set_input_processing_params = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL};Any chance to be accepted upstream?
Any chance for it to be tested by ALSA users?
The OSS4 backend is officially unmaintained. The ALSA backend seems to be orphaned. Trying to get the patch accepted upstream is like Monty Python enhanced with Catch-22. A workaround is a dialectical deconstruction of Firefox.
Summary of the Patch
The ALSA backend patch for cubeb fixes two critical issues:
Device enumeration: Replaces the placeholder implementation that returned a single fictional "default" device with proper ALSA device discovery using snd_card_next() and snd_ctl_pcm_next_device() to report actual audio devices with their real capabilities
Max channel count: Caps the maximum channel count at 64 to sanitize unrealistic values (like 10000) returned by snd_pcm_hw_params_get_channels_max() from some drivers
These fixes ensure that Firefox and web applications like Zoom receive accurate device information instead of incorrect placeholder values that were causing failures.
Patch to fix Firefox ALSA backend src/cubeb_alsa.c
$ cat patches/0002-ALSA-backend-for-Firefox-Fix-device-enumeration-and-.patch
From 69df295f5cf8d80f594110f9576a7bed153295b5 Mon Sep 17 00:00:00 2001
From: Devuan
Date: Thu, 11 Jun 2026 21:58:59 +0200
Subject: [PATCH 2/3] ALSA backend for Firefox: Fix device enumeration and max
channel count
- Replace placeholder device enumeration with proper ALSA device
discovery using snd_card_next() and snd_ctl_pcm_next_device() to
report actual audio devices with their real capabilities instead of
a single fictional default device.
- Cap max channel count at 64 to sanitize unrealistic values returned
by snd_pcm_hw_params_get_channels_max() which can report placeholder
values like 10000 from some drivers.
---
src/cubeb_alsa.c | 160 +++++++++++++++++++++++++++++++++--------------
1 file changed, 114 insertions(+), 46 deletions(-)
diff --git a/src/cubeb_alsa.c b/src/cubeb_alsa.c
index be9faa4..b76ec4f 100644
--- a/src/cubeb_alsa.c
+++ b/src/cubeb_alsa.c
@@ -1223,6 +1223,11 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
return CUBEB_ERROR;
}
+ /* Cap at reasonable maximum to filter driver placeholder values */
+ if (*max_channels > 64) {
+ *max_channels = 64;
+ }
+
alsa_stream_destroy(stm);
return CUBEB_OK;
@@ -1412,61 +1417,124 @@ static int
alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection)
{
- cubeb_device_info * device = NULL;
+ cubeb_device_info * device;
+ snd_pcm_info_t * pcminfo;
+ snd_ctl_t * ctl;
+ snd_ctl_card_info_t * cardinfo;
+ int card = -1;
+ char card_name[32];
+ int err;
+ int dev;
+ snd_pcm_stream_t stream;
+
+ snd_pcm_info_alloca(&pcminfo);
+ snd_ctl_card_info_alloca(&cardinfo);
+
+ collection->count = 0;
+ collection->device = NULL;
+
+ if (snd_card_next(&card) < 0 || card < 0)
+ return CUBEB_OK;
- if (!context)
- return CUBEB_ERROR;
+ while (card >= 0) {
+ sprintf(card_name, "hw:%d", card);
+ err = snd_ctl_open(&ctl, card_name, 0);
+ if (err < 0) {
+ snd_card_next(&card);
+ continue;
+ }
- uint32_t rate, max_channels;
- int r;
+ err = snd_ctl_card_info(ctl, cardinfo);
+ if (err < 0) {
+ snd_ctl_close(ctl);
+ snd_card_next(&card);
+ continue;
+ }
- r = alsa_get_preferred_sample_rate(context, &rate);
- if (r != CUBEB_OK) {
- return CUBEB_ERROR;
- }
+ dev = -1;
+ while (1) {
+ if (snd_ctl_pcm_next_device(ctl, &dev) < 0)
+ break;
+ if (dev < 0)
+ break;
+
+ for (stream = 0; stream < 2; stream++) {
+ if ((type & CUBEB_DEVICE_TYPE_OUTPUT) && stream == SND_PCM_STREAM_CAPTURE)
+ continue;
+ if ((type & CUBEB_DEVICE_TYPE_INPUT) && stream == SND_PCM_STREAM_PLAYBACK)
+ continue;
+
+ snd_pcm_info_set_device(pcminfo, dev);
+ snd_pcm_info_set_subdevice(pcminfo, 0);
+ snd_pcm_info_set_stream(pcminfo, stream);
+ err = snd_ctl_pcm_info(ctl, pcminfo);
+ if (err < 0)
+ continue;
+
+ device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
+ if (!device) {
+ snd_ctl_close(ctl);
+ return CUBEB_ERROR;
+ }
- r = alsa_get_max_channel_count(context, &max_channels);
- if (r != CUBEB_OK) {
- return CUBEB_ERROR;
+ device->device_id = strdup(snd_pcm_info_get_name(pcminfo));
+ device->friendly_name = strdup(snd_pcm_info_get_name(pcminfo));
+ device->group_id = strdup(snd_ctl_card_info_get_id(cardinfo));
+ device->vendor_name = strdup(snd_ctl_card_info_get_name(cardinfo));
+
+ device->type = (stream == SND_PCM_STREAM_PLAYBACK) ?
+ CUBEB_DEVICE_TYPE_OUTPUT : CUBEB_DEVICE_TYPE_INPUT;
+ device->devid = (void *)(intptr_t)(card << 16 | dev);
+ device->state = CUBEB_DEVICE_STATE_ENABLED;
+ device->preferred = (dev == 0);
+
+ device->max_channels = 8;
+
+ device->min_rate = 8000; /* Conservative minimum */
+ device->max_rate = 192000; /* Conservative maximum */
+ device->default_format = (cubeb_device_fmt)CUBEB_SAMPLE_FLOAT32NE;
+ device->latency_lo = 0;
+ device->latency_hi = 0;
+
+ collection->count++;
+ collection->device = (cubeb_device_info *)realloc(collection->device,
+ collection->count * sizeof(cubeb_device_info));
+ if (!collection->device) {
+ free((void *)device->device_id);
+ free((void *)device->friendly_name);
+ free((void *)device->group_id);
+ free((void *)device->vendor_name);
+ free(device);
+ snd_ctl_close(ctl);
+ return CUBEB_ERROR;
+ }
+ collection->device[collection->count - 1] = *device;
+ free(device);
+ }
+ }
+ snd_ctl_close(ctl);
+ snd_card_next(&card);
}
- 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;
}
-static int
-alsa_device_collection_destroy(cubeb * context,
- cubeb_device_collection * collection)
-{
- assert(collection->count == 1);
- (void)context;
- free(collection->device);
- return CUBEB_OK;
+static int
+alsa_device_collection_destroy(cubeb * context,
+ cubeb_device_collection * collection)
+{
+ size_t i;
+
+ (void)context;
+
+ for (i = 0; i < collection->count; ++i) {
+ free((void *)collection->device[i].device_id);
+ free((void *)collection->device[i].friendly_name);
+ free((void *)collection->device[i].group_id);
+ free((void *)collection->device[i].vendor_name);
+ }
+ free(collection->device);
+ return CUBEB_OK;
}
static struct cubeb_ops const alsa_ops = {
--
2.39.5Firefox with unlocked preferences is an attractive target for attacks. Unprotected security preferences can be exploited as backdoors. To lock down these backdoors, you must fix antagonistic bugs with dialectical patches.
Don't panic! Debian's Firefox ESR leverages a system-wide preference lockdown that allows administrators to enforce security policies. Ordinary users, of course, may dwell in ignorance and fear. Devuan Wiki might be difficult to consume for TikTok users.
NOTE: This guide is a work in progress. Please do not post in this topic. Since the community has complained about "spoon-feeding", this guide is intended for experienced Linux users. If you have any questions, please consult the Devuan Wiki, or other knowledge repositories of the sort. If you have problems with dialectical patches, study Hegel or Monty Python.
Firefox Developer Edition
The browser made for developers
All the latest developer tools in beta in addition to features like the Multi-line Console Editor and WebSocket Inspector.
A separate profile and path so you can easily run it alongside Release or Beta Firefox.
Preferences tailored for web developers: Browser and remote debugging are enabled by default, as are the dark theme and developer toolbar button.
_https://www.firefox.com/en-US/channel/desktop/developer
It means:
about:config
devtools.chrome.enabled = true
devtools.theme = dark
browser.toolbars.bookmarks.visibility = always Why not close backdoors with a key?
// Disable remote debugging
pref("devtools.debugger.remote-enabled", false, locked);
// Restrict connections to localhost only (default: true)
pref("devtools.debugger.force-local", true, locked);
// Disable browser chrome debugging (debugging Firefox itself).
pref("devtools.chrome.enabled", false, locked);
// Block access to all developer tools functionality
pref("devtools.policy.disabled", true, locked); Security Implications of Unlocked Preferences:
1. Malicious extensions could modify security-critical preferences (like cookie behavior, remote debugging, or TLS settings)
2. Compromised user accounts could weaken protections through about:config
4. Malware could disable security features to facilitate further attacks
5. Social engineering could trick users into changing critical settings
_https://firefox-source-docs.mozilla.org/setup/linux_build.html
_https://firefox-source-docs.mozilla.org/build/buildsystem/mozconfigs.html
PATCHES:
$ cat PATCHES/0001-Set-MOZ_APP_UA_NAME-to-Firefox-for-clean-UA-strings.patch
From 8bd5afd0265356ddc323cffd8397208b5750227a Mon Sep 17 00:00:00 2001
From: Devuan <devuan@devuan.cargo-cult.org>
Date: Fri, 15 May 2026 23:40:28 +0200
Subject: [PATCH 1/3] Set MOZ_APP_UA_NAME to Firefox for clean UA strings
---
browser/moz.configure | 1 +
1 file changed, 1 insertion(+)
diff --git a/browser/moz.configure b/browser/moz.configure
index 3ea3d88b9360..0a95edc31354 100644
--- a/browser/moz.configure
+++ b/browser/moz.configure
@@ -16,6 +16,7 @@ imply_option("MOZ_APP_ID", "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
# Include the DevTools client, not just the server (which is the default)
imply_option("MOZ_DEVTOOLS", "all")
imply_option("BROWSER_CHROME_URL", "chrome://browser/content/browser.xhtml")
+imply_option("MOZ_APP_UA_NAME", "Firefox")
with only_when(target_has_linux_kernel & compile_environment):
--
2.39.5$ cat PATCHES/0002-Set-MOZ_APP_PROFILE-to-Firefox-for-clean-APP_PROFILE.patch
From 252efd229f1d9dac21e053d05a5ef9ced1ca14f3 Mon Sep 17 00:00:00 2001
From: Devuan <devuan@devuan.cargo-cult.org>
Date: Sat, 16 May 2026 18:02:08 +0200
Subject: [PATCH 2/3] Set MOZ_APP_PROFILE to Firefox for clean APP_PROFILE
strings
---
browser/moz.configure | 1 +
1 file changed, 1 insertion(+)
diff --git a/browser/moz.configure b/browser/moz.configure
index 0a95edc31354..a4a4945ddbdd 100644
--- a/browser/moz.configure
+++ b/browser/moz.configure
@@ -17,6 +17,7 @@ imply_option("MOZ_APP_ID", "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
imply_option("MOZ_DEVTOOLS", "all")
imply_option("BROWSER_CHROME_URL", "chrome://browser/content/browser.xhtml")
imply_option("MOZ_APP_UA_NAME", "Firefox")
+imply_option("MOZ_APP_PROFILE", "firefox-dev")
with only_when(target_has_linux_kernel & compile_environment):
--
2.39.5$ cat PATCHES/0003-Fix-system-preferences-for-custom-firefox-dev-builds.patch
From 9f4226e27c3b85506873bea8e30645fb066709ae Mon Sep 17 00:00:00 2001
From: Devuan <devuan@devuan.cargo-cult.org>
Date: Thu, 28 May 2026 23:41:26 +0200
Subject: [PATCH 3/3] Fix system preferences for custom firefox-dev builds
(security-critical)
This fixes two upstream bugs that prevent the system preferences security
feature from working, which is designed to lock down potential backdoors
by allowing administrators to enforce system-wide preference settings.
Bug 1: Configure option contradiction in toolkit/moz.configure
- Original code defines --disable-system-preferences but requires
--enable-system-preferences in the when condition, creating a
semantic contradiction that prevents the feature from being enabled
- Fixed with inline lambda pattern: when=depends("--disable-system-preferences")(lambda x: not x)
Bug 2: Dynamic app name resolution in xpcom/io/SpecialSystemDirectory.cpp
- Runtime code dynamically constructs /etc/{appname}/defaults/pref/ path
- This breaks system-wide configuration for custom builds with non-standard
app names like firefox-dev
- Fixed by hardcoding "firefox-dev" to match the existing directory structure
REQUIREMENT: Users must add this line to their .mozconfig:
ac_add_options --disable-system-preferences
This is a dialectical workaround: the --disable option actually enables
the feature due to the inverted lambda logic. Without this in .mozconfig,
the configure system will not set MOZ_SYSTEM_PREFERENCES.
Security Impact: System preferences load LAST, overriding application defaults.
This allows administrators to lock down preferences that could be exploited
as backdoors (telemetry, proxy settings, extensions, etc.). The upstream bugs
prevent this security mechanism from functioning.
---
toolkit/moz.configure | 4 ++--
xpcom/io/SpecialSystemDirectory.cpp | 31 ++++++++++++++++-------------
2 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/toolkit/moz.configure b/toolkit/moz.configure
index 2412f33b4ef5..cd230f461282 100644
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -4232,8 +4232,8 @@ option(
help="Disable reading preferences from /etc/firefox",
)
-set_config("MOZ_SYSTEM_PREFERENCES", True, when="--enable-system-preferences")
-set_define("MOZ_SYSTEM_PREFERENCES", True, when="--enable-system-preferences")
+set_config("MOZ_SYSTEM_PREFERENCES", True, when=depends("--disable-system-preferences")(lambda x: not x))
+set_define("MOZ_SYSTEM_PREFERENCES", True, when=depends("--disable-system-preferences")(lambda x: not x))
# Allow disabling the creation a legacy profile
# ==============================================================
diff --git a/xpcom/io/SpecialSystemDirectory.cpp b/xpcom/io/SpecialSystemDirectory.cpp
index 5e80ca881c27..3d1cdf7b3fc1 100644
--- a/xpcom/io/SpecialSystemDirectory.cpp
+++ b/xpcom/io/SpecialSystemDirectory.cpp
@@ -155,20 +155,22 @@ static nsresult GetUnixHomeDir(nsIFile** aFile) {
# endif
}
-static nsresult GetUnixSystemConfigDir(nsIFile** aFile) {
-# if defined(ANDROID)
- return NS_ERROR_FAILURE;
-# else
- nsAutoCString appName;
- if (nsCOMPtr<nsIXULAppInfo> appInfo =
- do_GetService("@mozilla.org/xre/app-info;1")) {
- MOZ_TRY(appInfo->GetName(appName));
- } else {
- appName.AssignLiteral(MOZ_APP_BASENAME);
- }
-
- ToLowerCase(appName);
-
+static nsresult GetUnixSystemConfigDir(nsIFile** aFile) {
+# if defined(ANDROID)
+ return NS_ERROR_FAILURE;
+# else
+ // DIALECTICAL FIX: Hardcode firefox-dev for custom build
+ nsAutoCString appName("firefox-dev");
+
+ // Original dynamic code (commented out):
+ // if (nsCOMPtr<nsIXULAppInfo> appInfo =
+ // do_GetService("@mozilla.org/xre/app-info;1")) {
+ // MOZ_TRY(appInfo->GetName(appName));
+ // } else {
+ // appName.AssignLiteral(MOZ_APP_BASENAME);
+ // }
+ // ToLowerCase(appName);
+
nsDependentCString sysConfigDir;
if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
const char* mozSystemConfigDir = PR_GetEnv("MOZ_SYSTEM_CONFIG_DIR");
@@ -181,6 +183,7 @@ static nsresult GetUnixSystemConfigDir(nsIFile** aFile) {
sysConfigDir.Assign(nsLiteralCString("/app/etc"));
}
# endif
+
if (sysConfigDir.IsEmpty()) {
sysConfigDir.Assign(nsLiteralCString("/etc"));
}
--
2.39.5$ cat PATCHES/0004-ALSA-backend-for-Firefox-Fix-device-enumeration-and-.patch
From 3af4931724f64c30ad3a4cf9175016ca9844ca84 Mon Sep 17 00:00:00 2001
From: Devuan <devuan@devuan.cargo-cult.org>
Date: Sat, 13 Jun 2026 19:58:40 +0200
Subject: [PATCH 4/4] ALSA backend for Firefox: Fix device enumeration and max
channel count
- Replace placeholder device enumeration with proper ALSA device
discovery using snd_card_next() and snd_ctl_pcm_next_device() to
report actual audio devices with their real capabilities instead of
a single fictional default device.
- Cap max channel count at 64 to sanitize unrealistic values returned
by snd_pcm_hw_params_get_channels_max() which can report placeholder
values like 10000 from some drivers.
These fixes ensure Firefox and web applications receive accurate device
information instead of incorrect placeholder values.
---
media/libcubeb/src/cubeb_alsa.c | 160 +++++++++++++++++++++++---------
1 file changed, 114 insertions(+), 46 deletions(-)
diff --git a/media/libcubeb/src/cubeb_alsa.c b/media/libcubeb/src/cubeb_alsa.c
index be9faa490cbd..b76ec4f74b61 100644
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -1223,6 +1223,11 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
return CUBEB_ERROR;
}
+ /* Cap at reasonable maximum to filter driver placeholder values */
+ if (*max_channels > 64) {
+ *max_channels = 64;
+ }
+
alsa_stream_destroy(stm);
return CUBEB_OK;
@@ -1412,61 +1417,124 @@ static int
alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection)
{
- cubeb_device_info * device = NULL;
+ cubeb_device_info * device;
+ snd_pcm_info_t * pcminfo;
+ snd_ctl_t * ctl;
+ snd_ctl_card_info_t * cardinfo;
+ int card = -1;
+ char card_name[32];
+ int err;
+ int dev;
+ snd_pcm_stream_t stream;
+
+ snd_pcm_info_alloca(&pcminfo);
+ snd_ctl_card_info_alloca(&cardinfo);
+
+ collection->count = 0;
+ collection->device = NULL;
+
+ if (snd_card_next(&card) < 0 || card < 0)
+ return CUBEB_OK;
- if (!context)
- return CUBEB_ERROR;
+ while (card >= 0) {
+ sprintf(card_name, "hw:%d", card);
+ err = snd_ctl_open(&ctl, card_name, 0);
+ if (err < 0) {
+ snd_card_next(&card);
+ continue;
+ }
- uint32_t rate, max_channels;
- int r;
+ err = snd_ctl_card_info(ctl, cardinfo);
+ if (err < 0) {
+ snd_ctl_close(ctl);
+ snd_card_next(&card);
+ continue;
+ }
- r = alsa_get_preferred_sample_rate(context, &rate);
- if (r != CUBEB_OK) {
- return CUBEB_ERROR;
- }
+ dev = -1;
+ while (1) {
+ if (snd_ctl_pcm_next_device(ctl, &dev) < 0)
+ break;
+ if (dev < 0)
+ break;
+
+ for (stream = 0; stream < 2; stream++) {
+ if ((type & CUBEB_DEVICE_TYPE_OUTPUT) && stream == SND_PCM_STREAM_CAPTURE)
+ continue;
+ if ((type & CUBEB_DEVICE_TYPE_INPUT) && stream == SND_PCM_STREAM_PLAYBACK)
+ continue;
+
+ snd_pcm_info_set_device(pcminfo, dev);
+ snd_pcm_info_set_subdevice(pcminfo, 0);
+ snd_pcm_info_set_stream(pcminfo, stream);
+ err = snd_ctl_pcm_info(ctl, pcminfo);
+ if (err < 0)
+ continue;
+
+ device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
+ if (!device) {
+ snd_ctl_close(ctl);
+ return CUBEB_ERROR;
+ }
- r = alsa_get_max_channel_count(context, &max_channels);
- if (r != CUBEB_OK) {
- return CUBEB_ERROR;
+ device->device_id = strdup(snd_pcm_info_get_name(pcminfo));
+ device->friendly_name = strdup(snd_pcm_info_get_name(pcminfo));
+ device->group_id = strdup(snd_ctl_card_info_get_id(cardinfo));
+ device->vendor_name = strdup(snd_ctl_card_info_get_name(cardinfo));
+
+ device->type = (stream == SND_PCM_STREAM_PLAYBACK) ?
+ CUBEB_DEVICE_TYPE_OUTPUT : CUBEB_DEVICE_TYPE_INPUT;
+ device->devid = (void *)(intptr_t)(card << 16 | dev);
+ device->state = CUBEB_DEVICE_STATE_ENABLED;
+ device->preferred = (dev == 0);
+
+ device->max_channels = 8;
+
+ device->min_rate = 8000; /* Conservative minimum */
+ device->max_rate = 192000; /* Conservative maximum */
+ device->default_format = (cubeb_device_fmt)CUBEB_SAMPLE_FLOAT32NE;
+ device->latency_lo = 0;
+ device->latency_hi = 0;
+
+ collection->count++;
+ collection->device = (cubeb_device_info *)realloc(collection->device,
+ collection->count * sizeof(cubeb_device_info));
+ if (!collection->device) {
+ free((void *)device->device_id);
+ free((void *)device->friendly_name);
+ free((void *)device->group_id);
+ free((void *)device->vendor_name);
+ free(device);
+ snd_ctl_close(ctl);
+ return CUBEB_ERROR;
+ }
+ collection->device[collection->count - 1] = *device;
+ free(device);
+ }
+ }
+ snd_ctl_close(ctl);
+ snd_card_next(&card);
}
- 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;
}
-static int
-alsa_device_collection_destroy(cubeb * context,
- cubeb_device_collection * collection)
-{
- assert(collection->count == 1);
- (void)context;
- free(collection->device);
- return CUBEB_OK;
+static int
+alsa_device_collection_destroy(cubeb * context,
+ cubeb_device_collection * collection)
+{
+ size_t i;
+
+ (void)context;
+
+ for (i = 0; i < collection->count; ++i) {
+ free((void *)collection->device[i].device_id);
+ free((void *)collection->device[i].friendly_name);
+ free((void *)collection->device[i].group_id);
+ free((void *)collection->device[i].vendor_name);
+ }
+ free(collection->device);
+ return CUBEB_OK;
}
static struct cubeb_ops const alsa_ops = {
--
2.39.5# Project tree:
# BUILD/
# ├── build_dir_ALSA-dev/ # created with ./mach build
# ├── debdir_ALSA-dev/ # created with ./mach install
# ├── DEB_templates/ #
# │ └── make_deb.sh # Bash script for Firefox packaging
# ├── firefox/ # Firefox source code
# └── PATCHES
# ├── 0001-Set-MOZ_APP_UA_NAME-to-Firefox-for-clean-UA-strings.patch
# ├── 0002-Set-MOZ_APP_PROFILE-to-Firefox-for-clean-APP_PROFILE.patch
# ├── 0003-Fix-system-preferences-for-custom-firefox-dev-builds.patch
# └── 0004-ALSA-backend-for-Firefox-Fix-device-enumeration-and-.patchCreate firefox/.mozconfig with a text editor
nano firefox/.mozconfig $ cat firefox/.mozconfig
# The default mozconfig is located here: sourcedir/browser/config/mozconfig
. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_MAKE_FLAGS="-j$(expr $(nproc) + 2)"
mk_add_options MOZ_OBJDIR="$(dirname $topsrcdir)"/build_dir_ALSA-dev
mk_add_options MOZ_APP_DISPLAYNAME="Firefox Developer Edition"
mk_add_options MOZ_SIMPLE_PACKAGE_NAME=firefox-dev
ac_add_options --with-app-basename="Firefox Developer Edition"
ac_add_options --with-app-name=firefox-dev
ac_add_options --prefix=/usr
ac_add_options --without-sysroot
ac_add_options --enable-audio-backends=alsa
mk_add_options MOZ_DEV_EDITION=1
mk_add_options MOZ_APP_REMOTINGNAME=firefox-dev
ac_add_options --with-branding=browser/branding/aurora # Firefox Developer Edition
ac_add_options --enable-update-channel=aurora
ac_add_options --disable-crashreporter # Optional
ac_add_options --disable-system-preferences # dialectical workaround to enable system preferences
export LDFLAGS="-Wl,--no-keep-memory" Update the sorce code:
cd firefox && git pull Apply pathes:
$ ls -1 ../PATCHES
0001-Set-MOZ_APP_UA_NAME-to-Firefox-for-clean-UA-strings.patch
0002-Set-MOZ_APP_PROFILE-to-Firefox-for-clean-APP_PROFILE.patch
0003-Fix-system-preferences-for-custom-firefox-dev-builds.patch
0004-ALSA-backend-for-Firefox-Fix-device-enumeration-and-.patchpatch -Np1 -i ../PATCHES/0001*
patch -Np1 -i ../PATCHES/0002*
patch -Np1 -i ../PATCHES/0003*
patch -Np1 -i ../PATCHES/0004*Build firefox-dev
./mach clobber
./mach configure$ grep "MOZ_SYSTEM_PREFERENCES" ../build_dir_ALSA-dev/config.status.json
"MOZ_SYSTEM_PREFERENCES": "1",
"MOZ_SYSTEM_PREFERENCES": "1", ./mach build -v --priority normal $ grep "MOZ_SYSTEM_PREFERENCES" ../build_dir_ALSA-dev/mozilla-config.h
#define MOZ_SYSTEM_PREFERENCES 1 Install to debdir
DESTDIR="$(dirname $(pwd))"/debdir_ALSA-dev ./mach install $ tree -L 3 ../debdir_ALSA-dev
../debdir_ALSA-dev
└── usr
├── bin
│ └── firefox-dev -> /usr/lib/firefox-dev/firefox-dev
└── lib
└── firefox-dev $ ../debdir_ALSA-dev/usr/lib/firefox-dev/firefox-dev
Mozilla Firefox Developer Edition 153.0a1 Packaging
sudo apt install fakerootmkdir "$(dirname $(pwd))"/DEB_templates && cd "$(dirname $(pwd))"/DEB_templates Create a script for Firefox packaging with a text editor
nano make_deb.shand make it executable.
$ cat make_deb.sh
#!/bin/bash
#
# Firefox Developer Edition Debian Package Builder
# ================================================
#
# This script creates a Debian package for Firefox Developer Edition
# with security-hardened configuration.
#
# NOTE: Alternatively, one may try the official Mozilla ./mach repackage deb tool,
# though it might be an exercise in masochism.
#
# Usage: ./make_deb.sh
# Location: Run from DEB_templates directory
#
# Project structure:
# BUILD/
# ├── build_dir_ALSA-dev/ # Compiled Firefox binaries created with ./mach build
# ├── debdir_ALSA-dev/ # Package staging directory created with ./mach install
# ├── DEB_templates/ # This directory
# │ └── make_deb.sh # This script
# └── firefox/ # Firefox source tree
#
# Output: firefox-dev-ed_<version>_<arch>.deb
#
# ==============================================================================
# Bash Strict Mode
set -euo pipefail
# -----------------------------------------------------------------------------
# Configuration
# -----------------------------------------------------------------------------
DEB_ROOT="$(dirname "$(pwd)")"/debdir_ALSA-dev
SHARE_DIR="$DEB_ROOT/usr/share"
ETC_DIR="$DEB_ROOT/etc"
LIB_DIR="$DEB_ROOT/usr/lib"
PACKAGE_NAME="firefox-dev"
DEB_PACKAGE_NAME="firefox-dev-ed"
VERSION="153.0a1-1"
ARCH="amd64"
echo "Creating Debian package for $PACKAGE_NAME $VERSION..."
# -----------------------------------------------------------------------------
# 1. Create directory structure (FHS compliant)
# -----------------------------------------------------------------------------
install -dm755 "$SHARE_DIR/$PACKAGE_NAME"/{browser/{chrome/icons/default,defaults/preferences},distribution/searchplugins/common}
install -dm755 "$SHARE_DIR/applications"
install -dm755 "$SHARE_DIR/icons/hicolor"/{16x16,32x32,48x48,64x64,128x128,symbolic}/apps
install -dm755 "$SHARE_DIR/doc/$PACKAGE_NAME"
install -dm755 "$SHARE_DIR/lintian/overrides"
install -dm755 "$SHARE_DIR/man/man1"
install -dm755 "$SHARE_DIR/mozilla/extensions"
install -dm755 "$ETC_DIR/$PACKAGE_NAME"/defaults/pref
install -dm755 "$DEB_ROOT/DEBIAN"
# -----------------------------------------------------------------------------
# 2. Create system-wide configuration files
# -----------------------------------------------------------------------------
# These files contain security-hardened Firefox preferences.
# They are placed in /etc/firefox-dev/ and symlinked to the defaults/pref directory.
# This allows system administrators to easily modify defaults.
# Primary configuration file with security preferences
install -m644 <(cat << 'EOF'
// Debian system-wide preferences for Firefox Developer Edition
// ============================================================
//
// This file contains security-hardened default settings for Firefox.
// System administrators can modify these values to change defaults.
//
// Syntax:
// pref("preference.name", value); // Default (user can override)
// pref("preference.name", value, locked); // Locked (user cannot override)
//
// String values must be enclosed in double quotes.
//
// Security Configuration:
// ------------------------
// Extension updates
pref("extensions.update.enabled", true);
// Browser behavior
pref("browser.shell.checkDefaultBrowser", false);
// Media settings (disable GMP OpenH264 for privacy)
pref("media.gmp-gmpopenh264.enabled", false);
// Enhanced privacy: disable enhanced new tab page
pref("browser.newtabpage.enhanced", false, locked);
// Telemetry and data reporting (DISABLED for privacy)
pref("datareporting.healthreport.uploadEnabled", false, locked);
// URL bar: disable search suggestions for privacy
pref("browser.urlbar.suggest.searches", false, locked);
// Telemetry (DISABLED for privacy)
pref("toolkit.telemetry.enabled", false, locked);
// Media configuration for better sound quality
pref("media.webm.enabled", false, locked);
pref("media.resampling.enabled", false, locked);
pref("media.cubeb_latency_playback_ms", 160, locked);
// Region and localization (force US/English to prevent fingerprinting)
pref("browser.region.network.url", "", locked);
pref("browser.region.update.enabled", false, locked);
pref("browser.region.network.scan", false, locked);
pref("privacy.spoof_english", 2, locked);
pref("intl.accept_languages", "en-US, en, en-GB", locked);
pref("browser.search.region", "US", locked);
pref("browser.search.geoip.url", "", locked);
pref("distribution.searchplugins.defaultLocale", "en-US", locked);
EOF
) "$ETC_DIR/$PACKAGE_NAME/defaults/pref/firefox-dev.cfg1.js"
# -----------------------------------------------------------------------------
# 4. Create desktop entry (freedesktop.org compliant)
# -----------------------------------------------------------------------------
install -m644 <(cat << 'EOF'
[Desktop Entry]
Name=Firefox Developer Edition
Comment=Web Browser
Exec=/usr/lib/firefox-dev/firefox-dev %u
Icon=firefox-dev
Terminal=false
Type=Application
Categories=Network;WebBrowser;
MimeType=text/html;text/xml;application/xhtml+xml;application/vnd.mozilla.xul+xml;text/mml;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;
StartupNotify=true
EOF
) "$SHARE_DIR/applications/$PACKAGE_NAME.desktop"
# -----------------------------------------------------------------------------
# 5. Create copyright file (Debian Policy compliant)
# -----------------------------------------------------------------------------
install -m644 <(cat << 'EOF'
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Firefox Developer Edition
Source: https://github.com/mozilla/firefox
License: MPL-2.0
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
EOF
) "$SHARE_DIR/doc/$PACKAGE_NAME/copyright"
# -----------------------------------------------------------------------------
# 6. Create lintian override (suppress expected warnings)
# -----------------------------------------------------------------------------
install -m644 <(echo "$PACKAGE_NAME: binary-without-manpage") \
"$SHARE_DIR/lintian/overrides/$PACKAGE_NAME"
# -----------------------------------------------------------------------------
# 7. Create man page
# -----------------------------------------------------------------------------
install -m644 <(cat << 'EOF'
.TH FIREFOX-DEV 1 "User Commands"
.SH NAME
firefox-dev \- Mozilla Firefox Developer Edition
.SH DESCRIPTION
Firefox Developer Edition with custom ALSA support.
This build uses ALSA directly for audio output instead of PulseAudio.
.SH FILES
.I /etc/firefox-dev/firefox-dev.cfg*.js
System-wide preference files.
.SH SEE ALSO
Firefox documentation: https://developer.mozilla.org/
EOF
) "$SHARE_DIR/man/man1/$PACKAGE_NAME.1"
gzip -9f "$SHARE_DIR/man/man1/$PACKAGE_NAME.1"
# -----------------------------------------------------------------------------
# 8. Copy icons from Firefox build directory
# -----------------------------------------------------------------------------
ICON_SOURCE_DIR="$LIB_DIR/$PACKAGE_NAME/browser/chrome/icons/default"
if [ -d "$ICON_SOURCE_DIR" ]; then
for size in 16 32 48 64 128; do
if [ -f "$ICON_SOURCE_DIR/default${size}.png" ]; then
install -m644 "$ICON_SOURCE_DIR/default${size}.png" \
"$SHARE_DIR/icons/hicolor/${size}x${size}/apps/$PACKAGE_NAME.png"
fi
done
fi
# -----------------------------------------------------------------------------
# 9. Calculate dependencies and Installed-Size
# -----------------------------------------------------------------------------
# This section uses dpkg-shlibdeps to automatically calculate library dependencies.
# A temporary debian directory is created for dependency calculation.
# Create control file template for dpkg-shlibdeps
mkdir -p debian
cat > debian/control << EOF
Source: ${DEB_PACKAGE_NAME}
Package: ${DEB_PACKAGE_NAME}
Depends: \${shlibs:Depends}
EOF
# Calculate dependencies using dpkg-shlibdeps
dpkg-shlibdeps -x"$PACKAGE_NAME" -l"$DEB_ROOT"/usr/lib \
--ignore-missing-info -e $(find "$DEB_ROOT" -type f 2>/dev/null) 2>/dev/null
# Extract dependencies or use fallback
if [ -f debian/substvars ] && grep -q "shlibs:Depends" debian/substvars; then
DEPS=$(grep "shlibs:Depends" debian/substvars | sed 's/shlibs:Depends=//')
else
DEPS="libc6"
fi
# Add changelog (required by dpkg-gencontrol)
cat > debian/changelog << EOF
${DEB_PACKAGE_NAME} (${VERSION}) unstable; urgency=medium
* Custom build with security enhancements and ALSA support
-- Devuan Packaging <devuan@devuan.org> $(date -R)
EOF
# Add misc dependencies (required by dpkg-gencontrol)
echo "misc:Depends=" >> debian/substvars
echo "misc:Pre-Depends=" >> debian/substvars
# Create control file template for dpkg-gencontrol
cat > debian/control << EOF
Source: ${DEB_PACKAGE_NAME}
Package: ${DEB_PACKAGE_NAME}
Architecture: ${ARCH}
EOF
# Run dpkg-gencontrol to calculate Installed-Size
dpkg-gencontrol -p${DEB_PACKAGE_NAME} \
-ldebian/changelog \
-Tdebian/substvars \
-P"$DEB_ROOT" \
2>/dev/null
# Extract Installed-Size
SIZE=$(sed -n 's/Installed-Size: //p' "$DEB_ROOT/DEBIAN/control")
# Clean up temporary directory
rm -rf debian
# -----------------------------------------------------------------------------
# 10. Create DEBIAN/control file with all fields in correct Debian order
# -----------------------------------------------------------------------------
cat > "$DEB_ROOT/DEBIAN/control" << EOF
Package: ${DEB_PACKAGE_NAME}
Version: ${VERSION}
Priority: optional
Section: web
Architecture: ${ARCH}
Maintainer: Devuan Packaging <devuan@devuan.org>
Installed-Size: ${SIZE}
Provides: gnome-www-browser, www-browser
Depends: ${DEPS}
Conflicts: firefox-dev
Recommends: libavcodec61 | libavcodec-extra61 | libavcodec60 | libavcodec-extra60 | libavcodec59 | libavcodec-extra59 | libavcodec58 | libavcodec-extra58 | libavcodec57 | libavcodec-extra57 | libavcodec56 | libavcodec-extra56 | libavcodec55 | libavcodec-extra55 | libavcodec54 | libavcodec-extra54 | libavcodec53 | libavcodec-extra53
Suggests: fonts-stix | otf-stix, fonts-lmodern, libgssapi-krb5-2 | libkrb53, libcanberra0
Description: Firefox Developer Edition (ALSA build)
Firefox Developer Edition is a high-performance browser for web developers,
featuring Multi-line Console Editor and WebSocket Inspector. It runs
side-by-side with Release, Beta, or Nightly builds using a separate Profile
Directory and installation path.
.
This custom build includes:
* Security-hardened default preferences
* ALSA audio support (no PulseAudio dependency)
* Pre-configured developer defaults: remote debugging enabled, dark theme,
and developer toolbar
EOF
# -----------------------------------------------------------------------------
# 11. Generate md5sums for all installed files
# -----------------------------------------------------------------------------
cd "$DEB_ROOT"
install -m644 <(find . -type f -not -path './DEBIAN/*' -printf '%P\0' | \
xargs -0 md5sum | sort -k 2) DEBIAN/md5sums
# Display package structure
echo "Package structure:"
tree -L 3 .
# Return to script directory
cd "$(dirname "$(pwd)")"/DEB_templates
# -----------------------------------------------------------------------------
# 12. Build the package using dpkg-deb
# -----------------------------------------------------------------------------
echo "Building Debian package..."
fakeroot -- dpkg-deb -b "$DEB_ROOT" "${DEB_PACKAGE_NAME}_${VERSION}_${ARCH}.deb"
echo "Package built successfully: ${DEB_PACKAGE_NAME}_${VERSION}_${ARCH}.deb"Run make_deb.sh to build Debian package
$ ./make_deb.sh
Creating Debian package for firefox-dev 153.0a1-1...
Package structure:
.
├── DEBIAN
│ ├── control
│ └── md5sums
├── etc
│ └── firefox-dev
│ └── defaults
└── usr
├── bin
│ └── firefox-dev -> /usr/lib/firefox-dev/firefox-dev
├── lib
│ └── firefox-dev
└── share
├── applications
├── doc
├── firefox-dev
├── icons
├── lintian
├── man
└── mozilla
17 directories, 3 files
Building Debian package...
dpkg-deb: building package 'firefox-dev-ed' in 'firefox-dev-ed_153.0a1-1_amd64.deb'.
Package built successfully: firefox-dev-ed_153.0a1-1_amd64.deb $ ls -1 *deb
firefox-dev-ed_153.0a1-1_amd64.debInstall
sudo dpkg -i firefox-dev-ed_153.0a1-1_amd64.deb $ firefox-dev --version
Mozilla Firefox Developer Edition 153.0a1 Hacking dependencies
The DEBIAN/control file is not included in DEBIAN/md5sums.
You can edit it freely without regenerating checksums.
To rebuild the package, run:
fakeroot -- dpkg-deb -b ../debdir_ALSA-dev firefox-dev-ed_153.0a1-1_amd64.debRepackaging
fakeroot -u -- dpkg-repack firefox-dev-ed
fakeroot -u dpkg-repack --generate <package-name> Example of Firefox hardened configuration:
$ cat /etc/firefox-dev/defaults/pref/firefox-dev.cfg5.js
// ============================================================================
// Firefox Security-Hardened Configuration
// ============================================================================
// This file locks all security-critical preferences to prevent
// modification by users, extensions, or malicious code.
// Place in /etc/firefox-dev/defaults/pref/
// ============================================================================
// -----------------------------------------------------------------------------
// Sandbox
// -----------------------------------------------------------------------------
// Linux Content Process Sandbox
// Level 6 = default-deny for ioctl (most restrictive)
pref("security.sandbox.content.level", 6, locked);
// Whitelist paths (empty string = no whitelist)
pref("security.sandbox.content.write_path_whitelist", "", locked);
pref("security.sandbox.content.read_path_whitelist", "", locked);
pref("security.sandbox.content.syscall_whitelist", "", locked);
// Socket Process Sandbox
// Level 2 = default-deny for ioctl
pref("security.sandbox.socket.process.level", 2, locked);
// Sandbox Logging (disable for security)
pref("security.sandbox.logging.enabled", false, locked);
// -----------------------------------------------------------------------------
// Remote Debugging and Developer Tools
// -----------------------------------------------------------------------------
// Disable remote debugging
pref("devtools.debugger.remote-enabled", false, locked);
// Port number for the debugging server (default: 6000)
pref("devtools.debugger.remote-port", 6000, locked);
// Restrict connections to localhost only (default: true)
pref("devtools.debugger.force-local", true, locked);
// Disable browser chrome debugging (debugging Firefox itself). Debian's default: false
pref("devtools.chrome.enabled", false, locked);
// Block access to all developer tools functionality
pref("devtools.policy.disabled", true, locked);
// -----------------------------------------------------------------------------
// Cookie and Privacy Settings
// -----------------------------------------------------------------------------
// Strongest cookie privacy setting (reject trackers, partition third-party cookies)
pref("network.cookie.cookieBehavior", 5, locked);
pref("network.cookie.cookieBehavior.pbmode", 5, locked);
// Block-by-default with opt-in partitioning (more restrictive than dFPI)
pref("network.cookie.cookieBehavior.optInPartitioning", true, locked);
pref("network.cookie.cookieBehavior.optInPartitioning.pbmode", true, locked);
// Block third-party cookies from tracking protection list
pref("network.cookie.cookieBehavior.trackerCookieBlocking", true, locked);
// Prevent sync from propagating weaker settings
pref("services.sync.prefs.sync.network.cookie.cookieBehavior", false, locked);
// Global Privacy Control
pref("privacy.globalprivacycontrol.enabled", true, locked);
pref("privacy.globalprivacycontrol.functionality.enabled", true, locked);
pref("privacy.globalprivacycontrol.pbmode.enabled", true, locked);
// Disable First Party Isolation (incompatible with behavior 5)
pref("privacy.firstparty.isolate", false, locked);
pref("privacy.firstparty.isolate.block_post_message", false, locked);
pref("privacy.firstparty.isolate.restrict_opener_access", true, locked);
pref("privacy.firstparty.isolate.use_site", false, locked);
// -----------------------------------------------------------------------------
// TLS/SSL Configuration
// -----------------------------------------------------------------------------
// Minimum TLS version (1=tls1, 2=tls1.1, 3=tls1.2, 4=tls1.3)
pref("security.tls.version.min", 3, locked);
// Maximum TLS version
pref("security.tls.version.max", 4, locked);
// Disable deprecated TLS versions
pref("security.tls.version.enable-deprecated", false, locked);
// TLS Security Features
pref("security.ssl.require_safe_negotiation", true, locked);
pref("security.tls.hello_downgrade_check", true, locked);
pref("security.ssl.enable_ocsp_stapling", true, locked);
pref("security.OCSP.require", true, locked);
pref("security.OCSP.enabled", 1, locked);
// TLS 1.3 Features
pref("security.tls.enable_0rtt_data", false, locked);
pref("security.tls.enable_post_handshake_auth", true, locked);
pref("security.tls.enable_delegated_credentials", true, locked);
// Encrypted Client Hello (ECH)
pref("network.dns.echconfig.enabled", true, locked);
pref("network.dns.http3_echconfig.enabled", true, locked);
// Disable weak cipher suites
pref("security.ssl3.rsa_aes_128_sha", false, locked);
pref("security.ssl3.rsa_aes_256_sha", false, locked);
pref("security.ssl3.rsa_aes_128_gcm_sha256", false, locked);
pref("security.ssl3.rsa_aes_256_gcm_sha384", false, locked);
pref("security.ssl3.deprecated.rsa_des_ede3_sha", false, locked);
// -----------------------------------------------------------------------------
// Content Security
// -----------------------------------------------------------------------------
pref("security.block_fileuri_script_with_wrong_mime", true, locked);
pref("security.mixed_content.block_active_content", true, locked);
pref("security.mixed_content.block_display_content", true, locked);
pref("security.mixed_content.upgrade_display_content", true, locked);
pref("security.insecure_connection_text.enabled", true, locked);
pref("security.insecure_connection_text.pbmode.enabled", true, locked);
pref("security.warn_submit_secure_to_insecure", true, locked);
// HTTPS-Only Mode
//pref("dom.security.https_only_mode", true, locked);
//pref("dom.security.https_only_mode_pbm", true, locked);
//pref("dom.security.https_first", true, locked);
//pref("dom.security.https_first_pbm", true, locked);
// HTTPS-First (less aggressive than HTTPS-Only)
pref("dom.security.https_first", true, locked);
pref("dom.security.https_first_pbm", true, locked);
// -----------------------------------------------------------------------------
// Certificate and PKI Settings
// -----------------------------------------------------------------------------
pref("security.default_personal_cert", "Ask Every Time", locked);
pref("security.pki.certificate_transparency.mode", 1, locked);
pref("security.ssl.errorReporting.enabled", true, locked);
pref("security.enterprise_roots.enabled", true, locked);
// -----------------------------------------------------------------------------
// Safe Browsing
// -----------------------------------------------------------------------------
pref("browser.safebrowsing.malware.enabled", true, locked);
pref("browser.safebrowsing.phishing.enabled", true, locked);
pref("browser.safebrowsing.downloads.enabled", true, locked);
pref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", true, locked);
pref("browser.safebrowsing.downloads.remote.block_uncommon", true, locked);
// -----------------------------------------------------------------------------
// WebAuthn
// -----------------------------------------------------------------------------
pref("security.webauthn.always_allow_direct_attestation", false, locked);
// -----------------------------------------------------------------------------
// CSP Reporting
// -----------------------------------------------------------------------------
pref("security.csp.reporting.enabled", true, locked);
// -----------------------------------------------------------------------------
// Extension Security
// -----------------------------------------------------------------------------
//pref("xpinstall.whitelist.required", true, locked);
//pref("xpinstall.enabled", false, locked);
//pref("extensions.update.enabled", false, locked);
// -----------------------------------------------------------------------------
// Telemetry and Data Collection
// -----------------------------------------------------------------------------
pref("datareporting.healthreport.uploadEnabled", false, locked);
pref("toolkit.telemetry.enabled", false, locked);
pref("browser.newtabpage.activity-stream.feeds.telemetry", false, locked);
pref("browser.newtabpage.activity-stream.telemetry", false, locked);
// -----------------------------------------------------------------------------
// Network Security
// -----------------------------------------------------------------------------
pref("network.http.sendRefererHeader", 2, locked);
pref("privacy.resistFingerprinting", true, locked);
pref("privacy.trackingprotection.enabled", true, locked);
pref("privacy.trackingprotection.pbmode.enabled", true, locked);
// -----------------------------------------------------------------------------
// DNS over HTTPS
// -----------------------------------------------------------------------------
// pref("network.trr.mode", 3, locked);
// DoH with fallback (mode 2 instead of 3)
pref("network.trr.mode", 2, locked);
// -----------------------------------------------------------------------------
// Local Network Access
// -----------------------------------------------------------------------------
pref("network.lna.blocking", true, locked);
// -----------------------------------------------------------------------------
// Post-Quantum Cryptography
// -----------------------------------------------------------------------------
pref("security.tls.post_quantum_key_agreement.enabled", true, locked);That would be a real service to the community
A real service to the community might be to avoid annoying others with your comments and advice. Please do not post in my topics.
_https://en.wikipedia.org/wiki/Sndio
sndio is the software layer of the OpenBSD operating system that manages sound cards and MIDI ports. It provides an optional sound server and a documented application programming interface to access either the server or the audio and MIDI hardware in a uniform way.
_https://man.openbsd.org/sndiod.8
BUGS
Resampling is low quality; down-sampling especially should be avoided when recording.If -a off is used, sndiod creates sub-devices to expose first and then opens the audio hardware on demand. Technically, this allows sndiod to attempt to use one of the sub-devices it exposes as an audio device, creating a deadlock. There's nothing to prevent the user from shooting themselves in the foot by creating such a deadlock.
sndio might be perfectly suitable for semi-deaf and half-demented users.
If someone really needs sndio, he might consider forking it to integrate the fftrate resampler for higher-quality audio processing
_https://github.com/PetrovSE/fftrate
_https://man.openbsd.org/sndiod.8
BUGS
Resampling is low quality; down-sampling especially should be avoided when recording.
Perhaps sndio developers engage in post-modern humor. Linux users are unlikely to notice a Monty Python situation where a "bug" is documented rather than fixed.
If you don’t get this kind of humor, think of post-truth, post-philosophy, and post-documentation. It’s a post-real world where absurdity is plainly documented — in man pages, wikis, and official notes — without irony. The joke isn’t hidden; it’s right there, labeled "BUG". The word "bug" has become a post-word with a post-meaning: not a flaw to fix, but a punchline accepted as fact.
What is special about post-reality is that it can be consumed innocently — just like myth, it appears factual, not constructed.
The myth consumer takes the signification for a system of facts: myth is read as a factual system whereas it is but a semiological system.
Roland Barthes, Myth Today.
To get sndio working with Firefox (Cubeb) you have to build it with sndio support
# Install sndio development libraries
sudo apt-get install libsndio-dev
# Add to your mozconfig
ac_add_options --enable-sndio
# Alternatively, you may try to disable ALSA
ac_add_options --enable-audio-backends=sndio
# Firefox about:config
media.cubeb.backend sndiosndio Backend cubeb_sndio.c:314-360
Dynamic library loading
Basic stream operations
48kHz preferred sample rate cubeb_sndio.c:535-536
2048 frame minimum latency cubeb_sndio.c:546-547[Because of epidemic of deafness] Sonova is now exiting the consumer audio market to refocus on its core hearing care business (hearing aids and cochlear implants). The professional division remains with the Sennheiser family.
After the Fox - Gold Robbery of Cairo
_https://youtu.be/zgcGyt6qOLg
_https://en.wikipedia.org/wiki/After_the_Fox
Do you want to amplify audio volume in Firefox? It will reduce sound quality. At high levels, it may damage hearing or speakers.
Firefox:
Does not resample audio by default.
WebM can be easily disabled.
PulseAudio backend can be easily disabled.
Chrome (and all Chrome-based browsers):
Resampling cannot be disabled.
WebM cannot be disabled.
PulseAudio backend cannot be disabled.
This is because, perhaps, Chrome developers do not hear the difference.
The browser made for developers
All the latest developer tools in beta in addition to features like the Multi-line Console Editor and WebSocket Inspector.
A separate profile and path so you can easily run it alongside Release or Beta Firefox.
Preferences tailored for web developers: Browser and remote debugging are enabled by default, as are the dark theme and developer toolbar button.
_https://www.firefox.com/en-US/channel/desktop/developer
Building Firefox On Linux
_https://firefox-source-docs.mozilla.org/setup/linux_build.html
_https://firefox-source-docs.mozilla.org/build/buildsystem/mozconfigs.html
mozconfig for Firefox Developer Edition (ALSA only, without pulse-rust backend):
$ cat .mozconfig
# The default mozconfig is located here: sourcedir/browser/config/mozconfig
. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_MAKE_FLAGS="-j$(expr $(nproc) + 2)"
mk_add_options MOZ_OBJDIR="$(dirname $topsrcdir)"/build_dir_ALSA-dev
mk_add_options MOZ_APP_DISPLAYNAME="Firefox Developer Edition"
mk_add_options MOZ_SIMPLE_PACKAGE_NAME=firefox-dev
ac_add_options --with-app-basename="Firefox Developer Edition"
ac_add_options --with-app-name=firefox-dev
ac_add_options --prefix=/usr
ac_add_options --without-sysroot # classified
ac_add_options --enable-audio-backends=alsa
mk_add_options MOZ_DEV_EDITION=1
ac_add_options --with-branding=browser/branding/aurora # Firefox Developer Edition
export LDFLAGS="-Wl,--no-keep-memory"cd firefox
git pull
./mach clobber
./mach configure
./mach build -v --priority normal$ ./mach run --version
Mozilla Firefox Developer Edition 152.0a1 DESTDIR="$(dirname $(pwd))"/debdir_ALSA-dev ./mach install $ ls -1 ../debdir_ALSA-dev/usr
bin
lib
$ file ../debdir_ALSA-dev/usr/bin/firefox-dev
../debdir_ALSA-dev/usr/bin/firefox-dev: broken symbolic link to /usr/lib/firefox-dev/firefox-dev A bash script to create the /usr/share directory structure for Firefox Developer Edition
(modify it to suit your needs)
$ cat make_share.sh
#!/bin/bash
# Create Firefox Developer Edition /usr/share structure
SHARE_DIR="$(dirname $(pwd))"/debdir_ALSA-dev/usr/share
PACKAGE_NAME="firefox-dev"
APP_NAME="Firefox Developer Edition"
echo "Creating /usr/share structure for $APP_NAME..."
# Create main directories
mkdir -p "$SHARE_DIR/$PACKAGE_NAME"/{browser/{chrome/icons/default,defaults/preferences},distribution/searchplugins/common}
mkdir -p "$SHARE_DIR/applications"
mkdir -p "$SHARE_DIR/icons/hicolor"/{16x16,32x32,48x48,64x64,128x128,symbolic}/apps
mkdir -p "$SHARE_DIR/doc/$PACKAGE_NAME"
mkdir -p "$SHARE_DIR/man/man1"
mkdir -p "$SHARE_DIR/lintian/overrides"
mkdir -p "$SHARE_DIR/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
# Create desktop entry file
cat > "$SHARE_DIR/applications/$PACKAGE_NAME.desktop" << EOF
[Desktop Entry]
Version=1.0
Type=Application
Name=$APP_NAME
Comment=The browser made for developers
Exec=/usr/bin/$PACKAGE_NAME %U
Icon=$PACKAGE_NAME
Terminal=false
Categories=Network;WebBrowser;
StartupNotify=true
MimeType=text/html;text/xml;application/xhtml+xml;application/vnd.mozilla.xul+xml;text/mml;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;
EOF
# Create copyright file
cat > "$SHARE_DIR/doc/$PACKAGE_NAME/copyright" << EOF
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: $PACKAGE_NAME
Source: https://hg.mozilla.org/mozilla-central/
Files: *
Copyright: 2024 Mozilla Foundation
License: MPL-2.0
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
License: MPL-2.0
EOF
# Create lintian overrides
cat > "$SHARE_DIR/lintian/overrides/$PACKAGE_NAME" << EOF
$PACKAGE_NAME: package-name-doesnt-match-sonames
$PACKAGE_NAME: binary-without-manpage
$PACKAGE_NAME: missing-dep-for-interpreter /usr/bin/perl (perl >= 5.6)
$PACKAGE_NAME: script-not-executable ./usr/share/$PACKAGE_NAME/defaults/preferences/syspref.js
EOF
# Create man page
cat > "$SHARE_DIR/man/man1/$PACKAGE_NAME.1" << EOF
.TH $PACKAGE_NAME 1 "2024" "Mozilla Foundation" "User Commands"
.SH NAME
$PACKAGE_NAME \- Mozilla Firefox Developer Edition web browser
.SH DESCRIPTION
Firefox Developer Edition is the blazing fast browser that offers cutting edge developer tools and latest features like CSS Grid support and framework debugging
.SH OPTIONS
.TP
\fB\--help\fR
Prints the command line options.
.TP
\fB\--version\fR
Prints the version information.
.SH FILES
.I /usr/lib/$PACKAGE_NAME/firefox-dev
\- The main executable
.SH SEE ALSO
.BR firefox(1)
EOF
gzip -9 "$SHARE_DIR/man/man1/$PACKAGE_NAME.1"
# Copy icons from build if available
BUILD_LIB_DIR="$(dirname $(pwd))"/debdir_ALSA-dev/usr/lib/firefox-dev
if [ -f "$BUILD_LIB_DIR/browser/chrome/icons/default/default16.png" ]; then
for size in 16 32 48 64 128; do
if [ -f "$BUILD_LIB_DIR/browser/chrome/icons/default/default${size}.png" ]; then
cp "$BUILD_LIB_DIR/browser/chrome/icons/default/default${size}.png" \
"$SHARE_DIR/icons/hicolor/${size}x${size}/apps/$PACKAGE_NAME.png"
fi
done
else
# Create placeholder icons
for size in 16 32 48 64 128; do
convert -size ${size}x${size} xc:transparent "$SHARE_DIR/icons/hicolor/${size}x${size}/apps/$PACKAGE_NAME.png" 2>/dev/null || \
touch "$SHARE_DIR/icons/hicolor/${size}x${size}/apps/$PACKAGE_NAME.png"
done
fi
# Set proper permissions
chmod 644 "$SHARE_DIR/applications/$PACKAGE_NAME.desktop"
chmod 644 "$SHARE_DIR/doc/$PACKAGE_NAME"/*
chmod 644 "$SHARE_DIR/lintian/overrides/$PACKAGE_NAME"
find "$SHARE_DIR" -type d -exec chmod 755 {} \;
echo "Created /usr/share structure in $SHARE_DIR"
echo "Directory tree:"
find "$SHARE_DIR" -type d | sort
echo "Files created:"
find "$SHARE_DIR" -type f | sort./make_share.sh$ ls -1 ../debdir_ALSA-dev/usr
bin
lib
shareHow to calculate dependencies
Make a template
mkdir debian
echo -e "Source: firefox-dev\nPackage: firefox-dev\nDepends: \${shlibs:Depends}" >> debian/control $ cat debian/control
Source: firefox-dev
Package: firefox-dev
Depends: ${shlibs:Depends}Run "dpkg-shlibdeps" to calculate dependencies
dpkg-shlibdeps -v -xfirefox-dev -l"$(dirname $(pwd))"/debdir_ALSA-dev/usr/lib --ignore-missing-info -e $(find "$(dirname $(pwd))"/debdir_ALSA-dev/usr -type f 2>/dev/null) $ cat debian/substvars
shlibs:Depends=libasound2 (>= 1.1.0), libatk1.0-0 (>= 1.12.4), libc6 (>= 2.36), libcairo-gobject2 (>= 1.10.0), libcairo2 (>= 1.10.0), libdbus-1-3 (>= 1.9.14), libfontconfig1 (>= 2.12.6), libfreetype6 (>= 2.11.1), libgcc-s1 (>= 4.2), libgdk-pixbuf-2.0-0 (>= 2.22.0), libglib2.0-0 (>= 2.37.3), libgtk-3-0 (>= 3.13.7), libharfbuzz0b (>= 0.6.0), libnspr4 (>= 2:4.12), libnss3 (>= 2:3.82), libpango-1.0-0 (>= 1.14.0), libpangocairo-1.0-0 (>= 1.14.0), libstdc++6 (>= 12), libx11-6, libx11-xcb1 (>= 2:1.8.4), libxcb-shm0, libxcb1, libxcomposite1 (>= 1:0.4.5), libxcursor1 (>> 1.1.2), libxdamage1 (>= 1:1.1), libxext6, libxfixes3, libxi6, libxrandr2 (>= 2:1.4.0), libxrender1, zlib1g (>= 1:1.1.4)install -vm0755 -d "$(dirname $(pwd))"/debdir_ALSA-dev/DEBIAN$ cat "$(dirname $(pwd))"/debdir_ALSA-dev/DEBIAN/control
Package: firefox-dev
Version: 152.0a1
Priority: optional
Section: web
Architecture: amd64
Maintainer: Devuan
Installed-Size: 433 MB
Provides: gnome-www-browser, www-browser
Depends: libc6
Recommends: libavcodec61 | libavcodec-extra61 | libavcodec60 | libavcodec-extra60 | libavcodec59 | libavcodec-extra59 | libavcodec58 | libavcodec-extra58 | libavcodec57 | libavcodec-extra57 | libavcodec56 | libavcodec-extra56 | libavcodec55 | libavcodec-extra55 | libavcodec54 | libavcodec-extra54 | libavcodec53 | libavcodec-extra53
Suggests: fonts-stix | otf-stix, fonts-lmodern, libgssapi-krb5-2 | libkrb53, libcanberra0
Description: Mozilla Firefox Developer Edition. ALSA only.
The browser made for developers
.
All the latest developer tools in beta in addition to features like the Multi-line Console Editor and WebSocket Inspector.
.
A separate profile and path so you can easily run it alongside Release or Beta Firefox.
.
Preferences tailored for web developers: Browser and remote debugging are enabled by default, as are the dark theme and developer toolbar button.
.
Mozilla Firefox Developer Edition. ALSA only, without pulse-rust backend.Generate DEBIAN/md5sums
cd "$(dirname $(pwd))"/debdir_ALSA-dev
find . -type f -not -path "./DEBIAN/*" -exec md5sum {} + | sort -k 2 | sed 's/\.\/\(.*\)/\1/' > DEBIAN/md5sums
cd ..
chmod 0644 -- debdir_ALSA-dev/DEBIAN/md5sums Make Debian package
$ fakeroot -- dpkg-deb -b debdir_ALSA-dev firefox-dev_152.0a1_amd64.deb
dpkg-deb: building package 'firefox-dev' in 'firefox-dev_152.0a1_amd64.deb'.
$ ls -1 *deb
firefox-dev_152.0a1_amd64.debInstall
sudo dpkg -i firefox-dev_152.0a1_amd64.deb$ firefox-dev --version
Mozilla Firefox Developer Edition 152.0a1Firefox 152.0a1 is not on YouTube’s allowlist. To enable YouTube live chat, use a User Agent override to spoof Firefox 150.0. This prevents YouTube from incorrectly flagging your browser as an 'older version' and blocking the chat feature.
about:config
general.useragent.override Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0
privacy.resistFingerprinting false # defaultNOTE: This guide is for experienced Linux users. If you have need help, please start a new topic on "Desktop and Multimedia".
To summare "secret knowledge":
Configuration Editor for Firefox
_https://support.mozilla.org/en-US/kb/about-config-editor-firefox
Firefox settings for better sound quality
about:config
media.resampling.enabled false media.webm.enabled false media.mediasource.webm.enabled false # it might be deprecated media.cubeb.backend alsa # if ALSA backend is available media.cubeb_latency_playback_ms 160NOTE: Firefox's default value (for all platforms):
media.cubeb_latency_playback_ms 100This is because Firefox is optimized for macOS, not for Linux with ALSA.
media.encoder.webm.enabled false # Disable WebM recording media.mediasource.vp9.enabled false # Disable WebM in MSEMedia Source Extensions (MSE)
_https://en.wikipedia.org/wiki/Media_Source_Extensions
Verification:
1. about:support - search for "Audio Backend"
2. MOZ_LOG="MediaDecoder:4,cubeb:5"
On both Linux and macOS, you need Firefox logs to detect unwanted resampling, or to verify that Firefox does not resample.
Linux logs:
MOZ_LOG="MediaDecoder:4,cubeb:5" stdbuf -oL firefox 2>&1 https://www.youtube.com/watch?v=X0lwWwJJfXk | grep --line-buffered -E "MetadataLoaded.*rate=|FirstFrameLoaded.*rate=|CubebStreamInit output stream rate|target rate|Output hardware|Input|Output|Rates" | grep -vE "hasVideo=0|hasAudio=0" $ MOZ_LOG="MediaDecoder:4,cubeb:5" stdbuf -oL firefox 2>&1 https://www.youtube.com/watch?v=X0lwWwJJfXk | grep --line-buffered -E "MetadataLoaded.*rate=|FirstFrameLoaded.*rate=|CubebStreamInit output stream rate|target rate|Output hardware|Input|Output|Rates" | grep -vE "hasVideo=0|hasAudio=0"
[Child 25647: Main Thread]: D/MediaDecoder MediaDecoder[7f2a416c2e00] MetadataLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1
[Child 25647: Main Thread]: D/MediaDecoder MediaDecoder[7f2a416c2e00] FirstFrameLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1
[Child 25647: Main Thread]: D/MediaDecoder MediaDecoder[7f2a2c7d7d00] MetadataLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1
[Child 25647: Main Thread]: D/MediaDecoder MediaDecoder[7f2a2c7d7d00] FirstFrameLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1
[Child 25647: MediaDecoderStateMachine #1]: I/cubeb CubebStreamInit output stream rate 44100
[fftrate ALSA plugin log: 44100 --> 48000 Hz]
Input: 44100 Hz, 2 ch, 's32_le' (0xa): dummy = 0, period = 1764
Output: 48000 Hz, 2 ch, 's16_le' (0x2): dummy = 0, period = 1920
Rates: 44100 --> 48000 (J: 0.00%, T: FFT, W: Vorbis) macOS Firefox logs:
about:support
Name Firefox
Version 151.0b3
Audio Backend audiounit-rust
Max Channels 2
Preferred Sample Rate 44100
about:config
media.resampling.enabled false
media.webm.enabled false # Disable WebM playback [grep = GNU grep]
MOZ_LOG="MediaDecoder:4,cubeb:5" stdbuf -oL /Applications/Firefox\ Developer\ Edition.app/Contents/MacOS/firefox 2>&1 https://youtu.be/qeUcGD4rRRc | ggrep --line-buffered -E "MetadataLoaded.*rate=|FirstFrameLoaded.*rate=|CubebStreamInit output stream rate|target rate|Output hardware" | ggrep -vE "hasVideo=0|hasAudio=0"➤ MOZ_LOG="MediaDecoder:4,cubeb:5" stdbuf -oL /Applications/Firefox\ Developer\ Edition.app/Contents/MacOS/firefox 2>&1 https://youtu.be/qeUcGD4rRRc | ggrep --line-buffered -E "MetadataLoaded.*rate=|FirstFrameLoaded.*rate=|CubebStreamInit output stream rate|target rate|Output hardware" | ggrep -vE "hasVideo=0|hasAudio=0"
[Child 2125: Main Thread]: D/MediaDecoder MediaDecoder[133ffa100] MetadataLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1
[Child 2125: Main Thread]: D/MediaDecoder MediaDecoder[133ffa100] FirstFrameLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1
[Child 2125: MediaDecoderStateMachine #1]: I/cubeb CubebStreamInit output stream rate 44100
[Parent 2109: AudioIPC Server RPC]: E/cubeb mod.rs:4077: (0x14d932800) Output hardware description: AudioStreamBasicDescription { mSampleRate: 44100.0, mFormatID: 1819304813, mFormatFlags: 9, mBytesPerPacket: 8, mFramesPerPacket: 1, mBytesPerFrame: 8, mChannelsPerFrame: 2, mBitsPerChannel: 32, mReserved: 0 }
[Parent 2109: AudioIPC Server RPC]: E/cubeb cubeb_resampler_internal.h:555:Input and output sample-rate match, target rate of 44100HzExplanation:
Input Source Media Rate: 44100 Hz (from YouTube AAC metadata)
[Child 2125: Main Thread]: D/MediaDecoder MediaDecoder[133ffa100] MetadataLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1
[Child 2125: Main Thread]: D/MediaDecoder MediaDecoder[133ffa100] FirstFrameLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1 Output Stream Rate: 44100 Hz (Cubeb initialization)
[Child 2125: MediaDecoderStateMachine #1]: I/cubeb CubebStreamInit output stream rate 44100 Conclusion: Since input source rate (44100 Hz) = output stream rate (44100 Hz), no resampling is occurring in Firefox's audio pipeline.
NOTE: To prevent software resampling by the macOS software mixer, set the sample rate to 44100Hz using the Audio MIDI Setup utility:
open -a Audio\ MIDI\ Setup.app Reference media files:
Robert de Visée Prélude et Allemande, Jonas Nordberg, theorbo
_https://youtu.be/qeUcGD4rRRc
The 10 Questions Everyone Asks About My 6-Foot, 14-String Lute [Theorbo]
_https://www.youtube.com/watch?v=X0lwWwJJfXk
The coolest LUTE I've ever seen!
_https://www.youtube.com/watch?v=4YmELV5p6ZY
Building Firefox On Linux
_https://firefox-source-docs.mozilla.org/setup/linux_build.html
_https://firefox-source-docs.mozilla.org/build/buildsystem/mozconfigs.html
A secret mozconfig to disable pulse-rust backend
$ cat .mozconfig
# Default: sourcedir/browser/config/mozconfig
. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_MAKE_FLAGS="-j$(expr $(nproc) + 2)"
mk_add_options MOZ_OBJDIR="$(dirname $topsrcdir)"/build_dir_ALSA
mk_add_options MOZ_APP_DISPLAYNAME="Firefox ALSA"
mk_add_options MOZ_SIMPLE_PACKAGE_NAME=firefox-alsa
ac_add_options --with-app-basename="Firefox ALSA"
ac_add_options --with-app-name=firefox-alsa
ac_add_options --prefix=/usr
ac_add_options --without-sysroot # classified
ac_add_options --enable-audio-backends=alsa
export LDFLAGS="-Wl,--no-keep-memory" cd firefox
git pull
./mach clobber
./mach configure
./mach build -v --priority normal
./mach run --version
./mach run
DESTDIR="$(dirname $(pwd))"/debdir_ALSA ./mach install NOTE: The documented configure options can be listed with ./configure --help, but some may not work depending on undocumented options. To understand which options are actually functional, you need to study the source code.
$ ./configure --help
Usage: configure.py [options]
Options: [defaults in brackets after descriptions]
Help options:
--help Print this message
Options from build/moz.configure/init.configure:
--enable-application Application to build. Same as --enable-project
--enable-project Project to build [browser]
--enable-artifact-builds Download and use prebuilt binary artifacts
--host Define the system type performing the build
--target Define the system type where the resulting executables will be used
--with-version-file-path Specify a custom path to app version files instead of auto-detecting
--as-milestone={early-beta,late-beta,release}
Build with another milestone configuration (e.g., as release)
--enable-update-channel Select application update channel [default]
--with-app-basename Typically stays consistent for multiple branded versions of a given application (e.g. Aurora and Firefox both use "Firefox"), but may vary for full rebrandings (e.g. Iceweasel). Used for application.ini's "Name" field, which controls profile location in the absence of a "Profile" field (see below), and various system integration hooks (Unix remoting, Windows MessageWindow name, etc
--prefix=PREFIX Install files using PREFIX as root directory [/usr/local]
--includedir=DIR C header files in DIR [/usr/include]
--libdir=DIR Object code libraries in DIR [/usr/lib]
Options from moz.configure:
--enable-artifact-build-symbols[={full}]
Download symbols when artifact builds are enabled
--disable-compile-environment
Disable compiler/library checks
--disable-tests Do not build test libraries & programs
--enable-debug Enable building with developer debug info (using the given compiler flags)
--with-debug-label Debug DEBUG_<value> for each comma-separated value given
--enable-release Build with more conservative, release engineering-oriented options. This may slow down builds.
--disable-unified-build Enable building modules in non unified context
--enable-valgrind Enable Valgrind integration hooks
--enable-build-backend={Clangd,ChromeMap,CompileDB,CppEclipse,FasterMake,FasterMake+RecursiveMake,RecursiveMake,StaticAnalysis,TestManifest,VisualStudio},...
Deprecated
--build-backends={Clangd,ChromeMap,CompileDB,CppEclipse,FasterMake,FasterMake+RecursiveMake,RecursiveMake,StaticAnalysis,TestManifest,VisualStudio},...
Build backends to generate [RecursiveMake,FasterMake,Clangd]
--enable-gtest-in-build Enable building the gtest libxul during the build
--enable-ui-locale Select the user interface locale (default: en-US) [en-US]
--enable-strip Enable stripping of libs & executables
--disable-install-strip Enable stripping of libs & executables when packaging
--with-system-zlib Use system libz
Options from build/moz.configure/bootstrap.configure:
--disable-bootstrap Disable bootstrap or update of toolchains
Options from build/moz.configure/toolchain.configure:
--disable-optimize Disable optimizations via compiler flags
--with-toolchain-prefix Prefix for the target toolchain
--with-compiler-wrapper Enable compiling with wrappers such as distcc and ccache
--with-ccache Enable compiling with ccache
--enable-gold Deprecated
--enable-linker Select the linker {bfd, gold, ld64, lld, lld-*, mold}
--disable-debug-symbols Disable debug symbols using the given compiler flags
--enable-address-sanitizer
Enable Address Sanitizer
--enable-memory-sanitizer
Enable Memory Sanitizer
--enable-thread-sanitizer
Enable Thread Sanitizer
--enable-undefined-sanitizer
Enable UndefinedBehavior Sanitizer
--enable-signed-overflow-sanitizer
Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts)
--enable-unsigned-overflow-sanitizer
Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts)
--enable-hardening Enables security hardening compiler options
--enable-stl-hardening Enable C++ STL hardening
--enable-frame-pointers Enable frame pointers
--enable-coverage Enable code coverage
--enable-clang-plugin Enable building with the Clang plugin (gecko specific static analyzers)
--enable-fuzzing Enable fuzzing support
--enable-snapshot-fuzzing
Enable experimental snapshot fuzzing support
--enable-cpp-rtti Enable C++ RTTI
--enable-path-remapping[={c,rust},...]
Enable remapping source and object paths in compiled outputs
--enable-dtrace Build with dtrace support
Options from build/moz.configure/memory.configure:
--enable-jemalloc Replace memory allocator with jemalloc
Options from build/moz.configure/warnings.configure:
--enable-warnings-as-errors
Enable treating warnings as errors
Options from build/moz.configure/flags.configure:
--enable-icf Enable Identical Code Folding
--disable-new-pass-manager
Use the legacy LLVM pass manager in clang builds
Options from build/moz.configure/lto-pgo.configure:
--enable-profile-generate[={cross}]
Build a PGO instrumented binary
--enable-profile-use[={cross}]
Use a generated profile during the build
--with-pgo-profile-path Path to the directory with unmerged profile data to use during the build, or to a merged profdata file
--with-pgo-jarlog Use the provided jarlog file when packaging during a profile-use build
--enable-lto[={full,thin,cross},...]
Enable LTO
Options from browser/moz.configure:
--disable-browser-newtab-as-addon
Disable bundling newtab as a built-in addon
Options from toolkit/moz.configure:
--with-distribution-id Set distribution-specific id [org.mozilla]
--disable-gecko-profiler Disable the Gecko profiler
--enable-dmd Enable Dark Matter Detector (heap profiler). Also enables jemalloc, replace-malloc and profiling
--enable-audio-backends={aaudio,alsa,audiounit,jack,opensl,oss,pulseaudio,sndio,sunaudio,wasapi},...
Enable various cubeb backends [pulseaudio]
--enable-alsa Enable ALSA audio backend
--enable-jack Enable JACK audio backend
--enable-pulseaudio Enable PulseAudio audio backend
--enable-sndio Enable sndio audio backend
--with-l10n-base Path to l10n repositories
--enable-default-toolkit={cairo-gtk3,cairo-gtk3-wayland,cairo-gtk3-x11-wayland,cairo-gtk3-wayland-only,cairo-gtk3-x11-only}
Select default toolkit [cairo-gtk3]
--with-system-pipewire Use system PipeWire
--with-system-gbm Use system gbm
--with-system-libdrm Use system libdrm
--with-gl-provider Set GL provider backend type
--disable-wmf Disable support for Windows Media Foundation
--disable-ffmpeg Disable FFmpeg for fragmented H264/AAC decoding
--disable-av1 Disable av1 video support
--with-system-av1 Use system av1 (located with pkg-config)
--disable-jxl Disable jxl image support
--disable-real-time-tracing
Disable tracing of real-time audio callbacks
--enable-openmax Enable OpenMAX IL for video/audio decoding
--enable-chrome-format={omni,jar,flat}
Select FORMAT of chrome files during packaging [omni]
--enable-minify[={properties,js},...]
Select types of files to minify during packaging [properties]
--with-mozilla-api-keyfile
Use the secret key contained in the given keyfile for Mozilla API requests
--with-google-location-service-api-keyfile
Use the secret key contained in the given keyfile for Google Location Service API requests
--with-google-safebrowsing-api-keyfile
Use the secret key contained in the given keyfile for Google Safebrowsing API requests
--with-bing-api-keyfile Use the client id and secret key contained in the given keyfile for Bing API requests
--with-adjust-sdk-keyfile
Use the secret key contained in the given keyfile for Adjust SDK requests
--with-leanplum-sdk-keyfile
Use the client id and secret key contained in the given keyfile for Leanplum SDK requests
--with-pocket-api-keyfile
Use the secret key contained in the given keyfile for Pocket API requests
--enable-webrender-debugger
Build the websocket debug server in WebRender
--enable-app-system-headers
Use additional system headers defined in $MOZ_BUILD_APP/app-system-headers.mozbuild
--disable-printing Disable printing support
--disable-synth-speechd Disable speech-dispatcher support
--disable-webspeech Disable support for HTML Speech API
--disable-webspeechtestbackend
Disable support for HTML Speech API Test Backend
--disable-skia-pdf Disable Skia PDF
--with-system-webp Use system libwebp (located with pkgconfig)
--disable-webdriver Disable support for WebDriver remote protocols
--disable-geckodriver Do not build geckodriver
--enable-webrtc Enable support for WebRTC
--enable-raw Enable support for RAW media
--enable-address-sanitizer-reporter
Enable Address Sanitizer Reporter Extension
--enable-proxy-bypass-protection
Prevent suspected or confirmed proxy bypasses
--disable-proxy-direct-failover
Disable direct failover for system requests
--disable-accessibility Disable accessibility support
--with-unsigned-addon-scopes={app,system},...
Addon scopes where signature is not required
--allow-addon-sideload Addon sideloading is allowed
--disable-extensions-webidl-bindings
Disable building experimental WebExtensions WebIDL bindings
--enable-launcher-process
Enable launcher process by default
--enable-bundled-fonts Enable support for bundled fonts on desktop platforms
--enable-reflow-perf Enable reflow performance tracing
--enable-layout-debugger Enable layout debugger
--with-system-libvpx Use system libvpx (located with pkgconfig)
--with-system-jpeg Use system libjpeg (installed at given prefix)
--with-system-png Use system libpng
--with-wasm-sandboxed-libraries={graphite,ogg,hunspell,expat,woff2,soundtouch},...
Enable wasm sandboxing for the selected libraries
--enable-disk-remnant-avoidance
Prevent persistence of auxiliary files on application close
--enable-forkserver Enable fork server
--disable-backgroundtasks
Disable running in background task mode
--enable-mobile-optimize Enable mobile optimizations
--disable-pref-extensions
Disable pref extensions such as autoconfig
--disable-startupcache Disable startup cache
--enable-official-branding
Enable Official mozilla.org Branding. Do not distribute builds with --enable-official-branding unless you have permission to use trademarks per http://www.mozilla.org/foundation/trademarks/
--with-branding=DIR Use branding from directory DIR
--with-crashreporter-url Set an alternative crashreporter url [https://crash-reports.mozilla.com/]
--with-system-libevent Use system libevent
--enable-crashreporter Enable crash reporting
--disable-dbus Disable dbus support
--enable-debug-js-modules
Enable debug mode for frontend JS libraries
--enable-dump-painting Enable paint debugging
--enable-libproxy Enable libproxy support
--enable-logrefcnt Enable logging of refcounts
--disable-negotiateauth Disable GSS-API negotiation
--disable-parental-controls
Do not build parental controls
--enable-sandbox Enable sandboxing support
--disable-system-extension-dirs
Disable searching system- and account-global directories for extensions of any kind; use only profile-specific extension directories
--with-system-pixman Use system pixman (located with pkgconfig)
--disable-universalchardet
Disable universal encoding detection
--disable-zipwriter Disable zipwriter component
--with-user-appdir Set user-specific appdir [.mozilla]
--enable-uniffi-fixtures Enable UniFFI Fixtures/Examples
--disable-system-policies
Disable reading policies from Windows registry, macOS's file system attributes, and /etc/firefox
--disable-legacy-profile-creation
Disable the creation a legacy profile, to be used by old versions of Firefox, when no profiles exist
--with-onnx-runtime Location of the ONNX Runtime
Options from js/moz.configure:
--with-app-name Used for e.g. the binary program file name. If not set, defaults to a lowercase form of MOZ_APP_BASENAME
--enable-js-shell Build the JS shell
--enable-decorators Enable experimental JS Decorators support
--disable-explicit-resource-management
Disable explicit resource management
--enable-portable-baseline-interp
Enable the portable baseline interpreter
--enable-portable-baseline-interp-force
Enable forcing use of the portable baseline interpreter
--enable-aot-ics Enable including ahead-of-time corpus of CacheIR IC bodies
--enable-aot-ics-force Enable forcing the AOT ICs option on without additional configuration
--enable-aot-ics-enforce Enable enforcing that only AOT IC corpus is used, crashing otherwise (TEST ONLY)
--enable-jit Enable use of the JITs
--enable-ion Deprecated
--enable-simulator={arm,arm64,mips64,loong64,riscv64}
Enable a JIT code simulator for the specified architecture
--enable-instruments Enable instruments remote profiling
--enable-callgrind Enable callgrind profiling
--disable-profiling Do not set compile flags necessary for using sampling profilers (e.g. shark, perf)
--disable-execution-tracing
Do not set compile flags necessary for running the JS execution tracer
--enable-vtune Enable VTune profiling
--enable-gc-probes Turn on probes for allocation and finalization
--enable-gczeal Enable zealous GCing
--enable-oom-breakpoint Enable a breakpoint function for artificial OOMs
--disable-jitdump Disable perf jitdump integration
--enable-jitspew Enable the Jit spew and IONFLAGS environment variable
--enable-masm-verbose Enable MacroAssembler verbosity of generated code
--disable-ctypes Disable js-ctypes
--enable-rust-simd Enable explicit SIMD in Rust code
--disable-spidermonkey-telemetry
Disable performance telemetry for SpiderMonkey (e.g. compile and run times)
--enable-wasm-codegen-debug
Enable debugging for wasm codegen
--wasm-no-experimental Force disable all wasm experimental features for testing
--enable-wasm-jspi Enable WebAssembly JS PI
--disable-shared-memory Disable JS/WebAssembly shared memory and atomics
--enable-wasm-simd Enable WebAssembly SIMD
--enable-wasm-avx Enable AVX support for WebAssembly SIMD
--enable-wasm-relaxed-simd
Enable WebAssembly relaxed SIMD
--enable-wasm-moz-intgemm
Enable WebAssembly intgemm private intrinsics
--disable-wasm-memory-control
Disable WebAssembly fine-grained memory control instructions
--disable-wasm-branch-hinting
Disable WebAssembly Branch hints
--with-sixgill Enable static checking of code using sixgill
--with-jitreport-granularity[={0,1,2,3}]
Default granularity at which to report JIT code to external tools (0 - no info, 1 - code ranges for while functions only, 2 - per-line information, 3 - per-op information) [3]
--with-system-icu Use system ICU
--without-intl-api Disable ECMAScript Internationalization API
--disable-icu4x Disable using ICU4X
--disable-wasm-type-reflections
Disable type reflection in WASM JS-API
--disable-wasm-resizable-arraybuffer
Disable resizable ArrayBuffer in WASM
Options from build/moz.configure/nspr.configure:
--with-system-nspr Use system NSPR
Options from build/moz.configure/rust.configure:
--enable-rust-tests Enable building and running of Rust tests during `make check`
--enable-rust-debug Build Rust code with debug assertions turned on
--disable-cargo-incremental
Disable incremental rust compilation
Options from build/moz.configure/bindgen.configure:
--with-libclang-path Absolute path to a directory containing Clang/LLVM libraries for bindgen (version 3.9.x or above)
--with-clang-path Absolute path to a Clang binary for bindgen (version 3.9.x or above)
Options from js/ffi.configure:
--with-system-ffi Use system libffi (located with pkgconfig)
Options from build/moz.configure/node.configure:
--disable-nodejs Require Node.js to build
Options from build/moz.configure/nss.configure:
--with-system-nss Use system NSS
Options from build/moz.configure/update-programs.configure:
--disable-updater Disable building the updater
--enable-unverified-updates
Enable application update without verifying MAR or updater binary signatures
--enable-default-browser-agent
Enable building the default browser agent
Environment variables:
Options from build/moz.configure/init.configure:
MOZ_AUTOMATION Enable options for automated builds
MOZCONFIG Mozconfig location
MOZILLABUILD Path to Mozilla Build (Windows-only)
CONFIG_SHELL Path to a POSIX shell
GIT Path to the git program
MOZILLA_OFFICIAL Build an official release
MOZBUILD_STATE_PATH Path to a persistent state directory for the build system and related tools
Options from moz.configure:
MOZ_BUILD_HOOK Path to the moz.build file that will be executed as if it were appended to every moz.build in the tree
MOZ_COPY_PDBS For builds that do not support symbols in the normal fashion, generate and copy them into the resulting build archive
MOZ_PGO Build with profile guided optimizations
READELF Path to the readelf program
OBJCOPY Path to the objcopy program
AWK Path to the awk program
MAKE Path to GNU make
GMAKE Path to the gmake program
WATCHMAN Path to the watchman program
XARGS Path to the xargs program
MKFSHFS Path to the mkfshfs program
HFS_TOOL Path to the hfs_tool program
STRIP_FLAGS Flags for the strip command
STRIP Path to the strip program
USE_LIBZ_RS Use libz-rs-sys instead of zlib
Options from build/moz.configure/toolchain.configure:
HOST_CPPFLAGS Extra flags for Preprocessing host sources []
HOST_CFLAGS Extra flags for compiling host C sources []
HOST_CXXFLAGS Extra flags for compiling host C++ sources []
HOST_LDFLAGS Extra flags for linking host object files []
CPPFLAGS Extra flags for preprocessing sources []
CFLAGS Extra flags for compiling C sources []
CXXFLAGS Extra flags for compiling C++ sources []
ASFLAGS Extra flags for assembling sources []
LDFLAGS Extra flags for linking object files []
LIBS Extra libraries for linking object files []
MOZ_OPTIMIZE_FLAGS Extra optimization flags
MOZ_HAZARD Build for the GC rooting hazard analysis
CCACHE_PREFIX Compiler prefix to use when using ccache
RUSTC_WRAPPER Wrap rust compilation with given tool
SCCACHE_VERBOSE_STATS Print verbose sccache stats after build
CC Path to the target C compiler
LD Deprecated
CXX Path to the target C++ compiler
HOST_CC Path to the host C compiler
HOST_LD Deprecated
HOST_CXX Path to the host C++ compiler
MOZ_DEBUG_FLAGS Debug compiler flags
AS Path to the assembler
LLVM_OBJDUMP Path to llvm-objdump
AR Path to the ar program
HOST_AR Path to the host_ar program
Options from build/moz.configure/pkg.configure:
PKG_CONFIG Path to the pkg_config program
Options from build/moz.configure/lto-pgo.configure:
LLVM_PROFDATA Path to the llvm_profdata program
MOZ_LD64_KNOWN_GOOD Indicate that ld64 is free of symbol aliasing bugs
Options from toolkit/moz.configure:
MOZ_STUB_INSTALLER Produce a stub installer
MOZ_SOURCE_REPO Project source repository
MOZ_SOURCE_CHANGESET Source changeset
MOZ_INCLUDE_SOURCE_INFO Include build repository informations
USE_FC_FREETYPE Force-enable the use of fontconfig freetype
MOZ_TELEMETRY_REPORTING Enable telemetry reporting
TAR Path to the tar program
UNZIP Path to the unzip program
MIDL_FLAGS Extra flags to pass to MIDL
MOZ_REQUIRE_SIGNING Enforce that add-ons are signed by the trusted root
DUMP_SYMS Path to the dump_syms program
MOZ_BRANDING_DIRECTORY Path to the directory used for branding resources
MOZ_OFFICIAL_BRANDING_DIRECTORY
Path to the directory used for official branding resources
MOZ_APP_DISPLAYNAME Branded application name
MOZ_DEV_EDITION Whether this a dev edition build
MOZ_MACBUNDLE_ID ID of the associated mac bundle
MOZ_APP_REMOTINGNAME Used for the internal program name, which affects profile name and remoting. If not set, defaults to MOZ_APP_NAME if the update channel is release, and MOZ_APP_NAME-MOZ_UPDATE_CHANNEL otherwise
MOZ_WINCONSOLE Whether we can create a console window
MOZ_CRASHREPORTER_MOCK Mock the crashreporter to test native GUIs
MOZ_SIMPLE_PACKAGE_NAME Package name override
MOZ_PKG_SPECIAL Name of special moz flavor
MOZ_PACKAGE_JSSHELL Whether the installer bundles the JS shell
Options from build/moz.configure/rust.configure:
RUSTC Path to the rust compiler
CARGO Path to the Cargo package manager
RUSTDOC Path to the rustdoc program
RUSTDOCFLAGS Extra options for the rustdoc program
RUSTFLAGS Rust compiler flags
RUSTC_OPT_LEVEL Rust compiler optimization level (-C opt-level=%s) [2]
Options from build/moz.configure/bindgen.configure:
CBINDGEN Path to cbindgen
RUSTFMT Path to the rustfmt program
BINDGEN_CFLAGS Options bindgen should pass to the C/C++ parser
Options from build/moz.configure/node.configure:
NODEJS Path to nodejs
Options from build/moz.configure/update-programs.configure:
MAR_CHANNEL_ID MAR channel identifier
ACCEPTED_MAR_CHANNEL_IDS Accepted MAR channel identifiersIt's possible that it was just propaganda to make West to believe in it.
If you like conspiracy theories... maybe anti-AI propaganda is sponsored by Russians. Kremlin is not a charity organization.
The problem is not censorship, but mob censorship.
There was an old Polish joke from the 1930s. A group of hares tried to cross the Polish border. They said they needed refuge because the Soviet secret police were arresting camels. But you’re not camels! Yes, but how can we prove it?
The skepticism toward AI today parallels the Soviet-era rejection of cybernetics.
From 1950 to 1954, the reception of cybernetics by the Soviet Union establishment was exclusively negative. The Soviet Department for Agitation and Propaganda had called for anti-Americanism to be intensified throughout Soviet media, and in an attempt to fill the Department's quotas, Soviet journalists latched on to cybernetics as an American "reactionary pseudoscience" to denounce and mock. These attacks were interpreted as a signal of an official attitude to cybernetics, Soviet writers thus portraying cybernetics as "a full embodiment of imperialist ideology” during Joseph Stalin's premiership.
_https://en.wikipedia.org/wiki/Cybernetics_in_the_Soviet_Union
Nikita Khrushchev called genetics and cybernetics "prostitutes of capitalism."
Nikolai Vavilov, a geneticist, was arrested in 1940 and died in prison in 1943.
He was a fellow of the Royal Society, that is why, perhaps, we know what happened to him.
The fates of many other Soviet scientists remain obscure.
_https://en.wikipedia.org/wiki/Nikolai_Vavilov
Victor Glushkov: Insights to Remember
... In 1952, he earned his doctorate by proving the Fifth Generalized Problem of Hilbert and continued his academic career. In 1956, he headed the modeling and computing technology laboratory at the Mechanical Institute in Kyiv, where Sergey Lebedev and his team assembled the first European computer MESM.
In 1957, Glushkov transformed his lab into the Computing Center of the Academy of Sciences. In five years, it evolved into the separate Institute of Cybernetics...It is fascinating to delve into the intriguing narrative of how Glushkov navigated resistance to defend his mindset against Soviet bureaucrats, dragging talented managers into the swamp of the communist party's internal struggle.
_https://glushkov.dataart.com
For some strange reason, Victor Glushkov did not hate AI. In 1970, he developed the Evidence Algorithm (EA) as a research program in artificial intelligence focused on automated theorem proving. He considered AI as a part of cybernetics.
The Soviet rejection of cybernetics and repression of scientists contributed to its problems with technological innovations and failure in the Cold War arms and computing race.
Censorship doesn't necessarily mean editing. There are many ways to silence someone.
There are many subtle ways to discourage someone from posting on a forum — beyond outright censorship. Constant accusations, excessive moderation, personal attacks, or repeatedly questioning someone's intent can create a hostile environment that pushes people away.
You seem to insist on your right to censor my posts.