모든 요소에서 동영상 스트림 캡처

프랑수아 보포르
프랑수아 보포르
엘라드 아론
Elad Alon

Screen Capture API를 사용하면 현재 탭 전체를 캡처할 수 있습니다. Element Capture API를 사용하면 특정 HTML 요소를 캡처하고 기록할 수 있습니다. 전체 탭의 캡처를 특정 DOM 하위 트리의 캡처로 변환하여 target-element의 직접 하위 요소만 캡처합니다. 즉, 가려진 콘텐츠와 가려진 콘텐츠를 모두 잘라내고 제거합니다.

요소 캡처를 사용해야 하는 이유

화상 회의 애플리케이션의 요구사항을 고려하면 Element Capture가 어떤 면에서 유용한지 파악하는 데 도움이 될 수 있습니다. iframe에 서드 파티 애플리케이션을 삽입할 수 있는 화상 회의 애플리케이션이 있는 경우, iframe을 동영상으로 캡처하여 원격 참여자에게 전송하고자 할 수도 있습니다.

Chrome의 화상 회의 통화 스크린샷
Elad는 François와 화상 회의 통화를 위해 서드 파티 애플리케이션을 사용합니다.

getDisplayMedia()를 호출하고 사용자가 현재 탭을 선택하도록 허용하면 현재 탭 전체가 전송됩니다. 이는 다른 사람의 동영상을 그 사용자에게 다시 전송할 가능성이 높습니다. 지역 캡처를 사용해 이 부분을 자를 수 있습니다.

하지만 발표자가 화상 회의 애플리케이션을 사용할 때 드롭다운 목록과 같은 일부 콘텐츠가 캡처하려는 콘텐츠 위에 그려지면 어떻게 될까요?

캡처하려는 콘텐츠가 포함된 드롭다운 목록의 스크린샷
드롭다운 목록은 캡처하려는 콘텐츠 상단에 표시됩니다.

Region Capture는 도움이 되지 않습니다. 드롭다운 목록 중 일부가 원격 참여자의 화면에 표시될 수 있습니다.

캡처된 드롭다운 목록의 스크린샷
Elad의 드롭다운 목록이 François가 받은 콘텐츠 상단에 표시됩니다.

지역 캡처에서 이러한 방식으로 요소의 일부를 캡처하기 때문에 (가리는 콘텐츠라고 함) 다음과 같은 여러 문제가 발생합니다.

  • 콘텐츠를 가리면 사용자가 공유하려는 콘텐츠를 보지 못할 수 있습니다.
  • 콘텐츠를 가리는 것은 비공개일 수 있습니다 (예: 채팅 알림).
  • 콘텐츠를 제외하면 혼동될 수 있습니다. 예를 들어, 애플리케이션의 레이아웃을 재조정하면 원격 참여자의 동영상을 캡처된 타겟 위로 잠깐 가져올 수 있습니다.

Element Capture API는 공유하려는 요소를 타겟팅하여 이러한 문제를 모두 해결합니다.

뷰에 드롭다운 목록이 없는 타겟 요소의 스크린샷
François는 Elad의 드롭다운 목록을 볼 수 없습니다.

요소 캡처는 어떻게 사용하나요?

captureTarget는 사용자가 캡처하려는 콘텐츠가 포함된 페이지의 요소입니다. 화상 회의 웹 앱에서 captureTarget 앱을 캡처하여 원격 참여자와 공유하도록 하려고 합니다. 따라서 captureTarget에서 RestrictionTarget를 얻습니다. 이 RestrictionTarget를 사용하여 동영상 트랙을 제한하면 동영상 트랙의 프레임이 이제 captureTarget의 일부인 픽셀과 직접 DOM 하위 요소로만 구성됩니다.

captureTarget에서 크기, 모양 또는 위치를 변경하면 웹 앱에서 추가로 입력할 필요 없이 동영상 트랙이 계속 따라갑니다. 콘텐츠가 표시되거나 사라지거나 움직일 때도 마찬가지로 특별한 처리가 필요하지 않습니다.

다음 단계를 다시 검토하세요.

사용자가 현재 탭을 캡처할 수 있도록 허용하여 시작합니다.

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

원하는 요소를 입력으로 사용하여 RestrictionTarget.fromElement()를 호출하여 RestrictionTarget를 정의합니다.

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

그런 다음 RestrictionTarget를 입력으로 사용하여 동영상 트랙에서 restrictTo()를 호출합니다. 마지막 프로미스가 결정되면 모든 후속 프레임이 제한됩니다.

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

// Enjoy! Transmit remotely.

자세히 알아보기

특징 감지

RestrictionTarget.fromElement()가 지원되는지 확인하려면 다음을 사용하세요.

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

RestrictionTarget 도출

captureTarget라는 요소에 포커스를 맞춥니다. 여기에서 RestrictionTarget를 가져오려면 RestrictionTarget.fromElement(captureTarget)를 호출합니다. 성공하면 반환된 프로미스는 새 RestrictionTarget 객체로 확인됩니다. 그렇지 않은 경우 부당한 개수의 RestrictionTarget 객체를 발급한 경우 거부됩니다.

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

요소와 달리 RestrictionTarget 객체는 직렬화가 가능합니다. 예를 들어 Window.postMessage()를 사용하여 다른 문서에 전달할 수 있습니다.

제한

탭을 캡처할 때 동영상 트랙에는 restrictTo()이 노출됩니다. 현재 탭을 캡처할 때 null 또는 현재 탭 내의 요소에서 파생된 RestrictionTargetrestrictTo()를 호출하는 것이 유효합니다.

restrictTo(restrictionTarget) 호출은 동영상 트랙을 나머지 DOM과 독립적으로 그린 것처럼 captureTarget 캡처로 변경합니다. captureTarget의 모든 하위 요소도 캡처됩니다. captureTarget의 동위 요소는 캡처에서 제거됩니다. 결과적으로 트랙에 전달된 모든 프레임이 captureTarget의 윤곽에 맞게 잘린 것처럼 표시되고 가려진 콘텐츠와 가려진 콘텐츠가 삭제됩니다.

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

restrictTo(null)를 호출하면 트랙이 원래 상태로 되돌아갑니다.

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

restrictTo() 호출이 성공하면 반환된 프로미스는 모든 후속 동영상 프레임이 captureTarget로 제한된다는 보장이 있을 때 확인됩니다.

실패하면 프로미스가 거부됩니다. restrictTo() 호출이 실패하면 다음 이유 중 하나가 발생합니다.

  • restrictionTarget가 캡처되고 있는 탭이 아닌 다른 탭에서 발급된 경우 ('대신 이 탭 공유' 버튼을 사용하면 언제든지 캡처되는 탭을 변경할 수 있습니다.)
  • restrictionTarget가 더 이상 존재하지 않는 요소에서 파생된 경우
  • 트랙에 복제곡이 있는 경우 (문제 1509418)
  • 현재 트랙이 자체 캡처 동영상 트랙이 아닌 경우
  • restrictionTarget가 파생된 요소를 제한할 수 없는 경우

셀프 캡처 고려사항

앱에서 getDisplayMedia()를 호출하고 사용자가 앱의 자체 탭을 캡처하기로 선택하는 경우 이를 '자체 캡처'라고 합니다.

restrictTo() 메서드는 자체 캡처를 위해서가 아니라 모든 탭 캡처 동영상 트랙에 노출됩니다. 하지만 현재 요소 캡처는 셀프 캡처에만 사용 설정되어 있습니다. 따라서 트랙을 제한하기 전에 사용자가 현재 탭을 선택했는지 확인하는 것이 좋습니다. Capture Handle을 사용하면 됩니다. preferCurrentTab를 사용하여 사용자가 자체 캡처를 하도록 브라우저에 요청할 수도 있습니다.

투명성

앱이 getDisplayMedia()를 통해 가져오는 동영상 프레임에는 알파 채널이 포함되지 않습니다. 앱이 부분적으로 투명한 캡처 타겟을 설정하는 경우 알파 채널을 제거하면 다음과 같은 결과가 발생할 수 있습니다.

  • 색상이 변경될 수 있습니다. 밝은 배경 위에 그려진 부분적으로 투명한 타겟 요소는 알파 채널이 삭제될 때 더 어둡게 표시될 수 있고 어두운 배경 위에 그려진 타겟 요소는 더 밝게 보일 수 있습니다.
  • 알파 채널이 최대치로 설정되었을 때 사용자가 보이지 않거나 인식할 수 없는 색상은 알파 채널이 삭제되면 나타납니다. 예를 들어 투명한 섹션에 RGBA 코드 rgba(0, 0, 0, 0)가 있는 경우 캡처된 프레임에 예기치 않은 검은색 영역이 발생할 수 있습니다.
직사각형이 아닌 투명 캡처 타겟의 결과 스크린샷
직사각형이 아닌 투명 캡처 대상 동영상 스트림 (오른쪽)은 불투명한 파란색 원이 포함된 검은색 배경 직사각형입니다.

요건을 충족하지 않는 캡처 대상

언제든지 유효한 캡처 타겟으로 트랙을 제한할 수 있습니다. 그러나 요소 또는 상위 항목이 display:none인 경우와 같은 특정 조건에서는 프레임이 생성되지 않습니다. 일반적인 근거는 제한이 응집된 2차원의 단일 직사각형 영역을 구성하는 요소에만 적용된다는 점입니다. 해당 영역의 픽셀은 상위 요소 또는 동위 요소와 분리되어 논리적으로 결정될 수 있습니다.

요소가 제한 요건을 충족하도록 할 때 고려해야 할 한 가지 중요한 사항은 자체 스택 컨텍스트를 형성해야 한다는 것입니다. 이렇게 하려면 격리 CSS 속성을 지정하여 isolate로 설정합니다.

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

타겟 요소는 임의의 지점에서 제한 적용 가능 또는 사용 불가 요소 간에 전환할 수 있습니다(예: 앱이 CSS 속성을 변경하는 경우). 적절한 캡처 타겟을 사용하고 예기치 않게 속성이 변경되지 않도록 하는 것은 앱에 달려 있습니다. 타겟 요소를 사용할 수 없게 되면 타겟 요소가 다시 제한 대상이 될 때까지 트랙에 새 프레임을 내보내지 않습니다.

요소 캡처 사용 설정

Element Capture API는 데스크톱의 Chrome에서 Element Capture 플래그 뒤에서 사용할 수 있으며 chrome://flags/#element-capture에서 사용 설정할 수 있습니다.

또한 이 기능은 데스크톱의 Chrome 121부터 오리진 트라이얼에 진입하여 개발자가 사이트 방문자가 실제 사용자로부터 데이터를 수집하도록 허용하는 기능을 사용 설정할 수 있습니다. 오리진 트라이얼에 대한 자세한 내용은 오리진 트라이얼 시작하기를 참고하세요.

보안 및 개인 정보 보호

보안상의 장단점을 알아보려면 요소 캡처 사양의 개인 정보 보호 및 보안 고려사항 섹션을 확인하세요.

Chrome 브라우저에서 캡처된 탭의 가장자리 주위에 파란색 테두리를 그립니다.

데모

Glitch에서 데모를 실행하여 요소 캡처를 사용해 볼 수 있습니다. 소스 코드를 확인해야 합니다.

의견

Chrome팀과 웹 표준 커뮤니티는 Element Capture 사용 경험에 대해 듣고 싶어 합니다.

디자인에 대해 알려주세요.

Region Capture에서 예상한 대로 작동하지 않는 부분이 있나요? 아니면 아이디어를 구현하는 데 필요한 메서드나 속성이 누락되어 있나요? 보안 모델에 대한 질문이나 의견이 있으신가요?

  • GitHub 저장소에서 사양 문제를 제출하거나 기존 문제에 대한 의견을 추가하세요.

구현에 문제가 있나요?

Chrome 구현과 관련된 버그를 발견했나요? 아니면 구현이 사양과 다른가요?

  • https://new.crbug.com에서 버그를 신고합니다. 가능한 한 많은 세부정보와 간단한 재현 안내를 포함해 주세요. Glitch는 쉽고 빠르게 재현을 공유하는 데 효과적입니다.

감사의 말

사진: 폴 Skorupskas: Unsplash