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/base/seekable_buffer.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "media/base/data_buffer.h"
11
12namespace media {
13
14SeekableBuffer::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();
22}
23
24SeekableBuffer::~SeekableBuffer() {
25}
26
27void 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();
34}
35
36int SeekableBuffer::Read(uint8* data, int size) {
37  DCHECK(data);
38  return InternalRead(data, size, true, 0);
39}
40
41int SeekableBuffer::Peek(uint8* data, int size, int forward_offset) {
42  DCHECK(data);
43  return InternalRead(data, size, false, forward_offset);
44}
45
46bool 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;
54  }
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;
60}
61
62bool SeekableBuffer::Append(const scoped_refptr<DataBuffer>& buffer_in) {
63  if (buffers_.empty() && buffer_in->timestamp() != kNoTimestamp()) {
64    current_time_ = buffer_in->timestamp();
65  }
66
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);
70
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();
76  }
77
78  // Update the |forward_bytes_| counter since we have more bytes.
79  forward_bytes_ += buffer_in->data_size();
80
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;
87}
88
89bool 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_;
96  }
97}
98
99bool SeekableBuffer::Seek(int32 offset) {
100  if (offset > 0)
101    return SeekForward(offset);
102  else if (offset < 0)
103    return SeekBackward(-offset);
104  return true;
105}
106
107bool 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;
111
112  // Do a read of |size| bytes.
113  int taken = InternalRead(NULL, size, true, 0);
114  DCHECK_EQ(taken, size);
115  return true;
116}
117
118bool 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());
129
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_);
135
136    // Decreases the offset in the current buffer since we are rewinding.
137    current_buffer_offset_ -= consumed;
138
139    // Increase the amount of bytes taken in the backward direction. This
140    // determines when to stop the loop.
141    taken += consumed;
142
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);
148
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();
159    }
160  }
161
162  UpdateCurrentTime(current_buffer_, current_buffer_offset_);
163
164  DCHECK_EQ(taken, size);
165  return true;
166}
167
168void 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);
177
178    buffers_.erase(i);
179  }
180}
181
182int 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;
187
188  BufferQueue::iterator current_buffer = current_buffer_;
189  int current_buffer_offset = current_buffer_offset_;
190
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;
197
198    scoped_refptr<DataBuffer> buffer = *current_buffer;
199
200    int remaining_bytes_in_buffer =
201        buffer->data_size() - current_buffer_offset;
202
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);
208
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);
212
213      // Increase total number of bytes copied, which regulates when to end this
214      // loop.
215      taken += copied;
216
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;
224    }
225
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);
232      }
233
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;
239
240      // Advances the iterator.
241      current_buffer = next;
242      current_buffer_offset = 0;
243    }
244  }
245
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);
253
254    current_buffer_ = current_buffer;
255    current_buffer_offset_ = current_buffer_offset;
256
257    UpdateCurrentTime(current_buffer_, current_buffer_offset_);
258    EvictBackwardBuffers();
259  }
260
261  return taken;
262}
263
264void 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();
271
272    current_time_ = (*buffer)->timestamp() +
273                    base::TimeDelta::FromMicroseconds(time_offset);
274  }
275}
276
277}  // namespace media
278