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?

ブラウザだけで動画から音声を抽出する(ffmpeg.wasm)— COOP/COEPとCSPのハマりどころ付き

0
Posted at

「動画から音声だけ取り出したい」——BGMを抜き出したり、会議の録画を音声メモにしたり、地味によくある作業です。でも無料の変換サイトは動画をサーバーにアップロードさせるものが多く、大きいファイルだと時間もかかるしプライバシーも気になります。

そこでこの記事では、動画をアップロードせず、ブラウザの中だけで音声を抽出する方法を、ffmpeg.wasm を使ってまとめます。実装で実際にハマったポイント(SharedArrayBuffer / COOP・COEP / CSP)も共有します。

ゴール:ブラウザ内で MP4 などの動画から音声を抜き出し、MP3 で保存できるようになること。


ffmpeg.wasm とは

ffmpeg を WebAssembly にコンパイルしたもので、サーバーなしでブラウザ内で ffmpeg のコマンドを実行できます。動画→音声の抽出はもちろん、フォーマット変換なども可能です。

まずは CDN 版で最小のサンプルから。

<script src="https://unpkg.com/@ffmpeg/ffmpeg@0.11.6/dist/ffmpeg.min.js"></script>
<input type="file" id="file" accept="video/*">
const { createFFmpeg, fetchFile } = FFmpeg;
const ffmpeg = createFFmpeg({ log: true });

document.getElementById("file").onchange = async (e) => {
  const file = e.target.files[0];

  await ffmpeg.load();                                   // 初回はコア(数十MB)を読み込む
  ffmpeg.FS("writeFile", "input.mp4", await fetchFile(file));

  // -vn = 映像を捨てる / libmp3lame で MP3 にエンコード
  await ffmpeg.run("-i", "input.mp4", "-vn", "-c:a", "libmp3lame", "-q:a", "2", "output.mp3");

  const data = ffmpeg.FS("readFile", "output.mp3");
  const url = URL.createObjectURL(new Blob([data.buffer], { type: "audio/mpeg" }));

  const a = document.createElement("a");
  a.href = url; a.download = "output.mp3"; a.click();
};

ポイントは ffmpeg のコマンドそのままな点です。

  • -vn:映像トラックを除外(音声だけにする)
  • -c:a libmp3lame -q:a 2:MP3 でエンコード(-q:a は品質、0が最高〜9が最低)
  • M4A なら -c:a aac -b:a 192k output.m4a、WAV なら -c:a pcm_s16le output.wav

ハマりどころ①:SharedArrayBuffer is not defined

@ffmpeg/coreマルチスレッド版(0.11系の既定など)は SharedArrayBuffer を使うため、ページが cross-origin isolation されている必要があります。これを満たさないと SharedArrayBuffer is not defined で落ちます。

サーバー側で、そのページに次の2つのレスポンスヘッダーを付けます。

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

COEP を有効にすると、そのページが読み込むクロスオリジンのリソースは原則ブロックされます(同一オリジンのものはOK)。外部の解析タグや画像などは、このページだけ外すか、CORP対応が必要になります。

ヘッダーを触りたくない場合は、シングルスレッド版のコア@ffmpeg/core 0.12系)を使うと SharedArrayBuffer 不要で、COOP/COEP なしで動きます。ただしコアのファイルサイズは大きめ(30MB前後)です。マルチスレッド版は23MB前後で速い、という違いがあります。


ハマりどころ②:CSP で blob: をブロックしてしまう

ffmpeg.wasm は内部で Blob URL のワーカーを生成し、Blob を fetch します。Content-Security-Policy を設定していると、ここが引っかかります。ローカル(CSPなし)では気づけず、本番で初めて出るので厄介です。

script-srcconnect-srcblob: を許可します(worker-src を使っている場合はそちらにも)。

Content-Security-Policy:
  script-src 'self' 'wasm-unsafe-eval' blob:;
  connect-src 'self' blob:;
  worker-src 'self' blob:;
  ...
  • 'wasm-unsafe-eval':WebAssembly のコンパイルに必要
  • blob::ワーカー生成・Blob fetch のため

ハマりどころ③:コアの自ホスティングとサイズ

createFFmpeg({ corePath }) でコアの場所を指定すれば、CDN ではなく自分のサーバーからコアを配信できます(=実行時に外部通信なし・オフライン対応)。

const ffmpeg = createFFmpeg({
  corePath: new URL("assets/ffmpeg/ffmpeg-core.js", location.href).href,
  log: false,
});

このとき、ffmpeg-core.js と同じ場所に ffmpeg-core.wasm(と、マルチスレッド版は ffmpeg-core.worker.js)を置きます。コアの .wasm20〜30MBあるので、静的ホスティングの1ファイルサイズ上限(サービスによっては25MB程度)に引っかからないか注意してください。


コードを書かずに使いたいとき

「1回だけ音声を抜きたい」「動画をアップロードしたくない」なら、ブラウザ完結のツールが手軽です。

私が作っている Morphy の「動画→音声」なら、

  • 動画を選ぶだけで MP3・M4A・WAV に抽出
  • 処理はすべてブラウザ内で完結し、動画はアップロードされません
  • 登録不要・無料

初回だけ変換エンジンの読み込みが入りますが、以降はキャッシュされます。

👉 動画→音声 抽出ツールhttps://morphytools.com/video-to-audio.html


まとめ

  • ffmpeg.wasm を使えば、サーバーなし・ブラウザだけで動画から音声を抽出できる
  • コマンドは -i input.mp4 -vn -c:a libmp3lame output.mp3 のように ffmpeg そのまま
  • マルチスレッド版は COOP/COEP が必要(SharedArrayBuffer)。避けたいならシングルスレッド版
  • CSP を使うなら blob: の許可を忘れずに(本番で初めてハマりがち)

「動画をアップロードせずに音声だけ欲しい」という需要に、ブラウザ完結はよく合います。参考になれば嬉しいです。


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?