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

462 lines
22 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.

# v2/ffmpeg-lgpl.md — ffmpeg LGPL 打包策略
> 所屬TDD v2 §2.2
> 決策依據R5-6LGPL 方案 B 混合、R5-6amacOS decoder-only ~20 MB、R5-6bmacOS binary commit 到 repo、R5-6c三平台都打包 ffprobe
> 對應 milestoneM8-3
> 前置研究:`/Users/jimchen/visionA/local-tool/.autoflow/04-architecture/ffmpeg-lgpl-research.md`
---
## 1. 目的與範圍
把 v1 的「三平台 GPL ffmpeg build + `VISIONA_ALLOW_GPL_FFMPEG=1` 逃生門」換成 LGPL 方案 B混合
| 平台 | v1 來源 | v1 授權 | v2 來源 | v2 授權 | 存放位置 |
|------|--------|---------|--------|---------|---------|
| macOS x86_64 | evermeet.cx / zip | GPL | **自 build**ffmpeg n7.1 + decoder-only configure | **LGPLv3** | `vendor/ffmpeg/macos/`(進 git |
| Windows x86_64 | BtbN / `…-win64-gpl.zip` | GPL | BtbN / `…-win64-lgpl.zip` | **LGPLv3** | `vendor/ffmpeg/windows/`(不進 gitMakefile 下載) |
| Linux x86_64 | johnvansickle static | GPL | BtbN / `…-linux64-lgpl-7.1.tar.xz` | **LGPLv3** | `vendor/ffmpeg/linux/`(不進 gitMakefile 下載) |
三平台都包 **ffmpeg + ffprobe** 兩支 binaryR5-6c
---
## 2. macOS從源碼 build decoder-only~20 MB
### 2.1 選型理由
- 三平台只有 macOS x86_64 沒有現成的 LGPL static binary完整分析見 `ffmpeg-lgpl-research.md` §2.3
- R5-6a 決定「最小 decoder-only build~20 MBconfigure 用 `--disable-everything` + 主動 enable 需要的 decoder/demuxer
- R5-6b 決定 binary 直接 commit 到 `vendor/ffmpeg/macos/`,避免開發者每次 build 都跑一遍編譯(單次 build ~15 分鐘)
### 2.2 需要的 feature set對應「瀏覽器能吃的格式」
PRD v2 支援的上傳影片副檔名:`.mp4 / .avi / .mov / .mpeg / .mpg`。對應的 ffmpeg configure 能力:
| 能力 | 必要項目 | 來源 |
|------|---------|------|
| 容器解析 | `demuxer=mov,avi,mpeg,mpegps,mpegts,matroska` | 內建LGPL |
| 影像解碼 | `decoder=h264,hevc,mpeg1video,mpeg2video,mpeg4,mjpeg,prores` | libavcodec nativeLGPL |
| 音訊解碼 | `decoder=aac,mp2,mp3,pcm_s16le,pcm_s16be` | libavcodec nativeLGPL |
| pixel format 轉換 | `filter=scale,format,fps` | libavfilterLGPL |
| 輸出為 image pipe | `muxer=image2pipe` / `encoder=mjpeg` | libavcodecLGPLmjpeg encoder 不是 libx264LGPL 安全) |
| 協議 | `protocol=file` | 夠用(只處理本地上傳檔) |
| parser | `parser=h264,hevc,mpeg4video,mpegaudio,aac` | 必要,否則有些 decoder 會 fail |
**ffprobe**ffprobe 是 ffmpeg 主 source tree 的一部分,同一次 `./configure && make` 會同時產出 `ffmpeg``ffprobe`,不用另外處理。
### 2.3 `Makefile` 新 target`vendor-ffmpeg-macos-build`
新增到 `Makefile`(位置:現有 `vendor-ffmpeg` target 旁邊):
```makefile
FFMPEG_VERSION := n7.1
FFMPEG_SRC_URL := https://github.com/FFmpeg/FFmpeg/archive/refs/tags/$(FFMPEG_VERSION).tar.gz
FFMPEG_SRC_SHA256 := <填 sha256build 第一次時用 `shasum -a 256` 計算後記錄,之後每次 build 用 sha256sum 驗證>
# 這個 target 只有要升級 ffmpeg 時才跑一次;平常開發者不需要跑,
# 因為 vendor/ffmpeg/macos/ffmpeg 已經 commit 到 repoR5-6b
vendor-ffmpeg-macos-build: ## 從源碼 build LGPL decoder-only ffmpeg for macOS x86_64只有升級 ffmpeg 時才跑)
@if [ "$$(uname -s)" != "Darwin" ]; then \
echo "❌ vendor-ffmpeg-macos-build 只能在 macOS 上跑"; exit 1; \
fi
@command -v pkg-config >/dev/null 2>&1 || { echo "❌ 需要 pkg-configbrew install pkg-config"; exit 1; }
@command -v yasm >/dev/null 2>&1 || command -v nasm >/dev/null 2>&1 || { echo "❌ 需要 yasm 或 nasmbrew install yasm"; exit 1; }
@mkdir -p vendor/ffmpeg/macos build/ffmpeg-macos
@echo "==> 下載 ffmpeg source $(FFMPEG_VERSION)..."
curl -fL -o build/ffmpeg-macos/source.tar.gz "$(FFMPEG_SRC_URL)"
@echo "==> 驗證 source tarball sha256..."
@echo "$(FFMPEG_SRC_SHA256) build/ffmpeg-macos/source.tar.gz" | shasum -a 256 -c || { \
echo "❌ sha256 不符,請更新 FFMPEG_SRC_SHA256 或檢查來源"; exit 1; }
@rm -rf build/ffmpeg-macos/src
@mkdir -p build/ffmpeg-macos/src
tar xzf build/ffmpeg-macos/source.tar.gz -C build/ffmpeg-macos/src --strip-components=1
@echo "==> configuredecoder-only LGPL..."
cd build/ffmpeg-macos/src && ./configure \
--prefix="$$(pwd)/../install" \
--enable-version3 \
--disable-debug \
--disable-doc \
--disable-ffplay \
--disable-network \
--disable-autodetect \
--disable-shared \
--enable-static \
--disable-everything \
--enable-small \
--enable-protocol=file,pipe \
--enable-demuxer=mov,avi,mpegps,mpegts,matroska,image2 \
--enable-decoder=h264,hevc,mpeg1video,mpeg2video,mpeg4,mjpeg,prores,vp8,vp9,aac,mp2,mp3,pcm_s16le,pcm_s16be \
--enable-parser=h264,hevc,mpeg4video,mpegaudio,aac \
--enable-filter=scale,format,fps,null,anull \
--enable-muxer=image2pipe,image2,null \
--enable-encoder=mjpeg \
--enable-swscale \
--enable-swresample \
--extra-cflags="-arch x86_64 -mmacosx-version-min=10.15" \
--extra-ldflags="-arch x86_64 -mmacosx-version-min=10.15 -Wl,-search_paths_first" \
--arch=x86_64 \
--target-os=darwin \
--cc="clang -arch x86_64"
cd build/ffmpeg-macos/src && make -j$$(sysctl -n hw.ncpu)
cd build/ffmpeg-macos/src && make install
@echo "==> 複製 ffmpeg + ffprobe 到 vendor/ffmpeg/macos/..."
cp build/ffmpeg-macos/install/bin/ffmpeg vendor/ffmpeg/macos/ffmpeg
cp build/ffmpeg-macos/install/bin/ffprobe vendor/ffmpeg/macos/ffprobe
@strip -S -x vendor/ffmpeg/macos/ffmpeg
@strip -S -x vendor/ffmpeg/macos/ffprobe
@chmod +x vendor/ffmpeg/macos/ffmpeg vendor/ffmpeg/macos/ffprobe
@echo "==> ad-hoc 簽章..."
codesign --force --sign - vendor/ffmpeg/macos/ffmpeg
codesign --force --sign - vendor/ffmpeg/macos/ffprobe
@echo "==> 驗證授權configuration line 不含 --enable-gpl / libx264 / libx265..."
@if vendor/ffmpeg/macos/ffmpeg -version 2>&1 | grep -E -- '--enable-gpl|libx264|libx265'; then \
echo "❌ LGPL 驗證失敗build 不該出現 gpl / x264 / x265"; exit 1; \
fi
@echo "==> 複製 COPYING.LGPLv3 到 vendor/ffmpeg/macos/..."
cp build/ffmpeg-macos/src/COPYING.LGPLv3 vendor/ffmpeg/macos/COPYING.LGPLv3
@echo "==> ffmpeg 大小:$$(du -h vendor/ffmpeg/macos/ffmpeg | cut -f1)"
@echo "==> ffprobe 大小:$$(du -h vendor/ffmpeg/macos/ffprobe | cut -f1)"
@echo ""
@echo "✅ macOS LGPL ffmpeg build 完成。請1) 更新 vendor/ffmpeg/macos/BUILD.md 的 binary sha256"
@echo " 2) git add vendor/ffmpeg/macos/{ffmpeg,ffprobe,COPYING.LGPLv3,BUILD.md}"
```
**關鍵 flags 解釋**
| flag | 為什麼 |
|------|-------|
| `--enable-version3` | 使用 LGPLv3不是 v2.1),和 BtbN 對齊 |
| `--disable-debug` / `--disable-doc` | 縮 binary |
| `--disable-network` | 我們只處理本地檔案,網路 protocolrtmp/rtsp/http用不到可關 |
| `--disable-autodetect` | 不自動偵測系統上的外部 liblibopus/libvpx 等LGPL 合規稽核時更乾淨 |
| `--disable-shared --enable-static` | 產出 self-contained binary不依賴 macOS 上任何外部 lib |
| `--disable-everything` | 先關全部,白名單 enable確保不額外 link 任何東西 |
| `--enable-small` | 優化大小而非速度,進一步縮 binary |
| `--enable-protocol=file,pipe` | 只開 file:// 和 pipeffmpeg 內部 input/output不開 http/rtsp |
| `--extra-cflags=-mmacosx-version-min=10.15` | 相容 macOS 10.15+PRD v1 的 macOS 14/15 之外也能跑,無痛) |
### 2.4 `vendor/ffmpeg/macos/BUILD.md`(進 git稽核用
```markdown
# macOS LGPL ffmpeg build record
This directory contains a pre-built LGPL ffmpeg + ffprobe binary for macOS x86_64.
## Reproducibility
- ffmpeg release: n7.1
- Source tarball: https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n7.1.tar.gz
- Source sha256: <填值>
- Build host: macOS 14.4, Xcode Command Line Tools 15.3 (clang 1500.3.9.4)
- Build date: 2026-04-14
- Build flags: 見 `Makefile``vendor-ffmpeg-macos-build` target
## Binary sha256
- ffmpeg: <填值>
- ffprobe: <填值>
## License
LGPLv3 — 完整授權條款見 `COPYING.LGPLv3`(與 binary 同目錄)。
## How to rebuild
```
make vendor-ffmpeg-macos-build
```
會:
1. 從 GitHub 下載 ffmpeg source tarballn7.1
2. 驗證 sha256
3. configure只啟用 decoder/demuxer/filter 白名單)
4. make
5. 複製 ffmpeg + ffprobe 到這個目錄
6. strip + ad-hoc codesign
7. 驗證 `ffmpeg -version` 不含 `--enable-gpl` / `libx264` / `libx265`
Build 完成後請手動更新本檔的 Binary sha256 區塊,並 git commit。
## Verification
```
# 確認授權
vendor/ffmpeg/macos/ffmpeg -version | grep -E -- '--enable-gpl|libx264|libx265'
# 預期:沒有任何輸出
# 確認能解 5 種格式
for f in sample.mp4 sample.avi sample.mov sample.mpeg sample.mpg; do
vendor/ffmpeg/macos/ffmpeg -hide_banner -i "$f" -f null - 2>&1 | tail -5
done
# 確認 Gatekeeper 可以過
spctl --assess --verbose vendor/ffmpeg/macos/ffmpeg
# 預期acceptedad-hoc signed
```
```
### 2.5 現有 `vendor-ffmpeg` target 的處理
v1 現有的 `Makefile:106-132` `vendor-ffmpeg` target 是從 evermeet.cx 下載 GPL build**整段刪除**,改為:
```makefile
vendor-ffmpeg: ## macOSLGPL binary 已 commit 到 vendor/ffmpeg/macos/,此 target 只驗證存在
@if [ ! -f vendor/ffmpeg/macos/ffmpeg ]; then \
echo "❌ vendor/ffmpeg/macos/ffmpeg 不存在。請執行 'make vendor-ffmpeg-macos-build' 從源碼 build"; \
exit 1; \
fi
@if [ ! -f vendor/ffmpeg/macos/ffprobe ]; then \
echo "❌ vendor/ffmpeg/macos/ffprobe 不存在。請執行 'make vendor-ffmpeg-macos-build'"; \
exit 1; \
fi
@echo "==> vendor/ffmpeg/macos/ffmpeg 存在:$$(du -h vendor/ffmpeg/macos/ffmpeg | cut -f1)"
@echo "==> vendor/ffmpeg/macos/ffprobe 存在:$$(du -h vendor/ffmpeg/macos/ffprobe | cut -f1)"
@# 驗證 LGPL沒有 --enable-gpl / libx264 / libx265
@if vendor/ffmpeg/macos/ffmpeg -version 2>&1 | grep -qE -- '--enable-gpl|libx264|libx265'; then \
echo "❌ LGPL 驗證失敗"; exit 1; \
fi
@echo "==> LGPL 驗證通過"
```
**注意**:原本 `vendor-ffmpeg` 是把 binary 放在 `vendor/ffmpeg/darwin/`v2 改為 `vendor/ffmpeg/macos/`(與 PRD 術語對齊 + 與 git commit 的目錄名一致。payload 階段也要同步改§5
---
## 3. Windows換 BtbN LGPL改一行 URL
修改 `Makefile:218`
```makefile
# 原:
FFMPEG_URL_WINDOWS := https://github.com/BtbN/FFmpeg-Builds/releases/latest/download/ffmpeg-master-latest-win64-gpl.zip
# 改為:
FFMPEG_URL_WINDOWS := https://github.com/BtbN/FFmpeg-Builds/releases/latest/download/ffmpeg-n7.1-latest-win64-lgpl-7.1.zip
```
**為什麼用 n7.1 穩定分支而非 master-latest**:見 `ffmpeg-lgpl-research.md` §5.3n7.1 綁定 7.1 release + 每日 backport bugfix避免 master 的隨機 regression。
修改 `Makefile:261-286` `vendor-ffmpeg-windows` target
```makefile
vendor-ffmpeg-windows: ## 下載 Windows LGPL ffmpeg + ffprobe → vendor/ffmpeg/windows/
@mkdir -p vendor/ffmpeg/windows
@if [ -f vendor/ffmpeg/windows/ffmpeg.exe ] && [ -f vendor/ffmpeg/windows/ffprobe.exe ]; then \
echo "==> ffmpeg.exe + ffprobe.exe 已存在,跳過"; \
else \
echo "==> 下載 BtbN LGPL ffmpeg (Windows, n7.1)..."; \
curl -fL -o vendor/ffmpeg/windows/ffmpeg-win.zip "$(FFMPEG_URL_WINDOWS)"; \
PY=""; \
for candidate in "$$VISIONA_PYTHON" "py -3" python3 python; do \
[ -z "$$candidate" ] && continue; \
if $$candidate --version >/dev/null 2>&1; then PY="$$candidate"; break; fi; \
done; \
[ -n "$$PY" ] || { echo "❌ 需要 python"; exit 1; }; \
$$PY -c "import zipfile; z=zipfile.ZipFile('vendor/ffmpeg/windows/ffmpeg-win.zip'); \
names=[n for n in z.namelist() if n.endswith('/bin/ffmpeg.exe') or n.endswith('/bin/ffprobe.exe') or n.endswith('/LICENSE.txt') or n.endswith('/COPYING.LGPLv3')]; \
import os; os.makedirs('vendor/ffmpeg/windows', exist_ok=True); \
for m in names: \
src = z.open(m); \
base = m.rsplit('/',1)[1]; \
dst = open(f'vendor/ffmpeg/windows/{base}', 'wb'); \
dst.write(src.read()); dst.close(); src.close(); \
print('extracted:', names)"; \
rm -f vendor/ffmpeg/windows/ffmpeg-win.zip; \
[ -f vendor/ffmpeg/windows/ffmpeg.exe ] || { echo "❌ ffmpeg.exe 沒被寫出"; exit 1; }; \
[ -f vendor/ffmpeg/windows/ffprobe.exe ] || { echo "❌ ffprobe.exe 沒被寫出"; exit 1; }; \
echo "==> ffmpeg.exe 大小:$$(du -h vendor/ffmpeg/windows/ffmpeg.exe | cut -f1)"; \
echo "==> ffprobe.exe 大小:$$(du -h vendor/ffmpeg/windows/ffprobe.exe | cut -f1)"; \
fi
```
---
## 4. Linux換 BtbN LGPL改一行 URL + 解壓路徑調整)
修改 `Makefile:331`
```makefile
# 原:
FFMPEG_URL_LINUX := https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
# 改為:
FFMPEG_URL_LINUX := https://github.com/BtbN/FFmpeg-Builds/releases/latest/download/ffmpeg-n7.1-latest-linux64-lgpl-7.1.tar.xz
```
修改 `Makefile:362-379` `vendor-ffmpeg-linux` target
```makefile
vendor-ffmpeg-linux: ## 下載 Linux LGPL ffmpeg + ffprobe → vendor/ffmpeg/linux/
@mkdir -p vendor/ffmpeg/linux
@if [ -f vendor/ffmpeg/linux/ffmpeg ] && [ -f vendor/ffmpeg/linux/ffprobe ]; then \
echo "==> ffmpeg + ffprobe (Linux) 已存在,跳過"; \
else \
echo "==> 下載 BtbN LGPL ffmpeg (Linux, n7.1)..."; \
curl -fL -o /tmp/ffmpeg-linux.tar.xz "$(FFMPEG_URL_LINUX)"; \
mkdir -p /tmp/ffmpeg-linux-extract; \
tar xf /tmp/ffmpeg-linux.tar.xz -C /tmp/ffmpeg-linux-extract --strip-components=1; \
cp /tmp/ffmpeg-linux-extract/bin/ffmpeg vendor/ffmpeg/linux/; \
cp /tmp/ffmpeg-linux-extract/bin/ffprobe vendor/ffmpeg/linux/; \
cp /tmp/ffmpeg-linux-extract/LICENSE.txt vendor/ffmpeg/linux/LICENSE.txt 2>/dev/null || true; \
chmod +x vendor/ffmpeg/linux/ffmpeg vendor/ffmpeg/linux/ffprobe; \
rm -rf /tmp/ffmpeg-linux* ; \
echo "==> ffmpeg (Linux) 大小:$$(du -h vendor/ffmpeg/linux/ffmpeg | cut -f1)"; \
echo "==> ffprobe (Linux) 大小:$$(du -h vendor/ffmpeg/linux/ffprobe | cut -f1)"; \
fi
```
**差別於 v1**
- URL 來源換掉
- `--strip-components` 從 1 改為 1BtbN tar 的 `strip-components=1` 後頂層就是 `bin/`,一樣)— **注意**BtbN 的 Linux tar 頂層目錄是 `ffmpeg-n7.1-latest-linux64-lgpl-7.1/`,解壓後 `strip-components=1` 會進到 `bin/``doc/``lib/``include/``share/``LICENSE.txt``README.txt``cp /tmp/ffmpeg-linux-extract/bin/ffmpeg` 路徑正確
- 同時抓 `ffprobe`
- 複製 `LICENSE.txt` 作為授權檔
---
## 5. Vendor directory layout
```
vendor/ffmpeg/
├── macos/ ← R5-6bcommit 到 git
│ ├── ffmpeg ← ~10 MB進 git
│ ├── ffprobe ← ~10 MB進 git
│ ├── COPYING.LGPLv3 ← 進 git
│ └── BUILD.md ← 進 gitreproducibility 稽核用
├── windows/ ← 不進 gitMakefile 下載
│ ├── ffmpeg.exe ← ~100 MBBtbN LGPL 打包含很多 LGPL extra libs未 strip
│ ├── ffprobe.exe ← ~100 MB
│ ├── LICENSE.txt ← BtbN 自帶
│ └── COPYING.LGPLv3 ← BtbN 自帶
└── linux/ ← 不進 gitMakefile 下載
├── ffmpeg ← ~80 MB
├── ffprobe ← ~80 MB
└── LICENSE.txt ← BtbN 自帶
```
**為什麼 Windows/Linux 不自 build 省體積?** — BtbN LGPL 還 link 了 libopus / libvpx / libaom / libdav1d / libopenh264 / libmp3lame / libvorbis 等 LGPL-safe extra libbinary 較大。對 Windows / Linux installer~380 / 320 MB 現況)多出 50-100 MB 屬可接受範圍;要把 Windows/Linux 也降到 20 MB 就要跑三平台 CI matrix 自 build方案 C`ffmpeg-lgpl-research.md` §4.1 已分析,多花 1-1.5 人天,不划算)。
**macOS 自 build 是不得不做**(沒有現成 LGPL 來源),順便做到 20 MB 是 bonus。
---
## 6. `.gitignore` 更新
修改 `.gitignore`
```diff
# ── 第三方依賴(由 make vendor-sync 下載,不進 git第三輪決策 Q-D=D2 ──
/vendor/**
!/vendor/.gitkeep
!/vendor/README.md
+# R5-6bmacOS LGPL ffmpeg binary 進 git沒有現成來源自 build 成本高)
+!/vendor/ffmpeg/
+!/vendor/ffmpeg/macos/
+!/vendor/ffmpeg/macos/**
```
`!` 規則的順序對 git 很敏感,這四行必須一起加;實測前需要用 `git check-ignore vendor/ffmpeg/macos/ffmpeg` 驗證)
**注意**`!/vendor/ffmpeg/macos/**` 會取消忽略 macos/ 底下所有檔,包括未來可能意外丟進來的 `.o` 或 build artifact。為了防呆BUILD.md 要明示「只 commit ffmpeg / ffprobe / COPYING.LGPLv3 / BUILD.md 四個檔」,程式碼 review 時檢查。
---
## 7. Payload 階段變化
修改 `Makefile:182-203` `payload-macos` target
```diff
-payload-macos: build-server vendor-python vendor-wheels vendor-ffmpeg vendor-ytdlp
+payload-macos: build-server vendor-python vendor-wheels vendor-ffmpeg
@echo "==> 建立 macOS payload..."
rm -rf payload/darwin
mkdir -p payload/darwin/bin payload/darwin/data payload/darwin/scripts payload/darwin/python payload/darwin/wheels
cp dist/visiona-local-server payload/darwin/bin/
- cp vendor/ffmpeg/darwin/ffmpeg payload/darwin/bin/
- cp vendor/yt-dlp/darwin/yt-dlp payload/darwin/bin/
- chmod +x payload/darwin/bin/ffmpeg payload/darwin/bin/yt-dlp
+ cp vendor/ffmpeg/macos/ffmpeg payload/darwin/bin/
+ cp vendor/ffmpeg/macos/ffprobe payload/darwin/bin/
+ cp vendor/ffmpeg/macos/COPYING.LGPLv3 payload/darwin/bin/ffmpeg-COPYING.LGPLv3
+ chmod +x payload/darwin/bin/ffmpeg payload/darwin/bin/ffprobe
```
Windows`payload-windows`)與 Linux`payload-linux`)同步:
```diff
# payload-windows
- cp vendor/ffmpeg/windows/ffmpeg.exe payload/windows/bin/
- cp vendor/yt-dlp/windows/yt-dlp.exe payload/windows/bin/
+ cp vendor/ffmpeg/windows/ffmpeg.exe payload/windows/bin/
+ cp vendor/ffmpeg/windows/ffprobe.exe payload/windows/bin/
+ cp vendor/ffmpeg/windows/COPYING.LGPLv3 payload/windows/bin/ffmpeg-COPYING.LGPLv3 2>/dev/null || true
# payload-linux
- @cp vendor/ffmpeg/linux/ffmpeg payload/linux/bin/ 2>/dev/null && chmod +x payload/linux/bin/ffmpeg || echo "!! WARN: ffmpeg 缺失"
- @cp vendor/yt-dlp/linux/yt-dlp payload/linux/bin/ 2>/dev/null && chmod +x payload/linux/bin/yt-dlp || echo "!! WARN: yt-dlp 缺失"
+ @cp vendor/ffmpeg/linux/ffmpeg payload/linux/bin/ && chmod +x payload/linux/bin/ffmpeg
+ @cp vendor/ffmpeg/linux/ffprobe payload/linux/bin/ && chmod +x payload/linux/bin/ffprobe
+ @cp vendor/ffmpeg/linux/LICENSE.txt payload/linux/bin/ffmpeg-LICENSE.txt 2>/dev/null || true
```
`vendor-ytdlp*` / `vendor/yt-dlp/` 整塊砍除(見 `v2/deletions.md` §4
---
## 8. 授權檔案在 installer 中的呈現
每個 installer 都要附上 ffmpeg 的 LGPL 條款與 source 對應指引。
**macOS**`installer/macos/`目前沒有這個目錄dmgbuild 直接從 `.app` 建)— 把 `ffmpeg-COPYING.LGPLv3` 放在 `.app/Contents/Resources/bin/` 底下(已透過 payload-macos 自動 cp加上 About dialog 裡的 linkv2 後續 design agent 設計)。
**Windows Inno Setup**:在 `installer/windows/visiona-local.iss``[Files]` 段加一行:
```diff
Source: "..\..\payload\windows\bin\ffmpeg.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
-Source: "..\..\payload\windows\bin\yt-dlp.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
+Source: "..\..\payload\windows\bin\ffprobe.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
+Source: "..\..\payload\windows\bin\ffmpeg-COPYING.LGPLv3"; DestDir: "{app}\bin"; Flags: ignoreversion
```
**Linux AppImage**`installer/linux/build-appimage.sh` 第 61-62 行原本迭代 `ffmpeg / yt-dlp`,改為 `ffmpeg / ffprobe`,並額外 copy `ffmpeg-LICENSE.txt``AppDir/usr/share/doc/visiona-local/`
---
## 9. 取得 LGPL source 的 offer
LGPLv3 要求散發者提供對應原始碼。兩種做法:
1. **Written offer**(省空間):在 About dialog / Help menu / PRD 公告處提供 URL
- macOS LGPL build → `https://github.com/<org>/visiona-local` repo 的 `vendor/ffmpeg/macos/BUILD.md` 指向 ffmpeg n7.1 source tarball 與確切 configure flags任何人可依此重現 build
- Windows / Linux BtbN build → `https://github.com/BtbN/FFmpeg-Builds`BtbN 的 source 對應)
2. **Bundle source**:不採用 — 會讓 installer 多幾十 MB
選 1。詳細文字由 PMM / PM Agent 寫成 About dialog 內容;本 TDD 只需確認技術可行。
---
## 10. 驗收條件
| 檢查 | 指令 | 預期 |
|------|------|------|
| macOS ffmpeg 為 LGPL | `vendor/ffmpeg/macos/ffmpeg -version \| grep -- --enable-gpl` | 無輸出 |
| macOS ffmpeg 不含 libx264 | `vendor/ffmpeg/macos/ffmpeg -version \| grep -- libx264` | 無輸出 |
| macOS ffmpeg 大小 < 25 MB | `du -h vendor/ffmpeg/macos/ffmpeg` | < 25 MB |
| macOS ffprobe 存在 | `test -f vendor/ffmpeg/macos/ffprobe && echo ok` | ok |
| macOS Gatekeeper | `spctl --assess --verbose vendor/ffmpeg/macos/ffmpeg` | accepted |
| 可解 mp4 | `vendor/ffmpeg/macos/ffmpeg -i sample.mp4 -f null -` | 正常完成 |
| 可解 avi | 同上 | 正常完成 |
| 可解 mov | 同上 | 正常完成 |
| 可解 mpeg/mpg | 同上 | 正常完成 |
| Windows LGPL | `ffmpeg.exe -version \| findstr -- --enable-gpl` | 無輸出 |
| Linux LGPL | 同上 | 無輸出 |
| installer COPYING.LGPLv3 | 裝完後檢查 `<install-dir>/bin/ffmpeg-COPYING.LGPLv3` 存在 | 存在 |
---
## 11. 待確認
1. **ffmpeg `--disable-network` 會不會影響既有的 `NewVideoSourceFromURL` / RTSP 路徑?** v1 `camera/video_source.go:86-88` `NewVideoSourceFromURLWithSeek` URLv2 砍掉 yt-dlp 後是否還保留 URL 推論路徑 `v2/deletions.md`URL 推論整條砍R5-7 前置`StartFromURL` handler ** `NewVideoSourceFromURL` 函式本身可能還有其他呼叫者**。M8-1 執行時要先 grep 確認若確實沒其他呼叫者就一起砍若有就要保留 `--enable-protocol=http,https` 之類。**Architect 建議 M8-1 執行前先做這個 grep 掃描並回報**。
2. **ffmpeg n7.1 的 sha256** 第一次 build 時由開發者執行 `shasum -a 256 build/ffmpeg-macos/source.tar.gz` 後填進 Makefile + BUILD.md之後每次 build 用它驗證
3. **codesign ad-hoc 在 notarize 階段** 目前全案採 ad-hoc sign 沒做 notarize若未來要 notarizeffmpeg + ffprobe 屬於 app bundle 內的執行檔會跟著 `.app` 一起送不需單獨處理