295 lines
7.8 KiB
C#
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();
|
|
}
|
|
}
|
|
} |