OSDN Git Service

aaudio: prevent noise upon disconnect
authorPhil Burk <philburk@google.com>
Wed, 6 Sep 2017 21:36:11 +0000 (14:36 -0700)
committerPhil Burk <philburk@google.com>
Mon, 11 Sep 2017 19:20:56 +0000 (12:20 -0700)
Also clean up the state machine handling in the data callback.
Prevent error callback sometimes being called twice for the same error.

Bug: 63342351
Bug: 63087953
Test: add sleep(2) to write_sine_callback.cpp before the requestStop()
Change-Id: I27737bcb0371052741f50bda9f65c5994ccf6fd9

media/libaaudio/src/legacy/AudioStreamLegacy.cpp
media/libaaudio/src/legacy/AudioStreamLegacy.h
media/libaaudio/src/legacy/AudioStreamRecord.cpp
media/libaaudio/src/legacy/AudioStreamTrack.cpp
media/libaaudio/src/utility/AAudioUtilities.h

index a59fc6f..ee2504d 100644 (file)
@@ -31,7 +31,8 @@ using namespace android;
 using namespace aaudio;
 
 AudioStreamLegacy::AudioStreamLegacy()
-        : AudioStream(), mDeviceCallback(new StreamDeviceCallback(this)) {
+        : AudioStream()
+        , mDeviceCallback(new StreamDeviceCallback(this)) {
 }
 
 AudioStreamLegacy::~AudioStreamLegacy() {
@@ -78,18 +79,20 @@ int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes
 void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) {
     aaudio_data_callback_result_t callbackResult;
 
-    if (!mCallbackEnabled.load()) {
-        return;
-    }
-
     switch (opcode) {
         case AAUDIO_CALLBACK_OPERATION_PROCESS_DATA: {
-            if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
-                // Note that this code assumes an AudioTrack::Buffer is the same as
-                // AudioRecord::Buffer
-                // TODO define our own AudioBuffer and pass it from the subclasses.
-                AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
-                if (audioBuffer->frameCount == 0) return;
+            checkForDisconnectRequest();
+
+            // Note that this code assumes an AudioTrack::Buffer is the same as
+            // AudioRecord::Buffer
+            // TODO define our own AudioBuffer and pass it from the subclasses.
+            AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
+            if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED || !mCallbackEnabled.load()) {
+                audioBuffer->size = 0; // silence the buffer
+            } else {
+                if (audioBuffer->frameCount == 0) {
+                    return;
+                }
 
                 // If the caller specified an exact size then use a block size adapter.
                 if (mBlockAdapter != nullptr) {
@@ -107,26 +110,19 @@ void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode
                     audioBuffer->size = 0;
                 }
 
-                if (updateStateMachine() == AAUDIO_OK) {
-                    break; // don't fall through
+                if (updateStateMachine() != AAUDIO_OK) {
+                    forceDisconnect();
+                    mCallbackEnabled.store(false);
                 }
             }
         }
-        /// FALL THROUGH
+            break;
 
         // Stream got rerouted so we disconnect.
-        case AAUDIO_CALLBACK_OPERATION_DISCONNECTED: {
-            setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+        case AAUDIO_CALLBACK_OPERATION_DISCONNECTED:
             ALOGD("processCallbackCommon() stream disconnected");
-            if (getErrorCallbackProc() != nullptr) {
-                (*getErrorCallbackProc())(
-                        (AAudioStream *) this,
-                        getErrorCallbackUserData(),
-                        AAUDIO_ERROR_DISCONNECTED
-                        );
-            }
+            forceDisconnect();
             mCallbackEnabled.store(false);
-        }
             break;
 
         default:
@@ -134,6 +130,30 @@ void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode
     }
 }
 
+
+
+void AudioStreamLegacy::checkForDisconnectRequest() {
+    if (mRequestDisconnect.isRequested()) {
+        ALOGD("checkForDisconnectRequest() mRequestDisconnect acknowledged");
+        forceDisconnect();
+        mRequestDisconnect.acknowledge();
+        mCallbackEnabled.store(false);
+    }
+}
+
+void AudioStreamLegacy::forceDisconnect() {
+    if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
+        setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+        if (getErrorCallbackProc() != nullptr) {
+            (*getErrorCallbackProc())(
+                    (AAudioStream *) this,
+                    getErrorCallbackUserData(),
+                    AAUDIO_ERROR_DISCONNECTED
+            );
+        }
+    }
+}
+
 aaudio_result_t AudioStreamLegacy::getBestTimestamp(clockid_t clockId,
                                                    int64_t *framePosition,
                                                    int64_t *timeNanoseconds,
@@ -175,15 +195,18 @@ void AudioStreamLegacy::onAudioDeviceUpdate(audio_port_handle_t deviceId)
     ALOGD("onAudioDeviceUpdate() deviceId %d", (int)deviceId);
     if (getDeviceId() != AAUDIO_UNSPECIFIED && getDeviceId() != deviceId &&
             getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
-        setState(AAUDIO_STREAM_STATE_DISCONNECTED);
-        // if we have a data callback and the stream is active, send the error callback from
-        // data callback thread when it sees the DISCONNECTED state
-        if (!isDataCallbackActive() && getErrorCallbackProc() != nullptr) {
-            (*getErrorCallbackProc())(
-                    (AAudioStream *) this,
-                    getErrorCallbackUserData(),
-                    AAUDIO_ERROR_DISCONNECTED
-                    );
+        // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
+        // If we have a data callback and the stream is active, then ask the data callback
+        // to DISCONNECT and call the error callback.
+        if (isDataCallbackActive()) {
+            ALOGD("onAudioDeviceUpdate() request DISCONNECT in data callback due to device change");
+            // If the stream is stopped before the data callback has a chance to handle the
+            // request then the requestStop() and requestPause() methods will handle it after
+            // the callback has stopped.
+            mRequestDisconnect.request();
+        } else {
+            ALOGD("onAudioDeviceUpdate() DISCONNECT the stream now");
+            forceDisconnect();
         }
     }
     setDeviceId(deviceId);
index 66c216c..7e28579 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "AudioStream.h"
 #include "AAudioLegacy.h"
+#include "utility/AAudioUtilities.h"
 #include "utility/FixedBlockAdapter.h"
 
 namespace aaudio {
@@ -111,6 +112,10 @@ protected:
 
     void onAudioDeviceUpdate(audio_port_handle_t deviceId);
 
+    void checkForDisconnectRequest();
+
+    void forceDisconnect();
+
     void onStart() { mCallbackEnabled.store(true); }
     void onStop() { mCallbackEnabled.store(false); }
 
@@ -130,6 +135,8 @@ protected:
     aaudio_wrapping_frames_t   mPositionWhenStarting = 0;
     int32_t                    mCallbackBufferSize = 0;
     const android::sp<StreamDeviceCallback>   mDeviceCallback;
+
+    AtomicRequestor            mRequestDisconnect;
 };
 
 } /* namespace aaudio */
index 4d6fb2a..bc6e60c 100644 (file)
@@ -238,6 +238,7 @@ aaudio_result_t AudioStreamRecord::requestStop() {
     mAudioRecord->stop();
     mFramesRead.reset32();
     mTimestampPosition.reset32();
+    checkForDisconnectRequest();
     return AAUDIO_OK;
 }
 
index 09c078c..5e4446f 100644 (file)
@@ -261,6 +261,7 @@ aaudio_result_t AudioStreamTrack::requestPause() {
     onStop();
     setState(AAUDIO_STREAM_STATE_PAUSING);
     mAudioTrack->pause();
+    checkForDisconnectRequest();
     status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
     if (err != OK) {
         return AAudioConvert_androidToAAudioResult(err);
@@ -300,6 +301,7 @@ aaudio_result_t AudioStreamTrack::requestStop() {
     mFramesWritten.reset32();
     mTimestampPosition.reset32();
     mAudioTrack->stop();
+    checkForDisconnectRequest();
     return AAUDIO_OK;
 }
 
index f56be32..3afa976 100644 (file)
@@ -346,15 +346,18 @@ private:
  */
 class AtomicRequestor {
 public:
+
+    __attribute__((no_sanitize("integer")))
     void request() {
-        // TODO handle overflows, very unlikely
         mRequested++;
     }
 
+    __attribute__((no_sanitize("integer")))
     bool isRequested() {
-        return mRequested.load() > mAcknowledged.load();
+        return (mRequested.load() - mAcknowledged.load()) > 0;
     }
 
+    __attribute__((no_sanitize("integer")))
     void acknowledge() {
         mAcknowledged++;
     }