| OLD | NEW |
| (Empty) |
| 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 "content/browser/renderer_host/media/video_capture_buffer_pool.h" | |
| 6 | |
| 7 #include <memory> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "base/memory/ptr_util.h" | |
| 11 #include "base/stl_util.h" | |
| 12 #include "build/build_config.h" | |
| 13 #include "content/browser/renderer_host/media/video_capture_buffer_handle.h" | |
| 14 #include "content/browser/renderer_host/media/video_capture_buffer_tracker.h" | |
| 15 #include "content/browser/renderer_host/media/video_capture_buffer_tracker_facto
ry.h" | |
| 16 #include "ui/gfx/buffer_format_util.h" | |
| 17 | |
| 18 namespace content { | |
| 19 | |
| 20 VideoCaptureBufferPoolImpl::VideoCaptureBufferPoolImpl(int count) | |
| 21 : count_(count), | |
| 22 next_buffer_id_(0), | |
| 23 last_relinquished_buffer_id_(kInvalidId) { | |
| 24 DCHECK_GT(count, 0); | |
| 25 } | |
| 26 | |
| 27 VideoCaptureBufferPoolImpl::~VideoCaptureBufferPoolImpl() { | |
| 28 base::STLDeleteValues(&trackers_); | |
| 29 } | |
| 30 | |
| 31 bool VideoCaptureBufferPoolImpl::ShareToProcess( | |
| 32 int buffer_id, | |
| 33 base::ProcessHandle process_handle, | |
| 34 base::SharedMemoryHandle* new_handle) { | |
| 35 base::AutoLock lock(lock_); | |
| 36 | |
| 37 VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); | |
| 38 if (!tracker) { | |
| 39 NOTREACHED() << "Invalid buffer_id."; | |
| 40 return false; | |
| 41 } | |
| 42 if (tracker->ShareToProcess(process_handle, new_handle)) | |
| 43 return true; | |
| 44 DPLOG(ERROR) << "Error mapping memory"; | |
| 45 return false; | |
| 46 } | |
| 47 | |
| 48 bool VideoCaptureBufferPoolImpl::ShareToProcess2( | |
| 49 int buffer_id, | |
| 50 int plane, | |
| 51 base::ProcessHandle process_handle, | |
| 52 gfx::GpuMemoryBufferHandle* new_handle) { | |
| 53 base::AutoLock lock(lock_); | |
| 54 | |
| 55 VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); | |
| 56 if (!tracker) { | |
| 57 NOTREACHED() << "Invalid buffer_id."; | |
| 58 return false; | |
| 59 } | |
| 60 if (tracker->ShareToProcess2(plane, process_handle, new_handle)) | |
| 61 return true; | |
| 62 DPLOG(ERROR) << "Error mapping memory"; | |
| 63 return false; | |
| 64 } | |
| 65 | |
| 66 std::unique_ptr<VideoCaptureBufferHandle> | |
| 67 VideoCaptureBufferPoolImpl::GetBufferHandle(int buffer_id) { | |
| 68 base::AutoLock lock(lock_); | |
| 69 | |
| 70 VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); | |
| 71 if (!tracker) { | |
| 72 NOTREACHED() << "Invalid buffer_id."; | |
| 73 return std::unique_ptr<VideoCaptureBufferHandle>(); | |
| 74 } | |
| 75 | |
| 76 DCHECK(tracker->held_by_producer()); | |
| 77 return tracker->GetBufferHandle(); | |
| 78 } | |
| 79 | |
| 80 int VideoCaptureBufferPoolImpl::ReserveForProducer( | |
| 81 const gfx::Size& dimensions, | |
| 82 media::VideoPixelFormat format, | |
| 83 media::VideoPixelStorage storage, | |
| 84 int* buffer_id_to_drop) { | |
| 85 base::AutoLock lock(lock_); | |
| 86 return ReserveForProducerInternal(dimensions, format, storage, | |
| 87 buffer_id_to_drop); | |
| 88 } | |
| 89 | |
| 90 void VideoCaptureBufferPoolImpl::RelinquishProducerReservation(int buffer_id) { | |
| 91 base::AutoLock lock(lock_); | |
| 92 VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); | |
| 93 if (!tracker) { | |
| 94 NOTREACHED() << "Invalid buffer_id."; | |
| 95 return; | |
| 96 } | |
| 97 DCHECK(tracker->held_by_producer()); | |
| 98 tracker->set_held_by_producer(false); | |
| 99 last_relinquished_buffer_id_ = buffer_id; | |
| 100 } | |
| 101 | |
| 102 void VideoCaptureBufferPoolImpl::HoldForConsumers(int buffer_id, | |
| 103 int num_clients) { | |
| 104 base::AutoLock lock(lock_); | |
| 105 VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); | |
| 106 if (!tracker) { | |
| 107 NOTREACHED() << "Invalid buffer_id."; | |
| 108 return; | |
| 109 } | |
| 110 DCHECK(tracker->held_by_producer()); | |
| 111 DCHECK(!tracker->consumer_hold_count()); | |
| 112 | |
| 113 tracker->set_consumer_hold_count(num_clients); | |
| 114 // Note: |held_by_producer()| will stay true until | |
| 115 // RelinquishProducerReservation() (usually called by destructor of the object | |
| 116 // wrapping this tracker, e.g. a media::VideoFrame). | |
| 117 } | |
| 118 | |
| 119 void VideoCaptureBufferPoolImpl::RelinquishConsumerHold(int buffer_id, | |
| 120 int num_clients) { | |
| 121 base::AutoLock lock(lock_); | |
| 122 VideoCaptureBufferTracker* tracker = GetTracker(buffer_id); | |
| 123 if (!tracker) { | |
| 124 NOTREACHED() << "Invalid buffer_id."; | |
| 125 return; | |
| 126 } | |
| 127 DCHECK_GE(tracker->consumer_hold_count(), num_clients); | |
| 128 | |
| 129 tracker->set_consumer_hold_count(tracker->consumer_hold_count() - | |
| 130 num_clients); | |
| 131 } | |
| 132 | |
| 133 int VideoCaptureBufferPoolImpl::ResurrectLastForProducer( | |
| 134 const gfx::Size& dimensions, | |
| 135 media::VideoPixelFormat format, | |
| 136 media::VideoPixelStorage storage) { | |
| 137 base::AutoLock lock(lock_); | |
| 138 | |
| 139 // Return early if the last relinquished buffer has been re-used already. | |
| 140 if (last_relinquished_buffer_id_ == kInvalidId) | |
| 141 return kInvalidId; | |
| 142 | |
| 143 // If there are no consumers reading from this buffer, then it's safe to | |
| 144 // provide this buffer back to the producer (because the producer may | |
| 145 // potentially modify the content). Check that the expected dimensions, | |
| 146 // format, and storage match. | |
| 147 TrackerMap::iterator it = trackers_.find(last_relinquished_buffer_id_); | |
| 148 DCHECK(it != trackers_.end()); | |
| 149 DCHECK(!it->second->held_by_producer()); | |
| 150 if (it->second->consumer_hold_count() == 0 && | |
| 151 it->second->dimensions() == dimensions && | |
| 152 it->second->pixel_format() == format && | |
| 153 it->second->storage_type() == storage) { | |
| 154 it->second->set_held_by_producer(true); | |
| 155 const int resurrected_buffer_id = last_relinquished_buffer_id_; | |
| 156 last_relinquished_buffer_id_ = kInvalidId; | |
| 157 return resurrected_buffer_id; | |
| 158 } | |
| 159 | |
| 160 return kInvalidId; | |
| 161 } | |
| 162 | |
| 163 double VideoCaptureBufferPoolImpl::GetBufferPoolUtilization() const { | |
| 164 base::AutoLock lock(lock_); | |
| 165 int num_buffers_held = 0; | |
| 166 for (const auto& entry : trackers_) { | |
| 167 VideoCaptureBufferTracker* const tracker = entry.second; | |
| 168 if (tracker->held_by_producer() || tracker->consumer_hold_count() > 0) | |
| 169 ++num_buffers_held; | |
| 170 } | |
| 171 return static_cast<double>(num_buffers_held) / count_; | |
| 172 } | |
| 173 | |
| 174 int VideoCaptureBufferPoolImpl::ReserveForProducerInternal( | |
| 175 const gfx::Size& dimensions, | |
| 176 media::VideoPixelFormat pixel_format, | |
| 177 media::VideoPixelStorage storage_type, | |
| 178 int* buffer_id_to_drop) { | |
| 179 lock_.AssertAcquired(); | |
| 180 | |
| 181 const size_t size_in_pixels = dimensions.GetArea(); | |
| 182 // Look for a tracker that's allocated, big enough, and not in use. Track the | |
| 183 // largest one that's not big enough, in case we have to reallocate a tracker. | |
| 184 *buffer_id_to_drop = kInvalidId; | |
| 185 size_t largest_size_in_pixels = 0; | |
| 186 TrackerMap::iterator tracker_of_last_resort = trackers_.end(); | |
| 187 TrackerMap::iterator tracker_to_drop = trackers_.end(); | |
| 188 for (TrackerMap::iterator it = trackers_.begin(); it != trackers_.end(); | |
| 189 ++it) { | |
| 190 VideoCaptureBufferTracker* const tracker = it->second; | |
| 191 if (!tracker->consumer_hold_count() && !tracker->held_by_producer()) { | |
| 192 if (tracker->max_pixel_count() >= size_in_pixels && | |
| 193 (tracker->pixel_format() == pixel_format) && | |
| 194 (tracker->storage_type() == storage_type)) { | |
| 195 if (it->first == last_relinquished_buffer_id_) { | |
| 196 // This buffer would do just fine, but avoid returning it because the | |
| 197 // client may want to resurrect it. It will be returned perforce if | |
| 198 // the pool has reached it's maximum limit (see code below). | |
| 199 tracker_of_last_resort = it; | |
| 200 continue; | |
| 201 } | |
| 202 // Existing tracker is big enough and has correct format. Reuse it. | |
| 203 tracker->set_dimensions(dimensions); | |
| 204 tracker->set_held_by_producer(true); | |
| 205 return it->first; | |
| 206 } | |
| 207 if (tracker->max_pixel_count() > largest_size_in_pixels) { | |
| 208 largest_size_in_pixels = tracker->max_pixel_count(); | |
| 209 tracker_to_drop = it; | |
| 210 } | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 // Preferably grow the pool by creating a new tracker. If we're at maximum | |
| 215 // size, then try using |tracker_of_last_resort| or reallocate by deleting an | |
| 216 // existing one instead. | |
| 217 if (trackers_.size() == static_cast<size_t>(count_)) { | |
| 218 if (tracker_of_last_resort != trackers_.end()) { | |
| 219 last_relinquished_buffer_id_ = kInvalidId; | |
| 220 tracker_of_last_resort->second->set_dimensions(dimensions); | |
| 221 tracker_of_last_resort->second->set_held_by_producer(true); | |
| 222 return tracker_of_last_resort->first; | |
| 223 } | |
| 224 if (tracker_to_drop == trackers_.end()) { | |
| 225 // We're out of space, and can't find an unused tracker to reallocate. | |
| 226 return kInvalidId; | |
| 227 } | |
| 228 if (tracker_to_drop->first == last_relinquished_buffer_id_) | |
| 229 last_relinquished_buffer_id_ = kInvalidId; | |
| 230 *buffer_id_to_drop = tracker_to_drop->first; | |
| 231 delete tracker_to_drop->second; | |
| 232 trackers_.erase(tracker_to_drop); | |
| 233 } | |
| 234 | |
| 235 // Create the new tracker. | |
| 236 const int buffer_id = next_buffer_id_++; | |
| 237 | |
| 238 std::unique_ptr<VideoCaptureBufferTracker> tracker = | |
| 239 VideoCaptureBufferTrackerFactory::CreateTracker(storage_type); | |
| 240 // TODO(emircan): We pass the lock here to solve GMB allocation issue, see | |
| 241 // crbug.com/545238. | |
| 242 if (!tracker->Init(dimensions, pixel_format, storage_type, &lock_)) { | |
| 243 DLOG(ERROR) << "Error initializing VideoCaptureBufferTracker"; | |
| 244 return kInvalidId; | |
| 245 } | |
| 246 | |
| 247 tracker->set_held_by_producer(true); | |
| 248 trackers_[buffer_id] = tracker.release(); | |
| 249 | |
| 250 return buffer_id; | |
| 251 } | |
| 252 | |
| 253 VideoCaptureBufferTracker* VideoCaptureBufferPoolImpl::GetTracker( | |
| 254 int buffer_id) { | |
| 255 TrackerMap::const_iterator it = trackers_.find(buffer_id); | |
| 256 return (it == trackers_.end()) ? NULL : it->second; | |
| 257 } | |
| 258 | |
| 259 } // namespace content | |
| OLD | NEW |