IVS Web Broadcast SDK での発行とサブスクライブ | Real-Time Streaming
このドキュメントでは、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();
方針
StageStrategy
インターフェースは、ホストアプリケーションがステージの望ましい状態を SDK に伝える方法を提供します。shouldSubscribeToParticipant
、shouldPublishParticipant
、stageStreamsToPublish
の 3 つの関数を実装する必要があります。以下で、すべて説明します。
定義済みのストラテジーを使用するには、それを Stage
コンストラクターに渡します。以下は、参加者の Web カメラをステージに公開し、すべての参加者にサブスクライブするというストラテジーを使用したアプリケーションの完全な例です。必要な各ストラテジー機能の目的は、以下のセクションで説明します。
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 } }
スクリーン共有を公開する
アプリケーションでは、多くの場合、ユーザーの Web カメラに加えてスクリーン共有を公開する必要があります。スクリーン共有を公開するには、特にスクリーン共有のメディアを公開するためには、ステージ用の追加のトークンを作成する必要があります。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
を呼び出した後に新しい LocalStageStream
オブジェクトインスタンスが stageStreamsToPublish
によって返された場合、新しいストリームオブジェクトのミュート状態がステージに適用されます。新しい 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 } })
また、StageParticipantInfo
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 の 2 番目に高レイヤーのデフォルトmaxBitrate
値である 900 kbps を下回らない限り、これらの値を念頭に置いて最上位のレンディションレイヤーが設定されることが想定されています。 -
maxBitrate
が 2 番目に高いレイヤーのデフォルト値と比較して低すぎるように指定された場合は、simulcast
は無効になります。 -
simulcast
のオンとオフを切り替えるには、shouldPublishParticipant
がfalse
を返し、refreshStrategy
を呼び出し、shouldPublishParticipant
がtrue
を返し、refreshStrategy
をもう一度呼ぶ組み合わせにより、メディアを再パブリッシュする必要があります。
-
参加者属性を取得
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 ストリームでの複数のホストの有効化」を参照してください。