diff options
Diffstat (limited to 'media/libcubeb/src/cubeb_audiotrack.c')
-rw-r--r-- | media/libcubeb/src/cubeb_audiotrack.c | 438 |
1 files changed, 0 insertions, 438 deletions
diff --git a/media/libcubeb/src/cubeb_audiotrack.c b/media/libcubeb/src/cubeb_audiotrack.c deleted file mode 100644 index fe2603405e..0000000000 --- a/media/libcubeb/src/cubeb_audiotrack.c +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright © 2013 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#if !defined(NDEBUG) -#define NDEBUG -#endif -#include <assert.h> -#include <pthread.h> -#include <stdlib.h> -#include <time.h> -#include <dlfcn.h> -#include "android/log.h" - -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" -#include "android/audiotrack_definitions.h" - -#ifndef ALOG -#if defined(DEBUG) || defined(FORCE_ALOG) -#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb" , ## args) -#else -#define ALOG(args...) -#endif -#endif - -/** - * A lot of bytes for safety. It should be possible to bring this down a bit. */ -#define SIZE_AUDIOTRACK_INSTANCE 256 - -/** - * call dlsym to get the symbol |mangled_name|, handle the error and store the - * pointer in |pointer|. Because depending on Android version, we want different - * symbols, not finding a symbol is not an error. */ -#define DLSYM_DLERROR(mangled_name, pointer, lib) \ - do { \ - pointer = dlsym(lib, mangled_name); \ - if (!pointer) { \ - ALOG("error while loading %stm: %stm\n", mangled_name, dlerror()); \ - } else { \ - ALOG("%stm: OK", mangled_name); \ - } \ - } while(0); - -static struct cubeb_ops const audiotrack_ops; -void audiotrack_destroy(cubeb * context); -void audiotrack_stream_destroy(cubeb_stream * stream); - -struct AudioTrack { - /* only available on ICS and later. The second int paramter is in fact of type audio_stream_type_t. */ - /* static */ status_t (*get_min_frame_count)(int* frame_count, int stream_type, uint32_t rate); - /* if we have a recent ctor, but can't find the above symbol, we - * can get the minimum frame count with this signature, and we are - * running gingerbread. */ - /* static */ status_t (*get_min_frame_count_gingerbread)(int* frame_count, int stream_type, uint32_t rate); - void* (*ctor)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int, int); - void* (*dtor)(void* instance); - void (*start)(void* instance); - void (*pause)(void* instance); - uint32_t (*latency)(void* instance); - status_t (*check)(void* instance); - status_t (*get_position)(void* instance, uint32_t* position); - /* static */ int (*get_output_samplingrate)(int* samplerate, int stream); - status_t (*set_marker_position)(void* instance, unsigned int); - status_t (*set_volume)(void* instance, float left, float right); -}; - -struct cubeb { - struct cubeb_ops const * ops; - void * library; - struct AudioTrack klass; -}; - -struct cubeb_stream { - cubeb * context; - cubeb_stream_params params; - cubeb_data_callback data_callback; - cubeb_state_callback state_callback; - void * instance; - void * user_ptr; - /* Number of frames that have been passed to the AudioTrack callback */ - long unsigned written; - int draining; -}; - -static void -audiotrack_refill(int event, void* user, void* info) -{ - cubeb_stream * stream = user; - switch (event) { - case EVENT_MORE_DATA: { - long got = 0; - struct Buffer * b = (struct Buffer*)info; - - if (stream->draining) { - return; - } - - got = stream->data_callback(stream, stream->user_ptr, NULL, b->raw, b->frameCount); - - stream->written += got; - - if (got != (long)b->frameCount) { - stream->draining = 1; - /* set a marker so we are notified when the are done draining, that is, - * when every frame has been played by android. */ - stream->context->klass.set_marker_position(stream->instance, stream->written); - } - - break; - } - case EVENT_UNDERRUN: - ALOG("underrun in cubeb backend."); - break; - case EVENT_LOOP_END: - assert(0 && "We don't support the loop feature of audiotrack."); - break; - case EVENT_MARKER: - assert(stream->draining); - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); - break; - case EVENT_NEW_POS: - assert(0 && "We don't support the setPositionUpdatePeriod feature of audiotrack."); - break; - case EVENT_BUFFER_END: - assert(0 && "Should not happen."); - break; - } -} - -/* We are running on gingerbread if we found the gingerbread signature for - * getMinFrameCount */ -static int -audiotrack_version_is_gingerbread(cubeb * ctx) -{ - return ctx->klass.get_min_frame_count_gingerbread != NULL; -} - -int -audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int * min_frame_count) -{ - status_t status; - /* Recent Android have a getMinFrameCount method. */ - if (!audiotrack_version_is_gingerbread(ctx)) { - status = ctx->klass.get_min_frame_count(min_frame_count, params->stream_type, params->rate); - } else { - status = ctx->klass.get_min_frame_count_gingerbread(min_frame_count, params->stream_type, params->rate); - } - if (status != 0) { - ALOG("error getting the min frame count"); - return CUBEB_ERROR; - } - return CUBEB_OK; -} - -int -audiotrack_init(cubeb ** context, char const * context_name) -{ - cubeb * ctx; - struct AudioTrack* c; - - assert(context); - *context = NULL; - - ctx = calloc(1, sizeof(*ctx)); - assert(ctx); - - /* If we use an absolute path here ("/system/lib/libmedia.so"), and on Android - * 2.2, the dlopen succeeds, all the dlsym succeed, but a segfault happens on - * the first call to a dlsym'ed function. Somehow this does not happen when - * using only the name of the library. */ - ctx->library = dlopen("libmedia.so", RTLD_LAZY); - if (!ctx->library) { - ALOG("dlopen error: %s.", dlerror()); - free(ctx); - return CUBEB_ERROR; - } - - /* Recent Android first, then Gingerbread. */ - DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", ctx->klass.ctor, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library); - - DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, ctx->library); - DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check, ctx->library); - - DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", ctx->klass.get_output_samplingrate, ctx->library); - - /* |getMinFrameCount| is available on gingerbread and ICS with different signatures. */ - DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library); - if (!ctx->klass.get_min_frame_count) { - DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij", ctx->klass.get_min_frame_count_gingerbread, ctx->library); - } - - DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", ctx->klass.get_position, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", ctx->klass.set_marker_position, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrack9setVolumeEff", ctx->klass.set_volume, ctx->library); - - /* check that we have a combination of symbol that makes sense */ - c = &ctx->klass; - if(!(c->ctor && - c->dtor && c->latency && c->check && - /* at least one way to get the minimum frame count to request. */ - (c->get_min_frame_count || - c->get_min_frame_count_gingerbread) && - c->start && c->pause && c->get_position && c->set_marker_position)) { - ALOG("Could not find all the symbols we need."); - audiotrack_destroy(ctx); - return CUBEB_ERROR; - } - - ctx->ops = &audiotrack_ops; - - *context = ctx; - - return CUBEB_OK; -} - -char const * -audiotrack_get_backend_id(cubeb * context) -{ - return "audiotrack"; -} - -static int -audiotrack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) -{ - assert(ctx && max_channels); - - /* The android mixer handles up to two channels, see - http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */ - *max_channels = 2; - - return CUBEB_OK; -} - -static int -audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) -{ - /* We always use the lowest latency possible when using this backend (see - * audiotrack_stream_init), so this value is not going to be used. */ - int r; - - r = audiotrack_get_min_frame_count(ctx, ¶ms, (int *)latency_ms); - if (r != CUBEB_OK) { - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -static int -audiotrack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) -{ - status_t r; - - r = ctx->klass.get_output_samplingrate((int32_t *)rate, 3 /* MUSIC */); - - return r == 0 ? CUBEB_OK : CUBEB_ERROR; -} - -void -audiotrack_destroy(cubeb * context) -{ - assert(context); - - dlclose(context->library); - - free(context); -} - -int -audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, - cubeb_devid input_device, - cubeb_stream_params * input_stream_params, - cubeb_devid output_device, - cubeb_stream_params * output_stream_params, - unsigned int latency, - cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) -{ - cubeb_stream * stm; - int32_t channels; - uint32_t min_frame_count; - - assert(ctx && stream); - - assert(!input_stream_params && "not supported"); - if (input_device || output_device) { - /* Device selection not yet implemented. */ - return CUBEB_ERROR_DEVICE_UNAVAILABLE; - } - - if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE || - output_stream_params->format == CUBEB_SAMPLE_FLOAT32BE) { - return CUBEB_ERROR_INVALID_FORMAT; - } - - if (audiotrack_get_min_frame_count(ctx, output_stream_params, (int *)&min_frame_count)) { - return CUBEB_ERROR; - } - - stm = calloc(1, sizeof(*stm)); - assert(stm); - - stm->context = ctx; - stm->data_callback = data_callback; - stm->state_callback = state_callback; - stm->user_ptr = user_ptr; - stm->params = *output_stream_params; - - stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1); - (*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = 0xbaadbaad; - assert(stm->instance && "cubeb: EOM"); - - /* gingerbread uses old channel layout enum */ - if (audiotrack_version_is_gingerbread(ctx)) { - channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Legacy : AUDIO_CHANNEL_OUT_MONO_Legacy; - } else { - channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS; - } - - ctx->klass.ctor(stm->instance, stm->params.stream_type, stm->params.rate, - AUDIO_FORMAT_PCM_16_BIT, channels, min_frame_count, 0, - audiotrack_refill, stm, 0, 0); - - assert((*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) == 0xbaadbaad); - - if (ctx->klass.check(stm->instance)) { - ALOG("stream not initialized properly."); - audiotrack_stream_destroy(stm); - return CUBEB_ERROR; - } - - *stream = stm; - - return CUBEB_OK; -} - -void -audiotrack_stream_destroy(cubeb_stream * stream) -{ - assert(stream->context); - - stream->context->klass.dtor(stream->instance); - - free(stream->instance); - stream->instance = NULL; - free(stream); -} - -int -audiotrack_stream_start(cubeb_stream * stream) -{ - assert(stream->instance); - - stream->context->klass.start(stream->instance); - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED); - - return CUBEB_OK; -} - -int -audiotrack_stream_stop(cubeb_stream * stream) -{ - assert(stream->instance); - - stream->context->klass.pause(stream->instance); - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED); - - return CUBEB_OK; -} - -int -audiotrack_stream_get_position(cubeb_stream * stream, uint64_t * position) -{ - uint32_t p; - - assert(stream->instance && position); - stream->context->klass.get_position(stream->instance, &p); - *position = p; - - return CUBEB_OK; -} - -int -audiotrack_stream_get_latency(cubeb_stream * stream, uint32_t * latency) -{ - assert(stream->instance && latency); - - /* Android returns the latency in ms, we want it in frames. */ - *latency = stream->context->klass.latency(stream->instance); - /* with rate <= 96000, we won't overflow until 44.739 seconds of latency */ - *latency = (*latency * stream->params.rate) / 1000; - - return 0; -} - -int -audiotrack_stream_set_volume(cubeb_stream * stream, float volume) -{ - status_t status; - - status = stream->context->klass.set_volume(stream->instance, volume, volume); - - if (status) { - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -static struct cubeb_ops const audiotrack_ops = { - .init = audiotrack_init, - .get_backend_id = audiotrack_get_backend_id, - .get_max_channel_count = audiotrack_get_max_channel_count, - .get_min_latency = audiotrack_get_min_latency, - .get_preferred_sample_rate = audiotrack_get_preferred_sample_rate, - .enumerate_devices = NULL, - .destroy = audiotrack_destroy, - .stream_init = audiotrack_stream_init, - .stream_destroy = audiotrack_stream_destroy, - .stream_start = audiotrack_stream_start, - .stream_stop = audiotrack_stream_stop, - .stream_get_position = audiotrack_stream_get_position, - .stream_get_latency = audiotrack_stream_get_latency, - .stream_set_volume = audiotrack_stream_set_volume, - .stream_set_panning = NULL, - .stream_get_current_device = NULL, - .stream_device_destroy = NULL, - .stream_register_device_changed_callback = NULL, - .register_device_collection_changed = NULL -}; |