Запрос сервиса Amazon Transcribe Streaming в Node.js с Http/2 не дает ответа

Я пытаюсь использовать Amazon Transcribe Streaming Service с запросом http2 от Node.js. Вот ссылки на документацию, по которым я следую Формат потокового запроса. Согласно этому документу конечной точкой является https://transcribe-streaming. & Lt; 'region'>. Amazonaws.com, но при запросе по этому URL-адресу URL-адрес не найден. Но в примере Java найдена конечная точка как https://transcribestreaming. ''. Amazonaws.com, поэтому выполнение запроса по этому URL-адресу не возвращает никаких ошибок или ответов. Я пытаюсь из нас-восток-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.

Обновить: Изменение типа контента на application/json вернулось со статусом ответа 200, но со следующим исключением:

'{"Output":{"__type":"com.amazon.coral.service#SerializationException"},"Version":"1.0"}'

Обновление (22 апреля 2009 г.):

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

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

Перед завершением запроса я добавляю "аудиоблод" в качестве полезной нагрузки путем сериализации. Мои "audioBufferData" находятся в необработанном аудио формате PCM из браузера. Я вижу из документации полезная нагрузка должна быть закодирована в "Event Stream Encoding", но не могла понять, как ее реализовать.

Так что без этой кодировки потока событий в настоящее время я получаю следующее исключение с 200 статусом ответа.

{"Output":{"__type":"com.amazon.coral.service#UnknownOperationException"},"Version":"1.0"}

Ответ 1

Я обратился в службу поддержки AWS, и они, похоже, не смогли заставить реализацию HTTP/2 работать с NodeJS Either.

Однако теперь они предоставляют способ взаимодействия с потоковым API Transcribe напрямую через веб-сокеты (запись в блоге здесь)

Если это соответствует вашему варианту использования, я настоятельно рекомендую проверить новый репозиторий по адресу: https://github.com/aws-samples/amazon-transcribe-websocket-static

Если вы используете его на общедоступной странице, я бы рекомендовал использовать сеансы Cognito без аутентификации для обработки получения учетных данных. У меня это работает в производственном приложении, поэтому не стесняйтесь отмечать меня в любых других вопросах.

Ответ 2

У меня похожие проблемы, вы также получаете эту ошибку:

{response: [Object: null prototype] {': status': 403, 'x-amzn-requesttid': '7dc6a6eb-24bf-41e6-9ccf-c23fff859701', 'x-amzn-errortype': 'MissingAuthenticationTokenException: http://internal.amazon.com/coral/com.amazon.coral.service/ ', дата: четверг, 6 июня 2019 г. 08:12:10 GMT, тип контента: application/x-amz-json -1.1 ',' content-length ':' 42 '}, флаги: 36}

Ответ 3

Это не дает прямого ответа на вопрос, но я думаю, что это достаточно полезно, чтобы публиковать в качестве ответа, а не комментария.

AWS только что объявил о поддержке WebSocket для Amazon Transcribe. Вот документы, а вот пример приложения на стороне клиента. Самым большим отличием, которое, я думаю, делает интеграцию с 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
        }
    );
}

Чтобы упаковать вещи в нужный нам формат сообщения потока событий, мы оборачиваем звук в формате PCM в конверт JSON и преобразуем его в двоичный файл

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