AmazonConnectでオペレータ側の音声をリアルタイムに取得してみた

# 概要 
今回は[amazon-connect-streams](https://github.com/aws/amazon-connect-streams)と[connect-rtc-js](https://github.com/aws/connect-rtc-js)を使うことでAmazonConnectのオペレータの音声をリアルタイムに取得してみました。音声をリアルタイムに取得することができたことで、COTOHA等の音声認識システムに入れるなどの幅が広がります。

# なぜそんなことをしたの？
　AmazonConnectにはリアルタイムに音声を取得する方法として[「リアルタイム顧客音声ストリーム機能」](https://aws.amazon.com/jp/about-aws/whats-new/2018/12/amazon-connect-adds-real-time-customer-voice-stream/)が存在します。
　しかしこの機能では、**お客様側(カスタマ側)**の音声しかリアルタイムに取得することができません。
　そこで今回はAmazonConnectの**オペレータ側**の音声をリアルタイムに取得してみました。

# イメージ図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339600/8806135a-2de8-88ca-c087-0b80bda0d512.png)
※今回はCCPを動かすサーバ(取得した音声を操作するサーバ)をAWSのS3上に作っています。


# 作成方法
## 前提
- 実行する環境にNode.jsがインストールされていること
- 実際に動作をさせるにはAmazon Connectに1度ログインをしていること

## ライブラリの取得とビルド
1. ソフトフォンを使用するためのソースを右のリンクから取得します。[amazon-connect-streams](https://github.com/aws/amazon-connect-streams)
2. 通話しているストリームを取得するためのソースを右のリンクから取得します。[connect-rtc-js](https://github.com/aws/connect-rtc-js)
3. 1で取得したソースをビルドします。amazon-connect-streamsをおいたディレクトリにて`make`を実施してください。実施すると`amazon-connect-${version}.js`といったファイルが作られます。(versionの部分は変化します)。


### HTML側の作成
1. 自作のhtmlを作成し、以下のようにコードを書きます。<br>
amazon-connect-1.3-12-xxxxxxxx.jsはビルドしてできたjavascriptのファイル名に置き換えてください。<br>
URLでxxxx.awsapps.comとしている個所はAmazon Connectのインスタンスに置き換えてください。<br>
divのidに指定しているcontainerDivとremote-audioは変更しないでください。

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Custom CCP</title>
    <script type="text/javascript" src="amazon-connect-1.3-12-xxxxxxxx.js"></script>
    <script type="text/javascript" src="connect-rtc.js"></script>
</head>
 
<body>
    <center>
        <div id="containerDiv" style="width: 320px; height: 465px"></div>
        <div><audio id="remote-audio" controls><p>音声を再生するには、audioタグをサポートしたブラウザが必要です。</p></audio></div>
        <div><audio id="user-audio" controls><p>音声を再生するには、audioタグをサポートしたブラウザが必要です。</p></audio></div>
    </center>
</body>
<script type="text/javascript">
    const ccpUrl = "https://xxxx.awsapps.com/connect/ccp#/";

    connect.core.initCCP(containerDiv, {
        ccpUrl:        ccpUrl,
        loginPopup:    true,
        softphone: {
            allowFramedSoftphone: false
        },
    });
    connect.core.initSoftphoneManager({allowFramedSoftphone: true});

    // コンタクトイベントの取得
    connect.contact(function(contact) {
        var softphoneMediaInfo = contact.getAgentConnection().getSoftphoneMediaInfo();

        var remoteAudio = document.getElementById("remote-audio");
        var mediaInfo = JSON.parse(softphoneMediaInfo);
        var rtcConfig = mediaInfo.webcallConfig || JSON.parse(mediaInfo.callConfigJson);//mediaInfo.webcallConfig is used internally by Amazon Connect team only
        var session = new connect.RTCSession(rtcConfig.signalingEndpoint,
            rtcConfig.iceServers,
            mediaInfo.callContextToken,
            console);
        session.echoCancellation = true;
        session.remoteAudioElement = remoteAudio;
        session.forceAudioCodec = 'OPUS';
        session.onSessionConnected = () => {
            statsCollector = setInterval(() => {
                var collectTime = new Date();
                Promise.all([session.getUserAudioStats(), session.getRemoteAudioStats()]).then((streamStats) => {
                    console.log(collectTime + " Audio statistics : " + JSON.stringify(streamStats));
                });
            }, 2000);
        };
    });
 </script>
</html>
```

### amazon-connect-1.3-12-xxxxxxxx.jsの修正
1.このままではマイクから音声を取得するためのコードがないので、マイクから音声を取得するためのコードを書きます。<br>
今回は通話の開始時にマイクの音声を取得するために、ビルドしてできたamazon-connect-1.3-12-xxxxxxxx.jsの
変数onRefreshContactに定義されている関数の末尾に以下のコードを追加します。<br>
「amazon-connect-streams」内にあるsoftphone.jsのソースコードを変更して、再度ビルドしても同じ結果となります。

```javascript
var handleSuccess = function(stream) {
    document.getElementById('user-audio').srcObject = stream;
};
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(handleSuccess);
```
2.通話が終了したときにマイクの音声の再生が終了するように変数deleteLocalMediaStreamに定義されている関数の末尾に以下のコードを追加します。<br>
こちらも「amazon-connect-streams」内にあるsoftphone.jsのソースコードを変更して、再度ビルドしても同じ結果となります。

```javascript
document.getElementById('user-audio').srcObject = null;
```

## HTMLとjavascriptの配備
今回はcloudfrontを使用して、S3に置いたファイルを使用して自作のHTMLファイルを表示させ動作確認を行います。
S3にバケットを作成して、作成したhtmlファイル、amazon-connect-1.3-12-xxxxxxxx.js、connect-rtc.jsを配備します。
S3に配置したhtmlファイルをwebページとして表示するための手順はこちらの記事を参考にしました。
[特定バケットに特定ディストリビューションのみからアクセスできるよう設定する](https://dev.classmethod.jp/cloud/aws/cloudfront-s3-origin-access-identity/)
cloudfrontで公開するURLが発行されたら、Amazon Connectのアプリケーション統合画面でURLを追加します。その際のURLは`xxxxxxxxx.cloudfront.net`までにしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339600/3251e75c-0d43-c1a8-3e04-dab0185210f5.png)

## 動作確認
アプリケーション統合画面で追加した画面にアクセスを行うと以下のような画面が表示されます。
初めてアクセスする場合はマイクの使用許可のポップアップが表示されるので許可をしてください。
<img width="210" alt="CCPカスタム初期画面.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339600/1aa7b2cb-02c2-6795-86e8-543eb098da18.png">

通話中画面(電話番号部分は隠してあります)
ソフトフォンの方が電話をかけ始めてすぐにストリームが作成されますが、マイク側のstreamは経過時間と同じ時間であることが確認できます。
<img width="210" alt="CCPカスタム通話中画面.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339600/3290bf1d-7ca3-b30f-e072-eaf8af686fe0.png">

# 今後の利用
これを応用すれば、マイクから取得した音声をCOTOHA 音声認識APIに投げる、Google Speech to Textに投げるなどの応用が考えられます。
機会があればそちらも記事にしたいと思います。

# 参考資料
特定バケットに特定ディストリビューションのみからアクセスできるよう設定する
https://dev.classmethod.jp/cloud/aws/cloudfront-s3-origin-access-identity/