# Sora JavaScript SDK ドキュメント このドキュメントは Sora JavaScript SDK バージョン 2026.1.0 に対応しています。 Sora 自体の製品お問い合わせは sora at shiguredo dot jp までお願い致します。 (このメールアドレスへの特定電子メールの送信を拒否いたします) ## LLM 向け LLM が読み込みやすい形式でドキュメントを提供しています。 - 目次は [llms.txt](/llms.txt) にあります - 全文は [llms-full.txt](/llms-full.txt) にあります ## 問い合わせについて Sora JavaScript SDK の質問などについては Discord の `#sora-sdk-faq` をご利用ください。 ただし、 Sora のライセンス契約の有無に関わらず、応答時間と問題の解決を保証しませんのでご了承ください。 Sora JavaScript SDK に対する有償のサポートについては提供しておりません。 ## 注意事項 ### レガシーストリーム機能への対応終了について Sora 2025.1.0 のレガシーストリーム機能廃止に伴い、 Sora JavaScript SDK の 2025.1.0 でレガシーストリーム機能への対応を終了しました。 それに伴い `multistream` オプションを廃止しました。 Sora JavaScript SDK 2025.1.0 以降へ移行する際は `multistream` オプションを削除するようにしてください。 今後、レガシーストリーム機能を継続して利用したい場合は、 Sora JavaScript SDK の 2025.1.0 以前のバージョンを利用してください。 > **注釈** > > レガシーストリーム機能は 2015 年にリリースしました。 > その後 2016 年にマルチストリーム機能で代替できるようになり、 > 2024 年に非推奨となり 2025 年 6 月リリースの Sora にて廃止しました。 ### Compression Stream API のサポートブラウザについて Sora JavaScript SDK 2024.2.0 以降は [Compression Stream API](https://developer.mozilla.org/ja/docs/Web/API/Compression_Streams_API) を利用しています。 そのため、 Apple Safari の場合はバージョン 16.4 以降が必要です。 詳細は [対応ブラウザについて](faq.html#7e59ef) をご確認ください。 - [リリースノート](release.html) - [Sora JavaScript SDK 概要](overview.html) - [FAQ](faq.html) - [廃止機能](obsolete.html) - [インストール](install.html) - [チュートリアル](tutorial.html) - [サンプル](examples.html) - [API リファレンス](api.html) - [コールバック](callback.html) - [Sora Labo](sora_labo.html) - [ステレオ入出力](stereo.html) - [古いリリースノート](old_release.html) # リリースノート **CHANGE** : 下位互換のない変更 **UPDATE** : 下位互換がある変更 **ADD** : 下位互換がある追加 **FIX** : バグ修正 過去のリリースノートを確認したい場合は [古いリリースノート](old_release.html) をご確認ください。 ## 2026.1.0 **リリース日**: 2026-06-18 ### ESM 専用パッケージへの移行について Sora JavaScript SDK 2026.1.0 から `package.json` の `exports["."]` から `require` 条件を削除し、ESM のみのサポートになりました。 `require("sora-js-sdk")` は 2025.2.0 以前でも `ERR_REQUIRE_ESM` で失敗していましたが、 2026.1.0 以降は `ERR_PACKAGE_PATH_NOT_EXPORTED` でエラーメッセージが変わります。 CommonJS から利用していた場合は `import` または `await import()` に置き換えてください。 ### Node.js のサポートバージョンについて Sora JavaScript SDK 2026.1.0 から Node.js の最低要件は `22.18.0` 以上になりました。 Node.js 20 は 2026-04-30 に End Of Life (EoL) を迎えたためサポートを終了しました。 Node.js 22 系を利用する場合は 22.17.x 以前ではビルドが通らない点に注意してください。 ### 変更履歴 - [CHANGE] Node.js 20 のサポートを終了しました- Node.js 20 は 2026-04-30 に End Of Life (EoL) を迎えました- - [CHANGE] Node.js の最低要件を 22.18.0 以上に引き上げました- 後続のビルドツール移行で導入される `tsdown` 0.22 系が Node.js `^22.18.0 || >=24.0.0` を要求するため - [CHANGE] `package.json` の `exports["."]` から `require` 条件を削除し ESM のみをサポートするように変更しました- `require("sora-js-sdk")` は元から `ERR_REQUIRE_ESM` で失敗していましたが、削除後は `ERR_PACKAGE_PATH_NOT_EXPORTED` でエラーメッセージが変わります- CommonJS で利用していたコードは `import` または `await import()` に置き換える必要があります - [CHANGE] `removeAudioTrack` / `removeVideoTrack` 実行中に `disconnect()` 等で `RTCPeerConnection` が破棄された場合に `ConnectError` (reason `"REMOVE_TRACK_DURING_DISCONNECT"`) で reject するように変更しました- 利用者は `.catch` で reject を受ける必要がある (fire-and-forget で呼んでいる場合は unhandled promise rejection が発生する)- 非推奨の `stopAudioTrack` / `stopVideoTrack`、および `replaceAudioTrack` / `replaceVideoTrack` 経由でも同じ reject が伝播する - [CHANGE] `abendPeerConnectionState()` の発火順を他 3 系統と揃え timeline → callback の順に変更しました - [UPDATE] pnpm 11 系に上げました - [UPDATE] TypeScript 6.0 へ対応しました - [FIX] `trace()` が JWT 等の機密を含む metadata を `console` / `callbacks.log` に raw 出力していたセキュリティ問題を redact で修正しました - [FIX] `rpc` メソッドのサンプルコードで、実際に存在しない RPC メソッド名が使われていた問題を修正しました - [FIX] `type: "redirect"` 受信時に旧 WebSocket の `onmessage` ハンドラが解除されていなかった問題を修正しました - [FIX] `type: "switched"` (`ignore_disconnect_websocket`) 経路で旧 WebSocket の `onmessage` / `onerror` ハンドラが解除されていなかった問題を修正しました - [FIX] `disconnect()` が並列実行されたとき `callbacks.disconnect()` が複数回発火しないように修正しました - [FIX] `onDataChannel` で同名 label の DataChannel を上書きする際に旧 DataChannel のハンドラを解除し close するように修正しました - [FIX] `disconnect()` で WebSocket が `OPEN` 以外の状態のときに `disconnect` callback が呼ばれない問題を修正しました - [FIX] `iceConnectionState` が `disconnected` で 10 秒経過した際の検知が現行ブラウザで動作していなかった問題を修正しました - [FIX] 切断系メソッド 5 経路で `pc.onicecandidate` を解放し Trickle ICE 由来の unhandled rejection を防ぐように修正しました - [FIX] `sendAnswer()` の `ws.send()` が同期 throw したときに内部状態がクリーンアップされなかった問題を修正しました - [FIX] `signaling()` の `ws.onmessage` 内で例外が発生したときに `connect()` が `connectionTimeout` まで固まっていた問題を修正しました - [FIX] `generateCertificate` が失敗した環境 (FIPS モード等) でも接続できるようにフォールバックするように修正しました - [FIX] `disconnect()` で DataChannel 切断エラー (`code: 4999`) 時に `abend` event が `normal` で上書きされないように修正しました - [FIX] `createSignalingMessage()` で `{ audioBitRate: undefined }` のような呼び出しで `message.audio` / `message.video` が boolean true から空オブジェクト `{}` に置換されていた問題を修正しました- 関連して `spotlightNumber` / `audioOpusParams*` に `undefined` を渡しても `message` に `undefined` キーが積まれないように修正しました - [FIX] `messaging()` が呼び出し側に渡された options を破壊しないように修正しました - [FIX] `ConnectionBase` constructor で options を shallow copy に変更し `skipIceCandidateEvent` の内部代入が呼び出し側 options に漏れないようにしました- 上記 `messaging()` の修正とあわせて、同一 options を `sendrecv()` / `sendonly()` / `recvonly()` と `messaging()` に渡したときに先行 Connection の options まで書き換わる問題も解消しました - [FIX] `abend()` の abend 分岐で `SoraCloseEvent` を 2 回生成して timeline と callback に別インスタンスを渡していたのを 1 つのインスタンスを共有するように修正しました - [FIX] `sendSignalingMessage` / `sendStatsMessage` で送信前に readyState を確認し、 `OPEN` 以外の状態での送信を防ぐように修正しました ## 2025.2.0 **リリース日**: 2025-12-10 ### 変更履歴 - [ADD] 接続が確立した際に発火する `connected` コールバックを追加しました- `RTCPeerConnection` の `connectionState` が `connected` かつシグナリング通知で自身の `connection.created` が通知されたタイミングで発火します - `connected` コールバックは **一度のみ** 発火します - `connected` コールバックはシグナリング通知が無効な場合は発火しません - 詳細は [connected](callback.html#28c76d) をご確認ください - [ADD] シグナリングが DataChannel 経由に切り替わった際に発火する `switched` コールバックを追加しました- DataChannel シグナリング機能を利用している場合にのみ発火します- 詳細は [switched](callback.html#5d9002) をご確認ください - [ADD] サイマルキャスト機能で初期の `rid` を指定する `simulcast_request_rid` オプションを追加しました- デフォルトは未指定です- `simulcast` を `true` で指定する必要があります- `sendrecv` または `recvonly` でのみ有効です - [ADD] RPC 機能を利用できる `rpc` メソッドを追加しました - [ADD] 利用できる RPC 一覧が取得できる `rpcMethods` を追加しました ## 2025.1.0 **リリース日**: 2025-06-12 ### レガシーストリーム機能の廃止について Sora 2025.1.0 のレガシーストリーム機能の廃止に伴い、 Sora JavaScript SDK の 2025.1.0 でレガシーストリーム機能を廃止しました。 今後、レガシーストリーム機能を継続して利用したい場合は、 Sora JavaScript SDK の 2025.1.0 より以前のバージョンを利用してください。 ### multistream オプションの廃止について 今回 Sora 2025.1.0 でレガシーストリーム機能の廃止に伴い `multistream` オプションを廃止しました。 もし `multistream` オプションを指定している場合は、 `options` から `multistream` 項目を削除して利用するようにしてください。 `multistream` オプションを指定しない場合は何もする必要がありません。 ```typescript sora = Sora.connection(signalingUrl, debug); connection = sora.sendonly(channelId, undefined, { // 廃止された multistream オプションなので、この項目を削除してください multistream: true, }); ``` - 2017 年にリリースした Sora JavaScript SDK で `multistream` オプションを追加しました - 2022 年にリリースした Sora JavaScript SDK で `multistream` オプションのデフォルト値を `true` を変更しました ### 変更履歴 - [CHANGE] Sora 2025.1.0 で廃止される、レガシーストリーム機能への対応を終了しました - [CHANGE] レガシーストリーム対応の終了に伴い、オプションの `multistream` 項目を廃止しました - [ADD] `onIceCandidate` を呼ばない `skipIceCandidateEvent` オプションを追加しました - [FIX] シグナリング URL が一つの場合でも配列として扱うようになっていた問題を修正しました - [FIX] シグナリング URL が空配列の場合エラーになるように修正しました # Sora JavaScript SDK 概要 Sora JavaScript SDK は [株式会社時雨堂](https://shiguredo.jp) が開発、販売する [WebRTC SFU Sora](https://sora.shiguredo.jp) のブラウザ向け JavaScript ライブラリです。 ## 主な仕様 Sora JavaScript SDK は Sora との WebRTC 接続部分の API を提供します。 ### 依存ライブラリ Sora JavaScript SDK は依存ライブラリが **0** の SDK です。 ### シグナリング処理 シグナリングとは WebRTC を利用する際にクライアント(SDK) とサーバー (Sora) の接続確立用のハンドシェイクの一種です。 WebRTC ではこのシグナリングの仕様が特に決まっていないため、すべて独自仕様となります。 Sora JavaScript SDK では Sora 独自のシグナリング部分をすべて SDK 側で吸収しているため、 SDK を利用したアプリケーション開発者が Sora 独自のシグナリングの仕組みを細かく意識する必要はありません。 ### WebRTC 接続処理 Sora との WebRTC 接続処理に対応しています。利用者が WebRTC 接続部分を意識する必要がなくなります。 ## 問い合わせについて Sora JavaScript SDK の質問などについては Discord の `#sora-sdk-faq` にお願い致します。 ### 何か問題があり、相談したい場合に共有していただく情報 - Sora WebRTC SFU のバージョン - Sora JavaScript SDK のバージョン - 利用ブラウザの種類 - 利用ブラウザのバージョン - 利用 OS の種類 どのような問題が発生しており、何を解決したいのかを簡潔に教えてください。 ### 問題の再現方法 問題を再現することができる最小限のコードを提供してください。 - GitHub Gist - CodeSandbox - CodePen - StackBlitz などを利用してコードを提供してください。 # FAQ ## 対応ブラウザについて Sora JavaScript SDK の 2024.2.0 以降を利用するには [Compression Stream API](https://developer.mozilla.org/ja/docs/Web/API/Compression_Streams_API) をサポートしているブラウザのバージョンが必要になります。 - Chrome 80 以降 - Edge 80 以降 - Firefox 113 以降 - Safari 16.4 以降 ## Sora JavaScript SDK のサンプルについて ### Sora JavaScript SDK のサンプル Sora JavaScript SDK の最小限の書き方を確認できるサンプルです。 ### Sora JavaScript SDK ドキュメントのサンプル Sora JavaScript SDK の解説を目的としたサンプルです。 [サンプル](examples.html) ## 仕様 ### MediaStreamTrack と Connection ID の紐付け Sora JavaScript SDK は [MediaStreamTrack](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) と Connection ID の紐付けを行う仕組みは提供していません。 ただし `track` コールバック時に、 [RTCTrackEvent](https://developer.mozilla.org/en-US/docs/Web/API/RTCTrackEvent) の [streams](https://developer.mozilla.org/en-US/docs/Web/API/RTCTrackEvent/streams) から [MediaStream](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream) を取得できます。 [MediaStream](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream) の [stream.id](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/id) が Connection ID となります。 ```javascript client.on("track", (event: RTCTrackEvent) => { // Sora では必ず 1 クライアント から送られてくるのは 1 音声/ 1 映像に固定されています // そのため track.streams[0] でクライアントのメディアストリームを取得できます const stream = event.streams[0]; // メディアストリームの ID はその音声と映像を配信しているクライアントの connectionId です // stream.id で取得できます console.log("connectionId: ", stream.id); }); ``` ### メディアストリームを SDK から扱えますか? メディアストリームを SDK から扱うことはできません。 Sora JavaScript SDK は音声や映像といったメディアストリームを SDK から扱うことはありません。 そのため音声や映像を扱う場合は [MediaDevices.getUserMedia()](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) API や [MediaDevices.getDisplayMedia()](https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getDisplayMedia) API を利用し、 アプリケーション開発者がメディアストリームを取得し、Sora JavaScript SDK に渡す必要があります。 ### マルチトラックには対応していますか? Sora JavaScript SDK が配信可能なメディアストリームは 1 本で 1 音声トラック、1 映像トラックとなります。 これは Sora の仕様になります。 そのため、マルチトラックには対応しておりません。 もし複数の音声や映像を 1 クライアントで配信したい場合は複数のコネクションを張るようにしてください。 ### `"type": "answer"` の SDP 書き換えは SDK の挙動に影響はありますか? Sora JS SDK では `createAnswer` が生成した SDP をそのまま利用するようにしています。 そのため、書き換え処理で SDK 側には基本的に影響することはありません。 もし SDP 書き換えをを行う場合は `"type": "answer"` だけでなく、 `"type": "re-answer"` も書き換えるようにしてください。 > **注釈** > > WebRTC API を利用せずに手動で SDP を変更する処理を "SDP Munging" と呼びます。 Sora への影響については Sora または Sora Cloud のサポートまでご連絡ください。 ## シグナリング ### 切断が発生し、再接続をする場合はどのようにすればいいですか? 何らかの理由で JavaScript SDK と Sora との接続が切断された際、 かならずインスタンスを作り直して再接続をしてください。 今まで利用していたインスタンスは破棄するようにしてください。 ### Sora との接続が完了したことを通知するコールバックはありますか? `connected` コールバックを利用してください。 このコールバックは自分の接続確立時に 1 度だけ発火します。 ```javascript // 色々省略 const sendrecv = soraConnection.sendrecv(channelId, metadata, options); // connected コールバック(自分の接続確立時に1度だけ発火) sendrecv.on("connected", (event: SignalingNotifyConnectionCreated) => { console.log("connected"); console.log("self-connection_id: ", event.connection_id); }); await sendrecv.connect(stream); ``` ## サイマルキャスト機能 ### ブラウザで縦画面のサイマルキャストで期待した本数のストリームが出力されません 残念ながら、縦画面を利用したサイマルキャストで期待した本数のストリームが出力されないのは、 ビットレートが足りない以外であれば、ブラウザの仕様の可能性があります。 ブラウザのサイマルキャストは解像度が出力するストリームに依存します。 ブラウザのサイマルキャストの仕様は Sora のドキュメントに記載してあります。 たとえば 540x960 (width x height) であればストリーム 3 本出力されますが、 405x720 (width x height) の場合はストリーム 2 本しか出力されません。 ## データチャネルメッセージング機能 ### メッセージングのみを利用したいです `messaging()` を利用する事でメッセージングのみを利用できます。 ```javascript const options = { dataChannelSignaling: true, dataChannels: [ { label: "#example", direction: "sendrecv" as DataChannelDirection, }, { label: "#example2", direction: "recvonly" as DataChannelDirection, }, ], }; const messaging = soraConnection.messaging(channelId, metadata, options); await messaging.connect(); ``` ### maxPacketLifeTime や maxRetransmits が設定できません Sora のシグナリング仕様では `snake_case` での指定ですが、 Sora JavaScript SDK では `camelCase` での指定となります。 ```javascript const options = { dataChannelSignaling: true, dataChannels: [ { label: "#example", direction: "sendrecv" as DataChannelDirection, maxPacketLifeTime: 60000, }, ], }; ``` ```javascript const options = { dataChannelSignaling: true, dataChannels: [ { label: "#example", direction: "sendrecv" as DataChannelDirection, maxRetransmits: 0, }, ], }; ``` ## RPC 機能 ### RPC 機能が利用できません Sora では本来 API 経由で実行する仕組みをデータチャネル経由で実行できる RPC 機能を Sora 2025.2.0 から提供しています。 RPC 機能を利用するには Sora の認証成功時に `rpc_methods` で利用できる RPC メソッド一覧を指定する必要があります。この指定がない場合は RPC 機能を利用できません。 以下のように Sora 側で認証成功時に `rpc_methods` を指定してください。 ```javascript { "allowed": true, "rpc_methods": [ "2025.2.0/RequestSimulcastRid", ] } ``` `rpc_methods` で指定したメソッドを Sora JavaScript SDK から呼び出せます。 詳細は [RPC 機能を使用する](examples.html#da4b6a) をご確認ください。 ## WebRTC API Sora JavaScript SDK には Sora との接続部分の機能のみを提供しています。 そのため、映像や音声、データなどを実際に処理する場合はすべて JavaScript の WebRTC API やライブラリなどを利用する必要があります。 [WebRTC API - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/WebRTC_API) ### 解像度を変更するにはどうしたらいいですか? 解像度の変更は [MediaDevices.getUserMedia()](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) や [MediaDevices.getDisplayMedia()](https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getDisplayMedia) API 側で行ってください。 ### 背景をぼかすにはどうしたらいいですか? 時雨堂がオープンソースとして公開している [@shiguredo/virtual-background - npm](https://www.npmjs.com/package/@shiguredo/virtual-background) を利用することで簡単にブラウザで背景ぼかし機能を利用可能です。 以下で動作確認が可能です。 ### ノイズを抑制できますか? 時雨堂がオープンソースとして公開している [@shiguredo/noise-suppression - npm](https://www.npmjs.com/package/@shiguredo/noise-suppression) を利用することで簡単にブラウザでノイズ抑制機能を利用可能です。 以下で動作確認が可能です。 ### 音声でステレオは利用できますか? ステレオ入力やステレオ出力はブラウザや端末、マイク依存になります。 詳細は [ステレオ入出力](stereo.html) を参照してください。 ### 画面キャプチャはできますか? [MediaDevices.getDisplayMedia()](https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getDisplayMedia) を利用して画面のストリームを取得してください。 ### カメラを切り替えられますか? トラックを切り替えたい場合は [RTCRtpSender.replaceTrack()](https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/replaceTrack) を利用して既存のトラックと置き換えを行ってください。 ### 音声をミュートにできますか? Sora JavaScript SDK はトラックのミュートは行いません。 トラックのミュートを行いたい場合は [MediaStreamTrack.enabled](https://developer.mozilla.org/ja/docs/Web/API/MediaStreamTrack/enabled) を利用して、ミュートを行ってください。 ### Electron で利用したいです Sora JavaScript SDK は [Electron](https://www.electronjs.org/) でも利用可能です。 ### MediaStreamTrack から MediaStream の ID を取得したいです MediaStreamTrack から MediaStream の ID は取得できません。 そのため、もし取得したい場合は何かしらの方法で ID を保持しておく必要があります。 ただし `removetrack` コールバックは MediaStream が発火させるため `event.target` で発火した MediaStream を取得可能です。 [MediaStream: removetrack event - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/removetrack_event) ### CPU や帯域による解像度やフレームレートの制限を無効にしたいです 無効にできません。 ブラウザで CPU 負荷が高まったと判断して、利用ビットレートを下げたことにより解像度やフレームレートが下がる場合、 Chrome 102 までは `googCpuOveruseDetection` という設定があり、 `false` にすることで、判定を無効にできていましたが、 その設定は Chrome 103 にて廃止されました。 > **ヒント** > > 制限の理由は qualityLimitationReason で確認できます。 - [PSA: RTCPeerConnection's mediaConstraints - most constraints removed in M103](https://groups.google.com/g/discuss-webrtc/c/2l7jWgreYpw/m/v_TdkRfTAAAJ) - [PSA: RTCPeerConnection's mediaConstraints is deprecated and will soon be removed](https://groups.google.com/g/discuss-webrtc/c/85e-f_siCws/m/4yWismzCAQAJ) ### 映像を回転させたいです getUserMedia で取得した映像を回転させるには [Canvas API](https://developer.mozilla.org/ja/docs/Web/API/Canvas_API) を利用してください。 ```html
``` ```typescript // getUserMedia で取得した映像を Canvas API を利用して 90 度回転する document.addEventListener("DOMContentLoaded", async () => { const originalVideo = document.getElementById("originalVideo") as HTMLVideoElement; const rotatedVideo = document.getElementById("rotatedVideo") as HTMLVideoElement; const mediaStream = await navigator.mediaDevices.getUserMedia({ video: true, }); originalVideo.srcObject = mediaStream; const rotatedMediaStream = rotateMediaStream(mediaStream); rotatedVideo.srcObject = rotatedMediaStream; }); const rotateMediaStream = (mediaStream: MediaStream): MediaStream => { // 元の映像トラックを取得 const videoTrack = mediaStream.getVideoTracks()[0]; // 映像の設定を取得 const settings = videoTrack.getSettings(); const width = settings.width || 640; const height = settings.height || 480; // Canvas要素を作成 const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); // 90度回転させるため、幅と高さを入れ替える canvas.width = height; canvas.height = width; // Video要素を作成 const video = document.createElement("video"); video.srcObject = mediaStream; video.autoplay = true; // 映像フレームを描画して回転させる video.addEventListener("play", () => { const draw = () => { if (ctx) { // Canvasを90度回転 ctx.save(); ctx.translate(canvas.width, 0); ctx.rotate(Math.PI / 2); ctx.drawImage(video, 0, 0, width, height); ctx.restore(); } requestAnimationFrame(draw); }; draw(); }); // 回転した映像をMediaStreamとして取得 return canvas.captureStream(); }; ``` # 廃止機能 ## 概要 Sora JavaScript SDK では機能互換性を最優先にしているため、 Sora JavaScript SDK の機能を廃止することは基本的にありません。 しかし Sora 側で廃止された機能は Sora JavaScript SDK 側でも廃止します。 ## レガシーストリーム機能の廃止について *バージョン 2025.1.0 で削除。* Sora 2025.1.0 にてレガシーストリーム機能を廃止しました。 それに伴い Sora JavaScript SDK でもレガシーストリーム機能を廃止しました。 具体的には `multistream` オプション自体を廃止することで、 `multistream` オプションに `false` を指定できなくなりました。 ## multistream オプションの廃止について *バージョン 2025.1.0 で削除。* Sora 2025.1.0 にてレガシーストリーム機能を廃止しました。 それに伴い Sora JavaScript SDK 2025.1.0 からレガシー機能対応を終了し、 `multistream` オプションを廃止しました。 もし `multistream` オプションを指定している場合は、 `options` から `multistream` 項目を削除して利用するようにしてください。 `multistream` オプションを指定しない場合は何もする必要がありません。 ```typescript sora = Sora.connection(signalingUrl, debug); connection = sora.sendonly(channelId, undefined, { // multistream は廃止されたオプションなので、この項目を削除してください multistream: true, }); ``` - 2017 年にリリースした Sora JavaScript SDK で `multistream` オプションを追加しました - 2022 年にリリースした Sora JavaScript SDK で `multistream` オプションのデフォルト値を `true` を変更しました ## addstream コールバックの廃止について *バージョン 2024.2.0 で削除。* Sora JavaScript SDK 2020.1.0 にて `addstream` コールバックを廃止宣言しました。 廃止宣言から 4 年以上が経過したため Sora JavaScript SDK 2024.2.0 で `addstream` コールバックを廃止しました。 [onaddstream](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream) はブラウザでも既に非推奨から非標準とされています。 ## removestream コールバックの廃止について *バージョン 2024.2.0 で削除。* Sora JavaScript SDK 2020.1.0 にて `removestream` コールバックを廃止宣言しました。 廃止宣言から 4 年以上が経過したため Sora JavaScript SDK 2024.2.0 で `removestream` コールバックを廃止しました。 [onremovestream](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/removeStream) はブラウザでも既に非推奨かつ非標準とされています。 # インストール ## 動作環境 - Node.js 22.18.0 以上 - ESM 専用パッケージ (CommonJS 非対応) - 対応ブラウザは [対応ブラウザについて](faq.html#7e59ef) を参照 ## インストール方法 ### npm ```console $ npm add sora-js-sdk ``` ### pnpm ```console $ pnpm add sora-js-sdk ``` ## 使用方法 ```javascript import Sora from "sora-js-sdk"; ``` # チュートリアル 本章では Sora JavaScript SDK を使って音声と映像を送受信できる簡単なサンプルを作成します。 ## プロジェクトの作成 開発環境ツールとして [Vite](https://vite.dev/) を利用します。 無理に Vite を利用する必要は無く、慣れたツールを利用してください。 パッケージマネージャーとしては [pnpm](https://pnpm.io/) を利用していますが、 npm や [yarn](https://yarnpkg.com/) でも問題ありません。 ```console $ pnpm create vite@latest ✔ Project name: … sora-js-sdk-tutorial ✔ Select a framework: › Vanilla ✔ Select a variant: › TypeScript Scaffolding project in /private/tmp/sora-js-sdk-tutorial... Done. Now run: cd sora-js-sdk-tutorial pnpm install pnpm run dev ``` ``` tree . ├── index.html ├── package.json ├── public │   └── vite.svg ├── src │   ├── counter.ts │   ├── main.ts │   ├── style.css │   ├── typescript.svg │   └── vite-env.d.ts └── tsconfig.json ``` ## sora-js-sdk の追加 ```console $ pnpm add -E sora-js-sdk jose ``` ### jose について [jose](https://github.com/panva/jose) は [Sora Labo](https://sora-labo.shiguredo.app/) や [Sora Cloud](https://sora-cloud.shiguredo.jp/) を利用する場合の JWT 生成に必要になります。 ## Vite と TypeScript を最新にする ```console $ pnpm up vite@latest typescript@latest ``` ## 不要なファイルの削除 以下のファイルは利用しないため削除してください。 - public/vite.svg - src/counter.ts - src/typescript.svg - src/style.css ## index.html の変更 - `connect` は接続ボタン - `disconnect` は切断ボタン - `local-video` は自分が取得した映像を出力する - `remote-videos` は他の映像を表示する ```html Sora JS SDK Tutorial

``` ## .env.local の作成 .env.local ファイルを作成してください。 ```console $ touch .env.local ``` 環境変数を設定してください。 ### Sora を自前で立てる場合 ```bash # Sora のシグナリング URL VITE_SORA_SIGNALING_URL=wss://{host}/signaling # 好きな文字列 VITE_SORA_CHANNEL_ID=tutorial ``` ### Sora Cloud ```bash # Sora Cloud のシグナリング URL VITE_SORA_SIGNALING_URL=wss://sora.sora-cloud.shiguredo.app/signaling # 好きな文字列 VITE_SORA_CHANNEL_ID=tutorial # Sora Cloud のダッシュボードから取得できる @ + プロジェクト ID VITE_SORA_CHANNEL_ID_SUFFIX=@{project_id} # Sora Cloud のダッシュボードから取得できる API キー VITE_SECRET_KEY={api_key} ``` ### Sora Labo ```bash # Sora Labo のシグナリング URL VITE_SORA_SIGNALING_URL=wss://sora.sora-labo.shiguredo.app/signaling # 好きな文字列 VITE_SORA_CHANNEL_ID=tutorial # Sora Labo のダッシュボードから取得できるチャネル ID プレフィックス # {GitHub ユーザ名}_{GitHub ID}_ VITE_SORA_CHANNEL_ID_PREFIX={github_username}_{github_id}_ # Sora Labo のダッシュボードから取得できるシークレットキー VITE_SECRET_KEY={secret_key} ``` ## src/client.ts の追加 Sora JavaScript SDK を利用したクライアント、 Sora Client クラスを作成します。 ```typescript import { SignJWT } from "jose"; import Sora, { type ConnectionOptions, type SoraConnection, type ConnectionPublisher, } from "sora-js-sdk"; // Sora JavaScript SDK を利用した Sora クライアント class SoraClient { private debug = false; private channelId: string; private options: ConnectionOptions; private secretKey: string; private sora: SoraConnection; private connection: ConnectionPublisher; constructor( signalingUrl: string, channelId: string, secretKey: string, options: ConnectionOptions = {}, ) { this.channelId = channelId; this.options = options; this.secretKey = secretKey; this.sora = Sora.connection(signalingUrl, this.debug); // metadata はここでは undefined にして connect 時に指定する this.connection = this.sora.sendrecv(this.channelId, undefined, this.options); this.connection.on("track", this.onTrack); this.connection.on("removetrack", this.removeTrack); } async connect(stream: MediaStream) { // SecretKey が指定されていたら JWT を生成して metadata に設定する if (this.secretKey) { const jwt = await this.generateJwt(); this.connection.metadata = { access_token: jwt, }; } // 接続する await this.connection.connect(stream); } async disconnect() { // 切断する await this.connection.disconnect(); } private onTrack = (event: RTCTrackEvent) => { const stream = event.streams[0]; const remoteVideoId = `remote-video-${stream.id}`; const remoteVideos = document.querySelector("#remote-videos"); if (remoteVideos && !remoteVideos.querySelector(`#${remoteVideoId}`)) { const remoteVideo = document.createElement("video"); remoteVideo.id = remoteVideoId; remoteVideo.style.border = "1px solid red"; remoteVideo.autoplay = true; remoteVideo.playsInline = true; remoteVideo.controls = true; remoteVideo.width = 320; remoteVideo.height = 240; remoteVideo.srcObject = stream; remoteVideos.appendChild(remoteVideo); } }; private removeTrack = (event: MediaStreamTrackEvent) => { const target = event.target as MediaStream; const remoteVideo = document.getElementById(`remote-video-${target.id}`) as HTMLVideoElement; if (remoteVideo) { remoteVideo.remove(); } }; private generateJwt = async (): Promise => { return new SignJWT({ channel_id: this.channelId, }) .setProtectedHeader({ alg: "HS256", typ: "JWT" }) .setExpirationTime("30s") .sign(new TextEncoder().encode(this.secretKey)); }; } export default SoraClient; ``` ## src/vite-env.d.ts の変更 ```typescript /// interface ImportMetaEnv { VITE_SORA_SIGNALING_URL: string; VITE_SORA_CHANNEL_ID: string; VITE_SORA_CHANNEL_ID_PREFIX: string; VITE_SORA_CHANNEL_ID_SUFFIX: string; VITE_SECRET_KEY: string; } interface ImportMeta { readonly env: ImportMetaEnv; } ``` ## src/main.ts の変更 Sora Client クラスを利用したコードを追加します。 ```typescript import SoraClient from "./client"; addEventListener("DOMContentLoaded", () => { // .env.local からシグナリング URL を取得する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; // .env.local からシークレットキーを取得する、無ければ空文字 const secretKey = import.meta.env.VITE_SECRET_KEY || ""; // チャネル ID を生成する const channelId = generateChannelId(); // SoraClient を生成する const client = new SoraClient(signalingUrl, channelId, secretKey); // connect ボタンを押した時の処理 document.getElementById("connect")?.addEventListener("click", async () => { // getUserMedia でカメラから映像を取得する const stream = await navigator.mediaDevices.getUserMedia({ // 音声は無効 audio: false, video: true, }); await client.connect(stream); const localVideo = document.getElementById("local-video") as HTMLVideoElement; if (localVideo) { localVideo.srcObject = stream; } }); document.getElementById("disconnect")?.addEventListener("click", async () => { if (client === undefined) { return; } // sendrecv があるかどうか確認する // 切断する await client.disconnect(); const localVideo = document.getElementById("local-video") as HTMLVideoElement; if (localVideo) { localVideo.srcObject = null; } const remoteVideos = document.getElementById("remote-videos") as HTMLDivElement; if (remoteVideos) { while (remoteVideos.firstChild) { remoteVideos.firstChild.remove(); } } }); }); // 環境変数からチャネル ID を生成する export const generateChannelId = (): string => { const channelId = import.meta.env.VITE_SORA_CHANNEL_ID || ""; const channelIdPrefix = import.meta.env.VITE_SORA_CHANNEL_ID_PREFIX || ""; const channelIdSuffix = import.meta.env.VITE_SORA_CHANNEL_ID_SUFFIX || ""; // 環境変数の channelId が指定されていない場合はエラー if (!channelId) { throw new Error("VITE_SORA_CHANNEL_ID is not set"); } // channelIdPrefix と channelIdSuffix が指定されている場合はそれを利用する if (channelIdPrefix && channelIdSuffix) { return `${channelIdPrefix}${channelId}${channelIdSuffix}`; } // channelIdPrefix が指定されている場合はそれを利用する if (channelIdPrefix) { return `${channelIdPrefix}${channelId}`; } // channelIdSuffix が指定されている場合はそれを利用する if (channelIdSuffix) { return `${channelId}${channelIdSuffix}`; } return channelId; }; ``` ## 起動 ```console $ pnpm run dev VITE v8.0.16 ready in 85 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose ➜ press h + enter to show help ``` `http://localhost:5173/` へアクセスして、ブラウザのタブをふたつ以上開いて、 `Connect ボタン` を押して、双方向で配信ができていれば成功です。 # サンプル ## 概要 ここでは Sora JavaScript SDK を利用したサンプルを紹介します。 ## Vite このサンプルでは [Vite](https://vite.dev/) を使用して、環境変数から接続先の情報を取得するようにしています。 [Env Variables and Modes | Vite](https://vite.dev/guide/env-and-mode) ### .env または .env.local ```bash # Sora シグナリング URL VITE_SORA_SIGNALING_URL= # Sora チャネル ID VITE_SORA_CHANNEL_ID= # Sora チャネル ID プレフィックス VITE_SORA_CHANNEL_ID_PREFIX= # Sora チャネル ID サフィックス VITE_SORA_CHANNEL_ID_SUFFIX= # JWT 生成用のシークレットキー VITE_SECRET_KEY= ``` ### VITE_SORA_SIGNALING_URL Sora のシグナリング URL を指定してください。 - Sora 自前の場合は設定済のシグナリング URL を指定してください- `VITE_SORA_SIGNALING_URL=wss://sora.example.com/signaling` - Sora Labo の場合は `wss://sora.sora-labo.shiguredo.app/signaling` を指定してください- `VITE_SORA_SIGNALING_URL=wss://sora.sora-labo.shiguredo.app/signaling` - Sora Cloud の場合は `wss://sora.sora-cloud.shiguredo.app/signaling` を指定してください- `VITE_SORA_SIGNALING_URL=wss://sora.sora-cloud.shiguredo.app/signaling` ### VITE_SORA_CHANNEL_ID 好きな文字列を指定してください。 ### VITE_SORA_CHANNEL_ID_PREFIX **Sora Labo はこちらの環境変数を設定してください** Sora のチャネル ID プレフィックスを指定してください。 `{github_username}_{github_id}_` を指定してください。 例えば github_username が spam で github_id が 1234567890 の場合、 チャネル ID プレフィックスは `spam_1234567890_` となります ```bash # 例 # VITE_SORA_CHANNEL_ID_PREFIX=spam_1234567890_ VITE_SORA_CHANNEL_ID_PREFIX={github_username}_{github_id}_ ``` ### VITE_SORA_CHANNEL_ID_SUFFIX **Sora Labo はこちらの環境変数を設定してください** Sora Cloud のチャネル ID サフィックスを指定してください。 - `@{project_id}` を指定してください- `VITE_SORA_CHANNEL_ID_SUFFIX=@{project_id}` ### VITE_SECRET_KEY **Sora Labo と Sora Cloud はこちらの環境変数を指定してください** JWT 生成用のシークレットキーを指定してください。 - Sora Labo の場合はシークレットキーを指定してください- `VITE_SECRET_KEY={secret_key}` - Sora Cloud の場合は API キーを指定してください- `VITE_SECRET_KEY={api_key}` ### vite-env.d.ts ```typescript /// interface ImportMetaEnv { VITE_SORA_SIGNALING_URL: string; VITE_SORA_CHANNEL_ID: string; VITE_SORA_CHANNEL_ID_PREFIX: string; VITE_SORA_CHANNEL_ID_SUFFIX: string; VITE_SECRET_KEY: string; } interface ImportMeta { readonly env: ImportMetaEnv; } ``` ## jose [Sora Labo](https://sora-labo.shiguredo.app/) や [Sora Cloud](https://sora-cloud.shiguredo.jp/) では接続に JWT を利用しています。 このドキュメントでは JWT を生成するのに [jose](https://github.com/panva/jose) というライブラリを利用しています。 ### インストール ```console # npm $ npm install jose ``` ```console # pnpm $ pnpm add jose ``` ### JWT 生成するコード ```typescript // jose をインストールしていることを前提としています import { SignJWT } from "jose"; // channel_id と secret_key を指定して JWT を生成する export const generateJwt = async (channelId: string, secretKey: string): Promise => { // HS256 を利用しています return ( new SignJWT({ // channel_id を Claim に設定しています channel_id: channelId, }) // JWT のヘッダーを設定しています .setProtectedHeader({ alg: "HS256", typ: "JWT" }) // JWT の期限を 30 秒に設定しています .setExpirationTime("30s") // JWT を生成しています .sign(new TextEncoder().encode(secretKey)) ); }; ``` ## チャネル ID の生成 環境変数を利用してチャネル ID を生成します。 ```typescript export const generateChannelId = (): string => { // qs を確認する const urlParams = new URLSearchParams(window.location.search); const qsChannelId = urlParams.get("channelId") || ""; const qsChannelIdPrefix = urlParams.get("channelIdPrefix") || ""; const qsChannelIdSuffix = urlParams.get("channelIdSuffix") || ""; // qs が指定されていればその値を優先するようにする const channelId = qsChannelId || import.meta.env.VITE_SORA_CHANNEL_ID || ""; const channelIdPrefix = qsChannelIdPrefix || import.meta.env.VITE_SORA_CHANNEL_ID_PREFIX || ""; const channelIdSuffix = qsChannelIdSuffix || import.meta.env.VITE_SORA_CHANNEL_ID_SUFFIX || ""; // 環境変数の channelId が指定されていない場合はエラー if (!channelId) { throw new Error("VITE_SORA_CHANNEL_ID is not set"); } // channelIdPrefix と channelIdSuffix が指定されている場合はそれを利用する if (channelIdPrefix && channelIdSuffix) { return `${channelIdPrefix}${channelId}${channelIdSuffix}`; } // channelIdPrefix が指定されている場合はそれを利用する if (channelIdPrefix) { return `${channelIdPrefix}${channelId}`; } // channelIdSuffix が指定されている場合はそれを利用する if (channelIdSuffix) { return `${channelId}${channelIdSuffix}`; } return channelId; }; ``` ## 送受信で接続する 自分から音声、映像を配信し、他の参加者から音声、映像を受信する場合は [sendrecv()](https://shiguredo.github.io/sora-js-sdk/interfaces/SoraConnection.html#sendrecv) を使用して接続します。 マルチストリームで接続した [Connection オブジェクト](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html) は接続したチャネル ID に MediaStream が追加、削除されると track コールバックが呼ばれます。 コールバックを利用して追加、削除された MediaStream を処理します。 ```html
``` ```typescript import Sora, { type SoraConnection, type ConnectionPublisher } from "sora-js-sdk"; import { generateJwt } from "../jwt"; import { generateChannelId } from "../sora"; document.addEventListener("DOMContentLoaded", (_event) => { // Vite を利用して env.local から取得 const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const secretKey = import.meta.env.VITE_SECRET_KEY; const channelId = generateChannelId(); const soraClient = new SoraClient(signalingUrl, channelId, secretKey); document.querySelector("#connect")?.addEventListener("click", async () => { await soraClient.connect(); }); document.querySelector("#disconnect")?.addEventListener("click", async () => { await soraClient.disconnect(); }); }); class SoraClient { private debug = true; private signalingUrl: string; private channelId: string; private secretKey: string; private sora: SoraConnection; private connection: ConnectionPublisher; private streams: Record = {}; constructor(signalingUrl: string, channelId: string, secretKey: string) { this.signalingUrl = signalingUrl; this.channelId = channelId; this.secretKey = secretKey; // 接続先の Sora を設定する this.sora = Sora.connection(this.signalingUrl, this.debug); const options = { audio: true, video: true, }; this.connection = this.sora.sendrecv(this.channelId, undefined, options); // ontrack イベント // メディアストリームトラック単位で発火する this.connection.on("track", this.onaddtrack.bind(this)); // removetrack イベント (リモートメディアストリームが削除されたときに発生) this.connection.on("removetrack", this.onremovetrack.bind(this)); } async connect() { // オーディオとビデオのストリームを取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); const localVideElement = document.querySelector("#localVideo"); if (localVideElement !== null) { localVideElement.srcObject = stream; } // SecretKey があれば JWT を設定する if (this.secretKey) { const jwt = await generateJwt(this.channelId, this.secretKey); this.connection.metadata = { // Sora では特に "access_token" と決まっているわけではありません // access_token は Sora Labo や Sora Cloud の想定です access_token: jwt, }; } // 接続 await this.connection.connect(stream); } async disconnect() { // 切断 await this.connection.disconnect(); // ローカルビデオを削除 const localVideo = document.querySelector("#localVideo"); if (localVideo !== null) { localVideo.srcObject = null; } // リモートビデオを削除 const remoteVideos = document.querySelector("#remoteVideos"); remoteVideos?.remove(); } // 統計情報を取得できるようにする async getStats(): Promise { if (!this.connection.pc) { return null; } return this.connection.pc.getStats(); } private onaddtrack(event: RTCTrackEvent) { // 追加されたストリームを取得 // 注: Sora では 1 クライアント 1 音声/ 1 映像と決まっているため、 // ストリームが複数入ってこない const remoteStream = event.streams[0]; // リモートビデオエレメントを取得 const remoteVideos = document.querySelector("#remoteVideos"); // リモートビデオエレメントのIDを生成 const remoteVideoId = `remoteVideo-${remoteStream.id}`; // 既存のビデオエレメントが無ければ新たに作成 if (!remoteVideos?.querySelector(`#${remoteVideoId}`)) { const remoteVideo = document.createElement("video"); remoteVideo.id = remoteVideoId; remoteVideo.autoplay = true; remoteVideo.srcObject = remoteStream; remoteVideos?.appendChild(remoteVideo); } if (!this.streams[remoteStream.id]) { this.streams[remoteStream.id] = remoteStream; } } private onremovetrack(event: MediaStreamTrackEvent) { // target は removetrack が発火した MediaStream const target = event.target as MediaStream; const remoteVideo = document.querySelector(`#remoteVideo-${target.id}`); const remoteVideos = document.querySelector("#remoteVideos"); if (remoteVideo) { remoteVideos?.removeChild(remoteVideo); } if (this.streams[target.id]) { delete this.streams[target.id]; } } get getStreams(): Record { return this.streams; } } ``` ## 送信のみで接続する [sendonly()](https://shiguredo.github.io/sora-js-sdk/interfaces/SoraConnection.html#sendonly) を使用して接続します。 ```html
``` ```typescript import Sora, { type SoraConnection, type ConnectionPublisher } from "sora-js-sdk"; import { generateJwt } from "../jwt"; import { generateChannelId } from "../sora"; document.addEventListener("DOMContentLoaded", (_event) => { // Vite を利用して env.local から取得 const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const secretKey = import.meta.env.VITE_SECRET_KEY; const channelId = generateChannelId(); const soraClient = new SoraClient(signalingUrl, channelId, secretKey); document.querySelector("#connect")?.addEventListener("click", async () => { await soraClient.connect(); }); document.querySelector("#disconnect")?.addEventListener("click", async () => { await soraClient.disconnect(); }); }); class SoraClient { private debug = true; private signalingUrl: string; private channelId: string; private secretKey: string; private sora: SoraConnection; private connection: ConnectionPublisher; constructor(signalingUrl: string, channelId: string, secretKey: string) { this.signalingUrl = signalingUrl; this.channelId = channelId; this.secretKey = secretKey; // 接続先の Sora を設定する this.sora = Sora.connection(this.signalingUrl, this.debug); const options = {}; this.connection = this.sora.sendonly(this.channelId, undefined, options); // ontrack イベント // メディアストリームトラック単位で発火する this.connection.on("track", this.onaddtrack.bind(this)); // removetrack イベント (リモートメディアストリームが削除されたときに発生) this.connection.on("removetrack", this.onremovetrack.bind(this)); } async connect() { // オーディオとビデオのストリームを取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); const localVideElement = document.querySelector("#localVideo"); if (localVideElement !== null) { localVideElement.srcObject = stream; } // SecretKey があれば JWT を設定する if (this.secretKey) { const accessToken = await generateJwt(this.channelId, this.secretKey); this.connection.metadata = { access_token: accessToken, }; } // 接続 await this.connection.connect(stream); } async disconnect() { // 切断 await this.connection.disconnect(); // ローカルビデオを削除 const localVideElement = document.querySelector("#localVideo"); if (localVideElement !== null) { localVideElement.srcObject = null; } } private onaddtrack(event: RTCTrackEvent) { // 追加されたストリームを取得 // 注: Sora では 1 クライアント 1 音声/ 1 映像と決まっているため、 // ストリームが複数入ってこない const remoteStream = event.streams[0]; // リモートビデオエレメントを取得 const remoteVideos = document.querySelector("#remoteVideos"); // リモートビデオエレメントのIDを生成 const remoteVideoId = `remoteVideo-${remoteStream.id}`; // 既存のビデオエレメントが無ければ新たに作成 if (!remoteVideos?.querySelector(`#${remoteVideoId}`)) { const remoteVideo = document.createElement("video"); remoteVideo.id = remoteVideoId; remoteVideo.autoplay = true; remoteVideo.srcObject = remoteStream; remoteVideos?.appendChild(remoteVideo); } } private onremovetrack(event: MediaStreamTrackEvent) { // target は removetrack が発火した MediaStream const target = event.target as MediaStream; const remoteVideo = document.querySelector(`#remoteVideo-${target.id}`); const remoteVideos = document.querySelector("#remoteVideos"); if (remoteVideo) { remoteVideos?.removeChild(remoteVideo); } } } ``` ## 受信のみで接続する [recvonly()](https://shiguredo.github.io/sora-js-sdk/interfaces/SoraConnection.html#recvonly) を使用して接続します。 ```html
``` ```typescript import Sora, { type SoraConnection, type ConnectionSubscriber, type ConnectionOptions, } from "sora-js-sdk"; import { generateJwt } from "../jwt"; import { generateChannelId } from "../sora"; document.addEventListener("DOMContentLoaded", () => { // Vite を利用して env.local から取得 const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const secretKey = import.meta.env.VITE_SECRET_KEY; const channelId = generateChannelId(); const soraClient = new SoraClient(signalingUrl, channelId, secretKey); document.querySelector("#connect")?.addEventListener("click", async () => { await soraClient.connect(); }); document.querySelector("#disconnect")?.addEventListener("click", async () => { await soraClient.disconnect(); }); }); class SoraClient { private debug = false; private channelId: string; private options: ConnectionOptions; private secretKey: string; private sora: SoraConnection; private connection: ConnectionSubscriber; private streams: Record = {}; constructor( signalingUrl: string, channelId: string, secretKey: string, options: ConnectionOptions = {}, ) { this.channelId = channelId; this.secretKey = secretKey; this.options = options; // 接続先の Sora を設定する this.sora = Sora.connection(signalingUrl, this.debug); this.connection = this.sora.recvonly(this.channelId, undefined, this.options); // ontrack イベント // メディアストリームトラック単位で発火する this.connection.on("track", this.onaddtrack.bind(this)); // removetrack イベント (リモートメディアストリームが削除されたときに発生) this.connection.on("removetrack", this.onremovetrack.bind(this)); } async connect() { // SecretKey があれば JWT を設定する if (this.secretKey) { const jwt = await generateJwt(this.channelId, this.secretKey); this.connection.metadata = { // Sora では特に "access_token" と決まっているわけではありません // access_token は Sora Labo や Sora Cloud の想定です access_token: jwt, }; } // 接続 await this.connection.connect(); } async disconnect() { // 切断 await this.connection.disconnect(); // リモートビデオを全て削除 const remoteVideos = document.querySelector("#remote-videos"); if (remoteVideos) { remoteVideos.innerHTML = ""; } } async getStats(): Promise { if (!this.connection.pc) { throw new Error("missing pc"); } return this.connection.pc.getStats(); } private onaddtrack(event: RTCTrackEvent) { // 追加されたストリームを取得 // 注: Sora では 1 クライアント 1 音声/ 1 映像と決まっているため、 // ストリームが複数入ってこない const remoteStream = event.streams[0]; // リモートビデオエレメントを取得 const remoteVideos = document.querySelector("#remote-videos"); // リモートビデオエレメントのIDを生成 const remoteVideoId = `remote-video-${remoteStream.id}`; // 既存のビデオエレメントが無ければ新たに作成 if (!remoteVideos?.querySelector(`#${remoteVideoId}`)) { const remoteVideo = document.createElement("video"); remoteVideo.id = remoteVideoId; remoteVideo.autoplay = true; remoteVideo.srcObject = remoteStream; remoteVideos?.appendChild(remoteVideo); } if (!this.streams[remoteStream.id]) { this.streams[remoteStream.id] = remoteStream; } } private onremovetrack(event: MediaStreamTrackEvent) { // target は removetrack が発火した MediaStream const target = event.target as MediaStream; const remoteVideo = document.querySelector(`#remote-video-${target.id}`); const remoteVideos = document.querySelector("#remote-videos"); if (remoteVideo) { remoteVideos?.removeChild(remoteVideo); } if (this.streams[target.id]) { delete this.streams[target.id]; } } get getStreams(): Record { return this.streams; } } ``` ## 接続して 5 秒後に切断する [disconnect()](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html#disconnect) を使用して接続を切断します。 ```typescript import Sora from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // 接続先の Sora を設定する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const sora = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = {}; const client = sora.sendonly(channelId, undefined, options); // secretKey が指定されている場合は JWT を生成する const secretKey = import.meta.env.VITE_SECRET_KEY || ""; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); client.metadata = { access_token: jwt, }; } // オーディオとビデオのストリームを取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); await client.connect(stream); // 5 秒後に切断する setTimeout(async () => { await client.disconnect(); }, 5000); }; export default connectToSora; ``` ## サイマルキャストを有効にして接続する サイマルキャスト (Simulcast) は配信時に 1 つの RTCPeerConnection から複数種類のエンコードした映像を配信する機能です。 詳しくは Sora ドキュメント [サイマルキャスト](https://sora-doc.shiguredo.jp/SIMULCAST) をご確認ください。 [ConnectionBase オプション](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html#options) に `simulcast: true` を指定します ```typescript import Sora, { type VideoCodecType } from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // 接続先の Sora を設定する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const sora = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = { simulcast: true, videoCodecType: "VP8" as VideoCodecType, }; const sendonly = sora.sendonly(channelId, undefined, options); const secretKey = import.meta.env.VITE_SECRET_KEY || ""; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); sendonly.metadata = { access_token: jwt, }; } // オーディオとビデオのストリームを取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); await sendonly.connect(stream); }; export default connectToSora; ``` ## スポットライトで接続する スポットライト (Spotlight) は一定の音量を超えて音声を発している参加者の場合は音声や高画質映像を、それ以外の参加者は音声のない低画質映像を配信する機能です。 詳しくは Sora ドキュメント [スポットライト](https://sora-doc.shiguredo.jp/SPOTLIGHT) をご確認ください。 [ConnectionBase オプション](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html#options) に `spotlight: true` を指定します ```typescript import Sora, { type VideoCodecType } from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // 接続先の Sora を設定する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const sora = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = { simulcast: true, // スポットライト機能を有効にする spotlight: true, videoCodecType: "VP8" as VideoCodecType, }; const client = sora.sendonly(channelId, undefined, options); // シークレットキーが設定されている場合は JWT を設定する const secretKey = import.meta.env.VITE_SECRET_KEY || ""; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); client.metadata = { access_token: jwt, }; } // オーディオとビデオのストリームを取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); await client.connect(stream); }; export default connectToSora; ``` ## 音声や映像コーデックを指定する 音声や映像のコーデックタイプは [ConnectionBase オプション](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html#options) で指定します ```typescript import Sora, { type AudioCodecType, type VideoCodecType } from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // 接続先の Sora を設定する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const soraConnection = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = { // 音声コーデックに Opus を指定 audioCodecType: "OPUS" as AudioCodecType, // 映像コーデックに VP9 を指定 videoCodecType: "VP9" as VideoCodecType, }; const sendrecv = soraConnection.sendrecv(channelId, undefined, options); const secretKey = import.meta.env.VITE_SECRET_KEY || ""; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); sendrecv.metadata = { access_token: jwt, }; } // オーディオとビデオのストリームを取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); await sendrecv.connect(stream); }; export default connectToSora; ``` - [音声コーデックタイプ](https://shiguredo.github.io/sora-js-sdk/types/AudioCodecType.html) - [映像コーデックタイプ](https://shiguredo.github.io/sora-js-sdk/types/VideoCodecType.html) ## 音声や映像のビットレートを指定する **音声のビットレート指定は推奨しません** 音声や映像のビットレートは [ConnectionBase オプション](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html#options) で指定します ```typescript import Sora from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // Sora への接続を作成 const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const soraConnection = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = { audio: true, // オーディオのビットレートを 64 kbps に設定 audioBitRate: 64, video: true, // ビデオのビットレートを 192 kbps に設定 videoBitRate: 192, }; const sendrecv = soraConnection.sendrecv(channelId, undefined, options); const secretKey = import.meta.env.VITE_SECRET_KEY || ""; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); sendrecv.metadata = { access_token: jwt, }; } // オーディオとビデオのストリームを取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); await sendrecv.connect(stream); }; export default connectToSora; ``` - 音声ビットレートに指定できる範囲は 6-510 です - 映像ビットレートに指定できる範囲は 1-30000 です ## 映像コーデックパラメーターを指定する [ConnectionBase オプション](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html#options) で指定します。 ```typescript import Sora, { type VideoCodecType } from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // 接続先の Sora を設定する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const soraConnection = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = { video: true, videoCodecType: "VP9" as VideoCodecType, // VP9 の 映像パラメーターで profile-id を 2 に指定 // これを利用するには sora.conf にて signaling_vp9_params = true を設定する必要がある videoVp9Params: { profileId: 2, }, }; const sendonly = soraConnection.sendonly(channelId, undefined, options); // secretKey が設定されている場合は JWT を設定する const secretKey = import.meta.env.VITE_SECRET_KEY; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); sendonly.metadata = { access_token: jwt, }; } // オーディオとビデオのストリームを取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); await sendonly.connect(stream); }; export default connectToSora; ``` AV1 や H.264 や H.265 でも指定可能です。 ## 映像と音声の可否を指定する [ConnectionBase オプション](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html#options) で指定します。 音声なし ```typescript import Sora from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // Sora への接続を作成 const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const soraConnection = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = { // オーディオは不要 audio: false, }; const sendrecv = soraConnection.sendrecv(channelId, undefined, options); const secretKey = import.meta.env.VITE_SECRET_KEY; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); sendrecv.metadata = { access_token: jwt, }; } // ユーザーメディア(ビデオのみ)を取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: false, video: true, }); await sendrecv.connect(stream); }; export default connectToSora; ``` 映像なし ```typescript import Sora from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // ユーザーメディア(オーディオのみ)を取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false, }); // 接続先の Sora を設定する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const soraConnection = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = { // ビデオは不要 video: false, }; const sendonly = soraConnection.sendonly(channelId, undefined, options); // シークレットキーが設定されている場合は JWT を設定する const secretKey = import.meta.env.VITE_SECRET_KEY; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); sendonly.metadata = { access_token: jwt, }; } await sendonly.connect(stream); }; export default connectToSora; ``` ## クライアントIDを指定する 接続時やサーバー認証成功時に任意の文字列を指定できる値です。 詳しくは [Sora ドキュメント WebSocket 経由のシグナリング client_id](https://sora-doc.shiguredo.jp/SIGNALING#cbaf08) を参照してください。 [ConnectionBase オプション](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html#options) で指定します。 ```typescript import Sora from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // Sora への接続を作成 const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const soraConnection = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = { // client-id に xyz を指定 // client-id は重複可能 clientId: "xyz", }; const sendrecv = soraConnection.sendrecv(channelId, undefined, options); const secretKey = import.meta.env.VITE_SECRET_KEY || ""; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); sendrecv.metadata = { access_token: jwt, }; } // オーディオとビデオのストリームを取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); await sendrecv.connect(stream); }; export default connectToSora; ``` ## DataChannel 経由のシグナリングを使用する WebRTC 接続確立後に、シグナリングを WebSocket 経由から DataChannel 経由に切り替える機能です。 DataChannel 経由のシグナリングを利用するには、接続時に `dataChannelSignaling: true` を指定するか、認証成功時に `data_channel_signaling` を `true` を設定するか、 `sora.conf` にて `default_data_channel_signaling` を `true` に設定する必要があります。 詳しくは [Sora ドキュメント DataChannel 経由のシグナリング](https://sora-doc.shiguredo.jp/DATA_CHANNEL_SIGNALING) を参照してください。 [ConnectionBase オプション](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html#options) で指定します。 ```typescript import Sora, { type SignalingSwitchedMessage } from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // 接続先の Sora を設定する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const sora = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = { // シグナリングを WebSocket 経由から DataChannel 経由に切り替えるかどうかを指定 dataChannelSignaling: true, // シグナリングを DataChannel 経由に切り替えたあとに WebSocket を切断するかどうかを指定 // true にした場合、 Sora JS SDK は自動で WebSocket を切断します ignoreDisconnectWebSocket: false, }; const client = sora.sendrecv(channelId, undefined, options); client.on("switched", (event: SignalingSwitchedMessage) => { console.log("Switched signaling to DataChannel:", event); }); const secretKey = import.meta.env.VITE_SECRET_KEY || ""; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); client.metadata = { access_token: jwt, }; } const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); await client.connect(mediaStream); }; export default connectToSora; ``` ## メッセージング機能を使用する DataChannel を利用したデータの送受信を行える機能です。 詳しくは Sora ドキュメントの [リアルタイムメッセージング機能](https://sora-doc.shiguredo.jp/MESSAGING) をご確認ください。 [ConnectionBase オプション](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html#options) で指定します。 ```typescript import Sora, { type DataChannelDirection, type SignalingNotifyConnectionCreated, type SignalingSwitchedMessage, } from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // 接続先の Sora を設定する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const sora = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = { // メッセージング機能を利用するには、データチャネルを利用したシグナリングを有効にする必要がある dataChannelSignaling: true, dataChannels: [ { // メッセージングのラベルは # から始める必要がある label: "#example", // メッセージングの方向、sendrecv は送受信可能 // sendonly の場合は送信のみ可能 // recvonly の場合は受信のみ可能 direction: "sendrecv" as DataChannelDirection, // https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/maxPacketLifeTime // https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/maxRetransmits // maxPacketLifeTime か maxResends のどちらかしか指定できない maxPacketLifeTime: 60_000, // https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/ordered ordered: true, }, ], }; const sendrecv = sora.sendrecv(channelId, undefined, options); const secretKey = import.meta.env.VITE_SECRET_KEY || ""; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); sendrecv.metadata = { access_token: jwt, }; } // connected コールバック(自分の接続確立時に 1 度だけ発火) sendrecv.on("connected", (event: SignalingNotifyConnectionCreated) => { console.log("connected"); console.log("self-connection_id:", event.connection_id); }); // switched コールバック(DataChannel シグナリングへの切り替え完了時に発火) sendrecv.on("switched", (event: SignalingSwitchedMessage) => { console.log("switched"); console.log("ignore_disconnect_websocket:", event.ignore_disconnect_websocket); }); // メッセージ送信が可能な datachannel との接続が確立した場合に on datachannel イベントが発火する sendrecv.on("datachannel", async (event) => { // event.datachannel にメッセージ送信可能な datachannel の情報が含まれる const label = event.datachannel.label; // #example console.log("ondatachannel", label); // メッセージを送信する await sendrecv.sendMessage(label, new TextEncoder().encode("Hello world.")); }); // メッセージを受信した際に on message イベントが発火する sendrecv.on("message", (event) => { const label = event.label; const data = event.data; console.log("onmessage", label, data); }); const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); await sendrecv.connect(mediaStream); console.log("Connected to Sora"); }; // Soraへの接続とメッセージ送受信を開始 export default connectToSora; ``` メッセージングのみで接続することも可能です。 ```typescript import Sora, { type DataChannelDirection, type SignalingNotifyConnectionCreated, type SignalingSwitchedMessage, } from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // 接続先の Sora を設定する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const sora = Sora.connection(signalingUrl, debug); // ConnectionOptions の dataChannels を指定する const channelId = generateChannelId(); const options = { // データチャネルメッセージオンリーを使う場合は設定が必要 // https://sora-doc.shiguredo.jp/MESSAGING#3a3616 audio: false, video: false, // DataChannel シグナリングを利用する dataChannelSignaling: true, // データチャネルメッセージングの定義を追加 dataChannels: [ { label: "#example", direction: "sendrecv" as DataChannelDirection, }, ], }; // Sora 2023.2.0 から role が sendrecv 以外でもメッセージングオンリーが利用可能になった const messaging = sora.messaging(channelId, undefined, options); const secretKey = import.meta.env.VITE_SECRET_KEY || ""; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); messaging.metadata = { access_token: jwt, }; } // connected コールバック(自分の接続確立時に 1 度だけ発火) messaging.on("connected", (event: SignalingNotifyConnectionCreated) => { console.log("connected"); console.log("self-connection_id:", event.connection_id); }); // switched コールバック(DataChannel シグナリングへの切り替え完了時に発火) messaging.on("switched", async (event: SignalingSwitchedMessage) => { console.log("switched"); console.log("ignore_disconnect_websocket:", event.ignore_disconnect_websocket); // switched 後にメッセージ送信が可能 await messaging.sendMessage("#example", new TextEncoder().encode("Hello world.")); }); await messaging.connect(); // Sora 2023.2.0 以前のバージョンでは sendrecv で接続する必要がある // もし sendrecv や sendonly を利用する場合は空のメディアストリームを渡す // await sendrecv.connect(new MediaStream()); }; export default connectToSora; ``` ## RPC 機能を使用する DataChannel を利用した RPC 機能です。 RPC 機能を利用するには、DataChannel シグナリング機能を有効にし、さらに Sora 側出認証成功時に `rpc_methods` で利用できる RPC メソッド一覧を指定する必要があります。 詳しくは Sora ドキュメントの [RPC 機能](https://sora-doc.shiguredo.jp/RPC) をご確認ください。 [ConnectionBase オプション](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html#options) で指定します。 ```typescript import Sora, { type RPCOptions, type SignalingPushMessage } from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // 接続先の Sora を設定する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const sora = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = { // RPC 機能を利用するには、データチャネルを利用したシグナリングを有効にする必要がある dataChannelSignaling: true, }; const sendrecv = sora.sendrecv(channelId, undefined, options); const secretKey = import.meta.env.VITE_SECRET_KEY || ""; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); sendrecv.metadata = { access_token: jwt, }; } // RPC のレスポンスを受信するために push イベントをハンドリングする sendrecv.on("push", (event: SignalingPushMessage) => { console.log("push", event.data); }); const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); await sendrecv.connect(mediaStream); console.log("Connected to Sora"); // RPC を送信する // 利用可能な RPC メソッドは認証成功時に rpc_methods として払い出される const rpcMethod = "2025.2.0/RequestSimulcastRid"; const rpcParams = { rid: "r1", }; const rpcOptions: RPCOptions = { // notification を true にするとレスポンスを待たない notification: true, // タイムアウト時間をミリ秒で指定 (オプション) timeout: 5000, }; await sendrecv.rpc(rpcMethod, rpcParams, rpcOptions); console.log("RPC sent successfully"); }; // Sora への接続と RPC 送信を開始 export default connectToSora; ``` ## シグナリング通知機能を使用する シグナリング通知機能は自分の接続状況や他の接続の参加や離脱などの通知する仕組みです。 詳しくは Sora ドキュメント の [シグナリング通知](https://sora-doc.shiguredo.jp/SIGNALING_NOTIFY) をご確認ください。 ```typescript import Sora, { type SignalingNotifyConnectionCreated } from "sora-js-sdk"; import { generateJwt } from "./jwt"; import { generateChannelId } from "./sora"; const connectToSora = async () => { // 接続先の Sora を設定する const signalingUrl = import.meta.env.VITE_SORA_SIGNALING_URL; const debug = true; const sora = Sora.connection(signalingUrl, debug); const channelId = generateChannelId(); const options = {}; const sendonly = sora.sendonly(channelId, undefined, options); const secretKey = import.meta.env.VITE_SECRET_KEY || ""; if (secretKey) { const jwt = await generateJwt(channelId, secretKey); sendonly.metadata = { access_token: jwt, }; } // connected コールバック(自分の接続確立時に 1 度だけ発火) sendonly.on("connected", (event: SignalingNotifyConnectionCreated) => { console.log("connected"); console.log("self-connection_id:", event.connection_id); }); // オーディオとビデオのストリームを取得 const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); await sendonly.connect(stream); }; export default connectToSora; ``` # API リファレンス API リファレンスは [apidoc](https://shiguredo.github.io/sora-js-sdk/modules.html) を参照ください。 ## Sora Sora JS SDK メインオブジェクトです。 API リファレンスは [Sora](https://shiguredo.github.io/sora-js-sdk/variables/default.html) を参照ください。 ## SoraConnection Role、 ChannelId、 Metadata、Options を指定することにより Sora に接続するための SendRecv オブジェクトを作成します。 API リファレンスは [SoraConnection](https://shiguredo.github.io/sora-js-sdk/interfaces/SoraConnection.html) を参照ください。 ## ConnectionPublisher / ConnectionSubscriber / ConnectionMessaging Sora との接続/切断を管理するオブジェクトです。 - 基底クラス [ConnectionBase](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionBase.html) - Sendonly/SendRecv を扱うクラス [ConnectionPublisher](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionPublisher.html) - Recvonly を扱うクラス [ConnectionSubscriber](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionSubscriber.html) - メッセージングのみを扱うクラス [ConnectionMessaging](https://shiguredo.github.io/sora-js-sdk/interfaces/ConnectionMessaging.html) # コールバック Sora JavaScript SDK には WebRTC API のイベントや Sora 固有の機能のイベントをハンドリングするためのコールバックを用意しています。 ここではそのコールバックの利用方法について説明します。 ## connected Sora との接続が確立したタイミングで発火するコールバックです。 ```typescript import Sora, { type SignalingNotifyConnectionCreated } from "sora-js-sdk"; const conn = Sora.connection("wss://sora.example.com/signaling", true); const client = conn.sendrecv("sora", undefined, {}); client.on("connected", (_event: SignalingNotifyConnectionCreated) => { // ここに connected コールバック発火時の処理を記述します }); // メディアストリームを取得 const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); // Sora に繫ぐ await client.connect(mediaStream); ``` RTCPeerConnection が `connected` かつシグナリング通知でかつ、自身の `connection_id` の connection.created を受信したタイミングで **一回のみ** 発火するコールバックです。 ### 注意点 - シグナリング通知を無効にしている場合は発火しません - `connected` コールバック発火後に `connecting` から `connected` に状態が変化した場合は発火しません ## switched Sora とのシグナリングが WebSocket 経由から DataChannel 経由に切り替わったタイミングで発火するコールバックです。 ```typescript import Sora, { type SignalingSwitchedMessage } from "sora-js-sdk"; const conn = Sora.connection("wss://sora.example.com/signaling", true); const client = conn.sendrecv("sora", undefined, {}); client.on("switched", (_event: SignalingSwitchedMessage) => { // ここに switched コールバック発火時の処理を記述します }); // メディアストリームを取得 const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); // Sora に繫ぐ await client.connect(mediaStream); ``` ## track **自分以外** の音声や映像のメディアストリームが追加、 または削除されたタイミングで発火するコールバックです。 このコールバックは [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) の [track](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/track_event) をラップしています。 Sora では [RTCTrackEvent](https://developer.mozilla.org/en-US/docs/Web/API/RTCTrackEvent) から [MediaStream](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream) を取得する際、 [streams](https://developer.mozilla.org/en-US/docs/Web/API/RTCTrackEvent/streams) には 1 つしか入ってきません。そのため `streams[0]` で取得することができます。 ```typescript import Sora from "sora-js-sdk"; const conn = Sora.connection("wss://sora.example.com/signaling", true); const client = conn.sendrecv("sora", undefined, {}); // track で上がってくるのは RTCTrackEvent client.on("track", (_event: RTCTrackEvent) => { // Sora では必ず 1 クライアント から送られてくるのは 1 音声/ 1 映像に固定されています // そのため track.streams[0] でクライアントのメディアストリームを取得できます // メディアストリームの ID はその音声と映像を配信しているクライアントの connectionId になります // stream.id で取得できます // ここにストリームを表示したりする処理を書く }); // メディアストリームを取得 const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); // Sora に繫ぐ await client.connect(mediaStream); ``` > **重要** > > track コールバックは `audio` と `video` の **2 回** 発火します。 ## removetrack **自分以外** の音声や映像のメディアストリームが削除されたタイミングで発火するコールバックです。 このコールバックは [MediaStream](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream) の [removetrack](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/removetrack_event) をラップしています。 [MediaStreamTrackEvent](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrackEvent) から [MediaStream](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream) をする場合は [target](https://developer.mozilla.org/en-US/docs/Web/API/Event/target) を利用してください。 [target](https://developer.mozilla.org/en-US/docs/Web/API/Event/target) はイベントが送出されたオブジェクトへの参照で、 [MediaStream](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream) を取得できます。 ```typescript import Sora from "sora-js-sdk"; const conn = Sora.connection("wss://sora.example.com/signaling", true); const client = conn.sendrecv("sora", undefined, {}); // removetrack で上がってくるのは MediaStreamTrackEvent client.on("removetrack", (event: MediaStreamTrackEvent) => { // ここにトラック削除イベントの処理を書く // event.target は MediaStream if (event.target instanceof MediaStream) { // ここにトラック削除イベントの処理を書く } }); // メディアストリームを取得 const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); // Sora に繫ぐ await client.connect(mediaStream); ``` ## disconnect Sora からクライアントが切断したタイミングで発火するコールバックです。 - disconnect メソッドが呼ばれた場合 - PeerConnection の state が正常な状態では無くなった場合に強制終了する場合 - 上記以外で何かしらの異常があった場合 ```typescript import Sora, { type SoraCloseEvent } from "sora-js-sdk"; const conn = Sora.connection("wss://sora.example.com/signaling", true); const client = conn.sendrecv("sora", undefined, {}); // SoraCloseEvent が上がってくる client.on("disconnect", (_event: SoraCloseEvent) => { // ここに切断処理イベントの処理を書く }); // メディアストリームを取得 const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); // Sora に繫ぐ await client.connect(mediaStream); ``` ## push Sora のシグナリングで利用している通信経路経由でプッシュ通知が送られてきたタイミングで発火するコールバックです - WebSocket 経由でのシグナリングのプッシュ通知 - DataChannel 経由でのシグナリングのプッシュ通知 ```typescript import Sora, { type SignalingPushMessage, type TransportType } from "sora-js-sdk"; const conn = Sora.connection("wss://sora.example.com/signaling", true); const client = conn.sendrecv("sora", undefined, {}); client.on("push", (_message: SignalingPushMessage, _transportType: TransportType) => { // ここにシグナリング経由のプッシュ通知の処理を書く }); // メディアストリームを取得 const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); // Sora に繫ぐ await client.connect(mediaStream); ``` ### 参考 - [Sora ドキュメント プッシュ API](https://sora-doc.shiguredo.jp/API_PUSH) ## notify Sora のシグナリングで利用している通信経路経由での通知が送られてきた場合に発火するコールバックです。 ```typescript import Sora, { type SignalingNotifyMessage, type TransportType } from "sora-js-sdk"; const conn = Sora.connection("wss://sora.example.com/signaling", true); const client = conn.sendrecv("sora", undefined, {}); client.on("notify", (_message: SignalingNotifyMessage, _transportType: TransportType) => { // ここにシグナリング通知の処理を書く }); // メディアストリームを取得 const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); // Sora に繫ぐ await client.connect(mediaStream); ``` ### 参考 - [Sora ドキュメント シグナリング通知](https://sora-doc.shiguredo.jp/SIGNALING_NOTIFY) ## timeout Sora との接続でシグナリングは成功したが、WebRTC 接続が成功せずタイムアウトしたタイミングで発火するコールバックです。 シグナリングの開始には WebSocket over TLS を利用するため、多くの環境で接続が成功します。 ただし、その後の WebRTC の接続が失敗する場合があります。 その場合は Sora 側から WebSocket を切断するのですが、 ネットワークの問題などでクライアントがそれに気付けない状況への対策となります。 ```typescript import Sora from "sora-js-sdk"; const conn = Sora.connection("wss://sora.example.com/signaling", true); const connection = conn.sendrecv("sora", undefined, {}); connection.on("timeout", () => { // ここに接続がタイムアウトした時の処理を書く }); // メディアストリームを取得 const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); // Sora に繫ぐ await connection.connect(mediaStream); ``` ## message データチャネルメッセージング機能を利用した際に、メッセージを受信したタイミングで発火するコールバックです。 このコールバックは [RTCDataChannel](https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel) の [message](https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/message_event) をラップしています。 ```typescript import Sora, { type DataChannelMessageEvent } from "sora-js-sdk"; const conn = Sora.connection("wss://sora.example.com/signaling", true); const client = conn.sendrecv("sora", undefined, {}); client.on("message", (_event: DataChannelMessageEvent) => { // ここにデータチャネルメッセージイベントを処理する // event.label と event.data が取れる // https://shiguredo.github.io/sora-js-sdk/interfaces/DataChannelMessageEvent.html }); // メディアストリームを取得 const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); // Sora に繫ぐ await client.connect(mediaStream); ``` ### 参考 - [Sora ドキュメント メッセージング機能](https://sora-doc.shiguredo.jp/MESSAGING) ## datachannel DataChannel 経由シグナリングを利用した際に、利用可能な各データチャネルが利用に可能になったタイミングで発火するコールバックです。 メッセージング機能などを利用している場合はこちらのコールバックを利用して、メッセージングが送れるようになったかどうかを判断してください。 ```typescript import Sora, { type DataChannelEvent } from "sora-js-sdk"; const conn = Sora.connection("wss://sora.example.com/signaling", true); const client = conn.sendrecv("sora", undefined, {}); // RTCDataChannelEvent ではなく Sora 独自の DataChannelEvent client.on("datachannel", (_event: DataChannelEvent) => { // ここにデータチャネルが利用可能になった際の処理を書く // event.datachannel.direction や event.datachannel.label が取れる }); // メディアストリームを取得 const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true, }); // Sora に繫ぐ await client.connect(mediaStream); ``` このコールバックは [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) の [datachannel](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/datachannel_event) と同じタイミングで発火しません。 Sora JavaScript SDK が独自に発火させています。 ### 参考 - [Sora ドキュメント データチャネル経由のシグナリング](https://sora-doc.shiguredo.jp/DATA_CHANNEL_SIGNALING) - [Sora ドキュメント メッセージング機能](https://sora-doc.shiguredo.jp/MESSAGING) ## signaling > **注釈** > > この機能はログ出力用のコールバックです Sora とのシグナリングメッセージを送受信した際に発火するコールバックです。 WebSocket と DataChannel 経由でのシグナリングメッセージが取得可能です。 ## log > **注意** > > この機能はデバッグ用のトレースログ出力用のコールバックです SDK のトレースログを出力した際に発火するコールバックです。 このコールバックは `debug` が `false` でも発火します。 ## timeline > **注意** > > この機能はデバッグ用のタイムラインログ出力用のコールバックです クライアントの主要なイベントが発生した際に発火するコールバックです。 主に、クライアントの挙動を時系列に確認するために利用するためのデバッグ向けコールバックになっています。 ## シーケンス図 ```mermaid sequenceDiagram participant client1 as クライアント1
sendrecv participant client2 as クライアント2
sendrecv participant sora as WebRTC SFU Sora participant app as アプリケーションサーバー note over client1: getUserMedia client1->>+sora: 接続要求 sora-->>-client1: 接続成功 note over client1,sora: WebRTC 確立 sora-)client1: シグナリング通知
connection.created (クライアント1) client1->>client1: notify コールバックが発火 note over client1: 1 分経過 sora-)client1: シグナリング通知
connection.updated client1->>client1: notify コールバックが発火 note over client2: getUserMedia client2->>+sora: 接続要求 par sora-->>-client2: 接続成功 note over client2,sora: WebRTC 確立 sora-)client2: シグナリング通知
connection.created (クライアント2) client2->>client2: notify コールバックが発火 client2->>client2: track コールバックが発火 and sora-)client1: シグナリング通知
connection.created (クライアント2) client1->>client1: notify コールバックが発火 client1->>client1: track コールバックが発火 end app->>sora: PushChannel API
{"mute": true} par sora-)client1: プッシュ通知
{"mute": true} client1->>client1: push コールバックが発火 and sora-)client2: プッシュ通知
{"mute": true} client2->>client2: push コールバックが発火 end client1->>+sora: 切断要求 par sora-->>-client1: シグナリング切断 note over client1: 切断完了 client1->>client1: disconnect コールバックが発火 and sora-)client2: シグナリング通知
connection.destroyed (クライアント1) client2->>client2: notify コールバックが発火 client2->>client2: removetrack コールバックが発火 end app->>sora: DisconnectChannel API sora->>client2: シグナリング切断 note over client2: 切断完了 client2->>client2: disconnect コールバックが発火 ``` ## typedocs # Sora Labo 時雨堂の WebRTC SFU Sora を無料で検証できる [Sora Labo](https://sora-labo.shiguredo.app/) で Sora JavaScript SDK を利用する方法を紹介します。 ## Sora Labo にサインアップする Sora Labo にサインアップするには [GitHub](https://github.com/) のアカウントが必須です。 アカウント取得後 [Sora Labo](https://sora-labo.shiguredo.app/) にアクセスしてサインアップしてください。 ## Sora JavaScript SDK のサンプルを動かしてみる [Sora JavaScript SDK](https://github.com/shiguredo/sora-js-sdk) を Git Clone して、 `pnpm install` を実行してください。 > **注釈** > > pnpm のインストールについては をご確認ください。 ```bash git clone git@github.com:shiguredo/sora-js-sdk-examples.git cd sora-js-sdk-examples pnpm install ``` `.env.template` をコピーして `.env.local` ファイルを作成してください。 ```console cp .env.template .env.local ``` Sora Labo 向けの設定を追加していきます。 - `VITE_SORA_SIGNALING_URL` には `wss://sora.sora-labo.shiguredo.app/signaling` を指定してください - `VITE_SORA_CHANNEL_ID_PREFIX_PREFIX` には Sora Labo ダッシュボードで生成したチャンネル ID プレフィックスを指定してください- `{github_username}_{github_id}_` を指定してください - `VITE_SECRET_KEY` には Sora Labo ダッシュボードで生成した JWT のシークレットキーを指定してください ```bash VITE_SORA_SIGNALING_URL=wss://sora.sora-labo.shiguredo.app/signaling VITE_SORA_CHANNEL_ID_PREFIX_PREFIX={github_username}_{github_id}_ VITE_SECRET_KEY={secret_key} ``` あとはサンプルを起動するだけです。 `pnpm run dev` を実行する事でサンプルが起動します。 ```console pnpm run dev ``` 以下を二つ開いて `connect` ボタンを押して、音声と映像が双方向で表示されれば WebRTC SFU Sora 経由で Sora JavaScript SDK を利用できています。 # ステレオ入出力 > **重要** > > ステレオ入出力はブラウザや端末やマイクに依存します。 ## 概要 WebRTC をブラウザで利用する際、ステレオの入出力は基本的に利用できません。 これは WebRTC がそもそもリアルタイムコミュニケーションを目的として作られたため、 ステレオの入出力を必要としていなかったためです。 しかし、WebRTC でのステレオ需要が増えてきているため、 ここでは Sora JavaScript SDK でステレオ入出力を利用する方法を説明します。 ## 注意事項 - ブラウザでのステレオ出力は完全にブラウザ依存です。さらに、ステレオ入力はマイク依存になります - ステレオ音声を利用する場合はデフォルトで有効になっているエコーキャンセルを無効にする必要があります ## forceStereoOutput オプション `forceStereoOutput` オプションを有効にする事でステレオ試聴が利用できる用になる場合があります。 ```typescript import Sora from "sora-js-sdk"; const signalingUrl = "wss://signaling.example.com/signaling"; const debug = false; const channelId = "sora"; const metadata = { // access_token などを指定する }; const options = { forceStereoOutput: true, }; const sora = Sora.connection(signalingUrl, debug); const conn = sora.recvonly(channelId, metadata, options); await conn.connect(); ``` ## Chrome - Windows - 未検証です - macOS - forceStereoOutput オプションを有効にする事でステレオ試聴ができます - Linux- 未検証です - iOS - 未検証です - Android- 未検証です ## Safari **Safari のバージョン**: 18.5 ### macOS forceStereoOutput オプションを有効にする事でステレオ試聴ができます。 ### iPadOS forceStereoOutput オプションを有効にする事でステレオ試聴ができます。 ### iOS - ステレオ試聴できる- sendrecv と recvonly - sendonly と recvonly - ステレオ試聴できない- sendrecv と sendrecv ## Firefox 未検証です。 ## ステレオ入力が確認できているハードウェア ### マイク - [HyperX QuadCast S](https://jp.ext.hp.com/accessories/personal/quadcast_s/) ### カメラ - [Opal C1](https://opalcamera.com/opal-c1) # 古いリリースノート **UPDATE** : 下位互換がある変更 **ADD** : 下位互換がある追加 **CHANGE** : 下位互換のない変更 **FIX** : バグ修正 ## 2024.2.2 **リリース日**: 2024-11-29 - [FIX] `types` を `export` するように修正しました ## 2024.2.1 **リリース日**: 2024-11-29 - [FIX] リリースミスを修正しました ## 2024.2.0 **リリース日**: 2024-11-29 - [CHANGE] 廃止宣言から 4 年以上が経過したため `addstream` コールバックを削除しました- ブラウザでも既に非推奨から非標準とされています - [CHANGE] 廃止宣言から 4 年以上が経過したため `removestream` コールバックを削除しました- ブラウザでも既に非推奨かつ非標準とされています - [CHANGE] [fflate](https://www.npmjs.com/package/fflate) を依存から削除しました- 依存ライブラリが 0 になりました - [Compression Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Compression_Streams_API) を利用するように変更しました- Chrome/Edge 80 以降 - Firefox 113 以降 - Safari 16.4 以降 - [CHANGE] メッセージを送信する `sendMessage` 関数を非同期に変更しました- `void` ではなく `Promise` を返すようにしました - [CHANGE] `forwardingFilter` 項目の型を `JSONType` から `ForwardingFilter` に変更しました- `ForwardingFilter` 型を追加しました - `ForwardingFilterAction` 型を追加しました - `ForwardingFilterRule` 型を追加しました - `ForwardingFilterRuleField` 型を追加しました - `ForwardingFilterRuleKindValue` 型を追加しました - `ForwardingFilterRuleOperator` 型を追加しました - [CHANGE] E2EE 機能を削除しました - [UPDATE] メッセージングの圧縮と展開を [fflate](https://www.npmjs.com/package/fflate) から [Compression Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Compression_Streams_API) に変更しました - [ADD] メッセージングのみを利用する `messaging` を追加しました - [ADD] DataChannel シグナリングのみを利用した際の `"type": "close"` メッセージに対応しました - [ADD] シグナリング項目に `forwarding_filters` を追加しました- 将来的に `forwarding_filter` は廃止予定です - [FIX] Sora から API などで切断された場合の処理を改善しました- Sora から正常に切断された場合は `SoraCloseEvent` の `title` を `SHUTDOWN` に変更しました ## 2024.1.2 **リリース日**: 2024-07-09 - [FIX] "type": "offer" の simulcast の値が反映されていない問題を修正しました- @voluntas ## 2024.1.1 **リリース日**: 2024-06-17 - [FIX] 内部バージョンが canary になっていたリリースミスを修正しました- @voluntas ## 2024.1.0 **リリース日**: 2024-06-07 - [CHANGE] オーディオコーデック LYRA のサポートを削除しました - [CHANGE] シグナリングオプションの multistream は false の時のみレガシーストリームを使用するようにしました- レガシーストリームを使用したい場合は multistream: false の指定をしてください - [CHANGE] 挙動と関数名が一致しないため `stopAudioTrack` と `stopVideoTrack` を非推奨にしました- 今後は `removeAudioTrack` と `removeVideoTrack` を利用してください - [CHANGE] `examples` を Vite を利用して動かすように変更しました - [CHANGE] `examples` を クラスベースに変更しました - [ADD] `removeAudioTrack` と `removeVideoTrack` を追加しました- `stopAudioTrack` と `stopVideoTrack` の名前を変更しただけの関数です 詳細な変更履歴は をご確認ください。 ## 2023.2.0 **リリース日**: 2023-12-12 - [CHANGE] Nodejs の対応バージョンから 16 系を落としました - [CHANGE] フォーマッター/リンターを Biome に変更しました - [CHANGE] テストツールを Vitest に変更しました - [CHANGE] パッケージ管理ツールを pnpm に変更しました - [CHANGE] dist/ 以下をリポジトリに含めるないようにしました - [ADD] `"type": "offer"` で受け取った `session_id` を保持するようにしました - [ADD] Sora 2023.2.0 で対応した H.265 のパラメータ指定に対応しました- `ConnectionOptions` 型に `videoH265Params` フィールドを追加しました - [FIX] ユーザが直接使わない型には @internal を指定して .d.ts に含まれないようにしました - [FIX] `stopVideoTrack` で例外を掴んでしまっていたのを修正しました ## 2023.1.0 **リリース日**: 2023-06-21 - [UPDATE] Sora 2023.1.0 で入った転送フィルター機能を接続オプションで指定できるようにしました- `ConnectionOptions` 型に `forwardingFilter` フィールドを追加しました - [UPDATE] Sora 2023.1.0 で入った SDP 再利用機能に対応しました - [UPDATE] オファー SDP のメディアポートに 0 を指定することで古いトランシーバーを解放できるようにしました- Firefox は 0 ポートを指定するとエラーになるため、 SDK 側で従来の 9 に置換しています - [ADD] 最新版の Safari / Mobile Safari で Lyra コーデックを使用可能にしました - [ADD] 接続オプションとしてビデオコーデック用パラメータの送信を追加しました- `ConnectionOptions` 型に `videoVP9Params` `videoH264Params` `videoAV1Params` フィールドを追加しました ## 2022.3.3 **2022.3.2 からバージョン情報以外の変更点はありません** - [FIX] npm 最新バージョンへのリリースミスを修正しました。 ## 2022.3.2 **リリース日**: 2023-02-10 - [FIX] ミュート状態で接続すると、replace(Video|Audio)Track した場合に画像・音声データが送信されない問題を修正しました ## 2022.3.1 **リリース日**: 2022-12-21 - [FIX] E2EE が有効かどうかの判定を undefined ではなく null で行うよう修正しました ## 2022.3.0 **リリース日**: 2022-12-20 - [UPDATE] E2EE 有効時に Lyra コーデックを使用できるようにしました ## 2022.2.0 **リリース日**: 2022-12-13 - [CHANGE] ts-jest を @swc/jest に変更しました - [CHANGE] サンプルの sora-e2ee-wasm のダウンロード先を変更しました - [CHANGE] sora.min.js を削除しました - [ADD] audioCodecType に "LYRA" を追加しました- 注意: 現時点では Lyra コーデックと E2EE の併用はできず、両方が指定された場合には E2EE が優先されます - [ADD] Sora.initLyra() 関数を追加しました- Lyra でエンコードされた音声を送信ないし受信する場合には、事前にこの関数を呼び出しておく必要があります - wasm やモデルファイルのダウンロードは実際に必要になったタイミングで遅延して行われます - [ADD] ConnectOptions に audioLyraParamsUsedtx を追加しました - [ADD] ConnectOptions に audioLyraParamsBitrate を追加しました - [ADD] audio_streaming_language_code を追加しました - [FIX] 廃止になった opus_params の clock_rate を削除しました ## 2022.1.0 **リリース日**: 2022-6-19 - [CHANGE] 切断処理時に MediaStream の停止処理をしないように変更しました - [CHANGE] ConnectionOptions からシグナリング type: connect メッセージを生成する仕組みを変更しました- multistream オプションが false の場合、シグナリングメッセージに multistream: false を含めるよう変更しました - multistream フラグの値に関係なく spotlight オプションをシグナリングメッセージに含めるよう変更しました - spotlight フラグの値に関係なく spotlightFocusRid オプションをシグナリングメッセージに含めるよう変更しました - spotlight フラグの値に関係なく spotlightUnfocusRid オプションをシグナリングメッセージに含めるよう変更しました - spotlight フラグの値に関係なく spotlightNumber オプションをシグナリングメッセージに含めるよう変更しました - simulcast フラグの値に関係なく spotlightNumber オプションをシグナリングメッセージに含めるよう変更しました - [ADD] sendrecv オブジェクトのオプションに bundle_id を追加しました - [UPDATE] sendrecv API を使用して接続する場合に multistream option の初期値が true になるよう修正しました - [UPDATE] sendrecv API を使用して multistream: false で接続した場合、Sora との接続前に例外が発生するように修正しました - [CHANGE] connectedSignalingUrl は現在接続中の WebSocket の URL ではなく type offer メッセージを受信した URL を返すようにしました- ignoreDisconnectWebSocket を使用して WebSocket を切断した場合にも URL を返すように修正しました - [UPDATE] SendRecv オブジェクト に contactSignalingUrl プロパティを追加しました [古いリリースノート](old_release.html) はこちら ## 2021.2.3 **リリース日**: 2022-02-10 - [FIX] メッセージング機能で文字列データが送信されてきた場合にそのまま message callback に渡していた問題を修正しました ## 2021.2.2 **リリース日**: 2022-02-02 - [FIX] fflate package のバージョンを 0.7.1 から 0.7.3 に更新しました ## 2021.2.1 **リリース日**: 2022-01-27 - [FIX] type redirect 時のシグナリングで接続エラーになった場合、例外が発火しなかった問題を修正しました ## 2021.2.0 **リリース日**: 2021-12-15 - [UPDATE] simulcast 時の transceiver 判定条件に offer.mids.video での分岐を追加しました - [UPDATE] 複数パッケージの管理を lerna から npm workspace に変更しました - [ADD] メッセージング機能を追加しました- sendrecv オブジェクトのオプションに datachannels を追加しました - sendrecv オブジェクトに sendMessage API を追加しました - sendrecv オブジェクトに datachannels プロパティを追加しました - on callback に "message" を追加しました - on callback に "datachannel" を追加しました - [CHANGE] 複数 Signaling URL への接続に対応しました- Connection オブジェクト第一引数の type を `string` から `string | string[]` に変更しました - Connection オブジェクト signalingUrl プロパティの戻り値の type を string から `string | string[]` に変更しました - Connection オブジェクトに signalingUrlCandidates プロパティを追加しました - SendRecv オブジェクト signalingUrl プロパティの戻り値の type を `string` から `string | string[]` に変更しました - SendRecv オブジェクト に signalingUrlCandidates プロパティを追加しました - SendRecv オブジェクト に connectedSignalingUrl プロパティを追加しました - SendRecv オブジェクト に signalingCandidateTimeout オプションを追加しました - [UPDATE] type redirect 対応を追加しました - [CHANGE] spotlight_legacy 対応を削除しました- ConnectionOptions の spotlight オプションの型を boolean のみに変更しました ## 2021.1.7 **リリース日**: 2021-9-13 - [ADD] SoraCloseEvent 関連の type を export しました ## 2021.1.6 **リリース日**: 2021-8-10 - [FIX] timeline ログに re-answer のログが出力されていなかったので修正しました - [UPDATE] timeline ログの ontrack ログに詳細情報を追加しました ## 2021.1.5 **リリース日**: 2021-8-5 - [FIX] dataChannelSignaling false の場合に Disconnect API 経由で切断すると disconnect callback が発火しない問題を修正しました - [UPDATE] 非同期で disconnect を複数回呼んだ場合の処理を修正しました ## 2021.1.4 **リリース日**: 2021-8-3 - [FIX] DataChannel 切断のタイムアウト処理中に WebSocket が切断すると Uncaught (in promise) が発生する問題を修正しました - [UPDATE] 切断処理中の WebSocket の onclose タイムラインログに code と reason を入れるようにしました ## 2021.1.3 **リリース日**: 2021-7-30 - [FIX] DataChannel 切断処理を修正しました- 切断タイムアウト処理時にすでに DataChannel の readyState が `closed` 状態であれば onclose を待たないように修正しました ## 2021.1.2 > **注意** > > 2021.1.3 をご利用ください **リリース日**: 2021-7-30 - [CHANGE] disconnect API を修正しました- type: disconnect メッセージに reason を追加するように修正しました - [CHANGE] disconnect callback を修正しました- disconnect callback が受け取る event を CloseEvent から SoraCloseEvent に変更しました - disconnect callback が受け取る event の type は "close" のみから "normal" か "abend" のどちらかが返るように変更しました - disconnect callback が受け取る event の code, reason は undefined のパターンを追加しました - disconnect callback が受け取る event に title を追加しました - disconnect callback が受け取る event に params を追加しました - [CHANGE] connect signaling 時の意図しない WebSocket の切断時のメッセージを統一しました- "Signaling failed. {reason}" に統一しました - [CHANGE] timeline callback Event の property を変更しました- transportType を logType に変更しました - [CHANGE] signaling callback Event の property を変更しました- transportType は必須項目にしました - [UPDATE] PeerConnecion の状態が不正な場合に切断処理に入るようにしました- PeerConnecion connectionState が "failed" になった場合は切断するようにしました - PeerConnecion connectionState が undefined の場合 iceConnectionState が `disconnect` になって 1000ms 変化がない場合は切断するようにしました - [UPDATE] 型を export しました ## 2021.1.1 **リリース日**: 2021-6-29 - [FIX] 接続処理が途中で失敗した場合の timeline ログに connected のログが出力されていた問題を修正しました ## 2021.1.0 **リリース日**: 2021-6-18 - [CHANGE] fflate を導入して Signaling zlib 対応を追加しました - [CHANGE] timeout option を connectionTimeout option に名前を変更しました- timeout option を使用している場合は deprecated warning が出るように変更 - [CHANGE] Notify callback, Push callback の第二引数に TransportType を追加しました - [CHANGE] role から upstream と downstream を削除しました - [CHANGE] publisher と subscriber を削除しました - [CHANGE] シグナリングメッセージに型定義を追加しました - [CHANGE] 型定義の修正しました- Callbacks の各 callback 型定義を修正しました - on メソッドに渡す第2引数の型定義を修正しました - trace メソッドに渡す第3引数の型定義を any から unknown に変更しました - [CHANGE] packages 以下の npm*client を yarn に変更しました - [UPDATE] TypeScript を3系から4系に変更しました - [UPDATE] サイマルキャストのサンプルを low / middle / high から r0 / r1 / r2 へ変更しました - [ADD] ConnectionOptions に spotlightFocusRid / spotlightUnfocusRid を追加しました - [ADD] get audio, get video を追加して接続がそれぞれに対応しているかを返すようにしました - [ADD] stopAudioTrack, stopVideoTrack, replaceAudioTrack, replaceVideoTrack を追加しました - [ADD] helper メソッドを追加しました - [ADD] packages:upgrade コマンドを追加しました - [ADD] Switch DataChannel を実装しました- ConnectionOptions に dataChannelSignaling を追加しました - ConnectionOptions に ignoreDisconnectWebSocket を追加しました ## 2020.6.2 **リリース日**: 2021-1-28 - [FIX] simulcast が使用できるかどうかの判定を修正しました- UserAgent を用いた判定から RTCRtpSender.getCapabilities を用いた判定に変更 ## 2020.6.1 **リリース日**: 2020-12-22 - [FIX] simulcast 時に setParameters するための RTCRtpTransceiver 検索条件を変更しました- getUserMedia constraints の audio/video と Sora signaling の audio/video が一致しなかった場合に DOMException: Read-only field modified in setParameters(). が発生します - encodings が readonly な RTCRtpSender を持つ RTCRtpTransceiver を検索条件から除外して対応しました ## 2020.6.0 **リリース日**: 2020-12-10 - [UPDATE] e2ee 処理で signaling notify 時に metadata / authn_metadata どちらでも動作するように修正しました - [UPDATE] connect 時の例外に code と reason を含めるようにしました- WebSocket の onclose が発火した場合のみ Error オブジェクトに close event の code と reason を含めました - [FIX] type offer 時に受け取った encodings を type update 時にも setParametes するように修正しました ## 2020.5.0 **リリース日**: 2020-12-07 - [CHANGE] simulcastQuality を simulcastRid に変更しました - [CHANGE] simulcast を bool のみに変更しました - [CHANGE] simulcast_rid を追加しました - [CHANGE] オプションの e2ee を boolean のみに変更しました - [UPDATE] clientId option に空文字列を渡せるように修正しました - [UPDATE] sora-e2ee パッケージを内包するように変更しました- lerna を使って複数 package を管理するようにしました - sdk package を作成して既存コードを sdk package 内へ移動しました - e2ee package を作成して sora-e2ee コードを移植しました - go-wasm package を作成して wasm_exec.js コードを内包しました ## 2020.4.2 **リリース日**: 2020-11-30 - [FIX] metadata に直接 undefined を渡せるように修正しました ## 2020.4.1 **リリース日**: 2020-11-18 - [FIX] timeout option を設定時に特定の条件で正しく動かない問題を修正しました- peerconnection connectionState が undefined の場合に timeout error が強制的に発動してしまう - peerconnection 接続前に timeout の時間に到達した場合 timeout error が発動しない ## 2020.4.0 **リリース日**: 2020-10-30 - [CHANGE] signaling 時に処理に失敗した場合の reject の引数を CloseEvent オブジェクトから Error オブジェクトに変更しました - [CHANGE] connect() のタイムアウト処理にデフォルト値を設定しました- 60000 ms でタイムアウトするように設定しました - [UPDATE] connect() 実行時 PeerConnection connectionState が 'connected' になったら処理が完了するように変更しました - [UPDATE] disconnect 処理を修正しました- WebSocket で type: "disconnect" を send するように変更しました - WebSocket の readyState の監視をやめました - peerConnection の切断監視を signalingState から connectionState に変更しました - [UPDATE] sora-e2ee のバージョンを 2020.3.0 に更新しました - [FIX] package.json に定義されているモジュールの向き先を dist/sora.mjs に変更し、対象ファイルがビルドされるよう Rollup の設定を追加しました - [UPDATE] simulcast で active パラメータを有効にするための実装を追加しました ## 2020.3.0 **リリース日**: 2020-09-30 - [UPDATE] Safari 14 以降で Simulcast が使えるように変更しました ## 2020.2.0 **リリース日**: 2020-09-08 - [UPDATE] sora-e2ee を 2020.2.0 にアップデートしました - [UPDATE] 新スポットライトに対応しました- ConnectionOptions に spotlightNumber を追加しました - ConnectionOptions の spotlight に boolean を受け取れるよう修正しました - [FIX] disconnect() を複数回実行した場合に例外が発生しないように修正しました ## 2020.1.5 **リリース日**: 2020-07-20 - [FIX] metadata が undefined の場合以外は signaling connect message に metadata を含めるように変更しました ## 2020.1.4 **リリース日**: 2020-07-08 - [UPDATE] type.ts にある type Json のインデックスシグネチャに undefined を許可しました ## 2020.1.3 **リリース日**: 2020-06-22 - [CHANGE] type.ts にある Audio, Video をそれぞれ SignalingAudio, SignalingVideo に名前変更しました - [ADD] SoraConnection の型定義を export しました - [ADD] sendrecv, sendonly, recvonly の引数 options に signalingNotifyMetadata を追加しました ## 2020.1.2 **リリース日**: 2020-06-18 - [FIX] sendrecv, sendonly, recvonly の引数に渡す metadata の型を Json に変更しました - [FIX] authMetadata の型を Json に変更しました ## 2020.1.1 **リリース日**: 2020-06-17 - [UPDATE] type export を追加しました- 以下の型定義を export しました - AudioCodecType - Callbacks - ConnectionBase - ConnectionOptions - ConnectionPublisher - ConnectionSubscriber - Role - SimulcastQuality - VideoCodecType ## 2020.1.0 **リリース日**: 2020-06-01 - [CHANGE] @deprecated メッセージを追加しました- publisher/subscriber を使用している場合に warning を出すように変更しました - addstream/removestream を使用している場合に warning を出すように変更しました - debug: true 時に disconnect の MediaStream close 処理で warning を出すように変更しました - [CHANGE] property 名の変更とアクセス制限の追加しました- `_pc` を `pc` に名前変更しました - `_ws` を `ws` に名前変更してアクセス制限を protected に変更しました - `_callbacks` を `callbacks` に名前変更してアクセス制限を protected に変更しました - [CHANGE] method 名の変更とアクセス制限の追加しました- _ がついているメソッド名から _ を削除してアクセス制限を追加しました - [UPDATE] E2EE 対応しました - [UPDATE] TypeScript 化しました - [UPDATE] async / await 化しました - [ADD] Sora から `type: ping` が送られてきた際に `stats: true` だった場合 `type: pong` 送信時に `stats` に getStats の結果を入れました ## 1.16.0 **日時: 2020-01-20** - [ADD] タイムアウトを指定可能にしました- デフォルトではタイムアウトは有効にはなっていません - [ADD] 新しい role である sendrecv / sendonly / sendrecv を利用できるようにしました - [ADD] サンプルに multsitream_sendonly.html を追加しました - [UPDATE] サンプルで利用する role を新しいものに変更しました - [CHANGE] サンプルの multistream.html を multistream_sendrecv.html に変更しました - [CHANGE] サンプルの multistream_down.html を multistream_recvonly.html に変更しました - [CHANGE] サンプルの spotlight.html を spotlight_sendrecv.html に変更しました - [CHANGE] サンプルの spotlight_down.html を spotlight_recvonly.html に変更しました - [CHANGE] サンプルの updown.html を sendonly_recvonly.html に変更しました - [CHANGE] sdk_version と sdk_type を廃止し sora_client を追加しました - [CHANGE] user_agent を廃止し sora_client を追加しました - [FIX] README から simulcast_rid を削除しました ## 1.15.0 **日時: 2019-10-10** - [CHANGE] Plan B のコードをすべて削除しました - [CHANGE] ssrc group simulcast のコードをすべて削除しました - [UPDATE] タスクランナーを webpack から rollupjs に変更しました - [UPDATE] babel core を 6 から 7 へアップデートしました - [CHANGE] signaling message 作成時のチェックを修正しました- role が 'upstream' または 'downstream' でない場合はエラーになるように修正しました - channelId が null または undefined な場合はエラーになるように修正しました - metadata が null または undefined な場合は signaling message に metadata を含めないように修正しました - [ADD] multistream + simulcast に対応しました - [ADD] opus params 関連のオプションを追加しました ## 1.14.0 **日時: 2019-07-08** - [FIX] rid ベース simulcast で音声がでない問題を修正しました - [UPDATE] rid ベース simulcast で replaceTrack を使用しないで addTrack のみで実装しました ## 1.13.0 **日時: 2019-06-10** - [CHANGE] userAgent を user_agent に変更しました - [ADD] rid ベース simulcast への対応をしました- Firefox と Safari では利用できないようにしました ## 1.12.0 **日時: 2019-04-15** - [UPDATE] example の整理をしました - [UPDATE] development build 時に sora-js-sdk の version に '-dev' をつけるように変更しました - [ADD] Signaling Option に client_id を追加しました ## 1.11.0 **日時: 2019-03-27** - [UPDATE] Safari の Unified Plan, Plan B 両方に対応しました - [UPDATE] Simulcast option が使えるブラウザ判定を変更しました ## 1.10.2 **日時: 2019-01-21** - [FIX] マルチストリームで Safari 12.0 と 12.1 両方に対応しました ## 1.10.1 **日時: 2019-01-17** - [UPDATE] Firefox の Media Panel addon の Media-Webrtc が動作するよう RTCPeerConnection の変数格納を削除しました - [ADD] ConnectionOptions の新しいプロパティに型を追加しました - [FIX] マルチストリームで Safari 12.1 に対応しました ## 1.10.0 **日時: 2018-10-29** - [UPDATE] simulcast, simulcastQuality オプションを追加しました ## 1.9.3 **日時: 2018-10-19** - [FIX] Single Stream の subscriber で on('addstream', callback) が発火しない問題を修正しました ## 1.9.2 **日時: 2018-5-20** - [UPDATE] package json の文言修正を修正しました ## 1.9.1 **日時: 2018-5-13** - [UPDATE] Unified Plan の適応を Chrome M71 以降のバージョンに変更しました ## 1.9.0 **日時: 2018-5-28** - [ADD] Chrome M68 以降のバージョンの動作変更しました- RTCPeerConnection の config に sdpSemantics: 'unified-plan' を追加しました - signaling message に plan_b: true オプションを渡さないように修正しました - [CHANGE] snapshot 関連を削除しました - [FIX] ontrack で stream が取得できなかった場合のエラーを修正しました ## 1.8.2 **日時: 2018-4-27** - [CHANGE] vad を spotlight に変更しました ## 1.8.1 **日時: 2018-3-15** - [FIX] addTransceiver を使うのは safari の場合だけにしました - [FIX] pc が null の場合は reject するように修正しました ## 1.8.0 **日時: 2018-2-21** - [ADD] 認証ウェブフックで払い出された metadata を参照できるように修正しました - [UPDATE] Sora のプレビュー機能向けのパラメータ `vad` を signaling connect 時に指定できるように修正しました ## 1.7.7 **日時: 2018-1-12** - [UPDATE] example を修正しました。 動作に変更はありません - [FIX] disconnect 時 Safari では PeerConnection Closing Error で失敗していたので、正常に disconnect するように修正しました - [FIX] subscriber multistream 時に Chrome では remoteClientIds が更新されていなかった問題を修正しました - [FIX] websocket 切断時に特定の条件で remote clientId のリストが更新されない問題があったため、disconnect 時に初期化するように修正しました - [FIX] disconnect 時に特定の条件で peerConnection 切断処理中に oniceconnectionstatechange イベントが発火することがあるため peerConnection の oniceconnectionstatechange を明示的に初期化するように修正しました ## 1.7.6 日時: 2017-12-29 - [FIX] multistream subscriber 利用時に ontrack が video でしか発火しなかったのを修正しました - [FIX] multistream subscriber 利用時に onremovestream を ontrack の動作に合わせました ## 1.7.5 **日時: 2017-12-27** - [CHANGE] offer 作成用の peerConnection を close するように修正しました ## 1.7.4 **日時: 2017-11-29** - [UPDATE] signaling connect 時のパラメータに UserAgent を追加しました - [CHANGE] publisher, subscriber の引数の options に渡したオブジェクトの value 値が null の場合は処理しないように修正しました ## 1.7.3 **日時: 2017-11-15** - [UPDATE] Firefox で icecandidate に時間がかかる問題を修正しました ## 1.7.2 **日時: 2017-11-06** - [UPDATE] 最新の Edge に対応しました - [FIX] signaling offer 時の message に config が含まれていないとエラーになる問題を修正しました ## 1.7.1 **日時: 2017-11-01** - [UPDATE] signaling connect 時のパラメータに sdp を追加しました ## 1.7.0 **日時: 2017-10-30** - [ADD] event type に log を追加しました - [FIX] disconnect を同時に複数回呼ぶとエラーになる問題を修正しました ## 1.6.1 **日時: 2017-10-18** - [ADD] RTCPeerConnection の引数に MediaConstraints を渡せる機能を追加しました ## 1.6.0 **日時: 2017-10-03** - [ADD] Publisher と Subscriber の options に AudioBitRate を追加しました ## 1.5.0 **日時: 2017-8-29** - [CHANGE] Signaling 時の WebSocket onerror では reject しないように修正しました - [FIX] multistream audio only の場合に addstream イベントが動作しなかったので修正しました ## 1.4.1 **日時: 2017-7-3** - [FIX] Signaling message の metadata が旧仕様(access_token)のままだったので修正しました ## 1.4.0 **日時: 2017-6-16** - [ADD] Signaling notify 用の callback を追加できるように変更しました ## 1.3.0 **日時: 2017-6-14** - [ADD] Signaling notify 用の callback を追加できるように変更しました ## 1.3.0 **日時: 2017-6-9** - [UPDATE] Safari に対応しました- 現時点で確認できているのは Safari Technology Preview 32 での動作のみです ## 1.2.0 **日時: 2017-5-29** - [ADD] Subscriber の multistream に対応しました - [CHANGE] iceServers が指定されていない場合に 'stun:stun.l.google.com:19302' を使用していたのをやめました ## 1.1.0 **日時: 2017-4-18** - [UPDATE] Microsoft Edge に対応しました ## 1.0.0 **日時: 2017-4-11** - [CHANGE] PeerConnection まで含めた処理を SDK で実行するように変更しました - [CHANGE] multistream をパラメータに追加しました - [CHANGE] videoSnapshot をパラメータに追加しました - [CHANGE] videoBitRate をパラメータに追加しました - [CHANGE] audioCodecType をパラメータに追加しました - [CHANGE] codecType を videoCodecType に変更しました ## 0.5.0 **日時: 2016-5-13** - [CHANGE] codecType のチェックをしないようにしました - [UPDATE] シグナリングメッセージのキー名を変更しました ## 0.4.2 **日時: 2016-4-8** - [UPDATE] ドキュメントを修正しました ## 0.4.1 **日時: 2016-3-31** - [UPDATE] ドキュメントを修正しました ## 0.4.0 **日時: 2016-3-22** - [UPDATE] codecType が選択できるように修正しました - [UPDATE] パッケージの更新をしました - [UPDATE] ビルドのしくみを変更しました ## 0.3.2 **日時: 2016-3-1** - [UPDATE] パッケージの更新をしました ## 0.3.1 **日時: 2016-2-2** - [UPDATE] signaling 時に WS が切断した場合、ステータスコードが 440x だったら Promise.reject するように変更しました ## 0.3.0 **日時: 2016-1-18** - [UPDATE] disconnect を追加しました ## 0.2.0 **日時: 2015-12-15** - [CHANGE] constructor の引数に URL 文字列を受け取る用に修正しました - [CHANGE] package name を sora.js から sora-js-sdk に変更しました - [CHANGE] Promise 化しました - [FIX] PeerConnection Object が GC の対象にならないように修正しました ## 0.1.0 **日時: 2015-11-19**