Skip to content

Commit 3384809

Browse files
authored
Support flushFromFrame API. (#2271)
* Support flushFromFrame API. Fixes #2268.
1 parent 87c8e78 commit 3384809

14 files changed

Lines changed: 225 additions & 0 deletions

File tree

apps/OboeTester/app/src/main/cpp/NativeAudioContext.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,25 @@ void ActivityTestOutput::setupMemoryBuffer(std::unique_ptr<uint8_t[]> &buffer, i
587587
}
588588
}
589589

590+
int64_t ActivityTestOutput::flushFromFrame(int32_t accuracy, int64_t frame) {
591+
std::shared_ptr<oboe::AudioStream> oboeStream = getOutputStream();
592+
if (oboeStream == nullptr) {
593+
return static_cast<int64_t>(oboe::Result::ErrorInvalidState);
594+
}
595+
const int64_t requestedFrames = frame;
596+
if (auto result = oboeStream->flushFromFrame(
597+
static_cast<oboe::FlushFromAccuracy>(accuracy), frame);
598+
result.error() != oboe::Result::OK) {
599+
LOGE("Failed to flushFromFrame(%d, %jd), error=%d, suggestedFrame=%jd",
600+
accuracy, requestedFrames, result.error(), frame);
601+
return static_cast<int64_t>(result.error());
602+
} else {
603+
LOGD("Successfully flushFromFrame(%d, %jd), actual flushed frame: %jd",
604+
accuracy, requestedFrames, result.value());
605+
return result.value();
606+
}
607+
}
608+
590609
// ======================================================================= ActivityTestInput
591610
void ActivityTestInput::configureAfterOpen() {
592611
mInputAnalyzer.reset();

apps/OboeTester/app/src/main/cpp/NativeAudioContext.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ class ActivityContext {
154154

155155
oboe::Result flush();
156156

157+
virtual int64_t flushFromFrame(int32_t accuracy, int64_t frame) {
158+
return static_cast<int64_t>(oboe::Result::ErrorUnimplemented);
159+
}
160+
157161
oboe::Result stopAllStreams();
158162

159163
virtual oboe::Result stop() {
@@ -472,6 +476,8 @@ class ActivityTestOutput : public ActivityContext {
472476

473477
void setupMemoryBuffer(std::unique_ptr<uint8_t[]>& buffer, int length) final;
474478

479+
int64_t flushFromFrame(int32_t accuracy, int64_t frame) final;
480+
475481
protected:
476482
SignalType mSignalType = SignalType::Sine;
477483

apps/OboeTester/app/src/main/cpp/jni-bridge.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@ Java_com_mobileer_oboetester_TestAudioActivity_flushNative(JNIEnv *env, jobject)
222222
return (jint) engine.getCurrentActivity()->flush();
223223
}
224224

225+
JNIEXPORT jlong JNICALL
226+
Java_com_mobileer_oboetester_TestAudioActivity_flushFromFrameNative(
227+
JNIEnv * /*env*/, jobject, jint accuracy, jlong frames) {
228+
return (jlong) engine.getCurrentActivity()->flushFromFrame(
229+
accuracy, static_cast<int64_t>(frames));
230+
}
231+
225232
JNIEXPORT jint JNICALL
226233
Java_com_mobileer_oboetester_TestAudioActivity_stopNative(JNIEnv *env, jobject) {
227234
return (jint) engine.getCurrentActivity()->stop();

apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestAudioActivity.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,8 @@ private void openStreamContext(StreamContext streamContext) throws IOException {
777777

778778
private native int releaseNative();
779779

780+
protected native long flushFromFrameNative(int accuracy, long frames);
781+
780782
protected native void setActivityType(int activityType);
781783

782784
private native int getFramesPerCallback();

apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivity.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@
1919
import android.os.Bundle;
2020
import android.os.Handler;
2121
import android.os.Looper;
22+
import android.util.Log;
2223
import android.view.View;
2324
import android.widget.AdapterView;
25+
import android.widget.Button;
2426
import android.widget.CheckBox;
27+
import android.widget.EditText;
28+
import android.widget.LinearLayout;
2529
import android.widget.SeekBar;
2630
import android.widget.Spinner;
2731
import android.widget.TextView;
32+
import android.widget.Toast;
2833

2934
import java.io.IOException;
3035
import java.util.Locale;
@@ -42,6 +47,11 @@ public final class TestOutputActivity extends TestOutputActivityBase {
4247
private CheckBox mShouldSetStreamControlByAttributes;
4348
private boolean mShouldDisableForCompressedFormat = false;
4449

50+
private LinearLayout mFlushFromFrameLayout;
51+
private EditText mFlushFromFrameEditText;
52+
private Spinner mFlushFromAccuracySpinner;
53+
private Button mFlushFromFrameButton;
54+
4555
private class OutputSignalSpinnerListener implements android.widget.AdapterView.OnItemSelectedListener {
4656
@Override
4757
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
@@ -113,6 +123,17 @@ protected void onCreate(Bundle savedInstanceState) {
113123
mVolumeSeekBar.setOnSeekBarChangeListener(mVolumeChangeListener);
114124

115125
mShouldSetStreamControlByAttributes = (CheckBox) findViewById(R.id.enableSetStreamControlByAttributes);
126+
127+
mFlushFromFrameLayout = (LinearLayout) findViewById(R.id.flushFromFrameLayout);
128+
mFlushFromFrameEditText = (EditText) findViewById(R.id.flushFromFrameEditText);
129+
mFlushFromAccuracySpinner = (Spinner) findViewById(R.id.flushFromAccuracySpinner);
130+
mFlushFromFrameButton = (Button) findViewById(R.id.flushFromFrameButton);
131+
mFlushFromFrameButton.setOnClickListener(new View.OnClickListener() {
132+
@Override
133+
public void onClick(View v) {
134+
flushFromFrame();
135+
}
136+
});
116137
}
117138

118139
@Override
@@ -180,6 +201,27 @@ public void startAudio() throws IOException {
180201
mOutputSignalSpinner.setEnabled(false);
181202
}
182203

204+
public void flushFromFrame() {
205+
try {
206+
int accuracy = mFlushFromAccuracySpinner.getSelectedItemPosition();
207+
long positionInFrames = Long.parseLong(mFlushFromFrameEditText.getText().toString());
208+
long result = flushFromFrameNative(accuracy, positionInFrames);
209+
if (result >= 0) {
210+
Toast.makeText(this,
211+
String.format("Successfully flushed from: %d, actual flushed position: %d",
212+
positionInFrames, result),
213+
Toast.LENGTH_LONG).show();
214+
} else {
215+
Toast.makeText(
216+
this, "Failed to flush from Frame", Toast.LENGTH_LONG).show();
217+
}
218+
} catch (NumberFormatException e) {
219+
Log.e(TAG, "Failed to flushFromFrame, the requested frame(" +
220+
mFlushFromFrameEditText.getText().toString() + ") is invalid");
221+
showErrorToast("Invalid number of frames");
222+
}
223+
}
224+
183225
public void onChannelBoxClicked(View view) {
184226
CheckBox checkBox = (CheckBox) view;
185227
String text = (String) checkBox.getText();

apps/OboeTester/app/src/main/res/layout/activity_test_output.xml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
34
xmlns:tools="http://schemas.android.com/tools"
45
android:layout_width="match_parent"
56
android:layout_height="match_parent"
@@ -44,6 +45,48 @@
4445

4546
<include layout="@layout/merge_audio_common"/>
4647

48+
<LinearLayout
49+
android:id="@+id/flushFromFrameLayout"
50+
android:layout_width="match_parent"
51+
android:layout_height="wrap_content"
52+
android:orientation="vertical">
53+
<LinearLayout
54+
android:layout_width="match_parent"
55+
android:layout_height="wrap_content"
56+
android:orientation="horizontal">
57+
<TextView
58+
android:layout_width="wrap_content"
59+
android:layout_height="wrap_content"
60+
android:text="Flush from frame:" />
61+
<EditText
62+
android:id="@+id/flushFromFrameEditText"
63+
android:layout_width="0dp"
64+
android:layout_height="wrap_content"
65+
android:layout_weight="1"
66+
android:inputType="number"/>
67+
</LinearLayout>
68+
<LinearLayout
69+
android:layout_width="match_parent"
70+
android:layout_height="wrap_content"
71+
android:orientation="horizontal">
72+
<TextView
73+
android:layout_width="wrap_content"
74+
android:layout_height="wrap_content"
75+
android:text="FlushFromAccuracy:" />
76+
<Spinner
77+
android:id="@+id/flushFromAccuracySpinner"
78+
android:layout_width="wrap_content"
79+
android:layout_height="wrap_content"
80+
android:entries="@array/flush_from_accuracy"
81+
android:prompt="@string/flush_from_accuracy_prompt" />
82+
<Button
83+
android:id="@+id/flushFromFrameButton"
84+
android:layout_width="match_parent"
85+
android:layout_height="wrap_content"
86+
android:text="Flush From Frame" />
87+
</LinearLayout>
88+
</LinearLayout>
89+
4790
<HorizontalScrollView
4891
android:layout_width="wrap_content"
4992
android:layout_height="wrap_content">

apps/OboeTester/app/src/main/res/values/strings.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,4 +350,10 @@
350350
<string name="dynamic_workload_graph_info">
351351
Dynamic Workload: Black\nCPU Load: Green or Red (Glitch detected)
352352
</string>
353+
354+
<string name="flush_from_accuracy_prompt">Choose a FlushFromAccuracy</string>
355+
<string-array name="flush_from_accuracy">
356+
<item>Undefined</item>
357+
<item>Accurate</item>
358+
</string-array>
353359
</resources>

include/oboe/AudioStream.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,51 @@ class AudioStream : public AudioStreamBase {
690690
return Result::ErrorUnimplemented;
691691
}
692692

693+
/**
694+
* Flush all data from given position. If this operation returns successfully, the following
695+
* data will be written from the returned position.
696+
*
697+
* This method will only available when the performance mode is
698+
* PerformanceMode::PowerSavingOffloaded.
699+
*
700+
* The requested position must not be negative or greater than the written frames. The current
701+
* written position can be known by querying getFramesWritten().
702+
*
703+
* When clients request to flush from a certain position, the audio system will return the
704+
* actual flushed position based on the requested position, playback latency, etc. The written
705+
* position will be updated as the actual flush position. All data after actual flush position
706+
* flushed. The client can provide data from actual flush position at next write operation or
707+
* data callback request. When the stream is flushed, the stream end will be reset. The client
708+
* must not write any data before this function returns. Otherwise, the data will be corrupted.
709+
* When the method returns successfully and the stream is active, the client must write data
710+
* immediately if little audio data remains. Otherwise, the stream will underrun.
711+
*
712+
* This was introduced in Android API Level 37.
713+
*
714+
* @param accuracy the accuracy requirement when flushing. The value must be one of the valid
715+
* FlushFromAccuracy value.
716+
* @param position the start point in frames to flush the stream.
717+
* @return a result which the error code indicates if the stream is successfully flush or not
718+
* and value as the successfully flushed position or suggested flushed position.
719+
* Result::OK if the stream is successfully flushed. The value is the actual flushed
720+
* position.
721+
* Result::ErrorUnimplemented if it is not supported by the device. The value is the
722+
* requested position.
723+
* Result::ErrorIllegalArgument if the stream is not an output offload stream or the
724+
* accuracy is not one of valid FlushFromAccuracy values. The value is the requested
725+
* position.
726+
* Result::ErrorOutOfRange if the provided position is negative or is greater than the
727+
* frames written or the stream cannot flush from the requested position and
728+
* FlushFromAccuracy::Accurate is requested. The value is suggested flushed position.
729+
* Result::ErrorDisconnected if the stream is disconnected. The value is the requested
730+
* position.
731+
* Result::ErrorClosed if the stream is closed. The value is the requested position.
732+
*/
733+
virtual ResultWithValue<int64_t> flushFromFrame(
734+
FlushFromAccuracy accuracy, int64_t positionInFrames) {
735+
return ResultWithValue<int64_t>(Result::ErrorUnimplemented);
736+
}
737+
693738
protected:
694739

695740
/**

include/oboe/Definitions.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,24 @@ namespace oboe {
10771077
Always
10781078
};
10791079

1080+
/**
1081+
* The values are defined to be used for the accuracy requirement when calling
1082+
* AudioStream.flushFromFrame.
1083+
*/
1084+
enum class FlushFromAccuracy : int32_t {
1085+
/**
1086+
* There is not requirement for frame accuracy when flushing, it is up to the OS
1087+
* to select a right position to flush from.
1088+
*/
1089+
Undefined = 0, // AAUDIO_FLUSH_FROM_ACCURACY_UNDEFINED
1090+
1091+
/**
1092+
* The stream must be flushed from the requested position. If it is not possible to flush
1093+
* from the requested position, the stream must not be flushed.
1094+
*/
1095+
Accurate = 1, // AAUDIO_FLUSH_FROM_ACCURACY_ACCURATE
1096+
};
1097+
10801098
/**
10811099
* On API 16 to 26 OpenSL ES will be used. When using OpenSL ES the optimal values for sampleRate and
10821100
* framesPerBurst are not known by the native code.

include/oboe/ResultWithValue.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ class ResultWithValue {
6565
: mValue(value)
6666
, mError(oboe::Result::OK) {}
6767

68+
ResultWithValue(T value, oboe::Result error)
69+
: mValue(value)
70+
, mError(error) {}
71+
6872
/**
6973
* Get the result.
7074
*

0 commit comments

Comments
 (0)