IVS Web Broadcast SDK での発行とサブスクライブ | Real-Time Streaming - Amazon IVS

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 に伝える方法を提供します。shouldSubscribeToParticipantshouldPublishParticipantstageStreamsToPublish の 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 はその参加者に対して希望するサブスクリプションの状態についてホストアプリケーションに問い合わせます。使用できるオプションは NONEAUDIO_ONLY、および AUDIO_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 } }

スクリーン共有を公開する

アプリケーションでは、多くの場合、ユーザーの 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 イベントがトリガーされます。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 } })

また、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 統計を取得するには、StageStreamgetStats を使用してください。これは、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 ストリームでの複数のホストの有効化」を参照してください。