起きたこと
先日 Amazon Connect を駆使して構築したIVRシステムに、
以下の記事を参考にして留守録機能を搭載しました。
https://dev.classmethod.jp/articles/amazon-connect-voice-mail-from-kinesis-video-stream/
しかし、どうやら録音した音声に別の通話の音声が混ざるケースがあるようなので調査しました。
まとめたスライド
原因
StreamARNが重複することがあり、そのStreamARNにGetMedia
で開始契機だけを指定することで発生していました。
(StreamARNは1通話ごとに1つ作成されると思っていましたが、AWSへ問い合わせてみると再利用されることがあるようです。)
GetMedia
はStartSelector
(開始契機)しか指定できない。
GetMedia - Amazon Kinesis Video Streams
https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/API_dataplane_GetMedia.html
対応方法
ListFragments
と GetMediaForFragmentList
を併用することで対応しました。
コード抜粋
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が永遠に空の状態で取得される
GetDataEndpoint
の APIName
が誤っていました。
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で取得した音声がぐちゃぐちゃ
下記のようにソートしてあげないと、順番がぐちゃぐちゃでした笑
// listFragmentsで取得されるFragmentは順不同なのでProducerTimestampで昇順に並び替える
listFragments.Fragments.sort(function(a, b){
return (a.ProducerTimestamp > b.ProducerTimestamp ? 1 : -1);
});