This article is Day #22 in a series called 31 Days of Windows 8. Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.
Today, we get to talk about another really cool feature to Windows 8: Play To. At its core, this is the ability to share content from your computer to a television, another computer, or an Xbox 360. Imagine finding a cool video on YouTube that you want to share with the other people in the room. Play To would allow you share that content to your television so that everyone can see it without having to huddle around your tablet.
When I first started investigating this technology, I was concerned that it would be difficult to test effectively. While I have a couple of nice TVs in my house, they’re just old enough to not have any idea what Play To is. (If you’d like to check your devices, check out Microsoft’s Compatibility Center.) Anyways, what I’ve discovered is that you really only need a second machine running Windows 8 on it, or an Xbox 360 to test this out.
In my setup, I’ve deployed my application to my Samsung Series 7 Slate running Windows 8 Pro, and I am able to use Play To on my home network to share content with Windows Media Player on a second PC that I have on my desk. I’ve also tested it on my Xbox 360, and it works marvelously.
Before we get into the coding, I want to make sure you have a way to test this functionality. In Windows 8, open the Windows Media Player app. (You know, the app that you haven’t opened on a Windows machine in 10 years? Yeah, that one.)
Select the “Allow remote control of my Player…” option. You’ll be asked to confirm your choice, just to make sure this is actually what you want to do.
Once you’ve confirmed this choice, your machine will be registered on your network as a device available for Play To content. OK, let’s get to coding, so that we can see what this really means.
Making Your App a Play To Source
This is the easy half of this article, but the second half is just more involved, not necessarily difficult. When I first ventured out to learn how to do this, I fully expected a trip to the package.appxmanifest file, where we would simply treat this experience much like we did with Sharing on Day #7, where we have to do most of the work in configuration.
I was wrong. It’s WAY easier.
First, we need to create a PlayToManager object, which will activate our ability to share our content via Play To. We create an event handler which handles when the user connects their device to a target, and finally, we set the source of the content we want to send. The entire operation looks like this:
PlayToManager manager = null;
CoreDispatcher dispatcher = null;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
dispatcher = Window.Current.CoreWindow.Dispatcher;
manager = PlayToManager.GetForCurrentView();
manager.SourceRequested += manager_SourceRequested;
}
void manager_SourceRequested(PlayToManager sender, PlayToSourceRequestedEventArgs args)
{
var deferral = args.SourceRequest.GetDeferral();
var handler = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
args.SourceRequest.SetSource(MusicSource.PlayToSource);
deferral.Complete();
});
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
manager.SourceRequested -= manager_SourceRequested;
}
That’s seriously ALL the code you need to make this happen. Remember Day #20 when we talked about printing? It took over 400 lines of code to get a printer to put ink on paper. Getting a song to stream wirelessly over a network and play on another device without any need for credentials? We can do that in 10 lines. Here’s a little video to illustrate:
The only real “magic” that is happening in the code above is when we set our source. I am grabbing the Source of a MediaElement I have on my page, but this seems perfectly reasonable. The standard use case for Play To is that the user will start watching the content on their tablet or personal device, and then want to share it to another Play To device, so you’ll already have a MediaElement in use.
The next step is to make our app capable of being the source as well.
Making Your Windows 8 App a Play To Target
In this example, we’re going to add functionality to our app so that we can be on the receving end of any Play To content that might be flying around our local network. To do this, we will start with a PlayToReceiver object, and a giant pile of REQUIRED event handlers to go with it. I’ve modified my OnNavigatedTo method from earlier to accomodate this new code:
PlayToManager manager = null;
CoreDispatcher dispatcher = null;
PlayToReceiver receiver = null;
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
dispatcher = Window.Current.CoreWindow.Dispatcher;
manager = PlayToManager.GetForCurrentView();
manager.SourceRequested += manager_SourceRequested;
receiver = new PlayToReceiver();
receiver.PlaybackRateChangeRequested += receiver_PlaybackRateChangeRequested;
receiver.PlayRequested += receiver_PlayRequested;
receiver.PauseRequested += receiver_PauseRequested;
receiver.StopRequested += receiver_StopRequested;
receiver.MuteChangeRequested += receiver_MuteChangeRequested;
receiver.VolumeChangeRequested += receiver_VolumeChangeRequested;
receiver.TimeUpdateRequested += receiver_TimeUpdateRequested;
receiver.CurrentTimeChangeRequested += receiver_CurrentTimeChangeRequested;
receiver.SourceChangeRequested += receiver_SourceChangeRequested;
receiver.SupportsAudio = true;
receiver.SupportsVideo = true;
receiver.SupportsImage = true;
receiver.FriendlyName = "Day #22 - Play To";
await receiver.StartAsync();
}
As you can see, there are NINE event handlers that need to be implemented. I’m stressing this fact because if you don’t implement every single one of them, you will not be able to call the StartAsync() method on your PlayToReceiver object. (I just spent the last 30 minutes discovering that cold, hard fact.)
Once you’ve done it, however, and have your nine new methods, it’s up to you which ones you actually implement (though it’s recommended that you implement all of them.) Here are each of my nine event handlers, and the code that goes with each one:
SourceChangeRequested
In this method (probably the most important of the nine), we detect which type of content is being sent our way, grab it, and send it to the appropriate XAML control in our UI. The ShowSelectedPanel() method is one I built to manage the switching of visibilities and stop/start methods for the MediaElements. You can certainly choose to disregard those in your app.
async void receiver_SourceChangeRequested(PlayToReceiver sender, SourceChangeRequestedEventArgs args)
{
if (args.Stream != null)
{
if (args.Stream.ContentType.Contains("image"))
{
await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(args.Stream);
PhotoSource.Source = bmp;
ShowSelectedPanel(1);
});
}
else if (args.Stream.ContentType.Contains("video"))
{
await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
VideoSource.SetSource(args.Stream, args.Stream.ContentType);
ShowSelectedPanel(3);
});
}
else if (args.Stream.ContentType.Contains("audio"))
{
await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
MusicSource.SetSource(args.Stream, args.Stream.ContentType);
ShowSelectedPanel(2);
MusicSource.Play();
});
}
}
}
PlayRequested
This, and all of the subsequent event handlers simply make a call of the appropriate type (this example is obviously “Play”), and then notify our source that the call has been made.
async void receiver_PlayRequested(PlayToReceiver sender, object args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
MusicSource.Play();
VideoSource.Play();
receiver.NotifyPlaying();
});
}
PauseRequested
Practically identical to the PlayRequested method, this one implements Pause.
async void receiver_PauseRequested(PlayToReceiver sender, object args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
MusicSource.Pause();
VideoSource.Pause();
receiver.NotifyPaused();
});
}
The Rest
As you can see below, the last 5 event handlers follow the exact same format for their properties.
private async void receiver_PlaybackRateChangeRequested(PlayToReceiver sender, PlaybackRateChangeRequestedEventArgs args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
VideoSource.PlaybackRate = args.Rate;
});
}
private async void receiver_CurrentTimeChangeRequested(PlayToReceiver sender, CurrentTimeChangeRequestedEventArgs args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
if (VideoSource.CanSeek)
{
{
VideoSource.Position = args.Time;
receiver.NotifySeeking();
}
}
});
}
private async void receiver_TimeUpdateRequested(PlayToReceiver sender, object args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
receiver.NotifyTimeUpdate(VideoSource.Position);
});
}
private async void receiver_VolumeChangeRequested(PlayToReceiver sender, VolumeChangeRequestedEventArgs args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
VideoSource.Volume = args.Volume;
});
}
private async void receiver_MuteChangeRequested(PlayToReceiver sender, MuteChangeRequestedEventArgs args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
VideoSource.IsMuted = args.Mute;
});
}
Finally, we’ve got a working application that both send and receive content via Play To. Here’s a video of the final product in action:
Summary
Today, we dove in deep to the Play To protocol, which allows us to send and receive media files from another app on another device. I think we’ve done a pretty good job of giving you the fine-grained control necessary to make this a success in your applications.
If you’d like to download the working sample code from this article, click the icon below:
Tomorrow, we’re going to dive into using another sensor: the Compass. Navigate your way back here tomorrow to learn more about it. See you then!
Leave a Reply