Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb23d7c8 authored by Eric Laurent's avatar Eric Laurent
Browse files

AudioTrack: implement gapless transition for offload mode

Bug: 119775911
Test: test track transition with offload playback cmd line app
Change-Id: Ib105f65eb62845feceff45fbda9bec165e219841
parent 2c402057
Loading
Loading
Loading
Loading
+0 −13
Original line number Diff line number Diff line
@@ -1301,18 +1301,6 @@ static void android_media_AudioTrack_set_delay_padding(JNIEnv *env, jobject thi
    lpTrack->setParameters(param.toString());
}

static void android_media_AudioTrack_set_eos(JNIEnv *env,  jobject thiz) {
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    if (lpTrack == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
                          "AudioTrack not initialized");
        return;
    }
    AudioParameter param = AudioParameter();
    param.addInt(String8("EOS"), 1);
    lpTrack->setParameters(param.toString());
}

// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -1389,7 +1377,6 @@ static const JNINativeMethod gMethods[] = {
    {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
    {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
    {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding},
    {"native_set_eos",        "()V",    (void *)android_media_AudioTrack_set_eos},
};


+127 −9
Original line number Diff line number Diff line
@@ -101,6 +101,20 @@ public class AudioTrack extends PlayerBase
    public static final int PLAYSTATE_PAUSED  = 2;  // matches SL_PLAYSTATE_PAUSED
    /** indicates AudioTrack state is playing */
    public static final int PLAYSTATE_PLAYING = 3;  // matches SL_PLAYSTATE_PLAYING
    /**
      * @hide
      * indicates AudioTrack state is stopping waiting for NATIVE_EVENT_STREAM_END to
      * transition to PLAYSTATE_STOPPED.
      * Only valid for offload mode.
      */
    private static final int PLAYSTATE_STOPPING = 4;
    /**
      * @hide
      * indicates AudioTrack state is paused from stopping state. Will transition to
      * PLAYSTATE_STOPPING if play() is called.
      * Only valid for offload mode.
      */
    private static final int PLAYSTATE_PAUSED_STOPPING = 5;

    // keep these values in sync with android_media_AudioTrack.cpp
    /**
@@ -303,6 +317,14 @@ public class AudioTrack extends PlayerBase
     * One of PLAYSTATE_STOPPED, PLAYSTATE_PAUSED, or PLAYSTATE_PLAYING.
     */
    private int mPlayState = PLAYSTATE_STOPPED;

    /**
     * Indicates that we are expecting an end of stream callback following a call
     * to setOffloadEndOfStream() in a gapless track transition context. The native track
     * will be restarted automatically.
     */
    private boolean mOffloadEosPending = false;

    /**
     * Lock to ensure mPlayState updates reflect the actual state of the object.
     */
@@ -1073,6 +1095,10 @@ public class AudioTrack extends PlayerBase
     * Declares that the last write() operation on this track provided the last buffer of this
     * stream.
     * After the end of stream, previously set padding and delay values are ignored.
     * Can only be called only if the AudioTrack is opened in offload mode
     * {@see Builder#setOffloadedPlayback(boolean)}.
     * Can only be called only if the AudioTrack is in state {@link #PLAYSTATE_PLAYING}
     * {@see #getPlaystate()}.
     * Use this method in the same thread as any write() operation.
     */
    public void setOffloadEndOfStream() {
@@ -1082,7 +1108,20 @@ public class AudioTrack extends PlayerBase
        if (mState == STATE_UNINITIALIZED) {
            throw new IllegalStateException("Uninitialized track");
        }
        native_set_eos();
        if (mPlayState != PLAYSTATE_PLAYING) {
            throw new IllegalStateException("EOS not supported if not playing");
        }
        synchronized (mStreamEventCbLock) {
            if (mStreamEventCbInfoList.size() == 0) {
                throw new IllegalStateException("EOS not supported without StreamEventCallback");
            }
        }

        synchronized (mPlayStateLock) {
            native_stop();
            mOffloadEosPending = true;
            mPlayState = PLAYSTATE_STOPPING;
        }
    }

    /**
@@ -1366,7 +1405,11 @@ public class AudioTrack extends PlayerBase
        }
        baseRelease();
        native_release();
        synchronized (mPlayStateLock) {
            mState = STATE_UNINITIALIZED;
            mPlayState = PLAYSTATE_STOPPED;
            mPlayStateLock.notify();
        }
    }

    @Override
@@ -1525,9 +1568,16 @@ public class AudioTrack extends PlayerBase
     */
    public int getPlayState() {
        synchronized (mPlayStateLock) {
            switch (mPlayState) {
                case PLAYSTATE_STOPPING:
                    return PLAYSTATE_PLAYING;
                case PLAYSTATE_PAUSED_STOPPING:
                    return PLAYSTATE_PAUSED;
                default:
                    return mPlayState;
            }
        }
    }


    /**
@@ -2260,7 +2310,12 @@ public class AudioTrack extends PlayerBase
        synchronized(mPlayStateLock) {
            baseStart();
            native_start();
            if (mPlayState == PLAYSTATE_PAUSED_STOPPING) {
                mPlayState = PLAYSTATE_STOPPING;
            } else {
                mPlayState = PLAYSTATE_PLAYING;
                mOffloadEosPending = false;
            }
        }
    }

@@ -2282,9 +2337,15 @@ public class AudioTrack extends PlayerBase
        synchronized(mPlayStateLock) {
            native_stop();
            baseStop();
            if (mOffloaded && mPlayState != PLAYSTATE_PAUSED_STOPPING) {
                mPlayState = PLAYSTATE_STOPPING;
            } else {
                mPlayState = PLAYSTATE_STOPPED;
                mOffloadEosPending = false;
                mAvSyncHeader = null;
                mAvSyncBytesRemaining = 0;
                mPlayStateLock.notify();
            }
        }
    }

@@ -2305,9 +2366,13 @@ public class AudioTrack extends PlayerBase
        synchronized(mPlayStateLock) {
            native_pause();
            basePause();
            if (mPlayState == PLAYSTATE_STOPPING) {
                mPlayState = PLAYSTATE_PAUSED_STOPPING;
            } else {
                mPlayState = PLAYSTATE_PAUSED;
            }
        }
    }


    //---------------------------------------------------------
@@ -2434,6 +2499,9 @@ public class AudioTrack extends PlayerBase
            return ERROR_BAD_VALUE;
        }

        if (!blockUntilOffloadDrain(writeMode)) {
            return 0;
        }

        final int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
                writeMode == WRITE_BLOCKING);
@@ -2544,6 +2612,10 @@ public class AudioTrack extends PlayerBase
            return ERROR_BAD_VALUE;
        }

        if (!blockUntilOffloadDrain(writeMode)) {
            return 0;
        }

        final int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat,
                writeMode == WRITE_BLOCKING);

@@ -2632,6 +2704,10 @@ public class AudioTrack extends PlayerBase
            return ERROR_BAD_VALUE;
        }

        if (!blockUntilOffloadDrain(writeMode)) {
            return 0;
        }

        final int ret = native_write_float(audioData, offsetInFloats, sizeInFloats, mAudioFormat,
                writeMode == WRITE_BLOCKING);

@@ -2706,6 +2782,10 @@ public class AudioTrack extends PlayerBase
            return ERROR_BAD_VALUE;
        }

        if (!blockUntilOffloadDrain(writeMode)) {
            return 0;
        }

        int ret = 0;
        if (audioData.isDirect()) {
            ret = native_write_native_bytes(audioData,
@@ -2790,6 +2870,10 @@ public class AudioTrack extends PlayerBase
            return ERROR_BAD_VALUE;
        }

        if (!blockUntilOffloadDrain(writeMode)) {
            return 0;
        }

        // create timestamp header if none exists
        if (mAvSyncHeader == null) {
            mAvSyncHeader = ByteBuffer.allocate(mOffset);
@@ -2859,6 +2943,25 @@ public class AudioTrack extends PlayerBase
        return native_reload_static();
    }

    /**
     * When an AudioTrack in offload mode is in STOPPING play state, wait until event STREAM_END is
     * received if blocking write or return with 0 frames written if non blocking mode.
     */
    private boolean blockUntilOffloadDrain(int writeMode) {
        synchronized (mPlayStateLock) {
            while (mPlayState == PLAYSTATE_STOPPING || mPlayState == PLAYSTATE_PAUSED_STOPPING) {
                if (writeMode == WRITE_NON_BLOCKING) {
                    return false;
                }
                try {
                    mPlayStateLock.wait();
                } catch (InterruptedException e) {
                }
            }
            return true;
        }
    }

    //--------------------------------------------------------------------------
    // Audio effects management
    //--------------------
@@ -3293,6 +3396,22 @@ public class AudioTrack extends PlayerBase
        public void handleMessage(Message msg) {
            final LinkedList<StreamEventCbInfo> cbInfoList;
            synchronized (mStreamEventCbLock) {
                if (msg.what == NATIVE_EVENT_STREAM_END) {
                    synchronized (mPlayStateLock) {
                        if (mPlayState == PLAYSTATE_STOPPING) {
                            if (mOffloadEosPending) {
                                native_start();
                                mPlayState = PLAYSTATE_PLAYING;
                            } else {
                                mAvSyncHeader = null;
                                mAvSyncBytesRemaining = 0;
                                mPlayState = PLAYSTATE_STOPPED;
                            }
                            mOffloadEosPending = false;
                            mPlayStateLock.notify();
                        }
                    }
                }
                if (mStreamEventCbInfoList.size() == 0) {
                    return;
                }
@@ -3560,7 +3679,6 @@ public class AudioTrack extends PlayerBase
    private native int native_getPortId();

    private native void native_set_delay_padding(int delayInFrames, int paddingInFrames);
    private native void native_set_eos();

    //---------------------------------------------------------
    // Utility methods