草庐IT

gstBuffer的 data 和 meta

山西茄子 2023-04-03 原文

本文主要分析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;

}

有关gstBuffer的 data 和 meta的更多相关文章

  1. ruby-on-rails - 结合 meta_search 与 acts_as_taggable_on - 2

    我在开发的Rails3网站的一些搜索功能上遇到了一个小问题。我有一个简单的Post模型,如下所示:classPost我正在使用acts_as_taggable_on来更轻松地向我的帖子添加标签。当我有一个标记为“rails”的帖子并执行以下操作时,一切正常:@posts=Post.tagged_with("rails")问题是,我还想搜索帖子的标题。当我有一篇标题为“Helloworld”并标记为“rails”的帖子时,我希望能够通过搜索“hello”或“rails”来找到这篇帖子。因此,我希望标题列的LIKE语句与acts_as_taggable_on提供的tagged_with方法

  2. ruby-on-rails - 更好的替代方法 try( :output). try( :data). try( :name)? - 2

    “输出”是一个序列化的OpenStruct。定义标题try(:output).try(:data).try(:title)结束什么会更好?:) 最佳答案 或者只是这样:deftitleoutput.data.titlerescuenilend 关于ruby-on-rails-更好的替代方法try(:output).try(:data).try(:name)?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.c

  3. ruby - 如何在 InSpec 中访问 Chef data_bags - 2

    我正在为我正在处理的一些新ChefRecipe编写InSpec测试。我想利用Recipe使用的data_bags遍历数据包项。我不知道如何在我的InSpec测试中访问它们!这些Recipe使用了search、data_bag和data_bag_item方法。但是这些方法在我的InSpec测试中似乎不可用。我怀疑这些是ChefDSL特定的方法?data_bags的源代码受源代码控制,因此我可以在我的本地文件系统上访问它们的json。如何使用InSpec语法访问Chef_zero中的这些数据包?我在网上找到了几个示例,但我没有看到data_bags实际上是如何由chef_zero加载的,以

  4. ruby-on-rails - Rails 使用 send_data 或 send_file 将多个文件发送到浏览器 - 2

    我正在尝试向浏览器发送多个文件。我不能像下面的代码一样为每条记录调用send_data,因为我收到双重渲染错误。根据thispost我需要创建文件并压缩它们,以便我可以在一个请求中发送它们。@records.eachdo|r|ActiveRecord::Base.include_root_in_json=true@json=r.to_jsona=ActiveSupport::MessageEncryptor.new(Rails.application.config.secret_token)@json_encrypted=a.encrypt_and_sign(@json)send_da

  5. ruby-on-rails - 使用 rails send_data 发送 PDF - 2

    我使用ruby​​1.9.3和redmine1.4.4据此->Pleasehelpmesendajpgfileusingsend_data,我在Controller中这样做:@file=temp.pathFile.open(@file,'r')do|f|send_dataf.read,:filename=>"myfile.pdf",:type=>"application/pdf",:disposition=>"attachment"endFile.delete(@file)但它返回ArgumentError(UTF-8中的无效字节序列),为什么? 最佳答案

  6. ruby - 使用 ActiveResource 保存时有 'allocator undefined for Data' - 2

    我错过了什么?我正在尝试使用Active资源的休息服务,我有以下内容:classUser"Test",:email=>"test.user@domain.com")puserifuser.saveputs"success:#{user.uuid}"elseputs"error:#{user.errors.full_messages.to_sentence}"end以及用户的以下输出:#"Test","email"=>"test.user@domain.com"}>和这个错误:/Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/ac

  7. ruby - 使用 Ruby 和 net-ssh,如何通过 Net::SSH.start 使用 key_data 参数进行身份验证? - 2

    我已经阅读了net-ssh文档,但我仍然感到困惑。我可以手动进行身份验证(使用ssh-i...),也可以将key放在文件中并使用:keys参数。但是,我不想使用:keys参数,我想使用:key_data参数。任何人都可以举一个工作的例子吗?出于某种原因,直接将字符串输入:key_data是行不通的,它给出了错误:“既不是PUBkey也不是PRIVkey::嵌套的asn1错误”。当然,我用谷歌搜索了一下,它基本上告诉我要确保key是PEM格式。而且,当然是。有任何想法吗?如果需要,我可以提供更详细的信息... 最佳答案 我看到这个问题

  8. ruby - Aptana 3 ruby​​ 调试器 - DebugThread 循环中的异常 : undefined method `is_binary_data?' - 2

    我正在尝试在Aptana3中调试简单的ruby​​文件。classHelloWorlddefinitialize()enddefgreet()puts"helloworld"endendh=HelloWorld.newh.greet断点设置为h.greet在我开始调试后,调试器启动,但是当它尝试初始化ruby​​类时,调试器断开连接并显示消息FastDebugger(ruby-debug-ide0.4.9)listenson:54749ExceptioninDebugThreadloop:undefinedmethod`is_binary_data?'for"#":String当我将断

  9. c - Data_wrap_struct 和标记函数 - 2

    我正在编写一个Ruby扩展,我正在使用函数Data_wrap_struct。为了参与Ruby的标记和清除垃圾收集过程,我需要定义一个例程来释放我的结构,以及一个例程来标记从我的结构到其他结构的任何引用。我通过经典的free函数来释放内存,但我不知道如何使用标记函数。我的结构听起来像这样typedefstruct{intx;inty;}A;typedefstruct{Acollection[10];intcurrent;}B;我认为我需要一个标记函数来标记结构B的collection中的引用。谁能给我看一个例子,看看标记函数是如何工作的? 最佳答案

  10. Ruby 相当于 perl 的 "Data::Dumper",用于打印深度嵌套的哈希/数组 - 2

    这不是RubyequivalentofPerlData::Dumper的副本.这个问题已经超过3.5年了,因此想检查从那时起Ruby中是否有任何可用的新选项。我正在寻找perl的Dumper在ruby​​中的等价物。我不在乎Dumper在幕后做了什么。我已经广泛使用它在perl中打印深度嵌套的哈希和数组。到目前为止,我还没有在ruby​​中找到替代品(或者我可能没有找到一种方法来充分利用Ruby中的可用替代品)。这是我的perl代码及其输出:#!/usr/bin/perl-wusestrict;useData::Dumper;my$hash;$hash->{what}->{where}

随机推荐