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
--- 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