Play different videos back to back seamlessly using Unity Video Player

Mike picture Mike · Nov 20, 2017 · Viewed 9.5k times · Source

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();
     }
}

Answer

Programmer picture Programmer · Nov 21, 2017

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));
}