Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MediaPlayer seekTo() doesn't update SurfaceView

I already saw a lot of questions regarding issues about Android's MediaPlayer, most of them because of the seekTo() function. Now I tried to use it, and it worked just as expected: badly!

This function seems to be very inconsistent, specially when we want to provide its functionality while the video is paused. In my case, I have videos of 30 to 60 frames and I want to play them one by one - without that delay that MediaMetadataRetriever.getFrameAtTime() provides.

The problem I'm facing is when I call seekTo(), it doesn't update the SurfaceView. It only works in the first time, after that the SurfaceView just stays the same, it never gets updated again.

I heard a rumor that seekTo() only works with a minimum interval of 1 second, but I tested with a longer video and seeking second by second didn't work either.

Code

mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurfaceHolder.addCallback(this);

mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDisplay(mSurfaceHolder);

mMediaPlayer.setOnSeekCompleteListener(new OnSeekCompleteListener() {       
    @Override
    public void onSeekComplete(MediaPlayer mp) {
        // Need this postDelayed(), otherwise the media player always 
        // returns 0 in getCurrentPosition(), don't know why...
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            public void run() {
                mMediaPlayer.pause();
            }
        }, 100);
    }
});

mMediaPlayer.setDataSource(localfile_source);
mMediaPlayer.prepare();

// Set the initial position.
mMediaPlayer.start();
mMediaPlayer.seekTo(500);

/** 
We're assuming that targetMs is within 0 and the video's duration.
Also, targetMs is calculated to always move to the next/previous frame:

Example: currentMs + ( 1000 / framerate)
(if framerate = 20, then it will exist a frame in each 50ms) 
*/
private void seekTo(int targetMs) {
    mMediaPlayer.start();
    mMediaPlayer.seekTo(targetMs);
}

Note that because of a known bug regarding using this function while the video is paused, is used a workaround:

  • Start the video;

  • Call seekTo();

  • Pause it on onSeekComplete().
like image 256
Sergio Carneiro Avatar asked Apr 23 '14 00:04

Sergio Carneiro


1 Answers

From [this question]:

"You cannot do frame by frame seeking by using the MediaPlayer API's provided by Android.

If you really want implement frame by frame seeking then you will have to use a 3rd party multimedia framework like FFMPEG or you will need to implement your own."

I created some test code to try it out anyway. I did not start() the video before using seekTo()—I just used seekTo() while paused.

When I moved forward in 50ms increments, I saw a series of about 4-5 frames repeat until roughly 2 seconds had elapsed; then, the set of preview frames changed to a new series of 4-5 frames. This seems to align with my previous trial wherein I moved forward in increments of 2000ms and saw a unique frame for each seekTo() call.

In summary, it appears that MediaPlayer picks several frames in each 2-second interval to be used as preview frames when the video is paused.

like image 145
JstnPwll Avatar answered Oct 06 '22 17:10

JstnPwll