# 套件劃分

URL: https://flow.lanstar.dev/docs/architecture/packages

> Swift Packages 的目錄、命名、分層、依賴方向、邊界規則與 C 函式庫橋接。總覽見 [`architecture.md`](/docs/architecture)。

## 目錄

```
Flow/
├── Apps/
│   └── FlowMac/                 # App target；只經 FlowComposition 的 FlowPlayer 與 PlayerAPI 操作播放器
├── Benchmarks/                  # package-benchmark：micro、播放情境、size、compete、baseline（docs/benchmark.md）
├── Packages/
│   ├── Core/                    # 不含任何實作的播放器核心；各 target 位於 Sources/<層>/<target>/，測試位於 Tests/<層>/
│   │   ├── Contract/            # 契約：模組、UI 與自動化編譯時依賴的型別與協定
│   │   │   ├── FlowMedia/       # 媒體詞彙值型別，前端也用：MediaTime、HostTime、FrameRate、TrackInfo（CodecID、VideoFormat、ChromaFormat）、ColorInfo、HDRMetadata、FrameGeometry、SeekMode
│   │   │   ├── FlowDiagnostics/ # Logger、Signpost、計數器
│   │   │   ├── FlowPlayerAPI/   # PlayerAPI、Command／Property／Event、統計值、按鍵綁定與內建名稱；UI 與自動化唯一能碰的介面
│   │   │   └── FlowModuleAPI/   # FlowModule、擴充點協定、資料平面型別（Packet、VideoFrame、幀排列 VideoPixelFormat／ChromaSubsampling、PCMFormat）、DisplayPlan、SPSC ring
│   │   ├── Kernel/              # 只有 Composition、IntegrationTests、Benchmarks 連結（UI、MCP 測試另可連結 FlowController）
│   │   │   ├── FlowRegistry/    # ComponentRegistry：依擁有者記錄與撤銷註冊
│   │   │   ├── FlowGraph/       # Display Graph 編譯、pass 切分
│   │   │   ├── FlowController/  # PlayerController（PlayerAPI 實作）、PropertyStore、預設按鍵綁定（InputConf）
│   │   │   ├── FlowEngine/      # PlayerSession 狀態機、執行緒、FrameQueue／PCM ring、時鐘與 A/V 同步、Seek、元件選擇與 fallback
│   │   │   └── FlowModuleHost/  # 模組啟用／停用
│   │   └── Development/         # 開發用：App 不連結；Core 自身的測試也用，故留在 Core
│   │       ├── FlowSampling/    # 行程／執行緒／記憶體取樣器；測試與 benchmark 用
│   │       └── FlowTestSupport/ # mock 模組、TestModuleContext、fixture 定位、MemorySource；Core、模組、Composition 與整合測試共用
│   ├── Composition/             # FlowComposition：composition root，唯一組裝 Modules/* 之處；App 與 Benchmarks 共用
│   ├── IntegrationTests/        # 真實模組 + mock 輸出的端到端播放測試
│   ├── MCP/                     # FlowMCP，前端：MCP server（Streamable HTTP，僅 loopback）
│   └── UI/                      # FlowUI，前端：SwiftUI 元件（控制列、header、選單）
├── Modules/                     # 實作；模組本身靜態連結（LGPL 函式庫除外）
│   ├── File/                    # FileSource：本機檔案 MediaSource
│   ├── FFmpeg/                  # FFmpegDemuxer、FFmpegAudioDecoder
│   ├── VideoToolbox/            # VTVideoDecoder
│   ├── AVAudio/                 # AVAudioEngine AudioOutput
│   └── Metal4/                  # Metal4DisplayRuntime、MetalLayerPresenter、OffscreenPresenter
│       ├── Plugins/             # MetalLibraryPlugin：建置期把 Shaders/*.metal 編譯為 FlowMetal4.metallib
│       └── Shaders/
│           ├── Include/Flow/    # Shader ABI（見 modules.md#metal-4）
│           └── Display.metal    # 內建 display pass
├── Vendor/                      # 由 scripts/ 產生，不入庫
│   ├── lib{avformat,avcodec,avutil,swresample}.xcframework  # FFmpeg；LGPL，動態；scripts/build-ffmpeg.sh
│   └── Licenses/                # 各函式庫授權全文，隨 App 附於 Resources/Licenses/
├── Tests/（各 package 內）     # 單元、golden、GPU 測試
└── docs/
```

## 命名

資料夾與 package 名稱為角色（`Core`、`Composition`、`UI`、`FFmpeg`），product 與 Swift module 加 `Flow` 前綴（`FlowUI`、`FlowFFmpeg`），以免與 Apple framework（`AVAudio`、`VideoToolbox`）或其他 module 撞名。`Core` 與 `IntegrationTests`（只有測試 target，無 product）以外皆為單一 product `Flow<資料夾>`。

## 分層

Kernel 為核心中契約以外的 target：掌管邏輯、生命週期、執行緒、佇列、時鐘與元件選擇，不含任何模組實作。Core 的層即 `Sources/` 下的目錄（`Contract`、`Kernel`、`Development`），`check-deps.sh` 據此判定各 target 所屬的層。

| 層 | 位置 | 可依賴 |
| --- | --- | --- |
| 契約 | `FlowMedia`、`FlowDiagnostics`、`FlowPlayerAPI`、`FlowModuleAPI` | 契約 |
| Kernel | `FlowRegistry`、`FlowGraph`、`FlowController`、`FlowEngine`、`FlowModuleHost` | 契約、Kernel |
| 模組 | `Modules/*` | 契約、runtime 模組 |
| 組裝 | `Composition` | 契約、Kernel、模組；測試另可用開發用 |
| 前端 | `UI`、`MCP` | `FlowMedia`、`FlowDiagnostics`、`FlowPlayerAPI` |
| App | `Apps/FlowMac` | 前端、`FlowComposition`、`FlowMedia`、`FlowDiagnostics`、`FlowPlayerAPI` |
| 開發用 | `FlowSampling`、`FlowTestSupport` | 契約；App 不連結 |
| 工具 | `IntegrationTests`、`Benchmarks` | 不限；App 不連結 |

`check-deps.sh` 不區分模組間的依賴：模組可依賴任何模組，是否為 runtime 模組由 review 把關。

## 依賴方向

只能向下：

```
Apps → {FlowUI, FlowMCP, FlowComposition}
FlowComposition → {FlowModuleHost, FlowEngine, Modules/*}
{FlowUI, FlowMCP} → FlowPlayerAPI
FlowModuleHost → FlowRegistry
FlowEngine → {FlowGraph, FlowRegistry, FlowController}
{FlowGraph, FlowRegistry} → FlowModuleAPI → FlowPlayerAPI → FlowMedia
{FlowController, FlowModuleHost} → FlowDiagnostics
FlowController → FlowPlayerAPI
Modules/* → {FlowModuleAPI, FlowDiagnostics}
```

## 邊界規則

- `Core/` 只定義契約並掌管邏輯、生命週期與控制平面；不得匯入 SwiftUI、AppKit、Network、Metal、VideoToolbox、AVFAudio 與任何 vendored C 函式庫（CoreVideo 僅作為 `CVPixelBuffer`／IOSurface 的不透明載體，契約中只出現在 `FlowModuleAPI`；前端依賴的 `FlowMedia`、`FlowDiagnostics`、`FlowPlayerAPI` 不匯入 CoreVideo、CoreMedia、IOSurface）。SwiftLint custom rule（`core_banned_import`、`core_banned_link`、`frontend_contract_banned_import`）檢查 `Packages/Core` 的 import 與連結。
- `Modules/` 只能依賴契約、自身的 vendored 函式庫，以及所需的 runtime 模組；不得依賴 Kernel。模組測試另可用 `FlowTestSupport`（`TestModuleContext` 記錄註冊）。
- `FlowUI` 與 `FlowMCP` 只依賴 `FlowMedia`、`FlowDiagnostics`、`FlowPlayerAPI`；App 另依賴前端與 `FlowComposition`，只經 `FlowPlayer` 與 `PlayerAPI` 操作播放器；兩者都不依賴 `FlowModuleAPI`、`FlowEngine`。例外是 `FlowUI`、`FlowMCP` 的測試 target 可依賴 `FlowController`，以真實 `PlayerController` 搭配 stub handler；`FlowPlayer` 只以 `@_spi(Benchmark)` 開放指定模組的 init、額外 presenter 與 `PresentationSurface` 給 Benchmarks 與自身的測試，不暴露 registry、display runtime 與 `PlayerSession`；統計經 `PlayerAPI.statistics`，與前端同一條路。
- 依賴由 `Package.swift` 宣告強制，`scripts/check-deps.sh`（prek）依上表檢查各 manifest（含 Core 各層 target）、`project.yml` 各 target 連結的 product 與 App 的 import（App 不得以 `@_spi`、`@testable` import），並檢查命名；每次先以自我測試確認各規則擋得下違規；package 的 import 由 explicit module build 限制在依賴閉包內；所有 package 與 App target 啟用 `MemberImportVisibility`（只能使用本檔 import 的模組成員）、`ExistentialAny`、`InternalImportsByDefault`（import 預設 internal，公開 API 用到的型別才 `public import`，vendored C 型別因此無法出現在公開 API），警告視為錯誤；`scripts/check-settings.sh` 確認各 `Package.swift` 與 `project.yml` 設定一致。
- 模組只公開 `FlowModule` 型別；其餘實作為 internal，測試以 `@testable import` 存取。Core 與模組中只有 Benchmarks 需要的型別（如 `FrameQueue`、`PlaybackClock`）以 `@_spi(Benchmark)` 開放，不設為 `public`。
- 是否「必要、不可停用」由 App 組裝時標記（見[模組模型](/docs/architecture/modules#模組模型)），不由資料夾決定；核心不內建任何實作，沒有模組時無法播放。
- 官方功能用 `FlowModuleAPI` 寫不出來即視為介面缺陷。

## C 函式庫

vendored C 函式庫（`Modules/*`）的橋接規則：

- C 型別與指標只留在各自的 `Modules/<Lib>`，跨界只用 `FlowModuleAPI` 型別。
- 同一模組的元件可辨識自己的 `Packet.owner` 型別、直接共用 C 物件（如 `FlowFFmpeg` 的音訊解碼器借出 demuxer 的 `AVBufferRef`），但只能省配置或拷貝：輸出只依契約欄位決定，與其他 owner 的 packet 相同。
- `AVPacket`、`AVFrame` 等結構在 `Factory.make` 或 `init` 配置一次，處理時以 `av_packet_unref`／`av_frame_unref` 重用，於 `deinit` 釋放。FFmpeg 每次呼叫仍會配置資料 buffer，故只在吞吐級執行緒處理（見[執行緒分級](/docs/architecture/engine#執行緒分級)）。
- `withUnsafe*` 取得的指標不得逃出 closure；長期使用的記憶體自行 `allocate`，並明確指定擁有者。
- C callback 用 `@convention(c)`，以 `Unmanaged.passUnretained(self).toOpaque()` 傳 context；無法保證擁有者比 C 端活得久時用 `passRetained`，拆除時 `release()`。
- C callback 跑在函式庫自己的執行緒：依該執行緒分級，不得阻塞，不碰 actor 狀態。
