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]