readme update

This commit is contained in:
Frank Skare
2020-03-03 23:02:40 +01:00
parent 6294a96aea
commit ce3d5792ad
14 changed files with 440 additions and 399 deletions

View File

@@ -2,6 +2,8 @@
###
- fix: update routine did only work when mpv.net was located in 'Program Files'
- fix: errors were just ignored and only seen printed in the terminal in case mpv.net
was started from the terminal, now for every error a message box is shown
### 5.4.4.0

View File

@@ -50,7 +50,7 @@ Table of contents
- [Links](#links)
- [Changelog](#changelog)
### Features
## Features
- Very high degree of mpv compatibility, almost all mpv features are available
- Great usability due to everything in the application being searchable
@@ -88,7 +88,7 @@ Table of contents
- Update check and update routine ([Manual](Manual.md#help--check-for-updates))
- [Manual](#manual)
### Screenshots
## Screenshots
#### Main Window Screenshot
@@ -131,7 +131,7 @@ Media search feature powered by [Everything](https://www.voidtools.com) to find
![Media Search](https://raw.githubusercontent.com/stax76/mpv.net/master/img/MediaSearch.png)
### Installation
## Installation
mpv.net requires minimum .NET Framework 4.8 and Windows 7. For optimal results a modern graphics card is recommended.
@@ -150,11 +150,11 @@ Alternatively, Chocolatey can also be used:
`choco install mpvnet.install`
### Manual
## Manual
[Manual](Manual.md)
### Context Menu
## Context Menu
The context menu can be customized via input.conf file located in the config directory:
@@ -168,7 +168,7 @@ if it's missing mpv.net generates it with the following defaults:
input.conf defines mpv's key and mouse bindings and mpv.net uses comments to define the context menu.
### Settings
## Settings
When mpv.net finds no config folder on startup it will ask for a location.
@@ -196,47 +196,27 @@ mpv.net supports almost all mpv settings and features,
The config folder can be opened from the context menu.
### Scripting
## Scripting
[Scripting wiki page](https://github.com/stax76/mpv.net/wiki/Scripting)
### Extensions
## Extensions
[Extensions wiki page](https://github.com/stax76/mpv.net/wiki/Extensions)
### Architecture
## Architecture
Coding mpv.net was great fun because libmpv is such a awesome
library with a very clever design, I'm having a great experience
with libmpv.
The player does not contain any feature that was more work than 1-2 days or
was difficult to build, the hard parts are totally covered by libmpv.
mpv.net is written in C# 7 and runs on .NET 4.8, I've not yet decided
if I will port it to C# 8 and .NET 5 once available.
mpv.net is written in C# and runs on the .NET Framework
The Extension implementation is based on the [Managed Extensibility Framework](https://docs.microsoft.com/en-us/dotnet/framework/mef/).
There are no specific extension or scripting interfaces but instead everyting
is accessible for .NET compatible languages (C#, VB.NET, F#, Python, PowerShell),
this decision was made to keep the code simple and lightweight.
Python scripting is implemented with IronPython which uses Python 2.7.
The main window is WinForms based and uses less than 800 lines of code,
all other windows are WPF based and use even less code.
The main window is WinForms based, WinForms allows better libmpv integration compared to WPF, all other windows are WPF based.
The config editor adds it's controls dynamically and uses [TOML](https://en.wikipedia.org/wiki/TOML) to define it's
content, there are only two simple types, StringSetting and OptionSetting.
mpv.net was started 2017 and consists of about 7000 lines of code and markup.
IDE, Editor: Visual Studio, Visual Studio Code.
Due to mpv.net being my first WPF app and mpv.net never meant to be a large
application best practices and design pattern are not always applied.
Third party components:
- [libmpv, the heard and soul of mpv.net](https://mpv.io/)
@@ -246,7 +226,7 @@ Third party components:
- [CS-Script, scripting with C#](http://www.csscript.net/)
- [Everything, a blazing fast file search service](https://www.voidtools.com)
### Support
## Support
[Support thread in Doom9 forum](https://forum.doom9.org/showthread.php?t=174841)
@@ -262,21 +242,26 @@ If you want to support the development of mpv.net or express your appreciation y
<https://www.paypal.me/stax76>
### Links
## Links
- mpv.net wiki: <https://github.com/stax76/mpv.net/wiki>
- mpv.net default key bindings: <https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt>
- mpv.net download: <https://github.com/stax76/mpv.net/releases>
- mpv.net bugs and requests: <https://github.com/stax76/mpv.net/issues>
- mpv website: <https://mpv.io/>
- mpv manual: <https://mpv.io/manual/master/>
- mpv wiki: <https://github.com/mpv-player/mpv/wiki>
- mpv apps: <https://github.com/mpv-player/mpv/wiki/Applications-using-mpv>
- mpv user scripts: <https://github.com/mpv-player/mpv/wiki/User-Scripts>
- mpv default key bindings: <https://github.com/mpv-player/mpv/blob/master/etc/input.conf>
- mpv download: <https://mpv.io/installation/>
- mpv bugs and requests: <https://mpv.io/bug-reports/>
#### mpv.net
### Changelog
- wiki: <https://github.com/stax76/mpv.net/wiki>
- default key bindings: <https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt>
- download: <https://github.com/stax76/mpv.net/releases>
- bugs and requests: <https://github.com/stax76/mpv.net/issues>
#### mpv
- website: <https://mpv.io/>
- manual: <https://mpv.io/manual/master/>
- wiki: <https://github.com/mpv-player/mpv/wiki>
- apps: <https://github.com/mpv-player/mpv/wiki/Applications-using-mpv>
- user scripts: <https://github.com/mpv-player/mpv/wiki/User-Scripts>
- default key bindings: <https://github.com/mpv-player/mpv/blob/master/etc/input.conf>
- download: <https://mpv.io/installation/>
- bugs and requests: <https://mpv.io/bug-reports/>
## Changelog
[Changelog](Changelog.md)

View File

@@ -83,8 +83,11 @@ namespace mpvnet
mp.Shutdown += Shutdown;
mp.Initialized += Initialized;
mp.LogMessage += LogMessage;
}
private static void LogMessage(string msg) => Msg.ShowError(msg);
private static void Initialized()
{
if (RememberVolume)

View File

@@ -43,7 +43,7 @@ namespace mpvnet
MainForm.Instance.BeginInvoke(new Action(() => {
Message m = new Message() { Msg = 0x0202 }; // WM_LBUTTONUP
Native.SendMessage(MainForm.Instance.Handle, m.Msg, m.WParam, m.LParam);
WinAPI.SendMessage(MainForm.Instance.Handle, m.Msg, m.WParam, m.LParam);
}));
}
@@ -229,11 +229,15 @@ namespace mpvnet
using (var d = new OpenFileDialog())
{
string path = mp.get_property_string("path");
if (File.Exists(path)) d.InitialDirectory = Path.GetDirectoryName(path);
if (File.Exists(path))
d.InitialDirectory = Path.GetDirectoryName(path);
d.Multiselect = true;
if (d.ShowDialog() == DialogResult.OK)
foreach (string i in d.FileNames)
mp.commandv("sub-add", i);
foreach (string filename in d.FileNames)
mp.commandv("sub-add", filename);
}
}));
}

View File

@@ -19,7 +19,7 @@ namespace mpvnet
Application.SetCompatibleTextRenderingDefault(false);
if (App.IsStartedFromTerminal)
Native.AttachConsole(-1 /*ATTACH_PARENT_PROCESS*/);
WinAPI.AttachConsole(-1 /*ATTACH_PARENT_PROCESS*/);
if (mp.ConfigFolder == "")
return;
@@ -61,15 +61,15 @@ namespace mpvnet
{
if (proc.MainWindowHandle != IntPtr.Zero)
{
Native.AllowSetForegroundWindow(proc.Id);
var data = new Native.COPYDATASTRUCT();
WinAPI.AllowSetForegroundWindow(proc.Id);
var data = new WinAPI.COPYDATASTRUCT();
data.lpData = string.Join("\n", files.ToArray());
data.cbData = data.lpData.Length * 2 + 1;
Native.SendMessage(proc.MainWindowHandle, 0x004A /*WM_COPYDATA*/, IntPtr.Zero, ref data);
WinAPI.SendMessage(proc.MainWindowHandle, 0x004A /*WM_COPYDATA*/, IntPtr.Zero, ref data);
mutex.Dispose();
if (App.IsStartedFromTerminal)
Native.FreeConsole();
WinAPI.FreeConsole();
return;
}
@@ -85,7 +85,7 @@ namespace mpvnet
Application.Run(new MainForm());
if (App.IsStartedFromTerminal)
Native.FreeConsole();
WinAPI.FreeConsole();
mutex.Dispose();
}

View File

@@ -33,15 +33,16 @@ namespace mpvnet
Version onlineVersion = Version.Parse(match.Groups[1].Value);
Version currentVersion = Assembly.GetEntryAssembly().GetName().Version;
if (onlineVersion == currentVersion)
if (onlineVersion <= currentVersion)
{
if (showUpToDateMessage)
Msg.Show($"{Application.ProductName} is up to date.");
return;
}
if (RegistryHelp.GetString(RegistryHelp.ApplicationKey, "UpdateCheckVersion")
!= onlineVersion.ToString() && Msg.ShowQuestion(
if ((RegistryHelp.GetString(RegistryHelp.ApplicationKey, "UpdateCheckVersion")
!= onlineVersion.ToString() || showUpToDateMessage) && Msg.ShowQuestion(
$"New version {onlineVersion} is available, update now?") == MsgResult.OK)
{
string arch = IntPtr.Size == 8 ? "64" : "86";

View File

@@ -10,7 +10,7 @@ public class MediaInfo : IDisposable
{
if (!Loaded)
{
if (Native.LoadLibrary("MediaInfo.dll") == IntPtr.Zero)
if (WinAPI.LoadLibrary("MediaInfo.dll") == IntPtr.Zero)
throw new Exception("Failed to load MediaInfo.dll.");
Loaded = true;

View File

@@ -1,8 +1,9 @@
using System;

using System;
using System.Drawing;
using System.Runtime.InteropServices;
public class Native
public class WinAPI
{
[DllImport("kernel32.dll")]
public static extern bool AttachConsole(int dwProcessId);
@@ -92,4 +93,4 @@ public class Native
[MarshalAs(UnmanagedType.LPTStr)]
public string lpData;
}
}
}

View File

@@ -20,9 +20,9 @@ namespace mpvnet
}
}
public static void SubtractWindowBorders(IntPtr hwnd, ref Native.RECT rc)
public static void SubtractWindowBorders(IntPtr hwnd, ref WinAPI.RECT rc)
{
var b = new Native.RECT(0, 0, 0, 0);
var b = new WinAPI.RECT(0, 0, 0, 0);
AddWindowBorders(hwnd, ref b);
rc.Left -= b.Left;
rc.Top -= b.Top;
@@ -30,9 +30,9 @@ namespace mpvnet
rc.Bottom -= b.Bottom;
}
public static void AddWindowBorders(IntPtr hwnd, ref Native.RECT rc)
public static void AddWindowBorders(IntPtr hwnd, ref WinAPI.RECT rc)
{
Native.AdjustWindowRect(ref rc, (uint)Native.GetWindowLong(hwnd, -16 /* GWL_STYLE */), false);
WinAPI.AdjustWindowRect(ref rc, (uint)WinAPI.GetWindowLong(hwnd, -16 /* GWL_STYLE */), false);
}
}
}

View File

@@ -1,4 +1,5 @@
using System;

using System;
using System.Runtime.InteropServices;
public class Taskbar
@@ -9,9 +10,8 @@ public class Taskbar
public Taskbar(IntPtr handle) => Handle = handle;
[ComImportAttribute]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
[GuidAttribute("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF")]
private interface ITaskbarList3
{
// ITaskbarList
@@ -27,9 +27,9 @@ public class Taskbar
[PreserveSig] void SetProgressState(IntPtr hwnd, TaskbarStates state);
}
[ComImportAttribute]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
[GuidAttribute("56FDF344-FD6D-11d0-958A-006097C9A090")]
[ComImport]
[ClassInterface(ClassInterfaceType.None)]
[Guid("56FDF344-FD6D-11d0-958A-006097C9A090")]
private class TaskBarCommunication { }
public void SetState(TaskbarStates taskbarState)

View File

@@ -1,4 +1,5 @@
using System;

using System;
using System.IO;
using System.Threading;
using System.Management.Automation.Runspaces;

View File

@@ -68,7 +68,7 @@ namespace mpvnet
Application.ThreadException += (sender, e) => Msg.ShowException(e.Exception);
Msg.SupportURL = "https://github.com/stax76/mpv.net#support";
Text = "mpv.net " + Application.ProductVersion;
TaskbarButtonCreatedMessage = Native.RegisterWindowMessage("TaskbarButtonCreated");
TaskbarButtonCreatedMessage = WinAPI.RegisterWindowMessage("TaskbarButtonCreated");
ContextMenu = new ContextMenuStripEx(components);
ContextMenu.Opened += ContextMenu_Opened;
@@ -318,7 +318,7 @@ namespace mpvnet
}
Point middlePos = new Point(Left + Width / 2, Top + Height / 2);
var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height));
var rect = new WinAPI.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height));
NativeHelp.AddWindowBorders(Handle, ref rect);
int left = middlePos.X - rect.Width / 2;
int top = middlePos.Y - rect.Height / 2;
@@ -341,7 +341,7 @@ namespace mpvnet
if (top + rect.Height > maxBottom)
top = maxBottom - rect.Height;
Native.SetWindowPos(Handle, IntPtr.Zero /* HWND_TOP */, left, top, rect.Width, rect.Height, 4 /* SWP_NOZORDER */);
WinAPI.SetWindowPos(Handle, IntPtr.Zero /* HWND_TOP */, left, top, rect.Width, rect.Height, 4 /* SWP_NOZORDER */);
}
public void CycleFullscreen(bool enabled)
@@ -468,7 +468,7 @@ namespace mpvnet
case 0x0105: // WM_SYSKEYUP
case 0x319: // WM_APPCOMMAND
if (mp.WindowHandle != IntPtr.Zero)
Native.SendMessage(mp.WindowHandle, m.Msg, m.WParam, m.LParam);
WinAPI.SendMessage(mp.WindowHandle, m.Msg, m.WParam, m.LParam);
break;
case 0x0200: // WM_MOUSEMOVE
{
@@ -497,13 +497,13 @@ namespace mpvnet
if (!WasShown)
break;
Native.RECT rect = Marshal.PtrToStructure<Native.RECT>(m.LParam);
Native.SetWindowPos(Handle, IntPtr.Zero, rect.Left, rect.Top, rect.Width, rect.Height, 0);
WinAPI.RECT rect = Marshal.PtrToStructure<WinAPI.RECT>(m.LParam);
WinAPI.SetWindowPos(Handle, IntPtr.Zero, rect.Left, rect.Top, rect.Width, rect.Height, 0);
}
break;
case 0x0214: // WM_SIZING
{
var rc = Marshal.PtrToStructure<Native.RECT>(m.LParam);
var rc = Marshal.PtrToStructure<WinAPI.RECT>(m.LParam);
var r = rc;
NativeHelp.SubtractWindowBorders(Handle, ref r);
int c_w = r.Right - r.Left, c_h = r.Bottom - r.Top;
@@ -522,13 +522,13 @@ namespace mpvnet
if (corner >= 0)
corners[corner] -= d_corners[corner];
Marshal.StructureToPtr<Native.RECT>(new Native.RECT(corners[0], corners[1], corners[2], corners[3]), m.LParam, false);
Marshal.StructureToPtr<WinAPI.RECT>(new WinAPI.RECT(corners[0], corners[1], corners[2], corners[3]), m.LParam, false);
m.Result = new IntPtr(1);
}
return;
case 0x004A: // WM_COPYDATA
{
var copyData = (Native.COPYDATASTRUCT)m.GetLParam(typeof(Native.COPYDATASTRUCT));
var copyData = (WinAPI.COPYDATASTRUCT)m.GetLParam(typeof(WinAPI.COPYDATASTRUCT));
string[] files = copyData.lpData.Split('\n');
string mode = files[0];
files = files.Skip(1).ToArray();
@@ -574,6 +574,7 @@ namespace mpvnet
private void ProgressTimer_Tick(object sender, EventArgs e) => UpdateProgressBar();
// TODO: why is this in mainform?
void UpdateProgressBar()
{
if (mp.TaskbarProgress && Taskbar != null)
@@ -696,8 +697,8 @@ namespace mpvnet
e.Y < ClientSize.Height * 0.9)
{
var HTCAPTION = new IntPtr(2);
Native.ReleaseCapture();
Native.PostMessage(Handle, 0xA1 /* WM_NCLBUTTONDOWN */, HTCAPTION, IntPtr.Zero);
WinAPI.ReleaseCapture();
WinAPI.PostMessage(Handle, 0xA1 /* WM_NCLBUTTONDOWN */, HTCAPTION, IntPtr.Zero);
}
if (Width - e.Location.X < 10 && e.Location.Y < 10)

View File

@@ -1,188 +1,231 @@

using System;
using System.Runtime.InteropServices;
using System.Text;
namespace mpvnet
public class libmpv
{
public class libmpv
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mpv_create();
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_initialize(IntPtr mpvHandle);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_command(IntPtr mpvHandle, IntPtr strings);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_command_string(IntPtr mpvHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string command);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mpv_error_string(mpv_error error);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_terminate_destroy(IntPtr mpvHandle);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_request_log_messages(IntPtr mpvHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string min_level);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_set_option(IntPtr mpvHandle, byte[] name, mpv_format format, ref long data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_set_option_string(IntPtr mpvHandle, byte[] name, byte[] value);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_get_property(IntPtr mpvHandle, byte[] name, mpv_format format, out IntPtr data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_get_property(IntPtr mpvHandle, byte[] name, mpv_format format, out double data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_set_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref byte[] data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_set_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref Int64 data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_observe_property(IntPtr mpvHandle, UInt64 reply_userdata, [MarshalAs(UnmanagedType.LPUTF8Str)] string name, mpv_format format);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_unobserve_property(IntPtr mpvHandle, UInt64 registered_reply_userdata);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mpv_free(IntPtr data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mpv_wait_event(IntPtr mpvHandle, double timeout);
public enum mpv_error
{
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mpv_create();
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_initialize(IntPtr mpvHandle);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_command(IntPtr mpvHandle, IntPtr strings);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_command_string(IntPtr mpvHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string command);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_terminate_destroy(IntPtr mpvHandle);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_set_option(IntPtr mpvHandle, byte[] name, mpv_format format, ref long data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_set_option_string(IntPtr mpvHandle, byte[] name, byte[] value);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_get_property(IntPtr mpvHandle, byte[] name, mpv_format format, out IntPtr data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_get_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref double data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_set_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref byte[] data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_set_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref Int64 data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_observe_property(
IntPtr mpvHandle,
UInt64 reply_userdata,
[MarshalAs(UnmanagedType.LPUTF8Str)] string name,
mpv_format format);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_unobserve_property(IntPtr mpvHandle, UInt64 registered_reply_userdata);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mpv_free(IntPtr data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mpv_wait_event(IntPtr mpvHandle, double timeout);
public enum mpv_error
{
MPV_ERROR_SUCCESS = 0,
MPV_ERROR_EVENT_QUEUE_FULL = -1,
MPV_ERROR_NOMEM = -2,
MPV_ERROR_UNINITIALIZED = -3,
MPV_ERROR_INVALID_PARAMETER = -4,
MPV_ERROR_OPTION_NOT_FOUND = -5,
MPV_ERROR_OPTION_FORMAT = -6,
MPV_ERROR_OPTION_ERROR = -7,
MPV_ERROR_PROPERTY_NOT_FOUND = -8,
MPV_ERROR_PROPERTY_FORMAT = -9,
MPV_ERROR_PROPERTY_UNAVAILABLE = -10,
MPV_ERROR_PROPERTY_ERROR = -11,
MPV_ERROR_COMMAND = -12,
MPV_ERROR_LOADING_FAILED = -13,
MPV_ERROR_AO_INIT_FAILED = -14,
MPV_ERROR_VO_INIT_FAILED = -15,
MPV_ERROR_NOTHING_TO_PLAY = -16,
MPV_ERROR_UNKNOWN_FORMAT = -17,
MPV_ERROR_UNSUPPORTED = -18,
MPV_ERROR_NOT_IMPLEMENTED = -19,
MPV_ERROR_GENERIC = -20
}
public enum mpv_event_id
{
MPV_EVENT_NONE = 0,
MPV_EVENT_SHUTDOWN = 1,
MPV_EVENT_LOG_MESSAGE = 2,
MPV_EVENT_GET_PROPERTY_REPLY = 3,
MPV_EVENT_SET_PROPERTY_REPLY = 4,
MPV_EVENT_COMMAND_REPLY = 5,
MPV_EVENT_START_FILE = 6,
MPV_EVENT_END_FILE = 7,
MPV_EVENT_FILE_LOADED = 8,
MPV_EVENT_TRACKS_CHANGED = 9,
MPV_EVENT_TRACK_SWITCHED = 10,
MPV_EVENT_IDLE = 11,
MPV_EVENT_PAUSE = 12,
MPV_EVENT_UNPAUSE = 13,
MPV_EVENT_TICK = 14,
MPV_EVENT_SCRIPT_INPUT_DISPATCH = 15,
MPV_EVENT_CLIENT_MESSAGE = 16,
MPV_EVENT_VIDEO_RECONFIG = 17,
MPV_EVENT_AUDIO_RECONFIG = 18,
MPV_EVENT_METADATA_UPDATE = 19,
MPV_EVENT_SEEK = 20,
MPV_EVENT_PLAYBACK_RESTART = 21,
MPV_EVENT_PROPERTY_CHANGE = 22,
MPV_EVENT_CHAPTER_CHANGE = 23,
MPV_EVENT_QUEUE_OVERFLOW = 24,
MPV_EVENT_HOOK = 25
}
public enum mpv_format
{
MPV_FORMAT_NONE = 0,
MPV_FORMAT_STRING = 1,
MPV_FORMAT_OSD_STRING = 2,
MPV_FORMAT_FLAG = 3,
MPV_FORMAT_INT64 = 4,
MPV_FORMAT_DOUBLE = 5,
MPV_FORMAT_NODE = 6,
MPV_FORMAT_NODE_ARRAY = 7,
MPV_FORMAT_NODE_MAP = 8,
MPV_FORMAT_BYTE_ARRAY = 9
}
public enum mpv_log_level
{
MPV_LOG_LEVEL_NONE = 0,
MPV_LOG_LEVEL_FATAL = 10,
MPV_LOG_LEVEL_ERROR = 20,
MPV_LOG_LEVEL_WARN = 30,
MPV_LOG_LEVEL_INFO = 40,
MPV_LOG_LEVEL_V = 50,
MPV_LOG_LEVEL_DEBUG = 60,
MPV_LOG_LEVEL_TRACE = 70,
}
public enum mpv_end_file_reason
{
MPV_END_FILE_REASON_EOF = 0,
MPV_END_FILE_REASON_STOP = 2,
MPV_END_FILE_REASON_QUIT = 3,
MPV_END_FILE_REASON_ERROR = 4,
MPV_END_FILE_REASON_REDIRECT = 5
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_log_message
{
public string prefix;
public string level;
public string text;
public mpv_log_level log_level;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event
{
public mpv_event_id event_id;
public int error;
public UInt64 reply_userdata;
public IntPtr data;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_client_message
{
public int num_args;
public IntPtr args;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_property
{
public string name;
public mpv_format format;
public IntPtr data;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_end_file
{
public int reason;
public int error;
}
MPV_ERROR_SUCCESS = 0,
MPV_ERROR_EVENT_QUEUE_FULL = -1,
MPV_ERROR_NOMEM = -2,
MPV_ERROR_UNINITIALIZED = -3,
MPV_ERROR_INVALID_PARAMETER = -4,
MPV_ERROR_OPTION_NOT_FOUND = -5,
MPV_ERROR_OPTION_FORMAT = -6,
MPV_ERROR_OPTION_ERROR = -7,
MPV_ERROR_PROPERTY_NOT_FOUND = -8,
MPV_ERROR_PROPERTY_FORMAT = -9,
MPV_ERROR_PROPERTY_UNAVAILABLE = -10,
MPV_ERROR_PROPERTY_ERROR = -11,
MPV_ERROR_COMMAND = -12,
MPV_ERROR_LOADING_FAILED = -13,
MPV_ERROR_AO_INIT_FAILED = -14,
MPV_ERROR_VO_INIT_FAILED = -15,
MPV_ERROR_NOTHING_TO_PLAY = -16,
MPV_ERROR_UNKNOWN_FORMAT = -17,
MPV_ERROR_UNSUPPORTED = -18,
MPV_ERROR_NOT_IMPLEMENTED = -19,
MPV_ERROR_GENERIC = -20
}
}
public enum mpv_event_id
{
MPV_EVENT_NONE = 0,
MPV_EVENT_SHUTDOWN = 1,
MPV_EVENT_LOG_MESSAGE = 2,
MPV_EVENT_GET_PROPERTY_REPLY = 3,
MPV_EVENT_SET_PROPERTY_REPLY = 4,
MPV_EVENT_COMMAND_REPLY = 5,
MPV_EVENT_START_FILE = 6,
MPV_EVENT_END_FILE = 7,
MPV_EVENT_FILE_LOADED = 8,
MPV_EVENT_TRACKS_CHANGED = 9,
MPV_EVENT_TRACK_SWITCHED = 10,
MPV_EVENT_IDLE = 11,
MPV_EVENT_PAUSE = 12,
MPV_EVENT_UNPAUSE = 13,
MPV_EVENT_TICK = 14,
MPV_EVENT_SCRIPT_INPUT_DISPATCH = 15,
MPV_EVENT_CLIENT_MESSAGE = 16,
MPV_EVENT_VIDEO_RECONFIG = 17,
MPV_EVENT_AUDIO_RECONFIG = 18,
MPV_EVENT_METADATA_UPDATE = 19,
MPV_EVENT_SEEK = 20,
MPV_EVENT_PLAYBACK_RESTART = 21,
MPV_EVENT_PROPERTY_CHANGE = 22,
MPV_EVENT_CHAPTER_CHANGE = 23,
MPV_EVENT_QUEUE_OVERFLOW = 24,
MPV_EVENT_HOOK = 25
}
public enum mpv_format
{
MPV_FORMAT_NONE = 0,
MPV_FORMAT_STRING = 1,
MPV_FORMAT_OSD_STRING = 2,
MPV_FORMAT_FLAG = 3,
MPV_FORMAT_INT64 = 4,
MPV_FORMAT_DOUBLE = 5,
MPV_FORMAT_NODE = 6,
MPV_FORMAT_NODE_ARRAY = 7,
MPV_FORMAT_NODE_MAP = 8,
MPV_FORMAT_BYTE_ARRAY = 9
}
public enum mpv_log_level
{
MPV_LOG_LEVEL_NONE = 0,
MPV_LOG_LEVEL_FATAL = 10,
MPV_LOG_LEVEL_ERROR = 20,
MPV_LOG_LEVEL_WARN = 30,
MPV_LOG_LEVEL_INFO = 40,
MPV_LOG_LEVEL_V = 50,
MPV_LOG_LEVEL_DEBUG = 60,
MPV_LOG_LEVEL_TRACE = 70,
}
public enum mpv_end_file_reason
{
MPV_END_FILE_REASON_EOF = 0,
MPV_END_FILE_REASON_STOP = 2,
MPV_END_FILE_REASON_QUIT = 3,
MPV_END_FILE_REASON_ERROR = 4,
MPV_END_FILE_REASON_REDIRECT = 5
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_log_message
{
public string prefix;
public string level;
public string text;
public mpv_log_level log_level;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event
{
public mpv_event_id event_id;
public int error;
public UInt64 reply_userdata;
public IntPtr data;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_client_message
{
public int num_args;
public IntPtr args;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_property
{
public string name;
public mpv_format format;
public IntPtr data;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_end_file
{
public int reason;
public int error;
}
public static IntPtr AllocateUtf8ArrayWithSentinel(string[] arr, out IntPtr[] byteArrayPointers)
{
int numberOfStrings = arr.Length + 1; // add extra element for extra null pointer last (sentinel)
byteArrayPointers = new IntPtr[numberOfStrings];
IntPtr rootPointer = Marshal.AllocCoTaskMem(IntPtr.Size * numberOfStrings);
for (int index = 0; index < arr.Length; index++)
{
var bytes = GetUtf8Bytes(arr[index]);
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
byteArrayPointers[index] = unmanagedPointer;
}
Marshal.Copy(byteArrayPointers, 0, rootPointer, numberOfStrings);
return rootPointer;
}
public static string[] ConvertFromUtf8Strings(IntPtr utf8StringArray, int stringCount)
{
IntPtr[] intPtrArray = new IntPtr[stringCount];
string[] stringArray = new string[stringCount];
Marshal.Copy(utf8StringArray, intPtrArray, 0, stringCount);
for (int i = 0; i < stringCount; i++)
stringArray[i] = ConvertFromUtf8(intPtrArray[i]);
return stringArray;
}
public static string ConvertFromUtf8(IntPtr nativeUtf8)
{
int len = 0;
while (Marshal.ReadByte(nativeUtf8, len) != 0) ++len;
byte[] buffer = new byte[len];
Marshal.Copy(nativeUtf8, buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
}
public static string GetError(mpv_error err) => ConvertFromUtf8(mpv_error_string(err));
public static byte[] GetUtf8Bytes(string s) => Encoding.UTF8.GetBytes(s + "\0");
}

View File

@@ -9,15 +9,14 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using WinForms = System.Windows.Forms;
using static mpvnet.libmpv;
using static Native;
using static libmpv;
using static WinAPI;
namespace mpvnet
{
@@ -30,7 +29,7 @@ namespace mpvnet
// MPV_EVENT_NONE
public static event Action Shutdown; // shutdown MPV_EVENT_SHUTDOWN
public static event Action LogMessage; // log-message MPV_EVENT_LOG_MESSAGE
public static event Action <string>LogMessage; // log-message MPV_EVENT_LOG_MESSAGE
public static event Action GetPropertyReply; // get-property-reply MPV_EVENT_GET_PROPERTY_REPLY
public static event Action SetPropertyReply; // set-property-reply MPV_EVENT_SET_PROPERTY_REPLY
public static event Action CommandReply; // command-reply MPV_EVENT_COMMAND_REPLY
@@ -94,6 +93,11 @@ namespace mpvnet
public static void Init()
{
Handle = mpv_create();
if (Handle == IntPtr.Zero)
throw new Exception("error mpv_create");
mpv_request_log_messages(Handle, "error");
Task.Run(() => EventLoop());
if (App.IsStartedFromTerminal)
@@ -111,7 +115,11 @@ namespace mpvnet
set_property_string("config", "yes");
ProcessCommandLine(true);
mpv_initialize(Handle);
mpv_error err = mpv_initialize(Handle);
if (err < 0)
throw new Exception("mpv_initialize error\n\n" + GetError(err));
Initialized?.Invoke();
LoadMpvScripts();
}
@@ -296,7 +304,10 @@ namespace mpvnet
ShutdownAutoResetEvent.Set();
return;
case mpv_event_id.MPV_EVENT_LOG_MESSAGE:
LogMessage?.Invoke();
{
var data = (mpv_event_log_message)Marshal.PtrToStructure(evt.data, typeof(mpv_event_log_message));
LogMessage?.Invoke($"[{data.prefix}] {data.text}");
}
break;
case mpv_event_id.MPV_EVENT_GET_PROPERTY_REPLY:
GetPropertyReply?.Invoke();
@@ -311,28 +322,33 @@ namespace mpvnet
StartFile?.Invoke();
break;
case mpv_event_id.MPV_EVENT_END_FILE:
var end_fileData = (mpv_event_end_file)Marshal.PtrToStructure(evt.data, typeof(mpv_event_end_file));
EndFileEventMode reason = (EndFileEventMode)end_fileData.reason;
EndFile?.Invoke(reason);
{
var data = (mpv_event_end_file)Marshal.PtrToStructure(evt.data, typeof(mpv_event_end_file));
EndFileEventMode reason = (EndFileEventMode)data.reason;
EndFile?.Invoke(reason);
}
break;
case mpv_event_id.MPV_EVENT_FILE_LOADED:
HideLogo();
Duration = TimeSpan.FromSeconds(get_property_number("duration"));
Size vidSize = new Size(get_property_int("width"), get_property_int("height"));
if (vidSize.Width == 0 || vidSize.Height == 0)
vidSize = new Size(1, 1);
if (VideoSize != vidSize)
{
VideoSize = vidSize;
VideoSizeChanged?.Invoke();
HideLogo();
Duration = TimeSpan.FromSeconds(get_property_number("duration"));
Size vidSize = new Size(get_property_int("width"), get_property_int("height"));
if (vidSize.Width == 0 || vidSize.Height == 0)
vidSize = new Size(1, 1);
if (VideoSize != vidSize)
{
VideoSize = vidSize;
VideoSizeChanged?.Invoke();
}
VideoSizeAutoResetEvent.Set();
Task.Run(new Action(() => ReadMetaData()));
string path = mp.get_property_string("path");
if (path.Contains("://"))
path = mp.get_property_string("media-title");
WriteHistory(path);
FileLoaded?.Invoke();
}
VideoSizeAutoResetEvent.Set();
Task.Run(new Action(() => ReadMetaData()));
string path = mp.get_property_string("path");
if (path.Contains("://"))
path = mp.get_property_string("media-title");
WriteHistory(path);
FileLoaded?.Invoke();
break;
case mpv_event_id.MPV_EVENT_TRACKS_CHANGED:
TracksChanged?.Invoke();
@@ -357,12 +373,14 @@ namespace mpvnet
ScriptInputDispatch?.Invoke();
break;
case mpv_event_id.MPV_EVENT_CLIENT_MESSAGE:
var client_messageData = (mpv_event_client_message)Marshal.PtrToStructure(evt.data, typeof(mpv_event_client_message));
string[] args = NativeUtf8StrArray2ManagedStrArray(client_messageData.args, client_messageData.num_args);
if (args.Length > 1 && args[0] == "mpv.net")
Command.Execute(args[1], args.Skip(2).ToArray());
else if (args.Length > 0)
ClientMessage?.Invoke(args);
{
var data = (mpv_event_client_message)Marshal.PtrToStructure(evt.data, typeof(mpv_event_client_message));
string[] args = ConvertFromUtf8Strings(data.args, data.num_args);
if (args.Length > 1 && args[0] == "mpv.net")
Command.Execute(args[1], args.Skip(2).ToArray());
else if (args.Length > 0)
ClientMessage?.Invoke(args);
}
break;
case mpv_event_id.MPV_EVENT_VIDEO_RECONFIG:
VideoReconfig?.Invoke();
@@ -377,35 +395,37 @@ namespace mpvnet
Seek?.Invoke();
break;
case mpv_event_id.MPV_EVENT_PROPERTY_CHANGE:
var propData = (mpv_event_property)Marshal.PtrToStructure(evt.data, typeof(mpv_event_property));
{
var data = (mpv_event_property)Marshal.PtrToStructure(evt.data, typeof(mpv_event_property));
if (propData.format == mpv_format.MPV_FORMAT_FLAG)
{
lock (BoolPropChangeActions)
foreach (var i in BoolPropChangeActions)
if (i.Key== propData.name)
i.Value.Invoke(Marshal.PtrToStructure<int>(propData.data) == 1);
}
else if (propData.format == mpv_format.MPV_FORMAT_STRING)
{
lock (StringPropChangeActions)
foreach (var i in StringPropChangeActions)
if (i.Key == propData.name)
i.Value.Invoke(StringFromNativeUtf8(Marshal.PtrToStructure<IntPtr>(propData.data)));
}
else if(propData.format == mpv_format.MPV_FORMAT_INT64)
{
lock (IntPropChangeActions)
foreach (var i in IntPropChangeActions)
if (i.Key == propData.name)
i.Value.Invoke(Marshal.PtrToStructure<int>(propData.data));
}
else if (propData.format == mpv_format.MPV_FORMAT_DOUBLE)
{
lock (DoublePropChangeActions)
foreach (var i in DoublePropChangeActions)
if (i.Key == propData.name)
i.Value.Invoke(Marshal.PtrToStructure<double>(propData.data));
if (data.format == mpv_format.MPV_FORMAT_FLAG)
{
lock (BoolPropChangeActions)
foreach (var i in BoolPropChangeActions)
if (i.Key== data.name)
i.Value.Invoke(Marshal.PtrToStructure<int>(data.data) == 1);
}
else if (data.format == mpv_format.MPV_FORMAT_STRING)
{
lock (StringPropChangeActions)
foreach (var i in StringPropChangeActions)
if (i.Key == data.name)
i.Value.Invoke(ConvertFromUtf8(Marshal.PtrToStructure<IntPtr>(data.data)));
}
else if(data.format == mpv_format.MPV_FORMAT_INT64)
{
lock (IntPropChangeActions)
foreach (var i in IntPropChangeActions)
if (i.Key == data.name)
i.Value.Invoke(Marshal.PtrToStructure<int>(data.data));
}
else if (data.format == mpv_format.MPV_FORMAT_DOUBLE)
{
lock (DoublePropChangeActions)
foreach (var i in DoublePropChangeActions)
if (i.Key == data.name)
i.Value.Invoke(Marshal.PtrToStructure<double>(data.data));
}
}
break;
case mpv_event_id.MPV_EVENT_PLAYBACK_RESTART:
@@ -472,62 +492,74 @@ namespace mpvnet
if (eventObjects.PythonFunction == pyFunc)
eventObjects.EventInfo.RemoveEventHandler(eventObjects, eventObjects.Delegate);
}
public static void commandv(params string[] args)
{
if (Handle == IntPtr.Zero) return;
IntPtr mainPtr = AllocateUtf8IntPtrArrayWithSentinel(args, out IntPtr[] byteArrayPointers);
int err = mpv_command(Handle, mainPtr);
if (err < 0) throw new Exception($"{(mpv_error)err}");
foreach (var ptr in byteArrayPointers)
IntPtr mainPtr = AllocateUtf8ArrayWithSentinel(args, out IntPtr[] byteArrayPointers);
mpv_error err = mpv_command(Handle, mainPtr);
foreach (IntPtr ptr in byteArrayPointers)
Marshal.FreeHGlobal(ptr);
Marshal.FreeHGlobal(mainPtr);
if (err < 0)
throw new Exception("error executing command:\n\n" +
string.Join("\n", args) + "\r\n\r\n" + GetError(err));
}
public static void command(string command, bool throwException = false)
{
if (Handle == IntPtr.Zero) return;
int err = mpv_command_string(Handle, command);
if (err < 0 && throwException) throw new Exception($"{(mpv_error)err}\n\n" + command);
mpv_error err = mpv_command_string(Handle, command);
if (err < 0 && throwException)
throw new Exception("error executing command:\n\n" + command + "\r\n\r\n" + GetError(err));
}
public static void set_property_string(string name, string value, bool throwOnException = false)
{
byte[] bytes = GetUtf8Bytes(value);
int err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_STRING, ref bytes);
if (err < 0 && throwOnException) throw new Exception($"{name}: {(mpv_error)err}");
mpv_error err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_STRING, ref bytes);
if (err < 0 && throwOnException)
throw new Exception($"error setting property: {name} = " + value + "\r\n\r\n" + GetError(err));
}
public static string get_property_string(string name, bool throwOnException = false)
{
try
{
int err = mpv_get_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_STRING, out IntPtr lpBuffer);
mpv_error err = mpv_get_property(Handle, GetUtf8Bytes(name),
mpv_format.MPV_FORMAT_STRING, out IntPtr lpBuffer);
if (err < 0)
{
if (throwOnException) throw new Exception($"{name}: {(mpv_error)err}");
if (throwOnException)
throw new Exception($"error getting property: {name}\n\n" + GetError(err));
return "";
}
string ret = StringFromNativeUtf8(lpBuffer);
string ret = ConvertFromUtf8(lpBuffer);
mpv_free(lpBuffer);
return ret;
}
catch (Exception e)
{
if (throwOnException) throw e;
if (throwOnException)
throw e;
return "";
}
}
public static int get_property_int(string name, bool throwOnException = false)
{
int err = mpv_get_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_INT64, out IntPtr lpBuffer);
mpv_error err = mpv_get_property(Handle, GetUtf8Bytes(name),
mpv_format.MPV_FORMAT_INT64, out IntPtr lpBuffer);
if (err < 0)
{
if (throwOnException) throw new Exception($"{name}: {(mpv_error)err}");
if (throwOnException)
throw new Exception($"error getting property: {name}\n\n" + GetError(err));
return 0;
}
@@ -536,31 +568,35 @@ namespace mpvnet
public static double get_property_number(string name, bool throwOnException = false)
{
double val = 0;
int err = mpv_get_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_DOUBLE, ref val);
mpv_error err = mpv_get_property(Handle, GetUtf8Bytes(name),
mpv_format.MPV_FORMAT_DOUBLE, out double value);
if (err < 0)
{
if (throwOnException) throw new Exception($"{name}: {(mpv_error)err}");
if (throwOnException)
throw new Exception($"error getting property: {name}\n\n" + GetError(err));
return 0;
}
return val;
return value;
}
public static void set_property_int(string name, int value, bool throwOnException = false)
{
Int64 val = value;
int err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_INT64, ref val);
if (err < 0 && throwOnException) throw new Exception($"{name}: {(mpv_error)err}");
mpv_error err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_INT64, ref val);
if (err < 0 && throwOnException)
throw new Exception($"error setting property: {name} = {value}\n\n" + GetError(err));
}
public static void observe_property_int(string name, Action<int> action)
{
int err = mpv_observe_property(Handle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_INT64);
mpv_error err = mpv_observe_property(Handle, (ulong)action.GetHashCode(),
name, mpv_format.MPV_FORMAT_INT64);
if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}");
throw new Exception($"error observing property: {name}\n\n" + GetError(err));
else
lock (IntPropChangeActions)
IntPropChangeActions.Add(new KeyValuePair<string, Action<int>>(name, action));
@@ -568,10 +604,11 @@ namespace mpvnet
public static void observe_property_double(string name, Action<double> action)
{
int err = mpv_observe_property(Handle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_DOUBLE);
mpv_error err = mpv_observe_property(Handle, (ulong)action.GetHashCode(),
name, mpv_format.MPV_FORMAT_DOUBLE);
if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}");
throw new Exception($"error observing property: {name}\n\n" + GetError(err));
else
lock (DoublePropChangeActions)
DoublePropChangeActions.Add(new KeyValuePair<string, Action<double>>(name, action));
@@ -579,10 +616,11 @@ namespace mpvnet
public static void observe_property_bool(string name, Action<bool> action)
{
int err = mpv_observe_property(Handle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_FLAG);
mpv_error err = mpv_observe_property(Handle, (ulong)action.GetHashCode(),
name, mpv_format.MPV_FORMAT_FLAG);
if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}");
throw new Exception($"error observing property: {name}\n\n" + GetError(err));
else
lock (BoolPropChangeActions)
BoolPropChangeActions.Add(new KeyValuePair<string, Action<bool>>(name, action));
@@ -590,10 +628,11 @@ namespace mpvnet
public static void observe_property_string(string name, Action<string> action)
{
int err = mpv_observe_property(Handle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_STRING);
mpv_error err = mpv_observe_property(Handle, (ulong)action.GetHashCode(),
name, mpv_format.MPV_FORMAT_STRING);
if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}");
throw new Exception($"error observing property: {name}\n\n" + GetError(err));
else
lock (StringPropChangeActions)
StringPropChangeActions.Add(new KeyValuePair<string, Action<string>>(name, action));
@@ -615,12 +654,14 @@ namespace mpvnet
{
try
{
if (!arg.Contains("=")) arg += "=yes";
if (!arg.Contains("="))
arg += "=yes";
string left = arg.Substring(2, arg.IndexOf("=") - 2);
string right = arg.Substring(left.Length + 3);
if (left == "script") left = "scripts";
if (left == "script")
left = "scripts";
if (preInit && preInitProperties.Contains(left))
{
@@ -741,47 +782,6 @@ namespace mpvnet
commandv("playlist-move", "0", (index + 1).ToString());
}
public static IntPtr AllocateUtf8IntPtrArrayWithSentinel(string[] arr, out IntPtr[] byteArrayPointers)
{
int numberOfStrings = arr.Length + 1; // add extra element for extra null pointer last (sentinel)
byteArrayPointers = new IntPtr[numberOfStrings];
IntPtr rootPointer = Marshal.AllocCoTaskMem(IntPtr.Size * numberOfStrings);
for (int index = 0; index < arr.Length; index++)
{
var bytes = GetUtf8Bytes(arr[index]);
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
byteArrayPointers[index] = unmanagedPointer;
}
Marshal.Copy(byteArrayPointers, 0, rootPointer, numberOfStrings);
return rootPointer;
}
public static string[] NativeUtf8StrArray2ManagedStrArray(IntPtr unmanagedStringArray, int StringCount)
{
IntPtr[] intPtrArray = new IntPtr[StringCount];
string[] stringArray = new string[StringCount];
Marshal.Copy(unmanagedStringArray, intPtrArray, 0, StringCount);
for (int i = 0; i < StringCount; i++)
stringArray[i] = StringFromNativeUtf8(intPtrArray[i]);
return stringArray;
}
public static string StringFromNativeUtf8(IntPtr nativeUtf8)
{
int len = 0;
while (Marshal.ReadByte(nativeUtf8, len) != 0) ++len;
byte[] buffer = new byte[len];
Marshal.Copy(nativeUtf8, buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
}
public static byte[] GetUtf8Bytes(string s) => Encoding.UTF8.GetBytes(s + "\0");
static string LastHistoryPath;
static DateTime LastHistoryStartDateTime;