DeepStream的範例程式基本上都希望你照著設計好的方式開發,最明顯能感受到的就是PGIE和SGIE的設計,設計上只接受模型的輸入只有一個影像Tensor (NCHW或NHWC格式),並且要照著PGIE負責偵測物體,SGIE負責分類物體的方式運作。雖然SDK有提供一些Hook手段或是Tensor輸出/輸入手段,但不可能滿足所有應用的需求,維護上也很困難,必須跟著DeepStream的版本走,就實際的經歷來說,版本迭代的速度其實很快,必然會造成維護的負擔
我相信當一個團隊決定要採用一個框架時,往往都希望直接使用別人開發完成的工具就好,但我們總不能祈禱當遇到一個需求是既有程式庫沒有的時候,會有人跳出來幫你開發對吧?
我所處的團隊初期也是面臨這種情況,但在我研究了GStreamer這個框架一段時間後發現,其實有工具可以生成插件模板,在大部分應用情境下也不需要了解太多框架的知識,於是我就勇敢地站出來說,我要開發自己的插件
首先必須得了解用C開發程式的習慣及技巧,在這個充斥著使用物件導向及擁有高階特性的程式語言的時代,我們得回到最原始的程式是如何運作的:
其實用心感受一下就會發現,這就是最低配的物件導向,沒有繼承,沒有多型,就只有一組資料,數個函式實現邏輯
講到這裡我非常推薦對C/C++很陌生的人去看一次這支影片,POINTERS in C++。這裡面有提到一個很重要的觀念,而GObject也是這麼設計的
Everything is memory
剩下的就是習慣GLib提供的記憶體分配、字串操作、容器型別(GArray、GHashTable)
不得不提一下,我認為身為一位好的工程師,首要需求是會讀文件而不是會寫程式,很多知識其實文件就有告訴你,寫出正確的程式不是問題。反而是連文件都不看,又怎麼會期望你寫出來的程式是對的呢
首先是GLib,我就舉例g_strdup這個函式
gchar*
g_strdup (
const gchar* str
)
這份文件注意幾個重點
輸入可以是NULL
If str is NULL it returns NULL.
str是呼叫端擁有
The data is owned by the caller of the function.
看到這句話就是你有責任保證變數的生命週期
str必須是NUL terminated
The value is a NUL terminated UTF-8 string
C所認知的字串就是一個char array以null char(\0)做結尾
回傳值要釋放
A newly-allocated copy of
str.The caller of the function takes ownership of the data, and is responsible for freeing it.
重點基本上放在參數是否接受NULL,以及變數的擁有權在哪一端
再來是GStreamer的部分,以gst_element_factory_make作為例子
參數或回傳是否可以是NULL
GStreamer的文件中,如果變數可能是NULL,正常會寫上[nullable]。少數比較新的或沒在維護的文件可能什麼都沒寫
變數的所有權轉移
只要需要管理生命週期的變數,應該會看到文件寫上[transfer:none]、[transfer:full]、[transfer:floating]這幾種可能。[transfer:none]代表你不需要釋放或解引用(unref)這個變數,[transfer:full]代表你必須要釋放或解引用。
[transfer:floating]比較特別,因為GStreamer的物件是可以有從屬關係的,當GstObject擁有Parent時,Parent有責任釋放,在此之前GstObject的引用狀態屬於floating,你有責任要釋放
你可以限制特定的Caps,框架自己會協商到你能接受的輸入格式上,這在DeepStream提供的Hook是做不到的
DeepStream沒有給的屬性,我們自己加上去,依照需求做出最大的彈性
先前有示範過用GstProbe修改GstBuffer的數據,可是這麼做的維護成本也比較高,最大的缺點就是不能搭配gst-launch-1.0使用。如果自己開發插件,那就能有效管理這些邏輯,並且還能直接用gst-launch-1.0測試
這是我在實際任務中面臨的困境,我們的需求不得不去修改nvinfer的原始碼,起初改一個版本還好,後來發現大概半年DeepStream就更新一個版本,每次更新的原始碼變動幅度又很大,加上DeepStream官方只會持續維護2 ~ 3個版本,長期來看不是一個合適的做法。雖然是自己寫元件搭配DeepStream使用,可是至少在API層面的相容性保證是很高的,不會輕易有Breaking Changes
你可以設計自己的GstMessage跟應用程式互動,也可以直接操作GstEvent跟Pipeline上的其他元件互動,總之寫元件的好處就是你可以善用框架提供給你的資源
第一次寫插件的時候心裡也是抱持著會不會失敗的擔憂,但嘗試過後發現一點都不困難,而且當我用gst-inspect-1.0看見自己的元件資訊被顯示出來時,那個成就感是非常的棒