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 |
} |