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 #include "base/bind.h" | |
8 | |
9 namespace std { | |
10 | |
11 ostream& operator<<(ostream& o, const media::MultiBufferBlockId& id) { | |
12 if (id.url_data()) { | |
13 return o << "{" << id.url_data()->url() << ", " << id.block_num() << "}"; | |
14 } else { | |
15 return o << "{null, " << id.block_num() << "}"; | |
16 } | |
17 } | |
18 | |
19 } // namespace std | |
20 | |
21 namespace media { | |
22 | |
23 MultiBufferBlockId::MultiBufferBlockId() : | |
24 url_data_(nullptr), | |
25 block_num_(0) { | |
26 } | |
27 MultiBufferBlockId::MultiBufferBlockId(MultiBufferUrlData url_data, | |
28 MultiBufferBlockNum block_num) : | |
29 url_data_(url_data), | |
30 block_num_(block_num) { | |
31 } | |
32 | |
33 MultiBufferBlockId::MultiBufferBlockId(const MultiBufferBlockId& block_id) : | |
34 url_data_(block_id.url_data()), | |
35 block_num_(block_id.block_num()) { | |
36 } | |
37 | |
38 MultiBufferBlockId::~MultiBufferBlockId() {} | |
39 | |
40 // Returns the block ID closest to (but less or equal than) |pos| from |index|. | |
41 template<class T> | |
42 static MultiBuffer::BlockId ClosestPreviousEntry( | |
43 const std::map<MultiBuffer::BlockId, T>& index, | |
44 MultiBuffer::BlockId pos) { | |
45 auto i = index.upper_bound(pos); | |
46 DCHECK(i == index.end() || i->first > pos); | |
47 if (i == index.begin()) { | |
48 return MultiBufferBlockId(); | |
49 } | |
50 --i; | |
51 if (!i->first.SameUrl(pos)) { | |
52 return MultiBufferBlockId(); | |
53 } | |
54 DCHECK_LE(i->first, pos); | |
55 return i->first; | |
56 } | |
57 | |
58 // Returns the block ID closest to (but greter than or equal to) |pos| | |
59 // from |index|. | |
60 template<class T> | |
61 static MultiBuffer::BlockId ClosestNextEntry( | |
62 const std::map<MultiBuffer::BlockId, T>& index, | |
63 MultiBuffer::BlockId pos) { | |
64 auto i = index.lower_bound(pos); | |
65 if (i == index.end()) { | |
66 return MultiBufferBlockId(); | |
67 } | |
68 if (!i->first.SameUrl(pos)) { | |
69 return MultiBufferBlockId(); | |
70 } | |
71 DCHECK_GE(i->first, pos); | |
72 return i->first; | |
73 } | |
74 | |
75 // | |
76 // MultiBuffer | |
77 // | |
78 MultiBuffer::MultiBuffer(int32_t block_size_shift) : | |
79 max_size_(0), | |
80 block_size_shift_(block_size_shift) { | |
81 } | |
82 | |
83 MultiBuffer::~MultiBuffer() { | |
84 // Delete all writers. | |
85 for (const auto& i : writer_index_) { | |
86 delete i.second; | |
87 } | |
88 } | |
89 | |
90 void MultiBuffer::AddReader(const BlockId& pos, Reader* reader) { | |
91 std::set<Reader*> &set_of_readers = readers_[pos]; | |
92 bool already_waited_for = !set_of_readers.empty(); | |
93 set_of_readers.insert(reader); | |
94 | |
95 if (already_waited_for || Contains(pos)) { | |
96 return; | |
97 } | |
98 | |
99 // We may need to create a new data provider to service this request. | |
100 // Look for an existing data provider first. | |
101 DataProvider* provider = NULL; | |
102 BlockId closest_writer = ClosestPreviousEntry(writer_index_, pos); | |
103 | |
104 if (pos - closest_writer < kMaxWaitForWriterOffset) { | |
105 BlockId closest_block = ClosestPreviousEntry(data_, pos); | |
106 if (pos - closest_block > pos - closest_writer) { | |
107 provider = writer_index_[closest_writer]; | |
108 DCHECK(provider); | |
109 } | |
110 } | |
111 if (!provider) { | |
112 provider = writer_index_[pos] = CreateWriter(pos); | |
113 provider->SetAvailableCallback( | |
114 base::Bind(&MultiBuffer::DataProviderEvent, | |
115 base::Unretained(this), | |
116 base::Unretained(provider))); | |
117 } | |
118 provider->SetDeferred(false); | |
119 } | |
120 | |
121 void MultiBuffer::RemoveReader(const BlockId& pos, Reader* reader) { | |
122 auto i = readers_.find(pos); | |
123 if (i == readers_.end()) | |
124 return; | |
125 i->second.erase(reader); | |
126 if (i->second.empty()) { | |
127 readers_.erase(i); | |
128 } | |
129 } | |
130 | |
131 void MultiBuffer::CleanupWriters(const BlockId& pos) { | |
132 BlockId closest_writer = ClosestPreviousEntry(writer_index_, pos); | |
133 if (closest_writer == BlockId()) | |
134 return; | |
135 if (pos - closest_writer > kMaxWaitForWriterOffset) | |
136 return; | |
137 DCHECK(writer_index_[closest_writer]); | |
138 DataProviderEvent(writer_index_[closest_writer]); | |
139 } | |
140 | |
141 bool MultiBuffer::Contains(const BlockId& pos) const { | |
142 DCHECK(present_[pos] == 0 || present_[pos] == 1) | |
143 << " pos = " << pos | |
144 << " present_[pos] " << present_[pos]; | |
145 DCHECK_EQ(present_[pos], data_.find(pos) != data_.end() ? 1 : 0); | |
146 return !!present_[pos]; | |
147 } | |
148 | |
149 MultiBufferBlockId MultiBuffer::FindNextUnavailable(const BlockId& pos) const { | |
150 auto i = present_.find(pos); | |
151 if (i.value()) { | |
152 return i.range_end(); | |
153 } else { | |
DaleCurtis
2015/10/19 21:45:25
Don't use else w/ return. Ternary?
hubbe
2015/10/20 00:31:39
I'm not a big fan of terniary.
Is this better?
| |
154 return pos; | |
155 } | |
156 } | |
157 | |
158 void MultiBuffer::NotifyAvailableRange( | |
159 const Range<MultiBufferBlockId>& observer_range, | |
160 const Range<MultiBufferBlockId>& new_range) { | |
161 std::set<Reader*> tmp; | |
162 for (auto i = readers_.lower_bound(observer_range.begin); | |
163 i != readers_.end() && i->first < observer_range.end; | |
164 ++i) { | |
165 tmp.insert(i->second.begin(), i->second.end()); | |
166 } | |
167 for (Reader* reader: tmp) { | |
168 reader->NotifyAvailableRange(new_range); | |
169 } | |
170 } | |
171 | |
172 void MultiBuffer::Prune(size_t max_to_free) { | |
173 // Use a rangemap to merge all consequtive frees into | |
174 // ranges, then notify observers of changes to those ranges. | |
175 RangeMap<BlockId, int32_t> freed; | |
176 while (static_cast<int64_t>(data_.size()) > max_size_ && | |
177 !lru_.Empty() && | |
178 max_to_free > 0) { | |
179 BlockId to_free = lru_.Pop(); | |
180 DCHECK(data_[to_free]); | |
181 DCHECK_EQ(pinned_[to_free], 0); | |
182 DCHECK_EQ(present_[to_free], 1); | |
183 data_.erase(to_free); | |
184 freed.IncrementRange(to_free, to_free + 1, 1); | |
185 present_.IncrementRange(to_free, to_free + 1, -1); | |
186 max_to_free--; | |
187 } | |
188 | |
189 for (auto freed_iter = freed.first_range(); | |
190 freed_iter != freed.last_range(); | |
191 ++freed_iter) { | |
192 if (freed_iter.value()) { | |
193 // Technically, there shouldn't be any observers in this range | |
194 // as all observers really should be pinning the range where it's | |
195 // actually observing. | |
196 NotifyAvailableRange( | |
197 freed_iter.range(), | |
198 // Empty range. | |
199 Range<BlockId>(freed_iter.range_begin(), | |
200 freed_iter.range_begin())); | |
201 | |
202 auto i = present_.find(freed_iter.range_begin()); | |
203 DCHECK_EQ(i.value(), 0); | |
204 DCHECK_LE(i.range_begin(), freed_iter.range_begin()); | |
205 DCHECK_LE(freed_iter.range_end(), i.range_end()); | |
206 | |
207 if (i.range_begin() == freed_iter.range_begin()) { | |
208 // Notify the previous range that it contains fewer blocks. | |
209 auto j = i; | |
210 --j; | |
211 DCHECK_EQ(j.value(), 1); | |
212 NotifyAvailableRange(j.range(), j.range()); | |
213 } | |
214 if (i.range_end() == freed_iter.range_end()) { | |
215 // Notify the following range that it contains fewer blocks. | |
216 auto j = i; | |
217 ++j; | |
218 DCHECK_EQ(j.value(), 1); | |
219 NotifyAvailableRange(j.range(), j.range()); | |
220 } | |
221 } | |
222 } | |
223 } | |
224 | |
225 void MultiBuffer::AddProvider(scoped_ptr<DataProvider> provider) { | |
226 // If there is already a provider in the same location, we delete it. | |
227 DCHECK(!provider->Available()); | |
228 BlockId pos = provider->Tell(); | |
229 DataProvider** place = &writer_index_[pos]; | |
230 DCHECK_NE(*place, provider.get()); | |
231 if (*place) delete *place; | |
232 *place = provider.release(); | |
233 } | |
234 | |
235 scoped_ptr<MultiBuffer::DataProvider> MultiBuffer::RemoveProvider( | |
236 DataProvider *provider) { | |
237 BlockId pos = provider->Tell(); | |
238 DCHECK_EQ(writer_index_[pos], provider); | |
239 writer_index_.erase(pos); | |
240 return scoped_ptr<DataProvider>(provider); | |
241 } | |
242 | |
243 MultiBuffer::ProviderState MultiBuffer::SuggestProviderState( | |
244 const BlockId& pos) const { | |
245 MultiBufferBlockId next_reader_pos = ClosestNextEntry(readers_, pos); | |
246 if (next_reader_pos != MultiBufferBlockId() && | |
247 (next_reader_pos - pos <= kMaxWaitForWriterOffset || | |
248 !pos.url_data()->range_supported())) { | |
249 // Check if there is another writer between us and the next reader. | |
250 MultiBufferBlockId next_writer_pos = ClosestNextEntry( | |
251 writer_index_, pos + 1); | |
252 if (next_writer_pos == MultiBufferBlockId() || | |
253 next_writer_pos > next_reader_pos) { | |
254 return ProviderStateLoad; | |
255 } | |
256 } | |
257 | |
258 MultiBufferBlockId previous_reader_pos = ClosestPreviousEntry( | |
259 readers_, pos - 1); | |
260 if (previous_reader_pos != MultiBufferBlockId() && | |
261 (pos - previous_reader_pos <= kMaxWaitForReaderOffset || | |
262 !pos.url_data()->range_supported())) { | |
263 MultiBufferBlockId previous_writer_pos = | |
264 ClosestPreviousEntry(writer_index_, pos - 1); | |
265 if (previous_writer_pos < previous_reader_pos) { | |
266 return ProviderStateDefer; | |
267 } | |
268 } | |
269 | |
270 return ProviderStateDead; | |
271 } | |
272 | |
273 bool MultiBuffer::ProviderCollision(const BlockId& id) const { | |
274 // If there is a writer at the same location, it is always a collision. | |
275 if (writer_index_.find(id) != writer_index_.end()) | |
276 return true; | |
277 | |
278 // Data already exists at providers current position, | |
279 // if the URL supports ranges, we can kill the data provider. | |
280 if (id.url_data()->range_supported() && Contains(id)) | |
281 return true; | |
282 | |
283 return false; | |
284 } | |
285 | |
286 void MultiBuffer::DataProviderEvent(DataProvider *provider_tmp) { | |
287 scoped_ptr<DataProvider> provider(RemoveProvider(provider_tmp)); | |
288 BlockId start_pos = provider->Tell(); | |
289 BlockId pos = start_pos; | |
290 bool eof = false; | |
291 | |
292 while (!ProviderCollision(pos) && !eof) { | |
293 if (!provider->Available()) { | |
294 AddProvider(provider.Pass()); | |
295 break; | |
296 } | |
297 DCHECK_GE(pos.block_num(), 0); | |
298 data_[pos] = provider->Read(); | |
299 eof = data_[pos]->end_of_stream(); | |
300 if (!pinned_[pos]) | |
301 lru_.Use(pos); | |
302 ++pos; | |
303 } | |
304 | |
305 if (pos > start_pos) { | |
306 present_.SetRange(start_pos, pos, 1); | |
307 Range<BlockId> expanded_range = present_.find(start_pos).range(); | |
308 NotifyAvailableRange(expanded_range, expanded_range); | |
309 | |
310 Prune((pos - start_pos) * kMaxFreesPerAdd + 1); | |
311 } | |
312 | |
313 // Check that it's still there before we try to delete it. | |
314 auto i = writer_index_.find(pos); | |
315 if (i != writer_index_.end() && i->second == provider_tmp) { | |
316 switch (SuggestProviderState(pos)) { | |
317 case ProviderStateLoad: | |
318 // Not sure we actually need to do this | |
319 provider_tmp->SetDeferred(false); | |
320 break; | |
321 case ProviderStateDefer: | |
322 provider_tmp->SetDeferred(true); | |
323 break; | |
324 case ProviderStateDead: | |
325 RemoveProvider(provider_tmp); | |
326 break; | |
327 } | |
328 } | |
329 } | |
330 | |
331 void MultiBuffer::UpdateUrlData(const MultiBufferUrlData& old_url_data, | |
332 const MultiBufferUrlData& new_url_data) { | |
DaleCurtis
2015/10/19 21:45:25
Alignment?
hubbe
2015/10/20 00:31:39
Done.
| |
333 MultiBufferBlockId pos(old_url_data, 0); | |
334 auto i = readers_.lower_bound(pos); | |
335 while (i != readers_.end() && pos.SameUrl(i->first)) { | |
336 std::set<Reader*> tmp; | |
337 tmp.swap(i->second); | |
338 auto j = i; | |
339 ++j; | |
340 readers_.erase(i); | |
341 i = j; | |
342 for (Reader* reader: tmp) { | |
343 reader->UpdateUrlData(old_url_data, new_url_data); | |
344 } | |
345 } | |
346 } | |
347 | |
348 | |
349 void MultiBuffer::PinRange( | |
350 const BlockId& from, const BlockId& to, int32_t howmuch) { | |
DaleCurtis
2015/10/19 21:45:25
how_much
hubbe
2015/10/20 00:31:39
Done.
| |
351 DCHECK_NE(howmuch, 0); | |
352 DVLOG(3) << "PINRANGE [" << from << " - " << to << ") += " << howmuch; | |
353 pinned_.IncrementRange(from, to, howmuch); | |
354 | |
355 // Iterate over all the modified ranges and check if | |
356 // any of them have transitioned in or out of the | |
357 // unlocked state. If so, we iterate over all buffers | |
358 // in that range and add/remove them from the LRU as | |
359 // approperiate. We iterate *backwards* through the | |
360 // ranges, with the idea that data in a continous range | |
361 // should be freed from the end first. | |
362 | |
363 if (data_.empty()) | |
364 return; | |
365 | |
366 auto range = pinned_.find(to - 1); | |
367 while (1) { | |
368 if (range.value() == 0 || range.value() == howmuch) { | |
369 bool pin = range.value() == howmuch; | |
370 BlockId begin = std::max(range.range_begin(), from); | |
371 BlockId end = std::min(range.range_end(), to); | |
372 if (begin >= end) | |
373 break; | |
374 DataMap::iterator k = data_.lower_bound(end); | |
375 while (k != data_.begin()) { | |
376 --k; | |
377 if (k->first < begin) | |
378 break; | |
379 DCHECK(k->second); | |
380 DCHECK_GE(k->first.block_num(), 0); | |
381 if (pin) { | |
382 DCHECK(pinned_[k->first]); | |
383 lru_.Remove(k->first); | |
384 } else { | |
385 DCHECK(!pinned_[k->first]); | |
386 lru_.Insert(k->first); | |
387 } | |
388 } | |
389 } | |
390 if (range == pinned_.first_range()) break; | |
391 --range; | |
392 } | |
393 } | |
394 | |
395 void MultiBuffer::PinRanges(const RangeMap<BlockId, int32_t>& ranges) { | |
396 // We know that "last_range" has a zero value, so it is | |
397 // ok to skip over it. | |
398 for (auto i = ranges.first_range(); i != ranges.last_range(); ++i) { | |
399 if (i.value() != 0) { | |
400 PinRange(i.range_begin(), i.range_end(), i.value()); | |
401 } | |
402 } | |
403 } | |
404 | |
405 void MultiBuffer::IncrementMaxSize(int32_t size) { | |
406 max_size_ += size; | |
407 DCHECK_GE(max_size_, 0); | |
408 // Pruning only happens when blocks are added. | |
409 } | |
410 | |
411 } // namespace media | |
OLD | NEW |