FileDocCategorySizeDatePackage
LocalRenderer.javaAPI DocAndroid 5.1 API24418Thu Mar 12 22:22:44 GMT 2015com.android.onemedia.playback

LocalRenderer

public class LocalRenderer extends Renderer implements android.media.MediaPlayer.OnErrorListener, android.media.MediaPlayer.OnPreparedListener, android.media.MediaPlayer.OnCompletionListener, android.media.MediaPlayer.OnBufferingUpdateListener, android.media.AudioManager.OnAudioFocusChangeListener
Helper class for wrapping a MediaPlayer and doing a lot of the default work to play audio. This class is not currently thread safe and all calls to it should be made on the same thread.

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_PLAY
These are the states where it is valid to call play directly on the MediaPlayer.
private static final int
CAN_READY_PLAY
These 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_PAUSE
The states when it is valid to call pause on the MediaPlayer.
private static final int
CAN_SEEK
The states where it is valid to call seek on the MediaPlayer.
private static final int
CAN_READY_SEEK
The 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_STOP
The states where it is valid to call stop on the MediaPlayer.
private static final int
CAN_GET_POSITION
The 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
Constructors Summary
public LocalRenderer(android.content.Context context, android.os.Bundle params)


         
        super(context, params);
        mContext = context;
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    
Methods Summary
private booleancanPlay()

        return ((mState & CAN_PLAY) != 0) && mHasAudioFocus;
    
private booleancanReadyPlay()

        return (mState & CAN_PLAY) != 0 || (mState & CAN_READY_PLAY) != 0;
    
private voidcleanUpPlayer()

        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 longgetDuration()

        return ((mState & CAN_GET_POSITION) == 0) ? -1 : mPlayer.getDuration();
    
public longgetSeekPosition()

        return ((mState & CAN_GET_POSITION) == 0) ? -1 : mPlayer.getCurrentPosition();
    
protected voidinitFeatures(android.os.Bundle params)

        for (String feature : SUPPORTED_FEATURES) {
            mFeatures.add(feature);
        }
    
private booleanisCurrentPlayer(android.media.MediaPlayer player)

        return player.equals(mPlayer);
    
private booleanisHolderReady(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

param
holder
param
player
return

        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 booleanisPaused()

        return mState == STATE_PAUSED;
    
public booleanisPlaying()

        return mState == STATE_PLAYING;
    
public voidonAudioFocusChange(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 voidonBufferingUpdate(android.media.MediaPlayer player, int percent)

        if (!isCurrentPlayer(player)) {
            return;
        }
        pushOnBufferingUpdate(percent);
    
public voidonCompletion(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 voidonDestroy()
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 booleanonError(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 booleanonPause()
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.

return
true if the content was paused or will wait to play when ready later

        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 booleanonPlay()
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.

return
true if the content was started or will be started later

        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 voidonPrepared(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 booleanonSeekTo(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.

param
position The position to seek to in milliseconds
return
true if playback was moved or will be moved when ready

        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 booleanonStop()
Stop the player. It cannot be used again until {@link #setContent(String, boolean)} is called.

return
true if stopping the player succeeded

        cleanUpPlayer();
        setState(STATE_STOPPED);
        return true;
    
private booleanpreparePlayer(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 voidrequestAudioFocus()

        int result = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
                AudioManager.AUDIOFOCUS_GAIN);
        mHasAudioFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    
public voidsetContent(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.

see
RequestUtils for the set of valid keys.

        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 voidsetContent(android.os.Bundle request)

        setContent(request, null);
    
private voidsetError(int type, int extra, android.os.Bundle extras, java.lang.Exception e)

param
extra
param
e

        setState(STATE_ERROR);
        pushOnError(type, extra, extras, e);
        cleanUpPlayer();
        return;
    
public voidsetNextContent(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 voidsetState(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);
        }