概要
- Amazon Connectには留守番電話機能は無いため個別に作成する。
- 基本の考え方等は『[Amazon Connect]営業時間外に着信したビジネスチャンスを失わないように留守番電話機能をつけてみた』をもとにしています。
- ここまで作成すると、留守番電話で受け付けた音声がWAV形式でS3に保存されます。
使用ユーザー
- IAMユーザー
手順
WAVデータを保存するS3の作成
-
AWSにサインインします。
- アカウント、ユーザー名、パスワードを入力してサインインします。
アカウント内(IAM)で作成したユーザーを使用してコンソールにサインインする
- アカウント、ユーザー名、パスワードを入力してサインインします。
-
『AWSマネジメントコンソール』画面にある「サービスを検索」にS3と入力し、検索結果から《S3》をクリックし、Amazon S3 コンソール(https://console.aws.amazon.com/s3/)を開きます。
-
S3のバケット一覧が表示されるので、営業時間の判断で作成したバケットをクリックします。
-
- フォルダ名:フォルダの名前
- 暗号化:適宜必要な暗号化設定を選択
Lambdaでebmlを利用するための準備
-
Node.jsをインストールする
- 詳細は、Windows 10へNode.jsをインストールするを参照してください。
-
ebmlをダウンロードするため「nodejs」フォルダを作成するためコマンドを入力して実行します。
md nodejs
-
nodejsフォルダに移動するためコマンドを入力して実行します。
cd nodejs
-
ebmlをダウンロードするためコマンドを入力して実行します。
npm install ebml
-
- 作成したnodejsフォルダの中身ではなくフォルダ自体を圧縮します。
-
AWSにサインインします。
- アカウント、ユーザー名、パスワードを入力してサインインします。
アカウント内(IAM)で作成したユーザーを使用してコンソールにサインインする
- アカウント、ユーザー名、パスワードを入力してサインインします。
-
『AWSマネジメントコンソール』画面にある「サービスを検索」にLambdaと入力し、検索結果から《Lambda》をクリックし、Amazon Lambda コンソール(https://console.aws.amazon.com/lambda/)を開きます。
-
- 名前:ebml
- アップロード:先ほど作成したZipファイル
- ランタイム:Node.js 12.x
WAV形式に変換してS3に保存するLambdaの作成
- 以下のコードで各自で変える部分
- region変数:バケットを作成したリージョン名
- bucketName変数:作成したバケット名
- putKey変数:作成したフォルダ名(終わりに『/』を入れる)
const AWS = require("aws-sdk");
const region = 'XXXXXXXXX';
const bucketName = 'my-baseXXXXXXXXX';
const putKey = 'my-voicemessageXXXXXXXXX/wav/';
exports.handler = async (event) => {
console.log(JSON.stringify(event));
const s3 = new S3(AWS, region);
console.log(event.Records);
for (let record of event.Records) {
const getKey = record.s3.object.key;
var index = getKey.lastIndexOf("/");
var fileName = "noname";
if (index >= 0) {
fileName = getKey.substr(index + 1);
};
// S3から録音データに関する情報を取得
const data = await s3.get(record.s3.bucket.name, getKey);
const info = JSON.parse(data.Body);
const streamName = info.streamARN.split('stream/')[1].split('/')[0];
const fragmentNumber = info.startFragmentNumber;
console.log(info)
// Kinesis Video Streamsから当該RAWデータの取得
const raw = await getMedia(streamName, fragmentNumber);
// RAWデータからWAVファイルを作成
const wav = Converter.createWav(raw, 8000);
// WAVファイルをS3に保存する
let tagging = ''; // 付加情報をタグに追加する
tagging += "customerEndpoint=" + info.customerEndpoint + '&';
tagging += "systemEndpoint=" + info.systemEndpoint + '&';
tagging += "startTimestamp=" + info.startTimestamp;
await s3.put(bucketName, putKey + fileName + '.wav', Buffer.from(wav.buffer), tagging)
}
return {};
};
class S3 {
constructor(AWS, region){
this._s3 = new AWS.S3({region:region});
}
async get(bucketName, key){
const params = {
Bucket: bucketName,
Key: key
};
return await this._s3.getObject(params).promise();
}
async put(bucketName, key, body, tagging) {
const params = {
Bucket: bucketName,
Key: key,
Body: body,
Tagging: tagging
};
return await this._s3.putObject(params).promise();
}
}
const ebml = require('ebml');
async function getMedia(streamName, fragmentNumber) {
// Endpointの取得
const kinesisvideo = new AWS.KinesisVideo({region: region});
var params = {
APIName: "GET_MEDIA",
StreamName: streamName
};
const end = await kinesisvideo.getDataEndpoint(params).promise();
// RAWデータの取得
const kinesisvideomedia = new AWS.KinesisVideoMedia({endpoint: end.DataEndpoint, region:region});
var params = {
StartSelector: {
StartSelectorType: "FRAGMENT_NUMBER",
AfterFragmentNumber:fragmentNumber,
},
StreamName: streamName
};
const data = await kinesisvideomedia.getMedia(params).promise();
const decoder = new ebml.Decoder();
let chunks = [];
decoder.on('data', chunk => {
if(chunk[1].name == 'SimpleBlock'){
chunks.push(chunk[1].data);
}
});
decoder.write(data["Payload"]);
// chunksの結合
const margin = 4; // 各chunkの先頭4バイトを破棄する
var sumLength = 0;
chunks.forEach( chunk => {
sumLength += chunk.byteLength - margin;
})
var sample = new Uint8Array(sumLength);
var pos = 0;
chunks.forEach(chunk => {
let tmp = new Uint8Array(chunk.byteLength - margin);
for(var e = 0; e < chunk.byteLength - margin; e++){
tmp[e] = chunk[e + margin];
}
sample.set(tmp, pos);
pos += chunk.byteLength - margin;
})
return sample.buffer;
}
class Converter {
// WAVファイルの生成
static createWav(samples, sampleRate) {
const len = samples.byteLength;
const view = new DataView(new ArrayBuffer(44 + len));
this._writeString(view, 0, 'RIFF');
view.setUint32(4, 32 + len, true);
this._writeString(view, 8, 'WAVE');
this._writeString(view, 12, 'fmt ');
view.setUint32(16, 16, true);
view.setUint16(20, 1, true); // リニアPCM
view.setUint16(22, 1, true); // モノラル
view.setUint32(24, sampleRate, true);
view.setUint32(28, sampleRate * 2, true);
view.setUint16(32, 2, true);
view.setUint16(34, 16, true);
this._writeString(view, 36, 'data');
view.setUint32(40, len, true);
let offset = 44;
const srcView = new DataView(samples);
for (var i = 0; i < len; i+=4, offset+=4) {
view.setInt32(offset, srcView.getUint32(i));
}
return view;
}
static _writeString(view, offset, string) {
for (var i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
}
}
-
AWSにサインインします。
- アカウント、ユーザー名、パスワードを入力してサインインします。
アカウント内(IAM)で作成したユーザーを使用してコンソールにサインインする
- アカウント、ユーザー名、パスワードを入力してサインインします。
-
『AWSマネジメントコンソール』画面にある「サービスを検索」にLambdaと入力し、検索結果から《Lambda》をクリックし、Amazon Lambda コンソール(https://console.aws.amazon.com/lambda/)を開きます。
-
- voice-message-createwav
-
「関数コード」に初期で登録されているコードを削除して、上記voice-message-createwavをコピー&ペーストします。
-
日本時間に対応させるため、画面を下にスクロールして「環境変数」に以下の値を入力し《保存》ボタンをクリックします。
- キー:TZ、値:Asia/Tokyo
-
このLambdaは実行時間がかかるため、デフォルトの設定ではTimeOutエラー発生するためタイムアウトの設定を以下の内容に変更します。
- タイムアウト:10秒
-
Lambdaにサービスへのアクセス権を設定するため下にスクロールして「実行ロール」を表示し、《XXXXXXXXロールを表示》をクリックします。
- Lambdaを作成する毎に自動的にLambda名の付いたロールが一つ作成されます。
- Lambdaで利用しているAPIのアクセス権を付与します。
- kinesis Video Streamsデータ取得用情報(voice-message-infomationで作成したもの)をS3から取得するための権限
- GetObject
- kinesis Video Streamsからデータを読み込むための権限
- GetMedia
- GetDataEndpoint
- kinesis Video StreamsデータをWAV形式に変換したものをS3に保存するための権限
- PutObjectTagging
- PutObject
- kinesis Video Streamsデータ取得用情報(voice-message-infomationで作成したもの)をS3から取得するための権限
-
ビジュアルエディタが表示されるので《さらにアクセス許可を追加する》をクリックします。
- Lambdaの実行ログを記録するため、デフォルトでCloudWatch Logsの権限が付与されます。
-
- Bucket name:kinesis Video Streamsデータ取得用情報が保存されているS3のバケット名+フォルダ名(バケット名とフォルダ名やフォルダ名とフォルダ名の間は『/』で連結する)
- Bucket name:my-baseXXXXXXXXX/my-voicemessageXXXXXXX/infomation
- Object name:すべてにチェックを付ける(テキストボックスには*が入力される)
- Bucket name:kinesis Video Streamsデータ取得用情報が保存されているS3のバケット名+フォルダ名(バケット名とフォルダ名やフォルダ名とフォルダ名の間は『/』で連結する)
-
「アクション」に今回利用するPutObjectTaggingを上書き入力し、《PutObjectTagging》をクリックし、選択状態にします。
-
- Bucket name:kinesis Video StreamsデータをWAVに変換したデータを保存するS3のバケット名+フォルダ名(バケット名とフォルダ名やフォルダ名とフォルダ名の間は『/』で連結する)
- Bucket name:my-baseXXXXXXXXX/my-voicemessageXXXXXXX/wav
- Object name:すべてにチェックを付ける(テキストボックスには*が入力される)
- Bucket name:kinesis Video StreamsデータをWAVに変換したデータを保存するS3のバケット名+フォルダ名(バケット名とフォルダ名やフォルダ名とフォルダ名の間は『/』で連結する)
-
「サービス」にKinesis Video Streamsと入力し、検索結果から《Kinesis Video Streams》をクリックします。
-
「アクション」に今回利用するGetDAtaEndpointを入力し、《GetDAtaEndpoint》をクリックし、選択状態にします。
-
- Reagion:kinesis Video Streamsを作成したリージョン名
- Reagion : aap-northeast-1(Kinesis Video Streamsを作成したリージョン)
- Account:自分のアカウント名(デフォルトで入力される)
- Stream name:すべて
- Creation time:すべて
- Reagion:kinesis Video Streamsを作成したリージョン名
-
- 名前:ebml
- バージョン:先ほど作成したebmlで利用するバージョン番号
-
kinesis Video Streamsデータ取得用情報がS3に作成された場合に動かしたいので《トリガーを追加》をクリックします。
-
- バケット:kinesis Video Streamsデータ取得用情報を作成したバケット
- イベントタイプ:PUT
- プレフィックス:kinesis Video Streamsデータ取得用情報を作成したフォルダ
参考サイト
- [Amazon Connect]営業時間外に着信したビジネスチャンスを失わないように留守番電話機能をつけてみた
- AWS Lambda Layersでnode_modulesを使う
- Kinesis ビデオストリーム API およびプロデューサーライブラリのサポート
- Kinesis ビデオストリーム データにアクセスする方法
- GitHub amazon-connect/amazon-connect-realtime-transcription
- サンプリング周波数とビットレート
- 音ファイル(拡張子:WAVファイル)のデータ構造について
- Windows 10へNode.jsをインストールする