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