Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 21 Aug 2020 16:39:03 +0000 (UTC)
From:      Jan Beich <jbeich@FreeBSD.org>
To:        ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-branches@freebsd.org
Subject:   svn commit: r545614 - in branches/2020Q3: Mk mail/thunderbird/files www/firefox www/firefox-esr www/firefox-esr/files www/firefox/files
Message-ID:  <202008211639.07LGd3Xc074803@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jbeich
Date: Fri Aug 21 16:39:03 2020
New Revision: 545614
URL: https://svnweb.freebsd.org/changeset/ports/545614

Log:
  MFH: r545611
  
  gecko: add native OSS support, replacing ALSA as fallback
  
  OSS is always built but during runtime only selected by default if
  neither pulseaudio, jackit or sndio are installed. In particular,
  Gnome and KDE users would still be offered PulseAudio by default due
  to integration with multi-app volume widgets, current song bars,
  screensharing with audio, etc. that prefer to talk over DBus.
  Those can opt out via media.cubeb.backend=oss in about:config.
  
  Submitted by:	Ka Ho Ng <khng300@gmail.com> (based on)
  Approved by:	ports-secteam blanket

Added:
  branches/2020Q3/mail/thunderbird/files/patch-cubeb-oss
     - copied unchanged from r545611, head/mail/thunderbird/files/patch-cubeb-oss
  branches/2020Q3/www/firefox-esr/files/patch-cubeb-oss
     - copied unchanged from r545611, head/www/firefox-esr/files/patch-cubeb-oss
  branches/2020Q3/www/firefox/files/patch-cubeb-oss
     - copied unchanged from r545611, head/www/firefox/files/patch-cubeb-oss
Modified:
  branches/2020Q3/Mk/bsd.gecko.mk
  branches/2020Q3/www/firefox-esr/Makefile
  branches/2020Q3/www/firefox-esr/pkg-message
  branches/2020Q3/www/firefox/Makefile
  branches/2020Q3/www/firefox/Makefile.options
  branches/2020Q3/www/firefox/pkg-message
Directory Properties:
  branches/2020Q3/   (props changed)

Modified: branches/2020Q3/Mk/bsd.gecko.mk
==============================================================================
--- branches/2020Q3/Mk/bsd.gecko.mk	Fri Aug 21 16:38:22 2020	(r545613)
+++ branches/2020Q3/Mk/bsd.gecko.mk	Fri Aug 21 16:39:03 2020	(r545614)
@@ -234,8 +234,6 @@ MOZ_OPTIONS+=	--disable-libproxy
 
 .if ${PORT_OPTIONS:MALSA}
 BUILD_DEPENDS+=	${LOCALBASE}/include/alsa/asoundlib.h:audio/alsa-lib
-RUN_DEPENDS+=	${LOCALBASE}/lib/alsa-lib/libasound_module_pcm_oss.so:audio/alsa-plugins
-RUN_DEPENDS+=	alsa-lib>=1.1.1_1:audio/alsa-lib
 MOZ_OPTIONS+=	--enable-alsa
 .endif
 

Copied: branches/2020Q3/mail/thunderbird/files/patch-cubeb-oss (from r545611, head/mail/thunderbird/files/patch-cubeb-oss)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ branches/2020Q3/mail/thunderbird/files/patch-cubeb-oss	Fri Aug 21 16:39:03 2020	(r545614, copy of r545611, head/mail/thunderbird/files/patch-cubeb-oss)
@@ -0,0 +1,1192 @@
+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)
+     audiounit_rust,
+ #endif
+--- /dev/null
++++ media/libcubeb/src/cubeb_oss.c
+@@ -0,0 +1,1128 @@
++/*
++ * Copyright © 2019-2020 Nia Alarie <nia@NetBSD.org>
++ * Copyright © 2020 Ka Ho Ng <khng300@gmail.com>
++ *
++ * 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 <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 (40)
++#endif
++
++#ifndef OSS_DEFAULT_DEVICE
++#define OSS_DEFAULT_DEVICE "/dev/dsp"
++#endif
++
++#ifndef OSS_DEFAULT_MIXER
++#define OSS_DEFAULT_MIXER "/dev/mixer"
++#endif
++
++#ifndef OSS_DEFAULT_NFRAMES
++#define OSS_DEFAULT_NFRAMES (32)
++#endif
++
++#define ENV_AUDIO_DEVICE "AUDIODEVICE"
++
++#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 */
++  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_mutex_t mutex; /* protects running, volume, frames_written */
++  bool running;
++  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 */
++};
++
++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) {
++    free(c);
++    return CUBEB_ERROR;
++  }
++  c->ops = &oss_ops;
++  *context = c;
++  return CUBEB_OK;
++}
++
++static void
++oss_destroy(cubeb * context)
++{
++  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 = cubeb_strings_intern(context->devid_strs, sinfo.devname);
++    if (devid == NULL)
++      continue;
++
++    devinfop[collection_cnt].device_id = strdup(sinfo.devname);
++    devinfop[collection_cnt].friendly_name = strdup(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 = cubeb_strings_intern(context->devid_strs, 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->mutex);
++  if (s->running) {
++    s->running = false;
++    pthread_mutex_unlock(&s->mutex);
++    pthread_join(s->thread, NULL);
++  } else {
++    pthread_mutex_unlock(&s->mutex);
++  }
++  return CUBEB_OK;
++}
++
++static void
++oss_stream_destroy(cubeb_stream * s)
++{
++  oss_stream_stop(s);
++  pthread_mutex_destroy(&s->mutex);
++  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 void *
++oss_io_routine(void * arg)
++{
++  cubeb_stream *s = arg;
++  cubeb_state state = CUBEB_STATE_STARTED;
++  size_t to_read = 0;
++  size_t to_write = 0;
++  long cb_nfr = 0;
++  size_t write_ofs = 0;
++  size_t read_ofs = 0;
++  int drain = 0;
++  int trig = 0;
++
++  s->state_cb(s, s->user_ptr, CUBEB_STATE_STARTED);
++
++  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;
++    }
++  }
++
++  while (state == CUBEB_STATE_STARTED) {
++    pthread_mutex_lock(&s->mutex);
++    if (!s->running) {
++      pthread_mutex_unlock(&s->mutex);
++      state = CUBEB_STATE_STOPPED;
++      break;
++    }
++    pthread_mutex_unlock(&s->mutex);
++    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);
++    }
++    cb_nfr = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, s->nfr);
++    if (cb_nfr == CUBEB_ERROR) {
++      state = CUBEB_STATE_ERROR;
++      break;
++    }
++    if (s->play.fd != -1) {
++      float vol;
++
++      pthread_mutex_lock(&s->mutex);
++      vol = s->volume;
++      pthread_mutex_unlock(&s->mutex);
++
++      if (s->play.floating) {
++        oss_float_to_linear32(s->play.buf,
++                              s->play.info.channels * cb_nfr, vol);
++      } else {
++        oss_linear16_set_vol(s->play.buf,
++                             s->play.info.channels * cb_nfr, vol);
++      }
++    }
++    if (cb_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.
++         */
++
++        state = CUBEB_STATE_STOPPED;
++        break;
++      }
++    }
++
++    if (s->record.fd != -1 && !trig) {
++      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;
++        break;
++      }
++    }
++
++    to_write = s->play.fd != -1 ? cb_nfr : 0;
++    to_read = s->record.fd != -1 ? s->nfr : 0;
++    write_ofs = 0;
++    read_ofs = 0;
++    while (to_write > 0 || to_read > 0) {
++      size_t bytes;
++      ssize_t n, frames;
++      struct pollfd pfds[2];
++
++      pfds[0].fd = s->play.fd;
++      pfds[0].events = POLLOUT;
++      pfds[0].revents = 0;
++      pfds[1].fd = s->record.fd;
++      pfds[1].events = POLLIN;
++      pfds[1].revents = 0;
++
++      if (to_write > 0 && to_read > 0) {
++        int nfds;
++
++        nfds = poll(pfds, 2, 10000);
++        if (nfds == -1) {
++          if (errno == EINTR)
++            continue;
++          LOG("Error %d occured when polling playback and record fd", errno);
++          state = CUBEB_STATE_ERROR;
++          break;
++        } else if (nfds == 0)
++          continue;
++
++        if ((pfds[0].revents & (POLLERR|POLLHUP)) ||
++            (pfds[1].revents & (POLLERR|POLLHUP))) {
++          LOG("Error occured on playback or record fds", errno);
++          state = CUBEB_STATE_ERROR;
++          break;
++        }
++      } else if (to_write > 0) {
++        pfds[0].revents = POLLOUT;
++      } else {
++        pfds[1].revents = POLLIN;
++      }
++
++      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) {
++          state = CUBEB_STATE_ERROR;
++          break;
++        }
++        frames = n / s->play.frame_size;
++        pthread_mutex_lock(&s->mutex);
++        s->frames_written += frames;
++        pthread_mutex_unlock(&s->mutex);
++        to_write -= frames;
++        write_ofs += n;
++      }
++      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) {
++          state = CUBEB_STATE_ERROR;
++          break;
++        }
++        frames = n / s->record.frame_size;
++        to_read -= frames;
++        read_ofs += n;
++      }
++    }
++    if (drain && state != CUBEB_STATE_ERROR) {
++      state = CUBEB_STATE_DRAINED;
++      break;
++    }
++  }
++out:
++  if (s->record.fd != -1)
++    ioctl(s->record.fd, SNDCTL_DSP_HALT_INPUT, NULL);
++  s->state_cb(s, s->user_ptr, state);
++  return NULL;
++}
++
++static int
++oss_calc_frag_params(unsigned int frames, unsigned int frame_size)
++{
++  int n = 4;
++  int blksize = OSS_DEFAULT_NFRAMES * frame_size;
++  int nblks = (frames * frame_size + blksize - 1) / blksize;
++  while ((1 << n) < blksize)
++    n++;
++  return nblks << 16 | n;
++}
++
++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 latency_frames,
++                cubeb_data_callback data_callback,
++                cubeb_state_callback state_callback,
++                void * user_ptr)
++{
++  int ret = CUBEB_OK;
++  unsigned int playnfr = 1;
++  unsigned int recnfr = 1;
++  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 = -1;
++  s->play.fd = -1;
++  s->nfr = OSS_DEFAULT_NFRAMES;
++  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");

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202008211639.07LGd3Xc074803>