package com.nemustech.exoplayerleanback; import android.app.Activity; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.media.MediaCodec; import android.media.MediaPlayer; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.util.DisplayMetrics; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayerLibraryInfo; import com.google.android.exoplayer.FrameworkSampleSource; import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MediaCodecTrackRenderer; import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.VideoSurfaceView; import com.nemustech.exoplayerleanback.util.*; import java.util.Timer; import java.util.TimerTask; /** * Created by jhlee on 14. 8. 8. */ public class ExoplayerActivity extends Activity implements SurfaceHolder.Callback, ExoPlayer.Listener, MediaCodecVideoTrackRenderer.EventListener{ private static final String TAG = "ExoPlayerActivity"; private static final int HIDE_CONTROLLER_TIME = 5000; private static final int SEEKBAR_DELAY_TIME = 100; private static final int SEEKBAR_INTERVAL_TIME = 1000; private static final int MIN_SCRUB_TIME = 3000; private static final int SCRUB_SEGMENT_DIVISOR = 30; private static final double MEDIA_BAR_TOP_MARGIN = 0.8; private static final double MEDIA_BAR_RIGHT_MARGIN = 0.2; private static final double MEDIA_BAR_BOTTOM_MARGIN = 0.0; private static final double MEDIA_BAR_LEFT_MARGIN = 0.2; private static final double MEDIA_BAR_HEIGHT = 0.1; private static final double MEDIA_BAR_WIDTH = 0.9; private DisplayMetrics mMetrics; private VideoSurfaceView mSurfaceView; private TextView mStartText; private TextView mEndText; private SeekBar mSeekbar; private ImageView mPlayPause; private ProgressBar mLoading; private View mControllers; private Movie mSelectedMovie; private boolean mShouldStartPlayback; private PlaybackState mPlaybackState; private Timer mControllersTimer; private boolean mControllersVisible; private final Handler mHandler = new Handler(); private Handler mainHandler; private MediaCodecVideoTrackRenderer videoRenderer; private MediaCodecAudioTrackRenderer audioRenderer; private ExoPlayer exoPlayer; private RendererBuilderCallback callback; private RendererBuilder builder; private Uri contentUri; private int contentType; private String contentId; public static final int TYPE_DASH_VOD = 0; public static final int TYPE_SS_VOD = 1; public static final int TYPE_OTHER = 2; /** * Builds renderers for the player. */ public interface RendererBuilder { void buildRenderers(RendererBuilderCallback callback); } /* package */ public final class RendererBuilderCallback { public void onRenderers(MediaCodecVideoTrackRenderer videoRenderer, MediaCodecAudioTrackRenderer audioRenderer) { ExoplayerActivity.this.onRenderers(this, videoRenderer, audioRenderer); } public void onRenderersError(Exception e) { ExoplayerActivity.this.onRenderersError(this, e); } } private void onRenderers(RendererBuilderCallback rendererBuilderCallback, MediaCodecVideoTrackRenderer videoRenderer, MediaCodecAudioTrackRenderer audioRenderer) { if (this.callback != callback) { return; } this.callback = null; this.videoRenderer = videoRenderer; exoPlayer.prepare(videoRenderer, audioRenderer); Log.e(TAG, "onRenderers"); startVideoPlayer(); } private void onRenderersError(RendererBuilderCallback callback, Exception e) { if (this.callback != callback) { return; } this.callback = null; onError(e); } private void onError(Exception e) { Log.e(TAG, "Playback failed", e); Toast.makeText(this, "Playback failed", Toast.LENGTH_SHORT).show(); finish(); } @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { Log.e(TAG, "surfaceCreated"); startVideoPlayer(); } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) { } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { } @Override public void onDroppedFrames(int count, long elapsed) { } @Override public void onVideoSizeChanged(int width, int height) { } @Override public void onDrawnToSurface(Surface surface) { } @Override public void onDecoderInitializationError(MediaCodecTrackRenderer.DecoderInitializationException e) { } @Override public void onCryptoError(MediaCodec.CryptoException e) { } @Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { } @Override public void onPlayWhenReadyCommitted() { } @Override public void onPlayerError(ExoPlaybackException error) { } /* * List of various states that we can be in */ public static enum PlaybackState { PLAYING, PAUSED, BUFFERING, IDLE; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.exoplayer_activity); mMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(mMetrics); mainHandler = new Handler(getMainLooper()); contentType = getIntent().getIntExtra("content_type", 3); loadViews(); setupController(); setupControlsCallbacks(); if(contentType == 3) prepareVideoPlayer(); else { contentUri = getIntent().getData(); contentId = getIntent().getStringExtra("content_id"); builder = getRendererBuilder(); } //startVideoPlayer(); updateMetadata(true); } private void loadViews() { mSurfaceView = (VideoSurfaceView) findViewById(R.id.surface_view); mStartText = (TextView) findViewById(R.id.exo_startText); mEndText = (TextView) findViewById(R.id.exo_endText); mSeekbar = (SeekBar) findViewById(R.id.exo_seekBar); mPlayPause = (ImageView) findViewById(R.id.exo_playpause); mLoading = (ProgressBar) findViewById(R.id.exo_progressBar); mControllers = findViewById(R.id.exo_controllers); // mVideoView.setOnClickListener(mPlayPauseHandler); mSurfaceView.getHolder().addCallback(this); } private void setupController() { int w = (int) (mMetrics.widthPixels * MEDIA_BAR_WIDTH); int h = (int) (mMetrics.heightPixels * MEDIA_BAR_HEIGHT); int marginLeft = (int) (mMetrics.widthPixels * MEDIA_BAR_LEFT_MARGIN); int marginTop = (int) (mMetrics.heightPixels * MEDIA_BAR_TOP_MARGIN); int marginRight = (int) (mMetrics.widthPixels * MEDIA_BAR_RIGHT_MARGIN); int marginBottom = (int) (mMetrics.heightPixels * MEDIA_BAR_BOTTOM_MARGIN); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(w, h); lp.setMargins(marginLeft, marginTop, marginRight, marginBottom); mControllers.setLayoutParams(lp); mStartText.setText(getResources().getString(R.string.init_text)); mEndText.setText(getResources().getString(R.string.init_text)); } private void setupControlsCallbacks() { } @Override protected void onResume() { super.onResume(); // Setup the player Log.e(TAG, "contentType = " + contentType); if(contentType != 3) { exoPlayer = ExoPlayer.Factory.newInstance(2, 1000, 5000); exoPlayer.addListener(this); // Request the renderers callback = new RendererBuilderCallback(); builder.buildRenderers(callback); } } @Override protected void onPause() { super.onPause(); // Release the player if (exoPlayer != null) { exoPlayer.release(); exoPlayer = null; } callback = null; videoRenderer = null; } private void prepareVideoPlayer() { Bundle b = getIntent().getExtras(); mSelectedMovie = (Movie) getIntent().getSerializableExtra( getResources().getString(R.string.movie)); if (null != b) { mShouldStartPlayback = b.getBoolean(getResources().getString(R.string.should_start)); int startPosition = b.getInt(getResources().getString(R.string.start_position), 0); Uri uri = Uri.parse(mSelectedMovie.getVideoUrl()); Log.e(TAG, "URI is " + uri.toString()); final int numRenderers = 2; FrameworkSampleSource sampleSource = new FrameworkSampleSource(this, uri, /* headers */ null, numRenderers); videoRenderer = new MediaCodecVideoTrackRenderer(sampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT , 0, this.getMainHandler(), this, 50); audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource); exoPlayer = ExoPlayer.Factory.newInstance(numRenderers); exoPlayer.prepare(videoRenderer, audioRenderer); startVideoPlayer(); } } private void startVideoPlayer() { Log.e(TAG, "startVideoPlayer !!!!!"); Surface surface = mSurfaceView.getHolder().getSurface(); if (surface == null || !surface.isValid()) { // We're not ready yet. Log.e(TAG, "surface is not ready yet!!!!!"); return; } if (videoRenderer == null) { Log.e(TAG, "videoRenderer is not ready yet!!!!!"); return; } exoPlayer.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); //mVideoView.setVideoPath(mSelectedMovie.getVideoUrl()); mShouldStartPlayback = true; if (mShouldStartPlayback) { mPlaybackState = PlaybackState.PLAYING; updatePlayButton(mPlaybackState); // if (startPosition > 0) { // //mVideoView.seekTo(startPosition); // } //mVideoView.start(); exoPlayer.setPlayWhenReady(true); mPlayPause.requestFocus(); startControllersTimer(); } else { updatePlaybackLocation(); mPlaybackState = PlaybackState.PAUSED; updatePlayButton(mPlaybackState); } } public Handler getMainHandler() { return mainHandler; } private void updatePlayButton(PlaybackState state) { switch (state) { case PLAYING: mLoading.setVisibility(View.INVISIBLE); mPlayPause.setVisibility(View.VISIBLE); mPlayPause.setImageDrawable( getResources().getDrawable(R.drawable.ic_pause_playcontrol_normal)); break; case PAUSED: case IDLE: mLoading.setVisibility(View.INVISIBLE); mPlayPause.setVisibility(View.VISIBLE); mPlayPause.setImageDrawable( getResources().getDrawable(R.drawable.ic_play_playcontrol_normal)); break; case BUFFERING: mPlayPause.setVisibility(View.INVISIBLE); mLoading.setVisibility(View.VISIBLE); break; default: break; } } private void updateMetadata(boolean visible) { // mVideoView.invalidate(); } private void startControllersTimer() { if (null != mControllersTimer) { mControllersTimer.cancel(); } mControllersTimer = new Timer(); mControllersTimer.schedule(new HideControllersTask(), HIDE_CONTROLLER_TIME); } private void stopControllersTimer() { if (null != mControllersTimer) { mControllersTimer.cancel(); } } private void updatePlaybackLocation() { if (mPlaybackState == PlaybackState.PLAYING || mPlaybackState == PlaybackState.BUFFERING) { startControllersTimer(); } else { stopControllersTimer(); } } private void updateControllersVisibility(boolean show) { if (show) { mControllers.setVisibility(View.VISIBLE); } else { mControllers.setVisibility(View.INVISIBLE); } } private RendererBuilder getRendererBuilder() { String userAgent = getUserAgent(this); Log.e(TAG, "getRendererBuilder " + "contentType " + contentType); switch (contentType) { case TYPE_SS_VOD: return new SmoothStreamingRendererBuilder(this, userAgent, contentUri.toString(), contentId); case TYPE_DASH_VOD: return new DashVodRendererBuilder(this, userAgent, contentUri.toString(), contentId); default: return new DefaultRendererBuilder(this, contentUri); } } public static String getUserAgent(Context context) { String versionName; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); versionName = info.versionName; } catch (PackageManager.NameNotFoundException e) { versionName = "?"; } return "ExoPlayerDemo/" + versionName + " (Linux;Android " + Build.VERSION.RELEASE + ") " + "ExoPlayerLib/" + ExoPlayerLibraryInfo.VERSION; } private class HideControllersTask extends TimerTask { @Override public void run() { mHandler.post(new Runnable() { @Override public void run() { updateControllersVisibility(false); mControllersVisible = false; } }); } } }