美文网首页
webrtc 内存缓存池

webrtc 内存缓存池

作者: gykimo | 来源:发表于2021-05-08 10:31 被阅读0次

    用途

    内存缓存池,主要是解决频繁malloc和free内存时,会造成很大的性能开销;而且长时间运行后,会造成很多的内存碎片,运行时间越长,malloc和free的时间越长。
    缓存池就是提前分配一些内存,使用的时候,直接从缓存池中获取,这样可以节省malloc和free的时间,而且很多时候(甚至大部分时候)free的耗时会高于malloc。

    代码分析

    webrtc的内存缓存池实现得还是很简单的一种。
    它主要解决图像的缓存问题,而且为了简单,一个pool仅支持相同大小的图像。
    我们主要看CreateI420Buffer的实现,先判断下是否有空闲的内存使用(通过引用计数),如果有直接返回空闲的,如果没有就创建一个,然后将其插入到内存list缓存中,用于之后使用。

    其他注意点

    1. 可以控制缓存池的最大数量,max_number_of_buffers_,如果缓存池满了,那么CreateI420Buffer可能会返回null。就如malloc可能返回null一样。

    源码

    https://webrtc.googlesource.com/src/+/refs/heads/master/common_video/video_frame_buffer_pool.cc

    #include "common_video/include/video_frame_buffer_pool.h"
    
    #include <limits>
    
    #include "rtc_base/checks.h"
    
    namespace webrtc {
    
    namespace {
    bool HasOneRef(const rtc::scoped_refptr<VideoFrameBuffer>& buffer) {
      // Cast to rtc::RefCountedObject is safe because this function is only called
      // on locally created VideoFrameBuffers, which are either
      // |rtc::RefCountedObject<I420Buffer>| or |rtc::RefCountedObject<NV12Buffer>|.
      switch (buffer->type()) {
        case VideoFrameBuffer::Type::kI420: {
          return static_cast<rtc::RefCountedObject<I420Buffer>*>(buffer.get())
              ->HasOneRef();
        }
        case VideoFrameBuffer::Type::kNV12: {
          return static_cast<rtc::RefCountedObject<NV12Buffer>*>(buffer.get())
              ->HasOneRef();
        }
        default:
          RTC_NOTREACHED();
      }
      return false;
    }
    
    }  // namespace
    
    VideoFrameBufferPool::VideoFrameBufferPool() : VideoFrameBufferPool(false) {}
    
    VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize)
        : VideoFrameBufferPool(zero_initialize,
                               std::numeric_limits<size_t>::max()) {}
    
    VideoFrameBufferPool::VideoFrameBufferPool(bool zero_initialize,
                                               size_t max_number_of_buffers)
        : zero_initialize_(zero_initialize),
          max_number_of_buffers_(max_number_of_buffers) {}
    
    VideoFrameBufferPool::~VideoFrameBufferPool() = default;
    
    void VideoFrameBufferPool::Release() {
      buffers_.clear();
    }
    
    bool VideoFrameBufferPool::Resize(size_t max_number_of_buffers) {
      RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
      size_t used_buffers_count = 0;
      for (const rtc::scoped_refptr<VideoFrameBuffer>& buffer : buffers_) {
        // If the buffer is in use, the ref count will be >= 2, one from the list we
        // are looping over and one from the application. If the ref count is 1,
        // then the list we are looping over holds the only reference and it's safe
        // to reuse.
        if (!HasOneRef(buffer)) {
          used_buffers_count++;
        }
      }
      if (used_buffers_count > max_number_of_buffers) {
        return false;
      }
      max_number_of_buffers_ = max_number_of_buffers;
    
      size_t buffers_to_purge = buffers_.size() - max_number_of_buffers_;
      auto iter = buffers_.begin();
      while (iter != buffers_.end() && buffers_to_purge > 0) {
        if (HasOneRef(*iter)) {
          iter = buffers_.erase(iter);
          buffers_to_purge--;
        } else {
          ++iter;
        }
      }
      return true;
    }
    
    rtc::scoped_refptr<I420Buffer> VideoFrameBufferPool::CreateI420Buffer(
        int width,
        int height) {
      RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
    
      rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
          GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI420);
      if (existing_buffer) {
        // Cast is safe because the only way kI420 buffer is created is
        // in the same function below, where |RefCountedObject<I420Buffer>| is
        // created.
        rtc::RefCountedObject<I420Buffer>* raw_buffer =
            static_cast<rtc::RefCountedObject<I420Buffer>*>(existing_buffer.get());
        // Creates a new scoped_refptr, which is also pointing to the same
        // RefCountedObject as buffer, increasing ref count.
        return rtc::scoped_refptr<I420Buffer>(raw_buffer);
      }
    
      if (buffers_.size() >= max_number_of_buffers_)
        return nullptr;
      // Allocate new buffer.
      rtc::scoped_refptr<I420Buffer> buffer =
          rtc::make_ref_counted<I420Buffer>(width, height);
    
      if (zero_initialize_)
        buffer->InitializeData();
    
      buffers_.push_back(buffer);
      return buffer;
    }
    
    rtc::scoped_refptr<NV12Buffer> VideoFrameBufferPool::CreateNV12Buffer(
        int width,
        int height) {
      RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
    
      rtc::scoped_refptr<VideoFrameBuffer> existing_buffer =
          GetExistingBuffer(width, height, VideoFrameBuffer::Type::kNV12);
      if (existing_buffer) {
        // Cast is safe because the only way kI420 buffer is created is
        // in the same function below, where |RefCountedObject<I420Buffer>| is
        // created.
        rtc::RefCountedObject<NV12Buffer>* raw_buffer =
            static_cast<rtc::RefCountedObject<NV12Buffer>*>(existing_buffer.get());
        // Creates a new scoped_refptr, which is also pointing to the same
        // RefCountedObject as buffer, increasing ref count.
        return rtc::scoped_refptr<NV12Buffer>(raw_buffer);
      }
    
      if (buffers_.size() >= max_number_of_buffers_)
        return nullptr;
      // Allocate new buffer.
      rtc::scoped_refptr<NV12Buffer> buffer =
          rtc::make_ref_counted<NV12Buffer>(width, height);
    
      if (zero_initialize_)
        buffer->InitializeData();
    
      buffers_.push_back(buffer);
      return buffer;
    }
    
    rtc::scoped_refptr<VideoFrameBuffer> VideoFrameBufferPool::GetExistingBuffer(
        int width,
        int height,
        VideoFrameBuffer::Type type) {
      // Release buffers with wrong resolution or different type.
      for (auto it = buffers_.begin(); it != buffers_.end();) {
        const auto& buffer = *it;
        if (buffer->width() != width || buffer->height() != height ||
            buffer->type() != type) {
          it = buffers_.erase(it);
        } else {
          ++it;
        }
      }
      // Look for a free buffer.
      for (const rtc::scoped_refptr<VideoFrameBuffer>& buffer : buffers_) {
        // If the buffer is in use, the ref count will be >= 2, one from the list we
        // are looping over and one from the application. If the ref count is 1,
        // then the list we are looping over holds the only reference and it's safe
        // to reuse.
        if (HasOneRef(buffer)) {
          RTC_CHECK(buffer->type() == type);
          return buffer;
        }
      }
      return nullptr;
    }
    
    }  // namespace webrtc
    

    相关文章

      网友评论

          本文标题:webrtc 内存缓存池

          本文链接:https://www.haomeiwen.com/subject/pbyedltx.html