実装ガイド¶
概要¶
Sora Flutter SDK を利用して Sora に接続する方法について説明します。
前提¶
- Flutter がセットアップ済みであること
Flutter のセットアップ方法については Flutter ドキュメント をご確認ください。
- 利用する Sora 環境があり、接続できる状態であること
Sora については Sora ドキュメント をご確認ください。
- 接続先の Sora シグナリング URL が分かっていること
- 接続先のチャネル ID が決まっていること
1. パッケージを追加する¶
sora_sdk は pub.dev から追加して利用できます。
flutter pub add sora_sdk
または pubspec.yaml に直接追加します。
dependencies:
sora_sdk: ^2026.1.0
追加後に依存関係を取得してください。
flutter pub get
2. SDK を import する¶
接続処理を書く Dart ファイルの先頭に、次の import を追加します。
最初は lib/main.dart や、接続処理を実装するファイルに追加してください。
import 'package:sora_sdk/sora_sdk.dart';
3. 接続設定を作成する¶
role には接続時の送受信方式を指定します。
final config = SoraConnectionConfig(
signalingUrls: <String>[
'wss://sora.example.com/signaling',
],
channelId: 'example-channel',
role: SoraRole.sendrecv,
video: true,
audio: true,
);
signalingUrls は List<String> で複数指定できます。
Sora Flutter SDK は先頭から順番に WebSocket 接続を試みます。
接続を試みた URL が不正な場合は、その時点で接続処理を中止し、残りの URL は試しません。
不正な URL の例は次のとおりです。
not-a-url:URL の形式になっていないhttps://sora.example.com/signaling:ws/wss以外で始まるwss:///signaling:接続先のホスト名がない
URL の検証に成功し、候補 URL ごとの WebSocket 接続に失敗した場合、またはタイムアウトした場合は、次の URL を試します。
4. SoraConnection を生成する¶
final conn = await Sora.createConnection(config);
Sora.createConnection() の時点で、プラットフォーム側のクライアント初期化と WebRTC クライアントの生成が行われます。
5. 接続状態を確認する¶
接続状態の変化やリモートトラックの追加を確認したい場合は、接続前に conn.events でイベントを購読します。
イベント購読の例です。下記以外にも購読可能なイベントがあります。 イベントの一覧や発火タイミングの詳細は イベント をご確認ください。
final subscription = conn.events.listen((event) {
switch (event) {
case SoraConnectionStateChangedEvent(:final state):
// 接続状態の変化を確認する
print('connection state: $state');
if (state case SoraDisconnectedState(:final closeInfo)) {
if (closeInfo != null) {
// 切断時に close code / reason を取得できた場合
print('close: ${closeInfo.code} ${closeInfo.reason}');
}
}
case SoraConnectionErrorEvent(:final code, :final message):
// 接続エラーは別イベントで通知される
print('connection error: ${code ?? 'unknown'} ${message ?? ''}');
case SoraTrackEvent(:final track):
if (track.kind == 'video') {
// 受信した映像トラックの追加を確認する
print('remote video added: texture=${track.textureId}');
} else {
// 受信した音声トラックの追加を確認する
print('remote audio added: ${track.trackId}');
}
case SoraRemoveTrackEvent(:final track):
// 受信していたトラックの削除を確認する
print('remote track removed: ${track.trackId}');
default:
break;
}
});
StreamSubscription を使う場合はファイル先頭に import 'dart:async'; が必要です。
6. Sora に接続する¶
connect() に MediaStream を渡すかどうかは、送信設定で決まります。
roleがrecvonlyの場合はMediaStreamを渡さないroleがsendonly/sendrecvの場合audioとvideoが両方falseの場合はMediaStreamを渡さない- 上記以外(音声または映像を送信する)の場合は
MediaStreamを渡す
final stream = await MediaDevices.getUserMedia(
const GetUserMediaOptions(audio: true, video: true),
);
final localTextureId = await stream.getVideoTracks().first.textureId;
await conn.connect(stream);
接続が完了すると、 conn.events から SoraConnectionStateChangedEvent として SoraConnectedState が通知されます。
7. Flutter で映像をレンダリングする¶
ローカル映像は LocalVideoTrack.textureId 、リモート映像は conn.events の SoraTrackEvent (kind == 'video') で受け取る textureId を、それぞれ Texture ウィジェットに渡します。
映像レンダリング詳細については 映像を描画する をご確認ください。
class VideoView extends StatelessWidget {
const VideoView({super.key, required this.textureId});
final int textureId;
@override
Widget build(BuildContext context) {
return Texture(textureId: textureId);
}
}
複数のリモート映像が来る可能性があるため、リスト管理にしておくと扱いやすくなります。
8. Sora 接続の切断とリソース破棄¶
Sora から切断するには disconnect() を呼びます。
接続を再利用する場合は、切断後も同じインスタンスを使って再度 connect() を呼ぶことができます。
完全に使い終わったら、 disconnect() の後に dispose() を呼んでリソースを破棄してください。
await conn.disconnect();
await conn.dispose();
購読している StreamSubscription も破棄する必要があります。
getUserMedia() で作成した LocalMediaStream や track の解放方法は メディアデバイス をご確認ください。
for (final subscription in subscriptions) {
await subscription.cancel();
}
最小サンプル¶
Sora に recvonly で接続し、映像を受信したときに textureId をログ出力する最小サンプルです。
import 'dart:async';
import 'package:sora_sdk/sora_sdk.dart';
Future<void> startSora() async {
final conn = await Sora.createConnection(
SoraConnectionConfig(
signalingUrls: <String>[
'wss://sora.example.com/signaling',
],
channelId: 'example-channel',
role: SoraRole.recvonly,
),
);
final subscriptions = <StreamSubscription<dynamic>>[
conn.events.listen((event) {
switch (event) {
case SoraConnectionStateChangedEvent(:final state):
print('state=$state');
if (state case SoraDisconnectedState(:final closeInfo)) {
if (closeInfo != null) {
print('close=${closeInfo.code} ${closeInfo.reason}');
}
}
case SoraConnectionErrorEvent(:final code, :final message):
print('error=${code ?? 'unknown'} ${message ?? ''}');
case SoraTrackEvent(:final track):
if (track.kind == 'video') {
print('remote texture=${track.textureId}');
}
default:
break;
}
}),
];
try {
await conn.connect();
} catch (_) {
for (final subscription in subscriptions) {
await subscription.cancel();
}
await conn.dispose();
rethrow;
}
// 必要に応じて後で切断する
await conn.disconnect();
for (final subscription in subscriptions) {
await subscription.cancel();
}
await conn.dispose();
}
関連ページ¶
- 接続オプション: シグナリング設定
- イベント: イベント
- 映像描画 (
Texture): 映像を描画する - OS 別の権限や注意点: プラットフォーム と以下の OS 向けドキュメント
- よくある質問: FAQ
- カメラ / マイク: メディアデバイス
- 音声: 音声の送受信
- DataChannel の利用: DataChannel 経由のシグナリング と リアルタイムメッセージング機能
- RPC 機能: RPC 機能