aaudio: fix SHARED MMAP mode in server plus other bugs
Fixed some buffer miscalculations, and some NPEs in the close() code.
Added debugging and some general cleanup.
Fixed data conversion.
Fixed start/pause/flush in server.
Added reference counting in server for endpoints.
Programs can now be ran more than once.
General code cleanup.
Reconnect with service if server dies.
Move stop() logic into server for better synchronization.
Add sleep to prevent race condition when closing an MMAP stream.
Bug: 33398120
Test: two write_sine_callback.cpp can be run simultaneously
Change-Id: Ibb006215a498868c222228d675ff961d7e0bf514
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 84fa227..65b17bc 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define LOG_TAG "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
#include <assert.h>
#include <map>
#include <mutex>
@@ -28,13 +32,18 @@
ANDROID_SINGLETON_STATIC_INSTANCE(AAudioEndpointManager);
AAudioEndpointManager::AAudioEndpointManager()
- : Singleton<AAudioEndpointManager>() {
+ : Singleton<AAudioEndpointManager>()
+ , mInputs()
+ , mOutputs() {
}
-AAudioServiceEndpoint *AAudioEndpointManager::findEndpoint(AAudioService &audioService, int32_t deviceId,
+AAudioServiceEndpoint *AAudioEndpointManager::openEndpoint(AAudioService &audioService, int32_t deviceId,
aaudio_direction_t direction) {
AAudioServiceEndpoint *endpoint = nullptr;
std::lock_guard<std::mutex> lock(mLock);
+
+ // Try to find an existing endpoint.
+ ALOGD("AAudioEndpointManager::openEndpoint(), device = %d, dir = %d", deviceId, direction);
switch (direction) {
case AAUDIO_DIRECTION_INPUT:
endpoint = mInputs[deviceId];
@@ -48,11 +57,11 @@
}
// If we can't find an existing one then open one.
- ALOGD("AAudioEndpointManager::findEndpoint(), found %p", endpoint);
+ ALOGD("AAudioEndpointManager::openEndpoint(), found %p", endpoint);
if (endpoint == nullptr) {
endpoint = new AAudioServiceEndpoint(audioService);
if (endpoint->open(deviceId, direction) != AAUDIO_OK) {
- ALOGD("AAudioEndpointManager::findEndpoint(), open failed");
+ ALOGE("AAudioEndpointManager::findEndpoint(), open failed");
delete endpoint;
endpoint = nullptr;
} else {
@@ -66,22 +75,37 @@
}
}
}
+
+ if (endpoint != nullptr) {
+ // Increment the reference count under this lock.
+ endpoint->setReferenceCount(endpoint->getReferenceCount() + 1);
+ }
+
return endpoint;
}
-// FIXME add reference counter for serviceEndpoints and removed on last use.
-
-void AAudioEndpointManager::removeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
- aaudio_direction_t direction = serviceEndpoint->getDirection();
- int32_t deviceId = serviceEndpoint->getDeviceId();
-
+void AAudioEndpointManager::closeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
std::lock_guard<std::mutex> lock(mLock);
- switch(direction) {
- case AAUDIO_DIRECTION_INPUT:
- mInputs.erase(deviceId);
- break;
- case AAUDIO_DIRECTION_OUTPUT:
- mOutputs.erase(deviceId);
- break;
+ if (serviceEndpoint == nullptr) {
+ return;
}
-}
\ No newline at end of file
+
+ // Decrement the reference count under this lock.
+ int32_t newRefCount = serviceEndpoint->getReferenceCount() - 1;
+ serviceEndpoint->setReferenceCount(newRefCount);
+ if (newRefCount <= 0) {
+ aaudio_direction_t direction = serviceEndpoint->getDirection();
+ int32_t deviceId = serviceEndpoint->getDeviceId();
+
+ switch (direction) {
+ case AAUDIO_DIRECTION_INPUT:
+ mInputs.erase(deviceId);
+ break;
+ case AAUDIO_DIRECTION_OUTPUT:
+ mOutputs.erase(deviceId);
+ break;
+ }
+ serviceEndpoint->close();
+ delete serviceEndpoint;
+ }
+}
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index 48b27f0..bbcfc1d 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -39,11 +39,11 @@
* @param direction
* @return endpoint or nullptr
*/
- AAudioServiceEndpoint *findEndpoint(android::AAudioService &audioService,
+ AAudioServiceEndpoint *openEndpoint(android::AAudioService &audioService,
int32_t deviceId,
aaudio_direction_t direction);
- void removeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
+ void closeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
private:
diff --git a/services/oboeservice/AAudioMixer.cpp b/services/oboeservice/AAudioMixer.cpp
index 70da339..43203d4 100644
--- a/services/oboeservice/AAudioMixer.cpp
+++ b/services/oboeservice/AAudioMixer.cpp
@@ -41,7 +41,7 @@
memset(mOutputBuffer, 0, mBufferSizeInBytes);
}
-void AAudioMixer::mix(FifoBuffer *fifo, float volume) {
+bool AAudioMixer::mix(FifoBuffer *fifo, float volume) {
WrappingBuffer wrappingBuffer;
float *destination = mOutputBuffer;
fifo_frames_t framesLeft = mFramesPerBurst;
@@ -67,9 +67,10 @@
}
fifo->getFifoControllerBase()->advanceReadIndex(mFramesPerBurst - framesLeft);
if (framesLeft > 0) {
- ALOGW("AAudioMixer::mix() UNDERFLOW by %d / %d frames ----- UNDERFLOW !!!!!!!!!!",
- framesLeft, mFramesPerBurst);
+ //ALOGW("AAudioMixer::mix() UNDERFLOW by %d / %d frames ----- UNDERFLOW !!!!!!!!!!",
+ // framesLeft, mFramesPerBurst);
}
+ return (framesLeft > 0); // did not get all the frames we needed, ie. "underflow"
}
void AAudioMixer::mixPart(float *destination, float *source, int32_t numFrames, float volume) {
diff --git a/services/oboeservice/AAudioMixer.h b/services/oboeservice/AAudioMixer.h
index 2191183..9155fec 100644
--- a/services/oboeservice/AAudioMixer.h
+++ b/services/oboeservice/AAudioMixer.h
@@ -31,7 +31,13 @@
void clear();
- void mix(android::FifoBuffer *fifo, float volume);
+ /**
+ * Mix from this FIFO
+ * @param fifo
+ * @param volume
+ * @return true if underflowed
+ */
+ bool mix(android::FifoBuffer *fifo, float volume);
void mixPart(float *destination, float *source, int32_t numFrames, float volume);
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 723ef63..816d5ab 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -54,8 +54,8 @@
aaudio_result_t result = AAUDIO_OK;
AAudioServiceStreamBase *serviceStream = nullptr;
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
+ bool sharingModeMatchRequired = request.isSharingModeMatchRequired();
aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
- ALOGE("AAudioService::openStream(): sharingMode = %d", sharingMode);
if (sharingMode != AAUDIO_SHARING_MODE_EXCLUSIVE && sharingMode != AAUDIO_SHARING_MODE_SHARED) {
ALOGE("AAudioService::openStream(): unrecognized sharing mode = %d", sharingMode);
@@ -77,8 +77,9 @@
}
// if SHARED requested or if EXCLUSIVE failed
- if (serviceStream == nullptr) {
- ALOGD("AAudioService::openStream(), sharingMode = AAUDIO_SHARING_MODE_SHARED");
+ if (sharingMode == AAUDIO_SHARING_MODE_SHARED
+ || (serviceStream == nullptr && !sharingModeMatchRequired)) {
+ ALOGD("AAudioService::openStream(), try AAUDIO_SHARING_MODE_SHARED");
serviceStream = new AAudioServiceStreamShared(*this);
result = serviceStream->open(request, configurationOutput);
configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
@@ -126,9 +127,7 @@
ALOGE("AAudioService::getStreamDescription(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
- ALOGD("AAudioService::getStreamDescription(), handle = 0x%08x", streamHandle);
aaudio_result_t result = serviceStream->getDescription(parcelable);
- ALOGD("AAudioService::getStreamDescription(), result = %d", result);
// parcelable.dump();
return result;
}
@@ -140,7 +139,6 @@
return AAUDIO_ERROR_INVALID_HANDLE;
}
aaudio_result_t result = serviceStream->start();
- ALOGD("AAudioService::startStream(), serviceStream->start() returned %d", result);
return result;
}
@@ -154,6 +152,16 @@
return result;
}
+aaudio_result_t AAudioService::stopStream(aaudio_handle_t streamHandle) {
+ AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream == nullptr) {
+ ALOGE("AAudioService::pauseStream(), illegal stream handle = 0x%0x", streamHandle);
+ return AAUDIO_ERROR_INVALID_HANDLE;
+ }
+ aaudio_result_t result = serviceStream->stop();
+ return result;
+}
+
aaudio_result_t AAudioService::flushStream(aaudio_handle_t streamHandle) {
AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
if (serviceStream == nullptr) {
@@ -168,7 +176,6 @@
pid_t clientThreadId,
int64_t periodNanoseconds) {
AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- ALOGD("AAudioService::registerAudioThread(), serviceStream = %p", serviceStream);
if (serviceStream == nullptr) {
ALOGE("AAudioService::registerAudioThread(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
@@ -193,7 +200,6 @@
pid_t clientProcessId,
pid_t clientThreadId) {
AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- ALOGI("AAudioService::unregisterAudioThread(), serviceStream = %p", serviceStream);
if (serviceStream == nullptr) {
ALOGE("AAudioService::unregisterAudioThread(), illegal stream handle = 0x%0x",
streamHandle);
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 5a7a2b6..f5a7d2f 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -57,6 +57,8 @@
virtual aaudio_result_t pauseStream(aaudio_handle_t streamHandle);
+ virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle);
+
virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle);
virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 80551c9..b197798 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -14,6 +14,17 @@
* 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>
@@ -30,6 +41,12 @@
// Wait at least this many times longer than the operation should take.
#define MIN_TIMEOUT_OPERATIONS 4
+// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
+#define DEFAULT_BUFFER_CAPACITY (48 * 8)
+
+// Use 2 for "double buffered"
+#define BUFFER_SIZE_IN_BURSTS 2
+
// The mStreamInternal will use a service interface that does not go through Binder.
AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)
: mStreamInternal(audioService, true)
@@ -43,11 +60,18 @@
aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId, aaudio_direction_t direction) {
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.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
+
aaudio_result_t result = mStreamInternal.open(builder);
if (result == AAUDIO_OK) {
mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
+
+ int32_t desiredBufferSize = BUFFER_SIZE_IN_BURSTS * mStreamInternal.getFramesPerBurst();
+ mStreamInternal.setBufferSize(desiredBufferSize);
}
return result;
}
@@ -58,15 +82,12 @@
// TODO, maybe use an interface to reduce exposure
aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
- ALOGD("AAudioServiceEndpoint::registerStream(%p)", sharedStream);
- // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
std::lock_guard<std::mutex> lock(mLockStreams);
mRegisteredStreams.push_back(sharedStream);
return AAUDIO_OK;
}
aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
- ALOGD("AAudioServiceEndpoint::unregisterStream(%p)", sharedStream);
std::lock_guard<std::mutex> lock(mLockStreams);
mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
mRegisteredStreams.end());
@@ -75,7 +96,6 @@
aaudio_result_t AAudioServiceEndpoint::startStream(AAudioServiceStreamShared *sharedStream) {
// TODO use real-time technique to avoid mutex, eg. atomic command FIFO
- ALOGD("AAudioServiceEndpoint(): startStream() entering");
std::lock_guard<std::mutex> lock(mLockStreams);
mRunningStreams.push_back(sharedStream);
if (mRunningStreams.size() == 1) {
@@ -106,13 +126,10 @@
// Render audio in the application callback and then write the data to the stream.
void *AAudioServiceEndpoint::callbackLoop() {
- aaudio_result_t result = AAUDIO_OK;
-
ALOGD("AAudioServiceEndpoint(): callbackLoop() entering");
+ int32_t underflowCount = 0;
- result = mStreamInternal.requestStart();
- ALOGD("AAudioServiceEndpoint(): callbackLoop() after requestStart() %d, isPlaying() = %d",
- result, (int) mStreamInternal.isPlaying());
+ aaudio_result_t result = mStreamInternal.requestStart();
// result might be a frame count
while (mCallbackEnabled.load() && mStreamInternal.isPlaying() && (result >= 0)) {
@@ -123,12 +140,14 @@
for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
float volume = 0.5; // TODO get from system
- mMixer.mix(fifo, volume);
+ 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.
- ALOGD("AAudioServiceEndpoint(): callbackLoop() write(%d)", getFramesPerBurst());
int64_t timeoutNanos = calculateReasonableTimeout(mStreamInternal.getFramesPerBurst());
result = mStreamInternal.write(mMixer.getOutputBuffer(), getFramesPerBurst(), timeoutNanos);
if (result == AAUDIO_ERROR_DISCONNECTED) {
@@ -141,11 +160,9 @@
}
}
- ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, result = %d, isPlaying() = %d",
- result, (int) mStreamInternal.isPlaying());
-
result = mStreamInternal.requestStop();
+ ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, %d underflows", underflowCount);
return NULL; // TODO review
}
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 020d38a..a4ceae6 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -56,6 +56,16 @@
void *callbackLoop();
+ // This should only be called from the AAudioEndpointManager under a mutex.
+ int32_t getReferenceCount() const {
+ return mReferenceCount;
+ }
+
+ // This should only be called from the AAudioEndpointManager under a mutex.
+ void setReferenceCount(int32_t count) {
+ mReferenceCount = count;
+ }
+
private:
aaudio_result_t startMixer_l();
aaudio_result_t stopMixer_l();
@@ -64,13 +74,14 @@
AudioStreamInternal mStreamInternal;
AAudioMixer mMixer;
- AAudioServiceStreamMMAP mStreamMMAP;
std::atomic<bool> mCallbackEnabled;
+ int32_t mReferenceCount = 0;
std::mutex mLockStreams;
std::vector<AAudioServiceStreamShared *> mRegisteredStreams;
std::vector<AAudioServiceStreamShared *> mRunningStreams;
+
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index b15043d..d8882c9 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -63,6 +63,7 @@
}
aaudio_result_t AAudioServiceStreamBase::start() {
+ ALOGD("AAudioServiceStreamBase::start() send AAUDIO_SERVICE_EVENT_STARTED");
sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
mState = AAUDIO_STREAM_STATE_STARTED;
mThreadEnabled.store(true);
@@ -78,14 +79,37 @@
processError();
return result;
}
+ ALOGD("AAudioServiceStreamBase::pause() send AAUDIO_SERVICE_EVENT_PAUSED");
sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
mState = AAUDIO_STREAM_STATE_PAUSED;
return result;
}
+aaudio_result_t AAudioServiceStreamBase::stop() {
+ // TODO wait for data to be played out
+ sendCurrentTimestamp();
+ mThreadEnabled.store(false);
+ aaudio_result_t result = mAAudioThread.stop();
+ if (result != AAUDIO_OK) {
+ processError();
+ return result;
+ }
+ ALOGD("AAudioServiceStreamBase::stop() send AAUDIO_SERVICE_EVENT_STOPPED");
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
+ mState = AAUDIO_STREAM_STATE_STOPPED;
+ return result;
+}
+
+aaudio_result_t AAudioServiceStreamBase::flush() {
+ ALOGD("AAudioServiceStreamBase::flush() send AAUDIO_SERVICE_EVENT_FLUSHED");
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
+ mState = AAUDIO_STREAM_STATE_FLUSHED;
+ return AAUDIO_OK;
+}
+
// implement Runnable
void AAudioServiceStreamBase::run() {
- ALOGD("AAudioServiceStreamMMAP::run() entering ----------------");
+ ALOGD("AAudioServiceStreamBase::run() entering ----------------");
TimestampScheduler timestampScheduler;
timestampScheduler.setBurstPeriod(mFramesPerBurst, mSampleRate);
timestampScheduler.start(AudioClock::getNanoseconds());
@@ -102,7 +126,7 @@
AudioClock::sleepUntilNanoTime(nextTime);
}
}
- ALOGD("AAudioServiceStreamMMAP::run() exiting ----------------");
+ ALOGD("AAudioServiceStreamBase::run() exiting ----------------");
}
void AAudioServiceStreamBase::processError() {
@@ -122,6 +146,10 @@
aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) {
std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+ if (mUpMessageQueue == nullptr) {
+ ALOGE("writeUpMessageQueue(): mUpMessageQueue null! - stream not open");
+ return AAUDIO_ERROR_NULL;
+ }
int32_t count = mUpMessageQueue->getFifoBuffer()->write(command, 1);
if (count != 1) {
ALOGE("writeUpMessageQueue(): Queue full. Did client die?");
@@ -133,9 +161,11 @@
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);
command.what = AAudioServiceMessage::code::TIMESTAMP;
result = writeUpMessageQueue(&command);
}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 91eec35..d6b6ee3 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -17,6 +17,7 @@
#ifndef AAUDIO_AAUDIO_SERVICE_STREAM_BASE_H
#define AAUDIO_AAUDIO_SERVICE_STREAM_BASE_H
+#include <assert.h>
#include <mutex>
#include "fifo/FifoBuffer.h"
@@ -60,17 +61,22 @@
/**
* Start the flow of data.
*/
- virtual aaudio_result_t start() = 0;
+ virtual aaudio_result_t start();
/**
* Stop the flow of data such that start() can resume with loss of data.
*/
- virtual aaudio_result_t pause() = 0;
+ virtual aaudio_result_t pause();
+
+ /**
+ * Stop the flow of data after data in buffer has played.
+ */
+ virtual aaudio_result_t stop();
/**
* Discard any data held by the underlying HAL or Service.
*/
- virtual aaudio_result_t flush() = 0;
+ virtual aaudio_result_t flush();
// -------------------------------------------------------------------
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index b70c625..b2e7fc9 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -55,6 +55,11 @@
aaudio_result_t AAudioServiceStreamMMAP::close() {
ALOGD("AAudioServiceStreamMMAP::close() called, %p", mMmapStream.get());
mMmapStream.clear(); // TODO review. Is that all we have to do?
+ // Apparently the above close is asynchronous. An attempt to open a new device
+ // right after a close can fail. Also some callbacks may still be in flight!
+ // FIXME Make closing synchronous.
+ AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
+
return AAudioServiceStreamBase::close();
}
@@ -79,8 +84,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();
@@ -198,16 +203,25 @@
return (result1 != AAUDIO_OK) ? result1 : result2;
}
+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);
+ mFramesRead.reset32();
+ return (result1 != AAUDIO_OK) ? result1 : result2;
+}
+
/**
* Discard any data held by the underlying HAL or Service.
*/
aaudio_result_t AAudioServiceStreamMMAP::flush() {
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
// TODO how do we flush an MMAP/NOIRQ buffer? sync pointers?
- ALOGD("AAudioServiceStreamMMAP::pause() send AAUDIO_SERVICE_EVENT_FLUSHED");
+ ALOGD("AAudioServiceStreamMMAP::flush() send AAUDIO_SERVICE_EVENT_FLUSHED");
sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
mState = AAUDIO_STREAM_STATE_FLUSHED;
- return AAUDIO_OK;
+ return AAudioServiceStreamBase::flush();;
}
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index f121c5c..a8e63a6 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -66,6 +66,8 @@
*/
aaudio_result_t pause() override;
+ aaudio_result_t stop() override;
+
/**
* Discard any data held by the underlying HAL or Service.
*
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index cd9336b..b5d9927 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -61,7 +61,7 @@
ALOGD("AAudioServiceStreamShared::open(), direction = %d", direction);
AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
- mServiceEndpoint = mEndpointManager.findEndpoint(mAudioService, deviceId, direction);
+ mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, deviceId, direction);
ALOGD("AAudioServiceStreamShared::open(), mServiceEndPoint = %p", mServiceEndpoint);
if (mServiceEndpoint == nullptr) {
return AAUDIO_ERROR_UNAVAILABLE;
@@ -72,6 +72,7 @@
if (mAudioFormat == AAUDIO_FORMAT_UNSPECIFIED) {
mAudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
} else if (mAudioFormat != AAUDIO_FORMAT_PCM_FLOAT) {
+ ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need FLOAT", mAudioFormat);
return AAUDIO_ERROR_INVALID_FORMAT;
}
@@ -79,6 +80,8 @@
if (mSampleRate == AAUDIO_FORMAT_UNSPECIFIED) {
mSampleRate = mServiceEndpoint->getSampleRate();
} else if (mSampleRate != mServiceEndpoint->getSampleRate()) {
+ ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need %d",
+ mSampleRate, mServiceEndpoint->getSampleRate());
return AAUDIO_ERROR_INVALID_RATE;
}
@@ -86,17 +89,22 @@
if (mSamplesPerFrame == AAUDIO_FORMAT_UNSPECIFIED) {
mSamplesPerFrame = mServiceEndpoint->getSamplesPerFrame();
} else if (mSamplesPerFrame != mServiceEndpoint->getSamplesPerFrame()) {
+ ALOGE("AAudioServiceStreamShared::open(), mSamplesPerFrame = %d, need %d",
+ mSamplesPerFrame, mServiceEndpoint->getSamplesPerFrame());
return AAUDIO_ERROR_OUT_OF_RANGE;
}
// Determine this stream's shared memory buffer capacity.
mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
int32_t minCapacityFrames = configurationInput.getBufferCapacity();
- int32_t numBursts = (minCapacityFrames + mFramesPerBurst - 1) / mFramesPerBurst;
- if (numBursts < MIN_BURSTS_PER_BUFFER) {
- numBursts = MIN_BURSTS_PER_BUFFER;
- } else if (numBursts > MAX_BURSTS_PER_BUFFER) {
- numBursts = MAX_BURSTS_PER_BUFFER;
+ int32_t numBursts = MAX_BURSTS_PER_BUFFER;
+ if (minCapacityFrames != AAUDIO_UNSPECIFIED) {
+ numBursts = (minCapacityFrames + mFramesPerBurst - 1) / mFramesPerBurst;
+ if (numBursts < MIN_BURSTS_PER_BUFFER) {
+ numBursts = MIN_BURSTS_PER_BUFFER;
+ } else if (numBursts > MAX_BURSTS_PER_BUFFER) {
+ numBursts = MAX_BURSTS_PER_BUFFER;
+ }
}
mCapacityInFrames = numBursts * mFramesPerBurst;
ALOGD("AAudioServiceStreamShared::open(), mCapacityInFrames = %d", mCapacityInFrames);
@@ -122,8 +130,12 @@
* An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamShared::start() {
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
// Add this stream to the mixer.
- aaudio_result_t result = mServiceEndpoint->startStream(this);
+ aaudio_result_t result = endpoint->startStream(this);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamShared::start() mServiceEndpoint returned %d", result);
processError();
@@ -139,15 +151,31 @@
* An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamShared::pause() {
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
// Add this stream to the mixer.
- aaudio_result_t result = mServiceEndpoint->stopStream(this);
+ aaudio_result_t result = endpoint->stopStream(this);
+ if (result != AAUDIO_OK) {
+ ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
+ processError();
+ }
+ return AAudioServiceStreamBase::pause();
+}
+
+aaudio_result_t AAudioServiceStreamShared::stop() {
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ // Add this stream to the mixer.
+ aaudio_result_t result = endpoint->stopStream(this);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
processError();
- } else {
- result = AAudioServiceStreamBase::start();
}
- return AAUDIO_OK;
+ return AAudioServiceStreamBase::stop();
}
/**
@@ -157,15 +185,21 @@
*/
aaudio_result_t AAudioServiceStreamShared::flush() {
// TODO make sure we are paused
- return AAUDIO_OK;
+ // TODO actually flush the data
+ return AAudioServiceStreamBase::flush() ;
}
aaudio_result_t AAudioServiceStreamShared::close() {
pause();
// TODO wait for pause() to synchronize
- mServiceEndpoint->unregisterStream(this);
- mServiceEndpoint->close();
- mServiceEndpoint = nullptr;
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint != nullptr) {
+ endpoint->unregisterStream(this);
+
+ AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+ mEndpointManager.closeEndpoint(endpoint);
+ mServiceEndpoint = nullptr;
+ }
return AAudioServiceStreamBase::close();
}
@@ -189,10 +223,15 @@
mServiceEndpoint = nullptr;
}
+void AAudioServiceStreamShared::markTransferTime(int64_t nanoseconds) {
+ mMarkedPosition = mAudioDataQueue->getFifoBuffer()->getReadCounter();
+ mMarkedTime = nanoseconds;
+}
aaudio_result_t AAudioServiceStreamShared::getFreeRunningPosition(int64_t *positionFrames,
int64_t *timeNanos) {
- *positionFrames = mAudioDataQueue->getFifoBuffer()->getReadCounter();
- *timeNanos = AudioClock::getNanoseconds();
+ // TODO get these two numbers as an atomic pair
+ *positionFrames = mMarkedPosition;
+ *timeNanos = mMarkedTime;
return AAUDIO_OK;
}
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index f6df7ce..b981387 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -66,6 +66,11 @@
aaudio_result_t pause() override;
/**
+ * Stop the flow of data after data in buffer has played.
+ */
+ aaudio_result_t stop() override;
+
+ /**
* Discard any data held by the underlying HAL or Service.
*
* This is not guaranteed to be synchronous but it currently is.
@@ -77,6 +82,11 @@
android::FifoBuffer *getDataFifoBuffer() { return mAudioDataQueue->getFifoBuffer(); }
+ /* Keep a record of when a buffer transfer completed.
+ * This allows for a more accurate timing model.
+ */
+ void markTransferTime(int64_t nanoseconds);
+
void onStop();
void onDisconnect();
@@ -91,6 +101,9 @@
android::AAudioService &mAudioService;
AAudioServiceEndpoint *mServiceEndpoint = nullptr;
SharedRingBuffer *mAudioDataQueue;
+
+ int64_t mMarkedPosition = 0;
+ int64_t mMarkedTime = 0;
};
} /* namespace aaudio */