web-dev-qa-db-fra.com

La demande de service de streaming Amazon Transcribe dans Node.js avec Http / 2 ne donne aucune réponse

J'essaie d'utiliser Amazon Transcribe Streaming Service avec une demande http2 de Node.js, voici les liens de documentation que je suis en train de suivre Streaming request format . Selon ce document, le point de terminaison est https: // transcribe-streaming . <'Region'> .amazonaws.com, mais faire une demande à cette URL donne une erreur URL non trouvée. Mais dans le exemple Java , le point de fin trouvé est https: // transcribestreaming . ''. Amazonaws.com, donc faire une demande à cette URL ne donne aucune erreur ni réponse arrière. J'essaie de la région us-east-1.

Voici le code avec lequel j'essaie.

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();

Quelqu'un peut-il signaler ce qui me manque ici? Je n'ai pas non plus trouvé d'exemples implémentant cela avec HTTP/2.

Mise à jour: la modification du type de contenu en application/json est revenue avec un état de réponse 200 mais avec l'exception suivante:

`{"Output":{"__type":"com.Amazon.coral.service#SerializationException"},"Version":"1.0"}`

Mise à jour (22 avril 2019):

req.setEncoding('utf8');
req.write(audioBlob);

var audioBlob = new Buffer(JSON.stringify({
    "AudioStream": { 
       "AudioEvent": { 
          "AudioChunk": audioBufferData
     }
 }

Avant de terminer la demande, j'ajoute un "audioblod" comme charge utile en sérialisant. Mon "audioBufferData" est au format audio PCM brut du navigateur. Je vois dans documentation la charge utile doit être encodée en "Event Stream Encoding", mais je n'ai pas pu comprendre comment l'implémenter.

Donc, sans l'encodage de ce flux d'événements, j'obtiens cette exception suivante avec un état de réponse 200.

{"Output":{"__type":"com.Amazon.coral.service#UnknownOperationException"},"Version":"1.0"}
13
Manoj

Cela ne répond pas directement à la question, mais je pense que c'est assez utile pour poster une réponse plutôt qu'un commentaire.

AWS vient d'être annoncé Prise en charge WebSocket pour Amazon Transcribe. Voici les documents , et voici un exemple d'application côté client . La plus grande différence, et je pense que cela rend l'intégration avec WebSockets plus simple, est que vous n'avez pas besoin de signer chaque morceau audio comme http/2 l'exige.

Le code approprié pour autoriser et établir la connexion à l'aide d'une URL pré-signée se trouve dans 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);
};

Et nous l'invoquons dans 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
        }
    );
}

Pour emballer les choses dans le format de message de flux d'événements dont nous avons besoin, nous enveloppons l'audio codé PCM dans une enveloppe JSON et convertissez-le en binaire

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
    };
}
0
bwest

J'avais une exigence similaire pour utiliser le service de transcription AWS avec leur API WebSocket dans le nœud js. Étant donné qu'il n'y avait pas encore de support pour cela dans le package officiel, je suis allé de l'avant et j'ai écrit un package suivant cette implémentation disponible sur github . Il s'appelle AWS-transcribe et peut être trouvé ici . J'espère que ça aide.

Il fournit une interface de flux autour du WebSocket et peut être utilisé comme l'exemple ci-dessous

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)
0
Muhammad Qasim