【问题标题】:Amazon Transcribe Streaming service request in Node.js with Http/2 gives no response带有 Http/2 的 Node.js 中的 Amazon Transcribe Streaming 服务请求没有响应
【发布时间】:2019-07-31 01:32:03
【问题描述】:

我正在尝试将 Amazon Transcribe Streaming Service 与来自 Node.js 的 http2 请求一起使用,这是我正在关注的文档链接 Streaming request format。根据这个文档,端点是https://transcribe-streaming..amazonaws.com,但是向这个 url 发出请求会给出 url not found 错误。 但是在Java Example 找到的端点为https://transcribestreaming.''.amazonaws.com,因此向该网址发出请求不会给出任何错误或响应。我正在从 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"}

【问题讨论】:

  • 你曾经能够让它工作吗?我也有类似的情况。谢谢!
  • 不,仍然停留在同一个问题上。
  • 还有较旧的流式转录文档,它有正确的主机但不好content-type :D docs.aws.amazon.com/transcribe/latest/dg/… 我在 Go 中与这个相同的 API 作斗争,我能够通过最初的此处的连接和 IAM 身份验证 stackoverflow.com/questions/53743785/… 尚未完全正常工作。
  • 删除/不设置 content-type 应该会有所帮助。在我的情况下,设置正确的 content-type 返回 HTTP 404。不过,我被卡住了。
  • 是的,设置 content-type:application/json 返回状态为 200 但有异常 {"Output":{"__type":"com.amazon.coral.service#SerializationException"} 的响应, "版本":"1.0"};但如果未提供内容类型,则会给出 403。

标签: node.js amazon-web-services aws-transcribe


【解决方案1】:

我联系了 AWS 支持,但他们似乎无法使用 NodeJS 获得 HTTP/2 实现。

但是,他们现在提供了一种直接通过 websocket 与 Transcribe 流 API 交互的方法(博客文章 here

如果这适合您的用例,我强烈建议您查看新的示例 repo:https://github.com/aws-samples/amazon-transcribe-websocket-static

如果您在面向公众的页面中使用它,我建议您使用未经身份验证的 Cognito 会话来处理凭据检索。我已经在生产应用程序中使用了此功能,因此请随时在任何其他问题中标记我。

【讨论】:

  • 感谢@calvin 的努力。我尝试运行此代码,但它给出了以下异常“message-typeappexception {“Message”:“无法处理您提供的音频流。再次尝试您的请求。"}o?=o?=("。我使用的是同一个文件,当我使用问题中提到的 Java 示例时该文件运行良好。
  • 嘿@Manoj - 我的回答为时过早。然而,在与 AWS 反复交流一段时间后,我们终于有了一些进展。见编辑
  • 谢谢@calvin - 我的用例有点不同,我们有一个自制的 diarizer 服务来检测扬声器的变化等......目前 AWS 不提供。因此,如果我使用 websockets 浏览器将需要双倍的带宽来将数据发送到两个不同的服务。所以现在作为一种解决方法,我编辑了他们提供的 java 示例,并在后端使用它来发送请求。虽然在流式传输 20 或 30 分钟后仍然面临一些问题,但在 AWS 服务的会话仍处于活动状态时,会出现签名过期异常。
【解决方案2】:

这并不能直接回答问题,但我认为发布作为答案而不是评论已经足够有用了。

AWS just announced WebSocket 支持 Amazon Transcribe。 Here are the docs,这是client-side sample app。最大的不同,我认为与 WebSockets 集成更直接的一个区别是,您不需要像 http/2 那样对每个音频块进行签名。

使用预签名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
    };
}

【讨论】:

    【解决方案3】:

    我对在 node js 中使用 AWS 转录服务和他们的 WebSocket API 有类似的要求。看到官方包中还没有对此的支持,我已经继续编写了一个包,遵循这个实现可用on github。它称为 AWS-transcribe,可以在 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)
    

    【讨论】:

      【解决方案4】:
      猜你喜欢
      • 2018-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-13
      • 2018-08-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多