// NOTE: We only allow this script to compile in editor so we can easily check for compilation issues #if (UNITY_EDITOR || (UNITY_STANDALONE_WIN || UNITY_WSA_10_0)) #if UNITY_WSA_10 || ENABLE_IL2CPP #define AVPROVIDEO_MARSHAL_RETURN_BOOL #endif using UnityEngine; using System.Runtime.InteropServices; using System.Collections.Generic; using System; using System.Text; //----------------------------------------------------------------------------- // Copyright 2018-2021 RenderHeads Ltd. All rights reserved. //----------------------------------------------------------------------------- namespace RenderHeads.Media.AVProVideo { public enum PlaybackState { None = 0, Opening = 1, Buffering = 2, // Replace with Stalled and add Buffering as State 64?? Playing = 3, Paused = 4, StateMask = 7, Seeking = 32, } public class AuthData { public string URL { get; set; } public string Token { get; set; } public byte[] KeyBytes { get; set; } public AuthData() { Clear(); } public void Clear() { URL = string.Empty; Token = string.Empty; KeyBytes = null; } public string KeyBase64 { get { if (KeyBytes != null) { return System.Convert.ToBase64String(KeyBytes); } else { return string.Empty; } } set { if (value != null) { KeyBytes = System.Convert.FromBase64String(value); } else { KeyBytes = null; } } } }; public partial class WindowsRtMediaPlayer : BaseMediaPlayer { private bool _isMediaLoaded = false; private bool _isLooping = false; private float _volume = 1.0f; private bool _use10BitTextures = false; private bool _useLowLiveLatency = false; public WindowsRtMediaPlayer(MediaPlayer.OptionsWindows options) : base() { _playerDescription = "WinRT"; _use10BitTextures = options.use10BitTextures; _useLowLiveLatency = options.useLowLiveLatency; for (int i = 0; i < _eyeTextures.Length; i++) { _eyeTextures[i] = new EyeTexture(); } } public WindowsRtMediaPlayer(MediaPlayer.OptionsWindowsUWP options) : base() { _playerDescription = "WinRT"; _use10BitTextures = options.use10BitTextures; _useLowLiveLatency = options.useLowLiveLatency; for (int i = 0; i < _eyeTextures.Length; i++) { _eyeTextures[i] = new EyeTexture(); } } public override bool CanPlay() { return HasMetaData(); } public override void Dispose() { CloseMedia(); if (_playerInstance != System.IntPtr.Zero) { Native.DestroyPlayer(_playerInstance); _playerInstance = System.IntPtr.Zero; Native.IssueRenderThreadEvent_FreeAllTextures(); } for (int i = 0; i < _eyeTextures.Length; i++) { _eyeTextures[i].Dispose(); } } public override bool PlayerSupportsLinearColorSpace() { // The current player doesn't support rendering to SRGB textures return false; } public override double GetCurrentTime() { return Native.GetCurrentPosition(_playerInstance); } public override double GetDuration() { return Native.GetDuration(_playerInstance); } public override float GetPlaybackRate() { return Native.GetPlaybackRate(_playerInstance); } public override Texture GetTexture(int index = 0) { Texture result = null; if (_frameTimeStamp > 0 && index < _eyeTextures.Length) { result = _eyeTextures[index].texture; } return result; } public override int GetTextureCount() { if (_eyeTextures[1].texture != null) { return 2; } return 1; } public override int GetTextureFrameCount() { return (int)_frameTimeStamp; } internal override StereoPacking InternalGetTextureStereoPacking() { return Native.GetStereoPacking(_playerInstance); } public override string GetVersion() { return _version; } public override string GetExpectedVersion() { return Helper.ExpectedPluginVersion.WinRT; } public override float GetVideoFrameRate() { float result = 0f; Native.VideoTrack videoTrack; if (Native.GetActiveVideoTrackInfo(_playerInstance, out videoTrack)) { result = videoTrack.frameRate; } return result; } public override int GetVideoWidth() { int result = 0; if (_eyeTextures[0].texture) { result = _eyeTextures[0].texture.width; } return result; } public override int GetVideoHeight() { int result = 0; if (_eyeTextures[0].texture) { result = _eyeTextures[0].texture.height; } return result; } public override float GetVolume() { return _volume;//Native.GetAudioVolume(_playerInstance); } public override void SetBalance(float balance) { Native.SetAudioBalance(_playerInstance, balance); } public override float GetBalance() { return Native.GetAudioBalance(_playerInstance); } public override bool HasAudio() { return _audioTracks.Count > 0; } public override bool HasMetaData() { return Native.GetDuration(_playerInstance) > 0f; } public override bool HasVideo() { return _videoTracks.Count > 0; } public override bool IsBuffering() { return ((Native.GetPlaybackState(_playerInstance) & PlaybackState.StateMask) == PlaybackState.Buffering); } public override bool IsFinished() { bool result = false; if (IsPaused() && !IsSeeking() && GetCurrentTime() >= GetDuration()) { result = true; } return result; } public override bool IsLooping() { return _isLooping;//Native.IsLooping(_playerInstance); } public override bool IsMuted() { return Native.IsAudioMuted(_playerInstance); } public override bool IsPaused() { return ((Native.GetPlaybackState(_playerInstance) & PlaybackState.StateMask) == PlaybackState.Paused); } public override bool IsPlaying() { return ((Native.GetPlaybackState(_playerInstance) & PlaybackState.StateMask) == PlaybackState.Playing); } public override bool IsSeeking() { return ((Native.GetPlaybackState(_playerInstance) & PlaybackState.Seeking) != 0); } public override void MuteAudio(bool bMuted) { Native.SetAudioMuted(_playerInstance, bMuted); } // TODO: replace all these options with a structure public override bool OpenMedia(string path, long offset, string httpHeader, MediaHints mediaHints, int forceFileFormat = 0, bool startWithHighestBitrate = false) { bool result = false; // RJT NOTE: Commented out as already called by 'InternalOpenMedia()' which calls this function // CloseMedia(); if (_playerInstance == System.IntPtr.Zero) { _playerInstance = Native.CreatePlayer(); // Force setting any auth data as it wouldn't have been set without a _playerInstance AuthenticationData = _nextAuthData; } if (_playerInstance != System.IntPtr.Zero) { result = Native.OpenMedia(_playerInstance, path, httpHeader, (FileFormat)forceFileFormat, startWithHighestBitrate, _use10BitTextures); if (result) { if (_useLowLiveLatency) { Native.SetLiveOffset(_playerInstance, 0.0); } // RJT NOTE: Other platforms create their native instances earlier than 'OpenMedia()' and set looping at that // point which Windows misses, so make sure once we have an instance we pass the looping flag down retrospectively // - https://github.com/RenderHeads/UnityPlugin-AVProVideo/issues/1913 // - Same now with volume: https://github.com/RenderHeads/UnityPlugin-AVProVideo/issues/1916 Native.SetLooping(_playerInstance, _isLooping); Native.SetAudioVolume(_playerInstance, _volume); } _mediaHints = mediaHints; } return result; } public override void CloseMedia() { // NOTE: This unloads the current video, but the texture should remain _isMediaLoaded = false; _isLooping = false; _volume = 1.0f; Native.CloseMedia(_playerInstance); base.CloseMedia(); } public override void Pause() { Native.Pause(_playerInstance); } public override void Play() { Native.Play(_playerInstance); } public override void Render() { Native.IssueRenderThreadEvent_UpdateAllTextures(); } private void Update_Textures() { // See if there is a new frame ready { System.IntPtr texturePointerLeft = System.IntPtr.Zero; System.IntPtr texturePointerRight = System.IntPtr.Zero; ulong frameTimeStamp = 0; int width, height; if (Native.GetLatestFrame(_playerInstance, out texturePointerLeft, out texturePointerRight, out frameTimeStamp, out width, out height)) { bool isFrameUpdated = false; bool isNewFrameTime = (frameTimeStamp > _frameTimeStamp) || (_frameTimeStamp == 0 && frameTimeStamp == 0); for (int i = 0; i < _eyeTextures.Length; i++) { EyeTexture eyeTexture = _eyeTextures[i]; System.IntPtr texturePointer = texturePointerLeft; if (i == 1) { texturePointer = texturePointerRight; } bool isNewFrameSpecs = (eyeTexture.texture != null && (texturePointer == IntPtr.Zero || eyeTexture.texture.width != width || eyeTexture.texture.height != height)); //Debug.Log("tex? " + i + " " + width + " " + height + " " + (eyeTexture.texture != null) + " " + texturePointer.ToString() + " " + frameTimeStamp); // Check whether the latest frame is newer than the one we got last time if (isNewFrameTime || isNewFrameSpecs) { if (isNewFrameSpecs) { eyeTexture.Dispose(); // TODO: blit from the old texture to the new texture before destroying? } /// Switch to the latest texture pointer if (eyeTexture.texture != null) { // TODO: check whether UpdateExternalTexture resets the sampling filter to POINT - it seems to in Unity 5.6.6 if (eyeTexture.nativePointer != texturePointer) { eyeTexture.texture.UpdateExternalTexture(texturePointer); eyeTexture.nativePointer = texturePointer; } } else { if (texturePointer != IntPtr.Zero) { // RJT NOTE: See notes in 'WindowsMediaPlayer::UpdateTexture()' re: 'isLinear' bool isLinear = (/*!_supportsLinearColorSpace*/true && (QualitySettings.activeColorSpace == ColorSpace.Linear)); eyeTexture.texture = Texture2D.CreateExternalTexture(width, height, TextureFormat.BGRA32, false, isLinear, texturePointer); if (eyeTexture.texture != null) { eyeTexture.texture.name = "AVProVideo"; eyeTexture.nativePointer = texturePointer; ApplyTextureProperties(eyeTexture.texture); } else { Debug.LogError("[AVProVideo] Failed to create texture"); } } } isFrameUpdated = true; } } if (isFrameUpdated) { _frameTimeStamp = frameTimeStamp; } } } } private AuthData _nextAuthData = new AuthData(); public AuthData AuthenticationData { get { return _nextAuthData; } set { _nextAuthData = value; Native.SetNextAuthData(_playerInstance, _nextAuthData); } } public override bool RequiresVerticalFlip() { return true; } public override void Seek(double time) { Native.SeekParams seekParams = new Native.SeekParams(); seekParams.timeSeconds = time; seekParams.mode = Native.SeekMode.Accurate; Native.Seek(_playerInstance, ref seekParams); } public override void SeekFast(double time) { // Keyframe seeking is not supported on this platform Seek(time); } public override void SetLooping(bool bLooping) { _isLooping = bLooping; Native.SetLooping(_playerInstance, _isLooping); } public override void SetPlaybackRate(float rate) { // Clamp rate as WinRT doesn't seem to be able to handle negative rate rate = Mathf.Max(0f, rate); Native.SetPlaybackRate(_playerInstance, rate); } public override void SetVolume(float volume) { _volume = volume; Native.SetAudioVolume(_playerInstance, _volume); } public override void Stop() { Pause(); } private void UpdateTimeRanges() { UpdateTimeRange(ref _seekableTimes._ranges, Native.TimeRangeTypes.Seekable); UpdateTimeRange(ref _bufferedTimes._ranges, Native.TimeRangeTypes.Buffered); _seekableTimes.CalculateRange(); _bufferedTimes.CalculateRange(); } private void UpdateTimeRange(ref TimeRange[] range, Native.TimeRangeTypes timeRangeType) { int newCount = Native.GetTimeRanges(_playerInstance, range, range.Length, timeRangeType); if (newCount != range.Length) { range = new TimeRange[newCount]; Native.GetTimeRanges(_playerInstance, range, range.Length, timeRangeType); } } public override System.DateTime GetProgramDateTime() { double seconds = Native.GetCurrentDateTimeSecondsSince1970(_playerInstance); return Helper.ConvertSecondsSince1970ToDateTime(seconds); } public override void Update() { Native.Update(_playerInstance); UpdateTracks(); UpdateTextCue(); _lastError = (ErrorCode)Native.GetLastErrorCode(_playerInstance); UpdateTimeRanges(); UpdateSubtitles(); Update_Textures(); UpdateDisplayFrameRate(); if (!_isMediaLoaded) { if (HasVideo() && _eyeTextures[0].texture != null) { Native.VideoTrack videoTrack; if (Native.GetActiveVideoTrackInfo(_playerInstance, out videoTrack)) { Helper.LogInfo("Using playback path: " + _playerDescription + " (" + videoTrack.frameWidth + "x" + videoTrack.frameHeight + "@" + videoTrack.frameRate.ToString("F2") + ")"); _isMediaLoaded = true; } } else if (HasAudio() && !HasVideo()) { Helper.LogInfo("Using playback path: " + _playerDescription); _isMediaLoaded = true; } } } /*public override void SetKeyServerURL(string url) { _nextAuthData.URL = url; AuthenticationData = _nextAuthData; }*/ public override void SetKeyServerAuthToken(string token) { _nextAuthData.Token = token; AuthenticationData = _nextAuthData; } public override void SetOverrideDecryptionKey(byte[] key) { _nextAuthData.KeyBytes = key; AuthenticationData = _nextAuthData; } } // Tracks public sealed partial class WindowsRtMediaPlayer { internal override bool InternalSetActiveTrack(TrackType trackType, int trackUid) { return Native.SetActiveTrack(_playerInstance, trackType, trackUid); } // Has it changed since the last frame 'tick' internal override bool InternalIsChangedTracks(TrackType trackType) { return Native.IsChangedTracks(_playerInstance, trackType); } internal override int InternalGetTrackCount(TrackType trackType) { return Native.GetTrackCount(_playerInstance, trackType); } internal override TrackBase InternalGetTrackInfo(TrackType trackType, int trackIndex, ref bool isActiveTrack) { TrackBase result = null; StringBuilder name = new StringBuilder(128); StringBuilder language = new StringBuilder(16); int uid = -1; if (Native.GetTrackInfo(_playerInstance, trackType, trackIndex, ref uid, ref isActiveTrack, name, name.Capacity, language, language.Capacity)) { if (trackType == TrackType.Video) { result = new VideoTrack(uid, name.ToString(), language.ToString(), false); } else if (trackType == TrackType.Audio) { result = new AudioTrack(uid, name.ToString(), language.ToString(), false); } else if (trackType == TrackType.Text) { result = new TextTrack(uid, name.ToString(), language.ToString(), false); } } return result; } private partial struct Native { [DllImport("AVProVideoWinRT")] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsChangedTracks(System.IntPtr instance, TrackType trackType); [DllImport("AVProVideoWinRT")] public static extern int GetTrackCount(System.IntPtr instance, TrackType trackType); [DllImport("AVProVideoWinRT")] [return: MarshalAs(UnmanagedType.I1)] public static extern bool GetTrackInfo(System.IntPtr instance, TrackType trackType, int index, ref int uid, ref bool isActive, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder name, int maxNameLength, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder language, int maxLanguageLength); [DllImport("AVProVideoWinRT")] [return: MarshalAs(UnmanagedType.I1)] public static extern bool SetActiveTrack(System.IntPtr instance, TrackType trackType, int trackUid); } } // Text Cue public sealed partial class WindowsRtMediaPlayer { // Has it changed since the last frame 'tick' internal override bool InternalIsChangedTextCue() { return Native.IsChangedTextCue(_playerInstance); } internal override string InternalGetCurrentTextCue() { string result = null; System.IntPtr ptr = Native.GetCurrentTextCue(_playerInstance); if (ptr != System.IntPtr.Zero) { result = System.Runtime.InteropServices.Marshal.PtrToStringUni(ptr); } return result; } private partial struct Native { [DllImport("AVProVideoWinRT")] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsChangedTextCue(System.IntPtr instance); [DllImport("AVProVideoWinRT")] public static extern System.IntPtr GetCurrentTextCue(System.IntPtr instance); } } public sealed partial class WindowsRtMediaPlayer { private partial struct Native { [DllImport("AVProVideoWinRT", EntryPoint = "GetPluginVersion")] private static extern System.IntPtr GetPluginVersionStringPointer(); public static string GetPluginVersion() { return System.Runtime.InteropServices.Marshal.PtrToStringAnsi(GetPluginVersionStringPointer()); } [DllImport("AVProVideoWinRT")] public static extern System.IntPtr CreatePlayer(); [DllImport("AVProVideoWinRT")] public static extern void DestroyPlayer(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] #if AVPROVIDEO_MARSHAL_RETURN_BOOL [return: MarshalAs(UnmanagedType.I1)] #endif public static extern bool OpenMedia(System.IntPtr playerInstance, [MarshalAs(UnmanagedType.LPWStr)] string filePath, [MarshalAs(UnmanagedType.LPWStr)] string httpHeader, FileFormat overrideFileFormat, bool startWithHighestBitrate, bool use10BitTextures); [DllImport("AVProVideoWinRT")] public static extern void CloseMedia(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern void Pause(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern void Play(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern void SetAudioVolume(System.IntPtr playerInstance, float volume); [DllImport("AVProVideoWinRT")] public static extern void SetAudioBalance(System.IntPtr playerInstance, float balance); [DllImport("AVProVideoWinRT")] public static extern void SetPlaybackRate(System.IntPtr playerInstance, float rate); [DllImport("AVProVideoWinRT")] public static extern void SetAudioMuted(System.IntPtr playerInstance, bool muted); [DllImport("AVProVideoWinRT")] public static extern float GetAudioVolume(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] #if AVPROVIDEO_MARSHAL_RETURN_BOOL [return: MarshalAs(UnmanagedType.I1)] #endif public static extern bool IsAudioMuted(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern float GetAudioBalance(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern float GetPlaybackRate(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern void SetLooping(System.IntPtr playerInstance, bool looping); [DllImport("AVProVideoWinRT")] #if AVPROVIDEO_MARSHAL_RETURN_BOOL [return: MarshalAs(UnmanagedType.I1)] #endif public static extern bool IsLooping(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern int GetLastErrorCode(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern void Update(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern double GetDuration(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern StereoPacking GetStereoPacking(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern double GetCurrentPosition(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] #if AVPROVIDEO_MARSHAL_RETURN_BOOL [return: MarshalAs(UnmanagedType.I1)] #endif public static extern bool GetLatestFrame(System.IntPtr playerInstance, out System.IntPtr leftEyeTexturePointer, out System.IntPtr rightEyeTexturePointer, out ulong frameTimeStamp, out int width, out int height); [DllImport("AVProVideoWinRT")] public static extern PlaybackState GetPlaybackState(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] #if AVPROVIDEO_MARSHAL_RETURN_BOOL [return: MarshalAs(UnmanagedType.I1)] #endif public static extern bool GetActiveVideoTrackInfo(System.IntPtr playerInstance, out VideoTrack videoTrack); [DllImport("AVProVideoWinRT")] #if AVPROVIDEO_MARSHAL_RETURN_BOOL [return: MarshalAs(UnmanagedType.I1)] #endif public static extern bool GetActiveAudioTrackInfo(System.IntPtr playerInstance, out AudioTrack audioTrack); [DllImport("AVProVideoWinRT")] public static extern double GetCurrentDateTimeSecondsSince1970(System.IntPtr playerInstance); [DllImport("AVProVideoWinRT")] public static extern void SetLiveOffset(System.IntPtr playerInstance, double seconds); [DllImport("AVProVideoWinRT")] public static extern void DebugValues(System.IntPtr playerInstance, out int isD3D, out int isUnityD3D, out int isTexture, out int isSharedTexture, out int isSurface); public enum SeekMode { Fast = 0, Accurate = 1, // TODO: Add Fast_Before and Fast_After } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct VideoTrack { public int trackIndex; public int frameWidth; public int frameHeight; public float frameRate; public uint averageBitRate; //public string trackName; // TODO: add index, language, name, bitrate, codec etc } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct AudioTrack { public int trackIndex; public uint channelCount; public uint sampleRate; public uint bitsPerSample; public uint averageBitRate; //public string trackName; // TODO: add index, language, name, bitrate, codec etc } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct SeekParams { public double timeSeconds; public SeekMode mode; // TODO: add min-max thresholds } [DllImport("AVProVideoWinRT")] public static extern void Seek(System.IntPtr playerInstance, ref SeekParams seekParams); public static void SetNextAuthData(System.IntPtr playerInstance, RenderHeads.Media.AVProVideo.AuthData srcAuthData) { Native.AuthData ad = new Native.AuthData(); ad.url = string.IsNullOrEmpty(srcAuthData.URL) ? null : srcAuthData.URL; ad.token = string.IsNullOrEmpty(srcAuthData.Token) ? null : srcAuthData.Token; if (srcAuthData.KeyBytes != null && srcAuthData.KeyBytes.Length > 0) { ad.keyBytes = Marshal.AllocHGlobal(srcAuthData.KeyBytes.Length); Marshal.Copy(srcAuthData.KeyBytes, 0, ad.keyBytes, srcAuthData.KeyBytes.Length); ad.keyBytesLength = srcAuthData.KeyBytes.Length; } else { ad.keyBytes = System.IntPtr.Zero; ad.keyBytesLength = 0; } SetNextAuthData(playerInstance, ref ad); if (ad.keyBytes != System.IntPtr.Zero) { Marshal.FreeHGlobal(ad.keyBytes); } } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct AuthData { [MarshalAs(UnmanagedType.LPWStr)] public string url; [MarshalAs(UnmanagedType.LPWStr)] public string token; public System.IntPtr keyBytes; public int keyBytesLength; }; [DllImport("AVProVideoWinRT")] private static extern void SetNextAuthData(System.IntPtr playerInstance, ref AuthData authData); internal enum TimeRangeTypes { Seekable = 0, Buffered = 1, } [DllImport("AVProVideoWinRT")] public static extern int GetTimeRanges(System.IntPtr playerInstance, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=2)] TimeRange[] ranges, int rangeCount, TimeRangeTypes timeRangeType); // RJT TODO: Clean this up to better match non-WinRT [DllImport("AVProVideoWinRT")] public static extern System.IntPtr GetRenderEventFunc(); private static System.IntPtr _nativeFunction_UnityRenderEvent; public static void IssueRenderThreadEvent_UpdateAllTextures() { if (_nativeFunction_UnityRenderEvent == System.IntPtr.Zero) { _nativeFunction_UnityRenderEvent = Native.GetRenderEventFunc(); } if (_nativeFunction_UnityRenderEvent != System.IntPtr.Zero) { GL.IssuePluginEvent(_nativeFunction_UnityRenderEvent, /*(int)Native.RenderThreadEvent.UpdateAllTextures*/1); } } public static void IssueRenderThreadEvent_FreeAllTextures() { if (_nativeFunction_UnityRenderEvent == System.IntPtr.Zero) { _nativeFunction_UnityRenderEvent = Native.GetRenderEventFunc(); } if (_nativeFunction_UnityRenderEvent != System.IntPtr.Zero) { GL.IssuePluginEvent(_nativeFunction_UnityRenderEvent, /*(int)Native.RenderThreadEvent.FreeTextures*/2); } } } } public sealed partial class WindowsRtMediaPlayer { private static bool _isInitialised = false; private static string _version = "Plug-in not yet initialised"; private ulong _frameTimeStamp; private System.IntPtr _playerInstance; class EyeTexture { public Texture2D texture = null; public System.IntPtr nativePointer = System.IntPtr.Zero; public void Dispose() { if (texture) { if (Application.isPlaying) { Texture2D.Destroy(texture); } else { Texture2D.DestroyImmediate(texture); } texture = null; } nativePointer = System.IntPtr.Zero; } } private EyeTexture[] _eyeTextures = new EyeTexture[2]; public static bool InitialisePlatform() { if (!_isInitialised) { try { #if !UNITY_2019_3_OR_NEWER if (SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D12) { Debug.LogError("[AVProVideo] Direct3D 12 is not supported until Unity 2019.3"); return false; } #endif if (SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Null || SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D11 || SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Direct3D12) { /*if (!Native.Init(QualitySettings.activeColorSpace == ColorSpace.Linear)) { Debug.LogError("[AVProVideo] Failing to initialise platform"); } else*/ { _isInitialised = true; _version = Native.GetPluginVersion(); } } else { Debug.LogError("[AVProVideo] Only Direct3D 11 and 12 are supported, graphicsDeviceType not supported: " + SystemInfo.graphicsDeviceType); } } catch (System.DllNotFoundException e) { Debug.LogError("[AVProVideo] Failed to load DLL. " + e.Message); } } return _isInitialised; } public static void DeinitPlatform() { //Native.Deinit(); _isInitialised = false; } } } #endif