Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(238)

Side by Side Diff: media/filters/source_buffer_stream.cc

Issue 10228017: Add initial implementation of SourceBufferStream (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Response to CR Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/filters/source_buffer_stream.h"
6
7 #include <algorithm>
8 #include <map>
9
10 #include "base/logging.h"
11
12 namespace media {
13
14 // Helper class representing a range of buffered data. All buffers in a
15 // SourceBufferRange are ordered sequentially in presentation order with no
16 // gaps.
17 class SourceBufferRange {
18 public:
19 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue;
20
21 SourceBufferRange();
22
23 // Adds |new_buffers| into this range. |new_buffers| must belong to this
24 // range. Garbage collection may occur after Append().
25 void Append(const BufferQueue& new_buffers);
26
27 // Updates |next_buffer_index_| to point to the Buffer containing |timestamp|.
28 // Assumes |timestamp| is valid and in this range.
29 void Seek(base::TimeDelta timestamp);
30
31 // Updates |out_buffer| with the next buffer in presentation order. Seek()
32 // must be called before calls to GetNextBuffer(), and buffers are returned
33 // in order from the last call to Seek(). Returns true if |out_buffer| is
34 // filled with a valid buffer, false if there is not enough data to fulfill
35 // the request.
36 bool GetNextBuffer(scoped_refptr<StreamParserBuffer>* out_buffer);
37
38 // Returns the Timespan of buffered time in this range.
39 SourceBufferStream::Timespan GetBufferedTime() const;
40
41 // Appends the buffers from |range| into this range.
42 // The first buffer in |range| must come directly after the last buffer
43 // in this range.
44 // If |transfer_current_position| is true, |range|'s |next_buffer_position_|
45 // is transfered to this SourceBufferRange.
46 void AppendRange(const SourceBufferRange& range,
47 bool transfer_current_position);
48 bool CanAppendRange(const SourceBufferRange& range) const;
49
50 // Returns whether a buffer with a starting timestamp of |timestamp| would
51 // belong in this range. This includes a buffer that would be appended to
52 // the end of the range.
53 // Returns 0 if |timestamp| is in this range, -1 if |timestamp| appears
54 // before this range, or 1 if |timestamp| appears after this range.
55 int BelongsToRange(base::TimeDelta timestamp) const;
56
57 // Returns true if the range has enough data to seek to the specified
58 // |timestamp|, false otherwise.
59 bool CanSeekTo(base::TimeDelta timestamp) const;
60
61 // Returns true if the end of this range contains buffers that overlaps with
62 // the beginning of |range|.
63 bool EndOverlaps(const SourceBufferRange& range) const;
64
65 private:
66 // Appends |buffers| to the end of the range and updates |keyframe_map_| as
67 // it encounters new keyframes. Assumes |buffers| belongs at the end of the
68 // range.
69 void AppendToEnd(const BufferQueue& buffers);
70
71 // Returns the start timestamp of the range, or kNoTimestamp if the range is
72 // empty.
73 base::TimeDelta BufferedStart() const;
74
75 // Returns the end timestamp of the buffered data. (Note that this is equal to
76 // the last buffer's timestamp + its duration.) Returns kNoTimestamp if the
77 // range is empty.
78 base::TimeDelta BufferedEnd() const;
79
80 // Returns the upper bound for the starting timestamp for the next buffer to
81 // be appended at the end of the range. Should only be called if |buffers_| is
82 // nonempty.
83 base::TimeDelta MaxNextTimestamp() const;
84
85 // An ordered list of buffers in this range.
86 BufferQueue buffers_;
87
88 // Maps keyframe timestamps to its index position in |buffers_|.
89 typedef std::map<base::TimeDelta, size_t> KeyframeMap;
90 KeyframeMap keyframe_map_;
91
92 // Index into |buffers_| for the next buffer to be returned by
93 // GetBufferedTime(), set to -1 before Seek().
94 int next_buffer_index_;
95
96 DISALLOW_COPY_AND_ASSIGN(SourceBufferRange);
97 };
98
99 } // namespace media
100
101 // Helper method that returns true if |ranges| is sorted in increasing order,
102 // false otherwise.
103 static bool IsRangeListSorted(
104 const std::list<media::SourceBufferRange*>& ranges) {
105 base::TimeDelta prev = media::kNoTimestamp();
106 for (std::list<media::SourceBufferRange*>::const_iterator itr =
107 ranges.begin(); itr != ranges.end(); itr++) {
108 media::SourceBufferStream::Timespan buffered = (*itr)->GetBufferedTime();
109 if (prev != media::kNoTimestamp() && prev >= buffered.first)
110 return false;
111 prev = buffered.second;
112 }
113 return true;
114 }
115
116 // Comparison function for two Buffers based on timestamp.
117 static bool BufferComparator(scoped_refptr<media::Buffer> first,
118 scoped_refptr<media::Buffer> second) {
119 return first->GetTimestamp() < second->GetTimestamp();
120 }
121
122 namespace media {
123
124 SourceBufferStream::SourceBufferStream()
125 : seek_pending_(false),
126 seek_buffer_timestamp_(kNoTimestamp()),
127 selected_range_(NULL) {
128 }
129
130 SourceBufferStream::~SourceBufferStream() {
131 while (!ranges_.empty()) {
132 delete ranges_.front();
133 ranges_.pop_front();
134 }
135 }
136
137 bool SourceBufferStream::Append(
138 const SourceBufferStream::BufferQueue& buffers) {
139 DCHECK(!buffers.empty());
140 base::TimeDelta start_timestamp = buffers.front()->GetTimestamp();
141 base::TimeDelta end_timestamp = buffers.back()->GetTimestamp();
142
143 // Check to see if |buffers| will overlap the currently |selected_range_|,
144 // and if so, ignore this Append() request.
145 // TODO(vrk): Support end overlap properly. (crbug.com/125072)
146 if (selected_range_) {
147 Timespan selected_range_span = selected_range_->GetBufferedTime();
148 if (selected_range_span.second > start_timestamp &&
149 selected_range_span.first <= end_timestamp) {
150 return false;
151 }
152 }
153
154 SourceBufferRange* range = NULL;
155 RangeList::iterator itr = ranges_.end();
156 for (itr = ranges_.begin(); itr != ranges_.end(); itr++) {
157 int range_value = (*itr)->BelongsToRange(start_timestamp);
158
159 // |start_timestamp| is before the current range in this loop. Because
160 // |ranges_| is sorted, this means that we need to create a new range and it
161 // should be placed before |itr|.
162 if (range_value < 0)
163 break;
164
165 if (range_value == 0) {
166 // Found an existing range into which we can append buffers.
167 range = *itr;
168 break;
169 }
170 }
171
172 if (!range) {
173 // Ranges must begin with a keyframe.
174 if (!buffers.front()->IsKeyframe())
175 return false;
176
177 range = new SourceBufferRange();
178 itr = ranges_.insert(itr, range);
179 }
180
181 // Append buffers to the appropriate range.
182 range->Append(buffers);
183
184 // Increment |itr| to be the range after |range|.
185 itr++;
186
187 // Handle overlaps if they were created.
188 while (itr != ranges_.end() && range->EndOverlaps(*(*itr))) {
acolwell GONE FROM CHROMIUM 2012/05/02 00:01:43 nit: You should go back to const Range* here and i
vrk (LEFT CHROMIUM) 2012/05/02 20:34:19 I disagree! I stand by your original comment :) co
189 DCHECK_NE(*itr, selected_range_);
190 delete *itr;
191 itr = ranges_.erase(itr);
192 }
193
194 // Merge with neighbor if necessary.
195 if (itr != ranges_.end() && range->CanAppendRange(*(*itr))) {
196 bool transfer_current_position = selected_range_ == *itr;
197 range->AppendRange(*(*itr), transfer_current_position);
198 // Update |selected_range_| pointer if |range| has become selected after
199 // merges.
200 if (transfer_current_position)
201 selected_range_ = range;
202
203 delete *itr;
204 ranges_.erase(itr);
205 }
206
207 // Finally, try to complete pending seek if one exists.
208 if (seek_pending_)
209 Seek(seek_buffer_timestamp_);
210
211 DCHECK(IsRangeListSorted(ranges_));
212 return true;
213 }
214
215 void SourceBufferStream::Seek(base::TimeDelta timestamp) {
216 if (selected_range_)
217 selected_range_ = NULL;
218
219 seek_buffer_timestamp_ = timestamp;
220 seek_pending_ = true;
221
222 RangeList::iterator itr = ranges_.end();
223 for (itr = ranges_.begin(); itr != ranges_.end(); itr++) {
224 if ((*itr)->CanSeekTo(timestamp))
225 break;
226 }
227
228 if (itr == ranges_.end())
229 return;
230
231 selected_range_ = *itr;
232 selected_range_->Seek(timestamp);
233 seek_pending_ = false;
234 }
235
236 bool SourceBufferStream::GetNextBuffer(
237 scoped_refptr<StreamParserBuffer>* out_buffer) {
238 return selected_range_ && selected_range_->GetNextBuffer(out_buffer);
239 }
240
241 std::list<SourceBufferStream::Timespan>
242 SourceBufferStream::GetBufferedTime() const {
243 std::list<Timespan> timespans;
244 for (RangeList::const_iterator itr = ranges_.begin();
245 itr != ranges_.end(); itr++) {
246 timespans.push_back((*itr)->GetBufferedTime());
247 }
248 return timespans;
249 }
250
251 SourceBufferRange::SourceBufferRange()
252 : next_buffer_index_(-1) {
253 }
254
255 void SourceBufferRange::Append(const BufferQueue& new_buffers) {
256 base::TimeDelta start_timestamp = new_buffers.front()->GetTimestamp();
257
258 if (!buffers_.empty() && start_timestamp < BufferedEnd()) {
259 // We are overwriting existing data, so find the starting point where
260 // things will get overwritten.
261 BufferQueue::iterator starting_point =
262 std::lower_bound(buffers_.begin(), buffers_.end(),
263 new_buffers.front(),
264 BufferComparator);
265
266 // Remove everything from |starting_point| onward.
267 buffers_.erase(starting_point, buffers_.end());
268 }
269
270 // Append data.
271 AppendToEnd(new_buffers);
272 }
273
274 void SourceBufferRange::AppendToEnd(const BufferQueue& new_buffers) {
275 for (BufferQueue::const_iterator itr = new_buffers.begin();
276 itr != new_buffers.end(); itr++) {
277 DCHECK((*itr)->GetDuration() > base::TimeDelta());
278 DCHECK((*itr)->GetTimestamp() != kNoTimestamp());
279 buffers_.push_back(*itr);
280 if ((*itr)->IsKeyframe()) {
281 keyframe_map_.insert(
282 std::make_pair((*itr)->GetTimestamp(), buffers_.size() - 1));
283 }
284 }
285 }
286
287 void SourceBufferRange::Seek(base::TimeDelta timestamp) {
288 DCHECK(CanSeekTo(timestamp));
289 DCHECK(!keyframe_map_.empty());
290
291 KeyframeMap::iterator result = keyframe_map_.lower_bound(timestamp);
292 // lower_bound() returns the first element >= |timestamp|, so we want the
293 // previous element if it did not return the element exactly equal to
294 // |timestamp|.
295 if (result->first != timestamp) {
296 DCHECK(result != keyframe_map_.begin());
297 result--;
298 }
299 next_buffer_index_ = result->second;
300 DCHECK_LT(next_buffer_index_, static_cast<int>(buffers_.size()));
301 }
302
303 bool SourceBufferRange::GetNextBuffer(
304 scoped_refptr<StreamParserBuffer>* out_buffer) {
305 DCHECK_GE(next_buffer_index_, 0);
306 if (next_buffer_index_ >= static_cast<int>(buffers_.size()))
307 return false;
308
309 *out_buffer = buffers_.at(next_buffer_index_);
310 next_buffer_index_++;
311 return true;
312 }
313
314 SourceBufferStream::Timespan
315 SourceBufferRange::GetBufferedTime() const {
316 return std::make_pair(BufferedStart(), BufferedEnd());
317 }
318
319 void SourceBufferRange::AppendRange(const SourceBufferRange& range,
320 bool transfer_current_position) {
321 DCHECK(CanAppendRange(range));
322 DCHECK(!buffers_.empty());
323
324 if (transfer_current_position)
325 next_buffer_index_ = range.next_buffer_index_ + buffers_.size();
326
327 AppendToEnd(range.buffers_);
328 }
329
330 bool SourceBufferRange::CanAppendRange(const SourceBufferRange& range) const {
331
acolwell GONE FROM CHROMIUM 2012/05/01 23:44:59 nit:remove empty line
vrk (LEFT CHROMIUM) 2012/05/02 20:34:19 Done.
332 return range.BufferedStart() >= BufferedEnd() &&
333 range.BufferedStart() <= MaxNextTimestamp();
334 }
335
336 int SourceBufferRange::BelongsToRange(base::TimeDelta timestamp) const {
337 if (buffers_.empty() || MaxNextTimestamp() < timestamp)
338 return 1;
339 else if (BufferedStart() > timestamp)
340 return -1;
341 else
342 return 0;
343 }
344
345 bool SourceBufferRange::CanSeekTo(base::TimeDelta timestamp) const {
346 return !keyframe_map_.empty() && BufferedStart() <= timestamp &&
347 BufferedEnd() > timestamp;
348 }
349
350 bool SourceBufferRange::EndOverlaps(const SourceBufferRange& range) const {
351 return range.BufferedStart() < BufferedEnd();
352 }
353
354 base::TimeDelta SourceBufferRange::BufferedStart() const {
355 if (buffers_.empty())
356 return kNoTimestamp();
357
358 return buffers_.front()->GetTimestamp();
359 }
360
361 base::TimeDelta SourceBufferRange::BufferedEnd() const {
362 if (buffers_.empty())
363 return kNoTimestamp();
364
365 return buffers_.back()->GetTimestamp() + buffers_.back()->GetDuration();
366 }
367
368 base::TimeDelta SourceBufferRange::MaxNextTimestamp() const {
369 DCHECK(!buffers_.empty());
370
371 // Because we do not know exactly when is the next timestamp, any buffer
372 // that starts within 1/3 of the duration past the end of the last buffer
373 // in the queue is considered the next buffer in this range.
374 return BufferedEnd() + buffers_.back()->GetDuration() / 3;
375 }
376
377 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698