Bug 1379091 - Let block cache tell MediaCache its block use limit - r?cpearce
MozReview-Commit-ID: 5ZCD3NoeYEP
--- a/dom/media/FileBlockCache.cpp
+++ b/dom/media/FileBlockCache.cpp
@@ -1,15 +1,17 @@
/* -*- 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 "FileBlockCache.h"
+#include "MediaCache.h"
+#include "MediaPrefs.h"
#include "mozilla/SharedThreadPool.h"
#include "VideoUtils.h"
#include "prio.h"
#include <algorithm>
#include "nsAnonymousTemporaryFile.h"
#include "mozilla/dom/ContentChild.h"
#include "nsXULAppAPI.h"
@@ -116,16 +118,43 @@ FileBlockCache::Init()
if (NS_FAILED(rv)) {
Close();
}
return rv;
}
+int32_t
+FileBlockCache::GetMaxBlocks() const
+{
+ // We look up the cache size every time. This means dynamic changes
+ // to the pref are applied.
+ const uint32_t cacheSizeKb =
+ std::min(MediaPrefs::MediaCacheSizeKb(), uint32_t(INT32_MAX) * 2);
+ // Ensure we can divide BLOCK_SIZE by 1024.
+ static_assert(MediaCacheStream::BLOCK_SIZE % 1024 == 0,
+ "BLOCK_SIZE should be a multiple of 1024");
+ // Ensure BLOCK_SIZE/1024 is at least 2.
+ static_assert(MediaCacheStream::BLOCK_SIZE / 1024 >= 2,
+ "BLOCK_SIZE / 1024 should be at least 2");
+ // Ensure we can convert BLOCK_SIZE/1024 to a uint32_t without truncation.
+ static_assert(MediaCacheStream::BLOCK_SIZE / 1024 <= int64_t(UINT32_MAX),
+ "BLOCK_SIZE / 1024 should be at most UINT32_MAX");
+ // Since BLOCK_SIZE is a strict multiple of 1024,
+ // cacheSizeKb * 1024 / BLOCK_SIZE == cacheSizeKb / (BLOCK_SIZE / 1024),
+ // but the latter formula avoids a potential overflow from `* 1024`.
+ // And because BLOCK_SIZE/1024 is at least 2, the maximum cache size
+ // INT32_MAX*2 will give a maxBlocks that can fit in an int32_t.
+ constexpr uint32_t blockSizeKb =
+ uint32_t(MediaCacheStream::BLOCK_SIZE / 1024);
+ const int32_t maxBlocks = int32_t(cacheSizeKb / blockSizeKb);
+ return std::max(maxBlocks, int32_t(1));
+}
+
FileBlockCache::FileBlockCache()
: mFileMutex("MediaCache.Writer.IO.Mutex")
, mFD(nullptr)
, mFDCurrentPos(0)
, mDataMutex("MediaCache.Writer.Data.Mutex")
, mIsWriteScheduled(false)
, mIsReading(false)
{
--- a/dom/media/FileBlockCache.h
+++ b/dom/media/FileBlockCache.h
@@ -60,16 +60,20 @@ public:
protected:
virtual ~FileBlockCache();
public:
// Launch thread and open temporary file.
// If re-initializing, just discard pending writes if any.
nsresult Init() override;
+ // Maximum number of blocks allowed in this block cache.
+ // Calculated from "media.cache_size" pref.
+ int32_t GetMaxBlocks() const override;
+
// Can be called on any thread. This defers to a non-main thread.
nsresult WriteBlock(uint32_t aBlockIndex,
Span<const uint8_t> aData1,
Span<const uint8_t> aData2) override;
// Synchronously reads data from file. May read from file or memory
// depending on whether written blocks have been flushed to file yet.
// Not recommended to be called from the main thread, as can cause jank.
--- a/dom/media/MediaBlockCacheBase.h
+++ b/dom/media/MediaBlockCacheBase.h
@@ -47,16 +47,20 @@ public:
protected:
virtual ~MediaBlockCacheBase() {}
public:
// Initialize this cache.
// If called again, re-initialize cache with minimal chance of failure.
virtual nsresult Init() = 0;
+ // Maximum number of blocks expected in this block cache. (But allow overflow
+ // to accomodate incoming traffic before MediaCache can handle it.)
+ virtual int32_t GetMaxBlocks() const = 0;
+
// Can be called on any thread. This defers to a non-main thread.
virtual nsresult WriteBlock(uint32_t aBlockIndex,
Span<const uint8_t> aData1,
Span<const uint8_t> aData2) = 0;
// Synchronously reads data from file. May read from file or memory
// depending on whether written blocks have been flushed to file yet.
// Not recommended to be called from the main thread, as can cause jank.
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -3,16 +3,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/. */
#include "MediaCache.h"
#include "FileBlockCache.h"
#include "MediaBlockCacheBase.h"
+#include "MediaPrefs.h"
#include "MediaResource.h"
#include "MemoryBlockCache.h"
#include "mozilla/Attributes.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
@@ -732,41 +733,16 @@ MediaCache::ReadCacheFile(
// Since the monitor might be acquired on the main thread, we need to drop
// the monitor while doing IO in order not to block the main thread.
ReentrantMonitorAutoExit unlock(mReentrantMonitor);
return blockCache->Read(
aOffset, reinterpret_cast<uint8_t*>(aData), aLength, aBytes);
}
}
-static int32_t GetMaxBlocks()
-{
- // We look up the cache size every time. This means dynamic changes
- // to the pref are applied.
- const uint32_t cacheSizeKb =
- std::min(MediaPrefs::MediaCacheSizeKb(), uint32_t(INT32_MAX) * 2);
- // Ensure we can divide BLOCK_SIZE by 1024.
- static_assert(MediaCache::BLOCK_SIZE % 1024 == 0,
- "BLOCK_SIZE should be a multiple of 1024");
- // Ensure BLOCK_SIZE/1024 is at least 2.
- static_assert(MediaCache::BLOCK_SIZE / 1024 >= 2,
- "BLOCK_SIZE / 1024 should be at least 2");
- // Ensure we can convert BLOCK_SIZE/1024 to a uint32_t without truncation.
- static_assert(MediaCache::BLOCK_SIZE / 1024 <= int64_t(UINT32_MAX),
- "BLOCK_SIZE / 1024 should be at most UINT32_MAX");
- // Since BLOCK_SIZE is a strict multiple of 1024,
- // cacheSizeKb * 1024 / BLOCK_SIZE == cacheSizeKb / (BLOCK_SIZE / 1024),
- // but the latter formula avoids a potential overflow from `* 1024`.
- // And because BLOCK_SIZE/1024 is at least 2, the maximum cache size
- // INT32_MAX*2 will give a maxBlocks that can fit in an int32_t.
- constexpr uint32_t blockSizeKb = uint32_t(MediaCache::BLOCK_SIZE / 1024);
- const int32_t maxBlocks = int32_t(cacheSizeKb / blockSizeKb);
- return std::max(maxBlocks, int32_t(1));
-}
-
// Allowed range is whatever can be accessed with an int32_t block index.
static bool
IsOffsetAllowed(int64_t aOffset)
{
return aOffset < (int64_t(INT32_MAX) + 1) * MediaCache::BLOCK_SIZE &&
aOffset >= 0;
}
@@ -813,18 +789,20 @@ MediaCache::FindBlockForIncomingData(Tim
INT32_MAX);
if (blockIndex < 0 || !IsBlockFree(blockIndex)) {
// The block returned is already allocated.
// Don't reuse it if a) there's room to expand the cache or
// b) the data we're going to store in the free block is not higher
// priority than the data already stored in the free block.
// The latter can lead us to go over the cache limit a bit.
- if ((mIndex.Length() < uint32_t(GetMaxBlocks()) || blockIndex < 0 ||
- PredictNextUseForIncomingData(aStream) >= PredictNextUse(aNow, blockIndex))) {
+ if ((mIndex.Length() < uint32_t(mBlockCache->GetMaxBlocks()) ||
+ blockIndex < 0 ||
+ PredictNextUseForIncomingData(aStream) >=
+ PredictNextUse(aNow, blockIndex))) {
blockIndex = mIndex.Length();
if (!mIndex.AppendElement())
return -1;
mIndexWatermark = std::max(mIndexWatermark, blockIndex + 1);
mFreeBlocks.AddFirstBlock(blockIndex);
return blockIndex;
}
}
@@ -1158,17 +1136,17 @@ MediaCache::Update()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mUpdateQueued = false;
#ifdef DEBUG
mInUpdate = true;
#endif
- int32_t maxBlocks = GetMaxBlocks();
+ int32_t maxBlocks = mBlockCache->GetMaxBlocks();
TimeStamp now = TimeStamp::Now();
int32_t freeBlockCount = mFreeBlocks.GetCount();
TimeDuration latestPredictedUseForOverflow = 0;
if (mIndex.Length() > uint32_t(maxBlocks)) {
// Try to trim back the cache to its desired maximum size. The cache may
// have overflowed simply due to data being received when we have
// no blocks in the main part of the cache that are free or lower
--- a/dom/media/MemoryBlockCache.cpp
+++ b/dom/media/MemoryBlockCache.cpp
@@ -132,19 +132,33 @@ enum MemoryBlockCacheTelemetryErrors
ReadOverrun = 2,
WriteBlockOverflow = 3,
WriteBlockCannotGrow = 4,
MoveBlockSourceOverrun = 5,
MoveBlockDestOverflow = 6,
MoveBlockCannotGrow = 7,
};
+static int32_t
+CalculateMaxBlocks(int64_t aContentLength)
+{
+ // Note: It doesn't matter if calculations overflow, Init() would later fail.
+ // We want at least enough blocks to contain the original content length.
+ const int32_t requiredBlocks =
+ int32_t((aContentLength - 1) / MediaBlockCacheBase::BLOCK_SIZE + 1);
+ // Allow at least 1s of ultra HD (25Mbps).
+ const int32_t workableBlocks =
+ 25 * 1024 * 1024 / 8 / MediaBlockCacheBase::BLOCK_SIZE;
+ return std::max(requiredBlocks, workableBlocks);
+}
+
MemoryBlockCache::MemoryBlockCache(int64_t aContentLength)
// Buffer whole blocks.
: mInitialContentLength((aContentLength >= 0) ? size_t(aContentLength) : 0)
+ , mMaxBlocks(CalculateMaxBlocks(aContentLength))
, mMutex("MemoryBlockCache")
, mHasGrown(false)
{
if (aContentLength <= 0) {
LOG("MemoryBlockCache() MEMORYBLOCKCACHE_ERRORS='InitUnderuse'");
Telemetry::Accumulate(Telemetry::HistogramID::MEMORYBLOCKCACHE_ERRORS,
InitUnderuse);
}
--- a/dom/media/MemoryBlockCache.h
+++ b/dom/media/MemoryBlockCache.h
@@ -37,16 +37,20 @@ public:
protected:
virtual ~MemoryBlockCache();
public:
// Allocate initial buffer.
// If re-initializing, clear buffer.
virtual nsresult Init() override;
+ // Maximum number of blocks allowed in this block cache.
+ // Based on initial content length, and minimum usable block cache.
+ int32_t GetMaxBlocks() const override { return mMaxBlocks; }
+
// Can be called on any thread.
virtual nsresult WriteBlock(uint32_t aBlockIndex,
Span<const uint8_t> aData1,
Span<const uint8_t> aData2) override;
// Synchronously reads data from buffer.
virtual nsresult Read(int64_t aOffset,
uint8_t* aData,
@@ -66,16 +70,19 @@ private:
// Ensure the buffer has at least a multiple of BLOCK_SIZE that can contain
// aContentLength bytes. Buffer size can only grow.
// Returns false if allocation failed.
bool EnsureBufferCanContain(size_t aContentLength);
// Initial content length.
const size_t mInitialContentLength;
+ // Maximum number of blocks that this MemoryBlockCache expects.
+ const int32_t mMaxBlocks;
+
// Mutex which controls access to all members below.
Mutex mMutex;
nsTArray<uint8_t> mBuffer;
bool mHasGrown;
};
} // End namespace mozilla.