Document Picture-in-Picture
Document Picture-in-Picture(이하 Document PiP) API를 사용하면 통화 UI 전체를 항상 다른 창 위에 표시되는 별도 브라우저 창으로 분리할 수 있습니다. 애플리케이션은 여러 비디오 요소와 컨트롤 요소를 원하는 레이아웃으로 자유롭게 구성한 HTML을 이 PiP 창 안에 렌더링할 수 있으며, 이를 통해 사용자가 다른 탭이나 창에서 작업하는 동안에도 계속 통화 UI를 볼 수 있게 만들 수 있습니다.
| 지원 통화 유형 | 최소 SDK 버전 |
|---|---|
| 1대1 통화, 그룹 통화(컨퍼런스) | WebPlanetKit 6.0 |
개요
WebPlanetKit은 Document PiP 창을 열고 그 안에 빈 컨테이너 요소를 만들어 제공합니다. 이 컨테이너 안에 통화 UI(비디오 요소, 컨트롤 요소, 레이아웃)를 그리는 것은 애플리케이션에서 담당합니다.
브라우저 지원 여부 확인
Document PiP API는 Chrome과 Edge와 같은 Chromium 계열 데스크톱 브라우저에서만 사용할 수 있습니다. 따라서 UI에서 PiP 진입점을 노출하기 전에 반드시 현재 브라우저의 지원 여부를 확인해야 합니다.
현재 브라우저가 Document PiP API를 지원하는지 확인하려면 정적 메서드 isDocumentPipSupported()를 호출합니다. Call이나 Conference 객체를 만들지 않아도 사용할 수 있고, 통화를 시작하기 전에도 호출할 수 있습니다. Call.isDocumentPipSupported()와 Conference.isDocumentPipSupported()는 항상 같은 값을 반환합니다.
Document PiP 사용하기
Document PiP 창 열기
openDocumentPip(options)를 호출하면 PiP 창이 열립니다. 이 메서드는 새로 열린 PiP 창과 그 안에 생성된 빈 컨테이너 요소를 함께 담은 DocumentPipResult를 결과로 반환합니다. 애플리케이션에서는 이 컨테이너 안에 통화 UI를 직접 그리면 됩니다. 동일한 DocumentPipResult가 evtPipOpened 이벤트로도 전달됩니다. openDocumentPip()의 반환값(Promise)을 await으로 받아서 사용해도 되고, evtPipOpened 이벤트에서 받아서 사용해도 됩니다. 애플리케이션에서 편한 쪽을 선택하면 됩니다.
DocumentPipOptions의 필드 | 타입 | 설명 |
|---|---|---|
width | number | (선택) PiP 창의 초기 너비(픽셀). |
height | number | (선택) PiP 창의 초기 높이(픽셀). |
disallowReturnToOpener | boolean | (선택) true인 경우 PiP 창에서 원래 탭으로 돌아가는 컨트롤 요소를 숨깁니다. |
preferInitialWindowPlacement | boolean | (선택) true인 경우 이전에 열려 있던 PiP 창의 위치를 기억하지 않고 처음부터 새로 배치합니다. |
DocumentPipResult의 필드 | 타입 | 설명 |
|---|---|---|
pipWindow | Window | PiP가 렌더링되는 별도 브라우저 창입니다. |
container | HTMLDivElement | pipWindow 안에 생성된 빈 컨테이너 요소로 여기에 비디오와 UI를 그립니다. |
openDocumentPip()는 통화가 시작되기 전에는 호출할 수 없습니다. 먼저 통화를 시작해야 호출할 수 있으며,makeCall(),verifyCall(),joinConference()중 하나를 호출한 뒤에는 어떤 통화 상태에서도 PiP 창을 열 수 있습니다.- 한 페이지에는 Document PiP 창이 하나만 존재할 수 있습니다. 다른 인스턴스가 이미 PiP 창을 갖고 있는 상태에서
openDocumentPip()를 호출하면 브라우저가 요청을 거부합니다.
Document PiP 창 닫기
사용자가 PiP 창을 직접 닫거나, 통화가 종료되거나, 연결이 끊기면 PiP 창은 자동으로 닫힙니다. 이 경우 브라우저나 SDK가 알아서 처리하므로 애플리케이션에서 별도로 할 일은 없습니다.
애플리케이션에서 명시적으로 PiP 창을 닫으려면 closeDocumentPip()를 호출합니다. 이때 evtPipClosed 이벤트가 발생합니다. 열려 있는 PiP 창이 없는 상태에서 호출하면 아무 일도 일어나지 않고 정상 종료되며, evtPipClosed도 발생하지 않습니다.
Document PiP 창이 열리거나 닫힐 때 필요한 처리 넣기
PiP 창이 열리거나 닫히는 시점에 애플리케이션에서 처리할 로직이 있으면 통화 delegate에 evtPipOpened와 evtPipClosed를 등록합니다. evtPipOpened에는 openDocumentPip()가 반환하는 것과 같은 DocumentPipResult가 전달됩니다. evtPipClosed 이벤트를 통해 PiP 창이 닫힌 이유를 확인할 수 있으며, DocumentPipClosedInfo가 전달됩니다.
DocumentPipClosedInfo.reason 값 | 의미 |
|---|---|
'user' | 사용자가 PiP 창을 닫음 |
'disconnect' | 통화가 종료되거나 연결이 끊김 |
'api' | 애플리케이션이 closeDocumentPip()를 호출함 |
현재 Document PiP 창과 컨테이너 조회
지금 열려 있는 PiP 창의 Window나 컨테이너 요소를 얻고 싶을 때는 getDocumentPipWindow()와 getDocumentPipContainer()를 사용합니다. 열려 있는 PiP 창이 없다면 두 메서드 모두 null을 반환합니다.
예제: 탭 이동 시 Document PiP 자동 열기 구현하기
Chromium 계열 브라우저(Chrome 120 이상)에는 통화 중인 탭에서 사용자가 다른 탭으로 이동하거나 창을 최소화할 때 PiP 창을 자동으로 열어주는 기능이 있습니다. 애플리케이션이 브라우저의 enterpictureinpicture 액션에 대한 핸들러를 등록해 두면 사용자가 탭을 떠날 때마다 Chrome이 이 핸들러를 호출합니다. 핸들러 안에서 openDocumentPip()를 호출하면 PiP 창이 열립니다. 사용자가 원래 탭으로 다시 돌아오면 Chrome이 PiP 창을 알아서 닫아주기 때문에 애플리케이션이 창을 여닫는 처리를 매번 직접 관리할 필요가 없습니다.
- 마이크와 카메라를 아예 사용하지 않는 통화에서는 자동 PiP(auto-PiP)를 사용할 수 없습니다.
enterpictureinpicture액션과 Document PiP API는 Chromium 계열 브라우저(Chrome 120 이상)에서만 사용할 수 있습니다.
구현 방법
다음 코드는 브라우저 지원 확인, 액션 핸들러 등록, PiP 창이 열렸을 때 로컬 사용자 비디오를 그리는 처리, 통화 종료 시 핸들러 정리까지의 전체 흐름을 보여줍니다.
if (!PlanetKit.Call.isDocumentPipSupported()) {
// Unsupported browser.
return;
}
// When the user leaves the tab, Chrome calls this handler, which opens the PiP window.
navigator.mediaSession.setActionHandler('enterpictureinpicture', () => {
planetKit.openDocumentPip({ width: 480, height: 360 });
});
planetKit.makeCall({
...,
delegate: {
evtPipOpened: ({ pipWindow, container }) => {
// Draw the local user's video in the container inside the PiP window.
const video = pipWindow.document.createElement('video');
video.srcObject = planetKit.getMyMediaStream();
container.appendChild(video);
video.play();
},
evtDisconnected: () => {
// When the call ends, clean up the registered action handler.
navigator.mediaSession.setActionHandler('enterpictureinpicture', null);
}
}
});
자동 PiP 작동 조건
Chrome이 enterpictureinpicture 액션을 호출하려면 다음 두 조건을 모두 만족해야 합니다.
- 애플리케이션이 미리
enterpictureinpicture액션 핸들러를 등록해 두어야 합니다. - 사용자가 탭을 떠나는 시점에 그 탭이 마이크나 카메라를 실제로 사용 중이어야 합니다. 여기서 '사용 중'이란 브라우저가 마이크 및 카메라 스트림을 실제로 열어서 브라우저 탭이나 주소창 옆에 마이크 및 카메라 사용 표시등이 켜진 상태를 의미합니다.
위 두 번째 조건과 관련해서 유의해야 할 몇 가지 사항을 살펴보겠습니다.
첫 번째로, WebPlanetKit은 사용자에게 당장 필요하지 않은 권한 허용을 요청하는 것을 피하기 위해 다음 두 경우에는 통화 시작 시점에 마이크 및 카메라에 접근하지 않습니다.
micOn: false로 시작한 음성 통화micOn: false이고cameraOn: false로 시작한 영상 통화
따라서 위 두 가지 경우로 통화를 시작하면 브라우저가 마이크 및 카메라를 사용하고 있다고 인식하지 않으므로 enterpictureinpicture 액션이 발생하지 않고, 결과적으로 자동 PiP가 작동하지 않습니다. 그 외의 경우, 즉 마이크나 카메라 중 최소 하나 이상을 실제로 사용하며 통화를 시작하는 경우에는 SDK가 해당 미디어 스트림을 열기 때문에 자동 PiP가 정상 작동합니다.
두 번째로, 통화 도중에 음 소거(mute)나 일시 중지(pause)를 실행했을 때 마이크와 카메라의 작동이 서로 다릅니다.
- 마이크: 마이크 음 소거는 데이터 흐름만 잠시 막는 방식으로, 음 소거해도 SDK는 마이크 스트림을 닫지 않고 그대로 유지합니다. 따라서 통화 시작 시 마이크가 한 번이라도 켜졌던 통화에서는 이후 음 소거 상태로 백그라운드로 전환해도 자동 PiP가 계속 작동합니다.
- 카메라: 일시 중지하면 SDK는 카메라 LED를 끄기 위해 카메라 스트림을 완전히 닫습니다. 따라서 카메라를 켰다가 일시 중지한 상태에서 마이크가 사용 중이 아니라면 자동 PiP가 작동하지 않습니다.
세 번째로, MediaStreamManager를 통해 미디어 스트림을 생성해서 통화에 사용하는 경우, MediaStreamManager가 내부에서 마이크 및 카메라 스트림을 열어두므로 micOn이나 cameraOn 값과 관계없이 자동 PiP가 작동합니다.
관련 API
Document PiP와 관련된 API는 다음과 같습니다.
공통
1대1 통화
-
Call의isDocumentPipSupported() -
Call의openDocumentPip() -
Call의closeDocumentPip() -
Call의getDocumentPipWindow() -
Call의getDocumentPipContainer() -
MakeCallDelegate의evtPipOpened -
MakeCallDelegate의evtPipClosed -
VerifyCallDelegate의evtPipOpened -
VerifyCallDelegate의evtPipClosed
그룹 통화
-
Conference의isDocumentPipSupported() -
Conference의openDocumentPip() -
Conference의closeDocumentPip() -
Conference의getDocumentPipWindow() -
Conference의getDocumentPipContainer() -
ConferenceDelegate의evtPipOpened -
ConferenceDelegate의evtPipClosed