-
-
Save paulhayes/f70f8ec2790cd0ff1e6857c580c1a7ce to your computer and use it in GitHub Desktop.
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.Video; | |
public static class VideoPlayerExtentions | |
{ | |
public static void ShowFirstFrame(this VideoPlayer player, System.Action onCompleteCallback) | |
{ | |
VideoPlayer.FrameReadyEventHandler frameReadyHandler = null; | |
bool oldSendFrameReadyEvents = player.sendFrameReadyEvents; | |
frameReadyHandler = (source,index)=>{ | |
player.sendFrameReadyEvents = oldSendFrameReadyEvents; | |
player.frameReady -= frameReadyHandler; | |
onCompleteCallback(); | |
}; | |
player.frameReady += frameReadyHandler; | |
player.sendFrameReadyEvents = true; | |
player.Prepare(); | |
player.StepForward(); | |
} | |
public static IEnumerator ShowFirstFrame(this VideoPlayer player) | |
{ | |
VideoPlayer.FrameReadyEventHandler frameReadyHandler = null; | |
bool frameReady = false; | |
bool oldSendFrameReadyEvents = player.sendFrameReadyEvents; | |
frameReadyHandler = (source,index)=>{ | |
frameReady = true; | |
player.frameReady -= frameReadyHandler; | |
player.sendFrameReadyEvents = oldSendFrameReadyEvents; | |
}; | |
player.frameReady += frameReadyHandler; | |
player.sendFrameReadyEvents = true; | |
player.Prepare(); | |
player.StepForward(); | |
while(!frameReady){ | |
yield return null; | |
} | |
} | |
} |
This did not work for me. When I ran the Coroutine and did player.Play() after the Coroutine, it did the same as if I hadn't run the Coroutine at all.
I'm not sure I understand Rafael. This is intended to be used without calling player.Play(), once you call Play it should run normally.
This is also quite an old script, and maybe redundant. Which version of Unity are you using?
Maybe I did not understand the description of this extension.
My use case is that I have a main menu whose background is a video. The issue is that during the first frames when starting the game the video is still loading, thus displaying an empty RenderTexture. I thought you would run this script like so
`
StartCoroutine(player.ShowFirstFrame())
/// wait for the coroutine to finish, here it will display the first frame while "preparing" the video
player.Play();
`
I also tried without the player.Play() line, but it didn't work either. Using the callback version didn't work either.
I've just tested with the following script, and it worked as expected in 2019.3
public class FirstFrameTest : MonoBehaviour
{
[SerializeField] VideoPlayer videoPlayer;
void Start()
{
StartCoroutine( videoPlayer.ShowFirstFrame() );
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(KeyCode.P)){
videoPlayer.Play();
}
if(Input.GetKeyDown(KeyCode.S)){
videoPlayer.Stop();
StartCoroutine( videoPlayer.ShowFirstFrame() );
}
}
}
Judging by your response, I have probably misinterpreted the use case of this script. If so, what is it for?
My Unity version is 2019.4.12f1
I see your problem. Unity can't access the first video frame until the video has prepared! So this doesn't help your use case. Can I suggest using the following script to manually capture the first frame by saving the render texture as a png.
using UnityEngine;
using UnityEditor;
public class SaveRenderTextureToFile {
[MenuItem("Assets/Save RenderTexture to file")]
public static void SaveRTToFile()
{
RenderTexture rt = Selection.activeObject as RenderTexture;
Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
RenderTexture tmp = RenderTexture.GetTemporary(tex.width,tex.height,0,RenderTextureFormat.ARGB32);
Graphics.Blit(rt,tmp);
RenderTexture.active = tmp;
tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
RenderTexture.active = null;
byte[] bytes;
bytes = tex.EncodeToPNG();
string path = AssetDatabase.GetAssetPath(rt) + ".png";
System.IO.File.WriteAllBytes(path, bytes);
AssetDatabase.ImportAsset(path);
Debug.Log("Saved to " + path);
}
[MenuItem("Assets/Save RenderTexture to file", true)]
public static bool SaveRTToFileValidation()
{
return Selection.activeObject is RenderTexture;
}
}
you can then either use Graphics.Blit to copy the png into the rendertexture or you can swap the texture reference in the material, then swap back to the render texture once the video is ready to play.
Judging by your response, I have probably misinterpreted the use case of this script. If so, what is it for?
My Unity version is 2019.4.12f1
This script is for displaying the first frame of video until play is initiated by user interaction.
Ah okay I understand the logic now behind how it works.
I ended up coding something very similar to your suggestions with saving the initial frame, although I'm gonna experiment with AssetDatabase.ImportAsset(path) now, I hadn't seen that earlier.
This did not work for me. When I ran the Coroutine and did player.Play() after the Coroutine, it did the same as if I hadn't run the Coroutine at all.