aaudio: fix some state transitions

Now requestStop() and requestPause() do the appropriate thing
based on state.
Flush is allowed when OPEN, PAUSED or STOPPED because there might
be pre-roll data.

Bug: 69810494
Test: test_various.cpp
Change-Id: Ie1b306b17734a58fa71b1742bb186482893656b7
gugelfrei
Phil Burk 7 years ago
parent bb8ad0e6eb
commit 5cc83c3b8a

@ -36,7 +36,6 @@ typedef enum aaudio_service_event_e : uint32_t {
AAUDIO_SERVICE_EVENT_PAUSED,
AAUDIO_SERVICE_EVENT_STOPPED,
AAUDIO_SERVICE_EVENT_FLUSHED,
AAUDIO_SERVICE_EVENT_CLOSED,
AAUDIO_SERVICE_EVENT_DISCONNECTED,
AAUDIO_SERVICE_EVENT_VOLUME,
AAUDIO_SERVICE_EVENT_XRUN

@ -340,8 +340,13 @@ aaudio_result_t AudioStreamInternal::stopCallback()
}
}
aaudio_result_t AudioStreamInternal::requestStopInternal()
aaudio_result_t AudioStreamInternal::requestStop()
{
aaudio_result_t result = stopCallback();
if (result != AAUDIO_OK) {
return result;
}
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
ALOGE("requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
mServiceStreamHandle);
@ -355,16 +360,6 @@ aaudio_result_t AudioStreamInternal::requestStopInternal()
return mServiceInterface.stopStream(mServiceStreamHandle);
}
aaudio_result_t AudioStreamInternal::requestStop()
{
aaudio_result_t result = stopCallback();
if (result != AAUDIO_OK) {
return result;
}
result = requestStopInternal();
return result;
}
aaudio_result_t AudioStreamInternal::registerThread() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
ALOGE("registerThread() mServiceStreamHandle invalid");
@ -483,10 +478,6 @@ aaudio_result_t AudioStreamInternal::onEventFromServer(AAudioServiceMessage *mes
onFlushFromServer();
}
break;
case AAUDIO_SERVICE_EVENT_CLOSED:
ALOGD("%s - got AAUDIO_SERVICE_EVENT_CLOSED", __func__);
setState(AAUDIO_STREAM_STATE_CLOSED);
break;
case AAUDIO_SERVICE_EVENT_DISCONNECTED:
// Prevent hardware from looping on old data and making buzzing sounds.
if (getDirection() == AAUDIO_DIRECTION_OUTPUT) {

@ -121,8 +121,6 @@ protected:
aaudio_result_t processCommands();
aaudio_result_t requestStopInternal();
aaudio_result_t stopCallback();
virtual void advanceClientToMatchServerPosition() = 0;

@ -38,9 +38,12 @@ AudioStreamInternalPlay::AudioStreamInternalPlay(AAudioServiceInterface &servic
AudioStreamInternalPlay::~AudioStreamInternalPlay() {}
aaudio_result_t AudioStreamInternalPlay::requestPauseInternal()
aaudio_result_t AudioStreamInternalPlay::requestPause()
{
aaudio_result_t result = stopCallback();
if (result != AAUDIO_OK) {
return result;
}
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
ALOGE("AudioStreamInternal::requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
mServiceStreamHandle);
@ -53,16 +56,6 @@ aaudio_result_t AudioStreamInternalPlay::requestPauseInternal()
return mServiceInterface.pauseStream(mServiceStreamHandle);
}
aaudio_result_t AudioStreamInternalPlay::requestPause()
{
aaudio_result_t result = stopCallback();
if (result != AAUDIO_OK) {
return result;
}
result = requestPauseInternal();
return result;
}
aaudio_result_t AudioStreamInternalPlay::requestFlush() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
ALOGE("AudioStreamInternal::requestFlush() mServiceStreamHandle invalid = 0x%08X",

@ -37,6 +37,16 @@ public:
aaudio_result_t requestFlush() override;
bool isFlushSupported() const override {
// Only implement FLUSH for OUTPUT streams.
return true;
}
bool isPauseSupported() const override {
// Only implement PAUSE for OUTPUT streams.
return true;
}
aaudio_result_t write(const void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds) override;
@ -52,8 +62,6 @@ public:
protected:
aaudio_result_t requestPauseInternal();
void advanceClientToMatchServerPosition() override;
void onFlushFromServer() override;

@ -130,29 +130,106 @@ aaudio_result_t AudioStream::safeStart() {
}
aaudio_result_t AudioStream::safePause() {
if (!isPauseSupported()) {
return AAUDIO_ERROR_UNIMPLEMENTED;
}
std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
switch (getState()) {
// Proceed with pausing.
case AAUDIO_STREAM_STATE_STARTING:
case AAUDIO_STREAM_STATE_STARTED:
case AAUDIO_STREAM_STATE_DISCONNECTED:
break;
// Transition from one inactive state to another.
case AAUDIO_STREAM_STATE_OPEN:
case AAUDIO_STREAM_STATE_STOPPED:
case AAUDIO_STREAM_STATE_FLUSHED:
setState(AAUDIO_STREAM_STATE_PAUSED);
return AAUDIO_OK;
// Redundant?
case AAUDIO_STREAM_STATE_PAUSING:
case AAUDIO_STREAM_STATE_PAUSED:
return AAUDIO_OK;
// Don't interfere with transitional states or when closed.
case AAUDIO_STREAM_STATE_STOPPING:
case AAUDIO_STREAM_STATE_FLUSHING:
case AAUDIO_STREAM_STATE_CLOSING:
case AAUDIO_STREAM_STATE_CLOSED:
default:
ALOGW("safePause() stream not running, state = %s",
AAudio_convertStreamStateToText(getState()));
return AAUDIO_ERROR_INVALID_STATE;
}
return requestPause();
}
aaudio_result_t AudioStream::safeFlush() {
if (!isFlushSupported()) {
ALOGE("flush not supported for this stream");
return AAUDIO_ERROR_UNIMPLEMENTED;
}
std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
ALOGE("stream cannot be flushed from a callback!");
return AAUDIO_ERROR_INVALID_STATE;
}
aaudio_result_t result = AAudio_isFlushAllowed(getState());
if (result != AAUDIO_OK) {
return result;
}
return requestFlush();
}
aaudio_result_t AudioStream::safeStop() {
std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
ALOGE("stream cannot be stopped from a callback!");
return AAUDIO_ERROR_INVALID_STATE;
}
switch (getState()) {
// Proceed with stopping.
case AAUDIO_STREAM_STATE_STARTING:
case AAUDIO_STREAM_STATE_STARTED:
case AAUDIO_STREAM_STATE_DISCONNECTED:
break;
// Transition from one inactive state to another.
case AAUDIO_STREAM_STATE_OPEN:
case AAUDIO_STREAM_STATE_PAUSED:
case AAUDIO_STREAM_STATE_FLUSHED:
setState(AAUDIO_STREAM_STATE_STOPPED);
return AAUDIO_OK;
// Redundant?
case AAUDIO_STREAM_STATE_STOPPING:
case AAUDIO_STREAM_STATE_STOPPED:
return AAUDIO_OK;
// Don't interfere with transitional states or when closed.
case AAUDIO_STREAM_STATE_PAUSING:
case AAUDIO_STREAM_STATE_FLUSHING:
case AAUDIO_STREAM_STATE_CLOSING:
case AAUDIO_STREAM_STATE_CLOSED:
default:
ALOGW("requestStop() stream not running, state = %s",
AAudio_convertStreamStateToText(getState()));
return AAUDIO_ERROR_INVALID_STATE;
}
return requestStop();
}
@ -238,6 +315,7 @@ aaudio_result_t AudioStream::createThread(int64_t periodNanoseconds,
if (err != 0) {
return AAudioConvert_androidToAAudioResult(-errno);
} else {
// TODO Use AAudioThread or maybe AndroidThread
// Name the thread with an increasing index, "AAudio_#", for debugging.
static std::atomic<uint32_t> nextThreadIndex{1};
char name[16]; // max length for a pthread_name

@ -73,6 +73,24 @@ protected:
*/
virtual aaudio_result_t requestStart() = 0;
/**
* Check the state to see if Pause if currently legal.
*
* @param result pointer to return code
* @return true if OK to continue, if false then return result
*/
bool checkPauseStateTransition(aaudio_result_t *result);
virtual bool isFlushSupported() const {
// Only implement FLUSH for OUTPUT streams.
return false;
}
virtual bool isPauseSupported() const {
// Only implement PAUSE for OUTPUT streams.
return false;
}
virtual aaudio_result_t requestPause()
{
// Only implement this for OUTPUT streams.
@ -341,11 +359,13 @@ public:
return mPlayerBase->getResult();
}
// Pass pause request through PlayerBase for tracking.
aaudio_result_t systemPause() {
mPlayerBase->pause();
return mPlayerBase->getResult();
}
// Pass stop request through PlayerBase for tracking.
aaudio_result_t systemStop() {
mPlayerBase->stop();
return mPlayerBase->getResult();
@ -452,7 +472,14 @@ protected:
}
void setState(aaudio_stream_state_t state) {
mState = state;
if (mState == AAUDIO_STREAM_STATE_CLOSED) {
; // CLOSED is a final state
} else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
&& state != AAUDIO_STREAM_STATE_CLOSED) {
; // Once DISCONNECTED, we can only move to CLOSED state.
} else {
mState = state;
}
}
void setDeviceId(int32_t deviceId) {

@ -287,13 +287,8 @@ aaudio_result_t AudioStreamTrack::requestPause() {
if (mAudioTrack.get() == nullptr) {
ALOGE("requestPause() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
} else if (getState() != AAUDIO_STREAM_STATE_STARTING
&& getState() != AAUDIO_STREAM_STATE_STARTED) {
// TODO What about DISCONNECTED?
ALOGE("requestPause(), called when state is %s",
AAudio_convertStreamStateToText(getState()));
return AAUDIO_ERROR_INVALID_STATE;
}
setState(AAUDIO_STREAM_STATE_PAUSING);
mAudioTrack->pause();
mCallbackEnabled.store(false);
@ -308,10 +303,8 @@ aaudio_result_t AudioStreamTrack::requestFlush() {
if (mAudioTrack.get() == nullptr) {
ALOGE("requestFlush() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
} else if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
ALOGE("requestFlush() not paused");
return AAUDIO_ERROR_INVALID_STATE;
}
setState(AAUDIO_STREAM_STATE_FLUSHING);
incrementFramesRead(getFramesWritten() - getFramesRead());
mAudioTrack->flush();
@ -325,6 +318,7 @@ aaudio_result_t AudioStreamTrack::requestStop() {
ALOGE("requestStop() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
}
setState(AAUDIO_STREAM_STATE_STOPPING);
incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
mTimestampPosition.set(getFramesWritten());

@ -48,6 +48,16 @@ public:
aaudio_result_t requestFlush() override;
aaudio_result_t requestStop() override;
bool isFlushSupported() const override {
// Only implement FLUSH for OUTPUT streams.
return true;
}
bool isPauseSupported() const override {
// Only implement PAUSE for OUTPUT streams.
return true;
}
aaudio_result_t getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) override;

@ -441,3 +441,31 @@ int32_t AAudioProperty_getHardwareBurstMinMicros() {
}
return prop;
}
aaudio_result_t AAudio_isFlushAllowed(aaudio_stream_state_t state) {
aaudio_result_t result = AAUDIO_OK;
switch (state) {
// Proceed with flushing.
case AAUDIO_STREAM_STATE_OPEN:
case AAUDIO_STREAM_STATE_PAUSED:
case AAUDIO_STREAM_STATE_STOPPED:
case AAUDIO_STREAM_STATE_FLUSHED:
break;
// Transition from one inactive state to another.
case AAUDIO_STREAM_STATE_STARTING:
case AAUDIO_STREAM_STATE_STARTED:
case AAUDIO_STREAM_STATE_STOPPING:
case AAUDIO_STREAM_STATE_PAUSING:
case AAUDIO_STREAM_STATE_FLUSHING:
case AAUDIO_STREAM_STATE_CLOSING:
case AAUDIO_STREAM_STATE_CLOSED:
case AAUDIO_STREAM_STATE_DISCONNECTED:
default:
ALOGE("can only flush stream when PAUSED, OPEN or STOPPED, state = %s",
AAudio_convertStreamStateToText(state));
result = AAUDIO_ERROR_INVALID_STATE;
break;
}
return result;
}

@ -268,6 +268,14 @@ int32_t AAudioProperty_getMinimumSleepMicros();
*/
int32_t AAudioProperty_getHardwareBurstMinMicros();
/**
* Is flush allowed for the given state?
* @param state
* @return AAUDIO_OK if allowed or an error
*/
aaudio_result_t AAudio_isFlushAllowed(aaudio_stream_state_t state);
/**
* Try a function f until it returns true.
*

@ -271,11 +271,11 @@ aaudio_result_t AAudioServiceStreamBase::stopTimestampThread() {
}
aaudio_result_t AAudioServiceStreamBase::flush() {
if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
ALOGE("flush() stream not paused, state = %s",
AAudio_convertStreamStateToText(mState));
return AAUDIO_ERROR_INVALID_STATE;
aaudio_result_t result = AAudio_isFlushAllowed(getState());
if (result != AAUDIO_OK) {
return result;
}
// Data will get flushed when the client receives the FLUSHED event.
sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
setState(AAUDIO_STREAM_STATE_FLUSHED);

Loading…
Cancel
Save