IVS Android Broadcast SDK での発行とサブスクライブ | Real-Time Streaming
このドキュメントでは、IVS Real-Time Streaming Android Broadcast SDK を使用してステージに発行とサブスクライブを行うためのステップについて説明します。
概念
リアルタイム機能には、ステージ、ストラテジー、レンダラーという 3 つのコアコンセプトがあります。設計目標は、実際に動作する製品を構築するのに必要となるクライアント側ロジックの量を最小限に抑えることです。
ステージ
Stage
クラスは、ホストアプリケーションと SDK 間の主要な相互作用のポイントです。これはステージそのものを表し、ステージへの参加とステージからの退出に使用されます。ステージの作成と参加には、コントロールプレーンからの有効で有効期限内のトークン文字列 (token
として表示) が必要です。ステージへの参加と退出は簡単です。
Stage stage = new Stage(context, token, strategy); try { stage.join(); } catch (BroadcastException exception) { // handle join exception } stage.leave();
また、Stage
クラスには StageRenderer
をアタッチすることもできます。
stage.addRenderer(renderer); // multiple renderers can be added
方針
Stage.Strategy
インターフェースは、ホストアプリケーションがステージの望ましい状態を SDK に伝える方法を提供します。shouldSubscribeToParticipant
、shouldPublishFromParticipant
、stageStreamsToPublishForParticipant
の 3 つの関数を実装する必要があります。以下で、すべて説明します。
参加者へのサブスクライブ
Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);
リモート参加者がステージに参加すると、SDK はその参加者に対して希望するサブスクリプションの状態についてホストアプリケーションに問い合わせます。使用できるオプションは NONE
、AUDIO_ONLY
、および AUDIO_VIDEO
です。この関数の値を返す場合、ホストアプリケーションは公開の状態、現在のサブスクリプションの状態、またはステージ接続の状態を考慮する必要はありません。AUDIO_VIDEO
が返された場合、SDK はリモート参加者が公開するまで待ってからサブスクライブし、プロセス全体でレンダラーを通じてホストアプリケーションを更新します。
次に示すのは実装の例です。
@Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return Stage.SubscribeType.AUDIO_VIDEO; }
これは、ビデオチャットアプリケーションなど、すべての参加者が互いに常に可視化されているホストアプリケーション向けのの完全な実装です。
より高度な実装も可能です。ParticipantInfo
の userInfo
プロパティを使用して、サーバーが提供する属性に基づいて、参加者に対して選択的にサブスクライブできます。
@Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { switch(participantInfo.userInfo.get(“role”)) { case “moderator”: return Stage.SubscribeType.NONE; case “guest”: return Stage.SubscribeType.AUDIO_VIDEO; default: return Stage.SubscribeType.NONE; } }
これを使用すると、モデレーターは、自身は視聴の対象とならずに、すべてのゲストを監視できるステージを作ることができます。ホストアプリケーションでは、追加のビジネスロジックを使用して、モデレーター同士は見えるようにしつつ、ゲストには見えないようにすることができます。
参加者へのサブスクライブの設定
SubscribeConfiguration subscribeConfigurationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);
リモート参加者がサブスクライブしている場合 (「参加者へのサブスクライブ」を参照)、SDK はホストアプリケーションにその参加者のカスタムサブスクライブ設定についてクエリします。この設定はオプションであり、ホストアプリケーションがサブスクライバーの動作の特定の側面を制御できるようにします。設定できる内容の詳細については、SDK リファレンスドキュメントの「SubscribeConfiguration
次に示すのは実装の例です。
@Override public SubscribeConfiguration subscribeConfigrationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { SubscribeConfiguration config = new SubscribeConfiguration(); config.jitterBuffer.setMinDelay(JitterBufferConfiguration.JitterBufferDelay.MEDIUM()); return config; }
この実装では、サブスクライブしたすべての参加者のジッターバッファ最小遅延を MEDIUM
のプリセットに更新します。
shouldSubscribeToParticipant
を使用した、より高度な実装も可能です。指定された ParticipantInfo
を使用して、特定の参加者のサブスクライブ設定を選択的に更新できます。
デフォルトの動作を使用することをお勧めします。カスタム設定は、特定の動作を変更したい場合にのみ指定します。
公開
boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);
ステージに接続すると、SDK はホストアプリケーションにクエリを実行し、特定の参加者を公開とすべきかどうかを確認します。これは、提供されたトークンに基づいて公開する権限を持つローカル参加者においてのみ呼び出されます。
次に示すのは実装の例です。
@Override boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return true; }
これは、ユーザーは常に公開状態としたい標準的なビデオチャットアプリケーション用です。オーディオとビデオをミュートまたはミュート解除して、すぐに不可視または可視にできます。(公開/非公開も使用できますが、この方法では大幅に遅くなります。可視性を頻繁に変更したいユースケースには、ミュート/ミュート解除が適しています。)
公開するストリームの選択
@Override List<LocalStageStream> stageStreamsToPublishForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); }
公開時には、これを使用して公開するオーディオストリームとビデオストリームが決定されます。これについては、後ほど「メディアストリームの公開」で詳しく説明します。
ストラテジーの更新
このストラテジーは動的であることを意図しており、上記の関数のいずれかから返される値はいつでも変更できます。たとえば、ホストアプリケーションがエンドユーザーがボタンをタップするまで公開したくない場合、shouldPublishFromParticipant
(hasUserTappedPublishButton
など) から変数を返すことができます。その変数がエンドユーザーの相互作用に基づいて変更されたら、stage.refreshStrategy()
を呼び出して、変更されたもののみを適用して、最新の値のストラテジーを照会する必要があることを SDK に通知します。SDK は、shouldPublishFromParticipant
値が変更されたことを検出すると、公開プロセスを開始します。SDK クエリとすべての関数が以前と同じ値を返す場合、refreshStrategy
呼び出しはステージに変更を加えません。
shouldSubscribeToParticipant
の戻り値が AUDIO_VIDEO
から AUDIO_ONLY
に変更され、以前にビデオストリームが存在していた場合は、戻り値が変更されたすべての参加者のビデオストリームが削除されます。
通常、ホストアプリケーションは、適切に管理するために必要なすべての状態について考慮する必要はありません。ステージは以前のストラテジーと現在のストラテジーの違いを最も効率的に適用するストラテジーを使用します。このため、stage.refreshStrategy()
の呼び出しはストラテジーが変わらない限り何もしないため、低コストなオペレーションとみなすことができます。
レンダラー
StageRenderer
インターフェースはステージの状態をホストアプリケーションに伝えます。ホストアプリケーションの UI の更新は、通常、レンダラーが提供するイベントだけで行うことができます。次の関数では、以下のような結果が生成されます。
void onParticipantJoined(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); void onParticipantLeft(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); void onParticipantPublishStateChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull Stage.PublishState publishState); void onParticipantSubscribeStateChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull Stage.SubscribeState subscribeState); void onStreamsAdded(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onStreamsRemoved(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onStreamsMutedChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onError(@NonNull BroadcastException exception); void onConnectionStateChanged(@NonNull Stage stage, @NonNull Stage.ConnectionState state, @Nullable BroadcastException exception);
これらのメソッドのほとんどには、対応する Stage
および ParticipantInfo
が用意されています。
レンダラーから提供された情報がストラテジーの戻り値に影響することは想定されていません。たとえば、shouldSubscribeToParticipant
の戻り値は、onParticipantPublishStateChanged
が呼び出されても変化しない想定です。ホストアプリケーションが特定の参加者をサブスクライブする場合は、その参加者の公開状態に関係なく、目的のサブスクリプションタイプを返す必要があります。SDK は、ステージの状態に基づいて、望ましいストラテジーの状態が適切なタイミングで実行されるようにする役目を担います。
StageRenderer
は次のステージクラスにアタッチできます。
stage.addRenderer(renderer); // multiple renderers can be added
公開参加者のみが onParticipantJoined
をトリガーし、参加者が公開を停止するか、ステージ セッションを終了すると、onParticipantLeft
がトリガーされることに注意してください。
メディアストリームを公開する
内蔵マイクやカメラなどのローカルデバイスは、DeviceDiscovery
を介して検出されます。以下は、前面カメラとデフォルトのマイクを選択し、それらを LocalStageStreams
として SDK が公開できるように返す例です。
DeviceDiscovery deviceDiscovery = new DeviceDiscovery(context); List<Device> devices = deviceDiscovery.listLocalDevices(); List<LocalStageStream> publishStreams = new ArrayList<LocalStageStream>(); Device frontCamera = null; Device microphone = null; // Create streams using the front camera, first microphone for (Device device : devices) { Device.Descriptor descriptor = device.getDescriptor(); if (!frontCamera && descriptor.type == Device.Descriptor.DeviceType.Camera && descriptor.position = Device.Descriptor.Position.FRONT) { front Camera = device; } if (!microphone && descriptor.type == Device.Descriptor.DeviceType.Microphone) { microphone = device; } } ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera); AudioLocalStageStream microphoneStream = new AudioLocalStageStream(microphoneDevice); publishStreams.add(cameraStream); publishStreams.add(microphoneStream); // Provide the streams in Stage.Strategy @Override @NonNull List<LocalStageStream> stageStreamsToPublishForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return publishStreams; }
参加者を表示、削除する
サブスクライブが完了すると、レンダラーの StageStream
関数を介して onStreamsAdded
オブジェクトの配列を受け取ります。ImageStageStream
からのプレビューは次の場所から取得できます。
ImagePreviewView preview = ((ImageStageStream)stream).getPreview(); // Add the view to your view hierarchy LinearLayout previewHolder = findViewById(R.id.previewHolder); preview.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); previewHolder.addView(preview);
オーディオレベルの統計情報は、以下の AudioStageStream
から取得できます。
((AudioStageStream)stream).setStatsCallback((peak, rms) -> { // handle statistics });
参加者が公開を停止するか、サブスクライブを解除すると、削除されたストリームを使用して onStreamsRemoved
関数が呼び出されます。ホストアプリケーションは、これを通知として使用して、参加者のビデオストリームをビュー階層から削除する必要があります。
onStreamsRemoved
は、以下を含む、ストリームが削除される可能性のあるすべてのシナリオで呼び出されます。
-
リモート参加者は公開を停止します。
-
ローカルデバイスがサブスクリプションを解除するか、サブスクリプションを
AUDIO_VIDEO
からAUDIO_ONLY
に変更します。 -
リモート参加者がステージを退出します。
-
ローカルの参加者がステージを退出します。
onStreamsRemoved
はすべてのシナリオで呼び出されるため、リモートまたはローカルの離脱操作中、 UI から参加者を削除するためのカスタムのビジネスロジックは必要ありません。
メディアストリームをミュート、ミュート解除する
LocalStageStream
オブジェクトには、ストリームをミュートするかどうかを制御する setMuted
関数があります。この関数は、streamsToPublishForParticipant
ストラテジー関数から返される前または後にストリームで呼び出すことができます。
重要: refreshStrategy
を呼び出した後に新しい LocalStageStream
オブジェクトインスタンスが streamsToPublishForParticipant
によって返された場合、新しいストリームオブジェクトのミュート状態がステージに適用されます。新しい LocalStageStream
インスタンスを作成するときは、想定どおりのミュート状態を維持するように注意してください。
リモート参加者のメディアミュート状態の監視
参加者がビデオまたはオーディオストリームのミュート状態を変更すると、変更されたストリームのリストとともにレンダラー onStreamMutedChanged
関数が呼び出されます。StageStream
に getMuted
メソッドを使用して、適宜 UI を更新します。
@Override void onStreamsMutedChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams) { for (StageStream stream : streams) { boolean muted = stream.getMuted(); // handle UI changes } }
WebRTC 統計を取得する
公開ストリームまたはサブスクライブ中のストリームの最新の WebRTC 統計情報を取得するには、StageStream
に requestRTCStats
を使用してください。収集が完了すると、StageStream
に設定できる StageStream.Listener
から統計を受け取ります。
stream.requestRTCStats(); @Override void onRTCStats(Map<String, Map<String, String>> statsMap) { for (Map.Entry<String, Map<String, string>> stat : statsMap.entrySet()) { for(Map.Entry<String, String> member : stat.getValue().entrySet()) { Log.i(TAG, stat.getKey() + “ has member “ + member.getKey() + “ with value “ + member.getValue()); } } }
参加者属性を取得
CreateParticipantToken
エンドポイントリクエストで属性を指定すると、次のように ParticipantInfo
プロパティにその属性が表示されます。
@Override void onParticipantJoined(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { for (Map.Entry<String, String> entry : participantInfo.userInfo.entrySet()) { Log.i(TAG, “attribute: “ + entry.getKey() + “ = “ + entry.getValue()); } }
セッションをバックグラウンドで続行
アプリがバックグラウンドに入ると、公開を停止するか、他のリモート参加者の音声のみをサブスクライブすることができます。そのためには、Strategy
実装を更新して公開を停止し、AUDIO_ONLY
(または NONE
。該当する場合) をサブスクライブします。
// Local variables before going into the background boolean shouldPublish = true; Stage.SubscribeType subscribeType = Stage.SubscribeType.AUDIO_VIDEO; // Stage.Strategy implementation @Override boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return shouldPublish; } @Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return subscribeType; } // In our Activity, modify desired publish/subscribe when we go to background, then call refreshStrategy to update the stage @Override void onStop() { super.onStop(); shouldPublish = false; subscribeTpye = Stage.SubscribeType.AUDIO_ONLY; stage.refreshStrategy(); }
サイマルキャストによるレイヤードエンコーディングの有効化/無効化
メディアストリームを公開する際、SDK は高品質と低品質のビデオストリームを伝送するため、リモートの参加者はダウンリンクの帯域幅が限られていてもストリームをサブスクライブできます。サイマルキャストによるレイヤードエンコーディングはデフォルトでオンになっています。次の StageVideoConfiguration.Simulcast
クラスを使用すると無効にできます。
// Disable Simulcast StageVideoConfiguration config = new StageVideoConfiguration(); config.simulcast.setEnabled(false); ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera, config); // Other Stage implementation code
ビデオ設定の制限
SDK は、StageVideoConfiguration.setSize(BroadcastConfiguration.Vec2 size)
を使用したポートレートモードまたはランドスケープモードの強制をサポートしていません。縦の向きでは、小さい方の寸法が幅として使用され、横の向きでは高さとして使用されます。つまり、次の 2 つの setSize
への呼び出しが動画の設定に同じ効果をもたらすということです。
StageVideo Configuration config = new StageVideo Configuration(); config.setSize(BroadcastConfiguration.Vec2(720f, 1280f); config.setSize(BroadcastConfiguration.Vec2(1280f, 720f);
ネットワーク問題の処理
ローカルデバイスのネットワーク接続が失われると、SDK はユーザーアクションなしで内部で再接続を試みます。場合によっては、SDK が正常に動作せず、ユーザーアクションが必要なる可能性があります。ネットワーク接続の切断に関連する主なエラーは 2 つあります。
-
エラーコード 1400、メッセージ:「不明なネットワークエラーにより PeerConnection が失われました」
-
エラーコード 1300、メッセージ:「再試行の試行回数制限に達しました」
最初のエラーを受け取って 2 番目のエラーを受け取らない場合、SDK はまだステージに接続されており、自動的に接続を再確立しようとします。安全対策として、ストラテジーメソッドの戻り値を変更せずに refreshStrategy
を呼び出すと、手動で再接続を試みることができます。
2 番目のエラーを受け取った場合、SDK の再接続は失敗し、ローカルデバイスはステージに接続されていません。この場合は、ネットワーク接続が再確立された後に join
を呼び出してステージに再び参加してみてください。
一般に、ステージに正常に参加した後にエラーが発生した場合は、SDK が接続を再確立できなかったことを示します。新しい Stage
オブジェクトを作成し、ネットワークの状態が向上したら参加してみます。
Bluetooth マイクの使用
Bluetooth マイクデバイスを使用して公開するには、Bluetooth SCO 接続を開始する必要があります。
Bluetooth.startBluetoothSco(context); // Now bluetooth microphones can be used … // Must also stop bluetooth SCO Bluetooth.stopBluetoothSco(context);