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 |