Merge branch 'decoder-ffpool'

This commit is contained in:
itsmattkc
2020-04-14 02:45:38 +10:00
43 changed files with 1108 additions and 687 deletions
-1
View File
@@ -32,7 +32,6 @@ extern "C" {
#include "codec/frame.h"
#include "codec/samplebuffer.h"
#include "codec/waveoutput.h"
#include "common/constructors.h"
#include "common/rational.h"
#include "project/item/footage/footage.h"
-1
View File
@@ -25,7 +25,6 @@
#include <QString>
#include "codec/frame.h"
#include "common/constructors.h"
#include "common/timerange.h"
#include "render/audioparams.h"
#include "render/videoparams.h"
+3 -2
View File
@@ -16,13 +16,14 @@
set(OLIVE_SOURCES
${OLIVE_SOURCES}
codec/ffmpeg/avframeptr.h
codec/ffmpeg/ffmpegcommon.h
codec/ffmpeg/ffmpegcommon.cpp
codec/ffmpeg/ffmpegdecoder.h
codec/ffmpeg/ffmpegdecoder.cpp
codec/ffmpeg/ffmpegencoder.h
codec/ffmpeg/ffmpegencoder.cpp
codec/ffmpeg/ffmpegframecache.h
codec/ffmpeg/ffmpegframecache.cpp
codec/ffmpeg/ffmpegframepool.h
codec/ffmpeg/ffmpegframepool.cpp
PARENT_SCOPE
)
@@ -18,30 +18,43 @@
***/
#ifndef CONSTRUCTORS_H
#define CONSTRUCTORS_H
#ifndef AVFRAMEPTR_H
#define AVFRAMEPTR_H
extern "C" {
#include <libavcodec/avcodec.h>
}
#include <memory>
#include <QDateTime>
#include "common/define.h"
OLIVE_NAMESPACE_ENTER
/**
* Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we
* use our own functions for portability.
*/
class AVFrameWrapper {
public:
AVFrameWrapper() {
frame_ = av_frame_alloc();
}
#define DISABLE_COPY(Class) \
Class(const Class &) = delete;\
Class &operator=(const Class &) = delete;
virtual ~AVFrameWrapper() {
av_frame_free(&frame_);
}
#define DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
DISABLE_COPY_MOVE(AVFrameWrapper)
#define DISABLE_COPY_MOVE(Class) \
DISABLE_COPY(Class) \
DISABLE_MOVE(Class)
inline AVFrame* frame() const {
return frame_;
}
private:
AVFrame* frame_;
};
using AVFramePtr = std::shared_ptr<AVFrameWrapper>;
OLIVE_NAMESPACE_EXIT
#endif // CONSTRUCTORS_H
#endif // AVFRAMEPTR_H
File diff suppressed because it is too large Load Diff
+84 -31
View File
@@ -30,15 +30,84 @@ extern "C" {
#include <QAtomicInt>
#include <QTimer>
#include <QVector>
#include <QWaitCondition>
#include "audio/sampleformat.h"
#include "avframeptr.h"
#include "codec/decoder.h"
#include "codec/waveoutput.h"
#include "ffmpegframecache.h"
#include "ffmpegframepool.h"
#include "project/item/footage/videostream.h"
OLIVE_NAMESPACE_ENTER
class FFmpegDecoderInstance {
public:
FFmpegDecoderInstance(const char* filename, int stream_index);
virtual ~FFmpegDecoderInstance();
DISABLE_COPY_MOVE(FFmpegDecoderInstance)
bool IsValid() const;
int64_t RangeStart() const;
int64_t RangeEnd() const;
bool CacheContainsTime(const int64_t& t) const;
bool CacheWillContainTime(const int64_t& t) const;
bool CacheCouldContainTime(const int64_t& t) const;
bool CacheIsEmpty() const;
FFmpegFramePool::ElementPtr GetFrameFromCache(const int64_t& t) const;
void RemoveFramesBefore(const qint64& t);
rational sample_aspect_ratio() const;
AVStream* stream() const;
void ClearFrameCache();
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_)
*
* @return
*
* An FFmpeg error code, or >= 0 on success
*/
int GetFrame(AVPacket* pkt, AVFrame* frame);
QMutex* cache_lock();
QWaitCondition* cache_wait_cond();
bool IsWorking() const;
void SetWorking(bool working);
private:
void ClearResources();
void Seek(int64_t timestamp);
AVFormatContext* fmt_ctx_;
AVCodecContext* codec_ctx_;
AVStream* avstream_;
AVDictionary* opts_;
int64_t second_ts_;
QWaitCondition cache_wait_cond_;
QMutex cache_lock_;
QList<FFmpegFramePool::ElementPtr> cached_frames_;
FFmpegFramePool frame_pool_;
int64_t cache_target_time_;
bool is_working_;
bool cache_at_zero_;
bool cache_at_eof_;
};
/**
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
*/
@@ -87,50 +156,34 @@ private:
*/
void FFmpegError(int error_code);
/**
* @brief Uses the FFmpeg API to retrieve a packet (stored in pkt_) and decode it (stored in frame_)
*
* @return
*
* An FFmpeg error code, or >= 0 on success
*/
int GetFrame(AVPacket* pkt, AVFrame* frame);
virtual QString GetIndexFilename() override;
void UnconditionalAudioIndex(const QAtomicInt* cancelled);
void Seek(int64_t timestamp);
void CacheFrameToDisk(AVFrame* f);
void ClearFrameCache();
void ClearResources();
void SetupScaler(const int& divider);
void InitScaler(int divider);
void FreeScaler();
AVFormatContext* fmt_ctx_;
AVCodecContext* codec_ctx_;
AVStream* avstream_;
AVPixelFormat ideal_pix_fmt_;
PixelFormat::Format native_pix_fmt_;
SwsContext* scale_ctx_;
int scale_divider_;
AVPixelFormat src_pix_fmt_;
AVPixelFormat ideal_pix_fmt_;
PixelFormat::Format native_pix_fmt_;
FFmpegFrameCache::Client cached_frames_;
bool cache_at_zero_;
bool cache_at_eof_;
int64_t second_ts_;
AVDictionary* opts_;
rational time_base_;
rational aspect_ratio_;
int64_t start_time_;
QTimer clear_timer_;
FFmpegDecoderInstance* our_instance_;
static QHash< Stream*, QList<FFmpegDecoderInstance*> > instances_;
static QMutex instance_lock_;
static const int kMaxFrameLife;
private slots:
void ClearTimerEvent();
-123
View File
@@ -1,123 +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_;
QList<Frame*> FFmpegFrameCache::frame_pool_;
Frame *FFmpegFrameCache::Client::append(const VideoRenderingParams& params)
{
Frame* f = FFmpegFrameCache::Get(params);
frames_.append({f, QDateTime::currentMSecsSinceEpoch()});
return f;
}
void FFmpegFrameCache::Client::clear()
{
foreach (const CachedFrame& cf, frames_) {
FFmpegFrameCache::Release(cf.frame);
}
frames_.clear();
}
bool FFmpegFrameCache::Client::isEmpty() const
{
return frames_.isEmpty();
}
Frame *FFmpegFrameCache::Client::first() const
{
return frames_.first().frame;
}
Frame *FFmpegFrameCache::Client::at(int i) const
{
return frames_.at(i).frame;
}
Frame *FFmpegFrameCache::Client::last() const
{
return frames_.last().frame;
}
int FFmpegFrameCache::Client::size() const
{
return frames_.size();
}
void FFmpegFrameCache::Client::accessedFirst()
{
frames_.first().accessed = QDateTime::currentMSecsSinceEpoch();
}
void FFmpegFrameCache::Client::accessedLast()
{
frames_.last().accessed = QDateTime::currentMSecsSinceEpoch();
}
void FFmpegFrameCache::Client::accessed(int i)
{
frames_[i].accessed = QDateTime::currentMSecsSinceEpoch();
}
void FFmpegFrameCache::Client::remove_old_frames(qint64 older_than)
{
while (!frames_.isEmpty() && frames_.first().accessed < older_than) {
FFmpegFrameCache::Release(frames_.takeFirst().frame);
}
}
Frame* FFmpegFrameCache::Get(const VideoRenderingParams &params)
{
QMutexLocker locker(&pool_lock_);
// See if we have a frame matching this description in the pool
for (int i=0;i<frame_pool_.size();i++) {
if (frame_pool_.at(i)->width() == params.width()
&& frame_pool_.at(i)->height() == params.height()
&& frame_pool_.at(i)->format() == params.format()) {
return frame_pool_.takeAt(i);
}
}
// Otherwise we'll need to create one
Frame* f = new Frame();
f->set_video_params(params);
f->allocate();
return f;
}
void FFmpegFrameCache::Release(Frame *f)
{
QMutexLocker locker(&pool_lock_);
frame_pool_.append(f);
}
OLIVE_NAMESPACE_EXIT
-80
View File
@@ -1,80 +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 <QList>
#include <QMutex>
#include "codec/frame.h"
#include "render/videoparams.h"
OLIVE_NAMESPACE_ENTER
class FFmpegFrameCache
{
public:
FFmpegFrameCache() = default;
static Frame* Get(const VideoRenderingParams& params);
static void Release(Frame* f);
class Client
{
public:
Client() = default;
Frame* append(const VideoRenderingParams &params);
void clear();
bool isEmpty() const;
Frame* first() const;
Frame* at(int i) const;
Frame* last() const;
int size() const;
void accessedFirst();
void accessedLast();
void accessed(int i);
void remove_old_frames(qint64 older_than);
private:
struct CachedFrame {
Frame* frame;
qint64 accessed;
};
QList<CachedFrame> frames_;
};
private:
static QMutex pool_lock_;
static QList<Frame*> frame_pool_;
};
OLIVE_NAMESPACE_EXIT
#endif // FFMPEGFRAMECACHE_H
+95
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#include "ffmpegframepool.h"
extern "C" {
#include <libavutil/imgutils.h>
}
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<AVPixelFormat>(format_),
width_,
height_,
1);
if (buf_sz < 0) {
qDebug() << "Failed to find buffer size:" << buf_sz;
return 0;
}
return buf_sz;
}
OLIVE_NAMESPACE_EXIT
+53
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#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<uint8_t>
{
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
-1
View File
@@ -23,7 +23,6 @@
#include <memory>
#include "common/constructors.h"
#include "render/audioparams.h"
OLIVE_NAMESPACE_ENTER
-1
View File
@@ -23,7 +23,6 @@
#include <QFile>
#include "common/constructors.h"
#include "render/audioparams.h"
OLIVE_NAMESPACE_ENTER
-1
View File
@@ -25,7 +25,6 @@
#include <QFile>
#include "audio/sampleformat.h"
#include "common/constructors.h"
#include "render/audioparams.h"
OLIVE_NAMESPACE_ENTER
+1 -1
View File
@@ -21,7 +21,6 @@ set(OLIVE_SOURCES
common/cancelableobject.h
common/channellayout.h
common/clamp.h
common/constructors.h
common/crashhandler.h
common/crashhandler.cpp
common/debug.h
@@ -33,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
+17
View File
@@ -49,4 +49,21 @@ OLIVE_NAMESPACE_EXIT
#define OLIVE_NS_ARG(x, y) QArgument<OLIVE_NAMESPACE::x>(MACRO_VAL_AS_STR(OLIVE_NAMESPACE) "::" #x, y)
/**
* Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we
* use our own functions for portability.
*/
#define DISABLE_COPY(Class) \
Class(const Class &) = delete;\
Class &operator=(const Class &) = delete;
#define DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define DISABLE_COPY_MOVE(Class) \
DISABLE_COPY(Class) \
DISABLE_MOVE(Class)
#endif // OLIVECOMMONDEFINE_H
+165
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#ifndef MEMORYPOOL_H
#define MEMORYPOOL_H
#include <memory>
#include <QDateTime>
#include <stdint.h>
#include <QDebug>
#include "common/define.h"
OLIVE_NAMESPACE_ENTER
template <typename T>
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<Element>;
ElementPtr 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, reinterpret_cast<T*>(data_ + i * GetElementSize()));
}
}
// FIXME: Allocate a new "arena"
return nullptr;
}
void Release(Element* e) {
quintptr diff = reinterpret_cast<quintptr>(e->data()) - reinterpret_cast<quintptr>(data_);
int index = diff / GetElementSize();
available_.replace(index, true);
}
protected:
virtual size_t GetElementSize() {
return sizeof(T);
}
private:
char* data_;
QVector<bool> available_;
};
OLIVE_NAMESPACE_EXIT
#endif // MEMORYPOOL_H
-1
View File
@@ -24,7 +24,6 @@
#include <QList>
#include <QDateTime>
#include "common/constructors.h"
#include "common/rational.h"
#include "project/item/item.h"
#include "project/item/footage/audiostream.h"
-1
View File
@@ -28,7 +28,6 @@
#include <QString>
#include <QXmlStreamWriter>
#include "common/constructors.h"
#include "common/threadedobject.h"
#include "common/xmlutils.h"
#include "node/param.h"
+1 -1
View File
@@ -46,7 +46,7 @@ bool AudioBackend::InitInternal()
// Initiate one thread per CPU core
for (int i=0;i<threads().size();i++) {
// Create one processor object for each thread
AudioWorker* processor = new AudioWorker(decoder_cache(), &copy_map_);
AudioWorker* processor = new AudioWorker(&copy_map_);
processor->SetParameters(params());
processors_.append(processor);
}
+2 -2
View File
@@ -22,8 +22,8 @@
OLIVE_NAMESPACE_ENTER
AudioWorker::AudioWorker(DecoderCache* decoder_cache, QHash<Node *, Node *> *copy_map, QObject *parent) :
AudioRenderWorker(decoder_cache, copy_map, parent)
AudioWorker::AudioWorker(QHash<Node *, Node *> *copy_map, QObject *parent) :
AudioRenderWorker(copy_map, parent)
{
}
+1 -1
View File
@@ -28,7 +28,7 @@ OLIVE_NAMESPACE_ENTER
class AudioWorker : public AudioRenderWorker
{
public:
AudioWorker(DecoderCache* decoder_cache, QHash<Node*, Node*>* copy_map, QObject* parent = nullptr);
AudioWorker(QHash<Node*, Node*>* copy_map, QObject* parent = nullptr);
protected:
virtual void FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table) override;
+2 -2
View File
@@ -30,8 +30,8 @@
OLIVE_NAMESPACE_ENTER
AudioRenderWorker::AudioRenderWorker(DecoderCache* decoder_cache, QHash<Node *, Node *> *copy_map, QObject *parent) :
RenderWorker(decoder_cache, parent),
AudioRenderWorker::AudioRenderWorker(QHash<Node *, Node *> *copy_map, QObject *parent) :
RenderWorker(parent),
copy_map_(copy_map)
{
}
+1 -1
View File
@@ -29,7 +29,7 @@ class AudioRenderWorker : public RenderWorker
{
Q_OBJECT
public:
AudioRenderWorker(DecoderCache* decoder_cache, QHash<Node*, Node*>* copy_map, QObject* parent = nullptr);
AudioRenderWorker(QHash<Node*, Node*>* copy_map, QObject* parent = nullptr);
void SetParameters(const AudioRenderingParams& audio_params);
+1 -1
View File
@@ -63,7 +63,7 @@ bool OpenGLBackend::InitInternal()
// Initiate one thread per CPU core
for (int i=0;i<threads().size();i++) {
// Create one processor object for each thread
OpenGLWorker* processor = new OpenGLWorker(frame_cache(), decoder_cache());
OpenGLWorker* processor = new OpenGLWorker(frame_cache());
processor->SetParameters(params());
processors_.append(processor);
@@ -23,7 +23,6 @@
#include <QOpenGLContext>
#include "common/constructors.h"
#include "opengltexture.h"
OLIVE_NAMESPACE_ENTER
+1 -8
View File
@@ -67,7 +67,7 @@ bool OpenGLProxy::Init()
return true;
}
void OpenGLProxy::FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table)
void OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, const TimeRange &range, NodeValueTable* table)
{
// Ensure stream is video or image type
if (stream->type() != Stream::kVideo && stream->type() != Stream::kImage) {
@@ -105,13 +105,6 @@ void OpenGLProxy::FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeR
ColorManager::OCIOMethod ocio_method = ColorManager::GetOCIOMethodForMode(video_params_.mode());
FramePtr frame = decoder->RetrieveVideo(range.in(), video_params_.divider());
if (!frame) {
// Nothing to be done
return;
}
// OCIO's CPU conversion is more accurate, so for online we render on CPU but offline we render GPU
if (ocio_method == ColorManager::kOCIOAccurate) {
bool has_alpha = PixelFormat::FormatHasAlphaChannel(frame->format());
+1 -1
View File
@@ -63,7 +63,7 @@ public:
void Close();
void FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table);
void FrameToValue(FramePtr frame, StreamPtr stream, const TimeRange &range, NodeValueTable* table);
void RunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable* output_params);
@@ -25,7 +25,6 @@
#include <QOpenGLFunctions>
#include "codec/frame.h"
#include "common/constructors.h"
#include "render/pixelformat.h"
OLIVE_NAMESPACE_ENTER
+7 -3
View File
@@ -31,14 +31,18 @@
OLIVE_NAMESPACE_ENTER
OpenGLWorker::OpenGLWorker(VideoRenderFrameCache *frame_cache, DecoderCache* decoder_cache, QObject *parent) :
VideoRenderWorker(frame_cache, decoder_cache, parent)
OpenGLWorker::OpenGLWorker(VideoRenderFrameCache *frame_cache, QObject *parent) :
VideoRenderWorker(frame_cache, parent)
{
}
void OpenGLWorker::FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable *table)
{
emit RequestFrameToValue(decoder, stream, range, table);
FramePtr frame = decoder->RetrieveVideo(range.in(), video_params().divider());
if (frame) {
emit RequestFrameToValue(frame, stream, range, table);
}
}
void OpenGLWorker::RunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable *output_params)
+2 -2
View File
@@ -34,11 +34,11 @@ OLIVE_NAMESPACE_ENTER
class OpenGLWorker : public VideoRenderWorker {
Q_OBJECT
public:
OpenGLWorker(VideoRenderFrameCache* frame_cache, DecoderCache *decoder_cache,
OpenGLWorker(VideoRenderFrameCache* frame_cache,
QObject* parent = nullptr);
signals:
void RequestFrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table);
void RequestFrameToValue(FramePtr frame, StreamPtr stream, const TimeRange &range, NodeValueTable* table);
void RequestRunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable* output_params);
-7
View File
@@ -105,8 +105,6 @@ void RenderBackend::Close()
threads_.clear();
processors_.clear();
decoder_cache_.Clear();
}
const QString &RenderBackend::GetError() const
@@ -404,11 +402,6 @@ void RenderBackend::SetWorkerBusyState(RenderWorker *worker, bool busy)
processor_busy_state_.replace(processors_.indexOf(worker), busy);
}
DecoderCache *RenderBackend::decoder_cache()
{
return &decoder_cache_;
}
bool RenderBackend::AllProcessorsAreAvailable() const
{
foreach (bool busy, processor_busy_state_) {
-5
View File
@@ -23,7 +23,6 @@
#include <QLinkedList>
#include "common/constructors.h"
#include "dialog/rendercancel/rendercancel.h"
#include "decodercache.h"
#include "node/graph.h"
@@ -118,14 +117,10 @@ protected:
bool WorkerIsBusy(RenderWorker* worker) const;
void SetWorkerBusyState(RenderWorker* worker, bool busy);
DecoderCache* decoder_cache();
TimeRangeList cache_queue_;
QVector<RenderWorker*> processors_;
DecoderCache decoder_cache_;
bool compiled_;
QHash<TimeRange, qint64> render_job_info_;
+6 -7
View File
@@ -26,10 +26,9 @@
OLIVE_NAMESPACE_ENTER
RenderWorker::RenderWorker(DecoderCache *decoder_cache, QObject *parent) :
RenderWorker::RenderWorker(QObject *parent) :
QObject(parent),
started_(false),
decoder_cache_(decoder_cache)
started_(false)
{
}
@@ -50,6 +49,8 @@ void RenderWorker::Close()
{
CloseInternal();
decoder_cache_.Clear();
started_ = false;
}
@@ -82,11 +83,9 @@ StreamPtr RenderWorker::ResolveStreamFromInput(NodeInput *input)
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());
DecoderPtr decoder = decoder_cache_.Get(stream.get());
if (!decoder && stream) {
// Create a new Decoder here
@@ -94,7 +93,7 @@ DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream)
decoder->set_stream(stream);
if (decoder->Open()) {
decoder_cache_->Add(stream.get(), decoder);
decoder_cache_.Add(stream.get(), decoder);
} else {
decoder = nullptr;
qWarning() << "Failed to open decoder for" << stream->footage()->filename() << "::" << stream->index();
+2 -3
View File
@@ -23,7 +23,6 @@
#include <QObject>
#include "common/constructors.h"
#include "decodercache.h"
#include "node/node.h"
#include "node/output/track/track.h"
@@ -35,7 +34,7 @@ class RenderWorker : public QObject, public NodeTraverser
{
Q_OBJECT
public:
RenderWorker(DecoderCache* decoder_cache, QObject* parent = nullptr);
RenderWorker(QObject* parent = nullptr);
bool Init();
@@ -77,7 +76,7 @@ protected:
private:
bool started_;
DecoderCache* decoder_cache_;
DecoderCache decoder_cache_;
NodeDependency path_;
+21 -24
View File
@@ -40,7 +40,8 @@ VideoRenderBackend::VideoRenderBackend(QObject *parent) :
RenderBackend(parent),
operating_mode_(VideoRenderWorker::kHashRenderCache),
only_signal_last_frame_requested_(true),
limit_caching_(true)
limit_caching_(true),
pop_toggle_(false)
{
connect(DiskManager::instance(), &DiskManager::DeletedFrame, this, &VideoRenderBackend::FrameRemovedFromDiskCache);
}
@@ -164,7 +165,7 @@ VideoRenderFrameCache *VideoRenderBackend::frame_cache()
QString VideoRenderBackend::GetCachedFrame(const rational &time)
{
last_time_requested_ = time;
UpdateLastRequestedTime(time);
if (viewer_node() == nullptr) {
// Nothing is connected - nothing to show or render
@@ -181,8 +182,6 @@ QString VideoRenderBackend::GetCachedFrame(const rational &time)
return nullptr;
}
Requeue();
// Find frame in map
QByteArray frame_hash = frame_cache_.TimeToHash(time);
@@ -195,6 +194,13 @@ QString VideoRenderBackend::GetCachedFrame(const rational &time)
return QString();
}
void VideoRenderBackend::UpdateLastRequestedTime(const rational &time)
{
last_time_requested_ = time;
Requeue();
}
NodeInput *VideoRenderBackend::GetDependentInput()
{
return viewer_node()->texture_input();
@@ -208,44 +214,35 @@ bool VideoRenderBackend::CanRender()
TimeRange VideoRenderBackend::PopNextFrameFromQueue()
{
// Try to find the frame that's closest to the last time requested (the playhead)
rational earliest_allowed_time = (pop_toggle_) ? 0 : last_time_requested_;
pop_toggle_ = !pop_toggle_;
// Set up playhead frame range to see if the queue contains this frame precisely
TimeRange test_range(last_time_requested_, last_time_requested_ + params_.time_base());
TimeRange test_range(earliest_allowed_time, earliest_allowed_time + params_.time_base());
// Use this variable to find the closest frame in the range
rational closest_time = -1;
rational closest_time = RATIONAL_MAX;
foreach (const TimeRange& range_here, cache_queue_) {
if (range_here.OverlapsWith(test_range, false, false)) {
closest_time = -1;
closest_time = RATIONAL_MAX;
break;
}
for (int j=0;j<2;j++) {
rational compare;
if (range_here.in() >= earliest_allowed_time) {
rational frame_here = Timecode::snap_time_to_timebase(range_here.in(), params_.time_base());
if (j == 0) {
compare = Timecode::snap_time_to_timebase(range_here.in(), params_.time_base());
if (compare > range_here.in()) {
compare -= params_.time_base();
}
} else {
compare = Timecode::snap_time_to_timebase(range_here.out(), params_.time_base());
if (compare >= range_here.out()) {
compare -= params_.time_base();
}
if (frame_here > range_here.in()) {
frame_here = qMax(rational(), frame_here - params_.time_base());
}
if (closest_time < 0
|| qAbs(compare - last_time_requested_) < qAbs(closest_time - last_time_requested_)) {
closest_time = compare;
}
closest_time = qMin(closest_time, frame_here);
}
}
TimeRange frame_range;
if (closest_time == -1) {
if (closest_time == RATIONAL_MAX) {
frame_range = test_range;
} else {
frame_range = TimeRange(closest_time, closest_time + params_.time_base());
+4
View File
@@ -66,6 +66,8 @@ public:
QString GetCachedFrame(const rational& time);
void UpdateLastRequestedTime(const rational& time);
VideoRenderFrameCache* frame_cache();
const VideoRenderingParams& params() const;
@@ -129,6 +131,8 @@ private:
bool limit_caching_;
bool pop_toggle_;
private slots:
void ThreadCompletedDownload(NodeDependency dep, qint64 job_time, QByteArray hash, bool texture_existed);
void ThreadSkippedFrame(NodeDependency dep, qint64 job_time, QByteArray hash);
+2 -2
View File
@@ -34,8 +34,8 @@
OLIVE_NAMESPACE_ENTER
VideoRenderWorker::VideoRenderWorker(VideoRenderFrameCache *frame_cache, DecoderCache* decoder_cache, QObject *parent) :
RenderWorker(decoder_cache, parent),
VideoRenderWorker::VideoRenderWorker(VideoRenderFrameCache *frame_cache, QObject *parent) :
RenderWorker(parent),
frame_cache_(frame_cache),
operating_mode_(kHashRenderCache)
{
+1 -1
View File
@@ -63,7 +63,7 @@ public:
kHashRenderCache = 0x7
};
VideoRenderWorker(VideoRenderFrameCache* frame_cache, DecoderCache *decoder_cache, QObject* parent = nullptr);
VideoRenderWorker(VideoRenderFrameCache* frame_cache, QObject* parent = nullptr);
void SetParameters(const VideoRenderingParams& video_params);
-1
View File
@@ -25,7 +25,6 @@
namespace OCIO = OCIO_NAMESPACE::v1;
#include "codec/frame.h"
#include "common/constructors.h"
#include "render/color.h"
OLIVE_NAMESPACE_ENTER
-2
View File
@@ -42,9 +42,7 @@ void ConformTask::Action()
connect(decoder.get(), &Decoder::IndexProgress, this, &ConformTask::ProgressChanged);
decoder->Open();
decoder->Conform(params_, &IsCancelled());
decoder->Close();
emit Succeeded();
}
-2
View File
@@ -42,9 +42,7 @@ void IndexTask::Action()
connect(decoder.get(), &Decoder::IndexProgress, this, &IndexTask::ProgressChanged);
decoder->Open();
decoder->Index(&IsCancelled());
decoder->Close();
emit Succeeded();
}
-1
View File
@@ -24,7 +24,6 @@
#include <QVector>
#include <QUndoCommand>
#include "common/constructors.h"
#include "task/task.h"
OLIVE_NAMESPACE_ENTER
+1
View File
@@ -320,6 +320,7 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time)
{
if (!GetConnectedNode() || time >= GetConnectedNode()->Length()) {
main_gl_widget()->SetImage(QString());
video_renderer_->UpdateLastRequestedTime(time);
} else {
QString frame_fn = video_renderer_->GetCachedFrame(time);