diff --git a/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs b/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs index 9591eaaa..a60359eb 100644 --- a/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs +++ b/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs @@ -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 } } \ No newline at end of file diff --git a/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs b/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs new file mode 100644 index 00000000..7e40b96f --- /dev/null +++ b/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index 51c3fb35..380eb362 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -251,6 +251,7 @@ + @@ -484,4 +485,4 @@ --> - + \ No newline at end of file