我們已經示範過附加新的Metadata,那今天的目標是建立User Metadata,因為有些時候DeepStream設計的Metadata不能滿足應用的需求,特別是應用有需要寫自訂元件的時候,往往會需要額外的資料來完成目標。
我們複製Day 20的程式進行修改
[...]
#define PGIE_UNIQUE_ID 1
#define PERSON_CLASS_ID 2
#define IRONMAN_UNIQUE_ID 9
#define IRONMAN_YEAR 2026
#define IRONMAN_DAY 21
typedef struct {
guint year;
guint day;
} IronmanMeta;
#define IRONMAN_META nvds_get_user_meta_type("IRONMAN.DEEPSTREAM.META")
static IronmanMeta* ironman_meta_new(guint year, guint day) {
IronmanMeta* meta = g_malloc0(sizeof(IronmanMeta));
meta->year = year;
meta->day = day;
return meta;
}
static gpointer ironman_meta_copy_func(gpointer data, gpointer user_data) {
(void)user_data;
NvDsUserMeta* user_meta = data;
IronmanMeta* src = user_meta->user_meta_data;
IronmanMeta* copy = g_malloc0(sizeof(IronmanMeta));
*copy = *src;
return copy;
}
static void ironman_meta_release_func(gpointer data, gpointer user_data) {
(void)user_data;
NvDsUserMeta* user_meta = data;
/* The argument of g_free can be NULL */
g_free(user_meta->user_meta_data);
}
[...]
static NvDsUserMeta* create_ironman_meta(NvDsBatchMeta* batch_meta) {
NvDsUserMeta* user_meta = nvds_acquire_user_meta_from_pool(batch_meta);
if (!user_meta) {
g_print("WARNING: Can't acquire meta data from pool\n");
return NULL;
}
IronmanMeta* ironman_meta = ironman_meta_new(IRONMAN_YEAR, IRONMAN_DAY);
user_meta->user_meta_data = ironman_meta;
user_meta->base_meta.meta_type = IRONMAN_META;
user_meta->base_meta.copy_func = ironman_meta_copy_func;
user_meta->base_meta.release_func = ironman_meta_release_func;
return user_meta;
}
static GstPadProbeReturn handle_osd_sink_pad_probe(GstPad*, GstPadProbeInfo* info, gpointer) {
/* 'buf' is owned by 'info', we don't need to release it */
GstBuffer* buf = gst_pad_probe_info_get_buffer(info);
/* gst_pad_probe_info_get_buffer may return NULL */
if (!buf) {
g_print("WARNING: Can't get GstBuffer from GstPadProbeInfo\n");
return GST_PAD_PROBE_OK;
}
NvDsBatchMeta* batch_meta = gst_buffer_get_nvds_batch_meta(buf);
if (!batch_meta) {
g_print("WARNING: No NvDsBatchMeta was attched on the GstBuffer\n");
return GST_PAD_PROBE_OK;
}
for (NvDsFrameMetaList* l_frame = batch_meta->frame_meta_list; l_frame; l_frame = l_frame->next) {
NvDsFrameMeta* frame_meta = l_frame->data;
for (NvDsObjectMetaList* l_obj = frame_meta->obj_meta_list; l_obj; l_obj = l_obj->next) {
NvDsObjectMeta* obj_meta = l_obj->data;
if (obj_meta->unique_component_id == PGIE_UNIQUE_ID
&& obj_meta->class_id == PERSON_CLASS_ID) {
NvDsClassifierMeta* class_meta = create_classifier_meta(batch_meta, "ironman");
NvDsUserMeta* user_meta = create_ironman_meta(batch_meta);
/* Must lock 'batch_meta' when modifying existing metadata */
nvds_acquire_meta_lock(batch_meta);
nvds_add_classifier_meta_to_object(obj_meta, class_meta);
nvds_add_user_meta_to_obj(obj_meta, user_meta);
object_meta_refresh_label(obj_meta);
nvds_release_meta_lock(batch_meta);
}
}
}
return GST_PAD_PROBE_OK;
}
typedef struct {
guint year;
guint day;
} IronmanMeta;
#define IRONMAN_META nvds_get_user_meta_type("IRONMAN.DEEPSTREAM.META")
static IronmanMeta* ironman_meta_new(guint year, guint day) {
IronmanMeta* meta = g_malloc0(sizeof(IronmanMeta));
meta->year = year;
meta->day = day;
return meta;
}
static gpointer ironman_meta_copy_func(gpointer data, gpointer user_data) {
(void)user_data;
NvDsUserMeta* user_meta = data;
IronmanMeta* src = user_meta->user_meta_data;
IronmanMeta* copy = g_malloc0(sizeof(IronmanMeta));
*copy = *src;
return copy;
}
static void ironman_meta_release_func(gpointer data, gpointer user_data) {
(void)user_data;
NvDsUserMeta* user_meta = data;
/* The argument of g_free can be NULL */
g_free(user_meta->user_meta_data);
}
NvDsMetaType先前有介紹過了,這邊就是實際示範一次,增加大家的印象。主要就是得定義一個struct來儲存我們需要的資料,實例化(ironman_meta_new)則是為了方便直接採用g_malloc0,如果要進一步優化就是使用自己的Object Pool或是Allocator,搭配NvDsUserMeta的uContext使用。
NvDsUserMetastatic NvDsUserMeta* create_ironman_meta(NvDsBatchMeta* batch_meta) {
NvDsUserMeta* user_meta = nvds_acquire_user_meta_from_pool(batch_meta);
if (!user_meta) {
g_print("WARNING: Can't acquire meta data from pool\n");
return NULL;
}
IronmanMeta* ironman_meta = ironman_meta_new(IRONMAN_YEAR, IRONMAN_DAY);
user_meta->user_meta_data = ironman_meta;
user_meta->base_meta.meta_type = IRONMAN_META;
user_meta->base_meta.copy_func = ironman_meta_copy_func;
user_meta->base_meta.release_func = ironman_meta_release_func;
return user_meta;
}
NvDsUserMeta一樣是從batch_meta的Pool取得,user_meta_data放上我們自訂的資料,最重要的就是base_meta的meta_type、copy_func、release_func要記得填寫
NvDsUserMetaNvDsClassifierMeta* class_meta = create_classifier_meta(batch_meta, "ironman");
NvDsUserMeta* user_meta = create_ironman_meta(batch_meta);
/* Must lock 'batch_meta' when modifying existing metadata */
nvds_acquire_meta_lock(batch_meta);
nvds_add_classifier_meta_to_object(obj_meta, class_meta);
nvds_add_user_meta_to_obj(obj_meta, user_meta);
object_meta_refresh_label(obj_meta);
nvds_release_meta_lock(batch_meta);
跟其他Metadata一樣可以找到對應的附加函式,不過NvDsUserMeta可以附加在幾乎所有的Metadata上,依照需求找一個合理的位置附加上去才是正確的
雖然DeepStream提供了NvDsUserMeta來應付自定義資料的需求,可是實際開發中有很多SDK設計好的函式簽章是不會帶入NvDsUserMeta參數的,說真的用起來也是綁手綁腳,這也就衍伸出這系列最重的目標,為什麼我們需要自己建立GStreamer元件