SharpGIS

#GIS from a .NET developer's perspective

Make your Windows 8 Video App use the PlayTo feature

If you are making a video app for Windows 8 Metro there are 6 lines of code you simply MUST add. The feature you get for such a little effort will make your jaw drop (well at least mine did). Here’s what the experience is like:

Imagine you are watching a video on your Windows 8 PC or Tablet:

image

But watching TV on the PC kinda sucks. So the first thing you do is to swipe from the right to bring out the Windows 8 Charms menu:

2

You hit the “Devices” charm, and voila, all your media clients show up under “Play To”. Now you just pick your PlayTo enabled TV/Device, or even any Windows Media Player running on Win7 or 8:

image

And the next thing you know this happens:

4

Voila! The Christmas cheer is now playing on the TV for everyone to enjoy. And I can continue to use the playback controls on my PC to play, pause, fast forward, rewind etc.

How to accomplish this is described in depth in the “Play To and Media Sharing for Metro Style Apps” white paper. But it’s so simple to set up, that I’ll quickly tell you how.

First thing you do is that to add a MediaElement to your Win8 Metro app page (if you made a video app you already did this):

    <MediaElement x:Name="videoplayer" Source="http://www.contoso.com/clip.mp4" AutoPlay="true" />
When you assign a video source to the media element, simply hook up an event listener to when the user picks a play to device:

// Step 1: Obtain PlayToManager object for app’s current view.
PlayToManager ptm = Windows.Media.PlayTo.PlayToManager.GetForCurrentView();
// Step 2: Register for the SourceRequested event (user selects Devices button).
ptm.SourceRequested += (PlayToManager sender, PlayToSourceRequestedEventArgs e) => {
    request = e.SourceRequest;
    // Step 3: Specify the media to be streamed.
    PlayToSourceDeferral deferral = request.GetDeferral();
    request.SetSource(videoplayer.PlayToSource);
    deferral.Complete();
}
And that’s ALL you need to do! No need to build any UI around this. No need to figure out how to stream various types of video streams (as far as I can tell both local files and remote video streams works with this). And yes this can even be extended to work with images and music as well.

So those 6 lines of code should ALWAYS be in your video player app, and you’ll make a lot of people very happy (Netflix and Hulu are you reading this?).

In the Windows 8 Developer Preview there is a limitation on what devices this works on for now though. From the white paper:

Only the below list of Play To receivers are supported in the Developer Preview. For scenario testing/validation from apps the easiest solution is to use Windows Media Player on another PC on the same Private Network. Other available devices are:
·    WD Live Hub (firmware version: 2.07.17)
·    Onkyo AV Receivers (Windows 7 Certified models)
·    Windows Media Player (on Windows 7 and the Windows Developer Preview)
·    Metro style apps with Play To Receiver functionality

Since Windows Media Player supports this, it’s pretty easy to test for yourself with a second Win7 or 8 PC. Here’s how to configure Windows Media Player as a Play To receiver:

·    Open Windows Media Player and navigate to the Stream menu
·    Select Allow remote control of my Player

image

If both PC’s are on the same private network and have sharing enabled, that’s really all you need to do. Windows 8 will automatically pick it up and add it to the list of Play To devices.

If you have problems making this work, I suggest you first read the white paper, since this also covers known issues and how to correctly configure your devices on your network.

Why Silverlight 5’s 3D is (almost) useless

So Silverlight 5 finally hit the web this week, and there’s a lot of cool new features – some of which I’m very excited about, but probably the biggest feature ever added - the 3D XNA API that allows you to render directly to the graphics card using the new DrawingSurface API - I’m not so excited about any more. Why? Because between the RC and RTM release this feature has been heavily crippled. Here’s an excerpt from the recent blogpost describing the change:

[…] you may experience security errors with Silverlight 5 RTM when you want to use the wonderful new 3D feature. In fact, some graphics drivers may allow malicious code to execute. That may lead to an unwanted hard reset or a blue screen.

Starting with the beta version, to protect users for this kind of trouble, we initiate a first scenario where all Windows XP Display Driver Model (XPDM) drivers on Windows XP, Windows Vista, and Windows 7 will be blocked by default. Permission will be granted automatically in elevated trust scenarios and Windows Display Driver Model (WDDM) drivers will not require user consent at run-time.

But as always, features, including security features, continue to be refined and added during post-beta development.

And for the RTM version, there were a number of approaches considered to further improve security and stability, but the solution to block 3D in partial trust by default was the best option for this release. Permission is still granted automatically in elevated trust scenarios.

So already during beta and RC, we had a security limitation that certain old type of display drivers would disable this mode, unless you ran in full trust. Considering the uptake on WDDM, I didn’t consider this limitation a big problem. After all technology moves forward, and old tech sometimes needs to be left behind. After all one of the reasons for moving to the WDDM model was to stabilize the system.

Post RC this limitation was increased to be across all systems and display drivers. I was lucky enough to get a heads up about this a little over two months ago, but I feel truly sorry for the people who had been betting on this feature and are ready to release their 3D apps now that RTM has hit. The communication of this limitation shouldn’t have stayed within a small group of people under NDA, but should have been communicated out to the community. It probably didn’t happen because it could probably have fueled the “Silverlight is dead” fire (and rightfully so). I would have like to have seen a more open discussion of this, similar to what the Windows 8 team has on their “Building Windows 8” blog. In June the Silverlight Team had promised that the DoS issue that beta and RC had, would be mitigated and developers were counting on this feature.

So I’m not saying that security shouldn’t take precedence, but I would have like to see a more focused effort to either improve the experience – either by dedicating a lot of resources to weed out the security issues, delay the release, or at the very least make it easier for users to opt in. – perhaps communicating that this is a temporary limitation and will be resolved in a GDR update. Considering the late change, I get that the Silverlight team didn’t have much time to turn around and improve this experience. However, there’s already a feature like this for when you want to use the webcam that works much better. When I in Silverlight want to use the webcam, I ask the user for permission, and I’m met with the following dialog:

image

It’s pretty easy and it’s clear to the user what he/she needs to do. I wish we had gotten a similar dialog for enabling 3D. Instead, now in Silverlight, all you can do is detect that the 3D capability is blocked and try and describe to the user what to do. These are the steps you would have to guide them through:

  • Right click on your Silverlight plugin.
  • Click the “Silverlight” option.
  • Go to the permissions tab.
  • Find the domain that hosts the .xap file (which might not be obvious since it could be different than where the website is located).
  • When you find the entry (and this list could be quite long), you have to click “Allow” – no indication here what that really means for the casual PC user (how do you use a blocked display driver?!??).

image

Was this really the best experience we could have gotten? It’s actually easier to ask the user to install the app in full trust (which is an even unsafer mode), than the above experience. I’m sure Average Joe would not like this experience, have trouble finding this out, and probably even be scared to death to do anything he’s not used to doing (I know my parents wouldn’t ever get through this). So all you are going to see on your website is a plugin container that just sits there unused, and not getting to show how awesome this thing could really have been.

It’s such a shame, because this API can vastly improve rendering performance in many scenarios, and opens up an entirely new group of 3D based web based apps. And it would have worked across a wide range of browsers (more so than WebGL at this point).

The 3D capabilities are still great for out-of-browser apps and internal line-of-business apps where it’s a lot easier to deploy an app in full trust (especially with SL5’s new in-browser full trust feature). But are that really the type of apps where you expect to be using the 3D feature a lot?

Building an Augmented Reality XAML control

Augmented Reality on Windows Phone and Windows 8 Metro style apps

I’ve lately been playing a lot with augmented reality on my Windows Phone and my Windows 8 Tablet. Both of them feature an accelerometer, compass and gyro as well as a back facing camera. This makes it possible to overlay various XAML elements on top of the camera feed, and have them move with the camera as you rotate and orient the device. As an example of this, you can try the AR view in my GuidePost app for Windows Phone (and while you’re at it, make sure you throw some good reviews in there ;-). The app uses the exact approach described here.

To do augmented reality in XAML, I built a custom panel control that automatically handles the placement of all it’s child elements based on the device’s orientation. I used an attached property to define what ‘direction’ an element is supposed to be placed in. You already know this from Grid and Canvas, where for instance Grid.Row=”1” places it in the second row of the grid, or Canvas.Top=”100” places the element 100 pixels from the top of the canvas. In my case I use ARPanel.Direction to define a Point where the first value is the azimuth (ie up/down) and second value is the compass heading.

Note: Since you probably know the Grid panel very well, to help explain how this custom panel works, you’ll sometimes see a “How Grid uses this” section, to explain how the grid control would do something similar.

Here’s what the four cardinal directions and up and down would look like in such a custom panel that we’ll be building:

<ar:ARPanel>
    <TextBlock Text="North" ar:ARPanel.Direction="0,0" />
    <TextBlock Text="East"  ar:ARPanel.Direction="0,90" />
    <TextBlock Text="South" ar:ARPanel.Direction="0,180" />
    <TextBlock Text="West"  ar:ARPanel.Direction="0,270" />
    <TextBlock Text="Up"    ar:ARPanel.Direction="90,0" />
    <TextBlock Text="Down"  ar:ARPanel.Direction="-90,0" />
</ar:ARPanel>

Neat huh? This makes it VERY simple to spit out a bunch of different AR-type of apps. So how did I build this?

The custom panel

When building a custom panel, there’s really just 3 things you need to do:

  1. Inherit from Panel
  2. Override MeasureOverride
  3. Override ArrangeOverride

Or to put that in code:

public class ARPanel : Panel
{
    protected override Size MeasureOverride(Size availableSize)
    {
        //TODO
    }
    protected override Size ArrangeOverride(Size finalSize)
    {
        //TODO
    }
}

The two methods here have the following purposes:

MeasureOverride is called first, and it should go through all the child elements and tell them “Hey I got this much space available - how much of that do you need?”.

How Grid uses this: If a grid is 150x100 and has two rows and two columns, it would look at the row/column of the element then say “I got this much 75x50 space in this row/column - how much do you want”. If all the row/column sizes are auto, it would send the entire size of the grid itself to the child.

ArrangeOverride is called second. This goes through all the child elements and tells them “I heard you want this much space (as indicated by child.DesiredSize), but I can only give you this much, and this is the area where you get to place yourself”.

How Grid uses this: After going through the measure step, Grid would now determine what the size of each row and column needs to be based on the child desired elements (unless the cell sizes are fixed), and then calculate the rectangle for each cell distributed based on the row/column definitions. These rectangles are then used to place the child elements in the row/column they are set to be placed in.

Now first let’s implement MeasureOverride, since this is very simple in this scenario. Each child element can render itself anywhere around you in a 360 degree space, so we don’t need to limit the child elements to any size other than the size of the panel itself. So we’ll ask each child “Given all of my space, how much would you want to use?”. This is simply done by going through each child element and calling Measure on them with the full size available (after each measure call, you will notice that child.DesiredSize will now be assigned). We end the call by returning the size that the panel needs to it’s parent - in this case we want to use the entire space available to us:

protected override Size MeasureOverride(Size availableSize)
{
    foreach (var child in Children)
        child.Measure(availableSize);
    return availableSize;
}

MeasureOverride is called whenever the panel’s size is changing or anything else that would affect the size of the child elements. You can trigger this yourself by calling panel.InvalidateMeasure(). In our specific case we don’t ever need to call this, and the measure will only get invalidated when the panel’s size changes - for instance when page orientation changes, and luckily this is all done for us. (Grid would call InvalidateMeasure if the row/column definitions change)

Next up is ArrangeOverride. Now that we measured all the elements, we need to determine where to place them on the screen, and how much space we need to give each of them. We’ll basically go through each element and call Arrange(rect) on them, where the ‘rect’ is the rectangle it gets to render itself in. Ie. the top-left corner is where the area starts, and the width/height is the size it gets. It’s then up to the child to make use of that area. In our ARPanel case, the top/left corner would constantly change as the orientation of the device changes, but we’ll keep the width/height to the desired size of the element. So the method will look something like this:

protected override Size ArrangeOverride(Size finalSize)
{
    foreach(var child in Children)
    {
        double centerX = TODO;
double centerY = TODO;
double left = centerX - child.DesiredSize.Width * .5; double top = centerY - child.DesiredSize.Height * .5; Rect rect = new Rect(new Point(left, top), child.DesiredSize); child.Arrange(rect); } }

I subtract half the width/height of the size to center the element on top of ‘centerX’ and ‘centerY’, which we will still have to determine;

So this is basically our panel implementation. The tricky part is figuring out the “TODO” part of the above code based on the motion sensor. So basically, given a device orientation, what is the screen location of a direction?

Warning: I spent quite a lot of time on getting this part right. It’s a lot of vector math, and some of it I came to using pure trial and error :-). Therefore I might not even completely get all of it myself, but it works, and it works great. I’m pretty sure the math is right, but if I don’t go too much into detail in some of the math, just think of it as magic that works and use the code as is :-) For those of you who are used to XNA 3D Game programming, I’m sure this will all make sense to you though, since this is very similar to how you control a camera in a 3D environment.

The motion sensor

The Motion Sensor is what will be driving the placement of the child elements. Motion is a combination of Compass, Accelerometer and optionally Gyro. The Gyro is not required, but it makes the result A LOT better - especially when you rotate the device fast. Future certified Windows 8 tablets will require all 3 sensors, but for Windows Phone, only some of the newer Mango phones has a gyro, and I believe the Dell Venue Pro compass sensor doesn’t work. Future cheaper Windows Phone devices might not come with any sensors available, so beware that your app might not work on all devices, and you should check for the capability and notify the user if it’s missing.

On Windows Phone you can check whether the motion sensor is supported using ‘Microsoft.Devices.Sensors.Motion.IsSupported’. I didn’t find an equivalent property in Windows Runtime though. The following code starts reading from the sensor for both WinPhone and Windows Runtime:

#if WINDOWS_PHONE
    if (Microsoft.Devices.Sensors.Motion.IsSupported)
    {
        motion = new Microsoft.Devices.Sensors.Motion();
        motion.CurrentValueChanged += motion_CurrentValueChanged;
        motion.Start();
#elif WINRT
    motion = Windows.Devices.Sensors.OrientationSensor.GetDefault();
    if (motion != null)
    {
        motion.ReadingChanged += motion_CurrentValueChanged;
#endif
    }
    else
    {
        throw new InvalidOperationException("Motion sensor not supported on this device");
    }
#endif

When we get a new reading, all we have to do is tell the panel, that the current child placements are invalid. We don’t want to do too much work here, because the motion sensor can be triggering much more frequent than the layout cycle, so all we do here is flag the panel for arrange and clear any parameters that could be affected by the orientation change (like the attitude which I will get back to):

    private Matrix? _attitude;

    private void motion_CurrentValueChanged(object sender, EventArgs e)
    {
        _attitude = null;
 #if WINDOWS_PHONE
        Dispatcher.BeginInvoke(() => InvalidateArrange());
#elif WINRT
        Dispatcher.Invoke(Windows.UI.Core.CoreDispatcherPriority.Normal, 
                          (a, b) => InvalidateArrange(), this, null);
#endif
    }

Handling sensor and screen coordinate systems

The Motion sensor gives us a Matrix that defines the rotation of the device relative to up and north directions. The screen has a different coordinate system that is relative to the upper left corner of the screen. When the screen changes between landscape and portrait mode, this coordinate system changes relative to the motion sensors coordinate system, and it’s important to take this into account. Lastly we need to define a field of view for the screen. If you are overlaying elements on top of the camera, the field of view must match the field of view of the camera. Think of it as how ‘narrow’ the view is. A lens that is zoom in far has a small field of view, and only few elements will be visible on the screen, whereas a wide angle lens will have more child elements on the same screen. I found that on my phones a FOV of roughly 35 degrees seems appropriate. So to sum up we need 3 things: The orientation of the sensor, the orientation of the view (screen), and the parameters for the camera (the projection).

First the view, which is slightly different between phone and runtime (probably because the ‘natural’ orientation on a phone is portrait mode, whereas on a PC it’s landscape mode, so the sensors are mounted different).

    Matrix view;
if (orientation == LandscapeLeft) { view = Microsoft.Xna.Framework.Matrix.CreateLookAt( new Microsoft.Xna.Framework.Vector3(0, 0, 1), Vector3.Zero, #if WINDOWS_PHONE Vector3.Right); #elif WINRT Vector3.Up); #endif } else if (orientation == LandscapeRight) { view = Microsoft.Xna.Framework.Matrix.CreateLookAt( new Vector3(0, 0, 1), Vector3.Zero, #if WINDOWS_PHONE Vector3.Left); #elif WINRT Vector3.Down); #endif } else //portrait mode { view = Microsoft.Xna.Framework.Matrix.CreateLookAt( new Vector3(0, 0, 1), Vector3.Zero, #if WINDOWS_PHONE Vector3.Up); #elif WINRT Vector3.Left); #endif

Next we define a viewport based on this view. Note that this depends on the size of the screen, so on size changed we need to remember to reset this value.

private Microsoft.Xna.Framework.Graphics.Viewport? _viewport;
private Microsoft.Xna.Framework.Graphics.Viewport Viewport
{
    get
    {
        if (!_viewport.HasValue)
        {
            _viewport = new Microsoft.Xna.Framework.Graphics.Viewport(0, 0, (int)ActualWidth, (int)ActualHeight);
            _cameraProjection = null; //camera projection depends on viewport - force a reset
        }
        return _viewport.Value;
    }
}

And from this we can now define the projection of the camera / field of view.

private Matrix CameraProjection
{
    get
    {
        if (!_cameraProjection.HasValue)
        {
           _cameraProjection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians((float)FieldOfView), Viewport.AspectRatio, 1f, 12f); } return _cameraProjection.Value; } }

Now we need the attitude of the device based on the current sensor reading (this was the value we reset in reading changed event). We rotate this value so it matches the rotation of the XNA coordinate system as well.

    private Matrix Attitude
    {
        get
        {
            if (!_attitude.HasValue)
            {
                if (motion != null 
#if WINDOWS_PHONE
                    && motion.IsDataValid
#endif
                    )
                {
                    _attitude = Matrix.CreateRotationX(MathHelper.PiOver2) * CurrentReading;
                }
                else
                    return Matrix.Identity;
            }
            return _attitude.Value;
        }
    }
#if WINDOWS_PHONE
    private Matrix CurrentReading
#elif WINRT
    private SensorRotationMatrix CurrentReading
#endif
    {
        get
        {
            return
#if WINDOWS_PHONE
                motion.CurrentValue.Attitude.RotationMatrix;
#elif WINRT
                motion.GetCurrentReading().RotationMatrix;
#endif
        }
    }

Ok that’s a lot of funky stuff, but now we are pretty set to project from vectors to screen coordinates using this simple method:

Matrix world = Matrix.CreateWorld(Vector3.Zero, new Vector3(0, 0, -1), new Vector3(0, 1, 0));
// Projects the point from 3D space into screen coordinates.
private Vector3 Project(Vector3 vector)
{
    return Viewport.Project(vector, CameraProjection, view, world * Attitude);
}

Only thing is we have a direction ray and not a vector. A little extra method for converting to a vector is needed too:

private static Vector3 PolarToVector(double px, double py, double radius)
{
    var O = (py - 90) * PI_OVER_180; // / 180d * Math.PI;
    var W = (90 - px) * PI_OVER_180; // / 180d * Math.PI;
    var x = (float)((Math.Cos(O) * Math.Sin(W)) * radius);
    var y = (float)((Math.Cos(W)) * radius);
    var z = (float)((Math.Sin(O) * Math.Sin(W)) * radius);
    return new Vector3(x, y, z);
}

We don’t have any radius, but I found always using a value of ‘10’ (which is 10 units down the ray) works pretty well. So… we are now pretty set to implement the ArrangeOverride method.

Arranging the child elements

When arranging the children, we need to first check if it’s inside the screen view, and next where on the screen that is. I’m using the BoundingFrustum to do this. Think of the bounding frustum as the space the camera can see, and is a cone the expands out from the camera We can set this up using:

    BoundingFrustum viewFrustum = new BoundingFrustum(Attitude * view * CameraProjection);

If we now build a BoundingSphere around each element, we can use the .Contains method to see if that element is within the frustum. So we first grab the Point object from the element, and build a bounding sphere. If it is inside, all we have to do is call the Project method above to get the screen location, and lastly call Arrange on the element.

So our entire ArrangeOverride method ends up looking like this:

    protected override Size ArrangeOverride(Size finalSize)
    {
        if (ActualWidth > 0 && ActualHeight > 0 && motion != null
#if WINDOWS_PHONE
            && motion.IsDataValid
#endif
            )
        {
            BoundingFrustum viewFrustum = new BoundingFrustum(Attitude * view * CameraProjection);
            foreach (var child in Children)
            {
                object posObj = child.GetValue(DirectionProperty);
                if (posObj is Point && !double.IsNaN(((Point)posObj).X))
                {
                    Point p = (Point)posObj;
                    Vector3 direction = PolarToVector(p.X, p.Y, 10);
                    var size = child.DesiredSize;
                    //Create a bounding sphere around the element for hittesting against the current frustum
                    //This size is not entirely right... size we have is screen size but we use the world size. 
                    //*.008 seems to roughly fit as conversion factor for now
                    var box = new BoundingSphere(direction, (float)Math.Max(size.Width, size.Height) * .008f);
                    if (viewFrustum.Contains(box) != ContainmentType.Disjoint) //partially or fully inside camera frustum
                    {
                        Vector3 projected = Project(direction);
                        if (!float.IsNaN(projected.X) && !float.IsNaN(projected.Y))
                        {
                            //Arrange element centered on projected coordinate
                            double x = projected.X - size.Width * .5;
                            double y = projected.Y - size.Height * .5;
                            child.Arrange(new Rect(x, y, size.Width, size.Height));
                            continue;
                        }
                    }
                }
                //if we fall through to here, it's because the element is outside the view,
                //or placement can't be calculated
                child.Arrange(new Rect(0, 0, 0, 0));
            }
            return finalSize;
        }
        else
            return base.ArrangeOverride(finalSize);
    }

Congrats on making it this far - because we are actually pretty much done! All there’s left to do is put it all together, reset the used orientation parameters when needed and define the attached property for use on the child elements. And I already did all that for you!

You can download the control library together with a sample app here. There’s both a Windows Phone and Windows 8 Metro version included. You can use the code as you like, but if you improve on it, I would LOVE to hear about it. I didn’t include a camera background in the samples, so I’ll let that bit be up to you to add (you can use a VideoBrush as the background for that).

Note that for Windows Runtime, I have only tested this on the Samsung Build Tablet. I have no idea how this behaves on a device without a motion sensor (probably crashes, but let me know in the comments below). Also the Build tablets have a buggy fusion sensor firmware, which means that readings are fired VERY infrequent, making it almost useless for anything but rough testing.

Also note that the compass sensor can get very confused when you are inside - especially if the building has a metal frame construction. If directions seem off, try going outside away from anything that confuses the magnetic field.

WinRT vs. Silverlight - Part 8 - What other people are blogging

See intro blogpost here.

Over the last few months several other people have been writing blog posts covering the transition from WPF/Silverlight/WP7 to WinRT. Below are some of the ones I’ve stumbled upon.

Colin Eberhardt - XAMLFinance - A Cross-platform WPF, Silverlight and WP7 Application
An app that reuses code across 3 different XAML platforms and compiles for all of them. A great example that this can be accomplished. Also make sure to check out Colin’s blog for more WinRT goodness.

Jeffrey Richter - Core .NET Type usable from a Metro Styl Application
A list of the “standard” .NET types that are available in WinRT as well.

Andy’s blog - Physics Games: Multi-targeting Windows 8 and Windows Phone 7
Andy goes through building a physics-based game for both Windows 8 and Windows Phone 7. Also check out his Physics Helper library for WinRT.

Tim Greenfield - “WinRT Genome Project”
Visual comparison of how much overlay Silverlight 5 and WinRT has.

Tim Greenfield - Silverlight 5 vs. WinRT comparison
A follow-up to the link above with a comparison of namespaces, members, types and differences between SL5 and WinRT. This is an amazing list if you want to get into the details when reusing code between the two frameworks.

Pontus Wittenmarks’s - 10 tips about porting Silverlight apps to WinRT/Metro style apps (Part 1)
A quick list of of tips when porting from Silverlight to WinRT.

Petzold Book Blog - Windows 8 Dependency Property Strangeness
Talks about some of the issues with Dependency Properties in Windows 8. I already briefly touched on this, but this goes a lot more in-depth.

If I find more, I’ll add them here, or feel free to mention any other good resources in the comments below.