task: massively simplified task system

Made various changes and fixes to the task system:

- Tasks are built around QtConcurrent rather than QThread. Reduces
  code complexity significantly.
- Task error reporting is now streamlined in both TaskManager and
  TaskDialog.
- Moved ProjectImport/Save/LoadManager to the app/task folder
This commit is contained in:
itsmattkc
2020-05-19 02:44:32 +10:00
parent c4b0a53174
commit b9cd83eff1
49 changed files with 864 additions and 1332 deletions
+3 -1
View File
@@ -16,12 +16,14 @@
add_subdirectory(cache)
add_subdirectory(conform)
add_subdirectory(export)
add_subdirectory(project)
add_subdirectory(proxy)
add_subdirectory(render)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
task/task.h
task/task.cpp
task/taskmanager.h
task/taskmanager.cpp
PARENT_SCOPE
+7 -153
View File
@@ -22,181 +22,35 @@
#include <QLinkedList>
#include "common/timecodefunctions.h"
#include "project/item/sequence/sequence.h"
#include "render/backend/opengl/openglbackend.h"
OLIVE_NAMESPACE_ENTER
CacheTask::CacheTask(ViewerOutput* viewer, int divider, bool in_out_only) :
viewer_(viewer),
RenderTask(viewer),
in_out_only_(in_out_only),
divider_(divider)
{
SetTitle(tr("Caching \"%1\"").arg(viewer_->media_name()));
SetTitle(tr("Caching \"%1\"").arg(viewer->media_name()));
}
struct TimeHashFuturePair {
rational time;
QFuture<QByteArray> hash_future;
};
struct HashFrameFuturePair {
QByteArray hash;
QFuture<FramePtr> frame_future;
};
struct HashDownloadFuturePair {
QByteArray hash;
QFuture<void> download_future;
};
struct HashTimePair {
rational time;
QByteArray hash;
};
void CacheTask::Action()
bool CacheTask::Run()
{
OpenGLBackend backend;
RenderMode::Mode mode = RenderMode::kOffline;
PixelFormat::Format format = PixelFormat::instance()->GetConfiguredFormatForMode(mode);
backend.SetAudioEnabled(false);
backend.SetViewerNode(viewer_);
backend.SetPixelFormat(format);
backend.SetMode(mode);
backend.SetDivider(divider_);
backend.SetSampleFormat(SampleFormat::kInternalFormat);
// Get list of invalidated ranges
TimeRangeList range_to_cache = viewer_->video_frame_cache()->GetInvalidatedRanges();
TimeRangeList range_to_cache = viewer()->video_frame_cache()->GetInvalidatedRanges();
// If we're caching only in-out, limit the range to that
if (in_out_only_) {
Sequence* s = static_cast<Sequence*>(viewer_->parent());
Sequence* s = static_cast<Sequence*>(viewer()->parent());
if (s->workarea()->enabled()) {
range_to_cache = range_to_cache.Intersects(s->workarea()->range());
}
}
// Get hashes for each frame
QLinkedList<TimeHashFuturePair> hash_list;
while (!range_to_cache.isEmpty()) {
const TimeRange& range = range_to_cache.first();
Render(range_to_cache, 2);
const rational& timebase = viewer_->video_params().time_base();
rational time = range.in();
rational snapped = Timecode::snap_time_to_timebase(time, timebase);
rational next;
if (snapped > time) {
next = snapped;
snapped -= timebase;
} else {
next = snapped + timebase;
}
hash_list.append({snapped, backend.Hash(snapped, false)});
range_to_cache.RemoveTimeRange(TimeRange(snapped, next));
}
// Determine any duplicates
QMap< QByteArray, QLinkedList<rational> > times_to_render;
foreach (const TimeHashFuturePair& i, hash_list) {
times_to_render[i.hash_future.result()].append(i.time);
}
// 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();
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)});
}
}
OIIO::TypeDesc output_desc = PixelFormat::GetOIIOTypeDesc(format);
OIIO::ImageSpec output_spec(viewer_->video_params().width() / divider_,
viewer_->video_params().height() / divider_,
PixelFormat::ChannelCount(format),
output_desc);
// Start downloading frames that have finished
{
int counter = 0;
int nb_frames = render_lookup_table.size();
QLinkedList<HashDownloadFuturePair> download_futures;
// Iterators
QLinkedList<HashFrameFuturePair>::iterator i;
QLinkedList<HashDownloadFuturePair>::iterator j;
while (!render_lookup_table.isEmpty() || !download_futures.isEmpty()) {
i = render_lookup_table.begin();
while (i != render_lookup_table.end()) {
if (i->frame_future.isFinished()) {
FramePtr f = i->frame_future.result();
// Start multithreaded download here
download_futures.append({i->hash,
QtConcurrent::run(FrameHashCache::SaveCacheFrame, i->hash, f)});
i = render_lookup_table.erase(i);
} else {
i++;
}
}
j = download_futures.begin();
while (j != download_futures.end()) {
if (j->download_future.isFinished()) {
// Place it in the cache
const QLinkedList<rational>& times_with_hash = times_to_render.value(j->hash);
foreach (const rational& t, times_with_hash) {
viewer_->video_frame_cache()->SetHash(t, j->hash);
}
// Signal process
counter++;
emit ProgressChanged(qRound(100.0 * static_cast<double>(counter) / static_cast<double>(nb_frames)));
j = download_futures.erase(j);
} else {
j++;
}
}
}
}
return true;
}
OLIVE_NAMESPACE_EXIT
+6 -7
View File
@@ -21,23 +21,22 @@
#ifndef CACHETASK_H
#define CACHETASK_H
#include "node/output/viewer/viewer.h"
#include "task/task.h"
#include <QtConcurrent/QtConcurrent>
#include "task/render/render.h"
OLIVE_NAMESPACE_ENTER
class CacheTask : public Task
class CacheTask : public RenderTask
{
Q_OBJECT
public:
CacheTask(ViewerOutput* viewer, int divider, bool in_out_only);
protected:
virtual void Action() override;
public slots:
virtual bool Run() override;
private:
ViewerOutput* viewer_;
bool in_out_only_;
int divider_;
+7 -5
View File
@@ -31,10 +31,11 @@ ConformTask::ConformTask(AudioStreamPtr stream, const AudioRenderingParams& para
SetTitle(tr("Conforming Audio %1:%2").arg(stream_->footage()->filename(), QString::number(stream_->index())));
}
void ConformTask::Action()
bool ConformTask::Run()
{
if (stream_->footage()->decoder().isEmpty()) {
emit Failed(tr("Failed to find decoder to conform audio stream"));
SetError(tr("Failed to find decoder to conform audio stream"));
return false;
} else {
DecoderPtr decoder = Decoder::CreateFromID(stream_->footage()->decoder());
@@ -42,10 +43,11 @@ void ConformTask::Action()
connect(decoder.get(), &Decoder::IndexProgress, this, &ConformTask::ProgressChanged);
if (decoder->ConformAudio(&IsCancelled(), params_)) {
emit Succeeded();
if (!decoder->ConformAudio(&IsCancelled(), params_)) {
SetError(tr("Failed to conform audio"));
return false;
} else {
emit Failed(QStringLiteral("Failed to conform audio"));
return true;
}
}
}
+2 -2
View File
@@ -32,8 +32,8 @@ class ConformTask : public Task
public:
ConformTask(AudioStreamPtr stream, const AudioRenderingParams& params);
protected:
virtual void Action() override;
public slots:
virtual bool Run() override;
private:
AudioStreamPtr stream_;
+24
View File
@@ -0,0 +1,24 @@
# 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/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
task/export/export.h
task/export/export.cpp
task/export/exportparams.h
task/export/exportparams.cpp
PARENT_SCOPE
)
@@ -18,40 +18,22 @@
***/
#include "task.h"
#include "export.h"
OLIVE_NAMESPACE_ENTER
Task::Task() :
title_(tr("Task"))
ExportTask::ExportTask(ViewerOutput* viewer_node,
ColorManager* color_manager,
const ExportParams& params) :
RenderTask(viewer_node),
color_manager_(color_manager),
params_(params)
{
}
void Task::Start()
bool ExportTask::Run()
{
Action();
emit Finished();
}
const QString &Task::GetTitle()
{
return title_;
}
void Task::Cancel()
{
CancelableObject::Cancel();
}
void Task::SetErrorText(const QString &s)
{
error_ = s;
}
void Task::SetTitle(const QString &s)
{
title_ = s;
return true;
}
OLIVE_NAMESPACE_EXIT
+50
View File
@@ -0,0 +1,50 @@
/***
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 EXPORTTASK_H
#define EXPORTTASK_H
#include "exportparams.h"
#include "node/output/viewer/viewer.h"
#include "render/colorprocessor.h"
#include "task/render/render.h"
#include "task/task.h"
OLIVE_NAMESPACE_ENTER
class ExportTask : public RenderTask
{
Q_OBJECT
public:
ExportTask(ViewerOutput *viewer_node, ColorManager *color_manager, const ExportParams &params);
public slots:
virtual bool Run() override;
private:
ColorManager* color_manager_;
ExportParams params_;
};
OLIVE_NAMESPACE_EXIT
#endif // EXPORTTASK_H
+103
View File
@@ -0,0 +1,103 @@
/***
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 "exportparams.h"
OLIVE_NAMESPACE_ENTER
ExportParams::ExportParams() :
video_scaling_method_(kStretch),
has_custom_range_(false)
{
}
const QString &ExportParams::encoder() const
{
return encoder_id_;
}
void ExportParams::set_encoder(const QString &id)
{
encoder_id_ = id;
}
bool ExportParams::has_custom_range() const
{
return has_custom_range_;
}
const TimeRange &ExportParams::custom_range() const
{
return custom_range_;
}
void ExportParams::set_custom_range(const TimeRange &custom_range)
{
has_custom_range_ = true;
custom_range_ = custom_range;
}
const ExportParams::VideoScalingMethod &ExportParams::video_scaling_method() const
{
return video_scaling_method_;
}
void ExportParams::set_video_scaling_method(const ExportParams::VideoScalingMethod &video_scaling_method)
{
video_scaling_method_ = video_scaling_method;
}
const ColorTransform &ExportParams::color_transform() const
{
return color_transform_;
}
void ExportParams::set_color_transform(const ColorTransform &color_transform)
{
color_transform_ = color_transform;
}
QMatrix4x4 ExportParams::GenerateMatrix(ExportParams::VideoScalingMethod method,
int source_width, int source_height,
int dest_width, int dest_height)
{
QMatrix4x4 preview_matrix;
if (method == ExportParams::kStretch) {
return preview_matrix;
}
float export_ar = static_cast<float>(dest_width) / static_cast<float>(dest_height);
float source_ar = static_cast<float>(source_width) / static_cast<float>(source_height);
if (qFuzzyCompare(export_ar, source_ar)) {
return preview_matrix;
}
if ((export_ar > source_ar) == (method == ExportParams::kFit)) {
preview_matrix.scale(source_ar / export_ar, 1.0F);
} else {
preview_matrix.scale(1.0F, export_ar / source_ar);
}
return preview_matrix;
}
OLIVE_NAMESPACE_EXIT
+73
View File
@@ -0,0 +1,73 @@
/***
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 EXPORTPARAMS_H
#define EXPORTPARAMS_H
#include <QMatrix4x4>
#include "codec/encoder.h"
#include "node/output/viewer/viewer.h"
#include "render/colortransform.h"
OLIVE_NAMESPACE_ENTER
class ExportParams : public EncodingParams {
public:
enum VideoScalingMethod {
kFit,
kStretch,
kCrop
};
ExportParams();
const QString& encoder() const;
void set_encoder(const QString& id);
bool has_custom_range() const;
const TimeRange& custom_range() const;
void set_custom_range(const TimeRange& custom_range);
const VideoScalingMethod& video_scaling_method() const;
void set_video_scaling_method(const VideoScalingMethod& video_scaling_method);
const ColorTransform& color_transform() const;
void set_color_transform(const ColorTransform& color_transform);
static QMatrix4x4 GenerateMatrix(ExportParams::VideoScalingMethod method,
int source_width, int source_height,
int dest_width, int dest_height);
private:
QString encoder_id_;
VideoScalingMethod video_scaling_method_;
bool has_custom_range_;
TimeRange custom_range_;
ColorTransform color_transform_;
};
OLIVE_NAMESPACE_EXIT
#endif // EXPORTPARAMS_H
+24
View File
@@ -0,0 +1,24 @@
# 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/>.
add_subdirectory(import)
add_subdirectory(load)
add_subdirectory(save)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
PARENT_SCOPE
)
+22
View File
@@ -0,0 +1,22 @@
# 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/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
task/project/import/import.h
task/project/import/import.cpp
PARENT_SCOPE
)
+141
View File
@@ -0,0 +1,141 @@
/***
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 "import.h"
#include <QDir>
#include <QFileInfo>
#include "core.h"
#include "codec/decoder.h"
#include "project/item/footage/footage.h"
OLIVE_NAMESPACE_ENTER
ProjectImportTask::ProjectImportTask(ProjectViewModel *model, Folder *folder, const QStringList &filenames) :
command_(nullptr),
model_(model),
folder_(folder)
{
foreach (const QString& f, filenames) {
filenames_.append(f);
}
file_count_ = Core::CountFilesInFileList(filenames_);
SetTitle(tr("Importing %1 files").arg(file_count_));
}
const int &ProjectImportTask::GetFileCount() const
{
return file_count_;
}
bool ProjectImportTask::Run()
{
command_ = new QUndoCommand();
int imported = 0;
Import(folder_, filenames_, imported, command_);
if (IsCancelled()) {
delete command_;
command_ = nullptr;
return false;
} else {
return true;
}
}
void ProjectImportTask::Import(Folder *folder, const QFileInfoList &import, int &counter, QUndoCommand* parent_command)
{
foreach (const QFileInfo& file_info, import) {
if (IsCancelled()) {
break;
}
// Check if this file is a directory
if (file_info.isDir()) {
// QDir::entryList only returns filenames, we can use entryInfoList() to get full paths
QFileInfoList entry_list = QDir(file_info.absoluteFilePath()).entryInfoList();
// Strip out "." and ".." (for some reason QDir::NoDotAndDotDot doesn't work with entryInfoList, so we have to
// check manually)
for (int i=0;i<entry_list.size();i++) {
if (entry_list.at(i).fileName() == "." || entry_list.at(i).fileName() == "..") {
entry_list.removeAt(i);
i--;
}
}
// Only proceed if the empty actually has files in it
if (!entry_list.isEmpty()) {
// Create a folder corresponding to the directory
ItemPtr f = std::make_shared<Folder>();
f->set_name(file_info.fileName());
// Create undoable command that adds the items to the model
new ProjectViewModel::AddItemCommand(model_,
folder,
f,
parent_command);
// Recursively follow this path
Import(static_cast<Folder*>(f.get()), entry_list, counter, parent_command);
}
} else {
FootagePtr f = std::make_shared<Footage>();
f->set_filename(file_info.absoluteFilePath());
f->set_name(file_info.fileName());
f->set_timestamp(file_info.lastModified());
// Probe will fail if a project isn't set because ImageStream and its derivatives try to connect to the project's
// ColorManager instance
// FIXME: Perhaps re-think this approach at some point
f->set_project(model_->project());
Decoder::ProbeMedia(f.get(), &IsCancelled());
f->set_project(nullptr);
if (f->status() != Footage::kInvalid) {
// Create undoable command that adds the items to the model
new ProjectViewModel::AddItemCommand(model_,
folder,
f,
parent_command);
}
counter++;
emit ProgressChanged((counter * 100) / file_count_);
}
}
}
OLIVE_NAMESPACE_EXIT
+65
View File
@@ -0,0 +1,65 @@
/***
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 PROJECTIMPORTMANAGER_H
#define PROJECTIMPORTMANAGER_H
#include <QFileInfoList>
#include <QUndoCommand>
#include "project/projectviewmodel.h"
#include "task/task.h"
OLIVE_NAMESPACE_ENTER
class ProjectImportTask : public Task
{
Q_OBJECT
public:
ProjectImportTask(ProjectViewModel* model, Folder* folder, const QStringList& filenames);
const int& GetFileCount() const;
QUndoCommand* GetCommand() const
{
return command_;
}
public slots:
virtual bool Run() override;
private:
void Import(Folder* folder, const QFileInfoList &import, int& counter, QUndoCommand *parent_command);
QUndoCommand* command_;
ProjectViewModel* model_;
Folder* folder_;
QFileInfoList filenames_;
int file_count_;
};
OLIVE_NAMESPACE_EXIT
#endif // PROJECTIMPORTMANAGER_H
+22
View File
@@ -0,0 +1,22 @@
# 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/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
task/project/load/load.h
task/project/load/load.cpp
PARENT_SCOPE
)
+86
View File
@@ -0,0 +1,86 @@
/***
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 "load.h"
#include <QApplication>
#include <QFile>
#include <QXmlStreamReader>
#include "common/xmlutils.h"
OLIVE_NAMESPACE_ENTER
ProjectLoadTask::ProjectLoadTask(const QString &filename) :
filename_(filename)
{
SetTitle(tr("Loading '%1'").arg(filename));
}
bool ProjectLoadTask::Run()
{
QFile project_file(filename_);
if (project_file.open(QFile::ReadOnly | QFile::Text)) {
QXmlStreamReader reader(&project_file);
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("olive")) {
while(XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("version")) {
qDebug() << "Project version:" << reader.readElementText();
} else if (reader.name() == QStringLiteral("project")) {
ProjectPtr project = std::make_shared<Project>();
project->set_filename(filename_);
project->Load(&reader, &IsCancelled());
// Ensure project is in main thread
moveToThread(qApp->thread());
if (!IsCancelled()) {
projects_.append(project);
}
} else {
reader.skipCurrentElement();
}
}
} else {
reader.skipCurrentElement();
}
}
project_file.close();
if (reader.hasError()) {
SetError(reader.errorString());
return false;
} else {
return true;
}
} else {
SetError(tr("Failed to read file \"%1\" for reading.").arg(filename_));
return false;
}
}
OLIVE_NAMESPACE_EXIT
+52
View File
@@ -0,0 +1,52 @@
/***
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 PROJECTLOADMANAGER_H
#define PROJECTLOADMANAGER_H
#include "project/project.h"
#include "task/task.h"
OLIVE_NAMESPACE_ENTER
class ProjectLoadTask : public Task
{
Q_OBJECT
public:
ProjectLoadTask(const QString& filename);
const QList<ProjectPtr>& GetLoadedProjects()
{
return projects_;
}
public slots:
virtual bool Run() override;
private:
QList<ProjectPtr> projects_;
QString filename_;
};
OLIVE_NAMESPACE_EXIT
#endif // PROJECTLOADMANAGER_H
+22
View File
@@ -0,0 +1,22 @@
# 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/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
task/project/save/save.h
task/project/save/save.cpp
PARENT_SCOPE
)
+63
View File
@@ -0,0 +1,63 @@
/***
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 "save.h"
#include <QFile>
#include <QXmlStreamWriter>
OLIVE_NAMESPACE_ENTER
ProjectSaveTask::ProjectSaveTask(ProjectPtr project) :
project_(project)
{
SetTitle(tr("Saving '%1'").arg(project->filename()));
}
bool ProjectSaveTask::Run()
{
QFile project_file(project_->filename());
if (project_file.open(QFile::WriteOnly | QFile::Text)) {
QXmlStreamWriter writer(&project_file);
writer.setAutoFormatting(true);
writer.writeStartDocument();
writer.writeStartElement("olive");
writer.writeTextElement("version", "0.2.0");
project_->Save(&writer);
writer.writeEndElement(); // olive
writer.writeEndDocument();
project_file.close();
return true;
} else {
SetError(tr("Failed to open file \"%1\" for writing.").arg(project_->filename()));
return false;
}
}
OLIVE_NAMESPACE_EXIT
+50
View File
@@ -0,0 +1,50 @@
/***
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 PROJECTSAVEMANAGER_H
#define PROJECTSAVEMANAGER_H
#include "project/project.h"
#include "task/task.h"
OLIVE_NAMESPACE_ENTER
class ProjectSaveTask : public Task
{
Q_OBJECT
public:
ProjectSaveTask(ProjectPtr project);
ProjectPtr GetProject() const
{
return project_;
}
public slots:
virtual bool Run() override;
private:
ProjectPtr project_;
};
OLIVE_NAMESPACE_EXIT
#endif // PROJECTSAVEMANAGER_H
+6 -4
View File
@@ -38,10 +38,11 @@ ProxyTask::ProxyTask(VideoStreamPtr stream, int divider) :
}
}
void ProxyTask::Action()
bool ProxyTask::Run()
{
if (stream_->footage()->decoder().isEmpty()) {
emit Failed(tr("Failed to find decoder to conform audio stream"));
SetError(tr("Failed to find decoder to conform audio stream"));
return false;
} else {
DecoderPtr decoder = Decoder::CreateFromID(stream_->footage()->decoder());
@@ -50,9 +51,10 @@ void ProxyTask::Action()
connect(decoder.get(), &Decoder::IndexProgress, this, &ProxyTask::ProgressChanged);
if (decoder->ProxyVideo(&IsCancelled(), divider_)) {
emit Succeeded();
return true;
} else {
emit Failed(QStringLiteral("Failed to generate proxy"));
SetError(tr("Failed to generate proxy"));
return false;
}
}
}
+2 -2
View File
@@ -31,8 +31,8 @@ class ProxyTask : public Task
public:
ProxyTask(VideoStreamPtr stream, int divider);
protected:
virtual void Action() override;
public slots:
virtual bool Run() override;
private:
VideoStreamPtr stream_;
+22
View File
@@ -0,0 +1,22 @@
# 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/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
task/render/render.h
task/render/render.cpp
PARENT_SCOPE
)
+186
View File
@@ -0,0 +1,186 @@
/***
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 "render.h"
#include "common/timecodefunctions.h"
#include "render/backend/opengl/openglbackend.h"
OLIVE_NAMESPACE_ENTER
RenderTask::RenderTask(ViewerOutput* viewer) :
viewer_(viewer)
{
}
struct TimeHashFuturePair {
rational time;
QFuture<QByteArray> hash_future;
};
struct HashFrameFuturePair {
QByteArray hash;
QFuture<FramePtr> frame_future;
};
struct HashDownloadFuturePair {
QByteArray hash;
QFuture<void> download_future;
};
struct HashTimePair {
rational time;
QByteArray hash;
};
void RenderTask::Render(TimeRangeList range_to_cache, int divider)
{
OpenGLBackend backend;
RenderMode::Mode mode = RenderMode::kOffline;
PixelFormat::Format format = PixelFormat::instance()->GetConfiguredFormatForMode(mode);
backend.SetAudioEnabled(false);
backend.SetViewerNode(viewer_);
backend.SetPixelFormat(format);
backend.SetMode(mode);
backend.SetDivider(divider);
backend.SetSampleFormat(SampleFormat::kInternalFormat);
// Get hashes for each frame
QLinkedList<TimeHashFuturePair> hash_list;
while (!range_to_cache.isEmpty()) {
const TimeRange& range = range_to_cache.first();
const rational& timebase = viewer_->video_params().time_base();
rational time = range.in();
rational snapped = Timecode::snap_time_to_timebase(time, timebase);
rational next;
if (snapped > time) {
next = snapped;
snapped -= timebase;
} else {
next = snapped + timebase;
}
hash_list.append({snapped, backend.Hash(snapped, false)});
range_to_cache.RemoveTimeRange(TimeRange(snapped, next));
}
// Determine any duplicates
QMap< QByteArray, QLinkedList<rational> > times_to_render;
foreach (const TimeHashFuturePair& i, hash_list) {
times_to_render[i.hash_future.result()].append(i.time);
}
// 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();
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)});
}
}
OIIO::TypeDesc output_desc = PixelFormat::GetOIIOTypeDesc(format);
OIIO::ImageSpec output_spec(viewer_->video_params().width() / divider,
viewer_->video_params().height() / divider,
PixelFormat::ChannelCount(format),
output_desc);
// Start downloading frames that have finished
{
int counter = 0;
int nb_frames = render_lookup_table.size();
QLinkedList<HashDownloadFuturePair> download_futures;
// Iterators
QLinkedList<HashFrameFuturePair>::iterator i;
QLinkedList<HashDownloadFuturePair>::iterator j;
while (!render_lookup_table.isEmpty() || !download_futures.isEmpty()) {
i = render_lookup_table.begin();
while (i != render_lookup_table.end()) {
if (i->frame_future.isFinished()) {
FramePtr f = i->frame_future.result();
// Start multithreaded download here
download_futures.append({i->hash,
QtConcurrent::run(FrameHashCache::SaveCacheFrame, i->hash, f)});
i = render_lookup_table.erase(i);
} else {
i++;
}
}
j = download_futures.begin();
while (j != download_futures.end()) {
if (j->download_future.isFinished()) {
// Place it in the cache
const QLinkedList<rational>& times_with_hash = times_to_render.value(j->hash);
foreach (const rational& t, times_with_hash) {
viewer_->video_frame_cache()->SetHash(t, j->hash);
}
// Signal process
counter++;
emit ProgressChanged(qRound(100.0 * static_cast<double>(counter) / static_cast<double>(nb_frames)));
j = download_futures.erase(j);
} else {
j++;
}
}
}
}
}
OLIVE_NAMESPACE_EXIT
+49
View File
@@ -0,0 +1,49 @@
/***
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 RENDERTASK_H
#define RENDERTASK_H
#include "node/output/viewer/viewer.h"
#include "task/task.h"
OLIVE_NAMESPACE_ENTER
class RenderTask : public Task
{
public:
RenderTask(ViewerOutput* viewer);
protected:
void Render(TimeRangeList range_to_cache, int divider = 1);
ViewerOutput* viewer() const
{
return viewer_;
}
private:
ViewerOutput* viewer_;
};
OLIVE_NAMESPACE_EXIT
#endif // RENDERTASK_H
+43 -42
View File
@@ -52,59 +52,68 @@ public:
/**
* @brief Task Constructor
*/
Task();
Task() :
title_(tr("Task")),
error_(tr("Unknown error"))
{
}
/**
* @brief Retrieve the current title of this Task
*/
const QString& GetTitle();
const QString& GetTitle()
{
return title_;
}
/**
* @brief Returns the error that occurred if Run() returns false
*/
const QString& GetError()
{
return error_;
}
public slots:
/**
* @brief Try to start this Task
* @brief Run this task
*
* The main function for starting this Task. If this task is currently waiting, this function will start a new thread
* and set the status to kWorking.
* @return True if the task completed successfully, false if not.
*
* This function also checks its dependency Tasks and will only start if all of them are complete. If they are still
* working, this function will return FALSE and the status will continue to be kWaiting. If any of them failed, this
* Task will also fail - this function will return FALSE and the status will be set to kError.
* \see GetError() if this returns false.
*/
void Start();
virtual bool Run() = 0;
/**
* @brief Reset state so that Run() can be called again.
*
* Override this if your class holds any persistent state that should be cleared/modified before
* it's safe for Run() to run again.
*/
virtual void Reset(){}
/**
* @brief Cancel the Task
*
* Sends a signal to the Task to stop as soon as possible. Always call this directly or connect with
* Qt::DirectConnection, or else it'll be queued *after* the task has already finished.
* Sends a signal to the Task to stop as soon as possible. Always call this directly or connect
* with Qt::DirectConnection, or else it'll be queued *after* the task has already finished.
*/
void Cancel();
void Cancel()
{
CancelableObject::Cancel();
}
protected:
/**
* @brief The main Task function which is run in a separate thread
*
* Action() is the function that gets called once the separate thread has been created. This function should be
* overridden in subclasses.
*
* It's also recommended to emit ProgressChanged() throughout your Action() so that any attached ProgressBars can
* show accurate progress information.
*
* @return
*
* TRUE if the Task could complete successfully. FALSE if not. Note that FALSE should only be returned if the Task
* could not finish, not if the Task found a negative result (see Task documentation for details). Before returning
* FALSE, it's recommended to use set_error() to signal to the user what caused the failure.
*/
virtual void Action() = 0;
/**
* @brief Set the error message
*
* It is recommended to use this if your Action() function ever returns FALSE to tell the user why the failure
* occurred.
*/
void SetErrorText(const QString& s);
void SetError(const QString& s)
{
error_ = s;
}
/**
* @brief Set the Task title
@@ -113,7 +122,10 @@ protected:
* and shouldn't need to change during the life of the Task. To show an error message, it's recommended to use
* set_error() instead.
*/
void SetTitle(const QString& s);
void SetTitle(const QString& s)
{
title_ = s;
}
signals:
/**
@@ -127,17 +139,6 @@ signals:
*/
void ProgressChanged(int p);
void Succeeded();
void Failed(const QString& error);
void Finished();
/**
* @brief Signal emitted when this Task is removed from TaskManager
*/
void Removed();
private:
QString title_;
+29 -177
View File
@@ -27,45 +27,22 @@ OLIVE_NAMESPACE_ENTER
TaskManager* TaskManager::instance_ = nullptr;
TaskManager::TaskManager() :
active_thread_count_(0)
TaskManager::TaskManager()
{
// Initialize threads to run tasks on
threads_.resize(QThread::idealThreadCount());
for (int i=0;i<threads_.size();i++) {
QThread* t = new QThread(this);
t->start(QThread::IdlePriority);
threads_.replace(i, {t, false});
}
}
TaskManager::~TaskManager()
{
// First send the signal to all tasks to start cancelling
foreach (const TaskContainer& task_info, tasks_) {
if (task_info.status == kWorking) {
task_info.task->Cancel();
}
thread_pool_.clear();
foreach (Task* t, tasks_) {
t->Cancel();
}
// Next, signal each thread to quit as its next event in the queue
foreach (const ThreadContainer& tc, threads_) {
tc.thread->quit();
}
thread_pool_.waitForDone();
// Wait for each thread's event queue to finish
foreach (const ThreadContainer& tc, threads_) {
tc.thread->wait();
// This is technically unnecessary since each QThread is a child of this object, but we may as well
delete tc.thread;
}
// Finally delete all task objects (they shouldn't have been deleted by TaskSucceeded() or TaskFailed() because our
// event queue shouldn't be active by this point
foreach (const TaskContainer& task_info, tasks_) {
delete task_info.task;
foreach (Task* t, tasks_) {
t->deleteLater();
}
}
@@ -92,171 +69,46 @@ int TaskManager::GetTaskCount() const
Task *TaskManager::GetFirstTask() const
{
return tasks_.first().task;
return tasks_.begin().value();
}
void TaskManager::AddTask(Task* t)
{
// Connect Task's status signal to the Callback
connect(t, &Task::Succeeded, this, &TaskManager::TaskSucceeded, Qt::QueuedConnection);
connect(t, &Task::Failed, this, &TaskManager::TaskFailed, Qt::QueuedConnection);
connect(t, &Task::Finished, this, &TaskManager::TaskFinished, Qt::QueuedConnection);
// Create a watcher for signalling
QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>();
connect(watcher, &QFutureWatcher<bool>::finished, this, &TaskManager::TaskFinished);
// Add the Task to the queue
tasks_.append({t, kWaiting});
tasks_.insert(watcher, t);
// Run task concurrently
watcher->setFuture(QtConcurrent::run(t, &Task::Run));
// Emit signal that a Task was added
emit TaskAdded(t);
emit TaskListChanged();
// Scan through queue and start any Tasks that can (including this one)
StartNextWaiting();
}
void TaskManager::StartNextWaiting()
{
// If there are no tasks in the queue, there is nothing to be done
if (tasks_.isEmpty()) {
return;
}
// If all threads are occupied, nothing to be done
if (active_thread_count_ == threads_.size()) {
return;
}
// Create a list of tasks that are waiting
QList<Task*> waiting_tasks;
foreach (const TaskContainer& task_info, tasks_) {
if (task_info.status == kWaiting) {
waiting_tasks.append(task_info.task);
}
}
// No tasks waiting to start
if (waiting_tasks.isEmpty()) {
return;
}
// For any inactive threads,
for (int i=0;i<threads_.size();i++) {
if (!threads_.at(i).active) {
// This thread is inactive and needs a new Task
Task* task = waiting_tasks.takeFirst();
task->moveToThread(threads_.at(i).thread);
threads_[i].active = true;
active_thread_count_++;
SetTaskStatus(task, kWorking);
QMetaObject::invokeMethod(task,
"Start",
Qt::QueuedConnection);
if (active_thread_count_ == threads_.size() || waiting_tasks.isEmpty()) {
break;
}
}
}
}
void TaskManager::DeleteTask(Task *t)
{
if (GetTaskStatus(t) == kWorking) {
// Send a signal to the task to cancel, it will likely continue to cancel in the background after it's removed
t->Cancel();
}
// Remove instances of Task from queue
for (int i=0;i<tasks_.size();i++) {
if (tasks_.at(i).task == t) {
tasks_.removeAt(i);
break;
}
}
emit t->Removed();
emit TaskListChanged();
if (GetTaskStatus(t) != kWorking) {
// If the task isn't doing anything, we can simply delete it
t->deleteLater();
}
}
void TaskManager::TaskFinished()
{
Task* task_sender = static_cast<Task*>(sender());
QFutureWatcher<bool>* watcher = static_cast<QFutureWatcher<bool>*>(sender());
Task* t = tasks_.value(watcher);
// Set this thread's active value to false
for (int i=0;i<threads_.size();i++) {
if (threads_.at(i).thread == task_sender->thread()) {
threads_[i].active = false;
}
tasks_.remove(watcher);
if (watcher->result()) {
// Task completed successfully
emit TaskRemoved(t);
t->deleteLater();
} else {
// Task failed, keep it so the user can see the error message
emit TaskFailed(t);
failed_tasks_.append(t);
}
// See if we can delete this task
if (GetTaskStatus(task_sender) == kFinished) {
DeleteTask(task_sender);
} else if (GetTaskStatus(task_sender) == kError) {
// If this task has already been deleted, we'll free its memory now
bool was_deleted = true;
watcher->deleteLater();
for (int i=0;i<tasks_.size();i++) {
if (tasks_.at(i).task == task_sender) {
was_deleted = false;
break;
}
}
if (was_deleted) {
task_sender->deleteLater();
}
}
// Decrement the active thread count
active_thread_count_--;
// Signal that the task has finished
emit TaskListChanged();
// Start any tasks that could start now
StartNextWaiting();
}
TaskManager::TaskStatus TaskManager::GetTaskStatus(Task *t)
{
foreach (const TaskContainer& container, tasks_) {
if (container.task == t) {
return container.status;
}
}
return kError;
}
void TaskManager::SetTaskStatus(Task *t, TaskStatus status)
{
for (int i=0;i<tasks_.size();i++) {
TaskContainer& cont = tasks_[i];
if (cont.task == t) {
cont.status = status;
break;
}
}
}
void TaskManager::TaskSucceeded()
{
SetTaskStatus(static_cast<Task*>(sender()), kFinished);
}
void TaskManager::TaskFailed()
{
SetTaskStatus(static_cast<Task*>(sender()), kError);
}
OLIVE_NAMESPACE_EXIT
+16 -67
View File
@@ -21,6 +21,7 @@
#ifndef TASKMANAGER_H
#define TASKMANAGER_H
#include <QtConcurrent/QtConcurrent>
#include <QVector>
#include <QUndoCommand>
@@ -95,79 +96,31 @@ signals:
*/
void TaskListChanged();
/**
* @brief Signal emitted when a task is deleted
*/
void TaskRemoved(Task* t);
/**
* @brief Signal emitted when a task fails
*/
void TaskFailed(Task* t);
private:
/**
* @brief The Status enum
*
* All states that a Task can be in. When subclassing, you don't need to set the Task's status as the base class
* does that automatically.
*/
enum TaskStatus {
/// This Task is yet to start
kWaiting,
/// This Task is currently running (see Action())
kWorking,
/// This Task has completed successfully
kFinished,
/// This Task failed and could not complete
kError
};
struct TaskContainer {
Task* task;
TaskStatus status;
};
struct ThreadContainer {
QThread* thread;
bool active;
};
/**
* @brief Scan through the task queue and start any Tasks that are able to start
*
* This function is run whenever a Task is added and whenever a Task finishes. It determines how many Tasks are
* currently running and therefore how many Tasks can be started (if any). It will then start ones that can.
*
* This function is aware of "dependency Tasks" and if a Task is waiting but has a dependency that hasn't finished,
* it will skip to the next one.
*
* Like AddTask, this function is NOT thread-safe and currently only intended to be run from the main thread.
*/
void StartNextWaiting();
/**
* @brief Removes the Task from the queue and deletes it
*
* Recommended for use after a Task has completed or errorred.
*
* @param t
*
* Task to delete
*/
void DeleteTask(Task* t);
TaskStatus GetTaskStatus(Task* t);
void SetTaskStatus(Task* t, TaskStatus status);
/**
* @brief Internal task array
*/
QVector<TaskContainer> tasks_;
QHash<QFutureWatcher<bool>*, Task*> tasks_;
/**
* @brief Background threads to run tasks on
* @brief Internal list of failed tasks
*/
QVector<ThreadContainer> threads_;
QLinkedList<Task*> failed_tasks_;
/**
* @brief Value for how many threads are currently active
* @brief Task thread pool
*/
int active_thread_count_;
QThreadPool thread_pool_;
/**
* @brief TaskManager singleton instance
@@ -175,10 +128,6 @@ private:
static TaskManager* instance_;
private slots:
void TaskSucceeded();
void TaskFailed();
void TaskFinished();
};