decoder: began implementation of a frame pool
This commit is contained in:
@@ -25,6 +25,8 @@ set(OLIVE_SOURCES
|
|||||||
codec/encoder.cpp
|
codec/encoder.cpp
|
||||||
codec/frame.h
|
codec/frame.h
|
||||||
codec/frame.cpp
|
codec/frame.cpp
|
||||||
|
codec/framepool.h
|
||||||
|
codec/framepool.cpp
|
||||||
codec/samplebuffer.h
|
codec/samplebuffer.h
|
||||||
codec/samplebuffer.cpp
|
codec/samplebuffer.cpp
|
||||||
codec/waveinput.h
|
codec/waveinput.h
|
||||||
|
|||||||
@@ -23,7 +23,5 @@ set(OLIVE_SOURCES
|
|||||||
codec/ffmpeg/ffmpegdecoder.cpp
|
codec/ffmpeg/ffmpegdecoder.cpp
|
||||||
codec/ffmpeg/ffmpegencoder.h
|
codec/ffmpeg/ffmpegencoder.h
|
||||||
codec/ffmpeg/ffmpegencoder.cpp
|
codec/ffmpeg/ffmpegencoder.cpp
|
||||||
codec/ffmpeg/ffmpegframecache.h
|
|
||||||
codec/ffmpeg/ffmpegframecache.cpp
|
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,23 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
#ifndef AVFRAMEPTR_H
|
#ifndef AVFRAMEPTR_H
|
||||||
#define AVFRAMEPTR_H
|
#define AVFRAMEPTR_H
|
||||||
|
|
||||||
@@ -17,8 +37,6 @@ class AVFrameWrapper {
|
|||||||
public:
|
public:
|
||||||
AVFrameWrapper() {
|
AVFrameWrapper() {
|
||||||
frame_ = av_frame_alloc();
|
frame_ = av_frame_alloc();
|
||||||
birthtime_ = QDateTime::currentMSecsSinceEpoch();
|
|
||||||
accessed_ = birthtime_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~AVFrameWrapper() {
|
virtual ~AVFrameWrapper() {
|
||||||
@@ -31,25 +49,9 @@ public:
|
|||||||
return frame_;
|
return frame_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const qint64& birthtime() const {
|
|
||||||
return birthtime_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const qint64& last_accessed() const {
|
|
||||||
return accessed_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void access() {
|
|
||||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AVFrame* frame_;
|
AVFrame* frame_;
|
||||||
|
|
||||||
qint64 birthtime_;
|
|
||||||
|
|
||||||
qint64 accessed_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using AVFramePtr = std::shared_ptr<AVFrameWrapper>;
|
using AVFramePtr = std::shared_ptr<AVFrameWrapper>;
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ FFmpegDecoder::FFmpegDecoder() :
|
|||||||
clear_timer_.setInterval(kMaxFrameLife);
|
clear_timer_.setInterval(kMaxFrameLife);
|
||||||
clear_timer_.moveToThread(qApp->thread());
|
clear_timer_.moveToThread(qApp->thread());
|
||||||
connect(&clear_timer_, &QTimer::timeout, this, &FFmpegDecoder::ClearTimerEvent);
|
connect(&clear_timer_, &QTimer::timeout, this, &FFmpegDecoder::ClearTimerEvent);
|
||||||
|
|
||||||
|
av_buffer_pool_init(20, av_buffer_allocz);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFmpegDecoder::~FFmpegDecoder()
|
FFmpegDecoder::~FFmpegDecoder()
|
||||||
@@ -197,39 +199,33 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int &divid
|
|||||||
i->cache_lock()->unlock();
|
i->cache_lock()->unlock();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
} else if (i->CacheWillContainTime(target_ts)) {
|
} else if (i->CacheWillContainTime(target_ts) || i->CacheCouldContainTime(target_ts)) {
|
||||||
|
|
||||||
// Found our instance, allow others to enter the list
|
// Found our instance, allow others to enter the list
|
||||||
list_locker.unlock();
|
list_locker.unlock();
|
||||||
|
|
||||||
do {
|
if (i->IsWorking()) {
|
||||||
// Allow instance to continue to the next frame
|
do {
|
||||||
i->cache_wait_cond()->wait(i->cache_lock());
|
// Allow instance to continue to the next frame
|
||||||
|
i->cache_wait_cond()->wait(i->cache_lock());
|
||||||
|
|
||||||
// See if the cache now contains this frame, if so we'll exit this loop
|
// See if the cache now contains this frame, if so we'll exit this loop
|
||||||
if (i->CacheContainsTime(target_ts)) {
|
if (i->CacheContainsTime(target_ts)) {
|
||||||
return_frame = i->GetFrameFromCache(target_ts);
|
return_frame = i->GetFrameFromCache(target_ts);
|
||||||
|
} else if (!i->IsWorking()) {
|
||||||
|
// Grab this instance and continue it
|
||||||
|
working_instance = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (!return_frame);
|
||||||
|
|
||||||
|
if (working_instance != i) {
|
||||||
|
// We don't unlock if we're continuing this instance ourselves
|
||||||
|
i->cache_lock()->unlock();
|
||||||
}
|
}
|
||||||
} while (!return_frame);
|
} else {
|
||||||
|
working_instance = i;
|
||||||
// Got our frame, allow cache to continue
|
|
||||||
i->cache_lock()->unlock();
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else if (i->CacheCouldContainTime(target_ts)) {
|
|
||||||
|
|
||||||
// Found our instance, allow others to enter the list
|
|
||||||
list_locker.unlock();
|
|
||||||
|
|
||||||
// Wait for this instance to finish working
|
|
||||||
while (i->IsWorking()) {
|
|
||||||
i->cache_wait_cond()->wait(i->cache_lock());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab this instance
|
|
||||||
working_instance = i;
|
|
||||||
|
|
||||||
// We DON'T unlock here, since we'll be starting our own retrieve
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
} else if (i->IsWorking()) {
|
} else if (i->IsWorking()) {
|
||||||
@@ -877,7 +873,7 @@ void FFmpegDecoderInstance::ClearFrameCache()
|
|||||||
cache_at_zero_ = false;
|
cache_at_zero_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFramePtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target_ts, bool cache_is_locked)
|
FramePool::ElementPtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target_ts, bool cache_is_locked)
|
||||||
{
|
{
|
||||||
if (!cache_is_locked) {
|
if (!cache_is_locked) {
|
||||||
cache_lock_.lock();
|
cache_lock_.lock();
|
||||||
@@ -889,9 +885,7 @@ AVFramePtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target_ts, bool c
|
|||||||
cache_target_time_ = target_ts;
|
cache_target_time_ = target_ts;
|
||||||
|
|
||||||
// If the frame wasn't in the frame cache, see if this frame cache is too old to use
|
// If the frame wasn't in the frame cache, see if this frame cache is too old to use
|
||||||
if (cached_frames_.isEmpty()
|
if (!CacheCouldContainTime(target_ts)) {
|
||||||
|| target_ts < cached_frames_.first()->frame()->pts
|
|
||||||
|| target_ts > cached_frames_.last()->frame()->pts + 2*second_ts_) {
|
|
||||||
ClearFrameCache();
|
ClearFrameCache();
|
||||||
|
|
||||||
Seek(seek_ts);
|
Seek(seek_ts);
|
||||||
@@ -904,7 +898,7 @@ AVFramePtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target_ts, bool c
|
|||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
AVPacket* pkt = av_packet_alloc();
|
AVPacket* pkt = av_packet_alloc();
|
||||||
AVFramePtr return_frame = nullptr;
|
FramePool::ElementPtr return_frame = nullptr;
|
||||||
|
|
||||||
// Allocate a new frame
|
// Allocate a new frame
|
||||||
AVFrameWrapper working_frame;
|
AVFrameWrapper working_frame;
|
||||||
@@ -960,16 +954,17 @@ AVFramePtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target_ts, bool c
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Whatever it is, keep this frame in memory for the time being just in case
|
// Whatever it is, keep this frame in memory for the time being just in case
|
||||||
AVFramePtr cached = cached_frames_.append(working_frame.frame());
|
FramePool::ElementPtr cached = frame_pool_.Get();
|
||||||
|
cached->set_timestamp(working_frame.frame()->pts);
|
||||||
|
|
||||||
cache_wait_cond_.wakeAll();
|
cache_wait_cond_.wakeAll();
|
||||||
cache_lock_.unlock();
|
cache_lock_.unlock();
|
||||||
|
|
||||||
// If this is a valid frame, see if this or the frame before it are the one we need
|
// If this is a valid frame, see if this or the frame before it are the one we need
|
||||||
if (cached->frame()->pts == target_ts) {
|
if (cached->timestamp() == target_ts) {
|
||||||
return_frame = cached;
|
return_frame = cached;
|
||||||
break;
|
break;
|
||||||
} else if (cached->frame()->pts > target_ts) {
|
} else if (cached->timestamp() > target_ts) {
|
||||||
if (cached_frames_.isEmpty() && cache_at_zero_) {
|
if (cached_frames_.isEmpty() && cache_at_zero_) {
|
||||||
return_frame = cached;
|
return_frame = cached;
|
||||||
break;
|
break;
|
||||||
@@ -1029,7 +1024,7 @@ int64_t FFmpegDecoderInstance::RangeStart() const
|
|||||||
if (cached_frames_.isEmpty()) {
|
if (cached_frames_.isEmpty()) {
|
||||||
return AV_NOPTS_VALUE;
|
return AV_NOPTS_VALUE;
|
||||||
}
|
}
|
||||||
return cached_frames_.first()->frame()->pts;
|
return cached_frames_.first()->timestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t FFmpegDecoderInstance::RangeEnd() const
|
int64_t FFmpegDecoderInstance::RangeEnd() const
|
||||||
@@ -1037,25 +1032,25 @@ int64_t FFmpegDecoderInstance::RangeEnd() const
|
|||||||
if (cached_frames_.isEmpty()) {
|
if (cached_frames_.isEmpty()) {
|
||||||
return AV_NOPTS_VALUE;
|
return AV_NOPTS_VALUE;
|
||||||
}
|
}
|
||||||
return cached_frames_.last()->frame()->pts;
|
return cached_frames_.last()->timestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegDecoderInstance::CacheContainsTime(const int64_t &t) const
|
bool FFmpegDecoderInstance::CacheContainsTime(const int64_t &t) const
|
||||||
{
|
{
|
||||||
return !cached_frames_.isEmpty()
|
return !cached_frames_.isEmpty()
|
||||||
&& ((RangeStart() <= t && RangeEnd() >= t)
|
&& ((RangeStart() <= t && RangeEnd() >= t)
|
||||||
|| (cache_at_zero_ && t < cached_frames_.first()->frame()->pts)
|
|| (cache_at_zero_ && t < cached_frames_.first()->timestamp())
|
||||||
|| (cache_at_eof_ && t > cached_frames_.last()->frame()->pts));
|
|| (cache_at_eof_ && t > cached_frames_.last()->timestamp()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegDecoderInstance::CacheWillContainTime(const int64_t &t) const
|
bool FFmpegDecoderInstance::CacheWillContainTime(const int64_t &t) const
|
||||||
{
|
{
|
||||||
return !cached_frames_.isEmpty() && t >= cached_frames_.first()->frame()->pts && t <= cache_target_time_;
|
return !cached_frames_.isEmpty() && t >= cached_frames_.first()->timestamp() && t <= cache_target_time_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegDecoderInstance::CacheCouldContainTime(const int64_t &t) const
|
bool FFmpegDecoderInstance::CacheCouldContainTime(const int64_t &t) const
|
||||||
{
|
{
|
||||||
return !cached_frames_.isEmpty() && t >= cached_frames_.first()->frame()->pts && t <= (cache_target_time_ + 2*second_ts_);
|
return !cached_frames_.isEmpty() && t >= cached_frames_.first()->timestamp() && t <= (cache_target_time_ + 2*second_ts_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegDecoderInstance::CacheIsEmpty() const
|
bool FFmpegDecoderInstance::CacheIsEmpty() const
|
||||||
@@ -1063,15 +1058,15 @@ bool FFmpegDecoderInstance::CacheIsEmpty() const
|
|||||||
return cached_frames_.isEmpty();
|
return cached_frames_.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFramePtr FFmpegDecoderInstance::GetFrameFromCache(const int64_t &t) const
|
FramePool::ElementPtr FFmpegDecoderInstance::GetFrameFromCache(const int64_t &t) const
|
||||||
{
|
{
|
||||||
if (t < cached_frames_.first()->frame()->pts) {
|
if (t < cached_frames_.first()->timestamp()) {
|
||||||
|
|
||||||
if (cache_at_zero_) {
|
if (cache_at_zero_) {
|
||||||
return cached_frames_.first();
|
return cached_frames_.first();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (t > cached_frames_.last()->frame()->pts) {
|
} else if (t > cached_frames_.last()->timestamp()) {
|
||||||
|
|
||||||
if (cache_at_eof_) {
|
if (cache_at_eof_) {
|
||||||
return cached_frames_.last();
|
return cached_frames_.last();
|
||||||
@@ -1081,10 +1076,10 @@ AVFramePtr FFmpegDecoderInstance::GetFrameFromCache(const int64_t &t) const
|
|||||||
|
|
||||||
// We already have this frame in the cache, find it
|
// We already have this frame in the cache, find it
|
||||||
for (int i=0;i<cached_frames_.size();i++) {
|
for (int i=0;i<cached_frames_.size();i++) {
|
||||||
AVFramePtr this_frame = cached_frames_.at(i);
|
FramePool::ElementPtr this_frame = cached_frames_.at(i);
|
||||||
|
|
||||||
if (this_frame->frame()->pts == t // Test for an exact match
|
if (this_frame->timestamp() == t // Test for an exact match
|
||||||
|| (i < cached_frames_.size() - 1 && cached_frames_.at(i+1)->frame()->pts > t)) { // Or for this frame to be the "closest"
|
|| (i < cached_frames_.size() - 1 && cached_frames_.at(i+1)->timestamp() > t)) { // Or for this frame to be the "closest"
|
||||||
|
|
||||||
return this_frame;
|
return this_frame;
|
||||||
|
|
||||||
@@ -1097,7 +1092,8 @@ AVFramePtr FFmpegDecoderInstance::GetFrameFromCache(const int64_t &t) const
|
|||||||
|
|
||||||
void FFmpegDecoderInstance::RemoveFramesBefore(const qint64 &t)
|
void FFmpegDecoderInstance::RemoveFramesBefore(const qint64 &t)
|
||||||
{
|
{
|
||||||
if (cached_frames_.remove_old_frames(t)) {
|
while (cached_frames_.size() > 1 && cached_frames_.first()->last_accessed() < t) {
|
||||||
|
cached_frames_.removeFirst();
|
||||||
cache_at_zero_ = false;
|
cache_at_zero_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1186,6 +1182,14 @@ FFmpegDecoderInstance::FFmpegDecoderInstance(const char *filename, int stream_in
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create buffer pool
|
||||||
|
if (avstream_->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
|
||||||
|
&& !frame_pool_.Allocate()) {
|
||||||
|
qDebug() << "Failed to allocate frame pool";
|
||||||
|
ClearResources();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Store one second in the source's timebase
|
// Store one second in the source's timebase
|
||||||
second_ts_ = qRound64(av_q2d(av_inv_q(avstream_->time_base)));
|
second_ts_ = qRound64(av_q2d(av_inv_q(avstream_->time_base)));
|
||||||
}
|
}
|
||||||
@@ -1218,6 +1222,8 @@ void FFmpegDecoderInstance::ClearResources()
|
|||||||
avformat_close_input(&fmt_ctx_);
|
avformat_close_input(&fmt_ctx_);
|
||||||
fmt_ctx_ = nullptr;
|
fmt_ctx_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frame_pool_.Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFmpegDecoder::ClearTimerEvent()
|
void FFmpegDecoder::ClearTimerEvent()
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ extern "C" {
|
|||||||
#include "audio/sampleformat.h"
|
#include "audio/sampleformat.h"
|
||||||
#include "avframeptr.h"
|
#include "avframeptr.h"
|
||||||
#include "codec/decoder.h"
|
#include "codec/decoder.h"
|
||||||
|
#include "codec/framepool.h"
|
||||||
#include "codec/waveoutput.h"
|
#include "codec/waveoutput.h"
|
||||||
#include "ffmpegframecache.h"
|
|
||||||
#include "project/item/footage/videostream.h"
|
#include "project/item/footage/videostream.h"
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
@@ -56,7 +56,7 @@ public:
|
|||||||
bool CacheWillContainTime(const int64_t& t) const;
|
bool CacheWillContainTime(const int64_t& t) const;
|
||||||
bool CacheCouldContainTime(const int64_t& t) const;
|
bool CacheCouldContainTime(const int64_t& t) const;
|
||||||
bool CacheIsEmpty() const;
|
bool CacheIsEmpty() const;
|
||||||
AVFramePtr GetFrameFromCache(const int64_t& t) const;
|
FramePool::ElementPtr GetFrameFromCache(const int64_t& t) const;
|
||||||
|
|
||||||
void RemoveFramesBefore(const qint64& t);
|
void RemoveFramesBefore(const qint64& t);
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ public:
|
|||||||
|
|
||||||
void ClearFrameCache();
|
void ClearFrameCache();
|
||||||
|
|
||||||
AVFramePtr RetrieveFrame(const int64_t &target_ts, bool cache_is_locked);
|
FramePool::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_)
|
* @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_;
|
QWaitCondition cache_wait_cond_;
|
||||||
QMutex cache_lock_;
|
QMutex cache_lock_;
|
||||||
//QList<AVFramePtr> cached_frames_;
|
QList<FramePool::ElementPtr> cached_frames_;
|
||||||
FFmpegFrameCache::Client cached_frames_;
|
FramePool frame_pool_;
|
||||||
|
|
||||||
int64_t cache_target_time_;
|
int64_t cache_target_time_;
|
||||||
|
|
||||||
|
|||||||
@@ -1,144 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
#include "ffmpegframecache.h"
|
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
|
||||||
|
|
||||||
QMutex FFmpegFrameCache::pool_lock_;
|
|
||||||
QLinkedList<AVFramePtr> FFmpegFrameCache::frame_pool_;
|
|
||||||
|
|
||||||
AVFramePtr FFmpegFrameCache::Client::append(int width, int height, int format)
|
|
||||||
{
|
|
||||||
AVFramePtr f = FFmpegFrameCache::Get(width, height, format);
|
|
||||||
|
|
||||||
frames_.append(f);
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVFramePtr FFmpegFrameCache::Client::append(AVFrame *copy)
|
|
||||||
{
|
|
||||||
AVFramePtr f = append(copy->width, copy->height, copy->format);
|
|
||||||
|
|
||||||
av_frame_copy(f->frame(), copy);
|
|
||||||
f->frame()->pts = copy->pts;
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FFmpegFrameCache::Client::clear()
|
|
||||||
{
|
|
||||||
foreach (AVFramePtr cf, frames_) {
|
|
||||||
FFmpegFrameCache::Release(cf);
|
|
||||||
}
|
|
||||||
frames_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FFmpegFrameCache::Client::isEmpty() const
|
|
||||||
{
|
|
||||||
return frames_.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
AVFramePtr FFmpegFrameCache::Client::first() const
|
|
||||||
{
|
|
||||||
return frames_.first();
|
|
||||||
}
|
|
||||||
|
|
||||||
AVFramePtr FFmpegFrameCache::Client::at(int i) const
|
|
||||||
{
|
|
||||||
return frames_.at(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
AVFramePtr FFmpegFrameCache::Client::last() const
|
|
||||||
{
|
|
||||||
return frames_.last();
|
|
||||||
}
|
|
||||||
|
|
||||||
int FFmpegFrameCache::Client::size() const
|
|
||||||
{
|
|
||||||
return frames_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FFmpegFrameCache::Client::accessedFirst()
|
|
||||||
{
|
|
||||||
frames_.first()->access();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FFmpegFrameCache::Client::accessedLast()
|
|
||||||
{
|
|
||||||
frames_.last()->access();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FFmpegFrameCache::Client::accessed(int i)
|
|
||||||
{
|
|
||||||
frames_[i]->access();
|
|
||||||
}
|
|
||||||
|
|
||||||
int FFmpegFrameCache::Client::remove_old_frames(qint64 older_than)
|
|
||||||
{
|
|
||||||
int counter = 0;
|
|
||||||
|
|
||||||
while (frames_.size() > 1 && frames_.first()->last_accessed() < older_than) {
|
|
||||||
FFmpegFrameCache::Release(frames_.takeFirst());
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVFramePtr FFmpegFrameCache::Get(int width, int height, int format)
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&pool_lock_);
|
|
||||||
|
|
||||||
// See if we have a frame matching this description in the pool
|
|
||||||
QLinkedList<AVFramePtr>::iterator i;
|
|
||||||
for (i=frame_pool_.begin();i!=frame_pool_.end();i++) {
|
|
||||||
AVFramePtr f = (*i);
|
|
||||||
|
|
||||||
if (f->frame()->width == width
|
|
||||||
&& f->frame()->height == height
|
|
||||||
&& f->frame()->format == format) {
|
|
||||||
frame_pool_.erase(i);
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise we'll need to create one
|
|
||||||
AVFramePtr f = std::make_shared<AVFrameWrapper>();
|
|
||||||
f->frame()->width = width;
|
|
||||||
f->frame()->height = height;
|
|
||||||
f->frame()->format = format;
|
|
||||||
av_frame_get_buffer(f->frame(), 1);
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FFmpegFrameCache::Release(AVFramePtr f)
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&pool_lock_);
|
|
||||||
|
|
||||||
frame_pool_.append(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
|
||||||
@@ -1,76 +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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
#ifndef FFMPEGFRAMECACHE_H
|
|
||||||
#define FFMPEGFRAMECACHE_H
|
|
||||||
|
|
||||||
#include <QLinkedList>
|
|
||||||
#include <QMutex>
|
|
||||||
|
|
||||||
#include "avframeptr.h"
|
|
||||||
#include "render/videoparams.h"
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
|
||||||
|
|
||||||
class FFmpegFrameCache
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FFmpegFrameCache() = default;
|
|
||||||
|
|
||||||
static AVFramePtr Get(int width, int height, int format);
|
|
||||||
|
|
||||||
static void Release(AVFramePtr f);
|
|
||||||
|
|
||||||
class Client
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Client() = default;
|
|
||||||
|
|
||||||
AVFramePtr append(int width, int height, int format);
|
|
||||||
AVFramePtr append(AVFrame *copy);
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
bool isEmpty() const;
|
|
||||||
AVFramePtr first() const;
|
|
||||||
AVFramePtr at(int i) const;
|
|
||||||
AVFramePtr last() const;
|
|
||||||
int size() const;
|
|
||||||
|
|
||||||
void accessedFirst();
|
|
||||||
void accessedLast();
|
|
||||||
void accessed(int i);
|
|
||||||
|
|
||||||
int remove_old_frames(qint64 older_than);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QList<AVFramePtr> frames_;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
static QMutex pool_lock_;
|
|
||||||
|
|
||||||
static QLinkedList<AVFramePtr> frame_pool_;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
|
||||||
|
|
||||||
#endif // FFMPEGFRAMECACHE_H
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#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<available_.size();i++) {
|
||||||
|
if (available_.at(i)) {
|
||||||
|
// This buffer is available
|
||||||
|
available_.replace(i, false);
|
||||||
|
return std::make_shared<Element>(this, data_ + i * element_sz_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramePool::Release(FramePool::Element *e)
|
||||||
|
{
|
||||||
|
quintptr offs = reinterpret_cast<quintptr>(e->data());
|
||||||
|
quintptr start = reinterpret_cast<quintptr>(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
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#ifndef FRAMEPOOL_H
|
||||||
|
#define FRAMEPOOL_H
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#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<Element>;
|
||||||
|
|
||||||
|
ElementPtr Get();
|
||||||
|
|
||||||
|
void Release(Element* e);
|
||||||
|
|
||||||
|
private:
|
||||||
|
char* data_;
|
||||||
|
|
||||||
|
int element_sz_;
|
||||||
|
|
||||||
|
QVector<bool> available_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|
||||||
|
#endif // FRAMEPOOL_H
|
||||||
Reference in New Issue
Block a user