零拷贝缓冲 · Buffer 与 CopyOnWriteBuffer

一个 RTP 包在系统里要流经打包、加密、pacing、发送好几站,每站都复制一份负载会很浪费。这一章读 buffer.h / copy_on_write_buffer.h,讲清 WebRTC 怎么用引用计数实现「写时复制」缓冲,让数据在多个环节间零拷贝地共享。

作者 David更新于 第 96 篇(共 112 篇)

零拷贝缓冲 · Buffer 与 CopyOnWriteBuffer

一个 RTP 包从生成到发出,要经过打包 → 加密 → pacing → 发送好几站,还可能被缓存起来等重传。如果每站都把负载(可能上千字节)复制一份,开销会很可观。WebRTC 用写时复制缓冲解决——这一章看它,它正好是上一章引用计数的一个漂亮应用。

Buffer:WebRTC 的字节缓冲

先看最基础的 Buffer(buffer.h)——一个可增长的字节数组,类似 std::vector<uint8_t>,但为媒体数据的用法调过:管理一块连续内存、能 append、能 resize、避免不必要的清零。WebRTC 里包、帧、编码数据的原始字节,底层多是它。这没什么特别,特别的是下一个。

CopyOnWriteBuffer:共享,直到有人要改

真正体现设计的是 CopyOnWriteBuffer。看它的成员(真实源码 copy_on_write_buffer.h):

// copy_on_write_buffer.h(节选,真实源码)
class CopyOnWriteBuffer {
  // ...
  void EnsureCapacity(size_t capacity);
  void UnshareAndEnsureCapacity(size_t new_capacity);   // 写前:若共享则先复制
 private:
  scoped_refptr<RefCountedBuffer> buffer_;    // 注意:引用计数的缓冲!
};

关键就在 buffer_ 的类型:scoped_refptr<RefCountedBuffer>——底层字节被引用计数的对象持有。

CopyOnWriteBuffer 复制时只复制一个引用计数指针,不复制底层字节**——多个 CopyOnWriteBuffer 可以共享同一块数据。只有当某个持有者要修改内容时,才真正复制一份(UnshareAndEnsureCapacity),这就是“写时复制”(Copy-on-Write)。** 工作机制:

  • 拷贝 = 共享 —— 把一个 CopyOnWriteBuffer 传给下一站,只是 scoped_refptr 计数 +1,两者指向同一块字节,零拷贝。
  • 只读 = 一直共享 —— 只要没人改,大家一直共用一份。加密前只读地传、缓存等重传只读地存,全程不复制。
  • 写 = 这时才复制 —— 某站要改内容时,先检查“是不是还有别人共享这块”(计数 > 1?),是就复制出私有的一份再改(Unshare),不影响别人。

于是常见的“只读传递”路径完全零拷贝,只有真正要改的地方才付复制代价。这对一个包流经多站的场景是巨大的节省——数据在 pacer 队列、重传缓存、发送路径间流转,大多是“传引用、不动内容”,COW 让这些传递几乎免费。

为什么这对 WebRTC 特别重要

写时复制不是 WebRTC 独有的概念(很多字符串库也这么干),但它在 WebRTC 里特别值:

图表加载中…

一个媒体包天然会被多方同时引用——发出去一份、重传缓存留一份、统计看一眼。若都深拷贝,高码率视频下每秒成百上千个包、每个上千字节,复制开销会吃掉可观的 CPU 和内存带宽。COW 把这些“同一份数据的多个引用”变成真正的共享,是实时媒体处理的性能关键之一。这也再次说明上一章的引用计数不只管“对象生死”,还能拿来做“数据共享”——CopyOnWriteBuffer = 引用计数 + 写时复制。

小结与下一章

Buffer(buffer.h)是 WebRTC 的基础字节缓冲(类 vector<uint8_t>,为媒体调优)。CopyOnWriteBuffer 是重点:内部持 scoped_refptr<RefCountedBuffer>(引用计数的底层字节),拷贝只复制指针 + 计数、共享同一份数据,只有要修改时才真复制(UnshareAndEnsureCapacity)——写时复制。这让一个包流经打包 / 加密 / pacing / 重传缓存的“只读传递”路径零拷贝,只在真要改处付代价,是高码率媒体处理的性能关键。它是引用计数用于“数据共享”的漂亮范例。

零拷贝解决了“数据怎么高效流动”,但多线程访问共享数据还得同步。下一章看 WebRTC 的锁与线程注解。👉 同步与线程注解。

参考资料

  • [1] libwebrtc 源码.rtc_base/buffer.h、rtc_base/copy_on_write_buffer.h(CopyOnWriteBuffer、UnshareAndEnsureCapacity、scoped_refptr<RefCountedBuffer>)。
  • [2] 关联引用计数、RTP 包、NACK 重传缓存。
  • [3] libwebrtc 源码.rtc_base/zero_memory.h 等配套。

本页目录