Loading media/java/android/media/RemoteControlClient.java +118 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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. * Loading Loading @@ -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); } } } Loading Loading @@ -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*/); } } } Loading Loading @@ -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) { Loading Loading @@ -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"); } Loading Loading @@ -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); } } } Loading
media/java/android/media/RemoteControlClient.java +118 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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. * Loading Loading @@ -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); } } } Loading Loading @@ -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*/); } } } Loading Loading @@ -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) { Loading Loading @@ -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"); } Loading Loading @@ -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); } } }