Sora JavaScript SDK ドキュメント¶
このドキュメントは Sora JavaScript SDK バージョン 2024.2.2 に対応しています。
Sora 自体の製品お問い合わせは sora at shiguredo dot jp までお願い致します。 (このメールアドレスへの特定電子メールの送信を拒否いたします)
問い合わせについて¶
Sora JavaScript SDK の質問などについては Discord の #sora-sdk-faq
をご利用ください。
ただし、 Sora のライセンス契約の有無に関わらず、応答時間と問題の解決を保証しませんのでご了承ください。
Sora JavaScript SDK に対する有償のサポートについては提供しておりません。
注意事項¶
Sora JavaScript SDK 2024.2.0 以降は Compression Stream API を利用しています。 そのため、 Apple Safari の場合はバージョン 16.4 以降が必要です。
詳細は 対応ブラウザについて をご確認ください。
リリースノート¶
- CHANGE
下位互換のない変更
- UPDATE
下位互換がある変更
- ADD
下位互換がある追加
- 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
コールバックを削除しましたブラウザでも既に非推奨から非標準とされています
https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream
[CHANGE] 廃止宣言から 4 年以上が経過したため
removestream
コールバックを削除しましたブラウザでも既に非推奨かつ非標準とされています
https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/removeStream
[CHANGE] fflate を依存から削除しました
依存ライブラリが 0 になりました
Compression Streams API を利用するように変更しました
Chrome/Edge 80 以降
Firefox 113 以降
Safari 16.4 以降
[CHANGE] メッセージを送信する
sendMessage
関数を非同期に変更しましたvoid
ではなくPromise<void>
を返すようにしました
[CHANGE]
forwardingFilter
項目の型をJSONType
からForwardingFilter
に変更しましたForwardingFilter
型を追加しましたForwardingFilterAction
型を追加しましたForwardingFilterRule
型を追加しましたForwardingFilterRuleField
型を追加しましたForwardingFilterRuleKindValue
型を追加しましたForwardingFilterRuleOperator
型を追加しました
[CHANGE] E2EE 機能を削除しました
[UPDATE] メッセージングの圧縮と展開を fflate から Compression Streams API に変更しました
[ADD] メッセージングのみを利用する
messaging
を追加しました[ADD] DataChannel シグナリングのみを利用した際の
"type": "close"
メッセージに対応しました[ADD] シグナリング項目に
forwarding_filters
を追加しました将来的に
forwarding_filter
は廃止予定です
[FIX] Sora から API などで切断された場合の処理を改善しました
Sora から正常に切断された場合は
SoraCloseEvent
のtitle
をSHUTDOWN
に変更しました
Sora JavaScript SDK 概要¶
Sora JavaScript SDK は 株式会社時雨堂 が開発、販売する WebRTC SFU Sora のブラウザ向け 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 をサポートしているブラウザのバージョンが必要になります。
Chrome / Chrome 80 以降
Firefox 113 以降
Safari 16.4 以降
Sora JavaScript SDK のサンプルについて¶
Sora JavaScript SDK のサンプル¶
Sora JavaScript SDK の最小限の書き方を確認できるサンプルです。
Sora JavaScript SDK ドキュメントのサンプル¶
Sora JavaScript SDK の解説を目的としたサンプルです。
仕様¶
MediaStreamTrack と Connection ID の紐付け¶
Sora JavaScript SDK は MediaStreamTrack と Connection ID の紐付けを行う仕組みは提供していません。
ただし track
コールバック時に、
RTCTrackEvent の streams から MediaStream を取得できます。
MediaStream の stream.id が Connection ID となります。
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);
});
メディアストリームの扱い¶
Sora JavaScript SDK は音声や映像といったメディアストリームを SDK から扱うことはありません。
そのため音声や映像を扱う場合は MediaDevices.getUserMedia() 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 のサポートまでご連絡ください。
シグナリング¶
Sora との接続が完了したことを通知するコールバックはありますか?¶
Sora JavaScript SDK には Sora との接続完了を通知するには、 Sora のシグナリング通知機能を利用してください。
シグナリング通知機能の connection.created
と connection_id
を利用してください。
// 色々省略
const sendrecv = soraConnection.sendrecv(channelId, metadata, options);
await sendrecv.connect(stream);
// シグナリング通知
sendrecv.on("notify", (event: SignalingNotifyMessage) => {
console.log("notify", event.event_type);
if (
// connection.created が Sora とクライアントの間で WebRTC が確立した事を通知しています
event.event_type === "connection.created" &&
// 自分の connection_id と一致
sendrecv.connectionId === event.connection_id
) {
// 接続が成功
console.log("self-connection_id: ", event.connection_id);
}
// 自分以外の参加者の connection_id の取得
if (event.event_type === "connection.created") {
console.log("connection_id", event.connection_id);
}
});
データチャネルメッセージング¶
メッセージングのみを利用したいです¶
messaging()
を利用する事でメッセージングのみを利用できます。
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
での指定となります。
const options = {
dataChannelSignaling: true,
dataChannels: [
{
label: "#example",
direction: "sendrecv" as DataChannelDirection,
maxPacketLifeTime: 60000,
},
],
};
const options = {
dataChannelSignaling: true,
dataChannels: [
{
label: "#example",
direction: "sendrecv" as DataChannelDirection,
maxRetransmits: 0,
},
],
};
WebRTC API¶
Sora JavaScript SDK には Sora との接続部分の機能のみを提供しています。
そのため、映像や音声、データなどを実際に処理する場合はすべて JavaScript の WebRTC API やライブラリなどを利用する必要があります。
解像度を変更するにはどうしたらいいですか?¶
解像度の変更は MediaDevices.getUserMedia() や MediaDevices.getDisplayMedia() API 側で行ってください。
背景をぼかすにはどうしたらいいですか?¶
時雨堂がオープンソースとして公開している @shiguredo/virtual-background - npm を利用することで簡単にブラウザで背景ぼかし機能を利用可能です。
以下で動作確認が可能です。 https://shiguredo.github.io/media-processors/virtual-background/
ライトを調整はできますか?¶
時雨堂がオープンソースとして公開している @shiguredo/light-adjustment - npm を利用することで簡単にブラウザでライト調整機能を利用可能です。
以下で動作確認が可能です。 https://shiguredo.github.io/media-processors/light-adjustment/
ノイズを抑制できますか?¶
時雨堂がオープンソースとして公開している @shiguredo/noise-suppression - npm を利用することで簡単にブラウザでノイズ抑制機能を利用可能です。
以下で動作確認が可能です。 https://shiguredo.github.io/media-processors/noise-suppression/
音声でステレオは利用できますか?¶
WebRTC の Opus はデフォルトで 2 チャンネル利用するためステレオに対応しています。
ただし、ステレオ音声を利用する場合はデフォルトで有効になっているエコーキャンセルを無効にする必要があります。
https://www.w3.org/TR/mediacapture-streams/#dom-mediatracksupportedconstraints-echocancellation
画面キャプチャはできますか?¶
MediaDevices.getDisplayMedia() を利用して画面のストリームを取得してください。
カメラを切り替えられますか?¶
トラックを切り替えたい場合は RTCRtpSender.replaceTrack() を利用して既存のトラックと置き換えを行ってください。
音声をミュートにできますか?¶
Sora JavaScript SDK はトラックのミュートは行いません。
トラックのミュートを行いたい場合は MediaStreamTrack.enabled を利用して、ミュートを行ってください。
Electron で利用したいです¶
Sora JavaScript SDK は Electron でも利用可能です。
MediaStreamTrack から MediaStream の ID を取得したいです¶
MediaStreamTrack から MediaStream の ID は取得できません。 そのため、もし取得したい場合は何かしらの方法で ID を保持しておく必要があります。
ただし removetrack
コールバックは MediaStream が発火させるため event.target
で発火した MediaStream を取得可能です。
CPU や帯域による解像度やフレームレートの制限を無効にしたいです¶
無効にできません。
ブラウザで CPU 負荷が高まったと判断して、利用ビットレートを下げたことにより解像度やフレームレートが下がる場合、
Chrome 102 までは googCpuOveruseDetection
という設定があり、 false
にすることで、判定を無効にできていましたが、
その設定は Chrome 103 にて廃止されました。
Tip
制限の理由は qualityLimitationReason で確認できます。
PSA: RTCPeerConnection's mediaConstraints - most constraints removed in M103
PSA: RTCPeerConnection's mediaConstraints is deprecated and will soon be removed
https://www.w3.org/TR/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationreason
https://developer.mozilla.org/en-US/docs/Web/API/RTCOutboundRtpStreamStats/qualityLimitationReason
映像を回転させたいです¶
getUserMedia で取得した映像を回転させるには Canvas API を利用してください。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
</head>
<body>
<video id="originalVideo" autoplay="" playsinline="" controls="" muted=""></video>
<video id="rotatedVideo" autoplay="" playsinline="" controls="" muted=""></video>
<hr />
<script type="module" src="./main.ts"></script>
</body>
</html>
// 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();
};
インストール¶
インストール方法¶
npm¶
$ npm add sora-js-sdk
pnpm¶
$ pnpm add sora-js-sdk
使用方法¶
import Sora from "sora-js-sdk";
チュートリアル¶
本章では Sora JavaScript SDK を使って音声と映像を送受信できる簡単なサンプルを作成します。
プロジェクトの作成¶
開発環境ツールとして Vite を利用します。 無理に Vite を利用する必要は無く、慣れたツールを利用してください。
パッケージマネージャーとしては pnpm を利用していますが、 npm や yarn でも問題ありません。
$ 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 の追加¶
$ pnpm add -E sora-js-sdk jose
jose について¶
jose は Sora Labo や Sora Cloud を利用する場合の JWT 生成に必要になります。
Vite と TypeScript を最新にする¶
$ 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
は他の映像を表示する
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="data:,">
<title>Sora JS SDK Tutorial</title>
</head>
<body>
<p>
<button id="connect">Connect</button>
<button id="disconnect" disabled>Disconnect</button>
</p>
<video id="local-video" autoplay playsInline controls muted style="transform: scaleX(-1)"></video>
<div id="remote-videos"></div>
<script type="module" src="./src/main.ts"></script>
</body>
</html>
.env.local の作成¶
.env.local ファイルを作成してください。
$ touch .env.local
環境変数を設定してください。
Sora を自前で立てる場合¶
# Sora のシグナリング URL
VITE_SORA_SIGNALING_URL=wss://{host}/signaling
# 好きな文字列
VITE_SORA_CHANNEL_ID=tutorial
Sora Cloud¶
# 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¶
# 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 クラスを作成します。
import { SignJWT } from "jose";
import Sora, {
type ConnectionOptions,
type SoraConnection,
type ConnectionPublisher,
} from "sora-js-sdk";
// Sora JavaScript SDK を利用した Sora クライアント
class SoraClient {
private debug: boolean;
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.debug = false;
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<HTMLDivElement>("#remote-videos");
if (remoteVideos && !remoteVideos.querySelector<HTMLVideoElement>(`#${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 = (): Promise<string> => {
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 の変更¶
/// <reference types="vite/client" />
interface ImportMetaEnv {
VITE_SORA_SIGNALING_URL: string;
VITE_SORA_CHANNEL_ID_PREFIX_PREFIX: string;
VITE_SECRET_KEY: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
src/main.ts の変更¶
Sora Client クラスを利用したコードを追加します。
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;
while (remoteVideos?.firstChild) {
remoteVideos.removeChild(remoteVideos.firstChild);
}
});
});
// 環境変数からチャネル 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;
};
起動¶
$ pnpm run dev
VITE v6.0.7 ready in 232 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 を使用して、環境変数から接続先の情報を取得するようにしています。
Env Variables and Modes | Vite
.env または .env.local¶
# 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_
となります
# 例
# 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¶
https://vite.dev/guide/env-and-mode#intellisense-for-typescript
/// <reference types="vite/client" />
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 や Sora Cloud では接続に JWT を利用しています。
このドキュメントでは JWT を生成するのに jose というライブラリを利用しています。
インストール¶
# npm
$ npm install jose
# pnpm
$ pnpm add jose
JWT 生成するコード¶
// jose をインストールしていることを前提としています
import { SignJWT } from "jose";
// channel_id と secret_key を指定して JWT を生成する
export const generateJwt = (channelId: string, secretKey: string): Promise<string> => {
// 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 を生成します。
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() を使用して接続します。
マルチストリームで接続した Connection オブジェクト は接続したチャネル ID に MediaStream が追加、削除されると track コールバックが呼ばれます。 コールバックを利用して追加、削除された MediaStream を処理します。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
</head>
<body>
<video id="localVideo" autoplay="" playsinline="" controls="" muted=""></video>
<hr />
<div id="remoteVideos"></div>
<button id="connect">接続</button>
<button id="disconnect">切断</button>
<script type="module" src="./main.ts"></script>
</body>
</html>
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", () => soraClient.connect());
document.querySelector("#disconnect")?.addEventListener("click", () => 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<string, MediaStream> = {};
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<HTMLVideoElement>("#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<HTMLVideoElement>("#localVideo");
if (localVideo !== null) {
localVideo.srcObject = null;
}
// リモートビデオを削除
const remoteVideos = document.querySelector<HTMLDivElement>("#remoteVideos");
remoteVideos?.remove();
}
// 統計情報を取得できるようにする
getStats(): Promise<RTCStatsReport | null> {
if (!this.connection.pc) {
return Promise.resolve(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<string, MediaStream> {
return this.streams;
}
}
送信のみで接続する¶
sendonly() を使用して接続します。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
</head>
<body>
<video id="localVideo" autoplay="" playsinline="" controls="" muted=""></video>
<hr />
<button id="connect">接続</button>
<button id="disconnect">切断</button>
<script type="module" src="./main.ts"></script>
</body>
</html>
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", () => soraClient.connect());
document.querySelector("#disconnect")?.addEventListener("click", () => 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<HTMLVideoElement>("#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<HTMLVideoElement>("#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() を使用して接続します。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
</head>
<body>
<button id="connect">Connect</button>
<button id="disconnect">Disconnect</button>
<div id="remote-videos"></div>
<script type="module" src="./main.ts"></script>
</body>
</html>
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", () => soraClient.connect());
document.querySelector("#disconnect")?.addEventListener("click", () => 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<string, MediaStream> = {};
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<RTCStatsReport | null> {
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<string, MediaStream> {
return this.streams;
}
}
接続して 5 秒後に切断する¶
disconnect() を使用して接続を切断します。
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 ドキュメント サイマルキャスト をご確認ください。
ConnectionBase オプション に simulcast: true
を指定します
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 ドキュメント スポットライト をご確認ください。
ConnectionBase オプション に spotlight: true
multistream: true
を指定します
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 オプション で指定します
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;
音声や映像のビットレートを指定する¶
音声のビットレート指定は推奨しません
音声や映像のビットレートは ConnectionBase オプション で指定します
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 オプション で指定します。
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 オプション で指定します。
音声なし
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;
映像なし
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 を参照してください。
ConnectionBase オプション で指定します。
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 経由に切り替える機能です。 詳しくは Sora ドキュメント DataChannel 経由のシグナリング を参照してください。
ConnectionBase オプション で指定します。
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 = {
// シグナリングを WebSocket 経由から DataChannel 経由に切り替えるかどうかを指定
dataChannelSignaling: true,
// シグナリングを DataChannel 経由に切り替えたあとに WebSocket を切断するかどうかを指定
// true にした場合、 Sora JS SDK は自動で WebSocket を切断します
ignoreDisconnectWebSocket: false,
};
const client = sora.sendrecv(channelId, undefined, options);
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 ドキュメントの リアルタイムメッセージング機能 をご確認ください。
ConnectionBase オプション で指定します。
import Sora, { type DataChannelDirection } 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: 60000,
// 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,
};
}
// メッセージ送信が可能な 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;
メッセージングのみで接続することも可能です。
import Sora, { type DataChannelDirection } 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,
// データチャネルシグナリングを利用する
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,
};
}
await messaging.connect();
// Sora 2023.2.0 以前のバージョンでは sendrecv で接続する必要がある
// もし sendrecv や sendonly を利用する場合は空のメディアストリームを渡す
// await sendrecv.connect(new MediaStream());
// メッセージを送信する
messaging.sendMessage("#example", new TextEncoder().encode("Hello world."));
};
export default connectToSora;
シグナリング通知機能を使用する¶
シグナリング通知機能は自分の接続状況や他の接続の参加や離脱などの通知する仕組みです。
詳しくは Sora ドキュメント の シグナリング通知 をご確認ください。
import Sora, { type SignalingNotifyMessage } 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,
};
}
// シグナリング通知
sendonly.on("notify", (event: SignalingNotifyMessage) => {
console.log("notify", event.event_type);
if (
event.event_type === "connection.created" &&
// 自分の connection_id と一致
sendonly.connectionId === event.connection_id
) {
// 接続が成功
console.log("self-connection_id: ", event.connection_id);
}
if (event.event_type === "connection.created") {
// 自分以外の参加者の connection_id
console.log("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 を参照ください。
Sora¶
Sora JS SDK メインオブジェクトです。
API リファレンスは Sora を参照ください。
SoraConnection¶
Role、 ChannelId、 Metadata、Options を指定することにより Sora に接続するための SendRecv オブジェクトを作成します。
API リファレンスは SoraConnection を参照ください。
ConnectionPublisher / ConnectionSubscriber / ConnectionMessaging¶
Sora との接続/切断を管理するオブジェクトです。
基底クラス ConnectionBase
Sendonly/SendRecv を扱うクラス ConnectionPublisher
Recvonly を扱うクラス ConnectionSubscriber
メッセージングのみを扱うクラス ConnectionMessaging
コールバック¶
Sora JavaScript SDK には WebRTC API のイベントや Sora 固有の機能のイベントをハンドリングするためのコールバックを用意しています。
ここではそのコールバックの利用方法について説明します。
track¶
自分以外 の音声や映像のメディアストリームが追加、 または削除されたタイミングで発火するコールバックです。
このコールバックは RTCPeerConnection の track をラップしています。
Sora では RTCTrackEvent から MediaStream を取得する際、
streams には 1 つしか入ってきません。そのため streams[0]
で取得することができます。
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] でクライアントのメディアストリームを取得できます
const _stream = event.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 の removetrack をラップしています。
MediaStreamTrackEvent から MediaStream をする場合は target を利用してください。 target はイベントが送出されたオブジェクトへの参照で、 MediaStream を取得できます。
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 が正常な状態では無くなった場合に強制終了する場合
上記以外で何かしらの異常があった場合
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 経由でのシグナリングのプッシュ通知
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);
参考¶
notify¶
Sora のシグナリングで利用している通信経路経由での通知が送られてきた場合に発火するコールバックです。
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);
参考¶
timeout¶
Sora との接続でシグナリングは成功したが、WebRTC 接続が成功せずタイムアウトしたタイミングで発火するコールバックです。
シグナリングの開始には WebSocket over TLS を利用するため、多くの環境で接続が成功します。 ただし、その後の WebRTC の接続が失敗する場合があります。
その場合は Sora 側から WebSocket を切断するのですが、 ネットワークの問題などでクライアントがそれに気付けない状況への対策となります。
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 の message をラップしています。
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://sora-js-sdk.shiguredo.jp/apidoc/interfaces/DataChannelMessageEvent
});
// メディアストリームを取得
const mediaStream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true,
});
// Sora に繫ぐ
await client.connect(mediaStream);
参考¶
datachannel¶
DataChannel 経由シグナリングを利用した際に、利用可能な各データチャネルが利用に可能になったタイミングで発火するコールバックです。
メッセージング機能などを利用している場合はこちらのコールバックを利用して、メッセージングが送れるようになったかどうかを判断してください。
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 の datachannel と同じタイミングで発火しません。 Sora JavaScript SDK が独自に発火させています。
参考¶
signaling¶
注釈
この機能はログ出力用のコールバックです
Sora とのシグナリングメッセージを送受信した際に発火するコールバックです。
WebSocket と DataChannel 経由でのシグナリングメッセージが取得可能です。
log¶
注意
この機能はデバッグ用のトレースログ出力用のコールバックです
SDK のトレースログを出力した際に発火するコールバックです。
このコールバックは debug
が false
でも発火します。
timeline¶
注意
この機能はデバッグ用のタイムラインログ出力用のコールバックです
クライアントの主要なイベントが発生した際に発火するコールバックです。
主に、クライアントの挙動を時系列に確認するために利用するためのデバッグ向けコールバックになっています。
シーケンス図¶
sequenceDiagram participant client1 as クライアント1<br>sendrecv participant client2 as クライアント2<br>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: シグナリング通知<br>connection.created (クライアント1) client1->>client1: notify コールバックが発火 note over client1: 1 分経過 sora-)client1: シグナリング通知<br>connection.updated client1->>client1: notify コールバックが発火 note over client2: getUserMedia client2->>+sora: 接続要求 par sora-->>-client2: 接続成功 note over client2,sora: WebRTC 確立 sora-)client2: シグナリング通知<br>connection.created (クライアント2) client2->>client2: notify コールバックが発火 client2->>client2: track コールバックが発火 and sora-)client1: シグナリング通知<br>connection.created (クライアント2) client1->>client1: notify コールバックが発火 client1->>client1: track コールバックが発火 end app->>sora: PushChannel API<br>{"mute": true} par sora-)client1: プッシュ通知<br>{"mute": true} client1->>client1: push コールバックが発火 and sora-)client2: プッシュ通知<br>{"mute": true} client2->>client2: push コールバックが発火 end client1->>+sora: 切断要求 par sora-->>-client1: シグナリング切断 note over client1: 切断完了 client1->>client1: disconnect コールバックが発火 and sora-)client2: シグナリング通知<br>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 で Sora JavaScript SDK を利用する方法を紹介します。
Sora Labo にサインアップする¶
Sora Labo にサインアップするには GitHub のアカウントが必須です。
アカウント取得後 Sora Labo にアクセスしてサインアップしてください。
Sora JavaScript SDK のサンプルを動かしてみる¶
Sora JavaScript SDK を Git Clone して、 pnpm install
を実行してください。
注釈
pnpm のインストールについては https://pnpm.io/ja/installation をご確認ください。
git clone [email protected]:shiguredo/sora-js-sdk-examples.git
cd sora-js-sdk-examples
pnpm install
.env.template
をコピーして .env.local
ファイルを作成してください。
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 のシークレットキーを指定してください
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
を実行する事でサンプルが起動します。
pnpm run dev
以下を二つ開いて connect
ボタンを押して、音声と映像が双方向で表示されれば WebRTC SFU Sora 経由で Sora JavaScript SDK を利用できています。
古いリリースノート¶
- UPDATE
下位互換がある変更
- ADD
下位互換がある追加
- CHANGE
下位互換のない変更
- FIX
バグ修正
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
の名前を変更しただけの関数です
詳細な変更履歴は https://github.com/shiguredo/sora-js-sdk/releases/tag/2024.1.0 をご確認ください。
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 プロパティを追加しました
古いリリースノート はこちら
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