summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkylep@chromium.org <kylep@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-08 23:54:55 +0000
committerkylep@chromium.org <kylep@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-08 23:54:55 +0000
commit12f3ede02f588cce7c8a9e18daad19b08e582039 (patch)
tree567da4810d5a44eda72d8de120f1eb9a89cac097
parent1677984e6a69087de30496b2a9e7cfa21e172a77 (diff)
downloadchromium_src-12f3ede02f588cce7c8a9e18daad19b08e582039.zip
chromium_src-12f3ede02f588cce7c8a9e18daad19b08e582039.tar.gz
chromium_src-12f3ede02f588cce7c8a9e18daad19b08e582039.tar.bz2
BufferQueue class to hide audio data micromanagement from scaling algorithms. May be useful in other contexts.
BUG=16011 TEST=src/media/base/buffer_queue_unittest.cc Review URL: http://codereview.chromium.org/155193 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20211 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--media/base/buffer_queue.cc99
-rw-r--r--media/base/buffer_queue.h62
-rw-r--r--media/base/buffer_queue_unittest.cc126
-rw-r--r--media/media.gyp3
4 files changed, 290 insertions, 0 deletions
diff --git a/media/base/buffer_queue.cc b/media/base/buffer_queue.cc
new file mode 100644
index 0000000..18b30d50
--- /dev/null
+++ b/media/base/buffer_queue.cc
@@ -0,0 +1,99 @@
+// 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/buffer_queue.h"
+
+#include "media/base/buffers.h"
+
+namespace media {
+
+BufferQueue::BufferQueue()
+ : data_offset_(0),
+ size_in_bytes_(0) {
+}
+
+BufferQueue::~BufferQueue() {
+}
+
+void BufferQueue::Consume(size_t bytes_to_be_consumed) {
+ // Make sure user isn't trying to consume more than we have.
+ DCHECK(size_in_bytes_ >= bytes_to_be_consumed);
+
+ // As we have enough data to consume, adjust |size_in_bytes_|.
+ size_in_bytes_ -= bytes_to_be_consumed;
+
+ // Now consume them.
+ while (bytes_to_be_consumed > 0) {
+ // Calculate number of usable bytes in the front of the |queue_|.
+ size_t front_remaining = queue_.front()->GetDataSize() - data_offset_;
+
+ // If there is enough data in our first buffer to advance into it, do so.
+ // Otherwise, advance into the queue.
+ if (front_remaining > bytes_to_be_consumed) {
+ data_offset_ += bytes_to_be_consumed;
+ bytes_to_be_consumed = 0;
+ } else {
+ data_offset_ = 0;
+ queue_.pop_front();
+ bytes_to_be_consumed -= front_remaining;
+ }
+ }
+}
+
+size_t BufferQueue::Copy(uint8* dest, size_t bytes) {
+ if (bytes == 0)
+ return 0;
+
+ DCHECK(!queue_.empty());
+
+ size_t current_remaining = 0;
+ const uint8* current = NULL;
+ size_t copied = 0;
+
+ for (size_t i = 0; i < queue_.size() && bytes > 0; ++i) {
+ // Calculate number of usable bytes in the front of the |queue_|. Special
+ // case for front due to |data_offset_|.
+ if (i == 0) {
+ current_remaining = queue_.front()->GetDataSize() - data_offset_;
+ current = queue_.front()->GetData() + data_offset_;
+ } else {
+ current_remaining = queue_[i]->GetDataSize();
+ current = queue_[i]->GetData();
+ }
+
+ // Prevent writing over the end of the buffer.
+ if (current_remaining > bytes)
+ current_remaining = bytes;
+
+ memcpy(dest + copied, current, current_remaining);
+
+ // Modify counts and pointers.
+ copied += current_remaining;
+ bytes -= current_remaining;
+ }
+ return copied;
+}
+
+void BufferQueue::Enqueue(Buffer* buffer_in) {
+ queue_.push_back(buffer_in);
+ size_in_bytes_ += buffer_in->GetDataSize();
+}
+
+void BufferQueue::Clear() {
+ queue_.clear();
+ size_in_bytes_ = 0;
+ data_offset_ = 0;
+}
+
+bool BufferQueue::IsEmpty() {
+ // Since we keep track of the number of bytes, this is easier than calling
+ // into |queue_|.
+ return size_in_bytes_ == 0;
+}
+
+size_t BufferQueue::SizeInBytes() {
+ return size_in_bytes_;
+}
+
+} // namespace media
diff --git a/media/base/buffer_queue.h b/media/base/buffer_queue.h
new file mode 100644
index 0000000..363b136
--- /dev/null
+++ b/media/base/buffer_queue.h
@@ -0,0 +1,62 @@
+// 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.
+
+// BufferQueue is a simple Buffer manager that handles requests for data
+// while hiding Buffer boundaries, treating its internal queue of Buffers
+// as a contiguous region.
+//
+// This class is not threadsafe and requires external locking.
+
+#ifndef MEDIA_BASE_BUFFER_QUEUE_H_
+#define MEDIA_BASE_BUFFER_QUEUE_H_
+
+#include <deque>
+
+#include "base/ref_counted.h"
+
+namespace media {
+
+class Buffer;
+
+class BufferQueue {
+ public:
+ BufferQueue();
+ ~BufferQueue();
+
+ // Clears |queue_|.
+ void Clear();
+
+ // Advances front pointer |bytes_to_be_consumed| bytes and discards
+ // "consumed" buffers.
+ void Consume(size_t bytes_to_be_consumed);
+
+ // Tries to copy |bytes| bytes from our data to |dest|. Returns the number
+ // of bytes successfully copied.
+ size_t Copy(uint8* dest, size_t bytes);
+
+ // Enqueues |buffer_in| and adds a reference.
+ void Enqueue(Buffer* buffer_in);
+
+ // Returns true if the |queue_| is empty.
+ bool IsEmpty();
+
+ // Returns the number of bytes in the |queue_|.
+ size_t SizeInBytes();
+
+ private:
+ // Queued audio data.
+ std::deque< scoped_refptr<Buffer> > queue_;
+
+ // Remembers the amount of remaining audio data in the front buffer.
+ size_t data_offset_;
+
+ // Keeps track of the |queue_| size in bytes.
+ size_t size_in_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferQueue);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_BUFFER_QUEUE_H_
diff --git a/media/base/buffer_queue_unittest.cc b/media/base/buffer_queue_unittest.cc
new file mode 100644
index 0000000..7f64b43
--- /dev/null
+++ b/media/base/buffer_queue_unittest.cc
@@ -0,0 +1,126 @@
+// 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 "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "media/base/buffer_queue.h"
+#include "media/base/data_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class BufferQueueTest : public testing::Test {
+ protected:
+ BufferQueueTest() {
+ buffer1 = new DataBuffer();
+ data1 = buffer1->GetWritableData(kDataSize);
+
+ buffer2 = new DataBuffer();
+ data2 = buffer2->GetWritableData(kNewDataSize);
+
+ bufferBig = new DataBuffer();
+ dataBig = bufferBig->GetWritableData(2 * (kDataSize + kNewDataSize));
+
+ memcpy(data1, kData, kDataSize);
+ memcpy(data2, kNewData, kNewDataSize);
+ }
+
+ BufferQueue queue_;
+
+ static const char kData[];
+ static const size_t kDataSize;
+ static const char kNewData[];
+ static const size_t kNewDataSize;
+ scoped_refptr<DataBuffer> buffer1;
+ scoped_refptr<DataBuffer> buffer2;
+ scoped_refptr<DataBuffer> bufferBig;
+ uint8* data1;
+ uint8* data2;
+ uint8* dataBig;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BufferQueueTest);
+};
+
+const char BufferQueueTest::kData[] = "hello";
+const size_t BufferQueueTest::kDataSize = arraysize(BufferQueueTest::kData);
+const char BufferQueueTest::kNewData[] = "chromium";
+const size_t BufferQueueTest::kNewDataSize =
+ arraysize(BufferQueueTest::kNewData);
+
+TEST_F(BufferQueueTest, ValidTestData) {
+ ASSERT_TRUE(kNewDataSize > kDataSize);
+ ASSERT_TRUE(buffer1.get());
+ ASSERT_TRUE(data1);
+ ASSERT_TRUE(buffer2.get());
+ ASSERT_TRUE(data2);
+ ASSERT_TRUE(bufferBig.get());
+ ASSERT_TRUE(dataBig);
+}
+
+TEST_F(BufferQueueTest, Ctor) {
+ EXPECT_TRUE(queue_.IsEmpty());
+ EXPECT_EQ(0u, queue_.SizeInBytes());
+}
+
+TEST_F(BufferQueueTest, Enqueue) {
+ queue_.Enqueue(buffer1.get());
+ EXPECT_FALSE(queue_.IsEmpty());
+ EXPECT_EQ(kDataSize, queue_.SizeInBytes());
+}
+
+TEST_F(BufferQueueTest, CopyWithOneBuffer) {
+ queue_.Enqueue(buffer1.get());
+
+ EXPECT_EQ(kDataSize, queue_.Copy(data2, kDataSize));
+ EXPECT_EQ(0, memcmp(data2, kData, kDataSize));
+}
+
+TEST_F(BufferQueueTest, Clear) {
+ queue_.Enqueue(buffer1.get());
+
+ queue_.Clear();
+ EXPECT_TRUE(queue_.IsEmpty());
+ EXPECT_EQ(0u, queue_.SizeInBytes());
+}
+
+TEST_F(BufferQueueTest, MultipleEnqueues) {
+ queue_.Enqueue(buffer2.get());
+ queue_.Enqueue(buffer1.get());
+ EXPECT_EQ(kDataSize + kNewDataSize, queue_.SizeInBytes());
+}
+
+TEST_F(BufferQueueTest, CopyWithMultipleBuffers) {
+ queue_.Enqueue(buffer2.get());
+ queue_.Enqueue(buffer1.get());
+
+ EXPECT_EQ(kDataSize + kNewDataSize,
+ queue_.Copy(dataBig, kDataSize + kNewDataSize));
+ EXPECT_EQ(0, memcmp(dataBig, kNewData, kNewDataSize));
+ dataBig += kNewDataSize;
+ EXPECT_EQ(0, memcmp(dataBig, kData, kDataSize));
+}
+
+TEST_F(BufferQueueTest, Consume) {
+ queue_.Enqueue(buffer2.get());
+ queue_.Enqueue(buffer1.get());
+
+ queue_.Consume(kDataSize);
+ EXPECT_EQ(kNewDataSize, queue_.SizeInBytes());
+}
+
+TEST_F(BufferQueueTest, CopyFromMiddleOfBuffer) {
+ queue_.Enqueue(buffer2.get());
+ queue_.Enqueue(buffer1.get());
+ queue_.Consume(kDataSize);
+
+ EXPECT_EQ(kNewDataSize, queue_.Copy(dataBig, kNewDataSize));
+ EXPECT_EQ(0, memcmp(dataBig,
+ kNewData + kDataSize,
+ kNewDataSize - kDataSize));
+ dataBig += (kNewDataSize - kDataSize);
+ EXPECT_EQ(0, memcmp(dataBig, kData, kDataSize));
+}
+
+} // namespace media
diff --git a/media/media.gyp b/media/media.gyp
index 669f08e..2e149cb 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -44,6 +44,8 @@
'audio/win/audio_output_win.cc',
'audio/win/waveout_output_win.cc',
'audio/win/waveout_output_win.h',
+ 'base/buffer_queue.h',
+ 'base/buffer_queue.cc',
'base/buffers.h',
'base/data_buffer.cc',
'base/data_buffer.h',
@@ -150,6 +152,7 @@
'audio/win/audio_output_win_unittest.cc',
'audio/mac/audio_output_mac_unittest.cc',
'audio/simple_sources_unittest.cc',
+ 'base/buffer_queue_unittest.cc',
'base/data_buffer_unittest.cc',
'base/mock_ffmpeg.cc',
'base/mock_ffmpeg.h',