# FAQ


## 対応ブラウザについて

Sora JavaScript SDK の 2024.2.0 以降を利用するには [Compression Stream API](https://developer.mozilla.org/ja/docs/Web/API/Compression_Streams_API) をサポートしているブラウザのバージョンが必要になります。

- Chrome 80 以降
- Edge 80 以降
- Firefox 113 以降
- Safari 16.4 以降

## Sora JavaScript SDK のサンプルについて

### Sora JavaScript SDK のサンプル

Sora JavaScript SDK の最小限の書き方を確認できるサンプルです。



### Sora JavaScript SDK ドキュメントのサンプル

Sora JavaScript SDK の解説を目的としたサンプルです。

[サンプル](examples.html)

## 仕様

### MediaStreamTrack と Connection ID の紐付け

Sora JavaScript SDK は [MediaStreamTrack](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) と Connection ID の紐付けを行う仕組みは提供していません。

ただし `track` コールバック時に、
[RTCTrackEvent](https://developer.mozilla.org/en-US/docs/Web/API/RTCTrackEvent) の [streams](https://developer.mozilla.org/en-US/docs/Web/API/RTCTrackEvent/streams) から [MediaStream](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream) を取得できます。
[MediaStream](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream) の [stream.id](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/id) が Connection ID となります。

```javascript
client.on("track", (event: RTCTrackEvent) => {
  // Sora では必ず 1 クライアント から送られてくるのは 1 音声/ 1 映像に固定されています
  // そのため track.streams[0] でクライアントのメディアストリームを取得できます
  const stream = event.streams[0];

  // メディアストリームの ID はその音声と映像を配信しているクライアントの connectionId です
  // stream.id で取得できます
  console.log("connectionId: ", stream.id);
});
```






### メディアストリームを SDK から扱えますか？

メディアストリームを SDK から扱うことはできません。

Sora JavaScript SDK は音声や映像といったメディアストリームを SDK から扱うことはありません。

そのため音声や映像を扱う場合は [MediaDevices.getUserMedia()](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) API や [MediaDevices.getDisplayMedia()](https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getDisplayMedia) API を利用し、
アプリケーション開発者がメディアストリームを取得し、Sora JavaScript SDK に渡す必要があります。

### マルチトラックには対応していますか？

Sora JavaScript SDK が配信可能なメディアストリームは 1 本で 1 音声トラック、1 映像トラックとなります。
これは Sora の仕様になります。

そのため、マルチトラックには対応しておりません。

もし複数の音声や映像を 1 クライアントで配信したい場合は複数のコネクションを張るようにしてください。

### `"type": "answer"` の SDP 書き換えは SDK の挙動に影響はありますか？

Sora JS SDK では `createAnswer` が生成した SDP をそのまま利用するようにしています。
そのため、書き換え処理で SDK 側には基本的に影響することはありません。

もし SDP 書き換えをを行う場合は `"type": "answer"` だけでなく、
`"type": "re-answer"` も書き換えるようにしてください。

> **注釈**
>
> WebRTC API を利用せずに手動で SDP を変更する処理を "SDP Munging" と呼びます。

Sora への影響については Sora または Sora Cloud のサポートまでご連絡ください。

## シグナリング

### 切断が発生し、再接続をする場合はどのようにすればいいですか？

何らかの理由で JavaScript SDK と Sora との接続が切断された際、
かならずインスタンスを作り直して再接続をしてください。

今まで利用していたインスタンスは破棄するようにしてください。

### Sora との接続が完了したことを通知するコールバックはありますか？

`connected` コールバックを利用してください。
このコールバックは自分の接続確立時に 1 度だけ発火します。

```javascript
// 色々省略
const sendrecv = soraConnection.sendrecv(channelId, metadata, options);

// connected コールバック（自分の接続確立時に1度だけ発火）
sendrecv.on("connected", (event: SignalingNotifyConnectionCreated) => {
  console.log("connected");
  console.log("self-connection_id: ", event.connection_id);
});

await sendrecv.connect(stream);
```

## サイマルキャスト機能

### ブラウザで縦画面のサイマルキャストで期待した本数のストリームが出力されません

残念ながら、縦画面を利用したサイマルキャストで期待した本数のストリームが出力されないのは、
ビットレートが足りない以外であれば、ブラウザの仕様の可能性があります。

ブラウザのサイマルキャストは解像度が出力するストリームに依存します。

ブラウザのサイマルキャストの仕様は Sora のドキュメントに記載してあります。

たとえば 540x960 (width x height) であればストリーム 3 本出力されますが、
405x720 (width x height) の場合はストリーム 2 本しか出力されません。

## データチャネルメッセージング機能

### メッセージングのみを利用したいです

`messaging()` を利用する事でメッセージングのみを利用できます。

```javascript
const options = {
  dataChannelSignaling: true,
  dataChannels: [
    {
      label: "#example",
      direction: "sendrecv" as DataChannelDirection,
    },
    {
      label: "#example2",
      direction: "recvonly" as DataChannelDirection,
    },
  ],
};
const messaging = soraConnection.messaging(channelId, metadata, options);
await messaging.connect();
```

### maxPacketLifeTime や maxRetransmits が設定できません

Sora のシグナリング仕様では `snake_case` での指定ですが、
Sora JavaScript SDK では `camelCase` での指定となります。

```javascript
const options = {
  dataChannelSignaling: true,
  dataChannels: [
    {
    label: "#example",
    direction: "sendrecv" as DataChannelDirection,
    maxPacketLifeTime: 60000,
    },
  ],
};
```

```javascript
const options = {
  dataChannelSignaling: true,
  dataChannels: [
    {
      label: "#example",
      direction: "sendrecv" as DataChannelDirection,
      maxRetransmits: 0,
    },
  ],
};
```



## RPC 機能

### RPC 機能が利用できません

Sora では本来 API 経由で実行する仕組みをデータチャネル経由で実行できる RPC 機能を Sora 2025.2.0 から提供しています。

RPC 機能を利用するには Sora の認証成功時に `rpc_methods` で利用できる RPC メソッド一覧を指定する必要があります。この指定がない場合は RPC 機能を利用できません。

以下のように Sora 側で認証成功時に `rpc_methods` を指定してください。

```javascript
{
  "allowed": true,
  "rpc_methods": [
    "2025.2.0/RequestSimulcastRid",
  ]
}
```

`rpc_methods` で指定したメソッドを Sora JavaScript SDK から呼び出せます。

詳細は [RPC 機能を使用する](examples.html#da4b6a) をご確認ください。

## WebRTC API

Sora JavaScript SDK には Sora との接続部分の機能のみを提供しています。

そのため、映像や音声、データなどを実際に処理する場合はすべて JavaScript の WebRTC API やライブラリなどを利用する必要があります。

[WebRTC API - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/WebRTC_API)

### 解像度を変更するにはどうしたらいいですか？

解像度の変更は [MediaDevices.getUserMedia()](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) や [MediaDevices.getDisplayMedia()](https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getDisplayMedia) API 側で行ってください。

### 背景をぼかすにはどうしたらいいですか？

時雨堂がオープンソースとして公開している [@shiguredo/virtual-background - npm](https://www.npmjs.com/package/@shiguredo/virtual-background) を利用することで簡単にブラウザで背景ぼかし機能を利用可能です。

以下で動作確認が可能です。

### ノイズを抑制できますか？

時雨堂がオープンソースとして公開している [@shiguredo/noise-suppression - npm](https://www.npmjs.com/package/@shiguredo/noise-suppression) を利用することで簡単にブラウザでノイズ抑制機能を利用可能です。

以下で動作確認が可能です。

### 音声でステレオは利用できますか？

ステレオ入力やステレオ出力はブラウザや端末、マイク依存になります。

詳細は [ステレオ入出力](stereo.html) を参照してください。

### 画面キャプチャはできますか？

[MediaDevices.getDisplayMedia()](https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getDisplayMedia) を利用して画面のストリームを取得してください。

### カメラを切り替えられますか？

トラックを切り替えたい場合は [RTCRtpSender.replaceTrack()](https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpSender/replaceTrack) を利用して既存のトラックと置き換えを行ってください。

### 音声をミュートにできますか？

Sora JavaScript SDK はトラックのミュートは行いません。

トラックのミュートを行いたい場合は [MediaStreamTrack.enabled](https://developer.mozilla.org/ja/docs/Web/API/MediaStreamTrack/enabled) を利用して、ミュートを行ってください。

### Electron で利用したいです

Sora JavaScript SDK は [Electron](https://www.electronjs.org/) でも利用可能です。

### MediaStreamTrack から MediaStream の ID を取得したいです

MediaStreamTrack から MediaStream の ID は取得できません。
そのため、もし取得したい場合は何かしらの方法で ID を保持しておく必要があります。

ただし `removetrack` コールバックは MediaStream が発火させるため `event.target` で発火した MediaStream を取得可能です。

[MediaStream: removetrack event - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/removetrack_event)

### CPU や帯域による解像度やフレームレートの制限を無効にしたいです

無効にできません。

ブラウザで CPU 負荷が高まったと判断して、利用ビットレートを下げたことにより解像度やフレームレートが下がる場合、
Chrome 102 までは `googCpuOveruseDetection` という設定があり、 `false` にすることで、判定を無効にできていましたが、
その設定は Chrome 103 にて廃止されました。

> **ヒント**
>
> 制限の理由は qualityLimitationReason で確認できます。

- [PSA: RTCPeerConnection's mediaConstraints - most constraints removed in M103](https://groups.google.com/g/discuss-webrtc/c/2l7jWgreYpw/m/v_TdkRfTAAAJ)
- [PSA: RTCPeerConnection's mediaConstraints is deprecated and will soon be removed](https://groups.google.com/g/discuss-webrtc/c/85e-f_siCws/m/4yWismzCAQAJ)

### 映像を回転させたいです

getUserMedia で取得した映像を回転させるには [Canvas API](https://developer.mozilla.org/ja/docs/Web/API/Canvas_API) を利用してください。

```html
<!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>
```

```typescript
// getUserMedia で取得した映像を Canvas API を利用して 90 度回転する

document.addEventListener("DOMContentLoaded", async () => {
  const originalVideo = document.getElementById("originalVideo") as HTMLVideoElement;
  const rotatedVideo = document.getElementById("rotatedVideo") as HTMLVideoElement;

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

  originalVideo.srcObject = mediaStream;

  const rotatedMediaStream = rotateMediaStream(mediaStream);
  rotatedVideo.srcObject = rotatedMediaStream;
});

const rotateMediaStream = (mediaStream: MediaStream): MediaStream => {
  // 元の映像トラックを取得
  const videoTrack = mediaStream.getVideoTracks()[0];

  // 映像の設定を取得
  const settings = videoTrack.getSettings();
  const width = settings.width || 640;
  const height = settings.height || 480;

  // Canvas要素を作成
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");

  // 90度回転させるため、幅と高さを入れ替える
  canvas.width = height;
  canvas.height = width;

  // Video要素を作成
  const video = document.createElement("video");
  video.srcObject = mediaStream;
  video.autoplay = true;

  // 映像フレームを描画して回転させる
  video.addEventListener("play", () => {
    const draw = () => {
      if (ctx) {
        // Canvasを90度回転
        ctx.save();
        ctx.translate(canvas.width, 0);
        ctx.rotate(Math.PI / 2);
        ctx.drawImage(video, 0, 0, width, height);
        ctx.restore();
      }
      requestAnimationFrame(draw);
    };
    draw();
  });

  // 回転した映像をMediaStreamとして取得
  return canvas.captureStream();
};
```
