Skip to content

Instantly share code, notes, and snippets.

@matthewrdev
Created April 5, 2023 04:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewrdev/81ad78989af95dc6fbff87d2e6681009 to your computer and use it in GitHub Desktop.
Save matthewrdev/81ad78989af95dc6fbff87d2e6681009 to your computer and use it in GitHub Desktop.
Renders an OpenCV frame via SkiaSharp.
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