Quickstart: Streaming a slide show using Play To (XAML)
You can use Play To to enable users to easily stream audio, video, or images from their computer to devices in their home network. This topic shows you how to use Play To in a Windows Store app, to enable users to stream images as a slide show to a target device.
Objective: Use Play To to stream images as a slide show to a target device.
Microsoft Visual Studio
- Open Visual Studio and select New Project from the File menu. In the Visual C# or Visual Basic section, select Application. Name the application PlayToSlideShow and click OK.
- Open the Package.appxmanifest file and select the Capabilities tab. Select the Pictures Library capability to enable your application to access the Pictures folder on a computer. Close and save the manifest file.
Open the MainPage.xaml file add the following to the default <Grid>. The UI contains a StackPanel that is used to display the images and a TextBlock that is used to display status messages. The UI also contains a TextBlock that tells the user how to begin streaming using Play To and a Button to enable the user to disconnect when streaming. These two elements are hidden and made visible depending on whether the slide show is streaming or not.
<StackPanel Orientation="Vertical"> <StackPanel x:Name="SlideShowPanel" Height="600" Orientation="Horizontal" /> <TextBlock x:Name="MessageBlock" FontSize="14" >Slideshow disconnected</TextBlock> <Button x:Name="DisconnectButton" Width="600" Height="60" Visibility="Collapsed" FontSize="14"> <StackPanel Orientation="Horizontal"> <TextBlock>Connected to</TextBlock> <Image x:Name="IconImage" Width="30" /> <TextBlock x:Name="DeviceBlock" /> </StackPanel> </Button> <TextBlock x:Name="InstructionsBlock" FontSize="14" >Swipe from the right edge of the screen, select "Devices", and select a device to stream the slide show to.</TextBlock> </StackPanel>
The code in this step initializes the PlayToManager and then begins the slide show.
Open the MainPage.xaml.cs or MainPage.xaml.vb file and add the following code in place of the default OnNavigatedTo method.
Private ptm As Windows.Media.PlayTo.PlayToManager Private timer As Windows.UI.Xaml.DispatcherTimer Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs) ptm = Windows.Media.PlayTo.PlayToManager.GetForCurrentView() AddHandler ptm.SourceRequested, AddressOf SourceRequested AddHandler ptm.SourceSelected, AddressOf SourceSelected StartSlideShow() End Sub
This sample displays images as a slide show by using the images in the root folder of Pictures. This is accomplished by first obtaining the list of images from Pictures, and then creating <image> objects to cycle through the list.
When streaming the images using Play To, the code for this slide show app makes use of the ability to buffer the next media item using Play To. This is optional, but it is useful for scenarios where it takes additional time to obtain the next media item to stream. By buffering the media, you can stream it immediately after the current media item has finished and avoid a delay between media items.
To buffer the next media item, we set the Play To source of the next item to the Next property of the current item. When the current item completes, we call the PlayNext method of the current item to stream the next media source to the target device.
When the slide show is only playing locally, the code uses a timeout to move to the next image in the list. When the slide show is streaming to a Play To receiver, the code still uses a timeout to move to the next image, but also responds when the Play To receiver pauses the slide show, moves to the next image before the timeout expires, or is disconnected. This is accomplished using the Statechanged event of the Play To source referenced by the PlayToSource property of an image object. In the Statechanged event, the code examines the CurrentState and PreviousState properties of the arguments passed to the event. The different states, along with the number that identifies the index of the image that raised the Statechanged event tell us how to respond as shown in the following table.
|CurrentState||Action to take|
If the index of the image that raised the event is the same as the index of the image that is currently being displayed, then the Play To source was disconnected while an image was being displayed. This means that the Play To receiver is no longer connected and we start the slide show locally using the most recent image index. Otherwise, a state of Disconnected simply indicates that the slide show has finished displaying the image that raised the event and we can clean up the image object that is no longer required.
If the previous state is Disconnected, then the image that raised the event has just been connected to the Play To receiver. At this point, we get the next image so that it is loaded while the current image is displayed.
If the previous state is Rendering, then the user has paused the slide show on the Play To receiver and we clear the current timeout until the user restarts the show.
|rendering||If the previous state is Connected, then the Play To receiver has un-paused the slide show and we can start the show again.|
In the MainPage.xaml.cs or MainPage.xaml.cs file, add the following code after the code from the previous step.
Private imageList As IReadOnlyList(Of Windows.Storage.StorageFile) ' contains the list of images to show Private streaming As Boolean = False ' true when streaming using Play To otherwise false Private timeLapse As Integer = 5 ' time between images (5 seconds) Private imageSize As Integer = 600 ' size of current displayed image Private thumbnailSize As Integer = 200 ' size of "thumbnail" of next image Private currentImage As Integer = 0 ' index of the current image from imageList ' Get the list of images from the Pictures folder and start the slide show. Private Async Sub StartSlideShow() Dim resultsLibrary = Await Windows.Storage.KnownFolders.PicturesLibrary.GetFilesAsync() imageList = resultsLibrary If (imageList.Count > 0) Then Dim image = QueueImage(0, True) Else MessageBlock.Text = "There are no images in the Pictures library." End If End Sub ' PlayNextImage ' Called when a new image is displayed due to a timeout. ' Removes the current image object and queues a new next image. ' Sets the next image index as the new current image, and increases the size ' of the new current image. Then sets the timeout to display the next image. Private Async Sub PlayNextImage(num As Integer) ' Stop the timer to avoid repeating. If timer IsNot Nothing Then timer.Stop() Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, Async Sub() SlideShowPanel.Children.Remove(CType(SlideShowPanel.FindName("image" & num), UIElement)) Dim i = Await QueueImage(num + 2, False) currentImage = num + 1 CType(SlideShowPanel.FindName("image" & currentImage), Image).Width = imageSize End Sub) timer = New Windows.UI.Xaml.DispatcherTimer() timer.Interval = New TimeSpan(0, 0, timeLapse) AddHandler timer.Tick, Sub(sender As Object, e As Object) PlayNextImage(num + 1) End Sub timer.Start() End Sub ' QueueImage ' Called to create an image object for the displayed images. Private Async Function QueueImage(num As Integer, isFirstImage As Boolean) As Task(Of Image) ' Create the image element for the specified image index and add to the ' slide show div. Dim image = New Image() image.Width = If(isFirstImage, imageSize, thumbnailSize) image.Name = "image" & num image.VerticalAlignment = VerticalAlignment.Bottom Dim fileContents = Await imageList(num Mod imageList.Count).OpenReadAsync() Dim imageBitmap = New BitmapImage() imageBitmap.SetSource(fileContents) image.Source = imageBitmap Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, Sub() SlideShowPanel.Children.Add(image) End Sub) ' If this is the first image of the slide show, queue the next image. Do ' not queue if streaming as images are already queued before ' streaming using Play To. If isFirstImage AndAlso Not streaming Then Dim i = Await QueueImage(num + 1, False) timer = New Windows.UI.Xaml.DispatcherTimer() timer.Interval = New TimeSpan(0, 0, timeLapse) AddHandler timer.Tick, Sub(sender As Object, e As Object) PlayNextImage(num) End Sub timer.Start() End If ' Use the transferred event of the Play To connection for the current image object ' to "move" to the next image in the slide show. The transferred event occurs ' when the PlayToSource.playNext() method is called, or when the Play To ' Receiver selects the next image. AddHandler image.PlayToSource.Connection.Transferred, Async Sub(sender As Windows.Media.PlayTo.PlayToConnection, e As Windows.Media.PlayTo.PlayToConnectionTransferredEventArgs) currentImage = num + 1 Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, Sub() CType(SlideShowPanel.FindName("image" & currentImage), Image).Width = imageSize End Sub) End Sub ' Use the statechanged event to determine which action to take or to respond ' if the Play To Receiver is disconnected. AddHandler image.PlayToSource.Connection.StateChanged, Async Sub(sender As Windows.Media.PlayTo.PlayToConnection, e As Windows.Media.PlayTo.PlayToConnectionStateChangedEventArgs) Select Case e.CurrentState Case Windows.Media.PlayTo.PlayToConnectionState.Disconnected ' If the state is disconnected and the current image index equals the ' num value passed to queueImage, then the image element is not connected ' to the Play To Receiver any more. Restart the slide show. ' Otherwise, the current image has been discarded and the slide show ' has moved to the next image. Clear the current image object and ' remove it from the slide show div. If currentImage = num Then Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, Async Sub() MessageBlock.Text = "Slideshow disconnected" ' Cancel any existing timeout If timer IsNot Nothing Then timer.Stop() ' Clear all image objects from the slide show div SlideShowPanel.Children.Clear() ' Reset the slide show objects and values to their beginning state streaming = False DisconnectButton.Visibility = Visibility.Collapsed InstructionsBlock.Visibility = Visibility.Visible AddHandler DisconnectButton.Click, AddressOf DisconnectButtonClick ' Restart the slide show from the current image index Dim i = Await QueueImage(currentImage, True) End Sub) Else Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, Sub() image.PlayToSource.Next = Nothing If streaming Then SlideShowPanel.Children.Remove(image) End If End Sub) End If Case Windows.Media.PlayTo.PlayToConnectionState.Connected ' If the state is connected and the previous state is disconnected, ' then the image element is newly connected. Queue up the next image so ' that it is loaded while the current image is being displayed. ' If the previous state is rendering, then the user has paused the slideshow ' on the Play To Receiver. Clear the current timeout until the user restarts ' the slide show. Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, Async Sub() If e.PreviousState = Windows.Media.PlayTo.PlayToConnectionState.Disconnected Then Dim imageNext = Await QueueImage(num + 1, False) image.PlayToSource.Next = imageNext.PlayToSource ElseIf e.PreviousState = Windows.Media.PlayTo.PlayToConnectionState.Rendering Then If timer IsNot Nothing Then timer.Stop() End If If currentImage = num Then MessageBlock.Text = "Slideshow connected" End If End Sub) Case Windows.Media.PlayTo.PlayToConnectionState.Rendering ' If the state is rendering and the previous state is ' connected, then the Play To Receiver has restarted ' the slide show. Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, Sub() If e.PreviousState = Windows.Media.PlayTo.PlayToConnectionState.Connected Then ' Clear any existing timeout. If timer IsNot Nothing Then timer.Stop() ' Restart the slide show. timer = New Windows.UI.Xaml.DispatcherTimer() timer.Interval = New TimeSpan(0, 0, timeLapse) AddHandler timer.Tick, Sub(s As Object, args As Object) image.PlayToSource.PlayNext() End Sub timer.Start() End If If currentImage = num Then MessageBlock.Text = "Slideshow rendering" End If End Sub) End Select End Function Return image End Function
The code in this step implements the Play To contract. It gets a reference to the PlayToManager for the current application and associates the event handler for the SourceRequested and SourceSelected events.
Because image objects are created and destroyed for each image of the slide show, we use a temporary image object that is never destroyed in the SourceRequested event. This is because we do not know if the timeout will expire before the user selects a Play To receiver. If that occurred, then the current image would be destroyed and we would pass a null reference to Play To. Instead, we pass Play To a reference to image object that is never destroyed and update the image source to the currently displayed image once the user selects a Play To receiver. We know that has occurred when the state of the image changes to Connected.
In the MainPage.xaml.cs or MainPage.xaml.vb file, add the following code after the code from the previous step.
' Set up the Play To contract. ' Used to pass an image to Play To that will not be removed/destroyed ' by the slide show logic. For example, if the user opens the Devices ' charm and the sourcerequested event fires, but the image display timeout ' completes before the user selects a target device, then the image that ' was being displayed is removed and destroyed. intialImage is never ' destroyed so Play To will always have a valid source to stream. Private initialImage As Image = Nothing Private Async Sub SourceRequested(sender As Windows.Media.PlayTo.PlayToManager, e As Windows.Media.PlayTo.PlayToSourceRequestedEventArgs) Dim deferral = e.SourceRequest.GetDeferral() Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, Sub() initialImage = New Image() ' Use the statechanged event of the image passed to Play To to determine when ' the image is finally connected to the Play To Receiver. AddHandler initialImage.PlayToSource.Connection.StateChanged, AddressOf InitialImageConnectionStateChanged ' Provide Play To with the first image to stream. e.SourceRequest.SetSource(initialImage.PlayToSource) deferral.Complete() End Sub) End Sub Private Async Sub InitialImageConnectionStateChanged(sender As Windows.Media.PlayTo.PlayToConnection, e As Windows.Media.PlayTo.PlayToConnectionStateChangedEventArgs) If e.CurrentState = Windows.Media.PlayTo.PlayToConnectionState.Connected Then Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, Async Sub() ' Clear any existing timeout. If timer IsNot Nothing Then timer.Stop() ' Clear the slide show panel. SlideShowPanel.Children.Clear() ' Set the slide show objects and values to show that we are streaming. streaming = True DisconnectButton.Visibility = Visibility.Visible InstructionsBlock.Visibility = Visibility.Collapsed ' Queue and display the next image. Dim image = Await QueueImage(currentImage, True) initialImage.PlayToSource.Next = image.PlayToSource initialImage.PlayToSource.PlayNext() End Sub) End If End Sub ' Update the once the user has selected a device to stream to. Private Async Sub SourceSelected(sender As Windows.Media.PlayTo.PlayToManager, e As Windows.Media.PlayTo.PlayToSourceSelectedEventArgs) Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, Sub() AddHandler DisconnectButton.Click, AddressOf DisconnectButtonClick MessageBlock.Text = "Streaming to " & e.FriendlyName & "..." DeviceBlock.Text = e.FriendlyName & "." & vbCr & "Click here to disconnect." Dim imageBitmap = New Windows.UI.Xaml.Media.Imaging.BitmapImage() imageBitmap.SetSource(e.Icon) IconImage.Source = imageBitmap End Sub) End Sub Private Sub DisconnectButtonClick() Windows.Media.PlayTo.PlayToManager.ShowPlayToUI() End Sub
To run the application, you will need a target device to which Play To can stream media. If you do not have a certified Play To receiver, you can use Windows Media Player as a target device. To use Windows Media Player as a target device, your computer must be connected to a private network. Windows Media Player must be running on a different computer than your Play To source app.
- Start Windows Media Player.
- Expand the Stream menu and enable the Allow remote control of my Player... option. Leave Windows Media Player open; it must be running to be available as a Play To target.
- Open the Devices and Printers control panel. Click Add devices and printers. In the Add devices and printers wizard, in the Choose a device or printer to add to this PC window, locate the Digital media renderer for your PC. This is the Windows Media Player for your PC. Select it and click Next. When the wizard finishes, you will see your instance of Windows Media Player in the list of Multimedia Devices.
- In Visual Studio, press F5 (debug) to run the app. You can select any of the media buttons to play or view the first media item in the different media libraries. While the media is playing, open the Devices charm and select your Play To target to stream the media to the target device.
In this quickstart, you added Play To capability to an application that displays images as a slide show on a target device. The Play To capability enables users to stream the content from the application to a certified Play To receiver on their network.
- Streaming media to devices using Play To
- Play To sample
- PlayToReceiver sample
- Media Server sample