サンプル

概要

ここでは Sora JavaScript SDK を利用したサンプルを紹介します。

Vite

このサンプルでは Vite を使用して、環境変数から接続先の情報を取得するようにしています。

Env Variables and Modes | Vite

.env または .env.local

# Sora シグナリング URL
VITE_SORA_SIGNALING_URL=wss://sora.example.com/signaling
# Sora チャネル ID
VITE_SORA_CHANNEL_ID=sora-js-sdk-doc
# Sora Cloud や Sora Labo で認証で利用するアクセストークン
# Sora 自体に認証の仕組みはないため不要
VITE_ACCESS_TOKEN=access_token

vite-env.d.ts

https://vitejs.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_ACCESS_TOKEN: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

VITE_ACCESS_TOKEN

Sora 自体には認証機能を持っていないため、アクセストークンを処理するのは Sora の認証ウェブフック先のアプリケーションになります。そのためアクセストークンという仕組みは Sora 自体では定義していません。

Sora のクラウド版である Sora Cloud や、Sora を無料で検証できる Sora Labo では、metadata にアクセストークンを指定し、認証を行うため、ここではアクセストークンの例を用意しています。

送受信で接続する

自分から音声、映像を配信し、他の参加者から音声、映像を受信する場合は 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.mts"></script>

</body>

</html>
import Sora, {
  type SoraConnection,
  type ConnectionPublisher,
} from "sora-js-sdk";

class SoraClient {
  private debug = true;

  private signalingUrl: string;
  private channelId: string;
  private accessToken: string;

  private connection: SoraConnection;
  private sendrecv: ConnectionPublisher;

  private streams: Record<string, MediaStream> = {};

  constructor(signalingUrl: string, channelId: string, accessToken: string) {
    this.signalingUrl = signalingUrl;
    this.channelId = channelId;
    this.accessToken = accessToken;

    // 接続先の Sora を設定する
    this.connection = Sora.connection(this.signalingUrl, this.debug);
    const metadata = {
      // Sora では特に "access_token" と決まっているわけではありません
      // access_token は Sora Labo や Sora Cloud の想定です
      access_token: this.accessToken,
    };
    const options = {
      audio: true,
      video: true,
    };
    this.sendrecv = this.connection.sendrecv(this.channelId, metadata, options);

    // ontrack イベント
    // メディアストリームトラック単位で発火する
    this.sendrecv.on("track", this.onaddtrack.bind(this));

    // removetrack イベント (リモートメディアストリームが削除されたときに発生)
    this.sendrecv.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;
    }

    // 接続
    await this.sendrecv.connect(stream);
  }

  async disconnect() {
    // 切断
    await this.sendrecv.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.sendrecv.pc) {
      return Promise.resolve(null);
    }

    return this.sendrecv.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;
  }
}

// DOMContentLoaded イベントは、ページ全体が読み込まれ、DOMが準備できたときに発生する
// これを利用して、必要な DOM 要素が利用可能になったタイミングで addEventListener を呼び出している
// ページ読み込み後にボタンにクリックイベントリスナーが追加され、ボタンがクリックされると connect 関数が実行される
document.addEventListener("DOMContentLoaded", (_event) => {
  // Vite を利用して env.local から取得
  const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
  const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
  const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

  const soraClient = new SoraClient(SIGNALING_URL, CHANNEL_ID, ACCESS_TOKEN);
  document
    .querySelector("#connect")
    ?.addEventListener("click", () => soraClient.connect());
  document
    .querySelector("#disconnect")
    ?.addEventListener("click", () => soraClient.disconnect());
});

送信のみで接続する

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.mts"></script>

</body>

</html>
import Sora, {
  type SoraConnection,
  type ConnectionPublisher,
} from "sora-js-sdk";

class SoraClient {
  private debug = true;

  private signalingUrl: string;
  private channelId: string;
  private accessToken: string;

  private connection: SoraConnection;
  private sendonly: ConnectionPublisher;

  constructor(signalingUrl: string, channelId: string, accessToken: string) {
    this.signalingUrl = signalingUrl;
    this.channelId = channelId;
    this.accessToken = accessToken;

    // 接続先の Sora を設定する
    this.connection = Sora.connection(this.signalingUrl, this.debug);
    const metadata = {
      // Sora では特に "access_token" と決まっているわけではありません
      // access_token は Sora Labo や Sora Cloud の想定です
      access_token: this.accessToken,
    };
    const options = {};
    this.sendonly = this.connection.sendonly(this.channelId, metadata, options);

    // ontrack イベント
    // メディアストリームトラック単位で発火する
    this.sendonly.on("track", this.onaddtrack.bind(this));

    // removetrack イベント (リモートメディアストリームが削除されたときに発生)
    this.sendonly.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;
    }

    // 接続
    await this.sendonly.connect(stream);
  }

  async disconnect() {
    // 切断
    await this.sendonly.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);
    }
  }
}

// DOMContentLoaded イベントは、ページ全体が読み込まれ、DOMが準備できたときに発生する
// これを利用して、必要な DOM 要素が利用可能になったタイミングで addEventListener を呼び出している
// ページ読み込み後にボタンにクリックイベントリスナーが追加され、ボタンがクリックされると connect 関数が実行される
document.addEventListener("DOMContentLoaded", (_event) => {
  // Vite を利用して env.local から取得
  const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
  const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
  const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

  const soraClient = new SoraClient(SIGNALING_URL, CHANNEL_ID, ACCESS_TOKEN);
  document
    .querySelector("#connect")
    ?.addEventListener("click", () => soraClient.connect());
  document
    .querySelector("#disconnect")
    ?.addEventListener("click", () => soraClient.disconnect());
});

受信のみで接続する

recvonly() を使用して接続します。

<!doctype html>
<html lang="ja">

<head>
  <meta charset="UTF-8" />
</head>

<body>
  <hr />
  <div id="remoteVideos"></div>

  <button id="connect">接続</button>
  <button id="disconnect">切断</button>

  <script type="module" src="./main.mts"></script>

</body>

</html>
import Sora, {
  type SoraConnection,
  type ConnectionSubscriber,
} from "sora-js-sdk";

class SoraClient {
  private debug = true;

  private signalingUrl: string;
  private channelId: string;
  private accessToken: string;

  private connection: SoraConnection;
  private recvonly: ConnectionSubscriber;

  private streams: Record<string, MediaStream> = {};

  constructor(signalingUrl: string, channelId: string, accessToken: string) {
    this.signalingUrl = signalingUrl;
    this.channelId = channelId;
    this.accessToken = accessToken;

    // 接続先の Sora を設定する
    this.connection = Sora.connection(this.signalingUrl, this.debug);
    const metadata = {
      // Sora では特に "access_token" と決まっているわけではありません
      // access_token は Sora Labo や Sora Cloud の想定です
      access_token: this.accessToken,
    };
    const options = {};
    this.recvonly = this.connection.recvonly(this.channelId, metadata, options);

    // ontrack イベント
    // メディアストリームトラック単位で発火する
    this.recvonly.on("track", this.onaddtrack.bind(this));

    // removetrack イベント (リモートメディアストリームが削除されたときに発生)
    this.recvonly.on("removetrack", this.onremovetrack.bind(this));
  }

  async connect() {
    // 接続
    await this.recvonly.connect();
  }

  async disconnect() {
    // 切断
    await this.recvonly.disconnect();

    // リモートビデオを全て削除
    const remoteVideos = document.querySelector("#remoteVideos");
    if (remoteVideos) {
      remoteVideos.innerHTML = "";
    }
  }

  getStats(): Promise<RTCStatsReport | null> {
    if (!this.recvonly.pc) {
      return Promise.resolve(null);
    }    

    return this.recvonly.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;
  }
}

// DOMContentLoaded イベントは、ページ全体が読み込まれ、DOMが準備できたときに発生する
// これを利用して、必要な DOM 要素が利用可能になったタイミングで addEventListener を呼び出している
// ページ読み込み後にボタンにクリックイベントリスナーが追加され、ボタンがクリックされると connect 関数が実行される
document.addEventListener("DOMContentLoaded", (_event) => {
  // Vite を利用して env.local から取得
  const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
  const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
  const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

  const soraClient = new SoraClient(SIGNALING_URL, CHANNEL_ID, ACCESS_TOKEN);
  document
    .querySelector("#connect")
    ?.addEventListener("click", () => soraClient.connect());
  document
    .querySelector("#disconnect")
    ?.addEventListener("click", () => soraClient.disconnect());
});

接続して 5 秒後に切断する

disconnect() を使用して接続を切断します。

import Sora from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectToSora = async () => {
  // オーディオとビデオのストリームを取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
  });

  const debug = true;
  // 接続先の Sora を設定する
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  const options = {};
  const sendrecv = soraConnection.sendonly(channelId, metadata, options);
  await sendrecv.connect(stream);

  console.log("Connected to Sora");

  // 5 秒後に切断する
  setTimeout(async () => {
    console.log("Disconnect from Sora");
    await sendrecv.disconnect();
  }, 5000);
};

connectToSora();

サイマルキャストを有効にして接続する

サイマルキャスト (Simulcast) は配信時に 1 つの RTCPeerConnection から複数種類のエンコードした映像を配信する機能です。 詳しくは Sora ドキュメント サイマルキャスト をご確認ください。

ConnectionBase オプションsimulcast: true を指定します

import Sora, { type VideoCodecType } from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectToSora = async () => {
  // オーディオとビデオのストリームを取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
  });

  const debug = true;
  // 接続先の Sora を設定する
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  const options = {
    simulcast: true,
    videoCodecType: "VP8" as VideoCodecType,
  };
  const sendonly = soraConnection.sendonly(channelId, metadata, options);
  await sendonly.connect(stream);

  console.log("Connected to Sora");
};

connectToSora();

スポットライトで接続する

スポットライト (Spotlight) は一定の音量を超えて音声を発している参加者の場合は音声や高画質映像を、それ以外の参加者は音声のない低画質映像を配信する機能です。 詳しくは Sora ドキュメント スポットライト をご確認ください。

ConnectionBase オプションspotlight: true multistream: true を指定します

import Sora, { type VideoCodecType } from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectToSora = async () => {
  // オーディオとビデオのストリームを取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
  });

  const debug = true;
  // 接続先の Sora を設定する
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  const options = {
    simulcast: true,
    // スポットライト機能を有効にする
    spotlight: true,
    videoCodecType: "VP8" as VideoCodecType,
  };

  const sendonly = soraConnection.sendonly(channelId, metadata, options);
  await sendonly.connect(stream);

  console.log("Connected to Sora");
};

connectToSora();

音声や映像コーデックを指定する

音声や映像のコーデックタイプは ConnectionBase オプション で指定します

import Sora, { type AudioCodecType, type VideoCodecType } from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectToSora = async () => {
  // オーディオとビデオのストリームを取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
  });

  const debug = true;
  // 接続先の Sora を設定する
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  const options = {
    // 音声コーデックに Opus を指定
    audioCodecType: "OPUS" as AudioCodecType,
    // 映像コーデックに VP9 を指定
    videoCodecType: "VP9" as VideoCodecType,
  };

  const sendrecv = soraConnection.sendrecv(channelId, metadata, options);
  await sendrecv.connect(stream);

  console.log("Connected to Sora");
};

connectToSora();

音声や映像のビットレートを指定する

音声のビットレート指定は推奨しません

音声や映像のビットレートは ConnectionBase オプション で指定します

import Sora from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectToSora = async () => {
  // オーディオとビデオのストリームを取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
  });

  const debug = true;
  // Sora への接続を作成
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  const options = {
    audio: true,
    // オーディオのビットレートを 64 kbps に設定
    audioBitRate: 64,
    video: true,
    // ビデオのビットレートを 192 kbps に設定
    videoBitRate: 192,
  };

  const sendrecv = soraConnection.sendrecv(channelId, metadata, options);
  await sendrecv.connect(stream);

  console.log("Connected to Sora");
};

connectToSora();
  • 音声ビットレートに指定できる範囲は 6-510 です

  • 映像ビットレートに指定できる範囲は 1-30000 です

映像コーデックパラメーターを指定する

ConnectionBase オプション で指定します。

import Sora, { type VideoCodecType } from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectToSora = async () => {
  // オーディオとビデオのストリームを取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
  });

  const debug = true;
  // 接続先の Sora を設定する
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  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, metadata, options);
  await sendonly.connect(stream);

  console.log("Connected to Sora");
};

connectToSora();

AV1 や H.264 や H.265 でも指定可能です。

映像と音声の可否を指定する

ConnectionBase オプション で指定します。

音声なし

import Sora from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectToSora = async () => {
  // ユーザーメディア(ビデオのみ)を取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: false,
    video: true,
  });

  const debug = true;
  // Sora への接続を作成
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  const options = {
    // オーディオは不要
    audio: false,
  };

  const sendrecv = soraConnection.sendrecv(channelId, metadata, options);
  await sendrecv.connect(stream);

  console.log("Connected to Sora");
};

connectToSora();

映像なし

import Sora from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectToSora = async () => {
  // ユーザーメディア(オーディオのみ)を取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: false,
  });

  const debug = true;
  // 接続先の Sora を設定する
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  const options = {
    // ビデオは不要
    video: false,
  };

  const sendonly = soraConnection.sendonly(channelId, metadata, options);
  await sendonly.connect(stream);

  console.log("Connected to Sora");
};

connectToSora();

クライアントIDを指定する

接続時やサーバー認証成功時に任意の文字列を指定できる値です。 詳しくは Sora ドキュメント WebSocket 経由のシグナリング client_id を参照してください。

ConnectionBase オプション で指定します。

import Sora from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectToSora = async () => {
  // オーディオとビデオのストリームを取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
  });

  const debug = true;
  // Sora への接続を作成
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  const options = {
    // client-id に xyz を指定
    // client-id は重複可能
    clientId: "xyz",
  };

  const sendrecv = soraConnection.sendrecv(channelId, metadata, options);
  await sendrecv.connect(stream);

  console.log("Connected to Sora");
};

connectToSora();

DataChannel 経由のシグナリングを使用する

WebRTC 接続確立後に、シグナリングを WebSocket 経由から DataChannel 経由に切り替える機能です。 詳しくは Sora ドキュメント DataChannel 経由のシグナリング を参照してください。

ConnectionBase オプション で指定します。

import Sora from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectToSora = async () => {
  const mediaStream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
  });

  const debug = true;
  // 接続先の Sora を設定する
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  const options = {
    // シグナリングを WebSocket 経由から DataChannel 経由に切り替えるかどうかを指定
    dataChannelSignaling: true,
    // シグナリングを DataChannel 経由に切り替えたあとに WebSocket を切断するかどうかを指定
    // true にした場合、 Sora JS SDK は自動で WebSocket を切断します
    ignoreDisconnectWebSocket: false,
  };

  const sendrecv = soraConnection.sendrecv(channelId, metadata, options);
  await sendrecv.connect(mediaStream);
};

connectToSora();

メッセージング機能を使用する

DataChannel を利用したデータの送受信を行える機能です。 詳しくは Sora ドキュメントの メッセージング機能 をご確認ください。

ConnectionBase オプション で指定します。

import Sora, { type DataChannelDirection } from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectAndSendMessage = async () => {
  const mediaStream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
  });

  const debug = true;
  // 接続先の Sora を設定する
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  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 = soraConnection.sendrecv(channelId, metadata, options);

  // メッセージ送信が可能な datachannel との接続が確立した場合に on datachannel イベントが発火する
  sendrecv.on("datachannel", (event) => {
    // event.datachannel にメッセージ送信可能な datachannel の情報が含まれる
    const label = event.datachannel.label; // #example
    console.log("ondatachannel", label);
    // メッセージを送信する
    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);
  });

  await sendrecv.connect(mediaStream);

  console.log("Connected to Sora");
};

// Soraへの接続とメッセージ送受信を開始
connectAndSendMessage();

メッセージングのみで接続することも可能です。ただし connect 時に空のメディアストリームを渡す必要があります。

import Sora, { type DataChannelDirection } from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectAndSendMessage = async () => {
  const debug = true;
  // 接続先の Sora を設定する
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };

  // ConnectionOptions の dataChannels を指定する
  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 recvonly = soraConnection.recvonly(channelId, metadata, options);
  await recvonly.connect();
  // Sora 2023.2.0 以前のバージョンでは sendrecv で接続する必要がある
  // もし sendrecv や sendonly を利用する場合は空のメディアストリームを渡す
  // await sendrecv.connect(new MediaStream());

  // メッセージを送信する
  recvonly.sendMessage("#example", new TextEncoder().encode("Hello world."));
};

connectAndSendMessage();

シグナリング通知機能を使用する

シグナリング通知機能は自分の接続状況や他の接続の参加や離脱などの通知する仕組みです。

詳しくは Sora ドキュメント の シグナリング通知 をご確認ください。

import Sora, { type SignalingNotifyMessage } from "sora-js-sdk";

// Vite を利用して env.local から取得
const SIGNALING_URL = import.meta.env.VITE_SORA_SIGNALING_URL;
const CHANNEL_ID = import.meta.env.VITE_SORA_CHANNEL_ID;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN;

const connectToSora = async () => {
  // オーディオとビデオのストリームを取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
  });

  const debug = true;
  // 接続先の Sora を設定する
  const soraConnection = Sora.connection(SIGNALING_URL, debug);
  const channelId = CHANNEL_ID;
  const metadata = {
    access_token: ACCESS_TOKEN,
  };
  const options = {
    multistream: true,
  };
  const sendonly = soraConnection.sendonly(channelId, metadata, options);

  // シグナリング通知
  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);
    }
  });

  await sendonly.connect(stream);

  console.log("Connected to Sora");
};

connectToSora();
© Copyright 2024, Shiguredo Inc. Created using Sphinx 7.3.7