本文主要分析gsbuffer的创建,memory,meta的分配。
一 分析gstBuffer
gstBuffer
typedef struct _GstBuffer GstBuffer;
struct _GstBuffer {
GstMiniObject mini_object;
/* timestamp */
GstClockTime pts; //时间戳
GstClockTime dts;
.....
};
typedef struct
{
GstBuffer buffer;
gsize slice_size;
/* the memory blocks */
guint len; //mem的当前的个数
GstMemory *mem[GST_BUFFER_MEM_MAX]; //存储数据,GST_BUFFER_MEM_MAX为16
/* memory of the buffer when allocated from 1 chunk */
GstMemory *bufmem;
/* FIXME, make metadata allocation more efficient by using part of the
* GstBufferImpl */
GstMetaItem *item; //头指针
GstMetaItem *tail_item; //尾指针
} GstBufferImpl;
可以看到GstBuffer包含时间戳等成员。GstBufferImpl继承自GstBuffer,包含GstMemory, GstMetaItem等成员,GstMemory用来装数据,GstMeta用来装meta数据,GstMetaItem是个链表,可以为GstBuffer添加,删除GstMeta。
这里有一些操作的宏:
#define GST_BUFFER_SLICE_SIZE(b) (((GstBufferImpl *)(b))->slice_size)
#define GST_BUFFER_MEM_LEN(b) (((GstBufferImpl *)(b))->len)
#define GST_BUFFER_MEM_ARRAY(b) (((GstBufferImpl *)(b))->mem)
#define GST_BUFFER_MEM_PTR(b,i) (((GstBufferImpl *)(b))->mem[i])
#define GST_BUFFER_BUFMEM(b) (((GstBufferImpl *)(b))->bufmem)
#define GST_BUFFER_META(b) (((GstBufferImpl *)(b))->item)
#define GST_BUFFER_TAIL_META(b) (((GstBufferImpl *)(b))->tail_item)
gst_buffer_new
GstBuffer * gst_buffer_new (void)
{
GstBufferImpl *newbuf;
newbuf = g_slice_new (GstBufferImpl);
GST_CAT_LOG (GST_CAT_BUFFER, "new %p", newbuf);
gst_buffer_init (newbuf, sizeof (GstBufferImpl));
return GST_BUFFER_CAST (newbuf);
}
如上函数,可以看到创建一个GstBuffer,就是创建一个GstBufferImpl。
gst_buffer_new_allocate{
GstBuffer *newbuf;
GstMemory *mem;
mem = gst_allocator_alloc (allocator, size, params);
newbuf = gst_buffer_new ();
_memory_add (newbuf, -1, mem); //-1表示加到数组的最后一个空位。
return newbuf;
}
在gst_buffer_new_allocate里,可以看到先创建GstBuffer,再创建GstMemory,再将GstMemory添加到GstBuffer。
GstMeta
定义在gstmeta.h
typedef struct _GstMeta GstMeta;
struct _GstMeta {
GstMetaFlags flags;
const GstMetaInfo *info;
};
GstMetaItem定义在gst_private.h
typedef struct _GstMetaItem GstMetaItem;
struct _GstMetaItem {
GstMetaItem *next;
guint64 seq_num; //编号
GstMeta meta;
};
可以看到GstMetaItem是一个GstMeta的链表。
GstMemory
struct _GstMemory {
GstMiniObject mini_object;
GstAllocator *allocator;
GstMemory *parent;
gsize maxsize;
gsize align;
gsize offset;
gsize size;
};
看起来_GstMemory包含了内存的大小size,以及分配者allocator。
/* default memory implementation */
typedef struct
{
GstMemory mem;
gsize slice_size;
guint8 *data;
gpointer user_data;
GDestroyNotify notify;
} GstMemorySystem;
GstMemorySystem会对GstMemory再进行封装,真正的包含了内存的指针data,可以参考_sysmem_new。
GstMemory相关的重要函数有:
Getting access to the data of the memory is performed with gst_memory_map. The call will return a pointer to offset bytes into the region of memory. After the memory access is completed, gst_memory_unmap should be called.
gst_buffer_append_memory (GstBuffer * buffer, GstMemory * mem)->gst_buffer_insert_memory->_memory_add-> GST_BUFFER_MEM_PTR (buffer, idx) = mem;
二 分析memory
GstMemory 的例子, 分配一段buff,并改写。
GstMemory *mem;
GstMapInfo info;
gint i;
/* allocate 100 bytes */
mem = gst_allocator_alloc (NULL, 100, NULL);
/* get access to the memory in write mode */
gst_memory_map (mem, &info, GST_MAP_WRITE);
/* fill with pattern */
for (i = 0; i < info.size; i++)
info.data[i] = i;
/* release memory */
gst_memory_unmap (mem, &info);
gst_allocator_alloc的定义在gstallocator.c中,如果第一个参数为空,会调用_default_allocator类的alloc函数,那_default_allocator从哪里来呢?gstallocator.c中定义了一个类GstAllocatorSysmem,G_DEFINE_TYPE (GstAllocatorSysmem, gst_allocator_sysmem, GST_TYPE_ALLOCATOR);
在初始化函数_priv_gst_allocator_initialize (void)中,创建了GstAllocatorSysmem的对象_sysmem_allocator( _default_allocator = gst_object_ref (_sysmem_allocator);),然后赋给了_default_allocator,所以_default_allocator是GstAllocatorSysmem对象的指针。GstAllocatorSysmem类的alloc函数是default_alloc,最终调用的顺序是:
GstMemory * gst_allocator_alloc(gstallocator.c)->default_alloc->_sysmem_new_block->g_slice_alloc(gslice.c)->调g_malloc,slab
如果gst_allocator_alloc的第一个参数不为NULL, 参见另一篇分析:GstBufferPool与nvpreprocess_茄子船长的博客-CSDN博客
三 分析meta.
gst_buffer_add_meta
Arbitrary extra metadata can be set on a buffer with gst_buffer_add_meta. Metadata can be retrieved with gst_buffer_get_meta. See also GstMeta.
GstMeta *gst_buffer_add_meta (GstBuffer * buffer, const GstMetaInfo * info, gpointer params)
{
GstMetaItem *item;
GstMeta *result = NULL;
gsize size;
g_return_val_if_fail (buffer != NULL, NULL);
g_return_val_if_fail (info != NULL, NULL);
g_return_val_if_fail (gst_buffer_is_writable (buffer), NULL);
/* create a new slice */
size = ITEM_SIZE (info);
/* We warn in gst_meta_register() about metas without
* init function but let's play safe here and prevent
* uninitialized memory
*/
if (!info->init_func)
item = g_slice_alloc0 (size);
else
item = g_slice_alloc (size);
result = &item->meta;
result->info = info;
result->flags = GST_META_FLAG_NONE;
。。。。。
/* call the init_func when needed */
if (info->init_func)
if (!info->init_func (result, params, buffer))
goto init_failed;
item->seq_num = gst_atomic_int64_inc (&meta_seq);
item->next = NULL;
if (!GST_BUFFER_META (buffer)) { //插入到链表尾部
GST_BUFFER_META (buffer) = item;
GST_BUFFER_TAIL_META (buffer) = item;
} else {
GST_BUFFER_TAIL_META (buffer)->next = item;
GST_BUFFER_TAIL_META (buffer) = item;
}
。。。。。。
}
关键是第二个参数,传入GstMetaInfo类型,返回GstMetaInfo对应meta,例如注册一个GstParentBufferMeta名称的的GstMetaInfo。
const GstMetaInfo *
gst_parent_buffer_meta_get_info (void) {
static const GstMetaInfo *meta_info = NULL;
if (g_once_init_enter ((GstMetaInfo **) & meta_info)) {
const GstMetaInfo *meta =
gst_meta_register (gst_parent_buffer_meta_api_get_type (),
"GstParentBufferMeta",
sizeof (GstParentBufferMeta),
(GstMetaInitFunction) _gst_parent_buffer_meta_init,
(GstMetaFreeFunction) _gst_parent_buffer_meta_free,
_gst_parent_buffer_meta_transform);
g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) meta);
}
return meta_info;
}
GstParentBufferMeta *
gst_buffer_add_parent_buffer_meta (GstBuffer * buffer, GstBuffer * ref)
{
GstParentBufferMeta *meta;
g_return_val_if_fail (GST_IS_BUFFER (ref), NULL);
meta =
(GstParentBufferMeta *) gst_buffer_add_meta (buffer,
GST_PARENT_BUFFER_META_INFO, NULL);
if (!meta)
return NULL;
meta->buffer = gst_buffer_ref (ref);
return meta;
}
第三方库的应用(deepstream)
deepstream用于AI推理,比如输入交通上的视频,推理得出汽车的位置,车牌的位置等,这些推理后的数据都存放在meta当中。deepstream有个streammux插件,在mux之前,meta绑定在GstBuffer上,如用户加一些自定义的meta ,streammux之后,所有数据打成一个batch,之前所有的meta数据都会挂到batch meta的链表下。以deepstream_gst_metadata.c为例,pipeline是 file-source -> h264-parser -> nvh264-decoder -> nvinfer -> nvvidconv -> nvosd -> video-renderer,
用户添加的meta:
/* nvdecoder_src_pad_buffer_probe() will attach decoder metadata to gstreamer
* buffer on src pad. The decoder can not attach to NvDsBatchMeta metadata because
* batch level metadata is created by nvstreammux component. The decoder
* component is present is before nvstreammmux. So it attached the metadata
* using gstnvdsmeta API's. */
static GstPadProbeReturn
nvdecoder_src_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
gpointer u_data)
{
GstBuffer *buf = (GstBuffer *) info->data;
NvDsMeta *meta = NULL;
NvDecoderMeta *decoder_meta = (NvDecoderMeta *)g_malloc0(sizeof(NvDecoderMeta));
if(decoder_meta == NULL)
{
return GST_FLOW_ERROR;
}
/* Add dummy metadata */
decoder_meta->frame_type = (frame_number % 3);
decoder_meta->frame_num = frame_number++;
decoder_meta->dec_err = ((frame_number % 4) / 3);
/* Attach decoder metadata to gst buffer using gst_buffer_add_nvds_meta() */
meta = gst_buffer_add_nvds_meta (buf, decoder_meta, NULL,
decoder_meta_copy_func, decoder_meta_release_func);
/* Set metadata type */
meta->meta_type = (GstNvDsMetaType)NVDS_DECODER_GST_META_EXAMPLE;
/* Set transform function to transform decoder metadata from Gst meta to
* nvds meta */
meta->gst_to_nvds_meta_transform_func = decoder_gst_to_nvds_meta_transform_func;
需要设置转换函数,streammux之后,meta会转到bachmeta下的链表下。
/* Set release function to release the transformed nvds metadata */
meta->gst_to_nvds_meta_release_func = decoder_gst_nvds_meta_release_func;
g_print("GST Dec Meta attached with gst decoder output buffer for Frame_Num = %d\n",
decoder_meta->frame_num);
g_print("Attached decoder Metadata: frame type = %d, frame_num = %d decode_error_status = %d\n\n",
decoder_meta->frame_type, decoder_meta->frame_num,
decoder_meta->dec_err);
return GST_PAD_PROBE_OK;
}
infer过后,读取用户之前添加的meta,
/* nvinfer_src_pad_buffer_probe() will extract the metadata received on nvinfer
* src pad.
* It explains the mechanism to extract the decoder metadata (which is attached
* using gstnvdsmeta API's in nvdecoder_src_pad_buffer_probe()),
* now transformed into nvdsmeta. Decoder meta, attached to gst buffer
* is set as user data at NvDsFrameMeta level
* It also extracts metadata attached on src pad of h264parse element. */
static GstPadProbeReturn
nvinfer_src_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
gpointer u_data)
{
GstBuffer *buf = (GstBuffer *) info->data;
NvDsMetaList * l_frame = NULL;
NvDsUserMeta *user_meta = NULL;
NvDecoderMeta * decoder_meta = NULL;
H264parseMeta * h264parse_meta = NULL;
NvDsMetaList * l_user_meta = NULL;
NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);
for (l_frame = batch_meta->frame_meta_list; l_frame != NULL;
l_frame = l_frame->next) {
NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) (l_frame->data);
for (l_user_meta = frame_meta->frame_user_meta_list; l_user_meta != NULL;
l_user_meta = l_user_meta->next) {
user_meta = (NvDsUserMeta *) (l_user_meta->data);
if(user_meta->base_meta.meta_type == NVDS_DECODER_GST_META_EXAMPLE)
{
decoder_meta = (NvDecoderMeta *)user_meta->user_meta_data;
g_print("Dec Meta retrieved as NVDS USER METADTA For Frame_Num = %d \n",
decoder_meta->frame_num);
g_print("Retrieved Decoder Metadata: frame type = %d, frame_num = %d decode_error_status = %d\n\n",
decoder_meta->frame_type, decoder_meta->frame_num,
decoder_meta->dec_err);
}
}
}
return GST_PAD_PROBE_OK;
}