Videos mit der Funktion „Bild im Bild“ ansehen

François Beaufort
François Beaufort

Mit Bild im Bild (BiB) können Nutzer Videos in einem unverankerten Fenster ansehen (immer über anderen Fenstern). So haben sie bei der Interaktion mit anderen Websites oder Anwendungen einen besseren Überblick darüber, was sie gerade sehen.

Mit der Picture-in-Picture-Web API kannst du die Funktion „Bild im Bild“ für Videoelemente auf deiner Website starten und steuern. Probiert unser offizielles Bild-im-Bild-Beispiel aus.

Hintergrund

Im September 2016 hat Safari die Bild-im-Bild-Unterstützung über eine WebKit API in macOS Sierra hinzugefügt. Sechs Monate später spielte Chrome mit der Veröffentlichung von Android O über eine native Android API automatisch Bild-im-Bild-Videos auf Mobilgeräten ab. Sechs Monate später haben wir unsere Absicht bekannt gegeben, eine Web-API zu erstellen und zu standardisieren. Diese Web-API ist mit Safari kompatibel und ermöglicht Webentwicklern, die Nutzung von Bild im Bild zu steuern. Und da ist es!

In den Code einbauen

Bild im Bild aktivieren

Beginnen wir mit einem Videoelement und einer Möglichkeit für die Nutzer, damit zu interagieren, z. B. mit einem Schaltflächenelement.

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

Fordern Sie Bild im Bild nur als Reaktion auf eine Nutzergeste an und nie im Promise, das von videoElement.play() zurückgegeben wird. Das liegt daran, dass in Versprechen noch keine Nutzergesten weitergegeben werden. Rufen Sie stattdessen wie unten gezeigt requestPictureInPicture() in einem Klick-Handler für pipButtonElement auf. Sie sind dafür verantwortlich, zu handhaben, was passiert, wenn ein Nutzer zweimal klickt.

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

  await videoElement.requestPictureInPicture();

  pipButtonElement.disabled = false;
});

Wenn das Promise aufgelöst wird, verkleinert Chrome das Video in ein kleines Fenster, das der Nutzer bewegen und über anderen Fenstern positionieren kann.

Fertig! Gut gemacht! Sie können aufhören zu lesen und Ihren wohlverdienten Urlaub nehmen. Das ist leider nicht immer der Fall. Das Versprechen kann aus einem der folgenden Gründe ablehnen:

  • Bild im Bild wird vom System nicht unterstützt.
  • Das Dokument darf die Funktion „Bild im Bild“ aufgrund einer restriktiven Berechtigungsrichtlinie nicht verwenden.
  • Die Videometadaten wurden noch nicht geladen (videoElement.readyState === 0).
  • Die Videodatei ist nur Audio.
  • Das neue Attribut disablePictureInPicture ist im Videoelement vorhanden.
  • Der Aufruf wurde nicht in einem Ereignis-Handler für Touch-Gesten ausgeführt (z.B. ein Klick auf eine Schaltfläche). Ab Chrome 74 gilt dies nur dann, wenn im Bild-im-Bild-Modus noch kein Element vorhanden ist.

Der folgende Abschnitt Funktionsunterstützung zeigt, wie eine Schaltfläche basierend auf diesen Einschränkungen aktiviert/deaktiviert wird.

Fügen wir einen try...catch-Block hinzu, um diese potenziellen Fehler zu erfassen und den Nutzer über das Problem zu informieren.

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

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

Das Videoelement verhält sich immer gleich, egal ob es sich im Bild-im-Bild-Modus befindet oder nicht: Ereignisse werden ausgelöst und die Aufrufmethoden funktionieren. Sie spiegelt Statusänderungen im Bild-im-Bild-Fenster wider (z. B. Wiedergabe, Pause, Suche usw.). In JavaScript ist es auch möglich, den Status programmatisch zu ändern.

Bild im Bild beenden

Mit der Schaltfläche können wir den Bild-im-Bild-Modus aktivieren und deaktivieren. Zuerst müssen wir prüfen, ob das schreibgeschützte Objekt document.pictureInPictureElement unser Videoelement ist. Ist dies nicht der Fall, senden wir eine Anfrage zur Nutzung von Bild im Bild (siehe oben). Andernfalls müssen wir document.exitPictureInPicture() aufrufen, damit das Video wieder auf dem ursprünglichen Tab angezeigt wird. Beachte, dass diese Methode auch ein Promise zurückgibt.

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

Auf Bild-im-Bild-Ereignisse warten

Betriebssysteme beschränken die Bild-im-Bild-Funktion in der Regel auf ein Fenster, sodass die Implementierung von Chrome diesem Muster folgt. Das bedeutet, dass Nutzer jeweils nur ein Bild-im-Bild-Video abspielen können. Sie sollten davon ausgehen, dass Nutzende Bild-im-Bild verlassen werden, auch wenn Sie nicht danach gefragt haben.

Mit den neuen Event-Handlern enterpictureinpicture und leavepictureinpicture können wir die Nutzung auf Nutzer anpassen. Dabei kann es sich um Videos oder einen Livestream-Chat handeln.

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.
});

Fenster „Bild im Bild“ anpassen

Chrome 74 unterstützt die Schaltflächen für Wiedergabe/Pause, vorheriger Titel und nächster Titel im Bild-im-Bild-Fenster, die Sie mithilfe der Media Session API steuern können.

Wiedergabesteuerung für Medien in einem Bild-im-Bild-Fenster
Abbildung 1. Wiedergabesteuerung für Medien in einem Bild-im-Bild-Fenster

Standardmäßig wird im Bild-im-Bild-Fenster immer eine Schaltfläche für Wiedergabe/Pause angezeigt, es sei denn, im Video werden MediaStream-Objekte (z.B. getUserMedia(), getDisplayMedia(), canvas.captureStream()) wiedergegeben oder für das Video ist eine MediaSource-Dauer auf +Infinity festgelegt (z.B. Livefeed). Um sicherzustellen, dass eine Wiedergabe-/Pause-Schaltfläche immer sichtbar ist, legen Sie einige Media Session-Aktions-Handler für die Medienereignisse „Play“ und „Pause“ fest (siehe unten).

// 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.
});

Die Fenster „Vorheriger Titel“ und „Nächster Titel“ werden ähnlich angezeigt. Wenn du Aktions-Handler für Mediensitzungen für diese festlegst, werden sie im Bild-im-Bild-Fenster angezeigt und du kannst diese Aktionen ausführen.

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

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

Wenn du dies in Aktion sehen möchtest, probiere das offizielle Media Session-Beispiel aus.

Bild-im-Bild-Fenstergröße abrufen

Wenn Sie die Videoqualität beim Ein- und Ausschalten des Videos anpassen möchten, müssen Sie die Größe des Bild-im-Bild-Fensters kennen und benachrichtigt werden, wenn ein Nutzer die Größe des Fensters manuell ändert.

Das folgende Beispiel zeigt, wie Breite und Höhe des Bild-im-Bild-Fensters abgerufen werden können, wenn es erstellt oder seine Größe angepasst wird.

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

Es empfiehlt sich, nicht direkt auf das Größenänderungsereignis zu hängen, da jede kleine Änderung an der Bild-im-Bild-Fenstergröße ein separates Ereignis auslöst, das zu Leistungsproblemen führen kann, wenn Sie bei jeder Größenänderung einen aufwendigen Vorgang ausführen. Das heißt, dass die Ereignisse durch den Größenänderungsvorgang immer wieder sehr schnell ausgelöst werden. Zur Lösung dieses Problems empfehlen wir, gängige Techniken wie Drosselung und Entprellung zu verwenden.

Funktionsunterstützung

Da die Picture-in-Picture-Web API möglicherweise nicht unterstützt wird, musst du dies erkennen, um die progressive Verbesserung nutzen zu können. Selbst wenn die Funktion unterstützt wird, kann sie vom Nutzer deaktiviert oder durch eine Berechtigungsrichtlinie deaktiviert werden. Glücklicherweise können Sie den neuen booleschen Wert document.pictureInPictureEnabled verwenden, um dies zu bestimmen.

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.');
}

Wenn die Schaltfläche auf ein bestimmtes Schaltflächenelement für ein Video angewendet wird, kannst du die Sichtbarkeit der Schaltfläche „Bild im Bild“ anpassen.

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;
}

Unterstützung von MediaStream-Videos

Bei Videos, in denen MediaStream-Objekte wie getUserMedia(), getDisplayMedia() oder canvas.captureStream() wiedergegeben werden, wird die Funktion „Bild im Bild“ in Chrome 71 ebenfalls unterstützt. Das bedeutet, dass Sie ein Bild-im-Bild-Fenster anzeigen können, das den Webcam-Videostream, den Display-Videostream oder sogar ein Canvas-Element des Nutzers enthält. Das Videoelement muss nicht an das DOM angehängt sein, um den Bild-im-Bild-Modus aufzurufen (siehe unten).

Webcam des Nutzers im Bild-im-Bild-Fenster anzeigen

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

// Later on, video.requestPictureInPicture();

Anzeige im Bild-im-Bild-Fenster anzeigen

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

// Later on, video.requestPictureInPicture();

Canvas-Element im Bild-im-Bild-Fenster anzeigen

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();

Wenn Sie canvas.captureStream() mit der Media Session API kombinieren, können Sie in Chrome 74 beispielsweise ein Fenster für eine Audioplaylist erstellen. Dann sieh dir das offizielle Audio-Playlist-Sample an.

Audioplaylist in einem Bild-im-Bild-Fenster
Abbildung 2. Audioplaylist in einem Bild-im-Bild-Fenster

Beispiele, Demos und Codelabs

Sehen Sie sich unser offizielles Bild-im-Bild-Beispiel an, um die Bild-im-Bild-Web API auszuprobieren.

Demos und Codelabs folgen.

Weiteres Vorgehen

Sehen Sie sich zuerst auf der Seite zum Implementierungsstatus an, welche Teile der API derzeit in Chrome und anderen Browsern implementiert sind.

In naher Zukunft erwarten Sie Folgendes:

Unterstützte Browser

Die Bild-im-Bild-Web-API wird in Chrome, Edge, Opera und Safari unterstützt. Weitere Informationen finden Sie unter MDN.

Ressourcen

Vielen Dank an Mounir Lamouri und Jennifer Apacible für ihre Arbeit an Bild im Bild und für die Unterstützung in diesem Artikel. Vielen Dank an alle, die an den Standardisierungsbemühungen teilgenommen haben.