One of the things that I really like about Windows devices is the ability to transfer around music and video via “Play To”. I don't think it's unique to Windows by any means and I keep seeing it on Kindle commercials in the UK but on Windows If I’m sitting and listening to a bit of James Taylor at Christmas in Xbox Music on my laptop;
then I can pop out to the charms bar, pick out devices;
find my Xbox (as an example of a DLNA receiver);
and then the music plays from the Xbox rather than from the local PC – naturally, I can also do this with video although in both cases the Xbox Music app checks the content for protection here before it allows it to be played to another device like this – in my case I’m playing an .mp3 off my hard drive so the app lets me do it.
I can bring the music back to the local machine using the charms again.
This kind of PlayTo functionality has been in Windows for quite a while – I don’t remember exactly when it arrived but it certainly pre-dates Windows 8 which really brought it more to the forefront and, rather than hiding it away on a menu option inside of Media Player, it’s available to every app and discoverable via the charms bar to every user.
There are some changes in the way that Windows 8.1 handles PlayTo though – there’s a bit of coverage of this in the Build 2013 video below (around the 22m mark);
and I thought I'd make a basic blank app that allowed me to try out a feature that I'd heard about called "Play To By Reference". Getting PlayTo up and running can be as simple as pointing a MediaElement to an online video, i.e. with a piece of XAML;
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <MediaElement x:Name="mediaElement" AreTransportControlsEnabled="True" Source="http://ie.microsoft.com/testdrive/Graphics/VideoFormatSupport/big_buck_bunny_trailer_480p_high.mp4" AutoPlay="True"/> </Grid>
and then some code to set up the PlayToManager and give it the MediaElement as a source;
public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); this.Loaded += OnLoaded; } void OnLoaded(object sender, RoutedEventArgs e) { var manager = PlayToManager.GetForCurrentView(); manager.SourceRequested += OnSourceRequested; } void OnSourceRequested(PlayToManager sender, PlayToSourceRequestedEventArgs args) { var deferral = args.SourceRequest.GetDeferral(); Dispatcher.RunAsync( CoreDispatcherPriority.Normal, () => { args.SourceRequest.SetSource(this.mediaElement.PlayToSource); deferral.Complete(); }); } }
I couldn't decide in that particular OnSourceRequested method whether it's better to await the call to CoreDispatcher.RunAsync() and then complete the Deferral object passed to me or whether to complete it as I do above in the method passed across to Dispatcher.RunAsync(). If the Deferral object wasn't thread-agile then one choice would be obvious but (if that was the case and I hope it's not) the whole idea of a Deferral object might be a lot less useful.
With that in place, I get a UI that can play a video and can PlayTo on that video to send it to (e.g.) my Xbox.
and that nice, new AreTransportControlsEnabled property means that the developer using a MediaElement does not have to create "transport controls" any more for play/pause and so on because the control has them built-in if you want to use them and switch on the flag.
As the top picture shows, I also get to play this media to my other devices via PlayTo with this very small piece of code.
One of the things I always found a bit "odd" about this scenario is that the media is actually being downloaded from the web to my laptop and then my laptop is streaming it to my Xbox. My natural assumption would have been that the logic would run something like this;
- PlayTo source determines where the media is coming from.
- For networked media, target tells source whether it can access that media or not.
- If the target can access the media, it plays it independently.
- If the target can't, it streams it from the source.
- For non-networked media, the source streams it to the target.
But I don't think it works like that. The PlayTo source gets the media and streams it over to the PlayTo target.
I think that's one of the reasons why an app like Xbox Music can't stream content that's protected by DRM from my laptop to my Xbox. That doesn't happen. What I get instead when I try and share content in Xbox Music that I don't have locally stored on my PC I get an error saying that's not possible;
Similarly, if I'm using my own app and I'm streaming that Big Buck Bunny video from my laptop across to the Xbox via PlayTo and then I disconnect my laptop from the network, I see the video frame freeze on the Xbox and it displays a "playback error" – the Xbox is streaming the video from my laptop rather than going out to the source of the video on the internet and accessing it from there.
In Windows 8.1 the MediaElement has a new property that relates to PlayTo – a property called PreferredPlayToUriSource which enables the idea of "PlayTo by Reference" which I think is somewhat closer to passing a URI to the content from the PlayTo Source to the PlayTo target.
If I change my XAML to enable that (I deliberately tried to choose 2 different URLs below);
<MediaElement x:Name="mediaElement" AreTransportControlsEnabled="True" Source="http://mirrorblender.top-ix.org/peach/bigbuckbunny_movies/big_buck_bunny_720p_surround.avi" PlayToPreferredSourceUri="http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_surround-fix.avi" AutoPlay="True"/>
and I switch from using an Xbox as a PlayTo target and, instead, make use of Windows Media Player running on a second machine then what I first see when the video starts to play on the remote device is content being streamed from the source device;
i.e. in that screenshot above taken on the target device, the video is being pulled from my laptop which is the IP address highlighted.
However, if I unplug that source laptop from the network then the behaviour I see is the target machine pauses for a little while and then restarts the video (in this case) from the PreferredPlayToUriSource – I see it reaching out;
and pulling content directly from blender.org.
I'm not sure whether in other scenarios (e.g. if the content was under DRM) that things would work this way – I'd imagine that the target would go directly to the PreferredPlayToUriSource rather than attempting to stream from the source which is what happens in my little test case here.