Created
April 5, 2023 04:06
-
-
Save matthewrdev/81ad78989af95dc6fbff87d2e6681009 to your computer and use it in GitHub Desktop.
Renders an OpenCV frame via SkiaSharp.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Text; | |
using Microsoft.Extensions.Primitives; | |
using Microsoft.Maui.ApplicationModel; | |
using OpenCvSharp; | |
using SkiaSharp; | |
using static System.Net.Mime.MediaTypeNames; | |
namespace MyApp | |
{ | |
public class SkiaVideoFrameRenderer : IDisposable | |
{ | |
private int frameWidth; | |
private int frameHeight; | |
private object frameLock = new object(); | |
private Mat frame; | |
private SKPaint paint; | |
public SkiaVideoFrameRenderer() | |
{ | |
paint = new SKPaint | |
{ | |
IsAntialias = true, | |
FilterQuality = SKFilterQuality.High | |
}; | |
} | |
byte[] frameBuffer; | |
public void SetFrame(Mat frame, int width, int height) | |
{ | |
lock (frameLock) | |
{ | |
if (this.frame != null) | |
{ | |
if (this.frame.IsDisposed == false) | |
{ | |
this.frame.Release(); | |
this.frame.Dispose(); | |
} | |
this.frame = null; | |
} | |
this.frame = frame; | |
if (frame != null) | |
{ | |
framesReceived++; | |
} | |
if (frame == null) | |
{ | |
frameBuffer = null; | |
} | |
if (width != this.frameWidth || height != this.frameHeight) | |
{ | |
frameBuffer = new byte[width * height * 4]; | |
} | |
frameWidth = width; | |
frameHeight = height; | |
} | |
} | |
public void RenderFrame(SKRectI canvasRect, SKCanvas canvas) | |
{ | |
lock (frameLock) | |
{ | |
if (this.frame == null || frameBuffer == null) | |
{ | |
return; | |
} | |
VideoFrameDecoderHelper.DecodeInto(this.frame, ref frameBuffer); | |
canvas.Clear(SKColors.Black); | |
unsafe | |
{ | |
var size = SkiaHelpers.ResizeKeepAspect(frameWidth, frameHeight, canvasRect.Width, canvasRect.Height); | |
// The following code block performs a buffer copy of the open CV frame using pointer dereferencing (rather than C#'s array derefencing operator). | |
// This code AOT compiles in release mode and will run 30x-60x faster than in debug mode. | |
// Debug mode may be 50-60ms, release may be between 1-2ms. | |
fixed (byte* framePointer = &frameBuffer[0]) | |
{ | |
var imageInfo = new SKImageInfo(frameWidth, frameHeight, SKColorType.Rgba8888); | |
using (var image = SKImage.FromPixels(imageInfo, (IntPtr)framePointer)) | |
{ | |
var rect = SkiaHelpers.GetCenteredRect(canvasRect, size); | |
canvas.DrawImage(image, rect, paint); | |
} | |
} | |
} | |
} | |
} | |
public void Dispose() | |
{ | |
if (paint != null) | |
{ | |
paint.Dispose(); | |
paint = null; | |
} | |
if (this.frame != null) | |
{ | |
if (this.frame.IsDisposed == false) | |
{ | |
this.frame.Release(); | |
this.frame.Dispose(); | |
} | |
this.frame = null; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment