从VideoPlayer获取当前帧纹理
在这篇文章中说明使用新的Unity VideoPlayer和VideoClip API来播放video ,人们可以“根据需要检索每个帧的纹理”
请将当前帧作为Texture2D的正确方法是什么?
编辑:
答案之后,我做了这个,但它不起作用:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Video; public class AverageColorFromTexture : MonoBehaviour { public VideoClip videoToPlay; public Light lSource; private Color targetColor; private VideoPlayer videoPlayer; private VideoSource videoSource; private Renderer rend; private Texture tex; private AudioSource audioSource; void Start() { Application.runInBackground = true; StartCoroutine(playVideo()); } IEnumerator playVideo() { rend = GetComponent(); videoPlayer = gameObject.AddComponent(); audioSource = gameObject.AddComponent(); //Disable Play on Awake for both Video and Audio videoPlayer.playOnAwake = false; audioSource.playOnAwake = false; videoPlayer.source = VideoSource.VideoClip; videoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource; videoPlayer.EnableAudioTrack(0, true); videoPlayer.SetTargetAudioSource(0, audioSource); //Set video To Play then prepare Audio to prevent Buffering videoPlayer.clip = videoToPlay; videoPlayer.Prepare(); //Wait until video is prepared while (!videoPlayer.isPrepared) { Debug.Log("Preparing Video"); yield return null; } Debug.Log("Done Preparing Video"); //Assign the Texture from Video to Material texture tex = videoPlayer.texture; rend.material.mainTexture = tex; //Enable new frame Event videoPlayer.sendFrameReadyEvents = true; //Subscribe to the new frame Event videoPlayer.frameReady += OnNewFrame; //Play Video videoPlayer.Play(); //Play Sound audioSource.Play(); Debug.Log("Playing Video"); while (videoPlayer.isPlaying) { Debug.LogWarning("Video Time: " + Mathf.FloorToInt((float)videoPlayer.time)); yield return null; } Debug.Log("Done Playing Video"); } void OnNewFrame(VideoPlayer source, long frameIdx) { Texture2D videoFrame = (Texture2D)source.texture; targetColor = CalculateAverageColorFromTexture(videoFrame); lSource.color = targetColor ; } Color32 CalculateAverageColorFromTexture(Texture2D tex) { Color32[] texColors = tex.GetPixels32(); int total = texColors.Length; float r = 0; float g = 0; float b = 0; for(int i = 0; i < total; i++) { r += texColors[i].r; g += texColors[i].g; b += texColors[i].b; } return new Color32((byte)(r / total) , (byte)(g / total) , (byte)(b / total) , 0); } }
您可以通过三个步骤正确执行此操作:
1.通过将VideoPlayer.sendFrameReadyEvents
设置为true
来启用新的帧事件。
2.订阅VideoPlayerframeReady
事件
3.当新帧可用时,将调用您分配给VideoPlayerframeReady
事件的function。 只需从VideoPlayer
访问video帧,它将通过将VideoPlayer.texture
为Texture2D
传递给参数。
而已。
在代码中:
在video.Play()
之前添加以下内容:
//Enable new frame Event videoPlayer.sendFrameReadyEvents = true; //Subscribe to the new frame Event videoPlayer.frameReady += OnNewFrame;
这是您的OnNewFrame
函数签名。
void OnNewFrame(VideoPlayer source, long frameIdx) { Texture2D videoFrame = (Texture2D)source.texture; //Do anything with the videoFrame Texture. }
值得注意的是,启用该事件的成本很高。 在执行此操作之前,请确保您需要每个框架。
编辑:
Texture2D videoFrame = (Texture2D)source.texture;
和Texture2D videoFrame = source.texture as Texture2D;
失败。
我把Debug.Log(source.texture);
在OnNewFrame
函数内部得到:
TempBuffer 294 320×240(UnityEngine.RenderTexture)
因此,看起来Video.texture
属性返回RenderTexture
类型而不是像它应该的Texture
类型。
我们必须将RenderTexture
转换为Texture2D
。
void Start() { videoFrame = new Texture2D(2, 2);] ... } //Initialize in the Start function Texture2D videoFrame; void OnNewFrame(VideoPlayer source, long frameIdx) { RenderTexture renderTexture = source.texture as RenderTexture; if (videoFrame.width != renderTexture.width || videoFrame.height != renderTexture.height) { videoFrame.Resize(renderTexture.width, renderTexture.height); } RenderTexture.active = renderTexture; videoFrame.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); videoFrame.Apply(); RenderTexture.active = null; targetColor = CalculateAverageColorFromTexture(videoFrame); lSource.color = targetColor; }
您问题中的完整代码:
public class AverageColorFromTexture : MonoBehaviour { public VideoClip videoToPlay; public Light lSource; private Color targetColor; private VideoPlayer videoPlayer; private VideoSource videoSource; private Renderer rend; private Texture tex; private AudioSource audioSource; void Start() { videoFrame = new Texture2D(2, 2); Application.runInBackground = true; StartCoroutine(playVideo()); } IEnumerator playVideo() { rend = GetComponent(); videoPlayer = gameObject.AddComponent(); audioSource = gameObject.AddComponent(); //Disable Play on Awake for both Video and Audio videoPlayer.playOnAwake = false; audioSource.playOnAwake = false; videoPlayer.source = VideoSource.VideoClip; videoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource; videoPlayer.EnableAudioTrack(0, true); videoPlayer.SetTargetAudioSource(0, audioSource); //Set video To Play then prepare Audio to prevent Buffering videoPlayer.clip = videoToPlay; videoPlayer.Prepare(); //Wait until video is prepared while (!videoPlayer.isPrepared) { Debug.Log("Preparing Video"); yield return null; } Debug.Log("Done Preparing Video"); //Assign the Texture from Video to Material texture tex = videoPlayer.texture; rend.material.mainTexture = tex; //Enable new frame Event videoPlayer.sendFrameReadyEvents = true; //Subscribe to the new frame Event videoPlayer.frameReady += OnNewFrame; //Play Video videoPlayer.Play(); //Play Sound audioSource.Play(); Debug.Log("Playing Video"); while (videoPlayer.isPlaying) { Debug.LogWarning("Video Time: " + Mathf.FloorToInt((float)videoPlayer.time)); yield return null; } Debug.Log("Done Playing Video"); } //Initialize in the Start function Texture2D videoFrame; void OnNewFrame(VideoPlayer source, long frameIdx) { RenderTexture renderTexture = source.texture as RenderTexture; if (videoFrame.width != renderTexture.width || videoFrame.height != renderTexture.height) { videoFrame.Resize(renderTexture.width, renderTexture.height); } RenderTexture.active = renderTexture; videoFrame.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); videoFrame.Apply(); RenderTexture.active = null; targetColor = CalculateAverageColorFromTexture(videoFrame); lSource.color = targetColor; } Color32 CalculateAverageColorFromTexture(Texture2D tex) { Color32[] texColors = tex.GetPixels32(); int total = texColors.Length; float r = 0; float g = 0; float b = 0; for (int i = 0; i < total; i++) { r += texColors[i].r; g += texColors[i].g; b += texColors[i].b; } return new Color32((byte)(r / total), (byte)(g / total), (byte)(b / total), 0); } }