package org.witness.informacam.app.screens.editors; import info.guardianproject.iocipher.File; import info.guardianproject.iocipher.FileInputStream; import info.guardianproject.iocipher.camera.MediaConstants; import info.guardianproject.iocipher.camera.encoders.AACHelper; import info.guardianproject.iocipher.camera.viewer.MjpegInputStream; import info.guardianproject.iocipher.camera.viewer.MjpegView; import java.io.BufferedInputStream; import java.io.FileNotFoundException; 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.models.media.IRegion; import org.witness.informacam.models.media.IVideo; import org.witness.informacam.ui.editors.IRegionDisplay; import android.app.Activity; import android.content.res.Configuration; import android.graphics.RectF; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; 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.os.storage.OnObbStateChangeListener; import android.util.Log; import android.view.Display; import android.view.LayoutInflater; import android.view.SurfaceHolder; 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.SeekBar; import com.efor18.rangeseekbar.RangeSeekBar; import com.efor18.rangeseekbar.RangeSeekBar.OnRangeSeekBarChangeListener; public class FullScreenMJPEGViewFragment extends FullScreenViewFragment implements OnClickListener, OnVideoSizeChangedListener, SurfaceHolder.Callback, OnTouchListener, OnRangeSeekBarChangeListener<Integer> { IVideo media_; InformaCam informa; MjpegView videoView; SurfaceHolder surfaceHolder; AudioTrack at; InputStream isAudio = null; boolean useAAC = false; View mediaHolder_; LinearLayout videoControlsHolder, endpointHolder; VideoSeekBar videoSeekBar; ImageButton playPauseToggle; Uri videoUri; long duration = 0L; int currentCue = 1; private boolean isPlaying = false; 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(); } @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() }; } private void initVideo() throws IllegalArgumentException, SecurityException, IllegalStateException, IOException { try { videoView.setSource(new MjpegInputStream(new info.guardianproject.iocipher.FileInputStream(media_.dcimEntry.fileAsset.path))); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void initAudio () throws Exception { File fileAudio = null; String ioCipherAudioPath = null; String ioCipherVideoPath = media_.dcimEntry.fileAsset.path; if (ioCipherAudioPath == null) { fileAudio = new File(ioCipherVideoPath + ".pcm"); if (fileAudio.exists()) { initAudio(fileAudio.getAbsolutePath()); } } } public void initAudio(String vfsPath) throws Exception { isAudio = new BufferedInputStream(new FileInputStream(vfsPath)); int minBufferSize = AudioTrack.getMinBufferSize(MediaConstants.sAudioSampleRate, MediaConstants.sChannelConfigOut, AudioFormat.ENCODING_PCM_16BIT)*8; at = new AudioTrack(AudioManager.STREAM_MUSIC, MediaConstants.sAudioSampleRate, MediaConstants.sChannelConfigOut, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM); } public void playAudio () { new Thread () { public void run () { try { try{ byte[] music = null; music = new byte[512]; at.play(); int i = 0; while(isPlaying && (i = isAudio.read(music)) != -1) at.write(music, 0, i); } catch (IOException e) { e.printStackTrace(); } at.stop(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }.start(); } 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); } } } } @SuppressWarnings("deprecation") @Override protected void initLayout() { super.initLayout(); mediaHolder_ = LayoutInflater.from(getActivity()).inflate(R.layout.editors_mjpeg_video, null); videoView = (MjpegView) mediaHolder_.findViewById(R.id.video_view); videoView.setDisplayMode(MjpegView.SIZE_BEST_FIT); videoView.setFrameDelay(50); //we need to better sync each frame to the audio // 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(); surfaceHolder.addCallback(this); 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); videoSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (progress == 0) { pause(); initAndStart(); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); } @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"); initAndStart(); } private void initAndStart() { try { initAudio (); initVideo(); start(); } 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(); } catch (Exception 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); } public void onSeekComplete(MediaPlayer mp) { //videoSeekBar.update(); mIsSeeking = false; } @Override public void onClick(View v) { if(v == playPauseToggle) { if(videoView.isPlaying()) { pause(); } else { start(); } } } @Override public void onPause() { super.onPause(); pause(); //pause media playback } @Override public void onDestroy() { super.onDestroy(); if (at != null) { at.release(); try { isAudio.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public boolean isPlaying() { return videoView.isPlaying(); } public void pause() { playPauseToggle.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.ic_videol_play)); if (isPlaying) { videoView.stopPlayback(); if (at != null) at.stop(); } isPlaying = false; } private boolean mIsSeeking = false; public void seekTo(int pos) { if (!mIsSeeking) { /** if (pos <= (duration * (mBufferPercent/100))) { mIsSeeking = true; // mediaPlayer.seekTo(pos); }*/ } } public void start() { isPlaying = true; playPauseToggle.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.ic_videol_pause)); playAudio (); videoView.startPlayback(); } @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()); } }