Bug 1190238 - Part 1: Remove MediaResource::Read/Seek. r=cpearce, a=ritu
authorJean-Yves Avenard <jyavenard@mozilla.com>
Thu, 13 Aug 2015 11:15:48 +1000
changeset 288789 06da1c87b289768ccf0d6d59feea8c07989f8f71
parent 288788 33c0b0175e5fd925f3c7e6a28de0753958e72d25
child 288790 5e34ca12b2ef80632f7d6e5287fe91f56da1da9f
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, ritu
bugs1190238
milestone42.0a2
Bug 1190238 - Part 1: Remove MediaResource::Read/Seek. r=cpearce, a=ritu This functionality is now replaced with a dedicated new MediaResourceIndex class. This allows for concurrent Read/Seek use of the MediaResource without having side effects.
dom/media/BufferMediaResource.h
dom/media/MediaResource.cpp
dom/media/MediaResource.h
dom/media/RtspMediaResource.h
dom/media/apple/AppleMP3Reader.cpp
dom/media/apple/AppleMP3Reader.h
dom/media/directshow/DirectShowReader.cpp
dom/media/gstreamer/GStreamerReader.cpp
dom/media/gstreamer/GStreamerReader.h
dom/media/gtest/MockMediaResource.h
dom/media/mediasource/MediaSourceResource.h
dom/media/mediasource/SourceBufferResource.cpp
dom/media/mediasource/SourceBufferResource.h
dom/media/ogg/OggReader.cpp
dom/media/ogg/OggReader.h
dom/media/omx/MediaCodecReader.cpp
dom/media/omx/MediaOmxCommonReader.cpp
dom/media/omx/MediaOmxCommonReader.h
dom/media/omx/MediaOmxReader.cpp
dom/media/omx/MediaStreamSource.cpp
dom/media/omx/MediaStreamSource.h
dom/media/raw/RawReader.cpp
dom/media/raw/RawReader.h
dom/media/wave/WaveReader.cpp
dom/media/wave/WaveReader.h
dom/media/webm/WebMReader.cpp
dom/media/webm/WebMReader.h
--- a/dom/media/BufferMediaResource.h
+++ b/dom/media/BufferMediaResource.h
@@ -21,17 +21,16 @@ class BufferMediaResource : public Media
 {
 public:
   BufferMediaResource(const uint8_t* aBuffer,
                       uint32_t aLength,
                       nsIPrincipal* aPrincipal,
                       const nsACString& aContentType) :
     mBuffer(aBuffer),
     mLength(aLength),
-    mOffset(0),
     mPrincipal(aPrincipal),
     mContentType(aContentType)
   {
     MOZ_COUNT_CTOR(BufferMediaResource);
   }
 
 protected:
   virtual ~BufferMediaResource()
@@ -54,55 +53,25 @@ private:
   {
     return nullptr;
   }
 
   // These methods are called off the main thread.
   // The mode is initially MODE_PLAYBACK.
   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) override {}
   virtual void SetPlaybackRate(uint32_t aBytesPerSecond) override {}
-  virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) override
-  {
-    *aBytes = std::min(mLength - mOffset, aCount);
-    memcpy(aBuffer, mBuffer + mOffset, *aBytes);
-    mOffset += *aBytes;
-    MOZ_ASSERT(mOffset <= mLength);
-    return NS_OK;
-  }
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
                           uint32_t aCount, uint32_t* aBytes) override
   {
-    nsresult rv = Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
-    if (NS_FAILED(rv)) return rv;
-    return Read(aBuffer, aCount, aBytes);
-  }
-  virtual nsresult Seek(int32_t aWhence, int64_t aOffset) override
-  {
-    MOZ_ASSERT(aOffset <= UINT32_MAX);
-    switch (aWhence) {
-    case nsISeekableStream::NS_SEEK_SET:
-      if (aOffset < 0 || aOffset > mLength) {
-        return NS_ERROR_FAILURE;
-      }
-      mOffset = static_cast<uint32_t> (aOffset);
-      break;
-    case nsISeekableStream::NS_SEEK_CUR:
-      if (aOffset >= mLength - mOffset) {
-        return NS_ERROR_FAILURE;
-      }
-      mOffset += static_cast<uint32_t> (aOffset);
-      break;
-    case nsISeekableStream::NS_SEEK_END:
-      if (aOffset < 0 || aOffset > mLength) {
-        return NS_ERROR_FAILURE;
-      }
-      mOffset = mLength - aOffset;
-      break;
+    if (aOffset < 0 || aOffset > mLength) {
+      return NS_ERROR_FAILURE;
     }
-
+    *aBytes = std::min(mLength - static_cast<uint32_t>(aOffset), aCount);
+    memcpy(aBuffer, mBuffer + aOffset, *aBytes);
+    mOffset = aOffset + *aBytes;
     return NS_OK;
   }
   virtual int64_t Tell() override { return mOffset; }
 
   virtual void Pin() override {}
   virtual void Unpin() override {}
   virtual double GetDownloadRate(bool* aIsReliable) override { *aIsReliable = false; return 0.; }
   virtual int64_t GetLength() override { return mLength; }
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -719,30 +719,16 @@ void ChannelMediaResource::CloseChannel(
 
 nsresult ChannelMediaResource::ReadFromCache(char* aBuffer,
                                              int64_t aOffset,
                                              uint32_t aCount)
 {
   return mCacheStream.ReadFromCache(aBuffer, aOffset, aCount);
 }
 
-nsresult ChannelMediaResource::Read(char* aBuffer,
-                                    uint32_t aCount,
-                                    uint32_t* aBytes)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
-
-  int64_t offset = mCacheStream.Tell();
-  nsresult rv = mCacheStream.Read(aBuffer, aCount, aBytes);
-  if (NS_SUCCEEDED(rv)) {
-    DispatchBytesConsumed(*aBytes, offset);
-  }
-  return rv;
-}
-
 nsresult ChannelMediaResource::ReadAt(int64_t aOffset,
                                       char* aBuffer,
                                       uint32_t aCount,
                                       uint32_t* aBytes)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   nsresult rv = mCacheStream.ReadAt(aOffset, aBuffer, aCount, aBytes);
@@ -772,25 +758,16 @@ ChannelMediaResource::MediaReadAt(int64_
     aOffset += bytesRead;
     aCount -= bytesRead;
     curr += bytesRead;
   }
   bytes->SetLength(curr - start);
   return bytes.forget();
 }
 
-nsresult ChannelMediaResource::Seek(int32_t aWhence, int64_t aOffset)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
-
-  CMLOG("Seek requested for aOffset [%lld] for decoder [%p]",
-        aOffset, mDecoder);
-  return mCacheStream.Seek(aWhence, aOffset);
-}
-
 int64_t ChannelMediaResource::Tell()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   return mCacheStream.Tell();
 }
 
 nsresult ChannelMediaResource::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
@@ -1205,22 +1182,19 @@ public:
   virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) override;
   virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) override;
 
   // These methods are called off the main thread.
 
   // Other thread
   virtual void     SetReadMode(MediaCacheStream::ReadMode aMode) override {}
   virtual void     SetPlaybackRate(uint32_t aBytesPerSecond) override {}
-  virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) override;
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
                           uint32_t aCount, uint32_t* aBytes) override;
   virtual already_AddRefed<MediaByteBuffer> MediaReadAt(int64_t aOffset, uint32_t aCount) override;
-  virtual already_AddRefed<MediaByteBuffer> SilentReadAt(int64_t aOffset, uint32_t aCount) override;
-  virtual nsresult Seek(int32_t aWhence, int64_t aOffset) override;
   virtual int64_t  Tell() override;
 
   // Any thread
   virtual void    Pin() override {}
   virtual void    Unpin() override {}
   virtual double  GetDownloadRate(bool* aIsReliable) override
   {
     // The data's all already here
@@ -1496,31 +1470,16 @@ nsresult FileMediaResource::ReadFromCach
 
   // If a read failed in the loop above, we want to return its failure code.
   NS_ENSURE_SUCCESS(res,res);
 
   // Else we succeed if the reset-seek succeeds.
   return seekres;
 }
 
-nsresult FileMediaResource::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
-{
-  nsresult rv;
-  int64_t offset = 0;
-  {
-    MutexAutoLock lock(mLock);
-    mSeekable->Tell(&offset);
-    rv = UnsafeRead(aBuffer, aCount, aBytes);
-  }
-  if (NS_SUCCEEDED(rv)) {
-    DispatchBytesConsumed(*aBytes, offset);
-  }
-  return rv;
-}
-
 nsresult FileMediaResource::UnsafeRead(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
 {
   EnsureSizeInitialized();
   return mInput->Read(aBuffer, aCount, aBytes);
 }
 
 nsresult FileMediaResource::ReadAt(int64_t aOffset, char* aBuffer,
                                    uint32_t aCount, uint32_t* aBytes)
@@ -1568,40 +1527,16 @@ FileMediaResource::UnsafeMediaReadAt(int
     }
     aCount -= bytesRead;
     curr += bytesRead;
   }
   bytes->SetLength(curr - start);
   return bytes.forget();
 }
 
-already_AddRefed<MediaByteBuffer>
-FileMediaResource::SilentReadAt(int64_t aOffset, uint32_t aCount)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
-
-  MutexAutoLock lock(mLock);
-  int64_t pos = 0;
-  NS_ENSURE_TRUE(mSeekable, nullptr);
-  nsresult rv = mSeekable->Tell(&pos);
-  NS_ENSURE_SUCCESS(rv, nullptr);
-  nsRefPtr<MediaByteBuffer> bytes = UnsafeMediaReadAt(aOffset, aCount);
-  UnsafeSeek(nsISeekableStream::NS_SEEK_SET, pos);
-  NS_ENSURE_TRUE(bytes && bytes->Length() == aCount, nullptr);
-  return bytes.forget();
-}
-
-nsresult FileMediaResource::Seek(int32_t aWhence, int64_t aOffset)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
-
-  MutexAutoLock lock(mLock);
-  return UnsafeSeek(aWhence, aOffset);
-}
-
 nsresult FileMediaResource::UnsafeSeek(int32_t aWhence, int64_t aOffset)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   if (!mSeekable)
     return NS_ERROR_FAILURE;
   EnsureSizeInitialized();
   return mSeekable->Seek(aWhence, aOffset);
@@ -1748,10 +1683,53 @@ void BaseMediaResource::DispatchBytesCon
 {
   if (aNumBytes <= 0) {
     return;
   }
   RefPtr<nsIRunnable> event(new DispatchBytesConsumedEvent(mDecoder, aNumBytes, aOffset));
   NS_DispatchToMainThread(event);
 }
 
+nsresult
+MediaResourceIndex::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
+
+  // We purposefuly don't check that we may attempt to read past
+  // mResource->GetLength() as the resource's length may change over time.
+
+  nsresult rv = mResource->ReadAt(mOffset, aBuffer, aCount, aBytes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  mOffset += *aBytes;
+  return NS_OK;
+}
+
+nsresult
+MediaResourceIndex::Seek(int32_t aWhence, int64_t aOffset)
+{
+  switch (aWhence) {
+    case SEEK_SET:
+      break;
+    case SEEK_CUR:
+      aOffset += mOffset;
+      break;
+    case SEEK_END:
+    {
+      int64_t length = mResource->GetLength();
+      if (length == -1 || length - aOffset < 0) {
+        return NS_ERROR_FAILURE;
+      }
+      aOffset = mResource->GetLength() - aOffset;
+    }
+      break;
+    default:
+      return NS_ERROR_FAILURE;
+  }
+
+  mOffset = aOffset;
+
+  return NS_OK;
+}
+
 } // namespace mozilla
 
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -271,23 +271,16 @@ public:
 
   // These methods are called off the main thread.
   // The mode is initially MODE_PLAYBACK.
   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) = 0;
   // This is the client's estimate of the playback rate assuming
   // the media plays continuously. The cache can't guess this itself
   // because it doesn't know when the decoder was paused, buffering, etc.
   virtual void SetPlaybackRate(uint32_t aBytesPerSecond) = 0;
-  // Read up to aCount bytes from the stream. The buffer must have
-  // enough room for at least aCount bytes. Stores the number of
-  // actual bytes read in aBytes (0 on end of file).
-  // May read less than aCount bytes if the number of
-  // available bytes is less than aCount. Always check *aBytes after
-  // read, and call again if necessary.
-  virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) = 0;
   // Read up to aCount bytes from the stream. The read starts at
   // aOffset in the stream, seeking to that location initially if
   // it is not the current stream offset. The remaining arguments,
   // results and requirements are the same as per the Read method.
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
                           uint32_t aCount, uint32_t* aBytes) = 0;
   // This method returns nullptr if anything fails.
   // Otherwise, it returns an owned buffer.
@@ -310,69 +303,20 @@ public:
       aOffset += bytesRead;
       aCount -= bytesRead;
       curr += bytesRead;
     }
     bytes->SetLength(curr - start);
     return bytes.forget();
   }
 
-  // ReadAt without side-effects. Given that our MediaResource infrastructure
-  // is very side-effecty, this accomplishes its job by checking the initial
-  // position and seeking back to it. If the seek were to fail, a side-effect
-  // might be observable.
-  //
-  // This method returns null if anything fails, including the failure to read
-  // aCount bytes. Otherwise, it returns an owned buffer.
-  virtual already_AddRefed<MediaByteBuffer> SilentReadAt(int64_t aOffset, uint32_t aCount)
-  {
-    nsRefPtr<MediaByteBuffer> bytes = new MediaByteBuffer(aCount);
-    bytes->SetLength(aCount);
-    nsresult rv =
-      ReadFromCache(reinterpret_cast<char*>(bytes->Elements()), aOffset, aCount);
-    if (NS_SUCCEEDED(rv)) {
-      return bytes.forget();
-    }
-    int64_t pos = Tell();
-    // Free our buffer first to minimize memory usage.
-    bytes = nullptr;
-    bytes = MediaReadAt(aOffset, aCount);
-    Seek(nsISeekableStream::NS_SEEK_SET, pos);
-    NS_ENSURE_TRUE(bytes && bytes->Length() == aCount, nullptr);
-    return bytes.forget();
-  }
-
-  // Seek to the given bytes offset in the stream. aWhence can be
-  // one of:
-  //   NS_SEEK_SET
-  //   NS_SEEK_CUR
-  //   NS_SEEK_END
-  //
-  // In the Http strategy case the cancel will cause the http
-  // channel's listener to close the pipe, forcing an i/o error on any
-  // blocked read. This will allow the decode thread to complete the
-  // event.
-  //
-  // In the case of a seek in progress, the byte range request creates
-  // a new listener. This is done on the main thread via seek
-  // synchronously dispatching an event. This avoids the issue of us
-  // closing the listener but an outstanding byte range request
-  // creating a new one. They run on the same thread so no explicit
-  // synchronisation is required. The byte range request checks for
-  // the cancel flag and does not create a new channel or listener if
-  // we are cancelling.
-  //
-  // The default strategy does not do any seeking - the only issue is
-  // a blocked read which it handles by causing the listener to close
-  // the pipe, as per the http case.
-  //
-  // The file strategy doesn't block for any great length of time so
-  // is fine for a no-op cancel.
-  virtual nsresult Seek(int32_t aWhence, int64_t aOffset) = 0;
   // Report the current offset in bytes from the start of the stream.
+  // This is used to approximate where we currently are in the playback of a
+  // media.
+  // A call to ReadAt will update this position.
   virtual int64_t Tell() = 0;
   // Moves any existing channel loads into or out of background. Background
   // loads don't block the load event. This also determines whether or not any
   // new loads initiated (for example to seek) will be in the background.
   virtual void SetLoadInBackground(bool aLoadInBackground) {}
   // Ensures that the value returned by IsSuspendedByCache below is up to date
   // (i.e. the cache has examined this stream at least once).
   virtual void EnsureCacheUpToDate() {}
@@ -664,22 +608,20 @@ public:
     }
   }
   virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) override;
   virtual void     EnsureCacheUpToDate() override;
 
   // Other thread
   virtual void     SetReadMode(MediaCacheStream::ReadMode aMode) override;
   virtual void     SetPlaybackRate(uint32_t aBytesPerSecond) override;
-  virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) override;
   virtual nsresult ReadAt(int64_t offset, char* aBuffer,
                           uint32_t aCount, uint32_t* aBytes) override;
   virtual already_AddRefed<MediaByteBuffer> MediaReadAt(int64_t aOffset, uint32_t aCount) override;
-  virtual nsresult Seek(int32_t aWhence, int64_t aOffset) override;
-  virtual int64_t  Tell() override;
+  virtual int64_t Tell() override;
 
   // Any thread
   virtual void    Pin() override;
   virtual void    Unpin() override;
   virtual double  GetDownloadRate(bool* aIsReliable) override;
   virtual int64_t GetLength() override;
   virtual int64_t GetNextCachedData(int64_t aOffset) override;
   virtual int64_t GetCachedDataEnd(int64_t aOffset) override;
@@ -828,11 +770,102 @@ class MOZ_STACK_CLASS AutoPinned {
   operator T*() const { return mResource; }
   T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mResource; }
 
 private:
   T* mResource;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+/*
+ * MediaResourceIndex provides a way to access MediaResource objects.
+ * Read, Seek and Tell must only be called on non-main threads.
+ * In the case of the Ogg Decoder they are called on the Decode thread for
+ * example. You must ensure that no threads are calling these methods once
+ * the MediaResource has been Closed.
+ */
+
+class MediaResourceIndex
+{
+public:
+  explicit MediaResourceIndex(MediaResource* aResource)
+    : mResource(aResource)
+    , mOffset(0)
+  {}
+
+  // Read up to aCount bytes from the stream. The buffer must have
+  // enough room for at least aCount bytes. Stores the number of
+  // actual bytes read in aBytes (0 on end of file).
+  // May read less than aCount bytes if the number of
+  // available bytes is less than aCount. Always check *aBytes after
+  // read, and call again if necessary.
+  nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
+  // Seek to the given bytes offset in the stream. aWhence can be
+  // one of:
+  //   NS_SEEK_SET
+  //   NS_SEEK_CUR
+  //   NS_SEEK_END
+  //
+  // In the Http strategy case the cancel will cause the http
+  // channel's listener to close the pipe, forcing an i/o error on any
+  // blocked read. This will allow the decode thread to complete the
+  // event.
+  //
+  // In the case of a seek in progress, the byte range request creates
+  // a new listener. This is done on the main thread via seek
+  // synchronously dispatching an event. This avoids the issue of us
+  // closing the listener but an outstanding byte range request
+  // creating a new one. They run on the same thread so no explicit
+  // synchronisation is required. The byte range request checks for
+  // the cancel flag and does not create a new channel or listener if
+  // we are cancelling.
+  //
+  // The default strategy does not do any seeking - the only issue is
+  // a blocked read which it handles by causing the listener to close
+  // the pipe, as per the http case.
+  //
+  // The file strategy doesn't block for any great length of time so
+  // is fine for a no-op cancel.
+  nsresult Seek(int32_t aWhence, int64_t aOffset);
+  // Report the current offset in bytes from the start of the stream.
+  int64_t Tell() const { return mOffset; }
+
+  // Return the underlying MediaResource.
+  MediaResource* GetResource() const { return mResource; }
+
+  // Convenience methods, directly calling the MediaResource method of the same
+  // name.
+  // Those functions do not update the MediaResource offset as returned
+  // by Tell().
+
+  // Read up to aCount bytes from the stream. The read starts at
+  // aOffset in the stream, seeking to that location initially if
+  // it is not the current stream offset. The remaining arguments,
+  // results and requirements are the same as per the Read method.
+  nsresult ReadAt(int64_t aOffset, char* aBuffer,
+                  uint32_t aCount, uint32_t* aBytes) const
+  {
+    return mResource->ReadAt(aOffset, aBuffer, aCount, aBytes);
+  }
+  // This method returns nullptr if anything fails.
+  // Otherwise, it returns an owned buffer.
+  // MediaReadAt may return fewer bytes than requested if end of stream is
+  // encountered. There is no need to call it again to get more data.
+  already_AddRefed<MediaByteBuffer> MediaReadAt(int64_t aOffset, uint32_t aCount) const
+  {
+    return mResource->MediaReadAt(aOffset, aCount);
+  }
+  // Get the length of the stream in bytes. Returns -1 if not known.
+  // This can change over time; after a seek operation, a misbehaving
+  // server may give us a resource of a different length to what it had
+  // reported previously --- or it may just lie in its Content-Length
+  // header and give us more or less data than it reported. We will adjust
+  // the result of GetLength to reflect the data that's actually arriving.
+  int64_t GetLength() const { return mResource->GetLength(); }
+
+private:
+  nsRefPtr<MediaResource> mResource;
+  int64_t mOffset;
+};
+
 } // namespace mozilla
 
 #endif
--- a/dom/media/RtspMediaResource.h
+++ b/dom/media/RtspMediaResource.h
@@ -128,25 +128,16 @@ public:
                           uint32_t aCount, uint32_t* aBytes)  override{
     return NS_ERROR_FAILURE;
   }
   // dummy
   virtual void     SetReadMode(MediaCacheStream::ReadMode aMode) override {}
   // dummy
   virtual void     SetPlaybackRate(uint32_t aBytesPerSecond) override {}
   // dummy
-  virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
-  override {
-    return NS_OK;
-  }
-  // dummy
-  virtual nsresult Seek(int32_t aWhence, int64_t aOffset) override {
-    return NS_OK;
-  }
-  // dummy
   virtual int64_t  Tell() override { return 0; }
 
   // Any thread
   virtual void    Pin() override {}
   virtual void    Unpin() override {}
 
   virtual bool    IsSuspendedByCache() override { return mIsSuspend; }
 
--- a/dom/media/apple/AppleMP3Reader.cpp
+++ b/dom/media/apple/AppleMP3Reader.cpp
@@ -39,16 +39,17 @@ AppleMP3Reader::AppleMP3Reader(AbstractM
   , mStreamReady(false)
   , mAudioFramesPerCompressedPacket(0)
   , mCurrentAudioFrame(0)
   , mAudioChannels(0)
   , mAudioSampleRate(0)
   , mAudioFileStream(nullptr)
   , mAudioConverter(nullptr)
   , mMP3FrameParser(mDecoder->GetResource()->GetLength())
+  , mResource(mDecoder->GetResource())
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread");
 }
 
 AppleMP3Reader::~AppleMP3Reader()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread");
 }
@@ -85,24 +86,22 @@ static void _AudioSampleCallback(void *a
  * If we're not at end of stream, read |aNumBytes| from the media resource,
  * put it in |aData|, and return true.
  * Otherwise, put as much data as is left into |aData|, set |aNumBytes| to the
  * amount of data we have left, and return false.
  */
 nsresult
 AppleMP3Reader::Read(uint32_t *aNumBytes, char *aData)
 {
-  MediaResource *resource = mDecoder->GetResource();
-
   // Loop until we have all the data asked for, or we've reached EOS
   uint32_t totalBytes = 0;
   uint32_t numBytes;
   do {
     uint32_t bytesWanted = *aNumBytes - totalBytes;
-    nsresult rv = resource->Read(aData + totalBytes, bytesWanted, &numBytes);
+    nsresult rv = mResource.Read(aData + totalBytes, bytesWanted, &numBytes);
     totalBytes += numBytes;
 
     if (NS_FAILED(rv)) {
       *aNumBytes = 0;
       return NS_ERROR_FAILURE;
     }
   } while(totalBytes < *aNumBytes && numBytes);
 
@@ -252,17 +251,17 @@ AppleMP3Reader::AudioSampleCallback(UInt
     }
 
     int64_t time = FramesToUsecs(mCurrentAudioFrame, mAudioSampleRate).value();
     int64_t duration = FramesToUsecs(numFrames, mAudioSampleRate).value();
 
     LOGD("pushed audio at time %lfs; duration %lfs\n",
          (double)time / USECS_PER_S, (double)duration / USECS_PER_S);
 
-    AudioData *audio = new AudioData(mDecoder->GetResource()->Tell(),
+    AudioData *audio = new AudioData(mResource.Tell(),
                                      time, duration, numFrames,
                                      reinterpret_cast<AudioDataValue *>(decoded.forget()),
                                      mAudioChannels, mAudioSampleRate);
     mAudioQueue.Push(audio);
 
     mCurrentAudioFrame += numFrames;
 
     if (rv == kNeedMoreData) {
@@ -512,32 +511,33 @@ AppleMP3Reader::Seek(int64_t aTime, int6
     LOGE("Couldn't seek demuxer. Error code %x\n", rv);
     return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   LOGD("computed byte offset = %lld; estimated = %s\n",
        byteOffset,
        (flags & kAudioFileStreamSeekFlag_OffsetIsEstimated) ? "YES" : "NO");
 
-  mDecoder->GetResource()->Seek(nsISeekableStream::NS_SEEK_SET, byteOffset);
+  mResource.Seek(nsISeekableStream::NS_SEEK_SET, byteOffset);
 
   ResetDecode();
 
   return SeekPromise::CreateAndResolve(aTime, __func__);
 }
 
 void
 AppleMP3Reader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
 {
   MOZ_ASSERT(OnTaskQueue());
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
-  nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
+  nsRefPtr<MediaByteBuffer> bytes =
+    mDecoder->GetResource()->MediaReadAt(aOffset, aLength);
   NS_ENSURE_TRUE_VOID(bytes);
   mMP3FrameParser.Parse(bytes->Elements(), aLength, aOffset);
   if (!mMP3FrameParser.IsMP3()) {
     return;
   }
 
   uint64_t duration = mMP3FrameParser.GetDuration();
   if (duration != mDuration) {
--- a/dom/media/apple/AppleMP3Reader.h
+++ b/dom/media/apple/AppleMP3Reader.h
@@ -1,16 +1,17 @@
 /* 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 __AppleMP3Reader_h__
 #define __AppleMP3Reader_h__
 
 #include "MediaDecoderReader.h"
+#include "MediaResource.h"
 #include "MP3FrameParser.h"
 #include "VideoUtils.h"
 
 #include <AudioToolbox/AudioToolbox.h>
 
 namespace mozilla {
 
 class AppleMP3Reader : public MediaDecoderReader
@@ -74,13 +75,15 @@ private:
   UInt32 mAudioSampleRate;
 
   uint64_t mDuration;
 
   AudioFileStreamID mAudioFileStream;
   AudioConverterRef mAudioConverter;
 
   MP3FrameParser mMP3FrameParser;
+
+  MediaResourceIndex mResource;
 };
 
 } // namespace mozilla
 
 #endif // __AppleMP3Reader_h__
--- a/dom/media/directshow/DirectShowReader.cpp
+++ b/dom/media/directshow/DirectShowReader.cpp
@@ -403,17 +403,17 @@ DirectShowReader::SeekInternal(int64_t a
 void
 DirectShowReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
 {
   MOZ_ASSERT(OnTaskQueue());
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
-  nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
+  nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->MediaReadAt(aOffset, aLength);
   NS_ENSURE_TRUE_VOID(bytes);
   mMP3FrameParser.Parse(bytes->Elements(), aLength, aOffset);
   if (!mMP3FrameParser.IsMP3()) {
     return;
   }
 
   int64_t duration = mMP3FrameParser.GetDuration();
   if (duration != mDuration) {
--- a/dom/media/gstreamer/GStreamerReader.cpp
+++ b/dom/media/gstreamer/GStreamerReader.cpp
@@ -3,17 +3,16 @@
 /* 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 "nsError.h"
 #include "nsMimeTypes.h"
 #include "MediaDecoderStateMachine.h"
 #include "AbstractMediaDecoder.h"
-#include "MediaResource.h"
 #include "GStreamerReader.h"
 #if GST_VERSION_MAJOR >= 1
 #include "GStreamerAllocator.h"
 #endif
 #include "GStreamerFormatHelper.h"
 #include "VideoUtils.h"
 #include "mozilla/Endian.h"
 #include "mozilla/Preferences.h"
@@ -83,17 +82,18 @@ GStreamerReader::GStreamerReader(Abstrac
   mAudioSinkBufferCount(0),
   mGstThreadsMonitor("media.gst.threads"),
   mReachedAudioEos(false),
   mReachedVideoEos(false),
 #if GST_VERSION_MAJOR >= 1
   mConfigureAlignment(true),
 #endif
   fpsNum(0),
-  fpsDen(0)
+  fpsDen(0),
+  mResource(aDecoder->GetResource())
 {
   MOZ_COUNT_CTOR(GStreamerReader);
 
   mSrcCallbacks.need_data = GStreamerReader::NeedDataCb;
   mSrcCallbacks.enough_data = GStreamerReader::EnoughDataCb;
   mSrcCallbacks.seek_data = GStreamerReader::SeekDataCb;
 
   mSinkCallbacks.eos = GStreamerReader::EosCb;
@@ -276,30 +276,29 @@ void GStreamerReader::PlayBinSourceSetup
   g_object_get(aPlayBin, "source", &source, nullptr);
   reader->PlayBinSourceSetup(GST_APP_SRC(source));
 }
 
 void GStreamerReader::PlayBinSourceSetup(GstAppSrc* aSource)
 {
   mSource = GST_APP_SRC(aSource);
   gst_app_src_set_callbacks(mSource, &mSrcCallbacks, (gpointer) this, nullptr);
-  MediaResource* resource = mDecoder->GetResource();
 
   /* do a short read to trigger a network request so that GetLength() below
    * returns something meaningful and not -1
    */
   char buf[512];
   unsigned int size = 0;
-  resource->Read(buf, sizeof(buf), &size);
-  resource->Seek(SEEK_SET, 0);
+  mResource.Read(buf, sizeof(buf), &size);
+  mResource.Seek(SEEK_SET, 0);
 
   /* now we should have a length */
   int64_t resourceLength = GetDataLength();
   gst_app_src_set_size(mSource, resourceLength);
-  if (resource->IsDataCachedToEndOfResource(0) ||
+  if (mResource.GetResource()->IsDataCachedToEndOfResource(0) ||
       (resourceLength != -1 && resourceLength <= SHORT_FILE_SIZE)) {
     /* let the demuxer work in pull mode for local files (or very short files)
      * so that we get optimal seeking accuracy/performance
      */
     LOG(LogLevel::Debug, "configuring random access, len %lld", resourceLength);
     gst_app_src_set_stream_type(mSource, GST_APP_STREAM_TYPE_RANDOM_ACCESS);
   } else {
     /* make the demuxer work in push mode so that seeking is kept to a minimum
@@ -318,25 +317,23 @@ void GStreamerReader::PlayBinSourceSetup
 }
 
 /**
  * If this stream is an MP3, we want to parse the headers to estimate the
  * stream duration.
  */
 nsresult GStreamerReader::ParseMP3Headers()
 {
-  MediaResource *resource = mDecoder->GetResource();
-
   const uint32_t MAX_READ_BYTES = 4096;
 
   uint64_t offset = 0;
   char bytes[MAX_READ_BYTES];
   uint32_t bytesRead;
   do {
-    nsresult rv = resource->ReadAt(offset, bytes, MAX_READ_BYTES, &bytesRead);
+    nsresult rv = mResource.ReadAt(offset, bytes, MAX_READ_BYTES, &bytesRead);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_TRUE(bytesRead, NS_ERROR_FAILURE);
 
     mMP3FrameParser.Parse(reinterpret_cast<uint8_t*>(bytes), bytesRead, offset);
     offset += bytesRead;
   } while (!mMP3FrameParser.ParsedHeaders());
 
   if (mMP3FrameParser.IsMP3()) {
@@ -349,17 +346,17 @@ nsresult GStreamerReader::ParseMP3Header
   }
 
   return NS_OK;
 }
 
 int64_t
 GStreamerReader::GetDataLength()
 {
-  int64_t streamLen = mDecoder->GetResource()->GetLength();
+  int64_t streamLen = mResource.GetLength();
 
   if (streamLen < 0) {
     return streamLen;
   }
 
   return streamLen - mDataOffset;
 }
 
@@ -829,17 +826,17 @@ bool GStreamerReader::DecodeVideoFrame(b
      * allocating a PlanarYCbCrImage backed GstBuffer here and memcpy.
      */
     GstBuffer* tmp = nullptr;
     CopyIntoImageBuffer(buffer, &tmp, image);
     gst_buffer_unref(buffer);
     buffer = tmp;
   }
 
-  int64_t offset = mDecoder->GetResource()->Tell(); // Estimate location in media.
+  int64_t offset = mResource.Tell(); // Estimate location in media.
   nsRefPtr<VideoData> video = VideoData::CreateFromImage(mInfo.mVideo,
                                                          mDecoder->GetImageContainer(),
                                                          offset, timestamp, duration,
                                                          static_cast<Image*>(image.get()),
                                                          isKeyframe, -1, mPicture);
   mVideoQueue.Push(video);
 
   gst_buffer_unref(buffer);
@@ -929,41 +926,39 @@ media::TimeIntervals GStreamerReader::Ge
                           media::TimeUnit::FromMicroseconds(GST_TIME_AS_USECONDS(endTime)));
   }
 
   return buffered;
 }
 
 void GStreamerReader::ReadAndPushData(guint aLength)
 {
-  MediaResource* resource = mDecoder->GetResource();
-  NS_ASSERTION(resource, "Decoder has no media resource");
-  int64_t offset1 = resource->Tell();
+  int64_t offset1 = mResource.Tell();
   unused << offset1;
   nsresult rv = NS_OK;
 
   GstBuffer* buffer = gst_buffer_new_and_alloc(aLength);
 #if GST_VERSION_MAJOR >= 1
   GstMapInfo info;
   gst_buffer_map(buffer, &info, GST_MAP_WRITE);
   guint8 *data = info.data;
 #else
   guint8* data = GST_BUFFER_DATA(buffer);
 #endif
   uint32_t size = 0, bytesRead = 0;
   while(bytesRead < aLength) {
-    rv = resource->Read(reinterpret_cast<char*>(data + bytesRead),
-        aLength - bytesRead, &size);
+    rv = mResource.Read(reinterpret_cast<char*>(data + bytesRead),
+                        aLength - bytesRead, &size);
     if (NS_FAILED(rv) || size == 0)
       break;
 
     bytesRead += size;
   }
 
-  int64_t offset2 = resource->Tell();
+  int64_t offset2 = mResource.Tell();
   unused << offset2;
 
 #if GST_VERSION_MAJOR >= 1
   gst_buffer_unmap(buffer, &info);
   gst_buffer_set_size(buffer, bytesRead);
 #else
   GST_BUFFER_SIZE(buffer) = bytesRead;
 #endif
@@ -1027,35 +1022,28 @@ gboolean GStreamerReader::SeekDataCb(Gst
   return reader->SeekData(aSrc, aOffset);
 }
 
 gboolean GStreamerReader::SeekData(GstAppSrc* aSrc, guint64 aOffset)
 {
   aOffset += mDataOffset;
 
   ReentrantMonitorAutoEnter mon(mGstThreadsMonitor);
-  MediaResource* resource = mDecoder->GetResource();
-  int64_t resourceLength = resource->GetLength();
+  int64_t resourceLength = mResource.GetLength();
 
   if (gst_app_src_get_size(mSource) == -1) {
     /* It's possible that we didn't know the length when we initialized mSource
      * but maybe we do now
      */
     gst_app_src_set_size(mSource, GetDataLength());
   }
 
   nsresult rv = NS_ERROR_FAILURE;
   if (aOffset < static_cast<guint64>(resourceLength)) {
-    rv = resource->Seek(SEEK_SET, aOffset);
-  }
-
-  if (NS_FAILED(rv)) {
-    LOG(LogLevel::Error, "seek at %lu failed", aOffset);
-  } else {
-    MOZ_ASSERT(aOffset == static_cast<guint64>(resource->Tell()));
+    rv = mResource.Seek(SEEK_SET, aOffset);
   }
 
   return NS_SUCCEEDED(rv);
 }
 
 GstFlowReturn GStreamerReader::NewPrerollCb(GstAppSink* aSink,
                                             gpointer aUserData)
 {
@@ -1279,17 +1267,18 @@ void GStreamerReader::NotifyDataArrivedI
   MOZ_ASSERT(OnTaskQueue());
   if (HasVideo()) {
     return;
   }
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
-  nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
+  nsRefPtr<MediaByteBuffer> bytes =
+    mResource.MediaReadAt(aOffset, aLength);
   NS_ENSURE_TRUE_VOID(bytes);
   mMP3FrameParser.Parse(bytes->Elements(), aLength, aOffset);
   if (!mMP3FrameParser.IsMP3()) {
     return;
   }
 
   int64_t duration = mMP3FrameParser.GetDuration();
   if (duration != mLastParserDuration && mUseParserDuration) {
--- a/dom/media/gstreamer/GStreamerReader.h
+++ b/dom/media/gstreamer/GStreamerReader.h
@@ -16,16 +16,17 @@
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunknown-pragmas"
 #pragma GCC diagnostic ignored "-Wpragmas"
 #pragma GCC diagnostic ignored "-Wreserved-user-defined-literal"
 #include <gst/video/video.h>
 #pragma GCC diagnostic pop
 
 #include "MediaDecoderReader.h"
+#include "MediaResource.h"
 #include "MP3FrameParser.h"
 #include "ImageContainer.h"
 #include "nsRect.h"
 
 struct GstURIDecodeBin;
 
 namespace mozilla {
 
@@ -257,13 +258,15 @@ private:
    */
   bool mReachedAudioEos;
   bool mReachedVideoEos;
 #if GST_VERSION_MAJOR >= 1
   bool mConfigureAlignment;
 #endif
   int fpsNum;
   int fpsDen;
+
+  MediaResourceIndex mResource;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/gtest/MockMediaResource.h
+++ b/dom/media/gtest/MockMediaResource.h
@@ -27,27 +27,18 @@ public:
   virtual bool CanClone() override { return false; }
   virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder)
     override
   {
     return nullptr;
   }
   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) override {}
   virtual void SetPlaybackRate(uint32_t aBytesPerSecond) override {}
-  virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
-    override
-  {
-    return NS_OK;
-  }
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
                           uint32_t* aBytes) override;
-  virtual nsresult Seek(int32_t aWhence, int64_t aOffset) override
-  {
-    return NS_OK;
-  }
   virtual int64_t Tell() override { return 0; }
   virtual void Pin() override {}
   virtual void Unpin() override {}
   virtual double GetDownloadRate(bool* aIsReliable) override { return 0; }
   virtual int64_t GetLength() override;
   virtual int64_t GetNextCachedData(int64_t aOffset) override;
   virtual int64_t GetCachedDataEnd(int64_t aOffset) override;
   virtual bool IsDataCachedToEndOfResource(int64_t aOffset) override
--- a/dom/media/mediasource/MediaSourceResource.h
+++ b/dom/media/mediasource/MediaSourceResource.h
@@ -30,19 +30,17 @@ public:
 
   virtual nsresult Close() override { return NS_OK; }
   virtual void Suspend(bool aCloseImmediately) override { UNIMPLEMENTED(); }
   virtual void Resume() override { UNIMPLEMENTED(); }
   virtual bool CanClone() override { UNIMPLEMENTED(); return false; }
   virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) override { UNIMPLEMENTED(); return nullptr; }
   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) override { UNIMPLEMENTED(); }
   virtual void SetPlaybackRate(uint32_t aBytesPerSecond) override  { UNIMPLEMENTED(); }
-  virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) override { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) override { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
-  virtual nsresult Seek(int32_t aWhence, int64_t aOffset) override { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
   virtual int64_t Tell() override { UNIMPLEMENTED(); return -1; }
   virtual void Pin() override { UNIMPLEMENTED(); }
   virtual void Unpin() override { UNIMPLEMENTED(); }
   virtual double GetDownloadRate(bool* aIsReliable) override { UNIMPLEMENTED(); *aIsReliable = false; return 0; }
   virtual int64_t GetLength() override { UNIMPLEMENTED(); return -1; }
   virtual int64_t GetNextCachedData(int64_t aOffset) override { UNIMPLEMENTED(); return -1; }
   virtual int64_t GetCachedDataEnd(int64_t aOffset) override { UNIMPLEMENTED(); return -1; }
   virtual bool IsDataCachedToEndOfResource(int64_t aOffset) override { UNIMPLEMENTED(); return false; }
--- a/dom/media/mediasource/SourceBufferResource.cpp
+++ b/dom/media/mediasource/SourceBufferResource.cpp
@@ -34,26 +34,16 @@ SourceBufferResource::Close()
   SBR_DEBUG("Close");
   //MOZ_ASSERT(!mClosed);
   mClosed = true;
   mon.NotifyAll();
   return NS_OK;
 }
 
 nsresult
-SourceBufferResource::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
-{
-  SBR_DEBUGV("Read(aBuffer=%p, aCount=%u, aBytes=%p)",
-             aBuffer, aCount, aBytes);
-  ReentrantMonitorAutoEnter mon(mMonitor);
-
-  return ReadInternal(aBuffer, aCount, aBytes, /* aMayBlock = */ true);
-}
-
-nsresult
 SourceBufferResource::ReadInternal(char* aBuffer, uint32_t aCount, uint32_t* aBytes, bool aMayBlock)
 {
   mMonitor.AssertCurrentThreadIn();
   MOZ_ASSERT_IF(!aMayBlock, aBytes);
 
   // Cache the offset for the read in case mOffset changes while waiting on the
   // monitor below. It's basically impossible to implement these API semantics
   // sanely. :-(
@@ -110,43 +100,16 @@ SourceBufferResource::ReadAtInternal(int
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   return ReadInternal(aBuffer, aCount, aBytes, aMayBlock);
 }
 
 nsresult
-SourceBufferResource::Seek(int32_t aWhence, int64_t aOffset)
-{
-  SBR_DEBUG("Seek(aWhence=%d, aOffset=%lld)",
-            aWhence, aOffset);
-  ReentrantMonitorAutoEnter mon(mMonitor);
-
-  int64_t newOffset = mOffset;
-  switch (aWhence) {
-  case nsISeekableStream::NS_SEEK_END:
-    newOffset = GetLength() - aOffset;
-    break;
-  case nsISeekableStream::NS_SEEK_CUR:
-    newOffset += aOffset;
-    break;
-  case nsISeekableStream::NS_SEEK_SET:
-    newOffset = aOffset;
-    break;
-  }
-
-  SBR_DEBUGV("newOffset=%lld GetOffset()=%llu GetLength()=%llu)",
-             newOffset, mInputBuffer.GetOffset(), GetLength());
-  nsresult rv = SeekInternal(newOffset);
-  mon.NotifyAll();
-  return rv;
-}
-
-nsresult
 SourceBufferResource::SeekInternal(int64_t aOffset)
 {
   mMonitor.AssertCurrentThreadIn();
 
   if (mClosed ||
       aOffset < 0 ||
       uint64_t(aOffset) < mInputBuffer.GetOffset() ||
       aOffset > GetLength()) {
--- a/dom/media/mediasource/SourceBufferResource.h
+++ b/dom/media/mediasource/SourceBufferResource.h
@@ -41,19 +41,17 @@ public:
   explicit SourceBufferResource(const nsACString& aType);
   virtual nsresult Close() override;
   virtual void Suspend(bool aCloseImmediately) override { UNIMPLEMENTED(); }
   virtual void Resume() override { UNIMPLEMENTED(); }
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override { UNIMPLEMENTED(); return nullptr; }
   virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) override { UNIMPLEMENTED(); return nullptr; }
   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) override { UNIMPLEMENTED(); }
   virtual void SetPlaybackRate(uint32_t aBytesPerSecond) override { UNIMPLEMENTED(); }
-  virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes) override;
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) override;
-  virtual nsresult Seek(int32_t aWhence, int64_t aOffset) override;
   virtual int64_t Tell() override { return mOffset; }
   virtual void Pin() override { UNIMPLEMENTED(); }
   virtual void Unpin() override { UNIMPLEMENTED(); }
   virtual double GetDownloadRate(bool* aIsReliable) override { UNIMPLEMENTED(); *aIsReliable = false; return 0; }
   virtual int64_t GetLength() override { return mInputBuffer.GetLength(); }
   virtual int64_t GetNextCachedData(int64_t aOffset) override {
     ReentrantMonitorAutoEnter mon(mMonitor);
     MOZ_ASSERT(aOffset >= 0);
--- a/dom/media/ogg/OggReader.cpp
+++ b/dom/media/ogg/OggReader.cpp
@@ -60,17 +60,17 @@ static const int64_t SEEK_OPUS_PREROLL =
 enum PageSyncResult {
   PAGE_SYNC_ERROR = 1,
   PAGE_SYNC_END_OF_RANGE= 2,
   PAGE_SYNC_OK = 3
 };
 
 // Reads a page from the media resource.
 static PageSyncResult
-PageSync(MediaResource* aResource,
+PageSync(MediaResourceIndex* aResource,
          ogg_sync_state* aState,
          bool aCachedDataOnly,
          int64_t aOffset,
          int64_t aEndOffset,
          ogg_page* aPage,
          int& aSkippedBytes);
 
 // Chunk size to read when reading Ogg files. Average Ogg page length
@@ -134,17 +134,18 @@ OggReader::OggReader(AbstractMediaDecode
     mOpusState(nullptr),
     mOpusEnabled(MediaDecoder::IsOpusEnabled()),
     mSkeletonState(nullptr),
     mVorbisSerial(0),
     mOpusSerial(0),
     mTheoraSerial(0),
     mOpusPreSkip(0),
     mIsChained(false),
-    mDecodedAudioFrames(0)
+    mDecodedAudioFrames(0),
+    mResource(aDecoder->GetResource())
 {
   MOZ_COUNT_CTOR(OggReader);
   memset(&mTheoraInfo, 0, sizeof(mTheoraInfo));
 }
 
 OggReader::~OggReader()
 {
   ogg_sync_clear(&mOggState);
@@ -466,23 +467,22 @@ nsresult OggReader::ReadMetadata(MediaIn
   }
 
   SetupTargetSkeleton(mSkeletonState);
   SetupMediaTracksInfo(serials);
 
   if (HasAudio() || HasVideo()) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
-    MediaResource* resource = mDecoder->GetResource();
     if (mInfo.mMetadataDuration.isNothing() && !mDecoder->IsOggDecoderShutdown() &&
-        resource->GetLength() >= 0 && mDecoder->IsMediaSeekable())
+        mResource.GetLength() >= 0 && mDecoder->IsMediaSeekable())
     {
       // We didn't get a duration from the index or a Content-Duration header.
       // Seek to the end of file to find the end time.
-      int64_t length = resource->GetLength();
+      int64_t length = mResource.GetLength();
 
       NS_ASSERTION(length > 0, "Must have a content length to get end time");
 
       int64_t endTime = 0;
       {
         ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
         endTime = RangeEndTime(length);
       }
@@ -536,17 +536,17 @@ nsresult OggReader::DecodeVorbis(ogg_pac
 
     // No channel mapping for more than 8 channels.
     if (channels > 8) {
       return NS_ERROR_FAILURE;
     }
 
     int64_t duration = mVorbisState->Time((int64_t)frames);
     int64_t startTime = mVorbisState->Time(endFrame - frames);
-    mAudioQueue.Push(new AudioData(mDecoder->GetResource()->Tell(),
+    mAudioQueue.Push(new AudioData(mResource.Tell(),
                                    startTime,
                                    duration,
                                    frames,
                                    buffer.forget(),
                                    channels,
                                    mVorbisState->mInfo.rate));
 
     mDecodedAudioFrames += frames;
@@ -653,17 +653,17 @@ nsresult OggReader::DecodeOpus(ogg_packe
   // No channel mapping for more than 8 channels.
   if (channels > 8) {
     return NS_ERROR_FAILURE;
   }
 
   LOG(LogLevel::Debug, ("Opus decoder pushing %d frames", frames));
   int64_t startTime = mOpusState->Time(startFrame);
   int64_t endTime = mOpusState->Time(endFrame);
-  mAudioQueue.Push(new AudioData(mDecoder->GetResource()->Tell(),
+  mAudioQueue.Push(new AudioData(mResource.Tell(),
                                  startTime,
                                  endTime - startTime,
                                  frames,
                                  buffer.forget(),
                                  channels,
                                  mOpusState->mRate));
 
   mDecodedAudioFrames += frames;
@@ -862,17 +862,17 @@ nsresult OggReader::DecodeTheora(ogg_pac
     b.mPlanes[i].mHeight = buffer[i].height;
     b.mPlanes[i].mWidth = buffer[i].width;
     b.mPlanes[i].mStride = buffer[i].stride;
     b.mPlanes[i].mOffset = b.mPlanes[i].mSkip = 0;
   }
 
   nsRefPtr<VideoData> v = VideoData::Create(mInfo.mVideo,
                                             mDecoder->GetImageContainer(),
-                                            mDecoder->GetResource()->Tell(),
+                                            mResource.Tell(),
                                             time,
                                             endTime - time,
                                             b,
                                             isKeyframe,
                                             aPacket->granulepos,
                                             mPicture);
   if (!v) {
     // There may be other reasons for this error, but for
@@ -945,17 +945,17 @@ bool OggReader::ReadOggPage(ogg_page* aP
     // with the given size. This buffer is stored
     // in the ogg synchronisation structure.
     char* buffer = ogg_sync_buffer(&mOggState, 4096);
     NS_ASSERTION(buffer, "ogg_sync_buffer failed");
 
     // Read from the resource into the buffer
     uint32_t bytesRead = 0;
 
-    nsresult rv = mDecoder->GetResource()->Read(buffer, 4096, &bytesRead);
+    nsresult rv = mResource.Read(buffer, 4096, &bytesRead);
     if (NS_FAILED(rv) || (bytesRead == 0 && ret == 0)) {
       // End of file.
       return false;
     }
 
     // Update the synchronisation layer with the number
     // of bytes written to the buffer
     ret = ogg_sync_wrote(&mOggState, bytesRead);
@@ -1006,19 +1006,17 @@ GetChecksum(ogg_page* page)
                (p[2] << 16) +
                (p[3] << 24);
   return c;
 }
 
 int64_t OggReader::RangeStartTime(int64_t aOffset)
 {
   MOZ_ASSERT(OnTaskQueue());
-  MediaResource* resource = mDecoder->GetResource();
-  NS_ENSURE_TRUE(resource != nullptr, 0);
-  nsresult res = resource->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
+  nsresult res = mResource.Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   NS_ENSURE_SUCCESS(res, 0);
   int64_t startTime = 0;
   FindStartTime(startTime);
   return startTime;
 }
 
 struct nsAutoOggSyncState {
   nsAutoOggSyncState() {
@@ -1029,30 +1027,27 @@ struct nsAutoOggSyncState {
   }
   ogg_sync_state mState;
 };
 
 int64_t OggReader::RangeEndTime(int64_t aEndOffset)
 {
   MOZ_ASSERT(OnTaskQueue() || mDecoder->OnStateMachineTaskQueue());
 
-  MediaResource* resource = mDecoder->GetResource();
-  NS_ENSURE_TRUE(resource != nullptr, -1);
-  int64_t position = resource->Tell();
+  int64_t position = mResource.Tell();
   int64_t endTime = RangeEndTime(0, aEndOffset, false);
-  nsresult res = resource->Seek(nsISeekableStream::NS_SEEK_SET, position);
+  nsresult res = mResource.Seek(nsISeekableStream::NS_SEEK_SET, position);
   NS_ENSURE_SUCCESS(res, -1);
   return endTime;
 }
 
 int64_t OggReader::RangeEndTime(int64_t aStartOffset,
                                   int64_t aEndOffset,
                                   bool aCachedDataOnly)
 {
-  MediaResource* resource = mDecoder->GetResource();
   nsAutoOggSyncState sync;
 
   // We need to find the last page which ends before aEndOffset that
   // has a granulepos that we can convert to a timestamp. We do this by
   // backing off from aEndOffset until we encounter a page on which we can
   // interpret the granulepos. If while backing off we encounter a page which
   // we've previously encountered before, we'll either backoff again if we
   // haven't found an end time yet, or return the last end time found.
@@ -1095,25 +1090,25 @@ int64_t OggReader::RangeEndTime(int64_t 
       limit = std::max(static_cast<int64_t>(0), limit);
       limit = std::min(limit, static_cast<int64_t>(step));
       uint32_t bytesToRead = static_cast<uint32_t>(limit);
       uint32_t bytesRead = 0;
       char* buffer = ogg_sync_buffer(&sync.mState, bytesToRead);
       NS_ASSERTION(buffer, "Must have buffer");
       nsresult res;
       if (aCachedDataOnly) {
-        res = resource->ReadFromCache(buffer, readHead, bytesToRead);
+        res = mResource.GetResource()->ReadFromCache(buffer, readHead, bytesToRead);
         NS_ENSURE_SUCCESS(res, -1);
         bytesRead = bytesToRead;
       } else {
         NS_ASSERTION(readHead < aEndOffset,
                      "resource pos must be before range end");
-        res = resource->Seek(nsISeekableStream::NS_SEEK_SET, readHead);
+        res = mResource.Seek(nsISeekableStream::NS_SEEK_SET, readHead);
         NS_ENSURE_SUCCESS(res, -1);
-        res = resource->Read(buffer, bytesToRead, &bytesRead);
+        res = mResource.Read(buffer, bytesToRead, &bytesRead);
         NS_ENSURE_SUCCESS(res, -1);
       }
       readHead += bytesRead;
       if (readHead > readLimitOffset) {
         mustBackOff = true;
       }
 
       // Update the synchronisation layer with the number
@@ -1209,17 +1204,17 @@ OggReader::SeekRange
 OggReader::SelectSeekRange(const nsTArray<SeekRange>& ranges,
                              int64_t aTarget,
                              int64_t aStartTime,
                              int64_t aEndTime,
                              bool aExact)
 {
   MOZ_ASSERT(OnTaskQueue());
   int64_t so = 0;
-  int64_t eo = mDecoder->GetResource()->GetLength();
+  int64_t eo = mResource.GetLength();
   int64_t st = aStartTime;
   int64_t et = aEndTime;
   for (uint32_t i = 0; i < ranges.Length(); i++) {
     const SeekRange &r = ranges[i];
     if (r.mTimeStart < aTarget) {
       so = r.mOffsetStart;
       st = r.mTimeStart;
     }
@@ -1239,71 +1234,67 @@ OggReader::SelectSeekRange(const nsTArra
   return SeekRange(so, eo, st, et);
 }
 
 OggReader::IndexedSeekResult OggReader::RollbackIndexedSeek(int64_t aOffset)
 {
   if (mSkeletonState) {
     mSkeletonState->Deactivate();
   }
-  MediaResource* resource = mDecoder->GetResource();
-  NS_ENSURE_TRUE(resource != nullptr, SEEK_FATAL_ERROR);
-  nsresult res = resource->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
+  nsresult res = mResource.Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
   return SEEK_INDEX_FAIL;
 }
 
 OggReader::IndexedSeekResult OggReader::SeekToKeyframeUsingIndex(int64_t aTarget)
 {
-  MediaResource* resource = mDecoder->GetResource();
-  NS_ENSURE_TRUE(resource != nullptr, SEEK_FATAL_ERROR);
   if (!HasSkeleton() || !mSkeletonState->HasIndex()) {
     return SEEK_INDEX_FAIL;
   }
   // We have an index from the Skeleton track, try to use it to seek.
   nsAutoTArray<uint32_t, 2> tracks;
   BuildSerialList(tracks);
   SkeletonState::nsSeekTarget keyframe;
   if (NS_FAILED(mSkeletonState->IndexedSeekTarget(aTarget,
                                                   tracks,
                                                   keyframe)))
   {
     // Could not locate a keypoint for the target in the index.
     return SEEK_INDEX_FAIL;
   }
 
   // Remember original resource read cursor position so we can rollback on failure.
-  int64_t tell = resource->Tell();
+  int64_t tell = mResource.Tell();
 
   // Seek to the keypoint returned by the index.
-  if (keyframe.mKeyPoint.mOffset > resource->GetLength() ||
+  if (keyframe.mKeyPoint.mOffset > mResource.GetLength() ||
       keyframe.mKeyPoint.mOffset < 0)
   {
     // Index must be invalid.
     return RollbackIndexedSeek(tell);
   }
   LOG(LogLevel::Debug, ("Seeking using index to keyframe at offset %lld\n",
                      keyframe.mKeyPoint.mOffset));
-  nsresult res = resource->Seek(nsISeekableStream::NS_SEEK_SET,
-                              keyframe.mKeyPoint.mOffset);
+  nsresult res = mResource.Seek(nsISeekableStream::NS_SEEK_SET,
+                                keyframe.mKeyPoint.mOffset);
   NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
 
   // We've moved the read set, so reset decode.
   res = ResetDecode();
   NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
 
   // Check that the page the index thinks is exactly here is actually exactly
   // here. If not, the index is invalid.
   ogg_page page;
   int skippedBytes = 0;
-  PageSyncResult syncres = PageSync(resource,
+  PageSyncResult syncres = PageSync(&mResource,
                                     &mOggState,
                                     false,
                                     keyframe.mKeyPoint.mOffset,
-                                    resource->GetLength(),
+                                    mResource.GetLength(),
                                     &page,
                                     skippedBytes);
   NS_ENSURE_TRUE(syncres != PAGE_SYNC_ERROR, SEEK_FATAL_ERROR);
   if (syncres != PAGE_SYNC_OK || skippedBytes != 0) {
     LOG(LogLevel::Debug, ("Indexed-seek failure: Ogg Skeleton Index is invalid "
                        "or sync error after seek"));
     return RollbackIndexedSeek(tell);
   }
@@ -1426,27 +1417,25 @@ OggReader::Seek(int64_t aTarget, int64_t
 nsresult OggReader::SeekInternal(int64_t aTarget, int64_t aEndTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   NS_ENSURE_TRUE(HaveStartTime(), NS_ERROR_FAILURE);
   if (mIsChained)
     return NS_ERROR_FAILURE;
   LOG(LogLevel::Debug, ("%p About to seek to %lld", mDecoder, aTarget));
   nsresult res;
-  MediaResource* resource = mDecoder->GetResource();
-  NS_ENSURE_TRUE(resource != nullptr, NS_ERROR_FAILURE);
   int64_t adjustedTarget = aTarget;
   if (HasAudio() && mOpusState){
     adjustedTarget = std::max(StartTime(), aTarget - SEEK_OPUS_PREROLL);
   }
 
   if (adjustedTarget == StartTime()) {
     // We've seeked to the media start. Just seek to the offset of the first
     // content page.
-    res = resource->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+    res = mResource.Seek(nsISeekableStream::NS_SEEK_SET, 0);
     NS_ENSURE_SUCCESS(res,res);
 
     res = ResetDecode(true);
     NS_ENSURE_SUCCESS(res,res);
   } else {
     // TODO: This may seek back unnecessarily far in the video, but we don't
     // have a way of asking Skeleton to seek to a different target for each
     // stream yet. Using adjustedTarget here is at least correct, if slow.
@@ -1509,17 +1498,17 @@ nsresult OggReader::SeekInternal(int64_t
     }
 #endif
   }
   return NS_OK;
 }
 
 // Reads a page from the media resource.
 static PageSyncResult
-PageSync(MediaResource* aResource,
+PageSync(MediaResourceIndex* aResource,
          ogg_sync_state* aState,
          bool aCachedDataOnly,
          int64_t aOffset,
          int64_t aEndOffset,
          ogg_page* aPage,
          int& aSkippedBytes)
 {
   aSkippedBytes = 0;
@@ -1537,18 +1526,18 @@ PageSync(MediaResource* aResource,
       int64_t bytesToRead = std::min(static_cast<int64_t>(PAGE_STEP),
                                    aEndOffset - readHead);
       NS_ASSERTION(bytesToRead <= UINT32_MAX, "bytesToRead range check");
       if (bytesToRead <= 0) {
         return PAGE_SYNC_END_OF_RANGE;
       }
       nsresult rv = NS_OK;
       if (aCachedDataOnly) {
-        rv = aResource->ReadFromCache(buffer, readHead,
-                                      static_cast<uint32_t>(bytesToRead));
+        rv = aResource->GetResource()->ReadFromCache(buffer, readHead,
+                                                     static_cast<uint32_t>(bytesToRead));
         NS_ENSURE_SUCCESS(rv,PAGE_SYNC_ERROR);
         bytesRead = static_cast<uint32_t>(bytesToRead);
       } else {
         rv = aResource->Seek(nsISeekableStream::NS_SEEK_SET, readHead);
         NS_ENSURE_SUCCESS(rv,PAGE_SYNC_ERROR);
         rv = aResource->Read(buffer,
                              static_cast<uint32_t>(bytesToRead),
                              &bytesRead);
@@ -1579,23 +1568,22 @@ PageSync(MediaResource* aResource,
 }
 
 nsresult OggReader::SeekBisection(int64_t aTarget,
                                     const SeekRange& aRange,
                                     uint32_t aFuzz)
 {
   MOZ_ASSERT(OnTaskQueue());
   nsresult res;
-  MediaResource* resource = mDecoder->GetResource();
 
   if (aTarget == aRange.mTimeStart) {
     if (NS_FAILED(ResetDecode())) {
       return NS_ERROR_FAILURE;
     }
-    res = resource->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+    res = mResource.Seek(nsISeekableStream::NS_SEEK_SET, 0);
     NS_ENSURE_SUCCESS(res,res);
     return NS_OK;
   }
 
   // Bisection search, find start offset of last page with end time less than
   // the seek target.
   ogg_int64_t startOffset = aRange.mOffsetStart;
   ogg_int64_t startTime = aRange.mTimeStart;
@@ -1691,17 +1679,17 @@ nsresult OggReader::SeekBisection(int64_
       NS_ASSERTION(guess != previousGuess, "Guess should be different to previous");
       previousGuess = guess;
 
       hops++;
 
       // Locate the next page after our seek guess, and then figure out the
       // granule time of the audio and video bitstreams there. We can then
       // make a bisection decision based on our location in the media.
-      PageSyncResult res = PageSync(resource,
+      PageSyncResult res = PageSync(&mResource,
                                     &mOggState,
                                     false,
                                     guess,
                                     endOffset,
                                     &page,
                                     skippedBytes);
       NS_ENSURE_TRUE(res != PAGE_SYNC_ERROR, NS_ERROR_FAILURE);
 
@@ -1790,28 +1778,28 @@ nsresult OggReader::SeekBisection(int64_
       break;
     } // End of "until we determine time at guess offset" loop.
 
     if (interval == 0) {
       // Seek termination condition; we've found the page boundary of the
       // last page before the target, and the first page after the target.
       SEEK_LOG(LogLevel::Debug, ("Terminating seek at offset=%lld", startOffset));
       NS_ASSERTION(startTime < aTarget, "Start time must always be less than target");
-      res = resource->Seek(nsISeekableStream::NS_SEEK_SET, startOffset);
+      res = mResource.Seek(nsISeekableStream::NS_SEEK_SET, startOffset);
       NS_ENSURE_SUCCESS(res,res);
       if (NS_FAILED(ResetDecode())) {
         return NS_ERROR_FAILURE;
       }
       break;
     }
 
     SEEK_LOG(LogLevel::Debug, ("Time at offset %lld is %lld", guess, granuleTime));
     if (granuleTime < seekTarget && granuleTime > seekLowerBound) {
       // We're within the fuzzy region in which we want to terminate the search.
-      res = resource->Seek(nsISeekableStream::NS_SEEK_SET, pageOffset);
+      res = mResource.Seek(nsISeekableStream::NS_SEEK_SET, pageOffset);
       NS_ENSURE_SUCCESS(res,res);
       if (NS_FAILED(ResetDecode())) {
         return NS_ERROR_FAILURE;
       }
       SEEK_LOG(LogLevel::Debug, ("Terminating seek at offset=%lld", pageOffset));
       break;
     }
 
@@ -1885,17 +1873,17 @@ media::TimeIntervals OggReader::GetBuffe
 
     // Find the start time of the range. Read pages until we find one with a
     // granulepos which we can convert into a timestamp to use as the time of
     // the start of the buffered range.
     ogg_sync_reset(&sync.mState);
     while (startTime == -1) {
       ogg_page page;
       int32_t discard;
-      PageSyncResult res = PageSync(resource,
+      PageSyncResult res = PageSync(&mResource,
                                     &sync.mState,
                                     true,
                                     startOffset,
                                     endOffset,
                                     &page,
                                     discard);
       if (res == PAGE_SYNC_ERROR) {
         return media::TimeIntervals::Invalid();
--- a/dom/media/ogg/OggReader.h
+++ b/dom/media/ogg/OggReader.h
@@ -9,16 +9,17 @@
 #include <ogg/ogg.h>
 #include <theora/theoradec.h>
 #ifdef MOZ_TREMOR
 #include <tremor/ivorbiscodec.h>
 #else
 #include <vorbis/codec.h>
 #endif
 #include "MediaDecoderReader.h"
+#include "MediaResource.h"
 #include "OggCodecState.h"
 #include "VideoUtils.h"
 #include "mozilla/Monitor.h"
 #include "OggDecoder.h"
 
 namespace mozilla {
 
 // Thread safe container to store the codec information and the serial for each
@@ -311,13 +312,15 @@ private:
   nsIntRect mPicture;
 
   // True if we are decoding a chained ogg. Reading or writing to this member
   // should be done with |mMonitor| acquired.
   bool mIsChained;
 
   // Number of audio frames decoded so far.
   int64_t mDecodedAudioFrames;
+
+  MediaResourceIndex mResource;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -411,18 +411,19 @@ MediaCodecReader::DecodeAudioDataSync()
     }
   }
 
   if ((bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_EOS) ||
       (status == ERROR_END_OF_STREAM)) {
     AudioQueue().Finish();
   } else if (bufferInfo.mBuffer != nullptr && bufferInfo.mSize > 0 &&
              bufferInfo.mBuffer->data() != nullptr) {
+    MOZ_ASSERT(mStreamSource);
     // This is the approximate byte position in the stream.
-    int64_t pos = mDecoder->GetResource()->Tell();
+    int64_t pos = mStreamSource->Tell();
 
     uint32_t frames = bufferInfo.mSize /
                       (mInfo.mAudio.mChannels * sizeof(AudioDataValue));
 
     mAudioCompactor.Push(
       pos,
       bufferInfo.mTimeUs,
       mInfo.mAudio.mRate,
@@ -521,17 +522,18 @@ MediaCodecReader::HasVideo()
 {
   return mInfo.HasVideo();
 }
 
 void
 MediaCodecReader::NotifyDataArrivedInternal(uint32_t aLength,
                                             int64_t aOffset)
 {
-  nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
+  nsRefPtr<MediaByteBuffer> bytes =
+    mDecoder->GetResource()->MediaReadAt(aOffset, aLength);
   NS_ENSURE_TRUE_VOID(bytes);
 
   MonitorAutoLock monLock(mParserMonitor);
   if (mNextParserPosition == mParsedDataLength &&
       mNextParserPosition >= aOffset &&
       mNextParserPosition <= aOffset + aLength) {
     // No pending parsing runnable currently. And available data are adjacent to
     // parsed data.
@@ -932,18 +934,19 @@ MediaCodecReader::DecodeVideoFrameSync(i
     mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
     return;
   }
 
   nsRefPtr<VideoData> v;
   RefPtr<TextureClient> textureClient;
   sp<GraphicBuffer> graphicBuffer;
   if (bufferInfo.mBuffer != nullptr) {
+    MOZ_ASSERT(mStreamSource);
     // This is the approximate byte position in the stream.
-    int64_t pos = mDecoder->GetResource()->Tell();
+    int64_t pos = mStreamSource->Tell();
 
     if (mVideoTrack.mNativeWindow != nullptr &&
         mVideoTrack.mCodec->getOutputGraphicBufferFromIndex(bufferInfo.mIndex, &graphicBuffer) == OK &&
         graphicBuffer != nullptr) {
       textureClient = mVideoTrack.mNativeWindow->getTextureClientFromBuffer(graphicBuffer.get());
       v = VideoData::Create(mInfo.mVideo,
                             mDecoder->GetImageContainer(),
                             pos,
@@ -1191,17 +1194,17 @@ MediaCodecReader::CreateExtractor()
   DataSource::RegisterDefaultSniffers();
 
   if (mExtractor == nullptr) {
     sp<DataSource> dataSource = new MediaStreamSource(mDecoder->GetResource());
 
     if (dataSource->initCheck() != OK) {
       return false;
     }
-
+    mStreamSource = static_cast<MediaStreamSource*>(dataSource.get());
     mExtractor = MediaExtractor::Create(dataSource);
   }
 
   return mExtractor != nullptr;
 }
 
 void
 MediaCodecReader::DestroyExtractor()
--- a/dom/media/omx/MediaOmxCommonReader.cpp
+++ b/dom/media/omx/MediaOmxCommonReader.cpp
@@ -22,16 +22,17 @@ using namespace android;
 
 namespace mozilla {
 
 extern PRLogModuleInfo* gMediaDecoderLog;
 #define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
 
 MediaOmxCommonReader::MediaOmxCommonReader(AbstractMediaDecoder *aDecoder)
   : MediaDecoderReader(aDecoder)
+  , mStreamSource(nullptr)
 {
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
 
   mAudioChannel = dom::AudioChannelService::GetDefaultAudioChannel();
 }
 
--- a/dom/media/omx/MediaOmxCommonReader.h
+++ b/dom/media/omx/MediaOmxCommonReader.h
@@ -10,16 +10,17 @@
 #include "MediaDecoderReader.h"
 
 #include <utils/RefBase.h>
 
 #include "mozilla/dom/AudioChannelBinding.h"
 
 namespace android {
 struct MOZ_EXPORT MediaSource;
+class MediaStreamSource;
 } // namespace android
 
 namespace mozilla {
 
 class AbstractMediaDecoder;
 
 class MediaOmxCommonReader : public MediaDecoderReader
 {
@@ -38,13 +39,16 @@ public:
   // Check whether it is possible to offload current audio track. This access
   // canOffloadStream() from libStageFright Utils.cpp, which is not there in
   // ANDROID_VERSION < 19
   void CheckAudioOffload();
 #endif
 
 protected:
   dom::AudioChannel mAudioChannel;
+  // Weak reference to the MediaStreamSource that will be created by either
+  // MediaOmxReader or MediaCodecReader.
+  android::MediaStreamSource* mStreamSource;
 };
 
 } // namespace mozilla
 
 #endif // MEDIA_OMX_COMMON_READER_H
--- a/dom/media/omx/MediaOmxReader.cpp
+++ b/dom/media/omx/MediaOmxReader.cpp
@@ -208,16 +208,17 @@ nsresult MediaOmxReader::InitOmxDecoder(
     mExtractor = MediaExtractor::Create(dataSource);
     if (!mExtractor.get()) {
       return NS_ERROR_FAILURE;
     }
     mOmxDecoder = new OmxDecoder(mDecoder);
     if (!mOmxDecoder->Init(mExtractor)) {
       return NS_ERROR_FAILURE;
     }
+    mStreamSource = static_cast<MediaStreamSource*>(dataSource.get());
   }
   return NS_OK;
 }
 
 nsRefPtr<MediaDecoderReader::MetadataPromise>
 MediaOmxReader::AsyncReadMetadata()
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -387,18 +388,19 @@ bool MediaOmxReader::DecodeVideoFrame(bo
       // and we will preserve the ratio of the crop rectangle as it
       // was reported relative to the picture size reported by the container.
       picture.x = (mPicture.x * frame.Y.mWidth) / mInitialFrame.width;
       picture.y = (mPicture.y * frame.Y.mHeight) / mInitialFrame.height;
       picture.width = (frame.Y.mWidth * mPicture.width) / mInitialFrame.width;
       picture.height = (frame.Y.mHeight * mPicture.height) / mInitialFrame.height;
     }
 
+    MOZ_ASSERT(mStreamSource);
     // This is the approximate byte position in the stream.
-    int64_t pos = mDecoder->GetResource()->Tell();
+    int64_t pos = mStreamSource->Tell();
 
     nsRefPtr<VideoData> v;
     if (!frame.mGraphicBuffer) {
 
       VideoData::YCbCrBuffer b;
       b.mPlanes[0].mData = static_cast<uint8_t *>(frame.Y.mData);
       b.mPlanes[0].mStride = frame.Y.mStride;
       b.mPlanes[0].mHeight = frame.Y.mHeight;
@@ -466,17 +468,18 @@ void MediaOmxReader::NotifyDataArrivedIn
   }
   if (HasVideo()) {
     return;
   }
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
-  nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
+  nsRefPtr<MediaByteBuffer> bytes =
+    mDecoder->GetResource()->MediaReadAt(aOffset, aLength);
   NS_ENSURE_TRUE_VOID(bytes);
   mMP3FrameParser.Parse(bytes->Elements(), aLength, aOffset);
   if (!mMP3FrameParser.IsMP3()) {
     return;
   }
 
   int64_t duration = mMP3FrameParser.GetDuration();
   if (duration != mLastParserDuration) {
@@ -485,18 +488,19 @@ void MediaOmxReader::NotifyDataArrivedIn
   }
 }
 
 bool MediaOmxReader::DecodeAudioData()
 {
   MOZ_ASSERT(OnTaskQueue());
   EnsureActive();
 
+  MOZ_ASSERT(mStreamSource);
   // This is the approximate byte position in the stream.
-  int64_t pos = mDecoder->GetResource()->Tell();
+  int64_t pos = mStreamSource->Tell();
 
   // Read next frame
   MPAPI::AudioFrame source;
   if (!mOmxDecoder->ReadAudio(&source, mAudioSeekTimeUs)) {
     return false;
   }
   mAudioSeekTimeUs = -1;
 
--- a/dom/media/omx/MediaStreamSource.cpp
+++ b/dom/media/omx/MediaStreamSource.cpp
@@ -28,37 +28,44 @@ status_t MediaStreamSource::initCheck() 
 
 ssize_t MediaStreamSource::readAt(off64_t offset, void *data, size_t size)
 {
   char *ptr = static_cast<char *>(data);
   size_t todo = size;
   while (todo > 0) {
     Mutex::Autolock autoLock(mLock);
     uint32_t bytesRead;
-    if ((offset != mResource->Tell() &&
-         NS_FAILED(mResource->Seek(nsISeekableStream::NS_SEEK_SET, offset))) ||
-        NS_FAILED(mResource->Read(ptr, todo, &bytesRead))) {
+    if ((offset != mResource.Tell() &&
+         NS_FAILED(mResource.Seek(nsISeekableStream::NS_SEEK_SET, offset))) ||
+        NS_FAILED(mResource.Read(ptr, todo, &bytesRead))) {
       return ERROR_IO;
     }
 
     if (bytesRead == 0) {
       return size - todo;
     }
 
     offset += bytesRead;
     todo -= bytesRead;
     ptr += bytesRead;
   }
   return size;
 }
 
 status_t MediaStreamSource::getSize(off64_t *size)
 {
-  uint64_t length = mResource->GetLength();
+  uint64_t length = mResource.GetLength();
   if (length == static_cast<uint64_t>(-1))
     return ERROR_UNSUPPORTED;
 
   *size = length;
 
   return OK;
 }
 
+int64_t
+MediaStreamSource::Tell()
+{
+  Mutex::Autolock autoLock(mLock);
+  return mResource.Tell();
+}
+
 } // namespace android
--- a/dom/media/omx/MediaStreamSource.h
+++ b/dom/media/omx/MediaStreamSource.h
@@ -15,19 +15,20 @@
 #include "MediaResource.h"
 #include "nsAutoPtr.h"
 
 namespace android {
 
 // MediaStreamSource is a DataSource that reads from a MPAPI media stream.
 class MediaStreamSource : public DataSource {
   typedef mozilla::MediaResource MediaResource;
+  typedef mozilla::MediaResourceIndex MediaResourceIndex;
 
   Mutex mLock;
-  nsRefPtr<MediaResource> mResource;
+  MediaResourceIndex mResource;
 public:
   MediaStreamSource(MediaResource* aResource);
 
   virtual status_t initCheck() const;
   virtual ssize_t readAt(off64_t offset, void *data, size_t size);
   virtual ssize_t readAt(off_t offset, void *data, size_t size) {
     return readAt(static_cast<off64_t>(offset), data, size);
   }
@@ -37,16 +38,18 @@ public:
     *size = size64;
     return status;
   }
   virtual status_t getSize(off64_t *size);
   virtual uint32_t flags() {
     return kWantsPrefetching;
   }
 
+  int64_t Tell();
+
   virtual ~MediaStreamSource();
 
 private:
   MediaStreamSource(const MediaStreamSource &);
   MediaStreamSource &operator=(const MediaStreamSource &);
 };
 
 } // namespace android
--- a/dom/media/raw/RawReader.cpp
+++ b/dom/media/raw/RawReader.cpp
@@ -11,17 +11,17 @@
 #include "nsISeekableStream.h"
 #include "gfx2DGlue.h"
 
 using namespace mozilla;
 using namespace mozilla::media;
 
 RawReader::RawReader(AbstractMediaDecoder* aDecoder)
   : MediaDecoderReader(aDecoder),
-    mCurrentFrame(0), mFrameSize(0)
+    mCurrentFrame(0), mFrameSize(0), mResource(aDecoder->GetResource())
 {
   MOZ_COUNT_CTOR(RawReader);
 }
 
 RawReader::~RawReader()
 {
   MOZ_COUNT_DTOR(RawReader);
 }
@@ -37,20 +37,17 @@ nsresult RawReader::ResetDecode()
   return MediaDecoderReader::ResetDecode();
 }
 
 nsresult RawReader::ReadMetadata(MediaInfo* aInfo,
                                  MetadataTags** aTags)
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  MediaResource* resource = mDecoder->GetResource();
-  NS_ASSERTION(resource, "Decoder has no media resource");
-
-  if (!ReadFromResource(resource, reinterpret_cast<uint8_t*>(&mMetadata),
+  if (!ReadFromResource(reinterpret_cast<uint8_t*>(&mMetadata),
                         sizeof(mMetadata)))
     return NS_ERROR_FAILURE;
 
   // Validate the header
   if (!(mMetadata.headerPacketID == 0 /* Packet ID of 0 for the header*/ &&
         mMetadata.codecID == RAW_ID /* "YUV" */ &&
         mMetadata.majorVersion == 0 &&
         mMetadata.minorVersion == 1))
@@ -91,17 +88,17 @@ nsresult RawReader::ReadMetadata(MediaIn
       mMetadata.lumaChannelBpp != 8 ||
       mMetadata.colorspace != 1 /* 4:2:0 */)
     return NS_ERROR_FAILURE;
 
   mFrameSize = mMetadata.frameWidth * mMetadata.frameHeight *
     (mMetadata.lumaChannelBpp + mMetadata.chromaChannelBpp) / 8.0 +
     sizeof(RawPacketHeader);
 
-  int64_t length = resource->GetLength();
+  int64_t length = mResource.GetLength();
   if (length != -1) {
     mInfo.mMetadataDuration.emplace(TimeUnit::FromSeconds((length - sizeof(RawVideoHeader)) /
                                                           (mFrameSize * mFrameRate)));
   }
 
   *aInfo = mInfo;
 
   *aTags = nullptr;
@@ -119,24 +116,23 @@ RawReader::IsMediaSeekable()
  bool RawReader::DecodeAudioData()
 {
   MOZ_ASSERT(OnTaskQueue() || mDecoder->OnStateMachineTaskQueue());
   return false;
 }
 
 // Helper method that either reads until it gets aLength bytes
 // or returns false
-bool RawReader::ReadFromResource(MediaResource *aResource, uint8_t* aBuf,
-                                   uint32_t aLength)
+bool RawReader::ReadFromResource(uint8_t* aBuf, uint32_t aLength)
 {
   while (aLength > 0) {
     uint32_t bytesRead = 0;
     nsresult rv;
 
-    rv = aResource->Read(reinterpret_cast<char*>(aBuf), aLength, &bytesRead);
+    rv = mResource.Read(reinterpret_cast<char*>(aBuf), aLength, &bytesRead);
     NS_ENSURE_SUCCESS(rv, false);
 
     if (bytesRead == 0) {
       return false;
     }
 
     aLength -= bytesRead;
     aBuf += bytesRead;
@@ -156,31 +152,29 @@ bool RawReader::DecodeVideoFrame(bool &a
 
   if (!mFrameSize)
     return false; // Metadata read failed.  We should refuse to play.
 
   int64_t currentFrameTime = USECS_PER_S * mCurrentFrame / mFrameRate;
   uint32_t length = mFrameSize - sizeof(RawPacketHeader);
 
   nsAutoArrayPtr<uint8_t> buffer(new uint8_t[length]);
-  MediaResource* resource = mDecoder->GetResource();
-  NS_ASSERTION(resource, "Decoder has no media resource");
 
   // We're always decoding one frame when called
   while(true) {
     RawPacketHeader header;
 
     // Read in a packet header and validate
-    if (!(ReadFromResource(resource, reinterpret_cast<uint8_t*>(&header),
+    if (!(ReadFromResource(reinterpret_cast<uint8_t*>(&header),
                            sizeof(header))) ||
         !(header.packetID == 0xFF && header.codecID == RAW_ID /* "YUV" */)) {
       return false;
     }
 
-    if (!ReadFromResource(resource, buffer, length)) {
+    if (!ReadFromResource(buffer, length)) {
       return false;
     }
 
     a.mParsed++;
 
     if (currentFrameTime >= aTimeThreshold)
       break;
 
@@ -228,29 +222,26 @@ bool RawReader::DecodeVideoFrame(bool &a
   return true;
 }
 
 nsRefPtr<MediaDecoderReader::SeekPromise>
 RawReader::Seek(int64_t aTime, int64_t aEndTime)
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  MediaResource *resource = mDecoder->GetResource();
-  NS_ASSERTION(resource, "Decoder has no media resource");
-
   uint32_t frame = mCurrentFrame;
   if (aTime >= UINT_MAX)
     return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   mCurrentFrame = aTime * mFrameRate / USECS_PER_S;
 
   CheckedUint32 offset = CheckedUint32(mCurrentFrame) * mFrameSize;
   offset += sizeof(RawVideoHeader);
   NS_ENSURE_TRUE(offset.isValid(), SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__));
 
-  nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, offset.value());
+  nsresult rv = mResource.Seek(nsISeekableStream::NS_SEEK_SET, offset.value());
   NS_ENSURE_SUCCESS(rv, SeekPromise::CreateAndReject(rv, __func__));
 
   mVideoQueue.Reset();
   nsRefPtr<SeekPromise::Private> p = new SeekPromise::Private(__func__);
   nsRefPtr<RawReader> self = this;
   InvokeUntil([self] () {
     MOZ_ASSERT(self->OnTaskQueue());
     NS_ENSURE_TRUE(!self->mShutdown, false);
--- a/dom/media/raw/RawReader.h
+++ b/dom/media/raw/RawReader.h
@@ -42,20 +42,21 @@ public:
   virtual nsRefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aEndTime) override;
 
   virtual media::TimeIntervals GetBuffered() override;
 
   virtual bool IsMediaSeekable() override;
 
 private:
-  bool ReadFromResource(MediaResource *aResource, uint8_t *aBuf, uint32_t aLength);
+  bool ReadFromResource(uint8_t *aBuf, uint32_t aLength);
 
   RawVideoHeader mMetadata;
   uint32_t mCurrentFrame;
   double mFrameRate;
   uint32_t mFrameSize;
   nsIntRect mPicture;
+  MediaResourceIndex mResource;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/wave/WaveReader.cpp
+++ b/dom/media/wave/WaveReader.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "nsError.h"
 #include "AbstractMediaDecoder.h"
-#include "MediaResource.h"
 #include "WaveReader.h"
 #include "MediaDecoderStateMachine.h"
 #include "VideoUtils.h"
 #include "nsISeekableStream.h"
 
 #include <stdint.h>
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/CheckedInt.h"
@@ -102,16 +101,17 @@ namespace {
     uint8_t result = uint8_t((*aBuffer)[0]);
     *aBuffer += sizeof(uint8_t);
     return result;
   }
 } // namespace
 
 WaveReader::WaveReader(AbstractMediaDecoder* aDecoder)
   : MediaDecoderReader(aDecoder)
+  , mResource(aDecoder->GetResource())
 {
   MOZ_COUNT_CTOR(WaveReader);
 }
 
 WaveReader::~WaveReader()
 {
   MOZ_COUNT_DTOR(WaveReader);
 }
@@ -260,17 +260,17 @@ WaveReader::Seek(int64_t aTarget, int64_
   }
   double d = BytesToTime(GetDataLength());
   NS_ASSERTION(d < INT64_MAX / USECS_PER_S, "Duration overflow");
   int64_t duration = static_cast<int64_t>(d * USECS_PER_S);
   double seekTime = std::min(aTarget, duration) / static_cast<double>(USECS_PER_S);
   int64_t position = RoundDownToFrame(static_cast<int64_t>(TimeToBytes(seekTime)));
   NS_ASSERTION(INT64_MAX - mWavePCMOffset > position, "Integer overflow during wave seek");
   position += mWavePCMOffset;
-  nsresult res = mDecoder->GetResource()->Seek(nsISeekableStream::NS_SEEK_SET, position);
+  nsresult res = mResource.Seek(nsISeekableStream::NS_SEEK_SET, position);
   if (NS_FAILED(res)) {
     return SeekPromise::CreateAndReject(res, __func__);
   } else {
     return SeekPromise::CreateAndResolve(aTarget, __func__);
   }
 }
 
 media::TimeIntervals WaveReader::GetBuffered()
@@ -303,17 +303,17 @@ bool
 WaveReader::ReadAll(char* aBuf, int64_t aSize, int64_t* aBytesRead)
 {
   uint32_t got = 0;
   if (aBytesRead) {
     *aBytesRead = 0;
   }
   do {
     uint32_t read = 0;
-    if (NS_FAILED(mDecoder->GetResource()->Read(aBuf + got, uint32_t(aSize - got), &read))) {
+    if (NS_FAILED(mResource.Read(aBuf + got, uint32_t(aSize - got), &read))) {
       NS_WARNING("Resource read failed");
       return false;
     }
     if (read == 0) {
       return false;
     }
     got += read;
     if (aBytesRead) {
@@ -324,17 +324,17 @@ WaveReader::ReadAll(char* aBuf, int64_t 
 }
 
 bool
 WaveReader::LoadRIFFChunk()
 {
   char riffHeader[RIFF_INITIAL_SIZE];
   const char* p = riffHeader;
 
-  MOZ_ASSERT(mDecoder->GetResource()->Tell() == 0,
+  MOZ_ASSERT(mResource.Tell() == 0,
              "LoadRIFFChunk called when resource in invalid state");
 
   if (!ReadAll(riffHeader, sizeof(riffHeader))) {
     return false;
   }
 
   static_assert(sizeof(uint32_t) * 3 <= RIFF_INITIAL_SIZE,
                 "Reads would overflow riffHeader buffer.");
@@ -357,17 +357,17 @@ WaveReader::LoadRIFFChunk()
 bool
 WaveReader::LoadFormatChunk(uint32_t aChunkSize)
 {
   uint32_t rate, channels, frameSize, sampleFormat;
   char waveFormat[WAVE_FORMAT_CHUNK_SIZE];
   const char* p = waveFormat;
 
   // RIFF chunks are always word (two byte) aligned.
-  MOZ_ASSERT(mDecoder->GetResource()->Tell() % 2 == 0,
+  MOZ_ASSERT(mResource.Tell() % 2 == 0,
              "LoadFormatChunk called with unaligned resource");
 
   if (!ReadAll(waveFormat, sizeof(waveFormat))) {
     return false;
   }
 
   static_assert(sizeof(uint16_t) +
                 sizeof(uint16_t) +
@@ -419,17 +419,17 @@ WaveReader::LoadFormatChunk(uint32_t aCh
       nsAutoArrayPtr<char> chunkExtension(new char[extra]);
       if (!ReadAll(chunkExtension.get(), extra)) {
         return false;
       }
     }
   }
 
   // RIFF chunks are always word (two byte) aligned.
-  MOZ_ASSERT(mDecoder->GetResource()->Tell() % 2 == 0,
+  MOZ_ASSERT(mResource.Tell() % 2 == 0,
              "LoadFormatChunk left resource unaligned");
 
   // Make sure metadata is fairly sane.  The rate check is fairly arbitrary,
   // but the channels check is intentionally limited to mono or stereo
   // when the media is intended for direct playback because that's what the
   // audio backend currently supports.
   unsigned int actualFrameSize = (sampleFormat == 8 ? 1 : 2) * channels;
   if (rate < 100 || rate > 96000 ||
@@ -453,20 +453,20 @@ WaveReader::LoadFormatChunk(uint32_t aCh
   }
   return true;
 }
 
 bool
 WaveReader::FindDataOffset(uint32_t aChunkSize)
 {
   // RIFF chunks are always word (two byte) aligned.
-  MOZ_ASSERT(mDecoder->GetResource()->Tell() % 2 == 0,
+  MOZ_ASSERT(mResource.Tell() % 2 == 0,
              "FindDataOffset called with unaligned resource");
 
-  int64_t offset = mDecoder->GetResource()->Tell();
+  int64_t offset = mResource.Tell();
   if (offset <= 0 || offset > UINT32_MAX) {
     NS_WARNING("PCM data offset out of range");
     return false;
   }
 
   ReentrantMonitorAutoEnter monitor(mDecoder->GetReentrantMonitor());
   mWaveLength = aChunkSize;
   mWavePCMOffset = uint32_t(offset);
@@ -507,25 +507,25 @@ WaveReader::GetDataLength()
     length = std::min(dataLength, length);
   }
   return length;
 }
 
 int64_t
 WaveReader::GetPosition()
 {
-  return mDecoder->GetResource()->Tell();
+  return mResource.Tell();
 }
 
 bool
 WaveReader::GetNextChunk(uint32_t* aChunk, uint32_t* aChunkSize)
 {
   MOZ_ASSERT(aChunk, "Must have aChunk");
   MOZ_ASSERT(aChunkSize, "Must have aChunkSize");
-  MOZ_ASSERT(mDecoder->GetResource()->Tell() % 2 == 0,
+  MOZ_ASSERT(mResource.Tell() % 2 == 0,
              "GetNextChunk called with unaligned resource");
 
   char chunkHeader[CHUNK_HEADER_SIZE];
   const char* p = chunkHeader;
 
   if (!ReadAll(chunkHeader, sizeof(chunkHeader))) {
     return false;
   }
@@ -538,17 +538,17 @@ WaveReader::GetNextChunk(uint32_t* aChun
   return true;
 }
 
 bool
 WaveReader::LoadListChunk(uint32_t aChunkSize,
                           nsAutoPtr<dom::HTMLMediaElement::MetadataTags> &aTags)
 {
   // List chunks are always word (two byte) aligned.
-  MOZ_ASSERT(mDecoder->GetResource()->Tell() % 2 == 0,
+  MOZ_ASSERT(mResource.Tell() % 2 == 0,
              "LoadListChunk called with unaligned resource");
 
   static const unsigned int MAX_CHUNK_SIZE = 1 << 16;
   static_assert(uint64_t(MAX_CHUNK_SIZE) < UINT_MAX / sizeof(char),
                 "MAX_CHUNK_SIZE too large for enumerator.");
 
   if (aChunkSize > MAX_CHUNK_SIZE || aChunkSize < 4) {
     return false;
@@ -614,17 +614,17 @@ WaveReader::LoadListChunk(uint32_t aChun
 
   return true;
 }
 
 bool
 WaveReader::LoadAllChunks(nsAutoPtr<dom::HTMLMediaElement::MetadataTags> &aTags)
 {
   // Chunks are always word (two byte) aligned.
-  MOZ_ASSERT(mDecoder->GetResource()->Tell() % 2 == 0,
+  MOZ_ASSERT(mResource.Tell() % 2 == 0,
              "LoadAllChunks called with unaligned resource");
 
   bool loadFormatChunk = false;
   bool findDataOffset = false;
 
   for (;;) {
     static const unsigned int CHUNK_HEADER_SIZE = 8;
     char chunkHeader[CHUNK_HEADER_SIZE];
--- a/dom/media/wave/WaveReader.h
+++ b/dom/media/wave/WaveReader.h
@@ -2,16 +2,18 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 #if !defined(WaveReader_h_)
 #define WaveReader_h_
 
 #include "MediaDecoderReader.h"
+#include "MediaResource.h"
+
 #include "mozilla/dom/HTMLMediaElement.h"
 
 namespace mozilla {
 
 class WaveReader : public MediaDecoderReader
 {
 public:
   explicit WaveReader(AbstractMediaDecoder* aDecoder);
@@ -93,13 +95,15 @@ private:
 
   // Size of PCM data stored in the WAVE as reported by the data chunk in
   // the media.
   int64_t mWaveLength;
 
   // Start offset of the PCM data in the media stream.  Extends mWaveLength
   // bytes.
   int64_t mWavePCMOffset;
+
+  MediaResourceIndex mResource;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/webm/WebMReader.cpp
+++ b/dom/media/webm/WebMReader.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "nsError.h"
 #include "MediaDecoderStateMachine.h"
 #include "AbstractMediaDecoder.h"
-#include "MediaResource.h"
 #include "SoftwareWebMVideoDecoder.h"
 #include "WebMReader.h"
 #include "WebMBufferedParser.h"
 #include "gfx2DGlue.h"
 #include "Layers.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SharedThreadPool.h"
 
@@ -49,20 +48,18 @@ extern PRLogModuleInfo* gMediaDecoderLog
 PRLogModuleInfo* gNesteggLog;
 
 // Functions for reading and seeking using MediaResource required for
 // nestegg_io. The 'user data' passed to these functions is the
 // decoder from which the media resource is obtained.
 static int webm_read(void *aBuffer, size_t aLength, void *aUserData)
 {
   MOZ_ASSERT(aUserData);
-  AbstractMediaDecoder* decoder =
-    reinterpret_cast<AbstractMediaDecoder*>(aUserData);
-  MediaResource* resource = decoder->GetResource();
-  NS_ASSERTION(resource, "Decoder has no media resource");
+  MediaResourceIndex* resource =
+    reinterpret_cast<MediaResourceIndex*>(aUserData);
 
   nsresult rv = NS_OK;
   bool eof = false;
 
   char *p = static_cast<char *>(aBuffer);
   while (NS_SUCCEEDED(rv) && aLength > 0) {
     uint32_t bytes = 0;
     rv = resource->Read(p, aLength, &bytes);
@@ -75,31 +72,27 @@ static int webm_read(void *aBuffer, size
   }
 
   return NS_FAILED(rv) ? -1 : eof ? 0 : 1;
 }
 
 static int webm_seek(int64_t aOffset, int aWhence, void *aUserData)
 {
   MOZ_ASSERT(aUserData);
-  AbstractMediaDecoder* decoder =
-    reinterpret_cast<AbstractMediaDecoder*>(aUserData);
-  MediaResource* resource = decoder->GetResource();
-  NS_ASSERTION(resource, "Decoder has no media resource");
+  MediaResourceIndex* resource =
+    reinterpret_cast<MediaResourceIndex*>(aUserData);
   nsresult rv = resource->Seek(aWhence, aOffset);
   return NS_SUCCEEDED(rv) ? 0 : -1;
 }
 
 static int64_t webm_tell(void *aUserData)
 {
   MOZ_ASSERT(aUserData);
-  AbstractMediaDecoder* decoder =
-    reinterpret_cast<AbstractMediaDecoder*>(aUserData);
-  MediaResource* resource = decoder->GetResource();
-  NS_ASSERTION(resource, "Decoder has no media resource");
+  MediaResourceIndex* resource =
+    reinterpret_cast<MediaResourceIndex*>(aUserData);
   return resource->Tell();
 }
 
 static void webm_log(nestegg * context,
                      unsigned int severity,
                      char const * format, ...)
 {
   if (!MOZ_LOG_TEST(gNesteggLog, LogLevel::Debug)) {
@@ -153,16 +146,17 @@ WebMReader::WebMReader(AbstractMediaDeco
   , mAudioFrames(0)
   , mSeekPreroll(0)
   , mLastVideoFrameTime(0)
   , mAudioCodec(-1)
   , mVideoCodec(-1)
   , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
   , mHasVideo(false)
   , mHasAudio(false)
+  , mResource(aDecoder->GetResource())
 {
   MOZ_COUNT_CTOR(WebMReader);
   if (!gNesteggLog) {
     gNesteggLog = PR_NewLogModule("Nestegg");
   }
 
 #if defined(MOZ_PDM_VPX)
   sIsIntelDecoderEnabled = Preferences::GetBool("media.webm.intel_decoder.enabled", false);
@@ -283,17 +277,17 @@ nsresult WebMReader::ReadMetadata(MediaI
   // queue that TrackBuffer uses. We should be able to fix this when we do
   // bug 1148234.
   MOZ_ASSERT(mDecoder->OnDecodeTaskQueue());
 
   nestegg_io io;
   io.read = webm_read;
   io.seek = webm_seek;
   io.tell = webm_tell;
-  io.userdata = mDecoder;
+  io.userdata = &mResource;
   int64_t maxOffset = mDecoder->HasInitializationData() ?
     mBufferedState->GetInitEndOffset() : -1;
   int r = nestegg_init(&mContext, io, &webm_log, maxOffset);
   if (r == -1) {
     return NS_ERROR_FAILURE;
   }
 
   uint64_t duration = 0;
@@ -618,17 +612,17 @@ WebMReader::DemuxPacket()
     if (mVideoCodec == NESTEGG_CODEC_VP8) {
       vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), data, length, &si);
     } else if (mVideoCodec == NESTEGG_CODEC_VP9) {
       vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), data, length, &si);
     }
     isKeyframe = si.is_kf;
   }
 
-  int64_t offset = mDecoder->GetResource()->Tell();
+  int64_t offset = mResource.Tell();
   nsRefPtr<NesteggPacketHolder> holder = new NesteggPacketHolder();
   if (!holder->Init(packet, offset, track, isKeyframe)) {
     return nullptr;
   }
 
   return holder;
 }
 
@@ -838,17 +832,18 @@ media::TimeIntervals WebMReader::GetBuff
   }
 
   return buffered;
 }
 
 void WebMReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
 {
   MOZ_ASSERT(OnTaskQueue());
-  nsRefPtr<MediaByteBuffer> bytes = mDecoder->GetResource()->SilentReadAt(aOffset, aLength);
+  nsRefPtr<MediaByteBuffer> bytes =
+    mDecoder->GetResource()->MediaReadAt(aOffset, aLength);
   NS_ENSURE_TRUE_VOID(bytes);
   mBufferedState->NotifyDataArrived(bytes->Elements(), aLength, aOffset);
 }
 
 int64_t WebMReader::GetEvictionOffset(double aTime)
 {
   int64_t offset;
   if (!mBufferedState->GetOffsetForTime(aTime * NS_PER_S, &offset)) {
--- a/dom/media/webm/WebMReader.h
+++ b/dom/media/webm/WebMReader.h
@@ -4,16 +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/. */
 #if !defined(WebMReader_h_)
 #define WebMReader_h_
 
 #include <stdint.h>
 
 #include "MediaDecoderReader.h"
+#include "MediaResource.h"
 #include "nsAutoRef.h"
 #include "nestegg/nestegg.h"
 
 #define VPX_DONT_DEFINE_STDINT_TYPES
 #include "vpx/vpx_codec.h"
 
 #include "mozilla/layers/LayersTypes.h"
 
@@ -205,13 +206,15 @@ private:
 
   // For hardware video decoding.
   nsRefPtr<FlushableTaskQueue> mVideoTaskQueue;
 
   // Booleans to indicate if we have audio and/or video data
   bool mHasVideo;
   bool mHasAudio;
 
+  MediaResourceIndex mResource;
+
 };
 
 } // namespace mozilla
 
 #endif