blob: 3d3451265c2c37f19eb83fc0ebcf45b95d411b9f [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/cdm/ppapi/ppapi_cdm_buffer.h"
#include <algorithm>
#include "ppapi/cpp/core.h"
#include "ppapi/cpp/module.h"
namespace media {
// static
PpbBuffer* PpbBuffer::Create(const pp::Buffer_Dev& buffer,
uint32_t buffer_id,
PpbBufferAllocator* allocator) {
PP_DCHECK(buffer.data());
PP_DCHECK(buffer.size());
PP_DCHECK(buffer_id);
PP_DCHECK(allocator);
return new PpbBuffer(buffer, buffer_id, allocator);
}
void PpbBuffer::Destroy() {
delete this;
}
uint32_t PpbBuffer::Capacity() const {
return buffer_.size();
}
uint8_t* PpbBuffer::Data() {
return static_cast<uint8_t*>(buffer_.data());
}
void PpbBuffer::SetSize(uint32_t size) {
PP_DCHECK(size <= Capacity());
if (size > Capacity()) {
size_ = 0;
return;
}
size_ = size;
}
pp::Buffer_Dev PpbBuffer::TakeBuffer() {
PP_DCHECK(!buffer_.is_null());
pp::Buffer_Dev buffer;
std::swap(buffer, buffer_);
buffer_id_ = 0;
size_ = 0;
return buffer;
}
PpbBuffer::PpbBuffer(pp::Buffer_Dev buffer,
uint32_t buffer_id,
PpbBufferAllocator* allocator)
: buffer_(buffer), buffer_id_(buffer_id), size_(0), allocator_(allocator) {}
PpbBuffer::~PpbBuffer() {
PP_DCHECK(!buffer_id_ == buffer_.is_null());
// If still owning the |buffer_|, release it in the |allocator_|.
if (buffer_id_)
allocator_->Release(buffer_id_);
}
cdm::Buffer* PpbBufferAllocator::Allocate(uint32_t capacity) {
PP_DCHECK(pp::Module::Get()->core()->IsMainThread());
if (!capacity)
return NULL;
pp::Buffer_Dev buffer;
uint32_t buffer_id = 0;
// Reuse a buffer in the free list if there is one that fits |capacity|.
// Otherwise, create a new one.
FreeBufferMap::iterator found = free_buffers_.lower_bound(capacity);
if (found == free_buffers_.end()) {
// TODO(xhwang): Report statistics about how many new buffers are allocated.
buffer = AllocateNewBuffer(capacity);
if (buffer.is_null())
return NULL;
buffer_id = next_buffer_id_++;
} else {
buffer = found->second.second;
buffer_id = found->second.first;
free_buffers_.erase(found);
}
allocated_buffers_.insert(std::make_pair(buffer_id, buffer));
return PpbBuffer::Create(buffer, buffer_id, this);
}
void PpbBufferAllocator::Release(uint32_t buffer_id) {
if (!buffer_id)
return;
AllocatedBufferMap::iterator found = allocated_buffers_.find(buffer_id);
if (found == allocated_buffers_.end())
return;
pp::Buffer_Dev& buffer = found->second;
free_buffers_.insert(
std::make_pair(buffer.size(), std::make_pair(buffer_id, buffer)));
allocated_buffers_.erase(found);
}
pp::Buffer_Dev PpbBufferAllocator::AllocateNewBuffer(uint32_t capacity) {
// Always pad new allocated buffer so that we don't need to reallocate
// buffers frequently if requested sizes fluctuate slightly.
static const uint32_t kBufferPadding = 512;
// Maximum number of free buffers we can keep when allocating new buffers.
static const uint32_t kFreeLimit = 3;
// Destroy the smallest buffer before allocating a new bigger buffer if the
// number of free buffers exceeds a limit. This mechanism helps avoid ending
// up with too many small buffers, which could happen if the size to be
// allocated keeps increasing.
if (free_buffers_.size() >= kFreeLimit)
free_buffers_.erase(free_buffers_.begin());
// Creation of pp::Buffer_Dev is expensive! It involves synchronous IPC calls.
// That's why we try to avoid AllocateNewBuffer() as much as we can.
return pp::Buffer_Dev(instance_, capacity + kBufferPadding);
}
} // namespace media