using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; namespace RenderHeads.Media.AVProVideo { public class Resampler { public Resampler(MediaPlayer player, string name, int bufferSize = 2, Resampler.ResampleMode resampleMode = Resampler.ResampleMode.LINEAR) { this._bufferSize = Mathf.Max(2, bufferSize); if (player.Info != null) { this._elapsedTimeSinceBase = (float)this._bufferSize / player.Info.GetVideoFrameRate(); } player.Events.AddListener(new UnityAction(this.OnVideoEvent)); this._mediaPlayer = player; Shader shader = Shader.Find("AVProVideo/BlendFrames"); if (shader != null) { this._blendMat = new Material(shader); this._propT = Shader.PropertyToID("_t"); this._propAfterTex = Shader.PropertyToID("_AfterTex"); } else { Debug.LogError("[AVProVideo] Failed to find BlendFrames shader"); } this._resampleMode = resampleMode; this._name = name; Debug.Log("[AVProVideo] Resampler " + this._name + " started"); } public int DroppedFrames { get { return this._droppedFrames; } } public int FrameDisplayedTimer { get { return this._frameDisplayedTimer; } } public long BaseTimestamp { get { return this._baseTimestamp; } set { this._baseTimestamp = value; } } public float ElapsedTimeSinceBase { get { return this._elapsedTimeSinceBase; } set { this._elapsedTimeSinceBase = value; } } public float LastT { get; private set; } public long TextureTimeStamp { get; private set; } public void OnVideoEvent(MediaPlayer mp, MediaPlayerEvent.EventType et, ErrorCode errorCode) { if (et != MediaPlayerEvent.EventType.MetaDataReady) { if (et == MediaPlayerEvent.EventType.Closing) { this.Reset(); } } else { this._elapsedTimeSinceBase = (float)this._bufferSize / this._mediaPlayer.Info.GetVideoFrameRate(); } } public Texture[] OutputTexture { get { return this._outputTexture; } } public void Reset() { this._lastTimeStamp = -1L; this._baseTimestamp = 0L; this.InvalidateBuffer(); } public void Release() { this.ReleaseRenderTextures(); if (this._blendMat != null) { UnityEngine.Object.Destroy(this._blendMat); } } private void ReleaseRenderTextures() { for (int i = 0; i < this._buffer.Count; i++) { for (int j = 0; j < this._buffer[i].Length; j++) { if (this._buffer[i][j].texture != null) { RenderTexture.ReleaseTemporary(this._buffer[i][j].texture); this._buffer[i][j].texture = null; } } if (this._outputTexture != null && this._outputTexture[i] != null) { RenderTexture.ReleaseTemporary(this._outputTexture[i]); } } this._outputTexture = null; } private void ConstructRenderTextures() { this.ReleaseRenderTextures(); this._buffer.Clear(); this._outputTexture = new RenderTexture[this._mediaPlayer.TextureProducer.GetTextureCount()]; for (int i = 0; i < this._mediaPlayer.TextureProducer.GetTextureCount(); i++) { Texture texture = this._mediaPlayer.TextureProducer.GetTexture(i); this._buffer.Add(new Resampler.TimestampedRenderTexture[this._bufferSize]); for (int j = 0; j < this._bufferSize; j++) { this._buffer[i][j] = new Resampler.TimestampedRenderTexture(); } for (int k = 0; k < this._buffer[i].Length; k++) { this._buffer[i][k].texture = RenderTexture.GetTemporary(texture.width, texture.height, 0); this._buffer[i][k].timestamp = 0L; this._buffer[i][k].used = false; } this._outputTexture[i] = RenderTexture.GetTemporary(texture.width, texture.height, 0); } } private bool CheckRenderTexturesValid() { for (int i = 0; i < this._mediaPlayer.TextureProducer.GetTextureCount(); i++) { Texture texture = this._mediaPlayer.TextureProducer.GetTexture(i); for (int j = 0; j < this._buffer.Count; j++) { if (this._buffer[i][j].texture == null || this._buffer[i][j].texture.width != texture.width || this._buffer[i][j].texture.height != texture.height) { return false; } } if (this._outputTexture == null || this._outputTexture[i] == null || this._outputTexture[i].width != texture.width || this._outputTexture[i].height != texture.height) { return false; } } return true; } private int FindBeforeFrameIndex(int frameIdx) { if (frameIdx >= this._buffer.Count) { return -1; } int num = -1; float num2 = float.MaxValue; int num3 = -1; float num4 = float.MaxValue; for (int i = 0; i < this._buffer[frameIdx].Length; i++) { if (this._buffer[frameIdx][i].used) { float num5 = (float)(this._buffer[frameIdx][i].timestamp - this._baseTimestamp) / 10000000f; if (num5 < num4) { num3 = i; num4 = num5; } float num6 = this._elapsedTimeSinceBase - num5; if (num6 >= 0f && num6 < num2) { num2 = num6; num = i; } } } if (num >= 0) { return num; } if (num3 < 0) { return -1; } return num3; } private int findClosestFrame(int frameIdx) { if (frameIdx >= this._buffer.Count) { return -1; } int result = -1; float num = float.MaxValue; for (int i = 0; i < this._buffer[frameIdx].Length; i++) { if (this._buffer[frameIdx][i].used) { float num2 = (float)(this._buffer[frameIdx][i].timestamp - this._baseTimestamp) / 10000000f; float num3 = Mathf.Abs(this._elapsedTimeSinceBase - num2); if (num3 < num) { result = i; num = num3; } } } return result; } private void PointUpdate() { for (int i = 0; i < this._buffer.Count; i++) { int num = this.findClosestFrame(i); if (num >= 0) { this._outputTexture[i].DiscardContents(); Graphics.Blit(this._buffer[i][num].texture, this._outputTexture[i]); this._currentDisplayedTimestamp = this._buffer[i][num].timestamp; } } } private void SampleFrame(int frameIdx, int bufferIdx) { this._outputTexture[bufferIdx].DiscardContents(); Graphics.Blit(this._buffer[bufferIdx][frameIdx].texture, this._outputTexture[bufferIdx]); this.TextureTimeStamp = this._buffer[bufferIdx][frameIdx].timestamp; this._currentDisplayedTimestamp = this._buffer[bufferIdx][frameIdx].timestamp; } private void SampleFrames(int bufferIdx, int frameIdx1, int frameIdx2, float t) { this._blendMat.SetFloat(this._propT, t); this._blendMat.SetTexture(this._propAfterTex, this._buffer[bufferIdx][frameIdx2].texture); this._outputTexture[bufferIdx].DiscardContents(); Graphics.Blit(this._buffer[bufferIdx][frameIdx1].texture, this._outputTexture[bufferIdx], this._blendMat); this.TextureTimeStamp = (long)Mathf.Lerp((float)this._buffer[bufferIdx][frameIdx1].timestamp, (float)this._buffer[bufferIdx][frameIdx2].timestamp, t); this._currentDisplayedTimestamp = this._buffer[bufferIdx][frameIdx1].timestamp; } private void LinearUpdate() { for (int i = 0; i < this._buffer.Count; i++) { int num = this.FindBeforeFrameIndex(i); if (num >= 0) { float num2 = (float)(this._buffer[i][num].timestamp - this._baseTimestamp) / 10000000f; if (num2 > this._elapsedTimeSinceBase) { this.SampleFrame(num, i); this.LastT = -1f; } else { int num3 = (num + 1) % this._buffer[i].Length; float num4 = (float)(this._buffer[i][num3].timestamp - this._baseTimestamp) / 10000000f; if (num4 < num2) { this.SampleFrame(num, i); this.LastT = 2f; } else { float num5 = num4 - num2; float num6 = (this._elapsedTimeSinceBase - num2) / num5; this.SampleFrames(i, num, num3, num6); this.LastT = num6; } } } } } private void InvalidateBuffer() { this._elapsedTimeSinceBase = (float)(this._bufferSize / 2) / this._mediaPlayer.Info.GetVideoFrameRate(); for (int i = 0; i < this._buffer.Count; i++) { for (int j = 0; j < this._buffer[i].Length; j++) { this._buffer[i][j].used = false; } } this._start = (this._end = 0); } public void Update() { if (this._mediaPlayer.TextureProducer == null) { return; } if (this._mediaPlayer.TextureProducer == null || this._mediaPlayer.TextureProducer.GetTexture(0) == null) { return; } if (!this.CheckRenderTexturesValid()) { this.ConstructRenderTextures(); } long textureTimeStamp = this._mediaPlayer.TextureProducer.GetTextureTimeStamp(); if (textureTimeStamp != this._lastTimeStamp) { float num = Mathf.Abs((float)(textureTimeStamp - this._lastTimeStamp)); float num2 = 10000000f * (1f / this._mediaPlayer.Info.GetVideoFrameRate()); if (num > num2 * 1.1f && num < num2 * 3.1f) { this._droppedFrames += (int)((double)((num - num2) / num2) + 0.5); } this._lastTimeStamp = textureTimeStamp; } long textureTimeStamp2 = this._mediaPlayer.TextureProducer.GetTextureTimeStamp(); bool flag = !this._mediaPlayer.Control.IsSeeking(); if (this._start != this._end || this._buffer[0][this._end].used) { int num3 = (this._end + this._buffer[0].Length - 1) % this._buffer[0].Length; if (textureTimeStamp2 == this._buffer[0][num3].timestamp) { flag = false; } } if (flag) { if (this._start == this._end && !this._buffer[0][this._end].used) { this._baseTimestamp = textureTimeStamp2; } if (this._end == this._start && this._buffer[0][this._end].used) { this._start = (this._start + 1) % this._buffer[0].Length; } for (int i = 0; i < this._mediaPlayer.TextureProducer.GetTextureCount(); i++) { Texture texture = this._mediaPlayer.TextureProducer.GetTexture(i); this._buffer[i][this._end].texture.DiscardContents(); Graphics.Blit(texture, this._buffer[i][this._end].texture); this._buffer[i][this._end].timestamp = textureTimeStamp2; this._buffer[i][this._end].used = true; } this._end = (this._end + 1) % this._buffer[0].Length; } bool flag2 = this._start != this._end || !this._buffer[0][this._end].used; if (flag2) { for (int j = 0; j < this._buffer.Count; j++) { this._outputTexture[j].DiscardContents(); Graphics.Blit(this._buffer[j][this._start].texture, this._outputTexture[j]); this._currentDisplayedTimestamp = this._buffer[j][this._start].timestamp; } } if (this._mediaPlayer.Control.IsPaused()) { this.InvalidateBuffer(); } if (flag2) { return; } if (this._mediaPlayer.Control.IsPlaying() && !this._mediaPlayer.Control.IsFinished()) { long num4 = this._buffer[0][(this._start + this._bufferSize / 2) % this._bufferSize].timestamp - this._baseTimestamp; double num5 = (double)Mathf.Abs((float)((double)this._elapsedTimeSinceBase * 10000000.0) - (float)num4); double num6 = (double)((float)(this._buffer[0].Length / 2) / this._mediaPlayer.Info.GetVideoFrameRate() * 10000000f); if (num5 > num6) { this._elapsedTimeSinceBase = (float)num4 / 10000000f; } if (this._resampleMode == Resampler.ResampleMode.POINT) { this.PointUpdate(); } else if (this._resampleMode == Resampler.ResampleMode.LINEAR) { this.LinearUpdate(); } this._elapsedTimeSinceBase += Time.unscaledDeltaTime; } } public void UpdateTimestamp() { if (this._lastDisplayedTimestamp != this._currentDisplayedTimestamp) { this._lastDisplayedTimestamp = this._currentDisplayedTimestamp; this._frameDisplayedTimer = 0; } this._frameDisplayedTimer++; } private List _buffer = new List(); private MediaPlayer _mediaPlayer; private RenderTexture[] _outputTexture; private int _start; private int _end; private int _bufferSize; private long _baseTimestamp; private float _elapsedTimeSinceBase; private Material _blendMat; private Resampler.ResampleMode _resampleMode; private string _name = string.Empty; private long _lastTimeStamp = -1L; private int _droppedFrames; private long _lastDisplayedTimestamp; private int _frameDisplayedTimer; private long _currentDisplayedTimestamp; private const string ShaderPropT = "_t"; private const string ShaderPropAftertex = "_AfterTex"; private int _propAfterTex; private int _propT; private class TimestampedRenderTexture { public RenderTexture texture; public long timestamp; public bool used; } public enum ResampleMode { POINT, LINEAR } } }