diff options
author | Robert Wu <85952307+robertwu1@users.noreply.github.com> | 2023-03-24 16:53:58 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-24 09:53:58 -0700 |
commit | e54826cfdb2c9480ca0b7bb38f4558f1e99ad152 (patch) | |
tree | ca15a3e8bc6fb1ecbc9f147486cbb61a4a964b69 | |
parent | 2b70db2971751553550ac062c33fb3c1c3c81817 (diff) | |
download | oboe-e54826cfdb2c9480ca0b7bb38f4558f1e99ad152.tar.gz |
Add support for AAudioStream_release (#1755)
-rw-r--r-- | apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp | 10 | ||||
-rw-r--r-- | apps/OboeTester/app/src/main/cpp/NativeAudioContext.h | 2 | ||||
-rw-r--r-- | apps/OboeTester/app/src/main/cpp/jni-bridge.cpp | 5 | ||||
-rw-r--r-- | apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java | 24 | ||||
-rw-r--r-- | apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivity.java | 6 | ||||
-rw-r--r-- | apps/OboeTester/app/src/main/res/layout/merge_audio_common.xml | 10 | ||||
-rw-r--r-- | apps/OboeTester/app/src/main/res/values/strings.xml | 1 | ||||
-rw-r--r-- | include/oboe/AudioStream.h | 23 | ||||
-rw-r--r-- | src/aaudio/AAudioLoader.cpp | 4 | ||||
-rw-r--r-- | src/aaudio/AAudioLoader.h | 5 | ||||
-rw-r--r-- | src/aaudio/AudioStreamAAudio.cpp | 25 | ||||
-rw-r--r-- | src/aaudio/AudioStreamAAudio.h | 1 | ||||
-rw-r--r-- | tests/testStreamClosedMethods.cpp | 41 |
13 files changed, 155 insertions, 2 deletions
diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp index 08e10c15..6a08e967 100644 --- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp +++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp @@ -98,6 +98,16 @@ int32_t ActivityContext::allocateStreamIndex() { return mNextStreamHandle++; } +oboe::Result ActivityContext::release() { + oboe::Result result = oboe::Result::OK; + stopBlockingIOThread(); + for (auto entry : mOboeStreams) { + std::shared_ptr<oboe::AudioStream> oboeStream = entry.second; + result = oboeStream->release(); + } + return result; +} + void ActivityContext::close(int32_t streamIndex) { stopBlockingIOThread(); std::shared_ptr<oboe::AudioStream> oboeStream = getStream(streamIndex); diff --git a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h index 9ffd36e4..83dd62c6 100644 --- a/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h +++ b/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h @@ -131,6 +131,8 @@ public: jboolean isMMap, jboolean isInput); + oboe::Result release(); + virtual void close(int32_t streamIndex); virtual void configureForStart() {} diff --git a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp index 092d0917..e3fd803d 100644 --- a/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp +++ b/apps/OboeTester/app/src/main/cpp/jni-bridge.cpp @@ -169,6 +169,11 @@ Java_com_mobileer_oboetester_TestAudioActivity_stopNative(JNIEnv *env, jobject) } JNIEXPORT jint JNICALL +Java_com_mobileer_oboetester_TestAudioActivity_releaseNative(JNIEnv *env, jobject) { + return (jint) engine.getCurrentActivity()->release(); +} + +JNIEXPORT jint JNICALL Java_com_mobileer_oboetester_TestAudioActivity_getFramesPerCallback(JNIEnv *env, jobject) { return (jint) engine.getCurrentActivity()->getFramesPerCallback(); } diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java index 037cef20..9350cd88 100644 --- a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java +++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java @@ -59,8 +59,9 @@ abstract class TestAudioActivity extends Activity { public static final int AUDIO_STATE_STARTED = 1; public static final int AUDIO_STATE_PAUSED = 2; public static final int AUDIO_STATE_STOPPED = 3; - public static final int AUDIO_STATE_CLOSING = 4; - public static final int AUDIO_STATE_CLOSED = 5; + public static final int AUDIO_STATE_RELEASED = 4; + public static final int AUDIO_STATE_CLOSING = 5; + public static final int AUDIO_STATE_CLOSED = 6; public static final int COLOR_ACTIVE = 0xFFD0D0A0; public static final int COLOR_IDLE = 0xFFD0D0D0; @@ -86,6 +87,7 @@ abstract class TestAudioActivity extends Activity { private Button mStartButton; private Button mPauseButton; private Button mStopButton; + private Button mReleaseButton; private Button mCloseButton; private MyStreamSniffer mStreamSniffer; private CheckBox mCallbackReturnStopBox; @@ -309,6 +311,7 @@ abstract class TestAudioActivity extends Activity { mStartButton.setBackgroundColor(mAudioState == AUDIO_STATE_STARTED ? COLOR_ACTIVE : COLOR_IDLE); mPauseButton.setBackgroundColor(mAudioState == AUDIO_STATE_PAUSED ? COLOR_ACTIVE : COLOR_IDLE); mStopButton.setBackgroundColor(mAudioState == AUDIO_STATE_STOPPED ? COLOR_ACTIVE : COLOR_IDLE); + mReleaseButton.setBackgroundColor(mAudioState == AUDIO_STATE_RELEASED ? COLOR_ACTIVE : COLOR_IDLE); mCloseButton.setBackgroundColor(mAudioState == AUDIO_STATE_CLOSED ? COLOR_ACTIVE : COLOR_IDLE); } setConfigViewsEnabled(mAudioState == AUDIO_STATE_CLOSED); @@ -409,6 +412,7 @@ abstract class TestAudioActivity extends Activity { mStartButton = (Button) findViewById(R.id.button_start); mPauseButton = (Button) findViewById(R.id.button_pause); mStopButton = (Button) findViewById(R.id.button_stop); + mReleaseButton = (Button) findViewById(R.id.button_release); mCloseButton = (Button) findViewById(R.id.button_close); } mStreamContexts = new ArrayList<StreamContext>(); @@ -495,6 +499,10 @@ abstract class TestAudioActivity extends Activity { closeAudio(); } + public void releaseAudio(View view) { + releaseAudio(); + } + public int getSampleRate() { return mSampleRate; } @@ -579,6 +587,8 @@ abstract class TestAudioActivity extends Activity { private native int stopNative(); + private native int releaseNative(); + protected native void setActivityType(int activityType); private native int getFramesPerCallback(); @@ -627,6 +637,16 @@ abstract class TestAudioActivity extends Activity { } } + public void releaseAudio() { + int result = releaseNative(); + if (result != 0) { + showErrorToast("release failed with " + result); + } else { + mAudioState = AUDIO_STATE_RELEASED; + updateEnabledWidgets(); + } + } + public void runTest() { } diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivity.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivity.java index 0d5d0ce3..80812ccf 100644 --- a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivity.java +++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivity.java @@ -128,6 +128,12 @@ public final class TestOutputActivity extends TestOutputActivityBase { super.pauseAudio(); } + public void releaseAudio() { + configureChannelBoxes(0); + mOutputSignalSpinner.setEnabled(true); + super.releaseAudio(); + } + public void closeAudio() { configureChannelBoxes(0); mOutputSignalSpinner.setEnabled(true); diff --git a/apps/OboeTester/app/src/main/res/layout/merge_audio_common.xml b/apps/OboeTester/app/src/main/res/layout/merge_audio_common.xml index cee7bed5..df6079fd 100644 --- a/apps/OboeTester/app/src/main/res/layout/merge_audio_common.xml +++ b/apps/OboeTester/app/src/main/res/layout/merge_audio_common.xml @@ -69,6 +69,16 @@ android:text="@string/stopAudio" /> <Button + android:id="@+id/button_release" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:backgroundTint="@xml/button_color_selector" + android:backgroundTintMode="src_atop" + android:onClick="releaseAudio" + android:text="@string/releaseAudio" /> + + <Button android:id="@+id/button_close" android:layout_width="0dp" android:layout_weight="1" diff --git a/apps/OboeTester/app/src/main/res/values/strings.xml b/apps/OboeTester/app/src/main/res/values/strings.xml index eaa1e935..381388b6 100644 --- a/apps/OboeTester/app/src/main/res/values/strings.xml +++ b/apps/OboeTester/app/src/main/res/values/strings.xml @@ -9,6 +9,7 @@ <string name="startAudio">Start</string> <string name="pauseAudio">Pause</string> <string name="stopAudio">Stop</string> + <string name="releaseAudio">Release</string> <string name="closeAudio">Close</string> <string name="recordAudio">Record</string> <string name="playAudio">Play</string> diff --git a/include/oboe/AudioStream.h b/include/oboe/AudioStream.h index 8f0d29b9..6730a149 100644 --- a/include/oboe/AudioStream.h +++ b/include/oboe/AudioStream.h @@ -69,6 +69,29 @@ public: } /** + * Free the audio resources associated with a stream created by AAudioStreamBuilder_openStream(). + * + * AAudioStream_close() should be called at some point after calling this function. + * + * After this call, the stream will be in AAUDIO_STREAM_STATE_CLOSING + * + * This function is useful if you want to release the audio resources immediately, but still allow + * queries to the stream to occur from other threads. This often happens if you are monitoring + * stream progress from a UI thread. + * + * NOTE: This function is only fully implemented for MMAP streams, which are low latency streams + * supported by some devices. On other "Legacy" streams some audio resources will still be in use + * and some callbacks may still be in process after this call. + * + * Available in AAudio since API level 30. Returns Result::ErrorUnimplemented otherwise. + * + * * @return either Result::OK or an error. + */ + virtual Result release() { + return Result::ErrorUnimplemented; + } + + /** * Close the stream and deallocate any resources from the open() call. */ virtual Result close(); diff --git a/src/aaudio/AAudioLoader.cpp b/src/aaudio/AAudioLoader.cpp index 550ad6ee..e873e5dd 100644 --- a/src/aaudio/AAudioLoader.cpp +++ b/src/aaudio/AAudioLoader.cpp @@ -112,6 +112,10 @@ int AAudioLoader::open() { stream_getChannelCount = load_I_PS("AAudioStream_getSamplesPerFrame"); } + if (getSdkVersion() >= __ANDROID_API_R__) { + stream_release = load_I_PS("AAudioStream_release"); + } + stream_close = load_I_PS("AAudioStream_close"); stream_getBufferSize = load_I_PS("AAudioStream_getBufferSizeInFrames"); diff --git a/src/aaudio/AAudioLoader.h b/src/aaudio/AAudioLoader.h index 3ad33f6c..cff0bea9 100644 --- a/src/aaudio/AAudioLoader.h +++ b/src/aaudio/AAudioLoader.h @@ -71,6 +71,10 @@ typedef int32_t aaudio_session_id_t; typedef uint32_t aaudio_channel_mask_t; #endif +#ifndef __ANDROID_API_R__ +#define __ANDROID_API_R__ 30 +#endif + #ifndef __ANDROID_API_S__ #define __ANDROID_API_S__ 31 #endif @@ -202,6 +206,7 @@ class AAudioLoader { signature_I_PSKPLPL stream_getTimestamp = nullptr; + signature_I_PS stream_release = nullptr; signature_I_PS stream_close = nullptr; signature_I_PS stream_getChannelCount = nullptr; diff --git a/src/aaudio/AudioStreamAAudio.cpp b/src/aaudio/AudioStreamAAudio.cpp index b1b4c6d3..46c76668 100644 --- a/src/aaudio/AudioStreamAAudio.cpp +++ b/src/aaudio/AudioStreamAAudio.cpp @@ -357,6 +357,31 @@ error2: return result; } +Result AudioStreamAAudio::release() { + if (getSdkVersion() < __ANDROID_API_R__) { + return Result::ErrorUnimplemented; + } + + // AAudioStream_release() is buggy on Android R. + 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()) { + // Make sure we are really stopped. Do it under mLock + // so another thread cannot call requestStart() right before the close. + requestStop_l(stream); + } + return static_cast<Result>(mLibLoader->stream_release(stream)); + } else { + return Result::ErrorClosed; + } +} + Result AudioStreamAAudio::close() { // Prevent two threads from closing the stream at the same time and crashing. // This could occur, for example, if an application called close() at the same diff --git a/src/aaudio/AudioStreamAAudio.h b/src/aaudio/AudioStreamAAudio.h index 9846423f..31b75bf7 100644 --- a/src/aaudio/AudioStreamAAudio.h +++ b/src/aaudio/AudioStreamAAudio.h @@ -51,6 +51,7 @@ public: // These functions override methods in AudioStream. // See AudioStream for documentation. Result open() override; + Result release() override; Result close() override; Result requestStart() override; diff --git a/tests/testStreamClosedMethods.cpp b/tests/testStreamClosedMethods.cpp index a75e8aea..b8ab3231 100644 --- a/tests/testStreamClosedMethods.cpp +++ b/tests/testStreamClosedMethods.cpp @@ -36,6 +36,17 @@ protected: return (r == Result::OK); } + bool releaseStream() { + Result r = mStream->release(); + if (getSdkVersion() > __ANDROID_API_R__ && mBuilder.getAudioApi() != AudioApi::OpenSLES) { + EXPECT_EQ(r, Result::OK) << "Failed to release stream. " << convertToText(r); + return (r == Result::OK); + } else { + EXPECT_EQ(r, Result::ErrorUnimplemented) << "Did not get ErrorUnimplemented" << convertToText(r); + return (r == Result::ErrorUnimplemented); + } + } + bool closeStream() { Result r = mStream->close(); EXPECT_EQ(r, Result::OK) << "Failed to close stream. " << convertToText(r); @@ -399,3 +410,33 @@ TEST_F(StreamClosedReturnValues, DelayBeforeCloseOutputOpenSL){ mBuilder.setDirection(Direction::Output); testDelayBeforeClose(); } + +TEST_F(StreamClosedReturnValues, TestReleaseInput){ + mBuilder.setDirection(Direction::Input); + ASSERT_TRUE(openStream()); + ASSERT_TRUE(releaseStream()); + ASSERT_TRUE(closeStream()); +} + +TEST_F(StreamClosedReturnValues, TestReleaseInputOpenSLES){ + mBuilder.setAudioApi(AudioApi::OpenSLES); + mBuilder.setDirection(Direction::Input); + ASSERT_TRUE(openStream()); + ASSERT_TRUE(releaseStream()); + ASSERT_TRUE(closeStream()); +} + +TEST_F(StreamClosedReturnValues, TestReleaseOutput){ + mBuilder.setDirection(Direction::Output); + ASSERT_TRUE(openStream()); + ASSERT_TRUE(releaseStream()); + ASSERT_TRUE(closeStream()); +} + +TEST_F(StreamClosedReturnValues, TestReleaseOutputOpenSLES){ + mBuilder.setAudioApi(AudioApi::OpenSLES); + mBuilder.setDirection(Direction::Output); + ASSERT_TRUE(openStream()); + ASSERT_TRUE(releaseStream()); + ASSERT_TRUE(closeStream()); +} |