gstBuffer的 data 和 meta

Posted 山西茄子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gstBuffer的 data 和 meta相关的知识,希望对你有一定的参考价值。

本文主要分析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,在创建GstMemory,再将GstMemory添加到GstBuffer。

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;

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;

[gstreamer] 关于GstBuffer的refcount


首先需要明确一点,目前没有提供任何方法来创建一个 GstBuffer实例,因此不要试图使用下述语句来创建一个空的 GstBuffer :

GstBuffer buffer;   //错误
GstBuffer* pbuffer = gst_buffer_new(); //正确

基于上述现状,有如下结论:

1)当使用 gst_buffer_new_xxx 系列函数创建一个 GstBuffer 的时候时,会设置 refcount 为 1。

2)由于 gst_buffer_new_xxx 系列函数返回的是指针,因此当指针离开作用域时不会导致底层的 GstBuffer 被销毁,仅仅是指针被销毁,所以真正的 GstBuffer 实例不会受影响, 这一点非常重要。所以往往在 gst_buffer_new_xxx 存在的函数里都要在最后 unref , 不然这个 GstBuffer 实例就永远无法得到释放而成为内存泄漏点。

3)gst_buffer_ref 会增加一个计数,gst_buffer_unref 会减少一个计数,一般情况下,在一个函数里,ref 和 unref 要对称。

4)要注意是否有些函数会隐式改变计数,在使用函数之前要先看下函数注释,如果有隐式的计数操作,那么在离开作用域的时候要记得补偿计数。比如 gst_buffer_make_writable 会直接暴力地把计数设置为1。再比如, gst_pad_push 会把引用计数减1,这需要格外注意一下。

以上是关于gstBuffer的 data 和 meta的主要内容,如果未能解决你的问题,请参考以下文章

Gstreamer中GstBuffer 结构体的定义在哪找??

清单文件 meta-data标签

php 设置代码#woocommerce产品和产品变体#custom-fields#meta-data

Android中<meta-data;的使用

Manifest中meta-data扩展元素数据的配置与获取

使用imageio包将mp4视频转换为avi视频