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); stm->lastPosition = -1; stm->lastPositionTimeStamp = 0; stm->lastCompensativePosition = -1; 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; } { // Enqueue a silent frame so once the player becomes playing, the frame // will be consumed and kick off the buffer queue callback. // Note the duration of a single frame is less than 1ms. We don't bother // adjusting the playback position. uint8_t *buf = stm->queuebuf[stm->queuebuf_idx++]; memset(buf, 0, stm->framesize); res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->framesize); assert(res == SL_RESULT_SUCCESS); } *stream = stm; 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.rate < 8000 || stream_params.rate > 48000 || 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->framesize = stream_params.channels * sizeof(int16_t); stm->bytespersec = stream_params.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); } int i; for (i = 0; i < NBUFS; i++) { stm->queuebuf[i] = malloc(stm->queuebuf_len); assert(stm->queuebuf[i]); } 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_ANDROIDCONFIGURATION}; const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; #else const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE}; const SLboolean req[] = {SL_BOOLEAN_TRUE}; #endif assert(NELEMS(ids) == NELEMS(req)); SLresult 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; } #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->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm); if (res != SL_RESULT_SUCCESS) { opensl_stream_destroy(stm); return CUBEB_ERROR; } *stream = stm; return CUBEB_OK; }