jim800121chen 8cd5751ce3 feat(local-tool): M8 重構 — Wails 控制台 + 瀏覽器 Web UI(R5 決策)
依 R5 五輪決策把 visionA-local 從「Wails 內嵌 Next.js」重構為「Wails
本機伺服器控制台 + 瀏覽器 Web UI」模式(類比 Docker Desktop / Ollama)。

程式碼變動
  - M8-1 砍 yt-dlp 全套(後端 resolver / URL handler / 前端 URL tab /
    Makefile vendor / installer / bootstrap / CI workflow,-555 行)
  - M8-2 砍 Mock 模式全套(driver/mock、mock_camera、Settings runtimeMode、
    VISIONA_MOCK 環境變數,-528 行)
  - M8-3 ffmpeg 從 GPL 切換到 LGPL 混合方案:Windows/Linux 用 BtbN 現成
    LGPL binary,macOS 自 build minimal decoder-only 進 git
    (vendor/ffmpeg/macos/ffmpeg 5.7MB + ffprobe 5.6MB,比 GPL 版省 85% 空間)
  - M8-4 Wails Server Controller:state machine、log ring buffer 2000 行、
    preferences.json atomic write、boot-id、Gin SkipPaths、shutdown 7+1 秒、
    notify_*.go 三平台 OS 通知、watchServer 改 Error state 不 os.Exit
  - M8-4b 啟動階段管線 R5-E:6 階段進度 event、20s soft / 60s hard timeout、
    stage 5/6 skip 規則、sentinel file、RestartStartupSequence 5 步驟
  - M8-5 Wails 控制台 vanilla HTML/JS/CSS(9 檔 ~2012 行)取代 M7-B splash:
    state 視覺、log panel、startup progress panel、Stage 6 manual CTA
    pulse、shutdown modal、Settings、Dark Mode、i18n 中英雙語
  - M8-6 上傳影片副檔名擴充(mp4/avi/mov/mpeg/mpg)
  - M8-7 Web UI Server Offline Overlay(role=alertdialog + focus trap +
    wsEverConnected 容錯 + Page Visibility)
  - M8-8 CORS middleware(127.0.0.1/localhost only + suffix attack 防護)+
    ws/origin.go 獨立 WebSocket CheckOrigin 避 package cycle
  - MAJ-4 server:shutdown-imminent WebSocket broadcast 機制
    (/ws/system endpoint + notifyShutdownImminent helper)
  - M8-9 Boot-ID + 瀏覽器 tab 自動重連(sessionStorage loop guard)

品質
  - ~105+ 新 unit test + race detector (-count=2) 全綠
  - 10 個 milestone 全部通過 Reviewer 審查
  - 三方 v2 + v2.1 文件(PRD / Design Spec / TDD)+ 交叉互審紀錄
    收錄在 .autoflow/

交付前待處理(M8-10)
  - 重跑 make payload-macos 把舊 GPL 77MB binary 換成新 LGPL
  - 三平台 end-to-end build 驗證

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:57:54 +08:00

272 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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-onlyWindows/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/§418 項檢查全過:
| # | 驗證項 | 結果 | 位置 |
|---|-------|------|------|
| 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/COPYINGskipifsourcedoesntexist | ✅ | `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/aacmov-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 0ad-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 0ignored ✅) | 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.md295 行)完整度:
| 欄位 | 有無 |
|------|------|
| ffmpeg release n7.1 + source URL + sha256 | ✅ |
| Build hostmacOS 14.7.6Apple clang 16.0.0 | ✅ |
| Toolchainnasm 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**:無獨立 installerpayload-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/<name>` 找;`visiona-local/app.go:482-484` Wails shell 啟動 server 時注入 `VISIONA_BUNDLE_BIN_DIR = locateBundleBinDir()`macOS→`Contents/Resources/bin`Windows/Linux→`<exe>/bin`,開發→`cwd/payload/<os>/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 MBprogress.md M6-1 紀錄) | **5.73 MB** | 71.3 MB92% |
| macOS ffprobe | 未附 | **5.59 MB** | 新增 |
| macOS 合計 | ~77 MB | **11.32 MB** | **65.7 MB85%** |
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 MinorBUILD.md 的 spctl 預期描述錯誤)+ 2 Suggestion + 3 待處理事項,不阻擋 M8-10。**
Orchestrator 建議:
1. Minor #1BUILD.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