Skip to content

Commit

Permalink
fix: app crash if closing editor when it's playing
Browse files Browse the repository at this point in the history
  • Loading branch information
maitrungduc1410 authored Sep 28, 2024
1 parent c004525 commit 1db01c6
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 71 deletions.
119 changes: 49 additions & 70 deletions android/src/main/java/com/videotrim/widgets/VideoTrimmerView.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,11 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {

private ReactApplicationContext mContext;
private VideoView mVideoView;
// https://stackoverflow.com/a/73361868/7569705

// mediaPlayer is used for both video/audio
// the reason we use mediaPlayer for Video: https://stackoverflow.com/a/73361868/7569705
// the videoPlayer is to solve the issue after manually seek -> hit play -> it starts from a position slightly before with the one we just sought to
private MediaPlayer videoPlayer;
private MediaPlayer mediaPlayer;
private ImageView mPlayView;
private LinearLayout mThumbnailContainer;
private Uri mSourceUri;
Expand Down Expand Up @@ -99,7 +101,6 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
private TextView cancelBtn;
private FrameLayout audioBannerView;
private boolean isVideoType = true;
private MediaPlayer audioPlayer;
private ImageView failToLoadBtn;

private String mOutputExt = "mp4";
Expand Down Expand Up @@ -175,29 +176,28 @@ public void initByURI(final Uri videoURI) {

mVideoView.setOnPreparedListener(mp -> {
mp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT);
mediaPlayer = mp;
mediaPrepared();
videoPlayer = mp;
});

mVideoView.setOnErrorListener(this::onFailToLoadMedia);

mVideoView.setOnCompletionListener(mp -> mediaCompleted());
} else {
mVideoView.setVisibility(View.GONE);
audioBannerView.setAlpha(0f);
audioBannerView.setVisibility(View.VISIBLE);
audioBannerView.animate().alpha(1f).setDuration(500).start();

audioPlayer = new MediaPlayer();
mediaPlayer = new MediaPlayer();
try {
audioPlayer.setDataSource(videoURI.toString());
audioPlayer.setOnPreparedListener(mp -> {
mediaPlayer.setDataSource(videoURI.toString());
mediaPlayer.setOnPreparedListener(mp -> {
mediaPrepared();
});
audioPlayer.setOnCompletionListener(mp -> mediaCompleted());
audioPlayer.setOnErrorListener(this::onFailToLoadMedia);
mediaPlayer.setOnCompletionListener(mp -> mediaCompleted());
mediaPlayer.setOnErrorListener(this::onFailToLoadMedia);

audioPlayer.prepareAsync(); // use prepareAsync to avoid blocking the main thread
mediaPlayer.prepareAsync(); // use prepareAsync to avoid blocking the main thread
} catch (IOException e) {
e.printStackTrace();
mediaFailed();
Expand Down Expand Up @@ -244,7 +244,7 @@ private void startShootVideoThumbs(final Context context, int totalThumbsCount,
}

private void mediaPrepared() {
mDuration = isVideoType ? mVideoView.getDuration() : audioPlayer.getDuration();
mDuration = mediaPlayer.getDuration();
mMaxDuration = Math.min(mMaxDuration, mDuration);

if (isVideoType) {
Expand Down Expand Up @@ -332,46 +332,23 @@ private void mediaCompleted() {
}

private void playOrPause() {
if (isVideoType) {
if (mVideoView.isPlaying()) {
onMediaPause();
} else {
// if current video time >= end time, seek to start time
if (mVideoView.getCurrentPosition() >= endTime) {
seekTo(startTime, true);
}
mVideoView.start();
startTimingRunnable();
}
setPlayPauseViewIcon(mVideoView.isPlaying());

if (mediaPlayer.isPlaying()) {
onMediaPause();
} else {
if (audioPlayer.isPlaying()) {
onMediaPause();
} else {
if (audioPlayer.getCurrentPosition() >= endTime) {
seekTo(startTime, true);
}
audioPlayer.start();
startTimingRunnable();
if (mediaPlayer.getCurrentPosition() >= endTime) {
seekTo(startTime, true);
}
setPlayPauseViewIcon(audioPlayer.isPlaying());
mediaPlayer.start();
startTimingRunnable();
}
setPlayPauseViewIcon(mediaPlayer.isPlaying());
}

public void onMediaPause() {
if (isVideoType) {
if (mVideoView.isPlaying()) {
mTimingHandler.removeCallbacks(mTimingRunnable);
mVideoView.pause();
setPlayPauseViewIcon(false);
}
} else {
if (audioPlayer.isPlaying()) {
mTimingHandler.removeCallbacks(mTimingRunnable);
audioPlayer.pause();
setPlayPauseViewIcon(false);
}
if (mediaPlayer.isPlaying()) {
mTimingHandler.removeCallbacks(mTimingRunnable);
mediaPlayer.pause();
setPlayPauseViewIcon(false);
}
}

Expand Down Expand Up @@ -407,14 +384,10 @@ public void onCancelTrimClicked() {
}

private void seekTo(long msec, boolean needUpdateProgress) {
if (isVideoType) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
videoPlayer.seekTo((int) msec, MediaPlayer.SEEK_CLOSEST);
} else {
mVideoView.seekTo((int) msec);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mediaPlayer.seekTo((int) msec, MediaPlayer.SEEK_CLOSEST);
} else {
audioPlayer.seekTo((int) msec);
mediaPlayer.seekTo((int) msec);
}

updateCurrentTime(needUpdateProgress);
Expand Down Expand Up @@ -447,9 +420,15 @@ public void onDestroy() {
e.printStackTrace();
}

if (audioPlayer != null) {
audioPlayer.stop();
audioPlayer.release();
try {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
}
} catch (IllegalStateException e) {
// if it's video, resource is released with the view, and here we also call .release which will throw exception
e.printStackTrace();
Log.d(TAG, "onDestroy mediaPlayer is already released");
}
}

Expand Down Expand Up @@ -542,13 +521,21 @@ private void startTimingRunnable() {
mTimingRunnable = new Runnable() {
@Override
public void run() {
int currentPosition;
if (isVideoType) {
currentPosition = mVideoView.getCurrentPosition();
} else {
currentPosition = audioPlayer.getCurrentPosition();
// prevent crashing when video is playing and we close editor
try {
if (mediaPlayer == null || !mediaPlayer.isPlaying()) {
mTimingHandler.removeCallbacks(mTimingRunnable);
return;
}
} catch (IllegalStateException e) {
e.printStackTrace();
mTimingHandler.removeCallbacks(mTimingRunnable);
Log.d(TAG, "startTimingRunnable mediaPlayer is already released");
return;
}

int currentPosition = mediaPlayer.getCurrentPosition();

if (currentPosition >= endTime) {
onMediaPause();
seekTo(endTime, true); // Ensure exact end time display
Expand All @@ -562,15 +549,7 @@ public void run() {
}

private void updateCurrentTime(boolean needUpdateProgress) {
// TODO: check the case after drag the progress indicator and hit play, it'll play a little bit earlier than the progress indicator

int currentPosition;
if (isVideoType) {
currentPosition = mVideoView.getCurrentPosition();
} else {
currentPosition = audioPlayer.getCurrentPosition();
}

int currentPosition = mediaPlayer.getCurrentPosition();
int duration = mDuration;

if (currentPosition >= duration - 100) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-video-trim",
"version": "2.2.4",
"version": "2.2.5",
"description": "Video trimmer for your React Native app",
"main": "lib/commonjs/index",
"module": "lib/module/index",
Expand Down

0 comments on commit 1db01c6

Please sign in to comment.