Tra le varie feature di WinRT, una delle più originali è rappresentata dalla funzionalità Play To, che permette di effettuare lo streaming di elementi multimediali, come video, musica e foto, da un'applicazione Windows Store verso altri device connessi alla rete (come TV, Xbox e altri ricevitori abilitati), semplicemente cliccando sul charm Devices una volta che l'app si trova in esecuzione.
Immaginate, ad esempio, di condividere con un semplice gesto il video o le foto che state guardando sul vostro PC desktop o tablet con amici e familiari sulla TV di casa, o di eseguire contenuti musicali presenti sul vostro device su speaker connessi alla rete wireless.
Tutti i device e le periferiche in grado di sfruttare la funzionalità Play To espongono un apposito logo, mostrato nella prossima immagine (l'elenco aggiornato dei produttori e dei device che supportano questa funzionalità può essere verificato qui).
In questo articolo, vedremo come implementare un'applicazione Windows Store in HTML5/JavaScript in grado di effettuare lo streaming di elementi multimediali verso un qualunque device compatibile con questa tecnologia (in questo caso si parla di applicazione Play To "source"). Nel prossimo articolo, invece, vedremo come realizzare un'applicazione Windows Store in grado di ricevere questo stesso streaming (e per questo detta anche applicazione "receiver").
Il contratto Play To Source
Perché sia effettivamente possibile utilizzare la funzione Play To è necessario essere connessi a una rete e aver abilitato la condivisione delle risorse, come mostrato nella prossima immagine:
Una volta abilitata la condivisione, nel pannello PC Settings Devices appariranno i vari device connessi alla rete, come mostrato nella prossima figura:
Se non ci sono device Play To connessi alla rete, o se è stata disabilitata l'opzione di condivisione, cliccando sul charm Devices otterremo il seguente messaggio:
È importante sottolineare che qualunque applicazione Windows Store che "lavori" con elementi multimediali è già in grado di sfruttare per default la funzionalità Play To. Infatti, se l'utente seleziona il charm Devices quando l'app è in esecuzione e clicca su uno dei ricevitori Play To, Windows effettuerà lo stream utilizzando il primo elemento multimediale presente nella pagina.
Nota: Windows 8.1 ha modificato leggermente il comportamento del charm Devices. Per attivare la funzione Play To, dopo aver attivato il charm, occorre selezionare Play, come mostrato nella seguente immagine:
Questo comportamento di default può andare bene per applicazioni che presentano un solo elemento multimediale per pagina, mentre se vogliamo dotare la nostra app di un'interazione con l'utente anche minimamente evoluta, come ad esempio la possibilità di creare playlist di video e musica, o di effettuare uno slide show delle foto presenti nelle librerie dell'utente, è necessario implementare il contratto Play To.
Questo contratto introduce un elevato livello di astrazione rispetto alle sottostanti tecnologie, protocolli di streaming e formati multimediali, rendendo l'implementazione di questa funzionalità all'interno della tua applicazione Windows Store relativamente semplice.
Il seguente snippet mostra il pattern di base che caratterizza il contratto PlayTo (avremo modo di approfondire i vari dettagli nelle prossime pagine):
app.onloaded = function () {
var pm = Windows.Media.PlayTo.PlayToManager.getForCurrentView();
pm.onsourcerequested = sourceRequestedHandler;
}
function sourceRequestedHandler(args) {
var videoPlayer = document.getElementById("VideoPlayer");
if (videoPlayer !== null) {
args.sourceRequest.setSource(videoPlayer.msPlayToSource);
}
}
Per prima cosa, il codice ottiene una reference a un oggetto di tipo Windows.Media.PlayTo.PlayToManager
specifico per la pagina corrente invocando il metodo GetForCurrentView
della classe PlayToManager
, ossia la classe responsabile della gestione delle operazioni di streaming; a questo punto, il codice sottoscrive l'evento SourceRequested
e implementa il relativo handler.
Questo evento viene scatenato dal sistema quando l'utente attiva il charm Devices, ed è qui che occorre impostare l'elemento multimediale da usare per lo stream tramite il metodo SetSource
dell'oggetto di tipo PlayToSourceRequestedEventArgs
ricevuto come parametro dal relativo handler. A questo punto, quando, l'utente seleziona uno dei device Play To elencati nel pannello Devices, l'applicazione inizierà lo streaming dell'elemento multimediale verso il device selezionato.
Il flusso verso il device selezionato continuerà anche se l'applicazione è stata messa in background perché l'utente ha nel frattempo lanciato un'altra app. Questo perché l'applicazione source non viene sospesa dal sistema fino a quando il video o il brano musicale continuano ad essere eseguiti sul device target, ovvero fino a quando nuove immagini vengono inviate al ricevitore. In altri termini, il sistema mantiene l'app in esecuzione fino a quando la sessione Play To è ancora attiva.
In particolare, secondo la documentazione ufficiale MSDN, un'applicazione ha circa 10 secondi per inviare un nuovo file audio o video dopo che la riproduzione del contenuto precedente è terminata, ovvero per inviare una nuova immagine dopo che quella precedente è stata visualizzata, prima che l'app sia posta in sospensione dal sistema.
Creare un ambiente di test con il Windows Media Player
Prima di implementare la nostra applicazione source, vediamo come impostare l'ambiente di test in modo da poter verificare il corretto funzionamento dell'applicazione anche se non si dispone di un ricevitore compatibile con la funzionalità Play To.
Per testare questa funzionalità, è infatti possibile sfruttare Windows Media Player in esecuzione su un altro computer connesso in rete (PC, notebook, tablet o anche una macchina virtuale). È sufficiente lanciare un'istanza di Windows Media Player sul dispositivo remoto equipaggiato con Windows 8.x e, nel menu Stream, abilitare l'opzione Allow Remote Control Of My Player, come mostrato nella prossima immagine (è importante non chiudere l'istanza di Windows Media Player fino a quando non abbiamo terminato di testare la nostra applicazione).
A questo punto torniamo sulla macchina in locale e, nel pannello PC Settings, clicchiamo su Devices. Windows dovrebbe riconoscere e installare il nuovo dispositivo target non appena disponibile. Se l'istanza remota di Windows Media Player non viene visualizzata nell'elenco, cliccchiamo sul pulsante Add Device
nell'angolo superiore destro del pannello. La prossima immagine mostra un'istanza di Windows Media Player in esecuzione su una macchina virtuale con sopra Windows 8.
A questo punto possiamo utilizzare la funzione Play To per effettuare lo streaming di un elemento multimediale verso la macchina remota. Dalla Start screen di Windows, lanciamo una delle applicazioni native di Windows 8, come ad esempio Photos o Videos, selezioniamo un elemento multimediale dalla libreria e attiviamo il charm Devices; quindi selezioniamo l'istanza di Windows Media Player in esecuzione sul device remoto per iniziare lo streaming.
La prossima immagine mostra lo streaming di un video dalla macchina locale verso l'istanza di Windows Media Player in esecuzione su una macchina virtuale con Windows 8.
Implementare un'applicazione Play To source
Per testare il codice presentato nelle prossime pagine, utilizziamo come riferimento per la pagina di default (default.html
) dell'applicazione il seguente markup HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Demo.Html.it.PlayToSource.JS</title>
<!-- WinJS references -->
<link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>
<!-- Demo.Html.it.PlayToSource.JS references -->
<link href="/css/default.css" rel="stylesheet" />
<script src="/js/default.js"></script>
</head>
<body>
<div id="videoFrame">
<video id="videoPlayer" width="800" height="640" controls="controls" />
</div>
<div id="connectionStatus"></div>
<div id="errorMessage"></div>
</body>
In questa semplice applicazione demo ci limiteremo a effettuare lo streaming del primo video disponibile nella libreria Videos dell'utente (accertiamoci di avere almeno un video nella libreria per testare il codice).
Poiché il codice ha bisogno di accedere alle librerie dell'utente, è necessario dichiarare la corrispondente "capability" nell'application manifest dell'applicazione, altrimenti il sistema solleverà una eccezione. La prossima immagine mostra il file Package.appxmanifest
con la relativa dichiarazione.
Il seguente snippet di codice mostra l'handler dell'evento di click che carica il primo video trovato nella libreria Videos dell'utente sfruttando il metodo GetFilesAsync
della classe VideosLibrary
e quindi lo esegue in loop impostando a true
la proprietà loop
dell'oggetto media element.
var pm;
app.onloaded = function () {
var player = document.getElementById("videoPlayer");
Windows.Storage.KnownFolders.videosLibrary.
getFilesAsync().then(function (resultLibrary) {
if (resultLibrary.length > 0) {
player.src = URL.createObjectURL(resultLibrary[0]);
player.loop = true;
player.play();
document.getElementById("connectionStatus").innerHTML = "Video in riproduzione: " + resultLibrary[0].name + "<br />";
}
});
pm = Windows.Media.PlayTo.PlayToManager.getForCurrentView();
pm.onsourcerequested = sourceRequestedHandler;
pm.onsourceselected = sourceSelectedHandler;
}
Dopo aver recuperato il media element dalla cartella Videos
dell'utente, il codice ottiene una reference all'oggetto PlayToManager
per la pagina corrente tramite il metodo GetForCurrentView
, quindi si abbona ai due eventi esposti dalla classe PlayToManager
, vale a dire SourceRequested
e SourceSelected
(su quest'ultimo avremo modo di tornare più avanti nelle prossime pagine).
Come abbiamo già accennato, l'evento SourceRequested
viene scatenato dal sistema quando l'utente attiva il charm Devices (ma prima che venga selezionato un device target). Nel relativo handler, il codice utilizza la proprietà SourceRequest
dell'oggetto PlayToSourceRequestedEventArgs
ricevuto come parametro per impostare l'elemento multimediale da inviare al device target.
La proprietà SourceRequest
è un'istanza della classe PlayToSourceRequest
e, come il nome suggerisce, rappresenta una richiesta di streaming dell'elemento multimediale verso il device Play To. Il seguente codice mostra questo passaggio.
function sourceRequestedHandler(args) {
try {
var deferral = args.sourceRequest.getDeferral();
var player = document.getElementById("videoPlayer");
var controller = player.msPlayToSource;
args.sourceRequest.setSource(controller);
controller.connection.addEventListener("error", playToConnectionErrorHandler);
controller.connection.addEventListener("statechanged", playToConnectionStateChangedHandler);
controller.connection.addEventListener("transferred", playToConnectionTransferredHandler);
deferral.complete();
}
catch (ex) {
document.getElementById("errorMessage").innerHTML =
"Qualcosa è andato storto: " + ex.message;
}
}
Questo codice mostra anche altri punti che vale la pena di approfondire. In primo luogo, WinRT assegna all'applicazione un intervallo di 200 millisecondi per impostare l'elemento multimediale da utilizzare per lo stream, trascorsi i quali l'evento SourceRequested
andrà in time-out e il charm Devices non mostrerà alcun target Play To cui inviare lo stream. Nel caso in cui si renda necessario un intervallo di tempo maggiore, le API di WinRT mettono a disposizione il metodo GetDeferral
della classe PlayToSourceRequest
per creare un deferral ed effettuare una chiamata asincrona per recuperare il file multimediale.
A questo punto WinRT attenderà fino a quando il deferral non sarà marcato come completato mediante l'invocazione del metodo Complete
(l'intervallo di tempo concesso da WinRT per la conclusione dell'operazione asincrona non risulta ancora documentato ufficialmente su MSDN; tuttavia, è possibile determinare il tempo rimanente ispezionando la proprietà Deadlline
, di tipo DateTimeOffset
, esposta dall'oggetto PlayToSourceRequest
).
L'oggetto di tipo PlayToSource
passato al metodo SetSource
rappresenta l'elemento multimediale da inviare in streaming al device target. Questa classe espone una proprietà Connection
(di tipo PlayToConnection
) che, tramite tre eventi specifici, permette di essere aggiornati sullo stato della connessione con il target Play To. Gli eventi sono i seguenti:
Evento | Descrizione |
---|---|
Error | Scatenato al verificarsi di un errore nella connessione (ad esempio, il device target non risponde, è bloccato o è in errore); il tipo di errore può essere ispezionato mediante la proprietà Code (di tipo PlayToConnectionError ) dell'oggetto PlayToConnectionErrorEventArgs ricevuto come parametro dal relativo handler. Questa proprietà può assumere i valori di DeviceError , DeviceNotResponding , DeviceLocked o, nel caso si stia cercando di inviare file protetti, ProtectedPlaybackFailed . |
StateChanged | Scatenato quando lo stato della connessione cambia e indica, tramite l'oggetto di tipo PlayToConnectionStateChangedEventArgs ricevuto come parametro dal relativo handler, lo stato corrente (rappresentato dalla proprietà CurrentState ) e lo stato precedente al cambiamento (rappresentato dalla proprietà PreviousState ). Entrambe queste due proprietà sono di tipo PlayToConnectionState , un enum che può assumere uno dei seguenti valori: Disconnected , Connected e Rendering . |
Transferred | Scatenato ogniqualvolta un nuovo file multimediale viene inviato in streaming al device target. Le due proprietà CurrentSource e PreviousSource , esposte dall'oggetto di tipo PlayToConnectionTransferredEventArgs ricevuto come parametro dal relativo handler consentono di conoscere, rispettivamente, l'elemento sorgente corrente e quello precedente. |
Il seguente listato mostra gli handler per i tre eventi sopra esposti.
function playToConnectionErrorHandler(args) {
if (args.code == Windows.Media.PlayTo.PlayToConnectionError.deviceError ||
args.code == Windows.Media.PlayTo.PlayToConnectionError.deviceNotResponding) {
document.getElementById("connectionStatus").innerHTML += "Si è verificato un errore.
Disconnessione...<br />";
}
}
function playToConnectionStateChangedHandler(args) {
var status = document.getElementById("connectionStatus");
var states = Windows.Media.PlayTo.PlayToConnectionState;
status.innerHTML += "Stato precedente = " + getName(states, args.currentState) + "<br/>" +
"Stato corrente = " + getName(states, args.currentState) + "<br/>";
}
function getName(enumtype, enumvalue) {
for (var enumName in enumtype) {
if (enumtype[enumName] == enumvalue)
return enumName;
}
}
function playToConnectionTransferredHandler(args) {
document.getElementById("connectionStatus").innerHTML += "Connessione trasferita: PreviousSource = " +
args.previousSource + "<br /> CurrentSource = " + args.currentSource;
}
La prossima immagine mostra una porzione della finestra applicativa con i vari stati della connessione durante lo streaming.
Come già accennato, oltre all'evento SourceRequested
, la classe PlayToManager
espone anche un secondo evento, ossia SourceSelected. Questo evento viene scatenato quando l'utente seleziona uno specifico device Play To dall'elenco di quelli disponibili. In altri termini, se l'utente clicca sul charm Devices (facendo così scattare l'evento SourceRequested
), ma poi non seleziona alcun target Play To dall'elenco, l'evento SourceSelected
non viene scatenato. Il relativo handler rappresenta il luogo ideale per controllare che il target Play To selezionato supporti effettivamente lo specifico tipo di file multimediale da inviare. Il seguente snippet mostra un esempio di utilizzo di questo evento:
function sourceSelectedHandler(args) {
if (!args.supportsVideo) {
document.getElementById("errorMessage").innerHTML = "<p>" +
args.friendlyName + " non supporta lo streaming video.
Per favore seleziona un altro dispositivo.";
}
}
Come vedremo meglio nell'articolo dedicato alle applicazioni Play To target, la proprietà FriendlyName
dell'oggetto PlayToSourceSelectedEventArgs
ricevuto come parametro dal relativo handler identifica il device Play To target sulla rete locale.
Infine un'ultima considerazione. Per avviare lo streaming dell'elemento multimediale (così come per interromperlo) è necessario utilizzare il pannello attivabile tramite il charm Devices. Sebbene non sia possibile iniziare lo streaming o disconnettere il device target via codice, le API di WinRT mettono comunque a disposizione una "scorciatoia" rappresentata dal metodo statico ShowPlayToUI
esposto dalla classe PlayToManager
, il quale semplicemente mostra il pannello Play To senza che sia necessario cliccare sul relativo charm. Il seguente metodo ne mostra il funzionamento all'interno di un handler di un ipotetico evento di click.
function showPlayToUserInterface_click(args) {
Windows.Media.PlayTo.PlayToManager.showPlayToUI();
}