VideoPlayerController.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. #if UNITY_5_6_OR_NEWER
  2. #if UNITY_2018_3_OR_NEWER // The "length" property is only supported from 2018.3
  3. using UnityEngine;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using UnityEngine.SceneManagement;
  7. using UnityEngine.Video;
  8. //-----------------------------------------------------------------------------
  9. // Copyright 2012-2022 RenderHeads Ltd. All rights reserved.
  10. //-----------------------------------------------------------------------------
  11. namespace RenderHeads.Media.AVProMovieCapture
  12. {
  13. /// <summary>
  14. /// Controls VideoPlayer updates time during offline captures
  15. /// </summary>
  16. [AddComponentMenu("AVPro Movie Capture/Utils/VideoPlayer Controller", 300)]
  17. public class VideoPlayerController : MonoBehaviour
  18. {
  19. public enum ScanFrequencyMode
  20. {
  21. SceneLoad,
  22. Frame,
  23. }
  24. [SerializeField] ScanFrequencyMode _scanFrequency = ScanFrequencyMode.SceneLoad;
  25. public ScanFrequencyMode ScanFrequency
  26. {
  27. get { return _scanFrequency; }
  28. set { _scanFrequency = value; ResetSceneLoading(); }
  29. }
  30. internal class VideoPlayerInstance
  31. {
  32. private VideoPlayer _videoPlayer = null;
  33. private bool _isCapturing = false;
  34. private bool _isControlling = false;
  35. private bool _isSeekPending = false;
  36. private double _videoTime = 0.0;
  37. private float _postSeekTimer = 0f;
  38. internal VideoPlayerInstance(VideoPlayer videoPlayer)
  39. {
  40. _videoPlayer = videoPlayer;
  41. }
  42. internal bool Is(VideoPlayer videoPlayer)
  43. {
  44. return (_videoPlayer == videoPlayer);
  45. }
  46. internal void StartCapture()
  47. {
  48. // First capture to touch the playable directors
  49. if (!_isCapturing)
  50. {
  51. // Null check in case director no longer exists
  52. if (_videoPlayer != null)
  53. {
  54. TryTakeControl();
  55. }
  56. _isCapturing = true;
  57. }
  58. }
  59. internal bool IsSeekPending()
  60. {
  61. float d = (Time.realtimeSinceStartup - _postSeekTimer);
  62. return (_isSeekPending || (d < 0.2f));
  63. }
  64. internal void TryTakeControl()
  65. {
  66. if (!_isControlling)
  67. {
  68. if (_videoPlayer.isPrepared)
  69. {
  70. if (_videoPlayer.isPlaying && _videoPlayer.frame >= 0)
  71. {
  72. _videoPlayer.seekCompleted += VideoSeekCompleted;
  73. _videoPlayer.frameReady += VideoFrameReady;
  74. _videoPlayer.sendFrameReadyEvents = true;
  75. _videoPlayer.Pause();
  76. _isControlling = true;
  77. _videoTime = _videoPlayer.time;
  78. //Debug.Log("pause");
  79. //Debug.Log(_videoPlayer.canSetSkipOnDrop + " " + _videoPlayer.skipOnDrop);
  80. _videoPlayer.skipOnDrop = true;
  81. _postSeekTimer = Time.realtimeSinceStartup - 2f;
  82. Debug.Log("start " + _videoPlayer.frame + " " + _videoTime + " " + (_videoPlayer.time * 1000));
  83. }
  84. }
  85. }
  86. }
  87. void VideoFrameReady(VideoPlayer source, long frameIdx)
  88. {
  89. Debug.Log("frame " + frameIdx);
  90. _postSeekTimer = Time.realtimeSinceStartup - 2f;
  91. _isSeekPending = false;
  92. }
  93. void VideoSeekCompleted(VideoPlayer source)
  94. {
  95. Debug.Log("seek complete " + source.frame + " " + source.time * 1000);
  96. _isSeekPending = false;
  97. _postSeekTimer = Time.realtimeSinceStartup;
  98. }
  99. internal void ReleaseControl()
  100. {
  101. _isControlling = false;
  102. _isSeekPending = false;
  103. _videoPlayer.seekCompleted -= VideoSeekCompleted;
  104. }
  105. internal bool Update(float deltaTime)
  106. {
  107. bool updated = false;
  108. if (_isCapturing)
  109. {
  110. if (_videoPlayer != null)
  111. {
  112. if (!_isControlling)
  113. {
  114. TryTakeControl();
  115. }
  116. if (_isControlling)
  117. {
  118. if (!_videoPlayer.isPrepared)
  119. {
  120. ReleaseControl();
  121. }
  122. if (_isControlling && !_isSeekPending)
  123. {
  124. float delta = Time.realtimeSinceStartup - _postSeekTimer;
  125. //Debug.Log("post " + _postSeekTimer);
  126. if (delta > 0.2f)
  127. {
  128. _videoTime += deltaTime;
  129. if (_videoPlayer.isLooping && _videoTime >= _videoPlayer.length)
  130. {
  131. _videoTime %= _videoPlayer.length;
  132. }
  133. _isSeekPending = true;
  134. Debug.Log("seek begin " + _videoPlayer.frame + " " + _videoTime + " " + (_videoPlayer.time * 1000) + " " + (deltaTime * 1000));
  135. _videoPlayer.time = _videoTime;
  136. //_videoPlayer.frame = 18;
  137. //Debug.Log("seek begin2 " + _videoPlayer.frame + " " + (_videoPlayer.time * 1000));
  138. updated = true;
  139. //_isSeekPending = false;
  140. }
  141. }
  142. }
  143. }
  144. }
  145. return updated;
  146. }
  147. internal void StopCapture()
  148. {
  149. if (_isCapturing)
  150. {
  151. // TODO: what happens to the VideoPlayer when the scene is unloaded?
  152. if (_videoPlayer != null)
  153. {
  154. // We were controlling?
  155. if (_isControlling)
  156. {
  157. // Restore to original state
  158. _videoPlayer.Play();
  159. }
  160. }
  161. ReleaseControl();
  162. _isCapturing = false;
  163. }
  164. }
  165. }
  166. private List<VideoPlayerInstance> _instances = new List<VideoPlayerInstance>(8);
  167. void Awake()
  168. {
  169. ResetSceneLoading();
  170. }
  171. void Start()
  172. {
  173. //StartCapture();
  174. }
  175. void OnValidate()
  176. {
  177. ResetSceneLoading();
  178. }
  179. void Update()
  180. {
  181. //UpdateFrame();
  182. }
  183. internal void UpdateFrame()
  184. {
  185. if (!this.isActiveAndEnabled)
  186. return;
  187. if (_scanFrequency == ScanFrequencyMode.Frame)
  188. {
  189. ScanForVideoPlayers();
  190. }
  191. bool anyUpdates = false;
  192. foreach (VideoPlayerInstance instance in _instances)
  193. {
  194. //if (Input.GetKeyDown(KeyCode.P))
  195. {
  196. if (instance.Update(Time.deltaTime))
  197. {
  198. anyUpdates = true;
  199. }
  200. }
  201. }
  202. if (anyUpdates)
  203. {
  204. //StartCoroutine(WaitforSeekCompletes());
  205. //WaitforSeekCompletes2();
  206. //System.Threading.Thread.Sleep(500);
  207. }
  208. }
  209. public bool CanContinue()
  210. {
  211. bool result = true;
  212. foreach (VideoPlayerInstance instance in _instances)
  213. {
  214. if (instance.IsSeekPending())
  215. {
  216. result = false;
  217. break;
  218. }
  219. }
  220. return result;
  221. }
  222. internal IEnumerator WaitforSeekCompletes()
  223. {
  224. yield return new WaitUntil(() =>
  225. {
  226. bool isSeekPending = false;
  227. foreach (VideoPlayerInstance instance in _instances)
  228. {
  229. if (instance.IsSeekPending())
  230. {
  231. isSeekPending = true;
  232. break;
  233. }
  234. }
  235. return !isSeekPending;
  236. });
  237. System.Threading.Thread.Sleep(100);
  238. yield return new WaitForEndOfFrame();
  239. }
  240. internal void WaitforSeekCompletes2()
  241. {
  242. /*bool isSeekPending = false;
  243. foreach (VideoPlayerInstance instance in _instances)
  244. {
  245. if (instance.IsSeekPending())
  246. {
  247. isSeekPending = true;
  248. break;
  249. }
  250. }*/
  251. //Debug.Log("any pending: " + isSeekPending);
  252. }
  253. internal void StartCapture()
  254. {
  255. Debug.Log("startcap");
  256. ScanForVideoPlayers();
  257. foreach (VideoPlayerInstance instance in _instances)
  258. {
  259. instance.StartCapture();
  260. }
  261. }
  262. internal void StopCapture()
  263. {
  264. foreach (VideoPlayerInstance instance in _instances)
  265. {
  266. instance.StopCapture();
  267. }
  268. }
  269. public void ScanForVideoPlayers()
  270. {
  271. Debug.Log("scan");
  272. // Remove any VideoPlayer instances with deleted (null) VideoPlayers
  273. for (int i = 0; i < _instances.Count; i++)
  274. {
  275. VideoPlayerInstance instance = _instances[i];
  276. if (instance.Is(null))
  277. {
  278. _instances.RemoveAt(i); i--;
  279. }
  280. }
  281. // Find all inactive and active VideoPlayers
  282. VideoPlayer[] videoPlayers = Resources.FindObjectsOfTypeAll<VideoPlayer>();
  283. // Create a unique instance for each director
  284. foreach (VideoPlayer videoPlayer in videoPlayers)
  285. {
  286. // Check we don't already have this VideoPlayer
  287. bool hasVideoPlayer = false;
  288. foreach (VideoPlayerInstance instance in _instances)
  289. {
  290. if (instance.Is(videoPlayer))
  291. {
  292. hasVideoPlayer = true;
  293. break;
  294. }
  295. }
  296. // Add to the list
  297. if (!hasVideoPlayer)
  298. {
  299. _instances.Add(new VideoPlayerInstance(videoPlayer));
  300. Debug.Log("add");
  301. }
  302. }
  303. }
  304. void OnDestroy()
  305. {
  306. SceneManager.sceneLoaded -= OnSceneLoaded;
  307. StopCapture();
  308. }
  309. void ResetSceneLoading()
  310. {
  311. SceneManager.sceneLoaded -= OnSceneLoaded;
  312. if (_scanFrequency == ScanFrequencyMode.SceneLoad)
  313. {
  314. SceneManager.sceneLoaded += OnSceneLoaded;
  315. }
  316. }
  317. void OnSceneLoaded(Scene scene, LoadSceneMode mode)
  318. {
  319. if (_scanFrequency == ScanFrequencyMode.SceneLoad)
  320. {
  321. ScanForVideoPlayers();
  322. }
  323. }
  324. }
  325. }
  326. #endif
  327. #endif