快速入门:使用“播放到”流式传输幻灯片放映 (HTML)

[ 本文适用于编写 Windows 运行时应用的 Windows 8.x 和 Windows Phone 8.x 开发人员。如果你要针对 Windows 10 进行开发,请参阅 最新文档 ]

你可以使用“播放到”功能使用户能够很容易在其家庭网络中将音频、视频或图像以流数据的形式从计算机传输到其他设备。本主题介绍如何在 Windows 应用商店应用中使用“播放到”让用户将图像以幻灯片放映形式流式传输到目标设备。

目标: 使用“播放到”将图像以幻灯片放映形式流式传输到目标设备。

先决条件

Microsoft Visual Studio

说明

1. 创建一个新项目并允许访问到图片

  1. 打开 Visual Studio,然后从“文件”菜单中选择“新建项目”。在“JavaScript”部分中,选择“空白应用程序”。将该应用程序命名为 PlayToSlideShow,然后单击“确定”。
  2. 打开 Package.appxmanifest 文件并选择“功能”****选项卡。选择“图片库”功能,以支持你的应用程序访问计算机上的“图片”文件夹。关闭并保存清单文件。

2. 添加 HTML UI

打开 Default.html 文件并将下面的 HTML 添加到 <body> 部分中。UI 包含一个用于显示图像的 <div>,和另一个用于显示状态消息的 <div>。UI 还包含一个告知用户如何使用“播放到”开始流式播放的 <div> 和一个使用户可以在流式播放时断开连接的按钮。这两个元素均处于隐藏状态,并根据幻灯片是否流式播放而处于可见状态。

<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. 添加初始化代码

此步骤中的代码开始幻灯片放映并创建断开连接按钮单击事件的处理程序。该代码还包含快捷函数 id 以便可以方便地访问 getElementById 函数。

打开 js 文件夹。打开 Default.js 文件,添加以下代码替换默认的 onactivated 功能。

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. 添加代码以获取图像和作为幻灯片放映显示图像

本示例使用“图片”根文件夹中的图像以幻灯片放映方式显示图像。此操作可以通过首先从“图片”中获取图像列表,然后创建 <image> 对象以循环显示该列表来完成。

在使用“播放到”流式播放图像时,此幻灯片应用的代码利用该功能使用“播放到”缓冲下一媒体项。这是可选功能,但是它对花费额外时间获取下个媒体项以进行流式传输的方案非常有用。通过缓冲媒体,你可以在当前媒体项完成之后立即对媒体进行流式传输,从而避免媒体项之间的延迟。

若要缓冲下一个媒体项,我们将下一个项的“播放到”源设置为当前项的 next 属性。当前项完成时,我们调用当前项的 playNext 方法,将下一个媒体源流式传输到目标设备。

在幻灯片仅在本地播放时,该代码使用超时移至列表中的下一图像。在幻灯片放映流式传输到“播放到”接收器时,该代码仍使用超时移至下一图像,但在“播放到”接收器暂停幻灯片放映时还会做出响应,在超时到期前移至下一图像,否则将断开连接。使用图像对象的 msPlayToSource 属性引用的“播放到”源的 statechanged 事件,可以完成此操作。在 statechanged 事件中,代码检查传递给该事件的参数的 currentStatepreviousState 属性。不同的状态,以及指示引发 statechanged 事件的图像的索引的数将告知我们如何响应,如下表所示。

currentState 采取的操作
已断开连接

如果引发事件的图像的索引与当前显示的图像的索引相同,则在显示图像时“播放到”源断开连接。这表示“播放到”接收器不再连接,并且我们开始使用最常用的图像索引在本地播放幻灯片。否则,disconnected 的状态只指示幻灯片已结束显示引发事件的图像,并且我们可以清除不再需要的图像对象。

已连接

如果先前的状态为 disconnected, 则引发事件的图像刚刚连接到“播放到”接收器。此时,我们获取下一图像, 以便在当前图像显示时加载该图像。

如果先前的状态为 rendering,则用户已暂停放映“播放到”接收器上的幻灯片并且我们清除当前超时直至用户重新开始 放映。

呈现 如果先前的状态为 connected,则“播放到”接收器未暂停放映幻灯片并且我们可以再次开始放映幻灯片。

 

在 Default.js 文件中,在上一步骤的代码后面添加以下代码。

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. 添加“播放到”代码

本步骤中的代码可实现“播放到”合约。它可为当前应用程序获取一个到 PlayToManager 的引用并为 sourcerequestedsourceselected 事件关联事件处理程序。

由于对于幻灯片的每个图像,图像对象已创建并破坏,因此我们使用绝不会在 sourcerequested 事件中被破坏的临时图像对象。这是因为我们不知道在用户选择“播放到”接收器前超时是否到期。如果发生此类情况,则当前图像将被破坏,并且我们将向“播放到”传递空引用。反之,在用户选择“播放到”接收器后,我们将向“播放到”传递指向绝不会被破坏的图像对象并将图像源更新为当前显示的图像。我们知道这发生在图像的状态更改为 connected 时。

在 Default.js 文件中,在上一步骤的代码后面添加以下代码。

// 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. 创建一个“播放到”目标(可选)

要运行该应用程序,你需要选择一个目标设备,以便“播放到”功能可将媒体以流数据的形式传输到该设备。如果你没有经过认证的“播放到”接收器,则可使用 Windows Media Player 作为目标设备。要使用 Windows Media Player 作为目标设备,你的计算机必须连接到专用网络。Windows Media Player 必须运行在与你的“播放到”源应用不同的计算机上。

  1. 启动 Windows Media Player。
  2. 展开“流”菜单,并启用“允许远程控制我的播放器...”选项。****保持打开 Windows Media Player;它必须保持运行才能用作“播放到”目标。
  3. 打开“设备和打印机”控制面板。单击“添加设备和打印机”****。在“添加设备和打印机”向导中,在“选择要添加到此电脑的设备或打印机”窗口中找到你的电脑的“数字媒体呈现器”。这是你的电脑的 Windows Media Player。选中它,然后单击“下一步”。当向导完成后,你会在“多媒体设备”列表中看到你的 Windows Media Player 实例。

7. 运行该应用

  • 在 Visual Studio 中,按 F5(调试)以运行应用。你可以选择任何一个媒体按钮以播放或查看不同媒体库中的第一个媒体项目。在媒体播放期间,打开“设备”超级按钮并选择你的“播放到”目标,以将媒体以流数据的形式传输到该目标设备。

摘要

在本快速入门中,你已经将“播放到”功能添加到显示以幻灯片放映形式传输到目标设备的图像的应用程序中。“播放到”功能使用户能够将内容以流数据的形式从应用程序传输到在他们的网络中经过认证的“播放到”接收器。

相关主题

使用“播放到”向设备流式播放媒体

示例

播放到示例

PlayToReceiver 示例

媒体服务器示例