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

* commit '91695a0cdc0aaab3d3f939d9adb8855938b4f69d':
  Anti-drift in RCC playback position
This commit is contained in:
Jean-Michel Trivi
2013-04-19 08:57:43 -07:00
committed by Android Git Automerger

View File

@@ -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);
}
}
}