新SkyWay(@skyway-sdk/room)における Publication / Subscription のライフサイクル設計

新SkyWay(@skyway-sdk/room)では、
従来の SkyWay と比べて Stream の扱いが明確にモデル化されています。

その中心となるのが、

  • Publication(公開)
  • Subscription(購読)

という2つの概念です。

本記事では、Publication / Subscription のライフサイクルと状態管理を正しく理解し、安定したリアルタイム通信を実装するための設計指針を解説します。


新SkyWayにおける Stream の基本構造

新SkyWayでは、Stream は以下の流れで扱われます。

LocalStream
  ↓ publish
Publication
  ↓ subscribe
Subscription
  ↓
RemoteStream

重要なのは、Streamそのものと通信上の概念が分離されている点です。

  • Stream:映像・音声・データの実体
  • Publication:Room 上での「公開状態」
  • Subscription:受信者側の「購読状態」

この分離が、柔軟な制御を可能にしています。


Publication のライフサイクル

Publication は、Member が Stream を publish した時点で生成されます。

const publication = await member.publish(videoStream);

この時点で、

  • Room 内の他の Member から 購読可能
  • publish した Member 自身は 購読しない

という状態になります。

Publication が保持する情報

  • publisher(誰が公開したか)
  • contentType(video / audio / data)
  • stream(LocalStream 参照)
  • enabled / disabled 状態

Publication は「再利用されない」

重要な設計上のポイントとして、

Room を leave / join し直すと、Publication は必ず別物になる

という点があります。

  • 再接続
  • タブリロード
  • ブラウザスリープ復帰

これらが発生すると、以前の Publication は 論理的に消滅します。

そのため、

  • Publication ID を永続的に保持しない
  • 再接続時は再 publish が前提

という設計が必須です。


Subscription のライフサイクル

Subscription は、Member が Publication を subscribe した時点で生成されます。

const subscription = await member.subscribe(publication.id);

Subscription は以下の特徴を持ちます。

  • 購読者ごとに別インスタンス
  • Publication が同じでも Subscription は共有されない
  • subscribe を明示的に呼ばない限り生成されない

Subscription は「自動復元されない」

新SkyWayで最もハマりやすいポイントがここです。

  • Room 再Join時
  • ネットワーク瞬断後
  • タブ復帰後

これらのケースでは、

Subscription は一切自動で復元されません

つまり、

  • onStreamPublished だけに依存している
  • 再Join時に既存 Publication を subscribe していない

場合、映像・音声が片方向になります。


正しい Subscription 復元戦略

再Join後は、必ず次の2段構えを実装します。

  1. 既存 Publication をすべて subscribe
  2. 新規 Publication をイベントで subscribe
function setupSubscriptions(room, member) {
  room.publications.forEach(pub => {
    if (pub.publisher.id !== member.id) {
      member.subscribe(pub.id);
    }
  });

  room.onStreamPublished.add(e => {
    if (e.publication.publisher.id !== member.id) {
      member.subscribe(e.publication.id);
    }
  });
}

この関数は、

  • 初回 Join
  • 再接続 Join

両方で必ず呼ぶ必要があります。


enabled / disabled の扱いに注意する

Publication には enable() / disable() があります。

publication.disable();
publication.enable();

これは、

  • Stream の送信を止める
  • Publication 自体は残す

という挙動になります。

注意点

  • disable 中でも Publication は存在する
  • 新規 subscribe すると 無音・無映像の Stream を受け取る
  • UI 側で状態管理が必要

「ミュート= unpublish」と誤解すると、設計が破綻します。


LocalStream の再生成は安全側

再接続時に、

  • 以前の LocalVideoStream
  • 以前の LocalAudioStream

を使い回すと、ブラウザやデバイスによっては 送信が再開されないケースがあります。

安全な設計としては、

const { audio, video } =
  await SkyWayStreamFactory.createMicrophoneAudioAndCameraStream();

await member.publish(video);
await member.publish(audio);
  • 再接続時は LocalStream を再生成
  • publish もやり直す

という方針が推奨されます。


状態管理は「Room基準」で考える

新SkyWayでは、

  • Stream
  • Member
  • Publication
  • Subscription

の寿命が 一致しません

そのため、

  • Room Join を起点に初期化
  • Room Leave を起点に破棄
  • Room 再Join = 全再構築

という Room中心の状態管理が最も安定します。


まとめ

新SkyWay(@skyway-sdk/room)は、
Publication / Subscription を明確に分離した設計により、高い柔軟性を持っています。

一方で、

  • Subscription は自動復元されない
  • Publication は再利用されない
  • Room 再Join時は全再構築が前提

という点を理解していないと、
「映像が片方向になる」「再接続後に何も映らない」問題に直結します。

新SkyWayでは、

「明示的に管理する設計」が正解

であることを前提に実装しましょう。


参考

  • 新SkyWay JavaScript SDK API Reference
    https://javascript-sdk.api-reference.skyway.ntt.com/room/