Moviepyを使っていたけど、FFmpeg.exe＋ffmpeg-pythonに乗り換えようかなと考えている話

## HDR動画をSDR動画にトーンマッピングでいい感じで速く処理したかった
動画編集アプリを作成しています。

https://qiita.com/calcMCalcm/items/178901c752f6cef481f1

動画編集アプリでは、動画を入力し、フレーム単位でopencvで処理した後、一時ファイルとして動画にし、元動画の音声をつけて、動画に最終出力しています。
| 順 | 処理内容 | 使用ライブラリ |
|:-:|:-:|:-:|
| 1 | 動画の入力 | opencv |
| 2 | フレームごとに四角枠を描きだし | opencv |
| 3 | フレームを動画ファイルへ一時書き出し | opencv |
| 4 | 音声分割  | moviepy |
| 5 | 音声を結合、最終出力  | moviepy |

作成しているアプリはゲームキャプチャの動画を対象としています。
最近のゲーム動画は、WindowsのAutoHDRの機能で、HDR動画となっています。
このようなHDR動画をアプリに投入すると、全体に白っぽいSDR動画として出力されてしまうのを改善したいと考え、OPENCVのトーンマッピングで対応してみました。

https://qiita.com/calcMCalcm/items/8434605ecd037336a2ea

見た目は確かにいい感じになったのですが、処理が重く3FPS程度しか出ず、実際にはあまりいい対策にはなりませんでした。

Moviepyで出力する時に、`ffmpeg_params`を指定して、トーンマッピングやBT.709で出力できないか、と試行錯誤したのですが、どうにもうまくパラメータが渡せません。
当方の書き方が悪いのだろうと思うのですが、ffmpegでの記述例は多く見つかるものの、Moviepyへの適用例がほぼ見つからず、あきらめかけていました。

## 私がMoviepyを使っていたのは何のため？
Moviepyでffmpeg_paramsの記述やSDR動画にするためのヒントはないかと検索をしていて見つけたのがこちらの記事でした。

https://note.com/ma251/n/n5a063baf22f1

記事では、ffmpegのパスを通すのが面倒なので、丸っと利用できるMoviepyを使用してきたが、Moviepyのクロップ処理にバグがあったため、ffmpegのバイナリexeと、ffmpeg-pythonで実装しなおします、というような内容になっています。
### ffmpeg.exeのライセンスは？
私の場合、そもそもffmpegを回避していたのは、元々は収益化やソースを開示しない方向で検討していたこともあり、MITライセンスのMoviepyしかない！と考えていました。
しかしながら、収益化も難しく、また利用していたライブラリでは性能がでなかったこともあり、無償、オープンソースとしています。
それでもFFmpeg、特にlibx264のライセンスが厳しいので、ffmpeg.exeはないかなー？と思っていましたが、FFmpeg公式からリンクしてあるバイナリダウンロード先の[Windows builds by BtbN](https://github.com/BtbN/FFmpeg-Builds/releases)は、LGPLライセンスのものがありました。GPLのものも同じく並んでいることを考えると、LGPLのものはlibx264が含まれないバイナリだろうと考えられます。
gplのバイナリにソースzipを添付すれば、アプリにffmpeg.exeを同梱することも問題なさそうな気がします。

### libx264が必要か？
Moviepyでは、codecを指定しない場合、Moviepyのffmpegについているlibx264を利用するようになっています。
私も当初は、codecを指定せずにmoviepyを利用していましたが、処理速度を改善するためcodecにNVENCを指定し利用するようになっていました。

https://qiita.com/calcMCalcm/items/20719b00d50b972eb139

CUDAが見つからない場合libx264を利用する形になっていますが、アプリの性質上、CUDAがほぼ必須となっており、アプリユーザでNVENCを使えない状況は考えにくく、libx264を利用することはほぼないだろうと考えています。

### バージョン差は？
Moviepyのffmpegを見て見ると、ffmpeg-win64-v4.2.2.exeとなっており、ffmpeg公式のFFmpeg 7.0.2からはかなり古そうに見えます。
試しに、Moviepyのffmpeg-win64-v4.2.2.exe、先ほどのBtbNのffmpeg.exeを実行し、ログを確認してみます。
細かい点はよくわかりませんが、結構バージョンに違いがありそうです。もしかするとバージョンで使えるオプションが違うこともありそうです（試しても調べてもいない）
<details><summary>ffmpeg-win64-v4.2.2.exe</summary>

```cmd:ffmpeg-win64-v4.2.2.exe
ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers
  built with gcc 9.2.1 (GCC) 20200122
  configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100
Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...
```
</details>
<details><summary>BtbNのffmpeg.ex</summary>

```cmd:BtbNのffmpeg.exe
ffmpeg version N-117040-g3f84d1d1fb-20240914 Copyright (c) 2000-2024 the FFmpeg developers
  built with gcc 14.2.0 (crosstool-NG 1.26.0.106_ed12fa6)
  configuration: --prefix=/ffbuild/prefix --pkg-config-flags=--static --pkg-config=pkg-config --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --enable-gpl --enable-version3 --disable-debug --disable-w32threads --enable-pthreads --enable-iconv --enable-zlib --enable-libfreetype --enable-libfribidi --enable-gmp --enable-libxml2 --enable-lzma --enable-fontconfig --enable-libharfbuzz --enable-libvorbis --enable-opencl --disable-libpulse --enable-libvmaf --disable-libxcb --disable-xlib --enable-amf --enable-libaom --enable-libaribb24 --enable-avisynth --enable-chromaprint --enable-libdav1d --enable-libdavs2 --enable-libdvdread --enable-libdvdnav --disable-libfdk-aac --enable-ffnvcodec --enable-cuda-llvm --enable-frei0r --enable-libgme --enable-libkvazaar --enable-libaribcaption --enable-libass --enable-libbluray --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librist --enable-libssh --enable-libtheora --enable-libvpx --enable-libwebp --enable-libzmq --enable-lv2 --enable-libvpl --enable-openal --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopenmpt --enable-librav1e --enable-librubberband --enable-schannel --enable-sdl2 --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtwolame --enable-libuavs3d --disable-libdrm --enable-vaapi --enable-libvidstab --enable-vulkan --enable-libshaderc --enable-libplacebo --enable-libvvenc --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libzimg --enable-libzvbi --extra-cflags=-DLIBTWOLAME_STATIC --extra-cxxflags= --extra-libs=-lgomp --extra-ldflags=-pthread --extra-ldexeflags= --cc=x86_64-w64-mingw32-gcc --cxx=x86_64-w64-mingw32-g++ --ar=x86_64-w64-mingw32-gcc-ar --ranlib=x86_64-w64-mingw32-gcc-ranlib --nm=x86_64-w64-mingw32-gcc-nm --extra-version=20240914
  libavutil      59. 36.100 / 59. 36.100
  libavcodec     61. 13.100 / 61. 13.100
  libavformat    61.  5.101 / 61.  5.101
  libavdevice    61.  2.101 / 61.  2.101
  libavfilter    10.  2.102 / 10.  2.102
  libswscale      8.  2.100 /  8.  2.100
  libswresample   5.  2.100 /  5.  2.100
  libpostproc    58.  2.100 / 58.  2.100
Universal media converter
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...
```
</details>

### ここまでのまとめ
|  | Moviepyのffmpeg | FFmpeg.exe |備考|
|:-:|:-:|:-:|:-:|
| ライセンス  | MIT | GPL |LGPL版もある|
| libx264  | 有  |  無 ||
| バージョン  | 4.2.2  |  7.0.2 |20240915現在|
| 入手方法  | pip install moviepy |  別途ダウンロード ||
| パス設定  | 不要  | 必要  ||

### その他
現在アプリでは、動画の入力をopencvで行っていますが、ffmpegに置き換えることもできそうです。
また、動画のフレーム単位で四角枠の描画をopencvで処理していますが、こちらもffmpegに置き換え可能ではないかと考えています。
またmoviepyと比べ、ffmpeg+ffmpeg-pythonの方が事例が多いのではないかとも考えています。
動画ファイルだけでなく、ストリーミングの取り込みを考えるとffmpegの方が良さそうにも感じます。

## 結論：どうしよう？
ffmpeg+ffmpeg-pythonの方が、現状のmoviepy+opencvよりもすっきりとすることがで、また処理もより良い方向に向かいそうな気がしています。
ただコードの修正範囲はかなり広そうな印象で、ちょっとめんどくさいです。
でも表題の通り、せっかく綺麗になるんなら、gplのffmpeg.exeを同梱してもいいのかな、と思ったりもしています。

## 後日談（20240919追記）
作成しているwindowsアプリにおいて、opencvで動画の読み込み、処理後の出力をmoviepyで行っていた処理を、ffmpegとffmpeg-pythonに置き換えを行っています。
ほぼ作り直しに近いので苦労もしていますが、メリットも大きそうです。
* HDRをSDRとするためのフィルタを、HDR動画読み込み時にFFmpegで実施することができます。当方の環境で、通常処理が25FPSですが、FfmpegでSDR化フィルタを実施すると20FPSでSDR化込みで処理することができました。opencvで実施を試みた時には3FPSでしたので、大きく改善することができそうです。
* フレーム単位で処理して動画に一時保存した後、元の動画の音声と処理後の動画を結合します。Moviepyでは動画と音声の結合時、再圧縮がかかり、結合処理に時間がかかっていました。FFmpegでは再圧縮がかからないように指示することができ、一瞬で音声の結合が完了するようになりました。Moviepyでもffmpeg_paramsで指定できたかもしれませんが、moviepyと比べるとコードでの指示が簡単だったと感じます。
* フレーム単位の処理では、yolov8での物体検出し物体に四角枠を描画します。従来はopencvで実施しており、ffmpegに置き換えることも検討していますが、現状実現できていません。opencvは、他の場所の置き換えが難しいところでの利用があるので、完全に排除せず、当面はopencvでの描画を継続しようと考えています。余裕ができれば簡単にテストをして、置き換えのメリットがありそうか評価してみたいです。