engine: sequence parameters dialog migrates to the facade
- new facade API: video params ex (dimensions/rate/par/interlacing/ preview format/divider), audio params, preview divider, and undoable-flagged setters mirroring the dialog's dual undo/no-undo modes; label setting gains node_set_label_ex - the dialog's own SequenceParamCommand is gone; accept now issues facade calls (per-field commands, unchanged fields skipped) - preset system stays UI-side by design: its flat XML schema never touches engine objects - the auto-cache checkbox maps to the engine's existing stub (no undo noise)
This commit is contained in:
@@ -31,10 +31,10 @@
|
|||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "dialog/msgbox.h"
|
#include "dialog/msgbox.h"
|
||||||
#include "undo/undostack.h"
|
#include "oakengine/node.h"
|
||||||
|
#include "oakengine/timeline.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -139,40 +139,36 @@ void SequenceDialog::accept()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate video and audio parameter structs from data
|
// All parameter writes go through the liboakengine C ABI facade; in
|
||||||
VideoParams video_params =
|
// undoable mode each call lands on the shared undo stack as one command,
|
||||||
VideoParams(parameter_tab_->get_selected_video_width(),
|
// otherwise it applies directly (the flag mirrors the old
|
||||||
parameter_tab_->get_selected_video_height(),
|
// SequenceParamCommand / direct-set split).
|
||||||
parameter_tab_->get_selected_video_frame_rate().flipped(),
|
const int undoable = make_undoable_ ? 1 : 0;
|
||||||
parameter_tab_->get_selected_preview_format(),
|
OakEngineSequence *facade_handle =
|
||||||
VideoParams::k_internal_channel_count,
|
reinterpret_cast<OakEngineSequence *>(sequence_);
|
||||||
parameter_tab_->get_selected_video_pixel_aspect(),
|
const Rational frame_rate = parameter_tab_->get_selected_video_frame_rate();
|
||||||
parameter_tab_->get_selected_video_interlacing_mode(),
|
const Rational pixel_aspect =
|
||||||
parameter_tab_->get_selected_preview_resolution());
|
parameter_tab_->get_selected_video_pixel_aspect();
|
||||||
|
oakengine_sequence_set_video_params(
|
||||||
AudioParams audio_params =
|
facade_handle, parameter_tab_->get_selected_video_width(),
|
||||||
AudioParams(parameter_tab_->get_selected_audio_sample_rate(),
|
parameter_tab_->get_selected_video_height(), frame_rate.numerator(),
|
||||||
parameter_tab_->get_selected_audio_channel_layout(),
|
frame_rate.denominator(), pixel_aspect.numerator(),
|
||||||
Sequence::k_default_sample_format);
|
pixel_aspect.denominator(),
|
||||||
|
int(parameter_tab_->get_selected_video_interlacing_mode()),
|
||||||
if (make_undoable_) {
|
int(parameter_tab_->get_selected_preview_format()), undoable);
|
||||||
// Make undoable command to change the parameters
|
oakengine_sequence_set_preview_divider(
|
||||||
SequenceParamCommand *param_command = new SequenceParamCommand(
|
facade_handle, parameter_tab_->get_selected_preview_resolution(),
|
||||||
sequence_, video_params, audio_params, name_field_->text(),
|
undoable);
|
||||||
parameter_tab_->get_selected_preview_auto_cache());
|
oakengine_sequence_set_audio_params(
|
||||||
|
facade_handle, parameter_tab_->get_selected_audio_sample_rate(),
|
||||||
Core::instance()->undo_stack()->push(
|
parameter_tab_->get_selected_audio_channel_layout(), undoable);
|
||||||
param_command,
|
oakengine_node_set_label_ex(
|
||||||
tr("Set Sequence Parameters For \"%1\"").arg(sequence_->get_label()));
|
reinterpret_cast<OakEngineNode *>(sequence_),
|
||||||
|
name_field_->text().toUtf8().constData(), undoable);
|
||||||
} else {
|
oakengine_sequence_set_video_auto_cache(
|
||||||
// Set sequence values directly with no undo command
|
facade_handle, parameter_tab_->get_selected_preview_auto_cache() ? 1 :
|
||||||
sequence_->set_video_params(video_params);
|
0,
|
||||||
sequence_->set_audio_params(audio_params);
|
undoable);
|
||||||
sequence_->set_label(name_field_->text());
|
|
||||||
sequence_->set_video_auto_cache_enabled(
|
|
||||||
parameter_tab_->get_selected_preview_auto_cache());
|
|
||||||
}
|
|
||||||
|
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
}
|
}
|
||||||
@@ -201,48 +197,4 @@ void SequenceDialog::set_as_default_clicked()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SequenceDialog::SequenceParamCommand::SequenceParamCommand(
|
|
||||||
Sequence *s, const VideoParams &video_params,
|
|
||||||
const AudioParams &audio_params, const QString &name, bool autocache)
|
|
||||||
: sequence_(s)
|
|
||||||
, new_video_params_(video_params)
|
|
||||||
, new_audio_params_(audio_params)
|
|
||||||
, new_name_(name)
|
|
||||||
, new_autocache_(autocache)
|
|
||||||
, old_video_params_(s->get_video_params())
|
|
||||||
, old_audio_params_(s->get_audio_params())
|
|
||||||
, old_name_(s->get_label())
|
|
||||||
, old_autocache_(s->is_video_auto_cache_enabled())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Project *SequenceDialog::SequenceParamCommand::get_relevant_project() const
|
|
||||||
{
|
|
||||||
return sequence_->project();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SequenceDialog::SequenceParamCommand::redo()
|
|
||||||
{
|
|
||||||
if (sequence_->get_video_params() != new_video_params_) {
|
|
||||||
sequence_->set_video_params(new_video_params_);
|
|
||||||
}
|
|
||||||
if (sequence_->get_audio_params() != new_audio_params_) {
|
|
||||||
sequence_->set_audio_params(new_audio_params_);
|
|
||||||
}
|
|
||||||
sequence_->set_label(new_name_);
|
|
||||||
sequence_->set_video_auto_cache_enabled(new_autocache_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SequenceDialog::SequenceParamCommand::undo()
|
|
||||||
{
|
|
||||||
if (sequence_->get_video_params() != old_video_params_) {
|
|
||||||
sequence_->set_video_params(old_video_params_);
|
|
||||||
}
|
|
||||||
if (sequence_->get_audio_params() != old_audio_params_) {
|
|
||||||
sequence_->set_audio_params(old_audio_params_);
|
|
||||||
}
|
|
||||||
sequence_->set_label(old_name_);
|
|
||||||
sequence_->set_video_auto_cache_enabled(old_autocache_);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
#include "node/project/sequence/sequence.h"
|
#include "node/project/sequence/sequence.h"
|
||||||
#include "sequencedialogparametertab.h"
|
#include "sequencedialogparametertab.h"
|
||||||
#include "sequencedialogpresettab.h"
|
#include "sequencedialogpresettab.h"
|
||||||
#include "undo/undocommand.h"
|
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
@@ -101,35 +100,6 @@ private:
|
|||||||
|
|
||||||
QLineEdit *name_field_;
|
QLineEdit *name_field_;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief An UndoCommand for setting the parameters on a sequence
|
|
||||||
*/
|
|
||||||
class SequenceParamCommand : public UndoCommand {
|
|
||||||
public:
|
|
||||||
SequenceParamCommand(Sequence *s, const VideoParams &video_params,
|
|
||||||
const AudioParams &audio_params,
|
|
||||||
const QString &name, bool autocache);
|
|
||||||
|
|
||||||
virtual Project *get_relevant_project() const override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void redo() override;
|
|
||||||
virtual void undo() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Sequence *sequence_;
|
|
||||||
|
|
||||||
VideoParams new_video_params_;
|
|
||||||
AudioParams new_audio_params_;
|
|
||||||
QString new_name_;
|
|
||||||
bool new_autocache_;
|
|
||||||
|
|
||||||
VideoParams old_video_params_;
|
|
||||||
AudioParams old_audio_params_;
|
|
||||||
QString old_name_;
|
|
||||||
bool old_autocache_;
|
|
||||||
};
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void set_as_default_clicked();
|
void set_as_default_clicked();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include "oakengine/timeline.h"
|
||||||
|
|
||||||
namespace olive
|
namespace olive
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -100,19 +102,35 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence *sequence,
|
|||||||
|
|
||||||
layout->addWidget(preview_group);
|
layout->addWidget(preview_group);
|
||||||
|
|
||||||
// Set values based on input sequence
|
// Set values based on input sequence; the reads go through the
|
||||||
VideoParams vp = sequence->get_video_params();
|
// liboakengine C ABI facade (the sequence handle is the engine node
|
||||||
AudioParams ap = sequence->get_audio_params();
|
// pointer in this family).
|
||||||
width_slider_->set_value(vp.width());
|
OakEngineSequence *facade_handle =
|
||||||
height_slider_->set_value(vp.height());
|
reinterpret_cast<OakEngineSequence *>(sequence);
|
||||||
framerate_combo_->set_frame_rate(vp.time_base().flipped());
|
int width = 0, height = 0, fps_num = 0, fps_den = 1, par_num = 1,
|
||||||
pixelaspect_combo_->set_pixel_aspect_ratio(vp.pixel_aspect_ratio());
|
par_den = 1, interlacing = 0, format = 0, divider = 1;
|
||||||
interlacing_combo_->set_interlace_mode(vp.interlacing());
|
oakengine_sequence_get_video_params_ex(facade_handle, &width, &height,
|
||||||
preview_resolution_field_->set_divider(vp.divider());
|
&fps_num, &fps_den, &par_num,
|
||||||
preview_format_field_->set_pixel_format(vp.format());
|
&par_den, &interlacing, &format,
|
||||||
preview_autocache_field_->setChecked(sequence->is_video_auto_cache_enabled());
|
÷r);
|
||||||
audio_sample_rate_field_->set_sample_rate(ap.sample_rate());
|
int sample_rate = 0;
|
||||||
audio_channels_field_->set_channel_layout(ap.channel_layout());
|
uint64_t channel_layout = 0;
|
||||||
|
oakengine_sequence_get_audio_params(facade_handle, &sample_rate,
|
||||||
|
&channel_layout);
|
||||||
|
|
||||||
|
width_slider_->set_value(width);
|
||||||
|
height_slider_->set_value(height);
|
||||||
|
framerate_combo_->set_frame_rate(Rational(fps_num, fps_den));
|
||||||
|
pixelaspect_combo_->set_pixel_aspect_ratio(Rational(par_num, par_den));
|
||||||
|
interlacing_combo_->set_interlace_mode(
|
||||||
|
static_cast<VideoParams::Interlacing>(interlacing));
|
||||||
|
preview_resolution_field_->set_divider(divider);
|
||||||
|
preview_format_field_->set_pixel_format(
|
||||||
|
static_cast<PixelFormat::Format>(format));
|
||||||
|
preview_autocache_field_->setChecked(
|
||||||
|
oakengine_sequence_get_video_auto_cache(facade_handle) != 0);
|
||||||
|
audio_sample_rate_field_->set_sample_rate(sample_rate);
|
||||||
|
audio_channels_field_->set_channel_layout(channel_layout);
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
preview_resolution_field_,
|
preview_resolution_field_,
|
||||||
|
|||||||
@@ -140,6 +140,17 @@ OAKENGINE_API int oakengine_node_get_label(const OakEngineNode *self,
|
|||||||
OAKENGINE_API int oakengine_node_set_label(OakEngineNode *self,
|
OAKENGINE_API int oakengine_node_set_label(OakEngineNode *self,
|
||||||
const char *label);
|
const char *label);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the node's user label with an explicit undoable flag.
|
||||||
|
*
|
||||||
|
* `undoable` != 0 behaves like oakengine_node_set_label() (one undoable
|
||||||
|
* NodeRenameCommand on the global undo stack, degrading to a direct
|
||||||
|
* rename when the engine is not initialized); 0 renames directly with no
|
||||||
|
* undo entry.
|
||||||
|
*/
|
||||||
|
OAKENGINE_API int oakengine_node_set_label_ex(OakEngineNode *self,
|
||||||
|
const char *label, int undoable);
|
||||||
|
|
||||||
/* ---- Input introspection ---------------------------------------------------- */
|
/* ---- Input introspection ---------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ extern "C" {
|
|||||||
* @brief C ABI for sequences (Oak timelines)
|
* @brief C ABI for sequences (Oak timelines)
|
||||||
*
|
*
|
||||||
* An OakEngineSequence wraps the engine's olive::Sequence node
|
* An OakEngineSequence wraps the engine's olive::Sequence node
|
||||||
* (engine/node/project/sequence/sequence.h, a ViewerOutput). This family is
|
* (engine/node/project/sequence/sequence.h, a ViewerOutput). Most of the
|
||||||
* intentionally read-mostly this round: playhead and workarea are simple
|
* family is inspection; playhead, workarea, the sequence parameters
|
||||||
* writes, everything else is inspection. Clip/track editing is a later
|
* (video/audio/preview/auto-cache, see below) and clip/track editing are
|
||||||
* milestone.
|
* writes, undoable unless documented otherwise.
|
||||||
*
|
*
|
||||||
* Handles are borrowed from their owning OakEngineProject (Qt QObject parent
|
* Handles are borrowed from their owning OakEngineProject (Qt QObject parent
|
||||||
* chain; sequences are added to the project graph which becomes their
|
* chain; sequences are added to the project graph which becomes their
|
||||||
@@ -118,6 +118,100 @@ OAKENGINE_API int
|
|||||||
oakengine_sequence_get_video_params(const OakEngineSequence *self, int *width,
|
oakengine_sequence_get_video_params(const OakEngineSequence *self, int *width,
|
||||||
int *height, int *par_num, int *par_den);
|
int *height, int *par_num, int *par_den);
|
||||||
|
|
||||||
|
/* ---- Sequence parameters (sequence dialog) ------------------------------------
|
||||||
|
*
|
||||||
|
* The parameter set shown by the application's sequence dialog
|
||||||
|
* (app/dialog/sequence): video size/frame rate/pixel aspect/interlacing,
|
||||||
|
* preview pixel format and resolution divider, audio sample rate/channel
|
||||||
|
* layout, and the video auto-cache flag. Setters take an `undoable` flag:
|
||||||
|
* 1 pushes one undoable command onto the global undo stack (direct
|
||||||
|
* application when the engine is not initialized), 0 applies directly with
|
||||||
|
* no undo entry -- mirroring the dialog's two modes (SetUndoable()).
|
||||||
|
* Fields pass -1 (ints/rationals) or <= 0 / 0 (audio) to leave them
|
||||||
|
* unchanged; a call that changes nothing pushes no command and returns
|
||||||
|
* OAKENGINE_OK. The video channel count and the audio sample format are
|
||||||
|
* internal constants in the engine (VideoParams::k_internal_channel_count,
|
||||||
|
* ViewerOutput::k_default_sample_format) and are kept as-is.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Full read of the sequence's video parameters
|
||||||
|
* (ViewerOutput::get_video_params()). Any output pointer may be NULL.
|
||||||
|
* `fps_*` is the frame rate (the time base flipped), `format` a
|
||||||
|
* PixelFormat::Format value, `divider` the preview resolution divider.
|
||||||
|
*/
|
||||||
|
OAKENGINE_API int oakengine_sequence_get_video_params_ex(
|
||||||
|
const OakEngineSequence *self, int *width, int *height, int *fps_num,
|
||||||
|
int *fps_den, int *par_num, int *par_den, int *interlacing, int *format,
|
||||||
|
int *divider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write the sequence's video parameters (see the section comment
|
||||||
|
* for the undoable/unchanged conventions).
|
||||||
|
*
|
||||||
|
* -1 leaves a field unchanged; both fps and pixel-aspect must be given as
|
||||||
|
* num/den pairs (both -1 or both > 0). `interlacing` is a
|
||||||
|
* VideoParams::Interlacing value (0..2), `format` a PixelFormat::Format
|
||||||
|
* value (0..PixelFormat::count-1). Invalid values yield
|
||||||
|
* OAKENGINE_E_INVALID. The frame rate is stored as its flipped time base
|
||||||
|
* exactly like the application's dialog (VideoParams constructor), so the
|
||||||
|
* derived effective size and frame_rate stay in sync.
|
||||||
|
*/
|
||||||
|
OAKENGINE_API int oakengine_sequence_set_video_params(
|
||||||
|
OakEngineSequence *self, int width, int height, int fps_num, int fps_den,
|
||||||
|
int par_num, int par_den, int interlacing, int format, int undoable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sequence audio sample rate and channel layout
|
||||||
|
* (ViewerOutput::get_audio_params()). Any output pointer may be NULL.
|
||||||
|
*/
|
||||||
|
OAKENGINE_API int oakengine_sequence_get_audio_params(
|
||||||
|
const OakEngineSequence *self, int *sample_rate,
|
||||||
|
uint64_t *channel_layout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write the sequence's audio parameters (undoable flag as above).
|
||||||
|
* `sample_rate` <= 0 or `channel_layout` == 0 leaves the field unchanged.
|
||||||
|
* The sample format is kept unchanged (the dialog always uses
|
||||||
|
* ViewerOutput::k_default_sample_format).
|
||||||
|
*/
|
||||||
|
OAKENGINE_API int oakengine_sequence_set_audio_params(
|
||||||
|
OakEngineSequence *self, int sample_rate, uint64_t channel_layout,
|
||||||
|
int undoable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Preview resolution divider (VideoParams::divider()). 0 on a NULL
|
||||||
|
* handle.
|
||||||
|
*/
|
||||||
|
OAKENGINE_API int
|
||||||
|
oakengine_sequence_get_preview_divider(const OakEngineSequence *self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the preview resolution divider (undoable flag as above).
|
||||||
|
* `divider` < 1 yields OAKENGINE_E_INVALID.
|
||||||
|
*/
|
||||||
|
OAKENGINE_API int oakengine_sequence_set_preview_divider(
|
||||||
|
OakEngineSequence *self, int divider, int undoable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 1 when video auto-cache is enabled
|
||||||
|
* (ViewerOutput::is_video_auto_cache_enabled()). 0 on a NULL handle.
|
||||||
|
*/
|
||||||
|
OAKENGINE_API int
|
||||||
|
oakengine_sequence_get_video_auto_cache(const OakEngineSequence *self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable or disable video auto-cache.
|
||||||
|
*
|
||||||
|
* The engine's auto-cache accessors are currently stubs (the read always
|
||||||
|
* reports 0, the write is ignored; the application's dialog has the
|
||||||
|
* checkbox TEMP-disabled for the same reason). This forwards to the stub
|
||||||
|
* without an undo command so the ABI is ready when the engine
|
||||||
|
* implementation lands. `undoable` is accepted for symmetry and ignored.
|
||||||
|
*/
|
||||||
|
OAKENGINE_API int oakengine_sequence_set_video_auto_cache(
|
||||||
|
OakEngineSequence *self, int enabled, int undoable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Number of tracks per track type (Sequence::track_list(type)->
|
* @brief Number of tracks per track type (Sequence::track_list(type)->
|
||||||
* get_track_count()). Any of `video`/`audio`/`subtitle` may be NULL.
|
* get_track_count()). Any of `video`/`audio`/`subtitle` may be NULL.
|
||||||
|
|||||||
@@ -487,15 +487,26 @@ int oakengine_node_get_label(const OakEngineNode *self, char *buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int oakengine_node_set_label(OakEngineNode *self, const char *label)
|
int oakengine_node_set_label(OakEngineNode *self, const char *label)
|
||||||
|
{
|
||||||
|
return oakengine_node_set_label_ex(self, label, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int oakengine_node_set_label_ex(OakEngineNode *self, const char *label,
|
||||||
|
int undoable)
|
||||||
{
|
{
|
||||||
set_error(QString());
|
set_error(QString());
|
||||||
if (!self) {
|
if (!self) {
|
||||||
set_error(QStringLiteral("invalid node"));
|
set_error(QStringLiteral("invalid node"));
|
||||||
return OAKENGINE_E_INVALID;
|
return OAKENGINE_E_INVALID;
|
||||||
}
|
}
|
||||||
push_or_run(new olive::NodeRenameCommand(
|
olive::UndoCommand *command = new olive::NodeRenameCommand(
|
||||||
impl(self), QString::fromUtf8(label ? label : "")),
|
impl(self), QString::fromUtf8(label ? label : ""));
|
||||||
QStringLiteral("Rename Node"));
|
if (undoable) {
|
||||||
|
push_or_run(command, QStringLiteral("Rename Node"));
|
||||||
|
} else {
|
||||||
|
command->redo_now();
|
||||||
|
delete command;
|
||||||
|
}
|
||||||
return OAKENGINE_OK;
|
return OAKENGINE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -152,6 +152,88 @@ void push_or_run(olive::UndoCommand *command, const QString &name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply a command honoring an explicit undoable flag: 1 pushes through
|
||||||
|
// push_or_run(), 0 applies directly with no undo entry (the sequence
|
||||||
|
// dialog's non-undoable mode for freshly created sequences).
|
||||||
|
void apply_or_push(olive::UndoCommand *command, const QString &name,
|
||||||
|
int undoable)
|
||||||
|
{
|
||||||
|
if (undoable) {
|
||||||
|
push_or_run(command, name);
|
||||||
|
} else {
|
||||||
|
command->redo_now();
|
||||||
|
delete command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Undo commands for sequence parameter writes. The engine has no undo
|
||||||
|
// commands for these (the application's sequence dialog carries its own
|
||||||
|
// SequenceParamCommand at the app layer), so the facade carries
|
||||||
|
// read-modify-write equivalents with the same semantics.
|
||||||
|
class SequenceVideoParamsCommand : public olive::UndoCommand {
|
||||||
|
public:
|
||||||
|
SequenceVideoParamsCommand(olive::Sequence *sequence,
|
||||||
|
const olive::VideoParams ¶ms)
|
||||||
|
: sequence_(sequence)
|
||||||
|
, new_params_(params)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual olive::Project *get_relevant_project() const override
|
||||||
|
{
|
||||||
|
return sequence_->project();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void redo() override
|
||||||
|
{
|
||||||
|
old_params_ = sequence_->get_video_params();
|
||||||
|
sequence_->set_video_params(new_params_);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void undo() override
|
||||||
|
{
|
||||||
|
sequence_->set_video_params(old_params_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
olive::Sequence *sequence_;
|
||||||
|
olive::VideoParams old_params_;
|
||||||
|
olive::VideoParams new_params_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SequenceAudioParamsCommand : public olive::UndoCommand {
|
||||||
|
public:
|
||||||
|
SequenceAudioParamsCommand(olive::Sequence *sequence,
|
||||||
|
const olive::AudioParams ¶ms)
|
||||||
|
: sequence_(sequence)
|
||||||
|
, new_params_(params)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual olive::Project *get_relevant_project() const override
|
||||||
|
{
|
||||||
|
return sequence_->project();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void redo() override
|
||||||
|
{
|
||||||
|
old_params_ = sequence_->get_audio_params();
|
||||||
|
sequence_->set_audio_params(new_params_);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void undo() override
|
||||||
|
{
|
||||||
|
sequence_->set_audio_params(old_params_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
olive::Sequence *sequence_;
|
||||||
|
olive::AudioParams old_params_;
|
||||||
|
olive::AudioParams new_params_;
|
||||||
|
};
|
||||||
|
|
||||||
// The clip at (track_index, clip_index) within the given track list,
|
// The clip at (track_index, clip_index) within the given track list,
|
||||||
// skipping gap blocks; nullptr when out of range.
|
// skipping gap blocks; nullptr when out of range.
|
||||||
olive::ClipBlock *clip_at_index(olive::TrackList *list, int track_index,
|
olive::ClipBlock *clip_at_index(olive::TrackList *list, int track_index,
|
||||||
@@ -309,6 +391,220 @@ int oakengine_sequence_get_video_params(const OakEngineSequence *self,
|
|||||||
return OAKENGINE_OK;
|
return OAKENGINE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Sequence parameters (sequence dialog) ------------------------------------ */
|
||||||
|
|
||||||
|
int oakengine_sequence_get_video_params_ex(
|
||||||
|
const OakEngineSequence *self, int *width, int *height, int *fps_num,
|
||||||
|
int *fps_den, int *par_num, int *par_den, int *interlacing, int *format,
|
||||||
|
int *divider)
|
||||||
|
{
|
||||||
|
if (!self) {
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
const olive::VideoParams params = impl(self)->get_video_params();
|
||||||
|
if (width) {
|
||||||
|
*width = params.width();
|
||||||
|
}
|
||||||
|
if (height) {
|
||||||
|
*height = params.height();
|
||||||
|
}
|
||||||
|
const olive::Rational frame_rate = params.frame_rate();
|
||||||
|
if (fps_num) {
|
||||||
|
*fps_num = frame_rate.numerator();
|
||||||
|
}
|
||||||
|
if (fps_den) {
|
||||||
|
*fps_den = frame_rate.denominator();
|
||||||
|
}
|
||||||
|
const olive::Rational par = params.pixel_aspect_ratio();
|
||||||
|
if (par_num) {
|
||||||
|
*par_num = par.numerator();
|
||||||
|
}
|
||||||
|
if (par_den) {
|
||||||
|
*par_den = par.denominator();
|
||||||
|
}
|
||||||
|
if (interlacing) {
|
||||||
|
*interlacing = int(params.interlacing());
|
||||||
|
}
|
||||||
|
if (format) {
|
||||||
|
*format = int(params.format());
|
||||||
|
}
|
||||||
|
if (divider) {
|
||||||
|
*divider = params.divider();
|
||||||
|
}
|
||||||
|
return OAKENGINE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oakengine_sequence_set_video_params(
|
||||||
|
OakEngineSequence *self, int width, int height, int fps_num, int fps_den,
|
||||||
|
int par_num, int par_den, int interlacing, int format, int undoable)
|
||||||
|
{
|
||||||
|
set_seq_error(QString());
|
||||||
|
if (!self) {
|
||||||
|
set_seq_error(QStringLiteral("invalid sequence"));
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
// -1 leaves a field unchanged; 0 / out-of-range values are rejected.
|
||||||
|
if (width < -1 || width == 0 || height < -1 || height == 0) {
|
||||||
|
set_seq_error(QStringLiteral("invalid video size %1x%2")
|
||||||
|
.arg(width)
|
||||||
|
.arg(height));
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
if ((fps_num == -1) != (fps_den == -1) || fps_num < -1 || fps_num == 0 ||
|
||||||
|
fps_den < -1 || fps_den == 0) {
|
||||||
|
set_seq_error(QStringLiteral("invalid frame rate %1/%2")
|
||||||
|
.arg(fps_num)
|
||||||
|
.arg(fps_den));
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
if ((par_num == -1) != (par_den == -1) || par_num < -1 || par_num == 0 ||
|
||||||
|
par_den < -1 || par_den == 0) {
|
||||||
|
set_seq_error(QStringLiteral("invalid pixel aspect %1/%2")
|
||||||
|
.arg(par_num)
|
||||||
|
.arg(par_den));
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
if (interlacing < -1 ||
|
||||||
|
interlacing > int(olive::VideoParams::k_interlaced_bottom_first)) {
|
||||||
|
set_seq_error(
|
||||||
|
QStringLiteral("invalid interlacing %1").arg(interlacing));
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
if (format < -1 || format >= int(olive::PixelFormat::count)) {
|
||||||
|
set_seq_error(QStringLiteral("invalid pixel format %1").arg(format));
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
olive::Sequence *sequence = impl(self);
|
||||||
|
const olive::VideoParams current = sequence->get_video_params();
|
||||||
|
// Rebuild through the same constructor the application's dialog uses so
|
||||||
|
// the frame rate (flipped time base) and effective size stay in sync.
|
||||||
|
const olive::VideoParams updated(
|
||||||
|
width >= 0 ? width : current.width(),
|
||||||
|
height >= 0 ? height : current.height(),
|
||||||
|
fps_num >= 0 ? olive::Rational(fps_den, fps_num) : current.time_base(),
|
||||||
|
format >= 0 ? olive::PixelFormat::Format(format) :
|
||||||
|
olive::PixelFormat::Format(current.format()),
|
||||||
|
current.channel_count(),
|
||||||
|
par_num >= 0 ? olive::Rational(par_num, par_den) :
|
||||||
|
current.pixel_aspect_ratio(),
|
||||||
|
interlacing >= 0 ? olive::VideoParams::Interlacing(interlacing) :
|
||||||
|
current.interlacing(),
|
||||||
|
current.divider());
|
||||||
|
if (updated == current) {
|
||||||
|
return OAKENGINE_OK;
|
||||||
|
}
|
||||||
|
apply_or_push(new SequenceVideoParamsCommand(sequence, updated),
|
||||||
|
QStringLiteral("Set Sequence Video Parameters"), undoable);
|
||||||
|
return OAKENGINE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oakengine_sequence_get_audio_params(const OakEngineSequence *self,
|
||||||
|
int *sample_rate,
|
||||||
|
uint64_t *channel_layout)
|
||||||
|
{
|
||||||
|
if (!self) {
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
const olive::AudioParams params = impl(self)->get_audio_params();
|
||||||
|
if (sample_rate) {
|
||||||
|
*sample_rate = params.sample_rate();
|
||||||
|
}
|
||||||
|
if (channel_layout) {
|
||||||
|
*channel_layout = params.channel_layout();
|
||||||
|
}
|
||||||
|
return OAKENGINE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oakengine_sequence_set_audio_params(OakEngineSequence *self,
|
||||||
|
int sample_rate,
|
||||||
|
uint64_t channel_layout, int undoable)
|
||||||
|
{
|
||||||
|
set_seq_error(QString());
|
||||||
|
if (!self) {
|
||||||
|
set_seq_error(QStringLiteral("invalid sequence"));
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
olive::Sequence *sequence = impl(self);
|
||||||
|
const olive::AudioParams current = sequence->get_audio_params();
|
||||||
|
if (sample_rate <= 0) {
|
||||||
|
sample_rate = current.sample_rate();
|
||||||
|
}
|
||||||
|
if (channel_layout == 0) {
|
||||||
|
channel_layout = current.channel_layout();
|
||||||
|
}
|
||||||
|
const olive::AudioParams updated(sample_rate, channel_layout,
|
||||||
|
current.format());
|
||||||
|
if (updated == current) {
|
||||||
|
return OAKENGINE_OK;
|
||||||
|
}
|
||||||
|
apply_or_push(new SequenceAudioParamsCommand(sequence, updated),
|
||||||
|
QStringLiteral("Set Sequence Audio Parameters"), undoable);
|
||||||
|
return OAKENGINE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oakengine_sequence_get_preview_divider(const OakEngineSequence *self)
|
||||||
|
{
|
||||||
|
if (!self) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return impl(self)->get_video_params().divider();
|
||||||
|
}
|
||||||
|
|
||||||
|
int oakengine_sequence_set_preview_divider(OakEngineSequence *self,
|
||||||
|
int divider, int undoable)
|
||||||
|
{
|
||||||
|
set_seq_error(QString());
|
||||||
|
if (!self) {
|
||||||
|
set_seq_error(QStringLiteral("invalid sequence"));
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
if (divider < 1) {
|
||||||
|
set_seq_error(QStringLiteral("invalid preview divider %1")
|
||||||
|
.arg(divider));
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
olive::Sequence *sequence = impl(self);
|
||||||
|
const olive::VideoParams current = sequence->get_video_params();
|
||||||
|
if (current.divider() == divider) {
|
||||||
|
return OAKENGINE_OK;
|
||||||
|
}
|
||||||
|
// Same constructor rebuild as in set_video_params (keeps the derived
|
||||||
|
// effective size in sync).
|
||||||
|
const olive::VideoParams updated(
|
||||||
|
current.width(), current.height(), current.time_base(),
|
||||||
|
current.format(), current.channel_count(),
|
||||||
|
current.pixel_aspect_ratio(), current.interlacing(), divider);
|
||||||
|
apply_or_push(new SequenceVideoParamsCommand(sequence, updated),
|
||||||
|
QStringLiteral("Set Sequence Preview Divider"), undoable);
|
||||||
|
return OAKENGINE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oakengine_sequence_get_video_auto_cache(const OakEngineSequence *self)
|
||||||
|
{
|
||||||
|
if (!self) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return impl(self)->is_video_auto_cache_enabled() ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oakengine_sequence_set_video_auto_cache(OakEngineSequence *self,
|
||||||
|
int enabled, int undoable)
|
||||||
|
{
|
||||||
|
set_seq_error(QString());
|
||||||
|
Q_UNUSED(undoable)
|
||||||
|
if (!self) {
|
||||||
|
set_seq_error(QStringLiteral("invalid sequence"));
|
||||||
|
return OAKENGINE_E_INVALID;
|
||||||
|
}
|
||||||
|
// The engine's auto-cache accessors are stubs for now (the read always
|
||||||
|
// returns false, the write is a no-op; the application's dialog has the
|
||||||
|
// checkbox TEMP-disabled accordingly). Forwarded without an undo
|
||||||
|
// command so the facade surface is ready when the engine lands it.
|
||||||
|
impl(self)->set_video_auto_cache_enabled(enabled != 0);
|
||||||
|
return OAKENGINE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
int oakengine_sequence_track_count(const OakEngineSequence *self, int *video,
|
int oakengine_sequence_track_count(const OakEngineSequence *self, int *video,
|
||||||
int *audio, int *subtitle)
|
int *audio, int *subtitle)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
#include "oakengine/footage.h"
|
#include "oakengine/footage.h"
|
||||||
#include "oakengine/init.h"
|
#include "oakengine/init.h"
|
||||||
|
#include "oakengine/node.h"
|
||||||
#include "oakengine/project.h"
|
#include "oakengine/project.h"
|
||||||
#include "oakengine/timeline.h"
|
#include "oakengine/timeline.h"
|
||||||
|
|
||||||
@@ -641,6 +642,174 @@ static void test_markers(void)
|
|||||||
oakengine_project_free(project);
|
oakengine_project_free(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_sequence_params(void)
|
||||||
|
{
|
||||||
|
OakEngineProject *project = oakengine_project_create();
|
||||||
|
assert(project != NULL);
|
||||||
|
assert(oakengine_project_new(project) == OAKENGINE_OK);
|
||||||
|
OakEngineSequence *seq = oakengine_sequence_new(project, "Params");
|
||||||
|
assert(seq != NULL);
|
||||||
|
|
||||||
|
int w = 0, h = 0, fn = 0, fd = 0, pn = 0, pd = 0, il = -1, fmt = -1,
|
||||||
|
div = 0;
|
||||||
|
assert(oakengine_sequence_get_video_params_ex(seq, &w, &h, &fn, &fd, &pn,
|
||||||
|
&pd, &il, &fmt,
|
||||||
|
&div) == OAKENGINE_OK);
|
||||||
|
const int base_w = w, base_h = h, base_div = div;
|
||||||
|
assert(base_w > 0 && base_h > 0 && fn > 0 && fd > 0 && div >= 1);
|
||||||
|
assert(oakengine_sequence_get_video_params_ex(NULL, &w, &h, &fn, &fd,
|
||||||
|
&pn, &pd, &il, &fmt,
|
||||||
|
&div) == OAKENGINE_E_INVALID);
|
||||||
|
|
||||||
|
// Full video parameter write (f32 = 4, bottom-first = 2) + readback.
|
||||||
|
assert(oakengine_sequence_set_video_params(seq, 1280, 720, 24, 1, 1, 1, 2,
|
||||||
|
4, 1) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_video_params_ex(seq, &w, &h, &fn, &fd, &pn,
|
||||||
|
&pd, &il, &fmt,
|
||||||
|
&div) == OAKENGINE_OK);
|
||||||
|
assert(w == 1280 && h == 720 && fn == 24 && fd == 1);
|
||||||
|
assert(pn == 1 && pd == 1 && il == 2 && fmt == 4 && div == base_div);
|
||||||
|
int gn = 0, gd = 0;
|
||||||
|
assert(oakengine_sequence_get_frame_rate(seq, &gn, &gd) == OAKENGINE_OK);
|
||||||
|
assert(gn == 24 && gd == 1);
|
||||||
|
|
||||||
|
// Partial update: -1 leaves the other fields unchanged.
|
||||||
|
assert(oakengine_sequence_set_video_params(seq, 640, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, 1) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_video_params_ex(seq, &w, &h, &fn, &fd, &pn,
|
||||||
|
&pd, &il, &fmt,
|
||||||
|
&div) == OAKENGINE_OK);
|
||||||
|
assert(w == 640 && h == 720 && fn == 24 && il == 2);
|
||||||
|
|
||||||
|
// Invalid values: zero size, half a rational pair, out-of-range enum.
|
||||||
|
assert(oakengine_sequence_set_video_params(seq, 0, 720, -1, -1, -1, -1,
|
||||||
|
-1, -1,
|
||||||
|
1) == OAKENGINE_E_INVALID);
|
||||||
|
assert(oakengine_sequence_set_video_params(seq, -1, -1, 24, -1, -1, -1,
|
||||||
|
-1, -1,
|
||||||
|
1) == OAKENGINE_E_INVALID);
|
||||||
|
assert(oakengine_sequence_set_video_params(seq, -1, -1, -1, -1, -1, -1, 3,
|
||||||
|
-1,
|
||||||
|
1) == OAKENGINE_E_INVALID);
|
||||||
|
assert(oakengine_sequence_set_video_params(seq, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, 5,
|
||||||
|
1) == OAKENGINE_E_INVALID);
|
||||||
|
assert(oakengine_sequence_set_video_params(NULL, 1, 1, -1, -1, -1, -1, -1,
|
||||||
|
-1,
|
||||||
|
1) == OAKENGINE_E_INVALID);
|
||||||
|
|
||||||
|
// Undo restores step by step (partial write, then full write).
|
||||||
|
assert(oakengine_project_undo(project) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_video_params_ex(seq, &w, &h, &fn, &fd, &pn,
|
||||||
|
&pd, &il, &fmt,
|
||||||
|
&div) == OAKENGINE_OK);
|
||||||
|
assert(w == 1280 && h == 720);
|
||||||
|
assert(oakengine_project_undo(project) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_video_params_ex(seq, &w, &h, &fn, &fd, &pn,
|
||||||
|
&pd, &il, &fmt,
|
||||||
|
&div) == OAKENGINE_OK);
|
||||||
|
assert(w == base_w && h == base_h);
|
||||||
|
|
||||||
|
// Audio round trip; 0 / <= 0 leaves the field unchanged.
|
||||||
|
int sr = 0;
|
||||||
|
uint64_t layout = 0;
|
||||||
|
assert(oakengine_sequence_get_audio_params(seq, &sr, &layout) ==
|
||||||
|
OAKENGINE_OK);
|
||||||
|
assert(sr > 0 && layout != 0);
|
||||||
|
const int base_sr = sr;
|
||||||
|
const uint64_t base_layout = layout;
|
||||||
|
assert(oakengine_sequence_set_audio_params(seq, 44100, 3, 1) ==
|
||||||
|
OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_audio_params(seq, &sr, &layout) ==
|
||||||
|
OAKENGINE_OK);
|
||||||
|
assert(sr == 44100 && layout == 3);
|
||||||
|
assert(oakengine_sequence_set_audio_params(seq, 48000, 0, 1) ==
|
||||||
|
OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_audio_params(seq, &sr, &layout) ==
|
||||||
|
OAKENGINE_OK);
|
||||||
|
assert(sr == 48000 && layout == 3);
|
||||||
|
assert(oakengine_sequence_set_audio_params(NULL, 48000, 3, 1) ==
|
||||||
|
OAKENGINE_E_INVALID);
|
||||||
|
assert(oakengine_sequence_get_audio_params(NULL, &sr, &layout) ==
|
||||||
|
OAKENGINE_E_INVALID);
|
||||||
|
assert(oakengine_project_undo(project) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_project_undo(project) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_audio_params(seq, &sr, &layout) ==
|
||||||
|
OAKENGINE_OK);
|
||||||
|
assert(sr == base_sr && layout == base_layout);
|
||||||
|
|
||||||
|
// Preview divider; the other video params stay untouched.
|
||||||
|
assert(oakengine_sequence_get_preview_divider(seq) == base_div);
|
||||||
|
assert(oakengine_sequence_set_preview_divider(seq, 4, 1) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_preview_divider(seq) == 4);
|
||||||
|
assert(oakengine_sequence_set_preview_divider(seq, 0, 1) ==
|
||||||
|
OAKENGINE_E_INVALID);
|
||||||
|
assert(oakengine_sequence_set_preview_divider(NULL, 1, 1) ==
|
||||||
|
OAKENGINE_E_INVALID);
|
||||||
|
assert(oakengine_sequence_get_preview_divider(NULL) == 0);
|
||||||
|
assert(oakengine_sequence_get_video_params_ex(seq, &w, &h, &fn, &fd, &pn,
|
||||||
|
&pd, &il, &fmt,
|
||||||
|
&div) == OAKENGINE_OK);
|
||||||
|
assert(w == base_w && h == base_h && div == 4);
|
||||||
|
assert(oakengine_project_undo(project) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_preview_divider(seq) == base_div);
|
||||||
|
|
||||||
|
// Auto-cache flag: the engine accessors are stubs (read always 0, write
|
||||||
|
// ignored), so there is nothing to undo here.
|
||||||
|
assert(oakengine_sequence_get_video_auto_cache(seq) == 0);
|
||||||
|
assert(oakengine_sequence_set_video_auto_cache(seq, 1, 1) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_video_auto_cache(seq) == 0);
|
||||||
|
assert(oakengine_sequence_get_video_auto_cache(NULL) == 0);
|
||||||
|
assert(oakengine_sequence_set_video_auto_cache(NULL, 1, 1) ==
|
||||||
|
OAKENGINE_E_INVALID);
|
||||||
|
|
||||||
|
// Label through the node family with the undoable flag.
|
||||||
|
assert(oakengine_node_set_label_ex((OakEngineNode *)seq, "Renamed", 1) ==
|
||||||
|
OAKENGINE_OK);
|
||||||
|
char name[64];
|
||||||
|
assert(oakengine_sequence_name(seq, name, sizeof(name)) > 0);
|
||||||
|
assert(strcmp(name, "Renamed") == 0);
|
||||||
|
assert(oakengine_project_undo(project) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_name(seq, name, sizeof(name)) > 0);
|
||||||
|
assert(strcmp(name, "Params") == 0);
|
||||||
|
|
||||||
|
oakengine_project_free(project);
|
||||||
|
|
||||||
|
// undoable == 0 applies directly with no undo entry: on a fresh project
|
||||||
|
// the creation is undone first so the stack is empty (the node stays
|
||||||
|
// alive under its creation command).
|
||||||
|
OakEngineProject *p2 = oakengine_project_create();
|
||||||
|
assert(p2 != NULL);
|
||||||
|
assert(oakengine_project_new(p2) == OAKENGINE_OK);
|
||||||
|
OakEngineSequence *s2 = oakengine_sequence_new(p2, "Direct");
|
||||||
|
assert(s2 != NULL);
|
||||||
|
assert(oakengine_project_undo(p2) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_project_can_undo(p2) == 0);
|
||||||
|
assert(oakengine_sequence_set_video_params(s2, 800, 600, -1, -1, -1, -1,
|
||||||
|
-1, -1,
|
||||||
|
0) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_video_params_ex(s2, &w, &h, &fn, &fd, &pn,
|
||||||
|
&pd, &il, &fmt,
|
||||||
|
&div) == OAKENGINE_OK);
|
||||||
|
assert(w == 800 && h == 600);
|
||||||
|
assert(oakengine_project_can_undo(p2) == 0);
|
||||||
|
assert(oakengine_sequence_set_audio_params(s2, 22050, 0, 0) ==
|
||||||
|
OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_audio_params(s2, &sr, &layout) ==
|
||||||
|
OAKENGINE_OK);
|
||||||
|
assert(sr == 22050);
|
||||||
|
assert(oakengine_project_can_undo(p2) == 0);
|
||||||
|
assert(oakengine_sequence_set_preview_divider(s2, 2, 0) == OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_get_preview_divider(s2) == 2);
|
||||||
|
assert(oakengine_project_can_undo(p2) == 0);
|
||||||
|
assert(oakengine_node_set_label_ex((OakEngineNode *)s2, "NoUndo", 0) ==
|
||||||
|
OAKENGINE_OK);
|
||||||
|
assert(oakengine_sequence_name(s2, name, sizeof(name)) > 0);
|
||||||
|
assert(strcmp(name, "NoUndo") == 0);
|
||||||
|
assert(oakengine_project_can_undo(p2) == 0);
|
||||||
|
oakengine_project_free(p2);
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
make_tmpdir();
|
make_tmpdir();
|
||||||
@@ -669,6 +838,7 @@ int main(void)
|
|||||||
test_edit_round2(path);
|
test_edit_round2(path);
|
||||||
test_track_structure(path);
|
test_track_structure(path);
|
||||||
test_markers();
|
test_markers();
|
||||||
|
test_sequence_params();
|
||||||
|
|
||||||
oakengine_project_free(project);
|
oakengine_project_free(project);
|
||||||
assert(oakengine_shutdown() == OAKENGINE_OK);
|
assert(oakengine_shutdown() == OAKENGINE_OK);
|
||||||
|
|||||||
Reference in New Issue
Block a user