using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native { static partial class FFmpegApi { public const string AvCodecLibraryName = "avcodec"; public const string AvUtilLibraryName = "avutil"; private static readonly Dictionary _librariesWhitelist = new() { { AvCodecLibraryName, (58, 59) }, { AvUtilLibraryName, (56, 57) } }; private static string FormatLibraryNameForCurrentOs(string libraryName, int version) { if (OperatingSystem.IsWindows()) { return $"{libraryName}-{version}.dll"; } else if (OperatingSystem.IsLinux()) { return $"lib{libraryName}.so.{version}"; } else if (OperatingSystem.IsMacOS()) { return $"lib{libraryName}.{version}.dylib"; } else { throw new NotImplementedException($"Unsupported OS for FFmpeg: {RuntimeInformation.RuntimeIdentifier}"); } } private static bool TryLoadWhitelistedLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle) { handle = IntPtr.Zero; if (_librariesWhitelist.TryGetValue(libraryName, out var value)) { (int minVersion, int maxVersion) = value; for (int version = maxVersion; version >= minVersion; version--) { if (NativeLibrary.TryLoad(FormatLibraryNameForCurrentOs(libraryName, version), assembly, searchPath, out handle)) { return true; } } } return false; } static FFmpegApi() { NativeLibrary.SetDllImportResolver(typeof(FFmpegApi).Assembly, (name, assembly, path) => { if (name == AvUtilLibraryName && TryLoadWhitelistedLibrary(AvUtilLibraryName, assembly, path, out nint handle)) { return handle; } else if (name == AvCodecLibraryName && TryLoadWhitelistedLibrary(AvCodecLibraryName, assembly, path, out handle)) { return handle; } return IntPtr.Zero; }); } public unsafe delegate void av_log_set_callback_callback(void* a0, AVLog level, [MarshalAs(UnmanagedType.LPUTF8Str)] string a2, byte* a3); [LibraryImport(AvUtilLibraryName)] internal static unsafe partial AVFrame* av_frame_alloc(); [LibraryImport(AvUtilLibraryName)] internal static unsafe partial void av_frame_unref(AVFrame* frame); [LibraryImport(AvUtilLibraryName)] internal static unsafe partial void av_free(AVFrame* frame); [LibraryImport(AvUtilLibraryName)] internal static unsafe partial void av_log_set_level(AVLog level); [LibraryImport(AvUtilLibraryName)] internal static unsafe partial void av_log_set_callback(av_log_set_callback_callback callback); [LibraryImport(AvUtilLibraryName)] internal static unsafe partial AVLog av_log_get_level(); [LibraryImport(AvUtilLibraryName)] internal static unsafe partial void av_log_format_line(void* ptr, AVLog level, [MarshalAs(UnmanagedType.LPUTF8Str)] string fmt, byte* vl, byte* line, int lineSize, int* printPrefix); [LibraryImport(AvCodecLibraryName)] internal static unsafe partial AVCodec* avcodec_find_decoder(AVCodecID id); [LibraryImport(AvCodecLibraryName)] internal static unsafe partial AVCodecContext* avcodec_alloc_context3(AVCodec* codec); [LibraryImport(AvCodecLibraryName)] internal static unsafe partial int avcodec_open2(AVCodecContext* avctx, AVCodec* codec, void** options); [LibraryImport(AvCodecLibraryName)] internal static unsafe partial int avcodec_close(AVCodecContext* avctx); [LibraryImport(AvCodecLibraryName)] internal static unsafe partial void avcodec_free_context(AVCodecContext** avctx); [LibraryImport(AvCodecLibraryName)] internal static unsafe partial AVPacket* av_packet_alloc(); [LibraryImport(AvCodecLibraryName)] internal static unsafe partial void av_packet_unref(AVPacket* pkt); [LibraryImport(AvCodecLibraryName)] internal static unsafe partial void av_packet_free(AVPacket** pkt); [LibraryImport(AvCodecLibraryName)] internal static unsafe partial int avcodec_version(); } }