イベント

概要

Sora Flutter SDK のイベントは、主に Stream<SoraConnectionEvent> と Stream<SoraDebugEvent> で提供されます。

Stream<SoraConnectionEvent> では、接続状態、リモートトラックの追加・削除、シグナリング、DataChannel (通知・メッセージング・RPC 等)、タイムアウトを購読できます。

Stream<SoraDebugEvent> では、構造化されたログとタイムラインを購読できます。

統合イベント Stream

Stream<SoraConnectionEvent> は統合イベント Stream です。イベント型ごとに switch で分岐して処理します。

conn.events.listen((event) {
  switch (event) {
    case SoraConnectionStateChangedEvent(:final state):
      print('state: $state');
    case SoraConnectionErrorEvent(:final code, :final message):
      print('error: ${code ?? 'unknown'} ${message ?? ''}');
    case SoraTrackEvent(:final track):
      print('track added: ${track.trackId}');
    case SoraRemoveTrackEvent(:final track):
      print('track removed: ${track.trackId}');
    case SoraNotifyEvent(:final message):
      print('notify: $message');
    case SoraPushEvent(:final message):
      print('push: $message');
    case SoraSwitchedEvent(:final message):
      print('switched: $message');
    case SoraSignalingMessageEvent(:final event):
      print('signaling: ${event.transportType} ${event.direction}');
    case SoraDataChannelOpenEvent(:final event):
      print('datachannel open: ${event.label}');
    case SoraDataChannelMessageEvent(:final message):
      print('message: ${message.label}');
    case SoraTimeoutEvent():
      print('timeout');
  }
});

接続状態の変更

SoraConnectionStateChangedEvent は接続状態が変わったときに通知されます。

conn.events.listen((event) {
  if (event case SoraConnectionStateChangedEvent(:final state)) {
    switch (state) {
      case SoraConnectingState():
        print('connecting');
      case SoraConnectedState():
        print('connected');
      case SoraDisconnectedState(:final closeInfo):
        print('disconnected');
        if (closeInfo != null) {
          print('close code: ${closeInfo.code}');
          print('close reason: ${closeInfo.reason}');
        }
    }
  }
});

通知される状態は次の 3 種類です。

  • SoraConnectingState
  • SoraConnectedState
  • SoraDisconnectedState

SoraConnectingState

接続処理の開始後、シグナリング接続の確立に入ったタイミングで通知されます。

conn.events.listen((event) {
  if (event case SoraConnectionStateChangedEvent(state: SoraConnectingState())) {
    print('connecting');
  }
});

SoraConnectedState

Sora との PeerConnection が接続済になったタイミングで通知されます。

conn.events.listen((event) {
  if (event case SoraConnectionStateChangedEvent(state: SoraConnectedState())) {
    print('connected');
  }
});

SoraDisconnectedState

Sora から切断したタイミングで通知されます。

closeInfo を持つ場合は、切断時の code と reason を参照できます。

conn.events.listen((event) {
  if (event case SoraConnectionStateChangedEvent(
    state: SoraDisconnectedState(:final closeInfo),
  )) {
    print('disconnected');
    if (closeInfo != null) {
      print('close code: ${closeInfo.code}');
      print('close reason: ${closeInfo.reason}');
    }
  }
});

接続エラー

接続エラーは SoraConnectionErrorEvent で通知されます。

conn.events.listen((event) {
  if (event case SoraConnectionErrorEvent(:final code, :final message)) {
    print('connection error: ${code ?? 'unknown'} ${message ?? ''}');
  }
});

code にはエラー種別が、 message にはエラーの詳細が含まれます。 message は null になる可能性があるため、利用の際は null チェックを行ってください。 retriable にはリトライ可能かどうかが bool? で入ります。 details には SoraConnectionErrorDetails? として attempts (リトライ試行回数) と platformError (プラットフォーム固有のエラー情報) が入る場合があります。

SoraConnectionErrorEvent.code に入る既知のエラーコードは、公開 API の SoraErrorCode が定義する次の 18 個です。

  • SoraErrorCode.eventChannelError: イベントチャネルのエラー
    • エラーコード: event_channel_error
  • SoraErrorCode.websocketError: WebSocket のエラー
    • エラーコード: websocket_error
  • SoraErrorCode.connectionTimeout: WebRTC 接続確立のタイムアウト ( タイムアウト機能 )
    • エラーコード: connection_timeout
  • SoraErrorCode.disconnectTimeout: 切断処理のタイムアウト ( タイムアウト機能 )
    • エラーコード: disconnect_timeout
  • SoraErrorCode.signalingCandidateTimeout: シグナリング候補 URL の接続タイムアウト ( タイムアウト機能 )
    • エラーコード: signaling_candidate_timeout
  • SoraErrorCode.observerBridgeCreationFailed: observer bridge の生成失敗
    • エラーコード: observer_bridge_creation_failed
  • SoraErrorCode.observerBridgeObserverCreationFailed: PeerConnectionObserver の生成失敗
    • エラーコード: observer_bridge_observer_creation_failed
  • SoraErrorCode.audioInputInitializationFailed: iOS の音声入力初期化失敗
    • エラーコード: audio_input_initialization_failed
  • SoraErrorCode.cameraOpenError: カメラのオープン失敗
    • エラーコード: camera_open_error
  • SoraErrorCode.offerInvalid: Sora から受け取った offer の SDP が null 、または PeerConnection を用意できない
    • エラーコード: offer_invalid
  • SoraErrorCode.reofferInvalid: Sora から受け取った re-offer の SDP が null 、または再利用する PeerConnection がない
    • エラーコード: reoffer_invalid
  • SoraErrorCode.setRemoteDescriptionFailed: リモート SDP の設定に失敗
    • エラーコード: set_remote_description_failed
  • SoraErrorCode.createAnswerFailed: answer の生成に失敗
    • エラーコード: create_answer_failed
  • SoraErrorCode.setLocalDescriptionFailed: ローカル SDP の設定に失敗
    • エラーコード: set_local_description_failed
  • SoraErrorCode.createPeerConnectionFailed: PeerConnection の生成に失敗
    • エラーコード: create_peer_connection_failed
  • SoraErrorCode.addAudioTrackFailed: ローカルの音声トラックの追加に失敗
    • エラーコード: add_audio_track_failed
  • SoraErrorCode.addVideoTrackFailed: ローカルの映像トラックの追加に失敗
    • エラーコード: add_video_track_failed
  • SoraErrorCode.unexpectedNativeEvent: ネイティブから想定外のイベントが届いた
    • エラーコード: unexpected_native_event

SoraConnectionErrorEvent.code の型は String? です。イベントによっては null になる可能性があります。 この一覧は SDK が定義する既知の定数一覧であり、ネイティブの接続状態やプラットフォーム側のイベントから渡される実行時の文字列が、この 18 個に限定されることを示すものではありません。

切断理由コード

SoraDisconnectReason は、 disconnect メッセージの理由コードと、 SDK が切断を分類するためにネイティブから受け取る理由タグです。 SoraConnectionErrorEvent.code のエラーコード一覧には含まれません。

定数

値

SoraDisconnectReason.noError

NO-ERROR

SoraDisconnectReason.websocketOnClose

WEBSOCKET-ONCLOSE

SoraDisconnectReason.websocketOnError

WEBSOCKET-ONERROR

SoraDisconnectReason.serverDisconnect

server_disconnect

SoraDisconnectReason.peerConnectionFailed

peer_connection_failed

SoraDisconnectReason.peerConnectionClosed

peer_connection_closed

track

リモートトラックが追加されたときは SoraTrackEvent が通知されます。

トラックは RemoteMediaStreamTrack として渡され、 kind で音声か映像かを判別できます。

  • kind == 'audio': 音声トラック (textureId は null)
  • kind == 'video': 映像トラック (textureId は non-null)
conn.events.listen((event) {
  if (event case SoraTrackEvent(:final track)) {
    if (track.kind == 'audio') {
      print('audio track added: ${track.trackId}');
    } else {
      print('video track added: ${track.trackId}');
      print('textureId: ${track.textureId}');
    }
  }
});

RemoteMediaStreamTrack のフィールドは以下の通りです。

  • trackId: トラック ID
  • kind: audio または video
  • connectionId: Sora の connection_id
  • textureId: kind == 'video' の場合のみ値を取得できます

映像トラックの追加イベントでは、 textureId をそのまま Texture に渡せます。

if (event case SoraTrackEvent(:final track) when track.kind == 'video') {
  return Texture(textureId: track.textureId!);
}

removetrack

リモートトラックが削除されたときは SoraRemoveTrackEvent が通知されます。

conn.events.listen((event) {
  if (event case SoraRemoveTrackEvent(:final track)) {
    print('track removed: ${track.trackId}');
  }
});

削除イベントでも追加イベント時と同様に RemoteMediaStreamTrack 型となります。UI 側は通常 trackId で対象を特定して除去します。

接続単位でトラックを扱いたい場合は、 SoraConnection.remoteMediaStreams も利用できます。 connectionId をキーに RemoteMediaStream が入り、 audioTrack / videoTrack にそれぞれのトラックが格納されます。

notify

Sora からシグナリング通知を受け取ったときは SoraNotifyEvent が通知されます。

Sora のシグナリング通知の詳細は Sora のドキュメント シグナリング通知 をご確認ください。

conn.events.listen((event) {
  if (event case SoraNotifyEvent(:final message)) {
    print('notify: $message');
  }
});

connection.created などの通知をここで受け取ることができます。

push

Sora からのプッシュ通知を受け取ったときは SoraPushEvent が通知されます。

conn.events.listen((event) {
  if (event case SoraPushEvent(:final message)) {
    print('push: $message');
  }
});

switched

シグナリングが WebSocket から DataChannel に切り替わったときは SoraSwitchedEvent が通知されます。

conn.events.listen((event) {
  if (event case SoraSwitchedEvent(:final message)) {
    print('switched: $message');
  }
});

timeout

PeerConnection の接続確立前に、WebSocket が reason TIMEOUT で閉じられた場合に SoraTimeoutEvent が通知されます。切断処理中は通知されません。

conn.events.listen((event) {
  if (event case SoraTimeoutEvent()) {
    print('timeout');
  }
});

message

DataChannel を利用したリアルタイムメッセージング機能でメッセージを受信したときは SoraDataChannelMessageEvent が通知されます。

conn.events.listen((event) {
  if (event case SoraDataChannelMessageEvent(:final message)) {
    print('label: ${message.label}');
    print('data: ${message.data}');
  }
});

datachannel

DataChannel が利用可能になったときは SoraDataChannelOpenEvent が通知されます。

conn.events.listen((event) {
  if (event case SoraDataChannelOpenEvent(:final event)) {
    print('label: ${event.label}');
    print('direction: ${event.direction}');
  }
});

メッセージング機能を使う場合は、このイベントを送信可能の目安として扱えます。

signaling

シグナリングメッセージの送受信を確認したいときは SoraSignalingMessageEvent を利用します。

conn.events.listen((event) {
  if (event case SoraSignalingMessageEvent(:final event)) {
    print('${event.transportType} ${event.direction}');
    print(event.data);
  }
});

transportType は websocket または datachannel です。 direction は sent または received です。

data には、シグナリングメッセージの内容がそのまま入ります。 offer / answer / candidate などの内容を確認することができます。

デバッグイベント Stream

conn.debugEvents はデバッグイベント専用 Stream です。構造化された log と timeline を受け取りたい場合に利用します。

conn.debugEvents.listen((event) {
  switch (event) {
    case SoraLogDebugEvent(:final event):
      print('${event.title}: ${event.message}');
    case SoraTimelineDebugEvent(:final event):
      print('${event.logType.value} ${event.type}');
      print(event.data);
  }
});

log

SDK の構造化ログは SoraLogDebugEvent で受け取ることができます。

conn.debugEvents.listen((event) {
  if (event case SoraLogDebugEvent(:final event)) {
    print('${event.title}: ${event.message}');
  }
});

timeline

タイムラインの構造化ログは SoraTimelineDebugEvent で受け取ることができます。

conn.debugEvents.listen((event) {
  if (event case SoraTimelineDebugEvent(:final event)) {
    print('${event.logType.value} ${event.type}');
    print(event.data);
  }
});

このイベントに含まれる SoraTimelineEvent では、主に次の情報を参照します。

  • type: イベント種別
  • logType: websocket / datachannel / peerconnection / sora
  • data: イベントの詳細データ
  • dataChannelId: DataChannel に紐づく場合の ID
  • dataChannelLabel: DataChannel に紐づく場合の label

接続確立後の識別子

offer を受信すると、次の getter がサーバーから割り当てられた値で更新されます。

  • connectionId
  • serverClientId
  • bundleId
  • sessionId

接続前や offer 未到達のときは null です。RPC のパラメータ組み立てなどで利用できます。

イベント購読の終了

conn.events や conn.debugEvents を購読した場合は、 conn.dispose() の前に購読を解除してください。

late final StreamSubscription<SoraConnectionEvent> eventSubscription;
late final StreamSubscription<SoraDebugEvent> debugEventSubscription;

void setup(SoraConnection conn) {
  eventSubscription = conn.events.listen((event) {
    // イベント処理
  });
  debugEventSubscription = conn.debugEvents.listen((event) {
    // debug event 処理
  });
}

Future<void> dispose(SoraConnection conn) async {
  await eventSubscription.cancel();
  await debugEventSubscription.cancel();
  await conn.dispose();
}