Index: head/mail/thunderbird/Makefile =================================================================== --- head/mail/thunderbird/Makefile (revision 549982) +++ head/mail/thunderbird/Makefile (revision 549983) @@ -1,80 +1,80 @@ # Created by: Joe Marcus Clarke # $FreeBSD$ PORTNAME= thunderbird DISTVERSION= 78.3.0 -PORTREVISION= 2 +PORTREVISION= 3 CATEGORIES= mail news net-im MASTER_SITES= MOZILLA/${PORTNAME}/releases/${DISTVERSION}/source \ MOZILLA/${PORTNAME}/candidates/${DISTVERSION}-candidates/build2/source DISTFILES= ${DISTNAME}.source${EXTRACT_SUFX} MAINTAINER= gecko@FreeBSD.org COMMENT= Mozilla Thunderbird is standalone mail and news that stands above BUILD_DEPENDS= nspr>=4.26:devel/nspr \ nss>=3.56:security/nss \ icu>=67.1,1:devel/icu \ libevent>=2.1.8:devel/libevent \ harfbuzz>=2.6.6:print/harfbuzz \ graphite2>=1.3.14:graphics/graphite2 \ png>=1.6.35:graphics/png \ libvpx>=1.8.2:multimedia/libvpx \ ${PYTHON_PKGNAMEPREFIX}sqlite3>0:databases/py-sqlite3@${PY_FLAVOR} \ v4l_compat>0:multimedia/v4l_compat \ autoconf-2.13:devel/autoconf213 \ nasm:devel/nasm \ yasm:devel/yasm \ zip:archivers/zip LIB_DEPENDS= libjson-c.so:devel/json-c \ libbotan-2.so:security/botan2 SSP_UNSAFE= yes USE_GECKO= gecko USE_MOZILLA= -sqlite USES= tar:xz MOZ_OPTIONS= --enable-application=comm/mail --enable-official-branding MOZ_OPTIONS+= --with-system-bz2 --with-system-jsonc --with-system-botan MOZ_MK_OPTIONS= MOZ_THUNDERBIRD=1 MAIL_PKG_SHARED=1 MOZ_EXPORT= MOZ_THUNDERBIRD=1 MAIL_PKG_SHARED=1 PORTNAME_ICON= ${MOZILLA}.png PORTNAME_ICON_SRC= ${PREFIX}/lib/${MOZILLA}/chrome/icons/default/default48.png SYSTEM_PREFS= ${FAKEDIR}/lib/${PORTNAME}/defaults/pref/${PORTNAME}.js OPTIONS_DEFINE= LIGHTNING OPTIONS_DEFAULT=CANBERRA LIGHTNING .include "${.CURDIR}/../../www/firefox/Makefile.options" .include .if ${PORT_OPTIONS:MLIGHTNING} MOZ_OPTIONS+= --enable-calendar .else MOZ_OPTIONS+= --disable-calendar .endif post-extract: @${SED} -e 's|@PORTNAME_ICON@|${PORTNAME_ICON:R}|;s|@MOZILLA@|${MOZILLA}|' \ <${FILESDIR}/thunderbird.desktop.in >${WRKDIR}/${MOZILLA_EXEC_NAME}.desktop post-patch: @${REINPLACE_CMD} -e 's|%%LOCALBASE%%|${LOCALBASE}|g' \ ${WRKSRC}/comm/mail/app/nsMailApp.cpp pre-configure: (cd ${WRKSRC} && ${LOCALBASE}/bin/autoconf-2.13) (cd ${MOZSRC} && ${LOCALBASE}/bin/autoconf-2.13) (cd ${MOZSRC}/js/src/ && ${LOCALBASE}/bin/autoconf-2.13) port-pre-install: ${MKDIR} ${STAGEDIR}${PREFIX}/lib/${PORTNAME}/defaults post-install: ${INSTALL_DATA} ${WRKDIR}/${MOZILLA_EXEC_NAME}.desktop ${STAGEDIR}${PREFIX}/share/applications ${LN} -sf ${PORTNAME_ICON_SRC} ${STAGEDIR}${PREFIX}/share/pixmaps/${PORTNAME_ICON} .include Index: head/mail/thunderbird/files/patch-cubeb-oss =================================================================== --- head/mail/thunderbird/files/patch-cubeb-oss (revision 549982) +++ head/mail/thunderbird/files/patch-cubeb-oss (revision 549983) @@ -1,1274 +1,1309 @@ https://github.com/kinetiknz/cubeb/pull/600 --- dom/media/CubebUtils.cpp.orig 2020-08-19 02:08:51 UTC +++ dom/media/CubebUtils.cpp @@ -126,7 +126,7 @@ const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties"; const char* AUDIOSTREAM_BACKEND_ID_STR[] = { "jack", "pulse", "alsa", "audiounit", "audioqueue", "wasapi", - "winmm", "directsound", "sndio", "opensl", "audiotrack", "kai"}; + "winmm", "directsound", "sndio", "opensl", "oss", "audiotrack", "kai"}; /* Index for failures to create an audio stream the first time. */ const int CUBEB_BACKEND_INIT_FAILURE_FIRST = ArrayLength(AUDIOSTREAM_BACKEND_ID_STR); --- media/libcubeb/src/moz.build.orig 2020-08-19 02:09:19 UTC +++ media/libcubeb/src/moz.build @@ -40,6 +40,12 @@ if CONFIG['MOZ_JACK']: ] DEFINES['USE_JACK'] = True +if CONFIG['OS_ARCH'] in ('DragonFly', 'FreeBSD', 'SunOS'): + SOURCES += [ + 'cubeb_oss.c', + ] + DEFINES['USE_OSS'] = True + if CONFIG['OS_ARCH'] == 'OpenBSD': SOURCES += [ 'cubeb_sndio.c', --- media/libcubeb/src/cubeb.c.orig 2020-08-19 02:09:26 UTC +++ media/libcubeb/src/cubeb.c @@ -60,6 +60,9 @@ int sun_init(cubeb ** context, char const * context_name); #if defined(USE_OPENSL) int opensl_init(cubeb ** context, char const * context_name); #endif +#if defined(USE_OSS) +int oss_init(cubeb ** context, char const * context_name); +#endif #if defined(USE_AUDIOTRACK) int audiotrack_init(cubeb ** context, char const * context_name); #endif @@ -165,6 +168,10 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam #if defined(USE_OPENSL) init_oneshot = opensl_init; #endif + } else if (!strcmp(backend_name, "oss")) { +#if defined(USE_OSS) + init_oneshot = oss_init; +#endif } else if (!strcmp(backend_name, "audiotrack")) { #if defined(USE_AUDIOTRACK) init_oneshot = audiotrack_init; @@ -200,6 +207,9 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam #if defined(USE_ALSA) alsa_init, #endif +#if defined (USE_OSS) + oss_init, +#endif #if defined(USE_AUDIOUNIT_RUST) audiounit_rust_init, #endif --- /dev/null +++ media/libcubeb/src/cubeb_oss.c -@@ -0,0 +1,1210 @@ +@@ -0,0 +1,1245 @@ +/* + * Copyright © 2019-2020 Nia Alarie + * Copyright © 2020 Ka Ho Ng ++ * Copyright © 2020 The FreeBSD Foundation + * ++ * Portions of this software were developed by Ka Ho Ng ++ * under sponsorship from the FreeBSD Foundation. ++ * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ + +#if defined(__FreeBSD__) && __FreeBSD__ < 12 +#define _WITH_GETLINE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cubeb/cubeb.h" +#include "cubeb_mixer.h" +#include "cubeb_strings.h" +#include "cubeb-internal.h" + +/* Supported well by most hardware. */ +#ifndef OSS_PREFER_RATE +#define OSS_PREFER_RATE (48000) +#endif + +/* Standard acceptable minimum. */ +#ifndef OSS_LATENCY_MS +#define OSS_LATENCY_MS (8) +#endif + ++#ifndef OSS_NFRAGS ++#define OSS_NFRAGS (4) ++#endif ++ +#ifndef OSS_DEFAULT_DEVICE +#define OSS_DEFAULT_DEVICE "/dev/dsp" +#endif + +#ifndef OSS_DEFAULT_MIXER +#define OSS_DEFAULT_MIXER "/dev/mixer" +#endif + +#define ENV_AUDIO_DEVICE "AUDIO_DEVICE" + +#ifndef OSS_MAX_CHANNELS +# if defined(__FreeBSD__) || defined(__DragonFly__) +/* + * The current maximum number of channels supported + * on FreeBSD is 8. + * + * Reference: FreeBSD 12.1-RELEASE + */ +# define OSS_MAX_CHANNELS (8) +# elif defined(__sun__) +/* + * The current maximum number of channels supported + * on Illumos is 16. + * + * Reference: PSARC 2008/318 + */ +# define OSS_MAX_CHANNELS (16) +# else +# define OSS_MAX_CHANNELS (2) +# endif +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) +#define SNDSTAT_BEGIN_STR "Installed devices:" +#define SNDSTAT_USER_BEGIN_STR "Installed devices from userspace:" +#define SNDSTAT_FV_BEGIN_STR "File Versions:" +#endif + +static struct cubeb_ops const oss_ops; + +struct cubeb { + struct cubeb_ops const * ops; + + /* Our intern string store */ + pthread_mutex_t mutex; /* protects devid_strs */ + cubeb_strings *devid_strs; +}; + +struct oss_stream { + oss_devnode_t name; + int fd; + void * buf; + + struct stream_info { + int channels; + int sample_rate; + int fmt; + int precision; + } info; + + unsigned int frame_size; /* precision in bytes * channels */ + bool floating; +}; + +struct cubeb_stream { + struct cubeb * context; + void * user_ptr; + pthread_t thread; + pthread_cond_t doorbell_cv; + pthread_cond_t stopped_cv; + /* protects doorbell_cv, stopped_cv, running, destroying, volume, frames_written */ + pthread_mutex_t mtx; + bool thread_created; + bool running; + bool destroying; + float volume; + struct oss_stream play; + struct oss_stream record; + cubeb_data_callback data_cb; + cubeb_state_callback state_cb; + uint64_t frames_written; + unsigned int nfr; /* Number of frames allocated */ ++ unsigned int nfrags; ++ unsigned int bufframes; +}; + +static char const * +oss_cubeb_devid_intern(cubeb *context, char const * devid) +{ + char const *is; + pthread_mutex_lock(&context->mutex); + is = cubeb_strings_intern(context->devid_strs, devid); + pthread_mutex_unlock(&context->mutex); + return is; +} + +int +oss_init(cubeb **context, char const *context_name) { + cubeb * c; + + (void)context_name; + if ((c = calloc(1, sizeof(cubeb))) == NULL) { + return CUBEB_ERROR; + } + + if (cubeb_strings_init(&c->devid_strs) == CUBEB_ERROR) { + goto fail; + } + + if (pthread_mutex_init(&c->mutex, NULL) != 0) { + goto fail; + } + + c->ops = &oss_ops; + *context = c; + return CUBEB_OK; + +fail: + cubeb_strings_destroy(c->devid_strs); + free(c); + return CUBEB_ERROR; +} + +static void +oss_destroy(cubeb * context) +{ + pthread_mutex_destroy(&context->mutex); + cubeb_strings_destroy(context->devid_strs); + free(context); +} + +static char const * +oss_get_backend_id(cubeb * context) +{ + return "oss"; +} + +static int +oss_get_preferred_sample_rate(cubeb * context, uint32_t * rate) +{ + (void)context; + + *rate = OSS_PREFER_RATE; + return CUBEB_OK; +} + +static int +oss_get_max_channel_count(cubeb * context, uint32_t * max_channels) +{ + (void)context; + + *max_channels = OSS_MAX_CHANNELS; + return CUBEB_OK; +} + +static int +oss_get_min_latency(cubeb * context, cubeb_stream_params params, + uint32_t * latency_frames) +{ + (void)context; + + *latency_frames = (OSS_LATENCY_MS * params.rate) / 1000; + return CUBEB_OK; +} + +static void +oss_free_cubeb_device_info_strings(cubeb_device_info *cdi) +{ + free((char *)cdi->device_id); + free((char *)cdi->friendly_name); + free((char *)cdi->group_id); + cdi->device_id = NULL; + cdi->friendly_name = NULL; + cdi->group_id = NULL; +} + +#if defined(__FreeBSD__) || defined(__DragonFly__) +/* + * Check if the specified DSP is okay for the purpose specified + * in type. Here type can only specify one operation each time + * this helper is called. + * + * Return 0 if OK, otherwise 1. + */ +static int +oss_probe_open(const char *dsppath, cubeb_device_type type, + int *fdp, oss_audioinfo *resai) +{ + oss_audioinfo ai; + int error; + int oflags = (type == CUBEB_DEVICE_TYPE_INPUT) ? O_RDONLY : O_WRONLY; + int dspfd = open(dsppath, oflags); + if (dspfd == -1) + return 1; + + ai.dev = -1; + error = ioctl(dspfd, SNDCTL_AUDIOINFO, &ai); + if (error < 0) { + close(dspfd); + return 1; + } + + if (resai) + *resai = ai; + if (fdp) + *fdp = dspfd; + else + close(dspfd); + return 0; +} + +struct sndstat_info { + oss_devnode_t devname; + const char *desc; + cubeb_device_type type; + int preferred; +}; + +static int +oss_sndstat_line_parse(char *line, int is_ud, struct sndstat_info *sinfo) +{ + char *matchptr = line, *n = NULL; + struct sndstat_info res; + + memset(&res, 0, sizeof(res)); + + n = strchr(matchptr, ':'); + if (n == NULL) + goto fail; + if (is_ud == 0) { + unsigned int devunit; + + if (sscanf(matchptr, "pcm%u: ", &devunit) < 1) + goto fail; + + if (snprintf(res.devname, sizeof(res.devname), "/dev/dsp%u", devunit) < 1) + goto fail; + } else { + if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/"))) + goto fail; + + strlcpy(res.devname, "/dev/", sizeof(res.devname)); + strncat(res.devname, matchptr, n - matchptr); + } + matchptr = n + 1; + + n = strchr(matchptr, '<'); + if (n == NULL) + goto fail; + matchptr = n + 1; + n = strrchr(matchptr, '>'); + if (n == NULL) + goto fail; + *n = 0; + res.desc = matchptr; + matchptr = n + 1; + + n = strchr(matchptr, '('); + if (n == NULL) + goto fail; + matchptr = n + 1; + n = strrchr(matchptr, ')'); + if (n == NULL) + goto fail; + *n = 0; + if (!isdigit(matchptr[0])) { + if (strstr(matchptr, "play") != NULL) + res.type |= CUBEB_DEVICE_TYPE_OUTPUT; + if (strstr(matchptr, "rec") != NULL) + res.type |= CUBEB_DEVICE_TYPE_INPUT; + } else { + int p, r; + if (sscanf(matchptr, "%dp:%*dv/%dr:%*dv", &p, &r) != 2) + goto fail; + if (p > 0) + res.type |= CUBEB_DEVICE_TYPE_OUTPUT; + if (r > 0) + res.type |= CUBEB_DEVICE_TYPE_INPUT; + } + matchptr = n + 1; + if (strstr(matchptr, "default") != NULL) + res.preferred = 1; + + *sinfo = res; + return 0; + +fail: + return 1; +} + +/* + * XXX: On FreeBSD we have to rely on SNDCTL_CARDINFO to get all + * the usable audio devices currently, as SNDCTL_AUDIOINFO will + * never return directly usable audio device nodes. + */ +static int +oss_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection * collection) +{ + cubeb_device_info *devinfop = NULL; + char *line = NULL; + size_t linecap = 0; + FILE *sndstatfp = NULL; + int collection_cnt = 0; + int is_ud = 0; + int skipall = 0; + + devinfop = calloc(1, sizeof(cubeb_device_info)); + if (devinfop == NULL) + goto fail; + + sndstatfp = fopen("/dev/sndstat", "r"); + if (sndstatfp == NULL) + goto fail; + while (getline(&line, &linecap, sndstatfp) > 0) { + const char *devid = NULL; + struct sndstat_info sinfo; + oss_audioinfo ai; + + if (!strncmp(line, SNDSTAT_FV_BEGIN_STR, strlen(SNDSTAT_FV_BEGIN_STR))) { + skipall = 1; + continue; + } + if (!strncmp(line, SNDSTAT_BEGIN_STR, strlen(SNDSTAT_BEGIN_STR))) { + is_ud = 0; + skipall = 0; + continue; + } + if (!strncmp(line, SNDSTAT_USER_BEGIN_STR, strlen(SNDSTAT_USER_BEGIN_STR))) { + is_ud = 1; + skipall = 0; + continue; + } + if (skipall || isblank(line[0])) + continue; + + if (oss_sndstat_line_parse(line, is_ud, &sinfo)) + continue; + + devinfop[collection_cnt].type = 0; + switch (sinfo.type) { + case CUBEB_DEVICE_TYPE_INPUT: + if (type & CUBEB_DEVICE_TYPE_OUTPUT) + continue; + break; + case CUBEB_DEVICE_TYPE_OUTPUT: + if (type & CUBEB_DEVICE_TYPE_INPUT) + continue; + break; + case 0: + continue; + } + + if (oss_probe_open(sinfo.devname, type, NULL, &ai)) + continue; + + devid = oss_cubeb_devid_intern(context, sinfo.devname); + if (devid == NULL) + continue; + + devinfop[collection_cnt].device_id = strdup(sinfo.devname); + asprintf((char **)&devinfop[collection_cnt].friendly_name, "%s: %s", + sinfo.devname, sinfo.desc); + devinfop[collection_cnt].group_id = strdup(sinfo.devname); + devinfop[collection_cnt].vendor_name = NULL; + if (devinfop[collection_cnt].device_id == NULL || + devinfop[collection_cnt].friendly_name == NULL || + devinfop[collection_cnt].group_id == NULL) { + oss_free_cubeb_device_info_strings(&devinfop[collection_cnt]); + continue; + } + + devinfop[collection_cnt].type = type; + devinfop[collection_cnt].devid = devid; + devinfop[collection_cnt].state = CUBEB_DEVICE_STATE_ENABLED; + devinfop[collection_cnt].preferred = + (sinfo.preferred) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; + devinfop[collection_cnt].format = CUBEB_DEVICE_FMT_S16NE; + devinfop[collection_cnt].default_format = CUBEB_DEVICE_FMT_S16NE; + devinfop[collection_cnt].max_channels = ai.max_channels; + devinfop[collection_cnt].default_rate = OSS_PREFER_RATE; + devinfop[collection_cnt].max_rate = ai.max_rate; + devinfop[collection_cnt].min_rate = ai.min_rate; + devinfop[collection_cnt].latency_lo = 0; + devinfop[collection_cnt].latency_hi = 0; + + collection_cnt++; + + void *newp = reallocarray(devinfop, collection_cnt + 1, + sizeof(cubeb_device_info)); + if (newp == NULL) + goto fail; + devinfop = newp; + } + + free(line); + fclose(sndstatfp); + + collection->count = collection_cnt; + collection->device = devinfop; + + return CUBEB_OK; + +fail: + free(line); + if (sndstatfp) + fclose(sndstatfp); + free(devinfop); + return CUBEB_ERROR; +} + +#else + +static int +oss_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection * collection) +{ + oss_sysinfo si; + int error, i; + cubeb_device_info *devinfop = NULL; + int collection_cnt = 0; + int mixer_fd = -1; + + mixer_fd = open(OSS_DEFAULT_MIXER, O_RDWR); + if (mixer_fd == -1) { + LOG("Failed to open mixer %s. errno: %d", OSS_DEFAULT_MIXER, errno); + return CUBEB_ERROR; + } + + error = ioctl(mixer_fd, SNDCTL_SYSINFO, &si); + if (error) { + LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d", OSS_DEFAULT_MIXER, errno); + goto fail; + } + + devinfop = calloc(si.numaudios, sizeof(cubeb_device_info)); + if (devinfop == NULL) + goto fail; + + collection->count = 0; + for (i = 0; i < si.numaudios; i++) { + oss_audioinfo ai; + cubeb_device_info cdi = { 0 }; + const char *devid = NULL; + + ai.dev = i; + error = ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai); + if (error) + goto fail; + + assert(ai.dev < si.numaudios); + if (!ai.enabled) + continue; + + cdi.type = 0; + switch (ai.caps & DSP_CAP_DUPLEX) { + case DSP_CAP_INPUT: + if (type & CUBEB_DEVICE_TYPE_OUTPUT) + continue; + break; + case DSP_CAP_OUTPUT: + if (type & CUBEB_DEVICE_TYPE_INPUT) + continue; + break; + case 0: + continue; + } + cdi.type = type; + + devid = oss_cubeb_devid_intern(context, ai.devnode); + cdi.device_id = strdup(ai.name); + cdi.friendly_name = strdup(ai.name); + cdi.group_id = strdup(ai.name); + if (devid == NULL || cdi.device_id == NULL || cdi.friendly_name == NULL || + cdi.group_id == NULL) { + oss_free_cubeb_device_info_strings(&cdi); + continue; + } + + cdi.devid = devid; + cdi.vendor_name = NULL; + cdi.state = CUBEB_DEVICE_STATE_ENABLED; + cdi.preferred = CUBEB_DEVICE_PREF_NONE; + cdi.format = CUBEB_DEVICE_FMT_S16NE; + cdi.default_format = CUBEB_DEVICE_FMT_S16NE; + cdi.max_channels = ai.max_channels; + cdi.default_rate = OSS_PREFER_RATE; + cdi.max_rate = ai.max_rate; + cdi.min_rate = ai.min_rate; + cdi.latency_lo = 0; + cdi.latency_hi = 0; + + devinfop[collection_cnt++] = cdi; + } + + collection->count = collection_cnt; + collection->device = devinfop; + + if (mixer_fd != -1) + close(mixer_fd); + return CUBEB_OK; + +fail: + if (mixer_fd != -1) + close(mixer_fd); + free(devinfop); + return CUBEB_ERROR; +} + +#endif + +static int +oss_device_collection_destroy(cubeb * context, + cubeb_device_collection * collection) +{ + size_t i; + for (i = 0; i < collection->count; i++) { + oss_free_cubeb_device_info_strings(&collection->device[i]); + } + free(collection->device); + collection->device = NULL; + collection->count = 0; + return 0; +} + +static unsigned int +oss_chn_from_cubeb(cubeb_channel chn) +{ + switch (chn) { + case CHANNEL_FRONT_LEFT: + return CHID_L; + case CHANNEL_FRONT_RIGHT: + return CHID_R; + case CHANNEL_FRONT_CENTER: + return CHID_C; + case CHANNEL_LOW_FREQUENCY: + return CHID_LFE; + case CHANNEL_BACK_LEFT: + return CHID_LR; + case CHANNEL_BACK_RIGHT: + return CHID_RR; + case CHANNEL_SIDE_LEFT: + return CHID_LS; + case CHANNEL_SIDE_RIGHT: + return CHID_RS; + default: + return CHID_UNDEF; + } +} + +static unsigned long long +oss_cubeb_layout_to_chnorder(cubeb_channel_layout layout) +{ + unsigned int i, nchns = 0; + unsigned long long chnorder = 0; + + for (i = 0; layout; i++, layout >>= 1) { + unsigned long long chid = oss_chn_from_cubeb((layout & 1) << i); + if (chid == CHID_UNDEF) + continue; + + chnorder |= (chid & 0xf) << nchns * 4; + nchns++; + } + + return chnorder; +} + +static int +oss_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params, + struct stream_info * sinfo) +{ + unsigned long long chnorder; + + sinfo->channels = params->channels; + sinfo->sample_rate = params->rate; + switch (params->format) { + case CUBEB_SAMPLE_S16LE: + sinfo->fmt = AFMT_S16_LE; + sinfo->precision = 16; + break; + case CUBEB_SAMPLE_S16BE: + sinfo->fmt = AFMT_S16_BE; + sinfo->precision = 16; + break; + case CUBEB_SAMPLE_FLOAT32NE: + sinfo->fmt = AFMT_S32_NE; + sinfo->precision = 32; + break; + default: + LOG("Unsupported format"); + return CUBEB_ERROR_INVALID_FORMAT; + } + if (ioctl(fd, SNDCTL_DSP_CHANNELS, &sinfo->channels) == -1) { + return CUBEB_ERROR; + } + if (ioctl(fd, SNDCTL_DSP_SETFMT, &sinfo->fmt) == -1) { + return CUBEB_ERROR; + } + if (ioctl(fd, SNDCTL_DSP_SPEED, &sinfo->sample_rate) == -1) { + return CUBEB_ERROR; + } + /* Mono layout is an exception */ + if (params->layout != CUBEB_LAYOUT_UNDEFINED && params->layout != CUBEB_LAYOUT_MONO) { + chnorder = oss_cubeb_layout_to_chnorder(params->layout); + if (ioctl(fd, SNDCTL_DSP_SET_CHNORDER, &chnorder) == -1) + LOG("Non-fatal error %d occured when setting channel order.", errno); + } + return CUBEB_OK; +} + +static int +oss_stream_stop(cubeb_stream * s) +{ + pthread_mutex_lock(&s->mtx); + if (s->thread_created && s->running) { + s->running = false; + pthread_cond_signal(&s->doorbell_cv); + pthread_cond_wait(&s->stopped_cv, &s->mtx); + } + pthread_mutex_unlock(&s->mtx); + return CUBEB_OK; +} + +static void +oss_stream_destroy(cubeb_stream * s) +{ + pthread_mutex_lock(&s->mtx); + if (s->thread_created) { + s->destroying = true; + pthread_cond_signal(&s->doorbell_cv); + } + pthread_mutex_unlock(&s->mtx); + pthread_join(s->thread, NULL); + + pthread_cond_destroy(&s->doorbell_cv); + pthread_cond_destroy(&s->stopped_cv); + pthread_mutex_destroy(&s->mtx); + if (s->play.fd != -1) { + close(s->play.fd); + } + if (s->record.fd != -1) { + close(s->record.fd); + } + free(s->play.buf); + free(s->record.buf); + free(s); +} + +static void +oss_float_to_linear32(void * buf, unsigned sample_count, float vol) +{ + float * in = buf; + int32_t * out = buf; + int32_t * tail = out + sample_count; + + while (out < tail) { + int64_t f = *(in++) * vol * 0x80000000LL; + if (f < -INT32_MAX) + f = -INT32_MAX; + else if (f > INT32_MAX) + f = INT32_MAX; + *(out++) = f; + } +} + +static void +oss_linear32_to_float(void * buf, unsigned sample_count) +{ + int32_t * in = buf; + float * out = buf; + float * tail = out + sample_count; + + while (out < tail) { + *(out++) = (1.0 / 0x80000000LL) * *(in++); + } +} + +static void +oss_linear16_set_vol(int16_t * buf, unsigned sample_count, float vol) +{ + unsigned i; + int32_t multiplier = vol * 0x8000; + + for (i = 0; i < sample_count; ++i) { + buf[i] = (buf[i] * multiplier) >> 15; + } +} + +static int +oss_audio_loop(cubeb_stream * s) +{ + int state = CUBEB_STATE_STARTED; -+ long nfr = 0; + int trig = 0; + int drain = 0; + struct pollfd pfds[2]; -+ bool cbready = true; ++ unsigned int ppending, rpending; + + pfds[0].fd = s->play.fd; + pfds[0].events = POLLOUT; + pfds[1].fd = s->record.fd; + pfds[1].events = POLLIN; + ++ ppending = 0; ++ rpending = s->bufframes; ++ + if (s->record.fd != -1) { + if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) { + LOG("Error %d occured when setting trigger on record fd", errno); + state = CUBEB_STATE_ERROR; + goto out; + } + trig |= PCM_ENABLE_INPUT; + if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) { + LOG("Error %d occured when setting trigger on record fd", errno); + state = CUBEB_STATE_ERROR; + goto out; + } ++ memset(s->record.buf, 0, s->bufframes * s->record.frame_size); + } + -+ if (s->record.fd != -1) -+ memset(s->record.buf, 0, s->nfr); -+ + while (1) { ++ long nfr = 0; ++ + pthread_mutex_lock(&s->mtx); + if (!s->running || s->destroying) { + pthread_mutex_unlock(&s->mtx); + state = CUBEB_STATE_STOPPED; + break; + } + pthread_mutex_unlock(&s->mtx); + if (s->play.fd == -1 && s->record.fd == -1) { + /* + * Stop here if the stream is not play & record stream, + * play-only stream or record-only stream + */ + + state = CUBEB_STATE_STOPPED; + break; + } + -+ if (s->record.fd != -1 && s->record.floating) { -+ oss_linear32_to_float(s->record.buf, s->record.info.channels * s->nfr); -+ } -+ if (cbready) { -+ nfr = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, s->nfr); ++ while ((s->bufframes - ppending) >= s->nfr && rpending >= s->nfr) { ++ long n = ((s->bufframes - ppending) < rpending) ? s->bufframes - ppending : rpending; ++ char *rptr = NULL, *pptr = NULL; ++ if (s->record.fd != -1) ++ rptr = (char *)s->record.buf; ++ if (s->play.fd != -1) ++ pptr = (char *)s->play.buf + ppending * s->play.frame_size; ++ if (s->record.fd != -1 && s->record.floating) { ++ oss_linear32_to_float(s->record.buf, s->record.info.channels * n); ++ } ++ nfr = s->data_cb(s, s->user_ptr, rptr, pptr, n); + if (nfr == CUBEB_ERROR) { + state = CUBEB_STATE_ERROR; + goto out; + } -+ cbready = false; -+ } -+ if (s->play.fd != -1) { -+ float vol; ++ if (pptr) { ++ float vol; + -+ pthread_mutex_lock(&s->mtx); -+ vol = s->volume; -+ pthread_mutex_unlock(&s->mtx); ++ pthread_mutex_lock(&s->mtx); ++ vol = s->volume; ++ pthread_mutex_unlock(&s->mtx); + -+ if (s->play.floating) { -+ oss_float_to_linear32(s->play.buf, s->play.info.channels * nfr, vol); -+ } else { -+ oss_linear16_set_vol(s->play.buf, s->play.info.channels * nfr, vol); ++ if (s->play.floating) { ++ oss_float_to_linear32(pptr, s->play.info.channels * nfr, vol); ++ } else { ++ oss_linear16_set_vol((int16_t *)pptr, s->play.info.channels * nfr, vol); ++ } + } -+ } -+ if (nfr < (long)s->nfr) { -+ if (s->play.fd != -1) { -+ drain = 1; -+ } else { -+ /* -+ * This is a record-only stream and number of frames -+ * returned from data_cb() is smaller than number -+ * of frames required to read. Stop here. -+ */ ++ if (pptr) { ++ ppending += nfr; ++ assert(ppending <= s->bufframes); ++ } ++ if (rptr) { ++ assert(rpending >= nfr); ++ rpending -= nfr; ++ memmove(rptr, rptr + nfr * s->record.frame_size, ++ (s->bufframes - nfr) * s->record.frame_size); ++ } ++ if (nfr < n) { ++ if (s->play.fd != -1) { ++ drain = 1; ++ break; ++ } else { ++ /* ++ * This is a record-only stream and number of frames ++ * returned from data_cb() is smaller than number ++ * of frames required to read. Stop here. ++ */ + -+ state = CUBEB_STATE_STOPPED; -+ break; ++ state = CUBEB_STATE_STOPPED; ++ goto out; ++ } + } + } + -+ size_t to_write = s->play.fd != -1 ? nfr : 0; -+ size_t to_read = s->record.fd != -1 ? s->nfr : 0; -+ size_t write_ofs = 0; -+ size_t read_ofs = 0; -+ while (to_write > 0 || to_read > 0) { -+ size_t bytes; -+ ssize_t n, frames; -+ int nfds; ++ ssize_t n, frames; ++ int nfds; + -+ pfds[0].revents = 0; -+ pfds[1].revents = 0; ++ pfds[0].revents = 0; ++ pfds[1].revents = 0; + -+ nfds = poll(pfds, 2, 1000); -+ if (nfds == -1) { -+ if (errno == EINTR) -+ continue; -+ LOG("Error %d occured when polling playback and record fd", errno); -+ state = CUBEB_STATE_ERROR; -+ goto out; -+ } else if (nfds == 0) ++ nfds = poll(pfds, 2, 1000); ++ if (nfds == -1) { ++ if (errno == EINTR) + continue; ++ LOG("Error %d occured when polling playback and record fd", errno); ++ state = CUBEB_STATE_ERROR; ++ goto out; ++ } else if (nfds == 0) ++ continue; + -+ if ((pfds[0].revents & (POLLERR | POLLHUP)) || -+ (pfds[1].revents & (POLLERR | POLLHUP))) { -+ LOG("Error occured on playback, record fds"); -+ state = CUBEB_STATE_ERROR; -+ goto out; -+ } ++ if ((pfds[0].revents & (POLLERR | POLLHUP)) || ++ (pfds[1].revents & (POLLERR | POLLHUP))) { ++ LOG("Error occured on playback, record fds"); ++ state = CUBEB_STATE_ERROR; ++ goto out; ++ } + -+ if (to_write > 0 && pfds[0].revents) { -+ bytes = to_write * s->play.frame_size; -+ if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, bytes)) < 0) { ++ if (pfds[0].revents) { ++ while (ppending > 0) { ++ size_t bytes = ppending * s->play.frame_size; ++ if ((n = write(s->play.fd, (uint8_t *)s->play.buf, bytes)) < 0) { ++ if (errno == EINTR) ++ continue; ++ if (errno == EAGAIN) { ++ if (drain) ++ continue; ++ break; ++ } + state = CUBEB_STATE_ERROR; + goto out; + } + frames = n / s->play.frame_size; + pthread_mutex_lock(&s->mtx); + s->frames_written += frames; + pthread_mutex_unlock(&s->mtx); -+ to_write -= frames; -+ write_ofs += n; ++ ppending -= frames; ++ memmove(s->play.buf, (uint8_t *)s->play.buf + n, ++ (s->bufframes - frames) * s->play.frame_size); + } -+ if (to_read > 0 && pfds[1].revents) { -+ bytes = to_read * s->record.frame_size; -+ if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, -+ bytes)) < 0) { ++ } ++ if (pfds[1].revents) { ++ while (s->bufframes - rpending > 0) { ++ size_t bytes = (s->bufframes - rpending) * s->record.frame_size; ++ size_t read_ofs = rpending * s->record.frame_size; ++ if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, bytes)) < 0) { ++ if (errno == EINTR) ++ continue; ++ if (errno == EAGAIN) ++ break; + state = CUBEB_STATE_ERROR; + goto out; + } + frames = n / s->record.frame_size; -+ to_read -= frames; -+ read_ofs += n; ++ rpending += frames; + } + } + if (drain) { + state = CUBEB_STATE_DRAINED; + break; + } -+ cbready = true; + } + +out: + return state; +} + +static void * +oss_io_routine(void *arg) +{ + cubeb_stream *s = arg; + cubeb_state state = CUBEB_STATE_STARTED; + + do { + pthread_mutex_lock(&s->mtx); + if (s->destroying) { + pthread_mutex_unlock(&s->mtx); + break; + } + pthread_mutex_unlock(&s->mtx); + ++ state = CUBEB_STATE_STARTED; + s->state_cb(s, s->user_ptr, state); + + state = oss_audio_loop(s); + assert(state != CUBEB_STATE_STARTED); + + if (s->record.fd != -1) + ioctl(s->record.fd, SNDCTL_DSP_HALT_INPUT, NULL); + s->state_cb(s, s->user_ptr, state); + + pthread_mutex_lock(&s->mtx); + pthread_cond_signal(&s->stopped_cv); + if (s->destroying) { + pthread_mutex_unlock(&s->mtx); + break; + } + pthread_mutex_unlock(&s->mtx); + + pthread_mutex_lock(&s->mtx); + pthread_cond_wait(&s->doorbell_cv, &s->mtx); + pthread_mutex_unlock(&s->mtx); + } while (1); + + pthread_mutex_lock(&s->mtx); + s->thread_created = false; + pthread_mutex_unlock(&s->mtx); + return NULL; +} + +static inline int +oss_calc_frag_shift(unsigned int frames, unsigned int frame_size) +{ + int n = 4; -+ int blksize = (frames * frame_size + 4 - 1) / 4; ++ int blksize = (frames * frame_size + OSS_NFRAGS - 1) / OSS_NFRAGS; + while ((1 << n) < blksize) + n++; + return n; +} + +static inline int +oss_get_frag_params(unsigned int shift) +{ -+ return (8 << 16) | shift; ++ return (OSS_NFRAGS << 16) | shift; +} + +static int +oss_stream_init(cubeb * context, + 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 ret = CUBEB_OK; + unsigned int playnfr = 0, recnfr = 0; + cubeb_stream *s = NULL; + const char *defdsp; + + if (!(defdsp = getenv(ENV_AUDIO_DEVICE)) || *defdsp == '\0') + defdsp = OSS_DEFAULT_DEVICE; + + (void)stream_name; + if ((s = calloc(1, sizeof(cubeb_stream))) == NULL) { + ret = CUBEB_ERROR; + goto error; + } + s->record.fd = s->play.fd = -1; + s->nfr = latency_frames; + if (input_device != NULL) { + strlcpy(s->record.name, input_device, sizeof(s->record.name)); + } else { + strlcpy(s->record.name, defdsp, sizeof(s->record.name)); + } + if (output_device != NULL) { + strlcpy(s->play.name, output_device, sizeof(s->play.name)); + } else { + strlcpy(s->play.name, defdsp, sizeof(s->play.name)); + } + if (input_stream_params != NULL) { + unsigned int nb_channels; + if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { + LOG("Loopback not supported"); + ret = CUBEB_ERROR_NOT_SUPPORTED; + goto error; + } + nb_channels = cubeb_channel_layout_nb_channels(input_stream_params->layout); + if (input_stream_params->layout != CUBEB_LAYOUT_UNDEFINED && + nb_channels != input_stream_params->channels) { + LOG("input_stream_params->layout does not match input_stream_params->channels"); + ret = CUBEB_ERROR_INVALID_PARAMETER; + goto error; + } + if (s->record.fd == -1) { -+ if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) { ++ if ((s->record.fd = open(s->record.name, O_RDONLY | O_NONBLOCK)) == -1) { + LOG("Audio device \"%s\" could not be opened as read-only", + s->record.name); + ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; + goto error; + } + } + if ((ret = oss_copy_params(s->record.fd, s, input_stream_params, + &s->record.info)) != CUBEB_OK) { + LOG("Setting record params failed"); + goto error; + } + s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); + s->record.frame_size = s->record.info.channels * (s->record.info.precision / 8); + recnfr = (1 << oss_calc_frag_shift(s->nfr, s->record.frame_size)) / s->record.frame_size; + } + if (output_stream_params != NULL) { + unsigned int nb_channels; + if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { + LOG("Loopback not supported"); + ret = CUBEB_ERROR_NOT_SUPPORTED; + goto error; + } + nb_channels = cubeb_channel_layout_nb_channels(output_stream_params->layout); + if (output_stream_params->layout != CUBEB_LAYOUT_UNDEFINED && + nb_channels != output_stream_params->channels) { + LOG("output_stream_params->layout does not match output_stream_params->channels"); + ret = CUBEB_ERROR_INVALID_PARAMETER; + goto error; + } + if (s->play.fd == -1) { -+ if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) { ++ if ((s->play.fd = open(s->play.name, O_WRONLY | O_NONBLOCK)) == -1) { + LOG("Audio device \"%s\" could not be opened as write-only", + s->play.name); + ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; + goto error; + } + } + if ((ret = oss_copy_params(s->play.fd, s, output_stream_params, + &s->play.info)) != CUBEB_OK) { + LOG("Setting play params failed"); + goto error; + } + s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); + s->play.frame_size = s->play.info.channels * (s->play.info.precision / 8); + playnfr = (1 << oss_calc_frag_shift(s->nfr, s->play.frame_size)) / s->play.frame_size; + } + /* Use the largest nframes among playing and recording streams */ + s->nfr = (playnfr > recnfr) ? playnfr : recnfr; ++ s->nfrags = OSS_NFRAGS; ++ s->bufframes = s->nfr * s->nfrags; + if (s->play.fd != -1) { + int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->play.frame_size)); + if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) + LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", + frag); + } + if (s->record.fd != -1) { + int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->record.frame_size)); + if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) + LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", + frag); + } + s->context = context; + s->volume = 1.0; + s->state_cb = state_callback; + s->data_cb = data_callback; + s->user_ptr = user_ptr; + + if (pthread_mutex_init(&s->mtx, NULL) != 0) { + LOG("Failed to create mutex"); + goto error; + } + if (pthread_cond_init(&s->doorbell_cv, NULL) != 0) { + LOG("Failed to create cv"); + goto error; + } + if (pthread_cond_init(&s->stopped_cv, NULL) != 0) { + LOG("Failed to create cv"); + goto error; + } + + if (s->play.fd != -1) { -+ if ((s->play.buf = calloc(s->nfr, s->play.frame_size)) == NULL) { ++ if ((s->play.buf = calloc(s->bufframes, s->play.frame_size)) == NULL) { + ret = CUBEB_ERROR; + goto error; + } + } + if (s->record.fd != -1) { -+ if ((s->record.buf = calloc(s->nfr, s->record.frame_size)) == NULL) { ++ if ((s->record.buf = calloc(s->bufframes, s->record.frame_size)) == NULL) { + ret = CUBEB_ERROR; + goto error; + } + } + + *stream = s; + return CUBEB_OK; +error: + if (s != NULL) { + oss_stream_destroy(s); + } + return ret; +} + +static int +oss_stream_thr_create(cubeb_stream * s) +{ + if (s->thread_created) { -+ pthread_mutex_lock(&s->mtx); + pthread_cond_signal(&s->doorbell_cv); -+ pthread_mutex_unlock(&s->mtx); -+ + return CUBEB_OK; + } + + if (pthread_create(&s->thread, NULL, oss_io_routine, s) != 0) { + LOG("Couldn't create thread"); + return CUBEB_ERROR; + } + + return CUBEB_OK; +} + +static int +oss_stream_start(cubeb_stream * s) +{ + pthread_mutex_lock(&s->mtx); + if (!s->running && oss_stream_thr_create(s) != CUBEB_OK) { + pthread_mutex_unlock(&s->mtx); + return CUBEB_ERROR; + } + s->thread_created = true; + s->running = true; + pthread_mutex_unlock(&s->mtx); + return CUBEB_OK; +} + +static int +oss_stream_get_position(cubeb_stream * s, uint64_t * position) +{ + pthread_mutex_lock(&s->mtx); + *position = s->frames_written; + pthread_mutex_unlock(&s->mtx); + return CUBEB_OK; +} + +static int +oss_stream_get_latency(cubeb_stream * s, uint32_t * latency) +{ + int delay; + + if (ioctl(s->play.fd, SNDCTL_DSP_GETODELAY, &delay) == -1) { + return CUBEB_ERROR; + } + + /* Return number of frames there */ + *latency = delay / s->play.frame_size; + return CUBEB_OK; +} + +static int +oss_stream_set_volume(cubeb_stream * stream, float volume) +{ + if (volume < 0.0) + volume = 0.0; + else if (volume > 1.0) + volume = 1.0; + pthread_mutex_lock(&stream->mtx); + stream->volume = volume; + pthread_mutex_unlock(&stream->mtx); + return CUBEB_OK; +} + +static int +oss_get_current_device(cubeb_stream * stream, cubeb_device ** const device) +{ + *device = calloc(1, sizeof(cubeb_device)); + if (*device == NULL) { + return CUBEB_ERROR; + } + (*device)->input_name = stream->record.fd != -1 ? + strdup(stream->record.name) : NULL; + (*device)->output_name = stream->play.fd != -1 ? + strdup(stream->play.name) : NULL; + return CUBEB_OK; +} + +static int +oss_stream_device_destroy(cubeb_stream * stream, cubeb_device * device) +{ + (void)stream; + free(device->input_name); + free(device->output_name); + free(device); + return CUBEB_OK; +} + +static struct cubeb_ops const oss_ops = { + .init = oss_init, + .get_backend_id = oss_get_backend_id, + .get_max_channel_count = oss_get_max_channel_count, + .get_min_latency = oss_get_min_latency, + .get_preferred_sample_rate = oss_get_preferred_sample_rate, + .enumerate_devices = oss_enumerate_devices, + .device_collection_destroy = oss_device_collection_destroy, + .destroy = oss_destroy, + .stream_init = oss_stream_init, + .stream_destroy = oss_stream_destroy, + .stream_start = oss_stream_start, + .stream_stop = oss_stream_stop, + .stream_reset_default_device = NULL, + .stream_get_position = oss_stream_get_position, + .stream_get_latency = oss_stream_get_latency, + .stream_get_input_latency = NULL, + .stream_set_volume = oss_stream_set_volume, + .stream_get_current_device = oss_get_current_device, + .stream_device_destroy = oss_stream_device_destroy, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = NULL}; Index: head/www/firefox/Makefile =================================================================== --- head/www/firefox/Makefile (revision 549982) +++ head/www/firefox/Makefile (revision 549983) @@ -1,61 +1,61 @@ # Created by: Alan Eldridge # $FreeBSD$ PORTNAME= firefox DISTVERSION= 81.0 -PORTREVISION= 1 +PORTREVISION= 2 PORTEPOCH= 2 CATEGORIES= www MASTER_SITES= MOZILLA/${PORTNAME}/releases/${DISTVERSION}/source \ MOZILLA/${PORTNAME}/candidates/${DISTVERSION}-candidates/build2/source DISTFILES= ${DISTNAME}.source${EXTRACT_SUFX} MAINTAINER= gecko@FreeBSD.org COMMENT= Web browser based on the browser portion of Mozilla BUILD_DEPENDS= nspr>=4.26:devel/nspr \ nss>=3.56:security/nss \ icu>=67.1,1:devel/icu \ libevent>=2.1.8:devel/libevent \ harfbuzz>=2.6.8:print/harfbuzz \ graphite2>=1.3.14:graphics/graphite2 \ png>=1.6.35:graphics/png \ libvpx>=1.8.2:multimedia/libvpx \ ${PYTHON_PKGNAMEPREFIX}sqlite3>0:databases/py-sqlite3@${PY_FLAVOR} \ v4l_compat>0:multimedia/v4l_compat \ autoconf-2.13:devel/autoconf213 \ nasm:devel/nasm \ yasm:devel/yasm \ zip:archivers/zip USE_GECKO= gecko CONFLICTS_INSTALL= firefox-esr USE_MOZILLA= -sqlite USES= tar:xz FIREFOX_ICON= ${MOZILLA}.png FIREFOX_ICON_SRC= ${PREFIX}/lib/${MOZILLA}/browser/chrome/icons/default/default48.png FIREFOX_DESKTOP= ${MOZSRC}/taskcluster/docker/${MOZILLA}-snap/${MOZILLA}.desktop MOZ_OPTIONS= --enable-application=browser \ --enable-official-branding .include "${.CURDIR}/../../www/firefox/Makefile.options" post-patch: @${REINPLACE_CMD} -e 's/%u/%U/' -e '/X-MultipleArgs/d' \ -e '/^Icon/s/=.*/=${FIREFOX_ICON:R}/' \ ${FIREFOX_DESKTOP} @${REINPLACE_CMD} -e 's|%%LOCALBASE%%|${LOCALBASE}|g' \ ${WRKSRC}/browser/app/nsBrowserApp.cpp pre-configure: (cd ${WRKSRC} && ${LOCALBASE}/bin/autoconf-2.13) (cd ${WRKSRC}/js/src/ && ${LOCALBASE}/bin/autoconf-2.13) post-install: ${INSTALL_DATA} ${FIREFOX_DESKTOP} ${STAGEDIR}${PREFIX}/share/applications/ ${MKDIR} ${STAGEDIR}${PREFIX}/share/pixmaps ${LN} -sf ${FIREFOX_ICON_SRC} ${STAGEDIR}${PREFIX}/share/pixmaps/${FIREFOX_ICON} .include Index: head/www/firefox/files/patch-cubeb-oss =================================================================== --- head/www/firefox/files/patch-cubeb-oss (revision 549982) +++ head/www/firefox/files/patch-cubeb-oss (revision 549983) @@ -1,1274 +1,1309 @@ https://github.com/kinetiknz/cubeb/pull/600 --- dom/media/CubebUtils.cpp.orig 2020-08-19 02:08:51 UTC +++ dom/media/CubebUtils.cpp @@ -126,7 +126,7 @@ const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties"; const char* AUDIOSTREAM_BACKEND_ID_STR[] = { "jack", "pulse", "alsa", "audiounit", "audioqueue", "wasapi", - "winmm", "directsound", "sndio", "opensl", "audiotrack", "kai"}; + "winmm", "directsound", "sndio", "opensl", "oss", "audiotrack", "kai"}; /* Index for failures to create an audio stream the first time. */ const int CUBEB_BACKEND_INIT_FAILURE_FIRST = ArrayLength(AUDIOSTREAM_BACKEND_ID_STR); --- media/libcubeb/src/moz.build.orig 2020-08-19 02:09:19 UTC +++ media/libcubeb/src/moz.build @@ -40,6 +40,12 @@ if CONFIG['MOZ_JACK']: ] DEFINES['USE_JACK'] = True +if CONFIG['OS_ARCH'] in ('DragonFly', 'FreeBSD', 'SunOS'): + SOURCES += [ + 'cubeb_oss.c', + ] + DEFINES['USE_OSS'] = True + if CONFIG['OS_ARCH'] == 'OpenBSD': SOURCES += [ 'cubeb_sndio.c', --- media/libcubeb/src/cubeb.c.orig 2020-08-19 02:09:26 UTC +++ media/libcubeb/src/cubeb.c @@ -60,6 +60,9 @@ int sun_init(cubeb ** context, char const * context_name); #if defined(USE_OPENSL) int opensl_init(cubeb ** context, char const * context_name); #endif +#if defined(USE_OSS) +int oss_init(cubeb ** context, char const * context_name); +#endif #if defined(USE_AUDIOTRACK) int audiotrack_init(cubeb ** context, char const * context_name); #endif @@ -165,6 +168,10 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam #if defined(USE_OPENSL) init_oneshot = opensl_init; #endif + } else if (!strcmp(backend_name, "oss")) { +#if defined(USE_OSS) + init_oneshot = oss_init; +#endif } else if (!strcmp(backend_name, "audiotrack")) { #if defined(USE_AUDIOTRACK) init_oneshot = audiotrack_init; @@ -200,6 +207,9 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam #if defined(USE_ALSA) alsa_init, #endif +#if defined (USE_OSS) + oss_init, +#endif #if defined(USE_AUDIOUNIT_RUST) audiounit_rust_init, #endif --- /dev/null +++ media/libcubeb/src/cubeb_oss.c -@@ -0,0 +1,1210 @@ +@@ -0,0 +1,1245 @@ +/* + * Copyright © 2019-2020 Nia Alarie + * Copyright © 2020 Ka Ho Ng ++ * Copyright © 2020 The FreeBSD Foundation + * ++ * Portions of this software were developed by Ka Ho Ng ++ * under sponsorship from the FreeBSD Foundation. ++ * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ + +#if defined(__FreeBSD__) && __FreeBSD__ < 12 +#define _WITH_GETLINE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cubeb/cubeb.h" +#include "cubeb_mixer.h" +#include "cubeb_strings.h" +#include "cubeb-internal.h" + +/* Supported well by most hardware. */ +#ifndef OSS_PREFER_RATE +#define OSS_PREFER_RATE (48000) +#endif + +/* Standard acceptable minimum. */ +#ifndef OSS_LATENCY_MS +#define OSS_LATENCY_MS (8) +#endif + ++#ifndef OSS_NFRAGS ++#define OSS_NFRAGS (4) ++#endif ++ +#ifndef OSS_DEFAULT_DEVICE +#define OSS_DEFAULT_DEVICE "/dev/dsp" +#endif + +#ifndef OSS_DEFAULT_MIXER +#define OSS_DEFAULT_MIXER "/dev/mixer" +#endif + +#define ENV_AUDIO_DEVICE "AUDIO_DEVICE" + +#ifndef OSS_MAX_CHANNELS +# if defined(__FreeBSD__) || defined(__DragonFly__) +/* + * The current maximum number of channels supported + * on FreeBSD is 8. + * + * Reference: FreeBSD 12.1-RELEASE + */ +# define OSS_MAX_CHANNELS (8) +# elif defined(__sun__) +/* + * The current maximum number of channels supported + * on Illumos is 16. + * + * Reference: PSARC 2008/318 + */ +# define OSS_MAX_CHANNELS (16) +# else +# define OSS_MAX_CHANNELS (2) +# endif +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) +#define SNDSTAT_BEGIN_STR "Installed devices:" +#define SNDSTAT_USER_BEGIN_STR "Installed devices from userspace:" +#define SNDSTAT_FV_BEGIN_STR "File Versions:" +#endif + +static struct cubeb_ops const oss_ops; + +struct cubeb { + struct cubeb_ops const * ops; + + /* Our intern string store */ + pthread_mutex_t mutex; /* protects devid_strs */ + cubeb_strings *devid_strs; +}; + +struct oss_stream { + oss_devnode_t name; + int fd; + void * buf; + + struct stream_info { + int channels; + int sample_rate; + int fmt; + int precision; + } info; + + unsigned int frame_size; /* precision in bytes * channels */ + bool floating; +}; + +struct cubeb_stream { + struct cubeb * context; + void * user_ptr; + pthread_t thread; + pthread_cond_t doorbell_cv; + pthread_cond_t stopped_cv; + /* protects doorbell_cv, stopped_cv, running, destroying, volume, frames_written */ + pthread_mutex_t mtx; + bool thread_created; + bool running; + bool destroying; + float volume; + struct oss_stream play; + struct oss_stream record; + cubeb_data_callback data_cb; + cubeb_state_callback state_cb; + uint64_t frames_written; + unsigned int nfr; /* Number of frames allocated */ ++ unsigned int nfrags; ++ unsigned int bufframes; +}; + +static char const * +oss_cubeb_devid_intern(cubeb *context, char const * devid) +{ + char const *is; + pthread_mutex_lock(&context->mutex); + is = cubeb_strings_intern(context->devid_strs, devid); + pthread_mutex_unlock(&context->mutex); + return is; +} + +int +oss_init(cubeb **context, char const *context_name) { + cubeb * c; + + (void)context_name; + if ((c = calloc(1, sizeof(cubeb))) == NULL) { + return CUBEB_ERROR; + } + + if (cubeb_strings_init(&c->devid_strs) == CUBEB_ERROR) { + goto fail; + } + + if (pthread_mutex_init(&c->mutex, NULL) != 0) { + goto fail; + } + + c->ops = &oss_ops; + *context = c; + return CUBEB_OK; + +fail: + cubeb_strings_destroy(c->devid_strs); + free(c); + return CUBEB_ERROR; +} + +static void +oss_destroy(cubeb * context) +{ + pthread_mutex_destroy(&context->mutex); + cubeb_strings_destroy(context->devid_strs); + free(context); +} + +static char const * +oss_get_backend_id(cubeb * context) +{ + return "oss"; +} + +static int +oss_get_preferred_sample_rate(cubeb * context, uint32_t * rate) +{ + (void)context; + + *rate = OSS_PREFER_RATE; + return CUBEB_OK; +} + +static int +oss_get_max_channel_count(cubeb * context, uint32_t * max_channels) +{ + (void)context; + + *max_channels = OSS_MAX_CHANNELS; + return CUBEB_OK; +} + +static int +oss_get_min_latency(cubeb * context, cubeb_stream_params params, + uint32_t * latency_frames) +{ + (void)context; + + *latency_frames = (OSS_LATENCY_MS * params.rate) / 1000; + return CUBEB_OK; +} + +static void +oss_free_cubeb_device_info_strings(cubeb_device_info *cdi) +{ + free((char *)cdi->device_id); + free((char *)cdi->friendly_name); + free((char *)cdi->group_id); + cdi->device_id = NULL; + cdi->friendly_name = NULL; + cdi->group_id = NULL; +} + +#if defined(__FreeBSD__) || defined(__DragonFly__) +/* + * Check if the specified DSP is okay for the purpose specified + * in type. Here type can only specify one operation each time + * this helper is called. + * + * Return 0 if OK, otherwise 1. + */ +static int +oss_probe_open(const char *dsppath, cubeb_device_type type, + int *fdp, oss_audioinfo *resai) +{ + oss_audioinfo ai; + int error; + int oflags = (type == CUBEB_DEVICE_TYPE_INPUT) ? O_RDONLY : O_WRONLY; + int dspfd = open(dsppath, oflags); + if (dspfd == -1) + return 1; + + ai.dev = -1; + error = ioctl(dspfd, SNDCTL_AUDIOINFO, &ai); + if (error < 0) { + close(dspfd); + return 1; + } + + if (resai) + *resai = ai; + if (fdp) + *fdp = dspfd; + else + close(dspfd); + return 0; +} + +struct sndstat_info { + oss_devnode_t devname; + const char *desc; + cubeb_device_type type; + int preferred; +}; + +static int +oss_sndstat_line_parse(char *line, int is_ud, struct sndstat_info *sinfo) +{ + char *matchptr = line, *n = NULL; + struct sndstat_info res; + + memset(&res, 0, sizeof(res)); + + n = strchr(matchptr, ':'); + if (n == NULL) + goto fail; + if (is_ud == 0) { + unsigned int devunit; + + if (sscanf(matchptr, "pcm%u: ", &devunit) < 1) + goto fail; + + if (snprintf(res.devname, sizeof(res.devname), "/dev/dsp%u", devunit) < 1) + goto fail; + } else { + if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/"))) + goto fail; + + strlcpy(res.devname, "/dev/", sizeof(res.devname)); + strncat(res.devname, matchptr, n - matchptr); + } + matchptr = n + 1; + + n = strchr(matchptr, '<'); + if (n == NULL) + goto fail; + matchptr = n + 1; + n = strrchr(matchptr, '>'); + if (n == NULL) + goto fail; + *n = 0; + res.desc = matchptr; + matchptr = n + 1; + + n = strchr(matchptr, '('); + if (n == NULL) + goto fail; + matchptr = n + 1; + n = strrchr(matchptr, ')'); + if (n == NULL) + goto fail; + *n = 0; + if (!isdigit(matchptr[0])) { + if (strstr(matchptr, "play") != NULL) + res.type |= CUBEB_DEVICE_TYPE_OUTPUT; + if (strstr(matchptr, "rec") != NULL) + res.type |= CUBEB_DEVICE_TYPE_INPUT; + } else { + int p, r; + if (sscanf(matchptr, "%dp:%*dv/%dr:%*dv", &p, &r) != 2) + goto fail; + if (p > 0) + res.type |= CUBEB_DEVICE_TYPE_OUTPUT; + if (r > 0) + res.type |= CUBEB_DEVICE_TYPE_INPUT; + } + matchptr = n + 1; + if (strstr(matchptr, "default") != NULL) + res.preferred = 1; + + *sinfo = res; + return 0; + +fail: + return 1; +} + +/* + * XXX: On FreeBSD we have to rely on SNDCTL_CARDINFO to get all + * the usable audio devices currently, as SNDCTL_AUDIOINFO will + * never return directly usable audio device nodes. + */ +static int +oss_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection * collection) +{ + cubeb_device_info *devinfop = NULL; + char *line = NULL; + size_t linecap = 0; + FILE *sndstatfp = NULL; + int collection_cnt = 0; + int is_ud = 0; + int skipall = 0; + + devinfop = calloc(1, sizeof(cubeb_device_info)); + if (devinfop == NULL) + goto fail; + + sndstatfp = fopen("/dev/sndstat", "r"); + if (sndstatfp == NULL) + goto fail; + while (getline(&line, &linecap, sndstatfp) > 0) { + const char *devid = NULL; + struct sndstat_info sinfo; + oss_audioinfo ai; + + if (!strncmp(line, SNDSTAT_FV_BEGIN_STR, strlen(SNDSTAT_FV_BEGIN_STR))) { + skipall = 1; + continue; + } + if (!strncmp(line, SNDSTAT_BEGIN_STR, strlen(SNDSTAT_BEGIN_STR))) { + is_ud = 0; + skipall = 0; + continue; + } + if (!strncmp(line, SNDSTAT_USER_BEGIN_STR, strlen(SNDSTAT_USER_BEGIN_STR))) { + is_ud = 1; + skipall = 0; + continue; + } + if (skipall || isblank(line[0])) + continue; + + if (oss_sndstat_line_parse(line, is_ud, &sinfo)) + continue; + + devinfop[collection_cnt].type = 0; + switch (sinfo.type) { + case CUBEB_DEVICE_TYPE_INPUT: + if (type & CUBEB_DEVICE_TYPE_OUTPUT) + continue; + break; + case CUBEB_DEVICE_TYPE_OUTPUT: + if (type & CUBEB_DEVICE_TYPE_INPUT) + continue; + break; + case 0: + continue; + } + + if (oss_probe_open(sinfo.devname, type, NULL, &ai)) + continue; + + devid = oss_cubeb_devid_intern(context, sinfo.devname); + if (devid == NULL) + continue; + + devinfop[collection_cnt].device_id = strdup(sinfo.devname); + asprintf((char **)&devinfop[collection_cnt].friendly_name, "%s: %s", + sinfo.devname, sinfo.desc); + devinfop[collection_cnt].group_id = strdup(sinfo.devname); + devinfop[collection_cnt].vendor_name = NULL; + if (devinfop[collection_cnt].device_id == NULL || + devinfop[collection_cnt].friendly_name == NULL || + devinfop[collection_cnt].group_id == NULL) { + oss_free_cubeb_device_info_strings(&devinfop[collection_cnt]); + continue; + } + + devinfop[collection_cnt].type = type; + devinfop[collection_cnt].devid = devid; + devinfop[collection_cnt].state = CUBEB_DEVICE_STATE_ENABLED; + devinfop[collection_cnt].preferred = + (sinfo.preferred) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; + devinfop[collection_cnt].format = CUBEB_DEVICE_FMT_S16NE; + devinfop[collection_cnt].default_format = CUBEB_DEVICE_FMT_S16NE; + devinfop[collection_cnt].max_channels = ai.max_channels; + devinfop[collection_cnt].default_rate = OSS_PREFER_RATE; + devinfop[collection_cnt].max_rate = ai.max_rate; + devinfop[collection_cnt].min_rate = ai.min_rate; + devinfop[collection_cnt].latency_lo = 0; + devinfop[collection_cnt].latency_hi = 0; + + collection_cnt++; + + void *newp = reallocarray(devinfop, collection_cnt + 1, + sizeof(cubeb_device_info)); + if (newp == NULL) + goto fail; + devinfop = newp; + } + + free(line); + fclose(sndstatfp); + + collection->count = collection_cnt; + collection->device = devinfop; + + return CUBEB_OK; + +fail: + free(line); + if (sndstatfp) + fclose(sndstatfp); + free(devinfop); + return CUBEB_ERROR; +} + +#else + +static int +oss_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection * collection) +{ + oss_sysinfo si; + int error, i; + cubeb_device_info *devinfop = NULL; + int collection_cnt = 0; + int mixer_fd = -1; + + mixer_fd = open(OSS_DEFAULT_MIXER, O_RDWR); + if (mixer_fd == -1) { + LOG("Failed to open mixer %s. errno: %d", OSS_DEFAULT_MIXER, errno); + return CUBEB_ERROR; + } + + error = ioctl(mixer_fd, SNDCTL_SYSINFO, &si); + if (error) { + LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d", OSS_DEFAULT_MIXER, errno); + goto fail; + } + + devinfop = calloc(si.numaudios, sizeof(cubeb_device_info)); + if (devinfop == NULL) + goto fail; + + collection->count = 0; + for (i = 0; i < si.numaudios; i++) { + oss_audioinfo ai; + cubeb_device_info cdi = { 0 }; + const char *devid = NULL; + + ai.dev = i; + error = ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai); + if (error) + goto fail; + + assert(ai.dev < si.numaudios); + if (!ai.enabled) + continue; + + cdi.type = 0; + switch (ai.caps & DSP_CAP_DUPLEX) { + case DSP_CAP_INPUT: + if (type & CUBEB_DEVICE_TYPE_OUTPUT) + continue; + break; + case DSP_CAP_OUTPUT: + if (type & CUBEB_DEVICE_TYPE_INPUT) + continue; + break; + case 0: + continue; + } + cdi.type = type; + + devid = oss_cubeb_devid_intern(context, ai.devnode); + cdi.device_id = strdup(ai.name); + cdi.friendly_name = strdup(ai.name); + cdi.group_id = strdup(ai.name); + if (devid == NULL || cdi.device_id == NULL || cdi.friendly_name == NULL || + cdi.group_id == NULL) { + oss_free_cubeb_device_info_strings(&cdi); + continue; + } + + cdi.devid = devid; + cdi.vendor_name = NULL; + cdi.state = CUBEB_DEVICE_STATE_ENABLED; + cdi.preferred = CUBEB_DEVICE_PREF_NONE; + cdi.format = CUBEB_DEVICE_FMT_S16NE; + cdi.default_format = CUBEB_DEVICE_FMT_S16NE; + cdi.max_channels = ai.max_channels; + cdi.default_rate = OSS_PREFER_RATE; + cdi.max_rate = ai.max_rate; + cdi.min_rate = ai.min_rate; + cdi.latency_lo = 0; + cdi.latency_hi = 0; + + devinfop[collection_cnt++] = cdi; + } + + collection->count = collection_cnt; + collection->device = devinfop; + + if (mixer_fd != -1) + close(mixer_fd); + return CUBEB_OK; + +fail: + if (mixer_fd != -1) + close(mixer_fd); + free(devinfop); + return CUBEB_ERROR; +} + +#endif + +static int +oss_device_collection_destroy(cubeb * context, + cubeb_device_collection * collection) +{ + size_t i; + for (i = 0; i < collection->count; i++) { + oss_free_cubeb_device_info_strings(&collection->device[i]); + } + free(collection->device); + collection->device = NULL; + collection->count = 0; + return 0; +} + +static unsigned int +oss_chn_from_cubeb(cubeb_channel chn) +{ + switch (chn) { + case CHANNEL_FRONT_LEFT: + return CHID_L; + case CHANNEL_FRONT_RIGHT: + return CHID_R; + case CHANNEL_FRONT_CENTER: + return CHID_C; + case CHANNEL_LOW_FREQUENCY: + return CHID_LFE; + case CHANNEL_BACK_LEFT: + return CHID_LR; + case CHANNEL_BACK_RIGHT: + return CHID_RR; + case CHANNEL_SIDE_LEFT: + return CHID_LS; + case CHANNEL_SIDE_RIGHT: + return CHID_RS; + default: + return CHID_UNDEF; + } +} + +static unsigned long long +oss_cubeb_layout_to_chnorder(cubeb_channel_layout layout) +{ + unsigned int i, nchns = 0; + unsigned long long chnorder = 0; + + for (i = 0; layout; i++, layout >>= 1) { + unsigned long long chid = oss_chn_from_cubeb((layout & 1) << i); + if (chid == CHID_UNDEF) + continue; + + chnorder |= (chid & 0xf) << nchns * 4; + nchns++; + } + + return chnorder; +} + +static int +oss_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params, + struct stream_info * sinfo) +{ + unsigned long long chnorder; + + sinfo->channels = params->channels; + sinfo->sample_rate = params->rate; + switch (params->format) { + case CUBEB_SAMPLE_S16LE: + sinfo->fmt = AFMT_S16_LE; + sinfo->precision = 16; + break; + case CUBEB_SAMPLE_S16BE: + sinfo->fmt = AFMT_S16_BE; + sinfo->precision = 16; + break; + case CUBEB_SAMPLE_FLOAT32NE: + sinfo->fmt = AFMT_S32_NE; + sinfo->precision = 32; + break; + default: + LOG("Unsupported format"); + return CUBEB_ERROR_INVALID_FORMAT; + } + if (ioctl(fd, SNDCTL_DSP_CHANNELS, &sinfo->channels) == -1) { + return CUBEB_ERROR; + } + if (ioctl(fd, SNDCTL_DSP_SETFMT, &sinfo->fmt) == -1) { + return CUBEB_ERROR; + } + if (ioctl(fd, SNDCTL_DSP_SPEED, &sinfo->sample_rate) == -1) { + return CUBEB_ERROR; + } + /* Mono layout is an exception */ + if (params->layout != CUBEB_LAYOUT_UNDEFINED && params->layout != CUBEB_LAYOUT_MONO) { + chnorder = oss_cubeb_layout_to_chnorder(params->layout); + if (ioctl(fd, SNDCTL_DSP_SET_CHNORDER, &chnorder) == -1) + LOG("Non-fatal error %d occured when setting channel order.", errno); + } + return CUBEB_OK; +} + +static int +oss_stream_stop(cubeb_stream * s) +{ + pthread_mutex_lock(&s->mtx); + if (s->thread_created && s->running) { + s->running = false; + pthread_cond_signal(&s->doorbell_cv); + pthread_cond_wait(&s->stopped_cv, &s->mtx); + } + pthread_mutex_unlock(&s->mtx); + return CUBEB_OK; +} + +static void +oss_stream_destroy(cubeb_stream * s) +{ + pthread_mutex_lock(&s->mtx); + if (s->thread_created) { + s->destroying = true; + pthread_cond_signal(&s->doorbell_cv); + } + pthread_mutex_unlock(&s->mtx); + pthread_join(s->thread, NULL); + + pthread_cond_destroy(&s->doorbell_cv); + pthread_cond_destroy(&s->stopped_cv); + pthread_mutex_destroy(&s->mtx); + if (s->play.fd != -1) { + close(s->play.fd); + } + if (s->record.fd != -1) { + close(s->record.fd); + } + free(s->play.buf); + free(s->record.buf); + free(s); +} + +static void +oss_float_to_linear32(void * buf, unsigned sample_count, float vol) +{ + float * in = buf; + int32_t * out = buf; + int32_t * tail = out + sample_count; + + while (out < tail) { + int64_t f = *(in++) * vol * 0x80000000LL; + if (f < -INT32_MAX) + f = -INT32_MAX; + else if (f > INT32_MAX) + f = INT32_MAX; + *(out++) = f; + } +} + +static void +oss_linear32_to_float(void * buf, unsigned sample_count) +{ + int32_t * in = buf; + float * out = buf; + float * tail = out + sample_count; + + while (out < tail) { + *(out++) = (1.0 / 0x80000000LL) * *(in++); + } +} + +static void +oss_linear16_set_vol(int16_t * buf, unsigned sample_count, float vol) +{ + unsigned i; + int32_t multiplier = vol * 0x8000; + + for (i = 0; i < sample_count; ++i) { + buf[i] = (buf[i] * multiplier) >> 15; + } +} + +static int +oss_audio_loop(cubeb_stream * s) +{ + int state = CUBEB_STATE_STARTED; -+ long nfr = 0; + int trig = 0; + int drain = 0; + struct pollfd pfds[2]; -+ bool cbready = true; ++ unsigned int ppending, rpending; + + pfds[0].fd = s->play.fd; + pfds[0].events = POLLOUT; + pfds[1].fd = s->record.fd; + pfds[1].events = POLLIN; + ++ ppending = 0; ++ rpending = s->bufframes; ++ + if (s->record.fd != -1) { + if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) { + LOG("Error %d occured when setting trigger on record fd", errno); + state = CUBEB_STATE_ERROR; + goto out; + } + trig |= PCM_ENABLE_INPUT; + if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) { + LOG("Error %d occured when setting trigger on record fd", errno); + state = CUBEB_STATE_ERROR; + goto out; + } ++ memset(s->record.buf, 0, s->bufframes * s->record.frame_size); + } + -+ if (s->record.fd != -1) -+ memset(s->record.buf, 0, s->nfr); -+ + while (1) { ++ long nfr = 0; ++ + pthread_mutex_lock(&s->mtx); + if (!s->running || s->destroying) { + pthread_mutex_unlock(&s->mtx); + state = CUBEB_STATE_STOPPED; + break; + } + pthread_mutex_unlock(&s->mtx); + if (s->play.fd == -1 && s->record.fd == -1) { + /* + * Stop here if the stream is not play & record stream, + * play-only stream or record-only stream + */ + + state = CUBEB_STATE_STOPPED; + break; + } + -+ if (s->record.fd != -1 && s->record.floating) { -+ oss_linear32_to_float(s->record.buf, s->record.info.channels * s->nfr); -+ } -+ if (cbready) { -+ nfr = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, s->nfr); ++ while ((s->bufframes - ppending) >= s->nfr && rpending >= s->nfr) { ++ long n = ((s->bufframes - ppending) < rpending) ? s->bufframes - ppending : rpending; ++ char *rptr = NULL, *pptr = NULL; ++ if (s->record.fd != -1) ++ rptr = (char *)s->record.buf; ++ if (s->play.fd != -1) ++ pptr = (char *)s->play.buf + ppending * s->play.frame_size; ++ if (s->record.fd != -1 && s->record.floating) { ++ oss_linear32_to_float(s->record.buf, s->record.info.channels * n); ++ } ++ nfr = s->data_cb(s, s->user_ptr, rptr, pptr, n); + if (nfr == CUBEB_ERROR) { + state = CUBEB_STATE_ERROR; + goto out; + } -+ cbready = false; -+ } -+ if (s->play.fd != -1) { -+ float vol; ++ if (pptr) { ++ float vol; + -+ pthread_mutex_lock(&s->mtx); -+ vol = s->volume; -+ pthread_mutex_unlock(&s->mtx); ++ pthread_mutex_lock(&s->mtx); ++ vol = s->volume; ++ pthread_mutex_unlock(&s->mtx); + -+ if (s->play.floating) { -+ oss_float_to_linear32(s->play.buf, s->play.info.channels * nfr, vol); -+ } else { -+ oss_linear16_set_vol(s->play.buf, s->play.info.channels * nfr, vol); ++ if (s->play.floating) { ++ oss_float_to_linear32(pptr, s->play.info.channels * nfr, vol); ++ } else { ++ oss_linear16_set_vol((int16_t *)pptr, s->play.info.channels * nfr, vol); ++ } + } -+ } -+ if (nfr < (long)s->nfr) { -+ if (s->play.fd != -1) { -+ drain = 1; -+ } else { -+ /* -+ * This is a record-only stream and number of frames -+ * returned from data_cb() is smaller than number -+ * of frames required to read. Stop here. -+ */ ++ if (pptr) { ++ ppending += nfr; ++ assert(ppending <= s->bufframes); ++ } ++ if (rptr) { ++ assert(rpending >= nfr); ++ rpending -= nfr; ++ memmove(rptr, rptr + nfr * s->record.frame_size, ++ (s->bufframes - nfr) * s->record.frame_size); ++ } ++ if (nfr < n) { ++ if (s->play.fd != -1) { ++ drain = 1; ++ break; ++ } else { ++ /* ++ * This is a record-only stream and number of frames ++ * returned from data_cb() is smaller than number ++ * of frames required to read. Stop here. ++ */ + -+ state = CUBEB_STATE_STOPPED; -+ break; ++ state = CUBEB_STATE_STOPPED; ++ goto out; ++ } + } + } + -+ size_t to_write = s->play.fd != -1 ? nfr : 0; -+ size_t to_read = s->record.fd != -1 ? s->nfr : 0; -+ size_t write_ofs = 0; -+ size_t read_ofs = 0; -+ while (to_write > 0 || to_read > 0) { -+ size_t bytes; -+ ssize_t n, frames; -+ int nfds; ++ ssize_t n, frames; ++ int nfds; + -+ pfds[0].revents = 0; -+ pfds[1].revents = 0; ++ pfds[0].revents = 0; ++ pfds[1].revents = 0; + -+ nfds = poll(pfds, 2, 1000); -+ if (nfds == -1) { -+ if (errno == EINTR) -+ continue; -+ LOG("Error %d occured when polling playback and record fd", errno); -+ state = CUBEB_STATE_ERROR; -+ goto out; -+ } else if (nfds == 0) ++ nfds = poll(pfds, 2, 1000); ++ if (nfds == -1) { ++ if (errno == EINTR) + continue; ++ LOG("Error %d occured when polling playback and record fd", errno); ++ state = CUBEB_STATE_ERROR; ++ goto out; ++ } else if (nfds == 0) ++ continue; + -+ if ((pfds[0].revents & (POLLERR | POLLHUP)) || -+ (pfds[1].revents & (POLLERR | POLLHUP))) { -+ LOG("Error occured on playback, record fds"); -+ state = CUBEB_STATE_ERROR; -+ goto out; -+ } ++ if ((pfds[0].revents & (POLLERR | POLLHUP)) || ++ (pfds[1].revents & (POLLERR | POLLHUP))) { ++ LOG("Error occured on playback, record fds"); ++ state = CUBEB_STATE_ERROR; ++ goto out; ++ } + -+ if (to_write > 0 && pfds[0].revents) { -+ bytes = to_write * s->play.frame_size; -+ if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, bytes)) < 0) { ++ if (pfds[0].revents) { ++ while (ppending > 0) { ++ size_t bytes = ppending * s->play.frame_size; ++ if ((n = write(s->play.fd, (uint8_t *)s->play.buf, bytes)) < 0) { ++ if (errno == EINTR) ++ continue; ++ if (errno == EAGAIN) { ++ if (drain) ++ continue; ++ break; ++ } + state = CUBEB_STATE_ERROR; + goto out; + } + frames = n / s->play.frame_size; + pthread_mutex_lock(&s->mtx); + s->frames_written += frames; + pthread_mutex_unlock(&s->mtx); -+ to_write -= frames; -+ write_ofs += n; ++ ppending -= frames; ++ memmove(s->play.buf, (uint8_t *)s->play.buf + n, ++ (s->bufframes - frames) * s->play.frame_size); + } -+ if (to_read > 0 && pfds[1].revents) { -+ bytes = to_read * s->record.frame_size; -+ if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, -+ bytes)) < 0) { ++ } ++ if (pfds[1].revents) { ++ while (s->bufframes - rpending > 0) { ++ size_t bytes = (s->bufframes - rpending) * s->record.frame_size; ++ size_t read_ofs = rpending * s->record.frame_size; ++ if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, bytes)) < 0) { ++ if (errno == EINTR) ++ continue; ++ if (errno == EAGAIN) ++ break; + state = CUBEB_STATE_ERROR; + goto out; + } + frames = n / s->record.frame_size; -+ to_read -= frames; -+ read_ofs += n; ++ rpending += frames; + } + } + if (drain) { + state = CUBEB_STATE_DRAINED; + break; + } -+ cbready = true; + } + +out: + return state; +} + +static void * +oss_io_routine(void *arg) +{ + cubeb_stream *s = arg; + cubeb_state state = CUBEB_STATE_STARTED; + + do { + pthread_mutex_lock(&s->mtx); + if (s->destroying) { + pthread_mutex_unlock(&s->mtx); + break; + } + pthread_mutex_unlock(&s->mtx); + ++ state = CUBEB_STATE_STARTED; + s->state_cb(s, s->user_ptr, state); + + state = oss_audio_loop(s); + assert(state != CUBEB_STATE_STARTED); + + if (s->record.fd != -1) + ioctl(s->record.fd, SNDCTL_DSP_HALT_INPUT, NULL); + s->state_cb(s, s->user_ptr, state); + + pthread_mutex_lock(&s->mtx); + pthread_cond_signal(&s->stopped_cv); + if (s->destroying) { + pthread_mutex_unlock(&s->mtx); + break; + } + pthread_mutex_unlock(&s->mtx); + + pthread_mutex_lock(&s->mtx); + pthread_cond_wait(&s->doorbell_cv, &s->mtx); + pthread_mutex_unlock(&s->mtx); + } while (1); + + pthread_mutex_lock(&s->mtx); + s->thread_created = false; + pthread_mutex_unlock(&s->mtx); + return NULL; +} + +static inline int +oss_calc_frag_shift(unsigned int frames, unsigned int frame_size) +{ + int n = 4; -+ int blksize = (frames * frame_size + 4 - 1) / 4; ++ int blksize = (frames * frame_size + OSS_NFRAGS - 1) / OSS_NFRAGS; + while ((1 << n) < blksize) + n++; + return n; +} + +static inline int +oss_get_frag_params(unsigned int shift) +{ -+ return (8 << 16) | shift; ++ return (OSS_NFRAGS << 16) | shift; +} + +static int +oss_stream_init(cubeb * context, + 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 ret = CUBEB_OK; + unsigned int playnfr = 0, recnfr = 0; + cubeb_stream *s = NULL; + const char *defdsp; + + if (!(defdsp = getenv(ENV_AUDIO_DEVICE)) || *defdsp == '\0') + defdsp = OSS_DEFAULT_DEVICE; + + (void)stream_name; + if ((s = calloc(1, sizeof(cubeb_stream))) == NULL) { + ret = CUBEB_ERROR; + goto error; + } + s->record.fd = s->play.fd = -1; + s->nfr = latency_frames; + if (input_device != NULL) { + strlcpy(s->record.name, input_device, sizeof(s->record.name)); + } else { + strlcpy(s->record.name, defdsp, sizeof(s->record.name)); + } + if (output_device != NULL) { + strlcpy(s->play.name, output_device, sizeof(s->play.name)); + } else { + strlcpy(s->play.name, defdsp, sizeof(s->play.name)); + } + if (input_stream_params != NULL) { + unsigned int nb_channels; + if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { + LOG("Loopback not supported"); + ret = CUBEB_ERROR_NOT_SUPPORTED; + goto error; + } + nb_channels = cubeb_channel_layout_nb_channels(input_stream_params->layout); + if (input_stream_params->layout != CUBEB_LAYOUT_UNDEFINED && + nb_channels != input_stream_params->channels) { + LOG("input_stream_params->layout does not match input_stream_params->channels"); + ret = CUBEB_ERROR_INVALID_PARAMETER; + goto error; + } + if (s->record.fd == -1) { -+ if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) { ++ if ((s->record.fd = open(s->record.name, O_RDONLY | O_NONBLOCK)) == -1) { + LOG("Audio device \"%s\" could not be opened as read-only", + s->record.name); + ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; + goto error; + } + } + if ((ret = oss_copy_params(s->record.fd, s, input_stream_params, + &s->record.info)) != CUBEB_OK) { + LOG("Setting record params failed"); + goto error; + } + s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); + s->record.frame_size = s->record.info.channels * (s->record.info.precision / 8); + recnfr = (1 << oss_calc_frag_shift(s->nfr, s->record.frame_size)) / s->record.frame_size; + } + if (output_stream_params != NULL) { + unsigned int nb_channels; + if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { + LOG("Loopback not supported"); + ret = CUBEB_ERROR_NOT_SUPPORTED; + goto error; + } + nb_channels = cubeb_channel_layout_nb_channels(output_stream_params->layout); + if (output_stream_params->layout != CUBEB_LAYOUT_UNDEFINED && + nb_channels != output_stream_params->channels) { + LOG("output_stream_params->layout does not match output_stream_params->channels"); + ret = CUBEB_ERROR_INVALID_PARAMETER; + goto error; + } + if (s->play.fd == -1) { -+ if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) { ++ if ((s->play.fd = open(s->play.name, O_WRONLY | O_NONBLOCK)) == -1) { + LOG("Audio device \"%s\" could not be opened as write-only", + s->play.name); + ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; + goto error; + } + } + if ((ret = oss_copy_params(s->play.fd, s, output_stream_params, + &s->play.info)) != CUBEB_OK) { + LOG("Setting play params failed"); + goto error; + } + s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); + s->play.frame_size = s->play.info.channels * (s->play.info.precision / 8); + playnfr = (1 << oss_calc_frag_shift(s->nfr, s->play.frame_size)) / s->play.frame_size; + } + /* Use the largest nframes among playing and recording streams */ + s->nfr = (playnfr > recnfr) ? playnfr : recnfr; ++ s->nfrags = OSS_NFRAGS; ++ s->bufframes = s->nfr * s->nfrags; + if (s->play.fd != -1) { + int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->play.frame_size)); + if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) + LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", + frag); + } + if (s->record.fd != -1) { + int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->record.frame_size)); + if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) + LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", + frag); + } + s->context = context; + s->volume = 1.0; + s->state_cb = state_callback; + s->data_cb = data_callback; + s->user_ptr = user_ptr; + + if (pthread_mutex_init(&s->mtx, NULL) != 0) { + LOG("Failed to create mutex"); + goto error; + } + if (pthread_cond_init(&s->doorbell_cv, NULL) != 0) { + LOG("Failed to create cv"); + goto error; + } + if (pthread_cond_init(&s->stopped_cv, NULL) != 0) { + LOG("Failed to create cv"); + goto error; + } + + if (s->play.fd != -1) { -+ if ((s->play.buf = calloc(s->nfr, s->play.frame_size)) == NULL) { ++ if ((s->play.buf = calloc(s->bufframes, s->play.frame_size)) == NULL) { + ret = CUBEB_ERROR; + goto error; + } + } + if (s->record.fd != -1) { -+ if ((s->record.buf = calloc(s->nfr, s->record.frame_size)) == NULL) { ++ if ((s->record.buf = calloc(s->bufframes, s->record.frame_size)) == NULL) { + ret = CUBEB_ERROR; + goto error; + } + } + + *stream = s; + return CUBEB_OK; +error: + if (s != NULL) { + oss_stream_destroy(s); + } + return ret; +} + +static int +oss_stream_thr_create(cubeb_stream * s) +{ + if (s->thread_created) { -+ pthread_mutex_lock(&s->mtx); + pthread_cond_signal(&s->doorbell_cv); -+ pthread_mutex_unlock(&s->mtx); -+ + return CUBEB_OK; + } + + if (pthread_create(&s->thread, NULL, oss_io_routine, s) != 0) { + LOG("Couldn't create thread"); + return CUBEB_ERROR; + } + + return CUBEB_OK; +} + +static int +oss_stream_start(cubeb_stream * s) +{ + pthread_mutex_lock(&s->mtx); + if (!s->running && oss_stream_thr_create(s) != CUBEB_OK) { + pthread_mutex_unlock(&s->mtx); + return CUBEB_ERROR; + } + s->thread_created = true; + s->running = true; + pthread_mutex_unlock(&s->mtx); + return CUBEB_OK; +} + +static int +oss_stream_get_position(cubeb_stream * s, uint64_t * position) +{ + pthread_mutex_lock(&s->mtx); + *position = s->frames_written; + pthread_mutex_unlock(&s->mtx); + return CUBEB_OK; +} + +static int +oss_stream_get_latency(cubeb_stream * s, uint32_t * latency) +{ + int delay; + + if (ioctl(s->play.fd, SNDCTL_DSP_GETODELAY, &delay) == -1) { + return CUBEB_ERROR; + } + + /* Return number of frames there */ + *latency = delay / s->play.frame_size; + return CUBEB_OK; +} + +static int +oss_stream_set_volume(cubeb_stream * stream, float volume) +{ + if (volume < 0.0) + volume = 0.0; + else if (volume > 1.0) + volume = 1.0; + pthread_mutex_lock(&stream->mtx); + stream->volume = volume; + pthread_mutex_unlock(&stream->mtx); + return CUBEB_OK; +} + +static int +oss_get_current_device(cubeb_stream * stream, cubeb_device ** const device) +{ + *device = calloc(1, sizeof(cubeb_device)); + if (*device == NULL) { + return CUBEB_ERROR; + } + (*device)->input_name = stream->record.fd != -1 ? + strdup(stream->record.name) : NULL; + (*device)->output_name = stream->play.fd != -1 ? + strdup(stream->play.name) : NULL; + return CUBEB_OK; +} + +static int +oss_stream_device_destroy(cubeb_stream * stream, cubeb_device * device) +{ + (void)stream; + free(device->input_name); + free(device->output_name); + free(device); + return CUBEB_OK; +} + +static struct cubeb_ops const oss_ops = { + .init = oss_init, + .get_backend_id = oss_get_backend_id, + .get_max_channel_count = oss_get_max_channel_count, + .get_min_latency = oss_get_min_latency, + .get_preferred_sample_rate = oss_get_preferred_sample_rate, + .enumerate_devices = oss_enumerate_devices, + .device_collection_destroy = oss_device_collection_destroy, + .destroy = oss_destroy, + .stream_init = oss_stream_init, + .stream_destroy = oss_stream_destroy, + .stream_start = oss_stream_start, + .stream_stop = oss_stream_stop, + .stream_reset_default_device = NULL, + .stream_get_position = oss_stream_get_position, + .stream_get_latency = oss_stream_get_latency, + .stream_get_input_latency = NULL, + .stream_set_volume = oss_stream_set_volume, + .stream_get_current_device = oss_get_current_device, + .stream_device_destroy = oss_stream_device_destroy, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = NULL}; Index: head/www/firefox-esr/Makefile =================================================================== --- head/www/firefox-esr/Makefile (revision 549982) +++ head/www/firefox-esr/Makefile (revision 549983) @@ -1,63 +1,63 @@ # Created by: Alan Eldridge # $FreeBSD$ PORTNAME= firefox DISTVERSION= 78.3.0 -PORTREVISION= 1 +PORTREVISION= 2 PORTEPOCH= 1 CATEGORIES= www MASTER_SITES= MOZILLA/${PORTNAME}/releases/${DISTVERSION}esr/source \ MOZILLA/${PORTNAME}/candidates/${DISTVERSION}esr-candidates/build1/source PKGNAMESUFFIX= -esr DISTFILES= ${DISTNAME}esr.source${EXTRACT_SUFX} MAINTAINER= gecko@FreeBSD.org COMMENT= Web browser based on the browser portion of Mozilla BUILD_DEPENDS= nspr>=4.26:devel/nspr \ nss>=3.56:security/nss \ icu>=67.1,1:devel/icu \ libevent>=2.1.8:devel/libevent \ harfbuzz>=2.6.6:print/harfbuzz \ graphite2>=1.3.14:graphics/graphite2 \ png>=1.6.35:graphics/png \ libvpx>=1.8.2:multimedia/libvpx \ ${PYTHON_PKGNAMEPREFIX}sqlite3>0:databases/py-sqlite3@${PY_FLAVOR} \ v4l_compat>0:multimedia/v4l_compat \ autoconf-2.13:devel/autoconf213 \ nasm:devel/nasm \ yasm:devel/yasm \ zip:archivers/zip USE_GECKO= gecko CPE_PRODUCT= ${PORTNAME}_esr CONFLICTS_INSTALL= firefox USE_MOZILLA= -sqlite USES= tar:xz FIREFOX_ICON= ${MOZILLA}.png FIREFOX_ICON_SRC= ${PREFIX}/lib/${MOZILLA}/browser/chrome/icons/default/default48.png FIREFOX_DESKTOP= ${MOZSRC}/taskcluster/docker/${MOZILLA}-snap/${MOZILLA}.desktop MOZ_OPTIONS= --enable-application=browser \ --enable-official-branding .include "${.CURDIR}/../../www/firefox/Makefile.options" post-patch: @${REINPLACE_CMD} -e 's/%u/%U/' -e '/X-MultipleArgs/d' \ -e '/^Icon/s/=.*/=${FIREFOX_ICON:R}/' \ ${FIREFOX_DESKTOP} @${REINPLACE_CMD} -e 's|%%LOCALBASE%%|${LOCALBASE}|g' \ ${WRKSRC}/browser/app/nsBrowserApp.cpp pre-configure: (cd ${WRKSRC} && ${LOCALBASE}/bin/autoconf-2.13) (cd ${WRKSRC}/js/src/ && ${LOCALBASE}/bin/autoconf-2.13) post-install: ${INSTALL_DATA} ${FIREFOX_DESKTOP} ${STAGEDIR}${PREFIX}/share/applications/ ${MKDIR} ${STAGEDIR}${PREFIX}/share/pixmaps ${LN} -sf ${FIREFOX_ICON_SRC} ${STAGEDIR}${PREFIX}/share/pixmaps/${FIREFOX_ICON} .include Index: head/www/firefox-esr/files/patch-cubeb-oss =================================================================== --- head/www/firefox-esr/files/patch-cubeb-oss (revision 549982) +++ head/www/firefox-esr/files/patch-cubeb-oss (revision 549983) @@ -1,1274 +1,1309 @@ https://github.com/kinetiknz/cubeb/pull/600 --- dom/media/CubebUtils.cpp.orig 2020-08-19 02:08:51 UTC +++ dom/media/CubebUtils.cpp @@ -126,7 +126,7 @@ const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties"; const char* AUDIOSTREAM_BACKEND_ID_STR[] = { "jack", "pulse", "alsa", "audiounit", "audioqueue", "wasapi", - "winmm", "directsound", "sndio", "opensl", "audiotrack", "kai"}; + "winmm", "directsound", "sndio", "opensl", "oss", "audiotrack", "kai"}; /* Index for failures to create an audio stream the first time. */ const int CUBEB_BACKEND_INIT_FAILURE_FIRST = ArrayLength(AUDIOSTREAM_BACKEND_ID_STR); --- media/libcubeb/src/moz.build.orig 2020-08-19 02:09:19 UTC +++ media/libcubeb/src/moz.build @@ -40,6 +40,12 @@ if CONFIG['MOZ_JACK']: ] DEFINES['USE_JACK'] = True +if CONFIG['OS_ARCH'] in ('DragonFly', 'FreeBSD', 'SunOS'): + SOURCES += [ + 'cubeb_oss.c', + ] + DEFINES['USE_OSS'] = True + if CONFIG['OS_ARCH'] == 'OpenBSD': SOURCES += [ 'cubeb_sndio.c', --- media/libcubeb/src/cubeb.c.orig 2020-08-19 02:09:26 UTC +++ media/libcubeb/src/cubeb.c @@ -60,6 +60,9 @@ int sun_init(cubeb ** context, char const * context_name); #if defined(USE_OPENSL) int opensl_init(cubeb ** context, char const * context_name); #endif +#if defined(USE_OSS) +int oss_init(cubeb ** context, char const * context_name); +#endif #if defined(USE_AUDIOTRACK) int audiotrack_init(cubeb ** context, char const * context_name); #endif @@ -165,6 +168,10 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam #if defined(USE_OPENSL) init_oneshot = opensl_init; #endif + } else if (!strcmp(backend_name, "oss")) { +#if defined(USE_OSS) + init_oneshot = oss_init; +#endif } else if (!strcmp(backend_name, "audiotrack")) { #if defined(USE_AUDIOTRACK) init_oneshot = audiotrack_init; @@ -200,6 +207,9 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam #if defined(USE_ALSA) alsa_init, #endif +#if defined (USE_OSS) + oss_init, +#endif #if defined(USE_AUDIOUNIT_RUST) audiounit_rust_init, #endif --- /dev/null +++ media/libcubeb/src/cubeb_oss.c -@@ -0,0 +1,1210 @@ +@@ -0,0 +1,1245 @@ +/* + * Copyright © 2019-2020 Nia Alarie + * Copyright © 2020 Ka Ho Ng ++ * Copyright © 2020 The FreeBSD Foundation + * ++ * Portions of this software were developed by Ka Ho Ng ++ * under sponsorship from the FreeBSD Foundation. ++ * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ + +#if defined(__FreeBSD__) && __FreeBSD__ < 12 +#define _WITH_GETLINE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cubeb/cubeb.h" +#include "cubeb_mixer.h" +#include "cubeb_strings.h" +#include "cubeb-internal.h" + +/* Supported well by most hardware. */ +#ifndef OSS_PREFER_RATE +#define OSS_PREFER_RATE (48000) +#endif + +/* Standard acceptable minimum. */ +#ifndef OSS_LATENCY_MS +#define OSS_LATENCY_MS (8) +#endif + ++#ifndef OSS_NFRAGS ++#define OSS_NFRAGS (4) ++#endif ++ +#ifndef OSS_DEFAULT_DEVICE +#define OSS_DEFAULT_DEVICE "/dev/dsp" +#endif + +#ifndef OSS_DEFAULT_MIXER +#define OSS_DEFAULT_MIXER "/dev/mixer" +#endif + +#define ENV_AUDIO_DEVICE "AUDIO_DEVICE" + +#ifndef OSS_MAX_CHANNELS +# if defined(__FreeBSD__) || defined(__DragonFly__) +/* + * The current maximum number of channels supported + * on FreeBSD is 8. + * + * Reference: FreeBSD 12.1-RELEASE + */ +# define OSS_MAX_CHANNELS (8) +# elif defined(__sun__) +/* + * The current maximum number of channels supported + * on Illumos is 16. + * + * Reference: PSARC 2008/318 + */ +# define OSS_MAX_CHANNELS (16) +# else +# define OSS_MAX_CHANNELS (2) +# endif +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) +#define SNDSTAT_BEGIN_STR "Installed devices:" +#define SNDSTAT_USER_BEGIN_STR "Installed devices from userspace:" +#define SNDSTAT_FV_BEGIN_STR "File Versions:" +#endif + +static struct cubeb_ops const oss_ops; + +struct cubeb { + struct cubeb_ops const * ops; + + /* Our intern string store */ + pthread_mutex_t mutex; /* protects devid_strs */ + cubeb_strings *devid_strs; +}; + +struct oss_stream { + oss_devnode_t name; + int fd; + void * buf; + + struct stream_info { + int channels; + int sample_rate; + int fmt; + int precision; + } info; + + unsigned int frame_size; /* precision in bytes * channels */ + bool floating; +}; + +struct cubeb_stream { + struct cubeb * context; + void * user_ptr; + pthread_t thread; + pthread_cond_t doorbell_cv; + pthread_cond_t stopped_cv; + /* protects doorbell_cv, stopped_cv, running, destroying, volume, frames_written */ + pthread_mutex_t mtx; + bool thread_created; + bool running; + bool destroying; + float volume; + struct oss_stream play; + struct oss_stream record; + cubeb_data_callback data_cb; + cubeb_state_callback state_cb; + uint64_t frames_written; + unsigned int nfr; /* Number of frames allocated */ ++ unsigned int nfrags; ++ unsigned int bufframes; +}; + +static char const * +oss_cubeb_devid_intern(cubeb *context, char const * devid) +{ + char const *is; + pthread_mutex_lock(&context->mutex); + is = cubeb_strings_intern(context->devid_strs, devid); + pthread_mutex_unlock(&context->mutex); + return is; +} + +int +oss_init(cubeb **context, char const *context_name) { + cubeb * c; + + (void)context_name; + if ((c = calloc(1, sizeof(cubeb))) == NULL) { + return CUBEB_ERROR; + } + + if (cubeb_strings_init(&c->devid_strs) == CUBEB_ERROR) { + goto fail; + } + + if (pthread_mutex_init(&c->mutex, NULL) != 0) { + goto fail; + } + + c->ops = &oss_ops; + *context = c; + return CUBEB_OK; + +fail: + cubeb_strings_destroy(c->devid_strs); + free(c); + return CUBEB_ERROR; +} + +static void +oss_destroy(cubeb * context) +{ + pthread_mutex_destroy(&context->mutex); + cubeb_strings_destroy(context->devid_strs); + free(context); +} + +static char const * +oss_get_backend_id(cubeb * context) +{ + return "oss"; +} + +static int +oss_get_preferred_sample_rate(cubeb * context, uint32_t * rate) +{ + (void)context; + + *rate = OSS_PREFER_RATE; + return CUBEB_OK; +} + +static int +oss_get_max_channel_count(cubeb * context, uint32_t * max_channels) +{ + (void)context; + + *max_channels = OSS_MAX_CHANNELS; + return CUBEB_OK; +} + +static int +oss_get_min_latency(cubeb * context, cubeb_stream_params params, + uint32_t * latency_frames) +{ + (void)context; + + *latency_frames = (OSS_LATENCY_MS * params.rate) / 1000; + return CUBEB_OK; +} + +static void +oss_free_cubeb_device_info_strings(cubeb_device_info *cdi) +{ + free((char *)cdi->device_id); + free((char *)cdi->friendly_name); + free((char *)cdi->group_id); + cdi->device_id = NULL; + cdi->friendly_name = NULL; + cdi->group_id = NULL; +} + +#if defined(__FreeBSD__) || defined(__DragonFly__) +/* + * Check if the specified DSP is okay for the purpose specified + * in type. Here type can only specify one operation each time + * this helper is called. + * + * Return 0 if OK, otherwise 1. + */ +static int +oss_probe_open(const char *dsppath, cubeb_device_type type, + int *fdp, oss_audioinfo *resai) +{ + oss_audioinfo ai; + int error; + int oflags = (type == CUBEB_DEVICE_TYPE_INPUT) ? O_RDONLY : O_WRONLY; + int dspfd = open(dsppath, oflags); + if (dspfd == -1) + return 1; + + ai.dev = -1; + error = ioctl(dspfd, SNDCTL_AUDIOINFO, &ai); + if (error < 0) { + close(dspfd); + return 1; + } + + if (resai) + *resai = ai; + if (fdp) + *fdp = dspfd; + else + close(dspfd); + return 0; +} + +struct sndstat_info { + oss_devnode_t devname; + const char *desc; + cubeb_device_type type; + int preferred; +}; + +static int +oss_sndstat_line_parse(char *line, int is_ud, struct sndstat_info *sinfo) +{ + char *matchptr = line, *n = NULL; + struct sndstat_info res; + + memset(&res, 0, sizeof(res)); + + n = strchr(matchptr, ':'); + if (n == NULL) + goto fail; + if (is_ud == 0) { + unsigned int devunit; + + if (sscanf(matchptr, "pcm%u: ", &devunit) < 1) + goto fail; + + if (snprintf(res.devname, sizeof(res.devname), "/dev/dsp%u", devunit) < 1) + goto fail; + } else { + if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/"))) + goto fail; + + strlcpy(res.devname, "/dev/", sizeof(res.devname)); + strncat(res.devname, matchptr, n - matchptr); + } + matchptr = n + 1; + + n = strchr(matchptr, '<'); + if (n == NULL) + goto fail; + matchptr = n + 1; + n = strrchr(matchptr, '>'); + if (n == NULL) + goto fail; + *n = 0; + res.desc = matchptr; + matchptr = n + 1; + + n = strchr(matchptr, '('); + if (n == NULL) + goto fail; + matchptr = n + 1; + n = strrchr(matchptr, ')'); + if (n == NULL) + goto fail; + *n = 0; + if (!isdigit(matchptr[0])) { + if (strstr(matchptr, "play") != NULL) + res.type |= CUBEB_DEVICE_TYPE_OUTPUT; + if (strstr(matchptr, "rec") != NULL) + res.type |= CUBEB_DEVICE_TYPE_INPUT; + } else { + int p, r; + if (sscanf(matchptr, "%dp:%*dv/%dr:%*dv", &p, &r) != 2) + goto fail; + if (p > 0) + res.type |= CUBEB_DEVICE_TYPE_OUTPUT; + if (r > 0) + res.type |= CUBEB_DEVICE_TYPE_INPUT; + } + matchptr = n + 1; + if (strstr(matchptr, "default") != NULL) + res.preferred = 1; + + *sinfo = res; + return 0; + +fail: + return 1; +} + +/* + * XXX: On FreeBSD we have to rely on SNDCTL_CARDINFO to get all + * the usable audio devices currently, as SNDCTL_AUDIOINFO will + * never return directly usable audio device nodes. + */ +static int +oss_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection * collection) +{ + cubeb_device_info *devinfop = NULL; + char *line = NULL; + size_t linecap = 0; + FILE *sndstatfp = NULL; + int collection_cnt = 0; + int is_ud = 0; + int skipall = 0; + + devinfop = calloc(1, sizeof(cubeb_device_info)); + if (devinfop == NULL) + goto fail; + + sndstatfp = fopen("/dev/sndstat", "r"); + if (sndstatfp == NULL) + goto fail; + while (getline(&line, &linecap, sndstatfp) > 0) { + const char *devid = NULL; + struct sndstat_info sinfo; + oss_audioinfo ai; + + if (!strncmp(line, SNDSTAT_FV_BEGIN_STR, strlen(SNDSTAT_FV_BEGIN_STR))) { + skipall = 1; + continue; + } + if (!strncmp(line, SNDSTAT_BEGIN_STR, strlen(SNDSTAT_BEGIN_STR))) { + is_ud = 0; + skipall = 0; + continue; + } + if (!strncmp(line, SNDSTAT_USER_BEGIN_STR, strlen(SNDSTAT_USER_BEGIN_STR))) { + is_ud = 1; + skipall = 0; + continue; + } + if (skipall || isblank(line[0])) + continue; + + if (oss_sndstat_line_parse(line, is_ud, &sinfo)) + continue; + + devinfop[collection_cnt].type = 0; + switch (sinfo.type) { + case CUBEB_DEVICE_TYPE_INPUT: + if (type & CUBEB_DEVICE_TYPE_OUTPUT) + continue; + break; + case CUBEB_DEVICE_TYPE_OUTPUT: + if (type & CUBEB_DEVICE_TYPE_INPUT) + continue; + break; + case 0: + continue; + } + + if (oss_probe_open(sinfo.devname, type, NULL, &ai)) + continue; + + devid = oss_cubeb_devid_intern(context, sinfo.devname); + if (devid == NULL) + continue; + + devinfop[collection_cnt].device_id = strdup(sinfo.devname); + asprintf((char **)&devinfop[collection_cnt].friendly_name, "%s: %s", + sinfo.devname, sinfo.desc); + devinfop[collection_cnt].group_id = strdup(sinfo.devname); + devinfop[collection_cnt].vendor_name = NULL; + if (devinfop[collection_cnt].device_id == NULL || + devinfop[collection_cnt].friendly_name == NULL || + devinfop[collection_cnt].group_id == NULL) { + oss_free_cubeb_device_info_strings(&devinfop[collection_cnt]); + continue; + } + + devinfop[collection_cnt].type = type; + devinfop[collection_cnt].devid = devid; + devinfop[collection_cnt].state = CUBEB_DEVICE_STATE_ENABLED; + devinfop[collection_cnt].preferred = + (sinfo.preferred) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; + devinfop[collection_cnt].format = CUBEB_DEVICE_FMT_S16NE; + devinfop[collection_cnt].default_format = CUBEB_DEVICE_FMT_S16NE; + devinfop[collection_cnt].max_channels = ai.max_channels; + devinfop[collection_cnt].default_rate = OSS_PREFER_RATE; + devinfop[collection_cnt].max_rate = ai.max_rate; + devinfop[collection_cnt].min_rate = ai.min_rate; + devinfop[collection_cnt].latency_lo = 0; + devinfop[collection_cnt].latency_hi = 0; + + collection_cnt++; + + void *newp = reallocarray(devinfop, collection_cnt + 1, + sizeof(cubeb_device_info)); + if (newp == NULL) + goto fail; + devinfop = newp; + } + + free(line); + fclose(sndstatfp); + + collection->count = collection_cnt; + collection->device = devinfop; + + return CUBEB_OK; + +fail: + free(line); + if (sndstatfp) + fclose(sndstatfp); + free(devinfop); + return CUBEB_ERROR; +} + +#else + +static int +oss_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection * collection) +{ + oss_sysinfo si; + int error, i; + cubeb_device_info *devinfop = NULL; + int collection_cnt = 0; + int mixer_fd = -1; + + mixer_fd = open(OSS_DEFAULT_MIXER, O_RDWR); + if (mixer_fd == -1) { + LOG("Failed to open mixer %s. errno: %d", OSS_DEFAULT_MIXER, errno); + return CUBEB_ERROR; + } + + error = ioctl(mixer_fd, SNDCTL_SYSINFO, &si); + if (error) { + LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d", OSS_DEFAULT_MIXER, errno); + goto fail; + } + + devinfop = calloc(si.numaudios, sizeof(cubeb_device_info)); + if (devinfop == NULL) + goto fail; + + collection->count = 0; + for (i = 0; i < si.numaudios; i++) { + oss_audioinfo ai; + cubeb_device_info cdi = { 0 }; + const char *devid = NULL; + + ai.dev = i; + error = ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai); + if (error) + goto fail; + + assert(ai.dev < si.numaudios); + if (!ai.enabled) + continue; + + cdi.type = 0; + switch (ai.caps & DSP_CAP_DUPLEX) { + case DSP_CAP_INPUT: + if (type & CUBEB_DEVICE_TYPE_OUTPUT) + continue; + break; + case DSP_CAP_OUTPUT: + if (type & CUBEB_DEVICE_TYPE_INPUT) + continue; + break; + case 0: + continue; + } + cdi.type = type; + + devid = oss_cubeb_devid_intern(context, ai.devnode); + cdi.device_id = strdup(ai.name); + cdi.friendly_name = strdup(ai.name); + cdi.group_id = strdup(ai.name); + if (devid == NULL || cdi.device_id == NULL || cdi.friendly_name == NULL || + cdi.group_id == NULL) { + oss_free_cubeb_device_info_strings(&cdi); + continue; + } + + cdi.devid = devid; + cdi.vendor_name = NULL; + cdi.state = CUBEB_DEVICE_STATE_ENABLED; + cdi.preferred = CUBEB_DEVICE_PREF_NONE; + cdi.format = CUBEB_DEVICE_FMT_S16NE; + cdi.default_format = CUBEB_DEVICE_FMT_S16NE; + cdi.max_channels = ai.max_channels; + cdi.default_rate = OSS_PREFER_RATE; + cdi.max_rate = ai.max_rate; + cdi.min_rate = ai.min_rate; + cdi.latency_lo = 0; + cdi.latency_hi = 0; + + devinfop[collection_cnt++] = cdi; + } + + collection->count = collection_cnt; + collection->device = devinfop; + + if (mixer_fd != -1) + close(mixer_fd); + return CUBEB_OK; + +fail: + if (mixer_fd != -1) + close(mixer_fd); + free(devinfop); + return CUBEB_ERROR; +} + +#endif + +static int +oss_device_collection_destroy(cubeb * context, + cubeb_device_collection * collection) +{ + size_t i; + for (i = 0; i < collection->count; i++) { + oss_free_cubeb_device_info_strings(&collection->device[i]); + } + free(collection->device); + collection->device = NULL; + collection->count = 0; + return 0; +} + +static unsigned int +oss_chn_from_cubeb(cubeb_channel chn) +{ + switch (chn) { + case CHANNEL_FRONT_LEFT: + return CHID_L; + case CHANNEL_FRONT_RIGHT: + return CHID_R; + case CHANNEL_FRONT_CENTER: + return CHID_C; + case CHANNEL_LOW_FREQUENCY: + return CHID_LFE; + case CHANNEL_BACK_LEFT: + return CHID_LR; + case CHANNEL_BACK_RIGHT: + return CHID_RR; + case CHANNEL_SIDE_LEFT: + return CHID_LS; + case CHANNEL_SIDE_RIGHT: + return CHID_RS; + default: + return CHID_UNDEF; + } +} + +static unsigned long long +oss_cubeb_layout_to_chnorder(cubeb_channel_layout layout) +{ + unsigned int i, nchns = 0; + unsigned long long chnorder = 0; + + for (i = 0; layout; i++, layout >>= 1) { + unsigned long long chid = oss_chn_from_cubeb((layout & 1) << i); + if (chid == CHID_UNDEF) + continue; + + chnorder |= (chid & 0xf) << nchns * 4; + nchns++; + } + + return chnorder; +} + +static int +oss_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params, + struct stream_info * sinfo) +{ + unsigned long long chnorder; + + sinfo->channels = params->channels; + sinfo->sample_rate = params->rate; + switch (params->format) { + case CUBEB_SAMPLE_S16LE: + sinfo->fmt = AFMT_S16_LE; + sinfo->precision = 16; + break; + case CUBEB_SAMPLE_S16BE: + sinfo->fmt = AFMT_S16_BE; + sinfo->precision = 16; + break; + case CUBEB_SAMPLE_FLOAT32NE: + sinfo->fmt = AFMT_S32_NE; + sinfo->precision = 32; + break; + default: + LOG("Unsupported format"); + return CUBEB_ERROR_INVALID_FORMAT; + } + if (ioctl(fd, SNDCTL_DSP_CHANNELS, &sinfo->channels) == -1) { + return CUBEB_ERROR; + } + if (ioctl(fd, SNDCTL_DSP_SETFMT, &sinfo->fmt) == -1) { + return CUBEB_ERROR; + } + if (ioctl(fd, SNDCTL_DSP_SPEED, &sinfo->sample_rate) == -1) { + return CUBEB_ERROR; + } + /* Mono layout is an exception */ + if (params->layout != CUBEB_LAYOUT_UNDEFINED && params->layout != CUBEB_LAYOUT_MONO) { + chnorder = oss_cubeb_layout_to_chnorder(params->layout); + if (ioctl(fd, SNDCTL_DSP_SET_CHNORDER, &chnorder) == -1) + LOG("Non-fatal error %d occured when setting channel order.", errno); + } + return CUBEB_OK; +} + +static int +oss_stream_stop(cubeb_stream * s) +{ + pthread_mutex_lock(&s->mtx); + if (s->thread_created && s->running) { + s->running = false; + pthread_cond_signal(&s->doorbell_cv); + pthread_cond_wait(&s->stopped_cv, &s->mtx); + } + pthread_mutex_unlock(&s->mtx); + return CUBEB_OK; +} + +static void +oss_stream_destroy(cubeb_stream * s) +{ + pthread_mutex_lock(&s->mtx); + if (s->thread_created) { + s->destroying = true; + pthread_cond_signal(&s->doorbell_cv); + } + pthread_mutex_unlock(&s->mtx); + pthread_join(s->thread, NULL); + + pthread_cond_destroy(&s->doorbell_cv); + pthread_cond_destroy(&s->stopped_cv); + pthread_mutex_destroy(&s->mtx); + if (s->play.fd != -1) { + close(s->play.fd); + } + if (s->record.fd != -1) { + close(s->record.fd); + } + free(s->play.buf); + free(s->record.buf); + free(s); +} + +static void +oss_float_to_linear32(void * buf, unsigned sample_count, float vol) +{ + float * in = buf; + int32_t * out = buf; + int32_t * tail = out + sample_count; + + while (out < tail) { + int64_t f = *(in++) * vol * 0x80000000LL; + if (f < -INT32_MAX) + f = -INT32_MAX; + else if (f > INT32_MAX) + f = INT32_MAX; + *(out++) = f; + } +} + +static void +oss_linear32_to_float(void * buf, unsigned sample_count) +{ + int32_t * in = buf; + float * out = buf; + float * tail = out + sample_count; + + while (out < tail) { + *(out++) = (1.0 / 0x80000000LL) * *(in++); + } +} + +static void +oss_linear16_set_vol(int16_t * buf, unsigned sample_count, float vol) +{ + unsigned i; + int32_t multiplier = vol * 0x8000; + + for (i = 0; i < sample_count; ++i) { + buf[i] = (buf[i] * multiplier) >> 15; + } +} + +static int +oss_audio_loop(cubeb_stream * s) +{ + int state = CUBEB_STATE_STARTED; -+ long nfr = 0; + int trig = 0; + int drain = 0; + struct pollfd pfds[2]; -+ bool cbready = true; ++ unsigned int ppending, rpending; + + pfds[0].fd = s->play.fd; + pfds[0].events = POLLOUT; + pfds[1].fd = s->record.fd; + pfds[1].events = POLLIN; + ++ ppending = 0; ++ rpending = s->bufframes; ++ + if (s->record.fd != -1) { + if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) { + LOG("Error %d occured when setting trigger on record fd", errno); + state = CUBEB_STATE_ERROR; + goto out; + } + trig |= PCM_ENABLE_INPUT; + if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) { + LOG("Error %d occured when setting trigger on record fd", errno); + state = CUBEB_STATE_ERROR; + goto out; + } ++ memset(s->record.buf, 0, s->bufframes * s->record.frame_size); + } + -+ if (s->record.fd != -1) -+ memset(s->record.buf, 0, s->nfr); -+ + while (1) { ++ long nfr = 0; ++ + pthread_mutex_lock(&s->mtx); + if (!s->running || s->destroying) { + pthread_mutex_unlock(&s->mtx); + state = CUBEB_STATE_STOPPED; + break; + } + pthread_mutex_unlock(&s->mtx); + if (s->play.fd == -1 && s->record.fd == -1) { + /* + * Stop here if the stream is not play & record stream, + * play-only stream or record-only stream + */ + + state = CUBEB_STATE_STOPPED; + break; + } + -+ if (s->record.fd != -1 && s->record.floating) { -+ oss_linear32_to_float(s->record.buf, s->record.info.channels * s->nfr); -+ } -+ if (cbready) { -+ nfr = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, s->nfr); ++ while ((s->bufframes - ppending) >= s->nfr && rpending >= s->nfr) { ++ long n = ((s->bufframes - ppending) < rpending) ? s->bufframes - ppending : rpending; ++ char *rptr = NULL, *pptr = NULL; ++ if (s->record.fd != -1) ++ rptr = (char *)s->record.buf; ++ if (s->play.fd != -1) ++ pptr = (char *)s->play.buf + ppending * s->play.frame_size; ++ if (s->record.fd != -1 && s->record.floating) { ++ oss_linear32_to_float(s->record.buf, s->record.info.channels * n); ++ } ++ nfr = s->data_cb(s, s->user_ptr, rptr, pptr, n); + if (nfr == CUBEB_ERROR) { + state = CUBEB_STATE_ERROR; + goto out; + } -+ cbready = false; -+ } -+ if (s->play.fd != -1) { -+ float vol; ++ if (pptr) { ++ float vol; + -+ pthread_mutex_lock(&s->mtx); -+ vol = s->volume; -+ pthread_mutex_unlock(&s->mtx); ++ pthread_mutex_lock(&s->mtx); ++ vol = s->volume; ++ pthread_mutex_unlock(&s->mtx); + -+ if (s->play.floating) { -+ oss_float_to_linear32(s->play.buf, s->play.info.channels * nfr, vol); -+ } else { -+ oss_linear16_set_vol(s->play.buf, s->play.info.channels * nfr, vol); ++ if (s->play.floating) { ++ oss_float_to_linear32(pptr, s->play.info.channels * nfr, vol); ++ } else { ++ oss_linear16_set_vol((int16_t *)pptr, s->play.info.channels * nfr, vol); ++ } + } -+ } -+ if (nfr < (long)s->nfr) { -+ if (s->play.fd != -1) { -+ drain = 1; -+ } else { -+ /* -+ * This is a record-only stream and number of frames -+ * returned from data_cb() is smaller than number -+ * of frames required to read. Stop here. -+ */ ++ if (pptr) { ++ ppending += nfr; ++ assert(ppending <= s->bufframes); ++ } ++ if (rptr) { ++ assert(rpending >= nfr); ++ rpending -= nfr; ++ memmove(rptr, rptr + nfr * s->record.frame_size, ++ (s->bufframes - nfr) * s->record.frame_size); ++ } ++ if (nfr < n) { ++ if (s->play.fd != -1) { ++ drain = 1; ++ break; ++ } else { ++ /* ++ * This is a record-only stream and number of frames ++ * returned from data_cb() is smaller than number ++ * of frames required to read. Stop here. ++ */ + -+ state = CUBEB_STATE_STOPPED; -+ break; ++ state = CUBEB_STATE_STOPPED; ++ goto out; ++ } + } + } + -+ size_t to_write = s->play.fd != -1 ? nfr : 0; -+ size_t to_read = s->record.fd != -1 ? s->nfr : 0; -+ size_t write_ofs = 0; -+ size_t read_ofs = 0; -+ while (to_write > 0 || to_read > 0) { -+ size_t bytes; -+ ssize_t n, frames; -+ int nfds; ++ ssize_t n, frames; ++ int nfds; + -+ pfds[0].revents = 0; -+ pfds[1].revents = 0; ++ pfds[0].revents = 0; ++ pfds[1].revents = 0; + -+ nfds = poll(pfds, 2, 1000); -+ if (nfds == -1) { -+ if (errno == EINTR) -+ continue; -+ LOG("Error %d occured when polling playback and record fd", errno); -+ state = CUBEB_STATE_ERROR; -+ goto out; -+ } else if (nfds == 0) ++ nfds = poll(pfds, 2, 1000); ++ if (nfds == -1) { ++ if (errno == EINTR) + continue; ++ LOG("Error %d occured when polling playback and record fd", errno); ++ state = CUBEB_STATE_ERROR; ++ goto out; ++ } else if (nfds == 0) ++ continue; + -+ if ((pfds[0].revents & (POLLERR | POLLHUP)) || -+ (pfds[1].revents & (POLLERR | POLLHUP))) { -+ LOG("Error occured on playback, record fds"); -+ state = CUBEB_STATE_ERROR; -+ goto out; -+ } ++ if ((pfds[0].revents & (POLLERR | POLLHUP)) || ++ (pfds[1].revents & (POLLERR | POLLHUP))) { ++ LOG("Error occured on playback, record fds"); ++ state = CUBEB_STATE_ERROR; ++ goto out; ++ } + -+ if (to_write > 0 && pfds[0].revents) { -+ bytes = to_write * s->play.frame_size; -+ if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, bytes)) < 0) { ++ if (pfds[0].revents) { ++ while (ppending > 0) { ++ size_t bytes = ppending * s->play.frame_size; ++ if ((n = write(s->play.fd, (uint8_t *)s->play.buf, bytes)) < 0) { ++ if (errno == EINTR) ++ continue; ++ if (errno == EAGAIN) { ++ if (drain) ++ continue; ++ break; ++ } + state = CUBEB_STATE_ERROR; + goto out; + } + frames = n / s->play.frame_size; + pthread_mutex_lock(&s->mtx); + s->frames_written += frames; + pthread_mutex_unlock(&s->mtx); -+ to_write -= frames; -+ write_ofs += n; ++ ppending -= frames; ++ memmove(s->play.buf, (uint8_t *)s->play.buf + n, ++ (s->bufframes - frames) * s->play.frame_size); + } -+ if (to_read > 0 && pfds[1].revents) { -+ bytes = to_read * s->record.frame_size; -+ if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, -+ bytes)) < 0) { ++ } ++ if (pfds[1].revents) { ++ while (s->bufframes - rpending > 0) { ++ size_t bytes = (s->bufframes - rpending) * s->record.frame_size; ++ size_t read_ofs = rpending * s->record.frame_size; ++ if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, bytes)) < 0) { ++ if (errno == EINTR) ++ continue; ++ if (errno == EAGAIN) ++ break; + state = CUBEB_STATE_ERROR; + goto out; + } + frames = n / s->record.frame_size; -+ to_read -= frames; -+ read_ofs += n; ++ rpending += frames; + } + } + if (drain) { + state = CUBEB_STATE_DRAINED; + break; + } -+ cbready = true; + } + +out: + return state; +} + +static void * +oss_io_routine(void *arg) +{ + cubeb_stream *s = arg; + cubeb_state state = CUBEB_STATE_STARTED; + + do { + pthread_mutex_lock(&s->mtx); + if (s->destroying) { + pthread_mutex_unlock(&s->mtx); + break; + } + pthread_mutex_unlock(&s->mtx); + ++ state = CUBEB_STATE_STARTED; + s->state_cb(s, s->user_ptr, state); + + state = oss_audio_loop(s); + assert(state != CUBEB_STATE_STARTED); + + if (s->record.fd != -1) + ioctl(s->record.fd, SNDCTL_DSP_HALT_INPUT, NULL); + s->state_cb(s, s->user_ptr, state); + + pthread_mutex_lock(&s->mtx); + pthread_cond_signal(&s->stopped_cv); + if (s->destroying) { + pthread_mutex_unlock(&s->mtx); + break; + } + pthread_mutex_unlock(&s->mtx); + + pthread_mutex_lock(&s->mtx); + pthread_cond_wait(&s->doorbell_cv, &s->mtx); + pthread_mutex_unlock(&s->mtx); + } while (1); + + pthread_mutex_lock(&s->mtx); + s->thread_created = false; + pthread_mutex_unlock(&s->mtx); + return NULL; +} + +static inline int +oss_calc_frag_shift(unsigned int frames, unsigned int frame_size) +{ + int n = 4; -+ int blksize = (frames * frame_size + 4 - 1) / 4; ++ int blksize = (frames * frame_size + OSS_NFRAGS - 1) / OSS_NFRAGS; + while ((1 << n) < blksize) + n++; + return n; +} + +static inline int +oss_get_frag_params(unsigned int shift) +{ -+ return (8 << 16) | shift; ++ return (OSS_NFRAGS << 16) | shift; +} + +static int +oss_stream_init(cubeb * context, + 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 ret = CUBEB_OK; + unsigned int playnfr = 0, recnfr = 0; + cubeb_stream *s = NULL; + const char *defdsp; + + if (!(defdsp = getenv(ENV_AUDIO_DEVICE)) || *defdsp == '\0') + defdsp = OSS_DEFAULT_DEVICE; + + (void)stream_name; + if ((s = calloc(1, sizeof(cubeb_stream))) == NULL) { + ret = CUBEB_ERROR; + goto error; + } + s->record.fd = s->play.fd = -1; + s->nfr = latency_frames; + if (input_device != NULL) { + strlcpy(s->record.name, input_device, sizeof(s->record.name)); + } else { + strlcpy(s->record.name, defdsp, sizeof(s->record.name)); + } + if (output_device != NULL) { + strlcpy(s->play.name, output_device, sizeof(s->play.name)); + } else { + strlcpy(s->play.name, defdsp, sizeof(s->play.name)); + } + if (input_stream_params != NULL) { + unsigned int nb_channels; + if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { + LOG("Loopback not supported"); + ret = CUBEB_ERROR_NOT_SUPPORTED; + goto error; + } + nb_channels = cubeb_channel_layout_nb_channels(input_stream_params->layout); + if (input_stream_params->layout != CUBEB_LAYOUT_UNDEFINED && + nb_channels != input_stream_params->channels) { + LOG("input_stream_params->layout does not match input_stream_params->channels"); + ret = CUBEB_ERROR_INVALID_PARAMETER; + goto error; + } + if (s->record.fd == -1) { -+ if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) { ++ if ((s->record.fd = open(s->record.name, O_RDONLY | O_NONBLOCK)) == -1) { + LOG("Audio device \"%s\" could not be opened as read-only", + s->record.name); + ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; + goto error; + } + } + if ((ret = oss_copy_params(s->record.fd, s, input_stream_params, + &s->record.info)) != CUBEB_OK) { + LOG("Setting record params failed"); + goto error; + } + s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); + s->record.frame_size = s->record.info.channels * (s->record.info.precision / 8); + recnfr = (1 << oss_calc_frag_shift(s->nfr, s->record.frame_size)) / s->record.frame_size; + } + if (output_stream_params != NULL) { + unsigned int nb_channels; + if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { + LOG("Loopback not supported"); + ret = CUBEB_ERROR_NOT_SUPPORTED; + goto error; + } + nb_channels = cubeb_channel_layout_nb_channels(output_stream_params->layout); + if (output_stream_params->layout != CUBEB_LAYOUT_UNDEFINED && + nb_channels != output_stream_params->channels) { + LOG("output_stream_params->layout does not match output_stream_params->channels"); + ret = CUBEB_ERROR_INVALID_PARAMETER; + goto error; + } + if (s->play.fd == -1) { -+ if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) { ++ if ((s->play.fd = open(s->play.name, O_WRONLY | O_NONBLOCK)) == -1) { + LOG("Audio device \"%s\" could not be opened as write-only", + s->play.name); + ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; + goto error; + } + } + if ((ret = oss_copy_params(s->play.fd, s, output_stream_params, + &s->play.info)) != CUBEB_OK) { + LOG("Setting play params failed"); + goto error; + } + s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); + s->play.frame_size = s->play.info.channels * (s->play.info.precision / 8); + playnfr = (1 << oss_calc_frag_shift(s->nfr, s->play.frame_size)) / s->play.frame_size; + } + /* Use the largest nframes among playing and recording streams */ + s->nfr = (playnfr > recnfr) ? playnfr : recnfr; ++ s->nfrags = OSS_NFRAGS; ++ s->bufframes = s->nfr * s->nfrags; + if (s->play.fd != -1) { + int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->play.frame_size)); + if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) + LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", + frag); + } + if (s->record.fd != -1) { + int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->record.frame_size)); + if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) + LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", + frag); + } + s->context = context; + s->volume = 1.0; + s->state_cb = state_callback; + s->data_cb = data_callback; + s->user_ptr = user_ptr; + + if (pthread_mutex_init(&s->mtx, NULL) != 0) { + LOG("Failed to create mutex"); + goto error; + } + if (pthread_cond_init(&s->doorbell_cv, NULL) != 0) { + LOG("Failed to create cv"); + goto error; + } + if (pthread_cond_init(&s->stopped_cv, NULL) != 0) { + LOG("Failed to create cv"); + goto error; + } + + if (s->play.fd != -1) { -+ if ((s->play.buf = calloc(s->nfr, s->play.frame_size)) == NULL) { ++ if ((s->play.buf = calloc(s->bufframes, s->play.frame_size)) == NULL) { + ret = CUBEB_ERROR; + goto error; + } + } + if (s->record.fd != -1) { -+ if ((s->record.buf = calloc(s->nfr, s->record.frame_size)) == NULL) { ++ if ((s->record.buf = calloc(s->bufframes, s->record.frame_size)) == NULL) { + ret = CUBEB_ERROR; + goto error; + } + } + + *stream = s; + return CUBEB_OK; +error: + if (s != NULL) { + oss_stream_destroy(s); + } + return ret; +} + +static int +oss_stream_thr_create(cubeb_stream * s) +{ + if (s->thread_created) { -+ pthread_mutex_lock(&s->mtx); + pthread_cond_signal(&s->doorbell_cv); -+ pthread_mutex_unlock(&s->mtx); -+ + return CUBEB_OK; + } + + if (pthread_create(&s->thread, NULL, oss_io_routine, s) != 0) { + LOG("Couldn't create thread"); + return CUBEB_ERROR; + } + + return CUBEB_OK; +} + +static int +oss_stream_start(cubeb_stream * s) +{ + pthread_mutex_lock(&s->mtx); + if (!s->running && oss_stream_thr_create(s) != CUBEB_OK) { + pthread_mutex_unlock(&s->mtx); + return CUBEB_ERROR; + } + s->thread_created = true; + s->running = true; + pthread_mutex_unlock(&s->mtx); + return CUBEB_OK; +} + +static int +oss_stream_get_position(cubeb_stream * s, uint64_t * position) +{ + pthread_mutex_lock(&s->mtx); + *position = s->frames_written; + pthread_mutex_unlock(&s->mtx); + return CUBEB_OK; +} + +static int +oss_stream_get_latency(cubeb_stream * s, uint32_t * latency) +{ + int delay; + + if (ioctl(s->play.fd, SNDCTL_DSP_GETODELAY, &delay) == -1) { + return CUBEB_ERROR; + } + + /* Return number of frames there */ + *latency = delay / s->play.frame_size; + return CUBEB_OK; +} + +static int +oss_stream_set_volume(cubeb_stream * stream, float volume) +{ + if (volume < 0.0) + volume = 0.0; + else if (volume > 1.0) + volume = 1.0; + pthread_mutex_lock(&stream->mtx); + stream->volume = volume; + pthread_mutex_unlock(&stream->mtx); + return CUBEB_OK; +} + +static int +oss_get_current_device(cubeb_stream * stream, cubeb_device ** const device) +{ + *device = calloc(1, sizeof(cubeb_device)); + if (*device == NULL) { + return CUBEB_ERROR; + } + (*device)->input_name = stream->record.fd != -1 ? + strdup(stream->record.name) : NULL; + (*device)->output_name = stream->play.fd != -1 ? + strdup(stream->play.name) : NULL; + return CUBEB_OK; +} + +static int +oss_stream_device_destroy(cubeb_stream * stream, cubeb_device * device) +{ + (void)stream; + free(device->input_name); + free(device->output_name); + free(device); + return CUBEB_OK; +} + +static struct cubeb_ops const oss_ops = { + .init = oss_init, + .get_backend_id = oss_get_backend_id, + .get_max_channel_count = oss_get_max_channel_count, + .get_min_latency = oss_get_min_latency, + .get_preferred_sample_rate = oss_get_preferred_sample_rate, + .enumerate_devices = oss_enumerate_devices, + .device_collection_destroy = oss_device_collection_destroy, + .destroy = oss_destroy, + .stream_init = oss_stream_init, + .stream_destroy = oss_stream_destroy, + .stream_start = oss_stream_start, + .stream_stop = oss_stream_stop, + .stream_reset_default_device = NULL, + .stream_get_position = oss_stream_get_position, + .stream_get_latency = oss_stream_get_latency, + .stream_get_input_latency = NULL, + .stream_set_volume = oss_stream_set_volume, + .stream_get_current_device = oss_get_current_device, + .stream_device_destroy = oss_stream_device_destroy, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = NULL};