1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/cdm/ppapi/cdm_helpers.h"
6
7#include <algorithm>
8#include <utility>
9
10#include "base/basictypes.h"
11#include "base/compiler_specific.h"
12#include "build/build_config.h"
13#include "media/cdm/ppapi/api/content_decryption_module.h"
14#include "ppapi/c/pp_errors.h"
15#include "ppapi/c/pp_stdint.h"
16#include "ppapi/cpp/core.h"
17#include "ppapi/cpp/dev/buffer_dev.h"
18#include "ppapi/cpp/instance.h"
19#include "ppapi/cpp/logging.h"
20#include "ppapi/cpp/module.h"
21
22namespace media {
23
24// static
25PpbBuffer* PpbBuffer::Create(const pp::Buffer_Dev& buffer,
26                             uint32_t buffer_id,
27                             PpbBufferAllocator* allocator) {
28  PP_DCHECK(buffer.data());
29  PP_DCHECK(buffer.size());
30  PP_DCHECK(buffer_id);
31  PP_DCHECK(allocator);
32  return new PpbBuffer(buffer, buffer_id, allocator);
33}
34
35void PpbBuffer::Destroy() {
36  delete this;
37}
38
39uint32_t PpbBuffer::Capacity() const {
40  return buffer_.size();
41}
42
43uint8_t* PpbBuffer::Data() {
44  return static_cast<uint8_t*>(buffer_.data());
45}
46
47void PpbBuffer::SetSize(uint32_t size) {
48  PP_DCHECK(size <= Capacity());
49  if (size > Capacity()) {
50    size_ = 0;
51    return;
52  }
53
54  size_ = size;
55}
56
57pp::Buffer_Dev PpbBuffer::TakeBuffer() {
58  PP_DCHECK(!buffer_.is_null());
59  pp::Buffer_Dev buffer;
60  std::swap(buffer, buffer_);
61  buffer_id_ = 0;
62  size_ = 0;
63  return buffer;
64}
65
66PpbBuffer::PpbBuffer(pp::Buffer_Dev buffer,
67                     uint32_t buffer_id,
68                     PpbBufferAllocator* allocator)
69    : buffer_(buffer), buffer_id_(buffer_id), size_(0), allocator_(allocator) {
70}
71
72PpbBuffer::~PpbBuffer() {
73  PP_DCHECK(!buffer_id_ == buffer_.is_null());
74  // If still owning the |buffer_|, release it in the |allocator_|.
75  if (buffer_id_)
76    allocator_->Release(buffer_id_);
77}
78
79cdm::Buffer* PpbBufferAllocator::Allocate(uint32_t capacity) {
80  PP_DCHECK(pp::Module::Get()->core()->IsMainThread());
81
82  if (!capacity)
83    return NULL;
84
85  pp::Buffer_Dev buffer;
86  uint32_t buffer_id = 0;
87
88  // Reuse a buffer in the free list if there is one that fits |capacity|.
89  // Otherwise, create a new one.
90  FreeBufferMap::iterator found = free_buffers_.lower_bound(capacity);
91  if (found == free_buffers_.end()) {
92    // TODO(xhwang): Report statistics about how many new buffers are allocated.
93    buffer = AllocateNewBuffer(capacity);
94    if (buffer.is_null())
95      return NULL;
96    buffer_id = next_buffer_id_++;
97  } else {
98    buffer = found->second.second;
99    buffer_id = found->second.first;
100    free_buffers_.erase(found);
101  }
102
103  allocated_buffers_.insert(std::make_pair(buffer_id, buffer));
104
105  return PpbBuffer::Create(buffer, buffer_id, this);
106}
107
108void PpbBufferAllocator::Release(uint32_t buffer_id) {
109  if (!buffer_id)
110    return;
111
112  AllocatedBufferMap::iterator found = allocated_buffers_.find(buffer_id);
113  if (found == allocated_buffers_.end())
114    return;
115
116  pp::Buffer_Dev& buffer = found->second;
117  free_buffers_.insert(
118      std::make_pair(buffer.size(), std::make_pair(buffer_id, buffer)));
119
120  allocated_buffers_.erase(found);
121}
122
123pp::Buffer_Dev PpbBufferAllocator::AllocateNewBuffer(uint32_t capacity) {
124  // Always pad new allocated buffer so that we don't need to reallocate
125  // buffers frequently if requested sizes fluctuate slightly.
126  static const uint32_t kBufferPadding = 512;
127
128  // Maximum number of free buffers we can keep when allocating new buffers.
129  static const uint32_t kFreeLimit = 3;
130
131  // Destroy the smallest buffer before allocating a new bigger buffer if the
132  // number of free buffers exceeds a limit. This mechanism helps avoid ending
133  // up with too many small buffers, which could happen if the size to be
134  // allocated keeps increasing.
135  if (free_buffers_.size() >= kFreeLimit)
136    free_buffers_.erase(free_buffers_.begin());
137
138  // Creation of pp::Buffer_Dev is expensive! It involves synchronous IPC calls.
139  // That's why we try to avoid AllocateNewBuffer() as much as we can.
140  return pp::Buffer_Dev(instance_, capacity + kBufferPadding);
141}
142
143VideoFrameImpl::VideoFrameImpl()
144    : format_(cdm::kUnknownVideoFormat),
145      frame_buffer_(NULL),
146      timestamp_(0) {
147  for (uint32_t i = 0; i < kMaxPlanes; ++i) {
148    plane_offsets_[i] = 0;
149    strides_[i] = 0;
150  }
151}
152
153VideoFrameImpl::~VideoFrameImpl() {
154  if (frame_buffer_)
155    frame_buffer_->Destroy();
156}
157
158}  // namespace media
159