#if UNITY_5_6_OR_NEWER #if UNITY_2018_3_OR_NEWER // The "length" property is only supported from 2018.3 using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.SceneManagement; using UnityEngine.Video; //----------------------------------------------------------------------------- // Copyright 2012-2022 RenderHeads Ltd. All rights reserved. //----------------------------------------------------------------------------- namespace RenderHeads.Media.AVProMovieCapture { /// /// Controls VideoPlayer updates time during offline captures /// [AddComponentMenu("AVPro Movie Capture/Utils/VideoPlayer Controller", 300)] public class VideoPlayerController : MonoBehaviour { public enum ScanFrequencyMode { SceneLoad, Frame, } [SerializeField] ScanFrequencyMode _scanFrequency = ScanFrequencyMode.SceneLoad; public ScanFrequencyMode ScanFrequency { get { return _scanFrequency; } set { _scanFrequency = value; ResetSceneLoading(); } } internal class VideoPlayerInstance { private VideoPlayer _videoPlayer = null; private bool _isCapturing = false; private bool _isControlling = false; private bool _isSeekPending = false; private double _videoTime = 0.0; private float _postSeekTimer = 0f; internal VideoPlayerInstance(VideoPlayer videoPlayer) { _videoPlayer = videoPlayer; } internal bool Is(VideoPlayer videoPlayer) { return (_videoPlayer == videoPlayer); } internal void StartCapture() { // First capture to touch the playable directors if (!_isCapturing) { // Null check in case director no longer exists if (_videoPlayer != null) { TryTakeControl(); } _isCapturing = true; } } internal bool IsSeekPending() { float d = (Time.realtimeSinceStartup - _postSeekTimer); return (_isSeekPending || (d < 0.2f)); } internal void TryTakeControl() { if (!_isControlling) { if (_videoPlayer.isPrepared) { if (_videoPlayer.isPlaying && _videoPlayer.frame >= 0) { _videoPlayer.seekCompleted += VideoSeekCompleted; _videoPlayer.frameReady += VideoFrameReady; _videoPlayer.sendFrameReadyEvents = true; _videoPlayer.Pause(); _isControlling = true; _videoTime = _videoPlayer.time; //Debug.Log("pause"); //Debug.Log(_videoPlayer.canSetSkipOnDrop + " " + _videoPlayer.skipOnDrop); _videoPlayer.skipOnDrop = true; _postSeekTimer = Time.realtimeSinceStartup - 2f; Debug.Log("start " + _videoPlayer.frame + " " + _videoTime + " " + (_videoPlayer.time * 1000)); } } } } void VideoFrameReady(VideoPlayer source, long frameIdx) { Debug.Log("frame " + frameIdx); _postSeekTimer = Time.realtimeSinceStartup - 2f; _isSeekPending = false; } void VideoSeekCompleted(VideoPlayer source) { Debug.Log("seek complete " + source.frame + " " + source.time * 1000); _isSeekPending = false; _postSeekTimer = Time.realtimeSinceStartup; } internal void ReleaseControl() { _isControlling = false; _isSeekPending = false; _videoPlayer.seekCompleted -= VideoSeekCompleted; } internal bool Update(float deltaTime) { bool updated = false; if (_isCapturing) { if (_videoPlayer != null) { if (!_isControlling) { TryTakeControl(); } if (_isControlling) { if (!_videoPlayer.isPrepared) { ReleaseControl(); } if (_isControlling && !_isSeekPending) { float delta = Time.realtimeSinceStartup - _postSeekTimer; //Debug.Log("post " + _postSeekTimer); if (delta > 0.2f) { _videoTime += deltaTime; if (_videoPlayer.isLooping && _videoTime >= _videoPlayer.length) { _videoTime %= _videoPlayer.length; } _isSeekPending = true; Debug.Log("seek begin " + _videoPlayer.frame + " " + _videoTime + " " + (_videoPlayer.time * 1000) + " " + (deltaTime * 1000)); _videoPlayer.time = _videoTime; //_videoPlayer.frame = 18; //Debug.Log("seek begin2 " + _videoPlayer.frame + " " + (_videoPlayer.time * 1000)); updated = true; //_isSeekPending = false; } } } } } return updated; } internal void StopCapture() { if (_isCapturing) { // TODO: what happens to the VideoPlayer when the scene is unloaded? if (_videoPlayer != null) { // We were controlling? if (_isControlling) { // Restore to original state _videoPlayer.Play(); } } ReleaseControl(); _isCapturing = false; } } } private List _instances = new List(8); void Awake() { ResetSceneLoading(); } void Start() { //StartCapture(); } void OnValidate() { ResetSceneLoading(); } void Update() { //UpdateFrame(); } internal void UpdateFrame() { if (!this.isActiveAndEnabled) return; if (_scanFrequency == ScanFrequencyMode.Frame) { ScanForVideoPlayers(); } bool anyUpdates = false; foreach (VideoPlayerInstance instance in _instances) { //if (Input.GetKeyDown(KeyCode.P)) { if (instance.Update(Time.deltaTime)) { anyUpdates = true; } } } if (anyUpdates) { //StartCoroutine(WaitforSeekCompletes()); //WaitforSeekCompletes2(); //System.Threading.Thread.Sleep(500); } } public bool CanContinue() { bool result = true; foreach (VideoPlayerInstance instance in _instances) { if (instance.IsSeekPending()) { result = false; break; } } return result; } internal IEnumerator WaitforSeekCompletes() { yield return new WaitUntil(() => { bool isSeekPending = false; foreach (VideoPlayerInstance instance in _instances) { if (instance.IsSeekPending()) { isSeekPending = true; break; } } return !isSeekPending; }); System.Threading.Thread.Sleep(100); yield return new WaitForEndOfFrame(); } internal void WaitforSeekCompletes2() { /*bool isSeekPending = false; foreach (VideoPlayerInstance instance in _instances) { if (instance.IsSeekPending()) { isSeekPending = true; break; } }*/ //Debug.Log("any pending: " + isSeekPending); } internal void StartCapture() { Debug.Log("startcap"); ScanForVideoPlayers(); foreach (VideoPlayerInstance instance in _instances) { instance.StartCapture(); } } internal void StopCapture() { foreach (VideoPlayerInstance instance in _instances) { instance.StopCapture(); } } public void ScanForVideoPlayers() { Debug.Log("scan"); // Remove any VideoPlayer instances with deleted (null) VideoPlayers for (int i = 0; i < _instances.Count; i++) { VideoPlayerInstance instance = _instances[i]; if (instance.Is(null)) { _instances.RemoveAt(i); i--; } } // Find all inactive and active VideoPlayers VideoPlayer[] videoPlayers = Resources.FindObjectsOfTypeAll(); // Create a unique instance for each director foreach (VideoPlayer videoPlayer in videoPlayers) { // Check we don't already have this VideoPlayer bool hasVideoPlayer = false; foreach (VideoPlayerInstance instance in _instances) { if (instance.Is(videoPlayer)) { hasVideoPlayer = true; break; } } // Add to the list if (!hasVideoPlayer) { _instances.Add(new VideoPlayerInstance(videoPlayer)); Debug.Log("add"); } } } void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; StopCapture(); } void ResetSceneLoading() { SceneManager.sceneLoaded -= OnSceneLoaded; if (_scanFrequency == ScanFrequencyMode.SceneLoad) { SceneManager.sceneLoaded += OnSceneLoaded; } } void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (_scanFrequency == ScanFrequencyMode.SceneLoad) { ScanForVideoPlayers(); } } } } #endif #endif