summaryrefslogtreecommitdiffstats
path: root/media/base/seekable_buffer.cc
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-19 00:26:39 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-19 00:26:39 +0000
commitc029795f77cf332b95a2966af82dd2fe256649bb (patch)
tree93839cb805b9630ecfe10e73b813f677337936bc /media/base/seekable_buffer.cc
parent2248044b2b21b8e9803aa56cad2a48b1bfb66924 (diff)
downloadchromium_src-c029795f77cf332b95a2966af82dd2fe256649bb.zip
chromium_src-c029795f77cf332b95a2966af82dd2fe256649bb.tar.gz
chromium_src-c029795f77cf332b95a2966af82dd2fe256649bb.tar.bz2
SeekableBuffer to implement a window of buffer which has short seeking ability.
Defined the base interface SeekableBuffer and a thread safe implemntation of ThreadSafeSeekableBuffer. This class is to be used for media resource loading that does the meat of buffer queueing and handles short seeking for short distance out-of-order read patterns. Review URL: http://codereview.chromium.org/113213 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16348 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/base/seekable_buffer.cc')
-rw-r--r--media/base/seekable_buffer.cc192
1 files changed, 192 insertions, 0 deletions
diff --git a/media/base/seekable_buffer.cc b/media/base/seekable_buffer.cc
new file mode 100644
index 0000000..a3f31bc
--- /dev/null
+++ b/media/base/seekable_buffer.cc
@@ -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(0u, 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(0u, 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