クイックスタート
本チュートリアルでは、録音・録画機能を利用するための基本的な手順を説明します。
クラウドストレージの準備
録音・録画機能を使用するためには 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 ファイルがダウンロードされます。
バケットの作成
Google Cloud Storage の管理画面を開きます。
作成ボタンをクリックします。
バケット名、リージョンなどの設定を行い作成ボタンをクリックします。ここで設定したバケット名は後で利用するため、保存してください。
これで操作は完了です。
以下のデータを保存できていることを確認してください。
- バケット名
- サービスアカウントの鍵の JSON ファイル
Amazon S3 を利用する場合
S3 バケットの作成
Amazon S3 の管理画面 を開きます。
Create bucket をクリックして作成画面を開きます。
AWS Region より任意のリージョンを選択し、Bucket name に任意の名前を入力します。
なお、ここで設定したリージョン名(例. ap-northeast-1)、バケット名は後で利用するため、保存してください。
最下部の Create bucket をクリックし、S3 バケットを作成します。
IAMの設定
IAM の管理画面を開き、メニューよりUsersの管理画面を開きます。
Create user ボタンをクリックして作成画面を開きます。
User name に任意のユーザー名を入力します。
Next ボタンを押し次の画面を開きます。
Permissions options より Attach policies directly を選択します。
Permissions policies の右の Create policy リンクを開きます。
Select a service の Choose a service より S3を選択します。
Access level より Write メニューを開きます。
DeleteObject と PutObject にチェックを入れます。
Resources より Specific を選択し、Add ARNs リンクをクリックします。
Resource bucket name に先ほど設定したバケット名を入力します。
Resource object name に *
を入力します。
Add ARNs ボタンをクリックします。
最下部の Next ボタンをクリックします。
Policy details の Policy name にポリシー名を入力します。なお、この名前は後で利用するため、保存してください。
最下部の Create policy ボタンをクリックします。
Permissions policies のタブに戻り、Permissions policies の再読み込みボタンをクリックします。
Permissions policies の検索欄に先ほどの Policy name を入力します。 先ほどの Policy name の Policy にチェックを入れ、 最下部の Next ボタンをクリックし次の画面を開きます。
最下部の Create user ボタンをクリックし、User を作成します。
Users より作成した User をクリックし管理画面を開きます。
Security credentials タブを開きます。
Access keys より Create access key ボタンをクリックし、作成画面を開きます。
Use case より Third-party service を選択します。 Confirmation にチェックを入れ、Next ボタンをクリックし次の画面を開きます。
Create access key ボタンをクリックし、次の画面を開きます。
Download .csv file をクリックし、保存します。csv ファイルから Access key と Secret access key を取得できます。
Done ボタンをクリックします。
これで操作は完了です。
この時点で以下のデータを保存できていることを確認してください。
- リージョン名
- バケット名
- .csv ファイル
- ファイル中に以下の情報が含まれているか確認してください。
- Access key
- Secret access key
- ファイル中に以下の情報が含まれているか確認してください。
Wasabi を利用する場合
バケットの作成
Wasabiの管理画面 を開きます。
Create Bucket をクリックして作成画面を開きます。
Select Bucket Name に任意の名前を入力し、Select Region より任意のリージョンを選択します。
なお、ここで設定したバケット名、リージョン名(例. ap-northeast-1)は後で利用するため、保存してください。
最下部の Create Bucket をクリックし、バケットを作成します。
IAMの設定
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": {} } ] }
次に、Users の管理画面を開き、Create User ボタンをクリックして作成画面を開きます。
Create a Username に任意のユーザー名を入力し、Type of Access の Programmatic (create API key) を選択したあと、Next をクリックします。
Group の設定が必要な場合は設定し、Next をクリックします。不要な場合は、何も設定せずに Next をクリックします。
Attach Policy To User で先ほど作成した Policy を選択します。
Policies than will be attached: で先ほど作成した Policy が表示されていることを確認し、Next をクリックします。
内容を確認し、問題なければ Create User をクリックします。
User が作成され、 数秒ほど時間が経ってから Access Key が表示されます。Download CSV をクリックし、Access Key と Secret Key が記載された 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 の詳細な仕様は次の記事を参照してください。
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 の詳細な仕様は次の記事を参照してください。
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 の詳細な仕様は次の記事を参照してください。
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 ボタンを押すと、サーバー側の標準出力にクラウドストレージにアップロードされたファイルのパスが出力されます。 クラウドストレージのコンソールにアクセスし、ファイルがアップロードされていることを確認してください。
保存されるファイルの詳細な仕様については概要の録音・録画ファイルの項目を参照してください。