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

Commit b05c3f5e authored by Jean-Michel Trivi's avatar Jean-Michel Trivi Committed by Android Git Automerger
Browse files

am 0068e00d: am 91695a0c: Merge "Anti-drift in RCC playback position" into jb-mr2-dev

* commit '0068e00d':
  Anti-drift in RCC playback position
parents 2770caaf 0068e00d
Loading
Loading
Loading
Loading
+118 −1
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ import java.util.Iterator;
public class RemoteControlClient
{
    private final static String TAG = "RemoteControlClient";
    private final static boolean DEBUG = false;

    /**
     * Playback state of a RemoteControlClient which is stopped.
@@ -219,7 +220,7 @@ public class RemoteControlClient
    public final static int PLAYBACKINFO_USES_STREAM = 5;

    //==========================================
    // Public flags for the supported transport control capabililities
    // Public flags for the supported transport control capabilities
    /**
     * Flag indicating a RemoteControlClient makes use of the "previous" media key.
     *
@@ -642,6 +643,57 @@ public class RemoteControlClient
                sendPlaybackState_syncCacheLock();
                // update AudioService
                sendAudioServiceNewPlaybackState_syncCacheLock();

                // handle automatic playback position refreshes
                if (mEventHandler == null) {
                    return;
                }
                mEventHandler.removeMessages(MSG_POSITION_DRIFT_CHECK);
                if (timeInMs == PLAYBACK_POSITION_INVALID) {
                    // this playback state refresh has no known playback position, it's no use
                    // trying to see if there is any drift at this point
                    // (this also bypasses this mechanism for older apps that use the old
                    //  setPlaybackState(int) API)
                    return;
                }
                if (playbackPositionShouldMove(mPlaybackState)) {
                    // playback position moving, schedule next position drift check
                    mEventHandler.sendMessageDelayed(
                            mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
                            getCheckPeriodFromSpeed(playbackSpeed));
                }
            }
        }
    }

    private void onPositionDriftCheck() {
        if (DEBUG) { Log.d(TAG, "onPositionDriftCheck()"); }
        synchronized(mCacheLock) {
            if ((mEventHandler == null) || (mPositionProvider == null)) {
                return;
            }
            if ((mPlaybackPositionMs == PLAYBACK_POSITION_INVALID) || (mPlaybackSpeed == 0.0f)) {
                if (DEBUG) { Log.d(TAG, " no position or 0 speed, no check needed"); }
                return;
            }
            long estPos = mPlaybackPositionMs + (long)
                    ((SystemClock.elapsedRealtime() - mPlaybackStateChangeTimeMs) / mPlaybackSpeed);
            long actPos = mPositionProvider.onGetPlaybackPosition();
            if (actPos >= 0) {
                if (Math.abs(estPos - actPos) > POSITION_DRIFT_MAX_MS) {
                    // drift happened, report the new position
                    if (DEBUG) { Log.w(TAG, " drift detected: actual=" +actPos +"  est=" +estPos); }
                    setPlaybackState(mPlaybackState, actPos, mPlaybackSpeed);
                } else {
                    if (DEBUG) { Log.d(TAG, " no drift: actual=" + actPos +"  est=" + estPos); }
                    // no drift, schedule the next drift check
                    mEventHandler.sendMessageDelayed(
                            mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
                            getCheckPeriodFromSpeed(mPlaybackSpeed));
                }
            } else {
                // invalid position (negative value), can't check for drift
                mEventHandler.removeMessages(MSG_POSITION_DRIFT_CHECK);
            }
        }
    }
@@ -746,6 +798,14 @@ public class RemoteControlClient
                // tell RCDs that this RCC's playback position capabilities have changed
                sendTransportControlInfo_syncCacheLock();
            }
            if ((mPositionProvider != null) && (mEventHandler != null)
                    && playbackPositionShouldMove(mPlaybackState)) {
                // playback position is already moving, but now we have a position provider,
                // so schedule a drift check right now
                mEventHandler.sendMessageDelayed(
                        mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
                        0 /*check now*/);
            }
        }
    }

@@ -1099,6 +1159,7 @@ public class RemoteControlClient
    private final static int MSG_UNPLUG_DISPLAY = 8;
    private final static int MSG_UPDATE_DISPLAY_ARTWORK_SIZE = 9;
    private final static int MSG_SEEK_TO = 10;
    private final static int MSG_POSITION_DRIFT_CHECK = 11;

    private class EventHandler extends Handler {
        public EventHandler(RemoteControlClient rcc, Looper looper) {
@@ -1146,6 +1207,9 @@ public class RemoteControlClient
                case MSG_SEEK_TO:
                    onSeekTo(msg.arg1, ((Long)msg.obj).longValue());
                    break;
                case MSG_POSITION_DRIFT_CHECK:
                    onPositionDriftCheck();
                    break;
                default:
                    Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
            }
@@ -1440,4 +1504,57 @@ public class RemoteControlClient
            return false;
        }
    }

    /**
     * Returns whether, for the given playback state, the playback position is expected to
     * be changing.
     * @param playstate the playback state to evaluate
     * @return true during any form of playback, false if it's not playing anything while in this
     *     playback state
     */
    private static boolean playbackPositionShouldMove(int playstate) {
        switch(playstate) {
            case PLAYSTATE_STOPPED:
            case PLAYSTATE_PAUSED:
            case PLAYSTATE_BUFFERING:
            case PLAYSTATE_ERROR:
            case PLAYSTATE_SKIPPING_FORWARDS:
            case PLAYSTATE_SKIPPING_BACKWARDS:
                return false;
            case PLAYSTATE_PLAYING:
            case PLAYSTATE_FAST_FORWARDING:
            case PLAYSTATE_REWINDING:
            default:
                return true;
        }
    }

    /**
     * Period for playback position drift checks, 15s when playing at 1x or slower.
     */
    private final static long POSITION_REFRESH_PERIOD_PLAYING_MS = 15000;
    /**
     * Minimum period for playback position drift checks, never more often when every 2s, when
     * fast forwarding or rewinding.
     */
    private final static long POSITION_REFRESH_PERIOD_MIN_MS = 2000;
    /**
     * The value above which the difference between client-reported playback position and
     * estimated position is considered a drift.
     */
    private final static long POSITION_DRIFT_MAX_MS = 500;
    /**
     * Compute the period at which the estimated playback position should be compared against the
     * actual playback position. Is a funciton of playback speed.
     * @param speed 1.0f is normal playback speed
     * @return the period in ms
     */
    private static long getCheckPeriodFromSpeed(float speed) {
        if (Math.abs(speed) <= 1.0f) {
            return POSITION_REFRESH_PERIOD_PLAYING_MS;
        } else {
            return Math.max((long)(POSITION_REFRESH_PERIOD_PLAYING_MS / Math.abs(speed)),
                    POSITION_REFRESH_PERIOD_MIN_MS);
        }
    }
}