using Sdcb.FFmpeg.Raw; using Sdcb.FFmpeg.Swscales; using Sdcb.FFmpeg.Utils; namespace T3.VideoServices; /// /// Converts a software-decoded planar YUV frame to packed RGBA on the CPU via swscale — RGBA8 for SDR, /// RGBA16 (Rgba64le) for HDR/10-bit. The result is uploaded to a GPU texture by the caller. The /// hardware-decode path instead converts NV12 on the GPU (no CPU round-trip); this is the software fallback. /// Owned by a single decode worker; the destination frame is reused across calls. /// public sealed class SoftwareFrameConverter : IDisposable { /// Packed output pixel format: Rgba (SDR) or Rgba64le (HDR). public AVPixelFormat OutputFormat { get; } public int BytesPerPixel { get; } public SoftwareFrameConverter(bool hdr) { OutputFormat = hdr ? AVPixelFormat.Rgba64le : AVPixelFormat.Rgba; BytesPerPixel = hdr ? 8 : 4; } /// /// Converts into a reused packed-RGBA frame and returns it (valid until the /// next call). Read pixels via Data[0]/Linesize[0] or ToImageBuffer. /// public Frame Convert(Frame source) { if (_destination == null || _width != source.Width || _height != source.Height) { _destination?.Dispose(); _width = source.Width; _height = source.Height; _destination = Frame.CreateVideo(_width, _height, OutputFormat); _destination.EnsureBuffer(1); } _converter.ConvertFrame(source, _destination, SWS.Bilinear); return _destination; } public void Dispose() { _destination?.Dispose(); _converter.Free(); } private readonly VideoFrameConverter _converter = new(); private Frame? _destination; private int _width; private int _height; }