v0.9.3 RFC Draft — Experimental UDP / RTP Open Source

OSTP
Open Sonic Transport Protocol

UDPベースのリアルタイム・マルチルームオーディオ配信プロトコル。 サブ100ms遅延、100,000+同時受信者、DJデュアルデッキ、経済レイヤーを内蔵した次世代プロトコル仕様。

Section 1

概要

UDPベースのリアルタイム・マルチルームオーディオ配信プロトコル。 RTPを基盤にOSTPool拡張ヘッダを追加し、LAN・P2P・WAN全ての接続モードを統一的に扱う。

<100ms
サブ100ms遅延
LAN Multicastでは<5ms。P2P Direct <30ms。WANリレー経由でも<100msを保証。
🌐
100,000+
同時受信スケール
スワームトポロジーにより、リレーサーバーを増やさずに100,000+の同時受信者をサポート。
💎
内蔵
経済レイヤー内蔵
著作権追跡・チップ・DJ支援をプロトコルレベルでサポート。外部決済システム不要。

プロトコル比較

特徴 WebRTC AES67 OSTP
トポロジー Mesh P2P Multicast Hybrid (LAN/P2P/WAN Swarm)
最大受信者数 ~100 (SFU) LAN限定 100,000+ (Swarm)
LAN/WAN WAN中心 LANのみ 両方対応
著作権追跡 ✅ SSRC fingerprint
経済レイヤー ✅ TIP / SUPPORT / CHARGE
初回音声到達 500–2000ms (ICE) <5ms <5ms (LAN) / <100ms (WAN)
DJデュアルデッキ ✅ stream_id encoding
ファイルプリバッファ ✅ 50ms切替
Section 2

アーキテクチャ

OSTProはMedia Plane(UDPによる音声配信)とControl Plane(WebSocketによる制御)の2層構造を持つ。

Media Plane (UDP)
SourceRelay
  ├── Swarm Node 1
  │     ├── Leaf Node A
  │     └── Leaf Node B
  └── Swarm Node 2
        ├── Leaf Node C
        ├── Leaf Node D
        └── Leaf Node E
Control Plane (WebSocket)
solunad :8400/ws  (DCP)
  └── Monitor commands
  └── Player control
  └── System info

relay.solun.art:5100  (RSP)
  └── JOIN / HELLO / MEMBERS
  └── TIP / SUPPORT / CHARGE
  └── RTCP receiver reports

接続モード

🏠
LAN Multicast
239.69.0.1:5004
<5ms。IGMPv3対応スイッチのみ。チャンネル名ハッシュからマルチキャストアドレスを導出。
🔗
P2P Direct
UDP hole-punch
<30ms。リレーサーバーが PEER ヒントを配信。NATトラバーサルで直接接続。
🌐
Relay WAN
relay.solun.art:5100
<100ms。3リージョン(nrt/lax/ams)。スワームノードで10万+受信者にスケール。
Section 3

パケットフォーマット

OSTProパケットはRTP (RFC 3550) ベースに、RFC 5285準拠のRTP拡張ヘッダ(Profile=0x4F53 "OS")を付加した構造を持つ。

3.1 Modern OSTProパケット (28バイトヘッダ)

重要: Modern形式は拡張フラグ X=1 で識別。RTPヘッダ直後にRFC 5285拡張ヘッダが続き、stream_id・seq_ext・media_timestampを含む。
Bit offset
0 8 16 24 31
0–31
V=2
version
2b
P=0
padding
1b
X=1
extension
1b
CC=0
CSRC count
4b
M
marker
1b
PT
payload type (96–98)
7b
Sequence Number
シーケンス番号
16b
32–63
RTP Timestamp
48000 Hz タイムスタンプ
32bit
64–95
SSRC
送信者識別子 (著作権追跡に使用)
32bit
96–127
Profile = 0x4F53
"OS" — OSTP識別
16bit
Length = 0x0002
拡張ヘッダ長 (2×32bit)
16bit
128–159
deck_id
stream_id[15:12]
4b
ch_count
stream_id[11:8]
4b
reserved=0
stream_id[7:0]
8b
seq_ext
拡張シーケンス番号
16bit
160–191
media_timestamp
メディアタイムスタンプ (μs精度)
32bit
192+
Audio Payload
PCM24 / F32 / Opus (可変長)
variable
末尾
CRC-32
オプション。整合性チェック
32bit opt.

stream_id フィールド詳細

stream_id[15:12] = deck_id    // 0 = Deck A, 1 = Deck B
stream_id[11:8]  = ch_count   // 0=legacy 2ch, 1=mono, 2=stereo, 6=5.1, 8=7.1
stream_id[7:0]   = reserved   // MUST be 0x00

3.2 ペイロードタイプ (PT)

PT名前説明
96PCM2424-bit integer PCM、48000Hz
97F3232-bit float PCM、48000Hz
98OpusOpus compressed (RFC 6716)、可変ビットレート
10AES67/L24AES67 互換 24-bit PCM (互換レイヤー)
11AES67/L16AES67 互換 16-bit PCM (互換レイヤー)
126NACK再送要求 (RFC 4585 Generic NACK)
127FEC/XORXOR パリティパケット (N=5)

3.3 Legacy フォーマット (後方互換)

X=0の場合、RTPヘッダ12バイトの直後(offset 12)に4バイトのマジック 0x4F535450("OSTP")を検出することで識別する。 Modern形式への移行推奨。レガシー識別子は受信側でのみサポート。

MUST NOT: 新規実装でLegacyフォーマットを送信してはならない。受信側は両形式を受け入れること (MUST accept)。
Section 4

チャンネルアドレッシング

チャンネル名は人間が読めるUTF-8文字列で、LAN・WAN両方のアドレスに変換される。

名前規則

制約
エンコーディングUTF-8 NFC正規化
長さ1–64バイト
使用可能文字Printableユニコード + /(パス区切り)
禁止文字制御文字 (U+0000–U+001F)、NULL
パス最大深度4階層

名前例

"test"                    // シンプルな名前
"soluna/stage-a"          // 階層: ブランド/ステージ
"venue/tokyo/floor-1"     // 階層: 会場/都市/フロア
"dj/akira/set-20260316"  // 階層: アーティスト/セット

LAN → Multicastアドレス導出

チャンネル名のFNV-1a 32bitハッシュを計算し、Organization-Localマルチキャスト範囲にマップする:

h = FNV1a_32(channel_name)
X = (h >> 8) & 0xFF
Y = h & 0xFF
multicast_addr = 239.69.X.Y
port           = 5004          // AES67互換

WAN → Relay接続

// UDP unicast to relay
relay.solun.art:5100

// TCP/WS control
JOIN:channel_name:password:device_name\n
Section 5

リレープロトコル (RSP)

Relay Session Protocol。TCP上のテキストベースプロトコル。各コマンドは \n で終端。 接続先: relay.solun.art:5100

接続フロー

JOIN:channel:password:device_name\n
チャンネル参加リクエスト。パスワードなしの場合は空文字。
ROLE:dj|owner|listener\n
割り当てられたロールの通知。
OK:joined\n
接続成功確認。
HELLO\n
キープアライブ。5秒毎に送信しなければならない。15秒無応答でタイムアウト。
MEMBERS\n
現在のチャンネルメンバー一覧を要求。
MEMBERS:{...}\n
JSON形式のメンバー一覧。

コマンド一覧

コマンド方向説明
JOIN:<ch>:<pw>:<name>C→Rチャンネル参加。pw省略可
HELLOC→Rキープアライブ (5秒毎、MUST)
MEMBERSC→Rメンバー一覧取得
WALLETC→R残高照会 (Solana)
TIP:<amount>C→RDJ/配信者へチップ送信 (ENAI)
SUPPORT:<amount>C→RDJ/イベント支援 (ENAI)
REPORT:<ssrc>:<recv>:<lost>:<loss%>:<jitter>:<seq>C→RRTCP受信レポート
OK:joinedR→C接続成功
ROLE:<role>R→Cロール通知 (dj/owner/listener)
PEER:<ip>:<port>R→CP2Pピアヒント (NAT traverse用)
ERROR:<code>:<msg>R→Cエラー通知
Section 6

輻輳制御

RFC 8085準拠のUDP輻輳制御。損失率・ジッターを監視し、ビットレートラダーを自動調整する。

ビットレートラダー

Tierビットレート品質想定用途
032 kbps音声緊急/低速WAN (3G等)
164 kbps良好標準モバイル (4G)
2128 kbpsHi-Fiデフォルト (WiFi/5G)
3192 kbpsスタジオ高品質WiFi/有線
4320 kbpsマスター録音/アーカイブ/LAN

状態遷移ルール

⬇ ダウングレード
損失率 >10% が 3秒間継続した場合、1ティア下げる。Opaque NACKが溢れた場合も即座にダウングレード。
⬆ アップグレード
損失率 <0.5% が 30秒間安定して継続した場合のみ1ティア上げる。急激なアップグレードは禁止。

3層回復メカニズム

Opus In-Band FEC
Opusコーデック内蔵のFEC。前パケット情報を現パケットに埋め込む。追加帯域なし。
XOR Parity FEC (N=5)
5パケット毎にXORパリティパケット (PT=127) を送信。1バースト損失を1パケットで回復。
NACK Retransmit (RFC 4585)
FECで回復不能な場合、NACK (PT=126) を送信して再送要求。レート制限: 10 NACK/秒/セッション。

XOR FECパケットフォーマット

PT      = 127          // FEC/XOR
stream_id = 0xFFFF     // FECパケット識別
seq     = base_seq     // 保護対象の先頭シーケンス番号

Payload = XOR(pkt[N], pkt[N+1], pkt[N+2], pkt[N+3], pkt[N+4])

RTCP受信レポート (JSON over WebSocket)

{
  "command": "rx.receiver_report",
  "ssrc": 3141592653,
  "packets_received": 25000,
  "packets_lost": 12,
  "loss_rate_pct": 0.05,
  "jitter_ms": 1.8,
  "last_seq": 25012,
  "rtt_ms": 45.2
}
Section 7

ファイル配信プロトコル

次の曲ファイルを再生前に全受信者へ配信するプリバッファ機構。これにより曲切替を 50ms 以内に同期実行できる。

方式切替遅延
通常アップロード (逐次転送)3,000 – 30,000 ms (ファイルサイズに依存)
プリバッファ済み (OSTP)50 ms ✅

配信フロー

// ── 75%再生時点でプリバッファ開始 ─────────────────────────────

iOS/Android ──POST /api/player/upload?name=track.mp3&mode=next──→ solunad
solunad     ←── event: player.next_file_start {name, size} ──────→ 全受信者
            ←── binary: [0xFC][0xFD][hi][lo][32KB chunk] × N ──→ 全受信者
solunad     ←── event: player.next_file_done ────────────────────→ 全受信者
各受信者     ──command: player.next_file_ready ──────────────────────→ solunad

// ── 曲切替時 (全員準備完了後) ────────────────────────────────

solunad     ←── event: player.switch {switch_delay_ms: 50, file_pos_ms: 0} → 全受信者
各受信者     ── 50ms後にローカルAVPlayer/MediaPlayerで再生開始 ─────────

バイナリフレームフォーマット

マジック用途
0xFA 0xFB現在曲チャンク (ライブ配信)
0xFC 0xFD次曲プリバッファチャンク
フレーム構造: [magic 2B][size_hi 1B][size_lo 1B][payload 最大32768B] — サイズフィールドはbig-endian。最大チャンクサイズ = 32,768バイト = 32KB。
Section 8

デーモン制御プロトコル (DCP)

solunadデーモンをWebSocket経由で制御するJSONプロトコル。接続先: ws://host:8400/ws

メッセージフォーマット

// リクエスト (id は任意の整数)
{ "id": 42, "command": "monitor.set_delay", "ms": 40 }

// レスポンス
{ "id": 42, "success": true, "data": "" }

// ブロードキャストイベント (id なし)
{ "event": "player.switch", "switch_delay_ms": 50, "file_pos_ms": 0 }

コマンド一覧

Monitor (TX再生制御)

コマンド説明
monitor.stats送信統計 (送信パケット数、ビットレート、接続数)
monitor.start音声送信開始
monitor.stop音声送信停止
monitor.set_volume音量設定 (0.0–1.0)
monitor.set_muteミュート切替 (bool)
monitor.set_bufferバッファサイズ設定 (ms)
monitor.set_delay遅延補正設定 (ms)
monitor.list_devices利用可能なオーディオデバイス一覧

Player

コマンド説明
player.status現在の再生状態
player.play再生開始
player.pause一時停止
player.stop停止
player.seekシーク (pos_ms)
player.file_ready現在曲のプリバッファ完了通知 (受信者→送信者)
player.next_file_ready次曲のプリバッファ完了通知 (受信者→送信者)

System / Channel

コマンド説明
system.infoデーモンバージョン、OS、ビルド情報
time.pingRTT測定 (遅延補正計算に使用)
mode.get現在の動作モード取得 (tx/rx/loopback)
mode.set動作モード切替
channel.get現在のチャンネル名取得
channel.setチャンネル名変更
rx.receiver_reportRTCP受信レポート送信 (→ relay)

Time Sync アルゴリズム

目的: solunadのバッファ遅延をネットワーク遅延に合わせて自動調整し、全受信者の再生タイミングを同期する。
// クライアントサイド実装例 (TypeScript)
const t1 = Date.now()
ws.send(JSON.stringify({ command: "time.ping" }))

// recv: {"data": "{\"pong\":true}"}
const t2 = Date.now()
const latency = Math.max(20, Math.floor((t2 - t1) / 2) + 15)

// 遅延補正をデーモンに適用
ws.send(JSON.stringify({ command: "monitor.set_delay", ms: latency }))

// ジッターバッファも同期
jitterBufferTarget = latency
Section 9

DJデュアルデッキ

stream_idフィールドを利用して2つのデッキ(Deck A / Deck B)を同時に扱う。 クロスフェード・BPM同期・エフェクトをプロトコルレベルでサポート。

stream_id エンコーディング

stream_id
deck_id
bits [15:12]
4bit
ch_count
bits [11:8]
4bit
reserved
bits [7:0]
8bit = 0x00
deck_id意味
0x0Deck A (プライマリ)
0x1Deck B (セカンダリ)
0x2–0xFReserved (将来の拡張用)

動作モード

✅ param-only (デフォルト)
パラメータ(クロスフェード値・BPM等)のみ送信。受信者側でローカルファイルを加工して再生。 帯域幅を最小化。
dual-stream
2つのUDP音声ストリームを同時送信。各ストリームがstream_idで識別される。 帯域幅2倍が必要。

パラメータコマンド

// クロスフェード位置設定 (0.0 = Deck A, 1.0 = Deck B)
{ "command": "deck.set_param", "deck": 0, "param": "crossfade", "value": 0.7 }

// BPM設定
{ "command": "deck.set_param", "deck": 1, "param": "bpm", "value": 128.0 }

// モード切替
{ "command": "deck.set_mode", "mode": "param_only" }

等パワークロスフェードアルゴリズム

Equal-Power Crossfade: クリックノイズを防ぐため、10ms (480サンプル @ 48kHz) 以上でスムーシングする (MUST)。
// crossfade: 0.0 = Deck A only, 1.0 = Deck B only
θ = crossfade × π / 2

gain_A = cos(θ)   // Deck A: 1.0 → 0.0 (equal-power curve)
gain_B = sin(θ)   // Deck B: 0.0 → 1.0 (equal-power curve)

output = gain_A × deck_A + gain_B × deck_B

// MUST smooth over ≥ 10ms (480 samples @ 48kHz) to prevent clicks
// クリックノイズ防止のため ≥480サンプルかけてスムーシングすること
Section 10

セキュリティ

OSTProのセキュリティモデルはコンテンツの機密性に応じて段階的に適用される。

暗号化

要件ケース
OPTIONAL DTLS-SRTP パブリックチャンネル (無料コンテンツ)
REQUIRED DTLS-SRTP 有料コンテンツ / クローズドチャンネル / ペイウォール

セッショントークン

// 256-bit random, base64url encoded
token = base64url(crypto.randomBytes(32))

// WebSocket Authorizationヘッダで送信
Authorization: Bearer <token>

// トークン有効期限: セッション終了まで (最大24時間)

レート制限

対象制限
JOIN試行10回1分/IP
NACK送信10パケット1秒/セッション
UDP受信10,000パケット1秒/IP
CHARGE (支払い)nonce + 300秒ウィンドウリプレイ防止

プライバシー

Fingerprint保護: SSRCフィンガープリントはk-anonymityハッシュプレフィックスを使用。 著作権追跡に必要な最小限の情報のみ保持し、48時間後に自動削除される。
Section 11

IANA登録

OSTProが使用するポート番号・ペイロードタイプ・マルチキャストアドレスのIANA登録状況。

リソース状態
RTP Extension Profile 0x4F53 ("OS") 申請予定
UDP Port (audio) 5004 既存割当 (AES67互換)
UDP/TCP Port (relay) 5100 申請予定
TCP/WS Port (DCP) 8400 申請予定
Payload Type 96 PCM24 Dynamic
Payload Type 97 F32 Dynamic
Payload Type 98 Opus Dynamic
Payload Type 126 NACK Dynamic
Payload Type 127 FEC/XOR Dynamic
Multicast (LAN) 239.69.0.0/16 Organization-Local
参照RFC: RFC 3550 (RTP), RFC 4585 (NACK), RFC 5285 (RTP Extension Header), RFC 6716 (Opus), RFC 8085 (UDP Congestion Control)
Section 12

実装状況

各プラットフォームでのOSTProサポート状況。✅ = 実装済み・テスト済み、🔜 = 実装計画あり (DTLS)。

プラットフォーム 受信 RX 送信 TX FEC (動的) NACK DTLS RTCP TURN DJ Deck
Mac (solunad) 🔜
iOS (Swift) 🔜
Android (Kotlin) 🔜 🔜 🔜
Windows 🔜 🔜 🔜
Web (Browser) 🔜 🔜 🔜
Linux / RPi 🔜 🔜 🔜
ESP32 🔜
凡例: ✅ = 実装済みかつテスト済み / 🔜 = 実装計画あり / — = 対象外
FEC (動的): loss率に応じ N=0/10/5/3/2 を自動切替 (OSTP v0.9.3 §3.7)
RTCP: ビットレート適応 + RFC 3550 §6.4.1 ジッタバッファ駆動
TURN: Symmetric NAT フォールバック — TURN_ALLOC / TURN_OK (OSTP v0.9.3 §5.x)

v0.9.3 実装済み修正

修正詳細影響
media_timestamp ms化ナノ秒 (4.3秒ロールオーバー) → ミリ秒 (49日ウィンドウ)全プラットフォーム同期修正
Dynamic FECRTCP loss% → N自動調整 (0/10/5/3/2)WiFi損失への適応
RTCP → ジッタバッファRFC 3550 inter-arrival jitter → target_fill_framesiOS バッファ精度向上
TURN フォールバックTURN_ALLOC / TURN_OK リレー確認Symmetric NAT 対応
Gossip + dual-parentGOSSIP_PEERS 8候補 / PARENT_FAIL <80ms 再接続スワームチャーン耐性
RTCP APP SWCHPT=204 同期ファイル切替をリレー経由で全員転送player.switch 精度
TIP on-chain検証Solana RPC getTransaction で tx_signature 確認不正チップ防止
Control/Data HOL解消ファイルチャンク port 8401 / コントロール JSON port 8400大容量ファイル中の遅延防止
Section 13

ドキュメント

OSTP仕様書・Internet-Draft・APIリファレンスへのリンク。

📄
Internet-Draft (IETF)
draft-hamada-opensonic-ostp-00
Independent Submission, Experimental。 Expires: 2026-09-17。
📚
完全仕様書
OSTP-SPEC.md (2,183行)
17セクション。スワームトポロジー・経済レイヤー・DJデッキまで完全記述。
🔌
API リファレンス
api.md
DCP WebSocket API・RSP コマンド・イベント一覧。インタラクティブサンドボックス付き。
GitHub
yukihamada/opensonic
MIT License。solunad C++ / iOS Swift / Android Kotlin / Web JS 実装。