# Reviewer 審查 M8-3 ffmpeg LGPL 切換(2026-04-15) 對照文件: - TDD spec:`/Users/jimchen/visionA/local-tool/.autoflow/04-architecture/v2/ffmpeg-lgpl.md` - Research:`/Users/jimchen/visionA/local-tool/.autoflow/04-architecture/ffmpeg-lgpl-research.md` - BUILD.md:`/Users/jimchen/visionA/local-tool/vendor/ffmpeg/macos/BUILD.md` --- ## 摘要 | 項目 | 結論 | |------|------| | 總結論 | ✅ 通過(1 Minor + 2 Suggestion + 3 待處理事項) | | LGPL 合規 | ✅ 三平台齊備(macOS 自 build decoder-only;Windows/Linux BtbN n7.1 LGPL) | | Binary 大小減量 | ✅ macOS 77 MB → 11.32 MB(含 ffprobe),減量約 85% | | 阻擋 M8-10? | ❌ 不阻擋,所有硬性條件均通過 | --- ## A. Makefile 變更正確性 對照 `Makefile:1-449` ↔ TDD §2.3/§3/§4,18 項檢查全過: | # | 驗證項 | 結果 | 位置 | |---|-------|------|------| | 1 | `FFMPEG_VERSION=n7.1` + sha256 常數 | ✅ | `Makefile:73-77` | | 2 | `vendor-ffmpeg` 退化為「驗證存在 + LGPL guard」 | ✅ | `Makefile:112-130` | | 3 | `vendor-ffmpeg-macos-build` 僅限 Darwin + `nasm`/`yasm` | ✅ | `Makefile:136-140` | | 4 | 下載 tarball + sha256 驗證 + fail 印實際 sha | ✅ | `Makefile:142-148` | | 5 | configure 21 個 flag 與 TDD §2.3 逐字對齊 | ✅ | `Makefile:152-178` | | 6 | `--disable-everything` + decoder 白名單 h264/hevc/mpeg1/2video/mpeg4/mjpeg/prores/vp8/vp9/aac/mp2/mp3/pcm_* | ✅ | `Makefile:167` | | 7 | demuxer 白名單 mov,avi,mpegps,mpegts,matroska,image2 | ✅ | `Makefile:166` | | 8 | strip + ad-hoc codesign | ✅ | `Makefile:184-189` | | 9 | build 後 LGPL grep guard | ✅ | `Makefile:190-193` | | 10 | 複製 COPYING.LGPLv3 | ✅ | `Makefile:194-195` | | 11 | `FFMPEG_URL_WINDOWS` → BtbN n7.1 LGPL zip | ✅ | `Makefile:277` | | 12 | `FFMPEG_URL_LINUX` → BtbN n7.1 LGPL tar.xz | ✅ | `Makefile:387` | | 13 | `vendor-ffmpeg-windows` 解壓 ffmpeg.exe + ffprobe.exe + LICENSE + COPYING.LGPLv3 | ✅ | `Makefile:319-348` | | 14 | `vendor-ffmpeg-linux` 解壓 ffmpeg + ffprobe + LICENSE,`strip-components=1` | ✅ | `Makefile:417-434` | | 15 | `payload-macos` 複製 ffmpeg + ffprobe + ffmpeg-COPYING.LGPLv3 | ✅ | `Makefile:244-247` | | 16 | `payload-windows` 複製 ffprobe.exe + LICENSE/COPYING(skipifsourcedoesntexist) | ✅ | `Makefile:358-362` | | 17 | `payload-linux` 複製 ffprobe + ffmpeg-LICENSE.txt | ✅ | `Makefile:444-446` | | 18 | `payload-macos:241` 有 `rm -rf payload/darwin` 清舊 | ✅ | `Makefile:241` | **五 decoder + 五 demuxer 白名單對照 TDD 驗收條件 §10**(h264/hevc/mpeg2video/mpeg4/aac;mov-mp4/avi/mpeg/mpegts/matroska):全數涵蓋。 --- ## B. Binary 正確性(親自驗證) ``` $ ls -la vendor/ffmpeg/macos/ -rw-r--r-- 10500 BUILD.md -rw-r--r-- 7651 COPYING.LGPLv3 -rwxr-xr-x 6007520 ffmpeg (5.73 MB) -rwxr-xr-x 5865568 ffprobe (5.59 MB) ``` **sha256 與 BUILD.md `lines 26-30` 100% 吻合**: ``` c3cb9f1dad66730267c12fca92c6344d2f8939ab227889caac33005f8947992c ffmpeg bd388fb4372ed5f7e44ee331a51be6383d702fb2c067bf562cabbdfbdd8b0c5e ffprobe da7eabb7bafdf7d3ae5e9f223aa5bdc1eece45ac569dc21b3b037520b4464768 COPYING.LGPLv3 ``` **`ffmpeg -version` configuration**: - ✅ 無 `--enable-gpl`、無 `libx264`、無 `libx265` - ✅ 有 `--enable-version3`、`--disable-network`、`--disable-autodetect`、`--enable-static`、`--disable-everything` **Decoder 驗證**:`ffmpeg -decoders | grep -E ' (h264|hevc|aac|mpeg2video|mpeg4) '` 五個全中。 **Demuxer 驗證**:`ffmpeg -demuxers` 顯示 `avi` / `matroska,webm` / `mov,mp4,m4a,3gp,3g2,mj2` / `mpeg` / `mpegts` 五個全中。 **`otool -L` 動態依賴**: ``` /usr/lib/libSystem.B.dylib /System/Library/Frameworks/CoreFoundation.framework/.../CoreFoundation /System/Library/Frameworks/CoreVideo.framework/.../CoreVideo /System/Library/Frameworks/CoreMedia.framework/.../CoreMedia ``` ffprobe 相同四項。**無任何第三方 dylib**(x264/x265/vpx/opus/aom 都不在),符合 LGPL static self-contained。 **`ffprobe -version`**:可執行,顯示同一 configuration。 **`codesign -v`**:ffmpeg + ffprobe 皆 exit 0(ad-hoc signing 通過)。 --- ## C. .gitignore 雙層 **Inner `local-tool/.gitignore:6-15`**: ``` /vendor/** !/vendor/.gitkeep !/vendor/README.md !/vendor/ffmpeg/ !/vendor/ffmpeg/macos/ !/vendor/ffmpeg/macos/** ``` **Outer `visionA/.gitignore:20-27`**:同樣四行 un-ignore + `local-tool/vendor/**` 為基底。 `git check-ignore` 驗證: | 路徑 | inner repo | outer repo | |------|-----------|-----------| | `vendor/ffmpeg/macos/ffmpeg` | exit 1(未 ignore ✅) | exit 1 ✅ | | `vendor/ffmpeg/macos/BUILD.md` | exit 1 ✅ | exit 1 ✅ | | `vendor/ffmpeg/windows/ffmpeg.exe` | exit 0(ignored ✅) | exit 0 ✅ | | `vendor/ffmpeg/linux/ffmpeg` | exit 0 ✅ | exit 0 ✅ | `git status --ignored vendor/` 顯示: - `?? vendor/ffmpeg/macos/`(untracked,待 `git add`) - `!! vendor/ffmpeg/{linux,windows}/`、`!! vendor/{python,wheels,yt-dlp}/`(ignored) 雙層規則正確;outer repo `local-tool/vendor/` 仍為整個 untracked,需在交付前 `git add` — 屬待處理事項而非實作錯誤。 --- ## D. BUILD.md 可重現性 BUILD.md(295 行)完整度: | 欄位 | 有無 | |------|------| | ffmpeg release n7.1 + source URL + sha256 | ✅ | | Build host(macOS 14.7.6,Apple clang 16.0.0) | ✅ | | Toolchain(nasm 3.01、brew 5.1.6、Xcode CLT) | ✅ | | Configure flags 完整複製貼上 | ✅ | | Binary sha256(三項) | ✅ | | Binary 大小(bytes + 人類可讀) | ✅ | | Build 耗時(2 分 44 秒,user 559s/sys 56s) | ✅ | | 實測驗證輸出(ffmpeg -version / decoder / demuxer / otool / codesign 節錄) | ✅ | | Rebuild 指令 `make vendor-ffmpeg-macos-build` | ✅ | | Commit 清單(僅允許 ffmpeg/ffprobe/COPYING/BUILD.md 四檔) | ✅ | **兩年後重現評估**:可以。source tarball URL + sha256 固定、configure flags 可直貼、系統依賴明列、Makefile 本身就是 reproducibility script。唯一風險是 Homebrew nasm/clang 版本漂移,但 ffmpeg 7.1 對 toolchain 包容度高,不預期 break。 --- ## E. GPL flag 清除 grep 全 repo grep `VISIONA_ALLOW_GPL_FFMPEG` / `--enable-gpl` / `libx264` / `libx265`: | 類別 | 殘存位置 | 評估 | |------|---------|------| | `.autoflow/*`(progress/TDD/research/spec) | 允許(歷史紀錄) | ✅ | | `Makefile:126,190-193` | guard 驗證邏輯 | ✅ | | `vendor/ffmpeg/macos/BUILD.md` | 驗證輸出說明 | ✅ | | `build/ffmpeg-macos/src/*` | upstream source tarball 解壓產物 | ✅(非專案碼) | | `scripts/bootstrap-{windows,linux}.*` | 已改為 `LGPL v3 build` 標語 | ✅ | | `.github/workflows/build.yml` | 零殘留 | ✅ | | `installer/**` | 零殘留 | ✅ | | `server/**.go` + `visiona-local/**.go` | 零殘留 | ✅ | **GPL flag 完全清除**,只剩文件類歷史 + guard 邏輯。 --- ## F. Installer / Bootstrap / CI - **Windows Inno Setup** `installer/windows/visiona-local.iss:74-79`:含 ffmpeg.exe + ffprobe.exe + ffmpeg-LICENSE.txt + ffmpeg-COPYING.LGPLv3(後兩者用 `skipifsourcedoesntexist` fallback)✅ - **Linux AppImage** `installer/linux/build-appimage.sh:61-76`:`for tool in ffmpeg ffprobe` 迴圈 + 把 ffmpeg-LICENSE.txt 放到 `AppDir/usr/share/doc/visiona-local/` ✅ - **macOS**:無獨立 installer,payload-macos 直接把 COPYING.LGPLv3 複製到 `payload/darwin/bin/ffmpeg-COPYING.LGPLv3`,經 stage-macos 進 `.app/Contents/Resources/bin/` ✅ - **bootstrap-windows.ps1:71 / bootstrap-linux.sh:58**:只剩 LGPL 提示語,無 GPL flag ✅ - **`.github/workflows/build.yml`**:只保留 `vendor-ffmpeg-windows`(line 135)和 `vendor-ffmpeg-linux`(line 232)呼叫,無 env var 設定 ✅ --- ## G. Payload 流程相容性 + 真實 decode 測試 ### G.1 server runtime 相容性 `server/main.go:88-93` 把 `VISIONA_BUNDLE_BIN_DIR` prepend 到 PATH;`server/internal/deps/checker.go:23-94` 的 `resolveTool` 優先在 `$VISIONA_BUNDLE_BIN_DIR/` 找;`visiona-local/app.go:482-484` Wails shell 啟動 server 時注入 `VISIONA_BUNDLE_BIN_DIR = locateBundleBinDir()`(macOS→`Contents/Resources/bin`,Windows/Linux→`/bin`,開發→`cwd/payload//bin`)。 payload-macos `Makefile:244-245` 把 `ffmpeg` + `ffprobe` 放到 `payload/darwin/bin/`,stage-macos 會完整複製到 bundle Resources。`server/internal/camera/video_source.go:24` 的 `exec.Command("ffprobe", ...)` 依賴 basename lookup,加入 ffprobe 後完全相容。✅ ### G.2 實際 decode 測試 先用系統 ffmpeg 產生 h264 mp4:`/usr/local/bin/ffmpeg -f lavfi -i "testsrc=duration=1:size=320x240:rate=10" -c:v libx264 ... /tmp/testsrc.mp4`。 LGPL ffmpeg 解碼 → mjpeg: ``` $ ./vendor/ffmpeg/macos/ffmpeg -i /tmp/testsrc.mp4 \ -frames:v 1 -f image2pipe -vcodec mjpeg -q:v 5 /tmp/test_out.jpg Stream #0:0(und): Video: mjpeg, yuv444p, 320x240, 10 fps ... frame= 1 fps=0.0 q=5.0 Lsize= 9KiB $ file /tmp/test_out.jpg /tmp/test_out.jpg: JPEG image data, JFIF standard 1.02, ..., 320x240 ``` LGPL ffprobe probe h264 stream: ``` $ ./vendor/ffmpeg/macos/ffprobe -v error -show_streams /tmp/testsrc.mp4 codec_name=h264 codec_type=video width=320 height=240 pix_fmt=yuv444p ``` **真實 decode 路徑通過**,server extract first-frame 流程可直接使用。 ### G.3 `payload/darwin/bin/` 當前為舊 GPL 77 MB binary ``` $ shasum -a 256 payload/darwin/bin/ffmpeg b68f795f7fb4528daf697f57a2b6780846a1ae762a71907e994442ad103ee88f ``` **非 M8-3 實作錯誤**:payload-macos 尚未在 M8-3 後重跑。`Makefile:241` 的 `rm -rf payload/darwin` 與 `stage-macos:265` 的 `rm -rf visiona-local/build/darwin/Resources` 會自動清乾淨。列入待處理。 --- ## H. Size 比較 | 項目 | 舊 GPL | 新 LGPL | 差異 | |------|-------|--------|------| | macOS ffmpeg | ~77 MB(progress.md M6-1 紀錄) | **5.73 MB** | −71.3 MB(−92%) | | macOS ffprobe | 未附 | **5.59 MB** | 新增 | | macOS 合計 | ~77 MB | **11.32 MB** | **−65.7 MB(−85%)** | macOS 成果超越 TDD §2.1「~20 MB」目標,`--disable-everything` + 白名單策略效益顯著。Windows/Linux BtbN LGPL build 大小待 M8-10 CI runner 下載後驗證。 --- ## I. 問題清單 ### Critical / Major 無。 ### Minor | # | 位置 | 問題 | 建議 | |---|------|------|------| | 1 | `vendor/ffmpeg/macos/BUILD.md:186-194` §Verification §5 | 預期 `spctl --assess --verbose` 為「accepted」不準確:實測 ad-hoc signed binary 在 macOS 14.7 下被 `rejected`(exit 3)。Gatekeeper 由 outer `.app` bundle signing 決定,不對內嵌 binary 單獨 assess | 把該步驟改為 `codesign -v`(exit 0 即可),並註記「Gatekeeper 最終由 outer `.app` bundle signing 決定」 | ### Suggestion | # | 位置 | 建議 | |---|------|------| | 1 | `Makefile:126-130` `vendor-ffmpeg` target | 可多驗 `shasum -a 256` 對照 BUILD.md 記錄,防 binary 被意外覆蓋 / 損毀(非必要,git 已保護) | | 2 | `Makefile:361-362` payload-windows | 兩份授權檔都用 `skipifsourcedoesntexist`,若 BtbN zip 內 LICENSE.txt 與 COPYING.LGPLv3 同時缺失,installer 會無授權檔交付。建議補「至少其中一個存在」assertion | ### 待處理(非 M8-3 scope,但影響 release) 1. **outer repo `git add`**:`/Users/jimchen/visionA/` 的 `git status` 仍顯示 `local-tool/vendor/` 為整個 untracked。交付前須 `git add local-tool/vendor/ffmpeg/macos/{ffmpeg,ffprobe,COPYING.LGPLv3,BUILD.md}` 並 commit。 2. **`payload/darwin/bin/ffmpeg` 仍為舊 GPL 77 MB binary**:下次 `make payload-macos` 會自動清。M8-10 前應重跑。 3. **`vendor/yt-dlp/` 目錄仍在**:屬 M8-2 清理範圍,非 M8-3。 --- ## J. 結論 **M8-3 ffmpeg LGPL 切換:✅ 通過。** 核心成果確認: - Makefile 三平台變更逐字對齊 TDD §2-§4 - macOS binary 親測 LGPL 合規(無 gpl/x264/x265,有 version3)、五 decoder + 五 demuxer 全中、只依賴 macOS 系統 framework - sha256 與 BUILD.md 100% 吻合 - 實測 h264 mp4 decode → mjpeg JPEG 成功;ffprobe probe 成功 - 雙層 `.gitignore` inner + outer 規則均正確 - BUILD.md 完整可重現(兩年後可 rebuild) - GPL flag 在程式碼 / installer / bootstrap / CI 完全清除 - Installer Windows/Linux 含 ffprobe + 授權檔 - Size 減量 85% **僅 1 Minor(BUILD.md 的 spctl 預期描述錯誤)+ 2 Suggestion + 3 待處理事項,不阻擋 M8-10。** Orchestrator 建議: 1. Minor #1(BUILD.md spctl 描述修正)列為 M8-3 尾巴,由 Architect/DevOps 補 2. M8-10 CI 交付前執行 outer repo `git add` + commit 3. M8-10 時確保 `make payload-macos` + `stage-macos` 重跑清掉舊 GPL payload