NewN_UAVPlane/Assets/Plugins/RenderHeads/AVProMovieCapture/Runtime/Scripts/Components/CaptureFromTexture.cs

295 lines
7.8 KiB
C#

using UnityEngine;
using System.Collections;
//-----------------------------------------------------------------------------
// Copyright 2012-2021 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProMovieCapture
{
/// <summary>
/// Capture from a Texture object (including RenderTexture, WebcamTexture)
/// </summary>
[AddComponentMenu("AVPro Movie Capture/Capture From Texture", 3)]
public class CaptureFromTexture : CaptureBase
{
[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.")]
[SerializeField] bool _manualUpdate = false;
public bool IsManualUpdate
{
get { return _manualUpdate; }
set { _manualUpdate = value; }
}
private Texture _sourceTexture;
private RenderTexture _resolveTexture;
private System.IntPtr _targetNativePointer = System.IntPtr.Zero;
private bool _isSourceTextureChanged = false;
public void SetSourceTexture(Texture texture)
{
_sourceTexture = texture;
}
private bool RequiresResolve(Texture texture)
{
bool result = false;
if (texture is RenderTexture)
{
RenderTexture rt = texture as RenderTexture;
// Linear textures require resolving to sRGB
result = !rt.sRGB;
if (!result &&
(rt.format != RenderTextureFormat.ARGB32) &&
(rt.format != RenderTextureFormat.Default) &&
(rt.format != RenderTextureFormat.BGRA32)
)
{
// Exotic texture formats require resolving
result = true;
}
}
else
{
// Any other texture type needs to be resolve to RenderTexture
result = true;
}
return result;
}
public void UpdateSourceTexture()
{
_isSourceTextureChanged = true;
}
private bool ShouldCaptureFrame()
{
return (_capturing && !_paused && _sourceTexture != null);
}
private bool HasSourceTextureChanged()
{
return (!_manualUpdate || (_manualUpdate && _isSourceTextureChanged));
}
public override void UpdateFrame()
{
if (_useWaitForEndOfFrame)
{
StartCoroutine(FinalRenderCapture());
base.UpdateFrame();
}
else
{
Capture();
base.UpdateFrame();
}
}
private IEnumerator FinalRenderCapture()
{
yield return _waitForEndOfFrame;
Capture();
}
private void Capture()
{
TickFrameTimer();
AccumulateMotionBlur();
if (ShouldCaptureFrame())
{
bool hasSourceTextureChanged = HasSourceTextureChanged();
// If motion blur is enabled, wait until all frames are accumulated
if (IsUsingMotionBlur())
{
// If the motion blur is still accumulating, don't grab this frame
hasSourceTextureChanged = _motionBlur.IsFrameAccumulated;
}
_isSourceTextureChanged = false;
if (hasSourceTextureChanged)
{
if ((_manualUpdate /*&& NativePlugin.IsNewFrameDue(_handle)*/) || CanOutputFrame())
{
// If motion blur is enabled, use the motion blur result
Texture sourceTexture = _sourceTexture;
if (IsUsingMotionBlur())
{
sourceTexture = _motionBlur.FinalTexture;
}
// If the texture isn't suitable then blit it to the Rendertexture so the native plugin can grab it
if (RequiresResolve(sourceTexture))
{
CreateResolveTexture(_targetWidth, _targetHeight);
_resolveTexture.DiscardContents();
// Between Unity 2018.1.0 and 2018.3.0 Unity doesn't seem to set the correct sRGBWrite state and keeps it as false
#if (UNITY_2018_1_OR_NEWER && !UNITY_2018_3_OR_NEWER)
bool sRGBWritePrev = GL.sRGBWrite;
GL.sRGBWrite = true;
#endif
Graphics.Blit(sourceTexture, _resolveTexture);
sourceTexture = _resolveTexture;
#if (UNITY_2018_1_OR_NEWER && !UNITY_2018_3_OR_NEWER)
GL.sRGBWrite = sRGBWritePrev;
#endif
}
if (_targetNativePointer == System.IntPtr.Zero || _supportTextureRecreate)
{
// NOTE: If support for captures to survive through alt-tab events, or window resizes where the GPU resources are recreated
// is required, then this line is needed. It is very expensive though as it does a sync with the rendering thread.
_targetNativePointer = sourceTexture.GetNativeTexturePtr();
}
NativePlugin.SetTexturePointer(_handle, _targetNativePointer);
RenderThreadEvent(NativePlugin.PluginEvent.CaptureFrameBuffer);
if (!IsUsingMotionBlur())
{
_isSourceTextureChanged = false;
}
EncodeUnityAudio();
UpdateFPS();
}
}
}
RenormTimer();
}
private void CreateResolveTexture(int width, int height)
{
if (_resolveTexture != null)
{
if (_resolveTexture.width != width ||
_resolveTexture.height != height)
{
RenderTexture.ReleaseTemporary(_resolveTexture);
_resolveTexture = null;
}
}
if (_resolveTexture == null)
{
RenderTextureReadWrite readWriteMode = RenderTextureReadWrite.sRGB;
if (QualitySettings.activeColorSpace == ColorSpace.Linear && _sourceTexture is WebCamTexture)
{
// WebCamTexture has odd behaviour, and we're found that we need to resolve without the sRGB conversion
// It's still not 100% correct though - dark colours seem to get crushed to black - we suspect
// this is due to it using limited range instead of full...
// NOTE: This is now commented out as the behaviour isn't correct in Unity 2020
// readWriteMode = RenderTextureReadWrite.Linear;
}
_resolveTexture = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGB32, readWriteMode);
_resolveTexture.Create();
_targetNativePointer = _resolveTexture.GetNativeTexturePtr();
}
}
private void AccumulateMotionBlur()
{
if (_motionBlur != null)
{
if (ShouldCaptureFrame() && HasSourceTextureChanged())
{
_motionBlur.Accumulate(_sourceTexture);
_isSourceTextureChanged = false;
}
}
}
public override Texture GetPreviewTexture()
{
if (IsUsingMotionBlur())
{
return _motionBlur.FinalTexture;
}
if (_resolveTexture != null)
{
return _resolveTexture;
}
if (_sourceTexture is RenderTexture)
{
return _sourceTexture;
}
if (_sourceTexture is WebCamTexture)
{
return _sourceTexture;
}
return Texture2D.whiteTexture;
}
public override bool PrepareCapture()
{
if (_capturing)
{
return false;
}
if (_sourceTexture == null)
{
Debug.LogError("[AVProMovieCapture] No texture set to capture");
return false;
}
#if UNITY_EDITOR_WIN || (!UNITY_EDITOR && UNITY_STANDALONE_WIN)
if (SystemInfo.graphicsDeviceVersion.StartsWith("Direct3D 9"))
{
Debug.LogError("[AVProMovieCapture] Direct3D9 not yet supported, please use Direct3D11 instead.");
return false;
}
else if (SystemInfo.graphicsDeviceVersion.StartsWith("OpenGL") && !SystemInfo.graphicsDeviceVersion.Contains("emulated"))
{
Debug.LogError("[AVProMovieCapture] OpenGL not yet supported for CaptureFromTexture component, please use Direct3D11 instead. You may need to switch your build platform to Windows.");
return false;
}
#endif
_pixelFormat = NativePlugin.PixelFormat.RGBA32;
_isSourceTextureChanged = false;
SelectRecordingResolution(_sourceTexture.width, _sourceTexture.height);
if (_resolveTexture != null)
{
RenderTexture.ReleaseTemporary(_resolveTexture);
_resolveTexture = null;
}
GenerateFilename();
return base.PrepareCapture();
}
public override void UnprepareCapture()
{
_targetNativePointer = System.IntPtr.Zero;
if (_handle != -1)
{
NativePlugin.SetTexturePointer(_handle, System.IntPtr.Zero);
}
if (_resolveTexture != null)
{
RenderTexture.ReleaseTemporary(_resolveTexture);
_resolveTexture = null;
}
base.UnprepareCapture();
}
}
}