Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API to set sleep time before close. #1593

Merged
merged 2 commits into from
Aug 6, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions include/oboe/AudioStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,29 @@ class AudioStream : public AudioStreamBase {
return mErrorCallbackResult;
}


int32_t getDelayBeforeCloseMillis() const {
return mDelayBeforeCloseMillis;
}

/**
* Set the time to sleep before closing the internal stream.
*
* Sometimes a callback can occur shortly after a stream has been stopped and
* even after a close! If the stream has been closed then the callback
* might access memory that has been freed, which could cause a crash.
* This seems to be more likely in Android P or earlier.
* But it can also occur in later versions. By sleeping, we give time for
* the callback threads to finish.
*
* Note that this only has an effect when OboeGlobals::areWorkaroundsEnabled() is true.
*
* @param delayBeforeCloseMillis time to sleep before close.
*/
void setDelayBeforeCloseMillis(int32_t delayBeforeCloseMillis) {
mDelayBeforeCloseMillis = delayBeforeCloseMillis;
}

protected:

/**
Expand Down Expand Up @@ -510,6 +533,17 @@ class AudioStream : public AudioStreamBase {
mDataCallbackEnabled = enabled;
}

void calculateDefaultDelayBeforeCloseMillis();

/**
* Try to avoid a race condition when closing.
*/
void sleepBeforeClose() {
if (mDelayBeforeCloseMillis > 0) {
usleep(mDelayBeforeCloseMillis * 1000);
}
}

/*
* Set a weak_ptr to this stream from the shared_ptr so that we can
* later use a shared_ptr in the error callback.
Expand Down Expand Up @@ -553,6 +587,11 @@ class AudioStream : public AudioStreamBase {
*/
int32_t mFramesPerBurst = kUnspecified;

// Time to sleep in order to prevent a race condition with a callback after a close().
// Two milliseconds may be enough but 10 msec is even safer.
static constexpr int kMinDelayBeforeCloseMillis = 10;
int32_t mDelayBeforeCloseMillis = kMinDelayBeforeCloseMillis;

private:

// Log the scheduler if it changes.
Expand Down
9 changes: 3 additions & 6 deletions src/aaudio/AudioStreamAAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ Result AudioStreamAAudio::open() {
static_cast<int>(mFormat), static_cast<int>(mSampleRate),
static_cast<int>(mBufferCapacityInFrames));

calculateDefaultDelayBeforeCloseMillis();

error2:
mLibLoader->builder_delete(aaudioBuilder);
LOGD("AudioStreamAAudio.open: AAudioStream_Open() returned %s",
Expand Down Expand Up @@ -355,12 +357,7 @@ Result AudioStreamAAudio::close() {
// Make sure we are really stopped. Do it under mLock
// so another thread cannot call requestStart() right before the close.
requestStop_l(stream);
robertwu1 marked this conversation as resolved.
Show resolved Hide resolved
// Sometimes a callback can occur shortly after a stream has been stopped and
// even after a close! If the stream has been closed then the callback
// can access memory that has been freed. That causes a crash.
// This seems to be more likely in Android P or earlier.
// But it can also occur in later versions.
usleep(kDelayBeforeCloseMillis * 1000);
sleepBeforeClose();
}
return static_cast<Result>(mLibLoader->stream_close(stream));
} else {
Expand Down
9 changes: 6 additions & 3 deletions src/aaudio/AudioStreamAAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,12 @@ class AudioStreamAAudio : public AudioStream {
*/
void launchStopThread();

// Time to sleep in order to prevent a race condition with a callback after a close().
// Two milliseconds may be enough but 10 msec is even safer.
static constexpr int kDelayBeforeCloseMillis = 10;
public:
int32_t getMDelayBeforeCloseMillis() const;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make this uint32_t?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Google C++ style guide says:

You should not use the unsigned integer types such as uint32_t, unless there is a valid reason
such as representing a bit pattern rather than a number, or you need defined overflow
modulo 2^N. In particular, do not use unsigned types to say a number will never be negative.
Instead, use assertions for this.


void setDelayBeforeCloseMillis(int32_t mDelayBeforeCloseMillis);

private:

std::atomic<bool> mCallbackThreadEnabled;
std::atomic<bool> mStopThreadAllowed{false};
Expand Down
9 changes: 9 additions & 0 deletions src/common/AudioStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,13 @@ ResultWithValue<FrameTimestamp> AudioStream::getTimestamp(clockid_t clockId) {
}
}

void AudioStream::calculateDefaultDelayBeforeCloseMillis() {
robertwu1 marked this conversation as resolved.
Show resolved Hide resolved
// Calculate delay time before close based on burst duration.
// Start with a burst duration then add 1 msec as a safety margin.
mDelayBeforeCloseMillis = std::max(kMinDelayBeforeCloseMillis,
1 + ((mFramesPerBurst * 1000) / getSampleRate()));
LOGD("calculateDefaultDelayBeforeCloseMillis() default = %d",
static_cast<int>(mDelayBeforeCloseMillis));
}

} // namespace oboe
14 changes: 1 addition & 13 deletions src/opensles/AudioInputStreamOpenSLES.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,23 +195,11 @@ Result AudioInputStreamOpenSLES::open() {
goto error;
}

result = AudioStreamOpenSLES::registerBufferQueueCallback();
result = finishCommonOpen(configItf);
if (SL_RESULT_SUCCESS != result) {
goto error;
}

result = updateStreamParameters(configItf);
if (SL_RESULT_SUCCESS != result) {
goto error;
}

oboeResult = configureBufferSizes(mSampleRate);
if (Result::OK != oboeResult) {
goto error;
}

allocateFifo();

setState(StreamState::Open);
return Result::OK;

Expand Down
17 changes: 4 additions & 13 deletions src/opensles/AudioOutputStreamOpenSLES.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,23 +214,11 @@ Result AudioOutputStreamOpenSLES::open() {
goto error;
}

result = AudioStreamOpenSLES::registerBufferQueueCallback();
result = finishCommonOpen(configItf);
if (SL_RESULT_SUCCESS != result) {
goto error;
}

result = updateStreamParameters(configItf);
if (SL_RESULT_SUCCESS != result) {
goto error;
}

oboeResult = configureBufferSizes(mSampleRate);
if (Result::OK != oboeResult) {
goto error;
}

allocateFifo();

setState(StreamState::Open);
return Result::OK;

Expand All @@ -252,6 +240,9 @@ Result AudioOutputStreamOpenSLES::close() {
result = Result::ErrorClosed;
} else {
(void) requestPause_l();
if (OboeGlobals::areWorkaroundsEnabled()) {
sleepBeforeClose();
}
// invalidate any interfaces
mPlayInterface = nullptr;
result = AudioStreamOpenSLES::close_l();
Expand Down
24 changes: 24 additions & 0 deletions src/opensles/AudioStreamOpenSLES.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,30 @@ Result AudioStreamOpenSLES::open() {
return Result::OK;
}


SLresult AudioStreamOpenSLES::finishCommonOpen(SLAndroidConfigurationItf configItf) {
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;
}

Result AudioStreamOpenSLES::configureBufferSizes(int32_t sampleRate) {
LOGD("AudioStreamOpenSLES:%s(%d) initial mFramesPerBurst = %d, mFramesPerCallback = %d",
__func__, sampleRate, mFramesPerBurst, mFramesPerCallback);
Expand Down
18 changes: 12 additions & 6 deletions src/opensles/AudioStreamOpenSLES.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ class AudioStreamOpenSLES : public AudioStreamBuffered {

protected:

/**
* Finish setting up the stream. Common for INPUT and OUTPUT.
*
* @param configItf
* @return SL_RESULT_SUCCESS if OK.
*/
SLresult finishCommonOpen(SLAndroidConfigurationItf configItf);

// This must be called under mLock.
Result close_l();

Expand All @@ -88,21 +96,15 @@ class AudioStreamOpenSLES : public AudioStreamBuffered {

static SLuint32 getDefaultByteOrder();

SLresult registerBufferQueueCallback();

int32_t getBufferDepth(SLAndroidSimpleBufferQueueItf bq);

SLresult enqueueCallbackBuffer(SLAndroidSimpleBufferQueueItf bq);

SLresult configurePerformanceMode(SLAndroidConfigurationItf configItf);

SLresult updateStreamParameters(SLAndroidConfigurationItf configItf);

PerformanceMode convertPerformanceMode(SLuint32 openslMode) const;
SLuint32 convertPerformanceMode(PerformanceMode oboeMode) const;

Result configureBufferSizes(int32_t sampleRate);

void logUnsupportedAttributes();

/**
Expand All @@ -123,6 +125,10 @@ class AudioStreamOpenSLES : public AudioStreamBuffered {
MonotonicCounter mPositionMillis; // for tracking OpenSL ES service position

private:
SLresult registerBufferQueueCallback();
SLresult updateStreamParameters(SLAndroidConfigurationItf configItf);
Result configureBufferSizes(int32_t sampleRate);

std::unique_ptr<uint8_t[]> mCallbackBuffer[kBufferQueueLength];
int mCallbackBufferIndex = 0;
std::atomic<StreamState> mState{StreamState::Uninitialized};
Expand Down