Split CDecoder

This commit is contained in:
Flamefire committed 2014-02-26 00:47:29 +01:00
1 parent 149026ec32
commit 82b650416f
3 files changed
+346 -321

No files matched your search

+32 -320
View File
@@ -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;
}
}
}
+2 -1
View File
@@ -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>