# 音声ストリーミング機能

## 概要

Sora に WebRTC で送られてきた音声を、接続ごとに HTTP/2 で外部へ出力し、外部から受信した結果をクライアントへプッシュで通知する機能です。

## 注意

### OBS (WHIP) 機能利用時の Opus の設定

OBS の Opus のビットレートがデフォルトの場合は正常に動作しない場合があります。
その場合はビットレートを 128 kbps まで下げて見てください。

### IPv6 非対応

現時点で、音声ストリーミングは IPv6 を利用できません。

### 音声ストリーミングの送信単位

音声ストリーミングを [audio_streaming_url](SORA_CONF.html#6b5dd6) へ送信するのは、
コネクション単位であり、セッション単位ではありません。

## HTTP/2

- 音声ストリーミングゲートウェイは HTTP/2 に対応している必要があります
- h2c と h2 の両方に対応しています
- `http://` から始めると h2c を利用します
- `https://` から始めると h2 を利用します

## 音声ストリーミングの配信

音声ストリーミング機能はセッション単位で有効になります。

実際に音声ストリーミングで音声が外部に配信されるのは、
`role` が `sendrecv` または `sendonly`、かつ `audio` が `true` になっている場合のみ有効になります。

> **注釈**
>
> 外部から受信した結果はロールなどは関係なく、サブスクライブしているすべてのクライアントが受信できます。

## 音声ストリーミングのヘッダー

音声ストリーミング機能において、HTTP/2 経由で送信する音声パケットに Sora 側でヘッダーを追加する機能です。

`sora.conf` の [audio_streaming_header](SORA_CONF.html#3edcef) を `true` に設定するとヘッダーが追加されます。

```mermaid
---
title: "音声ストリーミングヘッダーフォーマット"
---
packet-beta
0-63: "Timestamp"
64-127: "SeqNum"
128-159: "Length"
```

- `Timestamp` は音声パケット送信時の UTC 時間マイクロ秒の整数です- RTP のタイムスタンプとは異なります
  - 64 ビット符号なし整数です
- `SequenceNumber` は音声パケットのシーケンス番号で、 1 から始まります- RTP のシーケンス番号とは異なります
  - 64 ビット符号なし整数です
- `Length` はヘッダーを除いた音声パケットの長さです- 32 ビット符号なし整数です

## 言語コードの指定

音声ストリーミングを利用する場合、 言語コードを指定する必要があります。

シグナリング接続時、または認証成功時の払い出しで `{"audio_streaming_language_code": "ja-JP"}` のように指定してください。
指定するのは **文字列であればなんでもかまいません** 。Sora 自体は言語コードには関与しません。あくまでただの文字列として扱います。

> **注意**
>
> 言語コードが指定されていない接続は、音声ストリーミングが開始している場合でも音声ストリーミングを行いません。
> 言語コードは音声を解析するにあたり必要になるためこのような仕様になっています。

## セッションウェブフック経由での開始

音声ストリーミングはセッション単位で開始、終了します。

セッション生成時から音声ストリーミングを開始したい場合、
セッションウェブフックの戻り値に `{"audio_streaming": true}` を指定することで音声ストリーミングが開始します。

そのセッションに参加してきた言語コードが指定されているクライアントはすべて音声ストリーミングが行われます。

## セッション破棄時の音声ストリーミング終了

チャネルのセッションが破棄したタイミングで音声ストリーミングは終了します。

## API 経由での開始と終了

セッションの途中で音声ストリーミングを開始する場合は [StartAudioStreaming](API_AUDIO_STREAMING.html#0f1087) API を利用する必要があります。

また、セッションの途中で音声ストリーミングを終了する場合は [StopAudioStreaming](API_AUDIO_STREAMING.html#bad1bb) API を利用する必要があります。


## 自動開始/停止機能

サブスクライブ中のコネクション数が 1 つ以上に増えたとき、自動的に音声ストリーミングが開始します。
逆に、サブスクライブ中のコネクション数が 0 になったときには、音声ストリーミングが自動的に停止します。

この機能を有効にすることで、サブスクライブが 0 の時は音声ストリーミングを行わなくなり、
転送量などを抑えることができます。

以下の方法でこの機能を有効にできます:

- セッションウェブフックの `session.created` 時の払い出しで `{"audio_streaming": true, "audio_streaming_auto": true}` を指定した場合

> **注意**
>
> この機能が有効化されているときには、
> [StartAudioStreaming](API_AUDIO_STREAMING.html#0f1087) API と [StopAudioStreaming](API_AUDIO_STREAMING.html#bad1bb) API は利用できません。
>
> この機能を停止するにはセッションを破棄する必要があります。
> API を使用してセッションを破棄する場合には、 [TerminateSession](API_SESSION.html#ba022b) API を利用してください。

## リトライ条件

Sora は音声ストリーミングゲートウェイへの接続が失敗した際、リトライを行います。

リトライを行うには [audio_streaming_max_retries](SORA_CONF.html#9fd67a) と [audio_streaming_retry_interval](SORA_CONF.html#15f73d) に `0` 以外の値を指定してある必要があります。

### リトライを行う

- 音声ストリーミングゲートウェイへ接続ができなかった場合
- 音声ストリーミングゲートウェイへ接続時に 5xx 系のステータスコードが返された場合

### リトライを行わない

- 接続時に 4xx 系が送られてきた場合
- 接続後に `"type": "error"` が送られてきた場合


## audio_streaming 関連の払い出し設定の組み合わせによる動作

### 音声ストリーミングゲートウェイへの接続

セッションウェブフックの session.created 時に払い出した、 `audio_streaming`, `audio_streaming_auto` の組み合わせごとの `配信者接続時` の音声ストリーミングゲートウェイへの接続の有無は下記の通りです。

* - audio_streaming の指定
  - audio_streaming_auto の指定
  - subscribe 数
  - 音声ストリーミングゲートウェイへの接続
* - true
  - true [#f1]_
  - 0
  - 無し
* - true
  - true [#f1]_
  - 1 以上
  - 有り
* - true
  - false
  - 0
  - 有り [#f2]_
* - true
  - false
  - 1 以上
  - 有り [#f2]_
* - true
  - 指定なし
  - 0
  - 有り [#f2]_
* - true
  - 指定なし
  - 1 以上
  - 有り [#f2]_
* - false
  - false
  - 0
  - 無し
* - false
  - false
  - 1 以上
  - 無し [#f3]_
* - false
  - 指定なし
  - 0
  - 無し [#f3]_
* - false
  - 指定なし
  - 1 以上
  - 無し [#f3]_
* - 指定なし
  - false
  - 0
  - 無し
* - 指定なし
  - false
  - 1 以上
  - 無し [#f3]_
* - 指定なし
  - 指定なし
  - 0
  - 無し [#f3]_
* - 指定なし
  - 指定なし
  - 1 以上
  - 無し [#f3]_

[^#f1]: .. [#f1] audio_streaming_auto に true を指定する場合には、audio_streaming にも true を指定する必要があります

[^#f2]: .. [#f2] 音声ストリーミングゲートウェイへの接続有無はサブスクライブの数に依存しません

[^#f3]: .. [#f3] 接続には StartAudioStreaminga API の実行が必要です

> **注意**
>
> audio_streaming_auto に true を指定したにもかかわらず、audio_streaming を指定しないか、または false を指定した場合は、不正な組み合わせのため audio_streaming_auto、audio_streaming の指定は無視されます

### クライアントへの解析結果の送信

セッションウェブフックの session.created 時に払い出した、 `audio_streaming`, `audio_streaming_auto` の組み合わせごとの subscribe しているクライアントへの解析結果の送信有無は下記の通りです。

* - audio_streaming の指定
  - audio_streaming_auto の指定
  - subscribe 数
  - クライアントへの解析結果の送信
* - true
  - true [#f4]_
  - 0
  - 無し
* - true
  - true [#f4]_
  - 1 以上
  - 有り
* - true
  - false
  - 0
  - 無し
* - true
  - false
  - 1 以上
  - 有り
* - true
  - 指定なし
  - 0
  - 無し
* - true
  - 指定なし
  - 1 以上
  - 有り
* - false
  - false
  - 0
  - 無し
* - false
  - false
  - 1 以上
  - 無し
* - false
  - 指定なし
  - 0
  - 無し
* - false
  - 指定なし
  - 1 以上
  - 無し
* - 指定なし
  - false
  - 0
  - 無し
* - 指定なし
  - false
  - 1 以上
  - 無し
* - 指定なし
  - 指定なし
  - 0
  - 無し
* - 指定なし
  - 指定なし
  - 1 以上
  - 無し

[^#f4]: .. [#f4] audio_streaming_auto に true を指定する場合には、audio_streaming にも true を指定する必要があります

> **注意**
>
> audio_streaming_auto に true を指定したにもかかわらず、audio_streaming を指定しないか、または false を指定した場合は、不正な組み合わせのため audio_streaming_auto、audio_streaming の指定は無視されます

## 1 コネクション 1 HTTP/2 コネクション

音声ストリーミングが開始している状態でそのチャネルに新規クライアントが参加すると、
`audio_streaming_url` 宛てに新しく HTTP/2 コネクションが張られ、
クライアントから送られてきた Opus のバイナリデータがそのまま送られます。

HTTP/2 コネクションを張る際のヘッダーには以下の値が入ります。

- sora-channel-id- クライアントのチャネル ID
- sora-connection-id- クライアントのコネクション ID
- sora-audio-streaming-language-code- クライアントの言語コード










## プッシュ通知経由での戻り値の通知

シグナリングのプッシュ通知経由で外部サーバーからの戻り値を通知します。

```javascript
{
    "type": "push",
    "data": {
        "type": "audio_streaming_result",
        "connection_id": "<connection_id>",
        "result": "レスポンスがそのまま含まれる"
    }
}
```


## ウェブフック

音声ストリーミングの開始、終了時にウェブフックを通知します。

このウェブフックはデフォルト無効です。

もし利用する場合は `sora.conf` の [ignore_audio_streaming_webhook](SORA_CONF.html#8f086b) を `false` にして通知を有効にしてください。

### audio-streaming.started

- セッションで音声ストリーミングが開始した場合に通知されます
- 自動開始/停止が有効の場合は `"audio_streaming_auto": true` が入ってきます
- 音声ストリーミングを開始した時間が `"audio_streaming_started_timestamp"` に入ってきます
- このウェブフックはかならず `session.created` よりも後に送信されます

```javascript
{
  "data": {
    "audio_streaming_auto": false,
    "audio_streaming_started_timestamp": "2023-05-01T08:23:58.489262Z"
  },
  "id": "VWGZ8NK2C55XBCGQ54SECK7J08",
  "label": "WebRTC SFU Sora",
  "timestamp": "2023-05-01T08:24:20.757279Z",
  "type": "audio-streaming.started",
  "version": "2023.2.0",
  "max_connections": 1,
  "node_name": "sora@127.0.0.1",
  "session_id": "DYPX8R6F4D11ZEG6XCX6N8EEWM",
  "channel_id": "sora",
  "spotlight": false,
  "created_time": 1682929437,
  "total_connections": 1,
  "created_timestamp": "2023-05-01T08:23:57.274844Z"
}
```

### audio-streaming.stopped

- セッションで音声ストリーミング停止した場合に通知されます
- 自動開始/停止が有効な場合は `"audio_streaming_auto": true` が入ってきます
- 音声ストリーミングを開始した時間が `"audio_streaming_started_timestamp"` に入ってきます
- 音声ストリーミングを停止した時間が `"audio_streaming_stopped_timestamp"` に入ってきます
- このウェブフックはかならず `session.destroyed` よりも前に送信されます

```javascript
{
  "data": {
    "audio_streaming_auto": false,
    "audio_streaming_started_timestamp": "2023-05-01T08:23:58.489262Z",
    "audio_streaming_stopped_timestamp": "2023-05-01T08:24:20.756785Z"
  },
  "id": "VWGZ8NK2C55XBCGQ54SECK7J08",
  "label": "WebRTC SFU Sora",
  "timestamp": "2023-05-01T08:24:20.757279Z",
  "type": "audio-streaming.stopped",
  "version": "2023.2.0",
  "max_connections": 1,
  "node_name": "sora@127.0.0.1",
  "session_id": "DYPX8R6F4D11ZEG6XCX6N8EEWM",
  "channel_id": "sora",
  "spotlight": false,
  "created_time": 1682929437,
  "total_connections": 1,
  "created_timestamp": "2023-05-01T08:23:57.274844Z"
}
```

### audio-streaming.failed

> **重要**
>
> このウェブフックは **イベントウェブフック** として送信されます。

このウェブフックは、Sora が接続する音声ストリーミングゲートウェイが利用するサービスに不具合があったことを、Sora に伝える仕組みです。

- `sora.conf` の [audio_streaming_max_retries](SORA_CONF.html#9fd67a) が `0` では無い場合に有効になります
- `sora.conf` の [ignore_audio_streaming_failed_webhook](SORA_CONF.html#80d7fb) が `false` の場合にウェブフックが送られるようになります- `true` にした場合でも `event_webhook.jsonl` にログは出力されます

リトライが発生する場合は最大リトライ数を超えている場合に、以下の状況でウェブフックが送信されます。

- 音声ストリーミングゲートウェイに接続した際 5xx 系のエラーが返ってきた場合に送信します
- 音声ストリーミングゲートウェイに接続した際 4xx 系のエラーが返ってきた場合に送信します
- 音声ストリーミングゲートウェイから `"type": "error"` が返ってきた場合に送信します
- 音声ストリーミングゲートウェイへの接続確立がタイムアウト以外で失敗した場合に送信します

音声ストリーミングを再開したい場合は、再接続をしてください。

音声ストリーミングゲートウェイへの接続がタイムアウトになった場合にはウェブフックは送信されません。

> **注釈**
>
> 音声ストリーミングゲートウェイへの接続は HTTP/2 で行われます。
> そのため接続の途中で問題が起きた場合は HTTP ステータスコードを受け取ることはできません。
> その代わりに音声ストリーミングゲートウェイが送ってくる `"type": "error"` メッセージでエラーを検知します。

```javascript
{
  "data": {
    "failed_reason": "<string>"
  },
  "id": "VWGZ8NK2C55XBCGQ54SECK7J08",
  "label": "WebRTC SFU Sora",
  "timestamp": "2023-05-01T08:24:20.757279Z",
  "type": "audio-streaming.failed",
  "role": "sendrecv",
  "version": "2024.1.0",
  "node_name": "sora@127.0.0.1",
  "client_id": "B2JPGFZPMD3H973Y811MF8ZZ70",
  "connection_id": "B2JPGFZPMD3H973Y811MF8ZZ70",
  "bundle_id": "B2JPGFZPMD3H973Y811MF8ZZ70",
  "session_id": "DYPX8R6F4D11ZEG6XCX6N8EEWM",
  "channel_id": "sora",
  "simulcast": false,
  "spotlight": false,
  "log_written": true
}
```

## シグナリング通知経由で audio-streaming.failed をクライアントへ通知する

このシグナリング通知は、Sora が接続する音声ストリーミングゲートウェイが利用するサービスに不具合があったことを、
そのチャネルに参加している全てのクライアントへ通知する仕組みです。

`sora.conf` にて [signaling_notify_audio_streaming_failed](SORA_CONF.html#d13e38) に `true` を指定した場合、
[audio_streaming_url](SORA_CONF.html#6b5dd6) への接続を諦めた際、セッションに参加している全てのコネクションにシグナリング通知 `audio-streaming.failed` を送信します。

この設定はデフォルトで `false` です。

```javascript
{
  "type": "notify",
  "event_type": "audio-streaming.failed",
  "failed_connection_id": "<connection_id>"
}
```

## 設定

### audio_streaming_url

**デフォルト**: 指定なし

音声ストリーミングゲートウェイの URL を指定してください。
音声ストリーミングゲートウェイは HTTP/2 に対応している必要があります。

https または http から始まる URL を指定してください。

詳細は [audio_streaming_url](SORA_CONF.html#6b5dd6) をご確認ください。

### audio_streaming_max_retries

**デフォルト**: 0
**範囲**: 0..10

音声ストリーミングゲートウェイへの接続が失敗した場合の最大リトライ回数を指定してください。

詳細は [audio_streaming_max_retries](SORA_CONF.html#9fd67a) をご確認ください。

### audio_streaming_retry_interval

**デフォルト**: 5 s
**範囲**: 0..60 s

音声ストリーミングゲートウェイへの接続が失敗した場合のリトライ間隔を指定してください。

詳細は [audio_streaming_retry_interval](SORA_CONF.html#15f73d) をご確認ください。

### default_audio_streaming_result_push

**デフォルト**: true

音声ストリーミングゲートウェイからのレスポンスをシグナリングプッシュ通知で送ることをデフォルトで行うかを指定してください。

詳細は [default_audio_streaming_result_push](SORA_CONF.html#0b4a07) をご確認ください。

### default_audio_streaming_language_code

**デフォルト**: 指定なし

音声ストリーミングゲートウェイ接続時に HTTP ヘッダー `sora-audio-steraming-language-code` にデフォルトで含める文字列を指定してください。

詳細は [default_audio_streaming_language_code](SORA_CONF.html#856531) をご確認ください。

### audio_streaming_tls_fullchain_file

**デフォルト**: 指定なし

音声ストリーミングゲートウェイとの通信に HTTPS で mTLS を利用するための設定です。
中間証明書を含む PEM 形式 のクライアント証明書のパスを設定して下さい。

詳細は [audio_streaming_tls_fullchain_file](SORA_CONF.html#e5459b) をご確認ください。

### audio_streaming_tls_privkey_file

**デフォルト**: 指定なし

音声ストリーミングゲートウェイとの通信に HTTPS で mTLS を利用するための設定です。
PEM 形式のクライアント証明書秘密鍵のパスを設定して下さい。

詳細は [audio_streaming_tls_privkey_file](SORA_CONF.html#5f8e61) をご確認ください。

### audio_streaming_tls_verify_cacert_file

**デフォルト**: 指定なし

音声ストリーミングゲートウェイとの通信に HTTPS を利用する際に、
サーバー証明書のチェックを行う PEM 形式の CA ファイルのパスを設定して下さい。

詳細は [audio_streaming_tls_verify_cacert_file](SORA_CONF.html#feef99) をご確認ください。

> **注釈**
>
> CA 証明書を指定しない場合、OS 組み込みの証明書を利用します。詳細は [ウェブフックリクエストなどの送信先サーバー証明書の検証に利用する OS 組み込みのルート CA 証明書について](WEBHOOK.html#e8a845) をご確認ください。

## API

### StartAudioStreaming API

セッションが存在し、音声ストリーミングが開始していないチャネルに対して音声ストリーミングを開始します。

詳細は [StartAudioStreaming](API_AUDIO_STREAMING.html#0f1087) をご確認ください。

> **注釈**
>
> この API は audio_streaming_auto が true の場合は利用できません。

### StopAudioStreaming API

セッションが存在し、音声ストリーミングが開始しているチャネルに対して音声ストリーミングを停止します。

> **注釈**
>
> 音声ストリーミングを終了すると、すべての接続のサブスクライブしている状態は初期化され、デフォルトの値に戻ります。

詳細は [StopAudioStreaming](API_AUDIO_STREAMING.html#bad1bb) をご確認ください。

> **注釈**
>
> この API は audio_streaming_auto が true の場合は利用できません。





### SubscribeAudioStreamingResultPush API

指定した接続が、音声ストリーミングゲートウェイからの戻り値のプッシュ通知をサブスクライブするよう設定します。

詳細は [SubscribeAudioStreamingResultPush](API_AUDIO_STREAMING.html#d26262) をご確認ください。

> **注釈**
>
> デフォルトの値は `sora.conf` の [default_audio_streaming_result_push](SORA_CONF.html#0b4a07) が適用されます。

### UnsubscribeAudioStreamingResultPush API

指定した接続が、音声ストリーミングゲートウェイからの戻り値のプッシュ通知をサブスクライブしないよう設定します。

> **注釈**
>
> デフォルトの値は `sora.conf` の [default_audio_streaming_result_push](SORA_CONF.html#0b4a07) が適用されます。

詳細は [UnsubscribeAudioStreamingResultPush](API_AUDIO_STREAMING.html#b63656) をご確認ください。

### ListAudioStreamingResultPushState API

指定したチャネルのセッションのコネクションごとのサブスクライブの状態を確認します。

```javascript
[
  {
    "connection_id": "B2JPGFZPMD3H973Y811MF8ZZ70",
    "subscribe": true
  },
  {
    "connection_id": "KT116Z11KX5Y547KA1V12HW8PG",
    "subscribe": false
  },
  {
    "connection_id": "A5GYXGRYAX6590M3AFH7F9Z2H4",
    "subscribe": false
  }
]
```

詳細は [ListAudioStreamingResultPushState](API_AUDIO_STREAMING.html#70450f) をご確認ください。

## Audio Streaming Gateway Suzu

> **重要**
>
> Audio Streaming Gateway Suzu は Sora サポートの対象外です

[shiguredo/suzu: Audio Streaming Gateway Suzu](https://github.com/shiguredo/suzu)

Sora から HTTP/2 経由で送られてくるヘッダーや Opus 音声データを Ogg 形式に変換して、
音声からテキストに変換するサービスへ送信し、サービスから受信した解析結果を Sora に返すゲートウェイです。

時雨堂が開発し、 OSS として公開しています。

### 対応サービス

- [Amazon Transcribe](https://aws.amazon.com/jp/transcribe/)
- [Google Cloud Speech-to-Text](https://cloud.google.com/speech-to-text)

## シーケンス図


### Suzu + AWS Transcribe

```mermaid
sequenceDiagram
    autonumber
    participant C as クライアント
    participant S as Sora
    participant Suzu
    participant AWS Transcribe
    C->>S: SRTP
    C->>S: SRTP
    C->>S: SRTP
    note over C, AWS Transcribe: StartAudioStreaming API 実行
    C->>S: SRTP
    S->>Suzu: Opus over HTTP/2
    Suzu->>AWS Transcribe: Ogg over HTTP/2
    C->>S: SRTP
    S->>Suzu: Opus over HTTP/2
    Suzu->>AWS Transcribe: Ogg over HTTP/2
    C->>S: SRTP
    S->>Suzu: Opus over HTTP/2
    Suzu->>AWS Transcribe: Ogg over HTTP/2
    AWS Transcribe->>Suzu: JSON over HTTP/2
    Suzu->>S: JSON over HTTP/2
    S->>C: シグナリングプッシュ通知
```

### 自動開始/停止

```mermaid
sequenceDiagram
  autonumber
  participant C as クライアント
  participant S as Sora
  participant A as アプリケーションサーバー
  participant Suzu
  S->>+A: セッションウェブフック<br>session.created
  A-->>-S: "audio_streaming": true<br>"audio_streaming_auto": true
  C->>S: SRTP
  C->>S: SRTP
  C->>S: SRTP
  A-->>+S: SubscribeAudioStreamingResultPush API 実行
  S->>-A: 200 OK
  note right of S: Subscribe しているCが 1 になった
  S->>+A: セッションウェブフック<br>audio-streaming.started
  A-->>-S: 200 OK
  note over S, Suzu: Suzu への HTTP/2 コネクション接続
  C->>S: SRTP
  S->>Suzu: Opus over HTTP/2
  C->>S: SRTP
  S->>Suzu: Opus over HTTP/2
  Suzu->>S: JSON over HTTP/2
  S->>C: シグナリングプッシュ通知
  C->>S: SRTP
  S->>Suzu: Opus over HTTP/2
      A-->>+S: UnsubscribeAudioStreamingResultPush API 実行
  S->>-A: 200 OK
  note right of S: Subscribe しているクライアントが 0 になった
  note over S, Suzu: Suzu への HTTP/2 コネクション切断
  C->>S: SRTP
```
