Does anyone have any sort of solution to this problem regarding Unity's video player component?
Basically I have a semi interactive application that plays a series of videos back to back when something happens. The problem is once the first video has ended there is a good 3-5 second gap of nothing because the second video needs time to load, however this does not look good in my application.
I need a way to either preload the second video (ideal) or a fairly simple way to hide the gap (like a still frame over top). I should mention for the latter idea the videos are displayed spherical.
using UnityEngine;
using UnityEngine.Video;
public class selectAndPlay_video : MonoBehaviour {
public VideoPlayer videoPlayer;
public VideoClip NewClip;
void OnEnable()
{
videoPlayer.loopPointReached += loopPointReached;
}
void OnDisable()
{
videoPlayer.loopPointReached -= loopPointReached;
}
void loopPointReached(VideoPlayer v)
{
videoPlayer.clip = NewClip;
videoPlayer.Play();
}
}
The key to the solution of this problem is the VideoPlayer.Prepare()
function, VideoPlayer.time
and VideoPlayer.clip.length
properties. First of all, I do suggest you forget the current way you use to play video with the events. Use coroutine as done in the example from this question because everything in the answer below assumes you are in a coroutine function. Below is how to play different videos without the long wait between them:
1.Have array/list of VideoClip
to play.
2.Create new list of VideoPlayer
from the VideoClip
array in #1. Simply set the VideoPlayer.clip
property with the VideoClips
from #1.
3.Call VideoPlayer.Prepare()
to prepare the first VideoPlayer
in the list then wait in a while
loop until the prepare is done or VideoPlayer.isPrepared
becomes true
.
4.Call VideoPlayer.Play()
to play the video you just prepared in #3.
Play different videos back to back seamlessly
This is the most important part.
When playing a video, check if the current time of the VideoPlayer
that is being played is half way the length of the video. If it is half way, call VideoPlayer.Prepare()
on the next video in the array then wait for video from #4 to finish playing before playing the next video.
By doing this, the next video will start preparing itself while the current one is still playing and that prepare process should be done by the time the current video has finished playing. You can then play the next video without waiting for it to load for a long time.
5.Wait for video from #4 to finish playing in a while
loop until VideoPlayer.isPlaying
becomes false
.
6.While waiting inside the while
loop in #5, check if the video has played half way with if (videoPlayerList[videoIndex].time >= (videoPlayerList[videoIndex].clip.length / 2))
. If this is true, call VideoPlayer.Prepare()
on the next VideoPlayer
in the list to keep it ready.
7.After the while
loop exist, the current video is done playing. Play the next VideoPlayer
in the list then repeat again from #5 to prepare the next VideoPlayer
in the list.
The script below should do everything I described above. Just create a RawImage
and plug it to the image slot. All all your videos to the videoClipList
variable too. It should play video one after another without the long loading time. The Debug.Log
are expensive so remove them once you verify that it is working properly.
//Raw Image to Show Video Images [Assign from the Editor]
public RawImage image;
//Set from the Editor
public List<VideoClip> videoClipList;
private List<VideoPlayer> videoPlayerList;
private int videoIndex = 0;
void Start()
{
StartCoroutine(playVideo());
}
IEnumerator playVideo(bool firstRun = true)
{
if (videoClipList == null || videoClipList.Count <= 0)
{
Debug.LogError("Assign VideoClips from the Editor");
yield break;
}
//Init videoPlayerList first time this function is called
if (firstRun)
{
videoPlayerList = new List<VideoPlayer>();
for (int i = 0; i < videoClipList.Count; i++)
{
//Create new Object to hold the Video and the sound then make it a child of this object
GameObject vidHolder = new GameObject("VP" + i);
vidHolder.transform.SetParent(transform);
//Add VideoPlayer to the GameObject
VideoPlayer videoPlayer = vidHolder.AddComponent<VideoPlayer>();
videoPlayerList.Add(videoPlayer);
//Add AudioSource to the GameObject
AudioSource audioSource = vidHolder.AddComponent<AudioSource>();
//Disable Play on Awake for both Video and Audio
videoPlayer.playOnAwake = false;
audioSource.playOnAwake = false;
//We want to play from video clip not from url
videoPlayer.source = VideoSource.VideoClip;
//Set Audio Output to AudioSource
videoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource;
//Assign the Audio from Video to AudioSource to be played
videoPlayer.EnableAudioTrack(0, true);
videoPlayer.SetTargetAudioSource(0, audioSource);
//Set video Clip To Play
videoPlayer.clip = videoClipList[i];
}
}
//Make sure that the NEXT VideoPlayer index is valid
if (videoIndex >= videoPlayerList.Count)
yield break;
//Prepare video
videoPlayerList[videoIndex].Prepare();
//Wait until this video is prepared
while (!videoPlayerList[videoIndex].isPrepared)
{
Debug.Log("Preparing Index: " + videoIndex);
yield return null;
}
Debug.LogWarning("Done Preparing current Video Index: " + videoIndex);
//Assign the Texture from Video to RawImage to be displayed
image.texture = videoPlayerList[videoIndex].texture;
//Play first video
videoPlayerList[videoIndex].Play();
//Wait while the current video is playing
bool reachedHalfWay = false;
int nextIndex = (videoIndex + 1);
while (videoPlayerList[videoIndex].isPlaying)
{
Debug.Log("Playing time: " + videoPlayerList[videoIndex].time + " INDEX: " + videoIndex);
//(Check if we have reached half way)
if (!reachedHalfWay && videoPlayerList[videoIndex].time >= (videoPlayerList[videoIndex].clip.length / 2))
{
reachedHalfWay = true; //Set to true so that we don't evaluate this again
//Make sure that the NEXT VideoPlayer index is valid. Othereise Exit since this is the end
if (nextIndex >= videoPlayerList.Count)
{
Debug.LogWarning("End of All Videos: " + videoIndex);
yield break;
}
//Prepare the NEXT video
Debug.LogWarning("Ready to Prepare NEXT Video Index: " + nextIndex);
videoPlayerList[nextIndex].Prepare();
}
yield return null;
}
Debug.Log("Done Playing current Video Index: " + videoIndex);
//Wait until NEXT video is prepared
while (!videoPlayerList[nextIndex].isPrepared)
{
Debug.Log("Preparing NEXT Video Index: " + nextIndex);
yield return null;
}
Debug.LogWarning("Done Preparing NEXT Video Index: " + videoIndex);
//Increment Video index
videoIndex++;
//Play next prepared video. Pass false to it so that some codes are not executed at-all
StartCoroutine(playVideo(false));
}