Fields Summary |
---|
private static final String | TAG |
private static final boolean | DEBUG |
private static long | sDebugInstanceId |
private static final String[] | SUPPORTED_FEATURES |
private static final int | CAN_PLAYThese are the states where it is valid to call play directly on the
MediaPlayer. |
private static final int | CAN_READY_PLAYThese are the states where we expect the MediaPlayer to be ready in the
future, so we can set a flag to start playing when it is. |
private static final int | CAN_PAUSEThe states when it is valid to call pause on the MediaPlayer. |
private static final int | CAN_SEEKThe states where it is valid to call seek on the MediaPlayer. |
private static final int | CAN_READY_SEEKThe states where we expect the MediaPlayer to be ready in the future and
can store a seek position to set later. |
private static final int | CAN_STOPThe states where it is valid to call stop on the MediaPlayer. |
private static final int | CAN_GET_POSITIONThe states where it is valid to get the current play position and the
duration from the MediaPlayer. |
private int | mState |
private android.media.AudioManager | mAudioManager |
private android.media.MediaPlayer | mPlayer |
private PlayerContent | mContent |
private android.media.MediaPlayer | mNextPlayer |
private PlayerContent | mNextContent |
private android.view.SurfaceHolder | mHolder |
private SurfaceHolder.Callback | mHolderCB |
private android.content.Context | mContext |
private android.os.Handler | mHandler |
private android.net.http.AndroidHttpClient | mHttpClient |
private AsyncErrorRetriever | mErrorRetriever |
private boolean | mSafeToCloseClient |
private final Object | mErrorLock |
private long | mErrorId |
private PlaybackError | mError |
private boolean | mPlayOnReady |
private int | mSeekOnReady |
private boolean | mHasAudioFocus |
private long | mDebugId |
Methods Summary |
---|
private boolean | canPlay()
return ((mState & CAN_PLAY) != 0) && mHasAudioFocus;
|
private boolean | canReadyPlay()
return (mState & CAN_PLAY) != 0 || (mState & CAN_READY_PLAY) != 0;
|
private void | cleanUpPlayer()
if (DEBUG) {
Log.d(TAG, mDebugId + ": Cleaning up current player");
}
synchronized (mErrorLock) {
mError = null;
if (mErrorRetriever != null) {
mErrorRetriever.cancelRequestLocked(false);
// Don't set to null as we may need to cancel again with true if
// the object gets destroyed.
}
}
mAudioManager.abandonAudioFocus(this);
SurfaceHolder.Callback cb = mHolderCB;
mHolderCB = null;
SurfaceHolder holder = mHolder;
mHolder = null;
if (holder != null && cb != null) {
holder.removeCallback(cb);
}
MediaPlayer player = mPlayer;
mPlayer = null;
if (player != null) {
player.reset();
player.release();
}
|
public long | getDuration()
return ((mState & CAN_GET_POSITION) == 0) ? -1 : mPlayer.getDuration();
|
public long | getSeekPosition()
return ((mState & CAN_GET_POSITION) == 0) ? -1 : mPlayer.getCurrentPosition();
|
protected void | initFeatures(android.os.Bundle params)
for (String feature : SUPPORTED_FEATURES) {
mFeatures.add(feature);
}
|
private boolean | isCurrentPlayer(android.media.MediaPlayer player)
return player.equals(mPlayer);
|
private boolean | isHolderReady(android.view.SurfaceHolder holder, android.media.MediaPlayer player)Checks if the holder is ready and either sets up a callback to wait for
it or sets it directly. If
mHolder = holder;
if (holder != null) {
if (holder.getSurface() != null && holder.getSurface().isValid()) {
player.setDisplay(holder);
return true;
} else {
Log.w(TAG, "Holder not null, waiting for it to be ready");
// If the holder isn't ready yet add a callback to set the
// holder when it's ready.
SurfaceHolder.Callback cb = new SurfaceHolder.Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
if (player.equals(mPlayer)) {
player.setDisplay(arg0);
preparePlayer(player, true);
}
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
};
mHolderCB = cb;
holder.addCallback(cb);
return false;
}
}
return true;
|
public boolean | isPaused()
return mState == STATE_PAUSED;
|
public boolean | isPlaying()
return mState == STATE_PLAYING;
|
public void | onAudioFocusChange(int focusChange)
// TODO figure out appropriate logic for handling focus loss at the TUQ
// level.
switch (focusChange) {
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
if (mState == STATE_PLAYING) {
onPause();
mPlayOnReady = true;
}
mHasAudioFocus = false;
break;
case AudioManager.AUDIOFOCUS_LOSS:
if (mState == STATE_PLAYING) {
onPause();
mPlayOnReady = false;
}
pushOnFocusLost();
mHasAudioFocus = false;
break;
case AudioManager.AUDIOFOCUS_GAIN:
mHasAudioFocus = true;
if (mPlayOnReady) {
onPlay();
}
break;
default:
Log.d(TAG, "Unknown focus change event " + focusChange);
break;
}
|
public void | onBufferingUpdate(android.media.MediaPlayer player, int percent)
if (!isCurrentPlayer(player)) {
return;
}
pushOnBufferingUpdate(percent);
|
public void | onCompletion(android.media.MediaPlayer player)
if (!isCurrentPlayer(player)) {
return;
}
if (DEBUG) {
Log.d(TAG, mDebugId + ": Completed item. Have next item? " + (mNextPlayer != null));
}
if (mNextPlayer != null) {
if (mPlayer != null) {
mPlayer.release();
}
mPlayer = mNextPlayer;
mContent = mNextContent;
mNextPlayer = null;
mNextContent = null;
pushOnNextStarted();
return;
}
setState(STATE_ENDED);
|
public void | onDestroy()Call this when completely finished with the MediaPlayerManager to have it
clean up. The instance may not be used again after this is called.
synchronized (mErrorLock) {
if (DEBUG) {
Log.d(TAG, "onDestroy, error retriever? " + mErrorRetriever + " safe to close? "
+ mSafeToCloseClient + " client? " + mHttpClient);
}
if (mErrorRetriever != null) {
mErrorRetriever.cancelRequestLocked(true);
mErrorRetriever = null;
}
// Increment the error id to ensure no errors are sent after this
// point.
mErrorId++;
if (mSafeToCloseClient) {
mHttpClient.close();
mHttpClient = null;
}
}
|
public boolean | onError(android.media.MediaPlayer player, int what, int extra)
if (!isCurrentPlayer(player)) {
return false;
}
if (DEBUG) {
Log.d(TAG, mDebugId + ": Entered error state, what: " + what + " extra: " + extra);
}
synchronized (mErrorLock) {
++mErrorId;
mError = new PlaybackError();
mError.type = what;
mError.extra = extra;
}
if (what == MediaPlayer.MEDIA_ERROR_UNKNOWN && extra == MediaPlayer.MEDIA_ERROR_IO
&& mContent != null && mContent.source.startsWith("http")) {
HttpGet request = new HttpGet(mContent.source);
if (mContent.headers != null) {
for (String key : mContent.headers.keySet()) {
request.addHeader(key, mContent.headers.get(key));
}
}
synchronized (mErrorLock) {
if (mErrorRetriever != null) {
mErrorRetriever.cancelRequestLocked(false);
}
mErrorRetriever = new AsyncErrorRetriever(mErrorId);
mErrorRetriever.execute(request);
}
} else {
setError(what, extra, null, null);
}
return true;
|
public boolean | onPause()Pause the player if possible or set it to not play when ready. If the
player is in a state where it will never be ready returns false.
MediaPlayer player = mPlayer;
// If the user paused us make sure we won't start playing again until
// asked to
mPlayOnReady = false;
if (player != null && (mState & CAN_PAUSE) != 0) {
player.pause();
setState(STATE_PAUSED);
} else if (!isPaused()) {
return false;
}
return true;
|
public boolean | onPlay()Start the player if possible or queue it to play when ready. If the
player is in a state where it will never be ready returns false.
MediaPlayer player = mPlayer;
if (player != null && mState == STATE_PLAYING) {
// already playing, just return
return true;
}
if (!mHasAudioFocus) {
requestAudioFocus();
}
if (player != null && canPlay()) {
player.start();
setState(STATE_PLAYING);
} else if (canReadyPlay()) {
mPlayOnReady = true;
} else if (!isPlaying()) {
return false;
}
return true;
|
public void | onPrepared(android.media.MediaPlayer player)
if (!isCurrentPlayer(player)) {
return;
}
setState(STATE_READY);
if (DEBUG) {
Log.d(TAG, mDebugId + ": Finished preparing, seekOnReady is " + mSeekOnReady);
}
if (mSeekOnReady >= 0) {
onSeekTo(mSeekOnReady);
mSeekOnReady = -1;
}
if (mPlayOnReady) {
player.start();
setState(STATE_PLAYING);
}
|
public boolean | onSeekTo(int position)Seek to a given position in the media. If the seek succeeded or will be
performed when loading is complete returns true. If the position is not
in range or the player will never be ready returns false.
MediaPlayer player = mPlayer;
if (player != null && (mState & CAN_SEEK) != 0) {
if (position < 0 || position >= getDuration()) {
return false;
} else {
if (mState == STATE_ENDED) {
player.start();
player.pause();
setState(STATE_PAUSED);
}
player.seekTo(position);
}
} else if ((mState & CAN_READY_SEEK) != 0) {
mSeekOnReady = position;
} else {
return false;
}
return true;
|
public boolean | onStop()Stop the player. It cannot be used again until
{@link #setContent(String, boolean)} is called.
cleanUpPlayer();
setState(STATE_STOPPED);
return true;
|
private boolean | preparePlayer(android.media.MediaPlayer player, boolean current)
player.setOnPreparedListener(this);
player.setOnBufferingUpdateListener(this);
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
try {
player.prepareAsync();
if (current) {
setState(STATE_PREPARING);
}
} catch (IllegalStateException e) {
if (current) {
setError(Listener.ERROR_PREPARE_ERROR, 0, null, e);
}
return false;
}
return true;
|
private void | requestAudioFocus()
int result = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
mHasAudioFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
|
public void | setContent(android.os.Bundle request, android.view.SurfaceHolder holder)Prepares the player for the given playback request. If the holder is null
it is assumed this is an audio only source. If playOnReady is set to true
the media will begin playing as soon as it can.
String source = request.getString(RequestUtils.EXTRA_KEY_SOURCE);
Map<String, String> headers = null; // request.mHeaders;
boolean playOnReady = true; // request.mPlayOnReady;
if (DEBUG) {
Log.d(TAG, mDebugId + ": Settings new content. Have a player? " + (mPlayer != null)
+ " have a next player? " + (mNextPlayer != null));
}
cleanUpPlayer();
setState(STATE_PREPARING);
mPlayOnReady = playOnReady;
mSeekOnReady = -1;
final MediaPlayer newPlayer = new MediaPlayer();
requestAudioFocus();
mPlayer = newPlayer;
mContent = new PlayerContent(source, headers);
try {
if (headers != null) {
Uri sourceUri = Uri.parse(source);
newPlayer.setDataSource(mContext, sourceUri, headers);
} else {
newPlayer.setDataSource(source);
}
} catch (Exception e) {
setError(Listener.ERROR_LOAD_FAILED, 0, null, e);
return;
}
if (isHolderReady(holder, newPlayer)) {
preparePlayer(newPlayer, true);
}
|
public void | setContent(android.os.Bundle request)
setContent(request, null);
|
private void | setError(int type, int extra, android.os.Bundle extras, java.lang.Exception e)
setState(STATE_ERROR);
pushOnError(type, extra, extras, e);
cleanUpPlayer();
return;
|
public void | setNextContent(android.os.Bundle request)
String source = request.getString(RequestUtils.EXTRA_KEY_SOURCE);
Map<String, String> headers = null; // request.mHeaders;
// TODO support video
if (DEBUG) {
Log.d(TAG, mDebugId + ": Setting next content. Have player? " + (mPlayer != null)
+ " have next player? " + (mNextPlayer != null));
}
if (mPlayer == null) {
// The manager isn't being used to play anything, don't try to
// set a next.
return;
}
if (mNextPlayer != null) {
// Before setting up the new one clear out the old one and release
// it to ensure it doesn't play.
mPlayer.setNextMediaPlayer(null);
mNextPlayer.release();
mNextPlayer = null;
mNextContent = null;
}
if (source == null) {
// If there's no new content we're done
return;
}
final MediaPlayer newPlayer = new MediaPlayer();
try {
if (headers != null) {
Uri sourceUri = Uri.parse(source);
newPlayer.setDataSource(mContext, sourceUri, headers);
} else {
newPlayer.setDataSource(source);
}
} catch (Exception e) {
newPlayer.release();
// Don't return an error until we get to this item in playback
return;
}
if (preparePlayer(newPlayer, false)) {
mPlayer.setNextMediaPlayer(newPlayer);
mNextPlayer = newPlayer;
mNextContent = new PlayerContent(source, headers);
}
|
private void | setState(int state)Sends a state update if the listener exists
if (state == mState) {
return;
}
Log.d(TAG, "Entering state " + state + " from state " + mState);
mState = state;
if (state != STATE_ERROR) {
// Don't notify error here, it'll get sent via onError
pushOnStateChanged(state);
}
|