aaudio: lower latency using MMAP capture
MMAP can be enabled by setting system properties.
Bug: 38267780
Test: input_monitor.cpp
Change-Id: I5e86fd1d9baef4fe59837ccbca7971acbb54d8b5
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 65b17bc..2cb0cba 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -21,10 +21,8 @@
#include <assert.h>
#include <map>
#include <mutex>
-#include <utils/Singleton.h>
#include "AAudioEndpointManager.h"
-#include "AAudioServiceEndpoint.h"
using namespace android;
using namespace aaudio;
@@ -55,32 +53,36 @@
assert(false); // There are only two possible directions.
break;
}
-
- // If we can't find an existing one then open one.
ALOGD("AAudioEndpointManager::openEndpoint(), found %p", endpoint);
+
+ // If we can't find an existing one then open a new one.
if (endpoint == nullptr) {
- endpoint = new AAudioServiceEndpoint(audioService);
- if (endpoint->open(deviceId, direction) != AAUDIO_OK) {
- ALOGE("AAudioEndpointManager::findEndpoint(), open failed");
- delete endpoint;
- endpoint = nullptr;
- } else {
- switch(direction) {
- case AAUDIO_DIRECTION_INPUT:
- mInputs[deviceId] = endpoint;
- break;
- case AAUDIO_DIRECTION_OUTPUT:
- mOutputs[deviceId] = endpoint;
- break;
+ if (direction == AAUDIO_DIRECTION_INPUT) {
+ AAudioServiceEndpointCapture *capture = new AAudioServiceEndpointCapture(audioService);
+ if (capture->open(deviceId) != AAUDIO_OK) {
+ ALOGE("AAudioEndpointManager::openEndpoint(), open failed");
+ delete capture;
+ } else {
+ mInputs[deviceId] = capture;
+ endpoint = capture;
+ }
+ } else if (direction == AAUDIO_DIRECTION_OUTPUT) {
+ AAudioServiceEndpointPlay *player = new AAudioServiceEndpointPlay(audioService);
+ if (player->open(deviceId) != AAUDIO_OK) {
+ ALOGE("AAudioEndpointManager::openEndpoint(), open failed");
+ delete player;
+ } else {
+ mOutputs[deviceId] = player;
+ endpoint = player;
}
}
+
}
if (endpoint != nullptr) {
// Increment the reference count under this lock.
endpoint->setReferenceCount(endpoint->getReferenceCount() + 1);
}
-
return endpoint;
}
@@ -105,6 +107,7 @@
mOutputs.erase(deviceId);
break;
}
+
serviceEndpoint->close();
delete serviceEndpoint;
}
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index bbcfc1d..db1103d 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -23,6 +23,8 @@
#include "binding/AAudioServiceMessage.h"
#include "AAudioServiceEndpoint.h"
+#include "AAudioServiceEndpointCapture.h"
+#include "AAudioServiceEndpointPlay.h"
namespace aaudio {
@@ -49,10 +51,8 @@
std::mutex mLock;
- // We need separate inputs and outputs because they may both have device==0.
- // TODO review
- std::map<int32_t, AAudioServiceEndpoint *> mInputs;
- std::map<int32_t, AAudioServiceEndpoint *> mOutputs;
+ std::map<int32_t, AAudioServiceEndpointCapture *> mInputs;
+ std::map<int32_t, AAudioServiceEndpointPlay *> mOutputs;
};
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 57dc58b..e7d9e0d 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -44,43 +44,23 @@
// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
#define DEFAULT_BUFFER_CAPACITY (48 * 8)
-// The mStreamInternal will use a service interface that does not go through Binder.
-AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)
- : mStreamInternal(audioService, true)
- {
-}
-
-AAudioServiceEndpoint::~AAudioServiceEndpoint() {
-}
-
// Set up an EXCLUSIVE MMAP stream that will be shared.
-aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId, aaudio_direction_t direction) {
+aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId) {
+ mStreamInternal = getStreamInternal();
+
AudioStreamBuilder builder;
builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
// Don't fall back to SHARED because that would cause recursion.
builder.setSharingModeMatchRequired(true);
builder.setDeviceId(deviceId);
- builder.setDirection(direction);
+ builder.setDirection(getDirection());
builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
- aaudio_result_t result = mStreamInternal.open(builder);
- if (result == AAUDIO_OK) {
- mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
-
- int32_t burstsPerBuffer = AAudioProperty_getMixerBursts();
- if (burstsPerBuffer == 0) {
- mLatencyTuningEnabled = true;
- burstsPerBuffer = 2;
- }
- ALOGD("AAudioServiceEndpoint(): burstsPerBuffer = %d", burstsPerBuffer);
- int32_t desiredBufferSize = burstsPerBuffer * mStreamInternal.getFramesPerBurst();
- mStreamInternal.setBufferSize(desiredBufferSize);
- }
- return result;
+ return getStreamInternal()->open(builder);
}
aaudio_result_t AAudioServiceEndpoint::close() {
- return mStreamInternal.close();
+ return getStreamInternal()->close();
}
// TODO, maybe use an interface to reduce exposure
@@ -102,96 +82,51 @@
std::lock_guard<std::mutex> lock(mLockStreams);
mRunningStreams.push_back(sharedStream);
if (mRunningStreams.size() == 1) {
- startMixer_l();
+ startSharingThread_l();
}
return AAUDIO_OK;
}
aaudio_result_t AAudioServiceEndpoint::stopStream(AAudioServiceStreamShared *sharedStream) {
- std::lock_guard<std::mutex> lock(mLockStreams);
- mRunningStreams.erase(std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
- mRunningStreams.end());
- if (mRunningStreams.size() == 0) {
- stopMixer_l();
+ int numRunningStreams = 0;
+ {
+ std::lock_guard<std::mutex> lock(mLockStreams);
+ mRunningStreams.erase(
+ std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
+ mRunningStreams.end());
+ numRunningStreams = mRunningStreams.size();
+ }
+ if (numRunningStreams == 0) {
+ // Don't call this under a lock because the callbackLoop also uses the lock.
+ stopSharingThread();
}
return AAUDIO_OK;
}
-static void *aaudio_mixer_thread_proc(void *context) {
- AAudioServiceEndpoint *stream = (AAudioServiceEndpoint *) context;
- if (stream != NULL) {
- return stream->callbackLoop();
+static void *aaudio_endpoint_thread_proc(void *context) {
+ AAudioServiceEndpoint *endpoint = (AAudioServiceEndpoint *) context;
+ if (endpoint != NULL) {
+ return endpoint->callbackLoop();
} else {
return NULL;
}
}
-// Render audio in the application callback and then write the data to the stream.
-void *AAudioServiceEndpoint::callbackLoop() {
- ALOGD("AAudioServiceEndpoint(): callbackLoop() entering");
- int32_t underflowCount = 0;
-
- aaudio_result_t result = mStreamInternal.requestStart();
-
- // result might be a frame count
- while (mCallbackEnabled.load() && mStreamInternal.isPlaying() && (result >= 0)) {
- // Mix data from each active stream.
- {
- mMixer.clear();
- std::lock_guard<std::mutex> lock(mLockStreams);
- for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
- FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
- const float volume = 1.0f; // to match the perceived volume from AudioTrack
- bool underflowed = mMixer.mix(fifo, volume);
- underflowCount += underflowed ? 1 : 0;
- // TODO log underflows in each stream
- sharedStream->markTransferTime(AudioClock::getNanoseconds());
- }
- }
-
- // Write audio data to stream using a blocking write.
- int64_t timeoutNanos = calculateReasonableTimeout(mStreamInternal.getFramesPerBurst());
- result = mStreamInternal.write(mMixer.getOutputBuffer(), getFramesPerBurst(), timeoutNanos);
- if (result == AAUDIO_ERROR_DISCONNECTED) {
- disconnectRegisteredStreams();
- break;
- } else if (result != getFramesPerBurst()) {
- ALOGW("AAudioServiceEndpoint(): callbackLoop() wrote %d / %d",
- result, getFramesPerBurst());
- break;
- }
- }
-
- result = mStreamInternal.requestStop();
-
- ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, %d underflows", underflowCount);
- return NULL; // TODO review
-}
-
-aaudio_result_t AAudioServiceEndpoint::startMixer_l() {
+aaudio_result_t AAudioServiceEndpoint::startSharingThread_l() {
// Launch the callback loop thread.
- int64_t periodNanos = mStreamInternal.getFramesPerBurst()
+ int64_t periodNanos = getStreamInternal()->getFramesPerBurst()
* AAUDIO_NANOS_PER_SECOND
/ getSampleRate();
mCallbackEnabled.store(true);
- return mStreamInternal.createThread(periodNanos, aaudio_mixer_thread_proc, this);
+ return getStreamInternal()->createThread(periodNanos, aaudio_endpoint_thread_proc, this);
}
-aaudio_result_t AAudioServiceEndpoint::stopMixer_l() {
+aaudio_result_t AAudioServiceEndpoint::stopSharingThread() {
+ ALOGD("AAudioServiceEndpoint(): call joinThread()");
mCallbackEnabled.store(false);
- return mStreamInternal.joinThread(NULL, calculateReasonableTimeout(mStreamInternal.getFramesPerBurst()));
-}
-
-// TODO Call method in AudioStreamInternal when that callback CL is merged.
-int64_t AAudioServiceEndpoint::calculateReasonableTimeout(int32_t framesPerOperation) {
-
- // Wait for at least a second or some number of callbacks to join the thread.
- int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS * framesPerOperation * AAUDIO_NANOS_PER_SECOND)
- / getSampleRate();
- if (timeoutNanoseconds < MIN_TIMEOUT_NANOS) { // arbitrary number of seconds
- timeoutNanoseconds = MIN_TIMEOUT_NANOS;
- }
- return timeoutNanoseconds;
+ aaudio_result_t result = getStreamInternal()->joinThread(NULL);
+ ALOGD("AAudioServiceEndpoint(): joinThread() returned %d", result);
+ return result;
}
void AAudioServiceEndpoint::disconnectRegisteredStreams() {
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index d0c2f53..50bf049 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -23,6 +23,7 @@
#include <vector>
#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalPlay.h"
#include "binding/AAudioServiceMessage.h"
#include "AAudioServiceStreamShared.h"
#include "AAudioServiceStreamMMAP.h"
@@ -33,14 +34,13 @@
class AAudioServiceEndpoint {
public:
- explicit AAudioServiceEndpoint(android::AAudioService &audioService);
- virtual ~AAudioServiceEndpoint();
+ virtual ~AAudioServiceEndpoint() = default;
- aaudio_result_t open(int32_t deviceId, aaudio_direction_t direction);
+ virtual aaudio_result_t open(int32_t deviceId);
- int32_t getSampleRate() const { return mStreamInternal.getSampleRate(); }
- int32_t getSamplesPerFrame() const { return mStreamInternal.getSamplesPerFrame(); }
- int32_t getFramesPerBurst() const { return mStreamInternal.getFramesPerBurst(); }
+ int32_t getSampleRate() const { return mStreamInternal->getSampleRate(); }
+ int32_t getSamplesPerFrame() const { return mStreamInternal->getSamplesPerFrame(); }
+ int32_t getFramesPerBurst() const { return mStreamInternal->getFramesPerBurst(); }
aaudio_result_t registerStream(AAudioServiceStreamShared *sharedStream);
aaudio_result_t unregisterStream(AAudioServiceStreamShared *sharedStream);
@@ -48,13 +48,13 @@
aaudio_result_t stopStream(AAudioServiceStreamShared *sharedStream);
aaudio_result_t close();
- int32_t getDeviceId() const { return mStreamInternal.getDeviceId(); }
+ int32_t getDeviceId() const { return mStreamInternal->getDeviceId(); }
- aaudio_direction_t getDirection() const { return mStreamInternal.getDirection(); }
+ aaudio_direction_t getDirection() const { return mStreamInternal->getDirection(); }
void disconnectRegisteredStreams();
- void *callbackLoop();
+ virtual void *callbackLoop() = 0;
// This should only be called from the AAudioEndpointManager under a mutex.
int32_t getReferenceCount() const {
@@ -66,23 +66,21 @@
mReferenceCount = count;
}
-private:
- aaudio_result_t startMixer_l();
- aaudio_result_t stopMixer_l();
-
- int64_t calculateReasonableTimeout(int32_t framesPerOperation);
-
- AudioStreamInternal mStreamInternal;
- AAudioMixer mMixer;
+ virtual AudioStreamInternal *getStreamInternal() = 0;
std::atomic<bool> mCallbackEnabled;
- int32_t mReferenceCount = 0;
- bool mLatencyTuningEnabled = false; // TODO implement tuning
std::mutex mLockStreams;
+
std::vector<AAudioServiceStreamShared *> mRegisteredStreams;
std::vector<AAudioServiceStreamShared *> mRunningStreams;
+private:
+ aaudio_result_t startSharingThread_l();
+ aaudio_result_t stopSharingThread();
+
+ AudioStreamInternal *mStreamInternal = nullptr;
+ int32_t mReferenceCount = 0;
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
new file mode 100644
index 0000000..b7b42b4
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <assert.h>
+#include <map>
+#include <mutex>
+#include <utils/Singleton.h>
+
+#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpoint.h"
+
+#include "core/AudioStreamBuilder.h"
+#include "AAudioServiceEndpoint.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceEndpointCapture.h"
+
+using namespace android; // TODO just import names needed
+using namespace aaudio; // TODO just import names needed
+
+AAudioServiceEndpointCapture::AAudioServiceEndpointCapture(AAudioService &audioService)
+ : mStreamInternalCapture(audioService, true) {
+}
+
+AAudioServiceEndpointCapture::~AAudioServiceEndpointCapture() {
+ delete mDistributionBuffer;
+}
+
+aaudio_result_t AAudioServiceEndpointCapture::open(int32_t deviceId) {
+ aaudio_result_t result = AAudioServiceEndpoint::open(deviceId);
+ if (result == AAUDIO_OK) {
+ delete mDistributionBuffer;
+ int distributionBufferSizeBytes = getStreamInternal()->getFramesPerBurst()
+ * getStreamInternal()->getBytesPerFrame();
+ mDistributionBuffer = new uint8_t[distributionBufferSizeBytes];
+ }
+ return result;
+}
+
+// Read data from the shared MMAP stream and then distribute it to the client streams.
+void *AAudioServiceEndpointCapture::callbackLoop() {
+ ALOGD("AAudioServiceEndpointCapture(): callbackLoop() entering");
+ int32_t underflowCount = 0;
+
+ aaudio_result_t result = getStreamInternal()->requestStart();
+
+ int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
+
+ // result might be a frame count
+ while (mCallbackEnabled.load() && getStreamInternal()->isActive() && (result >= 0)) {
+ // Read audio data from stream using a blocking read.
+ result = getStreamInternal()->read(mDistributionBuffer, getFramesPerBurst(), timeoutNanos);
+ if (result == AAUDIO_ERROR_DISCONNECTED) {
+ disconnectRegisteredStreams();
+ break;
+ } else if (result != getFramesPerBurst()) {
+ ALOGW("AAudioServiceEndpointCapture(): callbackLoop() read %d / %d",
+ result, getFramesPerBurst());
+ break;
+ }
+
+ // Distribute data to each active stream.
+ { // use lock guard
+ //ALOGD("AAudioServiceEndpointCapture(): try to lock()");
+ std::lock_guard <std::mutex> lock(mLockStreams);
+ //ALOGD("AAudioServiceEndpointCapture(): got lock()");
+ for (AAudioServiceStreamShared *sharedStream : mRunningStreams) {
+ FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
+ if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
+ getFramesPerBurst()) {
+ underflowCount++;
+ } else {
+ fifo->write(mDistributionBuffer, getFramesPerBurst());
+ }
+ sharedStream->markTransferTime(AudioClock::getNanoseconds());
+ }
+ }
+ }
+
+ result = getStreamInternal()->requestStop();
+
+ ALOGD("AAudioServiceEndpointCapture(): callbackLoop() exiting, %d underflows", underflowCount);
+ return NULL; // TODO review
+}
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.h b/services/oboeservice/AAudioServiceEndpointCapture.h
new file mode 100644
index 0000000..35857d1
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointCapture.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAUDIO_SERVICE_ENDPOINT_CAPTURE_H
+#define AAUDIO_SERVICE_ENDPOINT_CAPTURE_H
+
+#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalCapture.h"
+
+namespace aaudio {
+
+class AAudioServiceEndpointCapture : public AAudioServiceEndpoint {
+public:
+ explicit AAudioServiceEndpointCapture(android::AAudioService &audioService);
+ virtual ~AAudioServiceEndpointCapture();
+
+ aaudio_result_t open(int32_t deviceId) override;
+
+ AudioStreamInternal *getStreamInternal() override {
+ return &mStreamInternalCapture;
+ }
+
+ void *callbackLoop() override;
+
+private:
+ AudioStreamInternalCapture mStreamInternalCapture;
+ uint8_t *mDistributionBuffer = nullptr;
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_SERVICE_ENDPOINT_CAPTURE_H
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
new file mode 100644
index 0000000..cc09cc3
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <assert.h>
+#include <map>
+#include <mutex>
+#include <utils/Singleton.h>
+
+#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpoint.h"
+#include <algorithm>
+#include <mutex>
+#include <vector>
+
+#include "core/AudioStreamBuilder.h"
+#include "AAudioServiceEndpoint.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceEndpointPlay.h"
+
+using namespace android; // TODO just import names needed
+using namespace aaudio; // TODO just import names needed
+
+#define BURSTS_PER_BUFFER_DEFAULT 2
+
+AAudioServiceEndpointPlay::AAudioServiceEndpointPlay(AAudioService &audioService)
+ : mStreamInternalPlay(audioService, true) {
+}
+
+AAudioServiceEndpointPlay::~AAudioServiceEndpointPlay() {
+}
+
+aaudio_result_t AAudioServiceEndpointPlay::open(int32_t deviceId) {
+ aaudio_result_t result = AAudioServiceEndpoint::open(deviceId);
+ if (result == AAUDIO_OK) {
+ mMixer.allocate(getStreamInternal()->getSamplesPerFrame(),
+ getStreamInternal()->getFramesPerBurst());
+
+ int32_t burstsPerBuffer = AAudioProperty_getMixerBursts();
+ if (burstsPerBuffer == 0) {
+ mLatencyTuningEnabled = true;
+ burstsPerBuffer = BURSTS_PER_BUFFER_DEFAULT;
+ }
+ ALOGD("AAudioServiceEndpoint(): burstsPerBuffer = %d", burstsPerBuffer);
+ int32_t desiredBufferSize = burstsPerBuffer * getStreamInternal()->getFramesPerBurst();
+ getStreamInternal()->setBufferSize(desiredBufferSize);
+ }
+ return result;
+}
+
+// Mix data from each application stream and write result to the shared MMAP stream.
+void *AAudioServiceEndpointPlay::callbackLoop() {
+ ALOGD("AAudioServiceEndpointPlay(): callbackLoop() entering");
+ int32_t underflowCount = 0;
+
+ aaudio_result_t result = getStreamInternal()->requestStart();
+
+ int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
+
+ // result might be a frame count
+ while (mCallbackEnabled.load() && getStreamInternal()->isActive() && (result >= 0)) {
+ // Mix data from each active stream.
+ mMixer.clear();
+ { // use lock guard
+ std::lock_guard <std::mutex> lock(mLockStreams);
+ for (AAudioServiceStreamShared *sharedStream : mRunningStreams) {
+ FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
+ float volume = 0.5; // TODO get from system
+ bool underflowed = mMixer.mix(fifo, volume);
+ underflowCount += underflowed ? 1 : 0;
+ // TODO log underflows in each stream
+ sharedStream->markTransferTime(AudioClock::getNanoseconds());
+ }
+ }
+
+ // Write mixer output to stream using a blocking write.
+ result = getStreamInternal()->write(mMixer.getOutputBuffer(),
+ getFramesPerBurst(), timeoutNanos);
+ if (result == AAUDIO_ERROR_DISCONNECTED) {
+ disconnectRegisteredStreams();
+ break;
+ } else if (result != getFramesPerBurst()) {
+ ALOGW("AAudioServiceEndpoint(): callbackLoop() wrote %d / %d",
+ result, getFramesPerBurst());
+ break;
+ }
+ }
+
+ result = getStreamInternal()->requestStop();
+
+ ALOGD("AAudioServiceEndpointPlay(): callbackLoop() exiting, %d underflows", underflowCount);
+ return NULL; // TODO review
+}
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.h b/services/oboeservice/AAudioServiceEndpointPlay.h
new file mode 100644
index 0000000..b977960
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointPlay.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAUDIO_SERVICE_ENDPOINT_PLAY_H
+#define AAUDIO_SERVICE_ENDPOINT_PLAY_H
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalPlay.h"
+#include "binding/AAudioServiceMessage.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioMixer.h"
+#include "AAudioService.h"
+
+namespace aaudio {
+
+class AAudioServiceEndpointPlay : public AAudioServiceEndpoint {
+public:
+ explicit AAudioServiceEndpointPlay(android::AAudioService &audioService);
+ virtual ~AAudioServiceEndpointPlay();
+
+ aaudio_result_t open(int32_t deviceId) override;
+
+ AudioStreamInternal *getStreamInternal() override {
+ return &mStreamInternalPlay;
+ }
+
+ void *callbackLoop() override;
+
+private:
+ AudioStreamInternalPlay mStreamInternalPlay; // for playing output of mixer
+ bool mLatencyTuningEnabled = false; // TODO implement tuning
+ AAudioMixer mMixer; //
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_SERVICE_ENDPOINT_PLAY_H
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 8248f8b..f04bc86 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -162,11 +162,12 @@
aaudio_result_t AAudioServiceStreamBase::sendCurrentTimestamp() {
AAudioServiceMessage command;
- //ALOGD("sendCurrentTimestamp() called");
aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position,
&command.timestamp.timestamp);
if (result == AAUDIO_OK) {
- //ALOGD("sendCurrentTimestamp(): position %d", (int) command.timestamp.position);
+ // ALOGD("sendCurrentTimestamp(): position = %lld, nanos = %lld",
+ // (long long) command.timestamp.position,
+ // (long long) command.timestamp.timestamp);
command.what = AAudioServiceMessage::code::TIMESTAMP;
result = writeUpMessageQueue(&command);
}
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 78a1583..b9a5631 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -76,7 +76,7 @@
const audio_attributes_t attributes = {
.content_type = AUDIO_CONTENT_TYPE_MUSIC,
.usage = AUDIO_USAGE_MEDIA,
- .source = AUDIO_SOURCE_DEFAULT,
+ .source = AUDIO_SOURCE_VOICE_RECOGNITION,
.flags = AUDIO_FLAG_LOW_LATENCY,
.tags = ""
};
@@ -91,8 +91,8 @@
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
audio_port_handle_t deviceId = configurationInput.getDeviceId();
- // ALOGI("open request dump()");
- // request.dump();
+ ALOGI("open request dump()");
+ request.dump();
mMmapClient.clientUid = request.getUserId();
mMmapClient.clientPid = request.getProcessId();
@@ -171,7 +171,7 @@
: audio_channel_count_from_in_mask(config.channel_mask);
mAudioDataFileDescriptor = mMmapBufferinfo.shared_memory_fd;
- ALOGV("AAudioServiceStreamMMAP::open LEAK? mAudioDataFileDescriptor = %d\n",
+ ALOGD("AAudioServiceStreamMMAP::open LEAK? mAudioDataFileDescriptor = %d\n",
mAudioDataFileDescriptor);
mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
mCapacityInFrames = mMmapBufferinfo.buffer_size_frames;
@@ -205,16 +205,17 @@
return AAUDIO_OK;
}
-
/**
* Start the flow of data.
*/
aaudio_result_t AAudioServiceStreamMMAP::start() {
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
- aaudio_result_t result = mMmapStream->start(mMmapClient, &mPortHandle);
- if (result != AAUDIO_OK) {
- ALOGE("AAudioServiceStreamMMAP::start() mMmapStream->start() returned %d", result);
+ aaudio_result_t result;
+ status_t status = mMmapStream->start(mMmapClient, &mPortHandle);
+ if (status != OK) {
+ ALOGE("AAudioServiceStreamMMAP::start() mMmapStream->start() returned %d", status);
processError();
+ result = AAudioConvert_androidToAAudioResult(status);
} else {
result = AAudioServiceStreamBase::start();
}
@@ -228,18 +229,18 @@
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
aaudio_result_t result1 = AAudioServiceStreamBase::pause();
- aaudio_result_t result2 = mMmapStream->stop(mPortHandle);
+ status_t status = mMmapStream->stop(mPortHandle);
mFramesRead.reset32();
- return (result1 != AAUDIO_OK) ? result1 : result2;
+ return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status);
}
aaudio_result_t AAudioServiceStreamMMAP::stop() {
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
aaudio_result_t result1 = AAudioServiceStreamBase::stop();
- aaudio_result_t result2 = mMmapStream->stop(mPortHandle);
+ aaudio_result_t status = mMmapStream->stop(mPortHandle);
mFramesRead.reset32();
- return (result1 != AAUDIO_OK) ? result1 : result2;
+ return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status);
}
/**
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 713d1f8..3bf7811 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -62,8 +62,8 @@
ALOGD("AAudioServiceStreamShared::open(), direction = %d", direction);
AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, deviceId, direction);
- ALOGD("AAudioServiceStreamShared::open(), mServiceEndPoint = %p", mServiceEndpoint);
if (mServiceEndpoint == nullptr) {
+ ALOGE("AAudioServiceStreamShared::open(), mServiceEndPoint = %p", mServiceEndpoint);
return AAUDIO_ERROR_UNAVAILABLE;
}
@@ -134,7 +134,7 @@
if (endpoint == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
- // Add this stream to the mixer.
+ // For output streams, this will add the stream to the mixer.
aaudio_result_t result = endpoint->startStream(this);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamShared::start() mServiceEndpoint returned %d", result);
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index b981387..dfdbbb3 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -100,7 +100,7 @@
private:
android::AAudioService &mAudioService;
AAudioServiceEndpoint *mServiceEndpoint = nullptr;
- SharedRingBuffer *mAudioDataQueue;
+ SharedRingBuffer *mAudioDataQueue = nullptr;
int64_t mMarkedPosition = 0;
int64_t mMarkedTime = 0;
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index afb477e..b447725 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -29,6 +29,8 @@
AAudioMixer.cpp \
AAudioService.cpp \
AAudioServiceEndpoint.cpp \
+ AAudioServiceEndpointCapture.cpp \
+ AAudioServiceEndpointPlay.cpp \
AAudioServiceStreamBase.cpp \
AAudioServiceStreamMMAP.cpp \
AAudioServiceStreamShared.cpp \