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 "media/capture/video/video_capture_buffer_pool_impl.h"
6 
7 #include <memory>
8 
9 #include "base/logging.h"
10 #include "base/memory/platform_shared_memory_region.h"
11 #include "base/memory/ptr_util.h"
12 #include "build/build_config.h"
13 #include "media/capture/video/video_capture_buffer_handle.h"
14 #include "media/capture/video/video_capture_buffer_tracker.h"
15 #include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
16 #include "ui/gfx/buffer_format_util.h"
17 
18 namespace media {
19 
VideoCaptureBufferPoolImpl(VideoCaptureBufferType buffer_type,int count)20 VideoCaptureBufferPoolImpl::VideoCaptureBufferPoolImpl(
21     VideoCaptureBufferType buffer_type,
22     int count)
23     : buffer_type_(buffer_type),
24       count_(count),
25       buffer_tracker_factory_(
26           std::make_unique<media::VideoCaptureBufferTrackerFactoryImpl>()) {
27   DCHECK_GT(count, 0);
28 }
29 
30 VideoCaptureBufferPoolImpl::~VideoCaptureBufferPoolImpl() = default;
31 
32 base::UnsafeSharedMemoryRegion
DuplicateAsUnsafeRegion(int buffer_id)33 VideoCaptureBufferPoolImpl::DuplicateAsUnsafeRegion(int buffer_id) {
34   base::AutoLock lock(lock_);
35 
36   VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
37   if (!tracker) {
38     NOTREACHED() << "Invalid buffer_id.";
39     return {};
40   }
41   return tracker->DuplicateAsUnsafeRegion();
42 }
43 
44 mojo::ScopedSharedBufferHandle
DuplicateAsMojoBuffer(int buffer_id)45 VideoCaptureBufferPoolImpl::DuplicateAsMojoBuffer(int buffer_id) {
46   base::AutoLock lock(lock_);
47 
48   VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
49   if (!tracker) {
50     NOTREACHED() << "Invalid buffer_id.";
51     return mojo::ScopedSharedBufferHandle();
52   }
53   return tracker->DuplicateAsMojoBuffer();
54 }
55 
56 mojom::SharedMemoryViaRawFileDescriptorPtr
CreateSharedMemoryViaRawFileDescriptorStruct(int buffer_id)57 VideoCaptureBufferPoolImpl::CreateSharedMemoryViaRawFileDescriptorStruct(
58     int buffer_id) {
59 // This requires platforms where base::SharedMemoryHandle is backed by a
60 // file descriptor.
61 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
62   base::AutoLock lock(lock_);
63 
64   VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
65   if (!tracker) {
66     NOTREACHED() << "Invalid buffer_id.";
67     return 0u;
68   }
69 
70   // Convert the mojo::ScopedSharedBufferHandle to a PlatformSharedMemoryRegion
71   // in order to extract the platform file descriptor.
72   base::subtle::PlatformSharedMemoryRegion platform_region =
73       mojo::UnwrapPlatformSharedMemoryRegion(tracker->DuplicateAsMojoBuffer());
74   if (!platform_region.IsValid()) {
75     NOTREACHED();
76     return 0u;
77   }
78   base::subtle::ScopedFDPair fds = platform_region.PassPlatformHandle();
79   auto result = mojom::SharedMemoryViaRawFileDescriptor::New();
80   result->file_descriptor_handle = mojo::PlatformHandle(std::move(fds.fd));
81   result->shared_memory_size_in_bytes = tracker->GetMemorySizeInBytes();
82   return result;
83 #else
84   NOTREACHED();
85   return mojom::SharedMemoryViaRawFileDescriptorPtr();
86 #endif
87 }
88 
89 std::unique_ptr<VideoCaptureBufferHandle>
GetHandleForInProcessAccess(int buffer_id)90 VideoCaptureBufferPoolImpl::GetHandleForInProcessAccess(int buffer_id) {
91   base::AutoLock lock(lock_);
92 
93   VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
94   if (!tracker) {
95     NOTREACHED() << "Invalid buffer_id.";
96     return nullptr;
97   }
98 
99   return tracker->GetMemoryMappedAccess();
100 }
101 
GetGpuMemoryBufferHandle(int buffer_id)102 gfx::GpuMemoryBufferHandle VideoCaptureBufferPoolImpl::GetGpuMemoryBufferHandle(
103     int buffer_id) {
104   base::AutoLock lock(lock_);
105 
106   VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
107   if (!tracker) {
108     NOTREACHED() << "Invalid buffer_id.";
109     return gfx::GpuMemoryBufferHandle();
110   }
111 
112   return tracker->GetGpuMemoryBufferHandle();
113 }
114 
115 VideoCaptureDevice::Client::ReserveResult
ReserveForProducer(const gfx::Size & dimensions,VideoPixelFormat format,const mojom::PlaneStridesPtr & strides,int frame_feedback_id,int * buffer_id,int * buffer_id_to_drop)116 VideoCaptureBufferPoolImpl::ReserveForProducer(
117     const gfx::Size& dimensions,
118     VideoPixelFormat format,
119     const mojom::PlaneStridesPtr& strides,
120     int frame_feedback_id,
121     int* buffer_id,
122     int* buffer_id_to_drop) {
123   base::AutoLock lock(lock_);
124   return ReserveForProducerInternal(dimensions, format, strides,
125                                     frame_feedback_id, buffer_id,
126                                     buffer_id_to_drop);
127 }
128 
RelinquishProducerReservation(int buffer_id)129 void VideoCaptureBufferPoolImpl::RelinquishProducerReservation(int buffer_id) {
130   base::AutoLock lock(lock_);
131   VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
132   if (!tracker) {
133     NOTREACHED() << "Invalid buffer_id.";
134     return;
135   }
136   tracker->SetHeldByProducer(false);
137 }
138 
ReserveIdForExternalBuffer(const gfx::GpuMemoryBufferHandle & handle,int * buffer_id_to_drop)139 int VideoCaptureBufferPoolImpl::ReserveIdForExternalBuffer(
140     const gfx::GpuMemoryBufferHandle& handle,
141     int* buffer_id_to_drop) {
142   base::AutoLock lock(lock_);
143 
144   // Look for a tracker that matches this buffer and is not in use. While
145   // iterating, find the least recently used tracker.
146   *buffer_id_to_drop = kInvalidId;
147   auto lru_tracker_it = trackers_.end();
148   for (auto it = trackers_.begin(); it != trackers_.end(); ++it) {
149     VideoCaptureBufferTracker* const tracker = it->second.get();
150     if (tracker->IsHeldByProducerOrConsumer())
151       continue;
152 
153     if (tracker->IsSameGpuMemoryBuffer(handle)) {
154       tracker->SetHeldByProducer(true);
155       return it->first;
156     }
157 
158     if (lru_tracker_it == trackers_.end() ||
159         lru_tracker_it->second->LastCustomerUseSequenceNumber() >
160             tracker->LastCustomerUseSequenceNumber()) {
161       lru_tracker_it = it;
162     }
163   }
164 
165   // Free the least recently used tracker, if needed.
166   if (trackers_.size() >= static_cast<size_t>(count_) &&
167       lru_tracker_it != trackers_.end()) {
168     *buffer_id_to_drop = lru_tracker_it->first;
169     trackers_.erase(lru_tracker_it);
170   }
171 
172   // Create the new tracker.
173   const int new_buffer_id = next_buffer_id_++;
174   auto tracker =
175       buffer_tracker_factory_->CreateTrackerForExternalGpuMemoryBuffer(handle);
176   tracker->SetHeldByProducer(true);
177   trackers_[new_buffer_id] = std::move(tracker);
178   return new_buffer_id;
179 }
180 
HoldForConsumers(int buffer_id,int num_clients)181 void VideoCaptureBufferPoolImpl::HoldForConsumers(int buffer_id,
182                                                   int num_clients) {
183   base::AutoLock lock(lock_);
184   VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
185   if (!tracker) {
186     NOTREACHED() << "Invalid buffer_id.";
187     return;
188   }
189   tracker->AddConsumerHolds(num_clients);
190   // Note: The buffer will stay held by the producer until
191   // RelinquishProducerReservation() (usually called by destructor of the object
192   // wrapping this tracker, e.g. a VideoFrame).
193 }
194 
RelinquishConsumerHold(int buffer_id,int num_clients)195 void VideoCaptureBufferPoolImpl::RelinquishConsumerHold(int buffer_id,
196                                                         int num_clients) {
197   base::AutoLock lock(lock_);
198   VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
199   if (!tracker) {
200     NOTREACHED() << "Invalid buffer_id.";
201     return;
202   }
203   tracker->RemoveConsumerHolds(num_clients);
204 }
205 
GetBufferPoolUtilization() const206 double VideoCaptureBufferPoolImpl::GetBufferPoolUtilization() const {
207   base::AutoLock lock(lock_);
208   int num_buffers_held = 0;
209   for (const auto& entry : trackers_) {
210     VideoCaptureBufferTracker* const tracker = entry.second.get();
211     if (tracker->IsHeldByProducerOrConsumer())
212       ++num_buffers_held;
213   }
214   return static_cast<double>(num_buffers_held) / count_;
215 }
216 
217 VideoCaptureDevice::Client::ReserveResult
ReserveForProducerInternal(const gfx::Size & dimensions,VideoPixelFormat pixel_format,const mojom::PlaneStridesPtr & strides,int frame_feedback_id,int * buffer_id,int * buffer_id_to_drop)218 VideoCaptureBufferPoolImpl::ReserveForProducerInternal(
219     const gfx::Size& dimensions,
220     VideoPixelFormat pixel_format,
221     const mojom::PlaneStridesPtr& strides,
222     int frame_feedback_id,
223     int* buffer_id,
224     int* buffer_id_to_drop) {
225   lock_.AssertAcquired();
226 
227   // Look for a tracker that's allocated, big enough, and not in use. Track the
228   // largest one that's not big enough, in case we have to reallocate a tracker.
229   *buffer_id_to_drop = kInvalidId;
230   uint32_t largest_memory_size_in_bytes = 0;
231   auto tracker_to_drop = trackers_.end();
232   for (auto it = trackers_.begin(); it != trackers_.end(); ++it) {
233     VideoCaptureBufferTracker* const tracker = it->second.get();
234     if (!tracker->IsHeldByProducerOrConsumer()) {
235       if (tracker->IsReusableForFormat(dimensions, pixel_format, strides)) {
236         // Reuse this buffer
237         tracker->SetHeldByProducer(true);
238         tracker->set_frame_feedback_id(frame_feedback_id);
239         *buffer_id = it->first;
240         return VideoCaptureDevice::Client::ReserveResult::kSucceeded;
241       }
242       if (tracker->GetMemorySizeInBytes() > largest_memory_size_in_bytes) {
243         largest_memory_size_in_bytes = tracker->GetMemorySizeInBytes();
244         tracker_to_drop = it;
245       }
246     }
247   }
248 
249   // Preferably grow the pool by creating a new tracker. If we're at maximum
250   // size, reallocate by deleting an existing one.
251   if (trackers_.size() == static_cast<size_t>(count_)) {
252     if (tracker_to_drop == trackers_.end()) {
253       // We're out of space, and can't find an unused tracker to reallocate.
254       *buffer_id = kInvalidId;
255       DLOG(ERROR) << __func__
256                   << " max buffer count exceeded count_ = " << count_;
257       return VideoCaptureDevice::Client::ReserveResult::kMaxBufferCountExceeded;
258     }
259     *buffer_id_to_drop = tracker_to_drop->first;
260     trackers_.erase(tracker_to_drop);
261   }
262 
263   // Create the new tracker.
264   const int new_buffer_id = next_buffer_id_++;
265 
266   std::unique_ptr<VideoCaptureBufferTracker> tracker =
267       buffer_tracker_factory_->CreateTracker(buffer_type_);
268   if (!tracker->Init(dimensions, pixel_format, strides)) {
269     DLOG(ERROR) << "Error initializing VideoCaptureBufferTracker";
270     *buffer_id = kInvalidId;
271     return VideoCaptureDevice::Client::ReserveResult::kAllocationFailed;
272   }
273 
274   tracker->SetHeldByProducer(true);
275   tracker->set_frame_feedback_id(frame_feedback_id);
276   trackers_[new_buffer_id] = std::move(tracker);
277 
278   *buffer_id = new_buffer_id;
279   return VideoCaptureDevice::Client::ReserveResult::kSucceeded;
280 }
281 
GetTracker(int buffer_id)282 VideoCaptureBufferTracker* VideoCaptureBufferPoolImpl::GetTracker(
283     int buffer_id) {
284   auto it = trackers_.find(buffer_id);
285   return (it == trackers_.end()) ? nullptr : it->second.get();
286 }
287 
288 }  // namespace media
289