Statistics
| Branch: | Tag: | Revision:

securesmartcam / app / src / main / java / org / witness / obscuracam / video / VideoEditor.java @ 41590feb

History | View | Annotate | Download (53.6 KB)

1
/*
2
 * 
3
 * To Do:
4
 * 
5
 * Handles on SeekBar - Not quite right
6
 * Editing region, not quite right
7
 * RegionBarArea will be graphical display of region tracks, no editing, just selecting
8
 * 
9
 */
10

    
11
package org.witness.obscuracam.video;
12

    
13
import android.content.ContentUris;
14
import android.content.Context;
15
import android.content.Intent;
16
import android.content.SharedPreferences;
17
import android.content.res.Configuration;
18
import android.database.Cursor;
19
import android.graphics.Bitmap;
20
import android.graphics.BitmapFactory;
21
import android.graphics.Canvas;
22
import android.graphics.Color;
23
import android.graphics.ColorMatrix;
24
import android.graphics.ColorMatrixColorFilter;
25
import android.graphics.Paint;
26
import android.graphics.Paint.Style;
27
import android.graphics.PorterDuff.Mode;
28
import android.graphics.PorterDuffXfermode;
29
import android.graphics.RectF;
30
import android.media.MediaMetadataRetriever;
31
import android.media.MediaPlayer;
32
import android.media.MediaPlayer.OnBufferingUpdateListener;
33
import android.media.MediaPlayer.OnCompletionListener;
34
import android.media.MediaPlayer.OnErrorListener;
35
import android.media.MediaPlayer.OnInfoListener;
36
import android.media.MediaPlayer.OnPreparedListener;
37
import android.media.MediaPlayer.OnSeekCompleteListener;
38
import android.media.MediaPlayer.OnVideoSizeChangedListener;
39
import android.net.Uri;
40
import android.os.Bundle;
41
import android.os.Environment;
42
import android.os.Handler;
43
import android.os.Message;
44
import android.os.PowerManager;
45
import android.os.StrictMode;
46
import android.preference.PreferenceManager;
47
import android.provider.MediaStore;
48
import android.support.design.widget.Snackbar;
49
import android.support.v7.app.AppCompatActivity;
50
import android.util.Log;
51
import android.view.Display;
52
import android.view.Menu;
53
import android.view.MenuInflater;
54
import android.view.MenuItem;
55
import android.view.MotionEvent;
56
import android.view.SurfaceHolder;
57
import android.view.View;
58
import android.view.View.OnClickListener;
59
import android.view.View.OnTouchListener;
60
import android.widget.ImageButton;
61
import android.widget.ImageView;
62
import android.widget.MediaController;
63
import android.widget.ProgressBar;
64
import android.widget.SeekBar;
65
import android.widget.Toast;
66
import android.widget.VideoView;
67

    
68
import com.github.hiteshsondhi88.libffmpeg.ExecuteBinaryResponseHandler;
69

    
70

    
71
import org.witness.obscuracam.photo.detect.AndroidFaceDetection;
72
import org.witness.obscuracam.photo.detect.DetectedFace;
73
import org.witness.obscuracam.photo.filters.PixelizeObscure;
74
import org.witness.obscuracam.ObscuraApp;
75
import org.witness.sscphase1.R;
76

    
77
import java.io.File;
78
import java.io.IOException;
79
import java.text.SimpleDateFormat;
80
import java.util.ArrayList;
81
import java.util.Date;
82
import java.util.Iterator;
83

    
84
public class VideoEditor extends AppCompatActivity implements
85
        OnCompletionListener, OnErrorListener, OnInfoListener,
86
        OnBufferingUpdateListener, OnPreparedListener, OnSeekCompleteListener,
87
        OnVideoSizeChangedListener, SurfaceHolder.Callback,
88
        MediaController.MediaPlayerControl, OnTouchListener,
89
        InOutPlayheadSeekBar.InOutPlayheadSeekBarChangeListener {
90

    
91
    public static final String LOGTAG = ObscuraApp.TAG;
92

    
93
    public static final int SHARE = 1;
94

    
95
    private final static float REGION_CORNER_SIZE = 26;
96

    
97
    private final static String MIME_TYPE_MP4 = "video/mp4";
98
    private final static String MIME_TYPE_VIDEO = "video/*";
99

    
100
    private final static int FACE_TIME_BUFFER = 2000;
101

    
102
    private final static int HUMAN_OFFSET_BUFFER = 50;
103

    
104
    int completeActionFlag = 3;
105

    
106
    Uri originalVideoUri;
107
    Uri currentUri;
108
    boolean mIsPreview = false;
109

    
110
    File fileExternDir;
111
    File redactSettingsFile;
112
    File saveFile;
113
    File recordingFile;
114

    
115
    Display currentDisplay;
116

    
117
    VideoView videoView;
118
    SurfaceHolder surfaceHolder;
119
    MediaPlayer mediaPlayer;
120

    
121
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
122
    PixelizeObscure po = new PixelizeObscure();
123

    
124
    ImageView regionsView;
125
    Bitmap obscuredBmp;
126
    Canvas obscuredCanvas;
127
    Paint obscuredPaint;
128
    Paint selectedPaint;
129

    
130
    Bitmap bitmapPixel;
131

    
132
    InOutPlayheadSeekBar mVideoSeekbar;
133
    //RegionBarArea regionBarArea;
134

    
135
    int videoWidth = 0;
136
    int videoHeight = 0;
137

    
138
    ImageButton playPauseButton;
139

    
140
    private ArrayList<RegionTrail> obscureTrails = new ArrayList<RegionTrail>();
141
    private RegionTrail activeRegionTrail;
142
    private ObscureRegion activeRegion;
143

    
144
    boolean mAutoDetectEnabled = false;
145
    boolean eyesOnly = false;
146
    int autoDetectTimeInterval = 300; //ms
147

    
148
    FFMPEGWrapper ffmpeg;
149
    boolean mCompressVideo = true;
150
    int mObscureAudioAmount = 0;
151
    int mObscureVideoAmount = 0;
152

    
153
    int timeNudgeOffset = 2;
154

    
155
    float vRatio;
156

    
157
    int outFrameRate = -1;
158
    int outBitRate = -1;
159
    String outFormat = null;
160
    String outAcodec = null;
161
    String outVcodec = null;
162
    int outVWidth = -1;
163
    int outVHeight = -1;
164

    
165
    private final static String DEFAULT_OUT_FPS = "15";
166
    private final static String DEFAULT_OUT_RATE = "300";
167
    private final static String DEFAULT_OUT_FORMAT = "mp4";
168
    private final static String DEFAULT_OUT_VCODEC = "libx264";
169
    private final static String DEFAULT_OUT_ACODEC = "copy";
170
    private final static String DEFAULT_OUT_WIDTH = "480";
171
    private final static String DEFAULT_OUT_HEIGHT = "320";
172

    
173
    private ProgressBar mProgressBar;
174

    
175
    private Handler mHandler = new Handler() {
176
        public void handleMessage(Message msg) {
177
            switch (msg.what) {
178
                case 0: //status
179

    
180

    
181
                    break;
182
                case 1: //status
183

    
184

    
185
                    try {
186
                        if (msg.getData().getString("time") != null) {
187
                            //00:00:05.01
188
                            String time = msg.getData().getString("time");
189
                            time = time.substring(0,time.indexOf("."));
190
                            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
191
                            Date dateProgress = sdf.parse(time);
192
                            long progress = dateProgress.getSeconds()*1000;
193
                            int percentComplete = (int)((((float)progress)/((float)mDuration))*100f);
194
                            mProgressBar.setProgress(percentComplete);
195
                        }
196
                    }
197
                    catch (Exception e)
198
                    {
199
                        //handle text parsing errors
200
                    }
201

    
202
                    break;
203

    
204
                case 2: //cancelled
205
                    mCancelled = true;
206
                    mAutoDetectEnabled = false;
207
                    killVideoProcessor();
208
                    mProgressBar.setVisibility(View.GONE);
209
                    break;
210

    
211
                case 3: //completed
212
                    askPostProcessAction();
213

    
214
                    mProgressBar.setVisibility(View.GONE);
215
                    break;
216

    
217
                case 5:
218
                    updateRegionDisplay(mediaPlayer.getCurrentPosition());
219
                    break;
220
                default:
221
                    super.handleMessage(msg);
222
            }
223
        }
224
    };
225

    
226
    private boolean mCancelled = false;
227

    
228

    
229
    private int mDuration;
230

    
231
    @Override
232
    public void onCreate(Bundle savedInstanceState) {
233
        super.onCreate(savedInstanceState);
234

    
235

    
236
        getSupportActionBar().setTitle("");
237
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
238

    
239
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
240
                .detectAll()
241
                .penaltyLog()
242
                .build());
243

    
244

    
245
        setContentView(R.layout.videoeditor);
246

    
247

    
248
        if (getIntent() != null) {
249
            // Passed in from ObscuraApp
250
            originalVideoUri = getIntent().getData();
251

    
252
            if (originalVideoUri == null) {
253
                if (getIntent().hasExtra(Intent.EXTRA_STREAM)) {
254
                    originalVideoUri = (Uri) getIntent().getExtras().get(Intent.EXTRA_STREAM);
255
                }
256
            }
257

    
258
            if (originalVideoUri == null) {
259
                if (savedInstanceState.getString("path") != null) {
260
                    originalVideoUri = Uri.fromFile(new File(savedInstanceState.getString("path")));
261
                    recordingFile = new File(savedInstanceState.getString("path"));
262
                } else {
263
                    finish();
264
                    return;
265
                }
266
            } else {
267

    
268
                recordingFile = new File(pullPathFromUri(originalVideoUri));
269
            }
270
        }
271

    
272
        fileExternDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
273

    
274
        mAutoDetectEnabled = false; //first time do autodetect
275

    
276
        setPrefs();
277

    
278
        try {
279
            retriever.setDataSource(recordingFile.getAbsolutePath());
280

    
281
            bitmapPixel = BitmapFactory.decodeResource(getResources(),
282
                    R.drawable.ic_context_pixelate);
283

    
284
        } catch (RuntimeException re) {
285
            Toast.makeText(this, "There was an error with the video file", Toast.LENGTH_LONG).show();
286
            finish();
287
        }
288
    }
289

    
290
    private void resetMediaPlayer(Uri videoUri) {
291
        Log.i(LOGTAG, "releasing/loading media Player");
292

    
293
        mediaPlayer.release();
294

    
295
        loadMedia(videoUri);
296

    
297
        mediaPlayer.setDisplay(surfaceHolder);
298

    
299
        mediaPlayer.setScreenOnWhilePlaying(true);
300

    
301
        try {
302
            mediaPlayer.prepare();
303
            mDuration = mediaPlayer.getDuration();
304

    
305
            mVideoSeekbar.setMax(mDuration);
306

    
307
        } catch (Exception e) {
308
            Log.v(LOGTAG, "IllegalStateException " + e.getMessage());
309
            finish();
310
        }
311

    
312
        seekTo(0);
313

    
314
    }
315

    
316
    private void loadMedia(Uri uriVideo) {
317

    
318
        currentUri = uriVideo;
319

    
320
        mediaPlayer = new MediaPlayer();
321
        mediaPlayer.setOnCompletionListener(this);
322
        mediaPlayer.setOnErrorListener(this);
323
        mediaPlayer.setOnInfoListener(this);
324
        mediaPlayer.setOnPreparedListener(this);
325
        mediaPlayer.setOnSeekCompleteListener(this);
326
        mediaPlayer.setOnVideoSizeChangedListener(this);
327
        mediaPlayer.setOnBufferingUpdateListener(this);
328

    
329
        mediaPlayer.setLooping(true);
330

    
331
        try {
332
            mediaPlayer.setDataSource(this, currentUri);
333

    
334
        } catch (IllegalArgumentException e) {
335
            Log.e(LOGTAG, originalVideoUri.toString() + ": " + e.getMessage());
336

    
337
        } catch (IllegalStateException e) {
338
            Log.e(LOGTAG, originalVideoUri.toString() + ": " + e.getMessage());
339

    
340
        } catch (IOException e) {
341
            Log.e(LOGTAG, originalVideoUri.toString() + ": " + e.getMessage());
342

    
343
        }
344

    
345
    }
346

    
347
    @Override
348
    public void onSaveInstanceState(Bundle savedInstanceState) {
349

    
350
        savedInstanceState.putString("path", recordingFile.getAbsolutePath());
351

    
352
        super.onSaveInstanceState(savedInstanceState);
353
    }
354

    
355
    @Override
356
    public void surfaceCreated(SurfaceHolder holder) {
357

    
358
        Log.v(LOGTAG, "surfaceCreated Called");
359
        if (mediaPlayer != null) {
360

    
361
            mediaPlayer.setDisplay(holder);
362
            mediaPlayer.setScreenOnWhilePlaying(true);
363

    
364
            try {
365
                mediaPlayer.prepare();
366
                mDuration = mediaPlayer.getDuration();
367

    
368
                mVideoSeekbar.setMax(mDuration);
369

    
370
            } catch (Exception e) {
371
                Log.v(LOGTAG, "IllegalStateException " + e.getMessage());
372
                finish();
373
            }
374

    
375

    
376
            updateVideoLayout();
377

    
378
        }
379

    
380
    }
381

    
382
    @Override
383
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
384

    
385
        Log.i(LOGTAG, "SurfaceHolder changed");
386

    
387

    
388
    }
389

    
390
    @Override
391
    public void surfaceDestroyed(SurfaceHolder holder) {
392

    
393
        Log.i(LOGTAG, "SurfaceHolder destroyed");
394

    
395
    }
396

    
397
    @Override
398
    public void onCompletion(MediaPlayer mp) {
399
        Log.i(LOGTAG, "onCompletion Called");
400

    
401
        playPauseButton.setImageDrawable(this.getResources().getDrawable(android.R.drawable.ic_media_play));
402
    }
403

    
404
    @Override
405
    public boolean onError(MediaPlayer mp, int whatError, int extra) {
406
        Log.e(LOGTAG, "onError Called");
407
        if (whatError == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
408
            Log.e(LOGTAG, "Media Error, Server Died " + extra);
409

    
410
            boolean wasAutoDetect = mAutoDetectEnabled;
411

    
412
            //if (wasAutoDetect)
413
            mAutoDetectEnabled = false;
414

    
415
            resetMediaPlayer(originalVideoUri);
416

    
417

    
418
        } else if (whatError == MediaPlayer.MEDIA_ERROR_UNKNOWN) {
419
            Log.e(LOGTAG, "Media Error, Error Unknown " + extra);
420
        }
421

    
422

    
423
        return false;
424
    }
425

    
426
    @Override
427
    public boolean onInfo(MediaPlayer mp, int whatInfo, int extra) {
428
        if (whatInfo == MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING) {
429
            Log.v(LOGTAG, "Media Info, Media Info Bad Interleaving " + extra);
430
        } else if (whatInfo == MediaPlayer.MEDIA_INFO_NOT_SEEKABLE) {
431
            Log.v(LOGTAG, "Media Info, Media Info Not Seekable " + extra);
432
        } else if (whatInfo == MediaPlayer.MEDIA_INFO_UNKNOWN) {
433
            Log.v(LOGTAG, "Media Info, Media Info Unknown " + extra);
434
        } else if (whatInfo == MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING) {
435
            Log.v(LOGTAG, "MediaInfo, Media Info Video Track Lagging " + extra);
436
        } else if (whatInfo == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
437
            Log.v(LOGTAG, "MediaInfo, Media Info Metadata Update " + extra);
438
        }
439

    
440
        return false;
441
    }
442

    
443
    public void onPrepared(MediaPlayer mp) {
444
        //        Log.v(LOGTAG, "onPrepared Called");
445

    
446
        updateVideoLayout();
447
        mediaPlayer.seekTo(1);
448

    
449

    
450
    }
451

    
452
    private void beginAutoDetect() {
453
        mAutoDetectEnabled = true;
454

    
455
        new Thread(doAutoDetect).start();
456

    
457
    }
458

    
459
    public void onSeekComplete(MediaPlayer mp) {
460

    
461
        if (!mediaPlayer.isPlaying()) {
462
            mediaPlayer.start();
463
            mediaPlayer.pause();
464
            playPauseButton.setImageDrawable(this.getResources().getDrawable(android.R.drawable.ic_media_play));
465
        }
466
    }
467

    
468
    public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
469
        Log.v(LOGTAG, "onVideoSizeChanged Called");
470

    
471
        videoWidth = mp.getVideoWidth();
472
        videoHeight = mp.getVideoHeight();
473

    
474
        updateVideoLayout();
475

    
476
    }
477

    
478
    /*
479
     * Handling screen configuration changes ourselves, we don't want the activity to restart on rotation
480
     */
481
    @Override
482
    public void onConfigurationChanged(Configuration conf) {
483
        super.onConfigurationChanged(conf);
484

    
485

    
486
    }
487

    
488
    private boolean updateVideoLayout() {
489
        //Get the dimensions of the video
490
        int videoWidth = mediaPlayer.getVideoWidth();
491
        int videoHeight = mediaPlayer.getVideoHeight();
492
        //  Log.v(LOGTAG, "video size: " + videoWidth + "x" + videoHeight);
493

    
494
        if (videoWidth > 0 && videoHeight > 0) {
495
            //Get the width of the screen
496
            int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
497

    
498
            //Get the SurfaceView layout parameters
499
            android.view.ViewGroup.LayoutParams lp = videoView.getLayoutParams();
500

    
501
            //Set the width of the SurfaceView to the width of the screen
502
            lp.width = screenWidth;
503

    
504
            //Set the height of the SurfaceView to match the aspect ratio of the video
505
            //be sure to cast these as floats otherwise the calculation will likely be 0
506

    
507
            int videoScaledHeight = (int) (((float) videoHeight) / ((float) videoWidth) * (float) screenWidth);
508

    
509
            lp.height = videoScaledHeight;
510

    
511
            //Commit the layout parameters
512
            videoView.setLayoutParams(lp);
513
            regionsView.setLayoutParams(lp);
514

    
515
            // Log.v(LOGTAG, "view size: " + screenWidth + "x" + videoScaledHeight);
516

    
517
            vRatio = ((float) screenWidth) / ((float) videoWidth);
518

    
519
            //        Log.v(LOGTAG, "video/screen ration: " + vRatio);
520

    
521
            return true;
522
        } else
523
            return false;
524
    }
525

    
526
    public void onBufferingUpdate(MediaPlayer mp, int bufferedPercent) {
527
        Log.v(LOGTAG, "MediaPlayer Buffering: " + bufferedPercent + "%");
528
    }
529

    
530
    public boolean canPause() {
531
        return true;
532
    }
533

    
534
    public boolean canSeekBackward() {
535
        return true;
536
    }
537

    
538
    public boolean canSeekForward() {
539
        return true;
540
    }
541

    
542
    @Override
543
    public int getBufferPercentage() {
544
        return 0;
545
    }
546

    
547
    @Override
548
    public int getCurrentPosition() {
549
        return mediaPlayer.getCurrentPosition();
550
    }
551

    
552
    @Override
553
    public int getDuration() {
554
        Log.v(LOGTAG, "Calling our getDuration method");
555
        return mediaPlayer.getDuration();
556
    }
557

    
558
    @Override
559
    public boolean isPlaying() {
560
        Log.v(LOGTAG, "Calling our isPlaying method");
561
        return mediaPlayer.isPlaying();
562
    }
563

    
564
    @Override
565
    public void pause() {
566
        Log.v(LOGTAG, "Calling our pause method");
567
        if (mediaPlayer.isPlaying()) {
568
            mediaPlayer.pause();
569
            playPauseButton.setImageDrawable(this.getResources().getDrawable(android.R.drawable.ic_media_play));
570
        }
571
    }
572

    
573
    @Override
574
    public void seekTo(int pos) {
575
        mediaPlayer.seekTo(pos);
576

    
577
    }
578

    
579
    @Override
580
    public void start() {
581
        Log.v(LOGTAG, "Calling our start method");
582
        mediaPlayer.start();
583

    
584
        playPauseButton.setImageDrawable(this.getResources().getDrawable(android.R.drawable.ic_media_pause));
585

    
586
        mHandler.post(updatePlayProgress);
587

    
588

    
589
    }
590

    
591
    private Runnable doAutoDetect = new Runnable() {
592
        public void run() {
593

    
594
            try {
595

    
596
                if (mediaPlayer != null && mAutoDetectEnabled) {
597
                    // mediaPlayer.start();
598

    
599
                    //turn volume off
600
                    // mediaPlayer.setVolume(0f, 0f);
601

    
602
                    for (int f = 0; f < mDuration && mAutoDetectEnabled; f += autoDetectTimeInterval) {
603
                        try {
604
                            seekTo(f);
605

    
606
                            mVideoSeekbar.setProgress(mediaPlayer.getCurrentPosition());
607

    
608
                            //Bitmap bmp = getVideoFrame(rPath,f*1000);
609

    
610
                            Bitmap bmp = retriever.getFrameAtTime(f * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
611

    
612
                            if (bmp == null) {
613
                                resetMediaPlayer(originalVideoUri);
614
                                break;
615
                            } else {
616
                                autoDetectFrame(bmp, f, FACE_TIME_BUFFER, mDuration, eyesOnly);
617
                            }
618
                        } catch (Exception e) {
619
                            Log.v(LOGTAG, "error occured on frame " + f, e);
620

    
621
                        }
622
                    }
623

    
624
                    //turn volume on
625
                    //  mediaPlayer.setVolume(1f, 1f);
626

    
627
                    mediaPlayer.seekTo(0);
628
                   // progressBar.setProgress(mediaPlayer.getCurrentPosition());
629
                    // mediaPlayer.pause();
630

    
631

    
632
                }
633
            } catch (Exception e) {
634
                Log.e(LOGTAG, "autodetect errored out", e);
635
            } finally {
636
                mAutoDetectEnabled = false;
637
                Message msg = mHandler.obtainMessage(0);
638
                mHandler.sendMessage(msg);
639

    
640
            }
641

    
642
        }
643
    };
644

    
645
    private Runnable updatePlayProgress = new Runnable() {
646
        public void run() {
647

    
648
            try {
649
                if (mediaPlayer != null && mediaPlayer.isPlaying()) {
650
                    int curr = mediaPlayer.getCurrentPosition();
651
                    mVideoSeekbar.setProgress(curr);
652
                    updateRegionDisplay(curr);
653
                    mHandler.post(this);
654
                }
655

    
656
            } catch (Exception e) {
657
                Log.e(LOGTAG, "autoplay errored out", e);
658
            }
659
        }
660
    };
661

    
662
    private void updateRegionDisplay(int currentTime) {
663

    
664
        validateRegionView();
665
        clearRects();
666

    
667
        for (RegionTrail regionTrail : obscureTrails) {
668
            ;
669
            ObscureRegion region;
670

    
671
            if ((region = regionTrail.getCurrentRegion(currentTime, regionTrail.isDoTweening())) != null) {
672
                int currentColor = Color.WHITE;
673
                boolean selected = regionTrail == activeRegionTrail;
674

    
675
                if (selected) {
676
                    currentColor = Color.GREEN;
677
                    displayRegionTrail(regionTrail, selected, currentColor, currentTime);
678
                }
679

    
680
                displayRegion(region, selected, currentColor, regionTrail.getObscureMode());
681
            }
682
        }
683

    
684

    
685
        regionsView.invalidate();
686
        //seekBar.invalidate();
687
    }
688

    
689
    private void validateRegionView() {
690

    
691
        if (obscuredBmp == null && regionsView.getWidth() > 0 && regionsView.getHeight() > 0) {
692
            //        Log.v(LOGTAG,"obscuredBmp is null, creating it now");
693
            obscuredBmp = Bitmap.createBitmap(regionsView.getWidth(), regionsView.getHeight(), Bitmap.Config.ARGB_8888);
694
            obscuredCanvas = new Canvas(obscuredBmp);
695
            regionsView.setImageBitmap(obscuredBmp);
696
        }
697
    }
698

    
699
    private void displayRegionTrail(RegionTrail trail, boolean selected, int color, int currentTime) {
700

    
701

    
702
        RectF lastRect = null;
703

    
704
        obscuredPaint.setStyle(Style.FILL);
705
        obscuredPaint.setColor(color);
706
        obscuredPaint.setStrokeWidth(10f);
707

    
708
        for (Integer regionKey : trail.getRegionKeys()) {
709

    
710
            ObscureRegion region = trail.getRegion(regionKey);
711

    
712
            if (region.timeStamp < currentTime) {
713
                int alpha = 150;//Math.min(255,Math.max(0, ((currentTime - region.timeStamp)/1000)));
714

    
715
                RectF nRect = new RectF();
716
                nRect.set(region.getBounds());
717
                nRect.left *= vRatio;
718
                nRect.right *= vRatio;
719
                nRect.top *= vRatio;
720
                nRect.bottom *= vRatio;
721

    
722
                obscuredPaint.setAlpha(alpha);
723

    
724
                if (lastRect != null) {
725
                    obscuredCanvas.drawLine(lastRect.centerX(), lastRect.centerY(), nRect.centerX(), nRect.centerY(), obscuredPaint);
726
                }
727

    
728
                lastRect = nRect;
729
            }
730
        }
731

    
732

    
733
    }
734

    
735

    
736
    private void displayRegion(ObscureRegion region, boolean selected, int color, String mode) {
737

    
738
        RectF paintingRect = new RectF();
739
        paintingRect.set(region.getBounds());
740
        paintingRect.left *= vRatio;
741
        paintingRect.right *= vRatio;
742
        paintingRect.top *= vRatio;
743
        paintingRect.bottom *= vRatio;
744

    
745
        if (mode.equals(RegionTrail.OBSCURE_MODE_PIXELATE)) {
746
            obscuredPaint.setAlpha(150);
747
            obscuredCanvas.drawBitmap(bitmapPixel, null, paintingRect, obscuredPaint);
748

    
749

    
750
        } else if (mode.equals(RegionTrail.OBSCURE_MODE_REDACT)) {
751

    
752
            obscuredPaint.setStyle(Style.FILL);
753
            obscuredPaint.setColor(Color.BLACK);
754
            obscuredPaint.setAlpha(150);
755

    
756
            obscuredCanvas.drawRect(paintingRect, obscuredPaint);
757
        }
758

    
759
        obscuredPaint.setStyle(Style.STROKE);
760
        obscuredPaint.setStrokeWidth(10f);
761
        obscuredPaint.setColor(color);
762

    
763
        obscuredCanvas.drawRect(paintingRect, obscuredPaint);
764

    
765

    
766
    }
767

    
768
    private void clearRects() {
769
        Paint clearPaint = new Paint();
770
        clearPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
771

    
772
        if (obscuredCanvas != null)
773
            obscuredCanvas.drawPaint(clearPaint);
774
    }
775

    
776
    int fingerCount = 0;
777
    int regionCornerMode = 0;
778
    float downX = -1;
779
    float downY = -1;
780
    float MIN_MOVE = 10;
781

    
782
    public static final int NONE = 0;
783
    public static final int DRAG = 1;
784
    //int mode = NONE;
785

    
786
    public ObscureRegion findRegion(float x, float y, int currentTime) {
787
        ObscureRegion region = null;
788

    
789
        if (activeRegion != null && activeRegion.getRectF().contains(x, y))
790
            return activeRegion;
791

    
792
        for (RegionTrail regionTrail : obscureTrails) {
793
            if (currentTime != -1) {
794
                region = regionTrail.getCurrentRegion(currentTime, false);
795
                if (region != null && region.getRectF().contains(x, y)) {
796
                    return region;
797
                }
798
            } else {
799
                for (Integer regionKey : regionTrail.getRegionKeys()) {
800
                    region = regionTrail.getRegion(regionKey);
801

    
802
                    if (region.getRectF().contains(x, y)) {
803
                        return region;
804
                    }
805
                }
806
            }
807
        }
808

    
809
        return null;
810
    }
811

    
812

    
813
    @Override
814
    public boolean onTouch(View v, MotionEvent event) {
815

    
816
        boolean handled = false;
817

    
818
        if (v == mVideoSeekbar) {
819

    
820
            if (currentUri != originalVideoUri)
821
            {
822
                resetMediaPlayer(originalVideoUri);
823
                seekTo(0);
824
            }
825
            else {
826

    
827
                // It's the progress bar/scrubber
828
                if (event.getAction() == android.view.MotionEvent.ACTION_DOWN) {
829
                    start();
830
                } else if (event.getAction() == android.view.MotionEvent.ACTION_UP) {
831
                    pause();
832

    
833
                }
834

    
835

    
836
                mediaPlayer.seekTo(mVideoSeekbar.getProgress());
837
                // Attempt to get the player to update it's view - NOT WORKING
838
            }
839

    
840
            handled = false; // The progress bar doesn't get it if we have true here
841
        } else {
842
            float x = event.getX() / vRatio;
843
            float y = event.getY() / vRatio;
844

    
845
            fingerCount = event.getPointerCount();
846

    
847
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
848

    
849
                case MotionEvent.ACTION_DOWN:
850

    
851
                    downX = x;
852
                    downY = y;
853

    
854
                    ObscureRegion newActiveRegion = findRegion(x, y, mediaPlayer.getCurrentPosition());
855

    
856
                    if (newActiveRegion != null) {
857
                        activeRegionTrail = newActiveRegion.getRegionTrail();
858

    
859
                      //  updateProgressBar(activeRegionTrail);
860

    
861
                        activeRegion = newActiveRegion;
862

    
863
                        if (fingerCount == 3)
864
                        {
865
                            obscureTrails.remove(activeRegionTrail);
866
                            activeRegionTrail = null;
867
                            activeRegion = null;
868
                        }
869
                    } else {
870

    
871
                        activeRegion = makeNewRegion(fingerCount, x, y, event, HUMAN_OFFSET_BUFFER);
872

    
873
                        if (activeRegion != null) {
874
                            activeRegionTrail = findIntersectTrail(activeRegion, mediaPlayer.getCurrentPosition());
875

    
876
                            if (activeRegionTrail == null) {
877
                                activeRegionTrail = new RegionTrail(0, mDuration);
878
                                obscureTrails.add(activeRegionTrail);
879
                            }
880

    
881
                            activeRegionTrail.addRegion(activeRegion);
882

    
883
                            updateProgressBar(activeRegionTrail);
884
                        }
885
                    }
886

    
887

    
888
                    handled = true;
889

    
890
                    break;
891

    
892
                case MotionEvent.ACTION_UP:
893

    
894
                    activeRegion = null;
895

    
896
                    break;
897

    
898
                case MotionEvent.ACTION_MOVE:
899
                    // Calculate distance moved
900

    
901

    
902
                    if (Math.abs(x - downX) > MIN_MOVE
903
                            || Math.abs(y - downY) > MIN_MOVE) {
904

    
905
                        if (activeRegion != null && (!mediaPlayer.isPlaying())) {
906
                            ObscureRegion oRegion = makeNewRegion(fingerCount, x, y, event, HUMAN_OFFSET_BUFFER);
907

    
908
                            activeRegion.moveRegion(oRegion.sx, oRegion.sy, oRegion.ex, oRegion.ey);
909

    
910
                        } else {
911
                            activeRegion = makeNewRegion(fingerCount, x, y, event, HUMAN_OFFSET_BUFFER);
912

    
913
                            if (activeRegion != null)
914
                                activeRegionTrail.addRegion(activeRegion);
915
                        }
916
                    }
917

    
918
                    handled = true;
919

    
920

    
921
                    break;
922

    
923
            }
924
        }
925

    
926
        updateRegionDisplay(mediaPlayer.getCurrentPosition());
927

    
928

    
929
        return handled; // indicate event was handled
930
    }
931

    
932
    private ObscureRegion makeNewRegion(int fingerCount, float x, float y, MotionEvent event, int timeOffset) {
933
        ObscureRegion result = null;
934

    
935
        int regionTime = mediaPlayer.getCurrentPosition() - timeOffset;
936

    
937
        if (fingerCount > 1 && event != null) {
938
            float[] points = {event.getX(0) / vRatio, event.getY(0) / vRatio, event.getX(1) / vRatio, event.getY(1) / vRatio};
939

    
940
            float startX = Math.min(points[0], points[2]);
941
            float endX = Math.max(points[0], points[2]);
942
            float startY = Math.min(points[1], points[3]);
943
            float endY = Math.max(points[1], points[3]);
944

    
945
            result = new ObscureRegion(regionTime, startX, startY, endX, endY);
946

    
947
        } else {
948
            result = new ObscureRegion(mediaPlayer.getCurrentPosition(), x, y);
949

    
950
            if (activeRegion != null && RectF.intersects(activeRegion.getBounds(), result.getBounds())) {
951
                //newActiveRegion.ex = newActiveRegion.sx + (activeRegion.ex-activeRegion.sx);
952
                //newActiveRegion.ey = newActiveRegion.sy + (activeRegion.ey-activeRegion.sy);
953
                float arWidth = activeRegion.ex - activeRegion.sx;
954
                float arHeight = activeRegion.ey - activeRegion.sy;
955

    
956
                float sx = x - arWidth / 2;
957
                float ex = sx + arWidth;
958

    
959
                float sy = y - arHeight / 2;
960
                float ey = sy + arHeight;
961

    
962
                result = new ObscureRegion(regionTime, sx, sy, ex, ey);
963

    
964
            }
965

    
966

    
967
        }
968

    
969
        return result;
970

    
971
    }
972

    
973
    private void updateProgressBar(RegionTrail rTrail) {
974
     //   progressBar.setThumbsActive((int) ((double) rTrail.getStartTime() / (double) mDuration * 100), (int) ((double) rTrail.getEndTime() / (double) mDuration * 100));
975

    
976
    }
977

    
978
    public String pullPathFromUri(Uri originalUri) {
979
        String originalVideoFilePath = originalUri.toString();
980
        String[] columnsToSelect = {MediaStore.Video.Media.DATA};
981
        Cursor videoCursor = getContentResolver().query(originalUri, columnsToSelect, null, null, null);
982
        if (videoCursor != null && videoCursor.getCount() == 1) {
983
            videoCursor.moveToFirst();
984
            originalVideoFilePath = videoCursor.getString(videoCursor.getColumnIndex(MediaStore.Images.Media.DATA));
985
        }
986

    
987
        return originalVideoFilePath;
988
    }
989

    
990
    private File createCleanSavePath(String format) {
991

    
992
        try {
993
            saveFile = File.createTempFile("obscuracam-output", '.' + format, fileExternDir);
994
            redactSettingsFile = new File(fileExternDir, saveFile.getName() + ".txt");
995

    
996
            return redactSettingsFile;
997
        } catch (IOException e) {
998
            e.printStackTrace();
999
            return null;
1000
        }
1001
    }
1002

    
1003

    
1004
    public final static int PLAY = 1;
1005
    public final static int STOP = 2;
1006
    public final static int PROCESS = 3;
1007

    
1008
    @Override
1009
    public boolean onCreateOptionsMenu(Menu menu) {
1010
        MenuInflater inflater = getMenuInflater();
1011
        inflater.inflate(R.menu.video_editor_menu, menu);
1012

    
1013
        return true;
1014
    }
1015

    
1016

    
1017
    @Override
1018
    public boolean onOptionsItemSelected(MenuItem item) {
1019

    
1020
        switch (item.getItemId()) {
1021

    
1022

    
1023
            case R.id.menu_save:
1024

    
1025
                resetMediaPlayer(originalVideoUri);
1026
                processVideo(false);
1027

    
1028
                return true;
1029

    
1030
            case R.id.menu_share:
1031

    
1032
                shareVideo();
1033

    
1034
                return true;
1035

    
1036
            case android.R.id.home:
1037
                // Pull up about screen
1038
                finish();
1039

    
1040
                return true;
1041

    
1042

    
1043
            default:
1044
                return false;
1045
        }
1046
    }
1047

    
1048

    
1049
    PowerManager.WakeLock wl;
1050

    
1051
    private synchronized void processVideo(boolean isPreview) {
1052

    
1053
        if (ffmpeg != null
1054
        && ffmpeg.getFFMPEG().isFFmpegCommandRunning())
1055
            ffmpeg.getFFMPEG().killRunningProcesses();
1056

    
1057
        mIsPreview = isPreview;
1058

    
1059
        mProgressBar.setVisibility(View.VISIBLE);
1060
        mProgressBar.setProgress(0);
1061

    
1062
        if (isPreview)
1063
        {
1064
            saveFile = new File(fileExternDir,"obscuracam-preview-tmp." + outFormat);
1065
        }
1066
        else {
1067
            if (saveFile != null
1068
                && saveFile.exists() && saveFile.getName().contains("preview"))
1069
                    saveFile.delete();
1070

    
1071
            createCleanSavePath(outFormat);
1072

    
1073
            mSnackbar = Snackbar.make(findViewById(R.id.frameRoot), R.string.processing, Snackbar.LENGTH_INDEFINITE);
1074
            mSnackbar.show();
1075
        }
1076

    
1077
        mCancelled = false;
1078

    
1079
        mediaPlayer.pause();
1080

    
1081
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
1082
        wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
1083
        wl.acquire();
1084

    
1085
        try {
1086
            if (ffmpeg == null)
1087
                ffmpeg = new FFMPEGWrapper(VideoEditor.this.getBaseContext());
1088

    
1089
            int frameRate = 0;
1090

    
1091
            float startTime = ((float)mediaPlayer.getCurrentPosition())/1000f;
1092
            float duration = (float)mDuration/1000f;
1093

    
1094
            if (isPreview) {
1095
                frameRate = 1;
1096
                duration = Math.min(duration - startTime, 1f);
1097
            }
1098

    
1099
            else if (mObscureVideoAmount > 0)
1100
            {
1101
                frameRate = 3;
1102
            }
1103

    
1104
            // Could make some high/low quality presets
1105
            ffmpeg.processVideo(obscureTrails, recordingFile, saveFile,
1106
                    frameRate, startTime, duration, mCompressVideo, mObscureVideoAmount, mObscureAudioAmount,
1107
                    new ExecuteBinaryResponseHandler() {
1108

    
1109
                        @Override
1110
                        public void onStart() {
1111
                        }
1112

    
1113
                        @Override
1114
                        public void onProgress(String message) {
1115

    
1116
                            Log.i(getClass().getName(), "PROGRESS: " + message);
1117
                            //frame=  144 fps=0.6 q=29.0 size=    1010kB time=00:00:05.01 bitrate=1649.7kbits/s dup=3 drop=0 speed=0.0192x
1118

    
1119
                            Message msg = mHandler.obtainMessage(1);
1120
                            msg.getData().putString("status", message);
1121

    
1122
                            if (message.indexOf("time=")!=-1) {
1123
                                int timeidx = message.indexOf("time=")+5;
1124
                                String time = message.substring(timeidx,message.indexOf(" ",timeidx));
1125
                                msg.getData().putString("time", time);
1126
                            }
1127

    
1128
                            mHandler.sendMessage(msg);
1129

    
1130
                        }
1131

    
1132
                        @Override
1133
                        public void onFailure(String message) {
1134

    
1135
                            Log.w(getClass().getName(), "FAILURED: " + message);
1136

    
1137
                        }
1138

    
1139
                        @Override
1140
                        public void onSuccess(String message) {
1141

    
1142
                            Log.i(getClass().getName(), "SUCCESS: " + message);
1143

    
1144
                            if (!mIsPreview)
1145
                                addVideoToGallery(saveFile);
1146

    
1147
                            Message msg = mHandler.obtainMessage(completeActionFlag);
1148
                            msg.getData().putString("status", "complete");
1149
                            mHandler.sendMessage(msg);
1150
                        }
1151

    
1152
                        @Override
1153
                        public void onFinish() {
1154

    
1155
                            Log.i(getClass().getName(), "FINISHED");
1156
                            wl.release();
1157

    
1158
                        }
1159
                    });
1160
        } catch (Exception e) {
1161
            Log.e(LOGTAG, "error with ffmpeg", e);
1162
        }
1163

    
1164

    
1165
    }
1166

    
1167
    ;
1168

    
1169
    private void addVideoToGallery(File videoToAdd) {
1170
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(saveFile)));
1171
    }
1172

    
1173
    Snackbar mSnackbar;
1174

    
1175
    private void askPostProcessAction() {
1176
        if (saveFile != null && saveFile.exists()) {
1177

    
1178
            resetMediaPlayer(Uri.fromFile(saveFile));
1179
            start();
1180

    
1181
            if (!mIsPreview) {
1182

    
1183
                if (mSnackbar != null)
1184
                {
1185
                    mSnackbar.dismiss();
1186
                    mSnackbar = null;
1187
                }
1188

    
1189
                mSnackbar = Snackbar.make(findViewById(R.id.frameRoot), R.string.processing_complete, Snackbar.LENGTH_LONG);
1190
                mSnackbar.setAction("Open", new OnClickListener() {
1191
                    @Override
1192
                    public void onClick(View view) {
1193
                        playVideoExternal();
1194
                    }
1195
                });
1196
                mSnackbar.show();
1197
            }
1198
        }
1199

    
1200
    }
1201

    
1202
    private void showFailure (String message)
1203
    {
1204
        Snackbar snackbar = Snackbar.make(findViewById(R.id.frameRoot), message, Snackbar.LENGTH_SHORT);
1205
        snackbar.show();
1206
    }
1207

    
1208
    private void playVideoExternal() {
1209

    
1210
        if (saveFile != null && saveFile.exists()) {
1211

    
1212
            Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
1213
            intent.setDataAndType(Uri.fromFile(saveFile), MIME_TYPE_VIDEO);
1214
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
1215

    
1216
            startActivity(intent);
1217
        }
1218
    }
1219

    
1220
    private void shareVideo() {
1221

    
1222

    
1223
        if (saveFile != null && saveFile.exists()) {
1224

    
1225
            Intent intent = new Intent(Intent.ACTION_SEND);
1226
            intent.setType(MIME_TYPE_VIDEO);
1227
            intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(saveFile));
1228
            startActivityForResult(Intent.createChooser(intent, "Share Video"), 0);
1229
        }
1230
    }
1231

    
1232
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
1233
        super.onActivityResult(requestCode, resultCode, intent);
1234

    
1235
    }
1236

    
1237
    @Override
1238
    public void inOutValuesChanged(int thumbInValue, int thumbOutValue) {
1239
                /*
1240
                if (activeRegionTrail != null) {
1241
                        
1242
                        activeRegionTrail.setStartTime(thumbInValue);
1243
                        activeRegionTrail.setEndTime(thumbOutValue);
1244
                }*/
1245
    }
1246

    
1247

    
1248
    @Override
1249
    protected void onPause() {
1250

    
1251
        super.onPause();
1252
        mediaPlayer.reset();
1253

    
1254
    }
1255

    
1256
    @Override
1257
    public void onDestroy() {
1258

    
1259
        super.onDestroy();
1260

    
1261
        if (fd != null)
1262
            fd.release();
1263

    
1264
    }
1265

    
1266

    
1267
    @Override
1268
    protected void onStop() {
1269
        super.onStop();
1270
        this.mAutoDetectEnabled = false;
1271
    }
1272

    
1273
    private void killVideoProcessor() {
1274
        int killDelayMs = 300;
1275

    
1276
        String ffmpegBin = new File(getDir("bin", 0), "ffmpeg").getAbsolutePath();
1277

    
1278
        int procId = -1;
1279

    
1280
        while ((procId = ShellUtils.findProcessId(ffmpegBin)) != -1) {
1281

    
1282
            Log.d(LOGTAG, "Found PID=" + procId + " - killing now...");
1283

    
1284
            String[] cmd = {ShellUtils.SHELL_CMD_KILL + ' ' + procId + ""};
1285

    
1286
            try {
1287
                ShellUtils.doShellCommand(cmd, new ShellUtils.ShellCallback() {
1288

    
1289
                    @Override
1290
                    public void shellOut(char[] msg) {
1291
                        // TODO Auto-generated method stub
1292

    
1293
                    }
1294

    
1295
                }, false, false);
1296
                Thread.sleep(killDelayMs);
1297
            } catch (Exception e) {
1298
            }
1299
        }
1300
    }
1301

    
1302
    @Override
1303
    protected void onResume() {
1304
        super.onResume();
1305

    
1306
        videoView = (VideoView) this.findViewById(R.id.SurfaceView);
1307

    
1308
        mProgressBar = (ProgressBar) this.findViewById(R.id.progress_spinner);
1309

    
1310
        regionsView = (ImageView) this.findViewById(R.id.VideoEditorImageView);
1311
        regionsView.setOnClickListener(new OnClickListener() {
1312
            @Override
1313
            public void onClick(View view) {
1314
                resetMediaPlayer(originalVideoUri);
1315
            }
1316
        });
1317
        regionsView.setOnTouchListener(this);
1318

    
1319
        surfaceHolder = videoView.getHolder();
1320

    
1321
        surfaceHolder.addCallback(this);
1322
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
1323

    
1324
        currentDisplay = getWindowManager().getDefaultDisplay();
1325

    
1326
        mVideoSeekbar = (InOutPlayheadSeekBar) this.findViewById(R.id.InOutPlayheadSeekBar);
1327

    
1328
        mVideoSeekbar.setIndeterminate(false);
1329
        mVideoSeekbar.setSecondaryProgress(0);
1330
        mVideoSeekbar.setProgress(0);
1331
        mVideoSeekbar.setInOutPlayheadSeekBarChangeListener(this);
1332
        mVideoSeekbar.setThumbsInactive();
1333
        mVideoSeekbar.setOnTouchListener(this);
1334

    
1335
        playPauseButton = (ImageButton) this.findViewById(R.id.PlayPauseImageButton);
1336
        playPauseButton.setOnClickListener(new OnClickListener() {
1337
            @Override
1338
            public void onClick(View view) {
1339
                if (mediaPlayer.isPlaying()) {
1340
                    mediaPlayer.pause();
1341
                    playPauseButton.setImageDrawable(getResources().getDrawable(android.R.drawable.ic_media_play));
1342
                    mAutoDetectEnabled = false;
1343
                } else {
1344
                    if (currentUri != originalVideoUri)
1345
                    {
1346
                        resetMediaPlayer(originalVideoUri);
1347
                        seekTo(0);
1348
                    }
1349
                    start();
1350

    
1351

    
1352
                }
1353
            }
1354
        });
1355

    
1356

    
1357
        //regionBarArea = (RegionBarArea) this.findViewById(R.id.RegionBarArea);
1358
        //regionBarArea.obscureRegions = obscureRegions;
1359

    
1360
        obscuredPaint = new Paint();
1361
        obscuredPaint.setColor(Color.WHITE);
1362
        obscuredPaint.setStyle(Style.STROKE);
1363
        obscuredPaint.setStrokeWidth(10f);
1364

    
1365
        selectedPaint = new Paint();
1366
        selectedPaint.setColor(Color.GREEN);
1367
        selectedPaint.setStyle(Style.STROKE);
1368
        selectedPaint.setStrokeWidth(10f);
1369

    
1370
        /**
1371
        findViewById(R.id.button_auto).setOnClickListener(new OnClickListener() {
1372
            @Override
1373
            public void onClick(View view) {
1374
                beginAutoDetect();
1375
            }
1376
        });**/
1377

    
1378
        ((SeekBar)findViewById(R.id.seekbar_video_obscure)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
1379
            @Override
1380
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
1381

    
1382
                //make it even!
1383
                if (i%2!=0) {
1384
                    i+=1;
1385
                }
1386

    
1387
                mObscureVideoAmount = i;
1388

    
1389
            }
1390

    
1391
            @Override
1392
            public void onStartTrackingTouch(SeekBar seekBar) {
1393

    
1394
            }
1395

    
1396
            @Override
1397
            public void onStopTrackingTouch(SeekBar seekBar) {
1398

    
1399
                processVideo(true);
1400
            }
1401
        });
1402

    
1403
        ((SeekBar)findViewById(R.id.seekbar_audio_obscure)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
1404
            @Override
1405
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
1406
                mObscureAudioAmount = i;
1407
            }
1408

    
1409
            @Override
1410
            public void onStartTrackingTouch(SeekBar seekBar) {
1411

    
1412
            }
1413

    
1414
            @Override
1415
            public void onStopTrackingTouch(SeekBar seekBar) {
1416

    
1417
                processVideo(true);
1418
            }
1419
        });
1420

    
1421
        setPrefs();
1422

    
1423
        loadMedia(originalVideoUri);
1424

    
1425

    
1426
    }
1427

    
1428
    private void setPrefs() {
1429
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
1430

    
1431
        eyesOnly = prefs.getBoolean("pref_eyes_only", false);
1432

    
1433
        outFrameRate = Integer.parseInt(prefs.getString("pref_out_fps", DEFAULT_OUT_FPS).trim());
1434
        outBitRate = Integer.parseInt(prefs.getString("pref_out_rate", DEFAULT_OUT_RATE).trim());
1435
        outFormat = DEFAULT_OUT_FORMAT;
1436
        outAcodec = prefs.getString("pref_out_acodec", DEFAULT_OUT_ACODEC).trim();
1437
        outVcodec = prefs.getString("pref_out_vcodec", DEFAULT_OUT_VCODEC).trim();
1438

    
1439
        outVWidth = Integer.parseInt(prefs.getString("pref_out_vwidth", DEFAULT_OUT_WIDTH).trim());
1440
        outVHeight = Integer.parseInt(prefs.getString("pref_out_vheight", DEFAULT_OUT_HEIGHT).trim());
1441

    
1442
    }
1443
        
1444
        /*
1445
        private void doAutoDetectionThread()
1446
        {
1447
                Thread thread = new Thread ()
1448
                {
1449
                        public void run ()
1450
                        {
1451
                                long cTime = mediaPlayer.getCurrentPosition();
1452
                                Bitmap bmp = getVideoFrame(recordingFile.getAbsolutePath(),cTime);
1453
                                doAutoDetection(bmp, cTime, 500);
1454

1455
                        //        Message msg = mHandler.obtainMessage(3);
1456
                     //   mHandler.sendMessage(msg);
1457
                        }
1458
                };
1459
                thread.start();
1460
        }*/
1461
        
1462
        /*
1463
        public static Bitmap getVideoFrame(String videoPath,long frameTime) {
1464
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
1465
        try {
1466
            retriever.setDataSource(videoPath);                   
1467
            return retriever.getFrameAtTime(frameTime, MediaMetadataRetriever.OPTION_CLOSEST);
1468
        } catch (IllegalArgumentException ex) {
1469
            ex.printStackTrace();
1470
        } catch (RuntimeException ex) {
1471
            ex.printStackTrace();
1472
        } finally {
1473
            try {
1474
                retriever.release();
1475
            } catch (RuntimeException ex) {
1476
            }
1477
        }
1478
        return null;
1479
    }*/
1480
        
1481
        /*
1482
         * Do actual auto detection and create regions
1483
         * 
1484
         * public void createImageRegion(int _scaledStartX, int _scaledStartY, 
1485
                        int _scaledEndX, int _scaledEndY, 
1486
                        int _scaledImageWidth, int _scaledImageHeight, 
1487
                        int _imageWidth, int _imageHeight, 
1488
                        int _backgroundColor) {
1489
         */
1490

    
1491
    private int autoDetectFrame(Bitmap bmp, int cTime, int cBuffer, int cDuration, boolean eyesOnly) {
1492

    
1493
        ArrayList<DetectedFace> dFaces = runFaceDetection(bmp);
1494

    
1495
        if (dFaces == null)
1496
            return 0;
1497

    
1498
        for (DetectedFace dFace : dFaces) {
1499

    
1500
            //float faceBuffer = -1 * (autodetectedRect.right-autodetectedRect.left)/15;
1501
            //autodetectedRect.inset(faceBuffer, faceBuffer);
1502

    
1503
            if (eyesOnly) {
1504
                dFace.bounds.top = dFace.midpoint.y - dFace.eyeDistance / 2.5f;
1505
                dFace.bounds.bottom = dFace.midpoint.y + dFace.eyeDistance / 2.5f;
1506
            }
1507

    
1508
            //move the facet detect time back a few MS
1509
            int faceTime = cTime;
1510
            if (faceTime > autoDetectTimeInterval * 2)
1511
                faceTime -= autoDetectTimeInterval * 2;
1512

    
1513
            ObscureRegion newRegion = new ObscureRegion(faceTime, dFace.bounds.left,
1514
                    dFace.bounds.top,
1515
                    dFace.bounds.right,
1516
                    dFace.bounds.bottom);
1517

    
1518
            //if we have an existing/last region
1519

    
1520
            boolean foundTrail = false;
1521
            RegionTrail iTrail = findIntersectTrail(newRegion, cTime);
1522

    
1523
            if (iTrail != null) {
1524
                iTrail.addRegion(newRegion);
1525
                activeRegionTrail = iTrail;
1526
                foundTrail = true;
1527
                break;
1528
            }
1529

    
1530
            if (!foundTrail) {
1531
                activeRegionTrail = new RegionTrail(cTime, mDuration);
1532
                obscureTrails.add(activeRegionTrail);
1533

    
1534
                activeRegionTrail.addRegion(newRegion);
1535

    
1536
            }
1537

    
1538
            activeRegion = newRegion;
1539
            foundTrail = false;
1540
        }
1541

    
1542
        Message msg = mHandler.obtainMessage(5);
1543
        mHandler.sendMessage(msg);
1544

    
1545
        return dFaces.size();
1546
    }
1547

    
1548
    private RegionTrail findIntersectTrail(ObscureRegion region, int currentTime) {
1549
        for (RegionTrail trail : obscureTrails) {
1550
            if (trail.isWithinTime(currentTime)) {
1551
                float iLeft = -1, iTop = -1, iRight = -1, iBottom = -1;
1552

    
1553
                //intersects check points
1554
                RectF aRectF = region.getRectF();
1555
                float iBuffer = 15;
1556
                iLeft = aRectF.left - iBuffer;
1557
                iTop = aRectF.top - iBuffer;
1558
                iRight = aRectF.right + iBuffer;
1559
                iBottom = aRectF.bottom + iBuffer;
1560

    
1561
                Iterator<ObscureRegion> itRegions = trail.getRegionsIterator();
1562

    
1563
                while (itRegions.hasNext()) {
1564
                    ObscureRegion testRegion = itRegions.next();
1565

    
1566
                    if (testRegion.getRectF().intersects(iLeft, iTop, iRight, iBottom)) {
1567
                        return trail;
1568
                    }
1569
                }
1570
            }
1571
        }
1572

    
1573
        return null;
1574
    }
1575

    
1576
    AndroidFaceDetection fd = null;
1577

    
1578
    /*
1579
     * The actual face detection calling method
1580
     */
1581
    private ArrayList<DetectedFace> runFaceDetection(Bitmap bmp) {
1582

    
1583
        ArrayList<DetectedFace> dFaces = new ArrayList<DetectedFace>();
1584

    
1585
        if (fd == null)
1586
            fd = new AndroidFaceDetection(bmp.getWidth(), bmp.getHeight());
1587

    
1588
        try {
1589
            //Bitmap bProc = toGrayscale(bmp);
1590

    
1591
            int numFaces = fd.findFaces(bmp);
1592

    
1593
            if (numFaces > 0)
1594
                dFaces.addAll(fd.getFaces(numFaces));
1595

    
1596
            numFaces = fd.findFaces(bmp);
1597

    
1598
            if (numFaces > 0)
1599
                dFaces.addAll(fd.getFaces(numFaces));
1600

    
1601
        } catch (NullPointerException e) {
1602
            dFaces = null;
1603
        }
1604
        return dFaces;
1605
    }
1606

    
1607
    public Bitmap toGrayscale(Bitmap bmpOriginal) {
1608
        int width, height;
1609
        height = bmpOriginal.getHeight();
1610
        width = bmpOriginal.getWidth();
1611

    
1612
        Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
1613
        Canvas c = new Canvas(bmpGrayscale);
1614
        Paint paint = new Paint();
1615
        ColorMatrix cm = new ColorMatrix();
1616
        cm.setSaturation(0);
1617

    
1618
        ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
1619

    
1620
        paint.setColorFilter(f);
1621

    
1622
        c.drawBitmap(bmpOriginal, 0, 0, paint);
1623

    
1624

    
1625
        return bmpGrayscale;
1626
    }
1627

    
1628
    public static Bitmap createContrast(Bitmap src, double value) {
1629
        // image size
1630
        int width = src.getWidth();
1631
        int height = src.getHeight();
1632
        // create output bitmap
1633
        Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
1634
        // color information
1635
        int A, R, G, B;
1636
        int pixel;
1637
        // get contrast value
1638
        double contrast = Math.pow((100 + value) / 100, 2);
1639

    
1640
        // scan through all pixels
1641
        for (int x = 0; x < width; ++x) {
1642
            for (int y = 0; y < height; ++y) {
1643
                // get pixel color
1644
                pixel = src.getPixel(x, y);
1645
                A = Color.alpha(pixel);
1646
                // apply filter contrast for every channel R, G, B
1647
                R = Color.red(pixel);
1648
                R = (int) (((((R / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
1649
                if (R < 0) {
1650
                    R = 0;
1651
                } else if (R > 255) {
1652
                    R = 255;
1653
                }
1654

    
1655
                G = Color.red(pixel);
1656
                G = (int) (((((G / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
1657
                if (G < 0) {
1658
                    G = 0;
1659
                } else if (G > 255) {
1660
                    G = 255;
1661
                }
1662

    
1663
                B = Color.red(pixel);
1664
                B = (int) (((((B / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
1665
                if (B < 0) {
1666
                    B = 0;
1667
                } else if (B > 255) {
1668
                    B = 255;
1669
                }
1670

    
1671
                // set new pixel color to output bitmap
1672
                bmOut.setPixel(x, y, Color.argb(A, R, G, B));
1673
            }
1674
        }
1675

    
1676
        // return final image
1677
        return bmOut;
1678
    }
1679

    
1680
    public void showPrefs() {
1681
        Intent intent = new Intent(this, VideoPreferences.class);
1682
        startActivityForResult(intent, 0);
1683

    
1684
    }
1685

    
1686
    /**
1687
    public void onItemClick(QuickAction source, int pos, int actionId) {
1688

1689
        switch (actionId) {
1690
            case 0:
1691
                // set in point
1692
                activeRegionTrail.setStartTime(mediaPlayer.getCurrentPosition());
1693
                updateProgressBar(activeRegionTrail);
1694

1695

1696
                break;
1697
            case 1:
1698
                // set out point
1699
                activeRegionTrail.setEndTime(mediaPlayer.getCurrentPosition());
1700
                updateProgressBar(activeRegionTrail);
1701
                activeRegion = null;
1702
                activeRegionTrail = null;
1703

1704

1705
                break;
1706
            case 2:
1707
                // Remove region
1708
                if (activeRegion != null) {
1709
                    activeRegionTrail.removeRegion(activeRegion);
1710
                    activeRegion = null;
1711
                }
1712
                break;
1713

1714
            case 3:
1715
                // Remove region
1716
                obscureTrails.remove(activeRegionTrail);
1717
                activeRegionTrail = null;
1718
                activeRegion = null;
1719

1720
                break;
1721

1722
            case 4:
1723
                activeRegionTrail.setObscureMode(RegionTrail.OBSCURE_MODE_REDACT);
1724

1725
                break;
1726

1727
            case 5:
1728
                activeRegionTrail.setObscureMode(RegionTrail.OBSCURE_MODE_PIXELATE);
1729
                break;
1730

1731
            case 6:
1732
                activeRegionTrail.setDoTweening(!activeRegionTrail.isDoTweening());
1733
                break;
1734

1735
        }
1736

1737
        updateRegionDisplay(mediaPlayer.getCurrentPosition());
1738

1739
    }
1740
    **/
1741

    
1742
    /*
1743
     * Actual deletion of original
1744
     */
1745
    private void deleteOriginal() throws IOException {
1746

    
1747
        if (originalVideoUri != null) {
1748
            if (originalVideoUri.getScheme().equals("file")) {
1749
                String origFilePath = originalVideoUri.getPath();
1750
                File fileOrig = new File(origFilePath);
1751

    
1752
                String[] columnsToSelect = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA};
1753
                                
1754
                                /*
1755
                                ExifInterface ei = new ExifInterface(origFilePath);
1756
                                long dateTaken = new Date(ei.getAttribute(ExifInterface.TAG_DATETIME)).getTime();
1757
                                */
1758

    
1759
                Uri[] uriBases = {MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media.INTERNAL_CONTENT_URI};
1760

    
1761
                for (Uri uriBase : uriBases) {
1762

    
1763
                    Cursor imageCursor = getContentResolver().query(uriBase, columnsToSelect, MediaStore.Images.Media.DATA + " = ?", new String[]{origFilePath}, null);
1764
                    //Cursor imageCursor = getContentResolver().query(uriBase, columnsToSelect, MediaStore.Images.Media.DATE_TAKEN + " = ?",  new String[] {dateTaken+""}, null );
1765

    
1766
                    while (imageCursor.moveToNext()) {
1767

    
1768
                        long _id = imageCursor.getLong(imageCursor.getColumnIndex(MediaStore.Images.Media._ID));
1769

    
1770
                        getContentResolver().delete(ContentUris.withAppendedId(uriBase, _id), null, null);
1771

    
1772
                    }
1773
                }
1774

    
1775
                if (fileOrig.exists())
1776
                    fileOrig.delete();
1777

    
1778
            } else {
1779
                getContentResolver().delete(originalVideoUri, null, null);
1780
            }
1781
        }
1782

    
1783
        originalVideoUri = null;
1784
    }
1785

    
1786

    
1787
    public int getAudioSessionId() {
1788
        return 1;
1789
    }
1790

    
1791

    
1792
}