Quickstart: streaming a slide show using Play To (HTML)

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

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.

Prerequisites

Microsoft Visual Studio

Instructions

1. Create a new project and enable access to Pictures

  1. Open Visual Studio and select New Project from the File menu. In the Javascript section, select Blank Application. Name the application PlayToSlideShow and click OK.
  2. 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.

2. Add HTML UI

Open the Default.html file and add the following HTML to the <body> section. The UI contains a <div> that is used to display the images and another that is used to display status messages. The UI also contains a <div> 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.

<div id="slideshowDiv" style="height:600px;display:table-cell;vertical-align:bottom;"></div>
<div id="messageDiv">Slideshow disconnected</div>
<button id="disconnectButton" style="width:600px;height:60px;display: none">
    Connected to <img id="iconImage" style="width: 30px;" /> <span id="deviceSpan" />
</button>
<div id="instructionsDiv">Swipe from the right edge of the screen, select "Devices", and select a device to stream the slide show to.</div>

3. Add initialization code

The code in this step begins the slide show and creates the handler for the disconnect button click event. The code also includes a shortcut function, id, for convenient access to the getElementById function.

Open the js folder. Open the Default.js file and add the following code in place of the default onactivated function.

app.onactivated = function (args) {
    if (args.detail.kind === activation.ActivationKind.launch) {

        startSlideShow();

        args.setPromise(WinJS.UI.processAll());
    }
};

// Disconnect button event handler.
function disconnectButtonClick() {
    Windows.Media.PlayTo.PlayToManager.showPlayToUI();
}

// Shortcut function.
function id(tagName) {
    return document.getElementById(tagName);
}

4. Add code to get and display the images as a slide show

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 msPlayToSource 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
disconnected

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.

connected

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 Default.js file, add the following code after the code from the previous step.

var states = Windows.Media.PlayTo.PlayToConnectionState, // alias for PlayToConnectionState
    imageList,               // contains the list of images to show
    streaming = false,       // true when streaming using Play To; otherwise false
    cancel = 0,              // used to cancel a timeout
    timeLapse = 5,           // time between images (5 seconds)
    imageSize = "600px",     // size of current displayed image
    thumbnailSize = "200px", // size of "thumbnail" of next image
    currentImage = 0;        // index of the current image from imageList

// Get the list of images from the Pictures folder and start the slide show.

function startSlideShow() {
    Windows.Storage.KnownFolders.picturesLibrary.getFilesAsync().then(
        function (resultsLibrary) {
            imageList = resultsLibrary;
            if (imageList.length > 0) {
                var image = queueImage(0, true);
            } else {
                id("messageDiv").innerHTML = "There are no images in the Pictures library.";
            }
        });
}


// 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.

function playNextImage(num) {
    id("slideshowDiv").removeChild(id("image" + num));
    queueImage(num + 2, false);

    currentImage = num + 1;
    id("image" + currentImage).style.width = imageSize;

    cancel = setTimeout(function () {
        playNextImage(num + 1);
    }, timeLapse * 1000);
}


// queueImage
// Called to create an image object for the displayed images.

function queueImage(num, isFirstImage) {

    // Create the image element for the specified image index and add to the
    // slide show div.

    var image = document.createElement("img");
    image.style.width = (isFirstImage ? imageSize : thumbnailSize);
    image.id = "image" + num;
    image.src = URL.createObjectURL(imageList[num % imageList.length], { oneTimeOnly: true });
    id("slideshowDiv").appendChild(image);

    // 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 && !streaming) {

        queueImage(num + 1, false);

        cancel = setTimeout(function () {
            playNextImage(num);
        }, timeLapse * 1000);            
    }

    // 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.

    image.msPlayToSource.connection.addEventListener("transferred", function () {

        currentImage = num + 1;
        id("image" + currentImage).style.width = imageSize;

    }, false);


    // Use the statechanged event to determine which action to take or to respond
    // if the Play To Receiver is disconnected.
    image.msPlayToSource.connection.addEventListener("statechanged", function (e) {

        switch (e.currentState) {
            case states.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) {
                    id("messageDiv").innerHTML = "Slideshow disconnected";

                    // Cancel any existing timeout
                    if (cancel) {
                        clearTimeout(cancel);
                    }

                    // Clear all image objects from the slide show div
                    while (id("slideshowDiv").firstChild) {
                        id("slideshowDiv").removeChild(id("slideshowDiv").firstChild);
                    }

                    // Reset the slide show objects and values to their beginning state
                    streaming = false;
                    id("disconnectButton").style.display = "none";
                    id("instructionsDiv").style.display = "block";
                    disconnectButton.removeEventListener("click", disconnectButtonClick, false);

                    // Restart the slide show from the current image index
                    queueImage(currentImage, true);
                } else {
                    image.msPlayToSource.next = null;
                    image.removeAttribute("src");

                    if (streaming) {
                        id("slideshowDiv").removeChild(image);
                    }
                }

                break;
                
            case states.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.

                if (e.previousState === states.disconnected) {
                    var imageNext = queueImage(num + 1, false);
                    image.msPlayToSource.next = imageNext.msPlayToSource;
                } else if (e.previousState === states.rendering) {
                    if (cancel) {
                        clearTimeout(cancel);
                        cancel = 0;
                    }
                }

                if (currentImage == num) {
                    id("messageDiv").innerHTML = "Slideshow connected";
                }

                break;

            case states.rendering:

                // If the state is rendering and the previous state is
                // connected, then the Play To Receiver has restarted
                // the slide show.

                if (e.previousState === states.connected) {

                    // Clear any existing timeout.
                    if (cancel) {
                        clearTimeout(cancel);
                    }

                    // Restart the slide show.
                    cancel = setTimeout(function () {
                        image.msPlayToSource.playNext();
                    }, timeLapse * 1000);
                }

                if (currentImage == num) {
                    id("messageDiv").innerHTML = "Slideshow rendering";
                }

                break;
        }

    }, false);

    return image;
}

5. Add Play To code

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 Default.js 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.
var initialImage = null;

var ptm = Windows.Media.PlayTo.PlayToManager.getForCurrentView();

ptm.addEventListener("sourcerequested", function (e) {
    initialImage = document.createElement("img");

    // Use the statechanged event of the image passed to Play To to determine when
    // the image is finally connected to the Play To Receiver.
    initialImage.msPlayToSource.connection.addEventListener("statechanged", function (e) {

        if (e.currentState === states.connected) {

            // Clear any existing timeout.
            if (cancel) {
                clearTimeout(cancel);
                cancel = 0;
            }

            // Clear the slide show div.
            while (id("slideshowDiv").firstChild) {
                id("slideshowDiv").removeChild(id("slideshowDiv").firstChild);
            }

            // Set the slide show objects and values to show that we are streaming.
            streaming = true;
            id("disconnectButton").style.display = "block";
            id("instructionsDiv").style.display = "none";

            // Queue and display the next image.
            var image = queueImage(currentImage, true);
            initialImage.msPlayToSource.next = image.msPlayToSource;
            initialImage.msPlayToSource.playNext();
        }
    }, false);

    // Provide Play To with the first image to stream.
    e.sourceRequest.setSource(initialImage.msPlayToSource);

}, false);

// Update the once the user has selected a device to stream to.
ptm.addEventListener("sourceselected", function (e) {
    disconnectButton.addEventListener("click", disconnectButtonClick, false);
    id("messageDiv").innerHTML = "Streaming to " + e.friendlyName + "...";
    id("deviceSpan").innerHTML = e.friendlyName + ".<br/>Click here to disconnect.";
    id("iconImage").src = URL.createObjectURL(e.icon, { oneTimeOnly: true });
}, false);

6. Create a Play To target (optional)

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. In order 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.

  1. Start Windows Media Player.
  2. 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.
  3. 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.

7. Run the app

  • 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.

Summary

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

Samples

Play To sample

PlayToReceiver sample

Media Server sample