LoginSignup
0

posted at

Amazon Connect にて録音した音声に別録音の音声が入る

起きたこと

先日 Amazon Connect を駆使して構築したIVRシステムに、
以下の記事を参考にして留守録機能を搭載しました。
https://dev.classmethod.jp/articles/amazon-connect-voice-mail-from-kinesis-video-stream/

しかし、どうやら録音した音声に別の通話の音声が混ざるケースがあるようなので調査しました。

原因

StreamARNが重複することがあり、そのStreamARNにGetMediaで開始契機だけを指定することで発生していました。
(StreamARNは1通話ごとに1つ作成されると思っていましたが、AWSへ問い合わせてみると再利用されることがあるようです。)

GetMediaStartSelector(開始契機)しか指定できない。
GetMedia - Amazon Kinesis Video Streams
https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/API_dataplane_GetMedia.html

対応方法

ListFragmentsGetMediaForFragmentList を併用することで対応しました。

Image from Gyazo

コード抜粋

index.js
async function getMediaData(streamName, startTimestamp, endTimestamp) {

    // ListFragments用のEndpointの取得
    const kinesisvideo = new AWS.KinesisVideo({region: region});
    let listFragmentsEndParams = {
        APIName: "LIST_FRAGMENTS",
        StreamName: streamName
    };
    const listFragmentsEnd = await kinesisvideo.getDataEndpoint(listFragmentsEndParams).promise();

    const listFragmentsClient = new AWS.KinesisVideoArchivedMedia({endpoint: listFragmentsEnd.DataEndpoint, region:region});
    let listFragmentsParams = {
        FragmentSelector: {
            FragmentSelectorType: "PRODUCER_TIMESTAMP",
            TimestampRange: {
                StartTimestamp: startTimestamp,
                EndTimestamp: endTimestamp,
            }
        },
        StreamName: streamName
    };
    // ListFragmentsデータの取得
    const listFragments = await listFragmentsClient.listFragments(listFragmentsParams).promise();

    // GetMediaForFragmentList用のEndpointの取得
    let mediaEndParams = {
        APIName: "GET_MEDIA_FOR_FRAGMENT_LIST",
        StreamName: streamName
    };
    const mediaEnd = await kinesisvideo.getDataEndpoint(mediaEndParams).promise();

    const mediaClient = new AWS.KinesisVideoArchivedMedia({endpoint: mediaEnd.DataEndpoint, region:region});

    // listFragmentsで取得されるFragmentは順不同なのでProducerTimestampで昇順に並び替える
    listFragments.Fragments.sort(function(a, b){
        return (a.ProducerTimestamp > b.ProducerTimestamp ? 1 : -1);
    });
    let fragmentNumberArray = [];
    listFragments.Fragments.forEach(function(fragment) {
        fragmentNumberArray.push(fragment["FragmentNumber"]);
    });

    const mediaParams = {
        Fragments: fragmentNumberArray,
        StreamName: streamName
    };
    // GetMediaForFragmentListデータの取得
    const media = await mediaClient.getMediaForFragmentList(mediaParams).promise();

※参考
Amazon Kinesis Video Streams API/SDK による開発
https://qiita.com/yh1224/items/cb922f15b791e7f31677
Class: AWS.KinesisVideoArchivedMedia
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/KinesisVideoArchivedMedia.html
ListFragments - Amazon Kinesis Video Streams
https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/API_reader_ListFragments.html
GetMediaForFragmentList - Amazon Kinesis Video Streams
https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/API_reader_GetMediaForFragmentList.html

ハマった箇所

listFragmentsが永遠に空の状態で取得される

GetDataEndpointAPIName が誤っていました。
listFragments用のLIST_FRAGMENTSがあるのに、GET_MEDIAを流用していたことが原因でした。

リファレンスによると下記のようなものが返ってきてもいいはずなのに返却値はずっと{}

{
    "Fragments": [],
    "NextToken": "",
}

エラーも起きておらず、paramの指定の仕方が悪くてヒットしなかっただけなのか、原因特定にだいぶハマりました。

GetDataEndpoint - Amazon Kinesis Video Streams
https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/API_GetDataEndpoint.html

GetMediaForFragmentListで取得した音声がぐちゃぐちゃ

下記のようにソートしてあげないと、順番がぐちゃぐちゃでした笑

index.js
    // listFragmentsで取得されるFragmentは順不同なのでProducerTimestampで昇順に並び替える
    listFragments.Fragments.sort(function(a, b){
        return (a.ProducerTimestamp > b.ProducerTimestamp ? 1 : -1);
    });

Image from Gyazo

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0