闲鱼技术01 发表于 2022-4-27 16:54

Unity中,使用Recorder录制视频时,选择不同的TargetFPS,确保VideoPlayer能正常播放视频

问题

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{        private VideoPlayer _videoPlayer;        private AudioSource _audioSource;    private float _beginTime;    private float CurTime{ get { return Time.time - _beginTime; } }    private long _beginFrameIndex;    private IEnumerator _playVideoCoroutine;        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{        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);    }}
页: [1]
查看完整版本: Unity中,使用Recorder录制视频时,选择不同的TargetFPS,确保VideoPlayer能正常播放视频