IVS Web Broadcast SDK를 사용한 게시 및 구독 | 실시간 스트리밍 - Amazon IVS

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, shouldPublishParticipantstageStreamsToPublish 함수를 구현해야 합니다. 모든 함수를 아래에서 설명합니다.

정의된 전략을 사용하려면 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_ONLYAUDIO_VIDEO입니다. 이 함수의 값을 반환할 때 호스트 애플리케이션은 게시 상태, 현재 구독 상태 또는 스테이지 연결 상태에 대해 걱정할 필요가 없습니다. AUDIO_VIDEO가 반환되는 경우 SDK는 구독 전에 원격 참가자가 게시할 때까지 기다리고, 프로세스 전반에 걸쳐 이벤트를 발생시켜 호스트 애플리케이션을 업데이트합니다.

다음은 샘플 구현입니다.

const strategy = { shouldSubscribeToParticipant: (participant) => { return SubscribeType.AUDIO_VIDEO; } // ... other strategy functions }

항상 모든 참가자가 서로 보기를 원하는 호스트 애플리케이션(예: 비디오 채팅 애플리케이션)을 위한 이 기능의 전체 구현입니다.

고급 구현도 가능합니다. 예를 들어, CreateParticipantToken으로 토큰을 생성할 때 애플리케이션에서 role 속성을 제공한다고 가정해 보겠습니다. 애플리케이션에서 StageParticipantInfoattributes 속성을 사용하여 서버에서 제공하는 속성을 기반으로 참가자를 선택적으로 구독합니다.

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 이벤트가 트리거됩니다. StageStreamisMuted 속성을 사용하여 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 }

미디어 최적화

최상의 성능을 위해 다음 제약 조건으로 getUserMediagetDisplayMedia 호출을 제한하는 것이 좋습니다.

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 스트림에서 여러 호스트 활성화를 참조하세요.