google / oboe

Oboe is a C++ library that makes it easy to build high-performance audio apps on Android.
Apache License 2.0
3.71k stars 569 forks source link

callbacks stop working if rapidly paused then unpaused in quick succession (bug in Android P (9) itself) #810

Closed mgood7123 closed 4 years ago

mgood7123 commented 4 years ago

see https://github.com/google/oboe/issues/569 and https://github.com/google/oboe/issues/396

same setup except without root and without Viper4Android installed

this may be related to rapidly opening and closing the streams in a multi threaded environment such as one calllback overlaps with the other

im not sure

im currently testing if the problem persists if i keep the stream open during the pausing and unpausing instead of opening on play and closing on pause

as this will rule out any problems with Oboe callbacks themselves

ei

on play start the oboe::AudioStream *stream if it is not already started then stream audio to the engine

on pause stream silence

...

and it seems to have fixed the problem, even when my phone extreme lags due to 0% battery while charging (the battery is being drained faster than it can be recharged by the current power source)

bool STREAM_STARTED = false; // we only use a single stream for now

/*
 * IMPORTANT: avoid starting and stopping the `oboe::AudioStream *stream` rapidly
 * exact reason appears to due to a bug in the AAudio Legacy path for Android P (9),
 *
 * W/AudioStreamLegacy: processCallbackCommon() stopping because callback disabled
 * E/AudioTrack: EVENT_MORE_DATA requested 256 bytes but callback returned -1 bytes
 *
 * see https://github.com/google/oboe/issues/396 :
 *
 * When I returned oboe::DataCallbackResult::Stop in the input stream callback, the callback stopped
 * being called as expected, but the stream state remained in Stopping and never changed to Stopped.
 *
 * In this case, the callback stopped, but the stream state remained in Started with Errors
 * W/AudioStreamLegacy: processCallbackCommon() callback requested stop, fake an error and
 * E/AudioRecord: EVENT_MORE_DATA requested 384 bytes but callback returned -1 bytes
 *
 * And when I tried to restart the stream by calling requestStart(), it didn't work.
 *
 * That is due to a bug in the AAudio Legacy path for Android P (9).
 *
 * This and other bugs related to DataCallbackResult::Stop have been fixed for a future Android
 * release.
*/

// TODO: guard against playing without first loading an audio track
//  in multi track situations such as when using the Mixer, simply relying on currentAudioTrack is
//  unreliable

bool Oboe_Stream_Start() {
    LOGW("Oboe_Init: requesting Start");
    oboe::Result result = stream->requestStart();
    LOGW("Oboe_Init: requested Start");
    if (result != oboe::Result::OK) {
        LOGE("Oboe_Play: Failed to start AudioStream . Error: %s",
             oboe::convertToText(result));
        return false;
    }
    STREAM_STARTED = true;
    return true;
}

bool Oboe_Stream_Stop() {
    LOGW("Oboe_Init: requesting Stop");
    oboe::Result result = stream->requestStop();
    LOGW("Oboe_Init: requested Stop");
    if (result != oboe::Result::OK) {
        LOGE("Oboe_Play: Failed to stop AudioStream . Error: %s",
             oboe::convertToText(result));
        return false;
    }
    STREAM_STARTED = false;
    return true;
}

NATIVE(void, Oboe, Play)(JNIEnv *env, jobject type) {
    if (currentAudioTrack != NULL) {
        if (!STREAM_STARTED) Oboe_Stream_Start();
        currentAudioTrack->setPlaying(true);
    }
}

NATIVE(void, Oboe, Pause)(JNIEnv *env, jobject type) {
    if (currentAudioTrack != NULL) {
        currentAudioTrack->setPlaying(false);
    }
}

NATIVE(void, Oboe, Stop)(JNIEnv *env, jobject type) {
    if (currentAudioTrack != NULL) {
        if (STREAM_STARTED) Oboe_Stream_Stop();
        currentAudioTrack->setPlaying(false);
        currentAudioTrack->resetPlayHead();
    }
}
philburk commented 4 years ago

Rapidly closing and opening a stream will expose known race conditions in AudioFlinger. We do not recommend that.

if i keep the stream open during the pausing and unpausing instead of opening on play and closing on pause and it seems to have fixed the problem,

Yes, it is better to keep the stream open and just call pause() and start(). Note that if your app is pushed to the background by another app then we recommend closing the stream to free up the audio resources. Likewise, if the user pauses the stream for a long time then you should probably close it.

mgood7123 commented 4 years ago

Thanks

On Wed, Apr 1, 2020, 9:57 AM Phil Burk notifications@github.com wrote:

Rapidly closing and opening a stream will expose known race conditions in AudioFlinger. We do not recommend that.

if i keep the stream open during the pausing and unpausing instead of opening on play and closing on pause and it seems to have fixed the problem,

Yes, it is better to keep the stream open and just call pause() and start(). Note that if your app is pushed to the background by another app then we recommend closing the stream to free up the audio resources. Likewise, if the user pauses the stream for a long time then you should probably close it.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/google/oboe/issues/810#issuecomment-606948459, or unsubscribe https://github.com/notifications/unsubscribe-auth/AGLITHY7VLC4SILE4GJBQITRKJ7M3ANCNFSM4LNQ6MJQ .