1 GstBuffer基本概念

  • 缓冲区(Buffers)是 GStreamer 中数据传输的基本单元。Buffers包含事件timing、偏移量offset、GstMemory和GstMemory块相关联的其他任意元数据metadata。

  • 通常使用 gst_buffer_new 创建缓冲区。创建缓冲区后,通常会为其分配内存并将其添加到缓冲区中。以下示例创建了一个可以容纳给定宽度、高度和每像素位数的视频帧的缓冲区。

      GstBuffer *buffer;
      GstMemory *memory;
      gint size, width, height, bpp;
      ...
      size = width * height * bpp;
      buffer = gst_buffer_new ();
      memory = gst_allocator_alloc (NULL, size, NULL);
      gst_buffer_insert_memory (buffer, -1, memory);
      ...
    

    或者,使用 gst_buffer_new_allocate 来创建一个具有预分配数据的缓冲区(已经包含GstMemory,不需要再手动创建GstMemory和添加到GstBuffer)。

  • 缓冲区可以包含 GstMemory 对象的列表。您可以使用 gst_buffer_n_memory 获取有多少内存对象,并可以使用 gst_buffer_peek_memory 获取内存的指针。

  • 缓冲区通常有时间戳和持续时间,但这些都不是必须的(它们可能被设置为 GST_CLOCK_TIME_NONE)。只要能为这些值提供有意义的值,就应该设置。时间戳和持续时间以纳秒为单位(它们是 GstClockTime 值)。

  • 缓冲区的 DTS 指的是缓冲区应该被解码的时间戳,通常是单调递增的。缓冲区的 PTS 指的是缓冲区内容应该呈现给用户的时间戳,并不总是单调递增的。

  • 缓冲区还可以有开始和/或结束偏移量。这些是媒体类型特定的。对于视频缓冲区,开始偏移量通常是帧号。对于音频缓冲区,它将是到目前为止产生的样本数。对于压缩数据,它可能是源文件或目标文件中的字节偏移量。同样,结束偏移量将是缓冲区结束的偏移量。只有在知道缓冲区的媒体类型(前一个 CAPS 事件)时,这些才能被有意义地解释。可以将一个或两个都设置为 GST_BUFFER_OFFSET_NONE。

  • 使用 gst_buffer_ref 可以增加缓冲区的引用计数。当您想在将缓冲区推送到下一个元素后保留对缓冲区的引用时,必须这样做。缓冲区的引用计数决定了缓冲区的可写性,只有当引用计数正好为1时,即当调用者拥有对缓冲区的唯一引用时,缓冲区才是可写的。

    要有效地从现有的一个缓冲区创建一个较小的缓冲区,可以使用 gst_buffer_copy_region。这种方法试图在两个缓冲区之间共享内存对象。

    如果插件想要就地修改缓冲区的数据或元数据,它应该首先使用 gst_buffer_make_writable 获取一个安全可修改的缓冲区。这个函数是经过优化的,只有在必要时才会制作副本。

  • 可以使用 GST_BUFFER_FLAG_SET 和 GST_BUFFER_FLAG_UNSET 宏设置和取消设置缓冲区的几个标志。使用 GST_BUFFER_FLAG_IS_SET 测试是否设置了某个 GstBufferFlags 标志。

  • 可以使用 gst_buffer_append 高效地将缓冲区合并成一个更大的缓冲区。只有在绝对需要时才会进行内存复制。

  • 可以使用 gst_buffer_add_meta 在缓冲区上设置任意额外的元数据。元数据可以通过 gst_buffer_get_meta 检索。另见 GstMeta。

  • 元素应该使用 gst_pad_push (参见 GstPad)将缓冲区解除引用或推出到源垫上。

  • 通常通过使用 gst_buffer_unref 来释放缓冲区。当引用计数降至0时,缓冲区所指向的任何内存和元数据也会被解除引用。从 GstBufferPool 分配的缓冲区在引用计数降至0时将被返回到池中。

  • GstParentBufferMeta 是可以附加到 GstBuffer 上的元数据,用于保留对另一个缓冲区的引用,只有在子 GstBuffer 被释放时才会释放。

  • 通常情况下,当子缓冲区直接使用父缓冲区的 GstMemory 并且希望在 GstMemory 可以重新使用之前防止父缓冲区被返回到缓冲区池时,会使用 GstParentBufferMeta。(自 1.6 版本起)

2 GstBuffer类型结构

GstBuffer 是一个继承于 GstMiniObject 的GBoxed类型对象。

2.1 GstBuffer类型注册宏定义

/* filename: gstbuffer.h */
GST_API GType _gst_buffer_type;
#define GST_TYPE_BUFFER                         (_gst_buffer_type)

/* filename: gstbuffer.c */
GType _gst_buffer_type = 0;

GST_DEFINE_MINI_OBJECT_TYPE (GstBuffer, gst_buffer);

/**
 * @calledby: gst_init调用
*/
void
_priv_gst_buffer_initialize (void)
{
  _gst_buffer_type = gst_buffer_get_type ();

#ifdef NO_64BIT_ATOMIC_INT_FOR_PLATFORM
  GST_CAT_WARNING (GST_CAT_PERFORMANCE,
      "No 64-bit atomic int defined for this platform/toolchain!");
#endif
}

2.2 GstBuffer类型相关枚举

2.2.1 GstBufferFlags

GstBufferFlags 枚举使用在第一个成员 GstMiniObject 的flag成员中。

  • GST_BUFFER_FLAG_TAG_MEMORY:GstBuffer只要修改GstMemory存储,就会设定此标志。例如:GstMemory添加到GstBuffer的时候会给flag设定此标志,调用的是 _memory_add 函数,该函数中会设定此标志。
/* filename: gstbuffer.h */
/**
 * GstBufferFlags:
 * @GST_BUFFER_FLAG_LIVE: 缓冲区包含实时数据,在 PAUSED 状态时应该被丢弃。
 * @GST_BUFFER_FLAG_DECODE_ONLY: 缓冲区包含应该被丢弃的数据,因为它将会被剪辑到段边界,
 *                               或者因为它不包含应该向用户显示的数据。
 * @GST_BUFFER_FLAG_DISCONT: 缓冲区标记了流中的数据不连续性。这通常发生在寻找操作后,
 *                           或从实时或网络源丢弃缓冲区后。
 * @GST_BUFFER_FLAG_RESYNC: 缓冲区的时间戳可能有不连续性,这个缓冲区是重新同步的好时机。
 * @GST_BUFFER_FLAG_CORRUPTED: 缓冲区数据已损坏。
 * @GST_BUFFER_FLAG_MARKER: 缓冲区包含特定于媒体的标记。对于视频,这是帧边界的结束,
 *                           对于音频,这是说话开始的标志。对于 RTP 数据包,这与
 *                           RTP 数据包头中的标记标志相匹配。
 * @GST_BUFFER_FLAG_HEADER: 缓冲区包含解码后续数据所需的头信息。
 * @GST_BUFFER_FLAG_GAP: 缓冲区被创建用于填补流中的间隙,并包含媒体中立数据
 *                       (元素可以切换到优化的代码路径,忽略缓冲区内容)。
 * @GST_BUFFER_FLAG_DROPPABLE: 缓冲区可以被丢弃而不破坏流,例如为了减少带宽。
 * @GST_BUFFER_FLAG_DELTA_UNIT: 此单元不能独立解码。
 * @GST_BUFFER_FLAG_TAG_MEMORY: 当缓冲区的内存被添加/移除时设置此标志。
 * @GST_BUFFER_FLAG_LAST: 可以从此标志开始添加额外的特定于媒体的标志。
 *
 * 用于描述 #GstBuffer 属性的一组缓冲区标志。
 */
typedef enum {
  GST_BUFFER_FLAG_LIVE          = (GST_MINI_OBJECT_FLAG_LAST << 0),
  GST_BUFFER_FLAG_DECODE_ONLY   = (GST_MINI_OBJECT_FLAG_LAST << 1),
  GST_BUFFER_FLAG_DISCONT       = (GST_MINI_OBJECT_FLAG_LAST << 2),
  GST_BUFFER_FLAG_RESYNC        = (GST_MINI_OBJECT_FLAG_LAST << 3),
  GST_BUFFER_FLAG_CORRUPTED     = (GST_MINI_OBJECT_FLAG_LAST << 4),
  GST_BUFFER_FLAG_MARKER        = (GST_MINI_OBJECT_FLAG_LAST << 5),
  GST_BUFFER_FLAG_HEADER        = (GST_MINI_OBJECT_FLAG_LAST << 6),
  GST_BUFFER_FLAG_GAP           = (GST_MINI_OBJECT_FLAG_LAST << 7),
  GST_BUFFER_FLAG_DROPPABLE     = (GST_MINI_OBJECT_FLAG_LAST << 8),
  GST_BUFFER_FLAG_DELTA_UNIT    = (GST_MINI_OBJECT_FLAG_LAST << 9),
  GST_BUFFER_FLAG_TAG_MEMORY    = (GST_MINI_OBJECT_FLAG_LAST << 10),
  /* 枚举值与上面标志对应 */
  
  /**
   * GST_BUFFER_FLAG_SYNC_AFTER:
   *
   * 写入磁盘或永久存储的元素应确保在写入此缓冲区的内容后同步数据。
   *
   * 自版本 1.6 起。
   */
  GST_BUFFER_FLAG_SYNC_AFTER    = (GST_MINI_OBJECT_FLAG_LAST << 11),

  /**
   * GST_BUFFER_FLAG_NON_DROPPABLE:
   *
   * 这个缓冲区很重要,不应该被丢弃。
   *
   * 这可以用来标记重要的缓冲区,例如标记携带关键帧或编解码器设置数据的 RTP 数据包,
   * 用于 RTP 前向错误纠正目的,或防止由于 QoS 而导致的静态视频帧被元素丢弃。
   *
   * 自版本 1.14 起。
   */
  GST_BUFFER_FLAG_NON_DROPPABLE = (GST_MINI_OBJECT_FLAG_LAST << 12),

  GST_BUFFER_FLAG_LAST          = (GST_MINI_OBJECT_FLAG_LAST << 16)
} GstBufferFlags;

2.2.2 GstBufferCopyFlags

这个是GstBuffer拷贝的时候使用的flags。提供给 gst_buffer_copy_into 函数使用的标志,用于指定应该复制那些项目。

/**
 * GstBufferCopyFlags:
 * @GST_BUFFER_COPY_NONE:不复制任何内容
 * @GST_BUFFER_COPY_FLAGS:表示要拷贝GstBuffer的flags(GstBufferFlags)
 * @GST_BUFFER_COPY_TIMESTAMPS:表示要拷贝GstBuffer的 pts, dts, duration, offset 和 offset_end
 * @GST_BUFFER_COPY_MEMORY:表示被拷贝的GstBuffer中的GstMemory中的data被引用到要拷贝的GstBuffer中。
 *                         如果内存被标记为NO_SHARE,会进行实际的内存复制,而是简单地引用。
 *                         添加 @GST_BUFFER_COPY_DEEP 来强制进行真正的复制。
 * @GST_BUFFER_COPY_MERGE:表示应该合并GstBuffer缓冲区内存
 * @GST_BUFFER_COPY_META: 表示应该拷贝GstBuffer的元数据
 *
 * 一组可以提供给 gst_buffer_copy_into() 函数的标志,
 * 用于指定应该复制哪些项目。
 */
typedef enum {
  GST_BUFFER_COPY_NONE           = 0,
  GST_BUFFER_COPY_FLAGS          = (1 << 0),
  GST_BUFFER_COPY_TIMESTAMPS     = (1 << 1),
  GST_BUFFER_COPY_META           = (1 << 2),
  GST_BUFFER_COPY_MEMORY         = (1 << 3),
  GST_BUFFER_COPY_MERGE          = (1 << 4),

  /**
   * GST_BUFFER_COPY_DEEP:
   *
   * 表示内存应该被深拷贝,而不是ref引用一下
   *
   * Since: 1.2
   */
  GST_BUFFER_COPY_DEEP           = (1 << 5)
} GstBufferCopyFlags;

2.3 GstBuffer相关结构体

2.3.1 GstBuffer

/* filename: gstbuffer.h */
/**
 * GstBuffer:
 * @mini_object: 父结构
 * @pool: 指向缓冲区拥有者的池的指针
 * @pts: 缓冲区的展示时间戳,当 pts 未知或不相关时可以是 #GST_CLOCK_TIME_NONE。pts 包含媒体应该呈现给用户的时间戳。
 * @dts: 缓冲区的解码时间戳,当 dts 未知或不相关时可以是 #GST_CLOCK_TIME_NONE。dts 包含媒体应该被处理的时间戳。
 * @duration: 缓冲区数据的持续时间,当持续时间未知或不相关时可以是 #GST_CLOCK_TIME_NONE。
 * @offset: 缓冲区数据的特定于媒体的偏移量。
 *     对于视频帧,这是此缓冲区的帧号。
 *     对于音频样本,这是此缓冲区中第一个样本的偏移量。
 *     对于文件数据或压缩数据,这是此缓冲区中第一个字节的字节偏移量。
 * @offset_end: 此缓冲区中包含的最后一个偏移量。它的格式与 @offset 相同。
 *
 * #GstBuffer 的结构。使用相关的宏来访问公共变量。
 */

struct _GstBuffer {
  GstMiniObject          mini_object;

  /*< public >*/ /* with COW */
  GstBufferPool         *pool;

  /* timestamp */
  GstClockTime           pts;
  GstClockTime           dts;
  GstClockTime           duration;

  /* media specific offset */
  guint64                offset;
  guint64                offset_end;
};

2.3.2 GstBufferImpl

gst_buffer_new 函数实际创建的是 GstBufferImpl 结构体。

/* filename: gstbuffer.c */
typedef struct
{
  GstBuffer buffer;

  gsize slice_size; /* 存储的一般都是 sizeof(GstBufferImpl)  */

  /* the memory blocks */
  guint len;
  GstMemory *mem[GST_BUFFER_MEM_MAX];

  /* 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;

3 GstBuffer对象相关函数

3.1 gst_buffer_new

/**
 * @name: gst_buffer_new
 * @brief: 创建一个GstBufferImpl,并且初始化
 * @note: 此时的GstBuffer是空的,没有data(GstMemory)
 * @return: 新创建的GstBuffer
 */
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);
}

4 MetaData

4.1 GstParentBufferMeta

4.2 GstReferenceTimestampMeta