package github.madmarty.madsonic.activity; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Collection; import android.app.Activity; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.text.Html; import android.util.Log; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.FrameLayout; import android.widget.Spinner; import android.widget.TextView; import android.content.Intent; import github.madmarty.madsonic.R; import github.madmarty.madsonic.view.VideoControllerView; import github.madmarty.madsonic.util.SubtitleConverter.Caption; import github.madmarty.madsonic.util.SubtitleConverter.FormatSRT; import github.madmarty.madsonic.util.SubtitleConverter.TimedTextObject; public class VideoPlayerActivity extends Activity implements SurfaceHolder.Callback, MediaPlayer.OnPreparedListener, VideoControllerView.MediaPlayerControl, OnItemSelectedListener { private static final String TAG = "VideoPlayerActivity"; private static final int SUBTITLE_DISPLAY_CHECK = 100; SurfaceView videoSurface; MediaPlayer player = new MediaPlayer(); VideoControllerView controller; String videoSource, currentSource, subtitlesSource; TimedTextObject srtSubtitles; private Handler subtitleDisplayHandler = new Handler(); AsyncTask<Void, Void, Void> subtitlesTask; Spinner bitrateSpinner; Handler bitrateHandler; int duration = 0; int offset = 0; int bitRate = 500; boolean playerReady = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_player); videoSurface = (SurfaceView) findViewById(R.id.videoSurface); SurfaceHolder videoHolder = videoSurface.getHolder(); videoHolder.addCallback(this); Intent intent = getIntent(); controller = new VideoControllerView(this); videoSource = intent.getStringExtra("source").trim(); bitrateSpinner = (Spinner) findViewById(R.id.bitrate_spinner); subtitlesSource = intent.getStringExtra("subtitles"); duration = intent.getIntExtra("duration", 0); bitrateSpinner.setOnItemSelectedListener(this); bitrateSpinner.setSelection(1); // Default 500 Kbps bitrateSpinner.setVisibility(View.INVISIBLE); // Load Video subtitlesTask = new SubtitleAsyncTask().execute(); updateUrl(0); updateDataSource(); player.setAudioStreamType(AudioManager.STREAM_MUSIC); player.setOnPreparedListener(this); // Hide soft navigation buttons videoSurface.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); } @Override protected void onDestroy() { subtitleDisplayHandler.removeCallbacks(subtitle); player.release(); super.onDestroy(); } private void updateDataSource() { if(player != null && currentSource != null) { try { player.setDataSource(currentSource); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } private void updateUrl(int seekTime) { StringBuilder builder = new StringBuilder(); builder.append(videoSource); builder.append("&maxBitRate="); builder.append(bitRate); builder.append("&timeOffset="); builder.append(seekTime); currentSource = builder.toString().trim(); } @Override public boolean onTouchEvent(MotionEvent event) { controller.show(); bitrateSpinner.setVisibility(View.VISIBLE); bitrateHandler = new Handler(); bitrateHandler.postDelayed(new Runnable(){ public void run(){ // Hide bitrate spinner and soft navigation buttons after 3 seconds bitrateSpinner.setVisibility(View.INVISIBLE); videoSurface.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); } }, VideoControllerView.UI_DEFAULT_TIMEOUT); return false; } // Implement SurfaceHolder.Callback public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } public void surfaceCreated(SurfaceHolder holder) { player.setDisplay(holder); player.setScreenOnWhilePlaying(true); player.prepareAsync(); } public void surfaceDestroyed(SurfaceHolder holder) { } // End SurfaceHolder.Callback // Implement MediaPlayer.OnPreparedListener public void onPrepared(MediaPlayer mp) { controller.setMediaPlayer(this); controller.setAnchorView((FrameLayout) findViewById(R.id.videoSurfaceContainer)); controller.setDuration(duration); player.start(); playerReady = true; } // End MediaPlayer.OnPreparedListener // Implement VideoMediaController.MediaPlayerControl public boolean canPause() { return true; } public boolean canSeekBackward() { return false; } public boolean canSeekForward() { return false; } public int getBufferPercentage() { return 0; } public int getCurrentPosition() { return player.getCurrentPosition() + offset; } public boolean isPlaying() { return player.isPlaying(); } public void pause() { player.pause(); } public void seekTo(int i) { offset = i; player.reset(); updateUrl((int)(i * 0.001)); updateDataSource(); player.prepareAsync(); } public void start() { player.start(); } public boolean isFullScreen() { return false; } public void toggleFullScreen() { } public int getDuration() { return duration * 1000; } // // Bit Rate Controller // public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { bitRate = Integer.parseInt(parent.getItemAtPosition(pos).toString()); if(playerReady) { offset = player.getCurrentPosition() + offset; updateUrl((int)(offset * 0.001)); Log.i(TAG, "Seek Time = " + (offset * 0.001)); player.reset(); updateDataSource(); player.prepareAsync(); } } public void onNothingSelected(AdapterView<?> parent) { } // // Subtitles Controller // private Runnable subtitle = new Runnable() { public void run() { if (player != null) { int currentPos = player.getCurrentPosition() + offset; Collection<Caption> subtitles = srtSubtitles.captions.values(); for(Caption caption : subtitles) { if (currentPos >= caption.start.getMilliseconds() && currentPos <= caption.end.getMilliseconds()) { onTimedText(caption); break; } else if (currentPos > caption.end.getMilliseconds()) { onTimedText(null); } } } subtitleDisplayHandler.postDelayed(this, SUBTITLE_DISPLAY_CHECK); }; }; public void onTimedText(Caption text) { TextView subtitles = (TextView) findViewById(R.id.txtSubtitles); if (text == null) { subtitles.setVisibility(View.INVISIBLE); return; } subtitles.setText(Html.fromHtml(text.content)); subtitles.setVisibility(View.VISIBLE); } public class SubtitleAsyncTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { if (subtitlesSource != null) { try { URL url = new URL(subtitlesSource); InputStream stream = url.openStream(); FormatSRT formatSRT = new FormatSRT(); srtSubtitles = formatSRT.parseFile(null, stream); subtitleDisplayHandler.post(subtitle); } catch (Exception e) { Log.e(getClass().getName(), e.getMessage(), e); } } return null; } } }