C# 快速视频显示WPF

C# 快速视频显示WPF,c#,wpf,performance,video,frame-rate,C#,Wpf,Performance,Video,Frame Rate,我正在开发一个WPF应用程序,它需要以快速帧速率显示多个视频流(我们希望30 fps)。视频流是1920x1080原始(RGB24)帧(它们存储在System.Drawing.Bitmap中)。有人对如何实现这一目标有什么想法吗 更多详情: 我们以前的尝试使用了标准的WPF图像控件,为每一帧更改其源。这对单个流很有效,但现在我们必须渲染多个流,速度正在减慢 我们还尝试使用Direct2D处理图形,使用D3D9共享曲面作为图像控件的源。虽然速度更快,但我们仍然无法从中获得稳定的30fps(当事情

我正在开发一个WPF应用程序,它需要以快速帧速率显示多个视频流(我们希望30 fps)。视频流是1920x1080原始(RGB24)帧(它们存储在System.Drawing.Bitmap中)。有人对如何实现这一目标有什么想法吗

更多详情:

  • 我们以前的尝试使用了标准的WPF图像控件,为每一帧更改其源。这对单个流很有效,但现在我们必须渲染多个流,速度正在减慢
  • 我们还尝试使用Direct2D处理图形,使用D3D9共享曲面作为图像控件的源。虽然速度更快,但我们仍然无法从中获得稳定的30fps(当事情恢复时,它会在24-32fps之间跳跃)
  • 视频流通过后台线程进入,然后被封送(使用窗口的调度程序)到适当的UI线程进行绘制。然后在UI线程上完成所有绘图。我们还尝试为每个窗口提供自己的线程
如果有人想看,我可以提供我们尝试过的代码示例


谢谢

任何人都在寻找解决方案,我们使用DirectX 11编写了一个自定义winforms控件,并将高度优化的副本放入图形内存中,然后将该控件托管在
WinformsHost
中,我可以向感兴趣的任何人提供一些代码

优化复制到gpu内存中

    // Using native memcpy for the fastest possible copy
    [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
    private static extern IntPtr memcpy(IntPtr dest, IntPtr src, UIntPtr count);

    /// <summary>
    /// Copies a bitmap to gpu memory
    /// </summary>
    /// <param name="frame">The image to copy to the gpu</param>
    /// <returns>A texture in gpu memory for the bitmap</returns>
    public Texture2D CopyFrameToGpuMemory(Bitmap frame)
    {
        SampleDescription sampleDesc = new SampleDescription();
        sampleDesc.Count = 1;
        sampleDesc.Quality = 0;

        Texture2DDescription texDesc = new Texture2DDescription()
        {
            ArraySize = 1,
            MipLevels = 1,
            SampleDescription = sampleDesc,
            Format = Format.B8G8R8A8_UNorm,
            CpuAccessFlags = CpuAccessFlags.Write,
            BindFlags = BindFlags.ShaderResource,
            Usage = ResourceUsage.Dynamic,
            Height = frame.Height,
            Width = frame.Width
        };

        Texture2D tex = new Texture2D(Device, texDesc);

        Surface surface = tex.AsSurface();
        DataRectangle mappedRect = surface.Map(SlimDX.DXGI.MapFlags.Write | SlimDX.DXGI.MapFlags.Discard);

        BitmapData pixelData = frame.LockBits(new Rectangle(0, 0, frame.Width, frame.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);

        unsafe //!!!
        {
            byte* pixelDataStart = (byte*)pixelData.Scan0.ToPointer();
            byte* mappedRectDataStart = (byte*)mappedRect.Data.DataPointer.ToPointer();

            for (int y = 0; y < texDesc.Height; y++)
            {
                byte* lineLocationDest = mappedRectDataStart + (y * mappedRect.Pitch);
                byte* lineLocationSrc = pixelDataStart + (y * pixelData.Stride);

                // Copy line by line for best performance
                memcpy((IntPtr)lineLocationDest, (IntPtr)lineLocationSrc, (UIntPtr)(texDesc.Width * 4));
            }
        } //!!!

        frame.UnlockBits(pixelData);

        mappedRect.Data.Dispose();

        surface.Unmap();
        surface.Dispose();

        return tex;
    }
//使用本机memcpy进行尽可能快的复制
[DllImport(“msvcrt.dll”,EntryPoint=“memcpy”,CallingConvention=CallingConvention.Cdecl,SetLastError=false)]
私有静态外部IntPtr memcpy(IntPtr dest、IntPtr src、uintpttr计数);
/// 
///将位图复制到gpu内存
/// 
///要复制到gpu的图像
///gpu内存中位图的纹理
公共纹理2D CopyFrameToGpumMemory(位图帧)
{
SampleDescription sampleDesc=新的SampleDescription();
sampleDesc.Count=1;
样本描述质量=0;
Texture2DDescription texDesc=新的Texture2DDescription()
{
ArraySize=1,
MIP级别=1,
SampleDescription=sampleDesc,
格式=Format.B8G8R8A8_UNorm,
CpuAccessFlags=CpuAccessFlags.Write,
BindFlags=BindFlags.ShaderResource,
用法=ResourceUsage.Dynamic,
高度=框架高度,
宽度=帧宽度
};
Texture2D tex=新的Texture2D(设备,texDesc);
表面=特级表面();
DataRectangle mappedRect=surface.Map(SlimDX.DXGI.MapFlags.Write | SlimDX.DXGI.MapFlags.Discard);
BitmapData pixelData=frame.LockBits(新矩形(0,0,frame.Width,frame.Height),ImageLockMode.ReadOnly,PixelFormat.Format32bppRgb);
不安全/!!!
{
byte*pixelDataStart=(byte*)pixelData.Scan0.ToPointer();
byte*mappedRectDataStart=(byte*)mappedRect.Data.DataPointer.ToPointer();
对于(int y=0;y
如果有人在寻找解决方案,我们使用DirectX 11编写了一个自定义winforms控件,并将高度优化的副本保存到图形内存中,然后将该控件托管在
WinformsHost
中,我可以向感兴趣的任何人提供一些代码

优化复制到gpu内存中

    // Using native memcpy for the fastest possible copy
    [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
    private static extern IntPtr memcpy(IntPtr dest, IntPtr src, UIntPtr count);

    /// <summary>
    /// Copies a bitmap to gpu memory
    /// </summary>
    /// <param name="frame">The image to copy to the gpu</param>
    /// <returns>A texture in gpu memory for the bitmap</returns>
    public Texture2D CopyFrameToGpuMemory(Bitmap frame)
    {
        SampleDescription sampleDesc = new SampleDescription();
        sampleDesc.Count = 1;
        sampleDesc.Quality = 0;

        Texture2DDescription texDesc = new Texture2DDescription()
        {
            ArraySize = 1,
            MipLevels = 1,
            SampleDescription = sampleDesc,
            Format = Format.B8G8R8A8_UNorm,
            CpuAccessFlags = CpuAccessFlags.Write,
            BindFlags = BindFlags.ShaderResource,
            Usage = ResourceUsage.Dynamic,
            Height = frame.Height,
            Width = frame.Width
        };

        Texture2D tex = new Texture2D(Device, texDesc);

        Surface surface = tex.AsSurface();
        DataRectangle mappedRect = surface.Map(SlimDX.DXGI.MapFlags.Write | SlimDX.DXGI.MapFlags.Discard);

        BitmapData pixelData = frame.LockBits(new Rectangle(0, 0, frame.Width, frame.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);

        unsafe //!!!
        {
            byte* pixelDataStart = (byte*)pixelData.Scan0.ToPointer();
            byte* mappedRectDataStart = (byte*)mappedRect.Data.DataPointer.ToPointer();

            for (int y = 0; y < texDesc.Height; y++)
            {
                byte* lineLocationDest = mappedRectDataStart + (y * mappedRect.Pitch);
                byte* lineLocationSrc = pixelDataStart + (y * pixelData.Stride);

                // Copy line by line for best performance
                memcpy((IntPtr)lineLocationDest, (IntPtr)lineLocationSrc, (UIntPtr)(texDesc.Width * 4));
            }
        } //!!!

        frame.UnlockBits(pixelData);

        mappedRect.Data.Dispose();

        surface.Unmap();
        surface.Dispose();

        return tex;
    }
//使用本机memcpy进行尽可能快的复制
[DllImport(“msvcrt.dll”,EntryPoint=“memcpy”,CallingConvention=CallingConvention.Cdecl,SetLastError=false)]
私有静态外部IntPtr memcpy(IntPtr dest、IntPtr src、uintpttr计数);
/// 
///将位图复制到gpu内存
/// 
///要复制到gpu的图像
///gpu内存中位图的纹理
公共纹理2D CopyFrameToGpumMemory(位图帧)
{
SampleDescription sampleDesc=新的SampleDescription();
sampleDesc.Count=1;
样本描述质量=0;
Texture2DDescription texDesc=新的Texture2DDescription()
{
ArraySize=1,
MIP级别=1,
SampleDescription=sampleDesc,
格式=Format.B8G8R8A8_UNorm,
CpuAccessFlags=CpuAccessFlags.Write,
BindFlags=BindFlags.ShaderResource,
用法=ResourceUsage.Dynamic,
高度=框架高度,
宽度=帧宽度
};
Texture2D tex=新的Texture2D(设备,texDesc);
表面=特级表面();
DataRectangle mappedRect=surface.Map(SlimDX.DXGI.MapFlags.Write | SlimDX.DXGI.MapFlags.Discard);
BitmapData pixelData=frame.LockBits(新矩形(0,0,frame.Width,frame.Height),ImageLockMode.ReadOnly,PixelFormat.Format32bppRgb);
不安全/!!!
{
byte*pixelDataStart=(byte*)pixelData.Scan0.ToPointer();
byte*mappedRectDataStart=(byte*)mappedRect.Data.DataPointer.ToPointer();
对于(int y=0;y