Open6

S3にアップロードされた動画/音声ファイルの文字起こし

Makio TsukamotoMakio Tsukamoto

なにをするのか

S3にファイルをアップロードしたら、自動的に文字起こしを開始したい。

Amazon Transcribeによる文字起こしは、S3に動画または音声をアップロードして、アップロードしたオブジェクト(≒ファイル)のURIを確認し、そのURIを指定してTranscribeジョブを作成し、完了を待つといった手順で、久しぶりにやろうと思うとやり方を忘れてたり億劫だったりする。これを自動化したい。

具体的には

S3(アップロード)→EventBridge→StepFunction→Transcribe→S3(テキスト格納)の流れを想定している。

Makio TsukamotoMakio Tsukamoto

Copilot(bing AI)の提案

どうやればいいか、まずCopilot(旧bing AI)に相談してみた。以下回答。


こんにちは、Copilotです。S3にアップロードされたmp4を文字起こしする方法についてお答えします。😊

Step Functionsは、AWSのさまざまなサービスを組み合わせてワークフローを作成できるサービスです。S3にアップロードされたmp4を文字起こしするには、以下のようなステップが必要です。

  • S3にmp4ファイルがアップロードされたことを検知するイベントをEventBridgeで作成する
  • EventBridgeのイベントをトリガーにして、Step Functionsのステートマシンを実行する
  • ステートマシンでは、S3からmp4ファイルを取得し、Transcribeで音声をテキストに変換する
  • Transcribeの結果をS3に保存するか、他のサービスに渡す

このようなステートマシンを作成するには、以下の記事が参考になります。

  • Amazon S3 イベント発生時にステートマシンの実行をスタートする(※2)
  • EventBridge を使用して、S3 イベント発生時にStep Functionsを実行する(※3)
  • S3 Event Trigerから共通State Machine(Step Function)を実行する(※5)

これらの記事では、S3にアップロードされたファイルを処理するためのステートマシンの例が紹介されています。mp4ファイルを文字起こしする場合は、ステートマシンの中でTranscribeのAPIを呼び出す必要があります。TranscribeのAPIについては、以下のドキュメントを参照してください。

TranscribeのAPIを呼び出すには、Step FunctionsのTaskステートを使用します。Taskステートでは、ResourceにTranscribeのAPIのARNを指定し、ParametersにAPIのパラメータを指定します。例えば、以下のようなTaskステートを作成できます。

"Transcribe mp4": {
  "Type": "Task",
  "Resource": "arn:aws:states:::transcribe:startTranscriptionJob.sync",
  "Parameters": {
    "LanguageCode": "ja-JP",
    "Media": {
      "MediaFileUri.$": "$.detail.object.key"
    },
    "MediaFormat": "mp4",
    "OutputBucketName": "your-output-bucket",
    "TranscriptionJobName.$": "$.detail.object.key"
  },
  "Next": "Next state"
}

このTaskステートでは、S3にアップロードされたmp4ファイルのキーをMediaFileUriに渡し、Transcribeで日本語の音声をテキストに変換します。変換したテキストは、OutputBucketNameで指定したS3バケットに保存されます。TranscriptionJobNameは、Transcribeのジョブの名前を指定します。ここでは、S3のキーと同じ名前にしていますが、任意の文字列を指定できます。

このようにして、S3にアップロードされたmp4ファイルを文字起こしするステートマシンを作成できます。もし、文字起こししたテキストを他のサービスに渡したい場合は、ステートマシンにさらにステートを追加して、必要な処理を行うことができます。

以上が、S3にアップロードされたmp4を文字起こしする方法についての説明です。ご参考になれば幸いです。😊

ソース: Bing との会話 2024/2/4

  1. Amazon S3 イベント発生時にステートマシンの実行をスタートする
  2. EventBridge を使用して、S3 イベント発生時にStep Functionsを ...
  3. S3 Event Trigerから共通State Machine(Step Function)を ...
  4. Step Functions データフローシミュレーターでステートの入出力 ...
  5. 【AWS】EventBridgeによるS3ファイルアップロードイベント起動の ...
Makio TsukamotoMakio Tsukamoto

Step FunctionsステートマシンでS3バケットイベントを受ける

EventBridge イベントメッセージの構造」のメッセージが来るはずではあるけど、ステートマシンを作成して実際に受けてみる。

ステートマシンの作成

  1. Step Functionsを開く。
  2. 「ステートマシンの作成」をクリック。
  3. 「テンプレートの選択」で「Blank」を選択した状態のまま、「選択」をクリック。
  4. 編集画面(Workflow Editor)が開く。左の状態リストで「フロー」タブから「Pass」を選び、中央のビジュアルエディタ部の「最初の状態をここにドラッグ」にドラッグ&ドロップする。
  5. 編集画面上部の「設定」を開き、「ステートマシン名」を「MyTranscribe」に変更する。
  6. 「作成」をクリックし、「ロールの作成を確認」で「確認」をクリックして保存する。

編集画面で「Code」を開くと、以下のようなコードになっている。

{
  "Comment": "ステートマシンの説明",
  "StartAt": "Pass",
  "States": {
    "Pass": {
      "Type": "Pass",
      "End": true
    }
  }
}

S3イベントの受信

  1. ステップ 3: Amazon EventBridge ルールを作成する - ​Amazon S3 イベント発生時にステートマシンの実行をスタートする - AWS Step Functions」の手順で、S3イベントからステートマシンが呼び出されるように設定する。
  2. S3バケットにファイルをアップロードする。
  3. Step Functions > ステートマシン」からステートマシンを開く。
  4. 「実行」にアップロード直後の日時で実家王履歴があることを確認する。
  5. 4.のアップロード履歴を開き、「グラフビュー」で追加した「Pass」をクリックする。右の「ステップの詳細」にPassの実行結果が表示されるので「入力」タブで受け取っている入力を確認する。

受信したイベントメッセージ

こんなメッセージに(想定通り)なっていた。

{
  "version": "0",
  "id": "17f7614b-fbe5-5641-9f3e-07f4ba37dfcd",
  "detail-type": "Object Created",
  "source": "aws.s3",
  "account": "501593450062",
  "time": "2024-02-04T09:28:44Z",
  "region": "ap-northeast-1",
  "resources": [
    "arn:aws:s3:::transcribe-20220903"
  ],
  "detail": {
    "version": "0",
    "bucket": {
      "name": "transcribe-20220903"
    },
    "object": {
      "key": "source/ほげほげ.mp4",
      "size": 1523390,
      "etag": "bac516b13b9c06970b01f250e5ff4419",
      "sequencer": "0065BF58CCBBBF37C0"
    },
    "request-id": "66SBB8V4TF71Z77B",
    "requester": "501593450062",
    "source-ip-address": "123.456.789.1",
    "reason": "PutObject"
  }
}
Makio TsukamotoMakio Tsukamoto

ステートマシンで文字起こしを開始する

文字起こしに使用するのは、Amazon Transcribeの StartTranscriptionJob API。

APIパラメータの検討

引き渡すAPIパラメータの詳細は以下のドキュメント参照。

最低限指定しておきたいパラメータは、こんな感じか。

{
   "Media": { 
      "MediaFileUri": "s3://ソースのバケット名/ソースのオブジェクトキー"
   },
   "IdentifyMultipleLanguages": true,
   "OutputBucketName": "出力先バケット名",
   "OutputKey": "出力先オブジェクトキー",
   "TranscriptionJobName": "ジョブ名"
}

注意点として、ジョブ名には空白や / などが使えず、出力先オブジェクトキーには、空白などが使えない。

入力値の使用

ソースのバケット名ソースのオブジェクトキー など、入力値の使用方法は下記。

message という名前の指定されたノードの値をパラメータとして渡すには、次の構文を指定します。
"Parameters": {"myMessage.$": "$.input.message"},
パスを使用して状態入力をパラメータとして渡す - サービス API にパラメータを渡す - AWS Step Functions

バケット名とキーからS3 URIを作成するような文字列操作は、Step Functionsの組込み関数を使う。

これらをPass状態のパラメータで使って、まずuuidbucket(バケット名)、key(オブジェクトキー)だけにする。

  1. ワークフローの編集画面で、既存のPassを選択する。
  2. Passの「入力」タブを開き、「Parametersを使用した入力を変換」にチェックし、以下のJSONを指定する。
    {
      "uuid.$": "States.UUID()",
      "bucket.$": "$.detail.bucket.name",
      "key.$": "$.detail.object.key"
    }
    

次に、もう一つPassを追加して、StartTranscriptionJob APIパラメータなど以降の処理で使うためのパラメータに整形しておく。

  1. ワークフローの編集画面で、既存のPassの次にもう一つPassを挿入する(PassEndの間の矢印上にドラッグ&ドロップする)。
  2. Passの入力タブを開き、「Parametersを使用した入力を変換」にチェックし、以下のJSONを指定する。
    {
      "MediaFileUri.$": "States.Format('s3://{}/{}', $.bucket, $.key)",
      "OutputBucketName.$": "$.bucket",
      "OutputKeyTemporary.$": "States.Format('transcribe-{}.json', $.uuid)",
      "OutputKeyPermanent.$": "States.Format('{}.json', $.key)",
      "TranscriptionJobName.$": "States.Format('transcribe-{}', $.uuid)"
    }
    
  3. 実行して、二つ目のPassの「出力」タブで変換結果が期待通りであるか確認する。

文字起こしの追加

  1. ワークフローの編集画面で、二つ目の Pass の次に、アクションタブの「機械学習 > Amazon Transcribe > StartTranscriptionJob」を挿入する。
  2. StartTranscriptionJobのAPIパラメータに以下を指定する。
    {
      "Media": {
        "MediaFileUri.$": "$.MediaFileUri"
      },
      "IdentifyMultipleLanguages": true,
      "OutputBucketName.$": "$.OutputBucketName",
      "OutputKey.$": "$.OutputKeyTemporary",
      "TranscriptionJobName.$": "$.TranscriptionJobName"
    }
    
  3. 実行(S3バケットにmp3ファイル等をアップロード)して、トランスクリプションジョブ
    が作成されることを確認する。

この時点でのステートマシンコード

{
  "Comment": "ステートマシンの説明",
  "StartAt": "Pass",
  "States": {
    "Pass": {
      "Type": "Pass",
      "Next": "Format input",
      "Parameters": {
        "uuid.$": "States.UUID()",
        "bucket.$": "$.detail.bucket.name",
        "key.$": "$.detail.object.key"
      }
    },
    "Format input": {
      "Type": "Pass",
      "Parameters": {
        "MediaFileUri.$": "States.Format('s3://{}/{}', $.bucket, $.key)",
        "OutputBucketName.$": "$.bucket",
        "OutputKeyTemporary.$": "States.Format('transcribe-{}.json', $.uuid)",
        "OutputKeyPermanent.$": "States.Format('{}.json', $.key)",
        "TranscriptionJobName.$": "States.Format('transcribe-{}', $.uuid)"
      },
      "Next": "StartTranscriptionJob"
    },
    "StartTranscriptionJob": {
      "Type": "Task",
      "End": true,
      "Parameters": {
        "Media": {
          "MediaFileUri.$": "$.MediaFileUri"
        },
        "IdentifyMultipleLanguages": true,
        "OutputBucketName.$": "$.OutputBucketName",
        "OutputKey.$": "$.OutputKeyTemporary",
        "TranscriptionJobName.$": "$.TranscriptionJobName"
      },
      "Resource": "arn:aws:states:::aws-sdk:transcribe:startTranscriptionJob",
    }
  }
}
Makio TsukamotoMakio Tsukamoto

ブラッシュアップ

自動的に文字起こし(Transcribeジョブ作成)する基本機能ができたので、あとはいろいろとブラッシュアップ。

".mp3"と".mp4"ファイルでのみ動作するように修正

現在のところファイル種別関係なしにステートマシンが起動され、文字起こしジョブが作成するテンポラリファイルや文字起こし結果のJSONファイルに対しても動作してしまう。拡張子(後方一致=Suffix)で対象を限定する。

  1. EventBridge > ルール」で、「Step FunctionsステートマシンでS3バケットイベントを受ける」で作成したルールを開く。
  2. 「イベントパターン」の「編集」をクリックする。
  3. イベントパターンのJSONを以下に変更する。detail > object > keyに対して、suffixの確認を追加している。
    {
      "source": ["aws.s3"],
      "detail-type": ["Object Created"],
      "detail": {
        "bucket": {
          "name": ["transcribe-20220903"]
        },
        "object": {
          "key": [
            { "suffix": ".mp3" },
            { "suffix": ".mp4"}
          ]
        }
      }
    }
    
  4. 「次へ」をクリックしてウィザードを進めていき、「ルールの更新」を実行する。

言語候補と話者分割の指定

文字起こし時に、スピーカーの分割してみる。これをやろうとすると精度にセンシティブになるかもと思った(根拠レス)ので、言語候補も指定してみる。具体的には、StartTranscriptionJobのAPIパラメーターで以下のように、 LanguageOptionsSettings > ShowSpeakerLabels を指定する。

{
  "Media": {
    "MediaFileUri.$": "$.MediaFileUri"
  },
  "IdentifyMultipleLanguages": true,
  "LanguageOptions": [
    "ja-JP",
    "en-US"
  ],
  "Settings": { 
    "ShowSpeakerLabels": "TRUE"
  },
  "OutputBucketName.$": "$.OutputBucketName",
  "OutputKey.$": "$.OutputKeyTemporary",
  "TranscriptionJobName.$": "$.TranscriptionJobName"
}