Split CDecoder
This commit is contained in:
1 parent
149026ec32
commit
82b650416f
3 files changed
+346
-321
No files matched your search
@@ -27,19 +27,9 @@ namespace Vocaluxe.Lib.Video.Acinerella
|
||||
{
|
||||
class CDecoder
|
||||
{
|
||||
private IntPtr _Instance = IntPtr.Zero; // acinerella instance
|
||||
private IntPtr _Videodecoder = IntPtr.Zero; // acinerella video decoder instance
|
||||
|
||||
private readonly Stopwatch _LoopTimer = new Stopwatch();
|
||||
private readonly Thread _Thread;
|
||||
private readonly Object _MutexFramebuffer = new Object();
|
||||
private readonly CFramebuffer _Framebuffer;
|
||||
private readonly Object _MutexSyncSignals = new Object();
|
||||
|
||||
private bool _FileOpened;
|
||||
|
||||
private float _FrameDuration; // frame time in s
|
||||
private float _LastDecodedTime; // time of last decoded frame
|
||||
private float _LastShownTime; // time if the last shown frame in s
|
||||
|
||||
private float _Time;
|
||||
@@ -52,28 +42,21 @@ namespace Vocaluxe.Lib.Video.Acinerella
|
||||
private float _SetStart;
|
||||
private bool _SetSkip;
|
||||
|
||||
private bool _Terminated;
|
||||
private bool _FrameAvailable;
|
||||
private bool _NoMoreFrames;
|
||||
private readonly AutoResetEvent _EvWakeUp = new AutoResetEvent(false);
|
||||
private bool _IsSleeping;
|
||||
private int _WaitCount;
|
||||
|
||||
private float _SkipTime; // = Start + VideoGap
|
||||
|
||||
private string _FileName; // current video file name
|
||||
private int _Width;
|
||||
private int _Height;
|
||||
private bool _Paused;
|
||||
|
||||
public CDecoder()
|
||||
{
|
||||
_Thread = new Thread(_Execute);
|
||||
_Framebuffer = new CFramebuffer(10);
|
||||
}
|
||||
private CDecoderThread _Thread;
|
||||
|
||||
public CDecoder() {}
|
||||
|
||||
public float Length { get; private set; }
|
||||
|
||||
public bool Loop { private get; set; }
|
||||
|
||||
public bool Finished { get; private set; }
|
||||
|
||||
public bool Paused
|
||||
{
|
||||
set
|
||||
@@ -89,13 +72,9 @@ namespace Vocaluxe.Lib.Video.Acinerella
|
||||
}
|
||||
}
|
||||
|
||||
public bool Loop { private get; set; }
|
||||
|
||||
public bool Finished { get; private set; }
|
||||
|
||||
public bool Open(string fileName)
|
||||
{
|
||||
if (_FileOpened)
|
||||
if (_Thread != null)
|
||||
return false;
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
@@ -104,20 +83,38 @@ namespace Vocaluxe.Lib.Video.Acinerella
|
||||
_FileName = fileName;
|
||||
|
||||
//Do this here as one may want to get the length afterwards!
|
||||
if (!_DoOpen())
|
||||
IntPtr pInstance = _LoadFile();
|
||||
if (pInstance == IntPtr.Zero)
|
||||
return false;
|
||||
|
||||
_FileOpened = true;
|
||||
|
||||
_Thread.Priority = ThreadPriority.Normal;
|
||||
_Thread.Name = Path.GetFileName(fileName);
|
||||
_Thread.Start();
|
||||
_Thread = new CDecoderThread(Path.GetFileName(fileName), pInstance);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Open the file and get the length.
|
||||
private IntPtr _LoadFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
IntPtr pInstance = CAcinerella.AcInit();
|
||||
CAcinerella.AcOpen2(pInstance, _FileName);
|
||||
|
||||
var instance = (SACInstance)Marshal.PtrToStructure(pInstance, typeof(SACInstance));
|
||||
Length = instance.Info.Duration / 1000f;
|
||||
bool ok = instance.Opened;
|
||||
if (ok)
|
||||
return pInstance;
|
||||
}
|
||||
catch (Exception) {}
|
||||
CLog.LogError("Error opening video file: " + _FileName);
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_Terminated = true;
|
||||
if (_Thread != null)
|
||||
_Thread.Stop();
|
||||
Length = 0;
|
||||
}
|
||||
|
||||
@@ -160,290 +157,5 @@ namespace Vocaluxe.Lib.Video.Acinerella
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Threading
|
||||
private void _Skip()
|
||||
{
|
||||
_SkipTime = _Start + _Gap;
|
||||
|
||||
if (_SkipTime < 0)
|
||||
_SkipTime = 0;
|
||||
|
||||
try
|
||||
{
|
||||
CAcinerella.AcSeek(_Videodecoder, -1, (Int64)(_SkipTime * 1000f));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CLog.LogError("Error seeking video file \"" + _FileName + "\": " + e.Message);
|
||||
}
|
||||
_LastDecodedTime = _SkipTime;
|
||||
|
||||
lock (_MutexFramebuffer)
|
||||
{
|
||||
_Framebuffer.Clear();
|
||||
}
|
||||
|
||||
_FrameAvailable = false;
|
||||
}
|
||||
|
||||
private void _Execute()
|
||||
{
|
||||
while (!_Terminated)
|
||||
{
|
||||
bool skip;
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
_Time = _SetTime;
|
||||
skip = _SetSkip;
|
||||
_SetSkip = false;
|
||||
_Gap = _SetGap;
|
||||
_Start = _SetStart;
|
||||
_Loop = Loop;
|
||||
}
|
||||
|
||||
if (skip)
|
||||
_Skip();
|
||||
|
||||
if (!_FrameAvailable)
|
||||
_Decode();
|
||||
|
||||
if (_FrameAvailable)
|
||||
{
|
||||
if (!_Framebuffer.IsFull())
|
||||
{
|
||||
_Copy();
|
||||
_WaitCount = 0;
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
else if (_WaitCount > 3)
|
||||
{
|
||||
_IsSleeping = true;
|
||||
_EvWakeUp.WaitOne();
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep((int)((_Framebuffer.Size - 2) * _FrameDuration * 1000));
|
||||
_WaitCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_Free();
|
||||
}
|
||||
|
||||
private bool _DoOpen()
|
||||
{
|
||||
bool ok;
|
||||
try
|
||||
{
|
||||
_Instance = CAcinerella.AcInit();
|
||||
CAcinerella.AcOpen2(_Instance, _FileName);
|
||||
|
||||
var instance = (SACInstance)Marshal.PtrToStructure(_Instance, typeof(SACInstance));
|
||||
Length = instance.Info.Duration / 1000f;
|
||||
ok = instance.Opened;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
CLog.LogError("Error opening video file: " + _FileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
int videoStreamIndex;
|
||||
SACDecoder videodecoder;
|
||||
try
|
||||
{
|
||||
_Videodecoder = CAcinerella.AcCreateVideoDecoder(_Instance);
|
||||
videodecoder = (SACDecoder)Marshal.PtrToStructure(_Videodecoder, typeof(SACDecoder));
|
||||
videoStreamIndex = videodecoder.StreamIndex;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error opening video file (can't find decoder): " + _FileName);
|
||||
_Free();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (videoStreamIndex < 0)
|
||||
{
|
||||
_Free();
|
||||
return false;
|
||||
}
|
||||
|
||||
_Width = videodecoder.StreamInfo.VideoInfo.FrameWidth;
|
||||
_Height = videodecoder.StreamInfo.VideoInfo.FrameHeight;
|
||||
|
||||
if (videodecoder.StreamInfo.VideoInfo.FramesPerSecond > 0)
|
||||
_FrameDuration = 1f / (float)videodecoder.StreamInfo.VideoInfo.FramesPerSecond;
|
||||
|
||||
_LastDecodedTime = 0f;
|
||||
_SetTime = 0f;
|
||||
|
||||
_Framebuffer.Init(_Width * _Height * 4);
|
||||
_FrameAvailable = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void _Decode()
|
||||
{
|
||||
const int minFrameDropCount = 4;
|
||||
|
||||
if (_Paused || _NoMoreFrames)
|
||||
return;
|
||||
|
||||
float videoTime = _Time + _Gap;
|
||||
float timeDifference = videoTime - _LastDecodedTime;
|
||||
|
||||
bool dropFrame = timeDifference >= (minFrameDropCount - 1) * _FrameDuration;
|
||||
|
||||
if (_Terminated)
|
||||
return;
|
||||
|
||||
bool frameFinished = false;
|
||||
if (dropFrame)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (videoTime >= Length)
|
||||
{
|
||||
if (_Loop)
|
||||
{
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
_Start = 0;
|
||||
_Gap = 0f;
|
||||
_SetTime = 0;
|
||||
}
|
||||
_Skip();
|
||||
}
|
||||
else
|
||||
{
|
||||
_NoMoreFrames = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var frameDropCount = (int)(timeDifference / _FrameDuration);
|
||||
frameFinished = CAcinerella.AcSkipFrames(_Instance, _Videodecoder, frameDropCount);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error AcSkipFrame " + _FileName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!frameFinished)
|
||||
{
|
||||
try
|
||||
{
|
||||
frameFinished = CAcinerella.AcGetFrame(_Instance, _Videodecoder);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error AcGetFrame " + _FileName);
|
||||
}
|
||||
}
|
||||
if (frameFinished)
|
||||
_FrameAvailable = true;
|
||||
else
|
||||
{
|
||||
if (_Loop)
|
||||
{
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
_Start = 0f;
|
||||
_Gap = 0f;
|
||||
_SetTime = 0f;
|
||||
}
|
||||
_Skip();
|
||||
}
|
||||
else
|
||||
_NoMoreFrames = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void _Copy()
|
||||
{
|
||||
var videodecoder = (SACDecoder)Marshal.PtrToStructure(_Videodecoder, typeof(SACDecoder));
|
||||
if (videodecoder.Buffer == IntPtr.Zero)
|
||||
return;
|
||||
_LastDecodedTime = (float)videodecoder.Timecode;
|
||||
_Framebuffer.Put(videodecoder.Buffer, _LastDecodedTime);
|
||||
_FrameAvailable = false;
|
||||
}
|
||||
|
||||
private void _UploadNewFrame(ref CTexture frame)
|
||||
{
|
||||
lock (_MutexFramebuffer)
|
||||
{
|
||||
CFrame curFrame = _FindFrame();
|
||||
|
||||
if (curFrame != null)
|
||||
{
|
||||
CDraw.UpdateOrAddTexture(ref frame, _Width, _Height, curFrame.Data);
|
||||
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
_LastShownTime = curFrame.Time;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_NoMoreFrames && _Framebuffer.IsEmpty())
|
||||
Finished = true;
|
||||
}
|
||||
}
|
||||
if (!Finished && _IsSleeping)
|
||||
{
|
||||
_IsSleeping = false;
|
||||
_EvWakeUp.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private CFrame _FindFrame()
|
||||
{
|
||||
CFrame result = null;
|
||||
float now = _SetTime + _Gap;
|
||||
float maxEnd = now - _FrameDuration * 2; //Get only frames younger than 2
|
||||
CFrame frame;
|
||||
bool paused = _Paused;
|
||||
while ((frame = _Framebuffer.Get()) != null)
|
||||
{
|
||||
float frameEnd = frame.Time + _FrameDuration;
|
||||
//Don't show frames that are shown during or after now
|
||||
if (frameEnd >= now)
|
||||
break; //All following frames are after that one
|
||||
if (paused) // Don't modify anything
|
||||
break;
|
||||
_Framebuffer.SetRead();
|
||||
//Get the last(newest) possible frame and skip the rest to force in-order showing
|
||||
if (frameEnd <= maxEnd)
|
||||
continue;
|
||||
maxEnd = frameEnd;
|
||||
result = frame;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void _Free()
|
||||
{
|
||||
if (_Videodecoder != IntPtr.Zero)
|
||||
CAcinerella.AcFreeDecoder(_Videodecoder);
|
||||
|
||||
if (_Instance != IntPtr.Zero)
|
||||
{
|
||||
CAcinerella.AcClose(_Instance);
|
||||
CAcinerella.AcFree(_Instance);
|
||||
}
|
||||
}
|
||||
#endregion Threading
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Vocaluxe.Base;
|
||||
using VocaluxeLib.Draw;
|
||||
|
||||
namespace Vocaluxe.Lib.Video.Acinerella
|
||||
{
|
||||
class CDecoderThread
|
||||
{
|
||||
private readonly Thread _Thread;
|
||||
private readonly Object _MutexFramebuffer = new Object();
|
||||
private bool isStarted;
|
||||
|
||||
private IntPtr _Instance; // acinerella instance
|
||||
private IntPtr _Videodecoder = IntPtr.Zero; // acinerella video decoder instance
|
||||
|
||||
private readonly CFramebuffer _Framebuffer;
|
||||
private float _LastDecodedTime; // time of last decoded frame
|
||||
|
||||
private float _FrameDuration; // frame time in s
|
||||
private int _Width;
|
||||
private int _Height;
|
||||
|
||||
private bool _Terminated;
|
||||
private bool _FrameAvailable;
|
||||
private bool _NoMoreFrames;
|
||||
private readonly AutoResetEvent _EvWakeUp = new AutoResetEvent(false);
|
||||
private bool _IsSleeping;
|
||||
private int _WaitCount;
|
||||
|
||||
public CDecoderThread(String name, IntPtr instance)
|
||||
{
|
||||
_Framebuffer = new CFramebuffer(10);
|
||||
_Instance = instance;
|
||||
_Thread = new Thread(_Execute) {Priority = ThreadPriority.Normal, Name = name};
|
||||
_Thread.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_Terminated = true;
|
||||
}
|
||||
|
||||
private bool _DoOpen()
|
||||
{
|
||||
int videoStreamIndex;
|
||||
SACDecoder videodecoder;
|
||||
try
|
||||
{
|
||||
_Videodecoder = CAcinerella.AcCreateVideoDecoder(_Instance);
|
||||
videodecoder = (SACDecoder)Marshal.PtrToStructure(_Videodecoder, typeof(SACDecoder));
|
||||
videoStreamIndex = videodecoder.StreamIndex;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error opening video file (can't find decoder): " + _Thread.Name);
|
||||
_Free();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (videoStreamIndex < 0)
|
||||
{
|
||||
_Free();
|
||||
return false;
|
||||
}
|
||||
|
||||
_Width = videodecoder.StreamInfo.VideoInfo.FrameWidth;
|
||||
_Height = videodecoder.StreamInfo.VideoInfo.FrameHeight;
|
||||
|
||||
if (videodecoder.StreamInfo.VideoInfo.FramesPerSecond > 0)
|
||||
_FrameDuration = 1f / (float)videodecoder.StreamInfo.VideoInfo.FramesPerSecond;
|
||||
|
||||
_LastDecodedTime = 0f;
|
||||
_SetTime = 0f;
|
||||
|
||||
_Framebuffer.Init(_Width * _Height * 4);
|
||||
_FrameAvailable = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void _Free()
|
||||
{
|
||||
if (_Videodecoder != IntPtr.Zero)
|
||||
CAcinerella.AcFreeDecoder(_Videodecoder);
|
||||
|
||||
if (_Instance != IntPtr.Zero)
|
||||
{
|
||||
CAcinerella.AcClose(_Instance);
|
||||
CAcinerella.AcFree(_Instance);
|
||||
}
|
||||
}
|
||||
|
||||
private void _Skip()
|
||||
{
|
||||
_SkipTime = _Start + _Gap;
|
||||
|
||||
if (_SkipTime < 0)
|
||||
_SkipTime = 0;
|
||||
|
||||
try
|
||||
{
|
||||
CAcinerella.AcSeek(_Videodecoder, -1, (Int64)(_SkipTime * 1000f));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CLog.LogError("Error seeking video file \"" + _FileName + "\": " + e.Message);
|
||||
}
|
||||
_LastDecodedTime = _SkipTime;
|
||||
|
||||
lock (_MutexFramebuffer)
|
||||
{
|
||||
_Framebuffer.Clear();
|
||||
}
|
||||
|
||||
_FrameAvailable = false;
|
||||
}
|
||||
|
||||
private void _Execute()
|
||||
{
|
||||
if (!_DoOpen())
|
||||
Stop();
|
||||
while (!_Terminated)
|
||||
{
|
||||
bool skip;
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
_Time = _SetTime;
|
||||
skip = _SetSkip;
|
||||
_SetSkip = false;
|
||||
_Gap = _SetGap;
|
||||
_Start = _SetStart;
|
||||
_Loop = Loop;
|
||||
}
|
||||
|
||||
if (skip)
|
||||
_Skip();
|
||||
|
||||
if (!_FrameAvailable)
|
||||
_Decode();
|
||||
|
||||
if (_FrameAvailable)
|
||||
{
|
||||
if (!_Framebuffer.IsFull())
|
||||
{
|
||||
_Copy();
|
||||
_WaitCount = 0;
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
else if (_WaitCount > 3)
|
||||
{
|
||||
_IsSleeping = true;
|
||||
_EvWakeUp.WaitOne();
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep((int)((_Framebuffer.Size - 2) * _FrameDuration * 1000));
|
||||
_WaitCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_Free();
|
||||
}
|
||||
|
||||
private void _Decode()
|
||||
{
|
||||
const int minFrameDropCount = 4;
|
||||
|
||||
if (_Paused || _NoMoreFrames)
|
||||
return;
|
||||
|
||||
float videoTime = _Time + _Gap;
|
||||
float timeDifference = videoTime - _LastDecodedTime;
|
||||
|
||||
bool dropFrame = timeDifference >= (minFrameDropCount - 1) * _FrameDuration;
|
||||
|
||||
if (_Terminated)
|
||||
return;
|
||||
|
||||
bool frameFinished = false;
|
||||
if (dropFrame)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (videoTime >= Length)
|
||||
{
|
||||
if (_Loop)
|
||||
{
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
_Start = 0;
|
||||
_Gap = 0f;
|
||||
_SetTime = 0;
|
||||
}
|
||||
_Skip();
|
||||
}
|
||||
else
|
||||
{
|
||||
_NoMoreFrames = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var frameDropCount = (int)(timeDifference / _FrameDuration);
|
||||
frameFinished = CAcinerella.AcSkipFrames(_Instance, _Videodecoder, frameDropCount);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error AcSkipFrame " + _Thread.Name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!frameFinished)
|
||||
{
|
||||
try
|
||||
{
|
||||
frameFinished = CAcinerella.AcGetFrame(_Instance, _Videodecoder);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error AcGetFrame " + _Thread.Name);
|
||||
}
|
||||
}
|
||||
if (frameFinished)
|
||||
_FrameAvailable = true;
|
||||
else
|
||||
{
|
||||
if (_Loop)
|
||||
{
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
_Start = 0f;
|
||||
_Gap = 0f;
|
||||
_SetTime = 0f;
|
||||
}
|
||||
_Skip();
|
||||
}
|
||||
else
|
||||
_NoMoreFrames = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void _Copy()
|
||||
{
|
||||
var videodecoder = (SACDecoder)Marshal.PtrToStructure(_Videodecoder, typeof(SACDecoder));
|
||||
if (videodecoder.Buffer == IntPtr.Zero)
|
||||
return;
|
||||
_LastDecodedTime = (float)videodecoder.Timecode;
|
||||
_Framebuffer.Put(videodecoder.Buffer, _LastDecodedTime);
|
||||
_FrameAvailable = false;
|
||||
}
|
||||
|
||||
private void _UploadNewFrame(ref CTexture frame)
|
||||
{
|
||||
lock (_MutexFramebuffer)
|
||||
{
|
||||
CFrame curFrame = _FindFrame();
|
||||
|
||||
if (curFrame != null)
|
||||
{
|
||||
CDraw.UpdateOrAddTexture(ref frame, _Width, _Height, curFrame.Data);
|
||||
|
||||
lock (_MutexSyncSignals)
|
||||
{
|
||||
_LastShownTime = curFrame.Time;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_NoMoreFrames && _Framebuffer.IsEmpty())
|
||||
Finished = true;
|
||||
}
|
||||
}
|
||||
if (!Finished && _IsSleeping)
|
||||
{
|
||||
_IsSleeping = false;
|
||||
_EvWakeUp.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private CFrame _FindFrame()
|
||||
{
|
||||
CFrame result = null;
|
||||
float now = _SetTime + _Gap;
|
||||
float maxEnd = now - _FrameDuration * 2; //Get only frames younger than 2
|
||||
CFrame frame;
|
||||
bool paused = _Paused;
|
||||
while ((frame = _Framebuffer.Get()) != null)
|
||||
{
|
||||
float frameEnd = frame.Time + _FrameDuration;
|
||||
//Don't show frames that are shown during or after now
|
||||
if (frameEnd >= now)
|
||||
break; //All following frames are after that one
|
||||
if (paused) // Don't modify anything
|
||||
break;
|
||||
_Framebuffer.SetRead();
|
||||
//Get the last(newest) possible frame and skip the rest to force in-order showing
|
||||
if (frameEnd <= maxEnd)
|
||||
continue;
|
||||
maxEnd = frameEnd;
|
||||
result = frame;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,6 +251,7 @@
|
||||
<Compile Include="Lib\Input\CControllerFramework.cs" />
|
||||
<Compile Include="Lib\Sound\CGstreamerSharpAudio.cs" />
|
||||
<Compile Include="Lib\Video\Acinerella\CDecoder.cs" />
|
||||
<Compile Include="Lib\Video\Acinerella\CDecoderThread.cs" />
|
||||
<Compile Include="Lib\Video\Acinerella\CFramebuffer.cs" />
|
||||
<Compile Include="Screens\CPopupScreenServerQR.cs" />
|
||||
<Compile Include="Screens\CScreenOptionsVideoAdjustments.cs" />
|
||||
@@ -484,4 +485,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
Reference in new issue
Block a user