映像を描画する¶
概要¶
Sora Flutter SDK での映像レンダリングの方法について説明します。
描画までの流れは次のとおりです。
- 映像の
textureIdを取得する- ローカル映像は
LocalVideoTrack.textureIdからtextureIdを取得する - リモート映像は
SoraTrackEventで受け取ったRemoteMediaStreamTrack.textureIdを使う
- ローカル映像は
- 取得した
textureIdを Flutter のTextureに渡して描画する
SDK は Texture を直接使う方法に加えて、 SoraLocalVideoWidget / SoraRemoteVideoWidget を提供しています。
これらは textureId が未確定の間のプレースホルダー表示、親領域へのフィット、左右反転をまとめて扱えます。
イベント全体の一覧や SoraConnection.events の使い方は イベント をご確認ください。
TextureId を取得する¶
ローカル映像¶
ローカル映像を表示するには、まず送信用の MediaStream を用意します。 MediaStream の作成方法は メディアデバイス をご確認ください。
ローカル映像の textureId は LocalVideoTrack.textureId ( Future<int> ) で取得します。
final stream = await MediaDevices.getUserMedia(
const GetUserMediaOptions(audio: true, video: true),
);
final videoTrack = stream.getVideoTracks().first;
final textureId = await videoTrack.textureId;
createExternalVideoTrack() で生成した外部映像入力用の LocalVideoTrack で textureId を取得すると StateError が送出されます。詳細は 外部映像入力を利用する をご確認ください。
SoraConnection.localVideo ( Stream<SoraLocalVideoHandle> ) からも textureId を取得できます。
カメラのトラックが接続に設定され、キャプチャが開始されたタイミングで SoraLocalVideoHandle が emit されます。 textureId は常に有効な非負値です。
conn.localVideo.listen((handle) {
setState(() {
localTextureId = handle.textureId;
});
});
リモート映像¶
受信したリモート映像から textureId を取得する方法を説明します。
リモート映像を受信するまでの基本的な流れは 実装ガイド をご確認ください。また、 conn.events によりイベント購読しているものとします。
リモート映像は conn.events の SoraTrackEvent で受け取ります。トラックは RemoteMediaStreamTrack として通知され、 kind == 'video' の場合のみ textureId が non-null になります。
final remoteTextureIds = <String, int>{};
conn.events.listen((event) {
switch (event) {
case SoraTrackEvent(:final track):
final textureId = track.textureId;
if (track.kind == 'video' && textureId != null) {
setState(() {
remoteTextureIds[track.trackId] = textureId;
});
}
default:
break;
}
});
trackId は、複数リモートトラックを識別する際に利用できます。
RemoteMediaStreamTrack の型や SoraTrackEvent の通知タイミングの詳細は イベント を参照してください。
Texture によるレンダリング¶
textureId を Flutter の Texture ウィジェットに渡してレンダリングします。リモートトラックの textureId は int? なので、 kind == 'video' を確認してから利用します。
Widget buildVideo(int textureId) {
return Texture(textureId: textureId);
}
final localVideo = buildVideo(textureId);
final remoteVideo = buildVideo(track.textureId!);
複数のリモート映像を並べて表示する場合は、受信したトラックのリストを GridView や Wrap などに流し込みます。
Widget buildRemoteVideos(Map<String, int> remoteTextureIds) {
return GridView.count(
crossAxisCount: 2,
children: remoteTextureIds.values
.map((textureId) => Texture(textureId: textureId))
.toList(),
);
}
削除されたリモートトラックの映像を取り除く¶
リモート側の切断等により SoraRemoveTrackEvent が通知された映像トラックの textureId は描画に使えなくなるため、対応する Texture をツリーから取り除きます。
// trackId をキーに textureId を管理するマップです
final remoteTextureIds = <String, int>{};
conn.events.listen((event) {
switch (event) {
case SoraTrackEvent(:final track):
final textureId = track.textureId;
if (track.kind == 'video' && textureId != null) {
setState(() {
remoteTextureIds[track.trackId] = textureId;
});
}
// SoraRemoveTrackEvent で該当する trackId をリストから削除します
case SoraRemoveTrackEvent(:final track):
if (track.kind == 'video') {
setState(() {
// trackId をキーに管理すると、SoraRemoveTrackEvent を受けたタイミングで該当する Texture を自然に表示から外せます
remoteTextureIds.remove(track.trackId);
});
}
default:
break;
}
});
Widget によるレンダリング¶
SoraLocalVideoWidget と SoraRemoteVideoWidget を使うと、 textureId の管理と Texture の組み立てを Widget に任せられます。
SoraRemoteVideoWidget¶
SoraTrackEvent で受け取った RemoteMediaStreamTrack をそのまま渡します。 track.textureId が null の間は placeholder が表示されます。
Widget buildRemoteVideo(RemoteMediaStreamTrack track) {
return SoraRemoteVideoWidget(track: track);
}
フィールド |
説明 |
|---|---|
|
表示するリモート映像トラック ( |
|
親領域へのフィット方法。デフォルトは |
|
水平反転するかどうか。デフォルトは |
|
|
SoraLocalVideoWidget¶
textureId を渡します。 null または負値のときは placeholder が表示されます。 0 は有効な値として扱われます。
Widget buildLocalVideo(int? textureId) {
return SoraLocalVideoWidget(textureId: textureId, mirror: true);
}
fit / mirror / placeholder は SoraRemoteVideoWidget と同じです。