Fix style for cast_unittest: Add newlines at EOF
[chromium-blink-merge.git] / media / base / seekable_buffer.cc
blob019ae4651ca978b0696c49f9fa9015e1f1d257d2
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"
7 #include <algorithm>
9 #include "base/logging.h"
10 #include "media/base/data_buffer.h"
12 namespace media {
14 SeekableBuffer::SeekableBuffer(int backward_capacity, int forward_capacity)
15 : current_buffer_offset_(0),
16 backward_capacity_(backward_capacity),
17 backward_bytes_(0),
18 forward_capacity_(forward_capacity),
19 forward_bytes_(0),
20 current_time_(kNoTimestamp()) {
21 current_buffer_ = buffers_.begin();
24 SeekableBuffer::~SeekableBuffer() {
27 void SeekableBuffer::Clear() {
28 buffers_.clear();
29 current_buffer_ = buffers_.begin();
30 current_buffer_offset_ = 0;
31 backward_bytes_ = 0;
32 forward_bytes_ = 0;
33 current_time_ = kNoTimestamp();
36 int SeekableBuffer::Read(uint8* data, int size) {
37 DCHECK(data);
38 return InternalRead(data, size, true, 0);
41 int SeekableBuffer::Peek(uint8* data, int size, int forward_offset) {
42 DCHECK(data);
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()) {
52 ++current_buffer;
53 current_buffer_offset = 0;
55 if (current_buffer == buffers_.end())
56 return false;
57 *data = (*current_buffer)->data() + current_buffer_offset;
58 *size = (*current_buffer)->data_size() - current_buffer_offset;
59 return true;
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
72 // to it.
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_)
85 return false;
86 return true;
89 bool SeekableBuffer::Append(const uint8* data, int size) {
90 if (size > 0) {
91 scoped_refptr<DataBuffer> data_buffer = DataBuffer::CopyFrom(data, size);
92 return Append(data_buffer);
93 } else {
94 // Return true if we have forward capacity.
95 return forward_bytes_ < forward_capacity_;
99 bool SeekableBuffer::Seek(int32 offset) {
100 if (offset > 0)
101 return SeekForward(offset);
102 else if (offset < 0)
103 return SeekBackward(-offset);
104 return true;
107 bool SeekableBuffer::SeekForward(int size) {
108 // Perform seeking forward only if we have enough bytes in the queue.
109 if (size > forward_bytes_)
110 return false;
112 // Do a read of |size| bytes.
113 int taken = InternalRead(NULL, size, true, 0);
114 DCHECK_EQ(taken, size);
115 return true;
118 bool SeekableBuffer::SeekBackward(int size) {
119 if (size > backward_bytes_)
120 return false;
121 // Record the number of bytes taken.
122 int taken = 0;
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
133 // current buffer.
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.
141 taken += consumed;
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())
153 break;
154 // Move the iterator backward.
155 --current_buffer_;
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);
165 return true;
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_)
173 break;
174 scoped_refptr<DataBuffer> buffer = *i;
175 backward_bytes_ -= buffer->data_size();
176 DCHECK_GE(backward_bytes_, 0);
178 buffers_.erase(i);
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.
186 int taken = 0;
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
194 // with data.
195 if (current_buffer == buffers_.end())
196 break;
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.
210 if (data)
211 memcpy(data + taken, buffer->data() + current_buffer_offset, copied);
213 // Increase total number of bytes copied, which regulates when to end this
214 // loop.
215 taken += copied;
217 // We have read |copied| bytes from the current buffer. Advances the
218 // offset.
219 current_buffer_offset += copied;
220 } else {
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;
235 ++next;
236 // If we are at the last buffer, don't advance.
237 if (next == buffers_.end())
238 break;
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();
261 return taken;
264 void SeekableBuffer::UpdateCurrentTime(BufferQueue::iterator buffer,
265 int offset) {
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);
277 } // namespace media