套件劃分
Swift Packages 的目錄、命名、分層、依賴方向、邊界規則與 C 函式庫橋接。總覽見 architecture.md。
目錄
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、@testableimport),並檢查命名;每次先以自我測試確認各規則擋得下違規;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 組裝時標記(見模組模型),不由資料夾決定;核心不內建任何實作,沒有模組時無法播放。
- 官方功能用
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,故只在吞吐級執行緒處理(見執行緒分級)。withUnsafe*取得的指標不得逃出 closure;長期使用的記憶體自行allocate,並明確指定擁有者。- C callback 用
@convention(c),以Unmanaged.passUnretained(self).toOpaque()傳 context;無法保證擁有者比 C 端活得久時用passRetained,拆除時release()。 - C callback 跑在函式庫自己的執行緒:依該執行緒分級,不得阻塞,不碰 actor 狀態。