Эх сурвалжийг харах

Remove dependency for FFmpeg.AutoGen and Update FFmpeg to 5.0.1 for Windows (#3466)

* Remove dependency for FFMpeg.AutoGen

Also prepare for FFMpeg 5.0 and 5.1

* Update Ryujinx.Graphics.Nvdec.Dependencies to 5.0.1-build10

* Address gdkchan's comments

* Address Ack's comment

* Address gdkchan's comment
Mary 3 жил өмнө
parent
commit
c5bddfeab8

+ 1 - 1
Ryujinx.Ava/Ryujinx.Ava.csproj

@@ -28,7 +28,7 @@
     <PackageReference Include="FluentAvaloniaUI" Version="1.4.1" />
 
     <PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
-    <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.4.0-build9" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
+    <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
     <PackageReference Include="OpenTK.Graphics" Version="4.7.2" />
     <PackageReference Include="SPB" Version="0.0.4-build17" />
     <PackageReference Include="SharpZipLib" Version="1.3.3" />

+ 54 - 67
Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs

@@ -1,23 +1,21 @@
-using FFmpeg.AutoGen;
-using Ryujinx.Common.Logging;
+using Ryujinx.Common.Logging;
+using Ryujinx.Graphics.Nvdec.FFmpeg.Native;
 using System;
-using System.Diagnostics;
-using System.IO;
 using System.Runtime.InteropServices;
 
 namespace Ryujinx.Graphics.Nvdec.FFmpeg
 {
     unsafe class FFmpegContext : IDisposable
     {
-        private readonly AVCodec_decode _decodeFrame;
-        private static readonly av_log_set_callback_callback _logFunc;
+        private readonly FFCodec.AVCodec_decode _decodeFrame;
+        private static readonly FFmpegApi.av_log_set_callback_callback _logFunc;
         private readonly AVCodec* _codec;
         private AVPacket* _packet;
         private AVCodecContext* _context;
 
         public FFmpegContext(AVCodecID codecId)
         {
-            _codec = ffmpeg.avcodec_find_decoder(codecId);
+            _codec = FFmpegApi.avcodec_find_decoder(codecId);
             if (_codec == null)
             {
                 Logger.Error?.PrintMsg(LogClass.FFmpeg, $"Codec wasn't found. Make sure you have the {codecId} codec present in your FFmpeg installation.");
@@ -25,7 +23,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
                 return;
             }
 
-            _context = ffmpeg.avcodec_alloc_context3(_codec);
+            _context = FFmpegApi.avcodec_alloc_context3(_codec);
             if (_context == null)
             {
                 Logger.Error?.PrintMsg(LogClass.FFmpeg, "Codec context couldn't be allocated.");
@@ -33,14 +31,14 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
                 return;
             }
 
-            if (ffmpeg.avcodec_open2(_context, _codec, null) != 0)
+            if (FFmpegApi.avcodec_open2(_context, _codec, null) != 0)
             {
                 Logger.Error?.PrintMsg(LogClass.FFmpeg, "Codec couldn't be opened.");
 
                 return;
             }
 
-            _packet = ffmpeg.av_packet_alloc();
+            _packet = FFmpegApi.av_packet_alloc();
             if (_packet == null)
             {
                 Logger.Error?.PrintMsg(LogClass.FFmpeg, "Packet couldn't be allocated.");
@@ -48,52 +46,39 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
                 return;
             }
 
-            _decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(_codec->decode.Pointer);
+            int avCodecRawVersion = FFmpegApi.avcodec_version();
+            int avCodecMajorVersion = avCodecRawVersion >> 16;
+            int avCodecMinorVersion = (avCodecRawVersion >> 8) & 0xFF;
+
+            // libavcodec 59.24 changed AvCodec to move its private API and also move the codec function to an union.
+            if (avCodecMajorVersion > 59 || (avCodecMajorVersion == 59 && avCodecMinorVersion > 24))
+            {
+                _decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodec*)_codec)->CodecCallback);
+            }
+            // libavcodec 59.x changed AvCodec private API layout.
+            else if (avCodecMajorVersion == 59)
+            {
+                _decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodecLegacy<AVCodec>*)_codec)->Decode);
+            }
+            // libavcodec 58.x and lower
+            else
+            {
+                _decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodecLegacy<AVCodecLegacy>*)_codec)->Decode);
+            }
         }
 
         static FFmpegContext()
         {
-            SetRootPath();
-
             _logFunc = Log;
 
             // Redirect log output.
-            ffmpeg.av_log_set_level(ffmpeg.AV_LOG_MAX_OFFSET);
-            ffmpeg.av_log_set_callback(_logFunc);
+            FFmpegApi.av_log_set_level(AVLog.MaxOffset);
+            FFmpegApi.av_log_set_callback(_logFunc);
         }
 
-        private static void SetRootPath()
+        private static void Log(void* ptr, AVLog level, string format, byte* vl)
         {
-            if (OperatingSystem.IsLinux())
-            {
-                // Configure FFmpeg search path
-                Process lddProcess = Process.Start(new ProcessStartInfo
-                {
-                    FileName               = "/bin/sh",
-                    Arguments              = "-c \"ldd $(which ffmpeg 2>/dev/null) | grep libavfilter\" 2>/dev/null",
-                    UseShellExecute        = false,
-                    RedirectStandardOutput = true
-                });
-
-                string lddOutput = lddProcess.StandardOutput.ReadToEnd();
-
-                lddProcess.WaitForExit();
-                lddProcess.Close();
-
-                if (lddOutput.Contains(" => "))
-                {
-                    ffmpeg.RootPath = Path.GetDirectoryName(lddOutput.Split(" => ")[1]);
-                }
-                else
-                {
-                    Logger.Error?.PrintMsg(LogClass.FFmpeg, "FFmpeg wasn't found. Make sure that you have it installed and up to date.");
-                }
-            }
-        }
-
-        private static void Log(void* p0, int level, string format, byte* vl)
-        {
-            if (level > ffmpeg.av_log_get_level())
+            if (level > FFmpegApi.av_log_get_level())
             {
                 return;
             }
@@ -102,65 +87,67 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
             byte* lineBuffer = stackalloc byte[lineSize];
             int printPrefix = 1;
 
-            ffmpeg.av_log_format_line(p0, level, format, vl, lineBuffer, lineSize, &printPrefix);
+            FFmpegApi.av_log_format_line(ptr, level, format, vl, lineBuffer, lineSize, &printPrefix);
 
             string line = Marshal.PtrToStringAnsi((IntPtr)lineBuffer).Trim();
 
             switch (level)
             {
-                case ffmpeg.AV_LOG_PANIC:
-                case ffmpeg.AV_LOG_FATAL:
-                case ffmpeg.AV_LOG_ERROR:
+                case AVLog.Panic:
+                case AVLog.Fatal:
+                case AVLog.Error:
                     Logger.Error?.Print(LogClass.FFmpeg, line);
                     break;
-                case ffmpeg.AV_LOG_WARNING:
+                case AVLog.Warning:
                     Logger.Warning?.Print(LogClass.FFmpeg, line);
                     break;
-                case ffmpeg.AV_LOG_INFO:
+                case AVLog.Info:
                     Logger.Info?.Print(LogClass.FFmpeg, line);
                     break;
-                case ffmpeg.AV_LOG_VERBOSE:
-                case ffmpeg.AV_LOG_DEBUG:
-                case ffmpeg.AV_LOG_TRACE:
+                case AVLog.Verbose:
+                case AVLog.Debug:
                     Logger.Debug?.Print(LogClass.FFmpeg, line);
                     break;
+                case AVLog.Trace:
+                    Logger.Trace?.Print(LogClass.FFmpeg, line);
+                    break;
             }
         }
 
         public int DecodeFrame(Surface output, ReadOnlySpan<byte> bitstream)
         {
-            ffmpeg.av_frame_unref(output.Frame);
+            FFmpegApi.av_frame_unref(output.Frame);
 
             int result;
             int gotFrame;
 
             fixed (byte* ptr = bitstream)
             {
-                _packet->data = ptr;
-                _packet->size = bitstream.Length;
+                _packet->Data = ptr;
+                _packet->Size = bitstream.Length;
                 result = _decodeFrame(_context, output.Frame, &gotFrame, _packet);
             }
 
             if (gotFrame == 0)
             {
-                ffmpeg.av_frame_unref(output.Frame);
+                FFmpegApi.av_frame_unref(output.Frame);
 
                 // If the frame was not delivered, it was probably delayed.
                 // Get the next delayed frame by passing a 0 length packet.
-                _packet->data = null;
-                _packet->size = 0;
+                _packet->Data = null;
+                _packet->Size = 0;
                 result = _decodeFrame(_context, output.Frame, &gotFrame, _packet);
 
                 // We need to set B frames to 0 as we already consumed all delayed frames.
                 // This prevents the decoder from trying to return a delayed frame next time.
-                _context->has_b_frames = 0;
+                _context->HasBFrames = 0;
             }
 
-            ffmpeg.av_packet_unref(_packet);
+            FFmpegApi.av_packet_unref(_packet);
 
             if (gotFrame == 0)
             {
-                ffmpeg.av_frame_unref(output.Frame);
+                FFmpegApi.av_frame_unref(output.Frame);
 
                 return -1;
             }
@@ -172,14 +159,14 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
         {
             fixed (AVPacket** ppPacket = &_packet)
             {
-                ffmpeg.av_packet_free(ppPacket);
+                FFmpegApi.av_packet_free(ppPacket);
             }
 
-            ffmpeg.avcodec_close(_context);
+            FFmpegApi.avcodec_close(_context);
 
             fixed (AVCodecContext** ppContext = &_context)
             {
-                ffmpeg.avcodec_free_context(ppContext);
+                FFmpegApi.avcodec_free_context(ppContext);
             }
         }
     }

+ 1 - 1
Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs

@@ -1,4 +1,4 @@
-using FFmpeg.AutoGen;
+using Ryujinx.Graphics.Nvdec.FFmpeg.Native;
 using Ryujinx.Graphics.Video;
 using System;
 

+ 25 - 0
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs

@@ -0,0 +1,25 @@
+using System;
+
+namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
+{
+    struct AVCodec
+    {
+#pragma warning disable CS0649
+        public unsafe byte* Name;
+        public unsafe byte* LongName;
+        public int Type;
+        public AVCodecID Id;
+        public int Capabilities;
+        public byte MaxLowRes;
+        public unsafe AVRational* SupportedFramerates;
+        public IntPtr PixFmts;
+        public IntPtr SupportedSamplerates;
+        public IntPtr SampleFmts;
+        // Deprecated
+        public unsafe ulong* ChannelLayouts;
+        public unsafe IntPtr PrivClass;
+        public IntPtr Profiles;
+        public unsafe byte* WrapperName;
+#pragma warning restore CS0649
+    }
+}

+ 171 - 0
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs

@@ -0,0 +1,171 @@
+using Ryujinx.Common.Memory;
+using System;
+
+namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
+{
+    struct AVCodecContext
+    {
+#pragma warning disable CS0649
+        public unsafe IntPtr AvClass;
+        public int LogLevelOffset;
+        public int CodecType;
+        public unsafe AVCodecLegacy* Codec;
+        public AVCodecID CodecId;
+        public uint CodecTag;
+        public IntPtr PrivData;
+        public IntPtr Internal;
+        public IntPtr Opaque;
+        public long BitRate;
+        public int BitRateTolerance;
+        public int GlobalQuality;
+        public int CompressionLevel;
+        public int Flags;
+        public int Flags2;
+        public IntPtr ExtraData;
+        public int ExtraDataSize;
+        public AVRational TimeBase;
+        public int TicksPerFrame;
+        public int Delay;
+        public int Width;
+        public int Height;
+        public int CodedWidth;
+        public int CodedHeight;
+        public int GopSize;
+        public int PixFmt;
+        public IntPtr DrawHorizBand;
+        public IntPtr GetFormat;
+        public int MaxBFrames;
+        public float BQuantFactor;
+        public float BQuantOffset;
+        public int HasBFrames;
+        public float IQuantFactor;
+        public float IQuantOffset;
+        public float LumiMasking;
+        public float TemporalCplxMasking;
+        public float SpatialCplxMasking;
+        public float PMasking;
+        public float DarkMasking;
+        public int SliceCount;
+        public IntPtr SliceOffset;
+        public AVRational SampleAspectRatio;
+        public int MeCmp;
+        public int MeSubCmp;
+        public int MbCmp;
+        public int IldctCmp;
+        public int DiaSize;
+        public int LastPredictorCount;
+        public int MePreCmp;
+        public int PreDiaSize;
+        public int MeSubpelQuality;
+        public int MeRange;
+        public int SliceFlags;
+        public int MbDecision;
+        public IntPtr IntraMatrix;
+        public IntPtr InterMatrix;
+        public int IntraDcPrecision;
+        public int SkipTop;
+        public int SkipBottom;
+        public int MbLmin;
+        public int MbLmax;
+        public int BidirRefine;
+        public int KeyintMin;
+        public int Refs;
+        public int Mv0Threshold;
+        public int ColorPrimaries;
+        public int ColorPrc;
+        public int Colorspace;
+        public int ColorRange;
+        public int ChromaSampleLocation;
+        public int Slices;
+        public int FieldOrder;
+        public int SampleRate;
+        public int Channels;
+        public int SampleFmt;
+        public int FrameSize;
+        public int FrameNumber;
+        public int BlockAlign;
+        public int CutOff;
+        public ulong ChannelLayout;
+        public ulong RequestChannelLayout;
+        public int AudioServiceType;
+        public int RequestSampleFmt;
+        public IntPtr GetBuffer2;
+        public float QCompress;
+        public float QBlur;
+        public int QMin;
+        public int QMax;
+        public int MaxQdiff;
+        public int RcBufferSize;
+        public int RcOverrideCount;
+        public IntPtr RcOverride;
+        public long RcMaxRate;
+        public long RcMinRate;
+        public float RcMax_available_vbv_use;
+        public float RcMin_vbv_overflow_use;
+        public int RcInitialBufferOccupancy;
+        public int Trellis;
+        public IntPtr StatsOut;
+        public IntPtr StatsIn;
+        public int WorkaroundBugs;
+        public int StrictStdCompliance;
+        public int ErrorConcealment;
+        public int Debug;
+        public int ErrRecognition;
+        public long ReorderedOpaque;
+        public IntPtr HwAccel;
+        public IntPtr HwAccelContext;
+        public Array8<ulong> Error;
+        public int DctAlgo;
+        public int IdctAlgo;
+        public int BitsPerCodedSample;
+        public int BitsPerRawSample;
+        public int LowRes;
+        public int ThreadCount;
+        public int ThreadType;
+        public int ActiveThreadType;
+        public int ThreadSafeCallbacks;
+        public IntPtr Execute;
+        public IntPtr Execute2;
+        public int NsseWeight;
+        public int Profile;
+        public int Level;
+        public int SkipLoopFilter;
+        public int SkipIdct;
+        public int SkipFrame;
+        public IntPtr SubtitleHeader;
+        public int SubtitleHeaderSize;
+        public int InitialPadding;
+        public AVRational Framerate;
+        public int SwPixFmt;
+        public AVRational PktTimebase;
+        public IntPtr CodecDescriptor;
+        public long PtsCorrectionNumFaultyPts;
+        public long PtsCorrectionNumFaultyDts;
+        public long PtsCorrectionLastPts;
+        public long PtsCorrectionLastDts;
+        public IntPtr SubCharenc;
+        public int SubCharencMode;
+        public int SkipAlpha;
+        public int SeekPreroll;
+        public int DebugMv;
+        public IntPtr ChromaIntraMatrix;
+        public IntPtr DumpSeparator;
+        public IntPtr CodecWhitelist;
+        public uint Properties;
+        public IntPtr CodedSideData;
+        public int NbCodedSideData;
+        public IntPtr HwFramesCtx;
+        public int SubTextFormat;
+        public int TrailingPadding;
+        public long MaxPixels;
+        public IntPtr HwDeviceCtx;
+        public int HwAccelFlags;
+        public int applyCropping;
+        public int ExtraHwFrames;
+        public int DiscardDamagedPercentage;
+        public long MaxSamples;
+        public int ExportSideData;
+        public IntPtr GetEncodeBuffer;
+#pragma warning restore CS0649
+    }
+}

+ 8 - 0
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecID.cs

@@ -0,0 +1,8 @@
+namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
+{
+    enum AVCodecID
+    {
+        AV_CODEC_ID_H264 = 27,
+        AV_CODEC_ID_VP8 = 139,
+    }
+}

+ 26 - 0
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecLegacy.cs

@@ -0,0 +1,26 @@
+using System;
+
+namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
+{
+    struct AVCodecLegacy
+    {
+#pragma warning disable CS0649
+        public unsafe byte* Name;
+        public unsafe byte* LongName;
+        public int Type;
+        public AVCodecID Id;
+        public int Capabilities;
+        public byte MaxLowRes;
+        public unsafe AVRational* SupportedFramerates;
+        public IntPtr PixFmts;
+        public IntPtr SupportedSamplerates;
+        public IntPtr SampleFmts;
+        // Deprecated
+        public unsafe ulong* ChannelLayouts;
+        public unsafe IntPtr PrivClass;
+        public IntPtr Profiles;
+        public unsafe byte* WrapperName;
+        public IntPtr ChLayouts;
+#pragma warning restore CS0649
+    }
+}

+ 37 - 0
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs

@@ -0,0 +1,37 @@
+using Ryujinx.Common.Memory;
+using System;
+
+namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
+{
+    struct AVFrame
+    {
+#pragma warning disable CS0649
+        public Array8<IntPtr> Data;
+        public Array8<int> LineSize;
+        public IntPtr ExtendedData;
+        public int Width;
+        public int Height;
+        public int NumSamples;
+        public int Format;
+        public int KeyFrame;
+        public int PictureType;
+        public AVRational SampleAspectRatio;
+        public long Pts;
+        public long PktDts;
+        public AVRational TimeBase;
+        public int CodedPictureNumber;
+        public int DisplayPictureNumber;
+        public int Quality;
+        public IntPtr Opaque;
+        public int RepeatPicture;
+        public int InterlacedFrame;
+        public int TopFieldFirst;
+        public int PaletteHasChanged;
+        public long ReorderedOpaque;
+        public int SampleRate;
+        public ulong ChannelLayout;
+#pragma warning restore CS0649
+
+        // NOTE: There is more after, but the layout kind of changed a bit and we don't need more than this. This is safe as we only manipulate this behind a reference.
+    }
+}

+ 15 - 0
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVLog.cs

@@ -0,0 +1,15 @@
+namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
+{
+    enum AVLog
+    {
+        Panic = 0,
+        Fatal = 8,
+        Error = 16,
+        Warning = 24,
+        Info = 32,
+        Verbose = 40,
+        Debug = 48,
+        Trace = 56,
+        MaxOffset = 64
+    }
+}

+ 26 - 0
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVPacket.cs

@@ -0,0 +1,26 @@
+using System;
+
+using AVBufferRef = System.IntPtr;
+
+namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
+{
+    struct AVPacket
+    {
+#pragma warning disable CS0649
+        public unsafe AVBufferRef *Buf;
+        public long Pts;
+        public long Dts;
+        public unsafe byte* Data;
+        public int Size;
+        public int StreamIndex;
+        public int Flags;
+        public IntPtr SizeData;
+        public int SizeDataElems;
+        public long Duration;
+        public long Position;
+        public IntPtr Opaque;
+        public unsafe AVBufferRef *OpaqueRef;
+        public AVRational TimeBase;
+#pragma warning restore CS0649
+    }
+}

+ 8 - 0
Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVRational.cs

@@ -0,0 +1,8 @@
+namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
+{
+    public struct AVRational
+    {
+        public int Numerator;
+        public int Denominator;
+    }
+}

+ 23 - 0
Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs

@@ -0,0 +1,23 @@
+using System;
+
+namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
+{
+    struct FFCodec
+    {
+        public unsafe delegate int AVCodec_decode(AVCodecContext* avctx, void* outdata, int* got_frame_ptr, AVPacket* avpkt);
+
+#pragma warning disable CS0649
+        public AVCodec Base;
+        public int CapsInternalOrCbType;
+        public int PrivDataSize;
+        public IntPtr UpdateThreadContext;
+        public IntPtr UpdateThreadContextForUser;
+        public IntPtr Defaults;
+        public IntPtr InitStaticData;
+        public IntPtr Init;
+        public IntPtr CodecCallback;
+#pragma warning restore CS0649
+
+        // NOTE: There is more after, but the layout kind of changed a bit and we don't need more than this. This is safe as we only manipulate this behind a reference.
+    }
+}

+ 23 - 0
Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs

@@ -0,0 +1,23 @@
+using System;
+
+namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
+{
+    struct FFCodecLegacy<T> where T: struct
+    {
+#pragma warning disable CS0649
+        public T Base;
+        public uint CapsInternalOrCbType;
+        public int PrivDataSize;
+        public IntPtr UpdateThreadContext;
+        public IntPtr UpdateThreadContextForUser;
+        public IntPtr Defaults;
+        public IntPtr InitStaticData;
+        public IntPtr Init;
+        public IntPtr EncodeSub;
+        public IntPtr Encode2;
+        public IntPtr Decode;
+#pragma warning restore CS0649
+
+        // NOTE: There is more after, but the layout kind of changed a bit and we don't need more than this. This is safe as we only manipulate this behind a reference.
+    }
+}

+ 129 - 0
Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs

@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
+{
+    static class FFmpegApi
+    {
+        public const string AvCodecLibraryName = "avcodec";
+        public const string AvUtilLibraryName = "avutil";
+
+        private static readonly Dictionary<string, (int, int)> _librariesWhitelist = new Dictionary<string, (int, int)>
+        {
+            { 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 = minVersion; version <= maxVersion; 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) =>
+            {
+                IntPtr handle;
+
+                if (name == AvUtilLibraryName && TryLoadWhitelistedLibrary(AvUtilLibraryName, assembly, path, out 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);
+
+        [DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern AVFrame* av_frame_alloc();
+
+        [DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern void av_frame_unref(AVFrame* frame);
+
+        [DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern void av_free(AVFrame* frame);
+
+        [DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern void av_log_set_level(AVLog level);
+
+        [DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern void av_log_set_callback(av_log_set_callback_callback callback);
+
+        [DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern AVLog av_log_get_level();
+
+        [DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern void av_log_format_line(void* ptr, AVLog level, [MarshalAs(UnmanagedType.LPUTF8Str)] string fmt, byte* vl, byte* line, int lineSize, int* printPrefix);
+
+        [DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern AVCodec* avcodec_find_decoder(AVCodecID id);
+
+        [DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern AVCodecContext* avcodec_alloc_context3(AVCodec* codec);
+
+        [DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern int avcodec_open2(AVCodecContext* avctx, AVCodec* codec, void **options);
+
+        [DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern int avcodec_close(AVCodecContext* avctx);
+
+        [DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern void avcodec_free_context(AVCodecContext** avctx);
+
+        [DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern AVPacket* av_packet_alloc();
+
+        [DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern void av_packet_unref(AVPacket* pkt);
+
+        [DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern void av_packet_free(AVPacket** pkt);
+
+        [DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
+        internal static unsafe extern int avcodec_version();
+    }
+}

+ 1 - 5
Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj

@@ -1,14 +1,10 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>net6.0</TargetFramework>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
 
-  <ItemGroup>
-    <PackageReference Include="FFmpeg.AutoGen" Version="4.4.1" />
-  </ItemGroup>
-
   <ItemGroup>
     <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
     <ProjectReference Include="..\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj" />

+ 12 - 12
Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs

@@ -1,4 +1,4 @@
-using FFmpeg.AutoGen;
+using Ryujinx.Graphics.Nvdec.FFmpeg.Native;
 using Ryujinx.Graphics.Video;
 using System;
 
@@ -11,31 +11,31 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
         public int RequestedWidth { get; }
         public int RequestedHeight { get; }
 
-        public Plane YPlane => new Plane((IntPtr)Frame->data[0], Stride * Height);
-        public Plane UPlane => new Plane((IntPtr)Frame->data[1], UvStride * UvHeight);
-        public Plane VPlane => new Plane((IntPtr)Frame->data[2], UvStride * UvHeight);
+        public Plane YPlane => new Plane((IntPtr)Frame->Data[0], Stride * Height);
+        public Plane UPlane => new Plane((IntPtr)Frame->Data[1], UvStride * UvHeight);
+        public Plane VPlane => new Plane((IntPtr)Frame->Data[2], UvStride * UvHeight);
 
-        public FrameField Field => Frame->interlaced_frame != 0 ? FrameField.Interlaced : FrameField.Progressive;
+        public FrameField Field => Frame->InterlacedFrame != 0 ? FrameField.Interlaced : FrameField.Progressive;
 
-        public int Width => Frame->width;
-        public int Height => Frame->height;
-        public int Stride => Frame->linesize[0];
+        public int Width => Frame->Width;
+        public int Height => Frame->Height;
+        public int Stride => Frame->LineSize[0];
         public int UvWidth => (Width + 1) >> 1;
         public int UvHeight => (Height + 1) >> 1;
-        public int UvStride => Frame->linesize[1];
+        public int UvStride => Frame->LineSize[1];
 
         public Surface(int width, int height)
         {
             RequestedWidth = width;
             RequestedHeight = height;
 
-            Frame = ffmpeg.av_frame_alloc();
+            Frame = FFmpegApi.av_frame_alloc();
         }
 
         public void Dispose()
         {
-            ffmpeg.av_frame_unref(Frame);
-            ffmpeg.av_free(Frame);
+            FFmpegApi.av_frame_unref(Frame);
+            FFmpegApi.av_free(Frame);
         }
     }
 }

+ 1 - 1
Ryujinx.Graphics.Nvdec.FFmpeg/Vp8/Decoder.cs

@@ -1,4 +1,4 @@
-using FFmpeg.AutoGen;
+using Ryujinx.Graphics.Nvdec.FFmpeg.Native;
 using Ryujinx.Graphics.Video;
 using System;
 

+ 1 - 1
Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj

@@ -10,7 +10,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.4.0-build7" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
+    <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 1
Ryujinx/Ryujinx.csproj

@@ -19,7 +19,7 @@
   <ItemGroup>
     <PackageReference Include="GtkSharp" Version="3.22.25.128" />
     <PackageReference Include="GtkSharp.Dependencies" Version="1.1.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
-    <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.4.0-build9" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
+    <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
     <PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
     <PackageReference Include="OpenTK.Graphics" Version="4.7.2" />
     <PackageReference Include="SPB" Version="0.0.4-build17" />