録画機能

概要

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

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

用語

単一録画ファイル

切断または StopRecording API の実行、または録画期限が切れた場合に一つのファイルとして出力される動画ファイル

分割録画ファイル

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

録画ファイルの出力

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

単一録画ファイルのみ出力

  • split_duration を指定していない

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

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

  • split_duration が指定されている

  • expire_time0 に指定されている

  • split_onlytrue に指定されている

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

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

  • split_duration が指定されている

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

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

制限

コーデック

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

警告

H.265 の録画には非対応です。

解像度

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

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

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

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

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

対応しています。

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

対応しています。

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

詳細はサイマルキャストの 映像の優先度 をご確認下さい。

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

対応しています。

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

詳細はサイマルキャストの 映像の優先度 をご確認下さい。

キーフレーム要求間隔

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

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

無変換録画

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

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

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

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

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

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

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

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

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

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

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

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

archive.started ウェブフック

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

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

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

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

詳しくは archive.available をご確認ください。

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

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

詳しくは split-archive.available をご確認ください。

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

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

詳しくは split-archive.end をご確認ください。

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

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

詳しくは archive.failed をご確認ください。

単一録画ファイル出力のみ

単一録画ファイル出力のみの場合は sora.confarchive_dir に指定したディレクトリに 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_dirarchive_tmp_dir は違うディレクトリを指定して下さい

archive-<connection_id>

単一録画ファイル

単一録画ファイルは <recording_id>/archvie-<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 は省略しています

{
  "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": {
    "bit_rate": 1000,
    "codec_type": "VP9",
    "height": 480,
    "width": 640
  }
}

注釈

sora.conflegacy_webhook_audio_video_json_structurefalse にしている場合は audio/video はフラット化します。詳細は ウェブフックの audio と video 項目の JSON 構造のフラット化 をご確認ください

色々省略していますが audio / video は以下のようになります。

{
  "audio": true,
  "audio_codec_type": "OPUS"
  "video": true,
  "video_bit_rate": 1000,
  "video_codec_type": "VP9",
  "video_height": 480,
  "video_width": 640
}

report-<recording_id>

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

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

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

  • トップレベルの start_timestamp

    • StartRecording API を受け付た時刻を RFC 3339 (UTC) で表しています

  • トップレベルの stop_timestamp

    • StopRecording API を受け付た時刻か、 StartRecording API で 設定された期限の時刻を RFC 3339 (UTC) で表しています

  • archives 内の start_time_offset

    • StartRecording API を叩いてから何秒経過した後にこの録画が開始したかを表しています

  • archives 内の stop_time_offset

    • StartRecording API を叩いてから何秒経過した後にこの録画が終了したかを表しています

{
  "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 API 実行時に split_durationsplit_only: trueexpire_time: 0 の 3 つを指定することで、 録画ファイルを指定した間隔で出力する機能を提供しています。

重要

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

録画が完了したファイルは sora.confarchive_dir に指定したディレクトリに recording_id 名のディレクトリ以下にファイルが出力されます。

recording_idStartRecording API を実行したときに戻ってくる値です。

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

録画開始 API 実行時に split_durationsplit_only: trueexpire_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-archvie-<connection_id>_<split_index>.webm に WebM 形式で出力されます。

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

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

  • split_index

    • ファイル名につくインデックスです

    • 0001 から始まり 9999 の後は 10000 となります

stats は省略しています

{
  "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": {
    "bit_rate": 1000,
    "codec_type": "VP9",
    "height": 480,
    "width": 640
  }
}

注釈

sora.conflegacy_webhook_audio_video_json_structurefalse にしている場合は audio/video はフラット化します。詳細は ウェブフックの audio と video 項目の JSON 構造のフラット化 をご確認ください

split-archive-end-<connection_id>

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

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

stats は省略しています

{
  "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": {
    "bit_rate": 1000,
    "codec_type": "VP9"
  }
}

注釈

sora.conflegacy_webhook_audio_video_json_structurefalse にしている場合は audio/video はフラット化します。詳細は ウェブフックの audio と video 項目の JSON 構造のフラット化 をご確認ください

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

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

詳細は split-archive.end をご確認ください。

report-<recording_id>

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

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

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

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

  • トップレベルの start_timestamp

    • StartRecording API を受け付た時刻を RFC 3339 (UTC) で表しています

  • トップレベルの stop_timestamp

    • StopRecording API を受け付た時刻か、 StartRecording API で 設定された期限の時刻を RFC 3339 (UTC) で表しています

  • archives 内の start_time_offset

    • StartRecording API を叩いてから何秒経過した後にこの録画が開始したかを表しています

  • archives 内の stop_time_offset

    • StartRecording API を叩いてから何秒経過した後にこの録画が終了したかを表しています

{
  "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 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 API を受け付た時刻を RFC 3339 (UTC) で表しています

  • トップレベルの stop_timestamp

    • StopRecording API を受け付た時刻か、 StartRecording API で 設定された期限の時刻を RFC 3339 (UTC) で表しています

  • archives 内の start_time_offset

    • StartRecording API を叩いてから何秒経過した後にこの録画が開始したかを表しています

  • archives 内の stop_time_offset

    • StartRecording API を叩いてから何秒経過した後にこの録画が終了したかを表しています

{
  "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 形式のためそのまま再生できます。

シグナリング通知

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

詳細は 録画のシグナリング通知 をご確認ください。

Lyra コーデックの録画ファイル

Lyra の WebM 構造

Sora はコーデックの変換を行わないので、 音声コーデックに Lyra が指定されている場合には、 録画ファイルには Lyra でエンコードされた音声データが格納されます。

WebM は標準では Lyra に対応していないため、 Sora では独自に次のような TrackEntry を Lyra 用に定義しています。

Lyra 用の TrackEntry

項目名

TrackNumber

2

TrackUID

2

FlagLacing

0

Language

"und"

FlagDefault

0

CodecID

"A_LYRA"

SeekPreRoll

80000000

TrackType

2

Audio

ChannelsSamplingFrequency を含んだ Audio 要素

CodecPrivate

Lyra のエンコード情報を含んだバイナリ文字列。詳細は後述。

Lyra 用の CodecPrivate バイナリの中身は次の通りです。

Lyra 用の CodecPrivate

項目名

バイト長

マジックシグネチャ

8

"LyraHead"

CodecPrivate 形式のバージョン

1

1

チャネル数

1

整数値

DTX

1

0=無効、1=有効

サンプルレート

4

リトルエンディアンの整数値

ビットレート

4

リトルエンディアンの整数値

Lyra のエンコードフォーマットバージョン

可変長(バイナリ文字列の終端まで)

文字列

なお TrackEntry およびその中の各項目の詳細については WebM の仕様 を参照してください。

Lyra の WebM 再生方法

通常の動画プレイヤーでは Lyra でエンコードされた音声を含んだ録画ファイルを再生することはできません。 ただし以下のように FFmpeg と Lyra のコマンドラインデコーダを組み合わせることで WebM から音声だけを取り出してデコードおよび再生が行えます。

# 1) FFmpeg を使って WebM ファイルから Lyra でエンコードされた音声データだけを取り出す
#
# [NOTE]
# 以下の FFmpeg コマンドは WebM 内の音声データを未加工のまま取り出して単純に連結し、
# タイムスタンプやエンコードパラメータの情報は出力ファイルには含まれていないことには注意。
#
# 例えば、DTX が有効になっているとWebM 内の音声データのタイムスタンプは飛び飛びになるが、
# 出力時にはその情報は欠落して連結される。
$ ffmpeg -y -i archive.webm -vn -acodec copy -f u8 archive.lyra

# 2) google/lyra リポジトリで提供されているコマンドラインデコーダを使って Lyra 音声ファイルをデコードする
# コマンドを実行すると ${入力ファイル名}_decoded.wav という名前の WAV ファイルが作成される
#
# [NOTE]
# - decoder_main コマンドのビルド方法は後述
# - --bitrate オプションの値はエンコード設定に合わせて指定する必要がある
$ decoder_main --encoded_path archive.lyra --output_dir . --bitrate 6000

# 3) 適当な動画プレイヤーで WAV ファイルを再生する
$ ffplay archive_decoded.wav

decoder_main コマンドのビルド方法は次の通りです。

# 1) リポジトリをダウンロード
$ git clone https://github.com/google/lyra.git
$ cd lyra

# 2) 今回は Android 関連のビルドは不要なので、その設定をコメントアウトする
# (Android Studio が既に手元でセットアップ済みの場合には、この手順はスキップ可能)
$ vim WORKSPACE
  # load("//:android_configure.bzl", "android_configure")
  # android_configure(name = "local_config_android")

  # load("@local_config_android//:android_configure.bzl", "android_workspace")
  # android_workspace()

# 3) ビルド
$ bazel build -c opt lyra/cli_example:decoder_main

# 4) ビルドに成功したら以下のパスにデコードコマンドが配置される
$ ls bazel-bin/lyra/cli_example/decoder_main

録画ファイル合成ツール

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

詳細は WebRTC 録画合成ツール Hisui をご確認ください。

試してみる

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

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

チャネルの録画開始

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

httpie:

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

curl:

$ curl -X POST example.com:3000/ \
    -H "x-sora-target: Sora_20161101.StartRecording" \
    -d '{"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

curl:

$ curl -X POST example.com:3000/ \
    -H "x-sora-target: Sora_20161101.StopRecording" \
    -d '{"channel_id": "sora"}' -vvv

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

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

重要

このツールはサポート対象外です

時雨堂では、録画関連ファイルを Amazon S3 、または S3 互換オブジェクトストレージにアップロードするツールを OSS として Apache License 2.0 で公開しています。

これは、Sora のクラウド版である Sora Cloud で利用している仕組みを 切り出したものです。

shiguredo/sora-archive-uploader: Sora Archive Uploader

シーケンス図

StopRecording API

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

録画期限切れ

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

クライアント切断

sequenceDiagram autonumber participant Client as クライアント participant Sora participant App as アプリケーションサーバー note over Client,App: 認証成功 Sora->>+Client: "type": "offer" Client->>-Sora: "type": "answer" note over Client,App: WebRTC 確立 App->>+Sora: HTTP API StartRecording Sora-->>-App: 200 OK Sora->>+App: イベントウェブフック<br/>"type": "recording.started" App-->>-Sora: 200 OK note over Client,App: 録画中 Sora->>+App: イベントウェブフック<br/>"type": "archive.started" App-->>-Sora: 200 OK Client->>Sora: "type": "disconnect" Sora->>+App: イベントウェブフック<br/>"type" :"connection.destroyed" App-->-Sora: 200 OK Sora->>Client: WebSocekt Close note over Client,App: クライアント切断により録画終了 Sora->>+App: イベントウェブフック<br/>"type": "archive.available" App-->>-Sora: 200 OK Sora->>+App: イベントウェブフック<br/>"type": "recording.report" App-->>-Sora: 200 OK
© Copyright 2023, Shiguredo Inc Created using Sphinx 5.3.0