posted on Tuesday, June 27, 2006 1:31 PM
by
jritmeijer
Windows Presentation Foundation - Day 4 - Optimizing video performance
Click here for an overview of all posts in this series.
Before I go into the details of today's exercise I'll provide some quick links and useful information first. A few days ago Microsoft released a Post Beta 2
June CTP version of the .net Framework 3.0. Although it is always tempting to install the latest and greatest version, I advise against it as the Interactive Designer only works with the Beta 2 version. I have also had some problems installing the June version of the SDK and VS add-in. Until further notice my posts
assume a Beta 2 installation.
It is fun to see the WPF community growing little by little. Lee Brimelow, a very experienced Flash developer, has started an excellent
WPF blog. His blog contains a number of small but powerful WPF examples that include full source code. He approaches everything from a Flash developer's point of view, which is a good thing. He has also authored a
good article comparing WPF with Flash, which is spot on.
On to the good stuff. Having played around with some samples, looked at the SDK and read some blog entries I feel comfortable enough to create some code and...gasp... give some advice with respect to performance.

Our final result
Quite a few of the WPF showcases use a cool reflective surface that mirrors whatever action is going on, e.g. a movie, in real time. An obvious way to do this is to use 2 media elements with the same movie, vertically mirror one of the movies and add a transparency mask. Although in theory this works, it is very inefficient as the video needs to be streamed twice and decoded twice, which uses a lot of CPU and network bandwidth. It is also very difficult to keep the two video sources exactly in sync.
A better way to do this is to create one MediaElement, create a vertically mirrored rectangle, and assign the MediaElement's VisualBrush to it.
Sounds simple right, here is how we do it.
- Start Interactive Designer and create a new project
- Make the background of the Canvas black or use a fancy gradient to create a horizon
- In the Library window, select the MediaElement and draw it on the top 2/3 of the Canvas
- With the MediaElement still selected, navigate to the Properties Window and specify a video stream in the Source property, e.g. mms://wm.microsoft.com/ms/msnse/0606/27918/NathanDunlap_MBR.wmv
- Use the Rectangle Tool from the Toolbox Window to draw a rectangle about 1/3 of the height of the canvas straight underneath the MediaElement. I use gridlines and '0' margins to position everything and line things up, but you can also do it the quick and dirty way.
- Use the Properties Window to give the rectangle a name, in this case 'Reflection'
- To vertically mirror the rectangle, grab the handle at the top middle of the rectangle and flip it vertically by dragging it underneath the bottom line of the rectangle.

The look of the fully created Scene in ID
Make sure you save often as ID crashes frequently. I had to perform the previous steps about 3 times due to all kind of crashes.

Now for the tricky bit, we need to create a vertical opacity mask in the Reflection rectangle. Follow the steps outline below:
- Select the Reflection rectangle
- In the Appearance Window select the OpacityMask
- Select the Linear Gradient Brush, the third icon from the left
- Select the right most gradient stop, the white one, and give it an 'A' (Alpha) of 0. The Opacity mask now blends from opaque to transparent.
- To rotate the Opacity Mask 90 degrees, select the Brush Transform Icon from the Tools Window. Rotate the arrow in the reflection rectangle by 90 degrees until the white part of the gradient is at the top (Weird, you would expect it to be the other way around and have the transparency at the bottom). After rotating, move the arrow 50% up and make it 2/3 of the original size. This provides a more realistic reflection

Creating the opacity mask
The scene is now finished, we just need to add a little bit of code to assign the Video's brush to the reflection rectangle:
- Open scene1.xaml.cs using the Projects Window
- Add the following code to the constructor to create a new VisualBrush and assign it to the rectangle.
// Insert code required on object creation below this point.
Reflection.Fill = new VisualBrush(MediaElement);
That is it, press F5 to see the final results.
Because we have been a bit naughty it doesn't work 100% as expected. The reflection takes the full size of the rectangle while the video may be much smaller. The reason for this is, most likely, that we didn't wait for the video to finish initialising (and thereby determining the video's width) before assigning it to the rectangle.
There are 2 solutions, either wait for the video to initialise or draw a Border element around the video and use that border's VisualBrush, including the embedded video, as the reflection. The Border has a fixed width so we don't need to wait for it to finish loading.
I secretly thought that this video would play roughly using the same CPU percentage as it would take Windows Media Player. Unfortunately that is not the case, even when we remove the reflection. While Windows Media Player only takes 10% CPU time, this WPF application almost maxes out one of the cores of my Core Duo CPU. If Anyone can shed any light on this then that would be much appreciated as the current performance is unacceptable, especially considering my Laptop has a 512MB Radeon X1600.