動画の変換や圧縮、切り出し、フィルター処理で使われるFFmpegは、もともとPCやサーバー上で動かすネイティブアプリケーションです。ところが現在は、FFmpegをWebAssemblyへコンパイルすることで、ブラウザの中でも動画の読み込み、Filter Graphの処理、再エンコード、MP4の生成までかなりの範囲を実行できます。
ただし『FFmpegがブラウザで動く』ことと、『PC版FFmpegと同じ条件で動く』ことは別です。特にSingle-threadとMulti-threadでは、使えるCPUだけでなく、SharedArrayBuffer、Web Worker、cross-origin isolation、HTTPレスポンスヘッダーまで関係します。Browser KittyのFFmpeg Filter Builderでも、持ち運びやすいSingle-thread版と、HTTP(S)配信を前提にしたMulti-thread版を分けています。
ST / MTを理解するための5つの要素
FFmpegをブラウザへ持ち込む
FFmpegのソースコードをWebAssembly向けにビルドし、ブラウザのサンドボックス内で実行します。どのCodec・Filter・外部ライブラリを含めるかはビルド構成で決まります。
重い処理をUIから分離する
Single-thread版でもFFmpeg WASMをWeb Worker側で動かせます。『Single-thread』は、ページ全体が1スレッドしか使えないという意味ではありません。
複数Workerで同じメモリを共有する
Multi-thread版では、複数のWorkerから同じWebAssemblyメモリへアクセスして同期するためSharedArrayBufferを使います。EmscriptenのPthreads実装を支える重要な仕組みです。
MT版は配信条件まで変わる
SharedArrayBufferを使う一般的なWebページではcross-origin isolationが必要です。COOPとCOEPをHTTPレスポンスで設定するため、単にHTMLをダブルクリックするST版とは配布条件が変わります。
速さ以外のコストも見る
Multi-thread化はCPUをより広く使える一方、Worker、同期、共有メモリ、配信設定が増えます。処理内容によって速度差も変わるため、用途に合わせてST / MTを選ぶ必要があります。
FFmpegがブラウザに入っているのではなく、WebAssembly版を動かしている
ChromeやEdgeがffmpegコマンドをそのまま提供しているわけではありません。FFmpegのソースコードをEmscriptenなどでWebAssemblyへコンパイルし、JavaScriptからその実行環境を操作します。ブラウザがユーザーのPC上の任意パスを自由に読むことはできないため、入力もFile APIなどを通じてユーザーが明示的に選択したデータを渡します。
WebAssembly化してもFFmpegの全機能が自動的に入るわけではありません。Decoder、Encoder、Muxer、Filter、外部ライブラリのどれを含めるかで、WASMのサイズと対応機能が変わります。つまり『デスクトップFFmpegで動くコマンドならブラウザでも必ず動く』とは限りません。
ブラウザのサンドボックスとFile APIの条件の中でFFmpegを実行する
Single-threadでも、動画処理をUIのメインスレッドから外せる
Single-threadという名前から『ブラウザの画面も含めて1本のスレッドですべて動く』ように見えますが、そうとは限りません。FFmpeg WASMそのものをWeb Workerで動かせば、UIを管理するメインスレッドとは分離できます。重い動画処理をWorker側へ置き、進捗だけをメイン側へ返す構成にできます。
ここでいうSingle-threadは主に、FFmpeg側の処理が複数のPthreadsを使わない構成だと考えると分かりやすくなります。ST版にも、SharedArrayBufferやcross-origin isolationが不要で配布しやすいという明確な利点があります。
Multi-thread化の中心にあるのがSharedArrayBuffer
FFmpeg内部で複数スレッドを使うには、各Workerが独立したコピーを持つだけでは足りません。複数の実行コンテキストが同じWebAssemblyメモリを共有し、同期できる必要があります。そのためEmscriptenのPthreadsではSharedArrayBufferが使われます。
SharedArrayBufferは複数のWorkerから同じメモリ領域を参照でき、Atomicsと組み合わせて同期できます。この仕組みによって、ブラウザ上でもNative側のマルチスレッドに近い実行モデルを作れます。ただし、Worker作成や同期にもコストがあるため、スレッド数を増やせば比例して速くなるわけではありません。
SharedArrayBufferを使うと、HTMLの配信方法まで変わる
現在の主要ブラウザでSharedArrayBufferをWebページから使う一般的な構成では、Secure Contextとcross-origin isolationが必要です。cross-origin isolationは、代表的にはCross-Origin-Opener-Policy: same-originとCross-Origin-Embedder-Policy: require-corp(またはcredentialless)をHTTPレスポンスで設定して成立させます。ページ上ではcrossOriginIsolatedで状態を確認できます。
ここがST版との大きな違いです。HTTPレスポンスヘッダーはfile://でHTMLを直接開いたときには付けられません。そのため、Browser KittyのFFmpeg Filter Builderでは標準ST版はfile://でも使える単一HTMLとして残し、MT版はHTTP(S) + cross-origin isolationを前提にしています。Cross-Origin-Resource-Policy: same-originもBrowser Kittyでは追加していますが、これはcrossOriginIsolated成立そのものの必須条件ではなく、同一オリジン用途へ絞る追加の制限です。
| 観点 | Single-thread | Multi-thread |
|---|---|---|
| FFmpeg側 | 1スレッド構成 | 複数Pthreadsを利用 |
| SharedArrayBuffer | 不要 | 必要 |
| 配信 | file:// またはHTTP(S) | cross-origin isolation付きHTTP(S) |
| 持ち運び | 単一HTMLを直接開きやすい | サーバー側または代替手段が必要 |
| 主な利点 | 互換性・配布しやすさ | CPU並列性を使える |
Multi-threadなら必ず速い、ではない
動画の再エンコードや複雑なFilter GraphはCPU負荷が大きいため、複数コアを使えるMT版が有利になる場面があります。ffmpeg.wasmの公開ベンチマークにも、特定の変換でMulti-thread版がSingle-thread版より高速だった例があります。ただし、これは特定環境・特定バージョン・特定処理での結果であり、すべての処理が同じ比率で速くなることを意味しません。
処理には並列化しやすい部分としにくい部分があり、Worker間同期、メモリ管理、スレッド起動にもコストがあります。またMT版はWorkerや共有メモリを使うぶん、メモリ使用量が増えやすくなります。ブラウザ内の動画処理では、CPUより先に端末メモリが実用上の上限になることもあります。
Browser Kittyでは、MTが必要なアプリだけをcross-origin isolatedにする
cross-origin isolationはサイト全体へ一律に付ければよいとは限りません。COEPを有効にすると、別オリジンの画像、iframe、スクリプトなどの読み込み条件が変わり、広告や外部埋め込みにも影響し得ます。そこでBrowser Kittyでは、Azure Static Web Appsのルート設定で/apps/ffmpeg-filter-builder.htmlだけにCOOP / COEPを付け、他のツールやポータルページは従来どおりにしています。
FFmpeg Filter BuilderのGitHub Pages用MTページは、サーバー側で必要なヘッダーを自由に付けられない場合に備えて、crossOriginIsolatedがfalseのときだけ同一originのcoi-serviceworkerを補助的に読み込む構成です。一方Browser Kitty本番ではサーバー側ヘッダーで最初からcrossOriginIsolated === trueにできるため、そのfallbackに依存しません。
ブラウザ版FFmpegはNative FFmpegの置き換えではなく、別の使いどころがある
ブラウザ版FFmpegの強みは、インストールせずに使えること、選択した動画をアプリからサーバーへアップロードせずに処理できること、GUIや固定Recipeと組み合わせやすいことです。数本の動画へその場で処理をかけたい用途では、CLIを用意するより扱いやすい場合があります。
一方、大量ファイルのバッチ処理、長時間・高解像度動画、幅広いCodecや外部ライブラリ、最大限の速度が必要な処理ではNative FFmpegが適しています。WebAssemblyによってNative FFmpegと同じ実行環境になるのではなく、『これまでブラウザでは難しかった処理を、ブラウザの制約の中へ持ち込める』と考える方が実態に近いです。
速度だけでなく、配布方法・互換性・メモリまで含めて選ぶ
FFmpeg Filter Builder
FFmpeg Filter Graphをノードで組み、Previewで確認してH.264/AAC MP4を書き出す。Browser Kittyでは高速なMulti-thread版を利用します。
注意点
- Single-thread版でもWorkerへ処理を分けられるため、『ST = UIが必ず固まる』ではありません。UIスレッドとFFmpeg内部のスレッド数は分けて考える必要があります。
- SharedArrayBufferが必要だからといって、サイト全体へCOOP / COEPを付ける必要はありません。外部埋め込みへの影響を確認し、必要なルートだけに設定する方法もあります。
- 完全ローカル処理とオフライン起動は別です。MT版はHTTP(S)配信が必要でも、選択した動画そのものを外部サーバーへ送らずに処理できます。
- 大きな動画ではCPUだけでなくメモリも確認します。MT化によって高速化しても、端末メモリが先に限界になる場合があります。
よくある質問
Single-thread版は、動画処理中に画面が操作できなくなりますか?
必ずしもそうではありません。FFmpeg WASMをWeb Workerで実行すれば、UIを管理するメインスレッドとは分離できます。Single-threadは主にFFmpeg内部で複数Pthreadsを使わない構成を指します。
SharedArrayBufferがあればMulti-thread版はどこでも動きますか?
ブラウザの機能だけでは足りません。通常はSecure Contextとcross-origin isolationが必要で、HTTP(S)配信時にCOOP / COEPなどの条件を満たす必要があります。また、Permissions-Policy: cross-origin-isolatedで明示的に禁止されている場合は利用できません。既定値はselfなので、通常のトップレベルページでは追加設定は不要です。実際の対応状況はブラウザと配信環境の両方で確認します。
COOP / COEPに加えてCORPも必須ですか?
crossOriginIsolatedを成立させる直接の組み合わせは、一般的にはCOOP: same-originとCOEP: require-corpまたはcredentiallessです。CORPは個々のリソースをどのオリジンから利用できるか制限する別のヘッダーで、Browser KittyではFFmpeg Filter Builderへ追加の制限としてsame-originを設定しています。
Multi-thread版ならSingle-thread版より必ず速いですか?
必ずではありません。重い再エンコードなどで有利になる場合がありますが、並列化しにくい処理やWorker同期のコストもあります。端末のCPU・コア数・メモリ、入力動画、Filter Graphによって差は変わります。
Multi-thread版は完全ローカル処理ではないのですか?
いいえ。最初にアプリHTMLを取得する通信と、選択した動画の処理場所は別です。Browser Kitty版ではHTMLはWebから取得しますが、入力動画、Preview、Full Renderはブラウザ内で処理し、選択した動画をアプリから外部サーバーへアップロードしません。
参考資料
SharedArrayBufferとcross-origin isolation、Emscripten Pthreads、FFmpeg WebAssemblyの性能・構成については以下の公式資料・プロジェクト資料を参照しています。Browser Kitty固有のST / MT配信構成はFFmpeg Filter Builderの公開リポジトリに基づきます。
- MDN Web Docs SharedArrayBuffer
- MDN Web Docs Cross-Origin-Embedder-Policy (COEP)
- Emscripten Pthreads support
- ffmpeg.wasm Performance
- web.dev A guide to enable cross-origin isolation
- GitHub / ttomohisa FFmpeg Filter Builder