WPF中生成和绘制视频的快速方法是什么?

5
我正在编写一个视频播放器,用于播放我们ASIC捕获的帧。它们是自定义格式的,我已经有了一个解码ASIC状态的函数。视频大小可以是从640x480到2560x1200的任意尺寸。每个状态周期的输出是一个16x16像素块,我必须将其放入屏幕上的视频中。
每次屏幕需要更新时,我会得到以下信息:
- 块宽度 - 块高度 - 块起始点的X坐标 - 块起始点的Y坐标 - 一个RGB32像素颜色信息的一维数组
主要限制如下:
- .NET 3.5 - 不能使用不安全代码
今天上午我尝试使用WriteableBitmap,并将其用作Image的源,类似于以下方式:
    private WriteableBitmap ImageSource;

    public MainWindow()
    {
        InitializeComponent();

        ImageSource = new WriteableBitmap(FrameWidth, FrameHeight, 96, 96, PixelFormats.Bgr32, null);
        ImagePanel.Source = ImageSource;
    }

    private void DrawBox(byte Red, byte Green, byte Blue, int X, int Y)
    {
        int BoxWidth = 16;
        int BoxHeight = 16;
        int BytesPerPixel = ImageSource.Format.BitsPerPixel / 8;

        byte[] Pixels = new byte[BoxWidth * BoxHeight * BytesPerPixel];

        for (int i = 0; i < Pixels.Length; i += 4)
        {
            Pixels[i + 0] = Blue;
            Pixels[i + 1] = Green;
            Pixels[i + 2] = Red;
            Pixels[i + 3] = 0;
        }

        int Stride = BoxWidth * BytesPerPixel;
        Int32Rect DrawBox = new Int32Rect(X, Y, BoxWidth, BoxHeight);

        ImageSource.Lock();
        ImageSource.WritePixels(DrawBox, Pixels, Stride, 0);
        ImageSource.Unlock();
    }

功能能实现,我的视频正常播放,但速度太慢了。根本不能达到实时播放的速度。有没有更好的方法来解决这个问题,而不是程序生成一系列位图?我已经读到一些关于D3Dimage的东西,但似乎那更多用于将3D场景导出为位图。有什么建议吗?


DrawBox似乎在创建一个16 * 16像素的纯色块。这正确吗? - Tim Lloyd
通过WriteableBitmap.WritePixels方法(Int32Rect、Array、Int32、Int32、Int32),您无需使用锁定和解锁。来源: http://msdn.microsoft.com/en-us/library/cc490065.aspx - user376591
是的,DrawBox是我在玩的东西,只是为了绘制一个实心颜色块。另外,谢谢你提醒锁定问题,我应该更仔细地阅读。 - Stevoman
你正在使用带有SP1的3.5吗?据说它修复了WriteableBitmap的性能问题。另外,你是在XP上运行吗?根据我的有限经验,WPF在Vista及更高版本上的表现要好得多。 - Chris O
1个回答

5
如果我理解正确,您会收到一个16*16像素块作为捕获,然后在上面的示例中,在每个这些更新中更新可写位图。
这看起来像是很多渲染在每个块上都被触发了。
不如这样做:
- 维护一个表示整个帧的单个字节缓冲区。 - 当您接收到16*16块时,使用它们更新缓冲区。 - 当您收到该帧的所有块时,将缓冲区写入位图。 - 通过覆盖它来重复使用帧缓冲区。
这样做可以减少渲染的频率,因为您不会为每个接收到的块触发渲染,而只会为每个帧触发一次渲染。
更新1
如果您没有使用alpha通道,我建议您考虑使用Bgr24作为像素格式。这样,每个像素将有3个字节,而不是4个字节,所以开销会大大降低。
更新2
如果对于更大的帧大小仍然太慢,您也可以考虑添加以下内容,尽管这会增加复杂性。
考虑维护和翻转两个帧缓冲区。这样,您可以在后台线程上合成帧,然后在UI线程上复制完成的帧。当UI线程呈现帧时,您可以在备用缓冲区上继续合成。
更新3
如果您的像素颜色信息数组来自捕获的正确格式,则可以尝试使用Buffer.BlockCopy进行批量复制源像素数组到帧缓冲区数组。但是,您必须保持位图格式为bgr32。这种低级别的数组复制技术可能会产生一些性能提升。

谢谢!使用Buffer.BlockCopy在后台构建缓冲区,然后每帧调用WritePixels()就解决了问题! - Stevoman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接