2024-09-05 16:43:17 +02:00
// UnityEngine.UI was moved to a package in 2019.2.0
// Unfortunately no way to test for this across all Unity versions yet
// You can set up the asmdef to reference the new package, but the package doesn't
// existing in Unity 2017 etc, and it throws an error due to missing reference
#define AVPRO_PACKAGE_UNITYUI
#if ( UNITY_2019_2_OR_NEWER & & AVPRO_PACKAGE_UNITYUI ) | | ( ! UNITY_2019_2_OR_NEWER )
#if UNITY_EDITOR_OSX | | UNITY_STANDALONE_OSX | | UNITY_IOS | | UNITY_TVOS
#define UNITY_PLATFORM_SUPPORTS_YPCBCR
#endif
#if UNITY_EDITOR | | UNITY_STANDALONE_OSX | | UNITY_STANDALONE_WIN | | UNITY_WSA_10_0 | | UNITY_IOS | | UNITY_TVOS | | UNITY_ANDROID | | ( UNITY_WEBGL & & UNITY_2017_2_OR_NEWER )
#define UNITY_PLATFORM_SUPPORTS_LINEAR
#endif
#if ( ! UNITY_STANDALONE_WIN & & ! UNITY_EDITOR_WIN ) & & ( UNITY_EDITOR_OSX | | UNITY_STANDALONE_OSX | | UNITY_IOS | | UNITY_TVOS | | UNITY_VISIONOS | | UNITY_ANDROID )
#define UNITY_PLATFORM_SUPPORTS_VIDEOTRANSFORM
#endif
#if ( UNITY_EDITOR_WIN | | ( ! UNITY_EDITOR & & UNITY_STANDALONE_WIN ) )
#define UNITY_PLATFORM_SUPPORTS_VIDEOASPECTRATIO
#endif
using System.Collections.Generic ;
using UnityEngine ;
using UnityEngine.UI ;
using UnityEngine.Serialization ;
//-----------------------------------------------------------------------------
// Copyright 2015-2021 RenderHeads Ltd. All rights reserved.
//-----------------------------------------------------------------------------
namespace RenderHeads.Media.AVProVideo
{
/// <summary>
/// Displays the video from MediaPlayer component using uGUI
/// </summary>
[HelpURL("http://renderheads.com/products/avpro-video/")]
[AddComponentMenu("AVPro Video/Display uGUI", 200)]
2025-02-21 10:21:19 +01:00
[RequireComponent(typeof(CanvasRenderer))]
2024-09-05 16:43:17 +02:00
//[ExecuteInEditMode]
public class DisplayUGUI : MaskableGraphic
{
[SerializeField] MediaPlayer _mediaPlayer ;
public MediaPlayer Player
{
get { return _mediaPlayer ; }
set { ChangeMediaPlayer ( value ) ; }
}
[Tooltip("Default texture to display when the video texture is preparing")]
[SerializeField] Texture _defaultTexture ;
public Texture DefaultTexture
{
get { return _defaultTexture ; }
set { if ( _defaultTexture ! = value ) { _defaultTexture = value ; } }
}
[FormerlySerializedAs("m_UVRect")]
[SerializeField] Rect _uvRect = new Rect ( 0f , 0f , 1f , 1f ) ;
public Rect UVRect
{
get { return _uvRect ; }
set { _uvRect = value ; }
}
[SerializeField] bool _setNativeSize = false ;
public bool ApplyNativeSize
{
get { return _setNativeSize ; }
set { _setNativeSize = value ; }
}
[SerializeField] ScaleMode _scaleMode = ScaleMode . ScaleToFit ;
public ScaleMode ScaleMode
{
get { return _scaleMode ; }
set { _scaleMode = value ; }
}
[SerializeField] bool _noDefaultDisplay = true ;
public bool NoDefaultDisplay
{
get { return _noDefaultDisplay ; }
set { _noDefaultDisplay = value ; }
}
[SerializeField] bool _displayInEditor = true ;
public bool DisplayInEditor
{
get { return _displayInEditor ; }
set { _displayInEditor = value ; }
}
private int _lastWidth ;
private int _lastHeight ;
private Orientation _lastOrientation ;
private bool _flipY ;
private Texture _lastTexture ;
private static Shader _shaderStereoPacking ;
private static Shader _shaderAlphaPacking ;
private static Shader _shaderAndroidOES ;
private static Shader _shaderAndroidOESAlphaPacking ;
private bool _isUserMaterial = true ;
private Material _material ;
private List < UIVertex > _vertices = new List < UIVertex > ( 4 ) ;
private static List < int > QuadIndices = new List < int > ( new int [ ] { 0 , 1 , 2 , 2 , 3 , 0 } ) ;
protected override void Awake ( )
{
if ( _mediaPlayer ! = null )
{
_mediaPlayer . Events . AddListener ( OnMediaPlayerEvent ) ;
}
base . Awake ( ) ;
}
// Callback function to handle events
private void OnMediaPlayerEvent ( MediaPlayer mp , MediaPlayerEvent . EventType et , ErrorCode errorCode )
{
switch ( et )
{
case MediaPlayerEvent . EventType . FirstFrameReady :
if ( _isUserMaterial & & null ! = GetRequiredShader ( ) )
{
Debug . LogWarning ( "[AVProVideo] Custom material is being used but the video requires our internal shader for correct rendering. Consider removing custom shader or modifying it for AVPro Video support." , this ) ;
}
LateUpdate ( ) ;
break ;
case MediaPlayerEvent . EventType . PropertiesChanged :
case MediaPlayerEvent . EventType . ResolutionChanged :
case MediaPlayerEvent . EventType . Closing :
LateUpdate ( ) ;
break ;
}
// TODO: remove this, we're just doing this so we can make sure texture is correct when running in EDIT mode
LateUpdate ( ) ;
}
private void ChangeMediaPlayer ( MediaPlayer player )
{
if ( _mediaPlayer ! = player )
{
if ( _mediaPlayer ! = null )
{
_mediaPlayer . Events . RemoveListener ( OnMediaPlayerEvent ) ;
}
_mediaPlayer = player ;
if ( _mediaPlayer ! = null )
{
_mediaPlayer . Events . AddListener ( OnMediaPlayerEvent ) ;
}
LateUpdate ( ) ;
}
}
private static Shader EnsureShader ( Shader shader , string name )
{
if ( shader = = null )
{
shader = Shader . Find ( name ) ;
if ( shader = = null )
{
Debug . LogWarning ( "[AVProVideo] Missing shader " + name ) ;
}
}
return shader ;
}
private static Shader EnsureAlphaPackingShader ( )
{
_shaderAlphaPacking = EnsureShader ( _shaderAlphaPacking , "AVProVideo/Internal/UI/Transparent Packed (stereo)" ) ;
return _shaderAlphaPacking ;
}
private static Shader EnsureStereoPackingShader ( )
{
_shaderStereoPacking = EnsureShader ( _shaderStereoPacking , "AVProVideo/Internal/UI/Stereo" ) ;
return _shaderStereoPacking ;
}
private Shader EnsureAndroidOESShader ( )
{
_shaderAndroidOES = EnsureShader ( _shaderAndroidOES , "AVProVideo/Internal/UI/Stereo - AndroidOES" ) ;
return _shaderAndroidOES ;
}
private static Shader EnsureAndroidOESAlphaPackingShader ( )
{
_shaderAndroidOESAlphaPacking = EnsureShader ( _shaderAndroidOESAlphaPacking , "AVProVideo/Internal/UI/Transparent Packed (stereo) - AndroidOES" ) ;
return _shaderAndroidOESAlphaPacking ;
}
protected override void Start ( )
{
_isUserMaterial = ( this . m_Material ! = null ) ;
if ( _isUserMaterial )
{
_material = new Material ( this . material ) ;
this . material = _material ;
}
base . Start ( ) ;
}
protected override void OnDestroy ( )
{
// Destroy existing material
if ( _material ! = null )
{
this . material = null ;
#if UNITY_EDITOR
Material . DestroyImmediate ( _material ) ;
#else
Material . Destroy ( _material ) ;
#endif
_material = null ;
}
ChangeMediaPlayer ( null ) ;
base . OnDestroy ( ) ;
}
private Shader GetRequiredShader ( )
{
Shader result = null ;
if ( result = = null & & _mediaPlayer . TextureProducer ! = null )
{
switch ( _mediaPlayer . TextureProducer . GetTextureStereoPacking ( ) )
{
case StereoPacking . None :
break ;
case StereoPacking . LeftRight :
case StereoPacking . TopBottom :
case StereoPacking . TwoTextures :
result = EnsureStereoPackingShader ( ) ;
break ;
}
if ( _mediaPlayer . TextureProducer . GetTextureTransparency ( ) = = TransparencyMode . Transparent )
{
result = EnsureAlphaPackingShader ( ) ;
}
switch ( _mediaPlayer . TextureProducer . GetTextureAlphaPacking ( ) )
{
case AlphaPacking . None :
break ;
case AlphaPacking . LeftRight :
case AlphaPacking . TopBottom :
result = EnsureAlphaPackingShader ( ) ;
break ;
}
}
#if UNITY_PLATFORM_SUPPORTS_LINEAR
if ( result = = null & & _mediaPlayer . Info ! = null )
{
if ( QualitySettings . activeColorSpace = = ColorSpace . Linear & & ! _mediaPlayer . Info . PlayerSupportsLinearColorSpace ( ) )
{
result = EnsureAlphaPackingShader ( ) ;
}
}
#endif
if ( result = = null & & _mediaPlayer . TextureProducer ! = null & & _mediaPlayer . TextureProducer . GetTextureCount ( ) = = 2 )
{
result = EnsureAlphaPackingShader ( ) ;
}
// if (_mediaPlayer.TextureProducer != null && _mediaPlayer.IsUsingAndroidOESPath() && (_defaultTexture == null || _lastTexture != _defaultTexture ))
if ( _mediaPlayer . TextureProducer ! = null & & _mediaPlayer . IsUsingAndroidOESPath ( ) )
{
// This shader handles stereo too
result = EnsureAndroidOESShader ( ) ;
if ( _mediaPlayer . TextureProducer . GetTextureTransparency ( ) = = TransparencyMode . Transparent )
{
result = EnsureAndroidOESAlphaPackingShader ( ) ;
}
switch ( _mediaPlayer . TextureProducer . GetTextureAlphaPacking ( ) )
{
case AlphaPacking . None :
break ;
case AlphaPacking . LeftRight :
case AlphaPacking . TopBottom :
result = EnsureAndroidOESAlphaPackingShader ( ) ;
break ;
}
}
return result ;
}
/// <summary>
/// Returns the texture used to draw this Graphic.
/// </summary>
public override Texture mainTexture
{
get
{
Texture result = Texture2D . whiteTexture ;
if ( HasValidTexture ( ) )
{
Texture resamplerTex = _mediaPlayer . FrameResampler = = null | | _mediaPlayer . FrameResampler . OutputTexture = = null ? null : _mediaPlayer . FrameResampler . OutputTexture [ 0 ] ;
result = _mediaPlayer . UseResampler ? resamplerTex : _mediaPlayer . TextureProducer . GetTexture ( ) ;
}
else
{
if ( _noDefaultDisplay )
{
result = null ;
}
else if ( _defaultTexture ! = null )
{
result = _defaultTexture ;
}
#if UNITY_EDITOR
if ( result = = null & & _displayInEditor )
{
result = Resources . Load < Texture2D > ( "AVProVideoIcon" ) ;
}
#endif
}
return result ;
}
}
public bool HasValidTexture ( )
{
return ( Application . isPlaying & & _mediaPlayer ! = null & & _mediaPlayer . TextureProducer ! = null & & _mediaPlayer . TextureProducer . GetTexture ( ) ! = null ) ;
}
private void UpdateInternalMaterial ( )
{
if ( _mediaPlayer ! = null )
{
// Get required shader
Shader currentShader = null ;
if ( _material ! = null )
{
currentShader = _material . shader ;
}
Shader nextShader = GetRequiredShader ( ) ;
// If the shader requirement has changed
if ( currentShader ! = nextShader )
{
// Destroy existing material
if ( _material ! = null )
{
this . material = null ;
#if UNITY_EDITOR
Material . DestroyImmediate ( _material ) ;
#else
Material . Destroy ( _material ) ;
#endif
_material = null ;
}
// Create new material
if ( nextShader ! = null )
{
_material = new Material ( nextShader ) ;
}
}
this . material = _material ;
}
}
// We do a LateUpdate() to allow for any changes in the texture that may have happened in Update()
void LateUpdate ( )
{
if ( _setNativeSize )
{
SetNativeSize ( ) ;
}
if ( _lastTexture ! = mainTexture )
{
_lastTexture = mainTexture ;
SetVerticesDirty ( ) ;
SetMaterialDirty ( ) ;
}
if ( HasValidTexture ( ) )
{
if ( mainTexture ! = null )
{
Orientation orientation = Helper . GetOrientation ( _mediaPlayer . Info . GetTextureTransform ( ) ) ;
if ( mainTexture . width ! = _lastWidth | | mainTexture . height ! = _lastHeight | | orientation ! = _lastOrientation )
{
_lastWidth = mainTexture . width ;
_lastHeight = mainTexture . height ;
_lastOrientation = orientation ;
SetVerticesDirty ( ) ;
SetMaterialDirty ( ) ;
}
}
}
if ( Application . isPlaying )
{
if ( ! _isUserMaterial )
{
UpdateInternalMaterial ( ) ;
}
}
if ( material ! = null & & _mediaPlayer ! = null )
{
// TODO: only run when dirty
VideoRender . SetupMaterialForMedia ( materialForRendering , _mediaPlayer ) ;
}
}
/// <summary>
/// Texture to be used.
/// </summary>
public MediaPlayer CurrentMediaPlayer
{
get
{
return _mediaPlayer ;
}
set
{
if ( _mediaPlayer ! = value )
{
_mediaPlayer = value ;
//SetVerticesDirty();
SetMaterialDirty ( ) ;
}
}
}
/// <summary>
/// UV rectangle used by the texture.
/// </summary>
public Rect uvRect
{
get
{
return _uvRect ;
}
set
{
if ( _uvRect = = value )
{
return ;
}
_uvRect = value ;
SetVerticesDirty ( ) ;
}
}
/// <summary>
/// Adjust the scale of the Graphic to make it pixel-perfect.
/// </summary>
[ContextMenu("Set Native Size")]
public override void SetNativeSize ( )
{
Texture tex = mainTexture ;
if ( tex ! = null )
{
int w = Mathf . RoundToInt ( tex . width * uvRect . width ) ;
int h = Mathf . RoundToInt ( tex . height * uvRect . height ) ;
if ( _mediaPlayer ! = null )
{
#if UNITY_PLATFORM_SUPPORTS_VIDEOTRANSFORM & & ! ( ! UNITY_EDITOR & & UNITY_ANDROID )
if ( _mediaPlayer . Info ! = null )
{
Orientation ori = Helper . GetOrientation ( _mediaPlayer . Info . GetTextureTransform ( ) ) ;
if ( ori = = Orientation . Portrait | | ori = = Orientation . PortraitFlipped )
{
w = Mathf . RoundToInt ( tex . height * uvRect . width ) ;
h = Mathf . RoundToInt ( tex . width * uvRect . height ) ;
}
}
#endif
if ( _mediaPlayer . TextureProducer ! = null )
{
if ( _mediaPlayer . TextureProducer . GetTextureAlphaPacking ( ) = = AlphaPacking . LeftRight | |
_mediaPlayer . TextureProducer . GetTextureStereoPacking ( ) = = StereoPacking . LeftRight )
{
w / = 2 ;
}
else if ( _mediaPlayer . TextureProducer . GetTextureAlphaPacking ( ) = = AlphaPacking . TopBottom | |
_mediaPlayer . TextureProducer . GetTextureStereoPacking ( ) = = StereoPacking . TopBottom )
{
h / = 2 ;
}
}
}
rectTransform . anchorMax = rectTransform . anchorMin ;
rectTransform . sizeDelta = new Vector2 ( w , h ) ;
}
}
protected override void OnPopulateMesh ( VertexHelper vh )
{
vh . Clear ( ) ;
_OnFillVBO ( _vertices ) ;
vh . AddUIVertexStream ( _vertices , QuadIndices ) ;
}
private void _OnFillVBO ( List < UIVertex > vbo )
{
_flipY = false ;
if ( HasValidTexture ( ) )
{
_flipY = _mediaPlayer . TextureProducer . RequiresVerticalFlip ( ) ;
}
Rect uvRect = _uvRect ;
Vector4 v = GetDrawingDimensions ( _scaleMode , ref uvRect ) ;
#if UNITY_PLATFORM_SUPPORTS_VIDEOTRANSFORM
Matrix4x4 m = Matrix4x4 . identity ;
if ( HasValidTexture ( ) )
{
m = Helper . GetMatrixForOrientation ( Helper . GetOrientation ( _mediaPlayer . Info . GetTextureTransform ( ) ) ) ;
}
#endif
vbo . Clear ( ) ;
var vert = UIVertex . simpleVert ;
vert . color = color ;
vert . position = new Vector2 ( v . x , v . y ) ;
vert . uv0 = new Vector2 ( uvRect . xMin , uvRect . yMin ) ;
if ( _flipY )
{
vert . uv0 = new Vector2 ( uvRect . xMin , 1.0f - uvRect . yMin ) ;
}
#if UNITY_PLATFORM_SUPPORTS_VIDEOTRANSFORM
vert . uv0 = m . MultiplyPoint3x4 ( vert . uv0 ) ;
#endif
vbo . Add ( vert ) ;
vert . position = new Vector2 ( v . x , v . w ) ;
vert . uv0 = new Vector2 ( uvRect . xMin , uvRect . yMax ) ;
if ( _flipY )
{
vert . uv0 = new Vector2 ( uvRect . xMin , 1.0f - uvRect . yMax ) ;
}
#if UNITY_PLATFORM_SUPPORTS_VIDEOTRANSFORM
vert . uv0 = m . MultiplyPoint3x4 ( vert . uv0 ) ;
#endif
vbo . Add ( vert ) ;
vert . position = new Vector2 ( v . z , v . w ) ;
vert . uv0 = new Vector2 ( uvRect . xMax , uvRect . yMax ) ;
if ( _flipY )
{
vert . uv0 = new Vector2 ( uvRect . xMax , 1.0f - uvRect . yMax ) ;
}
#if UNITY_PLATFORM_SUPPORTS_VIDEOTRANSFORM
vert . uv0 = m . MultiplyPoint3x4 ( vert . uv0 ) ;
#endif
vbo . Add ( vert ) ;
vert . position = new Vector2 ( v . z , v . y ) ;
vert . uv0 = new Vector2 ( uvRect . xMax , uvRect . yMin ) ;
if ( _flipY )
{
vert . uv0 = new Vector2 ( uvRect . xMax , 1.0f - uvRect . yMin ) ;
}
#if UNITY_PLATFORM_SUPPORTS_VIDEOTRANSFORM
vert . uv0 = m . MultiplyPoint3x4 ( vert . uv0 ) ;
#endif
vbo . Add ( vert ) ;
}
private Vector4 GetDrawingDimensions ( ScaleMode scaleMode , ref Rect uvRect )
{
Vector4 returnSize = Vector4 . zero ;
if ( mainTexture ! = null )
{
var padding = Vector4 . zero ;
var textureSize = new Vector2 ( mainTexture . width , mainTexture . height ) ;
{
// Adjust textureSize based on orientation
#if UNITY_PLATFORM_SUPPORTS_VIDEOTRANSFORM & & ! ( ! UNITY_EDITOR & & UNITY_ANDROID )
if ( HasValidTexture ( ) )
{
Matrix4x4 m = Helper . GetMatrixForOrientation ( Helper . GetOrientation ( _mediaPlayer . Info . GetTextureTransform ( ) ) ) ;
textureSize = m . MultiplyVector ( textureSize ) ;
textureSize . x = Mathf . Abs ( textureSize . x ) ;
textureSize . y = Mathf . Abs ( textureSize . y ) ;
}
#endif
#if UNITY_PLATFORM_SUPPORTS_VIDEOASPECTRATIO
if ( HasValidTexture ( ) )
{
float par = _mediaPlayer . TextureProducer . GetTexturePixelAspectRatio ( ) ;
if ( par > 0f )
{
if ( par > 1f )
{
textureSize . x * = par ;
}
else
{
textureSize . y / = par ;
}
}
}
#endif
// Adjust textureSize based on alpha/stereo packing
if ( _mediaPlayer ! = null & & _mediaPlayer . TextureProducer ! = null )
{
if ( _mediaPlayer . TextureProducer . GetTextureAlphaPacking ( ) = = AlphaPacking . LeftRight | |
_mediaPlayer . TextureProducer . GetTextureStereoPacking ( ) = = StereoPacking . LeftRight )
{
textureSize . x / = 2 ;
}
else if ( _mediaPlayer . TextureProducer . GetTextureAlphaPacking ( ) = = AlphaPacking . TopBottom | |
_mediaPlayer . TextureProducer . GetTextureStereoPacking ( ) = = StereoPacking . TopBottom )
{
textureSize . y / = 2 ;
}
}
}
Rect r = GetPixelAdjustedRect ( ) ;
// Fit the above textureSize into rectangle r
int spriteW = Mathf . RoundToInt ( textureSize . x ) ;
int spriteH = Mathf . RoundToInt ( textureSize . y ) ;
var size = new Vector4 ( padding . x / spriteW ,
padding . y / spriteH ,
( spriteW - padding . z ) / spriteW ,
( spriteH - padding . w ) / spriteH ) ;
{
if ( textureSize . sqrMagnitude > 0.0f )
{
if ( scaleMode = = ScaleMode . ScaleToFit )
{
float spriteRatio = textureSize . x / textureSize . y ;
float rectRatio = r . width / r . height ;
if ( spriteRatio > rectRatio )
{
float oldHeight = r . height ;
r . height = r . width * ( 1.0f / spriteRatio ) ;
r . y + = ( oldHeight - r . height ) * rectTransform . pivot . y ;
}
else
{
float oldWidth = r . width ;
r . width = r . height * spriteRatio ;
r . x + = ( oldWidth - r . width ) * rectTransform . pivot . x ;
}
}
else if ( scaleMode = = ScaleMode . ScaleAndCrop )
{
float aspectRatio = textureSize . x / textureSize . y ;
float screenRatio = r . width / r . height ;
if ( screenRatio > aspectRatio )
{
float adjust = aspectRatio / screenRatio ;
uvRect = new Rect ( uvRect . xMin , ( uvRect . yMin * adjust ) + ( 1f - adjust ) * 0.5f , uvRect . width , adjust * uvRect . height ) ;
}
else
{
float adjust = screenRatio / aspectRatio ;
uvRect = new Rect ( uvRect . xMin * adjust + ( 0.5f - adjust * 0.5f ) , uvRect . yMin , adjust * uvRect . width , uvRect . height ) ;
}
}
}
}
returnSize = new Vector4 ( r . x + r . width * size . x ,
r . y + r . height * size . y ,
r . x + r . width * size . z ,
r . y + r . height * size . w ) ;
}
return returnSize ;
}
}
}
#endif