video_capture_buffer_pool.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright (c) 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 "content/browser/renderer_host/media/video_capture_buffer_pool.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/logging.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/stl_util.h"
12#include "media/base/video_frame.h"
13#include "media/base/video_util.h"
14
15namespace content {
16
17const int VideoCaptureBufferPool::kInvalidId = -1;
18
19VideoCaptureBufferPool::VideoCaptureBufferPool(int count)
20    : count_(count),
21      next_buffer_id_(0) {
22  DCHECK_GT(count, 0);
23}
24
25VideoCaptureBufferPool::~VideoCaptureBufferPool() {
26  STLDeleteValues(&buffers_);
27}
28
29base::SharedMemoryHandle VideoCaptureBufferPool::ShareToProcess(
30    int buffer_id,
31    base::ProcessHandle process_handle,
32    size_t* memory_size) {
33  base::AutoLock lock(lock_);
34
35  Buffer* buffer = GetBuffer(buffer_id);
36  if (!buffer) {
37    NOTREACHED() << "Invalid buffer_id.";
38    return base::SharedMemory::NULLHandle();
39  }
40  base::SharedMemoryHandle remote_handle;
41  buffer->shared_memory.ShareToProcess(process_handle, &remote_handle);
42  *memory_size = buffer->shared_memory.requested_size();
43  return remote_handle;
44}
45
46bool VideoCaptureBufferPool::GetBufferInfo(int buffer_id,
47                                           void** memory,
48                                           size_t* size) {
49  base::AutoLock lock(lock_);
50
51  Buffer* buffer = GetBuffer(buffer_id);
52  if (!buffer) {
53    NOTREACHED() << "Invalid buffer_id.";
54    return false;
55  }
56
57  DCHECK(buffer->held_by_producer);
58  *memory = buffer->shared_memory.memory();
59  *size = buffer->shared_memory.mapped_size();
60  return true;
61}
62
63int VideoCaptureBufferPool::ReserveForProducer(size_t size,
64                                               int* buffer_id_to_drop) {
65  base::AutoLock lock(lock_);
66  return ReserveForProducerInternal(size, buffer_id_to_drop);
67}
68
69void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id) {
70  base::AutoLock lock(lock_);
71  Buffer* buffer = GetBuffer(buffer_id);
72  if (!buffer) {
73    NOTREACHED() << "Invalid buffer_id.";
74    return;
75  }
76  DCHECK(buffer->held_by_producer);
77  buffer->held_by_producer = false;
78}
79
80void VideoCaptureBufferPool::HoldForConsumers(
81    int buffer_id,
82    int num_clients) {
83  base::AutoLock lock(lock_);
84  Buffer* buffer = GetBuffer(buffer_id);
85  if (!buffer) {
86    NOTREACHED() << "Invalid buffer_id.";
87    return;
88  }
89  DCHECK(buffer->held_by_producer);
90  DCHECK(!buffer->consumer_hold_count);
91
92  buffer->consumer_hold_count = num_clients;
93  // Note: |held_by_producer| will stay true until
94  // RelinquishProducerReservation() (usually called by destructor of the object
95  // wrapping this buffer, e.g. a media::VideoFrame).
96}
97
98void VideoCaptureBufferPool::RelinquishConsumerHold(int buffer_id,
99                                                    int num_clients) {
100  base::AutoLock lock(lock_);
101  Buffer* buffer = GetBuffer(buffer_id);
102  if (!buffer) {
103    NOTREACHED() << "Invalid buffer_id.";
104    return;
105  }
106  DCHECK_GE(buffer->consumer_hold_count, num_clients);
107
108  buffer->consumer_hold_count -= num_clients;
109}
110
111VideoCaptureBufferPool::Buffer::Buffer()
112    : held_by_producer(false), consumer_hold_count(0) {}
113
114int VideoCaptureBufferPool::ReserveForProducerInternal(size_t size,
115                                                       int* buffer_id_to_drop) {
116  lock_.AssertAcquired();
117
118  // Look for a buffer that's allocated, big enough, and not in use. Track the
119  // largest one that's not big enough, in case we have to reallocate a buffer.
120  *buffer_id_to_drop = kInvalidId;
121  size_t realloc_size = 0;
122  BufferMap::iterator realloc = buffers_.end();
123  for (BufferMap::iterator it = buffers_.begin(); it != buffers_.end(); ++it) {
124    Buffer* buffer = it->second;
125    if (!buffer->consumer_hold_count && !buffer->held_by_producer) {
126      if (buffer->shared_memory.requested_size() >= size) {
127        // Existing buffer is big enough. Reuse it.
128        buffer->held_by_producer = true;
129        return it->first;
130      }
131      if (buffer->shared_memory.requested_size() > realloc_size) {
132        realloc_size = buffer->shared_memory.requested_size();
133        realloc = it;
134      }
135    }
136  }
137
138  // Preferentially grow the pool by creating a new buffer. If we're at maximum
139  // size, then reallocate by deleting an existing one instead.
140  if (buffers_.size() == static_cast<size_t>(count_)) {
141    if (realloc == buffers_.end()) {
142      // We're out of space, and can't find an unused buffer to reallocate.
143      return kInvalidId;
144    }
145    *buffer_id_to_drop = realloc->first;
146    delete realloc->second;
147    buffers_.erase(realloc);
148  }
149
150  // Create the new buffer.
151  int buffer_id = next_buffer_id_++;
152  scoped_ptr<Buffer> buffer(new Buffer());
153  if (size) {
154    // |size| can be 0 for buffers that do not require memory backing.
155    if (!buffer->shared_memory.CreateAndMapAnonymous(size))
156      return kInvalidId;
157  }
158  buffer->held_by_producer = true;
159  buffers_[buffer_id] = buffer.release();
160  return buffer_id;
161}
162
163VideoCaptureBufferPool::Buffer* VideoCaptureBufferPool::GetBuffer(
164    int buffer_id) {
165  BufferMap::iterator it = buffers_.find(buffer_id);
166  if (it == buffers_.end())
167    return NULL;
168  return it->second;
169}
170
171}  // namespace content
172