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
This commit is contained in:
Mary 2022-07-14 15:13:23 +02:00 committed by GitHub
parent 70ec5def9c
commit c5bddfeab8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 563 additions and 89 deletions

View file

@ -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);
}
}
}