R8: finish app/ pure C ABI migration (P3-P9) and make OTIO required
- app/ no longer includes engine C++ headers nor holds engine C++ types: engine access goes through the oakengine C ABI plus C++ wrappers (oakutil/oaknode.h, oakutil/oakvideo.h) and app-local mirror types (tooltypes, trackreferencehandle, timelinecommonapp, keyframetypes, subtitleapp, serializedlayoutinfoapp, nodevaluehandle, sliderdisplaytypeapp) - engine: new C ABI functions for block/track/clip/transition navigation and predicates, links, caches, waveform/playback, disk folder, sequence_track_list, node_free, footage_is_valid, block_get_track, get_brush; loadotio/saveotio ported to the current engine API - OTIO is now a required dependency: CI and CD build it on every platform, FindOpenTimelineIO fixed for OTIO 0.16/0.19 (the old deps include requirement silently disabled OTIO everywhere), runtime libraries are bundled into packages and copied next to macOS binaries (oak_copy_otio_runtime) - fix ProjectViewModel drag&drop mime read/write size mismatch (segfault) - unify color label naming (k_olive -> "Oak") in the app-side mirror - docs: OTIO required, FFmpeg minimum corrected to 6.0 (en/zh) - gtest suite: 1925 passed, 0 failed
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "core.h"
|
||||
#include "oakutil/filefunctions.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
@@ -187,13 +187,13 @@ QString ColorDialog::get_color_space_input() const
|
||||
return chooser_->input();
|
||||
}
|
||||
|
||||
ColorTransform ColorDialog::get_color_space_output() const
|
||||
oak::ColorTransform ColorDialog::get_color_space_output() const
|
||||
{
|
||||
return chooser_->output();
|
||||
}
|
||||
|
||||
void ColorDialog::color_space_changed(const QString &input,
|
||||
const ColorTransform &output)
|
||||
const oak::ColorTransform &output)
|
||||
{
|
||||
QByteArray ref_cs = oak_query_string([this](char *buf, int size) {
|
||||
return oakengine_color_manager_reference_color_space(
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
|
||||
QString get_color_space_input() const;
|
||||
|
||||
ColorTransform get_color_space_output() const;
|
||||
oak::ColorTransform get_color_space_output() const;
|
||||
|
||||
public slots:
|
||||
void set_color(const ManagedColor &c);
|
||||
@@ -91,7 +91,7 @@ private:
|
||||
ColorSwatchChooser *swatch_;
|
||||
|
||||
private slots:
|
||||
void color_space_changed(const QString &input, const ColorTransform &output);
|
||||
void color_space_changed(const QString &input, const oak::ColorTransform &output);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -27,11 +27,26 @@
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "oakengine/disk.h"
|
||||
#include "oakutil/define.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
|
||||
namespace
|
||||
{
|
||||
|
||||
/// DiskCacheFolder::get_path() through the C ABI (buf/size convention)
|
||||
QString disk_folder_path(const void *folder)
|
||||
{
|
||||
char buf[1024];
|
||||
buf[0] = '\0';
|
||||
oakengine_disk_folder_get_path(folder, buf, sizeof(buf));
|
||||
return QString::fromUtf8(buf);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DiskCacheDialog::DiskCacheDialog(void *folder, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, folder_(folder)
|
||||
{
|
||||
@@ -39,8 +54,9 @@ DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
|
||||
|
||||
int row = 0;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Disk Cache: %1").arg(folder->get_path())),
|
||||
row, 0, 1, 2);
|
||||
layout->addWidget(
|
||||
new QLabel(tr("Disk Cache: %1").arg(disk_folder_path(folder))), row,
|
||||
0, 1, 2);
|
||||
setWindowTitle(tr("Disk Cache Settings"));
|
||||
|
||||
row++;
|
||||
@@ -50,8 +66,9 @@ DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
|
||||
maximum_cache_slider_ = new FloatSlider();
|
||||
maximum_cache_slider_->set_format(tr("%1 GB"));
|
||||
maximum_cache_slider_->set_minimum(1.0);
|
||||
maximum_cache_slider_->set_value(static_cast<double>(folder->get_limit()) /
|
||||
static_cast<double>(k_bytes_in_gigabyte));
|
||||
// The folder limit is a byte count; the slider works in GB
|
||||
maximum_cache_slider_->set_value(oakengine_disk_folder_get_limit(folder) /
|
||||
static_cast<double>(k_bytes_in_gigabyte));
|
||||
layout->addWidget(maximum_cache_slider_, row, 1);
|
||||
|
||||
row++;
|
||||
@@ -66,7 +83,8 @@ DiskCacheDialog::DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent)
|
||||
|
||||
clear_disk_cache_ =
|
||||
new QCheckBox(tr("Automatically clear disk cache on close"));
|
||||
clear_disk_cache_->setChecked(folder->get_clear_on_close());
|
||||
clear_disk_cache_->setChecked(
|
||||
oakengine_disk_folder_get_clear_on_close(folder) != 0);
|
||||
layout->addWidget(clear_disk_cache_, row, 1);
|
||||
|
||||
row++;
|
||||
@@ -84,12 +102,17 @@ void DiskCacheDialog::accept()
|
||||
{
|
||||
qint64 new_disk_cache_limit =
|
||||
qRound64(maximum_cache_slider_->get_value() * k_bytes_in_gigabyte);
|
||||
if (new_disk_cache_limit != folder_->get_limit()) {
|
||||
folder_->set_limit(new_disk_cache_limit);
|
||||
if (static_cast<double>(new_disk_cache_limit) !=
|
||||
oakengine_disk_folder_get_limit(folder_)) {
|
||||
oakengine_disk_folder_set_limit(
|
||||
folder_, static_cast<double>(new_disk_cache_limit));
|
||||
}
|
||||
|
||||
if (folder_->get_clear_on_close() != clear_disk_cache_->isChecked()) {
|
||||
folder_->set_clear_on_close(clear_disk_cache_->isChecked());
|
||||
const bool clear_on_close = clear_disk_cache_->isChecked();
|
||||
if ((oakengine_disk_folder_get_clear_on_close(folder_) != 0) !=
|
||||
clear_on_close) {
|
||||
oakengine_disk_folder_set_clear_on_close(folder_,
|
||||
clear_on_close ? 1 : 0);
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
@@ -97,7 +120,7 @@ void DiskCacheDialog::accept()
|
||||
|
||||
void DiskCacheDialog::clear_disk_cache()
|
||||
{
|
||||
clear_disk_cache(folder_->get_path(), this, clear_cache_btn_);
|
||||
clear_disk_cache(disk_folder_path(folder_), this, clear_cache_btn_);
|
||||
}
|
||||
|
||||
void DiskCacheDialog::clear_disk_cache(const QString &path, QWidget *parent,
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <QDialog>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "render/diskmanager.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
|
||||
namespace olive
|
||||
@@ -35,7 +34,12 @@ namespace olive
|
||||
class DiskCacheDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DiskCacheDialog(DiskCacheFolder *folder, QWidget *parent = nullptr);
|
||||
/**
|
||||
* @param folder Opaque engine DiskCacheFolder handle (from
|
||||
* oakengine_disk_get_open_folder()), accessed through the
|
||||
* oakengine_disk_folder_* C ABI.
|
||||
*/
|
||||
DiskCacheDialog(void *folder, QWidget *parent = nullptr);
|
||||
|
||||
static void clear_disk_cache(const QString &path, QWidget *parent,
|
||||
QPushButton *clear_btn = nullptr);
|
||||
@@ -44,7 +48,7 @@ public slots:
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
DiskCacheFolder *folder_;
|
||||
void *folder_;
|
||||
|
||||
FloatSlider *maximum_cache_slider_;
|
||||
|
||||
|
||||
@@ -33,13 +33,11 @@
|
||||
|
||||
#include "oakutil/digit.h"
|
||||
#include "oakutil/qtutils.h"
|
||||
#include "codec/exportcodec.h"
|
||||
#include "codec/exportformat.h"
|
||||
#include "oakutil/oaknode.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/color.h"
|
||||
#include "oakengine/events.h"
|
||||
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||
#include "widget/viewer/vieweroutpututils.h"
|
||||
@@ -47,6 +45,7 @@
|
||||
#include "oakengine/project.h"
|
||||
#include "oakengine/task.h"
|
||||
#include "oakengine/encoding.h"
|
||||
#include "oakengine/timeline.h"
|
||||
#include "oakengine/viewer.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
@@ -70,6 +69,22 @@ int pix_fmt_index(int codec, const QString &pix_fmt)
|
||||
return oakengine_encoding_pix_fmt_index(codec, pix_fmt.toUtf8().constData());
|
||||
}
|
||||
|
||||
// ViewerOutput::get_work_area() snapshot through the C ABI.
|
||||
oakengine_viewer_workarea export_viewer_workarea(const OakEngineNode *viewer)
|
||||
{
|
||||
oakengine_viewer_workarea wa = {};
|
||||
oakengine_viewer_get_workarea(viewer, &wa);
|
||||
return wa;
|
||||
}
|
||||
|
||||
// ViewerOutput::get_length() as a Rational through the C ABI.
|
||||
Rational export_viewer_length(const OakEngineNode *viewer)
|
||||
{
|
||||
int64_t num = 0, den = 1;
|
||||
oakengine_viewer_get_length(viewer, &num, &den);
|
||||
return Rational(num, den);
|
||||
}
|
||||
|
||||
// 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 OakEngineEncodingParams *p)
|
||||
@@ -182,7 +197,7 @@ oak_export_options_ex params_to_ex(const OakEngineEncodingParams *p)
|
||||
|
||||
} // namespace
|
||||
|
||||
ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
||||
ExportDialog::ExportDialog(OakEngineNode *viewer_node, bool stills_only_mode,
|
||||
QWidget *parent)
|
||||
: super(parent)
|
||||
, viewer_node_(viewer_node)
|
||||
@@ -253,7 +268,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
||||
range_combobox_ = new QComboBox();
|
||||
range_combobox_->addItem(tr("Entire Sequence"));
|
||||
range_combobox_->addItem(tr("In to Out"));
|
||||
range_combobox_->setEnabled(viewer_node_->get_work_area()->enabled());
|
||||
range_combobox_->setEnabled(export_viewer_workarea(viewer_node_).enabled);
|
||||
|
||||
preferences_layout->addWidget(range_combobox_, row, 1, 1, 3);
|
||||
|
||||
@@ -287,13 +302,14 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
||||
|
||||
preferences_tabs_ = new QTabWidget();
|
||||
|
||||
color_manager_ = oak_color_manager(viewer_node_->project()->color_manager());
|
||||
color_manager_ = oakengine_color_manager_from_project(
|
||||
oakengine_node_get_project(viewer_node_));
|
||||
video_tab_ = new ExportVideoTab(color_manager_);
|
||||
add_preferences_tab(video_tab_, tr("Video"));
|
||||
|
||||
// Set video tab time and make connections
|
||||
viewer_sub_ = oakengine_event_subscribe(
|
||||
reinterpret_cast<OakEngineNode *>(viewer_node),
|
||||
viewer_node,
|
||||
OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED,
|
||||
[](const oakengine_event *event, void *userdata) {
|
||||
auto *dlg = static_cast<ExportDialog *>(userdata);
|
||||
@@ -304,13 +320,12 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
||||
connect(video_tab_, &ExportVideoTab::time_changed, this,
|
||||
[viewer_node](const Rational &time) {
|
||||
oakengine_viewer_set_playhead(
|
||||
reinterpret_cast<OakEngineNode *>(viewer_node),
|
||||
viewer_node,
|
||||
time.numerator(), time.denominator());
|
||||
});
|
||||
{
|
||||
int64_t pn, pd;
|
||||
oakengine_viewer_get_playhead(
|
||||
reinterpret_cast<OakEngineNode *>(viewer_node), &pn, &pd);
|
||||
oakengine_viewer_get_playhead(viewer_node, &pn, &pd);
|
||||
video_tab_->set_time(Rational(pn, pd));
|
||||
}
|
||||
|
||||
@@ -389,7 +404,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
||||
connect(format_combobox_, &ExportFormatComboBox::format_changed, this,
|
||||
&ExportDialog::format_changed);
|
||||
|
||||
VideoParams vp = viewer_output_video_params(viewer_node_);
|
||||
oak::VideoParams vp = viewer_output_video_params(viewer_node_);
|
||||
video_aspect_ratio_ =
|
||||
static_cast<double>(vp.width()) / static_cast<double>(vp.height());
|
||||
|
||||
@@ -408,7 +423,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
||||
&ExportDialog::resolution_changed);
|
||||
|
||||
connect(video_tab_, &ExportVideoTab::color_space_changed, preview_viewer_,
|
||||
static_cast<void (ViewerWidget::*)(const ColorTransform &)>(
|
||||
static_cast<void (ViewerWidget::*)(const oak::ColorTransform &)>(
|
||||
&ViewerWidget::set_color_transform));
|
||||
connect(video_tab_, &ExportVideoTab::image_sequence_check_box_changed, this,
|
||||
&ExportDialog::image_sequence_check_box_changed);
|
||||
@@ -800,12 +815,10 @@ void ExportDialog::load_presets()
|
||||
|
||||
void ExportDialog::set_default_filename()
|
||||
{
|
||||
Project *p = viewer_node_->project();
|
||||
OakEngineProject *p = oakengine_node_get_project(viewer_node_);
|
||||
|
||||
char fn_buf[512];
|
||||
oakengine_project_filename(
|
||||
reinterpret_cast<OakEngineProject *>(p),
|
||||
fn_buf, sizeof(fn_buf));
|
||||
oakengine_project_filename(p, fn_buf, sizeof(fn_buf));
|
||||
QDir doc_location;
|
||||
|
||||
if (fn_buf[0] == '\0') {
|
||||
@@ -815,16 +828,25 @@ void ExportDialog::set_default_filename()
|
||||
doc_location = QFileInfo(fn_buf).dir();
|
||||
}
|
||||
|
||||
QString file_location = doc_location.filePath(viewer_node_->get_label());
|
||||
QString file_location = doc_location.filePath(
|
||||
oak::Node(viewer_node_).get_label());
|
||||
filename_edit_->setText(file_location);
|
||||
}
|
||||
|
||||
bool ExportDialog::sequence_has_subtitles() const
|
||||
{
|
||||
if (Sequence *s = dynamic_cast<Sequence *>(viewer_node_)) {
|
||||
TrackList *tl = s->track_list(Track::k_subtitle);
|
||||
for (Track *t : tl->get_tracks()) {
|
||||
if (!t->is_muted() && !t->blocks().empty()) {
|
||||
OakEngineNode *node = viewer_node_;
|
||||
if (oakengine_node_is_sequence(node)) {
|
||||
OakEngineSequence *seq = reinterpret_cast<OakEngineSequence *>(node);
|
||||
int track_count = 0;
|
||||
oakengine_sequence_track_count(seq, nullptr, nullptr, &track_count);
|
||||
for (int i = 0; i < track_count; i++) {
|
||||
OakEngineTrack *t = oakengine_sequence_track_at(
|
||||
seq, OAKENGINE_TRACK_TYPE_SUBTITLE, i);
|
||||
if (t &&
|
||||
!oakengine_track_is_muted(seq, OAKENGINE_TRACK_TYPE_SUBTITLE,
|
||||
i) &&
|
||||
oakengine_track_block_count(t) > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -842,7 +864,7 @@ void ExportDialog::set_defaults()
|
||||
}
|
||||
format_changed(format_combobox_->get_format());
|
||||
|
||||
VideoParams vp = viewer_output_video_params(viewer_node_);
|
||||
oak::VideoParams vp = viewer_output_video_params(viewer_node_);
|
||||
AudioParams ap = viewer_output_audio_params(viewer_node_);
|
||||
|
||||
video_tab_->width_slider()->set_value(vp.width());
|
||||
@@ -873,7 +895,7 @@ OakEngineEncodingParams *ExportDialog::generate_params() const
|
||||
oakengine_encoding_params_set_filename(
|
||||
params, filename_edit_->text().trimmed().toUtf8().constData());
|
||||
|
||||
const Rational export_len = viewer_node_->get_length();
|
||||
const Rational export_len = export_viewer_length(viewer_node_);
|
||||
oakengine_encoding_params_set_export_length(
|
||||
params, export_len.numerator(), export_len.denominator());
|
||||
|
||||
@@ -887,10 +909,10 @@ OakEngineEncodingParams *ExportDialog::generate_params() const
|
||||
(export_time + tb).numerator(),
|
||||
(export_time + tb).denominator());
|
||||
} else if (range_combobox_->currentIndex() == k_range_in_to_out) {
|
||||
const TimeRange &r = viewer_node_->get_work_area()->range();
|
||||
const oakengine_viewer_workarea wa =
|
||||
export_viewer_workarea(viewer_node_);
|
||||
oakengine_encoding_params_set_custom_range(
|
||||
params, r.in().numerator(), r.in().denominator(),
|
||||
r.out().numerator(), r.out().denominator());
|
||||
params, wa.in_num, wa.in_den, wa.out_num, wa.out_den);
|
||||
}
|
||||
|
||||
if (video_tab_->scaling_method_combobox()->isEnabled()) {
|
||||
@@ -985,7 +1007,7 @@ void ExportDialog::set_params(const OakEngineEncodingParams *e)
|
||||
format_changed(format_combobox_->get_format());
|
||||
|
||||
if (oakengine_encoding_params_has_custom_range(e) &&
|
||||
viewer_node_->get_work_area()->enabled()) {
|
||||
export_viewer_workarea(viewer_node_).enabled) {
|
||||
range_combobox_->setCurrentIndex(k_range_in_to_out);
|
||||
}
|
||||
|
||||
@@ -1110,9 +1132,12 @@ void ExportDialog::done(int r)
|
||||
Rational ExportDialog::get_export_length() const
|
||||
{
|
||||
if (range_combobox_->currentIndex() == k_range_in_to_out) {
|
||||
return viewer_node_->get_work_area()->range().length();
|
||||
const oakengine_viewer_workarea wa =
|
||||
export_viewer_workarea(viewer_node_);
|
||||
return Rational(wa.out_num, wa.out_den) -
|
||||
Rational(wa.in_num, wa.in_den);
|
||||
} else {
|
||||
return viewer_node_->get_length();
|
||||
return export_viewer_length(viewer_node_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1128,7 +1153,7 @@ 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_output_video_params(viewer_node_);
|
||||
oak::VideoParams vp = viewer_output_video_params(viewer_node_);
|
||||
|
||||
float mat16[16];
|
||||
oakengine_encoding_generate_matrix(
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <QLineEdit>
|
||||
#include <QProgressBar>
|
||||
|
||||
#include "codec/encoder.h"
|
||||
#include "dialog/export/exportformatcombobox.h"
|
||||
#include "exportaudiotab.h"
|
||||
#include "exportsubtitlestab.h"
|
||||
@@ -44,9 +43,9 @@ namespace olive
|
||||
class ExportDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExportDialog(ViewerOutput *viewer_node, bool stills_only_mode,
|
||||
ExportDialog(OakEngineNode *viewer_node, bool stills_only_mode,
|
||||
QWidget *parent = nullptr);
|
||||
ExportDialog(ViewerOutput *viewer_node, QWidget *parent = nullptr)
|
||||
ExportDialog(OakEngineNode *viewer_node, QWidget *parent = nullptr)
|
||||
: ExportDialog(viewer_node, false, parent)
|
||||
{
|
||||
}
|
||||
@@ -75,7 +74,7 @@ private:
|
||||
|
||||
void set_defaults();
|
||||
|
||||
ViewerOutput *viewer_node_;
|
||||
OakEngineNode *viewer_node_;
|
||||
|
||||
int64_t viewer_sub_ = 0;
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
|
||||
#include "codec/encoder.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
|
||||
namespace olive
|
||||
|
||||
@@ -43,24 +43,24 @@ ExportFormatComboBox::ExportFormatComboBox(Mode mode, QWidget *parent)
|
||||
switch (mode) {
|
||||
case k_show_all_formats:
|
||||
custom_menu_->addAction(create_header(icon::video, tr("Video")));
|
||||
populate_type(Track::k_video);
|
||||
populate_type(TrackReference::k_video);
|
||||
custom_menu_->addSeparator();
|
||||
|
||||
custom_menu_->addAction(create_header(icon::audio, tr("Audio")));
|
||||
populate_type(Track::k_audio);
|
||||
populate_type(TrackReference::k_audio);
|
||||
custom_menu_->addSeparator();
|
||||
|
||||
custom_menu_->addAction(create_header(icon::subtitles, tr("Subtitle")));
|
||||
populate_type(Track::k_subtitle);
|
||||
populate_type(TrackReference::k_subtitle);
|
||||
break;
|
||||
case k_show_audio_only:
|
||||
populate_type(Track::k_audio);
|
||||
populate_type(TrackReference::k_audio);
|
||||
break;
|
||||
case k_show_video_only:
|
||||
populate_type(Track::k_video);
|
||||
populate_type(TrackReference::k_video);
|
||||
break;
|
||||
case k_show_subtitles_only:
|
||||
populate_type(Track::k_subtitle);
|
||||
populate_type(TrackReference::k_subtitle);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ void ExportFormatComboBox::handle_index_change(QAction *a)
|
||||
emit format_changed(f);
|
||||
}
|
||||
|
||||
void ExportFormatComboBox::populate_type(Track::Type type)
|
||||
void ExportFormatComboBox::populate_type(TrackReference::Type type)
|
||||
{
|
||||
const int fmt_count = oakengine_encoding_format_count();
|
||||
for (int i = 0; i < fmt_count; i++) {
|
||||
@@ -101,11 +101,11 @@ void ExportFormatComboBox::populate_type(Track::Type type)
|
||||
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) {
|
||||
if (type == TrackReference::k_video && has_video) {
|
||||
// Do nothing
|
||||
} else if (type == Track::k_audio && !has_video && has_audio) {
|
||||
} else if (type == TrackReference::k_audio && !has_video && has_audio) {
|
||||
// Do nothing
|
||||
} else if (type == Track::k_subtitle && !has_video && !has_audio && has_sub) {
|
||||
} else if (type == TrackReference::k_subtitle && !has_video && !has_audio && has_sub) {
|
||||
// Do nothing
|
||||
} else {
|
||||
continue;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <QComboBox>
|
||||
#include <QWidgetAction>
|
||||
|
||||
#include "node/output/track/track.h"
|
||||
#include "common/trackreferencehandle.h"
|
||||
#include "widget/menu/menu.h"
|
||||
|
||||
namespace olive
|
||||
@@ -64,7 +64,7 @@ private slots:
|
||||
void handle_index_change(QAction *a);
|
||||
|
||||
private:
|
||||
void populate_type(Track::Type type);
|
||||
void populate_type(TrackReference::Type type);
|
||||
|
||||
QWidgetAction *create_header(const QIcon &icon, const QString &title);
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <QPushButton>
|
||||
|
||||
#include "exportadvancedvideodialog.h"
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
#include "oakengine/encoding.h"
|
||||
|
||||
namespace olive
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "oakengine/node.h"
|
||||
#include "oakengine/timeline.h"
|
||||
#include "oakengine/undo.h"
|
||||
#include "oakutil/oaknode.h"
|
||||
#include "streamproperties/audiostreamproperties.h"
|
||||
#include "streamproperties/videostreamproperties.h"
|
||||
#include "widget/viewer/vieweroutpututils.h"
|
||||
@@ -46,20 +47,26 @@ namespace olive
|
||||
{
|
||||
|
||||
FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
||||
Footage *footage)
|
||||
OakEngineNode *footage)
|
||||
: QDialog(parent)
|
||||
, footage_(footage)
|
||||
{
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
|
||||
setWindowTitle(tr("\"%1\" Properties").arg(footage_->get_label_or_name()));
|
||||
// WRAPPER-GAP: oakengine_node_get_label_or_name -- emulate inline
|
||||
// (Node::get_label_or_name(): the label, falling back to the name).
|
||||
const QString footage_label = oak::Node(footage_).get_label();
|
||||
setWindowTitle(
|
||||
tr("\"%1\" Properties")
|
||||
.arg(footage_label.isEmpty() ? oak::Node(footage_).name() :
|
||||
footage_label));
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
int row = 0;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Name:")), row, 0);
|
||||
|
||||
footage_name_field_ = new QLineEdit(footage_->get_label());
|
||||
footage_name_field_ = new QLineEdit(footage_label);
|
||||
layout->addWidget(footage_name_field_, row, 1);
|
||||
row++;
|
||||
|
||||
@@ -70,8 +77,15 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
||||
{
|
||||
QHBoxLayout *start_time_layout = new QHBoxLayout();
|
||||
|
||||
OakEngineFootage *start_time_handle = oakengine_footage_borrow(footage_);
|
||||
int sst_num = 0, sst_den = 1;
|
||||
const bool has_sst =
|
||||
oakengine_footage_get_source_start_time(start_time_handle,
|
||||
&sst_num,
|
||||
&sst_den) == 1;
|
||||
|
||||
source_start_time_enable_ = new QCheckBox(tr("Set"));
|
||||
source_start_time_enable_->setChecked(footage_->has_source_start_time());
|
||||
source_start_time_enable_->setChecked(has_sst);
|
||||
start_time_layout->addWidget(source_start_time_enable_);
|
||||
|
||||
source_start_time_spin_ = new QDoubleSpinBox();
|
||||
@@ -79,9 +93,7 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
||||
source_start_time_spin_->setDecimals(3);
|
||||
source_start_time_spin_->setSuffix(QStringLiteral(" s"));
|
||||
source_start_time_spin_->setValue(
|
||||
footage_->has_source_start_time() ?
|
||||
footage_->source_start_time().to_double() :
|
||||
0.0);
|
||||
has_sst ? double(sst_num) / double(sst_den) : 0.0);
|
||||
source_start_time_spin_->setEnabled(
|
||||
source_start_time_enable_->isChecked());
|
||||
start_time_layout->addWidget(source_start_time_spin_, 1);
|
||||
@@ -89,14 +101,11 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
||||
QString detection_note;
|
||||
// Detection source comes through the facade (auto-detected field or
|
||||
// "manual"), matching the engine's stored value.
|
||||
if (footage_->has_source_start_time()) {
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(
|
||||
reinterpret_cast<OakEngineNode *>(footage_));
|
||||
if (has_sst) {
|
||||
char source_buf[64];
|
||||
source_buf[0] = '\0';
|
||||
oakengine_footage_get_source_start_time_source(
|
||||
facade_handle, source_buf, sizeof(source_buf));
|
||||
oakengine_footage_free(facade_handle);
|
||||
start_time_handle, source_buf, sizeof(source_buf));
|
||||
const QString source = QString::fromUtf8(source_buf);
|
||||
detection_note =
|
||||
(source == QStringLiteral("manual")) ?
|
||||
@@ -105,6 +114,7 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
||||
} else {
|
||||
detection_note = tr("(not detected)");
|
||||
}
|
||||
oakengine_footage_free(start_time_handle);
|
||||
start_time_layout->addWidget(new QLabel(detection_note));
|
||||
|
||||
connect(source_start_time_enable_, &QCheckBox::toggled,
|
||||
@@ -127,49 +137,66 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
||||
|
||||
int first_usable_stream = -1;
|
||||
|
||||
for (int i = 0; i < footage_->get_total_stream_count(); i++) {
|
||||
Track::Reference reference = footage_->get_reference_from_real_index(i);
|
||||
int total_stream_count = 0;
|
||||
{
|
||||
OakEngineFootage *count_handle = oakengine_footage_borrow(footage_);
|
||||
total_stream_count =
|
||||
oakengine_footage_get_video_stream_count(count_handle) +
|
||||
oakengine_footage_get_audio_stream_count(count_handle) +
|
||||
oakengine_footage_get_subtitle_stream_count(count_handle);
|
||||
oakengine_footage_free(count_handle);
|
||||
}
|
||||
|
||||
for (int i = 0; i < total_stream_count; i++) {
|
||||
QString description;
|
||||
bool is_enabled = false;
|
||||
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(
|
||||
reinterpret_cast<OakEngineNode *>(footage_));
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||
|
||||
switch (reference.type()) {
|
||||
case Track::k_video: {
|
||||
// (track_type, stream_index) pair for this real stream index;
|
||||
// track types are OAKENGINE_TRACK_TYPE_* ordinals (identical to
|
||||
// engine Track::Type).
|
||||
int reference_type = -1;
|
||||
int reference_index = -1;
|
||||
oakengine_footage_get_stream_reference(facade_handle, i,
|
||||
&reference_type,
|
||||
&reference_index);
|
||||
|
||||
switch (reference_type) {
|
||||
case OAKENGINE_TRACK_TYPE_VIDEO: {
|
||||
stacked_widget_->addWidget(
|
||||
new VideoStreamProperties(footage_, reference.index()));
|
||||
new VideoStreamProperties(footage_, reference_index));
|
||||
|
||||
VideoParams vp = viewer_output_video_params(footage_, reference.index());
|
||||
is_enabled = vp.enabled();
|
||||
is_enabled = oakengine_viewer_get_stream_enabled(
|
||||
reinterpret_cast<const OakEngineNode *>(footage_),
|
||||
OAKENGINE_TRACK_TYPE_VIDEO, reference_index) == 1;
|
||||
{
|
||||
char desc_buf[256];
|
||||
oakengine_footage_describe_video_stream(
|
||||
facade_handle, reference.index(), desc_buf,
|
||||
facade_handle, reference_index, desc_buf,
|
||||
sizeof(desc_buf));
|
||||
description = QString::fromUtf8(desc_buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Track::k_audio: {
|
||||
case OAKENGINE_TRACK_TYPE_AUDIO: {
|
||||
stacked_widget_->addWidget(
|
||||
new AudioStreamProperties(footage_, reference.index()));
|
||||
new AudioStreamProperties(footage_, reference_index));
|
||||
|
||||
AudioParams ap = viewer_output_audio_params(footage_, reference.index());
|
||||
AudioParams ap = viewer_output_audio_params(footage_, reference_index);
|
||||
is_enabled = ap.enabled();
|
||||
{
|
||||
char desc_buf[256];
|
||||
oakengine_footage_describe_audio_stream(
|
||||
facade_handle, reference.index(), desc_buf,
|
||||
facade_handle, reference_index, desc_buf,
|
||||
sizeof(desc_buf));
|
||||
description = QString::fromUtf8(desc_buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Track::k_subtitle: {
|
||||
case OAKENGINE_TRACK_TYPE_SUBTITLE: {
|
||||
is_enabled = oakengine_footage_get_stream_enabled(
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference.index());
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference_index);
|
||||
|
||||
// FIXME: Language?
|
||||
description = tr("Subtitles");
|
||||
@@ -189,9 +216,9 @@ FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent,
|
||||
track_list_->addItem(item);
|
||||
|
||||
if (first_usable_stream == -1 &&
|
||||
(reference.type() == Track::k_video ||
|
||||
reference.type() == Track::k_audio ||
|
||||
reference.type() == Track::k_subtitle)) {
|
||||
(reference_type == OAKENGINE_TRACK_TYPE_VIDEO ||
|
||||
reference_type == OAKENGINE_TRACK_TYPE_AUDIO ||
|
||||
reference_type == OAKENGINE_TRACK_TYPE_SUBTITLE)) {
|
||||
first_usable_stream = i;
|
||||
}
|
||||
}
|
||||
@@ -230,16 +257,14 @@ void FootagePropertiesDialog::accept()
|
||||
}
|
||||
}
|
||||
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(
|
||||
reinterpret_cast<OakEngineNode *>(footage_));
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||
|
||||
// All writes go through the liboakengine C ABI facade; each call lands
|
||||
// on the shared undo stack as an undoable command (replacing this
|
||||
// dialog's own undo command classes with identical semantics).
|
||||
if (footage_->get_label() != footage_name_field_->text()) {
|
||||
if (oak::Node(footage_).get_label() != footage_name_field_->text()) {
|
||||
oakengine_node_set_label(
|
||||
reinterpret_cast<OakEngineNode *>(footage_),
|
||||
footage_name_field_->text().toUtf8().constData());
|
||||
footage_, footage_name_field_->text().toUtf8().constData());
|
||||
}
|
||||
|
||||
// Apply source start time changes
|
||||
@@ -247,41 +272,55 @@ void FootagePropertiesDialog::accept()
|
||||
const bool new_enabled = source_start_time_enable_->isChecked();
|
||||
const Rational new_time =
|
||||
Rational::from_double(source_start_time_spin_->value());
|
||||
if (new_enabled != footage_->has_source_start_time() ||
|
||||
(new_enabled && new_time != footage_->source_start_time())) {
|
||||
int cur_sst_num = 0, cur_sst_den = 1;
|
||||
const bool cur_has_sst =
|
||||
oakengine_footage_get_source_start_time(facade_handle,
|
||||
&cur_sst_num,
|
||||
&cur_sst_den) == 1;
|
||||
if (new_enabled != cur_has_sst ||
|
||||
(new_enabled &&
|
||||
new_time != Rational(cur_sst_num, cur_sst_den))) {
|
||||
oakengine_footage_set_source_start_time(
|
||||
facade_handle, new_enabled ? 1 : 0, new_time.numerator(),
|
||||
new_time.denominator());
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < footage_->get_total_stream_count(); i++) {
|
||||
Track::Reference reference = footage_->get_reference_from_real_index(i);
|
||||
int total_stream_count =
|
||||
oakengine_footage_get_video_stream_count(facade_handle) +
|
||||
oakengine_footage_get_audio_stream_count(facade_handle) +
|
||||
oakengine_footage_get_subtitle_stream_count(facade_handle);
|
||||
|
||||
for (int i = 0; i < total_stream_count; i++) {
|
||||
int reference_type = -1;
|
||||
int reference_index = -1;
|
||||
oakengine_footage_get_stream_reference(facade_handle, i,
|
||||
&reference_type,
|
||||
&reference_index);
|
||||
bool new_stream_enabled =
|
||||
(track_list_->item(i)->checkState() == Qt::Checked);
|
||||
bool old_stream_enabled = new_stream_enabled;
|
||||
|
||||
switch (reference.type()) {
|
||||
case Track::k_video:
|
||||
switch (reference_type) {
|
||||
case OAKENGINE_TRACK_TYPE_VIDEO:
|
||||
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_VIDEO, reference.index());
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_VIDEO, reference_index);
|
||||
break;
|
||||
case Track::k_audio:
|
||||
case OAKENGINE_TRACK_TYPE_AUDIO:
|
||||
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_AUDIO, reference.index());
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_AUDIO, reference_index);
|
||||
break;
|
||||
case Track::k_subtitle:
|
||||
case OAKENGINE_TRACK_TYPE_SUBTITLE:
|
||||
old_stream_enabled = oakengine_footage_get_stream_enabled(
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference.index());
|
||||
facade_handle, OAKENGINE_TRACK_TYPE_SUBTITLE, reference_index);
|
||||
break;
|
||||
case Track::k_none:
|
||||
case Track::k_count:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (old_stream_enabled != new_stream_enabled) {
|
||||
oakengine_footage_set_stream_enabled(
|
||||
facade_handle, int(reference.type()), reference.index(),
|
||||
facade_handle, reference_type, reference_index,
|
||||
new_stream_enabled ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <QListWidget>
|
||||
#include <QStackedWidget>
|
||||
|
||||
#include "node/project/footage/footage.h"
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
*
|
||||
* Media object to set properties for.
|
||||
*/
|
||||
FootagePropertiesDialog(QWidget *parent, Footage *footage);
|
||||
FootagePropertiesDialog(QWidget *parent, OakEngineNode *footage);
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -79,9 +79,9 @@ private:
|
||||
QDoubleSpinBox *source_start_time_spin_;
|
||||
|
||||
/**
|
||||
* @brief Internal pointer to Media object (set in constructor)
|
||||
* @brief Internal handle to the footage node (set in constructor)
|
||||
*/
|
||||
Footage *footage_;
|
||||
OakEngineNode *footage_;
|
||||
|
||||
/**
|
||||
* @brief A list widget for listing the tracks in Media
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
namespace olive
|
||||
{
|
||||
|
||||
AudioStreamProperties::AudioStreamProperties(Footage *footage, int audio_index)
|
||||
AudioStreamProperties::AudioStreamProperties(OakEngineNode *footage,
|
||||
int audio_index)
|
||||
: footage_(footage)
|
||||
, audio_index_(audio_index)
|
||||
{
|
||||
|
||||
@@ -22,20 +22,21 @@
|
||||
#ifndef OAK_AUDIOSTREAMPROPERTIES_H
|
||||
#define OAK_AUDIOSTREAMPROPERTIES_H
|
||||
|
||||
#include "node/project/footage/footage.h"
|
||||
#include "streamproperties.h"
|
||||
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class AudioStreamProperties : public StreamProperties {
|
||||
public:
|
||||
AudioStreamProperties(Footage *footage, int audio_index);
|
||||
AudioStreamProperties(OakEngineNode *footage, int audio_index);
|
||||
|
||||
virtual void accept(void *parent) override;
|
||||
|
||||
private:
|
||||
Footage *footage_;
|
||||
OakEngineNode *footage_;
|
||||
|
||||
int audio_index_;
|
||||
};
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "node/project.h"
|
||||
#include "oakengine/color.h"
|
||||
#include "widget/manageddisplay/colorprocessorhandle.h"
|
||||
#include "oakengine/footage.h"
|
||||
@@ -38,7 +37,8 @@
|
||||
namespace olive
|
||||
{
|
||||
|
||||
VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
||||
VideoStreamProperties::VideoStreamProperties(OakEngineNode *footage,
|
||||
int video_index)
|
||||
: footage_(footage)
|
||||
, video_index_(video_index)
|
||||
, video_premultiply_alpha_(nullptr)
|
||||
@@ -51,14 +51,11 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
||||
video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0);
|
||||
|
||||
oak_video_params vpod;
|
||||
oakengine_viewer_get_video_params(
|
||||
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
|
||||
&vpod);
|
||||
oakengine_viewer_get_video_params(footage_, video_index_, &vpod);
|
||||
|
||||
// Stream override values come through the liboakengine C ABI facade;
|
||||
// layout-only conditions (channel count, video type) stay direct reads.
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(
|
||||
reinterpret_cast<OakEngineNode *>(footage_));
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||
char colorspace_buf[256];
|
||||
int color_range = 0;
|
||||
int interlacing = 0;
|
||||
@@ -79,8 +76,7 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
||||
video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0);
|
||||
|
||||
video_interlace_combo_ = new InterlacedComboBox();
|
||||
video_interlace_combo_->set_interlace_mode(
|
||||
static_cast<VideoParams::Interlacing>(interlacing));
|
||||
video_interlace_combo_->set_interlace_mode(interlacing);
|
||||
|
||||
video_layout->addWidget(video_interlace_combo_, row, 1);
|
||||
|
||||
@@ -93,7 +89,7 @@ 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()));
|
||||
oakengine_node_get_project(footage_));
|
||||
video_color_space_->addItem(tr("Default (%1)")
|
||||
.arg(oak_query_string([cm](char *buf, int size) {
|
||||
return oakengine_color_manager_default_input_color_space(
|
||||
@@ -183,8 +179,7 @@ void VideoStreamProperties::accept(void *parent)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(
|
||||
reinterpret_cast<OakEngineNode *>(footage_));
|
||||
OakEngineFootage *facade_handle = oakengine_footage_borrow(footage_);
|
||||
|
||||
QString set_colorspace;
|
||||
|
||||
@@ -206,9 +201,7 @@ void VideoStreamProperties::accept(void *parent)
|
||||
&vp_par_num, &vp_par_den);
|
||||
|
||||
oak_video_params vpod;
|
||||
oakengine_viewer_get_video_params(
|
||||
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
|
||||
&vpod);
|
||||
oakengine_viewer_get_video_params(footage_, video_index_, &vpod);
|
||||
|
||||
int64_t vp_start_time = 0, vp_duration = 0;
|
||||
int vp_fr_num = 0, vp_fr_den = 1;
|
||||
@@ -222,9 +215,7 @@ void VideoStreamProperties::accept(void *parent)
|
||||
if ((video_premultiply_alpha_ &&
|
||||
video_premultiply_alpha_->isChecked() != (vp_premultiplied != 0)) ||
|
||||
set_colorspace != QString::fromUtf8(vp_colorspace) ||
|
||||
static_cast<VideoParams::Interlacing>(
|
||||
video_interlace_combo_->currentIndex()) !=
|
||||
static_cast<VideoParams::Interlacing>(vp_interlacing) ||
|
||||
video_interlace_combo_->currentIndex() != vp_interlacing ||
|
||||
color_range_combo_->currentData().toInt() != vp_color_range) {
|
||||
oakengine_footage_set_video_stream_overrides(
|
||||
facade_handle, video_index_,
|
||||
@@ -264,9 +255,7 @@ void VideoStreamProperties::accept(void *parent)
|
||||
bool VideoStreamProperties::sanity_check()
|
||||
{
|
||||
oak_video_params vpod;
|
||||
oakengine_viewer_get_video_params(
|
||||
reinterpret_cast<const OakEngineNode *>(footage_), video_index_,
|
||||
&vpod);
|
||||
oakengine_viewer_get_video_params(footage_, video_index_, &vpod);
|
||||
if (vpod.video_type == 2) {
|
||||
if (imgseq_start_time_->get_value() >= imgseq_end_time_->get_value()) {
|
||||
QMessageBox::critical(
|
||||
|
||||
@@ -25,25 +25,26 @@
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "node/project/footage/footage.h"
|
||||
#include "streamproperties.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
#include "widget/standardcombos/standardcombos.h"
|
||||
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class VideoStreamProperties : public StreamProperties {
|
||||
Q_OBJECT
|
||||
public:
|
||||
VideoStreamProperties(Footage *footage, int video_index);
|
||||
VideoStreamProperties(OakEngineNode *footage, int video_index);
|
||||
|
||||
virtual void accept(void *parent) override;
|
||||
|
||||
virtual bool sanity_check() override;
|
||||
|
||||
private:
|
||||
Footage *footage_;
|
||||
OakEngineNode *footage_;
|
||||
|
||||
int video_index_;
|
||||
|
||||
|
||||
@@ -22,9 +22,11 @@
|
||||
#include "footagerelinkdialog.h"
|
||||
|
||||
#include "core.h"
|
||||
#include "common/nodedatatypes.h"
|
||||
#include "oakengine/footage.h"
|
||||
#include "oakengine/node.h"
|
||||
#include "oakengine/project.h"
|
||||
#include "oakutil/oaknode.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileDialog>
|
||||
@@ -39,7 +41,7 @@
|
||||
namespace olive
|
||||
{
|
||||
|
||||
FootageRelinkDialog::FootageRelinkDialog(const QVector<Footage *> &footage,
|
||||
FootageRelinkDialog::FootageRelinkDialog(const QVector<OakEngineNode *> &footage,
|
||||
QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, footage_(footage)
|
||||
@@ -64,7 +66,7 @@ FootageRelinkDialog::FootageRelinkDialog(const QVector<Footage *> &footage,
|
||||
table_->header()->setStretchLastSection(false);
|
||||
|
||||
for (int i = 0; i < footage.size(); i++) {
|
||||
Footage *f = footage.at(i);
|
||||
OakEngineNode *f = footage.at(i);
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem();
|
||||
|
||||
QWidget *item_actions = new QWidget();
|
||||
@@ -75,9 +77,10 @@ FootageRelinkDialog::FootageRelinkDialog(const QVector<Footage *> &footage,
|
||||
&FootageRelinkDialog::browse_for_footage);
|
||||
item_actions_layout->addWidget(item_browse_btn);
|
||||
|
||||
item->setIcon(0, f->data(Node::icon).value<QIcon>());
|
||||
item->setText(0, f->get_label());
|
||||
item->setText(1, f->filename());
|
||||
item->setIcon(
|
||||
0, oak::Node(f).data(k_node_data_icon).value<QIcon>());
|
||||
item->setText(0, oak::Node(f).get_label());
|
||||
item->setText(1, oak::Footage::borrow(f).filename());
|
||||
|
||||
table_->addTopLevelItem(item);
|
||||
|
||||
@@ -99,21 +102,22 @@ FootageRelinkDialog::FootageRelinkDialog(const QVector<Footage *> &footage,
|
||||
|
||||
void FootageRelinkDialog::update_footage_item(int index)
|
||||
{
|
||||
Footage *f = footage_.at(index);
|
||||
OakEngineNode *f = footage_.at(index);
|
||||
QTreeWidgetItem *item = table_->topLevelItem(index);
|
||||
item->setIcon(0, f->data(Node::icon).value<QIcon>());
|
||||
item->setText(1, f->filename());
|
||||
item->setIcon(0, oak::Node(f).data(k_node_data_icon).value<QIcon>());
|
||||
item->setText(1, oak::Footage::borrow(f).filename());
|
||||
}
|
||||
|
||||
void FootageRelinkDialog::browse_for_footage()
|
||||
{
|
||||
int index = sender()->property("index").toInt();
|
||||
Footage *f = footage_.at(index);
|
||||
OakEngineNode *f = footage_.at(index);
|
||||
|
||||
QFileInfo info(f->filename());
|
||||
QFileInfo info(oak::Footage::borrow(f).filename());
|
||||
|
||||
QString new_fn = QFileDialog::getOpenFileName(
|
||||
this, tr("Relink \"%1\"").arg(f->get_label()), info.absolutePath(),
|
||||
this, tr("Relink \"%1\"").arg(oak::Node(f).get_label()),
|
||||
info.absolutePath(),
|
||||
Core::footage_file_dialog_filter());
|
||||
|
||||
// Originally, this function would attempt to filter to the exact filename of the missing file.
|
||||
@@ -141,8 +145,7 @@ void FootageRelinkDialog::browse_for_footage()
|
||||
// Relink through the facade (reprobes the file and resets stream /
|
||||
// proxy state; relinked footage becomes valid when the probe
|
||||
// succeeds).
|
||||
OakEngineFootage *relink_handle = oakengine_footage_borrow(
|
||||
reinterpret_cast<OakEngineNode *>(f));
|
||||
OakEngineFootage *relink_handle = oakengine_footage_borrow(f);
|
||||
const int relink_rc = oakengine_footage_relink(
|
||||
relink_handle, new_fn.toUtf8().constData());
|
||||
oakengine_footage_free(relink_handle);
|
||||
@@ -162,30 +165,33 @@ void FootageRelinkDialog::browse_for_footage()
|
||||
// Check all other footage files for matches in the new directory
|
||||
// (facade's exact file-name matching, mirroring the second attempt
|
||||
// of the old per-footage loop).
|
||||
Project *project = f->project();
|
||||
OakEngineProject *project = oakengine_node_get_project(f);
|
||||
if (project) {
|
||||
oakengine_project_find_offline_footage(
|
||||
reinterpret_cast<OakEngineProject *>(project),
|
||||
new_dir.absolutePath().toUtf8().constData());
|
||||
project, new_dir.absolutePath().toUtf8().constData());
|
||||
|
||||
// The old dialog also tried the original directory's relative
|
||||
// paths, which the facade's exact-name matching does not cover;
|
||||
// keep that pass here.
|
||||
for (int it = 0; it < footage_.size(); it++) {
|
||||
Footage *other_footage = footage_.at(it);
|
||||
OakEngineNode *other_footage = footage_.at(it);
|
||||
|
||||
// Ignore footage that's already valid of course
|
||||
if (!other_footage->is_valid()) {
|
||||
if (!oakengine_footage_is_valid(other_footage)) {
|
||||
// Get footage path relative to original directory
|
||||
QString relative_to_original =
|
||||
original_dir.relativeFilePath(other_footage->filename());
|
||||
original_dir.relativeFilePath(
|
||||
oak::Footage::borrow(other_footage).filename());
|
||||
QString absolute_to_new =
|
||||
new_dir.filePath(relative_to_original);
|
||||
|
||||
if (QFileInfo::exists(absolute_to_new)) {
|
||||
OakEngineFootage *other_handle =
|
||||
oakengine_footage_borrow(other_footage);
|
||||
oakengine_footage_relink(
|
||||
reinterpret_cast<OakEngineFootage *>(other_footage),
|
||||
other_handle,
|
||||
absolute_to_new.toUtf8().constData());
|
||||
oakengine_footage_free(other_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,7 +207,7 @@ void FootageRelinkDialog::browse_for_footage()
|
||||
// jump to that footage so the user knows where it is.
|
||||
int next_invalid = -1;
|
||||
for (int i = 0; i < footage_.size(); i++) {
|
||||
if (!footage_.at(i)->is_valid()) {
|
||||
if (!oakengine_footage_is_valid(footage_.at(i))) {
|
||||
next_invalid = i;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,9 @@
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTreeWidget>
|
||||
#include <QVector>
|
||||
|
||||
#include "node/project/footage/footage.h"
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -33,7 +34,7 @@ namespace olive
|
||||
class FootageRelinkDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FootageRelinkDialog(const QVector<Footage *> &footage,
|
||||
FootageRelinkDialog(const QVector<OakEngineNode *> &footage,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
@@ -41,7 +42,7 @@ private:
|
||||
|
||||
QTreeWidget *table_;
|
||||
|
||||
QVector<Footage *> footage_;
|
||||
QVector<OakEngineNode *> footage_;
|
||||
|
||||
private slots:
|
||||
void browse_for_footage();
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGridLayout>
|
||||
|
||||
#include "common/keyframetypes.h"
|
||||
#include "oakengine/node.h"
|
||||
#include "olive/core/util/timecodefunctions.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -32,41 +34,47 @@ namespace olive
|
||||
namespace
|
||||
{
|
||||
|
||||
Rational keyframe_rational_time(const oak::Keyframe &key)
|
||||
{
|
||||
int64_t num = 0, den = 1;
|
||||
key.time(&num, &den);
|
||||
return Rational(int(num), int(den));
|
||||
}
|
||||
|
||||
// Selected keyframes grouped by owning input (node/id/element), with
|
||||
// times converted to the facade's frame timestamps. The dialog's
|
||||
// per-property writes go through the facade as ONE undoable command per
|
||||
// group (usually just one).
|
||||
struct KeyGroup {
|
||||
Node *node;
|
||||
OakEngineNode *node;
|
||||
QString input;
|
||||
int element;
|
||||
QVector<int64_t> times;
|
||||
QVector<int> tracks;
|
||||
};
|
||||
|
||||
QVector<KeyGroup> group_keys(const std::vector<NodeKeyframe *> &keys)
|
||||
QVector<KeyGroup> group_keys(const QVector<oak::Keyframe> &keys)
|
||||
{
|
||||
QVector<KeyGroup> groups;
|
||||
for (NodeKeyframe *item : keys) {
|
||||
for (const oak::Keyframe &item : keys) {
|
||||
OakEngineNode *node = item.node().handle();
|
||||
int g = 0;
|
||||
for (; g < groups.size(); g++) {
|
||||
if (groups.at(g).node == item->parent() &&
|
||||
groups.at(g).input == item->input() &&
|
||||
groups.at(g).element == item->element()) {
|
||||
if (groups.at(g).node == node &&
|
||||
groups.at(g).input == item.input_id() &&
|
||||
groups.at(g).element == item.element()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (g == groups.size()) {
|
||||
groups.append({ item->parent(), item->input(), item->element(),
|
||||
groups.append({ node, item.input_id(), item.element(),
|
||||
{}, {} });
|
||||
}
|
||||
OakEngineNode *handle =
|
||||
reinterpret_cast<OakEngineNode *>(item->parent());
|
||||
int tbn = 0, tbd = 0;
|
||||
oakengine_node_frame_time_base(handle, &tbn, &tbd);
|
||||
oakengine_node_frame_time_base(node, &tbn, &tbd);
|
||||
groups[g].times.append(Timecode::time_to_timestamp(
|
||||
item->time(), Rational(tbn, tbd), Timecode::k_round));
|
||||
groups[g].tracks.append(item->track());
|
||||
keyframe_rational_time(item), Rational(tbn, tbd), Timecode::k_round));
|
||||
groups[g].tracks.append(item.track());
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
@@ -74,7 +82,7 @@ QVector<KeyGroup> group_keys(const std::vector<NodeKeyframe *> &keys)
|
||||
} // namespace
|
||||
|
||||
KeyframePropertiesDialog::KeyframePropertiesDialog(
|
||||
const std::vector<NodeKeyframe *> &keys, const Rational &timebase,
|
||||
const QVector<oak::Keyframe> &keys, const Rational &timebase,
|
||||
QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, keys_(keys)
|
||||
@@ -137,47 +145,48 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(
|
||||
bool all_same_bezier_out_x = true;
|
||||
bool all_same_bezier_out_y = true;
|
||||
|
||||
for (size_t i = 0; i < keys_.size(); i++) {
|
||||
for (int i = 0; i < keys_.size(); i++) {
|
||||
if (i > 0) {
|
||||
NodeKeyframe *prev_key = keys_.at(i - 1);
|
||||
NodeKeyframe *this_key = keys_.at(i);
|
||||
const oak::Keyframe &prev_key = keys_.at(i - 1);
|
||||
const oak::Keyframe &this_key = keys_.at(i);
|
||||
|
||||
// Determine if the keyframes are all the same time or not
|
||||
if (all_same_time) {
|
||||
all_same_time = (prev_key->time() == this_key->time());
|
||||
all_same_time = (keyframe_rational_time(prev_key) ==
|
||||
keyframe_rational_time(this_key));
|
||||
}
|
||||
|
||||
// Determine if the keyframes are all the same type
|
||||
if (all_same_type) {
|
||||
all_same_type = (prev_key->type() == this_key->type());
|
||||
all_same_type = (prev_key.type() == this_key.type());
|
||||
}
|
||||
|
||||
// Check all four bezier control points
|
||||
if (all_same_bezier_in_x) {
|
||||
all_same_bezier_in_x = (prev_key->bezier_control_in().x() ==
|
||||
this_key->bezier_control_in().x());
|
||||
all_same_bezier_in_x = (prev_key.bezier_point(0).x() ==
|
||||
this_key.bezier_point(0).x());
|
||||
}
|
||||
|
||||
if (all_same_bezier_in_y) {
|
||||
all_same_bezier_in_y = (prev_key->bezier_control_in().y() ==
|
||||
this_key->bezier_control_in().y());
|
||||
all_same_bezier_in_y = (prev_key.bezier_point(0).y() ==
|
||||
this_key.bezier_point(0).y());
|
||||
}
|
||||
|
||||
if (all_same_bezier_out_x) {
|
||||
all_same_bezier_out_x = (prev_key->bezier_control_out().x() ==
|
||||
this_key->bezier_control_out().x());
|
||||
all_same_bezier_out_x = (prev_key.bezier_point(1).x() ==
|
||||
this_key.bezier_point(1).x());
|
||||
}
|
||||
|
||||
if (all_same_bezier_out_y) {
|
||||
all_same_bezier_out_y = (prev_key->bezier_control_out().y() ==
|
||||
this_key->bezier_control_out().y());
|
||||
all_same_bezier_out_y = (prev_key.bezier_point(1).y() ==
|
||||
this_key.bezier_point(1).y());
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if any keyframes are on the same track (in which case we can't set the time)
|
||||
if (can_set_time) {
|
||||
for (size_t j = 0; j < keys_.size(); j++) {
|
||||
if (i != j && keys_.at(j)->track() == keys_.at(i)->track()) {
|
||||
for (int j = 0; j < keys_.size(); j++) {
|
||||
if (i != j && keys_.at(j).track() == keys_.at(i).track()) {
|
||||
can_set_time = false;
|
||||
break;
|
||||
}
|
||||
@@ -192,7 +201,7 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(
|
||||
}
|
||||
|
||||
if (all_same_time) {
|
||||
time_slider_->set_value(keys_.front()->time());
|
||||
time_slider_->set_value(keyframe_rational_time(keys_.front()));
|
||||
} else {
|
||||
time_slider_->set_tristate();
|
||||
}
|
||||
@@ -207,14 +216,16 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(
|
||||
key_type_changed(0);
|
||||
}
|
||||
|
||||
type_select_->addItem(tr("Linear"), NodeKeyframe::k_linear);
|
||||
type_select_->addItem(tr("Hold"), NodeKeyframe::k_hold);
|
||||
type_select_->addItem(tr("Bezier"), NodeKeyframe::k_bezier);
|
||||
// Item data uses the facade easing order (oak::Keyframe::type()):
|
||||
// 0 = linear, 1 = bezier, 2 = hold.
|
||||
type_select_->addItem(tr("Linear"), KeyframeTypes::k_facade_linear);
|
||||
type_select_->addItem(tr("Hold"), KeyframeTypes::k_facade_hold);
|
||||
type_select_->addItem(tr("Bezier"), KeyframeTypes::k_facade_bezier);
|
||||
|
||||
if (all_same_type) {
|
||||
// If all keyframes are the same type, set it here
|
||||
for (int i = 0; i < type_select_->count(); i++) {
|
||||
if (type_select_->itemData(i).toInt() == keys_.front()->type()) {
|
||||
if (type_select_->itemData(i).toInt() == keys_.front().type()) {
|
||||
type_select_->setCurrentIndex(i);
|
||||
|
||||
// Ensure UI updates for this index
|
||||
@@ -225,13 +236,13 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(
|
||||
}
|
||||
|
||||
set_up_bezier_slider(bezier_in_x_slider_, all_same_bezier_in_x,
|
||||
keys_.front()->bezier_control_in().x());
|
||||
keys_.front().bezier_point(0).x());
|
||||
set_up_bezier_slider(bezier_in_y_slider_, all_same_bezier_in_y,
|
||||
keys_.front()->bezier_control_in().y());
|
||||
keys_.front().bezier_point(0).y());
|
||||
set_up_bezier_slider(bezier_out_x_slider_, all_same_bezier_out_x,
|
||||
keys_.front()->bezier_control_out().x());
|
||||
keys_.front().bezier_point(1).x());
|
||||
set_up_bezier_slider(bezier_out_y_slider_, all_same_bezier_out_y,
|
||||
keys_.front()->bezier_control_out().y());
|
||||
keys_.front().bezier_point(1).y());
|
||||
|
||||
row++;
|
||||
|
||||
@@ -251,26 +262,20 @@ void KeyframePropertiesDialog::accept()
|
||||
const QVector<KeyGroup> groups = group_keys(keys_);
|
||||
|
||||
if (new_type > -1) {
|
||||
// Engine type (linear 0 / hold 1 / bezier 2) to the facade's
|
||||
// easing order (linear 0 / bezier 1 / hold 2).
|
||||
int facade_type = 0;
|
||||
if (new_type == NodeKeyframe::k_bezier) {
|
||||
facade_type = 1;
|
||||
} else if (new_type == NodeKeyframe::k_hold) {
|
||||
facade_type = 2;
|
||||
}
|
||||
// new_type is already in the facade's easing order (linear 0 /
|
||||
// bezier 1 / hold 2), so it can be passed straight through.
|
||||
foreach (const KeyGroup &g, groups) {
|
||||
oakengine_node_keyframes_set_type_many(
|
||||
reinterpret_cast<OakEngineNode *>(g.node),
|
||||
g.node,
|
||||
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
||||
g.tracks.data(), g.times.size(), facade_type);
|
||||
g.tracks.data(), g.times.size(), new_type);
|
||||
}
|
||||
}
|
||||
|
||||
if (bezier_group_->isEnabled()) {
|
||||
foreach (const KeyGroup &g, groups) {
|
||||
oakengine_node_keyframes_set_bezier_many(
|
||||
reinterpret_cast<OakEngineNode *>(g.node),
|
||||
g.node,
|
||||
g.input.toUtf8().constData(), g.element, g.times.constData(),
|
||||
g.tracks.data(), g.times.size(),
|
||||
bezier_in_x_slider_->get_value(),
|
||||
@@ -285,8 +290,7 @@ void KeyframePropertiesDialog::accept()
|
||||
// the times the groups were built from.
|
||||
if (time_slider_->isEnabled() && !time_slider_->is_tristate()) {
|
||||
foreach (const KeyGroup &g, groups) {
|
||||
OakEngineNode *handle =
|
||||
reinterpret_cast<OakEngineNode *>(g.node);
|
||||
OakEngineNode *handle = g.node;
|
||||
int tbn = 0, tbd = 0;
|
||||
oakengine_node_frame_time_base(handle, &tbn, &tbd);
|
||||
oakengine_node_keyframes_set_time_many(
|
||||
@@ -313,7 +317,7 @@ void KeyframePropertiesDialog::set_up_bezier_slider(FloatSlider *slider,
|
||||
void KeyframePropertiesDialog::key_type_changed(int index)
|
||||
{
|
||||
bezier_group_->setEnabled(type_select_->itemData(index) ==
|
||||
NodeKeyframe::k_bezier);
|
||||
KeyframeTypes::k_facade_bezier);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <QDialog>
|
||||
#include <QGroupBox>
|
||||
|
||||
#include "node/keyframe.h"
|
||||
#include "oakutil/oaknode.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
#include "widget/slider/rationalslider.h"
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace olive
|
||||
class KeyframePropertiesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyframePropertiesDialog(const std::vector<NodeKeyframe *> &keys,
|
||||
KeyframePropertiesDialog(const QVector<oak::Keyframe> &keys,
|
||||
const Rational &timebase,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
@@ -46,7 +46,7 @@ public slots:
|
||||
private:
|
||||
void set_up_bezier_slider(FloatSlider *slider, bool all_same, double value);
|
||||
|
||||
const std::vector<NodeKeyframe *> &keys_;
|
||||
const QVector<oak::Keyframe> &keys_;
|
||||
|
||||
Rational timebase_;
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "core.h"
|
||||
|
||||
#include "oakengine/timeline.h"
|
||||
#include "widget/timeruler/markerhandle.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -37,7 +38,7 @@ namespace olive
|
||||
#define super QDialog
|
||||
|
||||
MarkerPropertiesDialog::MarkerPropertiesDialog(
|
||||
const std::vector<TimelineMarker *> &markers, const Rational &timebase,
|
||||
const std::vector<OakEngineMarker *> &markers, const Rational &timebase,
|
||||
QWidget *parent)
|
||||
: super(parent)
|
||||
, markers_(markers)
|
||||
@@ -66,10 +67,11 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(
|
||||
}
|
||||
|
||||
if (markers.size() == 1) {
|
||||
in_slider_->set_value(markers.front()->time().in());
|
||||
const TimeRange marker_range = marker_time(markers.front());
|
||||
in_slider_->set_value(marker_range.in());
|
||||
in_slider_->set_display_type(slider::k_time);
|
||||
in_slider_->set_timebase(timebase);
|
||||
out_slider_->set_value(markers.front()->time().out());
|
||||
out_slider_->set_value(marker_range.out());
|
||||
out_slider_->set_display_type(slider::k_time);
|
||||
out_slider_->set_timebase(timebase);
|
||||
} else {
|
||||
@@ -89,9 +91,9 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(
|
||||
color_menu_ = new ColorCodingComboBox();
|
||||
layout->addWidget(color_menu_, row, 1);
|
||||
|
||||
color_menu_->set_color(markers.front()->color());
|
||||
color_menu_->set_color(marker_color(markers.front()));
|
||||
for (size_t i = 1; i < markers.size(); i++) {
|
||||
if (markers.at(i)->color() != color_menu_->get_selected_color()) {
|
||||
if (marker_color(markers.at(i)) != color_menu_->get_selected_color()) {
|
||||
color_menu_->set_color(-1);
|
||||
break;
|
||||
}
|
||||
@@ -107,9 +109,9 @@ MarkerPropertiesDialog::MarkerPropertiesDialog(
|
||||
layout->addWidget(label_edit_, row, 1);
|
||||
|
||||
// Determine what the startup label text should be
|
||||
label_edit_->setText(markers.front()->name());
|
||||
label_edit_->setText(marker_name(markers.front()));
|
||||
for (size_t i = 1; i < markers.size(); i++) {
|
||||
if (markers.at(i)->name() != label_edit_->text()) {
|
||||
if (marker_name(markers.at(i)) != label_edit_->text()) {
|
||||
label_edit_->clear();
|
||||
label_edit_->setPlaceholderText(tr("(multiple)"));
|
||||
break;
|
||||
@@ -143,8 +145,8 @@ void MarkerPropertiesDialog::accept()
|
||||
// Batch-set properties via facade (one undoable command)
|
||||
{
|
||||
QVector<OakEngineMarker *> oak_markers;
|
||||
foreach (TimelineMarker *m, markers_) {
|
||||
oak_markers.append(reinterpret_cast<OakEngineMarker *>(m));
|
||||
foreach (OakEngineMarker *m, markers_) {
|
||||
oak_markers.append(m);
|
||||
}
|
||||
int color = color_menu_->get_selected_color();
|
||||
QByteArray name_ba;
|
||||
|
||||
@@ -22,10 +22,12 @@
|
||||
#ifndef OAK_MARKERPROPERTIESDIALOG_H
|
||||
#define OAK_MARKERPROPERTIESDIALOG_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "timeline/timelinemarker.h"
|
||||
#include "oakengine/timeline.h"
|
||||
#include "widget/colorlabelmenu/colorcodingcombobox.h"
|
||||
#include "widget/slider/rationalslider.h"
|
||||
|
||||
@@ -54,14 +56,14 @@ signals:
|
||||
class MarkerPropertiesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MarkerPropertiesDialog(const std::vector<TimelineMarker *> &markers,
|
||||
MarkerPropertiesDialog(const std::vector<OakEngineMarker *> &markers,
|
||||
const Rational &timebase, QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
std::vector<TimelineMarker *> markers_;
|
||||
std::vector<OakEngineMarker *> markers_;
|
||||
|
||||
LineEditWithFocusSignal *label_edit_;
|
||||
|
||||
|
||||
@@ -26,13 +26,14 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "dialog/sequence/sequence.h"
|
||||
#include "oakutil/oaknode.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
OTIOPropertiesDialog::OTIOPropertiesDialog(const QList<Sequence *> &sequences,
|
||||
Project *active_project,
|
||||
QWidget *parent)
|
||||
OTIOPropertiesDialog::OTIOPropertiesDialog(
|
||||
const QList<OakEngineSequence *> &sequences,
|
||||
OakEngineProject *active_project, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, sequences_(sequences)
|
||||
{
|
||||
@@ -51,7 +52,7 @@ OTIOPropertiesDialog::OTIOPropertiesDialog(const QList<Sequence *> &sequences,
|
||||
|
||||
for (int i = 0; i < sequences.size(); i++) {
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem();
|
||||
Sequence *s = sequences.at(i);
|
||||
OakEngineSequence *s = sequences.at(i);
|
||||
|
||||
QWidget *item_actions = new QWidget();
|
||||
QHBoxLayout *item_actions_layout = new QHBoxLayout(item_actions);
|
||||
@@ -61,7 +62,8 @@ OTIOPropertiesDialog::OTIOPropertiesDialog(const QList<Sequence *> &sequences,
|
||||
&OTIOPropertiesDialog::SetupSequence);
|
||||
item_actions_layout->addWidget(item_settings_btn);
|
||||
|
||||
item->setText(0, s->GetLabel());
|
||||
item->setText(
|
||||
0, oak::Node(reinterpret_cast<OakEngineNode *>(s)).get_label());
|
||||
|
||||
table_->addTopLevelItem(item);
|
||||
|
||||
@@ -89,9 +91,10 @@ OTIOPropertiesDialog::OTIOPropertiesDialog(const QList<Sequence *> &sequences,
|
||||
void OTIOPropertiesDialog::SetupSequence()
|
||||
{
|
||||
int index = sender()->property("index").toInt();
|
||||
Sequence *s = sequences_.at(index);
|
||||
SequenceDialog sd(s, SequenceDialog::kNew);
|
||||
sd.SetUndoable(false);
|
||||
OakEngineSequence *s = sequences_.at(index);
|
||||
SequenceDialog sd(reinterpret_cast<OakEngineNode *>(s),
|
||||
SequenceDialog::k_new);
|
||||
sd.set_undoable(false);
|
||||
sd.exec();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
|
||||
#include "oakutil/define.h"
|
||||
#include "opentimelineio/timeline.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "node/project.h"
|
||||
|
||||
struct OakEngineProject;
|
||||
struct OakEngineSequence;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -39,13 +40,14 @@ namespace olive
|
||||
class OTIOPropertiesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OTIOPropertiesDialog(const QList<Sequence *> &sequences,
|
||||
Project *active_project, QWidget *parent = nullptr);
|
||||
OTIOPropertiesDialog(const QList<OakEngineSequence *> &sequences,
|
||||
OakEngineProject *active_project,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
QTreeWidget *table_;
|
||||
|
||||
const QList<Sequence *> sequences_;
|
||||
const QList<OakEngineSequence *> sequences_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
|
||||
@@ -27,8 +27,7 @@
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
|
||||
#include "node/node.h"
|
||||
#include "oakengine/node.h"
|
||||
#include "oakutil/oaknode.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -69,10 +68,8 @@ PreferencesAppearanceTab::PreferencesAppearanceTab()
|
||||
|
||||
QGridLayout *color_layout = new QGridLayout(color_group);
|
||||
|
||||
for (int i = 0; i < Node::k_category_count; i++) {
|
||||
char cat_buf[256];
|
||||
oakengine_node_category_name(i, cat_buf, sizeof(cat_buf));
|
||||
QString cat_name = QString::fromUtf8(cat_buf);
|
||||
for (int i = 0; i < oak::k_category_count; i++) {
|
||||
QString cat_name = oak::Node::category_name(i);
|
||||
color_layout->addWidget(new QLabel(cat_name), i, 0);
|
||||
|
||||
ColorCodingComboBox *ccc = new ColorCodingComboBox();
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "preferencesgeneraltab.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "dialog/configbase/configdialogbase.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "widget/slider/rationalslider.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
|
||||
|
||||
@@ -33,8 +33,10 @@ PluginProgressDialogReporter::PluginProgressDialogReporter(
|
||||
dialog_->setAttribute(Qt::WA_DeleteOnClose);
|
||||
QObject::connect(dialog_, &ProgressDialog::cancelled, dialog_, [this]() {
|
||||
cancelled_ = true;
|
||||
set_cancelled();
|
||||
});
|
||||
// The engine's C adapter treats show() as a no-op, so the dialog is
|
||||
// shown here at create time.
|
||||
dialog_->show();
|
||||
}
|
||||
|
||||
PluginProgressDialogReporter::~PluginProgressDialogReporter()
|
||||
@@ -49,18 +51,4 @@ void PluginProgressDialogReporter::set_progress(double value)
|
||||
}
|
||||
}
|
||||
|
||||
void PluginProgressDialogReporter::show()
|
||||
{
|
||||
if (dialog_) {
|
||||
dialog_->show();
|
||||
}
|
||||
}
|
||||
|
||||
void PluginProgressDialogReporter::close()
|
||||
{
|
||||
if (dialog_) {
|
||||
dialog_->close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,32 +24,27 @@
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
#include "pluginSupport/pluginprogressreporter.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ProgressDialog;
|
||||
|
||||
/**
|
||||
* @brief PluginProgressReporter implementation wrapping a ProgressDialog
|
||||
* @brief ProgressDialog wrapper created through the plugin progress
|
||||
* reporter factory
|
||||
*
|
||||
* UI-side reporter created through the plugin progress reporter factory.
|
||||
* Forwards progress calls to the dialog and the dialog's cancelled() signal
|
||||
* back to the engine. The dialog is deleted on close; the reporter itself
|
||||
* is destroyed by the engine with deleteLater().
|
||||
* Plain UI-side class; the engine only ever talks to it through the C
|
||||
* callbacks registered with oakengine_plugin_set_progress_reporter_factory()
|
||||
* (create/destroy/is_cancelled/set_progress, see engine/src/capi/plugin.cpp).
|
||||
* The dialog is shown on construction and deleted with the reporter.
|
||||
*/
|
||||
class PluginProgressDialogReporter : public plugin::PluginProgressReporter {
|
||||
class PluginProgressDialogReporter {
|
||||
public:
|
||||
PluginProgressDialogReporter(const QString &message, const QString &title);
|
||||
|
||||
virtual ~PluginProgressDialogReporter() override;
|
||||
~PluginProgressDialogReporter();
|
||||
|
||||
virtual void set_progress(double value) override;
|
||||
|
||||
virtual void show() override;
|
||||
|
||||
virtual void close() override;
|
||||
void set_progress(double value);
|
||||
|
||||
bool was_cancelled() const { return cancelled_; }
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <QPushButton>
|
||||
|
||||
#include "oakutil/filefunctions.h"
|
||||
#include "oakutil/qtutils.h"
|
||||
#include "common/projecttypes.h"
|
||||
#include "oakengine/color.h"
|
||||
#include "oakengine/disk.h"
|
||||
#include "oakengine/project.h"
|
||||
@@ -40,7 +42,8 @@ namespace olive
|
||||
|
||||
#define super QDialog
|
||||
|
||||
ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
||||
ProjectPropertiesDialog::ProjectPropertiesDialog(OakEngineProject *p,
|
||||
QWidget *parent)
|
||||
: super(parent)
|
||||
, working_project_(p)
|
||||
, ocio_config_is_valid_(true)
|
||||
@@ -49,7 +52,7 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
||||
|
||||
char name_buf[256];
|
||||
oakengine_project_name(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||
working_project_,
|
||||
name_buf, sizeof(name_buf));
|
||||
setWindowTitle(
|
||||
tr("Project Properties for '%1'").arg(name_buf));
|
||||
@@ -88,17 +91,20 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
||||
color_layout->addWidget(new QLabel(tr("Reference Space:")), row, 0);
|
||||
|
||||
reference_space_ = new QComboBox(this);
|
||||
reference_space_->addItem(tr("Scene Linear"), ocio::ROLE_SCENE_LINEAR);
|
||||
// OpenColorIO role names (ocio::ROLE_SCENE_LINEAR /
|
||||
// ocio::ROLE_COMPOSITING_LOG); mirrored as literals because the OCIO
|
||||
// headers only reached this file transitively through the engine.
|
||||
reference_space_->addItem(tr("Scene Linear"),
|
||||
QStringLiteral("scene_linear"));
|
||||
reference_space_->addItem(tr("Compositing Log"),
|
||||
ocio::ROLE_COMPOSITING_LOG);
|
||||
QStringLiteral("compositing_log"));
|
||||
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);
|
||||
}());
|
||||
[p]() -> QString {
|
||||
char buf[256];
|
||||
oakengine_project_get_color_reference_space(
|
||||
p, buf, sizeof(buf));
|
||||
return QString::fromUtf8(buf);
|
||||
}());
|
||||
color_layout->addWidget(reference_space_, row, 1, 1, 2);
|
||||
|
||||
row++;
|
||||
@@ -109,7 +115,7 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
||||
&ProjectPropertiesDialog::browse_for_ocio_config);
|
||||
|
||||
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_));
|
||||
working_project_);
|
||||
ocio_filename_->setText(oak_query_string([cm](char *buf, int size) {
|
||||
return oakengine_color_manager_get_config_filename(cm, buf, size);
|
||||
}));
|
||||
@@ -149,7 +155,7 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
||||
[this]() -> QString {
|
||||
char buf[4096];
|
||||
oakengine_project_get_custom_cache_path(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||
working_project_,
|
||||
buf, sizeof(buf));
|
||||
return QString::fromUtf8(buf);
|
||||
}(), this);
|
||||
@@ -163,7 +169,7 @@ ProjectPropertiesDialog::ProjectPropertiesDialog(Project *p, QWidget *parent)
|
||||
|
||||
// Check the radio button that should currently be active
|
||||
disk_cache_radios_[oakengine_project_get_cache_location_setting(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_))]
|
||||
working_project_)]
|
||||
->setChecked(true);
|
||||
|
||||
// Add disk cache settings button
|
||||
@@ -208,7 +214,7 @@ void ProjectPropertiesDialog::accept()
|
||||
[this]() -> QString {
|
||||
char buf[4096];
|
||||
oakengine_project_cache_alongside_path(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||
working_project_,
|
||||
buf, sizeof(buf));
|
||||
return QString::fromUtf8(buf);
|
||||
}())) {
|
||||
@@ -225,7 +231,7 @@ void ProjectPropertiesDialog::accept()
|
||||
[this]() -> QString {
|
||||
char buf[4096];
|
||||
oakengine_project_get_custom_cache_path(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||
working_project_,
|
||||
buf, sizeof(buf));
|
||||
return QString::fromUtf8(buf);
|
||||
}()) {
|
||||
@@ -235,17 +241,17 @@ void ProjectPropertiesDialog::accept()
|
||||
}
|
||||
|
||||
oakengine_project_set_custom_cache_path(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||
working_project_,
|
||||
custom_cache_path_->text().toUtf8().constData());
|
||||
|
||||
oakengine_disk_invalidate_project(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_));
|
||||
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
|
||||
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_));
|
||||
working_project_);
|
||||
QString old_config = oak_query_string([cm](char *buf, int size) {
|
||||
return oakengine_color_manager_get_config_filename(cm, buf, size);
|
||||
});
|
||||
@@ -263,12 +269,12 @@ void ProjectPropertiesDialog::accept()
|
||||
if ([this]() -> QString {
|
||||
char buf[256];
|
||||
oakengine_project_get_color_reference_space(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||
working_project_,
|
||||
buf, sizeof(buf));
|
||||
return QString::fromUtf8(buf);
|
||||
}() != reference_space_->currentData().toString()) {
|
||||
oakengine_project_set_color_reference_space(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||
working_project_,
|
||||
reference_space_->currentData().toString().toUtf8().constData());
|
||||
}
|
||||
|
||||
@@ -321,7 +327,7 @@ void ProjectPropertiesDialog::ocio_filename_updated()
|
||||
// List input color spaces
|
||||
int cs_count = oakengine_color_config_colorspace_count(config);
|
||||
OakEngineColorManager *cm = oakengine_color_manager_from_project(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_));
|
||||
working_project_);
|
||||
QString default_cs = oak_query_string([cm](char *buf, int size) {
|
||||
return oakengine_color_manager_default_input_color_space(cm, buf,
|
||||
size);
|
||||
@@ -361,7 +367,7 @@ void ProjectPropertiesDialog::open_disk_cache_settings()
|
||||
[this]() -> QString {
|
||||
char buf[4096];
|
||||
oakengine_project_cache_alongside_path(
|
||||
reinterpret_cast<OakEngineProject *>(working_project_),
|
||||
working_project_,
|
||||
buf, sizeof(buf));
|
||||
return QString::fromUtf8(buf);
|
||||
}().toUtf8().constData(), this);
|
||||
|
||||
@@ -29,16 +29,17 @@
|
||||
#include <QLineEdit>
|
||||
#include <QRadioButton>
|
||||
|
||||
#include "node/project.h"
|
||||
#include "widget/path/pathwidget.h"
|
||||
|
||||
struct OakEngineProject;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ProjectPropertiesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProjectPropertiesDialog(Project *p, QWidget *parent);
|
||||
ProjectPropertiesDialog(OakEngineProject *p, QWidget *parent);
|
||||
|
||||
public slots:
|
||||
virtual void accept() override;
|
||||
@@ -46,7 +47,7 @@ public slots:
|
||||
private:
|
||||
bool verify_path_and_warn_if_bad(const QString &path);
|
||||
|
||||
Project *working_project_;
|
||||
OakEngineProject *working_project_;
|
||||
|
||||
QLineEdit *ocio_filename_;
|
||||
|
||||
|
||||
@@ -30,13 +30,14 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "common/configwrapper.h"
|
||||
#include "node/project.h"
|
||||
#include "oakengine/project.h"
|
||||
#include "oakengine/timeline.h"
|
||||
#include "oakutil/oaknode.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
ProxyDialog::ProxyDialog(QWidget *parent, const QVector<Footage *> &footage)
|
||||
ProxyDialog::ProxyDialog(QWidget *parent, const QVector<OakEngineNode *> &footage)
|
||||
: QDialog(parent)
|
||||
, footage_(footage)
|
||||
, footage_tree_(nullptr)
|
||||
@@ -63,8 +64,10 @@ ProxyDialog::ProxyDialog(QWidget *parent, const QVector<Footage *> &footage)
|
||||
custom_params_checkbox_ =
|
||||
new QCheckBox(tr("Use custom settings for selected footage"));
|
||||
bool any_custom = false;
|
||||
for (const Footage *item : footage_) {
|
||||
if (item->has_custom_proxy_params()) {
|
||||
for (OakEngineNode *item : footage_) {
|
||||
// WRAPPER-GAP: oak::Footage::has_custom_proxy_params
|
||||
oak::Footage f = oak::Footage::borrow(item);
|
||||
if (f && oakengine_footage_has_custom_proxy_params(f.handle())) {
|
||||
any_custom = true;
|
||||
break;
|
||||
}
|
||||
@@ -186,14 +189,17 @@ void ProxyDialog::accept()
|
||||
save_global_settings();
|
||||
|
||||
if (!footage_.isEmpty()) {
|
||||
for (Footage *item : footage_) {
|
||||
for (OakEngineNode *item : footage_) {
|
||||
// WRAPPER-GAP: oak::Footage::{set,clear}_custom_proxy_params
|
||||
oak::Footage f = oak::Footage::borrow(item);
|
||||
if (!f) {
|
||||
continue;
|
||||
}
|
||||
if (custom_params_checkbox_->isChecked()) {
|
||||
oak_proxy_params p = current_params();
|
||||
oakengine_footage_set_custom_proxy_params(
|
||||
reinterpret_cast<OakEngineFootage *>(item), &p);
|
||||
oakengine_footage_set_custom_proxy_params(f.handle(), &p);
|
||||
} else {
|
||||
oakengine_footage_clear_custom_proxy_params(
|
||||
reinterpret_cast<OakEngineFootage *>(item));
|
||||
oakengine_footage_clear_custom_proxy_params(f.handle());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,17 +313,20 @@ void ProxyDialog::refresh_footage_list()
|
||||
}
|
||||
|
||||
footage_tree_->clear();
|
||||
for (const Footage *item : footage_) {
|
||||
for (OakEngineNode *item : footage_) {
|
||||
// WRAPPER-GAP: oak::Footage::{proxy_state,has_custom_proxy_params}
|
||||
oak::Footage f = oak::Footage::borrow(item);
|
||||
QTreeWidgetItem *tree_item = new QTreeWidgetItem(footage_tree_);
|
||||
tree_item->setText(0, item->filename());
|
||||
tree_item->setText(0, f.filename());
|
||||
{
|
||||
char state_buf[256];
|
||||
int state_len = oakengine_proxy_state_to_string(
|
||||
item->proxy_state(), state_buf, sizeof(state_buf));
|
||||
oakengine_footage_proxy_get_state(f.handle()), state_buf,
|
||||
sizeof(state_buf));
|
||||
QString state = (state_len > 0)
|
||||
? QString::fromUtf8(state_buf, state_len)
|
||||
: QString();
|
||||
if (item->has_custom_proxy_params()) {
|
||||
if (oakengine_footage_has_custom_proxy_params(f.handle())) {
|
||||
state = tr("%1 (custom settings)").arg(state);
|
||||
}
|
||||
tree_item->setText(1, state);
|
||||
@@ -327,15 +336,30 @@ void ProxyDialog::refresh_footage_list()
|
||||
|
||||
void ProxyDialog::generate_proxies()
|
||||
{
|
||||
for (Footage *item : footage_) {
|
||||
const VideoParams video = item->get_first_enabled_video_stream();
|
||||
for (OakEngineNode *item : footage_) {
|
||||
// WRAPPER-GAP: oak::Footage::{get_effective_proxy_params,set_proxy}
|
||||
oak::Footage f = oak::Footage::borrow(item);
|
||||
oak_video_params _vp;
|
||||
oakengine_viewer_get_first_enabled_video_stream(
|
||||
reinterpret_cast<OakEngineNode *>(item), &_vp);
|
||||
oakengine_viewer_get_first_enabled_video_stream(item, &_vp);
|
||||
if (!oakengine_video_params_is_valid(&_vp)) {
|
||||
qWarning()
|
||||
<< "ProxyDialog::GenerateProxies: skipping item with no valid video stream"
|
||||
<< item->filename();
|
||||
<< f.filename();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Index of the first enabled video stream
|
||||
// (VideoParams::stream_index() has no facade equivalent).
|
||||
int stream_index = -1;
|
||||
const QVector<QPair<int, int>> streams =
|
||||
oak::Node(item).enabled_streams();
|
||||
for (const QPair<int, int> &s : streams) {
|
||||
if (s.first == OAKENGINE_TRACK_TYPE_VIDEO) {
|
||||
stream_index = s.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (stream_index < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -343,27 +367,23 @@ void ProxyDialog::generate_proxies()
|
||||
if (custom_params_checkbox_->isChecked()) {
|
||||
params = current_params();
|
||||
} else {
|
||||
oakengine_footage_get_effective_proxy_params(
|
||||
reinterpret_cast<OakEngineFootage *>(item), ¶ms);
|
||||
oakengine_footage_get_effective_proxy_params(f.handle(), ¶ms);
|
||||
}
|
||||
oak_proxy_result proxy;
|
||||
char cache_buf[512];
|
||||
oakengine_project_cache_path(
|
||||
reinterpret_cast<OakEngineProject *>(item->project()),
|
||||
cache_buf, sizeof(cache_buf));
|
||||
oakengine_project_cache_path(oakengine_node_get_project(item),
|
||||
cache_buf, sizeof(cache_buf));
|
||||
int ret = oakengine_proxy_get_or_start(
|
||||
cache_buf,
|
||||
item->filename().toUtf8().constData(),
|
||||
video.stream_index(), ¶ms, &proxy);
|
||||
cache_buf, f.filename().toUtf8().constData(), stream_index,
|
||||
¶ms, &proxy);
|
||||
if (ret != 0) {
|
||||
qWarning() << "ProxyDialog::GenerateProxies: failed to get/start proxy for"
|
||||
<< item->filename();
|
||||
<< f.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));
|
||||
oakengine_footage_set_proxy(f.handle(), proxy.filename, proxy.state,
|
||||
stream_index, 1, params.version);
|
||||
f.invalidate();
|
||||
}
|
||||
|
||||
refresh_footage_list();
|
||||
@@ -371,22 +391,24 @@ void ProxyDialog::generate_proxies()
|
||||
|
||||
void ProxyDialog::delete_proxies()
|
||||
{
|
||||
for (Footage *item : footage_) {
|
||||
if (item->proxy_path().isEmpty()) {
|
||||
for (OakEngineNode *item : footage_) {
|
||||
// WRAPPER-GAP: oak::Footage::clear_proxy
|
||||
oak::Footage f = oak::Footage::borrow(item);
|
||||
if (f.proxy_path().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QFile::remove(item->proxy_path());
|
||||
QFile::remove(f.proxy_path());
|
||||
{
|
||||
char wbuf[4096];
|
||||
int wlen = oakengine_proxy_get_working_filename(
|
||||
item->proxy_path().toUtf8().constData(), wbuf, sizeof(wbuf));
|
||||
f.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));
|
||||
oakengine_footage_clear_proxy(f.handle());
|
||||
f.invalidate();
|
||||
}
|
||||
|
||||
refresh_footage_list();
|
||||
|
||||
@@ -29,16 +29,17 @@
|
||||
#include "oakengine/proxy.h"
|
||||
#include "oakengine/videoparams.h"
|
||||
#include "oakengine/viewer.h"
|
||||
#include "node/project/footage/footage.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ProxyDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProxyDialog(QWidget *parent, const QVector<Footage *> &footage = {});
|
||||
ProxyDialog(QWidget *parent, const QVector<OakEngineNode *> &footage = {});
|
||||
|
||||
virtual void accept() override;
|
||||
|
||||
@@ -77,7 +78,7 @@ private:
|
||||
|
||||
void refresh_footage_list();
|
||||
|
||||
QVector<Footage *> footage_;
|
||||
QVector<OakEngineNode *> footage_;
|
||||
|
||||
QTreeWidget *footage_tree_;
|
||||
|
||||
|
||||
@@ -36,11 +36,12 @@
|
||||
#include "oakengine/node.h"
|
||||
#include "oakengine/timeline.h"
|
||||
#include "oakengine/videoparams.h"
|
||||
#include "oakutil/oaknode.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
SequenceDialog::SequenceDialog(Sequence *s, Type t, QWidget *parent)
|
||||
SequenceDialog::SequenceDialog(OakEngineNode *s, Type t, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, sequence_(s)
|
||||
, make_undoable_(true)
|
||||
@@ -89,11 +90,12 @@ SequenceDialog::SequenceDialog(Sequence *s, Type t, QWidget *parent)
|
||||
setWindowTitle(tr("New Sequence"));
|
||||
break;
|
||||
case k_existing:
|
||||
setWindowTitle(tr("Editing \"%1\"").arg(sequence_->get_label()));
|
||||
setWindowTitle(
|
||||
tr("Editing \"%1\"").arg(oak::Node(sequence_).get_label()));
|
||||
break;
|
||||
}
|
||||
|
||||
name_field_->setText(sequence_->get_label());
|
||||
name_field_->setText(oak::Node(sequence_).get_label());
|
||||
}
|
||||
|
||||
void SequenceDialog::set_undoable(bool u)
|
||||
|
||||
@@ -26,10 +26,11 @@
|
||||
#include <QDialog>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "sequencedialogparametertab.h"
|
||||
#include "sequencedialogpresettab.h"
|
||||
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
@@ -67,7 +68,7 @@ public:
|
||||
* @param parent
|
||||
* QWidget parent
|
||||
*/
|
||||
SequenceDialog(Sequence *s, Type t = k_existing, QWidget *parent = nullptr);
|
||||
SequenceDialog(OakEngineNode *s, Type t = k_existing, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Set whether the parameter changes should be made into an undo command or not
|
||||
@@ -90,7 +91,7 @@ public slots:
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
Sequence *sequence_;
|
||||
OakEngineNode *sequence_;
|
||||
|
||||
SequenceDialogPresetTab *preset_tab_;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
namespace olive
|
||||
{
|
||||
|
||||
SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence *sequence,
|
||||
SequenceDialogParameterTab::SequenceDialogParameterTab(OakEngineNode *sequence,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
|
||||
@@ -24,18 +24,20 @@
|
||||
#include <QList>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "sequencepreset.h"
|
||||
#include "widget/slider/integerslider.h"
|
||||
#include "widget/standardcombos/standardcombos.h"
|
||||
|
||||
struct OakEngineNode;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class SequenceDialogParameterTab : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SequenceDialogParameterTab(Sequence *sequence, QWidget *parent = nullptr);
|
||||
SequenceDialogParameterTab(OakEngineNode *sequence,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
int get_selected_video_width() const
|
||||
{
|
||||
|
||||
@@ -27,11 +27,14 @@
|
||||
|
||||
#include "oakutil/xmlutils.h"
|
||||
#include "dialog/sequence/presetmanager.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
// Same namespace bridge the engine's render/videoparams.h used to provide
|
||||
// (unqualified Rational/PixelFormat inside namespace olive).
|
||||
using namespace core;
|
||||
|
||||
class SequencePreset : public Preset {
|
||||
public:
|
||||
SequencePreset() = default;
|
||||
|
||||
@@ -27,22 +27,63 @@
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "core.h"
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "oakengine/preview.h"
|
||||
#include "oakengine/timeline.h"
|
||||
#include "oakengine/node.h"
|
||||
#include "oakengine/undo.h"
|
||||
#include "timeline/timelinecommonapp.h"
|
||||
#include "widget/timelinewidget/cliphandle.h"
|
||||
#include "timeline/timelineundopointer.h"
|
||||
#include "timeline/timelinecommon.h"
|
||||
#include "timeline/timelineundopointer.h"
|
||||
#include "timeline/timelineundoripple.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
#define super QDialog
|
||||
|
||||
SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
||||
namespace
|
||||
{
|
||||
|
||||
/// Block::length() as rational seconds (C ABI facade).
|
||||
inline Rational sdd_block_length(const OakEngineBlock *block)
|
||||
{
|
||||
int num = 0, den = 1;
|
||||
oakengine_block_get_length_rational(
|
||||
reinterpret_cast<const OakEngineNode *>(block), &num, &den);
|
||||
return Rational(num, den);
|
||||
}
|
||||
|
||||
/// Block::in() as rational seconds.
|
||||
inline Rational sdd_block_in(const OakEngineBlock *block)
|
||||
{
|
||||
int num = 0, den = 1;
|
||||
oakengine_block_get_in_rational(
|
||||
reinterpret_cast<const OakEngineNode *>(block), &num, &den);
|
||||
return Rational(num, den);
|
||||
}
|
||||
|
||||
/// Block::out() as rational seconds.
|
||||
inline Rational sdd_block_out(const OakEngineBlock *block)
|
||||
{
|
||||
int num = 0, den = 1;
|
||||
oakengine_block_get_out_rational(
|
||||
reinterpret_cast<const OakEngineNode *>(block), &num, &den);
|
||||
return Rational(num, den);
|
||||
}
|
||||
|
||||
/// Block::next() as a block handle.
|
||||
inline OakEngineBlock *sdd_block_next(OakEngineBlock *block)
|
||||
{
|
||||
return oakengine_block_next(block);
|
||||
}
|
||||
|
||||
/// ClipBlock::track() as an opaque track handle.
|
||||
inline OakEngineTrack *sdd_clip_track(OakEngineBlock *clip)
|
||||
{
|
||||
return oakengine_block_get_track(clip);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SpeedDurationDialog::SpeedDurationDialog(const QVector<OakEngineBlock *> &clips,
|
||||
const Rational &timebase,
|
||||
QWidget *parent)
|
||||
: super(parent)
|
||||
@@ -113,9 +154,9 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
||||
loop_layout->addWidget(new QLabel(tr("Loop:")), row, 0);
|
||||
|
||||
loop_combo_ = new QComboBox();
|
||||
loop_combo_->addItem(tr("None"), int(LoopMode::k_loop_mode_off));
|
||||
loop_combo_->addItem(tr("Loop"), int(LoopMode::k_loop_mode_loop));
|
||||
loop_combo_->addItem(tr("Clamp"), int(LoopMode::k_loop_mode_clamp));
|
||||
loop_combo_->addItem(tr("None"), OAKENGINE_LOOP_MODE_OFF);
|
||||
loop_combo_->addItem(tr("Loop"), OAKENGINE_LOOP_MODE_LOOP);
|
||||
loop_combo_->addItem(tr("Clamp"), OAKENGINE_LOOP_MODE_CLAMP);
|
||||
loop_layout->addWidget(loop_combo_, row, 1);
|
||||
}
|
||||
|
||||
@@ -130,19 +171,19 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
||||
|
||||
// Determine which speed value to use
|
||||
start_speed_ = clip_speed(clips.first());
|
||||
start_duration_ = clips.first()->length();
|
||||
start_duration_ = sdd_block_length(clips.first());
|
||||
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);
|
||||
OakEngineBlock *c = clips.at(i);
|
||||
|
||||
if (!qIsNaN(start_speed_) && !qFuzzyCompare(start_speed_, clip_speed(c))) {
|
||||
// Speed differs per clip
|
||||
start_speed_ = qSNaN();
|
||||
}
|
||||
|
||||
if (start_duration_ != -1 && c->length() != start_duration_) {
|
||||
if (start_duration_ != -1 && sdd_block_length(c) != start_duration_) {
|
||||
start_duration_ = -1;
|
||||
}
|
||||
|
||||
@@ -204,9 +245,10 @@ void SpeedDurationDialog::accept()
|
||||
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());
|
||||
foreach (OakEngineBlock *c, clips_) {
|
||||
double speed = get_speed_adjustment(clip_speed(c),
|
||||
sdd_block_length(c),
|
||||
dur_slider_->get_value());
|
||||
oak_node_value val;
|
||||
memset(&val, 0, sizeof(val));
|
||||
val.type = OAK_NODE_VALUE_FLOAT;
|
||||
@@ -218,7 +260,7 @@ void SpeedDurationDialog::accept()
|
||||
}
|
||||
} else {
|
||||
// Set speeds to value of slider
|
||||
foreach (ClipBlock *c, clips_) {
|
||||
foreach (OakEngineBlock *c, clips_) {
|
||||
oak_node_value val;
|
||||
memset(&val, 0, sizeof(val));
|
||||
val.type = OAK_NODE_VALUE_FLOAT;
|
||||
@@ -230,14 +272,18 @@ void SpeedDurationDialog::accept()
|
||||
}
|
||||
|
||||
// Set duration values (undoable via facade)
|
||||
TimelineRippleDeleteGapsAtRegionsCommand::RangeList ripple_ranges;
|
||||
// (track handle, range) pairs fed to
|
||||
// oakengine_timeline_ripple_delete_gaps_command below; was
|
||||
// TimelineRippleDeleteGapsAtRegionsCommand::RangeList.
|
||||
QVector<QPair<void *, TimeRange>> ripple_ranges;
|
||||
|
||||
foreach (ClipBlock *c, clips_) {
|
||||
Rational proposed_length = c->length();
|
||||
foreach (OakEngineBlock *c, clips_) {
|
||||
Rational proposed_length = sdd_block_length(c);
|
||||
|
||||
if (dur_slider_->is_tristate()) {
|
||||
if (link_box_->isChecked() && !speed_slider_->is_tristate()) {
|
||||
proposed_length = get_length_adjustment(c->length(), clip_speed(c),
|
||||
proposed_length = get_length_adjustment(sdd_block_length(c),
|
||||
clip_speed(c),
|
||||
speed_slider_->get_value(),
|
||||
timebase_);
|
||||
}
|
||||
@@ -245,40 +291,44 @@ void SpeedDurationDialog::accept()
|
||||
proposed_length = dur_slider_->get_value();
|
||||
}
|
||||
|
||||
if (proposed_length != c->length()) {
|
||||
if (proposed_length != sdd_block_length(c)) {
|
||||
// Clip length should ideally change, but check if there's "room" to do so
|
||||
if (proposed_length > c->length() && c->next()) {
|
||||
if (GapBlock *gap = dynamic_cast<GapBlock *>(c->next())) {
|
||||
if (proposed_length > sdd_block_length(c) && sdd_block_next(c)) {
|
||||
// Gap check via the C ABI (GapBlock's type used to ride in
|
||||
// with the removed timelineundo headers)
|
||||
if (oakengine_block_is_gap(sdd_block_next(c))) {
|
||||
proposed_length =
|
||||
qMin(proposed_length, gap->out() - c->in());
|
||||
qMin(proposed_length,
|
||||
sdd_block_out(sdd_block_next(c)) -
|
||||
sdd_block_in(c));
|
||||
} else {
|
||||
proposed_length = c->length();
|
||||
proposed_length = sdd_block_length(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (proposed_length != c->length()) {
|
||||
if (proposed_length != sdd_block_length(c)) {
|
||||
// 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 *>(sdd_clip_track(c)),
|
||||
reinterpret_cast<void *>(c),
|
||||
proposed_length.numerator(),
|
||||
proposed_length.denominator(),
|
||||
olive::Timeline::k_trim_out, 0),
|
||||
TimelineApp::k_trim_out, 0),
|
||||
tr("Trim Clip").toUtf8().constData());
|
||||
ripple_ranges.append(
|
||||
{ c->track(),
|
||||
TimeRange(c->in() + proposed_length, c->out()) });
|
||||
{ reinterpret_cast<void *>(sdd_clip_track(c)),
|
||||
TimeRange(sdd_block_in(c) + proposed_length,
|
||||
sdd_block_out(c)) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ripple_box_->isChecked() && !ripple_ranges.isEmpty()) {
|
||||
Sequence *seq = reinterpret_cast<Sequence *>(
|
||||
oakengine_clip_get_sequence(
|
||||
reinterpret_cast<OakEngineClip *>(clips_.first())));
|
||||
OakEngineSequence *seq = oakengine_clip_get_sequence(
|
||||
reinterpret_cast<OakEngineClip *>(clips_.first()));
|
||||
if (seq) {
|
||||
QVector<int64_t> range_in_ts;
|
||||
QVector<int64_t> range_out_ts;
|
||||
@@ -292,8 +342,10 @@ void SpeedDurationDialog::accept()
|
||||
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_track_types.append(oakengine_track_get_type(
|
||||
reinterpret_cast<const OakEngineNode *>(range.first)));
|
||||
range_track_indexes.append(oakengine_track_get_index(
|
||||
reinterpret_cast<const OakEngineNode *>(range.first)));
|
||||
range_in_ts.append(olive::core::Timecode::time_to_timestamp(
|
||||
range.second.in(), olive::Rational(tbn, tbd),
|
||||
olive::core::Timecode::k_round));
|
||||
@@ -314,7 +366,7 @@ void SpeedDurationDialog::accept()
|
||||
|
||||
// Set reverse values
|
||||
if (!reverse_box_->isTristate()) {
|
||||
foreach (ClipBlock *c, clips_) {
|
||||
foreach (OakEngineBlock *c, clips_) {
|
||||
oak_node_value val;
|
||||
memset(&val, 0, sizeof(val));
|
||||
val.type = OAK_NODE_VALUE_BOOL;
|
||||
@@ -327,7 +379,7 @@ void SpeedDurationDialog::accept()
|
||||
|
||||
// Set maintain audio pitch values
|
||||
if (!maintain_audio_pitch_box_->isTristate()) {
|
||||
foreach (ClipBlock *c, clips_) {
|
||||
foreach (OakEngineBlock *c, clips_) {
|
||||
oak_node_value val;
|
||||
memset(&val, 0, sizeof(val));
|
||||
val.type = OAK_NODE_VALUE_BOOL;
|
||||
@@ -339,7 +391,7 @@ void SpeedDurationDialog::accept()
|
||||
}
|
||||
|
||||
if (loop_combo_->currentIndex() != -1) {
|
||||
foreach (ClipBlock *c, clips_) {
|
||||
foreach (OakEngineBlock *c, clips_) {
|
||||
oak_node_value val;
|
||||
memset(&val, 0, sizeof(val));
|
||||
val.type = OAK_NODE_VALUE_INT;
|
||||
|
||||
@@ -26,18 +26,16 @@
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "oakengine/timeline.h"
|
||||
#include "widget/slider/floatslider.h"
|
||||
#include "widget/slider/rationalslider.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class ClipBlock;
|
||||
|
||||
class SpeedDurationDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SpeedDurationDialog(const QVector<ClipBlock *> &clips,
|
||||
explicit SpeedDurationDialog(const QVector<OakEngineBlock *> &clips,
|
||||
const Rational &timebase,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
@@ -55,7 +53,7 @@ private:
|
||||
const Rational &original_length,
|
||||
const Rational &new_length);
|
||||
|
||||
QVector<ClipBlock *> clips_;
|
||||
QVector<OakEngineBlock *> clips_;
|
||||
|
||||
FloatSlider *speed_slider_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user