| Index: media/base/seekable_buffer.cc
|
| ===================================================================
|
| --- media/base/seekable_buffer.cc (revision 0)
|
| +++ media/base/seekable_buffer.cc (revision 0)
|
| @@ -0,0 +1,192 @@
|
| +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "media/base/seekable_buffer.h"
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/stl_util-inl.h"
|
| +
|
| +namespace media {
|
| +
|
| +SeekableBuffer::SeekableBuffer(size_t backward_capacity,
|
| + size_t forward_capacity)
|
| + : current_buffer_offset_(0),
|
| + backward_capacity_(backward_capacity),
|
| + backward_bytes_(0),
|
| + forward_capacity_(forward_capacity),
|
| + forward_bytes_(0) {
|
| + current_buffer_ = buffers_.begin();
|
| +}
|
| +
|
| +SeekableBuffer::~SeekableBuffer() {
|
| + STLDeleteElements(&buffers_);
|
| +}
|
| +
|
| +size_t SeekableBuffer::Read(size_t size, uint8* data) {
|
| + DCHECK(data);
|
| + return InternalRead(size, data);
|
| +}
|
| +
|
| +bool SeekableBuffer::Append(size_t size, const uint8* data) {
|
| + // Since the forward capacity is only used to check the criteria for buffer
|
| + // full, we will always append data to the buffer.
|
| + Buffer* buffer = new Buffer(size);
|
| + memcpy(buffer->data.get(), data, size);
|
| + buffers_.push_back(buffer);
|
| +
|
| + // After we have written the first buffer, update the |current_buffer_| to
|
| + // point to it.
|
| + if (current_buffer_ == buffers_.end()) {
|
| + DCHECK_EQ(0, forward_bytes_);
|
| + current_buffer_ = buffers_.begin();
|
| + }
|
| +
|
| + // Update the |forward_bytes_| counter since we have more bytes.
|
| + forward_bytes_ += size;
|
| +
|
| + // Advise the user to stop append if the amount of forward bytes exceeds
|
| + // the forward capacity. A false return value means the user should stop
|
| + // appending more data to this buffer.
|
| + if (forward_bytes_ >= forward_capacity_)
|
| + return false;
|
| + return true;
|
| +}
|
| +
|
| +bool SeekableBuffer::Seek(int32 offset) {
|
| + if (offset > 0)
|
| + return SeekForward(offset);
|
| + else if (offset < 0)
|
| + return SeekBackward(-offset);
|
| + return true;
|
| +}
|
| +
|
| +bool SeekableBuffer::SeekForward(size_t size) {
|
| + // Perform seeking forward only if we have enough bytes in the queue.
|
| + if (size > forward_bytes_)
|
| + return false;
|
| +
|
| + // Do a read of |size| bytes.
|
| + size_t taken = InternalRead(size, NULL);
|
| + DCHECK_EQ(taken, size);
|
| + return true;
|
| +}
|
| +
|
| +bool SeekableBuffer::SeekBackward(size_t size) {
|
| + if (size > backward_bytes_)
|
| + return false;
|
| + // Record the number of bytes taken.
|
| + size_t taken = 0;
|
| + // Loop until we taken enough bytes and rewind by the desired |size|.
|
| + while (taken < size) {
|
| + // |current_buffer_| can never be invalid when we are in this loop. It can
|
| + // only be invalid before any data is appended, this case should be handled
|
| + // by checks before we enter this loop.
|
| + DCHECK(current_buffer_ != buffers_.end());
|
| +
|
| + // We try to at most |size| bytes in the backward direction, we also have
|
| + // to account for the offset we are in the current buffer, take the minimum
|
| + // between the two to determine the amount of bytes to take from the
|
| + // current buffer.
|
| + size_t consumed = std::min(size - taken, current_buffer_offset_);
|
| +
|
| + // Decreases the offset in the current buffer since we are rewinding.
|
| + current_buffer_offset_ -= consumed;
|
| +
|
| + // Increase the amount of bytes taken in the backward direction, this
|
| + // determines when to stop the loop.
|
| + taken += consumed;
|
| +
|
| + // Forward bytes increases, and backward bytes decreases by the amount
|
| + // consumed in the current buffer.
|
| + forward_bytes_ += consumed;
|
| + backward_bytes_ -= consumed;
|
| + DCHECK_GE(backward_bytes_, 0u);
|
| +
|
| + // The current buffer pointed by current iterator has been consumed,
|
| + // move the iterator backward so it points to the previous buffer.
|
| + if (current_buffer_offset_ == 0) {
|
| + if (current_buffer_ == buffers_.begin())
|
| + break;
|
| + // Move the iterator backward.
|
| + --current_buffer_;
|
| + // Set the offset into the current buffer to be the buffer size as we
|
| + // are preparing for rewind for next iteration.
|
| + current_buffer_offset_ = (*current_buffer_)->size;
|
| + }
|
| + }
|
| + DCHECK_EQ(taken, size);
|
| + return true;
|
| +}
|
| +
|
| +void SeekableBuffer::EvictBackwardBuffers() {
|
| + // Advances the iterator until we hit the current pointer.
|
| + while (backward_bytes_ > backward_capacity_) {
|
| + BufferQueue::iterator i = buffers_.begin();
|
| + if (i == current_buffer_)
|
| + break;
|
| + Buffer* buffer = *i;
|
| + backward_bytes_ -= buffer->size;
|
| + DCHECK_GE(backward_bytes_, 0u);
|
| +
|
| + delete *i;
|
| + buffers_.erase(i);
|
| + }
|
| +}
|
| +
|
| +size_t SeekableBuffer::InternalRead(size_t size, uint8* data) {
|
| + // Counts how many bytes are actually read from the buffer queue.
|
| + size_t taken = 0;
|
| +
|
| + while (taken < size) {
|
| + // |current_buffer_| is valid since the first time this buffer is appended
|
| + // with data.
|
| + if (current_buffer_ == buffers_.end()) {
|
| + DCHECK_EQ(0, forward_bytes_);
|
| + break;
|
| + }
|
| + Buffer* buffer = *current_buffer_;
|
| +
|
| + // Find the right amount to copy from the current buffer referenced by
|
| + // |buffer|. We shall copy no more than |size| bytes in total and each
|
| + // single step copied no more than the current buffer size.
|
| + size_t copied = std::min(size - taken,
|
| + buffer->size - current_buffer_offset_);
|
| +
|
| + // |data| is NULL if we are seeking forward, thus there's no need to copy.
|
| + if (data)
|
| + memcpy(data + taken, buffer->data.get() + current_buffer_offset_, copied);
|
| +
|
| + // Increase total number of bytes copied, which regulates when to end this
|
| + // loop.
|
| + taken += copied;
|
| +
|
| + // We have read |copied| bytes from the current buffer, advances the offset.
|
| + current_buffer_offset_ += copied;
|
| +
|
| + // We have less forward bytes and more backward bytes, update these counters
|
| + // by |copied|.
|
| + forward_bytes_ -= copied;
|
| + backward_bytes_ += copied;
|
| + DCHECK_GE(forward_bytes_, 0u);
|
| +
|
| + // The buffer has been consumed.
|
| + if (current_buffer_offset_ == buffer->size) {
|
| + BufferQueue::iterator next = current_buffer_;
|
| + ++next;
|
| + // If we are at the last buffer, don't advance.
|
| + if (next == buffers_.end())
|
| + break;
|
| +
|
| + // Advances the iterator.
|
| + current_buffer_ = next;
|
| + current_buffer_offset_ = 0;
|
| + }
|
| + }
|
| + EvictBackwardBuffers();
|
| + return taken;
|
| +}
|
| +
|
| +} // namespace media
|
|
|