クイックスタート
本チュートリアルでは、録音・録画機能を利用するための基本的な手順を説明します。
クラウドストレージの準備
録音・録画機能を使用するためには 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 ボタンを押すと、サーバー側の標準出力にクラウドストレージにアップロードされたファイルのパスが出力されます。 クラウドストレージのコンソールにアクセスし、ファイルがアップロードされていることを確認してください。
保存されるファイルの詳細な仕様については概要の録音・録画ファイルの項目を参照してください。