Merge pull request #325 from olive-editor/threadedgl2

move renderer to separate thread
This commit is contained in:
itsmattkc
2019-01-17 23:10:27 +11:00
committed by GitHub
37 changed files with 1597 additions and 1020 deletions
+1 -1
View File
@@ -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("<font color='red'><b>[FATAL]</b> %1 (%2:%3, %4)</font><br>").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);
+60 -62
View File
@@ -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);
+26 -24
View File
@@ -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<QTextEdit*>(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());
}
+35 -28
View File
@@ -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();
}
+12 -5
View File
@@ -3,6 +3,8 @@
#include <QThread>
#include <QOffscreenSurface>
#include <QMutex>
#include <QWaitCondition>
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
+20 -20
View File
@@ -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);
}
}
+1 -1
View File
@@ -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;i<argc;i++) {
+76 -64
View File
@@ -351,6 +351,15 @@ void MainWindow::load_css_from_file(const QString &fn) {
}
}
void MainWindow::set_rendering_state(bool rendering) {
audio_rendering = rendering;
if (rendering) {
autorecovery_timer.stop();
} else {
autorecovery_timer.start();
}
}
void MainWindow::show_about() {
AboutDialog a(this);
a.exec();
@@ -377,10 +386,10 @@ void MainWindow::delete_slot() {
}
void MainWindow::select_all() {
QDockWidget* focused_panel = get_focused_panel();
if (focused_panel == panel_timeline) {
QDockWidget* focused_panel = get_focused_panel();
if (focused_panel == panel_timeline) {
panel_timeline->select_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;i<sequence->clips.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;i<sequence->clips.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;i<sequence->clips.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<QPushButton*>(static_cast<QAction*>(sender())->data().value<quintptr>())->click();
}
+3 -1
View File
@@ -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();
+8 -2
View File
@@ -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 +=
+9 -9
View File
@@ -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();
+1 -1
View File
@@ -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;
+37 -41
View File
@@ -39,10 +39,6 @@
#include <QSplitter>
#include <QStatusBar>
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("<none>"));
setWindowTitle(title + tr("<none>"));
} 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;i<clip->sequence->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);
+2 -3
View File
@@ -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<Selection> &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:
+9 -7
View File
@@ -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;i<seq->clips.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();
}
+6 -3
View File
@@ -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);
+2 -1
View File
@@ -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
+8 -2
View File
@@ -376,11 +376,13 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
qint16 new_sample = static_cast<qint16>((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<quint8>((mixed_sample >> 8) & 0xFF);
audio_ibuffer[lower_byte_index] = static_cast<quint8>(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<Clip*>& 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));
+9 -4
View File
@@ -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;
}
}
}
+2 -4
View File
@@ -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<Clip *> &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);
+1 -1
View File
@@ -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();
}
+1 -1
View File
@@ -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);
+11 -8
View File
@@ -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;i<fieldCount();i++) {
+8 -4
View File
@@ -7,14 +7,19 @@
#include "debug.h"
ProjectModel::ProjectModel(QObject *parent) : QAbstractItemModel(parent), root_item(nullptr) {
root_item = new Media(0);
root_item->root = 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();
}
+2 -1
View File
@@ -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();
+43 -35
View File
@@ -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);
}
+13 -12
View File
@@ -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
+503
View File
@@ -0,0 +1,503 @@
#include "renderfunctions.h"
#include <QOpenGLFramebufferObject>
#include <QApplication>
#include <QDesktopWidget>
#include <QDebug>
#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 <libavformat/avformat.h>
}
//#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, &current_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<Clip*>& nests,
bool video,
bool render_audio,
Effect** gizmos,
bool& texture_failed,
bool rendering) {
GLint current_fbo = 0;
if (video) {
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
}
Sequence* s = seq;
long playhead = s->playhead;
if (!nests.isEmpty()) {
for (int i=0;i<nests.size();i++) {
s = nests.at(i)->media->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<Clip*> current_clips;
for (int i=0;i<s->clips.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;j<current_clips.size();j++) {
if (current_clips.at(j)->track < 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;i<current_clips.size();i++) {
Clip* c = current_clips.at(i);
if (c->media != 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;j<c->effects.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;k<rows;k++) {
float row_prog = k/rows;
float next_row_prog = (k+1)/rows;
for (float j=0;j<cols;j++) {
float col_prog = j/cols;
float next_col_prog = (j+1)/cols;
float vertexTLX = float_lerp(coords.vertexTopLeftX, coords.vertexBottomLeftX, row_prog);
float vertexTRX = float_lerp(coords.vertexTopRightX, coords.vertexBottomRightX, row_prog);
float vertexBLX = float_lerp(coords.vertexTopLeftX, coords.vertexBottomLeftX, next_row_prog);
float vertexBRX = float_lerp(coords.vertexTopRightX, coords.vertexBottomRightX, next_row_prog);
float vertexTLY = float_lerp(coords.vertexTopLeftY, coords.vertexTopRightY, col_prog);
float vertexTRY = float_lerp(coords.vertexTopLeftY, coords.vertexTopRightY, next_col_prog);
float vertexBLY = float_lerp(coords.vertexBottomLeftY, coords.vertexBottomRightY, col_prog);
float vertexBRY = float_lerp(coords.vertexBottomLeftY, coords.vertexBottomRightY, next_col_prog);
glTexCoord2f(float_lerp(coords.textureTopLeftX, coords.textureTopRightX, col_prog), float_lerp(coords.textureTopLeftY, coords.textureBottomLeftY, row_prog)); // top left
glVertex2f(float_lerp(vertexTLX, vertexTRX, col_prog), float_lerp(vertexTLY, vertexBLY, row_prog)); // top left
glTexCoord2f(float_lerp(coords.textureTopLeftX, coords.textureTopRightX, next_col_prog), float_lerp(coords.textureTopRightY, coords.textureBottomRightY, row_prog)); // top right
glVertex2f(float_lerp(vertexTLX, vertexTRX, next_col_prog), float_lerp(vertexTRY, vertexBRY, row_prog)); // top right
glTexCoord2f(float_lerp(coords.textureBottomLeftX, coords.textureBottomRightX, next_col_prog), float_lerp(coords.textureTopRightY, coords.textureBottomRightY, next_row_prog)); // bottom right
glVertex2f(float_lerp(vertexBLX, vertexBRX, next_col_prog), float_lerp(vertexTRY, vertexBRY, next_row_prog)); // bottom right
glTexCoord2f(float_lerp(coords.textureBottomLeftX, coords.textureBottomRightX, col_prog), float_lerp(coords.textureTopLeftY, coords.textureBottomLeftY, next_row_prog)); // bottom left
glVertex2f(float_lerp(vertexBLX, vertexBRX, col_prog), float_lerp(vertexTLY, vertexBLY, next_row_prog)); // bottom left
}
}
}
glEnd();
glBindTexture(GL_TEXTURE_2D, 0); // unbind texture
// prepare gizmos
if ((*gizmos) != nullptr
&& nests.isEmpty()
&& ((*gizmos) == first_gizmo_effect
|| (*gizmos) == selected_effect)) {
(*gizmos)->gizmo_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;i<c->effects.size();i++) {
Effect* e = c->effects.at(i);
for (int j=0;j<e->row_count();j++) {
EffectRow* r = e->row(j);
for (int k=0;k<r->fieldCount();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<Clip*> nests;
bool texture_failed;
compose_sequence(viewer, nullptr, seq, nests, false, render_audio, nullptr, texture_failed, audio_rendering);
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef RENDERFUNCTIONS_H
#define RENDERFUNCTIONS_H
#include <QOpenGLContext>
#include <QVector>
class Effect;
class Viewer;
struct Sequence;
struct Clip;
GLuint compose_sequence(Viewer* viewer,
QOpenGLContext* ctx,
Sequence* seq,
QVector<Clip*>& 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
+199
View File
@@ -0,0 +1,199 @@
#include "renderthread.h"
#include <QApplication>
#include <QImage>
#include <QOpenGLFunctions>
#include <QDebug>
#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<Clip*> 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;
}
+54
View File
@@ -0,0 +1,54 @@
#ifndef RENDERTHREAD_H
#define RENDERTHREAD_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include <QOpenGLFramebufferObject>
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
+7 -7
View File
@@ -98,7 +98,7 @@ void TimelineWidget::show_context_menu(const QPoint& pos) {
QVector<Clip*> selected_clips;
for (int i=0;i<sequence->clips.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;i<sequence->clips.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<Clip*> selected_clips;
for (int i=0;i<sequence->clips.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;i<clip->linked.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;i<sequence->clips.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
+55 -54
View File
@@ -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) {
+224 -581
View File
@@ -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 <QPainter>
#include <QAudioOutput>
@@ -37,21 +41,22 @@
#include <QDesktopWidget>
#include <QInputDialog>
#include <QApplication>
#include <QScreen>
#include <QMessageBox>
extern "C" {
#include <libavformat/avformat.h>
}
#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<QScreen*> screens = QGuiApplication::screens();
if (window != nullptr && window->isVisible()) {
fullscreen_menu->addAction(tr("Disable"));
}
for (int i=0;i<screens.size();i++) {
QAction* screen_action = fullscreen_menu->addAction(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;i<gizmos->gizmo_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<Clip*>& nests, bool render_audio) {
Sequence* s = viewer->seq;
long playhead = s->playhead;
if (!nests.isEmpty()) {
for (int i=0;i<nests.size();i++) {
s = nests.at(i)->media->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<Clip*> current_clips;
for (int i=0;i<s->clips.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;j<current_clips.size();j++) {
if (current_clips.at(j)->track < 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;i<current_clips.size();i++) {
GL_DEFAULT_BLEND
glColor4f(1.0, 1.0, 1.0, 1.0);
Clip* c = current_clips.at(i);
if (c->media != 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;j<c->effects.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;k<rows;k++) {
float row_prog = k/rows;
float next_row_prog = (k+1)/rows;
for (float j=0;j<cols;j++) {
float col_prog = j/cols;
float next_col_prog = (j+1)/cols;
float vertexTLX = float_lerp(coords.vertexTopLeftX, coords.vertexBottomLeftX, row_prog);
float vertexTRX = float_lerp(coords.vertexTopRightX, coords.vertexBottomRightX, row_prog);
float vertexBLX = float_lerp(coords.vertexTopLeftX, coords.vertexBottomLeftX, next_row_prog);
float vertexBRX = float_lerp(coords.vertexTopRightX, coords.vertexBottomRightX, next_row_prog);
float vertexTLY = float_lerp(coords.vertexTopLeftY, coords.vertexTopRightY, col_prog);
float vertexTRY = float_lerp(coords.vertexTopLeftY, coords.vertexTopRightY, next_col_prog);
float vertexBLY = float_lerp(coords.vertexBottomLeftY, coords.vertexBottomRightY, col_prog);
float vertexBRY = float_lerp(coords.vertexBottomLeftY, coords.vertexBottomRightY, next_col_prog);
glTexCoord2f(float_lerp(coords.textureTopLeftX, coords.textureTopRightX, col_prog), float_lerp(coords.textureTopLeftY, coords.textureBottomLeftY, row_prog)); // top left
glVertex2f(float_lerp(vertexTLX, vertexTRX, col_prog), float_lerp(vertexTLY, vertexBLY, row_prog)); // top left
glTexCoord2f(float_lerp(coords.textureTopLeftX, coords.textureTopRightX, next_col_prog), float_lerp(coords.textureTopRightY, coords.textureBottomRightY, row_prog)); // top right
glVertex2f(float_lerp(vertexTLX, vertexTRX, next_col_prog), float_lerp(vertexTRY, vertexBRY, row_prog)); // top right
glTexCoord2f(float_lerp(coords.textureBottomLeftX, coords.textureBottomRightX, next_col_prog), float_lerp(coords.textureTopRightY, coords.textureBottomRightY, next_row_prog)); // bottom right
glVertex2f(float_lerp(vertexBLX, vertexBRX, next_col_prog), float_lerp(vertexTRY, vertexBRY, next_row_prog)); // bottom right
glTexCoord2f(float_lerp(coords.textureBottomLeftX, coords.textureBottomRightX, col_prog), float_lerp(coords.textureTopLeftY, coords.textureBottomLeftY, next_row_prog)); // bottom left
glVertex2f(float_lerp(vertexBLX, vertexBRX, col_prog), float_lerp(vertexTLY, vertexBLY, next_row_prog)); // bottom left
}
}
}
glEnd();
glBindTexture(GL_TEXTURE_2D, 0); // unbind texture
if (gizmos != nullptr && !drawn_gizmos) {
gizmos->gizmo_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;i<c->effects.size();i++) {
Effect* e = c->effects.at(i);
for (int j=0;j<e->row_count();j++) {
EffectRow* r = e->row(j);
for (int k=0;k<r->fieldCount();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;j<gizmos->gizmo_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;k<g->get_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<Clip*> 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;j<gizmos->gizmo_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;k<g->get_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);
}
}
}
+35 -33
View File
@@ -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<Clip *> &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
+68
View File
@@ -0,0 +1,68 @@
#include "viewerwindow.h"
#include <QMutex>
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();
}
}
+20
View File
@@ -0,0 +1,20 @@
#ifndef VIEWERWINDOW_H
#define VIEWERWINDOW_H
#include <QOpenGLWindow>
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