Xem video bằng tính năng Hình trong hình

François Beaufort
François Beaufort

Tính năng Hình trong hình (PiP) giúp người dùng xem video trong một cửa sổ nổi (luôn đặt trên các cửa sổ khác) để họ có thể theo dõi chúng xem trong khi tương tác với các trang web hoặc ứng dụng khác.

Với API web Hình trong hình, bạn có thể bắt đầu và kiểm soát Chế độ Hình trong hình cho các thành phần video trên trang web của bạn. Hãy dùng thử trên Mẫu Hình trong hình chính thức.

Thông tin khái quát

Vào tháng 9 năm 2016, Safari đã thêm tính năng hỗ trợ Hình trong hình thông qua WebKit API trong macOS Sierra. Sáu tháng sau, Chrome đã tự động phát Video có tính năng Hình trong hình trên thiết bị di động khi phát hành Android O sử dụng API Android gốc. Sáu tháng sau, chúng tôi thông báo ý định xây dựng và chuẩn hoá một API Web, tính năng tương thích với Safari, cho phép các trang web để các nhà phát triển tạo và kiểm soát toàn bộ trải nghiệm về tính năng Hình trong hình. Và chúng tôi ở đây!

Tìm hiểu về mã

Vào chế độ Hình trong hình

Hãy bắt đầu đơn giản với một thành phần video và cách để người dùng tương tác chẳng hạn như một phần tử nút.

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

Chỉ yêu cầu chế độ Hình trong hình theo cử chỉ của người dùng và không bao giờ được bật promise (lời hứa) do videoElement.play() trả về. Điều này là do lời hứa không chưa phổ biến các cử chỉ của người dùng. Thay vào đó, hãy gọi requestPictureInPicture() trong một trình xử lý lượt nhấp trên pipButtonElement như minh hoạ dưới đây. Bạn có trách nhiệm để xử lý những gì xảy ra nếu người dùng nhấp hai lần.

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

  await videoElement.requestPictureInPicture();

  pipButtonElement.disabled = false;
});

Khi lời hứa được thực hiện, Chrome sẽ thu nhỏ video thành một cửa sổ nhỏ người dùng có thể di chuyển xung quanh và đặt lên trên các cửa sổ khác.

Bạn đã hoàn tất. Tuyệt vời! Bạn có thể ngừng đọc và làm việc xứng đáng đi nghỉ mát. Tiếc là không phải lúc nào cũng như vậy. Lời hứa có thể từ chối cho bất kỳ trong số các lý do sau:

  • Hệ thống này không hỗ trợ chế độ Hình trong hình.
  • Tài liệu không được phép sử dụng tính năng Hình trong hình do hạn chế chính sách về quyền.
  • Siêu dữ liệu video chưa được tải (videoElement.readyState === 0).
  • Tệp video chỉ có âm thanh.
  • Thuộc tính disablePictureInPicture mới hiện đã có trên phần tử video.
  • Lệnh gọi không được thực hiện trong trình xử lý sự kiện cử chỉ của người dùng (ví dụ: nhấp vào nút). Kể từ Chrome 74, lựa chọn này chỉ áp dụng nếu không có phần tử trong Đã có chế độ Hình trong hình.

Mục Hỗ trợ tính năng dưới đây cho biết cách bật/tắt một nút dựa trên các hạn chế này.

Hãy thêm một khối try...catch để phát hiện các lỗi có thể xảy ra này và cho phép người dùng biết điều gì đang xảy ra.

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

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

Phần tử video sẽ hoạt động như nhau cho dù ở chế độ Hình trong hình hay not: sự kiện được kích hoạt còn phương thức gọi hoạt động. Chỉ số này phản ánh các thay đổi của trạng thái trong cửa sổ Hình trong hình (chẳng hạn như phát, tạm dừng, tua, v.v.) và cửa sổ này cũng có thể thay đổi trạng thái theo cách có lập trình trong JavaScript.

Thoát chế độ Hình trong hình

Bây giờ, hãy tạo nút chuyển đổi giữa chế độ nhập và thoát chế độ Hình trong hình. T4 trước tiên, bạn phải kiểm tra xem đối tượng chỉ đọc document.pictureInPictureElement có phải là là phần tử video của chúng tôi. Nếu không, chúng tôi sẽ gửi yêu cầu nhập Hình trong hình như ở trên. Nếu không, chúng tôi sẽ yêu cầu rời đi bằng cách gọi điện document.exitPictureInPicture(), tức là video sẽ xuất hiện lại sau thẻ ban đầu. Lưu ý rằng phương thức này cũng trả về một lời hứa (promise).

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

Nghe sự kiện Hình trong hình

Các hệ điều hành thường hạn chế chế độ Hình trong hình ở một cửa sổ, vì vậy Quá trình triển khai của Chrome tuân theo mẫu này. Tức là người dùng chỉ có thể chơi mỗi lần một video Hình trong hình. Bạn sẽ thấy người dùng thoát Chế độ Hình trong hình ngay cả khi bạn không yêu cầu.

Trình xử lý sự kiện enterpictureinpictureleavepictureinpicture mới cho phép chúng tôi điều chỉnh trải nghiệm cho người dùng. Có thể là bất cứ điều gì như việc duyệt qua danh mục video để hiển thị một cuộc trò chuyện phát trực tiếp.

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

Tuỳ chỉnh cửa sổ Hình trong hình

Chrome 74 hỗ trợ các nút phát/tạm dừng, bài hát trước và bài hát tiếp theo trong Cửa sổ Hình trong hình mà bạn có thể kiểm soát bằng API Phiên nội dung nghe nhìn.

Nút điều khiển chế độ phát nội dung nghe nhìn trong cửa sổ Hình trong hình
Hình 1. Nút điều khiển chế độ phát nội dung nghe nhìn trong cửa sổ Hình trong hình

Theo mặc định, nút phát/tạm dừng luôn hiển thị ở chế độ Hình trong hình trừ phi video đang phát các đối tượng MediaStream (ví dụ: getUserMedia(), getDisplayMedia(), canvas.captureStream()) hoặc video có MediaSource thời lượng được đặt thành +Infinity (ví dụ: nguồn cấp dữ liệu trực tiếp). Để đảm bảo nút phát/tạm dừng luôn hiển thị, hãy thiết lập some xem trình xử lý tác vụ Phiên nội dung đa phương tiện cho cả nút "Play" (Phát) và "Pause" (Tạm dừng) sự kiện truyền thông như sau.

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

Đang hiện "Bản nhạc trước" và "Bản nhạc tiếp theo" các thành phần điều khiển cửa sổ cũng tương tự như vậy. Chế độ cài đặt Trình xử lý thao tác của Phiên phát nội dung đa phương tiện cho những người dùng đó sẽ hiển thị ở chế độ Hình trong hình để có thể xử lý các thao tác này.

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

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

Để xem ví dụ thực tế, hãy thử mẫu Phiên nội dung nghe nhìn chính thức.

Xem kích thước cửa sổ Hình trong hình

Nếu bạn muốn điều chỉnh chất lượng video khi video truy cập vào và rời đi Chế độ Hình trong hình, bạn cần biết kích thước cửa sổ Hình trong hình và nếu người dùng đổi kích thước cửa sổ theo cách thủ công.

Ví dụ bên dưới cho thấy cách lấy chiều rộng và chiều cao của Cửa sổ Hình trong hình khi hình ảnh được tạo hoặc đổi kích thước.

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

Bạn không nên kết nối trực tiếp với sự kiện đổi kích thước khi mỗi thay đổi nhỏ được thực hiện kích thước cửa sổ Hình trong hình sẽ kích hoạt một sự kiện riêng có thể khiến hiệu suất nếu bạn đang thực hiện một thao tác tốn kém ở mỗi lần đổi kích thước. Trong nói cách khác, thao tác đổi kích thước sẽ kích hoạt các sự kiện lặp đi lặp lại để một cách nhanh chóng. Tôi khuyên bạn nên sử dụng các kỹ thuật phổ biến như điều tiết và gỡ bỏ để giải quyết vấn đề này.

Hỗ trợ tính năng

API web hình trong hình có thể không được hỗ trợ nên bạn phải phát hiện điều này để cung cấp cải tiến tăng dần. Ngay cả khi được hỗ trợ, tính năng này có thể bị người dùng tắt hoặc bị tắt theo chính sách về quyền. Thật may là bạn có thể sử dụng giá trị boolean mới document.pictureInPictureEnabled để xác định điều này.

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

Đây là cách mà bạn nên áp dụng cho một thành phần nút cụ thể trong video xử lý chế độ hiển thị của nút Hình trong hình.

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

Hỗ trợ video MediaStream

Phát video các đối tượng MediaStream (ví dụ: getUserMedia(), getDisplayMedia(), canvas.captureStream()) cũng hỗ trợ tính năng Hình trong hình trong Chrome 71. Chiến dịch này nghĩa là bạn có thể hiển thị cửa sổ Hình trong hình có chứa webcam của người dùng luồng video, hiển thị luồng video hoặc thậm chí là phần tử canvas. Lưu ý rằng phần tử video không cần phải được đính kèm vào DOM để nhập Hình trong hình như minh hoạ dưới đây.

Hiển thị webcam của người dùng trong cửa sổ Hình trong hình

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

// Later on, video.requestPictureInPicture();

Hiện màn hình trong cửa sổ Hình trong hình

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

// Later on, video.requestPictureInPicture();

Hiển thị thành phần canvas trong cửa sổ Hình trong hình

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

Khi kết hợp canvas.captureStream() với Media Session API, bạn có thể Bạn có thể tạo cửa sổ danh sách phát âm thanh trong Chrome 74. Xem kênh chính thức Mẫu danh sách phát âm thanh.

Danh sách phát âm thanh trong cửa sổ Hình trong hình
Hình 2. Danh sách phát âm thanh trong cửa sổ Hình trong hình

Mẫu, bản minh hoạ và lớp học lập trình

Hãy xem mẫu Hình trong hình chính thức của chúng tôi để dùng thử tính năng Hình trong hình Web API.

Sau đó là các bản minh hoạ và lớp học lập trình.

Bước tiếp theo

Trước tiên, hãy xem trang trạng thái triển khai để biết phần nào của API hiện được triển khai trong Chrome và các trình duyệt khác.

Dưới đây là những nội dung bạn có thể thấy trong tương lai gần:

Hỗ trợ trình duyệt

API web hình trong hình được hỗ trợ trong Chrome, Edge, Opera và Safari. Xem MDN để biết chi tiết.

Tài nguyên

Rất cảm ơn Mounir Lamouri và Jennifer Apacible vì những đóng góp của họ Chế độ Hình trong hình và trợ giúp trong bài viết này. Xin chân thành cảm ơn mọi người liên quan đến nỗ lực chuẩn hoá.