struct cubeb_stream { /* Note: Must match cubeb_stream layout in cubeb.c. */
cubeb * context; void * user_ptr; /**/
pthread_mutex_t mutex;
SLObjectItf playerObj;
SLPlayItf play;
SLBufferQueueItf bufq;
SLVolumeItf volume; void ** queuebuf;
uint32_t queuebuf_capacity;
uint32_t queuebuf_idx; long queuebuf_len; long bytespersec;
uint32_t framesize; /* Total number of played frames.
* Synchronized by stream::mutex lock. */
int64_t written; /* Flag indicating draining. Synchronized
* by stream::mutex lock. */ int draining; /* Flags to determine in/out.*/
uint32_t input_enabled;
uint32_t output_enabled; /* Recorder abstract object. */
SLObjectItf recorderObj; /* Recorder Itf for input capture. */
SLRecordItf recorderItf; /* Buffer queue for input capture. */
SLAndroidSimpleBufferQueueItf recorderBufferQueueItf; /* Store input buffers. */ void ** input_buffer_array; /* The capacity of the array. *Oncaptureonlycanbesmall(4). *Onfullduplexiscalculatedto
* store 1 sec of data buffers. */
uint32_t input_array_capacity; /* Current filled index of input buffer array. *Itisinitiatedto-1indicatingbuffering
* have not started yet. */ int input_buffer_index; /* Length of input buffer.*/
uint32_t input_buffer_length; /* Input frame size */
uint32_t input_frame_size; /* Device sampling rate. If user rate is not *acceptedancompatiblerateisset.Ifitis
* accepted this is equal to params.rate. */
uint32_t input_device_rate; /* Exchange input buffers between input
* and full duplex threads. */
array_queue * input_queue; /* Silent input buffer used on full duplex. */ void * input_silent_buffer; /* Number of input frames from the start of the stream*/
uint32_t input_total_frames; /* Flag to stop the execution of user callback and *closeallworkingthreads.Synchronizedby
* stream::mutex lock. */
uint32_t shutdown; /* Store user callback. */
cubeb_data_callback data_callback; /* Store state callback. */
cubeb_state_callback state_callback;
cubeb_resampler * resampler; unsignedint user_output_rate; unsignedint output_configured_rate; unsignedint buffer_size_frames; // Audio output latency used in cubeb_stream_get_position(). unsignedint output_latency_ms;
int64_t lastPosition;
int64_t lastPositionTimeStamp;
int64_t lastCompensativePosition; int voice_input; int voice_output;
std::unique_ptr<cubeb_stream_params> input_params;
std::unique_ptr<cubeb_stream_params> output_params; // A non-empty buffer means that f32 -> int16 conversion need to happen
std::vector<float> conversion_buffer_output;
std::vector<float> conversion_buffer_input;
};
staticint
opensl_get_draining(cubeb_stream * stm)
{ #ifdef DEBUG int r = pthread_mutex_trylock(&stm->mutex);
XASSERT((r == EDEADLK || r == EBUSY) && "get_draining: mutex should be locked but it's not."); #endif return stm->draining;
}
staticvoid
opensl_set_draining(cubeb_stream * stm, int value)
{ #ifdef DEBUG int r = pthread_mutex_trylock(&stm->mutex);
LOG("set draining try r = %d", r);
XASSERT((r == EDEADLK || r == EBUSY) && "set_draining: mutex should be locked but it's not."); #endif
XASSERT(value == 0 || value == 1);
stm->draining = value;
}
staticvoid
opensl_notify_drained(cubeb_stream * stm)
{
XASSERT(stm); int r = pthread_mutex_lock(&stm->mutex);
XASSERT(r == 0); int draining = opensl_get_draining(stm);
r = pthread_mutex_unlock(&stm->mutex);
XASSERT(r == 0); if (draining) {
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); if (stm->play) {
LOG("stop player in play_callback");
r = opensl_stop_player(stm);
XASSERT(r == CUBEB_OK);
} if (stm->recorderItf) {
r = opensl_stop_recorder(stm);
XASSERT(r == CUBEB_OK);
}
}
}
static uint32_t
opensl_get_shutdown(cubeb_stream * stm)
{ #ifdef DEBUG int r = pthread_mutex_trylock(&stm->mutex);
XASSERT((r == EDEADLK || r == EBUSY) && "get_shutdown: mutex should be locked but it's not."); #endif return stm->shutdown;
}
staticvoid
opensl_set_shutdown(cubeb_stream * stm, uint32_t value)
{ #ifdef DEBUG int r = pthread_mutex_trylock(&stm->mutex);
LOG("set shutdown try r = %d", r);
XASSERT((r == EDEADLK || r == EBUSY) && "set_shutdown: mutex should be locked but it's not."); #endif
XASSERT(value == 0 || value == 1);
stm->shutdown = value;
}
ALOGV("bufferqueue_callback: resampler fill returned %ld frames", written); if (written < 0 ||
written * stm->framesize > static_cast<uint32_t>(stm->queuebuf_len)) {
ALOGV("bufferqueue_callback: error, shutting down");
r = pthread_mutex_lock(&stm->mutex);
XASSERT(r == 0);
opensl_set_shutdown(stm, 1);
r = pthread_mutex_unlock(&stm->mutex);
XASSERT(r == 0);
opensl_stop_player(stm);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); return;
}
}
// Keep sending silent data even in draining mode to prevent the audio // back-end from being stopped automatically by OpenSL/ES.
XASSERT(static_cast<uint32_t>(stm->queuebuf_len) >= written * stm->framesize);
memset(reinterpret_cast<uint8_t *>(buf) + written * stm->framesize, 0,
stm->queuebuf_len - written * stm->framesize);
res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->queuebuf_len);
XASSERT(res == SL_RESULT_SUCCESS);
stm->queuebuf_idx = (stm->queuebuf_idx + 1) % stm->queuebuf_capacity;
if (!draining &&
written * stm->framesize < static_cast<uint32_t>(stm->queuebuf_len)) {
LOG("bufferqueue_callback draining");
r = pthread_mutex_lock(&stm->mutex);
XASSERT(r == 0);
int64_t written_duration =
INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
opensl_set_draining(stm, 1);
r = pthread_mutex_unlock(&stm->mutex);
XASSERT(r == 0);
if (written_duration == 0) { // since we didn't write any sample, it's not possible to reach the marker // time and trigger the callback. We should initiative notify drained.
opensl_notify_drained(stm);
} else { // Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf // to make sure all the data has been processed.
(*stm->play)
->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
} return;
}
}
int current_index = stm->input_buffer_index; void * last_buffer = nullptr;
if (current_index < 0) { // This is the first enqueue
current_index = 0;
} else { // The current index hold the last filled buffer get it before advance // index.
last_buffer = stm->input_buffer_array[current_index]; // Advance to get next available buffer
current_index = static_cast<int>((current_index + 1) % stm->input_array_capacity);
} // enqueue next empty buffer to be filled by the recorder
SLresult res = (*stm->recorderBufferQueueItf)
->Enqueue(stm->recorderBufferQueueItf,
stm->input_buffer_array[current_index],
stm->input_buffer_length); if (res != SL_RESULT_SUCCESS) {
LOG("Enqueue recorder failed. Error code: %u", res); return CUBEB_ERROR;
} // All good, update buffer and index.
stm->input_buffer_index = current_index; if (last_filled_buffer) {
*last_filled_buffer = last_buffer;
} return CUBEB_OK;
}
// If necessary, convert and returns an input buffer. // Otherwise, just returns the pointer that has been passed in. void *
convert_input_buffer_if_needed(cubeb_stream * stm, void * input_buffer,
uint32_t sample_count)
{ // Perform conversion if needed if (stm->conversion_buffer_input.empty()) { return input_buffer;
} if (stm->conversion_buffer_input.size() < sample_count) {
stm->conversion_buffer_input.resize(sample_count);
}
int16_t * int16_buf = reinterpret_cast<int16_t *>(input_buffer); for (uint32_t i = 0; i < sample_count; i++) {
stm->conversion_buffer_input[i] = static_cast<float>(int16_buf[i]) / 32768.f;
} return stm->conversion_buffer_input.data();
}
int r = pthread_mutex_lock(&stm->mutex);
XASSERT(r == 0);
uint32_t shutdown = opensl_get_shutdown(stm); int draining = opensl_get_draining(stm);
r = pthread_mutex_unlock(&stm->mutex);
XASSERT(r == 0);
if (shutdown || draining) { // According to the OpenSL ES 1.1 Specification, 8.14 SLBufferQueueItf // page 184, on transition to the SL_RECORDSTATE_STOPPED state, // the application should continue to enqueue buffers onto the queue // to retrieve the residual recorded data in the system.
r = opensl_enqueue_recorder(stm, nullptr);
XASSERT(r == CUBEB_OK); return;
}
// Enqueue next available buffer and get the last filled buffer. void * input_buffer = nullptr;
r = opensl_enqueue_recorder(stm, &input_buffer);
XASSERT(r == CUBEB_OK);
XASSERT(input_buffer);
int r = pthread_mutex_lock(&stm->mutex);
XASSERT(r == 0); int draining = opensl_get_draining(stm);
uint32_t shutdown = opensl_get_shutdown(stm);
r = pthread_mutex_unlock(&stm->mutex);
XASSERT(r == 0);
if (shutdown || draining) { /* On draining and shutdown the recorder should have been stoped from *theonesettheflags.Accordinttothedoc,ontransitionto *theSL_RECORDSTATE_STOPPEDstate,theapplicationshould *continuetoenqueuebuffersontothequeuetoretrievetheresidual
* recorded data in the system. */
LOG("Input shutdown %d or drain %d", shutdown, draining); int r = opensl_enqueue_recorder(stm, nullptr);
XASSERT(r == CUBEB_OK); return;
}
// Enqueue next available buffer and get the last filled buffer. void * input_buffer = nullptr;
r = opensl_enqueue_recorder(stm, &input_buffer);
XASSERT(r == CUBEB_OK);
XASSERT(input_buffer);
XASSERT(stm->input_queue);
r = array_queue_push(stm->input_queue, input_buffer); if (r == -1) {
LOG("Input queue is full, drop input ..."); return;
}
LOG("Input pushed in the queue, input array %zu",
array_queue_get_size(stm->input_queue));
}
int r = pthread_mutex_lock(&stm->mutex);
XASSERT(r == 0); int draining = opensl_get_draining(stm);
uint32_t shutdown = opensl_get_shutdown(stm);
r = pthread_mutex_unlock(&stm->mutex);
XASSERT(r == 0);
// Get output void * output_buffer = nullptr;
r = pthread_mutex_lock(&stm->mutex);
XASSERT(r == 0);
output_buffer = stm->queuebuf[stm->queuebuf_idx]; void * output_buffer_original_ptr = output_buffer; // Advance the output buffer queue index
stm->queuebuf_idx = (stm->queuebuf_idx + 1) % stm->queuebuf_capacity;
r = pthread_mutex_unlock(&stm->mutex);
XASSERT(r == 0);
if (shutdown || draining) {
LOG("Shutdown/draining, send silent"); // Set silent on buffer
memset(output_buffer, 0, stm->queuebuf_len);
// Enqueue data in player buffer queue
res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len);
XASSERT(res == SL_RESULT_SUCCESS); return;
}
// Get input. void * input_buffer = array_queue_pop(stm->input_queue); long input_frame_count = stm->input_buffer_length / stm->input_frame_size; long sample_count = input_frame_count * stm->input_params->channels; long frames_needed = stm->queuebuf_len / stm->framesize;
uint32_t output_sample_count =
stm->output_params->channels * stm->queuebuf_len / stm->framesize;
if (!input_buffer) {
LOG("Input hole set silent input buffer");
input_buffer = stm->input_silent_buffer;
}
long written = 0; // Trigger user callback through resampler
written =
cubeb_resampler_fill(stm->resampler, input_buffer, &input_frame_count,
output_buffer, frames_needed);
if (written < 0 || written > frames_needed) { // Error case
r = pthread_mutex_lock(&stm->mutex);
XASSERT(r == 0);
opensl_set_shutdown(stm, 1);
r = pthread_mutex_unlock(&stm->mutex);
XASSERT(r == 0);
opensl_stop_player(stm);
opensl_stop_recorder(stm);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
memset(output_buffer, 0, stm->queuebuf_len);
// Enqueue data in player buffer queue
res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len);
XASSERT(res == SL_RESULT_SUCCESS); return;
}
// Advance total out written frames counter
r = pthread_mutex_lock(&stm->mutex);
XASSERT(r == 0);
stm->written += written;
r = pthread_mutex_unlock(&stm->mutex);
XASSERT(r == 0);
if (written < frames_needed) {
r = pthread_mutex_lock(&stm->mutex);
XASSERT(r == 0);
int64_t written_duration =
INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
opensl_set_draining(stm, 1);
r = pthread_mutex_unlock(&stm->mutex);
XASSERT(r == 0);
// Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf // to make sure all the data has been processed.
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
}
// Keep sending silent data even in draining mode to prevent the audio // back-end from being stopped automatically by OpenSL/ES.
memset((uint8_t *)output_buffer + written * stm->framesize, 0,
stm->queuebuf_len - written * stm->framesize);
// Enqueue data in player buffer queue
res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len);
XASSERT(res == SL_RESULT_SUCCESS);
}
#if (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP) int len = wrap_system_property_get("ro.build.version.sdk", version_string); #else int len = __system_property_get("ro.build.version.sdk", version_string); #endif if (len <= 0) {
LOG("Failed to get Android version!\n"); return len;
}
int version = (int)strtol(version_string, nullptr, 10);
LOG("Android version %d", version); return version;
} #endif
#ifdefined(__ANDROID__) int android_version = get_android_version(); if (android_version > 0 &&
android_version <= ANDROID_VERSION_GINGERBREAD_MR1) { // Don't even attempt to run on Gingerbread and lower
LOG("Error: Android version too old, exiting."); return CUBEB_ERROR;
} #endif
res = (*ctx->outmixObj)->Realize(ctx->outmixObj, SL_BOOLEAN_FALSE); if (res != SL_RESULT_SUCCESS) {
LOG("Error: Output mix object failure, exiting.");
opensl_destroy(ctx); return CUBEB_ERROR;
}
ctx->p_output_latency_function =
cubeb_output_latency_load_method(android_version); if (!cubeb_output_latency_method_is_loaded(ctx->p_output_latency_function)) {
LOG("Warning: output latency is not available, cubeb_stream_get_position() " "is not supported");
}
staticint
opensl_set_format(SLDataFormat_PCM * format, cubeb_stream_params * params)
{
XASSERT(format);
XASSERT(params);
// If this function is called, this backend has been compiled with an older // version of Android, that doesn't support floating point audio IO. // The stream is configured with int16 of the proper endianess, and conversion // will happen during playback.
const SLboolean lSoundRecorderReqs[] = {
SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; // create the audio recorder abstract object
SLresult res =
(*stm->context->eng)
->CreateAudioRecorder(stm->context->eng, &stm->recorderObj,
&dataSource, &dataSink,
NELEMS(lSoundRecorderIIDs),
lSoundRecorderIIDs, lSoundRecorderReqs); // Sample rate not supported. Try again with default sample rate! if (res == SL_RESULT_CONTENT_UNSUPPORTED) { if (stm->output_enabled && stm->output_configured_rate != 0) { // Set the same with the player. Since there is no // api for input device this is a safe choice.
stm->input_device_rate = stm->output_configured_rate;
} else { // The output preferred rate is used for an input only scenario. // The default rate expected to be supported from all android // devices.
stm->input_device_rate = DEFAULT_SAMPLE_RATE;
}
*format_sample_rate = stm->input_device_rate * 1000;
res = (*stm->context->eng)
->CreateAudioRecorder(
stm->context->eng, &stm->recorderObj, &dataSource,
&dataSink, NELEMS(lSoundRecorderIIDs),
lSoundRecorderIIDs, lSoundRecorderReqs);
} if (res != SL_RESULT_SUCCESS) {
LOG("Failed to create recorder, not trying other input" " rate. Error code: %u",
res); return CUBEB_ERROR;
} // It's always possible to use int16 regardless of the Android version. // However if compiling for older Android version, it's possible to // request f32 audio, but Android only supports int16, in which case a // conversion need to happen. if ((params->format == CUBEB_SAMPLE_FLOAT32NE ||
params->format == CUBEB_SAMPLE_FLOAT32BE) &&
!using_floats) { // setup conversion from f32 to int16
LOG("Input stream configured for using float, but not supported: a " "conversion will be performed");
stm->conversion_buffer_input.resize(1);
} return CUBEB_OK;
});
if (rv != CUBEB_OK) {
LOG("Could not initialize recorder."); return rv;
}
SLresult res; if (get_android_version() > ANDROID_VERSION_JELLY_BEAN) {
SLAndroidConfigurationItf recorderConfig;
res = (*stm->recorderObj)
->GetInterface(stm->recorderObj,
stm->context->SL_IID_ANDROIDCONFIGURATION,
&recorderConfig);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get the android configuration interface for recorder. " "Error " "code: %u",
res); return CUBEB_ERROR;
}
// Voice recognition is the lowest latency, according to the docs. Camcorder // uses a microphone that is in the same direction as the camera.
SLint32 streamType = stm->voice_input
? SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
: SL_ANDROID_RECORDING_PRESET_CAMCORDER;
res =
(*recorderConfig)
->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET,
&streamType, sizeof(SLint32));
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to set the android configuration to VOICE for the recorder. " "Error code: %u",
res); return CUBEB_ERROR;
}
} // realize the audio recorder
res = (*stm->recorderObj)->Realize(stm->recorderObj, SL_BOOLEAN_FALSE); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to realize recorder. Error code: %u", res); return CUBEB_ERROR;
} // get the record interface
res = (*stm->recorderObj)
->GetInterface(stm->recorderObj, stm->context->SL_IID_RECORD,
&stm->recorderItf); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get recorder interface. Error code: %u", res); return CUBEB_ERROR;
}
res = (*stm->recorderItf)
->RegisterCallback(stm->recorderItf, recorder_marker_callback, stm); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to register recorder marker callback. Error code: %u", res); return CUBEB_ERROR;
}
uint32_t preferred_sampling_rate = stm->user_output_rate;
SLresult res = SL_RESULT_CONTENT_UNSUPPORTED; if (preferred_sampling_rate) {
res = (*stm->context->eng)
->CreateAudioPlayer(stm->context->eng, &stm->playerObj,
&source, &sink, NELEMS(ids), ids, req);
}
// Sample rate not supported? Try again with primary sample rate! if (res == SL_RESULT_CONTENT_UNSUPPORTED &&
preferred_sampling_rate != DEFAULT_SAMPLE_RATE) {
preferred_sampling_rate = DEFAULT_SAMPLE_RATE;
*format_sample_rate = preferred_sampling_rate * 1000;
res = (*stm->context->eng)
->CreateAudioPlayer(stm->context->eng, &stm->playerObj,
&source, &sink, NELEMS(ids), ids, req);
}
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to create audio player. Error code: %u", res); return CUBEB_ERROR;
}
stm->output_configured_rate = preferred_sampling_rate;
// It's always possible to use int16 regardless of the Android version. // However if compiling for older Android version, it's possible to // request f32 audio, but Android only supports int16, in which case a // conversion need to happen. if ((params->format == CUBEB_SAMPLE_FLOAT32NE ||
params->format == CUBEB_SAMPLE_FLOAT32BE) &&
!using_floats) { // setup conversion from f32 to int16
LOG("Input stream configured for using float, but not supported: a " "conversion will be performed");
stm->conversion_buffer_output.resize(1);
}
// Calculate the capacity of input array
stm->queuebuf_capacity = NBUFS; // Allocate input arrays
stm->queuebuf = (void **)calloc(1, sizeof(void *) * stm->queuebuf_capacity); for (uint32_t i = 0; i < stm->queuebuf_capacity; ++i) {
stm->queuebuf[i] = calloc(1, stm->queuebuf_len);
XASSERT(stm->queuebuf[i]);
}
SLAndroidConfigurationItf playerConfig = nullptr;
SLresult res; if (get_android_version() >= ANDROID_VERSION_N_MR1) {
res = (*stm->playerObj)
->GetInterface(stm->playerObj,
stm->context->SL_IID_ANDROIDCONFIGURATION,
&playerConfig); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get Android configuration interface. Error code: %u", res); return CUBEB_ERROR;
}
SLint32 streamType = SL_ANDROID_STREAM_MEDIA; if (stm->voice_output) {
streamType = SL_ANDROID_STREAM_VOICE;
}
res = (*playerConfig)
->SetConfiguration(playerConfig, SL_ANDROID_KEY_STREAM_TYPE,
&streamType, sizeof(streamType)); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to set Android configuration to %d Error code: %u",
streamType, res);
}
SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_LATENCY; if (stm->buffer_size_frames > POWERSAVE_LATENCY_FRAMES_THRESHOLD) {
LOG("Audio stream configured for power saving");
performanceMode = SL_ANDROID_PERFORMANCE_POWER_SAVING;
}
res = (*playerConfig)
->SetConfiguration(playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE,
&performanceMode, sizeof(performanceMode)); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to set Android performance mode to %d Error code: %u. This " "is not fatal.",
performanceMode, res);
}
}
res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to realize player object. Error code: %u", res); return CUBEB_ERROR;
}
// There are two ways of getting the audio output latency: // - a configuration value, only available on some devices (notably devices // running FireOS) // - A Java method, that we call using JNI. // // The first method is prefered, if available, because it can account for more // latency causes, and is more precise.
// Latency has to be queried after the realization of the interface, when // using SL_IID_ANDROIDCONFIGURATION.
SLuint32 audioLatency = 0;
SLuint32 paramSize = sizeof(SLuint32); // The reported latency is in milliseconds. if (playerConfig) {
res = (*playerConfig)
->GetConfiguration(playerConfig,
(const SLchar *)"androidGetAudioLatency",
¶mSize, &audioLatency); if (res == SL_RESULT_SUCCESS) {
LOG("Got playback latency using android configuration extension");
stm->output_latency_ms = audioLatency;
}
} // `playerConfig` is available, but the above failed, or `playerConfig` is not // available. In both cases, we need to acquire the output latency by an other // mean. if ((playerConfig && res != SL_RESULT_SUCCESS) || !playerConfig) { if (cubeb_output_latency_method_is_loaded(
stm->context->p_output_latency_function)) {
LOG("Got playback latency using JNI");
stm->output_latency_ms =
cubeb_get_output_latency(stm->context->p_output_latency_function);
} else {
LOG("No alternate latency querying method loaded, A/V sync will be off.");
stm->output_latency_ms = 0;
}
}
res =
(*stm->playerObj)
->GetInterface(stm->playerObj, stm->context->SL_IID_PLAY, &stm->play); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get play interface. Error code: %u", res); return CUBEB_ERROR;
}
res = (*stm->playerObj)
->GetInterface(stm->playerObj, stm->context->SL_IID_BUFFERQUEUE,
&stm->bufq); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get bufferqueue interface. Error code: %u", res); return CUBEB_ERROR;
}
res = (*stm->playerObj)
->GetInterface(stm->playerObj, stm->context->SL_IID_VOLUME,
&stm->volume); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get volume interface. Error code: %u", res); return CUBEB_ERROR;
}
res = (*stm->play)->RegisterCallback(stm->play, play_callback, stm); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to register play callback. Error code: %u", res); return CUBEB_ERROR;
}
// Work around wilhelm/AudioTrack badness, bug 1221228
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)0);
res = (*stm->play)
->SetCallbackEventsMask(stm->play,
(SLuint32)SL_PLAYEVENT_HEADATMARKER); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to set headatmarker event mask. Error code: %u", res); return CUBEB_ERROR;
}
slBufferQueueCallback player_callback = bufferqueue_callback; if (stm->input_enabled) {
player_callback = player_fullduplex_callback;
}
res = (*stm->bufq)->RegisterCallback(stm->bufq, player_callback, stm); if (res != SL_RESULT_SUCCESS) {
LOG("Failed to register bufferqueue callback. Error code: %u", res); 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 = reinterpret_cast<uint8_t *>(stm->queuebuf[stm->queuebuf_idx++]);
memset(buf, 0, stm->framesize);
res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->framesize);
XASSERT(res == SL_RESULT_SUCCESS);
}
XASSERT(ctx); if (input_device || output_device) {
LOG("Device selection is not supported in Android. The default will be " "used");
}
*stream = nullptr;
int r = opensl_validate_stream_param(output_stream_params); if (r != CUBEB_OK) {
LOG("Output stream params not valid"); return r;
}
r = opensl_validate_stream_param(input_stream_params); if (r != CUBEB_OK) {
LOG("Input stream params not valid"); return r;
}
// Use the actual configured rates for input // and output.
cubeb_stream_params input_params; if (input_stream_params) {
input_params = *input_stream_params;
input_params.rate = stm->input_device_rate;
}
cubeb_stream_params output_params; if (output_stream_params) {
output_params = *output_stream_params;
output_params.rate = stm->output_configured_rate;
}
// If we're still draining at stream destroy time, pause the streams now so we // can destroy them safely. if (stm->draining) {
opensl_stream_stop(stm);
} // Sleep for 10ms to give active streams time to pause so that no further // buffer callbacks occur. Inspired by the same workaround (sleepBeforeClose) // in liboboe.
usleep(10 * 1000);
if (stm->playerObj) {
(*stm->playerObj)->Destroy(stm->playerObj);
stm->playerObj = nullptr;
stm->play = nullptr;
stm->bufq = nullptr; for (uint32_t i = 0; i < stm->queuebuf_capacity; ++i) {
free(stm->queuebuf[i]);
}
}
if (stm->recorderObj) { int r = opensl_destroy_recorder(stm);
XASSERT(r == CUBEB_OK);
}
if (stm->resampler) {
cubeb_resampler_destroy(stm->resampler);
}
res = (*stm->volume)->GetMaxVolumeLevel(stm->volume, &max_level);
if (res != SL_RESULT_SUCCESS) { return CUBEB_ERROR;
}
/* millibels are 100*dB, so the conversion from the volume's linear amplitude *is100*20*log(volume).Howeverweclamptheresultingvaluebefore *passingittolroundf()inordertopreventitfromsilentlyreturningan
* erroneous value when the unclamped value exceeds the size of a long. */
unclamped_millibels = 100.0f * 20.0f * log10f(fmaxf(volume, 0.0f));
unclamped_millibels = fmaxf(unclamped_millibels, SL_MILLIBEL_MIN);
unclamped_millibels = fminf(unclamped_millibels, max_level);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.