using System; using System.IO; using SharpAvi; using SharpAvi.Codecs; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; namespace DispenserUI.ViewModels.Recorder; public class MJpegImageVideoEncoder : IVideoEncoder { private readonly int _width; private readonly int _height; private readonly JpegEncoder _jpegEncoder; private readonly MemoryStream _buffer; /// /// Creates a new instance of . /// /// Frame width. /// Frame height. /// /// Compression quality in the range [1..100]. /// Less values mean less size and lower image quality. /// public MJpegImageVideoEncoder(int width, int height, int quality) { this._width = width; this._height = height; _buffer = new MemoryStream(this.MaxEncodedSize); _jpegEncoder = new JpegEncoder { Quality = quality, }; } /// Video codec. public FourCC Codec => CodecIds.MotionJpeg; /// Number of bits per pixel in encoded image. public BitsPerPixel BitsPerPixel => BitsPerPixel.Bpp24; /// Maximum size of encoded frmae. public int MaxEncodedSize => Math.Max(_width * _height * 3, 1024); /// Encodes a frame. public int EncodeFrame( byte[] source, int srcOffset, byte[] destination, int destOffset, out bool isKeyFrame) { int num; using (MemoryStream destination1 = new MemoryStream(destination)) { destination1.Position = destOffset; num = LoadAndEncodeImage(source.AsSpan(srcOffset), destination1); } isKeyFrame = true; return num; } /// Encodes a frame. public int EncodeFrame(ReadOnlySpan source, Span destination, out bool isKeyFrame) { _buffer.SetLength(0L); var length = LoadAndEncodeImage(source, _buffer); _buffer.GetBuffer().AsSpan(0, length).CopyTo(destination); isKeyFrame = true; return length; } private int LoadAndEncodeImage(ReadOnlySpan source, Stream destination) { var position = (int)destination.Position; using (var image = Image.LoadPixelData(source, _width, _height)) _jpegEncoder.Encode(image, destination); destination.Flush(); return (int)(destination.Position - position); } }