Index: branches/2020Q3/Mk/bsd.gecko.mk =================================================================== --- branches/2020Q3/Mk/bsd.gecko.mk (revision 545613) +++ branches/2020Q3/Mk/bsd.gecko.mk (revision 545614) @@ -1,389 +1,387 @@ #-*- tab-width: 4; -*- # ex:ts=4 # # Date created: 12 Nov 2005 # Whom: Michael Johnson # # $FreeBSD$ # # 4 column tabs prevent hair loss and tooth decay! # bsd.gecko.mk abstracts the selection of gecko-based backends. It allows users # and porters to support any available gecko backend without needing to build # many conditional tests. ${USE_GECKO} is the list of backends that your port # can handle, and ${GECKO} is set by bsd.gecko.mk to be the chosen backend. # Users set ${WITH_GECKO} to the list of gecko backends they want on their # system. .if defined(USE_GECKO) .if !defined(_POSTMKINCLUDED) && !defined(Gecko_Pre_Include) Gecko_Pre_Include= bsd.gecko.mk # This file contains some reusable components for mozilla ports. It's of # use primarily to apps from the mozilla project itself (such as Firefox, # Thunderbird, etc.), and probably won't be of use for gecko-based ports # like epiphany, galeon, etc. # # You need to make sure to add USE_GECKO=gecko to for your port can uses # one of these options below. # # Ports can use the following: # # USE_MOZILLA By default, it enables every system dependency # listed in '_ALL_DEPENDS'. If your port doesn't # need one of those then you can use '-' like # 'USE_MOZILLA= -png -vpx' to subtract the # dependencies. Experimental deps use '+' like # 'USE_MOZILLA= +speex +theora'. # # MOZILLA_PLIST_DIRS List of directories to descend into when installing # and creating the plist # # MOZ_OPTIONS configure arguments (added to .mozconfig). If # NOMOZCONFIG is defined, you probably want to set # CONFIGURE_ARGS+=${MOZ_OPTIONS} # # MOZ_MK_OPTIONS The make(1) arguments (added to .mozconfig). If # NOMOZCONFIG is defined, you probably want to set # MAKE_ARGS+=${MOZ_MK_OPTIONS} # # MOZ_EXPORT Environment variables for the build process (added # to .mozconfig). If NOMOZCONFIG is defined, you # probably want to set MAKE_ENV+=${MOZ_EXPORT} # # NOMOZCONFIG Don't drop a customized .mozconfig into the build # directory. Options will have to be specified in # CONFIGURE_ARGS instead # MAINTAINER?= gecko@FreeBSD.org MOZILLA?= ${PORTNAME} MOZILLA_VER?= ${PORTVERSION} MOZILLA_BIN?= ${PORTNAME}-bin MOZILLA_EXEC_NAME?=${MOZILLA} USES+= compiler:c++17-lang cpe gl gmake gnome iconv localbase perl5 pkgconfig \ python:2.7,build desktop-file-utils CPE_VENDOR?=mozilla USE_GL= gl USE_GNOME= cairo gdkpixbuf2 gtk20 gtk30 USE_PERL5= build USE_XORG= x11 xcb xcomposite xdamage xext xfixes xrender xt HAS_CONFIGURE= yes CONFIGURE_OUTSOURCE= yes LDFLAGS+= -Wl,--as-needed BINARY_ALIAS+= python3=python${PYTHON3_DEFAULT} BUNDLE_LIBS= yes BUILD_DEPENDS+= llvm${LLVM_DEFAULT}>0:devel/llvm${LLVM_DEFAULT} \ rust-cbindgen>=0.14.3:devel/rust-cbindgen \ ${RUST_DEFAULT}>=1.41:lang/${RUST_DEFAULT} \ ${LOCALBASE}/bin/python${PYTHON3_DEFAULT}:lang/python${PYTHON3_DEFAULT:S/.//g} \ node:www/node LIB_DEPENDS+= libdrm.so:graphics/libdrm MOZ_EXPORT+= ${CONFIGURE_ENV} \ LLVM_CONFIG=llvm-config${LLVM_DEFAULT} \ PERL="${PERL}" \ PYTHON3="${LOCALBASE}/bin/python${PYTHON3_DEFAULT}" \ RUSTFLAGS="${RUSTFLAGS}" MOZ_OPTIONS+= --prefix="${PREFIX}" MOZ_MK_OPTIONS+=MOZ_OBJDIR="${BUILD_WRKSRC}" # Require newer Clang than what's in base system unless user opted out . if ${CC} == cc && ${CXX} == c++ && exists(/usr/lib/libc++.so) BUILD_DEPENDS+= ${LOCALBASE}/bin/clang${LLVM_DEFAULT}:devel/llvm${LLVM_DEFAULT} CPP= ${LOCALBASE}/bin/clang-cpp${LLVM_DEFAULT} CC= ${LOCALBASE}/bin/clang${LLVM_DEFAULT} CXX= ${LOCALBASE}/bin/clang++${LLVM_DEFAULT} USES:= ${USES:Ncompiler\:*} # XXX avoid warnings . endif MOZSRC?= ${WRKSRC} PLISTF?= ${WRKDIR}/plist_files MOZCONFIG?= ${WRKSRC}/.mozconfig MOZILLA_PLIST_DIRS?= bin lib share/pixmaps share/applications # Adjust -C target-cpu if -march/-mcpu is set by bsd.cpu.mk .if ${ARCH} == amd64 || ${ARCH} == i386 RUSTFLAGS+= ${CFLAGS:M-march=*:S/-march=/-C target-cpu=/} .else RUSTFLAGS+= ${CFLAGS:M-mcpu=*:S/-mcpu=/-C target-cpu=/} .endif # Standard depends _ALL_DEPENDS= av1 event ffi graphite harfbuzz icu jpeg nspr nss png pixman sqlite vpx webp .if exists(${FILESDIR}/patch-bug1559213) av1_LIB_DEPENDS= libaom.so:multimedia/aom libdav1d.so:multimedia/dav1d av1_MOZ_OPTIONS= --with-system-av1 .endif event_LIB_DEPENDS= libevent.so:devel/libevent event_MOZ_OPTIONS= --with-system-libevent ffi_LIB_DEPENDS= libffi.so:devel/libffi ffi_MOZ_OPTIONS= --enable-system-ffi .if exists(${FILESDIR}/patch-bug847568) graphite_LIB_DEPENDS= libgraphite2.so:graphics/graphite2 graphite_MOZ_OPTIONS= --with-system-graphite2 harfbuzz_LIB_DEPENDS= libharfbuzz.so:print/harfbuzz harfbuzz_MOZ_OPTIONS= --with-system-harfbuzz .endif icu_LIB_DEPENDS= libicui18n.so:devel/icu icu_MOZ_OPTIONS= --with-system-icu --with-intl-api -jpeg_BUILD_DEPENDS=yasm:devel/yasm jpeg_USES= jpeg jpeg_MOZ_OPTIONS= --with-system-jpeg=${LOCALBASE} nspr_LIB_DEPENDS= libnspr4.so:devel/nspr nspr_MOZ_OPTIONS= --with-system-nspr nss_LIB_DEPENDS= libnss3.so:security/nss nss_MOZ_OPTIONS= --with-system-nss pixman_LIB_DEPENDS= libpixman-1.so:x11/pixman pixman_MOZ_OPTIONS= --enable-system-pixman png_LIB_DEPENDS= libpng.so:graphics/png png_MOZ_OPTIONS= --with-system-png=${LOCALBASE} sqlite_LIB_DEPENDS= libsqlite3.so:databases/sqlite3 sqlite_MOZ_OPTIONS= --enable-system-sqlite -vpx_BUILD_DEPENDS= yasm:devel/yasm vpx_LIB_DEPENDS= libvpx.so:multimedia/libvpx vpx_MOZ_OPTIONS= --with-system-libvpx webp_LIB_DEPENDS= libwebp.so:graphics/webp webp_MOZ_OPTIONS= --with-system-webp .for use in ${USE_MOZILLA} ${use:S/-/_WITHOUT_/}= ${TRUE} .endfor LIB_DEPENDS+= libfontconfig.so:x11-fonts/fontconfig \ libfreetype.so:print/freetype2 .for dep in ${_ALL_DEPENDS} ${USE_MOZILLA:M+*:S/+//} .if !defined(_WITHOUT_${dep}) BUILD_DEPENDS+= ${${dep}_BUILD_DEPENDS} LIB_DEPENDS+= ${${dep}_LIB_DEPENDS} RUN_DEPENDS+= ${${dep}_RUN_DEPENDS} USES+= ${${dep}_USES} MOZ_OPTIONS+= ${${dep}_MOZ_OPTIONS} .else BUILD_DEPENDS+= ${-${dep}_BUILD_DEPENDS} .endif .endfor # Standard options MOZ_OPTIONS+= \ --enable-update-channel=${PKGNAMESUFFIX:Urelease:S/^-//} \ --disable-updater \ --with-system-zlib # API keys from www/chromium # http://www.chromium.org/developers/how-tos/api-keys # Note: these are for FreeBSD use ONLY. For your own distribution, # please get your own set of keys. MOZ_EXPORT+= MOZ_GOOGLE_LOCATION_SERVICE_API_KEY=AIzaSyBsp9n41JLW8jCokwn7vhoaMejDFRd1mp8 MOZ_EXPORT+= MOZ_GOOGLE_SAFEBROWSING_API_KEY=AIzaSyBsp9n41JLW8jCokwn7vhoaMejDFRd1mp8 .if ${PORT_OPTIONS:MOPTIMIZED_CFLAGS} CFLAGS+= -O3 MOZ_EXPORT+= MOZ_OPTIMIZE_FLAGS="${CFLAGS:M-O*}" MOZ_OPTIONS+= --enable-optimize .else MOZ_OPTIONS+= --disable-optimize . if ${/usr/bin/ld:L:tA} != /usr/bin/ld.lld # ld 2.17 barfs on Stylo built with -C opt-level=0 USE_BINUTILS= yes LDFLAGS+= -B${LOCALBASE}/bin . endif .endif .if ${PORT_OPTIONS:MCANBERRA} RUN_DEPENDS+= libcanberra>0:audio/libcanberra .endif .if ${PORT_OPTIONS:MDBUS} BUILD_DEPENDS+= libnotify>0:devel/libnotify LIB_DEPENDS+= libdbus-1.so:devel/dbus \ libdbus-glib-1.so:devel/dbus-glib .else MOZ_OPTIONS+= --disable-dbus .endif .if ${PORT_OPTIONS:MFFMPEG} # dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp RUN_DEPENDS+= ffmpeg>=0.8,1:multimedia/ffmpeg .endif .if ${PORT_OPTIONS:MLIBPROXY} LIB_DEPENDS+= libproxy.so:net/libproxy MOZ_OPTIONS+= --enable-libproxy .else MOZ_OPTIONS+= --disable-libproxy .endif .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 .if ${PORT_OPTIONS:MJACK} BUILD_DEPENDS+= ${LOCALBASE}/include/jack/jack.h:audio/jack MOZ_OPTIONS+= --enable-jack .endif .if ${PORT_OPTIONS:MPULSEAUDIO} BUILD_DEPENDS+= ${LOCALBASE}/include/pulse/pulseaudio.h:audio/pulseaudio MOZ_OPTIONS+= --enable-pulseaudio .else MOZ_OPTIONS+= --disable-pulseaudio .endif .if ${PORT_OPTIONS:MSNDIO} BUILD_DEPENDS+= ${LOCALBASE}/include/sndio.h:audio/sndio post-patch-SNDIO-on: @${REINPLACE_CMD} -e 's|OpenBSD|${OPSYS}|g' \ -e '/DISABLE_LIBSNDIO_DLOPEN/d' \ ${MOZSRC}/media/libcubeb/src/moz.build .endif .if ${PORT_OPTIONS:MDEBUG} MOZ_OPTIONS+= --enable-debug --disable-release STRIP= # ports/184285 .else MOZ_OPTIONS+= --disable-debug --disable-debug-symbols --enable-release . if ${ARCH:Maarch64} || ${MACHINE_CPU:Msse2} MOZ_OPTIONS+= --enable-rust-simd . endif .endif .if ${PORT_OPTIONS:MPROFILE} MOZ_OPTIONS+= --enable-profiling STRIP= .else MOZ_OPTIONS+= --disable-profiling .endif .if ${PORT_OPTIONS:MTEST} USE_XORG+= xscrnsaver MOZ_OPTIONS+= --enable-tests .else MOZ_OPTIONS+= --disable-tests .endif .if !defined(STRIP) || ${STRIP} == "" MOZ_OPTIONS+= --disable-strip --disable-install-strip .else MOZ_OPTIONS+= --enable-strip --enable-install-strip .endif # _MAKE_JOBS is only available after bsd.port.post.mk, thus cannot be # used in .mozconfig. And client.mk automatically uses -jN where N # is what multiprocessing.cpu_count() returns. .if defined(DISABLE_MAKE_JOBS) || defined(MAKE_JOBS_UNSAFE) MAKE_JOBS_NUMBER= 1 .endif .if defined(MAKE_JOBS_NUMBER) MOZ_MAKE_FLAGS+=-j${MAKE_JOBS_NUMBER} .endif .if defined(MOZ_MAKE_FLAGS) MOZ_MK_OPTIONS+=MOZ_MAKE_FLAGS="${MOZ_MAKE_FLAGS}" .endif .if ${ARCH} == amd64 . if ${USE_MOZILLA:M-nss} USE_BINUTILS= # intel-gcm.s CFLAGS+= -B${LOCALBASE}/bin LDFLAGS+= -B${LOCALBASE}/bin . endif .elif ${ARCH:Mpowerpc*} BUILD_DEPENDS+= as:devel/binutils . if ${ARCH} == "powerpc64" MOZ_EXPORT+= UNAME_m="${ARCH}" . endif .elif ${ARCH} == "sparc64" # Work around miscompilation/mislinkage of the sCanonicalVTable hacks. MOZ_OPTIONS+= --disable-v1-string-abi .endif .else # bsd.port.post.mk post-patch: gecko-post-patch gecko-post-patch: @${RM} ${MOZCONFIG} .if !defined(NOMOZCONFIG) .for arg in ${MOZ_OPTIONS} @${ECHO_CMD} ac_add_options ${arg:Q} >> ${MOZCONFIG} .endfor .for arg in ${MOZ_MK_OPTIONS} @${ECHO_CMD} mk_add_options ${arg:Q} >> ${MOZCONFIG} .endfor .for var in ${MOZ_EXPORT} @${ECHO_CMD} export ${var:Q} >> ${MOZCONFIG} .endfor .endif # .if !defined(NOMOZCONFIG) .if ${USE_MOZILLA:M-nspr} @${ECHO_MSG} "===> Applying NSPR patches" @for i in ${.CURDIR}/../../devel/nspr/files/patch-*; do \ ${PATCH} ${PATCH_ARGS} -d ${MOZSRC}/nsprpub < $$i; \ done .endif .if ${USE_MOZILLA:M-nss} @${ECHO_MSG} "===> Applying NSS patches" @for i in ${.CURDIR}/../../security/nss/files/patch-*; do \ ${PATCH} ${PATCH_ARGS} -d ${MOZSRC}/security/nss < $$i; \ done .endif @if [ -f ${WRKSRC}/config/baseconfig.mk ] ; then \ ${REINPLACE_CMD} -e 's|%%MOZILLA%%|${MOZILLA}|g' \ ${WRKSRC}/config/baseconfig.mk; \ fi @${REINPLACE_CMD} -e 's|%%MOZILLA%%|${MOZILLA}|g' \ ${MOZSRC}/config/baseconfig.mk @${REINPLACE_CMD} -e 's|/usr/local/netscape|${LOCALBASE}|g ; \ s|/usr/local/lib/netscape|${LOCALBASE}/lib|g' \ ${MOZSRC}/xpcom/io/SpecialSystemDirectory.cpp @${REINPLACE_CMD} -e 's|/etc|${PREFIX}&|g' \ ${MOZSRC}/xpcom/build/nsXPCOMPrivate.h @${REINPLACE_CMD} -e 's|/usr/local|${LOCALBASE}|g' \ -e 's|mozilla/plugins|browser_plugins|g' \ -e 's|share/mozilla/extensions|lib/xpi|g' \ ${MOZSRC}/xpcom/io/nsAppFileLocationProvider.cpp \ ${MOZSRC}/toolkit/xre/nsXREDirProvider.cpp # Disable vendor checksums like lang/rust @${REINPLACE_CMD} 's,"files":{[^}]*},"files":{},' \ ${MOZSRC}/third_party/rust/*/.cargo-checksum.json pre-configure-script: # Check that the running kernel has COMPAT_FREEBSD11 required by lang/rust post-ino64 @${SETENV} CC="${CC}" OPSYS="${OPSYS}" OSVERSION="${OSVERSION}" WRKDIR="${WRKDIR}" \ ${SH} ${SCRIPTSDIR}/rust-compat11-canary.sh post-install-script: gecko-create-plist gecko-create-plist: # Create the plist ${RM} ${PLISTF} .for dir in ${MOZILLA_PLIST_DIRS} @cd ${STAGEDIR}${PREFIX}/${dir} && ${FIND} -H -s * ! -type d | \ ${SED} -e 's|^|${dir}/|' >> ${PLISTF} .endfor ${CAT} ${PLISTF} | ${SORT} >> ${TMPPLIST} .endif .endif # HERE THERE BE TACOS -- adamw Index: branches/2020Q3/mail/thunderbird/files/patch-cubeb-oss =================================================================== --- branches/2020Q3/mail/thunderbird/files/patch-cubeb-oss (nonexistent) +++ branches/2020Q3/mail/thunderbird/files/patch-cubeb-oss (revision 545614) @@ -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 ++ * Copyright © 2020 Ka Ho Ng ++ * ++ * 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 "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"); ++ ret = CUBEB_ERROR_INVALID_PARAMETER; ++ goto error; ++ } ++ if (s->record.fd == -1) { ++ if ((s->record.fd = open(s->record.name, O_RDONLY)) == -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); ++ } ++ 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) { ++ 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->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->mutex, NULL) != 0) { ++ LOG("Failed to create mutex"); ++ goto error; ++ } ++ s->play.frame_size = s->play.info.channels * ++ (s->play.info.precision / 8); ++ if (s->play.fd != -1) { ++ audio_buf_info bi; ++ int frag = oss_calc_frag_params(latency_frames, s->play.frame_size); ++ if (ioctl(s->play.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) ++ LOG("Failed to set play fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", frag); ++ if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi) == 0) { ++ unsigned int nfr = bi.fragsize / s->play.frame_size; ++ if (playnfr < nfr) { ++ playnfr = nfr; ++ } ++ } ++ } ++ s->record.frame_size = s->record.info.channels * ++ (s->record.info.precision / 8); ++ if (s->record.fd != -1) { ++ audio_buf_info bi; ++ int frag = oss_calc_frag_params(latency_frames, 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); ++ if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi) == 0) { ++ unsigned int nfr = bi.fragsize / s->record.frame_size; ++ if (recnfr < nfr) { ++ recnfr = nfr; ++ } ++ } ++ } ++ if (s->play.fd != -1 && s->record.fd != -1) ++ s->nfr = (playnfr < recnfr) ? playnfr : recnfr; ++ else if (s->play.fd != -1) ++ s->nfr = playnfr; ++ else if (s->record.fd != -1) ++ s->nfr = recnfr; ++ ++ if (s->play.fd != -1) { ++ if ((s->play.buf = calloc(s->nfr, 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) { ++ ret = CUBEB_ERROR; ++ goto error; ++ } ++ } ++ ++ *stream = s; ++ return CUBEB_OK; ++error: ++ if (s != NULL) { ++ oss_stream_destroy(s); ++ } ++ return ret; ++} ++ ++static int ++oss_stream_start(cubeb_stream * s) ++{ ++ s->running = true; ++ 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_get_position(cubeb_stream * s, uint64_t * position) ++{ ++ pthread_mutex_lock(&s->mutex); ++ *position = s->frames_written; ++ pthread_mutex_unlock(&s->mutex); ++ 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->mutex); ++ stream->volume = volume; ++ pthread_mutex_unlock(&stream->mutex); ++ 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_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}; Property changes on: branches/2020Q3/mail/thunderbird/files/patch-cubeb-oss ___________________________________________________________________ Added: fbsd:nokeywords ## -0,0 +1 ## +yes \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: branches/2020Q3/www/firefox/Makefile =================================================================== --- branches/2020Q3/www/firefox/Makefile (revision 545613) +++ branches/2020Q3/www/firefox/Makefile (revision 545614) @@ -1,61 +1,61 @@ # Created by: Alan Eldridge # $FreeBSD$ PORTNAME= firefox DISTVERSION= 80.0 -PORTREVISION= 1 +PORTREVISION= 2 PORTEPOCH= 1 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.55: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 \ py${PYTHON3_DEFAULT:S/.//}-sqlite3>0:databases/py-sqlite3@py${PYTHON3_DEFAULT:S/.//} \ 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: branches/2020Q3/www/firefox/Makefile.options =================================================================== --- branches/2020Q3/www/firefox/Makefile.options (revision 545613) +++ branches/2020Q3/www/firefox/Makefile.options (revision 545614) @@ -1,14 +1,15 @@ # -*- makefile-bsdmake -*- OPTIONS_DEFINE+= CANBERRA DBUS DEBUG FFMPEG \ LIBPROXY OPTIMIZED_CFLAGS PROFILE TEST OPTIONS_DEFAULT+= DBUS FFMPEG OPTIMIZED_CFLAGS PROFILE \ - ${OPTIONS_GROUP_AUDIO} + ${OPTIONS_GROUP_AUDIO:NALSA} OPTIONS_GROUP+= AUDIO OPTIONS_GROUP_AUDIO= ALSA JACK PULSEAUDIO SNDIO +AUDIO_DESC?= Extra cubeb audio backends (OSS is always available) CANBERRA_DESC?= Sound theme alerts LIBPROXY_DESC?= Proxy support via libproxy LIGHTNING_DESC?= Calendar extension Index: branches/2020Q3/www/firefox/files/patch-cubeb-oss =================================================================== --- branches/2020Q3/www/firefox/files/patch-cubeb-oss (nonexistent) +++ branches/2020Q3/www/firefox/files/patch-cubeb-oss (revision 545614) @@ -0,0 +1,1193 @@ +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,1129 @@ ++/* ++ * Copyright © 2019-2020 Nia Alarie ++ * Copyright © 2020 Ka Ho Ng ++ * ++ * 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 "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"); ++ ret = CUBEB_ERROR_INVALID_PARAMETER; ++ goto error; ++ } ++ if (s->record.fd == -1) { ++ if ((s->record.fd = open(s->record.name, O_RDONLY)) == -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); ++ } ++ 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) { ++ 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->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->mutex, NULL) != 0) { ++ LOG("Failed to create mutex"); ++ goto error; ++ } ++ s->play.frame_size = s->play.info.channels * ++ (s->play.info.precision / 8); ++ if (s->play.fd != -1) { ++ audio_buf_info bi; ++ int frag = oss_calc_frag_params(latency_frames, s->play.frame_size); ++ if (ioctl(s->play.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) ++ LOG("Failed to set play fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", frag); ++ if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi) == 0) { ++ unsigned int nfr = bi.fragsize / s->play.frame_size; ++ if (playnfr < nfr) { ++ playnfr = nfr; ++ } ++ } ++ } ++ s->record.frame_size = s->record.info.channels * ++ (s->record.info.precision / 8); ++ if (s->record.fd != -1) { ++ audio_buf_info bi; ++ int frag = oss_calc_frag_params(latency_frames, 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); ++ if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi) == 0) { ++ unsigned int nfr = bi.fragsize / s->record.frame_size; ++ if (recnfr < nfr) { ++ recnfr = nfr; ++ } ++ } ++ } ++ if (s->play.fd != -1 && s->record.fd != -1) ++ s->nfr = (playnfr < recnfr) ? playnfr : recnfr; ++ else if (s->play.fd != -1) ++ s->nfr = playnfr; ++ else if (s->record.fd != -1) ++ s->nfr = recnfr; ++ ++ if (s->play.fd != -1) { ++ if ((s->play.buf = calloc(s->nfr, 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) { ++ ret = CUBEB_ERROR; ++ goto error; ++ } ++ } ++ ++ *stream = s; ++ return CUBEB_OK; ++error: ++ if (s != NULL) { ++ oss_stream_destroy(s); ++ } ++ return ret; ++} ++ ++static int ++oss_stream_start(cubeb_stream * s) ++{ ++ s->running = true; ++ 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_get_position(cubeb_stream * s, uint64_t * position) ++{ ++ pthread_mutex_lock(&s->mutex); ++ *position = s->frames_written; ++ pthread_mutex_unlock(&s->mutex); ++ 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->mutex); ++ stream->volume = volume; ++ pthread_mutex_unlock(&stream->mutex); ++ 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}; Property changes on: branches/2020Q3/www/firefox/files/patch-cubeb-oss ___________________________________________________________________ Added: fbsd:nokeywords ## -0,0 +1 ## +yes \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: branches/2020Q3/www/firefox/pkg-message =================================================================== --- branches/2020Q3/www/firefox/pkg-message (revision 545613) +++ branches/2020Q3/www/firefox/pkg-message (revision 545614) @@ -1,43 +1,43 @@ [ { type: install message: <> .ssh/authorized_keys" The SSH server on `remote_host` must allow pub key authentication. EOM } ] Index: branches/2020Q3/www/firefox-esr/Makefile =================================================================== --- branches/2020Q3/www/firefox-esr/Makefile (revision 545613) +++ branches/2020Q3/www/firefox-esr/Makefile (revision 545614) @@ -1,61 +1,62 @@ # Created by: Alan Eldridge # $FreeBSD$ PORTNAME= firefox DISTVERSION= 68.12.0 +PORTREVISION= 1 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.21:devel/nspr \ nss>=3.52.1:security/nss \ icu>=63.1,1:devel/icu \ libevent>=2.1.8:devel/libevent \ harfbuzz>=2.4.0:print/harfbuzz \ graphite2>=1.3.13:graphics/graphite2 \ png>=1.6.35:graphics/png \ libvpx>=1.5.0:multimedia/libvpx \ sqlite3>=3.28.0:databases/sqlite3 \ v4l_compat>0:multimedia/v4l_compat \ autoconf-2.13:devel/autoconf213 \ nasm:devel/nasm \ zip:archivers/zip USE_GECKO= gecko CPE_PRODUCT= ${PORTNAME}_esr CONFLICTS_INSTALL= firefox USE_MOZILLA= -vpx 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: branches/2020Q3/www/firefox-esr/files/patch-cubeb-oss =================================================================== --- branches/2020Q3/www/firefox-esr/files/patch-cubeb-oss (nonexistent) +++ branches/2020Q3/www/firefox-esr/files/patch-cubeb-oss (revision 545614) @@ -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 ++ * Copyright © 2020 Ka Ho Ng ++ * ++ * 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 "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"); ++ ret = CUBEB_ERROR_INVALID_PARAMETER; ++ goto error; ++ } ++ if (s->record.fd == -1) { ++ if ((s->record.fd = open(s->record.name, O_RDONLY)) == -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); ++ } ++ 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) { ++ 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->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->mutex, NULL) != 0) { ++ LOG("Failed to create mutex"); ++ goto error; ++ } ++ s->play.frame_size = s->play.info.channels * ++ (s->play.info.precision / 8); ++ if (s->play.fd != -1) { ++ audio_buf_info bi; ++ int frag = oss_calc_frag_params(latency_frames, s->play.frame_size); ++ if (ioctl(s->play.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) ++ LOG("Failed to set play fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", frag); ++ if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi) == 0) { ++ unsigned int nfr = bi.fragsize / s->play.frame_size; ++ if (playnfr < nfr) { ++ playnfr = nfr; ++ } ++ } ++ } ++ s->record.frame_size = s->record.info.channels * ++ (s->record.info.precision / 8); ++ if (s->record.fd != -1) { ++ audio_buf_info bi; ++ int frag = oss_calc_frag_params(latency_frames, 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); ++ if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi) == 0) { ++ unsigned int nfr = bi.fragsize / s->record.frame_size; ++ if (recnfr < nfr) { ++ recnfr = nfr; ++ } ++ } ++ } ++ if (s->play.fd != -1 && s->record.fd != -1) ++ s->nfr = (playnfr < recnfr) ? playnfr : recnfr; ++ else if (s->play.fd != -1) ++ s->nfr = playnfr; ++ else if (s->record.fd != -1) ++ s->nfr = recnfr; ++ ++ if (s->play.fd != -1) { ++ if ((s->play.buf = calloc(s->nfr, 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) { ++ ret = CUBEB_ERROR; ++ goto error; ++ } ++ } ++ ++ *stream = s; ++ return CUBEB_OK; ++error: ++ if (s != NULL) { ++ oss_stream_destroy(s); ++ } ++ return ret; ++} ++ ++static int ++oss_stream_start(cubeb_stream * s) ++{ ++ s->running = true; ++ 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_get_position(cubeb_stream * s, uint64_t * position) ++{ ++ pthread_mutex_lock(&s->mutex); ++ *position = s->frames_written; ++ pthread_mutex_unlock(&s->mutex); ++ 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->mutex); ++ stream->volume = volume; ++ pthread_mutex_unlock(&stream->mutex); ++ 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_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}; Property changes on: branches/2020Q3/www/firefox-esr/files/patch-cubeb-oss ___________________________________________________________________ Added: fbsd:nokeywords ## -0,0 +1 ## +yes \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: branches/2020Q3/www/firefox-esr/pkg-message =================================================================== --- branches/2020Q3/www/firefox-esr/pkg-message (revision 545613) +++ branches/2020Q3/www/firefox-esr/pkg-message (revision 545614) @@ -1,43 +1,43 @@ [ { type: install message: <> .ssh/authorized_keys" The SSH server on `remote_host` must allow pub key authentication. EOM } ] Index: branches/2020Q3 =================================================================== --- branches/2020Q3 (revision 545613) +++ branches/2020Q3 (revision 545614) Property changes on: branches/2020Q3 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r545611