Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a fast way to generate and draw video in WPF?

I am writing a video player to play back frames captured by our ASIC. They are in a custom format, and I have been provided with a function that decodes the ASIC states. The video can be any size from 640x480 to 2560x1200 (!!!). The output of each state cycle is a 16x16 blocks of pixels, which I have to get into a video on the screen.

Every time the screen needs to be updated, I have the following information:

  • Block width
  • Block height
  • X coordinate of block start
  • Y coordinate of block start
  • A 1-D array of RGB32 pixel color info

Major limitations:

  • .NET 3.5
  • No unsafe code

I spent this morning trying out a WriteableBitmap, and using it as a source for an Image, something like this:

    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();
    }

It works, my video plays on the screen, but it is sloooooooow. Nowhere near real-time play speed. Is there a better way, besides procedurally generating a series of bitmaps, to do this that I'm not seeing? I've read something about D3Dimage, but it seems that's more for exporting a 3D scene to a bitmap. Suggestions here?

like image 782
Stevoman Avatar asked Nov 23 '10 18:11

Stevoman


1 Answers

If I understand correctly you receive a 16*16 pixel block as a capture, and in the example above you are then updating the writeable bitmap on each of these updates.

This looks like a lot of churn as a render is being triggered on each block.

Would it not be better to:

  • Maintain a single byte buffer that represents an entire frame.
  • Update the buffer with your 16*16 blocks as you receive them.
  • When you have received all blocks for a frame, write the buffer to the bitmap.
  • Re-use the frame buffer for the next frame by over-writing it.

In this way you will have far less churn on rendering as you will not trigger a render for each block you receive, but only for each frame.

Update 1

I would also consider using Bgr24 as your pixel format if you are not using an alpha channel. That way you will have 3 bytes per pixel, instead of 4, so quite a lot less overhead.

Update 2

If things are still too slow for larger frame sizes, you could also consider the following which adds complexity though.

Consider maintaining and flipping two frame buffers. In this way you could composite frames on a background thread and then blit the finished frame on the UI thread. Whist the UI thread is rendering the frame, you can continue compositing on the alternative buffer.

Update 3

If your pixel colour info array from your capture is in the correct format, you could try using Buffer.BlockCopy to bulk copy the source pixel array into the frame buffer array. You would have to keep your bitmap format as bgr32 though. This low level array copying technique could yield some performance gains.

like image 85
Tim Lloyd Avatar answered Sep 21 '22 12:09

Tim Lloyd