儲存庫架構與設計
本頁面是為貢獻者提供的,他們需要了解函式庫的結構、責任歸屬,以及如何在不破壞其模組和執行階段合約的情況下擴展框架。
框架與環境
「Neat」一詞用於描述兩個相關但獨立的概念:
- Neat Library: 本儲存庫中的 C++/Python 函式庫和執行階段。它載入模型套件、組成管線、驗證合約、在 Modalix 硬體上執行,並公開 API。
- Neat SDK / 環境: 圍繞框架的容器化開發工作流程,包括 DevKit Sync、共享工作區和代理工具。
在修改此儲存庫時,請針對支援人和代理的框架屬性進行最佳化:明確的 API、確定性行為、結構化的診斷、嚴格的驗證和穩定的公共合約。
本函式庫的用途
主要使用者
希望:
- 從可重複使用的建置模塊中組裝管線(無需編寫原始 GStreamer 程式碼)
- 儘早驗證管線(適合 CI),並快速了解失敗原因
- 透過
appsink在 C++ 中執行管線並消費幀 - 可選擇透過 RTSP 提供管線(透過
gst-rtsp-server) - 透過適合張量的輸出提供機器學習程式碼,而無需編寫 GStreamer 程式碼
套件所有權
選定的核心成品是 Neat、LLiMa 和一起安裝的內部 Debian 套件的權威來源。核心使用並轉發該成品,而無需選擇或重寫相依性版本。成品之外的套件仍由平台擁有;不相容的平台必須進行更新,而不是由核心或 LLiMa 進行修復。
常見工作流程
- 解碼/導入: 檔案或 RTSP -> 拆分/多工/解析 -> 解碼 -> 轉換/caps -> appsink -> C++ 消費者
- 驗證: 建置 + 解析 + 預滾動(暫停)以儘早捕獲協商問題
- 提供 RTSP: 使用
appsrc將合成幀推送到 RTSP 伺服器管線 - 影像/影片張量適配器: 影像/影片/RTSP -> 解碼 -> 轉換/縮放 ->
add_output_tensor(...)->Run::pull_tensors() - 教學: 從 教學指南 開始,以獲得可執行的、有序的學習路徑
標準生產管線(權威來源)
此儲存庫的標準「生產路徑」為:輸入 -> 預處理 -> MLA -> 後處理。權威來源位於:tests/e2e_pipelines/obj_detection/sync_yolov8_test.cpp。
當此測試發生更改時,請更新 README + 架構,以保持 檔案的一致性。
心理模型(業務邏輯 <-> 管線黏合劑)
您的應用程式保留業務邏輯;框架擁有管線黏合劑。
Business logic
|
v
Nodes/Graph fragments -> GStreamer fragments -> caps negotiation -> runtime (Run)
| |
+-----------------------------------------------------------+
Sample / Tensor
核心概念
此框架刻意 圍繞少數核心概念設計。大多數使用者程式碼只會與 Model、Graph、Run、Tensor 和 Sample 互動;較底層的貢獻者也會使用 Node、可重複使用的 Graph 片段、MPK 合約解析及圖的內部結構。
| 概念 | 角色 |
|---|---|
| 模型封存檔 | 一個封裝的 .tar.gz 成品,其中包含 MPK 推論合約、外掛程式私有設定、模型二進位檔和核心成品。 |
Model | 用於載入 .tar.gz 模型封存檔的公開載入器。它會解析 MPK 合約、執行路徑規劃、公開模型階段,並提供簡單的 run(...) / Graph 組成進入點。 |
Tensor | 帶有 dtype、shape、layout、儲存、裝置和語義中繼資料的類型化數值有效載荷。 |
Sample | 圍繞張量、張量列表或捆綁包的執行階段/媒體封套。在讀取欄位之前,請檢查 Sample::kind。 |
Node | 一個原子管線階段,它會產生一個確定性的 GStreamer 片段和所擁有的元素名稱。 |
| 可重複使用的 Graph 片段 | 預先建立的 Graph,可展開為多個節點,例如已解碼的 RTSP 輸入或模型階段。 |
Graph | 組裝和驗證邊界。節點、模型和可重複使用的 Graph 片段會組成經過協商且可建置的管線。 |
Run | 由 Graph::build(...) 傳回的即時管線句柄;擁有推送/拉取/執行階段生命週期。 |
| 圖 | 使用建構器圖來在單個管線內進行 DAG 組成;使用執行階段圖來協調跨管線的階段/執行。 |
從左到右閱讀它們之間的關係:
model archive on disk -> Model -> Graph fragments/Nodes -> Graph -> Run
|
v
Tensor/Sample flow
Model 是使用者入門的起點,但它並非獨立的執行引擎。它會解析為模型圖的片段/節點,這些片段/節點可以新增到 Graph 中。Graph 是核心的組裝概念;Run 是建置完成後產生的即時物件。
對於貢獻者的設計原則
這些是框架背後的關鍵架構原則。在決定實作選項時,請使用它們。
- 確定性優先。 維持元件名稱、產生的管線字串、序列化的管線資料、報告欄位和測試的可重現性。診斷和代理迴圈取決於穩定的識別碼。
- 可除錯性是首要考量。 錯誤應產生結構化資料,而不僅僅是字串:
GraphReport.error_code、repro_note、匯流排訊息和可重現的後端管線。 - 不允許靜默回退。 不要透過靜默地轉換格式、變更圖族群、回退到 CPU 或忽略外掛程式錯誤來隱藏模型輸入錯誤或硬體/執行階段失敗。
- 在執行前進行驗證。 偏好在執行階段執行緒啟動或取得硬體資源之前進行結構、上限、形狀和合約驗證。
- MPK 合約是模型的事實來源。 核心路由、dtype、形狀、量化和階段決策必須來自
mpk.json/*_mpk.json。每個階段的 JSON 檔案都是外掛程式專用的。 - 邏輯等級和執行階段幾何形狀是分開的。 核心會保留 MPK 撰寫的
frame_shape作為邏輯輸出合約,並在需要時推導出明確的 MLA 幾何形狀。只有在已宣告的位元組範圍識別出唯一的解釋時,才會接受等級 2 的形狀作為 NC 或 HW。模糊或不一致的合約會在模型載入期間失敗。 - 公開 API 應保持穩定。
include/*下的公開標頭會安裝並受到支援。優先採用增量變更和棄用路徑,避免破壞簽章的變更。 - 並行性必須受到限制且可觀察。 串流執行緒的工作應盡可能輕量;探測端診斷需要原子操作或等效的執行緒安全處理;關閉時不應發生死鎖。
模型執行路徑
對於以模型為基礎的管線,高階路徑如下:
input Sample/Tensor
-> optional preprocessing / format normalization
-> MLA inference stages selected from MPK contract
-> optional postprocessing / box decode
-> output Sample/Tensor
使用者可見的 Model 介面,其設計目的就是比 MLA 硬體介面更為友善。MLA 可能需要 INT8/BF16 和鑲嵌佈局,而使用者程式碼通常使用 FP32 和標準張量佈局。此框架透過以資訊檔為基礎的配接器階段來彌合此差距。
預處理和後處理是明確的框架階段/選項。如果發生格式不匹配、缺少必要的預處理中繼資料、MLA 派遣器不可用、模型封存檔或 MPK 契約無效,或 caps 協商失敗,則應以可操作的結構化錯誤形式呈現,而不是以隱藏的執行階段修正來處理。
儲存庫佈局
高階結構
include/-- 公開標頭檔(支援的 API 介面)src/-- 實作docs/-- 檔案(本檔案)examples/-- 小型可執行範例tests/-- 單位/整合測試python/--pyneat套件來源、nanobind 繫結和 Python 測試old_*-- 舊版單體實作快照,用於參考/遷移
公開標頭樹 (include/)
公開標頭檔位於 include/<module>/... 下。
範例:include/pipeline/Graph.h、include/model/Model.h。
公開便利的進入點標頭檔:
include/neat.h(總攬)include/neat/runtime.hinclude/neat/models.hinclude/neat/nodes.hinclude/neat/node_groups.h
有意沒有 include/neat/graph.h 公開總攬。需要較低層級圖基礎的執行階段/編譯器測試,會直接包含較窄的 include/graph/... 標頭檔。應用程式、範例和公開檔案應使用來自 <neat.h> 的單一公開 simaai::neat::Graph。
內部標頭檔和執行階段外掛程式路徑
位於 include/ 下的公開標頭檔會被安裝並視為穩定的 API。
位於 src/**/internal 下 的內部標頭檔不會被安裝;範例/教學應該僅使用公開 API。
執行階段環境注意事項:
- 如果使用
deps/gst-plugins中的捆綁 GStreamer 外掛程式,請將GST_PLUGIN_PATH和/或GST_PLUGIN_PATH_1_0設定為包含該目錄。 - 如果使用
cmake --install安裝,外掛程式會放置在${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/sima-neat/gst-plugins下。 將該路徑新增到GST_PLUGIN_PATH和/或GST_PLUGIN_PATH_1_0。 - 使用
scripts/use_neatdecoder.sh為目前的 shell 設定外掛程式路徑。 - 如果要以系統範圍方式安裝外掛程式,請重新建置系統 GStreamer 快取。
規劃與穩定 (API 介面)
| 區域/API | 狀態 | 備註 |
|---|---|---|
核心管線 API (Graph、Run、Tensor、Sample) | 穩定 | 主要支援的 C++ 介面。 |
建置器內部元件 (Node、私有節點向量輔助工具、GraphPrinter) | 內部 | 僅使用 STL,支援 GStreamer 之前的組合。 |
模型 API (Model、可重複使用的 Graph 片段) | 穩定 | 標準模型檔案整合路徑。 |
include/policy/* | 穩定 | 最少的已驗證策略合約和預設值 (Decoder、Encoder、Memory、RTSP)。 |
include/nodes/groups/ImageToH264RtspGroup.h | 已規劃 | 空的佔位符群組。 |
Python 綁定 (python/、pyneat) | Beta | 基於 Nanobind 的綁定和封裝位於儲存庫中;API 介面著重於 Tensor、Graph/Run、Model,以及核心節點/群組輔助工具。 |
模組和職責
builder/ -- 節點合約和私有線性組合支援(不使用 GStreamer)
目的: 定義如何從邏輯部分組裝管線。
主要類型:
Node-- 每個管線建置區塊所實作的介面- 私有節點向量輔助工具和
GraphPrinter-- 組合工具和診斷
規則: 建置器必須主要使用 STL。它不應擁有 GStreamer 執行階段物件。
nodes/ -- 已定義類型的管線建置區塊
目的: 提供隨即可用的節點實作,這些節點會產生確定性的 GStreamer 片段。
範例:
nodes/io/HttpSource、nodes/io/RTSPInput、nodes/io/StillImageInputnodes/common/*(Caps、Queue、Output 等)nodes/sima/*(SiMa.ai 解碼/編碼/解析/酬載節點)nodes/rtp/*(depay/payload 輔助工具)nodes/groups/*(常見的多節點配方)
合約: 每個節點都必須產生:
backend_fragment(index)-- 在特定索引處,此節點的 GStreamer 片段element_names(index)-- 此節點所擁有的確定性元素名稱(用於診斷和強制執行)
gst/ -- 簡潔的 GStreamer 工具
目的: 針對常見的 GStreamer 模式提供小型包裝函式/輔助工具。
範例:
- 初始化 (
GstInit) - 解析啟動字串 (
GstParseLaunch) - 匯流排排清/字串化 (
GstBusWatch) - caps 輔助工具/元素內省 (
GstHelpers、GstIntrospection) - 填充點/探測輔助工具 (
GstPadTap)
規則: gst/ 不應依賴 pipeline/(以避免依賴循環和「工具層」膨脹)。
pipeline/ -- 執行階段協調和公開 API
目的: 擁有執行階段生命週期:建置 -> 解析 -> 執行 -> 消耗 -> 拆解,並提供診斷資訊。
主要類型:
Graph-- 使用者的主要進入點Run-- 具有推入/拉出 API 的執行管線處理器Sample-- 拉取操作所傳回的結構化輸出酬載GraphReport-- 針對失敗、停頓和重現的結構化診斷資訊Errors-- 異常 (NeatError) 嵌入報告
錯誤語義合約
GraphReport.error_code 是標準的機器可讀錯誤碼欄位。框架中的
執行階段/建置/I/O 路徑將終端錯誤映射到穩定的程式碼分類:
misconfig.pipeline_shapemisconfig.capsmisconfig.input_shapemisconfig.input_capacitymisconfig.media_capsmisconfig.tensor_dtype_missingmisconfig.option_out_of_rangebuild.parse_launchbuild.pipeline_syntaxbuild.plugin_missingbuild.property_invalidruntime.pullruntime.element_failedruntime.output_timeoutio.parseio.openio.file_not_foundio.permission_deniedio.rtsp_connection_failedio.camera_not_foundcodec.*、resource.*、infra.*和internal.*
GStreamer 錯誤會通過一個內部解析器、分類器和渲染器。
分類器優先使用帶版本號的 Neat 診斷 ID,然後使用原生
GStreamer 域/程式碼和元件工廠,然後縮小舊版外掛程式的相容性映射。
未知的錯誤會使用 runtime.element_failed;除非實際協商失敗,否則不會
將其報告為 misconfig.media_caps。當管線發布多個錯誤時,會渲染最特定的根本原因,並且每個錯誤都會
保留在匯流排日誌中。
GraphReport.repro_note 是面向使用者的摘要。生產渲染包含
以純文字形式呈現的原因、相關的觀察/預期值、具體的
使用者操作以及穩定的診斷 ID。原始外掛程式字串、來源位置和
GStreamer 域/程式碼僅用於除錯。在 NeatError 建立時,會添加一次括號中的公共程式碼。
GraphReport.bus 是外掛程式/執行階段錯誤詳細資訊的真實來源。
對於建置(輸入)流程,GraphReport.build_adaptation 會記錄已解析的形狀原則/功能、種子/最大限制的來源、位元組保護的來源以及應用/跳過的調整動作。
對於非拋出型執行階段拉取,PullError.code 使用相同的分類法。
輸入串流工作程式失敗會保留類型化的錯誤程式碼並報告到
工作程式執行緒邊界,因此 Run::pull() 和 Python 例外翻譯器
會顯示相同的 NeatError。
支援優先順序是:
- 依
error_code分組 - 閱讀
repro_note - 檢查第一個終端
bus錯誤 - 使用
repro_gst_launch重新執行
內部管線診斷
在 src/pipeline/internal/(僅限內部使用)下:
Diagnostics.h-- 執行階段使用的共享診斷類型:DiagCtx(匯流排日誌 + 節點報告 + 邊界/元件計數器)BoundaryFlowCounters(從串流執行緒更新的原子計數器)ElementTimingCounters(原子每元件計算時間)ElementFlowCounters(原子每元件流量統計資訊)
GstDiagnosticsUtil.h-- 用於格式化和收集 GStreamer 診斷資訊的輔助程式
SIMA 靜態資訊清單上下文合約
對於模型管線,框架會建置靜態階段/張量合約資料,並將其注入為
管線層級的 GstContext:
- 環境類型:
sima.model.manifest.v1 - 環境欄位:
manifest_versionmanifest_json(舊版相容性負載)manifest_accessor_v1(ABI 安全的存取器表格指標)- 可選的
session_id、model_id
- 模型資訊的所有權/生命週期與管線的生命週期相關聯;外掛程式借用指標並複製 它們需要的內容。
- 儲存庫邊界:此儲存庫不得新增建置時對外掛程式/分派器儲存庫的依賴。整合僅透過介面進行(執行階段
GstContext、屬性、caps/meta,以及 C-ABI 合約)。
已遷移欄位的解析器優先順序是確定性的:
- 從合約/執行階段訊號推斷(形狀/meta/caps)
- 環境/預設/屬性路徑
- 嚴格的匯流排錯誤(永遠不會中止/SIGSEGV)
StageTransformRuleRegistry(內部)是單一的映射表格,它告訴解析器哪些非 MLA 階段繼承來自 MLA 輸入與 MLA 輸出的張量合約,以及何時傳播輸出量化。這使得預/後推導過程明確且可測試。
對於使用聚合器範本的已遷移 SIMA 外掛程式,執行階段設定現在遵循環境/屬性驅動的解析:
- 階段靜態欄位來自模型資訊環境
- 執行階段旋鈕來自屬性/環境預設值
- 未解析的必要欄位會明確失敗(框架中沒有階段 JSON 備援)
對於 simaaiprocesscvu,CM 衍生的接線首先進行推斷,並且使用環境 sink_pad_tensor_index_map
進行確定性的多輸入映射;舊版輸入緩衝區名稱僅作為備援。
如果提供 logical_stage_id,則會從 stage-id/stage_id 管線屬性中解析,否則它會回退到元素名稱。
SIMA 模型路徑片段建構器預設會在 simaaiprocesscvu、simaaiprocessmla 和 simaaiboxdecode 元素上設定 stage-id。
YOLO26 BoxDecode 類別計數合約
對於模型管理的 YOLO26 偵測、姿勢和分割路徑,MPK 類別標頭深度是權威的類別計數。Model::Options::num_classes = 0 選擇該推斷值。一個正值必須與其匹配;如果出現矛盾,則會在合約建構期間失敗,並報告已設定的值、MPK 衍生的值和解碼類型。這可防止使用無效的類別計數來解釋分組的原始標頭佈局。SSD 和預先 YOLO26 非姿勢 YOLO 系列保留其現有的明確覆寫行為,而姿勢和 SuperPoint 解碼器保留其系列特定的規則。
SuperPoint BoxDecode 合約
SuperPoint 使用與其他模型管理的 BoxDecode 系列相同的 MPK 到靜態模型資訊邊界,並具有以下其他不變性:
- MPK 記錄擁有檢測器-logits 和描述符-grid 張量識別碼、儲存表示法、dtype/shape 資訊、數值設定檔來源,以及可選的明確 NMS 和邊界控制。核心永遠不會從張量值中識別這些角色。
- 核心將精確地將一個張量繫結到每個角色,驗證設定檔指紋和支援的表示法,應用明確的
Model::Options::superpoint覆寫,並且僅解決省略的設定檔預設值。更改設定檔會重新計算其衍生的預設值,同時保留 MPK 或 API 明確設定的控制項。 - 版本化的靜態資訊清單 ABI 攜帶已解決的合約到
simaaiboxdecode。外掛程式僅在設定期間借用資訊清單指標,並且必須複製執行階段所需的任何狀態;核心在管線的整個生命週期內保留資訊清單的所有權。 - 實際輸出使用
FEATURE_POINTS_V1線格式和特徵語義中繼資料。FEATURE_POINTS_LEGACY_A65_V0僅在明確選擇以用於相容性時才可用;使用者不應從緩衝區大小推斷任何一種格式。
contracts/ -- 驗證規則
目的: 編碼「有效管線的樣子」,而不僅僅是「gst_parse_launch 成功」。
範例:
- 驗證器介面和註冊表
- 結構化的
ValidationReport
此層可用於 CI,並在執行階段之前捕獲問題。
policy/ -- 可供使用者調整的行為
目的: 將可調整的參數(預設值、記憶體限制、編碼器/解碼器/RTSP 策略選項)集中化。
目標是使「旋鈕」明確且易於發現,而不是隱藏在分散的程式碼中。
模型封存檔整合
目的: 透過 Model 載入 .tar.gz 模型封存檔,並將解析後的 MPK 推論合約轉換為可路由的 Graph 片段。
安全的封存檔載入器是內部實作細節;應用程式程式碼應建立 Model 並組合 model.graph() 或特定階段的片段。
常見用法:
simaai::neat::Model model("resnet_50.tar.gz");
simaai::neat::Graph graph;
graph.add(model.graph());
模型階段片段
目的: 組成 Model 公開的預處理、推論、後處理或完整流程,而不公開內部封存檔載入器。
主要 API:
Model::preprocess()Model::inference()Model::postprocess()Model::graph()
這用於混合流程,其中預處理僅執行一次,而 MLA/BoxDecode 則在單獨的圖或執行緒中執行。
工作執行位置(CPU / CVU / MLA)
處理器路由由 MPK 合約(模型封存檔中定義的 CVU/MLA 階段)以及可選的執行階段覆寫決定:
Model::Options控制預處理、後處理、命名和緩衝區選擇。SIMA_MLA_NEXT_CPU可以在某些設定中覆寫 MLA 的下一個階段。- 管線節點本身是宣告式的;實際執行發生在 GStreamer 外掛程式及其設定中。
實際影響:更多的緩衝區和明確的路由可以提高吞吐量,而封包類型不匹配或緩衝區大小不足會在協商 期間快速失敗。
執行階段模型(執行方式)
初始化
所有執行階段進入點都呼叫單一的安全初始化例程:
gst_init_once()(執行緒安全,std::call_once)
此外,執行階段路徑可能會驗證是否存在所需的外掛程式:
require_element("appsink", ...)等。
建立管線
透過新增 Node 物件和可重複使用的 Graph 片段來建立 Graph。對於 RTSP,請使用能辨識編解碼器的片段,以便在解碼前對來源進行解封包和解析:
simaai::neat::nodes::groups::RtspDecodedInputOptions source;
source.url = "rtsp://example/live";
source.codec = simaai::neat::nodes::groups::RtspCodec::H265;
source.source_fps = 30;
simaai::neat::Graph graph;
graph.add(simaai::neat::nodes::groups::RtspDecodedInput(source));
graph.add(simaai::neat::nodes::Output());
內部:
-
圖強制每個邏輯組合節點使用一個節點物件。重複的
connect()呼叫可以重複使用該索引節點進行扇出。 -
一個組合的變更會以一個單元的形式提交,或者完全回滾。
-
圖會要求每個節點提供
backend_fragment(i),並使用!將片段串連起來。 -
它可選擇性地在節點之間插入邊界標記:
identity name=sima_b<i> silent=true
-
它會分析精確的
name=繫結,使用 GStreamer 進行一次解析,並清點建構的物件樹。在下游 設定之前,如果名稱重複或遺漏,則會失敗。 -
它會建立一個
DiagCtx:node_reports,用於可重現性boundaries作為BoundaryFlowCounters(原子變數)
推送/拉取執行階段模型
Run 擁有輸入/輸出佇列和一個輸入執行緒:
push(...)將輸入排入佇列(根據RunOptions::overflow_policy進行阻塞或丟棄)pull(...)從應用程式接收器中取出Sample輸出try_push(...)是非阻塞的(如果佇列已滿,則傳回 false)
這支援完全非同步的管線(生產者/消費者分離),以及單次流程(Graph::run(...))。
解碼器許可生命週期
在選擇單一管線或連接圖的執行階段之前,核心會掃描編譯後的執行計畫,以查找型別為 H.264/H.265 的 SimaDecode 節點。所有符合條件的解碼器都將作為一個群組許可,並且由此產生的保留將由頂層 Run 擁有,直到其管線工作者停止為止。這同樣適用於線性 Graph::add(...) 管線、普通連接的片段和融合的即時分支。
許可需要已知的解碼器寬度、高度和幀率。核心永遠不會發明一 個幀率。不完整的合約或不可用的可選許可端點會產生警告,並且計畫保持不變;使用 SIMA_DECODER_ADMISSION_REQUIRE=1,任何一種情況都會在解碼器硬體啟動之前失敗。容量拒絕和格式錯誤的租賃回應始終會失敗。
即時扇入降低
應用程式使用普通 Graph::connect(...) 描述即時邊緣,並使用普通 Graph::build(...) 將其具體化。GraphLinkOptions 攜帶最新的流策略、流識別碼、保留的佇列深度欄位和可選的原始幀許可限制。最新的流策略始終為每個流保留一個待處理樣本。
執行圖編譯器(而不是應用程式)決定是否可以將即時多來源扇入融合到一個 GStreamer 管線中。符合條件的私有、無輸入的來源分支將與其按流多工器和消費者一起降低,以便解碼的設備緩衝區不會跨越應用程式接收器/應用程式來源邊界。不符合條件的最新流拓撲結構將保持分割狀態。嵌套的已融合的來源片段在可以遞迴地保留其分支之前,將始終不符合條件。
內部邊界計時
一個邏輯 Graph 可以降低到幾個 GStreamer 管線片段。核心在它們之間的每個內部邊界處注入一個 appsrc。
一個注入的邊界會傳輸它所接收的時間軸,並且永遠不會自行產生時間戳。只有一個公開的、應用程式擁有的 Input 會產生時間戳。
appsrc 會 從其自身區段的執行時間中產生時間戳,因此產生時間戳的邊界會為扇出中的每個分支提供不同的時鐘。視訊 RTP 接著會停止與描述相同影格的模型輸出中繼資料同步,而且沒有任何應用程式可以更正它:降低操作會消耗應用程式宣告的 Input 節點,因此應用程式設定的 InputOptions 不會傳遞到注入的邊界。
在新增區段實體化路徑時:
- 使用
injected_boundary_input_options(...)建立注入選項,這是此不變量的唯一位置。 - 保持
is_live = true。清除它會導致即時區段停止。 - 維持公開的
InputOptions::do_timestamp預設值,以便推送的cv::Mat(不帶有 PTS)仍然可以在進入時接收到時間戳。
邊界會以零拷貝方式轉發保留的 GstBuffer,因此已存在的時間戳會在跨越時仍然存在。拒絕產生時間戳永遠無法移除它。
輸入合約的專業化
某些複合管線節點具有多個安全的後端表示形式。圖編譯器會從靜態建立的 OutputSpec 中對這些節點進行專業化;它不會修改公開的圖,也不會從第一個執行階段樣本中推斷出永久拓撲。Derived 或 Authoritative 合約可以選擇最佳化的表示形式。Hint、未知的格式/記憶體或缺少後端功能會選擇保守的表示形式。
例如,原始 VideoSender 僅在系統或 SiMaAI 記憶體中存在穩定的 NV12 合約,並且當 neatencoder 宣告其唯讀 input-layout-aware=true 功能時,才會省略 其 NV12 轉換。OutputSpec 目前不包含平面步幅和偏移量,因此沒有記憶體域會繞過該功能閘。如果缺少或功能為 false,則會將其視為不受支援,因此核心可以與較舊的內部元件包保持安全。
原始視訊幾何形狀和物理儲存佈局仍然是獨立的合約。OutputSpec 和 caps 描述可見的寬度和高度;核心不得將這些值四捨五入到編碼器區塊、DMA 步幅或表面高度對齊。佈局感知外掛程式會從 GstVideoMeta 或 GstVideoInfo 衍生物理平面偏移量和步幅,在物理合約不相容時重新封裝,並將編碼器/硬體授權留給編碼器服務。這保留了精確的解碼幾何形狀,同時將特定於裝置的對齊方式排除在公開的圖 API 之外。
解析與啟動
該函式庫主要使用:
gst_parse_launch(pipeline_string, &err)
這提供了靈活性和可除錯性(您可以透過 gst-launch-1.0 重新執行完全相同的字串)。
執行
典型流程(Graph::build() / Run):
- 執行合約(例如,對於
build()+ 拉取操作,執行「sink last」)。 - 建立管線字串(+ 可選的邊界)。
- 解析管線。
- 可選地執行元件命名合約。
- 附加可選的邊界探測器。
- 將管線設定為
PLAYING。 - 傳回一個
Run控點,用於推/ 拉控制。
幀的生命週期(簡潔語言)
- 建立: 節點變成一個確定性的 gst-launch 字串。
- 協商: GStreamer 在元件之間協商 caps(格式、大小、記憶體)。
- 執行: 輸入被推送到管線中(或從來源拉取)。
- 樣本: Appsink 將一個
Sample/Tensor傳回您的程式碼。 - 錯誤: 任何協商或執行階段失敗都會變成一個
NeatError,並附帶一個GraphReport。
Caps 協商是自動的;失敗會在早期(驗證/預滾動)或在執行階段發生,並提供您可以重現的診斷資訊(describe_backend() + 報告)。
相機設定的所有權
CameraInput 會在其相機 caps 之後,並在任何即時佇列之前,立即放置 neatcamerabridge。 在協商期間,橋接器會使用標準池來回應上游的 GST_QUERY_ALLOCATION,並請求 GstVideoMeta。 該池會分配來自一個打包的 SiMaAI 設定的已驗證平面,並為每個平面匯出一個 DMA-BUF。 一個相容的 libcamerasrc 會將這些 DMA-BUF 匯入到 ISP 捕獲佇列中。 然後,橋接器會解包相同的打包設定,以進行下游處理。 嚴格模式會拒絕任何不 滿足該合約的緩衝區;CPU 複製仍然是一種明確的相容性後備方案。
面向應用程式的捕獲深度與核心的私有 CSI 到 ISP RAW 傳輸環以及後續的 GStreamer 佇列都是獨立的。 可選的 capture_buffer_count 參數,用於 CameraInputWithCaptureBuffers,用於控制在 ISP 輸出、libcamera 和應用程式之間保留的緩衝區。 queue_depth 和 leaky_queue 分別控制下游延遲和幀丟棄策略。 可選的相容性複製池會按需增長,並且不受該佇列深度的限制,因此,漏洩佇列可以應用其丟棄策略,而無需首先使上游橋接器停頓。
拆解
拆解是故意防禦性的。 某些外掛程式堆疊可能會在狀態變更時掛起;執行階段更傾向於避免使主機程序/CI 陷入死鎖。
常見的模式是:
- 發送 EOS
- 設定
GST_STATE_NULL - 取消引用物件
- 應用逾時保護(如果需要,則洩漏而不是掛起)
SimaAI 並行
SimaAI 外掛程式支援每個程序中的多個管線。 如果您同時執行多個管線,請使用 GraphOptions 或 Model 名稱字尾/字首來使元件名稱唯一,以避免 GStreamer 名稱衝突。
限制與安全性
- 輸入格式必須與 caps 匹配:
InputOptions和模型設定必須就格式/寬度/高度達成一致。 如果格式不匹配,則會在協商期間或推送輸入時立即失敗。 - 基於功能的動態輸入:僅當已建構的圖宣佈具有動態功能時,才允許在執行階段重新協商。
FullyDynamic圖可以重新協商原始影片的幾何形狀/格式/幀率/媒體 caps;IngressDynamicCvuOnly允許幾何形狀的更改,並且僅當建構時的下游合約檢查證明輸出行為穩定時,才允許格式的更改。 - 在有效範圍內動態調整:
max_*是硬性上限;如果未設定max_*,則width/height/depth作為隱含的上限。 - 模型與圖的預設值:這兩種流程現在都透過
src/pipeline/internal/InputPolicy.*解析種子/最大值/位元組保護原則;Model仍然應用其記錄的基於元資料的預設值(例如 1920x1080 的上限),而Graph則保持由節點選項驅動,除非已進行設定。 caps_override具有權威性:如果已設定,則會阻止重新協商,並且形狀的更改需要重新建構。
| 流程 | 種子預設值 | 最大值預設值 | 位元組保護預設值 |
|---|---|---|---|
Model | 如果存在,則為預處理元資料,否則從使用者格式/選項推斷 | 明確的 input_max_*;否則為原則預設值(例如 1920x1080,基於格式的深度) | 明確的 RunOptions.max_input_bytes,否則為 InputPolicy 中的有界估計值或彈性預設值 |
Graph | 輸入節點選項和/或種子輸入樣本 | 明確的 max_*;否則為隱含的種子 width/height/depth(如果已提供) | 明確的 RunOptions.max_input_bytes,否則為 InputPolicy 中的有界估計值或彈性預設值 |
- SimaAI 並行處理:多個管線可以在進程內運行;請保持元素名稱的唯一性。
每幀屬性傳播
來源將 Sample::attributes 作為嵌套結構附加到 GstSimaMeta 中。保留緩衝區的元素會自然地攜帶元資料;在邊界處分配或重新使用緩衝區的核心會深度複製屬性並清除過時的值。neatdecoder 在解碼之前快照相同的幀上下文,並透過守護程式提供的關聯 ID 進行還原,因此重新排序或丟棄的幀無法將屬性轉移到另一個輸出。已協商的解碼器/守護程式協定擁有該關聯合約;舊版解碼器協定仍然僅為 FIFO。
支援的使用者介面路徑和限制記錄在 每幀屬性 中。
執行緒和所有權模型
執行緒
- GStreamer 串流執行緒:pad 探測、解碼、排程
- 使用者執行緒:
appsink輪詢 + 定期匯流排清空 - RTSP 伺服器執行緒:用於
gst-rtsp-server模式的 GLib 主迴圈
所有權規則(GStreamer 物件)
- GStreamer 物件會進行參考計數。
- 如果您將
GstObject*儲存在超出其取得範圍的位置,則必須gst_object_ref()它。 - 完成後,請始終
gst_object_unref()一次。
診斷執行緒安全性(重要)
Pad probe 在串流執行緒上執行,因此從 probe 取得的診斷資訊必須是無鎖的。
設計如下:
BoundaryFlowCounters儲存原子變數。- pad probe 僅執行原子
fetch_add()/store()操作。 - 報告使用
BoundaryFlowCounters::snapshot()將原子變數轉換為BoundaryFlowStats(普通整數)。
這可避免資料競用,同時保持 probe 的效能。
診斷與可觀察性
DiagCtx 擷取:
- 管線字串(用於重現)
- 節點報告(每個節點產生了什麼)
- 匯流排訊息(在互斥鎖下)
- 邊界流量計數器(原子變數)
- 元素計時 + 流量計數器(原子變數)
邊界流量 probe
啟用時,執行階段會將 pad probe 附加到邊界 identity 元素。
它們追蹤:
- 緩衝區計數(輸入/輸出)
- 最後看到的 PTS(奈秒)
- 最後看到的牆面時鐘時間(單調遞增,微秒)
這用於產生「可能停頓」摘要:
- 「我們上次看到活動在時間 T 處進入/離開邊界 X」
元素計時 probe
啟用時(SIMA_GST_ELEMENT_TIMINGS=1),執行階段會將 sink+src pad probe 附加到每個元素的所有 pad(靜態、動態和請求),並記錄每個緩衝區的 src_ts - sink_ts。這產生每個元素的計算計時,而無需依賴外掛程式檢測。
對於替換緩衝區的元素,實作會回退到 GstSimaMeta 關聯(框架 ID/串流 ID),並記錄 missed_in/missed_out 計數器。
元素流量 probe
啟用時(SIMA_GST_FLOW_DEBUG=1),執行階段會將每個元素的 pad probe 附加到每個元素,以追蹤緩衝區/位元組計數和 caps 變更,從而為圖中每個外掛程式提供輸送量上下文。
匯流排記錄和錯誤
執行階段會將匯流排訊息排入 DiagCtx。
如果發生錯誤訊息(GST_MESSAGE_ERROR),它會拋出 NeatError,其中包含 GraphReport 和重現提示。
DOT 轉儲
如果啟用,執行階段可以透過 gst_debug_bin_to_dot_file_with_ts(...) 將 DOT 圖輸出到已設定的目錄。
偵錯流程(生產環境)
- 重現管線:
Graph::describe_backend()或last_pipeline()。 - 擷取報告:
MeasureReport::to_text()或NeatError::report()。 - 啟用目標 probe:
SIMA_GST_BOUNDARY_PROBES=1,用於定位停頓SIMA_GST_ELEMENT_TIMINGS=1,用於每個元素的 計時SIMA_GST_FLOW_DEBUG=1,用於每個元素的流量計數器
- 產生 DOT 圖:設定
SIMA_GST_DOT_DIR並重現。 - 加強驗證:
SIMA_GST_ENFORCE_NAMES=1並驗證預先緩衝逾時。
輸出處理
Run::pull() 會產生一個 Sample,其中可能包含:
- 一個
Tensor有效載體(SampleKind::Tensor) - 一個包含多個輸出的集合(
SampleKind::Bundle)
在您希望使用張量負載而非完整的 Sample 封包時,對於以機器學習為中心的流程,請使用 Run::pull_tensors(...)。
管線序列化(儲存/載入)
管線可以儲存並還原為 JSON 格式:
Graph::save(path)會寫入包含節點類型/標籤/片段/元素的版本化 JSONGraph::load(path)會透過ConfiguredNode封裝來重新建立節點
目前的結構設計是簡潔且可重現的,並且可以演進到更豐富的節點設定。這也可用於未來綁定和工具的橋樑。