当前位置: 首页 > article >正文

【GStreamer】GstBuffer的简单分析

目录

  • 1.GstBuffer
    • 1.1 GstBuffer类定义
      • 1.1.1 GstBufferFlags
      • 1.1.2 GstBuffer
      • 1.1.3 GstBufferCopyFlags
      • 1.1.4 GstBufferImpl
    • 1.2 GstBuffer函数定义
      • 1.2.1 创建GstBuffer
        • 1.2.1.1 无给定参数,创建GstBuffer(gst_buffer_new_allocate)
        • 1.2.1.2 已给定参数,创建GstBuffer(gst_buffer_new_wrapped)
      • 1.2.2 初始化(gst_buffer_init)
      • 1.2.3 buffer添加mem(gst_buffer_insert_memory)
      • 1.2.4 获取buffer中的mem(gst_buffer_get_memory)
      • 1.2.5 替换buffer中的mem(gst_buffer_replace_memory)
      • 1.2.6 移除buffer中的mem(gst_buffer_remove_memory)
      • 1.2.7 buffer中的mem映射(gst_buffer_map)
      • 1.2.8 取消buffer中的mem映射(gst_buffer_unmap)
      • 1.2.9 合并两个buffer(gst_buffer_append)
      • 1.2.10 元数据的处理
      • 1.2.11 拷贝buffer(gst_buffer_copy)
      • 1.2.12 清除buffer(gst_clear_buffer)

1.GstBuffer

GstBuffer用于封装和管理多媒体数据(如视频、音频等),提供了一种灵活的方式来存储和操作数据,同时支持多种内存管理机制。GstBuffer的声明和定义位于gstreamer/subprojects/gstreamer/gst/gstbuffer.h和gstreamer/subprojects/gstreamer/gst/gstbuffer.c中。

1.1 GstBuffer类定义

1.1.1 GstBufferFlags

GstBufferFlags描述了GstBuffer属性的一组标志,这些标志可以用来标记缓冲区的某些特性,例如是否包含关键帧、是否可丢弃等。

/**
 * GstBufferFlags:
 * @GST_BUFFER_FLAG_LIVE:          the buffer is live data and should be discarded in
 *                                 the PAUSED state.
 * @GST_BUFFER_FLAG_DECODE_ONLY:   the buffer contains data that should be dropped
 *                                 because it will be clipped against the segment
 *                                 boundaries or because it does not contain data
 *                                 that should be shown to the user.
 * @GST_BUFFER_FLAG_DISCONT:       the buffer marks a data discontinuity in the stream.
 *                                 This typically occurs after a seek or a dropped buffer
 *                                 from a live or network source.
 * @GST_BUFFER_FLAG_RESYNC:        the buffer timestamps might have a discontinuity
 *                                 and this buffer is a good point to resynchronize.
 * @GST_BUFFER_FLAG_CORRUPTED:     the buffer data is corrupted.
 * @GST_BUFFER_FLAG_MARKER:        the buffer contains a media specific marker. for
 *                                 video this is the end of a frame boundary, for audio
 *                                 this is the start of a talkspurt. for RTP
 *                                 packets this matches the marker flag in the
 *                                 RTP packet header.
 * @GST_BUFFER_FLAG_HEADER:        the buffer contains header information that is
 *                                 needed to decode the following data.
 * @GST_BUFFER_FLAG_GAP:           the buffer has been created to fill a gap in the
 *                                 stream and contains media neutral data (elements can
 *                                 switch to optimized code path that ignores the buffer
 *                                 content).
 * @GST_BUFFER_FLAG_DROPPABLE:     the buffer can be dropped without breaking the
 *                                 stream, for example to reduce bandwidth.
 * @GST_BUFFER_FLAG_DELTA_UNIT:    this unit cannot be decoded independently.
 * @GST_BUFFER_FLAG_TAG_MEMORY:    this flag is set when memory of the buffer
 *                                 is added/removed
 * @GST_BUFFER_FLAG_LAST:          additional media specific flags can be added starting from
 *                                 this flag.
 *
 * A set of buffer flags used to describe properties of a #GstBuffer.
 */
typedef enum {
  // GST_MINI_OBJECT_FLAG_LAST = 16
  // 缓冲区包含实时数据,应在暂停时丢弃,确保不会积累不必要的数据
  GST_BUFFER_FLAG_LIVE          = (GST_MINI_OBJECT_FLAG_LAST << 0),
  // 缓冲区包含的数据应该被丢弃,因为它被clip到segment之外,或者不包含应该展示给调用者的信息
  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),
  /*
   * 缓冲区包含特定媒体标记,例如
   * (1) 视频: 帧边界结束的标记
   * (2) 音频: 语音段的开始
   * (3) RTP数据包:与RTP数据包头中的标记标志匹配
   */
  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:
   *
   * Elements which write to disk or permanent storage should ensure the data
   * is synced after writing the contents of this buffer.
   *
   * Since: 1.6
   */
  // 写入磁盘或永久存储的元素应在写入此缓冲区的内容后确保数据同步,确保数据的持久性(特别是写入磁盘)
  GST_BUFFER_FLAG_SYNC_AFTER    = (GST_MINI_OBJECT_FLAG_LAST << 11),

  /**
   * GST_BUFFER_FLAG_NON_DROPPABLE:
   *
   * This buffer is important and should not be dropped.
   *
   * This can be used to mark important buffers, e.g. to flag RTP packets
   * carrying keyframes or codec setup data for RTP Forward Error Correction
   * purposes, or to prevent still video frames from being dropped by elements
   * due to QoS.
   *
   * Since: 1.14
   */
  // 缓冲区很重要,不应被丢弃,可能携带关键帧或编码器设置数据的RTP数据包
  GST_BUFFER_FLAG_NON_DROPPABLE = (GST_MINI_OBJECT_FLAG_LAST << 12),

  GST_BUFFER_FLAG_LAST          = (GST_MINI_OBJECT_FLAG_LAST << 16)
} GstBufferFlags;

1.1.2 GstBuffer

/**
 * GstBuffer:
 * @mini_object: the parent structure
 * @pool: pointer to the pool owner of the buffer
 * @pts: presentation timestamp of the buffer, can be #GST_CLOCK_TIME_NONE when the
 *     pts is not known or relevant. The pts contains the timestamp when the
 *     media should be presented to the user.
 * @dts: decoding timestamp of the buffer, can be #GST_CLOCK_TIME_NONE when the
 *     dts is not known or relevant. The dts contains the timestamp when the
 *     media should be processed.
 * @duration: duration in time of the buffer data, can be #GST_CLOCK_TIME_NONE
 *     when the duration is not known or relevant.
 * @offset: a media specific offset for the buffer data.
 *     For video frames, this is the frame number of this buffer.
 *     For audio samples, this is the offset of the first sample in this buffer.
 *     For file data or compressed data this is the byte offset of the first
 *       byte in this buffer.
 * @offset_end: the last offset contained in this buffer. It has the same
 *     format as @offset.
 *
 * The structure of a #GstBuffer. Use the associated macros to access the public
 * variables.
 */
struct _GstBuffer {
  // 继承自GstMiniObject,用于引用计数
  GstMiniObject          mini_object;

  /*< public >*/ /* with COW */
  // 缓冲池,用于提高内存分配和释放的效率
  GstBufferPool         *pool;

  /* timestamp */
  // 播放时间戳
  GstClockTime           pts;
  // 解码时间戳
  GstClockTime           dts;
  // 数据持续时间
  GstClockTime           duration;

  /* media specific offset */
  // 缓冲区数据在媒体流中的偏移位置
  guint64                offset;
  // 缓冲区数据在媒体流中的结束位置
  guint64                offset_end;
};

1.1.3 GstBufferCopyFlags

在调用gst_buffer_copy_into()函数时,可以选择性地复制缓冲区的某些部分

/**
 * GstBufferCopyFlags:
 * @GST_BUFFER_COPY_NONE: copy nothing
 * @GST_BUFFER_COPY_FLAGS: flag indicating that buffer flags should be copied
 * @GST_BUFFER_COPY_TIMESTAMPS: flag indicating that buffer pts, dts,
 *   duration, offset and offset_end should be copied
 * @GST_BUFFER_COPY_MEMORY: flag indicating that buffer memory should be reffed
 *   and appended to already existing memory. Unless the memory is marked as
 *   NO_SHARE, no actual copy of the memory is made but it is simply reffed.
 *   Add @GST_BUFFER_COPY_DEEP to force a real copy.
 * @GST_BUFFER_COPY_MERGE: flag indicating that buffer memory should be
 *   merged
 * @GST_BUFFER_COPY_META: flag indicating that buffer meta should be
 *   copied
 *
 * A set of flags that can be provided to the gst_buffer_copy_into()
 * function to specify which items should be copied.
 */
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:
   *
   * flag indicating that memory should always be copied instead of reffed
   *
   * Since: 1.2
   */
  // 强制进行实际的内存拷贝
  GST_BUFFER_COPY_DEEP           = (1 << 5)
} GstBufferCopyFlags;

1.1.4 GstBufferImpl

GstBufferImpl用于实现GstBuffer的具体功能,它扩展了GstBuffer,并添加了一些用于管理内存块和元数据的字段

typedef struct
{
  // GstBufferImpl继承自GstBuffer,包含GstBuffer的所有字段和功能
  GstBuffer buffer;

  /* the memory blocks */
  // 内存块数量
  guint len;
  // 存储GstBufferImpl内存块的数组
  GstMemory *mem[GST_BUFFER_MEM_MAX];

  /* memory of the buffer when allocated from 1 chunk */
  // 指向一个单一的内存块,当GstBufferImpl从一个连续的内存块中分配时使用
  GstMemory *bufmem;

  /* FIXME, make metadata allocation more efficient by using part of the
   * GstBufferImpl */
  // 元数据项链表
  GstMetaItem *item;
  // 元数据项链表尾节点
  GstMetaItem *tail_item;
} GstBufferImpl;

1.2 GstBuffer函数定义

GstBuffer类相关的操作,主要包括创建GstBuffer、初始化GstBuffer、向GstBuffer中添加内存块、移除GstBuffer中的内存块、获取GstBuffer内存块信息、拷贝GstBuffer信息和释放GstBuffer。

1.2.1 创建GstBuffer

1.2.1.1 无给定参数,创建GstBuffer(gst_buffer_new_allocate)

gst_buffer_new_allocate()创建了buffer,同时使用allocator分配一片内存空间mem,随后将mem添加到buffer中

/**
 * gst_buffer_new_allocate:
 * @allocator: (transfer none) (allow-none): the #GstAllocator to use, or %NULL to use the
 *     default allocator
 * @size: the size in bytes of the new buffer's data.
 * @params: (transfer none) (allow-none): optional parameters
 *
 * Tries to create a newly allocated buffer with data of the given size and
 * extra parameters from @allocator. If the requested amount of memory can't be
 * allocated, %NULL will be returned. The allocated buffer memory is not cleared.
 *
 * When @allocator is %NULL, the default memory allocator will be used.
 *
 * Note that when @size == 0, the buffer will not have memory associated with it.
 *
 * Returns: (transfer full) (nullable): a new #GstBuffer
 */
GstBuffer *
gst_buffer_new_allocate (GstAllocator * allocator, gsize size,
    GstAllocationParams * params)
{
  GstBuffer *newbuf;
  GstMemory *mem;
#if 0
  guint8 *data;
  gsize asize;
#endif

#if 1
  if (size > 0) {
    // 使用allocator分配内存空间
    mem = gst_allocator_alloc (allocator, size, params);
    // G_UNLIKELY是GLib库的一个宏
    // 使用这个宏,编译器会将这个条件标记为不太可能发生的情况,从而在生成代码时优化分支预测
    if (G_UNLIKELY (mem == NULL))
      goto no_memory;
  } else {
    mem = NULL;
  }
  // 创建一个GstBuffer,但没有分配内存
  newbuf = gst_buffer_new ();

  if (mem != NULL) {
    gst_memory_lock (mem, GST_LOCK_FLAG_EXCLUSIVE);
    // 将分配的内存添加到GstBuffer中
    _memory_add (newbuf, -1, mem);
  }

  GST_CAT_LOG (GST_CAT_BUFFER,
      "new buffer %p of size %" G_GSIZE_FORMAT " from allocator %p", newbuf,
      size, allocator);
#endif

#if 0
  asize = sizeof (GstBufferImpl) + size;
  data = g_malloc (asize);
  if (G_UNLIKELY (data == NULL))
    goto no_memory;

  newbuf = GST_BUFFER_CAST (data);

  gst_buffer_init ((GstBufferImpl *) data, asize);
  if (size > 0) {
    mem = gst_memory_new_wrapped (0, data + sizeof (GstBufferImpl), NULL,
        size, 0, size);
    _memory_add (newbuf, -1, mem, TRUE);
  }
#endif

#if 0
  /* allocate memory and buffer, it might be interesting to do this but there
   * are many complications. We need to keep the memory mapped to access the
   * buffer fields and the memory for the buffer might be just very slow. We
   * also need to do some more magic to get the alignment right. */
  asize = sizeof (GstBufferImpl) + size;
  mem = gst_allocator_alloc (allocator, asize, align);
  if (G_UNLIKELY (mem == NULL))
    goto no_memory;

  /* map the data part and init the buffer in it, set the buffer size to 0 so
   * that a finalize won't free the buffer */
  data = gst_memory_map (mem, &asize, NULL, GST_MAP_WRITE);
  gst_buffer_init ((GstBufferImpl *) data, 0);
  gst_memory_unmap (mem);

  /* strip off the buffer */
  gst_memory_resize (mem, sizeof (GstBufferImpl), size);

  newbuf = GST_BUFFER_CAST (data);
  GST_BUFFER_BUFMEM (newbuf) = mem;

  if (size > 0)
    _memory_add (newbuf, -1, gst_memory_ref (mem), TRUE);
#endif
  GST_BUFFER_FLAG_UNSET (newbuf, GST_BUFFER_FLAG_TAG_MEMORY);

  return newbuf;

  /* ERRORS */
no_memory:
  {
    GST_CAT_WARNING (GST_CAT_BUFFER,
        "failed to allocate %" G_GSIZE_FORMAT " bytes", size);
    return NULL;
  }
}

_memory_add()是GstBuffer内部函数,用于将mem添加到buffer中,这是一个静态内联函数

static inline void
_memory_add (GstBuffer * buffer, gint idx, GstMemory * mem)
{
  guint i, len = GST_BUFFER_MEM_LEN (buffer);
  // log级别日志
  GST_CAT_LOG (GST_CAT_BUFFER, "buffer %p, idx %d, mem %p", buffer, idx, mem);
  // 如果内存数组已满,尝试合并所有内存块为一个单一的内存块(不常见)
  if (G_UNLIKELY (len >= GST_BUFFER_MEM_MAX)) {
    /* too many buffer, span them. */
    /* FIXME, there is room for improvement here: We could only try to merge
     * 2 buffers to make some room. If we can't efficiently merge 2 buffers we
     * could try to only merge the two smallest buffers to avoid memcpy, etc. */
    // debug级别日志
    GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "memory array overflow in buffer %p",
        buffer);
    /*
     * _get_merged_memory(buffer, 0, len)表示将buffer中的mem,从0开始到len,所有的mem合成一个mem
     * _replace_memory(buffer, len, 0, len, mem)表示将buffer中从索引0开始的len个内存块替换为一个新的内存
     * 这里的操作是,buffer中mem已满,现在将所有的mem合并成merged_mem,并替换原有buffer中的mem,
     * 此时,buffer当中mem的数量变为了1,但是包含了之前所有mem的信息
     */
    _replace_memory (buffer, len, 0, len, _get_merged_memory (buffer, 0, len));
    /* we now have 1 single spanned buffer */
    len = 1;
  }
  // 如果idx = -1,将内存添加到数组的末尾
  if (idx == -1)
    idx = len;
  // 移动内存块,腾出插入位置
  for (i = len; i > idx; i--) {
    /* move buffers to insert, FIXME, we need to insert first and then merge */
    GST_BUFFER_MEM_PTR (buffer, i) = GST_BUFFER_MEM_PTR (buffer, i - 1);
  }
  /* and insert the new buffer */
  // 将新的内存块插入到idx位置
  GST_BUFFER_MEM_PTR (buffer, idx) = mem;
  // buffer中mem数量加1
  GST_BUFFER_MEM_LEN (buffer) = len + 1;
  // 将buffer设置为mem的父对象,确保内存块的生命周期与缓冲区一致
  gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (mem),
      GST_MINI_OBJECT_CAST (buffer));
  // 更改FLAG,表示缓冲区内存已更改
  GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY);
}

_get_merged_memory()作用是将buffer中的mem,从idx开始到length,所有的mem合成一个mem

static inline GstMemory *
_get_merged_memory (GstBuffer * buffer, guint idx, guint length)
{
  GST_CAT_LOG (GST_CAT_BUFFER, "buffer %p, idx %u, length %u", buffer, idx,
      length);
  // buffer中mem数量为0,返回NULL
  if (G_UNLIKELY (length == 0))
    return NULL;
  // buffer中mem数量为1,返回mem,并添加引用计数
  if (G_LIKELY (length == 1))
    return gst_memory_ref (GST_BUFFER_MEM_PTR (buffer, idx));
  // 实际进行mem的merge
  return _actual_merged_memory (buffer, idx, length);
}

_actual_merged_memory()是进行mem merge的实际执行函数

static GstMemory *
_actual_merged_memory (GstBuffer * buffer, guint idx, guint length)
{
  GstMemory **mem, *result = NULL;
  GstMemory *parent = NULL;
  gsize size, poffset = 0;
  // mem队列
  mem = GST_BUFFER_MEM_ARRAY (buffer);
  // 获取从idx开始,长度为length的多个内存块的总大小
  size = gst_buffer_get_sizes_range (buffer, idx, length, NULL, NULL);
  // 检查从idx开始的length个内存块是否属于同一个父内存块
  if (G_UNLIKELY (_is_span (mem + idx, length, &poffset, &parent))) {
    // 检查parent是否允许内存共享
    if (!GST_MEMORY_IS_NO_SHARE (parent))
      // 尝试共享父内存块的一部分
      result = gst_memory_share (parent, poffset, size);
    // 如果不能共享内存,复制数据到一个新的内存块
    if (!result) {
      GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy for merge %p", parent);
      result = gst_memory_copy (parent, poffset, size);
    }
  } else { // 多数情况下,当前buffer的多个内存块不连续
    gsize i, tocopy, left;
    GstMapInfo sinfo, dinfo;
    guint8 *ptr;
 	// 分配一片内存空间,作为最后融合起来的内存块,需要将其他的内存块添加到result中
    result = gst_allocator_alloc (NULL, size, NULL);
    // 检查result是否可以写入,映射的指针是dinfo
    if (result == NULL || !gst_memory_map (result, &dinfo, GST_MAP_WRITE)) {
      GST_CAT_ERROR (GST_CAT_BUFFER, "Failed to map memory writable");
      if (result)
        gst_memory_unref (result);
      return NULL;
    }

    ptr = dinfo.data;
    left = size;
	// 遍历内存块
    for (i = idx; i < (idx + length) && left > 0; i++) {
      // 将mem中的信息映射到sinfo中
      if (!gst_memory_map (mem[i], &sinfo, GST_MAP_READ)) {
        GST_CAT_ERROR (GST_CAT_BUFFER,
            "buffer %p, idx %u, length %u failed to map readable", buffer,
            idx, length);
        gst_memory_unmap (result, &dinfo);
        gst_memory_unref (result);
        return NULL;
      }
      tocopy = MIN (sinfo.size, left);
      GST_CAT_DEBUG (GST_CAT_PERFORMANCE,
          "memcpy %" G_GSIZE_FORMAT " bytes for merge %p from memory %p",
          tocopy, result, mem[i]);
      // 将sinfo的信息拷贝到ptr中,ptr = dinfo.data
      // 而dinfo由result映射而来,所以实际是将sinfo信息写入result
      memcpy (ptr, (guint8 *) sinfo.data, tocopy);
      left -= tocopy;
      ptr += tocopy;
      gst_memory_unmap (mem[i], &sinfo);
    }
    gst_memory_unmap (result, &dinfo);
  }

  return result;
}

_replace_memory()的作用是替换GstBuffer中的内存块,在合并或者替换内存块时很常用。它从GstBuffer中从idx开始,替换长度为length个内存块,替换的内容是mem,如果mem为NULL,则移除指定范围内的内存块。下面函数的过程是:
(a)释放从idx开始,长度为length的内存(减少其引用计数,而非free)
(b)将mem插入到buffer当中idx的位置
(c)计算buffer现在的mem数量

如果原有buffer中有4个mem,现在需要从第1个开始到第4个进行替换,替换的对象是merged_mem,替换之后buffer中的mem数量为2个

/*
 * buffer : 要操作的缓冲区
 * len : 当前缓冲区中内存块的总数
 * idx : 从哪个索引开始替换内存块
 * length : 要替换的内存块的数量
 * mem : 指向新的mem对象指针,用于替换旧的内存块
 */
static void
_replace_memory (GstBuffer * buffer, guint len, guint idx, guint length,
    GstMemory * mem)
{
  gsize end, i;

  end = idx + length;

  GST_CAT_LOG (GST_CAT_BUFFER,
      "buffer %p replace %u-%" G_GSIZE_FORMAT " with memory %p", buffer, idx,
      end, mem);

  /* unref old memory */
  // 释放旧内存块
  for (i = idx; i < end; i++) {
    GstMemory *old = GST_BUFFER_MEM_PTR (buffer, i);

    gst_memory_unlock (old, GST_LOCK_FLAG_EXCLUSIVE);
    // 解除父对象引用
    gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (old),
        GST_MINI_OBJECT_CAST (buffer));
    // 释放引用计数
    gst_memory_unref (old);
  }
  // 替换内存块
  if (mem != NULL) {
    /* replace with single memory */
    gst_mini_object_add_parent (GST_MINI_OBJECT_CAST (mem),
        GST_MINI_OBJECT_CAST (buffer));
    gst_memory_lock (mem, GST_LOCK_FLAG_EXCLUSIVE);
    GST_BUFFER_MEM_PTR (buffer, idx) = mem;
    idx++;
    length--;
  }
  // 从end开始,将剩余内存块向前移动,填补被替换内存块的位置
  if (end < len) {
    memmove (&GST_BUFFER_MEM_PTR (buffer, idx),
        &GST_BUFFER_MEM_PTR (buffer, end), (len - end) * sizeof (gpointer));
  }
  // 计算替换之后buffer中的mem数量
  GST_BUFFER_MEM_LEN (buffer) = len - length;
  // 修改flag,表示缓冲区的内存内容已经更改
  GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY);
}
1.2.1.2 已给定参数,创建GstBuffer(gst_buffer_new_wrapped)

如果已给定参数,可以使用gst_buffer_new_wrapped()创建buffer,通过指定偏移量和大小,可以只封装内存区域的一部分。

/**
 * gst_buffer_new_wrapped:
 * @data: (array length=size) (element-type guint8) (transfer full): data to wrap
 * @size: allocated size of @data
 *
 * Creates a new buffer that wraps the given @data. The memory will be freed
 * with g_free() and will be marked writable.
 *
 * Returns: (transfer full): a new #GstBuffer
 */
GstBuffer *
gst_buffer_new_wrapped (gpointer data, gsize size)
{
  return gst_buffer_new_wrapped_full (0, data, size, 0, size, data, g_free);
}

调用了gst_buffer_new_wrapped_full()

/**
 * gst_buffer_new_wrapped_full:
 * @flags: #GstMemoryFlags
 * @data: (array length=size) (element-type guint8) (transfer none): data to wrap
 * @maxsize: allocated size of @data
 * @offset: offset in @data
 * @size: size of valid data
 * @user_data: (allow-none): user_data
 * @notify: (allow-none) (scope async) (closure user_data): called with @user_data when the memory is freed
 *
 * Allocates a new buffer that wraps the given memory. @data must point to
 * @maxsize of memory, the wrapped buffer will have the region from @offset and
 * @size visible.
 *
 * When the buffer is destroyed, @notify will be called with @user_data.
 *
 * The prefix/padding must be filled with 0 if @flags contains
 * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED respectively.
 *
 * Returns: (transfer full): a new #GstBuffer
 */
GstBuffer *
gst_buffer_new_wrapped_full (GstMemoryFlags flags, gpointer data,
    gsize maxsize, gsize offset, gsize size, gpointer user_data,
    GDestroyNotify notify)
{
  GstMemory *mem;
  GstBuffer *newbuf;
  // 创建buffer,但不分配mem
  newbuf = gst_buffer_new ();
  // 给定参数,分配mem
  mem =
      gst_memory_new_wrapped (flags, data, maxsize, offset, size, user_data,
      notify);
  gst_memory_lock (mem, GST_LOCK_FLAG_EXCLUSIVE);
  // 将mem添加到newbuf
  _memory_add (newbuf, -1, mem);
  GST_BUFFER_FLAG_UNSET (newbuf, GST_BUFFER_FLAG_TAG_MEMORY);

  return newbuf;
}

gst_memory_new_wrapped()定义在gstreamer/subprojects/gstreamer/gst/gstallocator.c中

/**
 * gst_memory_new_wrapped:
 * @flags: #GstMemoryFlags
 * @data: (array length=size) (element-type guint8) (transfer none): data to
 *   wrap
 * @maxsize: allocated size of @data
 * @offset: offset in @data
 * @size: size of valid data
 * @user_data: (nullable): user_data
 * @notify: (nullable) (scope async) (closure user_data): called with @user_data when the memory is freed
 *
 * Allocate a new memory block that wraps the given @data.
 *
 * The prefix/padding must be filled with 0 if @flags contains
 * #GST_MEMORY_FLAG_ZERO_PREFIXED and #GST_MEMORY_FLAG_ZERO_PADDED respectively.
 *
 * Returns: (transfer full) (nullable): a new #GstMemory.
 */
GstMemory *
gst_memory_new_wrapped (GstMemoryFlags flags, gpointer data,
    gsize maxsize, gsize offset, gsize size, gpointer user_data,
    GDestroyNotify notify)
{
  GstMemorySystem *mem;

  g_return_val_if_fail (data != NULL, NULL);
  g_return_val_if_fail (offset + size <= maxsize, NULL);
  // 给定参数,分配mem
  mem =
      _sysmem_new (flags, NULL, data, maxsize, 0, offset, size, user_data,
      notify);

  return (GstMemory *) mem;
}

1.2.2 初始化(gst_buffer_init)

初始化函数的入参是GstBufferImpl,完成了buffer中参数的初始化,包括引用计数、bufferpool、时间戳信息、偏移量和长度等信息

static void
gst_buffer_init (GstBufferImpl * buffer)
{
  // 初始化引用计数
  gst_mini_object_init (GST_MINI_OBJECT_CAST (buffer), 0, _gst_buffer_type,
      (GstMiniObjectCopyFunction) _gst_buffer_copy,
      (GstMiniObjectDisposeFunction) _gst_buffer_dispose,
      (GstMiniObjectFreeFunction) _gst_buffer_free);
  // 缓冲池
  GST_BUFFER (buffer)->pool = NULL;
  // 播放时间戳
  GST_BUFFER_PTS (buffer) = GST_CLOCK_TIME_NONE;
  // 解码时间戳
  GST_BUFFER_DTS (buffer) = GST_CLOCK_TIME_NONE;
  // 数据持续时间
  GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
  // 数据的媒体特定偏移量
  GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE;
  // 缓冲区中最后一个偏移量
  GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE;
  // buffer中mem长度
  GST_BUFFER_MEM_LEN (buffer) = 0;
  // buffer中meta数据
  GST_BUFFER_META (buffer) = NULL;
}

1.2.3 buffer添加mem(gst_buffer_insert_memory)

/**
 * gst_buffer_insert_memory:
 * @buffer: a #GstBuffer.
 * @idx: the index to add the memory at, or -1 to append it to the end
 * @mem: (transfer full): a #GstMemory.
 *
 * Inserts the memory block @mem into @buffer at @idx. This function takes ownership
 * of @mem and thus doesn't increase its refcount.
 *
 * Only gst_buffer_get_max_memory() can be added to a buffer. If more memory is
 * added, existing memory blocks will automatically be merged to make room for
 * the new memory.
 */
void
gst_buffer_insert_memory (GstBuffer * buffer, gint idx, GstMemory * mem)
{
  GstMemory *tmp;

  g_return_if_fail (GST_IS_BUFFER (buffer));
  g_return_if_fail (gst_buffer_is_writable (buffer));
  g_return_if_fail (mem != NULL);
  g_return_if_fail (idx == -1 ||
      (idx >= 0 && idx <= GST_BUFFER_MEM_LEN (buffer)));
  // 获取mem的独占权,防止其他占用
  tmp = _memory_get_exclusive_reference (mem);
  g_return_if_fail (tmp != NULL);
  gst_memory_unref (mem);
  // 将tmp添加到buffer的第idx位置
  _memory_add (buffer, idx, tmp);
}

_memory_get_exclusive_reference()的定义如下,其作用是获取mem的独占引用,如果无法直接获取独占引用,则尝试创建一个副本并获取该副本的独占引用

/* transfer full for return and transfer none for @mem */
static inline GstMemory *
_memory_get_exclusive_reference (GstMemory * mem)
{
  GstMemory *ret = NULL;
  // 尝试锁定mem,获取独占访问权限
  if (gst_memory_lock (mem, GST_LOCK_FLAG_EXCLUSIVE)) {
    // 独占成功,增加引用计数
    ret = gst_memory_ref (mem);
  } else { 
    /* we cannot take another exclusive lock as the memory is already
     * locked WRITE + EXCLUSIVE according to part-miniobject.txt */
    // 无法独占访问权限,尝试创建mem的副本,复制整个内存区域
    ret = gst_memory_copy (mem, 0, -1);

    if (ret) {
      if (!gst_memory_lock (ret, GST_LOCK_FLAG_EXCLUSIVE)) {
        gst_memory_unref (ret);
        ret = NULL;
      }
    }
  }

  if (!ret)
    GST_CAT_WARNING (GST_CAT_BUFFER, "Failed to acquire an exclusive lock for "
        "memory %p", mem);

  return ret;
}

实际上,gst_buffer_insert_memory()还会被gst_buffer_prepend_memory()和gst_buffer_append_memory()调用,其中gst_buffer_prepend_memory()会将新的mem添加到buffer中所有mem最前面的位置,gst_buffer_append_memory()会将新的mem添加到buffer中所有mem最后面位置。

/**
 * gst_buffer_append_memory:
 * @buffer: a #GstBuffer.
 * @mem: (transfer full): a #GstMemory.
 *
 * Appends the memory block @mem to @buffer. This function takes
 * ownership of @mem and thus doesn't increase its refcount.
 *
 * This function is identical to gst_buffer_insert_memory() with an index of -1.
 * See gst_buffer_insert_memory() for more details.
 */
void
gst_buffer_append_memory (GstBuffer * buffer, GstMemory * mem)
{
  // 添加到最后一个位置
  gst_buffer_insert_memory (buffer, -1, mem);
}

/**
 * gst_buffer_prepend_memory:
 * @buffer: a #GstBuffer.
 * @mem: (transfer full): a #GstMemory.
 *
 * Prepends the memory block @mem to @buffer. This function takes
 * ownership of @mem and thus doesn't increase its refcount.
 *
 * This function is identical to gst_buffer_insert_memory() with an index of 0.
 * See gst_buffer_insert_memory() for more details.
 */
void
gst_buffer_prepend_memory (GstBuffer * buffer, GstMemory * mem)
{
  // 添加到第一个位置
  gst_buffer_insert_memory (buffer, 0, mem);
}

1.2.4 获取buffer中的mem(gst_buffer_get_memory)

gst_buffer_get_memory()用于获取一个buffer中,位于idx位置的mem

/**
 * gst_buffer_get_memory:
 * @buffer: a #GstBuffer.
 * @idx: an index
 *
 * Gets the memory block at index @idx in @buffer.
 *
 * Returns: (transfer full) (nullable): a #GstMemory that contains the data of the
 * memory block at @idx.
 */
GstMemory *
gst_buffer_get_memory (GstBuffer * buffer, guint idx)
{
  // 从buffer中提取从idx开始,长度为1的内存块
  return gst_buffer_get_memory_range (buffer, idx, 1);
}

gst_buffer_get_memory_range()的作用是,从buffer中提取从idx开始,长度为length的内存块

/**
 * gst_buffer_get_memory_range:
 * @buffer: a #GstBuffer.
 * @idx: an index
 * @length: a length
 *
 * Gets @length memory blocks in @buffer starting at @idx. The memory blocks will
 * be merged into one large #GstMemory.
 *
 * If @length is -1, all memory starting from @idx is merged.
 *
 * Returns: (transfer full) (nullable): a #GstMemory that contains the merged data of @length
 *    blocks starting at @idx.
 */
GstMemory *
gst_buffer_get_memory_range (GstBuffer * buffer, guint idx, gint length)
{
  guint len;

  GST_CAT_DEBUG (GST_CAT_BUFFER, "idx %u, length %d", idx, length);

  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
  // 获取buffer的mem长度
  len = GST_BUFFER_MEM_LEN (buffer);
  g_return_val_if_fail ((len == 0 && idx == 0 && length == -1) ||
      (length == -1 && idx < len) || (length > 0 && length + idx <= len), NULL);

  if (length == -1)
    length = len - idx;
  // 从buffer中获取从idx开始,长度为length的内存块,这些内存块会被合并成
  // 一个内存块
  return _get_merged_memory (buffer, idx, length);
}

除了gst_buffer_get_memory()获取单个mem之外,gst_buffer_get_all_memory()用于获取buffer中所有的mem

/**
 * gst_buffer_get_all_memory:
 * @buffer: a #GstBuffer.
 *
 * Gets all the memory blocks in @buffer. The memory blocks will be merged
 * into one large #GstMemory.
 *
 * Returns: (transfer full) (nullable): a #GstMemory that contains the merged memory.
 */
GstMemory *
gst_buffer_get_all_memory (GstBuffer * buffer)
{
  // 获取buffer中的mem,索引从0开始到最后
  return gst_buffer_get_memory_range (buffer, 0, -1);
}

1.2.5 替换buffer中的mem(gst_buffer_replace_memory)

/**
 * gst_buffer_replace_memory:
 * @buffer: a #GstBuffer.
 * @idx: an index
 * @mem: (transfer full): a #GstMemory
 *
 * Replaces the memory block at index @idx in @buffer with @mem.
 */
void
gst_buffer_replace_memory (GstBuffer * buffer, guint idx, GstMemory * mem)
{
  // 替换buffer中从idx开始,长度为1的内存,替换进去的是mem
  gst_buffer_replace_memory_range (buffer, idx, 1, mem);
}

gst_buffer_replace_memory_range()的作用是替换buffer中从idx开始,长度为length的内存,替换进去的是mem

/**
 * gst_buffer_replace_memory_range:
 * @buffer: a #GstBuffer.
 * @idx: an index
 * @length: a length, should not be 0
 * @mem: (transfer full): a #GstMemory
 *
 * Replaces @length memory blocks in @buffer starting at @idx with @mem.
 *
 * If @length is -1, all memory starting from @idx will be removed and
 * replaced with @mem.
 *
 * @buffer should be writable.
 */
void
gst_buffer_replace_memory_range (GstBuffer * buffer, guint idx, gint length,
    GstMemory * mem)
{
  guint len;

  g_return_if_fail (GST_IS_BUFFER (buffer));
  g_return_if_fail (gst_buffer_is_writable (buffer));

  GST_CAT_DEBUG (GST_CAT_BUFFER, "idx %u, length %d, %p", idx, length, mem);
  // 获取buffer中mem的数量
  len = GST_BUFFER_MEM_LEN (buffer);
  g_return_if_fail ((len == 0 && idx == 0 && length == -1) ||
      (length == -1 && idx < len) || (length > 0 && length + idx <= len));

  if (length == -1)
    length = len - idx;
  // 替换内存
  _replace_memory (buffer, len, idx, length, mem);
}

gst_buffer_replace_memory()用于替换指定位置的mem,gst_buffer_replace_all_memory()用于替换buffer中所有的mem

/**
 * gst_buffer_replace_all_memory:
 * @buffer: a #GstBuffer.
 * @mem: (transfer full): a #GstMemory
 *
 * Replaces all memory in @buffer with @mem.
 */
void
gst_buffer_replace_all_memory (GstBuffer * buffer, GstMemory * mem)
{
  gst_buffer_replace_memory_range (buffer, 0, -1, mem);
}

1.2.6 移除buffer中的mem(gst_buffer_remove_memory)

/**
 * gst_buffer_remove_memory:
 * @buffer: a #GstBuffer.
 * @idx: an index
 *
 * Removes the memory block in @b at index @i.
 */
void
gst_buffer_remove_memory (GstBuffer * buffer, guint idx)
{
  // 移除buffer中,从idx位置开始,长度为1的mem
  gst_buffer_remove_memory_range (buffer, idx, 1);
}

gst_buffer_remove_memory_range()的作用是,移除buffer中,从idx位置开始,长度为length的mem

/**
 * gst_buffer_remove_memory_range:
 * @buffer: a #GstBuffer.
 * @idx: an index
 * @length: a length
 *
 * Removes @length memory blocks in @buffer starting from @idx.
 *
 * @length can be -1, in which case all memory starting from @idx is removed.
 */
void
gst_buffer_remove_memory_range (GstBuffer * buffer, guint idx, gint length)
{
  guint len;

  g_return_if_fail (GST_IS_BUFFER (buffer));
  g_return_if_fail (gst_buffer_is_writable (buffer));

  GST_CAT_DEBUG (GST_CAT_BUFFER, "idx %u, length %d", idx, length);
  // 获取buffer中mem的长度
  len = GST_BUFFER_MEM_LEN (buffer);
  g_return_if_fail ((len == 0 && idx == 0 && length == -1) ||
      (length == -1 && idx < len) || length + idx <= len);

  if (length == -1)
    length = len - idx;
  // 将所有内存替换成为NULL
  _replace_memory (buffer, len, idx, length, NULL);
}

1.2.7 buffer中的mem映射(gst_buffer_map)

gst_buffer_map()的作用是,将buffer中的mem映射到map_info中,以便直接访问缓冲区中的数据

/**
 * gst_buffer_map:
 * @buffer: a #GstBuffer.
 * @info: (out caller-allocates): info about the mapping
 * @flags: flags for the mapping
 *
 * Fills @info with the #GstMapInfo of all merged memory blocks in @buffer.
 *
 * @flags describe the desired access of the memory. When @flags is
 * #GST_MAP_WRITE, @buffer should be writable (as returned from
 * gst_buffer_is_writable()).
 *
 * When @buffer is writable but the memory isn't, a writable copy will
 * automatically be created and returned. The readonly copy of the
 * buffer memory will then also be replaced with this writable copy.
 *
 * The memory in @info should be unmapped with gst_buffer_unmap() after
 * usage.
 *
 * Returns: %TRUE if the map succeeded and @info contains valid data.
 */
gboolean
gst_buffer_map (GstBuffer * buffer, GstMapInfo * info, GstMapFlags flags)
{
  // 将buffer中从0开始到最后位置的内存块合并,映射到info中
  // 如果flags包含GST_MAP_WRITE,并且buffer是可写的,则会确保映射内存可写
  // 如果内存不可写,则会创建一个可写的副本返回
  return gst_buffer_map_range (buffer, 0, -1, info, flags);
}

gst_buffer_map_range()的作用是,将buffer中从idx开始,长度为length的内存映射到info当中,这样可以直接访问缓冲区中的数据。

/**
 * gst_buffer_map_range:
 * @buffer: a #GstBuffer.
 * @idx: an index
 * @length: a length
 * @info: (out caller-allocates): info about the mapping
 * @flags: flags for the mapping
 *
 * Fills @info with the #GstMapInfo of @length merged memory blocks
 * starting at @idx in @buffer. When @length is -1, all memory blocks starting
 * from @idx are merged and mapped.
 *
 * @flags describe the desired access of the memory. When @flags is
 * #GST_MAP_WRITE, @buffer should be writable (as returned from
 * gst_buffer_is_writable()).
 *
 * When @buffer is writable but the memory isn't, a writable copy will
 * automatically be created and returned. The readonly copy of the buffer memory
 * will then also be replaced with this writable copy.
 *
 * The memory in @info should be unmapped with gst_buffer_unmap() after usage.
 *
 * Returns: %TRUE if the map succeeded and @info contains valid
 * data.
 */
gboolean
gst_buffer_map_range (GstBuffer * buffer, guint idx, gint length,
    GstMapInfo * info, GstMapFlags flags)
{
  GstMemory *mem, *nmem;
  gboolean write, writable;
  gsize len;

  g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
  g_return_val_if_fail (info != NULL, FALSE);
  len = GST_BUFFER_MEM_LEN (buffer);
  g_return_val_if_fail ((len == 0 && idx == 0 && length == -1) ||
      (length == -1 && idx < len) || (length > 0
          && length + idx <= len), FALSE);

  GST_CAT_LOG (GST_CAT_BUFFER, "buffer %p, idx %u, length %d, flags %04x",
      buffer, idx, length, flags);

  write = (flags & GST_MAP_WRITE) != 0;
  // 检查buffer是否可写
  writable = gst_buffer_is_writable (buffer);

  /* check if we can write when asked for write access */
  if (G_UNLIKELY (write && !writable))
    goto not_writable;

  if (length == -1)
    length = len - idx;
  // 将buffer中从idx开始,长度为length的mem合并,返回给mem
  mem = _get_merged_memory (buffer, idx, length);
  if (G_UNLIKELY (mem == NULL))
    goto no_memory;

  /* now try to map */
  // 尝试将mem中的信息映射到info当中,如果无法映射,
  // 会创建mem的副本,再映射
  nmem = gst_memory_make_mapped (mem, info, flags);
  if (G_UNLIKELY (nmem == NULL))
    goto cannot_map;

  /* if we merged or when the map returned a different memory, we try to replace
   * the memory in the buffer */
  // 如果映射的内存块是合并后的,或者映射返回了不同的内存块,尝试替换buffer中的内存块
  if (G_UNLIKELY (length > 1 || nmem != mem)) {
    /* if the buffer is writable, replace the memory */
    // 如果buffer可写,则替换内存块
    if (writable) {
      _replace_memory (buffer, len, idx, length, gst_memory_ref (nmem));
    } else { // 如果buffer不可写,记录调试信息,表示这是一个临时映射
      if (len > 1) {
        GST_CAT_DEBUG (GST_CAT_PERFORMANCE,
            "temporary mapping for memory %p in buffer %p", nmem, buffer);
      }
    }
  }
  return TRUE;

  /* ERROR */
not_writable:
  {
    GST_WARNING ("write map requested on non-writable buffer");
    g_critical ("write map requested on non-writable buffer");
    memset (info, 0, sizeof (GstMapInfo));
    return FALSE;
  }
no_memory:
  {
    /* empty buffer, we need to return NULL */
    GST_DEBUG ("can't get buffer memory");
    memset (info, 0, sizeof (GstMapInfo));
    return TRUE;
  }
cannot_map:
  {
    GST_DEBUG ("cannot map memory");
    memset (info, 0, sizeof (GstMapInfo));
    return FALSE;
  }
}

1.2.8 取消buffer中的mem映射(gst_buffer_unmap)

/**
 * gst_buffer_unmap:
 * @buffer: a #GstBuffer.
 * @info: a #GstMapInfo
 *
 * Releases the memory previously mapped with gst_buffer_map().
 */
void
gst_buffer_unmap (GstBuffer * buffer, GstMapInfo * info)
{
  g_return_if_fail (GST_IS_BUFFER (buffer));
  g_return_if_fail (info != NULL);

  _gst_buffer_map_info_clear ((GstBufferMapInfo *) info);
}

_gst_buffer_map_info_clear()的作用是清理buffer映射之后的info

static inline void _gst_buffer_map_info_clear(GstBufferMapInfo *info)
{
  /* we need to check for NULL, it is possible that we tried to map a buffer
   * without memory and we should be able to unmap that fine */
  if (G_LIKELY (info->memory)) {
    gst_memory_unmap (info->memory, info);
    gst_memory_unref (info->memory);
  }
}

1.2.9 合并两个buffer(gst_buffer_append)

/**
 * gst_buffer_append:
 * @buf1: (transfer full): the first source #GstBuffer to append.
 * @buf2: (transfer full): the second source #GstBuffer to append.
 *
 * Appends all the memory from @buf2 to @buf1. The result buffer will contain a
 * concatenation of the memory of @buf1 and @buf2.
 *
 * Returns: (transfer full): the new #GstBuffer that contains the memory
 *     of the two source buffers.
 */
GstBuffer *
gst_buffer_append (GstBuffer * buf1, GstBuffer * buf2)
{
  // 将buf2的完整内容追加到buf1的末尾,并返回一个新的buffer
  return gst_buffer_append_region (buf1, buf2, 0, -1);
}

gst_buffer_append_region()的作用是,将buf2中指定区域的数据追加到buf1的末尾,offset为buf2中提取数据的位置,size为要提取数据的大小,如果size = -1,表示提取从offset到buf2末尾的所有数据。

/**
 * gst_buffer_append_region:
 * @buf1: (transfer full): the first source #GstBuffer to append.
 * @buf2: (transfer full): the second source #GstBuffer to append.
 * @offset: the offset in @buf2
 * @size: the size or -1 of @buf2
 *
 * Appends @size bytes at @offset from @buf2 to @buf1. The result buffer will
 * contain a concatenation of the memory of @buf1 and the requested region of
 * @buf2.
 *
 * Returns: (transfer full): the new #GstBuffer that contains the memory
 *     of the two source buffers.
 */
GstBuffer *
gst_buffer_append_region (GstBuffer * buf1, GstBuffer * buf2, gssize offset,
    gssize size)
{
  gsize i, len;

  g_return_val_if_fail (GST_IS_BUFFER (buf1), NULL);
  g_return_val_if_fail (GST_IS_BUFFER (buf2), NULL);
  // 检查buf是否可写
  buf1 = gst_buffer_make_writable (buf1);
  buf2 = gst_buffer_make_writable (buf2);
  // 调整buf2的大小,使其只包含从offset开始的size字节数据
  // 这里可能会导致offset之前的数据丢失,为了保护这些数据
  // 可以在之前创建一个buf2的副本(gst_buffer_copy),或者
  // 手动提取指定区域的内容
  gst_buffer_resize (buf2, offset, size);
  // 计算buf2中mem的长度
  len = GST_BUFFER_MEM_LEN (buf2);
  // 遍历buf2中的mem,将其添加到buf1的末尾
  for (i = 0; i < len; i++) {
    GstMemory *mem;

    mem = GST_BUFFER_MEM_PTR (buf2, i);
    gst_mini_object_remove_parent (GST_MINI_OBJECT_CAST (mem),
        GST_MINI_OBJECT_CAST (buf2));
    GST_BUFFER_MEM_PTR (buf2, i) = NULL;
    _memory_add (buf1, -1, mem);
  }
  // 将buf2长度置零
  GST_BUFFER_MEM_LEN (buf2) = 0;
  // 设置flag,表示该缓冲区的内存已经被处理
  GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_TAG_MEMORY);
  // 减少引用
  gst_buffer_unref (buf2);

  return buf1;
}

1.2.10 元数据的处理

(1)buffer中添加meta(gst_buffer_add_meta)

/**
 * gst_buffer_add_meta:
 * @buffer: a #GstBuffer
 * @info: a #GstMetaInfo
 * @params: params for @info
 *
 * Adds metadata for @info to @buffer using the parameters in @params.
 *
 * Returns: (transfer none) (nullable): the metadata for the api in @info on @buffer.
 */
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
   */
  // 如果没有初始化函数,使用g_malloc()分配未初始化的内存
  if (!info->init_func)
    item = g_malloc0 (size);
  else
    item = g_malloc (size);
  result = &item->meta;
  result->info = info;
  result->flags = GST_META_FLAG_NONE;
  GST_CAT_DEBUG (GST_CAT_BUFFER,
      "alloc metadata %p (%s) of size %" G_GSIZE_FORMAT, result,
      g_type_name (info->type), info->size);

  /* call the init_func when needed */
  // 使用init_func()对元数据进行初始化
  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;
  // 如果buffer没有元数据,item设置为buffer第一个元数据项
  // 如果有元数据,将item添加到链表的末尾
  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;
  }

  return result;

init_failed:
  {
    g_free (item);
    return NULL;
  }
}

(2)buffer中删除指定meta(gst_buffer_remove_meta)

/**
 * gst_buffer_remove_meta:
 * @buffer: a #GstBuffer
 * @meta: a #GstMeta
 *
 * Removes the metadata for @meta on @buffer.
 *
 * Returns: %TRUE if the metadata existed and was removed, %FALSE if no such
 * metadata was on @buffer.
 */
gboolean
gst_buffer_remove_meta (GstBuffer * buffer, GstMeta * meta)
{
  GstMetaItem *walk, *prev;

  g_return_val_if_fail (buffer != NULL, FALSE);
  g_return_val_if_fail (meta != NULL, FALSE);
  // 检查buffer是否可写
  g_return_val_if_fail (gst_buffer_is_writable (buffer), FALSE);
  // 检查meta是否被锁定
  g_return_val_if_fail (!GST_META_FLAG_IS_SET (meta, GST_META_FLAG_LOCKED),
      FALSE);

  /* find the metadata and delete */
  // 遍历buffer的元素数据链表
  prev = GST_BUFFER_META (buffer);
  for (walk = prev; walk; walk = walk->next) {
    GstMeta *m = &walk->meta;
    if (m == meta) {
      const GstMetaInfo *info = meta->info;

      /* remove from list */
      // 如果目标元数据是链表最后一个节点,更新链表尾指针
      if (GST_BUFFER_TAIL_META (buffer) == walk) {
        if (prev != walk)
          GST_BUFFER_TAIL_META (buffer) = prev;
        else
          GST_BUFFER_TAIL_META (buffer) = NULL;
      }
	  // 如果目标元数据项时链表的第一个节点
      if (GST_BUFFER_META (buffer) == walk)
        GST_BUFFER_META (buffer) = walk->next;
      else
        prev->next = walk->next;

      /* call free_func if any */
      // 释放元数据
      if (info->free_func)
        info->free_func (m, buffer);

      /* and free the slice */
      g_free (walk);
      break;
    }
    prev = walk;
  }
  return walk != NULL;
}

(3)遍历buffer中的meta(gst_buffer_iterate_meta & gst_buffer_foreach_meta)

/**
 * gst_buffer_iterate_meta: (skip)
 * @buffer: a #GstBuffer
 * @state: (out caller-allocates): an opaque state pointer
 *
 * Retrieves the next #GstMeta after @current. If @state points
 * to %NULL, the first metadata is returned.
 *
 * @state will be updated with an opaque state pointer
 *
 * Returns: (transfer none) (nullable): The next #GstMeta or %NULL
 * when there are no more items.
 */
GstMeta *
gst_buffer_iterate_meta (GstBuffer * buffer, gpointer * state)
{
  GstMetaItem **meta;

  g_return_val_if_fail (buffer != NULL, NULL);
  g_return_val_if_fail (state != NULL, NULL);

  meta = (GstMetaItem **) state;
  if (*meta == NULL)
    /* state NULL, move to first item */
    *meta = GST_BUFFER_META (buffer);
  else
    /* state !NULL, move to next item in list */
    *meta = (*meta)->next;

  if (*meta)
    return &(*meta)->meta;
  else
    return NULL;
}

gst_buffer_iterate_meta()中是没有循环遍历的,需要外面加上一个循环,循环遍历的功能在gst_buffer_foreach_meta()中实现

/**
 * gst_buffer_foreach_meta:
 * @buffer: a #GstBuffer
 * @func: (scope call) (closure user_data): a #GstBufferForeachMetaFunc to call
 * @user_data: user data passed to @func
 *
 * Calls @func with @user_data for each meta in @buffer.
 *
 * @func can modify the passed meta pointer or its contents. The return value
 * of @func defines if this function returns or if the remaining metadata items
 * in the buffer should be skipped.
 *
 * Returns: %FALSE when @func returned %FALSE for one of the metadata.
 */
gboolean
gst_buffer_foreach_meta (GstBuffer * buffer, GstBufferForeachMetaFunc func,
    gpointer user_data)
{
  GstMetaItem *walk, *prev, *next;
  gboolean res = TRUE;

  g_return_val_if_fail (buffer != NULL, FALSE);
  g_return_val_if_fail (func != NULL, FALSE);

  /* find the metadata and delete */
  prev = GST_BUFFER_META (buffer);
  // 遍历buffer中metadata列表
  for (walk = prev; walk; walk = next) {
    GstMeta *m, *new;

    m = new = &walk->meta;
    next = walk->next;
    // 回调函数,用于处理每个元数据,例如打印函数print
    res = func (buffer, &new, user_data);
	// 如果元数据需要被删除,则从链表中移除该元数据
    if (new == NULL) {
      const GstMetaInfo *info = m->info;

      GST_CAT_DEBUG (GST_CAT_BUFFER, "remove metadata %p (%s)", m,
          g_type_name (info->type));

      g_return_val_if_fail (gst_buffer_is_writable (buffer), FALSE);
      g_return_val_if_fail (!GST_META_FLAG_IS_SET (m, GST_META_FLAG_LOCKED),
          FALSE);

      if (GST_BUFFER_TAIL_META (buffer) == walk) {
        if (prev != walk)
          GST_BUFFER_TAIL_META (buffer) = prev;
        else
          GST_BUFFER_TAIL_META (buffer) = NULL;
      }

      /* remove from list */
      if (GST_BUFFER_META (buffer) == walk)
        prev = GST_BUFFER_META (buffer) = next;
      else
        prev->next = next;

      /* call free_func if any */
      // 释放函数
      if (info->free_func)
        info->free_func (m, buffer);

      /* and free the slice */
      g_free (walk);
    } else {
      prev = walk;
    }
    if (!res)
      break;
  }
  return res;
}

(4)提取buffer信息,复制到新内存中(gst_buffer_extract_dup)
gst_buffer_extract_dup()的作用是,从buffer中提取从offset为偏移量开始,长度为size的信息,拷贝到dest中,实际存储的数据大小为dest_size

解释 /**
 * gst_buffer_extract_dup:
 * @buffer: a #GstBuffer
 * @offset: the offset to extract
 * @size: the size to extract
 * @dest: (array length=dest_size) (element-type guint8) (out): A pointer where
 *  the destination array will be written. Might be %NULL if the size is 0.
 * @dest_size: (out): A location where the size of @dest can be written
 *
 * Extracts a copy of at most @size bytes the data at @offset into
 * newly-allocated memory. @dest must be freed using g_free() when done.
 *
 * Since: 1.0.10
 */

void
gst_buffer_extract_dup (GstBuffer * buffer, gsize offset, gsize size,
    gpointer * dest, gsize * dest_size)
{
  gsize real_size, alloc_size;
  // 获取buffer的总数据大小
  real_size = gst_buffer_get_size (buffer);
  // 可提取的数据大小
  alloc_size = MIN (real_size - offset, size);
  // 可提取数据大小为0,则dest为NULL
  if (alloc_size == 0) {
    *dest = NULL;
    *dest_size = 0;
  } else { // 分配大小为alloc_size的内存,将信息拷贝到dest中,并计算dest_size
    *dest = g_malloc (alloc_size);
    *dest_size = gst_buffer_extract (buffer, offset, *dest, size);
  }
}

(5)添加parent buffer meta(gst_buffer_add_parent_buffer_meta)
gst_buffer_add_parent_buffer_meta()用于在buffer中添加另一个parent buffer meta元数据,从而获得对另一个buffer的引用。这种机制常用于管理缓冲区之间的依赖关系,确保被引用的缓冲区不会被提前释放

/**
 * gst_buffer_add_parent_buffer_meta:
 * @buffer: (transfer none): a #GstBuffer
 * @ref: (transfer none): a #GstBuffer to ref
 *
 * Adds a #GstParentBufferMeta to @buffer that holds a reference on
 * @ref until the buffer is freed.
 *
 * Returns: (transfer none) (nullable): The #GstParentBufferMeta that was added to the buffer
 *
 * Since: 1.6
 */
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;
}

1.2.11 拷贝buffer(gst_buffer_copy)

gst_buffer_copy()是buffer的一个浅拷贝,会共享内存区域

/**
 * gst_buffer_copy: (skip)
 * @buf: a #GstBuffer.
 *
 * Creates a copy of the given buffer. This will only copy the buffer's
 * data to a newly allocated memory if needed (if the type of memory
 * requires it), otherwise the underlying data is just referenced.
 * Check gst_buffer_copy_deep() if you want to force the data
 * to be copied to newly allocated memory.
 *
 * Returns: (transfer full) (nullable): a new copy of @buf if the copy succeeded, %NULL otherwise.
 */
GstBuffer *
gst_buffer_copy (const GstBuffer * buf)
{
  return GST_BUFFER (gst_mini_object_copy (GST_MINI_OBJECT_CONST_CAST (buf)));
}

如果想要使用深度拷贝,函数为gst_buffer_copy_deep()

/**
 * gst_buffer_copy_deep:
 * @buf: a #GstBuffer.
 *
 * Creates a copy of the given buffer. This will make a newly allocated
 * copy of the data the source buffer contains.
 *
 * Returns: (transfer full) (nullable): a new copy of @buf if the copy succeeded, %NULL otherwise.
 *
 * Since: 1.6
 */
GstBuffer *
gst_buffer_copy_deep (const GstBuffer * buffer)
{
  return gst_buffer_copy_with_flags (buffer,
      GST_BUFFER_COPY_ALL | GST_BUFFER_COPY_DEEP);
}

gst_buffer_copy_with_flags()为带标签的拷贝函数

static GstBuffer *
gst_buffer_copy_with_flags (const GstBuffer * buffer, GstBufferCopyFlags flags)
{
  GstBuffer *copy;

  g_return_val_if_fail (buffer != NULL, NULL);

  /* create a fresh new buffer */
  // 创建新的空缓冲区copy
  copy = gst_buffer_new ();

  /* copy what the 'flags' want from our parent */
  /* FIXME why we can't pass const to gst_buffer_copy_into() ? */
  // 拷贝缓冲区数据,如果失败则置为NULL
  if (!gst_buffer_copy_into (copy, (GstBuffer *) buffer, flags, 0, -1))
    gst_buffer_replace (&copy, NULL);

  if (copy)
    GST_BUFFER_FLAG_UNSET (copy, GST_BUFFER_FLAG_TAG_MEMORY);

  return copy;
}

gst_buffer_copy_into()是buffer拷贝的核心函数,执行具体的拷贝操作

/**
 * gst_buffer_copy_into:
 * @dest: a destination #GstBuffer
 * @src: a source #GstBuffer
 * @flags: flags indicating what metadata fields should be copied.
 * @offset: offset to copy from
 * @size: total size to copy. If -1, all data is copied.
 *
 * Copies the information from @src into @dest.
 *
 * If @dest already contains memory and @flags contains GST_BUFFER_COPY_MEMORY,
 * the memory from @src will be appended to @dest.
 *
 * @flags indicate which fields will be copied.
 *
 * Returns: %TRUE if the copying succeeded, %FALSE otherwise.
 */
gboolean
gst_buffer_copy_into (GstBuffer * dest, GstBuffer * src,
    GstBufferCopyFlags flags, gsize offset, gsize size)
{
  GstMetaItem *walk;
  gsize bufsize;
  gboolean region = FALSE;
  gboolean sharing_mem = FALSE;

  g_return_val_if_fail (dest != NULL, FALSE);
  g_return_val_if_fail (src != NULL, FALSE);

  /* nothing to copy if the buffers are the same */
  if (G_UNLIKELY (dest == src))
    return TRUE;

  g_return_val_if_fail (gst_buffer_is_writable (dest), FALSE);
  // 计算源buffer的大小
  bufsize = gst_buffer_get_size (src);
  g_return_val_if_fail (bufsize >= offset, FALSE);
  if (offset > 0)
    region = TRUE;
  if (size == -1)
    size = bufsize - offset;
  if (size < bufsize)
    region = TRUE;
  g_return_val_if_fail (bufsize >= offset + size, FALSE);

  GST_CAT_LOG (GST_CAT_BUFFER, "copy %p to %p, offset %" G_GSIZE_FORMAT
      "-%" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT, src, dest, offset, size,
      bufsize);
  // 拷贝标志位
  if (flags & GST_BUFFER_COPY_FLAGS) {
    /* copy flags */
    guint flags_mask = ~GST_BUFFER_FLAG_TAG_MEMORY;

    GST_MINI_OBJECT_FLAGS (dest) =
        (GST_MINI_OBJECT_FLAGS (src) & flags_mask) |
        (GST_MINI_OBJECT_FLAGS (dest) & ~flags_mask);
  }
  // 拷贝时间戳和偏移量
  if (flags & GST_BUFFER_COPY_TIMESTAMPS) {
    if (offset == 0) {
      GST_BUFFER_PTS (dest) = GST_BUFFER_PTS (src);
      GST_BUFFER_DTS (dest) = GST_BUFFER_DTS (src);
      GST_BUFFER_OFFSET (dest) = GST_BUFFER_OFFSET (src);
      if (size == bufsize) {
        GST_BUFFER_DURATION (dest) = GST_BUFFER_DURATION (src);
        GST_BUFFER_OFFSET_END (dest) = GST_BUFFER_OFFSET_END (src);
      }
    } else {
      GST_BUFFER_PTS (dest) = GST_CLOCK_TIME_NONE;
      GST_BUFFER_DTS (dest) = GST_CLOCK_TIME_NONE;
      GST_BUFFER_DURATION (dest) = GST_CLOCK_TIME_NONE;
      GST_BUFFER_OFFSET (dest) = GST_BUFFER_OFFSET_NONE;
      GST_BUFFER_OFFSET_END (dest) = GST_BUFFER_OFFSET_NONE;
    }
  }
  // 拷贝内存数据
  if (flags & GST_BUFFER_COPY_MEMORY) {
    gsize skip, left, len, dest_len, i, bsize;
    gboolean deep;
    // 是否是深度拷贝
    deep = flags & GST_BUFFER_COPY_DEEP;

    len = GST_BUFFER_MEM_LEN (src);
    dest_len = GST_BUFFER_MEM_LEN (dest);
    left = size;
    skip = offset;

    /* copy and make regions of the memory */
    // 遍历buffer中的mem
    for (i = 0; i < len && left > 0; i++) {
      GstMemory *mem = GST_BUFFER_MEM_PTR (src, i);

      bsize = mem->size;

      if (bsize <= skip) {
        /* don't copy buffer */
        skip -= bsize;
      } else {
        GstMemory *newmem = NULL;
        gsize tocopy;

        tocopy = MIN (bsize - skip, left);

        if (tocopy < bsize && !deep && !GST_MEMORY_IS_NO_SHARE (mem)) {
          /* we need to clip something */
          newmem = gst_memory_share (mem, skip, tocopy);
          if (newmem) {
            gst_memory_lock (newmem, GST_LOCK_FLAG_EXCLUSIVE);
            skip = 0;
          }
        }

        if (deep || GST_MEMORY_IS_NO_SHARE (mem) || (!newmem && tocopy < bsize)) {
          /* deep copy or we're not allowed to share this memory
           * between buffers, always copy then */
          newmem = gst_memory_copy (mem, skip, tocopy);
          if (newmem) {
            gst_memory_lock (newmem, GST_LOCK_FLAG_EXCLUSIVE);
            skip = 0;
          }
        } else if (!newmem) {
          newmem = _memory_get_exclusive_reference (mem);
        }

        if (!newmem) {
          gst_buffer_remove_memory_range (dest, dest_len, -1);
          return FALSE;
        }

        /* Indicates if dest references any of src memories. */
        sharing_mem |= (newmem == mem);

        _memory_add (dest, -1, newmem);
        left -= tocopy;
      }
    }
    // 是否需要将目标缓冲区的内存合并成为一个连续的内存块
    if (flags & GST_BUFFER_COPY_MERGE) {
      GstMemory *mem;

      len = GST_BUFFER_MEM_LEN (dest);
      mem = _get_merged_memory (dest, 0, len);
      if (!mem) {
        gst_buffer_remove_memory_range (dest, dest_len, -1);
        return FALSE;
      }

      /* If we were sharing memory and the merge is no-op, we are still sharing. */
      sharing_mem &= (mem == GST_BUFFER_MEM_PTR (dest, 0));

      _replace_memory (dest, len, 0, len, mem);
    }
  }
  // 拷贝元数据
  if (flags & GST_BUFFER_COPY_META) {
    gboolean deep;

    deep = (flags & GST_BUFFER_COPY_DEEP) != 0;

    /* NOTE: GstGLSyncMeta copying relies on the meta
     *       being copied now, after the buffer data,
     *       so this has to happen last */
    for (walk = GST_BUFFER_META (src); walk; walk = walk->next) {
      GstMeta *meta = &walk->meta;
      const GstMetaInfo *info = meta->info;

      /* Don't copy memory metas if we only copied part of the buffer, didn't
       * copy memories or merged memories. In all these cases the memory
       * structure has changed and the memory meta becomes meaningless.
       */
      if ((region || !(flags & GST_BUFFER_COPY_MEMORY)
              || (flags & GST_BUFFER_COPY_MERGE))
          && gst_meta_api_type_has_tag (info->api, _gst_meta_tag_memory)) {
        GST_CAT_DEBUG (GST_CAT_BUFFER,
            "don't copy memory meta %p of API type %s", meta,
            g_type_name (info->api));
      } else if (deep && gst_meta_api_type_has_tag (info->api,
              _gst_meta_tag_memory_reference)) {
        GST_CAT_DEBUG (GST_CAT_BUFFER,
            "don't copy memory reference meta %p of API type %s", meta,
            g_type_name (info->api));
      } else if (info->transform_func) {
        GstMetaTransformCopy copy_data;

        copy_data.region = region;
        copy_data.offset = offset;
        copy_data.size = size;

        if (!info->transform_func (dest, meta, src,
                _gst_meta_transform_copy, &copy_data)) {
          GST_CAT_ERROR (GST_CAT_BUFFER,
              "failed to copy meta %p of API type %s", meta,
              g_type_name (info->api));
        }
      }
    }
  }

  if (sharing_mem && src->pool != NULL) {
    /* The new buffer references some of src's memories. We have to ensure that
     * src buffer does not return to its buffer pool as long as its memories are
     * used by other buffers. That would cause the buffer to be discarted by the
     * pool because its memories are not writable. */
    gst_buffer_add_parent_buffer_meta (dest, src);
  }

  return TRUE;
}

1.2.12 清除buffer(gst_clear_buffer)

/**
 * gst_clear_buffer: (skip)
 * @buf_ptr: a pointer to a #GstBuffer reference
 *
 * Clears a reference to a #GstBuffer.
 *
 * @buf_ptr must not be %NULL.
 *
 * If the reference is %NULL then this function does nothing. Otherwise, the
 * reference count of the buffer is decreased and the pointer is set to %NULL.
 *
 * Since: 1.16
 */
void
gst_clear_buffer (GstBuffer ** buf_ptr)
{
  gst_clear_mini_object ((GstMiniObject **) buf_ptr);
}

http://www.kler.cn/a/525489.html

相关文章:

  • github制作静态网页
  • EasyExcel写入和读取多个sheet
  • 17、Spring MVC 框架:构建强大的 Java Web 应用程序
  • 蓝牙技术在物联网中的应用有哪些
  • openeuler 22.03 lts sp4 使用 cri-o 和 静态 pod 的方式部署 k8s-v1.32.0 高可用集群
  • 渗透测试之WAF规则触发绕过规则之规则库绕过方式
  • 10.7 获得程序版本信息
  • 【DeepSeek】LLM强化学习GRPO Trainer详解
  • Baklib在知识管理效率提升中的独特价值与其他产品的比较探析
  • RocketMQ 中如何实现消息的可靠传递?
  • C++,STL 简介:历史、组成、优势
  • 9.1 LangChain深度解析:大模型应用开发的“万能胶水”与核心架构设计
  • 数论问题77一一3x+1问题
  • 【deepseek实战】绿色好用,不断网
  • UE5制作视差图
  • 热更新杂乱记
  • Android车机DIY开发之学习篇(七)NDK交叉工具构建
  • 数据结构---哈希表
  • Linux - 常用的I/O 多路复用技术 select, poll, epoll
  • PyTorch 与 Python 版本对应关系
  • hive:基本数据类型,关于表和列语法
  • Unity敌人逻辑笔记
  • 推动知识共享的在线知识库实施与优化指南
  • java实现mysql数据库备份还原定时删除过期备份文件
  • JavaScript图像处理,JavaScript实现高斯滤波图像处理算法
  • http://noi.openjudge.cn/——4.2算法之数论——2419:Coins