FFmpeg と Core ML に対応した Whisper.cpp をビルドする
はじめに
今さらではありますが FFmpeg と Core ML 対応の Whisper.cpp をビルドしたときのメモです。
公式の情報だけでは上手くいかなかったため、その補足です。
0. 今回の環境
- MacBook Air (Apple M2)
- macOS Tahoe 26.1
- Whisper.cpp v1.8.2
Xcode のコマンドラインツールと Homebrew は既にインストールしてあることを前提で始めます。
1. 必要なものをインストール
CMake をインストール。
また対応するオーディオファイルの種類を増やすために FFmpeg もインストールします。
brew install cmake
brew install ccache
brew install ffmpeg
OpenMP のランタイムライブラリをインストールして ~/.zshrc に環境変数を追加、それを反映させて Apple Clang にインストール先を教えてあげます。
brew install libomp
export OpenMP_ROOT=$(brew --prefix)/opt/libomp
source ~/.zshrc
公式に従って Python 3.11 の仮想環境を作ります。今回は 3.11.14 をインストールしました。
brew install pyenv
pyenv install 3.11.14
pyenv local 3.11.14
python -m venv whisper.cpp-env
source whisper.cpp-env/bin/activate
PyTorch は 2.5.0 を指定します。
pip install ane_transformers
pip install openai-whisper
pip install coremltools
pip install torch==2.5.0
リポジトリをクローンします。
git clone https://github.com/ggml-org/whisper.cpp.git
cd whisper.cpp
2. ビルド
Core ML 対応モデルを生成します。今回は small を選択しました。
./models/generate-coreml-model.sh small
この時点で Python の仮想環境は終了しても大丈夫です。
deactivate
バイナリファイルをビルドします。
-DGGML_NATIVE=OFF を付けないと Clang に「native とかいう引数は知らん」と叱られて途中で止まってしまいます。
rm -rf build
cmake -B build -DWHISPER_COREML=ON -DWHISPER_FFMPEG=ON -DGGML_NATIVE=OFF
cmake --build build -j --config Release
警告が出るかもしれませんが、ここまでの工程に問題がなければビルドは無事完了します。
- 途中省略 -
[100%] Built target vad-speech-segments
[100%] Built target test-vad-full
[100%] Built target whisper-cli
[100%] Built target test-vad
[100%] Built target whisper-server
[100%] Built target quantize
3. 動作テスト
FFmpeg の効果を確認するため、AAC に変換したサンプルを作成して入力してみます。
./build/bin/whisper-cli -m models/ggml-small.bin -f samples/jfk.aac
- 途中省略 -
system_info: n_threads = 4 / 8 | WHISPER : COREML = 1 | OPENVINO = 0 | Metal : EMBED_LIBRARY = 1 | CPU : NEON = 1 | ARM_FMA = 1 | FP16_VA = 1 | DOTPROD = 1 | ACCELERATE = 1 | OPENMP = 1 | REPACK = 1 |
main: processing 'samples/jfk.aac' (179200 samples, 11.2 sec), 4 threads, 1 processors, 5 beams + best of 5, lang = en, task = transcribe, timestamps = 1 ...
[00:00:00.000 --> 00:00:11.000] And so, my fellow Americans, ask not what your country can do for you, ask what you can do for your country.
whisper_print_timings: load time = 168.01 ms
whisper_print_timings: fallbacks = 0 p / 0 h
whisper_print_timings: mel time = 4.37 ms
whisper_print_timings: sample time = 33.78 ms / 142 runs ( 0.24 ms per run)
whisper_print_timings: encode time = 146.13 ms / 1 runs ( 146.13 ms per run)
whisper_print_timings: decode time = 6.74 ms / 1 runs ( 6.74 ms per run)
whisper_print_timings: batchd time = 186.15 ms / 139 runs ( 1.34 ms per run)
whisper_print_timings: prompt time = 0.00 ms / 1 runs ( 0.00 ms per run)
whisper_print_timings: total time = 639.47 ms
ggml_metal_free: deallocating
AAC は OK でしたが MP4 ではエラーが出てしまいます。
./build/bin/whisper-cli -m models/ggml-small.bin -f samples/jfk.mp4
- 途中省略 -
[mov,mp4,m4a,3gp,3g2,mj2 @ 0xa0f3fc000] stream 0, offset 0x2c: partial file
[mov,mp4,m4a,3gp,3g2,mj2 @ 0xa0f3fc000] stream 0, offset 0x2c: partial file
zsh: segmentation fault ./build/bin/whisper-cli -m models/ggml-small.bin -f samples/jfk.mp4
ですがサーバーモードで起動すると MP4 も処理してくれます。
./build/bin/whisper-server -m models/ggml-small.bin --port 8080 --convert
curl localhost:8080/inference -F "file=@samples/jfk.mp4"
{"text":" And so my fellow Americans, ask not what your country can do for you, ask what you can do for your country.\n"}%
と思いましたが、どうやら先に Whisper.cpp が FFmpeg を呼び出して変換した後に Whisper.cpp が処理しているようです。
つまり FFmpeg がインストールしてある環境で --convert オプションを付けてサーバーを起動させた場合、-DWHISPER_FFMPEG=OFF でビルドしたバイナリでも同じ結果になります。
また日本語の音声を認識させる場合サーバー起動時に -l ja オプションを付けるか、ファイル送信時に -F "language=ja" を付けないとローマ字に変換されてしまいます。
(例) 日本語の input.mp4 を送信し、返信された JSON から text フィールドのみを output.txt に保存する場合
curl localhost:8080/inference -F "file=@input.mp4" -F "language=ja" | jq -r '.text' > output.txt
おわりに
せっかくビルドできたので何かに活用したいと思います。
参考文献
Discussion