手把手教你实现buffer(四)——webrtc中的CopyOnWriteBuffer


前面的三篇文章介绍了 Buffer实现原理,简单的来说, Buffer就像一个 vector<uint8_t>,自动管理内存,自动扩容,相比 vector<uint8_t>它提供了更易用的写入,读取数据的接口。它可以作为一个基本 Buffer,在它的基础上构建功能更多丰富的buffer类。

在webrtc中有一个CopyOnWriteBuffer类,它就是基于Buffer实现了一个写时复制功能的buffer。

功能

写时复制指在改变内容时,产生一个buffer的拷贝,在拷贝上更改内容,而不影响原buffer的内容。这是CopyOnWriteBuffer的核心特性。它还具有如下几个特性:

  1. 它是个动态buffer,有size和容量的概念,容量大于size。容量变大后就不会减小(基于Buffer)
  2. 该对象支持拷贝语义,移动语义(基于Buffer对象)
  3. 拷贝语义,是让多个CopyOnwrite对象共享内存(Buffer对象)
  4. 当调用改动接口时,则会生成buffer的拷贝(产生新的Buffer对象)

作用

在流媒体,实时音视频这类应用中有个基本流程就是收媒体流,处理,再发送媒体流:

  • 在收流时,对媒体数据都是读操作,那么使用CopyOnWriteBuffer就很适合,将媒体数据写入CopyOnWirteBuffer对象,将以值语义传递,内部的Buffer并不会拷贝,这样比使用裸指针更方便,因为CopyOnWriteBuffer就提供了多种操作/读取数据的接口。
  • 在处理时,比如媒体流转发应用,通常只需要在rtp包中更改某些字段,再转发出去。那么将rtp包放入CopyOnWriteBuffer中,在更改时,自动copy一份rtp包。这样比直接使用裸指针更方便。

实现

内部buffer是一个Buffer类型shared_ptr指针,表述它的语义就是可以共享_buffer

//是一个Buffer对象
std::shared_ptr<Buffer> _buffer;

复制语义

CopyOnWriteBuffer支持复制语义,所以有复制构造函数,赋值操作符。

//复制构造函数
CopyOnWriteBuffer(const CopyOnWriteBuffer& buf);
//赋值操作符
CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf);

复制语义的作用就是让多个CopyOnWriteBuffer对象共享_buffer

CopyOnWriteBuffer::CopyOnWriteBuffer(const CopyOnWriteBuffer& buf)
    : _buffer(buf._buffer), _offset(buf._offset), _size(buf._size) {
    
    }

赋值操作符,也是如此:

CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) {
    
    
        assert(IsConsistent());
        assert(buf.IsConsistent());
        if (&buf != this) {
    
    
            _buffer = buf._buffer;
            _offset = buf._offset;
            _size = buf._size;
        }

        return *this;
    }

移动语义

CopyOnWriteBuffer定义有移动构造函数和**移动赋值函数。 **通过std::move_buffer的所有权交给另外一个CopyOnWriteBuffer对象。

CopyOnWriteBuffer::CopyOnWriteBuffer(CopyOnWriteBuffer&& buf)
    : _buffer(std::move(buf._buffer)), _offset(buf._offset), _size(buf._size) {
    
    
    buf._offset = 0;
    buf._size = 0;
    assert(IsConsistent());
}

移动赋值操作符也是如此:

CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) {
    
    
    assert(IsConsistent());
    assert(buf.IsConsistent());
    _buffer = std::move(buf._buffer);
    _offset = buf._offset;
    _size = buf._size;
    buf._offset = 0;
    buf._size = 0;
    return *this;
}

写时复制

CopyOnWriteBuffer核心特性就是写时复制,多个CopyOnWriteBuffer对象共享一个 Buffer,当某个对象改动Buffer时,就会对这个对象产生一个新的Buffer。涉及到修改Buffer的接口如下:

  • void AppendData(const uint8_t*data, size_t size)
  • uint8_t& operator[](size_t index)
  • void SetSize(size_t size)
  • void SetData(const uint8_t*data, size_t size)

前三个方法都会调用UnshareAndEnsureCapacity方法,通过它来确定是否产生新的Buffer。

UnshareAndEnsureCapacity 方法

如函数名表示的意思,它有两个逻辑 :如果_buffer被共享则产生一个新的_buffer对象;如果没有被共享,则通过capacity来确认是否需要产生一个新的_buffer对象。

void CopyOnWriteBuffer::UnshareAndEnsureCapacity(size_t new_capacity) {
    
    
    if (_buffer.unique() && new_capacity <= capacity()) {
    
    
        return;
    }

    _buffer.reset(new Buffer(_buffer->data() + _offset, _size, new_capacity));
    _offset = 0;
    assert(IsConsistent());
}

写时复制的一个例子

下面这个例子是使用AppendData的例子

const uint8_t kTestData[] = {
    
    0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf};

//共享
CopyOnWriteBuffer buf23(kTestData,3,10);
CopyOnWriteBuffer buf24(buf23);

const uint8_t* d1 = buf23.cdata();
const uint8_t* d2 = buf24.cdata();
std::cout<<"before append data d1 == d2 "<<(d1 == d2)<<",buf23 size "<<buf23.size()<<",buf24 size "<<buf24.size()<<",buf23 c "<<buf23.capacity()<<",buf24 c "<<buf24.capacity()<<std::endl;
    
uint8_t moreData[] = {
    
    17,18,19};

//buf24 调用Append时,会产生一个新的buffer
buf24.AppendData(moreData,3);
   
std::cout<<"after append data,buf23 size "<<buf23.size()<<",buf24 size "<<buf24.size()<<",buf23 c "<<buf23.capacity()<<",buf24 c "<<buf24.capacity()<<std::endl;
    
const uint8_t* d3 = buf24.cdata();
std::cout<<"d1 == d3 "<<(d1 == d3)<<std::endl;

//打印buf23的内容:1,2,3
std::cout<<"print buf23 data:";
for (size_t i=0; i<buf23.size(); ++i) {
    
    
    std::cout<<static_cast<int>(buf23[i])<<" ";
}

std::cout<<std::endl;

//打印buf24的内容:1,2,3,17,18,19
std::cout<<"print buf24 data:";
for (size_t i=0; i<buf24.size(); ++i) {
    
    
    std::cout<<static_cast<int>(buf24[i])<<" ";
}

std::cout<<std::endl;

slice方法

用于对CopyOnWriteBuffer对象进行分片,产生的一个从原对象offset处,sizelength,新的CopyOnWriteBuffer对象,它与原对象共享_buffer

CopyOnWriteBuffer Slice(size_t offset, size_t length) const {
    
    
    CopyOnWriteBuffer slice(*this);
    slice._offset += offset;
    slice._size = length;
    return slice;
}

示例代码

const uint8_t kTestData[] = {
    
    0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf};

CopyOnWriteBuffer ssbuf(kTestData,10,10);
//slice并不导致产生新buffer
CopyOnWriteBuffer sslice = ssbuf.Slice(0,3);
//sslice[0]改变了,将产生一个新的buffer
sslice[0] = 0xaa;
//sslice[0]的值为170
std::cout<<"sslice index 0 "<<static_cast<int>(*sslice.cdata())<<std::endl;
//ssbuf[0]的值为1
std::cout<<"ssbuf index 0 "<<static_cast<int>(*ssbuf.cdata())<<std::endl;
std::cout<<"sslic data == ssbuf data "<<(sslice.cdata() == ssbuf.cdata())<<std::endl;

总结

CopyOnWriteBuffer的实现基于Buffer,在功能上强于Buffer。我们也可以根据业务的需求,基于Buffer封装自己的Buffer类。

猜你喜欢

转载自blog.csdn.net/mo4776/article/details/127646579