In my last post I showed how to access the WPF's BitmapSource underlying WIC image, and I promised to explain my code, and also to post an Image stream (Video) code based on my solution.
In this post I will quickly talk about the unique CompositionTarget class, and I will show how to render a Raw Video using its Rendered event.
Download the code from here (FYI - I refractor the code from the last post and fixed several bugs).
I will start by reminding one of the disadvantages of my WICBitmap solution: "Sometimes the Lock/Copy fail. I think that it because I'm trying to lock the buffer while it is in use by the render thread (use the TryLock method instead of Lock, and try again and again if fails. The second or third lock should succeed)"
When I first encountered this symptom, I have searched in the Image class for an OnRender method or event. One such that will be raised after the image renders the BitmapSource. Unfortunately, I didn't find one. Searching for the word Rendered in the MSDN, I have found the WPF CompositionTarget class. Surprisingly, The CompositionTarget class has a Rendered event. It is written that the Rendered event fires a moment before WPF sends the visual tree to milcore for rendering. This is a great opportunity for manipulating my video buffer.
Here is the code for the Rendered event:
public Window1()
{
InitializeComponent();
CompositionTarget.Rendering += CompositionTarget_Rendering;
} void CompositionTarget_Rendering(object sender, EventArgs e)
{
if (_isPlaying)
{
NextFrame();
}
} private void NextFrame()
{
// Randomize a new buffer with random pixels
_random.NextBytes(_managedBuffer); // replace this code by copying the next video frame
// Copy the new buffer into the unmanaged memory.
Marshal.Copy(_managedBuffer, 0, _unmanagedBuffer, _managedBuffer.Length);
WICBitmapLock wicBitmapLock;
// Retrieve the low level WIC buffer of the BitmapSource.
int errorCode;
if (_wicBitmap.TryLock(LockFlags.ReadWrite, out wicBitmapLock, out errorCode))
{
// Copy the new randomized buffer into the unmanaged memory.
// It is also possible to work directly on the WIC buffer.
if (wicBitmapLock.Pixels.CopyFrom(_unmanagedBuffer, (uint)_managedBuffer.Length))
{
// Enforce Image instance to render itself.
image.InvalidateVisual();
}
else
{
Debug.WriteLine("Failed to copy buffer. Drop frame.");
}
// The dispose method releases the lock.
// WHY VALUE TYPES HAVE NO CONSTRUCTORS? :-)
wicBitmapLock.Dispose();
}
else
{
Debug.WriteLine("Failed to lock buffer. Drop frame.");
}
}
As you can see, instead of manipulating the WIC image using a timer or other mechanism, I'm using the Rendered event which is the best place for placing this code. This ensures that the image is fully rendered, and provides best performance.
Running this code in my Duo Core2 laptop, using a 32bit color depth with resolution of 800x600, ends up with 37fps with randomize, and with 70fps without randomize.
It is getting too late (have to work tomorrow), so I promise to explain my solution in the next post.
Download the code from here.