static int opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) { /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html * We don't want to deal with JNI here (and we don't have Java on b2g anyways), * so we just dlopen the library and get the two symbols we need. */ int rv; void * libmedia; size_t (*get_primary_output_frame_count)(void); int (*get_output_frame_count)(size_t * frameCount, int streamType); uint32_t primary_sampling_rate; size_t primary_buffer_size; rv = opensl_get_preferred_sample_rate(ctx, &primary_sampling_rate); if (rv) { return CUBEB_ERROR; } libmedia = dlopen("libmedia.so", RTLD_LAZY); if (!libmedia) { return CUBEB_ERROR; } /* JB variant */ /* size_t AudioSystem::getPrimaryOutputFrameCount(void) */ get_primary_output_frame_count = dlsym(libmedia, "_ZN7android11AudioSystem26getPrimaryOutputFrameCountEv"); if (!get_primary_output_frame_count) { /* ICS variant */ /* status_t AudioSystem::getOutputFrameCount(int* frameCount, int streamType) */ get_output_frame_count = dlsym(libmedia, "_ZN7android11AudioSystem19getOutputFrameCountEPii"); if (!get_output_frame_count) { dlclose(libmedia); return CUBEB_ERROR; } } if (get_primary_output_frame_count) { primary_buffer_size = get_primary_output_frame_count(); } else { if (get_output_frame_count(&primary_buffer_size, params.stream_type) != 0) { return CUBEB_ERROR; } } /* To get a fast track in Android's mixer, we need to be at the native * samplerate, which is device dependant. Some devices might be able to * resample when playing a fast track, but it's pretty rare. */ *latency_ms = NBUFS * primary_buffer_size / (primary_sampling_rate / 1000); dlclose(libmedia); return CUBEB_OK; }
int opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency) { int rv; void * libmedia; int32_t (*get_output_latency)(uint32_t * latency, int stream_type); uint32_t mixer_latency; uint32_t samplerate; /* The latency returned by AudioFlinger is in ms, so we have to get * AudioFlinger's samplerate to convert it to frames. */ rv = opensl_get_preferred_sample_rate(stm->context, &samplerate); if (rv) { return CUBEB_ERROR; } libmedia = dlopen("libmedia.so", RTLD_LAZY); if (!libmedia) { return CUBEB_ERROR; } /* Get the latency, in ms, from AudioFlinger */ /* status_t AudioSystem::getOutputLatency(uint32_t* latency, * audio_stream_type_t streamType) */ /* First, try the most recent signature. */ get_output_latency = dlsym(libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t"); if (!get_output_latency) { /* in case of failure, try the legacy version. */ /* status_t AudioSystem::getOutputLatency(uint32_t* latency, * int streamType) */ get_output_latency = dlsym(libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji"); if (!get_output_latency) { dlclose(libmedia); return CUBEB_ERROR; } } /* audio_stream_type_t is an int, so this is okay. */ rv = get_output_latency(&mixer_latency, stm->stream_type); if (rv) { dlclose(libmedia); return CUBEB_ERROR; } *latency = NBUFS * stm->queuebuf_len / stm->framesize + // OpenSL latency mixer_latency * samplerate / 1000; // AudioFlinger latency dlclose(libmedia); return CUBEB_OK; }
static int opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, 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 * stm; assert(ctx); *stream = NULL; if (stream_params.channels < 1 || stream_params.channels > 32 || latency < 1 || latency > 2000) { return CUBEB_ERROR_INVALID_FORMAT; } SLDataFormat_PCM format; format.formatType = SL_DATAFORMAT_PCM; format.numChannels = stream_params.channels; // samplesPerSec is in milliHertz format.samplesPerSec = stream_params.rate * 1000; format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; format.channelMask = stream_params.channels == 1 ? SL_SPEAKER_FRONT_CENTER : SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; switch (stream_params.format) { case CUBEB_SAMPLE_S16LE: format.endianness = SL_BYTEORDER_LITTLEENDIAN; break; case CUBEB_SAMPLE_S16BE: format.endianness = SL_BYTEORDER_BIGENDIAN; break; default: return CUBEB_ERROR_INVALID_FORMAT; } 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->inputrate = stream_params.rate; stm->latency = latency; stm->stream_type = stream_params.stream_type; stm->framesize = stream_params.channels * sizeof(int16_t); int r = pthread_mutex_init(&stm->mutex, NULL); assert(r == 0); SLDataLocator_BufferQueue loc_bufq; loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE; loc_bufq.numBuffers = NBUFS; SLDataSource source; source.pLocator = &loc_bufq; source.pFormat = &format; SLDataLocator_OutputMix loc_outmix; loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; loc_outmix.outputMix = ctx->outmixObj; SLDataSink sink; sink.pLocator = &loc_outmix; sink.pFormat = NULL; #if defined(__ANDROID__) const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_VOLUME, ctx->SL_IID_ANDROIDCONFIGURATION}; const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; #else const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_VOLUME}; const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; #endif assert(NELEMS(ids) == NELEMS(req)); SLresult res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj, &source, &sink, NELEMS(ids), ids, req); uint32_t preferred_sampling_rate = stm->inputrate; // Sample rate not supported? Try again with primary sample rate! if (res == SL_RESULT_CONTENT_UNSUPPORTED) { if (opensl_get_preferred_sample_rate(ctx, &preferred_sampling_rate)) { opensl_stream_destroy(stm); return CUBEB_ERROR; } format.samplesPerSec = preferred_sampling_rate * 1000; res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj, &source, &sink, NELEMS(ids), ids, req); } if (res != SL_RESULT_SUCCESS) { opensl_stream_destroy(stm); return CUBEB_ERROR; } stm->outputrate = preferred_sampling_rate; stm->bytespersec = preferred_sampling_rate * stm->framesize; stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS); // round up to the next multiple of stm->framesize, if needed. if (stm->queuebuf_len % stm->framesize) { stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize); } stm->resampler = cubeb_resampler_create(stm, stream_params, preferred_sampling_rate, data_callback, stm->queuebuf_len / stm->framesize, user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT); if (!stm->resampler) { opensl_stream_destroy(stm); return CUBEB_ERROR; } int i; for (i = 0; i < NBUFS; i++) { stm->queuebuf[i] = malloc(stm->queuebuf_len); assert(stm->queuebuf[i]); } #if defined(__ANDROID__) SLuint32 stream_type = convert_stream_type_to_sl_stream(stream_params.stream_type); if (stream_type != 0xFFFFFFFF) { SLAndroidConfigurationItf playerConfig; res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_ANDROIDCONFIGURATION, &playerConfig); res = (*playerConfig)->SetConfiguration(playerConfig, SL_ANDROID_KEY_STREAM_TYPE, &stream_type, sizeof(SLint32)); if (res != SL_RESULT_SUCCESS) { opensl_stream_destroy(stm); return CUBEB_ERROR; } } #endif res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE); if (res != SL_RESULT_SUCCESS) { opensl_stream_destroy(stm); return CUBEB_ERROR; } res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_PLAY, &stm->play); if (res != SL_RESULT_SUCCESS) { opensl_stream_destroy(stm); return CUBEB_ERROR; } res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_BUFFERQUEUE, &stm->bufq); if (res != SL_RESULT_SUCCESS) { opensl_stream_destroy(stm); return CUBEB_ERROR; } res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_VOLUME, &stm->volume); if (res != SL_RESULT_SUCCESS) { opensl_stream_destroy(stm); return CUBEB_ERROR; } res = (*stm->play)->RegisterCallback(stm->play, play_callback, stm); if (res != SL_RESULT_SUCCESS) { opensl_stream_destroy(stm); return CUBEB_ERROR; } res = (*stm->play)->SetCallbackEventsMask(stm->play, (SLuint32)SL_PLAYEVENT_HEADATMARKER); if (res != SL_RESULT_SUCCESS) { opensl_stream_destroy(stm); return CUBEB_ERROR; } res = (*stm->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm); if (res != SL_RESULT_SUCCESS) { opensl_stream_destroy(stm); return CUBEB_ERROR; } *stream = stm; return CUBEB_OK; }