#define DLL_METHODS #if UNITY_ANDROID #if UNITY_5 || UNITY_5_4_OR_NEWER #if !UNITY_5_0 && !UNITY_5_1 #define AVPROVIDEO_ISSUEPLUGINEVENT_UNITY52 #endif #if !UNITY_5_0 && !UNITY_5_1 && !UNITY_5_2 && !UNITY_5_3 && !UNITY_5_4_0 && !UNITY_5_4_1 #define AVPROVIDEO_FIXREGRESSION_TEXTUREQUALITY_UNITY542 #endif #endif using UnityEngine; using System; using System.Runtime.InteropServices; //----------------------------------------------------------------------------- // Copyright 2015-2018 RenderHeads Ltd. All rights reserverd. //----------------------------------------------------------------------------- namespace RenderHeads.Media.AVProVideo { /// /// Android implementation of BaseMediaPlayer /// // TODO: seal this class public class AndroidMediaPlayer : BaseMediaPlayer { protected static AndroidJavaObject s_ActivityContext = null; protected static AndroidJavaObject s_Interface = null; protected static bool s_bInitialised = false; private static string s_Version = "Plug-in not yet initialised"; #if AVPROVIDEO_ISSUEPLUGINEVENT_UNITY52 private static System.IntPtr _nativeFunction_RenderEvent; #endif protected AndroidJavaObject m_Video; private Texture2D m_Texture; private int m_TextureHandle; private bool m_UseFastOesPath; private float m_DurationMs = 0.0f; private int m_Width = 0; private int m_Height = 0; protected int m_iPlayerIndex = -1; private Android.VideoApi m_API; private bool m_HeadRotationEnabled = false; private bool m_FocusEnabled = false; private System.IntPtr m_Method_SetHeadRotation; private System.IntPtr m_Method_GetCurrentTimeMs; private System.IntPtr m_Method_GetSourceVideoFrameRate; private System.IntPtr m_Method_IsPlaying; private System.IntPtr m_Method_IsPaused; private System.IntPtr m_Method_IsFinished; private System.IntPtr m_Method_IsSeeking; private System.IntPtr m_Method_IsBuffering; private System.IntPtr m_Method_IsLooping; private System.IntPtr m_Method_HasVideo; private System.IntPtr m_Method_HasAudio; private System.IntPtr m_Method_SetFocusProps; private System.IntPtr m_Method_SetFocusEnabled; private System.IntPtr m_Method_SetFocusRotation; private jvalue[] m_Value0 = new jvalue[0]; private jvalue[] m_Value1 = new jvalue[1]; private jvalue[] m_Value2 = new jvalue[2]; private jvalue[] m_Value4 = new jvalue[4]; #if AVPROVIDEO_FIXREGRESSION_TEXTUREQUALITY_UNITY542 private int _textureQuality = QualitySettings.masterTextureLimit; #endif public static bool InitialisePlatform() { // Get the activity context if( s_ActivityContext == null ) { AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); if (activityClass != null) { s_ActivityContext = activityClass.GetStatic("currentActivity"); } } if( !s_bInitialised ) { s_bInitialised = true; s_Interface = new AndroidJavaObject("com.RenderHeads.AVProVideo.AVProMobileVideo"); if(s_Interface != null ) { s_Version = s_Interface.Call("GetPluginVersion"); s_Interface.Call("SetContext", s_ActivityContext); #if AVPROVIDEO_ISSUEPLUGINEVENT_UNITY52 _nativeFunction_RenderEvent = Native.GetRenderEventFunc(); #else // Calling this native function cause the .SO library to become loaded // This is important for Unity < 5.2.0 where GL.IssuePluginEvent works differently Native.GetRenderEventFunc(); #endif } else { s_bInitialised = false; } } return s_bInitialised; } private static void IssuePluginEvent(Native.AVPPluginEvent type, int param) { // Build eventId from the type and param. int eventId = 0x5d5ac000 | ((int)type << 8); switch (type) { case Native.AVPPluginEvent.PlayerSetup: case Native.AVPPluginEvent.PlayerUpdate: case Native.AVPPluginEvent.PlayerDestroy: case Native.AVPPluginEvent.ExtractFrame: { eventId |= param & 0xff; } break; } #if AVPROVIDEO_ISSUEPLUGINEVENT_UNITY52 GL.IssuePluginEvent(_nativeFunction_RenderEvent, eventId); #else GL.IssuePluginEvent(eventId); #endif } public AndroidMediaPlayer(bool useFastOesPath, bool showPosterFrame, Android.VideoApi api, bool enable360Audio, Audio360ChannelMode channelMode, bool preferSoftware) { m_API = api; // Create a java-size video class up front m_Video = s_Interface.Call("CreatePlayer", (int)m_API, useFastOesPath, enable360Audio, (int)channelMode, preferSoftware); if (m_Video != null) { m_Method_SetHeadRotation = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "SetHeadRotation", "(FFFF)V", false); m_Method_SetFocusProps = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "SetFocusProps", "(FF)V", false); m_Method_SetFocusEnabled = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "SetFocusEnabled", "(Z)V", false); m_Method_SetFocusRotation = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "SetFocusRotation", "(FFFF)V", false); m_Method_GetCurrentTimeMs = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "GetCurrentTimeMs", "()J", false); m_Method_GetSourceVideoFrameRate = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "GetSourceVideoFrameRate", "()F", false); m_Method_IsPlaying = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "IsPlaying", "()Z", false); m_Method_IsPaused = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "IsPaused", "()Z", false); m_Method_IsFinished = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "IsFinished", "()Z", false); m_Method_IsSeeking = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "IsSeeking", "()Z", false); m_Method_IsBuffering = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "IsBuffering", "()Z", false); m_Method_IsLooping = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "IsLooping", "()Z", false); m_Method_HasVideo = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "HasVideo", "()Z", false); m_Method_HasAudio = AndroidJNIHelper.GetMethodID(m_Video.GetRawClass(), "HasAudio", "()Z", false); m_iPlayerIndex = m_Video.Call("GetPlayerIndex"); Helper.LogInfo("Creating player " + m_iPlayerIndex); //Debug.Log( "AVPro: useFastOesPath: " + useFastOesPath ); SetOptions(useFastOesPath, showPosterFrame); // Initialise renderer, on the render thread AndroidMediaPlayer.IssuePluginEvent( Native.AVPPluginEvent.PlayerSetup, m_iPlayerIndex ); } } public void SetOptions(bool useFastOesPath, bool showPosterFrame) { m_UseFastOesPath = useFastOesPath; if (m_Video != null) { m_Video.Call("SetPlayerOptions", m_UseFastOesPath, showPosterFrame); } } public override long GetEstimatedTotalBandwidthUsed() { long result = -1; if (s_Interface != null) { result = m_Video.Call("GetEstimatedBandwidthUsed"); } return result; } public override string GetVersion() { return s_Version; } public override bool OpenVideoFromFile(string path, long offset, string httpHeaderJson, uint sourceSamplerate = 0, uint sourceChannels = 0, int forceFileFormat = 0) { bool bReturn = false; if( m_Video != null ) { #if UNITY_5 || UNITY_5_4_OR_NEWER Debug.Assert(m_Width == 0 && m_Height == 0 && m_DurationMs == 0.0f); #endif bReturn = m_Video.Call("OpenVideoFromFile", path, offset, httpHeaderJson, forceFileFormat); } return bReturn; } public override TimeRange[] GetSeekableTimeRanges() { float[] rangeArray = m_Video.Call("GetSeekableTimeRange"); TimeRange[] result = new TimeRange[1]; result[0].startTime = rangeArray[0]; result[0].duration = rangeArray[1] - rangeArray[0]; return result; } public override void CloseVideo() { if (m_Texture != null) { Texture2D.Destroy(m_Texture); m_Texture = null; } m_TextureHandle = 0; m_DurationMs = 0.0f; m_Width = 0; m_Height = 0; m_Video.Call("CloseVideo"); base.CloseVideo(); } public override void SetLooping( bool bLooping ) { if( m_Video != null ) { m_Video.Call("SetLooping", bLooping); } } public override bool IsLooping() { bool result = false; if( m_Video != null ) { if (m_Method_IsLooping != System.IntPtr.Zero) { result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsLooping, m_Value0); } else { result = m_Video.Call("IsLooping"); } } return result; } public override bool HasVideo() { bool result = false; if( m_Video != null ) { if (m_Method_HasVideo != System.IntPtr.Zero) { result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_HasVideo, m_Value0); } else { result = m_Video.Call("HasVideo"); } } return result; } public override bool HasAudio() { bool result = false; if( m_Video != null ) { if (m_Method_HasAudio != System.IntPtr.Zero) { result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_HasAudio, m_Value0); } else { result = m_Video.Call("HasAudio"); } } return result; } public override bool HasMetaData() { bool result = false; if( m_DurationMs > 0.0f ) { result = true; if( HasVideo() ) { result = ( m_Width > 0 && m_Height > 0 ); } } return result; } public override bool CanPlay() { bool result = false; #if DLL_METHODS result = Native._CanPlay( m_iPlayerIndex ); #else if (m_Video != null) { result = m_Video.Call("CanPlay"); } #endif return result; } public override void Play() { if (m_Video != null) { m_Video.Call("Play"); } } public override void Pause() { if (m_Video != null) { m_Video.Call("Pause"); } } public override void Stop() { if (m_Video != null) { // On Android we never need to actually Stop the playback, pausing is fine m_Video.Call("Pause"); } } public override void Rewind() { Seek( 0.0f ); } public override void Seek(float timeMs) { if (m_Video != null) { m_Video.Call("Seek", Mathf.FloorToInt(timeMs)); } } public override void SeekFast(float timeMs) { if (m_Video != null) { m_Video.Call("SeekFast", Mathf.FloorToInt(timeMs)); } } public override float GetCurrentTimeMs() { float result = 0.0f; if (m_Video != null) { if (m_Method_GetCurrentTimeMs != System.IntPtr.Zero) { result = AndroidJNI.CallLongMethod(m_Video.GetRawObject(), m_Method_GetCurrentTimeMs, m_Value0); } else { result = (float)m_Video.Call("GetCurrentTimeMs"); } } return result; } public override void SetPlaybackRate(float rate) { if (m_Video != null) { m_Video.Call("SetPlaybackRate", rate); } } public override float GetPlaybackRate() { float result = 0.0f; if (m_Video != null) { result = m_Video.Call("GetPlaybackRate"); } return result; } public override void SetAudioHeadRotation(Quaternion q) { if (m_Video != null) { if (!m_HeadRotationEnabled) { m_Video.Call("SetPositionTrackingEnabled", true); m_HeadRotationEnabled = true; } if (m_Method_SetHeadRotation != System.IntPtr.Zero) { m_Value4[0].f = q.x; m_Value4[1].f = q.y; m_Value4[2].f = q.z; m_Value4[3].f = q.w; AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetHeadRotation, m_Value4); } else { m_Video.Call("SetHeadRotation", q.x, q.y, q.z, q.w); } } } public override void ResetAudioHeadRotation() { if(m_Video != null && m_HeadRotationEnabled) { m_Video.Call("SetPositionTrackingEnabled", false); m_HeadRotationEnabled = false; } } public override void SetAudioFocusEnabled(bool enabled) { if (m_Video != null && enabled != m_FocusEnabled) { if (m_Method_SetFocusEnabled != System.IntPtr.Zero) { m_Value1[0].z = enabled; AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusEnabled, m_Value1); } else { m_Video.Call("SetFocusEnabled", enabled); } m_FocusEnabled = enabled; } } public override void SetAudioFocusProperties(float offFocusLevel, float widthDegrees) { if(m_Video != null && m_FocusEnabled) { if (m_Method_SetFocusProps != System.IntPtr.Zero) { m_Value2[0].f = offFocusLevel; m_Value2[1].f = widthDegrees; AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusProps, m_Value2); } else { m_Video.Call("SetFocusProps", offFocusLevel, widthDegrees); } } } public override void SetAudioFocusRotation(Quaternion q) { if (m_Video != null && m_FocusEnabled) { if (m_Method_SetFocusRotation != System.IntPtr.Zero) { m_Value4[0].f = q.x; m_Value4[1].f = q.y; m_Value4[2].f = q.z; m_Value4[3].f = q.w; AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusRotation, m_Value4); } else { m_Video.Call("SetFocusRotation", q.x, q.y, q.z, q.w); } } } public override void ResetAudioFocus() { if (m_Video != null) { if (m_Method_SetFocusProps != System.IntPtr.Zero && m_Method_SetFocusEnabled != System.IntPtr.Zero && m_Method_SetFocusRotation != System.IntPtr.Zero) { m_Value2[0].f = 0f; m_Value2[1].f = 90f; AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusProps, m_Value2); m_Value1[0].z = false; AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusEnabled, m_Value1); m_Value4[0].f = 0f; m_Value4[1].f = 0f; m_Value4[2].f = 0f; m_Value4[3].f = 1f; AndroidJNI.CallVoidMethod(m_Video.GetRawObject(), m_Method_SetFocusRotation, m_Value4); } else { m_Video.Call("SetFocusProps", 0f, 90f); m_Video.Call("SetFocusEnabled", false); m_Video.Call("SetFocusRotation", 0f, 0f, 0f, 1f); } } } public override float GetDurationMs() { return m_DurationMs; } public override int GetVideoWidth() { return m_Width; } public override int GetVideoHeight() { return m_Height; } public override float GetVideoFrameRate() { float result = 0.0f; if( m_Video != null ) { if (m_Method_GetSourceVideoFrameRate != System.IntPtr.Zero) { result = AndroidJNI.CallFloatMethod(m_Video.GetRawObject(), m_Method_GetSourceVideoFrameRate, m_Value0); } else { result = m_Video.Call("GetSourceVideoFrameRate"); } } return result; } public override float GetBufferingProgress() { float result = 0.0f; if( m_Video != null ) { result = m_Video.Call("GetBufferingProgressPercent") * 0.01f; } return result; } public override float GetVideoDisplayRate() { float result = 0.0f; #if DLL_METHODS result = Native._GetVideoDisplayRate( m_iPlayerIndex ); #else if (m_Video != null) { result = m_Video.Call("GetDisplayRate"); } #endif return result; } public override bool IsSeeking() { bool result = false; if (m_Video != null) { if (m_Method_IsSeeking != System.IntPtr.Zero) { result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsSeeking, m_Value0); } else { result = m_Video.Call("IsSeeking"); } } return result; } public override bool IsPlaying() { bool result = false; if (m_Video != null) { if (m_Method_IsPlaying != System.IntPtr.Zero) { result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsPlaying, m_Value0); } else { result = m_Video.Call("IsPlaying"); } } return result; } public override bool IsPaused() { bool result = false; if (m_Video != null) { if (m_Method_IsPaused != System.IntPtr.Zero) { result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsPaused, m_Value0); } else { result = m_Video.Call("IsPaused"); } } return result; } public override bool IsFinished() { bool result = false; if (m_Video != null) { if (m_Method_IsFinished != System.IntPtr.Zero) { result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsFinished, m_Value0); } else { result = m_Video.Call("IsFinished"); } } return result; } public override bool IsBuffering() { bool result = false; if (m_Video != null) { if (m_Method_IsBuffering != System.IntPtr.Zero) { result = AndroidJNI.CallBooleanMethod(m_Video.GetRawObject(), m_Method_IsBuffering, m_Value0); } else { result = m_Video.Call("IsBuffering"); } } return result; } public override Texture GetTexture( int index ) { Texture result = null; if (GetTextureFrameCount() > 0) { result = m_Texture; } return result; } public override int GetTextureFrameCount() { int result = 0; #if DLL_METHODS result = Native._GetFrameCount( m_iPlayerIndex ); #else if (m_Video != null) { result = m_Video.Call("GetFrameCount"); } #endif return result; } public override bool RequiresVerticalFlip() { return false; } public override void MuteAudio(bool bMuted) { if (m_Video != null) { m_Video.Call("MuteAudio", bMuted); } } public override bool IsMuted() { bool result = false; if( m_Video != null ) { result = m_Video.Call("IsMuted"); } return result; } public override void SetVolume(float volume) { if (m_Video != null) { m_Video.Call("SetVolume", volume); } } public override float GetVolume() { float result = 0.0f; if( m_Video != null ) { result = m_Video.Call("GetVolume"); } return result; } public override void SetBalance(float balance) { if( m_Video != null ) { m_Video.Call("SetAudioPan", balance); } } public override float GetBalance() { float result = 0.0f; if( m_Video != null ) { result = m_Video.Call("GetAudioPan"); } return result; } public override int GetAudioTrackCount() { int result = 0; if( m_Video != null ) { result = m_Video.Call("GetNumberAudioTracks"); } return result; } public override int GetCurrentAudioTrack() { int result = 0; if( m_Video != null ) { result = m_Video.Call("GetCurrentAudioTrackIndex"); } return result; } public override void SetAudioTrack( int index ) { if( m_Video != null ) { m_Video.Call("SetAudioTrack", index); } } public override string GetCurrentAudioTrackId() { /*string id = ""; if( m_Video != null ) { id = m_Video.Call("GetCurrentAudioTrackIndex"); } return id;*/ return GetCurrentAudioTrack().ToString(); } public override int GetCurrentAudioTrackBitrate() { int result = 0; /*if( m_Video != null ) { result = m_Video.Call("GetCurrentAudioTrackIndex"); }*/ return result; } public override int GetVideoTrackCount() { int result = 0; if( m_Video != null ) { if (HasVideo()) { result = 1; } //result = m_Video.Call("GetNumberVideoTracks"); } return result; } public override int GetCurrentVideoTrack() { int result = 0; /*if( m_Video != null ) { result = m_Video.Call("GetCurrentVideoTrackIndex"); }*/ return result; } public override void SetVideoTrack( int index ) { /*if( m_Video != null ) { m_Video.Call("SetVideoTrack", index); }*/ } public override string GetCurrentVideoTrackId() { string id = ""; /*if( m_Video != null ) { id = m_Video.Call("GetCurrentVideoTrackId"); }*/ return id; } public override int GetCurrentVideoTrackBitrate() { int bitrate = 0; /*if( m_Video != null ) { bitrate = m_Video.Call("GetCurrentVideoTrackBitrate"); }*/ return bitrate; } public override bool WaitForNextFrame(Camera dummyCamera, int previousFrameCount) { // Mark as extracting bool isMultiThreaded = m_Video.Call("StartExtractFrame"); // In single threaded Android this method won't work, so just return if (isMultiThreaded) { // Queue up render thread event to wait for the new frame IssuePluginEvent(Native.AVPPluginEvent.ExtractFrame, m_iPlayerIndex); // Force render thread to run dummyCamera.Render(); // Wait for the frame to change m_Video.Call("WaitForExtract"); // Return whether the frame changed return (previousFrameCount != GetTextureFrameCount()); } return false; } public override long GetTextureTimeStamp() { long timeStamp = long.MinValue; if (m_Video != null) { timeStamp = m_Video.Call("GetTextureTimeStamp"); } return timeStamp; } public override void Render() { if (m_Video != null) { if (m_UseFastOesPath) { // This is needed for at least Unity 5.5.0, otherwise it just renders black in OES mode GL.InvalidateState(); } AndroidMediaPlayer.IssuePluginEvent( Native.AVPPluginEvent.PlayerUpdate, m_iPlayerIndex ); if (m_UseFastOesPath) { GL.InvalidateState(); } // Check if we can create the texture // Scan for a change in resolution int newWidth = -1; int newHeight = -1; if (m_Texture != null) { #if DLL_METHODS newWidth = Native._GetWidth( m_iPlayerIndex ); newHeight = Native._GetHeight( m_iPlayerIndex ); #else newWidth = m_Video.Call("GetWidth"); newHeight = m_Video.Call("GetHeight"); #endif if (newWidth != m_Width || newHeight != m_Height) { m_Texture = null; m_TextureHandle = 0; } } #if DLL_METHODS int textureHandle = Native._GetTextureHandle( m_iPlayerIndex ); #else int textureHandle = m_Video.Call("GetTextureHandle"); #endif if (textureHandle != 0 && textureHandle != m_TextureHandle ) { // Already got? (from above) if( newWidth == -1 || newHeight == -1 ) { #if DLL_METHODS newWidth = Native._GetWidth( m_iPlayerIndex ); newHeight = Native._GetHeight( m_iPlayerIndex ); #else newWidth = m_Video.Call("GetWidth"); newHeight = m_Video.Call("GetHeight"); #endif } if (Mathf.Max(newWidth, newHeight) > SystemInfo.maxTextureSize) { m_Width = newWidth; m_Height = newHeight; m_TextureHandle = textureHandle; Debug.LogError("[AVProVideo] Video dimensions larger than maxTextureSize"); } else if( newWidth > 0 && newHeight > 0 ) { m_Width = newWidth; m_Height = newHeight; m_TextureHandle = textureHandle; switch(m_API) { case Android.VideoApi.MediaPlayer: _playerDescription = "MediaPlayer"; break; case Android.VideoApi.ExoPlayer: _playerDescription = "ExoPlayer"; break; default: _playerDescription = "UnknownPlayer"; break; } Helper.LogInfo("Using playback path: " + _playerDescription + " (" + m_Width + "x" + m_Height + "@" + GetVideoFrameRate().ToString("F2") + ")"); // NOTE: From Unity 5.4.x when using OES textures, an error "OPENGL NATIVE PLUG-IN ERROR: GL_INVALID_OPERATION: Operation illegal in current state" will be logged. // We assume this is because we're passing in TextureFormat.RGBA32 which isn't the true texture format. This error should be safe to ignore. m_Texture = Texture2D.CreateExternalTexture(m_Width, m_Height, TextureFormat.RGBA32, false, false, new System.IntPtr(textureHandle)); if (m_Texture != null) { ApplyTextureProperties(m_Texture); } Helper.LogInfo("Texture ID: " + textureHandle); } } #if AVPROVIDEO_FIXREGRESSION_TEXTUREQUALITY_UNITY542 // In Unity 5.4.2 and above the video texture turns black when changing the TextureQuality in the Quality Settings // The code below gets around this issue. A bug report has been sent to Unity. So far we have tested and replicated the // "bug" in Windows only, but a user has reported it in Android too. // Texture.GetNativeTexturePtr() must sync with the rendering thread, so this is a large performance hit! if (_textureQuality != QualitySettings.masterTextureLimit) { if (m_Texture != null && textureHandle > 0 && m_Texture.GetNativeTexturePtr() == System.IntPtr.Zero) { //Debug.Log("RECREATING"); m_Texture.UpdateExternalTexture(new System.IntPtr(textureHandle)); } _textureQuality = QualitySettings.masterTextureLimit; } #endif } } protected override void ApplyTextureProperties(Texture texture) { // NOTE: According to OES_EGL_image_external: For external textures, the default min filter is GL_LINEAR and the default S and T wrap modes are GL_CLAMP_TO_EDGE // See https://www.khronos.org/registry/gles/extensions/OES/OES_EGL_image_external_essl3.txt if (!m_UseFastOesPath) { base.ApplyTextureProperties(texture); } } public override void OnEnable() { base.OnEnable(); #if DLL_METHODS int textureHandle = Native._GetTextureHandle(m_iPlayerIndex); #else int textureHandle = m_Video.Call("GetTextureHandle"); #endif if (m_Texture != null && textureHandle > 0 && m_Texture.GetNativeTexturePtr() == System.IntPtr.Zero) { //Debug.Log("RECREATING"); m_Texture.UpdateExternalTexture(new System.IntPtr(textureHandle)); } #if AVPROVIDEO_FIXREGRESSION_TEXTUREQUALITY_UNITY542 _textureQuality = QualitySettings.masterTextureLimit; #endif } public override double GetCurrentDateTimeSecondsSince1970() { double result = 0.0; if (m_Video != null) { result = m_Video.Call("GetCurrentAbsoluteTimestamp"); } return result; } public override void Update() { if (m_Video != null) { // _lastError = (ErrorCode)( m_Video.Call("GetLastErrorCode") ); _lastError = (ErrorCode)( Native._GetLastErrorCode( m_iPlayerIndex) ); } UpdateSubtitles(); if(Mathf.Approximately(m_DurationMs, 0f)) { #if DLL_METHODS m_DurationMs = (float)( Native._GetDuration( m_iPlayerIndex ) ); #else m_DurationMs = (float)(m_Video.Call("GetDurationMs")); #endif // if( m_DurationMs > 0.0f ) { Helper.LogInfo("Duration: " + m_DurationMs); } } } public override bool PlayerSupportsLinearColorSpace() { return false; } public override void Dispose() { //Debug.LogError("DISPOSE"); // Deinitialise player (replaces call directly as GL textures are involved) AndroidMediaPlayer.IssuePluginEvent( Native.AVPPluginEvent.PlayerDestroy, m_iPlayerIndex ); if (m_Video != null) { m_Video.Call("SetDeinitialiseFlagged"); m_Video.Dispose(); m_Video = null; } if (m_Texture != null) { Texture2D.Destroy(m_Texture); m_Texture = null; } } private struct Native { [DllImport ("AVProLocal")] public static extern IntPtr GetRenderEventFunc(); [DllImport ("AVProLocal")] public static extern int _GetWidth( int iPlayerIndex ); [DllImport ("AVProLocal")] public static extern int _GetHeight( int iPlayerIndex ); [DllImport ("AVProLocal")] public static extern int _GetTextureHandle( int iPlayerIndex ); [DllImport ("AVProLocal")] public static extern long _GetDuration( int iPlayerIndex ); [DllImport ("AVProLocal")] public static extern int _GetLastErrorCode( int iPlayerIndex ); [DllImport ("AVProLocal")] public static extern int _GetFrameCount( int iPlayerIndex ); [DllImport ("AVProLocal")] public static extern float _GetVideoDisplayRate( int iPlayerIndex ); [DllImport ("AVProLocal")] public static extern bool _CanPlay( int iPlayerIndex ); public enum AVPPluginEvent { Nop, PlayerSetup, PlayerUpdate, PlayerDestroy, ExtractFrame, } } } } #endif