package com.bitmovin.bitcodin; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.StrictMode; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; import android.view.Window; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import com.bitmovin.bitcodin.Job.BitcodinJob; import com.bitmovin.bitcodin.Job.JobManager; import com.bitmovin.bitcodin.Overlay.BufferingOverlay; import com.bitmovin.bitcodin.Overlay.ControlOverlay; import com.bitmovin.bitcodin.Overlay.SidebarOverlay; import com.bitmovin.bitcodin.Overlay.SidebarOverlayListener; import com.bitmovin.bitcodin.Player.DemoPlayer; import com.bitmovin.bitcodin.Player.WidevineTestMediaDrmCallback; import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.VideoSurfaceView; import com.google.android.exoplayer.upstream.HttpDataSource; import com.google.android.exoplayer.util.Util; @SuppressWarnings("FieldCanBeLocal") public class PlayerActivity extends Activity implements SurfaceHolder.Callback, DemoPlayer.Listener, SidebarOverlayListener { public String API_KEY = ""; private Context context = this; private VideoSurfaceView playerSurface; private ErrorPopup mErrorPopup; private JobManager mJobManager; private BufferingOverlay bufferingOverlay; private ControlOverlay mControlOverlay; private SidebarOverlay mSidebarOverlay; private ImageView logo; private boolean lastStateBuffering = false; public static final int TYPE_DASH = 0; public static final int TYPE_HLS = 2; private DemoPlayer player; private boolean playerNeedsPrepare; private Uri contentUri; private int contentType; private BitcodinJob.Source currentSource; private BitcodinJob currentJob; private long currentPlaybackPosition; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); requestWindowFeature(Window.FEATURE_NO_TITLE); Utils.hideSystemUI(this, true); setContentView(R.layout.activity_player); this.API_KEY = getIntent().getStringExtra("API_KEY"); this.contentUri = Uri.parse(Settings.DEFAULT_PLAYER_URI); this.contentType = Settings.DEFAULT_PLAYER_TYPE; this.playerSurface = (VideoSurfaceView) findViewById(R.id.player_vsv_player_surface); this.playerSurface.getHolder().addCallback(this); Settings.init(this, this.API_KEY); Settings.WIDEVINE_GTS_DEFAULT_BASE_URI = this.context.getSharedPreferences("bitcodin", MODE_PRIVATE). getString( Settings.USER_FOLDER + "widevine_url", "http://widevine-proxy.appspot.com/proxy"); this.mJobManager = new JobManager( this, (LinearLayout) findViewById(R.id.player_llt_joblist_container), this.API_KEY ); this.bufferingOverlay = new BufferingOverlay( (RelativeLayout) findViewById(R.id.player_rlt_buffering_overlay_wrapper), 40 ); this.mControlOverlay = new ControlOverlay( this, (RelativeLayout) findViewById(R.id.player_rlt_overlay_wrapper), (LinearLayout) findViewById(R.id.player_llt_tooltip_wrapper), this.playerSurface ); this.mSidebarOverlay = new SidebarOverlay( this, (LinearLayout) findViewById(R.id.player_llt_sidebar_wrapper), this.mJobManager ); this.mControlOverlay.setTitle("Art of Motion"); this.mSidebarOverlay.addListener(this); this.mErrorPopup = new ErrorPopup(this); this.logo = (ImageView) findViewById(R.id.player_iv_bitcodin_logo); this.logo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent viewHomepageIntent = new Intent(Intent.ACTION_VIEW); viewHomepageIntent.setData(Uri.parse("http://www.bitcodin.com")); startActivity(viewHomepageIntent); } }); findViewById(R.id.player_llt_click_listener).setOnTouchListener(this.mOnTouchListener); findViewById(R.id.player_rlt_overlay_wrapper).setOnTouchListener(this.mOnTouchListener); this.currentPlaybackPosition = 0; releasePlayer(); preparePlayer(); } private View.OnTouchListener mOnTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { showControls(); mSidebarOverlay.show(); Utils.hideSystemUI(context, true); return false; } }; @Override public void onBackPressed() { this.finishAffinity(); } @Override public void onResume() { super.onResume(); preparePlayer(true, this.currentPlaybackPosition); } @Override public void onPause() { super.onPause(); this.currentPlaybackPosition = this.player.getCurrentPosition(); releasePlayer(); } @Override public void onDestroy() { super.onDestroy(); releasePlayer(); } @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { if (this.player != null) { this.player.setSurface(surfaceHolder.getSurface()); } } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { // do nothing } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { if (this.player != null) { this.player.blockingClearSurface(); } } private DemoPlayer.RendererBuilder getRendererBuilder() { String userAgent = Util.getUserAgent(this, "ExoPlayerDemo"); switch (this.contentType) { case TYPE_DASH: return new com.bitmovin.bitcodin.Player.DashRendererBuilder( this, userAgent, this.contentUri.toString(), new WidevineTestMediaDrmCallback() ); case TYPE_HLS: return new com.bitmovin.bitcodin.Player.HlsRendererBuilder( this, userAgent, this.contentUri.toString() ); default: throw new IllegalStateException("Unsupported type: " + this.contentType); } } private void preparePlayer() { preparePlayer(true, 0); } private void preparePlayer(boolean playWhenReady, long playbackPosition) { if (this.player == null) { this.player = new DemoPlayer(getRendererBuilder()); this.player.addListener(this); this.playerNeedsPrepare = true; if (playbackPosition >= 0) { this.player.seekTo(playbackPosition); } this.mControlOverlay.setMediaPlayer(this.player.getPlayerControl()); } if (this.playerNeedsPrepare) { this.player.prepare(); this.playerNeedsPrepare = false; } this.player.setSurface(this.playerSurface.getHolder().getSurface()); this.player.setPlayWhenReady(playWhenReady); } private void releasePlayer() { if (this.player != null) { this.player.release(); this.player = null; System.gc(); } } @Override public void onStateChanged(boolean playWhenReady, int playbackState) { if (playbackState == ExoPlayer.STATE_ENDED) { showControls(); } switch (playbackState) { case ExoPlayer.STATE_BUFFERING: this.lastStateBuffering = true; this.bufferingOverlay.show(); break; default: if (this.lastStateBuffering) { this.lastStateBuffering = false; this.bufferingOverlay.hide(); } break; } } @Override public void onError(Exception e) { if (e instanceof ExoPlaybackException && e.getCause() instanceof HttpDataSource.InvalidResponseCodeException && ((HttpDataSource.InvalidResponseCodeException)e.getCause()).responseCode == 404 && this.player.getCurrentPosition() > (this.player.getDuration() - 1000)) { releasePlayer(); preparePlayer(false, 0); } else { this.mErrorPopup.show("Playback failed for job " + this.currentJob.getId() + "\n(" + this.currentSource.getSrcUrl() + ")"); this.playerNeedsPrepare = true; showControls(); this.mSidebarOverlay.show(); e.printStackTrace(); } } @Override public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { this.playerSurface.setVideoWidthHeightRatio(height == 0 ? 1 : (width * pixelWidthHeightRatio) / height); } private void showControls() { this.mControlOverlay.show(); } @Override public void onSourceSelected(BitcodinJob.Source src, BitcodinJob job) { this.contentType = src.getType() == BitcodinJob.Source.Type.DASH ? TYPE_DASH : TYPE_HLS; this.contentUri = Uri.parse(src.getSrcUrl()); this.currentJob = job; this.currentSource = src; releasePlayer(); preparePlayer(); this.mControlOverlay.setTitle("" + job.getId() + ": " + job.getInputFilename()); this.mSidebarOverlay.hide(); showControls(); } }