こんにちは、takatein です。
突然ですが、みなさんは会議録画ってちゃんと見返してますか?
ぶっちゃけ、わたしはほぼ見返さないです。1 時間の Google Meet 録画が日々溜まっていって、「あとで誰かに共有したいやつだけ抜き出したい」と思いつつ、結局そのまま放置…これ「録画見ない問題」、あるあるじゃないですか?
特に「優先度高い打ち合わせが入ってデイリーの打ち合わせ出れない」のような人向けに展開したいなーと思っていました。
そこで、会議録画を「60 秒のハイライト動画」に自動圧縮するパイプラインを Mac 上で作ってみました!
組み合わせた技術はこちらです。
- mlx-whisper(Apple Silicon に最適化された Whisper 実装)で文字起こし
- LLM(Claude Code に直接処理させる)でトピック抽出
- Remotion(React で動画を作るフレームワーク)4.x で動画合成
会議録画の mp4 を投げると 60 秒の mp4 が出てくる、というところまで持っていきます!
想定読者は、Whisper や Remotion を一度は触ったことがある個人開発者です。コピペでひと通り動くハンズオン記事として書いてみました。
入出力と作業時間
ざっくり書くと、入力は「1 時間前後の会議録画 mp4」と「Gemini Meet の議事録要約」。
出力は「60 秒のテロップ付きハイライト mp4」で、3〜4 トピックを抜粋してくれます。
所要時間は、初回 10〜15 分、2 回目以降は 2〜5 分くらい(M3 Mac 24GB で計測)。これなら「会議終わってから 15 分後にはハイライト動画が共有される」みたいな運用ができそうです。
パイプラインの全体像
処理は 7 つのフェーズに分かれます。
Phase 0 symlink 層を作る(日本語ファイル名対策)
Phase 1 音声を抽出(16kHz mono WAV)
Phase 2 文字起こし(mlx-whisper、word-level timestamps 付き)
Phase 3 トピック × 時刻マッチング(LLM)
Phase 4 60 秒分のハイライト選定(LLM)
Phase 5 クリップ切り出し(CFR 30fps 再エンコ)
Phase 6 Remotion でレンダ
Phase 3 と 4 は Claude Code に「 topics.json 作って」「 highlights.json 作って」と頼むだけで終わらせています。
embedding モデル(テキストの意味をベクトル化するモデル)を別途ダウンロードしなくていいのが、地味ですがかなりラクでした!
使った環境
| 項目 | 必須条件 |
|---|---|
| OS | macOS 14+(Apple Silicon 推奨) |
| RAM | 16GB+ |
| ディスク | 10GB+ 空き |
| Node.js | 18+ |
| Python | 3.11(uv 経由) |
ちなみに Python は 3.14 系だと mlx-whisper の ImportError でハマります(詳細は後述の「ハマったところ」で)。あとのセットアップ手順では 3.11 を明示します。
セットアップしてみる
コマンドラインツールを入れる
ターミナルで以下を実行します。
brew install ffmpeg jq uv
brew install node # 未インストールの場合
Python 仮想環境を作る
リポジトリのルートに移動して、以下を実行します。
cd <your-repo>
uv venv --python 3.11 meeting_rec/.venv
source meeting_rec/.venv/bin/activate
uv pip install mlx-whisper
このあと、初回実行時に Hugging Face から Whisper モデル(約 1.5GB)が落ちてきます。1〜5 分ほど、コーヒーを淹れる時間にちょうどいい長さです。
Remotion 側を入れる
cd meeting_rec/remotion
npm install
200 パッケージほど、20〜40 秒で終わります。Chromium Headless Shell(90MB)は最初のレンダ時に勝手に取得されるので、初回だけ少し待ちます。
素材を置いてみる
会議ごとに meeting_rec/rec/<日付>/ フォルダを作って、原本素材を入れます。
meeting_rec/rec/
└── 2025_12_04/
├── Meeting Recording.mp4
├── geminiまとめ.txt
└── Meeting Chat.sbv (任意)
| ファイル | 必須/任意 | 用途 |
|---|---|---|
*.mp4 |
必須(1 本のみ) | 会議録画本体 |
geminiまとめ.txt |
必須 | Gemini Meet の要約。トピック分割の元ネタ |
*.sbv |
任意 | Meet のチャットログ。時刻アンカーとして参考利用 |
ちなみにファイル名に日本語や全角の ~ が入っていても大丈夫です!
最初は素直に日本語ファイル名のまま ffmpeg に渡してドハマりしたんですが、Phase 0 で ASCII 名の symlink に張り直す層を入れたら一気に安定しました。ありがとうClaudeCode。最近シェルも書かなくなりましたねw
「日本語ファイル名はとりあえず symlink」、シェル芸人やってた頃を思い出しました。
動かしてみる
cd <your-repo>
# Phase 0 symlink 層を作る(rec/ 直下の最新フォルダを自動選択)
bash meeting_rec/scripts/00_setup_symlinks.sh
# Phase 1 音声抽出
bash meeting_rec/scripts/10_extract_audio.sh
# Phase 2 文字起こし
source meeting_rec/.venv/bin/activate
python meeting_rec/scripts/20_transcribe.py
# Phase 3-4 Claude Code に topics.json と highlights.json を作ってもらう
# → 「transcript.json と geminiまとめ.txt から topics.json を作って」と依頼
# → 「topics.json から上位 3〜4 トピックで 60 秒の highlights.json を作って」と依頼
# Phase 5 クリップ切り出し
bash meeting_rec/scripts/50_cut_clips.sh
# Phase 6 Remotion レンダ
cd meeting_rec/remotion
npx remotion render Highlight60 ../out/highlight_60s.mp4 \
--props=../transcribe/highlights.json --concurrency=4
open ../out/highlight_60s.mp4
過去日の素材を処理したいときは、Phase 0 で DATE=2025_11_20 のように環境変数で日付フォルダを指定すれば OK です。
カスタマイズしたいとき
テロップ文だけ直したい
meeting_rec/transcribe/highlights.json の caption / subCaption を編集して、Phase 6 のレンダだけ叩き直せば反映されます。クリップ自体は再生成されないので、文字直しだけならめっちゃ早いです。
クリップ区間を変えたい
同じく highlights.json の sourceStartSec / sourceEndSec / durationSec を編集(合計 60 秒を維持)したあと、
bash meeting_rec/scripts/50_cut_clips.sh
を再実行すると、変更があったクリップだけ再切り出ししてくれます(キャッシュ判定が入っている)。
Remotion Studio でリアルタイムプレビュー
レイアウトを微調整したいときは Studio の起動が便利です。
cd meeting_rec/remotion
npx remotion studio
ブラウザで http://localhost:3000 を開くと、動画を見ながら触れます。これは触っているだけで楽しい!
ハマったところ
文字起こし後半が同じフレーズで埋まる現象
Whisper の condition_on_previous_text=True による反復ループです。スクリプトの既定は False にしてあるので普段は出ないんですが、過去の出力が壊れているときは再生成すれば戻ります。
CONDITION_PREV=0 FORCE=1 python meeting_rec/scripts/20_transcribe.py
スポーツで言うと、前のプレーを引きずってリズムが崩れてる選手みたいなもんですね…!
固有名詞の誤認識が多い
meeting_rec/scripts/20_transcribe.py の INITIAL_PROMPT に、会議でよく出る単語を足します。
INITIAL_PROMPT = (
"(会議名、サービス名、製品名、社内用語などをカンマ区切りで列挙)"
)
業界・社内用語や独自プロダクト名も忘れずに入れておくと、文字起こしの精度がぐっと上がります!
Remotion loadFont でエラー
loadFont("normal", {subsets: ["japanese"]}) だと、パッケージ版の登録状況によってコケることがあります。引数なしで loadFont() を呼ぶ形に書き直したら直りました。
mlx-whisper が ImportError(前述の Python バージョン問題)
これは Python 3.14 系で起きるやつです。3.11 で venv を作り直しましょう。
rm -rf meeting_rec/.venv
uv venv --python 3.11 meeting_rec/.venv
source meeting_rec/.venv/bin/activate
uv pip install mlx-whisper
ここで小一時間溶かしました…。Python のバージョンは「とりあえず 3.11」にしておくのが、現時点では正解っぽいです。
やってよかった設計判断
なぜこの構成にしたのかをメモしておきます。
- 素材は
rec/<日付>/に積む … 複数会議をディレクトリで分離 - 日本語ファイル名は symlink で ASCII 化 … シェル事故を防ぐ予防線
- 音声は 16kHz mono … Whisper 内部と一致してリサンプル不要
- トピックマッチは Claude に直接やらせる … embedding モデル(数百 MB 〜 数 GB クラス)の追加 DL を回避
-
highlights.jsonを props 化 … テロップ修正は Phase 6 だけで完結
トピック抽出を embedding でやるか LLM でやるかは少し悩んだんですが、議事録のトピック数が片手で数えられる規模なら、LLM の方がトータルでシンプルでした。
「embedding を仕込む前に、まず LLM に聞いてみる」、最近はこの順番でやることが増えてます。
やってみて思ったこと
もともと「録画見ない問題」を自分の手で解決したくて始めた小ネタだったんですが、やってみたら mlx-whisper の早さと Remotion の表現力の両方を一気に味わえて、めちゃくちゃ楽しかったです!
次は VTT 字幕付きで動画を書き出したり、30 秒版や 3 分版も同じ素材から出せるようにしてみたいなと思っています。
本記事のコードは個人のローカルで動かしているもので公開はしていませんが、本文中の手順とスクリプト断片を組み合わせれば、同じパイプラインを手元で再現できるはずです。同じ「録画見ない問題」に悩んでいる方の参考になれば嬉しいです!