aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Wu <85952307+robertwu1@users.noreply.github.com>2023-03-24 16:53:58 +0000
committerGitHub <noreply@github.com>2023-03-24 09:53:58 -0700
commite54826cfdb2c9480ca0b7bb38f4558f1e99ad152 (patch)
treeca15a3e8bc6fb1ecbc9f147486cbb61a4a964b69
parent2b70db2971751553550ac062c33fb3c1c3c81817 (diff)
downloadoboe-e54826cfdb2c9480ca0b7bb38f4558f1e99ad152.tar.gz
Add support for AAudioStream_release (#1755)
-rw-r--r--apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp10
-rw-r--r--apps/OboeTester/app/src/main/cpp/NativeAudioContext.h2
-rw-r--r--apps/OboeTester/app/src/main/cpp/jni-bridge.cpp5
-rw-r--r--apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java24
-rw-r--r--apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivity.java6
-rw-r--r--apps/OboeTester/app/src/main/res/layout/merge_audio_common.xml10
-rw-r--r--apps/OboeTester/app/src/main/res/values/strings.xml1
-rw-r--r--include/oboe/AudioStream.h23
-rw-r--r--src/aaudio/AAudioLoader.cpp4
-rw-r--r--src/aaudio/AAudioLoader.h5
-rw-r--r--src/aaudio/AudioStreamAAudio.cpp25
-rw-r--r--src/aaudio/AudioStreamAAudio.h1
-rw-r--r--tests/testStreamClosedMethods.cpp41
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());
+}