Android style movie clip freezes local file playback

I am developing an application that temporarily locally disables local video files at retail outlets. For this, I use the Android MediaPlayer class and am developing for the Minix X8 Plus, which is a multimedia center that runs on Android 4.4. The device will play the video on the HDMI screen.

In most cases, the video plays successfully, but I have problems with freezing the image quite regularly, while the sound continues to play. The frequency of freezing changes the video to video, but it is almost always at the beginning of video playback and can be either in the first pass or in subsequent cycles. The media player is configured to loop using mediaPlayer.setLooping(true) I have the following listeners:

  • MediaPlayer.onErrorListener
  • MediaPlayer.onInfoListener
  • MediaPlayer.OnVideoSizeChangedListener
  • MediaPlayer.OnSeekCompleteListener
  • MediaPlayer.OnBufferingUpdateListener
  • MediaPlayer.OnPreparedListener

During freezes, I get no callbacks, errors, no callbacks. I think the problem should be internal for Android MediaPlayer, because I get no exceptions or errors, and the problem only occurs between 5-20% of the time, depending on the video.

I have a work around where I control the position of the “playhead” mediaPlayer.getCurrentPosition() compared to the amount on which the media player has buffered MediaPlayer.OnBufferingUpdateListener.onBufferingUpdate() , which returns the percentage of the current video file. I find that sometimes freezing occurs and the play position is constantly updated, as if everything is in order, but ultimately the play position will convey the amount that the player started. When this happens, I call mediaPlayer.reset() and go through the entire setup process again, calling mediaPlayer.setDataSource() and mediaPlayer.prepareAsync() . I also make mediaPlayer.getCurrentPosition() that mediaPlayer.getCurrentPosition() returns the same position repeatedly. On some videos, this either happens at the beginning (stuck at position 0) or at the end after the video has frozen (stuck at the last position, the loop could not be completed).

I would really like to find another solution. All videos behave differently, and even when the media player reboots, I still have a frozen image long enough to be a problem.

I uncovered SO for the solution. The problem is similar to the following: SO message here, but not identical.

As suggested in this post, I polled mediaPlayer.getVideoWidth() and mediaPlayer.getVideoHeight() until it returns a number not 0 before starting playback.

I looked at ExoPlayer, but it had problems with a crash when playing a video file that Android MediaPlayer has no problems with. I also read here that there is no benefit in using ExoPlayer to play local files.

I also considered Grafika as an alternative to MediaPlayer, but this is only a video, without sound and not quite a complete media player.

I also looked at VLC for Android, but there is no documentation on its use as a library in the application, and it still works pretty hard.

If someone had similar problems or you can offer an open source alternative for Android MediaPlayer, I would like to hear from you.

Here is the code from the Activity that plays the video. This is a lot, I tried to catch it before the code of the media player.

  private void initMediaPlayerOnPreparedListener() { try { mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { obtainVideoDimens(); obtainDisplayDimens(); calcVidToDisplayRatio(); applyTransformTextureView(); applyLayoutParamsTextureView(); if (playbackStuckAtZeroReset) { mediaPlayer.seekTo((33)); // should be one one frame into the video at 30 frames per second } mediaPlayer.start(); launchCheckVidProgThread(); Log.d(J, "initMediaPlayerOnPreparedListener() -> mediaPlayer playing: " + mediaPlayer .isPlaying()); MyApplication.setVideoPlaying(true); } }); } catch (IllegalArgumentException | SecurityException | IllegalStateException e) { e.printStackTrace(); } } private void obtainVideoDimens() { videoWidth = 0; videoHeight = 0; do { videoWidth = mediaPlayer.getVideoWidth(); videoHeight = mediaPlayer.getVideoHeight(); Log.d(J, "video width: " + videoWidth + " x video height: " + videoHeight); } while (videoWidth == 0 && videoHeight == 0); } private void obtainDisplayDimens() { DisplayMetrics displayMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics); displayWidth = displayMetrics.widthPixels; int displayHeight = displayMetrics.heightPixels; Log.d(J, "real display metrics width pix: " + displayWidth + " x height pix: " + displayHeight); } private void calcVidToDisplayRatio() { videoToDisplayScaleWidth = displayWidth / videoWidth; Log.d(J, "video to display scale width: " + videoToDisplayScaleWidth); } private Matrix applyMatrixScale(float w, float h) { Matrix matrix = new Matrix(); matrix.setScale(w, h); return matrix; } private void initMediaPlayerOnSeekListener() { mediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() { @Override public void onSeekComplete(MediaPlayer mp) { } }); } private void initMediaPlayerOnErrorListener() { if (mediaPlayer != null) { mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { Log.d(J, "MediaPlayer error: " + mp.toString() + " what: " + what + " extra: " + extra); stopAndResetMediaPlayer(); return true; } }); } } private void initMediaPlayerOnInfoListener() { if (mediaPlayer != null) { mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() { @Override public boolean onInfo(MediaPlayer mp, int what, int extra) { Log.d(J, "MediaPlayer info: " + mp.toString() + " what: " + what + " extra: " + extra); if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { } // never was called, if (what == MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING) { Log.d(J, "media player video track lagging!"); } return false; } }); } } private void initMediaPlayerOnBufferingListener() { mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() { @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { if (percent > 0) { setLastBuffPercent(percent); } } }); } private static void stopAndResetMediaPlayer() { if (!isMediaPlayerNull()) { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); MyApplication.setVideoPlaying(false); } resetMediaPlayer(); } } private static void resetMediaPlayer() { if (!isMediaPlayerNull()) { Log.d(J, "resetting media player!!!!!!!!"); mediaPlayer.reset(); declareMediaPlayerAttributes(); mediaPlayer.prepareAsync(); } } private void initMediaPlayerOnVideoSizeChangedListener() { mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() { @Override public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { Log.d(J, "onVideoSizeChanged! width: " + width + " height: " + height); if (videoWidth != width || videoHeight != height) { // if obtainVideoDimens returns either dimen to 0, screen goes black, never recovers if (mp.isPlaying()) { mp.pause(); } videoWidth = width; videoHeight = height; calcVidToDisplayRatio(); applyTransformTextureView(); applyLayoutParamsTextureView(); mp.start(); } } }); } private void initMediaPlayerOnSeekCompleteListener() { mediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() { @Override public void onSeekComplete(MediaPlayer mp) { Log.d(J, "onSeekComplete!"); launchCheckVidProgThread(); mp.start(); // see if this changes things } }); } private void initMediaPlayerOnCompletionListener() { mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Log.d(J, "media player onCompletion"); } }); } 

Here is the background thread that resets the media player:

 private static class CheckVideoProgressRunnable implements Runnable { private static final int ZERO = 0; private static final int SECONDS_IN_ERROR_BEFORE_RESET = 5; //private static final int POLLS_PER_SECOND = 12; private static final int POLLS_PER_SECOND = 5; private static final int FIFTY = 50; private static final int ONE_HUNDRED = 100; private static final int ONE_THOUSAND = 1000; private static final int BUFFER_PERCENT_SAFETY_THRESHOLD = 15; private static final long SLEEP_TIME = ONE_THOUSAND / POLLS_PER_SECOND; // roughly 5 x per second private int lastPlaybackPosition; private int videoDuration; private int playbackPositionStuckCount; // tracks the amount of times the playback position remains static private int bufferExceededCount; // tracks the amount of times the playback position has exceeded the buffer position + BUFFER_PERCENT_SAFETY_THRESHOLD @Override public void run() { Log.d(J, "start of run in video monitoring thread!!!!!!"); CrashHandler crashHandler = new CrashHandler(context); crashHandler.initExceptionHandler(); boolean newLoop = false; videoDuration = mediaPlayer.getDuration(); int percentOfVideoDurationThreshold = videoDuration / FIFTY; // 2% of video duration int loopCount = ZERO; int playbackStuckThresh = POLLS_PER_SECOND * SECONDS_IN_ERROR_BEFORE_RESET; // if video is stuck for roughly five seconds, reset mediaPlayer Log.d(J, "sleep time millis: " + SLEEP_TIME); Log.d(J, "playbackStuckThresh: " + playbackStuckThresh); Log.d(J, "percent of video duration threshold: " + percentOfVideoDurationThreshold); Log.d(J, "video duration: " + videoDuration); while (!isMediaPlayerNull()) { loopCount++; try { Thread.sleep(SLEEP_TIME); } catch (InterruptedException e) { e.printStackTrace(); } int currentPos = ZERO; int currentPlaybackPercent = ZERO; int buffPercent = ZERO; if (!isMediaPlayerNull()) { currentPos = mediaPlayer.getCurrentPosition(); if (currentPos == lastPlaybackPosition) { playbackPositionStuckCount++; } else { lastPlaybackPosition = currentPos; playbackPositionStuckCount = ZERO; } currentPlaybackPercent = (int) (((double) currentPos / videoDuration) * ONE_HUNDRED); buffPercent = getLastBuffPercent(); if (buffPercent > ZERO && (buffPercent + BUFFER_PERCENT_SAFETY_THRESHOLD) < currentPlaybackPercent) { bufferExceededCount++; } boolean playbackStuck = playbackPositionStuckCount > playbackStuckThresh; boolean bufferExceeded = bufferExceededCount > playbackStuckThresh; if (((bufferExceeded) || playbackStuck)) { Log.d(J, "buffer: " + buffPercent + "%"); Log.d(J, "position: " + currentPlaybackPercent + "%"); Log.d(J, "playback stuck count: " + playbackPositionStuckCount); Log.d(J, "buffer exceeded count: " + bufferExceededCount); Log.d(J, "buffer exceeded: " + bufferExceeded + " playback stuck: " + playbackStuck); if (currentPos == 0) { //handler.post(new MediaPlayerSeekRunnable(videoDuration)); handler.post(new ResetMediaPlayerRunnable()); playbackStuckAtZeroReset = true; break; } else { handler.post(new ResetMediaPlayerRunnable()); break; } } } // for setting the volume the video loops (volume does not stay muted on loop without this) if (!isMediaPlayerNull()) { if (currentPos < percentOfVideoDurationThreshold && newLoop) { if (volumeMuted) { float zero = Constants.ZERO_FLOAT; mediaPlayer.setVolume(zero, zero); Log.d(J, "mediaPlayer volume: " + zero); } else { float loopingVolume = MyApplication.getCurrentDeviceVolume(); mediaPlayer.setVolume(loopingVolume, loopingVolume); Log.d(J, "mediaPlayer volume: " + loopingVolume); } Log.d(J, "video looping!!!!!!!!!!!!!!!!!!! volume muted: " + volumeMuted); newLoop = false; Log.d(J, "new loop set: " + newLoop); } } if (videoDuration - currentPos < percentOfVideoDurationThreshold && !newLoop) { newLoop = true; Log.d(J, "new loop set: " + newLoop); } // these conditions are for logging only if (newLoop) { Log.d(J, "current position: " + currentPos + " duration: " + videoDuration); } if (loopCount % (ONE_THOUSAND / SLEEP_TIME) == ZERO) { // once a second Log.d(J, "current position: " + currentPos); Log.d(J, "position: " + currentPlaybackPercent + "%"); Log.d(J, "buffer: " + buffPercent + "%"); } } Log.d(J, "video monitoring thread hit break"); } } 
+5
source share

Source: https://habr.com/ru/post/1237673/


All Articles