Ghi lại luồng video từ bất kỳ phần tử nào

François Beaufort
François Beaufort

Với Screen Capture API, bạn có thể chụp toàn bộ thẻ hiện tại. Element Capture API cho phép bạn chụp và ghi lại một phần tử HTML cụ thể. Thao tác này sẽ chuyển đổi một bản chụp toàn bộ thẻ thành một bản chụp của một cây con DOM cụ thể, chỉ chụp những phần tử con trực tiếp của phần tử đích. Nói cách khác, nó sẽ cắt và xoá cả nội dung che khuất và nội dung bị che khuất.

Tại sao nên sử dụng tính năng Ghi lại phần tử?

Việc xem xét các yêu cầu của một ứng dụng hội nghị truyền hình có thể giúp bạn hiểu rõ trường hợp nào thì Element Capture hữu ích. Nếu có một ứng dụng hội nghị truyền hình cho phép bạn nhúng các ứng dụng bên thứ ba vào một iframe, thì đôi khi bạn có thể muốn ghi lại iframe đó dưới dạng video và truyền đến những người tham gia từ xa.

Ảnh chụp màn hình của một cuộc gọi hội nghị truyền hình trong Chrome.
Elad sử dụng một ứng dụng bên thứ ba trong cuộc gọi hội nghị truyền hình với François.

Việc gọi getDisplayMedia() và cho phép người dùng chọn thẻ hiện tại sẽ truyền toàn bộ thẻ hiện tại. Điều này có thể truyền video của người dùng trở lại cho họ. Bạn có thể cắt bỏ phần này bằng tính năng Chụp một vùng màn hình.

Tuy nhiên, nếu người trình bày tương tác với ứng dụng hội nghị truyền hình và một số nội dung (chẳng hạn như danh sách thả xuống) xuất hiện ở trên cùng của nội dung dự định ghi lại thì sao?

Ảnh chụp màn hình của một danh sách thả xuống che khuất nội dung cần chụp.
Một danh sách thả xuống sẽ xuất hiện ở trên cùng của nội dung dự định chụp.

Công cụ Chụp ảnh màn hình theo vùng sẽ không giúp được bạn. Một phần của danh sách thả xuống có thể xuất hiện trên màn hình của người tham gia từ xa.

Ảnh chụp màn hình của một danh sách thả xuống.
Danh sách thả xuống của Elad xuất hiện ở trên cùng của nội dung mà François nhận được.

Việc tính năng Chụp theo vùng chụp các phần của phần tử theo cách này (được gọi là nội dung che khuất) gây ra nhiều vấn đề:

  • Nội dung che khuất có thể cản trở người dùng xem nội dung mà họ muốn chia sẻ.
  • Nội dung bị che khuất có thể là nội dung riêng tư (ví dụ: thông báo trò chuyện).
  • Nội dung che khuất có thể gây nhầm lẫn. (Ví dụ: việc bố trí lại ứng dụng có thể tạm thời đưa video của người tham gia từ xa lên trên mục tiêu được ghi lại.)

Element Capture API giải quyết tất cả những vấn đề này bằng cách cho phép bạn nhắm đến phần tử mà bạn muốn chia sẻ.

Ảnh chụp màn hình của phần tử đích không có danh sách thả xuống nào trong chế độ xem.
François không thấy danh sách thả xuống của Elad.

Làm cách nào để sử dụng tính năng Chụp phần tử?

captureTarget là một Phần tử trên trang của bạn, chứa nội dung mà người dùng muốn chụp. Bạn muốn ứng dụng hội nghị truyền hình trên web ghi lại captureTarget và chia sẻ với những người tham gia từ xa. Vì vậy, bạn sẽ lấy được RestrictionTarget từ captureTarget. Sau khi bạn hạn chế bản video bằng RestrictionTarget này, các khung hình trên bản video đó giờ đây chỉ bao gồm những pixel thuộc captureTarget và các phần tử DOM con trực tiếp của nó.

Nếu captureTarget thay đổi kích thước, hình dạng hoặc vị trí, thì bản video sẽ đi theo mà không cần thêm thông tin đầu vào nào từ ứng dụng web. Tương tự, nội dung che khuất xuất hiện, biến mất hoặc di chuyển xung quanh cũng không cần xử lý đặc biệt.

Xem lại các bước sau:

Bắt đầu bằng cách cho phép người dùng chụp thẻ hiện tại.

// Ask the user for permission to start capturing the current tab.
const stream = await navigator.mediaDevices.getDisplayMedia({
 preferCurrentTab: true,
});
const [track] = stream.getVideoTracks();

Xác định một RestrictionTarget bằng cách gọi RestrictionTarget.fromElement() với một phần tử mà bạn chọn làm dữ liệu đầu vào.

// Associate captureTarget with a new RestrictionTarget
const captureTarget = document.querySelector("#captureTarget");
const restrictionTarget = await RestrictionTarget.fromElement(captureTarget);

Sau đó, hãy gọi restrictTo() trên bản video với RestrictionTarget làm dữ liệu đầu vào. Sau khi lời hứa cuối cùng được thực hiện, tất cả các khung hình tiếp theo sẽ bị hạn chế.

// Start restricting the self-capture video track using the RestrictionTarget.
await track.restrictTo(restrictionTarget);

// Enjoy! Transmit remotely.

Tìm hiểu sâu

Phát hiện đối tượng

Để kiểm tra xem RestrictionTarget.fromElement() có được hỗ trợ hay không, hãy sử dụng:

if ("RestrictionTarget" in self && "fromElement" in RestrictionTarget) {
  // Deriving a restriction target is supported.
}

Dẫn xuất RestrictionTarget

Tập trung vào Phần tử có tên là captureTarget. Để lấy RestrictionTarget từ đó, hãy gọi RestrictionTarget.fromElement(captureTarget). Promise được trả về sẽ được phân giải bằng một đối tượng RestrictionTarget mới nếu thành công. Nếu không, yêu cầu sẽ bị từ chối nếu bạn đã tạo một số lượng đối tượng RestrictionTarget không hợp lý.

const captureTarget = document.querySelector("#captureTarget");
const restrictionTarget = await RestrictionTarget.fromElement(captureTarget);

Không giống như một Phần tử, đối tượng RestrictionTarget có thể chuyển đổi tuần tự. Bạn có thể truyền đối tượng này sang một tài liệu khác bằng cách sử dụng Window.postMessage(), chẳng hạn.

Hạn chế

Khi quay một thẻ, bản video sẽ hiển thị restrictTo(). Khi chụp thẻ hiện tại, bạn có thể gọi restrictTo() bằng null hoặc bất kỳ RestrictionTarget nào bắt nguồn từ một Phần tử trong thẻ hiện tại.

Các lệnh gọi đến restrictTo(restrictionTarget) sẽ biến đổi bản video thành một bản ghi của captureTarget, như thể bản ghi đó được vẽ riêng, độc lập với phần còn lại của DOM. Mọi phần tử con của captureTarget cũng được ghi lại; các phần tử ngang cấp của captureTarget sẽ bị loại bỏ khỏi quá trình ghi lại. Kết quả là mọi khung hình được phân phối trên kênh đều xuất hiện như thể chúng được cắt theo đường viền của captureTarget và mọi nội dung bị che khuất và che khuất đều bị xoá.

// Start restricting the self-capture video track using the RestrictionTarget.
await track.restrictTo(restrictionTarget);

Các lệnh gọi đến restrictTo(null) sẽ đưa bản nhạc về trạng thái ban đầu.

// Stop restricting.
await track.restrictTo(null);

Nếu lệnh gọi đến restrictTo() thành công, thì Lời hứa được trả về sẽ được phân giải khi có thể đảm bảo rằng tất cả các khung hình video tiếp theo sẽ bị giới hạn ở captureTarget.

Nếu không thành công, Lời hứa sẽ bị từ chối. Lệnh gọi không thành công đến restrictTo() sẽ là một trong những lý do sau:

  • Nếu restrictionTarget được đúc trong một thẻ khác với thẻ đang được ghi lại. (Xin lưu ý rằng khi dùng nút "chia sẻ thẻ này", người dùng có thể thay đổi thẻ được ghi lại bất cứ lúc nào.)
  • Nếu restrictionTarget được lấy từ một Phần tử không còn tồn tại.
  • Nếu bản nhạc có bản sao. (Xem vấn đề 1509418.)
  • Nếu bản nhạc hiện tại không phải là bản nhạc video tự quay.
  • Nếu Phần tử mà restrictionTarget được lấy ra không đủ điều kiện để bị hạn chế.

Những điều cần cân nhắc khi tự chụp ảnh

Khi một ứng dụng gọi getDisplayMedia() và người dùng chọn ghi lại thẻ của chính ứng dụng đó, chúng ta gọi đó là "tự ghi lại".

Phương thức restrictTo() được hiển thị trên mọi bản ghi video chụp màn hình thẻ chứ không chỉ dành cho tính năng tự chụp. Tuy nhiên, hiện tại, tính năng Chụp phần tử chỉ được bật cho chế độ tự chụp. Do đó, bạn nên kiểm tra xem người dùng có chọn thẻ hiện tại hay không trước khi tìm cách hạn chế bản nhạc. Bạn có thể thực hiện việc này bằng cách sử dụng Capture Handle (Xử lý chụp). Bạn cũng có thể yêu cầu trình duyệt nhắc người dùng tự chụp ảnh bằng cách sử dụng preferCurrentTab.

Sự minh bạch

Các khung hình video mà ứng dụng nhận được thông qua getDisplayMedia() không có kênh alpha. Nếu một ứng dụng đặt mục tiêu chụp trong suốt một phần, thì việc loại bỏ kênh alpha có thể dẫn đến một số hậu quả sau:

  • Màu sắc có thể thay đổi. Các phần tử đích trong suốt một phần được vẽ trên nền sáng có thể xuất hiện tối hơn khi kênh alpha bị xoá và các phần tử được vẽ trên nền tối có thể xuất hiện sáng hơn.
  • Những màu sắc mà người dùng không nhìn thấy hoặc không nhận biết được khi kênh alpha được đặt ở mức tối đa sẽ xuất hiện sau khi kênh alpha bị xoá. Ví dụ: điều này có thể dẫn đến các vùng màu đen không mong muốn trong khung hình đã chụp, nếu các phần trong suốt có mã RGBA rgba(0, 0, 0, 0).
Ảnh chụp màn hình kết quả của một mục tiêu chụp trong suốt không phải hình chữ nhật.
Luồng video đích chụp trong suốt không phải hình chữ nhật (bên phải) là một hình chữ nhật có nền màu đen chứa một vòng tròn màu xanh dương mờ.

Mục tiêu chụp không đủ điều kiện

Bạn luôn có thể bắt đầu hạn chế một bản nhạc đối với bất kỳ mục tiêu ghi hình hợp lệ nào. Tuy nhiên, các khung hình sẽ không được tạo trong một số điều kiện nhất định, chẳng hạn như nếu phần tử hoặc một phần tử tổ tiên là display:none. Lý do chung là quy tắc hạn chế chỉ áp dụng cho một phần tử bao gồm một vùng hình chữ nhật, hai chiều, gắn kết và riêng lẻ, có thể xác định các pixel một cách hợp lý mà không cần đến bất kỳ phần tử mẹ hoặc phần tử đồng cấp nào.

Một yếu tố quan trọng cần cân nhắc để đảm bảo phần tử đủ điều kiện bị hạn chế là phần tử đó phải tạo thành ngữ cảnh xếp chồng riêng. Để đảm bảo điều này, bạn có thể chỉ định thuộc tính CSS isolation, đặt thuộc tính này thành isolate.

<div id="captureTarget" style="isolation: isolate;"></iframe>

Xin lưu ý rằng phần tử đích có thể chuyển đổi giữa trạng thái đủ điều kiện và không đủ điều kiện để bị hạn chế tại bất kỳ thời điểm tuỳ ý nào, ví dụ: nếu ứng dụng thay đổi các thuộc tính CSS. Ứng dụng có trách nhiệm sử dụng các mục tiêu chụp hợp lý và tránh thay đổi các thuộc tính của mục tiêu một cách đột ngột. Nếu phần tử đích không đủ điều kiện, các khung hình mới sẽ không được phát trên bản nhạc cho đến khi phần tử đích đủ điều kiện để bị hạn chế trở lại.

Hỗ trợ trình duyệt

Tính năng Chụp phần tử chỉ có trên Chrome 132 cho máy tính.

Bảo mật và quyền riêng tư

Để hiểu rõ những điểm đánh đổi về bảo mật, hãy xem phần Những điểm cần cân nhắc về quyền riêng tư và bảo mật trong quy cách Chụp phần tử.

Trình duyệt Chrome vẽ một đường viền màu xanh dương xung quanh các cạnh của thẻ được chụp.

Bản minh hoạ

Bạn có thể chơi với tính năng Ghi lại phần tử bằng cách chạy bản minh hoạ.

Phản hồi

Nhóm Chrome và cộng đồng tiêu chuẩn web muốn biết trải nghiệm của bạn về tính năng Ghi lại phần tử.

Cho chúng tôi biết về thiết kế

Có vấn đề gì về tính năng Ghi lại phần tử không hoạt động như bạn mong đợi không? Hoặc có phương thức hoặc thuộc tính nào bị thiếu mà bạn cần triển khai ý tưởng của mình không? Bạn có câu hỏi hoặc bình luận về mô hình bảo mật?

  • Gửi vấn đề về quy cách trên kho lưu trữ GitHub hoặc thêm ý kiến của bạn vào một vấn đề hiện có.

Bạn gặp vấn đề khi triển khai?

Bạn có phát hiện thấy lỗi trong quá trình triển khai của Chrome không? Hoặc việc triển khai có khác với quy cách không?

  • Báo cáo lỗi tại https://new.crbug.com. Hãy nhớ cung cấp càng nhiều thông tin chi tiết càng tốt và hướng dẫn đơn giản để tái hiện lỗi.

Lời cảm ơn

Ảnh của Paul Skorupskas trên Unsplash