| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 | using UnityEngine;using System.Collections;//-----------------------------------------------------------------------------// Copyright 2015-2022 RenderHeads Ltd.  All rights reserved.//-----------------------------------------------------------------------------namespace RenderHeads.Media.AVProVideo{	public partial class MediaPlayer : MonoBehaviour	{#region Extract Frame		private bool ForceWaitForNewFrame(int lastFrameCount, float timeoutMs)		{			bool result = false;			// Wait for the frame to change, or timeout to happen (for the case that there is no new frame for this time)			System.DateTime startTime = System.DateTime.Now;			int iterationCount = 0;			while (Control != null && (System.DateTime.Now - startTime).TotalMilliseconds < (double)timeoutMs)			{				_playerInterface.Update();				// TODO: check if Seeking has completed!  Then we don't have to wait				// If frame has changed we can continue				// NOTE: this will never happen because GL.IssuePlugin.Event is never called in this loop				if (lastFrameCount != TextureProducer.GetTextureFrameCount())				{					result = true;					break;				}				iterationCount++;				// NOTE: we tried to add Sleep for 1ms but it was very slow, so switched to this time based method which burns more CPU but about double the speed				// NOTE: had to add the Sleep back in as after too many iterations (over 1000000) of GL.IssuePluginEvent Unity seems to lock up				// NOTE: seems that GL.IssuePluginEvent can't be called if we're stuck in a while loop and they just stack up				//System.Threading.Thread.Sleep(0);			}			_playerInterface.Render();			return result;		}				/// <summary>		/// Create or return (if cached) a camera that is inactive and renders nothing		/// This camera is used to call .Render() on which causes the render thread to run		/// This is useful for forcing GL.IssuePluginEvent() to run and is used for		/// wait for frames to render for ExtractFrame() and UpdateTimeScale()		/// </summary>		private static Camera GetDummyCamera()		{			if (_dummyCamera == null)			{				const string goName = "AVPro Video Dummy Camera";				GameObject go = GameObject.Find(goName);				if (go == null)				{					go = new GameObject(goName);					go.hideFlags = HideFlags.HideInHierarchy | HideFlags.DontSave;					go.SetActive(false);					Object.DontDestroyOnLoad(go);					_dummyCamera = go.AddComponent<Camera>();					_dummyCamera.hideFlags = HideFlags.HideInInspector | HideFlags.DontSave;					_dummyCamera.cullingMask = 0;					_dummyCamera.clearFlags = CameraClearFlags.Nothing;					_dummyCamera.enabled = false;				}				else				{					_dummyCamera = go.GetComponent<Camera>();				}			}			//Debug.Assert(_dummyCamera != null);			return _dummyCamera;		}		private IEnumerator ExtractFrameCoroutine(Texture2D target, ProcessExtractedFrame callback, double timeSeconds = -1.0, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)		{#if (!UNITY_EDITOR && UNITY_ANDROID) || UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN || UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || UNITY_IOS || UNITY_TVOS			Texture2D result = target;			Texture frame = null;			if (_controlInterface != null)			{				if (timeSeconds >= 0f)				{					Pause();					// If the right frame is already available (or close enough) just grab it					if (TextureProducer.GetTexture() != null && (System.Math.Abs(_controlInterface.GetCurrentTime() - timeSeconds) < (timeThresholdMs / 1000.0)))					{						frame = TextureProducer.GetTexture();					}					else					{						int preSeekFrameCount = _textureInterface.GetTextureFrameCount();						// Seek to the frame						if (accurateSeek)						{							_controlInterface.Seek(timeSeconds);						}						else						{							_controlInterface.SeekFast(timeSeconds);						}						// Wait for the new frame to arrive						if (!_controlInterface.WaitForNextFrame(GetDummyCamera(), preSeekFrameCount))						{							// If WaitForNextFrame fails (e.g. in android single threaded), we run the below code to asynchronously wait for the frame							int currFc = TextureProducer.GetTextureFrameCount();							int iterations = 0;							int maxIterations = 50;							//+1 as often there will be an extra frame produced after pause (so we need to wait for the second frame instead)							while((currFc + 1) >= TextureProducer.GetTextureFrameCount() && iterations++ < maxIterations)							{								yield return null;							}						}						frame = TextureProducer.GetTexture();					}				}				else				{					frame = TextureProducer.GetTexture();				}			}			if (frame != null)			{				result = Helper.GetReadableTexture(frame, TextureProducer.RequiresVerticalFlip(), Helper.GetOrientation(Info.GetTextureTransform()), target);			}#else			Texture2D result = ExtractFrame(target, timeSeconds, accurateSeek, timeoutMs, timeThresholdMs);#endif			callback(result);			yield return null;		}		public void ExtractFrameAsync(Texture2D target, ProcessExtractedFrame callback, double timeSeconds = -1.0, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)		{			StartCoroutine(ExtractFrameCoroutine(target, callback, timeSeconds, accurateSeek, timeoutMs, timeThresholdMs));		}		// "target" can be null or you can pass in an existing texture.		public Texture2D ExtractFrame(Texture2D target, double timeSeconds = -1.0, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)		{			Texture2D result = target;			// Extract frames returns the internal frame of the video player			Texture frame = ExtractFrame(timeSeconds, accurateSeek, timeoutMs, timeThresholdMs);			if (frame != null)			{				result = Helper.GetReadableTexture(frame, TextureProducer.RequiresVerticalFlip(), Helper.GetOrientation(Info.GetTextureTransform()), target);			}			return result;		}		private Texture ExtractFrame(double timeSeconds = -1.0, bool accurateSeek = true, int timeoutMs = 1000, int timeThresholdMs = 100)		{			Texture result = null;			if (_controlInterface != null)			{				if (timeSeconds >= 0f)				{					Pause();					// If the right frame is already available (or close enough) just grab it					if (TextureProducer.GetTexture() != null && (System.Math.Abs(_controlInterface.GetCurrentTime() - timeSeconds) < (timeThresholdMs / 1000.0)))					{						result = TextureProducer.GetTexture();					}					else					{						// Store frame count before seek						int frameCount = TextureProducer.GetTextureFrameCount();						// Seek to the frame						if (accurateSeek)						{							_controlInterface.Seek(timeSeconds);						}						else						{							_controlInterface.SeekFast(timeSeconds);						}						// Wait for frame to change						ForceWaitForNewFrame(frameCount, timeoutMs);						result = TextureProducer.GetTexture();					}				}				else				{					result = TextureProducer.GetTexture();				}			}			return result;		}#endregion // Extract Frame	}}
 |