クイックスタート

本チュートリアルでは、録音・録画機能を利用するための基本的な手順を説明します。

クラウドストレージの準備

録音・録画機能を使用するためには Google Cloud Storage、Amazon S3、Wasabi のいずれかのクラウドストレージの設定が必要となります。ここではクラウドストレージ別に設定方法を説明します。

Google Cloud Storage を利用する場合

ロールの作成

サービスアカウントに付与する最小権限のロールを作成します。

ロールの管理画面を開きます。

ロールの作成ボタンをクリックします。

ロールの管理画面で「ロールの作成」をクリックする

ロールの名前、説明を設定し、権限を追加ボタンをクリックします。

ロールの作成画面で権限を追加ボタンをクリックする

フィルタと書かれた検索欄に storage.objects. と入力し、検索結果から以下の権限を追加します。

  • storage.objects.create
  • storage.objects.delete
  • storage.objects.get
  • storage.objects.list

権限の追加画面で必要な権限を検索して追加する

作成ボタンをクリックします。

ロールの作成画面で作成ボタンをクリックする

サービスアカウントの作成

サービスアカウントの管理画面を開きます。

サービスアカウントを作成ボタンをクリックします。

サービスアカウントの管理画面で「サービスアカウントを作成」をクリックする

サービスアカウント名、ID を入力し、作成して続行ボタンをクリックします。なお、作成されたメールアドレスは後で利用します。

サービスアカウントの作成画面で、作成して続行ボタンをクリックする

ロールを選択メニューから先ほど作成したロールを選択します。

ロールの選択画面で、作成したロールを選択する

完了ボタンをクリックし、サービスアカウントを作成します。

サービスアカウントの作成画面で、完了ボタンをクリックする

検索欄にサービスアカウント名を入力し、検索結果のリンクをクリックします。

サービスアカウントの一覧画面で作成したサービスアカウントを検索し、作成したサービスアカウントを選択する

キーのタブをクリックしてキーの管理画面を開き、鍵を追加メニューを開きます。新しい鍵を作成ボタンをクリックします。

「キー」のタブで「鍵を追加」をクリックし、「新しい鍵を作成」を選択する

JSON を指定して作成ボタンをクリックします。JSON ファイルがダウンロードされます。

キーのタイプでJSONを選択して、作成ボタンをクリックする

バケットの作成

Google Cloud Storage の管理画面を開きます。

作成ボタンをクリックします。

Google Cloud Storage の管理画面で、作成ボタンをクリックする

バケット名、リージョンなどの設定を行い作成ボタンをクリックします。ここで設定したバケット名は後で利用するため、保存してください。

バケットの作成画面で、作成ボタンをクリックする

これで操作は完了です。

以下のデータを保存できていることを確認してください。

  • バケット名
  • サービスアカウントの鍵の JSON ファイル

Amazon S3 を利用する場合

S3 バケットの作成

Amazon S3 の管理画面 を開きます。

Create bucket をクリックして作成画面を開きます。

Amazon S3 の管理画面で、Create bucket をクリックする

AWS Region より任意のリージョンを選択し、Bucket name に任意の名前を入力します。

なお、ここで設定したリージョン名(例. ap-northeast-1)、バケット名は後で利用するため、保存してください。

バケットの作成画面で、リージョンとバケット名を入力する

最下部の Create bucket をクリックし、S3 バケットを作成します。

バケットの作成画面で、Create bucket をクリックする

IAMの設定

IAM の管理画面を開き、メニューよりUsersの管理画面を開きます。

IAM の管理画面で、Users の管理画面を開く

Create user ボタンをクリックして作成画面を開きます。

Users の管理画面で、Create user ボタンをクリックする

User name に任意のユーザー名を入力します。

Next ボタンを押し次の画面を開きます。

User の作成画面で、Next ボタンをクリックする

Permissions options より Attach policies directly を選択します。

権限の設定画面で、Attach policies directly を選択する

Permissions policies の右の Create policy リンクを開きます。

権限の設定画面で、Create policy をクリックする

Select a service の Choose a service より S3を選択します。

サービス選択画面で、S3 を選択する

Access level より Write メニューを開きます。

権限設定画面で、Write のメニューを開く

DeleteObject と PutObject にチェックを入れます。

Resources より Specific を選択し、Add ARNs リンクをクリックします。

権限設定画面で、Add ARNs リンクをクリックする

Resource bucket name に先ほど設定したバケット名を入力します。

Resource object name に * を入力します。

Add ARNs ボタンをクリックします。

ARN の設定画面で、Add ARNs ボタンをクリックする

最下部の Next ボタンをクリックします。

権限設定画面で、Next ボタンをクリックする

Policy details の Policy name にポリシー名を入力します。なお、この名前は後で利用するため、保存してください。

Policy details 画面で、Policy name にポリシー名を入力する

最下部の Create policy ボタンをクリックします。

権限設定画面で、Create policy ボタンをクリックする

Permissions policies のタブに戻り、Permissions policies の再読み込みボタンをクリックします。

権限設定画面の Permissions policies のタブで、再読み込みボタンをクリックする

Permissions policies の検索欄に先ほどの Policy name を入力します。 先ほどの Policy name の Policy にチェックを入れ、 最下部の Next ボタンをクリックし次の画面を開きます。

権限設定画面の検索欄に、作成したポリシーのポリシー名を入力し、検索結果からポリシーにチェックを入れ、Next ボタンをクリックする

最下部の Create user ボタンをクリックし、User を作成します。

Users の作成画面で、Create user ボタンをクリックする

Users より作成した User をクリックし管理画面を開きます。

Users の管理画面で、作成したユーザーを検索し、検索結果から User をクリックする

Security credentials タブを開きます。

Access keys より Create access key ボタンをクリックし、作成画面を開きます。

ユーザーの詳細画面で、Create access key ボタンをクリックする

Use case より Third-party service を選択します。 Confirmation にチェックを入れ、Next ボタンをクリックし次の画面を開きます。

Access key の作成画面で、Third-party service を選択し、Confirmation にチェックを入れ、Next ボタンをクリックする

Create access key ボタンをクリックし、次の画面を開きます。

Access key の作成画面で、Create access key ボタンをクリックする

Download .csv file をクリックし、保存します。csv ファイルから Access key と Secret access key を取得できます。

Access key の作成画面で、Download .csv file をクリックする

Done ボタンをクリックします。

Access key の作成画面で、Done ボタンをクリックする

これで操作は完了です。

この時点で以下のデータを保存できていることを確認してください。

  • リージョン名
  • バケット名
  • .csv ファイル
    • ファイル中に以下の情報が含まれているか確認してください。
      • Access key
      • Secret access key

Wasabi を利用する場合

バケットの作成

Wasabiの管理画面 を開きます。

Create Bucket をクリックして作成画面を開きます。

Buckets の画面で、Create Bucket をクリックする

Select Bucket Name に任意の名前を入力し、Select Region より任意のリージョンを選択します。

なお、ここで設定したバケット名、リージョン名(例. ap-northeast-1)は後で利用するため、保存してください。

バケットの作成画面で、バケット名とリージョン名を入力する

最下部の Create Bucket をクリックし、バケットを作成します。

バケットの作成画面で、Create Bucket をクリックする

IAMの設定

Policies の管理画面 を開き、Create Policy ボタンをクリックして作成画面を開きます。

Policies の管理画面で、Create Policy をクリックする

Policy Name に任意の名前を入力し、Policy Editor に以下の設定値を入力した後、Create Policy ボタンをクリックし、Policy を作成します。 設定値の例の Resource にある skyway-recording-tutorial の部分は、先ほど作成したバケット名で置き換えてください。

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:DeleteObject", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::skyway-recording-tutorial/*" ], "Condition": {} } ] }

ポリシーの設定画面で、ポリシー名とポリシーの設定を入力し、Create Policy をクリックする

次に、Users の管理画面を開き、Create User ボタンをクリックして作成画面を開きます。

Users の管理画面で、Create User をクリックする

Create a Username に任意のユーザー名を入力し、Type of Access の Programmatic (create API key) を選択したあと、Next をクリックします。

Users の作成画面で、Next ボタンをクリックする

Group の設定が必要な場合は設定し、Next をクリックします。不要な場合は、何も設定せずに Next をクリックします。

Users の作成画面で、Next ボタンをクリックする

Attach Policy To User で先ほど作成した Policy を選択します。

Users の作成画面で、Attach Policy To User で先ほど作成した Policy を選択する

Policies than will be attached: で先ほど作成した Policy が表示されていることを確認し、Next をクリックします。

Users の作成画面で、Next ボタンをクリックする

内容を確認し、問題なければ Create User をクリックします。

Users の作成画面で、Create User ボタンをクリックする

User が作成され、 数秒ほど時間が経ってから Access Key が表示されます。Download CSV をクリックし、Access Key と Secret Key が記載された CSV ファイルを保存した後、ポップアップを閉じます。

Create Access Key の画面で、Download CSV ボタンをクリックする

これで操作は完了です。

この時点で以下のデータを保存できていることを確認してください。

  • リージョン名
  • バケット名
  • .csv ファイル
    • ファイル中に以下の情報が含まれているか確認してください。
      • Access Key Id
      • Secret Access Key

チュートリアルアプリの作成

JavaScript SDK を利用して、録音・録画機能を体験できるシンプルなアプリケーションを作成します。

環境構築

Node.js のバージョン 18 以降をインストールし、任意の作業ディレクトリに tutorial ディレクトリを作成します。tutorial ディレクトリ直下に以下の内容の package.json ファイルを作成します。

{ "name": "tutorial", "version": "1.0.0", "type": "module", "scripts": { "client": "parcel client/index.html", "server": "node server/main.js" }, "browserslist": [ "last 3 chrome versions" ], "dependencies": { "@skyway-sdk/room": "^1.7.0", "cors": "^2.8.5", "express": "^4.18.2", "jsrsasign": "^11.1.0" }, "devDependencies": { "parcel": "^2.11.0" } }

その後、tutorial ディレクトリで npm i を実行してください。

サーバーサイド

録音・録画機能はサーバーサイドから SkyWay Recording API を通じて操作するので、そのためのサーバーアプリケーションを作成する必要があります。

ソースファイルの作成

tutorial/server/main.js ファイルを作成し、以下のコードを記述します。

tutorial/server/main.js

import cors from "cors"; import express from "express"; import jsrsasign from "jsrsasign"; import { SkyWayAuthToken, nowInSec, uuidV4 } from "@skyway-sdk/token"; import crypto from "crypto"; const appId = "ここにアプリケーションIDをペーストしてください"; const secret = "ここにシークレットキーをペーストしてください"; const gcsConfig = { service: "GOOGLE_CLOUD_STORAGE", credential: JSON.stringify({ // サービスアカウントの鍵のJSONファイルの内容をコピーペーストする type: "", project_id: "", private_key_id: "", private_key: "", client_email: "", client_id: "", auth_uri: "", token_uri: "", auth_provider_x509_cert_url: "", client_x509_cert_url: "", }), bucket: "", }; const s3Config = { service: "AMAZON_S3", bucket: "", accessKeyId: "", secretAccessKey: "", region: "", }; const wasabiConfig = { service: "WASABI", bucket: "", accessKeyId: "", secretAccessKey: "", endpoint: "", }; const recordingApiBaseUrl = "https://recording.skyway.ntt.com/v1"; const channelApiUrl = "https://channel.skyway.ntt.com/v1/json-rpc";

なお、appId,secret には自身のアプリケーションの値を入力してください。

また、利用するクラウドストレージに合わせて gcsConfig、s3Config、wasabiConfig のいずれかに必要な情報を入力してください。 wasabiConfig の endpoint は、 https://s3.<リージョン名>.wasabisys.com のように設定してください。 (例: https://s3.ap-northeast-1.wasabisys.com )

SkyWay Admin Auth Token の作成

SkyWay の Recording API および Channel API を操作するために SkyWay Admin Auth Token を作成する必要があります。

SkyWay Admin Auth Token の詳細な仕様は次の記事を参照してください。

SkyWay Admin Auth Token

tutorial/server/main.js

// Web APIを操作するためのトークン const createSkyWayAdminAuthToken = () => { const token = jsrsasign.KJUR.jws.JWS.sign( "HS256", JSON.stringify({ alg: "HS256", typ: "JWT" }), JSON.stringify({ exp: nowInSec() + 60, iat: nowInSec(), jti: uuidV4(), appId, }), secret ); return token; };

SkyWay Auth Token の作成

SkyWay のクライアントサイド SDK で利用する SkyWay Auth Token を作成する必要があります。

SkyWay Auth Token の詳細な仕様は次の記事を参照してください。

SkyWay Auth Token

tutorial/server/main.js

// クライアント用のトークン const createSkywayAuthToken = (channelName) => { const token = new SkyWayAuthToken({ jti: uuidV4(), iat: nowInSec(), exp: nowInSec() + 60 * 60 * 24, scope: { app: { id: appId, turn: true, actions: ["read"], channels: [ { name: channelName, actions: ["write"], members: [ { id: "*", name: "*", actions: ["write"], publication: { actions: ["write"], }, subscription: { actions: ["write"], }, }, ], sfuBots: [ { actions: ["write"], forwardings: [ { actions: ["write"], }, ], }, ], }, ], }, }, }).encode(secret); return token; };

ここでは決められた channel 名(room 名)の channel(room)にしか入室できないトークンを作成しています。SkyWay Auth Token の認可部分の設定は開発するサービスの要件に合わせて適切な設定を行ってください。

サーバーフレームワークの設定

このチュートリアルアプリでは、サーバーフレームワークとして express を利用します。こちらの設定を行います。

tutorial/server/main.js

const app = express(); app.use(cors()); app.use(express.json());

Channel(Room)の入室管理

クライアントから Channel 名(Room 名)を受け取って、その Channel 名の Channel を SkyWay Channel API で作成しています。SkyWay Channel API の詳細な仕様は次の記事を参照してください。

SkyWay Channel API

tutorial/server/main.js

const tokenHashChannelIdMap = {}; const channelNameIdMap = {}; const sha256 = (s) => crypto.createHash("sha256").update(s).digest("hex"); app.post("/channels/:channelName/join", async (req, res) => { const { channelName } = req.params; console.log("join", { channelName }); // 入力のchannelNameのChannelを作成する const response = await fetch(channelApiUrl, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${createSkyWayAdminAuthToken()}`, }, body: JSON.stringify({ jsonrpc: "2.0", id: uuidV4(), method: "findOrCreateChannel", params: { name: channelName, }, }), }); const { result: { channel: { id: channelId }, }, } = await response.json(); channelNameIdMap[channelName] = channelId; // 入室できるchannelNameを制限したトークンを作成する const token = createSkywayAuthToken(channelName); // 今後の録音・録画の開始、終了操作を認証するためにtokenとchannelIdの紐付けを行う tokenHashChannelIdMap[sha256(token)] = channelId; res.send({ token }); });

録音・録画 の開始

指定した Channel の全 Publication を録音・録画するために CreateRecordingSession API を呼び出します。

tutorial/server/main.js

const channelNameRecordingMap = {}; app.post("/channels/:channelName/start", async (req, res) => { const { channelName } = req.params; const { authorization } = req.headers; const channelId = channelNameIdMap[channelName]; console.log("start", { channelName, channelId }); if (channelNameRecordingMap[channelName]) { res.status(200).send({ message: "already recording" }); return; } // channelIdとtokenの紐付けを確認する if (channelId !== tokenHashChannelIdMap[sha256(authorization ?? "")]) { res.status(403).send({ message: "Forbidden" }); return; } // Recordingを開始する const response = await fetch( `${recordingApiBaseUrl}/channels/${channelId}/sessions`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${createSkyWayAdminAuthToken()}`, }, body: JSON.stringify({ input: { kind: "SFU", publications: [{ id: "*" }], // すべてのPublicationを保存する }, output: gcsConfig, // Amazon S3を使う場合は s3Config を、Wasabiを使う場合は wasabiConfig を指定 }), } ); const { error, id } = await response.json(); if (error) { res.status(500).send({ message: error.message }); return; } channelNameRecordingMap[channelName] = id; res.status(201).send({ id }); });

録音・録画の終了

RecordingSession を削除するとすべての録音・録画処理が終了し、録音・録画ファイルが指定したクラウドストレージに保存されます。

tutorial/server/main.js

app.delete("/channels/:channelName/stop", async (req, res) => { const { channelName } = req.params; const { authorization } = req.headers; const channelId = channelNameIdMap[channelName]; const sessionId = channelNameRecordingMap[channelName]; console.log("stop", { channelName, channelId, sessionId }); // channelIdとtokenの紐付けを確認する if (channelId !== tokenHashChannelIdMap[sha256(authorization ?? "")]) { res.status(403).send({ message: "Forbidden" }); return; } // 録音・録画を終了する const response = await fetch( `${recordingApiBaseUrl}/channels/${channelId}/sessions/${sessionId}`, { method: "DELETE", headers: { Authorization: `Bearer ${createSkyWayAdminAuthToken()}`, }, } ); // 録音・録画したファイルの一覧を取得する const { error, files } = await response.json(); if (error) { res.status(500).send({ message: error.message }); return; } const filePaths = files.map((file) => file.path); // Recordingしたファイルのパスを出力する。クラウドストレージのこのパスにアップロードされている console.log("filePaths", filePaths); delete channelNameRecordingMap[channelId]; res.status(200).send({ filePaths }); });

サーバーの起動

ポート 9090 でサーバーを起動します。

tutorial/server/main.js

app.listen(9090); console.log("Server is running on http://localhost:9090");

クライアントサイド

以下を参考に tutorial/client/index.html ファイルを作成してください。

tutorial/client/index.html

<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width" /> <title>SkyWay Tutorial</title> </head> <body> <p>ID: <span id="my-id"></span></p> <div> room name: <input id="room-name" type="text" /> <button id="join">join</button> <button id="startRecording">start recording</button> <button id="stopRecording">stop recording</button> </div> <video id="local-video" width="400px" muted playsinline></video> <div id="button-area"></div> <div id="remote-media-area"></div> <script type="module" src="main.js"></script> </body> </html>

次にスクリプトファイルを用意します。 以下を参照して、tutorial/client/main.js ファイルを作成してください。

tutorial/client/main.js

import { RemoteVideoStream, SkyWayContext, SkyWayRoom, SkyWayStreamFactory, } from "@skyway-sdk/room"; (async () => { const localVideo = document.getElementById("local-video"); const buttonArea = document.getElementById("button-area"); const remoteMediaArea = document.getElementById("remote-media-area"); const roomNameInput = document.getElementById("room-name"); const myId = document.getElementById("my-id"); const joinButton = document.getElementById("join"); const startRecordingButton = document.getElementById("startRecording"); const stopRecordingButton = document.getElementById("stopRecording"); const { audio, video } = await SkyWayStreamFactory.createMicrophoneAudioAndCameraStream(); video.attach(localVideo); await localVideo.play(); joinButton.onclick = async () => { const roomName = roomNameInput.value; if (roomName === "") return; const response = await fetch( `http://localhost:9090/channels/${roomName}/join`, { method: "POST", } ); const { token } = await response.json(); const context = await SkyWayContext.Create(token); const room = await SkyWayRoom.Find(context, { name: roomName }, "sfu"); const me = await room.join(); myId.textContent = me.id; await me.publish(audio); await me.publish(video); const subscribeAndAttach = (publication) => { if (publication.publisher.id === me.id) return; const subscribeButton = document.createElement("button"); subscribeButton.textContent = `${publication.publisher.id}: ${publication.contentType}`; buttonArea.appendChild(subscribeButton); subscribeButton.onclick = async () => { const { stream } = await me.subscribe(publication.id); let newMedia; switch (stream.track.kind) { case "video": newMedia = document.createElement("video"); newMedia.playsInline = true; newMedia.autoplay = true; break; case "audio": newMedia = document.createElement("audio"); newMedia.controls = true; newMedia.autoplay = true; break; default: return; } stream.attach(newMedia); remoteMediaArea.appendChild(newMedia); }; }; room.publications.forEach(subscribeAndAttach); room.onStreamPublished.add((e) => subscribeAndAttach(e.publication)); startRecordingButton.onclick = async () => { await fetch(`http://localhost:9090/channels/${room.name}/start`, { method: "POST", headers: { Authorization: context.authTokenString, }, }); }; stopRecordingButton.onclick = async () => { await fetch(`http://localhost:9090/channels/${room.name}/stop`, { method: "DELETE", headers: { Authorization: context.authTokenString, }, }); }; }; })().catch((e) => console.error("main error", e));

チュートリアルアプリの実行

アプリケーションの起動

ターミナルで npm run server を実行してサーバーを起動します。

別のターミナルで npm run client を実行してクライアントを起動します。なお、この際にターミナルにローカルアドレスが表示されるので、そのアドレスをブラウザで開いてください。

Room に join したあと、startRecording ボタンを押すと録音・録画が始まります。stopRecording ボタンを押すと録音・録画が終了します。

保存されたファイルの確認

stopRecording ボタンを押すと、サーバー側の標準出力にクラウドストレージにアップロードされたファイルのパスが出力されます。 クラウドストレージのコンソールにアクセスし、ファイルがアップロードされていることを確認してください。

保存されるファイルの詳細な仕様については概要の録音・録画ファイルの項目を参照してください。