Android MediaPlayer setNextMediaPlayer not working

2019-08-26 10:24发布

My objective is to create a VideoView that can play videos in a pre-defined play list.

I'm trying to use MediaPlayer.setNextMediaPlayer(...) to allow a seamless transition between two videos. However, when the first video finishes playing, the 2nd video will not start automatically as it should according to the documentation.

Xamarin Android Code:

    Queue<MediaPlayer> MediaPlayerList = null;
    private void PlayVideo()
    {
        MediaPlayerList = new Queue<MediaPlayer>();

        //Let's go ahead and create all media players
        VideoView_CurrentVideoView = new VideoView(this);

        VideoView_CurrentVideoView.Completion += mVideoView_Completion;
        VideoView_CurrentVideoView.Prepared += mVideoView_Prepared;

        //Let's prepare all MediaPlayer
        for (int i = 0; i < VideoView_CurrentVideoChannel.VideoAssetList.Count; i++)
        {
            string filePath = FilePath[i];
            if (i == 0)
            {
                VideoView_CurrentVideoView.SetVideoPath(filePath);
                VideoContainer.AddView(VideoView_CurrentVideoView);
            }
            else
            {
                MediaPlayer mpNew = new MediaPlayer();
                mpNew.SetDataSource(filePath);
                MediaPlayerList.Enqueue(mpNew);
            }
        }

        VideoView_CurrentVideoView.Start();
    }

    void mVideoView_Completion(object sender, EventArgs e)
    {
        MediaPlayer mp = (MediaPlayer)sender;
        mp.Release();
    }

    void mVideoView_Prepared(object sender, EventArgs e)
    {
        MediaPlayer mp = (MediaPlayer)sender;

        //Take the next available MediaPlayer from the queue
        MediaPlayer nextMediaPlayer = MediaPlayerList.Dequeue();

        //Put the current MediaPlayer at the end of the queue
        MediaPlayerList.Enqueue(mp);


        nextMediaPlayer.Prepare();
        mp.SetNextMediaPlayer(nextMediaPlayer);
    }

Any help or suggestions will be greatly appreciated. This is coded in Xamarin Android.

Update #1: After moving the .Prepare() away from Prepared() event

    Queue<string> VideoListQueue = null;
    MediaPlayer NextMediaPlayer = null;
    private void PlayVideo()
    {
        string filePath = FilePath[0];


        //Create video view
        if (VideoContainer.ChildCount == 0)
        {
            //setup the videoview container
            VideoView_CurrentVideoView = new VideoView(this);
            VideoView_CurrentVideoView.Info += VideoView_CurrentVideoView_Info;
            VideoView_CurrentVideoView.Error += VideoView_CurrentVideoView_Error;

            LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.FillParent);
            param.LeftMargin = 0;
            param.RightMargin = 0;
            param.BottomMargin = 0;
            param.TopMargin = 0;

            VideoView_CurrentVideoView.LayoutParameters = param;
            VideoView_CurrentVideoView.LayoutParameters.Width = ViewGroup.LayoutParams.FillParent;
            VideoView_CurrentVideoView.LayoutParameters.Height = ViewGroup.LayoutParams.FillParent;

            VideoView_CurrentVideoView.Completion += VideoView_CurrentVideoView_Completion;
            VideoView_CurrentVideoView.Prepared += VideoView_CurrentVideoView_Prepared;
            VideoContainer.AddView(VideoView_CurrentVideoView);
        }

        VideoView_CurrentVideoView.SetVideoPath(filePath);
        VideoView_CurrentVideoView.SeekTo(0);
        VideoView_CurrentVideoView.Start();
    }

    void VideoView_CurrentVideoView_Prepared(object sender, EventArgs e)
    {
        //Do nothing at this moment
        MediaPlayer mp = (MediaPlayer)sender;
    }

    void VideoView_CurrentVideoView_Completion(object sender, EventArgs e)
    {
        //GC the finished MediaPlayer
        MediaPlayer mp = (MediaPlayer)sender;
        mp.Reset();
        mp.Release();
        mp = null;


        //Preparing the next MediaPlayer
        MediaPlayer currentPlayer = NextMediaPlayer;
        NextMediaPlayer = SetupNextMediaPlayer();
        currentPlayer.SetNextMediaPlayer(NextMediaPlayer);
    }


    MediaPlayer SetupNextMediaPlayer()
    {
        MediaPlayer mp = new MediaPlayer();
        //When the video start playing, let's get ready for next one
        string sourceURL = VideoListQueue.Dequeue();
        VideoListQueue.Enqueue(sourceURL);

        string filePath = sourceURL;


        mp = new MediaPlayer();
        mp.Info += VideoView_CurrentVideoView_Info;
        mp.Completion += VideoView_CurrentVideoView_Completion;
        mp.Prepared += VideoView_CurrentVideoView_Prepared;

        mp.SetDataSource(filePath);
        mp.Prepare();


        //Fire back the created MediaPlayer object to the caller
        return mp;
    }

    void VideoView_CurrentVideoView_Info(object sender, MediaPlayer.InfoEventArgs e)
    {
        Console.WriteLine("What = " + e.What);

        switch (e.What)
        {
            case MediaInfo.VideoRenderingStart:
                {
                    //This is only happening on video first started
                    NextMediaPlayer = SetupNextMediaPlayer();
                    e.Mp.SetNextMediaPlayer(NextMediaPlayer);
                    break;
                }
        }

    }

    void VideoView_CurrentVideoView_Error(object sender, MediaPlayer.ErrorEventArgs e)
    {
        e.Handled = true;
    }

At this moment, the media player will begin playing the 2nd video once the first one is done. However, the 2nd video only have sound and no video showing.

Anyone know what I did wrong? I have a feeling that it has to do something with the MediaPlayer not attached to the SurfaceView. However, I created the view using VideoView, how can I get the Surface from VideoView?

3条回答
戒情不戒烟
2楼-- · 2019-08-26 11:05

Regarding playing 2nd video only with sound: try to implement OnCompletionListener listener for each MediaPlayer with this:

mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

    @Override
    public void onCompletion(MediaPlayer mp) {
        mp.setDisplay(null); //for current mediaPlayer
        nextMediaPlayer.setDisplay(getHolder()); //for next video
    }
});

I can't say that is is gapless, but somehow it works. To archive this I don't use standard VideoView, but custom View, that extends from SurfaceView.

查看更多
3楼-- · 2019-08-26 11:07

what if you prepare and play the next mediaplayer on .Completion event? have you try it? although it may have a small delay

查看更多
不美不萌又怎样
4楼-- · 2019-08-26 11:11

After many years of testing, this problem does not happen on all hardware. For example, I run the same APK on Nexus7, it appears to be seamless and everything is working. In contrast, if I run it on Amlogic media player board, it will render the above-described problem.

I decided to close this post off with a conclusion that it is something to do with the hardware. I know someone overcome this limitation by run everything in OpenGL, but that is completely a separate beast to deal with.

Conclusion

If you are having a similar problem as described above, there's nothing much you can do as it is heavily dependent on the hardware.

查看更多
登录 后发表回答