NekoX/TMessagesProj/src/main/java/org/telegram/ui/VideoEditorActivity.java

553 lines
23 KiB
Java

/*
* This is the source code of Telegram for Android v. 1.7.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2014.
*/
package org.telegram.ui;
import android.content.res.Configuration;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
import com.googlecode.mp4parser.authoring.tracks.CroppedTrack;
import org.telegram.android.AndroidUtilities;
import org.telegram.android.LocaleController;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.ui.Views.ActionBar.ActionBarLayer;
import org.telegram.ui.Views.ActionBar.ActionBarMenu;
import org.telegram.ui.Views.ActionBar.BaseFragment;
import org.telegram.ui.Views.VideoSeekBarView;
import org.telegram.ui.Views.VideoTimelineView;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class VideoEditorActivity extends BaseFragment implements SurfaceHolder.Callback {
private MediaPlayer videoPlayer = null;
private SurfaceHolder surfaceHolder = null;
private VideoTimelineView videoTimelineView = null;
private View videoContainerView = null;
private TextView originalSizeTextView = null;
private TextView editedSizeTextView = null;
private View textContainerView = null;
private ImageView playButton = null;
private VideoSeekBarView videoSeekBarView = null;
private boolean initied = false;
private String videoPath = null;
private int videoWidth;
private int videoHeight;
private float lastProgress = 0;
private boolean needSeek = false;
private VideoEditorActivityDelegate delegate;
public interface VideoEditorActivityDelegate {
public abstract void didFinishedVideoConverting(String videoPath);
}
private Runnable progressRunnable = new Runnable() {
@Override
public void run() {
while (videoPlayer.isPlaying()) {
AndroidUtilities.RunOnUIThread(new Runnable() {
@Override
public void run() {
if (videoPlayer.isPlaying()) {
float startTime = videoTimelineView.getLeftProgress() * videoPlayer.getDuration();
float endTime = videoTimelineView.getRightProgress() * videoPlayer.getDuration();
if (startTime == endTime) {
startTime = endTime - 0.01f;
}
float progress = (videoPlayer.getCurrentPosition() - startTime) / (endTime - startTime);
if (progress > lastProgress) {
videoSeekBarView.setProgress(progress);
lastProgress = progress;
}
if (videoPlayer.getCurrentPosition() >= endTime) {
try {
videoPlayer.pause();
onPlayComplete();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
}
}
});
try {
Thread.sleep(50);
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
}
};
public VideoEditorActivity(Bundle args) {
super(args);
videoPath = args.getString("videoPath");
}
@Override
public boolean onFragmentCreate() {
if (videoPath == null) {
return false;
}
videoPlayer = new MediaPlayer();
videoPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
AndroidUtilities.RunOnUIThread(new Runnable() {
@Override
public void run() {
onPlayComplete();
}
});
}
});
return super.onFragmentCreate();
}
@Override
public void onFragmentDestroy() {
if (videoTimelineView != null) {
videoTimelineView.destroy();
}
super.onFragmentDestroy();
}
@Override
public View createView(LayoutInflater inflater, ViewGroup container) {
if (fragmentView == null) {
actionBarLayer.setBackgroundColor(0xff333333);
actionBarLayer.setItemsBackground(R.drawable.bar_selector_white);
actionBarLayer.setDisplayHomeAsUpEnabled(true, R.drawable.photo_back);
actionBarLayer.setTitle(LocaleController.getString("EditVideo", R.string.EditVideo));
actionBarLayer.setActionBarMenuOnItemClick(new ActionBarLayer.ActionBarMenuOnItemClick() {
@Override
public void onItemClick(int id) {
if (id == -1) {
finishFragment();
} else if (id == 1) {
try {
startConvert();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
}
});
ActionBarMenu menu = actionBarLayer.createMenu();
View doneItem = menu.addItemResource(1, R.layout.group_create_done_layout);
TextView doneTextView = (TextView)doneItem.findViewById(R.id.done_button);
doneTextView.setText(LocaleController.getString("Done", R.string.Done).toUpperCase());
fragmentView = inflater.inflate(R.layout.video_editor_layout, container, false);
originalSizeTextView = (TextView)fragmentView.findViewById(R.id.original_size);
editedSizeTextView = (TextView)fragmentView.findViewById(R.id.edited_size);
videoContainerView = fragmentView.findViewById(R.id.video_container);
textContainerView = fragmentView.findViewById(R.id.info_container);
videoTimelineView = (VideoTimelineView)fragmentView.findViewById(R.id.video_timeline_view);
videoTimelineView.setVideoPath(videoPath);
videoTimelineView.setDelegate(new VideoTimelineView.VideoTimelineViewDelegate() {
@Override
public void onLeftProgressChanged(float progress) {
try {
if (videoPlayer.isPlaying()) {
videoPlayer.pause();
playButton.setImageResource(R.drawable.video_play);
}
videoPlayer.setOnSeekCompleteListener(null);
videoPlayer.seekTo((int)(videoPlayer.getDuration() * progress));
} catch (Exception e) {
FileLog.e("tmessages", e);
}
needSeek = true;
videoSeekBarView.setProgress(0);
updateVideoEditedInfo();
}
@Override
public void onRifhtProgressChanged(float progress) {
try {
if (videoPlayer.isPlaying()) {
videoPlayer.pause();
playButton.setImageResource(R.drawable.video_play);
}
videoPlayer.setOnSeekCompleteListener(null);
videoPlayer.seekTo((int)(videoPlayer.getDuration() * progress));
} catch (Exception e) {
FileLog.e("tmessages", e);
}
needSeek = true;
videoSeekBarView.setProgress(0);
updateVideoEditedInfo();
}
});
videoSeekBarView = (VideoSeekBarView)fragmentView.findViewById(R.id.video_seekbar);
videoSeekBarView.delegate = new VideoSeekBarView.SeekBarDelegate() {
@Override
public void onSeekBarDrag(float progress) {
if (videoPlayer.isPlaying()) {
try {
float prog = videoTimelineView.getLeftProgress() + (videoTimelineView.getRightProgress() - videoTimelineView.getLeft()) * progress;
videoPlayer.seekTo((int)(videoPlayer.getDuration() * prog));
lastProgress = progress;
} catch (Exception e) {
FileLog.e("tmessages", e);
}
} else {
lastProgress = progress;
needSeek = true;
}
}
};
playButton = (ImageView)fragmentView.findViewById(R.id.play_button);
playButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (surfaceHolder.isCreating()) {
return;
}
play();
}
});
SurfaceView surfaceView = (SurfaceView) fragmentView.findViewById(R.id.video_view);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceHolder.setFixedSize(270, 480);
updateVideoOriginalInfo();
updateVideoEditedInfo();
} else {
ViewGroup parent = (ViewGroup)fragmentView.getParent();
if (parent != null) {
parent.removeView(fragmentView);
}
}
return fragmentView;
}
@Override
public void onResume() {
super.onResume();
fixLayout();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
fixLayout();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
videoPlayer.setDisplay(holder);
try {
videoPlayer.setDataSource(videoPath);
videoPlayer.prepare();
videoWidth = videoPlayer.getVideoWidth();
videoHeight = videoPlayer.getVideoHeight();
fixVideoSize();
videoPlayer.seekTo((int) (videoTimelineView.getLeftProgress() * videoPlayer.getDuration()));
initied = true;
} catch (Exception e) {
FileLog.e("tmessages", e);
}
updateVideoOriginalInfo();
updateVideoEditedInfo();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
videoPlayer.setDisplay(null);
}
private void onPlayComplete() {
playButton.setImageResource(R.drawable.video_play);
videoSeekBarView.setProgress(0);
try {
videoPlayer.seekTo((int) (videoTimelineView.getLeftProgress() * videoPlayer.getDuration()));
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
private void updateVideoOriginalInfo() {
if (!initied || originalSizeTextView == null) {
return;
}
File file = new File(videoPath);
String videoDimension = String.format("%dx%d", videoPlayer.getVideoWidth(), videoPlayer.getVideoHeight());
int minutes = videoPlayer.getDuration() / 1000 / 60;
int seconds = (int)Math.ceil(videoPlayer.getDuration() / 1000) - minutes * 60;
String videoTimeSize = String.format("%d:%02d, %s", minutes, seconds, Utilities.formatFileSize(file.length()));
originalSizeTextView.setText(String.format("%s: %s, %s", LocaleController.getString("OriginalVideo", R.string.OriginalVideo), videoDimension, videoTimeSize));
}
private void updateVideoEditedInfo() {
if (!initied || editedSizeTextView == null) {
return;
}
File file = new File(videoPath);
long size = file.length();
float videoWidth = videoPlayer.getVideoWidth();
float videoHeight = videoPlayer.getVideoHeight();
if (videoWidth > 640 || videoHeight > 640) {
float scale = videoWidth > videoHeight ? 640.0f / videoWidth : 640.0f / videoHeight;
videoWidth *= scale;
videoHeight *= scale;
size *= (scale * scale);
}
String videoDimension = String.format("%dx%d", (int)videoWidth, (int)videoHeight);
int minutes = videoPlayer.getDuration() / 1000 / 60;
int seconds = (int)Math.ceil(videoPlayer.getDuration() / 1000) - minutes * 60;
String videoTimeSize = String.format("%d:%02d, ~%s", minutes, seconds, Utilities.formatFileSize(size));
editedSizeTextView.setText(String.format("%s: %s, %s", LocaleController.getString("EditedVideo", R.string.EditedVideo), videoDimension, videoTimeSize));
}
private void fixVideoSize() {
if (videoWidth == 0 || videoHeight == 0 || fragmentView == null || getParentActivity() == null) {
return;
}
int viewHeight = 0;
if (!Utilities.isTablet(getParentActivity()) && getParentActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
viewHeight = AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight - AndroidUtilities.dp(40);
} else {
viewHeight = AndroidUtilities.displaySize.y - AndroidUtilities.statusBarHeight - AndroidUtilities.dp(48);
}
int width = 0;
int height = 0;
if (getParentActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
width = AndroidUtilities.displaySize.x - AndroidUtilities.displaySize.x / 2 - AndroidUtilities.dp(24);
height = viewHeight - AndroidUtilities.dp(32);
} else {
width = AndroidUtilities.displaySize.x;
height = viewHeight - AndroidUtilities.dp(176);
}
float wr = (float)width / (float)videoWidth;
float hr = (float)height / (float)videoHeight;
float ar = (float)videoWidth / (float)videoHeight;
if (wr > hr) {
width = (int) (height * ar);
} else {
height = (int) (width / ar);
}
surfaceHolder.setFixedSize(width, height);
}
private void fixLayout() {
if (originalSizeTextView == null) {
return;
}
originalSizeTextView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
originalSizeTextView.getViewTreeObserver().removeOnPreDrawListener(this);
if (getParentActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)videoContainerView.getLayoutParams();
layoutParams.topMargin = AndroidUtilities.dp(16);
layoutParams.bottomMargin = AndroidUtilities.dp(16);
layoutParams.width = AndroidUtilities.displaySize.x / 2 - AndroidUtilities.dp(24);
layoutParams.leftMargin = AndroidUtilities.dp(16);
videoContainerView.setLayoutParams(layoutParams);
layoutParams = (FrameLayout.LayoutParams)textContainerView.getLayoutParams();
layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT;
layoutParams.width = AndroidUtilities.displaySize.x / 2 - AndroidUtilities.dp(24);
layoutParams.leftMargin = AndroidUtilities.displaySize.x / 2 + AndroidUtilities.dp(8);
layoutParams.rightMargin = AndroidUtilities.dp(16);
layoutParams.topMargin = AndroidUtilities.dp(16);
textContainerView.setLayoutParams(layoutParams);
} else {
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)videoContainerView.getLayoutParams();
layoutParams.topMargin = AndroidUtilities.dp(16);
layoutParams.bottomMargin = AndroidUtilities.dp(160);
layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT;
layoutParams.leftMargin = 0;
videoContainerView.setLayoutParams(layoutParams);
layoutParams = (FrameLayout.LayoutParams)textContainerView.getLayoutParams();
layoutParams.height = AndroidUtilities.dp(143);
layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT;
layoutParams.leftMargin = 0;
layoutParams.rightMargin = 0;
layoutParams.topMargin = 0;
textContainerView.setLayoutParams(layoutParams);
}
fixVideoSize();
videoTimelineView.clearFrames();
return false;
}
});
}
private void play() {
if (videoPlayer.isPlaying()) {
videoPlayer.pause();
playButton.setImageResource(R.drawable.video_play);
} else {
try {
playButton.setImageDrawable(null);
lastProgress = 0;
if (needSeek) {
float prog = videoTimelineView.getLeftProgress() + (videoTimelineView.getRightProgress() - videoTimelineView.getLeft()) * videoSeekBarView.getProgress();
videoPlayer.seekTo((int)(videoPlayer.getDuration() * prog));
needSeek = false;
}
videoPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(MediaPlayer mp) {
float startTime = videoTimelineView.getLeftProgress() * videoPlayer.getDuration();
float endTime = videoTimelineView.getRightProgress() * videoPlayer.getDuration();
if (startTime == endTime) {
startTime = endTime - 0.01f;
}
lastProgress = (videoPlayer.getCurrentPosition() - startTime) / (endTime - startTime);
videoSeekBarView.setProgress(lastProgress);
}
});
videoPlayer.start();
new Thread(progressRunnable).start();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
}
public void setDelegate(VideoEditorActivityDelegate delegate) {
this.delegate = delegate;
}
private void startConvert() throws Exception {
Movie movie = MovieCreator.build(videoPath);
List<Track> tracks = movie.getTracks();
movie.setTracks(new LinkedList<Track>());
double startTime = 0;
double endTime = 0;
for (Track track : tracks) {
if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
double duration = (double)track.getDuration() / (double)track.getTrackMetaData().getTimescale();
startTime = correctTimeToSyncSample(track, videoTimelineView.getLeftProgress() * duration, false);
endTime = videoTimelineView.getRightProgress() * duration;
break;
}
}
for (Track track : tracks) {
long currentSample = 0;
double currentTime = 0;
double lastTime = 0;
long startSample = 0;
long endSample = -1;
for (int i = 0; i < track.getSampleDurations().length; i++) {
long delta = track.getSampleDurations()[i];
if (currentTime > lastTime && currentTime <= startTime) {
startSample = currentSample;
}
if (currentTime > lastTime && currentTime <= endTime) {
endSample = currentSample;
}
lastTime = currentTime;
currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
currentSample++;
}
movie.addTrack(new CroppedTrack(track, startSample, endSample));
}
Container out = new DefaultMp4Builder().build(movie);
String fileName = Integer.MIN_VALUE + "_" + UserConfig.lastLocalId + ".mp4";
UserConfig.lastLocalId--;
File cacheFile = new File(AndroidUtilities.getCacheDir(), fileName);
UserConfig.saveConfig(false);
FileOutputStream fos = new FileOutputStream(cacheFile);
FileChannel fc = fos.getChannel();
out.writeContainer(fc);
fc.close();
fos.close();
if (delegate != null) {
delegate.didFinishedVideoConverting(cacheFile.getAbsolutePath());
finishFragment();
}
}
// private void startEncodeVideo() {
// MediaExtractor mediaExtractor = new MediaExtractor();
// mediaExtractor.s
// }
private static double correctTimeToSyncSample(Track track, double cutHere, boolean next) {
double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
long currentSample = 0;
double currentTime = 0;
for (int i = 0; i < track.getSampleDurations().length; i++) {
long delta = track.getSampleDurations()[i];
if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(), currentSample + 1)] = currentTime;
}
currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
currentSample++;
}
double previous = 0;
for (double timeOfSyncSample : timeOfSyncSamples) {
if (timeOfSyncSample > cutHere) {
if (next) {
return timeOfSyncSample;
} else {
return previous;
}
}
previous = timeOfSyncSample;
}
return timeOfSyncSamples[timeOfSyncSamples.length - 1];
}
}