質問編集履歴
26
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -152,4 +152,3 @@
|
|
152
152
|
}
|
153
153
|
}
|
154
154
|
}
|
155
|
-
|
25
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -99,15 +99,15 @@
|
|
99
99
|
|
100
100
|
// Decoderの初期化
|
101
101
|
let chunks = [];
|
102
|
-
let decoder = function (data,
|
102
|
+
let decoder = function (data, fragmentNumber) {}; // デコーダー関数の宣言
|
103
103
|
|
104
104
|
decoderOn(); // decoderOn関数の呼び出し
|
105
105
|
|
106
106
|
// デコーダー関数
|
107
|
-
function
|
107
|
+
decoder = function (data, fragmentNumber) {
|
108
108
|
// ここでデコード処理を行う
|
109
109
|
return data.Payload; // 今回はデータそのものを返す
|
110
|
-
}
|
110
|
+
};
|
111
111
|
|
112
112
|
function decoderOn() {
|
113
113
|
// デコーダーのオン/オフの設定
|
@@ -153,21 +153,3 @@
|
|
153
153
|
}
|
154
154
|
}
|
155
155
|
|
156
|
-
|
157
|
-
// Decoderの初期化
|
158
|
-
let chunks = [];
|
159
|
-
let decoder = function (data, fragmentNumber) {}; // デコーダー関数の宣言
|
160
|
-
|
161
|
-
decoderOn(); // decoderOn関数の呼び出し
|
162
|
-
|
163
|
-
// デコーダー関数
|
164
|
-
decoder = function (data, fragmentNumber) {
|
165
|
-
// ここでデコード処理を行う
|
166
|
-
return data.Payload; // 今回はデータそのものを返す
|
167
|
-
};
|
168
|
-
|
169
|
-
function decoderOn() {
|
170
|
-
// デコーダーのオン/オフの設定
|
171
|
-
}
|
172
|
-
|
173
|
-
|
24
修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -153,3 +153,21 @@
|
|
153
153
|
}
|
154
154
|
}
|
155
155
|
|
156
|
+
|
157
|
+
// Decoderの初期化
|
158
|
+
let chunks = [];
|
159
|
+
let decoder = function (data, fragmentNumber) {}; // デコーダー関数の宣言
|
160
|
+
|
161
|
+
decoderOn(); // decoderOn関数の呼び出し
|
162
|
+
|
163
|
+
// デコーダー関数
|
164
|
+
decoder = function (data, fragmentNumber) {
|
165
|
+
// ここでデコード処理を行う
|
166
|
+
return data.Payload; // 今回はデータそのものを返す
|
167
|
+
};
|
168
|
+
|
169
|
+
function decoderOn() {
|
170
|
+
// デコーダーのオン/オフの設定
|
171
|
+
}
|
172
|
+
|
173
|
+
|
23
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -4,4 +4,152 @@
|
|
4
4
|
|
5
5
|
-------------------------Node.js 18.xコード---------------------------------------------------
|
6
6
|
```
|
7
|
+
import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
8
|
+
import { KinesisVideoClient, GetDataEndpointCommand } from "@aws-sdk/client-kinesis-video";
|
9
|
+
import { KinesisVideoMediaClient, GetMediaCommand } from "@aws-sdk/client-kinesis-video-media";
|
7
10
|
|
11
|
+
// リージョンやバケット名の設定
|
12
|
+
const region = 'ap-northeast-1';
|
13
|
+
const putKey = 'voice-message/wav/';
|
14
|
+
const bucketName = 'testname-pjcode';
|
15
|
+
|
16
|
+
// Lambda関数のエントリーポイント
|
17
|
+
export const handler = async (event) => {
|
18
|
+
// S3クライアントを作成
|
19
|
+
const s3Client = new S3Client({ region: region });
|
20
|
+
|
21
|
+
// イベントの各レコードに対して処理を実行
|
22
|
+
for (let record of event.Records) {
|
23
|
+
// information/yyyymmdd_×××をgetKeyに格納
|
24
|
+
const getKey = record.s3.object.key;
|
25
|
+
// '/'までの文字数10がindexに格納
|
26
|
+
let index = getKey.lastIndexOf('/');
|
27
|
+
let fileName = 'noname';
|
28
|
+
|
29
|
+
if (index >= 0) {
|
30
|
+
// getKeyに対して、文字数11以降("/")の文字列を取得
|
31
|
+
fileName = getKey.substr(index + 1);
|
32
|
+
}
|
33
|
+
|
34
|
+
// S3から録音データに関する情報を取得
|
35
|
+
const s3GetCommand = new GetObjectCommand({
|
36
|
+
Bucket: bucketName,
|
37
|
+
Key: getKey
|
38
|
+
});
|
39
|
+
|
40
|
+
const s3GetRes = await s3Client.send(s3GetCommand);
|
41
|
+
const info = await s3GetRes.Body.transformToString();
|
42
|
+
const infoStr = JSON.parse(info);
|
43
|
+
const streamName = infoStr.streamARN.split('stream/')[1].split('/')[0];
|
44
|
+
const fragmentNumber = infoStr.startFragmentNumber;
|
45
|
+
const streamARN = infoStr.streamARN;
|
46
|
+
|
47
|
+
// Kinesis Video Media Clientを作成して、録音データを取得
|
48
|
+
const kvmClient = new KinesisVideoMediaClient({ region : region });
|
49
|
+
const kvmInput = {
|
50
|
+
StreamName: streamName,
|
51
|
+
StreamARN: streamARN,
|
52
|
+
StartSelector: {
|
53
|
+
StartSelectorType: 'FRAGMENT_NUMBER',
|
54
|
+
AfterFragmentNumber: fragmentNumber,
|
55
|
+
},
|
56
|
+
};
|
57
|
+
|
58
|
+
const kvmCommand = new GetMediaCommand(kvmInput);
|
59
|
+
const data = await kvmClient.send(kvmCommand);
|
60
|
+
|
61
|
+
// デコードされたデータをWAV形式に変換
|
62
|
+
const wav = Converter.createWav(data.Payload, 8000);
|
63
|
+
|
64
|
+
// WAVファイルをS3に保存
|
65
|
+
let tagging = ''; // 付加情報をタグに追加する
|
66
|
+
tagging += "customerEndpoint=" + infoStr.customerEndpoint + '&';
|
67
|
+
tagging += "systemEndpoint=" + infoStr.systemEndpoint + '&';
|
68
|
+
tagging += "startTimestamp=" + infoStr.startTimestamp;
|
69
|
+
|
70
|
+
const wavInput = new PutObjectCommand({
|
71
|
+
Bucket: bucketName,
|
72
|
+
Key: putKey + createKeyName() + fileName + '.wav',
|
73
|
+
Body: Buffer.from(wav.buffer),
|
74
|
+
Tagging: tagging
|
75
|
+
});
|
76
|
+
|
77
|
+
const wavRes = await s3Client.send(wavInput);
|
78
|
+
}
|
79
|
+
};
|
80
|
+
|
81
|
+
// S3バケット保存時のオブジェクト名を生成
|
82
|
+
function createKeyName() {
|
83
|
+
const date = new Date();
|
84
|
+
const year = date.getFullYear();
|
85
|
+
const mon = (date.getMonth() + 1);
|
86
|
+
const day = date.getDate();
|
87
|
+
const hour = date.getHours();
|
88
|
+
|
89
|
+
const space = (n) => {
|
90
|
+
return ('0' + (n)).slice(-2);
|
91
|
+
};
|
92
|
+
|
93
|
+
let result = year + '/';
|
94
|
+
result += space(mon) + '/';
|
95
|
+
result += space(day) + '/';
|
96
|
+
result += space(hour) + '/';
|
97
|
+
return result;
|
98
|
+
}
|
99
|
+
|
100
|
+
// Decoderの初期化
|
101
|
+
let chunks = [];
|
102
|
+
let decoder = function (data, chunk) {}; // 空のデコーダー関数
|
103
|
+
|
104
|
+
decoderOn(); // decoderOn関数の呼び出し
|
105
|
+
|
106
|
+
// デコーダー関数
|
107
|
+
function decoder(data, fragmentNumber) {
|
108
|
+
// ここでデコード処理を行う
|
109
|
+
return data.Payload; // 今回はデータそのものを返す
|
110
|
+
}
|
111
|
+
|
112
|
+
function decoderOn() {
|
113
|
+
// デコーダーのオン/オフの設定
|
114
|
+
}
|
115
|
+
|
116
|
+
class Converter {
|
117
|
+
// WAVファイルの生成
|
118
|
+
static createWav(samples, sampleRate) {
|
119
|
+
const len = samples.length;
|
120
|
+
const view = new DataView(new ArrayBuffer(44 + len));
|
121
|
+
this._writeString(view, 0, 'RIFF');
|
122
|
+
view.setUint32(4, 36 + len, true); // RIFFのサイズを修正
|
123
|
+
this._writeString(view, 8, 'WAVE');
|
124
|
+
this._writeString(view, 12, 'fmt ');
|
125
|
+
view.setUint32(16, 16, true);
|
126
|
+
view.setUint16(20, 1, true); // リニアPCM
|
127
|
+
view.setUint16(22, 1, true); // モノラル
|
128
|
+
view.setUint32(24, sampleRate, true);
|
129
|
+
view.setUint32(28, sampleRate * 2, true);
|
130
|
+
view.setUint16(32, 2, true);
|
131
|
+
view.setUint16(34, 16, true);
|
132
|
+
this._writeString(view, 36, 'data');
|
133
|
+
view.setUint32(40, len, true);
|
134
|
+
|
135
|
+
for (let i = 0; i < len; i++) {
|
136
|
+
view.setUint8(44 + i, samples[i]);
|
137
|
+
}
|
138
|
+
|
139
|
+
return view;
|
140
|
+
}
|
141
|
+
|
142
|
+
static _writeString(view, offset, string) {
|
143
|
+
if (offset + string.length > view.byteLength) {
|
144
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
145
|
+
}
|
146
|
+
for (let i = 0; i < string.length; i++) {
|
147
|
+
if (offset + i < view.byteLength) {
|
148
|
+
view.setUint8(offset + i, string.charCodeAt(i));
|
149
|
+
} else {
|
150
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
22
修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -4,148 +4,4 @@
|
|
4
4
|
|
5
5
|
-------------------------Node.js 18.xコード---------------------------------------------------
|
6
6
|
```
|
7
|
-
import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
8
|
-
import { KinesisVideoClient, GetDataEndpointCommand } from "@aws-sdk/client-kinesis-video";
|
9
|
-
import { KinesisVideoMediaClient, GetMediaCommand } from "@aws-sdk/client-kinesis-video-media";
|
10
7
|
|
11
|
-
// リージョンやバケット名の設定
|
12
|
-
const region = 'ap-northeast-1';
|
13
|
-
const putKey = 'voice-message/wav/';
|
14
|
-
const bucketName = 'testname-pjcode';
|
15
|
-
|
16
|
-
// Lambda関数のエントリーポイント
|
17
|
-
export const handler = async (event) => {
|
18
|
-
// S3クライアントを作成
|
19
|
-
const s3Client = new S3Client({ region: region });
|
20
|
-
|
21
|
-
// イベントの各レコードに対して処理を実行
|
22
|
-
for (let record of event.Records) {
|
23
|
-
// information/yyyymmdd_×××をgetKeyに格納
|
24
|
-
const getKey = record.s3.object.key;
|
25
|
-
// '/'までの文字数10がindexに格納
|
26
|
-
let index = getKey.lastIndexOf('/');
|
27
|
-
let fileName = 'noname';
|
28
|
-
|
29
|
-
if (index >= 0) {
|
30
|
-
// getKeyに対して、文字数11以降("/")の文字列を取得
|
31
|
-
fileName = getKey.substr(index + 1);
|
32
|
-
}
|
33
|
-
|
34
|
-
// S3から録音データに関する情報を取得
|
35
|
-
const s3GetCommand = new GetObjectCommand({
|
36
|
-
Bucket: bucketName,
|
37
|
-
Key: getKey
|
38
|
-
});
|
39
|
-
|
40
|
-
const s3GetRes = await s3Client.send(s3GetCommand);
|
41
|
-
const info = await s3GetRes.Body.transformToString();
|
42
|
-
const infoStr = JSON.parse(info);
|
43
|
-
const streamName = infoStr.streamARN.split('stream/')[1].split('/')[0];
|
44
|
-
const fragmentNumber = infoStr.startFragmentNumber;
|
45
|
-
const streamARN = infoStr.streamARN;
|
46
|
-
|
47
|
-
// Kinesis Video Media Clientを作成して、録音データを取得
|
48
|
-
const kvmClient = new KinesisVideoMediaClient({ region : region });
|
49
|
-
const kvmInput = {
|
50
|
-
StreamName: streamName,
|
51
|
-
StreamARN: streamARN,
|
52
|
-
StartSelector: {
|
53
|
-
StartSelectorType: 'FRAGMENT_NUMBER',
|
54
|
-
AfterFragmentNumber: fragmentNumber,
|
55
|
-
},
|
56
|
-
};
|
57
|
-
|
58
|
-
const kvmCommand = new GetMediaCommand(kvmInput);
|
59
|
-
const data = await kvmClient.send(kvmCommand);
|
60
|
-
|
61
|
-
// デコードされたデータをWAV形式に変換
|
62
|
-
const wav = Converter.createWav(data.Payload, 8000);
|
63
|
-
|
64
|
-
// WAVファイルをS3に保存
|
65
|
-
let tagging = ''; // 付加情報をタグに追加する
|
66
|
-
tagging += "customerEndpoint=" + infoStr.customerEndpoint + '&';
|
67
|
-
tagging += "systemEndpoint=" + infoStr.systemEndpoint + '&';
|
68
|
-
tagging += "startTimestamp=" + infoStr.startTimestamp;
|
69
|
-
|
70
|
-
const wavInput = new PutObjectCommand({
|
71
|
-
Bucket: bucketName,
|
72
|
-
Key: putKey + createKeyName() + fileName + '.wav',
|
73
|
-
Body: Buffer.from(wav.buffer),
|
74
|
-
Tagging: tagging
|
75
|
-
});
|
76
|
-
|
77
|
-
const wavRes = await s3Client.send(wavInput);
|
78
|
-
}
|
79
|
-
};
|
80
|
-
|
81
|
-
// S3バケット保存時のオブジェクト名を生成
|
82
|
-
function createKeyName() {
|
83
|
-
const date = new Date();
|
84
|
-
const year = date.getFullYear();
|
85
|
-
const mon = (date.getMonth() + 1);
|
86
|
-
const day = date.getDate();
|
87
|
-
const hour = date.getHours();
|
88
|
-
|
89
|
-
const space = (n) => {
|
90
|
-
return ('0' + (n)).slice(-2);
|
91
|
-
};
|
92
|
-
|
93
|
-
let result = year + '/';
|
94
|
-
result += space(mon) + '/';
|
95
|
-
result += space(day) + '/';
|
96
|
-
result += space(hour) + '/';
|
97
|
-
return result;
|
98
|
-
}
|
99
|
-
|
100
|
-
// Decoderの初期化
|
101
|
-
let chunks = [];
|
102
|
-
let decoder = function (data, chunk) {}; // 空のデコーダー関数
|
103
|
-
|
104
|
-
decoderOn();
|
105
|
-
|
106
|
-
// デコーダー関数
|
107
|
-
let decoder = function (data, fragmentNumber) {
|
108
|
-
// ここでデコード処理を行う
|
109
|
-
return data.Payload; // 今回はデータそのものを返す
|
110
|
-
};
|
111
|
-
|
112
|
-
class Converter {
|
113
|
-
// WAVファイルの生成
|
114
|
-
static createWav(samples, sampleRate) {
|
115
|
-
const len = samples.length;
|
116
|
-
const view = new DataView(new ArrayBuffer(44 + len));
|
117
|
-
this._writeString(view, 0, 'RIFF');
|
118
|
-
view.setUint32(4, 36 + len, true); // RIFFのサイズを修正
|
119
|
-
this._writeString(view, 8, 'WAVE');
|
120
|
-
this._writeString(view, 12, 'fmt ');
|
121
|
-
view.setUint32(16, 16, true);
|
122
|
-
view.setUint16(20, 1, true); // リニアPCM
|
123
|
-
view.setUint16(22, 1, true); // モノラル
|
124
|
-
view.setUint32(24, sampleRate, true);
|
125
|
-
view.setUint32(28, sampleRate * 2, true);
|
126
|
-
view.setUint16(32, 2, true);
|
127
|
-
view.setUint16(34, 16, true);
|
128
|
-
this._writeString(view, 36, 'data');
|
129
|
-
view.setUint32(40, len, true);
|
130
|
-
|
131
|
-
for (let i = 0; i < len; i++) {
|
132
|
-
view.setUint8(44 + i, samples[i]);
|
133
|
-
}
|
134
|
-
|
135
|
-
return view;
|
136
|
-
}
|
137
|
-
|
138
|
-
static _writeString(view, offset, string) {
|
139
|
-
if (offset + string.length > view.byteLength) {
|
140
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
141
|
-
}
|
142
|
-
for (let i = 0; i < string.length; i++) {
|
143
|
-
if (offset + i < view.byteLength) {
|
144
|
-
view.setUint8(offset + i, string.charCodeAt(i));
|
145
|
-
} else {
|
146
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
147
|
-
}
|
148
|
-
}
|
149
|
-
}
|
150
|
-
}
|
151
|
-
|
21
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -104,14 +104,10 @@
|
|
104
104
|
decoderOn();
|
105
105
|
|
106
106
|
// デコーダー関数
|
107
|
-
function
|
107
|
+
let decoder = function (data, fragmentNumber) {
|
108
108
|
// ここでデコード処理を行う
|
109
109
|
return data.Payload; // 今回はデータそのものを返す
|
110
|
-
}
|
110
|
+
};
|
111
|
-
|
112
|
-
function decoderOn() {
|
113
|
-
// デコーダーのオン/オフの設定
|
114
|
-
}
|
115
111
|
|
116
112
|
class Converter {
|
117
113
|
// WAVファイルの生成
|
@@ -153,12 +149,3 @@
|
|
153
149
|
}
|
154
150
|
}
|
155
151
|
|
156
|
-
|
157
|
-
|
158
|
-
// デコーダー関数
|
159
|
-
let decoder = function (data, fragmentNumber) {
|
160
|
-
// ここでデコード処理を行う
|
161
|
-
return data.Payload; // 今回はデータそのものを返す
|
162
|
-
};
|
163
|
-
|
164
|
-
|
20
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -153,3 +153,12 @@
|
|
153
153
|
}
|
154
154
|
}
|
155
155
|
|
156
|
+
|
157
|
+
|
158
|
+
// デコーダー関数
|
159
|
+
let decoder = function (data, fragmentNumber) {
|
160
|
+
// ここでデコード処理を行う
|
161
|
+
return data.Payload; // 今回はデータそのものを返す
|
162
|
+
};
|
163
|
+
|
164
|
+
|
19
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -4,4 +4,152 @@
|
|
4
4
|
|
5
5
|
-------------------------Node.js 18.xコード---------------------------------------------------
|
6
6
|
```
|
7
|
+
import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
8
|
+
import { KinesisVideoClient, GetDataEndpointCommand } from "@aws-sdk/client-kinesis-video";
|
9
|
+
import { KinesisVideoMediaClient, GetMediaCommand } from "@aws-sdk/client-kinesis-video-media";
|
7
10
|
|
11
|
+
// リージョンやバケット名の設定
|
12
|
+
const region = 'ap-northeast-1';
|
13
|
+
const putKey = 'voice-message/wav/';
|
14
|
+
const bucketName = 'testname-pjcode';
|
15
|
+
|
16
|
+
// Lambda関数のエントリーポイント
|
17
|
+
export const handler = async (event) => {
|
18
|
+
// S3クライアントを作成
|
19
|
+
const s3Client = new S3Client({ region: region });
|
20
|
+
|
21
|
+
// イベントの各レコードに対して処理を実行
|
22
|
+
for (let record of event.Records) {
|
23
|
+
// information/yyyymmdd_×××をgetKeyに格納
|
24
|
+
const getKey = record.s3.object.key;
|
25
|
+
// '/'までの文字数10がindexに格納
|
26
|
+
let index = getKey.lastIndexOf('/');
|
27
|
+
let fileName = 'noname';
|
28
|
+
|
29
|
+
if (index >= 0) {
|
30
|
+
// getKeyに対して、文字数11以降("/")の文字列を取得
|
31
|
+
fileName = getKey.substr(index + 1);
|
32
|
+
}
|
33
|
+
|
34
|
+
// S3から録音データに関する情報を取得
|
35
|
+
const s3GetCommand = new GetObjectCommand({
|
36
|
+
Bucket: bucketName,
|
37
|
+
Key: getKey
|
38
|
+
});
|
39
|
+
|
40
|
+
const s3GetRes = await s3Client.send(s3GetCommand);
|
41
|
+
const info = await s3GetRes.Body.transformToString();
|
42
|
+
const infoStr = JSON.parse(info);
|
43
|
+
const streamName = infoStr.streamARN.split('stream/')[1].split('/')[0];
|
44
|
+
const fragmentNumber = infoStr.startFragmentNumber;
|
45
|
+
const streamARN = infoStr.streamARN;
|
46
|
+
|
47
|
+
// Kinesis Video Media Clientを作成して、録音データを取得
|
48
|
+
const kvmClient = new KinesisVideoMediaClient({ region : region });
|
49
|
+
const kvmInput = {
|
50
|
+
StreamName: streamName,
|
51
|
+
StreamARN: streamARN,
|
52
|
+
StartSelector: {
|
53
|
+
StartSelectorType: 'FRAGMENT_NUMBER',
|
54
|
+
AfterFragmentNumber: fragmentNumber,
|
55
|
+
},
|
56
|
+
};
|
57
|
+
|
58
|
+
const kvmCommand = new GetMediaCommand(kvmInput);
|
59
|
+
const data = await kvmClient.send(kvmCommand);
|
60
|
+
|
61
|
+
// デコードされたデータをWAV形式に変換
|
62
|
+
const wav = Converter.createWav(data.Payload, 8000);
|
63
|
+
|
64
|
+
// WAVファイルをS3に保存
|
65
|
+
let tagging = ''; // 付加情報をタグに追加する
|
66
|
+
tagging += "customerEndpoint=" + infoStr.customerEndpoint + '&';
|
67
|
+
tagging += "systemEndpoint=" + infoStr.systemEndpoint + '&';
|
68
|
+
tagging += "startTimestamp=" + infoStr.startTimestamp;
|
69
|
+
|
70
|
+
const wavInput = new PutObjectCommand({
|
71
|
+
Bucket: bucketName,
|
72
|
+
Key: putKey + createKeyName() + fileName + '.wav',
|
73
|
+
Body: Buffer.from(wav.buffer),
|
74
|
+
Tagging: tagging
|
75
|
+
});
|
76
|
+
|
77
|
+
const wavRes = await s3Client.send(wavInput);
|
78
|
+
}
|
79
|
+
};
|
80
|
+
|
81
|
+
// S3バケット保存時のオブジェクト名を生成
|
82
|
+
function createKeyName() {
|
83
|
+
const date = new Date();
|
84
|
+
const year = date.getFullYear();
|
85
|
+
const mon = (date.getMonth() + 1);
|
86
|
+
const day = date.getDate();
|
87
|
+
const hour = date.getHours();
|
88
|
+
|
89
|
+
const space = (n) => {
|
90
|
+
return ('0' + (n)).slice(-2);
|
91
|
+
};
|
92
|
+
|
93
|
+
let result = year + '/';
|
94
|
+
result += space(mon) + '/';
|
95
|
+
result += space(day) + '/';
|
96
|
+
result += space(hour) + '/';
|
97
|
+
return result;
|
98
|
+
}
|
99
|
+
|
100
|
+
// Decoderの初期化
|
101
|
+
let chunks = [];
|
102
|
+
let decoder = function (data, chunk) {}; // 空のデコーダー関数
|
103
|
+
|
104
|
+
decoderOn();
|
105
|
+
|
106
|
+
// デコーダー関数
|
107
|
+
function decoder(data, fragmentNumber) {
|
108
|
+
// ここでデコード処理を行う
|
109
|
+
return data.Payload; // 今回はデータそのものを返す
|
110
|
+
}
|
111
|
+
|
112
|
+
function decoderOn() {
|
113
|
+
// デコーダーのオン/オフの設定
|
114
|
+
}
|
115
|
+
|
116
|
+
class Converter {
|
117
|
+
// WAVファイルの生成
|
118
|
+
static createWav(samples, sampleRate) {
|
119
|
+
const len = samples.length;
|
120
|
+
const view = new DataView(new ArrayBuffer(44 + len));
|
121
|
+
this._writeString(view, 0, 'RIFF');
|
122
|
+
view.setUint32(4, 36 + len, true); // RIFFのサイズを修正
|
123
|
+
this._writeString(view, 8, 'WAVE');
|
124
|
+
this._writeString(view, 12, 'fmt ');
|
125
|
+
view.setUint32(16, 16, true);
|
126
|
+
view.setUint16(20, 1, true); // リニアPCM
|
127
|
+
view.setUint16(22, 1, true); // モノラル
|
128
|
+
view.setUint32(24, sampleRate, true);
|
129
|
+
view.setUint32(28, sampleRate * 2, true);
|
130
|
+
view.setUint16(32, 2, true);
|
131
|
+
view.setUint16(34, 16, true);
|
132
|
+
this._writeString(view, 36, 'data');
|
133
|
+
view.setUint32(40, len, true);
|
134
|
+
|
135
|
+
for (let i = 0; i < len; i++) {
|
136
|
+
view.setUint8(44 + i, samples[i]);
|
137
|
+
}
|
138
|
+
|
139
|
+
return view;
|
140
|
+
}
|
141
|
+
|
142
|
+
static _writeString(view, offset, string) {
|
143
|
+
if (offset + string.length > view.byteLength) {
|
144
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
145
|
+
}
|
146
|
+
for (let i = 0; i < string.length; i++) {
|
147
|
+
if (offset + i < view.byteLength) {
|
148
|
+
view.setUint8(offset + i, string.charCodeAt(i));
|
149
|
+
} else {
|
150
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
18
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -4,172 +4,4 @@
|
|
4
4
|
|
5
5
|
-------------------------Node.js 18.xコード---------------------------------------------------
|
6
6
|
```
|
7
|
-
import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
8
|
-
import { KinesisVideoClient, GetDataEndpointCommand } from "@aws-sdk/client-kinesis-video";
|
9
|
-
import { KinesisVideoMediaClient, GetMediaCommand } from "@aws-sdk/client-kinesis-video-media";
|
10
7
|
|
11
|
-
// リージョンやバケット名の設定
|
12
|
-
const region = 'ap-northeast-1';
|
13
|
-
const putKey = 'voice-message/wav/';
|
14
|
-
const bucketName = 'testname-pjcode';
|
15
|
-
|
16
|
-
// Lambda関数のエントリーポイント
|
17
|
-
export const handler = async (event) => {
|
18
|
-
// S3クライアントを作成
|
19
|
-
const s3Client = new S3Client({ region: region });
|
20
|
-
|
21
|
-
// イベントの各レコードに対して処理を実行
|
22
|
-
for (let record of event.Records) {
|
23
|
-
// information/yyyymmdd_×××をgetKeyに格納
|
24
|
-
const getKey = record.s3.object.key;
|
25
|
-
// '/'までの文字数10がindexに格納
|
26
|
-
let index = getKey.lastIndexOf('/');
|
27
|
-
let fileName = 'noname';
|
28
|
-
|
29
|
-
if (index >= 0) {
|
30
|
-
// getKeyに対して、文字数11以降("/")の文字列を取得
|
31
|
-
fileName = getKey.substr(index + 1);
|
32
|
-
}
|
33
|
-
|
34
|
-
// S3から録音データに関する情報を取得
|
35
|
-
const s3GetCommand = new GetObjectCommand({
|
36
|
-
Bucket: bucketName,
|
37
|
-
Key: getKey
|
38
|
-
});
|
39
|
-
|
40
|
-
const s3GetRes = await s3Client.send(s3GetCommand);
|
41
|
-
const info = await s3GetRes.Body.transformToString();
|
42
|
-
const infoStr = JSON.parse(info);
|
43
|
-
const streamName = infoStr.streamARN.split('stream/')[1].split('/')[0];
|
44
|
-
const fragmentNumber = infoStr.startFragmentNumber;
|
45
|
-
const streamARN = infoStr.streamARN;
|
46
|
-
|
47
|
-
// Kinesis Video Media Clientを作成して、録音データを取得
|
48
|
-
const kvmClient = new KinesisVideoMediaClient({ region : region });
|
49
|
-
const kvmInput = {
|
50
|
-
StreamName: streamName,
|
51
|
-
StreamARN: streamARN,
|
52
|
-
StartSelector: {
|
53
|
-
StartSelectorType: 'FRAGMENT_NUMBER',
|
54
|
-
AfterFragmentNumber: fragmentNumber,
|
55
|
-
},
|
56
|
-
};
|
57
|
-
|
58
|
-
const kvmCommand = new GetMediaCommand(kvmInput);
|
59
|
-
const data = await kvmClient.send(kvmCommand);
|
60
|
-
|
61
|
-
// デコードされたデータをWAV形式に変換
|
62
|
-
const wav = Converter.createWav(data.Payload, 8000);
|
63
|
-
|
64
|
-
// WAVファイルをS3に保存
|
65
|
-
let tagging = ''; // 付加情報をタグに追加する
|
66
|
-
tagging += "customerEndpoint=" + infoStr.customerEndpoint + '&';
|
67
|
-
tagging += "systemEndpoint=" + infoStr.systemEndpoint + '&';
|
68
|
-
tagging += "startTimestamp=" + infoStr.startTimestamp;
|
69
|
-
|
70
|
-
const wavInput = new PutObjectCommand({
|
71
|
-
Bucket: bucketName,
|
72
|
-
Key: putKey + createKeyName() + fileName + '.wav',
|
73
|
-
Body: Buffer.from(wav.buffer),
|
74
|
-
Tagging: tagging
|
75
|
-
});
|
76
|
-
|
77
|
-
const wavRes = await s3Client.send(wavInput);
|
78
|
-
}
|
79
|
-
};
|
80
|
-
|
81
|
-
// S3バケット保存時のオブジェクト名を生成
|
82
|
-
function createKeyName() {
|
83
|
-
const date = new Date();
|
84
|
-
const year = date.getFullYear();
|
85
|
-
const mon = (date.getMonth() + 1);
|
86
|
-
const day = date.getDate();
|
87
|
-
const hour = date.getHours();
|
88
|
-
|
89
|
-
const space = (n) => {
|
90
|
-
return ('0' + (n)).slice(-2);
|
91
|
-
};
|
92
|
-
|
93
|
-
let result = year + '/';
|
94
|
-
result += space(mon) + '/';
|
95
|
-
result += space(day) + '/';
|
96
|
-
result += space(hour) + '/';
|
97
|
-
return result;
|
98
|
-
}
|
99
|
-
|
100
|
-
// Decoderの初期化
|
101
|
-
let chunks = [];
|
102
|
-
let decoder = function (data, chunk) {}; // 空のデコーダー関数
|
103
|
-
|
104
|
-
decoderOn();
|
105
|
-
|
106
|
-
// デコーダー関数
|
107
|
-
function decoder(data, fragmentNumber) {
|
108
|
-
// ここでデコード処理を行う
|
109
|
-
return data.Payload; // 今回はデータそのものを返す
|
110
|
-
}
|
111
|
-
|
112
|
-
function decoderOn() {
|
113
|
-
// デコーダーのオン/オフの設定
|
114
|
-
}
|
115
|
-
|
116
|
-
class Converter {
|
117
|
-
// WAVファイルの生成
|
118
|
-
static createWav(samples, sampleRate) {
|
119
|
-
const len = samples.length;
|
120
|
-
const view = new DataView(new ArrayBuffer(44 + len));
|
121
|
-
this._writeString(view, 0, 'RIFF');
|
122
|
-
view.setUint32(4, 36 + len, true); // RIFFのサイズを修正
|
123
|
-
this._writeString(view, 8, 'WAVE');
|
124
|
-
this._writeString(view, 12, 'fmt ');
|
125
|
-
view.setUint32(16, 16, true);
|
126
|
-
view.setUint16(20, 1, true); // リニアPCM
|
127
|
-
view.setUint16(22, 1, true); // モノラル
|
128
|
-
view.setUint32(24, sampleRate, true);
|
129
|
-
view.setUint32(28, sampleRate * 2, true);
|
130
|
-
view.setUint16(32, 2, true);
|
131
|
-
view.setUint16(34, 16, true);
|
132
|
-
this._writeString(view, 36, 'data');
|
133
|
-
view.setUint32(40, len, true);
|
134
|
-
|
135
|
-
for (let i = 0; i < len; i++) {
|
136
|
-
view.setUint8(44 + i, samples[i]);
|
137
|
-
}
|
138
|
-
|
139
|
-
return view;
|
140
|
-
}
|
141
|
-
|
142
|
-
static _writeString(view, offset, string) {
|
143
|
-
if (offset + string.length > view.byteLength) {
|
144
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
145
|
-
}
|
146
|
-
for (let i = 0; i < string.length; i++) {
|
147
|
-
if (offset + i < view.byteLength) {
|
148
|
-
view.setUint8(offset + i, string.charCodeAt(i));
|
149
|
-
} else {
|
150
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
151
|
-
}
|
152
|
-
}
|
153
|
-
}
|
154
|
-
}
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
static _writeString(view, offset, string) {
|
159
|
-
if (offset + string.length > view.byteLength) {
|
160
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
161
|
-
}
|
162
|
-
for (let i = 0; i < string.length; i++) {
|
163
|
-
if (offset + i < view.byteLength) {
|
164
|
-
view.setUint8(offset + i, string.charCodeAt(i));
|
165
|
-
} else {
|
166
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
167
|
-
}
|
168
|
-
}
|
169
|
-
}
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
17
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -160,17 +160,6 @@
|
|
160
160
|
throw new RangeError('Offset is outside the bounds of the DataView');
|
161
161
|
}
|
162
162
|
for (let i = 0; i < string.length; i++) {
|
163
|
-
view.setUint8(offset + i, string.charCodeAt(i));
|
164
|
-
}
|
165
|
-
}
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
static _writeString(view, offset, string) {
|
170
|
-
if (offset + string.length > view.byteLength) {
|
171
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
172
|
-
}
|
173
|
-
for (let i = 0; i < string.length; i++) {
|
174
163
|
if (offset + i < view.byteLength) {
|
175
164
|
view.setUint8(offset + i, string.charCodeAt(i));
|
176
165
|
} else {
|
16
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -165,3 +165,22 @@
|
|
165
165
|
}
|
166
166
|
|
167
167
|
|
168
|
+
|
169
|
+
static _writeString(view, offset, string) {
|
170
|
+
if (offset + string.length > view.byteLength) {
|
171
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
172
|
+
}
|
173
|
+
for (let i = 0; i < string.length; i++) {
|
174
|
+
if (offset + i < view.byteLength) {
|
175
|
+
view.setUint8(offset + i, string.charCodeAt(i));
|
176
|
+
} else {
|
177
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
|
183
|
+
|
184
|
+
|
185
|
+
|
186
|
+
|
15
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -153,3 +153,15 @@
|
|
153
153
|
}
|
154
154
|
}
|
155
155
|
|
156
|
+
|
157
|
+
|
158
|
+
static _writeString(view, offset, string) {
|
159
|
+
if (offset + string.length > view.byteLength) {
|
160
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
161
|
+
}
|
162
|
+
for (let i = 0; i < string.length; i++) {
|
163
|
+
view.setUint8(offset + i, string.charCodeAt(i));
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
|
14
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -153,35 +153,3 @@
|
|
153
153
|
}
|
154
154
|
}
|
155
155
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
static createWav(samples, sampleRate) {
|
164
|
-
const len = samples.length;
|
165
|
-
const view = new DataView(new ArrayBuffer(44 + len));
|
166
|
-
this._writeString(view, 0, 'RIFF');
|
167
|
-
view.setUint32(4, 36 + len, true); // RIFFのサイズを修正
|
168
|
-
this._writeString(view, 8, 'WAVE');
|
169
|
-
this._writeString(view, 12, 'fmt ');
|
170
|
-
view.setUint32(16, 16, true);
|
171
|
-
view.setUint16(20, 1, true); // リニアPCM
|
172
|
-
view.setUint16(22, 1, true); // モノラル
|
173
|
-
view.setUint32(24, sampleRate, true);
|
174
|
-
view.setUint32(28, sampleRate * 2, true);
|
175
|
-
view.setUint16(32, 2, true);
|
176
|
-
view.setUint16(34, 16, true);
|
177
|
-
this._writeString(view, 36, 'data');
|
178
|
-
view.setUint32(40, len, true);
|
179
|
-
|
180
|
-
for (let i = 0; i < len; i++) {
|
181
|
-
view.setUint8(44 + i, samples[i]);
|
182
|
-
}
|
183
|
-
|
184
|
-
return view;
|
185
|
-
}
|
186
|
-
|
187
|
-
|
13
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -152,3 +152,36 @@
|
|
152
152
|
}
|
153
153
|
}
|
154
154
|
}
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
|
159
|
+
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
static createWav(samples, sampleRate) {
|
164
|
+
const len = samples.length;
|
165
|
+
const view = new DataView(new ArrayBuffer(44 + len));
|
166
|
+
this._writeString(view, 0, 'RIFF');
|
167
|
+
view.setUint32(4, 36 + len, true); // RIFFのサイズを修正
|
168
|
+
this._writeString(view, 8, 'WAVE');
|
169
|
+
this._writeString(view, 12, 'fmt ');
|
170
|
+
view.setUint32(16, 16, true);
|
171
|
+
view.setUint16(20, 1, true); // リニアPCM
|
172
|
+
view.setUint16(22, 1, true); // モノラル
|
173
|
+
view.setUint32(24, sampleRate, true);
|
174
|
+
view.setUint32(28, sampleRate * 2, true);
|
175
|
+
view.setUint16(32, 2, true);
|
176
|
+
view.setUint16(34, 16, true);
|
177
|
+
this._writeString(view, 36, 'data');
|
178
|
+
view.setUint32(40, len, true);
|
179
|
+
|
180
|
+
for (let i = 0; i < len; i++) {
|
181
|
+
view.setUint8(44 + i, samples[i]);
|
182
|
+
}
|
183
|
+
|
184
|
+
return view;
|
185
|
+
}
|
186
|
+
|
187
|
+
|
12
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -115,52 +115,6 @@
|
|
115
115
|
|
116
116
|
class Converter {
|
117
117
|
// WAVファイルの生成
|
118
|
-
static createWav(samples, sampleRate) {
|
119
|
-
const len = samples.byteLength;
|
120
|
-
const view = new DataView(new ArrayBuffer(44 + len));
|
121
|
-
this._writeString(view, 0, 'RIFF');
|
122
|
-
view.setUint32(4, 36 + len, true); // RIFFのサイズを修正
|
123
|
-
this._writeString(view, 8, 'WAVE');
|
124
|
-
this._writeString(view, 12, 'fmt ');
|
125
|
-
view.setUint32(16, 16, true);
|
126
|
-
view.setUint16(20, 1, true); // リニアPCM
|
127
|
-
view.setUint16(22, 1, true); // モノラル
|
128
|
-
view.setUint32(24, sampleRate, true);
|
129
|
-
view.setUint32(28, sampleRate * 2, true);
|
130
|
-
view.setUint16(32, 2, true);
|
131
|
-
view.setUint16(34, 16, true);
|
132
|
-
this._writeString(view, 36, 'data');
|
133
|
-
view.setUint32(40, len, true);
|
134
|
-
|
135
|
-
const srcView = new DataView(samples);
|
136
|
-
let offset = 44;
|
137
|
-
for (let i = 0; i < len; i++) {
|
138
|
-
if (offset + i >= view.byteLength) {
|
139
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
140
|
-
}
|
141
|
-
view.setUint8(offset + i, srcView.getUint8(i));
|
142
|
-
}
|
143
|
-
|
144
|
-
return view;
|
145
|
-
}
|
146
|
-
|
147
|
-
static _writeString(view, offset, string) {
|
148
|
-
if (offset + string.length > view.byteLength) {
|
149
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
150
|
-
}
|
151
|
-
for (let i = 0; i < string.length; i++) {
|
152
|
-
if (offset + i < view.byteLength) {
|
153
|
-
view.setUint8(offset + i, string.charCodeAt(i));
|
154
|
-
} else {
|
155
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
156
|
-
}
|
157
|
-
}
|
158
|
-
}
|
159
|
-
}
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
118
|
static createWav(samples, sampleRate) {
|
165
119
|
const len = samples.length;
|
166
120
|
const view = new DataView(new ArrayBuffer(44 + len));
|
@@ -185,3 +139,16 @@
|
|
185
139
|
return view;
|
186
140
|
}
|
187
141
|
|
142
|
+
static _writeString(view, offset, string) {
|
143
|
+
if (offset + string.length > view.byteLength) {
|
144
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
145
|
+
}
|
146
|
+
for (let i = 0; i < string.length; i++) {
|
147
|
+
if (offset + i < view.byteLength) {
|
148
|
+
view.setUint8(offset + i, string.charCodeAt(i));
|
149
|
+
} else {
|
150
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
11
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -158,3 +158,30 @@
|
|
158
158
|
}
|
159
159
|
}
|
160
160
|
|
161
|
+
|
162
|
+
|
163
|
+
|
164
|
+
static createWav(samples, sampleRate) {
|
165
|
+
const len = samples.length;
|
166
|
+
const view = new DataView(new ArrayBuffer(44 + len));
|
167
|
+
this._writeString(view, 0, 'RIFF');
|
168
|
+
view.setUint32(4, 36 + len, true); // RIFFのサイズを修正
|
169
|
+
this._writeString(view, 8, 'WAVE');
|
170
|
+
this._writeString(view, 12, 'fmt ');
|
171
|
+
view.setUint32(16, 16, true);
|
172
|
+
view.setUint16(20, 1, true); // リニアPCM
|
173
|
+
view.setUint16(22, 1, true); // モノラル
|
174
|
+
view.setUint32(24, sampleRate, true);
|
175
|
+
view.setUint32(28, sampleRate * 2, true);
|
176
|
+
view.setUint16(32, 2, true);
|
177
|
+
view.setUint16(34, 16, true);
|
178
|
+
this._writeString(view, 36, 'data');
|
179
|
+
view.setUint32(40, len, true);
|
180
|
+
|
181
|
+
for (let i = 0; i < len; i++) {
|
182
|
+
view.setUint8(44 + i, samples[i]);
|
183
|
+
}
|
184
|
+
|
185
|
+
return view;
|
186
|
+
}
|
187
|
+
|
10
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -4,5 +4,157 @@
|
|
4
4
|
|
5
5
|
-------------------------Node.js 18.xコード---------------------------------------------------
|
6
6
|
```
|
7
|
+
import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
8
|
+
import { KinesisVideoClient, GetDataEndpointCommand } from "@aws-sdk/client-kinesis-video";
|
9
|
+
import { KinesisVideoMediaClient, GetMediaCommand } from "@aws-sdk/client-kinesis-video-media";
|
7
10
|
|
11
|
+
// リージョンやバケット名の設定
|
12
|
+
const region = 'ap-northeast-1';
|
13
|
+
const putKey = 'voice-message/wav/';
|
14
|
+
const bucketName = 'testname-pjcode';
|
8
15
|
|
16
|
+
// Lambda関数のエントリーポイント
|
17
|
+
export const handler = async (event) => {
|
18
|
+
// S3クライアントを作成
|
19
|
+
const s3Client = new S3Client({ region: region });
|
20
|
+
|
21
|
+
// イベントの各レコードに対して処理を実行
|
22
|
+
for (let record of event.Records) {
|
23
|
+
// information/yyyymmdd_×××をgetKeyに格納
|
24
|
+
const getKey = record.s3.object.key;
|
25
|
+
// '/'までの文字数10がindexに格納
|
26
|
+
let index = getKey.lastIndexOf('/');
|
27
|
+
let fileName = 'noname';
|
28
|
+
|
29
|
+
if (index >= 0) {
|
30
|
+
// getKeyに対して、文字数11以降("/")の文字列を取得
|
31
|
+
fileName = getKey.substr(index + 1);
|
32
|
+
}
|
33
|
+
|
34
|
+
// S3から録音データに関する情報を取得
|
35
|
+
const s3GetCommand = new GetObjectCommand({
|
36
|
+
Bucket: bucketName,
|
37
|
+
Key: getKey
|
38
|
+
});
|
39
|
+
|
40
|
+
const s3GetRes = await s3Client.send(s3GetCommand);
|
41
|
+
const info = await s3GetRes.Body.transformToString();
|
42
|
+
const infoStr = JSON.parse(info);
|
43
|
+
const streamName = infoStr.streamARN.split('stream/')[1].split('/')[0];
|
44
|
+
const fragmentNumber = infoStr.startFragmentNumber;
|
45
|
+
const streamARN = infoStr.streamARN;
|
46
|
+
|
47
|
+
// Kinesis Video Media Clientを作成して、録音データを取得
|
48
|
+
const kvmClient = new KinesisVideoMediaClient({ region : region });
|
49
|
+
const kvmInput = {
|
50
|
+
StreamName: streamName,
|
51
|
+
StreamARN: streamARN,
|
52
|
+
StartSelector: {
|
53
|
+
StartSelectorType: 'FRAGMENT_NUMBER',
|
54
|
+
AfterFragmentNumber: fragmentNumber,
|
55
|
+
},
|
56
|
+
};
|
57
|
+
|
58
|
+
const kvmCommand = new GetMediaCommand(kvmInput);
|
59
|
+
const data = await kvmClient.send(kvmCommand);
|
60
|
+
|
61
|
+
// デコードされたデータをWAV形式に変換
|
62
|
+
const wav = Converter.createWav(data.Payload, 8000);
|
63
|
+
|
64
|
+
// WAVファイルをS3に保存
|
65
|
+
let tagging = ''; // 付加情報をタグに追加する
|
66
|
+
tagging += "customerEndpoint=" + infoStr.customerEndpoint + '&';
|
67
|
+
tagging += "systemEndpoint=" + infoStr.systemEndpoint + '&';
|
68
|
+
tagging += "startTimestamp=" + infoStr.startTimestamp;
|
69
|
+
|
70
|
+
const wavInput = new PutObjectCommand({
|
71
|
+
Bucket: bucketName,
|
72
|
+
Key: putKey + createKeyName() + fileName + '.wav',
|
73
|
+
Body: Buffer.from(wav.buffer),
|
74
|
+
Tagging: tagging
|
75
|
+
});
|
76
|
+
|
77
|
+
const wavRes = await s3Client.send(wavInput);
|
78
|
+
}
|
79
|
+
};
|
80
|
+
|
81
|
+
// S3バケット保存時のオブジェクト名を生成
|
82
|
+
function createKeyName() {
|
83
|
+
const date = new Date();
|
84
|
+
const year = date.getFullYear();
|
85
|
+
const mon = (date.getMonth() + 1);
|
86
|
+
const day = date.getDate();
|
87
|
+
const hour = date.getHours();
|
88
|
+
|
89
|
+
const space = (n) => {
|
90
|
+
return ('0' + (n)).slice(-2);
|
91
|
+
};
|
92
|
+
|
93
|
+
let result = year + '/';
|
94
|
+
result += space(mon) + '/';
|
95
|
+
result += space(day) + '/';
|
96
|
+
result += space(hour) + '/';
|
97
|
+
return result;
|
98
|
+
}
|
99
|
+
|
100
|
+
// Decoderの初期化
|
101
|
+
let chunks = [];
|
102
|
+
let decoder = function (data, chunk) {}; // 空のデコーダー関数
|
103
|
+
|
104
|
+
decoderOn();
|
105
|
+
|
106
|
+
// デコーダー関数
|
107
|
+
function decoder(data, fragmentNumber) {
|
108
|
+
// ここでデコード処理を行う
|
109
|
+
return data.Payload; // 今回はデータそのものを返す
|
110
|
+
}
|
111
|
+
|
112
|
+
function decoderOn() {
|
113
|
+
// デコーダーのオン/オフの設定
|
114
|
+
}
|
115
|
+
|
116
|
+
class Converter {
|
117
|
+
// WAVファイルの生成
|
118
|
+
static createWav(samples, sampleRate) {
|
119
|
+
const len = samples.byteLength;
|
120
|
+
const view = new DataView(new ArrayBuffer(44 + len));
|
121
|
+
this._writeString(view, 0, 'RIFF');
|
122
|
+
view.setUint32(4, 36 + len, true); // RIFFのサイズを修正
|
123
|
+
this._writeString(view, 8, 'WAVE');
|
124
|
+
this._writeString(view, 12, 'fmt ');
|
125
|
+
view.setUint32(16, 16, true);
|
126
|
+
view.setUint16(20, 1, true); // リニアPCM
|
127
|
+
view.setUint16(22, 1, true); // モノラル
|
128
|
+
view.setUint32(24, sampleRate, true);
|
129
|
+
view.setUint32(28, sampleRate * 2, true);
|
130
|
+
view.setUint16(32, 2, true);
|
131
|
+
view.setUint16(34, 16, true);
|
132
|
+
this._writeString(view, 36, 'data');
|
133
|
+
view.setUint32(40, len, true);
|
134
|
+
|
135
|
+
const srcView = new DataView(samples);
|
136
|
+
let offset = 44;
|
137
|
+
for (let i = 0; i < len; i++) {
|
138
|
+
if (offset + i >= view.byteLength) {
|
139
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
140
|
+
}
|
141
|
+
view.setUint8(offset + i, srcView.getUint8(i));
|
142
|
+
}
|
143
|
+
|
144
|
+
return view;
|
145
|
+
}
|
146
|
+
|
147
|
+
static _writeString(view, offset, string) {
|
148
|
+
if (offset + string.length > view.byteLength) {
|
149
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
150
|
+
}
|
151
|
+
for (let i = 0; i < string.length; i++) {
|
152
|
+
if (offset + i < view.byteLength) {
|
153
|
+
view.setUint8(offset + i, string.charCodeAt(i));
|
154
|
+
} else {
|
155
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
156
|
+
}
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
9
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -4,157 +4,5 @@
|
|
4
4
|
|
5
5
|
-------------------------Node.js 18.xコード---------------------------------------------------
|
6
6
|
```
|
7
|
-
import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
8
|
-
import { KinesisVideoClient, GetDataEndpointCommand } from "@aws-sdk/client-kinesis-video";
|
9
|
-
import { KinesisVideoMediaClient, GetMediaCommand } from "@aws-sdk/client-kinesis-video-media";
|
10
7
|
|
11
|
-
// リージョンやバケット名の設定
|
12
|
-
const region = 'ap-northeast-1';
|
13
|
-
const putKey = 'voice-message/wav/';
|
14
|
-
const bucketName = 'testname-pjcode';
|
15
8
|
|
16
|
-
// Lambda関数のエントリーポイント
|
17
|
-
export const handler = async (event) => {
|
18
|
-
// S3クライアントを作成
|
19
|
-
const s3Client = new S3Client({ region: region });
|
20
|
-
|
21
|
-
// イベントの各レコードに対して処理を実行
|
22
|
-
for (let record of event.Records) {
|
23
|
-
// information/yyyymmdd_×××をgetKeyに格納
|
24
|
-
const getKey = record.s3.object.key;
|
25
|
-
// '/'までの文字数10がindexに格納
|
26
|
-
let index = getKey.lastIndexOf('/');
|
27
|
-
let fileName = 'noname';
|
28
|
-
|
29
|
-
if (index >= 0) {
|
30
|
-
// getKeyに対して、文字数11以降("/")の文字列を取得
|
31
|
-
fileName = getKey.substr(index + 1);
|
32
|
-
}
|
33
|
-
|
34
|
-
// S3から録音データに関する情報を取得
|
35
|
-
const s3GetCommand = new GetObjectCommand({
|
36
|
-
Bucket: bucketName,
|
37
|
-
Key: getKey
|
38
|
-
});
|
39
|
-
|
40
|
-
const s3GetRes = await s3Client.send(s3GetCommand);
|
41
|
-
const info = await s3GetRes.Body.transformToString();
|
42
|
-
const infoStr = JSON.parse(info);
|
43
|
-
const streamName = infoStr.streamARN.split('stream/')[1].split('/')[0];
|
44
|
-
const fragmentNumber = infoStr.startFragmentNumber;
|
45
|
-
const streamARN = infoStr.streamARN;
|
46
|
-
|
47
|
-
// Kinesis Video Media Clientを作成して、録音データを取得
|
48
|
-
const kvmClient = new KinesisVideoMediaClient({ region : region });
|
49
|
-
const kvmInput = {
|
50
|
-
StreamName: streamName,
|
51
|
-
StreamARN: streamARN,
|
52
|
-
StartSelector: {
|
53
|
-
StartSelectorType: 'FRAGMENT_NUMBER',
|
54
|
-
AfterFragmentNumber: fragmentNumber,
|
55
|
-
},
|
56
|
-
};
|
57
|
-
|
58
|
-
const kvmCommand = new GetMediaCommand(kvmInput);
|
59
|
-
const data = await kvmClient.send(kvmCommand);
|
60
|
-
|
61
|
-
// デコードされたデータをWAV形式に変換
|
62
|
-
const wav = Converter.createWav(data.Payload, 8000);
|
63
|
-
|
64
|
-
// WAVファイルをS3に保存
|
65
|
-
let tagging = ''; // 付加情報をタグに追加する
|
66
|
-
tagging += "customerEndpoint=" + infoStr.customerEndpoint + '&';
|
67
|
-
tagging += "systemEndpoint=" + infoStr.systemEndpoint + '&';
|
68
|
-
tagging += "startTimestamp=" + infoStr.startTimestamp;
|
69
|
-
|
70
|
-
const wavInput = new PutObjectCommand({
|
71
|
-
Bucket: bucketName,
|
72
|
-
Key: putKey + createKeyName() + fileName + '.wav',
|
73
|
-
Body: Buffer.from(wav.buffer),
|
74
|
-
Tagging: tagging
|
75
|
-
});
|
76
|
-
|
77
|
-
const wavRes = await s3Client.send(wavInput);
|
78
|
-
}
|
79
|
-
};
|
80
|
-
|
81
|
-
// S3バケット保存時のオブジェクト名を生成
|
82
|
-
function createKeyName() {
|
83
|
-
const date = new Date();
|
84
|
-
const year = date.getFullYear();
|
85
|
-
const mon = (date.getMonth() + 1);
|
86
|
-
const day = date.getDate();
|
87
|
-
const hour = date.getHours();
|
88
|
-
|
89
|
-
const space = (n) => {
|
90
|
-
return ('0' + (n)).slice(-2);
|
91
|
-
};
|
92
|
-
|
93
|
-
let result = year + '/';
|
94
|
-
result += space(mon) + '/';
|
95
|
-
result += space(day) + '/';
|
96
|
-
result += space(hour) + '/';
|
97
|
-
return result;
|
98
|
-
}
|
99
|
-
|
100
|
-
// Decoderの初期化
|
101
|
-
let chunks = [];
|
102
|
-
let decoder = function (data, chunk) {}; // 空のデコーダー関数
|
103
|
-
|
104
|
-
decoderOn();
|
105
|
-
|
106
|
-
// デコーダー関数
|
107
|
-
function decoder(data, fragmentNumber) {
|
108
|
-
// ここでデコード処理を行う
|
109
|
-
return data.Payload; // 今回はデータそのものを返す
|
110
|
-
}
|
111
|
-
|
112
|
-
function decoderOn() {
|
113
|
-
// デコーダーのオン/オフの設定
|
114
|
-
}
|
115
|
-
|
116
|
-
class Converter {
|
117
|
-
// WAVファイルの生成
|
118
|
-
static createWav(samples, sampleRate) {
|
119
|
-
const len = samples.byteLength;
|
120
|
-
const view = new DataView(new ArrayBuffer(44 + len));
|
121
|
-
this._writeString(view, 0, 'RIFF');
|
122
|
-
view.setUint32(4, 36 + len, true); // RIFFのサイズを修正
|
123
|
-
this._writeString(view, 8, 'WAVE');
|
124
|
-
this._writeString(view, 12, 'fmt ');
|
125
|
-
view.setUint32(16, 16, true);
|
126
|
-
view.setUint16(20, 1, true); // リニアPCM
|
127
|
-
view.setUint16(22, 1, true); // モノラル
|
128
|
-
view.setUint32(24, sampleRate, true);
|
129
|
-
view.setUint32(28, sampleRate * 2, true);
|
130
|
-
view.setUint16(32, 2, true);
|
131
|
-
view.setUint16(34, 16, true);
|
132
|
-
this._writeString(view, 36, 'data');
|
133
|
-
view.setUint32(40, len, true);
|
134
|
-
|
135
|
-
const srcView = new DataView(samples);
|
136
|
-
let offset = 44;
|
137
|
-
for (let i = 0; i < len; i++) {
|
138
|
-
if (offset + i >= view.byteLength) {
|
139
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
140
|
-
}
|
141
|
-
view.setUint8(offset + i, srcView.getUint8(i));
|
142
|
-
}
|
143
|
-
|
144
|
-
return view;
|
145
|
-
}
|
146
|
-
|
147
|
-
static _writeString(view, offset, string) {
|
148
|
-
if (offset + string.length > view.byteLength) {
|
149
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
150
|
-
}
|
151
|
-
for (let i = 0; i < string.length; i++) {
|
152
|
-
if (offset + i < view.byteLength) {
|
153
|
-
view.setUint8(offset + i, string.charCodeAt(i));
|
154
|
-
} else {
|
155
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
156
|
-
}
|
157
|
-
}
|
158
|
-
}
|
159
|
-
}
|
160
|
-
|
8
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -50,30 +50,28 @@
|
|
50
50
|
StreamName: streamName,
|
51
51
|
StreamARN: streamARN,
|
52
52
|
StartSelector: {
|
53
|
+
StartSelectorType: 'FRAGMENT_NUMBER',
|
53
|
-
|
54
|
+
AfterFragmentNumber: fragmentNumber,
|
54
55
|
},
|
55
56
|
};
|
56
57
|
|
57
58
|
const kvmCommand = new GetMediaCommand(kvmInput);
|
58
59
|
const data = await kvmClient.send(kvmCommand);
|
59
60
|
|
60
|
-
// Kinesis Video StreamsからRAWデータの取得
|
61
|
-
const raw = await getMedia(streamName, fragmentNumber);
|
62
|
-
|
63
|
-
//
|
61
|
+
// デコードされたデータをWAV形式に変換
|
64
|
-
const wav = Converter.createWav(
|
62
|
+
const wav = Converter.createWav(data.Payload, 8000);
|
65
63
|
|
66
64
|
// WAVファイルをS3に保存
|
67
65
|
let tagging = ''; // 付加情報をタグに追加する
|
68
|
-
tagging += "customerEndpoint=" + info.customerEndpoint + '&';
|
66
|
+
tagging += "customerEndpoint=" + infoStr.customerEndpoint + '&';
|
69
|
-
tagging += "systemEndpoint=" + info.systemEndpoint + '&';
|
67
|
+
tagging += "systemEndpoint=" + infoStr.systemEndpoint + '&';
|
70
|
-
tagging += "startTimestamp=" + info.startTimestamp;
|
68
|
+
tagging += "startTimestamp=" + infoStr.startTimestamp;
|
71
69
|
|
72
70
|
const wavInput = new PutObjectCommand({
|
73
71
|
Bucket: bucketName,
|
74
|
-
Key: putKey + createKeyName()
|
72
|
+
Key: putKey + createKeyName() + fileName + '.wav',
|
75
73
|
Body: Buffer.from(wav.buffer),
|
76
|
-
Tag: tagging
|
74
|
+
Tagging: tagging
|
77
75
|
});
|
78
76
|
|
79
77
|
const wavRes = await s3Client.send(wavInput);
|
@@ -101,58 +99,18 @@
|
|
101
99
|
|
102
100
|
// Decoderの初期化
|
103
101
|
let chunks = [];
|
104
|
-
let decoder = function () {};
|
105
|
-
function decoderOn() {
|
106
|
-
|
102
|
+
let decoder = function (data, chunk) {}; // 空のデコーダー関数
|
107
|
-
if(chunk[1].name == 'SimpleBlock'){
|
108
|
-
chunks.push(chunk[1].data);
|
109
|
-
}
|
110
|
-
|
111
|
-
for(let chunk of chunks){
|
112
|
-
if(chunk[1].name == 'SimpleBlock'){
|
113
|
-
chunks.push(chunk[1].data);
|
114
|
-
}
|
115
|
-
}
|
116
|
-
return chunk;
|
117
|
-
};
|
118
|
-
}
|
119
103
|
|
120
104
|
decoderOn();
|
121
105
|
|
122
|
-
//
|
106
|
+
// デコーダー関数
|
123
|
-
|
107
|
+
function decoder(data, fragmentNumber) {
|
124
|
-
let kvInput = {
|
125
|
-
APIName: "GET_MEDIA",
|
126
|
-
|
108
|
+
// ここでデコード処理を行う
|
109
|
+
return data.Payload; // 今回はデータそのものを返す
|
127
|
-
|
110
|
+
}
|
128
111
|
|
129
|
-
// Endpointの取得
|
130
|
-
const kvClient = new KinesisVideoClient({ region : region });
|
131
|
-
const kvCommand = new GetDataEndpointCommand(kvInput);
|
132
|
-
const end = await kvClient.send(kvCommand);
|
133
|
-
|
134
|
-
let kvmInput = {
|
135
|
-
|
112
|
+
function decoderOn() {
|
136
|
-
StartSelectorType: "FRAGMENT_NUMBER",
|
137
|
-
AfterFragmentNumber:fragmentNumber,
|
138
|
-
},
|
139
|
-
StreamName: streamName
|
140
|
-
};
|
141
|
-
|
142
|
-
// RAWデータの取得
|
143
|
-
const kvmClient = new KinesisVideoMediaClient({ endpoint: end.DataEndpoint, region : region });
|
144
|
-
const kvmCommand = new GetMediaCommand(kvmInput);
|
145
|
-
const data = await kvmClient.send(kvmCommand);
|
146
|
-
|
147
|
-
//取得したRAWデータをでコード
|
148
|
-
const decodedData = decoder(data, fragmentNumber);
|
149
|
-
|
150
|
-
// デコー
|
113
|
+
// デコーダーのオン/オフの設定
|
151
|
-
const wav = Converter.createWav(decodedData, 8000);
|
152
|
-
|
153
|
-
return wav.buffer;
|
154
|
-
|
155
|
-
|
156
114
|
}
|
157
115
|
|
158
116
|
class Converter {
|
@@ -188,12 +146,15 @@
|
|
188
146
|
|
189
147
|
static _writeString(view, offset, string) {
|
190
148
|
if (offset + string.length > view.byteLength) {
|
191
|
-
throw new RangeError('Offset is outside the bounds of the DataView');
|
149
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
192
150
|
}
|
193
|
-
for (let i = 0; i < len; i++) {
|
151
|
+
for (let i = 0; i < string.length; i++) {
|
194
152
|
if (offset + i < view.byteLength) {
|
195
|
-
view.setUint8(offset + i, sr
|
153
|
+
view.setUint8(offset + i, string.charCodeAt(i));
|
196
154
|
} else {
|
197
155
|
throw new RangeError('Offset is outside the bounds of the DataView');
|
156
|
+
}
|
198
157
|
}
|
199
158
|
}
|
159
|
+
}
|
160
|
+
|
7
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -161,29 +161,39 @@
|
|
161
161
|
const len = samples.byteLength;
|
162
162
|
const view = new DataView(new ArrayBuffer(44 + len));
|
163
163
|
this._writeString(view, 0, 'RIFF');
|
164
|
-
view.setUint32(4, 3
|
164
|
+
view.setUint32(4, 36 + len, true); // RIFFのサイズを修正
|
165
165
|
this._writeString(view, 8, 'WAVE');
|
166
166
|
this._writeString(view, 12, 'fmt ');
|
167
167
|
view.setUint32(16, 16, true);
|
168
168
|
view.setUint16(20, 1, true); // リニアPCM
|
169
169
|
view.setUint16(22, 1, true); // モノラル
|
170
|
-
view.setUint32(24, sampleRate, true);
|
170
|
+
view.setUint32(24, sampleRate, true);
|
171
171
|
view.setUint32(28, sampleRate * 2, true);
|
172
172
|
view.setUint16(32, 2, true);
|
173
173
|
view.setUint16(34, 16, true);
|
174
174
|
this._writeString(view, 36, 'data');
|
175
175
|
view.setUint32(40, len, true);
|
176
|
+
|
177
|
+
const srcView = new DataView(samples);
|
176
178
|
let offset = 44;
|
177
|
-
const srcView = new DataView(samples);
|
178
|
-
for (
|
179
|
+
for (let i = 0; i < len; i++) {
|
180
|
+
if (offset + i >= view.byteLength) {
|
181
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
182
|
+
}
|
179
|
-
view.set
|
183
|
+
view.setUint8(offset + i, srcView.getUint8(i));
|
180
184
|
}
|
185
|
+
|
181
186
|
return view;
|
182
187
|
}
|
183
188
|
|
184
189
|
static _writeString(view, offset, string) {
|
190
|
+
if (offset + string.length > view.byteLength) {
|
191
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
192
|
+
}
|
185
|
-
for (
|
193
|
+
for (let i = 0; i < len; i++) {
|
194
|
+
if (offset + i < view.byteLength) {
|
186
|
-
view.setUint8(offset + i, s
|
195
|
+
view.setUint8(offset + i, srcView.getUint8(i));
|
196
|
+
} else {
|
197
|
+
throw new RangeError('Offset is outside the bounds of the DataView');
|
187
198
|
}
|
188
199
|
}
|
189
|
-
}
|
6
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -10,8 +10,8 @@
|
|
10
10
|
|
11
11
|
// リージョンやバケット名の設定
|
12
12
|
const region = 'ap-northeast-1';
|
13
|
-
const putKey = '
|
13
|
+
const putKey = 'voice-message/wav/';
|
14
|
-
const bucketName = 'm
|
14
|
+
const bucketName = 'testname-pjcode';
|
15
15
|
|
16
16
|
// Lambda関数のエントリーポイント
|
17
17
|
export const handler = async (event) => {
|
@@ -151,6 +151,8 @@
|
|
151
151
|
const wav = Converter.createWav(decodedData, 8000);
|
152
152
|
|
153
153
|
return wav.buffer;
|
154
|
+
|
155
|
+
|
154
156
|
}
|
155
157
|
|
156
158
|
class Converter {
|
5
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -146,25 +146,11 @@
|
|
146
146
|
|
147
147
|
//取得したRAWデータをでコード
|
148
148
|
const decodedData = decoder(data, fragmentNumber);
|
149
|
+
|
150
|
+
// デコードされたデータをWAV形式に変換
|
151
|
+
const wav = Converter.createWav(decodedData, 8000);
|
149
152
|
|
150
|
-
// 取得したRAWデータをWAV形式に変換
|
151
|
-
const margin = 4;
|
152
|
-
var sumLength = 0;
|
153
|
-
chunks.forEach( chunk => {
|
154
|
-
sumLength += chunk.byteLength - margin;
|
155
|
-
});
|
156
|
-
var sample = new Uint8Array(sumLength);
|
157
|
-
var pos = 0;
|
158
|
-
chunks.forEach(chunk => {
|
159
|
-
let tmp = new Uint8Array(chunk.byteLength - margin);
|
160
|
-
for(var e = 0; e < chunk.byteLength - margin; e++){
|
161
|
-
tmp[e] = chunk[e + margin];
|
162
|
-
}
|
163
|
-
sample.set(tmp, pos);
|
164
|
-
pos += chunk.byteLength - margin;
|
165
|
-
|
166
|
-
});
|
167
|
-
return
|
153
|
+
return wav.buffer;
|
168
154
|
}
|
169
155
|
|
170
156
|
class Converter {
|
4
コードを修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -103,7 +103,7 @@
|
|
103
103
|
let chunks = [];
|
104
104
|
let decoder = function () {};
|
105
105
|
function decoderOn() {
|
106
|
-
decoder
|
106
|
+
decoder = function (data, chunk) {
|
107
107
|
if(chunk[1].name == 'SimpleBlock'){
|
108
108
|
chunks.push(chunk[1].data);
|
109
109
|
}
|
@@ -143,6 +143,9 @@
|
|
143
143
|
const kvmClient = new KinesisVideoMediaClient({ endpoint: end.DataEndpoint, region : region });
|
144
144
|
const kvmCommand = new GetMediaCommand(kvmInput);
|
145
145
|
const data = await kvmClient.send(kvmCommand);
|
146
|
+
|
147
|
+
//取得したRAWデータをでコード
|
148
|
+
const decodedData = decoder(data, fragmentNumber);
|
146
149
|
|
147
150
|
// 取得したRAWデータをWAV形式に変換
|
148
151
|
const margin = 4;
|
@@ -196,5 +199,3 @@
|
|
196
199
|
}
|
197
200
|
}
|
198
201
|
}
|
199
|
-
|
200
|
-
|
3
コードを修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -4,47 +4,28 @@
|
|
4
4
|
|
5
5
|
-------------------------Node.js 18.xコード---------------------------------------------------
|
6
6
|
```
|
7
|
-
|
7
|
+
import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
8
|
-
const ebml = import('ebml');
|
9
|
-
console.log(`===ebml===:${ebml}`);
|
10
|
-
console.log('process.env.NODE_PATH==');
|
11
|
-
console.log(process.env.NODE_PATH);
|
12
|
-
|
13
|
-
|
8
|
+
import { KinesisVideoClient, GetDataEndpointCommand } from "@aws-sdk/client-kinesis-video";
|
14
|
-
|
9
|
+
import { KinesisVideoMediaClient, GetMediaCommand } from "@aws-sdk/client-kinesis-video-media";
|
10
|
+
|
15
|
-
|
11
|
+
// リージョンやバケット名の設定
|
16
12
|
const region = 'ap-northeast-1';
|
17
|
-
const putKey = 'voice
|
13
|
+
const putKey = 'my-voicemessageXXXXXXXXX/wav/';
|
18
14
|
const bucketName = 'my-baseXXXXXXXXX';
|
19
15
|
|
20
|
-
//
|
16
|
+
// Lambda関数のエントリーポイント
|
21
|
-
import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
22
|
-
// Amazon Kinesis Videoインポート
|
23
|
-
import { KinesisVideoClient, GetDataEndpointCommand } from "@aws-sdk/client-kinesis-video";
|
24
|
-
// Amazon Kinesis Video mediaインポート
|
25
|
-
import { KinesisVideoMediaClient, GetMediaCommand } from "@aws-sdk/client-kinesis-video-media";
|
26
|
-
const kvmClient = new KinesisVideoMediaClient({ region : region });
|
27
|
-
// ebmlのインポート ★importの書き方がよくわからない★
|
28
|
-
// const ebml = import('ebml');
|
29
|
-
|
30
|
-
|
31
17
|
export const handler = async (event) => {
|
32
|
-
|
33
|
-
console.log('==process.env.NODE_PATH==')
|
34
|
-
console.log(process.env.NODE_PATH)
|
35
|
-
console.log(JSON.stringify(event));
|
36
|
-
|
37
|
-
|
18
|
+
// S3クライアントを作成
|
38
19
|
const s3Client = new S3Client({ region: region });
|
20
|
+
|
39
|
-
|
21
|
+
// イベントの各レコードに対して処理を実行
|
40
22
|
for (let record of event.Records) {
|
41
23
|
// information/yyyymmdd_×××をgetKeyに格納
|
42
24
|
const getKey = record.s3.object.key;
|
43
|
-
|
44
25
|
// '/'までの文字数10がindexに格納
|
45
26
|
let index = getKey.lastIndexOf('/');
|
46
27
|
let fileName = 'noname';
|
47
|
-
|
28
|
+
|
48
29
|
if (index >= 0) {
|
49
30
|
// getKeyに対して、文字数11以降("/")の文字列を取得
|
50
31
|
fileName = getKey.substr(index + 1);
|
@@ -59,16 +40,12 @@
|
|
59
40
|
const s3GetRes = await s3Client.send(s3GetCommand);
|
60
41
|
const info = await s3GetRes.Body.transformToString();
|
61
42
|
const infoStr = JSON.parse(info);
|
62
|
-
console.log(`==info==:${info}`);
|
63
|
-
console.log(`==infoStr==:${infoStr}`);
|
64
|
-
|
65
43
|
const streamName = infoStr.streamARN.split('stream/')[1].split('/')[0];
|
66
44
|
const fragmentNumber = infoStr.startFragmentNumber;
|
67
45
|
const streamARN = infoStr.streamARN;
|
68
|
-
|
46
|
+
|
69
|
-
console.log(`==fragmentNumber==:${fragmentNumber}`);
|
70
|
-
|
71
|
-
// Kinesis Video
|
47
|
+
// Kinesis Video Media Clientを作成して、録音データを取得
|
48
|
+
const kvmClient = new KinesisVideoMediaClient({ region : region });
|
72
49
|
const kvmInput = {
|
73
50
|
StreamName: streamName,
|
74
51
|
StreamARN: streamARN,
|
@@ -79,18 +56,18 @@
|
|
79
56
|
|
80
57
|
const kvmCommand = new GetMediaCommand(kvmInput);
|
81
58
|
const data = await kvmClient.send(kvmCommand);
|
82
|
-
|
59
|
+
|
83
60
|
// Kinesis Video StreamsからRAWデータの取得
|
84
61
|
const raw = await getMedia(streamName, fragmentNumber);
|
62
|
+
|
85
63
|
// RAWデータからWAVファイルを作成
|
86
64
|
const wav = Converter.createWav(raw, 8000);
|
87
|
-
|
65
|
+
|
88
66
|
// WAVファイルをS3に保存
|
89
67
|
let tagging = ''; // 付加情報をタグに追加する
|
90
68
|
tagging += "customerEndpoint=" + info.customerEndpoint + '&';
|
91
69
|
tagging += "systemEndpoint=" + info.systemEndpoint + '&';
|
92
70
|
tagging += "startTimestamp=" + info.startTimestamp;
|
93
|
-
// await s3.put(bucketName, putKey + createKeyName() + fileName + '.wav', Buffer.from(wav.buffer), tagging)
|
94
71
|
|
95
72
|
const wavInput = new PutObjectCommand({
|
96
73
|
Bucket: bucketName,
|
@@ -100,13 +77,11 @@
|
|
100
77
|
});
|
101
78
|
|
102
79
|
const wavRes = await s3Client.send(wavInput);
|
103
|
-
|
80
|
+
}
|
104
|
-
|
81
|
+
};
|
105
|
-
}; // export const handler
|
106
82
|
|
107
83
|
// S3バケット保存時のオブジェクト名を生成
|
108
84
|
function createKeyName() {
|
109
|
-
|
110
85
|
const date = new Date();
|
111
86
|
const year = date.getFullYear();
|
112
87
|
const mon = (date.getMonth() + 1);
|
@@ -114,7 +89,6 @@
|
|
114
89
|
const hour = date.getHours();
|
115
90
|
|
116
91
|
const space = (n) => {
|
117
|
-
console.log(`==n==${n}`);
|
118
92
|
return ('0' + (n)).slice(-2);
|
119
93
|
};
|
120
94
|
|
@@ -123,12 +97,11 @@
|
|
123
97
|
result += space(day) + '/';
|
124
98
|
result += space(hour) + '/';
|
125
99
|
return result;
|
100
|
+
}
|
101
|
+
|
126
|
-
|
102
|
+
// Decoderの初期化
|
127
|
-
|
128
|
-
// ebml「on」のインポート
|
129
103
|
let chunks = [];
|
130
|
-
let decoder = function () {};
|
104
|
+
let decoder = function () {};
|
131
|
-
// let decoder = ebml();
|
132
105
|
function decoderOn() {
|
133
106
|
decoder.on = function (data, chunk) {
|
134
107
|
if(chunk[1].name == 'SimpleBlock'){
|
@@ -143,25 +116,21 @@
|
|
143
116
|
return chunk;
|
144
117
|
};
|
145
118
|
}
|
146
|
-
|
147
|
-
|
119
|
+
|
148
120
|
decoderOn();
|
149
|
-
|
150
|
-
console.log(`==decoder==${decoder}`);
|
151
121
|
|
152
122
|
// 録音データの取得・wavデータへの変換
|
153
123
|
async function getMedia(streamName, fragmentNumber) {
|
154
|
-
// Endpointの取得
|
155
124
|
let kvInput = {
|
156
125
|
APIName: "GET_MEDIA",
|
157
126
|
StreamName: streamName
|
158
127
|
};
|
128
|
+
|
159
|
-
|
129
|
+
// Endpointの取得
|
160
130
|
const kvClient = new KinesisVideoClient({ region : region });
|
161
131
|
const kvCommand = new GetDataEndpointCommand(kvInput);
|
162
132
|
const end = await kvClient.send(kvCommand);
|
163
133
|
|
164
|
-
// RAWデータの取得
|
165
134
|
let kvmInput = {
|
166
135
|
StartSelector: {
|
167
136
|
StartSelectorType: "FRAGMENT_NUMBER",
|
@@ -169,31 +138,14 @@
|
|
169
138
|
},
|
170
139
|
StreamName: streamName
|
171
140
|
};
|
141
|
+
|
172
|
-
|
142
|
+
// RAWデータの取得
|
173
143
|
const kvmClient = new KinesisVideoMediaClient({ endpoint: end.DataEndpoint, region : region });
|
174
144
|
const kvmCommand = new GetMediaCommand(kvmInput);
|
175
145
|
const data = await kvmClient.send(kvmCommand);
|
176
146
|
|
177
|
-
console.log('==chunks==');
|
178
|
-
console.log(chunks);
|
179
|
-
|
180
|
-
//
|
147
|
+
// 取得したRAWデータをWAV形式に変換
|
181
|
-
function decoderWrite() {
|
182
|
-
decoder.write = function () {
|
183
|
-
decoder.write(data["Payload"]);
|
184
|
-
return data;
|
185
|
-
};
|
186
|
-
}
|
187
|
-
|
188
|
-
// export default decoderWrite();
|
189
|
-
decoderWrite();
|
190
|
-
|
191
|
-
|
192
|
-
console.log('==chunks2==');
|
193
|
-
cons
|
148
|
+
const margin = 4;
|
194
|
-
|
195
|
-
// chunksの結合
|
196
|
-
const margin = 4; // 各chunkの先頭4バイトを破棄する
|
197
149
|
var sumLength = 0;
|
198
150
|
chunks.forEach( chunk => {
|
199
151
|
sumLength += chunk.byteLength - margin;
|
@@ -209,13 +161,9 @@
|
|
209
161
|
pos += chunk.byteLength - margin;
|
210
162
|
|
211
163
|
});
|
212
|
-
console.log('==sample==');
|
213
|
-
console.log(sample);
|
214
164
|
return sample.buffer;
|
215
|
-
}
|
165
|
+
}
|
216
|
-
|
217
|
-
|
218
|
-
|
166
|
+
|
219
167
|
class Converter {
|
220
168
|
// WAVファイルの生成
|
221
169
|
static createWav(samples, sampleRate) {
|
@@ -247,6 +195,6 @@
|
|
247
195
|
view.setUint8(offset + i, string.charCodeAt(i));
|
248
196
|
}
|
249
197
|
}
|
250
|
-
}
|
198
|
+
}
|
251
|
-
|
252
|
-
|
199
|
+
|
200
|
+
|
2
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -4,8 +4,17 @@
|
|
4
4
|
|
5
5
|
-------------------------Node.js 18.xコード---------------------------------------------------
|
6
6
|
```
|
7
|
+
// import { ebml } from './node/nodejs';
|
8
|
+
const ebml = import('ebml');
|
9
|
+
console.log(`===ebml===:${ebml}`);
|
10
|
+
console.log('process.env.NODE_PATH==');
|
11
|
+
console.log(process.env.NODE_PATH);
|
12
|
+
|
13
|
+
// 「test-voice-message-infomation」でS3に保存されたデータ(発信者番号、ストリーム名、フラグメント番号など)をもとに、
|
14
|
+
// Amazon Kinesis Video Streamsから、RAWファイルをWAVファイルに変換し、S3に保存する。
|
15
|
+
|
7
16
|
const region = 'ap-northeast-1';
|
8
|
-
const putKey = 'wav/';
|
17
|
+
const putKey = 'voice-message/wav/';
|
9
18
|
const bucketName = 'my-baseXXXXXXXXX';
|
10
19
|
|
11
20
|
// S3インポート
|
@@ -15,11 +24,14 @@
|
|
15
24
|
// Amazon Kinesis Video mediaインポート
|
16
25
|
import { KinesisVideoMediaClient, GetMediaCommand } from "@aws-sdk/client-kinesis-video-media";
|
17
26
|
const kvmClient = new KinesisVideoMediaClient({ region : region });
|
27
|
+
// ebmlのインポート ★importの書き方がよくわからない★
|
18
|
-
|
28
|
+
// const ebml = import('ebml');
|
19
|
-
|
29
|
+
|
20
30
|
|
21
31
|
export const handler = async (event) => {
|
22
32
|
|
33
|
+
console.log('==process.env.NODE_PATH==')
|
34
|
+
console.log(process.env.NODE_PATH)
|
23
35
|
console.log(JSON.stringify(event));
|
24
36
|
|
25
37
|
const region = 'ap-northeast-1';
|
@@ -112,12 +124,11 @@
|
|
112
124
|
result += space(hour) + '/';
|
113
125
|
return result;
|
114
126
|
} // createKeyName
|
115
|
-
|
116
|
-
|
117
127
|
|
118
128
|
// ebml「on」のインポート
|
119
129
|
let chunks = [];
|
120
|
-
let decoder = function () {};
|
130
|
+
let decoder = function () {}; // 下記に修正
|
131
|
+
// let decoder = ebml();
|
121
132
|
function decoderOn() {
|
122
133
|
decoder.on = function (data, chunk) {
|
123
134
|
if(chunk[1].name == 'SimpleBlock'){
|
@@ -226,7 +237,7 @@
|
|
226
237
|
let offset = 44;
|
227
238
|
const srcView = new DataView(samples);
|
228
239
|
for (var i = 0; i < len; i+=4, offset+=4) {
|
229
|
-
view.setInt32(offset, srcView.getUint32(i));
|
240
|
+
view.setInt32(offset, srcView.getUint32(i), true);
|
230
241
|
}
|
231
242
|
return view;
|
232
243
|
}
|
@@ -238,6 +249,4 @@
|
|
238
249
|
}
|
239
250
|
} // class Converter
|
240
251
|
|
241
|
-
|
252
|
+
|
242
|
-
|
243
|
-
|
1
コードの修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
参考サイト:https://qiita.com/echolimitless/items/af068e147e233c51d6a4
|
4
4
|
|
5
5
|
-------------------------Node.js 18.xコード---------------------------------------------------
|
6
|
-
|
6
|
+
```
|
7
7
|
const region = 'ap-northeast-1';
|
8
8
|
const putKey = 'wav/';
|
9
9
|
const bucketName = 'my-baseXXXXXXXXX';
|
@@ -238,4 +238,6 @@
|
|
238
238
|
}
|
239
239
|
} // class Converter
|
240
240
|
|
241
|
-
|
241
|
+
```
|
242
|
+
|
243
|
+
|