リアルタイムメッセージング機能

概要

Sora Flutter SDK では DataChannel を利用したリアルタイムメッセージング機能を利用することができます。

同一チャネルに参加しているクライアント同士でリアルタイムなメッセージを送受信することができます。 また、メッセージにはラベルを指定でき、同一チャネル内で複数の種類のメッセージを扱えます。

リアルタイムメッセージング機能の詳細は Sora のドキュメントの リアルタイムメッセージング機能 をご確認ください。

リアルタイムメッセージングの設定をする

Sora Flutter SDK でリアルタイムメッセージング機能を利用するには、接続時の設定で dataChannels を指定する必要があります。

dataChannels には必須パラメータとして label と direction を指定します。 label はメッセージの経路を識別するための文字列で、 direction はその経路の方向を指定します。

この label には必ず # を先頭につける必要があります。 # をつけることで、Sora のメッセージング用の DataChannel であることを示しています。

dataChannels はリストなので、複数のラベルを同時に指定できます。

label は送受信時にそのまま使う識別子です。 sendDataChannelMessage() の第 1 引数にも同じ文字列を渡します。

また、ここで指定する dataChannels はアプリケーション向けメッセージング用です。 signaling や notify などの Sora / SDK 内部用 DataChannel を追加する必要はありません。

final config = SoraConnectionConfig(
  signalingUrls: <String>['wss://example.com/signaling'],
  channelId: 'example-channel',
  role: SoraRole.sendrecv,
  dataChannelSignaling: true,
  dataChannels: <Map<String, Object?>>[
    <String, Object?>{
      'label': '#chat',
      'direction': 'sendrecv',
      // max_retransmits と max_packet_life_time を省略すると完全信頼。
      'ordered': true,
    },
    <String, Object?>{
      'label': '#notifications',
      'direction': 'recvonly',
      // 再送時間を 3000 ms に制限した部分信頼。
      'ordered': false,
      'max_packet_life_time': 3000,
    },
    <String, Object?>{
      'label': '#telemetry',
      'direction': 'sendonly',
      // 再送回数を 2 回に制限した部分信頼。
      'max_retransmits': 2,
    },
  ],
);

順序保証

リアルタイムメッセージは label 毎に順序保証をするかどうかを指定できます。順序保証をする場合は ordered: true を指定します。 ordered: false を指定すると、到着順とは異なる順序で配信されることがあります。

ordered は順序だけを設定します。 ordered: false でも、 max_retransmits と max_packet_life_time を省略すれば完全信頼です。

信頼性(再送制御)

リアルタイムメッセージの信頼性は、 max_retransmits と max_packet_life_time で再送の上限を指定します。これらの項目を指定しない場合は、再送制限のない完全信頼のチャネルになります。

max_retransmits または max_packet_life_time のどちらか一方を指定すると部分信頼のチャネルになります。

  • max_retransmits は再送回数の上限です。 0 を指定すると再送しません
  • max_packet_life_time は送信および再送を試みる時間の上限をミリ秒で指定します

max_retransmits と max_packet_life_time は同時に指定できません。両方を指定すると Sora / WebRTC 側で設定エラーになります。

ここでいう完全信頼は、接続中の DataChannel が再送制限なく配信を試みることを意味します。接続が切断された後もアプリケーションへ配信されることや、アプリケーション処理が完了することまで保証するものではありません。

dataChannels の各要素は SoraConnectionConfig.dataChannels から connect メッセージの data_channels へ渡されます。SDK は各要素の ordered 、 max_retransmits 、 max_packet_life_time を別の意味へ変換せず、これらの組み合わせも検証しません。キー名、値の型、同時指定の禁止などの制約は Sora / WebRTC の仕様に従って指定してください。

圧縮機能

リアルタイムメッセージは label 毎に圧縮するかどうかを指定できます。圧縮する場合は compress: true を指定します。

compress: true を指定したラベルでは、 SDK が送信時に自動で圧縮し、受信時に自動で展開します。アプリケーション側で個別に圧縮処理を書く必要はありません。

メッセージを送信する

メッセージは sendDataChannelMessage() で label を指定して送ることができます。

送信する label は、接続時に dataChannels へ設定したものと一致させます。

メッセージの実体は Uint8List です。文字列や JSON を送りたい場合は、アプリケーション側で UTF-8 エンコードしてから送信します。

以下のコード例では、接続済みの SoraConnection を conn として保持しているものとします。

import 'dart:convert';
import 'dart:typed_data';

final data = Uint8List.fromList(
  utf8.encode('{"type":"chat","text":"hello"}'),
);

conn.sendDataChannelMessage('#chat', data);

DataChannel が開く前に送信すると取り扱いが分かりにくくなるため、通常は後述の SoraDataChannelOpenEvent を確認してから送信するのが安全です。

メッセージを受信する

メッセージは SoraDataChannelMessageEvent イベント通知で受け取ることができます。 message には、送信したときの label と data が入ります。

label でメッセージの経路を識別でき、 data で送られてきたデータ内容を受け取ることができます。 data は Uint8List のため、文字列や JSON として扱いたい場合は UTF-8 でデコードします。

import 'dart:convert';

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

DataChannel が使えるようになったタイミング

利用可能になったことは SoraDataChannelOpenEvent イベント通知で確認できます。

SoraDataChannelOpenEvent は、どのラベルの DataChannel が開いたかを確認するためのイベントです。複数の label を設定している場合は、ラベルごとに監視すると分かりやすくなります。

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

リアルタイムメッセージのみを利用する

音声 / 映像を送受信せず、リアルタイムメッセージ機能のみを利用できます。 ただし、この場合は Sora の設定が必要です。

この構成では、通常 audio: false 、 video: false 、 dataChannelSignaling: true に加えて、必要な dataChannels を明示します。 role は sendonly ですが、音声 / 映像を送信しないため、 MediaDevices.getUserMedia() は不要です。 connect() は引数なしで呼び出します。

final config = SoraConnectionConfig(
  signalingUrls: <String>['wss://example.com/signaling'],
  channelId: 'example-channel',
  role: SoraRole.sendonly,
  audio: false,
  video: false,
  dataChannelSignaling: true,
  dataChannels: <Map<String, Object?>>[
    <String, Object?>{
      'label': '#chat',
      'direction': 'sendrecv',
      'ordered': true,
      'compress': true,
    },
  ],
);

final conn = await Sora.createConnection(config);

// 音声 / 映像を送信しないため、MediaStream を渡さずに接続する。
await conn.connect();

詳細は Sora のドキュメントの DataChannel を利用したメッセージングのみで接続する をご確認ください。