double buffered video for fast loops
parent
1954665c93
commit
c70560025d
|
@ -3,10 +3,13 @@ using System.IO;
|
|||
|
||||
namespace MoonWorks.Video
|
||||
{
|
||||
public unsafe class VideoAV1 : IDisposable
|
||||
public unsafe class VideoAV1
|
||||
{
|
||||
public IntPtr Handle => handle;
|
||||
IntPtr handle;
|
||||
public string Filename { get; }
|
||||
|
||||
// "double buffering" so we can loop without a stutter
|
||||
internal VideoAV1Stream StreamA { get; }
|
||||
internal VideoAV1Stream StreamB { get; }
|
||||
|
||||
public int Width => width;
|
||||
public int Height => height;
|
||||
|
@ -19,8 +22,6 @@ namespace MoonWorks.Video
|
|||
private int height;
|
||||
private Dav1dfile.PixelLayout pixelLayout;
|
||||
|
||||
bool IsDisposed;
|
||||
|
||||
public VideoAV1(string filename, double framesPerSecond)
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
|
@ -28,12 +29,13 @@ namespace MoonWorks.Video
|
|||
throw new ArgumentException("Video file not found!");
|
||||
}
|
||||
|
||||
if (Dav1dfile.df_fopen(filename, out handle) == 0)
|
||||
if (Dav1dfile.df_fopen(filename, out var handle) == 0)
|
||||
{
|
||||
throw new Exception("Failed to open video file!");
|
||||
}
|
||||
|
||||
Dav1dfile.df_videoinfo(Handle, out width, out height, out pixelLayout);
|
||||
Dav1dfile.df_videoinfo(handle, out width, out height, out pixelLayout);
|
||||
Dav1dfile.df_close(handle);
|
||||
|
||||
if (pixelLayout == Dav1dfile.PixelLayout.I420)
|
||||
{
|
||||
|
@ -56,34 +58,11 @@ namespace MoonWorks.Video
|
|||
}
|
||||
|
||||
FramesPerSecond = framesPerSecond;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!IsDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// dispose managed state (managed objects)
|
||||
}
|
||||
Filename = filename;
|
||||
|
||||
// free unmanaged resources (unmanaged objects)
|
||||
Dav1dfile.df_close(Handle);
|
||||
|
||||
IsDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~VideoAV1()
|
||||
{
|
||||
Dispose(disposing: false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
StreamA = new VideoAV1Stream(this);
|
||||
StreamB = new VideoAV1Stream(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
using System;
|
||||
|
||||
namespace MoonWorks.Video
|
||||
{
|
||||
internal class VideoAV1Stream
|
||||
{
|
||||
public IntPtr Handle => handle;
|
||||
IntPtr handle;
|
||||
|
||||
public bool Ended => Dav1dfile.df_eos(Handle) == 1;
|
||||
|
||||
public IntPtr yDataHandle;
|
||||
public IntPtr uDataHandle;
|
||||
public IntPtr vDataHandle;
|
||||
public uint yDataLength;
|
||||
public uint uvDataLength;
|
||||
public uint yStride;
|
||||
public uint uvStride;
|
||||
|
||||
public bool FrameDataUpdated { get; private set; }
|
||||
|
||||
bool IsDisposed;
|
||||
|
||||
public VideoAV1Stream(VideoAV1 video)
|
||||
{
|
||||
if (Dav1dfile.df_fopen(video.Filename, out handle) == 0)
|
||||
{
|
||||
throw new Exception("Failed to open video file!");
|
||||
}
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
Dav1dfile.df_reset(Handle);
|
||||
ReadNextFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadNextFrame()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (Dav1dfile.df_readvideo(
|
||||
Handle,
|
||||
1,
|
||||
out yDataHandle,
|
||||
out uDataHandle,
|
||||
out vDataHandle,
|
||||
out yDataLength,
|
||||
out uvDataLength,
|
||||
out yStride,
|
||||
out uvStride) == 1
|
||||
) {
|
||||
FrameDataUpdated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!IsDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// dispose managed state (managed objects)
|
||||
}
|
||||
|
||||
// free unmanaged resources (unmanaged objects)
|
||||
Dav1dfile.df_close(Handle);
|
||||
|
||||
IsDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~VideoAV1Stream()
|
||||
{
|
||||
Dispose(disposing: false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.InteropServices;
|
||||
using MoonWorks.Audio;
|
||||
using MoonWorks.Graphics;
|
||||
|
@ -14,6 +15,7 @@ namespace MoonWorks.Video
|
|||
public float PlaybackSpeed { get; set; } = 1;
|
||||
|
||||
private VideoAV1 Video = null;
|
||||
private VideoAV1Stream CurrentStream = null;
|
||||
|
||||
private GraphicsDevice GraphicsDevice;
|
||||
private Texture yTexture = null;
|
||||
|
@ -21,17 +23,6 @@ namespace MoonWorks.Video
|
|||
private Texture vTexture = null;
|
||||
private Sampler LinearSampler;
|
||||
|
||||
private object readLock = new object();
|
||||
private bool frameDataUpdated;
|
||||
|
||||
private IntPtr yDataHandle;
|
||||
private IntPtr uDataHandle;
|
||||
private IntPtr vDataHandle;
|
||||
private uint yDataLength;
|
||||
private uint uvDataLength;
|
||||
private uint yStride;
|
||||
private uint uvStride;
|
||||
|
||||
private int currentFrame;
|
||||
|
||||
private Stopwatch timer;
|
||||
|
@ -152,7 +143,7 @@ namespace MoonWorks.Video
|
|||
lastTimestamp = 0;
|
||||
timeElapsed = 0;
|
||||
|
||||
Dav1dfile.df_reset(Video.Handle);
|
||||
InitializeDav1dStream();
|
||||
|
||||
State = VideoState.Stopped;
|
||||
}
|
||||
|
@ -176,28 +167,27 @@ namespace MoonWorks.Video
|
|||
int thisFrame = ((int) (timeElapsed / (1000.0 / Video.FramesPerSecond)));
|
||||
if (thisFrame > currentFrame)
|
||||
{
|
||||
if (frameDataUpdated)
|
||||
if (CurrentStream.FrameDataUpdated)
|
||||
{
|
||||
UpdateRenderTexture();
|
||||
}
|
||||
|
||||
currentFrame = thisFrame;
|
||||
System.Threading.Tasks.Task.Run(ReadNextFrame);
|
||||
Task.Run(CurrentStream.ReadNextFrame);
|
||||
}
|
||||
|
||||
bool ended = Dav1dfile.df_eos(Video.Handle) == 1;
|
||||
if (ended)
|
||||
if (CurrentStream.Ended)
|
||||
{
|
||||
timer.Stop();
|
||||
timer.Reset();
|
||||
|
||||
Dav1dfile.df_reset(Video.Handle);
|
||||
Task.Run(CurrentStream.Reset);
|
||||
|
||||
if (Loop)
|
||||
{
|
||||
// Start over!
|
||||
InitializeDav1dStream();
|
||||
|
||||
// Start over on the next stream!
|
||||
CurrentStream = (CurrentStream == Video.StreamA) ? Video.StreamB : Video.StreamA;
|
||||
currentFrame = -1;
|
||||
timer.Start();
|
||||
}
|
||||
else
|
||||
|
@ -209,7 +199,7 @@ namespace MoonWorks.Video
|
|||
|
||||
private void UpdateRenderTexture()
|
||||
{
|
||||
lock (readLock)
|
||||
lock (CurrentStream)
|
||||
{
|
||||
var commandBuffer = GraphicsDevice.AcquireCommandBuffer();
|
||||
|
||||
|
@ -217,13 +207,13 @@ namespace MoonWorks.Video
|
|||
yTexture,
|
||||
uTexture,
|
||||
vTexture,
|
||||
yDataHandle,
|
||||
uDataHandle,
|
||||
vDataHandle,
|
||||
yDataLength,
|
||||
uvDataLength,
|
||||
yStride,
|
||||
uvStride
|
||||
CurrentStream.yDataHandle,
|
||||
CurrentStream.uDataHandle,
|
||||
CurrentStream.vDataHandle,
|
||||
CurrentStream.yDataLength,
|
||||
CurrentStream.uvDataLength,
|
||||
CurrentStream.yStride,
|
||||
CurrentStream.uvStride
|
||||
);
|
||||
|
||||
commandBuffer.BeginRenderPass(
|
||||
|
@ -269,28 +259,11 @@ namespace MoonWorks.Video
|
|||
|
||||
private void InitializeDav1dStream()
|
||||
{
|
||||
System.Threading.Tasks.Task.Run(ReadNextFrame);
|
||||
currentFrame = -1;
|
||||
}
|
||||
Task.Run(Video.StreamA.Reset);
|
||||
Task.Run(Video.StreamB.Reset);
|
||||
|
||||
private void ReadNextFrame()
|
||||
{
|
||||
lock (readLock)
|
||||
{
|
||||
if (Dav1dfile.df_readvideo(
|
||||
Video.Handle,
|
||||
1,
|
||||
out yDataHandle,
|
||||
out uDataHandle,
|
||||
out vDataHandle,
|
||||
out yDataLength,
|
||||
out uvDataLength,
|
||||
out yStride,
|
||||
out uvStride) == 1
|
||||
) {
|
||||
frameDataUpdated = true;
|
||||
}
|
||||
}
|
||||
CurrentStream = Video.StreamA;
|
||||
currentFrame = -1;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
|
|
Loading…
Reference in New Issue