diff --git a/debug.cpp b/debug.cpp
index 405cb010a..1dced8017 100644
--- a/debug.cpp
+++ b/debug.cpp
@@ -63,7 +63,7 @@ void debug_message_handler(QtMsgType type, const QMessageLogContext &context, co
if (debug_file.isOpen()) debug_stream << QString("[FATAL] %1 (%2:%3, %4)\n").arg(localMsg.constData(), context.file, QString::number(context.line), context.function);
debug_info.prepend(QString("[FATAL] %1 (%2:%3, %4)
").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
fflush(stderr);
- abort();
+// abort();
}
if (debug_dialog != nullptr && debug_dialog->isVisible()) {
QMetaObject::invokeMethod(debug_dialog, "update_log", Qt::QueuedConnection);
diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp
index d5c52e8fb..5ed83c0fd 100644
--- a/dialogs/exportdialog.cpp
+++ b/dialogs/exportdialog.cpp
@@ -57,7 +57,7 @@ enum ExportFormats {
ExportDialog::ExportDialog(QWidget *parent) :
QDialog(parent)
{
- setWindowTitle(tr("Export \"%1\"").arg(sequence->name));
+ setWindowTitle(tr("Export \"%1\"").arg(sequence->name));
setup_ui();
rangeCombobox->setCurrentIndex(0);
@@ -320,12 +320,12 @@ void ExportDialog::format_changed(int index)
void ExportDialog::render_thread_finished() {
if (progressBar->value() < 100 && !cancelled) {
- QMessageBox::critical(
- this,
- tr("Export Failed"),
- tr("Export failed - %1").arg(export_error),
- QMessageBox::Ok
- );
+ QMessageBox::critical(
+ this,
+ tr("Export Failed"),
+ tr("Export failed - %1").arg(export_error),
+ QMessageBox::Ok
+ );
}
prep_ui_for_render(false);
panel_sequence_viewer->viewer_widget->makeCurrent();
@@ -342,12 +342,12 @@ void ExportDialog::prep_ui_for_render(bool r) {
void ExportDialog::export_action() {
if (widthSpinbox->value()%2 == 1 || heightSpinbox->value()%2 == 1) {
- QMessageBox::critical(
- this,
- tr("Invalid dimensions"),
- tr("Export width and height must both be even numbers/divisible by 2."),
- QMessageBox::Ok
- );
+ QMessageBox::critical(
+ this,
+ tr("Invalid dimensions"),
+ tr("Export width and height must both be even numbers/divisible by 2."),
+ QMessageBox::Ok
+ );
return;
}
@@ -399,12 +399,12 @@ void ExportDialog::export_action() {
break;
default:
qCritical() << "Invalid codec selection for an image sequence";
- QMessageBox::critical(
- this,
- tr("Invalid codec"),
- tr("Couldn't determine output parameters for the selected codec. This is a bug, please contact the developers."),
- QMessageBox::Ok
- );
+ QMessageBox::critical(
+ this,
+ tr("Invalid codec"),
+ tr("Couldn't determine output parameters for the selected codec. This is a bug, please contact the developers."),
+ QMessageBox::Ok
+ );
return;
}
break;
@@ -469,20 +469,20 @@ void ExportDialog::export_action() {
break;
default:
qCritical() << "Invalid format - this is a bug, please inform the developers";
- QMessageBox::critical(
- this,
- tr("Invalid format"),
- tr("Couldn't determine output format. This is a bug, please contact the developers."),
- QMessageBox::Ok
- );
+ QMessageBox::critical(
+ this,
+ tr("Invalid format"),
+ tr("Couldn't determine output format. This is a bug, please contact the developers."),
+ QMessageBox::Ok
+ );
return;
}
- QString filename = QFileDialog::getSaveFileName(
- this,
- tr("Export Media"),
- "",
- format_strings[formatCombobox->currentIndex()] + " (*." + ext + ")"
- );
+ QString filename = QFileDialog::getSaveFileName(
+ this,
+ tr("Export Media"),
+ "",
+ format_strings[formatCombobox->currentIndex()] + " (*." + ext + ")"
+ );
if (!filename.isEmpty()) {
if (!filename.endsWith("." + ext, Qt::CaseInsensitive)) {
filename += "." + ext;
@@ -506,11 +506,9 @@ void ExportDialog::export_action() {
closeActiveClips(sequence);
- mainWindow->autorecover_interval();
+ mainWindow->set_rendering_state(true);
- rendering = true;
- panel_sequence_viewer->viewer_widget->context()->doneCurrent();
- panel_sequence_viewer->viewer_widget->context()->moveToThread(et);
+ mainWindow->autorecover_interval();
prep_ui_for_render(true);
@@ -556,20 +554,20 @@ void ExportDialog::update_progress_bar(int value, qint64 remaining_ms) {
}
void ExportDialog::cancel_render() {
- panel_sequence_viewer->viewer_widget->force_quit = true;
et->continueEncode = false;
cancelled = true;
+ et->wake();
}
void ExportDialog::vcodec_changed(int index) {
compressionTypeCombobox->clear();
if ((format_vcodecs.size() > 0 && format_vcodecs.at(index) == AV_CODEC_ID_H264)) {
compressionTypeCombobox->setEnabled(true);
- compressionTypeCombobox->addItem(tr("Quality-based (Constant Rate Factor)"), COMPRESSION_TYPE_CFR);
+ compressionTypeCombobox->addItem(tr("Quality-based (Constant Rate Factor)"), COMPRESSION_TYPE_CFR);
// compressionTypeCombobox->addItem("File size-based (Two-Pass)", COMPRESSION_TYPE_TARGETSIZE);
// compressionTypeCombobox->addItem("Average bitrate (Two-Pass)", COMPRESSION_TYPE_TARGETBR);
} else {
- compressionTypeCombobox->addItem(tr("Constant Bitrate"), COMPRESSION_TYPE_CBR);
+ compressionTypeCombobox->addItem(tr("Constant Bitrate"), COMPRESSION_TYPE_CBR);
compressionTypeCombobox->setCurrentIndex(0);
compressionTypeCombobox->setEnabled(false);
}
@@ -582,17 +580,17 @@ void ExportDialog::comp_type_changed(int) {
switch (compressionTypeCombobox->currentData().toInt()) {
case COMPRESSION_TYPE_CBR:
case COMPRESSION_TYPE_TARGETBR:
- videoBitrateLabel->setText(tr("Bitrate (Mbps):"));
+ videoBitrateLabel->setText(tr("Bitrate (Mbps):"));
videobitrateSpinbox->setValue(qMax(0.5, (double) qRound((0.01528 * sequence->height) - 4.5)));
break;
case COMPRESSION_TYPE_CFR:
- videoBitrateLabel->setText(tr("Quality (CRF):"));
+ videoBitrateLabel->setText(tr("Quality (CRF):"));
videobitrateSpinbox->setValue(36);
videobitrateSpinbox->setMaximum(51);
- videobitrateSpinbox->setToolTip(tr("Quality Factor:\n\n0 = lossless\n17-18 = visually lossless (compressed, but unnoticeable)\n23 = high quality\n51 = lowest quality possible"));
+ videobitrateSpinbox->setToolTip(tr("Quality Factor:\n\n0 = lossless\n17-18 = visually lossless (compressed, but unnoticeable)\n23 = high quality\n51 = lowest quality possible"));
break;
case COMPRESSION_TYPE_TARGETSIZE:
- videoBitrateLabel->setText(tr("Target File Size (MB):"));
+ videoBitrateLabel->setText(tr("Target File Size (MB):"));
videobitrateSpinbox->setValue(100);
break;
}
@@ -601,57 +599,57 @@ void ExportDialog::comp_type_changed(int) {
void ExportDialog::setup_ui() {
QVBoxLayout* verticalLayout = new QVBoxLayout(this);
- QHBoxLayout* format_layout = new QHBoxLayout();
+ QHBoxLayout* format_layout = new QHBoxLayout();
- format_layout->addWidget(new QLabel(tr("Format:")));
+ format_layout->addWidget(new QLabel(tr("Format:")));
formatCombobox = new QComboBox(this);
- format_layout->addWidget(formatCombobox);
+ format_layout->addWidget(formatCombobox);
- verticalLayout->addLayout(format_layout);
+ verticalLayout->addLayout(format_layout);
- QHBoxLayout* range_layout = new QHBoxLayout();
+ QHBoxLayout* range_layout = new QHBoxLayout();
- range_layout->addWidget(new QLabel(tr("Range:")));
+ range_layout->addWidget(new QLabel(tr("Range:")));
rangeCombobox = new QComboBox(this);
- rangeCombobox->addItem(tr("Entire Sequence"));
- rangeCombobox->addItem(tr("In to Out"));
+ rangeCombobox->addItem(tr("Entire Sequence"));
+ rangeCombobox->addItem(tr("In to Out"));
- range_layout->addWidget(rangeCombobox);
+ range_layout->addWidget(rangeCombobox);
- verticalLayout->addLayout(range_layout);
+ verticalLayout->addLayout(range_layout);
videoGroupbox = new QGroupBox(this);
- videoGroupbox->setTitle(tr("Video"));
+ videoGroupbox->setTitle(tr("Video"));
videoGroupbox->setFlat(false);
videoGroupbox->setCheckable(true);
QGridLayout* videoGridLayout = new QGridLayout(videoGroupbox);
- videoGridLayout->addWidget(new QLabel(tr("Codec:")), 0, 0, 1, 1);
+ videoGridLayout->addWidget(new QLabel(tr("Codec:")), 0, 0, 1, 1);
vcodecCombobox = new QComboBox(videoGroupbox);
videoGridLayout->addWidget(vcodecCombobox, 0, 1, 1, 1);
- videoGridLayout->addWidget(new QLabel(tr("Width:")), 1, 0, 1, 1);
+ videoGridLayout->addWidget(new QLabel(tr("Width:")), 1, 0, 1, 1);
widthSpinbox = new QSpinBox(videoGroupbox);
widthSpinbox->setMaximum(16777216);
videoGridLayout->addWidget(widthSpinbox, 1, 1, 1, 1);
- videoGridLayout->addWidget(new QLabel(tr("Height:")), 2, 0, 1, 1);
+ videoGridLayout->addWidget(new QLabel(tr("Height:")), 2, 0, 1, 1);
heightSpinbox = new QSpinBox(videoGroupbox);
heightSpinbox->setMaximum(16777216);
videoGridLayout->addWidget(heightSpinbox, 2, 1, 1, 1);
- videoGridLayout->addWidget(new QLabel(tr("Frame Rate:")), 3, 0, 1, 1);
+ videoGridLayout->addWidget(new QLabel(tr("Frame Rate:")), 3, 0, 1, 1);
framerateSpinbox = new QDoubleSpinBox(videoGroupbox);
framerateSpinbox->setMaximum(60);
framerateSpinbox->setValue(0);
videoGridLayout->addWidget(framerateSpinbox, 3, 1, 1, 1);
- videoGridLayout->addWidget(new QLabel(tr("Compression Type:")), 4, 0, 1, 1);
- compressionTypeCombobox = new QComboBox(videoGroupbox);
+ videoGridLayout->addWidget(new QLabel(tr("Compression Type:")), 4, 0, 1, 1);
+ compressionTypeCombobox = new QComboBox(videoGroupbox);
videoGridLayout->addWidget(compressionTypeCombobox, 4, 1, 1, 1);
videoBitrateLabel = new QLabel(videoGroupbox);
@@ -669,17 +667,17 @@ void ExportDialog::setup_ui() {
QGridLayout* audioGridLayout = new QGridLayout(audioGroupbox);
- audioGridLayout->addWidget(new QLabel(tr("Codec:")), 0, 0, 1, 1);
+ audioGridLayout->addWidget(new QLabel(tr("Codec:")), 0, 0, 1, 1);
acodecCombobox = new QComboBox(audioGroupbox);
audioGridLayout->addWidget(acodecCombobox, 0, 1, 1, 1);
- audioGridLayout->addWidget(new QLabel(tr("Sampling Rate:")), 1, 0, 1, 1);
+ audioGridLayout->addWidget(new QLabel(tr("Sampling Rate:")), 1, 0, 1, 1);
samplingRateSpinbox = new QSpinBox(audioGroupbox);
samplingRateSpinbox->setMaximum(96000);
samplingRateSpinbox->setValue(0);
audioGridLayout->addWidget(samplingRateSpinbox, 1, 1, 1, 1);
- audioGridLayout->addWidget(new QLabel(tr("Bitrate (Kbps/CBR):")), 3, 0, 1, 1);
+ audioGridLayout->addWidget(new QLabel(tr("Bitrate (Kbps/CBR):")), 3, 0, 1, 1);
audiobitrateSpinbox = new QSpinBox(audioGroupbox);
audiobitrateSpinbox->setMaximum(320);
audiobitrateSpinbox->setValue(256);
diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp
index 05b46c33f..12368b18a 100644
--- a/effects/internal/texteffect.cpp
+++ b/effects/internal/texteffect.cpp
@@ -29,49 +29,49 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) :
enable_superimpose = true;
//enable_shader = true;
- text_val = add_row(tr("Text"))->add_field(EFFECT_FIELD_STRING, "text", 2);
+ text_val = add_row(tr("Text"))->add_field(EFFECT_FIELD_STRING, "text", 2);
QTextEdit* text_widget = static_cast(text_val->ui_element);
text_widget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(text_widget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(text_edit_menu()));
- set_font_combobox = add_row(tr("Font"))->add_field(EFFECT_FIELD_FONT, "font", 2);
+ set_font_combobox = add_row(tr("Font"))->add_field(EFFECT_FIELD_FONT, "font", 2);
- size_val = add_row(tr("Size"))->add_field(EFFECT_FIELD_DOUBLE, "size", 2);
+ size_val = add_row(tr("Size"))->add_field(EFFECT_FIELD_DOUBLE, "size", 2);
size_val->set_double_minimum_value(0);
- set_color_button = add_row(tr("Color"))->add_field(EFFECT_FIELD_COLOR, "color", 2);
+ set_color_button = add_row(tr("Color"))->add_field(EFFECT_FIELD_COLOR, "color", 2);
- EffectRow* alignment_row = add_row(tr("Alignment"));
+ EffectRow* alignment_row = add_row(tr("Alignment"));
halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "halign");
- halign_field->add_combo_item(tr("Left"), Qt::AlignLeft);
- halign_field->add_combo_item(tr("Center"), Qt::AlignHCenter);
- halign_field->add_combo_item(tr("Right"), Qt::AlignRight);
- halign_field->add_combo_item(tr("Justify"), Qt::AlignJustify);
+ halign_field->add_combo_item(tr("Left"), Qt::AlignLeft);
+ halign_field->add_combo_item(tr("Center"), Qt::AlignHCenter);
+ halign_field->add_combo_item(tr("Right"), Qt::AlignRight);
+ halign_field->add_combo_item(tr("Justify"), Qt::AlignJustify);
valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO, "valign");
- valign_field->add_combo_item(tr("Top"), Qt::AlignTop);
- valign_field->add_combo_item(tr("Center"), Qt::AlignVCenter);
- valign_field->add_combo_item(tr("Bottom"), Qt::AlignBottom);
+ valign_field->add_combo_item(tr("Top"), Qt::AlignTop);
+ valign_field->add_combo_item(tr("Center"), Qt::AlignVCenter);
+ valign_field->add_combo_item(tr("Bottom"), Qt::AlignBottom);
- word_wrap_field = add_row(tr("Word Wrap"))->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2);
+ word_wrap_field = add_row(tr("Word Wrap"))->add_field(EFFECT_FIELD_BOOL, "wordwrap", 2);
- outline_bool = add_row(tr("Outline"))->add_field(EFFECT_FIELD_BOOL, "outline", 2);
- outline_color = add_row(tr("Outline Color"))->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2);
- outline_width = add_row(tr("Outline Width"))->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2);
+ outline_bool = add_row(tr("Outline"))->add_field(EFFECT_FIELD_BOOL, "outline", 2);
+ outline_color = add_row(tr("Outline Color"))->add_field(EFFECT_FIELD_COLOR, "outlinecolor", 2);
+ outline_width = add_row(tr("Outline Width"))->add_field(EFFECT_FIELD_DOUBLE, "outlinewidth", 2);
outline_width->set_double_minimum_value(0);
- shadow_bool = add_row(tr("Shadow"))->add_field(EFFECT_FIELD_BOOL, "shadow", 2);
- shadow_color = add_row(tr("Shadow Color"))->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2);
- shadow_distance = add_row(tr("Shadow Distance"))->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2);
+ shadow_bool = add_row(tr("Shadow"))->add_field(EFFECT_FIELD_BOOL, "shadow", 2);
+ shadow_color = add_row(tr("Shadow Color"))->add_field(EFFECT_FIELD_COLOR, "shadowcolor", 2);
+ shadow_distance = add_row(tr("Shadow Distance"))->add_field(EFFECT_FIELD_DOUBLE, "shadowdistance", 2);
shadow_distance->set_double_minimum_value(0);
- shadow_softness = add_row(tr("Shadow Softness"))->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2);
+ shadow_softness = add_row(tr("Shadow Softness"))->add_field(EFFECT_FIELD_DOUBLE, "shadowsoftness", 2);
shadow_softness->set_double_minimum_value(0);
- shadow_opacity = add_row(tr("Shadow Opacity"))->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2);
+ shadow_opacity = add_row(tr("Shadow Opacity"))->add_field(EFFECT_FIELD_DOUBLE, "shadowopacity", 2);
shadow_opacity->set_double_minimum_value(0);
shadow_opacity->set_double_maximum_value(100);
size_val->set_double_default_value(48);
- text_val->set_string_value(tr("Sample Text"));
+ text_val->set_string_value(tr("Sample Text"));
halign_field->set_combo_index(1);
valign_field->set_combo_index(1);
word_wrap_field->set_bool_value(true);
@@ -94,7 +94,9 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) :
}
void TextEffect::redraw(double timecode) {
- img.fill(Qt::transparent);
+ QColor bkg = set_color_button->get_color_value(timecode);
+ bkg.setAlpha(0);
+ img.fill(bkg);
QPainter p(&img);
p.setRenderHint(QPainter::Antialiasing);
@@ -217,7 +219,7 @@ void TextEffect::shadow_enable(bool e) {
void TextEffect::text_edit_menu() {
QMenu menu;
- menu.addAction(tr("&Edit Text"), this, SLOT(open_text_edit()));
+ menu.addAction(tr("&Edit Text"), this, SLOT(open_text_edit()));
menu.exec(QCursor::pos());
}
diff --git a/io/exportthread.cpp b/io/exportthread.cpp
index 0931b12f3..5e4f1a34e 100644
--- a/io/exportthread.cpp
+++ b/io/exportthread.cpp
@@ -6,9 +6,12 @@
#include "panels/timeline.h"
#include "panels/viewer.h"
#include "ui/viewerwidget.h"
+#include "ui/renderthread.h"
+#include "ui/renderfunctions.h"
#include "playback/playback.h"
#include "playback/audio.h"
#include "dialogs/exportdialog.h"
+#include "mainwindow.h"
#include "debug.h"
extern "C" {
@@ -111,7 +114,7 @@ bool ExportThread::setupVideo() {
vcodec_ctx->sample_aspect_ratio = {1, 1};
vcodec_ctx->pix_fmt = vcodec->pix_fmts[0]; // maybe be breakable code
vcodec_ctx->framerate = av_d2q(video_frame_rate, INT_MAX);
- if (video_compression_type == COMPRESSION_TYPE_CBR) vcodec_ctx->bit_rate = video_bitrate * 1000000;
+ if (video_compression_type == COMPRESSION_TYPE_CBR) vcodec_ctx->bit_rate = qRound(video_bitrate * 1000000);
vcodec_ctx->time_base = av_inv_q(vcodec_ctx->framerate);
video_stream->time_base = vcodec_ctx->time_base;
@@ -312,12 +315,7 @@ bool ExportThread::setupContainer() {
void ExportThread::run() {
panel_sequence_viewer->pause();
-
- if (!panel_sequence_viewer->viewer_widget->context()->makeCurrent(&surface)) {
- qCritical() << "Make current failed";
- ed->export_error = tr("could not make OpenGL context current");
- return;
- }
+ panel_sequence_viewer->seek(start_frame);
// copy filename
QByteArray ba = filename.toUtf8();
@@ -339,28 +337,35 @@ void ExportThread::run() {
}
}
- panel_sequence_viewer->seek(start_frame);
- panel_sequence_viewer->reset_all_audio();
-
- QOpenGLFramebufferObject fbo(sequence->width, sequence->height, QOpenGLFramebufferObject::CombinedDepthStencil, GL_TEXTURE_RECTANGLE);
- fbo.bind();
-
- panel_sequence_viewer->viewer_widget->default_fbo = &fbo;
-
long file_audio_samples = 0;
qint64 start_time, frame_time, avg_time, eta, total_time = 0;
long remaining_frames, frame_count = 1;
+ RenderThread* renderer = panel_sequence_viewer->viewer_widget->get_renderer();
+ disconnect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget, SLOT(queue_repaint()));
+ connect(renderer, SIGNAL(ready()), this, SLOT(wake()));
+
+ mutex.lock();
+
while (sequence->playhead <= end_frame && continueEncode) {
start_time = QDateTime::currentMSecsSinceEpoch();
- panel_sequence_viewer->viewer_widget->paintGL();
+ if (audio_enabled) {
+ compose_audio(nullptr, sequence, true);
+ }
+ if (video_enabled) {
+ do {
+ // TODO optimize by rendering the next frame while encoding the last
+ renderer->start_render(nullptr, sequence, nullptr, video_frame->data[0]);
+ waitCond.wait(&mutex);
+ if (!continueEncode) break;
+ } while (renderer->did_texture_fail());
+ if (!continueEncode) break;
+ }
+ // encode last frame while rendering next frame
double timecode_secs = (double) (sequence->playhead-start_frame) / sequence->frame_rate;
if (video_enabled) {
- // get image from opengl
- glReadPixels(0, 0, video_frame->linesize[0]/4, sequence->height, GL_RGBA, GL_UNSIGNED_BYTE, video_frame->data[0]);
-
// change pixel format
sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize);
sws_frame->pts = qRound(timecode_secs/av_q2d(video_stream->time_base));
@@ -371,6 +376,7 @@ void ExportThread::run() {
if (audio_enabled) {
// do we need to encode more audio samples?
while (continueEncode && file_audio_samples <= (timecode_secs*audio_sampling_rate)) {
+ // copy samples from audio buffer to AVFrame
int adjusted_read = audio_ibuffer_read%audio_ibuffer_size;
int copylen = qMin(aframe_bytes, audio_ibuffer_size-adjusted_read);
memcpy(audio_frame->data[0], audio_ibuffer+adjusted_read, copylen);
@@ -405,20 +411,22 @@ void ExportThread::run() {
// qInfo() << "Encoded frame" << sequence->playhead << "- took" << frame_time << "ms (avg:" << avg_time << "ms, remaining:" << remaining_frames << ", ETA:" << eta << ")";
- emit progress_changed(qRound(((double) (sequence->playhead-start_frame) / (double) (end_frame-start_frame)) * 100), eta);
+ emit progress_changed(qRound((double(sequence->playhead-start_frame) / double(end_frame-start_frame)) * 100.0), eta);
sequence->playhead++;
frame_count++;
}
+ disconnect(renderer, SIGNAL(ready()), this, SLOT(wake()));
+ connect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget, SLOT(queue_repaint()));
+
+ mutex.unlock();
+
if (continueEncode) {
if (video_enabled) vpkt_alloc = true;
if (audio_enabled) apkt_alloc = true;
}
- panel_sequence_viewer->viewer_widget->default_fbo = nullptr;
- rendering = false;
-
- fbo.release();
+ mainWindow->set_rendering_state(false);
if (audio_enabled && continueEncode) {
// flush swresample
@@ -478,9 +486,8 @@ void ExportThread::run() {
}
delete [] c_filename;
+}
- panel_sequence_viewer->viewer_widget->context()->doneCurrent();
- panel_sequence_viewer->viewer_widget->context()->moveToThread(qApp->thread());
-
- rendering = false;
+void ExportThread::wake() {
+ waitCond.wakeAll();
}
diff --git a/io/exportthread.h b/io/exportthread.h
index 0710343db..b06ddc2c2 100644
--- a/io/exportthread.h
+++ b/io/exportthread.h
@@ -3,6 +3,8 @@
#include
#include
+#include
+#include
class ExportDialog;
struct AVFormatContext;
@@ -52,19 +54,21 @@ public:
bool continueEncode;
signals:
void progress_changed(int value, qint64 remaining_ms);
+public slots:
+ void wake();
private:
bool encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream, bool rescale);
bool setupVideo();
bool setupAudio();
bool setupContainer();
- AVFormatContext* fmt_ctx;
+ AVFormatContext* fmt_ctx;
AVStream* video_stream;
AVCodec* vcodec;
AVCodecContext* vcodec_ctx;
AVFrame* video_frame;
AVFrame* sws_frame;
- SwsContext* sws_ctx;
+ SwsContext* sws_ctx;
AVStream* audio_stream;
AVCodec* acodec;
AVFrame* audio_frame;
@@ -72,14 +76,17 @@ private:
AVCodecContext* acodec_ctx;
AVPacket video_pkt;
AVPacket audio_pkt;
- SwrContext* swr_ctx;
+ SwrContext* swr_ctx;
- bool vpkt_alloc;
- bool apkt_alloc;
+ bool vpkt_alloc;
+ bool apkt_alloc;
int aframe_bytes;
int ret;
char* c_filename;
+
+ QMutex mutex;
+ QWaitCondition waitCond;
};
#endif // EXPORTTHREAD_H
diff --git a/io/loadthread.cpp b/io/loadthread.cpp
index 957bb84aa..64d87ff9a 100644
--- a/io/loadthread.cpp
+++ b/io/loadthread.cpp
@@ -153,12 +153,12 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
if (type == LOAD_TYPE_VERSION) {
int proj_version = stream.readElementText().toInt();
if (proj_version < MIN_SAVE_VERSION && proj_version > SAVE_VERSION) {
- if (QMessageBox::warning(
- mainWindow,
- tr("Version Mismatch"),
- tr("This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?"),
- QMessageBox::Yes,
- QMessageBox::No) == QMessageBox::No) {
+ if (QMessageBox::warning(
+ mainWindow,
+ tr("Version Mismatch"),
+ tr("This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?"),
+ QMessageBox::Yes,
+ QMessageBox::No) == QMessageBox::No) {
show_err = false;
return false;
}
@@ -444,11 +444,11 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
if (!found) {
correct_clip->linked.removeAt(j);
j--;
- if (QMessageBox::warning(mainWindow,
- tr("Invalid Clip Link"),
- tr("This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?"),
- QMessageBox::Yes,
- QMessageBox::No) == QMessageBox::No) {
+ if (QMessageBox::warning(mainWindow,
+ tr("Invalid Clip Link"),
+ tr("This project contains an invalid clip link. It may be corrupt. Would you like to continue loading it?"),
+ QMessageBox::Yes,
+ QMessageBox::No) == QMessageBox::No) {
delete s;
return false;
}
@@ -601,7 +601,7 @@ void LoadThread::run() {
xml_error = false;
if (show_err) emit error();
} else if (stream.hasError()) {
- error_str = tr("%1 - Line: %2 Col: %3").arg(stream.errorString(), QString::number(stream.lineNumber()), QString::number(stream.columnNumber()));
+ error_str = tr("%1 - Line: %2 Col: %3").arg(stream.errorString(), QString::number(stream.lineNumber()), QString::number(stream.columnNumber()));
xml_error = true;
emit error();
cont = false;
@@ -641,15 +641,15 @@ void LoadThread::cancel() {
void LoadThread::error_func() {
if (xml_error) {
qCritical() << "Error parsing XML." << error_str;
- QMessageBox::critical(mainWindow,
- tr("XML Parsing Error"),
- tr("Couldn't load '%1'. %2").arg(project_url, error_str),
- QMessageBox::Ok);
+ QMessageBox::critical(mainWindow,
+ tr("XML Parsing Error"),
+ tr("Couldn't load '%1'. %2").arg(project_url, error_str),
+ QMessageBox::Ok);
} else {
- QMessageBox::critical(mainWindow,
- tr("Project Load Error"),
- tr("Error loading project: %1").arg(error_str),
- QMessageBox::Ok);
+ QMessageBox::critical(mainWindow,
+ tr("Project Load Error"),
+ tr("Error loading project: %1").arg(error_str),
+ QMessageBox::Ok);
}
}
diff --git a/main.cpp b/main.cpp
index f2806308e..12e6f051e 100644
--- a/main.cpp
+++ b/main.cpp
@@ -20,7 +20,7 @@ int main(int argc, char *argv[]) {
bool launch_fullscreen = false;
QString load_proj;
- qInstallMessageHandler(debug_message_handler);
+// qInstallMessageHandler(debug_message_handler);
if (argc > 1) {
for (int i=1;iselect_all();
- } else if (focused_panel == panel_graph_editor) {
+ } else if (focused_panel == panel_graph_editor) {
panel_graph_editor->select_all();
}
}
@@ -452,7 +461,7 @@ void MainWindow::open_speed_dialog() {
SpeedDialog s(this);
for (int i=0;iclips.size();i++) {
Clip* c = sequence->clips.at(i);
- if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
+ if (c != nullptr && is_clip_selected(c, true)) {
s.clips.append(c);
}
}
@@ -502,7 +511,7 @@ void MainWindow::new_project() {
}
void MainWindow::autorecover_interval() {
- if (!rendering && isWindowModified()) {
+ if (isWindowModified()) {
panel_project->save_project(true);
qInfo() << "Auto-recovery project saved";
}
@@ -972,6 +981,9 @@ void MainWindow::closeEvent(QCloseEvent *e) {
set_sequence(nullptr);
+ panel_footage_viewer->viewer_widget->close_window();
+ panel_sequence_viewer->viewer_widget->close_window();
+
panel_footage_viewer->set_main_sequence();
QString data_dir = get_data_path();
@@ -1051,78 +1063,78 @@ void MainWindow::reset_layout() {
}
void MainWindow::go_to_in() {
- QDockWidget* focused_panel = get_focused_panel();
- if (focused_panel == panel_footage_viewer) {
- panel_footage_viewer->go_to_in();
- } else {
- panel_sequence_viewer->go_to_in();
- }
+ QDockWidget* focused_panel = get_focused_panel();
+ if (focused_panel == panel_footage_viewer) {
+ panel_footage_viewer->go_to_in();
+ } else {
+ panel_sequence_viewer->go_to_in();
+ }
}
void MainWindow::go_to_out() {
- QDockWidget* focused_panel = get_focused_panel();
- if (focused_panel == panel_footage_viewer) {
- panel_footage_viewer->go_to_out();
- } else {
- panel_sequence_viewer->go_to_out();
- }
+ QDockWidget* focused_panel = get_focused_panel();
+ if (focused_panel == panel_footage_viewer) {
+ panel_footage_viewer->go_to_out();
+ } else {
+ panel_sequence_viewer->go_to_out();
+ }
}
void MainWindow::go_to_start() {
- QDockWidget* focused_panel = get_focused_panel();
- if (focused_panel == panel_footage_viewer) {
- panel_footage_viewer->go_to_start();
- } else {
- panel_sequence_viewer->go_to_start();
- }
+ QDockWidget* focused_panel = get_focused_panel();
+ if (focused_panel == panel_footage_viewer) {
+ panel_footage_viewer->go_to_start();
+ } else {
+ panel_sequence_viewer->go_to_start();
+ }
}
void MainWindow::prev_frame() {
- QDockWidget* focused_panel = get_focused_panel();
- if (focused_panel == panel_footage_viewer) {
- panel_footage_viewer->previous_frame();
- } else {
- panel_sequence_viewer->previous_frame();
- }
+ QDockWidget* focused_panel = get_focused_panel();
+ if (focused_panel == panel_footage_viewer) {
+ panel_footage_viewer->previous_frame();
+ } else {
+ panel_sequence_viewer->previous_frame();
+ }
}
void MainWindow::next_frame() {
- QDockWidget* focused_panel = get_focused_panel();
- if (focused_panel == panel_footage_viewer) {
- panel_footage_viewer->next_frame();
- } else {
- panel_sequence_viewer->next_frame();
- }
+ QDockWidget* focused_panel = get_focused_panel();
+ if (focused_panel == panel_footage_viewer) {
+ panel_footage_viewer->next_frame();
+ } else {
+ panel_sequence_viewer->next_frame();
+ }
}
void MainWindow::go_to_end() {
- QDockWidget* focused_panel = get_focused_panel();
- if (focused_panel == panel_footage_viewer) {
- panel_footage_viewer->go_to_end();
- } else {
- panel_sequence_viewer->go_to_end();
- }
+ QDockWidget* focused_panel = get_focused_panel();
+ if (focused_panel == panel_footage_viewer) {
+ panel_footage_viewer->go_to_end();
+ } else {
+ panel_sequence_viewer->go_to_end();
+ }
}
void MainWindow::playpause() {
- QDockWidget* focused_panel = get_focused_panel();
- if (focused_panel == panel_footage_viewer) {
- panel_footage_viewer->toggle_play();
- } else {
- panel_sequence_viewer->toggle_play();
- }
+ QDockWidget* focused_panel = get_focused_panel();
+ if (focused_panel == panel_footage_viewer) {
+ panel_footage_viewer->toggle_play();
+ } else {
+ panel_sequence_viewer->toggle_play();
+ }
}
void MainWindow::prev_cut() {
- QDockWidget* focused_panel = get_focused_panel();
- if (sequence != nullptr && (panel_timeline == focused_panel || panel_sequence_viewer == focused_panel)) {
+ QDockWidget* focused_panel = get_focused_panel();
+ if (sequence != nullptr && (panel_timeline == focused_panel || panel_sequence_viewer == focused_panel)) {
panel_timeline->previous_cut();
}
}
void MainWindow::next_cut() {
- QDockWidget* focused_panel = get_focused_panel();
- if (sequence != nullptr && (panel_timeline == focused_panel || panel_sequence_viewer == focused_panel)) {
+ QDockWidget* focused_panel = get_focused_panel();
+ if (sequence != nullptr && (panel_timeline == focused_panel || panel_sequence_viewer == focused_panel)) {
panel_timeline->next_cut();
}
}
@@ -1404,7 +1416,7 @@ void MainWindow::toggle_enable_clips() {
bool push_undo = false;
for (int i=0;iclips.size();i++) {
Clip* c = sequence->clips.at(i);
- if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
+ if (c != nullptr && is_clip_selected(c, true)) {
ca->append(new SetEnableCommand(c, !c->enabled));
push_undo = true;
}
@@ -1419,13 +1431,13 @@ void MainWindow::toggle_enable_clips() {
}
void MainWindow::edit_to_in_point() {
- QDockWidget* focused_panel = get_focused_panel();
- if (focused_panel == panel_timeline) panel_timeline->ripple_to_in_point(true, false);
+ QDockWidget* focused_panel = get_focused_panel();
+ if (focused_panel == panel_timeline) panel_timeline->ripple_to_in_point(true, false);
}
void MainWindow::edit_to_out_point() {
- QDockWidget* focused_panel = get_focused_panel();
- if (focused_panel == panel_timeline) panel_timeline->ripple_to_in_point(false, false);
+ QDockWidget* focused_panel = get_focused_panel();
+ if (focused_panel == panel_timeline) panel_timeline->ripple_to_in_point(false, false);
}
void MainWindow::nest() {
@@ -1436,7 +1448,7 @@ void MainWindow::nest() {
// get selected clips
for (int i=0;iclips.size();i++) {
Clip* c = sequence->clips.at(i);
- if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
+ if (c != nullptr && is_clip_selected(c, true)) {
selected_clips.append(i);
earliest_point = qMin(c->timeline_in, earliest_point);
}
@@ -1488,8 +1500,8 @@ void MainWindow::nest() {
}
void MainWindow::paste_insert() {
- QDockWidget* focused_panel = get_focused_panel();
- if (focused_panel == panel_timeline && sequence != nullptr) {
+ QDockWidget* focused_panel = get_focused_panel();
+ if (focused_panel == panel_timeline && sequence != nullptr) {
panel_timeline->paste(true);
}
}
@@ -1507,11 +1519,11 @@ void MainWindow::set_autoscroll() {
}
void MainWindow::menu_click_button() {
- QDockWidget* focused_panel = get_focused_panel();
- if (focused_panel == panel_timeline
- || focused_panel == panel_effect_controls
- || focused_panel == panel_footage_viewer
- || focused_panel == panel_sequence_viewer)
+ QDockWidget* focused_panel = get_focused_panel();
+ if (focused_panel == panel_timeline
+ || focused_panel == panel_effect_controls
+ || focused_panel == panel_footage_viewer
+ || focused_panel == panel_sequence_viewer)
reinterpret_cast(static_cast(sender())->data().value())->click();
}
diff --git a/mainwindow.h b/mainwindow.h
index 490c75097..42b5b8ea8 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -23,7 +23,9 @@ public:
void load_shortcuts(const QString &fn, bool first = false);
void save_shortcuts(const QString &fn);
- void load_css_from_file(const QString& fn);
+ void load_css_from_file(const QString& fn);
+
+ void set_rendering_state(bool rendering);
public slots:
void undo();
diff --git a/olive.pro b/olive.pro
index e47ba1c0a..6cc52430a 100644
--- a/olive.pro
+++ b/olive.pro
@@ -124,7 +124,10 @@ SOURCES += \
effects/internal/fillleftrighteffect.cpp \
effects/internal/voideffect.cpp \
dialogs/texteditdialog.cpp \
- dialogs/debugdialog.cpp
+ dialogs/debugdialog.cpp \
+ ui/renderthread.cpp \
+ ui/renderfunctions.cpp \
+ ui/viewerwindow.cpp
HEADERS += \
mainwindow.h \
@@ -216,7 +219,10 @@ HEADERS += \
effects/internal/fillleftrighteffect.h \
effects/internal/voideffect.h \
dialogs/texteditdialog.h \
- dialogs/debugdialog.h
+ dialogs/debugdialog.h \
+ ui/renderthread.h \
+ ui/renderfunctions.h \
+ ui/viewerwindow.h
FORMS +=
diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp
index 392423e89..d9ca18dad 100644
--- a/panels/effectcontrols.cpp
+++ b/panels/effectcontrols.cpp
@@ -34,7 +34,7 @@ EffectControls::EffectControls(QWidget *parent) :
QDockWidget(parent),
multiple(false),
zoom(1),
- panel_name(tr("Effects: ")),
+ panel_name(tr("Effects: ")),
mode(TA_NO_TRANSITION)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
@@ -94,7 +94,7 @@ void EffectControls::menu_select(QAction* q) {
update_ui(true);
} else {
reload_clips();
- panel_sequence_viewer->viewer_widget->update();
+ panel_sequence_viewer->viewer_widget->frame_update();
}
}
@@ -303,7 +303,7 @@ void EffectControls::setup_ui() {
QPushButton* btnAddVideoEffect = new QPushButton(veHeader);
btnAddVideoEffect->setIcon(QIcon(":/icons/add-effect.png"));
- btnAddVideoEffect->setToolTip(tr("Add Video Effect"));
+ btnAddVideoEffect->setToolTip(tr("Add Video Effect"));
veHeaderLayout->addWidget(btnAddVideoEffect);
connect(btnAddVideoEffect, SIGNAL(clicked(bool)), this, SLOT(video_effect_click()));
@@ -314,14 +314,14 @@ void EffectControls::setup_ui() {
font.setPointSize(9);
lblVideoEffects->setFont(font);
lblVideoEffects->setAlignment(Qt::AlignCenter);
- lblVideoEffects->setText(tr("VIDEO EFFECTS"));
+ lblVideoEffects->setText(tr("VIDEO EFFECTS"));
veHeaderLayout->addWidget(lblVideoEffects);
veHeaderLayout->addStretch();
QPushButton* btnAddVideoTransition = new QPushButton(veHeader);
btnAddVideoTransition->setIcon(QIcon(":/icons/add-transition.png"));
- btnAddVideoTransition->setToolTip(tr("Add Video Transition"));
+ btnAddVideoTransition->setToolTip(tr("Add Video Transition"));
connect(btnAddVideoTransition, SIGNAL(clicked(bool)), this, SLOT(video_transition_click()));
veHeaderLayout->addWidget(btnAddVideoTransition);
@@ -351,7 +351,7 @@ void EffectControls::setup_ui() {
QPushButton* btnAddAudioEffect = new QPushButton(aeHeader);
btnAddAudioEffect->setIcon(QIcon(":/icons/add-effect.png"));
- btnAddAudioEffect->setToolTip(tr("Add Audio Effect"));
+ btnAddAudioEffect->setToolTip(tr("Add Audio Effect"));
connect(btnAddAudioEffect, SIGNAL(clicked(bool)), this, SLOT(audio_effect_click()));
aeHeaderLayout->addWidget(btnAddAudioEffect);
@@ -360,14 +360,14 @@ void EffectControls::setup_ui() {
QLabel* lblAudioEffects = new QLabel(aeHeader);
lblAudioEffects->setFont(font);
lblAudioEffects->setAlignment(Qt::AlignCenter);
- lblAudioEffects->setText(tr("AUDIO EFFECTS"));
+ lblAudioEffects->setText(tr("AUDIO EFFECTS"));
aeHeaderLayout->addWidget(lblAudioEffects);
aeHeaderLayout->addStretch();
QPushButton* btnAddAudioTransition = new QPushButton(aeHeader);
btnAddAudioTransition->setIcon(QIcon(":/icons/add-transition.png"));
- btnAddAudioTransition->setToolTip(tr("Add Audio Transition"));
+ btnAddAudioTransition->setToolTip(tr("Add Audio Transition"));
connect(btnAddAudioTransition, SIGNAL(clicked(bool)), this, SLOT(audio_transition_click()));
aeHeaderLayout->addWidget(btnAddAudioTransition);
@@ -384,7 +384,7 @@ void EffectControls::setup_ui() {
lblMultipleClipsSelected = new QLabel(effects_area);
lblMultipleClipsSelected->setAlignment(Qt::AlignCenter);
- lblMultipleClipsSelected->setText(tr("(Multiple clips selected)"));
+ lblMultipleClipsSelected->setText(tr("(Multiple clips selected)"));
effects_area_layout->addWidget(lblMultipleClipsSelected);
effects_area_layout->addStretch();
diff --git a/panels/project.cpp b/panels/project.cpp
index b12ee265d..035d58a0b 100644
--- a/panels/project.cpp
+++ b/panels/project.cpp
@@ -913,7 +913,7 @@ void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only,
}
// save_folder(stream, item, type, set_ids_only);
} else {
- int folder = (m->parentItem() != nullptr) ? m->parentItem()->temp_id : 0;
+ int folder = m->parentItem()->temp_id;
if (type == MEDIA_TYPE_FOOTAGE) {
Footage* f = m->to_footage();
f->save_id = media_id;
diff --git a/panels/timeline.cpp b/panels/timeline.cpp
index ba20c7236..6216ed0bb 100644
--- a/panels/timeline.cpp
+++ b/panels/timeline.cpp
@@ -39,10 +39,6 @@
#include
#include
-long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) {
- return qRound((double(framenumber)/source_frame_rate)*target_frame_rate);
-}
-
Timeline::Timeline(QWidget *parent) :
QDockWidget(parent),
cursor_frame(0),
@@ -397,11 +393,11 @@ void Timeline::update_sequence() {
addButton->setEnabled(!null_sequence);
headers->setEnabled(!null_sequence);
- QString title = tr("Timeline: ");
+ QString title = tr("Timeline: ");
if (null_sequence) {
- setWindowTitle(title + tr(""));
+ setWindowTitle(title + tr(""));
} else {
- setWindowTitle(title + sequence->name);
+ setWindowTitle(title + sequence->name);
update_ui(false);
}
}
@@ -623,7 +619,7 @@ void Timeline::zoom_out() {
set_zoom(false);
}
-bool Timeline::is_clip_selected(Clip* clip, bool containing) {
+bool is_clip_selected(Clip* clip, bool containing) {
for (int i=0;isequence->selections.size();i++) {
const Selection& s = clip->sequence->selections.at(i);
if (clip->track == s.track && ((clip->timeline_in >= s.in && clip->timeline_out <= s.out && containing) ||
@@ -1008,15 +1004,15 @@ void Timeline::paste(bool insert) {
}
if (found >= 0 && ask_conflict) {
QMessageBox box(this);
- box.setWindowTitle(tr("Effect already exists"));
- box.setText(tr("Clip '%1' already contains a '%2' effect. Would you like to replace it with the pasted one or add it as a separate effect?").arg(c->name, e->meta->name));
+ box.setWindowTitle(tr("Effect already exists"));
+ box.setText(tr("Clip '%1' already contains a '%2' effect. Would you like to replace it with the pasted one or add it as a separate effect?").arg(c->name, e->meta->name));
box.setIcon(QMessageBox::Icon::Question);
- box.addButton(tr("Add"), QMessageBox::YesRole);
- QPushButton* replace_button = box.addButton(tr("Replace"), QMessageBox::NoRole);
- QPushButton* skip_button = box.addButton(tr("Skip"), QMessageBox::RejectRole);
+ box.addButton(tr("Add"), QMessageBox::YesRole);
+ QPushButton* replace_button = box.addButton(tr("Replace"), QMessageBox::NoRole);
+ QPushButton* skip_button = box.addButton(tr("Skip"), QMessageBox::RejectRole);
- QCheckBox* future_box = new QCheckBox(tr("Do this for all conflicts found"));
+ QCheckBox* future_box = new QCheckBox(tr("Do this for all conflicts found"));
box.setCheckBox(future_box);
box.exec();
@@ -1390,8 +1386,8 @@ void Timeline::set_marker() {
if (!add_marker) {
QInputDialog d(this);
- d.setWindowTitle(tr("Set Marker"));
- d.setLabelText(tr("Set marker name:"));
+ d.setWindowTitle(tr("Set Marker"));
+ d.setLabelText(tr("Set marker name:"));
d.setInputMode(QInputDialog::TextInput);
add_marker = (d.exec() == QDialog::Accepted);
marker_name = d.textValue();
@@ -1479,29 +1475,29 @@ void Timeline::add_btn_click() {
QMenu add_menu(this);
QAction* titleMenuItem = new QAction(&add_menu);
- titleMenuItem->setText(tr("Title..."));
+ titleMenuItem->setText(tr("Title..."));
titleMenuItem->setData(ADD_OBJ_TITLE);
add_menu.addAction(titleMenuItem);
QAction* solidMenuItem = new QAction(&add_menu);
- solidMenuItem->setText(tr("Solid Color..."));
+ solidMenuItem->setText(tr("Solid Color..."));
solidMenuItem->setData(ADD_OBJ_SOLID);
add_menu.addAction(solidMenuItem);
QAction* barsMenuItem = new QAction(&add_menu);
- barsMenuItem->setText(tr("Bars..."));
+ barsMenuItem->setText(tr("Bars..."));
barsMenuItem->setData(ADD_OBJ_BARS);
add_menu.addAction(barsMenuItem);
add_menu.addSeparator();
QAction* toneMenuItem = new QAction(&add_menu);
- toneMenuItem->setText(tr("Tone..."));
+ toneMenuItem->setText(tr("Tone..."));
toneMenuItem->setData(ADD_OBJ_TONE);
add_menu.addAction(toneMenuItem);
QAction* noiseMenuItem = new QAction(&add_menu);
- noiseMenuItem->setText(tr("Noise..."));
+ noiseMenuItem->setText(tr("Noise..."));
noiseMenuItem->setData(ADD_OBJ_NOISE);
add_menu.addAction(noiseMenuItem);
@@ -1523,16 +1519,16 @@ void Timeline::setScroll(int s) {
void Timeline::record_btn_click() {
if (project_url.isEmpty()) {
- QMessageBox::critical(this,
- tr("Unsaved Project"),
- tr("You must save this project before you can record audio in it."),
- QMessageBox::Ok);
+ QMessageBox::critical(this,
+ tr("Unsaved Project"),
+ tr("You must save this project before you can record audio in it."),
+ QMessageBox::Ok);
} else {
creating = true;
creating_object = ADD_OBJ_AUDIO;
- mainWindow->statusBar()->showMessage(
- tr("Click on the timeline where you want to start recording (drag to limit the recording to a certain timeframe)"),
- 10000);
+ mainWindow->statusBar()->showMessage(
+ tr("Click on the timeline where you want to start recording (drag to limit the recording to a certain timeframe)"),
+ 10000);
}
}
@@ -1611,7 +1607,7 @@ void Timeline::setup_ui() {
arrow_icon.addFile(QStringLiteral(":/icons/arrow-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
toolArrowButton->setIcon(arrow_icon);
toolArrowButton->setCheckable(true);
- toolArrowButton->setToolTip(tr("Pointer Tool") + " (V)");
+ toolArrowButton->setToolTip(tr("Pointer Tool") + " (V)");
toolArrowButton->setProperty("tool", TIMELINE_TOOL_POINTER);
connect(toolArrowButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolArrowButton);
@@ -1622,7 +1618,7 @@ void Timeline::setup_ui() {
icon1.addFile(QStringLiteral(":/icons/beam-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
toolEditButton->setIcon(icon1);
toolEditButton->setCheckable(true);
- toolEditButton->setToolTip(tr("Edit Tool") + " (X)");
+ toolEditButton->setToolTip(tr("Edit Tool") + " (X)");
toolEditButton->setProperty("tool", TIMELINE_TOOL_EDIT);
connect(toolEditButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolEditButton);
@@ -1633,7 +1629,7 @@ void Timeline::setup_ui() {
icon2.addFile(QStringLiteral(":/icons/ripple-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
toolRippleButton->setIcon(icon2);
toolRippleButton->setCheckable(true);
- toolRippleButton->setToolTip(tr("Ripple Tool") + " (B)");
+ toolRippleButton->setToolTip(tr("Ripple Tool") + " (B)");
toolRippleButton->setProperty("tool", TIMELINE_TOOL_RIPPLE);
connect(toolRippleButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolRippleButton);
@@ -1644,7 +1640,7 @@ void Timeline::setup_ui() {
icon4.addFile(QStringLiteral(":/icons/razor-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
toolRazorButton->setIcon(icon4);
toolRazorButton->setCheckable(true);
- toolRazorButton->setToolTip(tr("Razor Tool") + " (C)");
+ toolRazorButton->setToolTip(tr("Razor Tool") + " (C)");
toolRazorButton->setProperty("tool", TIMELINE_TOOL_RAZOR);
connect(toolRazorButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolRazorButton);
@@ -1655,7 +1651,7 @@ void Timeline::setup_ui() {
icon5.addFile(QStringLiteral(":/icons/slip-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
toolSlipButton->setIcon(icon5);
toolSlipButton->setCheckable(true);
- toolSlipButton->setToolTip(tr("Slip Tool") + " (Y)");
+ toolSlipButton->setToolTip(tr("Slip Tool") + " (Y)");
toolSlipButton->setProperty("tool", TIMELINE_TOOL_SLIP);
connect(toolSlipButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolSlipButton);
@@ -1666,7 +1662,7 @@ void Timeline::setup_ui() {
icon6.addFile(QStringLiteral(":/icons/slide-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
toolSlideButton->setIcon(icon6);
toolSlideButton->setCheckable(true);
- toolSlideButton->setToolTip(tr("Slide Tool") + " (U)");
+ toolSlideButton->setToolTip(tr("Slide Tool") + " (U)");
toolSlideButton->setProperty("tool", TIMELINE_TOOL_SLIDE);
connect(toolSlideButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolSlideButton);
@@ -1677,7 +1673,7 @@ void Timeline::setup_ui() {
icon7.addFile(QStringLiteral(":/icons/hand-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
toolHandButton->setIcon(icon7);
toolHandButton->setCheckable(true);
- toolHandButton->setToolTip(tr("Hand Tool") + " (H)");
+ toolHandButton->setToolTip(tr("Hand Tool") + " (H)");
toolHandButton->setProperty("tool", TIMELINE_TOOL_HAND);
connect(toolHandButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolHandButton);
@@ -1688,7 +1684,7 @@ void Timeline::setup_ui() {
icon8.addFile(QStringLiteral(":/icons/transition-tool-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
toolTransitionButton->setIcon(icon8);
toolTransitionButton->setCheckable(true);
- toolTransitionButton->setToolTip(tr("Transition Tool") + " (T)");
+ toolTransitionButton->setToolTip(tr("Transition Tool") + " (T)");
connect(toolTransitionButton, SIGNAL(clicked(bool)), this, SLOT(transition_tool_click()));
tool_buttons_layout->addWidget(toolTransitionButton);
@@ -1699,7 +1695,7 @@ void Timeline::setup_ui() {
snappingButton->setIcon(icon9);
snappingButton->setCheckable(true);
snappingButton->setChecked(true);
- snappingButton->setToolTip(tr("Snapping") + " (S)");
+ snappingButton->setToolTip(tr("Snapping") + " (S)");
connect(snappingButton, SIGNAL(toggled(bool)), this, SLOT(snapping_clicked(bool)));
tool_buttons_layout->addWidget(snappingButton);
@@ -1708,7 +1704,7 @@ void Timeline::setup_ui() {
icon10.addFile(QStringLiteral(":/icons/zoomin.png"), QSize(), QIcon::Normal, QIcon::On);
icon10.addFile(QStringLiteral(":/icons/zoomin-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
zoomInButton->setIcon(icon10);
- zoomInButton->setToolTip(tr("Zoom In") + " (=)");
+ zoomInButton->setToolTip(tr("Zoom In") + " (=)");
connect(zoomInButton, SIGNAL(clicked(bool)), this, SLOT(zoom_in()));
tool_buttons_layout->addWidget(zoomInButton);
@@ -1717,7 +1713,7 @@ void Timeline::setup_ui() {
icon11.addFile(QStringLiteral(":/icons/zoomout.png"), QSize(), QIcon::Normal, QIcon::On);
icon11.addFile(QStringLiteral(":/icons/zoomout-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
zoomOutButton->setIcon(icon11);
- zoomOutButton->setToolTip(tr("Zoom Out") + " (-)");
+ zoomOutButton->setToolTip(tr("Zoom Out") + " (-)");
connect(zoomOutButton, SIGNAL(clicked(bool)), this, SLOT(zoom_out()));
tool_buttons_layout->addWidget(zoomOutButton);
@@ -1726,7 +1722,7 @@ void Timeline::setup_ui() {
icon12.addFile(QStringLiteral(":/icons/record.png"), QSize(), QIcon::Normal, QIcon::On);
icon12.addFile(QStringLiteral(":/icons/record-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
recordButton->setIcon(icon12);
- recordButton->setToolTip(tr("Record audio"));
+ recordButton->setToolTip(tr("Record audio"));
connect(recordButton, SIGNAL(clicked(bool)), this, SLOT(record_btn_click()));
tool_buttons_layout->addWidget(recordButton);
@@ -1736,7 +1732,7 @@ void Timeline::setup_ui() {
icon13.addFile(QStringLiteral(":/icons/add-button.png"), QSize(), QIcon::Normal, QIcon::On);
icon13.addFile(QStringLiteral(":/icons/add-button-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
addButton->setIcon(icon13);
- addButton->setToolTip(tr("Add title, solid, bars, etc."));
+ addButton->setToolTip(tr("Add title, solid, bars, etc."));
connect(addButton, SIGNAL(clicked()), this, SLOT(add_btn_click()));
tool_buttons_layout->addWidget(addButton);
diff --git a/panels/timeline.h b/panels/timeline.h
index 02ef8240c..2254bfff9 100644
--- a/panels/timeline.h
+++ b/panels/timeline.h
@@ -35,7 +35,7 @@ struct Clip;
struct Footage;
struct FootageStream;
-long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate);
+bool is_clip_selected(Clip* clip, bool containing);
int getScreenPointFromFrame(double zoom, long frame);
long getFrameFromScreenPoint(double zoom, int x);
bool selection_contains_transition(const Selection& s, Clip* c, int type);
@@ -137,7 +137,6 @@ public:
// selecting functions
bool selecting;
int selection_offset;
- bool is_clip_selected(Clip* clip, bool containing);
void delete_selection(QVector &selections, bool ripple);
void select_all();
bool rect_select_init;
@@ -204,7 +203,7 @@ public:
QPushButton* snappingButton;
void scroll_to_frame(long frame);
- void select_from_playhead();
+ void select_from_playhead();
void resizeEvent(QResizeEvent *event);
public slots:
diff --git a/panels/viewer.cpp b/panels/viewer.cpp
index 7969e8445..c1bdfab2c 100644
--- a/panels/viewer.cpp
+++ b/panels/viewer.cpp
@@ -44,7 +44,7 @@ Viewer::Viewer(QWidget *parent) :
seq(nullptr),
created_sequence(false),
cue_recording_internal(false),
- panel_name(tr("Viewer: ")),
+ panel_name(tr("Viewer: ")),
minimum_zoom(1.0)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
@@ -107,7 +107,9 @@ void Viewer::reset_all_audio() {
for (int i=0;iclips.size();i++) {
Clip* c = seq->clips.at(i);
- if (c != nullptr) c->reset_audio();
+ if (c != nullptr) {
+ c->reset_audio();
+ }
}
}
clear_audio_ibuffer();
@@ -251,9 +253,9 @@ void Viewer::seek(long p) {
update_fx = true;
}
}
- update_parents(update_fx);
reset_all_audio();
audio_scrub = true;
+ update_parents(update_fx);
}
void Viewer::go_to_start() {
@@ -439,7 +441,7 @@ void Viewer::resizeEvent(QResizeEvent *) {
void Viewer::update_viewer() {
update_header_zoom();
- viewer_widget->update();
+ viewer_widget->frame_update();
if (seq != nullptr) update_playhead_timecode(seq->playhead);
update_end_timecode();
}
@@ -675,7 +677,7 @@ void Viewer::set_media(Media* m) {
viewer_widget->waveform = true;
viewer_widget->waveform_clip = c;
viewer_widget->waveform_ms = &audio_stream;
- viewer_widget->update();
+ viewer_widget->frame_update();
}
} else {
seq->audio_frequency = 48000;
@@ -785,12 +787,12 @@ void Viewer::set_sequence(bool main, Sequence *s) {
update_playhead_timecode(0);
update_end_timecode();
- setWindowTitle(panel_name + tr("(none)"));
+ setWindowTitle(panel_name + tr("(none)"));
}
update_header_zoom();
- viewer_widget->update();
+ viewer_widget->frame_update();
update();
}
diff --git a/playback/audio.cpp b/playback/audio.cpp
index 915c42f8e..19e2f656f 100644
--- a/playback/audio.cpp
+++ b/playback/audio.cpp
@@ -29,6 +29,7 @@ bool audio_scrub = false;
QMutex audio_write_lock;
QAudioInput* audio_input = nullptr;
QFile output_recording;
+bool audio_rendering = false;
bool recording = false;
qint8 audio_ibuffer[audio_ibuffer_size];
@@ -102,13 +103,15 @@ void stop_audio() {
void clear_audio_ibuffer() {
if (audio_thread != nullptr) audio_thread->lock.lock();
+ audio_write_lock.lock();
memset(audio_ibuffer, 0, audio_ibuffer_size);
audio_ibuffer_read = 0;
+ audio_write_lock.unlock();
if (audio_thread != nullptr) audio_thread->lock.unlock();
}
int current_audio_freq() {
- return rendering ? sequence->audio_frequency : audio_output->format().sampleRate();
+ return audio_rendering ? sequence->audio_frequency : audio_output->format().sampleRate();
}
int get_buffer_offset_from_frame(double framerate, long frame) {
@@ -292,7 +295,7 @@ bool start_recording() {
return false;
}
- QString audio_path = project_url + " " + QCoreApplication::translate("Audio", "Audio");
+ QString audio_path = project_url + " " + QCoreApplication::translate("Audio", "Audio");
QDir audio_dir(audio_path);
if (!audio_dir.exists() && !audio_dir.mkpath(".")) {
qCritical() << "Failed to create audio directory";
@@ -303,7 +306,7 @@ bool start_recording() {
int file_number = 0;
do {
file_number++;
- audio_filename = audio_path + "/" + QCoreApplication::translate("Audio", "Recording") + " " + QString::number(file_number) + ".wav";
+ audio_filename = audio_path + "/" + QCoreApplication::translate("Audio", "Recording") + " " + QString::number(file_number) + ".wav";
} while (QFile(audio_filename).exists());
output_recording.setFileName(audio_filename);
diff --git a/playback/audio.h b/playback/audio.h
index 081a92e78..c2836a6f0 100644
--- a/playback/audio.h
+++ b/playback/audio.h
@@ -41,6 +41,8 @@ extern int audio_ibuffer_read;
extern long audio_ibuffer_frame;
extern double audio_ibuffer_timecode;
extern bool audio_scrub;
+extern bool recording;
+extern bool audio_rendering;
void clear_audio_ibuffer();
int current_audio_freq();
@@ -54,6 +56,5 @@ int get_buffer_offset_from_frame(double framerate, long frame);
bool start_recording();
void stop_recording();
QString get_recorded_audio_filename();
-extern bool recording;
#endif // AUDIO_H
diff --git a/playback/cacher.cpp b/playback/cacher.cpp
index b6df93249..3066402e1 100644
--- a/playback/cacher.cpp
+++ b/playback/cacher.cpp
@@ -376,11 +376,13 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) {
qint16 new_sample = static_cast((frame->data[0][c->frame_sample_index+1] & 0xFF) << 8 | (frame->data[0][c->frame_sample_index] & 0xFF));
qint16 mixed_sample = mix_audio_sample(old_sample, new_sample);
- audio_ibuffer[upper_byte_index] = static_cast((mixed_sample >> 8) & 0xFF);
- audio_ibuffer[lower_byte_index] = static_cast(mixed_sample & 0xFF);
+ audio_ibuffer[upper_byte_index] = quint8((mixed_sample >> 8) & 0xFF);
+ audio_ibuffer[lower_byte_index] = quint8(mixed_sample & 0xFF);
c->audio_buffer_write+=2;
c->frame_sample_index+=2;
+
+ if (c->audio_reset) break;
}
#ifdef AUDIOWARNINGS
@@ -389,6 +391,8 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector& nests) {
audio_write_lock.unlock();
+ if (c->audio_reset) return;
+
if (scrubbing) {
if (audio_thread != nullptr) audio_thread->notifyReceiver();
}
@@ -558,7 +562,9 @@ void reset_cache(Clip* c, long target_frame) {
} else {
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
// clear current queue
+ c->queue_lock.lock();
c->queue_clear();
+ c->queue_lock.unlock();
// seeks to nearest keyframe (target_frame represents internal clip frame)
int64_t target_ts = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame));
diff --git a/playback/playback.cpp b/playback/playback.cpp
index 8a30a9c6d..4e16d8d54 100644
--- a/playback/playback.cpp
+++ b/playback/playback.cpp
@@ -33,9 +33,12 @@ extern "C" {
//#define GCF_DEBUG
#endif
-bool texture_failed = false;
bool rendering = false;
+long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) {
+ return qRound((double(framenumber)/source_frame_rate)*target_frame_rate);
+}
+
bool clip_uses_cacher(Clip* clip) {
return (clip->media == nullptr && clip->track >= 0) || (clip->media != nullptr && clip->media->get_type() == MEDIA_TYPE_FOOTAGE);
}
@@ -118,7 +121,7 @@ double get_timecode(Clip* c, long playhead) {
return ((double)(playhead-c->get_timeline_in_with_transition()+c->get_clip_in_with_transition())/(double)c->sequence->frame_rate);
}
-void get_clip_frame(Clip* c, long playhead) {
+void get_clip_frame(Clip* c, long playhead, bool& texture_failed) {
if (c->finished_opening) {
const FootageStream* ms = c->media->to_footage()->get_stream_from_file_index(c->track < 0, c->media_stream);
@@ -154,8 +157,10 @@ void get_clip_frame(Clip* c, long playhead) {
#endif
closest_frame = i;
break;
- } else if (c->queue.at(i)->pts > c->queue.at(closest_frame)->pts && c->queue.at(i)->pts < target_pts) {
- closest_frame = i;
+ } else {
+ if (c->queue.at(i)->pts > c->queue.at(closest_frame)->pts && c->queue.at(i)->pts < target_pts) {
+ closest_frame = i;
+ }
}
}
diff --git a/playback/playback.h b/playback/playback.h
index db4a80600..2c0a6cd17 100644
--- a/playback/playback.h
+++ b/playback/playback.h
@@ -9,9 +9,7 @@ struct ClipCache;
struct Sequence;
struct AVFrame;
-extern bool texture_failed;
-extern bool rendering;
-
+long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate);
bool clip_uses_cacher(Clip* clip);
void open_clip(Clip* clip, bool multithreaded);
void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector &nests);
@@ -20,7 +18,7 @@ void cache_audio_worker(Clip* c, bool write_A);
void cache_video_worker(Clip* c, long playhead);
void handle_media(Sequence* sequence, long playhead, bool multithreaded);
void reset_cache(Clip* c, long target_frame);
-void get_clip_frame(Clip* c, long playhead);
+void get_clip_frame(Clip* c, long playhead, bool &texture_failed);
double get_timecode(Clip* c, long playhead);
long playhead_to_clip_frame(Clip* c, long playhead);
diff --git a/project/effect.cpp b/project/effect.cpp
index 4d8109f43..e777335c1 100644
--- a/project/effect.cpp
+++ b/project/effect.cpp
@@ -537,7 +537,7 @@ int Effect::gizmo_count(){
void Effect::refresh() {}
void Effect::field_changed() {
- panel_sequence_viewer->viewer_widget->update();
+ panel_sequence_viewer->viewer_widget->frame_update();
panel_graph_editor->update_panel();
}
diff --git a/project/effect.h b/project/effect.h
index 413df6d9f..330426b2c 100644
--- a/project/effect.h
+++ b/project/effect.h
@@ -134,7 +134,7 @@ public:
Effect* copy(Clip* c);
void copy_field_keyframes(Effect *e);
- virtual void load(QXmlStreamReader& stream);
+ virtual void load(QXmlStreamReader& stream);
virtual void custom_load(QXmlStreamReader& stream);
virtual void save(QXmlStreamWriter& stream);
diff --git a/project/effectrow.cpp b/project/effectrow.cpp
index 9998ad189..d40c77bcf 100644
--- a/project/effectrow.cpp
+++ b/project/effectrow.cpp
@@ -31,9 +31,10 @@ EffectRow::EffectRow(Effect *parent, bool save, QGridLayout *uilayout, const QSt
column_count = 1;
- if (parent_effect->meta != nullptr
- && parent_effect->meta->type != EFFECT_TYPE_TRANSITION
- && keyframable) {
+ keyframe_nav = nullptr;
+ if (parent_effect->meta != nullptr
+ && parent_effect->meta->type != EFFECT_TYPE_TRANSITION
+ && keyframable) {
connect(label, SIGNAL(clicked()), this, SLOT(focus_row()));
keyframe_nav = new KeyframeNavigator();
@@ -53,7 +54,9 @@ bool EffectRow::isKeyframing() {
void EffectRow::setKeyframing(bool b) {
if (parent_effect->meta->type != EFFECT_TYPE_TRANSITION) {
keyframing = b;
- keyframe_nav->enable_keyframes(b);
+ if (keyframe_nav != nullptr) {
+ keyframe_nav->enable_keyframes(b);
+ }
}
}
@@ -64,10 +67,10 @@ void EffectRow::set_keyframe_enabled(bool enabled) {
set_keyframe_now(ca);
undo_stack.push(ca);
} else {
- if (QMessageBox::question(panel_effect_controls,
- tr("Disable Keyframes"),
- tr("Disabling keyframes will delete all current keyframes. Are you sure you want to do this?"),
- QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
+ if (QMessageBox::question(panel_effect_controls,
+ tr("Disable Keyframes"),
+ tr("Disabling keyframes will delete all current keyframes. Are you sure you want to do this?"),
+ QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
// clear
ComboAction* ca = new ComboAction();
for (int i=0;iroot = true;
+ make_root();
}
ProjectModel::~ProjectModel() {
destroy_root();
}
+void ProjectModel::make_root() {
+ root_item = new Media(nullptr);
+ root_item->temp_id = 0;
+ root_item->root = true;
+}
+
void ProjectModel::destroy_root() {
if (panel_sequence_viewer != nullptr) panel_sequence_viewer->viewer_widget->delete_function();
if (panel_footage_viewer != nullptr) panel_footage_viewer->viewer_widget->delete_function();
@@ -27,8 +32,7 @@ void ProjectModel::destroy_root() {
void ProjectModel::clear() {
beginResetModel();
destroy_root();
- root_item = new Media(0);
- root_item->root = true;
+ make_root();
endResetModel();
}
diff --git a/project/projectmodel.h b/project/projectmodel.h
index 03ea9eadb..349080b7a 100644
--- a/project/projectmodel.h
+++ b/project/projectmodel.h
@@ -9,9 +9,10 @@ class ProjectModel : public QAbstractItemModel
{
Q_OBJECT
public:
- ProjectModel(QObject* parent = 0);
+ ProjectModel(QObject* parent = nullptr);
~ProjectModel() override;
+ void make_root();
void destroy_root();
void clear();
Media* get_root();
diff --git a/ui/keyframenavigator.cpp b/ui/keyframenavigator.cpp
index b3bb95c32..8115ed42f 100644
--- a/ui/keyframenavigator.cpp
+++ b/ui/keyframenavigator.cpp
@@ -14,50 +14,58 @@ KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) {
QSize clock_size(clock_size_val, clock_size_val);
QSize button_size(button_size_val, button_size_val);
- key_controls = new QHBoxLayout();
- key_controls->setSpacing(0);
- key_controls->setMargin(0);
+ key_controls = new QHBoxLayout();
+ key_controls->setSpacing(0);
+ key_controls->setMargin(0);
//key_controls->addStretch();
- setLayout(key_controls);
+ setLayout(key_controls);
- left_key_nav = new QPushButton();
- left_key_nav->setIcon(QIcon(":/icons/tri-left.png"));
- left_key_nav->setMaximumSize(button_size);
- left_key_nav->setIconSize(icon_size);
- left_key_nav->setVisible(false);
- key_controls->addWidget(left_key_nav);
- connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_previous_key()));
+ left_key_nav = new QPushButton();
+ left_key_nav->setIcon(QIcon(":/icons/tri-left.png"));
+ left_key_nav->setMaximumSize(button_size);
+ left_key_nav->setIconSize(icon_size);
+ left_key_nav->setVisible(false);
+ key_controls->addWidget(left_key_nav);
+ connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_previous_key()));
connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
- key_addremove = new QPushButton();
- key_addremove->setIcon(QIcon(":/icons/diamond.png"));
- key_addremove->setMaximumSize(button_size);
- key_addremove->setIconSize(QSize(8, 8));
- key_addremove->setVisible(false);
- key_controls->addWidget(key_addremove);
- connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(toggle_key()));
+ key_addremove = new QPushButton();
+ key_addremove->setIcon(QIcon(":/icons/diamond.png"));
+ key_addremove->setMaximumSize(button_size);
+ key_addremove->setIconSize(QSize(8, 8));
+ key_addremove->setVisible(false);
+ key_controls->addWidget(key_addremove);
+ connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(toggle_key()));
connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
- right_key_nav = new QPushButton();
- right_key_nav->setIcon(QIcon(":/icons/tri-right.png"));
- right_key_nav->setMaximumSize(button_size);
- right_key_nav->setIconSize(icon_size);
- right_key_nav->setVisible(false);
- key_controls->addWidget(right_key_nav);
- connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_next_key()));
+ right_key_nav = new QPushButton();
+ right_key_nav->setIcon(QIcon(":/icons/tri-right.png"));
+ right_key_nav->setMaximumSize(button_size);
+ right_key_nav->setIconSize(icon_size);
+ right_key_nav->setVisible(false);
+ key_controls->addWidget(right_key_nav);
+ connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_next_key()));
connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
- keyframe_enable = new QPushButton(QIcon(":/icons/clock.png"), "");
- keyframe_enable->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
- keyframe_enable->setMaximumSize(button_size);
+ keyframe_enable = new QPushButton(QIcon(":/icons/clock.png"), "");
+ keyframe_enable->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
+ keyframe_enable->setMaximumSize(button_size);
keyframe_enable->setIconSize(clock_size);
- keyframe_enable->setCheckable(true);
- keyframe_enable->setToolTip(tr("Enable Keyframes"));
+ keyframe_enable->setCheckable(true);
+ keyframe_enable->setToolTip(tr("Enable Keyframes"));
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(keyframe_enabled_changed(bool)));
- connect(keyframe_enable, SIGNAL(toggled(bool)), this, SLOT(keyframe_ui_enabled(bool)));
+ connect(keyframe_enable, SIGNAL(toggled(bool)), this, SLOT(keyframe_ui_enabled(bool)));
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
- key_controls->addWidget(keyframe_enable);
+ key_controls->addWidget(keyframe_enable);
+}
+
+KeyframeNavigator::~KeyframeNavigator() {
+ delete keyframe_enable;
+ delete right_key_nav;
+ delete key_addremove;
+ delete left_key_nav;
+ delete key_controls;
}
void KeyframeNavigator::enable_keyframes(bool b) {
@@ -69,7 +77,7 @@ void KeyframeNavigator::enable_keyframe_toggle(bool b) {
}
void KeyframeNavigator::keyframe_ui_enabled(bool enabled) {
- left_key_nav->setVisible(enabled);
- key_addremove->setVisible(enabled);
- right_key_nav->setVisible(enabled);
+ left_key_nav->setVisible(enabled);
+ key_addremove->setVisible(enabled);
+ right_key_nav->setVisible(enabled);
}
diff --git a/ui/keyframenavigator.h b/ui/keyframenavigator.h
index 6b6eaee85..d81f87f26 100644
--- a/ui/keyframenavigator.h
+++ b/ui/keyframenavigator.h
@@ -8,25 +8,26 @@ class QPushButton;
class KeyframeNavigator : public QWidget
{
- Q_OBJECT
+ Q_OBJECT
public:
- KeyframeNavigator(QWidget* parent = 0);
- void enable_keyframes(bool);
+ KeyframeNavigator(QWidget* parent = 0);
+ ~KeyframeNavigator();
+ void enable_keyframes(bool);
void enable_keyframe_toggle(bool);
signals:
- void goto_previous_key();
- void toggle_key();
- void goto_next_key();
+ void goto_previous_key();
+ void toggle_key();
+ void goto_next_key();
void keyframe_enabled_changed(bool);
void clicked();
private slots:
- void keyframe_ui_enabled(bool);
+ void keyframe_ui_enabled(bool);
private:
- QHBoxLayout* key_controls;
- QPushButton* left_key_nav;
- QPushButton* key_addremove;
- QPushButton* right_key_nav;
- QPushButton* keyframe_enable;
+ QHBoxLayout* key_controls;
+ QPushButton* left_key_nav;
+ QPushButton* key_addremove;
+ QPushButton* right_key_nav;
+ QPushButton* keyframe_enable;
};
#endif // KEYFRAMENAVIGATOR_H
diff --git a/ui/renderfunctions.cpp b/ui/renderfunctions.cpp
new file mode 100644
index 000000000..c719acdda
--- /dev/null
+++ b/ui/renderfunctions.cpp
@@ -0,0 +1,503 @@
+#include "renderfunctions.h"
+
+#include
+#include
+#include
+#include
+
+#include "project/clip.h"
+#include "project/sequence.h"
+#include "project/media.h"
+#include "project/effect.h"
+#include "project/footage.h"
+#include "project/transition.h"
+
+#include "ui/collapsiblewidget.h"
+
+#include "playback/audio.h"
+#include "playback/playback.h"
+
+#include "io/math.h"
+#include "io/config.h"
+#include "io/avtogl.h"
+
+#include "panels/timeline.h"
+#include "panels/viewer.h"
+
+extern "C" {
+ #include
+}
+
+//#define GL_DEFAULT_BLEND glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE)
+#define GL_DEFAULT_BLEND glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE)
+
+GLuint draw_clip(QOpenGLContext* ctx, QOpenGLFramebufferObject* fbo, GLuint texture, bool clear) {
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0, 1, 0, 1, -1, 1);
+
+ GLint current_fbo = 0;
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
+
+ fbo->bind();
+
+ if (clear) glClear(GL_COLOR_BUFFER_BIT);
+
+ // get current blend mode
+ GLint src_rgb, src_alpha, dst_rgb, dst_alpha;
+ glGetIntegerv(GL_BLEND_SRC_RGB, &src_rgb);
+ glGetIntegerv(GL_BLEND_SRC_ALPHA, &src_alpha);
+ glGetIntegerv(GL_BLEND_DST_RGB, &dst_rgb);
+ glGetIntegerv(GL_BLEND_DST_ALPHA, &dst_alpha);
+
+ ctx->functions()->GL_DEFAULT_BLEND;
+
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glBegin(GL_QUADS);
+ glTexCoord2f(0, 0); // top left
+ glVertex2f(0, 0); // top left
+ glTexCoord2f(1, 0); // top right
+ glVertex2f(1, 0); // top right
+ glTexCoord2f(1, 1); // bottom right
+ glVertex2f(1, 1); // bottom right
+ glTexCoord2f(0, 1); // bottom left
+ glVertex2f(0, 1); // bottom left
+ glEnd();
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+// fbo->release();
+ ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
+
+ // restore previous blendFunc
+ ctx->functions()->glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha);
+
+ //if (default_fbo != nullptr) default_fbo->bind();
+
+ glPopMatrix();
+ return fbo->texture();
+}
+
+void process_effect(QOpenGLContext* ctx,
+ Clip* c,
+ Effect* e,
+ double timecode,
+ GLTextureCoords& coords,
+ GLuint& composite_texture,
+ bool& fbo_switcher,
+ bool& texture_failed,
+ int data) {
+ if (e->is_enabled()) {
+ if (e->enable_coords) {
+ e->process_coords(timecode, coords, data);
+ }
+ if ((e->enable_shader && shaders_are_enabled) || e->enable_superimpose) {
+ e->startEffect();
+ if ((e->enable_shader && shaders_are_enabled) && e->is_glsl_linked()) {
+ e->process_shader(timecode, coords);
+ composite_texture = draw_clip(ctx, c->fbo[fbo_switcher], composite_texture, true);
+ fbo_switcher = !fbo_switcher;
+ }
+ if (e->enable_superimpose) {
+ GLuint superimpose_texture = e->process_superimpose(timecode);
+ if (superimpose_texture == 0) {
+ qWarning() << "Superimpose texture was nullptr, retrying...";
+ texture_failed = true;
+ } else {
+ composite_texture = draw_clip(ctx, c->fbo[!fbo_switcher], superimpose_texture, false);
+ }
+ }
+ e->endEffect();
+ }
+ }
+}
+
+GLuint compose_sequence(Viewer* viewer,
+ QOpenGLContext* ctx,
+ Sequence* seq,
+ QVector& nests,
+ bool video,
+ bool render_audio,
+ Effect** gizmos,
+ bool& texture_failed,
+ bool rendering) {
+ GLint current_fbo = 0;
+ if (video) {
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
+ }
+
+ Sequence* s = seq;
+ long playhead = s->playhead;
+
+ if (!nests.isEmpty()) {
+ for (int i=0;imedia->to_sequence();
+ playhead += nests.at(i)->clip_in - nests.at(i)->get_timeline_in_with_transition();
+ playhead = refactor_frame_number(playhead, nests.at(i)->sequence->frame_rate, s->frame_rate);
+ }
+
+ if (video && nests.last()->fbo != nullptr) {
+ nests.last()->fbo[0]->bind();
+ glClear(GL_COLOR_BUFFER_BIT);
+// nests.last()->fbo[0]->release();
+ ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
+ }
+ }
+
+ int audio_track_count = 0;
+
+ QVector current_clips;
+
+ for (int i=0;iclips.size();i++) {
+ Clip* c = s->clips.at(i);
+
+ // if clip starts within one second and/or hasn't finished yet
+ if (c != nullptr) {
+// if (!(!nests.isEmpty() && !same_sign(c->track, nests.last()->track))) {
+ if ((c->track < 0) == video) {
+ bool clip_is_active = false;
+
+ if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
+ Footage* m = c->media->to_footage();
+ if (!m->invalid && !(c->track >= 0 && !is_audio_device_set())) {
+ if (m->ready) {
+ const FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
+ if (ms != nullptr && is_clip_active(c, playhead)) {
+ // if thread is already working, we don't want to touch this,
+ // but we also don't want to hang the UI thread
+ if (!c->open) {
+ open_clip(c, !rendering);
+ }
+ clip_is_active = true;
+ if (c->track >= 0) audio_track_count++;
+ } else if (c->open) {
+ close_clip(c, false);
+ }
+ } else {
+ //qWarning() << "Media '" + m->name + "' was not ready, retrying...";
+ texture_failed = true;
+ }
+ }
+ } else {
+ if (is_clip_active(c, playhead)) {
+ if (!c->open) open_clip(c, !rendering);
+ clip_is_active = true;
+ } else if (c->open) {
+ close_clip(c, false);
+ }
+ }
+ if (clip_is_active) {
+ bool added = false;
+ for (int j=0;jtrack < c->track) {
+ current_clips.insert(j, c);
+ added = true;
+ break;
+ }
+ }
+ if (!added) {
+ current_clips.append(c);
+ }
+ }
+ }
+ }
+ }
+
+ int half_width = s->width/2;
+ int half_height = s->height/2;
+
+ if (video) {
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(-half_width, half_width, -half_height, half_height, -1, 10);
+ }
+
+ for (int i=0;imedia != nullptr && c->media->get_type() == MEDIA_TYPE_FOOTAGE && !c->finished_opening) {
+ qWarning() << "Tried to display clip" << i << "but it's closed";
+ texture_failed = true;
+ } else {
+ if (c->track < 0) {
+ ctx->functions()->GL_DEFAULT_BLEND;
+ glColor4f(1.0, 1.0, 1.0, 1.0);
+
+ GLuint textureID = 0;
+ int video_width = c->getWidth();
+ int video_height = c->getHeight();
+
+ if (c->media != nullptr) {
+ switch (c->media->get_type()) {
+ case MEDIA_TYPE_FOOTAGE:
+ // set up opengl texture
+ if (c->texture == nullptr) {
+ c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ c->texture->setSize(c->stream->codecpar->width, c->stream->codecpar->height);
+ c->texture->setFormat(get_gl_tex_fmt_from_av(c->pix_fmt));
+ c->texture->setMipLevels(c->texture->maximumMipLevels());
+ c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
+ c->texture->allocateStorage(get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8);
+ }
+ get_clip_frame(c, playhead, texture_failed);
+ textureID = c->texture->textureId();
+ break;
+ case MEDIA_TYPE_SEQUENCE:
+ textureID = -1;
+ break;
+ }
+ }
+
+ if (textureID == 0 && c->media != nullptr) {
+ qWarning() << "Texture hasn't been created yet";
+ texture_failed = true;
+ } else if (playhead >= c->get_timeline_in_with_transition()) {
+ glPushMatrix();
+
+ // start preparing cache
+ if (c->fbo == nullptr) {
+ c->fbo = new QOpenGLFramebufferObject* [2];
+ c->fbo[0] = new QOpenGLFramebufferObject(video_width, video_height);
+ c->fbo[1] = new QOpenGLFramebufferObject(video_width, video_height);
+ }
+
+ // clear fbos
+ /*c->fbo[0]->bind();
+ glClear(GL_COLOR_BUFFER_BIT);
+ c->fbo[0]->release();
+ c->fbo[1]->bind();
+ glClear(GL_COLOR_BUFFER_BIT);
+ c->fbo[1]->release();*/
+
+ bool fbo_switcher = false;
+
+ glViewport(0, 0, video_width, video_height);
+
+ GLuint composite_texture;
+
+ if (c->media == nullptr) {
+ c->fbo[fbo_switcher]->bind();
+ glClear(GL_COLOR_BUFFER_BIT);
+// c->fbo[fbo_switcher]->release();
+ ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
+ composite_texture = c->fbo[fbo_switcher]->texture();
+ } else {
+ // for nested sequences
+ if (c->media->get_type()== MEDIA_TYPE_SEQUENCE) {
+ nests.append(c);
+ textureID = compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering);
+ nests.removeLast();
+ fbo_switcher = true;
+ }
+
+ composite_texture = draw_clip(ctx, c->fbo[fbo_switcher], textureID, true);
+ }
+
+ fbo_switcher = !fbo_switcher;
+
+ // set up default coords
+ GLTextureCoords coords;
+ coords.grid_size = 1;
+ coords.vertexTopLeftX = coords.vertexBottomLeftX = -video_width/2;
+ coords.vertexTopLeftY = coords.vertexTopRightY = -video_height/2;
+ coords.vertexTopRightX = coords.vertexBottomRightX = video_width/2;
+ coords.vertexBottomLeftY = coords.vertexBottomRightY = video_height/2;
+ coords.vertexBottomLeftZ = coords.vertexBottomRightZ = coords.vertexTopLeftZ = coords.vertexTopRightZ = 1;
+ coords.textureTopLeftY = coords.textureTopRightY = coords.textureTopLeftX = coords.textureBottomLeftX = 0.0;
+ coords.textureBottomLeftY = coords.textureBottomRightY = coords.textureTopRightX = coords.textureBottomRightX = 1.0;
+ coords.textureTopLeftQ = coords.textureTopRightQ = coords.textureTopLeftQ = coords.textureBottomLeftQ = 1;
+
+ // set up autoscale
+ if (c->autoscale && (video_width != s->width && video_height != s->height)) {
+ float width_multiplier = float(s->width) / float(video_width);
+ float height_multiplier = float(s->height) / float(video_height);
+ float scale_multiplier = qMin(width_multiplier, height_multiplier);
+ glScalef(scale_multiplier, scale_multiplier, 1);
+ }
+
+ // EFFECT CODE START
+ double timecode = get_timecode(c, playhead);
+
+ Effect* first_gizmo_effect = nullptr;
+ Effect* selected_effect = nullptr;
+
+ for (int j=0;jeffects.size();j++) {
+ Effect* e = c->effects.at(j);
+ process_effect(ctx, c, e, timecode, coords, composite_texture, fbo_switcher, texture_failed, TA_NO_TRANSITION);
+
+ if (e->are_gizmos_enabled()) {
+ if (first_gizmo_effect == nullptr) first_gizmo_effect = e;
+ if (e->container->selected) selected_effect = e;
+ }
+ }
+
+ if (selected_effect != nullptr) {
+ (*gizmos) = selected_effect;
+ } else if (is_clip_selected(c, true)) {
+ (*gizmos) = first_gizmo_effect;
+ }
+
+ if (c->get_opening_transition() != nullptr) {
+ int transition_progress = playhead - c->get_timeline_in_with_transition();
+ if (transition_progress < c->get_opening_transition()->get_length()) {
+ process_effect(ctx, c, c->get_opening_transition(), (double)transition_progress/(double)c->get_opening_transition()->get_length(), coords, composite_texture, fbo_switcher, texture_failed, TA_OPENING_TRANSITION);
+ }
+ }
+
+ if (c->get_closing_transition() != nullptr) {
+ int transition_progress = playhead - (c->get_timeline_out_with_transition() - c->get_closing_transition()->get_length());
+ if (transition_progress >= 0 && transition_progress < c->get_closing_transition()->get_length()) {
+ process_effect(ctx, c, c->get_closing_transition(), (double)transition_progress/(double)c->get_closing_transition()->get_length(), coords, composite_texture, fbo_switcher, texture_failed, TA_CLOSING_TRANSITION);
+ }
+ }
+ // EFFECT CODE END
+
+ /*if (!nests.isEmpty()) {
+ nests.last()->fbo[0]->bind();
+ glViewport(0, 0, s->width, s->height);
+ } else if (rendering) {
+ glViewport(0, 0, s->width, s->height);
+ } else {
+ int widget_width = width();
+ int widget_height = height();
+
+ widget_width *= QApplication::desktop()->devicePixelRatio();
+ widget_height *= QApplication::desktop()->devicePixelRatio();
+
+ glViewport(0, 0, widget_width, widget_height);
+ }*/
+ if (!nests.isEmpty()) {
+ nests.last()->fbo[0]->bind();
+ }
+ glViewport(0, 0, s->width, s->height);
+
+ glBindTexture(GL_TEXTURE_2D, composite_texture);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glBegin(GL_QUADS);
+
+ if (coords.grid_size <= 1) {
+ float z = 0.0f;
+
+ glTexCoord2f(coords.textureTopLeftX, coords.textureTopLeftY); // top left
+ glVertex3f(coords.vertexTopLeftX, coords.vertexTopLeftY, z); // top left
+ glTexCoord2f(coords.textureTopRightX, coords.textureTopRightY); // top right
+ glVertex3f(coords.vertexTopRightX, coords.vertexTopRightY, z); // top right
+ glTexCoord2f(coords.textureBottomRightX, coords.textureBottomRightY); // bottom right
+ glVertex3f(coords.vertexBottomRightX, coords.vertexBottomRightY, z); // bottom right
+ glTexCoord2f(coords.textureBottomLeftX, coords.textureBottomLeftY); // bottom left
+ glVertex3f(coords.vertexBottomLeftX, coords.vertexBottomLeftY, z); // bottom left
+ } else {
+ float rows = coords.grid_size;
+ float cols = coords.grid_size;
+
+ for (float k=0;kgizmo_draw(timecode, coords); // set correct gizmo coords
+ (*gizmos)->gizmo_world_to_screen(); // convert gizmo coords to screen coords
+ }
+
+ if (!nests.isEmpty()) {
+ ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
+ }
+
+ glPopMatrix();
+
+ /*GLfloat motion_blur_frac = (GLfloat) motion_blur_prog / (GLfloat) motion_blur_lim;
+ if (motion_blur_prog == 0) {
+ glAccum(GL_LOAD, motion_blur_frac);
+ } else {
+ glAccum(GL_ACCUM, motion_blur_frac);
+ }
+ motion_blur_prog++;*/
+ }
+ } else {
+ if (render_audio || (config.enable_audio_scrubbing && audio_scrub)) {
+ if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
+ nests.append(c);
+ compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering);
+ nests.removeLast();
+ } else {
+ if (c->lock.tryLock()) {
+ // clip is not caching, start caching audio
+ cache_clip(c, playhead, c->audio_reset, !render_audio, nests);
+ c->lock.unlock();
+ }
+ }
+ }
+
+ // visually update all the keyframe values
+ if (c->sequence == seq) { // only if you can currently see them
+ double ts = (playhead - c->get_timeline_in_with_transition() + c->get_clip_in_with_transition())/s->frame_rate;
+ for (int i=0;ieffects.size();i++) {
+ Effect* e = c->effects.at(i);
+ for (int j=0;jrow_count();j++) {
+ EffectRow* r = e->row(j);
+ for (int k=0;kfieldCount();k++) {
+ r->field(k)->validate_keyframe_data(ts);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (audio_track_count == 0 && viewer != nullptr) {
+ viewer->play_wake();
+ }
+
+ if (video) {
+ glPopMatrix();
+ }
+
+ if (!nests.isEmpty() && nests.last()->fbo != nullptr) {
+ // returns nested clip's texture
+ return nests.last()->fbo[0]->texture();
+ }
+
+ return 0;
+}
+
+void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio) {
+ QVector nests;
+ bool texture_failed;
+ compose_sequence(viewer, nullptr, seq, nests, false, render_audio, nullptr, texture_failed, audio_rendering);
+}
diff --git a/ui/renderfunctions.h b/ui/renderfunctions.h
new file mode 100644
index 000000000..de405bf8c
--- /dev/null
+++ b/ui/renderfunctions.h
@@ -0,0 +1,26 @@
+#ifndef RENDERFUNCTIONS_H
+#define RENDERFUNCTIONS_H
+
+#include
+#include
+
+class Effect;
+class Viewer;
+struct Sequence;
+struct Clip;
+
+GLuint compose_sequence(Viewer* viewer,
+ QOpenGLContext* ctx,
+ Sequence* seq,
+ QVector& nests,
+ bool video,
+ bool render_audio,
+ Effect **gizmos,
+ bool &texture_failed,
+ bool rendering);
+
+void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio);
+
+void viewport_render();
+
+#endif // RENDERFUNCTIONS_H
diff --git a/ui/renderthread.cpp b/ui/renderthread.cpp
new file mode 100644
index 000000000..c1ead032d
--- /dev/null
+++ b/ui/renderthread.cpp
@@ -0,0 +1,199 @@
+#include "renderthread.h"
+
+#include
+#include
+#include
+#include
+
+#include "ui/renderfunctions.h"
+#include "playback/playback.h"
+#include "project/sequence.h"
+
+RenderThread::RenderThread() :
+ frameBuffer(0),
+ texColorBuffer(0),
+ gizmos(nullptr),
+ share_ctx(nullptr),
+ ctx(nullptr),
+ seq(nullptr),
+ tex_width(-1),
+ tex_height(-1),
+ queued(false),
+ texture_failed(false),
+ running(true)
+{
+ surface.create();
+}
+
+RenderThread::~RenderThread() {
+ surface.destroy();
+}
+
+void RenderThread::run() {
+ mutex.lock();
+
+ while (running) {
+ if (!queued) {
+ waitCond.wait(&mutex);
+ }
+ if (!running) {
+ break;
+ }
+ queued = false;
+
+
+ if (share_ctx != nullptr) {
+ if (ctx != nullptr) {
+ ctx->makeCurrent(&surface);
+
+ // gen fbo
+ if (frameBuffer == 0) {
+ delete_fbo();
+ ctx->functions()->glGenFramebuffers(1, &frameBuffer);
+ }
+
+ // bind
+ ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer);
+
+ // gen texture
+ if (texColorBuffer == 0 || tex_width != seq->width || tex_height != seq->height) {
+ delete_texture();
+ glGenTextures(1, &texColorBuffer);
+ glBindTexture(GL_TEXTURE_2D, texColorBuffer);
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, GL_RGB, seq->width, seq->height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr
+ );
+ tex_width = seq->width;
+ tex_height = seq->height;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ ctx->functions()->glFramebufferTexture2D(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0
+ );
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ // draw
+ paint();
+
+ // flush changes
+// glFlush();
+ glFinish();
+
+ // release
+ ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ emit ready();
+ }
+ }
+ }
+
+ delete_ctx();
+
+ mutex.unlock();
+}
+
+void RenderThread::paint() {
+ glLoadIdentity();
+
+ texture_failed = false;
+
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glClearColor(0, 0, 0, 0);
+ glMatrixMode(GL_MODELVIEW);
+
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+ glEnable(GL_DEPTH);
+
+ gizmos = nullptr;
+ QVector nests;
+ compose_sequence(nullptr, ctx, seq, nests, true, false, &gizmos, texture_failed, false);
+
+ if (!save_fn.isEmpty()) {
+ if (texture_failed) {
+ // texture failed, try again
+ queued = true;
+ } else {
+ ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBuffer);
+ QImage img(tex_width, tex_height, QImage::Format_RGBA8888);
+ glReadPixels(0, 0, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+ img.save(save_fn);
+ ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ save_fn = "";
+ }
+ }
+
+ if (pixel_buffer != nullptr) {
+ ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBuffer);
+ glReadPixels(0, 0, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, pixel_buffer);
+ ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ pixel_buffer = nullptr;
+ }
+
+ glDisable(GL_DEPTH);
+ glDisable(GL_BLEND);
+ glDisable(GL_TEXTURE_2D);
+}
+
+void RenderThread::start_render(QOpenGLContext *share, Sequence *s, const QString& save, GLvoid* pixels, int idivider) {
+ seq = s;
+
+ // stall any dependent actions
+ texture_failed = true;
+
+ if (share != nullptr && (ctx == nullptr || ctx->shareContext() != share_ctx)) {
+ share_ctx = share;
+ delete_ctx();
+ ctx = new QOpenGLContext();
+ ctx->setFormat(share_ctx->format());
+ ctx->setShareContext(share_ctx);
+ ctx->create();
+ ctx->moveToThread(this);
+ }
+
+ save_fn = save;
+ pixel_buffer = pixels;
+
+ queued = true;
+ waitCond.wakeAll();
+}
+
+bool RenderThread::did_texture_fail() {
+ return texture_failed;
+}
+
+void RenderThread::cancel() {
+ running = false;
+ waitCond.wakeAll();
+ wait();
+}
+
+void RenderThread::delete_texture() {
+ if (texColorBuffer > 0) {
+ ctx->functions()->glFramebufferTexture2D(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0
+ );
+ glDeleteTextures(1, &texColorBuffer);
+ }
+ texColorBuffer = 0;
+}
+
+void RenderThread::delete_fbo() {
+ if (frameBuffer > 0) {
+ ctx->functions()->glDeleteFramebuffers(1, &frameBuffer);
+ }
+ frameBuffer = 0;
+}
+
+void RenderThread::delete_ctx() {
+ if (ctx != nullptr) {
+ delete_texture();
+ delete_fbo();
+ ctx->doneCurrent();
+ delete ctx;
+ }
+ ctx = nullptr;
+}
diff --git a/ui/renderthread.h b/ui/renderthread.h
new file mode 100644
index 000000000..cd4f1aec3
--- /dev/null
+++ b/ui/renderthread.h
@@ -0,0 +1,54 @@
+#ifndef RENDERTHREAD_H
+#define RENDERTHREAD_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct Sequence;
+class Effect;
+
+class RenderThread : public QThread {
+ Q_OBJECT
+public:
+ RenderThread();
+ ~RenderThread();
+ void run();
+ QMutex mutex;
+ GLuint frameBuffer;
+ GLuint texColorBuffer;
+ Effect* gizmos;
+ void paint();
+ void start_render(QOpenGLContext* share, Sequence* s, const QString &save = nullptr, GLvoid *pixels = nullptr, int idivider = 0);
+ bool did_texture_fail();
+ void cancel();
+
+public slots:
+ // cleanup functions
+ void delete_ctx();
+signals:
+ void ready();
+private:
+ // cleanup functions
+ void delete_texture();
+ void delete_fbo();
+
+ QWaitCondition waitCond;
+ QOffscreenSurface surface;
+ QOpenGLContext* share_ctx;
+ QOpenGLContext* ctx;
+ Sequence* seq;
+ int divider;
+ int tex_width;
+ int tex_height;
+ bool queued;
+ bool texture_failed;
+ bool running;
+ QString save_fn;
+ GLvoid *pixel_buffer;
+};
+
+#endif // RENDERTHREAD_H
diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp
index 118bdd6f2..8e8c1ed62 100644
--- a/ui/timelinewidget.cpp
+++ b/ui/timelinewidget.cpp
@@ -98,7 +98,7 @@ void TimelineWidget::show_context_menu(const QPoint& pos) {
QVector selected_clips;
for (int i=0;iclips.size();i++) {
Clip* c = sequence->clips.at(i);
- if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
+ if (c != nullptr && is_clip_selected(c, true)) {
selected_clips.append(c);
}
}
@@ -204,7 +204,7 @@ void TimelineWidget::toggle_autoscale() {
SetAutoscaleAction* action = new SetAutoscaleAction();
for (int i=0;iclips.size();i++) {
Clip* c = sequence->clips.at(i);
- if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
+ if (c != nullptr && is_clip_selected(c, true)) {
action->clips.append(c);
}
}
@@ -237,7 +237,7 @@ void TimelineWidget::rename_clip() {
QVector selected_clips;
for (int i=0;iclips.size();i++) {
Clip* c = sequence->clips.at(i);
- if (c != nullptr && panel_timeline->is_clip_selected(c, true)) {
+ if (c != nullptr && is_clip_selected(c, true)) {
selected_clips.append(c);
}
}
@@ -629,7 +629,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
if (clip_index >= 0) {
Clip* clip = sequence->clips.at(clip_index);
if (clip != nullptr) {
- if (panel_timeline->is_clip_selected(clip, true)) {
+ if (is_clip_selected(clip, true)) {
if (shift) {
panel_timeline->deselect_area(clip->timeline_in, clip->timeline_out, clip->track);
@@ -695,7 +695,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
if (!alt && panel_timeline->transition_select == TA_NO_TRANSITION) {
for (int i=0;ilinked.size();i++) {
Clip* link = sequence->clips.at(clip->linked.at(i));
- if (!panel_timeline->is_clip_selected(link, true)) {
+ if (!is_clip_selected(link, true)) {
Selection ss;
ss.in = link->timeline_in;
ss.out = link->timeline_out;
@@ -1696,7 +1696,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
Ghost g;
g.transition = nullptr;
- bool add = panel_timeline->is_clip_selected(c, true);
+ bool add = is_clip_selected(c, true);
// if a whole clip is not selected, maybe just a transition is
if (panel_timeline->tool == TIMELINE_TOOL_POINTER && (c->get_opening_transition() != nullptr || c->get_closing_transition() != nullptr)) {
@@ -1838,7 +1838,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
for (int i=0;iclips.size();i++) {
Clip* c = sequence->clips.at(i);
- if (c != nullptr && !panel_timeline->is_clip_selected(c, true)) {
+ if (c != nullptr && !is_clip_selected(c, true)) {
bool clip_is_post = (c->timeline_in >= axis);
// see if this a clip on this track is already in the list, and if it's closer
diff --git a/ui/viewercontainer.cpp b/ui/viewercontainer.cpp
index 21f709b1c..145cd49fc 100644
--- a/ui/viewercontainer.cpp
+++ b/ui/viewercontainer.cpp
@@ -12,85 +12,86 @@
// enforces aspect ratio
ViewerContainer::ViewerContainer(QWidget *parent) :
- QScrollArea(parent),
- fit(true),
- child(nullptr)
+ QScrollArea(parent),
+ fit(true),
+ child(nullptr)
{
- setFrameShadow(QFrame::Plain);
- setFrameShape(QFrame::NoFrame);
+ setFrameShadow(QFrame::Plain);
+ setFrameShape(QFrame::NoFrame);
- area = new QWidget(this);
- area->move(0, 0);
- setWidget(area);
+ area = new QWidget(this);
+ area->move(0, 0);
+ setWidget(area);
- child = new ViewerWidget(area);
- child->container = this;
+ child = new ViewerWidget(area);
+ child->container = this;
}
ViewerContainer::~ViewerContainer() {
- delete area;
+ delete child;
+ delete area;
}
void ViewerContainer::dragScrollPress(const QPoint &p) {
- drag_start_x = p.x();
- drag_start_y = p.y();
- horiz_start = horizontalScrollBar()->value();
- vert_start = verticalScrollBar()->value();
+ drag_start_x = p.x();
+ drag_start_y = p.y();
+ horiz_start = horizontalScrollBar()->value();
+ vert_start = verticalScrollBar()->value();
}
void ViewerContainer::dragScrollMove(const QPoint &p) {
- int true_x = p.x() + (horiz_start - horizontalScrollBar()->value());
- int true_y = p.y() + (vert_start - verticalScrollBar()->value());
+ int true_x = p.x() + (horiz_start - horizontalScrollBar()->value());
+ int true_y = p.y() + (vert_start - verticalScrollBar()->value());
- horizontalScrollBar()->setValue(horizontalScrollBar()->value() + (drag_start_x - true_x));
- verticalScrollBar()->setValue(verticalScrollBar()->value() + (drag_start_y - true_y));
+ horizontalScrollBar()->setValue(horizontalScrollBar()->value() + (drag_start_x - true_x));
+ verticalScrollBar()->setValue(verticalScrollBar()->value() + (drag_start_y - true_y));
- drag_start_x = true_x;
- drag_start_y = true_y;
+ drag_start_x = true_x;
+ drag_start_y = true_y;
}
void ViewerContainer::adjust() {
- if (viewer->seq != nullptr) {
- if (child->waveform) {
- child->move(0, 0);
- child->resize(size());
- } else if (fit) {
- double aspect_ratio = double(viewer->seq->width)/double(viewer->seq->height);
+ if (viewer->seq != nullptr) {
+ if (child->waveform) {
+ child->move(0, 0);
+ child->resize(size());
+ } else if (fit) {
+ double aspect_ratio = double(viewer->seq->width)/double(viewer->seq->height);
- int widget_x = 0;
- int widget_y = 0;
- int widget_width = width();
- int widget_height = height();
- float widget_ar = (float) widget_width /(float) widget_height;
+ int widget_x = 0;
+ int widget_y = 0;
+ int widget_width = width();
+ int widget_height = height();
+ double widget_ar = double(widget_width) / double(widget_height);
- bool widget_is_wider_than_sequence = widget_ar > aspect_ratio;
+ bool widget_is_wider_than_sequence = widget_ar > aspect_ratio;
- if (widget_is_wider_than_sequence) {
- widget_width = widget_height * aspect_ratio;
- widget_x = (width() / 2) - (widget_width / 2);
- } else {
- widget_height = widget_width / aspect_ratio;
- widget_y = (height() / 2) - (widget_height / 2);
- }
+ if (widget_is_wider_than_sequence) {
+ widget_width = widget_height * aspect_ratio;
+ widget_x = (width() / 2) - (widget_width / 2);
+ } else {
+ widget_height = widget_width / aspect_ratio;
+ widget_y = (height() / 2) - (widget_height / 2);
+ }
- child->move(widget_x, widget_y);
- child->resize(widget_width, widget_height);
+ child->move(widget_x, widget_y);
+ child->resize(widget_width, widget_height);
- zoom = double(widget_width) / double(viewer->seq->width);
- } else {
- int zoomed_width = double(viewer->seq->width)*zoom;
- int zoomed_height = double(viewer->seq->height)*zoom;
- int zoomed_x = 0;
- int zoomed_y = 0;
+ zoom = double(widget_width) / double(viewer->seq->width);
+ } else {
+ int zoomed_width = double(viewer->seq->width)*zoom;
+ int zoomed_height = double(viewer->seq->height)*zoom;
+ int zoomed_x = 0;
+ int zoomed_y = 0;
- if (zoomed_width < width()) zoomed_x = (width()>>1)-(zoomed_width>>1);
- if (zoomed_height < height()) zoomed_y = (height()>>1)-(zoomed_height>>1);
+ if (zoomed_width < width()) zoomed_x = (width()>>1)-(zoomed_width>>1);
+ if (zoomed_height < height()) zoomed_y = (height()>>1)-(zoomed_height>>1);
- child->move(zoomed_x, zoomed_y);
- child->resize(zoomed_width, zoomed_height);
- }
+ child->move(zoomed_x, zoomed_y);
+ child->resize(zoomed_width, zoomed_height);
+ }
}
- area->resize(qMax(width(), child->width()), qMax(height(), child->height()));
+ area->resize(qMax(width(), child->width()), qMax(height(), child->height()));
}
void ViewerContainer::resizeEvent(QResizeEvent *event) {
diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp
index 6aca8f47b..7791d28d4 100644
--- a/ui/viewerwidget.cpp
+++ b/ui/viewerwidget.cpp
@@ -21,6 +21,10 @@
#include "ui/viewercontainer.h"
#include "io/avtogl.h"
#include "ui/timelinewidget.h"
+#include "ui/renderfunctions.h"
+#include "ui/renderthread.h"
+#include "ui/viewerwindow.h"
+#include "mainwindow.h"
#include
#include
@@ -37,21 +41,22 @@
#include
#include
#include
+#include
+#include
extern "C" {
#include
}
-#define GL_DEFAULT_BLEND glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
-
ViewerWidget::ViewerWidget(QWidget *parent) :
QOpenGLWidget(parent),
- default_fbo(nullptr),
waveform(false),
waveform_zoom(1.0),
waveform_scroll(0),
dragging(false),
- selected_gizmo(nullptr)
+ gizmos(nullptr),
+ selected_gizmo(nullptr),
+ window(nullptr)
{
setMouseTracking(true);
setFocusPolicy(Qt::ClickFocus);
@@ -60,21 +65,26 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
format.setDepthBufferSize(24);
setFormat(format);
- // error handler - retries after 50ms if we couldn't get the entire image
- retry_timer.setInterval(50);
- connect(&retry_timer, SIGNAL(timeout()), this, SLOT(retry()));
-
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu()));
+
+ renderer = new RenderThread();
+ renderer->start(QThread::HighPriority);
+ connect(renderer, SIGNAL(ready()), this, SLOT(queue_repaint()));
+ connect(renderer, SIGNAL(finished()), renderer, SLOT(deleteLater()));
+}
+
+ViewerWidget::~ViewerWidget() {
+ if (window != nullptr) {
+ window->close();
+ delete window;
+ }
+ renderer->cancel();
+ delete renderer;
}
void ViewerWidget::delete_function() {
- // destroy all textures as well
- if (viewer->seq != nullptr) {
- makeCurrent();
- closeActiveClips(viewer->seq);
- doneCurrent();
- }
+ closeActiveClips(viewer->seq);
}
void ViewerWidget::set_waveform_scroll(int s) {
@@ -90,8 +100,21 @@ void ViewerWidget::show_context_menu() {
QAction* save_frame_as_image = menu.addAction(tr("Save Frame as Image..."));
connect(save_frame_as_image, SIGNAL(triggered(bool)), this, SLOT(save_frame()));
- QAction* show_fullscreen_action = menu.addAction(tr("Show Fullscreen"));
- connect(show_fullscreen_action, SIGNAL(triggered()), this, SLOT(show_fullscreen()));
+ /*QAction* show_fullscreen_action = menu.addAction(tr("Show Fullscreen"));
+ connect(show_fullscreen_action, SIGNAL(triggered()), this, SLOT(show_fullscreen()));*/
+ QMenu* fullscreen_menu = menu.addMenu(tr("Show Fullscreen"));
+ QList screens = QGuiApplication::screens();
+ if (window != nullptr && window->isVisible()) {
+ fullscreen_menu->addAction(tr("Disable"));
+ }
+ for (int i=0;iaddAction(tr("Screen %1: %2x%3").arg(
+ QString::number(i),
+ QString::number(screens.at(i)->size().width()),
+ QString::number(screens.at(i)->size().height())));
+ screen_action->setData(i);
+ }
+ connect(fullscreen_menu, SIGNAL(triggered(QAction*)), this, SLOT(fullscreen_menu_action(QAction*)));
QMenu zoom_menu(tr("Zoom"));
QAction* fit_zoom = zoom_menu.addAction(tr("Fit"));
@@ -129,27 +152,30 @@ void ViewerWidget::save_frame() {
if (!fn.endsWith(selected_ext, Qt::CaseInsensitive)) {
fn += selected_ext;
}
- QOpenGLFramebufferObject fbo(viewer->seq->width, viewer->seq->height, QOpenGLFramebufferObject::CombinedDepthStencil, GL_TEXTURE_RECTANGLE);
- rendering = true;
- fbo.bind();
-
- default_fbo = &fbo;
-
- paintGL();
-
- QImage img(viewer->seq->width, viewer->seq->height, QImage::Format_RGBA8888);
- glReadPixels(0, 0, img.width(), img.height(), GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
- img.save(fn);
-
- fbo.release();
- default_fbo = nullptr;
- rendering = false;
+ renderer->start_render(context(), viewer->seq, fn);
}
}
-void ViewerWidget::show_fullscreen() {
- showFullScreen();
+void ViewerWidget::queue_repaint() {
+ update();
+}
+
+void ViewerWidget::fullscreen_menu_action(QAction *action) {
+ if (window == nullptr) {
+ QMessageBox::critical(this, "Error", "Failed to create viewer window");
+ } else {
+ if (action->data().isNull()) {
+ window->hide();
+ } else {
+ QScreen* selected_screen = QGuiApplication::screens().at(action->data().toInt());
+ window->showFullScreen();
+ window->setGeometry(selected_screen->geometry());
+
+ // HACK: window seems to show with distorted texture on first showing, so we queue an update after it's shown
+ QTimer::singleShot(100, window, SLOT(update()));
+ }
+ }
}
void ViewerWidget::set_fit_zoom() {
@@ -186,30 +212,63 @@ void ViewerWidget::retry() {
void ViewerWidget::initializeGL() {
initializeOpenGLFunctions();
- connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(delete_function()), Qt::DirectConnection);
+ connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(context_destroy()), Qt::DirectConnection);
- retry_timer.start();
+ window = new ViewerWindow(context());
+}
+
+void ViewerWidget::frame_update() {
+ if (viewer->seq != nullptr) {
+ bool render_audio = (viewer->playing || audio_rendering);
+
+ // send context to other thread for drawing
+ if (waveform) {
+ update();
+ } else {
+ doneCurrent();
+ renderer->start_render(context(), viewer->seq);
+ }
+
+ // render the audio
+ compose_audio(viewer, viewer->seq, render_audio);
+ }
+}
+
+RenderThread *ViewerWidget::get_renderer() {
+ return renderer;
}
//void ViewerWidget::resizeGL(int w, int h)
//{
//}
-void ViewerWidget::paintEvent(QPaintEvent *e) {
- if (!rendering && context()->thread() == this->thread()) {
- makeCurrent();
+/*void ViewerWidget::paintEvent(QPaintEvent *e) {
+ if (!rendering) {
QOpenGLWidget::paintEvent(e);
}
-}
+}*/
void ViewerWidget::seek_from_click(int x) {
viewer->seek(getFrameFromScreenPoint(waveform_zoom, x+waveform_scroll));
}
+void ViewerWidget::context_destroy() {
+ makeCurrent();
+ if (viewer->seq != nullptr) {
+ closeActiveClips(viewer->seq);
+ }
+ if (window != nullptr) {
+ delete window;
+ }
+ //QMetaObject::invokeMethod(renderer, "delete_ctx", Qt::QueuedConnection);
+ renderer->delete_ctx();
+ doneCurrent();
+}
+
EffectGizmo* ViewerWidget::get_gizmo_from_mouse(int x, int y) {
if (gizmos != nullptr) {
double multiplier = double(viewer->seq->width) / double(width());
- QPoint mouse_pos(qRound(x*multiplier), qRound(y*multiplier));
+ QPoint mouse_pos(qRound(x*multiplier), qRound((height()-y)*multiplier));
int dot_size = 2 * qRound(GIZMO_DOT_SIZE * multiplier);
int target_size = 2 * qRound(GIZMO_TARGET_SIZE * multiplier);
for (int i=0;igizmo_count();i++) {
@@ -238,7 +297,6 @@ EffectGizmo* ViewerWidget::get_gizmo_from_mouse(int x, int y) {
}
break;
}
-
}
}
return nullptr;
@@ -248,8 +306,8 @@ void ViewerWidget::move_gizmos(QMouseEvent *event, bool done) {
if (selected_gizmo != nullptr) {
double multiplier = double(viewer->seq->width) / double(width());
- int x_movement = (event->pos().x() - drag_start_x)*multiplier;
- int y_movement = (event->pos().y() - drag_start_y)*multiplier;
+ int x_movement = qRound((event->pos().x() - drag_start_x)*multiplier);
+ int y_movement = qRound((event->pos().y() - drag_start_y)*multiplier);
gizmos->gizmo_move(selected_gizmo, x_movement, y_movement, get_timecode(gizmos->parent_clip, gizmos->parent_clip->sequence->playhead), done);
@@ -315,11 +373,38 @@ void ViewerWidget::mouseMoveEvent(QMouseEvent* event) {
}
void ViewerWidget::mouseReleaseEvent(QMouseEvent *event) {
- if (dragging) move_gizmos(event, true);
+ if (dragging && gizmos != nullptr && !(event->button() == Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND)) {
+ move_gizmos(event, true);
+ }
dragging = false;
}
-void ViewerWidget::drawTitleSafeArea() {
+void ViewerWidget::close_window() {
+ if (window != nullptr) window->hide();
+}
+
+void ViewerWidget::draw_waveform_func() {
+ QPainter p(this);
+ if (viewer->seq->using_workarea) {
+ int in_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->workarea_in) - waveform_scroll;
+ int out_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->workarea_out) - waveform_scroll;
+
+ p.fillRect(QRect(in_x, 0, out_x - in_x, height()), QColor(255, 255, 255, 64));
+ p.setPen(Qt::white);
+ p.drawLine(in_x, 0, in_x, height());
+ p.drawLine(out_x, 0, out_x, height());
+ }
+ QRect wr = rect();
+ wr.setX(wr.x() - waveform_scroll);
+
+ p.setPen(Qt::green);
+ draw_waveform(waveform_clip, waveform_ms, waveform_clip->timeline_out, &p, wr, waveform_scroll, width()+waveform_scroll, waveform_zoom);
+ p.setPen(Qt::red);
+ int playhead_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->playhead) - waveform_scroll;
+ p.drawLine(playhead_x, 0, playhead_x, height());
+}
+
+void ViewerWidget::draw_title_safe_area() {
double halfWidth = 0.5;
double halfHeight = 0.5;
double viewportAr = (double) width() / (double) height();
@@ -387,571 +472,129 @@ void ViewerWidget::drawTitleSafeArea() {
glEnd();
}
-GLuint ViewerWidget::draw_clip(QOpenGLFramebufferObject* fbo, GLuint texture, bool clear) {
- glPushMatrix();
- glLoadIdentity();
- glOrtho(0, 1, 0, 1, -1, 1);
+void ViewerWidget::draw_gizmos() {
+ float color[4];
+ glGetFloatv(GL_CURRENT_COLOR, color);
- fbo->bind();
-
- if (clear) glClear(GL_COLOR_BUFFER_BIT);
-
- // get current blend mode
- GLint src_rgb, src_alpha, dst_rgb, dst_alpha;
- glGetIntegerv(GL_BLEND_SRC_RGB, &src_rgb);
- glGetIntegerv(GL_BLEND_SRC_ALPHA, &src_alpha);
- glGetIntegerv(GL_BLEND_DST_RGB, &dst_rgb);
- glGetIntegerv(GL_BLEND_DST_ALPHA, &dst_alpha);
-
- GL_DEFAULT_BLEND
-
- glBindTexture(GL_TEXTURE_2D, texture);
- glBegin(GL_QUADS);
- glTexCoord2f(0, 0); // top left
- glVertex2f(0, 0); // top left
- glTexCoord2f(1, 0); // top right
- glVertex2f(1, 0); // top right
- glTexCoord2f(1, 1); // bottom right
- glVertex2f(1, 1); // bottom right
- glTexCoord2f(0, 1); // bottom left
- glVertex2f(0, 1); // bottom left
- glEnd();
- glBindTexture(GL_TEXTURE_2D, 0);
-
- fbo->release();
-
- // restore previous blendFunc
- glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha);
-
- if (default_fbo != nullptr) default_fbo->bind();
-
- glPopMatrix();
- return fbo->texture();
-}
-
-void ViewerWidget::process_effect(Clip* c, Effect* e, double timecode, GLTextureCoords& coords, GLuint& composite_texture, bool& fbo_switcher, int data) {
- if (e->is_enabled()) {
- if (e->enable_coords) {
- e->process_coords(timecode, coords, data);
- }
- if ((e->enable_shader && shaders_are_enabled) || e->enable_superimpose) {
- e->startEffect();
- if ((e->enable_shader && shaders_are_enabled) && e->is_glsl_linked()) {
- e->process_shader(timecode, coords);
- composite_texture = draw_clip(c->fbo[fbo_switcher], composite_texture, true);
- fbo_switcher = !fbo_switcher;
- }
- if (e->enable_superimpose) {
- GLuint superimpose_texture = e->process_superimpose(timecode);
- if (superimpose_texture == 0) {
- qWarning() << "Superimpose texture was nullptr, retrying...";
- texture_failed = true;
- } else {
- composite_texture = draw_clip(c->fbo[!fbo_switcher], superimpose_texture, false);
- }
- }
- e->endEffect();
- }
- }
-}
-
-int motion_blur_prog = 0;
-int motion_blur_lim = 4;
-
-GLuint ViewerWidget::compose_sequence(QVector& nests, bool render_audio) {
- Sequence* s = viewer->seq;
- long playhead = s->playhead;
-
- if (!nests.isEmpty()) {
- for (int i=0;imedia->to_sequence();
- playhead += nests.at(i)->clip_in - nests.at(i)->get_timeline_in_with_transition();
- playhead = refactor_frame_number(playhead, nests.at(i)->sequence->frame_rate, s->frame_rate);
- }
-
- if (nests.last()->fbo != nullptr) {
- nests.last()->fbo[0]->bind();
- glClear(GL_COLOR_BUFFER_BIT);
- nests.last()->fbo[0]->release();
- }
- }
-
- int audio_track_count = 0;
-
- QVector current_clips;
-
- for (int i=0;iclips.size();i++) {
- Clip* c = s->clips.at(i);
-
- // if clip starts within one second and/or hasn't finished yet
- if (c != nullptr) {
- if (!(!nests.isEmpty() && !same_sign(c->track, nests.last()->track))) {
- bool clip_is_active = false;
-
- if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_FOOTAGE) {
- Footage* m = c->media->to_footage();
- if (!m->invalid && !(c->track >= 0 && !is_audio_device_set())) {
- if (m->ready) {
- const FootageStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
- if (ms != nullptr && is_clip_active(c, playhead)) {
- // if thread is already working, we don't want to touch this,
- // but we also don't want to hang the UI thread
- if (!c->open) {
- open_clip(c, !rendering);
- }
- clip_is_active = true;
- if (c->track >= 0) audio_track_count++;
- } else if (c->open) {
- close_clip(c, false);
- }
- } else {
- //qWarning() << "Media '" + m->name + "' was not ready, retrying...";
- texture_failed = true;
- }
- }
- } else {
- if (is_clip_active(c, playhead)) {
- if (!c->open) open_clip(c, !rendering);
- clip_is_active = true;
- } else if (c->open) {
- close_clip(c, false);
- }
- }
- if (clip_is_active) {
- bool added = false;
- for (int j=0;jtrack < c->track) {
- current_clips.insert(j, c);
- added = true;
- break;
- }
- }
- if (!added) {
- current_clips.append(c);
- }
- }
- }
- }
- }
-
- int half_width = s->width/2;
- int half_height = s->height/2;
- if (rendering || !nests.isEmpty()) half_height = -half_height; // invert vertical
+ float dot_size = GIZMO_DOT_SIZE / width() * viewer->seq->width;
+ float target_size = GIZMO_TARGET_SIZE / width() * viewer->seq->width;
glPushMatrix();
glLoadIdentity();
- glOrtho(-half_width, half_width, half_height, -half_height, -1, 10);
-
- for (int i=0;imedia != nullptr && c->media->get_type() == MEDIA_TYPE_FOOTAGE && !c->finished_opening) {
- qWarning() << "Tried to display clip" << i << "but it's closed";
- texture_failed = true;
- } else {
- if (c->track < 0) {
- GLuint textureID = 0;
- int video_width = c->getWidth();
- int video_height = c->getHeight();
-
- if (c->media != nullptr) {
- switch (c->media->get_type()) {
- case MEDIA_TYPE_FOOTAGE:
- // set up opengl texture
- if (c->texture == nullptr) {
- c->texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
- c->texture->setSize(c->stream->codecpar->width, c->stream->codecpar->height);
- c->texture->setFormat(get_gl_tex_fmt_from_av(c->pix_fmt));
- c->texture->setMipLevels(c->texture->maximumMipLevels());
- c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
- c->texture->allocateStorage(get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8);
- }
- get_clip_frame(c, playhead);
- textureID = c->texture->textureId();
- break;
- case MEDIA_TYPE_SEQUENCE:
- textureID = -1;
- break;
- }
- }
-
- if (textureID == 0 && c->media != nullptr) {
- qWarning() << "Texture hasn't been created yet";
- texture_failed = true;
- } else if (playhead >= c->get_timeline_in_with_transition()) {
- glPushMatrix();
-
- // start preparing cache
- if (c->fbo == nullptr) {
- c->fbo = new QOpenGLFramebufferObject* [2];
- c->fbo[0] = new QOpenGLFramebufferObject(video_width, video_height);
- c->fbo[1] = new QOpenGLFramebufferObject(video_width, video_height);
- }
-
- // clear fbos
- /*c->fbo[0]->bind();
- glClear(GL_COLOR_BUFFER_BIT);
- c->fbo[0]->release();
- c->fbo[1]->bind();
- glClear(GL_COLOR_BUFFER_BIT);
- c->fbo[1]->release();*/
-
- bool fbo_switcher = false;
-
- glViewport(0, 0, video_width, video_height);
-
- GLuint composite_texture;
-
- if (c->media == nullptr) {
- c->fbo[fbo_switcher]->bind();
- glClear(GL_COLOR_BUFFER_BIT);
- c->fbo[fbo_switcher]->release();
- composite_texture = c->fbo[fbo_switcher]->texture();
- } else {
- // for nested sequences
- if (c->media->get_type()== MEDIA_TYPE_SEQUENCE) {
- nests.append(c);
- textureID = compose_sequence(nests, render_audio);
- nests.removeLast();
- fbo_switcher = true;
- }
-
- composite_texture = draw_clip(c->fbo[fbo_switcher], textureID, true);
- }
-
- fbo_switcher = !fbo_switcher;
-
- // set up default coords
- GLTextureCoords coords;
- coords.grid_size = 1;
- coords.vertexTopLeftX = coords.vertexBottomLeftX = -video_width/2;
- coords.vertexTopLeftY = coords.vertexTopRightY = -video_height/2;
- coords.vertexTopRightX = coords.vertexBottomRightX = video_width/2;
- coords.vertexBottomLeftY = coords.vertexBottomRightY = video_height/2;
- coords.vertexBottomLeftZ = coords.vertexBottomRightZ = coords.vertexTopLeftZ = coords.vertexTopRightZ = 1;
- coords.textureTopLeftY = coords.textureTopRightY = coords.textureTopLeftX = coords.textureBottomLeftX = 0.0;
- coords.textureBottomLeftY = coords.textureBottomRightY = coords.textureTopRightX = coords.textureBottomRightX = 1.0;
- coords.textureTopLeftQ = coords.textureTopRightQ = coords.textureTopLeftQ = coords.textureBottomLeftQ = 1;
-
- // set up autoscale
- if (c->autoscale && (video_width != s->width && video_height != s->height)) {
- float width_multiplier = float(s->width) / float(video_width);
- float height_multiplier = float(s->height) / float(video_height);
- float scale_multiplier = qMin(width_multiplier, height_multiplier);
- glScalef(scale_multiplier, scale_multiplier, 1);
- }
-
- // EFFECT CODE START
- double timecode = get_timecode(c, playhead);
-
- Effect* first_gizmo_effect = nullptr;
- Effect* selected_effect = nullptr;
-
- for (int j=0;jeffects.size();j++) {
- Effect* e = c->effects.at(j);
- process_effect(c, e, timecode, coords, composite_texture, fbo_switcher, TA_NO_TRANSITION);
-
- if (e->are_gizmos_enabled()) {
- if (first_gizmo_effect == nullptr) first_gizmo_effect = e;
- if (e->container->selected) selected_effect = e;
- }
- }
-
- if (!rendering) {
- if (selected_effect != nullptr) {
- gizmos = selected_effect;
- } else if (panel_timeline->is_clip_selected(c, true)) {
- gizmos = first_gizmo_effect;
- }
- }
-
- if (c->get_opening_transition() != nullptr) {
- int transition_progress = playhead - c->get_timeline_in_with_transition();
- if (transition_progress < c->get_opening_transition()->get_length()) {
- process_effect(c, c->get_opening_transition(), (double)transition_progress/(double)c->get_opening_transition()->get_length(), coords, composite_texture, fbo_switcher, TA_OPENING_TRANSITION);
- }
- }
-
- if (c->get_closing_transition() != nullptr) {
- int transition_progress = playhead - (c->get_timeline_out_with_transition() - c->get_closing_transition()->get_length());
- if (transition_progress >= 0 && transition_progress < c->get_closing_transition()->get_length()) {
- process_effect(c, c->get_closing_transition(), (double)transition_progress/(double)c->get_closing_transition()->get_length(), coords, composite_texture, fbo_switcher, TA_CLOSING_TRANSITION);
- }
- }
- // EFFECT CODE END
-
- if (!nests.isEmpty()) {
- nests.last()->fbo[0]->bind();
- glViewport(0, 0, s->width, s->height);
- } else if (rendering) {
- glViewport(0, 0, s->width, s->height);
- } else {
- int widget_width = width();
- int widget_height = height();
-
- widget_width *= QApplication::desktop()->devicePixelRatio();
- widget_height *= QApplication::desktop()->devicePixelRatio();
-
- glViewport(0, 0, widget_width, widget_height);
- }
-
- glBindTexture(GL_TEXTURE_2D, composite_texture);
-
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- glBegin(GL_QUADS);
-
- if (coords.grid_size <= 1) {
- float z = 0.0f;
-
- glTexCoord2f(coords.textureTopLeftX, coords.textureTopLeftY); // top left
- glVertex3f(coords.vertexTopLeftX, coords.vertexTopLeftY, z); // top left
- glTexCoord2f(coords.textureTopRightX, coords.textureTopRightY); // top right
- glVertex3f(coords.vertexTopRightX, coords.vertexTopRightY, z); // top right
- glTexCoord2f(coords.textureBottomRightX, coords.textureBottomRightY); // bottom right
- glVertex3f(coords.vertexBottomRightX, coords.vertexBottomRightY, z); // bottom right
- glTexCoord2f(coords.textureBottomLeftX, coords.textureBottomLeftY); // bottom left
- glVertex3f(coords.vertexBottomLeftX, coords.vertexBottomLeftY, z); // bottom left
- } else {
- float rows = coords.grid_size;
- float cols = coords.grid_size;
-
- for (float k=0;kgizmo_draw(timecode, coords); // set correct gizmo coords
- gizmos->gizmo_world_to_screen();
-
- drawn_gizmos = true;
- }
-
- if (!nests.isEmpty()) {
- nests.last()->fbo[0]->release();
- if (default_fbo != nullptr) default_fbo->bind();
- }
-
- glPopMatrix();
-
- /*GLfloat motion_blur_frac = (GLfloat) motion_blur_prog / (GLfloat) motion_blur_lim;
- if (motion_blur_prog == 0) {
- glAccum(GL_LOAD, motion_blur_frac);
- } else {
- glAccum(GL_ACCUM, motion_blur_frac);
- }
- motion_blur_prog++;*/
- }
- } else {
- if (render_audio || (config.enable_audio_scrubbing && audio_scrub)) {
- if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
- nests.append(c);
- compose_sequence(nests, render_audio);
- nests.removeLast();
- } else {
- if (c->lock.tryLock()) {
- // clip is not caching, start caching audio
- cache_clip(c, playhead, c->audio_reset, !render_audio, nests);
- c->lock.unlock();
- }
- }
- }
-
- // visually update all the keyframe values
- if (c->sequence == viewer->seq) { // only if you can currently see them
- double ts = (playhead - c->get_timeline_in_with_transition() + c->get_clip_in_with_transition())/s->frame_rate;
- for (int i=0;ieffects.size();i++) {
- Effect* e = c->effects.at(i);
- for (int j=0;jrow_count();j++) {
- EffectRow* r = e->row(j);
- for (int k=0;kfieldCount();k++) {
- r->field(k)->validate_keyframe_data(ts);
- }
- }
- }
- }
+ glOrtho(0, viewer->seq->width, 0, viewer->seq->height, -1, 10);
+ float gizmo_z = 0.0f;
+ for (int j=0;jgizmo_count();j++) {
+ EffectGizmo* g = gizmos->gizmo(j);
+ glColor4f(g->color.redF(), g->color.greenF(), g->color.blueF(), 1.0);
+ switch (g->get_type()) {
+ case GIZMO_TYPE_DOT: // draw dot
+ glBegin(GL_QUADS);
+ glVertex3f(g->screen_pos[0].x()-dot_size, g->screen_pos[0].y()-dot_size, gizmo_z);
+ glVertex3f(g->screen_pos[0].x()+dot_size, g->screen_pos[0].y()-dot_size, gizmo_z);
+ glVertex3f(g->screen_pos[0].x()+dot_size, g->screen_pos[0].y()+dot_size, gizmo_z);
+ glVertex3f(g->screen_pos[0].x()-dot_size, g->screen_pos[0].y()+dot_size, gizmo_z);
+ glEnd();
+ break;
+ case GIZMO_TYPE_POLY: // draw lines
+ glBegin(GL_LINES);
+ for (int k=1;kget_point_count();k++) {
+ glVertex3f(g->screen_pos[k-1].x(), g->screen_pos[k-1].y(), gizmo_z);
+ glVertex3f(g->screen_pos[k].x(), g->screen_pos[k].y(), gizmo_z);
}
+ glVertex3f(g->screen_pos[g->get_point_count()-1].x(), g->screen_pos[g->get_point_count()-1].y(), gizmo_z);
+ glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y(), gizmo_z);
+ glEnd();
+ break;
+ case GIZMO_TYPE_TARGET: // draw target
+ glBegin(GL_LINES);
+ glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z);
+ glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z);
+
+ glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z);
+ glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z);
+
+ glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z);
+ glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z);
+
+ glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z);
+ glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z);
+
+ glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y(), gizmo_z);
+ glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y(), gizmo_z);
+
+ glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()-target_size, gizmo_z);
+ glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()+target_size, gizmo_z);
+ glEnd();
+ break;
}
}
-
- if (audio_track_count == 0) {
- viewer->play_wake();
- }
-
glPopMatrix();
- if (!nests.isEmpty() && nests.last()->fbo != nullptr) {
- // returns nested clip's texture
- return nests.last()->fbo[0]->texture();
- }
-
- return 0;
+ glColor4f(color[0], color[1], color[2], color[3]);
}
void ViewerWidget::paintGL() {
- drawn_gizmos = false;
- force_quit = false;
- if (viewer->seq != nullptr) {
- gizmos = nullptr;
+ if (waveform) {
+ draw_waveform_func();
+ } else {
+ renderer->mutex.lock();
- bool render_audio = (viewer->playing || rendering);
- bool loop = false;
- do {
- loop = false;
+ makeCurrent();
- glClearColor(0, 0, 0, 1);
- glMatrixMode(GL_MODELVIEW);
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glEnable(GL_DEPTH);
+ // clear to solid black
- texture_failed = false;
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
- if (!rendering) retry_timer.stop();
+ // set color multipler to straight white
+ glColor4f(1.0, 1.0, 1.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
+ glEnable(GL_TEXTURE_2D);
- // compose video preview
- glClearColor(0, 0, 0, 0);
+ // set screen coords to widget size
+ glLoadIdentity();
+ glOrtho(0, 1, 0, 1, -1, 1);
- QVector nests;
+ // draw texture from render thread
- compose_sequence(nests, render_audio);
+ glBindTexture(GL_TEXTURE_2D, renderer->texColorBuffer);
- if (waveform) {
- QPainter p(this);
- if (viewer->seq->using_workarea) {
- int in_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->workarea_in) - waveform_scroll;
- int out_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->workarea_out) - waveform_scroll;
+ glBegin(GL_QUADS);
- p.fillRect(QRect(in_x, 0, out_x - in_x, height()), QColor(255, 255, 255, 64));
- p.setPen(Qt::white);
- p.drawLine(in_x, 0, in_x, height());
- p.drawLine(out_x, 0, out_x, height());
- }
- QRect wr = rect();
- wr.setX(wr.x() - waveform_scroll);
+ glVertex2f(0, 0);
+ glTexCoord2f(0, 0);
+ glVertex2f(0, 1);
+ glTexCoord2f(1, 0);
+ glVertex2f(1, 1);
+ glTexCoord2f(1, 1);
+ glVertex2f(1, 0);
+ glTexCoord2f(0, 1);
- p.setPen(Qt::green);
- draw_waveform(waveform_clip, waveform_ms, waveform_clip->timeline_out, &p, wr, waveform_scroll, width()+waveform_scroll, waveform_zoom);
- p.setPen(Qt::red);
- int playhead_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->playhead) - waveform_scroll;
- p.drawLine(playhead_x, 0, playhead_x, height());
- }
+ glEnd();
- if (force_quit) break;
- if (texture_failed) {
- if (rendering) {
- qInfo() << "Texture failed - looping";
- loop = true;
- } else if (!viewer->playing) {
- retry_timer.start();
- }
- }
+ glBindTexture(GL_TEXTURE_2D, 0);
- if (config.show_title_safe_area && !rendering) {
- drawTitleSafeArea();
- }
+ // draw title/action safe area
+ if (config.show_title_safe_area) {
+ draw_title_safe_area();
+ }
- if (gizmos != nullptr && drawn_gizmos) {
- float color[4];
- glGetFloatv(GL_CURRENT_COLOR, color);
+ gizmos = renderer->gizmos;
+ if (gizmos != nullptr) {
+ draw_gizmos();
+ }
- float dot_size = GIZMO_DOT_SIZE / width() * viewer->seq->width;
- float target_size = GIZMO_TARGET_SIZE / width() * viewer->seq->width;
+ glDisable(GL_TEXTURE_2D);
- glPushMatrix();
- glLoadIdentity();
- glOrtho(0, viewer->seq->width, viewer->seq->height, 0, -1, 10);
- float gizmo_z = 0.0f;
- for (int j=0;jgizmo_count();j++) {
- EffectGizmo* g = gizmos->gizmo(j);
- glColor4f(g->color.redF(), g->color.greenF(), g->color.blueF(), 1.0);
- switch (g->get_type()) {
- case GIZMO_TYPE_DOT: // draw dot
- glBegin(GL_QUADS);
- glVertex3f(g->screen_pos[0].x()-dot_size, g->screen_pos[0].y()-dot_size, gizmo_z);
- glVertex3f(g->screen_pos[0].x()+dot_size, g->screen_pos[0].y()-dot_size, gizmo_z);
- glVertex3f(g->screen_pos[0].x()+dot_size, g->screen_pos[0].y()+dot_size, gizmo_z);
- glVertex3f(g->screen_pos[0].x()-dot_size, g->screen_pos[0].y()+dot_size, gizmo_z);
- glEnd();
- break;
- case GIZMO_TYPE_POLY: // draw lines
- glBegin(GL_LINES);
- for (int k=1;kget_point_count();k++) {
- glVertex3f(g->screen_pos[k-1].x(), g->screen_pos[k-1].y(), gizmo_z);
- glVertex3f(g->screen_pos[k].x(), g->screen_pos[k].y(), gizmo_z);
- }
- glVertex3f(g->screen_pos[g->get_point_count()-1].x(), g->screen_pos[g->get_point_count()-1].y(), gizmo_z);
- glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y(), gizmo_z);
- glEnd();
- break;
- case GIZMO_TYPE_TARGET: // draw target
- glBegin(GL_LINES);
- glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z);
- glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z);
+ if (window != nullptr && window->isVisible()) {
+ window->set_texture(renderer->texColorBuffer, double(viewer->seq->width)/double(viewer->seq->height), &renderer->mutex);
+ }
- glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z);
- glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z);
+ renderer->mutex.unlock();
- glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z);
- glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z);
-
- glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z);
- glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z);
-
- glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y(), gizmo_z);
- glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y(), gizmo_z);
-
- glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()-target_size, gizmo_z);
- glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()+target_size, gizmo_z);
- glEnd();
- break;
- }
- }
- glPopMatrix();
-
- glColor4f(color[0], color[1], color[2], color[3]);
-
- drawn_gizmos = true;
- }
-
- glDisable(GL_DEPTH);
- glDisable(GL_BLEND);
- glDisable(GL_TEXTURE_2D);
- } while (loop);
+ if (renderer->did_texture_fail()) {
+ doneCurrent();
+ renderer->start_render(context(), viewer->seq);
+ }
}
}
diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h
index 0450efd52..a4909ac5f 100644
--- a/ui/viewerwidget.h
+++ b/ui/viewerwidget.h
@@ -18,62 +18,64 @@ class Effect;
class EffectGizmo;
class ViewerContainer;
struct GLTextureCoords;
+class RenderThread;
+class ViewerWindow;
class ViewerWidget : public QOpenGLWidget, QOpenGLFunctions
{
Q_OBJECT
public:
- ViewerWidget(QWidget *parent = 0);
+ ViewerWidget(QWidget *parent = 0);
+ ~ViewerWidget();
- void paintGL();
- void initializeGL();
+ void delete_function();
+ void close_window();
+
+ void paintGL();
+ void initializeGL();
Viewer* viewer;
- ViewerContainer* container;
-
- QOpenGLFramebufferObject* default_fbo;
+ ViewerContainer* container;
bool waveform;
Clip* waveform_clip;
- const FootageStream* waveform_ms;
- double waveform_zoom;
- int waveform_scroll;
+ const FootageStream* waveform_ms;
+ double waveform_zoom;
+ int waveform_scroll;
- bool force_quit;
+ void frame_update();
+ RenderThread* get_renderer();
public slots:
- void delete_function();
- void set_waveform_scroll(int s);
+ void set_waveform_scroll(int s);
protected:
- void paintEvent(QPaintEvent *e);
-// void resizeGL(int w, int h);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
- QTimer retry_timer;
- void drawTitleSafeArea();
+ void draw_waveform_func();
+ void draw_title_safe_area();
+ void draw_gizmos();
+ EffectGizmo* get_gizmo_from_mouse(int x, int y);
+ void move_gizmos(QMouseEvent *event, bool done);
bool dragging;
void seek_from_click(int x);
- GLuint compose_sequence(QVector &nests, bool render_audio);
- GLuint draw_clip(QOpenGLFramebufferObject *clip, GLuint texture, bool clear);
- void process_effect(Clip* c, Effect* e, double timecode, GLTextureCoords& coords, GLuint& composite_texture, bool& fbo_switcher, int data);
- Effect* gizmos;
- int drag_start_x;
- int drag_start_y;
- int gizmo_x_mvmt;
- int gizmo_y_mvmt;
- EffectGizmo* selected_gizmo;
- EffectGizmo* get_gizmo_from_mouse(int x, int y);
- bool drawn_gizmos;
- void move_gizmos(QMouseEvent *event, bool done);
+ Effect* gizmos;
+ int drag_start_x;
+ int drag_start_y;
+ int gizmo_x_mvmt;
+ int gizmo_y_mvmt;
+ EffectGizmo* selected_gizmo;
+ RenderThread* renderer;
+ ViewerWindow* window;
private slots:
+ void context_destroy();
void retry();
void show_context_menu();
void save_frame();
- void show_fullscreen();
-
- void set_fit_zoom();
- void set_custom_zoom();
- void set_menu_zoom(QAction *action);
+ void queue_repaint();
+ void fullscreen_menu_action(QAction* action);
+ void set_fit_zoom();
+ void set_custom_zoom();
+ void set_menu_zoom(QAction *action);
};
#endif // VIEWERWIDGET_H
diff --git a/ui/viewerwindow.cpp b/ui/viewerwindow.cpp
new file mode 100644
index 000000000..7982c0b1f
--- /dev/null
+++ b/ui/viewerwindow.cpp
@@ -0,0 +1,68 @@
+#include "viewerwindow.h"
+
+#include
+
+ViewerWindow::ViewerWindow(QOpenGLContext *share) :
+ QOpenGLWindow(share),
+ texture(0),
+ mutex(nullptr)
+{}
+
+void ViewerWindow::set_texture(GLuint t, double iar, QMutex* imutex) {
+ texture = t;
+ ar = iar;
+ mutex = imutex;
+ update();
+}
+
+void ViewerWindow::paintGL() {
+ if (texture > 0) {
+ if (mutex != nullptr) mutex->lock();
+
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glEnable(GL_TEXTURE_2D);
+
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ glLoadIdentity();
+ glOrtho(0, 1, 0, 1, -1, 1);
+
+ glBegin(GL_QUADS);
+
+ double top = 0;
+ double left = 0;
+ double right = 1;
+ double bottom = 1;
+
+ double widget_ar = double(width()) / double(height());
+
+ if (widget_ar > ar) {
+ double width = 1.0 * ar / widget_ar;
+ left = (1.0 - width)*0.5;
+ right = left + width;
+ } else {
+ double height = 1.0 / ar * widget_ar;
+ top = (1.0 - height)*0.5;
+ bottom = top + height;
+ }
+
+ glVertex2d(left, top);
+ glTexCoord2d(0, 0);
+ glVertex2d(left, bottom);
+ glTexCoord2d(1, 0);
+ glVertex2d(right, bottom);
+ glTexCoord2d(1, 1);
+ glVertex2d(right, top);
+ glTexCoord2d(0, 1);
+
+ glEnd();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glDisable(GL_TEXTURE_2D);
+
+ if (mutex != nullptr) mutex->unlock();
+ }
+}
diff --git a/ui/viewerwindow.h b/ui/viewerwindow.h
new file mode 100644
index 000000000..fd4e9e972
--- /dev/null
+++ b/ui/viewerwindow.h
@@ -0,0 +1,20 @@
+#ifndef VIEWERWINDOW_H
+#define VIEWERWINDOW_H
+
+#include
+
+class QMutex;
+
+class ViewerWindow : public QOpenGLWindow {
+ Q_OBJECT
+public:
+ ViewerWindow(QOpenGLContext* share);
+ void set_texture(GLuint t, double iar, QMutex *imutex);
+private:
+ void paintGL();
+ GLuint texture;
+ double ar;
+ QMutex* mutex;
+};
+
+#endif // VIEWERWINDOW_H