Changeset View
Changeset View
Standalone View
Standalone View
head/mail/thunderbird/files/patch-bug1021761
| Property | Old Value | New Value |
|---|---|---|
| fbsd:nokeywords | null | yes \ No newline at end of property |
| svn:eol-style | null | native \ No newline at end of property |
| svn:mime-type | null | text/plain \ No newline at end of property |
| commit f9bcf9c81c4a | |||||
| Author: Evgeniy Vodolazskiy <waterlaz@gmail.com> | |||||
| Date: Tue Sep 9 14:38:00 2014 -0700 | |||||
| Bug 1021761 - Make ALSA optional on Linux, allowing fallback to another backend. r=kinetik r=glandium | |||||
| --- | |||||
| media/libcubeb/gtest/moz.build | 1 - | |||||
| media/libcubeb/src/cubeb_alsa.c | 228 +++++++++++++++++++++++++++++----------- | |||||
| toolkit/library/moz.build | 3 - | |||||
| 3 files changed, 168 insertions(+), 64 deletions(-) | |||||
| diff --git media/libcubeb/gtest/moz.build media/libcubeb/gtest/moz.build | |||||
| index 558130188c2e..0cf157d41903 100644 | |||||
| --- media/libcubeb/gtest/moz.build | |||||
| +++ media/libcubeb/gtest/moz.build | |||||
| @@ -72,7 +72,6 @@ elif CONFIG['OS_TARGET'] == 'OpenBSD': | |||||
| 'sndio', | |||||
| ] | |||||
| else: | |||||
| - OS_LIBS += CONFIG['MOZ_ALSA_LIBS'] | |||||
| OS_LIBS += CONFIG['MOZ_PULSEAUDIO_LIBS'] | |||||
| if CONFIG['CC_TYPE'] in ('clang', 'gcc'): | |||||
| diff --git media/libcubeb/src/cubeb_alsa.c media/libcubeb/src/cubeb_alsa.c | |||||
| index bfd4d8f199d4..213c1eaa3d07 100644 | |||||
| --- media/libcubeb/src/cubeb_alsa.c | |||||
| +++ media/libcubeb/src/cubeb_alsa.c | |||||
| @@ -12,6 +12,7 @@ | |||||
| #include <sys/time.h> | |||||
| #include <assert.h> | |||||
| #include <limits.h> | |||||
| +#include <dlfcn.h> | |||||
| #include <poll.h> | |||||
| #include <unistd.h> | |||||
| #include <alsa/asoundlib.h> | |||||
| @@ -25,6 +26,52 @@ | |||||
| #define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin" | |||||
| +#ifdef DISABLE_LIBASOUND_DLOPEN | |||||
| +#define WRAP(x) x | |||||
| +#else | |||||
| +#define WRAP(x) cubeb_##x | |||||
| +#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x | |||||
| +MAKE_TYPEDEF(snd_config); | |||||
| +MAKE_TYPEDEF(snd_config_add); | |||||
| +MAKE_TYPEDEF(snd_config_copy); | |||||
| +MAKE_TYPEDEF(snd_config_delete); | |||||
| +MAKE_TYPEDEF(snd_config_get_id); | |||||
| +MAKE_TYPEDEF(snd_config_get_string); | |||||
| +MAKE_TYPEDEF(snd_config_imake_integer); | |||||
| +MAKE_TYPEDEF(snd_config_search); | |||||
| +MAKE_TYPEDEF(snd_config_search_definition); | |||||
| +MAKE_TYPEDEF(snd_lib_error_set_handler); | |||||
| +MAKE_TYPEDEF(snd_pcm_avail_update); | |||||
| +MAKE_TYPEDEF(snd_pcm_close); | |||||
| +MAKE_TYPEDEF(snd_pcm_delay); | |||||
| +MAKE_TYPEDEF(snd_pcm_drain); | |||||
| +MAKE_TYPEDEF(snd_pcm_frames_to_bytes); | |||||
| +MAKE_TYPEDEF(snd_pcm_get_params); | |||||
| +/* snd_pcm_hw_params_alloca is actually a macro */ | |||||
| +/* MAKE_TYPEDEF(snd_pcm_hw_params_alloca); */ | |||||
| +MAKE_TYPEDEF(snd_pcm_hw_params_sizeof); | |||||
| +#define snd_pcm_hw_params_sizeof cubeb_snd_pcm_hw_params_sizeof | |||||
| +MAKE_TYPEDEF(snd_pcm_hw_params_any); | |||||
| +MAKE_TYPEDEF(snd_pcm_hw_params_get_channels_max); | |||||
| +MAKE_TYPEDEF(snd_pcm_hw_params_get_rate); | |||||
| +MAKE_TYPEDEF(snd_pcm_hw_params_set_rate_near); | |||||
| +MAKE_TYPEDEF(snd_pcm_nonblock); | |||||
| +MAKE_TYPEDEF(snd_pcm_open); | |||||
| +MAKE_TYPEDEF(snd_pcm_open_lconf); | |||||
| +MAKE_TYPEDEF(snd_pcm_pause); | |||||
| +MAKE_TYPEDEF(snd_pcm_poll_descriptors); | |||||
| +MAKE_TYPEDEF(snd_pcm_poll_descriptors_count); | |||||
| +MAKE_TYPEDEF(snd_pcm_poll_descriptors_revents); | |||||
| +MAKE_TYPEDEF(snd_pcm_readi); | |||||
| +MAKE_TYPEDEF(snd_pcm_recover); | |||||
| +MAKE_TYPEDEF(snd_pcm_set_params); | |||||
| +MAKE_TYPEDEF(snd_pcm_start); | |||||
| +MAKE_TYPEDEF(snd_pcm_state); | |||||
| +MAKE_TYPEDEF(snd_pcm_writei); | |||||
| + | |||||
| +#undef MAKE_TYPEDEF | |||||
| +#endif | |||||
| + | |||||
| /* ALSA is not thread-safe. snd_pcm_t instances are individually protected | |||||
| by the owning cubeb_stream's mutex. snd_pcm_t creation and destruction | |||||
| is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1), | |||||
| @@ -65,6 +112,8 @@ struct cubeb { | |||||
| workaround is not required. */ | |||||
| snd_config_t * local_config; | |||||
| int is_pa; | |||||
| + | |||||
| + void * libasound; | |||||
| }; | |||||
| enum stream_state { | |||||
| @@ -245,8 +294,8 @@ set_timeout(struct timeval * timeout, unsigned int ms) | |||||
| static void | |||||
| stream_buffer_decrement(cubeb_stream * stm, long count) | |||||
| { | |||||
| - char * bufremains = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, count); | |||||
| - memmove(stm->buffer, bufremains, snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes - count)); | |||||
| + char * bufremains = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count); | |||||
| + memmove(stm->buffer, bufremains, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count)); | |||||
| stm->bufframes -= count; | |||||
| } | |||||
| @@ -278,9 +327,9 @@ alsa_process_stream(cubeb_stream * stm) | |||||
| /* Call _poll_descriptors_revents() even if we don't use it | |||||
| to let underlying plugins clear null events. Otherwise poll() | |||||
| may wake up again and again, producing unnecessary CPU usage. */ | |||||
| - snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents); | |||||
| + WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents); | |||||
| - avail = snd_pcm_avail_update(stm->pcm); | |||||
| + avail = WRAP(snd_pcm_avail_update)(stm->pcm); | |||||
| /* Got null event? Bail and wait for another wakeup. */ | |||||
| if (avail == 0) { | |||||
| @@ -303,7 +352,7 @@ alsa_process_stream(cubeb_stream * stm) | |||||
| // TODO: should it be marked as DRAINING? | |||||
| } | |||||
| - got = snd_pcm_readi(stm->pcm, stm->buffer+stm->bufframes, avail); | |||||
| + got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer+stm->bufframes, avail); | |||||
| if (got < 0) { | |||||
| avail = got; // the error handler below will recover us | |||||
| @@ -347,7 +396,7 @@ alsa_process_stream(cubeb_stream * stm) | |||||
| (!stm->other_stream || stm->other_stream->bufframes > 0)) { | |||||
| long got = avail - stm->bufframes; | |||||
| void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL; | |||||
| - char * buftail = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes); | |||||
| + char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes); | |||||
| /* Correct read size to the other stream available frames */ | |||||
| if (stm->other_stream && got > (snd_pcm_sframes_t) stm->other_stream->bufframes) { | |||||
| @@ -374,8 +423,8 @@ alsa_process_stream(cubeb_stream * stm) | |||||
| long drain_frames = avail - stm->bufframes; | |||||
| double drain_time = (double) drain_frames / stm->params.rate; | |||||
| - char * buftail = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes); | |||||
| - memset(buftail, 0, snd_pcm_frames_to_bytes(stm->pcm, drain_frames)); | |||||
| + char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes); | |||||
| + memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames)); | |||||
| stm->bufframes = avail; | |||||
| /* Mark as draining, unless we're waiting for capture */ | |||||
| @@ -402,7 +451,7 @@ alsa_process_stream(cubeb_stream * stm) | |||||
| } | |||||
| } | |||||
| - wrote = snd_pcm_writei(stm->pcm, stm->buffer, avail); | |||||
| + wrote = WRAP(snd_pcm_writei)(stm->pcm, stm->buffer, avail); | |||||
| if (wrote < 0) { | |||||
| avail = wrote; // the error handler below will recover us | |||||
| } else { | |||||
| @@ -415,13 +464,13 @@ alsa_process_stream(cubeb_stream * stm) | |||||
| /* Got some error? Let's try to recover the stream. */ | |||||
| if (avail < 0) { | |||||
| - avail = snd_pcm_recover(stm->pcm, avail, 0); | |||||
| + avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0); | |||||
| /* Capture pcm must be started after initial setup/recover */ | |||||
| if (avail >= 0 && | |||||
| stm->stream_type == SND_PCM_STREAM_CAPTURE && | |||||
| - snd_pcm_state(stm->pcm) == SND_PCM_STATE_PREPARED) { | |||||
| - avail = snd_pcm_start(stm->pcm); | |||||
| + WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) { | |||||
| + avail = WRAP(snd_pcm_start)(stm->pcm); | |||||
| } | |||||
| } | |||||
| @@ -537,26 +586,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; | |||||
| } | |||||
| @@ -565,7 +614,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; | |||||
| } | |||||
| @@ -574,7 +623,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; | |||||
| @@ -597,22 +646,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; | |||||
| } | |||||
| @@ -621,7 +670,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; | |||||
| } | |||||
| @@ -632,12 +681,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; | |||||
| } | |||||
| @@ -648,18 +697,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; | |||||
| } | |||||
| @@ -667,7 +716,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; | |||||
| } | |||||
| @@ -679,9 +728,9 @@ alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name, snd_pcm_stream_t s | |||||
| pthread_mutex_lock(&cubeb_alsa_mutex); | |||||
| if (local_config) { | |||||
| - r = snd_pcm_open_lconf(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config); | |||||
| + r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config); | |||||
| } else { | |||||
| - r = snd_pcm_open(pcm, pcm_name, stream, SND_PCM_NONBLOCK); | |||||
| + r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK); | |||||
| } | |||||
| pthread_mutex_unlock(&cubeb_alsa_mutex); | |||||
| @@ -694,7 +743,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; | |||||
| @@ -757,12 +806,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); | |||||
| @@ -770,6 +872,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); | |||||
| @@ -819,7 +923,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) { | |||||
| @@ -859,9 +963,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); | |||||
| } | |||||
| @@ -948,7 +1056,7 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream | |||||
| return CUBEB_ERROR; | |||||
| } | |||||
| - r = snd_pcm_nonblock(stm->pcm, 1); | |||||
| + r = WRAP(snd_pcm_nonblock)(stm->pcm, 1); | |||||
| assert(r == 0); | |||||
| latency_us = latency_frames * 1e6 / stm->params.rate; | |||||
| @@ -961,7 +1069,7 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream | |||||
| latency_us = latency_us < min_latency ? min_latency: latency_us; | |||||
| } | |||||
| - r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED, | |||||
| + r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED, | |||||
| stm->params.channels, stm->params.rate, 1, | |||||
| latency_us); | |||||
| if (r < 0) { | |||||
| @@ -969,20 +1077,20 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream | |||||
| return CUBEB_ERROR_INVALID_FORMAT; | |||||
| } | |||||
| - r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &period_size); | |||||
| + r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size); | |||||
| assert(r == 0); | |||||
| /* Double internal buffer size to have enough space when waiting for the other side of duplex connection */ | |||||
| stm->buffer_size *= 2; | |||||
| - stm->buffer = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, stm->buffer_size)); | |||||
| + stm->buffer = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size)); | |||||
| assert(stm->buffer); | |||||
| - stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm); | |||||
| + stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm); | |||||
| assert(stm->nfds > 0); | |||||
| stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd)); | |||||
| assert(stm->saved_fds); | |||||
| - r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds); | |||||
| + r = WRAP(snd_pcm_poll_descriptors)(stm->pcm, stm->saved_fds, stm->nfds); | |||||
| assert((nfds_t) r == stm->nfds); | |||||
| if (alsa_register_stream(ctx, stm) != 0) { | |||||
| @@ -1054,7 +1162,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; | |||||
| @@ -1100,12 +1208,12 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) | |||||
| assert(stm); | |||||
| - r = snd_pcm_hw_params_any(stm->pcm, hw_params); | |||||
| + r = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params); | |||||
| if (r < 0) { | |||||
| return CUBEB_ERROR; | |||||
| } | |||||
| - r = snd_pcm_hw_params_get_channels_max(hw_params, max_channels); | |||||
| + r = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels); | |||||
| if (r < 0) { | |||||
| return CUBEB_ERROR; | |||||
| } | |||||
| @@ -1126,34 +1234,34 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { | |||||
| /* get a pcm, disabling resampling, so we get a rate the | |||||
| * hardware/dmix/pulse/etc. supports. */ | |||||
| - r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE); | |||||
| + r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE); | |||||
| if (r < 0) { | |||||
| return CUBEB_ERROR; | |||||
| } | |||||
| - r = snd_pcm_hw_params_any(pcm, hw_params); | |||||
| + r = WRAP(snd_pcm_hw_params_any)(pcm, hw_params); | |||||
| if (r < 0) { | |||||
| - snd_pcm_close(pcm); | |||||
| + WRAP(snd_pcm_close)(pcm); | |||||
| return CUBEB_ERROR; | |||||
| } | |||||
| - r = snd_pcm_hw_params_get_rate(hw_params, rate, &dir); | |||||
| + r = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir); | |||||
| if (r >= 0) { | |||||
| /* There is a default rate: use it. */ | |||||
| - snd_pcm_close(pcm); | |||||
| + WRAP(snd_pcm_close)(pcm); | |||||
| return CUBEB_OK; | |||||
| } | |||||
| /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */ | |||||
| *rate = 44100; | |||||
| - r = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL); | |||||
| + r = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL); | |||||
| if (r < 0) { | |||||
| - snd_pcm_close(pcm); | |||||
| + WRAP(snd_pcm_close)(pcm); | |||||
| return CUBEB_ERROR; | |||||
| } | |||||
| - snd_pcm_close(pcm); | |||||
| + WRAP(snd_pcm_close)(pcm); | |||||
| return CUBEB_OK; | |||||
| } | |||||
| @@ -1186,10 +1294,10 @@ alsa_stream_start(cubeb_stream * stm) | |||||
| pthread_mutex_lock(&stm->mutex); | |||||
| /* Capture pcm must be started after initial setup/recover */ | |||||
| if (stm->stream_type == SND_PCM_STREAM_CAPTURE && | |||||
| - snd_pcm_state(stm->pcm) == SND_PCM_STATE_PREPARED) { | |||||
| - snd_pcm_start(stm->pcm); | |||||
| + WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) { | |||||
| + WRAP(snd_pcm_start)(stm->pcm); | |||||
| } | |||||
| - snd_pcm_pause(stm->pcm, 0); | |||||
| + WRAP(snd_pcm_pause)(stm->pcm, 0); | |||||
| gettimeofday(&stm->last_activity, NULL); | |||||
| pthread_mutex_unlock(&stm->mutex); | |||||
| @@ -1229,7 +1337,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; | |||||
| @@ -1245,8 +1353,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; | |||||
| @@ -1271,7 +1379,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 toolkit/library/moz.build toolkit/library/moz.build | |||||
| index b0df6b98b91f..e06592daa265 100644 | |||||
| --- toolkit/library/moz.build | |||||
| +++ toolkit/library/moz.build | |||||
| @@ -247,9 +247,6 @@ if CONFIG['MOZ_SYSTEM_LIBVPX']: | |||||
| if not CONFIG['MOZ_TREE_PIXMAN']: | |||||
| OS_LIBS += CONFIG['MOZ_PIXMAN_LIBS'] | |||||
| -if CONFIG['MOZ_ALSA']: | |||||
| - OS_LIBS += CONFIG['MOZ_ALSA_LIBS'] | |||||
| - | |||||
| if CONFIG['HAVE_CLOCK_MONOTONIC']: | |||||
| OS_LIBS += CONFIG['REALTIME_LIBS'] | |||||
| commit 161bcd671217 | |||||
| Author: Evgeniy Vodolazskiy <waterlaz@gmail.com> | |||||
| Date: Wed Sep 3 10:47:00 2014 -0700 | |||||
| Bug 1021761 - Add OSS backend to libcubeb, default but last on Linux. r=kinetik r=glandium | |||||
| --- | |||||
| build/moz.configure/old.configure | 1 + | |||||
| dom/media/CubebUtils.cpp | 3 +- | |||||
| media/libcubeb/AUTHORS | 1 + | |||||
| media/libcubeb/src/cubeb.c | 10 + | |||||
| media/libcubeb/src/cubeb_oss.c | 453 ++++++++++++++++++++++++++++++++++++++ | |||||
| media/libcubeb/src/moz.build | 7 + | |||||
| media/libcubeb/update.sh | 1 + | |||||
| old-configure.in | 62 ++++++ | |||||
| toolkit/library/moz.build | 3 + | |||||
| 9 files changed, 540 insertions(+), 1 deletion(-) | |||||
| diff --git build/moz.configure/old.configure build/moz.configure/old.configure | |||||
| index 17d0c5bf3420..3e6dbc16ca14 100644 | |||||
| --- build/moz.configure/old.configure | |||||
| +++ build/moz.configure/old.configure | |||||
| @@ -262,6 +262,7 @@ def old_configure_options(*options): | |||||
| '--with-nspr-prefix', | |||||
| '--with-nss-exec-prefix', | |||||
| '--with-nss-prefix', | |||||
| + '--with-oss', | |||||
| '--with-pthreads', | |||||
| '--with-qemu-exe', | |||||
| '--with-sixgill', | |||||
| diff --git dom/media/CubebUtils.cpp dom/media/CubebUtils.cpp | |||||
| index 88063ed3a4d6..8613f86dbd16 100644 | |||||
| --- dom/media/CubebUtils.cpp | |||||
| +++ dom/media/CubebUtils.cpp | |||||
| @@ -149,7 +149,8 @@ const char* AUDIOSTREAM_BACKEND_ID_STR[] = { | |||||
| "sndio", | |||||
| "opensl", | |||||
| "audiotrack", | |||||
| - "kai" | |||||
| + "kai", | |||||
| + "oss", | |||||
| }; | |||||
| /* Index for failures to create an audio stream the first time. */ | |||||
| const int CUBEB_BACKEND_INIT_FAILURE_FIRST = | |||||
| diff --git media/libcubeb/AUTHORS media/libcubeb/AUTHORS | |||||
| index f0f9595227f2..e7e7048190ab 100644 | |||||
| --- media/libcubeb/AUTHORS | |||||
| +++ media/libcubeb/AUTHORS | |||||
| @@ -4,6 +4,7 @@ Michael Wu <mwu@mozilla.com> | |||||
| Paul Adenot <paul@paul.cx> | |||||
| David Richards <drichards@mozilla.com> | |||||
| Sebastien Alaiwan <sebastien.alaiwan@gmail.com> | |||||
| +Evgeniy Vodolazskiy <waterlaz@gmail.com> | |||||
| KO Myung-Hun <komh@chollian.net> | |||||
| Haakon Sporsheim <haakon.sporsheim@telenordigital.com> | |||||
| Alex Chronopoulos <achronop@gmail.com> | |||||
| diff --git media/libcubeb/src/cubeb.c media/libcubeb/src/cubeb.c | |||||
| index bb35e0ce349f..e523d94108a3 100644 | |||||
| --- media/libcubeb/src/cubeb.c | |||||
| +++ media/libcubeb/src/cubeb.c | |||||
| @@ -60,6 +60,9 @@ int audiotrack_init(cubeb ** context, char const * context_name); | |||||
| #if defined(USE_KAI) | |||||
| int kai_init(cubeb ** context, char const * context_name); | |||||
| #endif | |||||
| +#if defined(USE_OSS) | |||||
| +int oss_init(cubeb ** context, char const * context_name); | |||||
| +#endif | |||||
| static int | |||||
| validate_stream_params(cubeb_stream_params * input_stream_params, | |||||
| @@ -159,6 +162,10 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam | |||||
| } else if (!strcmp(backend_name, "kai")) { | |||||
| #if defined(USE_KAI) | |||||
| init_oneshot = kai_init; | |||||
| +#endif | |||||
| + } else if (!strcmp(backend_name, "oss")) { | |||||
| +#if defined(USE_OSS) | |||||
| + init_oneshot = oss_init; | |||||
| #endif | |||||
| } else { | |||||
| /* Already set */ | |||||
| @@ -203,6 +210,9 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam | |||||
| #endif | |||||
| #if defined(USE_KAI) | |||||
| kai_init, | |||||
| +#endif | |||||
| +#if defined(USE_OSS) | |||||
| + oss_init, | |||||
| #endif | |||||
| }; | |||||
| int i; | |||||
| diff --git media/libcubeb/src/cubeb_oss.c media/libcubeb/src/cubeb_oss.c | |||||
| new file mode 100644 | |||||
| index 000000000000..7d96168b9ea6 | |||||
| --- /dev/null | |||||
| +++ media/libcubeb/src/cubeb_oss.c | |||||
| @@ -0,0 +1,454 @@ | |||||
| +/* | |||||
| + * Copyright © 2014 Mozilla Foundation | |||||
| + * | |||||
| + * This program is made available under an ISC-style license. See the | |||||
| + * accompanying file LICENSE for details. | |||||
| + */ | |||||
| +#if defined(HAVE_SYS_SOUNDCARD_H) | |||||
| +#include <sys/soundcard.h> | |||||
| +#else | |||||
| +#include <soundcard.h> | |||||
| +#endif | |||||
| +#include <unistd.h> | |||||
| +#include <stdlib.h> | |||||
| +#include <sys/types.h> | |||||
| +#include <sys/stat.h> | |||||
| +#include <fcntl.h> | |||||
| +#include <sys/ioctl.h> | |||||
| +#include <errno.h> | |||||
| +#include <pthread.h> | |||||
| +#include <stdio.h> | |||||
| +#include <assert.h> | |||||
| + | |||||
| +#include "cubeb/cubeb.h" | |||||
| +#include "cubeb-internal.h" | |||||
| + | |||||
| +#ifndef CUBEB_OSS_DEFAULT_OUTPUT | |||||
| +#define CUBEB_OSS_DEFAULT_OUTPUT "/dev/dsp" | |||||
| +#endif | |||||
| + | |||||
| +#define OSS_BUFFER_SIZE 1024 | |||||
| + | |||||
| +struct cubeb { | |||||
| + struct cubeb_ops const * ops; | |||||
| +}; | |||||
| + | |||||
| +struct cubeb_stream { | |||||
| + /* Note: Must match cubeb_stream layout in cubeb.c. */ | |||||
| + cubeb * context; | |||||
| + void * user_ptr; | |||||
| + /**/ | |||||
| + | |||||
| + cubeb_data_callback data_callback; | |||||
| + cubeb_state_callback state_callback; | |||||
| + float volume; | |||||
| + float panning; | |||||
| + | |||||
| + pthread_mutex_t state_mutex; | |||||
| + pthread_cond_t state_cond; | |||||
| + | |||||
| + int running; | |||||
| + int stopped; | |||||
| + int floating; | |||||
| + | |||||
| + /* These two vars are needed to support old versions of OSS */ | |||||
| + unsigned int position_bytes; | |||||
| + unsigned int last_position_bytes; | |||||
| + | |||||
| + uint64_t written_frags; /* The number of fragments written to /dev/dsp */ | |||||
| + uint64_t missed_frags; /* fragments output with stopped stream */ | |||||
| + | |||||
| + cubeb_stream_params params; | |||||
| + int fd; | |||||
| + pthread_t th; | |||||
| +}; | |||||
| + | |||||
| +static struct cubeb_ops const oss_ops; | |||||
| + | |||||
| +int oss_init(cubeb ** context, char const * context_name) | |||||
| +{ | |||||
| + cubeb* ctx = (cubeb*)malloc(sizeof(cubeb)); | |||||
| + ctx->ops = &oss_ops; | |||||
| + *context = ctx; | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| +static void oss_destroy(cubeb *ctx) | |||||
| +{ | |||||
| + free(ctx); | |||||
| +} | |||||
| + | |||||
| +static char const * oss_get_backend_id(cubeb * context) | |||||
| +{ | |||||
| + static char oss_name[] = "oss"; | |||||
| + return oss_name; | |||||
| +} | |||||
| + | |||||
| +static int oss_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) | |||||
| +{ | |||||
| + *max_channels = 2; /* Let's support only stereo for now */ | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| +static int oss_get_min_latency(cubeb * context, cubeb_stream_params params, | |||||
| + uint32_t * latency_frames) | |||||
| +{ | |||||
| + (void)context; | |||||
| + /* 40ms is a big enough number to work ok */ | |||||
| + *latency_frames = 40 * params.rate / 1000; | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| +static int oss_get_preferred_sample_rate(cubeb *context, uint32_t * rate) | |||||
| +{ | |||||
| + /* 48000 seems a prefered choice for most audio devices | |||||
| + * and a good choice for OSS */ | |||||
| + *rate = 48000; | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| +static void run_state_callback(cubeb_stream *stream, cubeb_state state) | |||||
| +{ | |||||
| + if (stream->state_callback) { | |||||
| + stream->state_callback(stream, stream->user_ptr, state); | |||||
| + } | |||||
| +} | |||||
| + | |||||
| +static long run_data_callback(cubeb_stream *stream, void *buffer, long nframes) | |||||
| +{ | |||||
| + long got = 0; | |||||
| + pthread_mutex_lock(&stream->state_mutex); | |||||
| + if (stream->data_callback && stream->running && !stream->stopped) { | |||||
| + pthread_mutex_unlock(&stream->state_mutex); | |||||
| + got = stream->data_callback(stream, stream->user_ptr, NULL, buffer, nframes); | |||||
| + } else { | |||||
| + pthread_mutex_unlock(&stream->state_mutex); | |||||
| + } | |||||
| + return got; | |||||
| +} | |||||
| + | |||||
| +static void apply_volume_int(int16_t* buffer, unsigned int n, | |||||
| + float volume, float panning) | |||||
| +{ | |||||
| + float left = volume; | |||||
| + float right = volume; | |||||
| + unsigned int i; | |||||
| + int pan[2]; | |||||
| + if (panning<0) { | |||||
| + right *= (1+panning); | |||||
| + } else { | |||||
| + left *= (1-panning); | |||||
| + } | |||||
| + pan[0] = 128.0*left; | |||||
| + pan[1] = 128.0*right; | |||||
| + for(i=0; i<n; i++){ | |||||
| + buffer[i] = ((int)buffer[i])*pan[i%2]/128; | |||||
| + } | |||||
| +} | |||||
| + | |||||
| +static void apply_volume_float(float* buffer, unsigned int n, | |||||
| + float volume, float panning) | |||||
| +{ | |||||
| + float left = volume; | |||||
| + float right = volume; | |||||
| + unsigned int i; | |||||
| + float pan[2]; | |||||
| + if (panning<0) { | |||||
| + right *= (1+panning); | |||||
| + } else { | |||||
| + left *= (1-panning); | |||||
| + } | |||||
| + pan[0] = left; | |||||
| + pan[1] = right; | |||||
| + for(i=0; i<n; i++){ | |||||
| + buffer[i] = buffer[i]*pan[i%2]; | |||||
| + } | |||||
| +} | |||||
| + | |||||
| + | |||||
| +static void *writer(void *stm) | |||||
| +{ | |||||
| + cubeb_stream* stream = (cubeb_stream*)stm; | |||||
| + int16_t buffer[OSS_BUFFER_SIZE]; | |||||
| + float f_buffer[OSS_BUFFER_SIZE]; | |||||
| + int got; | |||||
| + unsigned long i; | |||||
| + while (stream->running) { | |||||
| + pthread_mutex_lock(&stream->state_mutex); | |||||
| + if (stream->stopped) { | |||||
| + pthread_mutex_unlock(&stream->state_mutex); | |||||
| + run_state_callback(stream, CUBEB_STATE_STOPPED); | |||||
| + pthread_mutex_lock(&stream->state_mutex); | |||||
| + while (stream->stopped) { | |||||
| + pthread_cond_wait(&stream->state_cond, &stream->state_mutex); | |||||
| + } | |||||
| + pthread_mutex_unlock(&stream->state_mutex); | |||||
| + run_state_callback(stream, CUBEB_STATE_STARTED); | |||||
| + continue; | |||||
| + } | |||||
| + pthread_mutex_unlock(&stream->state_mutex); | |||||
| + if (stream->floating) { | |||||
| + got = run_data_callback(stream, f_buffer, | |||||
| + OSS_BUFFER_SIZE/stream->params.channels); | |||||
| + apply_volume_float(f_buffer, got*stream->params.channels, | |||||
| + stream->volume, stream->panning); | |||||
| + for (i=0; i<((unsigned long)got)*stream->params.channels; i++) { | |||||
| + /* Clipping is prefered to overflow */ | |||||
| + if(f_buffer[i]>=1.0){ | |||||
| + f_buffer[i]=1.0; | |||||
| + } | |||||
| + if(f_buffer[i]<=-1.0){ | |||||
| + f_buffer[i]=-1.0; | |||||
| + } | |||||
| + /* One might think that multipling by 32767.0 is logical but results in clipping */ | |||||
| + buffer[i] = f_buffer[i]*32767.0; | |||||
| + } | |||||
| + } else { | |||||
| + got = run_data_callback(stream, buffer, | |||||
| + OSS_BUFFER_SIZE/stream->params.channels); | |||||
| + apply_volume_int(buffer, got*stream->params.channels, | |||||
| + stream->volume, stream->panning); | |||||
| + } | |||||
| + if (got<0) { | |||||
| + run_state_callback(stream, CUBEB_STATE_ERROR); | |||||
| + break; | |||||
| + } | |||||
| + if (!got) { | |||||
| + run_state_callback(stream, CUBEB_STATE_DRAINED); | |||||
| + } | |||||
| + if (got) { | |||||
| + size_t i = 0; | |||||
| + size_t s = got*stream->params.channels*sizeof(int16_t); | |||||
| + while (i < s) { | |||||
| + ssize_t n = write(stream->fd, ((char*)buffer) + i, s - i); | |||||
| + if (n<=0) { | |||||
| + run_state_callback(stream, CUBEB_STATE_ERROR); | |||||
| + break; | |||||
| + } | |||||
| + i+=n; | |||||
| + } | |||||
| + stream->written_frags+=got; | |||||
| + } | |||||
| + } | |||||
| + return NULL; | |||||
| +} | |||||
| + | |||||
| +static void oss_try_set_latency(cubeb_stream* stream, unsigned int latency) | |||||
| +{ | |||||
| + unsigned int latency_bytes, n_frag; | |||||
| + int frag; | |||||
| + /* fragment size of 1024 is a good choice with good chances to be accepted */ | |||||
| + unsigned int frag_log=10; /* 2^frag_log = fragment size */ | |||||
| + latency_bytes = | |||||
| + latency*stream->params.rate*stream->params.channels*sizeof(uint16_t)/1000; | |||||
| + n_frag = latency_bytes>>frag_log; | |||||
| + frag = (n_frag<<16) | frag_log; | |||||
| + /* Even if this fails we wish to continue, not checking for errors */ | |||||
| + ioctl(stream->fd, SNDCTL_DSP_SETFRAGMENT, &frag); | |||||
| +} | |||||
| + | |||||
| +static int oss_stream_init(cubeb * context, cubeb_stream ** stm, | |||||
| + char const * stream_name, | |||||
| + cubeb_devid input_device, | |||||
| + cubeb_stream_params * input_stream_params, | |||||
| + cubeb_devid output_device, | |||||
| + cubeb_stream_params * output_stream_params, | |||||
| + unsigned int latency, | |||||
| + cubeb_data_callback data_callback, | |||||
| + cubeb_state_callback state_callback, void * user_ptr) | |||||
| +{ | |||||
| + cubeb_stream* stream = (cubeb_stream*)malloc(sizeof(cubeb_stream)); | |||||
| + stream->context = context; | |||||
| + stream->data_callback = data_callback; | |||||
| + stream->state_callback = state_callback; | |||||
| + stream->user_ptr = user_ptr; | |||||
| + | |||||
| + assert(!input_stream_params && "not supported."); | |||||
| + if (input_device || output_device) { | |||||
| + /* Device selection not yet implemented. */ | |||||
| + return CUBEB_ERROR_DEVICE_UNAVAILABLE; | |||||
| + } | |||||
| + | |||||
| + if ((input_stream_params && input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) || | |||||
| + (output_stream_params && output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) { | |||||
| + return CUBEB_ERROR_NOT_SUPPORTED; | |||||
| + } | |||||
| + | |||||
| + if ((stream->fd = open(CUBEB_OSS_DEFAULT_OUTPUT, O_WRONLY)) == -1) { | |||||
| + free(stream); | |||||
| + return CUBEB_ERROR; | |||||
| + } | |||||
| +#define SET(what, to) do { unsigned int i = to; \ | |||||
| + int j = ioctl(stream->fd, what, &i); \ | |||||
| + if (j == -1 || i != to) { \ | |||||
| + close(stream->fd); \ | |||||
| + free(stream); \ | |||||
| + return CUBEB_ERROR_INVALID_FORMAT; } } while (0) | |||||
| + | |||||
| + stream->params = *output_stream_params; | |||||
| + stream->volume = 1.0; | |||||
| + stream->panning = 0.0; | |||||
| + | |||||
| + oss_try_set_latency(stream, latency); | |||||
| + | |||||
| + stream->floating = 0; | |||||
| + SET(SNDCTL_DSP_CHANNELS, stream->params.channels); | |||||
| + SET(SNDCTL_DSP_SPEED, stream->params.rate); | |||||
| + switch (stream->params.format) { | |||||
| + case CUBEB_SAMPLE_S16LE: | |||||
| + SET(SNDCTL_DSP_SETFMT, AFMT_S16_LE); | |||||
| + break; | |||||
| + case CUBEB_SAMPLE_S16BE: | |||||
| + SET(SNDCTL_DSP_SETFMT, AFMT_S16_BE); | |||||
| + break; | |||||
| + case CUBEB_SAMPLE_FLOAT32LE: | |||||
| + SET(SNDCTL_DSP_SETFMT, AFMT_S16_NE); | |||||
| + stream->floating = 1; | |||||
| + break; | |||||
| + default: | |||||
| + close(stream->fd); | |||||
| + free(stream); | |||||
| + return CUBEB_ERROR; | |||||
| + } | |||||
| + | |||||
| + | |||||
| + pthread_mutex_init(&stream->state_mutex, NULL); | |||||
| + pthread_cond_init(&stream->state_cond, NULL); | |||||
| + | |||||
| + stream->running = 1; | |||||
| + stream->stopped = 1; | |||||
| + stream->position_bytes = 0; | |||||
| + stream->last_position_bytes = 0; | |||||
| + stream->written_frags = 0; | |||||
| + stream->missed_frags = 0; | |||||
| + | |||||
| + pthread_create(&stream->th, NULL, writer, (void*)stream); | |||||
| + | |||||
| + *stm = stream; | |||||
| + | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| +static void oss_stream_destroy(cubeb_stream * stream) | |||||
| +{ | |||||
| + pthread_mutex_lock(&stream->state_mutex); | |||||
| + | |||||
| + stream->running = 0; | |||||
| + stream->stopped = 0; | |||||
| + pthread_cond_signal(&stream->state_cond); | |||||
| + | |||||
| + pthread_mutex_unlock(&stream->state_mutex); | |||||
| + | |||||
| + pthread_join(stream->th, NULL); | |||||
| + | |||||
| + pthread_mutex_destroy(&stream->state_mutex); | |||||
| + pthread_cond_destroy(&stream->state_cond); | |||||
| + close(stream->fd); | |||||
| + free(stream); | |||||
| +} | |||||
| + | |||||
| +static int oss_stream_get_latency(cubeb_stream * stream, uint32_t * latency) | |||||
| +{ | |||||
| + if (ioctl(stream->fd, SNDCTL_DSP_GETODELAY, latency)==-1) { | |||||
| + return CUBEB_ERROR; | |||||
| + } | |||||
| + /* Convert latency from bytes to frames */ | |||||
| + *latency /= stream->params.channels*sizeof(int16_t); | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| + | |||||
| +static int oss_stream_current_optr(cubeb_stream * stream, uint64_t * position) | |||||
| +{ | |||||
| + count_info ci; | |||||
| + /* Unfortunately, this ioctl is only available in OSS 4.x */ | |||||
| +#ifdef SNDCTL_DSP_CURRENT_OPTR | |||||
| + oss_count_t count; | |||||
| + if (ioctl(stream->fd, SNDCTL_DSP_CURRENT_OPTR, &count) != -1) { | |||||
| + *position = count.samples;// + count.fifo_samples; | |||||
| + return CUBEB_OK; | |||||
| + } | |||||
| +#endif | |||||
| + /* Fall back to this ioctl in case the previous one fails */ | |||||
| + if (ioctl(stream->fd, SNDCTL_DSP_GETOPTR, &ci) == -1) { | |||||
| + return CUBEB_ERROR; | |||||
| + } | |||||
| + /* ci.bytes is only 32 bit and will start to wrap after arithmetic overflow */ | |||||
| + stream->position_bytes += ci.bytes - stream->last_position_bytes; | |||||
| + stream->last_position_bytes = ci.bytes; | |||||
| + *position = stream->position_bytes/stream->params.channels/sizeof(int16_t); | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| +static int oss_stream_get_position(cubeb_stream * stream, uint64_t * position) | |||||
| +{ | |||||
| + if ( oss_stream_current_optr(stream, position) == CUBEB_OK ){ | |||||
| + *position -= stream->missed_frags; | |||||
| + return CUBEB_OK; | |||||
| + } | |||||
| + /* If no correct method to get position works we resort to this */ | |||||
| + *position = stream->written_frags; | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| + | |||||
| +static int oss_stream_start(cubeb_stream * stream) | |||||
| +{ | |||||
| + pthread_mutex_lock(&stream->state_mutex); | |||||
| + if (stream->stopped) { | |||||
| + uint64_t ptr; | |||||
| + oss_stream_current_optr(stream, &ptr); | |||||
| + stream->missed_frags = ptr - stream->written_frags; | |||||
| + stream->stopped = 0; | |||||
| + pthread_cond_signal(&stream->state_cond); | |||||
| + } | |||||
| + pthread_mutex_unlock(&stream->state_mutex); | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| +static int oss_stream_stop(cubeb_stream * stream) | |||||
| +{ | |||||
| + pthread_mutex_lock(&stream->state_mutex); | |||||
| + stream->stopped = 1; | |||||
| + pthread_mutex_unlock(&stream->state_mutex); | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| +int oss_stream_set_panning(cubeb_stream * stream, float panning) | |||||
| +{ | |||||
| + if (stream->params.channels == 2) { | |||||
| + stream->panning=panning; | |||||
| + } | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| +int oss_stream_set_volume(cubeb_stream * stream, float volume) | |||||
| +{ | |||||
| + stream->volume=volume; | |||||
| + return CUBEB_OK; | |||||
| +} | |||||
| + | |||||
| +static struct cubeb_ops const oss_ops = { | |||||
| + .init = oss_init, | |||||
| + .get_backend_id = oss_get_backend_id, | |||||
| + .get_max_channel_count = oss_get_max_channel_count, | |||||
| + .get_min_latency = oss_get_min_latency, | |||||
| + .get_preferred_sample_rate = oss_get_preferred_sample_rate, | |||||
| + .get_preferred_channel_layout = NULL, | |||||
| + .enumerate_devices = NULL, | |||||
| + .device_collection_destroy = NULL, | |||||
| + .destroy = oss_destroy, | |||||
| + .stream_init = oss_stream_init, | |||||
| + .stream_destroy = oss_stream_destroy, | |||||
| + .stream_start = oss_stream_start, | |||||
| + .stream_stop = oss_stream_stop, | |||||
| + .stream_reset_default_device = NULL, | |||||
| + .stream_get_position = oss_stream_get_position, | |||||
| + .stream_get_latency = oss_stream_get_latency, | |||||
| + .stream_set_volume = oss_stream_set_volume, | |||||
| + .stream_set_panning = oss_stream_set_panning, | |||||
| + .stream_get_current_device = NULL, | |||||
| + .stream_device_destroy = NULL, | |||||
| + .stream_register_device_changed_callback = NULL, | |||||
| + .register_device_collection_changed = NULL | |||||
| +}; | |||||
| diff --git media/libcubeb/src/moz.build media/libcubeb/src/moz.build | |||||
| index e1fea30ca417..a5b1100f1014 100644 | |||||
| --- media/libcubeb/src/moz.build | |||||
| +++ media/libcubeb/src/moz.build | |||||
| @@ -23,6 +23,12 @@ if CONFIG['MOZ_ALSA']: | |||||
| ] | |||||
| DEFINES['USE_ALSA'] = True | |||||
| +if CONFIG['MOZ_OSS']: | |||||
| + SOURCES += [ | |||||
| + 'cubeb_oss.c', | |||||
| + ] | |||||
| + DEFINES['USE_OSS'] = True | |||||
| + | |||||
| if CONFIG['MOZ_PULSEAUDIO'] or CONFIG['MOZ_JACK']: | |||||
| SOURCES += [ | |||||
| 'cubeb_resampler.cpp', | |||||
| @@ -88,6 +94,7 @@ if CONFIG['OS_TARGET'] == 'Android': | |||||
| FINAL_LIBRARY = 'gkmedias' | |||||
| CFLAGS += CONFIG['MOZ_ALSA_CFLAGS'] | |||||
| +CFLAGS += CONFIG['MOZ_OSS_CFLAGS'] | |||||
| CFLAGS += CONFIG['MOZ_PULSEAUDIO_CFLAGS'] | |||||
| # We allow warnings for third-party code that can be updated from upstream. | |||||
| diff --git media/libcubeb/update.sh media/libcubeb/update.sh | |||||
| index 0bb6345c9fa9..78a102dc47cb 100755 | |||||
| --- media/libcubeb/update.sh | |||||
| +++ media/libcubeb/update.sh | |||||
| @@ -20,6 +20,7 @@ cp $1/src/cubeb_log.h src | |||||
| cp $1/src/cubeb_mixer.cpp src | |||||
| cp $1/src/cubeb_mixer.h src | |||||
| cp $1/src/cubeb_opensl.c src | |||||
| +cp $1/src/cubeb_oss.c src | |||||
| cp $1/src/cubeb-jni.cpp src | |||||
| cp $1/src/cubeb-jni.h src | |||||
| cp $1/src/android/cubeb-output-latency.h src/android | |||||
| diff --git old-configure.in old-configure.in | |||||
| index 28e1a9e48d61..edacedcf6e5d 100644 | |||||
| --- old-configure.in | |||||
| +++ old-configure.in | |||||
| @@ -2598,6 +2598,67 @@ MOZ_WEBM_ENCODER=1 | |||||
| AC_DEFINE(MOZ_WEBM_ENCODER) | |||||
| AC_SUBST(MOZ_WEBM_ENCODER) | |||||
| +dnl ================================== | |||||
| +dnl = Check OSS availability | |||||
| +dnl ================================== | |||||
| + | |||||
| +dnl If using Linux, Solaris or BSDs, ensure that OSS is available | |||||
| +case "$OS_TARGET" in | |||||
| +Linux|SunOS|DragonFly|FreeBSD|NetBSD|GNU/kFreeBSD) | |||||
| + MOZ_OSS=1 | |||||
| + ;; | |||||
| +esac | |||||
| + | |||||
| +MOZ_ARG_WITH_STRING(oss, | |||||
| +[ --with-oss[=PFX] Enable OpenSoundSystem support [installed at prefix PFX]], | |||||
| + OSSPREFIX=$withval) | |||||
| + | |||||
| +if test -n "$OSSPREFIX"; then | |||||
| + if test "$OSSPREFIX" != "no"; then | |||||
| + MOZ_OSS=1 | |||||
| + else | |||||
| + MOZ_OSS= | |||||
| + fi | |||||
| +fi | |||||
| + | |||||
| +_SAVE_CFLAGS=$CFLAGS | |||||
| +_SAVE_LIBS=$LIBS | |||||
| +if test -n "$MOZ_OSS"; then | |||||
| + dnl Prefer 4Front implementation | |||||
| + AC_MSG_CHECKING([MOZ_OSS_CFLAGS]) | |||||
| + if test "$OSSPREFIX" != "yes"; then | |||||
| + oss_conf=${OSSPREFIX%/usr}/etc/oss.conf | |||||
| + if test -f "$oss_conf"; then | |||||
| + . "$oss_conf" | |||||
| + else | |||||
| + OSSLIBDIR=$OSSPREFIX/lib/oss | |||||
| + fi | |||||
| + if test -d "$OSSLIBDIR"; then | |||||
| + MOZ_OSS_CFLAGS="$MOZ_OSS_CFLAGS -I$OSSLIBDIR/include" | |||||
| + fi | |||||
| + fi | |||||
| + AC_MSG_RESULT([$MOZ_OSS_CFLAGS]) | |||||
| + | |||||
| + CFLAGS="$CFLAGS $MOZ_OSS_CFLAGS" | |||||
| + MOZ_CHECK_HEADERS(sys/soundcard.h soundcard.h) | |||||
| + | |||||
| + if test "$ac_cv_header_sys_soundcard_h" != "yes" -a \ | |||||
| + "$ac_cv_header_soundcard_h" != "yes"; then | |||||
| + AC_MSG_ERROR([Need OSS for Ogg, Wave or WebM decoding on $OS_TARGET. Disable with --without-oss.]) | |||||
| + fi | |||||
| + | |||||
| + dnl Assume NetBSD implementation over SunAudio | |||||
| + AC_CHECK_LIB(ossaudio, _oss_ioctl, | |||||
| + [AC_DEFINE_UNQUOTED(CUBEB_OSS_DEFAULT_OUTPUT, "/dev/sound") | |||||
| + MOZ_OSS_LIBS="$MOZ_OSS_LIBS -lossaudio"]) | |||||
| +fi | |||||
| +CFLAGS=$_SAVE_CFLAGS | |||||
| +LIBS=$_SAVE_LIBS | |||||
| + | |||||
| +AC_SUBST(MOZ_OSS) | |||||
| +AC_SUBST_LIST(MOZ_OSS_CFLAGS) | |||||
| +AC_SUBST_LIST(MOZ_OSS_LIBS) | |||||
| + | |||||
| dnl ================================== | |||||
| dnl = Check alsa availability on Linux | |||||
| dnl ================================== | |||||
| diff --git toolkit/library/moz.build toolkit/library/moz.build | |||||
| index e06592daa265..ce016b96c2bc 100644 | |||||
| --- toolkit/library/moz.build | |||||
| +++ toolkit/library/moz.build | |||||
| @@ -247,6 +247,9 @@ if CONFIG['MOZ_SYSTEM_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['HAVE_CLOCK_MONOTONIC']: | |||||
| OS_LIBS += CONFIG['REALTIME_LIBS'] | |||||