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"
11 #include "media/base/timestamp_constants.h"
15 SeekableBuffer::SeekableBuffer(int backward_capacity
, int forward_capacity
)
16 : current_buffer_offset_(0),
17 backward_capacity_(backward_capacity
),
19 forward_capacity_(forward_capacity
),
21 current_time_(kNoTimestamp()) {
22 current_buffer_
= buffers_
.begin();
25 SeekableBuffer::~SeekableBuffer() {
28 void SeekableBuffer::Clear() {
30 current_buffer_
= buffers_
.begin();
31 current_buffer_offset_
= 0;
34 current_time_
= kNoTimestamp();
37 int SeekableBuffer::Read(uint8
* data
, int size
) {
39 return InternalRead(data
, size
, true, 0);
42 int SeekableBuffer::Peek(uint8
* data
, int size
, int forward_offset
) {
44 return InternalRead(data
, size
, false, forward_offset
);
47 bool SeekableBuffer::GetCurrentChunk(const uint8
** data
, int* size
) const {
48 BufferQueue::iterator current_buffer
= current_buffer_
;
49 int current_buffer_offset
= current_buffer_offset_
;
50 // Advance position if we are in the end of the current buffer.
51 while (current_buffer
!= buffers_
.end() &&
52 current_buffer_offset
>= (*current_buffer
)->data_size()) {
54 current_buffer_offset
= 0;
56 if (current_buffer
== buffers_
.end())
58 *data
= (*current_buffer
)->data() + current_buffer_offset
;
59 *size
= (*current_buffer
)->data_size() - current_buffer_offset
;
63 bool SeekableBuffer::Append(const scoped_refptr
<DataBuffer
>& buffer_in
) {
64 if (buffers_
.empty() && buffer_in
->timestamp() != kNoTimestamp()) {
65 current_time_
= buffer_in
->timestamp();
68 // Since the forward capacity is only used to check the criteria for buffer
69 // full, we always append data to the buffer.
70 buffers_
.push_back(buffer_in
);
72 // After we have written the first buffer, update |current_buffer_| to point
74 if (current_buffer_
== buffers_
.end()) {
75 DCHECK_EQ(0, forward_bytes_
);
76 current_buffer_
= buffers_
.begin();
79 // Update the |forward_bytes_| counter since we have more bytes.
80 forward_bytes_
+= buffer_in
->data_size();
82 // Advise the user to stop append if the amount of forward bytes exceeds
83 // the forward capacity. A false return value means the user should stop
84 // appending more data to this buffer.
85 if (forward_bytes_
>= forward_capacity_
)
90 bool SeekableBuffer::Append(const uint8
* data
, int size
) {
92 scoped_refptr
<DataBuffer
> data_buffer
= DataBuffer::CopyFrom(data
, size
);
93 return Append(data_buffer
);
95 // Return true if we have forward capacity.
96 return forward_bytes_
< forward_capacity_
;
100 bool SeekableBuffer::Seek(int32 offset
) {
102 return SeekForward(offset
);
104 return SeekBackward(-offset
);
108 bool SeekableBuffer::SeekForward(int size
) {
109 // Perform seeking forward only if we have enough bytes in the queue.
110 if (size
> forward_bytes_
)
113 // Do a read of |size| bytes.
114 int taken
= InternalRead(NULL
, size
, true, 0);
115 DCHECK_EQ(taken
, size
);
119 bool SeekableBuffer::SeekBackward(int size
) {
120 if (size
> backward_bytes_
)
122 // Record the number of bytes taken.
124 // Loop until we taken enough bytes and rewind by the desired |size|.
125 while (taken
< size
) {
126 // |current_buffer_| can never be invalid when we are in this loop. It can
127 // only be invalid before any data is appended. The invalid case should be
128 // handled by checks before we enter this loop.
129 DCHECK(current_buffer_
!= buffers_
.end());
131 // We try to consume at most |size| bytes in the backward direction. We also
132 // have to account for the offset we are in the current buffer, so take the
133 // minimum between the two to determine the amount of bytes to take from the
135 int consumed
= std::min(size
- taken
, current_buffer_offset_
);
137 // Decreases the offset in the current buffer since we are rewinding.
138 current_buffer_offset_
-= consumed
;
140 // Increase the amount of bytes taken in the backward direction. This
141 // determines when to stop the loop.
144 // Forward bytes increases and backward bytes decreases by the amount
145 // consumed in the current buffer.
146 forward_bytes_
+= consumed
;
147 backward_bytes_
-= consumed
;
148 DCHECK_GE(backward_bytes_
, 0);
150 // The current buffer pointed by current iterator has been consumed. Move
151 // the iterator backward so it points to the previous buffer.
152 if (current_buffer_offset_
== 0) {
153 if (current_buffer_
== buffers_
.begin())
155 // Move the iterator backward.
157 // Set the offset into the current buffer to be the buffer size as we
158 // are preparing for rewind for next iteration.
159 current_buffer_offset_
= (*current_buffer_
)->data_size();
163 UpdateCurrentTime(current_buffer_
, current_buffer_offset_
);
165 DCHECK_EQ(taken
, size
);
169 void SeekableBuffer::EvictBackwardBuffers() {
170 // Advances the iterator until we hit the current pointer.
171 while (backward_bytes_
> backward_capacity_
) {
172 BufferQueue::iterator i
= buffers_
.begin();
173 if (i
== current_buffer_
)
175 scoped_refptr
<DataBuffer
> buffer
= *i
;
176 backward_bytes_
-= buffer
->data_size();
177 DCHECK_GE(backward_bytes_
, 0);
183 int SeekableBuffer::InternalRead(uint8
* data
, int size
,
184 bool advance_position
,
185 int forward_offset
) {
186 // Counts how many bytes are actually read from the buffer queue.
189 BufferQueue::iterator current_buffer
= current_buffer_
;
190 int current_buffer_offset
= current_buffer_offset_
;
192 int bytes_to_skip
= forward_offset
;
193 while (taken
< size
) {
194 // |current_buffer| is valid since the first time this buffer is appended
196 if (current_buffer
== buffers_
.end())
199 scoped_refptr
<DataBuffer
> buffer
= *current_buffer
;
201 int remaining_bytes_in_buffer
=
202 buffer
->data_size() - current_buffer_offset
;
204 if (bytes_to_skip
== 0) {
205 // Find the right amount to copy from the current buffer referenced by
206 // |buffer|. We shall copy no more than |size| bytes in total and each
207 // single step copied no more than the current buffer size.
208 int copied
= std::min(size
- taken
, remaining_bytes_in_buffer
);
210 // |data| is NULL if we are seeking forward, so there's no need to copy.
212 memcpy(data
+ taken
, buffer
->data() + current_buffer_offset
, copied
);
214 // Increase total number of bytes copied, which regulates when to end this
218 // We have read |copied| bytes from the current buffer. Advances the
220 current_buffer_offset
+= copied
;
222 int skipped
= std::min(remaining_bytes_in_buffer
, bytes_to_skip
);
223 current_buffer_offset
+= skipped
;
224 bytes_to_skip
-= skipped
;
227 // The buffer has been consumed.
228 if (current_buffer_offset
== buffer
->data_size()) {
229 if (advance_position
) {
230 // Next buffer may not have timestamp, so we need to update current
231 // timestamp before switching to the next buffer.
232 UpdateCurrentTime(current_buffer
, current_buffer_offset
);
235 BufferQueue::iterator next
= current_buffer
;
237 // If we are at the last buffer, don't advance.
238 if (next
== buffers_
.end())
241 // Advances the iterator.
242 current_buffer
= next
;
243 current_buffer_offset
= 0;
247 if (advance_position
) {
248 // We have less forward bytes and more backward bytes. Updates these
249 // counters by |taken|.
250 forward_bytes_
-= taken
;
251 backward_bytes_
+= taken
;
252 DCHECK_GE(forward_bytes_
, 0);
253 DCHECK(current_buffer_
!= buffers_
.end() || forward_bytes_
== 0);
255 current_buffer_
= current_buffer
;
256 current_buffer_offset_
= current_buffer_offset
;
258 UpdateCurrentTime(current_buffer_
, current_buffer_offset_
);
259 EvictBackwardBuffers();
265 void SeekableBuffer::UpdateCurrentTime(BufferQueue::iterator buffer
,
267 // Garbage values are unavoidable, so this check will remain.
268 if (buffer
!= buffers_
.end() &&
269 (*buffer
)->timestamp() != kNoTimestamp()) {
270 int64 time_offset
= ((*buffer
)->duration().InMicroseconds() * offset
) /
271 (*buffer
)->data_size();
273 current_time_
= (*buffer
)->timestamp() +
274 base::TimeDelta::FromMicroseconds(time_offset
);