0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

サーバーに音声を送らずに、ブラウザだけで動画を文字起こしするツールを作った

0
Posted at

はじめに

動画や音声ファイルをアップロードすると文字起こししてくれるサービスはたくさんありますが、「ファイルがサーバーに送信される」ことが気になる場面も多いはずです。会議の録画、取材音声、未公開の動画など、外部に出したくない素材は意外とあります。

そこで、ファイルが端末の外に出ないことを前提に、ブラウザ内だけで完結する文字起こしツールを自作しました。本記事はその実装メモです(自作ツールの紹介を含みます)。

完成品はこちらで公開しています:
https://mytoolster.com/ja/video-to-text

mytoolster-video-to-srt-1.png

なぜブラウザでやるのか

理由は3つあります。

  1. プライバシー:音声データがネットワークに流れない。推論がローカルで完結するので、機密性の高い録音でも扱えます
  2. サーバーコストがゼロ:推論はユーザーの端末で走るため、GPUサーバーの維持費がかかりません。無料で公開し続けられます
  3. 登録不要で使える:アカウントもAPIキーも要らないので、開いてすぐ使えます

代わりに、端末の性能に依存する・初回のモデルダウンロードが必要、といった制約はあります。このトレードオフをどう実装で吸収したかを書いていきます。

全体の構成

処理の流れはシンプルです。

  1. ファイルを読み込み、Web Audio API でデコードする
  2. OfflineAudioContext で 16kHz モノラルにリサンプルする
  3. Web Worker 上の Transformers.js で Whisper を推論する
  4. 結果をテキスト(字幕用途なら SRT)としてダウンロードする

音声抽出に ffmpeg.wasm は使っていません。ブラウザがデコードできる形式であれば、Web Audio API だけで十分に処理でき、余分な数十MBのWasmバイナリを読み込まずに済みます。

音声の前処理

Whisper は 16kHz モノラルの音声を前提としているので、入力を揃えます。

// 抜粋・簡略化
const arrayBuffer = await file.arrayBuffer()
const decoded = await audioCtx.decodeAudioData(arrayBuffer)

const offlineCtx = new OfflineAudioContext(
  1, // モノラル
  Math.ceil(decoded.duration * 16000),
  16000
)
const source = offlineCtx.createBufferSource()
source.buffer = decoded
source.connect(offlineCtx.destination)
source.start()

const resampled = await offlineCtx.startRendering()
const audioData = resampled.getChannelData(0) // Float32Array

推論は Web Worker で

推論をメインスレッドで回すと UI が固まるので、Transformers.js の automatic-speech-recognition パイプラインは Web Worker の中で動かしています。

モデルは ONNX 化された Whisper(onnx-community の whisper-tiny / base / small)で、デフォルトはバランスの良い base です。デバイスは WebGPU が使える環境では WebGPU、使えない環境では WASM にフォールバックします。ページ側で対応状況を判定してバッジ表示するようにしたら、「自分の環境で動くのか?」という不安はだいぶ減りました。

ハマったところ

1. モデルの配信と初回ロード

Hugging Face Hub からだけモデルを取得する構成だと、初回ダウンロードが失敗したときに利用者を待たせてしまいます。そこで、モデル一式は 自前の CDN にもミラーしてあり、Hugging Face からの取得に失敗した場合はそちらにフォールバックする構成にしました。

  • レスポンスには Cache-Control: public, max-age=31536000, immutable を付与
  • Transformers.js 側は env.useBrowserCache を有効化し、Cache API に保存

これで2回目以降の起動はダウンロードなしで開始できます。モデル選択肢(tiny / base / small)を残したのは、端末性能と精度のバランスを利用者に選んでもらうためです。

2. ブラウザがデコードできる形式に依存する

Web Audio API で抽出する方式の弱点はここです。ブラウザがデコードできないコーデックのファイルは処理できません。ffmpeg.wasm を積めば対応範囲は広がりますが、読み込みサイズと引き換えになります。「プライバシーと手軽さ」を優先した今回の設計では、割り切った部分です。

3. 長い音声はメモリを食う

16kHz モノラルの Float32 は 1秒あたり約64KB です。数時間の録音になると、波形を丸ごとメモリに抱える現在の実装では重くなります。チャンク分割して逐次処理するのが正攻法ですが、まずは「短〜中尺の素材を手早く」用途に絞って公開しました。

まとめ

  • 音声抽出は Web Audio API、推論は Transformers.js + Whisper(ONNX)を Web Worker で
  • WebGPU 優先、WASM フォールバック
  • モデルは自前 CDN にもミラーし、ブラウザキャッシュとあわせて初回コストを吸収
  • ファイルは端末の外に出ないので、サーバーコストゼロで無料公開できる

「サーバーで推論するのが当たり前」という前提を疑うと、ブラウザ推論は小規模なツールにとってかなり現実的な選択肢でした。同じような構成で作っている方の参考になれば嬉しいです。

ツール本体:https://mytoolster.com/ja/video-to-text
(MyToolster という無料ツール集の1つとして公開しています。他のツールもすべてブラウザ内で処理します。)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?