diff options
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.cc | 427 |
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; } |