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.
5 #include "media/base/seekable_buffer.h"
9 #include "base/logging.h"
10 #include "media/base/data_buffer.h"
14 SeekableBuffer::SeekableBuffer(int backward_capacity
, int forward_capacity
)
15 : current_buffer_offset_(0),
16 backward_capacity_(backward_capacity
),
18 forward_capacity_(forward_capacity
),
20 current_time_(kNoTimestamp()) {
21 current_buffer_
= buffers_
.begin();
24 SeekableBuffer::~SeekableBuffer() {
27 void SeekableBuffer::Clear() {
29 current_buffer_
= buffers_
.begin();
30 current_buffer_offset_
= 0;
33 current_time_
= kNoTimestamp();
36 int SeekableBuffer::Read(uint8
* data
, int size
) {
38 return InternalRead(data
, size
, true, 0);
41 int SeekableBuffer::Peek(uint8
* data
, int size
, int forward_offset
) {
43 return InternalRead(data
, size
, false, forward_offset
);
46 bool SeekableBuffer::GetCurrentChunk(const uint8
** data
, int* size
) const {
47 BufferQueue::iterator current_buffer
= current_buffer_
;
48 int current_buffer_offset
= current_buffer_offset_
;
49 // Advance position if we are in the end of the current buffer.
50 while (current_buffer
!= buffers_
.end() &&
51 current_buffer_offset
>= (*current_buffer
)->data_size()) {
53 current_buffer_offset
= 0;
55 if (current_buffer
== buffers_
.end())
57 *data
= (*current_buffer
)->data() + current_buffer_offset
;
58 *size
= (*current_buffer
)->data_size() - current_buffer_offset
;
62 bool SeekableBuffer::Append(const scoped_refptr
<DataBuffer
>& buffer_in
) {
63 if (buffers_
.empty() && buffer_in
->timestamp() != kNoTimestamp()) {
64 current_time_
= buffer_in
->timestamp();
67 // Since the forward capacity is only used to check the criteria for buffer
68 // full, we always append data to the buffer.
69 buffers_
.push_back(buffer_in
);
71 // After we have written the first buffer, update |current_buffer_| to point
73 if (current_buffer_
== buffers_
.end()) {
74 DCHECK_EQ(0, forward_bytes_
);
75 current_buffer_
= buffers_
.begin();
78 // Update the |forward_bytes_| counter since we have more bytes.
79 forward_bytes_
+= buffer_in
->data_size();
81 // Advise the user to stop append if the amount of forward bytes exceeds
82 // the forward capacity. A false return value means the user should stop
83 // appending more data to this buffer.
84 if (forward_bytes_
>= forward_capacity_
)
89 bool SeekableBuffer::Append(const uint8
* data
, int size
) {
91 scoped_refptr
<DataBuffer
> data_buffer
= DataBuffer::CopyFrom(data
, size
);
92 return Append(data_buffer
);
94 // Return true if we have forward capacity.
95 return forward_bytes_
< forward_capacity_
;
99 bool SeekableBuffer::Seek(int32 offset
) {
101 return SeekForward(offset
);
103 return SeekBackward(-offset
);
107 bool SeekableBuffer::SeekForward(int size
) {
108 // Perform seeking forward only if we have enough bytes in the queue.
109 if (size
> forward_bytes_
)
112 // Do a read of |size| bytes.
113 int taken
= InternalRead(NULL
, size
, true, 0);
114 DCHECK_EQ(taken
, size
);
118 bool SeekableBuffer::SeekBackward(int size
) {
119 if (size
> backward_bytes_
)
121 // Record the number of bytes taken.
123 // Loop until we taken enough bytes and rewind by the desired |size|.
124 while (taken
< size
) {
125 // |current_buffer_| can never be invalid when we are in this loop. It can
126 // only be invalid before any data is appended. The invalid case should be
127 // handled by checks before we enter this loop.
128 DCHECK(current_buffer_
!= buffers_
.end());
130 // We try to consume at most |size| bytes in the backward direction. We also
131 // have to account for the offset we are in the current buffer, so take the
132 // minimum between the two to determine the amount of bytes to take from the
134 int consumed
= std::min(size
- taken
, current_buffer_offset_
);
136 // Decreases the offset in the current buffer since we are rewinding.
137 current_buffer_offset_
-= consumed
;
139 // Increase the amount of bytes taken in the backward direction. This
140 // determines when to stop the loop.
143 // Forward bytes increases and backward bytes decreases by the amount
144 // consumed in the current buffer.
145 forward_bytes_
+= consumed
;
146 backward_bytes_
-= consumed
;
147 DCHECK_GE(backward_bytes_
, 0);
149 // The current buffer pointed by current iterator has been consumed. Move
150 // the iterator backward so it points to the previous buffer.
151 if (current_buffer_offset_
== 0) {
152 if (current_buffer_
== buffers_
.begin())
154 // Move the iterator backward.
156 // Set the offset into the current buffer to be the buffer size as we
157 // are preparing for rewind for next iteration.
158 current_buffer_offset_
= (*current_buffer_
)->data_size();
162 UpdateCurrentTime(current_buffer_
, current_buffer_offset_
);
164 DCHECK_EQ(taken
, size
);
168 void SeekableBuffer::EvictBackwardBuffers() {
169 // Advances the iterator until we hit the current pointer.
170 while (backward_bytes_
> backward_capacity_
) {
171 BufferQueue::iterator i
= buffers_
.begin();
172 if (i
== current_buffer_
)
174 scoped_refptr
<DataBuffer
> buffer
= *i
;
175 backward_bytes_
-= buffer
->data_size();
176 DCHECK_GE(backward_bytes_
, 0);
182 int SeekableBuffer::InternalRead(uint8
* data
, int size
,
183 bool advance_position
,
184 int forward_offset
) {
185 // Counts how many bytes are actually read from the buffer queue.
188 BufferQueue::iterator current_buffer
= current_buffer_
;
189 int current_buffer_offset
= current_buffer_offset_
;
191 int bytes_to_skip
= forward_offset
;
192 while (taken
< size
) {
193 // |current_buffer| is valid since the first time this buffer is appended
195 if (current_buffer
== buffers_
.end())
198 scoped_refptr
<DataBuffer
> buffer
= *current_buffer
;
200 int remaining_bytes_in_buffer
=
201 buffer
->data_size() - current_buffer_offset
;
203 if (bytes_to_skip
== 0) {
204 // Find the right amount to copy from the current buffer referenced by
205 // |buffer|. We shall copy no more than |size| bytes in total and each
206 // single step copied no more than the current buffer size.
207 int copied
= std::min(size
- taken
, remaining_bytes_in_buffer
);
209 // |data| is NULL if we are seeking forward, so there's no need to copy.
211 memcpy(data
+ taken
, buffer
->data() + current_buffer_offset
, copied
);
213 // Increase total number of bytes copied, which regulates when to end this
217 // We have read |copied| bytes from the current buffer. Advances the
219 current_buffer_offset
+= copied
;
221 int skipped
= std::min(remaining_bytes_in_buffer
, bytes_to_skip
);
222 current_buffer_offset
+= skipped
;
223 bytes_to_skip
-= skipped
;
226 // The buffer has been consumed.
227 if (current_buffer_offset
== buffer
->data_size()) {
228 if (advance_position
) {
229 // Next buffer may not have timestamp, so we need to update current
230 // timestamp before switching to the next buffer.
231 UpdateCurrentTime(current_buffer
, current_buffer_offset
);
234 BufferQueue::iterator next
= current_buffer
;
236 // If we are at the last buffer, don't advance.
237 if (next
== buffers_
.end())
240 // Advances the iterator.
241 current_buffer
= next
;
242 current_buffer_offset
= 0;
246 if (advance_position
) {
247 // We have less forward bytes and more backward bytes. Updates these
248 // counters by |taken|.
249 forward_bytes_
-= taken
;
250 backward_bytes_
+= taken
;
251 DCHECK_GE(forward_bytes_
, 0);
252 DCHECK(current_buffer_
!= buffers_
.end() || forward_bytes_
== 0);
254 current_buffer_
= current_buffer
;
255 current_buffer_offset_
= current_buffer_offset
;
257 UpdateCurrentTime(current_buffer_
, current_buffer_offset_
);
258 EvictBackwardBuffers();
264 void SeekableBuffer::UpdateCurrentTime(BufferQueue::iterator buffer
,
266 // Garbage values are unavoidable, so this check will remain.
267 if (buffer
!= buffers_
.end() &&
268 (*buffer
)->timestamp() != kNoTimestamp()) {
269 int64 time_offset
= ((*buffer
)->duration().InMicroseconds() * offset
) /
270 (*buffer
)->data_size();
272 current_time_
= (*buffer
)->timestamp() +
273 base::TimeDelta::FromMicroseconds(time_offset
);