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 (API chụp màn hình), bạn có thể chụp toàn bộ thẻ hiện tại. Element Capture API (API chụp phần tử) cho phép bạn chụp và ghi lại một phần tử HTML cụ thể. Việc này biến đổi việc chụp toàn bộ thẻ, thành việc chụp một cây con DOM cụ thể, chỉ chụp các thành phần con trực tiếp của phần tử mục tiêu. Nói cách khác, công cụ này sẽ cắt và xoá cả nội dung bị che khuất và nội dung bị che khuất.

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

Việc xem xét các yêu cầu của ứng dụng hội nghị truyền hình có thể giúp bạn hiểu được trường hợp sử dụng tính năng Chụp thành phần. 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 chụp iframe đó dưới dạng video và truyền iframe đó đến những người tham gia từ xa.

Ảnh chụp màn hình 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 đó có khả năng truyền video của chính mọi người trở lại với họ. Bạn có thể cắt bỏ phần này bằng tính năng Chụp vùng.

Tuy nhiên, điều gì sẽ xảy ra 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, tình cờ vẽ lên đầu nội dung mà họ muốn ghi lại?

Ảnh chụp màn hình 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 ở đầu nội dung cần chụp.

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

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

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

  • Nội dung che khuất có thể khiến người dùng không xem được 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).
  • Việc che khuất nội dung có thể gây nhầm lẫn. (Ví dụ: việc bố cục lại ứng dụng có thể nhanh chóng đưa video của người tham gia từ xa lên mục tiêu đã ghi lại.)

Element Capture API giải quyết tất cả các 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 phần tử mục tiêu không có danh sách thả xuống 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 Thành phần 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 web hội nghị truyền hình ghi lại captureTarget và chia sẻ thiết bị đó với những người tham gia từ xa. Vì vậy, bạn sẽ lấy RestrictionTarget từ captureTarget. Sau khi hạn chế kênh video bằng RestrictionTarget này, các khung trên kênh video đó hiện chỉ bao gồm các pixel thuộc captureTarget và các phần tử con DOM trực tiếp của captureTarget.

Nếu captureTarget thay đổi kích thước, hình dạng hoặc vị trí, thì kênh video sẽ thay đổi theo mà không cần bất kỳ dữ liệu đầu vào nào khác từ ứng dụng web. Tương tự, việc che khuất nội dung 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 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 kênh video, trong đó RestrictionTarget làm đầu vào. Sau khi lời hứa cuối cùng được giải quyết, 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 chuyên sâu

Phát hiện tính nă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.
}

Trích 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). Lời hứa được trả về sẽ được phân giải bằng đối tượng RestrictionTarget mới nếu thành công. Nếu không, yêu cầu này 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ư Phần tử, đối tượng RestrictionTargetcó thể chuyển đổi tuần tự. Bạn có thể chuyển giá trị này đến một tài liệu khác bằng cách sử dụng Window.postMessage().

Hạn chế

Khi bạn chụp một thẻ, kênh 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 kênh video thành một bản ghi của captureTarget, như thể kênh video đó được tự vẽ, độc lập với phần còn lại của DOM. Mọi thành phần con của captureTarget cũng bị bắt; các thành phần con của captureTarget sẽ bị loại bỏ khỏi quá trình chụp. Kết quả là mọi khung hình được phân phối trên kênh sẽ hiển thị như thể chúng đã bị cắt theo đường viền của captureTarget, đồng thời mọi nội dung bị che khuất và bị che khuất đều sẽ bị xoá.

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

Lệnh gọi đến restrictTo(null) sẽ khôi phục 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, Promise được trả về sẽ được phân giải khi có thể đảm bảo rằng tất 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. Cuộc gọi đến restrictTo() không thành công sẽ do một trong các lý do sau đây:

  • Nếu restrictionTarget được tạo trong một thẻ khác với thẻ đang được chụp. (Xin lưu ý rằng khi sử dụng nút "chia sẻ thẻ này", người dùng có thể thay đổi thẻ được ghi lại vào một thời điểm nhất định.)
  • 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ử tạo ra restrictionTarget không đủ điều kiện để áp dụng quy định hạn chế.

Những điều cần cân nhắc khi tự quay

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

Phương thức restrictTo() được hiển thị trên mọi kênh video quay lại thẻ, chứ không chỉ dành cho tính năng tự quay. Tuy nhiên, tính năng Chụp phần tử hiện chỉ được bật để tự chụp. Do đó, bạn nên kiểm tra xem người dùng đã chọn thẻ hiện tại hay chưa 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 Tên người dùng để ghi lại. 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 preferCurrentTab.

Sự minh bạch

Khung video mà ứng dụng nhận được đến getDisplayMedia() không bao gồm kênh alpha. Nếu một ứng dụng đặt mục tiêu chụp có độ 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ả:

  • Màu sắc có thể thay đổi. Các phần tử mục tiêu trong suốt một phần được vẽ trên nền sáng có thể trông 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ể trông sáng hơn.
  • Các màu không nhìn thấy hoặc không nhìn thấy được đối với người dùng khi kênh alpha được đặt thành 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 các 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ục tiêu chụp trong suốt không phải hình chữ nhật.
Luồng video mục tiêu 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 nền đen chứa một vòng tròn màu xanh dương mờ.

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

Bạn luôn có thể bắt đầu hạn chế một kênh cho bất kỳ mục tiêu quay video hợp lệ nào. Tuy nhiên, khung 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 phần tử cấp trên là display:none. Lý do chung là quy tắc hạn chế này 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, có tính liên kết, mà các điểm ảnh của phần tử đó có thể được xác định một cách logic tách biệt với mọi phần tử mẹ hoặc phần tử đồng cấp.

Một điều 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 cách ly bằng cách đặ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ử mục tiêu 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 của phần tử đó. Ứng dụng phải sử dụng các mục tiêu thu thập hợp lý và tránh thay đổi thuộc tính của các mục tiêu đó một cách đột ngột. Nếu phần tử đích không đủ điều kiện, thì các khung hình mới sẽ không được phát trên kênh cho đến khi phần tử mục tiêu đó lại đủ điều kiện để hạn chế.

Bật tính năng Chụp phần tử

Element Capture API có trong Chrome trên máy tính đằng sau cờ Element Capture và có thể được bật tại chrome://flags/#element-capture.

Tính năng này cũng đang bước vào giai đoạn thử nghiệm theo nguồn gốc từ Chrome 121 trên máy tính. Tính năng này cho phép nhà phát triển bật tính năng này cho khách truy cập trang web của họ để thu thập dữ liệu từ người dùng thực. Hãy xem bài viết Bắt đầu thử nghiệm nguồn gốc để biết thêm thông tin về thử nghiệm nguồn gốc.

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

Để hiểu rõ những đá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 thông số kỹ thuật của tính năng Chụp phần tử.

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

Bản minh hoạ

Bạn có thể thử nghiệm tính năng Chụp phần tử bằng cách chạy bản minh hoạ trên Glitch. Hãy nhớ xem mã nguồn.

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ới tính năng Chụp phần tử.

Giới thiệu về thiết kế

Tính năng Chụp vùng không hoạt động như mong đợi? Hay 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 nhận xét về mô hình bảo mật?

  • Gửi vấn đề về thông số kỹ thuật 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 đề trong quá trình triển khai?

Bạn có phát hiện lỗi trong quá trình triển khai Chrome không? Hay cách triển khai có khác với thông số kỹ thuật không?

  • Gửi 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. Glitch rất hữu ích để chia sẻ các bản tái hiện nhanh chóng và dễ dàng.

Thư cảm ơn

Ảnh chụp của Paul Skorupskas trên Unsplash