Editor/HumanoidMocap/Inference/VideoFrameLayout.cs
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
namespace HumanoidMocap.Inference;
/// <summary>Visible RGB32 aperture and display orientation, independent of coded padding.
/// Rotation undoes the counter-clockwise content rotation reported by Media Foundation.</summary>
internal sealed class VideoFrameLayout
{
readonly int bufferWidth,bufferHeight,x,y,width,height,rotation;
public int Width=>rotation is 90 or 270?height:width;
public int Height=>rotation is 90 or 270?width:height;
public VideoFrameLayout(int bufferWidth,int bufferHeight,int x,int y,int width,int height,int rotation)
{
if(bufferWidth<=0||bufferHeight<=0||(long)bufferWidth*bufferHeight>33_177_600)
throw new InvalidDataException("Video exceeds the 8K decode limit.");
if(x<0||y<0||width<=0||height<=0||(long)x+width>bufferWidth||(long)y+height>bufferHeight)
throw new InvalidDataException("Video display aperture is outside its decoded frame.");
if(rotation is not (0 or 90 or 180 or 270))throw new InvalidDataException("Unsupported video rotation metadata.");
this.bufferWidth=bufferWidth;this.bufferHeight=bufferHeight;this.x=x;this.y=y;
this.width=width;this.height=height;this.rotation=rotation;
}
public byte[] CopyRgba(IntPtr data,int length,int stride,CancellationToken token)
{
var pitch=Math.Abs((long)stride);
if(pitch<bufferWidth*4L||pitch*bufferHeight>length)
throw new InvalidDataException("Decoded buffer is shorter than its frame dimensions or stride.");
var result=new byte[checked(Width*Height*4)];
var row=rotation==0?null:new byte[checked(width*4)];
for(var sourceY=0;sourceY<height;sourceY++)
{
token.ThrowIfCancellationRequested();
var bufferY=stride<0?bufferHeight-1-y-sourceY:y+sourceY;
var source=IntPtr.Add(data,checked((int)(bufferY*pitch+x*4L)));
if(rotation==0)Marshal.Copy(source,result,sourceY*width*4,width*4);
else
{
Marshal.Copy(source,row!,0,width*4);
for(var sourceX=0;sourceX<width;sourceX++)
{
var destination=rotation switch
{
90=>(sourceX*Width+height-1-sourceY)*4,
180=>((height-1-sourceY)*Width+width-1-sourceX)*4,
_=>((width-1-sourceX)*Width+sourceY)*4
};
var offset=sourceX*4;
result[destination]=row![offset];result[destination+1]=row[offset+1];
result[destination+2]=row[offset+2];
}
}
}
for(var i=0;i<result.Length;i+=4){(result[i],result[i+2])=(result[i+2],result[i]);result[i+3]=255;}
return result;
}
}