Android's MediaPlayer setSurface while on paused state
Asked Answered
G

2

6

EDIT:

So apparently this has nothing to do with multiple activities, and this has something to do with the encoding of the video file.

I will try to simplify the question: I have a MediaPlayer object in a paused state, when I call mediaPlayer.getCurrentPosition() I get a result which is accurate. When I call mediaPlayer.setSurface() (using a different surface) followed by mediaPlayer.play() on this object the video is played in a different position than the one returned by getCurrentPosition(). I am testing on API >= ICE_CREAM_SANDWICH.

  • Tested both using a local resource in the project and via network stream, result: no difference.

Link to video file: http://wpc.4ba9.edgecastcdn.net/804BA9/testenvmedia/m_8705_LuS3w7ctSZjB.mov.bs.mp4


I have two activities, Activity-A and Activity-B.

Activity-A plays video from a remote stream to a TextureView using a MediaPlayer, it also holds a button, who's purpose is to:

  1. Call mediaPlayer.pause();

  2. Open Activity-B.

Activity-B holds also a TextureView which is supposed to play the same video at it's current location using the same MediaPlayer object.

Activity-A onCreate method:

    textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {        
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int w, int h) {
            try {
                final MediaPlayer mediaPlayer = MyApplication.getMediaPlayer();
                mediaPlayer.setDataSource(context, videoURI);
                mediaPlayer.setSurface(new Surface(surfaceTexture));
                mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                mediaPlayer.setLooping(shouldLoop);
                mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                    @Override
                    public void onPrepared(MediaPlayer mp) {
                        mp.start();
                    }
                });

                mediaPlayer.prepareAsync();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return false; }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
    });

    btnFullScreen.setOnClickListener(new OnClickListener(){
    @Override
    public void onClick(View v) {     
        Log.e("WHATEVER", ">>> pause video at " + MyApplication.getMediaPlayer().getCurrentPosition());
        MyApplication.getMediaPlayer().pause();
        Intent intent = new Intent(getActivity(), ActivityB.class);
        startActivity(intent);
    }});

Activity-B onCreate method:

        textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int w, int h) {
            MediaPlayer mediaPlayer = MyApplication.getMediaPlayer();
            mediaPlayer.setSurface(new Surface(surface));

            Log.e("WHATEVER", ">>> resume video at " + mediaPlayer.getCurrentPosition());
            mediaPlayer.start();
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            return false;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        }
    });

LogCat output:

10-10 17:33:15.966  13886-13886/com.whatever.android.debug E/WHATEVER﹕ >>> pause video at 1958
10-10 17:33:16.817  13886-13886/com.whatever.android.debug E/WHATEVER﹕ >>> resume video at 1958

So even though it seems like the MediaPlayer is going to resume from that exact position, I get different behaviour for different videos, most of them start at the exact same frame every time I try to play those in Activity-B, one video starts at the very beginning, other one starts at some other point every time.

From Android's documentation of MediaPlayer.SetSurface() method:

This method can be called in any state and calling it does not change the object state.

Any help would be greatly appreciated. Thanks!

Gabe answered 10/10, 2013 at 15:1 Comment(15)
Have you tried to initialize and create your MediaPlayer in a singleton? vogella.com/articles/DesignPatternSingleton/article.htmlLevitical
@Levitical it is a Singleton, that's how I share it with the second activity.Gabe
Oh sry, didn't see it. Hummm....Levitical
Why do you want a new Activity? It seems like watching the video is the activity, and pressing the button should just modify Activity-A rather than starting a new one. Have you looked at Fragments? they might be what you need developer.android.com/reference/android/app/Fragment.htmlSibelle
@GrahamRogers the setup behind is more complex than it seems. It has to be in another activity..Gabe
It might help to give more details on your setup then. Other than that, you could pass the value of getCurrentPosition() from Activity-A to Activity-B and then use seekTo(int) to set the position of the video.Sibelle
@GrahamRogers this will require to load the stream from the start, call prepare, and only then call seekTo(). I'm trying to re-use the same MediaPlayer object between two activities while preserving it's state.Gabe
Sticking with one Activity then, the button makes the video fullscreen right? So could you not just change the layout using setContentView(...) in the button's callback method with a layout for fullscreen. Is there anything else complicated that requires a new activity, or is it just making the video fullscreen?Sibelle
@GrahamRogers because it is not my use case, and is not my question.. The question is about sharing an instance of a MediaPlayer object between multiple activities. If I wanted to host them all inside one activity I would do that, but it is not the case.Gabe
I realise that's not your question, but I'm suggesting that you might be going about your problem in the wrong way and trying to suggest alternatives. It seems like MediaPlayer just wasn't designed to be used between multiple activities, and unless you give a good reason to use multiple activities over just one, I suggest you try a different method.Sibelle
@TomReznik Are the particular media files you are having problems with available online somewhere? What types of files are they, and how far away from the desired position does the last one you mentioned start? Some files don't support random seeking, and others that do may have sparse i-frames, causing a playback to resume at a slightly different position.Monohydroxy
@Monohydroxy I've attached the file I was using for testing up until now, though it seems to happen with every video I've tested so it's probably an issue with the code. The position is always the same, I always get the video played at it's last two seconds exactly at the same frame, I can't give you the exact position as getCurrentPosition returns the expected position rather than the one played.Gabe
@Monohydroxy tested on a different device (originally LG Nexus 4, now HTC Sensation) on the HTC, the video plays from the beginning..Gabe
I took a quick look at your video. VLC player can seek to any time, but both Totem and ffplay refuse to seek anywhere other than a point in second 9 of the video. I have also seen a post about ffmpeg not being able to cut a video encoded with Lavf54.0.100 properly (this video has that encoding). My best guess is the same as yours, I believe, which is that Android's AwesomePlayer has the same or similar seek limitations as most other players when working with certain encodings. Short of writing an OMX codec, I think you should probably just find a different video for testing.Monohydroxy
@Monohydroxy thanks for your input! we have our own encoding server that does that. I will review the encoding settings following your comment and post update once I have more info.Gabe
T
8

it seems your problem is with this specific video, more keyframes will help the player to resume the playing more accurately.

try to re-encode again, with ffmpeg and libx264 try adding these parameters:

-g=25 -keyint_min=25
Tricuspid answered 17/10, 2013 at 13:3 Comment(0)
H
0

Try passing the position of your MediaPlayer from activity A to Activity B using Intent.putExtra(). So Basically in Activity A where you call intent to start activity B, you would do this:

 public void onClick(View v) {     
    Log.e("WHATEVER", ">>> pause video at " + MyApplication.getMediaPlayer().getCurrentPosition());
    MyApplication.getMediaPlayer().pause();
    int position = MyApplication.getMediaPlayer().getCurrentPosition(); //new line added
    Intent intent = new Intent(getActivity(), ActivityB.class);
    intent.putExtra("position", position); //another new line of code
    startActivity(intent);
}

Now in Activity B seek to the position where mediaplayer was paused before calling start, like this:

@Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int w, int h) {
        MediaPlayer mediaPlayer = MyApplication.getMediaPlayer();
        mediaPlayer.setSurface(new Surface(surface));

        Log.e("WHATEVER", ">>> resume video at " + mediaPlayer.getCurrentPosition());
        mediaPlayer.seekTo(getIntent().getExtras().getInt("position"));// new line of code
        mediaPlayer.start();
    }
Haiphong answered 17/10, 2013 at 1:50 Comment(1)
I have tried that as well, it will not work, mediaPlayer.seekTo() does virtually nothing in the scenario I've specified..Gabe

© 2022 - 2024 — McMap. All rights reserved.