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