Loading media/java/android/media/MediaPlayer.java +382 −1 Original line number Diff line number Diff line Loading @@ -39,6 +39,8 @@ import android.graphics.Bitmap; import android.graphics.SurfaceTexture; import android.media.AudioManager; import android.media.MediaFormat; import android.media.MediaTimeProvider; import android.media.MediaTimeProvider.OnMediaTimeListener; import android.media.SubtitleData; import java.io.File; Loading @@ -48,6 +50,7 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.util.Map; import java.util.Set; import java.util.Vector; import java.lang.ref.WeakReference; /** Loading Loading @@ -590,6 +593,8 @@ public class MediaPlayer mEventHandler = null; } mTimeProvider = new TimeProvider(this); /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ Loading Loading @@ -1337,6 +1342,8 @@ public class MediaPlayer mOnInfoListener = null; mOnVideoSizeChangedListener = null; mOnTimedTextListener = null; mTimeProvider.close(); mTimeProvider = null; mOnSubtitleDataListener = null; _release(); } Loading Loading @@ -1914,11 +1921,21 @@ public class MediaPlayer private static final int MEDIA_BUFFERING_UPDATE = 3; private static final int MEDIA_SEEK_COMPLETE = 4; private static final int MEDIA_SET_VIDEO_SIZE = 5; private static final int MEDIA_STARTED = 6; private static final int MEDIA_PAUSED = 7; private static final int MEDIA_STOPPED = 8; private static final int MEDIA_TIMED_TEXT = 99; private static final int MEDIA_ERROR = 100; private static final int MEDIA_INFO = 200; private static final int MEDIA_SUBTITLE_DATA = 201; private TimeProvider mTimeProvider; /** @hide */ public MediaTimeProvider getMediaTimeProvider() { return mTimeProvider; } private class EventHandler extends Handler { private MediaPlayer mMediaPlayer; Loading Loading @@ -1946,14 +1963,31 @@ public class MediaPlayer stayAwake(false); return; case MEDIA_STOPPED: if (mTimeProvider != null) { mTimeProvider.onStopped(); } break; case MEDIA_STARTED: case MEDIA_PAUSED: if (mTimeProvider != null) { mTimeProvider.onPaused(msg.what == MEDIA_PAUSED); } break; case MEDIA_BUFFERING_UPDATE: if (mOnBufferingUpdateListener != null) mOnBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1); return; case MEDIA_SEEK_COMPLETE: if (mOnSeekCompleteListener != null) if (mOnSeekCompleteListener != null) { mOnSeekCompleteListener.onSeekComplete(mMediaPlayer); } if (mTimeProvider != null) { mTimeProvider.onSeekComplete(mMediaPlayer); } return; case MEDIA_SET_VIDEO_SIZE: Loading Loading @@ -2496,4 +2530,351 @@ public class MediaPlayer } private native void updateProxyConfig(ProxyProperties props); /** @hide */ static class TimeProvider implements MediaPlayer.OnSeekCompleteListener, MediaTimeProvider { private static final String TAG = "MTP"; private static final long MAX_NS_WITHOUT_POSITION_CHECK = 5000000000L; private static final long MAX_EARLY_CALLBACK_US = 1000; private static final long TIME_ADJUSTMENT_RATE = 2; /* meaning 1/2 */ private long mLastTimeUs = 0; private MediaPlayer mPlayer; private boolean mPaused = true; private boolean mStopped = true; private long mLastReportedTime; private long mTimeAdjustment; // since we are expecting only a handful listeners per stream, there is // no need for log(N) search performance private MediaTimeProvider.OnMediaTimeListener mListeners[]; private long mTimes[]; private long mLastNanoTime; private Handler mEventHandler; private boolean mRefresh = false; private boolean mPausing = false; private static final int NOTIFY = 1; private static final int NOTIFY_TIME = 0; private static final int REFRESH_AND_NOTIFY_TIME = 1; private static final int NOTIFY_STOP = 2; private static final int NOTIFY_SEEK = 3; /** @hide */ public boolean DEBUG = false; public TimeProvider(MediaPlayer mp) { mPlayer = mp; try { getCurrentTimeUs(true, false); } catch (IllegalStateException e) { // we assume starting position mRefresh = true; } mEventHandler = new EventHandler(); mListeners = new MediaTimeProvider.OnMediaTimeListener[0]; mTimes = new long[0]; mLastTimeUs = 0; mTimeAdjustment = 0; } private void scheduleNotification(int type, long delayUs) { if (DEBUG) Log.v(TAG, "scheduleNotification " + type + " in " + delayUs); mEventHandler.removeMessages(NOTIFY); Message msg = mEventHandler.obtainMessage(NOTIFY, type, 0); mEventHandler.sendMessageDelayed(msg, (int) (delayUs / 1000)); } /** @hide */ public void close() { mEventHandler.removeMessages(NOTIFY); } /** @hide */ public void onPaused(boolean paused) { synchronized(this) { if (DEBUG) Log.d(TAG, "onPaused: " + paused); if (mStopped) { // handle as seek if we were stopped mStopped = false; scheduleNotification(NOTIFY_SEEK, 0 /* delay */); } else { mPausing = paused; // special handling if player disappeared scheduleNotification(REFRESH_AND_NOTIFY_TIME, 0 /* delay */); } } } /** @hide */ public void onStopped() { synchronized(this) { if (DEBUG) Log.d(TAG, "onStopped"); mPaused = true; mStopped = true; scheduleNotification(NOTIFY_STOP, 0 /* delay */); } } /** @hide */ @Override public void onSeekComplete(MediaPlayer mp) { synchronized(this) { mStopped = false; scheduleNotification(NOTIFY_SEEK, 0 /* delay */); } } /** @hide */ public void onNewPlayer() { if (mRefresh) { synchronized(this) { scheduleNotification(NOTIFY_SEEK, 0 /* delay */); } } } private synchronized void notifySeek() { try { long timeUs = getCurrentTimeUs(true, false); if (DEBUG) Log.d(TAG, "onSeekComplete at " + timeUs); for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) { if (listener == null) { break; } listener.onSeek(timeUs); } } catch (IllegalStateException e) { // we should not be there, but at least signal pause if (DEBUG) Log.d(TAG, "onSeekComplete but no player"); mPausing = true; // special handling if player disappeared notifyTimedEvent(false /* refreshTime */); } } private synchronized void notifyStop() { for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) { if (listener == null) { break; } listener.onStop(); } } private int registerListener(MediaTimeProvider.OnMediaTimeListener listener) { int i = 0; for (; i < mListeners.length; i++) { if (mListeners[i] == listener || mListeners[i] == null) { break; } } // new listener if (i >= mListeners.length) { MediaTimeProvider.OnMediaTimeListener[] newListeners = new MediaTimeProvider.OnMediaTimeListener[i + 1]; long[] newTimes = new long[i + 1]; System.arraycopy(mListeners, 0, newListeners, 0, mListeners.length); System.arraycopy(mTimes, 0, newTimes, 0, mTimes.length); mListeners = newListeners; mTimes = newTimes; } if (mListeners[i] == null) { mListeners[i] = listener; mTimes[i] = MediaTimeProvider.NO_TIME; } return i; } public void notifyAt( long timeUs, MediaTimeProvider.OnMediaTimeListener listener) { synchronized(this) { if (DEBUG) Log.d(TAG, "notifyAt " + timeUs); mTimes[registerListener(listener)] = timeUs; scheduleNotification(NOTIFY_TIME, 0 /* delay */); } } public void scheduleUpdate(MediaTimeProvider.OnMediaTimeListener listener) { synchronized(this) { if (DEBUG) Log.d(TAG, "scheduleUpdate"); int i = registerListener(listener); if (mStopped) { scheduleNotification(NOTIFY_STOP, 0 /* delay */); } else { mTimes[i] = 0; scheduleNotification(NOTIFY_TIME, 0 /* delay */); } } } public void cancelNotifications( MediaTimeProvider.OnMediaTimeListener listener) { synchronized(this) { int i = 0; for (; i < mListeners.length; i++) { if (mListeners[i] == listener) { System.arraycopy(mListeners, i + 1, mListeners, i, mListeners.length - i - 1); System.arraycopy(mTimes, i + 1, mTimes, i, mTimes.length - i - 1); mListeners[mListeners.length - 1] = null; mTimes[mTimes.length - 1] = NO_TIME; break; } else if (mListeners[i] == null) { break; } } scheduleNotification(NOTIFY_TIME, 0 /* delay */); } } private synchronized void notifyTimedEvent(boolean refreshTime) { // figure out next callback long nowUs; try { nowUs = getCurrentTimeUs(refreshTime, true); } catch (IllegalStateException e) { // assume we paused until new player arrives mRefresh = true; mPausing = true; // this ensures that call succeeds nowUs = getCurrentTimeUs(refreshTime, true); } long nextTimeUs = nowUs; if (DEBUG) { StringBuilder sb = new StringBuilder(); sb.append("notifyTimedEvent(").append(mLastTimeUs).append(" -> ") .append(nowUs).append(") from {"); boolean first = true; for (long time: mTimes) { if (time == NO_TIME) { continue; } if (!first) sb.append(", "); sb.append(time); first = false; } sb.append("}"); Log.d(TAG, sb.toString()); } Vector<MediaTimeProvider.OnMediaTimeListener> activatedListeners = new Vector<MediaTimeProvider.OnMediaTimeListener>(); for (int ix = 0; ix < mTimes.length; ix++) { if (mListeners[ix] == null) { break; } if (mTimes[ix] <= NO_TIME) { // ignore, unless we were stopped } else if (mTimes[ix] <= nowUs + MAX_EARLY_CALLBACK_US) { activatedListeners.add(mListeners[ix]); if (DEBUG) Log.d(TAG, "removed"); mTimes[ix] = NO_TIME; } else if (nextTimeUs == nowUs || mTimes[ix] < nextTimeUs) { nextTimeUs = mTimes[ix]; } } if (nextTimeUs > nowUs && !mPaused) { // schedule callback at nextTimeUs if (DEBUG) Log.d(TAG, "scheduling for " + nextTimeUs + " and " + nowUs); scheduleNotification(NOTIFY_TIME, nextTimeUs - nowUs); } else { mEventHandler.removeMessages(NOTIFY); // no more callbacks } for (MediaTimeProvider.OnMediaTimeListener listener: activatedListeners) { listener.onTimedEvent(nowUs); } } private long getEstimatedTime(long nanoTime, boolean monotonic) { if (mPaused) { mLastReportedTime = mLastTimeUs + mTimeAdjustment; } else { long timeSinceRead = (nanoTime - mLastNanoTime) / 1000; mLastReportedTime = mLastTimeUs + timeSinceRead; if (mTimeAdjustment > 0) { long adjustment = mTimeAdjustment - timeSinceRead / TIME_ADJUSTMENT_RATE; if (adjustment <= 0) { mTimeAdjustment = 0; } else { mLastReportedTime += adjustment; } } } return mLastReportedTime; } public long getCurrentTimeUs(boolean refreshTime, boolean monotonic) throws IllegalStateException { synchronized (this) { // we always refresh the time when the paused-state changes, because // we expect to have received the pause-change event delayed. if (mPaused && !refreshTime) { return mLastReportedTime; } long nanoTime = System.nanoTime(); if (refreshTime || nanoTime >= mLastNanoTime + MAX_NS_WITHOUT_POSITION_CHECK) { try { mLastTimeUs = mPlayer.getCurrentPosition() * 1000; mPaused = !mPlayer.isPlaying(); if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs); } catch (IllegalStateException e) { if (mPausing) { // if we were pausing, get last estimated timestamp mPausing = false; getEstimatedTime(nanoTime, monotonic); mPaused = true; if (DEBUG) Log.d(TAG, "illegal state, but pausing: estimating at " + mLastReportedTime); return mLastReportedTime; } // TODO get time when prepared throw e; } mLastNanoTime = nanoTime; if (monotonic && mLastTimeUs < mLastReportedTime) { /* have to adjust time */ mTimeAdjustment = mLastReportedTime - mLastTimeUs; } else { mTimeAdjustment = 0; } } return getEstimatedTime(nanoTime, monotonic); } } private class EventHandler extends Handler { @Override public void handleMessage(Message msg) { if (msg.what == NOTIFY) { switch (msg.arg1) { case NOTIFY_TIME: notifyTimedEvent(false /* refreshTime */); break; case REFRESH_AND_NOTIFY_TIME: notifyTimedEvent(true /* refreshTime */); break; case NOTIFY_STOP: notifyStop(); break; case NOTIFY_SEEK: notifySeek(); break; } } } } /** @hide */ public Handler getHandler() { return mEventHandler; } } } Loading
media/java/android/media/MediaPlayer.java +382 −1 Original line number Diff line number Diff line Loading @@ -39,6 +39,8 @@ import android.graphics.Bitmap; import android.graphics.SurfaceTexture; import android.media.AudioManager; import android.media.MediaFormat; import android.media.MediaTimeProvider; import android.media.MediaTimeProvider.OnMediaTimeListener; import android.media.SubtitleData; import java.io.File; Loading @@ -48,6 +50,7 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.util.Map; import java.util.Set; import java.util.Vector; import java.lang.ref.WeakReference; /** Loading Loading @@ -590,6 +593,8 @@ public class MediaPlayer mEventHandler = null; } mTimeProvider = new TimeProvider(this); /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ Loading Loading @@ -1337,6 +1342,8 @@ public class MediaPlayer mOnInfoListener = null; mOnVideoSizeChangedListener = null; mOnTimedTextListener = null; mTimeProvider.close(); mTimeProvider = null; mOnSubtitleDataListener = null; _release(); } Loading Loading @@ -1914,11 +1921,21 @@ public class MediaPlayer private static final int MEDIA_BUFFERING_UPDATE = 3; private static final int MEDIA_SEEK_COMPLETE = 4; private static final int MEDIA_SET_VIDEO_SIZE = 5; private static final int MEDIA_STARTED = 6; private static final int MEDIA_PAUSED = 7; private static final int MEDIA_STOPPED = 8; private static final int MEDIA_TIMED_TEXT = 99; private static final int MEDIA_ERROR = 100; private static final int MEDIA_INFO = 200; private static final int MEDIA_SUBTITLE_DATA = 201; private TimeProvider mTimeProvider; /** @hide */ public MediaTimeProvider getMediaTimeProvider() { return mTimeProvider; } private class EventHandler extends Handler { private MediaPlayer mMediaPlayer; Loading Loading @@ -1946,14 +1963,31 @@ public class MediaPlayer stayAwake(false); return; case MEDIA_STOPPED: if (mTimeProvider != null) { mTimeProvider.onStopped(); } break; case MEDIA_STARTED: case MEDIA_PAUSED: if (mTimeProvider != null) { mTimeProvider.onPaused(msg.what == MEDIA_PAUSED); } break; case MEDIA_BUFFERING_UPDATE: if (mOnBufferingUpdateListener != null) mOnBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1); return; case MEDIA_SEEK_COMPLETE: if (mOnSeekCompleteListener != null) if (mOnSeekCompleteListener != null) { mOnSeekCompleteListener.onSeekComplete(mMediaPlayer); } if (mTimeProvider != null) { mTimeProvider.onSeekComplete(mMediaPlayer); } return; case MEDIA_SET_VIDEO_SIZE: Loading Loading @@ -2496,4 +2530,351 @@ public class MediaPlayer } private native void updateProxyConfig(ProxyProperties props); /** @hide */ static class TimeProvider implements MediaPlayer.OnSeekCompleteListener, MediaTimeProvider { private static final String TAG = "MTP"; private static final long MAX_NS_WITHOUT_POSITION_CHECK = 5000000000L; private static final long MAX_EARLY_CALLBACK_US = 1000; private static final long TIME_ADJUSTMENT_RATE = 2; /* meaning 1/2 */ private long mLastTimeUs = 0; private MediaPlayer mPlayer; private boolean mPaused = true; private boolean mStopped = true; private long mLastReportedTime; private long mTimeAdjustment; // since we are expecting only a handful listeners per stream, there is // no need for log(N) search performance private MediaTimeProvider.OnMediaTimeListener mListeners[]; private long mTimes[]; private long mLastNanoTime; private Handler mEventHandler; private boolean mRefresh = false; private boolean mPausing = false; private static final int NOTIFY = 1; private static final int NOTIFY_TIME = 0; private static final int REFRESH_AND_NOTIFY_TIME = 1; private static final int NOTIFY_STOP = 2; private static final int NOTIFY_SEEK = 3; /** @hide */ public boolean DEBUG = false; public TimeProvider(MediaPlayer mp) { mPlayer = mp; try { getCurrentTimeUs(true, false); } catch (IllegalStateException e) { // we assume starting position mRefresh = true; } mEventHandler = new EventHandler(); mListeners = new MediaTimeProvider.OnMediaTimeListener[0]; mTimes = new long[0]; mLastTimeUs = 0; mTimeAdjustment = 0; } private void scheduleNotification(int type, long delayUs) { if (DEBUG) Log.v(TAG, "scheduleNotification " + type + " in " + delayUs); mEventHandler.removeMessages(NOTIFY); Message msg = mEventHandler.obtainMessage(NOTIFY, type, 0); mEventHandler.sendMessageDelayed(msg, (int) (delayUs / 1000)); } /** @hide */ public void close() { mEventHandler.removeMessages(NOTIFY); } /** @hide */ public void onPaused(boolean paused) { synchronized(this) { if (DEBUG) Log.d(TAG, "onPaused: " + paused); if (mStopped) { // handle as seek if we were stopped mStopped = false; scheduleNotification(NOTIFY_SEEK, 0 /* delay */); } else { mPausing = paused; // special handling if player disappeared scheduleNotification(REFRESH_AND_NOTIFY_TIME, 0 /* delay */); } } } /** @hide */ public void onStopped() { synchronized(this) { if (DEBUG) Log.d(TAG, "onStopped"); mPaused = true; mStopped = true; scheduleNotification(NOTIFY_STOP, 0 /* delay */); } } /** @hide */ @Override public void onSeekComplete(MediaPlayer mp) { synchronized(this) { mStopped = false; scheduleNotification(NOTIFY_SEEK, 0 /* delay */); } } /** @hide */ public void onNewPlayer() { if (mRefresh) { synchronized(this) { scheduleNotification(NOTIFY_SEEK, 0 /* delay */); } } } private synchronized void notifySeek() { try { long timeUs = getCurrentTimeUs(true, false); if (DEBUG) Log.d(TAG, "onSeekComplete at " + timeUs); for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) { if (listener == null) { break; } listener.onSeek(timeUs); } } catch (IllegalStateException e) { // we should not be there, but at least signal pause if (DEBUG) Log.d(TAG, "onSeekComplete but no player"); mPausing = true; // special handling if player disappeared notifyTimedEvent(false /* refreshTime */); } } private synchronized void notifyStop() { for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) { if (listener == null) { break; } listener.onStop(); } } private int registerListener(MediaTimeProvider.OnMediaTimeListener listener) { int i = 0; for (; i < mListeners.length; i++) { if (mListeners[i] == listener || mListeners[i] == null) { break; } } // new listener if (i >= mListeners.length) { MediaTimeProvider.OnMediaTimeListener[] newListeners = new MediaTimeProvider.OnMediaTimeListener[i + 1]; long[] newTimes = new long[i + 1]; System.arraycopy(mListeners, 0, newListeners, 0, mListeners.length); System.arraycopy(mTimes, 0, newTimes, 0, mTimes.length); mListeners = newListeners; mTimes = newTimes; } if (mListeners[i] == null) { mListeners[i] = listener; mTimes[i] = MediaTimeProvider.NO_TIME; } return i; } public void notifyAt( long timeUs, MediaTimeProvider.OnMediaTimeListener listener) { synchronized(this) { if (DEBUG) Log.d(TAG, "notifyAt " + timeUs); mTimes[registerListener(listener)] = timeUs; scheduleNotification(NOTIFY_TIME, 0 /* delay */); } } public void scheduleUpdate(MediaTimeProvider.OnMediaTimeListener listener) { synchronized(this) { if (DEBUG) Log.d(TAG, "scheduleUpdate"); int i = registerListener(listener); if (mStopped) { scheduleNotification(NOTIFY_STOP, 0 /* delay */); } else { mTimes[i] = 0; scheduleNotification(NOTIFY_TIME, 0 /* delay */); } } } public void cancelNotifications( MediaTimeProvider.OnMediaTimeListener listener) { synchronized(this) { int i = 0; for (; i < mListeners.length; i++) { if (mListeners[i] == listener) { System.arraycopy(mListeners, i + 1, mListeners, i, mListeners.length - i - 1); System.arraycopy(mTimes, i + 1, mTimes, i, mTimes.length - i - 1); mListeners[mListeners.length - 1] = null; mTimes[mTimes.length - 1] = NO_TIME; break; } else if (mListeners[i] == null) { break; } } scheduleNotification(NOTIFY_TIME, 0 /* delay */); } } private synchronized void notifyTimedEvent(boolean refreshTime) { // figure out next callback long nowUs; try { nowUs = getCurrentTimeUs(refreshTime, true); } catch (IllegalStateException e) { // assume we paused until new player arrives mRefresh = true; mPausing = true; // this ensures that call succeeds nowUs = getCurrentTimeUs(refreshTime, true); } long nextTimeUs = nowUs; if (DEBUG) { StringBuilder sb = new StringBuilder(); sb.append("notifyTimedEvent(").append(mLastTimeUs).append(" -> ") .append(nowUs).append(") from {"); boolean first = true; for (long time: mTimes) { if (time == NO_TIME) { continue; } if (!first) sb.append(", "); sb.append(time); first = false; } sb.append("}"); Log.d(TAG, sb.toString()); } Vector<MediaTimeProvider.OnMediaTimeListener> activatedListeners = new Vector<MediaTimeProvider.OnMediaTimeListener>(); for (int ix = 0; ix < mTimes.length; ix++) { if (mListeners[ix] == null) { break; } if (mTimes[ix] <= NO_TIME) { // ignore, unless we were stopped } else if (mTimes[ix] <= nowUs + MAX_EARLY_CALLBACK_US) { activatedListeners.add(mListeners[ix]); if (DEBUG) Log.d(TAG, "removed"); mTimes[ix] = NO_TIME; } else if (nextTimeUs == nowUs || mTimes[ix] < nextTimeUs) { nextTimeUs = mTimes[ix]; } } if (nextTimeUs > nowUs && !mPaused) { // schedule callback at nextTimeUs if (DEBUG) Log.d(TAG, "scheduling for " + nextTimeUs + " and " + nowUs); scheduleNotification(NOTIFY_TIME, nextTimeUs - nowUs); } else { mEventHandler.removeMessages(NOTIFY); // no more callbacks } for (MediaTimeProvider.OnMediaTimeListener listener: activatedListeners) { listener.onTimedEvent(nowUs); } } private long getEstimatedTime(long nanoTime, boolean monotonic) { if (mPaused) { mLastReportedTime = mLastTimeUs + mTimeAdjustment; } else { long timeSinceRead = (nanoTime - mLastNanoTime) / 1000; mLastReportedTime = mLastTimeUs + timeSinceRead; if (mTimeAdjustment > 0) { long adjustment = mTimeAdjustment - timeSinceRead / TIME_ADJUSTMENT_RATE; if (adjustment <= 0) { mTimeAdjustment = 0; } else { mLastReportedTime += adjustment; } } } return mLastReportedTime; } public long getCurrentTimeUs(boolean refreshTime, boolean monotonic) throws IllegalStateException { synchronized (this) { // we always refresh the time when the paused-state changes, because // we expect to have received the pause-change event delayed. if (mPaused && !refreshTime) { return mLastReportedTime; } long nanoTime = System.nanoTime(); if (refreshTime || nanoTime >= mLastNanoTime + MAX_NS_WITHOUT_POSITION_CHECK) { try { mLastTimeUs = mPlayer.getCurrentPosition() * 1000; mPaused = !mPlayer.isPlaying(); if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs); } catch (IllegalStateException e) { if (mPausing) { // if we were pausing, get last estimated timestamp mPausing = false; getEstimatedTime(nanoTime, monotonic); mPaused = true; if (DEBUG) Log.d(TAG, "illegal state, but pausing: estimating at " + mLastReportedTime); return mLastReportedTime; } // TODO get time when prepared throw e; } mLastNanoTime = nanoTime; if (monotonic && mLastTimeUs < mLastReportedTime) { /* have to adjust time */ mTimeAdjustment = mLastReportedTime - mLastTimeUs; } else { mTimeAdjustment = 0; } } return getEstimatedTime(nanoTime, monotonic); } } private class EventHandler extends Handler { @Override public void handleMessage(Message msg) { if (msg.what == NOTIFY) { switch (msg.arg1) { case NOTIFY_TIME: notifyTimedEvent(false /* refreshTime */); break; case REFRESH_AND_NOTIFY_TIME: notifyTimedEvent(true /* refreshTime */); break; case NOTIFY_STOP: notifyStop(); break; case NOTIFY_SEEK: notifySeek(); break; } } } } /** @hide */ public Handler getHandler() { return mEventHandler; } } }