package org.witness.informacam.app.screens.editors;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.witness.informacam.InformaCam;
import org.witness.informacam.app.R;
import org.witness.informacam.app.screens.FullScreenViewFragment;
import org.witness.informacam.app.utils.Constants.App.Editor;
import org.witness.informacam.app.utils.Constants.EditorActivityListener;
import org.witness.informacam.app.utils.StreamOverHttp;
import org.witness.informacam.models.media.IRegion;
import org.witness.informacam.models.media.IRegionBounds;
import org.witness.informacam.models.media.IVideo;
import org.witness.informacam.models.media.IVideoRegion;
import org.witness.informacam.ui.editors.IRegionDisplay;
import org.witness.informacam.utils.Constants.App.Storage;
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.RectF;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnSeekCompleteListener;
import android.media.MediaPlayer.OnVideoSizeChangedListener;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.MediaController;
import android.widget.VideoView;
import com.efor18.rangeseekbar.RangeSeekBar;
import com.efor18.rangeseekbar.RangeSeekBar.OnRangeSeekBarChangeListener;
public class FullScreenVideoViewFragment extends FullScreenViewFragment implements OnCompletionListener,OnClickListener,
OnErrorListener, OnInfoListener, OnBufferingUpdateListener, OnPreparedListener, OnSeekCompleteListener,
OnVideoSizeChangedListener, SurfaceHolder.Callback, OnTouchListener, MediaController.MediaPlayerControl,
OnRangeSeekBarChangeListener<Integer> {
IVideo media_;
InformaCam informa;
//MediaMetadataRetriever retriever = new MediaMetadataRetriever();
SurfaceView videoView;
SurfaceHolder surfaceHolder;
View mediaHolder_;
MediaPlayer mediaPlayer;
MediaController mediaController;
LinearLayout videoControlsHolder, endpointHolder;
VideoSeekBar videoSeekBar;
ImageButton playPauseToggle;
Uri videoUri;
long duration = 0L;
int currentCue = 0;//half a second in
private int mLocalHostPort = 7231;
private Handler handler = new Handler();
private final static String LOG = Editor.LOG;
@Override
public void onAttach(Activity a) {
super.onAttach(a);
try {
media_ = new IVideo(((EditorActivityListener) a).media());
} catch (java.lang.InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
informa = (InformaCam)a.getApplication();
}
boolean keepRunning = true;
//StreamProxy mStreamProxy = null;
StreamOverHttp mStreamProxy = null;
int serverPort = 7231;
private void initMediaServer ()
{
closeMediaServer();
//mStreamProxy = new StreamProxy(7231);
//mStreamProxy.start();
//File f, String forceMimeType, long forceFileSize, int serverPort
File f = null;
if (media_.dcimEntry.fileAsset.source == Storage.Type.INTERNAL_STORAGE
|| media_.dcimEntry.fileAsset.source == Storage.Type.FILE_SYSTEM)
f = new File(media_.dcimEntry.fileAsset.path);
else if (media_.dcimEntry.fileAsset.source == Storage.Type.IOCIPHER)
f = new info.guardianproject.iocipher.File(media_.dcimEntry.fileAsset.path);
try {
InputStream is = informa.ioService.getStream(media_.dcimEntry.fileAsset);
mStreamProxy = new StreamOverHttp(f, media_.dcimEntry.mediaType, media_.dcimEntry.size,is,serverPort);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
// TODO Auto-generated method stub
super.onConfigurationChanged(newConfig);
Display display =getActivity().getWindowManager().getDefaultDisplay();
dims = new int[] { display.getWidth(), display.getHeight() };
initVideoPost();
}
private void initVideo() throws IllegalArgumentException, SecurityException, IllegalStateException, IOException {
if (mediaPlayer == null)
mediaPlayer = new MediaPlayer();
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnInfoListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnSeekCompleteListener(this);
mediaPlayer.setOnVideoSizeChangedListener(this);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setLooping(false);
initMediaServer();
handler.postDelayed(new Runnable ()
{
public void run ()
{
String fileName = new File(media_.dcimEntry.fileAsset.path).getName();
String urlPath = "http://localhost:" + mLocalHostPort + "/" + fileName;
videoUri = Uri.parse(urlPath);
if (mediaPlayer != null)
{
try
{
mediaPlayer.setDataSource(urlPath);
mediaPlayer.prepareAsync();
}
catch (Exception e)
{
Log.e(LOG,"can't prepare mediaplayer for: " + urlPath,e);
}
}
}
},1000);
}
private void initVideoPost ()
{
// so it fits on the screen
int videoWidth = mediaPlayer.getVideoWidth();
int videoHeight = mediaPlayer.getVideoHeight();
float videoProportion = (float) videoWidth / (float) videoHeight;
float screenProportion = (float) dims[0] / (float) dims[1];
android.view.ViewGroup.LayoutParams lp = videoView.getLayoutParams();
if (videoProportion > screenProportion) {
lp.width = dims[0];
lp.height = (int) ((float) dims[0] / videoProportion);
} else {
lp.width = (int) (videoProportion * (float) dims[1]);
lp.height = dims[1];
}
videoView.setLayoutParams(lp);
mediaPlayer.setScreenOnWhilePlaying(true);
RangeSeekBar<Integer> rsb = videoSeekBar.init(mediaPlayer);
rsb.setOnRangeSeekBarChangeListener(FullScreenVideoViewFragment.this);
endpointHolder.addView(rsb);
videoSeekBar.hideEndpoints();
playPauseToggle.setClickable(true);
mediaPlayer.start();
mediaPlayer.setVolume(1f, 1f);
//mediaPlayer.seekTo(currentCue);
//mediaPlayer.pause();
updateRegionView(mediaPlayer.getCurrentPosition());
}
private void updateRegionView(final long timestamp) {
if (media_ == null || media_.associatedRegions == null || mediaHolder == null)
return;
for(IRegion r : media_.associatedRegions) {
if(r.getRegionDisplay() != null && r.bounds.displayWidth != 0 && r.bounds.displayHeight != 0) {
IRegionDisplay rd = (IRegionDisplay) mediaHolder.getChildAt(r.getRegionDisplay().indexOnScreen);
if(timestamp >= r.bounds.startTime && timestamp <= r.bounds.endTime) {
rd.setVisibility(View.VISIBLE);
// TODO: update region display with new bounds from trail
IRegionBounds rb = ((IVideoRegion) r).getBoundsAtTime(mediaPlayer.getCurrentPosition());
Log.d(LOG, rb.asJson().toString());
} else {
rd.setVisibility(View.GONE);
}
}
}
}
@Override
public void onDetach() {
super.onDetach();
closeMediaServer();
}
@Override
public void onPause() {
super.onPause();
closeMediaServer();
}
private synchronized void closeMediaServer()
{
if (mStreamProxy != null)
mStreamProxy.close();
}
@SuppressWarnings("deprecation")
@Override
protected void initLayout() {
super.initLayout();
mediaHolder_ = LayoutInflater.from(getActivity()).inflate(R.layout.editors_video, null);
videoView = (VideoView) mediaHolder_.findViewById(R.id.video_view);
// LayoutParams vv_lp = videoView.getLayoutParams();
// vv_lp.width = dims[0];
// vv_lp.height = (int) (((float) media_.dcimEntry.exif.height) / ((float) media_.dcimEntry.exif.width) * dims[0]);
// videoView.setLayoutParams(vv_lp);
videoView.setOnTouchListener(this);
mediaHolder.addView(mediaHolder_);
surfaceHolder = videoView.getHolder();
//Log.d(LOG, "surface holder dims: " + surfaceHolder.getSurfaceFrame().width() + " x " + surfaceHolder.getSurfaceFrame().height());
surfaceHolder.addCallback(this);
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//Log.d(LOG, "video view dims: " + videoView.getWidth() + " x " + videoView.getHeight());
videoControlsHolder = (LinearLayout) mediaHolder_.findViewById(R.id.video_controls_holder);
videoSeekBar = (VideoSeekBar) mediaHolder_.findViewById(R.id.video_seek_bar);
endpointHolder = (LinearLayout) mediaHolder_.findViewById(R.id.video_seek_bar_endpoint_holder);
playPauseToggle = (ImageButton) mediaHolder_.findViewById(R.id.video_play_pause_toggle);
playPauseToggle.setOnClickListener(this);
playPauseToggle.setClickable(false);
}
@Override
public void onSelected(IRegionDisplay regionDisplay) {
((IVideoRegion) regionDisplay.parent).setTimestampInQuestion(mediaPlayer.getCurrentPosition());
setCurrentRegion(regionDisplay.parent);
videoSeekBar.showEndpoints((IVideoRegion) regionDisplay.parent);
mediaPlayer.seekTo((int) regionDisplay.bounds.startTime);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(LOG, "surfaceChanged called");
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d(LOG, "surfaceCreated Called");
surfaceHolder = holder;
try {
initVideo();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(LOG, "surfaceDestroyed called");
if (mediaPlayer != null)
{
mediaPlayer.stop();
videoSeekBar.disable();
mediaPlayer.release();
mediaPlayer = null;
}
}
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
Log.d(LOG, "onVideoSizeChanged called, new width: " + width + ", new height: " + height);
}
@Override
public void onSeekComplete(MediaPlayer mp) {
videoSeekBar.update();
mIsSeeking = false;
}
@Override
public void onPrepared(MediaPlayer mp) {
Log.d(LOG,"mediaplayer prepared");
initVideoPost ();
}
int mBufferPercent = 0;
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
mBufferPercent = percent;
Log.d(LOG,"buffering " + percent + "%");
}
@Override
public boolean onError(MediaPlayer mp, int whatInfo, int extra) {
Log.d(LOG, "onError called " + whatInfo + " (extra: " + extra + ")");
if (whatInfo == -38 || whatInfo == 1)
{
playPauseToggle.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.ic_videol_play));
currentCue = mediaPlayer.getCurrentPosition();
mediaPlayer.reset();
if (whatInfo == MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING) {
Log.d(LOG, "Media Info, Media Info Bad Interleaving " + extra);
} else if (whatInfo == MediaPlayer.MEDIA_INFO_NOT_SEEKABLE) {
Log.d(LOG, "Media Info, Media Info Not Seekable " + extra);
} else if (whatInfo == MediaPlayer.MEDIA_INFO_UNKNOWN) {
Log.d(LOG, "Media Info, Media Info Unknown " + extra);
} else if (whatInfo == MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING) {
Log.d(LOG, "MediaInfo, Media Info Video Track Lagging " + extra);
} else if (whatInfo == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
Log.d(LOG, "MediaInfo, Media Info Metadata Update " + extra);
} else if (whatInfo == MediaPlayer.MEDIA_ERROR_IO) {
Log.d(LOG, "Media Info, Media Info IO error " + extra);
} else if (whatInfo == -38) {
Log.d(LOG, "i have no clue what error -38 is");
}
}
return true;
}
@Override
public void onCompletion(MediaPlayer mp) {
Log.d(LOG, "onCompletion called");
}
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
Log.d(LOG, "onInfo called: what=" + what + "; extra=" + extra);
return false;
}
@Override
public void onClick(View v) {
if(v == playPauseToggle) {
if(mediaPlayer.isPlaying()) {
pause();
} else {
start();
}
}
}
@Override
public boolean canPause() {
return true;
}
@Override
public boolean canSeekBackward() {
return true;
}
@Override
public boolean canSeekForward() {
return (mBufferPercent == 100);
}
@Override
public int getBufferPercentage() {
return mBufferPercent;
}
@Override
public int getCurrentPosition() {
return mediaPlayer.getCurrentPosition();
}
@Override
public int getDuration() {
return mediaPlayer.getDuration();
}
@Override
public boolean isPlaying() {
return mediaPlayer.isPlaying();
}
@Override
public void pause() {
playPauseToggle.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.ic_videol_play));
videoSeekBar.pause();
mediaPlayer.pause();
}
private boolean mIsSeeking = false;
@Override
public void seekTo(int pos) {
if (!mIsSeeking)
{
if (pos <= (duration * (mBufferPercent/100)))
{
mIsSeeking = true;
mediaPlayer.seekTo(pos);
}
}
}
@Override
public void start() {
playPauseToggle.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.ic_videol_pause));
duration = mediaPlayer.getDuration();
mediaPlayer.start();
videoSeekBar.play();
}
@Override
public void onRangeSeekBarValuesChanged(RangeSeekBar<?> bar, Integer minValue, Integer maxValue) {
Log.d(LOG, "new range: " + minValue + " - " + maxValue);
if(currentRegion != null) {
currentRegion.bounds.startTime = minValue;
currentRegion.bounds.endTime = maxValue;
}
}
@Override
public void onStartTrackingTouch(RangeSeekBar<?> bar) {}
@Override
public void onStopTrackingTouch(RangeSeekBar<?> bar) {}
@Override
public int[] getSpecs() {
//Log.d(LOG, "RECALCULATING FOR VIDEO");
List<Integer> specs = new ArrayList<Integer>(Arrays.asList(ArrayUtils.toObject(super.getSpecs())));
int[] locationInWindow = new int[2];
videoView.getLocationInWindow(locationInWindow);
for(int i : locationInWindow) {
specs.add(i);
}
// these might not be needed
specs.add(videoView.getWidth());
specs.add(videoView.getHeight());
Log.d(LOG, "position on screen : " + locationInWindow[0] + ", " + locationInWindow[1]);
return ArrayUtils.toPrimitive(specs.toArray(new Integer[specs.size()]));
}
@Override
public RectF getImageBounds()
{
return new RectF(0,0,videoView.getWidth(),videoView.getHeight());
}
@Override
public int getAudioSessionId() {
return 1;
}
}