[SVN-Commit] r1707 - in trunk: mail/thunderbird/files www/firefox-esr/files www/firefox-nightly/files www/firefox/files www/libxul/files www/seamonkey/files

svn-freebsd-gecko at chruetertee.ch svn-freebsd-gecko at chruetertee.ch
Thu Sep 25 22:14:36 UTC 2014


Author: jbeich
Date: Thu Sep 25 22:14:18 2014
New Revision: 1707

Log:
add OSS audio fallback for HTML5 audio

Added:
   trunk/mail/thunderbird/files/patch-bug1021761
   trunk/www/firefox-esr/files/patch-bug1021761
   trunk/www/firefox-nightly/files/patch-bug1021761
   trunk/www/firefox/files/patch-bug1021761
   trunk/www/libxul/files/patch-bug1021761
   trunk/www/seamonkey/files/patch-bug1021761

Added: trunk/mail/thunderbird/files/patch-bug1021761
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/mail/thunderbird/files/patch-bug1021761	Thu Sep 25 22:14:18 2014	(r1707)
@@ -0,0 +1,1092 @@
+diff --git configure.in configure.in
+index 48e60c0..ec08417 100644
+--- mozilla/configure.in
++++ mozilla/configure.in
+@@ -5438,6 +5438,60 @@ fi
+ 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_DISABLE_BOOL(oss,
++[  --disable-oss           Disable OpenSoundSystem support],
++   MOZ_OSS=,
++   MOZ_OSS=1)
++
++_SAVE_CFLAGS=$CFLAGS
++_SAVE_LIBS=$LIBS
++if test -n "$MOZ_OSS"; then
++    dnl Prefer 4Front implementation
++    AC_MSG_CHECKING([MOZ_OSS_CFLAGS])
++    if test -z "$MOZ_OSS_CFLAGS"; then
++        for oss_conf in /etc/oss.conf /usr/local/etc/oss.conf; do
++            if test -e "$oss_conf"; then
++                . "$oss_conf"
++            fi
++        done
++        if test -d "$OSSLIBDIR"; then
++            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 linux/soundcard.h soundcard.h)
++
++    if test "$ac_cv_header_sys_soundcard_h" != "yes" -a \
++            "$ac_cv_header_linux_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 --disable-ogg --disable-wave --disable-webm.])
++    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="-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 ==================================
+ 
+@@ -5451,12 +5505,23 @@ MOZ_ARG_ENABLE_BOOL(alsa,
+    MOZ_ALSA=1,
+    MOZ_ALSA=)
+ 
++MOZ_ARG_DISABLE_BOOL(alsa-dlopen,
++[  --disable-alsa-dlopen   Disable runtime linking of libasound.so],
++   DISABLE_LIBASOUND_DLOPEN=1,
++   DISABLE_LIBASOUND_DLOPEN=)
++
+ if test -n "$MOZ_ALSA"; then
+     PKG_CHECK_MODULES(MOZ_ALSA, alsa, ,
+          [echo "$MOZ_ALSA_PKG_ERRORS"
+           AC_MSG_ERROR([Need alsa for Ogg, Wave or WebM decoding on Linux.  Disable with --disable-ogg --disable-wave --disable-webm.  (On Ubuntu, you might try installing the package libasound2-dev.)])])
+ fi
+ 
++if test -n "$DISABLE_LIBASOUND_DLOPEN"; then
++    AC_DEFINE(DISABLE_LIBASOUND_DLOPEN)
++else
++    MOZ_ALSA_LIBS=
++fi
++
+ AC_SUBST(MOZ_ALSA)
+ 
+ dnl ========================================================
+diff --git media/libcubeb/AUTHORS media/libcubeb/AUTHORS
+index b441e8a..950d9e5 100644
+--- mozilla/media/libcubeb/AUTHORS
++++ mozilla/media/libcubeb/AUTHORS
+@@ -4,3 +4,4 @@ Michael Wu <mwu at mozilla.com>
+ Paul Adenot <paul at paul.cx>
+ David Richards <drichards at mozilla.com>
+ Sebastien Alaiwan <sebastien.alaiwan at gmail.com>
++Evgeniy Vodolazskiy <waterlaz at gmail.com>
+diff --git media/libcubeb/src/cubeb.c media/libcubeb/src/cubeb.c
+index 9c3adcc..45d765b 100644
+--- mozilla/media/libcubeb/src/cubeb.c
++++ mozilla/media/libcubeb/src/cubeb.c
+@@ -54,6 +54,9 @@ int opensl_init(cubeb ** context, char const * context_name);
+ #if defined(USE_AUDIOTRACK)
+ int audiotrack_init(cubeb ** context, char const * context_name);
+ #endif
++#if defined(USE_OSS)
++int oss_init(cubeb ** context, char const * context_name);
++#endif
+ 
+ int
+ validate_stream_params(cubeb_stream_params stream_params)
+@@ -120,6 +123,9 @@ cubeb_init(cubeb ** context, char const * context_name)
+ #if defined(USE_AUDIOTRACK)
+     audiotrack_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 a962553..1f780f4 100644
+--- mozilla/media/libcubeb/src/cubeb_alsa.c
++++ mozilla/media/libcubeb/src/cubeb_alsa.c
+@@ -11,6 +11,7 @@
+ #include <sys/time.h>
+ #include <assert.h>
+ #include <limits.h>
++#include <dlfcn.h>
+ #include <poll.h>
+ #include <unistd.h>
+ #include <alsa/asoundlib.h>
+@@ -24,6 +25,50 @@
+ 
+ #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_recover);
++MAKE_TYPEDEF(snd_pcm_set_params);
++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),
+@@ -64,6 +109,8 @@ struct cubeb {
+      workaround is not required. */
+   snd_config_t * local_config;
+   int is_pa;
++
++  void * libasound;
+ };
+ 
+ enum stream_state {
+@@ -262,7 +309,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+   pthread_mutex_lock(&stm->mutex);
+ 
+-  r = snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
++  r = WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
+   if (r < 0 || revents != POLLOUT) {
+     /* This should be a stream error; it makes no sense for poll(2) to wake
+        for this stream and then have the stream report that it's not ready.
+@@ -271,10 +318,10 @@ alsa_refill_stream(cubeb_stream * stm)
+     return RUNNING;
+   }
+ 
+-  avail = snd_pcm_avail_update(stm->pcm);
++  avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   if (avail == -EPIPE) {
+-    snd_pcm_recover(stm->pcm, avail, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, avail, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   }
+ 
+   /* Failed to recover from an xrun, this stream must be broken. */
+@@ -293,8 +340,8 @@ alsa_refill_stream(cubeb_stream * stm)
+      available to write.  If avail is still zero here, the stream must be in
+      a funky state, so recover and try again. */
+   if (avail == 0) {
+-    snd_pcm_recover(stm->pcm, -EPIPE, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, -EPIPE, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+     if (avail <= 0) {
+       pthread_mutex_unlock(&stm->mutex);
+       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+@@ -302,7 +349,7 @@ alsa_refill_stream(cubeb_stream * stm)
+     }
+   }
+ 
+-  p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
++  p = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, avail));
+   assert(p);
+ 
+   pthread_mutex_unlock(&stm->mutex);
+@@ -313,10 +360,10 @@ alsa_refill_stream(cubeb_stream * stm)
+     return ERROR;
+   }
+   if (got > 0) {
+-    snd_pcm_sframes_t wrote = snd_pcm_writei(stm->pcm, p, got);
++    snd_pcm_sframes_t wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     if (wrote == -EPIPE) {
+-      snd_pcm_recover(stm->pcm, wrote, 1);
+-      wrote = snd_pcm_writei(stm->pcm, p, got);
++      WRAP(snd_pcm_recover)(stm->pcm, wrote, 1);
++      wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     }
+     assert(wrote >= 0 && wrote == got);
+     stm->write_position += wrote;
+@@ -342,7 +389,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+     /* Fill the remaining buffer with silence to guarantee one full period
+        has been written. */
+-    snd_pcm_writei(stm->pcm, (char *) p + got, avail - got);
++    WRAP(snd_pcm_writei)(stm->pcm, (char *) p + got, avail - got);
+ 
+     set_timeout(&stm->drain_timeout, buffer_time * 1000);
+ 
+@@ -453,26 +500,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;
+     }
+@@ -481,7 +528,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;
+     }
+@@ -490,7 +537,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;
+@@ -513,22 +560,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;
+     }
+@@ -537,7 +584,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;
+     }
+@@ -548,12 +595,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;
+     }
+@@ -564,18 +611,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;
+     }
+@@ -583,7 +630,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;
+ }
+@@ -595,9 +642,9 @@ alsa_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * l
+ 
+   pthread_mutex_lock(&cubeb_alsa_mutex);
+   if (local_config) {
+-    r = snd_pcm_open_lconf(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
++    r = WRAP(snd_pcm_open_lconf)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
+   } else {
+-    r = snd_pcm_open(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
++    r = WRAP(snd_pcm_open)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
+   }
+   pthread_mutex_unlock(&cubeb_alsa_mutex);
+ 
+@@ -610,7 +657,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;
+@@ -667,12 +714,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);
+@@ -680,6 +780,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);
+@@ -729,7 +831,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) {
+@@ -768,9 +870,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);
+   }
+ 
+@@ -838,7 +944,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     return CUBEB_ERROR;
+   }
+ 
+-  r = snd_pcm_nonblock(stm->pcm, 1);
++  r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
+   assert(r == 0);
+ 
+   /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
+@@ -848,23 +954,23 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     latency = latency < 500 ? 500 : latency;
+   }
+ 
+-  r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
+-                         stm->params.channels, stm->params.rate, 1,
+-                         latency * 1000);
++  r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
++                               stm->params.channels, stm->params.rate, 1,
++                               latency * 1000);
+   if (r < 0) {
+     alsa_stream_destroy(stm);
+     return CUBEB_ERROR_INVALID_FORMAT;
+   }
+ 
+-  r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size);
++  r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &stm->period_size);
+   assert(r == 0);
+ 
+-  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);
+ 
+   r = pthread_cond_init(&stm->cond, NULL);
+@@ -937,12 +1043,12 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(stm->pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
++  rv = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+@@ -962,34 +1068,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. */
+-  rv = snd_pcm_open(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
++  rv = WRAP(snd_pcm_open)(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
+   if (rv < 0) {
+-    snd_pcm_close(pcm);
++    WRAP(snd_pcm_close)(pcm);
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_rate(hw_params, rate, &dir);
++  rv = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
+   if (rv >= 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;
+ 
+-  rv = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL);
++  rv = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
+   if (rv < 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;
+ }
+@@ -1013,7 +1119,7 @@ alsa_stream_start(cubeb_stream * stm)
+   ctx = stm->context;
+ 
+   pthread_mutex_lock(&stm->mutex);
+-  snd_pcm_pause(stm->pcm, 0);
++  WRAP(snd_pcm_pause)(stm->pcm, 0);
+   gettimeofday(&stm->last_activity, NULL);
+   pthread_mutex_unlock(&stm->mutex);
+ 
+@@ -1047,7 +1153,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;
+@@ -1063,8 +1169,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;
+@@ -1089,7 +1195,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 0000000..5e38e27
+--- mozilla//dev/null
++++ mozilla/media/libcubeb/src/cubeb_oss.c
+@@ -0,0 +1,399 @@
++/*
++ * 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>
++#elif defined(HAVE_LINUX_SOUNDCARD_H)
++#include <linux/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 "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_ms)
++{
++  /* 40ms is a big enough number to work ok */
++  *latency_ms = 40;
++  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, buffer, nframes);
++  } else {
++    pthread_mutex_unlock(&stream->state_mutex);
++  }
++  return got;
++}
++
++static void apply_volume(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 *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);
++      for (i=0; i<((unsigned long)got)*stream->params.channels; i++) {
++          buffer[i] = f_buffer[i]*32767.0;
++      }
++    } else {
++      got = run_data_callback(stream, buffer,
++                              OSS_BUFFER_SIZE/stream->params.channels);
++    }
++    apply_volume(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_size=1024;
++  unsigned int frag_log=10; /* 2^frag_log = frag_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_stream_params 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;
++
++  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 = 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,
++  .destroy = oss_destroy,
++  .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
++};
+diff --git media/libcubeb/src/moz.build media/libcubeb/src/moz.build
+index 8b7a0dd..31212ce 100644
+--- mozilla/media/libcubeb/src/moz.build
++++ mozilla/media/libcubeb/src/moz.build
+@@ -17,6 +17,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']:
+     SOURCES += [
+         'cubeb_pulse.c',
+@@ -75,5 +81,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ 
+ FAIL_ON_WARNINGS = True
+ 
++CFLAGS += CONFIG['MOZ_OSS_CFLAGS']
+ CFLAGS += CONFIG['MOZ_ALSA_CFLAGS']
+ CFLAGS += CONFIG['MOZ_PULSEAUDIO_CFLAGS']
+diff --git toolkit/library/libxul.mk toolkit/library/libxul.mk
+index e191f13..4fb268a 100644
+--- mozilla/toolkit/library/libxul.mk
++++ mozilla/toolkit/library/libxul.mk
+@@ -146,6 +146,10 @@ OS_LIBS += $(call EXPAND_LIBNAME,secur32
+ endif
+ endif
+ 
++ifdef MOZ_OSS
++EXTRA_DSO_LDOPTS += $(MOZ_OSS_LIBS)
++endif
++
+ ifdef MOZ_ALSA
+ EXTRA_DSO_LDOPTS += $(MOZ_ALSA_LIBS)
+ endif

Added: trunk/www/firefox-esr/files/patch-bug1021761
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/www/firefox-esr/files/patch-bug1021761	Thu Sep 25 22:14:18 2014	(r1707)
@@ -0,0 +1,1092 @@
+diff --git configure.in configure.in
+index 48e60c0..ec08417 100644
+--- configure.in
++++ configure.in
+@@ -5438,6 +5438,60 @@ fi
+ 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_DISABLE_BOOL(oss,
++[  --disable-oss           Disable OpenSoundSystem support],
++   MOZ_OSS=,
++   MOZ_OSS=1)
++
++_SAVE_CFLAGS=$CFLAGS
++_SAVE_LIBS=$LIBS
++if test -n "$MOZ_OSS"; then
++    dnl Prefer 4Front implementation
++    AC_MSG_CHECKING([MOZ_OSS_CFLAGS])
++    if test -z "$MOZ_OSS_CFLAGS"; then
++        for oss_conf in /etc/oss.conf /usr/local/etc/oss.conf; do
++            if test -e "$oss_conf"; then
++                . "$oss_conf"
++            fi
++        done
++        if test -d "$OSSLIBDIR"; then
++            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 linux/soundcard.h soundcard.h)
++
++    if test "$ac_cv_header_sys_soundcard_h" != "yes" -a \
++            "$ac_cv_header_linux_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 --disable-ogg --disable-wave --disable-webm.])
++    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="-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 ==================================
+ 
+@@ -5451,12 +5505,23 @@ MOZ_ARG_ENABLE_BOOL(alsa,
+    MOZ_ALSA=1,
+    MOZ_ALSA=)
+ 
++MOZ_ARG_DISABLE_BOOL(alsa-dlopen,
++[  --disable-alsa-dlopen   Disable runtime linking of libasound.so],
++   DISABLE_LIBASOUND_DLOPEN=1,
++   DISABLE_LIBASOUND_DLOPEN=)
++
+ if test -n "$MOZ_ALSA"; then
+     PKG_CHECK_MODULES(MOZ_ALSA, alsa, ,
+          [echo "$MOZ_ALSA_PKG_ERRORS"
+           AC_MSG_ERROR([Need alsa for Ogg, Wave or WebM decoding on Linux.  Disable with --disable-ogg --disable-wave --disable-webm.  (On Ubuntu, you might try installing the package libasound2-dev.)])])
+ fi
+ 
++if test -n "$DISABLE_LIBASOUND_DLOPEN"; then
++    AC_DEFINE(DISABLE_LIBASOUND_DLOPEN)
++else
++    MOZ_ALSA_LIBS=
++fi
++
+ AC_SUBST(MOZ_ALSA)
+ 
+ dnl ========================================================
+diff --git media/libcubeb/AUTHORS media/libcubeb/AUTHORS
+index b441e8a..950d9e5 100644
+--- media/libcubeb/AUTHORS
++++ media/libcubeb/AUTHORS
+@@ -4,3 +4,4 @@ Michael Wu <mwu at mozilla.com>
+ Paul Adenot <paul at paul.cx>
+ David Richards <drichards at mozilla.com>
+ Sebastien Alaiwan <sebastien.alaiwan at gmail.com>
++Evgeniy Vodolazskiy <waterlaz at gmail.com>
+diff --git media/libcubeb/src/cubeb.c media/libcubeb/src/cubeb.c
+index 9c3adcc..45d765b 100644
+--- media/libcubeb/src/cubeb.c
++++ media/libcubeb/src/cubeb.c
+@@ -54,6 +54,9 @@ int opensl_init(cubeb ** context, char const * context_name);
+ #if defined(USE_AUDIOTRACK)
+ int audiotrack_init(cubeb ** context, char const * context_name);
+ #endif
++#if defined(USE_OSS)
++int oss_init(cubeb ** context, char const * context_name);
++#endif
+ 
+ int
+ validate_stream_params(cubeb_stream_params stream_params)
+@@ -120,6 +123,9 @@ cubeb_init(cubeb ** context, char const * context_name)
+ #if defined(USE_AUDIOTRACK)
+     audiotrack_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 a962553..1f780f4 100644
+--- media/libcubeb/src/cubeb_alsa.c
++++ media/libcubeb/src/cubeb_alsa.c
+@@ -11,6 +11,7 @@
+ #include <sys/time.h>
+ #include <assert.h>
+ #include <limits.h>
++#include <dlfcn.h>
+ #include <poll.h>
+ #include <unistd.h>
+ #include <alsa/asoundlib.h>
+@@ -24,6 +25,50 @@
+ 
+ #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_recover);
++MAKE_TYPEDEF(snd_pcm_set_params);
++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),
+@@ -64,6 +109,8 @@ struct cubeb {
+      workaround is not required. */
+   snd_config_t * local_config;
+   int is_pa;
++
++  void * libasound;
+ };
+ 
+ enum stream_state {
+@@ -262,7 +309,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+   pthread_mutex_lock(&stm->mutex);
+ 
+-  r = snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
++  r = WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
+   if (r < 0 || revents != POLLOUT) {
+     /* This should be a stream error; it makes no sense for poll(2) to wake
+        for this stream and then have the stream report that it's not ready.
+@@ -271,10 +318,10 @@ alsa_refill_stream(cubeb_stream * stm)
+     return RUNNING;
+   }
+ 
+-  avail = snd_pcm_avail_update(stm->pcm);
++  avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   if (avail == -EPIPE) {
+-    snd_pcm_recover(stm->pcm, avail, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, avail, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   }
+ 
+   /* Failed to recover from an xrun, this stream must be broken. */
+@@ -293,8 +340,8 @@ alsa_refill_stream(cubeb_stream * stm)
+      available to write.  If avail is still zero here, the stream must be in
+      a funky state, so recover and try again. */
+   if (avail == 0) {
+-    snd_pcm_recover(stm->pcm, -EPIPE, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, -EPIPE, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+     if (avail <= 0) {
+       pthread_mutex_unlock(&stm->mutex);
+       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+@@ -302,7 +349,7 @@ alsa_refill_stream(cubeb_stream * stm)
+     }
+   }
+ 
+-  p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
++  p = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, avail));
+   assert(p);
+ 
+   pthread_mutex_unlock(&stm->mutex);
+@@ -313,10 +360,10 @@ alsa_refill_stream(cubeb_stream * stm)
+     return ERROR;
+   }
+   if (got > 0) {
+-    snd_pcm_sframes_t wrote = snd_pcm_writei(stm->pcm, p, got);
++    snd_pcm_sframes_t wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     if (wrote == -EPIPE) {
+-      snd_pcm_recover(stm->pcm, wrote, 1);
+-      wrote = snd_pcm_writei(stm->pcm, p, got);
++      WRAP(snd_pcm_recover)(stm->pcm, wrote, 1);
++      wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     }
+     assert(wrote >= 0 && wrote == got);
+     stm->write_position += wrote;
+@@ -342,7 +389,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+     /* Fill the remaining buffer with silence to guarantee one full period
+        has been written. */
+-    snd_pcm_writei(stm->pcm, (char *) p + got, avail - got);
++    WRAP(snd_pcm_writei)(stm->pcm, (char *) p + got, avail - got);
+ 
+     set_timeout(&stm->drain_timeout, buffer_time * 1000);
+ 
+@@ -453,26 +500,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;
+     }
+@@ -481,7 +528,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;
+     }
+@@ -490,7 +537,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;
+@@ -513,22 +560,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;
+     }
+@@ -537,7 +584,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;
+     }
+@@ -548,12 +595,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;
+     }
+@@ -564,18 +611,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;
+     }
+@@ -583,7 +630,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;
+ }
+@@ -595,9 +642,9 @@ alsa_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * l
+ 
+   pthread_mutex_lock(&cubeb_alsa_mutex);
+   if (local_config) {
+-    r = snd_pcm_open_lconf(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
++    r = WRAP(snd_pcm_open_lconf)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
+   } else {
+-    r = snd_pcm_open(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
++    r = WRAP(snd_pcm_open)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
+   }
+   pthread_mutex_unlock(&cubeb_alsa_mutex);
+ 
+@@ -610,7 +657,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;
+@@ -667,12 +714,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);
+@@ -680,6 +780,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);
+@@ -729,7 +831,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) {
+@@ -768,9 +870,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);
+   }
+ 
+@@ -838,7 +944,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     return CUBEB_ERROR;
+   }
+ 
+-  r = snd_pcm_nonblock(stm->pcm, 1);
++  r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
+   assert(r == 0);
+ 
+   /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
+@@ -848,23 +954,23 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     latency = latency < 500 ? 500 : latency;
+   }
+ 
+-  r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
+-                         stm->params.channels, stm->params.rate, 1,
+-                         latency * 1000);
++  r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
++                               stm->params.channels, stm->params.rate, 1,
++                               latency * 1000);
+   if (r < 0) {
+     alsa_stream_destroy(stm);
+     return CUBEB_ERROR_INVALID_FORMAT;
+   }
+ 
+-  r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size);
++  r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &stm->period_size);
+   assert(r == 0);
+ 
+-  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);
+ 
+   r = pthread_cond_init(&stm->cond, NULL);
+@@ -937,12 +1043,12 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(stm->pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
++  rv = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+@@ -962,34 +1068,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. */
+-  rv = snd_pcm_open(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
++  rv = WRAP(snd_pcm_open)(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
+   if (rv < 0) {
+-    snd_pcm_close(pcm);
++    WRAP(snd_pcm_close)(pcm);
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_rate(hw_params, rate, &dir);
++  rv = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
+   if (rv >= 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;
+ 
+-  rv = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL);
++  rv = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
+   if (rv < 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;
+ }
+@@ -1013,7 +1119,7 @@ alsa_stream_start(cubeb_stream * stm)
+   ctx = stm->context;
+ 
+   pthread_mutex_lock(&stm->mutex);
+-  snd_pcm_pause(stm->pcm, 0);
++  WRAP(snd_pcm_pause)(stm->pcm, 0);
+   gettimeofday(&stm->last_activity, NULL);
+   pthread_mutex_unlock(&stm->mutex);
+ 
+@@ -1047,7 +1153,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;
+@@ -1063,8 +1169,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;
+@@ -1089,7 +1195,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 0000000..5e38e27
+--- /dev/null
++++ media/libcubeb/src/cubeb_oss.c
+@@ -0,0 +1,399 @@
++/*
++ * 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>
++#elif defined(HAVE_LINUX_SOUNDCARD_H)
++#include <linux/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 "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_ms)
++{
++  /* 40ms is a big enough number to work ok */
++  *latency_ms = 40;
++  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, buffer, nframes);
++  } else {
++    pthread_mutex_unlock(&stream->state_mutex);
++  }
++  return got;
++}
++
++static void apply_volume(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 *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);
++      for (i=0; i<((unsigned long)got)*stream->params.channels; i++) {
++          buffer[i] = f_buffer[i]*32767.0;
++      }
++    } else {
++      got = run_data_callback(stream, buffer,
++                              OSS_BUFFER_SIZE/stream->params.channels);
++    }
++    apply_volume(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_size=1024;
++  unsigned int frag_log=10; /* 2^frag_log = frag_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_stream_params 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;
++
++  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 = 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,
++  .destroy = oss_destroy,
++  .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
++};
+diff --git media/libcubeb/src/moz.build media/libcubeb/src/moz.build
+index 8b7a0dd..31212ce 100644
+--- media/libcubeb/src/moz.build
++++ media/libcubeb/src/moz.build
+@@ -17,6 +17,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']:
+     SOURCES += [
+         'cubeb_pulse.c',
+@@ -75,5 +81,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ 
+ FAIL_ON_WARNINGS = True
+ 
++CFLAGS += CONFIG['MOZ_OSS_CFLAGS']
+ CFLAGS += CONFIG['MOZ_ALSA_CFLAGS']
+ CFLAGS += CONFIG['MOZ_PULSEAUDIO_CFLAGS']
+diff --git toolkit/library/libxul.mk toolkit/library/libxul.mk
+index e191f13..4fb268a 100644
+--- toolkit/library/libxul.mk
++++ toolkit/library/libxul.mk
+@@ -146,6 +146,10 @@ OS_LIBS += $(call EXPAND_LIBNAME,secur32
+ endif
+ endif
+ 
++ifdef MOZ_OSS
++EXTRA_DSO_LDOPTS += $(MOZ_OSS_LIBS)
++endif
++
+ ifdef MOZ_ALSA
+ EXTRA_DSO_LDOPTS += $(MOZ_ALSA_LIBS)
+ endif

Added: trunk/www/firefox-nightly/files/patch-bug1021761
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/www/firefox-nightly/files/patch-bug1021761	Thu Sep 25 22:14:18 2014	(r1707)
@@ -0,0 +1,1105 @@
+diff --git configure.in configure.in
+index 48e60c0..ec08417 100644
+--- configure.in
++++ configure.in
+@@ -5438,6 +5438,60 @@ fi
+ 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_DISABLE_BOOL(oss,
++[  --disable-oss           Disable OpenSoundSystem support],
++   MOZ_OSS=,
++   MOZ_OSS=1)
++
++_SAVE_CFLAGS=$CFLAGS
++_SAVE_LIBS=$LIBS
++if test -n "$MOZ_OSS"; then
++    dnl Prefer 4Front implementation
++    AC_MSG_CHECKING([MOZ_OSS_CFLAGS])
++    if test -z "$MOZ_OSS_CFLAGS"; then
++        for oss_conf in /etc/oss.conf /usr/local/etc/oss.conf; do
++            if test -e "$oss_conf"; then
++                . "$oss_conf"
++            fi
++        done
++        if test -d "$OSSLIBDIR"; then
++            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 linux/soundcard.h soundcard.h)
++
++    if test "$ac_cv_header_sys_soundcard_h" != "yes" -a \
++            "$ac_cv_header_linux_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 --disable-ogg --disable-wave --disable-webm.])
++    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="-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 ==================================
+ 
+@@ -5451,12 +5505,23 @@ MOZ_ARG_ENABLE_BOOL(alsa,
+    MOZ_ALSA=1,
+    MOZ_ALSA=)
+ 
++MOZ_ARG_DISABLE_BOOL(alsa-dlopen,
++[  --disable-alsa-dlopen   Disable runtime linking of libasound.so],
++   DISABLE_LIBASOUND_DLOPEN=1,
++   DISABLE_LIBASOUND_DLOPEN=)
++
+ if test -n "$MOZ_ALSA"; then
+     PKG_CHECK_MODULES(MOZ_ALSA, alsa, ,
+          [echo "$MOZ_ALSA_PKG_ERRORS"
+           AC_MSG_ERROR([Need alsa for Ogg, Wave or WebM decoding on Linux.  Disable with --disable-ogg --disable-wave --disable-webm.  (On Ubuntu, you might try installing the package libasound2-dev.)])])
+ fi
+ 
++if test -n "$DISABLE_LIBASOUND_DLOPEN"; then
++    AC_DEFINE(DISABLE_LIBASOUND_DLOPEN)
++else
++    MOZ_ALSA_LIBS=
++fi
++
+ AC_SUBST(MOZ_ALSA)
+ 
+ dnl ========================================================
+diff --git media/libcubeb/AUTHORS media/libcubeb/AUTHORS
+index b441e8a..950d9e5 100644
+--- media/libcubeb/AUTHORS
++++ media/libcubeb/AUTHORS
+@@ -4,3 +4,4 @@ Michael Wu <mwu at mozilla.com>
+ Paul Adenot <paul at paul.cx>
+ David Richards <drichards at mozilla.com>
+ Sebastien Alaiwan <sebastien.alaiwan at gmail.com>
++Evgeniy Vodolazskiy <waterlaz at gmail.com>
+diff --git media/libcubeb/src/cubeb.c media/libcubeb/src/cubeb.c
+index 9c3adcc..45d765b 100644
+--- media/libcubeb/src/cubeb.c
++++ media/libcubeb/src/cubeb.c
+@@ -54,6 +54,9 @@ int opensl_init(cubeb ** context, char const * context_name);
+ #if defined(USE_AUDIOTRACK)
+ int audiotrack_init(cubeb ** context, char const * context_name);
+ #endif
++#if defined(USE_OSS)
++int oss_init(cubeb ** context, char const * context_name);
++#endif
+ 
+ int
+ validate_stream_params(cubeb_stream_params stream_params)
+@@ -120,6 +123,9 @@ cubeb_init(cubeb ** context, char const * context_name)
+ #if defined(USE_AUDIOTRACK)
+     audiotrack_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 a962553..1f780f4 100644
+--- media/libcubeb/src/cubeb_alsa.c
++++ media/libcubeb/src/cubeb_alsa.c
+@@ -11,6 +11,7 @@
+ #include <sys/time.h>
+ #include <assert.h>
+ #include <limits.h>
++#include <dlfcn.h>
+ #include <poll.h>
+ #include <unistd.h>
+ #include <alsa/asoundlib.h>
+@@ -24,6 +25,50 @@
+ 
+ #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_recover);
++MAKE_TYPEDEF(snd_pcm_set_params);
++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),
+@@ -64,6 +109,8 @@ struct cubeb {
+      workaround is not required. */
+   snd_config_t * local_config;
+   int is_pa;
++
++  void * libasound;
+ };
+ 
+ enum stream_state {
+@@ -262,7 +309,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+   pthread_mutex_lock(&stm->mutex);
+ 
+-  r = snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
++  r = WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
+   if (r < 0 || revents != POLLOUT) {
+     /* This should be a stream error; it makes no sense for poll(2) to wake
+        for this stream and then have the stream report that it's not ready.
+@@ -271,10 +318,10 @@ alsa_refill_stream(cubeb_stream * stm)
+     return RUNNING;
+   }
+ 
+-  avail = snd_pcm_avail_update(stm->pcm);
++  avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   if (avail == -EPIPE) {
+-    snd_pcm_recover(stm->pcm, avail, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, avail, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   }
+ 
+   /* Failed to recover from an xrun, this stream must be broken. */
+@@ -293,8 +340,8 @@ alsa_refill_stream(cubeb_stream * stm)
+      available to write.  If avail is still zero here, the stream must be in
+      a funky state, so recover and try again. */
+   if (avail == 0) {
+-    snd_pcm_recover(stm->pcm, -EPIPE, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, -EPIPE, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+     if (avail <= 0) {
+       pthread_mutex_unlock(&stm->mutex);
+       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+@@ -302,7 +349,7 @@ alsa_refill_stream(cubeb_stream * stm)
+     }
+   }
+ 
+-  p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
++  p = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, avail));
+   assert(p);
+ 
+   pthread_mutex_unlock(&stm->mutex);
+@@ -327,10 +374,10 @@ alsa_refill_stream(cubeb_stream * stm)
+         b[i] *= stm->volume;
+       }
+     }
+-    wrote = snd_pcm_writei(stm->pcm, p, got);
++    wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     if (wrote == -EPIPE) {
+-      snd_pcm_recover(stm->pcm, wrote, 1);
+-      wrote = snd_pcm_writei(stm->pcm, p, got);
++      WRAP(snd_pcm_recover)(stm->pcm, wrote, 1);
++      wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     }
+     assert(wrote >= 0 && wrote == got);
+     stm->write_position += wrote;
+@@ -342,7 +389,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+     /* Fill the remaining buffer with silence to guarantee one full period
+        has been written. */
+-    snd_pcm_writei(stm->pcm, (char *) p + got, avail - got);
++    WRAP(snd_pcm_writei)(stm->pcm, (char *) p + got, avail - got);
+ 
+     set_timeout(&stm->drain_timeout, buffer_time * 1000);
+ 
+@@ -453,26 +500,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;
+     }
+@@ -481,7 +528,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;
+     }
+@@ -490,7 +537,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;
+@@ -513,22 +560,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;
+     }
+@@ -537,7 +584,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;
+     }
+@@ -548,12 +595,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;
+     }
+@@ -564,18 +611,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;
+     }
+@@ -583,7 +630,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;
+ }
+@@ -595,9 +642,9 @@ alsa_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * l
+ 
+   pthread_mutex_lock(&cubeb_alsa_mutex);
+   if (local_config) {
+-    r = snd_pcm_open_lconf(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
++    r = WRAP(snd_pcm_open_lconf)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
+   } else {
+-    r = snd_pcm_open(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
++    r = WRAP(snd_pcm_open)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
+   }
+   pthread_mutex_unlock(&cubeb_alsa_mutex);
+ 
+@@ -610,7 +657,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;
+@@ -667,12 +714,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);
+@@ -680,6 +780,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);
+@@ -729,7 +831,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) {
+@@ -768,9 +870,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);
+   }
+ 
+@@ -838,7 +944,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     return CUBEB_ERROR;
+   }
+ 
+-  r = snd_pcm_nonblock(stm->pcm, 1);
++  r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
+   assert(r == 0);
+ 
+   /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
+@@ -848,23 +954,23 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     latency = latency < 500 ? 500 : latency;
+   }
+ 
+-  r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
+-                         stm->params.channels, stm->params.rate, 1,
+-                         latency * 1000);
++  r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
++                               stm->params.channels, stm->params.rate, 1,
++                               latency * 1000);
+   if (r < 0) {
+     alsa_stream_destroy(stm);
+     return CUBEB_ERROR_INVALID_FORMAT;
+   }
+ 
+-  r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size);
++  r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &stm->period_size);
+   assert(r == 0);
+ 
+-  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);
+ 
+   r = pthread_cond_init(&stm->cond, NULL);
+@@ -895,7 +1001,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;
+@@ -937,12 +1043,12 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(stm->pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
++  rv = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+@@ -962,34 +1068,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. */
+-  rv = snd_pcm_open(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
++  rv = WRAP(snd_pcm_open)(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
+   if (rv < 0) {
+-    snd_pcm_close(pcm);
++    WRAP(snd_pcm_close)(pcm);
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_rate(hw_params, rate, &dir);
++  rv = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
+   if (rv >= 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;
+ 
+-  rv = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL);
++  rv = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
+   if (rv < 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;
+ }
+@@ -1013,7 +1119,7 @@ alsa_stream_start(cubeb_stream * stm)
+   ctx = stm->context;
+ 
+   pthread_mutex_lock(&stm->mutex);
+-  snd_pcm_pause(stm->pcm, 0);
++  WRAP(snd_pcm_pause)(stm->pcm, 0);
+   gettimeofday(&stm->last_activity, NULL);
+   pthread_mutex_unlock(&stm->mutex);
+ 
+@@ -1047,7 +1153,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;
+@@ -1063,8 +1169,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;
+@@ -1089,7 +1195,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 0000000..5e38e27
+--- /dev/null
++++ media/libcubeb/src/cubeb_oss.c
+@@ -0,0 +1,404 @@
++/*
++ * 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>
++#elif defined(HAVE_LINUX_SOUNDCARD_H)
++#include <linux/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 "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_ms)
++{
++  /* 40ms is a big enough number to work ok */
++  *latency_ms = 40;
++  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, buffer, nframes);
++  } else {
++    pthread_mutex_unlock(&stream->state_mutex);
++  }
++  return got;
++}
++
++static void apply_volume(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 *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);
++      for (i=0; i<((unsigned long)got)*stream->params.channels; i++) {
++          buffer[i] = f_buffer[i]*32767.0;
++      }
++    } else {
++      got = run_data_callback(stream, buffer,
++                              OSS_BUFFER_SIZE/stream->params.channels);
++    }
++    apply_volume(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_size=1024;
++  unsigned int frag_log=10; /* 2^frag_log = frag_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_stream_params 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;
++
++  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 = 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,
++  .destroy = oss_destroy,
++  .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
++};
+diff --git media/libcubeb/src/moz.build media/libcubeb/src/moz.build
+index 8b7a0dd..31212ce 100644
+--- media/libcubeb/src/moz.build
++++ media/libcubeb/src/moz.build
+@@ -17,6 +17,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']:
+     SOURCES += [
+         'cubeb_pulse.c',
+@@ -75,5 +81,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ 
+ FAIL_ON_WARNINGS = True
+ 
++CFLAGS += CONFIG['MOZ_OSS_CFLAGS']
+ CFLAGS += CONFIG['MOZ_ALSA_CFLAGS']
+ CFLAGS += CONFIG['MOZ_PULSEAUDIO_CFLAGS']
+diff --git toolkit/library/moz.build toolkit/library/moz.build
+index e191f13..4fb268a 100644
+--- toolkit/library/moz.build
++++ toolkit/library/moz.build
+@@ -239,6 +239,9 @@ if CONFIG['MOZ_NATIVE_LIBVPX']:
+ if not CONFIG['MOZ_TREE_PIXMAN']:
+     OS_LIBS += CONFIG['MOZ_PIXMAN_LIBS']
+ 
++if CONFIG['MOZ_OSS']:
++    OS_LIBS += CONFIG['MOZ_OSS_LIBS']
++
+ if CONFIG['MOZ_ALSA']:
+     OS_LIBS += CONFIG['MOZ_ALSA_LIBS']
+ 

Added: trunk/www/firefox/files/patch-bug1021761
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/www/firefox/files/patch-bug1021761	Thu Sep 25 22:14:18 2014	(r1707)
@@ -0,0 +1,1101 @@
+diff --git configure.in configure.in
+index 48e60c0..ec08417 100644
+--- configure.in
++++ configure.in
+@@ -5438,6 +5438,60 @@ fi
+ 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_DISABLE_BOOL(oss,
++[  --disable-oss           Disable OpenSoundSystem support],
++   MOZ_OSS=,
++   MOZ_OSS=1)
++
++_SAVE_CFLAGS=$CFLAGS
++_SAVE_LIBS=$LIBS
++if test -n "$MOZ_OSS"; then
++    dnl Prefer 4Front implementation
++    AC_MSG_CHECKING([MOZ_OSS_CFLAGS])
++    if test -z "$MOZ_OSS_CFLAGS"; then
++        for oss_conf in /etc/oss.conf /usr/local/etc/oss.conf; do
++            if test -e "$oss_conf"; then
++                . "$oss_conf"
++            fi
++        done
++        if test -d "$OSSLIBDIR"; then
++            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 linux/soundcard.h soundcard.h)
++
++    if test "$ac_cv_header_sys_soundcard_h" != "yes" -a \
++            "$ac_cv_header_linux_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 --disable-ogg --disable-wave --disable-webm.])
++    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="-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 ==================================
+ 
+@@ -5451,12 +5505,23 @@ MOZ_ARG_ENABLE_BOOL(alsa,
+    MOZ_ALSA=1,
+    MOZ_ALSA=)
+ 
++MOZ_ARG_DISABLE_BOOL(alsa-dlopen,
++[  --disable-alsa-dlopen   Disable runtime linking of libasound.so],
++   DISABLE_LIBASOUND_DLOPEN=1,
++   DISABLE_LIBASOUND_DLOPEN=)
++
+ if test -n "$MOZ_ALSA"; then
+     PKG_CHECK_MODULES(MOZ_ALSA, alsa, ,
+          [echo "$MOZ_ALSA_PKG_ERRORS"
+           AC_MSG_ERROR([Need alsa for Ogg, Wave or WebM decoding on Linux.  Disable with --disable-ogg --disable-wave --disable-webm.  (On Ubuntu, you might try installing the package libasound2-dev.)])])
+ fi
+ 
++if test -n "$DISABLE_LIBASOUND_DLOPEN"; then
++    AC_DEFINE(DISABLE_LIBASOUND_DLOPEN)
++else
++    MOZ_ALSA_LIBS=
++fi
++
+ AC_SUBST(MOZ_ALSA)
+ 
+ dnl ========================================================
+diff --git media/libcubeb/AUTHORS media/libcubeb/AUTHORS
+index b441e8a..950d9e5 100644
+--- media/libcubeb/AUTHORS
++++ media/libcubeb/AUTHORS
+@@ -4,3 +4,4 @@ Michael Wu <mwu at mozilla.com>
+ Paul Adenot <paul at paul.cx>
+ David Richards <drichards at mozilla.com>
+ Sebastien Alaiwan <sebastien.alaiwan at gmail.com>
++Evgeniy Vodolazskiy <waterlaz at gmail.com>
+diff --git media/libcubeb/src/cubeb.c media/libcubeb/src/cubeb.c
+index 9c3adcc..45d765b 100644
+--- media/libcubeb/src/cubeb.c
++++ media/libcubeb/src/cubeb.c
+@@ -54,6 +54,9 @@ int opensl_init(cubeb ** context, char const * context_name);
+ #if defined(USE_AUDIOTRACK)
+ int audiotrack_init(cubeb ** context, char const * context_name);
+ #endif
++#if defined(USE_OSS)
++int oss_init(cubeb ** context, char const * context_name);
++#endif
+ 
+ int
+ validate_stream_params(cubeb_stream_params stream_params)
+@@ -120,6 +123,9 @@ cubeb_init(cubeb ** context, char const * context_name)
+ #if defined(USE_AUDIOTRACK)
+     audiotrack_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 a962553..1f780f4 100644
+--- media/libcubeb/src/cubeb_alsa.c
++++ media/libcubeb/src/cubeb_alsa.c
+@@ -11,6 +11,7 @@
+ #include <sys/time.h>
+ #include <assert.h>
+ #include <limits.h>
++#include <dlfcn.h>
+ #include <poll.h>
+ #include <unistd.h>
+ #include <alsa/asoundlib.h>
+@@ -24,6 +25,50 @@
+ 
+ #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_recover);
++MAKE_TYPEDEF(snd_pcm_set_params);
++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),
+@@ -64,6 +109,8 @@ struct cubeb {
+      workaround is not required. */
+   snd_config_t * local_config;
+   int is_pa;
++
++  void * libasound;
+ };
+ 
+ enum stream_state {
+@@ -262,7 +309,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+   pthread_mutex_lock(&stm->mutex);
+ 
+-  r = snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
++  r = WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
+   if (r < 0 || revents != POLLOUT) {
+     /* This should be a stream error; it makes no sense for poll(2) to wake
+        for this stream and then have the stream report that it's not ready.
+@@ -271,10 +318,10 @@ alsa_refill_stream(cubeb_stream * stm)
+     return RUNNING;
+   }
+ 
+-  avail = snd_pcm_avail_update(stm->pcm);
++  avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   if (avail == -EPIPE) {
+-    snd_pcm_recover(stm->pcm, avail, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, avail, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   }
+ 
+   /* Failed to recover from an xrun, this stream must be broken. */
+@@ -293,8 +340,8 @@ alsa_refill_stream(cubeb_stream * stm)
+      available to write.  If avail is still zero here, the stream must be in
+      a funky state, so recover and try again. */
+   if (avail == 0) {
+-    snd_pcm_recover(stm->pcm, -EPIPE, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, -EPIPE, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+     if (avail <= 0) {
+       pthread_mutex_unlock(&stm->mutex);
+       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+@@ -302,7 +349,7 @@ alsa_refill_stream(cubeb_stream * stm)
+     }
+   }
+ 
+-  p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
++  p = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, avail));
+   assert(p);
+ 
+   pthread_mutex_unlock(&stm->mutex);
+@@ -313,10 +360,10 @@ alsa_refill_stream(cubeb_stream * stm)
+     return ERROR;
+   }
+   if (got > 0) {
+-    snd_pcm_sframes_t wrote = snd_pcm_writei(stm->pcm, p, got);
++    snd_pcm_sframes_t wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     if (wrote == -EPIPE) {
+-      snd_pcm_recover(stm->pcm, wrote, 1);
+-      wrote = snd_pcm_writei(stm->pcm, p, got);
++      WRAP(snd_pcm_recover)(stm->pcm, wrote, 1);
++      wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     }
+     assert(wrote >= 0 && wrote == got);
+     stm->write_position += wrote;
+@@ -342,7 +389,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+     /* Fill the remaining buffer with silence to guarantee one full period
+        has been written. */
+-    snd_pcm_writei(stm->pcm, (char *) p + got, avail - got);
++    WRAP(snd_pcm_writei)(stm->pcm, (char *) p + got, avail - got);
+ 
+     set_timeout(&stm->drain_timeout, buffer_time * 1000);
+ 
+@@ -453,26 +500,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;
+     }
+@@ -481,7 +528,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;
+     }
+@@ -490,7 +537,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;
+@@ -513,22 +560,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;
+     }
+@@ -537,7 +584,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;
+     }
+@@ -548,12 +595,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;
+     }
+@@ -564,18 +611,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;
+     }
+@@ -583,7 +630,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;
+ }
+@@ -595,9 +642,9 @@ alsa_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * l
+ 
+   pthread_mutex_lock(&cubeb_alsa_mutex);
+   if (local_config) {
+-    r = snd_pcm_open_lconf(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
++    r = WRAP(snd_pcm_open_lconf)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
+   } else {
+-    r = snd_pcm_open(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
++    r = WRAP(snd_pcm_open)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
+   }
+   pthread_mutex_unlock(&cubeb_alsa_mutex);
+ 
+@@ -610,7 +657,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;
+@@ -667,12 +714,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);
+@@ -680,6 +780,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);
+@@ -729,7 +831,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) {
+@@ -768,9 +870,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);
+   }
+ 
+@@ -838,7 +944,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     return CUBEB_ERROR;
+   }
+ 
+-  r = snd_pcm_nonblock(stm->pcm, 1);
++  r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
+   assert(r == 0);
+ 
+   /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
+@@ -848,23 +954,23 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     latency = latency < 500 ? 500 : latency;
+   }
+ 
+-  r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
+-                         stm->params.channels, stm->params.rate, 1,
+-                         latency * 1000);
++  r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
++                               stm->params.channels, stm->params.rate, 1,
++                               latency * 1000);
+   if (r < 0) {
+     alsa_stream_destroy(stm);
+     return CUBEB_ERROR_INVALID_FORMAT;
+   }
+ 
+-  r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size);
++  r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &stm->period_size);
+   assert(r == 0);
+ 
+-  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);
+ 
+   r = pthread_cond_init(&stm->cond, NULL);
+@@ -895,7 +1001,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;
+@@ -937,12 +1043,12 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(stm->pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
++  rv = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+@@ -962,34 +1068,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. */
+-  rv = snd_pcm_open(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
++  rv = WRAP(snd_pcm_open)(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
+   if (rv < 0) {
+-    snd_pcm_close(pcm);
++    WRAP(snd_pcm_close)(pcm);
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_rate(hw_params, rate, &dir);
++  rv = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
+   if (rv >= 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;
+ 
+-  rv = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL);
++  rv = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
+   if (rv < 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;
+ }
+@@ -1013,7 +1119,7 @@ alsa_stream_start(cubeb_stream * stm)
+   ctx = stm->context;
+ 
+   pthread_mutex_lock(&stm->mutex);
+-  snd_pcm_pause(stm->pcm, 0);
++  WRAP(snd_pcm_pause)(stm->pcm, 0);
+   gettimeofday(&stm->last_activity, NULL);
+   pthread_mutex_unlock(&stm->mutex);
+ 
+@@ -1047,7 +1153,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;
+@@ -1063,8 +1169,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;
+@@ -1089,7 +1195,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 0000000..5e38e27
+--- /dev/null
++++ media/libcubeb/src/cubeb_oss.c
+@@ -0,0 +1,399 @@
++/*
++ * 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>
++#elif defined(HAVE_LINUX_SOUNDCARD_H)
++#include <linux/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 "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_ms)
++{
++  /* 40ms is a big enough number to work ok */
++  *latency_ms = 40;
++  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, buffer, nframes);
++  } else {
++    pthread_mutex_unlock(&stream->state_mutex);
++  }
++  return got;
++}
++
++static void apply_volume(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 *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);
++      for (i=0; i<((unsigned long)got)*stream->params.channels; i++) {
++          buffer[i] = f_buffer[i]*32767.0;
++      }
++    } else {
++      got = run_data_callback(stream, buffer,
++                              OSS_BUFFER_SIZE/stream->params.channels);
++    }
++    apply_volume(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_size=1024;
++  unsigned int frag_log=10; /* 2^frag_log = frag_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_stream_params 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;
++
++  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 = 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,
++  .destroy = oss_destroy,
++  .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
++};
+diff --git media/libcubeb/src/moz.build media/libcubeb/src/moz.build
+index 8b7a0dd..31212ce 100644
+--- media/libcubeb/src/moz.build
++++ media/libcubeb/src/moz.build
+@@ -17,6 +17,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']:
+     SOURCES += [
+         'cubeb_pulse.c',
+@@ -75,5 +81,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ 
+ FAIL_ON_WARNINGS = True
+ 
++CFLAGS += CONFIG['MOZ_OSS_CFLAGS']
+ CFLAGS += CONFIG['MOZ_ALSA_CFLAGS']
+ CFLAGS += CONFIG['MOZ_PULSEAUDIO_CFLAGS']
+diff --git toolkit/library/libxul.mk toolkit/library/libxul.mk
+index e191f13..4fb268a 100644
+--- toolkit/library/libxul.mk
++++ toolkit/library/libxul.mk
+@@ -146,6 +146,10 @@ OS_LIBS += $(call EXPAND_LIBNAME,secur32
+ endif
+ endif
+ 
++ifdef MOZ_OSS
++EXTRA_DSO_LDOPTS += $(MOZ_OSS_LIBS)
++endif
++
+ ifdef MOZ_ALSA
+ EXTRA_DSO_LDOPTS += $(MOZ_ALSA_LIBS)
+ endif

Added: trunk/www/libxul/files/patch-bug1021761
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/www/libxul/files/patch-bug1021761	Thu Sep 25 22:14:18 2014	(r1707)
@@ -0,0 +1,1092 @@
+diff --git configure.in configure.in
+index 48e60c0..ec08417 100644
+--- configure.in
++++ configure.in
+@@ -5438,6 +5438,60 @@ fi
+ 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_DISABLE_BOOL(oss,
++[  --disable-oss           Disable OpenSoundSystem support],
++   MOZ_OSS=,
++   MOZ_OSS=1)
++
++_SAVE_CFLAGS=$CFLAGS
++_SAVE_LIBS=$LIBS
++if test -n "$MOZ_OSS"; then
++    dnl Prefer 4Front implementation
++    AC_MSG_CHECKING([MOZ_OSS_CFLAGS])
++    if test -z "$MOZ_OSS_CFLAGS"; then
++        for oss_conf in /etc/oss.conf /usr/local/etc/oss.conf; do
++            if test -e "$oss_conf"; then
++                . "$oss_conf"
++            fi
++        done
++        if test -d "$OSSLIBDIR"; then
++            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 linux/soundcard.h soundcard.h)
++
++    if test "$ac_cv_header_sys_soundcard_h" != "yes" -a \
++            "$ac_cv_header_linux_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 --disable-ogg --disable-wave --disable-webm.])
++    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="-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 ==================================
+ 
+@@ -5451,12 +5505,23 @@ MOZ_ARG_ENABLE_BOOL(alsa,
+    MOZ_ALSA=1,
+    MOZ_ALSA=)
+ 
++MOZ_ARG_DISABLE_BOOL(alsa-dlopen,
++[  --disable-alsa-dlopen   Disable runtime linking of libasound.so],
++   DISABLE_LIBASOUND_DLOPEN=1,
++   DISABLE_LIBASOUND_DLOPEN=)
++
+ if test -n "$MOZ_ALSA"; then
+     PKG_CHECK_MODULES(MOZ_ALSA, alsa, ,
+          [echo "$MOZ_ALSA_PKG_ERRORS"
+           AC_MSG_ERROR([Need alsa for Ogg, Wave or WebM decoding on Linux.  Disable with --disable-ogg --disable-wave --disable-webm.  (On Ubuntu, you might try installing the package libasound2-dev.)])])
+ fi
+ 
++if test -n "$DISABLE_LIBASOUND_DLOPEN"; then
++    AC_DEFINE(DISABLE_LIBASOUND_DLOPEN)
++else
++    MOZ_ALSA_LIBS=
++fi
++
+ AC_SUBST(MOZ_ALSA)
+ 
+ dnl ========================================================
+diff --git media/libcubeb/AUTHORS media/libcubeb/AUTHORS
+index b441e8a..950d9e5 100644
+--- media/libcubeb/AUTHORS
++++ media/libcubeb/AUTHORS
+@@ -4,3 +4,4 @@ Michael Wu <mwu at mozilla.com>
+ Paul Adenot <paul at paul.cx>
+ David Richards <drichards at mozilla.com>
+ Sebastien Alaiwan <sebastien.alaiwan at gmail.com>
++Evgeniy Vodolazskiy <waterlaz at gmail.com>
+diff --git media/libcubeb/src/cubeb.c media/libcubeb/src/cubeb.c
+index 9c3adcc..45d765b 100644
+--- media/libcubeb/src/cubeb.c
++++ media/libcubeb/src/cubeb.c
+@@ -54,6 +54,9 @@ int opensl_init(cubeb ** context, char const * context_name);
+ #if defined(USE_AUDIOTRACK)
+ int audiotrack_init(cubeb ** context, char const * context_name);
+ #endif
++#if defined(USE_OSS)
++int oss_init(cubeb ** context, char const * context_name);
++#endif
+ 
+ int
+ validate_stream_params(cubeb_stream_params stream_params)
+@@ -120,6 +123,9 @@ cubeb_init(cubeb ** context, char const * context_name)
+ #if defined(USE_AUDIOTRACK)
+     audiotrack_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 a962553..1f780f4 100644
+--- media/libcubeb/src/cubeb_alsa.c
++++ media/libcubeb/src/cubeb_alsa.c
+@@ -11,6 +11,7 @@
+ #include <sys/time.h>
+ #include <assert.h>
+ #include <limits.h>
++#include <dlfcn.h>
+ #include <poll.h>
+ #include <unistd.h>
+ #include <alsa/asoundlib.h>
+@@ -24,6 +25,50 @@
+ 
+ #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_recover);
++MAKE_TYPEDEF(snd_pcm_set_params);
++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),
+@@ -64,6 +109,8 @@ struct cubeb {
+      workaround is not required. */
+   snd_config_t * local_config;
+   int is_pa;
++
++  void * libasound;
+ };
+ 
+ enum stream_state {
+@@ -262,7 +309,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+   pthread_mutex_lock(&stm->mutex);
+ 
+-  r = snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
++  r = WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
+   if (r < 0 || revents != POLLOUT) {
+     /* This should be a stream error; it makes no sense for poll(2) to wake
+        for this stream and then have the stream report that it's not ready.
+@@ -271,10 +318,10 @@ alsa_refill_stream(cubeb_stream * stm)
+     return RUNNING;
+   }
+ 
+-  avail = snd_pcm_avail_update(stm->pcm);
++  avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   if (avail == -EPIPE) {
+-    snd_pcm_recover(stm->pcm, avail, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, avail, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   }
+ 
+   /* Failed to recover from an xrun, this stream must be broken. */
+@@ -293,8 +340,8 @@ alsa_refill_stream(cubeb_stream * stm)
+      available to write.  If avail is still zero here, the stream must be in
+      a funky state, so recover and try again. */
+   if (avail == 0) {
+-    snd_pcm_recover(stm->pcm, -EPIPE, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, -EPIPE, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+     if (avail <= 0) {
+       pthread_mutex_unlock(&stm->mutex);
+       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+@@ -302,7 +349,7 @@ alsa_refill_stream(cubeb_stream * stm)
+     }
+   }
+ 
+-  p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
++  p = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, avail));
+   assert(p);
+ 
+   pthread_mutex_unlock(&stm->mutex);
+@@ -313,10 +360,10 @@ alsa_refill_stream(cubeb_stream * stm)
+     return ERROR;
+   }
+   if (got > 0) {
+-    snd_pcm_sframes_t wrote = snd_pcm_writei(stm->pcm, p, got);
++    snd_pcm_sframes_t wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     if (wrote == -EPIPE) {
+-      snd_pcm_recover(stm->pcm, wrote, 1);
+-      wrote = snd_pcm_writei(stm->pcm, p, got);
++      WRAP(snd_pcm_recover)(stm->pcm, wrote, 1);
++      wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     }
+     assert(wrote >= 0 && wrote == got);
+     stm->write_position += wrote;
+@@ -342,7 +389,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+     /* Fill the remaining buffer with silence to guarantee one full period
+        has been written. */
+-    snd_pcm_writei(stm->pcm, (char *) p + got, avail - got);
++    WRAP(snd_pcm_writei)(stm->pcm, (char *) p + got, avail - got);
+ 
+     set_timeout(&stm->drain_timeout, buffer_time * 1000);
+ 
+@@ -453,26 +500,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;
+     }
+@@ -481,7 +528,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;
+     }
+@@ -490,7 +537,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;
+@@ -513,22 +560,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;
+     }
+@@ -537,7 +584,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;
+     }
+@@ -548,12 +595,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;
+     }
+@@ -564,18 +611,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;
+     }
+@@ -583,7 +630,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;
+ }
+@@ -595,9 +642,9 @@ alsa_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * l
+ 
+   pthread_mutex_lock(&cubeb_alsa_mutex);
+   if (local_config) {
+-    r = snd_pcm_open_lconf(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
++    r = WRAP(snd_pcm_open_lconf)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
+   } else {
+-    r = snd_pcm_open(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
++    r = WRAP(snd_pcm_open)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
+   }
+   pthread_mutex_unlock(&cubeb_alsa_mutex);
+ 
+@@ -610,7 +657,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;
+@@ -667,12 +714,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);
+@@ -680,6 +780,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);
+@@ -729,7 +831,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) {
+@@ -768,9 +870,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);
+   }
+ 
+@@ -838,7 +944,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     return CUBEB_ERROR;
+   }
+ 
+-  r = snd_pcm_nonblock(stm->pcm, 1);
++  r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
+   assert(r == 0);
+ 
+   /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
+@@ -848,23 +954,23 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     latency = latency < 500 ? 500 : latency;
+   }
+ 
+-  r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
+-                         stm->params.channels, stm->params.rate, 1,
+-                         latency * 1000);
++  r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
++                               stm->params.channels, stm->params.rate, 1,
++                               latency * 1000);
+   if (r < 0) {
+     alsa_stream_destroy(stm);
+     return CUBEB_ERROR_INVALID_FORMAT;
+   }
+ 
+-  r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size);
++  r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &stm->period_size);
+   assert(r == 0);
+ 
+-  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);
+ 
+   r = pthread_cond_init(&stm->cond, NULL);
+@@ -937,12 +1043,12 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(stm->pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
++  rv = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+@@ -962,34 +1068,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. */
+-  rv = snd_pcm_open(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
++  rv = WRAP(snd_pcm_open)(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
+   if (rv < 0) {
+-    snd_pcm_close(pcm);
++    WRAP(snd_pcm_close)(pcm);
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_rate(hw_params, rate, &dir);
++  rv = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
+   if (rv >= 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;
+ 
+-  rv = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL);
++  rv = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
+   if (rv < 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;
+ }
+@@ -1013,7 +1119,7 @@ alsa_stream_start(cubeb_stream * stm)
+   ctx = stm->context;
+ 
+   pthread_mutex_lock(&stm->mutex);
+-  snd_pcm_pause(stm->pcm, 0);
++  WRAP(snd_pcm_pause)(stm->pcm, 0);
+   gettimeofday(&stm->last_activity, NULL);
+   pthread_mutex_unlock(&stm->mutex);
+ 
+@@ -1047,7 +1153,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;
+@@ -1063,8 +1169,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;
+@@ -1089,7 +1195,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 0000000..5e38e27
+--- /dev/null
++++ media/libcubeb/src/cubeb_oss.c
+@@ -0,0 +1,399 @@
++/*
++ * 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>
++#elif defined(HAVE_LINUX_SOUNDCARD_H)
++#include <linux/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 "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_ms)
++{
++  /* 40ms is a big enough number to work ok */
++  *latency_ms = 40;
++  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, buffer, nframes);
++  } else {
++    pthread_mutex_unlock(&stream->state_mutex);
++  }
++  return got;
++}
++
++static void apply_volume(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 *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);
++      for (i=0; i<((unsigned long)got)*stream->params.channels; i++) {
++          buffer[i] = f_buffer[i]*32767.0;
++      }
++    } else {
++      got = run_data_callback(stream, buffer,
++                              OSS_BUFFER_SIZE/stream->params.channels);
++    }
++    apply_volume(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_size=1024;
++  unsigned int frag_log=10; /* 2^frag_log = frag_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_stream_params 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;
++
++  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 = 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,
++  .destroy = oss_destroy,
++  .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
++};
+diff --git media/libcubeb/src/moz.build media/libcubeb/src/moz.build
+index 8b7a0dd..31212ce 100644
+--- media/libcubeb/src/moz.build
++++ media/libcubeb/src/moz.build
+@@ -17,6 +17,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']:
+     SOURCES += [
+         'cubeb_pulse.c',
+@@ -75,5 +81,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ 
+ FAIL_ON_WARNINGS = True
+ 
++CFLAGS += CONFIG['MOZ_OSS_CFLAGS']
+ CFLAGS += CONFIG['MOZ_ALSA_CFLAGS']
+ CFLAGS += CONFIG['MOZ_PULSEAUDIO_CFLAGS']
+diff --git toolkit/library/libxul.mk toolkit/library/libxul.mk
+index e191f13..4fb268a 100644
+--- toolkit/library/libxul.mk
++++ toolkit/library/libxul.mk
+@@ -146,6 +146,10 @@ OS_LIBS += $(call EXPAND_LIBNAME,secur32
+ endif
+ endif
+ 
++ifdef MOZ_OSS
++EXTRA_DSO_LDOPTS += $(MOZ_OSS_LIBS)
++endif
++
+ ifdef MOZ_ALSA
+ EXTRA_DSO_LDOPTS += $(MOZ_ALSA_LIBS)
+ endif

Added: trunk/www/seamonkey/files/patch-bug1021761
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/www/seamonkey/files/patch-bug1021761	Thu Sep 25 22:14:18 2014	(r1707)
@@ -0,0 +1,1101 @@
+diff --git configure.in configure.in
+index 48e60c0..ec08417 100644
+--- mozilla/configure.in
++++ mozilla/configure.in
+@@ -5438,6 +5438,60 @@ fi
+ 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_DISABLE_BOOL(oss,
++[  --disable-oss           Disable OpenSoundSystem support],
++   MOZ_OSS=,
++   MOZ_OSS=1)
++
++_SAVE_CFLAGS=$CFLAGS
++_SAVE_LIBS=$LIBS
++if test -n "$MOZ_OSS"; then
++    dnl Prefer 4Front implementation
++    AC_MSG_CHECKING([MOZ_OSS_CFLAGS])
++    if test -z "$MOZ_OSS_CFLAGS"; then
++        for oss_conf in /etc/oss.conf /usr/local/etc/oss.conf; do
++            if test -e "$oss_conf"; then
++                . "$oss_conf"
++            fi
++        done
++        if test -d "$OSSLIBDIR"; then
++            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 linux/soundcard.h soundcard.h)
++
++    if test "$ac_cv_header_sys_soundcard_h" != "yes" -a \
++            "$ac_cv_header_linux_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 --disable-ogg --disable-wave --disable-webm.])
++    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="-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 ==================================
+ 
+@@ -5451,12 +5505,23 @@ MOZ_ARG_ENABLE_BOOL(alsa,
+    MOZ_ALSA=1,
+    MOZ_ALSA=)
+ 
++MOZ_ARG_DISABLE_BOOL(alsa-dlopen,
++[  --disable-alsa-dlopen   Disable runtime linking of libasound.so],
++   DISABLE_LIBASOUND_DLOPEN=1,
++   DISABLE_LIBASOUND_DLOPEN=)
++
+ if test -n "$MOZ_ALSA"; then
+     PKG_CHECK_MODULES(MOZ_ALSA, alsa, ,
+          [echo "$MOZ_ALSA_PKG_ERRORS"
+           AC_MSG_ERROR([Need alsa for Ogg, Wave or WebM decoding on Linux.  Disable with --disable-ogg --disable-wave --disable-webm.  (On Ubuntu, you might try installing the package libasound2-dev.)])])
+ fi
+ 
++if test -n "$DISABLE_LIBASOUND_DLOPEN"; then
++    AC_DEFINE(DISABLE_LIBASOUND_DLOPEN)
++else
++    MOZ_ALSA_LIBS=
++fi
++
+ AC_SUBST(MOZ_ALSA)
+ 
+ dnl ========================================================
+diff --git media/libcubeb/AUTHORS media/libcubeb/AUTHORS
+index b441e8a..950d9e5 100644
+--- mozilla/media/libcubeb/AUTHORS
++++ mozilla/media/libcubeb/AUTHORS
+@@ -4,3 +4,4 @@ Michael Wu <mwu at mozilla.com>
+ Paul Adenot <paul at paul.cx>
+ David Richards <drichards at mozilla.com>
+ Sebastien Alaiwan <sebastien.alaiwan at gmail.com>
++Evgeniy Vodolazskiy <waterlaz at gmail.com>
+diff --git media/libcubeb/src/cubeb.c media/libcubeb/src/cubeb.c
+index 9c3adcc..45d765b 100644
+--- mozilla/media/libcubeb/src/cubeb.c
++++ mozilla/media/libcubeb/src/cubeb.c
+@@ -54,6 +54,9 @@ int opensl_init(cubeb ** context, char const * context_name);
+ #if defined(USE_AUDIOTRACK)
+ int audiotrack_init(cubeb ** context, char const * context_name);
+ #endif
++#if defined(USE_OSS)
++int oss_init(cubeb ** context, char const * context_name);
++#endif
+ 
+ int
+ validate_stream_params(cubeb_stream_params stream_params)
+@@ -120,6 +123,9 @@ cubeb_init(cubeb ** context, char const * context_name)
+ #if defined(USE_AUDIOTRACK)
+     audiotrack_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 a962553..1f780f4 100644
+--- mozilla/media/libcubeb/src/cubeb_alsa.c
++++ mozilla/media/libcubeb/src/cubeb_alsa.c
+@@ -11,6 +11,7 @@
+ #include <sys/time.h>
+ #include <assert.h>
+ #include <limits.h>
++#include <dlfcn.h>
+ #include <poll.h>
+ #include <unistd.h>
+ #include <alsa/asoundlib.h>
+@@ -24,6 +25,50 @@
+ 
+ #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_recover);
++MAKE_TYPEDEF(snd_pcm_set_params);
++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),
+@@ -64,6 +109,8 @@ struct cubeb {
+      workaround is not required. */
+   snd_config_t * local_config;
+   int is_pa;
++
++  void * libasound;
+ };
+ 
+ enum stream_state {
+@@ -262,7 +309,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+   pthread_mutex_lock(&stm->mutex);
+ 
+-  r = snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
++  r = WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
+   if (r < 0 || revents != POLLOUT) {
+     /* This should be a stream error; it makes no sense for poll(2) to wake
+        for this stream and then have the stream report that it's not ready.
+@@ -271,10 +318,10 @@ alsa_refill_stream(cubeb_stream * stm)
+     return RUNNING;
+   }
+ 
+-  avail = snd_pcm_avail_update(stm->pcm);
++  avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   if (avail == -EPIPE) {
+-    snd_pcm_recover(stm->pcm, avail, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, avail, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+   }
+ 
+   /* Failed to recover from an xrun, this stream must be broken. */
+@@ -293,8 +340,8 @@ alsa_refill_stream(cubeb_stream * stm)
+      available to write.  If avail is still zero here, the stream must be in
+      a funky state, so recover and try again. */
+   if (avail == 0) {
+-    snd_pcm_recover(stm->pcm, -EPIPE, 1);
+-    avail = snd_pcm_avail_update(stm->pcm);
++    WRAP(snd_pcm_recover)(stm->pcm, -EPIPE, 1);
++    avail = WRAP(snd_pcm_avail_update)(stm->pcm);
+     if (avail <= 0) {
+       pthread_mutex_unlock(&stm->mutex);
+       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
+@@ -302,7 +349,7 @@ alsa_refill_stream(cubeb_stream * stm)
+     }
+   }
+ 
+-  p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
++  p = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, avail));
+   assert(p);
+ 
+   pthread_mutex_unlock(&stm->mutex);
+@@ -313,10 +360,10 @@ alsa_refill_stream(cubeb_stream * stm)
+     return ERROR;
+   }
+   if (got > 0) {
+-    snd_pcm_sframes_t wrote = snd_pcm_writei(stm->pcm, p, got);
++    snd_pcm_sframes_t wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     if (wrote == -EPIPE) {
+-      snd_pcm_recover(stm->pcm, wrote, 1);
+-      wrote = snd_pcm_writei(stm->pcm, p, got);
++      WRAP(snd_pcm_recover)(stm->pcm, wrote, 1);
++      wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got);
+     }
+     assert(wrote >= 0 && wrote == got);
+     stm->write_position += wrote;
+@@ -342,7 +389,7 @@ alsa_refill_stream(cubeb_stream * stm)
+ 
+     /* Fill the remaining buffer with silence to guarantee one full period
+        has been written. */
+-    snd_pcm_writei(stm->pcm, (char *) p + got, avail - got);
++    WRAP(snd_pcm_writei)(stm->pcm, (char *) p + got, avail - got);
+ 
+     set_timeout(&stm->drain_timeout, buffer_time * 1000);
+ 
+@@ -453,26 +500,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;
+     }
+@@ -481,7 +528,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;
+     }
+@@ -490,7 +537,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;
+@@ -513,22 +560,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;
+     }
+@@ -537,7 +584,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;
+     }
+@@ -548,12 +595,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;
+     }
+@@ -564,18 +611,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;
+     }
+@@ -583,7 +630,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;
+ }
+@@ -595,9 +642,9 @@ alsa_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * l
+ 
+   pthread_mutex_lock(&cubeb_alsa_mutex);
+   if (local_config) {
+-    r = snd_pcm_open_lconf(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
++    r = WRAP(snd_pcm_open_lconf)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
+   } else {
+-    r = snd_pcm_open(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
++    r = WRAP(snd_pcm_open)(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
+   }
+   pthread_mutex_unlock(&cubeb_alsa_mutex);
+ 
+@@ -610,7 +657,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;
+@@ -667,12 +714,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);
+@@ -680,6 +780,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);
+@@ -729,7 +831,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) {
+@@ -768,9 +870,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);
+   }
+ 
+@@ -838,7 +944,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     return CUBEB_ERROR;
+   }
+ 
+-  r = snd_pcm_nonblock(stm->pcm, 1);
++  r = WRAP(snd_pcm_nonblock)(stm->pcm, 1);
+   assert(r == 0);
+ 
+   /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
+@@ -848,23 +954,23 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
+     latency = latency < 500 ? 500 : latency;
+   }
+ 
+-  r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
+-                         stm->params.channels, stm->params.rate, 1,
+-                         latency * 1000);
++  r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
++                               stm->params.channels, stm->params.rate, 1,
++                               latency * 1000);
+   if (r < 0) {
+     alsa_stream_destroy(stm);
+     return CUBEB_ERROR_INVALID_FORMAT;
+   }
+ 
+-  r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size);
++  r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &stm->period_size);
+   assert(r == 0);
+ 
+-  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);
+ 
+   r = pthread_cond_init(&stm->cond, NULL);
+@@ -895,7 +1001,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;
+@@ -937,12 +1043,12 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(stm->pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
++  rv = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+@@ -962,34 +1068,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. */
+-  rv = snd_pcm_open(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
++  rv = WRAP(snd_pcm_open)(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
+   if (rv < 0) {
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_any(pcm, hw_params);
++  rv = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
+   if (rv < 0) {
+-    snd_pcm_close(pcm);
++    WRAP(snd_pcm_close)(pcm);
+     return CUBEB_ERROR;
+   }
+ 
+-  rv = snd_pcm_hw_params_get_rate(hw_params, rate, &dir);
++  rv = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
+   if (rv >= 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;
+ 
+-  rv = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL);
++  rv = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
+   if (rv < 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;
+ }
+@@ -1013,7 +1119,7 @@ alsa_stream_start(cubeb_stream * stm)
+   ctx = stm->context;
+ 
+   pthread_mutex_lock(&stm->mutex);
+-  snd_pcm_pause(stm->pcm, 0);
++  WRAP(snd_pcm_pause)(stm->pcm, 0);
+   gettimeofday(&stm->last_activity, NULL);
+   pthread_mutex_unlock(&stm->mutex);
+ 
+@@ -1047,7 +1153,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;
+@@ -1063,8 +1169,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;
+@@ -1089,7 +1195,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 0000000..5e38e27
+--- mozilla//dev/null
++++ mozilla/media/libcubeb/src/cubeb_oss.c
+@@ -0,0 +1,399 @@
++/*
++ * 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>
++#elif defined(HAVE_LINUX_SOUNDCARD_H)
++#include <linux/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 "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_ms)
++{
++  /* 40ms is a big enough number to work ok */
++  *latency_ms = 40;
++  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, buffer, nframes);
++  } else {
++    pthread_mutex_unlock(&stream->state_mutex);
++  }
++  return got;
++}
++
++static void apply_volume(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 *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);
++      for (i=0; i<((unsigned long)got)*stream->params.channels; i++) {
++          buffer[i] = f_buffer[i]*32767.0;
++      }
++    } else {
++      got = run_data_callback(stream, buffer,
++                              OSS_BUFFER_SIZE/stream->params.channels);
++    }
++    apply_volume(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_size=1024;
++  unsigned int frag_log=10; /* 2^frag_log = frag_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_stream_params 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;
++
++  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 = 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,
++  .destroy = oss_destroy,
++  .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
++};
+diff --git media/libcubeb/src/moz.build media/libcubeb/src/moz.build
+index 8b7a0dd..31212ce 100644
+--- mozilla/media/libcubeb/src/moz.build
++++ mozilla/media/libcubeb/src/moz.build
+@@ -17,6 +17,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']:
+     SOURCES += [
+         'cubeb_pulse.c',
+@@ -75,5 +81,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ 
+ FAIL_ON_WARNINGS = True
+ 
++CFLAGS += CONFIG['MOZ_OSS_CFLAGS']
+ CFLAGS += CONFIG['MOZ_ALSA_CFLAGS']
+ CFLAGS += CONFIG['MOZ_PULSEAUDIO_CFLAGS']
+diff --git toolkit/library/libxul.mk toolkit/library/libxul.mk
+index e191f13..4fb268a 100644
+--- mozilla/toolkit/library/libxul.mk
++++ mozilla/toolkit/library/libxul.mk
+@@ -146,6 +146,10 @@ OS_LIBS += $(call EXPAND_LIBNAME,secur32
+ endif
+ endif
+ 
++ifdef MOZ_OSS
++EXTRA_DSO_LDOPTS += $(MOZ_OSS_LIBS)
++endif
++
+ ifdef MOZ_ALSA
+ EXTRA_DSO_LDOPTS += $(MOZ_ALSA_LIBS)
+ endif


More information about the freebsd-gecko mailing list