Guardare il video utilizzando Picture in picture

François Beaufort
François Beaufort

La funzionalità Picture in picture (PIP) consente agli utenti di guardare i video in una finestra mobile (sempre in cima alle altre finestre) in modo da poter tenere d'occhio ciò che stanno guardando mentre interagiscono con altri siti o applicazioni.

Con l'API web Picture-in-Picture, puoi avviare e controllare la modalità Picture-in-Picture per gli elementi video sul tuo sito web. Prova la funzionalità sul nostro esempio ufficiale di Picture in picture.

Sfondo

Nel settembre 2016, Safari ha aggiunto il supporto di Picture in picture tramite un'API WebKit in macOS Sierra. Sei mesi dopo, Chrome riproduceva automaticamente i video in Picture in Picture sui dispositivi mobili con il rilascio di Android O utilizzando un'API Android nativa. Sei mesi dopo, abbiamo annunciato la nostra intenzione di creare e standardizzare un'API web, compatibile con quella di Safari, che consenta agli sviluppatori web di creare e controllare l'esperienza completa relativa al Picture-in-Picture. Ed eccoci qui!

Impara a conoscere il codice

Attivare Picture in picture

Iniziamo semplicemente con un elemento video e un modo per consentire all'utente di interagire con esso, ad esempio un elemento pulsante.

<video id="videoElement" src="https://example.com/file.mp4"></video>
<button id="pipButtonElement"></button>

Richiedi la modalità Picture in picture solo in risposta a un gesto dell'utente e mai nel promise restituito da videoElement.play(). Questo perché le promesse non ancora propagano i gesti dell'utente. Chiama invece requestPictureInPicture() in un gestore dei clic su pipButtonElement come mostrato di seguito. È tua responsabilità gestire ciò che accade se un utente fa clic due volte.

pipButtonElement.addEventListener('click', async function () {
  pipButtonElement.disabled = true;

  await videoElement.requestPictureInPicture();

  pipButtonElement.disabled = false;
});

Quando la promessa viene risolta, Chrome riduce il video in una piccola finestra che l'utente può spostare e posizionare su altre finestre.

Hai finito. Ottimo lavoro. Puoi smettere di leggere e partire per le tue meritate vacanze. Purtroppo, non è sempre così. La promessa può essere rifiutata per uno qualsiasi dei seguenti motivi:

  • La funzionalità Picture in picture non è supportata dal sistema.
  • I documenti non possono utilizzare Picture in picture a causa di criteri di autorizzazione restrittivi.
  • I metadati del video non sono ancora stati caricati (videoElement.readyState === 0).
  • Il file video è solo audio.
  • Il nuovo attributo disablePictureInPicture è presente nell'elemento video.
  • La chiamata non è stata effettuata in un gestore di eventi dei gesti dell'utente (ad es. un clic sul pulsante). A partire da Chrome 74, questa opzione è applicabile solo se non è già presente un elemento in Picture in picture.

La sezione Supporto delle funzionalità di seguito mostra come attivare/disattivare un pulsante in base a queste limitazioni.

Aggiungiamo un blocco try...catch per rilevare questi potenziali errori e comunicare all'utente cosa sta succedendo.

pipButtonElement.addEventListener('click', async function () {
  pipButtonElement.disabled = true;

  try {
    await videoElement.requestPictureInPicture();
  } catch (error) {
    // TODO: Show error message to user.
  } finally {
    pipButtonElement.disabled = false;
  }
});

L'elemento video si comporta allo stesso modo in modalità Picture in picture o meno: gli eventi vengono attivati e i metodi di chiamata funzionano. Riflette le modifiche di stato nella finestra Picture in picture (ad esempio riproduzione, messa in pausa, ricerca e così via) ed è anche possibile modificare lo stato in modo programmatico in JavaScript.

Uscire da Picture in picture

Ora, facciamo in modo che il pulsante attivi e disattivi la funzionalità Picture in picture. dobbiamo prima verificare se l'oggetto di sola lettura document.pictureInPictureElement è il nostro elemento video. In caso contrario, invieremo una richiesta per attivare la modalità Picture in Picture come indicato sopra. In caso contrario, chiediamo di uscire chiamandodocument.exitPictureInPicture(), il che significa che il video verrà visualizzato nuovamente nella scheda originale. Tieni presente che anche questo metodo restituisce una promessa.

    ...
    try {
      if (videoElement !== document.pictureInPictureElement) {
        await videoElement.requestPictureInPicture();
      } else {
        await document.exitPictureInPicture();
      }
    }
    ...

Ascoltare gli eventi Picture in picture

In genere, i sistemi operativi limitano la funzionalità Picture in picture a una sola finestra, pertanto l'implementazione di Chrome segue questo schema. Ciò significa che gli utenti possono riprodurre un solo video in Picture in picture alla volta. Dovresti aspettarti che gli utenti escano dalla modalità Picture in picture anche quando non lo chiedi.

I nuovi gestori di eventi enterpictureinpicture e leavepictureinpicture ci consentono di personalizzare l'esperienza per gli utenti. Può essere qualsiasi cosa, dalla navigazione in un catalogo di video alla visualizzazione di una chat live streaming.

videoElement.addEventListener('enterpictureinpicture', function (event) {
  // Video entered Picture-in-Picture.
});

videoElement.addEventListener('leavepictureinpicture', function (event) {
  // Video left Picture-in-Picture.
  // User may have played a Picture-in-Picture video from a different page.
});

Personalizzare la finestra Picture in picture

Chrome 74 supporta i pulsanti per riprodurre/mettere in pausa, Traccia precedente e Traccia successiva nella finestra Picture in picture che puoi controllare usando l'API Media Session.

Controlli di riproduzione di contenuti multimediali in una finestra Picture in picture
Figura 1. Controlli di riproduzione dei contenuti multimediali in una finestra Picture in picture

Per impostazione predefinita, nella finestra Picture in picture viene sempre mostrato un pulsante di riproduzione/pausa, a meno che il video non stia riproducendo oggetti MediaStream (ad es. getUserMedia(), getDisplayMedia(), canvas.captureStream()) o che il video non abbia una durata MediaSource impostata su +Infinity (ad esempio, feed live). Per assicurarti che un pulsante di riproduzione/messa in pausa sia sempre visibile, imposta alcuni gestori di azioni della sessione multimediale sia per gli eventi multimediali "Riproduci" sia per quelli "Metti in pausa" come indicato di seguito.

// Show a play/pause button in the Picture-in-Picture window
navigator.mediaSession.setActionHandler('play', function () {
  // User clicked "Play" button.
});
navigator.mediaSession.setActionHandler('pause', function () {
  // User clicked "Pause" button.
});

La visualizzazione dei controlli della finestra "Brano precedente" e "Brano successivo" è simile. Se imposti gestori di azioni Media Session per questi elementi, verranno visualizzati nella finestra Picture-in-Picture e potrai gestirli.

navigator.mediaSession.setActionHandler('previoustrack', function () {
  // User clicked "Previous Track" button.
});

navigator.mediaSession.setActionHandler('nexttrack', function () {
  // User clicked "Next Track" button.
});

Per vedere come funziona, prova l'esempio di sessione multimediale ufficiale.

Ottenere le dimensioni della finestra Picture in picture

Se vuoi regolare la qualità del video quando entra e esce dal Picture in picture, devi conoscere le dimensioni della finestra Picture in picture e ricevere una notifica se un utente ne modifica manualmente le dimensioni.

L'esempio seguente mostra come ottenere la larghezza e l'altezza della finestra Picture in Picture quando viene creata o ridimensionata.

let pipWindow;

videoElement.addEventListener('enterpictureinpicture', function (event) {
  pipWindow = event.pictureInPictureWindow;
  console.log(`> Window size is ${pipWindow.width}x${pipWindow.height}`);
  pipWindow.addEventListener('resize', onPipWindowResize);
});

videoElement.addEventListener('leavepictureinpicture', function (event) {
  pipWindow.removeEventListener('resize', onPipWindowResize);
});

function onPipWindowResize(event) {
  console.log(
    `> Window size changed to ${pipWindow.width}x${pipWindow.height}`
  );
  // TODO: Change video quality based on Picture-in-Picture window size.
}

Ti consiglio di non eseguire il collegamento direttamente all'evento di ridimensionamento, poiché ogni piccola modifica apportata alle dimensioni della finestra Picture-in-Picture attiverà un evento separato che potrebbe causare problemi di prestazioni se esegui un'operazione dispendiosa a ogni ridimensionamento. In altre parole, l'operazione di ridimensionamento attiverà gli eventi più volte e molto rapidamente. Per risolvere il problema, ti consiglio di utilizzare tecniche comuni come il throttling e il debounce.

Supporto delle funzionalità

L'API web Picture-in-Picture potrebbe non essere supportata, quindi devi rilevarla per fornire il miglioramento progressivo. Anche se è supportata, questa opzione potrebbe essere stata disattivata dall'utente o da un criterio di autorizzazione. Fortunatamente, puoi utilizzare il nuovo valore booleano document.pictureInPictureEnabled per determinare questo.

if (!('pictureInPictureEnabled' in document)) {
  console.log('The Picture-in-Picture Web API is not available.');
} else if (!document.pictureInPictureEnabled) {
  console.log('The Picture-in-Picture Web API is disabled.');
}

Applicato a un elemento del pulsante specifico per un video, ecco come potresti gestire la visibilità del pulsante Picture-in-Picture.

if ('pictureInPictureEnabled' in document) {
  // Set button ability depending on whether Picture-in-Picture can be used.
  setPipButton();
  videoElement.addEventListener('loadedmetadata', setPipButton);
  videoElement.addEventListener('emptied', setPipButton);
} else {
  // Hide button if Picture-in-Picture is not supported.
  pipButtonElement.hidden = true;
}

function setPipButton() {
  pipButtonElement.disabled =
    videoElement.readyState === 0 ||
    !document.pictureInPictureEnabled ||
    videoElement.disablePictureInPicture;
}

Supporto video MediaStream

I video che riproducono oggetti MediaStream (ad es. getUserMedia(), getDisplayMedia(), canvas.captureStream()) supportano anche Picture in picture in Chrome 71. Ciò significa che puoi mostrare una finestra Picture in Picture contenente lo stream video della webcam dell'utente, lo stream video del display o persino un elemento canvas. Tieni presente che l'elemento video non deve essere collegato al DOM per attivare Picture in picture, come illustrato di seguito.

Mostrare la webcam dell'utente nella finestra Picture in picture

const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getUserMedia({video: true});
video.play();

// Later on, video.requestPictureInPicture();

Mostrare il display nella finestra Picture in picture

const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getDisplayMedia({video: true});
video.play();

// Later on, video.requestPictureInPicture();

Mostrare l'elemento della tela nella finestra Picture in picture

const canvas = document.createElement('canvas');
// Draw something to canvas.
canvas.getContext('2d').fillRect(0, 0, canvas.width, canvas.height);

const video = document.createElement('video');
video.muted = true;
video.srcObject = canvas.captureStream();
video.play();

// Later on, video.requestPictureInPicture();

Se combini canvas.captureStream() con l'API Media Session, puoi ad esempio creare una finestra di playlist audio in Chrome 74. Consulta l'esempio di playlist audio ufficiale.

Playlist audio in una finestra Picture in picture
Figura 2. Playlist audio in una finestra Picture in picture

Samples, demo e codelab

Consulta il nostro esempio ufficiale Picture in picture per provare l'API web Picture in picture.

Seguiranno demo e codelab.

Passaggi successivi

Innanzitutto, consulta la pagina dello stato dell'implementazione per sapere quali parti dell'API sono attualmente implementate in Chrome e altri browser.

Ecco cosa puoi aspettarti di vedere nel prossimo futuro:

Supporto browser

L'API Web Picture in picture è supportata in Chrome, Edge, Opera e Safari. Per informazioni dettagliate, consulta la pagina MDN.

Risorse

Un grazie a Mounir Lamouri e Jennifer Apacible per il loro lavoro sul Picture in Picture e per l'aiuto fornito per la stesura di questo articolo. Un grazie enorme a tutti coloro che sono coinvolti nell'impegno di standardizzazione.