パフォーマンス

Mediabunnyで動画を圧縮し、容量・文字・音声同期を比べた

技術ノート本文を読む ↓

Mediabunny 1.52.0で20秒の1080p動画を圧縮し、容量・文字の見え方・音声同期を13試行で比較した結果です。

動画をアップロードする前に、ブラウザ側で容量を減らす使い方を想定して、Mediabunny 1.52.0の圧縮設定を比較しました。

20秒の1080p動画では、量子化値32で20.43 MBから11.38 MBへ約44%減りました。一方、変換後には音声が映像より約40 ms遅れました。容量だけでなく、文字の見え方と音声同期を確認した結果をまとめます。

比較した設定

Mediabunnyは、JavaScriptで動画や音声の読み取り・変換・出力を扱うライブラリです。今回はブラウザのWebCodecsでH.264を再エンコードし、MP4へ出力しました。検証対象は変換処理であり、完成したアップロード画面の操作性は評価していません。

量子化値は、圧縮時にどれだけ映像の細部を省くかを調整する設定です。今回のAVCでは、値を大きくすると容量を減らしやすくなる一方、細部が失われやすくなります。

1.52.0では、quality: new Quality({ quantizer: 32 })のように量子化値を指定できます。旧bitrateフィールドからqualityへの変更は、1.52.0のリリースと公式解説に記載されています。

検証環境と入力動画

測定環境はApple M4、メモリ32 GiB、macOS 27.0.1、Chrome 155.0.8059.85のヘッドレスモードです。Mediabunnyは1.52.0、素材の作成と解析に使ったFFmpegは9.0.1です。

この実験では、コードの作成、ブラウザ操作、計測をAIエージェントが実行しました。使用したコードと測定データは付録に掲載しています。

入力は1920×1080、30 fps、20秒、600フレームの人工動画です。動く模様、12・18・24・36 pxの日本語と英数字、細い縞を入れました。音声同期の測定用に、1・5・10・15・19秒には白い点滅とビープ音も入れています。人工模様のため、人物や自然な動きの画質を代表する素材ではありません。

入力映像はlibx264のCRF16、最大8 Mbps、バッファ8 Mbpsで作成しました。実際の平均映像ビットレートは約8.158 Mbpsでした。音声はAAC-LC、48 kHz、モノラル、128 kbps指定です。入力も非可逆圧縮されているため、画質の比較対象は無圧縮の原画ではなく、この入力をデコードした映像としました。

出力はH.264、1920×1080、30 fpsに固定し、映像を強制再エンコードしました。量子化値20・26・32を各3回、「20→26→32」「32→20→26」「26→32→20」の順に変換しました。固定ビットレートは指定していません。入力のCRF16と出力の量子化値は別の設定で、数値は直接対応しません。

量子化値32で44%削減、20では容量が増えた

同じ設定の3回で出力容量は一致しました。ここでは1 MBを1,000,000 bytesとしています。

量子化値 出力容量 入力からの変化 平均映像ビットレート 変換時間の中央値 3回の時間範囲
20 30.35 MB 48.57%増加 12.129 Mbps 3.308秒 3.127〜4.310秒
26 19.82 MB 2.95%削減 7.918 Mbps 3.140秒 3.104〜3.561秒
32 11.38 MB 44.30%削減 4.539 Mbps 3.241秒 3.130〜3.365秒

量子化値26ではほとんど容量が変わらず、32では約9 MB減りました。20では逆に約49%増えており、再エンコードすれば必ず小さくなるわけではありません。

変換時間の中央値は約3.1〜3.3秒でした。Conversion.init()からexecute()の完了までを測り、入力取得、JavaScriptの読み込み、保存、解析は含めていません。通常利用中のMacで実行前の状態も厳密に揃えていないため、時間は参考値とします。

文字の輪郭には差が出る

全画面と左上1120×320 pxの文字領域について、入力と出力の600フレームを比較しました。

量子化値 全画面SSIM 全画面PSNR 文字領域SSIM 文字領域PSNR
20 0.985113 27.683 dB 0.998164 46.734 dB
26 0.983308 27.648 dB 0.997020 44.000 dB
32 0.978227 27.549 dB 0.995649 39.761 dB

SSIMは画像の構造の似ている度合い、PSNRは画素ごとの差をもとにした指標です。同じ比較条件では、SSIMは1に近いほど、PSNRは高いほど入力に近いと評価されます。

ここでは設定による変化を見るために使い、合否の基準値は設けていません。文字領域には単色の背景も多いため、高いSSIMだけで文字が読めると判断せず、切り出した画像も比較しました。

入力・2秒地点

入力の文字領域

量子化値32・同じフレーム

量子化値32の文字領域

量子化値20、26、32の15秒地点も原寸で確認できます。

2秒・15秒地点の静止画では、量子化値32で文字の輪郭の粗さが増えました。一方、18・24 pxの日本語ラベルと英数字列に、新たに判読できなくなる変化は見つかりませんでした。12 px文字やIl1、O0は入力画像でも区別しにくく、正確な読み取りを確かめられたわけではありません。

判読結果はAIによる静止画比較で、人を集めた盲検評価やOCR正解率の測定ではありません。入力の模様と量子化値32の模様も比較できますが、再生中のちらつきは未評価です。

SSIM・PSNRは各設定の初回に計算し、残り2回は映像全パケットの順序付きハッシュの一致を確認して同じ値を使用しました。

元の動画を変換すると、音声が約40 ms遅れた

量子化値を比較した9回では、5つの測定地点すべてで、入力にはなかった約40 msの音声遅延が生じました。映像は20秒・600フレームを維持しましたが、音声とMP4全体の長さは約74.7 ms増えました。

音声の設定はaudio: {}でしたが、実際にはコピーではなく再エンコードされていました。AudioEncoderのログには、毎回938回のencode呼び出しが記録されています。設定はAAC-LC、48 kHz、モノラル、192 kbpsで、出力のAACパケットも入力とは異なっていました。

入力の先頭AACパケットは時刻が−21.333 msで、1024サンプルのSkip Samples情報を持ちます。Mediabunny 1.52.0では、音声が変換開始の0秒より前から始まる場合、トリミングが必要と判断してコピー経路から外れます。1.52.0の変換処理と今回の動作は一致していました。

今回の同期の目安は「変換で追加されるずれが1フレーム、約33.3 ms以内」とし、約40 msはこれを超えました。視聴時に不快に感じる境界を測った値ではありません。

音声コピーと再エンコードを、同じ入力で比べる

音声処理の違いを調べるため、映像・音声の符号化済みパケットを保ったまま、開始時刻を非負にした補助入力を作成しました。この補助入力を使い、通常処理とaudio: { forceTranscode: true }を各2回比較しました。映像の量子化値は26に固定しています。

表のプラスは、音声が映像より遅いことを示します。

入力・音声条件 回数 音声encode呼び出し 入力の同期差 出力の同期差 変換で追加された差
元の動画・通常処理 9 各938回 約0 ms 約+40 ms 約+40 ms
補助入力・通常処理 2 0回 ほぼ0 ms 約+20 ms 約+21 ms
同じ補助入力・強制再エンコード 2 各939回 ほぼ0 ms 約+65 ms 約+66 ms

補助入力の同期差は、丸める前の検出値では−1.354 msでした。そのため、出力の同期差と、入力から追加された差には約1 msの違いがあります。

通常処理では音声encode呼び出しが0回で、AACパケットも入力と一致しました。強制再エンコードではパケットが変わり、音声の終端も入力より約74.7 ms延びました。同期差は通常処理が約20 ms、強制再エンコードが約65 msで、約45 msの差がつきました。各条件の2回、5地点で同じ結果でした。

補助比較4回の映像パケットはすべて一致し、実タイムスタンプを照合した3回では600フレームの時刻も一致しました。この環境と補助入力では、音声の強制再エンコード経路を選ぶことで、約45 msの遅れが加わったといえます。

音声は5 msのRMS窓で検出しており、表の値は概数です。遅れの何msがAAC・ブラウザ・ライブラリの各処理に由来するかや、元の動画の約40 msと同じ内部原因かは未特定です。

コピーでも約20 msずれた理由

補助入力の先頭映像フレームは約54.688 msと長く、厳密な等間隔30 fpsではありませんでした。入力では点滅が1.021354秒、ビープの検出が1.020秒などで、測定の分解能ではほぼ同期していました。

出力では音声の時刻は変わらず、点滅が1.000秒などへ約21 ms早まりました。30 fpsへ変換する際に映像側の時刻が変わったため、音声をコピーしても同期差が生じました。音声データの一致だけでなく、映像を含めた再生時刻の確認が必要になります。

この補助入力は音声処理を比較するためのもので、一般的な音ずれ修正の方法として使うことは想定していません。比較は量子化値26でのみ行い、32との組み合わせは未検証です。

変換コードと再現手順

変換では、入力と出力を設定してConversion.init()を呼び、execute()の完了を待ちます。公式の変換ガイドも参照できます。

const conversion = await Mediabunny.Conversion.init({
  input, output,
  video: {
    width: 1920, height: 1080, fit: "fill", frameRate: 30,
    codec: "avc",
    quality: new Mediabunny.Quality({ quantizer: 32 }),
    forceTranscode: true,
    hardwareAcceleration: "no-preference",
  },
  audio: {},
});
if (!conversion.isValid || conversion.discardedTracks.length) {
  throw new Error("指定したトラックを保持できない");
}
await conversion.execute();

convert.mjsに完全な処理、再現手順に素材の生成と解析方法を掲載しました。素材には付属の文字パネルを使い、フォントの違いによる変化を避けます。生成動画はハッシュで記録時の入力と照合できます。

実行前にはブラウザのAVC・量子化指定への対応を確認します。今回はVideoEncoderの実設定も確認しましたが、hardwareAcceleration: "no-preference"の指定だけでは、全処理がハードウェアで行われたとは判断できません。

今回はBufferTargetで動画全体をメモリに保持しました。検証は1台のMacで行っており、長時間動画やメモリの少ない端末での動作は未確認です。

まとめ

今回の素材では、容量を減らす設定として量子化値32が候補になりました。ただし、音声には約40 msの遅れが出ています。実際に使う場合は、圧縮後の容量とプレビューに加え、音声と映像の時刻も確認する必要があります。

測定データと解析上の注記

測定日は2026年10月20日です。集計対象は量子化値比較9回と音声比較4回の計13回で、予備実行と中断した試行は含めていません。実測データと試行別の数値を付録に掲載しました。版固有の動作はMediabunny 1.52.0のソースと照合しています。

補助入力の同期解析は、フレーム番号を30で割る初期の方法から、デコード後の実フレーム時刻を使う方法へ訂正しました。本文と付録は訂正後の値で、元の動画を使った量子化値比較の数値に変更はありません。