web-dev-qa-db-ja.com

SDKなしのAmazon Transcribe Streaming API

Go 1.11のAmazonの新しいストリーミング文字起こしAPIを使用しようとしています。現在、AmazonはJava SDKのみを提供しているため、低レベルの方法を試しています。

関連する唯一のドキュメントは here ですが、エンドポイントは表示されません。私はそれを_https://transcribestreaming.<region>.amazonaws.com_である Javaの例 で見つけ、アイルランドの地域、つまり_https://transcribestreaming.eu-west-1.amazonaws.com_を試しています。以下は、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)
...
_

問題は、リクエスト(client.Do(req))の実行が5分間フリーズし、「予期しないEOF」エラーで終了することです。

私が間違っていることはありますか?誰かがJava SDKなしで新しいストリーミング文字起こしAPIを正常に使用しましたか?

EDIT(2019年3月11日):

私はこれをもう一度テストしましたが、タイムアウトしませんが、すぐに_200 OK_応答を返します。ただし、応答本文には「例外」があります。_{"Output":{"__type":"com.Amazon.coral.service#SerializationException"},"Version":"1.0"}_

_io.Pipe_(上記のコードのような)とドキュメントに記載されているJSONボディを使用してHTTP2ストリームを開こうとしました:

_{
    "AudioStream": { 
        "AudioEvent": { 
            "AudioChunk": ""
        }
    }
}
_

結果は同じでした。

EDIT(2019年3月13日):

@gpengで述べたように、ヘッダーから_content-type_を削除すると、SerializationExceptionが修正されます。ただし、IAM例外があり、_transcription:StartStreamTranscription_権限をIAMユーザーに追加する必要があります。それはAWS IAMコンソールのどこにもありませんが、カスタムJSON権限として手動で追加する必要があります:/

また、新しい/別のドキュメントドキュメント here は誤ったHostと新しい_content-type_を示しています(その_content-type_は使用しないでください。リクエストは404を返しますそれと)。

_content-type_を削除し、新しい権限を追加した後、例外_{"Message":"A complete signal was sent without the preceding empty frame."}_が発生します。また、パイプへの書き込みが永遠にブロックされるため、再び行き詰まってしまいます。新しいドキュメントで説明されているメッセージは古いものとは異なり、最終的にはバイナリですが、理解できません。 GoでそのようなHTTP2メッセージを送信する方法はありますか?

EDIT(Match 15、2019):*

署名の不一致に関するHTTP 403エラーが発生する場合は、_transfer-encoding_および_x-amz-content-sha256_ HTTPヘッダーを設定しないでください。それらを設定したら、AWS SDKのV4署名者でリクエストに署名すると、HTTP 403 _The request signature we calculated does not match the signature you provided._を受け取ります

13
shelll

私はAWSサポートに連絡しましたが、可能な場合はHTTP/2ではなくwebsocketを使用することを推奨しています(ブログ投稿 ここ

これがあなたのユースケースに合う場合は、次の場所にある新しいサンプルリポジトリをチェックすることを強くお勧めします: https://github.com/aws-samples/Amazon-transcribe-websocket-static これはJSのブラウザベースのソリューションを示しています。

また、デモの作者が https://github.com/brandonmwest/Amazon-transcribe-websocket-express しかし、これが機能しているかどうかは確認していません。

これらの例がPythonにないことを感謝しますが、HTTP/2ではなくWebsocketクライアントを使用すると幸運になると思います(正直に言うと、まだ少し恐ろしいです:P)。

2
Calvin

コンテンツタイプヘッダーを設定せずに、どのような応答が得られるかを確認してください。私は同じことをしようとしていますが(Rubyで)、SerializationExceptionを「修正」しました。それでも動作しませんが、考えられる新しいエラーが発生しました:)

更新:私はそれを今働いています。私の問題は署名にありました。 Hostヘッダーとauthorityヘッダーの両方が渡された場合、それらは,と結合され、署名がチェックされるときにサーバー側でHostとして扱われるため、署名は一致しません。これはAWS側では正しい動作のようには見えませんが、Goで問題になるとは思われません。

1
gpeng

私は今でもNode.jsでこの問題と戦っています。ドキュメントについて明確でないのは、 1か所 で、Content-Typeapplication/jsonであってはならないということですが、 他の場所 では、ペイロードがapplication/vnd.Amazon.eventstreamとしてエンコードされているように見えます。ペイロードは、次のようにJSONオブジェクトではなく、バイナリ形式で慎重にフォーマットする必要があるようです。

Amazon Transcribeは、文字起こしのストリーミングにイベントストリームエンコーディングと呼ばれる形式を使用します。この形式は、各イベントの内容を説明するヘッダー情報を含むバイナリデータをエンコードしました。この情報は、Amazon Transcribe SDKを使用せずにAmazon Transcribeエンドポイントを呼び出すアプリケーションに使用できます。 Amazon Transcribeは、文字起こしのストリーミングにHTTP/2プロトコルを使用します。ストリーミングリクエストの主なコンポーネントは次のとおりです。

  • ヘッダーフレーム。これには、リクエストのHTTPヘッダーと、Amazon Transcribeが後続のデータフレームに署名するためのシード署名として使用する認証ヘッダーの署名が含まれます。

  • イベントストリームエンコーディングの1つまたはメッセージフレーム。フレームにはメタデータと生の音声バイトが含まれています。

  • 終了フレーム。これは、空のボディを持つイベントストリームエンコーディングの署名付きメッセージです。

Javaを使用してすべてを 実装する方法を示すサンプル関数があります 。これにより、このエンコードの実行方法がわかりやすくなります。

0

ノードjsでWebSocket APIを使用してAWS transcribeサービスを使用するための同様の要件がありました。公式パッケージではまだこれがサポートされていないため、私は先に進んで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)
0
Muhammad Qasim