Download File
Download Project
Settings
Line Wrap
Themes
default
ambiance
bespin
dracula
eclipse
material
mbo
mdn-like
neat
solarized dark
ttcn
zenburn
ExoPlayerAdapter.java
package android.support.v17.leanback.supportleanbackshowcase.app.media; import android.content.Context; import android.media.AudioManager; import android.net.Uri; import android.os.Handler; import android.support.v17.leanback.media.PlaybackGlueHost; import android.support.v17.leanback.media.PlayerAdapter; import android.support.v17.leanback.media.SurfaceHolderGlueHost; import android.support.v17.leanback.supportleanbackshowcase.R; import android.view.SurfaceHolder; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.util.Util; /** * This implementation extends the {@link PlayerAdapter} with a {@link SimpleExoPlayer}. */ public class ExoPlayerAdapter extends PlayerAdapter implements ExoPlayer.EventListener{ Context mContext; final SimpleExoPlayer mPlayer; SurfaceHolderGlueHost mSurfaceHolderGlueHost; final Runnable mRunnable = new Runnable() { @Override public void run() { getCallback().onCurrentPositionChanged(ExoPlayerAdapter.this); getCallback().onBufferedPositionChanged(ExoPlayerAdapter.this); mHandler.postDelayed(this, getUpdatePeriod()); } }; final Handler mHandler = new Handler(); boolean mInitialized = false; Uri mMediaSourceUri = null; boolean mHasDisplay; boolean mBufferingStart; @C.StreamType int mAudioStreamType; /** * Constructor. */ public ExoPlayerAdapter(Context context) { mContext = context; mPlayer = ExoPlayerFactory.newSimpleInstance(mContext, new DefaultTrackSelector(), new DefaultLoadControl()); mPlayer.addListener(this); } @Override public void onAttachedToHost(PlaybackGlueHost host) { if (host instanceof SurfaceHolderGlueHost) { mSurfaceHolderGlueHost = ((SurfaceHolderGlueHost) host); mSurfaceHolderGlueHost.setSurfaceHolderCallback(new VideoPlayerSurfaceHolderCallback()); } } /** * Will reset the {@link ExoPlayer} and the glue such that a new file can be played. You are * not required to call this method before playing the first file. However you have to call it * before playing a second one. */ public void reset() { changeToUninitialized(); mPlayer.stop(); } void changeToUninitialized() { if (mInitialized) { mInitialized = false; notifyBufferingStartEnd(); if (mHasDisplay) { getCallback().onPreparedStateChanged(ExoPlayerAdapter.this); } } } /** * Notify the state of buffering. For example, an app may enable/disable a loading figure * according to the state of buffering. */ void notifyBufferingStartEnd() { getCallback().onBufferingStateChanged(ExoPlayerAdapter.this, mBufferingStart || !mInitialized); } /** * Release internal {@link SimpleExoPlayer}. Should not use the object after call release(). */ public void release() { changeToUninitialized(); mHasDisplay = false; mPlayer.release(); } @Override public void onDetachedFromHost() { if (mSurfaceHolderGlueHost != null) { mSurfaceHolderGlueHost.setSurfaceHolderCallback(null); mSurfaceHolderGlueHost = null; } reset(); release(); } /** * @see SimpleExoPlayer#setVideoSurfaceHolder(SurfaceHolder) */ void setDisplay(SurfaceHolder surfaceHolder) { boolean hadDisplay = mHasDisplay; mHasDisplay = surfaceHolder != null; if (hadDisplay == mHasDisplay) { return; } mPlayer.setVideoSurfaceHolder(surfaceHolder); if (mHasDisplay) { if (mInitialized) { getCallback().onPreparedStateChanged(ExoPlayerAdapter.this); } } else { if (mInitialized) { getCallback().onPreparedStateChanged(ExoPlayerAdapter.this); } } } @Override public void setProgressUpdatingEnabled(final boolean enabled) { mHandler.removeCallbacks(mRunnable); if (!enabled) { return; } mHandler.postDelayed(mRunnable, getUpdatePeriod()); } int getUpdatePeriod() { return 16; } @Override public boolean isPlaying() { boolean exoPlayerIsPlaying = mPlayer.getPlaybackState() == ExoPlayer.STATE_READY && mPlayer.getPlayWhenReady(); return mInitialized && exoPlayerIsPlaying; } @Override public long getDuration() { return mInitialized ? mPlayer.getDuration() : -1; } @Override public long getCurrentPosition() { return mInitialized ? mPlayer.getCurrentPosition() : -1; } @Override public void play() { if (!mInitialized || isPlaying()) { return; } mPlayer.setPlayWhenReady(true); getCallback().onPlayStateChanged(ExoPlayerAdapter.this); getCallback().onCurrentPositionChanged(ExoPlayerAdapter.this); } @Override public void pause() { if (isPlaying()) { mPlayer.setPlayWhenReady(false); getCallback().onPlayStateChanged(ExoPlayerAdapter.this); } } @Override public void seekTo(long newPosition) { if (!mInitialized) { return; } mPlayer.seekTo(newPosition); } @Override public long getBufferedPosition() { return mPlayer.getBufferedPosition(); } public Context getContext() { return mContext; } /** * Sets the media source of the player with a given URI. * * @return Returns
true
if uri represents a new media;
false
* otherwise. * @see ExoPlayer#prepare(MediaSource) */ public boolean setDataSource(Uri uri) { if (mMediaSourceUri != null ? mMediaSourceUri.equals(uri) : uri == null) { return false; } mMediaSourceUri = uri; prepareMediaForPlaying(); return true; } public int getAudioStreamType() { return mAudioStreamType; } public void setAudioStreamType(@C.StreamType int audioStreamType) { mAudioStreamType = audioStreamType; } /** * Set {@link MediaSource} for {@link SimpleExoPlayer}. An app may override this method in order * to use different {@link MediaSource}. * @param uri The url of media source * @return MediaSource for the player */ public MediaSource onCreateMediaSource(Uri uri) { String userAgent = Util.getUserAgent(mContext, "ExoPlayerAdapter"); return new ExtractorMediaSource(uri, new DefaultDataSourceFactory(mContext, userAgent), new DefaultExtractorsFactory(), null, null); } private void prepareMediaForPlaying() { reset(); if (mMediaSourceUri != null) { MediaSource mediaSource = onCreateMediaSource(mMediaSourceUri); mPlayer.prepare(mediaSource); } else { return; } mPlayer.setAudioStreamType(mAudioStreamType); mPlayer.setVideoListener(new SimpleExoPlayer.VideoListener() { @Override public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { getCallback().onVideoSizeChanged(ExoPlayerAdapter.this, width, height); } @Override public void onRenderedFirstFrame() { } }); notifyBufferingStartEnd(); getCallback().onPlayStateChanged(ExoPlayerAdapter.this); } /** * @return True if ExoPlayer is ready and got a SurfaceHolder if * {@link PlaybackGlueHost} provides SurfaceHolder. */ @Override public boolean isPrepared() { return mInitialized && (mSurfaceHolderGlueHost == null || mHasDisplay); } /** * Implements {@link SurfaceHolder.Callback} that can then be set on the * {@link PlaybackGlueHost}. */ class VideoPlayerSurfaceHolderCallback implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { setDisplay(surfaceHolder); } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { setDisplay(null); } } // ExoPlayer Event Listeners @Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { mBufferingStart = false; if (playbackState == ExoPlayer.STATE_READY && !mInitialized) { mInitialized = true; if (mSurfaceHolderGlueHost == null || mHasDisplay) { getCallback().onPreparedStateChanged(ExoPlayerAdapter.this); } } else if (playbackState == ExoPlayer.STATE_BUFFERING) { mBufferingStart = true; } else if (playbackState == ExoPlayer.STATE_ENDED) { getCallback().onPlayStateChanged(ExoPlayerAdapter.this); getCallback().onPlayCompleted(ExoPlayerAdapter.this); } notifyBufferingStartEnd(); } @Override public void onPlayerError(ExoPlaybackException error) { getCallback().onError(ExoPlayerAdapter.this, error.type, mContext.getString(R.string.lb_media_player_error, error.type, error.rendererIndex)); } @Override public void onLoadingChanged(boolean isLoading) { } @Override public void onTimelineChanged(Timeline timeline, Object manifest) { } @Override public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { } @Override public void onPositionDiscontinuity() { } }