Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* |
michael@0 | 2 | * Copyright © 2013 Mozilla Foundation |
michael@0 | 3 | * |
michael@0 | 4 | * This program is made available under an ISC-style license. See the |
michael@0 | 5 | * accompanying file LICENSE for details. |
michael@0 | 6 | */ |
michael@0 | 7 | |
michael@0 | 8 | #if !defined(NDEBUG) |
michael@0 | 9 | #define NDEBUG |
michael@0 | 10 | #endif |
michael@0 | 11 | #include <assert.h> |
michael@0 | 12 | #include <pthread.h> |
michael@0 | 13 | #include <stdlib.h> |
michael@0 | 14 | #include <time.h> |
michael@0 | 15 | #include <dlfcn.h> |
michael@0 | 16 | #include "android/log.h" |
michael@0 | 17 | |
michael@0 | 18 | #include "cubeb/cubeb.h" |
michael@0 | 19 | #include "cubeb-internal.h" |
michael@0 | 20 | #include "android/audiotrack_definitions.h" |
michael@0 | 21 | |
michael@0 | 22 | #ifndef ALOG |
michael@0 | 23 | #if defined(DEBUG) || defined(FORCE_ALOG) |
michael@0 | 24 | #define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb" , ## args) |
michael@0 | 25 | #else |
michael@0 | 26 | #define ALOG(args...) |
michael@0 | 27 | #endif |
michael@0 | 28 | #endif |
michael@0 | 29 | |
michael@0 | 30 | /** |
michael@0 | 31 | * A lot of bytes for safety. It should be possible to bring this down a bit. */ |
michael@0 | 32 | #define SIZE_AUDIOTRACK_INSTANCE 256 |
michael@0 | 33 | |
michael@0 | 34 | /** |
michael@0 | 35 | * call dlsym to get the symbol |mangled_name|, handle the error and store the |
michael@0 | 36 | * pointer in |pointer|. Because depending on Android version, we want different |
michael@0 | 37 | * symbols, not finding a symbol is not an error. */ |
michael@0 | 38 | #define DLSYM_DLERROR(mangled_name, pointer, lib) \ |
michael@0 | 39 | do { \ |
michael@0 | 40 | pointer = dlsym(lib, mangled_name); \ |
michael@0 | 41 | if (!pointer) { \ |
michael@0 | 42 | ALOG("error while loading %stm: %stm\n", mangled_name, dlerror()); \ |
michael@0 | 43 | } else { \ |
michael@0 | 44 | ALOG("%stm: OK", mangled_name); \ |
michael@0 | 45 | } \ |
michael@0 | 46 | } while(0); |
michael@0 | 47 | |
michael@0 | 48 | static struct cubeb_ops const audiotrack_ops; |
michael@0 | 49 | void audiotrack_destroy(cubeb * context); |
michael@0 | 50 | void audiotrack_stream_destroy(cubeb_stream * stream); |
michael@0 | 51 | |
michael@0 | 52 | struct AudioTrack { |
michael@0 | 53 | /* only available on ICS and later. The second int paramter is in fact of type audio_stream_type_t. */ |
michael@0 | 54 | /* static */ status_t (*get_min_frame_count)(int* frame_count, int stream_type, uint32_t rate); |
michael@0 | 55 | /* if we have a recent ctor, but can't find the above symbol, we |
michael@0 | 56 | * can get the minimum frame count with this signature, and we are |
michael@0 | 57 | * running gingerbread. */ |
michael@0 | 58 | /* static */ status_t (*get_min_frame_count_gingerbread)(int* frame_count, int stream_type, uint32_t rate); |
michael@0 | 59 | /* if this symbol is not availble, and the next one is, we know |
michael@0 | 60 | * we are on a Froyo (Android 2.2) device. */ |
michael@0 | 61 | void* (*ctor)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int, int); |
michael@0 | 62 | void* (*ctor_froyo)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int); |
michael@0 | 63 | void* (*dtor)(void* instance); |
michael@0 | 64 | void (*start)(void* instance); |
michael@0 | 65 | void (*pause)(void* instance); |
michael@0 | 66 | uint32_t (*latency)(void* instance); |
michael@0 | 67 | status_t (*check)(void* instance); |
michael@0 | 68 | status_t (*get_position)(void* instance, uint32_t* position); |
michael@0 | 69 | /* only used on froyo. */ |
michael@0 | 70 | /* static */ int (*get_output_frame_count)(int* frame_count, int stream); |
michael@0 | 71 | /* static */ int (*get_output_latency)(uint32_t* latency, int stream); |
michael@0 | 72 | /* static */ int (*get_output_samplingrate)(int* samplerate, int stream); |
michael@0 | 73 | status_t (*set_marker_position)(void* instance, unsigned int); |
michael@0 | 74 | |
michael@0 | 75 | }; |
michael@0 | 76 | |
michael@0 | 77 | struct cubeb { |
michael@0 | 78 | struct cubeb_ops const * ops; |
michael@0 | 79 | void * library; |
michael@0 | 80 | struct AudioTrack klass; |
michael@0 | 81 | }; |
michael@0 | 82 | |
michael@0 | 83 | struct cubeb_stream { |
michael@0 | 84 | cubeb * context; |
michael@0 | 85 | cubeb_stream_params params; |
michael@0 | 86 | cubeb_data_callback data_callback; |
michael@0 | 87 | cubeb_state_callback state_callback; |
michael@0 | 88 | void * instance; |
michael@0 | 89 | void * user_ptr; |
michael@0 | 90 | /* Number of frames that have been passed to the AudioTrack callback */ |
michael@0 | 91 | long unsigned written; |
michael@0 | 92 | int draining; |
michael@0 | 93 | }; |
michael@0 | 94 | |
michael@0 | 95 | static void |
michael@0 | 96 | audiotrack_refill(int event, void* user, void* info) |
michael@0 | 97 | { |
michael@0 | 98 | cubeb_stream * stream = user; |
michael@0 | 99 | switch (event) { |
michael@0 | 100 | case EVENT_MORE_DATA: { |
michael@0 | 101 | long got = 0; |
michael@0 | 102 | struct Buffer * b = (struct Buffer*)info; |
michael@0 | 103 | |
michael@0 | 104 | if (stream->draining) { |
michael@0 | 105 | return; |
michael@0 | 106 | } |
michael@0 | 107 | |
michael@0 | 108 | got = stream->data_callback(stream, stream->user_ptr, b->raw, b->frameCount); |
michael@0 | 109 | |
michael@0 | 110 | stream->written += got; |
michael@0 | 111 | |
michael@0 | 112 | if (got != (long)b->frameCount) { |
michael@0 | 113 | uint32_t p; |
michael@0 | 114 | stream->draining = 1; |
michael@0 | 115 | /* set a marker so we are notified when the are done draining, that is, |
michael@0 | 116 | * when every frame has been played by android. */ |
michael@0 | 117 | stream->context->klass.set_marker_position(stream->instance, stream->written); |
michael@0 | 118 | } |
michael@0 | 119 | |
michael@0 | 120 | break; |
michael@0 | 121 | } |
michael@0 | 122 | case EVENT_UNDERRUN: |
michael@0 | 123 | ALOG("underrun in cubeb backend."); |
michael@0 | 124 | break; |
michael@0 | 125 | case EVENT_LOOP_END: |
michael@0 | 126 | assert(0 && "We don't support the loop feature of audiotrack."); |
michael@0 | 127 | break; |
michael@0 | 128 | case EVENT_MARKER: |
michael@0 | 129 | assert(stream->draining); |
michael@0 | 130 | stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); |
michael@0 | 131 | break; |
michael@0 | 132 | case EVENT_NEW_POS: |
michael@0 | 133 | assert(0 && "We don't support the setPositionUpdatePeriod feature of audiotrack."); |
michael@0 | 134 | break; |
michael@0 | 135 | case EVENT_BUFFER_END: |
michael@0 | 136 | assert(0 && "Should not happen."); |
michael@0 | 137 | break; |
michael@0 | 138 | } |
michael@0 | 139 | } |
michael@0 | 140 | |
michael@0 | 141 | /* We are running on froyo if we found the right AudioTrack constructor */ |
michael@0 | 142 | static int |
michael@0 | 143 | audiotrack_version_is_froyo(cubeb * ctx) |
michael@0 | 144 | { |
michael@0 | 145 | return ctx->klass.ctor_froyo != NULL; |
michael@0 | 146 | } |
michael@0 | 147 | |
michael@0 | 148 | /* We are running on gingerbread if we found the gingerbread signature for |
michael@0 | 149 | * getMinFrameCount */ |
michael@0 | 150 | static int |
michael@0 | 151 | audiotrack_version_is_gingerbread(cubeb * ctx) |
michael@0 | 152 | { |
michael@0 | 153 | return ctx->klass.get_min_frame_count_gingerbread != NULL; |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | int |
michael@0 | 157 | audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int * min_frame_count) |
michael@0 | 158 | { |
michael@0 | 159 | status_t status; |
michael@0 | 160 | /* Recent Android have a getMinFrameCount method. On Froyo, we have to compute it by hand. */ |
michael@0 | 161 | if (audiotrack_version_is_froyo(ctx)) { |
michael@0 | 162 | int samplerate, frame_count, latency, min_buffer_count; |
michael@0 | 163 | status = ctx->klass.get_output_frame_count(&frame_count, params->stream_type); |
michael@0 | 164 | if (status) { |
michael@0 | 165 | ALOG("error getting the output frame count."); |
michael@0 | 166 | return CUBEB_ERROR; |
michael@0 | 167 | } |
michael@0 | 168 | status = ctx->klass.get_output_latency((uint32_t*)&latency, params->stream_type); |
michael@0 | 169 | if (status) { |
michael@0 | 170 | ALOG("error getting the output frame count."); |
michael@0 | 171 | return CUBEB_ERROR; |
michael@0 | 172 | } |
michael@0 | 173 | status = ctx->klass.get_output_samplingrate(&samplerate, params->stream_type); |
michael@0 | 174 | if (status) { |
michael@0 | 175 | ALOG("error getting the output frame count."); |
michael@0 | 176 | return CUBEB_ERROR; |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | /* Those numbers were found reading the Android source. It is the minimum |
michael@0 | 180 | * numbers that will be accepted by the AudioTrack class, hence yielding the |
michael@0 | 181 | * best latency possible. |
michael@0 | 182 | * See https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/media/libmedia/AudioTrack.cpp |
michael@0 | 183 | * around line 181 for Android 2.2 */ |
michael@0 | 184 | min_buffer_count = latency / ((1000 * frame_count) / samplerate); |
michael@0 | 185 | min_buffer_count = min_buffer_count < 2 ? min_buffer_count : 2; |
michael@0 | 186 | *min_frame_count = (frame_count * params->rate * min_buffer_count) / samplerate; |
michael@0 | 187 | return CUBEB_OK; |
michael@0 | 188 | } |
michael@0 | 189 | /* Recent Android have a getMinFrameCount method. */ |
michael@0 | 190 | if (!audiotrack_version_is_gingerbread(ctx)) { |
michael@0 | 191 | status = ctx->klass.get_min_frame_count(min_frame_count, params->stream_type, params->rate); |
michael@0 | 192 | } else { |
michael@0 | 193 | status = ctx->klass.get_min_frame_count_gingerbread(min_frame_count, params->stream_type, params->rate); |
michael@0 | 194 | } |
michael@0 | 195 | if (status != 0) { |
michael@0 | 196 | ALOG("error getting the min frame count"); |
michael@0 | 197 | return CUBEB_ERROR; |
michael@0 | 198 | } |
michael@0 | 199 | return CUBEB_OK; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | int |
michael@0 | 203 | audiotrack_init(cubeb ** context, char const * context_name) |
michael@0 | 204 | { |
michael@0 | 205 | cubeb * ctx; |
michael@0 | 206 | struct AudioTrack* c; |
michael@0 | 207 | |
michael@0 | 208 | assert(context); |
michael@0 | 209 | *context = NULL; |
michael@0 | 210 | |
michael@0 | 211 | ctx = calloc(1, sizeof(*ctx)); |
michael@0 | 212 | assert(ctx); |
michael@0 | 213 | |
michael@0 | 214 | /* If we use an absolute path here ("/system/lib/libmedia.so"), and on Android |
michael@0 | 215 | * 2.2, the dlopen succeeds, all the dlsym succeed, but a segfault happens on |
michael@0 | 216 | * the first call to a dlsym'ed function. Somehow this does not happen when |
michael@0 | 217 | * using only the name of the library. */ |
michael@0 | 218 | ctx->library = dlopen("libmedia.so", RTLD_LAZY); |
michael@0 | 219 | if (!ctx->library) { |
michael@0 | 220 | ALOG("dlopen error: %s.", dlerror()); |
michael@0 | 221 | free(ctx); |
michael@0 | 222 | return CUBEB_ERROR; |
michael@0 | 223 | } |
michael@0 | 224 | |
michael@0 | 225 | /* Recent Android first, then Froyo. */ |
michael@0 | 226 | DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", ctx->klass.ctor, ctx->library); |
michael@0 | 227 | if (!ctx->klass.ctor) { |
michael@0 | 228 | DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i", ctx->klass.ctor_froyo, ctx->library); |
michael@0 | 229 | assert(ctx->klass.ctor_froyo); |
michael@0 | 230 | } |
michael@0 | 231 | DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library); |
michael@0 | 232 | |
michael@0 | 233 | DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, ctx->library); |
michael@0 | 234 | DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check, ctx->library); |
michael@0 | 235 | |
michael@0 | 236 | DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", ctx->klass.get_output_samplingrate, ctx->library); |
michael@0 | 237 | |
michael@0 | 238 | /* |getMinFrameCount| is not available on Froyo, and is available on |
michael@0 | 239 | * gingerbread and ICS with a different signature. */ |
michael@0 | 240 | if (audiotrack_version_is_froyo(ctx)) { |
michael@0 | 241 | DLSYM_DLERROR("_ZN7android11AudioSystem19getOutputFrameCountEPii", ctx->klass.get_output_frame_count, ctx->library); |
michael@0 | 242 | DLSYM_DLERROR("_ZN7android11AudioSystem16getOutputLatencyEPji", ctx->klass.get_output_latency, ctx->library); |
michael@0 | 243 | } else { |
michael@0 | 244 | DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library); |
michael@0 | 245 | if (!ctx->klass.get_min_frame_count) { |
michael@0 | 246 | DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij", ctx->klass.get_min_frame_count_gingerbread, ctx->library); |
michael@0 | 247 | } |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, ctx->library); |
michael@0 | 251 | DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, ctx->library); |
michael@0 | 252 | DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", ctx->klass.get_position, ctx->library); |
michael@0 | 253 | DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", ctx->klass.set_marker_position, ctx->library); |
michael@0 | 254 | |
michael@0 | 255 | /* check that we have a combination of symbol that makes sense */ |
michael@0 | 256 | c = &ctx->klass; |
michael@0 | 257 | if(!((c->ctor || c->ctor_froyo) && /* at least on ctor. */ |
michael@0 | 258 | c->dtor && c->latency && c->check && |
michael@0 | 259 | /* at least one way to get the minimum frame count to request. */ |
michael@0 | 260 | ((c->get_output_frame_count && c->get_output_latency && c->get_output_samplingrate) || |
michael@0 | 261 | c->get_min_frame_count || |
michael@0 | 262 | c->get_min_frame_count_gingerbread) && |
michael@0 | 263 | c->start && c->pause && c->get_position && c->set_marker_position)) { |
michael@0 | 264 | ALOG("Could not find all the symbols we need."); |
michael@0 | 265 | audiotrack_destroy(ctx); |
michael@0 | 266 | return CUBEB_ERROR; |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | ctx->ops = &audiotrack_ops; |
michael@0 | 270 | |
michael@0 | 271 | *context = ctx; |
michael@0 | 272 | |
michael@0 | 273 | return CUBEB_OK; |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | char const * |
michael@0 | 277 | audiotrack_get_backend_id(cubeb * context) |
michael@0 | 278 | { |
michael@0 | 279 | return "audiotrack"; |
michael@0 | 280 | } |
michael@0 | 281 | |
michael@0 | 282 | static int |
michael@0 | 283 | audiotrack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) |
michael@0 | 284 | { |
michael@0 | 285 | assert(ctx && max_channels); |
michael@0 | 286 | |
michael@0 | 287 | /* The android mixer handles up to two channels, see |
michael@0 | 288 | http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */ |
michael@0 | 289 | *max_channels = 2; |
michael@0 | 290 | |
michael@0 | 291 | return CUBEB_OK; |
michael@0 | 292 | } |
michael@0 | 293 | |
michael@0 | 294 | static int |
michael@0 | 295 | audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) |
michael@0 | 296 | { |
michael@0 | 297 | /* We always use the lowest latency possible when using this backend (see |
michael@0 | 298 | * audiotrack_stream_init), so this value is not going to be used. */ |
michael@0 | 299 | int rv; |
michael@0 | 300 | |
michael@0 | 301 | rv = audiotrack_get_min_frame_count(ctx, ¶ms, (int *)latency_ms); |
michael@0 | 302 | if (rv != CUBEB_OK) { |
michael@0 | 303 | return CUBEB_ERROR; |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | /* Convert to milliseconds. */ |
michael@0 | 307 | *latency_ms = *latency_ms * 1000 / params.rate; |
michael@0 | 308 | |
michael@0 | 309 | return CUBEB_OK; |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | static int |
michael@0 | 313 | audiotrack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) |
michael@0 | 314 | { |
michael@0 | 315 | status_t rv; |
michael@0 | 316 | |
michael@0 | 317 | rv = ctx->klass.get_output_samplingrate((int32_t *)rate, 3 /* MUSIC */); |
michael@0 | 318 | |
michael@0 | 319 | return rv == 0 ? CUBEB_OK : CUBEB_ERROR; |
michael@0 | 320 | } |
michael@0 | 321 | |
michael@0 | 322 | void |
michael@0 | 323 | audiotrack_destroy(cubeb * context) |
michael@0 | 324 | { |
michael@0 | 325 | assert(context); |
michael@0 | 326 | |
michael@0 | 327 | dlclose(context->library); |
michael@0 | 328 | |
michael@0 | 329 | free(context); |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | int |
michael@0 | 333 | audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, |
michael@0 | 334 | cubeb_stream_params stream_params, unsigned int latency, |
michael@0 | 335 | cubeb_data_callback data_callback, |
michael@0 | 336 | cubeb_state_callback state_callback, |
michael@0 | 337 | void * user_ptr) |
michael@0 | 338 | { |
michael@0 | 339 | cubeb_stream * stm; |
michael@0 | 340 | int32_t channels; |
michael@0 | 341 | uint32_t min_frame_count; |
michael@0 | 342 | |
michael@0 | 343 | assert(ctx && stream); |
michael@0 | 344 | |
michael@0 | 345 | if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE || |
michael@0 | 346 | stream_params.format == CUBEB_SAMPLE_FLOAT32BE) { |
michael@0 | 347 | return CUBEB_ERROR_INVALID_FORMAT; |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | if (audiotrack_get_min_frame_count(ctx, &stream_params, (int *)&min_frame_count)) { |
michael@0 | 351 | return CUBEB_ERROR; |
michael@0 | 352 | } |
michael@0 | 353 | |
michael@0 | 354 | stm = calloc(1, sizeof(*stm)); |
michael@0 | 355 | assert(stm); |
michael@0 | 356 | |
michael@0 | 357 | stm->context = ctx; |
michael@0 | 358 | stm->data_callback = data_callback; |
michael@0 | 359 | stm->state_callback = state_callback; |
michael@0 | 360 | stm->user_ptr = user_ptr; |
michael@0 | 361 | stm->params = stream_params; |
michael@0 | 362 | |
michael@0 | 363 | stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1); |
michael@0 | 364 | (*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = 0xbaadbaad; |
michael@0 | 365 | assert(stm->instance && "cubeb: EOM"); |
michael@0 | 366 | |
michael@0 | 367 | /* gingerbread uses old channel layout enum */ |
michael@0 | 368 | if (audiotrack_version_is_froyo(ctx) || audiotrack_version_is_gingerbread(ctx)) { |
michael@0 | 369 | channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Legacy : AUDIO_CHANNEL_OUT_MONO_Legacy; |
michael@0 | 370 | } else { |
michael@0 | 371 | channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS; |
michael@0 | 372 | } |
michael@0 | 373 | |
michael@0 | 374 | if (audiotrack_version_is_froyo(ctx)) { |
michael@0 | 375 | ctx->klass.ctor_froyo(stm->instance, |
michael@0 | 376 | stm->params.stream_type, |
michael@0 | 377 | stm->params.rate, |
michael@0 | 378 | AUDIO_FORMAT_PCM_16_BIT, |
michael@0 | 379 | channels, |
michael@0 | 380 | min_frame_count, |
michael@0 | 381 | 0, |
michael@0 | 382 | audiotrack_refill, |
michael@0 | 383 | stm, |
michael@0 | 384 | 0); |
michael@0 | 385 | } else { |
michael@0 | 386 | ctx->klass.ctor(stm->instance, |
michael@0 | 387 | stm->params.stream_type, |
michael@0 | 388 | stm->params.rate, |
michael@0 | 389 | AUDIO_FORMAT_PCM_16_BIT, |
michael@0 | 390 | channels, |
michael@0 | 391 | min_frame_count, |
michael@0 | 392 | 0, |
michael@0 | 393 | audiotrack_refill, |
michael@0 | 394 | stm, |
michael@0 | 395 | 0, |
michael@0 | 396 | 0); |
michael@0 | 397 | } |
michael@0 | 398 | |
michael@0 | 399 | assert((*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) == 0xbaadbaad); |
michael@0 | 400 | |
michael@0 | 401 | if (ctx->klass.check(stm->instance)) { |
michael@0 | 402 | ALOG("stream not initialized properly."); |
michael@0 | 403 | audiotrack_stream_destroy(stm); |
michael@0 | 404 | return CUBEB_ERROR; |
michael@0 | 405 | } |
michael@0 | 406 | |
michael@0 | 407 | *stream = stm; |
michael@0 | 408 | |
michael@0 | 409 | return CUBEB_OK; |
michael@0 | 410 | } |
michael@0 | 411 | |
michael@0 | 412 | void |
michael@0 | 413 | audiotrack_stream_destroy(cubeb_stream * stream) |
michael@0 | 414 | { |
michael@0 | 415 | assert(stream->context); |
michael@0 | 416 | |
michael@0 | 417 | stream->context->klass.dtor(stream->instance); |
michael@0 | 418 | |
michael@0 | 419 | free(stream->instance); |
michael@0 | 420 | stream->instance = NULL; |
michael@0 | 421 | free(stream); |
michael@0 | 422 | } |
michael@0 | 423 | |
michael@0 | 424 | int |
michael@0 | 425 | audiotrack_stream_start(cubeb_stream * stream) |
michael@0 | 426 | { |
michael@0 | 427 | assert(stream->instance); |
michael@0 | 428 | |
michael@0 | 429 | stream->context->klass.start(stream->instance); |
michael@0 | 430 | stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED); |
michael@0 | 431 | |
michael@0 | 432 | return CUBEB_OK; |
michael@0 | 433 | } |
michael@0 | 434 | |
michael@0 | 435 | int |
michael@0 | 436 | audiotrack_stream_stop(cubeb_stream * stream) |
michael@0 | 437 | { |
michael@0 | 438 | assert(stream->instance); |
michael@0 | 439 | |
michael@0 | 440 | stream->context->klass.pause(stream->instance); |
michael@0 | 441 | stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED); |
michael@0 | 442 | |
michael@0 | 443 | return CUBEB_OK; |
michael@0 | 444 | } |
michael@0 | 445 | |
michael@0 | 446 | int |
michael@0 | 447 | audiotrack_stream_get_position(cubeb_stream * stream, uint64_t * position) |
michael@0 | 448 | { |
michael@0 | 449 | uint32_t p; |
michael@0 | 450 | |
michael@0 | 451 | assert(stream->instance && position); |
michael@0 | 452 | stream->context->klass.get_position(stream->instance, &p); |
michael@0 | 453 | *position = p; |
michael@0 | 454 | |
michael@0 | 455 | return CUBEB_OK; |
michael@0 | 456 | } |
michael@0 | 457 | |
michael@0 | 458 | int |
michael@0 | 459 | audiotrack_stream_get_latency(cubeb_stream * stream, uint32_t * latency) |
michael@0 | 460 | { |
michael@0 | 461 | assert(stream->instance && latency); |
michael@0 | 462 | |
michael@0 | 463 | /* Android returns the latency in ms, we want it in frames. */ |
michael@0 | 464 | *latency = stream->context->klass.latency(stream->instance); |
michael@0 | 465 | /* with rate <= 96000, we won't overflow until 44.739 seconds of latency */ |
michael@0 | 466 | *latency = (*latency * stream->params.rate) / 1000; |
michael@0 | 467 | |
michael@0 | 468 | return 0; |
michael@0 | 469 | } |
michael@0 | 470 | |
michael@0 | 471 | static struct cubeb_ops const audiotrack_ops = { |
michael@0 | 472 | .init = audiotrack_init, |
michael@0 | 473 | .get_backend_id = audiotrack_get_backend_id, |
michael@0 | 474 | .get_max_channel_count = audiotrack_get_max_channel_count, |
michael@0 | 475 | .get_min_latency = audiotrack_get_min_latency, |
michael@0 | 476 | .get_preferred_sample_rate = audiotrack_get_preferred_sample_rate, |
michael@0 | 477 | .destroy = audiotrack_destroy, |
michael@0 | 478 | .stream_init = audiotrack_stream_init, |
michael@0 | 479 | .stream_destroy = audiotrack_stream_destroy, |
michael@0 | 480 | .stream_start = audiotrack_stream_start, |
michael@0 | 481 | .stream_stop = audiotrack_stream_stop, |
michael@0 | 482 | .stream_get_position = audiotrack_stream_get_position, |
michael@0 | 483 | .stream_get_latency = audiotrack_stream_get_latency |
michael@0 | 484 | }; |