CaptureFromTexture.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. using UnityEngine;
  2. using System.Collections;
  3. //-----------------------------------------------------------------------------
  4. // Copyright 2012-2022 RenderHeads Ltd. All rights reserved.
  5. //-----------------------------------------------------------------------------
  6. namespace RenderHeads.Media.AVProMovieCapture
  7. {
  8. /// <summary>
  9. /// Capture from a Texture object (including RenderTexture, WebcamTexture)
  10. /// </summary>
  11. [AddComponentMenu("AVPro Movie Capture/Capture From Texture", 3)]
  12. public class CaptureFromTexture : CaptureBase
  13. {
  14. [Tooltip("If enabled the method the encoder will only process frames each time UpdateSourceTexture() is called. This is useful if the texture is updating at a different rate compared to Unity, eg for webcam capture.")]
  15. [SerializeField] bool _manualUpdate = false;
  16. public bool IsManualUpdate
  17. {
  18. get { return _manualUpdate; }
  19. set { _manualUpdate = value; }
  20. }
  21. private Texture _sourceTexture;
  22. private RenderTexture _resolveTexture;
  23. private System.IntPtr _targetNativePointer = System.IntPtr.Zero;
  24. private bool _isSourceTextureChanged = false;
  25. public void SetSourceTexture(Texture texture)
  26. {
  27. _sourceTexture = texture;
  28. }
  29. private bool RequiresResolve(Texture texture)
  30. {
  31. #if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || (UNITY_IOS && !UNITY_EDITOR) || (UNITY_ANDROID)
  32. // Texture resolve wholly unnecessary on macOS and iOS
  33. return false;
  34. #else
  35. bool result = false;
  36. if (texture is RenderTexture)
  37. {
  38. RenderTexture rt = texture as RenderTexture;
  39. // Linear textures require resolving to sRGB
  40. result = !rt.sRGB;
  41. if (!result &&
  42. (rt.format != RenderTextureFormat.ARGB32) &&
  43. (rt.format != RenderTextureFormat.Default) &&
  44. (rt.format != RenderTextureFormat.BGRA32)
  45. )
  46. {
  47. // Exotic texture formats require resolving
  48. result = true;
  49. }
  50. }
  51. else
  52. {
  53. // Any other texture type needs to be resolve to RenderTexture
  54. result = true;
  55. }
  56. return result;
  57. #endif
  58. }
  59. public void UpdateSourceTexture()
  60. {
  61. _isSourceTextureChanged = true;
  62. }
  63. private bool ShouldCaptureFrame()
  64. {
  65. return (_capturing && !_paused && _sourceTexture != null);
  66. }
  67. private bool HasSourceTextureChanged()
  68. {
  69. return (!_manualUpdate || (_manualUpdate && _isSourceTextureChanged));
  70. }
  71. public override void UpdateFrame()
  72. {
  73. if (_useWaitForEndOfFrame)
  74. {
  75. StartCoroutine(FinalRenderCapture());
  76. base.UpdateFrame();
  77. }
  78. else
  79. {
  80. Capture();
  81. base.UpdateFrame();
  82. }
  83. }
  84. private IEnumerator FinalRenderCapture()
  85. {
  86. yield return _waitForEndOfFrame;
  87. Capture();
  88. }
  89. private void Capture()
  90. {
  91. TickFrameTimer();
  92. AccumulateMotionBlur();
  93. if (ShouldCaptureFrame())
  94. {
  95. bool hasSourceTextureChanged = HasSourceTextureChanged();
  96. // If motion blur is enabled, wait until all frames are accumulated
  97. if (IsUsingMotionBlur())
  98. {
  99. // If the motion blur is still accumulating, don't grab this frame
  100. hasSourceTextureChanged = _motionBlur.IsFrameAccumulated;
  101. }
  102. _isSourceTextureChanged = false;
  103. if (hasSourceTextureChanged)
  104. {
  105. if ((_manualUpdate /*&& NativePlugin.IsNewFrameDue(_handle)*/) || CanOutputFrame())
  106. {
  107. // If motion blur is enabled, use the motion blur result
  108. Texture sourceTexture = _sourceTexture;
  109. if (IsUsingMotionBlur())
  110. {
  111. sourceTexture = _motionBlur.FinalTexture;
  112. }
  113. // If the texture isn't suitable then blit it to the Rendertexture so the native plugin can grab it
  114. if (RequiresResolve(sourceTexture))
  115. {
  116. CreateResolveTexture(_targetWidth, _targetHeight);
  117. _resolveTexture.DiscardContents();
  118. // Between Unity 2018.1.0 and 2018.3.0 Unity doesn't seem to set the correct sRGBWrite state and keeps it as false
  119. #if (UNITY_2018_1_OR_NEWER && !UNITY_2018_3_OR_NEWER)
  120. bool sRGBWritePrev = GL.sRGBWrite;
  121. GL.sRGBWrite = true;
  122. #endif
  123. Graphics.Blit(sourceTexture, _resolveTexture);
  124. sourceTexture = _resolveTexture;
  125. #if (UNITY_2018_1_OR_NEWER && !UNITY_2018_3_OR_NEWER)
  126. GL.sRGBWrite = sRGBWritePrev;
  127. #endif
  128. }
  129. // Side-by-side transparency
  130. RenderTexture newSourceTexture = UpdateForSideBySideTransparency( sourceTexture );
  131. if( newSourceTexture )
  132. {
  133. sourceTexture = newSourceTexture;
  134. }
  135. if (_targetNativePointer == System.IntPtr.Zero || _supportTextureRecreate)
  136. {
  137. // NOTE: If support for captures to survive through alt-tab events, or window resizes where the GPU resources are recreated
  138. // is required, then this line is needed. It is very expensive though as it does a sync with the rendering thread.
  139. _targetNativePointer = sourceTexture.GetNativeTexturePtr();
  140. }
  141. NativePlugin.SetTexturePointer(_handle, _targetNativePointer);
  142. RenderThreadEvent(NativePlugin.PluginEvent.CaptureFrameBuffer);
  143. if (!IsUsingMotionBlur())
  144. {
  145. _isSourceTextureChanged = false;
  146. }
  147. EncodeUnityAudio();
  148. UpdateFPS();
  149. }
  150. }
  151. }
  152. RenormTimer();
  153. }
  154. private void CreateResolveTexture(int width, int height)
  155. {
  156. if (_resolveTexture != null)
  157. {
  158. if (_resolveTexture.width != width ||
  159. _resolveTexture.height != height)
  160. {
  161. RenderTexture.ReleaseTemporary(_resolveTexture);
  162. _resolveTexture = null;
  163. }
  164. }
  165. if (_resolveTexture == null)
  166. {
  167. RenderTextureReadWrite readWriteMode = RenderTextureReadWrite.sRGB;
  168. #if AVPRO_MOVIECAPTURE_WEBCAMTEXTURE_SUPPORT
  169. if (QualitySettings.activeColorSpace == ColorSpace.Linear && _sourceTexture is WebCamTexture)
  170. {
  171. // WebCamTexture has odd behaviour, and we're found that we need to resolve without the sRGB conversion
  172. // It's still not 100% correct though - dark colours seem to get crushed to black - we suspect
  173. // this is due to it using limited range instead of full...
  174. // NOTE: This is now commented out as the behaviour isn't correct in Unity 2020
  175. // readWriteMode = RenderTextureReadWrite.Linear;
  176. }
  177. #endif
  178. _resolveTexture = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGB32, readWriteMode);
  179. _resolveTexture.Create();
  180. _targetNativePointer = _resolveTexture.GetNativeTexturePtr();
  181. }
  182. }
  183. private void AccumulateMotionBlur()
  184. {
  185. if (_motionBlur != null)
  186. {
  187. if (ShouldCaptureFrame() && HasSourceTextureChanged())
  188. {
  189. _motionBlur.Accumulate(_sourceTexture);
  190. _isSourceTextureChanged = false;
  191. }
  192. }
  193. }
  194. public override Texture GetPreviewTexture()
  195. {
  196. if (IsUsingMotionBlur())
  197. {
  198. return _motionBlur.FinalTexture;
  199. }
  200. if (_resolveTexture != null)
  201. {
  202. return _resolveTexture;
  203. }
  204. if (_sourceTexture is RenderTexture)
  205. {
  206. return _sourceTexture;
  207. }
  208. #if AVPRO_MOVIECAPTURE_WEBCAMTEXTURE_SUPPORT
  209. if (_sourceTexture is WebCamTexture)
  210. {
  211. return _sourceTexture;
  212. }
  213. #endif
  214. return Texture2D.whiteTexture;
  215. }
  216. public override bool PrepareCapture()
  217. {
  218. if (_capturing)
  219. {
  220. return false;
  221. }
  222. if (_sourceTexture == null)
  223. {
  224. Debug.LogError("[AVProMovieCapture] No texture set to capture");
  225. return false;
  226. }
  227. #if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
  228. if (SystemInfo.graphicsDeviceVersion.StartsWith("Direct3D 9"))
  229. {
  230. Debug.LogError("[AVProMovieCapture] Direct3D9 not yet supported, please use Direct3D11 instead.");
  231. return false;
  232. }
  233. else if (SystemInfo.graphicsDeviceVersion.StartsWith("OpenGL") && !SystemInfo.graphicsDeviceVersion.Contains("emulated"))
  234. {
  235. Debug.LogError("[AVProMovieCapture] OpenGL not yet supported for CaptureFromTexture component, please use Direct3D11 instead. You may need to switch your build platform to Windows.");
  236. return false;
  237. }
  238. #endif
  239. int width = _sourceTexture.width;
  240. int height = _sourceTexture.height;
  241. _Transparency = Transparency.None;
  242. if ( !NativePlugin.IsBasicEdition() )
  243. {
  244. if( (_outputTarget == OutputTarget.VideoFile || _outputTarget == OutputTarget.NamedPipe) && GetEncoderHints().videoHints.transparency != Transparency.None )
  245. {
  246. _Transparency = GetEncoderHints().videoHints.transparency;
  247. }
  248. else if( _outputTarget == OutputTarget.ImageSequence && GetEncoderHints().imageHints.transparency != Transparency.None )
  249. {
  250. _Transparency = GetEncoderHints().imageHints.transparency;
  251. }
  252. //
  253. switch( _Transparency )
  254. {
  255. case Transparency.TopBottom: height *= 2; break;
  256. case Transparency.LeftRight: width *= 2; break;
  257. }
  258. //
  259. if( _Transparency == Transparency.TopBottom || _Transparency == Transparency.LeftRight )
  260. {
  261. InitialiseSideBySideTransparency( _sourceTexture.width, _sourceTexture.height );
  262. }
  263. }
  264. _pixelFormat = NativePlugin.PixelFormat.RGBA32;
  265. _isSourceTextureChanged = false;
  266. SelectRecordingResolution(/*_sourceTexture.*/width, /*_sourceTexture.*/height);
  267. if (_resolveTexture != null)
  268. {
  269. RenderTexture.ReleaseTemporary(_resolveTexture);
  270. _resolveTexture = null;
  271. }
  272. GenerateFilename();
  273. return base.PrepareCapture();
  274. }
  275. public override void UnprepareCapture()
  276. {
  277. _targetNativePointer = System.IntPtr.Zero;
  278. if (_handle != -1)
  279. {
  280. NativePlugin.SetTexturePointer(_handle, System.IntPtr.Zero);
  281. }
  282. if (_resolveTexture != null)
  283. {
  284. RenderTexture.ReleaseTemporary(_resolveTexture);
  285. _resolveTexture = null;
  286. }
  287. base.UnprepareCapture();
  288. }
  289. }
  290. }