Oglądanie filmu w trybie obrazu w obrazie

François Beaufort
François Beaufort

Funkcja obraz w obrazie umożliwia oglądanie filmów w pływającym oknie (zawsze nad innymi oknami), dzięki czemu mogą obserwować, co oglądają podczas korzystania z innych witryn lub aplikacji.

Za pomocą interfejsu Picture-in-Picture Web API możesz uruchamiać obrazy w obrazie w elementach wideo w swojej witrynie i nimi zarządzać. Wypróbuj tę funkcję za pomocą naszej oficjalnej próbki funkcji obraz w obrazie.

Wprowadzenie

We wrześniu 2016 r. Safari dodała obsługę obrazu w obrazie za pomocą interfejsu WebKit API w systemie macOS Sierra. Sześć miesięcy później Chrome automatycznie odtwarzał filmy w trybie obrazu w obrazie na urządzeniach mobilnych z Androidem O, korzystając z natywnego interfejsu API Androida. Sześć miesięcy później ogłosiliśmy zamiar utworzenia i standaryzacji interfejsu Web API, który jest zgodny z przeglądarką Safari, który umożliwi programistom internetowym tworzenie i kontrolowanie wszystkich funkcji związanych z obrazem w obrazie. I o to właśnie chodzi.

Wejdź do kodu

Włącz tryb obrazu w obrazie

Zacznijmy od elementu wideo i sposobu, w jaki użytkownik może z niego korzystać, np. przycisku.

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

Obrazy w obrazie należy wysyłać tylko w odpowiedzi na gest użytkownika. Nie można tego robić w ramach obietnicy zwracanej przez funkcję videoElement.play(). Dzieje się tak, ponieważ obietnice jeszcze nie propagują gestów użytkowników. Zamiast tego wywołaj requestPictureInPicture() w module obsługi kliknięć pipButtonElement, jak pokazano poniżej. To Ty odpowiadasz za to, co się stanie, gdy użytkownik kliknie dwa razy.

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

  await videoElement.requestPictureInPicture();

  pipButtonElement.disabled = false;
});

Po rozwiązaniu obietnicy Chrome zmniejszy film do małego okna, które użytkownik będzie mógł przesuwać i ustawiać nad innymi oknami.

Gotowe. Brawo! Możesz przestać czytać i udać się do zasłużonego urlopu. Niestety, nie zawsze tak jest. Obietnica może zostać odrzucona z dowolnego z tych powodów:

  • System nie obsługuje trybu obrazu w obrazie.
  • Na tym dokumencie nie można korzystać z funkcji obraz w obrazie ze względu na restrykcyjne zasady dotyczące uprawnień.
  • Metadane filmu nie zostały jeszcze wczytane (videoElement.readyState === 0).
  • Plik wideo zawiera tylko dźwięk.
  • W elemencie wideo występuje nowy atrybut disablePictureInPicture.
  • Wywołanie nie zostało wykonane w ramach modułu obsługi zdarzeń gestów (np. kliknięcia przycisku). Od wersji Chrome 74 ten problem dotyczy tylko sytuacji, gdy w trybie obrazu w obrazie nie ma jeszcze żadnego elementu.

W sekcji Obsługa funkcji poniżej dowiesz się, jak włączyć lub wyłączyć przycisk na podstawie tych ograniczeń.

Dodajmy blok try...catch, aby wychwycić te potencjalne błędy i poinformować użytkownika, co się dzieje.

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

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

Element video działa tak samo niezależnie od tego, czy działa w obrazie w obrazie, czy też nie: wywoływane są zdarzenia, a metody wywoływania działają. Odzwierciedla zmiany stanu w oknie obrazu w obrazie (np. odtwarzanie, wstrzymanie, przewijanie itp.), a także możliwość automatycznej zmiany stanu w JavaScript.

Wyłącz obraz w obrazie

Przełączmy przycisk, aby otworzyć i zamknąć tryb obrazu w obrazie. Najpierw musimy sprawdzić, czy obiektem tylko do odczytu document.pictureInPictureElement jest nasz element wideo. Jeśli nie, wysyłamy prośbę o włączenie obrazu w obrazie (jak powyżej). Jeśli go nie widzisz, zadzwonimy pod numer document.exitPictureInPicture(). Oznacza to, że film pojawi się z powrotem na oryginalnej karcie. Pamiętaj, że ta metoda również zwraca obietnicę.

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

Słuchanie zdarzeń typu obraz w obrazie

Systemy operacyjne zazwyczaj ograniczają obraz w obrazie do jednego okna, więc implementacja Chrome jest zgodna z tym wzorcem. Oznacza to, że użytkownicy mogą w danym momencie odtwarzać tylko jeden film w trybie obrazu w obrazie. Użytkownicy wyłączają obraz w obrazie nawet wtedy, gdy o to nie prosisz.

Nowe moduły obsługi zdarzeń enterpictureinpicture i leavepictureinpicture pozwalają nam dostosowywać środowisko do potrzeb użytkowników. Może to być wszystko – od przeglądania katalogu filmów po czat w transmisji na żywo.

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

Dostosowywanie okna obrazu w obrazie

Chrome 74 obsługuje przyciski odtwarzania/wstrzymania, poprzedniej i następnej ścieżki w oknie obrazu w obrazie, którym można sterować za pomocą interfejsu Media Session API.

Elementy sterujące odtwarzaniem multimediów w oknie obrazu w obrazie
Rysunek 1. Elementy sterujące odtwarzaniem multimediów w oknie obrazu w obrazie

Domyślnie przycisk odtwarzania/wstrzymania w oknie obrazu w obrazie jest zawsze wyświetlany, chyba że podczas odtwarzania filmu są odtwarzane obiekty MediaStream (np. getUserMedia(), getDisplayMedia() czy canvas.captureStream()) lub czas trwania MediaSource jest ustawiony na +Infinity (np. w przypadku transmisji na żywo). Aby przycisk odtwarzania/wstrzymania był zawsze widoczny, skonfiguruj moduły obsługi działań w sesji multimediów dla zdarzeń multimedialnych „Odtwarzanie” i „Wstrzymaj”, jak poniżej.

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

Opcje okien „Poprzednia ścieżka” i „Następna ścieżka” działają podobnie. Po ustawieniu modułów obsługi sesji multimedialnych dla tych działań będą one wyświetlane w oknie obrazu w obrazie i będziesz mieć możliwość ich obsługi.

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

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

Aby zobaczyć, jak to działa, wypróbuj oficjalną przykładową sesję medialną.

Ustaw rozmiar okna obrazu w obrazie

Jeśli chcesz dostosować jakość filmu po otwarciu i wyjściu filmu, musisz znać rozmiar okna obrazu w obrazie i otrzymywać powiadomienia, gdy użytkownik ręcznie zmieni rozmiar okna.

Poniższy przykład pokazuje, jak uzyskać szerokość i wysokość okna obrazu w obrazie podczas jego tworzenia lub zmiany jego rozmiaru.

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

Sugerujemy, aby nie łączyć bezpośrednio zdarzenia zmiany rozmiaru, ponieważ każda mała zmiana rozmiaru okna obrazu w obrazie spowoduje uruchomienie osobnego zdarzenia, które może powodować problemy z wydajnością, jeśli przy każdej zmianie rozmiaru wykonujesz kosztowne operacje. Innymi słowy, operacja zmiany rozmiaru będzie wywoływać zdarzenia w nieskończoność bardzo szybko. Aby rozwiązać ten problem, zalecamy użycie popularnych technik, takich jak ograniczanie i wyciszanie reklam.

Obsługa funkcji

Interfejs Picture-in-Picture Web API może nie być obsługiwany, więc musisz go wykryć, aby korzystać z progresywnych ulepszeń. Nawet jeśli jest obsługiwana, użytkownik może ją wyłączyć lub wyłączyć przez zasadę uprawnień. Aby to określić, możesz użyć nowej wartości logicznej document.pictureInPictureEnabled.

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

Stosowany do konkretnego elementu przycisku.

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

Obsługa wideo MediaStream

Odtwarzane w filmach obiekty MediaStream (np. getUserMedia(), getDisplayMedia(), canvas.captureStream()) również obsługują obraz w obrazie w Chrome 71. Oznacza to, że możesz wyświetlić okno obrazu w obrazie, które zawiera strumień wideo z kamery internetowej użytkownika, strumień wideo z sieci reklamowej, a nawet element canvas. Pamiętaj, że aby można było korzystać z obrazu w obrazie, jak pokazano poniżej, element video nie musi być dołączony do modelu DOM.

Pokazuj obraz z kamery internetowej użytkownika w oknie obrazu w obrazie

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

// Later on, video.requestPictureInPicture();

Pokaż ekran w oknie obrazu w obrazie

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

// Later on, video.requestPictureInPicture();

Pokazuj element na płótnie w oknie obrazu w obrazie

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

Dzięki połączeniu canvas.captureStream() z interfejsem Media Session API możesz na przykład utworzyć okno playlisty audio w Chrome 74. Wypróbuj oficjalną próbkę playlisty audio.

Playlista audio w oknie obrazu w obrazie
Rysunek 2. Playlista audio w oknie obrazu w obrazie

Przykłady, wersje demonstracyjne i ćwiczenia z programowania

Zapoznaj się z naszym oficjalnym przykładem funkcji Obraz w obrazie, aby wypróbować interfejs Picture-in-Picture Web API.

Zostaną wyświetlone prezentacje i ćwiczenia z programowania.

Co dalej

Po pierwsze, wejdź na stronę stanu implementacji, aby dowiedzieć się, które części interfejsu API są obecnie wdrożone w Chrome i innych przeglądarkach.

W najbliższej przyszłości:

Obsługiwane przeglądarki

Interfejs Picture-in-Picture Web API jest obsługiwany w przeglądarkach Chrome, Edge, Opera i Safari. Więcej informacji znajdziesz w MDN.

Zasoby

Dziękujemy Mounir Lamouri i Jennifer Apacible za pracę nad obrazem w obrazie. Pomogę też w tym artykule. Serdecznie dziękujemy za zaangażowanie w standaryzację.