diff --git a/src/simulacrum-dalamud-av/Mpv.cs b/src/simulacrum-dalamud-av/Mpv.cs new file mode 100644 index 0000000..e090e05 --- /dev/null +++ b/src/simulacrum-dalamud-av/Mpv.cs @@ -0,0 +1,33 @@ +using System.Runtime.InteropServices; + +namespace Simulacrum.AV; + +public static partial class Mpv +{ + [LibraryImport("mpv.exe", EntryPoint = "mpv_create")] + internal static partial nint Create(); + + [LibraryImport("mpv.exe", EntryPoint = "mpv_initialize")] + internal static partial int Initialize(nint client); + + [LibraryImport("mpv.exe", EntryPoint = "mpv_terminate_destroy")] + internal static partial int TerminateDestroy(nint client); + + [LibraryImport("mpv.exe", EntryPoint = "mpv_command", StringMarshalling = StringMarshalling.Utf8)] + internal static partial int Command(nint client, string[,] args); + + [LibraryImport("mpv.exe", EntryPoint = "mpv_set_option")] + internal static partial int SetOption(nint client, ReadOnlySpan name, int format, ReadOnlySpan data); + + [LibraryImport("mpv.exe", EntryPoint = "mpv_set_option_string")] + internal static partial int SetOptionString(nint client, ReadOnlySpan name, ReadOnlySpan data); + + [LibraryImport("mpv.exe", EntryPoint = "mpv_get_property")] + internal static partial int GetProperty(nint client, ReadOnlySpan name, int format, ref nint data); + + [LibraryImport("mpv.exe", EntryPoint = "mpv_set_property")] + internal static partial int SetProperty(nint client, ReadOnlySpan name, int format, ReadOnlySpan data); + + [LibraryImport("mpv.exe", EntryPoint = "mpv_free")] + internal static partial void Free(nint data); +} \ No newline at end of file diff --git a/src/simulacrum-dalamud-av/MpvHandle.cs b/src/simulacrum-dalamud-av/MpvHandle.cs new file mode 100644 index 0000000..37f7961 --- /dev/null +++ b/src/simulacrum-dalamud-av/MpvHandle.cs @@ -0,0 +1,58 @@ +namespace Simulacrum.AV; + +public class MpvHandle : IDisposable +{ + private nint _handle = Mpv.Create(); + + public int Initialize() + { + return _handle != nint.Zero ? Mpv.Initialize(_handle) : -1; + } + + public int Command(string[,] args) + { + return _handle != nint.Zero ? Mpv.Command(_handle, args) : -1; + } + + public int SetOption(ReadOnlySpan name, int format, ReadOnlySpan data) + { + return _handle != nint.Zero ? Mpv.SetOption(_handle, name, format, data) : -1; + } + + public int SetOptionString(ReadOnlySpan name, ReadOnlySpan data) + { + return _handle != nint.Zero ? Mpv.SetOptionString(_handle, name, data) : -1; + } + + public int GetProperty(ReadOnlySpan name, int format, ref nint data) + { + return _handle != nint.Zero ? Mpv.GetProperty(_handle, name, format, ref data) : -1; + } + + public int SetProperty(ReadOnlySpan name, int format, ReadOnlySpan data) + { + return _handle != nint.Zero ? Mpv.SetProperty(_handle, name, format, data) : -1; + } + + private void ReleaseUnmanagedResources() + { + if (_handle == nint.Zero) + { + return; + } + + Mpv.TerminateDestroy(_handle); + _handle = nint.Zero; + } + + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + ~MpvHandle() + { + ReleaseUnmanagedResources(); + } +} \ No newline at end of file diff --git a/src/simulacrum-dalamud-plugin/Drawing/MpvMediaSource.cs b/src/simulacrum-dalamud-plugin/Drawing/MpvMediaSource.cs new file mode 100644 index 0000000..aac0c18 --- /dev/null +++ b/src/simulacrum-dalamud-plugin/Drawing/MpvMediaSource.cs @@ -0,0 +1,64 @@ +using System.Globalization; +using R3; +using Simulacrum.AV; +using Simulacrum.Drawing.Common; + +namespace Simulacrum.Drawing; + +public class MpvMediaSource : IMediaSource, IDisposable +{ + private readonly MpvHandle _handle; + + private readonly IDisposable _unsubscribeAll; + + public MpvMediaSource(string? uri, IReadOnlyPlaybackTracker sync) + { + ArgumentNullException.ThrowIfNull(uri); + + _handle = new MpvHandle(); + _handle.Initialize(); + + _handle.SetOptionString("keep-open"u8, "always"u8); + + _handle.Command(new[,] { { "loadfile", uri } }); + + var unsubscribePause = sync.OnPause() + .Subscribe(_handle, static (_, mpv) => mpv.SetProperty("pause"u8, 1, "yes"u8)); + var unsubscribePlay = + sync.OnPlay().Subscribe(_handle, static (_, mpv) => mpv.SetProperty("pause"u8, 1, "no"u8)); + var unsubscribePan = sync.OnPan().Subscribe(_handle, + static (t, mpv) => + { + mpv.Command(new[,] { { "seek", t.TotalSeconds.ToString(CultureInfo.InvariantCulture), "absolute" } }); + }); + + _unsubscribeAll = Disposable.Combine(unsubscribePause, unsubscribePlay, unsubscribePan); + } + + public void RenderTo(Span buffer) + { + throw new NotImplementedException(); + } + + public void RenderTo(Span buffer, out TimeSpan delay) + { + throw new NotImplementedException(); + } + + public int PixelSize() + { + throw new NotImplementedException(); + } + + public IntVector2 Size() + { + throw new NotImplementedException(); + } + + public void Dispose() + { + _unsubscribeAll.Dispose(); + _handle.Dispose(); + GC.SuppressFinalize(this); + } +} \ No newline at end of file