IVS Web Broadcast SDK를 사용한 게시 및 구독 | 실시간 스트리밍
이 문서에서는 IVS Real-Time Streaming Web Broadcast SDK를 사용하여 스테이지에 게시하고 구독하는 단계를 안내합니다.
개념
실시간 기능의 3가지 핵심 개념은 스테이지, 전략 및 이벤트입니다. 설계 목표는 작동하는 제품을 구축하는 데 필요한 클라이언트 측 로직의 수를 최소화하는 것입니다.
단계
Stage
클래스는 호스트 애플리케이션과 SDK 사이의 주요 상호 작용 지점입니다. 스테이지 자체를 나타내며 스테이지에 참가하고 나가는 데 사용됩니다. 스테이지를 만들고 참가하려면 제어 플레인에서 유효하고 만료되지 않은 토큰 문자열(token
으로 표시됨)이 필요합니다. 스테이지 참가 및 나가기는 간단합니다.
const stage = new Stage(token, strategy) try { await stage.join(); } catch (error) { // handle join exception } stage.leave();
Strategy
StageStrategy
인터페이스는 호스트 애플리케이션이 원하는 스테이지 상태를 SDK에 전달하는 방법을 제공합니다. shouldSubscribeToParticipant
, shouldPublishParticipant
및 stageStreamsToPublish
함수를 구현해야 합니다. 모든 함수를 아래에서 설명합니다.
정의된 전략을 사용하려면 Stage
생성자에게 전달합니다. 다음은 참가자의 웹캠을 스테이지에 게시하고 모든 참가자를 구독하는 전략을 사용하는 애플리케이션의 전체 예제입니다. 각 필수 전력 함수의 목적은 후속 섹션에서 자세히 설명합니다.
const devices = await navigator.mediaDevices.getUserMedia({ audio: true, video: { width: { max: 1280 }, height: { max: 720 }, } }); const myAudioTrack = new LocalStageStream(devices.getAudioTracks()[0]); const myVideoTrack = new LocalStageStream(devices.getVideoTracks()[0]); // Define the stage strategy, implementing required functions const strategy = { audioTrack: myAudioTrack, videoTrack: myVideoTrack, // optional updateTracks(newAudioTrack, newVideoTrack) { this.audioTrack = newAudioTrack; this.videoTrack = newVideoTrack; }, // required stageStreamsToPublish() { return [this.audioTrack, this.videoTrack]; }, // required shouldPublishParticipant(participant) { return true; }, // required shouldSubscribeToParticipant(participant) { return SubscribeType.AUDIO_VIDEO; } }; // Initialize the stage and start publishing const stage = new Stage(token, strategy); await stage.join(); // To update later (e.g. in an onClick event handler) strategy.updateTracks(myNewAudioTrack, myNewVideoTrack); stage.refreshStrategy();
참가자 구독
shouldSubscribeToParticipant(participant: StageParticipantInfo): SubscribeType
원격 참가자가 스테이지에 참가하면 SDK는 호스트 애플리케이션에 해당 참가자의 원하는 구독 상태를 쿼리합니다. 옵션은 NONE
, AUDIO_ONLY
및 AUDIO_VIDEO
입니다. 이 함수의 값을 반환할 때 호스트 애플리케이션은 게시 상태, 현재 구독 상태 또는 스테이지 연결 상태에 대해 걱정할 필요가 없습니다. AUDIO_VIDEO
가 반환되는 경우 SDK는 구독 전에 원격 참가자가 게시할 때까지 기다리고, 프로세스 전반에 걸쳐 이벤트를 발생시켜 호스트 애플리케이션을 업데이트합니다.
다음은 샘플 구현입니다.
const strategy = { shouldSubscribeToParticipant: (participant) => { return SubscribeType.AUDIO_VIDEO; } // ... other strategy functions }
항상 모든 참가자가 서로 보기를 원하는 호스트 애플리케이션(예: 비디오 채팅 애플리케이션)을 위한 이 기능의 전체 구현입니다.
고급 구현도 가능합니다. 예를 들어, CreateParticipantToken으로 토큰을 생성할 때 애플리케이션에서 role
속성을 제공한다고 가정해 보겠습니다. 애플리케이션에서 StageParticipantInfo
의 attributes
속성을 사용하여 서버에서 제공하는 속성을 기반으로 참가자를 선택적으로 구독합니다.
const strategy = { shouldSubscribeToParticipant(participant) { switch (participant.attributes.role) { case 'moderator': return SubscribeType.NONE; case 'guest': return SubscribeType.AUDIO_VIDEO; default: return SubscribeType.NONE; } } // . . . other strategies properties }
이를 통해 중재자가 직접 보거나 듣지 않고 모든 게스트를 모니터링할 수 있는 스테이지를 만들 수 있습니다. 호스트 애플리케이션은 추가 비즈니스 로직을 사용하여 중재자가 서로를 볼 수는 있지만 게스트에게는 보이지 않도록 할 수 있습니다.
참가자 구독 구성
subscribeConfiguration(participant: StageParticipantInfo): SubscribeConfiguration
원격 참가자를 구독 중인 경우(참가자 구독 참조) SDK에서는 해당 참가자의 사용자 지정 구독 구성에 대한 호스트 애플리케이션을 쿼리합니다. 이 구성은 선택 사항이며, 호스트 애플리케이션에서 구독자 동작의 특정 측면을 제어할 수 있습니다. 구성할 수 있는 항목에 대한 내용은 SDK 참조 설명서의 SubscribeConfiguration
다음은 샘플 구현입니다.
const strategy = { subscribeConfiguration: (participant) => { return { jitterBuffer: { minDelay: JitterBufferMinDelay.MEDIUM } } // ... other strategy functions }
구독한 모든 참가자의 지터-버퍼 최소 지연이 이 구현을 통해 MEDIUM
사전 설정으로 업데이트됩니다.
shouldSubscribeToParticipant
와 마찬가지로 고급 구현도 가능합니다. 주어진 ParticipantInfo
를 사용하여 특정 참가자에 대한 구독 구성을 선택적으로 업데이트할 수 있습니다.
기본 동작을 사용하는 것이 좋습니다. 변경하려는 특정 동작이 있는 경우에만 사용자 지정 구성을 지정합니다.
게시
shouldPublishParticipant(participant: StageParticipantInfo): boolean
스테이지에 연결되면 SDK는 호스트 애플리케이션을 쿼리하여 특정 참가자가 게시해야 하는지 여부를 확인합니다. 이는 제공된 토큰을 기반으로 게시할 권한이 있는 로컬 참가자에게만 호출됩니다.
다음은 샘플 구현입니다.
const strategy = { shouldPublishParticipant: (participant) => { return true; } // . . . other strategies properties }
이는 사용자가 항상 게시하기를 원하는 표준 비디오 채팅 애플리케이션에 대한 구현입니다. 오디오 및 비디오를 음소거 또는 음소거 해제하여 즉시 숨기거나 보기/듣기가 가능하도록 할 수 있습니다. (게시/게시 취소를 사용할 수도 있지만 속도가 훨씬 느립니다. 음소거/음소거 해제는 가시성을 자주 변경하는 것이 바람직한 사용 사례에 적합합니다.)
게시할 스트림 선택
stageStreamsToPublish(): LocalStageStream[];
게시할 때 이를 사용하여 게시해야 하는 오디오 및 비디오 스트림을 결정합니다. 이에 대해서는 미디어 스트림 게시에서 자세히 설명합니다.
전략 업데이트
전략은 동적이어야 합니다. 위 함수 중에서 반환되는 값은 언제든지 변경될 수 있습니다. 예를 들어 호스트 애플리케이션이 최종 사용자가 버튼을 탭할 때까지 게시하지 않으려는 경우, shouldPublishParticipant
에서 변수를 반환할 수 있습니다(예: hasUserTappedPublishButton
). 최종 사용자의 상호 작용에 따라 변수가 변경되면 stage.refreshStrategy()
를 호출하여 SDK에 최신 값에 대한 전략을 쿼리하고 변경된 사항만 적용하도록 신호를 보냅니다. SDK에서 shouldPublishParticipant
값이 변경된 것을 관찰하면 게시 프로세스가 시작됩니다. SDK 쿼리와 모든 함수가 이전과 동일한 값을 반환하는 경우 refreshStrategy
호출 시 스테이지가 수정되지 않습니다.
shouldSubscribeToParticipant
의 반환 값이 AUDIO_VIDEO
에서 AUDIO_ONLY
로 변경된 경우 이전에 비디오 스트림이 존재했다면 반환된 값이 변경된 모든 참가자에 대한 비디오 스트림이 제거됩니다.
일반적으로 스테이지는 전략을 사용하여 이전 전략과 현재 전략 간의 차이를 가장 효율적으로 적용하므로 호스트 애플리케이션에서 이를 올바르게 관리하는 데 필요한 모든 상태에 대해 걱정할 필요가 없습니다. 이로 인해 stage.refreshStrategy()
호출은 전략이 변경되지 않는 한 아무 소용도 없으므로 소모량이 적은 작업이라고 생각하면 됩니다.
이벤트
Stage
인스턴스는 이벤트 이미터입니다. stage.on()
을 사용하면 스테이지 상태가 프로토콜은 호스트 애플리케이션에 전달됩니다. 호스트 애플리케이션의 UI 업데이트는 전적으로 이벤트에 의해 지원될 수 있습니다. 이벤트는 다음과 같습니다.
stage.on(StageEvents.STAGE_CONNECTION_STATE_CHANGED, (state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_JOINED, (participant) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_LEFT, (participant) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_PUBLISH_STATE_CHANGED, (participant, state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_SUBSCRIBE_STATE_CHANGED, (participant, state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_REMOVED, (participant, streams) => {}) stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => {})
대부분의 이벤트에서 해당 ParticipantInfo
가 제공됩니다.
이벤트에서 제공하는 정보가 전략의 반환 값에 영향을 미칠 것으로 예상되지는 않습니다. 예를 들어, shouldSubscribeToParticipant
의 반환 값은 STAGE_PARTICIPANT_PUBLISH_STATE_CHANGED
가 호출될 때 변경되지 않을 것으로 예상됩니다. 호스트 애플리케이션이 특정 참가자를 구독하려는 경우 해당 참가자의 게시 상태와 무관하게 원하는 구독 유형을 반환해야 합니다. SDK는 원하는 전략 상태가 스테이지 상태를 기반을 정확한 시간에 실행되도록 하는 역할을 합니다.
미디어 스트림 게시
마이크 및 카메라와 같은 로컬 디바이스는 디바이스에서 MediaStream 검색에서 설명하는 것과 동일한 단계를 사용하여 검색됩니다. 이 예제에서는 MediaStream
을 사용하여 SDK에서 게시하는 데 사용되는 LocalStageStream
객체 목록을 만듭니다.
try { // Get stream using steps outlined in document above const stream = await getMediaStreamFromDevice(); let streamsToPublish = stream.getTracks().map(track => { new LocalStageStream(track) }); // Create stage with strategy, or update existing strategy const strategy = { stageStreamsToPublish: () => streamsToPublish } }
화면 공유 게시
애플리케이션에서 종종 사용자의 웹 카메라 외에도 화면 공유를 게시해야 합니다. 특히 화면 공유의 미디어를 게시하는 경우에 화면 공유를 게시하려면 스테이지에 대한 추가 토큰을 생성해야 합니다. getDisplayMedia
를 사용하고 해상도를 최대 720p로 제한합니다. 이후 단계는 스테이지에 카메라를 게시하는 것과 비슷합니다.
// Invoke the following lines to get the screenshare's tracks const media = await navigator.mediaDevices.getDisplayMedia({ video: { width: { max: 1280, }, height: { max: 720, } } }); const screenshare = { videoStream: new LocalStageStream(media.getVideoTracks()[0]) }; const screenshareStrategy = { stageStreamsToPublish: () => { return [screenshare.videoStream]; }, shouldPublishParticipant: (participant) => { return true; }, shouldSubscribeToParticipant: (participant) => { return SubscribeType.AUDIO_VIDEO; } } const screenshareStage = new Stage(screenshareToken, screenshareStrategy); await screenshareStage.join();
참가자 표시 및 제거
구독이 완료되면 STAGE_PARTICIPANT_STREAMS_ADDED
이벤트를 통해 StageStream
객체 배열을 받게 됩니다. 이벤트는 미디어 스트림을 표시할 때 도움이 되는 참가자 정보도 제공합니다.
stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => { const streamsToDisplay = streams; if (participant.isLocal) { // Ensure to exclude local audio streams, otherwise echo will occur streamsToDisplay = streams.filter(stream => stream.streamType === StreamType.VIDEO) } // Create or find video element already available in your application const videoEl = getParticipantVideoElement(participant.id); // Attach the participants streams videoEl.srcObject = new MediaStream(); streamsToDisplay.forEach(stream => videoEl.srcObject.addTrack(stream.mediaStreamTrack)); })
참가자가 게시를 중단하거나 스트림 구독이 취소되면 제거된 스트림과 함께 STAGE_PARTICIPANT_STREAMS_REMOVED
함수가 호출됩니다. 호스트 애플리케이션은 이를 신호로 사용하여 DOM에서 참가자의 비디오 스트림을 제거해야 합니다.
STAGE_PARTICIPANT_STREAMS_REMOVED
는 다음을 포함하여 스트림이 제거될 수 있는 모든 시나리오에서 호출됩니다.
-
원격 참가자가 게시를 중단합니다.
-
로컬 디바이스가 구독을 취소하거나 구독을
AUDIO_VIDEO
에서AUDIO_ONLY
로 변경합니다. -
원격 참가자가 스테이지를 나갑니다.
-
로컬 참가자가 스테이지를 나갑니다.
모든 시나리오에서 STAGE_PARTICIPANT_STREAMS_REMOVED
가 호출되므로 원격 또는 로컬 나가기 작업 중에 UI에서 참가자를 제거하는 사용자 지정 비즈니스 로직이 필요하지 않습니다.
미디어 스트림 음소거 및 음소거 해제
LocalStageStream
객체에는 스트림의 음소거 여부를 제어하는 setMuted
함수가 있습니다. 이 함수는 stageStreamsToPublish
전략 함수에서 반환되기 전이나 후에 스트림에서 호출할 수 있습니다.
중요: refreshStrategy
호출 이후 stageStreamsToPublish
에 의해 새 LocalStageStream
객체 인스턴스가 반환되면 새 스트림 객체의 음소거 상태가 스테이지에 적용됩니다. 새 LocalStageStream
인스턴스를 만들 때는 예상되는 음소거 상태가 유지되도록 주의해야 합니다.
원격 참가자 미디어 음소거 상태 모니터링
참가자가 비디오 또는 오디오의 음소거 상태를 변경하면 변경된 스트림 목록과 함께 STAGE_STREAM_MUTE_CHANGED
이벤트가 트리거됩니다. StageStream
의 isMuted
속성을 사용하여 UI를 적절히 업데이트합니다.
stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => { if (stream.streamType === 'video' && stream.isMuted) { // handle UI changes for video track getting muted } })
또한 StageParticiantInfo
stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => { if (participant.videoStopped || participant.audioMuted) { // handle UI changes for either video or audio } })
WebRTC 통계 가져오기
게시 스트림 또는 구독 스트림에 대한 최신 WebRTC 통계를 가져오려면 StageStream
에서 getStats
를 사용합니다. await을 통해 또는 promise 체인으로 통계를 검색할 수 있는 비동기 메서드입니다. 결과는 모든 표준 통계를 포함하는 딕셔너리인 RTCStatsReport
입니다.
try { const stats = await stream.getStats(); } catch (error) { // Unable to retrieve stats }
미디어 최적화
최상의 성능을 위해 다음 제약 조건으로 getUserMedia
및 getDisplayMedia
호출을 제한하는 것이 좋습니다.
const CONSTRAINTS = { video: { width: { ideal: 1280 }, // Note: flip width and height values if portrait is desired height: { ideal: 720 }, framerate: { ideal: 30 }, }, };
LocalStageStream
생성자에 전달된 추가 옵션을 통해 미디어를 더 제한할 수 있습니다.
const localStreamOptions = { minBitrate?: number; maxBitrate?: number; maxFramerate?: number; simulcast: { enabled: boolean } } const localStream = new LocalStageStream(track, localStreamOptions)
위의 코드에서
-
minBitrate
는 브라우저가 사용할 것으로 예상되는 최소 비트레이트를 설정합니다. 그러나 조금 복잡한 비디오 스트림은 인코더를 이 비트레이트보다 낮게 만들 수 있습니다. -
maxBitrate
는 브라우저가 이 스트림에 대해 초과하지 않을 것으로 예상되는 최대 비트레이트를 설정합니다. -
maxFramerate
는 브라우저가 이 스트림에 대해 초과하지 않을 것으로 예상되는 최대 프레임 속도를 설정합니다. -
simulcast
옵션은 Chromium 기반 브라우저에서만 사용할 수 있습니다. 이 옵션을 사용하면 스트림의 3개 렌더링 계층을 전송할 수 있습니다.-
이를 통해 서버는 네트워킹 제한에 따라 다른 참가자에게 전송할 변환을 선택할 수 있습니다.
-
simulcast
가maxBitrate
및/또는maxFramerate
값과 함께 지정되는 경우maxBitrate
가 내부 SDK의 두 번째로 높은 계층의 기본maxBitrate
값인 900kbps 아래로 내려가지 않는 한 이러한 값을 염두에 두고 가장 높은 변환 계층이 구성될 것으로 예상됩니다. -
maxBitrate
가 두 번째로 높은 계층의 기본값에 비해 너무 낮게 지정되면simulcast
가 비활성화됩니다. -
shouldPublishParticipant
가false
를 반환하고,refreshStrategy
를 직접적으로 호출하고,shouldPublishParticipant
가true
를 반환하게 하고,refreshStrategy
를 다시 직접적으로 호출하는 조합을 통해 미디어를 다시 게시하지 않고는simulcast
를 켜고 끌 수 없습니다.
-
참가자 특성 가져오기
CreateParticipantToken
엔드포인트 요청에서 특성을 지정하는 경우 StageParticipantInfo
속성에서 특성을 볼 수 있습니다.
stage.on(StageEvents.STAGE_PARTICIPANT_JOINED, (participant) => { console.log(`Participant ${participant.id} info:`, participant.attributes); })
네트워크 문제 처리
로컬 디바이스의 네트워크 연결이 끊어지면 SDK는 사용자 작업 없이 내부적으로 재연결을 시도합니다. SDK에서 재연결이 성공적이지 않아 사용자 작업이 필요한 경우도 있습니다.
스테이지의 상태는 대체로 STAGE_CONNECTION_STATE_CHANGED
이벤트를 통해 처리할 수 있습니다.
stage.on(StageEvents.STAGE_CONNECTION_STATE_CHANGED, (state) => { switch (state) { case StageConnectionState.DISCONNECTED: // handle disconnected UI break; case StageConnectionState.CONNECTING: // handle establishing connection UI break; case StageConnectionState.CONNECTED: // SDK is connected to the Stage break; case StageConnectionState.ERRORED: // SDK encountered an error and lost its connection to the stage. Wait for CONNECTED. break; })
일반적으로 SDK에서는 내부적으로 복구를 시도하므로 스테이지를 조인한 후 발생하는 오류 상태는 무시할 수 있습니다. SDK에서 ERRORED
상태를 보고하고 스테이지가 장기간(예: 30초 이상) CONNECTING
상태로 유지되는 경우 네트워크 연결이 해제될 수 있습니다.
IVS 채널로 스테이지 브로드캐스트
스테이지를 브로드캐스트하려면 별도의 IVSBroadcastClient
세션을 만든 다음 위에서 설명한 대로 SDK로 브로드캐스트하기 위한 일반적인 지침을 따릅니다. STAGE_PARTICIPANT_STREAMS_ADDED
를 통해 노출된 StageStream
목록을 사용하여 다음과 같이 브로드캐스트 스트림 구성에 적용할 수 있는 참가자 미디어 스트림을 검색할 수 있습니다.
// Setup client with preferred settings const broadcastClient = getIvsBroadcastClient(); stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => { streams.forEach(stream => { const inputStream = new MediaStream([stream.mediaStreamTrack]); switch (stream.streamType) { case StreamType.VIDEO: broadcastClient.addVideoInputDevice(inputStream, `video-${participant.id}`, { index: DESIRED_LAYER, width: MAX_WIDTH, height: MAX_HEIGHT }); break; case StreamType.AUDIO: broadcastClient.addAudioInputDevice(inputStream, `audio-${participant.id}`); break; } }) })
필요에 따라 스테이지를 합성하고 IVS 지연 시간이 짧은 채널로 브로드캐스트하여 더 많은 청중에게 다가갈 수 있습니다. IVS 지연 시간이 짧은 스트리밍 사용 설명서의 Amazon IVS 스트림에서 여러 호스트 활성화를 참조하세요.