fixed various segfaults in decoder and renderer

This commit is contained in:
itsmattkc
2020-05-18 02:40:58 +10:00
parent a0f2035240
commit 814bbe8c22
22 changed files with 255 additions and 193 deletions
+3 -1
View File
@@ -1463,7 +1463,9 @@ FFmpegDecoderInstance::FFmpegDecoderInstance(const char *filename, int stream_in
// Open codec
error_code = avcodec_open2(codec_ctx_, codec, &opts_);
if (error_code < 0) {
qDebug() << "Failed to open codec" << codec->id << error_code;
char buf[50];
av_strerror(error_code, buf, 50);
qDebug() << "Failed to open codec" << codec->id << error_code << buf;
ClearResources();
return;
}
+32 -11
View File
@@ -57,6 +57,7 @@ public:
*/
MemoryPool(int element_count) {
element_count_ = element_count;
ignore_arena_empty_signal_ = false;
}
/**
@@ -65,6 +66,7 @@ public:
* Deletes all arenas.
*/
virtual ~MemoryPool() {
ignore_arena_empty_signal_ = true;
qDeleteAll(arenas_);
}
@@ -112,7 +114,7 @@ public:
* Automatically releases this element's memory back to the arena it was retrieved from.
*/
~Element() {
parent_->Release(this);
release();
}
DISABLE_COPY_MOVE(Element)
@@ -151,6 +153,13 @@ public:
return accessed_;
}
void release() {
if (data_) {
parent_->Release(this);
data_ = nullptr;
}
}
private:
Arena* parent_;
@@ -176,11 +185,13 @@ public:
Arena(MemoryPool* parent) {
parent_ = parent;
data_ = nullptr;
use_count_ = 0;
}
~Arena() {
// FIXME: Invalidate elements that have been lent out?
QLinkedList<Element*> copy = lent_elements_;
foreach (Element* e, copy) {
e->release();
}
delete [] data_;
}
@@ -197,8 +208,11 @@ public:
if (available_.at(i)) {
// This buffer is available
available_.replace(i, false);
use_count_++;
return std::make_shared<Element>(this, reinterpret_cast<T*>(data_ + i * element_sz_));
ElementPtr e = std::make_shared<Element>(this,
reinterpret_cast<T*>(data_ + i * element_sz_));
lent_elements_.append(e.get());
return e;
}
}
@@ -215,17 +229,18 @@ public:
int index = diff / element_sz_;
available_.replace(index, true);
use_count_--;
if (!use_count_) {
lent_elements_.removeOne(e);
if (lent_elements_.isEmpty()) {
locker.unlock();
parent_->ArenaIsEmpty(this);
}
}
const int& GetUsageCount() {
int GetUsageCount() {
QMutexLocker locker(&lock_);
return use_count_;
return lent_elements_.size();
}
bool Allocate(size_t ele_sz, size_t nb_elements) {
@@ -242,7 +257,6 @@ public:
return true;
} else {
available_.clear();
data_ = nullptr;
return false;
}
@@ -267,7 +281,7 @@ public:
size_t element_sz_;
int use_count_;
QLinkedList<Element*> lent_elements_;
};
@@ -317,6 +331,11 @@ public:
}
void ArenaIsEmpty(Arena* a) {
// FIXME: Does this need to be mutexed?
if (ignore_arena_empty_signal_) {
return;
}
QMutexLocker locker(&lock_);
if (!a->GetUsageCount()) {
@@ -343,6 +362,8 @@ private:
QMutex lock_;
bool ignore_arena_empty_signal_;
};
OLIVE_NAMESPACE_EXIT
+3 -2
View File
@@ -36,8 +36,9 @@ OLIVE_NAMESPACE_ENTER
/**
* @brief Retrieves the width of a string according to certain QFontMetrics
*
* QFontMetrics::width() has been deprecatd in favor of QFontMetrics::horizontalAdvance(), but the latter was only
* introduced in 5.11+. This function wraps the latter for 5.11+ and the former for earlier.
* QFontMetrics::width() has been deprecatd in favor of QFontMetrics::horizontalAdvance(), but the
* latter was only introduced in 5.11+. This function wraps the latter for 5.11+ and the former for
* earlier.
*/
int QFontMetricsWidth(QFontMetrics fm, const QString& s);
+3 -2
View File
@@ -32,8 +32,8 @@ TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, Node
track_input_(track_input),
type_(type)
{
connect(track_input, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected);
connect(track_input, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected);
connect(track_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected);
connect(track_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected);
}
const Timeline::TrackType &TrackList::type() const
@@ -192,6 +192,7 @@ void TrackList::TrackConnected(NodeEdgePtr edge)
connect(connected_track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
connect(connected_track, &TrackOutput::TrackHeightChanged, this, &TrackList::TrackHeightChangedSlot);
qDebug() << "Setting track type" << connected_track << type_;
connected_track->set_track_type(type_);
emit TrackListChanged();
-1
View File
@@ -30,7 +30,6 @@ set(OLIVE_SOURCES
render/backend/renderworker.h
render/backend/renderworker.cpp
render/backend/rendercache.h
render/backend/colorprocessorcache.h
render/backend/decodercache.h
+1 -2
View File
@@ -23,11 +23,10 @@
#include "project/item/footage/stream.h"
#include "render/colorprocessor.h"
#include "rendercache.h"
OLIVE_NAMESPACE_ENTER
using ColorProcessorCache = RenderCache<QString, ColorProcessorPtr>;
using ColorProcessorCache = QHash<QString, ColorProcessorPtr>;
OLIVE_NAMESPACE_EXIT
+1 -12
View File
@@ -23,21 +23,10 @@
#include "codec/decoder.h"
#include "project/item/footage/stream.h"
#include "rendercache.h"
OLIVE_NAMESPACE_ENTER
class DecoderCache : public RenderCache<Stream*, DecoderPtr>
{
public:
DecoderCache() = default;
QMutex* lock() {return &lock_;}
private:
QMutex lock_;
};
using DecoderCache = QHash<Stream*, DecoderPtr>;
OLIVE_NAMESPACE_EXIT
+1 -1
View File
@@ -53,7 +53,7 @@ RenderWorker *OpenGLBackend::CreateNewWorker()
}
}
return new OpenGLWorker(proxy_);
return new OpenGLWorker(this, proxy_);
}
void OpenGLBackend::ClearProxy()
@@ -22,7 +22,6 @@
#define OPENGLCOLORPROCESSOR_H
#include "openglshader.h"
#include "render/backend/rendercache.h"
#include "render/colorprocessor.h"
OLIVE_NAMESPACE_ENTER
@@ -63,7 +62,7 @@ private slots:
};
using OpenGLColorProcessorCache = RenderCache<QString, OpenGLColorProcessorPtr>;
using OpenGLColorProcessorCache = QHash<QString, OpenGLColorProcessorPtr>;
OLIVE_NAMESPACE_EXIT
+5 -5
View File
@@ -74,13 +74,13 @@ NodeValue OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, const Vide
// Set up OCIO context
QString colorspace_match = video_stream->get_colorspace_match_string();
OpenGLColorProcessorPtr color_processor = std::static_pointer_cast<OpenGLColorProcessor>(color_cache_.Get(colorspace_match));
OpenGLColorProcessorPtr color_processor = std::static_pointer_cast<OpenGLColorProcessor>(color_cache_.value(colorspace_match));
if (!color_processor) {
color_processor = OpenGLColorProcessor::Create(video_stream->footage()->project()->color_manager(),
video_stream->colorspace(),
video_stream->footage()->project()->color_manager()->GetReferenceColorSpace());
color_cache_.Add(colorspace_match, color_processor);
color_cache_.insert(colorspace_match, color_processor);
}
ColorManager::OCIOMethod ocio_method = ColorManager::GetOCIOMethodForMode(params.mode());
@@ -172,7 +172,7 @@ NodeValue OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, const Vide
void OpenGLProxy::Close()
{
shader_cache_.Clear();
shader_cache_.clear();
buffer_.Destroy();
copy_pipeline_ = nullptr;
functions_ = nullptr;
@@ -186,7 +186,7 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node,
NodeValueTable &output_params,
const VideoRenderingParams& params)
{
OpenGLShaderPtr shader = shader_cache_.Get(node->ShaderID(input_params));
OpenGLShaderPtr shader = shader_cache_.value(node->ShaderID(input_params));
if (!shader) {
// Since we have shader code, compile it now
@@ -208,7 +208,7 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node,
shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_code);
shader->link();
shader_cache_.Add(node->id(), shader);
shader_cache_.insert(node->id(), shader);
}
// Create the output textures
@@ -24,11 +24,10 @@
#include <QString>
#include "openglshader.h"
#include "render/backend/rendercache.h"
OLIVE_NAMESPACE_ENTER
using OpenGLShaderCache = RenderCache<QString, OpenGLShaderPtr>;
using OpenGLShaderCache = QHash<QString, OpenGLShaderPtr>;
OLIVE_NAMESPACE_EXIT
+2 -1
View File
@@ -22,7 +22,8 @@
OLIVE_NAMESPACE_ENTER
OpenGLWorker::OpenGLWorker(OpenGLProxy* proxy) :
OpenGLWorker::OpenGLWorker(RenderBackend *parent, OpenGLProxy* proxy) :
RenderWorker(parent),
proxy_(proxy)
{
}
+1 -1
View File
@@ -29,7 +29,7 @@ OLIVE_NAMESPACE_ENTER
class OpenGLWorker : public RenderWorker
{
public:
OpenGLWorker(OpenGLProxy* proxy);
OpenGLWorker(RenderBackend* parent, OpenGLProxy* proxy);
protected:
virtual void TextureToFrame(const QVariant& texture, FramePtr frame, const QMatrix4x4 &mat) const override;
+68 -54
View File
@@ -32,6 +32,8 @@ OLIVE_NAMESPACE_ENTER
RenderBackend::RenderBackend(QObject *parent) :
QObject(parent),
viewer_node_(nullptr),
video_instance_queuer_(0),
audio_instance_queuer_(0),
divider_(1),
render_mode_(RenderMode::kOnline),
pix_fmt_(PixelFormat::PIX_FMT_RGBA32F),
@@ -57,7 +59,7 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
CancelQueue();
// Delete all of our copied nodes
foreach (RenderWorker* instance, instance_pool_) {
foreach (RenderWorker* instance, video_instance_pool_) {
instance->Close();
}
@@ -69,7 +71,7 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
disconnect(viewer_node_->audio_playback_cache(),
&AudioPlaybackCache::Invalidated,
this,
&RenderBackend::AudioCallback);
&RenderBackend::AudioInvalidated);
}
// Set viewer node
@@ -77,7 +79,7 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
if (viewer_node_) {
// Initiate instances with new node
foreach (RenderWorker* instance, instance_pool_) {
foreach (RenderWorker* instance, video_instance_pool_) {
instance->Init(viewer_node_);
}
@@ -89,34 +91,36 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
connect(viewer_node_->audio_playback_cache(),
&AudioPlaybackCache::Invalidated,
this,
&RenderBackend::AudioCallback);
&RenderBackend::AudioInvalidated);
}
}
void RenderBackend::CancelQueue()
{
// FIXME: Implement something better than this...
thread_pool_.waitForDone();
video_thread_pool_.waitForDone();
}
QFuture<QByteArray> RenderBackend::Hash(const rational &time)
QFuture<QByteArray> RenderBackend::Hash(const rational &time, bool block_for_update)
{
return QtConcurrent::run(&thread_pool_,
GetInstanceFromPool(),
return QtConcurrent::run(&video_thread_pool_,
GetInstanceFromPool(video_instance_pool_, video_thread_pool_, video_instance_queuer_),
&RenderWorker::Hash,
time);
time,
block_for_update);
}
QFuture<FramePtr> RenderBackend::RenderFrame(const rational &time, bool clear_queue)
QFuture<FramePtr> RenderBackend::RenderFrame(const rational &time, bool clear_queue, bool block_for_update)
{
if (clear_queue) {
thread_pool_.clear();
video_thread_pool_.clear();
}
return QtConcurrent::run(&thread_pool_,
GetInstanceFromPool(),
return QtConcurrent::run(&video_thread_pool_,
GetInstanceFromPool(video_instance_pool_, video_thread_pool_, video_instance_queuer_),
&RenderWorker::RenderFrame,
time);
time,
block_for_update);
}
void RenderBackend::SetDivider(const int &divider)
@@ -146,19 +150,26 @@ void RenderBackend::SetVideoDownloadMatrix(const QMatrix4x4 &mat)
void RenderBackend::NodeGraphChanged(NodeInput *source)
{
QLinkedList<RenderWorker*>::iterator i;
for (i=instance_pool_.begin(); i!=instance_pool_.end(); i++) {
(*i)->Queue(source);
foreach (RenderWorker* worker, video_instance_pool_) {
worker->Queue(source);
}
}
void RenderBackend::UpdateInstance(RenderWorker *instance)
{
instance->SetAvailable(false);
instance->ProcessQueue();
instance->SetVideoParams(video_params());
instance->SetAudioParams(audio_params());
instance->SetVideoDownloadMatrix(video_download_matrix_);
}
void RenderBackend::Close()
{
CancelQueue();
qDeleteAll(instance_pool_);
instance_pool_.clear();
qDeleteAll(video_instance_pool_);
video_instance_pool_.clear();
}
VideoRenderingParams RenderBackend::video_params() const
@@ -171,60 +182,63 @@ AudioRenderingParams RenderBackend::audio_params() const
return AudioRenderingParams(viewer_node_->audio_params(), sample_fmt_);
}
RenderWorker *RenderBackend::GetInstanceFromPool()
RenderWorker *RenderBackend::GetInstanceFromPool(QVector<RenderWorker *> &worker_pool, QThreadPool &thread_pool, int &instance_queuer)
{
RenderWorker* instance = nullptr;
QLinkedList<RenderWorker*>::iterator i;
for (i=instance_pool_.begin(); i!=instance_pool_.end(); i++) {
if ((*i)->IsAvailable()) {
instance = *i;
foreach (RenderWorker* worker, worker_pool) {
if (worker->IsAvailable()) {
instance = worker;
break;
}
}
if (!instance) {
instance = CreateNewWorker();
instance_pool_.append(instance);
if (worker_pool.size() < thread_pool.maxThreadCount()) {
// Can create another instance
instance = CreateNewWorker();
worker_pool.append(instance);
if (viewer_node_) {
instance->Init(viewer_node_);
if (viewer_node_) {
instance->Init(viewer_node_);
}
connect(instance,
&RenderWorker::FinishedJob,
this,
&RenderBackend::WorkerFinished,
Qt::QueuedConnection);
} else {
instance = worker_pool.at(instance_queuer % worker_pool.size());
instance_queuer++;
}
connect(instance,
&RenderWorker::FinishedJob,
this,
&RenderBackend::WorkerFinished,
Qt::QueuedConnection);
}
instance->SetAvailable(false);
instance->ProcessQueue();
instance->SetVideoParams(video_params());
instance->SetAudioParams(audio_params());
instance->SetVideoDownloadMatrix(video_download_matrix_);
return instance;
}
void RenderBackend::AudioCallback()
void RenderBackend::AudioInvalidated(const TimeRange& r)
{
qDebug() << "STUB";
/*
AudioPlaybackCache* pb_cache = viewer_node_->audio_playback_cache();
QThreadPool* thread_pool = audio_copy_map_.thread_pool();
QFutureWatcher<SampleBufferPtr>* watcher = new QFutureWatcher<SampleBufferPtr>();
connect(watcher, &QFutureWatcher<SampleBufferPtr>::finished, this, &RenderBackend::AudioRendered);
while (!pb_cache->IsFullyValidated()
&& thread_pool->activeThreadCount() < thread_pool->maxThreadCount()) {
// FIXME: Trigger a background audio thread
TimeRange r = viewer_node_->audio_playback_cache()->GetInvalidatedRanges().first();
watcher->setFuture(QtConcurrent::run(&video_thread_pool_,
GetInstanceFromPool(audio_instance_pool_, audio_thread_pool_, audio_instance_queuer_),
&RenderWorker::RenderAudio,
r));
}
qDebug() << "FIXME: Start rendering audio at:" << r;
void RenderBackend::AudioRendered()
{
QFutureWatcher<SampleBufferPtr>* watcher = static_cast<QFutureWatcher<SampleBufferPtr>*>(sender());
break;
if (watcher->result()) {
qDebug() << "AUDIO: Received" << watcher->result()->sample_count_per_channel() << "samples!";
} else {
qDebug() << "AUDIO: Received null";
}
*/
watcher->deleteLater();
}
void RenderBackend::WorkerFinished()
+17 -6
View File
@@ -47,12 +47,12 @@ public:
/**
* @brief Asynchronously generate a hash at a given time
*/
QFuture<QByteArray> Hash(const rational& time);
QFuture<QByteArray> Hash(const rational& time, bool block_for_update);
/**
* @brief Asynchronously generate a frame at a given time
*/
QFuture<FramePtr> RenderFrame(const rational& time, bool clear_queue);
QFuture<FramePtr> RenderFrame(const rational& time, bool clear_queue, bool block_for_update);
void SetDivider(const int& divider);
@@ -67,6 +67,8 @@ public:
public slots:
void NodeGraphChanged(NodeInput *source);
void UpdateInstance(OLIVE_NAMESPACE::RenderWorker* instance);
protected:
virtual RenderWorker* CreateNewWorker() = 0;
@@ -77,15 +79,22 @@ protected:
AudioRenderingParams audio_params() const;
private:
RenderWorker *GetInstanceFromPool();
RenderWorker *GetInstanceFromPool(QVector<RenderWorker*>& worker_pool,
QThreadPool& thread_pool,
int& instance_queuer);
ViewerOutput* viewer_node_;
RenderCancelDialog* cancel_dialog_;
QLinkedList<RenderWorker*> instance_pool_;
QVector<RenderWorker*> video_instance_pool_;
QVector<RenderWorker*> audio_instance_pool_;
QThreadPool thread_pool_;
QThreadPool video_thread_pool_;
QThreadPool audio_thread_pool_;
int video_instance_queuer_;
int audio_instance_queuer_;
// VIDEO MEMBERS
int divider_;
@@ -107,7 +116,9 @@ private:
QList<ConformWaitInfo> footage_wait_info_;
private slots:
void AudioCallback();
void AudioInvalidated(const TimeRange &r);
void AudioRendered();
void WorkerFinished();
-52
View File
@@ -1,52 +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 RENDERCACHE_H
#define RENDERCACHE_H
#include <QMap>
#include <QMutex>
OLIVE_NAMESPACE_ENTER
template<class K, class V>
class RenderCache
{
public:
RenderCache() = default;
void Clear(){values_.clear();}
void Add(K key, V val){values_.insert(key, val);}
V Get(K key) const {return values_.value(key);}
bool Has(K key) const {return values_.contains(key);}
void Remove(K key) {values_.remove(key);}
private:
QHash<K, V> values_;
};
OLIVE_NAMESPACE_EXIT
#endif // RENDERCACHE_H
+60 -15
View File
@@ -25,10 +25,12 @@
#include "audio/sumsamples.h"
#include "config/config.h"
#include "node/block/clip/clip.h"
#include "renderbackend.h"
OLIVE_NAMESPACE_ENTER
RenderWorker::RenderWorker() :
RenderWorker::RenderWorker(RenderBackend* parent) :
parent_(parent),
viewer_(nullptr),
available_(true)
{
@@ -39,12 +41,16 @@ RenderWorker::~RenderWorker()
Close();
}
QByteArray RenderWorker::Hash(const rational &time)
QByteArray RenderWorker::Hash(const rational &time, bool block_for_update)
{
if (!viewer_) {
return QByteArray();
}
QMutexLocker locker(&lock_);
UpdateData(block_for_update);
QCryptographicHash hasher(QCryptographicHash::Sha1);
// Embed video parameters into this hash
@@ -60,14 +66,18 @@ QByteArray RenderWorker::Hash(const rational &time)
return hasher.result();
}
FramePtr RenderWorker::RenderFrame(const rational &time)
FramePtr RenderWorker::RenderFrame(const rational &time, bool block_for_update)
{
if (!viewer_) {
return nullptr;
}
NodeValueTable table = GenerateTable(viewer_,
TimeRange(time, time + video_params_.time_base()));
QMutexLocker locker(&lock_);
UpdateData(block_for_update);
NodeValueTable table = ProcessInput(viewer_->texture_input(),
TimeRange(time, time + video_params_.time_base()));
QVariant texture = table.Get(NodeParam::kTexture);
@@ -89,10 +99,45 @@ FramePtr RenderWorker::RenderFrame(const rational &time)
return frame;
}
SampleBufferPtr RenderWorker::RenderAudio(const TimeRange &range)
{
if (!viewer_) {
return nullptr;
}
QMutexLocker locker(&lock_);
UpdateData(true);
NodeValueTable table = ProcessInput(viewer_->samples_input(), range);
QVariant samples = table.Get(NodeParam::kSamples);
return samples.value<SampleBufferPtr>();
}
void RenderWorker::UpdateData(bool block_for_update)
{
// FIXME: This is pretty trashy. It works, but it's not good. Should probably be changed at some
// point.
if (block_for_update) {
QMetaObject::invokeMethod(parent_,
"UpdateInstance",
Qt::BlockingQueuedConnection,
OLIVE_NS_ARG(RenderWorker*, this));
} else {
parent_->UpdateInstance(this);
}
}
NodeValueTable RenderWorker::GenerateBlockTable(const TrackOutput *track, const TimeRange &range)
{
qDebug() << "Hello from track" << track << "type" << track->track_type();
if (track->track_type() == Timeline::kTrackTypeAudio) {
qDebug() << "Hello?";
QList<Block*> active_blocks = track->BlocksAtTimeRange(range);
// All these blocks will need to output to a buffer so we create one here
@@ -273,8 +318,8 @@ void RenderWorker::FootageProcessingEvent(StreamPtr stream, const TimeRange &inp
NodeValue value;
bool found_cache = false;
if (still_image_cache_.Has(stream.get())) {
CachedStill cs = still_image_cache_.Get(stream.get());
if (still_image_cache_.contains(stream.get())) {
const CachedStill& cs = still_image_cache_[stream.get()];
if (cs.colorspace == colorspace_match
&& cs.alpha_is_associated == video_stream->premultiplied_alpha()
@@ -283,7 +328,7 @@ void RenderWorker::FootageProcessingEvent(StreamPtr stream, const TimeRange &inp
value = cs.texture;
found_cache = true;
} else {
still_image_cache_.Remove(stream.get());
still_image_cache_.remove(stream.get());
}
}
@@ -291,11 +336,11 @@ void RenderWorker::FootageProcessingEvent(StreamPtr stream, const TimeRange &inp
value = GetDataFromStream(stream, input_time);
still_image_cache_.Add(stream.get(), {value,
colorspace_match,
video_stream->premultiplied_alpha(),
video_params_.divider(),
time_match});
still_image_cache_.insert(stream.get(), {value,
colorspace_match,
video_stream->premultiplied_alpha(),
video_params_.divider(),
time_match});
}
@@ -323,7 +368,7 @@ DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream)
{
// Access a map of Node inputs and decoder instances and retrieve a frame!
DecoderPtr decoder = decoder_cache_.Get(stream.get());
DecoderPtr decoder = decoder_cache_.value(stream.get());
if (!decoder && stream) {
// Create a new Decoder here
@@ -331,7 +376,7 @@ DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream)
decoder->set_stream(stream);
if (decoder->Open()) {
decoder_cache_.Add(stream.get(), decoder);
decoder_cache_.insert(stream.get(), decoder);
} else {
decoder = nullptr;
qWarning() << "Failed to open decoder for" << stream->footage()->filename()
+13 -8
View File
@@ -26,15 +26,16 @@
#include "decodercache.h"
#include "node/traverser.h"
#include "node/output/viewer/viewer.h"
#include "render/backend/rendercache.h"
OLIVE_NAMESPACE_ENTER
class RenderBackend;
class RenderWorker : public QObject, public NodeTraverser
{
Q_OBJECT
public:
RenderWorker();
RenderWorker(RenderBackend* parent);
virtual ~RenderWorker() override;
@@ -84,7 +85,7 @@ public:
*
* SHA-1 hash or empty QByteArray if no viewer node is set.
*/
QByteArray Hash(const rational &time);
QByteArray Hash(const rational &time, bool block_for_update);
/**
* @brief Render the frame at this time
@@ -97,11 +98,9 @@ public:
* function will still return a blank frame with the same parameters. If no viewer node is set,
* nullptr is returned.
*/
FramePtr RenderFrame(const rational& time);
FramePtr RenderFrame(const rational& time, bool block_for_update);
SampleBufferPtr RenderAudio(ViewerOutput* viewer,
const TimeRange& range,
const SampleFormat::Format& sample_fmt);
SampleBufferPtr RenderAudio(const TimeRange& range);
protected:
virtual void TextureToFrame(const QVariant& texture, FramePtr frame, const QMatrix4x4 &mat) const = 0;
@@ -130,6 +129,8 @@ signals:
void FinishedJob();
private:
void UpdateData(bool block_for_update);
NodeValue GetDataFromStream(StreamPtr stream, const TimeRange& input_time);
DecoderPtr ResolveDecoderFromInput(StreamPtr stream);
@@ -138,6 +139,8 @@ private:
Node *CopyNodeConnections(Node *src_node);
void CopyNodeMakeConnection(NodeInput *src_input, NodeInput *dst_input);
RenderBackend* parent_;
VideoRenderingParams video_params_;
AudioRenderingParams audio_params_;
@@ -150,7 +153,7 @@ private:
rational time;
};
RenderCache<Stream*, CachedStill> still_image_cache_;
QHash<Stream*, CachedStill> still_image_cache_;
QMatrix4x4 video_download_matrix_;
@@ -161,6 +164,8 @@ private:
QHash<Node*, Node*> copy_map_;
bool available_;
QMutex lock_;
};
OLIVE_NAMESPACE_EXIT
+30 -3
View File
@@ -51,6 +51,11 @@ struct HashDownloadFuturePair {
QFuture<void> download_future;
};
struct HashTimePair {
rational time;
QByteArray hash;
};
void CacheTask::Action()
{
OpenGLBackend backend;
@@ -94,7 +99,7 @@ void CacheTask::Action()
next = snapped + timebase;
}
hash_list.append({snapped, backend.Hash(snapped)});
hash_list.append({snapped, backend.Hash(snapped, false)});
range_to_cache.RemoveTimeRange(TimeRange(snapped, next));
}
@@ -107,11 +112,32 @@ void CacheTask::Action()
// Render all frames necessary
QLinkedList<HashFrameFuturePair> render_lookup_table;
{
QLinkedList<HashTimePair> sorted_times;
QLinkedList<HashTimePair>::iterator sorted_iterator;
// Rendering is more efficient if we cache in order
QMap< QByteArray, QLinkedList<rational> >::const_iterator i;
for (i=times_to_render.constBegin(); i!=times_to_render.constEnd(); i++) {
const QByteArray& hash = i.key();
const rational& time = i.value().first();
render_lookup_table.append({hash, backend.RenderFrame(i.value().first(), false)});
bool inserted = false;
for (sorted_iterator=sorted_times.begin(); sorted_iterator!=sorted_times.end(); sorted_iterator++) {
if (sorted_iterator->time > time) {
sorted_times.insert(sorted_iterator, {time, hash});
inserted = true;
break;
}
}
if (!inserted) {
sorted_times.append({time, hash});
}
}
foreach (const HashTimePair& p, sorted_times) {
render_lookup_table.append({p.hash, backend.RenderFrame(p.time, false, false)});
}
}
@@ -124,6 +150,7 @@ void CacheTask::Action()
// Start downloading frames that have finished
{
int counter = 0;
int nb_frames = render_lookup_table.size();
QLinkedList<HashDownloadFuturePair> download_futures;
@@ -160,7 +187,7 @@ void CacheTask::Action()
// Signal process
counter++;
emit ProgressChanged(qRound(100.0 * static_cast<double>(counter) / static_cast<double>(render_lookup_table.size())));
emit ProgressChanged(qRound(100.0 * static_cast<double>(counter) / static_cast<double>(nb_frames)));
j = download_futures.erase(j);
} else {
+6 -6
View File
@@ -418,7 +418,7 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time)
this,
&ViewerWidget::RendererGeneratedFrame);
watcher->setFuture(GetFrame(time, true));
watcher->setFuture(GetFrame(time, true, true));
}
void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
@@ -525,7 +525,7 @@ void ViewerWidget::FillPlaybackQueue()
rational next_time = Timecode::timestamp_to_time(playback_queue_next_frame_,
timebase());
playback_queue_next_frame_ += playback_speed_;
QFuture<FramePtr> future = GetFrame(next_time, false);
QFuture<FramePtr> future = GetFrame(next_time, false, false);
future.waitForFinished();
playback_queue_.AppendTimewise({future.result()->timestamp(), future.result()}, playback_speed_);
@@ -552,7 +552,7 @@ QString ViewerWidget::GetCachedFilenameFromTime(const rational &time)
bool ViewerWidget::FrameExistsAtTime(const rational &time)
{
return GetConnectedNode() && time < GetConnectedNode()->GetLength();
return GetConnectedNode() && time >= 0 && time < GetConnectedNode()->GetLength();
}
void ViewerWidget::SetDisplayImage(FramePtr frame, bool main_only)
@@ -584,7 +584,7 @@ void ViewerWidget::RequestNextFrameForQueue()
&QFutureWatcher<FramePtr>::finished,
this,
&ViewerWidget::RendererGeneratedFrameForQueue);
watcher->setFuture(GetFrame(next_time, false));
watcher->setFuture(GetFrame(next_time, false, true));
}
PixelFormat::Format ViewerWidget::GetCurrentPixelFormat() const
@@ -592,12 +592,12 @@ PixelFormat::Format ViewerWidget::GetCurrentPixelFormat() const
return PixelFormat::instance()->GetConfiguredFormatForMode(RenderMode::kOffline);
}
QFuture<FramePtr> ViewerWidget::GetFrame(const rational &t, bool clear_render_queue)
QFuture<FramePtr> ViewerWidget::GetFrame(const rational &t, bool clear_render_queue, bool block_update)
{
QByteArray cached_hash = GetConnectedNode()->video_frame_cache()->GetHash(t);
if (cached_hash.isEmpty()) {
// Frame hasn't been cached, start render job
return renderer_->RenderFrame(t, clear_render_queue);
return renderer_->RenderFrame(t, clear_render_queue, block_update);
} else {
// Frame has been cached, grab the frame
QString cache_fn = GetConnectedNode()->video_frame_cache()->CachePathName(cached_hash,
+1 -1
View File
@@ -188,7 +188,7 @@ private:
PixelFormat::Format GetCurrentPixelFormat() const;
QFuture<FramePtr> GetFrame(const rational& t, bool clear_render_queue);
QFuture<FramePtr> GetFrame(const rational& t, bool clear_render_queue, bool block_update);
QStackedWidget* stack_;
+6 -5
View File
@@ -38,17 +38,18 @@ public:
void AppendTimewise(const ViewerPlaybackFrame& f, int playback_speed)
{
if (this->isEmpty() || f.timestamp > this->last().timestamp == playback_speed > 0) {
reverse_iterator i = this->rbegin();
if (this->isEmpty() || (this->last().timestamp < f.timestamp) == (playback_speed > 0)) {
this->append(f);
} else {
reverse_iterator i = this->rbegin();
while (i != this->rend()) {
reverse_iterator next = i + 1;
if (next->timestamp < f.timestamp == playback_speed > 0) {
if ((next->timestamp < f.timestamp) == (playback_speed > 0)) {
this->insert(i.base(), f);
break;
return;
} else {
i = next;
}