Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 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/blink/multibuffer.h" | |
| 6 | |
| 7 namespace media { | |
| 8 | |
| 9 template<class T> | |
| 10 static int32_t ClosestPreviousEntry(const std::map<int32_t, T>& index, | |
| 11 int32_t pos) { | |
| 12 typename std::map<int32_t, T>::const_iterator i = index.upper_bound(pos); | |
| 13 if (i == index.begin()) { | |
| 14 return -1; | |
| 15 } | |
| 16 --i; | |
| 17 return i->first; | |
| 18 } | |
| 19 | |
| 20 MultiBuffer::MultiBuffer(int32_t block_size_shift) : | |
| 21 size_(0), | |
| 22 max_size_(0), | |
| 23 block_size_shift_(block_size_shift) { | |
| 24 } | |
| 25 MultiBuffer::~MultiBuffer() { | |
| 26 writer_index_.DeleteWaiters(); | |
| 27 } | |
| 28 | |
| 29 void MultiBuffer::AddWriter(int32_t pos, Waiter* writer) { | |
| 30 writer_index_.WaitFor(pos, writer); | |
| 31 } | |
| 32 | |
| 33 void MultiBuffer::RemoveWriter(int32_t pos, Waiter* writer) { | |
| 34 writer_index_.StopWaitFor(pos, writer); | |
| 35 } | |
| 36 | |
| 37 | |
| 38 void MultiBuffer::WaitFor(int32_t pos, Waiter* reader) { | |
| 39 DCHECK(pinned_[pos]); | |
| 40 DCHECK(data_.find(pos) == data_.end()); | |
| 41 reader_index_.WaitFor(pos, reader); | |
| 42 | |
| 43 int32_t closest_writer = ClosestPreviousEntry(writer_index_.map(), pos); | |
| 44 int32_t closest_block = ClosestPreviousEntry(data_, pos); | |
| 45 if (closest_block >= closest_writer) { | |
| 46 closest_writer = -1; | |
| 47 } | |
| 48 if (closest_writer != -1 && | |
| 49 pos - closest_writer < kMaxWaitForWriterOffset) { | |
| 50 // There is already a write available, wake it up. | |
| 51 writer_index_.WakeUp(closest_writer); | |
| 52 } else { | |
| 53 StartWriter(pos); | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 void MultiBuffer::DeferredWaitFor(int32_t pos, Waiter* reader) { | |
| 58 DCHECK(pinned_[pos]); | |
| 59 DCHECK(data_.find(pos) == data_.end()); | |
| 60 deferred_reader_index_.WaitFor(pos, reader); | |
| 61 } | |
| 62 | |
| 63 void MultiBuffer::StopWaitFor(int32_t pos, Waiter* reader) { | |
| 64 reader_index_.StopWaitFor(pos, reader); | |
| 65 deferred_reader_index_.StopWaitFor(pos, reader); | |
| 66 } | |
| 67 | |
| 68 bool MultiBuffer::Contains(int32_t pos) const { | |
| 69 return data_.find(pos) != data_.end(); | |
| 70 } | |
| 71 | |
| 72 bool MultiBuffer::WantNow(int32_t pos) const { | |
| 73 return reader_index_.map().find(pos) != reader_index_.map().end(); | |
| 74 } | |
| 75 | |
| 76 bool MultiBuffer::WantSoon(int32_t pos) const { | |
| 77 return deferred_reader_index_.map().find(pos) != | |
| 78 deferred_reader_index_.map().end(); | |
| 79 } | |
| 80 | |
| 81 void MultiBuffer::Prune() { | |
| 82 int count = 0; | |
| 83 while (size_ > max_size_ && !lru_.Empty()) { | |
| 84 int32_t to_free = lru_.Pop(); | |
| 85 DCHECK(data_[to_free]); | |
| 86 DCHECK_EQ(pinned_[to_free], 0); | |
| 87 data_.erase(to_free); | |
| 88 size_--; | |
| 89 if (++count > kMaxFreesPerAdd) break; | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 void MultiBuffer::AddData(int32_t pos, scoped_refptr<DataBuffer> data) { | |
| 94 if (data_[pos]) { | |
| 95 // We already have this data. | |
| 96 return; | |
| 97 } | |
| 98 data_[pos] = data; | |
| 99 if (!pinned_[pos]) { | |
| 100 lru_.Insert(pos); | |
| 101 } | |
| 102 size_++; | |
| 103 Prune(); | |
| 104 reader_index_.WakeUp(pos); | |
| 105 } | |
| 106 | |
| 107 void MultiBuffer::PinRange(int32_t from, int32_t to, int32_t howmuch) { | |
| 108 pinned_.IncrementRange(from, to, howmuch); | |
| 109 | |
| 110 // Iterate over all the modified ranges and check if | |
| 111 // any of them have transitioned in or out of the | |
| 112 // unlocked state. If so, we iterate over all buffers | |
| 113 // in that range and add/remove them from the LRU as | |
| 114 // approperiate. We iterate *backwards* through the | |
| 115 // ranges, with the idea that data in a continous range | |
| 116 // should be freed from the end first. | |
| 117 RangeMap<int32_t, int32_t>::MapType::const_iterator i; | |
| 118 i = pinned_.map().lower_bound(to); | |
| 119 while (i != pinned_.map().begin()) { | |
| 120 if (i->first < from) break; | |
| 121 RangeMap<int32_t, int32_t>::MapType::const_iterator j = i; | |
| 122 i--; | |
| 123 if (i->second == 0 || i->second == howmuch) { | |
| 124 bool pin = i->second == howmuch; | |
| 125 int32_t begin = std::max(i->first, from); | |
| 126 int32_t end = j == pinned_.map().end() ? to : std::min(to, j->first); | |
| 127 DataMap::iterator k = data_.lower_bound(end); | |
| 128 while (k != data_.begin()) { | |
| 129 k--; | |
| 130 if (k->first < begin) break; | |
| 131 if (pin) { | |
| 132 lru_.Remove(k->first); | |
| 133 } else { | |
| 134 lru_.Insert(k->first); | |
| 135 } | |
| 136 } | |
| 137 } | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 void MultiBuffer::IncrementMaxSize(int32_t size) { | |
| 142 max_size_ += size; | |
| 143 DCHECK_GE(max_size_, 0); | |
| 144 // Pruning only happens when blocks are added. | |
| 145 } | |
| 146 | |
| 147 // MultiBufferWriter | |
| 148 | |
| 149 MultiBufferWriter::MultiBufferWriter(MultiBuffer* multibuffer, int32_t pos) : | |
| 150 multibuffer_(multibuffer), | |
| 151 pos_(pos) { | |
| 152 multibuffer_->AddWriter(pos_, this); | |
| 153 } | |
| 154 | |
| 155 MultiBufferWriter::~MultiBufferWriter() { | |
| 156 multibuffer_->RemoveWriter(pos_, this); | |
| 157 } | |
| 158 | |
| 159 void MultiBufferWriter::Write(scoped_refptr<DataBuffer> data) { | |
| 160 multibuffer_->RemoveWriter(pos_, this); | |
| 161 if (multibuffer_->Contains(pos_)) { | |
| 162 delete this; | |
| 163 return; | |
| 164 } | |
| 165 multibuffer_->AddData(pos_, data); | |
| 166 pos_++; | |
| 167 multibuffer_->AddWriter(pos_, this); | |
|
liberato (no reviews please)
2015/06/09 15:00:53
it seems like there are many calls to AddWriter()
hubbe
2015/06/09 21:23:48
They seem evenly matched to me...
liberato (no reviews please)
2015/06/09 21:50:47
Indeed, you're right. i missed the first line of
| |
| 168 if (!multibuffer_->Contains(pos_)) { | |
| 169 if (multibuffer_->WantNow(pos_)) { | |
| 170 return; | |
| 171 } | |
| 172 if (multibuffer_->WantSoon(pos_)) { | |
| 173 SetDeferred(true); | |
| 174 return; | |
| 175 } | |
| 176 } | |
| 177 delete this; | |
|
liberato (no reviews please)
2015/06/09 15:00:53
who calls Write()? how would they know that it ch
hubbe
2015/06/09 21:23:48
Updated comments in header file, hope it explains.
| |
| 178 } | |
| 179 | |
| 180 void MultiBufferWriter::Continue() { | |
| 181 SetDeferred(false); | |
| 182 } | |
| 183 | |
| 184 | |
| 185 MultiBufferReader::MultiBufferReader(MultiBuffer* multibuffer, | |
| 186 int64_t start, | |
| 187 int64_t end, | |
| 188 int64_t preload, | |
| 189 int64_t max_buffer_forward, | |
| 190 int64_t max_buffer_backward) : | |
| 191 multibuffer_(multibuffer), | |
| 192 pos_(start), | |
| 193 end_(end), | |
| 194 preload_(preload), | |
| 195 max_buffer_forward_(max_buffer_forward), | |
| 196 max_buffer_backward_(max_buffer_backward) { | |
| 197 multibuffer_->IncrementMaxSize( | |
| 198 block_ciel(max_buffer_forward_ + max_buffer_backward_)); | |
| 199 multibuffer_->PinRange( | |
| 200 block(std::max<int64_t>(0, pos_ - max_buffer_backward_)), | |
| 201 block_ciel(std::min(pos_ + max_buffer_forward_, end_)), | |
| 202 1); | |
| 203 IncrementPreloadPos(); | |
| 204 } | |
| 205 | |
| 206 MultiBufferReader::~MultiBufferReader() { | |
| 207 multibuffer_->StopWaitFor(preload_pos_, this); | |
| 208 multibuffer_->IncrementMaxSize( | |
| 209 -block_ciel(max_buffer_forward_ + max_buffer_backward_)); | |
| 210 multibuffer_->PinRange( | |
| 211 block(std::max<int64_t>(0, pos_ - max_buffer_backward_)), | |
| 212 block_ciel(std::min(pos_ + max_buffer_forward_, end_)), | |
| 213 -1); | |
| 214 } | |
| 215 | |
| 216 int32_t MultiBufferReader::block(int64_t byte_pos) const { | |
| 217 return byte_pos << multibuffer_->block_size_shift(); | |
| 218 } | |
| 219 | |
| 220 int32_t MultiBufferReader::block_ciel(int64_t byte_pos) const { | |
| 221 return block(byte_pos + (1 << multibuffer_->block_size_shift()) - 1); | |
| 222 } | |
| 223 | |
| 224 void MultiBufferReader::Seek(int64_t pos) { | |
| 225 DCHECK(cb_.is_null()); | |
| 226 RangeMap<int32_t, int32_t> tmp; | |
| 227 tmp.IncrementRange(block(std::max<int64_t>(0, pos_ - max_buffer_backward_)), | |
| 228 block_ciel(std::min(pos_ + max_buffer_forward_, end_)), | |
| 229 -1); | |
| 230 | |
| 231 // If we're seeking to somewhere between pos_ and | |
| 232 // preload_pos_, then we don't need to reset preload_pos_. | |
| 233 if (pos < pos_ || block(pos) >= preload_pos_) { | |
| 234 multibuffer_->StopWaitFor(preload_pos_, this); | |
| 235 preload_pos_ = pos_; | |
| 236 } | |
| 237 pos_ = pos; | |
| 238 IncrementPreloadPos(); | |
| 239 | |
| 240 tmp.IncrementRange(block(std::max<int64_t>(0, pos_ - max_buffer_backward_)), | |
| 241 block_ciel(std::min(pos_ + max_buffer_forward_, end_)), | |
|
liberato (no reviews please)
2015/06/09 15:00:53
nit: s/ciel/ceil/
hubbe
2015/06/09 21:23:48
Done.
| |
| 242 1); | |
| 243 RangeMap<int32_t, int32_t>::MapType::const_iterator i, j; | |
| 244 for (i = tmp.map().begin(); i != tmp.map().end(); ++i) { | |
| 245 if (i->second != 0) { | |
| 246 j = i; | |
| 247 ++j; | |
| 248 multibuffer_->PinRange(i->first, j->first, i->second); | |
| 249 } | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 int64_t MultiBufferReader::Available() const { | |
| 254 uint64_t preload_byte_pos = | |
| 255 preload_pos_ << multibuffer_->block_size_shift(); | |
| 256 return preload_byte_pos - pos_; | |
| 257 } | |
| 258 | |
| 259 bool MultiBufferReader::TryRead(unsigned char *data, int64_t len) { | |
| 260 if (Available() < len) { | |
| 261 return false; | |
| 262 } | |
| 263 const MultiBuffer::DataMap& data_map = multibuffer_->map(); | |
| 264 MultiBuffer::DataMap::const_iterator i = data_map.find(block(pos_)); | |
| 265 DCHECK(i != data_map.end()); | |
| 266 int64_t p = pos_; | |
| 267 while (len) { | |
| 268 DCHECK_EQ(i->first, block(pos_)); | |
| 269 size_t offset = p & ((1 << multibuffer_->block_size_shift()) - 1); | |
| 270 size_t tocopy = i->second->data_size() - offset; | |
| 271 memcpy(data, i->second->data() + offset, tocopy); | |
| 272 data += tocopy; | |
| 273 len -= tocopy; | |
| 274 p += tocopy; | |
| 275 ++i; | |
|
liberato (no reviews please)
2015/06/09 15:00:53
i'm not sure that this doesn't sometimes walk righ
hubbe
2015/06/09 21:23:48
Hmm, why are you not sure?
What can I do to make y
liberato (no reviews please)
2015/06/09 21:50:47
i'm not sure. :)
the available() test is suppose
hubbe
2015/06/09 23:14:12
What you're describing should never happen because
| |
| 276 } | |
| 277 Seek(p); | |
| 278 return true; | |
| 279 } | |
| 280 | |
| 281 void MultiBufferReader::Wait(int64_t len, base::Closure cb) { | |
| 282 DCHECK_LE(Available(), len); | |
| 283 DCHECK_LE(len, max_buffer_forward_); | |
| 284 current_wait_size_ = len; | |
| 285 cb_ = cb; | |
| 286 IncrementPreloadPos(); | |
| 287 } | |
| 288 | |
| 289 void MultiBufferReader::Continue() { | |
| 290 IncrementPreloadPos(); | |
| 291 } | |
| 292 | |
| 293 void MultiBufferReader::CheckWait() { | |
| 294 if (!cb_.is_null() && Available() >= current_wait_size_) { | |
| 295 base::Closure cb; | |
| 296 std::swap(cb_, cb); | |
| 297 current_wait_size_ = 0; | |
| 298 cb.Run(); | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 void MultiBufferReader::IncrementPreloadPos() { | |
| 303 int64_t max_preload = block_ciel( | |
| 304 std::min(end_, pos_ + std::max(preload_, current_wait_size_))); | |
| 305 | |
| 306 if (preload_pos_ < max_preload) { | |
| 307 MultiBuffer::DataMap::const_iterator i = | |
| 308 multibuffer_->map().find(preload_pos_); | |
| 309 if (i != multibuffer_->map().end()) { | |
| 310 multibuffer_->StopWaitFor(preload_pos_, this); | |
| 311 preload_pos_++; | |
| 312 ++i; | |
| 313 while (preload_pos_ < max_preload && | |
| 314 i != multibuffer_->map().end() && | |
| 315 i->first == preload_pos_) { | |
| 316 preload_pos_++; | |
| 317 ++i; | |
| 318 } | |
| 319 } | |
| 320 } | |
| 321 if (preload_pos_ < max_preload) { | |
| 322 multibuffer_->WaitFor(preload_pos_, this); | |
| 323 } else if (preload_pos_ < end_) { | |
| 324 multibuffer_->DeferredWaitFor(preload_pos_, this); | |
| 325 } | |
| 326 CheckWait(); | |
| 327 } | |
| 328 | |
| 329 } // namespace media | |
| OLD | NEW |