1218 lines
38 KiB
Text
1218 lines
38 KiB
Text
diff --git build/moz.configure/old.configure build/moz.configure/old.configure
|
|
index cd6c37cf4c7c..e98dc7973a6a 100644
|
|
--- build/moz.configure/old.configure
|
|
+++ build/moz.configure/old.configure
|
|
@@ -275,6 +275,7 @@ def old_configure_options(*options):
|
|
'--with-nspr-prefix',
|
|
'--with-nss-exec-prefix',
|
|
'--with-nss-prefix',
|
|
+ '--with-oss',
|
|
'--with-pthreads',
|
|
'--with-qemu-exe',
|
|
'--with-sixgill',
|
|
diff --git old-configure.in old-configure.in
|
|
index dff46614635e..dbbfdb76ab78 100644
|
|
--- old-configure.in
|
|
+++ old-configure.in
|
|
@@ -3128,6 +3128,67 @@ AC_DEFINE(MOZ_WEBM_ENCODER)
|
|
AC_SUBST(MOZ_WEBM_ENCODER)
|
|
|
|
dnl ==================================
|
|
+dnl = Check OSS availability
|
|
+dnl ==================================
|
|
+
|
|
+dnl If using Linux, Solaris or BSDs, ensure that OSS is available
|
|
+case "$OS_TARGET" in
|
|
+Linux|SunOS|DragonFly|FreeBSD|NetBSD|GNU/kFreeBSD)
|
|
+ MOZ_OSS=1
|
|
+ ;;
|
|
+esac
|
|
+
|
|
+MOZ_ARG_WITH_STRING(oss,
|
|
+[ --with-oss[=PFX] Enable OpenSoundSystem support [installed at prefix PFX]],
|
|
+ OSSPREFIX=$withval)
|
|
+
|
|
+if test -n "$OSSPREFIX"; then
|
|
+ if test "$OSSPREFIX" != "no"; then
|
|
+ MOZ_OSS=1
|
|
+ else
|
|
+ MOZ_OSS=
|
|
+ fi
|
|
+fi
|
|
+
|
|
+_SAVE_CFLAGS=$CFLAGS
|
|
+_SAVE_LIBS=$LIBS
|
|
+if test -n "$MOZ_OSS"; then
|
|
+ dnl Prefer 4Front implementation
|
|
+ AC_MSG_CHECKING([MOZ_OSS_CFLAGS])
|
|
+ if test "$OSSPREFIX" != "yes"; then
|
|
+ oss_conf=${OSSPREFIX%/usr}/etc/oss.conf
|
|
+ if test -f "$oss_conf"; then
|
|
+ . "$oss_conf"
|
|
+ else
|
|
+ OSSLIBDIR=$OSSPREFIX/lib/oss
|
|
+ fi
|
|
+ if test -d "$OSSLIBDIR"; then
|
|
+ MOZ_OSS_CFLAGS="$MOZ_OSS_CFLAGS -I$OSSLIBDIR/include"
|
|
+ fi
|
|
+ fi
|
|
+ AC_MSG_RESULT([$MOZ_OSS_CFLAGS])
|
|
+
|
|
+ CFLAGS="$CFLAGS $MOZ_OSS_CFLAGS"
|
|
+ MOZ_CHECK_HEADERS(sys/soundcard.h soundcard.h)
|
|
+
|
|
+ if test "$ac_cv_header_sys_soundcard_h" != "yes" -a \
|
|
+ "$ac_cv_header_soundcard_h" != "yes"; then
|
|
+ AC_MSG_ERROR([Need OSS for Ogg, Wave or WebM decoding on $OS_TARGET. Disable with --without-oss.])
|
|
+ fi
|
|
+
|
|
+ dnl Assume NetBSD implementation over SunAudio
|
|
+ AC_CHECK_LIB(ossaudio, _oss_ioctl,
|
|
+ [AC_DEFINE_UNQUOTED(CUBEB_OSS_DEFAULT_OUTPUT, "/dev/sound")
|
|
+ MOZ_OSS_LIBS="$MOZ_OSS_LIBS -lossaudio"])
|
|
+fi
|
|
+CFLAGS=$_SAVE_CFLAGS
|
|
+LIBS=$_SAVE_LIBS
|
|
+
|
|
+AC_SUBST(MOZ_OSS)
|
|
+AC_SUBST_LIST(MOZ_OSS_CFLAGS)
|
|
+AC_SUBST_LIST(MOZ_OSS_LIBS)
|
|
+
|
|
+dnl ==================================
|
|
dnl = Check alsa availability on Linux
|
|
dnl ==================================
|
|
|
|
diff --git dom/media/CubebUtils.cpp dom/media/CubebUtils.cpp
|
|
index e10e07e06ade..1fd561bea113 100644
|
|
--- dom/media/CubebUtils.cpp
|
|
+++ dom/media/CubebUtils.cpp
|
|
@@ -71,7 +71,8 @@ const char* AUDIOSTREAM_BACKEND_ID_STR[] = {
|
|
"sndio",
|
|
"opensl",
|
|
"audiotrack",
|
|
- "kai"
|
|
+ "kai",
|
|
+ "oss",
|
|
};
|
|
/* Index for failures to create an audio stream the first time. */
|
|
const int CUBEB_BACKEND_INIT_FAILURE_FIRST =
|
|
diff --git media/libcubeb/AUTHORS media/libcubeb/AUTHORS
|
|
index 0fde65baad34..f8663c43b475 100644
|
|
--- media/libcubeb/AUTHORS
|
|
+++ media/libcubeb/AUTHORS
|
|
@@ -4,6 +4,7 @@ Michael Wu <mwu@mozilla.com>
|
|
Paul Adenot <paul@paul.cx>
|
|
David Richards <drichards@mozilla.com>
|
|
Sebastien Alaiwan <sebastien.alaiwan@gmail.com>
|
|
+Evgeniy Vodolazskiy <waterlaz@gmail.com>
|
|
KO Myung-Hun <komh@chollian.net>
|
|
Haakon Sporsheim <haakon.sporsheim@telenordigital.com>
|
|
Alex Chronopoulos <achronop@gmail.com>
|
|
diff --git media/libcubeb/src/cubeb.c media/libcubeb/src/cubeb.c
|
|
index 57bcb4c13652..68be024f4a68 100644
|
|
--- media/libcubeb/src/cubeb.c
|
|
+++ media/libcubeb/src/cubeb.c
|
|
@@ -54,6 +54,9 @@ int audiotrack_init(cubeb ** context, char const * context_name);
|
|
#if defined(USE_KAI)
|
|
int kai_init(cubeb ** context, char const * context_name);
|
|
#endif
|
|
+#if defined(USE_OSS)
|
|
+int oss_init(cubeb ** context, char const * context_name);
|
|
+#endif
|
|
|
|
static int
|
|
validate_stream_params(cubeb_stream_params * input_stream_params,
|
|
@@ -155,6 +158,10 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam
|
|
#if defined(USE_KAI)
|
|
init_oneshot = kai_init;
|
|
#endif
|
|
+ } else if (!strcmp(backend_name, "oss")) {
|
|
+#if defined(USE_OSS)
|
|
+ init_oneshot = oss_init;
|
|
+#endif
|
|
} else {
|
|
/* Already set */
|
|
}
|
|
@@ -196,6 +203,9 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam
|
|
#if defined(USE_KAI)
|
|
kai_init,
|
|
#endif
|
|
+#if defined(USE_OSS)
|
|
+ oss_init,
|
|
+#endif
|
|
};
|
|
int i;
|
|
|
|
diff --git media/libcubeb/src/cubeb_alsa.c media/libcubeb/src/cubeb_alsa.c
|
|
index 05ad27fef53b..3ea78e994f59 100644
|
|
--- media/libcubeb/src/cubeb_alsa.c
|
|
+++ media/libcubeb/src/cubeb_alsa.c
|
|
@@ -12,6 +12,7 @@
|
|
#include <sys/time.h>
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
+#include <dlfcn.h>
|
|
#include <poll.h>
|
|
#include <unistd.h>
|
|
#include <alsa/asoundlib.h>
|
|
@@ -25,6 +26,52 @@
|
|
|
|
#define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"
|
|
|
|
+#ifdef DISABLE_LIBASOUND_DLOPEN
|
|
+#define WRAP(x) x
|
|
+#else
|
|
+#define WRAP(x) cubeb_##x
|
|
+#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x
|
|
+MAKE_TYPEDEF(snd_config);
|
|
+MAKE_TYPEDEF(snd_config_add);
|
|
+MAKE_TYPEDEF(snd_config_copy);
|
|
+MAKE_TYPEDEF(snd_config_delete);
|
|
+MAKE_TYPEDEF(snd_config_get_id);
|
|
+MAKE_TYPEDEF(snd_config_get_string);
|
|
+MAKE_TYPEDEF(snd_config_imake_integer);
|
|
+MAKE_TYPEDEF(snd_config_search);
|
|
+MAKE_TYPEDEF(snd_config_search_definition);
|
|
+MAKE_TYPEDEF(snd_lib_error_set_handler);
|
|
+MAKE_TYPEDEF(snd_pcm_avail_update);
|
|
+MAKE_TYPEDEF(snd_pcm_close);
|
|
+MAKE_TYPEDEF(snd_pcm_delay);
|
|
+MAKE_TYPEDEF(snd_pcm_drain);
|
|
+MAKE_TYPEDEF(snd_pcm_frames_to_bytes);
|
|
+MAKE_TYPEDEF(snd_pcm_get_params);
|
|
+/* snd_pcm_hw_params_alloca is actually a macro */
|
|
+/* MAKE_TYPEDEF(snd_pcm_hw_params_alloca); */
|
|
+MAKE_TYPEDEF(snd_pcm_hw_params_sizeof);
|
|
+#define snd_pcm_hw_params_sizeof cubeb_snd_pcm_hw_params_sizeof
|
|
+MAKE_TYPEDEF(snd_pcm_hw_params_any);
|
|
+MAKE_TYPEDEF(snd_pcm_hw_params_get_channels_max);
|
|
+MAKE_TYPEDEF(snd_pcm_hw_params_get_rate);
|
|
+MAKE_TYPEDEF(snd_pcm_hw_params_set_rate_near);
|
|
+MAKE_TYPEDEF(snd_pcm_nonblock);
|
|
+MAKE_TYPEDEF(snd_pcm_open);
|
|
+MAKE_TYPEDEF(snd_pcm_open_lconf);
|
|
+MAKE_TYPEDEF(snd_pcm_pause);
|
|
+MAKE_TYPEDEF(snd_pcm_poll_descriptors);
|
|
+MAKE_TYPEDEF(snd_pcm_poll_descriptors_count);
|
|
+MAKE_TYPEDEF(snd_pcm_poll_descriptors_revents);
|
|
+MAKE_TYPEDEF(snd_pcm_readi);
|
|
+MAKE_TYPEDEF(snd_pcm_recover);
|
|
+MAKE_TYPEDEF(snd_pcm_set_params);
|
|
+MAKE_TYPEDEF(snd_pcm_start);
|
|
+MAKE_TYPEDEF(snd_pcm_state);
|
|
+MAKE_TYPEDEF(snd_pcm_writei);
|
|
+
|
|
+#undef MAKE_TYPEDEF
|
|
+#endif
|
|
+
|
|
/* ALSA is not thread-safe. snd_pcm_t instances are individually protected
|
|
by the owning cubeb_stream's mutex. snd_pcm_t creation and destruction
|
|
is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1),
|
|
@@ -65,6 +112,8 @@ struct cubeb {
|
|
workaround is not required. */
|
|
snd_config_t * local_config;
|
|
int is_pa;
|
|
+
|
|
+ void * libasound;
|
|
};
|
|
|
|
enum stream_state {
|
|
@@ -243,8 +292,8 @@ set_timeout(struct timeval * timeout, unsigned int ms)
|
|
static void
|
|
stream_buffer_decrement(cubeb_stream * stm, long count)
|
|
{
|
|
- char * bufremains = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, count);
|
|
- memmove(stm->buffer, bufremains, snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes - count));
|
|
+ char * bufremains = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count);
|
|
+ memmove(stm->buffer, bufremains, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count));
|
|
stm->bufframes -= count;
|
|
}
|
|
|
|
@@ -276,9 +325,9 @@ alsa_process_stream(cubeb_stream * stm)
|
|
/* Call _poll_descriptors_revents() even if we don't use it
|
|
to let underlying plugins clear null events. Otherwise poll()
|
|
may wake up again and again, producing unnecessary CPU usage. */
|
|
- snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
|
|
+ WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
|
|
|
|
- avail = snd_pcm_avail_update(stm->pcm);
|
|
+ avail = WRAP(snd_pcm_avail_update)(stm->pcm);
|
|
|
|
/* Got null event? Bail and wait for another wakeup. */
|
|
if (avail == 0) {
|
|
@@ -301,7 +350,7 @@ alsa_process_stream(cubeb_stream * stm)
|
|
// TODO: should it be marked as DRAINING?
|
|
}
|
|
|
|
- got = snd_pcm_readi(stm->pcm, stm->buffer+stm->bufframes, avail);
|
|
+ got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer+stm->bufframes, avail);
|
|
|
|
if (got < 0) {
|
|
avail = got; // the error handler below will recover us
|
|
@@ -345,7 +394,7 @@ alsa_process_stream(cubeb_stream * stm)
|
|
(!stm->other_stream || stm->other_stream->bufframes > 0)) {
|
|
long got = avail - stm->bufframes;
|
|
void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL;
|
|
- char * buftail = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes);
|
|
+ char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
|
|
|
|
/* Correct read size to the other stream available frames */
|
|
if (stm->other_stream && got > (snd_pcm_sframes_t) stm->other_stream->bufframes) {
|
|
@@ -372,8 +421,8 @@ alsa_process_stream(cubeb_stream * stm)
|
|
long drain_frames = avail - stm->bufframes;
|
|
double drain_time = (double) drain_frames / stm->params.rate;
|
|
|
|
- char * buftail = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes);
|
|
- memset(buftail, 0, snd_pcm_frames_to_bytes(stm->pcm, drain_frames));
|
|
+ char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
|
|
+ memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames));
|
|
stm->bufframes = avail;
|
|
|
|
/* Mark as draining, unless we're waiting for capture */
|
|
@@ -400,7 +449,7 @@ alsa_process_stream(cubeb_stream * stm)
|
|
}
|
|
}
|
|
|
|
- wrote = snd_pcm_writei(stm->pcm, stm->buffer, avail);
|
|
+ wrote = WRAP(snd_pcm_writei)(stm->pcm, stm->buffer, avail);
|
|
if (wrote < 0) {
|
|
avail = wrote; // the error handler below will recover us
|
|
} else {
|
|
@@ -413,13 +462,13 @@ alsa_process_stream(cubeb_stream * stm)
|
|
|
|
/* Got some error? Let's try to recover the stream. */
|
|
if (avail < 0) {
|
|
- avail = snd_pcm_recover(stm->pcm, avail, 0);
|
|
+ avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0);
|
|
|
|
/* Capture pcm must be started after initial setup/recover */
|
|
if (avail >= 0 &&
|
|
stm->stream_type == SND_PCM_STREAM_CAPTURE &&
|
|
- snd_pcm_state(stm->pcm) == SND_PCM_STATE_PREPARED) {
|
|
- avail = snd_pcm_start(stm->pcm);
|
|
+ WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
|
|
+ avail = WRAP(snd_pcm_start)(stm->pcm);
|
|
}
|
|
}
|
|
|
|
@@ -535,26 +584,26 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
|
|
|
|
slave_def = NULL;
|
|
|
|
- r = snd_config_search(root_pcm, "slave", &slave_pcm);
|
|
+ r = WRAP(snd_config_search)(root_pcm, "slave", &slave_pcm);
|
|
if (r < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
- r = snd_config_get_string(slave_pcm, &string);
|
|
+ r = WRAP(snd_config_get_string)(slave_pcm, &string);
|
|
if (r >= 0) {
|
|
- r = snd_config_search_definition(lconf, "pcm_slave", string, &slave_def);
|
|
+ r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string, &slave_def);
|
|
if (r < 0) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
do {
|
|
- r = snd_config_search(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
|
|
+ r = WRAP(snd_config_search)(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
|
|
if (r < 0) {
|
|
break;
|
|
}
|
|
|
|
- r = snd_config_get_string(slave_def ? slave_def : slave_pcm, &string);
|
|
+ r = WRAP(snd_config_get_string)(slave_def ? slave_def : slave_pcm, &string);
|
|
if (r < 0) {
|
|
break;
|
|
}
|
|
@@ -563,7 +612,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
|
|
if (r < 0 || r > (int) sizeof(node_name)) {
|
|
break;
|
|
}
|
|
- r = snd_config_search(lconf, node_name, &pcm);
|
|
+ r = WRAP(snd_config_search)(lconf, node_name, &pcm);
|
|
if (r < 0) {
|
|
break;
|
|
}
|
|
@@ -572,7 +621,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
|
|
} while (0);
|
|
|
|
if (slave_def) {
|
|
- snd_config_delete(slave_def);
|
|
+ WRAP(snd_config_delete)(slave_def);
|
|
}
|
|
|
|
return NULL;
|
|
@@ -595,22 +644,22 @@ init_local_config_with_workaround(char const * pcm_name)
|
|
|
|
lconf = NULL;
|
|
|
|
- if (snd_config == NULL) {
|
|
+ if (*WRAP(snd_config) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
- r = snd_config_copy(&lconf, snd_config);
|
|
+ r = WRAP(snd_config_copy)(&lconf, *WRAP(snd_config));
|
|
if (r < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
do {
|
|
- r = snd_config_search_definition(lconf, "pcm", pcm_name, &pcm_node);
|
|
+ r = WRAP(snd_config_search_definition)(lconf, "pcm", pcm_name, &pcm_node);
|
|
if (r < 0) {
|
|
break;
|
|
}
|
|
|
|
- r = snd_config_get_id(pcm_node, &string);
|
|
+ r = WRAP(snd_config_get_id)(pcm_node, &string);
|
|
if (r < 0) {
|
|
break;
|
|
}
|
|
@@ -619,7 +668,7 @@ init_local_config_with_workaround(char const * pcm_name)
|
|
if (r < 0 || r > (int) sizeof(node_name)) {
|
|
break;
|
|
}
|
|
- r = snd_config_search(lconf, node_name, &pcm_node);
|
|
+ r = WRAP(snd_config_search)(lconf, node_name, &pcm_node);
|
|
if (r < 0) {
|
|
break;
|
|
}
|
|
@@ -630,12 +679,12 @@ init_local_config_with_workaround(char const * pcm_name)
|
|
}
|
|
|
|
/* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */
|
|
- r = snd_config_search(pcm_node, "type", &node);
|
|
+ r = WRAP(snd_config_search)(pcm_node, "type", &node);
|
|
if (r < 0) {
|
|
break;
|
|
}
|
|
|
|
- r = snd_config_get_string(node, &string);
|
|
+ r = WRAP(snd_config_get_string)(node, &string);
|
|
if (r < 0) {
|
|
break;
|
|
}
|
|
@@ -646,18 +695,18 @@ init_local_config_with_workaround(char const * pcm_name)
|
|
|
|
/* Don't clobber an explicit existing handle_underrun value, set it only
|
|
if it doesn't already exist. */
|
|
- r = snd_config_search(pcm_node, "handle_underrun", &node);
|
|
+ r = WRAP(snd_config_search)(pcm_node, "handle_underrun", &node);
|
|
if (r != -ENOENT) {
|
|
break;
|
|
}
|
|
|
|
/* Disable pcm_pulse's asynchronous underrun handling. */
|
|
- r = snd_config_imake_integer(&node, "handle_underrun", 0);
|
|
+ r = WRAP(snd_config_imake_integer)(&node, "handle_underrun", 0);
|
|
if (r < 0) {
|
|
break;
|
|
}
|
|
|
|
- r = snd_config_add(pcm_node, node);
|
|
+ r = WRAP(snd_config_add)(pcm_node, node);
|
|
if (r < 0) {
|
|
break;
|
|
}
|
|
@@ -665,7 +714,7 @@ init_local_config_with_workaround(char const * pcm_name)
|
|
return lconf;
|
|
} while (0);
|
|
|
|
- snd_config_delete(lconf);
|
|
+ WRAP(snd_config_delete)(lconf);
|
|
|
|
return NULL;
|
|
}
|
|
@@ -677,9 +726,9 @@ alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name, snd_pcm_stream_t s
|
|
|
|
pthread_mutex_lock(&cubeb_alsa_mutex);
|
|
if (local_config) {
|
|
- r = snd_pcm_open_lconf(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config);
|
|
+ r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config);
|
|
} else {
|
|
- r = snd_pcm_open(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
|
|
+ r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
|
|
}
|
|
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
|
|
|
@@ -692,7 +741,7 @@ alsa_locked_pcm_close(snd_pcm_t * pcm)
|
|
int r;
|
|
|
|
pthread_mutex_lock(&cubeb_alsa_mutex);
|
|
- r = snd_pcm_close(pcm);
|
|
+ r = WRAP(snd_pcm_close)(pcm);
|
|
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
|
|
|
return r;
|
|
@@ -755,12 +804,65 @@ alsa_init(cubeb ** context, char const * context_name)
|
|
pthread_attr_t attr;
|
|
snd_pcm_t * dummy;
|
|
|
|
+ void * libasound = NULL;
|
|
+
|
|
+#ifndef DISABLE_LIBASOUND_DLOPEN
|
|
+ libasound = dlopen("libasound.so", RTLD_LAZY);
|
|
+ if (!libasound) {
|
|
+ return CUBEB_ERROR;
|
|
+ }
|
|
+
|
|
+#define LOAD(x) do { \
|
|
+ cubeb_##x = dlsym(libasound, #x); \
|
|
+ if (!cubeb_##x) { \
|
|
+ dlclose(libasound); \
|
|
+ return CUBEB_ERROR; \
|
|
+ } \
|
|
+ } while(0)
|
|
+
|
|
+ LOAD(snd_config);
|
|
+ LOAD(snd_config_add);
|
|
+ LOAD(snd_config_copy);
|
|
+ LOAD(snd_config_delete);
|
|
+ LOAD(snd_config_get_id);
|
|
+ LOAD(snd_config_get_string);
|
|
+ LOAD(snd_config_imake_integer);
|
|
+ LOAD(snd_config_search);
|
|
+ LOAD(snd_config_search_definition);
|
|
+ LOAD(snd_lib_error_set_handler);
|
|
+ LOAD(snd_pcm_avail_update);
|
|
+ LOAD(snd_pcm_close);
|
|
+ LOAD(snd_pcm_delay);
|
|
+ LOAD(snd_pcm_drain);
|
|
+ LOAD(snd_pcm_frames_to_bytes);
|
|
+ LOAD(snd_pcm_get_params);
|
|
+ /* snd_pcm_hw_params_alloca is actually a macro */
|
|
+ /* LOAD(snd_pcm_hw_params_alloca); */
|
|
+ LOAD(snd_pcm_hw_params_sizeof);
|
|
+ LOAD(snd_pcm_hw_params_any);
|
|
+ LOAD(snd_pcm_hw_params_get_channels_max);
|
|
+ LOAD(snd_pcm_hw_params_get_rate);
|
|
+ LOAD(snd_pcm_hw_params_set_rate_near);
|
|
+ LOAD(snd_pcm_nonblock);
|
|
+ LOAD(snd_pcm_open);
|
|
+ LOAD(snd_pcm_open_lconf);
|
|
+ LOAD(snd_pcm_pause);
|
|
+ LOAD(snd_pcm_poll_descriptors);
|
|
+ LOAD(snd_pcm_poll_descriptors_count);
|
|
+ LOAD(snd_pcm_poll_descriptors_revents);
|
|
+ LOAD(snd_pcm_recover);
|
|
+ LOAD(snd_pcm_set_params);
|
|
+ LOAD(snd_pcm_state);
|
|
+ LOAD(snd_pcm_writei);
|
|
+
|
|
+#undef LOAD
|
|
+#endif
|
|
assert(context);
|
|
*context = NULL;
|
|
|
|
pthread_mutex_lock(&cubeb_alsa_mutex);
|
|
if (!cubeb_alsa_error_handler_set) {
|
|
- snd_lib_error_set_handler(silent_error_handler);
|
|
+ WRAP(snd_lib_error_set_handler)(silent_error_handler);
|
|
cubeb_alsa_error_handler_set = 1;
|
|
}
|
|
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
|
@@ -768,6 +870,8 @@ alsa_init(cubeb ** context, char const * context_name)
|
|
ctx = calloc(1, sizeof(*ctx));
|
|
assert(ctx);
|
|
|
|
+ ctx->libasound = libasound;
|
|
+
|
|
ctx->ops = &alsa_ops;
|
|
|
|
r = pthread_mutex_init(&ctx->mutex, NULL);
|
|
@@ -817,7 +921,7 @@ alsa_init(cubeb ** context, char const * context_name)
|
|
config fails with EINVAL, the PA PCM is too old for this workaround. */
|
|
if (r == -EINVAL) {
|
|
pthread_mutex_lock(&cubeb_alsa_mutex);
|
|
- snd_config_delete(ctx->local_config);
|
|
+ WRAP(snd_config_delete)(ctx->local_config);
|
|
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
|
ctx->local_config = NULL;
|
|
} else if (r >= 0) {
|
|
@@ -857,9 +961,13 @@ alsa_destroy(cubeb * ctx)
|
|
pthread_mutex_destroy(&ctx->mutex);
|
|
free(ctx->fds);
|
|
|
|
+ if (ctx->libasound) {
|
|
+ dlclose(ctx->libasound);
|
|
+ }
|
|
+
|
|
if (ctx->local_config) {
|
|
pthread_mutex_lock(&cubeb_alsa_mutex);
|
|
- snd_config_delete(ctx->local_config);
|
|
+ WRAP(snd_config_delete)(ctx->local_config);
|
|
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
|
}
|
|
|
|
@@ -939,7 +1047,7 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
|
return CUBEB_ERROR;
|
|
}
|
|
|
|
- r = snd_pcm_nonblock(stm->pcm, 1);
|
|
+ r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
|
|
assert(r == 0);
|
|
|
|
latency_us = latency_frames * 1e6 / stm->params.rate;
|
|
@@ -952,7 +1060,7 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
|
latency_us = latency_us < min_latency ? min_latency: latency_us;
|
|
}
|
|
|
|
- r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
|
|
+ r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
|
|
stm->params.channels, stm->params.rate, 1,
|
|
latency_us);
|
|
if (r < 0) {
|
|
@@ -960,20 +1068,20 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
|
return CUBEB_ERROR_INVALID_FORMAT;
|
|
}
|
|
|
|
- r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &period_size);
|
|
+ r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size);
|
|
assert(r == 0);
|
|
|
|
/* Double internal buffer size to have enough space when waiting for the other side of duplex connection */
|
|
stm->buffer_size *= 2;
|
|
- stm->buffer = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, stm->buffer_size));
|
|
+ stm->buffer = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size));
|
|
assert(stm->buffer);
|
|
|
|
- stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm);
|
|
+ stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm);
|
|
assert(stm->nfds > 0);
|
|
|
|
stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
|
|
assert(stm->saved_fds);
|
|
- r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds);
|
|
+ r = WRAP(snd_pcm_poll_descriptors)(stm->pcm, stm->saved_fds, stm->nfds);
|
|
assert((nfds_t) r == stm->nfds);
|
|
|
|
if (alsa_register_stream(ctx, stm) != 0) {
|
|
@@ -1048,7 +1156,7 @@ alsa_stream_destroy(cubeb_stream * stm)
|
|
pthread_mutex_lock(&stm->mutex);
|
|
if (stm->pcm) {
|
|
if (stm->state == DRAINING) {
|
|
- snd_pcm_drain(stm->pcm);
|
|
+ WRAP(snd_pcm_drain)(stm->pcm);
|
|
}
|
|
alsa_locked_pcm_close(stm->pcm);
|
|
stm->pcm = NULL;
|
|
@@ -1094,12 +1202,12 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
|
|
|
assert(stm);
|
|
|
|
- r = snd_pcm_hw_params_any(stm->pcm, hw_params);
|
|
+ r = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
|
|
if (r < 0) {
|
|
return CUBEB_ERROR;
|
|
}
|
|
|
|
- r = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
|
|
+ r = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
|
|
if (r < 0) {
|
|
return CUBEB_ERROR;
|
|
}
|
|
@@ -1120,34 +1228,34 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
|
|
|
|
/* get a pcm, disabling resampling, so we get a rate the
|
|
* hardware/dmix/pulse/etc. supports. */
|
|
- r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE);
|
|
+ r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE);
|
|
if (r < 0) {
|
|
return CUBEB_ERROR;
|
|
}
|
|
|
|
- r = snd_pcm_hw_params_any(pcm, hw_params);
|
|
+ r = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
|
|
if (r < 0) {
|
|
- snd_pcm_close(pcm);
|
|
+ WRAP(snd_pcm_close)(pcm);
|
|
return CUBEB_ERROR;
|
|
}
|
|
|
|
- r = snd_pcm_hw_params_get_rate(hw_params, rate, &dir);
|
|
+ r = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
|
|
if (r >= 0) {
|
|
/* There is a default rate: use it. */
|
|
- snd_pcm_close(pcm);
|
|
+ WRAP(snd_pcm_close)(pcm);
|
|
return CUBEB_OK;
|
|
}
|
|
|
|
/* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
|
|
*rate = 44100;
|
|
|
|
- r = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL);
|
|
+ r = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
|
|
if (r < 0) {
|
|
- snd_pcm_close(pcm);
|
|
+ WRAP(snd_pcm_close)(pcm);
|
|
return CUBEB_ERROR;
|
|
}
|
|
|
|
- snd_pcm_close(pcm);
|
|
+ WRAP(snd_pcm_close)(pcm);
|
|
|
|
return CUBEB_OK;
|
|
}
|
|
@@ -1180,10 +1288,10 @@ alsa_stream_start(cubeb_stream * stm)
|
|
pthread_mutex_lock(&stm->mutex);
|
|
/* Capture pcm must be started after initial setup/recover */
|
|
if (stm->stream_type == SND_PCM_STREAM_CAPTURE &&
|
|
- snd_pcm_state(stm->pcm) == SND_PCM_STATE_PREPARED) {
|
|
- snd_pcm_start(stm->pcm);
|
|
+ WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
|
|
+ WRAP(snd_pcm_start)(stm->pcm);
|
|
}
|
|
- snd_pcm_pause(stm->pcm, 0);
|
|
+ WRAP(snd_pcm_pause)(stm->pcm, 0);
|
|
gettimeofday(&stm->last_activity, NULL);
|
|
pthread_mutex_unlock(&stm->mutex);
|
|
|
|
@@ -1223,7 +1331,7 @@ alsa_stream_stop(cubeb_stream * stm)
|
|
pthread_mutex_unlock(&ctx->mutex);
|
|
|
|
pthread_mutex_lock(&stm->mutex);
|
|
- snd_pcm_pause(stm->pcm, 1);
|
|
+ WRAP(snd_pcm_pause)(stm->pcm, 1);
|
|
pthread_mutex_unlock(&stm->mutex);
|
|
|
|
return CUBEB_OK;
|
|
@@ -1239,8 +1347,8 @@ alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
|
pthread_mutex_lock(&stm->mutex);
|
|
|
|
delay = -1;
|
|
- if (snd_pcm_state(stm->pcm) != SND_PCM_STATE_RUNNING ||
|
|
- snd_pcm_delay(stm->pcm, &delay) != 0) {
|
|
+ if (WRAP(snd_pcm_state)(stm->pcm) != SND_PCM_STATE_RUNNING ||
|
|
+ WRAP(snd_pcm_delay)(stm->pcm, &delay) != 0) {
|
|
*position = stm->last_position;
|
|
pthread_mutex_unlock(&stm->mutex);
|
|
return CUBEB_OK;
|
|
@@ -1265,7 +1373,7 @@ alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
|
snd_pcm_sframes_t delay;
|
|
/* This function returns the delay in frames until a frame written using
|
|
snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */
|
|
- if (snd_pcm_delay(stm->pcm, &delay)) {
|
|
+ if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
|
|
return CUBEB_ERROR;
|
|
}
|
|
|
|
diff --git media/libcubeb/src/cubeb_oss.c media/libcubeb/src/cubeb_oss.c
|
|
new file mode 100644
|
|
index 000000000000..7b0b4f37dbe4
|
|
--- /dev/null
|
|
+++ media/libcubeb/src/cubeb_oss.c
|
|
@@ -0,0 +1,445 @@
|
|
+/*
|
|
+ * Copyright © 2014 Mozilla Foundation
|
|
+ *
|
|
+ * This program is made available under an ISC-style license. See the
|
|
+ * accompanying file LICENSE for details.
|
|
+ */
|
|
+#if defined(HAVE_SYS_SOUNDCARD_H)
|
|
+#include <sys/soundcard.h>
|
|
+#else
|
|
+#include <soundcard.h>
|
|
+#endif
|
|
+#include <unistd.h>
|
|
+#include <stdlib.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+#include <sys/ioctl.h>
|
|
+#include <errno.h>
|
|
+#include <pthread.h>
|
|
+#include <stdio.h>
|
|
+#include <assert.h>
|
|
+
|
|
+#include "cubeb/cubeb.h"
|
|
+#include "cubeb-internal.h"
|
|
+
|
|
+#ifndef CUBEB_OSS_DEFAULT_OUTPUT
|
|
+#define CUBEB_OSS_DEFAULT_OUTPUT "/dev/dsp"
|
|
+#endif
|
|
+
|
|
+#define OSS_BUFFER_SIZE 1024
|
|
+
|
|
+struct cubeb {
|
|
+ struct cubeb_ops const * ops;
|
|
+};
|
|
+
|
|
+struct cubeb_stream {
|
|
+ cubeb * context;
|
|
+
|
|
+ cubeb_data_callback data_callback;
|
|
+ cubeb_state_callback state_callback;
|
|
+ void * user_ptr;
|
|
+ float volume;
|
|
+ float panning;
|
|
+
|
|
+ pthread_mutex_t state_mutex;
|
|
+ pthread_cond_t state_cond;
|
|
+
|
|
+ int running;
|
|
+ int stopped;
|
|
+ int floating;
|
|
+
|
|
+ /* These two vars are needed to support old versions of OSS */
|
|
+ unsigned int position_bytes;
|
|
+ unsigned int last_position_bytes;
|
|
+
|
|
+ uint64_t written_frags; /* The number of fragments written to /dev/dsp */
|
|
+ uint64_t missed_frags; /* fragments output with stopped stream */
|
|
+
|
|
+ cubeb_stream_params params;
|
|
+ int fd;
|
|
+ pthread_t th;
|
|
+};
|
|
+
|
|
+static struct cubeb_ops const oss_ops;
|
|
+
|
|
+int oss_init(cubeb ** context, char const * context_name)
|
|
+{
|
|
+ cubeb* ctx = (cubeb*)malloc(sizeof(cubeb));
|
|
+ ctx->ops = &oss_ops;
|
|
+ *context = ctx;
|
|
+ return CUBEB_OK;
|
|
+}
|
|
+
|
|
+static void oss_destroy(cubeb *ctx)
|
|
+{
|
|
+ free(ctx);
|
|
+}
|
|
+
|
|
+static char const * oss_get_backend_id(cubeb * context)
|
|
+{
|
|
+ static char oss_name[] = "oss";
|
|
+ return oss_name;
|
|
+}
|
|
+
|
|
+static int oss_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
|
+{
|
|
+ *max_channels = 2; /* Let's support only stereo for now */
|
|
+ return CUBEB_OK;
|
|
+}
|
|
+
|
|
+static int oss_get_min_latency(cubeb * context, cubeb_stream_params params,
|
|
+ uint32_t * latency_frames)
|
|
+{
|
|
+ (void)context;
|
|
+ /* 40ms is a big enough number to work ok */
|
|
+ *latency_frames = 40 * params.rate / 1000;
|
|
+ return CUBEB_OK;
|
|
+}
|
|
+
|
|
+static int oss_get_preferred_sample_rate(cubeb *context, uint32_t * rate)
|
|
+{
|
|
+ /* 48000 seems a prefered choice for most audio devices
|
|
+ * and a good choice for OSS */
|
|
+ *rate = 48000;
|
|
+ return CUBEB_OK;
|
|
+}
|
|
+
|
|
+static void run_state_callback(cubeb_stream *stream, cubeb_state state)
|
|
+{
|
|
+ if (stream->state_callback) {
|
|
+ stream->state_callback(stream, stream->user_ptr, state);
|
|
+ }
|
|
+}
|
|
+
|
|
+static long run_data_callback(cubeb_stream *stream, void *buffer, long nframes)
|
|
+{
|
|
+ long got = 0;
|
|
+ pthread_mutex_lock(&stream->state_mutex);
|
|
+ if (stream->data_callback && stream->running && !stream->stopped) {
|
|
+ pthread_mutex_unlock(&stream->state_mutex);
|
|
+ got = stream->data_callback(stream, stream->user_ptr, NULL, buffer, nframes);
|
|
+ } else {
|
|
+ pthread_mutex_unlock(&stream->state_mutex);
|
|
+ }
|
|
+ return got;
|
|
+}
|
|
+
|
|
+static void apply_volume_int(int16_t* buffer, unsigned int n,
|
|
+ float volume, float panning)
|
|
+{
|
|
+ float left = volume;
|
|
+ float right = volume;
|
|
+ unsigned int i;
|
|
+ int pan[2];
|
|
+ if (panning<0) {
|
|
+ right *= (1+panning);
|
|
+ } else {
|
|
+ left *= (1-panning);
|
|
+ }
|
|
+ pan[0] = 128.0*left;
|
|
+ pan[1] = 128.0*right;
|
|
+ for(i=0; i<n; i++){
|
|
+ buffer[i] = ((int)buffer[i])*pan[i%2]/128;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void apply_volume_float(float* buffer, unsigned int n,
|
|
+ float volume, float panning)
|
|
+{
|
|
+ float left = volume;
|
|
+ float right = volume;
|
|
+ unsigned int i;
|
|
+ float pan[2];
|
|
+ if (panning<0) {
|
|
+ right *= (1+panning);
|
|
+ } else {
|
|
+ left *= (1-panning);
|
|
+ }
|
|
+ pan[0] = left;
|
|
+ pan[1] = right;
|
|
+ for(i=0; i<n; i++){
|
|
+ buffer[i] = buffer[i]*pan[i%2];
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static void *writer(void *stm)
|
|
+{
|
|
+ cubeb_stream* stream = (cubeb_stream*)stm;
|
|
+ int16_t buffer[OSS_BUFFER_SIZE];
|
|
+ float f_buffer[OSS_BUFFER_SIZE];
|
|
+ int got;
|
|
+ unsigned long i;
|
|
+ while (stream->running) {
|
|
+ pthread_mutex_lock(&stream->state_mutex);
|
|
+ if (stream->stopped) {
|
|
+ pthread_mutex_unlock(&stream->state_mutex);
|
|
+ run_state_callback(stream, CUBEB_STATE_STOPPED);
|
|
+ pthread_mutex_lock(&stream->state_mutex);
|
|
+ while (stream->stopped) {
|
|
+ pthread_cond_wait(&stream->state_cond, &stream->state_mutex);
|
|
+ }
|
|
+ pthread_mutex_unlock(&stream->state_mutex);
|
|
+ run_state_callback(stream, CUBEB_STATE_STARTED);
|
|
+ continue;
|
|
+ }
|
|
+ pthread_mutex_unlock(&stream->state_mutex);
|
|
+ if (stream->floating) {
|
|
+ got = run_data_callback(stream, f_buffer,
|
|
+ OSS_BUFFER_SIZE/stream->params.channels);
|
|
+ apply_volume_float(f_buffer, got*stream->params.channels,
|
|
+ stream->volume, stream->panning);
|
|
+ for (i=0; i<((unsigned long)got)*stream->params.channels; i++) {
|
|
+ /* Clipping is prefered to overflow */
|
|
+ if(f_buffer[i]>=1.0){
|
|
+ f_buffer[i]=1.0;
|
|
+ }
|
|
+ if(f_buffer[i]<=-1.0){
|
|
+ f_buffer[i]=-1.0;
|
|
+ }
|
|
+ /* One might think that multipling by 32767.0 is logical but results in clipping */
|
|
+ buffer[i] = f_buffer[i]*32767.0;
|
|
+ }
|
|
+ } else {
|
|
+ got = run_data_callback(stream, buffer,
|
|
+ OSS_BUFFER_SIZE/stream->params.channels);
|
|
+ apply_volume_int(buffer, got*stream->params.channels,
|
|
+ stream->volume, stream->panning);
|
|
+ }
|
|
+ if (got<0) {
|
|
+ run_state_callback(stream, CUBEB_STATE_ERROR);
|
|
+ break;
|
|
+ }
|
|
+ if (!got) {
|
|
+ run_state_callback(stream, CUBEB_STATE_DRAINED);
|
|
+ }
|
|
+ if (got) {
|
|
+ size_t i = 0;
|
|
+ size_t s = got*stream->params.channels*sizeof(int16_t);
|
|
+ while (i < s) {
|
|
+ ssize_t n = write(stream->fd, ((char*)buffer) + i, s - i);
|
|
+ if (n<=0) {
|
|
+ run_state_callback(stream, CUBEB_STATE_ERROR);
|
|
+ break;
|
|
+ }
|
|
+ i+=n;
|
|
+ }
|
|
+ stream->written_frags+=got;
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void oss_try_set_latency(cubeb_stream* stream, unsigned int latency)
|
|
+{
|
|
+ unsigned int latency_bytes, n_frag;
|
|
+ int frag;
|
|
+ /* fragment size of 1024 is a good choice with good chances to be accepted */
|
|
+ unsigned int frag_log=10; /* 2^frag_log = fragment size */
|
|
+ latency_bytes =
|
|
+ latency*stream->params.rate*stream->params.channels*sizeof(uint16_t)/1000;
|
|
+ n_frag = latency_bytes>>frag_log;
|
|
+ frag = (n_frag<<16) | frag_log;
|
|
+ /* Even if this fails we wish to continue, not checking for errors */
|
|
+ ioctl(stream->fd, SNDCTL_DSP_SETFRAGMENT, &frag);
|
|
+}
|
|
+
|
|
+static int oss_stream_init(cubeb * context, cubeb_stream ** stm,
|
|
+ char const * stream_name,
|
|
+ cubeb_devid input_device,
|
|
+ cubeb_stream_params * input_stream_params,
|
|
+ cubeb_devid output_device,
|
|
+ cubeb_stream_params * output_stream_params,
|
|
+ unsigned int latency,
|
|
+ cubeb_data_callback data_callback,
|
|
+ cubeb_state_callback state_callback, void * user_ptr)
|
|
+{
|
|
+ cubeb_stream* stream = (cubeb_stream*)malloc(sizeof(cubeb_stream));
|
|
+ stream->context = context;
|
|
+ stream->data_callback = data_callback;
|
|
+ stream->state_callback = state_callback;
|
|
+ stream->user_ptr = user_ptr;
|
|
+
|
|
+ assert(!input_stream_params && "not supported.");
|
|
+ if (input_device || output_device) {
|
|
+ /* Device selection not yet implemented. */
|
|
+ return CUBEB_ERROR_DEVICE_UNAVAILABLE;
|
|
+ }
|
|
+
|
|
+ if ((stream->fd = open(CUBEB_OSS_DEFAULT_OUTPUT, O_WRONLY)) == -1) {
|
|
+ free(stream);
|
|
+ return CUBEB_ERROR;
|
|
+ }
|
|
+#define SET(what, to) do { unsigned int i = to; \
|
|
+ int j = ioctl(stream->fd, what, &i); \
|
|
+ if (j == -1 || i != to) { \
|
|
+ close(stream->fd); \
|
|
+ free(stream); \
|
|
+ return CUBEB_ERROR_INVALID_FORMAT; } } while (0)
|
|
+
|
|
+ stream->params = *output_stream_params;
|
|
+ stream->volume = 1.0;
|
|
+ stream->panning = 0.0;
|
|
+
|
|
+ oss_try_set_latency(stream, latency);
|
|
+
|
|
+ stream->floating = 0;
|
|
+ SET(SNDCTL_DSP_CHANNELS, stream->params.channels);
|
|
+ SET(SNDCTL_DSP_SPEED, stream->params.rate);
|
|
+ switch (stream->params.format) {
|
|
+ case CUBEB_SAMPLE_S16LE:
|
|
+ SET(SNDCTL_DSP_SETFMT, AFMT_S16_LE);
|
|
+ break;
|
|
+ case CUBEB_SAMPLE_S16BE:
|
|
+ SET(SNDCTL_DSP_SETFMT, AFMT_S16_BE);
|
|
+ break;
|
|
+ case CUBEB_SAMPLE_FLOAT32LE:
|
|
+ SET(SNDCTL_DSP_SETFMT, AFMT_S16_NE);
|
|
+ stream->floating = 1;
|
|
+ break;
|
|
+ default:
|
|
+ close(stream->fd);
|
|
+ free(stream);
|
|
+ return CUBEB_ERROR;
|
|
+ }
|
|
+
|
|
+
|
|
+ pthread_mutex_init(&stream->state_mutex, NULL);
|
|
+ pthread_cond_init(&stream->state_cond, NULL);
|
|
+
|
|
+ stream->running = 1;
|
|
+ stream->stopped = 1;
|
|
+ stream->position_bytes = 0;
|
|
+ stream->last_position_bytes = 0;
|
|
+ stream->written_frags = 0;
|
|
+ stream->missed_frags = 0;
|
|
+
|
|
+ pthread_create(&stream->th, NULL, writer, (void*)stream);
|
|
+
|
|
+ *stm = stream;
|
|
+
|
|
+ return CUBEB_OK;
|
|
+}
|
|
+
|
|
+static void oss_stream_destroy(cubeb_stream * stream)
|
|
+{
|
|
+ pthread_mutex_lock(&stream->state_mutex);
|
|
+
|
|
+ stream->running = 0;
|
|
+ stream->stopped = 0;
|
|
+ pthread_cond_signal(&stream->state_cond);
|
|
+
|
|
+ pthread_mutex_unlock(&stream->state_mutex);
|
|
+
|
|
+ pthread_join(stream->th, NULL);
|
|
+
|
|
+ pthread_mutex_destroy(&stream->state_mutex);
|
|
+ pthread_cond_destroy(&stream->state_cond);
|
|
+ close(stream->fd);
|
|
+ free(stream);
|
|
+}
|
|
+
|
|
+static int oss_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
|
|
+{
|
|
+ if (ioctl(stream->fd, SNDCTL_DSP_GETODELAY, latency)==-1) {
|
|
+ return CUBEB_ERROR;
|
|
+ }
|
|
+ /* Convert latency from bytes to frames */
|
|
+ *latency /= stream->params.channels*sizeof(int16_t);
|
|
+ return CUBEB_OK;
|
|
+}
|
|
+
|
|
+
|
|
+static int oss_stream_current_optr(cubeb_stream * stream, uint64_t * position)
|
|
+{
|
|
+ count_info ci;
|
|
+ /* Unfortunately, this ioctl is only available in OSS 4.x */
|
|
+#ifdef SNDCTL_DSP_CURRENT_OPTR
|
|
+ oss_count_t count;
|
|
+ if (ioctl(stream->fd, SNDCTL_DSP_CURRENT_OPTR, &count) != -1) {
|
|
+ *position = count.samples;// + count.fifo_samples;
|
|
+ return CUBEB_OK;
|
|
+ }
|
|
+#endif
|
|
+ /* Fall back to this ioctl in case the previous one fails */
|
|
+ if (ioctl(stream->fd, SNDCTL_DSP_GETOPTR, &ci) == -1) {
|
|
+ return CUBEB_ERROR;
|
|
+ }
|
|
+ /* ci.bytes is only 32 bit and will start to wrap after arithmetic overflow */
|
|
+ stream->position_bytes += ci.bytes - stream->last_position_bytes;
|
|
+ stream->last_position_bytes = ci.bytes;
|
|
+ *position = stream->position_bytes/stream->params.channels/sizeof(int16_t);
|
|
+ return CUBEB_OK;
|
|
+}
|
|
+
|
|
+static int oss_stream_get_position(cubeb_stream * stream, uint64_t * position)
|
|
+{
|
|
+ if ( oss_stream_current_optr(stream, position) == CUBEB_OK ){
|
|
+ *position -= stream->missed_frags;
|
|
+ return CUBEB_OK;
|
|
+ }
|
|
+ /* If no correct method to get position works we resort to this */
|
|
+ *position = stream->written_frags;
|
|
+ return CUBEB_OK;
|
|
+}
|
|
+
|
|
+
|
|
+static int oss_stream_start(cubeb_stream * stream)
|
|
+{
|
|
+ pthread_mutex_lock(&stream->state_mutex);
|
|
+ if (stream->stopped) {
|
|
+ uint64_t ptr;
|
|
+ oss_stream_current_optr(stream, &ptr);
|
|
+ stream->missed_frags = ptr - stream->written_frags;
|
|
+ stream->stopped = 0;
|
|
+ pthread_cond_signal(&stream->state_cond);
|
|
+ }
|
|
+ pthread_mutex_unlock(&stream->state_mutex);
|
|
+ return CUBEB_OK;
|
|
+}
|
|
+
|
|
+static int oss_stream_stop(cubeb_stream * stream)
|
|
+{
|
|
+ pthread_mutex_lock(&stream->state_mutex);
|
|
+ stream->stopped = 1;
|
|
+ pthread_mutex_unlock(&stream->state_mutex);
|
|
+ return CUBEB_OK;
|
|
+}
|
|
+
|
|
+int oss_stream_set_panning(cubeb_stream * stream, float panning)
|
|
+{
|
|
+ if (stream->params.channels == 2) {
|
|
+ stream->panning=panning;
|
|
+ }
|
|
+ return CUBEB_OK;
|
|
+}
|
|
+
|
|
+int oss_stream_set_volume(cubeb_stream * stream, float volume)
|
|
+{
|
|
+ stream->volume=volume;
|
|
+ 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,
|
|
+ .get_preferred_channel_layout = NULL,
|
|
+ .destroy = oss_destroy,
|
|
+ .enumerate_devices = NULL,
|
|
+ .stream_init = oss_stream_init,
|
|
+ .stream_destroy = oss_stream_destroy,
|
|
+ .stream_start = oss_stream_start,
|
|
+ .stream_stop = oss_stream_stop,
|
|
+ .stream_get_position = oss_stream_get_position,
|
|
+ .stream_get_latency = oss_stream_get_latency,
|
|
+ .stream_set_volume = oss_stream_set_volume,
|
|
+ .stream_set_panning = oss_stream_set_panning,
|
|
+ .stream_get_current_device = NULL,
|
|
+ .stream_device_destroy = NULL,
|
|
+ .stream_register_device_changed_callback = NULL,
|
|
+ .register_device_collection_changed = NULL
|
|
+};
|
|
diff --git media/libcubeb/src/moz.build media/libcubeb/src/moz.build
|
|
index fc9c79198e30..57d039d5a0f2 100644
|
|
--- media/libcubeb/src/moz.build
|
|
+++ media/libcubeb/src/moz.build
|
|
@@ -20,6 +20,12 @@ if CONFIG['MOZ_ALSA']:
|
|
]
|
|
DEFINES['USE_ALSA'] = True
|
|
|
|
+if CONFIG['MOZ_OSS']:
|
|
+ SOURCES += [
|
|
+ 'cubeb_oss.c',
|
|
+ ]
|
|
+ DEFINES['USE_OSS'] = True
|
|
+
|
|
if CONFIG['MOZ_PULSEAUDIO'] or CONFIG['MOZ_JACK']:
|
|
SOURCES += [
|
|
'cubeb_resampler.cpp',
|
|
@@ -92,6 +98,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
|
'%' + '%s/system/media/wilhelm/include' % CONFIG['ANDROID_SOURCE'],
|
|
]
|
|
|
|
+CFLAGS += CONFIG['MOZ_OSS_CFLAGS']
|
|
CFLAGS += CONFIG['MOZ_ALSA_CFLAGS']
|
|
CFLAGS += CONFIG['MOZ_PULSEAUDIO_CFLAGS']
|
|
|
|
diff --git media/libcubeb/gtest/moz.build media/libcubeb/gtest/moz.build
|
|
index e99968648554..efbbbf779e11 100644
|
|
--- media/libcubeb/gtest/moz.build
|
|
+++ media/libcubeb/gtest/moz.build
|
|
@@ -71,7 +71,6 @@ elif CONFIG['OS_TARGET'] == 'OpenBSD':
|
|
'sndio',
|
|
]
|
|
else:
|
|
- OS_LIBS += CONFIG['MOZ_ALSA_LIBS']
|
|
OS_LIBS += CONFIG['MOZ_PULSEAUDIO_LIBS']
|
|
|
|
if CONFIG['GNU_CXX']:
|
|
diff --git media/libcubeb/update.sh media/libcubeb/update.sh
|
|
index b139b8f497fd..d1f8a223c6d8 100755
|
|
--- media/libcubeb/update.sh
|
|
+++ media/libcubeb/update.sh
|
|
@@ -20,6 +20,7 @@ cp $1/src/cubeb_log.h src
|
|
cp $1/src/cubeb_mixer.cpp src
|
|
cp $1/src/cubeb_mixer.h src
|
|
cp $1/src/cubeb_opensl.c src
|
|
+cp $1/src/cubeb_oss.c src
|
|
cp $1/src/cubeb_osx_run_loop.h src
|
|
cp $1/src/cubeb_panner.cpp src
|
|
cp $1/src/cubeb_panner.h src
|
|
diff --git toolkit/library/moz.build toolkit/library/moz.build
|
|
index a61c689c83c3..7764df6f8a6b 100644
|
|
--- toolkit/library/moz.build
|
|
+++ toolkit/library/moz.build
|
|
@@ -242,8 +242,8 @@ if CONFIG['MOZ_SYSTEM_LIBVPX']:
|
|
if not CONFIG['MOZ_TREE_PIXMAN']:
|
|
OS_LIBS += CONFIG['MOZ_PIXMAN_LIBS']
|
|
|
|
-if CONFIG['MOZ_ALSA']:
|
|
- OS_LIBS += CONFIG['MOZ_ALSA_LIBS']
|
|
+if CONFIG['MOZ_OSS']:
|
|
+ OS_LIBS += CONFIG['MOZ_OSS_LIBS']
|
|
|
|
if CONFIG['HAVE_CLOCK_MONOTONIC']:
|
|
OS_LIBS += CONFIG['REALTIME_LIBS']
|