我正在尝试将Amazon Transcribe流服务与Node.js的http2请求一起使用,这是我关注的文档链接 Streaming request format。根据该文档,端点为https://transcribe-streaming。<'region'>。amazonaws.com,但是对此URL进行请求将导致URL找不到错误。 但是在Java Example找到的端点为https://transcribestreaming。''。amazonaws.com中,因此对此URL发出请求不会产生任何错误或响应。我正在从us-east-1地区尝试。
这是我尝试使用的代码。
const http2 = require('http2');
var aws4 = require('aws4');
var opts = {
service: 'transcribe',
region: 'us-east-1',
path: '/stream-transcription',
headers:{
'content-type': 'application/json',
'x-amz-target': 'com.amazonaws.transcribe.Transcribe.StartStreamTranscription'
}
}
var urlObj = aws4.sign(opts, {accessKeyId: '<access key>', secretAccessKey: '<aws secret>'});
const client = http2.connect('https://transcribestreaming.<region>.amazonaws.com');
client.on('error', function(err){
console.error("error in request ",err);
});
const req = client.request({
':method': 'POST',
':path': '/stream-transcription',
'authorization': urlObj.headers.Authorization,
'content-type': 'application/json',
'x-amz-content-sha256': 'STREAMING-AWS4-HMAC-SHA256-EVENTS',
'x-amz-target': 'com.amazonaws.transcribe.Transcribe.StartStreamTranscription',
'x-amz-date': urlObj['headers']['X-Amz-Date'],
'x-amz-transcribe-language-code': 'en-US',
'x-amz-transcribe-media-encoding': 'pcm',
'x-amz-transcribe-sample-rate': 44100
});
req.on('response', (headers, flags) => {
for (const name in headers) {
console.log(`${name}: ${headers[name]}`);
}
});
let data = '';
req.on('data', (chunk) => { data += chunk; });
req.on('end', () => {
console.log(`\n${data}`);
client.close();
});
req.end();
谁能指出我在这里所缺少的。我也找不到任何使用HTTP / 2实现此功能的示例。
更新: 将Content-type更改为application / json的响应状态为200,但有以下异常:
`{"Output":{"__type":"com.amazon.coral.service#SerializationException"},"Version":"1.0"}`
更新(2019年4月22日):
req.setEncoding('utf8');
req.write(audioBlob);
var audioBlob = new Buffer(JSON.stringify({
"AudioStream": {
"AudioEvent": {
"AudioChunk": audioBufferData
}
}
在结束请求之前,我要通过序列化添加“ audioblod”作为有效载荷。我的“ audioBufferData”是来自浏览器的原始PCM音频格式。 我从documentation看,有效载荷必须被编码为“事件流编码”,但是无法弄清楚如何实现它。
因此,在没有此事件流编码的情况下,我得到了以下具有200个响应状态的异常。
{"Output":{"__type":"com.amazon.coral.service#UnknownOperationException"},"Version":"1.0"}
答案 0 :(得分:0)
我联系了AWS支持,他们为此提供了支持。我已经在本地进行了测试,它似乎可以正常工作
var access_key = process.env.AWS_ACCESS_KEY_ID;
var secret_key = process.env.AWS_SECRET_ACCESS_KEY;
// I changed the region according my account, you can ignore it.
var region = 'eu-west-1';
var url = 'https://transcribestreaming.' + region + '.amazonaws.com';
var myService = 'transcribe';
var myMethod = 'POST';
var myPath = '/stream-transcription';
var query = ''
var crypto = require('crypto-js');
var http2 = require('http2');
var fs = require('fs')
const trimAll = (val) => {
return val.trim().replace(/\s+/g, ' ');
}
// this function gets the Signature Key, see AWS documentation(https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html) for more details
const getSignatureKey = (Crypto, key, dateStamp, regionName, serviceName) => {
var kDate = Crypto.HmacSHA256(dateStamp, "AWS4" + key);
var kRegion = Crypto.HmacSHA256(regionName, kDate);
var kService = Crypto.HmacSHA256(serviceName, kRegion);
var kSigning = Crypto.HmacSHA256("aws4_request", kService);
return kSigning;
}
// this function converts the generic JS ISO8601 date format to the specific format the AWS API wants
const getAmzDate = (dateStr) => {
var chars = [":", "-"];
for (var i = 0; i < chars.length; i++) {
while (dateStr.indexOf(chars[i]) != -1) {
dateStr = dateStr.replace(chars[i], "");
}
}
dateStr = dateStr.split(".")[0] + "Z";
return dateStr;
}
const getAuthHeaders = () => {
// get the various date formats needed to form our request
var amzDate = getAmzDate(new Date().toISOString());
var authDate = amzDate.split("T")[0];
const buf = fs.readFileSync('./test.mp3')
let arraybuffer = Uint8Array.from(buf).buffer;
// Payload needs to be managed on client side
var payload = `{
"AudioStream": {
"AudioEvent": {
"AudioChunk": ${arraybuffer}
}
}
}`;
// get the SHA256 hash value for payload
//var payload = '';
//var hashedPayload = crypto.SHA256(payload).toString();
var cannonicalHeaders = '';
var signedHeaders = [];
var headers = {
':authority': url,
':method': myMethod,
':path': myPath,
'content-type': 'application/vnd.amazon.eventstream',
//'transfer-encoding': 'chunked',
'x-amz-content-sha256': 'STREAMING-AWS4-HMAC-SHA256-EVENTS',
'x-amz-date': amzDate,
//'content-type':'application/json',
//'x-amz-security-token': session_token,
'x-amzn-target': 'com.amazonaws.transcribe.Transcribe.StartStreamTranscription',
'x-amzn-transcribe-language-code': 'en-US',
'x-amzn-transcribe-media-encoding': 'pcm',
'x-amzn-transcribe-sample-rate': '8000'
}
Object.keys(headers).sort().forEach((key) => {
header_key = key.toLowerCase();
if (header_key == ':authority') {
header_key = 'host';
}
if (header_key == 'x-amz-content-sha256' || header_key == 'x-amz-date' || header_key == 'host') {
cannonicalHeaders += header_key + ':' + trimAll(headers[key]) + '\n';
signedHeaders.push(header_key);
}
});
signedHeaders = signedHeaders.join(';');
var cannonicalReq = myMethod + '\n'
+ myPath + '\n'
+ query + '\n'
+ cannonicalHeaders + '\n'
+ signedHeaders + '\n'
+ 'STREAMING-AWS4-HMAC-SHA256-EVENTS';
console.log('\n=== cannonicalReq ===');
console.log(cannonicalReq);
// This is what the Canonical request should look like, you can get it from the 403 error message
// cannonicalReq = `POST
///stream-transcription
//
//host:https://transcribestreaming.eu-west-1.amazonaws.com
//x-amz-content-sha256:STREAMING-AWS4-HMAC-SHA256-EVENTS
//x-amz-date:${amzDate}
//
//host;x-amz-content-sha256;x-amz-date
//STREAMING-AWS4-HMAC-SHA256-EVENTS`
// hash the canonical request
var canonicalReqHash = crypto.SHA256(cannonicalReq).toString();
var stringToSign = 'AWS4-HMAC-SHA256\n'
+ amzDate + '\n'
+ authDate + '/' + region + '/transcribe/aws4_request\n'
+ canonicalReqHash;
console.log('\n=== StringToSign ===');
console.log(stringToSign);
// get our Signing Key
var signingKey = getSignatureKey(crypto, secret_key, authDate, region, myService);
// Sign our String-to-Sign with our Signing Key
var authKey = crypto.HmacSHA256(stringToSign, signingKey);
// Form our authorization header
var authString = 'AWS4-HMAC-SHA256 ' +
'Credential=' +
access_key + '/' +
authDate + '/' +
region + '/' +
myService + '/aws4_request,' +
'SignedHeaders=host;x-amz-content-sha256;x-amz-date,' +
'Signature=' + authKey;
console.log('\n=== authorization in headers ===');
console.log(authString);
headers['authorization'] = authString;
console.log('\n=== headers ===');
console.log('HEADERS:::::::');
console.log(headers);
return {
headers,
payload
};
}
try {
const client = http2.connect(url);
const { headers, payload } = getAuthHeaders();
var data;
client.on('error', (err) => console.error(err))
const req = client.request(headers);
console.log('Main Requestt');
console.log(req);
req.on('response', (headers, flags) => {
// may check and play with the http/2 response headers, and flags
console.dir(headers);
});a
req.setEncoding('utf8');
console.log(req)
req.on('data', (chunk) => {
// do something with the data
data += new Buffer.from(chunk).toString('ascii');
console.log(new Buffer.from(chunk).toString('ascii'));
});
req.on('end', () => {
client.close();
});
req.write(payload);
req.end();
} catch (error) {
console.error(error);
}
答案 1 :(得分:0)
这不能直接回答问题,但我认为将其作为回答而不是评论是足够有用的。
AWS just announced WebSocket对Amazon Transcribe的支持。 Here are the docs,这里是client-side sample app。我认为最大的区别是使您无需像http / 2那样对每个音频块进行签名,这使我与WebSockets集成更加简单。
用于使用预签名URL授权和启动连接的相关代码在lib/aws-signature-v4.js
exports.createPresignedURL = function(method, host, path, service, payload, options) {
options = options || {};
options.key = options.key || process.env.AWS_ACCESS_KEY_ID;
options.secret = options.secret || process.env.AWS_SECRET_ACCESS_KEY;
options.protocol = options.protocol || 'https';
options.headers = options.headers || {};
options.timestamp = options.timestamp || Date.now();
options.region = options.region || process.env.AWS_REGION || 'us-east-1';
options.expires = options.expires || 86400; // 24 hours
options.headers = options.headers || {};
// host is required
options.headers.Host = host;
var query = options.query ? querystring.parse(options.query) : {};
query['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256';
query['X-Amz-Credential'] = options.key + '/' + exports.createCredentialScope(options.timestamp, options.region, service);
query['X-Amz-Date'] = toTime(options.timestamp);
query['X-Amz-Expires'] = options.expires;
query['X-Amz-SignedHeaders'] = exports.createSignedHeaders(options.headers);
var canonicalRequest = exports.createCanonicalRequest(method, path, query, options.headers, payload);
var stringToSign = exports.createStringToSign(options.timestamp, options.region, service, canonicalRequest);
var signature = exports.createSignature(options.secret, options.timestamp, options.region, service, stringToSign);
query['X-Amz-Signature'] = signature;
return options.protocol + '://' + host + path + '?' + querystring.stringify(query);
};
然后我们在lib/main.js
中调用它:
function createPresignedUrl() {
let endpoint = "transcribestreaming." + region + ".amazonaws.com:8443";
// get a preauthenticated URL that we can use to establish our WebSocket
return v4.createPresignedURL(
'GET',
endpoint,
'/stream-transcription-websocket',
'transcribe',
crypto.createHash('sha256').update('', 'utf8').digest('hex'), {
'key': $('#access_id').val(),
'secret': $('#secret_key').val(),
'protocol': 'wss',
'expires': 15,
'region': region,
'query': "language-code=" + languageCode + "&media-encoding=pcm&sample-rate=" + sampleRate
}
);
}
要以事件流消息格式打包内容,我们wrap the PCM-encoded audio in a JSON envelope and convert it to binary
function convertAudioToBinaryMessage(audioChunk) {
let raw = mic.toRaw(audioChunk);
if (raw == null)
return;
// downsample and convert the raw audio bytes to PCM
let downsampledBuffer = audioUtils.downsampleBuffer(raw, sampleRate);
let pcmEncodedBuffer = audioUtils.pcmEncode(downsampledBuffer);
// add the right JSON headers and structure to the message
let audioEventMessage = getAudioEventMessage(Buffer.from(pcmEncodedBuffer));
//convert the JSON object + headers into a binary event stream message
let binary = eventStreamMarshaller.marshall(audioEventMessage);
return binary;
}
function getAudioEventMessage(buffer) {
// wrap the audio data in a JSON envelope
return {
headers: {
':message-type': {
type: 'string',
value: 'event'
},
':event-type': {
type: 'string',
value: 'AudioEvent'
}
},
body: buffer
};
}
答案 2 :(得分:0)
对于在节点js中将AWS transcribe服务与WebSocket API结合使用,我有类似的要求。鉴于目前官方包中尚无此支持,因此我继续在on github的此实现之后编写了一个包。它称为AWS转录,可以在here中找到。希望对您有所帮助。
它提供了围绕WebSocket的流接口,并且可以像下面的示例一样使用
import { AwsTranscribe, StreamingClient } from "aws-transcribe"
const client = new AwsTranscribe({
// if these aren't provided, they will be taken from the environment
accessKeyId: "ACCESS KEY HERE",
secretAccessKey: "SECRET KEY HERE",
})
const transcribeStream = client
.createStreamingClient({
region: "eu-west-1",
sampleRate,
languageCode: "en-US",
})
// enums for returning the event names which the stream will emit
.on(StreamingClient.EVENTS.OPEN, () => console.log(`transcribe connection opened`))
.on(StreamingClient.EVENTS.ERROR, console.error)
.on(StreamingClient.EVENTS.CLOSE, () => console.log(`transcribe connection closed`))
.on(StreamingClient.EVENTS.DATA, (data) => {
const results = data.Transcript.Results
if (!results || results.length === 0) {
return
}
const result = results[0]
const final = !result.IsPartial
const prefix = final ? "recognized" : "recognizing"
const text = result.Alternatives[0].Transcript
console.log(`${prefix} text: ${text}`)
})
someStream.pipe(transcribeStream)
答案 3 :(得分:0)