Add helper classes to for managing shared buffers.

First of a series of patches for Motown.

FifoAllocator - does the bookkeeping to implement heap semantics on an
imaginary buffer assuming normal streaming behavior (first allocated,
first released).

MappedSharedBuffer - owns a shared buffer, maps and unmaps it and does
offset/pointer conversions.

SharedMediaBufferAllocator - derived from MappedSharedBuffer, adds
heap semantics using FifoAllocator and thread safety.

BUG=none
R=johngro@google.com

Review URL: https://codereview.chromium.org/1460693004 .
diff --git a/.gitignore b/.gitignore
index d1bb5d5..e24c222 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,7 +39,7 @@
 /third_party/colorama/src/
 /third_party/dart-sdk/
 /third_party/dejavu-fonts-ttf-2.34/ttf/*.ttf
-/third_party/ffmpeg
+/third_party/ffmpeg/
 /third_party/freetype-android/src
 /third_party/go/
 /third_party/icu/
diff --git a/mojo/services/media/common/cpp/BUILD.gn b/mojo/services/media/common/cpp/BUILD.gn
index 98697a8..627a726 100644
--- a/mojo/services/media/common/cpp/BUILD.gn
+++ b/mojo/services/media/common/cpp/BUILD.gn
@@ -10,9 +10,15 @@
   sources = [
     "circular_buffer_media_pipe_adapter.cc",
     "circular_buffer_media_pipe_adapter.h",
+    "fifo_allocator.cc",
+    "fifo_allocator.h",
     "linear_transform.cc",
     "linear_transform.h",
     "local_time.h",
+    "mapped_shared_buffer.cc",
+    "mapped_shared_buffer.h",
+    "shared_media_buffer_allocator.cc",
+    "shared_media_buffer_allocator.h",
   ]
 
   if (is_posix) {
diff --git a/mojo/services/media/common/cpp/fifo_allocator.cc b/mojo/services/media/common/cpp/fifo_allocator.cc
new file mode 100644
index 0000000..0bbb69d
--- /dev/null
+++ b/mojo/services/media/common/cpp/fifo_allocator.cc
@@ -0,0 +1,222 @@
+// Copyright 2015 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 "mojo/public/cpp/environment/logging.h"
+#include "mojo/services/media/common/cpp/fifo_allocator.h"
+
+namespace mojo {
+namespace media {
+
+FifoAllocator::FifoAllocator(uint64_t size) : front_(nullptr), free_(nullptr) {
+  // front_ and free_ need to be set to nullptr before calling reset.
+  Reset(size);
+}
+
+FifoAllocator::~FifoAllocator() {
+  DeleteFrontToBack(front_);
+  DeleteFrontToBack(free_);
+}
+
+void FifoAllocator::Reset(uint64_t size) {
+  MOJO_DCHECK(size < kNullOffset);
+  DeleteFrontToBack(front_);
+  DeleteFrontToBack(free_);
+  size_ = size;
+  free_ = nullptr;
+  front_ = back_ = active_ = get_free(false, size, 0);
+  active_->prev = nullptr;
+  active_->next = nullptr;
+}
+
+uint64_t FifoAllocator::AllocateRegion(uint64_t size) {
+  MOJO_DCHECK(size != 0);
+
+  if (active_->size < size) {
+    // The active region is too small. Look for one that's large enough.
+    if (!AdvanceActive(size)) {
+      // No unallocated regions are large enough. Can't do the allocation.
+      return kNullOffset;
+    }
+  }
+
+  if (active_->size == size) {
+    // The active region is exactly the right size. Use it for the allocation.
+    uint64_t result = active_->offset;
+    active_->allocated = true;
+    if (active_ == back_ && !front_->allocated) {
+      // active_ was the back region and the front region isn't allocated. Make
+      // the front region the new active region.
+      active_ = front_;
+    } else {
+      // The region after active_ is allocated, so make a zero-sized
+      // placeholder.
+      MakeActivePlaceholder();
+    }
+    return result;
+  }
+
+  // The active region can accommodate this allocation with room left over.
+  // Create a new region (allocated) of the requested size at the front of the
+  // active region, and adjust the active region to reflect the deficit.
+  MOJO_DCHECK(active_->size > size);
+  Region *allocated = get_free(true, size, active_->offset);
+  active_->size -= size;
+  active_->offset += size;
+  insert_before(allocated, active_);
+  return allocated->offset;
+}
+
+void FifoAllocator::ReleaseRegion(uint64_t size, uint64_t offset) {
+  // Start at active_->next. That's usually the region we're looking for.
+  bool released =
+      Release(size, offset, active_->next, nullptr) ||
+      Release(size, offset, front_, active_);
+  MOJO_DCHECK(released);
+}
+
+bool FifoAllocator::Release(
+    uint64_t size,
+    uint64_t offset,
+    Region* begin,
+    Region* end) {
+  MOJO_DCHECK(begin != nullptr || end == nullptr);
+  for (Region* region = begin; region != end; region = region->next) {
+    if (region->offset == offset) {
+      MOJO_DCHECK(region->allocated);
+      MOJO_DCHECK(region->size == size);
+      region->allocated = false;
+
+      Region *prev = region->prev;
+      if (prev != nullptr && !prev->allocated) {
+        // Coalesce wtih the previous region.
+        prev->size += region->size;
+        remove(region);
+        put_free(region);
+        region = prev;
+      }
+
+      Region *next = region->next;
+      if (next != nullptr && !next->allocated) {
+        // Coalesce wtih the next region.
+        next->offset = region->offset;
+        next->size += region->size;
+        if (active_ == region) {
+          // This can happen if we coalesced the previous region.
+          active_ = next;
+        }
+        remove(region);
+        put_free(region);
+      }
+
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool FifoAllocator::AdvanceActive(uint64_t size) {
+  MOJO_DCHECK(size != 0);
+  return
+      AdvanceActive(size, active_->next, nullptr) ||
+      AdvanceActive(size, front_, active_);
+}
+
+bool FifoAllocator::AdvanceActive(uint64_t size, Region* begin, Region* end) {
+  for (Region* region = begin; region != end; region = region->next) {
+    if (!region->allocated && region->size >= size) {
+      if (active_->size == 0) {
+        // The old active region is zero-sized. Get rid of it.
+        MOJO_DCHECK(!active_->allocated);
+        remove(active_);
+        put_free(active_);
+      }
+      active_ = region;
+      return true;
+    }
+  }
+  return false;
+}
+
+void FifoAllocator::MakeActivePlaceholder() {
+  // If the old active region was at the back of the list, we'll be inserting
+  // at the front, so make the offset zero. We insert at the front, because it's
+  // a bit more efficient and because we don't need to implement insert_after.
+  Region *new_active = get_free(
+      false,
+      0,
+      active_ == back_ ? 0 : active_->offset + active_->size);
+
+  MOJO_DCHECK((active_ == back_) == (active_->next == nullptr));
+  insert_before(new_active, active_ == back_ ? front_ : active_->next);
+  active_ = new_active;
+}
+
+void FifoAllocator::remove(Region* region) {
+  MOJO_DCHECK(region);
+
+  if (front_ == region) {
+    MOJO_DCHECK(region->prev == nullptr);
+    front_ = region->next;
+  } else {
+    MOJO_DCHECK(region->prev);
+    MOJO_DCHECK(region->prev->next ==region);
+    region->prev->next = region->next;
+  }
+
+  if (back_ == region) {
+    MOJO_DCHECK(region->next == nullptr);
+    back_ = region->prev;
+  } else {
+    MOJO_DCHECK(region->next);
+    MOJO_DCHECK(region->next->prev == region);
+    region->next->prev = region->prev;
+  }
+}
+
+void FifoAllocator::insert_before(Region* region, Region* before_this) {
+  MOJO_DCHECK(region);
+  MOJO_DCHECK(before_this);
+
+  region->prev = before_this->prev;
+  before_this->prev = region;
+  region->next = before_this;
+  if (front_ == before_this) {
+    MOJO_DCHECK(region->prev == nullptr);
+    front_ = region;
+  } else {
+    MOJO_DCHECK(region->prev);
+    region->prev->next = region;
+  }
+}
+
+FifoAllocator::Region* FifoAllocator::get_free(
+    bool allocated, uint64_t size, uint64_t offset) {
+  MOJO_DCHECK(size <= size_);
+  MOJO_DCHECK(offset <= size_ - size);
+
+  Region *result = free_;
+  if (result == nullptr) {
+    result = new Region();
+  } else {
+    free_ = free_->next;
+  }
+
+  result->allocated = allocated;
+  result->size = size;
+  result->offset = offset;
+
+  return result;
+}
+
+void FifoAllocator::DeleteFrontToBack(Region* region) {
+  while (region != nullptr) {
+    Region *to_delete = region;
+    region = region->next;
+    delete to_delete;
+  }
+}
+
+}  // namespace media
+}  // namespace mojo
diff --git a/mojo/services/media/common/cpp/fifo_allocator.h b/mojo/services/media/common/cpp/fifo_allocator.h
new file mode 100644
index 0000000..538223e
--- /dev/null
+++ b/mojo/services/media/common/cpp/fifo_allocator.h
@@ -0,0 +1,147 @@
+// Copyright 2015 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.
+
+#ifndef MOJO_SERVICES_MEDIA_COMMON_CPP_FIFO_ALLOCATOR_H_
+#define MOJO_SERVICES_MEDIA_COMMON_CPP_FIFO_ALLOCATOR_H_
+
+#include <cstdint>
+#include <limits>
+
+namespace mojo {
+namespace media {
+
+// FifoAllocator implements heap semantics on a single contiguous buffer using
+// a strategy that is especially suited to streaming. Allocations can vary in
+// size, but the expectation is that regions will be released in roughly the
+// order they were allocated (hence 'Fifo'). It's important that FifoAllocator
+// be used in this way. FifoAllocator can deal with regions that don't get
+// released in the order they were allocated, but they can potentially fragment
+// the buffer and impact performance.
+//
+// FifoAllocator doesn't actually deal with any particular region of memory. It
+// simply does the bookkeeping regarding how a buffer of a given size is
+// allocated into regions.
+//
+// DESIGN:
+//
+// FifoAllocator maintains an ordered list of regions that partition the buffer.
+// Some regions are allocated and some are free. Free regions are always
+// coalesced, so there are no two adjacent free regions. Allocated regions are
+// not coalesced. There is always at least one free region.
+//
+// One free region is distinguished as the 'active' region. New allocations are
+// taken from the front of the active region. If the active region is too small
+// to accommodate a requested allocation, FifoAllocator walks the list looking
+// for an unallocated region that's large enough. The old active region becomes
+// an unused scrap that is recovered when the active region catches up to it
+// again. In some cases, the active region has a length of zero.
+//
+// The allocation strategy that emerges from all this is well-suited to many
+// streaming scenarios in which packets vary in size. If packets are of
+// consistent size, this strategy will still work, but is overkill given that
+// a fixed set of regions can be preallocated from the buffer.
+//
+// An internal region structure is employed to represent the list of regions.
+// FifoAllocator keeps unused region structures in a lookaside and never deletes
+// them until the FifoAllocator is deleted. This could cause a large number of
+// region structures to sit unused if the number of regions ever gets large.
+// This is generally not an issue for the streaming scenarios for which the
+// class is intended.
+//
+// Deallocations (releases) employ a sequential search for a matching
+// region. The search is done starting immediately after the active region, so
+// it typically finds the desired region immediately. If the number of regions
+// is very large and deallocation is frequently done out of order, the
+// sequential searches may be a performance issue.
+class FifoAllocator {
+  public:
+   // Returned by AllocatedRegion when the requested allocation cannot be
+   // performed.
+   static const uint64_t kNullOffset = std::numeric_limits<uint64_t>::max();
+
+   FifoAllocator(uint64_t size);
+
+   ~FifoAllocator();
+
+   // Returns the size of the entire buffer as determined by the call to the
+   // constructor or the most recent call to Reset.
+   uint64_t size() const {
+     return size_;
+   }
+
+   // Resets the buffer manager to its initial state (no regions allocated)
+   // with a new buffer size. Also deletes all the regions in the lookaside.
+   void Reset(uint64_t size);
+
+   // Allocates a region and returns its offset or kNullOffset if the allocation
+   // could not be performed.
+   uint64_t AllocateRegion(uint64_t size);
+
+   // Releases a previously-allocated region.
+   void ReleaseRegion(uint64_t size, uint64_t offset);
+
+ private:
+  // List element to track allocated and free regions.
+  struct Region {
+    bool allocated;
+    uint64_t size;
+    uint64_t offset;
+
+    // Intrusive list pointers.
+    Region* prev;
+    Region* next;
+  };
+
+  // Releases the specified region if it's found between begin (inclusive) and
+  // end (exclusive).
+  bool Release(uint64_t size, uint64_t offset, Region* begin, Region* end);
+
+  // Advances the active region to one that's at least the specified size.
+  // Returns false if none could be found.
+  bool AdvanceActive(uint64_t size);
+
+  // Does the above for the interval between begin (inclusive) and end
+  // (exclusive).
+  bool AdvanceActive(uint64_t size, Region* begin, Region* end);
+
+  // Inserts a zero-sized region after active_ and makes that the active region.
+  void MakeActivePlaceholder();
+
+  // Deletes a list of regions by following their next pointers.
+  void DeleteFrontToBack(Region* region);
+
+  // Removes a region from the list.
+  void remove(Region* region);
+
+  // Inserts a region into the list before the specified region.
+  void insert_before(Region* region, Region* before_this);
+
+  // gets a free region structure, checking the lookaside first.
+  Region* get_free(bool allocated, uint64_t size, uint64_t offset);
+
+  // Saves a unused region structure to the lookaside.
+  void put_free(Region* region) {
+    region->next = free_;
+    free_ = region;
+  }
+
+  // Total size of the buffer to be managed. The sum of the sizes of all the
+  // regions in the list should equal size_.
+  uint64_t size_;
+
+  // Doubly-linked intrusive list of current regions in offset order.
+  Region* front_;
+  Region* back_;
+
+  // Lookaside for free region objects.
+  Region* free_;
+
+  // Unallocated region from which allocations are currently being made.
+  Region* active_;
+};
+
+}  // namespace media
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_MEDIA_COMMON_CPP_FIFO_ALLOCATOR_H_
diff --git a/mojo/services/media/common/cpp/mapped_shared_buffer.cc b/mojo/services/media/common/cpp/mapped_shared_buffer.cc
new file mode 100644
index 0000000..5d0cb4b
--- /dev/null
+++ b/mojo/services/media/common/cpp/mapped_shared_buffer.cc
@@ -0,0 +1,104 @@
+// Copyright 2015 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 "mojo/public/cpp/environment/logging.h"
+#include "mojo/services/media/common/cpp/mapped_shared_buffer.h"
+
+namespace mojo {
+namespace media {
+
+MappedSharedBuffer::MappedSharedBuffer() {}
+
+MappedSharedBuffer::~MappedSharedBuffer() {}
+
+void MappedSharedBuffer::InitNew(uint64_t size) {
+  MOJO_DCHECK(size > 0);
+
+  buffer_.reset(new SharedBuffer(size));
+  handle_.reset();
+
+  InitInternal(buffer_->handle, size);
+}
+
+void MappedSharedBuffer::InitFromHandle(
+    ScopedSharedBufferHandle handle,
+    uint64_t size) {
+  MOJO_DCHECK(handle.is_valid());
+  MOJO_DCHECK(size > 0);
+
+  buffer_.reset();
+  handle_ = handle.Pass();
+
+  InitInternal(handle_, size);
+}
+
+void MappedSharedBuffer::InitInternal(
+    ScopedSharedBufferHandle& handle,
+    uint64_t size) {
+  MOJO_DCHECK(handle.is_valid());
+  MOJO_DCHECK(size > 0);
+
+  size_ = size;
+  buffer_ptr_.reset();
+
+  void* ptr;
+  auto result = MapBuffer(
+      handle.get(),
+      0, // offset
+      size,
+      &ptr,
+      MOJO_MAP_BUFFER_FLAG_NONE);
+  MOJO_DCHECK(result == MOJO_RESULT_OK);
+  MOJO_DCHECK(ptr);
+
+  buffer_ptr_.reset(reinterpret_cast<uint8_t*>(ptr));
+
+  OnInit();
+}
+
+bool MappedSharedBuffer::initialized() const {
+  return buffer_ptr_ != nullptr;
+}
+
+uint64_t MappedSharedBuffer::size() const {
+  return size_;
+}
+
+ScopedSharedBufferHandle MappedSharedBuffer::GetDuplicateHandle() const {
+  MOJO_DCHECK(initialized());
+  ScopedSharedBufferHandle handle;
+  if (buffer_) {
+    DuplicateBuffer(buffer_->handle.get(), nullptr, &handle);
+  } else {
+    MOJO_DCHECK(handle_.is_valid());
+    DuplicateBuffer(handle_.get(), nullptr, &handle);
+  }
+  return handle.Pass();
+}
+
+void* MappedSharedBuffer::PtrFromOffset(uint64_t offset) const {
+  MOJO_DCHECK(buffer_ptr_);
+
+  if (offset == FifoAllocator::kNullOffset) {
+    return nullptr;
+  }
+
+  MOJO_DCHECK(offset < size_);
+  return buffer_ptr_.get() + offset;
+}
+
+uint64_t MappedSharedBuffer::OffsetFromPtr(void *ptr) const {
+  MOJO_DCHECK(buffer_ptr_);
+  if (ptr == nullptr) {
+    return FifoAllocator::kNullOffset;
+  }
+  uint64_t offset = reinterpret_cast<uint8_t*>(ptr) - buffer_ptr_.get();
+  MOJO_DCHECK(offset < size_);
+  return offset;
+}
+
+void MappedSharedBuffer::OnInit() {}
+
+} // namespace media
+} // namespace mojo
diff --git a/mojo/services/media/common/cpp/mapped_shared_buffer.h b/mojo/services/media/common/cpp/mapped_shared_buffer.h
new file mode 100644
index 0000000..5fd7e8a
--- /dev/null
+++ b/mojo/services/media/common/cpp/mapped_shared_buffer.h
@@ -0,0 +1,81 @@
+// Copyright 2015 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.
+
+#ifndef MOJO_SERVICES_MEDIA_COMMON_CPP_MAPPED_SHARED_BUFFER_H_
+#define MOJO_SERVICES_MEDIA_COMMON_CPP_MAPPED_SHARED_BUFFER_H_
+
+#include <memory>
+
+#include "mojo/public/cpp/system/buffer.h"
+#include "mojo/services/media/common/cpp/fifo_allocator.h"
+
+namespace mojo {
+namespace media {
+
+// MappedSharedBuffer simplifies the use of shared buffers by taking care of
+// mapping/unmapping and by providing offset/pointer translation. It can be
+// used when the caller wants to allocate its own buffer (InitNew) and when
+// the caller needs to use a buffer supplied by another party (InitFromHandle).
+// It can be used by itself when regions of the buffer are allocated by another
+// party. If the caller needs to allocate regions, SharedMediaBufferAllocator,
+// which is derived from MappedSharedBuffer, provides allocation semantics
+// using FifoAllocator.
+class MappedSharedBuffer {
+ public:
+  MappedSharedBuffer();
+
+  virtual ~MappedSharedBuffer();
+
+  // Initializes by creating a new shared buffer of the indicated size.
+  void InitNew(uint64_t size);
+
+  // Initializes from a handle to an existing shared buffer.
+  void InitFromHandle(ScopedSharedBufferHandle handle, uint64_t size);
+
+  // Indicates whether the buffer is initialized.
+  bool initialized() const;
+
+  // Gets the size of the buffer.
+  uint64_t size() const;
+
+  // Gets a duplicate handle for the shared buffer.
+  ScopedSharedBufferHandle GetDuplicateHandle() const;
+
+  // Translates an offset into a pointer.
+  void* PtrFromOffset(uint64_t offset) const;
+
+  // Translates a pointer into an offset.
+  uint64_t OffsetFromPtr(void *payload_ptr) const;
+
+ protected:
+  void InitInternal(ScopedSharedBufferHandle& handle, uint64_t size);
+
+  // Does nothing. Called when initialization is complete. Subclasses may
+  // override.
+  virtual void OnInit();
+
+ private:
+  struct MappedBufferDeleter {
+    inline void operator()(uint8_t* ptr) const {
+      UnmapBuffer(ptr);
+    }
+  };
+
+  // Size of the shared buffer.
+  uint64_t size_;
+
+  // Shared buffer when initialized with InitNew.
+  std::unique_ptr<SharedBuffer> buffer_;
+
+  // Handle to shared buffer when initialized with InitFromHandle.
+  ScopedSharedBufferHandle handle_;
+
+  // Pointer to the mapped buffer.
+  std::unique_ptr<uint8_t, MappedBufferDeleter> buffer_ptr_;
+};
+
+}  // namespace media
+}  // namespace mojo
+
+#endif // MOJO_SERVICES_MEDIA_COMMON_CPP_MAPPED_SHARED_BUFFER_H_
diff --git a/mojo/services/media/common/cpp/shared_media_buffer_allocator.cc b/mojo/services/media/common/cpp/shared_media_buffer_allocator.cc
new file mode 100644
index 0000000..0754203
--- /dev/null
+++ b/mojo/services/media/common/cpp/shared_media_buffer_allocator.cc
@@ -0,0 +1,20 @@
+// Copyright 2015 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 "mojo/services/media/common/cpp/shared_media_buffer_allocator.h"
+
+namespace mojo {
+namespace media {
+
+SharedMediaBufferAllocator::~SharedMediaBufferAllocator() {
+  std::lock_guard<std::mutex> lock(lock_);
+}
+
+void SharedMediaBufferAllocator::OnInit() {
+  std::lock_guard<std::mutex> lock(lock_);
+  fifo_allocator_.Reset(size());
+}
+
+} // namespace media
+} // namespace mojo
diff --git a/mojo/services/media/common/cpp/shared_media_buffer_allocator.h b/mojo/services/media/common/cpp/shared_media_buffer_allocator.h
new file mode 100644
index 0000000..d7b7011
--- /dev/null
+++ b/mojo/services/media/common/cpp/shared_media_buffer_allocator.h
@@ -0,0 +1,67 @@
+// Copyright 2015 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.
+
+#ifndef MOJO_SERVICES_MEDIA_COMMON_CPP_MAPPED_SHARED_MEDIA_BUFFER_ALLOCATOR_H_
+#define MOJO_SERVICES_MEDIA_COMMON_CPP_MAPPED_SHARED_MEDIA_BUFFER_ALLOCATOR_H_
+
+#include <memory>
+#include <mutex>  // NOLINT(build/c++11)
+
+#include "mojo/services/media/common/cpp/fifo_allocator.h"
+#include "mojo/services/media/common/cpp/mapped_shared_buffer.h"
+
+namespace mojo {
+namespace media {
+
+// SharedMediaBufferAllocator enhances MappedSharedBuffer by adding allocation
+// semantics (allocate and release) using FifoAllocator. This is useful in media
+// applications in which media buffers are typically allocated and released in
+// a first-allocated, first-released manner. SharedMediaBufferAllocator is
+// thread-safe.
+class SharedMediaBufferAllocator : public MappedSharedBuffer {
+ public:
+  static const uint64_t kNullOffset = FifoAllocator::kNullOffset;
+
+  SharedMediaBufferAllocator() : fifo_allocator_(0) {}
+
+  ~SharedMediaBufferAllocator() override;
+
+  // Allocates a region of the buffer returning an offset. If the requested
+  // region could not be allocated, returns kNullOffset.
+  uint64_t AllocateRegionByOffset(uint64_t size) {
+    std::lock_guard<std::mutex> lock(lock_);
+    return fifo_allocator_.AllocateRegion(size);
+  }
+
+  // Releases a region of the buffer previously allocated by calling
+  // AllocateRegionByOffset.
+  void ReleaseRegionByOffset(uint64_t size, uint64_t offset) {
+    std::lock_guard<std::mutex> lock(lock_);
+    fifo_allocator_.ReleaseRegion(size, offset);
+  }
+
+  // Allocates a region of the buffer returning a pointer. If the requested
+  // region could not be allocated, returns nullptr.
+  void* AllocateRegion(uint64_t size) {
+    return PtrFromOffset(AllocateRegionByOffset(size));
+  }
+
+  // Releases a region of the buffer previously allocated by calling
+  // AllocateRegion.
+  void ReleaseRegion(uint64_t size, void* ptr) {
+    ReleaseRegionByOffset(size, OffsetFromPtr(ptr));
+  }
+
+ protected:
+  void OnInit() override;
+
+ private:
+  mutable std::mutex lock_;
+  FifoAllocator fifo_allocator_;
+};
+
+}  // namespace media
+}  // namespace mojo
+
+#endif // MOJO_SERVICES_MEDIA_COMMON_CPP_MAPPED_SHARED_MEDIA_BUFFER_ALLOCATOR_H_