Bug 1102642 - Use ref counted compressed data within mediasource. r=mattwoodrow, a=sledru
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 27 Jan 2015 02:01:09 +1100
changeset 243072 8b4f59c3ae71
parent 243071 afc24a951c4e
child 243073 e26bc7acea8e
push id4382
push userryanvm@gmail.com
push date2015-01-28 14:58 +0000
treeherdermozilla-beta@f35aa2298df8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, sledru
bugs1102642
milestone36.0
Bug 1102642 - Use ref counted compressed data within mediasource. r=mattwoodrow, a=sledru
dom/media/MediaData.h
dom/media/mediasource/ContainerParser.cpp
dom/media/mediasource/ContainerParser.h
dom/media/mediasource/ResourceQueue.h
dom/media/mediasource/SourceBuffer.cpp
dom/media/mediasource/SourceBuffer.h
dom/media/mediasource/SourceBufferResource.cpp
dom/media/mediasource/SourceBufferResource.h
dom/media/mediasource/TrackBuffer.cpp
dom/media/mediasource/TrackBuffer.h
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -7,16 +7,18 @@
 #define MediaData_h
 
 #include "nsSize.h"
 #include "mozilla/gfx/Rect.h"
 #include "nsRect.h"
 #include "AudioSampleFormat.h"
 #include "nsIMemoryReporter.h"
 #include "SharedBuffer.h"
+#include "nsRefPtr.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 
 namespace layers {
 class Image;
 class ImageContainer;
 }
 
@@ -267,11 +269,20 @@ public:
             bool aKeyframe,
             int64_t aTimecode,
             IntSize aDisplay);
 
 protected:
   ~VideoData();
 };
 
+  // LargeDataBuffer is a ref counted fallible TArray.
+  // It is designed to share potentially big byte arrays.
+class LargeDataBuffer : public FallibleTArray<uint8_t> {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LargeDataBuffer);
+
+private:
+  ~LargeDataBuffer() {}
+};
+
 } // namespace mozilla
 
 #endif // MediaData_h
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -3,61 +3,69 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ContainerParser.h"
 
 #include "WebMBufferedParser.h"
 #include "mozilla/Endian.h"
-#include "mp4_demuxer/BufferStream.h"
 #include "mp4_demuxer/MoofParser.h"
 #include "prlog.h"
+#include "MediaData.h"
+#include "MP4Stream.h"
+#include "SourceBufferResource.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
 extern PRLogModuleInfo* GetMediaSourceAPILog();
 
 #define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
 #define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
 #define MSE_API(...)
 #endif
 
 namespace mozilla {
 
+ContainerParser::ContainerParser()
+  : mInitData(new LargeDataBuffer())
+  , mHasInitData(false)
+{
+}
+
 bool
-ContainerParser::IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
+ContainerParser::IsInitSegmentPresent(LargeDataBuffer* aData)
 {
 MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
-            this, aLength,
-            aLength > 0 ? aData[0] : 0,
-            aLength > 1 ? aData[1] : 0,
-            aLength > 2 ? aData[2] : 0,
-            aLength > 3 ? aData[3] : 0);
+            this, aData->Length(),
+            aData->Length() > 0 ? (*aData)[0] : 0,
+            aData->Length() > 1 ? (*aData)[1] : 0,
+            aData->Length() > 2 ? (*aData)[2] : 0,
+            aData->Length() > 3 ? (*aData)[3] : 0);
 return false;
 }
 
 bool
-ContainerParser::IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
+ContainerParser::IsMediaSegmentPresent(LargeDataBuffer* aData)
 {
   MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
-            this, aLength,
-            aLength > 0 ? aData[0] : 0,
-            aLength > 1 ? aData[1] : 0,
-            aLength > 2 ? aData[2] : 0,
-            aLength > 3 ? aData[3] : 0);
+            this, aData->Length(),
+            aData->Length() > 0 ? (*aData)[0] : 0,
+            aData->Length() > 1 ? (*aData)[1] : 0,
+            aData->Length() > 2 ? (*aData)[2] : 0,
+            aData->Length() > 3 ? (*aData)[3] : 0);
   return false;
 }
 
 bool
-ContainerParser::ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
+ContainerParser::ParseStartAndEndTimestamps(LargeDataBuffer* aData,
                                             int64_t& aStart, int64_t& aEnd)
 {
   return false;
 }
 
 bool
 ContainerParser::TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
 {
@@ -66,106 +74,111 @@ ContainerParser::TimestampsFuzzyEqual(in
 
 int64_t
 ContainerParser::GetRoundingError()
 {
   NS_WARNING("Using default ContainerParser::GetRoundingError implementation");
   return 0;
 }
 
-const nsTArray<uint8_t>&
+LargeDataBuffer*
 ContainerParser::InitData()
 {
   MOZ_ASSERT(mHasInitData);
   return mInitData;
 }
 
 class WebMContainerParser : public ContainerParser {
 public:
   WebMContainerParser()
     : mParser(0), mOffset(0)
   {}
 
   static const unsigned NS_PER_USEC = 1000;
   static const unsigned USEC_PER_SEC = 1000000;
 
-  bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
+  bool IsInitSegmentPresent(LargeDataBuffer* aData)
   {
-    ContainerParser::IsInitSegmentPresent(aData, aLength);
+    ContainerParser::IsInitSegmentPresent(aData);
     // XXX: This is overly primitive, needs to collect data as it's appended
     // to the SB and handle, rather than assuming everything is present in a
     // single aData segment.
     // 0x1a45dfa3 // EBML
     // ...
     // DocType == "webm"
     // ...
     // 0x18538067 // Segment (must be "unknown" size)
     // 0x1549a966 // -> Segment Info
     // 0x1654ae6b // -> One or more Tracks
-    if (aLength >= 4 &&
-        aData[0] == 0x1a && aData[1] == 0x45 && aData[2] == 0xdf && aData[3] == 0xa3) {
+    if (aData->Length() >= 4 &&
+        (*aData)[0] == 0x1a && (*aData)[1] == 0x45 && (*aData)[2] == 0xdf &&
+        (*aData)[3] == 0xa3) {
       return true;
     }
     return false;
   }
 
-  bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
+  bool IsMediaSegmentPresent(LargeDataBuffer* aData)
   {
-    ContainerParser::IsMediaSegmentPresent(aData, aLength);
+    ContainerParser::IsMediaSegmentPresent(aData);
     // XXX: This is overly primitive, needs to collect data as it's appended
     // to the SB and handle, rather than assuming everything is present in a
     // single aData segment.
     // 0x1a45dfa3 // EBML
     // ...
     // DocType == "webm"
     // ...
     // 0x18538067 // Segment (must be "unknown" size)
     // 0x1549a966 // -> Segment Info
     // 0x1654ae6b // -> One or more Tracks
-    if (aLength >= 4 &&
-        aData[0] == 0x1f && aData[1] == 0x43 && aData[2] == 0xb6 && aData[3] == 0x75) {
+    if (aData->Length() >= 4 &&
+        (*aData)[0] == 0x1f && (*aData)[1] == 0x43 && (*aData)[2] == 0xb6 &&
+        (*aData)[3] == 0x75) {
       return true;
     }
     return false;
   }
 
-  bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
+  bool ParseStartAndEndTimestamps(LargeDataBuffer* aData,
                                   int64_t& aStart, int64_t& aEnd)
   {
-    bool initSegment = IsInitSegmentPresent(aData, aLength);
+    bool initSegment = IsInitSegmentPresent(aData);
     if (initSegment) {
       mOffset = 0;
       mParser = WebMBufferedParser(0);
       mOverlappedMapping.Clear();
     }
 
     // XXX if it only adds new mappings, overlapped but not available
     // (e.g. overlap < 0) frames are "lost" from the reported mappings here.
     nsTArray<WebMTimeDataOffset> mapping;
     mapping.AppendElements(mOverlappedMapping);
     mOverlappedMapping.Clear();
     ReentrantMonitor dummy("dummy");
-    mParser.Append(aData, aLength, mapping, dummy);
+    mParser.Append(aData->Elements(), aData->Length(), mapping, dummy);
 
     // XXX This is a bit of a hack.  Assume if there are no timecodes
     // present and it's an init segment that it's _just_ an init segment.
     // We should be more precise.
     if (initSegment) {
-      uint32_t length = aLength;
+      uint32_t length = aData->Length();
       if (!mapping.IsEmpty()) {
         length = mapping[0].mSyncOffset;
-        MOZ_ASSERT(length <= aLength);
+        MOZ_ASSERT(length <= aData->Length());
       }
       MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
                 this, length);
-
-      mInitData.ReplaceElementsAt(0, mInitData.Length(), aData, length);
+      if (!mInitData->ReplaceElementsAt(0, mInitData->Length(),
+                                        aData->Elements(), length)) {
+        // Unlikely OOM
+        return false;
+      }
       mHasInitData = true;
     }
-    mOffset += aLength;
+    mOffset += aData->Length();
 
     if (mapping.IsEmpty()) {
       return false;
     }
 
     // Exclude frames that we don't enough data to cover the end of.
     uint32_t endIdx = mapping.Length() - 1;
     while (mOffset < mapping[endIdx].mEndOffset && endIdx > 0) {
@@ -200,89 +213,96 @@ private:
   nsTArray<WebMTimeDataOffset> mOverlappedMapping;
   int64_t mOffset;
 };
 
 class MP4ContainerParser : public ContainerParser {
 public:
   MP4ContainerParser() :mMonitor("MP4ContainerParser Index Monitor") {}
 
-  bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
+  bool IsInitSegmentPresent(LargeDataBuffer* aData)
   {
-    ContainerParser::IsInitSegmentPresent(aData, aLength);
+    ContainerParser::IsInitSegmentPresent(aData);
     // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
     // file is the 'ftyp' atom followed by a file type. We just check for a
     // vaguely valid 'ftyp' atom.
 
-    if (aLength < 8) {
+    if (aData->Length() < 8) {
       return false;
     }
 
-    uint32_t chunk_size = BigEndian::readUint32(aData);
+    uint32_t chunk_size = BigEndian::readUint32(aData->Elements());
     if (chunk_size < 8) {
       return false;
     }
 
-    return aData[4] == 'f' && aData[5] == 't' && aData[6] == 'y' &&
-           aData[7] == 'p';
+    return (*aData)[4] == 'f' && (*aData)[5] == 't' && (*aData)[6] == 'y' &&
+           (*aData)[7] == 'p';
   }
 
-  bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
+  bool IsMediaSegmentPresent(LargeDataBuffer* aData)
   {
-    ContainerParser::IsMediaSegmentPresent(aData, aLength);
-    if (aLength < 8) {
+    ContainerParser::IsMediaSegmentPresent(aData);
+    if (aData->Length() < 8) {
       return false;
     }
 
-    uint32_t chunk_size = BigEndian::readUint32(aData);
+    uint32_t chunk_size = BigEndian::readUint32(aData->Elements());
     if (chunk_size < 8) {
       return false;
     }
 
-    return (aData[4] == 'm' && aData[5] == 'o' && aData[6] == 'o' && aData[7] == 'f') ||
-           (aData[4] == 's' && aData[5] == 't' && aData[6] == 'y' && aData[7] == 'p');
+    return ((*aData)[4] == 'm' && (*aData)[5] == 'o' && (*aData)[6] == 'o' &&
+            (*aData)[7] == 'f') ||
+           ((*aData)[4] == 's' && (*aData)[5] == 't' && (*aData)[6] == 'y' &&
+            (*aData)[7] == 'p');
   }
 
-  bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
+  bool ParseStartAndEndTimestamps(LargeDataBuffer* aData,
                                   int64_t& aStart, int64_t& aEnd)
   {
     MonitorAutoLock mon(mMonitor); // We're not actually racing against anything,
                                    // but mParser requires us to hold a monitor.
-    bool initSegment = IsInitSegmentPresent(aData, aLength);
+    bool initSegment = IsInitSegmentPresent(aData);
     if (initSegment) {
-      mStream = new mp4_demuxer::BufferStream();
+      mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/mp4"));
+      mStream = new MP4Stream(mResource);
       // We use a timestampOffset of 0 for ContainerParser, and require
       // consumers of ParseStartAndEndTimestamps to add their timestamp offset
       // manually. This allows the ContainerParser to be shared across different
       // timestampOffsets.
       mParser = new mp4_demuxer::MoofParser(mStream, 0, 0, &mMonitor);
     } else if (!mStream || !mParser) {
       return false;
     }
 
-    mStream->AppendBytes(aData, aLength);
+    mResource->AppendData(aData);
     nsTArray<MediaByteRange> byteRanges;
-    byteRanges.AppendElement(mStream->GetByteRange());
+    MediaByteRange mbr =
+      MediaByteRange(mParser->mOffset, mResource->GetLength());
+    byteRanges.AppendElement(mbr);
     mParser->RebuildFragmentedIndex(byteRanges);
 
     if (initSegment) {
       const MediaByteRange& range = mParser->mInitRange;
       MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
                 this, range.mEnd - range.mStart);
 
-      mInitData.ReplaceElementsAt(0, mInitData.Length(),
-                                  aData + range.mStart,
-                                  range.mEnd - range.mStart);
+      if (!mInitData->ReplaceElementsAt(0, mInitData->Length(),
+                                        aData->Elements() + range.mStart,
+                                        range.mEnd - range.mStart)) {
+        // Super unlikely OOM
+        return false;
+      }
       mHasInitData = true;
     }
 
     mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
       mParser->GetCompositionRange(byteRanges);
-
-    mStream->DiscardBefore(mParser->mOffset);
+    mResource->EvictData(mParser->mOffset, mParser->mOffset);
 
     if (compositionRange.IsNull()) {
       return false;
     }
     aStart = compositionRange.start;
     aEnd = compositionRange.end;
     MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld]",
               this, aStart, aEnd);
@@ -292,18 +312,19 @@ public:
   // Gaps of up to 20ms (marginally longer than a single frame at 60fps) are considered
   // to be sequential frames.
   int64_t GetRoundingError()
   {
     return 20000;
   }
 
 private:
-  nsRefPtr<mp4_demuxer::BufferStream> mStream;
+  nsRefPtr<MP4Stream> mStream;
   nsAutoPtr<mp4_demuxer::MoofParser> mParser;
+  nsRefPtr<SourceBufferResource> mResource;
   Monitor mMonitor;
 };
 
 /*static*/ ContainerParser*
 ContainerParser::CreateForMIMEType(const nsACString& aType)
 {
   if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) {
     return new WebMContainerParser();
--- a/dom/media/mediasource/ContainerParser.h
+++ b/dom/media/mediasource/ContainerParser.h
@@ -2,56 +2,58 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_CONTAINERPARSER_H_
 #define MOZILLA_CONTAINERPARSER_H_
 
-#include "nsTArray.h"
+#include "nsRefPtr.h"
 
 namespace mozilla {
 
+class LargeDataBuffer;
+
 class ContainerParser {
 public:
-  ContainerParser() : mHasInitData(false) {}
+  ContainerParser();
   virtual ~ContainerParser() {}
 
   // Return true if aData starts with an initialization segment.
   // The base implementation exists only for debug logging and is expected
   // to be called first from the overriding implementation.
-  virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength);
+  virtual bool IsInitSegmentPresent(LargeDataBuffer* aData);
 
   // Return true if aData starts with a media segment.
   // The base implementation exists only for debug logging and is expected
   // to be called first from the overriding implementation.
-  virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength);
+  virtual bool IsMediaSegmentPresent(LargeDataBuffer* aData);
 
   // Parse aData to extract the start and end frame times from the media
   // segment.  aData may not start on a parser sync boundary.  Return true
   // if aStart and aEnd have been updated.
-  virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
+  virtual bool ParseStartAndEndTimestamps(LargeDataBuffer* aData,
                                           int64_t& aStart, int64_t& aEnd);
 
   // Compare aLhs and rHs, considering any error that may exist in the
   // timestamps from the format's base representation.  Return true if aLhs
   // == aRhs within the error epsilon.
   bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs);
 
   virtual int64_t GetRoundingError();
 
-  const nsTArray<uint8_t>& InitData();
+  LargeDataBuffer* InitData();
 
   bool HasInitData()
   {
     return mHasInitData;
   }
 
   static ContainerParser* CreateForMIMEType(const nsACString& aType);
 
 protected:
-  nsTArray<uint8_t> mInitData;
+  nsRefPtr<LargeDataBuffer> mInitData;
   bool mHasInitData;
 };
 
 } // namespace mozilla
 #endif /* MOZILLA_CONTAINERPARSER_H_ */
--- a/dom/media/mediasource/ResourceQueue.h
+++ b/dom/media/mediasource/ResourceQueue.h
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_RESOURCEQUEUE_H_
 #define MOZILLA_RESOURCEQUEUE_H_
 
 #include <algorithm>
 #include "nsDeque.h"
-#include "nsTArray.h"
+#include "MediaData.h"
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetSourceBufferResourceLog();
 
 #define SBR_DEBUG(...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #define SBR_DEBUGV(...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
 #else
@@ -31,31 +31,32 @@ namespace mozilla {
 
 // Data is evicted once it reaches a size threshold. This pops the items off
 // the front of the queue and deletes it.  If an eviction happens then the
 // MediaSource is notified (done in SourceBuffer::AppendData) which then
 // requests all SourceBuffers to evict data up to approximately the same
 // timepoint.
 
 struct ResourceItem {
-  ResourceItem(const uint8_t* aData, uint32_t aSize) {
-    mData.AppendElements(aData, aSize);
+  explicit ResourceItem(LargeDataBuffer* aData)
+  : mData(aData)
+  {
   }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
     // size including this
     size_t size = aMallocSizeOf(this);
 
     // size excluding this
-    size += mData.SizeOfExcludingThis(aMallocSizeOf);
+    size += mData->SizeOfExcludingThis(aMallocSizeOf);
 
     return size;
   }
 
-  nsTArray<uint8_t> mData;
+  nsRefPtr<LargeDataBuffer> mData;
 };
 
 class ResourceQueueDeallocator : public nsDequeFunctor {
   virtual void* operator() (void* aObject) {
     delete static_cast<ResourceItem*>(aObject);
     return nullptr;
   }
 };
@@ -82,63 +83,63 @@ public:
 
   // Copies aCount bytes from aOffset in the queue into aDest.
   void CopyData(uint64_t aOffset, uint32_t aCount, char* aDest) {
     uint32_t offset = 0;
     uint32_t start = GetAtOffset(aOffset, &offset);
     uint32_t end = std::min(GetAtOffset(aOffset + aCount, nullptr) + 1, uint32_t(GetSize()));
     for (uint32_t i = start; i < end; ++i) {
       ResourceItem* item = ResourceAt(i);
-      uint32_t bytes = std::min(aCount, uint32_t(item->mData.Length() - offset));
+      uint32_t bytes = std::min(aCount, uint32_t(item->mData->Length() - offset));
       if (bytes != 0) {
-        memcpy(aDest, &item->mData[offset], bytes);
+        memcpy(aDest, &(*item->mData)[offset], bytes);
         offset = 0;
         aCount -= bytes;
         aDest += bytes;
       }
     }
   }
 
-  void AppendItem(const uint8_t* aData, uint32_t aLength) {
-    mLogicalLength += aLength;
-    Push(new ResourceItem(aData, aLength));
+  void AppendItem(LargeDataBuffer* aData) {
+    mLogicalLength += aData->Length();
+    Push(new ResourceItem(aData));
   }
 
   // Tries to evict at least aSizeToEvict from the queue up until
   // aOffset. Returns amount evicted.
   uint32_t Evict(uint64_t aOffset, uint32_t aSizeToEvict) {
     SBR_DEBUG("ResourceQueue(%p)::Evict(aOffset=%llu, aSizeToEvict=%u)",
               this, aOffset, aSizeToEvict);
     return EvictBefore(std::min(aOffset, (uint64_t)aSizeToEvict));
   }
 
   uint32_t EvictBefore(uint64_t aOffset) {
     SBR_DEBUG("ResourceQueue(%p)::EvictBefore(%llu)", this, aOffset);
     uint32_t evicted = 0;
     while (ResourceItem* item = ResourceAt(0)) {
       SBR_DEBUG("ResourceQueue(%p)::EvictBefore item=%p length=%d offset=%llu",
-                this, item, item->mData.Length(), mOffset);
-      if (item->mData.Length() + mOffset >= aOffset) {
+                this, item, item->mData->Length(), mOffset);
+      if (item->mData->Length() + mOffset >= aOffset) {
         break;
       }
-      mOffset += item->mData.Length();
-      evicted += item->mData.Length();
+      mOffset += item->mData->Length();
+      evicted += item->mData->Length();
       delete PopFront();
     }
     return evicted;
   }
 
   uint32_t EvictAll() {
     SBR_DEBUG("ResourceQueue(%p)::EvictAll()", this);
     uint32_t evicted = 0;
     while (ResourceItem* item = ResourceAt(0)) {
       SBR_DEBUG("ResourceQueue(%p)::EvictAll item=%p length=%d offset=%llu",
-                this, item, item->mData.Length(), mOffset);
-      mOffset += item->mData.Length();
-      evicted += item->mData.Length();
+                this, item, item->mData->Length(), mOffset);
+      mOffset += item->mData->Length();
+      evicted += item->mData->Length();
       delete PopFront();
     }
     return evicted;
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
     // Calculate the size of the internal deque.
     size_t size = nsDeque::SizeOfExcludingThis(aMallocSizeOf);
@@ -158,17 +159,17 @@ public:
       ResourceItem* item = ResourceAt(i);
 
       char buf[255];
       PR_snprintf(buf, sizeof(buf), "%s/%08u.bin", aPath, i);
       FILE* fp = fopen(buf, "wb");
       if (!fp) {
         return;
       }
-      fwrite(item->mData.Elements(), item->mData.Length(), 1, fp);
+      fwrite(item->mData->Elements(), item->mData->Length(), 1, fp);
       fclose(fp);
     }
   }
 #endif
 
 private:
   ResourceItem* ResourceAt(uint32_t aIndex) const {
     return static_cast<ResourceItem*>(ObjectAt(aIndex));
@@ -181,23 +182,23 @@ private:
   // untouched.
   uint32_t GetAtOffset(uint64_t aOffset, uint32_t *aResourceOffset) {
     MOZ_ASSERT(aOffset >= mOffset);
     uint64_t offset = mOffset;
     for (uint32_t i = 0; i < uint32_t(GetSize()); ++i) {
       ResourceItem* item = ResourceAt(i);
       // If the item contains the start of the offset we want to
       // break out of the loop.
-      if (item->mData.Length() + offset > aOffset) {
+      if (item->mData->Length() + offset > aOffset) {
         if (aResourceOffset) {
           *aResourceOffset = aOffset - offset;
         }
         return i;
       }
-      offset += item->mData.Length();
+      offset += item->mData->Length();
     }
     return GetSize();
   }
 
   ResourceItem* PopFront() {
     return static_cast<ResourceItem*>(nsDeque::PopFront());
   }
 
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SourceBuffer.h"
 
 #include "AsyncEventRunner.h"
+#include "MediaData.h"
 #include "MediaSourceUtils.h"
 #include "TrackBuffer.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/MediaSourceBinding.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "nsError.h"
@@ -39,37 +40,34 @@ extern PRLogModuleInfo* GetMediaSourceAP
 
 namespace mozilla {
 
 namespace dom {
 
 class AppendDataRunnable : public nsRunnable {
 public:
   AppendDataRunnable(SourceBuffer* aSourceBuffer,
-                     const uint8_t* aData,
-                     uint32_t aLength,
+                     LargeDataBuffer* aData,
                      double aTimestampOffset)
   : mSourceBuffer(aSourceBuffer)
+  , mData(aData)
   , mTimestampOffset(aTimestampOffset)
   {
-    mData.AppendElements(aData, aLength);
   }
 
   NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
 
-    mSourceBuffer->AppendData(mData.Elements(),
-                              mData.Length(),
-                              mTimestampOffset);
+    mSourceBuffer->AppendData(mData, mTimestampOffset);
 
     return NS_OK;
   }
 
 private:
   nsRefPtr<SourceBuffer> mSourceBuffer;
-  nsTArray<uint8_t> mData;
+  nsRefPtr<LargeDataBuffer> mData;
   double mTimestampOffset;
 };
 
 class RangeRemovalRunnable : public nsRunnable {
 public:
   RangeRemovalRunnable(SourceBuffer* aSourceBuffer,
                      double aStart,
                      double aEnd)
@@ -405,45 +403,47 @@ SourceBuffer::CheckEndTime()
     mMediaSource->SetDuration(endTime, MSRangeRemovalAction::SKIP);
   }
 }
 
 void
 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
   MSE_DEBUG("SourceBuffer(%p)::AppendData(aLength=%u)", this, aLength);
-  if (!PrepareAppend(aRv)) {
+
+  nsRefPtr<LargeDataBuffer> data = PrepareAppend(aData, aLength, aRv);
+  if (!data) {
     return;
   }
   StartUpdating();
 
   MOZ_ASSERT(mAppendMode == SourceBufferAppendMode::Segments,
              "We don't handle timestampOffset for sequence mode yet");
   nsRefPtr<nsIRunnable> task =
-    new AppendDataRunnable(this, aData, aLength, mTimestampOffset);
+    new AppendDataRunnable(this, data, mTimestampOffset);
   NS_DispatchToMainThread(task);
 }
 
 void
-SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, double aTimestampOffset)
+SourceBuffer::AppendData(LargeDataBuffer* aData, double aTimestampOffset)
 {
   if (!mUpdating) {
     // The buffer append algorithm has been interrupted by abort().
     //
     // If the sequence appendBuffer(), abort(), appendBuffer() occurs before
     // the first StopUpdating() runnable runs, then a second StopUpdating()
     // runnable will be scheduled, but still only one (the first) will queue
     // events.
     return;
   }
 
   MOZ_ASSERT(mMediaSource);
 
-  if (aLength) {
-    if (!mTrackBuffer->AppendData(aData, aLength, aTimestampOffset * USECS_PER_S)) {
+  if (aData->Length()) {
+    if (!mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)) {
       AppendError(true);
       return;
     }
 
     if (mTrackBuffer->HasInitSegment()) {
       mMediaSource->QueueInitializationEvent();
     }
 
@@ -471,50 +471,59 @@ SourceBuffer::AppendError(bool aDecoderE
   if (aDecoderError) {
     Optional<MediaSourceEndOfStreamError> decodeError(
       MediaSourceEndOfStreamError::Decode);
     ErrorResult dummy;
     mMediaSource->EndOfStream(decodeError, dummy);
   }
 }
 
-bool
-SourceBuffer::PrepareAppend(ErrorResult& aRv)
+already_AddRefed<LargeDataBuffer>
+SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return false;
+    return nullptr;
   }
   if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
     mMediaSource->SetReadyState(MediaSourceReadyState::Open);
   }
 
   // Eviction uses a byte threshold. If the buffer is greater than the
   // number of bytes then data is evicted. The time range for this
   // eviction is reported back to the media source. It will then
   // evict data before that range across all SourceBuffers it knows
   // about.
   // TODO: Make the eviction threshold smaller for audio-only streams.
   // TODO: Drive evictions off memory pressure notifications.
   // TODO: Consider a global eviction threshold  rather than per TrackBuffer.
   double newBufferStartTime = 0.0;
+  // Attempt to evict the amount of data we are about to add by lowering the
+  // threshold.
+  uint32_t toEvict =
+    (mEvictionThreshold > aLength) ? mEvictionThreshold - aLength : aLength;
   bool evicted =
     mTrackBuffer->EvictData(mMediaSource->GetDecoder()->GetCurrentTime(),
-                            mEvictionThreshold, &newBufferStartTime);
+                            toEvict, &newBufferStartTime);
   if (evicted) {
     MSE_DEBUG("SourceBuffer(%p)::AppendData Evict; current buffered start=%f",
               this, GetBufferedStart());
 
     // We notify that we've evicted from the time range 0 through to
     // the current start point.
     mMediaSource->NotifyEvicted(0.0, newBufferStartTime);
   }
 
+  nsRefPtr<LargeDataBuffer> data = new LargeDataBuffer();
+  if (!data->AppendElements(aData, aLength)) {
+    aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
+    return nullptr;
+  }
   // TODO: Test buffer full flag.
-  return true;
+  return data.forget();
 }
 
 double
 SourceBuffer::GetBufferedStart()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ErrorResult dummy;
   nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -24,16 +24,17 @@
 #include "nscore.h"
 
 class JSObject;
 struct JSContext;
 
 namespace mozilla {
 
 class ErrorResult;
+class LargeDataBuffer;
 class TrackBuffer;
 template <typename T> class AsyncEventRunner;
 
 namespace dom {
 
 class TimeRanges;
 
 class SourceBuffer MOZ_FINAL : public DOMEventTargetHelper
@@ -133,28 +134,29 @@ private:
 
   // If the media segment contains data beyond the current duration,
   // then run the duration change algorithm with new duration set to the
   // maximum of the current duration and the group end timestamp.
   void CheckEndTime();
 
   // Shared implementation of AppendBuffer overloads.
   void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
-  void AppendData(const uint8_t* aData, uint32_t aLength,
-                  double aTimestampOffset);
+  void AppendData(LargeDataBuffer* aData, double aTimestampOffset);
 
   // Implement the "Append Error Algorithm".
   // Will call endOfStream() with "decode" error if aDecodeError is true.
   // 3.5.3 Append Error Algorithm
   // http://w3c.github.io/media-source/#sourcebuffer-append-error
   void AppendError(bool aDecoderError);
 
-  // Implements the "Prepare Append Algorithm".  Returns true if the append
-  // may continue, or false (with aRv set) on error.
-  bool PrepareAppend(ErrorResult& aRv);
+  // Implements the "Prepare Append Algorithm". Returns LargeDataBuffer object
+  // on success or nullptr (with aRv set) on error.
+  already_AddRefed<LargeDataBuffer> PrepareAppend(const uint8_t* aData,
+                                                uint32_t aLength,
+                                                ErrorResult& aRv);
 
   nsRefPtr<MediaSource> mMediaSource;
 
   uint32_t mEvictionThreshold;
 
   nsRefPtr<TrackBuffer> mTrackBuffer;
 
   double mAppendWindowStart;
--- a/dom/media/mediasource/SourceBufferResource.cpp
+++ b/dom/media/mediasource/SourceBufferResource.cpp
@@ -6,16 +6,17 @@
 
 #include "SourceBufferResource.h"
 
 #include <algorithm>
 
 #include "nsISeekableStream.h"
 #include "nsISupports.h"
 #include "prlog.h"
+#include "MediaData.h"
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* GetSourceBufferResourceLog()
 {
   static PRLogModuleInfo* sLogModule;
   if (!sLogModule) {
     sLogModule = PR_NewLogModule("SourceBufferResource");
   }
@@ -195,21 +196,22 @@ uint32_t
 SourceBufferResource::EvictAll()
 {
   SBR_DEBUG("SourceBufferResource(%p)::EvictAll()", this);
   ReentrantMonitorAutoEnter mon(mMonitor);
   return mInputBuffer.EvictAll();
 }
 
 void
-SourceBufferResource::AppendData(const uint8_t* aData, uint32_t aLength)
+SourceBufferResource::AppendData(LargeDataBuffer* aData)
 {
-  SBR_DEBUG("SourceBufferResource(%p)::AppendData(aData=%p, aLength=%u)", this, aData, aLength);
+  SBR_DEBUG("SourceBufferResource(%p)::AppendData(aData=%p, aLength=%u)", this,
+            aData->Elements(), aData->Length());
   ReentrantMonitorAutoEnter mon(mMonitor);
-  mInputBuffer.AppendItem(aData, aLength);
+  mInputBuffer.AppendItem(aData);
   mEnded = false;
   mon.NotifyAll();
 }
 
 void
 SourceBufferResource::Ended()
 {
   SBR_DEBUG("SourceBufferResource(%p)::Ended()", this);
--- a/dom/media/mediasource/SourceBufferResource.h
+++ b/dom/media/mediasource/SourceBufferResource.h
@@ -31,16 +31,17 @@ extern PRLogModuleInfo* GetMediaSourceAP
 
 #define UNIMPLEMENTED() { /* Logging this is too spammy to do by default */ }
 
 class nsIStreamListener;
 
 namespace mozilla {
 
 class MediaDecoder;
+class LargeDataBuffer;
 
 namespace dom {
 
 class SourceBuffer;
 
 }  // namespace dom
 
 class SourceBufferResource MOZ_FINAL : public MediaResource
@@ -106,17 +107,17 @@ public:
 
   virtual size_t SizeOfIncludingThis(
                       MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   // Used by SourceBuffer.
-  void AppendData(const uint8_t* aData, uint32_t aLength);
+  void AppendData(LargeDataBuffer* aData);
   void Ended();
   // Remove data from resource if it holds more than the threshold
   // number of bytes. Returns amount evicted.
   uint32_t EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold);
 
   // Remove data from resource before the given offset.
   void EvictBefore(uint64_t aOffset);
 
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TrackBuffer.h"
 
 #include "ContainerParser.h"
+#include "MediaData.h"
 #include "MediaSourceDecoder.h"
 #include "SharedThreadPool.h"
 #include "MediaTaskQueue.h"
 #include "SourceBufferDecoder.h"
 #include "SourceBufferResource.h"
 #include "VideoUtils.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "mozilla/Preferences.h"
@@ -139,85 +140,85 @@ TrackBuffer::ContinueShutdown()
 
   mInitializedDecoders.Clear();
   mParentDecoder = nullptr;
 
   mShutdownPromise.Resolve(true, __func__);
 }
 
 bool
-TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength, int64_t aTimestampOffset)
+TrackBuffer::AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset)
 {
   MOZ_ASSERT(NS_IsMainThread());
   DecodersToInitialize decoders(this);
   // TODO: Run more of the buffer append algorithm asynchronously.
-  if (mParser->IsInitSegmentPresent(aData, aLength)) {
+  if (mParser->IsInitSegmentPresent(aData)) {
     MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this);
     if (!decoders.NewDecoder(aTimestampOffset)) {
       return false;
     }
   } else if (!mParser->HasInitData()) {
     MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
     return false;
   }
 
   int64_t start, end;
-  if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
+  if (mParser->ParseStartAndEndTimestamps(aData, start, end)) {
     start += aTimestampOffset;
     end += aTimestampOffset;
-    if (mParser->IsMediaSegmentPresent(aData, aLength) &&
+    if (mParser->IsMediaSegmentPresent(aData) &&
         mLastEndTimestamp &&
         (!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
          mLastTimestampOffset != aTimestampOffset ||
          mDecoderPerSegment || mCurrentDecoder->WasTrimmed())) {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
 
       // This data is earlier in the timeline than data we have already
       // processed, so we must create a new decoder to handle the decoding.
       if (!decoders.NewDecoder(aTimestampOffset)) {
         return false;
       }
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
-      const nsTArray<uint8_t>& initData = mParser->InitData();
-      AppendDataToCurrentResource(initData.Elements(), initData.Length(), end - start);
+      nsRefPtr<LargeDataBuffer> initData = mParser->InitData();
+      AppendDataToCurrentResource(initData, end - start);
       mLastStartTimestamp = start;
     } else {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp ? mLastEndTimestamp.value() : 0, start, end);
     }
     mLastEndTimestamp.reset();
     mLastEndTimestamp.emplace(end);
   }
 
-  if (!AppendDataToCurrentResource(aData, aLength, end - start)) {
+  if (!AppendDataToCurrentResource(aData, end - start)) {
     return false;
   }
 
   // Tell our reader that we have more data to ensure that playback starts if
   // required when data is appended.
   mParentDecoder->GetReader()->MaybeNotifyHaveData();
   return true;
 }
 
 bool
-TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength, uint32_t aDuration)
+TrackBuffer::AppendDataToCurrentResource(LargeDataBuffer* aData, uint32_t aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mCurrentDecoder) {
     return false;
   }
 
   SourceBufferResource* resource = mCurrentDecoder->GetResource();
   int64_t appendOffset = resource->GetLength();
-  resource->AppendData(aData, aLength);
+  resource->AppendData(aData);
   mCurrentDecoder->SetRealMediaDuration(mCurrentDecoder->GetRealMediaDuration() + aDuration);
   // XXX: For future reference: NDA call must run on the main thread.
-  mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
-                                     aLength, appendOffset);
+  mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData->Elements()),
+                                     aData->Length(), appendOffset);
   mParentDecoder->NotifyBytesDownloaded();
   mParentDecoder->NotifyTimeRangesChanged();
 
   return true;
 }
 
 class DecoderSorter
 {
--- a/dom/media/mediasource/TrackBuffer.h
+++ b/dom/media/mediasource/TrackBuffer.h
@@ -16,16 +16,17 @@
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nscore.h"
 
 namespace mozilla {
 
 class ContainerParser;
 class MediaSourceDecoder;
+class LargeDataBuffer;
 
 namespace dom {
 
 class TimeRanges;
 
 } // namespace dom
 
 class TrackBuffer MOZ_FINAL {
@@ -34,17 +35,17 @@ public:
 
   TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType);
 
   nsRefPtr<ShutdownPromise> Shutdown();
 
   // Append data to the current decoder.  Also responsible for calling
   // NotifyDataArrived on the decoder to keep buffered range computation up
   // to date.  Returns false if the append failed.
-  bool AppendData(const uint8_t* aData, uint32_t aLength, int64_t aTimestampOffset /* microseconds */);
+  bool AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset /* microseconds */);
 
   // Evicts data held in the current decoders SourceBufferResource from the
   // start of the buffer through to aPlaybackTime. aThreshold is used to
   // bound the data being evicted. It will not evict more than aThreshold
   // bytes. aBufferStartTime contains the new start time of the current
   // decoders buffered data after the eviction. Returns true if data was
   // evicted.
   bool EvictData(double aPlaybackTime, uint32_t aThreshold, double* aBufferStartTime);
@@ -115,17 +116,17 @@ private:
   // returns it. The new decoder must be queued using QueueInitializeDecoder
   // for initialization.
   // The decoder is not considered initialized until it is added to
   // mInitializedDecoders.
   already_AddRefed<SourceBufferDecoder> NewDecoder(int64_t aTimestampOffset /* microseconds */);
 
   // Helper for AppendData, ensures NotifyDataArrived is called whenever
   // data is appended to the current decoder's SourceBufferResource.
-  bool AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength,
+  bool AppendDataToCurrentResource(LargeDataBuffer* aData,
                                    uint32_t aDuration /* microseconds */);
 
   // Queue execution of InitializeDecoder on mTaskQueue.
   bool QueueInitializeDecoder(SourceBufferDecoder* aDecoder);
 
   // Runs decoder initialization including calling ReadMetadata.  Runs as an
   // event on the decode thread pool.
   void InitializeDecoder(SourceBufferDecoder* aDecoder);