diff options
Diffstat (limited to 'www/firefox-esr/files/patch-cubeb-oss')
-rw-r--r-- | www/firefox-esr/files/patch-cubeb-oss | 1327 |
1 files changed, 0 insertions, 1327 deletions
diff --git a/www/firefox-esr/files/patch-cubeb-oss b/www/firefox-esr/files/patch-cubeb-oss deleted file mode 100644 index 2b151a07fb20..000000000000 --- a/www/firefox-esr/files/patch-cubeb-oss +++ /dev/null @@ -1,1327 +0,0 @@ -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,1263 @@ -+/* -+ * Copyright © 2019-2020 Nia Alarie <nia@NetBSD.org> -+ * Copyright © 2020 Ka Ho Ng <khng300@gmail.com> -+ * 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 <assert.h> -+#include <ctype.h> -+#include <limits.h> -+#include <errno.h> -+#include <sys/types.h> -+#include <sys/soundcard.h> -+#include <sys/ioctl.h> -+#include <fcntl.h> -+#include <unistd.h> -+#include <pthread.h> -+#include <stdbool.h> -+#include <stdlib.h> -+#include <stdio.h> -+#include <string.h> -+#include <poll.h> -+#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; -+ bool doorbell; /* (m) */ -+ pthread_cond_t doorbell_cv; /* (m) */ -+ pthread_cond_t stopped_cv; /* (m) */ -+ pthread_mutex_t mtx; /* Members protected by this should be marked (m) */ -+ bool thread_created; /* (m) */ -+ bool running; /* (m) */ -+ bool destroying; /* (m) */ -+ cubeb_state state; /* (m) */ -+ float volume /* (m) */; -+ struct oss_stream play; -+ struct oss_stream record; -+ cubeb_data_callback data_cb; -+ cubeb_state_callback state_cb; -+ uint64_t frames_written /* (m) */; -+ 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; -+ s->doorbell = false; -+ pthread_cond_wait(&s->stopped_cv, &s->mtx); -+ } -+ if (s->state != CUBEB_STATE_STOPPED) { -+ s->state = CUBEB_STATE_STOPPED; -+ pthread_mutex_unlock(&s->mtx); -+ s->state_cb(s, s->user_ptr, CUBEB_STATE_STOPPED); -+ } else { -+ 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; -+ s->doorbell = 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; -+ } -+} -+ -+/* 1 - Stopped by cubeb_stream_stop, otherwise 0 */ -+static int -+oss_audio_loop(cubeb_stream * s, cubeb_state *new_state) -+{ -+ cubeb_state state = CUBEB_STATE_STOPPED; -+ int trig = 0; -+ int drain = 0; -+ struct pollfd pfds[2]; -+ 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 breakdown; -+ } -+ 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 breakdown; -+ } -+ memset(s->record.buf, 0, s->bufframes * s->record.frame_size); -+ } -+ -+ while (1) { -+ long nfr = 0; -+ -+ pthread_mutex_lock(&s->mtx); -+ if (!s->running || s->destroying) { -+ pthread_mutex_unlock(&s->mtx); -+ 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 -+ */ -+ -+ goto breakdown; -+ } -+ -+ 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 breakdown; -+ } -+ if (pptr) { -+ float vol; -+ -+ pthread_mutex_lock(&s->mtx); -+ vol = s->volume; -+ pthread_mutex_unlock(&s->mtx); -+ -+ 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 (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; -+ goto breakdown; -+ } -+ } -+ } -+ -+ ssize_t n, frames; -+ int nfds; -+ -+ 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 breakdown; -+ } 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 breakdown; -+ } -+ -+ 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 breakdown; -+ } -+ frames = n / s->play.frame_size; -+ pthread_mutex_lock(&s->mtx); -+ s->frames_written += frames; -+ pthread_mutex_unlock(&s->mtx); -+ ppending -= frames; -+ memmove(s->play.buf, (uint8_t *)s->play.buf + n, -+ (s->bufframes - frames) * s->play.frame_size); -+ } -+ } -+ 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 breakdown; -+ } -+ frames = n / s->record.frame_size; -+ rpending += frames; -+ } -+ } -+ if (drain) { -+ state = CUBEB_STATE_DRAINED; -+ goto breakdown; -+ } -+ } -+ -+ return 1; -+ -+breakdown: -+ pthread_mutex_lock(&s->mtx); -+ *new_state = s->state = state; -+ s->running = false; -+ pthread_mutex_unlock(&s->mtx); -+ return 0; -+} -+ -+static void * -+oss_io_routine(void *arg) -+{ -+ cubeb_stream *s = arg; -+ cubeb_state new_state; -+ int stopped; -+ -+ do { -+ pthread_mutex_lock(&s->mtx); -+ if (s->destroying) { -+ pthread_mutex_unlock(&s->mtx); -+ break; -+ } -+ pthread_mutex_unlock(&s->mtx); -+ -+ stopped = oss_audio_loop(s, &new_state); -+ if (s->record.fd != -1) -+ ioctl(s->record.fd, SNDCTL_DSP_HALT_INPUT, NULL); -+ if (!stopped) -+ s->state_cb(s, s->user_ptr, new_state); -+ -+ pthread_mutex_lock(&s->mtx); -+ pthread_cond_signal(&s->stopped_cv); -+ if (s->destroying) { -+ pthread_mutex_unlock(&s->mtx); -+ break; -+ } -+ while (!s->doorbell) { -+ pthread_cond_wait(&s->doorbell_cv, &s->mtx); -+ } -+ s->doorbell = false; -+ 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 + OSS_NFRAGS - 1) / OSS_NFRAGS; -+ while ((1 << n) < blksize) -+ n++; -+ return n; -+} -+ -+static inline int -+oss_get_frag_params(unsigned int 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->state = CUBEB_STATE_STOPPED; -+ 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 | 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 | 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; -+ } -+ s->doorbell = false; -+ -+ if (s->play.fd != -1) { -+ 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->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) { -+ s->doorbell = true; -+ pthread_cond_signal(&s->doorbell_cv); -+ 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) -+{ -+ s->state_cb(s, s->user_ptr, CUBEB_STATE_STARTED); -+ pthread_mutex_lock(&s->mtx); -+ /* Disallow starting an already started stream */ -+ assert(!s->running && s->state != CUBEB_STATE_STARTED); -+ if (oss_stream_thr_create(s) != CUBEB_OK) { -+ pthread_mutex_unlock(&s->mtx); -+ s->state_cb(s, s->user_ptr, CUBEB_STATE_ERROR); -+ return CUBEB_ERROR; -+ } -+ s->state = CUBEB_STATE_STARTED; -+ 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}; |