using RenderHeads.Media.AVProVideo; using System; using System.Runtime.InteropServices; using UnityEngine; namespace Assets.AR { public class AVProVideoPlayer : MonoBehaviour { private const float DefaultFps = 29.97f; public MediaPlayer videoPlayer; private Vector2Int videoResolution = Vector2Int.zero; private Vector2Int textureResolution = Vector2Int.zero; private int displacement; private bool interpolate; private bool useFlow; private bool pausedByTrainingPause = false; private bool pausedByPlaybackSpeed = true; private RenderTexture currentTexture; private RenderTexture previousTexture; private bool switchBuffer = true; private bool canSwitchBuffer = true; private int previousTextureFrameCount; private float previousVideoTimeMs; private float shift; private DateTime lastPlaybackSpeedChange = DateTime.MinValue; private float localCurrentFrame; private bool intendedRear; private void Awake() { //this.videoPlayer = this.gameObject.AddComponent(); //this.videoPlayer.PlatformOptionsAndroid.videoApi = RenderHeads.Media.AVProVideo.Android.VideoApi.MediaPlayer; //this.videoPlayer.PlatformOptionsAndroid.preferSoftwareDecoder = true; //GameObject primitive = GameObject.CreatePrimitive(PrimitiveType.Quad); //primitive.name = "VideoMesh"; //primitive.layer = 9; //UnityEngine.Object.Destroy((UnityEngine.Object)primitive.GetComponent()); //this.VideoRenderer = (Renderer)primitive.GetComponent(); //primitive.transform.parent = this.transform; //this.VideoRenderer.material = new Material(Shader.Find("Rouvy/FrameInterpolation")); } private void OnDisable() => this.ReleaseRenderTextures(); private void OnDestroy() => UnityEngine.Object.Destroy((UnityEngine.Object)this.VideoRenderer?.material); public bool Initialized => true; public float CurrentFrame => (double)this.localCurrentFrame >= (double)this.StreamFrameCount ? this.localCurrentFrame - (float)this.StreamFrameCount : this.localCurrentFrame; public bool IntendedRear { get => this.intendedRear; set { if (this.intendedRear == value) return; this.intendedRear = value; this.Seek((long)(int)this.CurrentFrame, true); } } public bool IsRear => (double)this.localCurrentFrame >= (double)this.StreamFrameCount; public int StreamFrameCount { get; set; } public void UpdateCurrentFrame() { this.localCurrentFrame = (float)(this.videoPlayer.Control.GetCurrentTime() * 29.97f); } public float FrameRate { get; private set; } = 29.97f; public bool HasOpticalFlow { get; private set; } public float PlaybackSpeed => this.videoPlayer.Control.GetPlaybackRate(); public Renderer VideoRenderer { get; private set; } public bool IsPaused => this.pausedByTrainingPause; public bool IsSeeking => this.videoPlayer.Control.IsSeeking(); private bool Interpolate { get => this.interpolate; set { if (this.interpolate == value) return; this.interpolate = value; this.SetShaderKeywords(); } } private bool UseFlow { get => this.useFlow; set { if (this.useFlow == value) return; this.useFlow = value; this.SetShaderKeywords(); } } public void OpenVideo(string videoUrl, int streamFramesCount) { this.StreamFrameCount = streamFramesCount; this.StopVideo(); //this.videoPlayer.OpenVideoFromFile(MediaPlayer.FileLocation.AbsolutePathOrURL, videoUrl); } public void Pause() { this.videoPlayer.Pause(); this.pausedByTrainingPause = true; } public void Resume() { if (!this.pausedByPlaybackSpeed) this.videoPlayer.Play(); this.pausedByTrainingPause = false; } public void StopVideo() { this.videoPlayer.Stop(); this.videoPlayer.CloseMedia(); this.ReleaseRenderTextures(); } private float pauseSpeed = 0.001f; public bool SetPlaybackSpeed(float playbackSpeed) { if (this.IsSeeking || this.IsPaused || DateTime.UtcNow.Subtract(this.lastPlaybackSpeedChange).TotalSeconds < 1.0) return false; if ((double)playbackSpeed <= pauseSpeed && !this.pausedByPlaybackSpeed) { this.pausedByPlaybackSpeed = true; this.videoPlayer.Pause(); } else if ((double)playbackSpeed > pauseSpeed && this.pausedByPlaybackSpeed) { this.pausedByPlaybackSpeed = false; if (!this.pausedByTrainingPause) this.videoPlayer.Play(); } this.videoPlayer.PlaybackRate = playbackSpeed; this.lastPlaybackSpeedChange = DateTime.UtcNow; return true; } public bool Seek(long frame, bool force = false) { if (this.IntendedRear) frame += (long)this.StreamFrameCount; if (!this.Initialized || this.IsSeeking && !force) return false; this.videoPlayer.Control.SeekToFrame((int)frame); return true; } private void Initialize() { if (this.Initialized) return; this.VideoPrepareAction(); } private void LateUpdate() { if (!this.Initialized) { this.Initialize(); } else { this.previousVideoTimeMs = (float)this.videoPlayer.Control.GetCurrentTime(); if (!this.pausedByTrainingPause && this.videoPlayer.Control.IsPaused() && !this.pausedByPlaybackSpeed) this.videoPlayer.Play(); if (!this.pausedByTrainingPause && !this.pausedByPlaybackSpeed || this.videoPlayer.Control.IsPaused()) return; this.videoPlayer.Pause(); } } private void CheckAndPauseByPlaybackSpeed() { if ((double)this.videoPlayer.Control.GetPlaybackRate() != 0.0 || this.pausedByPlaybackSpeed) return; this.pausedByPlaybackSpeed = true; this.videoPlayer.Pause(); } private void VideoPrepareAction() { this.FrameRate = 29.97f; this.videoPlayer.Control.SetTextureProperties(anisoLevel: 0); if (Math.Abs(this.videoPlayer.Info.GetVideoWidth() - 1688) < 10) { this.videoResolution = new Vector2Int(1280, 720); this.displacement = 40; this.HasOpticalFlow = true; } else if (Math.Abs(this.videoPlayer.Info.GetVideoWidth() - 2528) < 10) { this.videoResolution = new Vector2Int(1920, 1080); this.displacement = 64; this.HasOpticalFlow = true; } else if (Math.Abs(this.videoPlayer.Info.GetVideoWidth() - 3372) < 10) { this.videoResolution = new Vector2Int(2560, 1440); this.displacement = 128; this.HasOpticalFlow = true; } else if (Math.Abs(this.videoPlayer.Info.GetVideoWidth() - 1264) < 10) { this.videoResolution = new Vector2Int(960, 540); this.HasOpticalFlow = true; this.displacement = 32; } else { this.videoResolution = new Vector2Int(this.videoPlayer.Info.GetVideoWidth(), this.videoPlayer.Info.GetVideoHeight()); this.HasOpticalFlow = false; } this.Interpolate = this.UseFlow = this.HasOpticalFlow; this.videoPlayer.Play(); if (!this.pausedByTrainingPause) return; this.videoPlayer.Pause(); } private void ReleaseRenderTextures() { if ((UnityEngine.Object)this.currentTexture != (UnityEngine.Object)null) { this.currentTexture.Release(); this.currentTexture = (RenderTexture)null; } if (!((UnityEngine.Object)this.previousTexture != (UnityEngine.Object)null)) return; this.previousTexture.Release(); this.previousTexture = (RenderTexture)null; } private void SetShaderKeywords() { if (this.Interpolate) this.VideoRenderer.material.EnableKeyword(AVProVideoPlayer.ShaderKeyword.interpolate); else this.VideoRenderer.material.DisableKeyword(AVProVideoPlayer.ShaderKeyword.interpolate); if (this.UseFlow && this.videoResolution != this.textureResolution) this.VideoRenderer.material.EnableKeyword(AVProVideoPlayer.ShaderKeyword.useFlow); else this.VideoRenderer.material.DisableKeyword(AVProVideoPlayer.ShaderKeyword.useFlow); } [StructLayout(LayoutKind.Sequential, Size = 1)] private struct ShaderID { public static int previousFrame = Shader.PropertyToID("_PreviousFrame"); public static int currentFrame = Shader.PropertyToID("_CurrentFrame"); public static int displacement = Shader.PropertyToID("_MaxDisplacement"); public static int shift = Shader.PropertyToID("_Shift"); public static int flip = Shader.PropertyToID("_Flip"); public static int videoResolution = Shader.PropertyToID("_VideoResolution"); public static int textureResolution = Shader.PropertyToID("_TextureResolution"); public static int screenResolution = Shader.PropertyToID("_ScreenResolution"); } [StructLayout(LayoutKind.Sequential, Size = 1)] private struct ShaderKeyword { public static string interpolate = "INTERPOLATE"; public static string useFlow = "USE_FLOW"; } } }