#include <cassert>
#include <stdint.h>
#include <stdlib.h>
#include "aaudio/AAudioLoader.h"
#include "aaudio/AudioStreamAAudio.h"
#include "common/AudioClock.h"
#include "common/OboeDebug.h"
#include "oboe/Utilities.h"
#include "AAudioExtensions.h"
#ifdef __ANDROID__
#include <sys/system_properties.h>
#include <common/QuirksManager.h>
#endif
#ifndef OBOE_FIX_FORCE_STARTING_TO_STARTED
#define OBOE_FIX_FORCE_STARTING_TO_STARTED 1
#endif
using namespace oboe;
AAudioLoader *AudioStreamAAudio::mLibLoader = nullptr;
static aaudio_data_callback_result_t oboe_aaudio_data_callback_proc(
AAudioStream *stream,
void *userData,
void *audioData,
int32_t numFrames) {
AudioStreamAAudio *oboeStream = reinterpret_cast<AudioStreamAAudio*>(userData);
if (oboeStream != nullptr) {
return static_cast<aaudio_data_callback_result_t>(
oboeStream->callOnAudioReady(stream, audioData, numFrames));
} else {
return static_cast<aaudio_data_callback_result_t>(DataCallbackResult::Stop);
}
}
static void oboe_aaudio_error_thread_proc(AudioStreamAAudio *oboeStream,
Result error) {
LOGD("%s(,%d) - entering >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", __func__, error);
AudioStreamErrorCallback *errorCallback = oboeStream->getErrorCallback();
if (errorCallback == nullptr) return; bool isErrorHandled = errorCallback->onError(oboeStream, error);
if (!isErrorHandled) {
oboeStream->requestStop();
errorCallback->onErrorBeforeClose(oboeStream, error);
oboeStream->close();
errorCallback->onErrorAfterClose(oboeStream, error);
}
LOGD("%s() - exiting <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", __func__);
}
static void oboe_aaudio_error_thread_proc_shared(std::shared_ptr<AudioStream> sharedStream,
Result error) {
AudioStreamAAudio *oboeStream = reinterpret_cast<AudioStreamAAudio*>(sharedStream.get());
oboe_aaudio_error_thread_proc(oboeStream, error);
}
namespace oboe {
AudioStreamAAudio::AudioStreamAAudio(const AudioStreamBuilder &builder)
: AudioStream(builder)
, mAAudioStream(nullptr) {
mCallbackThreadEnabled.store(false);
mLibLoader = AAudioLoader::getInstance();
}
bool AudioStreamAAudio::isSupported() {
mLibLoader = AAudioLoader::getInstance();
int openResult = mLibLoader->open();
return openResult == 0;
}
void AudioStreamAAudio::internalErrorCallback(
AAudioStream *stream,
void *userData,
aaudio_result_t error) {
oboe::Result oboeResult = static_cast<Result>(error);
AudioStreamAAudio *oboeStream = reinterpret_cast<AudioStreamAAudio*>(userData);
if (OboeGlobals::areWorkaroundsEnabled()
&& getSdkVersion() == __ANDROID_API_R__
&& oboeResult == oboe::Result::ErrorTimeout) {
oboeResult = oboe::Result::ErrorDisconnected;
LOGD("%s() ErrorTimeout changed to ErrorDisconnected to fix b/173928197", __func__);
}
oboeStream->mErrorCallbackResult = oboeResult;
std::shared_ptr<AudioStream> sharedStream = oboeStream->lockWeakThis();
if (oboeStream->wasErrorCallbackCalled()) { LOGE("%s() multiple error callbacks called!", __func__);
} else if (stream != oboeStream->getUnderlyingStream()) {
LOGW("%s() stream already closed or closing", __func__); } else if (sharedStream) {
std::thread t(oboe_aaudio_error_thread_proc_shared, sharedStream, oboeResult);
t.detach();
} else {
std::thread t(oboe_aaudio_error_thread_proc, oboeStream, oboeResult);
t.detach();
}
}
void AudioStreamAAudio::beginPerformanceHintInCallback() {
if (isPerformanceHintEnabled()) {
if (!mAdpfOpenAttempted) {
int64_t targetDurationNanos = (mFramesPerBurst * 1e9) / getSampleRate();
int adpfResult = mAdpfWrapper.open(gettid(), targetDurationNanos);
if (adpfResult < 0) {
LOGW("WARNING ADPF not supported, %d\n", adpfResult);
} else {
LOGD("ADPF is now active\n");
}
mAdpfOpenAttempted = true;
}
mAdpfWrapper.onBeginCallback();
} else if (!isPerformanceHintEnabled() && mAdpfOpenAttempted) {
LOGD("ADPF closed\n");
mAdpfWrapper.close();
mAdpfOpenAttempted = false;
}
}
void AudioStreamAAudio::endPerformanceHintInCallback(int32_t numFrames) {
if (mAdpfWrapper.isOpen()) {
double durationScaler = static_cast<double>(mFramesPerBurst) / numFrames;
if (durationScaler < 2.0) {
mAdpfWrapper.onEndCallback(durationScaler);
}
}
}
void AudioStreamAAudio::logUnsupportedAttributes() {
int sdkVersion = getSdkVersion();
if (sdkVersion < __ANDROID_API_P__) {
if (mUsage != Usage::Media) {
LOGW("Usage [AudioStreamBuilder::setUsage()] "
"is not supported on AAudio streams running on pre-Android P versions.");
}
if (mContentType != ContentType::Music) {
LOGW("ContentType [AudioStreamBuilder::setContentType()] "
"is not supported on AAudio streams running on pre-Android P versions.");
}
if (mSessionId != SessionId::None) {
LOGW("SessionId [AudioStreamBuilder::setSessionId()] "
"is not supported on AAudio streams running on pre-Android P versions.");
}
}
}
Result AudioStreamAAudio::open() {
Result result = Result::OK;
if (mAAudioStream != nullptr) {
return Result::ErrorInvalidState;
}
result = AudioStream::open();
if (result != Result::OK) {
return result;
}
AAudioStreamBuilder *aaudioBuilder;
result = static_cast<Result>(mLibLoader->createStreamBuilder(&aaudioBuilder));
if (result != Result::OK) {
return result;
}
int32_t capacity = mBufferCapacityInFrames;
constexpr int kCapacityRequiredForFastLegacyTrack = 4096; if (OboeGlobals::areWorkaroundsEnabled()
&& mDirection == oboe::Direction::Input
&& capacity != oboe::Unspecified
&& capacity < kCapacityRequiredForFastLegacyTrack
&& mPerformanceMode == oboe::PerformanceMode::LowLatency) {
capacity = kCapacityRequiredForFastLegacyTrack;
LOGD("AudioStreamAAudio.open() capacity changed from %d to %d for lower latency",
static_cast<int>(mBufferCapacityInFrames), capacity);
}
mLibLoader->builder_setBufferCapacityInFrames(aaudioBuilder, capacity);
if (mLibLoader->builder_setSessionId != nullptr) {
mLibLoader->builder_setSessionId(aaudioBuilder,
static_cast<aaudio_session_id_t>(mSessionId));
if (OboeGlobals::areWorkaroundsEnabled()
&& mSessionId != SessionId::None
&& mDirection == oboe::Direction::Output
&& mPerformanceMode == PerformanceMode::LowLatency) {
mPerformanceMode = PerformanceMode::None;
LOGD("AudioStreamAAudio.open() performance mode changed to None when session "
"id is requested");
}
}
if (mLibLoader->builder_setChannelMask != nullptr && mChannelMask != ChannelMask::Unspecified) {
mLibLoader->builder_setChannelMask(aaudioBuilder,
static_cast<aaudio_channel_mask_t>(mChannelMask));
} else {
mLibLoader->builder_setChannelCount(aaudioBuilder, mChannelCount);
}
mLibLoader->builder_setDeviceId(aaudioBuilder, mDeviceId);
mLibLoader->builder_setDirection(aaudioBuilder, static_cast<aaudio_direction_t>(mDirection));
mLibLoader->builder_setFormat(aaudioBuilder, static_cast<aaudio_format_t>(mFormat));
mLibLoader->builder_setSampleRate(aaudioBuilder, mSampleRate);
mLibLoader->builder_setSharingMode(aaudioBuilder,
static_cast<aaudio_sharing_mode_t>(mSharingMode));
mLibLoader->builder_setPerformanceMode(aaudioBuilder,
static_cast<aaudio_performance_mode_t>(mPerformanceMode));
if (mLibLoader->builder_setUsage != nullptr) {
mLibLoader->builder_setUsage(aaudioBuilder,
static_cast<aaudio_usage_t>(mUsage));
}
if (mLibLoader->builder_setContentType != nullptr) {
mLibLoader->builder_setContentType(aaudioBuilder,
static_cast<aaudio_content_type_t>(mContentType));
}
if (mLibLoader->builder_setInputPreset != nullptr) {
aaudio_input_preset_t inputPreset = mInputPreset;
if (getSdkVersion() <= __ANDROID_API_P__ && inputPreset == InputPreset::VoicePerformance) {
LOGD("InputPreset::VoicePerformance not supported before Q. Using VoiceRecognition.");
inputPreset = InputPreset::VoiceRecognition; }
mLibLoader->builder_setInputPreset(aaudioBuilder,
static_cast<aaudio_input_preset_t>(inputPreset));
}
if (mLibLoader->builder_setPackageName != nullptr && !mPackageName.empty()) {
mLibLoader->builder_setPackageName(aaudioBuilder,
mPackageName.c_str());
}
if (mLibLoader->builder_setAttributionTag != nullptr && !mAttributionTag.empty()) {
mLibLoader->builder_setAttributionTag(aaudioBuilder,
mAttributionTag.c_str());
}
if (mLibLoader->builder_setAllowedCapturePolicy != nullptr && mDirection == oboe::Direction::Output) {
mLibLoader->builder_setAllowedCapturePolicy(aaudioBuilder,
static_cast<aaudio_allowed_capture_policy_t>(mAllowedCapturePolicy));
}
if (mLibLoader->builder_setPrivacySensitive != nullptr && mDirection == oboe::Direction::Input
&& mPrivacySensitiveMode != PrivacySensitiveMode::Unspecified) {
mLibLoader->builder_setPrivacySensitive(aaudioBuilder,
mPrivacySensitiveMode == PrivacySensitiveMode::Enabled);
}
if (mLibLoader->builder_setIsContentSpatialized != nullptr) {
mLibLoader->builder_setIsContentSpatialized(aaudioBuilder, mIsContentSpatialized);
}
if (mLibLoader->builder_setSpatializationBehavior != nullptr) {
if (mSpatializationBehavior == SpatializationBehavior::Unspecified) {
mSpatializationBehavior = SpatializationBehavior::Never;
}
mLibLoader->builder_setSpatializationBehavior(aaudioBuilder,
static_cast<aaudio_spatialization_behavior_t>(mSpatializationBehavior));
} else {
mSpatializationBehavior = SpatializationBehavior::Never;
}
if (isDataCallbackSpecified()) {
mLibLoader->builder_setDataCallback(aaudioBuilder, oboe_aaudio_data_callback_proc, this);
mLibLoader->builder_setFramesPerDataCallback(aaudioBuilder, getFramesPerDataCallback());
if (!isErrorCallbackSpecified()) {
mErrorCallback = &mDefaultErrorCallback;
}
mLibLoader->builder_setErrorCallback(aaudioBuilder, internalErrorCallback, this);
}
{
AAudioStream *stream = nullptr;
result = static_cast<Result>(mLibLoader->builder_openStream(aaudioBuilder, &stream));
mAAudioStream.store(stream);
}
if (result != Result::OK) {
if (result == Result::ErrorInternal && mDirection == Direction::Input) {
LOGW("AudioStreamAAudio.open() may have failed due to lack of "
"audio recording permission.");
}
goto error2;
}
mDeviceId = mLibLoader->stream_getDeviceId(mAAudioStream);
mChannelCount = mLibLoader->stream_getChannelCount(mAAudioStream);
mSampleRate = mLibLoader->stream_getSampleRate(mAAudioStream);
mFormat = static_cast<AudioFormat>(mLibLoader->stream_getFormat(mAAudioStream));
mSharingMode = static_cast<SharingMode>(mLibLoader->stream_getSharingMode(mAAudioStream));
mPerformanceMode = static_cast<PerformanceMode>(
mLibLoader->stream_getPerformanceMode(mAAudioStream));
mBufferCapacityInFrames = mLibLoader->stream_getBufferCapacity(mAAudioStream);
mBufferSizeInFrames = mLibLoader->stream_getBufferSize(mAAudioStream);
mFramesPerBurst = mLibLoader->stream_getFramesPerBurst(mAAudioStream);
if (mLibLoader->stream_getUsage != nullptr) {
mUsage = static_cast<Usage>(mLibLoader->stream_getUsage(mAAudioStream));
}
if (mLibLoader->stream_getContentType != nullptr) {
mContentType = static_cast<ContentType>(mLibLoader->stream_getContentType(mAAudioStream));
}
if (mLibLoader->stream_getInputPreset != nullptr) {
mInputPreset = static_cast<InputPreset>(mLibLoader->stream_getInputPreset(mAAudioStream));
}
if (mLibLoader->stream_getSessionId != nullptr) {
mSessionId = static_cast<SessionId>(mLibLoader->stream_getSessionId(mAAudioStream));
} else {
mSessionId = SessionId::None;
}
if (mLibLoader->stream_getAllowedCapturePolicy != nullptr && mDirection == oboe::Direction::Output) {
mAllowedCapturePolicy = static_cast<AllowedCapturePolicy>(mLibLoader->stream_getAllowedCapturePolicy(mAAudioStream));
} else {
mAllowedCapturePolicy = AllowedCapturePolicy::Unspecified;
}
if (mLibLoader->stream_isPrivacySensitive != nullptr && mDirection == oboe::Direction::Input) {
bool isPrivacySensitive = mLibLoader->stream_isPrivacySensitive(mAAudioStream);
mPrivacySensitiveMode = isPrivacySensitive ? PrivacySensitiveMode::Enabled :
PrivacySensitiveMode::Disabled;
} else {
mPrivacySensitiveMode = PrivacySensitiveMode::Unspecified;
}
if (mLibLoader->stream_getChannelMask != nullptr) {
mChannelMask = static_cast<ChannelMask>(mLibLoader->stream_getChannelMask(mAAudioStream));
}
if (mLibLoader->stream_isContentSpatialized != nullptr) {
mIsContentSpatialized = mLibLoader->stream_isContentSpatialized(mAAudioStream);
}
if (mLibLoader->stream_getSpatializationBehavior != nullptr) {
mSpatializationBehavior = static_cast<SpatializationBehavior>(
mLibLoader->stream_getSpatializationBehavior(mAAudioStream));
}
if (mLibLoader->stream_getHardwareChannelCount != nullptr) {
mHardwareChannelCount = mLibLoader->stream_getHardwareChannelCount(mAAudioStream);
}
if (mLibLoader->stream_getHardwareSampleRate != nullptr) {
mHardwareSampleRate = mLibLoader->stream_getHardwareSampleRate(mAAudioStream);
}
if (mLibLoader->stream_getHardwareFormat != nullptr) {
mHardwareFormat = static_cast<AudioFormat>(mLibLoader->stream_getHardwareFormat(mAAudioStream));
}
LOGD("AudioStreamAAudio.open() format=%d, sampleRate=%d, capacity = %d",
static_cast<int>(mFormat), static_cast<int>(mSampleRate),
static_cast<int>(mBufferCapacityInFrames));
calculateDefaultDelayBeforeCloseMillis();
error2:
mLibLoader->builder_delete(aaudioBuilder);
if (static_cast<int>(result) > 0) {
LOGW("AudioStreamAAudio.open: AAudioStream_Open() returned positive error = %d",
static_cast<int>(result));
if (OboeGlobals::areWorkaroundsEnabled()) {
result = Result::ErrorInternal; }
} else {
LOGD("AudioStreamAAudio.open: AAudioStream_Open() returned %s = %d",
mLibLoader->convertResultToText(static_cast<aaudio_result_t>(result)),
static_cast<int>(result));
}
return result;
}
Result AudioStreamAAudio::release() {
if (getSdkVersion() < __ANDROID_API_R__) {
return Result::ErrorUnimplemented;
}
if (OboeGlobals::areWorkaroundsEnabled() && getSdkVersion() == __ANDROID_API_R__) {
LOGW("Skipping release() on Android R");
return Result::ErrorUnimplemented;
}
std::lock_guard<std::mutex> lock(mLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
if (OboeGlobals::areWorkaroundsEnabled()) {
requestStop_l(stream);
}
return static_cast<Result>(mLibLoader->stream_release(stream));
} else {
return Result::ErrorClosed;
}
}
Result AudioStreamAAudio::close() {
std::lock_guard<std::mutex> lock(mLock);
AudioStream::close();
AAudioStream *stream = nullptr;
{
std::unique_lock<std::shared_mutex> lock2(mAAudioStreamLock);
stream = mAAudioStream.exchange(nullptr);
}
if (stream != nullptr) {
if (OboeGlobals::areWorkaroundsEnabled()) {
requestStop_l(stream);
sleepBeforeClose();
}
return static_cast<Result>(mLibLoader->stream_close(stream));
} else {
return Result::ErrorClosed;
}
}
static void oboe_stop_thread_proc(AudioStream *oboeStream) {
if (oboeStream != nullptr) {
oboeStream->requestStop();
}
}
void AudioStreamAAudio::launchStopThread() {
if (mStopThreadAllowed.exchange(false)) {
std::thread t(oboe_stop_thread_proc, this);
t.detach();
}
}
DataCallbackResult AudioStreamAAudio::callOnAudioReady(AAudioStream * ,
void *audioData,
int32_t numFrames) {
DataCallbackResult result = fireDataCallback(audioData, numFrames);
if (result == DataCallbackResult::Continue) {
return result;
} else {
if (result == DataCallbackResult::Stop) {
LOGD("Oboe callback returned DataCallbackResult::Stop");
} else {
LOGE("Oboe callback returned unexpected value = %d", result);
}
if (OboeGlobals::areWorkaroundsEnabled() && getSdkVersion() <= __ANDROID_API_R__) {
launchStopThread();
return DataCallbackResult::Continue;
} else {
return DataCallbackResult::Stop; }
}
}
Result AudioStreamAAudio::requestStart() {
std::lock_guard<std::mutex> lock(mLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
if (getSdkVersion() <= __ANDROID_API_O_MR1__) {
StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream));
if (state == StreamState::Starting || state == StreamState::Started) {
return Result::OK;
}
}
if (isDataCallbackSpecified()) {
setDataCallbackEnabled(true);
}
mStopThreadAllowed = true;
closePerformanceHint();
return static_cast<Result>(mLibLoader->stream_requestStart(stream));
} else {
return Result::ErrorClosed;
}
}
Result AudioStreamAAudio::requestPause() {
std::lock_guard<std::mutex> lock(mLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
if (getSdkVersion() <= __ANDROID_API_O_MR1__) {
StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream));
if (state == StreamState::Pausing || state == StreamState::Paused) {
return Result::OK;
}
}
return static_cast<Result>(mLibLoader->stream_requestPause(stream));
} else {
return Result::ErrorClosed;
}
}
Result AudioStreamAAudio::requestFlush() {
std::lock_guard<std::mutex> lock(mLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
if (getSdkVersion() <= __ANDROID_API_O_MR1__) {
StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream));
if (state == StreamState::Flushing || state == StreamState::Flushed) {
return Result::OK;
}
}
return static_cast<Result>(mLibLoader->stream_requestFlush(stream));
} else {
return Result::ErrorClosed;
}
}
Result AudioStreamAAudio::requestStop() {
std::lock_guard<std::mutex> lock(mLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
return requestStop_l(stream);
} else {
return Result::ErrorClosed;
}
}
Result AudioStreamAAudio::requestStop_l(AAudioStream *stream) {
if (getSdkVersion() <= __ANDROID_API_O_MR1__) {
StreamState state = static_cast<StreamState>(mLibLoader->stream_getState(stream));
if (state == StreamState::Stopping || state == StreamState::Stopped) {
return Result::OK;
}
}
return static_cast<Result>(mLibLoader->stream_requestStop(stream));
}
ResultWithValue<int32_t> AudioStreamAAudio::write(const void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds) {
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
int32_t result = mLibLoader->stream_write(mAAudioStream, buffer,
numFrames, timeoutNanoseconds);
return ResultWithValue<int32_t>::createBasedOnSign(result);
} else {
return ResultWithValue<int32_t>(Result::ErrorClosed);
}
}
ResultWithValue<int32_t> AudioStreamAAudio::read(void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds) {
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
int32_t result = mLibLoader->stream_read(mAAudioStream, buffer,
numFrames, timeoutNanoseconds);
return ResultWithValue<int32_t>::createBasedOnSign(result);
} else {
return ResultWithValue<int32_t>(Result::ErrorClosed);
}
}
Result AudioStreamAAudio::waitForStateChange(StreamState currentState,
StreamState *nextState,
int64_t timeoutNanoseconds) {
Result oboeResult = Result::ErrorTimeout;
int64_t sleepTimeNanos = 20 * kNanosPerMillisecond; aaudio_stream_state_t currentAAudioState = static_cast<aaudio_stream_state_t>(currentState);
aaudio_result_t result = AAUDIO_OK;
int64_t timeLeftNanos = timeoutNanoseconds;
mLock.lock();
while (true) {
AAudioStream *stream = mAAudioStream.load();
if (stream == nullptr) {
if (nextState != nullptr) {
*nextState = StreamState::Closed;
}
oboeResult = Result::ErrorClosed;
break;
}
aaudio_stream_state_t aaudioNextState;
result = mLibLoader->stream_waitForStateChange(
mAAudioStream,
currentAAudioState,
&aaudioNextState,
0); if (result != AAUDIO_OK && result != AAUDIO_ERROR_TIMEOUT) {
oboeResult = static_cast<Result>(result);
break;
}
#if OBOE_FIX_FORCE_STARTING_TO_STARTED
if (OboeGlobals::areWorkaroundsEnabled()
&& aaudioNextState == static_cast<aaudio_stream_state_t >(StreamState::Starting)) {
aaudioNextState = static_cast<aaudio_stream_state_t >(StreamState::Started);
}
#endif if (nextState != nullptr) {
*nextState = static_cast<StreamState>(aaudioNextState);
}
if (currentAAudioState != aaudioNextState) { oboeResult = Result::OK;
break;
}
if (timeLeftNanos <= 0) {
break;
}
mLock.unlock(); if (sleepTimeNanos > timeLeftNanos) {
sleepTimeNanos = timeLeftNanos; }
AudioClock::sleepForNanos(sleepTimeNanos);
timeLeftNanos -= sleepTimeNanos;
mLock.lock();
}
mLock.unlock();
return oboeResult;
}
ResultWithValue<int32_t> AudioStreamAAudio::setBufferSizeInFrames(int32_t requestedFrames) {
int32_t adjustedFrames = requestedFrames;
if (adjustedFrames > mBufferCapacityInFrames) {
adjustedFrames = mBufferCapacityInFrames;
}
adjustedFrames = QuirksManager::getInstance().clipBufferSize(*this, adjustedFrames);
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
int32_t newBufferSize = mLibLoader->stream_setBufferSize(mAAudioStream, adjustedFrames);
if (newBufferSize > 0) mBufferSizeInFrames = newBufferSize;
return ResultWithValue<int32_t>::createBasedOnSign(newBufferSize);
} else {
return ResultWithValue<int32_t>(Result::ErrorClosed);
}
}
StreamState AudioStreamAAudio::getState() {
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
aaudio_stream_state_t aaudioState = mLibLoader->stream_getState(stream);
#if OBOE_FIX_FORCE_STARTING_TO_STARTED
if (OboeGlobals::areWorkaroundsEnabled()
&& aaudioState == AAUDIO_STREAM_STATE_STARTING) {
aaudioState = AAUDIO_STREAM_STATE_STARTED;
}
#endif return static_cast<StreamState>(aaudioState);
} else {
return StreamState::Closed;
}
}
int32_t AudioStreamAAudio::getBufferSizeInFrames() {
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
mBufferSizeInFrames = mLibLoader->stream_getBufferSize(stream);
}
return mBufferSizeInFrames;
}
void AudioStreamAAudio::updateFramesRead() {
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
AAudioStream *stream = mAAudioStream.load();
#define DEBUG_CLOSE_RACE 0
#if DEBUG_CLOSE_RACE
AudioClock::sleepForNanos(400 * kNanosPerMillisecond);
#endif if (stream != nullptr) {
mFramesRead = mLibLoader->stream_getFramesRead(stream);
}
}
void AudioStreamAAudio::updateFramesWritten() {
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
mFramesWritten = mLibLoader->stream_getFramesWritten(stream);
}
}
ResultWithValue<int32_t> AudioStreamAAudio::getXRunCount() {
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
return ResultWithValue<int32_t>::createBasedOnSign(mLibLoader->stream_getXRunCount(stream));
} else {
return ResultWithValue<int32_t>(Result::ErrorNull);
}
}
Result AudioStreamAAudio::getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) {
if (getState() != StreamState::Started) {
return Result::ErrorInvalidState;
}
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
return static_cast<Result>(mLibLoader->stream_getTimestamp(stream, clockId,
framePosition, timeNanoseconds));
} else {
return Result::ErrorNull;
}
}
ResultWithValue<double> AudioStreamAAudio::calculateLatencyMillis() {
int64_t hardwareFrameIndex;
int64_t hardwareFrameHardwareTime;
auto result = getTimestamp(CLOCK_MONOTONIC,
&hardwareFrameIndex,
&hardwareFrameHardwareTime);
if (result != oboe::Result::OK) {
return ResultWithValue<double>(static_cast<Result>(result));
}
bool isOutput = (getDirection() == oboe::Direction::Output);
int64_t appFrameIndex = isOutput ? getFramesWritten() : getFramesRead();
using namespace std::chrono;
int64_t appFrameAppTime =
duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count();
int64_t frameIndexDelta = appFrameIndex - hardwareFrameIndex;
int64_t frameTimeDelta = (frameIndexDelta * oboe::kNanosPerSecond) / getSampleRate();
int64_t appFrameHardwareTime = hardwareFrameHardwareTime + frameTimeDelta;
double latencyNanos = static_cast<double>(isOutput
? (appFrameHardwareTime - appFrameAppTime) : (appFrameAppTime - appFrameHardwareTime)); double latencyMillis = latencyNanos / kNanosPerMillisecond;
return ResultWithValue<double>(latencyMillis);
}
bool AudioStreamAAudio::isMMapUsed() {
std::shared_lock<std::shared_mutex> lock(mAAudioStreamLock);
AAudioStream *stream = mAAudioStream.load();
if (stream != nullptr) {
return AAudioExtensions::getInstance().isMMapUsed(stream);
} else {
return false;
}
}
}