【ITニュース解説】I replaced FFmpeg.wasm with WebCodecs in my browser video tools. Compression went from 97 s to 5.6 s
2026年10月27日に「Dev.to」が公開したITニュース「I replaced FFmpeg.wasm with WebCodecs in my browser video tools. Compression went from 97 s to 5.6 s」について初心者にもわかりやすく解説しています。
ITニュース概要
ブラウザ内動画ツールで、従来の重いFFmpeg.wasmからWebCodecsへ変更し、処理を高速化した。WebCodecsはブラウザの動画機能やGPUを使うため、重いエンジン不要で、動画圧縮は97秒から5.6秒に短縮。ファイル処理も劇的に速くなり、ユーザー体験が大幅に改善された。
ITニュース解説
オンラインツールサイト「DashUtils」は、画像を変換したり、PDFを編集したり、動画を圧縮したりと、多種多様な392種類の無料ファイルツールを提供している。このサイトの大きな特徴は、ユーザーのファイルをサーバーにアップロードせず、すべての処理をユーザー自身のブラウザ内で行う点だ。これはプライバシー保護の観点からも非常に優れた設計である。しかし、この方式には技術的な課題も伴っていた。
これまで動画や音声の処理には「FFmpeg.wasm」という技術が使われていた。FFmpegは非常に強力なマルチメディア処理ライブラリで、ほとんどすべての動画・音声フォーマットを扱うことができる。それをWebAssemblyという技術を使ってブラウザ上で動かしていたのがFFmpeg.wasmだ。WebAssemblyは、ウェブブラウザで高速に動作するプログラムの形式で、複雑な処理をブラウザ上で実行するのに適している。しかし、このFFmpeg.wasmにもいくつかの問題点があった。
まず、FFmpeg.wasmを初めて使う際には、約31メガバイト(圧縮時で約10メガバイト)もの巨大なエンジンファイルをダウンロードする必要があった。これはウェブサイトの読み込みを遅くし、ユーザーがツールを使い始めるまでに待たされる原因となっていた。次に、FFmpeg.wasmは基本的にCPUの単一スレッドでしか動作しなかった。動画処理のような重いタスクでは、複数の処理を同時に行えるマルチスレッド処理が望ましいが、FFmpeg.wasmでマルチスレッド版を構築するには「SharedArrayBuffer」という技術が必要となる。この技術を使うためには、ウェブサイトがセキュリティのために「クロスオリジン分離」という特別な状態である必要があり、これは外部サービスを埋め込むような他の機能との相性が悪かった。さらに、FFmpeg.wasmはCPUの力を借りて処理を行うため、GPU(Graphics Processing Unit)という画像や動画の処理に特化した高速な計算装置を利用する「ハードウェアアクセラレーション」が使えなかった。これらの制約により、例えばMP4動画をGIFアニメに変換するような処理は非常に時間がかかり、ユーザーが途中でタブを閉じてしまうほどの遅さだった。
そこで、開発者はこの状況を改善するため、新たな技術への移行を決断した。それが「WebCodecs」と「Mediabunny」の組み合わせだ。
WebCodecsは、JavaScriptからブラウザが持つ動画や音声のエンコーダー(符号化器)およびデコーダー(復号化器)を直接利用できるようにする新しいAPI(Application Programming Interface)である。これにより、ブラウザに搭載されているGPUアクセラレーション機能を活用できる場合があり、処理速度の大幅な向上が期待できる。ただし、WebCodecsは動画や音声の「生のフレームデータ」しか扱えないため、MP4やMOV、WebMといった一般的なファイル形式の読み書きを直接行うことはできない。そこで登場するのが「Mediabunny」だ。Mediabunnyは、WebCodecsの弱点を補い、様々なファイル形式を読み書きできるようにするライブラリである。そのコア部分は圧縮時で約140キロバイトと非常に軽量で、ユーザーがファイルをオープンしたときに初めて読み込まれる。これにより、初回ダウンロードの負担が大幅に軽減された。
新しい変換処理の核となる部分は、ごく短いコードで表現される。まず、入力ファイルや出力形式、動画や音声のエンコード設定などを定義し、それをMediabunnyの変換(Conversion)オブジェクトに渡す。例えば、動画のコーデックに「AVC」(H.264の別名)、解像度を720p、品質を「中」に、音声コーデックに「AAC」を指定する。もし、指定したコーデックが現在のブラウザ環境でサポートされていない場合、Mediabunnyはそのトラック(動画や音声のデータ)を変換せずに破棄し、その場合は以前のFFmpeg.wasmに処理をフォールバックさせるように設計されていた。このようにして、互換性を保ちつつ新しい技術への移行を進めた。
しかし、WebCodecsへの移行後も、いくつかの予想外の課題が発生し、さらなる最適化が必要となった。
一つ目は「サイレントフォールバック」の問題だ。最初のベンチマークでは、動画圧縮の速度だけが依然として97秒と遅いままだった。その原因は、Linux版ChromeブラウザがAACオーディオのエンコードに対応していなかったことにある。MediabunnyはAAC音声トラックをエンコードできないと判断し、それをユーザーに知らせることなくFFmpeg.wasmに処理を切り替えていたのだ。解決策として、個々のコーデック(ここではAAC)がエンコード可能かどうかを事前に確認し、もしサポートされていない場合は、約260キロバイトのWebAssemblyベースのAACエンコーダーを読み込むようにした。この修正により、圧縮時間は劇的に短縮され、5.6秒となった。この経験から、開発者は「期待したエンジンではなく、実際にどのエンジンが処理を実行したかをログに記録すること」の重要性を学んだ。
二つ目は「プログレスバーがボトルネックになっていた」という問題だ。動画や音声の変換中、進捗状況を示すプログレスバーを更新するために、フレームごと、オーディオチャンクごとにUI(ユーザーインターフェース)の更新イベントを発火させていた。これをReactの状態管理に直接渡していたため、例えば30秒のMP3ファイルをWAVに変換するだけで、ページが約1,150回も再描画されていた。変換処理自体は0.43秒で完了していたにもかかわらず、UI更新にかかる時間が5.1秒にも及んでいたのだ。この問題は、プログレスバーの更新頻度を1秒あたり10回に制限(スロットリング)することで解決し、全体の処理時間を0.62秒にまで短縮できた。
三つ目は「ブラウザのエンコーダーにはビットレートの下限がある」という点だ。例えばDiscordのようなサービスに動画をアップロードする際、ファイルサイズに制限があるため、動画のビットレートを計算して調整する必要がある。しかし、ChromeのソフトウェアH.264エンコーダーは、1080pの動画の場合、約2Mbps以下のビットレートを指定してもそれを無視し、結果として指定したよりも大きなファイルサイズで出力してしまうことが判明した。例えば10MBに抑えたい動画が14.7MBになってしまうようなケースだ。この問題に対しては、目標とするファイルサイズから適切な解像度を逆算し、必要であれば元の動画よりも低い解像度(例えば1080pから720pへ)で再エンコードすることで、目標サイズに収まるように調整する方法を導入した。これにより、60秒の1080p動画を720pに縮小して約9.3MBに収めることが、21秒で可能になった。
四つ目は「GPUからピクセルデータを読み戻す処理が遅い」という問題だ。GIFアニメーションを生成する際には、色の量子化(使える色数を減らす処理)のために、動画の各フレームの生ピクセルデータが必要となる。動画デコーダーがGPUで描画したキャンバスから「getImageData()」を使ってピクセルデータを取得すると、フレームごとに約45ミリ秒かかっていた。これはGPUからCPUへのデータコピーが発生するため、非常にコストが高い処理だった。この対策として、「createImageBitmap()」を使ってフレームを画像データとして効率的に取得し、さらに「willReadFrequently」という属性を設定したキャンバスを使うことで、データコピーを約3倍高速化した。加えて、色の量子化処理自体を「Web Workers」というブラウザのバックグラウンドで並列処理を行う仕組みを利用し、CPUのコア数に応じて複数のワーカーで分担させることで、72フレームのGIFアニメ生成時間を15.2秒から3.5秒へと大幅に短縮した。
五つ目は「ビットレートの指定が不要な再エンコードを引き起こしていた」という問題だ。動画のトリミング(切り出し)のような処理は、本来であれば動画データの内容をコピーするだけで済み、再エンコードは不要な場合が多い。しかし、これまでの実装では、常に音声のビットレートを指定していたため、Mediabunnyが不要に音声を再エンコードしてしまい、処理が遅くなっていた。そこで、本当に音声のトランスコード(変換)が必要な場合にのみビットレートを設定するように変更した結果、トリミングの処理時間は5.5秒からわずか0.31秒へと短縮された。
これらの最適化の結果、全体として目覚ましい性能向上が実現された。2016年製のノートPC(Intel i7-6820HQ、Linux環境、ハードウェア動画エンコーダーなし)での測定では、20秒の1080p動画の圧縮は97.1秒から5.6秒に、72フレームのGIF変換は15.2秒から3.5秒に、20秒クリップのトリミングは5.5秒から0.31秒に、30秒のMP3からWAVへの変換は5.1秒から0.62秒へと、それぞれ大幅に高速化された。さらに、ファイルのオープン時に31MBのエンジンをダウンロードする待ち時間も、約0.1秒程度に短縮された。
この経験から得られた重要な教訓はいくつかある。一つは、WebCodecsのようなAPI全体でサポート状況を判断するのではなく、特定のコーデック(AVCやAAC、HEVCなど)ごとにサポートされているかを詳細に検出することだ。次に、新しい技術に移行する際も、既存のエンジンをフォールバック(代替手段)として残し、ログなどを通じて実際にどのエンジンが使われたかを可視化することの重要性だ。また、開発を行う際は、最も性能の低い、または古いサポート対象環境でテストを行うべきだ。なぜなら、今回の問題の多くは、高速なMacのような高性能環境では気づかれなかったからである。そして、フレームごとやチャンクごとに頻繁に発生するイベントは、UIに影響を与える前に更新頻度を制限(スロットリング)するべきだ。最後に、動画や音声の処理においてビットレートなどの設定は、本当にその処理が必要な場合にのみ適用するように注意深く設計することが、無駄な処理を省き性能を向上させる鍵となる。これらの学びは、ブラウザ内で高性能なマルチメディア処理を実現する上で非常に価値のある指針となるだろう。