美文网首页
Unity中,使用Recorder录制视频时,选择不同的Targ

Unity中,使用Recorder录制视频时,选择不同的Targ

作者: 全新的饭 | 来源:发表于2022-04-26 11:24 被阅读0次

问题

Unity中,使用Recorder录制视频时,因为选择的帧率不同,导致录制的视频中的VideoPlayer播放视频不正常。

不正常体现在:Recorder的TargetFPS和VideoPlayer播放的视频的帧率不一致,导致VideoPlayer播放的视频被减慢和加快(为了去和TargetFPS匹配),而且原视频对应的音频的播放速度也和原视频不匹配。

需求

不同的帧率下VideoPlayer的视频的画面和声音播放速度均为原速。
允许从中途指定帧数开始播放。(使用本功能时,最好将视频先播放一次,至少播放至“指定帧数”,确保设置videoPlayer的frame能起效)

实现

做法:使用VideoPlayer的StepForward去播放视频,因为这种播放方式下视频不播声音,因此需将视频转为一个mp3文件,使用AudioSource去专门播放声音。


image.png

在线工具:Mp4转Mp3

MyVideoPlayer.cs

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Video;

public class MyVideoPlayer : MonoBehaviour
{
    [SerializeField]
    private VideoPlayer _videoPlayer;
    [SerializeField]
    private AudioSource _audioSource;

    private float _beginTime;
    private float CurTime{ get { return Time.time - _beginTime; } }
    private long _beginFrameIndex;
    private IEnumerator _playVideoCoroutine;

    [SerializeField]
    private bool _shouldAutoStart = true;

    private void Start()
    {
        Init();
    }

    private void OnDestroy()
    {
        Destroy();
    }

    public void Init()
    {
        _videoPlayer.playOnAwake = false;
        _videoPlayer.isLooping = false;
        _videoPlayer.Stop();
        _audioSource.playOnAwake = false;
        _audioSource.loop = false;
        _audioSource.Stop();

        if (_shouldAutoStart)
        {
            Play();
        }
    }
    public void Destroy()
    { }

    // 从触发后,等待多久后开始播放,从第几帧开始播放,是否要清除视频缓存
    public void Play(float delayTime = 0, long frameIndex = 0, bool shouldClearTexture = true)
    {
        Stop();

        // 为了确保跳到指定帧直接开始播放时,能跳到指定帧(若指定帧未准备好,_videoPlayer的frame设置不会成功,会是-1)
        // 实际用时,为确保设置frame有效,较好的做法:让videoPlayer的视频先完整播放一次(至少播到接下来要直接跳到的位置处),再使用从指定帧开始播放的功能
        _videoPlayer.Prepare();

        _beginFrameIndex = frameIndex;

        if (shouldClearTexture)
        {
            ReleaseTexture();
        }

        DelayCall(delayTime, () =>
        {
            _beginTime = Time.time;
            PlayVideo();
            PlayAudio();
        });


        void ReleaseTexture()
        {
            _videoPlayer.targetTexture.Release();
            _videoPlayer.targetTexture.MarkRestoreExpected();
        }

        void PlayVideo()
        {
            _videoPlayer.frame = _beginFrameIndex;
            _videoPlayer.Play();
            _playVideoCoroutine = PlayVideoCoroutine();
            StartCoroutine(_playVideoCoroutine);
        }
        void PlayAudio()
        {
            _audioSource.time = _beginFrameIndex / _videoPlayer.frameRate;
            _audioSource.Play();
        }

        IEnumerator PlayVideoCoroutine()
        {
            while (true)
            {
                _videoPlayer.Pause();
                if ((_videoPlayer.frame - _beginFrameIndex) / _videoPlayer.frameRate < CurTime)
                {
                    _videoPlayer.StepForward();
                }
                yield return null;

                // 循环播放
                if (_videoPlayer.frame ==(long)_videoPlayer.frameCount - 1)
                {
                    Play(0, 0, false);
                }
            }
        }
    }

    public void Stop()
    {
        Stop(0f);
    }

    public void Stop(float delayTime)
    {
        DelayCall(delayTime, () =>
        {
            Stop(out long frame, out float time);
        });
    }
    /// <summary>
    /// 停止时,视频已播放了多少帧,多少秒(从视频开头计)
    /// </summary>
    /// <returns></returns>
    public void Stop(out long frame, out float time)
    {
        StopVideo();
        StopAudio();

        frame = _videoPlayer.frame;
        time = frame / _videoPlayer.frameRate;

#if UNITY_EDITOR
        Debug.Log("停止播放,此时的frame:" + frame + "   time:" + time);
#endif

        void StopVideo()
        {
            if (_playVideoCoroutine != null)
            {
                StopCoroutine(_playVideoCoroutine);
                _videoPlayer.Pause();
                _playVideoCoroutine = null;
            }
        }
        void StopAudio()
        {
            _audioSource.Pause();
        }
    }

    private void DelayCall(float delayTime, Action action)
    {
        StartCoroutine(DelayCallCoroutine());

        IEnumerator DelayCallCoroutine()
        {
            yield return new WaitForSeconds(delayTime);
            action?.Invoke();
        }
    }
}

测试

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestMyVideoPlayer : MonoBehaviour
{
    [SerializeField]
    private MyVideoPlayer _myVideoPlayer;
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Test1();
        }

        if (Input.GetKeyDown(KeyCode.A))
        {
            Test2();
        }
    }

    private void Test1()
    {
        _myVideoPlayer.Play(3);
        _myVideoPlayer.Stop(15);
    }
    private void Test2()
    {
        var frameIndex = 360;
        _myVideoPlayer.Play(1, frameIndex, false);
    }

}

相关文章

网友评论

      本文标题:Unity中,使用Recorder录制视频时,选择不同的Targ

      本文链接:https://www.haomeiwen.com/subject/jimcyrtx.html