Web
소개
맥스버스 AR Live 가 제공하는 실시간 통신 서비스에 대해 별도의 WebRTC
해당 SDK를 사용하여 간편하게 화상 통화 서비스를 구현할 수 있습니다.
개발 프로세스
web 환경에서 AR Live 웹 SDK를 사용하여 화상 통화 서비스를 구현하는 프로세스는 다음과 같습니다.
정보 | 설명 | 필수 여부 |
---|---|---|
프로젝트 생성 | 웹 프론트엔드 개발 준비를 위한 프로젝트 생성 | REQUIRED |
SDK 설치 | npm을 추가하여 SDK 설치 | REQUIRED |
서비스 개발 | 서비스 별 개발 프로세스 작업 | REQUIRED |
개발 요구 사항
AR Live 웹 SDK는 Internet Explorer(IE)를 제외한 WebRTC를 지원하는 모든 브라우저를 지원합니다.
브라우저별 최소 요구 사항은 다음과 같습니다.
구분 | 항목 | 테스크탑 OS |
---|---|---|
브라우저 | Chrome | Windows, macOS, Linux |
브라우저 | Firefox | Windows, macOS, Linux |
브라우저 | Safari | IOS |
브라우저 | Edge(Chromium) | Windows, macOS |
현재 개발 환경이 위 사항에 해당하지 않는 경우, adaptiveStream 이나 dynacast 같은 특정 기능 사용에 제약이 있습니다.
미지원 브라우저를 사용하면서 adaptiveStream 기능을 원한다면, ResizeObserver및 IntersectionObserver
에 대해 Polyfill
를 사용해야 할 수 있습니다.
사전 작업
AR Live 웹 SDK를 사용하여 개발을 수행하기 전에 다음 작업이 선제되어야 합니다.
프로젝트 생성
AR Live 웹 SDK는 브라우저 환경에서 작동하며, 일반적인 웹 프론트엔드 개발 준비가 필요합니다.
터미널을 실행하고 폴더를 생성한 후에 해당 폴더에 진입합니다.
mkdir exmaple
cd example패키지 생성과 초기화 작업을 위해 다음 명령어를 실행합니다.
npm init
각 개발 상황에 따라 웹 서버를 구축하고, 서버를 활성화합니다.
# Node.js
# Open browser "localhost:8080"
npm i http-server
touch index.html
npx http-server
SDK 설치하기
AR Live 웹 SDK는 npm을 통해 설치할 수 있습니다.
npm 명령어를 통해 SDK를 설치합니다.
npm i @maxverse/media-web-sdk
media-web-sdk
해당 프로젝트 내에서 CommonJS(CJS)
또는 ECMAScript Modules(ESM)
패턴으로 SDK를 불러온 후 사용할 수 있습니다.
import LiveRoom from '@maxverse/media-web-sdk'
// or
const LiveRoom from require("@maxverse/media-web-sdk");
AR Live Web SDK 개발
사전 작업을 완료하면, 다음과 같은 프로세스를 거쳐 SDK을 통해 서비스 개발 시작할 수 있습니다.
프로세스 | 필수 여부 | 설명 |
---|---|---|
View 등록 | REQUIRED | 통화 중 자신과 상대방을 확인할 수 있는 뷰 또는 해당 뷰를 조작할 수 있는 섹션을 등록 |
LiveRoom 객체 생성 | REQUIRED | 설정된 Config 객체로 LiveRoom 인스턴스 생성 |
Event Callback 구현 | OPTIONAL | 각 이벤트 상태에 따른 후속 처리를 위한 Callback 구현 |
RoomEvent Callback 구현 | OPTIONAL | Room에서 발생하는 이벤트 상태에 대해 Callback 구현 |
ParticipantEvent Callback 구현 | OPTIONAL | Participant에서 발생하는 이벤트 상태에 대해 Callback 구현 |
LiveRoom 접속 | REQUIRED | 방에 접속하고 관련 초기화 작업을 구현 |
View 등록
통화 시 필요한 전체 레이아웃을 사전에 구성합니다.
레이아웃에는 각 참석자의 비디오 및 오디오 활성 상태에 따라 동적으로 렌더링될 UI 요소들이 추가됩니다.
통화 중 자신의 화면과 참석자들의 화면을 볼 수 있는
View Section
을 등록합니다.<div id="participant-view-section"></div>
Local View
의 비디오와 오디오를 조작할 수 있는Control Section
을 등록합니다.<div id="control-section"></div>
위 마크업은 예시 코드 입니다. 각 서비스 상황에 맞는 태그 및 추가적인 HTMLAttributes
를 설정하여도 무방합니다.
디민 원활한 서비스 개발 시, 정확한 노드를 참조하기 위해 각 섹션에 해당하는 태그들에 대해선 고유 식별 값이 필수적으로 요구됩니다.
LiveRoom 객체 생성
실시간 통화를 위해 LiveRoom
인스턴스를 생성합니다.
LiveRoom
인스턴스는 웹 상에서 클라이언트가 원활한 실시간 통화 개발 환경을 구축할 수 있도록 상황에 맞는 메서드를 제공합니다.
Media에 대한 사용자 정의 Config 객체를 설정합니다.
const config = {
isVideoEnabled: true,
isAudioEnabled: false,
};
LiveRoom Config 객체
구분 타입 설명 isVideoEnabled boolean 로컬 참석자의 카메라 사용 여부 설정
기본 trueisAudioEnabled boolean 로컬 참석자의 오디오 사용 여부 설정
기본 truevideoDeviceId string 접속 시 처음으로 사용할 카메라의 MediaDeviceInfo.deviceId 설정
미설정 시 mediaDeviceInfo[0].deviceId를 참조audioDeviceId string 접속 시 처음으로 사용할 오디오의 MediaDeviceInfo.deviceId 설정
미설정 시 mediaDeviceInfo[0].deviceId를 참조autoSubscribe boolean 참여 후 방에 있는 모든 트랙들을 자동 구독
기본 truepeerConnectionTimeout number PeerConnection 이 설정되는 시간 설정
기본 15초rtcConfig RTCConfiguration 사용자 RTCConfiguration 재정의
maxRetries number 초기 연결 재시도 빈도 지정
서버에 연결할 수 없는 경우에만 적용 가능adaptiveStream boolean 서버에서 구독한 비디오 트랙의 품질을 자동으로 관리하여 대역폭과 CPU를 최적화 dynacast boolean 구독자(subscriber)가 사용하지 않는 비디오 레이어를 동적으로 일시 중지하여 대역폭 및 CPU를 최적화
기본 false
LiveRoom 인스턴스 생성 및 초기화를 합니다
const liveRoom = new LiveRoom(config);
LiveRoom 클래스
구분 설명 prepareConnection(url?: string) HEAD 요청을 전송하여 서버에 대한 연결을 준비
connectRoom(token: string, url?: string) 실제 방 접속을 위한 연결 시도 toggleCam() 참가자의 카메라 트랙을 활성화 또는 비활성화
비디오 트랙이 이미 게시(publish)된 트랙의 경우 음소거하거나 음소거 해제toggleMic() 참가자의 오디오 트랙을 활성화 또는 비활성화
오디오 트랙이 이미 게시된(publish)된 트랙의 경우 음소거하거나 음소거 해제bindRoomEvents(handler: RoomHandler) 현재 방에 RoomEvent 이벤트
연결initializeCurrentRoomStatus(handler: RoomHandler) 현재 방의 상태 정보를 동기화 bindParticipantEvents(sid: string, handler: ParticipantHandler) 현재 참석자에 Participant 이벤트
연결initializeCurrentParticipantStatus(sid: string, handler: ParticipantHandler) 현재 참석자의 상태 정보를 동기화 initializeLocalTracks() 참석자의 videoDeviceId
와audioDeviceId
를 참조하여 mediaTrack 게시.
videoDeviceId
와audioDeviceId
가 설정되어 있지 않는 경우 사용자 로컬에 저장된 첫 번째 기기 정보 아이디 값을 참조
이벤트 Callback 구현
통화 시작/종료, 접속 상태 변경, publish/subscribe 성공 여부 등과 같은 이벤트가 발생했거나 특정 시점에 도달했을 때, 시스템에서 호출할 수 있는 Callback 메서드를 구현합니다.
AR Live 웹 SDK에선 기본적으로 RoomEvent Callback 과 ParticipantEvent Callback을 해당 목적에 맞게 등록합니다.RoomEvent Callback에서는 관련 인스턴스 초기화 및 전체 방의 상태에 대한 UI를 변경할 수 있도록 하고 ParticipantEvent Callback에서는 각 참석자의 비디오 및 오디오 상태를 관리하도록, 개발하는 것을 지향합니다.
이는 UDF및 declarative UI
패턴의 기타 웹 프레임워크 및 라이브러리 내에서도 본 SDK를 활용하여 개발될 수 있도록 구조가 설계되었기 때문입니다.
다음은 이벤트 발생 시 반환해 주는 주요 인자값입니다.
targetParticipant
- 현재 방에 접속 중 특정 이벤트에 노출이 된 참석자의 주요 정보 및 관련 메서드를 제공합니다
구분 | 타입 | 설명 |
---|---|---|
sid | string | 서버에서 각 참석자에게 할당한 고유 키 |
identity | string | 방 접속 시 클라이언트에서 제공한 token을 인코딩한 id |
name | string | 방 접속 시 클라이언트에서 제공한 token을 인코딩한 name |
isLocal | boolean | 현재 이벤트에 노출된 참석자가 로컬 환경인지의 여부 |
isVideoEnabled | boolean | 현재 이벤트에 노출된 참석자의 비디오 트랙이 활성화 상태인지에 대한 여부 |
isAudioEnabled | boolean | 현재 이벤트에 노출된 참석자의 오디오 트랙이 활성화 상태인지에 대한 여부 |
isScreenShareEnabled | boolean | 현재 이벤트에 노출된 참석자의 화면 공유(비디오) 트랙이 활성화 상태인지에 대한 여부 |
attachTrackToElement | (element: HTMLMediaElement, trackSource: TrackSource) => void | 활성화 된 트랙을 특정 HTMLMediaElement에 첨부시켜 주는 메서드 |
detachTrackToElement | (element: HTMLMediaElement, trackSource: TrackSource) => void | 활성화 된 트랙을 특정 HTMLMediaElement로 부터 제거 시켜 주는 메서드 |
RoomEvent Callback 구현
onParticipantConnected(targetParticipant: TargetParticipant)
- 자신이 방에 이미 참가한 이후에 외부 참석자가 참가한 경우, 해당 Callback이 호출됩니다.
이미 방에 있는 참석자에 대해선 해당 이벤트를 내보내기(emit)하지 않습니다.
구분 타입 설명 targetParticipant TargetParticipant 현재 방에 접속 중 특정 이벤트에 노출이 된 참석자 인스턴스 우선
sid
값을 통해 각 참석자의 view 영역을 초기화 해 줍니다.const onParticipantConnected = (targetParticipant: TargetParticipant) => {
const $participant = document.createElement("div");
$participant.id = `participant-${targetParticipant.sid}`;
$participant.className = `participant ${targetParticipant.sid}`;
// 사용자 서비스에 맞는 UI를 작성
};참석자에 대한 공통 UI를 작성이 끝났다면 targetParticipant의
isLocal
값을 통해 참석자의 현재 환경에 맞는 추가 UI를 업데이트 하거나 이벤트를 연결하여 줍니다. 현재 로컬 PC 기준 처음 접속한 모든 참석자들에 대해서 ParticipantEvent를 초기화해주기 위해 liveRoom.bindParticipantEvents 메서드를 호출하여 줍니다.
해당 초기화 작업은 로컬 및 원격 참석자 모두에 한해서 이뤄져야 합니다.if (targetParticipant.isLocal) {
// 로컬 침석자에 필요한 추가 UI 작업
} else {
// 원격 참석자에 필요한 추가 UI 작업
}
liveRoom.bindParticipantEvents(sid, participantHandler);bindParticipantEvents 와 initializeCurrentParticipantStatus는 내부적으로 인자로 받은 sid 값을 통해 DOM Element를 참조하기 때문에 우선적으로 참석자에 대한 UI를 초기화하지 않고 사용 시, ParticipantEvent Callback에 정의된 기능이 제대로 동작하지 않을 수 있습니다.
하울링 현상이 발생하는 것을 방지하기 위해 원격 참석자 한해서 HTMLAudioElement
를 생성하거나 로컬 참석자의 오디오를 mute
하는 것을 추천합니다.
- 자신이 방에 이미 참가한 이후에 외부 참석자가 참가한 경우, 해당 Callback이 호출됩니다.
onParticpantDisconnected(targetParticipant: TargetParticipant)
- 방 안에 참석자가 연결이 끊어질 경우 해당 이벤트 Callback이 호출됩니다.
구분 | 타입 | 설명 |
---|---|---|
targetParticipant | TargetParticipant | 현재 방에 접속 중 특정 이벤트에 노출이 된 참석자 인스턴스 |
const onParticipantDisconnected = (targetParticipant: TargetParticipant) => {
// 연결이 끊어진 참석자를 섹션에서 제거
const $participant = document.getElementById(
`participant-${targetParticipant.sid}`
);
$participantSection.removeChild($participant);
};
onConnectionStateChanged(currentConnectionInfo: CurrentConnnectionInfo)
- 현재 자신의 방에 접속 상태 정보가 바뀔 때, 해당 Callback이 호출됩니다.
현재 방 상태에 따라 UI를 추가하고 싶다면 해당 Callback에 관련 기능을 구현합니다.
구분 타입 설명 currentConnectionInfo CurrentConnectionInfo 현재 방 접속 상태 값 구분 타입 설명 roomId string 현재 참가한 방 고유 키 값 state enum 현재 참가한 방의 접속 상태 const onConnectionStateChanged = (
currentConnectionInfo: CurrentConnectionInfo
) => {
const { state } = currentConnectionInfo;
switch (status) {
case ConnectionState.Disconnected:
// 연결이 끊어졌을 때
break;
case ConnectionState.Connecting:
// 연결을 시도하는 중일 때
break;
case ConnectionState.Connected:
// 연결이 완료되었을 때
break;
case ConnectionState.Reconnecting:
// 처음 연결이 실패하고 재연결을 시도할 때
break;
}
};- 현재 자신의 방에 접속 상태 정보가 바뀔 때, 해당 Callback이 호출됩니다.
ParticipantEvent Callback 구현
onLocalVideoUpdated(targetParticipant: TargetParticipant)
- 로컬 참석자가 자신의 비디오 트랙을 설공적으로 게시 또는 중지한 경우, 해당 Callback이 호출됩니다.
- 이미 게시된 비디오 트랙에 대해선 해당 Callback은 호출되지 않습니다.
- liveRoom Config에서 설정한 videoDeviceId에 해당하는 트랙 정보를 참조합니다.
구분 | 타입 | 설명 |
---|---|---|
targetParticipant | TargetParticipant | 현재 방에 접속 중 특정 이벤트에 노출이 된 참석자 인스턴스 |
const onLocalVideoUpdated = (targetParticipant: TargetParticipant) => {
// 사용자의 고유 HTMLVideoElement
const $video = renderVideo(targetParticipant.sid);
attachTrack($video);
$video.style.transform = "rotateY(180deg)";
// 관련 UI 작업
};
화면 미러링 방지를 위해 내보내는 비디오 화면을 뒤짚는 추가 작업이 필수적으로 요구됩니다.
onLocalAudioUpdated(targetParticipant: TargetParticipant)
- 로컬 참석자가 자신의 오디오 트랙을 성공적으로 게시 또는 중지한 경우, 해당 Callback이 호출됩니다.
- 이미 게시된 오디오 트랙에 대해선 해당 Callback은 호출되지 않습니다.
- liveRoom Config에서 설정한 audioDeviceId에 해당하는 트랙 정보를 참조합니다.
구분 | 타입 | 설명 |
---|---|---|
targetParticipant | TargetParticipant | 현재 방에 접속 중 특정 이벤트에 노출이 된 참석자 인스턴스 |
const onLocalAudioUpdated = (targetParticipant: TargetParticipant) => {
// 사용자의 고유 HTMLAudioElement
const $audio = renderAudio(targetParticipant.sid);
targetParticipant.attachTrackToElement($audio);
// 관련 UI 작업
};
onRemoteVideoUpdated(targetParticipant: TargetParticipant)
- 원격 참석자의 비디오 트랙을 성공적으로 구독하였을 경우, 해당 Callback이 호출됩니다.
- 새로운 비디오 트랙이 준비되어 있는 경우 항상 실행되지만 이미 구독된 비디오 트랙에 대해선 실행되지 않기 때문에 구독이 완료된 비디오 트랙에 대해선 onVideoSwitched 이벤트를 통해 UI을 렌더링해야 합니다.
구분 | 타입 | 설명 |
---|---|---|
targetParticipant | TargetParticipant | 현재 방에 접속 중 특정 이벤트에 노출이 된 참석자 인스턴스 |
const onRemoteVideoUpdated = (targetParticipant: TargetParticipant) => {
// 사용자의 고유 HTMLVideoElement
const $video = renderVideo(targetParticipant.sid);
attachTrack($video);
$video.style.transform = "rotateY(180deg)";
// 관련 UI 작업
};
onRemoteAudioUpdated(targetParticipant: TargetParticipant)
- 원격 참석자의 오디오 트랙을 성공적으로 구독하였을 경우, 해당 Callback이 호출됩니다.
- 새로운 오디오 트랙이 준비되어 있는 경우 항상 실행되지만 이미 구독된 오디오 트랙에 대해선 실행되지 않기 때문에 구독이 완료된 오디오 트랙에 대해선 onAudioSwitched 이벤트를 통해 UI을 렌더링해야 합니다.
구분 | 타입 | 설명 |
---|---|---|
targetParticipant | TargetParticipant | 현재 방에 접속 중 특정 이벤트에 노출이 된 참석자 인스턴스 |
const onRemoteAudioUpdated = (targetParticipant: TargetParticipant) => {
// 사용자의 고유 HTMLAudioElement
const $audio = renderAudio(targetParticipant.sid);
attachTrack($audio);
// 관련 UI 작업
};
onLocalScreenShareUpdated(targetParticipant: TargetParticipant)
- 로컬 참석자의 회면 공유(비디오) 트랙을 성공적으로 게시 및 중지할 경우, 해당 Callback이 호출됩니다.
- 새로운 화면 공유(비디오) 트랙이 준비되어 있는 경우 항상 실행됩니다.
- LiveRoom의 `toggleScreenShare` 메서드를 통해 해당 이벤트를 발생시킬 수 있습니다.
구분 | 타입 | 설명 |
---|---|---|
targetParticipant | TargetParticipant | 현재 방에 접속 중 특정 이벤트에 노출이 된 참석자 인스턴스 |
const onLocalScreenShareUpdated = (targetParticipant: TargetParticipant) => {
const $participant = document.getElementById(
`participant-${targetParticipant.sid}`
);
const $screenShareVideo = renderVideo(`screenShare-${targetParticipant.sid}`);
// 관련 UI 작업
};
const $screenShareButton = document.getElementById("screenShareButton");
$screenShareButton?.addEventListener("click", () => {
liveRoom.toggleScreenShare();
});
onRemoteScreenShareUpdated(targetParticipant: TargetParticipant)
- 원격 참석자의 회면 공유(비디오) 트랙을 성공적으로 구독한 경우, 해당 Callback이 호출됩니다.
구분 | 타입 | 설명 |
---|---|---|
targetParticipant | TargetParticipant | 현재 방에 접속 중 특정 이벤트에 노출이 된 참석자 인스턴스 |
const onRemoteScreenShareUpdated = (targetParticipant: TargetParticipant) => {
const $participant = document.getElementById(
`participant-${targetParticipant.sid}`
);
const $screenShareVideo = renderVideo(`screenShare-${targetParticipant.sid}`);
// 관련 UI 작업
};
onVideoSwitched(targetParticipant: TargetParticipant)
- 현재 활성화되어 있는 비디오를 on/off 할 경우 해당 Callback을 사용한다.
- 해당 이벤트는 로컬 및 원격 환경 모두에서 감지됩니다.
- LiveRoom의 `toggleCam` 메서드를 통해 해당 이벤트를 발생시킬 수 있습니다.
구분 | 타입 | 설명 |
---|---|---|
targetParticipant | TargetParticipant | 현재 방에 접속 중 특정 이벤트에 노출이 된 참석자 인스턴스 |
const onVideoSwitched = (targetParticpant: TargetParticipant) => {
const $video = renderVideo(targetParticipant.sid);
targetParticipant.isVideoEnabled
? targetParticipant.attachTrackToElement()
: targetParticipant.detachTrackToElement();
// 변경된 비디오 상태를 UI에 반영
if (targetParticipant.isVideoEnabled) {
switchScreen($video, $mutedScreen);
} else {
$video.style.transform = "rotateY(180deg)";
switchScreen($defaultScreen, $mutedScreen);
}
};
const $videoButton = document.getElementById("videoButton");
$videoButton?.addEventListener("click", () => {
liveRoom.toggleCam();
});
각 서비스에 따라 targetParticipant의 isLocal 값이
true
일 경우 관련 카메라 컨트롤 UI들을isVideoEnabled
상태에 맞쳐 변경이 필요할 수 있습니다.
onAudeoSwitched(targetParticipant: TargetParticipant)
- 현재 활성화되어 있는 오디오를 on/off 할 경우 해당 Callback을 사용한다.
- 해당 이벤트는 로컬 및 원격 환경 모두에서 감지됩니다.
- LiveRoom의 `toggleMic` 메서드를 통해 해당 이벤트를 발생시킬 수 있습니다.
구분 | 타입 | 설명 |
---|---|---|
targetParticipant | TargetParticipant | 현재 방에 접속 중 특정 이벤트에 노출이 된 참석자 인스턴스 |
const onAudioSwitched = (targetParticipant: TargetParticipant) => {
const $audio = renderAudio(targetParticipant.sid);
switchTrack($audio);
};
const $audioButton = document.getElementById("audioButton");
$audioButton?.addEventListener("click", () => {
liveRoom.toggleMic();
});
각 서비스에 따라 targetParticipant의 isLocal 값이
true
일 경우 관련 오디오 컨트롤 UI들을isAudioEnalbed
상태에 맞쳐 변경이 필요할 수 있습니다.
LiveRoom 접속
LiveRoom의 인스턴스 초기화와 서비스에 필요한 Callback들을 구현이 완료되었다면, 이제 생성된 LiveRoom 인스턴스를 통해 접속 및 필요한 초기화 작업 과정을 거치면 됩니다.
접속 단계
접속은 사전 접속과 실제 접속 두 단계로 나뉘어 집니다.
접속 성공 여부에 따라 초기화된
InitalConnectionStatus
enum 값을 반환합니다.구분 타입 설명 CONNECTION_SUCESS string 접속 성공 CONNECTION_FAIL string 접속 실패 PREPARE_CONNECTION_SUCCESS string 사전 접속 성공 PREPARE_CONNECTION_FAIL string 사전 접속 실패
entry 힘수에 템플릿를 선언하여 줍니다.
(async function app() {
try {
} catch (error) {}
})();liveRoom.prepareConnection
메서드를 호출하여 줍니다.
사전 접속이 성공할 경우 현재 방에 대해 RoomEvent들을 연결하여 줍니다.const status = await liveRoom.prepareConnection();
if (status === InitialConnectionStatus.PREPARE_CONNECTION_SUCCESS) {
liveRoom.bindRoomEvents(roomHandler);
}
방 입장 전 발급 받은 Room 침여 액세스 토큰을 인자에 할당하여 줍니다.liveRoom.connectRoom
메서드를 호출하여 줍니다.
접속이 성공한 경우 현재 방 상태의 정보를 동기화해 주기 위해 `liveRoom.initializeCurrentRoomStatus` 메서드를 호출하여 줍니다.
이는 처음 입장한 참석자와 기존 방에 참석해 있던 참석자들에 대해서 RoomEvent의 onParticipantConnected 이벤트가 감지되지 않기 때문입니다.await liveRoom.connectRoom(token).then((status) => {
if (status === InitialConnectionStatus.CONNECTION_SUCCESS) {
liveRoom.initializeCurrentRoomStatus(roomHandler);
}
});
mediaDeviceInfo 초기화 단계
- liveRoom Config에서 설정한 `videoDeviceId`와 `audioDeviceId` 값을 토대로 현재 로컬 참석자의 미디어 트랙 정보를 publish 합니다.
- 만약 지정된
mediaDeviceInfo.id
가 없다면 기본적으로 사용자 로컬에 저장된 첫 번째 기기 정보 아이디 값을 참조하여 publish합니다. - publish된 미디어 트랙 정보는 ParticipantEvent의 onLocalVideoTrackPublished, onLocalAudioTrackPublished, onVideoTrackSubscribed, onAudioTrackSubscribed에서 감지됩니다.
liveRoom.initializeLocalTracks();