3

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();
     }
}
Mike
  • 51
  • 1
  • 1
  • 4

1 Answers1

5

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));
}
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Thank you for the detailed answer. However it is going over my head a little bit, I am just now getting back into Unity and programming after several years away from it – Mike Nov 21 '17 at 19:01
  • If I am understanding it right this script assumes all videos will be played back to back 1 through 10 or such. This is somewhat what I needed but unfortunately it gets a little more complicated. It works more like this: video 1 plays then video 2 plays and loops until the user does something. based on what the user does video 3, 4 or 5 will play. This is the kind of structure I need. In most cases its never known when or what the next video is going to be until the user selects something. – Mike Nov 21 '17 at 19:08
  • Ok I think I understand now. The only thing I am a little confused on is if in your method do you have a Video Player for each video clip? The issue I've been having with Prepare() is as soon as you set the video player to a new clip it stops the current clip being played and as far as I can tell Prepare() needs to be called after the new clip is selected otherwise its just reloading the clip that is already playing. I attempted your method of calling Prepare() halfway through the video clip and the result is the same, keep in mind this was using only 1 Video Player. – Mike Nov 22 '17 at 16:31
  • @Programmer, this is really helpful for me for switching among multiple videos. In my case, FPS increased from 2 to 30-35 FPS but I want 50-60 FPS. Are there any other optimizations I can try to increase the FPS further? – MSD Paul Aug 08 '18 at 15:58
  • @MSDPaul Nope. Playing Video is a very expensive stuff and Unity is already ding it in another Thread for you. 30-35 FPS seems ok. What exactly are you doing and when do you see drop in fps? – Programmer Aug 08 '18 at 16:08
  • @Programmer, Nothing I have a player and a few objects around. I kept them static although the FPS is around 35 (just video clips are getting switched). Without the video playing, the FPS can reach 120-150 (on the desktop), which is quite a large. Any Suggestions? – MSD Paul Aug 08 '18 at 16:14
  • Nope. Nothing else you can do. 30-35 FPS is totally fine. Although, you should try rendering the video to the camera instead of RawImage by changing the Video Render Mode to Camera. – Programmer Aug 08 '18 at 16:30
  • @Programmer, what I found with the increase in the number of video clips the FPS getting lower. For a small number of videos its great solution but it doesn't seem to be scalable with a large number of video and also what I found preparing the video clips taking a huge amount of time. Any other scalable solutions or any other suggestions? – MSD Paul Aug 08 '18 at 21:44
  • *"huge amount of time"* Like what time? Give an estimate. Also, what's the length of the playing you're playing? – Programmer Aug 08 '18 at 21:47
  • @Programmer Why are you preparing next video when halfway of the video? Why halfway? Is it arbitrary? – Matthieu Charbonnier Feb 12 '22 at 22:19