summaryrefslogtreecommitdiffstats
path: root/content/browser/renderer_host/media/video_capture_buffer_pool.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/renderer_host/media/video_capture_buffer_pool.cc')
-rw-r--r--content/browser/renderer_host/media/video_capture_buffer_pool.cc427
1 files changed, 220 insertions, 207 deletions
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool.cc b/content/browser/renderer_host/media/video_capture_buffer_pool.cc
index cd3e0be..831c060 100644
--- a/content/browser/renderer_host/media/video_capture_buffer_pool.cc
+++ b/content/browser/renderer_host/media/video_capture_buffer_pool.cc
@@ -16,99 +16,31 @@ namespace content {
const int VideoCaptureBufferPool::kInvalidId = -1;
-// A simple holder of a memory-backed buffer and accessors to it.
-class SimpleBufferHandle final : public VideoCaptureBufferPool::BufferHandle {
- public:
- SimpleBufferHandle(void* data,
- size_t mapped_size,
- base::SharedMemoryHandle handle)
- : data_(data),
- mapped_size_(mapped_size)
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
- ,
- handle_(handle)
-#endif
- {
- }
- ~SimpleBufferHandle() override {}
-
- gfx::Size dimensions() const override {
- NOTREACHED();
- return gfx::Size();
- }
- size_t mapped_size() const override { return mapped_size_; }
- void* data(int plane) override {
- DCHECK_EQ(plane, 0);
- return data_;
- }
- ClientBuffer AsClientBuffer(int plane) override {
- NOTREACHED();
- return nullptr;
- }
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
- base::FileDescriptor AsPlatformFile() override {
- return handle_;
- }
-#endif
-
- private:
- void* const data_;
- const size_t mapped_size_;
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
- const base::SharedMemoryHandle handle_;
-#endif
-};
-
-// A holder of a GpuMemoryBuffer-backed buffer. Holds weak references to
-// GpuMemoryBuffer-backed buffers and provides accessors to their data.
-class GpuMemoryBufferBufferHandle final
- : public VideoCaptureBufferPool::BufferHandle {
- public:
- GpuMemoryBufferBufferHandle(const gfx::Size& dimensions,
- std::vector<
- scoped_ptr<gfx::GpuMemoryBuffer>>* gmbs)
- : dimensions_(dimensions), gmbs_(gmbs) {
- DCHECK(gmbs);
- }
- ~GpuMemoryBufferBufferHandle() override {}
-
- gfx::Size dimensions() const override { return dimensions_; }
- size_t mapped_size() const override { return dimensions_.GetArea(); }
- void* data(int plane) override {
- DCHECK_GE(plane, 0);
- DCHECK_LT(plane, static_cast<int>(gmbs_->size()));
- DCHECK((*gmbs_)[plane]);
- return (*gmbs_)[plane]->memory(0);
- }
- ClientBuffer AsClientBuffer(int plane) override {
- DCHECK_GE(plane, 0);
- DCHECK_LT(plane, static_cast<int>(gmbs_->size()));
- return (*gmbs_)[plane]->AsClientBuffer();
- }
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
- base::FileDescriptor AsPlatformFile() override {
- NOTREACHED();
- return base::FileDescriptor();
- }
-#endif
-
- private:
- const gfx::Size dimensions_;
- std::vector<scoped_ptr<gfx::GpuMemoryBuffer>>* const gmbs_;
-};
-
// Tracker specifics for SharedMemory.
class VideoCaptureBufferPool::SharedMemTracker final : public Tracker {
public:
- SharedMemTracker();
- bool Init(media::VideoPixelFormat format,
+ SharedMemTracker() : Tracker() {}
+
+ bool Init(const gfx::Size& dimensions,
+ media::VideoPixelFormat format,
media::VideoPixelStorage storage_type,
- const gfx::Size& dimensions,
- base::Lock* lock) override;
+ base::Lock* lock) override {
+ DVLOG(2) << "allocating ShMem of " << dimensions.ToString();
+ set_dimensions(dimensions);
+ // |dimensions| can be 0x0 for trackers that do not require memory backing.
+ set_max_pixel_count(dimensions.GetArea());
+ set_pixel_format(format);
+ set_storage_type(storage_type);
+ mapped_size_ =
+ media::VideoCaptureFormat(dimensions, 0.0f, format, storage_type)
+ .ImageAllocationSize();
+ if (!mapped_size_)
+ return true;
+ return shared_memory_.CreateAndMapAnonymous(mapped_size_);
+ }
scoped_ptr<BufferHandle> GetBufferHandle() override {
- return make_scoped_ptr(new SimpleBufferHandle(
- shared_memory_.memory(), mapped_size_, shared_memory_.handle()));
+ return make_scoped_ptr(new SharedMemBufferHandle(this));
}
bool ShareToProcess(base::ProcessHandle process_handle,
base::SharedMemoryHandle* new_handle) override {
@@ -122,6 +54,37 @@ class VideoCaptureBufferPool::SharedMemTracker final : public Tracker {
}
private:
+ // A simple proxy that implements the BufferHandle interface, providing
+ // accessors to SharedMemTracker's memory and properties.
+ class SharedMemBufferHandle : public VideoCaptureBufferPool::BufferHandle {
+ public:
+ // |tracker| must outlive SimpleBufferHandle. This is ensured since a
+ // tracker is pinned until ownership of this SimpleBufferHandle is returned
+ // to VideoCaptureBufferPool.
+ explicit SharedMemBufferHandle(SharedMemTracker* tracker)
+ : tracker_(tracker) {}
+ ~SharedMemBufferHandle() override {}
+
+ gfx::Size dimensions() const override { return tracker_->dimensions(); }
+ size_t mapped_size() const override { return tracker_->mapped_size_; }
+ void* data(int plane) override {
+ DCHECK_EQ(plane, 0);
+ return tracker_->shared_memory_.memory();
+ }
+ ClientBuffer AsClientBuffer(int plane) override {
+ NOTREACHED();
+ return nullptr;
+ }
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ base::FileDescriptor AsPlatformFile() override {
+ return tracker_->shared_memory_.handle();
+ }
+#endif
+
+ private:
+ SharedMemTracker* const tracker_;
+ };
+
// The memory created to be shared with renderer processes.
base::SharedMemory shared_memory_;
size_t mapped_size_;
@@ -131,131 +94,134 @@ class VideoCaptureBufferPool::SharedMemTracker final : public Tracker {
// associated pixel dimensions.
class VideoCaptureBufferPool::GpuMemoryBufferTracker final : public Tracker {
public:
- GpuMemoryBufferTracker();
- bool Init(media::VideoPixelFormat format,
+ GpuMemoryBufferTracker() : Tracker() {}
+
+ ~GpuMemoryBufferTracker() override {
+ for (const auto& gmb : gpu_memory_buffers_)
+ gmb->Unmap();
+ }
+
+ bool Init(const gfx::Size& dimensions,
+ media::VideoPixelFormat format,
media::VideoPixelStorage storage_type,
- const gfx::Size& dimensions,
- base::Lock* lock) override;
- ~GpuMemoryBufferTracker() override;
+ base::Lock* lock) override {
+ DVLOG(2) << "allocating GMB for " << dimensions.ToString();
+ // BrowserGpuMemoryBufferManager::current() may not be accessed on IO
+ // Thread.
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(BrowserGpuMemoryBufferManager::current());
+ // This class is only expected to be called with I420 buffer requests at
+ // this point.
+ DCHECK_EQ(format, media::PIXEL_FORMAT_I420);
+ set_dimensions(dimensions);
+ set_max_pixel_count(dimensions.GetArea());
+ set_pixel_format(format);
+ set_storage_type(storage_type);
+ // |dimensions| can be 0x0 for trackers that do not require memory backing.
+ if (dimensions.GetArea() == 0u)
+ return true;
+
+ base::AutoUnlock auto_unlock(*lock);
+ const size_t num_planes = media::VideoFrame::NumPlanes(pixel_format());
+ for (size_t i = 0; i < num_planes; ++i) {
+ const gfx::Size& size =
+ media::VideoFrame::PlaneSize(pixel_format(), i, dimensions);
+ gpu_memory_buffers_.push_back(
+ BrowserGpuMemoryBufferManager::current()->AllocateGpuMemoryBuffer(
+ size, gfx::BufferFormat::R_8,
+ gfx::BufferUsage::GPU_READ_CPU_READ_WRITE));
+
+ DLOG_IF(ERROR, !gpu_memory_buffers_[i]) << "Allocating GpuMemoryBuffer";
+ if (!gpu_memory_buffers_[i] || !gpu_memory_buffers_[i]->Map())
+ return false;
+ }
+ return true;
+ }
scoped_ptr<BufferHandle> GetBufferHandle() override {
DCHECK_EQ(gpu_memory_buffers_.size(),
media::VideoFrame::NumPlanes(pixel_format()));
- return make_scoped_ptr(
- new GpuMemoryBufferBufferHandle(dimensions_, &gpu_memory_buffers_));
+ return make_scoped_ptr(new GpuMemoryBufferBufferHandle(this));
}
+
bool ShareToProcess(base::ProcessHandle process_handle,
base::SharedMemoryHandle* new_handle) override {
NOTREACHED();
return false;
}
+
bool ShareToProcess2(int plane,
base::ProcessHandle process_handle,
- gfx::GpuMemoryBufferHandle* new_handle) override;
-
- private:
- gfx::Size dimensions_;
- // Owned references to GpuMemoryBuffers.
- std::vector<scoped_ptr<gfx::GpuMemoryBuffer>> gpu_memory_buffers_;
-};
-
-VideoCaptureBufferPool::SharedMemTracker::SharedMemTracker() : Tracker() {}
-
-bool VideoCaptureBufferPool::SharedMemTracker::Init(
- media::VideoPixelFormat format,
- media::VideoPixelStorage storage_type,
- const gfx::Size& dimensions,
- base::Lock* lock) {
- DVLOG(2) << "allocating ShMem of " << dimensions.ToString();
- set_pixel_format(format);
- set_storage_type(storage_type);
- // |dimensions| can be 0x0 for trackers that do not require memory backing.
- set_pixel_count(dimensions.GetArea());
- mapped_size_ =
- media::VideoCaptureFormat(dimensions, 0.0f, format, storage_type)
- .ImageAllocationSize();
- if (!mapped_size_)
- return true;
- return shared_memory_.CreateAndMapAnonymous(mapped_size_);
-}
-
-VideoCaptureBufferPool::GpuMemoryBufferTracker::GpuMemoryBufferTracker()
- : Tracker() {
-}
-
-VideoCaptureBufferPool::GpuMemoryBufferTracker::~GpuMemoryBufferTracker() {
- for (const auto& gmb : gpu_memory_buffers_)
- gmb->Unmap();
-}
-
-bool VideoCaptureBufferPool::GpuMemoryBufferTracker::Init(
- media::VideoPixelFormat format,
- media::VideoPixelStorage storage_type,
- const gfx::Size& dimensions,
- base::Lock* lock) {
- DVLOG(2) << "allocating GMB for " << dimensions.ToString();
- // BrowserGpuMemoryBufferManager::current() may not be accessed on IO Thread.
- DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK(BrowserGpuMemoryBufferManager::current());
- // This class is only expected to be called with I420 buffer requests at this
- // point.
- DCHECK_EQ(format, media::PIXEL_FORMAT_I420);
- set_pixel_format(format);
- set_storage_type(storage_type);
- set_pixel_count(dimensions.GetArea());
- // |dimensions| can be 0x0 for trackers that do not require memory backing.
- if (dimensions.GetArea() == 0u)
+ gfx::GpuMemoryBufferHandle* new_handle) override {
+ DCHECK_LE(plane, static_cast<int>(gpu_memory_buffers_.size()));
+
+ const auto& current_gmb_handle = gpu_memory_buffers_[plane]->GetHandle();
+ switch (current_gmb_handle.type) {
+ case gfx::EMPTY_BUFFER:
+ NOTREACHED();
+ return false;
+ case gfx::SHARED_MEMORY_BUFFER: {
+ DCHECK(base::SharedMemory::IsHandleValid(current_gmb_handle.handle));
+ base::SharedMemory shared_memory(
+ base::SharedMemory::DuplicateHandle(current_gmb_handle.handle),
+ false);
+ shared_memory.ShareToProcess(process_handle, &new_handle->handle);
+ DCHECK(base::SharedMemory::IsHandleValid(new_handle->handle));
+ new_handle->type = gfx::SHARED_MEMORY_BUFFER;
+ return true;
+ }
+ case gfx::IO_SURFACE_BUFFER:
+ case gfx::SURFACE_TEXTURE_BUFFER:
+ case gfx::OZONE_NATIVE_PIXMAP:
+ *new_handle = current_gmb_handle;
+ return true;
+ }
+ NOTREACHED();
return true;
- dimensions_ = dimensions;
-
- lock->Release();
- const size_t num_planes = media::VideoFrame::NumPlanes(pixel_format());
- for (size_t i = 0; i < num_planes; ++i) {
- const gfx::Size& size =
- media::VideoFrame::PlaneSize(pixel_format(), i, dimensions);
- gpu_memory_buffers_.push_back(
- BrowserGpuMemoryBufferManager::current()->AllocateGpuMemoryBuffer(
- size, gfx::BufferFormat::R_8,
- gfx::BufferUsage::GPU_READ_CPU_READ_WRITE));
-
- DLOG_IF(ERROR, !gpu_memory_buffers_[i]) << "Allocating GpuMemoryBuffer";
- if (!gpu_memory_buffers_[i] || !gpu_memory_buffers_[i]->Map())
- return false;
}
- lock->Acquire();
- return true;
-}
-
-bool VideoCaptureBufferPool::GpuMemoryBufferTracker::ShareToProcess2(
- int plane,
- base::ProcessHandle process_handle,
- gfx::GpuMemoryBufferHandle* new_handle) {
- DCHECK_LE(plane, static_cast<int>(gpu_memory_buffers_.size()));
- const auto& current_gmb_handle = gpu_memory_buffers_[plane]->GetHandle();
- switch (current_gmb_handle.type) {
- case gfx::EMPTY_BUFFER:
+ private:
+ // A simple proxy that implements the BufferHandle interface, providing
+ // accessors to GpuMemoryBufferTracker's memory and properties.
+ class GpuMemoryBufferBufferHandle
+ : public VideoCaptureBufferPool::BufferHandle {
+ public:
+ // |tracker| must outlive GpuMemoryBufferBufferHandle. This is ensured since
+ // a tracker is pinned until ownership of this GpuMemoryBufferBufferHandle
+ // is returned to VideoCaptureBufferPool.
+ explicit GpuMemoryBufferBufferHandle(GpuMemoryBufferTracker* tracker)
+ : tracker_(tracker) {}
+ ~GpuMemoryBufferBufferHandle() override {}
+
+ gfx::Size dimensions() const override { return tracker_->dimensions(); }
+ size_t mapped_size() const override {
+ return tracker_->dimensions().GetArea();
+ }
+ void* data(int plane) override {
+ DCHECK_GE(plane, 0);
+ DCHECK_LT(plane, static_cast<int>(tracker_->gpu_memory_buffers_.size()));
+ DCHECK(tracker_->gpu_memory_buffers_[plane]);
+ return tracker_->gpu_memory_buffers_[plane]->memory(0);
+ }
+ ClientBuffer AsClientBuffer(int plane) override {
+ DCHECK_GE(plane, 0);
+ DCHECK_LT(plane, static_cast<int>(tracker_->gpu_memory_buffers_.size()));
+ return tracker_->gpu_memory_buffers_[plane]->AsClientBuffer();
+ }
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ base::FileDescriptor AsPlatformFile() override {
NOTREACHED();
- return false;
- case gfx::SHARED_MEMORY_BUFFER: {
- DCHECK(base::SharedMemory::IsHandleValid(current_gmb_handle.handle));
- base::SharedMemory shared_memory(
- base::SharedMemory::DuplicateHandle(current_gmb_handle.handle),
- false);
- shared_memory.ShareToProcess(process_handle, &new_handle->handle);
- DCHECK(base::SharedMemory::IsHandleValid(new_handle->handle));
- new_handle->type = gfx::SHARED_MEMORY_BUFFER;
- return true;
+ return base::FileDescriptor();
}
- case gfx::IO_SURFACE_BUFFER:
- case gfx::SURFACE_TEXTURE_BUFFER:
- case gfx::OZONE_NATIVE_PIXMAP:
- *new_handle = current_gmb_handle;
- return true;
- }
- NOTREACHED();
- return true;
-}
+#endif
+
+ private:
+ GpuMemoryBufferTracker* const tracker_;
+ };
+
+ // Owned references to GpuMemoryBuffers.
+ std::vector<scoped_ptr<gfx::GpuMemoryBuffer>> gpu_memory_buffers_;
+};
// static
scoped_ptr<VideoCaptureBufferPool::Tracker>
@@ -275,7 +241,8 @@ VideoCaptureBufferPool::Tracker::~Tracker() {}
VideoCaptureBufferPool::VideoCaptureBufferPool(int count)
: count_(count),
- next_buffer_id_(0) {
+ next_buffer_id_(0),
+ last_relinquished_buffer_id_(kInvalidId) {
DCHECK_GT(count, 0);
}
@@ -332,13 +299,12 @@ VideoCaptureBufferPool::GetBufferHandle(int buffer_id) {
return tracker->GetBufferHandle();
}
-int VideoCaptureBufferPool::ReserveForProducer(
- media::VideoPixelFormat format,
- media::VideoPixelStorage storage,
- const gfx::Size& dimensions,
- int* buffer_id_to_drop) {
+int VideoCaptureBufferPool::ReserveForProducer(const gfx::Size& dimensions,
+ media::VideoPixelFormat format,
+ media::VideoPixelStorage storage,
+ int* buffer_id_to_drop) {
base::AutoLock lock(lock_);
- return ReserveForProducerInternal(format, storage, dimensions,
+ return ReserveForProducerInternal(dimensions, format, storage,
buffer_id_to_drop);
}
@@ -351,6 +317,7 @@ void VideoCaptureBufferPool::RelinquishProducerReservation(int buffer_id) {
}
DCHECK(tracker->held_by_producer());
tracker->set_held_by_producer(false);
+ last_relinquished_buffer_id_ = buffer_id;
}
void VideoCaptureBufferPool::HoldForConsumers(
@@ -385,6 +352,34 @@ void VideoCaptureBufferPool::RelinquishConsumerHold(int buffer_id,
num_clients);
}
+int VideoCaptureBufferPool::ResurrectLastForProducer(
+ const gfx::Size& dimensions,
+ media::VideoPixelFormat format,
+ media::VideoPixelStorage storage) {
+ base::AutoLock lock(lock_);
+
+ // Return early if the last relinquished buffer has been re-used already.
+ if (last_relinquished_buffer_id_ == kInvalidId)
+ return kInvalidId;
+
+ // If there are no consumers reading from this buffer, then it's safe to
+ // provide this buffer back to the producer (because the producer may
+ // potentially modify the content). Check that the expected dimensions,
+ // format, and storage match.
+ TrackerMap::iterator it = trackers_.find(last_relinquished_buffer_id_);
+ DCHECK(it != trackers_.end());
+ DCHECK(!it->second->held_by_producer());
+ if (it->second->consumer_hold_count() == 0 &&
+ it->second->dimensions() == dimensions &&
+ it->second->pixel_format() == format &&
+ it->second->storage_type() == storage) {
+ it->second->set_held_by_producer(true);
+ return last_relinquished_buffer_id_;
+ }
+
+ return kInvalidId;
+}
+
double VideoCaptureBufferPool::GetBufferPoolUtilization() const {
base::AutoLock lock(lock_);
int num_buffers_held = 0;
@@ -397,9 +392,9 @@ double VideoCaptureBufferPool::GetBufferPoolUtilization() const {
}
int VideoCaptureBufferPool::ReserveForProducerInternal(
+ const gfx::Size& dimensions,
media::VideoPixelFormat pixel_format,
media::VideoPixelStorage storage_type,
- const gfx::Size& dimensions,
int* buffer_id_to_drop) {
lock_.AssertAcquired();
@@ -408,32 +403,50 @@ int VideoCaptureBufferPool::ReserveForProducerInternal(
// largest one that's not big enough, in case we have to reallocate a tracker.
*buffer_id_to_drop = kInvalidId;
size_t largest_size_in_pixels = 0;
+ TrackerMap::iterator tracker_of_last_resort = trackers_.end();
TrackerMap::iterator tracker_to_drop = trackers_.end();
for (TrackerMap::iterator it = trackers_.begin(); it != trackers_.end();
++it) {
Tracker* const tracker = it->second;
if (!tracker->consumer_hold_count() && !tracker->held_by_producer()) {
- if (tracker->pixel_count() >= size_in_pixels &&
+ if (tracker->max_pixel_count() >= size_in_pixels &&
(tracker->pixel_format() == pixel_format) &&
(tracker->storage_type() == storage_type)) {
+ if (it->first == last_relinquished_buffer_id_) {
+ // This buffer would do just fine, but avoid returning it because the
+ // client may want to resurrect it. It will be returned perforce if
+ // the pool has reached it's maximum limit (see code below).
+ tracker_of_last_resort = it;
+ continue;
+ }
// Existing tracker is big enough and has correct format. Reuse it.
+ tracker->set_dimensions(dimensions);
tracker->set_held_by_producer(true);
return it->first;
}
- if (tracker->pixel_count() > largest_size_in_pixels) {
- largest_size_in_pixels = tracker->pixel_count();
+ if (tracker->max_pixel_count() > largest_size_in_pixels) {
+ largest_size_in_pixels = tracker->max_pixel_count();
tracker_to_drop = it;
}
}
}
// Preferably grow the pool by creating a new tracker. If we're at maximum
- // size, then reallocate by deleting an existing one instead.
+ // size, then try using |tracker_of_last_resort| or reallocate by deleting an
+ // existing one instead.
if (trackers_.size() == static_cast<size_t>(count_)) {
+ if (tracker_of_last_resort != trackers_.end()) {
+ last_relinquished_buffer_id_ = kInvalidId;
+ tracker_of_last_resort->second->set_dimensions(dimensions);
+ tracker_of_last_resort->second->set_held_by_producer(true);
+ return tracker_of_last_resort->first;
+ }
if (tracker_to_drop == trackers_.end()) {
// We're out of space, and can't find an unused tracker to reallocate.
return kInvalidId;
}
+ if (tracker_to_drop->first == last_relinquished_buffer_id_)
+ last_relinquished_buffer_id_ = kInvalidId;
*buffer_id_to_drop = tracker_to_drop->first;
delete tracker_to_drop->second;
trackers_.erase(tracker_to_drop);
@@ -445,7 +458,7 @@ int VideoCaptureBufferPool::ReserveForProducerInternal(
scoped_ptr<Tracker> tracker = Tracker::CreateTracker(storage_type);
// TODO(emircan): We pass the lock here to solve GMB allocation issue, see
// crbug.com/545238.
- if (!tracker->Init(pixel_format, storage_type, dimensions, &lock_)) {
+ if (!tracker->Init(dimensions, pixel_format, storage_type, &lock_)) {
DLOG(ERROR) << "Error initializing Tracker";
return kInvalidId;
}