【问题标题】:Get current frame Texture from VideoPlayer从 VideoPlayer 获取当前帧纹理
【发布时间】:2021-08-13 21:06:29
【问题描述】:

在这篇帖子Using new Unity VideoPlayer and VideoClip API to play 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<Renderer>();

        videoPlayer = gameObject.AddComponent<VideoPlayer>();
        audioSource = gameObject.AddComponent<AudioSource>();

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

【问题讨论】:

    标签: c# unity3d unity5


    【解决方案1】:

    您可以通过三个步骤正确地做到这一点:

    1. 通过将VideoPlayer.sendFrameReadyEvents 设置为true 来启用新帧事件。

    2. 订阅VideoPlayer.frameReady 活动

    3. 当新框架可用时,将调用您分配给VideoPlayer.frameReady 事件的函数。只需从VideoPlayer 访问视频帧,它将通过将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 320x240 (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<Renderer>();
    
            videoPlayer = gameObject.AddComponent<VideoPlayer>();
            audioSource = gameObject.AddComponent<AudioSource>();
    
            //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);
        }
    }
    

    【讨论】:

    • 非常感谢您的回复并花时间编写您的答案! ...但是在投射 VideoPlayer 纹理时出现“无法将 UnityEngine.Video.VideoSource 类型转换为 UnityEngine.Texture2D”错误!
    • 我的回答两次说您将VideoPlayer.texture not VideoPlayer 转换为Texture2D。请直接从我的回答中复制功能。
    • 感谢您的回复!我已经完全按照你说的做了……但在 OnNewFrame 函数内的 Texture2D videoFrame = (Texture2D)source.texture; 上仍然出现转换错误!你能帮我指出我做错了什么吗?谢谢
    • 检查我的编辑答案。我没有尝试我的解决方案。我刚刚做了并发现了问题。
    • 感谢@Programmer 它解决了我的问题stackoverflow.com/questions/47974403/…
    猜你喜欢
    • 1970-01-01
    • 2018-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多