clips);
protected:
+ /**
+ * @brief Accept override. Saves the current properties to the array of Clips.
+ */
virtual void accept() override;
private:
+ /**
+ * @brief Internal clip array (set in the constructor)
+ */
QVector clips_;
+ /**
+ * @brief Widget for setting the Clip names
+ */
QLineEdit* clip_name_field_;
+
+ /**
+ * @brief Widget for setting the Clip durations
+ */
LabelSlider* duration_field_;
};
diff --git a/dialogs/debugdialog.h b/dialogs/debugdialog.h
index 3f9d84d75..a1b6aab3c 100644
--- a/dialogs/debugdialog.h
+++ b/dialogs/debugdialog.h
@@ -24,22 +24,52 @@
#include
#include
+/**
+ * @brief The DebugDialog class
+ *
+ * A dialog to display the current debug output.
+ */
class DebugDialog : public QDialog {
- Q_OBJECT
+ Q_OBJECT
public:
- DebugDialog(QWidget* parent = 0);
+ /**
+ * @brief DebugDialog Constructor
+ * @param parent
+ *
+ * Parent widget. Usually MainWindow.
+ */
+ DebugDialog(QWidget* parent = nullptr);
+
+ /**
+ * @brief Retranslate window title
+ *
+ * Sets title based on the current translation.
+ */
void Retranslate();
public slots:
- void update_log();
+ /**
+ * @brief Update the visual log with the debug text from get_debug_str()
+ */
+ void update_log();
protected:
+ /**
+ * @brief Overrides change event to trigger Retranslate() on a LanguageChange event.
+ */
virtual void changeEvent(QEvent* e) override;
+ /**
+ * @brief Overrides show event to trigger an update of the visual log (the visual log does not update while the
+ * debug dialog is hidden).
+ */
virtual void showEvent(QShowEvent* event) override;
private:
- QTextEdit* textEdit;
+ /**
+ * @brief Display widget for the debug dialog.
+ */
+ QTextEdit* textEdit;
};
namespace olive {
- extern DebugDialog* DebugDialog;
+extern DebugDialog* DebugDialog;
}
#endif // DEBUGDIALOG_H
diff --git a/dialogs/demonotice.cpp b/dialogs/demonotice.cpp
index 3f2cc9169..e91570131 100644
--- a/dialogs/demonotice.cpp
+++ b/dialogs/demonotice.cpp
@@ -25,38 +25,38 @@
#include
DemoNotice::DemoNotice(QWidget *parent) :
- QDialog(parent)
+ QDialog(parent)
{
- setWindowTitle(tr("Welcome to Olive!"));
+ setWindowTitle(tr("Welcome to Olive!"));
- QVBoxLayout* vlayout = new QVBoxLayout(this);
+ QVBoxLayout* vlayout = new QVBoxLayout(this);
- QHBoxLayout* layout = new QHBoxLayout();
- layout->setMargin(10);
- layout->setSpacing(20);
+ QHBoxLayout* layout = new QHBoxLayout();
+ layout->setMargin(10);
+ layout->setSpacing(20);
- QLabel* icon = new QLabel(""
- "
"
- "", this);
- layout->addWidget(icon);
+ QLabel* icon = new QLabel(""
+ "
"
+ "", this);
+ layout->addWidget(icon);
- QLabel* text = new QLabel(""
- ""
- + tr("Welcome to Olive!")
- + "
"
- + tr("Olive is a free open-source video editor released under the GNU GPL. If you have paid for this software, you have been scammed.")
- + "
"
- + tr("This software is currently in ALPHA which means it is unstable and very likely to crash, have bugs, and have missing features. We offer no warranty so use at your own risk. Please report any bugs or feature requests at %1").arg("www.olivevideoeditor.org")
- + "
"
- + tr("Thank you for trying Olive and we hope you enjoy it!")
- + "
", this);
- text->setWordWrap(true);
- layout->addWidget(text);
+ QLabel* text = new QLabel(""
+ ""
+ + tr("Welcome to Olive!")
+ + "
"
+ + tr("Olive is a free open-source video editor released under the GNU GPL. If you have paid for this software, you have been scammed.")
+ + "
"
+ + tr("This software is currently in ALPHA which means it is unstable and very likely to crash, have bugs, and have missing features. We offer no warranty so use at your own risk. Please report any bugs or feature requests at %1").arg("www.olivevideoeditor.org")
+ + "
"
+ + tr("Thank you for trying Olive and we hope you enjoy it!")
+ + "
", this);
+ text->setWordWrap(true);
+ layout->addWidget(text);
- vlayout->addLayout(layout);
+ vlayout->addLayout(layout);
- QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this);
- buttons->setCenterButtons(true);
- connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
- vlayout->addWidget(buttons);
+ QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this);
+ buttons->setCenterButtons(true);
+ connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
+ vlayout->addWidget(buttons);
}
diff --git a/dialogs/demonotice.h b/dialogs/demonotice.h
index b50a690fe..a986e51ab 100644
--- a/dialogs/demonotice.h
+++ b/dialogs/demonotice.h
@@ -23,11 +23,22 @@
#include
+/**
+ * @brief The DemoNotice class
+ *
+ * Simple dialog shown on startup to introduce Olive as alpha software (in release builds).
+ */
class DemoNotice : public QDialog
{
- Q_OBJECT
+ Q_OBJECT
public:
- explicit DemoNotice(QWidget *parent = 0);
+ /**
+ * @brief DemoNotice Constructor
+ * @param parent
+ *
+ * QWidget parent. Usually MainWindow.
+ */
+ explicit DemoNotice(QWidget *parent = nullptr);
};
#endif // DEMONOTICE_H
diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp
index be924d870..73dc3ec65 100644
--- a/dialogs/exportdialog.cpp
+++ b/dialogs/exportdialog.cpp
@@ -20,6 +20,10 @@
#include "exportdialog.h"
+extern "C" {
+#include
+}
+
#include
#include
#include
@@ -39,10 +43,6 @@
#include "rendering/exportthread.h"
#include "ui/mainwindow.h"
-extern "C" {
-#include
-}
-
enum ExportFormats {
FORMAT_3GPP,
FORMAT_AIFF,
@@ -118,9 +118,6 @@ ExportDialog::ExportDialog(QWidget *parent) :
vcodec_params.threads = 0;
}
-ExportDialog::~ExportDialog()
-{}
-
void ExportDialog::add_codec_to_combobox(QComboBox* box, enum AVCodecID codec) {
QString codec_name;
@@ -334,21 +331,42 @@ void ExportDialog::format_changed(int index) {
}
void ExportDialog::render_thread_finished() {
- if (progressBar->value() < 100 && !cancelled) {
+ // Determine if the export succeeded
+ bool succeeded = (progressBar->value() == 100);
+
+ // If it failed and we didn't cancel it, it must have errored out. Show an error message.
+ if (!succeeded && !et->WasInterrupted()) {
QMessageBox::critical(
this,
tr("Export Failed"),
- tr("Export failed - %1").arg(export_error),
+ tr("Export failed - %1").arg(et->GetError()),
QMessageBox::Ok
);
}
+
+ // Clear audio buffer
clear_audio_ibuffer();
+
+ // Re-enable/disable UI widgets based on the rendering state
prep_ui_for_render(false);
+
+ // Move OpenGL context back to the sequence viewer
panel_sequence_viewer->viewer_widget->makeCurrent();
panel_sequence_viewer->viewer_widget->initializeGL();
+
+ // Update the application UI
update_ui(false);
+
+ // Disconnect cancel button from export thread
+ disconnect(renderCancel, SIGNAL(clicked(bool)), et, SLOT(Interrupt()));
+
+ // Free the export thread
et->deleteLater();
- if (progressBar->value() == 100) accept();
+
+ // If the export succeeded, close the dialog
+ if (succeeded) {
+ accept();
+ }
}
void ExportDialog::prep_ui_for_render(bool r) {
@@ -543,7 +561,8 @@ void ExportDialog::export_action() {
et = new ExportThread(params, vcodec_params, this);
connect(et, SIGNAL(finished()), this, SLOT(render_thread_finished()));
- connect(et, SIGNAL(progress_changed(int, qint64)), this, SLOT(update_progress_bar(int, qint64)));
+ connect(et, SIGNAL(ProgressChanged(int, qint64)), this, SLOT(update_progress_bar(int, qint64)));
+ connect(renderCancel, SIGNAL(clicked(bool)), et, SLOT(Interrupt()));
close_active_clips(olive::ActiveSequence.get());
@@ -553,8 +572,6 @@ void ExportDialog::export_action() {
prep_ui_for_render(true);
- cancelled = false;
-
total_export_time_start = QDateTime::currentMSecsSinceEpoch();
et->start();
@@ -587,11 +604,6 @@ void ExportDialog::update_progress_bar(int value, qint64 remaining_ms) {
progressBar->setValue(value);
}
-void ExportDialog::cancel_render() {
- et->continueEncode = false;
- cancelled = true;
-}
-
void ExportDialog::vcodec_changed(int index) {
compressionTypeCombobox->clear();
@@ -755,7 +767,6 @@ void ExportDialog::setup_ui() {
renderCancel = new QPushButton(this);
renderCancel->setIcon(QIcon(":/icons/error.svg"));
renderCancel->setEnabled(false);
- connect(renderCancel, SIGNAL(clicked(bool)), this, SLOT(cancel_render()));
progressLayout->addWidget(renderCancel);
verticalLayout->addLayout(progressLayout);
diff --git a/dialogs/exportdialog.h b/dialogs/exportdialog.h
index 909912c55..df3f14c21 100644
--- a/dialogs/exportdialog.h
+++ b/dialogs/exportdialog.h
@@ -30,34 +30,41 @@
#include
#include "timeline/sequence.h"
-
#include "rendering/exportthread.h"
+/**
+ * @brief The ExportDialog class
+ *
+ * The dialog to initiate an export.
+ */
class ExportDialog : public QDialog
{
Q_OBJECT
public:
- explicit ExportDialog(QWidget *parent = nullptr);
- ~ExportDialog();
- QString export_error;
+ /**
+ * @brief ExportDialog Constructor
+ *
+ * @param parent
+ *
+ * QWidget parent. Usually MainWindow.
+ */
+ explicit ExportDialog(QWidget *parent);
private slots:
void format_changed(int index);
void export_action();
void update_progress_bar(int value, qint64 remaining_ms);
- void cancel_render();
void render_thread_finished();
void vcodec_changed(int index);
void comp_type_changed(int index);
void open_advanced_video_dialog();
private:
- QVector format_strings;
void setup_ui();
-
- ExportThread* et;
void prep_ui_for_render(bool r);
- bool cancelled;
+
+ QVector format_strings;
+ ExportThread* et;
void add_codec_to_combobox(QComboBox* box, enum AVCodecID codec);
diff --git a/effects/effect.cpp b/effects/effect.cpp
index 5690000a9..ec17cb84d 100644
--- a/effects/effect.cpp
+++ b/effects/effect.cpp
@@ -44,7 +44,6 @@
#include "panels/timeline.h"
#include "panels/effectcontrols.h"
#include "panels/grapheditor.h"
-#include "ui/checkboxex.h"
#include "global/debug.h"
#include "global/path.h"
#include "ui/mainwindow.h"
diff --git a/effects/effect.h b/effects/effect.h
index acf5e255b..b9571ea2f 100644
--- a/effects/effect.h
+++ b/effects/effect.h
@@ -41,7 +41,6 @@
#include
#include "ui/collapsiblewidget.h"
-#include "ui/checkboxex.h"
#include "effectrow.h"
#include "effectgizmo.h"
diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp
index 86886d3ad..e09723088 100644
--- a/effects/effectfield.cpp
+++ b/effects/effectfield.cpp
@@ -23,14 +23,6 @@
#include
#include
-#include "ui/labelslider.h"
-#include "ui/colorbutton.h"
-#include "ui/texteditex.h"
-#include "ui/checkboxex.h"
-#include "ui/comboboxex.h"
-#include "ui/fontcombobox.h"
-#include "ui/embeddedfilechooser.h"
-
#include "rendering/renderfunctions.h"
#include "global/config.h"
diff --git a/effects/fields/boolfield.h b/effects/fields/boolfield.h
index 5af612530..8a7ce1f6f 100644
--- a/effects/fields/boolfield.h
+++ b/effects/fields/boolfield.h
@@ -3,6 +3,11 @@
#include "../effectfield.h"
+/**
+ * @brief The BoolField class
+ *
+ * An EffectField derivative the uses boolean values (true or false) and uses a checkbox as its visual representation.
+ */
class BoolField : public EffectField
{
Q_OBJECT
diff --git a/olive.pro b/olive.pro
index 95509fe66..3fe5ca515 100644
--- a/olive.pro
+++ b/olive.pro
@@ -85,7 +85,6 @@ SOURCES += \
ui/colorbutton.cpp \
dialogs/replaceclipmediadialog.cpp \
ui/fontcombobox.cpp \
- ui/checkboxex.cpp \
ui/keyframeview.cpp \
ui/texteditex.cpp \
dialogs/demonotice.cpp \
@@ -210,7 +209,6 @@ HEADERS += \
ui/colorbutton.h \
dialogs/replaceclipmediadialog.h \
ui/fontcombobox.h \
- ui/checkboxex.h \
ui/keyframeview.h \
ui/texteditex.h \
dialogs/demonotice.h \
diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp
index 8d23fe99b..1fc51e93b 100644
--- a/rendering/cacher.cpp
+++ b/rendering/cacher.cpp
@@ -1146,8 +1146,6 @@ void Cacher::CloseWorker() {
}
}
- clip->reset();
-
qInfo() << "Clip closed on track" << clip->track();
}
diff --git a/rendering/exportthread.cpp b/rendering/exportthread.cpp
index 3c50776ed..6e389c607 100644
--- a/rendering/exportthread.cpp
+++ b/rendering/exportthread.cpp
@@ -44,35 +44,34 @@ extern "C" {
#include
#include
-ExportThread::ExportThread(const ExportParams &iparams,
- const VideoCodecParams& ivparams,
+ExportThread::ExportThread(const ExportParams ¶ms,
+ const VideoCodecParams& vparams,
QObject *parent) :
- QThread(parent)
+ QThread(parent),
+ params_(params),
+ vcodec_params_(vparams),
+ interrupt_(false),
+ fmt_ctx(nullptr),
+ video_stream(nullptr),
+ vcodec(nullptr),
+ vcodec_ctx(nullptr),
+ video_frame(nullptr),
+ sws_ctx(nullptr),
+ audio_stream(nullptr),
+ acodec(nullptr),
+ audio_frame(nullptr),
+ swr_frame(nullptr),
+ acodec_ctx(nullptr),
+ swr_ctx(nullptr),
+ vpkt_alloc(false),
+ apkt_alloc(false),
+ c_filename(nullptr)
{
- params = iparams;
- vcodec_params = ivparams;
- continueEncode = true;
-
+ // Create offscreen surface for rendering while exporting
surface.create();
-
- fmt_ctx = nullptr;
- video_stream = nullptr;
- vcodec = nullptr;
- vcodec_ctx = nullptr;
- video_frame = nullptr;
- sws_ctx = nullptr;
- audio_stream = nullptr;
- acodec = nullptr;
- audio_frame = nullptr;
- swr_frame = nullptr;
- acodec_ctx = nullptr;
- swr_ctx = nullptr;
-
- vpkt_alloc = false;
- apkt_alloc = false;
}
-bool ExportThread::encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream, bool rescale) {
+bool ExportThread::Encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream, bool rescale) {
ret = avcodec_send_frame(codec_ctx, frame);
if (ret < 0) {
qCritical() << "Failed to send frame to encoder." << ret;
@@ -100,15 +99,15 @@ bool ExportThread::encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx,
return true;
}
-bool ExportThread::setupVideo() {
+bool ExportThread::SetupVideo() {
// if video is disabled, no setup necessary
- if (!params.video_enabled) return true;
+ if (!params_.video_enabled) return true;
// find video encoder
- vcodec = avcodec_find_encoder(static_cast(params.video_codec));
+ vcodec = avcodec_find_encoder(static_cast(params_.video_codec));
if (!vcodec) {
qCritical() << "Could not find video encoder";
- export_error = tr("could not video encoder for %1").arg(QString::number(params.video_codec));
+ export_error = tr("could not video encoder for %1").arg(QString::number(params_.video_codec));
return false;
}
@@ -131,15 +130,15 @@ bool ExportThread::setupVideo() {
}
// setup context
- vcodec_ctx->codec_id = static_cast(params.video_codec);
+ vcodec_ctx->codec_id = static_cast(params_.video_codec);
vcodec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
- vcodec_ctx->width = params.video_width;
- vcodec_ctx->height = params.video_height;
+ vcodec_ctx->width = params_.video_width;
+ vcodec_ctx->height = params_.video_height;
vcodec_ctx->sample_aspect_ratio = {1, 1};
- vcodec_ctx->pix_fmt = static_cast(vcodec_params.pix_fmt);
- vcodec_ctx->framerate = av_d2q(params.video_frame_rate, INT_MAX);
- if (params.video_compression_type == COMPRESSION_TYPE_CBR) {
- vcodec_ctx->bit_rate = qRound(params.video_bitrate * 1000000);
+ vcodec_ctx->pix_fmt = static_cast(vcodec_params_.pix_fmt);
+ vcodec_ctx->framerate = av_d2q(params_.video_frame_rate, INT_MAX);
+ if (params_.video_compression_type == COMPRESSION_TYPE_CBR) {
+ vcodec_ctx->bit_rate = qRound(params_.video_bitrate * 1000000);
}
vcodec_ctx->time_base = av_inv_q(vcodec_ctx->framerate);
video_stream->time_base = vcodec_ctx->time_base;
@@ -148,27 +147,30 @@ bool ExportThread::setupVideo() {
vcodec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
+ // Some codecs require special settings so we set that up here
switch (vcodec_ctx->codec_id) {
/// H.264 specific settings
case AV_CODEC_ID_H264:
case AV_CODEC_ID_H265:
- switch (params.video_compression_type) {
+ switch (params_.video_compression_type) {
case COMPRESSION_TYPE_CFR:
- av_opt_set(vcodec_ctx->priv_data, "crf", QString::number(static_cast(params.video_bitrate)).toUtf8(), AV_OPT_SEARCH_CHILDREN);
+ av_opt_set(vcodec_ctx->priv_data, "crf", QString::number(static_cast(params_.video_bitrate)).toUtf8(), AV_OPT_SEARCH_CHILDREN);
break;
}
break;
}
+ // Set export to be multithreaded
AVDictionary* opts = nullptr;
- if (vcodec_params.threads == 0) {
+ if (vcodec_params_.threads == 0) {
av_dict_set(&opts, "threads", "auto", 0);
} else {
- av_dict_set(&opts, "threads", QString::number(vcodec_params.threads).toUtf8(), 0);
+ av_dict_set(&opts, "threads", QString::number(vcodec_params_.threads).toUtf8(), 0);
}
+ // Open video encoder
ret = avcodec_open2(vcodec_ctx, vcodec, &opts);
if (ret < 0) {
qCritical() << "Could not open output video encoder." << ret;
@@ -176,7 +178,7 @@ bool ExportThread::setupVideo() {
return false;
}
- // copy video encoder parameters to output stream
+ // Copy video encoder parameters to output stream
ret = avcodec_parameters_from_context(video_stream->codecpar, vcodec_ctx);
if (ret < 0) {
qCritical() << "Could not copy video encoder parameters to output stream." << ret;
@@ -184,7 +186,7 @@ bool ExportThread::setupVideo() {
return false;
}
- // create AVFrame
+ // Create raw AVFrame that will contain the RGBA buffer straight from compositing
video_frame = av_frame_alloc();
av_frame_make_writable(video_frame);
video_frame->format = AV_PIX_FMT_RGBA;
@@ -194,14 +196,15 @@ bool ExportThread::setupVideo() {
av_init_packet(&video_pkt);
+ // Set up conversion context
sws_ctx = sws_getContext(
olive::ActiveSequence->width,
olive::ActiveSequence->height,
AV_PIX_FMT_RGBA,
- params.video_width,
- params.video_height,
+ params_.video_width,
+ params_.video_height,
vcodec_ctx->pix_fmt,
- SWS_FAST_BILINEAR,
+ SWS_BILINEAR,
nullptr,
nullptr,
nullptr
@@ -210,29 +213,31 @@ bool ExportThread::setupVideo() {
return true;
}
-bool ExportThread::setupAudio() {
- // if audio is disabled, no setup necessary
- if (!params.audio_enabled) return true;
+bool ExportThread::SetupAudio() {
- // find encoder
- acodec = avcodec_find_encoder(static_cast(params.audio_codec));
+ // Find encoder for this codec
+ acodec = avcodec_find_encoder(static_cast(params_.audio_codec));
if (!acodec) {
qCritical() << "Could not find audio encoder";
- export_error = tr("could not audio encoder for %1").arg(QString::number(params.audio_codec));
+ export_error = tr("could not audio encoder for %1").arg(QString::number(params_.audio_codec));
return false;
}
- // allocate audio stream
+ // Allocate audio stream
audio_stream = avformat_new_stream(fmt_ctx, acodec);
- audio_stream->id = 1;
- if (!audio_stream) {
+ if (audio_stream == nullptr) {
qCritical() << "Could not allocate audio stream";
export_error = tr("could not allocate audio stream");
return false;
}
- // allocate context
- // acodec_ctx = audio_stream->codec;
+ // Set audio stream's ID to 1
+ audio_stream->id = 1;
+
+ // set sample rate to use for project
+ audio_rendering_rate = params_.audio_sampling_rate;
+
+ // Allocate encoding context
acodec_ctx = avcodec_alloc_context3(acodec);
if (!acodec_ctx) {
qCritical() << "Could not find allocate audio encoding context";
@@ -240,27 +245,24 @@ bool ExportThread::setupAudio() {
return false;
}
- // set sample rate to use for project
- audio_rendering_rate = params.audio_sampling_rate;
-
- // setup context
- acodec_ctx->codec_id = static_cast(params.audio_codec);
+ // Set up encoding context
+ acodec_ctx->codec_id = static_cast(params_.audio_codec);
acodec_ctx->codec_type = AVMEDIA_TYPE_AUDIO;
- acodec_ctx->sample_rate = params.audio_sampling_rate;
+ acodec_ctx->sample_rate = params_.audio_sampling_rate;
acodec_ctx->channel_layout = AV_CH_LAYOUT_STEREO; // change this to support surround/mono sound in the future (this is what the user sets the output audio to)
acodec_ctx->channels = av_get_channel_layout_nb_channels(acodec_ctx->channel_layout);
acodec_ctx->sample_fmt = acodec->sample_fmts[0];
- acodec_ctx->bit_rate = params.audio_bitrate * 1000;
+ acodec_ctx->bit_rate = params_.audio_bitrate * 1000;
acodec_ctx->time_base.num = 1;
- acodec_ctx->time_base.den = params.audio_sampling_rate;
+ acodec_ctx->time_base.den = params_.audio_sampling_rate;
audio_stream->time_base = acodec_ctx->time_base;
if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
acodec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
- // open encoder
+ // Open encoder
ret = avcodec_open2(acodec_ctx, acodec, nullptr);
if (ret < 0) {
qCritical() << "Could not open output audio encoder." << ret;
@@ -268,7 +270,7 @@ bool ExportThread::setupAudio() {
return false;
}
- // copy params to output stream
+ // Copy paramters from the codec context (set up above) to the output stream
ret = avcodec_parameters_from_context(audio_stream->codecpar, acodec_ctx);
if (ret < 0) {
qCritical() << "Could not copy audio encoder parameters to output stream." << ret;
@@ -300,8 +302,10 @@ bool ExportThread::setupAudio() {
audio_frame->nb_samples = 256;
}
+ // TODO change this to support surround/mono sound in the future (this is whatever format they're held in the internal buffer)
+ audio_frame->channel_layout = AV_CH_LAYOUT_STEREO;
+
audio_frame->format = AV_SAMPLE_FMT_S16;
- audio_frame->channel_layout = AV_CH_LAYOUT_STEREO; // change this to support surround/mono sound in the future (this is whatever format they're held in the internal buffer)
audio_frame->channels = av_get_channel_layout_nb_channels(audio_frame->channel_layout);
av_frame_make_writable(audio_frame);
ret = av_frame_get_buffer(audio_frame, 0);
@@ -328,18 +332,25 @@ bool ExportThread::setupAudio() {
return true;
}
-bool ExportThread::setupContainer() {
+bool ExportThread::SetupContainer() {
+
+ // Set up output context (using the filename as the format specification)
+
avformat_alloc_output_context2(&fmt_ctx, nullptr, nullptr, c_filename);
- if (!fmt_ctx) {
+ if (fmt_ctx == nullptr) {
+
+ // Failed to create the output format context. Exit the export and throw an error.
+
qCritical() << "Could not create output context";
export_error = tr("could not create output format context");
return false;
}
- //av_dump_format(fmt_ctx, 0, c_filename, 1);
-
ret = avio_open(&fmt_ctx->pb, c_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
+
+ // Failed to get a valid write handle for the exported file. Exit the export and throw an error.
+
qCritical() << "Could not open output file." << ret;
export_error = tr("could not open output file (%1)").arg(QString::number(ret));
return false;
@@ -348,60 +359,98 @@ bool ExportThread::setupContainer() {
return true;
}
-void ExportThread::run() {
- panel_sequence_viewer->pause();
- panel_sequence_viewer->seek(params.start_frame);
-
- // copy filename
- QByteArray ba = params.filename.toUtf8();
+void ExportThread::Export()
+{
+ // Copy filename from QString to const char
+ QByteArray ba = params_.filename.toUtf8();
c_filename = new char[ba.size()+1];
strcpy(c_filename, ba.data());
- continueEncode = setupContainer();
-
- if (params.video_enabled && continueEncode) continueEncode = setupVideo();
-
- if (params.audio_enabled && continueEncode) continueEncode = setupAudio();
-
- if (continueEncode) {
- ret = avformat_write_header(fmt_ctx, nullptr);
- if (ret < 0) {
- qCritical() << "Could not write output file header." << ret;
- export_error = tr("could not write output file header (%1)").arg(QString::number(ret));
- continueEncode = false;
- }
+ // Set up file container
+ if (!SetupContainer()) {
+ return;
}
+ // If video is enabled, set it up in the container now
+ if (params_.video_enabled && !SetupVideo()) {
+ return;
+ }
+
+ // If audio is enabled, set it up in the container now
+ if (params_.audio_enabled && !SetupAudio()) {
+ return;
+ }
+
+ // Write the container header based on what's been set up above
+ ret = avformat_write_header(fmt_ctx, nullptr);
+ if (ret < 0) {
+
+ // FFmpeg failed to write the header, so cancel the export and throw an error
+
+ qCritical() << "Could not write output file header." << ret;
+ export_error = tr("could not write output file header (%1)").arg(QString::number(ret));
+
+ return;
+ }
+
+ // Count audio samples in file (used for calculating PTS)
long file_audio_samples = 0;
- qint64 start_time, frame_time, avg_time, eta, total_time = 0;
+
+ // Set up timing variables, used for determining rendering ETA
+ qint64 frame_start_time, frame_time, avg_time, eta, total_time = 0;
+
+ // Frame counters - used for generating encoding statistics (e.g. average frame time, ETA, etc.)
long remaining_frames, frame_count = 1;
+ // Use Sequence Viewer's render thread - TODO separate this into a new render thread for background rendering
RenderThread* renderer = panel_sequence_viewer->viewer_widget->get_renderer();
+
+ // Override connection from RenderThread
disconnect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget, SLOT(queue_repaint()));
connect(renderer, SIGNAL(ready()), this, SLOT(wake()));
+ // Lock mutex (used for synchronization with RenderThread)
mutex.lock();
- while (olive::ActiveSequence->playhead <= params.end_frame && continueEncode) {
- start_time = QDateTime::currentMSecsSinceEpoch();
+ // Loop from now (set to the beginning frame earlier) to the end of the frame
+ while (olive::ActiveSequence->playhead <= params_.end_frame && !interrupt_) {
- if (params.audio_enabled) {
- compose_audio(nullptr, olive::ActiveSequence.get(), 1, true);
+ // Start timing how long this frame will take
+ frame_start_time = QDateTime::currentMSecsSinceEpoch();
+
+ // If we're exporting audio, run compose_audio() which will write mixed audio to the internal audio buffer
+ if (params_.audio_enabled) {
+ olive::rendering::compose_audio(nullptr, olive::ActiveSequence.get(), 1, true);
}
- if (params.video_enabled) {
+
+ // If we're exporting video, trigger a render on the RenderThread
+ if (params_.video_enabled) {
do {
// TODO optimize by rendering the next frame while encoding the last
renderer->start_render(nullptr, olive::ActiveSequence.get(), 1, nullptr, video_frame->data[0], video_frame->linesize[0]/4);
+
+ // Wait for RenderThread to return
waitCond.wait(&mutex);
- if (!continueEncode) break;
+
+ if (interrupt_) {
+ return;
+ }
+
+ // If the RenderThread failed, do another render
} while (renderer->did_texture_fail());
- if (!continueEncode) break;
+
+ if (interrupt_) {
+ return;
+ }
+
}
- // encode last frame while rendering next frame
- double timecode_secs = double(olive::ActiveSequence->playhead - params.start_frame) / olive::ActiveSequence->frame_rate;
- if (params.video_enabled) {
- // create sws_frame for converting pixel format
+ // Get the current sequence playhead in seconds (used for timestamp calculations later on)
+ double timecode_secs = double(olive::ActiveSequence->playhead - params_.start_frame) / olive::ActiveSequence->frame_rate;
+
+ // If we're exporting video, construct an AVFrame in the destination codec's pixel format to convert the raw RGBA
+ // OpenGL buffer to
+ if (params_.video_enabled) {
//
// - I'm not sure why, but we have to alloc/free sws_frame every frame, or it breaks GIF exporting.
@@ -411,146 +460,222 @@ void ExportThread::run() {
// - Anyway, here we are.
//
+ // Construct destination pixel format frame
sws_frame = av_frame_alloc();
sws_frame->format = vcodec_ctx->pix_fmt;
- sws_frame->width = params.video_width;
- sws_frame->height = params.video_height;
+ sws_frame->width = params_.video_width;
+ sws_frame->height = params_.video_height;
av_frame_get_buffer(sws_frame, 0);
- // convert pixel format to format expected by the encoder
+ // Convert raw RGBA buffer to format expected by the encoder
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));
- // send converted frame to encoder
- if (!encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream, false)) continueEncode = false;
+ // Send frame to encoder
+ if (!Encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream, false)) {
+ return;
+ }
av_frame_free(&sws_frame);
+ sws_frame = nullptr;
}
- if (params.audio_enabled) {
- // do we need to encode more audio samples?
- while (continueEncode && file_audio_samples <= (timecode_secs*params.audio_sampling_rate)) {
+ // If we're exporting audio, copy audio from the buffer into an AVFrame for encoding
+ if (params_.audio_enabled) {
- // copy samples from audio buffer to AVFrame
+ // Check if the count of encoded samples exceeds the current Sequence playhead, in which case we don't need to
+ // encode any audio at this moment
+ while (!interrupt_ && file_audio_samples <= (timecode_secs*params_.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);
memset(audio_ibuffer+adjusted_read, 0, copylen);
audio_ibuffer_read += copylen;
+ // If we reached the end of the buffer without reaching the end of the frame, do another copy from the start
+ // of the buffer
if (copylen < aframe_bytes) {
- // copy remainder
int remainder_len = aframe_bytes-copylen;
memcpy(audio_frame->data[0]+copylen, audio_ibuffer, remainder_len);
memset(audio_ibuffer, 0, remainder_len);
audio_ibuffer_read += remainder_len;
}
- // convert to export sample format
+ // Convert raw audio samples to the destination codec's sample format
swr_convert_frame(swr_ctx, swr_frame, audio_frame);
+ // The timestamp is set to the current count of audio samples (since the audio stream's timebase is
swr_frame->pts = file_audio_samples;
- // send to encoder
- if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream, true)) continueEncode = false;
+ // Send frame to encoder
+ if (!Encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream, true)) {
+ return;
+ }
+ // Increment by the frame's number of samples
file_audio_samples += swr_frame->nb_samples;
}
}
- // generating encoding statistics (time it took to encode this frame/estimated remaining time)
- frame_time = (QDateTime::currentMSecsSinceEpoch()-start_time);
+ // Generating encoding statistics (e.g. the time it took to encode this frame/estimated remaining time)
+ frame_time = (QDateTime::currentMSecsSinceEpoch()-frame_start_time);
total_time += frame_time;
- remaining_frames = (params.end_frame - olive::ActiveSequence->playhead);
+ remaining_frames = (params_.end_frame - olive::ActiveSequence->playhead);
avg_time = (total_time/frame_count);
eta = (remaining_frames*avg_time);
- emit progress_changed(qRound((double(olive::ActiveSequence->playhead - params.start_frame) / double(params.end_frame - params.start_frame)) * 100.0), eta);
+ // Emit a signal for the percent of the sequence that's been encoded so far
+ emit ProgressChanged(qRound((double(olive::ActiveSequence->playhead - params_.start_frame) / double(params_.end_frame - params_.start_frame)) * 100.0), eta);
+
+ // Increment sequence playhead
olive::ActiveSequence->playhead++;
+
+ // Increment frame count (used for generating encoding statistics above)
frame_count++;
}
+ // Restore original connection from RenderThread
disconnect(renderer, SIGNAL(ready()), this, SLOT(wake()));
connect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget, SLOT(queue_repaint()));
mutex.unlock();
- if (continueEncode) {
- if (params.video_enabled) vpkt_alloc = true;
- if (params.audio_enabled) apkt_alloc = true;
+ if (interrupt_) {
+ return;
}
+ if (params_.video_enabled) vpkt_alloc = true;
+ if (params_.audio_enabled) apkt_alloc = true;
+
olive::Global->set_rendering_state(false);
- if (params.audio_enabled && continueEncode) {
+ // If audio is enabled, flush the rest of the audio out of swresample
+ if (params_.audio_enabled) {
- // flush swresample
do {
swr_convert_frame(swr_ctx, swr_frame, nullptr);
if (swr_frame->nb_samples == 0) break;
swr_frame->pts = file_audio_samples;
- if (!encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream, true)) continueEncode = false;
+ if (!Encode(fmt_ctx, acodec_ctx, swr_frame, &audio_pkt, audio_stream, true)) {
+ return;
+ }
file_audio_samples += swr_frame->nb_samples;
} while (swr_frame->nb_samples > 0);
}
- bool continueVideo = true;
- bool continueAudio = true;
- if (continueEncode) {
- // flush remaining packets
- while (continueVideo && continueAudio) {
- if (continueVideo && params.video_enabled) continueVideo = encode(fmt_ctx, vcodec_ctx, nullptr, &video_pkt, video_stream, false);
- if (continueAudio && params.audio_enabled) continueAudio = encode(fmt_ctx, acodec_ctx, nullptr, &audio_pkt, audio_stream, true);
- }
+ if (interrupt_) {
+ return;
+ }
- ret = av_write_trailer(fmt_ctx);
- if (ret < 0) {
- qCritical() << "Could not write output file trailer." << ret;
- export_error = tr("could not write output file trailer (%1)").arg(QString::number(ret));
- continueEncode = false;
- }
+ bool continueVideo = params_.video_enabled;
+ bool continueAudio = params_.audio_enabled;
- if (continueEncode) {
- emit progress_changed(100, 0);
+ // Flush remaining packets out of video and audio encoders
+ while (continueVideo && continueAudio) {
+ if (continueVideo) {
+ continueVideo = Encode(fmt_ctx, vcodec_ctx, nullptr, &video_pkt, video_stream, false);
+ }
+ if (continueAudio) {
+ continueAudio = Encode(fmt_ctx, acodec_ctx, nullptr, &audio_pkt, audio_stream, true);
}
}
- avio_closep(&fmt_ctx->pb);
+ // Write container trailer
+ ret = av_write_trailer(fmt_ctx);
+ if (ret < 0) {
+ qCritical() << "Could not write output file trailer." << ret;
+ export_error = tr("could not write output file trailer (%1)").arg(QString::number(ret));
+ return;
+ }
- if (vpkt_alloc) av_packet_unref(&video_pkt);
- if (video_frame != nullptr) av_frame_free(&video_frame);
- if (vcodec_ctx != nullptr) {
- avcodec_close(vcodec_ctx);
- avcodec_free_context(&vcodec_ctx);
+ emit ProgressChanged(100, 0);
+}
+
+void ExportThread::Cleanup()
+{
+ if (fmt_ctx != nullptr) {
+ avio_closep(&fmt_ctx->pb);
+ avformat_free_context(fmt_ctx);
}
- if (apkt_alloc) av_packet_unref(&audio_pkt);
- if (audio_frame != nullptr) av_frame_free(&audio_frame);
if (acodec_ctx != nullptr) {
avcodec_close(acodec_ctx);
avcodec_free_context(&acodec_ctx);
}
- avformat_free_context(fmt_ctx);
+ if (audio_frame != nullptr) {
+ av_frame_free(&audio_frame);
+ }
+
+ if (apkt_alloc) {
+ av_packet_unref(&audio_pkt);
+ }
+
+ if (vcodec_ctx != nullptr) {
+ avcodec_close(vcodec_ctx);
+ avcodec_free_context(&vcodec_ctx);
+ }
+
+ if (video_frame != nullptr) {
+ av_frame_free(&video_frame);
+ }
+
+ if (vpkt_alloc) {
+ av_packet_unref(&video_pkt);
+ }
if (sws_ctx != nullptr) {
sws_freeContext(sws_ctx);
}
+
if (swr_ctx != nullptr) {
- av_frame_free(&swr_frame);
swr_free(&swr_ctx);
}
+ if (swr_frame != nullptr) {
+ av_frame_free(&swr_frame);
+ }
+
+ if (sws_frame != nullptr) {
+ av_frame_free(&sws_frame);
+ }
+
delete [] c_filename;
}
-const QString &ExportThread::getError() {
+void ExportThread::run() {
+ // Ensure sequence isn't currently playing
+ panel_sequence_viewer->pause();
+
+ // Seek to the first frame we're exporting
+ panel_sequence_viewer->seek(params_.start_frame);
+
+ // Run export function (which will return if there's a failure)
+ Export();
+
+ // Clean up anything that was allocated in Export() (whether it succeeded or not)
+ Cleanup();
+}
+
+const QString &ExportThread::GetError() {
return export_error;
}
+bool ExportThread::WasInterrupted()
+{
+ return interrupt_;
+}
+
+void ExportThread::Interrupt()
+{
+ interrupt_ = true;
+}
+
void ExportThread::wake() {
mutex.lock();
waitCond.wakeAll();
diff --git a/rendering/exportthread.h b/rendering/exportthread.h
index ab30f6da8..e4bc03080 100644
--- a/rendering/exportthread.h
+++ b/rendering/exportthread.h
@@ -72,27 +72,30 @@ struct VideoCodecParams {
class ExportThread : public QThread {
Q_OBJECT
public:
- ExportThread(const ExportParams& iparams, const VideoCodecParams& ivparams, QObject* parent = nullptr);
- void run();
+ ExportThread(const ExportParams& params, const VideoCodecParams& vparams, QObject* parent = nullptr);
+ virtual void run() override;
- const QString& getError();
+ const QString& GetError();
+
+ bool WasInterrupted();
+signals:
+ void ProgressChanged(int value, qint64 remaining_ms);
+public slots:
+ void Interrupt();
+private:
+ bool Encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream, bool rescale);
+ bool SetupVideo();
+ bool SetupAudio();
+ bool SetupContainer();
+ void Export();
+ void Cleanup();
QOffscreenSurface surface;
-
- 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();
+ bool interrupt_;
// params imported from dialogs
- ExportParams params;
- VideoCodecParams vcodec_params;
+ ExportParams params_;
+ VideoCodecParams vcodec_params_;
AVFormatContext* fmt_ctx;
AVStream* video_stream;
@@ -121,6 +124,8 @@ private:
QWaitCondition waitCond;
QString export_error;
+private slots:
+ void wake();
};
#endif // EXPORTTHREAD_H
diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h
index abea07c33..c370b0493 100644
--- a/rendering/renderfunctions.h
+++ b/rendering/renderfunctions.h
@@ -215,6 +215,8 @@ struct ComposeSequenceParams {
GLuint ocio_lut_texture;
};
+namespace olive {
+namespace rendering {
/**
* @brief Compose a frame of a given sequence
*
@@ -263,6 +265,8 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms);
* Whether to wait for media to open or simply fail if the media is not yet open. This should usually be **FALSE**.
*/
void compose_audio(Viewer* viewer, Sequence *seq, int playback_speed, bool wait_for_mutexes);
+}
+}
/**
* @brief Rescale a frame number between two frame rates
diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp
index 6d92ea422..1fb88308c 100644
--- a/rendering/renderthread.cpp
+++ b/rendering/renderthread.cpp
@@ -178,7 +178,7 @@ void RenderThread::paint() {
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
- compose_sequence(params);
+ olive::rendering::compose_sequence(params);
// flush changes
ctx->functions()->glFinish();
diff --git a/timeline/clip.cpp b/timeline/clip.cpp
index 5b4311de6..743587f79 100644
--- a/timeline/clip.cpp
+++ b/timeline/clip.cpp
@@ -40,26 +40,23 @@ const int kRGBAComponentCount = 4;
Clip::Clip(Sequence* s) :
sequence(s),
- cacher(this)
+ cacher(this),
+ enabled_(true),
+ clip_in_(0),
+ timeline_in_(0),
+ timeline_out_(0),
+ track_(0),
+ media_(nullptr),
+ reverse_(false),
+ autoscale_(olive::CurrentConfig.autoscale_by_default),
+ opening_transition(nullptr),
+ closing_transition(nullptr),
+ undeletable(false),
+ replaced(false),
+ fbo(nullptr),
+ open_(false),
+ texture(nullptr)
{
- enabled_ = true;
- clip_in_ = 0;
- timeline_in_ = 0;
- timeline_out_ = 0;
- track_ = 0;
- media_ = nullptr;
- speed_.value = 1.0;
- speed_.maintain_audio_pitch = false;
- reverse_ = false;
- autoscale_ = olive::CurrentConfig.autoscale_by_default;
- opening_transition = nullptr;
- closing_transition = nullptr;
- undeletable = false;
- replaced = false;
- fbo = nullptr;
- open_ = false;
-
- reset();
}
ClipPtr Clip::copy(Sequence* s) {
@@ -197,10 +194,6 @@ void Clip::move(ComboAction* ca, long iin, long iout, long iclip_in, int itrack,
}
}
-void Clip::reset() {
- texture = nullptr;
-}
-
void Clip::reset_audio() {
if (UsesCacher()) {
cacher.ResetAudio();
@@ -619,7 +612,6 @@ bool Clip::Retrieve()
const_cast(using_db_1 ? data_buffer_1 : data_buffer_2));
if (data_buffer_1 != frame->data[0]) {
- qDebug() << data_buffer_1 << frame->data[0];
delete [] data_buffer_1;
delete [] data_buffer_2;
}
@@ -641,3 +633,9 @@ bool Clip::UsesCacher()
{
return track() >= 0 || (media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE);
}
+
+ClipSpeed::ClipSpeed() :
+ value(1.0),
+ maintain_audio_pitch(false)
+{
+}
diff --git a/timeline/clip.h b/timeline/clip.h
index 8e6e94914..3f29708f1 100644
--- a/timeline/clip.h
+++ b/timeline/clip.h
@@ -44,6 +44,7 @@ extern "C" {
}
struct ClipSpeed {
+ ClipSpeed();
double value;
bool maintain_audio_pitch;
};
@@ -115,7 +116,6 @@ public:
AVRational time_base();
void reset_audio();
- void reset();
void refresh();
long length();
diff --git a/ui/checkboxex.cpp b/ui/checkboxex.cpp
deleted file mode 100644
index 0682e8b52..000000000
--- a/ui/checkboxex.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2019 Olive Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-***/
-
-#include "checkboxex.h"
-
-#include "undo/undostack.h"
-#include "undo/undo.h"
-
-CheckboxEx::CheckboxEx(QWidget* parent) : QCheckBox(parent) {
-// connect(this, SIGNAL(clicked(bool)), this, SLOT(checkbox_command()));
-}
-
-void CheckboxEx::checkbox_command() {
- CheckboxCommand* c = new CheckboxCommand(this);
- olive::UndoStack.push(c);
-}
diff --git a/ui/checkboxex.h b/ui/checkboxex.h
deleted file mode 100644
index eb3db0f01..000000000
--- a/ui/checkboxex.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2019 Olive Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-***/
-
-#ifndef CHECKBOXEX_H
-#define CHECKBOXEX_H
-
-#include
-
-class CheckboxEx : public QCheckBox
-{
- Q_OBJECT
-public:
- CheckboxEx(QWidget* parent = 0);
-private slots:
- void checkbox_command();
-};
-
-#endif // CHECKBOXEX_H
diff --git a/ui/collapsiblewidget.cpp b/ui/collapsiblewidget.cpp
index e81c3dbc8..21b719018 100644
--- a/ui/collapsiblewidget.cpp
+++ b/ui/collapsiblewidget.cpp
@@ -30,9 +30,7 @@
#include
#include
-#include "ui/checkboxex.h"
#include "ui/icons.h"
-
#include "global/debug.h"
CollapsibleWidget::CollapsibleWidget(QWidget* parent) : QWidget(parent) {
@@ -47,7 +45,7 @@ CollapsibleWidget::CollapsibleWidget(QWidget* parent) : QWidget(parent) {
title_bar->setAutoFillBackground(true);
title_bar_layout = new QHBoxLayout(title_bar);
title_bar_layout->setMargin(5);
- enabled_check = new CheckboxEx(title_bar);
+ enabled_check = new QCheckBox(title_bar);
enabled_check->setChecked(true);
header = new QLabel(title_bar);
collapse_button = new QPushButton(title_bar);
diff --git a/ui/collapsiblewidget.h b/ui/collapsiblewidget.h
index 8aa67ebb4..3cc25c522 100644
--- a/ui/collapsiblewidget.h
+++ b/ui/collapsiblewidget.h
@@ -30,8 +30,6 @@
#include
#include
-#include "ui/checkboxex.h"
-
class CollapsibleWidgetHeader : public QWidget {
Q_OBJECT
public:
@@ -56,7 +54,7 @@ public:
bool IsExpanded();
bool IsSelected();
protected:
- CheckboxEx* enabled_check;
+ QCheckBox* enabled_check;
CollapsibleWidgetHeader* title_bar;
QWidget* contents;
private:
diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp
index 523b56c24..ee2de87b3 100644
--- a/ui/viewerwidget.cpp
+++ b/ui/viewerwidget.cpp
@@ -233,7 +233,7 @@ void ViewerWidget::frame_update() {
}
// render the audio
- compose_audio(viewer, viewer->seq.get(), viewer->get_playback_speed(), viewer->WaitingForPlayWake());
+ olive::rendering::compose_audio(viewer, viewer->seq.get(), viewer->get_playback_speed(), viewer->WaitingForPlayWake());
}
}
diff --git a/undo/undostack.h b/undo/undostack.h
index 72086a823..9d42c4583 100644
--- a/undo/undostack.h
+++ b/undo/undostack.h
@@ -4,6 +4,9 @@
#include
namespace olive {
+/**
+ * @brief Global undo stack object
+ */
extern QUndoStack UndoStack;
}