Merge branch 'master' into otio_transition
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include "codec/decoder.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
ConformTask::ConformTask(AudioStreamPtr stream, const AudioParams& params) :
|
||||
stream_(stream),
|
||||
@@ -33,7 +33,10 @@ ConformTask::ConformTask(AudioStreamPtr stream, const AudioParams& params) :
|
||||
|
||||
bool ConformTask::Run()
|
||||
{
|
||||
if (stream_->footage()->decoder().isEmpty()) {
|
||||
// Conforming is done by the renderer now, but I would like to use something like this just to
|
||||
// show progress
|
||||
|
||||
/*if (stream_->footage()->decoder().isEmpty()) {
|
||||
SetError(tr("Failed to find decoder to conform audio stream"));
|
||||
return false;
|
||||
} else {
|
||||
@@ -49,7 +52,9 @@ bool ConformTask::Run()
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -25,10 +25,11 @@
|
||||
#include "render/audioparams.h"
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class ConformTask : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ConformTask(AudioStreamPtr stream, const AudioParams& params);
|
||||
|
||||
@@ -42,6 +43,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // CONFORMTASK_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
+57
-59
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "render/colormanager.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
ExportTask::ExportTask(ViewerOutput* viewer_node,
|
||||
ColorManager* color_manager,
|
||||
@@ -33,16 +33,22 @@ ExportTask::ExportTask(ViewerOutput* viewer_node,
|
||||
params_(params)
|
||||
{
|
||||
SetTitle(tr("Exporting \"%1\"").arg(viewer_node->media_name()));
|
||||
|
||||
// Render highest quality
|
||||
backend()->SetRenderMode(RenderMode::kOnline);
|
||||
}
|
||||
|
||||
bool ExportTask::Run()
|
||||
{
|
||||
TimeRange range;
|
||||
|
||||
// For safety, if we're overwriting, we save to a temporary filename and then only overwrite it
|
||||
// at the end
|
||||
QString real_filename = params_.filename();
|
||||
if (QFileInfo::exists(params_.filename())) {
|
||||
// Generate a filename that definitely doesn't exist
|
||||
params_.SetFilename(FileFunctions::GetSafeTemporaryFilename(real_filename));
|
||||
}
|
||||
|
||||
encoder_ = Encoder::CreateFromID(params_.encoder(), params_);
|
||||
|
||||
if (!encoder_) {
|
||||
SetError(tr("Failed to create encoder"));
|
||||
return false;
|
||||
@@ -62,22 +68,28 @@ bool ExportTask::Run()
|
||||
range = TimeRange(0, viewer()->GetLength());
|
||||
}
|
||||
|
||||
frame_time_ = Timecode::time_to_timestamp(range.in(), viewer()->video_params().time_base());
|
||||
frame_time_ = 0;
|
||||
|
||||
QSize video_force_size;
|
||||
QMatrix4x4 video_force_matrix;
|
||||
|
||||
if (params_.video_enabled()) {
|
||||
|
||||
// Ensure renderer always provides the same resolution
|
||||
backend()->SetForceDownloadResolution(true);
|
||||
|
||||
// If a transformation matrix is applied to this video, create it here
|
||||
if (params_.video_scaling_method() != ExportParams::kStretch) {
|
||||
QMatrix4x4 mat = ExportParams::GenerateMatrix(params_.video_scaling_method(),
|
||||
viewer()->video_params().width(),
|
||||
viewer()->video_params().height(),
|
||||
params_.video_params().width(),
|
||||
params_.video_params().height());
|
||||
if (viewer()->video_params().width() != params_.video_params().width()
|
||||
|| params_.video_params().height() != params_.video_params().height()) {
|
||||
video_force_size = QSize(params_.video_params().width(), params_.video_params().height());
|
||||
|
||||
backend()->SetVideoDownloadMatrix(mat);
|
||||
if (params_.video_scaling_method() != ExportParams::kStretch) {
|
||||
video_force_matrix = ExportParams::GenerateMatrix(params_.video_scaling_method(),
|
||||
viewer()->video_params().width(),
|
||||
viewer()->video_params().height(),
|
||||
params_.video_params().width(),
|
||||
params_.video_params().height());
|
||||
}
|
||||
} else {
|
||||
// Disables forcing size in the renderer
|
||||
video_force_size = QSize(0, 0);
|
||||
}
|
||||
|
||||
// Create color processor
|
||||
@@ -94,15 +106,17 @@ bool ExportTask::Run()
|
||||
TimeRangeList video_range, audio_range;
|
||||
|
||||
if (params_.video_enabled()) {
|
||||
video_range.append(range);
|
||||
video_range = {range};
|
||||
}
|
||||
|
||||
if (params_.audio_enabled()) {
|
||||
audio_range.append(range);
|
||||
audio_range = {range};
|
||||
audio_data_.SetLength(range.length());
|
||||
}
|
||||
|
||||
Render(video_range, audio_range, false);
|
||||
Render(color_manager_, video_range, audio_range, RenderMode::kOnline, nullptr,
|
||||
video_force_size, video_force_matrix, encoder_->GetDesiredPixelFormat(),
|
||||
color_processor_);
|
||||
|
||||
bool success = true;
|
||||
|
||||
@@ -113,52 +127,37 @@ bool ExportTask::Run()
|
||||
|
||||
encoder_->Close();
|
||||
|
||||
encoder_->deleteLater();
|
||||
delete encoder_;
|
||||
|
||||
// If cancelled, delete the file we made, which is always a file we created since we write to a
|
||||
// temp file during the actual encoding process
|
||||
if (IsCancelled()) {
|
||||
QFile::remove(params_.filename());
|
||||
} else if (params_.filename() != real_filename) {
|
||||
// If we were writing to a temp file, overwrite now
|
||||
if (!FileFunctions::RenameFileAllowOverwrite(params_.filename(), real_filename)) {
|
||||
SetError(tr("Failed to overwrite \"%1\". Export has been saved as \"%2\" instead.")
|
||||
.arg(real_filename, params_.filename()));
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void FrameColorConvert(ColorProcessorPtr processor, FramePtr frame)
|
||||
{
|
||||
// OCIO conversion requires a frame in 32F format
|
||||
if (frame->format() != PixelFormat::PIX_FMT_RGBA32F
|
||||
&& frame->format() != PixelFormat::PIX_FMT_RGB32F) {
|
||||
PixelFormat::Format dst;
|
||||
|
||||
if (PixelFormat::FormatHasAlphaChannel(frame->format())) {
|
||||
dst = PixelFormat::PIX_FMT_RGBA32F;
|
||||
} else {
|
||||
dst = PixelFormat::PIX_FMT_RGB32F;
|
||||
}
|
||||
|
||||
frame = PixelFormat::ConvertPixelFormat(frame, dst);
|
||||
}
|
||||
|
||||
// Color conversion must be done with unassociated alpha, and the pipeline is always associated
|
||||
ColorManager::DisassociateAlpha(frame);
|
||||
|
||||
// Convert color space
|
||||
processor->ConvertFrame(frame);
|
||||
|
||||
// Re-associate alpha
|
||||
ColorManager::ReassociateAlpha(frame);
|
||||
}
|
||||
|
||||
QFuture<void> ExportTask::DownloadFrame(FramePtr frame, const QByteArray &hash)
|
||||
{
|
||||
rendered_frame_.insert(hash, frame);
|
||||
|
||||
return QtConcurrent::run(FrameColorConvert, color_processor_, frame);
|
||||
}
|
||||
|
||||
void ExportTask::FrameDownloaded(const QByteArray &hash, const std::list<rational> ×, qint64 job_time)
|
||||
void ExportTask::FrameDownloaded(FramePtr f, const QByteArray &hash, const QVector<rational> ×, qint64 job_time)
|
||||
{
|
||||
Q_UNUSED(job_time)
|
||||
|
||||
FramePtr f = rendered_frame_.value(hash);
|
||||
Q_UNUSED(hash)
|
||||
|
||||
foreach (const rational& t, times) {
|
||||
time_map_.insert(t, f);
|
||||
rational actual_time = t;
|
||||
|
||||
if (params_.has_custom_range()) {
|
||||
actual_time -= params_.custom_range().in();
|
||||
}
|
||||
|
||||
time_map_.insert(actual_time, f);
|
||||
}
|
||||
|
||||
forever {
|
||||
@@ -174,7 +173,6 @@ void ExportTask::FrameDownloaded(const QByteArray &hash, const std::list<rationa
|
||||
encoder_->WriteFrame(time_map_.take(real_time), real_time);
|
||||
|
||||
frame_time_++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,4 +189,4 @@ void ExportTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples
|
||||
audio_data_.WritePCM(adjusted_range, samples, QDateTime::currentMSecsSinceEpoch());
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "task/render/render.h"
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class ExportTask : public RenderTask
|
||||
{
|
||||
@@ -38,15 +38,16 @@ public:
|
||||
protected:
|
||||
virtual bool Run() override;
|
||||
|
||||
virtual QFuture<void> DownloadFrame(FramePtr frame, const QByteArray &hash) override;
|
||||
|
||||
virtual void FrameDownloaded(const QByteArray& hash, const std::list<rational>& times, qint64 job_time) override;
|
||||
virtual void FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector<rational>& times, qint64 job_time) override;
|
||||
|
||||
virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples, qint64 job_time) override;
|
||||
|
||||
private:
|
||||
QHash<QByteArray, FramePtr> rendered_frame_;
|
||||
virtual bool TwoStepFrameRendering() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
QHash<rational, FramePtr> time_map_;
|
||||
|
||||
ColorManager* color_manager_;
|
||||
@@ -63,6 +64,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // EXPORTTASK_H
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "exportparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
ExportParams::ExportParams() :
|
||||
video_scaling_method_(kStretch),
|
||||
@@ -122,4 +122,4 @@ void ExportParams::Save(QXmlStreamWriter *writer) const
|
||||
writer->writeEndElement(); // export
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "render/colortransform.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class ExportParams : public EncodingParams {
|
||||
public:
|
||||
@@ -70,6 +70,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // EXPORTPARAMS_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -20,7 +20,9 @@
|
||||
|
||||
#include "precachetask.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
#include "project/project.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
PreCacheTask::PreCacheTask(VideoStreamPtr footage, Sequence* sequence) :
|
||||
RenderTask(new ViewerOutput(), sequence->video_params(), sequence->audio_params()),
|
||||
@@ -29,23 +31,18 @@ PreCacheTask::PreCacheTask(VideoStreamPtr footage, Sequence* sequence) :
|
||||
viewer()->set_video_params(sequence->video_params());
|
||||
viewer()->set_audio_params(sequence->audio_params());
|
||||
|
||||
// Render fastest quality
|
||||
backend()->SetRenderMode(RenderMode::kOffline);
|
||||
|
||||
video_node_ = new VideoInput();
|
||||
video_node_ = new MediaInput();
|
||||
video_node_->SetStream(footage);
|
||||
|
||||
NodeParam::ConnectEdge(video_node_->output(), viewer()->texture_input());
|
||||
|
||||
SetTitle(tr("Pre-caching %1:%2").arg(footage->footage()->filename(),
|
||||
QString::number(footage->index())));
|
||||
|
||||
backend()->NodeGraphChanged(viewer()->texture_input());
|
||||
backend()->ProcessUpdateQueue();
|
||||
}
|
||||
|
||||
PreCacheTask::~PreCacheTask()
|
||||
{
|
||||
// We created this viewer node ourselves, so now we should delete it
|
||||
delete viewer();
|
||||
delete video_node_;
|
||||
}
|
||||
@@ -66,23 +63,21 @@ bool PreCacheTask::Run()
|
||||
}
|
||||
*/
|
||||
|
||||
Render(video_range, TimeRangeList(), true);
|
||||
|
||||
download_threads_.waitForDone();
|
||||
Render(footage_->footage()->project()->color_manager(),
|
||||
video_range,
|
||||
TimeRangeList(),
|
||||
RenderMode::kOnline,
|
||||
viewer()->video_frame_cache());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QFuture<void> PreCacheTask::DownloadFrame(FramePtr frame, const QByteArray &hash)
|
||||
{
|
||||
return QtConcurrent::run(&download_threads_, viewer()->video_frame_cache(), &FrameHashCache::SaveCacheFrame, hash, frame);
|
||||
}
|
||||
|
||||
void PreCacheTask::FrameDownloaded(const QByteArray &hash, const std::list<rational> ×, qint64 job_time)
|
||||
void PreCacheTask::FrameDownloaded(FramePtr frame, const QByteArray &hash, const QVector<rational> ×, qint64 job_time)
|
||||
{
|
||||
// Do nothing. Pre-cache essentially just creates more frames in the cache, it doesn't need to do
|
||||
// anything else.
|
||||
|
||||
Q_UNUSED(frame)
|
||||
Q_UNUSED(hash)
|
||||
Q_UNUSED(times)
|
||||
Q_UNUSED(job_time)
|
||||
@@ -97,4 +92,4 @@ void PreCacheTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr sampl
|
||||
Q_UNUSED(job_time)
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -21,15 +21,16 @@
|
||||
#ifndef PRECACHETASK_H
|
||||
#define PRECACHETASK_H
|
||||
|
||||
#include "node/input/media/video/video.h"
|
||||
#include "node/input/media/media.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
#include "project/item/sequence/sequence.h"
|
||||
#include "task/render/render.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class PreCacheTask : public RenderTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PreCacheTask(VideoStreamPtr footage, Sequence* sequence);
|
||||
|
||||
@@ -38,21 +39,17 @@ public:
|
||||
protected:
|
||||
virtual bool Run() override;
|
||||
|
||||
virtual QFuture<void> DownloadFrame(FramePtr frame, const QByteArray &hash) override;
|
||||
|
||||
virtual void FrameDownloaded(const QByteArray& hash, const std::list<rational>& times, qint64 job_time) override;
|
||||
virtual void FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector<rational>& times, qint64 job_time) override;
|
||||
|
||||
virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples, qint64 job_time) override;
|
||||
|
||||
private:
|
||||
VideoStreamPtr footage_;
|
||||
|
||||
VideoInput* video_node_;
|
||||
|
||||
QThreadPool download_threads_;
|
||||
MediaInput* video_node_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // PRECACHETASK_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "core.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
ProjectImportTask::ProjectImportTask(ProjectViewModel *model, Folder *folder, const QStringList &filenames) :
|
||||
command_(nullptr),
|
||||
@@ -110,8 +110,8 @@ void ProjectImportTask::Import(Folder *folder, QFileInfoList import, int &counte
|
||||
|
||||
} else {
|
||||
|
||||
FootagePtr item = Decoder::ProbeMedia(model_->project(), file_info.absoluteFilePath(),
|
||||
&IsCancelled());
|
||||
FootagePtr item = Decoder::Probe(model_->project(), file_info.absoluteFilePath(),
|
||||
&IsCancelled());
|
||||
|
||||
if (item) {
|
||||
// See if this footage is an image sequence
|
||||
@@ -284,4 +284,4 @@ int64_t ProjectImportTask::GetImageSequenceLimit(const QString& start_fn, int64_
|
||||
return start;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "project/projectviewmodel.h"
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class ProjectImportTask : public Task
|
||||
{
|
||||
@@ -83,6 +83,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // PROJECTIMPORTMANAGER_H
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <QListWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
ProjectImportErrorDialog::ProjectImportErrorDialog(const QStringList& filenames, QWidget* parent) :
|
||||
QDialog(parent)
|
||||
@@ -50,4 +50,4 @@ ProjectImportErrorDialog::ProjectImportErrorDialog(const QStringList& filenames,
|
||||
layout->addWidget(buttons);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class ProjectImportErrorDialog : public QDialog
|
||||
{
|
||||
@@ -35,6 +35,6 @@ public:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // PROJECTIMPORTERRORDIALOG_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "common/xmlutils.h"
|
||||
#include "core.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
ProjectLoadTask::ProjectLoadTask(const QString &filename) :
|
||||
ProjectLoadBaseTask(filename)
|
||||
@@ -37,6 +37,7 @@ ProjectLoadTask::ProjectLoadTask(const QString &filename) :
|
||||
bool ProjectLoadTask::Run()
|
||||
{
|
||||
QFile project_file(GetFilename());
|
||||
uint project_version = Core::kProjectVersion;
|
||||
|
||||
if (project_file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QXmlStreamReader reader(&project_file);
|
||||
@@ -45,7 +46,7 @@ bool ProjectLoadTask::Run()
|
||||
if (reader.name() == QStringLiteral("olive")) {
|
||||
while(XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("version")) {
|
||||
uint project_version = reader.readElementText().toUInt();
|
||||
project_version = reader.readElementText().toUInt();
|
||||
|
||||
if (project_version > Core::kProjectVersion) {
|
||||
// Project is newer than we support
|
||||
@@ -63,7 +64,7 @@ bool ProjectLoadTask::Run()
|
||||
|
||||
project_->set_filename(GetFilename());
|
||||
|
||||
project_->Load(&reader, &layout_info_, &IsCancelled());
|
||||
project_->Load(&reader, &layout_info_, project_version, &IsCancelled());
|
||||
|
||||
// Ensure project is in main thread
|
||||
project_->moveToThread(qApp->thread());
|
||||
@@ -99,4 +100,4 @@ bool ProjectLoadTask::Run()
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "loadbasetask.h"
|
||||
#include "window/mainwindow/mainwindowlayoutinfo.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class ProjectLoadTask : public ProjectLoadBaseTask
|
||||
{
|
||||
@@ -37,6 +37,6 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // PROJECTLOADMANAGER_H
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "loadbasetask.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
ProjectLoadBaseTask::ProjectLoadBaseTask(const QString &filename) :
|
||||
project_(nullptr),
|
||||
@@ -29,4 +29,4 @@ ProjectLoadBaseTask::ProjectLoadBaseTask(const QString &filename) :
|
||||
SetTitle(tr("Loading '%1'").arg(filename));
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "project/project.h"
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class ProjectLoadBaseTask : public Task
|
||||
{
|
||||
@@ -69,6 +69,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // LOADBASETASK_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -31,14 +31,13 @@
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/block/transition/crossdissolve/crossdissolvetransition.h"
|
||||
#include "node/input/media/audio/audio.h"
|
||||
#include "node/input/media/video/video.h"
|
||||
#include "node/input/media/media.h"
|
||||
#include "project/item/folder/folder.h"
|
||||
#include "project/item/sequence/sequence.h"
|
||||
|
||||
#define OTIO opentimelineio::v1_0
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
LoadOTIOTask::LoadOTIOTask(const QString& s) :
|
||||
ProjectLoadBaseTask(s)
|
||||
@@ -210,18 +209,16 @@ bool LoadOTIOTask::Run()
|
||||
if (imported_footage.contains(footage_url)) {
|
||||
probed_item = imported_footage.value(footage_url);
|
||||
} else {
|
||||
probed_item = Decoder::ProbeMedia(project_.get(), footage_url, &IsCancelled());
|
||||
probed_item = Decoder::Probe(project_.get(), footage_url, &IsCancelled());
|
||||
imported_footage.insert(footage_url, probed_item);
|
||||
project_->root()->add_child(probed_item);
|
||||
}
|
||||
|
||||
if (probed_item && probed_item->type() == Item::kFootage) {
|
||||
MediaInput* media;
|
||||
MediaInput* media = new MediaInput();
|
||||
if (track->track_type() == Timeline::kTrackTypeVideo) {
|
||||
media = new VideoInput();
|
||||
media->SetStream(probed_item->get_first_stream_of_type(Stream::kVideo));
|
||||
} else {
|
||||
media = new AudioInput();
|
||||
media->SetStream(probed_item->get_first_stream_of_type(Stream::kAudio));
|
||||
}
|
||||
sequence->AddNode(media);
|
||||
@@ -253,4 +250,4 @@ bool LoadOTIOTask::Run()
|
||||
return true;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "project/project.h"
|
||||
#include "task/project/load/loadbasetask.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class LoadOTIOTask : public ProjectLoadBaseTask
|
||||
{
|
||||
@@ -37,6 +37,6 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // OTIODECODER_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "common/filefunctions.h"
|
||||
#include "core.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
ProjectSaveTask::ProjectSaveTask(ProjectPtr project) :
|
||||
project_(project)
|
||||
@@ -38,7 +38,7 @@ ProjectSaveTask::ProjectSaveTask(ProjectPtr project) :
|
||||
bool ProjectSaveTask::Run()
|
||||
{
|
||||
// File to temporarily save to (ensures we can't half-write the user's main file and crash)
|
||||
QString temp_save = QDir(FileFunctions::GetTempFilePath()).filePath(QStringLiteral("tempsv"));
|
||||
QString temp_save = FileFunctions::GetSafeTemporaryFilename(project_->filename());
|
||||
|
||||
QFile project_file(temp_save);
|
||||
|
||||
@@ -74,18 +74,17 @@ bool ProjectSaveTask::Run()
|
||||
}
|
||||
|
||||
// Save was successful, we can now rewrite the original file
|
||||
QFile original(project_->filename());
|
||||
if ((!original.exists() || original.remove())
|
||||
&& QFile::copy(temp_save, project_->filename())) {
|
||||
if (FileFunctions::RenameFileAllowOverwrite(temp_save, project_->filename())) {
|
||||
return true;
|
||||
} else {
|
||||
SetError(tr("Failed to write to \"%1\".").arg(project_->filename()));
|
||||
SetError(tr("Failed to overwrite \"%1\". Project has been saved as \"%2\" instead.")
|
||||
.arg(project_->filename(), temp_save));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
SetError(tr("Failed to open file \"%1\" for writing.").arg(project_->filename()));
|
||||
SetError(tr("Failed to open temporary file \"%1\" for writing.").arg(temp_save));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "project/project.h"
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class ProjectSaveTask : public Task
|
||||
{
|
||||
@@ -45,6 +45,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // PROJECTSAVEMANAGER_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/input/media/media.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
SaveOTIOTask::SaveOTIOTask(ProjectPtr project) :
|
||||
project_(project)
|
||||
@@ -49,7 +49,7 @@ bool SaveOTIOTask::Run()
|
||||
std::vector<opentimelineio::v1_0::SerializableObject*> serialized;
|
||||
|
||||
foreach (ItemPtr item, sequences) {
|
||||
SequencePtr seq = std::static_pointer_cast<Sequence>(sequences.first());
|
||||
SequencePtr seq = std::static_pointer_cast<Sequence>(item);
|
||||
|
||||
auto otio_timeline = SerializeTimeline(seq);
|
||||
|
||||
@@ -133,7 +133,7 @@ opentimelineio::v1_0::Track *SaveOTIOTask::SerializeTrack(TrackOutput *track)
|
||||
otio_clip->set_source_range(opentimelineio::v1_0::TimeRange(block->in().toRationalTime(),
|
||||
block->length().toRationalTime()));
|
||||
|
||||
QList<MediaInput*> media_nodes = block->FindInputNodes<MediaInput>();
|
||||
QVector<MediaInput*> media_nodes = block->FindInputNodes<MediaInput>();
|
||||
if (!media_nodes.isEmpty()) {
|
||||
auto media_ref = new opentimelineio::v1_0::ExternalReference(media_nodes.first()->stream()->footage()->filename().toStdString());
|
||||
otio_clip->set_media_reference(media_ref);
|
||||
@@ -206,4 +206,4 @@ bool SaveOTIOTask::SerializeTrackList(TrackList *list, opentimelineio::v1_0::Tim
|
||||
return true;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "project/project.h"
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class SaveOTIOTask : public Task
|
||||
{
|
||||
@@ -49,6 +49,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // PROJECTSAVEASOTIOTASK_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
+167
-169
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -21,223 +21,221 @@
|
||||
#include "render.h"
|
||||
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "render/rendermanager.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
RenderTask::RenderTask(ViewerOutput* viewer, const VideoParams &vparams, const AudioParams &aparams)
|
||||
RenderTask::RenderTask(ViewerOutput* viewer, const VideoParams &vparams, const AudioParams &aparams) :
|
||||
viewer_(viewer),
|
||||
video_params_(vparams),
|
||||
audio_params_(aparams),
|
||||
running_tickets_(0)
|
||||
{
|
||||
backend_ = new OpenGLBackend();
|
||||
backend_->SetViewerNode(viewer);
|
||||
backend_->SetVideoParams(vparams);
|
||||
backend_->SetAudioParams(aparams);
|
||||
}
|
||||
|
||||
RenderTask::~RenderTask()
|
||||
{
|
||||
delete backend_;
|
||||
}
|
||||
|
||||
struct TimeHashFuturePair {
|
||||
rational time;
|
||||
RenderTicketPtr hash_future;
|
||||
};
|
||||
|
||||
struct HashTimePair {
|
||||
rational time;
|
||||
QByteArray hash;
|
||||
};
|
||||
|
||||
struct HashFrameFuturePair {
|
||||
QByteArray hash;
|
||||
RenderTicketPtr frame_future;
|
||||
};
|
||||
|
||||
struct RangeSampleFuturePair {
|
||||
TimeRange range;
|
||||
RenderTicketPtr sample_future;
|
||||
};
|
||||
|
||||
struct HashDownloadFuturePair {
|
||||
QByteArray hash;
|
||||
QFuture<void> download_future;
|
||||
qint64 job_time;
|
||||
};
|
||||
|
||||
void RenderTask::Render(const TimeRangeList& video_range,
|
||||
bool RenderTask::Render(ColorManager* manager,
|
||||
const TimeRangeList& video_range,
|
||||
const TimeRangeList &audio_range,
|
||||
bool use_disk_cache)
|
||||
RenderMode::Mode mode,
|
||||
FrameHashCache* cache, const QSize &force_size,
|
||||
const QMatrix4x4 &force_matrix, VideoParams::Format force_format,
|
||||
ColorProcessorPtr force_color_output)
|
||||
{
|
||||
// Run watchers in another thread so they can accept signals even while this thread is blocked
|
||||
QThread watcher_thread;
|
||||
watcher_thread.start();
|
||||
|
||||
double progress_counter = 0;
|
||||
double total_length = 0;
|
||||
double video_frame_sz = video_params().time_base().toDouble();
|
||||
|
||||
std::list<TimeRange> audio_queue;
|
||||
std::list<RangeSampleFuturePair> audio_lookup_table;
|
||||
if (!audio_range.isEmpty()) {
|
||||
foreach (const TimeRange& r, audio_range) {
|
||||
total_length += r.length().toDouble();
|
||||
// Store real time before any rendering takes place
|
||||
qint64 job_time = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
std::list<TimeRange> ranges = RenderBackend::SplitRangeIntoChunks(r);
|
||||
audio_queue.insert(audio_queue.end(), ranges.begin(), ranges.end());
|
||||
}
|
||||
// Queue audio jobs
|
||||
foreach (const TimeRange& r, audio_range) {
|
||||
// Don't count audio progress, since it's generally a lot faster than video and is weighted at
|
||||
// 50%, which makes the progress bar look weird to the uninitiated
|
||||
//total_length += r.length().toDouble();
|
||||
|
||||
IncrementRunningTickets();
|
||||
|
||||
RenderTicketWatcher* watcher = CreateWatcher(&watcher_thread);
|
||||
watcher->setProperty("range", QVariant::fromValue(r));
|
||||
watcher->SetTicket(RenderManager::instance()->RenderAudio(viewer_, r, audio_params_, false));
|
||||
}
|
||||
|
||||
std::list<HashFrameFuturePair> render_lookup_table;
|
||||
QVector<rational> times;
|
||||
QVector<QByteArray> hashes;
|
||||
std::list<HashTimePair> frame_queue;
|
||||
qint64 hash_job_time = 0;
|
||||
// Look up hashes
|
||||
QMap<QByteArray, QVector<rational> > time_map;
|
||||
|
||||
if (!video_range.isEmpty()) {
|
||||
times = viewer()->video_frame_cache()->GetFrameListFromTimeRange(video_range);
|
||||
// Get list of discrete frames from range
|
||||
QVector<rational> times = viewer()->video_frame_cache()->GetFrameListFromTimeRange(video_range);
|
||||
QVector<QByteArray> hashes(times.size());
|
||||
|
||||
// Add to "total progress"
|
||||
total_length += video_frame_sz * times.size();
|
||||
|
||||
RenderTicketPtr hash_future = backend_->Hash(times);
|
||||
hashes = hash_future->Get().value<QVector<QByteArray> >();
|
||||
hash_job_time = hash_future->GetJobTime();
|
||||
// Generate hashes
|
||||
for (int i=0; i<times.size(); i++) {
|
||||
if (IsCancelled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!hash_future->WasCancelled()) {
|
||||
for (int i=0;i<times.size();i++) {
|
||||
frame_queue.push_back({times.at(i), hashes.at(i)});
|
||||
hashes[i] = RenderManager::instance()->Hash(viewer(), video_params_, times.at(i));
|
||||
}
|
||||
|
||||
// Filter out duplicates
|
||||
for (int i=0; i<hashes.size(); i++) {
|
||||
if (IsCancelled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const QByteArray& hash = hashes.at(i);
|
||||
|
||||
time_map[hash].append(times.at(i));
|
||||
|
||||
if (time_map[hash].size() == 1) {
|
||||
// This is the first frame with this hash, so we signal a render
|
||||
RenderTicketWatcher* watcher = CreateWatcher(&watcher_thread);
|
||||
watcher->setProperty("hash", hash);
|
||||
|
||||
IncrementRunningTickets();
|
||||
|
||||
watcher->SetTicket(RenderManager::instance()->RenderFrame(viewer_, manager, times.at(i),
|
||||
mode, video_params_, audio_params_,
|
||||
force_size, force_matrix,
|
||||
force_format, force_color_output,
|
||||
cache));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start downloading frames that have finished
|
||||
std::list<HashDownloadFuturePair> download_futures;
|
||||
finished_watcher_mutex_.lock();
|
||||
|
||||
// Iterators
|
||||
std::list<HashFrameFuturePair>::iterator i;
|
||||
std::list<HashDownloadFuturePair>::iterator j;
|
||||
std::list<RangeSampleFuturePair>::iterator k;
|
||||
while (!IsCancelled()) {
|
||||
while (!finished_watchers_.empty() && !IsCancelled()) {
|
||||
RenderTicketWatcher* watcher = finished_watchers_.front();
|
||||
finished_watchers_.pop_front();
|
||||
|
||||
std::list<QByteArray> running_hashes;
|
||||
std::list<QByteArray> existing_hashes;
|
||||
finished_watcher_mutex_.unlock();
|
||||
|
||||
while (!IsCancelled()
|
||||
&& (!render_lookup_table.empty()
|
||||
|| !frame_queue.empty()
|
||||
|| !audio_queue.empty()
|
||||
|| !download_futures.empty()
|
||||
|| !audio_lookup_table.empty())) {
|
||||
// Analyze watcher here
|
||||
RenderManager::TicketType ticket_type = watcher->GetTicket()->property("type").value<RenderManager::TicketType>();
|
||||
|
||||
while (!IsCancelled() && !frame_queue.empty()) {
|
||||
if (ticket_type == RenderManager::kTypeAudio) {
|
||||
|
||||
// Pop another frame off the frame queue
|
||||
const HashTimePair& p = frame_queue.front();
|
||||
TimeRange range = watcher->property("range").value<TimeRange>();
|
||||
|
||||
// Check if we're already rendering this hash
|
||||
bool rendering_hash = (std::find(running_hashes.begin(), running_hashes.end(), p.hash) != running_hashes.end());
|
||||
AudioDownloaded(range,
|
||||
watcher->Get().value<SampleBufferPtr>(),
|
||||
job_time);
|
||||
|
||||
// Skip this hash if we're already rendering it
|
||||
if (!rendering_hash) {
|
||||
// Check if this frame already exists (has already been rendered previously or during this job)
|
||||
bool hash_exists = false;
|
||||
// Don't count audio progress, since it's generally a lot faster than video and is weighted at
|
||||
// 50%, which makes the progress bar look weird to the uninitiated
|
||||
//progress_counter += range.length().toDouble();
|
||||
//emit ProgressChanged(progress_counter / total_length);
|
||||
|
||||
if (use_disk_cache) {
|
||||
// Check if this hash is in our "existing hashes" list
|
||||
hash_exists = (std::find(existing_hashes.begin(), existing_hashes.end(), p.hash) != existing_hashes.end());
|
||||
} else if (ticket_type == RenderManager::kTypeVideo && TwoStepFrameRendering()) {
|
||||
|
||||
// If not, check if it's in the filesystem
|
||||
if (!hash_exists) {
|
||||
hash_exists = QFileInfo::exists(viewer()->video_frame_cache()->CachePathName(p.hash));
|
||||
DownloadFrame(&watcher_thread,
|
||||
watcher->Get().value<FramePtr>(),
|
||||
watcher->property("hash").toByteArray());
|
||||
|
||||
// If so, add it to the list so we don't have to check the filesystem again later
|
||||
if (hash_exists) {
|
||||
existing_hashes.push_back(p.hash);
|
||||
}
|
||||
}
|
||||
|
||||
if (hash_exists) {
|
||||
// Already exists, no need to render it again
|
||||
FrameDownloaded(p.hash, {p.time}, hash_job_time);
|
||||
progress_counter += video_frame_sz;
|
||||
emit ProgressChanged(progress_counter / total_length);
|
||||
}
|
||||
}
|
||||
|
||||
// If no existing disk cache was found, queue it now
|
||||
if (!hash_exists) {
|
||||
render_lookup_table.push_back({p.hash, backend_->RenderFrame(p.time)});
|
||||
running_hashes.push_back(p.hash);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove first element
|
||||
frame_queue.pop_front();
|
||||
}
|
||||
|
||||
while (!IsCancelled() && !audio_queue.empty()) {
|
||||
audio_lookup_table.push_back({audio_queue.front(), backend_->RenderAudio(audio_queue.front())});
|
||||
audio_queue.pop_front();
|
||||
}
|
||||
|
||||
i = render_lookup_table.begin();
|
||||
|
||||
while (!IsCancelled() && i != render_lookup_table.end()) {
|
||||
if (i->frame_future->IsFinished()) {
|
||||
if (!i->frame_future->WasCancelled()) {
|
||||
FramePtr f = i->frame_future->Get().value<FramePtr>();
|
||||
|
||||
// Start multithreaded download here
|
||||
download_futures.push_back({i->hash, DownloadFrame(f, i->hash), i->frame_future->GetJobTime()});
|
||||
}
|
||||
|
||||
i = render_lookup_table.erase(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
j = download_futures.begin();
|
||||
|
||||
while (!IsCancelled() && j != download_futures.end()) {
|
||||
if (j->download_future.isFinished()) {
|
||||
// Place it in the cache
|
||||
std::list<rational> times_with_hash;
|
||||
|
||||
for (int hash_index=0;hash_index<hashes.size();hash_index++) {
|
||||
if (hashes.at(hash_index) == j->hash) {
|
||||
times_with_hash.push_back(times.at(hash_index));
|
||||
}
|
||||
}
|
||||
|
||||
FrameDownloaded(j->hash, times_with_hash, j->job_time);
|
||||
|
||||
existing_hashes.push_back(j->hash);
|
||||
|
||||
// Signal process
|
||||
progress_counter += times_with_hash.size() * video_frame_sz;
|
||||
progress_counter += video_frame_sz * 0.5;
|
||||
emit ProgressChanged(progress_counter / total_length);
|
||||
|
||||
j = download_futures.erase(j);
|
||||
|
||||
} else {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
k = audio_lookup_table.begin();
|
||||
// Assume single-step video or video download ticket
|
||||
QByteArray rendered_hash = watcher->property("hash").toByteArray();
|
||||
FrameDownloaded(watcher->Get().value<FramePtr>(), rendered_hash, time_map.value(rendered_hash), job_time);
|
||||
|
||||
while (!IsCancelled() && k != audio_lookup_table.end()) {
|
||||
if (k->sample_future->IsFinished()) {
|
||||
AudioDownloaded(k->range,
|
||||
k->sample_future->Get().value<SampleBufferPtr>(),
|
||||
k->sample_future->GetJobTime());
|
||||
double progress_to_add = video_frame_sz;
|
||||
if (TwoStepFrameRendering()) {
|
||||
progress_to_add *= 0.5;
|
||||
}
|
||||
progress_counter += progress_to_add;
|
||||
|
||||
progress_counter += k->range.length().toDouble();
|
||||
emit ProgressChanged(progress_counter / total_length);
|
||||
|
||||
k = audio_lookup_table.erase(k);
|
||||
} else {
|
||||
k++;
|
||||
}
|
||||
|
||||
delete watcher;
|
||||
running_watchers_.removeOne(watcher);
|
||||
|
||||
finished_watcher_mutex_.lock();
|
||||
}
|
||||
|
||||
if (IsCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Run out of finished watchers. If we still have running tickets, wait for the next one to finish.
|
||||
if (running_tickets_ > 0) {
|
||||
finished_watcher_wait_cond_.wait(&finished_watcher_mutex_);
|
||||
} else {
|
||||
// No more running tickets or finished tickets, wem ust be
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// `Close` will block until all jobs are done making a safe deletion
|
||||
backend_->Close();
|
||||
finished_watcher_mutex_.unlock();
|
||||
|
||||
if (IsCancelled()) {
|
||||
// Cancel every watcher we created
|
||||
foreach (RenderTicketWatcher* watcher, running_watchers_) {
|
||||
disconnect(watcher, &RenderTicketWatcher::Finished, this, &RenderTask::TicketDone);
|
||||
watcher->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
watcher_thread.quit();
|
||||
watcher_thread.wait();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
void RenderTask::DownloadFrame(QThread *thread, FramePtr frame, const QByteArray &hash)
|
||||
{
|
||||
RenderTicketWatcher* watcher = CreateWatcher(thread);
|
||||
|
||||
watcher->setProperty("hash", hash);
|
||||
|
||||
IncrementRunningTickets();
|
||||
|
||||
watcher->SetTicket(RenderManager::instance()->SaveFrameToCache(viewer_->video_frame_cache(),
|
||||
frame,
|
||||
hash));
|
||||
}
|
||||
|
||||
RenderTicketWatcher *RenderTask::CreateWatcher(QThread *thread)
|
||||
{
|
||||
RenderTicketWatcher* watcher = new RenderTicketWatcher();
|
||||
watcher->moveToThread(thread);
|
||||
connect(watcher, &RenderTicketWatcher::Finished, this, &RenderTask::TicketDone, Qt::DirectConnection);
|
||||
running_watchers_.append(watcher);
|
||||
return watcher;
|
||||
}
|
||||
|
||||
void RenderTask::IncrementRunningTickets()
|
||||
{
|
||||
finished_watcher_mutex_.lock();
|
||||
running_tickets_++;
|
||||
finished_watcher_mutex_.unlock();
|
||||
}
|
||||
|
||||
void RenderTask::TicketDone(RenderTicketWatcher* watcher)
|
||||
{
|
||||
finished_watcher_mutex_.lock();
|
||||
finished_watchers_.push_back(watcher);
|
||||
finished_watcher_wait_cond_.wakeAll();
|
||||
running_tickets_--;
|
||||
finished_watcher_mutex_.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+47
-17
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -24,54 +24,84 @@
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "render/backend/opengl/openglbackend.h"
|
||||
#include "render/colormanager.h"
|
||||
#include "task/task.h"
|
||||
#include "threading/threadticket.h"
|
||||
#include "threading/threadticketwatcher.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
class RenderTask : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RenderTask(ViewerOutput* viewer, const VideoParams &vparams, const AudioParams &aparams);
|
||||
|
||||
virtual ~RenderTask() override;
|
||||
|
||||
protected:
|
||||
void Render(const TimeRangeList &video_range,
|
||||
const TimeRangeList &audio_range,
|
||||
bool use_disk_cache);
|
||||
bool Render(ColorManager *manager, const TimeRangeList &video_range,
|
||||
const TimeRangeList &audio_range, RenderMode::Mode mode,
|
||||
FrameHashCache *cache, const QSize& force_size = QSize(0, 0),
|
||||
const QMatrix4x4& force_matrix = QMatrix4x4(),
|
||||
VideoParams::Format force_format = VideoParams::kFormatInvalid,
|
||||
ColorProcessorPtr force_color_output = nullptr);
|
||||
|
||||
virtual QFuture<void> DownloadFrame(FramePtr frame, const QByteArray &hash) = 0;
|
||||
virtual void DownloadFrame(QThread* thread, FramePtr frame, const QByteArray &hash);
|
||||
|
||||
virtual void FrameDownloaded(const QByteArray& hash, const std::list<rational>& times, qint64 job_time) = 0;
|
||||
virtual void FrameDownloaded(FramePtr frame, const QByteArray& hash, const QVector<rational>& times, qint64 job_time) = 0;
|
||||
|
||||
virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples, qint64 job_time) = 0;
|
||||
|
||||
ViewerOutput* viewer() const
|
||||
{
|
||||
return backend_->GetViewerNode();
|
||||
return viewer_;
|
||||
}
|
||||
|
||||
VideoParams video_params() const
|
||||
const VideoParams& video_params() const
|
||||
{
|
||||
return backend_->GetVideoParams();
|
||||
return video_params_;
|
||||
}
|
||||
|
||||
AudioParams audio_params() const
|
||||
const AudioParams& audio_params() const
|
||||
{
|
||||
return backend_->GetAudioParams();
|
||||
return audio_params_;
|
||||
}
|
||||
|
||||
RenderBackend* backend()
|
||||
virtual void CancelEvent() override
|
||||
{
|
||||
return backend_;
|
||||
finished_watcher_mutex_.lock();
|
||||
finished_watcher_wait_cond_.wakeAll();
|
||||
finished_watcher_mutex_.unlock();
|
||||
}
|
||||
|
||||
virtual bool TwoStepFrameRendering() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
RenderBackend* backend_;
|
||||
RenderTicketWatcher* CreateWatcher(QThread *thread);
|
||||
|
||||
void IncrementRunningTickets();
|
||||
|
||||
ViewerOutput* viewer_;
|
||||
|
||||
VideoParams video_params_;
|
||||
|
||||
AudioParams audio_params_;
|
||||
|
||||
QVector<RenderTicketWatcher*> running_watchers_;
|
||||
std::list<RenderTicketWatcher*> finished_watchers_;
|
||||
int running_tickets_;
|
||||
QMutex finished_watcher_mutex_;
|
||||
QWaitCondition finished_watcher_wait_cond_;
|
||||
|
||||
private slots:
|
||||
void TicketDone(RenderTicketWatcher *watcher);
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // RENDERTASK_H
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include "common/cancelableobject.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief A base class for background tasks running in Olive.
|
||||
@@ -162,6 +162,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // TASK_H
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
TaskManager* TaskManager::instance_ = nullptr;
|
||||
|
||||
@@ -133,4 +133,4 @@ void TaskManager::TaskFinished()
|
||||
emit TaskListChanged();
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
Copyright (C) 2020 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
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief An object that manages background Task objects, handling their start and end
|
||||
@@ -136,6 +136,6 @@ private slots:
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
}
|
||||
|
||||
#endif // TASKMANAGER_H
|
||||
|
||||
Reference in New Issue
Block a user