J'essaie d'utiliser la nouvelle API de transcription de streaming d'Amazon à partir de Go 1.11. Actuellement, Amazon fournit Java SDK uniquement, donc j'essaie la méthode de bas niveau.
Le seul élément de documentation pertinent est ici mais il ne montre pas le point final. Je l'ai trouvé dans un exemple Java que c'est https://transcribestreaming.<region>.amazonaws.com
Et j'essaie la région Irlande c'est-à-dire https://transcribestreaming.eu-west-1.amazonaws.com
. Voici mon code pour ouvrir un flux bidirectionnel HTTP/2:
import (
"crypto/tls"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/external"
"github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"golang.org/x/net/http2"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"time"
)
const (
HeaderKeyLanguageCode = "x-amzn-transcribe-language-code" // en-US
HeaderKeyMediaEncoding = "x-amzn-transcribe-media-encoding" // pcm only
HeaderKeySampleRate = "x-amzn-transcribe-sample-rate" // 8000, 16000 ... 48000
HeaderKeySessionId = "x-amzn-transcribe-session-id" // For retrying a session. Pattern: [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}
HeaderKeyVocabularyName = "x-amzn-transcribe-vocabulary-name"
HeaderKeyRequestId = "x-amzn-request-id"
)
...
region := "eu-west-1"
cfg, err := external.LoadDefaultAWSConfig(aws.Config{
Region: region,
})
if err != nil {
log.Printf("could not load default AWS config: %v", err)
return
}
signer := v4.NewSigner(cfg.Credentials)
transport := &http2.Transport{
TLSClientConfig: &tls.Config{
// allow insecure just for debugging
InsecureSkipVerify: true,
},
}
client := &http.Client{
Transport: transport,
}
signTime := time.Now()
header := http.Header{}
header.Set(HeaderKeyLanguageCode, "en-US")
header.Set(HeaderKeyMediaEncoding, "pcm")
header.Set(HeaderKeySampleRate, "16000")
header.Set("Content-type", "application/json")
// Bi-directional streaming via a pipe.
pr, pw := io.Pipe()
req, err := http.NewRequest(http.MethodPost, "https://transcribestreaming.eu-west-1.amazonaws.com/stream-transcription", ioutil.NopCloser(pr))
if err != nil {
log.Printf("err: %+v", err)
return
}
req.Header = header
_, err = signer.Sign(req, nil, "transcribe", region, signTime)
if err != nil {
log.Printf("problem signing headers: %+v", err)
return
}
// This freezes and ends after 5 minutes with "unexpected EOF".
res, err := client.Do(req)
...
Le problème est que l'exécution de la demande (client.Do(req)
) se bloque pendant cinq minutes, puis se termine par l'erreur "EOF inattendue".
Des idées sur ce que je fais mal? Quelqu'un a-t-il réussi à utiliser la nouvelle API de transcription en continu sans le Java SDK?
EDIT (11 mars 2019):
J'ai testé cela à nouveau et maintenant il ne s'arrête pas mais renvoie immédiatement la réponse 200 OK
. Il y a cependant une "exception" dans le corps de la réponse: {"Output":{"__type":"com.Amazon.coral.service#SerializationException"},"Version":"1.0"}
J'ai essayé d'ouvrir le flux HTTP2 avec io.Pipe
(Comme le code ci-dessus) et aussi avec un corps JSON décrit dans la documentation:
{
"AudioStream": {
"AudioEvent": {
"AudioChunk": ""
}
}
}
Le résultat était le même.
EDIT (13 mars 2019):
Comme mentionné par @gpeng, la suppression du content-type
Des en-têtes corrigera le SerializationException
. Mais il y a ensuite une exception IAM et il est nécessaire d'ajouter l'autorisation transcription:StartStreamTranscription
À votre utilisateur IAM. Cela n'est cependant nulle part dans la console AWS IAM et doit être ajouté manuellement en tant qu'autorisation JSON personnalisée: /
Il y a aussi un nouveau/un autre document de documentation ici qui montre un Host
incorrect et un nouveau content-type
(N'utilisez pas ce content-type
, La requête sera retournez 404 avec).
Après avoir supprimé le content-type
Et ajouté la nouvelle autorisation, je reçois maintenant une exception {"Message":"A complete signal was sent without the preceding empty frame."}
. Également écrit pour toujours sur les blocs de tuyaux, donc je suis de nouveau coincé. Les messages décrits dans la nouvelle documentation sont différents de ceux de l'ancienne, maintenant finalement binaire, mais je ne les comprends pas. Des idées pour envoyer de tels messages HTTP2 dans Go?
EDIT (Match 15, 2019): *
Si vous obtenez une erreur HTTP 403 sur la non-correspondance de signature, ne définissez pas les en-têtes HTTP transfer-encoding
Et x-amz-content-sha256
. Lorsque je les ai définis, signez la demande avec le signataire V4 du SDK AWS, puis je reçois HTTP 403 The request signature we calculated does not match the signature you provided.
J'ai contacté le support AWS et ils recommandent maintenant d'utiliser les websockets au lieu de HTTP/2 lorsque cela est possible (article de blog ici )
Si cela correspond à votre cas d'utilisation, je recommande fortement de consulter le nouvel exemple de dépôt à: https://github.com/aws-samples/Amazon-transcribe-websocket-static qui montre une solution basée sur un navigateur dans JS.
J'ai également remarqué que l'auteur de la démo a un exemple exprès sur son Github personnel à: https://github.com/brandonmwest/Amazon-transcribe-websocket-express mais je ne l'ai pas confirmé si cela fonctionne.
Appréciez que ces exemples ne sont pas en Python mais je pense que vous aurez plus de chance d'utiliser le client Websocket par opposition à HTTP/2 (qui, soyons honnêtes, est toujours un peu terrifiant: P)
Essayez de ne pas définir l'en-tête du type de contenu et voyez quelle réponse vous obtenez. J'essaie de faire la même chose (mais en Ruby) et cela a "corrigé" le SerializationException
. Je n'arrive toujours pas à le faire fonctionner mais j'ai maintenant une nouvelle erreur à penser :)
MISE À JOUR: Je l'ai fait fonctionner maintenant. Mon problème était avec la signature. Si les en-têtes Host
et authority
sont passés, ils sont joints à ,
et traité comme Host
côté serveur lorsque la signature est vérifiée afin que les signatures ne correspondent jamais. Cela ne semble pas être un comportement correct du côté AWS, mais il ne semble pas que cela va être un problème pour vous dans Go.
Je lutte toujours contre cette chose avec Node.js également. Ce qui n'est pas clair à propos des documents, c'est que en un seul endroit il dit que le Content-Type
ne devrait pas être application/json
, mais dans un autre endroit , cela donne l'impression que la charge utile devrait être codée comme application/vnd.Amazon.eventstream
. Il semble que la charge utile doit être soigneusement formatée dans un format binaire au lieu d'un objet JSON comme suit:
Amazon Transcribe utilise un format appelé encodage de flux d'événements pour la transcription en streaming. Ce format encodait des données binaires avec des informations d'en-tête qui décrivent le contenu de chaque événement. Vous pouvez utiliser ces informations pour les applications qui appellent le point de terminaison Amazon Transcribe sans utiliser le SDK Amazon Transcribe. Amazon Transcribe utilise le protocole HTTP/2 pour les transcriptions en streaming. Les composants clés d'une demande de streaming sont:
Un cadre d'en-tête. Celui-ci contient les en-têtes HTTP de la demande et une signature dans l'en-tête d'autorisation qu'Amazon Transcribe utilise comme signature de départ pour signer les trames de données suivantes.
Une ou des trames de message dans l'encodage de flux d'événements. Le cadre contient des métadonnées et les octets audio bruts.
Un cadre de fin. Il s'agit d'un message signé dans le codage de flux d'événements avec un corps vide.
Il existe un exemple de fonction qui montre comment implémenter tout cela en utilisant Java qui pourrait éclairer la façon dont cet encodage doit être effectué.
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, j'ai continué et j'ai écrit un package qui 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)