using UnityEngine; //----------------------------------------------------------------------------- // Copyright 2019-2023 RenderHeads Ltd. All rights reserved. //----------------------------------------------------------------------------- namespace RenderHeads.Media.AVProVideo { /// Renders the video texture to a RenderTexture - either one provided by the user (external) or to an internal one. /// The video frames can optionally be "resolved" to unpack packed alpha, display a single stereo eye, generate mip maps, and apply colorspace conversions [AddComponentMenu("AVPro Video/Resolve To RenderTexture", 330)] [HelpURL("https://www.renderheads.com/products/avpro-video/")] public class ResolveToRenderTexture : MonoBehaviour { [SerializeField] MediaPlayer _mediaPlayer = null; [SerializeField] VideoResolveOptions _options = VideoResolveOptions.Create(); [SerializeField] VideoRender.ResolveFlags _resolveFlags = (VideoRender.ResolveFlags.ColorspaceSRGB | VideoRender.ResolveFlags.Mipmaps | VideoRender.ResolveFlags.PackedAlpha | VideoRender.ResolveFlags.StereoLeft); [SerializeField] RenderTexture _externalTexture = null; private Material _materialResolve; private bool _isMaterialSetup; private bool _isMaterialDirty; private bool _isMaterialOES; private RenderTexture _internalTexture; private int _textureFrameCount = -1; // Material used for blitting the texture as we need a shader to provide clamp to border colour style texture sampling private Material _materialBlit; private int _srcTexId; public MediaPlayer MediaPlayer { get { return _mediaPlayer; } set { ChangeMediaPlayer(value); } } public VideoResolveOptions VideoResolveOptions { get { return _options; } set { _options = value; _isMaterialDirty = true; } } public RenderTexture ExternalTexture { get { return _externalTexture; } set { _externalTexture = value; } } public RenderTexture TargetTexture { get { if (_externalTexture == null) return _internalTexture; return _externalTexture; } } public void SetMaterialDirty() { _isMaterialDirty = true; } private void ChangeMediaPlayer(MediaPlayer mediaPlayer) { if (_mediaPlayer != mediaPlayer) { _mediaPlayer = mediaPlayer; _textureFrameCount = -1; _isMaterialSetup = false; _isMaterialDirty = true; Resolve(); } } void Start() { _isMaterialOES = _mediaPlayer != null ? _mediaPlayer.IsUsingAndroidOESPath() : false; _materialResolve = VideoRender.CreateResolveMaterial(_isMaterialOES); VideoRender.SetupMaterialForMedia(_materialResolve, _mediaPlayer, -1); _materialBlit = new Material(Shader.Find("AVProVideo/Internal/Blit")); _srcTexId = Shader.PropertyToID("_SrcTex"); } void LateUpdate() { Debug.Assert(_mediaPlayer != null); Resolve(); } public void Resolve() { ITextureProducer textureProducer = _mediaPlayer != null ? _mediaPlayer.TextureProducer : null; if (textureProducer == null) return; if (textureProducer.GetTexture()) { // Check for a swap between OES and none-OES bool playerIsOES = _mediaPlayer.IsUsingAndroidOESPath(); if (_isMaterialOES != playerIsOES) { _isMaterialOES = playerIsOES; _materialResolve = VideoRender.CreateResolveMaterial(playerIsOES); } if (!_isMaterialSetup) { VideoRender.SetupMaterialForMedia(_materialResolve, _mediaPlayer, -1); _isMaterialSetup = true; _isMaterialDirty = true; } if (_isMaterialDirty) { VideoRender.SetupResolveMaterial(_materialResolve, _options); _isMaterialDirty = false; } int textureFrameCount = textureProducer.GetTextureFrameCount(); if (textureFrameCount != _textureFrameCount) { _internalTexture = VideoRender.ResolveVideoToRenderTexture(_materialResolve, _internalTexture, textureProducer, _resolveFlags); _textureFrameCount = textureFrameCount; if (_internalTexture && _externalTexture) { float srcAspectRatio = (float)_internalTexture.width / (float)_internalTexture.height; float dstAspectRatio = (float)_externalTexture.width / (float)_externalTexture.height; Vector2 offset = Vector2.zero; Vector2 scale = new Vector2(1.0f, 1.0f); // No point in handling the aspect ratio if the textures dimension's are the same if (srcAspectRatio != dstAspectRatio) { switch (_options.aspectRatio) { case VideoResolveOptions.AspectRatio.NoScaling: scale.x = (float)_externalTexture.width / (float)_internalTexture.width; scale.y = (float)_externalTexture.height / (float)_internalTexture.height; offset.x = (1.0f - scale.x) * 0.5f; offset.y = (1.0f - scale.y) * 0.5f; break; case VideoResolveOptions.AspectRatio.FitVertically: scale.x = (float)_internalTexture.height / (float)_internalTexture.width * dstAspectRatio; offset.x = (1.0f - scale.x) * 0.5f; break; case VideoResolveOptions.AspectRatio.FitHorizontally: scale.y = (float)_externalTexture.height / (float)_externalTexture.width * srcAspectRatio; offset.y = (1.0f - scale.y) * 0.5f; break; case VideoResolveOptions.AspectRatio.FitInside: { if (srcAspectRatio > dstAspectRatio) goto case VideoResolveOptions.AspectRatio.FitHorizontally; else if (srcAspectRatio < dstAspectRatio) goto case VideoResolveOptions.AspectRatio.FitVertically; } break; case VideoResolveOptions.AspectRatio.FitOutside: { if (srcAspectRatio > dstAspectRatio) goto case VideoResolveOptions.AspectRatio.FitVertically; else if (srcAspectRatio < dstAspectRatio) goto case VideoResolveOptions.AspectRatio.FitHorizontally; } break; case VideoResolveOptions.AspectRatio.Stretch: break; } } // NOTE: This blit can be removed once we can ResolveVideoToRenderTexture is made not to recreate textures // NOTE: This blit probably doesn't do correct linear/srgb conversion if the colorspace settings differ, may have to use GL.sRGBWrite // NOTE: Cannot use _MainTex as Graphics.Blit replaces the texture offset and scale when using a material _materialBlit.SetTexture(_srcTexId, _internalTexture); _materialBlit.SetTextureOffset(_srcTexId, offset); _materialBlit.SetTextureScale(_srcTexId, scale); Graphics.Blit(null, _externalTexture, _materialBlit, 0); } } } } void OnDisable() { if (_internalTexture) { RenderTexture.ReleaseTemporary(_internalTexture); _internalTexture = null; } } void OnDestroy() { if (_materialResolve) { Destroy(_materialResolve); _materialResolve = null; } } #if false void OnGUI() { if (TargetTexture) { GUI.DrawTexture(new Rect(0f, 0f, Screen.width * 0.8f, Screen.height * 0.8f), TargetTexture, ScaleMode.ScaleToFit, true); } } #endif } }