[Amazon Connect] 留守番電話を設置した場合に必要なKinesis Video StreamsとS3のデータサイズについて

2020.06.07

1 はじめに

CX事業本部の平内(SIN)です。

以前に、Amazon Connect(以下、Connect)で、留守番電話を構築するブログを書かせて頂きました。

上記では、Connectの機能である、「メディアストリーミングへの保存」を利用して、音声をKinesis Video Streamsに保存しています。

この仕組みを構築した場合に、ランニングコストの一つとなる「Kinesis Video Streamsに保存されるデータや、S3のデータサイズは、どれぐらいになるのか?」というお問い合わせを受けましたので、今回、少し、雑になりますが、確認してみました。

なお、上記ブログの通り構築すると、録音時間は、最大13秒間となっています。
※(#)シャープを押して終了すれば、それより短くなります。

2 音声データの形式

Connectで「メディアストリームへの保存」を行うと、音声データは、16bit PCM 8000Hzで保存されます。

保存の対象として、「顧客から」と「顧客へ」の2つが選択可能ですが、これが、チャンネル数になります。「顧客から」のみにチェックがある場合、1チャンネル、両方にチェックされた場合2チャンネルとなります。

ここで音声のRAWデータのみを計算すると以下のようになります。

  • 量子化ビット数が16bitなので1つのデータは2byte
  • サンプリング周波数8kHzなので1秒間のデータ数は8,000個
  • チャンネル数が1の場合、16KByte/sec、チャンネル数が2の場合、32KbByte/sec

例えば、「顧客から」のみ(1チャンネル)の場合、13秒で、208KByte、30秒で、480KByteです。


参考:[Amazon Connect] 営業時間外に着信したビジネスチャンスを失わないように留守番電話機能をつけてみた

3 S3上の録音データ

録音したデータは、WAVファイルとしてS3に保存されます。

下記は、デフォルトの13秒で2回、設定を60秒に変更して1回、保存した様子です。

S3上の録音データのサイズは、以下の通りとなっていました。

  • 255KByte (13秒のデータ)
  • 270KByte (13秒のデータ)
  • 525KByte (30秒のデータ)

コンタクトフローでは、13秒と設定していますが、実際に保存される時間は、ピッタリ13秒とはならないため、いくらかの誤差が生じています。

また、RAWデータをWAV形式にする場合、ヘッダの追加が必要になるため、RAWデータのサイズに数バイトのヘッダが追加されています。

4 Kinesis Video Stremas上のデータ

Kinesis Video Streams上では、に保存されたストリームです。

音声は、RAWデータのままですが、保存は、フラグメント単位にヘッダが付与された状態になります。

参考:Fragment Header

Long    fragmentLengthInMilliseconds() 時間
String  fragmentNumber() フラグメント番号
Long    fragmentSizeInBytes() データサイズ
int hashCode() ハッシュ
Instant producerTimestamp() プロデューサ・タイムスタンプ
Instant serverTimestamp() サーバ・タイムスタンプ

下記は、フラグメント単位でデータを列挙してみた状況です。

  • 設定値13秒
0 960msec 17806bytes
1 960msec 17508bytes
2 960msec 17508bytes
(略)
13 960msec 17508bytes
14 960msec 17568bytes
15 960msec 17508bytes
16 960msec 17508bytes
17 832msec 15016bytes
フラグメント数:  18
サイズ:  313010
動画の時間(msec):  17152
  • 設定値13秒
0 960msec 17806bytes
1 960msec 17508bytes
2 960msec 17508bytes
(略)
14 960msec 17568bytes
15 960msec 17508bytes
16 896msec 16053bytes

フラグメント数:  17
サイズ:  296539
動画の時間(msec):  16256
  • 設定値60秒
0 960msec 17806bytes
1 960msec 17508bytes
2 960msec 17508bytes
(略)
31 960msec 17508bytes
32 960msec 17508bytes
33 768msec 13979bytes
フラグメント数:  34
サイズ:  592161
動画の時間(msec):  32448
設定時間(sec) フラグメント数 サイズ(Kbyte) 保存時間(sec)
13 18 313.010 17.152
13 17 296.539 16.256
30 34 592.161 32.448

設定した時間(13sec,60sec)より、やや多長い時間保存され、また、一定でないことも分かりますが、RAWデータとして計算した数値に、フラグメント(1秒弱)ごとにヘッダが追加されていると考えると、だいたい納得できる数字であると言えるかも知れません。

フラグメント一覧を出力したコードです。

const AWS = require('aws-sdk');
const kinesisvideo = new AWS.KinesisVideo();

async function listFragments(streamName, fragmentSelectorType, startTimestamp, endTimestamp) {
    // EndPointで初期化して、KinesisVideoArchivedMediaを生成する
    var params = {
        APIName: "LIST_FRAGMENTS",
        StreamName: streamName
    };
    const e = await kinesisvideo.getDataEndpoint(params).promise();
    const kinesisvideoarchivedmedia = new AWS.KinesisVideoArchivedMedia({endpoint: e.DataEndpoint});

    const maxResults = 100; // 指定可能な数字は1000まで、これを超えた場合は、NextTokenで取得する
    let fragments = [];
    while(true){
        var nextToken;

        // listFragmentsのパラメータの生成(2回目以降は変換する)
        var params = { StreamName: streamName }
        if (nextToken) {
            params.NextToken = nextToken
        } else {
            params.FragmentSelector =  {
                FragmentSelectorType: fragmentSelectorType,
                TimestampRange: { 
                    EndTimestamp: endTimestamp, 
                    StartTimestamp: startTimestamp
                }
            }
            params.MaxResults =  maxResults;
        }

        // listFragmentsでフラグメント情報を取得する
        const data = await kinesisvideoarchivedmedia.listFragments(params).promise();
        data.Fragments.forEach( fragment => fragments.push(fragment) )

        // 次のデータがない場合は、列挙を終了する
        if(data.NextToken == null) {
            break;
        }
        nextToken = data.NextToken;
    }

    // 配列は、古い順にソートする
    return  fragments.sort((a,b)=> {
        if( a.ProducerTimestamp < b.ProducerTimestamp ) return -1;
        if( a.ProducerTimestamp > b.ProducerTimestamp ) return 1;
        return 0;
    })
}

async function job() {
    const streamName = 'sample-connect-xxxxxxxxxxxx';
    const fragmentSelectorType = "PRODUCER_TIMESTAMP";
    const startTimestamp = 0; // データの最初から取得する
    const endTimestamp = new Date(); // 現在の時間まで

    // フラグメントの列挙
    let fragments = await listFragments(streamName, fragmentSelectorType, startTimestamp, endTimestamp);
    fragments.forEach( (fragment,i) => {
        console.log(`${i} ${fragment.FragmentLengthInMilliseconds}msec ${fragment.FragmentSizeInBytes}bytes`)
    })

    // 全時間
    const msec = fragments.map( f => f.FragmentLengthInMilliseconds).reduce( (p, c) => p + c);
    // 全サイズ
    const bytes = fragments.map( f => f.FragmentSizeInBytes).reduce( (p, c) => p + c);

    console.log("フラグメント数: " , fragments.length);
    console.log("サイズ: " , bytes);
    console.log("動画の時間(msec): " , msec);
}

job();


参考 : [Kinesis Video Streams] ストリーム上のフラグメントデータから、任意の時間帯を指定して動画を取得してみました

5 最後に

結果的に、正確なサイズは算出できないことが分かってしまいましたが、RAWデータ(音声データ)のサイズを基準に、ヘッダサイズと、やや長くなる保存時間等を考慮すると、だいたい計算できるかも知れません。

改めて、今回試してみた結果です。

設定時間(sec) RAWデータ計算値(KByte) KVS(KByte) S3(KByte)
13 208 313 255
13 208 296 270
30 480 592 525