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
,
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
)->GetDataSize()) {
54 current_buffer_offset
= 0;
56 if (current_buffer
== buffers_
.end())
58 *data
= (*current_buffer
)->GetData() + current_buffer_offset
;
59 *size
= (*current_buffer
)->GetDataSize() - current_buffer_offset
;
63 bool SeekableBuffer::Append(Buffer
* buffer_in
) {
64 if (buffers_
.empty() && buffer_in
->GetTimestamp() != kNoTimestamp()) {
65 current_time_
= buffer_in
->GetTimestamp();
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(scoped_refptr
<Buffer
>(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
->GetDataSize();
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 DataBuffer
* data_buffer
= new DataBuffer(size
);
93 memcpy(data_buffer
->GetWritableData(), data
, size
);
94 data_buffer
->SetDataSize(size
);
95 return Append(data_buffer
);
97 // Return true if we have forward capacity.
98 return forward_bytes_
< forward_capacity_
;
102 bool SeekableBuffer::Seek(int32 offset
) {
104 return SeekForward(offset
);
106 return SeekBackward(-offset
);
110 bool SeekableBuffer::SeekForward(int size
) {
111 // Perform seeking forward only if we have enough bytes in the queue.
112 if (size
> forward_bytes_
)
115 // Do a read of |size| bytes.
116 int taken
= InternalRead(NULL
, size
, true, 0);
117 DCHECK_EQ(taken
, size
);
121 bool SeekableBuffer::SeekBackward(int size
) {
122 if (size
> backward_bytes_
)
124 // Record the number of bytes taken.
126 // Loop until we taken enough bytes and rewind by the desired |size|.
127 while (taken
< size
) {
128 // |current_buffer_| can never be invalid when we are in this loop. It can
129 // only be invalid before any data is appended. The invalid case should be
130 // handled by checks before we enter this loop.
131 DCHECK(current_buffer_
!= buffers_
.end());
133 // We try to consume at most |size| bytes in the backward direction. We also
134 // have to account for the offset we are in the current buffer, so take the
135 // minimum between the two to determine the amount of bytes to take from the
137 int consumed
= std::min(size
- taken
, current_buffer_offset_
);
139 // Decreases the offset in the current buffer since we are rewinding.
140 current_buffer_offset_
-= consumed
;
142 // Increase the amount of bytes taken in the backward direction. This
143 // determines when to stop the loop.
146 // Forward bytes increases and backward bytes decreases by the amount
147 // consumed in the current buffer.
148 forward_bytes_
+= consumed
;
149 backward_bytes_
-= consumed
;
150 DCHECK_GE(backward_bytes_
, 0);
152 // The current buffer pointed by current iterator has been consumed. Move
153 // the iterator backward so it points to the previous buffer.
154 if (current_buffer_offset_
== 0) {
155 if (current_buffer_
== buffers_
.begin())
157 // Move the iterator backward.
159 // Set the offset into the current buffer to be the buffer size as we
160 // are preparing for rewind for next iteration.
161 current_buffer_offset_
= (*current_buffer_
)->GetDataSize();
165 UpdateCurrentTime(current_buffer_
, current_buffer_offset_
);
167 DCHECK_EQ(taken
, size
);
171 void SeekableBuffer::EvictBackwardBuffers() {
172 // Advances the iterator until we hit the current pointer.
173 while (backward_bytes_
> backward_capacity_
) {
174 BufferQueue::iterator i
= buffers_
.begin();
175 if (i
== current_buffer_
)
177 scoped_refptr
<Buffer
> buffer
= *i
;
178 backward_bytes_
-= buffer
->GetDataSize();
179 DCHECK_GE(backward_bytes_
, 0);
185 int SeekableBuffer::InternalRead(uint8
* data
, int size
,
186 bool advance_position
,
187 int forward_offset
) {
188 // Counts how many bytes are actually read from the buffer queue.
191 BufferQueue::iterator current_buffer
= current_buffer_
;
192 int current_buffer_offset
= current_buffer_offset_
;
194 int bytes_to_skip
= forward_offset
;
195 while (taken
< size
) {
196 // |current_buffer| is valid since the first time this buffer is appended
198 if (current_buffer
== buffers_
.end())
201 scoped_refptr
<Buffer
> buffer
= *current_buffer
;
203 int remaining_bytes_in_buffer
=
204 buffer
->GetDataSize() - current_buffer_offset
;
206 if (bytes_to_skip
== 0) {
207 // Find the right amount to copy from the current buffer referenced by
208 // |buffer|. We shall copy no more than |size| bytes in total and each
209 // single step copied no more than the current buffer size.
210 int copied
= std::min(size
- taken
, remaining_bytes_in_buffer
);
212 // |data| is NULL if we are seeking forward, so there's no need to copy.
214 memcpy(data
+ taken
, buffer
->GetData() + current_buffer_offset
, copied
);
216 // Increase total number of bytes copied, which regulates when to end this
220 // We have read |copied| bytes from the current buffer. Advances the
222 current_buffer_offset
+= copied
;
224 int skipped
= std::min(remaining_bytes_in_buffer
, bytes_to_skip
);
225 current_buffer_offset
+= skipped
;
226 bytes_to_skip
-= skipped
;
229 // The buffer has been consumed.
230 if (current_buffer_offset
== buffer
->GetDataSize()) {
231 if (advance_position
) {
232 // Next buffer may not have timestamp, so we need to update current
233 // timestamp before switching to the next buffer.
234 UpdateCurrentTime(current_buffer
, current_buffer_offset
);
237 BufferQueue::iterator next
= current_buffer
;
239 // If we are at the last buffer, don't advance.
240 if (next
== buffers_
.end())
243 // Advances the iterator.
244 current_buffer
= next
;
245 current_buffer_offset
= 0;
249 if (advance_position
) {
250 // We have less forward bytes and more backward bytes. Updates these
251 // counters by |taken|.
252 forward_bytes_
-= taken
;
253 backward_bytes_
+= taken
;
254 DCHECK_GE(forward_bytes_
, 0);
255 DCHECK(current_buffer_
!= buffers_
.end() || forward_bytes_
== 0);
257 current_buffer_
= current_buffer
;
258 current_buffer_offset_
= current_buffer_offset
;
260 UpdateCurrentTime(current_buffer_
, current_buffer_offset_
);
261 EvictBackwardBuffers();
267 void SeekableBuffer::UpdateCurrentTime(BufferQueue::iterator buffer
,
269 // Garbage values are unavoidable, so this check will remain.
270 if (buffer
!= buffers_
.end() && (*buffer
)->GetTimestamp() != kNoTimestamp()) {
271 int64 time_offset
= ((*buffer
)->GetDuration().InMicroseconds() *
272 offset
) / (*buffer
)->GetDataSize();
274 current_time_
= (*buffer
)->GetTimestamp() +
275 base::TimeDelta::FromMicroseconds(time_offset
);