package org.edx.mobile.player; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.media.MediaCodec.CryptoException; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceHolder; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.Toast; import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; import com.google.android.exoplayer.R; import com.google.android.exoplayer.VideoSurfaceView; public class PlayerDemoActivity extends Activity implements SurfaceHolder.Callback, ExoPlayer.Listener, MediaCodecVideoTrackRenderer.EventListener { /** * Builds renderers for the player. */ public interface RendererBuilder { void buildRenderers(RendererBuilderCallback callback); } public static final int RENDERER_COUNT = 2; public static final int TYPE_VIDEO = 0; public static final int TYPE_AUDIO = 1; private static final String TAG = "PlayerDemoActivity"; public static final int TYPE_OTHER = 2; private EPlayerController mediaController; private Handler mainHandler; private View shutterView; private VideoSurfaceView surfaceView; private IEPlayer mPlayer; private RendererBuilder builder; private RendererBuilderCallback callback; private MediaCodecVideoTrackRenderer videoRenderer; private boolean autoPlay = true; private int playerPosition; private IVideo video = new IVideo() { private IClosedCaption selectedClosedCaption; @Override public String getVideoLink() { String contentUri = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4";; return contentUri; } @Override public String getTitle() { return "video title here"; } @Override public String getExternalLink() { return "https://www.edx.org/"; } @Override public List<IClosedCaption> getClosedCaptions() { // TODO: use CC URLs here List<IClosedCaption> list = new ArrayList<IVideo.IClosedCaption>(); list.add(new IClosedCaption() { @Override public String getPath() { // TODO: give a file path of srt file return "fake path"; } @Override public String getLanguage() { return "English"; } }); return list; } @Override public void setSelectedClosedCaption(IClosedCaption lang) { this.selectedClosedCaption = lang; } @Override public IClosedCaption getSelectedClosedCaption() { return selectedClosedCaption; } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mainHandler = new Handler(getMainLooper()); builder = getRendererBuilder(); setContentView(R.layout.activity_player_demo); ViewGroup root = (ViewGroup) findViewById(R.id.root); root.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View arg0, MotionEvent arg1) { if (arg1.getAction() == MotionEvent.ACTION_DOWN) { toggleControlsVisibility(); } arg0.performClick(); return true; } }); mediaController = new EPlayerController(this); mediaController.setAnchorView(root); shutterView = findViewById(R.id.shutter); surfaceView = (VideoSurfaceView) findViewById(R.id.surface_view); surfaceView.getHolder().addCallback(this); } @Override public void onResume() { super.onResume(); // Setup the player ExoPlayer player = ExoPlayer.Factory.newInstance(RENDERER_COUNT, 1000, 5000); player.addListener(this); player.seekTo(playerPosition); // Build the player controls mPlayer = new EPlayerImpl(player); // set the video information mPlayer.setVideo(video); mediaController.setMediaPlayer(mPlayer); mediaController.setEnabled(true); // Request the renderers callback = new RendererBuilderCallback(); builder.buildRenderers(callback); } @Override public void onPause() { super.onPause(); // Release the player if (mPlayer != null) { playerPosition = mPlayer.getCurrentPosition(); mPlayer.release(); mPlayer = null; } callback = null; videoRenderer = null; shutterView.setVisibility(View.VISIBLE); } // Public methods public Handler getMainHandler() { return mainHandler; } // Internal methods private void toggleControlsVisibility() { if (mediaController.isShowing()) { mediaController.hide(); } else { mediaController.show(0); } } private RendererBuilder getRendererBuilder() { return new DefaultRendererBuilder(this, Uri.parse(video.getVideoLink())); } private void onRenderers(RendererBuilderCallback callback, MediaCodecVideoTrackRenderer videoRenderer, MediaCodecAudioTrackRenderer audioRenderer) { if (this.callback != callback) { return; } this.callback = null; this.videoRenderer = videoRenderer; mPlayer.getExoPlayer().prepare(videoRenderer, audioRenderer); maybeStartPlayback(); } private void maybeStartPlayback() { Surface surface = surfaceView.getHolder().getSurface(); if (videoRenderer == null || surface == null || !surface.isValid()) { // We're not ready yet. return; } mPlayer.getExoPlayer().sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); if (autoPlay) { mPlayer.start(); autoPlay = false; } } 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(); } // ExoPlayer.Listener implementation @Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { // Do nothing. } @Override public void onPlayWhenReadyCommitted() { // Do nothing. } @Override public void onPlayerError(ExoPlaybackException e) { onError(e); } // MediaCodecVideoTrackRenderer.Listener @Override public void onVideoSizeChanged(int width, int height) { surfaceView.setVideoWidthHeightRatio(height == 0 ? 1 : (float) width / height); } @Override public void onDrawnToSurface(Surface surface) { shutterView.setVisibility(View.GONE); } @Override public void onDroppedFrames(int count, long elapsed) { Log.d(TAG, "Dropped frames: " + count); } @Override public void onDecoderInitializationError(DecoderInitializationException e) { // This is for informational purposes only. Do nothing. } @Override public void onCryptoError(CryptoException e) { // This is for informational purposes only. Do nothing. } // SurfaceHolder.Callback implementation @Override public void surfaceCreated(SurfaceHolder holder) { maybeStartPlayback(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // Do nothing. } @Override public void surfaceDestroyed(SurfaceHolder holder) { if (videoRenderer != null) { mPlayer.getExoPlayer().blockingSendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, null); } } final class RendererBuilderCallback { public void onRenderers(MediaCodecVideoTrackRenderer videoRenderer, MediaCodecAudioTrackRenderer audioRenderer) { PlayerDemoActivity.this.onRenderers(this, videoRenderer, audioRenderer); } public void onRenderersError(Exception e) { PlayerDemoActivity.this.onRenderersError(this, e); } } }