6

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().
Sergio Carneiro
  • 3,726
  • 4
  • 35
  • 51
  • `SurfaceView` seems to have lot of bugs. If your app supports only 4.0+ then you can try using `TextureView` instead of `SurfaceView`. – Abhishek V Apr 25 '14 at 12:54
  • What type of media are you trying to use? Video isn't typically just a series of images. There are a variety of frame types and not all of them provide a basis to start playback... which means you can't seek to them without either some amount of inaccuracy or some processing. – Dave Apr 29 '14 at 15:52
  • It's a .mp4 video recorded by the phone's camera. The problem is not the inaccuracy or processing time, it just doesn't show anything. – Sergio Carneiro Apr 29 '14 at 15:58
  • Please review my updated answer. – JstnPwll May 01 '14 at 16:14

1 Answers1

4

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.

Community
  • 1
  • 1
JstnPwll
  • 8,585
  • 2
  • 33
  • 56
  • Well, that's the point where I was. I guess MediaPlayer isn't really prepared for this, but what I'm trying to do is actually pretty simple, is not rocket science. It should be some player capable of doing it pretty easily. Also, I tested FFMPEG's `getFrameAtTime()` and it doesn't work either, it takes too much time to get the frame (some seconds), which as you can imagine, is not user-friendly. – Sergio Carneiro May 02 '14 at 00:39