Here below is a class I use to loop a video smoothly:
using System.Collections;
using UnityEngine;
using UnityEngine.Video;
public class MegaPlayer : MonoBehaviour
{
private VideoPlayer[] players;
public RenderTexture targetTexture;
public string url;
void Awake()
{
players = gameObject.GetComponentsInChildren<VideoPlayer>();
for (int i = 0; i < players.Length; i++) {
players*.source = VideoSource.Url;*
players*.url = url;*
players*.playOnAwake = false;*
players*.waitForFirstFrame = true;*
players*.isLooping = false;*
players*.skipOnDrop = true;*
players*.targetTexture = targetTexture;*
}
}
void Start()
{
StartCoroutine(Play(0));
}
IEnumerator Play(int activePlayer)
{
// wait until the video is ready
players[activePlayer].Prepare();
while (!players[activePlayer].isPrepared) {
yield return null;
}
// wait until the current video ends
while (players[activePlayer ^1].isPlaying) {
yield return null;
}
players[activePlayer].Play();
// wait until the current video has not finished
int nextPlayer = activePlayer ^ 1;
while (players[activePlayer].isPlaying) {
if (players[activePlayer].time >= (players[activePlayer].length / 2)) {
players[nextPlayer].time = 0f;
break;
}
yield return null;
}
StartCoroutine(Play(nextPlayer));
}
}
I use two VideoPlayer
that alternate in playing the same video to reproduce a smooth loop without the short break you hear when using the isLooping
property.
The coroutine works 2 times, i.e. each VideoPlayer
plays the video as expected… then, the third time, the image remains freezed at the first frame (but the video still works in the background as I hear the musing going). Then, the fourth time the video plays correctly again, and so on. Long story short, after the first two iterations that work fine, the video is played once yes and once no.
Finally I found where the problem was… I forgot to set the texture of the render area before I start the player at line 77. Here below is the working version:
#region References
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
#endregion
/// <summary>
/// Provides functionality for playing videos in seamless loops.
/// </summary>
public class PlayerManager : MonoBehaviour
{
#region Fields
private RawImage renderArea;
private VideoPlayer[] players;
private double twoThirdOfTheWay;
public string url;
public float overlappingLength = .3f;
#endregion
#region Methods
/// <summary>
/// Called when this script instance is being loaded.
/// </summary>
void Awake()
{
Texture texture = GetComponent<RawImage>().texture;
RenderTexture targetTexture = new RenderTexture(texture.width, texture.height, 0);
Graphics.Blit(texture, targetTexture);
players = gameObject.GetComponentsInChildren<VideoPlayer>();
for (int i = 0; i < players.Length; i++) {
players*.source = VideoSource.Url;*
players*.url = url;*
players*.playOnAwake = false;*
players*.waitForFirstFrame = true;*
players*.isLooping = false;*
players*.skipOnDrop = true;*
players*.targetTexture = targetTexture;*
}
renderArea = GetComponent();
twoThirdOfTheWay = (players[0].length / 3d) * 2d;
}
///
/// Called before the first frame update.
///
void Start()
{
StartCoroutine(Play(0));
}
///
/// Lets the specified player playback the video referenced by .
///
/// The player to start.
IEnumerator Play(int activePlayer)
{
// wait until the video is ready
if (!players[activePlayer].isPrepared) {
players[activePlayer].Prepare();
while (!players[activePlayer].isPrepared) {
yield return null;
}
}
int otherPlayer = activePlayer ^ 1;
// wait until the previous playback reaches the overlapping interval
while (players[otherPlayer].isPlaying && players[otherPlayer].time < (players[otherPlayer].length - overlappingLength)) {
yield return null;
}
// associate the render area with the active player and start the playback
renderArea.texture = players[activePlayer].texture;
players[activePlayer].Play();
// let the previous playback end and stop the player
yield return new WaitForSeconds(overlappingLength);
players[otherPlayer].Stop();
// wait until the current playback is 2/3 of the way and then let the other player start
while (players[activePlayer].isPlaying) {
if (players[activePlayer].time >= twoThirdOfTheWay) {
players[otherPlayer].time = 0f;
break;
}
yield return null;
}
StartCoroutine(Play(otherPlayer));
}
#endregion
}
I hope it helps also someone else