Bug 1368837 - media.cache.resource-index controls the MediaResourceIndex cache size - r=cpearce draft
authorGerald Squelart <gsquelart@mozilla.com>
Tue, 30 May 2017 21:43:28 +1200
changeset 587483 c14274263d3739e5dd6c66ffb1ed3a414ba531ef
parent 587482 f319e0b48b62f05c063f89f0ac9dbf947e753c48
child 587484 9a37fffdef77cf56200ca1c595d47d200b252a14
push id61722
push usergsquelart@mozilla.com
push dateThu, 01 Jun 2017 04:10:47 +0000
reviewerscpearce
bugs1368837
milestone55.0a1
Bug 1368837 - media.cache.resource-index controls the MediaResourceIndex cache size - r=cpearce 8KB by default, otherwise using the next power of two from the given media.cache.resource-index (but staying within 32B-128KB). '0' means we don't want to use caching. MozReview-Commit-ID: 8LmS15Ft2MA
dom/media/MediaPrefs.h
dom/media/MediaResource.cpp
dom/media/MediaResource.h
modules/libpref/init/all.js
--- a/dom/media/MediaPrefs.h
+++ b/dom/media/MediaPrefs.h
@@ -81,16 +81,19 @@ private:
     {
       AssertMainThread();
       PrefAddVarCache(&mValue, aPreference, mValue);
     }
   };
 
   // This is where DECL_MEDIA_PREF for each of the preferences should go.
 
+  // Cache sizes.
+  DECL_MEDIA_PREF("media.cache.resource-index",               MediaResourceIndexCache, uint32_t, 8192);
+
   // AudioSink
   DECL_MEDIA_PREF("accessibility.monoaudio.enable",           MonoAudio, bool, false);
   DECL_MEDIA_PREF("media.resampling.enabled",                 AudioSinkResampling, bool, false);
   DECL_MEDIA_PREF("media.resampling.rate",                    AudioSinkResampleRate, uint32_t, 48000);
 #if defined(XP_WIN) || defined(XP_DARWIN) || defined(MOZ_PULSEAUDIO)
   // libcubeb backend implement .get_preferred_channel_layout
   DECL_MEDIA_PREF("media.forcestereo.enabled",                AudioSinkForceStereo, bool, false);
 #else
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -1725,16 +1725,20 @@ MediaResourceIndex::ReadAt(int64_t aOffs
 }
 
 nsresult
 MediaResourceIndex::CachedReadAt(int64_t aOffset,
                                  char* aBuffer,
                                  uint32_t aCount,
                                  uint32_t* aBytes)
 {
+  if (mCacheBlockSize == 0) {
+    return UncachedReadAt(aOffset, aBuffer, aCount, aBytes);
+  }
+
   const int oOffset = int(aOffset);
   const unsigned oCount = unsigned(aCount);
   *aBytes = 0;
 
   if (aCount == 0) {
     printf("**** [%p]ReadAt(%u@%d) - aCount==0 -> NS_OK, 0\n",
            this,
            oCount,
@@ -1825,17 +1829,17 @@ MediaResourceIndex::CachedReadAt(int64_t
              unsigned(aCount),
              int(aOffset));
     }
 
     if (aOffset - 1 >= lastBlockOffset) {
       // We were already reading cached data from the last block, we need more
       // from it -> try to top-up, read what we can, and we'll be done.
       MOZ_ASSERT(aOffset == mCachedOffset + mCachedBytes);
-      MOZ_ASSERT(endOffset <= lastBlockOffset + BLOCK_SIZE);
+      MOZ_ASSERT(endOffset <= lastBlockOffset + mCacheBlockSize);
       return CacheOrReadAt(
         oOffset, oCount, "top-up cache", aOffset, aBuffer, aCount, aBytes);
     }
 
     // We were not in the last block (but we may just have crossed the line now)
     MOZ_ASSERT(aOffset <= lastBlockOffset);
     // Continue below...
   } else if (aOffset >= lastBlockOffset) {
@@ -1897,17 +1901,17 @@ MediaResourceIndex::CachedReadAt(int64_t
       oOffset,
       unsigned(read),
       unsigned(aCount),
       int(aOffset));
   }
 
   // We should just have reached the start of the last block.
   MOZ_ASSERT(aOffset == lastBlockOffset);
-  MOZ_ASSERT(aCount <= BLOCK_SIZE);
+  MOZ_ASSERT(aCount <= mCacheBlockSize);
   // Make sure to invalidate the cache first.
   mCachedBytes = 0;
   return CacheOrReadAt(
     oOffset, oCount, "last block", aOffset, aBuffer, aCount, aBytes);
 }
 
 nsresult
 MediaResourceIndex::CacheOrReadAt(int oOffset,
@@ -1916,33 +1920,34 @@ MediaResourceIndex::CacheOrReadAt(int oO
                                   int64_t aOffset,
                                   char* aBuffer,
                                   uint32_t aCount,
                                   uint32_t* aBytes)
 {
   // We should be here because there is more data to read.
   MOZ_ASSERT(aCount > 0);
   // We should be in the last block, so we shouldn't try to read past it.
-  MOZ_ASSERT(IndexInCache(aOffset) + aCount <= BLOCK_SIZE);
+  MOZ_ASSERT(IndexInCache(aOffset) + aCount <= mCacheBlockSize);
 
   const int64_t length = GetLength();
   // If length is unknown (-1), look at resource-cached data.
   // If length is known and equal or greater than requested, also look at
   // resource-cached data.
   // Otherwise, if length is known but same, or less than(!?), requested, don't
   // attempt to access resource-cached data, as we're not expecting it to ever
   // be greater than the length.
   if (length < 0 || length >= aOffset + aCount) {
     // Is there cached data covering at least the requested range?
     const int64_t cachedDataEnd = mResource->GetCachedDataEnd(aOffset);
     if (cachedDataEnd >= aOffset + aCount) {
       // Try to read as much resource-cached data as can fill our local cache.
       const uint32_t cacheIndex = IndexInCache(aOffset);
-      const uint32_t toRead = uint32_t(
-        std::min(cachedDataEnd - aOffset, int64_t(BLOCK_SIZE - cacheIndex)));
+      const uint32_t toRead =
+        uint32_t(std::min(cachedDataEnd - aOffset,
+                          int64_t(mCacheBlockSize - cacheIndex)));
       MOZ_ASSERT(toRead >= aCount);
       nsresult rv =
         mResource->ReadFromCache(&mCachedBlock[cacheIndex], aOffset, toRead);
       if (NS_SUCCEEDED(rv)) {
         // Success means we have read the full `toRead` amount.
         printf("**** [%p]ReadAt(%u@%d) - %s - ReadFromCache(%u@%d) succeeded\n",
                this,
                oCount,
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -13,20 +13,22 @@
 #include "nsIStreamingProtocolController.h"
 #include "nsIStreamListener.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
 #include "Intervals.h"
 #include "MediaCache.h"
 #include "MediaContainerType.h"
 #include "MediaData.h"
+#include "MediaPrefs.h"
 #include "MediaResourceCallback.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
 #include "nsThreadUtils.h"
 #include <algorithm>
 
 // For HTTP seeking, if number of bytes needing to be
 // seeked forward is less than this value then a read is
 // done rather than a byte range request.
 //
 // If we assume a 100Mbit connection, and assume reissuing an HTTP seek causes
@@ -755,18 +757,20 @@ private:
  */
 
 class MediaResourceIndex
 {
 public:
   explicit MediaResourceIndex(MediaResource* aResource)
     : mResource(aResource)
     , mOffset(0)
+    , mCacheBlockSize(SelectCacheSize(MediaPrefs::MediaResourceIndexCache()))
     , mCachedOffset(0)
     , mCachedBytes(0)
+    , mCachedBlock(MakeUnique<char[]>(mCacheBlockSize))
   {}
 
   // 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.
@@ -859,54 +863,72 @@ private:
   nsresult CacheOrReadAt(int oOffset,
                          unsigned oCount,
                          const char* oContext,
                          int64_t aOffset,
                          char* aBuffer,
                          uint32_t aCount,
                          uint32_t* aBytes);
 
+  // Select the next power of 2 (in range 32B-128KB, or 0 -> no cache)
+  static uint32_t SelectCacheSize(uint32_t aHint)
+  {
+    if (aHint == 0) {
+      return 0;
+    }
+    if (aHint <= 32) {
+      return 32;
+    }
+    if (aHint > 64*1024) {
+      return 128*1024;
+    }
+    // 32-bit next power of 2, from:
+    // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+    aHint--;
+    aHint |= aHint >> 1;
+    aHint |= aHint >> 2;
+    aHint |= aHint >> 4;
+    aHint |= aHint >> 8;
+    aHint |= aHint >> 16;
+    aHint++;
+    return aHint;
+  }
+
   // Maps a file offset to a mCachedBlock index.
   uint32_t IndexInCache(int64_t aOffsetInFile) const
   {
-    static_assert((BLOCK_SIZE & (BLOCK_SIZE - 1)) == 0,
-                  "BLOCK_SIZE must be power of 2");
-    static_assert(BLOCK_SIZE <= int64_t(UINT32_MAX),
-                  "BLOCK_SIZE must fit in 32 bits");
-    const uint32_t index = uint32_t(aOffsetInFile & (BLOCK_SIZE - 1));
-    MOZ_ASSERT(index == aOffsetInFile % BLOCK_SIZE);
+    const uint32_t index = uint32_t(aOffsetInFile) & (mCacheBlockSize - 1);
+    MOZ_ASSERT(index == aOffsetInFile % mCacheBlockSize);
     return index;
   }
 
   // Starting file offset of the cache block that contains a given file offset.
   int64_t CacheOffsetContaining(int64_t aOffsetInFile) const
   {
-    static_assert((BLOCK_SIZE & (BLOCK_SIZE - 1)) == 0,
-                  "BLOCK_SIZE must be power of 2");
-    const int64_t offset = aOffsetInFile & ~(BLOCK_SIZE - 1);
+    const int64_t offset = aOffsetInFile & ~(int64_t(mCacheBlockSize) - 1);
     MOZ_ASSERT(offset == aOffsetInFile - IndexInCache(aOffsetInFile));
     return offset;
   }
 
   RefPtr<MediaResource> mResource;
   int64_t mOffset;
 
   // Local cache used by ReadAt().
   // mCachedBlock is valid when mCachedBytes != 0, in which case it contains
   // data of length mCachedBytes, starting at offset `mCachedOffset` in the
   // resource, located at index `IndexInCache(mCachedOffset)` in mCachedBlock.
   //
   // resource: |------------------------------------------------------|
-  //                                          <----------> BLOCK_SIZE
+  //                                          <----------> mCacheBlockSize
   //           <---------------------------------> mCachedOffset
   //                                             <--> mCachedBytes
   // mCachedBlock:                            |..----....|
   //  CacheOffsetContaining(mCachedOffset)    <--> IndexInCache(mCachedOffset)
   //           <------------------------------>
-  static constexpr int64_t BLOCK_SIZE = MediaCacheStream::BLOCK_SIZE;
+  const uint32_t mCacheBlockSize;
   int64_t mCachedOffset;
   uint32_t mCachedBytes;
-  char mCachedBlock[BLOCK_SIZE];
+  UniquePtr<char[]> mCachedBlock;
 };
 
 } // namespace mozilla
 
 #endif
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -307,16 +307,20 @@ pref("media.cache_size", 512000);
 // When a network connection is suspended, don't resume it until the
 // amount of buffered data falls below this threshold (in seconds).
 pref("media.cache_resume_threshold", 30);
 // Stop reading ahead when our buffered data is this many seconds ahead
 // of the current playback position. This limit can stop us from using arbitrary
 // amounts of network bandwidth prefetching huge videos.
 pref("media.cache_readahead_limit", 60);
 
+// Cache size hint (in bytes) for each MediaResourceIndex.
+// 0 -> no cache. Will use next power of 2, clamped to 32B-128KB.
+pref("media.cache.resource-index", 8192);
+
 // We'll throttle the download if the download rate is throttle-factor times
 // the estimated playback rate, AND we satisfy the cache readahead_limit
 // above. The estimated playback rate is time_duration/length_in_bytes.
 // This means we'll only throttle the download if there's no concern that
 // throttling would cause us to stop and buffer.
 pref("media.throttle-factor", 2);
 // By default, we'll throttle media download once we've reached the the
 // readahead_limit if the download is fast. This pref toggles the "and the