app: migrate all engine access to the C ABI facade (nm U _ZN5olive = 0)

Every app module now reaches liboakengine exclusively through
oakengine_* C calls, EngineEventBridge subscriptions and app-side
handle headers (cliphandle/keyframehandle/nodevaluehandle/oakvaluehelper).
Direct C++ command construction, engine signal connect()s, and engine
type usage in MOC-visible signatures are gone: 557 -> 0 undefined
olive:: symbols in oak-editor.
This commit is contained in:
2026-07-26 22:43:21 +08:00
parent f95590e924
commit 0aa5879f35
256 changed files with 12357 additions and 4044 deletions
+1 -1
View File
@@ -26,7 +26,7 @@
#include <QLabel>
#include <QVBoxLayout>
#include "config/config.h"
#include "common/configwrapper.h"
#include "patreon.h"
#include "scrollinglabel.h"
+56 -16
View File
@@ -30,7 +30,7 @@
namespace olive
{
ColorDialog::ColorDialog(ColorManager *color_manager, const ManagedColor &start,
ColorDialog::ColorDialog(OakEngineColorManager *color_manager, const ManagedColor &start,
QWidget *parent)
: QDialog(parent)
, color_manager_(color_manager)
@@ -142,11 +142,23 @@ void ColorDialog::set_color(const ManagedColor &start)
} else {
// Convert reference color to the input space
ColorProcessorPtr linear_to_input = ColorProcessor::create(
color_manager_, color_manager_->get_reference_color_space(),
start.color_input());
QByteArray ref_cs = oak_query_string([this](char *buf, int size) {
return oakengine_color_manager_reference_color_space(
color_manager_, buf, size);
}).toUtf8();
QByteArray in_cs = start.color_input().toUtf8();
oak_color_transform in_pod;
in_pod.is_display = 0;
in_pod.output = in_cs.constData();
in_pod.view = nullptr;
in_pod.look = nullptr;
ColorProcessorHandlePtr linear_to_input(
oakengine_color_processor_create(color_manager_, ref_cs.constData(),
&in_pod,
OAKENGINE_COLOR_PROCESSOR_NORMAL),
ColorProcessorHandleDeleter());
managed_start = linear_to_input->convert_color(start);
managed_start = oak_convert_color(linear_to_input, start);
}
color_wheel_->set_selected_color(managed_start);
@@ -161,7 +173,7 @@ ManagedColor ColorDialog::get_selected_color() const
// Convert to linear and return a linear color
if (input_to_ref_processor_) {
selected = input_to_ref_processor_->convert_color(selected);
selected = oak_convert_color(input_to_ref_processor_, selected);
}
selected.set_color_input(get_color_space_input());
@@ -183,22 +195,50 @@ ColorTransform ColorDialog::get_color_space_output() const
void ColorDialog::color_space_changed(const QString &input,
const ColorTransform &output)
{
input_to_ref_processor_ = ColorProcessor::create(
color_manager_, input, color_manager_->get_reference_color_space());
QByteArray ref_cs = oak_query_string([this](char *buf, int size) {
return oakengine_color_manager_reference_color_space(
color_manager_, buf, size);
}).toUtf8();
QByteArray in = input.toUtf8();
QByteArray o, v, l;
oak_color_transform out_pod = oak_to_transform(output, &o, &v, &l);
ColorProcessorPtr ref_to_display = ColorProcessor::create(
color_manager_, color_manager_->get_reference_color_space(), output);
auto make_proc = [&](const char *input_cs, const oak_color_transform *dest,
int dir) -> ColorProcessorHandlePtr {
return ColorProcessorHandlePtr(
oakengine_color_processor_create(color_manager_, input_cs, dest,
dir),
ColorProcessorHandleDeleter());
};
ColorProcessorPtr ref_to_input = ColorProcessor::create(
color_manager_, color_manager_->get_reference_color_space(), input);
input_to_ref_processor_ = make_proc(in.constData(), &out_pod,
OAKENGINE_COLOR_PROCESSOR_NORMAL);
oak_color_transform ref_display_pod;
ref_display_pod.is_display = out_pod.is_display;
ref_display_pod.output = out_pod.output;
ref_display_pod.view = out_pod.view;
ref_display_pod.look = out_pod.look;
ColorProcessorHandlePtr ref_to_display = make_proc(
ref_cs.constData(), &ref_display_pod,
OAKENGINE_COLOR_PROCESSOR_NORMAL);
oak_color_transform ref_input_pod;
ref_input_pod.is_display = 0;
ref_input_pod.output = in.constData();
ref_input_pod.view = nullptr;
ref_input_pod.look = nullptr;
ColorProcessorHandlePtr ref_to_input = make_proc(
ref_cs.constData(), &ref_input_pod,
OAKENGINE_COLOR_PROCESSOR_NORMAL);
// Display -> reference is the inverse of the display transform. Older OCIO
// versions crashed on TRANSFORM_DIR_INVERSE; guard by requiring a valid
// processor and fall back to disabling the display tab if creation fails.
ColorProcessorPtr display_to_ref = ColorProcessor::create(
color_manager_, color_manager_->get_reference_color_space(), output,
ColorProcessor::k_inverse);
if (display_to_ref && !display_to_ref->get_processor()) {
ColorProcessorHandlePtr display_to_ref = make_proc(
ref_cs.constData(), &ref_display_pod,
OAKENGINE_COLOR_PROCESSOR_INVERSE);
if (display_to_ref && !oakengine_color_processor_is_valid(display_to_ref.get())) {
display_to_ref = nullptr;
}
+5 -5
View File
@@ -24,8 +24,8 @@
#include <QDialog>
#include "node/color/colormanager/colormanager.h"
#include "render/managedcolor.h"
#include "oakengine/color.h"
#include "widget/manageddisplay/colorprocessorhandle.h"
#include "widget/colorwheel/colorgradientwidget.h"
#include "widget/colorwheel/colorspacechooser.h"
#include "widget/colorwheel/colorswatchchooser.h"
@@ -57,7 +57,7 @@ public:
*
* QWidget parent.
*/
ColorDialog(ColorManager *color_manager,
ColorDialog(OakEngineColorManager *color_manager,
const ManagedColor &start = Color(1.0f, 1.0f, 1.0f),
QWidget *parent = nullptr);
@@ -76,7 +76,7 @@ public slots:
void set_color(const ManagedColor &c);
private:
ColorManager *color_manager_;
OakEngineColorManager *color_manager_;
ColorWheelWidget *color_wheel_;
@@ -84,7 +84,7 @@ private:
ColorGradientWidget *hsv_value_gradient_;
ColorProcessorPtr input_to_ref_processor_;
ColorProcessorHandlePtr input_to_ref_processor_;
ColorSpaceChooser *chooser_;
+3 -2
View File
@@ -27,6 +27,7 @@
#include "core.h"
#include "oakengine/undo.h"
namespace olive
{
@@ -70,13 +71,13 @@ void ConfigDialogBase::accept()
}
}
MultiUndoCommand *command = new MultiUndoCommand();
void *command = oakengine_undo_command_create_multi();
foreach (ConfigDialogBaseTab *tab, tabs_) {
tab->accept(command);
}
Core::instance()->undo_stack()->push(command, tr("Set Configuration"));
oakengine_undo_push(command, tr("Set Configuration").toUtf8().constData());
AcceptEvent();
+2 -3
View File
@@ -24,8 +24,7 @@
#include <QWidget>
#include "config/config.h"
#include "undo/undocommand.h"
#include "common/configwrapper.h"
namespace olive
{
@@ -36,7 +35,7 @@ public:
virtual bool validate();
virtual void accept(MultiUndoCommand *parent) = 0;
virtual void accept(void *parent) = 0;
};
}
+3 -1
View File
@@ -26,6 +26,8 @@
#include <QLabel>
#include <QMessageBox>
#include "oakengine/disk.h"
namespace olive
{
@@ -109,7 +111,7 @@ void DiskCacheDialog::clear_disk_cache(const QString &path, QWidget *parent,
if (clear_btn)
clear_btn->setEnabled(false);
if (DiskManager::instance()->clear_disk_cache(path)) {
if (oakengine_disk_clear_cache(path.toUtf8().constData())) {
if (clear_btn)
clear_btn->setText(tr("Disk Cache Cleared"));
} else {
+7 -5
View File
@@ -89,19 +89,21 @@ AV1Section::AV1Section(int default_crf, QWidget *parent)
compression_method_stack_, &QStackedWidget::setCurrentIndex);
}
void AV1Section::add_opts(EncodingParams *params)
void AV1Section::add_opts(OakEngineEncodingParams *params)
{
CompressionMethod method = static_cast<CompressionMethod>(
compression_method_stack_->currentIndex());
if (method == k_constant_rate_factor) {
// Set Quantizer value
params->set_video_option(QStringLiteral("qp"),
QString::number(crf_section_->get_value()));
oakengine_encoding_params_set_video_option(
params, "qp",
QByteArray::number(crf_section_->get_value()).constData());
}
params->set_video_option(QStringLiteral("preset"),
QString::number(preset_combobox_->currentIndex()));
oakengine_encoding_params_set_video_option(
params, "preset",
QByteArray::number(preset_combobox_->currentIndex()).constData());
}
AV1CRFSection::AV1CRFSection(int default_crf, QWidget *parent)
+1 -1
View File
@@ -58,7 +58,7 @@ public:
AV1Section(QWidget *parent = nullptr);
AV1Section(int default_crf, QWidget *parent);
virtual void add_opts(EncodingParams *params) override;
virtual void add_opts(OakEngineEncodingParams *params) override;
private:
QStackedWidget *compression_method_stack_;
+11 -7
View File
@@ -79,17 +79,21 @@ CineformSection::CineformSection(QWidget *parent)
layout->addWidget(quality_combobox_, row, 1);
}
void CineformSection::add_opts(EncodingParams *params)
void CineformSection::add_opts(OakEngineEncodingParams *params)
{
params->set_video_option(
QStringLiteral("quality"),
QString::number(quality_combobox_->currentIndex()));
oakengine_encoding_params_set_video_option(
params, "quality",
QByteArray::number(quality_combobox_->currentIndex()).constData());
}
void CineformSection::set_opts(const EncodingParams *p)
void CineformSection::set_opts(const OakEngineEncodingParams *p)
{
quality_combobox_->setCurrentIndex(
p->video_option(QStringLiteral("quality")).toInt());
char buf[64];
const int ret = oakengine_encoding_params_video_option(
p, "quality", buf, static_cast<int>(sizeof(buf)));
if (ret > 0) {
quality_combobox_->setCurrentIndex(QString::fromUtf8(buf).toInt());
}
}
}
+2 -2
View File
@@ -34,9 +34,9 @@ class CineformSection : public CodecSection {
public:
CineformSection(QWidget *parent = nullptr);
virtual void add_opts(EncodingParams *params) override;
virtual void add_opts(OakEngineEncodingParams *params) override;
virtual void set_opts(const EncodingParams *p) override;
virtual void set_opts(const OakEngineEncodingParams *p) override;
private:
QComboBox *quality_combobox_;
+3 -3
View File
@@ -24,7 +24,7 @@
#include <QWidget>
#include "codec/encoder.h"
#include "oakengine/encoding.h"
namespace olive
{
@@ -34,12 +34,12 @@ class CodecSection : public QWidget {
public:
CodecSection(QWidget *parent = nullptr);
virtual void add_opts(EncodingParams *params)
virtual void add_opts(OakEngineEncodingParams *params)
{
Q_UNUSED(params)
}
virtual void set_opts(const EncodingParams *p)
virtual void set_opts(const OakEngineEncodingParams *p)
{
Q_UNUSED(p)
}
+48 -25
View File
@@ -101,7 +101,7 @@ H264Section::H264Section(int default_crf, QWidget *parent)
compression_method_stack_, &QStackedWidget::setCurrentIndex);
}
void H264Section::add_opts(EncodingParams *params)
void H264Section::add_opts(OakEngineEncodingParams *params)
{
// FIXME: Implement two-pass
@@ -110,13 +110,15 @@ void H264Section::add_opts(EncodingParams *params)
// This option is not used by the encoder (nor is anything with the ove_ prefix), it's to help us
// identify which option was chosen when params are restored
params->set_video_option(QStringLiteral("ove_compressionmethod"),
QString::number(method));
oakengine_encoding_params_set_video_option(
params, "ove_compressionmethod",
QByteArray::number(method).constData());
if (method == k_constant_rate_factor) {
// Simply set CRF value
params->set_video_option(QStringLiteral("crf"),
QString::number(crf_section_->get_value()));
oakengine_encoding_params_set_video_option(
params, "crf",
QByteArray::number(crf_section_->get_value()).constData());
} else {
int64_t target_rate, max_rate, min_rate;
@@ -129,40 +131,58 @@ void H264Section::add_opts(EncodingParams *params)
} else {
// Calculate the bit rate from the file size divided by the sequence length in seconds (bits per second)
int64_t target_fs = filesize_section_->get_file_size();
target_rate = qRound64(static_cast<double>(target_fs) /
params->get_export_length().to_double());
int export_len_num = 0, export_len_den = 1;
oakengine_encoding_params_get_export_length(
params, &export_len_num, &export_len_den);
const double export_len_sec =
(export_len_den > 0)
? static_cast<double>(export_len_num)
/ static_cast<double>(export_len_den)
: 1.0;
target_rate = qRound64(static_cast<double>(target_fs) / export_len_sec);
min_rate = target_rate;
max_rate = target_rate;
params->set_video_option(QStringLiteral("ove_targetfilesize"),
QString::number(target_fs));
oakengine_encoding_params_set_video_option(
params, "ove_targetfilesize",
QByteArray::number(target_fs).constData());
}
// Disable CRF encoding
params->set_video_option(QStringLiteral("crf"), QStringLiteral("-1"));
oakengine_encoding_params_set_video_option(params, "crf", "-1");
params->set_video_bit_rate(target_rate);
params->set_video_min_bit_rate(min_rate);
params->set_video_max_bit_rate(max_rate);
params->set_video_buffer_size(2000000);
oakengine_encoding_params_set_video_bit_rate(params, target_rate);
oakengine_encoding_params_set_video_min_bit_rate(params, min_rate);
oakengine_encoding_params_set_video_max_bit_rate(params, max_rate);
oakengine_encoding_params_set_video_buffer_size(params, 2000000);
}
params->set_video_option(QStringLiteral("preset"),
QString::number(preset_combobox_->currentIndex()));
oakengine_encoding_params_set_video_option(
params, "preset",
QByteArray::number(preset_combobox_->currentIndex()).constData());
}
void H264Section::set_opts(const EncodingParams *p)
void H264Section::set_opts(const OakEngineEncodingParams *p)
{
CompressionMethod method = static_cast<CompressionMethod>(
p->video_option(QStringLiteral("ove_compressionmethod")).toInt());
char buf[64];
CompressionMethod method = k_constant_rate_factor;
if (oakengine_encoding_params_video_option(
p, "ove_compressionmethod", buf,
static_cast<int>(sizeof(buf))) > 0) {
method = static_cast<CompressionMethod>(QString::fromUtf8(buf).toInt());
}
compression_method_stack_->setCurrentIndex(method);
if (method == k_constant_rate_factor) {
crf_section_->set_value(p->video_option(QStringLiteral("crf")).toInt());
if (oakengine_encoding_params_video_option(
p, "crf", buf, static_cast<int>(sizeof(buf))) > 0) {
crf_section_->set_value(QString::fromUtf8(buf).toInt());
}
} else {
int64_t target_rate = p->video_bit_rate();
int64_t max_rate = p->video_max_bit_rate();
int64_t target_rate = oakengine_encoding_params_video_bit_rate(p);
int64_t max_rate = oakengine_encoding_params_video_max_bit_rate(p);
if (method == k_target_bit_rate) {
// Use user-supplied values for the bit rate
@@ -170,9 +190,12 @@ void H264Section::set_opts(const EncodingParams *p)
bitrate_section_->set_maximum_bit_rate(max_rate);
} else {
// Calculate the bit rate from the file size divided by the sequence length in seconds (bits per second)
filesize_section_->set_file_size(
p->video_option(QStringLiteral("ove_targetfilesize"))
.toLongLong());
if (oakengine_encoding_params_video_option(
p, "ove_targetfilesize", buf,
static_cast<int>(sizeof(buf))) > 0) {
filesize_section_->set_file_size(
QString::fromUtf8(buf).toLongLong());
}
}
}
}
+2 -2
View File
@@ -100,9 +100,9 @@ public:
H264Section(QWidget *parent = nullptr);
H264Section(int default_crf, QWidget *parent);
virtual void add_opts(EncodingParams *params) override;
virtual void add_opts(OakEngineEncodingParams *params) override;
virtual void set_opts(const EncodingParams *p) override;
virtual void set_opts(const OakEngineEncodingParams *p) override;
private:
QStackedWidget *compression_method_stack_;
+358 -252
View File
@@ -33,16 +33,24 @@
#include "common/digit.h"
#include "common/qtutils.h"
#include "codec/ffmpeg/ffmpegencoder.h"
#include "codec/exportcodec.h"
#include "codec/exportformat.h"
#include "dialog/msgbox.h"
#include "dialog/task/task.h"
#include "exportsavepresetdialog.h"
#include "node/project.h"
#include "node/project/sequence/sequence.h"
#include "oakengine/events.h"
#include "widget/manageddisplay/colorprocessorhandle.h"
#include "widget/viewer/vieweroutpututils.h"
#include "oakengine/exporter.h"
#include "task/taskmanager.h"
#include "oakengine/project.h"
#include "oakengine/task.h"
#include "oakengine/encoding.h"
#include "oakengine/viewer.h"
#include "ui/icons/icons.h"
#include "widget/timeruler/timeruler.h"
#include "common/configwrapper.h"
namespace olive
{
@@ -54,159 +62,126 @@ namespace
// pix_fmt string (e.g. "yuv420p") to its index in the codec's supported
// list; 0 (the codec's preferred format) when absent.
int pix_fmt_index(ExportCodec::Codec codec, const QString &pix_fmt)
int pix_fmt_index(int codec, const QString &pix_fmt)
{
if (pix_fmt.isEmpty()) {
return 0;
}
FFmpegEncoder probe{ EncodingParams() };
const int index = probe.get_pixel_formats_for_codec(codec).indexOf(pix_fmt);
return index >= 0 ? index : 0;
return oakengine_encoding_pix_fmt_index(codec, pix_fmt.toUtf8().constData());
}
// EncodingParams (assembled by the dialog) -> facade POD. One-to-one with
// OakEngineEncodingParams (assembled by the dialog) -> facade POD. One-to-one with
// oak_export_options_ex; see oakengine/exporter.h for the field docs.
oak_export_options_ex params_to_ex(const EncodingParams &p)
oak_export_options_ex params_to_ex(const OakEngineEncodingParams *p)
{
oak_export_options_ex o = {};
const VideoParams &vp = p.video_params();
const Rational tb = vp.frame_rate().flipped();
int64_t vbrate = 0, abrate = 0;
int asample_rate = 0;
uint64_t ach_layout = 0;
int asample_fmt = 0;
int vthreads = 0;
int scaling = 0;
int is_img_seq = 0;
if (p.has_custom_range()) {
oak_video_params vp = {};
oakengine_encoding_params_get_video_params(p, &vp);
vbrate = oakengine_encoding_params_video_bit_rate(p);
abrate = oakengine_encoding_params_audio_bit_rate(p);
vthreads = oakengine_encoding_params_video_threads(p);
scaling = oakengine_encoding_params_video_scaling_method(p);
is_img_seq = oakengine_encoding_params_video_is_image_sequence(p);
if (oakengine_encoding_params_has_custom_range(p)) {
o.range_mode = OAKENGINE_EXPORT_RANGE_CUSTOM;
o.range_in_ts = Timecode::time_to_timestamp(p.custom_range().in(), tb);
int64_t r_in_num = 0, r_in_den = 1, r_out_num = 0, r_out_den = 1;
oakengine_encoding_params_get_custom_range(
p, &r_in_num, &r_in_den, &r_out_num, &r_out_den);
o.range_in_ts =
Timecode::time_to_timestamp(
Rational(r_in_num, r_in_den),
Rational(vp.time_base_num, vp.time_base_den));
o.range_out_ts =
Timecode::time_to_timestamp(p.custom_range().out(), tb);
Timecode::time_to_timestamp(
Rational(r_out_num, r_out_den),
Rational(vp.time_base_num, vp.time_base_den));
} else {
o.range_mode = OAKENGINE_EXPORT_RANGE_ENTIRE;
}
o.format = int(p.format());
o.video_enabled = p.video_enabled() ? 1 : 0;
o.video_codec = int(p.video_codec());
o.audio_enabled = p.audio_enabled() ? 1 : 0;
o.audio_codec = int(p.audio_codec());
o.subtitles_enabled = p.subtitles_enabled() ? 1 : 0;
o.subtitles_sidecar = p.subtitles_are_sidecar() ? 1 : 0;
o.format = oakengine_encoding_params_format(p);
o.video_enabled = oakengine_encoding_params_video_enabled(p) ? 1 : 0;
o.video_codec = oakengine_encoding_params_video_codec(p);
o.audio_enabled = oakengine_encoding_params_audio_enabled(p) ? 1 : 0;
o.audio_codec = oakengine_encoding_params_audio_codec(p);
o.subtitles_enabled = oakengine_encoding_params_subtitles_enabled(p) ? 1 : 0;
o.subtitles_sidecar = oakengine_encoding_params_subtitles_are_sidecar(p) ? 1 : 0;
o.subtitles_format =
p.subtitles_are_sidecar() ? int(p.subtitle_sidecar_fmt()) : 0;
o.subtitles_codec = p.subtitles_enabled() ? int(p.subtitles_codec()) : 0;
oakengine_encoding_params_subtitles_are_sidecar(p)
? oakengine_encoding_params_subtitles_sidecar_format(p)
: 0;
o.subtitles_codec = oakengine_encoding_params_subtitles_enabled(p)
? oakengine_encoding_params_subtitles_codec(p)
: 0;
o.video_bit_rate = p.video_bit_rate();
o.audio_bit_rate = p.audio_bit_rate();
o.video_pix_fmt = pix_fmt_index(p.video_codec(), p.video_pix_fmt());
o.video_bit_rate = vbrate;
o.audio_bit_rate = abrate;
o.audio_sample_rate = p.audio_params().sample_rate();
o.audio_channel_layout = p.audio_params().channel_layout();
o.audio_sample_format = int(p.audio_params().format());
const QString ct = p.color_transform().output();
if (ct.isEmpty()) {
o.color_transform = OAKENGINE_EXPORT_COLOR_REFERENCE;
} else if (ct == QStringLiteral("sRGB OETF")) {
o.color_transform = OAKENGINE_EXPORT_COLOR_SRGB_OETF;
} else if (ct == QStringLiteral("Rec.709 OETF")) {
o.color_transform = OAKENGINE_EXPORT_COLOR_REC709_OETF;
} else if (ct == QStringLiteral("BT.1886 EOTF")) {
o.color_transform = OAKENGINE_EXPORT_COLOR_BT1886_EOTF;
char pix_fmt_buf[64];
if (oakengine_encoding_params_video_pix_fmt(
p, pix_fmt_buf, static_cast<int>(sizeof(pix_fmt_buf))) > 0) {
o.video_pix_fmt = oakengine_encoding_pix_fmt_index(
o.video_codec, pix_fmt_buf);
} else {
o.color_transform = OAKENGINE_EXPORT_COLOR_CUSTOM;
const QByteArray utf = ct.toUtf8();
snprintf(o.color_transform_name, sizeof(o.color_transform_name),
"%s", utf.constData());
o.video_pix_fmt = 0;
}
o.video_width = vp.width();
o.video_height = vp.height();
o.frame_rate_num = vp.frame_rate().numerator();
o.frame_rate_den = vp.frame_rate().denominator();
o.pixel_aspect_num = vp.pixel_aspect_ratio().numerator();
o.pixel_aspect_den = vp.pixel_aspect_ratio().denominator();
o.interlacing = int(vp.interlacing());
o.pixel_format = int(vp.format());
o.scaling_method = int(p.video_scaling_method());
o.color_range = int(vp.color_range());
o.video_threads = p.video_threads();
o.is_image_sequence = p.video_is_image_sequence() ? 1 : 0;
if (oakengine_encoding_params_get_audio_params(
p, &asample_rate, &ach_layout, &asample_fmt) == OAKENGINE_OK) {
o.audio_sample_rate = asample_rate;
o.audio_channel_layout = ach_layout;
o.audio_sample_format = asample_fmt;
}
char ct_buf[128];
const int ct_ret = oakengine_encoding_params_color_transform_output(
p, ct_buf, static_cast<int>(sizeof(ct_buf)));
if (ct_ret <= 0 || ct_buf[0] == '\0') {
o.color_transform = OAKENGINE_EXPORT_COLOR_REFERENCE;
} else {
const QString ct = QString::fromUtf8(ct_buf);
if (ct == QStringLiteral("sRGB OETF")) {
o.color_transform = OAKENGINE_EXPORT_COLOR_SRGB_OETF;
} else if (ct == QStringLiteral("Rec.709 OETF")) {
o.color_transform = OAKENGINE_EXPORT_COLOR_REC709_OETF;
} else if (ct == QStringLiteral("BT.1886 EOTF")) {
o.color_transform = OAKENGINE_EXPORT_COLOR_BT1886_EOTF;
} else {
o.color_transform = OAKENGINE_EXPORT_COLOR_CUSTOM;
snprintf(o.color_transform_name, sizeof(o.color_transform_name),
"%s", ct_buf);
}
}
o.video_width = vp.width;
o.video_height = vp.height;
o.frame_rate_num = vp.time_base_den; // time_base is frame duration, so rate = den/num
o.frame_rate_den = vp.time_base_num;
o.pixel_aspect_num = vp.pixel_aspect_num;
o.pixel_aspect_den = vp.pixel_aspect_den;
o.interlacing = vp.interlacing;
o.pixel_format = vp.format;
o.scaling_method = scaling;
o.color_range = vp.color_range;
o.video_threads = vthreads;
o.is_image_sequence = is_img_seq;
return o;
}
} // namespace
/**
* @brief ExportTask replacement driven by the liboakengine C ABI facade
*
* Same Task contract as the engine's ExportTask (progress via
* progress_changed, cancel via CancelEvent), but the actual
* render+encode goes through oakengine_export_render_ex(): the facade
* owns the ExportTask instance, its event-loop drive and the conform
* prewarm. Cancellation is forwarded to the facade
* (oakengine_export_cancel()), which reports OAKENGINE_E_CANCELLED back.
*/
class FacadeExportTask : public Task {
public:
FacadeExportTask(ViewerOutput *viewer_node, const EncodingParams &params)
: sequence_(reinterpret_cast<OakEngineSequence *>(viewer_node))
, params_(params)
{
set_title(tr("Exporting \"%1\"").arg(viewer_node->get_label()));
}
protected:
virtual bool run() override
{
oak_export_options_ex o = params_to_ex(params_);
// Pass the codec section's encoder-specific options through.
for (auto it = params_.video_opts().cbegin();
it != params_.video_opts().cend(); ++it) {
oakengine_export_set_video_option(it.key().toUtf8().constData(),
it.value().toUtf8().constData());
}
oakengine_export_set_progress_callback(
&FacadeExportTask::forward_progress, this);
const int rc = oakengine_export_render_ex(
sequence_, params_.filename().toUtf8().constData(), &o);
oakengine_export_set_progress_callback(nullptr, nullptr);
oakengine_export_set_video_option("", nullptr);
if (rc == OAKENGINE_E_CANCELLED) {
// Mirror the engine task's cancelled state for TaskDialog.
cancel();
return false;
}
if (rc != OAKENGINE_OK) {
char err[1024];
err[0] = '\0';
oakengine_export_last_error(err, sizeof(err));
set_error(err[0] ? QString::fromUtf8(err) :
QStringLiteral("Export failed"));
return false;
}
return true;
}
virtual void CancelEvent() override
{
oakengine_export_cancel();
}
private:
static void forward_progress(double fraction, void *userdata)
{
static_cast<FacadeExportTask *>(userdata)->emit_progress(fraction);
}
void emit_progress(double fraction)
{
emit progress_changed(fraction);
}
OakEngineSequence *sequence_;
EncodingParams params_;
};
ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
QWidget *parent)
: super(parent)
@@ -312,16 +287,32 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
preferences_tabs_ = new QTabWidget();
color_manager_ = viewer_node_->project()->color_manager();
color_manager_ = oak_color_manager(viewer_node_->project()->color_manager());
video_tab_ = new ExportVideoTab(color_manager_);
add_preferences_tab(video_tab_, tr("Video"));
// Set video tab time and make connections
connect(viewer_node, &ViewerOutput::playhead_changed, video_tab_,
&ExportVideoTab::set_time);
connect(video_tab_, &ExportVideoTab::time_changed, viewer_node,
&ViewerOutput::set_playhead);
video_tab_->set_time(viewer_node->get_playhead());
viewer_sub_ = oakengine_event_subscribe(
reinterpret_cast<OakEngineNode *>(viewer_node),
OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED,
[](const oakengine_event *event, void *userdata) {
auto *dlg = static_cast<ExportDialog *>(userdata);
auto *tab = dlg->video_tab_;
tab->set_time(Rational(event->a, event->b));
},
this);
connect(video_tab_, &ExportVideoTab::time_changed, this,
[viewer_node](const Rational &time) {
oakengine_viewer_set_playhead(
reinterpret_cast<OakEngineNode *>(viewer_node),
time.numerator(), time.denominator());
});
{
int64_t pn, pd;
oakengine_viewer_get_playhead(
reinterpret_cast<OakEngineNode *>(viewer_node), &pn, &pd);
video_tab_->set_time(Rational(pn, pd));
}
audio_tab_ = new ExportAudioTab();
add_preferences_tab(audio_tab_, tr("Audio"));
@@ -394,11 +385,11 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
set_default_filename();
// Set defaults
previously_selected_format_ = ExportFormat::k_format_mpe_g4_video;
previously_selected_format_ = OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO;
connect(format_combobox_, &ExportFormatComboBox::format_changed, this,
&ExportDialog::format_changed);
VideoParams vp = viewer_node_->get_video_params();
VideoParams vp = viewer_output_video_params(viewer_node_);
video_aspect_ratio_ =
static_cast<double>(vp.width()) / static_cast<double>(vp.height());
@@ -430,7 +421,8 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
// If the viewer already has cached params, use them
if (!stills_only_mode_ &&
viewer_node_->get_last_used_encoding_params().is_valid()) {
oakengine_encoding_params_get_last_used(
reinterpret_cast<OakEngineSequence *>(viewer_node_)) != nullptr) {
// This will automatically set the param data
QtUtils::set_combo_box_data(preset_combobox_, k_preset_last_used);
} else {
@@ -477,8 +469,9 @@ void ExportDialog::start_export()
// Validate if the entered filename contains the correct extension (the extension is necessary
// for both FFmpeg and OIIO to determine the output format)
QString necessary_ext = QStringLiteral(".%1").arg(
ExportFormat::get_extension(format_combobox_->get_format()));
char ext_buf[64];
int ext_len = oakengine_encoding_format_extension(format_combobox_->get_format(), ext_buf, sizeof(ext_buf));
QString necessary_ext = QStringLiteral(".%1").arg(QString::fromUtf8(ext_buf, ext_len));
QString proposed_filename = filename_edit_->text().trimmed();
// If it doesn't, see if the user wants to append it automatically. If not, we don't abort the export.
@@ -513,7 +506,7 @@ void ExportDialog::start_export()
// Validate if this is an image sequence and if the filename contains enough digits
if (video_tab_->is_image_sequence_set()) {
// Ensure filename contains digits
if (!Encoder::filename_contains_digit_placeholder(proposed_filename)) {
if (!oakengine_encoding_filename_contains_digit_placeholder(proposed_filename.toUtf8().constData())) {
msg_box(
this, QMessageBox::Critical, tr("Invalid filename"),
tr("Export is set to an image sequence, but the filename does not have a section for digits "
@@ -524,7 +517,7 @@ void ExportDialog::start_export()
int64_t frame_count = get_export_length_in_timebase_units();
int64_t needed_digit_count = get_digit_count(frame_count);
int current_digit_count =
Encoder::get_image_sequence_placeholder_digit_count(proposed_filename);
oakengine_encoding_image_sequence_digit_count(proposed_filename.toUtf8().constData());
if (current_digit_count < needed_digit_count) {
msg_box(
this, QMessageBox::Critical, tr("Invalid filename"),
@@ -549,8 +542,8 @@ void ExportDialog::start_export()
// Validate video resolution
if (video_enabled_->isChecked() &&
(video_tab_->get_selected_codec() == ExportCodec::k_codec_h264 ||
video_tab_->get_selected_codec() == ExportCodec::k_codec_h265) &&
(video_tab_->get_selected_codec() == OAKENGINE_ENCODING_CODEC_H264 ||
video_tab_->get_selected_codec() == OAKENGINE_ENCODING_CODEC_H265) &&
(video_tab_->width_slider()->get_value() % 2 != 0 ||
video_tab_->height_slider()->get_value() % 2 != 0)) {
msg_box(this, QMessageBox::Critical, tr("Invalid Parameters"),
@@ -558,12 +551,13 @@ void ExportDialog::start_export()
return;
}
FacadeExportTask *task =
new FacadeExportTask(viewer_node_, generate_params());
OakEngineTask *task = oakengine_task_create_export(
reinterpret_cast<OakEngineSequence *>(viewer_node_),
generate_params());
if (export_bkg_box_->isChecked()) {
// Send to TaskManager to export in background
TaskManager::instance()->add_task(task);
oakengine_task_manager_add(task);
this->accept();
} else {
// Use modal dialog box
@@ -578,7 +572,7 @@ void ExportDialog::export_finished()
{
TaskDialog *td = static_cast<TaskDialog *>(sender());
if (td->get_task()->is_cancelled()) {
if (oakengine_task_is_cancelled(td->get_task())) {
// If this task was cancelled, we stay open so the user can potentially queue another export
} else {
// Accept this dialog and close
@@ -600,11 +594,14 @@ void ExportDialog::image_sequence_check_box_changed(bool e)
QString suffix = current_fileinfo.suffix();
if (e) {
if (!Encoder::filename_contains_digit_placeholder(basename)) {
if (!oakengine_encoding_filename_contains_digit_placeholder(basename.toUtf8().constData())) {
basename.append(QStringLiteral("_[#####]"));
}
} else {
basename = Encoder::filename_remove_digit_placeholder(basename);
char buf[1024];
oakengine_encoding_filename_remove_digit_placeholder(
basename.toUtf8().constData(), buf, sizeof(buf));
basename = QString::fromUtf8(buf);
}
// Set filename
@@ -636,7 +633,14 @@ void ExportDialog::preset_combo_box_changed()
if (preset_number == k_preset_default) {
set_defaults();
} else if (preset_number == k_preset_last_used) {
set_params(viewer_node_->get_last_used_encoding_params());
OakEngineEncodingParams *last =
oakengine_encoding_params_get_last_used(
reinterpret_cast<OakEngineSequence *>(viewer_node_));
if (last) {
set_params(last);
} else {
set_defaults();
}
} else {
set_params(presets_.at(preset_number));
}
@@ -653,12 +657,17 @@ void ExportDialog::add_preferences_tab(QWidget *inner_widget,
void ExportDialog::browse_filename()
{
ExportFormat::Format f = format_combobox_->get_format();
int f = format_combobox_->get_format();
char name_buf[256];
char ext_buf[64];
oakengine_encoding_format_name(f, name_buf, sizeof(name_buf));
oakengine_encoding_format_extension(f, ext_buf, sizeof(ext_buf));
QString browsed_fn = QFileDialog::getSaveFileName(
this, "", filename_edit_->text().trimmed(),
QStringLiteral("%1 (*.%2)")
.arg(ExportFormat::get_name(f), ExportFormat::get_extension(f)),
.arg(QString::fromUtf8(name_buf), QString::fromUtf8(ext_buf)),
nullptr,
// We don't confirm overwrite here because we do it later
@@ -669,12 +678,14 @@ void ExportDialog::browse_filename()
}
}
void ExportDialog::format_changed(ExportFormat::Format current_format)
void ExportDialog::format_changed(int current_format)
{
QString current_filename = filename_edit_->text().trimmed();
QString previously_selected_ext =
ExportFormat::get_extension(previously_selected_format_);
QString currently_selected_ext = ExportFormat::get_extension(current_format);
char ext_buf[64];
oakengine_encoding_format_extension(previously_selected_format_, ext_buf, sizeof(ext_buf));
QString previously_selected_ext = QString::fromUtf8(ext_buf);
oakengine_encoding_format_extension(current_format, ext_buf, sizeof(ext_buf));
QString currently_selected_ext = QString::fromUtf8(ext_buf);
// If the previous extension was added, remove it
if (current_filename.endsWith(previously_selected_ext,
@@ -742,25 +753,45 @@ void ExportDialog::load_presets()
preset_combobox_->addItem(tr("Default"), k_preset_default);
if (viewer_node_->get_last_used_encoding_params().is_valid()) {
if (oakengine_encoding_params_get_last_used(
reinterpret_cast<OakEngineSequence *>(viewer_node_)) != nullptr) {
preset_combobox_->addItem(tr("Last Used"), k_preset_last_used);
}
preset_combobox_->insertSeparator(preset_combobox_->count());
QStringList l = EncodingParams::get_list_of_presets();
QStringList l;
{
const int n = oakengine_encoding_preset_count();
for (int i = 0; i < n; i++) {
char name_buf[256];
if (oakengine_encoding_preset_name(
i, name_buf, static_cast<int>(sizeof(name_buf))) > 0) {
l.append(QString::fromUtf8(name_buf));
}
}
}
presets_.reserve(l.size());
for (const QString &preset : l) {
EncodingParams p;
OakEngineEncodingParams *p = oakengine_encoding_params_create();
QFile f(EncodingParams::get_preset_path().filePath(preset));
if (f.open(QFile::ReadOnly)) {
if (p.load(&f)) {
preset_combobox_->addItem(preset, int(presets_.size()));
presets_.push_back(p);
}
f.close();
char preset_path_buf[1024];
preset_path_buf[0] = '\0';
oakengine_encoding_preset_path(
preset_path_buf, static_cast<int>(sizeof(preset_path_buf)));
const QByteArray preset_path_utf =
QDir(QString::fromUtf8(preset_path_buf))
.filePath(preset)
.toUtf8();
const int rc = oakengine_encoding_params_load_file(
p, preset_path_utf.constData());
if (rc == OAKENGINE_OK) {
preset_combobox_->addItem(preset, int(presets_.size()));
presets_.push_back(p);
} else {
oakengine_encoding_params_destroy(p);
}
}
@@ -771,13 +802,17 @@ void ExportDialog::set_default_filename()
{
Project *p = viewer_node_->project();
char fn_buf[512];
oakengine_project_filename(
reinterpret_cast<OakEngineProject *>(p),
fn_buf, sizeof(fn_buf));
QDir doc_location;
if (p->filename().isEmpty()) {
if (fn_buf[0] == '\0') {
doc_location.setPath(QStandardPaths::writableLocation(
QStandardPaths::DocumentsLocation));
} else {
doc_location = QFileInfo(p->filename()).dir();
doc_location = QFileInfo(fn_buf).dir();
}
QString file_location = doc_location.filePath(viewer_node_->get_label());
@@ -801,14 +836,14 @@ bool ExportDialog::sequence_has_subtitles() const
void ExportDialog::set_defaults()
{
if (!stills_only_mode_) {
format_combobox_->set_format(ExportFormat::k_format_mpe_g4_video);
format_combobox_->set_format(OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO);
} else {
format_combobox_->set_format(ExportFormat::k_format_png);
format_combobox_->set_format(OAKENGINE_ENCODING_FORMAT_PNG);
}
format_changed(format_combobox_->get_format());
VideoParams vp = viewer_node_->get_video_params();
AudioParams ap = viewer_node_->get_audio_params();
VideoParams vp = viewer_output_video_params(viewer_node_);
AudioParams ap = viewer_output_audio_params(viewer_node_);
video_tab_->width_slider()->set_value(vp.width());
video_tab_->width_slider()->SetDefaultValue(vp.width());
@@ -826,151 +861,217 @@ void ExportDialog::set_defaults()
audio_tab_->channel_layout_combobox()->set_channel_layout(
ap.channel_layout());
subtitles_enabled_->setChecked(sequence_has_subtitles());
subtitle_tab_->set_sidecar_format(ExportFormat::k_format_srt);
subtitle_tab_->set_sidecar_format(OAKENGINE_ENCODING_FORMAT_SRT);
}
EncodingParams ExportDialog::generate_params() const
OakEngineEncodingParams *ExportDialog::generate_params() const
{
VideoParams video_render_params(
static_cast<int>(video_tab_->width_slider()->get_value()),
static_cast<int>(video_tab_->height_slider()->get_value()),
get_selected_timebase(),
video_tab_->pixel_format_field()->get_pixel_format(),
VideoParams::k_internal_channel_count,
video_tab_->pixel_aspect_combobox()->get_pixel_aspect_ratio(),
video_tab_->interlaced_combobox()->get_interlace_mode(), 1);
OakEngineEncodingParams *params = oakengine_encoding_params_create();
AudioParams audio_render_params(
audio_tab_->sample_rate_combobox()->get_sample_rate(),
audio_tab_->channel_layout_combobox()->get_channel_layout(),
audio_tab_->sample_format_combobox()->get_sample_format());
oakengine_encoding_params_set_format(
params, format_combobox_->get_format());
oakengine_encoding_params_set_filename(
params, filename_edit_->text().trimmed().toUtf8().constData());
EncodingParams params;
params.set_format(format_combobox_->get_format());
params.set_filename(filename_edit_->text().trimmed());
params.set_export_length(viewer_node_->get_length());
const Rational export_len = viewer_node_->get_length();
oakengine_encoding_params_set_export_length(
params, export_len.numerator(), export_len.denominator());
if (ExportCodec::is_codec_a_still_image(video_tab_->get_selected_codec()) &&
if (oakengine_encoding_codec_is_still_image(video_tab_->get_selected_codec()) &&
!video_tab_->is_image_sequence_set()) {
// Exporting as image without exporting image sequence, only export one frame
Rational export_time = video_tab_->get_still_image_time();
params.set_custom_range(
TimeRange(export_time, export_time + get_selected_timebase()));
const Rational tb = get_selected_timebase();
oakengine_encoding_params_set_custom_range(
params, export_time.numerator(), export_time.denominator(),
(export_time + tb).numerator(),
(export_time + tb).denominator());
} else if (range_combobox_->currentIndex() == k_range_in_to_out) {
// Assume if this combobox is enabled, workarea is enabled - a check that we make in this dialog's constructor
params.set_custom_range(viewer_node_->get_work_area()->range());
const TimeRange &r = viewer_node_->get_work_area()->range();
oakengine_encoding_params_set_custom_range(
params, r.in().numerator(), r.in().denominator(),
r.out().numerator(), r.out().denominator());
}
if (video_tab_->scaling_method_combobox()->isEnabled()) {
params.set_video_scaling_method(
static_cast<EncodingParams::VideoScalingMethod>(
video_tab_->scaling_method_combobox()->currentData().toInt()));
oakengine_encoding_params_set_video_scaling_method(
params,
video_tab_->scaling_method_combobox()->currentData().toInt());
}
if (video_enabled_->isChecked()) {
ExportCodec::Codec video_codec = video_tab_->get_selected_codec();
const int video_codec = video_tab_->get_selected_codec();
video_render_params.set_color_range(video_tab_->color_range());
// Build video params from the tab
const int vw = static_cast<int>(video_tab_->width_slider()->get_value());
const int vh = static_cast<int>(video_tab_->height_slider()->get_value());
const Rational tb = get_selected_timebase();
const int pix_fmt = video_tab_->pixel_format_field()->get_pixel_format();
const int ch_count = oakengine_video_params_internal_channel_count();
const Rational par = video_tab_->pixel_aspect_combobox()->get_pixel_aspect_ratio();
const int interlace = video_tab_->interlaced_combobox()->get_interlace_mode();
params.enable_video(video_render_params, video_codec);
oak_video_params vp = {};
vp.width = vw;
vp.height = vh;
vp.time_base_num = tb.numerator();
vp.time_base_den = tb.denominator();
vp.format = pix_fmt;
vp.pixel_aspect_num = par.numerator();
vp.pixel_aspect_den = par.denominator();
vp.interlacing = interlace;
vp.color_range = video_tab_->color_range();
params.set_video_threads(video_tab_->threads());
oakengine_encoding_params_enable_video(params, &vp, video_codec);
oakengine_encoding_params_set_video_threads(
params, video_tab_->threads());
if (video_tab_->isVisible()) {
video_tab_->get_codec_section()->add_opts(&params);
video_tab_->get_codec_section()->add_opts(params);
}
params.set_color_transform(video_tab_->current_ocio_color_space());
{
const QString ct = video_tab_->current_ocio_color_space();
oakengine_encoding_params_set_color_transform(
params, ct.isEmpty() ? nullptr : ct.toUtf8().constData());
}
params.set_video_pix_fmt(video_tab_->pix_fmt());
{
const QString pix_fmt_name = video_tab_->pix_fmt();
oakengine_encoding_params_set_video_pix_fmt(
params,
pix_fmt_name.isEmpty() ? nullptr
: pix_fmt_name.toUtf8().constData());
}
params.set_video_is_image_sequence(video_tab_->is_image_sequence_set());
oakengine_encoding_params_set_video_is_image_sequence(
params, video_tab_->is_image_sequence_set() ? 1 : 0);
}
if (audio_enabled_->isChecked()) {
ExportCodec::Codec audio_codec = audio_tab_->get_codec();
params.enable_audio(audio_render_params, audio_codec);
const int audio_codec = audio_tab_->get_codec();
const int sample_rate = audio_tab_->sample_rate_combobox()->get_sample_rate();
const uint64_t ch_layout = audio_tab_->channel_layout_combobox()->get_channel_layout();
const int sample_fmt = audio_tab_->sample_format_combobox()->get_sample_format();
params.set_audio_bit_rate(audio_tab_->bit_rate_slider()->get_value() *
1000);
oakengine_encoding_params_enable_audio(
params, sample_rate, ch_layout, sample_fmt, audio_codec);
oakengine_encoding_params_set_audio_bit_rate(
params,
audio_tab_->bit_rate_slider()->get_value() * 1000);
}
if (subtitles_enabled_->isEnabled() && subtitles_enabled_->isChecked()) {
if (!subtitle_tab_->get_sidecar_enabled()) {
// Export subtitles embedded in container
params.enable_subtitles(subtitle_tab_->get_subtitle_codec());
oakengine_encoding_params_enable_subtitles(
params, subtitle_tab_->get_subtitle_codec());
} else {
// Export subtitles to a sidecar file
params.enable_sidecar_subtitles(subtitle_tab_->get_sidecar_format(),
subtitle_tab_->get_subtitle_codec());
oakengine_encoding_params_enable_sidecar_subtitles(
params, subtitle_tab_->get_sidecar_format(),
subtitle_tab_->get_subtitle_codec());
}
}
return params;
}
void ExportDialog::set_params(const EncodingParams &e)
void ExportDialog::set_params(const OakEngineEncodingParams *e)
{
format_combobox_->set_format(e.format());
format_combobox_->set_format(oakengine_encoding_params_format(e));
format_changed(format_combobox_->get_format());
if (e.has_custom_range() && viewer_node_->get_work_area()->enabled()) {
if (oakengine_encoding_params_has_custom_range(e) &&
viewer_node_->get_work_area()->enabled()) {
range_combobox_->setCurrentIndex(k_range_in_to_out);
}
QtUtils::set_combo_box_data(video_tab_->scaling_method_combobox(),
e.video_scaling_method());
oakengine_encoding_params_video_scaling_method(e));
video_enabled_->setChecked(e.video_enabled());
if (e.video_enabled()) {
video_tab_->width_slider()->set_value(e.video_params().width());
video_tab_->height_slider()->set_value(e.video_params().height());
set_selected_timebase(e.video_params().time_base());
const int video_enabled = oakengine_encoding_params_video_enabled(e);
video_enabled_->setChecked(video_enabled);
if (video_enabled) {
oak_video_params vp = {};
oakengine_encoding_params_get_video_params(e, &vp);
video_tab_->width_slider()->set_value(vp.width);
video_tab_->height_slider()->set_value(vp.height);
set_selected_timebase(Rational(vp.time_base_num, vp.time_base_den));
video_tab_->pixel_format_field()->set_pixel_format(
e.video_params().format());
static_cast<olive::core::PixelFormat::Format>(vp.format));
video_tab_->pixel_aspect_combobox()->set_pixel_aspect_ratio(
e.video_params().pixel_aspect_ratio());
video_tab_->interlaced_combobox()->set_interlace_mode(
e.video_params().interlacing());
Rational(vp.pixel_aspect_num, vp.pixel_aspect_den));
video_tab_->interlaced_combobox()->set_interlace_mode(vp.interlacing);
video_tab_->set_selected_codec(e.video_codec());
video_tab_->set_selected_codec(oakengine_encoding_params_video_codec(e));
video_tab_->set_color_range(e.video_params().color_range());
video_tab_->set_color_range(vp.color_range);
video_tab_->set_threads(e.video_threads());
video_tab_->set_threads(oakengine_encoding_params_video_threads(e));
if (video_tab_->isVisible()) {
video_tab_->get_codec_section()->set_opts(&e);
video_tab_->get_codec_section()->set_opts(e);
}
video_tab_->set_ocio_color_space(e.color_transform().output());
{
char ct_buf[128];
if (oakengine_encoding_params_color_transform_output(
e, ct_buf, static_cast<int>(sizeof(ct_buf))) > 0) {
video_tab_->set_ocio_color_space(QString::fromUtf8(ct_buf));
} else {
video_tab_->set_ocio_color_space(QString());
}
}
video_tab_->set_pix_fmt(e.video_pix_fmt());
{
char pix_fmt_buf[64];
if (oakengine_encoding_params_video_pix_fmt(
e, pix_fmt_buf, static_cast<int>(sizeof(pix_fmt_buf))) > 0) {
video_tab_->set_pix_fmt(QString::fromUtf8(pix_fmt_buf));
} else {
video_tab_->set_pix_fmt(QString());
}
}
video_tab_->set_image_sequence(e.video_is_image_sequence());
video_tab_->set_image_sequence(
oakengine_encoding_params_video_is_image_sequence(e));
}
audio_enabled_->setChecked(e.audio_enabled());
if (e.audio_enabled()) {
audio_tab_->sample_rate_combobox()->set_sample_rate(
e.audio_params().sample_rate());
audio_tab_->channel_layout_combobox()->set_channel_layout(
e.audio_params().channel_layout());
const int audio_enabled = oakengine_encoding_params_audio_enabled(e);
audio_enabled_->setChecked(audio_enabled);
if (audio_enabled) {
int asample_rate = 0;
uint64_t ach_layout = 0;
int asample_fmt = 0;
oakengine_encoding_params_get_audio_params(
e, &asample_rate, &ach_layout, &asample_fmt);
audio_tab_->sample_rate_combobox()->set_sample_rate(asample_rate);
audio_tab_->channel_layout_combobox()->set_channel_layout(ach_layout);
audio_tab_->sample_format_combobox()->set_sample_format(
e.audio_params().format());
static_cast<olive::core::SampleFormat::Format>(asample_fmt));
audio_tab_->set_codec(e.audio_codec());
audio_tab_->set_codec(oakengine_encoding_params_audio_codec(e));
audio_tab_->bit_rate_slider()->set_value(e.audio_bit_rate() / 1000);
audio_tab_->bit_rate_slider()->set_value(
oakengine_encoding_params_audio_bit_rate(e) / 1000);
}
if (subtitles_enabled_->isEnabled()) {
subtitles_enabled_->setChecked(e.subtitles_enabled());
subtitle_tab_->set_sidecar_enabled(e.subtitles_are_sidecar());
if (e.subtitles_enabled()) {
subtitle_tab_->set_subtitle_codec(e.subtitles_codec());
if (e.subtitles_are_sidecar()) {
subtitle_tab_->set_sidecar_format(e.subtitle_sidecar_fmt());
const int subs_enabled = oakengine_encoding_params_subtitles_enabled(e);
subtitles_enabled_->setChecked(subs_enabled);
subtitle_tab_->set_sidecar_enabled(
oakengine_encoding_params_subtitles_are_sidecar(e));
if (subs_enabled) {
subtitle_tab_->set_subtitle_codec(
oakengine_encoding_params_subtitles_codec(e));
if (oakengine_encoding_params_subtitles_are_sidecar(e)) {
subtitle_tab_->set_sidecar_format(
oakengine_encoding_params_subtitles_sidecar_format(e));
}
}
}
@@ -997,7 +1098,10 @@ void ExportDialog::done(int r)
preview_viewer_->connect_viewer_node(nullptr);
if (!stills_only_mode_) {
viewer_node_->set_last_used_encoding_params(generate_params());
OakEngineEncodingParams *p = generate_params();
oakengine_encoding_params_set_last_used(
reinterpret_cast<OakEngineSequence *>(viewer_node_), p);
oakengine_encoding_params_destroy(p);
}
super::done(r);
@@ -1024,14 +1128,16 @@ void ExportDialog::update_viewer_dimensions()
static_cast<int>(video_tab_->width_slider()->get_value()),
static_cast<int>(video_tab_->height_slider()->get_value()));
VideoParams vp = viewer_node_->get_video_params();
VideoParams vp = viewer_output_video_params(viewer_node_);
QMatrix4x4 transform = EncodingParams::generate_matrix(
static_cast<EncodingParams::VideoScalingMethod>(
video_tab_->scaling_method_combobox()->currentData().toInt()),
float mat16[16];
oakengine_encoding_generate_matrix(
video_tab_->scaling_method_combobox()->currentData().toInt(),
vp.width(), vp.height(),
static_cast<int>(video_tab_->width_slider()->get_value()),
static_cast<int>(video_tab_->height_slider()->get_value()));
static_cast<int>(video_tab_->height_slider()->get_value()),
mat16);
QMatrix4x4 transform(mat16);
preview_viewer_->set_matrix(transform);
}
+10 -8
View File
@@ -24,17 +24,17 @@
#include <QComboBox>
#include <QDialog>
#include <cstdint>
#include <QDialogButtonBox>
#include <QLineEdit>
#include <QProgressBar>
#include "codec/encoder.h"
#include "codec/exportcodec.h"
#include "codec/exportformat.h"
#include "dialog/export/exportformatcombobox.h"
#include "exportaudiotab.h"
#include "exportsubtitlestab.h"
#include "exportvideotab.h"
#include "oakengine/encoding.h"
#include "widget/nodeparamview/nodeparamviewwidgetbridge.h"
#include "widget/viewer/viewer.h"
@@ -54,8 +54,8 @@ public:
Rational get_selected_timebase() const;
void set_selected_timebase(const Rational &r);
EncodingParams generate_params() const;
void set_params(const EncodingParams &e);
OakEngineEncodingParams *generate_params() const;
void set_params(const OakEngineEncodingParams *e);
virtual bool eventFilter(QObject *o, QEvent *e) override;
@@ -77,7 +77,9 @@ private:
ViewerOutput *viewer_node_;
ExportFormat::Format previously_selected_format_;
int64_t viewer_sub_ = 0;
int previously_selected_format_;
Rational get_export_length() const;
int64_t get_export_length_in_timebase_units() const;
@@ -93,7 +95,7 @@ private:
QComboBox *preset_combobox_;
QComboBox *range_combobox_;
std::vector<EncodingParams> presets_;
std::vector<OakEngineEncodingParams *> presets_;
QCheckBox *video_enabled_;
QCheckBox *audio_enabled_;
@@ -109,7 +111,7 @@ private:
double video_aspect_ratio_;
ColorManager *color_manager_;
OakEngineColorManager *color_manager_;
QWidget *preferences_area_;
QCheckBox *export_bkg_box_;
@@ -122,7 +124,7 @@ private:
private slots:
void browse_filename();
void format_changed(ExportFormat::Format current_format);
void format_changed(int current_format);
void resolution_changed();
@@ -54,13 +54,13 @@ public:
pixel_format_combobox_->setCurrentText(s);
}
VideoParams::ColorRange yuv_range() const
int yuv_range() const
{
return static_cast<VideoParams::ColorRange>(
return static_cast<int>(
yuv_color_range_combobox_->currentIndex());
}
void set_yuv_range(VideoParams::ColorRange i)
void set_yuv_range(int i)
{
yuv_color_range_combobox_->setCurrentIndex(i);
}
+21 -8
View File
@@ -24,6 +24,9 @@
#include <QGridLayout>
#include <QLabel>
#include <olive/core/core.h>
#include "oakengine/encoding.h"
namespace olive
{
@@ -87,14 +90,17 @@ ExportAudioTab::ExportAudioTab(QWidget *parent)
outer_layout->addStretch();
}
int ExportAudioTab::set_format(ExportFormat::Format format)
int ExportAudioTab::set_format(int format)
{
QList<ExportCodec::Codec> acodecs = ExportFormat::get_audio_codecs(format);
setEnabled(!acodecs.isEmpty());
const int acodec_count = oakengine_encoding_format_audio_codec_count(format);
setEnabled(acodec_count > 0);
codec_combobox_->blockSignals(true);
codec_combobox_->clear();
foreach (ExportCodec::Codec acodec, acodecs) {
codec_combobox_->addItem(ExportCodec::get_codec_name(acodec), acodec);
for (int i = 0; i < acodec_count; i++) {
int codec = oakengine_encoding_format_audio_codec_at(format, i);
char buf[256];
oakengine_encoding_codec_name(codec, buf, sizeof(buf));
codec_combobox_->addItem(QString::fromUtf8(buf), codec);
}
codec_combobox_->blockSignals(false);
fmt_ = format;
@@ -102,18 +108,25 @@ int ExportAudioTab::set_format(ExportFormat::Format format)
update_sample_formats();
update_bit_rate_enabled();
return acodecs.size();
return acodec_count;
}
void ExportAudioTab::update_sample_formats()
{
auto fmts = ExportFormat::get_sample_formats_for_codec(fmt_, get_codec());
// Use oakengine to get sample format values and build the vector
const int count = oakengine_encoding_sample_format_count(fmt_, get_codec());
std::vector<olive::core::SampleFormat> fmts;
fmts.reserve(count);
for (int i = 0; i < count; i++) {
int val = oakengine_encoding_sample_format_at(fmt_, get_codec(), i);
fmts.push_back(olive::core::SampleFormat(static_cast<olive::core::SampleFormat::Format>(val)));
}
sample_format_combobox_->set_available_formats(fmts);
}
void ExportAudioTab::update_bit_rate_enabled()
{
bool uses_bitrate = !ExportCodec::is_codec_lossless(get_codec());
bool uses_bitrate = !oakengine_encoding_codec_is_lossless(get_codec());
bit_rate_slider_->setEnabled(uses_bitrate);
if (!uses_bitrate) {
+5 -7
View File
@@ -26,7 +26,6 @@
#include <QWidget>
#include "common/define.h"
#include "codec/exportformat.h"
#include "widget/slider/integerslider.h"
#include "widget/standardcombos/standardcombos.h"
@@ -38,13 +37,12 @@ class ExportAudioTab : public QWidget {
public:
ExportAudioTab(QWidget *parent = nullptr);
ExportCodec::Codec get_codec() const
int get_codec() const
{
return static_cast<ExportCodec::Codec>(
codec_combobox_->currentData().toInt());
return codec_combobox_->currentData().toInt();
}
void set_codec(ExportCodec::Codec c)
void set_codec(int c)
{
for (int i = 0; i < codec_combobox_->count(); i++) {
if (codec_combobox_->itemData(i) == c) {
@@ -75,10 +73,10 @@ public:
}
public slots:
int set_format(ExportFormat::Format format);
int set_format(int format);
private:
ExportFormat::Format fmt_;
int fmt_;
QComboBox *codec_combobox_;
SampleRateComboBox *sample_rate_combobox_;
ChannelLayoutComboBox *channel_layout_combobox_;
+23 -16
View File
@@ -24,6 +24,7 @@
#include <QHBoxLayout>
#include <QLabel>
#include "oakengine/encoding.h"
#include "ui/icons/icons.h"
namespace olive
@@ -32,6 +33,10 @@ namespace olive
ExportFormatComboBox::ExportFormatComboBox(Mode mode, QWidget *parent)
: QComboBox(parent)
{
// The invalid placeholder format is the format count itself
// (ExportFormat::k_format_count), not -1.
current_ = oakengine_encoding_format_count();
custom_menu_ = new Menu(this);
// Populate combobox formats
@@ -69,43 +74,45 @@ void ExportFormatComboBox::showPopup()
custom_menu_->exec(mapToGlobal(QPoint(0, 0)));
}
void ExportFormatComboBox::set_format(ExportFormat::Format fmt)
void ExportFormatComboBox::set_format(int fmt)
{
current_ = fmt;
clear();
addItem(ExportFormat::get_name(current_));
char buf[256];
oakengine_encoding_format_name(fmt, buf, sizeof(buf));
addItem(QString::fromUtf8(buf));
}
void ExportFormatComboBox::handle_index_change(QAction *a)
{
ExportFormat::Format f =
static_cast<ExportFormat::Format>(a->data().toInt());
int f = a->data().toInt();
set_format(f);
emit format_changed(f);
}
void ExportFormatComboBox::populate_type(Track::Type type)
{
for (int i = 0; i < ExportFormat::k_format_count; i++) {
ExportFormat::Format f = static_cast<ExportFormat::Format>(i);
const int fmt_count = oakengine_encoding_format_count();
for (int i = 0; i < fmt_count; i++) {
int f = i;
char buf[256];
if (type == Track::k_video &&
!ExportFormat::get_video_codecs(f).isEmpty()) {
bool has_video = oakengine_encoding_format_video_codec_count(f) > 0;
bool has_audio = oakengine_encoding_format_audio_codec_count(f) > 0;
bool has_sub = oakengine_encoding_format_subtitle_codec_count(f) > 0;
if (type == Track::k_video && has_video) {
// Do nothing
} else if (type == Track::k_audio &&
ExportFormat::get_video_codecs(f).isEmpty() &&
!ExportFormat::get_audio_codecs(f).isEmpty()) {
} else if (type == Track::k_audio && !has_video && has_audio) {
// Do nothing
} else if (type == Track::k_subtitle &&
ExportFormat::get_video_codecs(f).isEmpty() &&
ExportFormat::get_audio_codecs(f).isEmpty() &&
!ExportFormat::get_subtitle_codecs(f).isEmpty()) {
} else if (type == Track::k_subtitle && !has_video && !has_audio && has_sub) {
// Do nothing
} else {
continue;
}
QString format_name = ExportFormat::get_name(f);
oakengine_encoding_format_name(f, buf, sizeof(buf));
QString format_name = QString::fromUtf8(buf);
QAction *a = custom_menu_->addAction(format_name);
a->setData(i);
+4 -5
View File
@@ -25,7 +25,6 @@
#include <QComboBox>
#include <QWidgetAction>
#include "codec/exportformat.h"
#include "node/output/track/track.h"
#include "widget/menu/menu.h"
@@ -48,7 +47,7 @@ public:
{
}
ExportFormat::Format get_format() const
int get_format() const
{
return current_;
}
@@ -56,10 +55,10 @@ public:
void showPopup();
signals:
void format_changed(ExportFormat::Format fmt);
void format_changed(int fmt);
public slots:
void set_format(ExportFormat::Format fmt);
void set_format(int fmt);
private slots:
void handle_index_change(QAction *a);
@@ -71,7 +70,7 @@ private:
Menu *custom_menu_;
ExportFormat::Format current_ = ExportFormat::k_format_count;
int current_ = -1; // was ExportFormat::k_format_count
};
}
+27 -11
View File
@@ -22,6 +22,7 @@
#include "exportsavepresetdialog.h"
#include <QDialogButtonBox>
#include <QDir>
#include <QLabel>
#include <QMessageBox>
#include <QVBoxLayout>
@@ -29,7 +30,7 @@
namespace olive
{
ExportSavePresetDialog::ExportSavePresetDialog(const EncodingParams &p,
ExportSavePresetDialog::ExportSavePresetDialog(const OakEngineEncodingParams *p,
QWidget *parent)
: QDialog(parent)
, params_(p)
@@ -39,7 +40,17 @@ ExportSavePresetDialog::ExportSavePresetDialog(const EncodingParams &p,
name_edit_ = new QLineEdit();
// Populate existing list
QStringList l = EncodingParams::get_list_of_presets();
QStringList l;
{
const int n = oakengine_encoding_preset_count();
for (int i = 0; i < n; i++) {
char name_buf[256];
if (oakengine_encoding_preset_name(
i, name_buf, static_cast<int>(sizeof(name_buf))) > 0) {
l.append(QString::fromUtf8(name_buf));
}
}
}
if (!l.empty()) {
auto list_widget = new QListWidget();
for (const QString &f : l) {
@@ -78,13 +89,17 @@ void ExportSavePresetDialog::accept()
return;
}
QDir d(EncodingParams::get_preset_path());
char preset_path_buf[1024];
preset_path_buf[0] = '\0';
oakengine_encoding_preset_path(
preset_path_buf, static_cast<int>(sizeof(preset_path_buf)));
QDir d(QString::fromUtf8(preset_path_buf));
if (!d.exists()) {
d.mkpath(QStringLiteral("."));
}
QFile f(d.filePath(name_edit_->text()));
if (f.exists()) {
if (d.exists(name_edit_->text())) {
if (QMessageBox::question(
this, tr("Overwrite Preset"),
tr("A preset with the name \"%1\" already exists. Do you wish to overwrite it?")
@@ -94,17 +109,18 @@ void ExportSavePresetDialog::accept()
}
}
if (!f.open(QFile::WriteOnly)) {
const QByteArray full_path =
d.filePath(name_edit_->text()).toUtf8();
const int rc = oakengine_encoding_params_save_file(
params_, full_path.constData());
if (rc != OAKENGINE_OK) {
QMessageBox::critical(
this, tr("Write Error"),
tr("Failed to open file \"%1\" for writing.").arg(f.fileName()));
tr("Failed to save preset to \"%1\".").arg(
QString::fromUtf8(full_path)));
return;
}
params_.save(&f);
f.close();
QDialog::accept();
}
+3 -3
View File
@@ -26,7 +26,7 @@
#include <QLineEdit>
#include <QListWidget>
#include "codec/encoder.h"
#include "oakengine/encoding.h"
namespace olive
{
@@ -34,7 +34,7 @@ namespace olive
class ExportSavePresetDialog : public QDialog {
Q_OBJECT
public:
ExportSavePresetDialog(const EncodingParams &p, QWidget *parent = nullptr);
ExportSavePresetDialog(const OakEngineEncodingParams *p, QWidget *parent = nullptr);
QString get_selected_preset_name() const
{
@@ -47,7 +47,7 @@ public slots:
private:
QLineEdit *name_edit_;
EncodingParams params_;
const OakEngineEncodingParams *params_;
};
}
+18 -31
View File
@@ -1,25 +1,9 @@
/*
* Oak Video Editor - Non-Linear Video Editor
* Copyright (C) 2025 Olive CE Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "exportsubtitlestab.h"
#include <QGridLayout>
#include "oakengine/encoding.h"
namespace olive
{
@@ -62,32 +46,35 @@ ExportSubtitlesTab::ExportSubtitlesTab(QWidget *parent)
&QWidget::setVisible);
}
int ExportSubtitlesTab::set_format(ExportFormat::Format format)
int ExportSubtitlesTab::set_format(int format)
{
auto vcodecs = ExportFormat::get_video_codecs(format);
auto acodecs = ExportFormat::get_audio_codecs(format);
const bool has_video = oakengine_encoding_format_video_codec_count(format) > 0;
const bool has_audio = oakengine_encoding_format_audio_codec_count(format) > 0;
int scodec_count = oakengine_encoding_format_subtitle_codec_count(format);
auto scodecs = ExportFormat::get_subtitle_codecs(format);
if (!scodecs.empty() && vcodecs.empty() && acodecs.empty()) {
if (scodec_count > 0 && !has_video && !has_audio) {
// If format supports ONLY scodecs, default this to off and disable it
sidecar_checkbox_->setChecked(false);
sidecar_checkbox_->setEnabled(false);
} else {
// If format does not support scodecs, default this to checked and disable it
sidecar_checkbox_->setChecked(scodecs.empty());
sidecar_checkbox_->setEnabled(!scodecs.empty());
sidecar_checkbox_->setChecked(scodec_count == 0);
sidecar_checkbox_->setEnabled(scodec_count > 0);
}
scodecs =
ExportFormat::get_subtitle_codecs(sidecar_format_combobox_->get_format());
// Refresh for sidecar format
int sidecar_fmt = sidecar_format_combobox_->get_format();
scodec_count = oakengine_encoding_format_subtitle_codec_count(sidecar_fmt);
codec_combobox_->clear();
foreach (ExportCodec::Codec scodec, scodecs) {
codec_combobox_->addItem(ExportCodec::get_codec_name(scodec), scodec);
for (int i = 0; i < scodec_count; i++) {
int scodec = oakengine_encoding_format_subtitle_codec_at(sidecar_fmt, i);
char buf[256];
oakengine_encoding_codec_name(scodec, buf, sizeof(buf));
codec_combobox_->addItem(QString::fromUtf8(buf), scodec);
}
return scodecs.size();
return scodec_count;
}
}
+6 -8
View File
@@ -26,7 +26,6 @@
#include <QComboBox>
#include <QLabel>
#include "codec/exportformat.h"
#include "common/qtutils.h"
#include "dialog/export/exportformatcombobox.h"
@@ -47,24 +46,23 @@ public:
sidecar_checkbox_->setChecked(e);
}
ExportFormat::Format get_sidecar_format() const
int get_sidecar_format() const
{
return sidecar_format_combobox_->get_format();
}
void set_sidecar_format(ExportFormat::Format f)
void set_sidecar_format(int f)
{
sidecar_format_combobox_->set_format(f);
}
int set_format(ExportFormat::Format format);
int set_format(int format);
ExportCodec::Codec get_subtitle_codec()
int get_subtitle_codec()
{
return static_cast<ExportCodec::Codec>(
codec_combobox_->currentData().toInt());
return codec_combobox_->currentData().toInt();
}
void set_subtitle_codec(ExportCodec::Codec c)
void set_subtitle_codec(int c)
{
QtUtils::set_combo_box_data(codec_combobox_, c);
}
+37 -23
View File
@@ -29,15 +29,16 @@
#include "exportadvancedvideodialog.h"
#include "node/color/colormanager/colormanager.h"
#include "oakengine/encoding.h"
namespace olive
{
ExportVideoTab::ExportVideoTab(ColorManager *color_manager, QWidget *parent)
ExportVideoTab::ExportVideoTab(OakEngineColorManager *color_manager, QWidget *parent)
: QWidget(parent)
, color_manager_(color_manager)
, threads_(0)
, color_range_(VideoParams::k_color_range_default)
, color_range_(0) // k_color_range_default
{
QVBoxLayout *outer_layout = new QVBoxLayout(this);
@@ -50,17 +51,20 @@ ExportVideoTab::ExportVideoTab(ColorManager *color_manager, QWidget *parent)
outer_layout->addStretch();
}
int ExportVideoTab::set_format(ExportFormat::Format format)
int ExportVideoTab::set_format(int format)
{
format_ = format;
QList<ExportCodec::Codec> vcodecs = ExportFormat::get_video_codecs(format);
setEnabled(!vcodecs.isEmpty());
const int vcodec_count = oakengine_encoding_format_video_codec_count(format);
setEnabled(vcodec_count > 0);
codec_combobox()->clear();
foreach (ExportCodec::Codec vcodec, vcodecs) {
codec_combobox()->addItem(ExportCodec::get_codec_name(vcodec), vcodec);
for (int i = 0; i < vcodec_count; i++) {
int vcodec = oakengine_encoding_format_video_codec_at(format, i);
char buf[256];
oakengine_encoding_codec_name(vcodec, buf, sizeof(buf));
codec_combobox()->addItem(QString::fromUtf8(buf), vcodec);
}
return vcodecs.size();
return vcodec_count;
}
bool ExportVideoTab::is_image_sequence_set() const
@@ -116,9 +120,9 @@ QWidget *ExportVideoTab::setup_resolution_section()
scaling_method_combobox_ = new QComboBox();
scaling_method_combobox_->setEnabled(false);
scaling_method_combobox_->addItem(tr("Fit"), EncodingParams::k_fit);
scaling_method_combobox_->addItem(tr("Stretch"), EncodingParams::k_stretch);
scaling_method_combobox_->addItem(tr("Crop"), EncodingParams::k_crop);
scaling_method_combobox_->addItem(tr("Fit"), OAKENGINE_ENCODING_SCALING_FIT);
scaling_method_combobox_->addItem(tr("Stretch"), OAKENGINE_ENCODING_SCALING_STRETCH);
scaling_method_combobox_->addItem(tr("Crop"), OAKENGINE_ENCODING_SCALING_CROP);
layout->addWidget(scaling_method_combobox_, row, 1);
// Automatically enable/disable the scaling method depending on maintain aspect ratio
@@ -223,9 +227,14 @@ void ExportVideoTab::maintain_aspect_ratio_changed(bool val)
void ExportVideoTab::open_advanced_dialog()
{
// Find export formats compatible with this encoder
QStringList pixel_formats =
ExportFormat::get_pixel_formats_for_codec(format_, get_selected_codec());
// Find pixel formats compatible with this encoder
QStringList pixel_formats;
const int pix_count = oakengine_encoding_pix_fmt_count(format_, get_selected_codec());
for (int i = 0; i < pix_count; i++) {
char buf[64];
oakengine_encoding_pix_fmt_at(format_, get_selected_codec(), i, buf, sizeof(buf));
pixel_formats.append(QString::fromUtf8(buf));
}
ExportAdvancedVideoDialog d(pixel_formats, this);
@@ -256,30 +265,35 @@ void ExportVideoTab::update_frame_rate(Rational r)
void ExportVideoTab::video_codec_changed()
{
ExportCodec::Codec codec = get_selected_codec();
int codec = get_selected_codec();
switch (codec) {
case ExportCodec::k_codec_h264:
case ExportCodec::k_codec_h264rgb:
case OAKENGINE_ENCODING_CODEC_H264:
case OAKENGINE_ENCODING_CODEC_H264RGB:
set_codec_section(h264_section_);
break;
case ExportCodec::k_codec_h265:
case OAKENGINE_ENCODING_CODEC_H265:
set_codec_section(h265_section_);
break;
case ExportCodec::k_codec_a_v1:
case OAKENGINE_ENCODING_CODEC_AV1:
set_codec_section(av1_section_);
break;
case ExportCodec::k_codec_cineform:
case OAKENGINE_ENCODING_CODEC_CINEFORM:
set_codec_section(cineform_section_);
break;
default:
set_codec_section(
ExportCodec::is_codec_a_still_image(codec) ? image_section_ : nullptr);
oakengine_encoding_codec_is_still_image(codec) ? image_section_ : nullptr);
}
// Set default pixel format
QStringList pix_fmts =
ExportFormat::get_pixel_formats_for_codec(format_, codec);
QStringList pix_fmts;
const int pix_count = oakengine_encoding_pix_fmt_count(format_, codec);
for (int i = 0; i < pix_count; i++) {
char buf[64];
oakengine_encoding_pix_fmt_at(format_, codec, i, buf, sizeof(buf));
pix_fmts.append(QString::fromUtf8(buf));
}
if (!pix_fmts.isEmpty()) {
pix_fmt_ = pix_fmts.first();
} else {
+12 -12
View File
@@ -32,8 +32,9 @@
#include "dialog/export/codec/codecstack.h"
#include "dialog/export/codec/h264section.h"
#include "dialog/export/codec/imagesection.h"
#include "node/color/colormanager/colormanager.h"
#include "oakengine/color.h"
#include "widget/colorwheel/colorspacechooser.h"
#include "widget/manageddisplay/colorprocessorhandle.h"
#include "widget/slider/integerslider.h"
#include "widget/standardcombos/standardcombos.h"
@@ -43,9 +44,9 @@ namespace olive
class ExportVideoTab : public QWidget {
Q_OBJECT
public:
ExportVideoTab(ColorManager *color_manager, QWidget *parent = nullptr);
ExportVideoTab(OakEngineColorManager *color_manager, QWidget *parent = nullptr);
int set_format(ExportFormat::Format format);
int set_format(int format);
bool is_image_sequence_set() const;
void set_image_sequence(bool e) const;
@@ -55,13 +56,12 @@ public:
return image_section_->get_time();
}
ExportCodec::Codec get_selected_codec() const
int get_selected_codec() const
{
return static_cast<ExportCodec::Codec>(
codec_combobox()->currentData().toInt());
return codec_combobox()->currentData().toInt();
}
void set_selected_codec(ExportCodec::Codec c)
void set_selected_codec(int c)
{
QtUtils::set_combo_box_data(codec_combobox(), c);
}
@@ -161,11 +161,11 @@ public:
pix_fmt_ = s;
}
VideoParams::ColorRange color_range() const
int color_range() const
{
return color_range_;
}
void set_color_range(VideoParams::ColorRange c)
void set_color_range(int c)
{
color_range_ = c;
}
@@ -204,7 +204,7 @@ private:
IntegerSlider *width_slider_;
IntegerSlider *height_slider_;
ColorManager *color_manager_;
OakEngineColorManager *color_manager_;
InterlacedComboBox *interlaced_combobox_;
PixelAspectRatioComboBox *pixel_aspect_combobox_;
@@ -213,9 +213,9 @@ private:
int threads_;
QString pix_fmt_;
VideoParams::ColorRange color_range_;
int color_range_;
ExportFormat::Format format_;
int format_;
private slots:
void maintain_aspect_ratio_changed(bool val);
@@ -34,11 +34,13 @@
#include <QSpinBox>
#include "core.h"
#include "node/nodeundo.h"
#include "oakengine/footage.h"
#include "oakengine/node.h"
#include "oakengine/timeline.h"
#include "oakengine/undo.h"
#include "streamproperties/audiostreamproperties.h"
#include "streamproperties/videostreamproperties.h"
#include "widget/viewer/vieweroutpututils.h"
namespace olive
{
@@ -131,28 +133,43 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
QString description;
bool is_enabled = false;
OakEngineFootage *facade_handle = oakengine_footage_borrow(
reinterpret_cast<OakEngineNode *>(footage_));
switch (reference.type()) {
case Track::k_video: {
stacked_widget_->addWidget(
new VideoStreamProperties(footage_, reference.index()));
VideoParams vp = footage_->get_video_params(reference.index());
VideoParams vp = viewer_output_video_params(footage_, reference.index());
is_enabled = vp.enabled();
description = Footage::describe_video_stream(vp);
{
char desc_buf[256];
oakengine_footage_describe_video_stream(
facade_handle, reference.index(), desc_buf,
sizeof(desc_buf));
description = QString::fromUtf8(desc_buf);
}
break;
}
case Track::k_audio: {
stacked_widget_->addWidget(
new AudioStreamProperties(footage_, reference.index()));
AudioParams ap = footage_->get_audio_params(reference.index());
AudioParams ap = viewer_output_audio_params(footage_, reference.index());
is_enabled = ap.enabled();
description = Footage::describe_audio_stream(ap);
{
char desc_buf[256];
oakengine_footage_describe_audio_stream(
facade_handle, reference.index(), desc_buf,
sizeof(desc_buf));
description = QString::fromUtf8(desc_buf);
}
break;
}
case Track::k_subtitle: {
SubtitleParams sp = footage_->get_subtitle_params(reference.index());
is_enabled = sp.enabled();
is_enabled = oakengine_footage_get_stream_enabled(
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference.index());
// FIXME: Language?
description = tr("Subtitles");
@@ -164,6 +181,8 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
break;
}
oakengine_footage_free(facade_handle);
QListWidgetItem *item = new QListWidgetItem(description, track_list_);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(is_enabled ? Qt::Checked : Qt::Unchecked);
@@ -244,16 +263,16 @@ void FootagePropertiesDialog::accept()
switch (reference.type()) {
case Track::k_video:
old_stream_enabled =
footage_->get_video_params(reference.index()).enabled();
old_stream_enabled = oakengine_footage_get_stream_enabled(
facade_handle, OAKENGINE_TRACK_TYPE_VIDEO, reference.index());
break;
case Track::k_audio:
old_stream_enabled =
footage_->get_audio_params(reference.index()).enabled();
old_stream_enabled = oakengine_footage_get_stream_enabled(
facade_handle, OAKENGINE_TRACK_TYPE_AUDIO, reference.index());
break;
case Track::k_subtitle:
old_stream_enabled =
footage_->get_subtitle_params(reference.index()).enabled();
old_stream_enabled = oakengine_footage_get_stream_enabled(
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference.index());
break;
case Track::k_none:
case Track::k_count:
@@ -269,12 +288,12 @@ void FootagePropertiesDialog::accept()
oakengine_footage_free(facade_handle);
MultiUndoCommand *command = new MultiUndoCommand();
void *command = oakengine_undo_command_create_multi();
for (int i = 0; i < stacked_widget_->count(); i++) {
static_cast<StreamProperties *>(stacked_widget_->widget(i))
->accept(command);
}
delete command; // stream pages write through the facade directly
oakengine_undo_command_free(command); // stream pages write through the facade directly
QDialog::accept();
}
@@ -31,7 +31,6 @@
#include <QStackedWidget>
#include "node/project/footage/footage.h"
#include "undo/undocommand.h"
namespace olive
{
@@ -30,7 +30,7 @@ AudioStreamProperties::AudioStreamProperties(Footage *footage, int audio_index)
{
}
void AudioStreamProperties::accept(MultiUndoCommand *)
void AudioStreamProperties::accept(void *)
{
Q_UNUSED(footage_)
Q_UNUSED(audio_index_)
@@ -32,7 +32,7 @@ class AudioStreamProperties : public StreamProperties {
public:
AudioStreamProperties(Footage *footage, int audio_index);
virtual void accept(MultiUndoCommand *parent) override;
virtual void accept(void *parent) override;
private:
Footage *footage_;
@@ -25,7 +25,6 @@
#include <QWidget>
#include "common/define.h"
#include "undo/undocommand.h"
namespace olive
{
@@ -34,7 +33,7 @@ class StreamProperties : public QWidget {
public:
StreamProperties(QWidget *parent = nullptr);
virtual void accept(MultiUndoCommand *)
virtual void accept(void *)
{
}
@@ -28,8 +28,12 @@
#include <QMessageBox>
#include "node/project.h"
#include "oakengine/color.h"
#include "widget/manageddisplay/colorprocessorhandle.h"
#include "oakengine/footage.h"
#include "oakengine/node.h"
#include "oakengine/viewer.h"
#include "oakengine/videoparams.h"
namespace olive
{
@@ -46,7 +50,10 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0);
VideoParams vp = footage_->get_video_params(video_index_);
oak_video_params vpod;
oakengine_viewer_get_video_params(
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
&vpod);
// Stream override values come through the liboakengine C ABI facade;
// layout-only conditions (channel count, video type) stay direct reads.
@@ -85,10 +92,13 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
// The dropdown's color space list comes through the facade (same list
// the engine's color config reports).
OakEngineColorManager *cm = oakengine_color_manager_from_project(
reinterpret_cast<OakEngineProject *>(footage_->project()));
video_color_space_->addItem(tr("Default (%1)")
.arg(footage_->project()
->color_manager()
->get_default_input_color_space()));
.arg(oak_query_string([cm](char *buf, int size) {
return oakengine_color_manager_default_input_color_space(
cm, buf, size);
})));
const int colorspace_count =
oakengine_footage_colorspace_count(facade_handle);
@@ -110,14 +120,14 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
color_range_combo_ = new QComboBox();
color_range_combo_->addItem(tr("Limited (16-235)"),
VideoParams::k_color_range_limited);
0);
color_range_combo_->addItem(tr("Full (0-255)"),
VideoParams::k_color_range_full);
1);
color_range_combo_->setCurrentIndex(color_range);
video_layout->addWidget(color_range_combo_, row, 1);
if (vp.channel_count() == VideoParams::k_rgba_channel_count) {
if (oakengine_video_params_internal_channel_count() == 4) {
row++;
video_premultiply_alpha_ = new QCheckBox(tr("Premultiplied Alpha"));
@@ -127,7 +137,7 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
row++;
if (vp.video_type() == VideoParams::k_video_type_image_sequence) {
if (vpod.video_type == 2) {
QGroupBox *imgseq_group = new QGroupBox(tr("Image Sequence"));
QGridLayout *imgseq_layout = new QGridLayout(imgseq_group);
@@ -169,7 +179,7 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
oakengine_footage_free(facade_handle);
}
void VideoStreamProperties::accept(MultiUndoCommand *parent)
void VideoStreamProperties::accept(void *parent)
{
Q_UNUSED(parent)
@@ -182,17 +192,40 @@ void VideoStreamProperties::accept(MultiUndoCommand *parent)
set_colorspace = video_color_space_->currentText();
}
VideoParams vp = footage_->get_video_params(video_index_);
// Fetch current values through the facade (avoids the inline
// ViewerOutput::get_video_params() which references k_video_params_input).
char vp_colorspace[256];
vp_colorspace[0] = '\0';
int vp_color_range = 0, vp_interlacing = 0, vp_premultiplied = 0;
oakengine_footage_get_video_stream_overrides(
facade_handle, video_index_, vp_colorspace, sizeof(vp_colorspace),
&vp_color_range, &vp_interlacing, &vp_premultiplied);
int vp_par_num = 1, vp_par_den = 1;
oakengine_footage_get_pixel_aspect(facade_handle, video_index_,
&vp_par_num, &vp_par_den);
oak_video_params vpod;
oakengine_viewer_get_video_params(
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
&vpod);
int64_t vp_start_time = 0, vp_duration = 0;
int vp_fr_num = 0, vp_fr_den = 1;
oakengine_footage_get_image_sequence_params(
facade_handle, video_index_, &vp_start_time, &vp_duration,
&vp_fr_num, &vp_fr_den);
// Write every override through the facade (each call is one undoable
// command on the shared undo stack, replacing this dialog's own undo
// command classes with identical semantics).
if ((video_premultiply_alpha_ &&
video_premultiply_alpha_->isChecked() != vp.premultiplied_alpha()) ||
set_colorspace != vp.colorspace() ||
video_premultiply_alpha_->isChecked() != (vp_premultiplied != 0)) ||
set_colorspace != QString::fromUtf8(vp_colorspace) ||
static_cast<VideoParams::Interlacing>(
video_interlace_combo_->currentIndex()) != vp.interlacing() ||
color_range_combo_->currentData().toInt() != vp.color_range()) {
video_interlace_combo_->currentIndex()) !=
static_cast<VideoParams::Interlacing>(vp_interlacing) ||
color_range_combo_->currentData().toInt() != vp_color_range) {
oakengine_footage_set_video_stream_overrides(
facade_handle, video_index_,
set_colorspace.toUtf8().constData(),
@@ -204,19 +237,19 @@ void VideoStreamProperties::accept(MultiUndoCommand *parent)
}
const Rational new_par = pixel_aspect_combo_->get_pixel_aspect_ratio();
if (new_par != vp.pixel_aspect_ratio()) {
if (new_par != Rational(vp_par_num, vp_par_den)) {
oakengine_footage_set_pixel_aspect(facade_handle, video_index_,
new_par.numerator(),
new_par.denominator());
}
if (vp.video_type() == VideoParams::k_video_type_image_sequence) {
if (vpod.video_type == 2) {
int64_t new_dur =
imgseq_end_time_->get_value() - imgseq_start_time_->get_value() + 1;
if (vp.start_time() != imgseq_start_time_->get_value() ||
vp.duration() != new_dur ||
vp.frame_rate() != imgseq_frame_rate_->get_frame_rate()) {
if (vp_start_time != imgseq_start_time_->get_value() ||
vp_duration != new_dur ||
Rational(vp_fr_num, vp_fr_den) != imgseq_frame_rate_->get_frame_rate()) {
const Rational fr = imgseq_frame_rate_->get_frame_rate();
oakengine_footage_set_image_sequence_params(
facade_handle, video_index_,
@@ -230,8 +263,11 @@ void VideoStreamProperties::accept(MultiUndoCommand *parent)
bool VideoStreamProperties::sanity_check()
{
if (footage_->get_video_params(video_index_).video_type() ==
VideoParams::k_video_type_image_sequence) {
oak_video_params vpod;
oakengine_viewer_get_video_params(
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
&vpod);
if (vpod.video_type == 2) {
if (imgseq_start_time_->get_value() >= imgseq_end_time_->get_value()) {
QMessageBox::critical(
this, tr("Invalid Configuration"),
@@ -38,7 +38,7 @@ class VideoStreamProperties : public StreamProperties {
public:
VideoStreamProperties(Footage *footage, int video_index);
virtual void accept(MultiUndoCommand *parent) override;
virtual void accept(void *parent) override;
virtual bool sanity_check() override;
@@ -183,7 +183,9 @@ void FootageRelinkDialog::browse_for_footage()
new_dir.filePath(relative_to_original);
if (QFileInfo::exists(absolute_to_new)) {
other_footage->set_filename(absolute_to_new);
oakengine_footage_relink(
reinterpret_cast<OakEngineFootage *>(other_footage),
absolute_to_new.toUtf8().constData());
}
}
}
@@ -29,6 +29,8 @@
#include "core.h"
#include "oakengine/timeline.h"
namespace olive
{
@@ -138,29 +140,29 @@ void MarkerPropertiesDialog::accept()
return;
}
MultiUndoCommand *command = new MultiUndoCommand();
int color = color_menu_->get_selected_color();
foreach (TimelineMarker *m, markers_) {
if (color != -1) {
command->add_child(new MarkerChangeColorCommand(m, color));
// Batch-set properties via facade (one undoable command)
{
QVector<OakEngineMarker *> oak_markers;
foreach (TimelineMarker *m, markers_) {
oak_markers.append(reinterpret_cast<OakEngineMarker *>(m));
}
int color = color_menu_->get_selected_color();
QByteArray name_ba;
const char *name = nullptr;
if (label_edit_->placeholderText().isEmpty()) {
command->add_child(
new MarkerChangeNameCommand(m, label_edit_->text()));
name_ba = label_edit_->text().toUtf8();
name = name_ba.constData();
}
oakengine_marker_set_properties(
oak_markers.data(), oak_markers.size(), color, name,
(markers_.size() == 1) ? 1 : 0,
in_slider_->get_value().numerator(),
in_slider_->get_value().denominator(),
out_slider_->get_value().numerator(),
out_slider_->get_value().denominator(),
nullptr);
}
if (markers_.size() == 1) {
command->add_child(new MarkerChangeTimeCommand(
markers_.front(),
TimeRange(in_slider_->get_value(), out_slider_->get_value())));
}
Core::instance()->undo_stack()->push(command, tr("Set Marker Properties"));
super::accept();
}
+2 -2
View File
@@ -26,7 +26,7 @@
#include <QSplitter>
#include <QVBoxLayout>
#include "config/config.h"
#include "oakengine/config.h"
#include "tabs/preferencesgeneraltab.h"
#include "tabs/preferencesbehaviortab.h"
#include "tabs/preferencesappearancetab.h"
@@ -69,7 +69,7 @@ PreferencesDialog::PreferencesDialog(MainWindow *main_window, int start_tab)
void PreferencesDialog::AcceptEvent()
{
Config::save();
oakengine_config_save();
}
}
@@ -28,6 +28,7 @@
#include <QLabel>
#include "node/node.h"
#include "oakengine/node.h"
namespace olive
{
@@ -69,8 +70,9 @@ PreferencesAppearanceTab::PreferencesAppearanceTab()
QGridLayout *color_layout = new QGridLayout(color_group);
for (int i = 0; i < Node::k_category_count; i++) {
QString cat_name =
Node::get_category_name(static_cast<Node::CategoryID>(i));
char cat_buf[256];
oakengine_node_category_name(i, cat_buf, sizeof(cat_buf));
QString cat_name = QString::fromUtf8(cat_buf);
color_layout->addWidget(new QLabel(cat_name), i, 0);
ColorCodingComboBox *ccc = new ColorCodingComboBox();
@@ -102,7 +104,7 @@ PreferencesAppearanceTab::PreferencesAppearanceTab()
layout->addStretch();
}
void PreferencesAppearanceTab::accept(MultiUndoCommand *command)
void PreferencesAppearanceTab::accept(void *command)
{
Q_UNUSED(command)
@@ -38,7 +38,7 @@ class PreferencesAppearanceTab : public ConfigDialogBaseTab {
public:
PreferencesAppearanceTab();
virtual void accept(MultiUndoCommand *command) override;
virtual void accept(void *command) override;
private:
/**
@@ -25,8 +25,9 @@
#include <QGroupBox>
#include <QLabel>
#include "audio/audiomanager.h"
#include "config/config.h"
#include "oakengine/audio.h"
#include <portaudio.h>
#include "common/configwrapper.h"
namespace olive
{
@@ -171,13 +172,13 @@ PreferencesAudioTab::PreferencesAudioTab()
new ExportFormatComboBox(ExportFormatComboBox::k_show_audio_only);
record_format_combo_->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
record_format_combo_->set_format(static_cast<ExportFormat::Format>(
record_format_combo_->set_format(static_cast<int>(
OAK_CONFIG("AudioRecordingFormat").toInt()));
fmt_layout->addWidget(record_format_combo_);
record_options_ = new ExportAudioTab();
record_options_->set_format(record_format_combo_->get_format());
record_options_->set_codec(static_cast<ExportCodec::Codec>(
record_options_->set_codec(static_cast<int>(
OAK_CONFIG("AudioRecordingCodec").toInt()));
record_options_->sample_rate_combobox()->set_sample_rate(
OAK_CONFIG("AudioRecordingSampleRate").toInt());
@@ -213,7 +214,7 @@ PreferencesAudioTab::PreferencesAudioTab()
refresh_backends();
}
void PreferencesAudioTab::accept(MultiUndoCommand *command)
void PreferencesAudioTab::accept(void *command)
{
Q_UNUSED(command)
@@ -228,8 +229,8 @@ void PreferencesAudioTab::accept(MultiUndoCommand *command)
OAK_CONFIG("AudioInput") = audio_input_devices_->currentText();
// Set devices to be used from now on
AudioManager::instance()->set_output_device(output_device);
AudioManager::instance()->set_input_device(input_device);
oakengine_audio_set_output_device(output_device);
oakengine_audio_set_input_device(input_device);
OAK_CONFIG("AudioOutputSampleRate") = output_rate_combo_->get_sample_rate();
OAK_CONFIG("AudioOutputChannelLayout") =
@@ -251,7 +252,8 @@ void PreferencesAudioTab::accept(MultiUndoCommand *command)
->get_sample_format()
.to_string());
emit AudioManager::instance() -> output_params_changed();
// AudioManager output params changed is handled internally by the facade
// when oakengine_audio_set_output_device() is called.
OAK_CONFIG("AudioScrubbing") = audio_scrubbing_->isChecked();
}
@@ -299,7 +301,7 @@ void PreferencesAudioTab::refresh_devices()
void PreferencesAudioTab::hard_refresh_backends()
{
AudioManager::instance()->hard_reset();
oakengine_audio_hard_reset();
refresh_backends();
}
@@ -307,9 +309,9 @@ void PreferencesAudioTab::attempt_to_set_devices_from_config()
{
// Load with currently active devices
PaDeviceIndex current_output_index =
AudioManager::instance()->get_output_device();
static_cast<PaDeviceIndex>(oakengine_audio_get_output_device());
PaDeviceIndex current_input_index =
AudioManager::instance()->get_input_device();
static_cast<PaDeviceIndex>(oakengine_audio_get_input_device());
const PaDeviceInfo *current_output = nullptr, *current_input = nullptr;
if (current_output_index != paNoDevice) {
@@ -40,7 +40,7 @@ class PreferencesAudioTab : public ConfigDialogBaseTab {
public:
PreferencesAudioTab();
virtual void accept(MultiUndoCommand *command) override;
virtual void accept(void *command) override;
private:
QComboBox *audio_backend_combobox_;
@@ -23,7 +23,7 @@
#include <QLabel>
#include "config/config.h"
#include "common/configwrapper.h"
namespace olive
{
@@ -114,7 +114,7 @@ PreferencesBehaviorTab::PreferencesBehaviorTab(Category category)
}
}
void PreferencesBehaviorTab::accept(MultiUndoCommand *command)
void PreferencesBehaviorTab::accept(void *command)
{
Q_UNUSED(command)
@@ -45,7 +45,7 @@ public:
PreferencesBehaviorTab(Category category);
virtual void accept(MultiUndoCommand *command) override;
virtual void accept(void *command) override;
static QString behavior_pref_tr(const char *text)
{
@@ -29,16 +29,24 @@
#include <QMessageBox>
#include "common/filefunctions.h"
#include "config/config.h"
#include "common/configwrapper.h"
#include "oakengine/disk.h"
#include "olive/core/core.h"
namespace olive
{
PreferencesDiskTab::PreferencesDiskTab()
{
// Get default disk cache folder
default_disk_cache_folder_ =
DiskManager::instance()->get_default_cache_folder();
// Get default disk cache folder path
{
int len = oakengine_disk_get_default_cache_path(nullptr, 0);
if (len > 0) {
QByteArray buf(len + 1, '\0');
oakengine_disk_get_default_cache_path(buf.data(), buf.size());
default_disk_cache_folder_ = QString::fromUtf8(buf.constData());
}
}
QVBoxLayout *outer_layout = new QVBoxLayout(this);
@@ -54,7 +62,7 @@ PreferencesDiskTab::PreferencesDiskTab()
row, 0);
disk_cache_location_ =
new PathWidget(default_disk_cache_folder_->get_path());
new PathWidget(default_disk_cache_folder_);
disk_management_layout->addWidget(disk_cache_location_, row, 1);
row++;
@@ -62,8 +70,8 @@ PreferencesDiskTab::PreferencesDiskTab()
QPushButton *disk_cache_settings_btn =
new QPushButton(tr("Disk Cache Settings"));
connect(disk_cache_settings_btn, &QPushButton::clicked, this, [this]() {
DiskManager::instance()->show_disk_cache_settings_dialog(
disk_cache_location_->text(), this);
oakengine_disk_show_settings_dialog(
disk_cache_location_->text().toUtf8().constData(), this);
});
disk_management_layout->addWidget(disk_cache_settings_btn, row, 1);
@@ -81,7 +89,7 @@ PreferencesDiskTab::PreferencesDiskTab()
cache_ahead_slider_->set_format(tr("%1 seconds"));
cache_ahead_slider_->set_minimum(0);
cache_ahead_slider_->set_value(
OAK_CONFIG("DiskCacheAhead").value<Rational>().to_double());
OAK_CONFIG("DiskCacheAhead").value<core::Rational>().to_double());
cache_behavior_layout->addWidget(cache_ahead_slider_, row, 1);
cache_behavior_layout->addWidget(new QLabel(tr("Cache Behind:")), row, 2);
@@ -90,7 +98,7 @@ PreferencesDiskTab::PreferencesDiskTab()
cache_behind_slider_->set_minimum(0);
cache_behind_slider_->set_format(tr("%1 seconds"));
cache_behind_slider_->set_value(
OAK_CONFIG("DiskCacheBehind").value<Rational>().to_double());
OAK_CONFIG("DiskCacheBehind").value<core::Rational>().to_double());
cache_behavior_layout->addWidget(cache_behind_slider_, row, 3);
row++;
@@ -171,11 +179,11 @@ PreferencesDiskTab::PreferencesDiskTab()
bool PreferencesDiskTab::validate()
{
if (disk_cache_location_->text() != default_disk_cache_folder_->get_path()) {
if (disk_cache_location_->text() != default_disk_cache_folder_) {
// Disk cache location is changing
// Check if the user is okay with invalidating the current cache
if (!DiskManager::show_disk_cache_change_confirmation_dialog(this)) {
if (!oakengine_disk_show_change_confirmation_dialog(this)) {
return false;
}
@@ -191,18 +199,19 @@ bool PreferencesDiskTab::validate()
return true;
}
void PreferencesDiskTab::accept(MultiUndoCommand *command)
void PreferencesDiskTab::accept(void *command)
{
Q_UNUSED(command)
if (disk_cache_location_->text() != default_disk_cache_folder_->get_path()) {
default_disk_cache_folder_->set_path(disk_cache_location_->text());
if (disk_cache_location_->text() != default_disk_cache_folder_) {
oakengine_disk_set_default_cache_path(
disk_cache_location_->text().toUtf8().constData());
}
OAK_CONFIG("DiskCacheBehind") = QVariant::fromValue(
Rational::from_double(cache_behind_slider_->get_value()));
core::Rational::from_double(cache_behind_slider_->get_value()));
OAK_CONFIG("DiskCacheAhead") = QVariant::fromValue(
Rational::from_double(cache_ahead_slider_->get_value()));
core::Rational::from_double(cache_ahead_slider_->get_value()));
OAK_CONFIG("ProxyWidth") =
static_cast<int>(proxy_width_slider_->get_value());
@@ -28,7 +28,7 @@
#include <QPushButton>
#include "dialog/configbase/configdialogbase.h"
#include "render/diskmanager.h"
#include "oakengine/disk.h"
#include "widget/slider/floatslider.h"
#include "widget/slider/integerslider.h"
#include "widget/path/pathwidget.h"
@@ -43,7 +43,7 @@ public:
virtual bool validate() override;
virtual void accept(MultiUndoCommand *command) override;
virtual void accept(void *command) override;
private:
PathWidget *disk_cache_location_;
@@ -52,7 +52,7 @@ private:
FloatSlider *cache_behind_slider_;
DiskCacheFolder *default_disk_cache_folder_;
QString default_disk_cache_folder_;
IntegerSlider *proxy_width_slider_;
IntegerSlider *proxy_height_slider_;
@@ -198,7 +198,7 @@ PreferencesGeneralTab::PreferencesGeneralTab()
layout->addStretch();
}
void PreferencesGeneralTab::accept(MultiUndoCommand *command)
void PreferencesGeneralTab::accept(void *command)
{
Q_UNUSED(command)
@@ -39,7 +39,7 @@ class PreferencesGeneralTab : public ConfigDialogBaseTab {
public:
PreferencesGeneralTab();
virtual void accept(MultiUndoCommand *command) override;
virtual void accept(void *command) override;
private:
void add_language(const QString &locale_name);
@@ -81,7 +81,7 @@ PreferencesKeyboardTab::PreferencesKeyboardTab(MainWindow *main_window)
setup_kbd_shortcuts(main_window_->menuBar());
}
void PreferencesKeyboardTab::accept(MultiUndoCommand *command)
void PreferencesKeyboardTab::accept(void *command)
{
Q_UNUSED(command)
@@ -38,7 +38,7 @@ class PreferencesKeyboardTab : public ConfigDialogBaseTab {
public:
PreferencesKeyboardTab(MainWindow *main_window);
virtual void accept(MultiUndoCommand *command) override;
virtual void accept(void *command) override;
private slots:
/**
@@ -26,8 +26,9 @@
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <vector>
#include "render/lutlibrary.h"
#include "oakengine/lut.h"
namespace olive
{
@@ -46,7 +47,16 @@ PreferencesLutTab::PreferencesLutTab()
"these locations when picking a LUT file.")));
library_dirs_list_ = new QListWidget();
library_dirs_list_->addItems(LUTLibrary::get_directories());
{
int dir_count = oakengine_lut_directory_count();
for (int i = 0; i < dir_count; i++) {
char buf[4096];
int len = oakengine_lut_directory_at(i, buf, sizeof(buf));
if (len > 0) {
library_dirs_list_->addItem(QString::fromUtf8(buf, len));
}
}
}
library_layout->addWidget(library_dirs_list_);
QHBoxLayout *button_layout = new QHBoxLayout();
@@ -74,7 +84,7 @@ PreferencesLutTab::PreferencesLutTab()
outer_layout->addStretch();
}
void PreferencesLutTab::accept(MultiUndoCommand *command)
void PreferencesLutTab::accept(void *command)
{
Q_UNUSED(command)
@@ -83,7 +93,14 @@ void PreferencesLutTab::accept(MultiUndoCommand *command)
dirs.append(library_dirs_list_->item(i)->text());
}
LUTLibrary::set_directories(dirs);
std::vector<QByteArray> utf8_dirs;
std::vector<const char*> cstr_dirs;
for (int i = 0; i < dirs.size(); i++) {
utf8_dirs.push_back(dirs[i].toUtf8());
cstr_dirs.push_back(utf8_dirs.back().constData());
}
oakengine_lut_set_directories(cstr_dirs.data(),
static_cast<int>(cstr_dirs.size()));
}
}
@@ -33,7 +33,7 @@ class PreferencesLutTab : public ConfigDialogBaseTab {
public:
PreferencesLutTab();
virtual void accept(MultiUndoCommand *command) override;
virtual void accept(void *command) override;
private:
QListWidget *library_dirs_list_;
@@ -31,8 +31,10 @@ PluginProgressDialogReporter::PluginProgressDialogReporter(
: dialog_(new ProgressDialog(message, title, nullptr))
{
dialog_->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog_, &ProgressDialog::cancelled, this,
&PluginProgressReporter::cancelled);
QObject::connect(dialog_, &ProgressDialog::cancelled, dialog_, [this]() {
cancelled_ = true;
set_cancelled();
});
}
PluginProgressDialogReporter::~PluginProgressDialogReporter()
@@ -40,7 +40,6 @@ class ProgressDialog;
* is destroyed by the engine with deleteLater().
*/
class PluginProgressDialogReporter : public plugin::PluginProgressReporter {
Q_OBJECT
public:
PluginProgressDialogReporter(const QString &message, const QString &title);
@@ -52,8 +51,11 @@ public:
virtual void close() override;
bool was_cancelled() const { return cancelled_; }
private:
QPointer<ProgressDialog> dialog_;
bool cancelled_ = false;
};
}
@@ -30,8 +30,10 @@
#include <QPushButton>
#include "common/filefunctions.h"
#include "node/color/colormanager/colormanager.h"
#include "render/diskmanager.h"
#include "oakengine/color.h"
#include "oakengine/disk.h"
#include "oakengine/project.h"
#include "widget/manageddisplay/colorprocessorhandle.h"
namespace olive
{
@@ -45,8 +47,12 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);
char name_buf[256];
oakengine_project_name(
reinterpret_cast<OakEngineProject *>(working_project_),
name_buf, sizeof(name_buf));
setWindowTitle(
tr("Project Properties for '%1'").arg(working_project_->name()));
tr("Project Properties for '%1'").arg(name_buf));
QTabWidget *tabs = new QTabWidget;
layout->addWidget(tabs);
@@ -85,7 +91,14 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
reference_space_->addItem(tr("Scene Linear"), ocio::ROLE_SCENE_LINEAR);
reference_space_->addItem(tr("Compositing Log"),
ocio::ROLE_COMPOSITING_LOG);
QtUtils::set_combo_box_data(reference_space_, p->get_color_reference_space());
QtUtils::set_combo_box_data(reference_space_,
[p]() -> QString {
char buf[256];
oakengine_project_get_color_reference_space(
reinterpret_cast<OakEngineProject *>(p),
buf, sizeof(buf));
return QString::fromUtf8(buf);
}());
color_layout->addWidget(reference_space_, row, 1, 1, 2);
row++;
@@ -95,8 +108,11 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
connect(browse_btn, &QPushButton::clicked, this,
&ProjectPropertiesDialog::browse_for_ocio_config);
ocio_filename_->setText(
working_project_->color_manager()->get_config_filename());
OakEngineColorManager *cm = oakengine_color_manager_from_project(
reinterpret_cast<OakEngineProject *>(working_project_));
ocio_filename_->setText(oak_query_string([cm](char *buf, int size) {
return oakengine_color_manager_get_config_filename(cm, buf, size);
}));
connect(ocio_filename_, &QLineEdit::textChanged, this,
&ProjectPropertiesDialog::ocio_filename_updated);
@@ -129,7 +145,14 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
// Create custom cache path widget
custom_cache_path_ =
new PathWidget(working_project_->get_custom_cache_path(), this);
new PathWidget(
[this]() -> QString {
char buf[4096];
oakengine_project_get_custom_cache_path(
reinterpret_cast<OakEngineProject *>(working_project_),
buf, sizeof(buf));
return QString::fromUtf8(buf);
}(), this);
custom_cache_path_->setEnabled(false);
cache_layout->addWidget(custom_cache_path_);
@@ -139,7 +162,8 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
&PathWidget::setEnabled);
// Check the radio button that should currently be active
disk_cache_radios_[working_project_->get_cache_location_setting()]
disk_cache_radios_[oakengine_project_get_cache_location_setting(
reinterpret_cast<OakEngineProject *>(working_project_))]
->setChecked(true);
// Add disk cache settings button
@@ -181,7 +205,13 @@ void ProjectPropertiesDialog::accept()
->isChecked()) {
// Ensure alongside project path is valid
if (!verify_path_and_warn_if_bad(
working_project_->get_cache_alongside_project_path())) {
[this]() -> QString {
char buf[4096];
oakengine_project_cache_alongside_path(
reinterpret_cast<OakEngineProject *>(working_project_),
buf, sizeof(buf));
return QString::fromUtf8(buf);
}())) {
return;
}
} else {
@@ -191,33 +221,55 @@ void ProjectPropertiesDialog::accept()
}
}
if (custom_cache_path_->text() != working_project_->get_custom_cache_path()) {
if (custom_cache_path_->text() !=
[this]() -> QString {
char buf[4096];
oakengine_project_get_custom_cache_path(
reinterpret_cast<OakEngineProject *>(working_project_),
buf, sizeof(buf));
return QString::fromUtf8(buf);
}()) {
// Check if the user is okay with invalidating the current cache
if (!DiskManager::show_disk_cache_change_confirmation_dialog(this)) {
if (!oakengine_disk_show_change_confirmation_dialog(this)) {
return;
}
working_project_->set_custom_cache_path(custom_cache_path_->text());
oakengine_project_set_custom_cache_path(
reinterpret_cast<OakEngineProject *>(working_project_),
custom_cache_path_->text().toUtf8().constData());
emit DiskManager::instance() -> invalidate_project(working_project_);
oakengine_disk_invalidate_project(
reinterpret_cast<OakEngineProject *>(working_project_));
}
// This should ripple changes throughout the graph/cache that the color config has changed, and
// therefore should be done after the cache path is changed
if (working_project_->color_manager()->get_config_filename() !=
ocio_filename_->text()) {
working_project_->color_manager()->set_config_filename(
ocio_filename_->text());
OakEngineColorManager *cm = oakengine_color_manager_from_project(
reinterpret_cast<OakEngineProject *>(working_project_));
QString old_config = oak_query_string([cm](char *buf, int size) {
return oakengine_color_manager_get_config_filename(cm, buf, size);
});
QString old_input_cs = oak_query_string([cm](char *buf, int size) {
return oakengine_color_manager_default_input_color_space(cm, buf, size);
});
if (old_config != ocio_filename_->text()) {
oakengine_color_manager_set_config_filename(
cm, ocio_filename_->text().toUtf8().constData());
}
if (working_project_->color_manager()->get_default_input_color_space() !=
default_input_colorspace_->currentText()) {
working_project_->color_manager()->set_default_input_color_space(
default_input_colorspace_->currentText());
if (old_input_cs != default_input_colorspace_->currentText()) {
oakengine_color_manager_set_default_input_color_space(
cm, default_input_colorspace_->currentText().toUtf8().constData());
}
if (working_project_->get_color_reference_space() !=
reference_space_->currentData().toString()) {
working_project_->set_color_reference_space(
reference_space_->currentData().toString());
if ([this]() -> QString {
char buf[256];
oakengine_project_get_color_reference_space(
reinterpret_cast<OakEngineProject *>(working_project_),
buf, sizeof(buf));
return QString::fromUtf8(buf);
}() != reference_space_->currentData().toString()) {
oakengine_project_set_color_reference_space(
reinterpret_cast<OakEngineProject *>(working_project_),
reference_space_->currentData().toString().toUtf8().constData());
}
super::accept();
@@ -253,50 +305,69 @@ void ProjectPropertiesDialog::ocio_filename_updated()
{
default_input_colorspace_->clear();
try {
ocio::ConstConfigRcPtr c;
OakEngineColorConfig *config = nullptr;
if (ocio_filename_->text().isEmpty()) {
c = ColorManager::get_default_config();
} else {
c = ColorManager::create_config_from_file(ocio_filename_->text());
}
if (ocio_filename_->text().isEmpty()) {
config = oakengine_color_config_load_default();
} else {
config = oakengine_color_config_load_file(
ocio_filename_->text().toUtf8().constData());
}
if (config) {
ocio_filename_->setStyleSheet(QString());
ocio_config_is_valid_ = true;
// List input color spaces
QStringList input_cs = ColorManager::list_available_colorspaces(c);
int cs_count = oakengine_color_config_colorspace_count(config);
OakEngineColorManager *cm = oakengine_color_manager_from_project(
reinterpret_cast<OakEngineProject *>(working_project_));
QString default_cs = oak_query_string([cm](char *buf, int size) {
return oakengine_color_manager_default_input_color_space(cm, buf,
size);
});
foreach (QString cs, input_cs) {
for (int i = 0; i < cs_count; i++) {
QString cs = oak_query_string([config, i](char *buf, int size) {
return oakengine_color_config_colorspace_at(config, i, buf,
size);
});
default_input_colorspace_->addItem(cs);
if (cs ==
working_project_->color_manager()->get_default_input_color_space()) {
if (cs == default_cs) {
default_input_colorspace_->setCurrentIndex(
default_input_colorspace_->count() - 1);
}
}
} catch (ocio::Exception &e) {
oakengine_color_config_free(config);
} else {
char err_buf[1024];
oakengine_color_last_error(err_buf, sizeof(err_buf));
ocio_config_is_valid_ = false;
ocio_filename_->setStyleSheet(
QStringLiteral("QLineEdit {color: red;}"));
ocio_config_error_ = e.what();
ocio_config_error_ = QString::fromUtf8(err_buf);
}
}
void ProjectPropertiesDialog::open_disk_cache_settings()
{
if (disk_cache_radios_[Project::k_cache_use_default_location]->isChecked()) {
DiskManager::instance()->show_disk_cache_settings_dialog(
DiskManager::instance()->get_default_cache_folder(), this);
oakengine_disk_show_settings_dialog(nullptr, this);
} else if (disk_cache_radios_[Project::k_cache_store_alongside_project]
->isChecked()) {
DiskManager::instance()->show_disk_cache_settings_dialog(
working_project_->get_cache_alongside_project_path(), this);
oakengine_disk_show_settings_dialog(
[this]() -> QString {
char buf[4096];
oakengine_project_cache_alongside_path(
reinterpret_cast<OakEngineProject *>(working_project_),
buf, sizeof(buf));
return QString::fromUtf8(buf);
}().toUtf8().constData(), this);
} else {
DiskManager::instance()->show_disk_cache_settings_dialog(
custom_cache_path_->text(), this);
oakengine_disk_show_settings_dialog(
custom_cache_path_->text().toUtf8().constData(), this);
}
}
+67 -32
View File
@@ -27,9 +27,11 @@
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <cstring>
#include "config/config.h"
#include "common/configwrapper.h"
#include "node/project.h"
#include "oakengine/project.h"
namespace olive
{
@@ -42,8 +44,8 @@ ProxyDialog::ProxyDialog(QWidget *parent, const QVector<Footage *> &footage)
{
setWindowTitle(tr("Proxy Settings"));
const ProxyManager::ProxyParams params =
ProxyManager::proxy_params_from_config();
oak_proxy_params params;
oakengine_proxy_params_from_config(&params);
QVBoxLayout *layout = new QVBoxLayout(this);
@@ -186,9 +188,12 @@ void ProxyDialog::accept()
if (!footage_.isEmpty()) {
for (Footage *item : footage_) {
if (custom_params_checkbox_->isChecked()) {
item->set_custom_proxy_params(current_params());
oak_proxy_params p = current_params();
oakengine_footage_set_custom_proxy_params(
reinterpret_cast<OakEngineFootage *>(item), &p);
} else {
item->clear_custom_proxy_params();
oakengine_footage_clear_custom_proxy_params(
reinterpret_cast<OakEngineFootage *>(item));
}
}
}
@@ -269,15 +274,18 @@ void ProxyDialog::set_f_fmpeg_path(const QString &path)
ffmpeg_path_edit_->setText(path);
}
ProxyManager::ProxyParams ProxyDialog::current_params() const
oak_proxy_params ProxyDialog::current_params() const
{
ProxyManager::ProxyParams params = ProxyManager::proxy_params_from_config();
oak_proxy_params params;
oakengine_proxy_params_from_config(&params);
params.width = static_cast<int>(width_slider_->get_value());
params.height = static_cast<int>(height_slider_->get_value());
params.divider = resolution_combo_->currentData().toInt();
params.crf = static_cast<int>(crf_slider_->get_value());
params.preset = preset_combo_->currentText();
params.include_audio = include_audio_checkbox_->isChecked();
strncpy(params.preset, preset_combo_->currentText().toUtf8().constData(),
sizeof(params.preset) - 1);
params.preset[sizeof(params.preset) - 1] = '\0';
params.include_audio = include_audio_checkbox_->isChecked() ? 1 : 0;
return params;
}
@@ -302,40 +310,60 @@ void ProxyDialog::refresh_footage_list()
for (const Footage *item : footage_) {
QTreeWidgetItem *tree_item = new QTreeWidgetItem(footage_tree_);
tree_item->setText(0, item->filename());
QString state = ProxyManager::proxy_state_to_string(item->proxy_state());
if (item->has_custom_proxy_params()) {
state = tr("%1 (custom settings)").arg(state);
{
char state_buf[256];
int state_len = oakengine_proxy_state_to_string(
item->proxy_state(), state_buf, sizeof(state_buf));
QString state = (state_len > 0)
? QString::fromUtf8(state_buf, state_len)
: QString();
if (item->has_custom_proxy_params()) {
state = tr("%1 (custom settings)").arg(state);
}
tree_item->setText(1, state);
}
tree_item->setText(1, state);
}
}
void ProxyDialog::generate_proxies()
{
if (!ProxyManager::instance()) {
qWarning() << "ProxyDialog::GenerateProxies: ProxyManager unavailable";
return;
}
for (Footage *item : footage_) {
const VideoParams video = item->get_first_enabled_video_stream();
if (!video.is_valid()) {
oak_video_params _vp;
oakengine_viewer_get_first_enabled_video_stream(
reinterpret_cast<OakEngineNode *>(item), &_vp);
if (!oakengine_video_params_is_valid(&_vp)) {
qWarning()
<< "ProxyDialog::GenerateProxies: skipping item with no valid video stream"
<< item->filename();
continue;
}
const ProxyManager::ProxyParams params =
custom_params_checkbox_->isChecked() ? current_params()
: item->get_effective_proxy_params();
const ProxyManager::Proxy proxy =
ProxyManager::instance()->get_or_start_proxy(
item->project()->cache_path(), item->filename(),
video.stream_index(), params);
item->set_proxy(proxy.filename, proxy.state, video.stream_index(),
params.version, true);
item->invalidate_all(Footage::k_filename_input);
oak_proxy_params params;
if (custom_params_checkbox_->isChecked()) {
params = current_params();
} else {
oakengine_footage_get_effective_proxy_params(
reinterpret_cast<OakEngineFootage *>(item), &params);
}
oak_proxy_result proxy;
char cache_buf[512];
oakengine_project_cache_path(
reinterpret_cast<OakEngineProject *>(item->project()),
cache_buf, sizeof(cache_buf));
int ret = oakengine_proxy_get_or_start(
cache_buf,
item->filename().toUtf8().constData(),
video.stream_index(), &params, &proxy);
if (ret != 0) {
qWarning() << "ProxyDialog::GenerateProxies: failed to get/start proxy for"
<< item->filename();
continue;
}
oakengine_footage_set_proxy(reinterpret_cast<OakEngineFootage *>(item),
proxy.filename, proxy.state,
video.stream_index(), 1, params.version);
oakengine_footage_invalidate(reinterpret_cast<OakEngineFootage *>(item));
}
refresh_footage_list();
@@ -349,9 +377,16 @@ void ProxyDialog::delete_proxies()
}
QFile::remove(item->proxy_path());
QFile::remove(ProxyManager::get_working_proxy_filename(item->proxy_path()));
item->clear_proxy();
item->invalidate_all(Footage::k_filename_input);
{
char wbuf[4096];
int wlen = oakengine_proxy_get_working_filename(
item->proxy_path().toUtf8().constData(), wbuf, sizeof(wbuf));
if (wlen > 0) {
QFile::remove(QString::fromUtf8(wbuf, wlen));
}
}
oakengine_footage_clear_proxy(reinterpret_cast<OakEngineFootage *>(item));
oakengine_footage_invalidate(reinterpret_cast<OakEngineFootage *>(item));
}
refresh_footage_list();
+5 -2
View File
@@ -25,7 +25,10 @@
#include <QLineEdit>
#include <QTreeWidget>
#include "codec/proxymanager.h"
#include "oakengine/footage.h"
#include "oakengine/proxy.h"
#include "oakengine/videoparams.h"
#include "oakengine/viewer.h"
#include "node/project/footage/footage.h"
#include "widget/slider/integerslider.h"
@@ -68,7 +71,7 @@ public:
void set_f_fmpeg_path(const QString &path);
private:
ProxyManager::ProxyParams current_params() const;
oak_proxy_params current_params() const;
void save_global_settings();
+3 -2
View File
@@ -30,11 +30,12 @@
#include <QSplitter>
#include <QVBoxLayout>
#include "config/config.h"
#include "common/configwrapper.h"
#include "common/qtutils.h"
#include "dialog/msgbox.h"
#include "oakengine/node.h"
#include "oakengine/timeline.h"
#include "oakengine/videoparams.h"
namespace olive
{
@@ -114,7 +115,7 @@ void SequenceDialog::accept()
return;
}
if (!VideoParams::format_is_float(
if (!oakengine_video_params_format_is_float(
parameter_tab_->get_selected_preview_format()) &&
!OAK_CONFIG("PreviewNonFloatDontAskAgain").toBool()) {
QMessageBox b(this);
@@ -24,6 +24,7 @@
#include <QVBoxLayout>
#include "oakengine/timeline.h"
#include "oakengine/videoparams.h"
namespace olive
{
@@ -122,8 +123,7 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence *sequence,
height_slider_->set_value(height);
framerate_combo_->set_frame_rate(Rational(fps_num, fps_den));
pixelaspect_combo_->set_pixel_aspect_ratio(Rational(par_num, par_den));
interlacing_combo_->set_interlace_mode(
static_cast<VideoParams::Interlacing>(interlacing));
interlacing_combo_->set_interlace_mode(interlacing);
preview_resolution_field_->set_divider(divider);
preview_format_field_->set_pixel_format(
static_cast<PixelFormat::Format>(format));
@@ -173,15 +173,14 @@ void SequenceDialogParameterTab::save_preset_clicked()
void SequenceDialogParameterTab::update_preview_resolution_label()
{
VideoParams test_param(get_selected_video_width(), get_selected_video_height(),
PixelFormat::invalid,
VideoParams::k_internal_channel_count, Rational(1),
VideoParams::k_interlace_none,
preview_resolution_field_->currentData().toInt());
int ew, eh;
oakengine_video_params_effective_size(
get_selected_video_width(), get_selected_video_height(),
preview_resolution_field_->currentData().toInt(), &ew, &eh);
preview_resolution_label_->setText(
tr("(%1x%2)").arg(QString::number(test_param.effective_width()),
QString::number(test_param.effective_height())));
tr("(%1x%2)").arg(QString::number(ew),
QString::number(eh)));
}
}
@@ -57,7 +57,7 @@ public:
return pixelaspect_combo_->get_pixel_aspect_ratio();
}
VideoParams::Interlacing get_selected_video_interlacing_mode() const
int get_selected_video_interlacing_mode() const
{
return interlacing_combo_->get_interlace_mode();
}
+30 -18
View File
@@ -30,8 +30,8 @@
#include <QTreeWidgetItem>
#include <QXmlStreamWriter>
#include "config/config.h"
#include "render/videoparams.h"
#include "common/configwrapper.h"
#include "oakengine/videoparams.h"
#include "ui/icons/icons.h"
#include "widget/menu/menu.h"
@@ -75,12 +75,12 @@ SequenceDialogPresetTab::SequenceDialogPresetTab(QWidget *parent)
preset_tree_->addTopLevelItem(
create_sd_preset_folder(tr("NTSC"), 720, 480, Rational(30000, 1001),
VideoParams::k_pixel_aspect_ntsc_standard,
VideoParams::k_pixel_aspect_ntsc_widescreen, 1));
Rational(8, 9),
Rational(32, 27), 1));
preset_tree_->addTopLevelItem(
create_sd_preset_folder(tr("PAL"), 720, 576, Rational(25, 1),
VideoParams::k_pixel_aspect_pal_standard,
VideoParams::k_pixel_aspect_pal_widescreen, 1));
Rational(16, 15),
Rational(64, 45), 1));
// Load custom presets
for (int i = 0; i < get_number_of_presets(); i++) {
@@ -118,32 +118,42 @@ SequenceDialogPresetTab::create_hd_preset_folder(const QString &name, int width,
add_standard_item(parent,
std::make_shared<SequencePreset>(
tr("%1 23.976 FPS").arg(name), width, height,
Rational(24000, 1001), VideoParams::k_pixel_aspect_square,
VideoParams::k_interlace_none, 48000, layout, divider,
Rational(24000, 1001),
Rational(1), // k_pixel_aspect_square
0, // k_interlace_none
48000, layout, divider,
default_format, default_autocache));
add_standard_item(parent,
std::make_shared<SequencePreset>(
tr("%1 25 FPS").arg(name), width, height,
Rational(25, 1), VideoParams::k_pixel_aspect_square,
VideoParams::k_interlace_none, 48000, layout, divider,
Rational(25, 1),
Rational(1), // k_pixel_aspect_square
0, // k_interlace_none
48000, layout, divider,
default_format, default_autocache));
add_standard_item(parent,
std::make_shared<SequencePreset>(
tr("%1 29.97 FPS").arg(name), width, height,
Rational(30000, 1001), VideoParams::k_pixel_aspect_square,
VideoParams::k_interlace_none, 48000, layout, divider,
Rational(30000, 1001),
Rational(1), // k_pixel_aspect_square
0, // k_interlace_none
48000, layout, divider,
default_format, default_autocache));
add_standard_item(parent,
std::make_shared<SequencePreset>(
tr("%1 50 FPS").arg(name), width, height,
Rational(50, 1), VideoParams::k_pixel_aspect_square,
VideoParams::k_interlace_none, 48000, layout, divider,
Rational(50, 1),
Rational(1), // k_pixel_aspect_square
0, // k_interlace_none
48000, layout, divider,
default_format, default_autocache));
add_standard_item(parent,
std::make_shared<SequencePreset>(
tr("%1 59.94 FPS").arg(name), width, height,
Rational(60000, 1001), VideoParams::k_pixel_aspect_square,
VideoParams::k_interlace_none, 48000, layout, divider,
Rational(60000, 1001),
Rational(1), // k_pixel_aspect_square
0, // k_interlace_none
48000, layout, divider,
default_format, default_autocache));
return parent;
}
@@ -161,12 +171,14 @@ QTreeWidgetItem *SequenceDialogPresetTab::create_sd_preset_folder(
add_standard_item(
parent, std::make_shared<SequencePreset>(
tr("%1 Standard").arg(name), width, height, frame_rate,
standard_par, VideoParams::k_interlaced_bottom_first, 48000,
standard_par, 2, // k_interlaced_bottom_first
48000,
layout, divider, default_format, default_autocache));
add_standard_item(
parent, std::make_shared<SequencePreset>(
tr("%1 Widescreen").arg(name), width, height, frame_rate,
wide_par, VideoParams::k_interlaced_bottom_first, 48000,
wide_par, 2, // k_interlaced_bottom_first
48000,
layout, divider, default_format, default_autocache));
return parent;
}
+4 -4
View File
@@ -38,7 +38,7 @@ public:
SequencePreset(const QString &name, int width, int height,
const Rational &frame_rate, const Rational &pixel_aspect,
VideoParams::Interlacing interlacing, int sample_rate,
int interlacing, int sample_rate,
uint64_t channel_layout, int preview_divider,
PixelFormat preview_format, bool preview_autocache)
: width_(width)
@@ -74,7 +74,7 @@ public:
reader->name() == QStringLiteral("interlacing_")) {
// "interlacing_" is the element name mistakenly written by
// older versions of Save(); accept it for backward compatibility
interlacing_ = static_cast<VideoParams::Interlacing>(
interlacing_ = static_cast<int>(
reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("samplerate")) {
sample_rate_ = reader->readElementText().toInt();
@@ -140,7 +140,7 @@ public:
return pixel_aspect_;
}
VideoParams::Interlacing interlacing() const
int interlacing() const
{
return interlacing_;
}
@@ -175,7 +175,7 @@ private:
int height_;
Rational frame_rate_;
Rational pixel_aspect_;
VideoParams::Interlacing interlacing_;
int interlacing_;
int sample_rate_;
uint64_t channel_layout_;
int preview_divider_;
+117 -55
View File
@@ -27,8 +27,15 @@
#include <QMessageBox>
#include "core.h"
#include "node/nodeundo.h"
#include "node/block/clip/clip.h"
#include "oakengine/timeline.h"
#include "oakengine/node.h"
#include "oakengine/undo.h"
#include "widget/timelinewidget/cliphandle.h"
#include "timeline/timelineundopointer.h"
#include "timeline/timelinecommon.h"
#include "timeline/timelineundopointer.h"
#include "timeline/timelineundoripple.h"
namespace olive
{
@@ -122,15 +129,15 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
layout->addWidget(btns);
// Determine which speed value to use
start_speed_ = clips.first()->speed();
start_speed_ = clip_speed(clips.first());
start_duration_ = clips.first()->length();
start_reverse_ = clips.first()->reverse();
start_maintain_audio_pitch_ = clips.first()->maintain_audio_pitch();
start_loop_ = int(clips.first()->loop_mode());
start_reverse_ = clip_is_reversed(clips.first());
start_maintain_audio_pitch_ = clip_maintain_audio_pitch(clips.first());
start_loop_ = clip_loop_mode(clips.first());
for (int i = 1; i < clips.size(); i++) {
ClipBlock *c = clips.at(i);
if (!qIsNaN(start_speed_) && !qFuzzyCompare(start_speed_, c->speed())) {
if (!qIsNaN(start_speed_) && !qFuzzyCompare(start_speed_, clip_speed(c))) {
// Speed differs per clip
start_speed_ = qSNaN();
}
@@ -141,8 +148,8 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
// Yes, in theory a bool should only ever be 0 or 1 anyway, but MSVC complained and it is
// *possible* that a bool could be something else, so this code is safer
int clip_reverse = c->reverse() ? 1 : 0;
int clip_maintain_pitch = c->maintain_audio_pitch() ? 1 : 0;
int clip_reverse = clip_is_reversed(c) ? 1 : 0;
int clip_maintain_pitch = clip_maintain_audio_pitch(c) ? 1 : 0;
if (start_reverse_ != -1 && clip_reverse != start_reverse_) {
start_reverse_ = -1;
}
@@ -151,7 +158,7 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
start_maintain_audio_pitch_ = -1;
}
if (start_loop_ != -1 && int(c->loop_mode()) != start_loop_) {
if (start_loop_ != -1 && clip_loop_mode(c) != start_loop_) {
start_loop_ = -1;
}
}
@@ -189,9 +196,40 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
void SpeedDurationDialog::accept()
{
MultiUndoCommand *command = new MultiUndoCommand();
// Collect all duration/speed changes into a single undo entry.
const QByteArray undo_name = tr("Speed/Duration").toUtf8();
oakengine_undo_group_begin(undo_name.constData());
// Set duration values
// Set speed values
if (speed_slider_->is_tristate()) {
if (link_box_->isChecked() && !dur_slider_->is_tristate()) {
// Automatically determine speed from duration
foreach (ClipBlock *c, clips_) {
double speed = get_speed_adjustment(clip_speed(c), c->length(),
dur_slider_->get_value());
oak_node_value val;
memset(&val, 0, sizeof(val));
val.type = OAK_NODE_VALUE_FLOAT;
val.f[0] = speed;
oakengine_node_set_input(
reinterpret_cast<OakEngineNode *>(c),
oakengine_clip_speed_input_id(), &val);
}
}
} else {
// Set speeds to value of slider
foreach (ClipBlock *c, clips_) {
oak_node_value val;
memset(&val, 0, sizeof(val));
val.type = OAK_NODE_VALUE_FLOAT;
val.f[0] = speed_slider_->get_value();
oakengine_node_set_input(
reinterpret_cast<OakEngineNode *>(c),
oakengine_clip_speed_input_id(), &val);
}
}
// Set duration values (undoable via facade)
TimelineRippleDeleteGapsAtRegionsCommand::RangeList ripple_ranges;
foreach (ClipBlock *c, clips_) {
@@ -199,7 +237,7 @@ void SpeedDurationDialog::accept()
if (dur_slider_->is_tristate()) {
if (link_box_->isChecked() && !speed_slider_->is_tristate()) {
proposed_length = get_length_adjustment(c->length(), c->speed(),
proposed_length = get_length_adjustment(c->length(), clip_speed(c),
speed_slider_->get_value(),
timebase_);
}
@@ -219,8 +257,17 @@ void SpeedDurationDialog::accept()
}
if (proposed_length != c->length()) {
command->add_child(new BlockTrimCommand(
c->track(), c, proposed_length, Timeline::k_trim_out));
// Trim the clip's out-point to the new length (one undoable child
// inside the group, kept as a direct C++ command because the dialog
// already works in Rational time and has the track available).
oakengine_undo_push(
oakengine_block_trim_command(
reinterpret_cast<void *>(c->track()),
reinterpret_cast<void *>(c),
proposed_length.numerator(),
proposed_length.denominator(),
olive::Timeline::k_trim_out, 0),
tr("Trim Clip").toUtf8().constData());
ripple_ranges.append(
{ c->track(),
TimeRange(c->in() + proposed_length, c->out()) });
@@ -228,70 +275,85 @@ void SpeedDurationDialog::accept()
}
}
if (ripple_box_->isChecked()) {
command->add_child(new TimelineRippleDeleteGapsAtRegionsCommand(
clips_.first()->track()->sequence(), ripple_ranges));
}
// Set speed values
if (speed_slider_->is_tristate()) {
if (link_box_->isChecked() && !dur_slider_->is_tristate()) {
// Automatically determine speed from duration
foreach (ClipBlock *c, clips_) {
command->add_child(new NodeParamSetStandardValueCommand(
NodeKeyframeTrackReference(
NodeInput(c, ClipBlock::k_speed_input)),
get_speed_adjustment(c->speed(), c->length(),
dur_slider_->get_value())));
if (ripple_box_->isChecked() && !ripple_ranges.isEmpty()) {
Sequence *seq = reinterpret_cast<Sequence *>(
oakengine_clip_get_sequence(
reinterpret_cast<OakEngineClip *>(clips_.first())));
if (seq) {
QVector<int64_t> range_in_ts;
QVector<int64_t> range_out_ts;
QVector<int> range_track_types;
QVector<int> range_track_indexes;
range_in_ts.reserve(ripple_ranges.size());
range_out_ts.reserve(ripple_ranges.size());
range_track_types.reserve(ripple_ranges.size());
range_track_indexes.reserve(ripple_ranges.size());
int tbn = 0, tbd = 0;
oakengine_node_frame_time_base(
reinterpret_cast<OakEngineNode *>(seq), &tbn, &tbd);
for (const auto &range : ripple_ranges) {
range_track_types.append(range.first->type());
range_track_indexes.append(range.first->index());
range_in_ts.append(olive::core::Timecode::time_to_timestamp(
range.second.in(), olive::Rational(tbn, tbd),
olive::core::Timecode::k_round));
range_out_ts.append(olive::core::Timecode::time_to_timestamp(
range.second.out(), olive::Rational(tbn, tbd),
olive::core::Timecode::k_round));
}
}
} else {
// Set speeds to value of slider
foreach (ClipBlock *c, clips_) {
command->add_child(new NodeParamSetStandardValueCommand(
NodeKeyframeTrackReference(NodeInput(c, ClipBlock::k_speed_input)),
speed_slider_->get_value()));
oakengine_undo_push(
oakengine_timeline_ripple_delete_gaps_command(
reinterpret_cast<void *>(seq),
range_in_ts.constData(), range_out_ts.constData(),
range_track_types.constData(),
range_track_indexes.constData(),
ripple_ranges.size()),
tr("Ripple Delete Gaps").toUtf8().constData());
}
}
// Set reverse values
if (!reverse_box_->isTristate()) {
foreach (ClipBlock *c, clips_) {
command->add_child(new NodeParamSetStandardValueCommand(
NodeKeyframeTrackReference(
NodeInput(c, ClipBlock::k_reverse_input)),
reverse_box_->isChecked()));
oak_node_value val;
memset(&val, 0, sizeof(val));
val.type = OAK_NODE_VALUE_BOOL;
val.num = reverse_box_->isChecked() ? 1 : 0;
oakengine_node_set_input(
reinterpret_cast<OakEngineNode *>(c),
oakengine_clip_reverse_input_id(), &val);
}
}
// Set reverse values
// Set maintain audio pitch values
if (!maintain_audio_pitch_box_->isTristate()) {
foreach (ClipBlock *c, clips_) {
command->add_child(new NodeParamSetStandardValueCommand(
NodeKeyframeTrackReference(
NodeInput(c, ClipBlock::k_maintain_audio_pitch_input)),
maintain_audio_pitch_box_->isChecked()));
oak_node_value val;
memset(&val, 0, sizeof(val));
val.type = OAK_NODE_VALUE_BOOL;
val.num = maintain_audio_pitch_box_->isChecked() ? 1 : 0;
oakengine_node_set_input(
reinterpret_cast<OakEngineNode *>(c),
oakengine_clip_maintain_audio_pitch_input_id(), &val);
}
}
if (loop_combo_->currentIndex() != -1) {
foreach (ClipBlock *c, clips_) {
command->add_child(new NodeParamSetStandardValueCommand(
NodeKeyframeTrackReference(
NodeInput(c, ClipBlock::k_loop_mode_input)),
loop_combo_->currentData()));
oak_node_value val;
memset(&val, 0, sizeof(val));
val.type = OAK_NODE_VALUE_INT;
val.num = loop_combo_->currentData().toInt();
oakengine_node_set_input(
reinterpret_cast<OakEngineNode *>(c),
oakengine_clip_loop_mode_input_id(), &val);
}
}
QString name = (clips_.size() > 1) ?
tr("Set %1 Clip Properties").arg(clips_.size()) :
tr("Set Clip \"%1\" Properties")
.arg(clips_.first()->get_label_or_name());
Core::instance()->undo_stack()->push(command, name);
oakengine_undo_group_end();
super::accept();
}
Rational SpeedDurationDialog::get_length_adjustment(
const Rational &original_length, double original_speed, double new_speed,
const Rational &timebase)
@@ -26,14 +26,13 @@
#include <QComboBox>
#include <QDialog>
#include "node/block/clip/clip.h"
#include "node/block/gap/gap.h"
#include "undo/undocommand.h"
#include "widget/slider/floatslider.h"
#include "widget/slider/rationalslider.h"
namespace olive
{
namespace olive {
class ClipBlock;
class SpeedDurationDialog : public QDialog {
Q_OBJECT
+32 -28
View File
@@ -24,29 +24,42 @@
#include <QFutureWatcher>
#include <QtConcurrent>
#include "oakengine/task.h"
namespace olive
{
#define super ProgressDialog
TaskDialog::TaskDialog(Task *task, const QString &title, QWidget *parent)
: super(task->get_title(), title, parent)
TaskDialog::TaskDialog(OakEngineTask *task, const QString &title, QWidget *parent)
: super([&]() {
char buf[512];
buf[0] = '\0';
oakengine_task_title(task, buf, sizeof(buf));
return QString::fromUtf8(buf);
}(), title, parent)
, task_(task)
, destroy_on_close_(true)
, already_shown_(false)
, task_finished_(false)
{
// Clear task when this dialog is destroyed
task_->setParent(this);
bridge_ = new EngineEventBridge(this);
bridge_->subscribe(task, OAKENGINE_EVENT_TASK_PROGRESS);
connect(bridge_, &EngineEventBridge::task_progress, this,
[this](OakEngineTask *, double progress) {
set_progress(progress);
}, Qt::QueuedConnection);
// Connect the save manager progress signal to the progress bar update on the dialog
connect(task_, &Task::progress_changed, this, &TaskDialog::set_progress,
Qt::QueuedConnection);
connect(this, &TaskDialog::cancelled, this, [this]() {
oakengine_task_cancel(task_);
}, Qt::DirectConnection);
}
// Connect cancel signal (must be a direct connection or it'll be queued after the task has
// already finished)
connect(this, &TaskDialog::cancelled, task_, &Task::Cancel,
Qt::DirectConnection);
TaskDialog::~TaskDialog()
{
if (task_) {
oakengine_task_free(task_);
}
}
void TaskDialog::showEvent(QShowEvent *e)
@@ -54,20 +67,15 @@ void TaskDialog::showEvent(QShowEvent *e)
super::showEvent(e);
if (!already_shown_) {
// Create watcher for when the task finishes
QFutureWatcher<bool> *task_watcher = new QFutureWatcher<bool>();
// Listen for when the task finishes
connect(task_watcher, &QFutureWatcher<bool>::finished, this,
&TaskDialog::task_finished, Qt::QueuedConnection);
// Run task in another thread with QtConcurrent
task_watcher->setFuture(
#if QT_VERSION_MAJOR >= 6
QtConcurrent::run(&Task::start, task_)
#else
QtConcurrent::run(task_, &Task::Start)
#endif
QtConcurrent::run([this]() -> bool {
return oakengine_task_start_sync(task_) == 1;
})
);
already_shown_ = true;
@@ -76,19 +84,12 @@ void TaskDialog::showEvent(QShowEvent *e)
void TaskDialog::closeEvent(QCloseEvent *e)
{
// Cancel task if it is running
task_->Cancel();
oakengine_task_cancel(task_);
// Standard close function
super::closeEvent(e);
// Reset shown
already_shown_ = false;
// Clean up this task and dialog, but only if the task has actually finished.
// If the user closes the window while the task is still running, deleting now
// would destroy the Task object out from under the worker thread and crash
// when the task later touches its own members (e.g. ExportTask::encoder_).
if (destroy_on_close_ && task_finished_) {
deleteLater();
}
@@ -104,7 +105,10 @@ void TaskDialog::task_finished()
if (task_watcher->result()) {
emit task_succeeded(task_);
} else {
show_error_message(tr("Task Failed"), task_->get_error());
char err[512];
err[0] = '\0';
oakengine_task_error(task_, err, sizeof(err));
show_error_message(tr("Task Failed"), QString::fromUtf8(err));
emit task_failed(task_);
}
+11 -21
View File
@@ -23,7 +23,8 @@
#define OAK_TASKDIALOG_H
#include "dialog/progress/progress.h"
#include "task/task.h"
#include "engineeventbridge.h"
#include "oakengine/task.h"
namespace olive
{
@@ -31,29 +32,16 @@ namespace olive
class TaskDialog : public ProgressDialog {
Q_OBJECT
public:
/**
* @brief TaskDialog Constructor
*
* Creates a TaskDialog. The TaskDialog takes ownership of the Task and will destroy it on close.
* Connect to the Task::Succeeded() if you want to retrieve information from the task before it
* gets destroyed.
*/
TaskDialog(Task *task, const QString &title, QWidget *parent = nullptr);
TaskDialog(OakEngineTask *task, const QString &title, QWidget *parent = nullptr);
~TaskDialog() override;
/**
* @brief Set whether TaskDialog should destroy itself (and the task) when it's closed
*
* This is TRUE by default.
*/
void set_destroy_on_close(bool e)
{
destroy_on_close_ = e;
}
/**
* @brief Returns this dialog's task
*/
Task *get_task() const
OakEngineTask *get_task() const
{
return task_;
}
@@ -64,12 +52,14 @@ protected:
virtual void closeEvent(QCloseEvent *e) override;
signals:
void task_succeeded(Task *task);
void task_succeeded(OakEngineTask *task);
void task_failed(Task *task);
void task_failed(OakEngineTask *task);
private:
Task *task_;
OakEngineTask *task_;
EngineEventBridge *bridge_ = nullptr;
bool destroy_on_close_;