123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- // 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))
- #define AVPROVIDEO_FIXREGRESSION_TEXTUREQUALITY_UNITY542
- #if UNITY_WSA_10 || ENABLE_IL2CPP
- #define AVPROVIDEO_MARSHAL_RETURN_BOOL
- #endif
- #if UNITY_2019_3_OR_NEWER && !UNITY_2020_1_OR_NEWER
- #define AVPROVIDEO_FIX_UPDATEEXTERNALTEXTURE_LEAK
- #endif
- using UnityEngine;
- using System.Runtime.InteropServices;
- using System.Collections.Generic;
- using System;
- using System.Text;
- #if NETFX_CORE
- using Windows.Storage.Streams;
- #endif
- //-----------------------------------------------------------------------------
- // Copyright 2015-2021 RenderHeads Ltd. All rights reserved.
- //-----------------------------------------------------------------------------
- namespace RenderHeads.Media.AVProVideo
- {
- /// <summary>
- /// Windows desktop and UWP implementation of BaseMediaPlayer
- /// </summary>
- public /*sealed*/ partial class WindowsMediaPlayer : BaseMediaPlayer
- {
- #region IBufferedDisplay Implementation
- private BufferedFrameSelectionMode _frameSelectionMode = BufferedFrameSelectionMode.None;
- private bool _pauseOnPrerollComplete = false;
- private IBufferedDisplay _masterDisplay;
- private IBufferedDisplay[] _slaveDisplays;
- private double _displayClockTime = 0.0;
- private double _timeAccumulation = 0.0;
- private bool _needsInitialFrame = true;
- private void FlushFrameBuffering(bool releaseTexture)
- {
- if (_frameSelectionMode == BufferedFrameSelectionMode.None) return;
- if (releaseTexture && _textureFrame.internalNativePointer != System.IntPtr.Zero)
- {
- Native.UnlockTextureFrame(_instance, ref _textureFrame);
- _textureFrame.internalNativePointer = System.IntPtr.Zero;
- _textureFrame.texturePointer = System.IntPtr.Zero;
- }
- Native.FlushFrameBuffering(_instance);
- // Native _pauseOnPrerollComplete needs to be reset
- //Native.SetFrameBufferingEnabled(_instance, (_frameSelectionMode != BufferedFrameSelectionMode.None), _pauseOnPrerollComplete);
- _needsInitialFrame = true;
- _timeAccumulation = 0.0;
- }
- internal override long InternalUpdateBufferedDisplay()
- {
- BufferedFramesState state = GetBufferedFramesState();
- if (state.bufferedFrameCount > 0)
- {
- if (_frameSelectionMode == BufferedFrameSelectionMode.NewestFrame)
- {
- SetBufferedDisplayTime(_frameSelectionMode, -1, false);
- }
- else if (_frameSelectionMode == BufferedFrameSelectionMode.OldestFrame)
- {
- SetBufferedDisplayTime(_frameSelectionMode, -1, false);
- }
- else if (_frameSelectionMode == BufferedFrameSelectionMode.ElapsedTime ||
- _frameSelectionMode == BufferedFrameSelectionMode.ElapsedTimeVsynced)
- {
- // Only start consuming frames on these conditions
- bool needsInitialFrame = (_textureFrame.texturePointer == System.IntPtr.Zero || _needsInitialFrame);
- bool playingBufferedFrames = (IsPrerollComplete() && (IsPlaying() || (!IsPlaying() && IsFinished())));
- if (needsInitialFrame || playingBufferedFrames)
- {
- if (needsInitialFrame)
- {
- if (SetBufferedDisplayTime(BufferedFrameSelectionMode.OldestFrame, -1, true))
- {
- _displayClockTime = _textureFrame.timeStamp;
- _needsInitialFrame = false;
- }
- }
- else
- {
- // TODO: run without vsync, just show next frame (use media clock for present?)
- // use our own clock...
- const double SecondsToHNS = 10000000.0;
- double videoFrameDuration = SecondsToHNS / (double)GetVideoFrameRate();
- long videoDuration = (long)Math.Floor(SecondsToHNS * GetDuration());
- long lastFrameTime = Math.Max(videoDuration, state.maxTimeStamp);
- double delta = SecondsToHNS * Time.deltaTime;
- if (_frameSelectionMode == BufferedFrameSelectionMode.ElapsedTimeVsynced && QualitySettings.vSyncCount > 0)
- {
- // Since we're running with vsync enabled, the MINIMUM elapsed time will be 1 monitor refresh (multiplied by QualitySettings.vSyncCount)
- double monitorDuration = (QualitySettings.vSyncCount * SecondsToHNS) / (double)Screen.currentResolution.refreshRate;
- int wholeFrames = (int)System.Math.Floor(_timeAccumulation / monitorDuration);
- wholeFrames = System.Math.Max(1, wholeFrames);
- delta = monitorDuration * wholeFrames;
- //LogBufferState();
- if (wholeFrames > 1)
- {
- //Debug.Log(Time.frameCount + "] " + Time.deltaTime + " " + wholeFrames + " " + _timeAccumulation + " " + _timeAccumulation / SecondsToHNS);
- //LogBufferState();
- }
- _timeAccumulation += (Time.deltaTime * SecondsToHNS) - delta;
- //delta = monitorDuration;
- /*double actualFrameDuration = Time.deltaTime * SecondsToHNS;
- double idealFrameTimeDifference = (actualFrameDuration);// - minMonitorDuration);
- if (idealFrameTimeDifference > (minMonitorDuration / 2))
- {
- int droppedFrames = (int)Math.Round(idealFrameTimeDifference / minMonitorDuration);
- //Debug.Log(Time.maximumDeltaTime + " " + Time.deltaTime + " " + actualFrameDuration + " " + idealFrameTimeDifference + " = " + droppedFrames);
- delta += minMonitorDuration * droppedFrames;
-
- //LogBufferState();
- }
- else
- {
- //Debug.Log(Time.deltaTime);
- }
- // If we're running slower than this or there is a frame drop, the elapsed time will be a multiple
- // of the monitor refresh rate
- */
- }
- _displayClockTime += delta;
- int multiple = (int)videoFrameDuration;
- long snappedFrameTime = (long)Math.Floor(_displayClockTime / multiple) * multiple;
- if (_isLooping && snappedFrameTime > lastFrameTime)
- {
- snappedFrameTime %= lastFrameTime;
- _needsInitialFrame = true;
- }
- else
- {
- snappedFrameTime = Math.Min(snappedFrameTime, lastFrameTime);
- }
- if (System.Math.Abs(snappedFrameTime - _textureFrame.timeStamp) > 1000)
- {
- //Debug.Log("1 " + _displayClockTime + " > " + snappedFrameTime + " d:" + delta);
- //LogBufferState();
- if (_needsInitialFrame)
- {
- if (SetBufferedDisplayTime(BufferedFrameSelectionMode.OldestFrame, -1, true))
- {
- _displayClockTime = _textureFrame.timeStamp;
- //Debug.Log("initial: " + _displayClockTime);
- _needsInitialFrame = false;
- }
- }
- else if (!SetBufferedDisplayTime(_frameSelectionMode, snappedFrameTime, false))
- {
- //Debug.LogWarning("[AVProVideo] failed to set time at " + snappedFrameTime);
- //LogBufferState();
- // Try to snap to oldest buffered time
- _displayClockTime = (state.minTimeStamp + state.maxTimeStamp) / 2.0;
- snappedFrameTime = (long)Math.Floor(_displayClockTime / multiple) * multiple;
- if (_isLooping && snappedFrameTime > lastFrameTime)
- {
- snappedFrameTime %= lastFrameTime;
- }
- else
- {
- snappedFrameTime = Math.Min(snappedFrameTime, lastFrameTime);
- }
- if (SetBufferedDisplayTime(BufferedFrameSelectionMode.FromExternalTimeClosest, snappedFrameTime, false))
- {
- _displayClockTime = _textureFrame.timeStamp;
- //Debug.LogWarning("[AVProVideo] Good set: " + _displayClockTime);
- }
- else
- {
- //Debug.LogWarning("[AVProVideo] Failed to display frame time " + snappedFrameTime);
- //LogBufferState();
- }
- }
- }
- }
- }
- }
- else if (_frameSelectionMode == BufferedFrameSelectionMode.FromExternalTime)
- {
- if (_masterDisplay != null)
- {
- // Use the time from the master
- long timeStamp = _masterDisplay.UpdateBufferedDisplay();
- if (timeStamp != GetTextureTimeStamp())
- {
- if (!SetBufferedDisplayTime(BufferedFrameSelectionMode.FromExternalTimeClosest, timeStamp, false))
- {
- Debug.LogWarning("[AVProVideo] Failed to display frame using external clock at time " + timeStamp);
- }
- }
- }
- }
- }
- return GetTextureTimeStamp();
- }
- private void LogBufferState()
- {
- BufferedFramesState state = GetBufferedFramesState();
- long timeStamp = GetTextureTimeStamp();
- string result = string.Format("[AVProVideo] {4} - {2},{3}\t\t{0}-{1} ({5})", state.minTimeStamp, state.maxTimeStamp, state.bufferedFrameCount, state.freeFrameCount, timeStamp, Time.deltaTime);
- Debug.Log(result);
- }
- private bool SetBufferedDisplayTime(BufferedFrameSelectionMode mode, long timeOfDesiredFrameToDisplay, bool ignorePreroll)
- {
- bool result = false;
- //if (!_isPaused)
- {
- result = Native.LockTextureFrame(_instance, mode, timeOfDesiredFrameToDisplay, ref _textureFrame, ignorePreroll);
- }
- return result;
- }
- public override BufferedFramesState GetBufferedFramesState()
- {
- BufferedFramesState state = new BufferedFramesState();
- Native.GetBufferedFramesState(_instance, ref state);
- return state;
- }
- public override void SetBufferedDisplayMode(BufferedFrameSelectionMode mode, IBufferedDisplay master = null)
- {
- _frameSelectionMode = mode;
- _masterDisplay = master;
- UpdateBufferedDisplay();
- }
- public override void SetBufferedDisplayOptions(bool pauseOnPrerollComplete)
- {
- _pauseOnPrerollComplete = pauseOnPrerollComplete;
- Native.SetFrameBufferingEnabled(_instance, (_frameSelectionMode != BufferedFrameSelectionMode.None), _pauseOnPrerollComplete);
- }
- public override void SetSlaves(IBufferedDisplay[] slaves)
- {
- foreach (IBufferedDisplay slave in slaves)
- {
- slave.SetBufferedDisplayMode(BufferedFrameSelectionMode.FromExternalTime, this);
- }
- _slaveDisplays = slaves;
- }
- private bool IsPrerollComplete()
- {
- bool result = true;
-
- if (GetBufferedFramesState().prerolledCount <= 0)
- {
- result = false;
- }
- if (_slaveDisplays != null && result)
- {
- foreach (IBufferedDisplay slave in _slaveDisplays)
- {
- if (slave.GetBufferedFramesState().prerolledCount <= 0)
- {
- result = false;
- break;
- }
- }
- }
- return result;
- }
- private partial struct Native
- {
- [DllImport("AVProVideo")]
- public static extern bool GetBufferedFramesState(System.IntPtr playerInstance, ref BufferedFramesState state);
- [DllImport("AVProVideo")]
- #if AVPROVIDEO_MARSHAL_RETURN_BOOL
- [return: MarshalAs(UnmanagedType.I1)]
- #endif
- public static extern bool LockTextureFrame(System.IntPtr instance, BufferedFrameSelectionMode mode, long time, ref TextureFrame textureFrame, bool ignorePreroll);
- [DllImport("AVProVideo")]
- public static extern void UnlockTextureFrame(System.IntPtr instance, ref TextureFrame textureFrame);
- [DllImport("AVProVideo")]
- public static extern void ReleaseTextureFrame(System.IntPtr instance, ref TextureFrame textureFrame);
- [DllImport("AVProVideo")]
- public static extern void FlushFrameBuffering(System.IntPtr instance);
- }
- #endregion // IBufferedDisplay Implementation
- }
- }
- #endif
|