memorymanager: rewrote entire memory management system
This commit is contained in:
@@ -22,5 +22,7 @@ set(OLIVE_SOURCES
|
||||
codec/ffmpeg/ffmpegdecoder.cpp
|
||||
codec/ffmpeg/ffmpegencoder.h
|
||||
codec/ffmpeg/ffmpegencoder.cpp
|
||||
codec/ffmpeg/ffmpegframecache.h
|
||||
codec/ffmpeg/ffmpegframecache.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/***
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
@@ -39,7 +39,6 @@ extern "C" {
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "ffmpegcommon.h"
|
||||
#include "render/diskmanager.h"
|
||||
#include "render/memorymanager.h"
|
||||
#include "render/pixelservice.h"
|
||||
|
||||
FFmpegDecoder::FFmpegDecoder() :
|
||||
@@ -50,17 +49,13 @@ FFmpegDecoder::FFmpegDecoder() :
|
||||
cache_at_eof_(false),
|
||||
opts_(nullptr)
|
||||
{
|
||||
connect(MemoryManager::instance(), &MemoryManager::FreeMemory, this, &FFmpegDecoder::FreeMemory, Qt::DirectConnection);
|
||||
|
||||
clear_timer_.setInterval(5000);
|
||||
clear_timer_.setSingleShot(true);
|
||||
connect(&clear_timer_, &QTimer::timeout, this, &FFmpegDecoder::ClearTimerEvent);
|
||||
}
|
||||
|
||||
FFmpegDecoder::~FFmpegDecoder()
|
||||
{
|
||||
Close();
|
||||
clear_timer_.stop();
|
||||
}
|
||||
|
||||
bool FFmpegDecoder::Open()
|
||||
@@ -182,6 +177,8 @@ bool FFmpegDecoder::Open()
|
||||
}
|
||||
|
||||
second_ts_ = qRound64(av_q2d(av_inv_q(avstream_->time_base)));
|
||||
|
||||
clear_timer_.start();
|
||||
}
|
||||
|
||||
// All allocation succeeded so we set the state to open
|
||||
@@ -215,9 +212,6 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
// Reset clear timer
|
||||
QMetaObject::invokeMethod(this, "RestartClearTimer", Qt::QueuedConnection);
|
||||
|
||||
if (!open_) {
|
||||
qWarning() << "Tried to retrieve video on a decoder that's still closed";
|
||||
return nullptr;
|
||||
@@ -242,10 +236,12 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode)
|
||||
if (cache_at_zero_ && target_ts < cached_frames_.first()->pts) {
|
||||
|
||||
found_frame = cached_frames_.first();
|
||||
cached_frames_.accessedFirst();
|
||||
|
||||
} else if (cache_at_eof_ && target_ts > cached_frames_.last()->pts) {
|
||||
|
||||
found_frame = cached_frames_.last();
|
||||
cached_frames_.accessedLast();
|
||||
|
||||
} else if (target_ts >= cached_frames_.first()->pts
|
||||
&& target_ts <= cached_frames_.last()->pts) {
|
||||
@@ -258,6 +254,7 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode)
|
||||
|| (i < cached_frames_.size() - 1 && cached_frames_.at(i+1)->pts > target_ts)) { // Or for this frame to be the "closest"
|
||||
|
||||
found_frame = this_frame;
|
||||
cached_frames_.accessed(i);
|
||||
|
||||
break;
|
||||
|
||||
@@ -385,10 +382,6 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode)
|
||||
}
|
||||
}
|
||||
|
||||
if (MemoryManager::instance()->RegisterMemory()) {
|
||||
RemoveFirstFromFrameCache();
|
||||
}
|
||||
|
||||
// Whatever it is, keep this frame in memory for the time being just in case
|
||||
cached_frames_.append(working_frame);
|
||||
}
|
||||
@@ -484,6 +477,8 @@ void FFmpegDecoder::Close()
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
ClearResources();
|
||||
|
||||
clear_timer_.stop();
|
||||
}
|
||||
|
||||
QString FFmpegDecoder::id()
|
||||
@@ -921,7 +916,7 @@ void FFmpegDecoder::CacheFrameToDisk(AVFrame *f)
|
||||
}
|
||||
}
|
||||
|
||||
void FFmpegDecoder::RemoveFirstFromFrameCache()
|
||||
/*void FFmpegDecoder::RemoveFirstFromFrameCache()
|
||||
{
|
||||
if (cached_frames_.isEmpty()) {
|
||||
return;
|
||||
@@ -941,7 +936,7 @@ void FFmpegDecoder::RemoveLastFromFrameCache()
|
||||
AVFrame* last = cached_frames_.takeLast();
|
||||
av_frame_free(&last);
|
||||
cache_at_eof_ = false;
|
||||
}
|
||||
}*/
|
||||
|
||||
void FFmpegDecoder::ClearFrameCache()
|
||||
{
|
||||
@@ -982,26 +977,9 @@ void FFmpegDecoder::ClearResources()
|
||||
open_ = false;
|
||||
}
|
||||
|
||||
void FFmpegDecoder::FreeMemory()
|
||||
{
|
||||
if (mutex_.tryLock()) {
|
||||
if (cached_frames_.size() > 1) {
|
||||
RemoveFirstFromFrameCache();
|
||||
}
|
||||
|
||||
mutex_.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void FFmpegDecoder::ClearTimerEvent()
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
ClearFrameCache();
|
||||
}
|
||||
|
||||
void FFmpegDecoder::RestartClearTimer()
|
||||
{
|
||||
clear_timer_.stop();
|
||||
clear_timer_.start();
|
||||
cached_frames_.remove_old_frames(QDateTime::currentMSecsSinceEpoch() - 5000);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ extern "C" {
|
||||
#include "audio/sampleformat.h"
|
||||
#include "codec/decoder.h"
|
||||
#include "codec/waveoutput.h"
|
||||
#include "ffmpegframecache.h"
|
||||
#include "project/item/footage/videostream.h"
|
||||
|
||||
/**
|
||||
@@ -101,8 +102,8 @@ private:
|
||||
|
||||
void CacheFrameToDisk(AVFrame* f);
|
||||
|
||||
void RemoveFirstFromFrameCache();
|
||||
void RemoveLastFromFrameCache();
|
||||
//void RemoveFirstFromFrameCache();
|
||||
//void RemoveLastFromFrameCache();
|
||||
void ClearFrameCache();
|
||||
|
||||
void ClearResources();
|
||||
@@ -116,7 +117,7 @@ private:
|
||||
|
||||
SwsContext* scale_ctx_;
|
||||
|
||||
QList<AVFrame*> cached_frames_;
|
||||
FFmpegFrameCache cached_frames_;
|
||||
bool cache_at_zero_;
|
||||
bool cache_at_eof_;
|
||||
|
||||
@@ -127,12 +128,8 @@ private:
|
||||
QTimer clear_timer_;
|
||||
|
||||
private slots:
|
||||
void FreeMemory();
|
||||
|
||||
void ClearTimerEvent();
|
||||
|
||||
void RestartClearTimer();
|
||||
|
||||
};
|
||||
|
||||
#endif // FFMPEGDECODER_H
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "ffmpegframecache.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
|
||||
int FFmpegFrameCache::global_frame_count_ = 0;
|
||||
|
||||
FFmpegFrameCache::FFmpegFrameCache()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::append(AVFrame *f)
|
||||
{
|
||||
global_frame_count_++;
|
||||
|
||||
frames_.append({f, QDateTime::currentMSecsSinceEpoch()});
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::clear()
|
||||
{
|
||||
global_frame_count_ -= frames_.size();
|
||||
|
||||
frames_.clear();
|
||||
}
|
||||
|
||||
bool FFmpegFrameCache::isEmpty() const
|
||||
{
|
||||
return frames_.isEmpty();
|
||||
}
|
||||
|
||||
AVFrame *FFmpegFrameCache::first() const
|
||||
{
|
||||
return frames_.first().frame;
|
||||
}
|
||||
|
||||
AVFrame *FFmpegFrameCache::at(int i) const
|
||||
{
|
||||
return frames_.at(i).frame;
|
||||
}
|
||||
|
||||
AVFrame *FFmpegFrameCache::last() const
|
||||
{
|
||||
return frames_.last().frame;
|
||||
}
|
||||
|
||||
int FFmpegFrameCache::size() const
|
||||
{
|
||||
return frames_.size();
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::accessedFirst()
|
||||
{
|
||||
frames_.first().accessed = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::accessedLast()
|
||||
{
|
||||
frames_.last().accessed = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::accessed(int i)
|
||||
{
|
||||
frames_[i].accessed = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::remove_old_frames(qint64 older_than)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
while (!frames_.isEmpty() && frames_.first().accessed < older_than) {
|
||||
CachedFrame cf = frames_.takeFirst();
|
||||
av_frame_free(&cf.frame);
|
||||
counter++;
|
||||
}
|
||||
|
||||
qDebug() << " * Removed" << counter << "frames";
|
||||
global_frame_count_ -= counter;
|
||||
qDebug() << " * Global frames:" << global_frame_count_;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef FFMPEGFRAMECACHE_H
|
||||
#define FFMPEGFRAMECACHE_H
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
#include <QList>
|
||||
#include <QTimer>
|
||||
|
||||
class FFmpegFrameCache
|
||||
{
|
||||
public:
|
||||
FFmpegFrameCache();
|
||||
|
||||
void append(AVFrame* f);
|
||||
void clear();
|
||||
|
||||
bool isEmpty() const;
|
||||
AVFrame* first() const;
|
||||
AVFrame* at(int i) const;
|
||||
AVFrame* last() const;
|
||||
int size() const;
|
||||
|
||||
void accessedFirst();
|
||||
void accessedLast();
|
||||
void accessed(int i);
|
||||
|
||||
void remove_old_frames(qint64 older_than);
|
||||
|
||||
private:
|
||||
struct CachedFrame {
|
||||
AVFrame* frame;
|
||||
qint64 accessed;
|
||||
};
|
||||
|
||||
QList<CachedFrame> frames_;
|
||||
|
||||
static int global_frame_count_;
|
||||
|
||||
};
|
||||
|
||||
#endif // FFMPEGFRAMECACHE_H
|
||||
@@ -48,7 +48,6 @@
|
||||
#include "render/backend/opengl/opengltexturecache.h"
|
||||
#include "render/colormanager.h"
|
||||
#include "render/diskmanager.h"
|
||||
#include "render/memorymanager.h"
|
||||
#include "render/pixelservice.h"
|
||||
#include "task/taskmanager.h"
|
||||
#include "ui/style/style.h"
|
||||
@@ -112,9 +111,6 @@ void Core::Start()
|
||||
// Set up the index manager for renderers
|
||||
IndexManager::CreateInstance();
|
||||
|
||||
// Set up memory manager
|
||||
MemoryManager::CreateInstance();
|
||||
|
||||
// Load application config
|
||||
Config::Load();
|
||||
|
||||
@@ -155,8 +151,6 @@ void Core::Stop()
|
||||
|
||||
IndexManager::DestroyInstance();
|
||||
|
||||
MemoryManager::DestroyInstance();
|
||||
|
||||
delete main_window_;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@ set(OLIVE_SOURCES
|
||||
render/colorprocessor.cpp
|
||||
render/diskmanager.h
|
||||
render/diskmanager.cpp
|
||||
render/memorymanager.h
|
||||
render/memorymanager.cpp
|
||||
render/pixelformat.h
|
||||
render/pixelformat.cpp
|
||||
render/pixelservice.h
|
||||
|
||||
@@ -63,9 +63,10 @@ DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream)
|
||||
QMutexLocker locker(decoder_cache_->lock());
|
||||
|
||||
// Access a map of Node inputs and decoder instances and retrieve a frame!
|
||||
|
||||
DecoderPtr decoder = decoder_cache_->Get(stream.get());
|
||||
|
||||
if (decoder == nullptr && stream != nullptr) {
|
||||
if (!decoder && stream) {
|
||||
// Create a new Decoder here
|
||||
decoder = Decoder::CreateFromID(stream->footage()->decoder());
|
||||
decoder->set_stream(stream);
|
||||
@@ -74,7 +75,7 @@ DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream)
|
||||
decoder_cache_->Add(stream.get(), decoder);
|
||||
} else {
|
||||
decoder = nullptr;
|
||||
qWarning() << "Failed to open decoder for" << stream->footage()->filename();
|
||||
qWarning() << "Failed to open decoder for" << stream->footage()->filename() << "::" << stream->index();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
#include "memorymanager.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
MemoryManager* MemoryManager::instance_ = nullptr;
|
||||
|
||||
// FIXME: Hardcoded, should be a preference
|
||||
const uint64_t MemoryManager::minimum_available_memory_ = 2147483648;
|
||||
|
||||
MemoryManager::MemoryManager(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void MemoryManager::CreateInstance()
|
||||
{
|
||||
instance_ = new MemoryManager();
|
||||
}
|
||||
|
||||
MemoryManager *MemoryManager::instance()
|
||||
{
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void MemoryManager::DestroyInstance()
|
||||
{
|
||||
delete instance_;
|
||||
instance_ = nullptr;
|
||||
}
|
||||
|
||||
bool MemoryManager::ShouldFreeMemory()
|
||||
{
|
||||
return (GetAvailableMemory() < minimum_available_memory_);
|
||||
}
|
||||
|
||||
bool MemoryManager::RegisterMemory()
|
||||
{
|
||||
if (ShouldFreeMemory()) {
|
||||
emit FreeMemory();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t MemoryManager::GetAvailableMemory()
|
||||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
MEMORYSTATUSEX status;
|
||||
status.dwLength = sizeof(status);
|
||||
GlobalMemoryStatusEx(&status);
|
||||
return status.ullAvailPhys;
|
||||
#else
|
||||
long pages = sysconf(_SC_AVPHYS_PAGES);
|
||||
long page_size = sysconf(_SC_PAGE_SIZE);
|
||||
return pages * page_size;
|
||||
#endif
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#ifndef MEMORYMANAGER_H
|
||||
#define MEMORYMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class MemoryManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MemoryManager(QObject* parent = nullptr);
|
||||
|
||||
static void CreateInstance();
|
||||
static MemoryManager* instance();
|
||||
static void DestroyInstance();
|
||||
|
||||
static bool ShouldFreeMemory();
|
||||
|
||||
bool RegisterMemory();
|
||||
|
||||
signals:
|
||||
void FreeMemory();
|
||||
|
||||
private:
|
||||
static uint64_t GetAvailableMemory();
|
||||
|
||||
static MemoryManager* instance_;
|
||||
|
||||
static const uint64_t minimum_available_memory_;
|
||||
|
||||
};
|
||||
|
||||
#endif // MEMORYMANAGER_H
|
||||
Reference in New Issue
Block a user