Tonton video menggunakan Picture-in-Picture

François Beaufort
François Beaufort

Fitur Picture-in-Picture (PiP) memungkinkan pengguna menonton video di jendela mengambang (selalu di atas jendela lain) sehingga mereka dapat mengawasi apa yang menonton sambil berinteraksi dengan situs atau aplikasi lain.

Dengan Picture-in-Picture Web API, Anda dapat memulai dan mengontrol Picture-in-Picture untuk elemen video di situs Anda. Cobalah di contoh Picture-in-Picture resmi.

Latar belakang

Pada September 2016, Safari menambahkan dukungan Picture-in-Picture melalui WebKit API di macOS Sierra. Enam bulan kemudian, Chrome otomatis diputar Video Picture-in-Picture di seluler dengan rilis Android O menggunakan API Android native. Enam bulan kemudian, kami mengumumkan niat kami untuk membuat dan menstandarkan Web API, fitur yang kompatibel dengan Safari, yang akan memungkinkan pengembang untuk membuat dan mengontrol pengalaman penuh seputar Picture-in-Picture. Inilah kita!

Memahami kode

Masuk ke Mode Picture-in-Picture

Mari kita mulai dengan elemen video dan cara pengguna untuk berinteraksi dengannya, seperti elemen tombol.

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

Hanya minta Picture-in-Picture sebagai respons terhadap gestur pengguna, dan tidak pernah dalam promise yang ditampilkan oleh videoElement.play(). Hal ini karena promise tidak belum menyebarkan gestur pengguna. Sebagai gantinya, panggil requestPictureInPicture() di klik handler pada pipButtonElement seperti yang ditunjukkan di bawah ini. Anda bertanggung jawab untuk menangani apa yang terjadi jika pengguna mengeklik dua kali.

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

  await videoElement.requestPictureInPicture();

  pipButtonElement.disabled = false;
});

Saat promise teratasi, Chrome akan mengecilkan video ke dalam jendela kecil yang pengguna dapat berpindah-pindah dan memosisikan di atas jendela lain.

Tugas Anda sudah selesai! Bagus! Anda dapat berhenti membaca dan mengambil buku yang layak Anda liburan. Sayangnya, hal itu tidak selalu terjadi. Promise dapat ditolak untuk semua alasan berikut:

  • Picture-in-Picture tidak didukung oleh sistem.
  • Dokumen tidak diizinkan menggunakan Picture-in-Picture karena pembatasan kebijakan izin.
  • Metadata video belum dimuat (videoElement.readyState === 0).
  • File video hanya untuk audio.
  • Atribut disablePictureInPicture baru ada di elemen video.
  • Panggilan tidak dilakukan dalam pengendali peristiwa gestur pengguna (misalnya, klik tombol). Mulai Chrome 74, setelan ini hanya berlaku jika tidak ada elemen di Picture-in-Picture.

Bagian Dukungan fitur di bawah menunjukkan cara mengaktifkan/menonaktifkan tombol berdasarkan pembatasan tersebut.

Mari kita tambahkan blok try...catch untuk menangkap potensi error ini dan membiarkan pengguna tahu apa yang sedang terjadi.

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

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

Elemen video berperilaku sama baik dalam Picture-in-Picture atau bukan: peristiwa diaktifkan dan metode pemanggilan berfungsi. Hal itu mencerminkan perubahan status pada jendela Picture-in-Picture (seperti putar, jeda, cari, dll.) dan juga dapat mengubah status secara terprogram di JavaScript.

Keluar dari Picture-in-Picture

Sekarang, mari kita buat tombol beralih untuk masuk dan keluar dari Picture-in-Picture. Rab harus terlebih dahulu memeriksa apakah objek hanya baca document.pictureInPictureElement adalah elemen video kita. Jika tidak, kami akan mengirimkan permintaan untuk memasukkan Picture-in-Picture seperti di atas. Jika tidak, kita meminta untuk pergi dengan menelepon document.exitPictureInPicture(), yang berarti video akan muncul kembali tab awal. Perhatikan bahwa metode ini juga menampilkan promise.

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

Memproses peristiwa Picture-in-Picture

Sistem operasi biasanya membatasi {i> Picture-in-Picture<i} ke satu jendela, jadi Implementasi Chrome mengikuti pola ini. Artinya pengguna hanya dapat memainkan satu video Picture-in-Picture dalam satu waktu. Anda harus memperkirakan pengguna akan keluar Picture-in-Picture meskipun Anda tidak memintanya.

Pengendali peristiwa enterpictureinpicture dan leavepictureinpicture baru memungkinkan menyesuaikan pengalaman bagi pengguna. Bisa apa saja mulai dari menelusuri katalog video, hingga menampilkan chat livestream.

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

Menyesuaikan jendela Picture-in-Picture

Chrome 74 mendukung tombol putar/jeda, trek sebelumnya, dan trek berikutnya di Jendela Picture-in-Picture yang dapat Anda kontrol menggunakan Media Session API.

Kontrol pemutaran media di jendela Picture-in-Picture
Gambar 1. Kontrol pemutaran media di jendela Picture-in-Picture

Secara default, tombol putar/jeda selalu ditampilkan dalam Picture-in-Picture jendela kecuali video sedang memutar objek MediaStream (mis. getUserMedia(), getDisplayMedia(), canvas.captureStream()) atau video memiliki MediaSource durasi ditetapkan ke +Infinity (mis. feed live). Untuk memastikan tombol putar/jeda selalu terlihat, setel beberapa pengendali tindakan Sesi Media untuk "Putar" dan "Pause" (Jeda) peristiwa media seperti di bawah ini.

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

Menampilkan "Lagu Sebelumnya" dan "{i>Next track<i}" {i>window controls<i} yang serupa. Latar (Setting) Pengendali tindakan Sesi Media untuk pengguna tersebut akan menampilkannya di Picture-in-Picture sehingga Anda dapat menangani tindakan ini.

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

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

Untuk melihat cara kerjanya, coba contoh Sesi Media resmi.

Mendapatkan ukuran jendela Picture-in-Picture

Jika Anda ingin menyesuaikan kualitas video saat video masuk dan keluar Picture-in-Picture, Anda perlu mengetahui ukuran jendela Picture-in-Picture serta diberi tahu jika pengguna mengubah ukuran jendela secara manual.

Contoh di bawah ini menunjukkan cara memperoleh lebar dan tinggi Jendela Picture-in-Picture saat dibuat atau diubah ukurannya.

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

Sebaiknya jangan langsung mengaitkan ke peristiwa ubah ukuran karena setiap perubahan kecil dilakukan ke ukuran jendela Picture-in-Picture akan mengaktifkan peristiwa terpisah yang dapat menyebabkan masalah kinerja jika Anda melakukan operasi yang mahal untuk setiap perubahan ukuran. Di beberapa dengan kata lain, operasi ubah ukuran akan memicu peristiwa berulang kali dengan sangat dengan cepat. Saya sarankan untuk menggunakan teknik umum seperti throttling dan debousing untuk mengatasi masalah ini.

Dukungan fitur

Picture-in-Picture Web API mungkin tidak didukung, jadi Anda harus mendeteksinya untuk memberikan {i>progressive enhancement<i}. Bahkan jika didukung, itu mungkin dinonaktifkan oleh pengguna atau dinonaktifkan oleh kebijakan izin. Untungnya, Anda dapat menggunakan boolean baru document.pictureInPictureEnabled untuk menentukannya.

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

Diterapkan ke elemen tombol tertentu untuk video, ini adalah cara yang mungkin Anda inginkan menangani visibilitas tombol Picture-in-Picture Anda.

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

Dukungan video MediaStream

Video memutar objek MediaStream (misalnya getUserMedia(), getDisplayMedia(), canvas.captureStream()) juga mendukung Picture-in-Picture di Chrome 71. Ini berarti Anda dapat menampilkan jendela Picture-in-Picture yang berisi webcam pengguna streaming video, menampilkan streaming video, atau bahkan elemen kanvas. Perhatikan bahwa elemen video tidak harus dilampirkan ke DOM untuk masuk Picture-in-Picture seperti yang ditampilkan di bawah ini.

Tampilkan webcam pengguna di jendela 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();

Tampilkan tampilan di jendela 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();

Tampilkan elemen kanvas di jendela 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();

Dengan menggabungkan canvas.captureStream() dengan Media Session API, Anda dapat untuk membuat jendela playlist audio di Chrome 74. Kunjungi situs resmi Contoh playlist audio.

Playlist audio di jendela Picture-in-Picture
Gambar 2. Playlist audio di jendela Picture-in-Picture

Contoh, demo, dan codelab

Lihat contoh Picture-in-Picture resmi kami untuk mencoba Picture-in-Picture API Web.

Demo dan codelab akan mengikuti.

Langkah selanjutnya

Pertama, lihat halaman status penerapan untuk mengetahui bagian API saat ini diterapkan di Chrome dan browser lainnya.

Berikut beberapa hal yang akan Anda lihat dalam waktu dekat:

Dukungan browser

Picture-in-Picture Web API didukung di Chrome, Edge, Opera, dan Safari. Lihat MDN untuk mengetahui detailnya.

Resource

Terima kasih banyak kepada Mounir Lamouri dan Jennifer Apacible atas karya mereka dalam Picture-in-Picture, dan bantuan terkait artikel ini. Dan terima kasih banyak kepada semuanya yang terlibat dalam upaya standardisasi.