From 050103a85733b076134c91518a899ebe2a9ff0f1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 29 Aug 2020 13:34:54 +1000 Subject: [PATCH] memory: implemented basic memory limiting functionality Keeps track of total memory usage and uses it as a guide towards allocating/reusing more memory. --- app/codec/ffmpeg/ffmpegdecoder.cpp | 34 +++++++++++++++++++++++------- app/codec/ffmpeg/ffmpegdecoder.h | 6 ++++-- app/common/CMakeLists.txt | 1 + app/common/memorypool.cpp | 14 ++++++++++++ app/common/memorypool.h | 19 ++++++++++++++++- 5 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 app/common/memorypool.cpp diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index b7a6cfee4..096cfcd07 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -889,13 +889,15 @@ QWaitCondition *FFmpegDecoderInstance::cache_wait_cond() return &cache_wait_cond_; } -bool FFmpegDecoderInstance::IsWorking() const +bool FFmpegDecoderInstance::IsWorking() { + QMutexLocker locker(&is_working_mutex_); return is_working_; } void FFmpegDecoderInstance::SetWorking(bool working) { + QMutexLocker locker(&is_working_mutex_); is_working_ = working; } @@ -1113,7 +1115,12 @@ FFmpegFramePool::ElementPtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& } // Clear early frames - TruncateCacheRangeTo(second_ts_); + int removed = TruncateCacheRangeTo(second_ts_); + + if (removed == 0 && MemoryPoolLimitReached()) { + // Relinquish a frame if we have to conserve memory + RemoveFirstFrame(); + } // Append this frame and signal to other threads that a new frame has arrived cached_frames_.append(cached); @@ -1295,19 +1302,30 @@ FFmpegFramePool::ElementPtr FFmpegDecoderInstance::GetFrameFromCache(const int64 void FFmpegDecoderInstance::RemoveFramesBefore(const qint64 &t) { // We keep one frame in memory as an identifier for what pts the decoder is up to - while (cached_frames_.size() > 1 && cached_frames_.first()->last_accessed() < t) { - cached_frames_.removeFirst(); - cache_at_zero_ = false; + int min_frames = (MemoryPoolLimitReached() && !IsWorking()) ? 0 : 1; + + while (cached_frames_.size() > min_frames && cached_frames_.first()->last_accessed() < t) { + RemoveFirstFrame(); } } -void FFmpegDecoderInstance::TruncateCacheRangeTo(const qint64 &t) +int FFmpegDecoderInstance::TruncateCacheRangeTo(const qint64 &t) { + int counter = 0; + // We keep one frame in memory as an identifier for what pts the decoder is up to while (cached_frames_.size() > 1 && (RangeEnd() - RangeStart()) > t) { - cached_frames_.removeFirst(); - cache_at_zero_ = false; + RemoveFirstFrame(); + counter++; } + + return counter; +} + +void FFmpegDecoderInstance::RemoveFirstFrame() +{ + cached_frames_.removeFirst(); + cache_at_zero_ = false; } FFmpegDecoderInstance::FFmpegDecoderInstance(const char *filename, int stream_index) : diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index 1686283f2..e8a5ed296 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -62,7 +62,8 @@ public: FFmpegFramePool::ElementPtr GetFrameFromCache(const int64_t& t) const; void RemoveFramesBefore(const qint64& t); - void TruncateCacheRangeTo(const qint64& t); + int TruncateCacheRangeTo(const qint64& t); + void RemoveFirstFrame(); AVFormatContext* fmt_ctx() const { @@ -90,7 +91,7 @@ public: QMutex* cache_lock(); QWaitCondition* cache_wait_cond(); - bool IsWorking() const; + bool IsWorking(); void SetWorking(bool working); private: @@ -119,6 +120,7 @@ private: int64_t cache_target_time_; bool is_working_; + QMutex is_working_mutex_; bool cache_at_zero_; bool cache_at_eof_; diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt index e35ef66ef..ee07f43c4 100644 --- a/app/common/CMakeLists.txt +++ b/app/common/CMakeLists.txt @@ -37,6 +37,7 @@ set(OLIVE_SOURCES common/functiontimer.h common/lerp.h common/memorypool.h + common/memorypool.cpp common/qtutils.h common/qtutils.cpp common/range.h diff --git a/app/common/memorypool.cpp b/app/common/memorypool.cpp new file mode 100644 index 000000000..f88ba8d46 --- /dev/null +++ b/app/common/memorypool.cpp @@ -0,0 +1,14 @@ +#include "memorypool.h" + +OLIVE_NAMESPACE_ENTER + +size_t memory_pool_consumption = 0; +QMutex memory_pool_consumption_lock; + +bool MemoryPoolLimitReached() +{ + QMutexLocker locker(&memory_pool_consumption_lock); + return (memory_pool_consumption >= 2147483648); +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/common/memorypool.h b/app/common/memorypool.h index 6566505eb..d52d24b46 100644 --- a/app/common/memorypool.h +++ b/app/common/memorypool.h @@ -32,6 +32,10 @@ OLIVE_NAMESPACE_ENTER +extern size_t memory_pool_consumption; +extern QMutex memory_pool_consumption_lock; +bool MemoryPoolLimitReached(); + template /** * @brief MemoryPool base class @@ -198,6 +202,7 @@ public: Arena(MemoryPool* parent) { parent_ = parent; data_ = nullptr; + allocated_sz_ = 0; } ~Arena() { @@ -207,6 +212,10 @@ public: } delete [] data_; + + memory_pool_consumption_lock.lock(); + memory_pool_consumption -= allocated_sz_; + memory_pool_consumption_lock.unlock(); } DISABLE_COPY_MOVE(Arena) @@ -264,10 +273,16 @@ public: element_sz_ = ele_sz; - if ((data_ = new char[element_sz_ * nb_elements])) { + allocated_sz_ = element_sz_ * nb_elements; + + if ((data_ = new char[allocated_sz_])) { available_.resize(nb_elements); available_.fill(true); + memory_pool_consumption_lock.lock(); + memory_pool_consumption += allocated_sz_; + memory_pool_consumption_lock.unlock(); + return true; } else { available_.clear(); @@ -289,6 +304,8 @@ public: char* data_; + size_t allocated_sz_; + QVector available_; QMutex lock_;