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?
Regarding playing 2nd video only with sound: try to implement OnCompletionListener listener for each MediaPlayer with this:
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.
what if you prepare and play the next mediaplayer on .Completion event? have you try it? although it may have a small delay
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.