找回密码
 立即注册
查看: 283|回复: 0

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

[复制链接]
发表于 2022-4-27 16:54 | 显示全部楼层 |阅读模式
问题

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开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 13:33 , Processed in 0.090317 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表