From ce3d5792ad713dc605adc27cf0bd59fa40c85ca2 Mon Sep 17 00:00:00 2001 From: Frank Skare Date: Tue, 3 Mar 2020 23:02:40 +0100 Subject: [PATCH] readme update --- Changelog.md | 2 + README.md | 77 ++--- mpv.net/Misc/App.cs | 3 + mpv.net/Misc/Command.cs | 12 +- mpv.net/Misc/Program.cs | 12 +- mpv.net/Misc/UpdateCheck.cs | 7 +- mpv.net/Native/MediaInfo.cs | 2 +- mpv.net/Native/Native.cs | 7 +- mpv.net/Native/NativeHelp.cs | 8 +- mpv.net/Native/Taskbar.cs | 14 +- mpv.net/Scripting/PowerShellScript.cs | 3 +- mpv.net/WinForms/MainForm.cs | 23 +- mpv.net/mpv/libmpv.cs | 405 ++++++++++++++------------ mpv.net/mpv/mp.cs | 264 ++++++++--------- 14 files changed, 440 insertions(+), 399 deletions(-) diff --git a/Changelog.md b/Changelog.md index 335f7d9..a4edcf3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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 diff --git a/README.md b/README.md index 4d9227f..d5d83c8 100644 --- a/README.md +++ b/README.md @@ -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 -### Links +## Links -- mpv.net wiki: -- mpv.net default key bindings: -- mpv.net download: -- mpv.net bugs and requests: -- mpv website: -- mpv manual: -- mpv wiki: -- mpv apps: -- mpv user scripts: -- mpv default key bindings: -- mpv download: -- mpv bugs and requests: +#### mpv.net -### Changelog +- wiki: +- default key bindings: +- download: +- bugs and requests: + +#### mpv + +- website: +- manual: +- wiki: +- apps: +- user scripts: +- default key bindings: +- download: +- bugs and requests: + +## Changelog [Changelog](Changelog.md) diff --git a/mpv.net/Misc/App.cs b/mpv.net/Misc/App.cs index 3efd078..cf1c6c2 100644 --- a/mpv.net/Misc/App.cs +++ b/mpv.net/Misc/App.cs @@ -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) diff --git a/mpv.net/Misc/Command.cs b/mpv.net/Misc/Command.cs index 6aaca4d..7d81937 100644 --- a/mpv.net/Misc/Command.cs +++ b/mpv.net/Misc/Command.cs @@ -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); } })); } diff --git a/mpv.net/Misc/Program.cs b/mpv.net/Misc/Program.cs index 35c9045..2157e7e 100644 --- a/mpv.net/Misc/Program.cs +++ b/mpv.net/Misc/Program.cs @@ -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(); } diff --git a/mpv.net/Misc/UpdateCheck.cs b/mpv.net/Misc/UpdateCheck.cs index 23523b9..203bbf6 100644 --- a/mpv.net/Misc/UpdateCheck.cs +++ b/mpv.net/Misc/UpdateCheck.cs @@ -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"; diff --git a/mpv.net/Native/MediaInfo.cs b/mpv.net/Native/MediaInfo.cs index 41bdee8..9243186 100644 --- a/mpv.net/Native/MediaInfo.cs +++ b/mpv.net/Native/MediaInfo.cs @@ -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; diff --git a/mpv.net/Native/Native.cs b/mpv.net/Native/Native.cs index 920b411..45eff20 100644 --- a/mpv.net/Native/Native.cs +++ b/mpv.net/Native/Native.cs @@ -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; } -} \ No newline at end of file +} diff --git a/mpv.net/Native/NativeHelp.cs b/mpv.net/Native/NativeHelp.cs index 6e0b0c8..0b63716 100644 --- a/mpv.net/Native/NativeHelp.cs +++ b/mpv.net/Native/NativeHelp.cs @@ -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); } } } \ No newline at end of file diff --git a/mpv.net/Native/Taskbar.cs b/mpv.net/Native/Taskbar.cs index 499d361..94685db 100644 --- a/mpv.net/Native/Taskbar.cs +++ b/mpv.net/Native/Taskbar.cs @@ -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) diff --git a/mpv.net/Scripting/PowerShellScript.cs b/mpv.net/Scripting/PowerShellScript.cs index 362020a..9d669d1 100644 --- a/mpv.net/Scripting/PowerShellScript.cs +++ b/mpv.net/Scripting/PowerShellScript.cs @@ -1,4 +1,5 @@ -using System; + +using System; using System.IO; using System.Threading; using System.Management.Automation.Runspaces; diff --git a/mpv.net/WinForms/MainForm.cs b/mpv.net/WinForms/MainForm.cs index 4c21d72..b2415ad 100644 --- a/mpv.net/WinForms/MainForm.cs +++ b/mpv.net/WinForms/MainForm.cs @@ -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(m.LParam); - Native.SetWindowPos(Handle, IntPtr.Zero, rect.Left, rect.Top, rect.Width, rect.Height, 0); + WinAPI.RECT rect = Marshal.PtrToStructure(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(m.LParam); + var rc = Marshal.PtrToStructure(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(new Native.RECT(corners[0], corners[1], corners[2], corners[3]), m.LParam, false); + Marshal.StructureToPtr(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) diff --git a/mpv.net/mpv/libmpv.cs b/mpv.net/mpv/libmpv.cs index 1cf42db..08d617f 100644 --- a/mpv.net/mpv/libmpv.cs +++ b/mpv.net/mpv/libmpv.cs @@ -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 } -} \ No newline at end of file + + 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"); +} diff --git a/mpv.net/mpv/mp.cs b/mpv.net/mpv/mp.cs index 1c8e664..6db59b2 100644 --- a/mpv.net/mpv/mp.cs +++ b/mpv.net/mpv/mp.cs @@ -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 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(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(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(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(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(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(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(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(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 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>(name, action)); @@ -568,10 +604,11 @@ namespace mpvnet public static void observe_property_double(string name, Action 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>(name, action)); @@ -579,10 +616,11 @@ namespace mpvnet public static void observe_property_bool(string name, Action 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>(name, action)); @@ -590,10 +628,11 @@ namespace mpvnet public static void observe_property_string(string name, Action 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>(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;