using System.ComponentModel; using System.Runtime.InteropServices; using Bmp.Core.FFMpeg.CsCorePorts.FFMpegWrap.Interops; namespace Bmp.Core.FFMpeg.CsCorePorts.FFMpegWrap; /// /// Contains some utilities for working with ffmpeg. /// public static class FfmpegUtils { // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable private static readonly FfmpegCalls.LogCallback LogCallback; private static readonly FfmpegCalls.LogCallback DefaultLogCallback; private static readonly object LockObj = new object(); /// /// Occurs when a ffmpeg log entry was received. /// public static event EventHandler FfmpegLogReceived; /// /// Occurs when the location of the native FFmpeg binaries has get resolved. /// Note: This is currently only available for Windows Platforms. /// public static event EventHandler ResolveFfmpegAssemblyLocation; static unsafe FfmpegUtils() { LogCallback = OnLogMessage; DefaultLogCallback = FfmpegCalls.GetDefaultLogCallback(); FfmpegCalls.SetLogCallback(LogCallback); } /// /// Gets the output formats. /// /// All supported output formats. public static IEnumerable GetOutputFormats() { var outputFormats = FfmpegCalls.GetOutputFormats(); return outputFormats.Select(format => new Format(format)); } /// /// Gets the input formats. /// /// All supported input formats. public static IEnumerable GetInputFormats() { var inputFormats = FfmpegCalls.GetInputFormats(); return inputFormats.Select(format => new Format(format)); } /// /// Gets or sets the log level. /// /// /// The log level. /// /// value public static LogLevel LogLevel { get { return FfmpegCalls.GetLogLevel(); } set { if ((int)value < (int)LogLevel.Quit || (int)value > (int)LogLevel.Debug || (int)value % 8 != 0) throw new InvalidEnumArgumentException("value", (int)value, typeof(LogLevel)); FfmpegCalls.SetLogLevel(value); } } /// /// Gets or sets a value indicating whether log entries should be passed to the default ffmpeg logger. /// /// /// true if log messages should be passed to the default ffmpeg logger; otherwise, false. /// public static bool LogToDefaultLogger { get; set; } private static unsafe void OnLogMessage(void* ptr, int level, byte* fmt, nint vl) { lock (LockObj) { if (level >= 0) { level &= 0xFF; } if (level > (int)LogLevel) return; if (LogToDefaultLogger) { DefaultLogCallback(ptr, level, fmt, vl); } var eventHandler = FfmpegLogReceived; if (eventHandler != null) { AVClass? avClass = null; AVClass? parentLogContext = null; AVClass** ppParent = default; if (ptr != null) { avClass = **(AVClass**)ptr; if (avClass.Value.parent_log_context_offset != 0) { ppParent = *(AVClass***)((byte*)ptr + avClass.Value.parent_log_context_offset); if (ppParent != null && *ppParent != null) { parentLogContext = **ppParent; } } } var printPrefix = 1; var line = FfmpegCalls.FormatLine(ptr, level, Marshal.PtrToStringAnsi((nint)fmt), vl, ref printPrefix); eventHandler(null, new FfmpegLogReceivedEventArgs(avClass, parentLogContext, (LogLevel)level, line, ptr, ppParent)); } } } internal static DirectoryInfo FindFfmpegDirectory(PlatformID platform) { var resolveEvent = ResolveFfmpegAssemblyLocation; var eventArgs = new ResolveFfmpegAssemblyLocationEventArgs(platform); if (resolveEvent != null) resolveEvent(null, eventArgs); return eventArgs.FfmpegDirectory; } }