| // Copyright (c) 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/capture/video/video_capture_buffer_pool_impl.h" |
| |
| #include <memory> |
| |
| #include "base/logging.h" |
| #include "base/memory/ptr_util.h" |
| #include "build/build_config.h" |
| #include "media/capture/video/video_capture_buffer_handle.h" |
| #include "media/capture/video/video_capture_buffer_tracker.h" |
| #include "mojo/public/cpp/system/platform_handle.h" |
| #include "ui/gfx/buffer_format_util.h" |
| |
| namespace media { |
| |
| VideoCaptureBufferPoolImpl::VideoCaptureBufferPoolImpl( |
| std::unique_ptr<VideoCaptureBufferTrackerFactory> buffer_tracker_factory, |
| int count) |
| : count_(count), |
| next_buffer_id_(0), |
| buffer_tracker_factory_(std::move(buffer_tracker_factory)) { |
| DCHECK_GT(count, 0); |
| } |
| |
| VideoCaptureBufferPoolImpl::~VideoCaptureBufferPoolImpl() = default; |
| |
| mojo::ScopedSharedBufferHandle |
| VideoCaptureBufferPoolImpl::GetHandleForInterProcessTransit(int buffer_id, |
| bool read_only) { |
| base::AutoLock lock(lock_); |
| |
| VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); |
| if (!tracker) { |
| NOTREACHED() << "Invalid buffer_id."; |
| return mojo::ScopedSharedBufferHandle(); |
| } |
| return tracker->GetHandleForTransit(read_only); |
| } |
| |
| base::SharedMemoryHandle |
| VideoCaptureBufferPoolImpl::GetNonOwnedSharedMemoryHandleForLegacyIPC( |
| int buffer_id) { |
| base::AutoLock lock(lock_); |
| |
| VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); |
| if (!tracker) { |
| NOTREACHED() << "Invalid buffer_id."; |
| return base::SharedMemoryHandle(); |
| } |
| return tracker->GetNonOwnedSharedMemoryHandleForLegacyIPC(); |
| } |
| |
| mojom::SharedMemoryViaRawFileDescriptorPtr |
| VideoCaptureBufferPoolImpl::CreateSharedMemoryViaRawFileDescriptorStruct( |
| int buffer_id) { |
| // This requires platforms where base::SharedMemoryHandle is backed by a |
| // file descriptor. |
| #if defined(OS_LINUX) |
| base::AutoLock lock(lock_); |
| |
| VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); |
| if (!tracker) { |
| NOTREACHED() << "Invalid buffer_id."; |
| return 0u; |
| } |
| |
| auto result = mojom::SharedMemoryViaRawFileDescriptor::New(); |
| result->file_descriptor_handle = mojo::WrapPlatformFile( |
| base::SharedMemory::DuplicateHandle( |
| tracker->GetNonOwnedSharedMemoryHandleForLegacyIPC()) |
| .GetHandle()); |
| result->shared_memory_size_in_bytes = tracker->GetMemorySizeInBytes(); |
| return result; |
| #else |
| NOTREACHED(); |
| return mojom::SharedMemoryViaRawFileDescriptorPtr(); |
| #endif |
| } |
| |
| std::unique_ptr<VideoCaptureBufferHandle> |
| VideoCaptureBufferPoolImpl::GetHandleForInProcessAccess(int buffer_id) { |
| base::AutoLock lock(lock_); |
| |
| VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); |
| if (!tracker) { |
| NOTREACHED() << "Invalid buffer_id."; |
| return nullptr; |
| } |
| |
| return tracker->GetMemoryMappedAccess(); |
| } |
| |
| VideoCaptureDevice::Client::ReserveResult |
| VideoCaptureBufferPoolImpl::ReserveForProducer( |
| const gfx::Size& dimensions, |
| VideoPixelFormat format, |
| const mojom::PlaneStridesPtr& strides, |
| int frame_feedback_id, |
| int* buffer_id, |
| int* buffer_id_to_drop) { |
| base::AutoLock lock(lock_); |
| return ReserveForProducerInternal(dimensions, format, strides, |
| frame_feedback_id, buffer_id, |
| buffer_id_to_drop); |
| } |
| |
| void VideoCaptureBufferPoolImpl::RelinquishProducerReservation(int buffer_id) { |
| base::AutoLock lock(lock_); |
| VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); |
| if (!tracker) { |
| NOTREACHED() << "Invalid buffer_id."; |
| return; |
| } |
| DCHECK(tracker->held_by_producer()); |
| tracker->set_held_by_producer(false); |
| } |
| |
| void VideoCaptureBufferPoolImpl::HoldForConsumers(int buffer_id, |
| int num_clients) { |
| base::AutoLock lock(lock_); |
| VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); |
| if (!tracker) { |
| NOTREACHED() << "Invalid buffer_id."; |
| return; |
| } |
| DCHECK(tracker->held_by_producer()); |
| DCHECK(!tracker->consumer_hold_count()); |
| |
| tracker->set_consumer_hold_count(num_clients); |
| // Note: |held_by_producer()| will stay true until |
| // RelinquishProducerReservation() (usually called by destructor of the object |
| // wrapping this tracker, e.g. a VideoFrame). |
| } |
| |
| void VideoCaptureBufferPoolImpl::RelinquishConsumerHold(int buffer_id, |
| int num_clients) { |
| base::AutoLock lock(lock_); |
| VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); |
| if (!tracker) { |
| NOTREACHED() << "Invalid buffer_id."; |
| return; |
| } |
| DCHECK_GE(tracker->consumer_hold_count(), num_clients); |
| |
| tracker->set_consumer_hold_count(tracker->consumer_hold_count() - |
| num_clients); |
| } |
| |
| double VideoCaptureBufferPoolImpl::GetBufferPoolUtilization() const { |
| base::AutoLock lock(lock_); |
| int num_buffers_held = 0; |
| for (const auto& entry : trackers_) { |
| VideoCaptureBufferTracker* const tracker = entry.second.get(); |
| if (tracker->held_by_producer() || tracker->consumer_hold_count() > 0) |
| ++num_buffers_held; |
| } |
| return static_cast<double>(num_buffers_held) / count_; |
| } |
| |
| VideoCaptureDevice::Client::ReserveResult |
| VideoCaptureBufferPoolImpl::ReserveForProducerInternal( |
| const gfx::Size& dimensions, |
| VideoPixelFormat pixel_format, |
| const mojom::PlaneStridesPtr& strides, |
| int frame_feedback_id, |
| int* buffer_id, |
| int* buffer_id_to_drop) { |
| lock_.AssertAcquired(); |
| |
| // Look for a tracker that's allocated, big enough, and not in use. Track the |
| // largest one that's not big enough, in case we have to reallocate a tracker. |
| *buffer_id_to_drop = kInvalidId; |
| uint32_t largest_memory_size_in_bytes = 0; |
| auto tracker_to_drop = trackers_.end(); |
| for (auto it = trackers_.begin(); it != trackers_.end(); ++it) { |
| VideoCaptureBufferTracker* const tracker = it->second.get(); |
| if (!tracker->consumer_hold_count() && !tracker->held_by_producer()) { |
| if (tracker->IsReusableForFormat(dimensions, pixel_format, strides)) { |
| // Reuse this buffer |
| tracker->set_held_by_producer(true); |
| tracker->set_frame_feedback_id(frame_feedback_id); |
| *buffer_id = it->first; |
| return VideoCaptureDevice::Client::ReserveResult::kSucceeded; |
| } |
| if (tracker->GetMemorySizeInBytes() > largest_memory_size_in_bytes) { |
| largest_memory_size_in_bytes = tracker->GetMemorySizeInBytes(); |
| tracker_to_drop = it; |
| } |
| } |
| } |
| |
| // Preferably grow the pool by creating a new tracker. If we're at maximum |
| // size, reallocate by deleting an existing one. |
| if (trackers_.size() == static_cast<size_t>(count_)) { |
| if (tracker_to_drop == trackers_.end()) { |
| // We're out of space, and can't find an unused tracker to reallocate. |
| *buffer_id = kInvalidId; |
| return VideoCaptureDevice::Client::ReserveResult::kMaxBufferCountExceeded; |
| } |
| *buffer_id_to_drop = tracker_to_drop->first; |
| trackers_.erase(tracker_to_drop); |
| } |
| |
| // Create the new tracker. |
| const int new_buffer_id = next_buffer_id_++; |
| |
| std::unique_ptr<VideoCaptureBufferTracker> tracker = |
| buffer_tracker_factory_->CreateTracker(); |
| if (!tracker->Init(dimensions, pixel_format, strides)) { |
| DLOG(ERROR) << "Error initializing VideoCaptureBufferTracker"; |
| *buffer_id = kInvalidId; |
| return VideoCaptureDevice::Client::ReserveResult::kAllocationFailed; |
| } |
| |
| tracker->set_held_by_producer(true); |
| tracker->set_frame_feedback_id(frame_feedback_id); |
| trackers_[new_buffer_id] = std::move(tracker); |
| |
| *buffer_id = new_buffer_id; |
| return VideoCaptureDevice::Client::ReserveResult::kSucceeded; |
| } |
| |
| VideoCaptureBufferTracker* VideoCaptureBufferPoolImpl::GetTracker( |
| int buffer_id) { |
| auto it = trackers_.find(buffer_id); |
| return (it == trackers_.end()) ? nullptr : it->second.get(); |
| } |
| |
| } // namespace media |