- feature-device-mgmt-tdd.md + api-device-mgmt.md(B 設備管理 TDD) - feature-model-sharing-tdd.md + api-model-sharing.md + PRD feature + 設計規格(C 模型共享三方規劃) - adr-020-ffmpeg-camera-indev.md(camera 三平台 indev) - PRD.md / TDD.md 索引增補 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
191 lines
11 KiB
Markdown
191 lines
11 KiB
Markdown
# 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 "<index>:none"` |
|
||
| Windows | `-f dshow -list_devices true -i dummy` | `-f dshow -i video="<name>"` |
|
||
| Linux(新增) | `-f v4l2 -list_devices true -i ""` 或列舉 `/dev/video*` | `-f v4l2 -i /dev/video<N>` |
|
||
|
||
> 三平台後段皆接 `-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 主線,可獨立排期。
|