# レガシー録画機能

*バージョン 2025.2.0 で削除。*

> **注意**
>
> レガシー録画機能は 2025 年 12 月リリースの Sora にて廃止しました。
> 今後は [録画機能 (セッション単位)](RECORDING.html) をご利用ください。

> **注意**
>
> レガシー録画機能は MP4 形式でのファイル出力に対応していません。
> MP4 形式でのファイル出力を利用する場合は [録画機能 (セッション単位)](RECORDING.html) をご利用ください。

> **重要**
>
> レガシー録画機能と録画機能 (セッション単位) はチャネル単位で異なる仕組みを同時に利用できます。
> 例えば、channel_id a ではレガシー録画機能、channel_id b では録画機能（セッション単位）のように利用することができます。

## 概要

Sora では、配信している映像や音声を録画、録音して保存できます。
配信されている映像をできるかぎりそのまま保存するため、CPU リソースを最小限に抑えることができます。
出力される録画ファイルは WebM 形式です。映像のみの録画、音声のみの録音にも対応しています。

録画時には一切トランスコードを行っていません。配信された映像や音声をそのままに記録します。

## 用語

**一括録画ファイル**
: 切断または [StopRecording](OBSOLETE_API_LEGACY_RECORDING.html#fd0de5) API の実行、または録画期限が切れた場合に一つのファイルとして出力される動画ファイル

**分割録画ファイル**
: `split_duration` で指定した時間ごとに区切られ分割されて出力される動画ファイル

## 録画ファイルの出力

録画ファイルの出力には 3 パターンあります。

### 一括録画ファイルのみ出力

- `split_duration` を指定していない

上記を満たした場合、1 接続で 1 つ録画ファイルが出力されます。

### 分割録画ファイルのみ出力

- `split_duration` が指定されている
- `expire_time` が `0` に指定されている
- `split_only` が `true` に指定されている

上記を満たした場合、分割された録画ファイルのみが出力されます。

### 一括録画ファイルと分割録画ファイルの両方が出力

- `sora.conf` で `recording_dual_output` が `true` に指定されている
- `split_duration` が指定されている

上記を満たした場合、 1 接続で 1 つの録画ファイルと分割された録画ファイルの両方が出力されます。

イベントウェブフックも一括録画、分割録画、両方のイベントウェブフックリクエストが送信されます。

## 制限

### コーデック

録画 (録音) は、映像コーデックに VP8 と VP9 と H.264 と AV1 、また音声コーデックに Opus を選択した場合のみ利用できます。

> **警告**
>
> - H.265 の録画には非対応です
> - B フレームの録画には非対応です

### 解像度

WebRTC では配信側の CPU リソースが不足した場合や、
回線の品質が悪化した場合に解像度を動的に変更します。
そのため録画したデータの途中で解像度が低くなる可能性があります。

### 音声や映像のクライアント側でのトラック削除

クライアント側でシグナリング接続時に音声や映像を有効にした状態で、
クライアント側で音声トラック、または映像トラックのどちらかを削除した場合でも録画は行われます。
さらに追加して戻した形であれば録画側も戻ります。

ただし、音声と映像両方のトラックを削除した場合は正常に録画が行われません。

### マルチストリーム機能での録画

対応しています。

### サイマルキャスト機能での録画

対応しています。

サイマルキャストを利用している際の録画は **一番優先度が低い** ストリーム、すなわちデフォルトでは最も高い画質の映像を録画します。

詳細はサイマルキャストの [映像の優先度](SIMULCAST.html#36c708) をご確認ください。

### スポットライト機能での録画

対応しています。

サイマルキャストを利用している際の録画は **一番優先度が低い** ストリーム、すなわちデフォルトでは最も高い画質の映像を録画します。

詳細はサイマルキャストの [映像の優先度](SIMULCAST.html#36c708) をご確認ください。

### キーフレーム要求間隔

録画機能利用時の Sora からのキーフレーム要求間隔は 20 秒に固定されています。
そのため、分割録画の最小時間は 20 秒 + `split_duration` に指定した秒数となります。

もし録画時のキーフレーム要求間隔を変更したい場合は、サポートまでご連絡ください。

## 無変換録画

WebRTC 経由で流れてきている映像や音声を変換せず、
そのまま録画するファイルの形式に組み立て直してファイルを保存します。
そのため、CPU リソースを最小限に抑えられます。
ブラウザでの録画など、通常の録画は変換が入るため CPU に多くの負荷がかかります。

変換を行わないため、録画を終了した数秒後には録画したファイルを取得できます。

解像度は送られてきた映像の最大値を録画ファイルの解像度として使用します。

## 録画の開始と終了について

Sora の録画機能は **明示的にチャネルの録画を停止するか、
チャネルの録画開始から指定した期限が過ぎるまでは、
そのチャネルでの配信を自動で録画する** といった機能になります。

## sora.conf の設定による録画指定制限について

録画ファイルが大きくならないように、sora.conf の指定で録画開始時のオプションを制限することができます。

設定の詳細は以下をご確認ください。

- [recording_max_expire_time](SORA_CONF.html#f3ff8d)
- [recording_max_split_duration](SORA_CONF.html#927da9)
- [recording_expire_time_required](SORA_CONF.html#fcfa2d)
- [recording_dual_output](SORA_CONF.html#4cc9c0)

## 録画関連イベントのウェブフックについて

### recording.started イベントウェブフック

録画開始 API が実行されたタイミングで `recording.started` リクエストを送信します。

詳しくは `recording.started` をご確認ください。

### recording.report イベントウェブフック

録画終了 API が実行されたか、
録画の期限が切れたタイミングで `recording.report` リクエストを送信します。

詳しくは `recording.report` をご確認ください。

### archive.started ウェブフック

録画ファイルを保存しはじめたタイミングで `archive.started` リクエストを送信します。

詳しくは [archive.started](EVENT_WEBHOOK.html#462c97) をご確認ください。

### archive.available イベントウェブフック

一括録画ファイルが出力されたタイミングで `archive.available` リクエストを送信します。

詳しくは [archive.available](EVENT_WEBHOOK.html#de9132) をご確認ください。

### split-archive.available イベントウェブフック

録画ファイル分割出力機能を有効にした場合、
分割された録画ファイルが出力されたタイミングで `split-archive.available` リクエストを送信します。

詳しくは [split-archive.available](EVENT_WEBHOOK.html#555071) をご確認ください。

### split-archive.end イベントウェブフック

録画ファイル分割出力機能を有効にした場合、
分割された録画が終了したタイミングで `split-archive.end` リクエストを送信します。

詳しくは [split-archive.end](EVENT_WEBHOOK.html#31be7a) をご確認ください。

### archive.failed イベントウェブフック

録画ファイルの保存に失敗した場合、 `archive.failed` リクエストを送信します。

詳しくは [archive.failed](EVENT_WEBHOOK.html#5006f8) をご確認ください。

## 一括録画ファイル出力のみ

一括録画ファイル出力のみの場合は `sora.conf` の [archive_dir](SORA_CONF.html#ad2156) に指定したディレクトリに `recording_id` 名のディレクトリ以下にファイルが出力されます。

`recording_id` は録画開始 API を実行したときに戻ってくる値で、 Base32 でエンコードされた UUIDv4 となります。

ディレクトリ構造:

```
├── archive
│   ├── 1CS9QJ0XPN4C76HBGBN6MGMK5M
│   │   ├── archive-A4756MXP914ZB265E92JE3ZMWC.json
│   │   ├── archive-A4756MXP914ZB265E92JE3ZMWC.webm
│   │   ├── archive-H2NDA2YCGH7S1E9CVMFMXMA34R.json
│   │   ├── archive-H2NDA2YCGH7S1E9CVMFMXMA34R.webm
│   │   ├── archive-PBVZQQN3JS3MQF8XHVFXDMCEEC.json
│   │   ├── archive-PBVZQQN3JS3MQF8XHVFXDMCEEC.webm
│   │   └── report-1CS9QJ0XPN4C76HBGBN6MGMK5M.json
│   └── CZZ8A8KZB16A1DF5PKERBHGFNR
│       ├── archive-3B7AFF8ZRX6VNEYV40B35Z9S2C.json
│       ├── archive-3B7AFF8ZRX6VNEYV40B35Z9S2C.webm
│       ├── archive-DGSN3TC0E91RSCZT5KVPRWCDHR.json
│       ├── archive-DGSN3TC0E91RSCZT5KVPRWCDHR.webm
│       └── report-CZZ8A8KZB16A1DF5PKERBHGFNR.json
```

> **注意**
>
> `archive_dir` と `archive_tmp_dir` は違うディレクトリを指定してください

### archive-<connection_id>

#### 一括録画ファイル

一括録画ファイルは `<recording_id>/archive-<connection_id>.webm` に WebM 形式で出力されます。

#### 一括録画メタデータファイル

一括録画メタデータファイルは `<recording_id>/archive-<connection_id>.json` に JSON 形式で出力されます。

メタデータファイルには WebM ファイルがいつ出力され、どんな形式なのか、開始時刻や終了時刻などの情報が含まれています。

- start_timestamp- この録画が開始された時刻を RFC 3339 (UTC) で表しています
- stop_timestamp- この録画が終了した時刻を RFC 3339 (UTC) で表しています
- start_time- この録画が開始された時刻を UNIX 時間で表しています
- stop_time- この録画が終了した時刻を UNIX 時間で表しています

**stats は省略しています**

```javascript
{
  "audio": true,
  "audio_codec_type": "OPUS",
  "channel_id": "sora",
  "session_id": "JA8FB89ZJS1H9CV5GN3NCT5RA0",
  "client_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",
  "bundle_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",
  "connection_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",
  "created_at": 1615524156,
  "file_path": "/path/to/sora/archive/WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.webm",
  "filename": "WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.webm",
  "metadata_file_path": "/path/to/sora/archive/WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.json",
  "metadata_filename": "WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.json",
  "recording_id": "WHEJ888HQ55KDCFE3TZ4VPFQHR",
  "size": 0,
  "start_time": 1615524137,
  "start_time_offset": 7,
  "start_timestamp": "2021-03-12T04:42:17.455668Z",
  "stats": {},
  "stop_time": 1615524154,
  "stop_time_offset": 24,
  "stop_timestamp": "2021-03-12T04:42:34.094375Z",
  "label": "WebRTC SFU Sora",
  "node_name": "node1@192.0.2.10",
  "event_metadata": {"spam": "egg"},
  "video": true,
  "video_bit_rate": 1000,
  "video_codec_type": "VP9",
  "video_vp9_params": {
    "profile_id": 0
  },
  "video_height": 480,
  "video_width": 640,
  "format": "mp4"
}
```

### report-<recording_id>

録画終了時に、それまでにそのチャネルで録画したファイル一覧が記載されているレポートファイルが JSON 形式で出力されます。
録画終了は [StopRecording](OBSOLETE_API_LEGACY_RECORDING.html#fd0de5) API を使用して指定したチャネルに対する録画を停止するか、
録画の期限が切れた場合のふたつのパターンがあります。

このファイルは主にマルチストリームや途中で切れてしまった場合などを考慮しており、
録画ファイルのグルーピングを目的としたファイルです。

ファイルは `<recording_id>/report-<recording_id>.json` に出力されます。

- トップレベルの start_timestamp- [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API を受け付けた時刻を RFC 3339 (UTC) で表しています
- トップレベルの stop_timestamp- [StopRecording](OBSOLETE_API_LEGACY_RECORDING.html#fd0de5) API を受け付けた時刻か、 [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API で設定された期限の時刻を RFC 3339 (UTC) で表しています
- archives 内の start_time_offset- [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API を叩いてから何秒経過した後にこの録画が開始したかを表しています
- archives 内の stop_time_offset- [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API を叩いてから何秒経過した後にこの録画が終了したかを表しています

```javascript
{
  "archives": [
    {
      "client_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",
      "bundle_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",
      "connection_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",
      "file_path": "/path/to/sora/archive/WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.webm",
      "filename": "WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.webm",
      "metadata_file_path": "/path/to/sora/archive/WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.json",
      "metadata_filename": "WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.json",
      "size": 0,
      "start_time_offset": 0,
      "start_timestamp": "2021-03-12T04:42:17.455668Z",
      "stop_time_offset": 17,
      "stop_timestamp": "2021-03-12T04:42:34.094375Z",
      "label": "WebRTC SFU Sora",
      "node_name": "node1@192.0.2.10"
    }
  ],
  "channel_id": "sora",
  "created_at": 1615524137,
  "expire_time": 3600,
  "expired_at": 1615527737,
  "file_path": "/path/to/sora/archive/WHEJ888HQ55KDCFE3TZ4VPFQHR/report-WHEJ888HQ55KDCFE3TZ4VPFQHR.json",
  "filename": "WHEJ888HQ55KDCFE3TZ4VPFQHR/report-WHEJ888HQ55KDCFE3TZ4VPFQHR.json",
  "metadata": {"spam": "egg"},
  "recording_id": "WHEJ888HQ55KDCFE3TZ4VPFQHR",
  "split_only": false,
  "start_timestamp": "2021-03-12T04:42:17.455668Z",
  "stop_timestamp": "2021-03-12T04:42:34.094375Z"
}
```

## 分割録画ファイル出力のみ

Sora では [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API 実行時に `split_duration` と `split_only: true` と `expire_time: 0` の 3 つを指定することで、
録画ファイルを指定した間隔で出力する機能を提供しています。

> **重要**
>
> 分割の最小単位はキーフレームから次のキーフレームまでです。例えば `split_duration` を 1 秒に設定した場合は、1 秒経過後に次のキーフレームが来たタイミングで分割出力されます。

録画が完了したファイルは `sora.conf` の [archive_dir](SORA_CONF.html#ad2156) に指定したディレクトリに `recording_id` 名のディレクトリ以下にファイルが出力されます。

`recording_id` は [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API を実行したときに戻ってくる値です。

### 録画ファイル分割出力のみを行う

録画開始 API 実行時に `split_duration` と `split_only: true` と `expire_time: 0` の 3 つを指定することで、
録画ファイル分割出力 **のみ** を行うことが可能になります。

ディレクトリ構造:

```
├── archive
│   ├── 1CS9QJ0XPN4C76HBGBN6MGMK5M
│   │   ├── split-archive-end-A4756MXP914ZB265E92JE3ZMWC.json
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0001.json
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0001.webm
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0002.json
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0002.webm
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0003.json
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0003.webm
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0003.json
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0003.webm
│   │   └── report-1CS9QJ0XPN4C76HBGBN6MGMK5M.json
│   └── CZZ8A8KZB16A1DF5PKERBHGFNR
│       ├── split-archive-end-3B7AFF8ZRX6VNEYV40B35Z9S2C.json
│       ├── split-archive-end-DGSN3TC0E91RSCZT5KVPRWCDHR.json
│       ├── split-archive-3B7AFF8ZRX6VNEYV40B35Z9S2C_0001.json
│       ├── split-archive-3B7AFF8ZRX6VNEYV40B35Z9S2C_0001.webm
│       ├── split-archive-DGSN3TC0E91RSCZT5KVPRWCDHR_0001.json
│       ├── split-archive-DGSN3TC0E91RSCZT5KVPRWCDHR_0001.webm
│       ├── split-archive-DGSN3TC0E91RSCZT5KVPRWCDHR_0002.json
│       ├── split-archive-DGSN3TC0E91RSCZT5KVPRWCDHR_0002.webm
│       └── report-CZZ8A8KZB16A1DF5PKERBHGFNR.json
```

### split-archive-<connection_id>_<split_index>

#### 分割録画ファイル

分割録画ファイルは `<recording_id>/split-archive-<connection_id>_<split_index>.webm` に WebM 形式で出力されます。

#### 分割録画メタデータファイル

分割録画メタデータファイルは `<recording_id>/split-archive-<connection_id>_<split_index>.json` に JSON 形式で出力されます。

- split_index- ファイル名につくインデックスです
  - 0001 から始まり 9999 の後は 10000 となります

**stats は省略しています**

```javascript
{
  "audio": true,
  "audio_codec_type": "OPUS",
  "recording_id": "CZZ8A8KZB16A1DF5PKERBHGFNR",
  "file_path": "/path/to/sora/archive/CZZ8A8KZB16A1DF5PKERBHGFNR/split-archive-3B7AFF8ZRX6VNEYV40B35Z9S2C_001.webm",
  "filename": "CZZ8A8KZB16A1DF5PKERBHGFNR/split-archive-3B7AFF8ZRX6VNEYV40B35Z9S2C_001.webm",
  "metadata_file_path": "/path/to/sora/split-archive/CZZ8A8KZB16A1DF5PKERBHGFNR/archive-3B7AFF8ZRX6VNEYV40B35Z9S2C_001.json",
  "metadata_filename": "CZZ8A8KZB16A1DF5PKERBHGFNR/split-archive-3B7AFF8ZRX6VNEYV40B35Z9S2C_001.json",
  "channel_id": "sora",
  "session_id": "JA8FB89ZJS1H9CV5GN3NCT5RA0",
  "client_id": "3B7AFF8ZRX6VNEYV40B35Z9S2C",
  "bundle_id": "3B7AFF8ZRX6VNEYV40B35Z9S2C",
  "connection_id": "3B7AFF8ZRX6VNEYV40B35Z9S2C",
  "split_index": "0001",
  "created_at": 1604656364,
  "size": 823263,
  "start_time": 1604656354,
  "start_time_offset": 4,
  "start_timestamp": "2020-11-06T09:52:34.696758Z",
  "stats": {},
  "stop_time": 1604656364,
  "stop_time_offset": 14,
  "stop_timestamp": "2020-11-06T09:52:44.493179Z",
  "label": "WebRTC SFU Sora",
  "node_name": "node1@192.0.2.10",
  "event_metadata": {"spam": "egg"},
  "video": true,
  "video_bit_rate": 1000,
  "video_codec_type": "VP9",
  "video_vp9_params": {
    "profile_id": 0
  },
  "video_height": 480,
  "video_width": 640,
  "format": "mp4"
}
```

### split-archive-end-<connection_id>

#### 分割録画終了メタデータファイル

分割録画終了メタデータファイルは `<recording_id>/split-archive-end-<connection_id>.json` に JSON 形式で出力されます。

**stats は省略しています**

```javascript
{
  "audio": true,
  "audio_codec_type": "OPUS",
  "split_last_index": "0042",
  "recording_id": "CZZ8A8KZB16A1DF5PKERBHGFNR",
  "file_path": "/path/to/sora/archive/CZZ8A8KZB16A1DF5PKERBHGFNR/split-archive-end-3B7AFF8ZRX6VNEYV40B35Z9S2C.json",
  "filename": "CZZ8A8KZB16A1DF5PKERBHGFNR/split-archive-end-3B7AFF8ZRX6VNEYV40B35Z9S2C.json",
  "channel_id": "sora",
  "session_id": "JA8FB89ZJS1H9CV5GN3NCT5RA0",
  "client_id": "3B7AFF8ZRX6VNEYV40B35Z9S2C",
  "bundle_id": "3B7AFF8ZRX6VNEYV40B35Z9S2C",
  "connection_id": "3B7AFF8ZRX6VNEYV40B35Z9S2C",
  "start_time": 1604656354,
  "start_time_offset": 4,
  "start_timestamp": "2020-11-06T09:52:34.696758Z",
  "stats": {},
  "stop_time": 1604656364,
  "stop_time_offset": 14,
  "stop_timestamp": "2020-11-06T09:52:44.493179Z",
  "label": "WebRTC SFU Sora",
  "node_name": "node1@192.0.2.10",
  "event_metadata": {"spam": "egg"},
  "video": true,
  "video_bit_rate": 1000,
  "video_codec_type": "VP9",
  "video_vp9_params": {
    "profile_id": 0
  },
  "video_height": 480,
  "video_width": 640,
  "format": "mp4"
}
```

#### 録画ファイル分割出力終了時

接続単位での録画が終了したタイミングでイベントウェブフックリクエスト `split-archive.end` がリクエスト送信されます。

詳細は [split-archive.end](EVENT_WEBHOOK.html#31be7a) をご確認ください。

### report-<recording_id>

録画終了時に、それまでにそのチャネルで録画したファイル一覧が記載されているレポートファイルが JSON 形式で出力されます。
録画終了は [StopRecording](OBSOLETE_API_LEGACY_RECORDING.html#fd0de5) API を使用して指定したチャネルに対する録画を停止するか、
録画の期限が切れた場合のふたつのパターンがあります。

このファイルは主にマルチストリームや途中で切れてしまった場合などを考慮しており、
録画ファイルのグルーピングを目的としたファイルです。

ファイルは `<recording_id>/report-<recording_id>.json` に出力されます。

分割録画ファイル出力のみの場合は archives には `connection_id` や `client_id` といった接続情報と、
分割録画ファイルの最後のインデックス番号のみが含まれます。

- トップレベルの start_timestamp- [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API を受け付けた時刻を RFC 3339 (UTC) で表しています
- トップレベルの stop_timestamp- [StopRecording](OBSOLETE_API_LEGACY_RECORDING.html#fd0de5) API を受け付けた時刻か、 [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API で設定された期限の時刻を RFC 3339 (UTC) で表しています
- archives 内の start_time_offset- [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API を叩いてから何秒経過した後にこの録画が開始したかを表しています
- archives 内の stop_time_offset- [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API を叩いてから何秒経過した後にこの録画が終了したかを表しています

```javascript
{
  "archives": [
    {
      "client_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",
      "bundle_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",
      "connection_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",
      "split_last_index": "0042",
      "start_time_offset": 4,
      "start_timestamp": "2020-11-06T09:52:34.696758Z",
      "stop_time_offset": 14,
      "stop_timestamp": "2020-11-06T09:52:44.493179Z",
      "label": "WebRTC SFU Sora",
      "node_name": "node1@192.0.2.10"
    }
  ],
  "channel_id": "sora",
  "created_at": 1615524137,
  "expire_time": 3600,
  "expired_at": 1615527737,
  "file_path": "/path/to/sora/archive/WHEJ888HQ55KDCFE3TZ4VPFQHR/report-GK2R6PSDYX68VDQPRX4FVVFN8W.json",
  "filename": "WHEJ888HQ55KDCFE3TZ4VPFQHR/report-GK2R6PSDYX68VDQPRX4FVVFN8W.json",
  "recording_id": "WHEJ888HQ55KDCFE3TZ4VPFQHR",
  "split_duration": 3600,
  "split_only": false,
  "metadata": {"spam": "egg"},
  "start_timestamp": "2020-11-06T09:52:34.696758Z",
  "stop_timestamp": "2020-11-06T09:52:44.493179Z"
}
```

## 一括録画ファイルと分割録画ファイル

[StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API 実行時に `split_only` を有効にしない限り、
単一ファイルと分割ファイルの二つが出力されます。
一括録画ファイルと分割録画ファイルのいいところ取りですが、その分ストレージの容量も 2 倍消費します。

出力されるファイルは単一と分割のファイルが混ざった形式になります。

ディレクトリ構造:

```
├── archive
│   ├── 1CS9QJ0XPN4C76HBGBN6MGMK5M
│   │   ├── split-archive-end-A4756MXP914ZB265E92JE3ZMWC.json
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0001.json
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0001.webm
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0002.json
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0002.webm
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0003.json
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0003.webm
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0003.json
│   │   ├── split-archive-A4756MXP914ZB265E92JE3ZMWC_0003.webm
│   │   ├── archive-A4756MXP914ZB265E92JE3ZMWC.json
│   │   ├── archive-A4756MXP914ZB265E92JE3ZMWC.webm
│   │   └── report-1CS9QJ0XPN4C76HBGBN6MGMK5M.json
│   └── CZZ8A8KZB16A1DF5PKERBHGFNR
│       ├── split-archive-end-3B7AFF8ZRX6VNEYV40B35Z9S2C.json
│       ├── split-archive-3B7AFF8ZRX6VNEYV40B35Z9S2C_0001.json
│       ├── split-archive-3B7AFF8ZRX6VNEYV40B35Z9S2C_0001.webm
│       ├── split-archive-end-DGSN3TC0E91RSCZT5KVPRWCDHR.json
│       ├── split-archive-DGSN3TC0E91RSCZT5KVPRWCDHR_0001.json
│       ├── split-archive-DGSN3TC0E91RSCZT5KVPRWCDHR_0001.webm
│       ├── split-archive-DGSN3TC0E91RSCZT5KVPRWCDHR_0002.json
│       ├── split-archive-DGSN3TC0E91RSCZT5KVPRWCDHR_0002.webm
│       ├── archive-3B7AFF8ZRX6VNEYV40B35Z9S2C.json
│       ├── archive-3B7AFF8ZRX6VNEYV40B35Z9S2C.webm
│       ├── archive-DGSN3TC0E91RSCZT5KVPRWCDHR.json
│       ├── archive-DGSN3TC0E91RSCZT5KVPRWCDHR.webm
│       └── report-CZZ8A8KZB16A1DF5PKERBHGFNR.json
```

### report-<recording_id>

出力される report ファイルは単一と分割が混ざった形式になります。

- トップレベルの start_timestamp- [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API を受け付けた時刻を RFC 3339 (UTC) で表しています
- トップレベルの stop_timestamp- [StopRecording](OBSOLETE_API_LEGACY_RECORDING.html#fd0de5) API を受け付けた時刻か、 [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API で設定された期限の時刻を RFC 3339 (UTC) で表しています
- archives 内の start_time_offset- [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API を叩いてから何秒経過した後にこの録画が開始したかを表しています
- archives 内の stop_time_offset- [StartRecording](OBSOLETE_API_LEGACY_RECORDING.html#c5b527) API を叩いてから何秒経過した後にこの録画が終了したかを表しています

```javascript
{
  "archives": [
    {
      "client_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",
      "bundle_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",
      "connection_id": "GK2R6PSDYX68VDQPRX4FVVFN8W",

      "file_path": "/path/to/sora/archive/WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.webm",
      "filename": "WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.webm",
      "metadata_file_path": "/path/to/sora/archive/WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.json",
      "metadata_filename": "WHEJ888HQ55KDCFE3TZ4VPFQHR/archive-GK2R6PSDYX68VDQPRX4FVVFN8W.json",
      "size": 0,
      "start_time_offset": 0,
      "start_timestamp": "2021-03-12T04:42:17.455668Z",
      "stop_time_offset": 17,
      "stop_timestamp": "2021-03-12T04:42:34.094375Z",

      "split_last_index": "0042",
      "label": "WebRTC SFU Sora",
      "node_name": "node1@192.0.2.10"
    }
  ],
  "channel_id": "sora",
  "created_at": 1615524137,
  "expire_time": 3600,
  "expired_at": 1615527737,
  "file_path": "/path/to/sora/archive/WHEJ888HQ55KDCFE3TZ4VPFQHR/report-WHEJ888HQ55KDCFE3TZ4VPFQHR.json",
  "filename": "WHEJ888HQ55KDCFE3TZ4VPFQHR/report-WHEJ888HQ55KDCFE3TZ4VPFQHR.json",
  "recording_id": "WHEJ888HQ55KDCFE3TZ4VPFQHR",
  "label": "WebRTC SFU Sora",
  "node_name": "node1@192.0.2.10",
  "split_duration": 3600,
  "split_only": false,
  "metadata": {"spam": "egg"},
  "start_timestamp": "2021-03-12T04:42:17.455668Z",
  "stop_timestamp": "2021-03-12T04:42:34.094375Z"
}
```

## 録画ファイル出力失敗時の録画一時ファイル

何らかの理由で録画ファイル出力が失敗した場合、 `archive_tmp_dir` で指定したディレクトリに録画一時ファイルが削除されずに残ります。そのため、定期的な削除が必要です。

この録画一時ファイルは WebM 形式のためそのまま再生できます。

## シグナリング通知

シグナリング通知で録画開始と終了を通知できます。

詳細は [録画のシグナリング通知](SIGNALING_NOTIFY.html#7ce275) をご確認ください。

## 録画ファイル合成ツール

マルチストリームを録画した場合はそれぞれの接続に対して録画ファイルが出力されます。このそれぞれ分かれた録画ファイルを合成して一つにするツールをオープンソースとして公開しています。

詳細は [WebRTC 録画合成ツール Hisui](HISUI.html) をご確認ください。

## 試してみる

Sora では録画機能を試すための開発ツールを提供しています。 [開発ツール](DEVTOOLS.html) を参照の上、開発ツールを有効にしてください。

ここでは Sora が立っているサーバーは example.com としています。

### チャネルの録画開始

API を叩いて録画を開始してください。

httpie:

```
$ http POST example.com:3000/ \
    x-sora-target:Sora_20161101.StartRecording \
    channel_id=sora \
    expire_time:=3600 -vvv
```

その後 `https://example.com/sendonly.html` を開き、 connect ボタンを押して配信を開始します。

切断またはチャネルの録画終了、もしくはチャネルの録画期限が来たタイミングでクライアントの録画は終了します。

### チャネルの録画終了

チャネルの録画を終了するには API を叩く必要があります。

httpie:

```
$ http POST example.com:3000/ \
    x-sora-target:Sora_20161101.StopRecording \
    channel_id=sora -vvv
```

その後 archive/ ディレクトリに webm 形式のファイルが出力されます。
Chrome または Firefox にドラッグアンドドロップして、動作を確認してください。

## 録画関連ファイルアップローダー

> **重要**
>
> このツールはサポート対象外です

時雨堂では、録画関連ファイルを [Amazon S3](https://aws.amazon.com/jp/s3/) 、または S3
互換オブジェクトストレージにアップロードするツールを OSS として Apache License 2.0 で公開しています。

これは、Sora のクラウド版である [Sora Cloud](https://sora-cloud.shiguredo.jp) で利用している仕組みを
切り出したものです。

[shiguredo/sora-archive-uploader: Sora Archive Uploader](https://github.com/shiguredo/sora-archive-uploader)

## シーケンス図

### StopRecording API

```mermaid
sequenceDiagram
    autonumber
    participant C as クライアント
    participant S as Sora
    participant A as アプリケーションサーバー
    note over C,A: 認証成功
    S->>+C: "type": "offer"
    C->>-S: "type": "answer"
    note over C,A: WebRTC 確立
    A->>+S: HTTP API StartRecording
    S-->>-A: 200 OK
    S->>+A: イベントウェブフック<br/>"type": "recording.started"
    A-->>-S: 200 OK
    note over C,A: 録画中
    S->>+A: イベントウェブフック<br/>"type": "archive.started"
    A-->>-S: 200 OK
    A->>+S: HTTP API StopRecording
    S-->>-A: 200 OK
    note over C,A: 録画終了
    S->>+A: イベントウェブフック<br/>"type": "archive.available"
    A-->>-S: 200 OK
    S->>+A: イベントウェブフック<br/>"type": "recording.report"
    A-->>-S: 200 OK
```

### 録画期限切れ

```mermaid
sequenceDiagram
    autonumber
    participant C as クライアント
    participant S as Sora
    participant A as アプリケーションサーバー
    note over C,A: 認証成功
    S->>+C: "type": "offer"
    C->>-S: "type": "answer"
    note over C,A: WebRTC 確立
    A->>+S: HTTP API StartRecording
    S-->>-A: 200 OK
    S->>+A: イベントウェブフック<br/>"type": "recording.started"
    A-->>-S: 200 OK
    note over C,A: 録画中
    S->>+A: イベントウェブフック<br/>"type": "archive.started"
    A-->>-S: 200 OK
    note over C,A: 期限切れにより録画終了
    S->>+A: イベントウェブフック<br/>"type": "archive.available"
    A-->>-S: 200 OK
    S->>+A: イベントウェブフック<br/>"type": "recording.report"
    A-->>-S: 200 OK
```

### クライアント切断

```mermaid
sequenceDiagram
    autonumber
    participant C as クライアント
    participant S as Sora
    participant A as アプリケーションサーバー
    note over C,A: 認証成功
    S->>+C: "type": "offer"
    C->>-S: "type": "answer"
    note over C,A: WebRTC 確立
    A->>+S: HTTP API StartRecording
    S-->>-A: 200 OK
    S->>+A: イベントウェブフック<br/>"type": "recording.started"
    A-->>-S: 200 OK
    note over C,A: 録画中
    S->>+A: イベントウェブフック<br/>"type": "archive.started"
    A-->>-S: 200 OK
    C->>S: "type": "disconnect"
    S->>+A: イベントウェブフック<br/>"type" :"connection.destroyed"
    A-->-S: 200 OK
    S->>C: WebSocket Close
    note over C,A: クライアント切断により録画終了
    S->>+A: イベントウェブフック<br/>"type": "archive.available"
    A-->>-S: 200 OK
    S->>+A: イベントウェブフック<br/>"type": "recording.report"
    A-->>-S: 200 OK
```
