#include <sys/types.h>
#include <cassert>
#include <android/log.h>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <oboe/AudioStream.h>
#include <common/AudioClock.h>
#include "common/OboeDebug.h"
#include "oboe/AudioStreamBuilder.h"
#include "AudioStreamOpenSLES.h"
#include "OpenSLESUtilities.h"
using namespace oboe;
AudioStreamOpenSLES::AudioStreamOpenSLES(const AudioStreamBuilder &builder)
: AudioStreamBuffered(builder) {
mDeviceId = kUnspecified;
mSessionId = SessionId::None;
}
static constexpr int32_t kHighLatencyBufferSizeMillis = 20; static constexpr SLuint32 kAudioChannelCountMax = 30; static constexpr SLuint32 SL_ANDROID_UNKNOWN_CHANNELMASK = 0;
SLuint32 AudioStreamOpenSLES::channelCountToChannelMaskDefault(int channelCount) const {
if (channelCount > kAudioChannelCountMax) {
return SL_ANDROID_UNKNOWN_CHANNELMASK;
}
SLuint32 bitfield = (1 << channelCount) - 1;
if(getSdkVersion() >= __ANDROID_API_N__) {
return SL_ANDROID_MAKE_INDEXED_CHANNEL_MASK(bitfield);
}
return bitfield;
}
static bool s_isLittleEndian() {
static uint32_t value = 1;
return (*reinterpret_cast<uint8_t *>(&value) == 1); }
SLuint32 AudioStreamOpenSLES::getDefaultByteOrder() {
return s_isLittleEndian() ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
}
Result AudioStreamOpenSLES::open() {
LOGI("AudioStreamOpenSLES::open() chans=%d, rate=%d", mChannelCount, mSampleRate);
if (mFormat != AudioFormat::I16 && mFormat != AudioFormat::Float) {
LOGW("%s() Android's OpenSL ES implementation only supports I16 and Float. Format: %d",
__func__, mFormat);
return Result::ErrorInvalidFormat;
}
SLresult result = EngineOpenSLES::getInstance().open();
if (SL_RESULT_SUCCESS != result) {
return Result::ErrorInternal;
}
Result oboeResult = AudioStreamBuffered::open();
if (oboeResult != Result::OK) {
EngineOpenSLES::getInstance().close();
return oboeResult;
}
if (mSampleRate == kUnspecified) {
mSampleRate = DefaultStreamValues::SampleRate;
}
if (mChannelCount == kUnspecified) {
mChannelCount = DefaultStreamValues::ChannelCount;
}
if (mContentType == kUnspecified) {
mContentType = ContentType::Music;
}
if (static_cast<const int32_t>(mUsage) == kUnspecified) {
mUsage = Usage::Media;
}
mSharingMode = SharingMode::Shared;
return Result::OK;
}
SLresult AudioStreamOpenSLES::finishCommonOpen(SLAndroidConfigurationItf configItf) {
mPrivacySensitiveMode = PrivacySensitiveMode::Unspecified;
mAllowedCapturePolicy = AllowedCapturePolicy::Unspecified;
mSpatializationBehavior = SpatializationBehavior::Never;
SLresult result = registerBufferQueueCallback();
if (SL_RESULT_SUCCESS != result) {
return result;
}
result = updateStreamParameters(configItf);
if (SL_RESULT_SUCCESS != result) {
return result;
}
Result oboeResult = configureBufferSizes(mSampleRate);
if (Result::OK != oboeResult) {
return (SLresult) oboeResult;
}
allocateFifo();
calculateDefaultDelayBeforeCloseMillis();
return SL_RESULT_SUCCESS;
}
static int32_t roundUpDivideByN(int32_t x, int32_t n) {
return (x + n - 1) / n;
}
int32_t AudioStreamOpenSLES::calculateOptimalBufferQueueLength() {
int32_t queueLength = kBufferQueueLengthDefault;
int32_t likelyFramesPerBurst = estimateNativeFramesPerBurst();
int32_t minCapacity = mBufferCapacityInFrames; minCapacity = std::max(minCapacity, kDoubleBufferCount * mFramesPerCallback);
if (minCapacity > 0) {
int32_t queueLengthFromCapacity = roundUpDivideByN(minCapacity, likelyFramesPerBurst);
queueLength = std::max(queueLength, queueLengthFromCapacity);
}
queueLength = std::min(queueLength, kBufferQueueLengthMax); return queueLength;
}
int32_t AudioStreamOpenSLES::estimateNativeFramesPerBurst() {
int32_t framesPerBurst = DefaultStreamValues::FramesPerBurst;
LOGD("AudioStreamOpenSLES:%s() DefaultStreamValues::FramesPerBurst = %d",
__func__, DefaultStreamValues::FramesPerBurst);
framesPerBurst = std::max(framesPerBurst, 16);
int32_t sampleRate = 48000;
sampleRate = (DefaultStreamValues::SampleRate > 0)
? DefaultStreamValues::SampleRate : sampleRate;
sampleRate = (mSampleRate > 0) ? mSampleRate : sampleRate;
int32_t framesPerHighLatencyBuffer =
(kHighLatencyBufferSizeMillis * sampleRate) / kMillisPerSecond;
if (getSdkVersion() >= __ANDROID_API_N_MR1__
&& mPerformanceMode != PerformanceMode::LowLatency
&& framesPerBurst < framesPerHighLatencyBuffer) {
int32_t numBursts = roundUpDivideByN(framesPerHighLatencyBuffer, framesPerBurst);
framesPerBurst *= numBursts;
LOGD("AudioStreamOpenSLES:%s() NOT low latency, numBursts = %d, mSampleRate = %d, set framesPerBurst = %d",
__func__, numBursts, mSampleRate, framesPerBurst);
}
return framesPerBurst;
}
Result AudioStreamOpenSLES::configureBufferSizes(int32_t sampleRate) {
LOGD("AudioStreamOpenSLES:%s(%d) initial mFramesPerBurst = %d, mFramesPerCallback = %d",
__func__, mSampleRate, mFramesPerBurst, mFramesPerCallback);
mFramesPerBurst = estimateNativeFramesPerBurst();
mFramesPerCallback = (mFramesPerCallback > 0) ? mFramesPerCallback : mFramesPerBurst;
LOGD("AudioStreamOpenSLES:%s(%d) final mFramesPerBurst = %d, mFramesPerCallback = %d",
__func__, mSampleRate, mFramesPerBurst, mFramesPerCallback);
mBytesPerCallback = mFramesPerCallback * getBytesPerFrame();
if (mBytesPerCallback <= 0) {
LOGE("AudioStreamOpenSLES::open() bytesPerCallback < 0 = %d, bad format?",
mBytesPerCallback);
return Result::ErrorInvalidFormat; }
for (int i = 0; i < mBufferQueueLength; ++i) {
mCallbackBuffer[i] = std::make_unique<uint8_t[]>(mBytesPerCallback);
}
if (!usingFIFO()) {
mBufferCapacityInFrames = mFramesPerBurst * mBufferQueueLength;
if (mBufferCapacityInFrames <= 0) {
mBufferCapacityInFrames = 0;
LOGE("AudioStreamOpenSLES::open() numeric overflow because mFramesPerBurst = %d",
mFramesPerBurst);
return Result::ErrorOutOfRange;
}
mBufferSizeInFrames = mBufferCapacityInFrames;
}
return Result::OK;
}
SLuint32 AudioStreamOpenSLES::convertPerformanceMode(PerformanceMode oboeMode) const {
SLuint32 openslMode = SL_ANDROID_PERFORMANCE_NONE;
switch(oboeMode) {
case PerformanceMode::None:
openslMode = SL_ANDROID_PERFORMANCE_NONE;
break;
case PerformanceMode::LowLatency:
openslMode = (getSessionId() == SessionId::None) ? SL_ANDROID_PERFORMANCE_LATENCY : SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS;
break;
case PerformanceMode::PowerSaving:
openslMode = SL_ANDROID_PERFORMANCE_POWER_SAVING;
break;
default:
break;
}
return openslMode;
}
PerformanceMode AudioStreamOpenSLES::convertPerformanceMode(SLuint32 openslMode) const {
PerformanceMode oboeMode = PerformanceMode::None;
switch(openslMode) {
case SL_ANDROID_PERFORMANCE_NONE:
oboeMode = PerformanceMode::None;
break;
case SL_ANDROID_PERFORMANCE_LATENCY:
case SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS:
oboeMode = PerformanceMode::LowLatency;
break;
case SL_ANDROID_PERFORMANCE_POWER_SAVING:
oboeMode = PerformanceMode::PowerSaving;
break;
default:
break;
}
return oboeMode;
}
void AudioStreamOpenSLES::logUnsupportedAttributes() {
if (mDeviceId != kUnspecified) {
LOGW("Device ID [AudioStreamBuilder::setDeviceId()] "
"is not supported on OpenSLES streams.");
}
if (mSharingMode != SharingMode::Shared) {
LOGW("SharingMode [AudioStreamBuilder::setSharingMode()] "
"is not supported on OpenSLES streams.");
}
int sdkVersion = getSdkVersion();
if (mPerformanceMode != PerformanceMode::None && sdkVersion < __ANDROID_API_N_MR1__) {
LOGW("PerformanceMode [AudioStreamBuilder::setPerformanceMode()] "
"is not supported on OpenSLES streams running on pre-Android N-MR1 versions.");
}
if (mContentType != ContentType::Music) {
LOGW("ContentType [AudioStreamBuilder::setContentType()] "
"is not supported on OpenSLES streams.");
}
if (mSessionId != SessionId::None) {
LOGW("SessionId [AudioStreamBuilder::setSessionId()] "
"is not supported on OpenSLES streams.");
}
if (mPrivacySensitiveMode != PrivacySensitiveMode::Unspecified) {
LOGW("PrivacySensitiveMode [AudioStreamBuilder::setPrivacySensitiveMode()] "
"is not supported on OpenSLES streams.");
}
if (mSpatializationBehavior != SpatializationBehavior::Unspecified) {
LOGW("SpatializationBehavior [AudioStreamBuilder::setSpatializationBehavior()] "
"is not supported on OpenSLES streams.");
}
if (mAllowedCapturePolicy != AllowedCapturePolicy::Unspecified) {
LOGW("AllowedCapturePolicy [AudioStreamBuilder::setAllowedCapturePolicy()] "
"is not supported on OpenSLES streams.");
}
}
SLresult AudioStreamOpenSLES::configurePerformanceMode(SLAndroidConfigurationItf configItf) {
if (configItf == nullptr) {
LOGW("%s() called with NULL configuration", __func__);
mPerformanceMode = PerformanceMode::None;
return SL_RESULT_INTERNAL_ERROR;
}
if (getSdkVersion() < __ANDROID_API_N_MR1__) {
LOGW("%s() not supported until N_MR1", __func__);
mPerformanceMode = PerformanceMode::None;
return SL_RESULT_SUCCESS;
}
SLresult result = SL_RESULT_SUCCESS;
SLuint32 performanceMode = convertPerformanceMode(getPerformanceMode());
result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE,
&performanceMode, sizeof(performanceMode));
if (SL_RESULT_SUCCESS != result) {
LOGW("SetConfiguration(PERFORMANCE_MODE, SL %u) returned %s",
performanceMode, getSLErrStr(result));
mPerformanceMode = PerformanceMode::None;
}
return result;
}
SLresult AudioStreamOpenSLES::updateStreamParameters(SLAndroidConfigurationItf configItf) {
SLresult result = SL_RESULT_SUCCESS;
if(getSdkVersion() >= __ANDROID_API_N_MR1__ && configItf != nullptr) {
SLuint32 performanceMode = 0;
SLuint32 performanceModeSize = sizeof(performanceMode);
result = (*configItf)->GetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE,
&performanceModeSize, &performanceMode);
if (getSdkVersion() <= __ANDROID_API_O_MR1__) {
result = SL_RESULT_SUCCESS; }
if (SL_RESULT_SUCCESS != result) {
LOGW("GetConfiguration(SL_ANDROID_KEY_PERFORMANCE_MODE) returned %d", result);
mPerformanceMode = PerformanceMode::None; } else {
mPerformanceMode = convertPerformanceMode(performanceMode); }
} else {
mPerformanceMode = PerformanceMode::None; }
return result;
}
Result AudioStreamOpenSLES::close_l() {
if (mState == StreamState::Closed) {
return Result::ErrorClosed;
}
AudioStreamBuffered::close();
onBeforeDestroy();
if (mObjectInterface != nullptr) {
(*mObjectInterface)->Destroy(mObjectInterface);
mObjectInterface = nullptr;
}
onAfterDestroy();
mSimpleBufferQueueInterface = nullptr;
EngineOpenSLES::getInstance().close();
setState(StreamState::Closed);
return Result::OK;
}
SLresult AudioStreamOpenSLES::enqueueCallbackBuffer(SLAndroidSimpleBufferQueueItf bq) {
SLresult result = (*bq)->Enqueue(
bq, mCallbackBuffer[mCallbackBufferIndex].get(), mBytesPerCallback);
mCallbackBufferIndex = (mCallbackBufferIndex + 1) % mBufferQueueLength;
return result;
}
int32_t AudioStreamOpenSLES::getBufferDepth(SLAndroidSimpleBufferQueueItf bq) {
SLAndroidSimpleBufferQueueState queueState;
SLresult result = (*bq)->GetState(bq, &queueState);
return (result == SL_RESULT_SUCCESS) ? queueState.count : -1;
}
bool AudioStreamOpenSLES::processBufferCallback(SLAndroidSimpleBufferQueueItf bq) {
bool shouldStopStream = false;
DataCallbackResult result =
fireDataCallback(mCallbackBuffer[mCallbackBufferIndex].get(), mFramesPerCallback);
if (result == DataCallbackResult::Continue) {
SLresult enqueueResult = enqueueCallbackBuffer(bq);
if (enqueueResult != SL_RESULT_SUCCESS) {
LOGE("%s() returned %d", __func__, enqueueResult);
shouldStopStream = true;
}
if (getDirection() == Direction::Input) {
mFramesRead += mFramesPerCallback;
} else {
mFramesWritten += mFramesPerCallback;
}
} else if (result == DataCallbackResult::Stop) {
LOGD("Oboe callback returned Stop");
shouldStopStream = true;
} else {
LOGW("Oboe callback returned unexpected value = %d", result);
shouldStopStream = true;
}
if (shouldStopStream) {
mCallbackBufferIndex = 0;
}
return shouldStopStream;
}
static void bqCallbackGlue(SLAndroidSimpleBufferQueueItf bq, void *context) {
bool shouldStopStream = (reinterpret_cast<AudioStreamOpenSLES *>(context))
->processBufferCallback(bq);
if (shouldStopStream) {
(reinterpret_cast<AudioStreamOpenSLES *>(context))->requestStop();
}
}
SLresult AudioStreamOpenSLES::registerBufferQueueCallback() {
SLresult result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&mSimpleBufferQueueInterface);
if (SL_RESULT_SUCCESS != result) {
LOGE("get buffer queue interface:%p result:%s",
mSimpleBufferQueueInterface,
getSLErrStr(result));
} else {
result = (*mSimpleBufferQueueInterface)->RegisterCallback(mSimpleBufferQueueInterface,
bqCallbackGlue, this);
if (SL_RESULT_SUCCESS != result) {
LOGE("RegisterCallback result:%s", getSLErrStr(result));
}
}
return result;
}
int64_t AudioStreamOpenSLES::getFramesProcessedByServer() {
updateServiceFrameCounter();
int64_t millis64 = mPositionMillis.get();
int64_t framesProcessed = millis64 * getSampleRate() / kMillisPerSecond;
return framesProcessed;
}
Result AudioStreamOpenSLES::waitForStateChange(StreamState currentState,
StreamState *nextState,
int64_t timeoutNanoseconds) {
Result oboeResult = Result::ErrorTimeout;
int64_t sleepTimeNanos = 20 * kNanosPerMillisecond; int64_t timeLeftNanos = timeoutNanoseconds;
while (true) {
const StreamState state = getState(); if (nextState != nullptr) {
*nextState = state;
}
if (currentState != state) { oboeResult = Result::OK;
break;
}
if (timeLeftNanos <= 0) {
break;
}
if (sleepTimeNanos > timeLeftNanos){
sleepTimeNanos = timeLeftNanos;
}
AudioClock::sleepForNanos(sleepTimeNanos);
timeLeftNanos -= sleepTimeNanos;
}
return oboeResult;
}