diff options
Diffstat (limited to 'Ryujinx.Graphics.Nvdec/VDec/FFmpeg.cs')
| -rw-r--r-- | Ryujinx.Graphics.Nvdec/VDec/FFmpeg.cs | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Nvdec/VDec/FFmpeg.cs b/Ryujinx.Graphics.Nvdec/VDec/FFmpeg.cs new file mode 100644 index 00000000..ccd01f0d --- /dev/null +++ b/Ryujinx.Graphics.Nvdec/VDec/FFmpeg.cs @@ -0,0 +1,168 @@ +using FFmpeg.AutoGen; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.VDec +{ + static unsafe class FFmpegWrapper + { + private static AVCodec* _codec; + private static AVCodecContext* _context; + private static AVFrame* _frame; + private static SwsContext* _scalerCtx; + + private static int _scalerWidth; + private static int _scalerHeight; + + public static bool IsInitialized { get; private set; } + + public static void H264Initialize() + { + EnsureCodecInitialized(AVCodecID.AV_CODEC_ID_H264); + } + + public static void Vp9Initialize() + { + EnsureCodecInitialized(AVCodecID.AV_CODEC_ID_VP9); + } + + private static void EnsureCodecInitialized(AVCodecID codecId) + { + if (IsInitialized) + { + Uninitialize(); + } + + _codec = ffmpeg.avcodec_find_decoder(codecId); + _context = ffmpeg.avcodec_alloc_context3(_codec); + _frame = ffmpeg.av_frame_alloc(); + + ffmpeg.avcodec_open2(_context, _codec, null); + + IsInitialized = true; + } + + public static int DecodeFrame(byte[] data) + { + if (!IsInitialized) + { + throw new InvalidOperationException("Tried to use uninitialized codec!"); + } + + AVPacket packet; + + ffmpeg.av_init_packet(&packet); + + fixed (byte* ptr = data) + { + packet.data = ptr; + packet.size = data.Length; + + ffmpeg.avcodec_send_packet(_context, &packet); + } + + return ffmpeg.avcodec_receive_frame(_context, _frame); + } + + public static FFmpegFrame GetFrame() + { + if (!IsInitialized) + { + throw new InvalidOperationException("Tried to use uninitialized codec!"); + } + + AVFrame managedFrame = Marshal.PtrToStructure<AVFrame>((IntPtr)_frame); + + byte*[] data = managedFrame.data.ToArray(); + + return new FFmpegFrame() + { + Width = managedFrame.width, + Height = managedFrame.height, + + LumaPtr = data[0], + ChromaBPtr = data[1], + ChromaRPtr = data[2] + }; + } + + public static FFmpegFrame GetFrameRgba() + { + if (!IsInitialized) + { + throw new InvalidOperationException("Tried to use uninitialized codec!"); + } + + AVFrame managedFrame = Marshal.PtrToStructure<AVFrame>((IntPtr)_frame); + + EnsureScalerSetup(managedFrame.width, managedFrame.height); + + byte*[] data = managedFrame.data.ToArray(); + + int[] lineSizes = managedFrame.linesize.ToArray(); + + byte[] dst = new byte[managedFrame.width * managedFrame.height * 4]; + + fixed (byte* ptr = dst) + { + byte*[] dstData = new byte*[] { ptr }; + + int[] dstLineSizes = new int[] { managedFrame.width * 4 }; + + ffmpeg.sws_scale(_scalerCtx, data, lineSizes, 0, managedFrame.height, dstData, dstLineSizes); + } + + return new FFmpegFrame() + { + Width = managedFrame.width, + Height = managedFrame.height, + + Data = dst + }; + } + + private static void EnsureScalerSetup(int width, int height) + { + if (width == 0 || height == 0) + { + return; + } + + if (_scalerCtx == null || _scalerWidth != width || _scalerHeight != height) + { + FreeScaler(); + + _scalerCtx = ffmpeg.sws_getContext( + width, height, AVPixelFormat.AV_PIX_FMT_YUV420P, + width, height, AVPixelFormat.AV_PIX_FMT_RGBA, 0, null, null, null); + + _scalerWidth = width; + _scalerHeight = height; + } + } + + public static void Uninitialize() + { + if (IsInitialized) + { + ffmpeg.av_frame_unref(_frame); + ffmpeg.av_free(_frame); + ffmpeg.avcodec_close(_context); + + FreeScaler(); + + IsInitialized = false; + } + } + + private static void FreeScaler() + { + if (_scalerCtx != null) + { + ffmpeg.sws_freeContext(_scalerCtx); + + _scalerCtx = null; + } + } + } +}
\ No newline at end of file |
