diff --git a/app/codec/CMakeLists.txt b/app/codec/CMakeLists.txt index 606efc295..791500cdd 100644 --- a/app/codec/CMakeLists.txt +++ b/app/codec/CMakeLists.txt @@ -25,8 +25,6 @@ set(OLIVE_SOURCES codec/encoder.cpp codec/frame.h codec/frame.cpp - codec/framepool.h - codec/framepool.cpp codec/samplebuffer.h codec/samplebuffer.cpp codec/waveinput.h diff --git a/app/codec/ffmpeg/CMakeLists.txt b/app/codec/ffmpeg/CMakeLists.txt index 5cac2e216..12267fe76 100644 --- a/app/codec/ffmpeg/CMakeLists.txt +++ b/app/codec/ffmpeg/CMakeLists.txt @@ -23,5 +23,7 @@ set(OLIVE_SOURCES codec/ffmpeg/ffmpegdecoder.cpp codec/ffmpeg/ffmpegencoder.h codec/ffmpeg/ffmpegencoder.cpp + codec/ffmpeg/ffmpegframepool.h + codec/ffmpeg/ffmpegframepool.cpp PARENT_SCOPE ) diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 154b61877..2dcc97622 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -48,11 +48,11 @@ QHash< Stream*, QList > FFmpegDecoder::instances_; QMutex FFmpegDecoder::instance_lock_; // FIXME: Hardcoded, ideally this value is dynamically chosen based on memory restraints -const int FFmpegDecoder::kMaxFrameLife = 5000; +const int FFmpegDecoder::kMaxFrameLife = 2000; FFmpegDecoder::FFmpegDecoder() : scale_ctx_(nullptr), - scale_divider_(-1) + scale_divider_(0) { clear_timer_.setInterval(kMaxFrameLife); clear_timer_.moveToThread(qApp->thread()); @@ -172,7 +172,7 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int &divid int64_t target_ts = Timecode::time_to_timestamp(timecode, time_base_) + start_time_; FFmpegDecoderInstance* working_instance = nullptr; - AVFramePtr return_frame = nullptr; + FFmpegFramePool::ElementPtr return_frame = nullptr; // Find instance do { @@ -276,11 +276,12 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int &divid if (return_frame) { if (divider != scale_divider_) { FreeScaler(); - SetupScaler(divider); + InitScaler(divider); } VideoStream* vs = static_cast(stream().get()); + // Create frame to return FramePtr copy = Frame::Create(); copy->set_video_params(VideoRenderingParams(vs->width() / divider, vs->height() / divider, @@ -289,15 +290,27 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int &divid copy->set_sample_aspect_ratio(aspect_ratio_); copy->allocate(); - // Convert frame to RGBA for the rest of the pipeline + // Align buffer to data/linesize points that can be passed to sws_scale + uint8_t* input_data[4]; + int input_linesize[4]; + + av_image_fill_arrays(input_data, + input_linesize, + reinterpret_cast(return_frame->data()), + src_pix_fmt_, + vs->width(), + vs->height(), + 1); + + // Convert frame to RGB/A for the rest of the pipeline uint8_t* output_data = reinterpret_cast(copy->data()); - int output_linesize = copy->width() * PixelFormat::ChannelCount(native_pix_fmt_) * PixelFormat::BytesPerChannel(native_pix_fmt_); + int output_linesize = copy->width() * PixelFormat::BytesPerPixel(native_pix_fmt_); sws_scale(scale_ctx_, - return_frame->frame()->data, - return_frame->frame()->linesize, + input_data, + input_linesize, 0, - return_frame->frame()->height, + vs->height(), &output_data, &output_linesize); @@ -789,6 +802,8 @@ void FFmpegDecoderInstance::Seek(int64_t timestamp) av_seek_frame(fmt_ctx_, avstream_->index, timestamp, AVSEEK_FLAG_BACKWARD); } +/* OLD UNUSED CODE: Keeping this around in case the code proves useful + void FFmpegDecoder::CacheFrameToDisk(AVFrame *f) { QFile save_frame(GetIndexFilename().append(QString::number(f->pts))); @@ -818,7 +833,7 @@ void FFmpegDecoder::CacheFrameToDisk(AVFrame *f) } // See if we stored this frame in the disk cache - /* + QByteArray frame_loader; if (!got_frame) { QFile compressed_frame(GetIndexFilename().append(QString::number(target_ts))); @@ -841,30 +856,8 @@ void FFmpegDecoder::CacheFrameToDisk(AVFrame *f) got_frame = true; } } - */ } - -/*void FFmpegDecoder::RemoveFirstFromFrameCache() -{ - if (cached_frames_.isEmpty()) { - return; - } - - AVFrame* first = cached_frames_.takeFirst(); - av_frame_free(&first); - cache_at_zero_ = false; -} - -void FFmpegDecoder::RemoveLastFromFrameCache() -{ - if (cached_frames_.isEmpty()) { - return; - } - - AVFrame* last = cached_frames_.takeLast(); - av_frame_free(&last); - cache_at_eof_ = false; -}*/ +*/ void FFmpegDecoderInstance::ClearFrameCache() { @@ -873,7 +866,7 @@ void FFmpegDecoderInstance::ClearFrameCache() cache_at_zero_ = false; } -FramePool::ElementPtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target_ts, bool cache_is_locked) +FFmpegFramePool::ElementPtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target_ts, bool cache_is_locked) { if (!cache_is_locked) { cache_lock_.lock(); @@ -898,7 +891,7 @@ FramePool::ElementPtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target int ret; AVPacket* pkt = av_packet_alloc(); - FramePool::ElementPtr return_frame = nullptr; + FFmpegFramePool::ElementPtr return_frame = nullptr; // Allocate a new frame AVFrameWrapper working_frame; @@ -954,9 +947,23 @@ FramePool::ElementPtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target } else { // Whatever it is, keep this frame in memory for the time being just in case - FramePool::ElementPtr cached = frame_pool_.Get(); + FFmpegFramePool::ElementPtr cached = frame_pool_.Get(working_frame.frame()); + Q_ASSERT(cached); + + // Set timestamp so this frame can be identified later cached->set_timestamp(working_frame.frame()->pts); + // Store frame before just in case + FFmpegFramePool::ElementPtr previous; + if (cached_frames_.isEmpty()) { + previous = nullptr; + } else { + previous = cached_frames_.last(); + } + + // Append this frame and signal to other threads that a new frame has arrived + cached_frames_.append(cached); + cache_wait_cond_.wakeAll(); cache_lock_.unlock(); @@ -965,11 +972,11 @@ FramePool::ElementPtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target return_frame = cached; break; } else if (cached->timestamp() > target_ts) { - if (cached_frames_.isEmpty() && cache_at_zero_) { + if (!previous && cache_at_zero_) { return_frame = cached; break; } else { - return_frame = cached_frames_.at(cached_frames_.size() - 2); + return_frame = previous; break; } } @@ -988,7 +995,7 @@ void FFmpegDecoder::ClearResources() open_ = false; } -void FFmpegDecoder::SetupScaler(const int ÷r) +void FFmpegDecoder::InitScaler(int divider) { VideoStream* vs = static_cast(stream().get()); @@ -1003,10 +1010,10 @@ void FFmpegDecoder::SetupScaler(const int ÷r) nullptr, nullptr); - if (!scale_ctx_) { - Error(QStringLiteral("Failed to allocate SwsContext")); - } else { + if (scale_ctx_) { scale_divider_ = divider; + } else { + scale_divider_ = 0; } } @@ -1015,7 +1022,8 @@ void FFmpegDecoder::FreeScaler() if (scale_ctx_) { sws_freeContext(scale_ctx_); scale_ctx_ = nullptr; - scale_divider_ = -1; + + scale_divider_ = 0; } } @@ -1058,17 +1066,19 @@ bool FFmpegDecoderInstance::CacheIsEmpty() const return cached_frames_.isEmpty(); } -FramePool::ElementPtr FFmpegDecoderInstance::GetFrameFromCache(const int64_t &t) const +FFmpegFramePool::ElementPtr FFmpegDecoderInstance::GetFrameFromCache(const int64_t &t) const { if (t < cached_frames_.first()->timestamp()) { if (cache_at_zero_) { + cached_frames_.first()->access(); return cached_frames_.first(); } } else if (t > cached_frames_.last()->timestamp()) { if (cache_at_eof_) { + cached_frames_.last()->access(); return cached_frames_.last(); } @@ -1076,11 +1086,12 @@ FramePool::ElementPtr FFmpegDecoderInstance::GetFrameFromCache(const int64_t &t) // We already have this frame in the cache, find it for (int i=0;itimestamp() == t // Test for an exact match || (i < cached_frames_.size() - 1 && cached_frames_.at(i+1)->timestamp() > t)) { // Or for this frame to be the "closest" + this_frame->access(); return this_frame; } @@ -1092,6 +1103,7 @@ FramePool::ElementPtr FFmpegDecoderInstance::GetFrameFromCache(const int64_t &t) 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; @@ -1182,12 +1194,19 @@ FFmpegDecoderInstance::FFmpegDecoderInstance(const char *filename, int stream_in return; } - // Create buffer pool - if (avstream_->codecpar->codec_type == AVMEDIA_TYPE_VIDEO - && !frame_pool_.Allocate()) { - qDebug() << "Failed to allocate frame pool"; - ClearResources(); - return; + // Create frame pool + if (avstream_->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + + frame_pool_.SetParams(avstream_->codecpar->width, + avstream_->codecpar->height, + static_cast(avstream_->codecpar->format)); + + if (!frame_pool_.Allocate(64)) { + qDebug() << "Failed to allocate frame pool"; + ClearResources(); + return; + } + } // Store one second in the source's timebase @@ -1208,6 +1227,8 @@ void FFmpegDecoderInstance::ClearResources() { ClearFrameCache(); + frame_pool_.Destroy(); + if (opts_) { av_dict_free(&opts_); opts_ = nullptr; @@ -1222,8 +1243,6 @@ void FFmpegDecoderInstance::ClearResources() avformat_close_input(&fmt_ctx_); fmt_ctx_ = nullptr; } - - frame_pool_.Destroy(); } void FFmpegDecoder::ClearTimerEvent() diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index 746446f1e..a7939aa33 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -35,8 +35,8 @@ extern "C" { #include "audio/sampleformat.h" #include "avframeptr.h" #include "codec/decoder.h" -#include "codec/framepool.h" #include "codec/waveoutput.h" +#include "ffmpegframepool.h" #include "project/item/footage/videostream.h" OLIVE_NAMESPACE_ENTER @@ -56,7 +56,7 @@ public: bool CacheWillContainTime(const int64_t& t) const; bool CacheCouldContainTime(const int64_t& t) const; bool CacheIsEmpty() const; - FramePool::ElementPtr GetFrameFromCache(const int64_t& t) const; + FFmpegFramePool::ElementPtr GetFrameFromCache(const int64_t& t) const; void RemoveFramesBefore(const qint64& t); @@ -65,7 +65,7 @@ public: void ClearFrameCache(); - FramePool::ElementPtr RetrieveFrame(const int64_t &target_ts, bool cache_is_locked); + FFmpegFramePool::ElementPtr RetrieveFrame(const int64_t &target_ts, bool cache_is_locked); /** * @brief Uses the FFmpeg API to retrieve a packet (stored in pkt_) and decode it (stored in frame_) @@ -96,8 +96,8 @@ private: QWaitCondition cache_wait_cond_; QMutex cache_lock_; - QList cached_frames_; - FramePool frame_pool_; + QList cached_frames_; + FFmpegFramePool frame_pool_; int64_t cache_target_time_; @@ -160,13 +160,13 @@ private: void UnconditionalAudioIndex(const QAtomicInt* cancelled); - void CacheFrameToDisk(AVFrame* f); - void ClearResources(); - void SetupScaler(const int& divider); + void InitScaler(int divider); void FreeScaler(); + SwsContext* scale_ctx_; + int scale_divider_; AVPixelFormat src_pix_fmt_; AVPixelFormat ideal_pix_fmt_; PixelFormat::Format native_pix_fmt_; @@ -175,9 +175,6 @@ private: rational aspect_ratio_; int64_t start_time_; - SwsContext* scale_ctx_; - int scale_divider_; - QTimer clear_timer_; FFmpegDecoderInstance* our_instance_; diff --git a/app/codec/ffmpeg/ffmpegframepool.cpp b/app/codec/ffmpeg/ffmpegframepool.cpp new file mode 100644 index 000000000..b3b99cb6f --- /dev/null +++ b/app/codec/ffmpeg/ffmpegframepool.cpp @@ -0,0 +1,95 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "ffmpegframepool.h" + +extern "C" { +#include +} + +OLIVE_NAMESPACE_ENTER + +FFmpegFramePool::FFmpegFramePool() : + width_(0), + height_(0), + format_(AV_PIX_FMT_NONE) +{ +} + +FFmpegFramePool::ElementPtr FFmpegFramePool::Get(AVFrame *copy) +{ + ElementPtr ele = MemoryPool::Get(); + + if (ele) { + av_image_copy_to_buffer(ele->data(), + GetElementSize(), + copy->data, + copy->linesize, + format_, + width_, + height_, + 1); + } + + return ele; +} + +void FFmpegFramePool::SetParams(int width, int height, AVPixelFormat format) +{ + int old_nb_elements; + + if (IsAllocated()) { + old_nb_elements = GetElementCount(); + + Destroy(); + } else { + old_nb_elements = 0; + } + + width_ = width; + height_ = height; + format_ = format; + + if (old_nb_elements) { + // Re-allocate automatically + Allocate(old_nb_elements); + } +} + +size_t FFmpegFramePool::GetElementSize() +{ + if (width_ == 0 || height_ == 0 || format_ == AV_PIX_FMT_NONE) { + return 0; + } + + int buf_sz = av_image_get_buffer_size(static_cast(format_), + width_, + height_, + 1); + + if (buf_sz < 0) { + qDebug() << "Failed to find buffer size:" << buf_sz; + return 0; + } + + return buf_sz; +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/codec/ffmpeg/ffmpegframepool.h b/app/codec/ffmpeg/ffmpegframepool.h new file mode 100644 index 000000000..1239c20c1 --- /dev/null +++ b/app/codec/ffmpeg/ffmpegframepool.h @@ -0,0 +1,53 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef FFMPEGFRAMEPOOL_H +#define FFMPEGFRAMEPOOL_H + +#include "common/memorypool.h" +#include "render/pixelformat.h" +#include "render/videoparams.h" + +OLIVE_NAMESPACE_ENTER + +class FFmpegFramePool : public MemoryPool +{ +public: + FFmpegFramePool(); + + ElementPtr Get(AVFrame* copy); + + void SetParams(int width, int height, AVPixelFormat format); + +protected: + virtual size_t GetElementSize() override; + +private: + int width_; + + int height_; + + AVPixelFormat format_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // FFMPEGFRAMEPOOL_H diff --git a/app/codec/framepool.cpp b/app/codec/framepool.cpp deleted file mode 100644 index d0fceaa14..000000000 --- a/app/codec/framepool.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "framepool.h" - -OLIVE_NAMESPACE_ENTER - -FramePool::FramePool() -{ - data_ = nullptr; -} - -bool FramePool::Allocate(int width, int height, PixelFormat::Format format, int nb_elements) -{ - delete [] data_; - - element_sz_ = width * height * PixelFormat::BytesPerPixel(format); - - if ((data_ = new char[element_sz_ * nb_elements])) { - // Only allocate available array if data allocation succeeded - available_.resize(nb_elements); - available_.fill(true); - - return true; - } else { - available_.clear(); - - return false; - } -} - -void FramePool::Destroy() -{ - delete [] data_; - available_.clear(); -} - -FramePool::~FramePool() -{ - delete [] data_; -} - -FramePool::ElementPtr FramePool::Get() -{ - for (int i=0;i(this, data_ + i * element_sz_); - } - } - - return nullptr; -} - -void FramePool::Release(FramePool::Element *e) -{ - quintptr offs = reinterpret_cast(e->data()); - quintptr start = reinterpret_cast(data_); - - quintptr diff = offs - start; - - int index = diff / element_sz_; - - available_.replace(index, true); -} - -FramePool::Element::Element(FramePool *parent, char *data) -{ - parent_ = parent; - data_ = data; - accessed_ = QDateTime::currentMSecsSinceEpoch(); -} - -FramePool::Element::~Element() -{ - parent_->Release(this); -} - -OLIVE_NAMESPACE_EXIT diff --git a/app/codec/framepool.h b/app/codec/framepool.h deleted file mode 100644 index 86da3bed2..000000000 --- a/app/codec/framepool.h +++ /dev/null @@ -1,97 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef FRAMEPOOL_H -#define FRAMEPOOL_H - -#include -#include - -#include "render/pixelformat.h" - -OLIVE_NAMESPACE_ENTER - -class FramePool -{ -public: - FramePool(); - - ~FramePool(); - - bool Allocate(int width, int height, PixelFormat::Format format, int nb_elements); - - void Destroy(); - - DISABLE_COPY_MOVE(FramePool) - - class Element { - public: - Element(FramePool* parent, char* data); - ~Element(); - - inline char* data() const { - return data_; - } - - inline const int64_t& timestamp() const { - return timestamp_; - } - - inline void set_timestamp(const int64_t& timestamp) { - timestamp_ = timestamp; - } - - inline void access() { - accessed_ = QDateTime::currentMSecsSinceEpoch(); - } - - inline const int64_t& last_accessed() const { - return accessed_; - } - - private: - FramePool* parent_; - - char* data_; - - int64_t timestamp_; - - int64_t accessed_; - - }; - - using ElementPtr = std::shared_ptr; - - ElementPtr Get(); - - void Release(Element* e); - -private: - char* data_; - - int element_sz_; - - QVector available_; - -}; - -OLIVE_NAMESPACE_EXIT - -#endif // FRAMEPOOL_H diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt index 3fd83b995..2b1d61727 100644 --- a/app/common/CMakeLists.txt +++ b/app/common/CMakeLists.txt @@ -32,6 +32,7 @@ set(OLIVE_SOURCES common/flipmodifiers.cpp common/functiontimer.h common/lerp.h + common/memorypool.h common/qtutils.h common/qtutils.cpp common/range.h diff --git a/app/common/memorypool.h b/app/common/memorypool.h new file mode 100644 index 000000000..7b2105f0b --- /dev/null +++ b/app/common/memorypool.h @@ -0,0 +1,165 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef MEMORYPOOL_H +#define MEMORYPOOL_H + +#include +#include +#include + +#include + +#include "common/define.h" + +OLIVE_NAMESPACE_ENTER + +template +class MemoryPool +{ +public: + MemoryPool() { + data_ = nullptr; + } + + ~MemoryPool() { + delete [] data_; + } + + DISABLE_COPY_MOVE(MemoryPool) + + bool Allocate(int nb_elements) { + delete [] data_; + + size_t ele_sz = GetElementSize(); + + if (!ele_sz) { + return false; + } + + if ((data_ = new char[ele_sz * nb_elements])) { + available_.resize(nb_elements); + available_.fill(true); + + return true; + } else { + available_.clear(); + + return false; + } + } + + void Destroy() { + delete [] data_; + data_ = nullptr; + + available_.clear(); + } + + inline bool IsAllocated() const { + return data_; + } + + inline int GetElementCount() const { + return available_.size(); + } + + class Element { + public: + Element(MemoryPool* parent, T* data) { + parent_ = parent; + data_ = data; + accessed_ = QDateTime::currentMSecsSinceEpoch(); + } + + ~Element() { + parent_->Release(this); + } + + inline T* data() const { + return data_; + } + + inline const int64_t& timestamp() const { + return timestamp_; + } + + inline void set_timestamp(const int64_t& timestamp) { + timestamp_ = timestamp; + } + + inline void access() { + accessed_ = QDateTime::currentMSecsSinceEpoch(); + } + + inline const int64_t& last_accessed() const { + return accessed_; + } + + private: + MemoryPool* parent_; + + T* data_; + + int64_t timestamp_; + + int64_t accessed_; + + }; + + using ElementPtr = std::shared_ptr; + + ElementPtr Get() { + for (int i=0;i(this, reinterpret_cast(data_ + i * GetElementSize())); + } + } + + // FIXME: Allocate a new "arena" + return nullptr; + } + + void Release(Element* e) { + quintptr diff = reinterpret_cast(e->data()) - reinterpret_cast(data_); + + int index = diff / GetElementSize(); + + available_.replace(index, true); + } + +protected: + virtual size_t GetElementSize() { + return sizeof(T); + } + +private: + char* data_; + + QVector available_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // MEMORYPOOL_H