# ADR-020: vendor ffmpeg 加回 camera input device(indev)— 三平台 avfoundation / dshow / v4l2 ## 狀態 Proposed。 > 待「三平台 ffmpeg rebuild / 驗證 + `buildCaptureArgs` 補 Linux 分支 + 三平台實機驗(需實體攝影機)」全數完成後,轉 Accepted。 > 使用者目前僅有 macOS 機器可測,Windows / Linux 標為「待實機驗證」。 ## 日期 2026-08-02 ## 作者 Architect Agent ## 相關 - 根因評估:`local-tool/.autoflow/05-implementation/camera-ffmpeg-avfoundation-eval.md` - 打破的前提:v2 TDD §2(decoder-only ffmpeg 決策)、`vendor/ffmpeg/macos/BUILD.md` - 相關實作:`local-tool/server/internal/camera/ffmpeg_camera.go`、`ffmpeg_detect.go` --- ## 1. 背景與範圍 (Context) ### 1.1 觸發問題 camera 即時推論在真機報錯(camera 修 bug 讓「吞錯誤」不再發生後,底層錯誤終於浮現): ``` failed to open camera (index=0): camera did not start: ffmpeg exited before producing a frame: ffmpeg stream ended: EOF ... Unknown input format: 'avfoundation' Error opening input file 0:none ``` `Unknown input format: 'avfoundation'` = 這顆 ffmpeg binary **沒有編進 avfoundation input device(indev)**。 ### 1.2 根因(已 100% 確認,非推測) camera 抓實體攝影機是 **Go 端 `os/exec` 起 ffmpeg subprocess**(非 Python),指令形如 `ffmpeg -f avfoundation -i "0:none" ... -f image2pipe -vcodec mjpeg -`,ffmpeg 把攝影機輸出成連續 MJPEG stream 到 stdout,Go 端掃 JPEG SOI/EOI marker 切 frame。 問題在 vendor 的 macOS ffmpeg 是「decoder-only」自 build(v2 TDD §2 決策、`vendor/ffmpeg/macos/BUILD.md`),configure flags 為縮體積 `--disable-everything`,白名單只 re-enable 了「解碼既有檔案」需要的元件: ``` --disable-everything --enable-protocol=file,pipe --enable-demuxer=mov,avi,mpegps,mpegts,matroska,image2 --enable-decoder=h264,hevc,...,mjpeg,... --enable-parser=... --enable-filter=... --enable-muxer=image2pipe,image2,null --enable-encoder=mjpeg --disable-network ``` **白名單裡完全沒有任何 `--enable-indev=...`。** `--disable-everything` 一併關掉所有 input device,而白名單沒把 camera 需要的 indev 加回來 → `avfoundation` 認不得 → camera 開不了。 ### 1.3 為什麼只有 camera 壞、影片 / 圖片 / 批次正常 | 功能 | 用到的 ffmpeg 能力 | 白名單有無 | 結果 | |------|-------------------|-----------|------| | 影片 / 圖片 / 批次推論 | demuxer + decoder(解碼**既有檔案**) | ✅ 有 | 正常 | | camera 即時推論 | **indev**(從實體裝置抓 raw frame) | ❌ 沒有 | 壞 | 這是純「build 白名單漏了 indev」的問題,**不是程式碼邏輯錯**。 ### 1.4 跨平台現況(讀 code 確認) camera 三平台共用同一套 MJPEG pipe 抓取架構,但每平台用不同 indev: | 平台 | 抓取 indev | ffmpeg 來源 | ffmpeg indev 現況 | camera code 現況 | |------|-----------|------------|------------------|-----------------| | macOS | `avfoundation` | 自 build decoder-only | ❌ 缺 avfoundation(根因) | ✅ 完整 | | Windows | `dshow` | BtbN n7.1 完整 LGPL build | ⚠️ 大概率已含 dshow(**待實機驗**) | ✅ 完整(dshow 路徑齊全) | | Linux | `v4l2`(應為) | BtbN n7.1 完整 LGPL build | ⚠️ 大概率已含 v4l2(**待實機驗**) | ❌ 缺 Linux 分支 | **附帶 code bug(獨立於 ffmpeg)**:`buildCaptureArgs`(`ffmpeg_camera.go`)的 `switch runtime.GOOS` 只有 `windows` 與 `default`,`default` 分支用 avfoundation。**Linux 會誤落 `default` → 對 Linux 攝影機用 avfoundation → 必錯**。即使 macOS ffmpeg 修好,Linux camera 仍會壞。 --- ## 2. 決策 (Decision) 採**方案 A:rebuild / 驗證三平台 ffmpeg 加回對應 indev,並補 Linux camera code 分支**。 ### 2.1 三平台 ffmpeg indev | 平台 | 動作 | 具體 | |------|------|------| | macOS | rebuild 加 indev | `vendor-ffmpeg-macos-build` 的 configure 加 `--enable-indev=avfoundation`;rebuild;重算 sha256;更新 `BUILD.md`;commit 新 binary | | Windows | 驗證(大概率零改動) | 實機 `ffmpeg -f dshow -list_devices true -i dummy` 確認 BtbN build 已含 dshow;**若缺**才需換含 dshow 的 build | | Linux | 驗證(大概率零改動) | 實機 `ffmpeg -devices` 確認 BtbN build 已含 v4l2;**若缺**才需換 build 或補 `--enable-indev=v4l2` | > BtbN 官方 LGPL 完整 build(`win64-lgpl` / `linux64-lgpl`)預設含 dshow / v4l2 indev,故 Windows / Linux **預期零 ffmpeg 改動、只需實機驗證**。macOS 是自 build 精簡版,是唯一確定要 rebuild 的。 ### 2.2 補 Linux camera code 分支(獨立必補項) `buildCaptureArgs`(`ffmpeg_camera.go`)與 `ListFFmpegDevices`(`ffmpeg_detect.go`)補 Linux 分支,三平台 capture args 對照: | 平台 | 列裝置 | 抓 frame(capture args) | |------|--------|-------------------------| | macOS | `-f avfoundation -list_devices true -i ""` | `-f avfoundation -i ":none"` | | Windows | `-f dshow -list_devices true -i dummy` | `-f dshow -i video=""` | | Linux(新增) | `-f v4l2 -list_devices true -i ""` 或列舉 `/dev/video*` | `-f v4l2 -i /dev/video` | > 三平台後段皆接 `-f image2pipe -vcodec mjpeg -q:v 5 -an -`,MJPEG pipe 架構不動。 --- ## 3. 考慮過的替代方案 (Alternatives Considered) | 方案 | 優點 | 缺點 | 排除原因 | |------|------|------|---------| | **A. rebuild 加 indev(採用)** | 成本最低(macOS 加一行 flag);不推翻 decoder-only;LGPL 乾淨;架構不動 | 需 rebuild macOS binary + 補 Linux code 分支 | — | | B. 換完整版 ffmpeg | 省事、一次到位所有格式 | 體積爆增(40–70MB vs 現 5.7MB);推翻 v2 TDD §2 decoder-only;macOS 無現成 LGPL static 完整 build(正是當初自 build 的理由) | 唯一好處「省事」在 A 只加一個 flag 前提下不成立 | | C. camera 改用平台原生 API(AVFoundation / Media Foundation / V4L2 ioctl + cgo) | 不依賴 ffmpeg subprocess | 三平台各寫一套原生 + cgo,複雜度爆炸 = 重寫 camera 子系統 | 解「build flag 少一行」不該砍掉可用的抓取架構 | | D. macOS 專用第二顆含 avfoundation 的 ffmpeg | 主 binary 維持純解碼 | 多一顆 binary + 兩套 build 維護 | A 加一個 flag 就能讓同一顆 binary 兼顧(indev 增量 < 0.5MB),D 無意義 | --- ## 4. 後果 (Consequences) ### 4.1 打破的既有前提 本 ADR **打破 v2 TDD §2 decoder-only「不含 indev」的前提**。原決策的假設是「local-tool 只處理本地檔案解碼」,但 camera 即時推論需要從實體裝置抓 frame(indev),該假設對 camera 情境不成立。本 ADR 確立新契約: > **camera indev 是 ffmpeg build 白名單的必要一部分。** 未來升級 ffmpeg 版本 rebuild 時,三平台的 indev(avfoundation / dshow / v4l2)不得再遺漏。 ### 4.2 體積影響 | 平台 | 增量 | 說明 | |------|------|------| | macOS | **< 0.5 MB**(現 5.7MB) | avfoundation indev 是薄封裝,呼叫系統 AVFoundation / CoreMedia framework(`otool -L` 已顯示 binary 已 link 這些 framework),不自帶 codec | | Windows / Linux | 0(若零改動) | BtbN build 已含 indev,不重 build | ### 4.3 LGPL 合規 - avfoundation / dshow / v4l2 **皆為 LGPL-safe indev,不引入任何 GPL 元件**。 - macOS 維持 `--enable-version3`(LGPL v3),rebuild 後仍須通過既有驗證(`ffmpeg -version` 不含 `--enable-gpl` / `libx264` / `libx265`)。 - Windows / Linux 沿用 BtbN LGPL build,合規不變。 ### 4.4 三平台 indev 對照(契約,供未來 rebuild 參照) | 平台 | indev | ffmpeg 來源 | build 需求 | |------|-------|------------|-----------| | macOS | avfoundation | 自 build decoder-only | configure 白名單須含 `--enable-indev=avfoundation` | | Windows | dshow | BtbN 完整 LGPL build | 沿用 upstream(含 dshow) | | Linux | v4l2 | BtbN 完整 LGPL build | 沿用 upstream(含 v4l2) | ### 4.5 風險 - 低。macOS 改的是 build flag + 補一個 OS 分支(Linux code),不動 MJPEG pipe 抓取架構。 - 主要不確定性在「Windows / Linux BtbN build 是否已含 indev」,透過實機 `-list_devices` / `-devices` 驗證即可消除;若不含則退化為「換 build」(+1h)。 --- ## 5. 合規性 Checklist 實作與轉 Accepted 前須全數完成: - [ ] **macOS ffmpeg rebuild**:configure 加 `--enable-indev=avfoundation`、rebuild、重算 sha256、更新 `vendor/ffmpeg/macos/BUILD.md`(含新 configure line / sha256 / 大小)、commit 新 binary - [ ] **macOS LGPL 驗證**:rebuild 後 `ffmpeg -version` 不含 `--enable-gpl` / `libx264` / `libx265`;`ffmpeg -devices` 列出 avfoundation - [ ] **Windows dshow 驗證**:實機 `ffmpeg -f dshow -list_devices true -i dummy` 有列裝置(若缺 → 換含 dshow 的 build) - [ ] **Linux v4l2 驗證**:實機 `ffmpeg -devices` 含 v4l2(若缺 → 換 build 或補 flag) - [ ] **`buildCaptureArgs` 補 Linux 分支**:`ffmpeg_camera.go` + `ffmpeg_detect.go` 加 `case "linux": -f v4l2`(列裝置 + 抓 frame) - [ ] **三平台實機驗**:各平台接實體攝影機,camera 即時推論從「開不了」變「出 frame」 - [ ] **體積 / LGPL 確認**:macOS binary 增量 < 0.5MB、LGPL v3 合規未破 - [ ] **v2 TDD ffmpeg 章節交叉引用**(增補不覆蓋):註記「camera indev 為 build 白名單必要部分、三平台 indev 對照見 ADR-020」 - [ ] 與 Tech Lead / 使用者確認 - [ ] 成本影響已評估(體積增量微、無新增基礎設施成本) --- ## 6. WP 拆解(供 Orchestrator 排期) | WP | 內容 | 派誰 | 需實機 | 前置 | 估時 | |----|------|------|--------|------|------| | WP-1 | macOS:configure 加 `--enable-indev=avfoundation`、rebuild、重算 sha256、更新 BUILD.md、commit binary | devops(build/vendor)| macOS(使用者有) | — | 1–1.5h(含 ~3 分 rebuild) | | WP-2 | `buildCaptureArgs` / `ListFFmpegDevices` 補 Linux v4l2 分支(三平台 capture args 對照) | backend(Go camera code) | 否(可先寫、Linux 驗證階段測) | — | 1–1.5h | | WP-3 | Windows:實機 `-f dshow -list_devices` 驗證 dshow;若缺才換 build | devops | Windows(**待實機**) | — | 0.5h(順利)/ +1h(換 build) | | WP-4 | Linux:實機驗證 v4l2 + camera 即時推論測試 | devops + backend | Linux(**待實機**) | WP-2 | 1–1.5h | | WP-5 | v2 TDD ffmpeg 章節交叉引用(增補);ADR-020 轉 Accepted | architect | 否(待 WP-1~4 完成) | WP-1~4 | 0.5h | > **平行 / 接力**:WP-1(macOS ffmpeg)與 WP-2(Linux code)可**平行**(互不相依、不同檔)。WP-3(Windows 驗)獨立可平行。WP-4(Linux 驗)**接力 WP-2**。WP-5 收尾,等前四個。 > **關鍵路徑**:WP-2 → WP-4 → WP-5(Linux 驗需先有 code 分支)。 > **使用者只有 macOS**:WP-1 可立即做並驗證;WP-3 / WP-4 的實機驗證需 Windows / Linux 機器,標為「待實機」,先寫好 code / build 再擇機驗。 --- ## 7. 等級與後續 - **等級**:M 級(跨多檔 + 跨三平台 + 動 vendor build 策略,但非新 user story / 新架構)。 - **後續**:ADR 定稿後由 Orchestrator 派 devops(ffmpeg rebuild / 驗證)+ backend(Linux camera code 分支)落地。camera 不阻擋 B+C 主線,可獨立排期。