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

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: . 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 // Comparison function for two Buffers based on timestamp.
13 static bool BufferComparator(scoped_refptr<media::Buffer> first,
14 scoped_refptr<media::Buffer> second) {
15 return first->GetTimestamp() < second->GetTimestamp();
16 }
17
18 namespace media {
19
20 // Helper class representing a range of buffered data. All buffers in a
21 // SourceBufferRange are ordered sequentially in presentation order with no
22 // gaps.
23 class SourceBufferRange {
24 public:
25 typedef std::deque<scoped_refptr<Buffer> > BufferQueue;
26
27 SourceBufferRange();
28 void Append(const BufferQueue& buffers);
acolwell GONE FROM CHROMIUM 2012/04/29 18:13:06 nit: add documentation for these methods.
vrk (LEFT CHROMIUM) 2012/05/01 22:51:13 Done.
29 void Seek(base::TimeDelta timestamp);
30 bool GetNextBuffer(scoped_refptr<Buffer>* out_buffer);
31 SourceBufferStream::Timespan GetBufferedTime() const;
32
33 // Moves the buffers from |range| into this range.
34 // The first buffer in |range| must come directly after the last buffer
35 // in this range.
36 // If |transfer_current_position| is true, |range|'s |next_buffer_position_|
37 // is transfered to this SourceBufferRange.
38 void Merge(SourceBufferRange* range, bool transfer_current_position);
39 bool CanMerge(const SourceBufferRange& range) const;
40
41 // Returns whether a buffer with a starting timestamp of |timestamp| would
42 // belong in this range. This includes a buffer that would be appended to
43 // the end of the range.
44 // Returns 0 if |timestamp| is in this range, 1 if |timestamp| appears before
45 // this range, or -1 if |timestamp| appears after this range.
46 int BelongsToRange(base::TimeDelta timestamp) const;
acolwell GONE FROM CHROMIUM 2012/04/29 18:13:06 nit: This is really minor, but mind if we switch t
vrk (LEFT CHROMIUM) 2012/05/01 22:51:13 Yeah, now that I've explained it outloud, it makes
47
48 // Returns true if the range has enough data to seek to the specified
49 // |timestamp|, false otherwise.
50 bool CanSeekTo(base::TimeDelta timestamp) const;
51
52 // Returns true if the end of this range contains buffers that overlaps with
53 // the beginning of |range|.
54 bool EndOverlaps(const SourceBufferRange& range) const;
55
56 private:
57 void AppendToEnd(const BufferQueue& buffers);
acolwell GONE FROM CHROMIUM 2012/04/29 18:13:06 docs for these methods.
vrk (LEFT CHROMIUM) 2012/05/01 22:51:13 Done.
58 base::TimeDelta BufferedStart() const;
59 base::TimeDelta BufferedEnd() const;
60 base::TimeDelta MaxNextTimestamp() const;
61 void Reset();
62
63 // An ordered list of buffers in this range.
64 BufferQueue buffers_;
65
66 // Maps keyframe timestamps to its index position in |buffers_|.
67 typedef std::map<base::TimeDelta, size_t> KeyframeMap;
68 KeyframeMap keyframe_map_;
69
70 // Index into |buffers_| for the next buffer to be returned by
71 // GetBufferedTime();
72 int next_buffer_index_;
73
74 DISALLOW_COPY_AND_ASSIGN(SourceBufferRange);
75 };
76
77 } // namespace media
78
79 // Helper method that returns true if |ranges| is sorted in increasing order,
80 // false otherwise.
81 static bool IsRangeListSorted(
82 const std::list<media::SourceBufferRange*>& ranges) {
83 base::TimeDelta prev = media::kNoTimestamp();
84 for (std::list<media::SourceBufferRange*>::const_iterator itr =
85 ranges.begin(); itr != ranges.end(); itr++) {
86 media::SourceBufferStream::Timespan buffered = (*itr)->GetBufferedTime();
87 if (prev != media::kNoTimestamp() && prev >= buffered.first)
88 return false;
89 prev = buffered.second;
90 }
91 return true;
92 }
93
94 namespace media {
95
96 SourceBufferStream::SourceBufferStream()
97 : seek_pending_(false),
98 selected_range_(NULL) {
acolwell GONE FROM CHROMIUM 2012/04/29 18:13:06 seek_buffer_timestamp_(kNoTimestamp()) ?
vrk (LEFT CHROMIUM) 2012/05/01 22:51:13 Yes, thanks for the catch! Done.
99 }
100
101 SourceBufferStream::~SourceBufferStream() {
102 while (!ranges_.empty()) {
103 delete ranges_.front();
104 ranges_.pop_front();
105 }
106 }
107
108 bool SourceBufferStream::Append(
109 const SourceBufferStream::BufferQueue& buffers) {
110 DCHECK(!buffers.empty());
111 base::TimeDelta start_timestamp = buffers.front()->GetTimestamp();
112 base::TimeDelta end_timestamp = buffers.back()->GetTimestamp();
113
114 // Check to see if |buffers| will overlap the currently |selected_range_|,
115 // and if so, ignore this Append() request.
116 // TODO(vrk): Support end overlap properly. (crbug.com/125072)
117 if (selected_range_) {
118 Timespan selected_range_span = selected_range_->GetBufferedTime();
119 if (selected_range_span.second > start_timestamp &&
120 selected_range_span.first <= end_timestamp) {
121 return false;
122 }
123 }
124
125 bool create_new_range = true;
126 RangeList::iterator itr = ranges_.end();
127 for (itr = ranges_.begin(); itr != ranges_.end();itr++) {
acolwell GONE FROM CHROMIUM 2012/04/29 18:13:06 nit: space after ;
vrk (LEFT CHROMIUM) 2012/05/01 22:51:13 Done.
128 int range_value = (*itr)->BelongsToRange(start_timestamp);
129 if (range_value > 0)
acolwell GONE FROM CHROMIUM 2012/04/29 18:13:06 Please add a comment why we are breaking here. I'm
vrk (LEFT CHROMIUM) 2012/05/01 22:51:13 Done.
130 break;
131
132 if (range_value == 0) {
133 // Found an existing range into which we can append buffers.
134 create_new_range = false;
135 break;
136 }
137 }
138
139 if (create_new_range)
acolwell GONE FROM CHROMIUM 2012/04/29 18:13:06 I think you might be able to reduce the subtlety o
vrk (LEFT CHROMIUM) 2012/05/01 22:51:13 Good idea! Done.
140 itr = ranges_.insert(itr, new SourceBufferRange());
acolwell GONE FROM CHROMIUM 2012/04/29 18:13:06 Before creating the new range object you should ad
vrk (LEFT CHROMIUM) 2012/05/01 22:51:13 Done and modified/added test.
141 SourceBufferRange* range = *itr;
142
143 // Append buffers to the appropriate range.
144 range->Append(buffers);
145
146 // Increment |itr| to be the range after |range|.
147 itr++;
148
149 // Handle overlaps if they were created.
150 while (itr != ranges_.end() && range->EndOverlaps(*(*itr))) {
151 DCHECK_NE(*itr, selected_range_);
152 delete *itr;
153 itr = ranges_.erase(itr);
154 }
155
156 // Merge with neighbor if necessary.
157 if (itr != ranges_.end() && range->CanMerge(*(*itr))) {
158 bool transfer_current_position = selected_range_ == *itr;
159 range->Merge(*itr, transfer_current_position);
160 // Update |selected_range_| pointer if |range| has become selected after
161 // merges.
162 if (transfer_current_position)
163 selected_range_ = range;
164
165 delete *itr;
166 ranges_.erase(itr);
167 }
168
169 // Finally, try to complete pending seek if one exists.
170 if (seek_pending_)
171 Seek(seek_buffer_timestamp_);
172
173 DCHECK(IsRangeListSorted(ranges_));
174 return true;
175 }
176
177 void SourceBufferStream::Seek(base::TimeDelta timestamp) {
178 if (selected_range_)
179 selected_range_ = NULL;
180
181 seek_buffer_timestamp_ = timestamp;
182 seek_pending_ = true;
183
184 RangeList::iterator itr = ranges_.end();
185 for (itr = ranges_.begin(); itr != ranges_.end(); itr++) {
186 if ((*itr)->CanSeekTo(timestamp))
187 break;
188 }
189
190 if (itr == ranges_.end())
191 return;
192
193 selected_range_ = *itr;
194 selected_range_->Seek(timestamp);
195 seek_pending_ = false;
196 }
197
198 bool SourceBufferStream::GetNextBuffer(scoped_refptr<Buffer>* out_buffer) {
199 return selected_range_ && selected_range_->GetNextBuffer(out_buffer);
200 }
201
202 std::list<SourceBufferStream::Timespan>
203 SourceBufferStream::GetBufferedTime() const {
204 std::list<Timespan> timespans;
205 for (RangeList::const_iterator itr = ranges_.begin();
206 itr != ranges_.end(); itr++) {
207 timespans.push_back((*itr)->GetBufferedTime());
208 }
209 return timespans;
210 }
211
212 SourceBufferRange::SourceBufferRange()
213 : next_buffer_index_(-1) {
214 }
215
216 void SourceBufferRange::Append(const BufferQueue& new_buffers) {
217 base::TimeDelta start_timestamp = new_buffers.front()->GetTimestamp();
218
219 if (!buffers_.empty() && start_timestamp < BufferedEnd()) {
220 // We are overwriting existing data, so find the starting point where
221 // things will get overwritten.
222 BufferQueue::iterator starting_point =
223 std::lower_bound(buffers_.begin(), buffers_.end(),
224 new_buffers.front(),
225 BufferComparator);
226
227 // Remove everything from |starting_point| onward.
228 buffers_.erase(starting_point, buffers_.end());
229 }
230
231 // Append data.
232 AppendToEnd(new_buffers);
233 }
234
235 void SourceBufferRange::AppendToEnd(const BufferQueue& new_buffers) {
236 for (BufferQueue::const_iterator itr = new_buffers.begin();
237 itr != new_buffers.end(); itr++) {
238 DCHECK((*itr)->GetDuration() > base::TimeDelta());
239 DCHECK((*itr)->GetTimestamp() != kNoTimestamp());
240 buffers_.push_back(*itr);
241 if ((*itr)->IsKeyframe()) {
242 keyframe_map_.insert(
243 std::make_pair((*itr)->GetTimestamp(), buffers_.size() - 1));
244 }
245 }
246 }
247
248 void SourceBufferRange::Seek(base::TimeDelta timestamp) {
249 DCHECK(CanSeekTo(timestamp));
250 DCHECK(!keyframe_map_.empty());
251
252 KeyframeMap::iterator result = keyframe_map_.lower_bound(timestamp);
253 // lower_bound() returns the first element >= |timestamp|, so we want the
254 // previous element if it did not return the element exactly equal to
255 // |timestamp|.
256 if (result->first != timestamp) {
257 DCHECK(result != keyframe_map_.begin());
258 result--;
259 }
260 next_buffer_index_ = result->second;
261 DCHECK_LT(next_buffer_index_, static_cast<int>(buffers_.size()));
262 }
263
264 bool SourceBufferRange::GetNextBuffer(
265 scoped_refptr<Buffer>* out_buffer) {
266 DCHECK_GE(next_buffer_index_, 0);
267 if (next_buffer_index_ >= static_cast<int>(buffers_.size()))
268 return false;
269
270 *out_buffer = buffers_.at(next_buffer_index_);
271 next_buffer_index_++;
272 return true;
273 }
274
275 SourceBufferStream::Timespan
276 SourceBufferRange::GetBufferedTime() const {
277 return std::make_pair<base::TimeDelta, base::TimeDelta>(
acolwell GONE FROM CHROMIUM 2012/04/29 18:13:06 nit: Lint says you don't need the template paramet
vrk (LEFT CHROMIUM) 2012/05/01 22:51:13 D'oh, yes, this is true! You can omit the template
278 BufferedStart(), BufferedEnd());
279 }
280
281 void SourceBufferRange::Merge(SourceBufferRange* range,
acolwell GONE FROM CHROMIUM 2012/04/29 18:13:06 How about renaming this to AppendRange() instead o
vrk (LEFT CHROMIUM) 2012/05/01 22:51:13 Done.
282 bool transfer_current_position) {
283 DCHECK(CanMerge(*range));
284 DCHECK(!buffers_.empty());
285
286 if (transfer_current_position) {
287 next_buffer_index_ = range->next_buffer_index_;
288 next_buffer_index_ += buffers_.size();
289 }
290
291 AppendToEnd(range->buffers_);
292 range->Reset();
293 }
294
295 bool SourceBufferRange::CanMerge(const SourceBufferRange& range) const {
296 return range.BufferedStart() >= BufferedEnd() &&
297 range.BufferedStart() <= MaxNextTimestamp();
298 }
299
300 int SourceBufferRange::BelongsToRange(base::TimeDelta timestamp) const {
301 if (buffers_.empty() || MaxNextTimestamp() < timestamp)
302 return -1;
303 else if (BufferedStart() > timestamp)
304 return 1;
305 else
306 return 0;
307 }
308
309 bool SourceBufferRange::CanSeekTo(base::TimeDelta timestamp) const {
310 return BufferedStart() <= timestamp && BufferedEnd() > timestamp;
acolwell GONE FROM CHROMIUM 2012/04/29 18:13:06 !keyframe_map_.empty() &&
vrk (LEFT CHROMIUM) 2012/05/01 22:51:13 Done.
311 }
312
313 bool SourceBufferRange::EndOverlaps(const SourceBufferRange& range) const {
314 return range.BufferedStart() < BufferedEnd();
315 }
316
317 base::TimeDelta SourceBufferRange::BufferedStart() const {
318 if (buffers_.empty())
319 return kNoTimestamp();
320
321 return buffers_.front()->GetTimestamp();
322 }
323
324 base::TimeDelta SourceBufferRange::BufferedEnd() const {
325 if (buffers_.empty())
326 return kNoTimestamp();
327
328 return buffers_.back()->GetTimestamp() + buffers_.back()->GetDuration();
329 }
330
331 base::TimeDelta SourceBufferRange::MaxNextTimestamp() const {
332 DCHECK(!buffers_.empty());
333
334 // Because we do not know exactly when is the next timestamp, any buffer
335 // that starts within 1/3 of the duration past the end of the last buffer
336 // in the queue is considered the next buffer in this range.
337 return BufferedEnd() + buffers_.back()->GetDuration() / 3;
338 }
339
340 void SourceBufferRange::Reset() {
341 keyframe_map_.clear();
342 buffers_.clear();
343 next_buffer_index_ = -1;
344 }
345
346 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698