refactor(node): switch oaknode to refcounted value handles, migrate consumers

- all 15 OakNode* handle types become neutral by-value structs
  {ctx, addref, release, abi_version}; shared box in
  src/node/c_api/nodehandle.h with owns flag (borrowed accessors
  return non-owning boxes; graph insertion flips owns off)
- oaktimeline/oaktask/oakrender call sites and their own public
  headers migrated to value handles; identity comparisons in
  timeline/task now compare native pointers
- regressions green: oaknode 96, oaktimeline 117, oaktask 106,
  oakrender 44, oakcommon 193, oakcodec 18, oakaudio 36
This commit is contained in:
2026-08-07 17:20:42 +08:00
parent 295ea1bfe5
commit 67176281d9
87 changed files with 4499 additions and 3431 deletions
+148 -122
View File
@@ -20,7 +20,8 @@
#include "node/block.h"
#include "alivecount.h"
#include "node/node.h"
#include "node/track.h"
#include "block/block.h"
#include "block/clip/clip.h"
@@ -30,27 +31,26 @@
#include "block/transition/transition.h"
#include "output/track/track.h"
#include "nodehandle.h"
using oaknode_c_api::delete_as;
using oaknode_c_api::free_handle;
using oaknode_c_api::make_handle;
using oaknode_c_api::to_native;
namespace
{
olive::Block *impl(OakNodeBlock *h)
olive::ClipBlock *clip_impl(OakNodeBlock h)
{
return reinterpret_cast<olive::Block *>(h);
olive::Block *b = to_native<olive::Block>(h);
return b ? dynamic_cast<olive::ClipBlock *>(b) : nullptr;
}
olive::ClipBlock *clip_impl(OakNodeBlock *h)
olive::TransitionBlock *transition_impl(OakNodeBlock h)
{
return h ? dynamic_cast<olive::ClipBlock *>(impl(h)) : nullptr;
}
olive::TransitionBlock *transition_impl(OakNodeBlock *h)
{
return h ? dynamic_cast<olive::TransitionBlock *>(impl(h)) : nullptr;
}
OakNodeBlock *wrap(olive::Block *b)
{
return reinterpret_cast<OakNodeBlock *>(b);
olive::Block *b = to_native<olive::Block>(h);
return b ? dynamic_cast<olive::TransitionBlock *>(b) : nullptr;
}
int get_rational(const olive::core::Rational &r, int *numerator,
@@ -65,30 +65,29 @@ int get_rational(const olive::core::Rational &r, int *numerator,
}
template <typename T, typename... Args>
OakNodeBlock *create_block(Args &&...args)
OakNodeBlock create_block(Args &&...args)
{
try {
T *b = new T(std::forward<Args>(args)...);
oaknode_c_api::alive_inc();
return wrap(b);
return make_handle<OakNodeBlock>(new T(std::forward<Args>(args)...),
true, &delete_as<T>);
} catch (...) {
return nullptr;
return OakNodeBlock{};
}
}
} // namespace
OakNodeBlock *oaknode_block_clip_create(void)
OakNodeBlock oaknode_block_clip_create(void)
{
return create_block<olive::ClipBlock>();
}
OakNodeBlock *oaknode_block_gap_create(void)
OakNodeBlock oaknode_block_gap_create(void)
{
return create_block<olive::GapBlock>();
}
OakNodeBlock *oaknode_block_transition_create(int kind)
OakNodeBlock oaknode_block_transition_create(int kind)
{
switch (kind) {
case OAKNODE_TRANSITION_CROSS_DISSOLVE:
@@ -96,70 +95,72 @@ OakNodeBlock *oaknode_block_transition_create(int kind)
case OAKNODE_TRANSITION_DIP_TO_COLOR:
return create_block<olive::DipToColorTransition>();
default:
return nullptr;
return OakNodeBlock{};
}
}
void oaknode_block_free(OakNodeBlock *block)
{
if (!block) {
return;
}
delete impl(block);
oaknode_c_api::alive_dec();
free_handle(block);
}
int oaknode_block_get_in(OakNodeBlock *block, int *numerator, int *denominator)
int oaknode_block_get_in(OakNodeBlock block, int *numerator, int *denominator)
{
if (!block) {
olive::Block *b = to_native<olive::Block>(block);
if (!b) {
return OAKNODE_E_INVALID;
}
return get_rational(impl(block)->in(), numerator, denominator);
return get_rational(b->in(), numerator, denominator);
}
int oaknode_block_set_in(OakNodeBlock *block, int numerator, int denominator)
int oaknode_block_set_in(OakNodeBlock block, int numerator, int denominator)
{
if (!block) {
olive::Block *b = to_native<olive::Block>(block);
if (!b) {
return OAKNODE_E_INVALID;
}
impl(block)->set_in(olive::core::Rational(numerator, denominator));
b->set_in(olive::core::Rational(numerator, denominator));
return OAKNODE_OK;
}
int oaknode_block_get_out(OakNodeBlock *block, int *numerator, int *denominator)
int oaknode_block_get_out(OakNodeBlock block, int *numerator, int *denominator)
{
if (!block) {
olive::Block *b = to_native<olive::Block>(block);
if (!b) {
return OAKNODE_E_INVALID;
}
return get_rational(impl(block)->out(), numerator, denominator);
return get_rational(b->out(), numerator, denominator);
}
int oaknode_block_set_out(OakNodeBlock *block, int numerator, int denominator)
int oaknode_block_set_out(OakNodeBlock block, int numerator, int denominator)
{
if (!block) {
olive::Block *b = to_native<olive::Block>(block);
if (!b) {
return OAKNODE_E_INVALID;
}
impl(block)->set_out(olive::core::Rational(numerator, denominator));
b->set_out(olive::core::Rational(numerator, denominator));
return OAKNODE_OK;
}
int oaknode_block_get_length(OakNodeBlock *block, int *numerator,
int oaknode_block_get_length(OakNodeBlock block, int *numerator,
int *denominator)
{
if (!block) {
olive::Block *b = to_native<olive::Block>(block);
if (!b) {
return OAKNODE_E_INVALID;
}
return get_rational(impl(block)->length(), numerator, denominator);
return get_rational(b->length(), numerator, denominator);
}
int oaknode_block_set_length_and_media_out(OakNodeBlock *block, int numerator,
int oaknode_block_set_length_and_media_out(OakNodeBlock block, int numerator,
int denominator)
{
if (!block) {
olive::Block *b = to_native<olive::Block>(block);
if (!b) {
return OAKNODE_E_INVALID;
}
try {
impl(block)->set_length_and_media_out(
b->set_length_and_media_out(
olive::core::Rational(numerator, denominator));
} catch (...) {
return OAKNODE_E_FAILED;
@@ -167,14 +168,15 @@ int oaknode_block_set_length_and_media_out(OakNodeBlock *block, int numerator,
return OAKNODE_OK;
}
int oaknode_block_set_length_and_media_in(OakNodeBlock *block, int numerator,
int oaknode_block_set_length_and_media_in(OakNodeBlock block, int numerator,
int denominator)
{
if (!block) {
olive::Block *b = to_native<olive::Block>(block);
if (!b) {
return OAKNODE_E_INVALID;
}
try {
impl(block)->set_length_and_media_in(
b->set_length_and_media_in(
olive::core::Rational(numerator, denominator));
} catch (...) {
return OAKNODE_E_FAILED;
@@ -182,102 +184,123 @@ int oaknode_block_set_length_and_media_in(OakNodeBlock *block, int numerator,
return OAKNODE_OK;
}
int oaknode_block_get_enabled(OakNodeBlock *block, int *enabled)
int oaknode_block_get_enabled(OakNodeBlock block, int *enabled)
{
if (!block || !enabled) {
olive::Block *b = to_native<olive::Block>(block);
if (!b || !enabled) {
return OAKNODE_E_INVALID;
}
*enabled = impl(block)->is_enabled() ? 1 : 0;
*enabled = b->is_enabled() ? 1 : 0;
return OAKNODE_OK;
}
int oaknode_block_set_enabled(OakNodeBlock *block, int enabled)
int oaknode_block_set_enabled(OakNodeBlock block, int enabled)
{
if (!block) {
olive::Block *b = to_native<olive::Block>(block);
if (!b) {
return OAKNODE_E_INVALID;
}
impl(block)->set_enabled(enabled != 0);
b->set_enabled(enabled != 0);
return OAKNODE_OK;
}
int oaknode_block_get_previous(OakNodeBlock *block, OakNodeBlock **out)
int oaknode_block_get_previous(OakNodeBlock block, OakNodeBlock *out)
{
if (!block || !out) {
olive::Block *b = to_native<olive::Block>(block);
if (!b || !out) {
return OAKNODE_E_INVALID;
}
*out = wrap(impl(block)->previous());
// Borrowed (empty when there is no neighbour)
*out = make_handle<OakNodeBlock>(b->previous(), false,
&delete_as<olive::Block>);
return OAKNODE_OK;
}
int oaknode_block_get_next(OakNodeBlock *block, OakNodeBlock **out)
int oaknode_block_get_next(OakNodeBlock block, OakNodeBlock *out)
{
if (!block || !out) {
olive::Block *b = to_native<olive::Block>(block);
if (!b || !out) {
return OAKNODE_E_INVALID;
}
*out = wrap(impl(block)->next());
*out = make_handle<OakNodeBlock>(b->next(), false,
&delete_as<olive::Block>);
return OAKNODE_OK;
}
int oaknode_block_get_track(OakNodeBlock *block, OakNodeTrack **out)
int oaknode_block_get_track(OakNodeBlock block, OakNodeTrack *out)
{
if (!block || !out) {
olive::Block *b = to_native<olive::Block>(block);
if (!b || !out) {
return OAKNODE_E_INVALID;
}
*out = reinterpret_cast<OakNodeTrack *>(impl(block)->track());
// Borrowed (empty when the block is not on a track)
*out = make_handle<OakNodeTrack>(b->track(), false,
&delete_as<olive::Track>);
return OAKNODE_OK;
}
int oaknode_block_link(OakNodeBlock *a, OakNodeBlock *b)
int oaknode_block_link(OakNodeBlock a, OakNodeBlock b)
{
if (!a || !b) {
olive::Block *na = to_native<olive::Block>(a);
olive::Block *nb = to_native<olive::Block>(b);
if (!na || !nb) {
return OAKNODE_E_INVALID;
}
return olive::Node::link(impl(a), impl(b)) ? OAKNODE_OK : OAKNODE_E_FAILED;
return olive::Node::link(na, nb) ? OAKNODE_OK : OAKNODE_E_FAILED;
}
int oaknode_block_unlink(OakNodeBlock *a, OakNodeBlock *b)
int oaknode_block_unlink(OakNodeBlock a, OakNodeBlock b)
{
if (!a || !b) {
olive::Block *na = to_native<olive::Block>(a);
olive::Block *nb = to_native<olive::Block>(b);
if (!na || !nb) {
return OAKNODE_E_INVALID;
}
return olive::Node::unlink(impl(a), impl(b)) ? OAKNODE_OK : OAKNODE_E_FAILED;
return olive::Node::unlink(na, nb) ? OAKNODE_OK : OAKNODE_E_FAILED;
}
int oaknode_block_are_linked(OakNodeBlock *a, OakNodeBlock *b, int *linked)
int oaknode_block_are_linked(OakNodeBlock a, OakNodeBlock b, int *linked)
{
if (!a || !b || !linked) {
olive::Block *na = to_native<olive::Block>(a);
olive::Block *nb = to_native<olive::Block>(b);
if (!na || !nb || !linked) {
return OAKNODE_E_INVALID;
}
*linked = olive::Node::are_linked(impl(a), impl(b)) ? 1 : 0;
*linked = olive::Node::are_linked(na, nb) ? 1 : 0;
return OAKNODE_OK;
}
int oaknode_block_get_link_count(OakNodeBlock *block, int *count)
int oaknode_block_get_link_count(OakNodeBlock block, int *count)
{
if (!block || !count) {
olive::Block *b = to_native<olive::Block>(block);
if (!b || !count) {
return OAKNODE_E_INVALID;
}
*count = int(impl(block)->links().size());
*count = int(b->links().size());
return OAKNODE_OK;
}
int oaknode_block_get_link_at(OakNodeBlock *block, int index,
OakNodeBlock **out)
int oaknode_block_get_link_at(OakNodeBlock block, int index,
OakNodeBlock *out)
{
if (!block || !out || index < 0) {
olive::Block *b = to_native<olive::Block>(block);
if (!b || !out || index < 0) {
return OAKNODE_E_INVALID;
}
const auto &links = impl(block)->links();
const auto &links = b->links();
if (index >= int(links.size())) {
return OAKNODE_E_NOT_FOUND;
}
*out = wrap(static_cast<olive::Block *>(links.at(index)));
return OAKNODE_OK;
// Borrowed; the linked block stays owned by its graph
*out = make_handle<OakNodeBlock>(
static_cast<olive::Block *>(links.at(index)), false,
&delete_as<olive::Block>);
return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM;
}
/* ---------------------------------------------------------------- Clip */
int oaknode_clip_get_media_in(OakNodeBlock *clip, int *numerator,
int oaknode_clip_get_media_in(OakNodeBlock clip, int *numerator,
int *denominator)
{
olive::ClipBlock *c = clip_impl(clip);
@@ -287,7 +310,7 @@ int oaknode_clip_get_media_in(OakNodeBlock *clip, int *numerator,
return get_rational(c->media_in(), numerator, denominator);
}
int oaknode_clip_set_media_in(OakNodeBlock *clip, int numerator,
int oaknode_clip_set_media_in(OakNodeBlock clip, int numerator,
int denominator)
{
olive::ClipBlock *c = clip_impl(clip);
@@ -298,7 +321,7 @@ int oaknode_clip_set_media_in(OakNodeBlock *clip, int numerator,
return OAKNODE_OK;
}
int oaknode_clip_get_speed(OakNodeBlock *clip, double *speed)
int oaknode_clip_get_speed(OakNodeBlock clip, double *speed)
{
olive::ClipBlock *c = clip_impl(clip);
if (!c || !speed) {
@@ -308,7 +331,7 @@ int oaknode_clip_get_speed(OakNodeBlock *clip, double *speed)
return OAKNODE_OK;
}
int oaknode_clip_set_speed(OakNodeBlock *clip, double speed)
int oaknode_clip_set_speed(OakNodeBlock clip, double speed)
{
olive::ClipBlock *c = clip_impl(clip);
if (!c) {
@@ -318,7 +341,7 @@ int oaknode_clip_set_speed(OakNodeBlock *clip, double speed)
return OAKNODE_OK;
}
int oaknode_clip_get_reverse(OakNodeBlock *clip, int *reverse)
int oaknode_clip_get_reverse(OakNodeBlock clip, int *reverse)
{
olive::ClipBlock *c = clip_impl(clip);
if (!c || !reverse) {
@@ -328,7 +351,7 @@ int oaknode_clip_get_reverse(OakNodeBlock *clip, int *reverse)
return OAKNODE_OK;
}
int oaknode_clip_set_reverse(OakNodeBlock *clip, int reverse)
int oaknode_clip_set_reverse(OakNodeBlock clip, int reverse)
{
olive::ClipBlock *c = clip_impl(clip);
if (!c) {
@@ -338,7 +361,7 @@ int oaknode_clip_set_reverse(OakNodeBlock *clip, int reverse)
return OAKNODE_OK;
}
int oaknode_clip_get_maintain_audio_pitch(OakNodeBlock *clip, int *maintain)
int oaknode_clip_get_maintain_audio_pitch(OakNodeBlock clip, int *maintain)
{
olive::ClipBlock *c = clip_impl(clip);
if (!c || !maintain) {
@@ -348,7 +371,7 @@ int oaknode_clip_get_maintain_audio_pitch(OakNodeBlock *clip, int *maintain)
return OAKNODE_OK;
}
int oaknode_clip_set_maintain_audio_pitch(OakNodeBlock *clip, int maintain)
int oaknode_clip_set_maintain_audio_pitch(OakNodeBlock clip, int maintain)
{
olive::ClipBlock *c = clip_impl(clip);
if (!c) {
@@ -358,7 +381,7 @@ int oaknode_clip_set_maintain_audio_pitch(OakNodeBlock *clip, int maintain)
return OAKNODE_OK;
}
int oaknode_clip_get_loop_mode(OakNodeBlock *clip, int *loop_mode)
int oaknode_clip_get_loop_mode(OakNodeBlock clip, int *loop_mode)
{
olive::ClipBlock *c = clip_impl(clip);
if (!c || !loop_mode) {
@@ -368,7 +391,7 @@ int oaknode_clip_get_loop_mode(OakNodeBlock *clip, int *loop_mode)
return OAKNODE_OK;
}
int oaknode_clip_set_loop_mode(OakNodeBlock *clip, int loop_mode)
int oaknode_clip_set_loop_mode(OakNodeBlock clip, int loop_mode)
{
olive::ClipBlock *c = clip_impl(clip);
if (!c) {
@@ -378,7 +401,7 @@ int oaknode_clip_set_loop_mode(OakNodeBlock *clip, int loop_mode)
return OAKNODE_OK;
}
int oaknode_clip_get_track_type(OakNodeBlock *clip, int *type)
int oaknode_clip_get_track_type(OakNodeBlock clip, int *type)
{
olive::ClipBlock *c = clip_impl(clip);
if (!c || !type) {
@@ -390,7 +413,7 @@ int oaknode_clip_get_track_type(OakNodeBlock *clip, int *type)
/* ----------------------------------------------------------- Transition */
int oaknode_transition_get_in_offset(OakNodeBlock *transition, int *numerator,
int oaknode_transition_get_in_offset(OakNodeBlock transition, int *numerator,
int *denominator)
{
olive::TransitionBlock *t = transition_impl(transition);
@@ -400,7 +423,7 @@ int oaknode_transition_get_in_offset(OakNodeBlock *transition, int *numerator,
return get_rational(t->in_offset(), numerator, denominator);
}
int oaknode_transition_get_out_offset(OakNodeBlock *transition, int *numerator,
int oaknode_transition_get_out_offset(OakNodeBlock transition, int *numerator,
int *denominator)
{
olive::TransitionBlock *t = transition_impl(transition);
@@ -410,7 +433,7 @@ int oaknode_transition_get_out_offset(OakNodeBlock *transition, int *numerator,
return get_rational(t->out_offset(), numerator, denominator);
}
int oaknode_transition_get_offset_center(OakNodeBlock *transition,
int oaknode_transition_get_offset_center(OakNodeBlock transition,
int *numerator, int *denominator)
{
olive::TransitionBlock *t = transition_impl(transition);
@@ -420,7 +443,7 @@ int oaknode_transition_get_offset_center(OakNodeBlock *transition,
return get_rational(t->offset_center(), numerator, denominator);
}
int oaknode_transition_set_offset_center(OakNodeBlock *transition,
int oaknode_transition_set_offset_center(OakNodeBlock transition,
int numerator, int denominator)
{
olive::TransitionBlock *t = transition_impl(transition);
@@ -431,7 +454,7 @@ int oaknode_transition_set_offset_center(OakNodeBlock *transition,
return OAKNODE_OK;
}
int oaknode_transition_set_offsets_and_length(OakNodeBlock *transition,
int oaknode_transition_set_offsets_and_length(OakNodeBlock transition,
int in_num, int in_den,
int out_num, int out_den)
{
@@ -444,7 +467,7 @@ int oaknode_transition_set_offsets_and_length(OakNodeBlock *transition,
return OAKNODE_OK;
}
int oaknode_transition_is_dual(OakNodeBlock *transition, int *dual)
int oaknode_transition_is_dual(OakNodeBlock transition, int *dual)
{
olive::TransitionBlock *t = transition_impl(transition);
if (!t || !dual) {
@@ -454,35 +477,34 @@ int oaknode_transition_is_dual(OakNodeBlock *transition, int *dual)
return OAKNODE_OK;
}
int oaknode_transition_get_connected_out_block(OakNodeBlock *transition,
OakNodeBlock **out)
int oaknode_transition_get_connected_out_block(OakNodeBlock transition,
OakNodeBlock *out)
{
olive::TransitionBlock *t = transition_impl(transition);
if (!t || !out) {
return OAKNODE_E_INVALID;
}
*out = wrap(t->connected_out_block());
// Borrowed (empty when unconnected)
*out = make_handle<OakNodeBlock>(t->connected_out_block(), false,
&delete_as<olive::Block>);
return OAKNODE_OK;
}
int oaknode_transition_get_connected_in_block(OakNodeBlock *transition,
OakNodeBlock **out)
int oaknode_transition_get_connected_in_block(OakNodeBlock transition,
OakNodeBlock *out)
{
olive::TransitionBlock *t = transition_impl(transition);
if (!t || !out) {
return OAKNODE_E_INVALID;
}
*out = wrap(t->connected_in_block());
*out = make_handle<OakNodeBlock>(t->connected_in_block(), false,
&delete_as<olive::Block>);
return OAKNODE_OK;
}
int oaknode_clip_add_cache_passthrough_from(OakNodeBlock *clip,
OakNodeBlock *other)
int oaknode_clip_add_cache_passthrough_from(OakNodeBlock clip,
OakNodeBlock other)
{
if (!clip || !other) {
return OAKNODE_E_INVALID;
}
olive::ClipBlock *c = clip_impl(clip);
olive::ClipBlock *o = clip_impl(other);
if (!c || !o) {
@@ -497,27 +519,31 @@ int oaknode_clip_add_cache_passthrough_from(OakNodeBlock *clip,
}
}
OakNodeNode *oaknode_block_as_node(OakNodeBlock *block)
OakNodeNode oaknode_block_as_node(OakNodeBlock block)
{
return reinterpret_cast<OakNodeNode *>(block);
// Borrowed; releasing the result never destroys the block
return make_handle<OakNodeNode>(to_native<olive::Block>(block), false,
&delete_as<olive::Node>);
}
OakNodeBlock *oaknode_block_from_node(OakNodeNode *node)
OakNodeBlock oaknode_block_from_node(OakNodeNode node)
{
if (!node) {
return NULL;
olive::Node *n = to_native<olive::Node>(node);
if (!n) {
return OakNodeBlock{};
}
olive::Node *n = reinterpret_cast<olive::Node *>(node);
return wrap(dynamic_cast<olive::Block *>(n));
// Borrowed (empty when the node is not a Block)
return make_handle<OakNodeBlock>(dynamic_cast<olive::Block *>(n), false,
&delete_as<olive::Block>);
}
int oaknode_block_get_kind(OakNodeBlock *block, int *out_kind)
int oaknode_block_get_kind(OakNodeBlock block, int *out_kind)
{
if (!block || !out_kind) {
olive::Block *b = to_native<olive::Block>(block);
if (!b || !out_kind) {
return OAKNODE_E_INVALID;
}
olive::Block *b = impl(block);
if (dynamic_cast<olive::TransitionBlock *>(b)) {
*out_kind = OAKNODE_BLOCK_TRANSITION;
} else if (dynamic_cast<olive::ClipBlock *>(b)) {
+132 -105
View File
@@ -22,15 +22,11 @@
#include <cstring>
#include "alivecount.h"
#include "color/colormanager/colormanager.h"
#include "colortransform.h"
#include "project.h"
struct OakNodeColorManager {
olive::ColorManager impl;
};
#include "nodehandle.h"
namespace
{
@@ -44,9 +40,9 @@ int copy_string(const std::string &value, char *buf, int buf_size)
return needed;
}
bool has_config(olive::ColorManager *cm)
bool has_config(const olive::ColorManager *cm)
{
return cm && cm->get_config() != nullptr;
return cm->get_config() != nullptr;
}
int list_at(const olive::StringList &list, int index, char *buf, int buf_size)
@@ -59,37 +55,35 @@ int list_at(const olive::StringList &list, int index, char *buf, int buf_size)
} // namespace
OakNodeColorManager *oaknode_colormanager_init(OakNodeProject *project)
OakNodeColorManager oaknode_colormanager_init(OakNodeProject project)
{
if (!project) {
return nullptr;
olive::Project *native = oaknode_c_api::to_native<olive::Project>(project);
if (!native) {
return OakNodeColorManager{};
}
try {
auto *m = new OakNodeColorManager{
olive::ColorManager(reinterpret_cast<olive::Project *>(project))};
oaknode_c_api::alive_inc();
return m;
return oaknode_c_api::make_handle<OakNodeColorManager>(
new olive::ColorManager(native), true,
&oaknode_c_api::delete_as<olive::ColorManager>);
} catch (...) {
return nullptr;
return OakNodeColorManager{};
}
}
void oaknode_colormanager_free(OakNodeColorManager *manager)
{
if (!manager) {
return;
}
delete manager;
oaknode_c_api::alive_dec();
oaknode_c_api::free_handle(manager);
}
int oaknode_colormanager_initialize(OakNodeColorManager *manager)
int oaknode_colormanager_initialize(OakNodeColorManager manager)
{
if (!manager) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm) {
return OAKNODE_E_INVALID;
}
try {
manager->impl.init();
cm->init();
} catch (...) {
return OAKNODE_E_FAILED;
}
@@ -106,33 +100,39 @@ int oaknode_colormanager_set_up_default_config(void)
return OAKNODE_OK;
}
int oaknode_colormanager_get_config_filename(OakNodeColorManager *manager,
int oaknode_colormanager_get_config_filename(OakNodeColorManager manager,
char *buf, int buf_size)
{
if (!manager) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm) {
return OAKNODE_E_INVALID;
}
return copy_string(manager->impl.get_config_filename(), buf, buf_size);
return copy_string(cm->get_config_filename(), buf, buf_size);
}
int oaknode_colormanager_set_config_filename(OakNodeColorManager *manager,
int oaknode_colormanager_set_config_filename(OakNodeColorManager manager,
const char *filename)
{
if (!manager || !filename) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm || !filename) {
return OAKNODE_E_INVALID;
}
manager->impl.set_config_filename(filename);
cm->set_config_filename(filename);
return OAKNODE_OK;
}
int oaknode_colormanager_update_config_from_filename(
OakNodeColorManager *manager)
OakNodeColorManager manager)
{
if (!manager) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm) {
return OAKNODE_E_INVALID;
}
try {
manager->impl.update_config_from_filename();
cm->update_config_from_filename();
} catch (...) {
return OAKNODE_E_FAILED;
}
@@ -140,212 +140,240 @@ int oaknode_colormanager_update_config_from_filename(
}
int oaknode_colormanager_get_default_input_color_space(
OakNodeColorManager *manager, char *buf, int buf_size)
OakNodeColorManager manager, char *buf, int buf_size)
{
if (!manager) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm) {
return OAKNODE_E_INVALID;
}
return copy_string(manager->impl.get_default_input_color_space(), buf,
buf_size);
return copy_string(cm->get_default_input_color_space(), buf, buf_size);
}
int oaknode_colormanager_set_default_input_color_space(
OakNodeColorManager *manager, const char *colorspace)
OakNodeColorManager manager, const char *colorspace)
{
if (!manager || !colorspace) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm || !colorspace) {
return OAKNODE_E_INVALID;
}
manager->impl.set_default_input_color_space(colorspace);
cm->set_default_input_color_space(colorspace);
return OAKNODE_OK;
}
int oaknode_colormanager_get_reference_color_space(
OakNodeColorManager *manager, char *buf, int buf_size)
OakNodeColorManager manager, char *buf, int buf_size)
{
if (!manager) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm) {
return OAKNODE_E_INVALID;
}
return copy_string(manager->impl.get_reference_color_space(), buf,
buf_size);
return copy_string(cm->get_reference_color_space(), buf, buf_size);
}
int oaknode_colormanager_get_compliant_color_space(
OakNodeColorManager *manager, const char *colorspace, char *buf,
OakNodeColorManager manager, const char *colorspace, char *buf,
int buf_size)
{
if (!manager || !colorspace) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm || !colorspace) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
return copy_string(manager->impl.get_compliant_color_space(colorspace), buf,
return copy_string(cm->get_compliant_color_space(colorspace), buf,
buf_size);
}
int oaknode_colormanager_get_colorspace_for_ffmpeg_tags(
OakNodeColorManager *manager, int primaries, int trc, char *buf,
OakNodeColorManager manager, int primaries, int trc, char *buf,
int buf_size)
{
if (!manager) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
return copy_string(
manager->impl.get_colorspace_for_ffmpeg_tags(primaries, trc), buf,
buf_size);
return copy_string(cm->get_colorspace_for_ffmpeg_tags(primaries, trc), buf,
buf_size);
}
int oaknode_colormanager_get_display_count(OakNodeColorManager *manager,
int oaknode_colormanager_get_display_count(OakNodeColorManager manager,
int *count)
{
if (!manager || !count) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm || !count) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
*count = int(manager->impl.list_available_displays().size());
*count = int(cm->list_available_displays().size());
return OAKNODE_OK;
}
int oaknode_colormanager_get_display_at(OakNodeColorManager *manager,
int oaknode_colormanager_get_display_at(OakNodeColorManager manager,
int index, char *buf, int buf_size)
{
if (!manager) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
return list_at(manager->impl.list_available_displays(), index, buf,
buf_size);
return list_at(cm->list_available_displays(), index, buf, buf_size);
}
int oaknode_colormanager_get_default_display(OakNodeColorManager *manager,
int oaknode_colormanager_get_default_display(OakNodeColorManager manager,
char *buf, int buf_size)
{
if (!manager) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
return copy_string(manager->impl.get_default_display(), buf, buf_size);
return copy_string(cm->get_default_display(), buf, buf_size);
}
int oaknode_colormanager_get_view_count(OakNodeColorManager *manager,
int oaknode_colormanager_get_view_count(OakNodeColorManager manager,
const char *display, int *count)
{
if (!manager || !display || !count) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm || !display || !count) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
*count = int(manager->impl.list_available_views(display).size());
*count = int(cm->list_available_views(display).size());
return OAKNODE_OK;
}
int oaknode_colormanager_get_view_at(OakNodeColorManager *manager,
int oaknode_colormanager_get_view_at(OakNodeColorManager manager,
const char *display, int index, char *buf,
int buf_size)
{
if (!manager || !display) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm || !display) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
return list_at(manager->impl.list_available_views(display), index, buf,
buf_size);
return list_at(cm->list_available_views(display), index, buf, buf_size);
}
int oaknode_colormanager_get_default_view(OakNodeColorManager *manager,
int oaknode_colormanager_get_default_view(OakNodeColorManager manager,
const char *display, char *buf,
int buf_size)
{
if (!manager || !display) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm || !display) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
return copy_string(manager->impl.get_default_view(display), buf, buf_size);
return copy_string(cm->get_default_view(display), buf, buf_size);
}
int oaknode_colormanager_get_look_count(OakNodeColorManager *manager,
int oaknode_colormanager_get_look_count(OakNodeColorManager manager,
int *count)
{
if (!manager || !count) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm || !count) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
*count = int(manager->impl.list_available_looks().size());
*count = int(cm->list_available_looks().size());
return OAKNODE_OK;
}
int oaknode_colormanager_get_look_at(OakNodeColorManager *manager, int index,
int oaknode_colormanager_get_look_at(OakNodeColorManager manager, int index,
char *buf, int buf_size)
{
if (!manager) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
return list_at(manager->impl.list_available_looks(), index, buf, buf_size);
return list_at(cm->list_available_looks(), index, buf, buf_size);
}
int oaknode_colormanager_get_colorspace_count(OakNodeColorManager *manager,
int oaknode_colormanager_get_colorspace_count(OakNodeColorManager manager,
int *count)
{
if (!manager || !count) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm || !count) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
*count = int(manager->impl.list_available_colorspaces().size());
*count = int(cm->list_available_colorspaces().size());
return OAKNODE_OK;
}
int oaknode_colormanager_get_colorspace_at(OakNodeColorManager *manager,
int oaknode_colormanager_get_colorspace_at(OakNodeColorManager manager,
int index, char *buf,
int buf_size)
{
if (!manager) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
return list_at(manager->impl.list_available_colorspaces(), index, buf,
buf_size);
return list_at(cm->list_available_colorspaces(), index, buf, buf_size);
}
int oaknode_colormanager_get_default_luma_coefs(OakNodeColorManager *manager,
int oaknode_colormanager_get_default_luma_coefs(OakNodeColorManager manager,
double rgb[3])
{
if (!manager || !rgb) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm || !rgb) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
manager->impl.get_default_luma_coefs(rgb);
cm->get_default_luma_coefs(rgb);
return OAKNODE_OK;
}
int oaknode_colormanager_get_compliant_color_transform(
OakNodeColorManager *manager, OakColorTransform transform,
OakNodeColorManager manager, OakColorTransform transform,
int force_display, OakColorTransform *out)
{
if (!manager || !out) {
olive::ColorManager *cm =
oaknode_c_api::to_native<olive::ColorManager>(manager);
if (!cm || !out) {
return OAKNODE_E_INVALID;
}
const olive::ColorTransform *native =
@@ -353,13 +381,12 @@ int oaknode_colormanager_get_compliant_color_transform(
if (!native) {
return OAKNODE_E_INVALID;
}
if (!has_config(&manager->impl)) {
if (!has_config(cm)) {
return OAKNODE_E_STATE;
}
try {
const olive::ColorTransform compliant =
manager->impl.get_compliant_color_space(*native,
force_display != 0);
cm->get_compliant_color_space(*native, force_display != 0);
*out = oakcommon_colortransform_init_from_native(&compliant);
} catch (...) {
return OAKNODE_E_NOMEM;
+11 -19
View File
@@ -22,17 +22,10 @@
#include "factory.h"
#include "nodehandle.h"
#include "valueconvert.h"
namespace
{
inline OakNodeNode *from_node(olive::Node *node)
{
return reinterpret_cast<OakNodeNode *>(node);
}
}
using oaknode_c_api::make_handle;
int oaknode_factory_initialize(void)
{
@@ -104,24 +97,22 @@ int oaknode_factory_name_from_id(const char *type_id, char *buf,
}
}
OakNodeNode *oaknode_factory_create_from_id(const char *type_id)
OakNodeNode oaknode_factory_create_from_id(const char *type_id)
{
if (!type_id) {
return NULL;
return OakNodeNode{};
}
try {
olive::Node *node = olive::NodeFactory::create_from_id(type_id);
if (node) {
oaknode_c_api::alive_inc();
}
return from_node(node);
return make_handle<OakNodeNode>(
olive::NodeFactory::create_from_id(type_id), true,
oaknode_c_api::delete_as<olive::Node>);
} catch (...) {
return NULL;
return OakNodeNode{};
}
}
int oaknode_factory_node_at(int index, OakNodeNode **out_node)
int oaknode_factory_node_at(int index, OakNodeNode *out_node)
{
if (!out_node) {
return OAKNODE_E_INVALID;
@@ -136,7 +127,8 @@ int oaknode_factory_node_at(int index, OakNodeNode **out_node)
if (index < 0 || index >= static_cast<int>(library.size())) {
return OAKNODE_E_NOT_FOUND;
}
*out_node = from_node(library[size_t(index)]);
*out_node = make_handle<OakNodeNode>(library[size_t(index)], false,
nullptr);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
+71 -84
View File
@@ -24,97 +24,75 @@
#include <new>
#include "node/node.h"
#include "../src/project.h"
#include "../src/project/folder/folder.h"
namespace
{
#include "nodehandle.h"
olive::Folder *to_cpp(OakNodeFolder *folder)
{
return reinterpret_cast<olive::Folder *>(folder);
}
using oaknode_c_api::delete_as;
using oaknode_c_api::make_handle;
using oaknode_c_api::to_native;
const olive::Folder *to_cpp(const OakNodeFolder *folder)
OakNodeFolder oaknode_folder_create(OakNodeProject project)
{
return reinterpret_cast<const olive::Folder *>(folder);
}
olive::Node *to_cpp(OakNodeNode *node)
{
return reinterpret_cast<olive::Node *>(node);
}
const olive::Node *to_cpp(const OakNodeNode *node)
{
return reinterpret_cast<const olive::Node *>(node);
}
OakNodeNode *to_c(olive::Node *node)
{
return reinterpret_cast<OakNodeNode *>(node);
}
olive::Project *to_cpp(OakNodeProject *project)
{
return reinterpret_cast<olive::Project *>(project);
}
} // namespace
OakNodeFolder *oaknode_folder_create(OakNodeProject *project)
{
if (!project) {
return NULL;
if (!project.ctx) {
return OakNodeFolder{};
}
try {
auto *folder = new (std::nothrow) olive::Folder();
if (!folder) {
return NULL;
return OakNodeFolder{};
}
to_cpp(project)->add_node(folder);
return reinterpret_cast<OakNodeFolder *>(folder);
to_native<olive::Project>(project)->add_node(folder);
// Borrowed handle: the project graph owns the folder.
return make_handle<OakNodeFolder>(folder, false,
&delete_as<olive::Folder>);
} catch (...) {
return NULL;
return OakNodeFolder{};
}
}
int oaknode_folder_child_count(const OakNodeFolder *folder)
int oaknode_folder_child_count(OakNodeFolder folder)
{
if (!folder) {
if (!folder.ctx) {
return OAKNODE_E_INVALID;
}
try {
return to_cpp(folder)->item_child_count();
return to_native<olive::Folder>(folder)->item_child_count();
} catch (...) {
return OAKNODE_E_FAILED;
}
}
OakNodeNode *oaknode_folder_child_at(const OakNodeFolder *folder, int index)
OakNodeNode oaknode_folder_child_at(OakNodeFolder folder, int index)
{
if (!folder || index < 0 || index >= to_cpp(folder)->item_child_count()) {
return NULL;
if (!folder.ctx || index < 0 ||
index >= to_native<olive::Folder>(folder)->item_child_count()) {
return OakNodeNode{};
}
try {
return to_c(to_cpp(folder)->item_child(index));
return make_handle<OakNodeNode>(
to_native<olive::Folder>(folder)->item_child(index), false,
&delete_as<olive::Node>);
} catch (...) {
return NULL;
return OakNodeNode{};
}
}
int oaknode_folder_add_child(OakNodeFolder *folder, OakNodeNode *child)
int oaknode_folder_add_child(OakNodeFolder folder, OakNodeNode child)
{
if (!folder || !child) {
if (!folder.ctx || !child.ctx) {
return OAKNODE_E_INVALID;
}
try {
olive::Folder *f = to_cpp(folder);
olive::Node *c = to_cpp(child);
olive::Folder *f = to_native<olive::Folder>(folder);
olive::Node *c = to_native<olive::Node>(child);
if (c->folder()) {
return OAKNODE_E_STATE;
@@ -122,21 +100,24 @@ int oaknode_folder_add_child(OakNodeFolder *folder, OakNodeNode *child)
olive::FolderAddChild cmd(f, c);
cmd.redo_now();
// The graph now owns the child; releasing `child` must not
// delete it.
oaknode_c_api::mark_container_owned(child);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_folder_remove_child(OakNodeFolder *folder, OakNodeNode *child)
int oaknode_folder_remove_child(OakNodeFolder folder, OakNodeNode child)
{
if (!folder || !child) {
if (!folder.ctx || !child.ctx) {
return OAKNODE_E_INVALID;
}
try {
olive::Folder *f = to_cpp(folder);
olive::Node *c = to_cpp(child);
olive::Folder *f = to_native<olive::Folder>(folder);
olive::Node *c = to_native<olive::Node>(child);
if (f->index_of_child(c) == -1) {
return OAKNODE_E_NOT_FOUND;
@@ -150,22 +131,22 @@ int oaknode_folder_remove_child(OakNodeFolder *folder, OakNodeNode *child)
}
}
int oaknode_folder_move_children(OakNodeNode *const *nodes, int count,
OakNodeFolder *dest_folder)
int oaknode_folder_move_children(const OakNodeNode *nodes, int count,
OakNodeFolder dest_folder)
{
if (!nodes || count < 0 || !dest_folder) {
if (!nodes || count < 0 || !dest_folder.ctx) {
return OAKNODE_E_INVALID;
}
try {
olive::Folder *dest = to_cpp(dest_folder);
olive::Folder *dest = to_native<olive::Folder>(dest_folder);
for (int i = 0; i < count; i++) {
if (!nodes[i]) {
if (!nodes[i].ctx) {
return OAKNODE_E_INVALID;
}
olive::Node *node = to_cpp(nodes[i]);
olive::Node *node = to_native<olive::Node>(nodes[i]);
olive::Folder *old_folder = node->folder();
if (old_folder == dest) {
@@ -179,6 +160,9 @@ int oaknode_folder_move_children(OakNodeNode *const *nodes, int count,
olive::FolderAddChild add_cmd(dest, node);
add_cmd.redo_now();
// The graph owns the moved node; releasing the caller's
// handle must not delete it.
oaknode_c_api::mark_container_owned(nodes[i]);
}
return OAKNODE_OK;
@@ -187,16 +171,16 @@ int oaknode_folder_move_children(OakNodeNode *const *nodes, int count,
}
}
int oaknode_folder_has_child_recursive(const OakNodeFolder *folder,
const OakNodeNode *child)
int oaknode_folder_has_child_recursive(OakNodeFolder folder,
OakNodeNode child)
{
if (!folder || !child) {
if (!folder.ctx || !child.ctx) {
return OAKNODE_E_INVALID;
}
try {
return to_cpp(folder)->has_child_recursive(
const_cast<olive::Node *>(to_cpp(child)))
return to_native<olive::Folder>(folder)->has_child_recursive(
to_native<olive::Node>(child))
? 1
: 0;
} catch (...) {
@@ -204,52 +188,55 @@ int oaknode_folder_has_child_recursive(const OakNodeFolder *folder,
}
}
int oaknode_folder_index_of_child(const OakNodeFolder *folder,
const OakNodeNode *child)
int oaknode_folder_index_of_child(OakNodeFolder folder,
OakNodeNode child)
{
if (!folder || !child) {
if (!folder.ctx || !child.ctx) {
return OAKNODE_E_INVALID;
}
try {
int index = to_cpp(folder)->index_of_child(
const_cast<olive::Node *>(to_cpp(child)));
int index = to_native<olive::Folder>(folder)->index_of_child(
to_native<olive::Node>(child));
return index == -1 ? OAKNODE_E_NOT_FOUND : index;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
OakNodeFolder *oaknode_folder_parent_of(const OakNodeNode *node)
OakNodeFolder oaknode_folder_parent_of(OakNodeNode node)
{
if (!node) {
return NULL;
if (!node.ctx) {
return OakNodeFolder{};
}
try {
return reinterpret_cast<OakNodeFolder *>(to_cpp(node)->folder());
return make_handle<OakNodeFolder>(
to_native<olive::Node>(node)->folder(), false,
&delete_as<olive::Folder>);
} catch (...) {
return NULL;
return OakNodeFolder{};
}
}
OakNodeNode *oaknode_folder_as_node(OakNodeFolder *folder)
OakNodeNode oaknode_folder_as_node(OakNodeFolder folder)
{
return reinterpret_cast<OakNodeNode *>(folder);
// Borrowed cast: same object, the handle only releases itself.
return make_handle<OakNodeNode>(to_native<olive::Folder>(folder), false,
&delete_as<olive::Node>);
}
OakUndoCommand oaknode_command_create_folder_add_child(
OakNodeFolder *folder, OakNodeNode *child)
OakNodeFolder folder, OakNodeNode child)
{
if (!folder || !child) {
if (!folder.ctx || !child.ctx) {
return OakUndoCommand{};
}
try {
return oakundo_capi::make_command_handle(
new olive::FolderAddChild(
reinterpret_cast<olive::Folder *>(folder),
reinterpret_cast<olive::Node *>(child)),
new olive::FolderAddChild(to_native<olive::Folder>(folder),
to_native<olive::Node>(child)),
true);
} catch (...) {
return OakUndoCommand{};
+86 -84
View File
@@ -27,27 +27,16 @@
#include <new>
#include <string>
#include "node/node.h"
#include "../src/project.h"
#include "../src/project/footage/footage.h"
#include "nodehandle.h"
namespace
{
olive::Footage *to_cpp(OakNodeFootage *footage)
{
return reinterpret_cast<olive::Footage *>(footage);
}
const olive::Footage *to_cpp(const OakNodeFootage *footage)
{
return reinterpret_cast<const olive::Footage *>(footage);
}
olive::Project *to_cpp(OakNodeProject *project)
{
return reinterpret_cast<olive::Project *>(project);
}
/**
* @brief Shared two-stage string getter.
*
@@ -72,167 +61,176 @@ int copy_string(const std::string &value, char *buf, int buf_size)
} // namespace
OakNodeFootage *oaknode_footage_create(OakNodeProject *project,
const char *filename)
using oaknode_c_api::delete_as;
using oaknode_c_api::make_handle;
using oaknode_c_api::to_native;
OakNodeFootage oaknode_footage_create(OakNodeProject project,
const char *filename)
{
if (!project) {
return NULL;
if (!project.ctx) {
return OakNodeFootage{};
}
try {
auto *footage = new (std::nothrow)
olive::Footage(filename ? filename : "");
if (!footage) {
return NULL;
return OakNodeFootage{};
}
to_cpp(project)->add_node(footage);
return reinterpret_cast<OakNodeFootage *>(footage);
to_native<olive::Project>(project)->add_node(footage);
// Borrowed handle: the project graph owns the footage.
return make_handle<OakNodeFootage>(footage, false,
&delete_as<olive::Footage>);
} catch (...) {
return NULL;
return OakNodeFootage{};
}
}
int oaknode_footage_filename(const OakNodeFootage *footage, char *buf,
int oaknode_footage_filename(OakNodeFootage footage, char *buf,
int buf_size)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
return copy_string(to_cpp(footage)->filename(), buf, buf_size);
return copy_string(to_native<olive::Footage>(footage)->filename(), buf,
buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_set_filename(OakNodeFootage *footage, const char *filename)
int oaknode_footage_set_filename(OakNodeFootage footage, const char *filename)
{
if (!footage || !filename) {
if (!footage.ctx || !filename) {
return OAKNODE_E_INVALID;
}
try {
to_cpp(footage)->set_filename(filename);
to_native<olive::Footage>(footage)->set_filename(filename);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_is_valid(const OakNodeFootage *footage)
int oaknode_footage_is_valid(OakNodeFootage footage)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
return to_cpp(footage)->is_valid() ? 1 : 0;
return to_native<olive::Footage>(footage)->is_valid() ? 1 : 0;
}
int oaknode_footage_timestamp(const OakNodeFootage *footage,
int oaknode_footage_timestamp(OakNodeFootage footage,
int64_t *out_timestamp)
{
if (!footage || !out_timestamp) {
if (!footage.ctx || !out_timestamp) {
return OAKNODE_E_INVALID;
}
try {
*out_timestamp = to_cpp(footage)->timestamp();
*out_timestamp = to_native<olive::Footage>(footage)->timestamp();
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_set_timestamp(OakNodeFootage *footage, int64_t timestamp)
int oaknode_footage_set_timestamp(OakNodeFootage footage, int64_t timestamp)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
to_cpp(footage)->set_timestamp(timestamp);
to_native<olive::Footage>(footage)->set_timestamp(timestamp);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_decoder(const OakNodeFootage *footage, char *buf,
int oaknode_footage_decoder(OakNodeFootage footage, char *buf,
int buf_size)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
return copy_string(to_cpp(footage)->decoder(), buf, buf_size);
return copy_string(to_native<olive::Footage>(footage)->decoder(), buf,
buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_total_stream_count(const OakNodeFootage *footage)
int oaknode_footage_total_stream_count(OakNodeFootage footage)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
return to_cpp(footage)->get_total_stream_count();
return to_native<olive::Footage>(footage)->get_total_stream_count();
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_video_stream_count(const OakNodeFootage *footage)
int oaknode_footage_video_stream_count(OakNodeFootage footage)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
return to_cpp(footage)->get_video_stream_count();
return to_native<olive::Footage>(footage)->get_video_stream_count();
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_audio_stream_count(const OakNodeFootage *footage)
int oaknode_footage_audio_stream_count(OakNodeFootage footage)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
return to_cpp(footage)->get_audio_stream_count();
return to_native<olive::Footage>(footage)->get_audio_stream_count();
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_subtitle_stream_count(const OakNodeFootage *footage)
int oaknode_footage_subtitle_stream_count(OakNodeFootage footage)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
return to_cpp(footage)->get_subtitle_stream_count();
return to_native<olive::Footage>(footage)->get_subtitle_stream_count();
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_duration(const OakNodeFootage *footage, int *out_numerator,
int oaknode_footage_duration(OakNodeFootage footage, int *out_numerator,
int *out_denominator)
{
if (!footage || !out_numerator || !out_denominator) {
if (!footage.ctx || !out_numerator || !out_denominator) {
return OAKNODE_E_INVALID;
}
try {
const olive::Rational &length = to_cpp(footage)->get_length();
const olive::Rational &length =
to_native<olive::Footage>(footage)->get_length();
*out_numerator = length.numerator();
*out_denominator = length.denominator();
return OAKNODE_OK;
@@ -241,66 +239,68 @@ int oaknode_footage_duration(const OakNodeFootage *footage, int *out_numerator,
}
}
int oaknode_footage_proxy_enabled(const OakNodeFootage *footage)
int oaknode_footage_proxy_enabled(OakNodeFootage footage)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
return to_cpp(footage)->proxy_enabled() ? 1 : 0;
return to_native<olive::Footage>(footage)->proxy_enabled() ? 1 : 0;
}
int oaknode_footage_set_proxy_enabled(OakNodeFootage *footage, int enabled)
int oaknode_footage_set_proxy_enabled(OakNodeFootage footage, int enabled)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
to_cpp(footage)->set_proxy_enabled(enabled != 0);
to_native<olive::Footage>(footage)->set_proxy_enabled(enabled != 0);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_proxy_path(const OakNodeFootage *footage, char *buf,
int oaknode_footage_proxy_path(OakNodeFootage footage, char *buf,
int buf_size)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
return copy_string(to_cpp(footage)->proxy_path(), buf, buf_size);
return copy_string(to_native<olive::Footage>(footage)->proxy_path(),
buf, buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_proxy_state(const OakNodeFootage *footage)
int oaknode_footage_proxy_state(OakNodeFootage footage)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
return static_cast<int>(to_cpp(footage)->proxy_state());
return static_cast<int>(
to_native<olive::Footage>(footage)->proxy_state());
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_set_proxy(OakNodeFootage *footage, const char *path,
int oaknode_footage_set_proxy(OakNodeFootage footage, const char *path,
int state, int video_stream_index,
int preset_version, int enabled)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
to_cpp(footage)->set_proxy(
to_native<olive::Footage>(footage)->set_proxy(
path ? path : "",
static_cast<olive::ProxyManager::ProxyState>(state),
video_stream_index, preset_version, enabled != 0);
@@ -310,24 +310,24 @@ int oaknode_footage_set_proxy(OakNodeFootage *footage, const char *path,
}
}
int oaknode_footage_clear_proxy(OakNodeFootage *footage)
int oaknode_footage_clear_proxy(OakNodeFootage footage)
{
if (!footage) {
if (!footage.ctx) {
return OAKNODE_E_INVALID;
}
try {
to_cpp(footage)->clear_proxy();
to_native<olive::Footage>(footage)->clear_proxy();
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_footage_get_video_params(OakNodeFootage *footage, int index,
int oaknode_footage_get_video_params(OakNodeFootage footage, int index,
OakVideoParams *out)
{
olive::Footage *f = to_cpp(footage);
olive::Footage *f = to_native<olive::Footage>(footage);
if (!f || !out || index < 0) {
return OAKNODE_E_INVALID;
}
@@ -345,10 +345,10 @@ int oaknode_footage_get_video_params(OakNodeFootage *footage, int index,
}
}
int oaknode_footage_set_video_params(OakNodeFootage *footage, int index,
int oaknode_footage_set_video_params(OakNodeFootage footage, int index,
const OakVideoParams *params)
{
olive::Footage *f = to_cpp(footage);
olive::Footage *f = to_native<olive::Footage>(footage);
if (!f || !params || !params->ctx) {
return OAKNODE_E_INVALID;
}
@@ -367,10 +367,10 @@ int oaknode_footage_set_video_params(OakNodeFootage *footage, int index,
}
}
int oaknode_footage_get_video_length(OakNodeFootage *footage,
int oaknode_footage_get_video_length(OakNodeFootage footage,
int64_t *out_num, int64_t *out_den)
{
olive::Footage *f = to_cpp(footage);
olive::Footage *f = to_native<olive::Footage>(footage);
if (!f || !out_num || !out_den) {
return OAKNODE_E_INVALID;
}
@@ -381,10 +381,10 @@ int oaknode_footage_get_video_length(OakNodeFootage *footage,
return OAKNODE_OK;
}
int oaknode_footage_set_cancel_atom(OakNodeFootage *footage,
int oaknode_footage_set_cancel_atom(OakNodeFootage footage,
OakCancelAtom atom)
{
olive::Footage *f = to_cpp(footage);
olive::Footage *f = to_native<olive::Footage>(footage);
if (!f) {
return OAKNODE_E_INVALID;
}
@@ -394,7 +394,9 @@ int oaknode_footage_set_cancel_atom(OakNodeFootage *footage,
return OAKNODE_OK;
}
OakNodeNode *oaknode_footage_as_node(OakNodeFootage *footage)
OakNodeNode oaknode_footage_as_node(OakNodeFootage footage)
{
return reinterpret_cast<OakNodeNode *>(footage);
// Borrowed cast: same object, the handle only releases itself.
return make_handle<OakNodeNode>(to_native<olive::Footage>(footage), false,
&delete_as<olive::Node>);
}
+73 -88
View File
@@ -22,106 +22,82 @@
#include "group/group.h"
#include "nodehandle.h"
#include "valueconvert.h"
namespace
{
using oaknode_c_api::make_handle;
using oaknode_c_api::to_native;
inline olive::NodeGroup *to_group(OakNodeGroup *group)
{
return reinterpret_cast<olive::NodeGroup *>(group);
}
inline const olive::NodeGroup *to_group(const OakNodeGroup *group)
{
return reinterpret_cast<const olive::NodeGroup *>(group);
}
inline olive::Node *to_node(OakNodeNode *node)
{
return reinterpret_cast<olive::Node *>(node);
}
inline OakNodeNode *from_node(olive::Node *node)
{
return reinterpret_cast<OakNodeNode *>(node);
}
}
OakNodeGroup *oaknode_group_create(void)
OakNodeGroup oaknode_group_create(void)
{
try {
olive::NodeGroup *group = new (std::nothrow) olive::NodeGroup();
if (group) {
oaknode_c_api::alive_inc();
}
return reinterpret_cast<OakNodeGroup *>(group);
return make_handle<OakNodeGroup>(new (std::nothrow) olive::NodeGroup(),
true,
oaknode_c_api::delete_as<olive::NodeGroup>);
} catch (...) {
return NULL;
return OakNodeGroup{};
}
}
OakNodeGroup *oaknode_group_cast(OakNodeNode *node)
OakNodeGroup oaknode_group_cast(OakNodeNode node)
{
if (!node) {
return NULL;
olive::Node *native = to_native<olive::Node>(node);
if (!native) {
return OakNodeGroup{};
}
try {
return reinterpret_cast<OakNodeGroup *>(
dynamic_cast<olive::NodeGroup *>(to_node(node)));
return make_handle<OakNodeGroup>(
dynamic_cast<olive::NodeGroup *>(native), false, nullptr);
} catch (...) {
return NULL;
return OakNodeGroup{};
}
}
void oaknode_group_free(OakNodeGroup *group)
{
if (!group) {
return;
}
try {
delete to_group(group);
oaknode_c_api::alive_dec();
oaknode_c_api::free_handle(group);
} catch (...) {
}
}
int oaknode_group_add_input_passthrough(OakNodeGroup *group,
OakNodeNode *node,
int oaknode_group_add_input_passthrough(OakNodeGroup group,
OakNodeNode node,
const char *input_id, int element,
char *buf, int buf_size)
{
if (!group || !node || !input_id) {
olive::NodeGroup *g = to_native<olive::NodeGroup>(group);
olive::Node *n = to_native<olive::Node>(node);
if (!g || !n || !input_id) {
return OAKNODE_E_INVALID;
}
try {
std::string id = to_group(group)->add_input_passthrough(
olive::NodeInput(to_node(node), input_id, element));
std::string id = g->add_input_passthrough(
olive::NodeInput(n, input_id, element));
return oaknode_c_api::copy_string(id, buf, buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_group_add_input_passthrough_undoable(OakNodeGroup *group,
OakNodeNode *node,
int oaknode_group_add_input_passthrough_undoable(OakNodeGroup group,
OakNodeNode node,
const char *input_id,
int element,
OakUndoCommand *out_command)
{
if (!group || !node || !input_id || !out_command) {
olive::NodeGroup *g = to_native<olive::NodeGroup>(group);
olive::Node *n = to_native<olive::Node>(node);
if (!g || !n || !input_id || !out_command) {
return OAKNODE_E_INVALID;
}
try {
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeGroupAddInputPassthrough(
to_group(group),
olive::NodeInput(to_node(node), input_id, element)));
g, olive::NodeInput(n, input_id, element)));
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
@@ -132,51 +108,54 @@ int oaknode_group_add_input_passthrough_undoable(OakNodeGroup *group,
}
}
int oaknode_group_remove_input_passthrough(OakNodeGroup *group,
OakNodeNode *node,
int oaknode_group_remove_input_passthrough(OakNodeGroup group,
OakNodeNode node,
const char *input_id, int element)
{
if (!group || !node || !input_id) {
olive::NodeGroup *g = to_native<olive::NodeGroup>(group);
olive::Node *n = to_native<olive::Node>(node);
if (!g || !n || !input_id) {
return OAKNODE_E_INVALID;
}
try {
olive::NodeInput input(to_node(node), input_id, element);
if (!to_group(group)->contains_input_passthrough(input)) {
olive::NodeInput input(n, input_id, element);
if (!g->contains_input_passthrough(input)) {
return OAKNODE_E_NOT_FOUND;
}
to_group(group)->remove_input_passthrough(input);
g->remove_input_passthrough(input);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_group_passthrough_count(const OakNodeGroup *group, int *out_count)
int oaknode_group_passthrough_count(OakNodeGroup group, int *out_count)
{
if (!group || !out_count) {
olive::NodeGroup *g = to_native<olive::NodeGroup>(group);
if (!g || !out_count) {
return OAKNODE_E_INVALID;
}
try {
*out_count =
static_cast<int>(to_group(group)->get_input_passthroughs().size());
*out_count = static_cast<int>(g->get_input_passthroughs().size());
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_group_passthrough_id_at(const OakNodeGroup *group, int index,
int oaknode_group_passthrough_id_at(OakNodeGroup group, int index,
char *buf, int buf_size)
{
if (!group) {
olive::NodeGroup *g = to_native<olive::NodeGroup>(group);
if (!g) {
return OAKNODE_E_INVALID;
}
try {
const olive::NodeGroup::InputPassthroughs &passthroughs =
to_group(group)->get_input_passthroughs();
g->get_input_passthroughs();
if (index < 0 || index >= static_cast<int>(passthroughs.size())) {
return OAKNODE_E_NOT_FOUND;
}
@@ -187,24 +166,25 @@ int oaknode_group_passthrough_id_at(const OakNodeGroup *group, int index,
}
}
int oaknode_group_passthrough_input_at(const OakNodeGroup *group, int index,
OakNodeNode **out_node, char *buf,
int oaknode_group_passthrough_input_at(OakNodeGroup group, int index,
OakNodeNode *out_node, char *buf,
int buf_size, int *out_element)
{
if (!group) {
olive::NodeGroup *g = to_native<olive::NodeGroup>(group);
if (!g) {
return OAKNODE_E_INVALID;
}
try {
const olive::NodeGroup::InputPassthroughs &passthroughs =
to_group(group)->get_input_passthroughs();
g->get_input_passthroughs();
if (index < 0 || index >= static_cast<int>(passthroughs.size())) {
return OAKNODE_E_NOT_FOUND;
}
const olive::NodeInput &input = passthroughs[size_t(index)].second;
if (out_node) {
*out_node = from_node(input.node());
*out_node = make_handle<OakNodeNode>(input.node(), false, nullptr);
}
if (out_element) {
*out_element = input.element();
@@ -215,30 +195,33 @@ int oaknode_group_passthrough_input_at(const OakNodeGroup *group, int index,
}
}
int oaknode_group_get_output_passthrough(const OakNodeGroup *group,
OakNodeNode **out_node)
int oaknode_group_get_output_passthrough(OakNodeGroup group,
OakNodeNode *out_node)
{
if (!group || !out_node) {
olive::NodeGroup *g = to_native<olive::NodeGroup>(group);
if (!g || !out_node) {
return OAKNODE_E_INVALID;
}
try {
*out_node = from_node(to_group(group)->get_output_passthrough());
*out_node =
make_handle<OakNodeNode>(g->get_output_passthrough(), false, nullptr);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_group_set_output_passthrough(OakNodeGroup *group,
OakNodeNode *node)
int oaknode_group_set_output_passthrough(OakNodeGroup group,
OakNodeNode node)
{
if (!group) {
olive::NodeGroup *g = to_native<olive::NodeGroup>(group);
if (!g) {
return OAKNODE_E_INVALID;
}
try {
to_group(group)->set_output_passthrough(to_node(node));
g->set_output_passthrough(to_native<olive::Node>(node));
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
@@ -246,16 +229,17 @@ int oaknode_group_set_output_passthrough(OakNodeGroup *group,
}
int oaknode_group_set_output_passthrough_undoable(
OakNodeGroup *group, OakNodeNode *node, OakUndoCommand *out_command)
OakNodeGroup group, OakNodeNode node, OakUndoCommand *out_command)
{
if (!group || !out_command) {
olive::NodeGroup *g = to_native<olive::NodeGroup>(group);
if (!g || !out_command) {
return OAKNODE_E_INVALID;
}
try {
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeGroupSetOutputPassthrough(to_group(group),
to_node(node)));
new olive::NodeGroupSetOutputPassthrough(
g, to_native<olive::Node>(node)));
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
@@ -266,23 +250,24 @@ int oaknode_group_set_output_passthrough_undoable(
}
}
int oaknode_group_resolve_input(OakNodeNode *node, const char *input_id,
int element, OakNodeNode **out_node,
int oaknode_group_resolve_input(OakNodeNode node, const char *input_id,
int element, OakNodeNode *out_node,
char *buf, int buf_size, int *out_element)
{
if (!node || !input_id) {
olive::Node *n = to_native<olive::Node>(node);
if (!n || !input_id) {
return OAKNODE_E_INVALID;
}
try {
olive::NodeInput resolved = olive::NodeGroup::resolve_input(
olive::NodeInput(to_node(node), input_id, element));
olive::NodeInput(n, input_id, element));
if (!resolved.is_valid()) {
return OAKNODE_E_NOT_FOUND;
}
if (out_node) {
*out_node = from_node(resolved.node());
*out_node = make_handle<OakNodeNode>(resolved.node(), false, nullptr);
}
if (out_element) {
*out_element = resolved.element();
+104 -111
View File
@@ -24,31 +24,15 @@
#include "node.h"
#include "nodeundo.h"
#include "nodehandle.h"
#include "valueconvert.h"
using oaknode_c_api::make_handle;
using oaknode_c_api::to_native;
namespace
{
inline olive::NodeKeyframe *to_key(OakNodeKeyframe *keyframe)
{
return reinterpret_cast<olive::NodeKeyframe *>(keyframe);
}
inline const olive::NodeKeyframe *to_key(const OakNodeKeyframe *keyframe)
{
return reinterpret_cast<const olive::NodeKeyframe *>(keyframe);
}
inline olive::Node *to_node(OakNodeNode *node)
{
return reinterpret_cast<olive::Node *>(node);
}
inline OakNodeNode *from_node(olive::Node *node)
{
return reinterpret_cast<OakNodeNode *>(node);
}
/**
* @brief Convert an oaknode_keyframe_type to olive::NodeKeyframe::Type.
* The oaknode enum mirrors the olive ordinals exactly (invalid = -1,
@@ -132,61 +116,56 @@ private:
}
OakNodeKeyframe *oaknode_keyframe_create(int64_t time_num, int64_t time_den,
const oaknode_value *value, int type,
int track, int element,
const char *input_id,
OakNodeNode *parent_or_null)
OakNodeKeyframe oaknode_keyframe_create(int64_t time_num, int64_t time_den,
const oaknode_value *value, int type,
int track, int element,
const char *input_id,
OakNodeNode parent_or_null)
{
olive::NodeKeyframe::Type keyframe_type;
if (!keyframe_type_from_oak(type, &keyframe_type)) {
return NULL;
return OakNodeKeyframe{};
}
try {
olive::Variant variant;
if (value) {
if (!oaknode_c_api::variant_from_value(value, &variant)) {
return NULL;
return OakNodeKeyframe{};
}
}
olive::core::Rational time(static_cast<int>(time_num),
static_cast<int>(time_den));
olive::NodeKeyframe *key = new (std::nothrow) olive::NodeKeyframe(
time, variant, keyframe_type, track, element,
input_id ? input_id : "", to_node(parent_or_null));
if (key) {
oaknode_c_api::alive_inc();
}
return reinterpret_cast<OakNodeKeyframe *>(key);
return make_handle<OakNodeKeyframe>(
new (std::nothrow) olive::NodeKeyframe(
time, variant, keyframe_type, track, element,
input_id ? input_id : "",
to_native<olive::Node>(parent_or_null)),
true, oaknode_c_api::delete_as<olive::NodeKeyframe>);
} catch (...) {
return NULL;
return OakNodeKeyframe{};
}
}
void oaknode_keyframe_free(OakNodeKeyframe *keyframe)
{
if (!keyframe) {
return;
}
try {
delete to_key(keyframe);
oaknode_c_api::alive_dec();
oaknode_c_api::free_handle(keyframe);
} catch (...) {
}
}
int oaknode_keyframe_get_time(const OakNodeKeyframe *keyframe,
int oaknode_keyframe_get_time(OakNodeKeyframe keyframe,
int64_t *out_num, int64_t *out_den)
{
if (!keyframe || !out_num || !out_den) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !out_num || !out_den) {
return OAKNODE_E_INVALID;
}
try {
const olive::core::Rational &time = to_key(keyframe)->time();
const olive::core::Rational &time = key->time();
*out_num = time.numerator();
*out_den = time.denominator();
return OAKNODE_OK;
@@ -195,36 +174,37 @@ int oaknode_keyframe_get_time(const OakNodeKeyframe *keyframe,
}
}
int oaknode_keyframe_set_time(OakNodeKeyframe *keyframe, int64_t time_num,
int oaknode_keyframe_set_time(OakNodeKeyframe keyframe, int64_t time_num,
int64_t time_den)
{
if (!keyframe) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key) {
return OAKNODE_E_INVALID;
}
try {
to_key(keyframe)->set_time(olive::core::Rational(
static_cast<int>(time_num), static_cast<int>(time_den)));
key->set_time(olive::core::Rational(static_cast<int>(time_num),
static_cast<int>(time_den)));
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_keyframe_set_time_undoable(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_time_undoable(OakNodeKeyframe keyframe,
int64_t time_num, int64_t time_den,
OakUndoCommand *out_command)
{
if (!keyframe || !out_command) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !out_command) {
return OAKNODE_E_INVALID;
}
try {
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeParamSetKeyframeTimeCommand(
to_key(keyframe),
olive::core::Rational(static_cast<int>(time_num),
static_cast<int>(time_den))));
key, olive::core::Rational(static_cast<int>(time_num),
static_cast<int>(time_den))));
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
@@ -235,15 +215,15 @@ int oaknode_keyframe_set_time_undoable(OakNodeKeyframe *keyframe,
}
}
int oaknode_keyframe_get_value(const OakNodeKeyframe *keyframe,
int oaknode_keyframe_get_value(OakNodeKeyframe keyframe,
oaknode_value *out)
{
if (!keyframe || !out) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !out) {
return OAKNODE_E_INVALID;
}
try {
const olive::NodeKeyframe *key = to_key(keyframe);
const olive::Variant &variant = key->value();
// Preferred path: the parent node's declared input type pins the
@@ -295,10 +275,11 @@ int oaknode_keyframe_get_value(const OakNodeKeyframe *keyframe,
}
}
int oaknode_keyframe_set_value(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_value(OakNodeKeyframe keyframe,
const oaknode_value *v)
{
if (!keyframe || !v) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !v) {
return OAKNODE_E_INVALID;
}
@@ -307,18 +288,19 @@ int oaknode_keyframe_set_value(OakNodeKeyframe *keyframe,
if (!oaknode_c_api::variant_from_value(v, &variant)) {
return OAKNODE_E_INVALID;
}
to_key(keyframe)->set_value(variant);
key->set_value(variant);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_value_undoable(OakNodeKeyframe keyframe,
const oaknode_value *v,
OakUndoCommand *out_command)
{
if (!keyframe || !v || !out_command) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !v || !out_command) {
return OAKNODE_E_INVALID;
}
@@ -329,8 +311,7 @@ int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe,
}
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeParamSetKeyframeValueCommand(to_key(keyframe),
variant));
new olive::NodeParamSetKeyframeValueCommand(key, variant));
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
@@ -341,48 +322,51 @@ int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe,
}
}
int oaknode_keyframe_get_value_string(const OakNodeKeyframe *keyframe,
int oaknode_keyframe_get_value_string(OakNodeKeyframe keyframe,
char *buf, int buf_size)
{
if (!keyframe) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key) {
return OAKNODE_E_INVALID;
}
try {
return oaknode_c_api::copy_string(to_key(keyframe)->value().to_string(),
buf, buf_size);
return oaknode_c_api::copy_string(key->value().to_string(), buf,
buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_keyframe_set_value_string(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_value_string(OakNodeKeyframe keyframe,
const char *value)
{
if (!keyframe || !value) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !value) {
return OAKNODE_E_INVALID;
}
try {
to_key(keyframe)->set_value(olive::Variant(value));
key->set_value(olive::Variant(value));
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe keyframe,
const char *value,
OakUndoCommand *out_command)
{
if (!keyframe || !value || !out_command) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !value || !out_command) {
return OAKNODE_E_INVALID;
}
try {
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeParamSetKeyframeValueCommand(
to_key(keyframe), olive::Variant(value)));
new olive::NodeParamSetKeyframeValueCommand(key,
olive::Variant(value)));
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
@@ -393,23 +377,25 @@ int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe *keyframe,
}
}
int oaknode_keyframe_get_type(const OakNodeKeyframe *keyframe, int *out_type)
int oaknode_keyframe_get_type(OakNodeKeyframe keyframe, int *out_type)
{
if (!keyframe || !out_type) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !out_type) {
return OAKNODE_E_INVALID;
}
try {
*out_type = static_cast<int>(to_key(keyframe)->type());
*out_type = static_cast<int>(key->type());
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_keyframe_set_type(OakNodeKeyframe *keyframe, int type)
int oaknode_keyframe_set_type(OakNodeKeyframe keyframe, int type)
{
if (!keyframe) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key) {
return OAKNODE_E_INVALID;
}
@@ -418,17 +404,18 @@ int oaknode_keyframe_set_type(OakNodeKeyframe *keyframe, int type)
if (!keyframe_type_from_oak(type, &keyframe_type)) {
return OAKNODE_E_INVALID;
}
to_key(keyframe)->set_type(keyframe_type);
key->set_type(keyframe_type);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_keyframe_set_type_undoable(OakNodeKeyframe *keyframe, int type,
int oaknode_keyframe_set_type_undoable(OakNodeKeyframe keyframe, int type,
OakUndoCommand *out_command)
{
if (!keyframe || !out_command) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !out_command) {
return OAKNODE_E_INVALID;
}
@@ -439,7 +426,7 @@ int oaknode_keyframe_set_type_undoable(OakNodeKeyframe *keyframe, int type,
}
OakUndoCommand handle = oaknode_c_api::wrap_command(
new KeyframeSetTypeCommand(to_key(keyframe), keyframe_type));
new KeyframeSetTypeCommand(key, keyframe_type));
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
@@ -450,20 +437,21 @@ int oaknode_keyframe_set_type_undoable(OakNodeKeyframe *keyframe, int type,
}
}
int oaknode_keyframe_get_bezier_control(const OakNodeKeyframe *keyframe,
int oaknode_keyframe_get_bezier_control(OakNodeKeyframe keyframe,
int handle, double *out_x,
double *out_y)
{
if (!keyframe || !out_x || !out_y) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !out_x || !out_y) {
return OAKNODE_E_INVALID;
}
try {
olive::PointF point;
if (handle == OAKNODE_KEYFRAME_IN_HANDLE) {
point = to_key(keyframe)->bezier_control_in();
point = key->bezier_control_in();
} else if (handle == OAKNODE_KEYFRAME_OUT_HANDLE) {
point = to_key(keyframe)->bezier_control_out();
point = key->bezier_control_out();
} else {
return OAKNODE_E_INVALID;
}
@@ -475,18 +463,19 @@ int oaknode_keyframe_get_bezier_control(const OakNodeKeyframe *keyframe,
}
}
int oaknode_keyframe_set_bezier_control(OakNodeKeyframe *keyframe, int handle,
int oaknode_keyframe_set_bezier_control(OakNodeKeyframe keyframe, int handle,
double x, double y)
{
if (!keyframe) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key) {
return OAKNODE_E_INVALID;
}
try {
if (handle == OAKNODE_KEYFRAME_IN_HANDLE) {
to_key(keyframe)->set_bezier_control_in(olive::PointF(x, y));
key->set_bezier_control_in(olive::PointF(x, y));
} else if (handle == OAKNODE_KEYFRAME_OUT_HANDLE) {
to_key(keyframe)->set_bezier_control_out(olive::PointF(x, y));
key->set_bezier_control_out(olive::PointF(x, y));
} else {
return OAKNODE_E_INVALID;
}
@@ -496,11 +485,12 @@ int oaknode_keyframe_set_bezier_control(OakNodeKeyframe *keyframe, int handle,
}
}
int oaknode_keyframe_set_bezier_control_undoable(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_bezier_control_undoable(OakNodeKeyframe keyframe,
int handle, double x, double y,
OakUndoCommand *out_command)
{
if (!keyframe || !out_command) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !out_command) {
return OAKNODE_E_INVALID;
}
@@ -514,73 +504,76 @@ int oaknode_keyframe_set_bezier_control_undoable(OakNodeKeyframe *keyframe,
return OAKNODE_E_INVALID;
}
OakUndoCommand handle_ptr = oaknode_c_api::wrap_command(
new KeyframeSetBezierControlCommand(to_key(keyframe), bezier_handle,
OakUndoCommand command = oaknode_c_api::wrap_command(
new KeyframeSetBezierControlCommand(key, bezier_handle,
olive::PointF(x, y)));
if (!handle_ptr.ctx) {
if (!command.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle_ptr;
*out_command = command;
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_keyframe_get_track(const OakNodeKeyframe *keyframe,
int oaknode_keyframe_get_track(OakNodeKeyframe keyframe,
int *out_track)
{
if (!keyframe || !out_track) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !out_track) {
return OAKNODE_E_INVALID;
}
try {
*out_track = to_key(keyframe)->track();
*out_track = key->track();
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_keyframe_get_element(const OakNodeKeyframe *keyframe,
int oaknode_keyframe_get_element(OakNodeKeyframe keyframe,
int *out_element)
{
if (!keyframe || !out_element) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !out_element) {
return OAKNODE_E_INVALID;
}
try {
*out_element = to_key(keyframe)->element();
*out_element = key->element();
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_keyframe_get_input(const OakNodeKeyframe *keyframe, char *buf,
int oaknode_keyframe_get_input(OakNodeKeyframe keyframe, char *buf,
int buf_size)
{
if (!keyframe) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key) {
return OAKNODE_E_INVALID;
}
try {
return oaknode_c_api::copy_string(to_key(keyframe)->input(), buf,
buf_size);
return oaknode_c_api::copy_string(key->input(), buf, buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_keyframe_get_parent(const OakNodeKeyframe *keyframe,
OakNodeNode **out_node)
int oaknode_keyframe_get_parent(OakNodeKeyframe keyframe,
OakNodeNode *out_node)
{
if (!keyframe || !out_node) {
olive::NodeKeyframe *key = to_native<olive::NodeKeyframe>(keyframe);
if (!key || !out_node) {
return OAKNODE_E_INVALID;
}
try {
*out_node = from_node(to_key(keyframe)->parent());
*out_node = make_handle<OakNodeNode>(key->parent(), false, nullptr);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
+375 -300
View File
File diff suppressed because it is too large Load Diff
+180
View File
@@ -0,0 +1,180 @@
/***
Oak Video Editor - Non-Linear Video Editor
Copyright (C) 2026 Oak Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
the GNU General Public License. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_EDITOR_NODE_NODEHANDLE_H
#define OAK_EDITOR_NODE_NODEHANDLE_H
#include <atomic>
#include <new>
#include "node/error.h"
#include "alivecount.h"
/**
* @brief Internal control block behind every OakNode* value handle,
* shared between the c_api translation units.
*
* All oaknode public handle structs have the identical layout
* (ctx/addref/release/abi_version), so a single generic box and
* addref/release pair serves every family; `deleter` knows the
* concrete C++ type to destroy.
*
* `owns` is true for objects created through init/create/factory
* functions (releasing the last reference destroys the object through
* `deleter`) and false for references into library-owned graphs
* (children, tracks in a track list, nodes in a project): releasing
* those only destroys the box. Functions that insert an owned object
* into a graph (oaknode_project_add_node(), oaknode_tracklist_add_track(),
* the track block operations, ...) flip `owns` to false through
* mark_container_owned() once the graph assumes the lifetime.
*
* Live-object accounting: make_handle() counts every owned handle it
* creates (alive_inc) and handle_release() un-counts an owned object
* right after destroying it (alive_dec), keeping
* oaknode_debug_alive_count() meaningful for leak checking.
*/
struct OakNodeBox {
void *object;
bool owns;
std::atomic<uint32_t> refs;
void (*deleter)(void *object);
OakNodeBox(void *o, bool own, void (*del)(void *))
: object(o)
, owns(own)
, refs(1)
, deleter(del)
{
}
};
namespace oaknode_c_api
{
inline void handle_addref(void *ctx)
{
if (ctx) {
static_cast<OakNodeBox *>(ctx)->refs.fetch_add(1);
}
}
inline void handle_release(void *ctx)
{
if (!ctx) {
return;
}
OakNodeBox *box = static_cast<OakNodeBox *>(ctx);
if (box->refs.fetch_sub(1) == 1) {
if (box->owns) {
box->deleter(box->object);
alive_dec();
}
delete box;
}
}
/**
* @brief Wrap `object` in a value handle with reference count 1.
*
* `owns` selects whether the final release destroys the object through
* `deleter`. Returns an empty handle (ctx == nullptr) for a null
* object or on allocation failure (an owned object is destroyed via
* `deleter` in the latter case).
*/
template <typename Handle>
inline Handle make_handle(void *object, bool owns, void (*deleter)(void *))
{
Handle handle = {};
if (!object) {
return handle;
}
OakNodeBox *box = new (std::nothrow) OakNodeBox(object, owns, deleter);
if (!box) {
if (owns && deleter) {
deleter(object);
}
return handle;
}
if (owns) {
alive_inc();
}
handle.ctx = box;
handle.addref = handle_addref;
handle.release = handle_release;
handle.abi_version = OAKNODE_ABI_VERSION;
return handle;
}
/**
* @brief Unwrap a value handle to the native C++ pointer (nullptr for
* an empty handle).
*/
template <typename T, typename Handle>
inline T *to_native(Handle h)
{
if (!h.ctx) {
return nullptr;
}
return static_cast<T *>(static_cast<OakNodeBox *>(h.ctx)->object);
}
/**
* @brief Mark a handle's object as owned by a container (graph), so
* releasing the handle no longer destroys the object.
*/
template <typename Handle>
inline void mark_container_owned(Handle h)
{
if (h.ctx) {
static_cast<OakNodeBox *>(h.ctx)->owns = false;
}
}
/**
* @brief Deleter callback stamping the concrete C++ type.
*/
template <typename T>
inline void delete_as(void *object)
{
delete static_cast<T *>(object);
}
/**
* @brief Generic free() implementation for every oaknode family:
* release the caller's reference and null out the handle. NULL and
* ctx == NULL are no-ops.
*/
template <typename Handle>
inline void free_handle(Handle *h)
{
if (!h || !h->ctx) {
return;
}
h->release(h->ctx);
h->ctx = nullptr;
}
} // namespace oaknode_c_api
#endif // OAK_EDITOR_NODE_NODEHANDLE_H
+130 -120
View File
@@ -25,31 +25,17 @@
#include <new>
#include <string>
#include "node/node.h"
#include "node/folder.h"
#include "../src/project.h"
#include "../src/project/folder/folder.h"
#include "nodehandle.h"
namespace
{
olive::Project *to_cpp(OakNodeProject *project)
{
return reinterpret_cast<olive::Project *>(project);
}
const olive::Project *to_cpp(const OakNodeProject *project)
{
return reinterpret_cast<const olive::Project *>(project);
}
olive::Node *to_cpp(OakNodeNode *node)
{
return reinterpret_cast<olive::Node *>(node);
}
OakNodeNode *to_c(olive::Node *node)
{
return reinterpret_cast<OakNodeNode *>(node);
}
/**
* @brief Shared two-stage string getter.
*
@@ -74,189 +60,202 @@ int copy_string(const std::string &value, char *buf, int buf_size)
} // namespace
OakNodeProject *oaknode_project_init(void)
using oaknode_c_api::delete_as;
using oaknode_c_api::make_handle;
using oaknode_c_api::to_native;
OakNodeProject oaknode_project_init(void)
{
try {
return reinterpret_cast<OakNodeProject *>(
new (std::nothrow) olive::Project());
return make_handle<OakNodeProject>(new (std::nothrow) olive::Project(),
true,
&delete_as<olive::Project>);
} catch (...) {
return NULL;
return OakNodeProject{};
}
}
void oaknode_project_free(OakNodeProject *project)
{
delete to_cpp(project);
oaknode_c_api::free_handle(project);
}
int oaknode_project_initialize(OakNodeProject *project)
int oaknode_project_initialize(OakNodeProject project)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
if (to_cpp(project)->root()) {
olive::Project *p = to_native<olive::Project>(project);
if (p->root()) {
return OAKNODE_E_STATE;
}
to_cpp(project)->initialize();
p->initialize();
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_clear(OakNodeProject *project)
int oaknode_project_clear(OakNodeProject project)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
to_cpp(project)->clear();
to_native<olive::Project>(project)->clear();
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
OakNodeFolder *oaknode_project_root(OakNodeProject *project)
OakNodeFolder oaknode_project_root(OakNodeProject project)
{
if (!project) {
return NULL;
if (!project.ctx) {
return OakNodeFolder{};
}
try {
return reinterpret_cast<OakNodeFolder *>(to_cpp(project)->root());
return make_handle<OakNodeFolder>(
to_native<olive::Project>(project)->root(), false,
&delete_as<olive::Folder>);
} catch (...) {
return NULL;
return OakNodeFolder{};
}
}
int oaknode_project_name(const OakNodeProject *project, char *buf, int buf_size)
int oaknode_project_name(OakNodeProject project, char *buf, int buf_size)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
return copy_string(to_cpp(project)->name(), buf, buf_size);
return copy_string(to_native<olive::Project>(project)->name(), buf,
buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_filename(const OakNodeProject *project, char *buf,
int oaknode_project_filename(OakNodeProject project, char *buf,
int buf_size)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
return copy_string(to_cpp(project)->filename(), buf, buf_size);
return copy_string(to_native<olive::Project>(project)->filename(), buf,
buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_pretty_filename(const OakNodeProject *project, char *buf,
int oaknode_project_pretty_filename(OakNodeProject project, char *buf,
int buf_size)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
return copy_string(to_cpp(project)->pretty_filename(), buf, buf_size);
return copy_string(to_native<olive::Project>(project)->pretty_filename(),
buf, buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_set_filename(OakNodeProject *project, const char *filename)
int oaknode_project_set_filename(OakNodeProject project, const char *filename)
{
if (!project || !filename) {
if (!project.ctx || !filename) {
return OAKNODE_E_INVALID;
}
try {
to_cpp(project)->set_filename(filename);
to_native<olive::Project>(project)->set_filename(filename);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_is_modified(const OakNodeProject *project)
int oaknode_project_is_modified(OakNodeProject project)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
return to_cpp(project)->is_modified() ? 1 : 0;
return to_native<olive::Project>(project)->is_modified() ? 1 : 0;
}
int oaknode_project_set_modified(OakNodeProject *project, int modified)
int oaknode_project_set_modified(OakNodeProject project, int modified)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
to_cpp(project)->set_modified(modified != 0);
to_native<olive::Project>(project)->set_modified(modified != 0);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_is_new(const OakNodeProject *project)
int oaknode_project_is_new(OakNodeProject project)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
return to_cpp(project)->is_new() ? 1 : 0;
return to_native<olive::Project>(project)->is_new() ? 1 : 0;
}
int oaknode_project_cache_path(const OakNodeProject *project, char *buf,
int oaknode_project_cache_path(OakNodeProject project, char *buf,
int buf_size)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
return copy_string(to_cpp(project)->cache_path(), buf, buf_size);
return copy_string(to_native<olive::Project>(project)->cache_path(),
buf, buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_get_cache_location_setting(const OakNodeProject *project)
int oaknode_project_get_cache_location_setting(OakNodeProject project)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
return static_cast<int>(to_cpp(project)->get_cache_location_setting());
return static_cast<int>(
to_native<olive::Project>(project)->get_cache_location_setting());
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_set_cache_location_setting(OakNodeProject *project,
int oaknode_project_set_cache_location_setting(OakNodeProject project,
int setting)
{
if (!project || setting < 0 ||
if (!project.ctx || setting < 0 ||
setting > static_cast<int>(olive::Project::k_cache_custom_path)) {
return OAKNODE_E_INVALID;
}
try {
to_cpp(project)->set_cache_location_setting(
to_native<olive::Project>(project)->set_cache_location_setting(
static_cast<olive::Project::CacheSetting>(setting));
return OAKNODE_OK;
} catch (...) {
@@ -264,77 +263,85 @@ int oaknode_project_set_cache_location_setting(OakNodeProject *project,
}
}
int oaknode_project_get_custom_cache_path(const OakNodeProject *project,
int oaknode_project_get_custom_cache_path(OakNodeProject project,
char *buf, int buf_size)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
return copy_string(to_cpp(project)->get_custom_cache_path(), buf,
return copy_string(
to_native<olive::Project>(project)->get_custom_cache_path(), buf,
buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_set_custom_cache_path(OakNodeProject project,
const char *path)
{
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
to_native<olive::Project>(project)->set_custom_cache_path(
path ? path : "");
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_get_uuid(OakNodeProject project, char *buf,
int buf_size)
{
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
return copy_string(to_native<olive::Project>(project)->get_uuid(), buf,
buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_set_custom_cache_path(OakNodeProject *project,
const char *path)
int oaknode_project_add_node(OakNodeProject project, OakNodeNode node)
{
if (!project) {
if (!project.ctx || !node.ctx) {
return OAKNODE_E_INVALID;
}
try {
to_cpp(project)->set_custom_cache_path(path ? path : "");
to_native<olive::Project>(project)->add_node(
to_native<olive::Node>(node));
// The graph now owns the node; releasing `node` must not delete it.
oaknode_c_api::mark_container_owned(node);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_get_uuid(const OakNodeProject *project, char *buf,
int buf_size)
int oaknode_project_remove_node(OakNodeProject project, OakNodeNode node)
{
if (!project) {
if (!project.ctx || !node.ctx) {
return OAKNODE_E_INVALID;
}
try {
return copy_string(to_cpp(project)->get_uuid(), buf, buf_size);
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_add_node(OakNodeProject *project, OakNodeNode *node)
{
if (!project || !node) {
return OAKNODE_E_INVALID;
}
try {
to_cpp(project)->add_node(to_cpp(node));
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_project_remove_node(OakNodeProject *project, OakNodeNode *node)
{
if (!project || !node) {
return OAKNODE_E_INVALID;
}
try {
olive::Project *p = to_cpp(project);
olive::Node *n = to_cpp(node);
olive::Project *p = to_native<olive::Project>(project);
olive::Node *n = to_native<olive::Node>(node);
const auto &nodes = p->nodes();
if (std::find(nodes.begin(), nodes.end(), n) == nodes.end()) {
return OAKNODE_E_NOT_FOUND;
}
// Detach without deleting (legacy semantics); the caller's handle
// keeps its current ownership state.
p->remove_node(n);
return OAKNODE_OK;
} catch (...) {
@@ -342,45 +349,48 @@ int oaknode_project_remove_node(OakNodeProject *project, OakNodeNode *node)
}
}
int oaknode_project_node_count(const OakNodeProject *project)
int oaknode_project_node_count(OakNodeProject project)
{
if (!project) {
if (!project.ctx) {
return OAKNODE_E_INVALID;
}
try {
return static_cast<int>(to_cpp(project)->nodes().size());
return static_cast<int>(
to_native<olive::Project>(project)->nodes().size());
} catch (...) {
return OAKNODE_E_FAILED;
}
}
OakNodeNode *oaknode_project_node_at(const OakNodeProject *project, int index)
OakNodeNode oaknode_project_node_at(OakNodeProject project, int index)
{
if (!project || index < 0) {
return NULL;
if (!project.ctx || index < 0) {
return OakNodeNode{};
}
try {
const auto &nodes = to_cpp(project)->nodes();
const auto &nodes = to_native<olive::Project>(project)->nodes();
if (static_cast<size_t>(index) >= nodes.size()) {
return NULL;
return OakNodeNode{};
}
return to_c(nodes[static_cast<size_t>(index)]);
return make_handle<OakNodeNode>(nodes[static_cast<size_t>(index)],
false, &delete_as<olive::Node>);
} catch (...) {
return NULL;
return OakNodeNode{};
}
}
int oaknode_project_copy_settings(OakNodeProject *dst,
const OakNodeProject *src)
int oaknode_project_copy_settings(OakNodeProject dst,
OakNodeProject src)
{
if (!dst || !src) {
if (!dst.ctx || !src.ctx) {
return OAKNODE_E_INVALID;
}
try {
olive::Project::copy_settings(const_cast<olive::Project *>(to_cpp(src)), to_cpp(dst));
olive::Project::copy_settings(to_native<olive::Project>(src),
to_native<olive::Project>(dst));
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
+116 -100
View File
@@ -20,7 +20,7 @@
#include "node/sequence.h"
#include "alivecount.h"
#include "node/node.h"
#include "node/track.h"
#include "globals.h"
@@ -28,14 +28,16 @@
#include "project/sequence/sequence.h"
#include "videoparams.h"
#include "nodehandle.h"
using oaknode_c_api::delete_as;
using oaknode_c_api::free_handle;
using oaknode_c_api::make_handle;
using oaknode_c_api::to_native;
namespace
{
olive::Sequence *impl(OakNodeSequence *h)
{
return reinterpret_cast<olive::Sequence *>(h);
}
int get_rational(const olive::core::Rational &r, int *numerator,
int *denominator)
{
@@ -54,158 +56,165 @@ bool valid_track_type(int type)
} // namespace
OakNodeSequence *oaknode_sequence_create(void)
OakNodeSequence oaknode_sequence_create(void)
{
try {
olive::Sequence *s = new olive::Sequence();
oaknode_c_api::alive_inc();
return reinterpret_cast<OakNodeSequence *>(s);
return make_handle<OakNodeSequence>(new olive::Sequence(), true,
&delete_as<olive::Sequence>);
} catch (...) {
return nullptr;
return OakNodeSequence{};
}
}
void oaknode_sequence_free(OakNodeSequence *sequence)
{
if (!sequence) {
return;
}
olive::Sequence *s = impl(sequence);
// ~Sequence() deletes the owned TrackLists
delete s;
oaknode_c_api::alive_dec();
// The final release runs ~Sequence(), which deletes the owned TrackLists
free_handle(sequence);
}
int oaknode_sequence_get_track_list(OakNodeSequence *sequence, int type,
OakNodeTrackList **out)
int oaknode_sequence_get_track_list(OakNodeSequence sequence, int type,
OakNodeTrackList *out)
{
if (!sequence || !out) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s || !out) {
return OAKNODE_E_INVALID;
}
if (!valid_track_type(type)) {
return OAKNODE_E_NOT_FOUND;
}
*out = reinterpret_cast<OakNodeTrackList *>(
impl(sequence)->track_list(static_cast<olive::Track::Type>(type)));
return OAKNODE_OK;
// Borrowed; the track list stays owned by the sequence
*out = make_handle<OakNodeTrackList>(
s->track_list(static_cast<olive::Track::Type>(type)), false,
&delete_as<olive::TrackList>);
return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM;
}
int oaknode_sequence_get_track_count(OakNodeSequence *sequence, int type,
int oaknode_sequence_get_track_count(OakNodeSequence sequence, int type,
int *count)
{
if (!sequence || !count) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s || !count) {
return OAKNODE_E_INVALID;
}
if (!valid_track_type(type)) {
return OAKNODE_E_NOT_FOUND;
}
*count = impl(sequence)
->track_list(static_cast<olive::Track::Type>(type))
*count = s->track_list(static_cast<olive::Track::Type>(type))
->get_track_count();
return OAKNODE_OK;
}
int oaknode_sequence_get_track_at(OakNodeSequence *sequence, int type,
int index, OakNodeTrack **out)
int oaknode_sequence_get_track_at(OakNodeSequence sequence, int type,
int index, OakNodeTrack *out)
{
if (!sequence || !out || index < 0) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s || !out || index < 0) {
return OAKNODE_E_INVALID;
}
if (!valid_track_type(type)) {
return OAKNODE_E_NOT_FOUND;
}
olive::TrackList *list =
impl(sequence)->track_list(static_cast<olive::Track::Type>(type));
s->track_list(static_cast<olive::Track::Type>(type));
if (index >= list->get_track_count()) {
return OAKNODE_E_NOT_FOUND;
}
*out = reinterpret_cast<OakNodeTrack *>(list->get_track_at(index));
// Borrowed; the track stays owned by the list
*out = make_handle<OakNodeTrack>(list->get_track_at(index), false,
&delete_as<olive::Track>);
return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM;
}
int oaknode_sequence_get_all_track_count(OakNodeSequence sequence, int *count)
{
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s || !count) {
return OAKNODE_E_INVALID;
}
*count = int(s->get_tracks().size());
return OAKNODE_OK;
}
int oaknode_sequence_get_all_track_count(OakNodeSequence *sequence, int *count)
int oaknode_sequence_get_all_track_at(OakNodeSequence sequence, int index,
OakNodeTrack *out)
{
if (!sequence || !count) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s || !out || index < 0) {
return OAKNODE_E_INVALID;
}
*count = int(impl(sequence)->get_tracks().size());
return OAKNODE_OK;
}
int oaknode_sequence_get_all_track_at(OakNodeSequence *sequence, int index,
OakNodeTrack **out)
{
if (!sequence || !out || index < 0) {
return OAKNODE_E_INVALID;
}
const auto &tracks = impl(sequence)->get_tracks();
const auto &tracks = s->get_tracks();
if (index >= int(tracks.size())) {
return OAKNODE_E_NOT_FOUND;
}
*out = reinterpret_cast<OakNodeTrack *>(tracks.at(index));
return OAKNODE_OK;
// Borrowed; the track stays owned by its list
*out = make_handle<OakNodeTrack>(tracks.at(index), false,
&delete_as<olive::Track>);
return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM;
}
int oaknode_sequence_get_playhead(OakNodeSequence *sequence, int *numerator,
int oaknode_sequence_get_playhead(OakNodeSequence sequence, int *numerator,
int *denominator)
{
if (!sequence) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s) {
return OAKNODE_E_INVALID;
}
return get_rational(impl(sequence)->get_playhead(), numerator, denominator);
return get_rational(s->get_playhead(), numerator, denominator);
}
int oaknode_sequence_set_playhead(OakNodeSequence *sequence, int numerator,
int oaknode_sequence_set_playhead(OakNodeSequence sequence, int numerator,
int denominator)
{
if (!sequence) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s) {
return OAKNODE_E_INVALID;
}
try {
impl(sequence)->set_playhead(olive::core::Rational(numerator,
denominator));
s->set_playhead(olive::core::Rational(numerator, denominator));
} catch (...) {
return OAKNODE_E_FAILED;
}
return OAKNODE_OK;
}
int oaknode_sequence_get_length(OakNodeSequence *sequence, int *numerator,
int oaknode_sequence_get_length(OakNodeSequence sequence, int *numerator,
int *denominator)
{
if (!sequence) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s) {
return OAKNODE_E_INVALID;
}
return get_rational(impl(sequence)->get_length(), numerator, denominator);
return get_rational(s->get_length(), numerator, denominator);
}
int oaknode_sequence_get_video_length(OakNodeSequence *sequence, int *numerator,
int *denominator)
int oaknode_sequence_get_video_length(OakNodeSequence sequence,
int *numerator, int *denominator)
{
if (!sequence) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s) {
return OAKNODE_E_INVALID;
}
return get_rational(impl(sequence)->get_video_length(), numerator,
denominator);
return get_rational(s->get_video_length(), numerator, denominator);
}
int oaknode_sequence_get_audio_length(OakNodeSequence *sequence, int *numerator,
int *denominator)
int oaknode_sequence_get_audio_length(OakNodeSequence sequence,
int *numerator, int *denominator)
{
if (!sequence) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s) {
return OAKNODE_E_INVALID;
}
return get_rational(impl(sequence)->get_audio_length(), numerator,
denominator);
return get_rational(s->get_audio_length(), numerator, denominator);
}
int oaknode_sequence_verify_length(OakNodeSequence *sequence)
int oaknode_sequence_verify_length(OakNodeSequence sequence)
{
if (!sequence) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s) {
return OAKNODE_E_INVALID;
}
try {
impl(sequence)->verify_length();
s->verify_length();
} catch (...) {
return OAKNODE_E_FAILED;
}
@@ -214,38 +223,40 @@ int oaknode_sequence_verify_length(OakNodeSequence *sequence)
/* --------------------------------------------------- Video/audio params */
int oaknode_sequence_get_video_stream_count(OakNodeSequence *sequence,
int oaknode_sequence_get_video_stream_count(OakNodeSequence sequence,
int *count)
{
if (!sequence || !count) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s || !count) {
return OAKNODE_E_INVALID;
}
*count = impl(sequence)->get_video_stream_count();
*count = s->get_video_stream_count();
return OAKNODE_OK;
}
int oaknode_sequence_get_audio_stream_count(OakNodeSequence *sequence,
int oaknode_sequence_get_audio_stream_count(OakNodeSequence sequence,
int *count)
{
if (!sequence || !count) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s || !count) {
return OAKNODE_E_INVALID;
}
*count = impl(sequence)->get_audio_stream_count();
*count = s->get_audio_stream_count();
return OAKNODE_OK;
}
int oaknode_sequence_get_video_params(OakNodeSequence *sequence, int index,
int oaknode_sequence_get_video_params(OakNodeSequence sequence, int index,
OakVideoParams *out)
{
if (!sequence || !out || index < 0) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s || !out || index < 0) {
return OAKNODE_E_INVALID;
}
if (index >= impl(sequence)->get_video_stream_count()) {
if (index >= s->get_video_stream_count()) {
return OAKNODE_E_NOT_FOUND;
}
try {
const olive::VideoParams params =
impl(sequence)->get_video_params(index);
const olive::VideoParams params = s->get_video_params(index);
*out = oakcommon_videoparams_init_from_native(&params);
} catch (...) {
return OAKNODE_E_NOMEM;
@@ -256,10 +267,11 @@ int oaknode_sequence_get_video_params(OakNodeSequence *sequence, int index,
return OAKNODE_OK;
}
int oaknode_sequence_set_video_params(OakNodeSequence *sequence, int index,
int oaknode_sequence_set_video_params(OakNodeSequence sequence, int index,
OakVideoParams params)
{
if (!sequence || index < 0) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s || index < 0) {
return OAKNODE_E_INVALID;
}
const olive::VideoParams *native =
@@ -267,28 +279,29 @@ int oaknode_sequence_set_video_params(OakNodeSequence *sequence, int index,
if (!native) {
return OAKNODE_E_INVALID;
}
if (index >= impl(sequence)->get_video_stream_count()) {
if (index >= s->get_video_stream_count()) {
return OAKNODE_E_NOT_FOUND;
}
try {
impl(sequence)->set_video_params(*native, index);
s->set_video_params(*native, index);
} catch (...) {
return OAKNODE_E_FAILED;
}
return OAKNODE_OK;
}
int oaknode_sequence_get_audio_params(OakNodeSequence *sequence, int index,
int oaknode_sequence_get_audio_params(OakNodeSequence sequence, int index,
OakAudioParams **out)
{
if (!sequence || !out || index < 0) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s || !out || index < 0) {
return OAKNODE_E_INVALID;
}
if (index >= impl(sequence)->get_audio_stream_count()) {
if (index >= s->get_audio_stream_count()) {
return OAKNODE_E_NOT_FOUND;
}
OakAudioParams *copy =
oakcore_audioparams_copy(impl(sequence)->get_audio_params(index).handle());
oakcore_audioparams_copy(s->get_audio_params(index).handle());
if (!copy) {
return OAKNODE_E_NOMEM;
}
@@ -296,13 +309,14 @@ int oaknode_sequence_get_audio_params(OakNodeSequence *sequence, int index,
return OAKNODE_OK;
}
int oaknode_sequence_set_audio_params(OakNodeSequence *sequence, int index,
int oaknode_sequence_set_audio_params(OakNodeSequence sequence, int index,
const OakAudioParams *params)
{
if (!sequence || !params || index < 0) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s || !params || index < 0) {
return OAKNODE_E_INVALID;
}
if (index >= impl(sequence)->get_audio_stream_count()) {
if (index >= s->get_audio_stream_count()) {
return OAKNODE_E_NOT_FOUND;
}
OakAudioParams *copy = oakcore_audioparams_copy(params);
@@ -310,8 +324,7 @@ int oaknode_sequence_set_audio_params(OakNodeSequence *sequence, int index,
return OAKNODE_E_NOMEM;
}
try {
impl(sequence)->set_audio_params(
olive::core::AudioParams::from_handle(copy), index);
s->set_audio_params(olive::core::AudioParams::from_handle(copy), index);
} catch (...) {
oakcore_audioparams_free(copy);
return OAKNODE_E_FAILED;
@@ -319,19 +332,22 @@ int oaknode_sequence_set_audio_params(OakNodeSequence *sequence, int index,
return OAKNODE_OK;
}
OakNodeNode *oaknode_sequence_as_node(OakNodeSequence *sequence)
OakNodeNode oaknode_sequence_as_node(OakNodeSequence sequence)
{
return reinterpret_cast<OakNodeNode *>(sequence);
// Borrowed; releasing the result never destroys the sequence
return make_handle<OakNodeNode>(to_native<olive::Sequence>(sequence),
false, &delete_as<olive::Node>);
}
int oaknode_sequence_set_default_parameters(OakNodeSequence *sequence)
int oaknode_sequence_set_default_parameters(OakNodeSequence sequence)
{
if (!sequence) {
olive::Sequence *s = to_native<olive::Sequence>(sequence);
if (!s) {
return OAKNODE_E_INVALID;
}
try {
impl(sequence)->set_default_parameters();
s->set_default_parameters();
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
+100 -92
View File
@@ -30,40 +30,13 @@
#include "../src/project/serializer/serializer.h"
#include "xmlutils.h"
struct OakNodeSerializerSaveData {
olive::ProjectSerializer::SaveData impl;
OakNodeSerializerSaveData(olive::ProjectSerializer::LoadType type,
olive::Project *project)
: impl(type, project)
{
}
};
struct OakNodeSerializerLoadData {
olive::ProjectSerializer::LoadData impl;
};
#include "nodehandle.h"
namespace
{
bool g_initialized = false;
olive::Project *to_cpp(OakNodeProject *project)
{
return reinterpret_cast<olive::Project *>(project);
}
olive::Node *to_cpp(OakNodeNode *node)
{
return reinterpret_cast<olive::Node *>(node);
}
OakNodeNode *to_c(olive::Node *node)
{
return reinterpret_cast<OakNodeNode *>(node);
}
bool is_valid_load_type(int load_type)
{
return load_type >= static_cast<int>(olive::ProjectSerializer::k_project) &&
@@ -126,31 +99,36 @@ void oaknode_serializer_shutdown(void)
g_initialized = false;
}
OakNodeSerializerSaveData *oaknode_serializer_savedata_create(
int load_type, OakNodeProject *project)
OakNodeSerializerSaveData oaknode_serializer_savedata_create(
int load_type, OakNodeProject project)
{
if (!is_valid_load_type(load_type)) {
return NULL;
return OakNodeSerializerSaveData{};
}
try {
return new (std::nothrow) OakNodeSerializerSaveData(
static_cast<olive::ProjectSerializer::LoadType>(load_type),
to_cpp(project));
return oaknode_c_api::make_handle<OakNodeSerializerSaveData>(
new olive::ProjectSerializer::SaveData(
static_cast<olive::ProjectSerializer::LoadType>(load_type),
oaknode_c_api::to_native<olive::Project>(project)),
true,
&oaknode_c_api::delete_as<olive::ProjectSerializer::SaveData>);
} catch (...) {
return NULL;
return OakNodeSerializerSaveData{};
}
}
void oaknode_serializer_savedata_free(OakNodeSerializerSaveData *save_data)
{
delete save_data;
oaknode_c_api::free_handle(save_data);
}
int oaknode_serializer_savedata_set_nodes(
OakNodeSerializerSaveData *save_data, OakNodeNode *const *nodes, int count)
OakNodeSerializerSaveData save_data, const OakNodeNode *nodes, int count)
{
if (!save_data || !nodes || count < 0) {
olive::ProjectSerializer::SaveData *sd =
oaknode_c_api::to_native<olive::ProjectSerializer::SaveData>(save_data);
if (!sd || !nodes || count < 0) {
return OAKNODE_E_INVALID;
}
@@ -158,13 +136,14 @@ int oaknode_serializer_savedata_set_nodes(
std::vector<olive::Node *> cpp_nodes;
cpp_nodes.reserve(static_cast<size_t>(count));
for (int i = 0; i < count; i++) {
if (!nodes[i]) {
olive::Node *node = oaknode_c_api::to_native<olive::Node>(nodes[i]);
if (!node) {
return OAKNODE_E_INVALID;
}
cpp_nodes.push_back(to_cpp(nodes[i]));
cpp_nodes.push_back(node);
}
save_data->impl.set_only_serialize_nodes(cpp_nodes);
sd->set_only_serialize_nodes(cpp_nodes);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
@@ -172,28 +151,33 @@ int oaknode_serializer_savedata_set_nodes(
}
int oaknode_serializer_savedata_set_property(
OakNodeSerializerSaveData *save_data, OakNodeNode *node, const char *key,
OakNodeSerializerSaveData save_data, OakNodeNode node, const char *key,
const char *value)
{
if (!save_data || !node || !key || !value) {
olive::ProjectSerializer::SaveData *sd =
oaknode_c_api::to_native<olive::ProjectSerializer::SaveData>(save_data);
olive::Node *native_node = oaknode_c_api::to_native<olive::Node>(node);
if (!sd || !native_node || !key || !value) {
return OAKNODE_E_INVALID;
}
try {
olive::ProjectSerializer::SerializedProperties properties =
save_data->impl.get_properties();
properties[to_cpp(node)][key] = value;
save_data->impl.set_properties(properties);
sd->get_properties();
properties[native_node][key] = value;
sd->set_properties(properties);
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData *save_data,
int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData save_data,
char *buf, int buf_size)
{
if (!save_data) {
olive::ProjectSerializer::SaveData *sd =
oaknode_c_api::to_native<olive::ProjectSerializer::SaveData>(save_data);
if (!sd) {
return OAKNODE_E_INVALID;
}
if (!g_initialized) {
@@ -203,7 +187,7 @@ int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData *save_data,
try {
olive::XmlStreamWriter writer;
olive::ProjectSerializer::Result result =
olive::ProjectSerializer::save(&writer, save_data->impl);
olive::ProjectSerializer::save(&writer, *sd);
if (result != olive::ProjectSerializer::k_success) {
return OAKNODE_E_FAILED;
}
@@ -213,9 +197,9 @@ int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData *save_data,
}
}
int oaknode_serializer_load_from_xml(OakNodeProject *project, const char *xml,
int oaknode_serializer_load_from_xml(OakNodeProject project, const char *xml,
int load_type, int *out_result,
OakNodeSerializerLoadData **out_load_data,
OakNodeSerializerLoadData *out_load_data,
char *details_buf, int details_buf_size)
{
if (!xml || !out_result || !is_valid_load_type(load_type)) {
@@ -226,13 +210,13 @@ int oaknode_serializer_load_from_xml(OakNodeProject *project, const char *xml,
}
if (out_load_data) {
*out_load_data = NULL;
*out_load_data = OakNodeSerializerLoadData{};
}
try {
olive::XmlStreamReader reader(xml);
olive::ProjectSerializer::Result result = olive::ProjectSerializer::load(
to_cpp(project), &reader,
oaknode_c_api::to_native<olive::Project>(project), &reader,
static_cast<olive::ProjectSerializer::LoadType>(load_type));
*out_result = static_cast<int>(result.code());
@@ -242,12 +226,20 @@ int oaknode_serializer_load_from_xml(OakNodeProject *project, const char *xml,
}
if (result == olive::ProjectSerializer::k_success && out_load_data) {
auto *load_data = new (std::nothrow) OakNodeSerializerLoadData();
auto *load_data =
new (std::nothrow) olive::ProjectSerializer::LoadData();
if (!load_data) {
return OAKNODE_E_NOMEM;
}
load_data->impl = result.get_load_data();
*out_load_data = load_data;
*load_data = result.get_load_data();
*out_load_data =
oaknode_c_api::make_handle<OakNodeSerializerLoadData>(
load_data, true,
&oaknode_c_api::delete_as<
olive::ProjectSerializer::LoadData>);
if (!out_load_data->ctx) {
return OAKNODE_E_NOMEM;
}
}
return OAKNODE_OK;
@@ -258,49 +250,59 @@ int oaknode_serializer_load_from_xml(OakNodeProject *project, const char *xml,
void oaknode_serializer_loaddata_free(OakNodeSerializerLoadData *load_data)
{
delete load_data;
oaknode_c_api::free_handle(load_data);
}
int oaknode_serializer_loaddata_node_count(
const OakNodeSerializerLoadData *load_data)
OakNodeSerializerLoadData load_data)
{
if (!load_data) {
olive::ProjectSerializer::LoadData *ld =
oaknode_c_api::to_native<olive::ProjectSerializer::LoadData>(load_data);
if (!ld) {
return OAKNODE_E_INVALID;
}
try {
return static_cast<int>(load_data->impl.nodes.size());
return static_cast<int>(ld->nodes.size());
} catch (...) {
return OAKNODE_E_FAILED;
}
}
OakNodeNode *oaknode_serializer_loaddata_node_at(
const OakNodeSerializerLoadData *load_data, int index)
OakNodeNode oaknode_serializer_loaddata_node_at(
OakNodeSerializerLoadData load_data, int index)
{
if (!load_data || index < 0 ||
static_cast<size_t>(index) >= load_data->impl.nodes.size()) {
return NULL;
olive::ProjectSerializer::LoadData *ld =
oaknode_c_api::to_native<olive::ProjectSerializer::LoadData>(load_data);
if (!ld || index < 0 ||
static_cast<size_t>(index) >= ld->nodes.size()) {
return OakNodeNode{};
}
try {
return to_c(load_data->impl.nodes[static_cast<size_t>(index)]);
// Borrowed: the node is owned by the caller only in the sense of
// the documented adoption contract; see oaknode_project_add_node().
return oaknode_c_api::make_handle<OakNodeNode>(
ld->nodes[static_cast<size_t>(index)], false, nullptr);
} catch (...) {
return NULL;
return OakNodeNode{};
}
}
int oaknode_serializer_loaddata_get_property(
const OakNodeSerializerLoadData *load_data, OakNodeNode *node,
const char *key, char *buf, int buf_size)
OakNodeSerializerLoadData load_data, OakNodeNode node, const char *key,
char *buf, int buf_size)
{
if (!load_data || !node || !key) {
olive::ProjectSerializer::LoadData *ld =
oaknode_c_api::to_native<olive::ProjectSerializer::LoadData>(load_data);
olive::Node *native_node = oaknode_c_api::to_native<olive::Node>(node);
if (!ld || !native_node || !key) {
return OAKNODE_E_INVALID;
}
try {
auto node_it = load_data->impl.properties.find(to_cpp(node));
if (node_it == load_data->impl.properties.end()) {
auto node_it = ld->properties.find(native_node);
if (node_it == ld->properties.end()) {
return OAKNODE_E_NOT_FOUND;
}
auto key_it = node_it->second.find(key);
@@ -314,37 +316,43 @@ int oaknode_serializer_loaddata_get_property(
}
int oaknode_serializer_loaddata_connection_count(
const OakNodeSerializerLoadData *load_data)
OakNodeSerializerLoadData load_data)
{
if (!load_data) {
olive::ProjectSerializer::LoadData *ld =
oaknode_c_api::to_native<olive::ProjectSerializer::LoadData>(load_data);
if (!ld) {
return OAKNODE_E_INVALID;
}
try {
return static_cast<int>(load_data->impl.promised_connections.size());
return static_cast<int>(ld->promised_connections.size());
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_serializer_loaddata_connection_at(
const OakNodeSerializerLoadData *load_data, int index,
OakNodeNode **out_output_node, OakNodeNode **out_input_node,
OakNodeSerializerLoadData load_data, int index,
OakNodeNode *out_output_node, OakNodeNode *out_input_node,
char *input_id_buf, int input_id_buf_size, int *out_element)
{
if (!load_data || !out_output_node || !out_input_node || !out_element) {
olive::ProjectSerializer::LoadData *ld =
oaknode_c_api::to_native<olive::ProjectSerializer::LoadData>(load_data);
if (!ld || !out_output_node || !out_input_node || !out_element) {
return OAKNODE_E_INVALID;
}
if (index < 0 || static_cast<size_t>(index) >=
load_data->impl.promised_connections.size()) {
if (index < 0 ||
static_cast<size_t>(index) >= ld->promised_connections.size()) {
return OAKNODE_E_NOT_FOUND;
}
try {
const olive::Node::OutputConnection &connection =
load_data->impl.promised_connections[static_cast<size_t>(index)];
*out_output_node = to_c(connection.first);
*out_input_node = to_c(connection.second.node());
ld->promised_connections[static_cast<size_t>(index)];
*out_output_node = oaknode_c_api::make_handle<OakNodeNode>(
connection.first, false, nullptr);
*out_input_node = oaknode_c_api::make_handle<OakNodeNode>(
connection.second.node(), false, nullptr);
if (input_id_buf && input_id_buf_size > 0) {
copy_string(connection.second.input(), input_id_buf,
input_id_buf_size);
@@ -383,11 +391,12 @@ int report_serializer_result(const olive::ProjectSerializer::Result &result,
} // namespace
int oaknode_serializer_save_to_file(OakNodeProject *project,
int oaknode_serializer_save_to_file(OakNodeProject project,
const char *filename, int use_compression, int *out_code,
char *details, int details_size)
{
if (!project || !filename) {
olive::Project *native = oaknode_c_api::to_native<olive::Project>(project);
if (!native || !filename) {
return OAKNODE_E_INVALID;
}
@@ -395,8 +404,7 @@ int oaknode_serializer_save_to_file(OakNodeProject *project,
oaknode_serializer_initialize();
olive::ProjectSerializer::SaveData data(
olive::ProjectSerializer::k_project,
reinterpret_cast<olive::Project *>(project), filename);
olive::ProjectSerializer::k_project, native, filename);
olive::ProjectSerializer::Result result =
olive::ProjectSerializer::save(data, use_compression != 0);
@@ -408,11 +416,12 @@ int oaknode_serializer_save_to_file(OakNodeProject *project,
}
}
int oaknode_serializer_load_from_file(OakNodeProject *project,
int oaknode_serializer_load_from_file(OakNodeProject project,
const char *filename, int *out_code, char *details,
int details_size)
{
if (!project || !filename) {
olive::Project *native = oaknode_c_api::to_native<olive::Project>(project);
if (!native || !filename) {
return OAKNODE_E_INVALID;
}
@@ -420,9 +429,8 @@ int oaknode_serializer_load_from_file(OakNodeProject *project,
oaknode_serializer_initialize();
olive::ProjectSerializer::Result result =
olive::ProjectSerializer::load(
reinterpret_cast<olive::Project *>(project), filename,
olive::ProjectSerializer::k_project);
olive::ProjectSerializer::load(native, filename,
olive::ProjectSerializer::k_project);
return report_serializer_result(result, out_code, details,
details_size);
+252 -200
View File
@@ -22,7 +22,9 @@
#include <algorithm>
#include "alivecount.h"
#include "node/block.h"
#include "node/node.h"
#include "node/sequence.h"
#include "valueconvert.h"
@@ -31,34 +33,17 @@
#include "output/track/tracklist.h"
#include "project/sequence/sequence.h"
#include "nodehandle.h"
using oaknode_c_api::delete_as;
using oaknode_c_api::free_handle;
using oaknode_c_api::make_handle;
using oaknode_c_api::mark_container_owned;
using oaknode_c_api::to_native;
namespace
{
olive::Track *impl(OakNodeTrack *h)
{
return reinterpret_cast<olive::Track *>(h);
}
olive::TrackList *list_impl(OakNodeTrackList *h)
{
return reinterpret_cast<olive::TrackList *>(h);
}
olive::Block *block_impl(OakNodeBlock *h)
{
return reinterpret_cast<olive::Block *>(h);
}
OakNodeTrack *wrap(olive::Track *t)
{
return reinterpret_cast<OakNodeTrack *>(t);
}
OakNodeBlock *wrap_block(olive::Block *b)
{
return reinterpret_cast<OakNodeBlock *>(b);
}
bool valid_type(int type)
{
return type >= OAKNODE_TRACK_TYPE_VIDEO && type < OAKNODE_TRACK_TYPE_COUNT;
@@ -98,81 +83,82 @@ int get_rational(const olive::core::Rational &r, int *numerator,
/* ---------------------------------------------------------------- Track */
OakNodeTrack *oaknode_track_create(int type)
OakNodeTrack oaknode_track_create(int type)
{
if (!valid_type(type)) {
return nullptr;
return OakNodeTrack{};
}
try {
olive::Track *t = new olive::Track();
t->set_type(static_cast<olive::Track::Type>(type));
oaknode_c_api::alive_inc();
return wrap(t);
return make_handle<OakNodeTrack>(t, true, &delete_as<olive::Track>);
} catch (...) {
return nullptr;
return OakNodeTrack{};
}
}
void oaknode_track_free(OakNodeTrack *track)
{
if (!track) {
return;
}
delete impl(track);
oaknode_c_api::alive_dec();
free_handle(track);
}
int oaknode_track_get_type(OakNodeTrack *track, int *type)
int oaknode_track_get_type(OakNodeTrack track, int *type)
{
if (!track || !type) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !type) {
return OAKNODE_E_INVALID;
}
*type = int(impl(track)->type());
*type = int(t->type());
return OAKNODE_OK;
}
int oaknode_track_set_type(OakNodeTrack *track, int type)
int oaknode_track_set_type(OakNodeTrack track, int type)
{
if (!track || !valid_type(type)) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !valid_type(type)) {
return OAKNODE_E_INVALID;
}
impl(track)->set_type(static_cast<olive::Track::Type>(type));
t->set_type(static_cast<olive::Track::Type>(type));
return OAKNODE_OK;
}
int oaknode_track_get_height(OakNodeTrack *track, double *height)
int oaknode_track_get_height(OakNodeTrack track, double *height)
{
if (!track || !height) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !height) {
return OAKNODE_E_INVALID;
}
*height = impl(track)->get_track_height();
*height = t->get_track_height();
return OAKNODE_OK;
}
int oaknode_track_set_height(OakNodeTrack *track, double height)
int oaknode_track_set_height(OakNodeTrack track, double height)
{
if (!track) {
olive::Track *t = to_native<olive::Track>(track);
if (!t) {
return OAKNODE_E_INVALID;
}
impl(track)->set_track_height(height);
t->set_track_height(height);
return OAKNODE_OK;
}
int oaknode_track_get_height_in_pixels(OakNodeTrack *track, int *height)
int oaknode_track_get_height_in_pixels(OakNodeTrack track, int *height)
{
if (!track || !height) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !height) {
return OAKNODE_E_INVALID;
}
*height = impl(track)->get_track_height_in_pixels();
*height = t->get_track_height_in_pixels();
return OAKNODE_OK;
}
int oaknode_track_set_height_in_pixels(OakNodeTrack *track, int height)
int oaknode_track_set_height_in_pixels(OakNodeTrack track, int height)
{
if (!track) {
olive::Track *t = to_native<olive::Track>(track);
if (!t) {
return OAKNODE_E_INVALID;
}
impl(track)->set_track_height_in_pixels(height);
t->set_track_height_in_pixels(height);
return OAKNODE_OK;
}
@@ -186,223 +172,267 @@ int oaknode_track_get_minimum_height_in_pixels(void)
return olive::Track::get_minimum_track_height_in_pixels();
}
int oaknode_track_get_index(OakNodeTrack *track, int *index)
int oaknode_track_get_index(OakNodeTrack track, int *index)
{
if (!track || !index) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !index) {
return OAKNODE_E_INVALID;
}
*index = impl(track)->index();
*index = t->index();
return OAKNODE_OK;
}
int oaknode_track_set_index(OakNodeTrack *track, int index)
int oaknode_track_set_index(OakNodeTrack track, int index)
{
if (!track) {
olive::Track *t = to_native<olive::Track>(track);
if (!t) {
return OAKNODE_E_INVALID;
}
impl(track)->set_index(index);
t->set_index(index);
return OAKNODE_OK;
}
int oaknode_track_get_muted(OakNodeTrack *track, int *muted)
int oaknode_track_get_muted(OakNodeTrack track, int *muted)
{
if (!track || !muted) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !muted) {
return OAKNODE_E_INVALID;
}
*muted = impl(track)->is_muted() ? 1 : 0;
*muted = t->is_muted() ? 1 : 0;
return OAKNODE_OK;
}
int oaknode_track_set_muted(OakNodeTrack *track, int muted)
int oaknode_track_set_muted(OakNodeTrack track, int muted)
{
if (!track) {
olive::Track *t = to_native<olive::Track>(track);
if (!t) {
return OAKNODE_E_INVALID;
}
impl(track)->set_muted(muted != 0);
t->set_muted(muted != 0);
return OAKNODE_OK;
}
int oaknode_track_get_locked(OakNodeTrack *track, int *locked)
int oaknode_track_get_locked(OakNodeTrack track, int *locked)
{
if (!track || !locked) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !locked) {
return OAKNODE_E_INVALID;
}
*locked = impl(track)->is_locked() ? 1 : 0;
*locked = t->is_locked() ? 1 : 0;
return OAKNODE_OK;
}
int oaknode_track_set_locked(OakNodeTrack *track, int locked)
int oaknode_track_set_locked(OakNodeTrack track, int locked)
{
if (!track) {
olive::Track *t = to_native<olive::Track>(track);
if (!t) {
return OAKNODE_E_INVALID;
}
impl(track)->set_locked(locked != 0);
t->set_locked(locked != 0);
return OAKNODE_OK;
}
int oaknode_track_get_reference(OakNodeTrack *track, int *type, int *index)
int oaknode_track_get_reference(OakNodeTrack track, int *type, int *index)
{
if (!track || !type || !index) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !type || !index) {
return OAKNODE_E_INVALID;
}
olive::Track::Reference ref = impl(track)->to_reference();
olive::Track::Reference ref = t->to_reference();
*type = int(ref.type());
*index = ref.index();
return OAKNODE_OK;
}
int oaknode_track_get_length(OakNodeTrack *track, int *numerator,
int oaknode_track_get_length(OakNodeTrack track, int *numerator,
int *denominator)
{
if (!track) {
olive::Track *t = to_native<olive::Track>(track);
if (!t) {
return OAKNODE_E_INVALID;
}
return get_rational(impl(track)->track_length(), numerator, denominator);
return get_rational(t->track_length(), numerator, denominator);
}
int oaknode_track_get_sequence(OakNodeTrack *track, OakNodeSequence **out)
int oaknode_track_get_sequence(OakNodeTrack track, OakNodeSequence *out)
{
if (!track || !out) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !out) {
return OAKNODE_E_INVALID;
}
*out = reinterpret_cast<OakNodeSequence *>(impl(track)->sequence());
// Borrowed (empty when the track is not in a sequence)
*out = make_handle<OakNodeSequence>(t->sequence(), false,
&delete_as<olive::Sequence>);
return OAKNODE_OK;
}
/* ------------------------------------------------------- Track blocks */
int oaknode_track_get_block_count(OakNodeTrack *track, int *count)
int oaknode_track_get_block_count(OakNodeTrack track, int *count)
{
if (!track || !count) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !count) {
return OAKNODE_E_INVALID;
}
*count = int(impl(track)->blocks().size());
*count = int(t->blocks().size());
return OAKNODE_OK;
}
int oaknode_track_get_block_at(OakNodeTrack *track, int index,
OakNodeBlock **out)
int oaknode_track_get_block_at(OakNodeTrack track, int index,
OakNodeBlock *out)
{
if (!track || !out || index < 0) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !out || index < 0) {
return OAKNODE_E_INVALID;
}
const auto &blocks = impl(track)->blocks();
const auto &blocks = t->blocks();
if (index >= int(blocks.size())) {
return OAKNODE_E_NOT_FOUND;
}
*out = wrap_block(blocks.at(index));
return OAKNODE_OK;
// Borrowed; the block stays owned by the track
*out = make_handle<OakNodeBlock>(blocks.at(index), false,
&delete_as<olive::Block>);
return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM;
}
int oaknode_track_append_block(OakNodeTrack *track, OakNodeBlock *block)
int oaknode_track_append_block(OakNodeTrack track, OakNodeBlock block)
{
if (!track || !block) {
olive::Track *t = to_native<olive::Track>(track);
olive::Block *b = to_native<olive::Block>(block);
if (!t || !b) {
return OAKNODE_E_INVALID;
}
try {
impl(track)->append_block(block_impl(block));
t->append_block(b);
} catch (...) {
return OAKNODE_E_FAILED;
}
refresh_lengths(impl(track));
// The track now owns the block; the caller's handle becomes a
// non-owning reference (its release no longer deletes)
mark_container_owned(block);
refresh_lengths(t);
return OAKNODE_OK;
}
int oaknode_track_prepend_block(OakNodeTrack *track, OakNodeBlock *block)
int oaknode_track_prepend_block(OakNodeTrack track, OakNodeBlock block)
{
if (!track || !block) {
olive::Track *t = to_native<olive::Track>(track);
olive::Block *b = to_native<olive::Block>(block);
if (!t || !b) {
return OAKNODE_E_INVALID;
}
try {
impl(track)->prepend_block(block_impl(block));
t->prepend_block(b);
} catch (...) {
return OAKNODE_E_FAILED;
}
refresh_lengths(impl(track));
mark_container_owned(block);
refresh_lengths(t);
return OAKNODE_OK;
}
int oaknode_track_insert_block_at_index(OakNodeTrack *track,
OakNodeBlock *block, int index)
int oaknode_track_insert_block_at_index(OakNodeTrack track,
OakNodeBlock block, int index)
{
if (!track || !block) {
olive::Track *t = to_native<olive::Track>(track);
olive::Block *b = to_native<olive::Block>(block);
if (!t || !b) {
return OAKNODE_E_INVALID;
}
try {
impl(track)->insert_block_at_index(block_impl(block), index);
t->insert_block_at_index(b, index);
} catch (...) {
return OAKNODE_E_FAILED;
}
refresh_lengths(impl(track));
mark_container_owned(block);
refresh_lengths(t);
return OAKNODE_OK;
}
int oaknode_track_insert_block_after(OakNodeTrack *track, OakNodeBlock *block,
OakNodeBlock *before)
int oaknode_track_insert_block_after(OakNodeTrack track, OakNodeBlock block,
OakNodeBlock before)
{
if (!track || !block || !before) {
olive::Track *t = to_native<olive::Track>(track);
olive::Block *b = to_native<olive::Block>(block);
olive::Block *bf = to_native<olive::Block>(before);
if (!t || !b || !bf) {
return OAKNODE_E_INVALID;
}
try {
impl(track)->insert_block_after(block_impl(block), block_impl(before));
t->insert_block_after(b, bf);
} catch (...) {
return OAKNODE_E_FAILED;
}
refresh_lengths(impl(track));
mark_container_owned(block);
refresh_lengths(t);
return OAKNODE_OK;
}
int oaknode_track_insert_block_before(OakNodeTrack *track, OakNodeBlock *block,
OakNodeBlock *after)
int oaknode_track_insert_block_before(OakNodeTrack track, OakNodeBlock block,
OakNodeBlock after)
{
if (!track || !block || !after) {
olive::Track *t = to_native<olive::Track>(track);
olive::Block *b = to_native<olive::Block>(block);
olive::Block *af = to_native<olive::Block>(after);
if (!t || !b || !af) {
return OAKNODE_E_INVALID;
}
try {
impl(track)->insert_block_before(block_impl(block), block_impl(after));
t->insert_block_before(b, af);
} catch (...) {
return OAKNODE_E_FAILED;
}
refresh_lengths(impl(track));
mark_container_owned(block);
refresh_lengths(t);
return OAKNODE_OK;
}
int oaknode_track_ripple_remove_block(OakNodeTrack *track, OakNodeBlock *block)
int oaknode_track_ripple_remove_block(OakNodeTrack track, OakNodeBlock block)
{
if (!track || !block) {
olive::Track *t = to_native<olive::Track>(track);
olive::Block *b = to_native<olive::Block>(block);
if (!t || !b) {
return OAKNODE_E_INVALID;
}
try {
impl(track)->ripple_remove_block(block_impl(block));
t->ripple_remove_block(b);
} catch (...) {
return OAKNODE_E_FAILED;
}
refresh_lengths(impl(track));
refresh_lengths(t);
return OAKNODE_OK;
}
int oaknode_track_replace_block(OakNodeTrack *track, OakNodeBlock *old_block,
OakNodeBlock *new_block)
int oaknode_track_replace_block(OakNodeTrack track, OakNodeBlock old_block,
OakNodeBlock new_block)
{
if (!track || !old_block || !new_block) {
olive::Track *t = to_native<olive::Track>(track);
olive::Block *old_b = to_native<olive::Block>(old_block);
olive::Block *new_b = to_native<olive::Block>(new_block);
if (!t || !old_b || !new_b) {
return OAKNODE_E_INVALID;
}
try {
impl(track)->replace_block(block_impl(old_block), block_impl(new_block));
t->replace_block(old_b, new_b);
} catch (...) {
return OAKNODE_E_FAILED;
}
refresh_lengths(impl(track));
// The track takes over the replacement; the caller's handle to it
// becomes non-owning
mark_container_owned(new_block);
refresh_lengths(t);
return OAKNODE_OK;
}
int oaknode_track_get_block_index(OakNodeTrack *track, OakNodeBlock *block,
int oaknode_track_get_block_index(OakNodeTrack track, OakNodeBlock block,
int *index)
{
if (!track || !block || !index) {
olive::Track *t = to_native<olive::Track>(track);
olive::Block *b = to_native<olive::Block>(block);
if (!t || !b || !index) {
return OAKNODE_E_INVALID;
}
int i = impl(track)->get_array_index_from_block(block_impl(block));
int i = t->get_array_index_from_block(b);
if (i < 0) {
return OAKNODE_E_NOT_FOUND;
}
@@ -410,48 +440,51 @@ int oaknode_track_get_block_index(OakNodeTrack *track, OakNodeBlock *block,
return OAKNODE_OK;
}
int oaknode_track_get_block_containing_time(OakNodeTrack *track, int numerator,
int oaknode_track_get_block_containing_time(OakNodeTrack track, int numerator,
int denominator,
OakNodeBlock **out)
OakNodeBlock *out)
{
if (!track || !out) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !out) {
return OAKNODE_E_INVALID;
}
olive::Block *b = impl(track)->block_containing_time(
olive::Block *b = t->block_containing_time(
olive::core::Rational(numerator, denominator));
if (!b) {
return OAKNODE_E_NOT_FOUND;
}
*out = wrap_block(b);
return OAKNODE_OK;
*out = make_handle<OakNodeBlock>(b, false, &delete_as<olive::Block>);
return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM;
}
int oaknode_track_get_visible_block_at_time(OakNodeTrack *track, int numerator,
int oaknode_track_get_visible_block_at_time(OakNodeTrack track, int numerator,
int denominator,
OakNodeBlock **out)
OakNodeBlock *out)
{
if (!track || !out) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !out) {
return OAKNODE_E_INVALID;
}
olive::Block *b = impl(track)->visible_block_at_time(
olive::Block *b = t->visible_block_at_time(
olive::core::Rational(numerator, denominator));
if (!b) {
return OAKNODE_E_NOT_FOUND;
}
*out = wrap_block(b);
return OAKNODE_OK;
*out = make_handle<OakNodeBlock>(b, false, &delete_as<olive::Block>);
return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM;
}
int oaknode_track_is_range_free(OakNodeTrack *track, int in_num, int in_den,
int oaknode_track_is_range_free(OakNodeTrack track, int in_num, int in_den,
int out_num, int out_den, int *is_free)
{
if (!track || !is_free) {
olive::Track *t = to_native<olive::Track>(track);
if (!t || !is_free) {
return OAKNODE_E_INVALID;
}
*is_free = impl(track)->is_range_free(
olive::core::TimeRange(
olive::core::Rational(in_num, in_den),
olive::core::Rational(out_num, out_den))) ?
*is_free = t->is_range_free(
olive::core::TimeRange(
olive::core::Rational(in_num, in_den),
olive::core::Rational(out_num, out_den))) ?
1 :
0;
return OAKNODE_OK;
@@ -459,63 +492,69 @@ int oaknode_track_is_range_free(OakNodeTrack *track, int in_num, int in_den,
/* ------------------------------------------------------------ TrackList */
int oaknode_tracklist_get_type(OakNodeTrackList *list, int *type)
int oaknode_tracklist_get_type(OakNodeTrackList list, int *type)
{
if (!list || !type) {
olive::TrackList *l = to_native<olive::TrackList>(list);
if (!l || !type) {
return OAKNODE_E_INVALID;
}
*type = int(list_impl(list)->type());
*type = int(l->type());
return OAKNODE_OK;
}
int oaknode_tracklist_get_track_count(OakNodeTrackList *list, int *count)
int oaknode_tracklist_get_track_count(OakNodeTrackList list, int *count)
{
if (!list || !count) {
olive::TrackList *l = to_native<olive::TrackList>(list);
if (!l || !count) {
return OAKNODE_E_INVALID;
}
*count = list_impl(list)->get_track_count();
*count = l->get_track_count();
return OAKNODE_OK;
}
int oaknode_tracklist_get_track_at(OakNodeTrackList *list, int index,
OakNodeTrack **out)
int oaknode_tracklist_get_track_at(OakNodeTrackList list, int index,
OakNodeTrack *out)
{
if (!list || !out || index < 0) {
olive::TrackList *l = to_native<olive::TrackList>(list);
if (!l || !out || index < 0) {
return OAKNODE_E_INVALID;
}
if (index >= list_impl(list)->get_track_count()) {
if (index >= l->get_track_count()) {
return OAKNODE_E_NOT_FOUND;
}
*out = wrap(list_impl(list)->get_track_at(index));
return OAKNODE_OK;
// Borrowed; the track stays owned by the list
*out = make_handle<OakNodeTrack>(l->get_track_at(index), false,
&delete_as<olive::Track>);
return out->ctx ? OAKNODE_OK : OAKNODE_E_NOMEM;
}
int oaknode_tracklist_get_total_length(OakNodeTrackList *list, int *numerator,
int oaknode_tracklist_get_total_length(OakNodeTrackList list, int *numerator,
int *denominator)
{
if (!list) {
olive::TrackList *l = to_native<olive::TrackList>(list);
if (!l) {
return OAKNODE_E_INVALID;
}
return get_rational(list_impl(list)->get_total_length(), numerator,
denominator);
return get_rational(l->get_total_length(), numerator, denominator);
}
int oaknode_tracklist_get_array_size(OakNodeTrackList *list, int *size)
int oaknode_tracklist_get_array_size(OakNodeTrackList list, int *size)
{
if (!list || !size) {
olive::TrackList *l = to_native<olive::TrackList>(list);
if (!l || !size) {
return OAKNODE_E_INVALID;
}
*size = list_impl(list)->array_size();
*size = l->array_size();
return OAKNODE_OK;
}
int oaknode_tracklist_add_track(OakNodeTrackList *list, OakNodeTrack *track)
int oaknode_tracklist_add_track(OakNodeTrackList list, OakNodeTrack track)
{
if (!list || !track) {
olive::TrackList *l = to_native<olive::TrackList>(list);
olive::Track *t = to_native<olive::Track>(track);
if (!l || !t) {
return OAKNODE_E_INVALID;
}
olive::TrackList *l = list_impl(list);
olive::Track *t = impl(track);
olive::Sequence *sequence = l->parent();
if (!sequence) {
return OAKNODE_E_STATE;
@@ -537,16 +576,19 @@ int oaknode_tracklist_add_track(OakNodeTrackList *list, OakNodeTrack *track)
} catch (...) {
return OAKNODE_E_FAILED;
}
// The graph now owns the track; the caller's handle becomes a
// non-owning reference (its release no longer deletes)
mark_container_owned(track);
return OAKNODE_OK;
}
int oaknode_tracklist_remove_track(OakNodeTrackList *list, OakNodeTrack *track)
int oaknode_tracklist_remove_track(OakNodeTrackList list, OakNodeTrack track)
{
if (!list || !track) {
olive::TrackList *l = to_native<olive::TrackList>(list);
olive::Track *t = to_native<olive::Track>(track);
if (!l || !t) {
return OAKNODE_E_INVALID;
}
olive::TrackList *l = list_impl(list);
olive::Track *t = impl(track);
olive::Sequence *sequence = l->parent();
if (!sequence) {
return OAKNODE_E_STATE;
@@ -575,70 +617,78 @@ int oaknode_tracklist_remove_track(OakNodeTrackList *list, OakNodeTrack *track)
return OAKNODE_OK;
}
int oaknode_track_get_nearest_block_before_or_at(OakNodeTrack *track,
int numerator, int denominator, OakNodeBlock **out)
int oaknode_track_get_nearest_block_before_or_at(OakNodeTrack track,
int numerator, int denominator, OakNodeBlock *out)
{
olive::Track *t = impl(track);
olive::Track *t = to_native<olive::Track>(track);
if (!t || !out) {
return OAKNODE_E_INVALID;
}
*out = wrap_block(
t->nearest_block_before_or_at(olive::core::Rational(numerator, denominator)));
// Borrowed (empty when there is no such block)
*out = make_handle<OakNodeBlock>(
t->nearest_block_before_or_at(olive::core::Rational(numerator, denominator)),
false, &delete_as<olive::Block>);
return OAKNODE_OK;
}
int oaknode_track_get_nearest_block_after_or_at(OakNodeTrack *track,
int numerator, int denominator, OakNodeBlock **out)
int oaknode_track_get_nearest_block_after_or_at(OakNodeTrack track,
int numerator, int denominator, OakNodeBlock *out)
{
olive::Track *t = impl(track);
olive::Track *t = to_native<olive::Track>(track);
if (!t || !out) {
return OAKNODE_E_INVALID;
}
*out = wrap_block(
t->nearest_block_after_or_at(olive::core::Rational(numerator, denominator)));
*out = make_handle<OakNodeBlock>(
t->nearest_block_after_or_at(olive::core::Rational(numerator, denominator)),
false, &delete_as<olive::Block>);
return OAKNODE_OK;
}
int oaknode_tracklist_get_sequence(OakNodeTrackList *list,
OakNodeSequence **out)
int oaknode_tracklist_get_sequence(OakNodeTrackList list,
OakNodeSequence *out)
{
if (!list || !out) {
olive::TrackList *l = to_native<olive::TrackList>(list);
if (!l || !out) {
return OAKNODE_E_INVALID;
}
*out = reinterpret_cast<OakNodeSequence *>(list_impl(list)->parent());
// Borrowed; the sequence owns the list
*out = make_handle<OakNodeSequence>(l->parent(), false,
&delete_as<olive::Sequence>);
return OAKNODE_OK;
}
int oaknode_tracklist_get_track_input_id(OakNodeTrackList *list, char *buf,
int oaknode_tracklist_get_track_input_id(OakNodeTrackList list, char *buf,
int buf_size)
{
if (!list) {
olive::TrackList *l = to_native<olive::TrackList>(list);
if (!l) {
return OAKNODE_E_INVALID;
}
return oaknode_c_api::copy_string(list_impl(list)->track_input(), buf,
buf_size);
return oaknode_c_api::copy_string(l->track_input(), buf, buf_size);
}
int oaknode_tracklist_array_append(OakNodeTrackList *list)
int oaknode_tracklist_array_append(OakNodeTrackList list)
{
if (!list) {
olive::TrackList *l = to_native<olive::TrackList>(list);
if (!l) {
return OAKNODE_E_INVALID;
}
try {
list_impl(list)->array_append();
l->array_append();
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
}
}
int oaknode_tracklist_array_remove_last(OakNodeTrackList *list)
int oaknode_tracklist_array_remove_last(OakNodeTrackList list)
{
if (!list) {
olive::TrackList *l = to_native<olive::TrackList>(list);
if (!l) {
return OAKNODE_E_INVALID;
}
try {
list_impl(list)->array_remove_last();
l->array_remove_last();
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
@@ -646,17 +696,19 @@ int oaknode_tracklist_array_remove_last(OakNodeTrackList *list)
}
int oaknode_tracklist_get_array_index_from_cache_index(
OakNodeTrackList *list, int cache_index, int *out_index)
OakNodeTrackList list, int cache_index, int *out_index)
{
if (!list || !out_index) {
olive::TrackList *l = to_native<olive::TrackList>(list);
if (!l || !out_index) {
return OAKNODE_E_INVALID;
}
*out_index =
list_impl(list)->get_array_index_from_cache_index(cache_index);
*out_index = l->get_array_index_from_cache_index(cache_index);
return OAKNODE_OK;
}
OakNodeNode *oaknode_track_as_node(OakNodeTrack *track)
OakNodeNode oaknode_track_as_node(OakNodeTrack track)
{
return reinterpret_cast<OakNodeNode *>(track);
// Borrowed; releasing the result never destroys the track
return make_handle<OakNodeNode>(to_native<olive::Track>(track), false,
&delete_as<olive::Node>);
}
+55 -59
View File
@@ -25,32 +25,22 @@
#include "traverser.h"
#include "valuedatabase.h"
#include "nodehandle.h"
#include "valueconvert.h"
struct OakNodeValueDatabase {
olive::NodeValueDatabase impl;
};
using oaknode_c_api::make_handle;
using oaknode_c_api::to_native;
namespace
{
inline olive::NodeTraverser *to_traverser(OakNodeTraverser *traverser)
{
return reinterpret_cast<olive::NodeTraverser *>(traverser);
}
inline olive::Node *to_node(OakNodeNode *node)
{
return reinterpret_cast<olive::Node *>(node);
}
/**
* @brief Find the table named `key`, or NULL when absent.
*/
const olive::NodeValueTable *find_table(const OakNodeValueDatabase *db,
const olive::NodeValueTable *find_table(const olive::NodeValueDatabase *db,
const char *key)
{
for (auto it = db->impl.cbegin(); it != db->impl.cend(); ++it) {
for (auto it = db->cbegin(); it != db->cend(); ++it) {
if (it->first == key) {
return &it->second;
}
@@ -60,39 +50,34 @@ const olive::NodeValueTable *find_table(const OakNodeValueDatabase *db,
}
OakNodeTraverser *oaknode_traverser_init(void)
OakNodeTraverser oaknode_traverser_init(void)
{
try {
olive::NodeTraverser *traverser = new (std::nothrow) olive::NodeTraverser();
if (traverser) {
oaknode_c_api::alive_inc();
}
return reinterpret_cast<OakNodeTraverser *>(traverser);
return make_handle<OakNodeTraverser>(
new (std::nothrow) olive::NodeTraverser(), true,
oaknode_c_api::delete_as<olive::NodeTraverser>);
} catch (...) {
return NULL;
return OakNodeTraverser{};
}
}
void oaknode_traverser_free(OakNodeTraverser *traverser)
{
if (!traverser) {
return;
}
try {
delete to_traverser(traverser);
oaknode_c_api::alive_dec();
oaknode_c_api::free_handle(traverser);
} catch (...) {
}
}
int oaknode_traverser_generate_database(OakNodeTraverser *traverser,
OakNodeNode *node, int64_t in_num,
int oaknode_traverser_generate_database(OakNodeTraverser traverser,
OakNodeNode node, int64_t in_num,
int64_t in_den, int64_t out_num,
int64_t out_den,
OakNodeValueDatabase **out_db)
OakNodeValueDatabase *out_db)
{
if (!traverser || !node || !out_db) {
olive::NodeTraverser *t = to_native<olive::NodeTraverser>(traverser);
olive::Node *n = to_native<olive::Node>(node);
if (!t || !n || !out_db) {
return OAKNODE_E_INVALID;
}
@@ -102,16 +87,19 @@ int oaknode_traverser_generate_database(OakNodeTraverser *traverser,
olive::core::Rational out(static_cast<int>(out_num),
static_cast<int>(out_den));
OakNodeValueDatabase *db = new (std::nothrow) OakNodeValueDatabase();
olive::NodeValueDatabase *db =
new (std::nothrow) olive::NodeValueDatabase();
if (!db) {
return OAKNODE_E_NOMEM;
}
db->impl = to_traverser(traverser)->generate_database(
to_node(node), olive::core::TimeRange(in, out));
*db = t->generate_database(n, olive::core::TimeRange(in, out));
*out_db = db;
oaknode_c_api::alive_inc();
*out_db = make_handle<OakNodeValueDatabase>(
db, true, oaknode_c_api::delete_as<olive::NodeValueDatabase>);
if (!out_db->ctx) {
return OAKNODE_E_NOMEM;
}
return OAKNODE_OK;
} catch (...) {
return OAKNODE_E_FAILED;
@@ -120,24 +108,24 @@ int oaknode_traverser_generate_database(OakNodeTraverser *traverser,
void oaknode_traverser_database_free(OakNodeValueDatabase *db)
{
if (!db) {
return;
try {
oaknode_c_api::free_handle(db);
} catch (...) {
}
delete db;
oaknode_c_api::alive_dec();
}
int oaknode_traverser_database_row_count(const OakNodeValueDatabase *db,
int oaknode_traverser_database_row_count(OakNodeValueDatabase db,
int *out_count)
{
if (!db || !out_count) {
const olive::NodeValueDatabase *impl =
to_native<olive::NodeValueDatabase>(db);
if (!impl || !out_count) {
return OAKNODE_E_INVALID;
}
try {
int count = 0;
for (auto it = db->impl.cbegin(); it != db->impl.cend(); ++it) {
for (auto it = impl->cbegin(); it != impl->cend(); ++it) {
count++;
}
*out_count = count;
@@ -147,10 +135,12 @@ int oaknode_traverser_database_row_count(const OakNodeValueDatabase *db,
}
}
int oaknode_traverser_database_row_key_at(const OakNodeValueDatabase *db,
int oaknode_traverser_database_row_key_at(OakNodeValueDatabase db,
int index, char *buf, int buf_size)
{
if (!db) {
const olive::NodeValueDatabase *impl =
to_native<olive::NodeValueDatabase>(db);
if (!impl) {
return OAKNODE_E_INVALID;
}
@@ -158,10 +148,10 @@ int oaknode_traverser_database_row_key_at(const OakNodeValueDatabase *db,
if (index < 0) {
return OAKNODE_E_NOT_FOUND;
}
auto it = db->impl.cbegin();
for (int i = 0; i < index && it != db->impl.cend(); i++, ++it) {
auto it = impl->cbegin();
for (int i = 0; i < index && it != impl->cend(); i++, ++it) {
}
if (it == db->impl.cend()) {
if (it == impl->cend()) {
return OAKNODE_E_NOT_FOUND;
}
return oaknode_c_api::copy_string(it->first, buf, buf_size);
@@ -170,16 +160,18 @@ int oaknode_traverser_database_row_key_at(const OakNodeValueDatabase *db,
}
}
int oaknode_traverser_database_row_value_count(const OakNodeValueDatabase *db,
int oaknode_traverser_database_row_value_count(OakNodeValueDatabase db,
const char *key,
int *out_count)
{
if (!db || !key || !out_count) {
const olive::NodeValueDatabase *impl =
to_native<olive::NodeValueDatabase>(db);
if (!impl || !key || !out_count) {
return OAKNODE_E_INVALID;
}
try {
const olive::NodeValueTable *table = find_table(db, key);
const olive::NodeValueTable *table = find_table(impl, key);
if (!table) {
return OAKNODE_E_NOT_FOUND;
}
@@ -190,16 +182,18 @@ int oaknode_traverser_database_row_value_count(const OakNodeValueDatabase *db,
}
}
int oaknode_traverser_database_value_at(const OakNodeValueDatabase *db,
int oaknode_traverser_database_value_at(OakNodeValueDatabase db,
const char *key, int index,
oaknode_value *out)
{
if (!db || !key || !out) {
const olive::NodeValueDatabase *impl =
to_native<olive::NodeValueDatabase>(db);
if (!impl || !key || !out) {
return OAKNODE_E_INVALID;
}
try {
const olive::NodeValueTable *table = find_table(db, key);
const olive::NodeValueTable *table = find_table(impl, key);
if (!table || index < 0 || index >= table->count()) {
return OAKNODE_E_NOT_FOUND;
}
@@ -212,16 +206,18 @@ int oaknode_traverser_database_value_at(const OakNodeValueDatabase *db,
}
}
int oaknode_traverser_database_value_string_at(const OakNodeValueDatabase *db,
int oaknode_traverser_database_value_string_at(OakNodeValueDatabase db,
const char *key, int index,
char *buf, int buf_size)
{
if (!db || !key) {
const olive::NodeValueDatabase *impl =
to_native<olive::NodeValueDatabase>(db);
if (!impl || !key) {
return OAKNODE_E_INVALID;
}
try {
const olive::NodeValueTable *table = find_table(db, key);
const olive::NodeValueTable *table = find_table(impl, key);
if (!table || index < 0 || index >= table->count()) {
return OAKNODE_E_NOT_FOUND;
}
+5 -2
View File
@@ -84,10 +84,13 @@ Sequence::~Sequence()
void Sequence::add_default_nodes(MultiUndoCommand *command)
{
// Create tracks and connect them to the viewer
// (borrowed handles: the track lists are owned by this sequence)
UndoCommand *video_track_command = new TimelineAddTrackCommand(
reinterpret_cast<OakNodeTrackList *>(track_list(Track::k_video)));
oaknode_c_api::make_handle<OakNodeTrackList>(
track_list(Track::k_video), false, nullptr));
UndoCommand *audio_track_command = new TimelineAddTrackCommand(
reinterpret_cast<OakNodeTrackList *>(track_list(Track::k_audio)));
oaknode_c_api::make_handle<OakNodeTrackList>(
track_list(Track::k_audio), false, nullptr));
if (command) {
command->add_child(video_track_command);
+71 -61
View File
@@ -40,32 +40,33 @@ void expect_rational(int num, int den, int expected_num, int expected_den)
TEST(BlockTest, CreateFreeClip)
{
int base = oaknode_debug_alive_count();
OakNodeBlock *clip = oaknode_block_clip_create();
ASSERT_NE(clip, nullptr);
OakNodeBlock clip = oaknode_block_clip_create();
ASSERT_NE(clip.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), base + 1);
oaknode_block_free(clip);
oaknode_block_free(&clip);
EXPECT_EQ(oaknode_debug_alive_count(), base);
EXPECT_EQ(clip.ctx, nullptr);
}
TEST(BlockTest, CreateFreeGap)
{
OakNodeBlock *gap = oaknode_block_gap_create();
ASSERT_NE(gap, nullptr);
oaknode_block_free(gap);
OakNodeBlock gap = oaknode_block_gap_create();
ASSERT_NE(gap.ctx, nullptr);
oaknode_block_free(&gap);
}
TEST(BlockTest, CreateTransitions)
{
OakNodeBlock *cd =
OakNodeBlock cd =
oaknode_block_transition_create(OAKNODE_TRANSITION_CROSS_DISSOLVE);
EXPECT_NE(cd, nullptr);
OakNodeBlock *dc =
EXPECT_NE(cd.ctx, nullptr);
OakNodeBlock dc =
oaknode_block_transition_create(OAKNODE_TRANSITION_DIP_TO_COLOR);
EXPECT_NE(dc, nullptr);
EXPECT_EQ(oaknode_block_transition_create(99), nullptr);
EXPECT_EQ(oaknode_block_transition_create(-1), nullptr);
oaknode_block_free(cd);
oaknode_block_free(dc);
EXPECT_NE(dc.ctx, nullptr);
EXPECT_EQ(oaknode_block_transition_create(99).ctx, nullptr);
EXPECT_EQ(oaknode_block_transition_create(-1).ctx, nullptr);
oaknode_block_free(&cd);
oaknode_block_free(&dc);
}
TEST(BlockTest, FreeNullIsNoOp)
@@ -76,23 +77,24 @@ TEST(BlockTest, FreeNullIsNoOp)
TEST(BlockTest, NullHandleReturnsInvalid)
{
int num, den;
EXPECT_EQ(oaknode_block_get_in(nullptr, &num, &den), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_block_set_in(nullptr, 0, 1), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_block_get_length(nullptr, &num, &den), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_block_get_enabled(nullptr, &num), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_clip_get_speed(nullptr, nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_transition_is_dual(nullptr, &num), OAKNODE_E_INVALID);
OakNodeBlock empty = {};
EXPECT_EQ(oaknode_block_get_in(empty, &num, &den), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_block_set_in(empty, 0, 1), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_block_get_length(empty, &num, &den), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_block_get_enabled(empty, &num), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_clip_get_speed(empty, nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_transition_is_dual(empty, &num), OAKNODE_E_INVALID);
OakNodeBlock *clip = oaknode_block_clip_create();
ASSERT_NE(clip, nullptr);
OakNodeBlock clip = oaknode_block_clip_create();
ASSERT_NE(clip.ctx, nullptr);
EXPECT_EQ(oaknode_block_get_in(clip, nullptr, &den), OAKNODE_E_INVALID);
oaknode_block_free(clip);
oaknode_block_free(&clip);
}
TEST(BlockTest, InOutLength)
{
OakNodeBlock *gap = oaknode_block_gap_create();
ASSERT_NE(gap, nullptr);
OakNodeBlock gap = oaknode_block_gap_create();
ASSERT_NE(gap.ctx, nullptr);
int num, den;
ASSERT_EQ(oaknode_block_set_length_and_media_out(gap, 2, 1), OAKNODE_OK);
@@ -116,13 +118,13 @@ TEST(BlockTest, InOutLength)
ASSERT_EQ(oaknode_block_get_length(gap, &num, &den), OAKNODE_OK);
expect_rational(num, den, 1, 1);
oaknode_block_free(gap);
oaknode_block_free(&gap);
}
TEST(BlockTest, EnabledFlag)
{
OakNodeBlock *gap = oaknode_block_gap_create();
ASSERT_NE(gap, nullptr);
OakNodeBlock gap = oaknode_block_gap_create();
ASSERT_NE(gap.ctx, nullptr);
int enabled = 0;
ASSERT_EQ(oaknode_block_get_enabled(gap, &enabled), OAKNODE_OK);
@@ -132,32 +134,34 @@ TEST(BlockTest, EnabledFlag)
ASSERT_EQ(oaknode_block_get_enabled(gap, &enabled), OAKNODE_OK);
EXPECT_EQ(enabled, 0);
oaknode_block_free(gap);
oaknode_block_free(&gap);
}
TEST(BlockTest, TracklessBlockHasNoNeighbours)
{
OakNodeBlock *gap = oaknode_block_gap_create();
ASSERT_NE(gap, nullptr);
OakNodeBlock gap = oaknode_block_gap_create();
ASSERT_NE(gap.ctx, nullptr);
OakNodeBlock *neighbour = reinterpret_cast<OakNodeBlock *>(0x1);
OakNodeTrack *track = reinterpret_cast<OakNodeTrack *>(0x1);
OakNodeBlock neighbour = {};
neighbour.ctx = reinterpret_cast<void *>(0x1);
OakNodeTrack track = {};
track.ctx = reinterpret_cast<void *>(0x1);
ASSERT_EQ(oaknode_block_get_previous(gap, &neighbour), OAKNODE_OK);
EXPECT_EQ(neighbour, nullptr);
EXPECT_EQ(neighbour.ctx, nullptr);
ASSERT_EQ(oaknode_block_get_next(gap, &neighbour), OAKNODE_OK);
EXPECT_EQ(neighbour, nullptr);
EXPECT_EQ(neighbour.ctx, nullptr);
ASSERT_EQ(oaknode_block_get_track(gap, &track), OAKNODE_OK);
EXPECT_EQ(track, nullptr);
EXPECT_EQ(track.ctx, nullptr);
oaknode_block_free(gap);
oaknode_block_free(&gap);
}
TEST(BlockTest, LinkUnlink)
{
OakNodeBlock *a = oaknode_block_clip_create();
OakNodeBlock *b = oaknode_block_clip_create();
ASSERT_NE(a, nullptr);
ASSERT_NE(b, nullptr);
OakNodeBlock a = oaknode_block_clip_create();
OakNodeBlock b = oaknode_block_clip_create();
ASSERT_NE(a.ctx, nullptr);
ASSERT_NE(b.ctx, nullptr);
int linked = -1;
ASSERT_EQ(oaknode_block_are_linked(a, b, &linked), OAKNODE_OK);
@@ -174,9 +178,12 @@ TEST(BlockTest, LinkUnlink)
ASSERT_EQ(oaknode_block_get_link_count(a, &count), OAKNODE_OK);
EXPECT_EQ(count, 1);
OakNodeBlock *other = nullptr;
OakNodeBlock other = {};
ASSERT_EQ(oaknode_block_get_link_at(a, 0, &other), OAKNODE_OK);
EXPECT_EQ(other, b);
// The linked block is b: it is the only block linked to a
int linked_to_a = -1;
ASSERT_EQ(oaknode_block_are_linked(other, a, &linked_to_a), OAKNODE_OK);
EXPECT_EQ(linked_to_a, 1);
EXPECT_EQ(oaknode_block_get_link_at(a, 1, &other), OAKNODE_E_NOT_FOUND);
ASSERT_EQ(oaknode_block_unlink(a, b), OAKNODE_OK);
@@ -184,16 +191,17 @@ TEST(BlockTest, LinkUnlink)
ASSERT_EQ(oaknode_block_are_linked(a, b, &linked), OAKNODE_OK);
EXPECT_EQ(linked, 0);
EXPECT_EQ(oaknode_block_link(a, nullptr), OAKNODE_E_INVALID);
OakNodeBlock empty = {};
EXPECT_EQ(oaknode_block_link(a, empty), OAKNODE_E_INVALID);
oaknode_block_free(a);
oaknode_block_free(b);
oaknode_block_free(&a);
oaknode_block_free(&b);
}
TEST(BlockTest, ClipProperties)
{
OakNodeBlock *clip = oaknode_block_clip_create();
ASSERT_NE(clip, nullptr);
OakNodeBlock clip = oaknode_block_clip_create();
ASSERT_NE(clip.ctx, nullptr);
int num, den;
ASSERT_EQ(oaknode_clip_set_media_in(clip, 3, 2), OAKNODE_OK);
@@ -234,13 +242,13 @@ TEST(BlockTest, ClipProperties)
ASSERT_EQ(oaknode_clip_get_track_type(clip, &type), OAKNODE_OK);
EXPECT_EQ(type, OAKNODE_TRACK_TYPE_NONE);
oaknode_block_free(clip);
oaknode_block_free(&clip);
}
TEST(BlockTest, ClipApiRejectsNonClip)
{
OakNodeBlock *gap = oaknode_block_gap_create();
ASSERT_NE(gap, nullptr);
OakNodeBlock gap = oaknode_block_gap_create();
ASSERT_NE(gap.ctx, nullptr);
double speed;
int num, den;
@@ -248,14 +256,14 @@ TEST(BlockTest, ClipApiRejectsNonClip)
EXPECT_EQ(oaknode_clip_set_media_in(gap, 1, 1), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_clip_get_media_in(gap, &num, &den), OAKNODE_E_INVALID);
oaknode_block_free(gap);
oaknode_block_free(&gap);
}
TEST(BlockTest, TransitionOffsets)
{
OakNodeBlock *t =
OakNodeBlock t =
oaknode_block_transition_create(OAKNODE_TRANSITION_CROSS_DISSOLVE);
ASSERT_NE(t, nullptr);
ASSERT_NE(t.ctx, nullptr);
int num, den;
ASSERT_EQ(oaknode_transition_set_offsets_and_length(t, 1, 2, 1, 2),
@@ -281,21 +289,23 @@ TEST(BlockTest, TransitionOffsets)
ASSERT_EQ(oaknode_transition_is_dual(t, &dual), OAKNODE_OK);
EXPECT_EQ(dual, 0);
OakNodeBlock *connected = reinterpret_cast<OakNodeBlock *>(0x1);
OakNodeBlock connected = {};
connected.ctx = reinterpret_cast<void *>(0x1);
ASSERT_EQ(oaknode_transition_get_connected_out_block(t, &connected),
OAKNODE_OK);
EXPECT_EQ(connected, nullptr);
EXPECT_EQ(connected.ctx, nullptr);
connected.ctx = reinterpret_cast<void *>(0x1);
ASSERT_EQ(oaknode_transition_get_connected_in_block(t, &connected),
OAKNODE_OK);
EXPECT_EQ(connected, nullptr);
EXPECT_EQ(connected.ctx, nullptr);
oaknode_block_free(t);
oaknode_block_free(&t);
}
TEST(BlockTest, TransitionApiRejectsNonTransition)
{
OakNodeBlock *gap = oaknode_block_gap_create();
ASSERT_NE(gap, nullptr);
OakNodeBlock gap = oaknode_block_gap_create();
ASSERT_NE(gap.ctx, nullptr);
int num, den;
EXPECT_EQ(oaknode_transition_get_in_offset(gap, &num, &den),
@@ -303,5 +313,5 @@ TEST(BlockTest, TransitionApiRejectsNonTransition)
EXPECT_EQ(oaknode_transition_set_offset_center(gap, 1, 2),
OAKNODE_E_INVALID);
oaknode_block_free(gap);
oaknode_block_free(&gap);
}
+61 -31
View File
@@ -33,6 +33,8 @@
// object directly as scaffolding (borrowed OakNodeProject handle).
#include "project.h"
#include "../c_api/nodehandle.h"
#ifndef OAK_OCIO_TEST_CONFIG
#define OAK_OCIO_TEST_CONFIG ""
#endif
@@ -40,6 +42,25 @@
namespace
{
/**
* @brief Wrap a native project in a borrowed (non-owning) value handle.
*/
OakNodeProject borrow_project(olive::Project *project)
{
return oaknode_c_api::make_handle<OakNodeProject>(project, false, nullptr);
}
/**
* @brief Release the box of a borrowed handle produced by
* borrow_project(); the native object is not touched.
*/
void release_borrowed(OakNodeProject h)
{
if (h.ctx) {
h.release(h.ctx);
}
}
/**
* @brief Fixture ensuring the process-wide default OCIO config resolves
*
@@ -56,9 +77,9 @@ protected:
}
}
static std::string get_string(int (*fn)(OakNodeColorManager *, char *,
static std::string get_string(int (*fn)(OakNodeColorManager, char *,
int),
OakNodeColorManager *m)
OakNodeColorManager m)
{
int needed = fn(m, nullptr, 0);
EXPECT_GT(needed, 0);
@@ -73,39 +94,43 @@ protected:
TEST_F(ColorManagerTest, InitFree)
{
olive::Project project;
OakNodeProject ph = borrow_project(&project);
int base = oaknode_debug_alive_count();
OakNodeColorManager *m =
oaknode_colormanager_init(reinterpret_cast<OakNodeProject *>(&project));
ASSERT_NE(m, nullptr);
OakNodeColorManager m = oaknode_colormanager_init(ph);
ASSERT_NE(m.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), base + 1);
oaknode_colormanager_free(m);
oaknode_colormanager_free(&m);
EXPECT_EQ(oaknode_debug_alive_count(), base);
EXPECT_EQ(m.ctx, nullptr);
EXPECT_EQ(oaknode_colormanager_init(nullptr), nullptr);
EXPECT_EQ(oaknode_colormanager_init(OakNodeProject{}).ctx, nullptr);
oaknode_colormanager_free(nullptr);
release_borrowed(ph);
}
TEST_F(ColorManagerTest, NullHandleReturnsInvalid)
{
char buf[64];
int count;
EXPECT_EQ(oaknode_colormanager_get_config_filename(nullptr, buf,
OakNodeColorManager empty = {};
EXPECT_EQ(oaknode_colormanager_get_config_filename(empty, buf,
sizeof(buf)),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_colormanager_initialize(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_colormanager_get_display_count(nullptr, &count),
EXPECT_EQ(oaknode_colormanager_initialize(empty), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_colormanager_get_display_count(empty, &count),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_colormanager_set_config_filename(nullptr, "x"),
EXPECT_EQ(oaknode_colormanager_set_config_filename(empty, "x"),
OAKNODE_E_INVALID);
}
TEST_F(ColorManagerTest, ConfigFilenameRoundTrip)
{
olive::Project project;
OakNodeColorManager *m =
oaknode_colormanager_init(reinterpret_cast<OakNodeProject *>(&project));
ASSERT_NE(m, nullptr);
OakNodeProject ph = borrow_project(&project);
OakNodeColorManager m = oaknode_colormanager_init(ph);
ASSERT_NE(m.ctx, nullptr);
ASSERT_EQ(oaknode_colormanager_set_config_filename(m, "/tmp/myconfig.ocio"),
OAKNODE_OK);
@@ -115,15 +140,16 @@ TEST_F(ColorManagerTest, ConfigFilenameRoundTrip)
EXPECT_EQ(oaknode_colormanager_set_config_filename(m, nullptr),
OAKNODE_E_INVALID);
oaknode_colormanager_free(m);
oaknode_colormanager_free(&m);
release_borrowed(ph);
}
TEST_F(ColorManagerTest, ProjectBackedColorSpaces)
{
olive::Project project;
OakNodeColorManager *m =
oaknode_colormanager_init(reinterpret_cast<OakNodeProject *>(&project));
ASSERT_NE(m, nullptr);
OakNodeProject ph = borrow_project(&project);
OakNodeColorManager m = oaknode_colormanager_init(ph);
ASSERT_NE(m.ctx, nullptr);
// Stored on the project: works even before a config is attached
ASSERT_EQ(oaknode_colormanager_set_default_input_color_space(m, "Linear"),
@@ -138,15 +164,16 @@ TEST_F(ColorManagerTest, ProjectBackedColorSpaces)
get_string(oaknode_colormanager_get_reference_color_space, m);
EXPECT_FALSE(ref.empty());
oaknode_colormanager_free(m);
oaknode_colormanager_free(&m);
release_borrowed(ph);
}
TEST_F(ColorManagerTest, ConfigDependentCallsRequireConfig)
{
olive::Project project;
OakNodeColorManager *m =
oaknode_colormanager_init(reinterpret_cast<OakNodeProject *>(&project));
ASSERT_NE(m, nullptr);
OakNodeProject ph = borrow_project(&project);
OakNodeColorManager m = oaknode_colormanager_init(ph);
ASSERT_NE(m.ctx, nullptr);
// This manager never got initialize(): no config attached
int count;
@@ -167,15 +194,16 @@ TEST_F(ColorManagerTest, ConfigDependentCallsRequireConfig)
sizeof(buf)),
OAKNODE_E_STATE);
oaknode_colormanager_free(m);
oaknode_colormanager_free(&m);
release_borrowed(ph);
}
TEST_F(ColorManagerTest, InitializeAndQueryConfig)
{
olive::Project project;
OakNodeColorManager *m =
oaknode_colormanager_init(reinterpret_cast<OakNodeProject *>(&project));
ASSERT_NE(m, nullptr);
OakNodeProject ph = borrow_project(&project);
OakNodeColorManager m = oaknode_colormanager_init(ph);
ASSERT_NE(m.ctx, nullptr);
ASSERT_EQ(oaknode_colormanager_initialize(m), OAKNODE_OK);
@@ -248,15 +276,16 @@ TEST_F(ColorManagerTest, InitializeAndQueryConfig)
get_string(oaknode_colormanager_get_default_input_color_space,
m));
oaknode_colormanager_free(m);
oaknode_colormanager_free(&m);
release_borrowed(ph);
}
TEST_F(ColorManagerTest, CompliantColorTransform)
{
olive::Project project;
OakNodeColorManager *m =
oaknode_colormanager_init(reinterpret_cast<OakNodeProject *>(&project));
ASSERT_NE(m, nullptr);
OakNodeProject ph = borrow_project(&project);
OakNodeColorManager m = oaknode_colormanager_init(ph);
ASSERT_NE(m.ctx, nullptr);
ASSERT_EQ(oaknode_colormanager_initialize(m), OAKNODE_OK);
std::string display =
@@ -294,5 +323,6 @@ TEST_F(ColorManagerTest, CompliantColorTransform)
&compliant),
OAKNODE_E_INVALID);
oaknode_colormanager_free(m);
oaknode_colormanager_free(&m);
release_borrowed(ph);
}
+15 -9
View File
@@ -79,9 +79,9 @@ TEST_F(NodeFactoryTest, NodeAt)
ASSERT_EQ(oaknode_factory_id_count(&count), OAKNODE_OK);
ASSERT_GT(count, 0);
OakNodeNode *prototype = nullptr;
OakNodeNode prototype = {};
EXPECT_EQ(oaknode_factory_node_at(0, &prototype), OAKNODE_OK);
ASSERT_NE(prototype, nullptr);
ASSERT_NE(prototype.ctx, nullptr);
// The prototype's id matches id_at(0).
int required = oaknode_factory_id_at(0, nullptr, 0);
@@ -93,6 +93,8 @@ TEST_F(NodeFactoryTest, NodeAt)
ASSERT_GT(oaknode_node_get_id(prototype, buf, sizeof(buf)), 1);
EXPECT_STREQ(buf, id.data());
oaknode_node_free(&prototype); // borrowed: releases the handle only
EXPECT_EQ(oaknode_factory_node_at(count, &prototype), OAKNODE_E_NOT_FOUND);
EXPECT_EQ(oaknode_factory_node_at(0, nullptr), OAKNODE_E_INVALID);
}
@@ -111,9 +113,9 @@ TEST_F(NodeFactoryTest, CreateFromId)
name.data(), name_required),
name_required);
OakNodeNode *node =
OakNodeNode node =
oaknode_factory_create_from_id("org.olivevideoeditor.Olive.group");
ASSERT_NE(node, nullptr);
ASSERT_NE(node.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1);
char buf[256];
@@ -121,13 +123,17 @@ TEST_F(NodeFactoryTest, CreateFromId)
EXPECT_STREQ(buf, "org.olivevideoeditor.Olive.group");
// The created instance is a group.
EXPECT_NE(oaknode_group_cast(node), nullptr);
OakNodeGroup as_group = oaknode_group_cast(node);
EXPECT_NE(as_group.ctx, nullptr);
oaknode_group_free(&as_group);
oaknode_node_free(node);
oaknode_node_free(&node);
EXPECT_EQ(node.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before);
EXPECT_EQ(oaknode_factory_create_from_id("org.oak.DoesNotExist"), nullptr);
EXPECT_EQ(oaknode_factory_create_from_id(nullptr), nullptr);
EXPECT_EQ(oaknode_factory_create_from_id("org.oak.DoesNotExist").ctx,
nullptr);
EXPECT_EQ(oaknode_factory_create_from_id(nullptr).ctx, nullptr);
}
TEST(NodeFactoryUninitializedTest, StateErrors)
@@ -138,7 +144,7 @@ TEST(NodeFactoryUninitializedTest, StateErrors)
int count = 0;
EXPECT_EQ(oaknode_factory_id_count(&count), OAKNODE_E_STATE);
EXPECT_EQ(oaknode_factory_id_at(0, nullptr, 0), OAKNODE_E_STATE);
OakNodeNode *node = nullptr;
OakNodeNode node = {};
EXPECT_EQ(oaknode_factory_node_at(0, &node), OAKNODE_E_STATE);
// Re-initialize for any subsequent test run.
+74 -54
View File
@@ -21,14 +21,27 @@
#include <gtest/gtest.h>
#include "node/folder.h"
#include "node/node.h"
#include "node/project.h"
#include "../c_api/nodehandle.h"
namespace
{
OakNodeNode *as_node(OakNodeFolder *folder)
OakNodeNode as_node(OakNodeFolder folder)
{
return reinterpret_cast<OakNodeNode *>(folder);
return oaknode_folder_as_node(folder);
}
/**
* @brief Identity compare for value handles (each handle has its own
* control block, so compare the wrapped objects).
*/
template <typename H1, typename H2>
bool same_object(H1 a, H2 b)
{
return oaknode_c_api::to_native<void>(a) == oaknode_c_api::to_native<void>(b);
}
/**
@@ -40,15 +53,19 @@ OakNodeNode *as_node(OakNodeFolder *folder)
* folder edges (upstream bug, reported to the parent agent). Removing
* the edges first keeps teardown clean.
*/
void detach_all(OakNodeFolder *root, OakNodeFolder *a, OakNodeFolder *b)
void detach_all(OakNodeFolder root, OakNodeFolder a, OakNodeFolder b)
{
if (oaknode_folder_index_of_child(root, as_node(a)) >= 0) {
if (!root.ctx) {
return;
}
if (a.ctx && oaknode_folder_index_of_child(root, as_node(a)) >= 0) {
oaknode_folder_remove_child(root, as_node(a));
}
if (oaknode_folder_index_of_child(root, as_node(b)) >= 0) {
if (b.ctx && oaknode_folder_index_of_child(root, as_node(b)) >= 0) {
oaknode_folder_remove_child(root, as_node(b));
}
if (oaknode_folder_index_of_child(a, as_node(b)) >= 0) {
if (a.ctx && b.ctx &&
oaknode_folder_index_of_child(a, as_node(b)) >= 0) {
oaknode_folder_remove_child(a, as_node(b));
}
}
@@ -57,19 +74,19 @@ void detach_all(OakNodeFolder *root, OakNodeFolder *a, OakNodeFolder *b)
TEST(NodeFolder, CreateAndHierarchy)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK);
OakNodeFolder *root = oaknode_project_root(project);
ASSERT_NE(root, nullptr);
OakNodeFolder root = oaknode_project_root(project);
ASSERT_NE(root.ctx, nullptr);
// Creating a folder requires a project
EXPECT_EQ(oaknode_folder_create(nullptr), nullptr);
EXPECT_EQ(oaknode_folder_create(OakNodeProject{}).ctx, nullptr);
OakNodeFolder *a = oaknode_folder_create(project);
OakNodeFolder *b = oaknode_folder_create(project);
ASSERT_NE(a, nullptr);
ASSERT_NE(b, nullptr);
OakNodeFolder a = oaknode_folder_create(project);
OakNodeFolder b = oaknode_folder_create(project);
ASSERT_NE(a.ctx, nullptr);
ASSERT_NE(b.ctx, nullptr);
EXPECT_EQ(oaknode_folder_child_count(root), 0);
EXPECT_EQ(oaknode_folder_child_count(a), 0);
@@ -77,9 +94,9 @@ TEST(NodeFolder, CreateAndHierarchy)
// add_child builds the hierarchy
EXPECT_EQ(oaknode_folder_add_child(root, as_node(a)), OAKNODE_OK);
EXPECT_EQ(oaknode_folder_child_count(root), 1);
EXPECT_EQ(oaknode_folder_child_at(root, 0), as_node(a));
EXPECT_TRUE(same_object(oaknode_folder_child_at(root, 0), as_node(a)));
EXPECT_EQ(oaknode_folder_index_of_child(root, as_node(a)), 0);
EXPECT_EQ(oaknode_folder_parent_of(as_node(a)), root);
EXPECT_TRUE(same_object(oaknode_folder_parent_of(as_node(a)), root));
EXPECT_EQ(oaknode_folder_add_child(a, as_node(b)), OAKNODE_OK);
EXPECT_EQ(oaknode_folder_has_child_recursive(root, as_node(b)), 1);
@@ -89,32 +106,32 @@ TEST(NodeFolder, CreateAndHierarchy)
// A node can only be in one folder at a time
EXPECT_EQ(oaknode_folder_add_child(root, as_node(b)), OAKNODE_E_STATE);
// Out-of-range child access yields NULL
EXPECT_EQ(oaknode_folder_child_at(root, -1), nullptr);
EXPECT_EQ(oaknode_folder_child_at(root, 1), nullptr);
// Out-of-range child access yields an empty handle
EXPECT_EQ(oaknode_folder_child_at(root, -1).ctx, nullptr);
EXPECT_EQ(oaknode_folder_child_at(root, 1).ctx, nullptr);
// index_of_child on a non-child is E_NOT_FOUND
EXPECT_EQ(oaknode_folder_index_of_child(root, as_node(b)),
OAKNODE_E_NOT_FOUND);
detach_all(root, a, b);
oaknode_project_free(project);
oaknode_project_free(&project);
}
TEST(NodeFolder, RemoveChild)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK);
OakNodeFolder *root = oaknode_project_root(project);
OakNodeFolder *a = oaknode_folder_create(project);
ASSERT_NE(root, nullptr);
ASSERT_NE(a, nullptr);
OakNodeFolder root = oaknode_project_root(project);
OakNodeFolder a = oaknode_folder_create(project);
ASSERT_NE(root.ctx, nullptr);
ASSERT_NE(a.ctx, nullptr);
ASSERT_EQ(oaknode_folder_add_child(root, as_node(a)), OAKNODE_OK);
EXPECT_EQ(oaknode_folder_remove_child(root, as_node(a)), OAKNODE_OK);
EXPECT_EQ(oaknode_folder_child_count(root), 0);
EXPECT_EQ(oaknode_folder_parent_of(as_node(a)), nullptr);
EXPECT_EQ(oaknode_folder_parent_of(as_node(a)).ctx, nullptr);
// Removing a non-child is E_NOT_FOUND
EXPECT_EQ(oaknode_folder_remove_child(root, as_node(a)),
@@ -124,37 +141,37 @@ TEST(NodeFolder, RemoveChild)
EXPECT_EQ(oaknode_folder_add_child(root, as_node(a)), OAKNODE_OK);
EXPECT_EQ(oaknode_folder_child_count(root), 1);
detach_all(root, a, nullptr);
oaknode_project_free(project);
detach_all(root, a, OakNodeFolder{});
oaknode_project_free(&project);
}
TEST(NodeFolder, MoveChildren)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK);
OakNodeFolder *root = oaknode_project_root(project);
OakNodeFolder *a = oaknode_folder_create(project);
OakNodeFolder *b = oaknode_folder_create(project);
OakNodeFolder *c = oaknode_folder_create(project);
ASSERT_NE(root, nullptr);
ASSERT_NE(a, nullptr);
ASSERT_NE(b, nullptr);
ASSERT_NE(c, nullptr);
OakNodeFolder root = oaknode_project_root(project);
OakNodeFolder a = oaknode_folder_create(project);
OakNodeFolder b = oaknode_folder_create(project);
OakNodeFolder c = oaknode_folder_create(project);
ASSERT_NE(root.ctx, nullptr);
ASSERT_NE(a.ctx, nullptr);
ASSERT_NE(b.ctx, nullptr);
ASSERT_NE(c.ctx, nullptr);
ASSERT_EQ(oaknode_folder_add_child(root, as_node(a)), OAKNODE_OK);
ASSERT_EQ(oaknode_folder_add_child(root, as_node(b)), OAKNODE_OK);
ASSERT_EQ(oaknode_folder_add_child(a, as_node(c)), OAKNODE_OK);
// Move b and c into a in one call
OakNodeNode *to_move[] = { as_node(b), as_node(c) };
OakNodeNode to_move[] = { as_node(b), as_node(c) };
EXPECT_EQ(oaknode_folder_move_children(to_move, 2, a), OAKNODE_OK);
EXPECT_EQ(oaknode_folder_child_count(root), 1);
// c was already in a and is skipped; a ends up with {c, b}
EXPECT_EQ(oaknode_folder_child_count(a), 2);
EXPECT_EQ(oaknode_folder_parent_of(as_node(b)), a);
EXPECT_EQ(oaknode_folder_parent_of(as_node(c)), a);
EXPECT_TRUE(same_object(oaknode_folder_parent_of(as_node(b)), a));
EXPECT_TRUE(same_object(oaknode_folder_parent_of(as_node(c)), a));
EXPECT_EQ(oaknode_folder_index_of_child(a, as_node(c)), 0);
EXPECT_EQ(oaknode_folder_index_of_child(a, as_node(b)), 1);
@@ -164,29 +181,32 @@ TEST(NodeFolder, MoveChildren)
// A node with no folder is simply appended
EXPECT_EQ(oaknode_folder_remove_child(root, as_node(a)), OAKNODE_OK);
OakNodeNode *one[] = { as_node(a) };
OakNodeNode one[] = { as_node(a) };
EXPECT_EQ(oaknode_folder_move_children(one, 1, root), OAKNODE_OK);
EXPECT_EQ(oaknode_folder_parent_of(as_node(a)), root);
EXPECT_TRUE(same_object(oaknode_folder_parent_of(as_node(a)), root));
// Detach every remaining edge before teardown (see detach_all).
detach_all(root, a, b);
detach_all(a, b, c);
detach_all(b, a, c);
oaknode_project_free(project);
oaknode_project_free(&project);
}
TEST(NodeFolder, NullHandleErrors)
{
EXPECT_EQ(oaknode_folder_child_count(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_folder_child_at(nullptr, 0), nullptr);
EXPECT_EQ(oaknode_folder_add_child(nullptr, nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_folder_remove_child(nullptr, nullptr),
EXPECT_EQ(oaknode_folder_child_count(OakNodeFolder{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_folder_move_children(nullptr, 0, nullptr),
EXPECT_EQ(oaknode_folder_child_at(OakNodeFolder{}, 0).ctx, nullptr);
EXPECT_EQ(oaknode_folder_add_child(OakNodeFolder{}, OakNodeNode{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_folder_has_child_recursive(nullptr, nullptr),
EXPECT_EQ(oaknode_folder_remove_child(OakNodeFolder{}, OakNodeNode{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_folder_index_of_child(nullptr, nullptr),
EXPECT_EQ(oaknode_folder_move_children(nullptr, 0, OakNodeFolder{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_folder_parent_of(nullptr), nullptr);
EXPECT_EQ(oaknode_folder_has_child_recursive(OakNodeFolder{},
OakNodeNode{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_folder_index_of_child(OakNodeFolder{}, OakNodeNode{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_folder_parent_of(OakNodeNode{}).ctx, nullptr);
}
+44 -36
View File
@@ -33,8 +33,8 @@ namespace
/**
* @brief Two-stage string getter helper: query, then fetch.
*/
std::string get_string(int (*fn)(const OakNodeFootage *, char *, int),
const OakNodeFootage *footage)
std::string get_string(int (*fn)(OakNodeFootage, char *, int),
OakNodeFootage footage)
{
int required = fn(footage, nullptr, 0);
if (required <= 0) {
@@ -49,14 +49,14 @@ std::string get_string(int (*fn)(const OakNodeFootage *, char *, int),
TEST(NodeFootage, CreateAndFilename)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
// Creating footage requires a project
EXPECT_EQ(oaknode_footage_create(nullptr, nullptr), nullptr);
EXPECT_EQ(oaknode_footage_create(OakNodeProject{}, nullptr).ctx, nullptr);
OakNodeFootage *footage = oaknode_footage_create(project, nullptr);
ASSERT_NE(footage, nullptr);
OakNodeFootage footage = oaknode_footage_create(project, nullptr);
ASSERT_NE(footage.ctx, nullptr);
EXPECT_EQ(get_string(oaknode_footage_filename, footage), "");
// A nonexistent path keeps the footage invalid but stores the name
@@ -71,15 +71,15 @@ TEST(NodeFootage, CreateAndFilename)
EXPECT_EQ(oaknode_footage_set_filename(footage, nullptr),
OAKNODE_E_INVALID);
oaknode_project_free(project);
oaknode_project_free(&project);
}
TEST(NodeFootage, TimestampAndMetadataDefaults)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeFootage *footage = oaknode_footage_create(project, nullptr);
ASSERT_NE(footage, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
OakNodeFootage footage = oaknode_footage_create(project, nullptr);
ASSERT_NE(footage.ctx, nullptr);
int64_t timestamp = -1;
EXPECT_EQ(oaknode_footage_timestamp(footage, &timestamp), OAKNODE_OK);
@@ -102,15 +102,15 @@ TEST(NodeFootage, TimestampAndMetadataDefaults)
OAKNODE_OK);
EXPECT_EQ(numerator, 0);
oaknode_project_free(project);
oaknode_project_free(&project);
}
TEST(NodeFootage, Proxy)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeFootage *footage = oaknode_footage_create(project, nullptr);
ASSERT_NE(footage, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
OakNodeFootage footage = oaknode_footage_create(project, nullptr);
ASSERT_NE(footage.ctx, nullptr);
EXPECT_EQ(oaknode_footage_proxy_enabled(footage), 0);
EXPECT_EQ(get_string(oaknode_footage_proxy_path, footage), "");
@@ -132,7 +132,7 @@ TEST(NodeFootage, Proxy)
EXPECT_EQ(get_string(oaknode_footage_proxy_path, footage), "");
EXPECT_EQ(oaknode_footage_proxy_state(footage), 0);
oaknode_project_free(project);
oaknode_project_free(&project);
}
TEST(NodeFootage, NullHandleErrors)
@@ -141,32 +141,40 @@ TEST(NodeFootage, NullHandleErrors)
int num = 0;
int den = 0;
EXPECT_EQ(oaknode_footage_filename(nullptr, nullptr, 0),
EXPECT_EQ(oaknode_footage_filename(OakNodeFootage{}, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_set_filename(nullptr, "/tmp/x"),
EXPECT_EQ(oaknode_footage_set_filename(OakNodeFootage{}, "/tmp/x"),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_is_valid(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_timestamp(nullptr, &timestamp),
EXPECT_EQ(oaknode_footage_is_valid(OakNodeFootage{}), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_timestamp(OakNodeFootage{}, &timestamp),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_timestamp(nullptr, nullptr),
EXPECT_EQ(oaknode_footage_timestamp(OakNodeFootage{}, nullptr),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_set_timestamp(nullptr, 0), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_decoder(nullptr, nullptr, 0),
EXPECT_EQ(oaknode_footage_set_timestamp(OakNodeFootage{}, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_total_stream_count(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_video_stream_count(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_audio_stream_count(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_subtitle_stream_count(nullptr),
EXPECT_EQ(oaknode_footage_decoder(OakNodeFootage{}, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_duration(nullptr, &num, &den),
EXPECT_EQ(oaknode_footage_total_stream_count(OakNodeFootage{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_proxy_enabled(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_set_proxy_enabled(nullptr, 1),
EXPECT_EQ(oaknode_footage_video_stream_count(OakNodeFootage{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_proxy_path(nullptr, nullptr, 0),
EXPECT_EQ(oaknode_footage_audio_stream_count(OakNodeFootage{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_proxy_state(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_set_proxy(nullptr, "/tmp/p", 2, 0, 1, 1),
EXPECT_EQ(oaknode_footage_subtitle_stream_count(OakNodeFootage{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_duration(OakNodeFootage{}, &num, &den),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_proxy_enabled(OakNodeFootage{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_set_proxy_enabled(OakNodeFootage{}, 1),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_proxy_path(OakNodeFootage{}, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_proxy_state(OakNodeFootage{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_set_proxy(OakNodeFootage{}, "/tmp/p", 2, 0, 1,
1),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_clear_proxy(OakNodeFootage{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_footage_clear_proxy(nullptr), OAKNODE_E_INVALID);
}
+79 -45
View File
@@ -26,6 +26,8 @@
#include "undo/undocommand.h"
#include "../c_api/nodehandle.h"
#include "../src/group/group.h"
#include "testnode.h"
namespace
@@ -34,17 +36,28 @@ namespace
using oaknode_test::TestNode;
using oaknode_test::as_handle;
/**
* @brief Borrowed OakNodeNode view of a group handle (the group IS a
* node). Release with oaknode_node_free().
*/
OakNodeNode group_as_node(OakNodeGroup group)
{
return oaknode_c_api::make_handle<OakNodeNode>(
oaknode_c_api::to_native<olive::NodeGroup>(group), false, nullptr);
}
/**
* @brief Create a group with `inner` registered in its context
* (NodeGroup::add_input_passthrough() asserts context membership).
*/
OakNodeGroup *make_group_with_inner(OakNodeNode *inner)
OakNodeGroup make_group_with_inner(OakNodeNode inner)
{
OakNodeGroup *group = oaknode_group_create();
EXPECT_NE(group, nullptr);
EXPECT_EQ(oaknode_node_set_context_position(
reinterpret_cast<OakNodeNode *>(group), inner, 0.0, 0.0, 0),
OakNodeGroup group = oaknode_group_create();
EXPECT_NE(group.ctx, nullptr);
OakNodeNode node_view = group_as_node(group);
EXPECT_EQ(oaknode_node_set_context_position(node_view, inner, 0.0, 0.0, 0),
OAKNODE_OK);
oaknode_node_free(&node_view);
return group;
}
@@ -52,18 +65,24 @@ TEST(NodeGroupTest, CreateCastFree)
{
int alive_before = oaknode_debug_alive_count();
OakNodeGroup *group = oaknode_group_create();
ASSERT_NE(group, nullptr);
OakNodeGroup group = oaknode_group_create();
ASSERT_NE(group.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1);
OakNodeNode *as_node = reinterpret_cast<OakNodeNode *>(group);
EXPECT_EQ(oaknode_group_cast(as_node), group);
OakNodeNode as_node = group_as_node(group);
OakNodeGroup casted = oaknode_group_cast(as_node);
ASSERT_NE(casted.ctx, nullptr);
EXPECT_EQ(oaknode_c_api::to_native<olive::NodeGroup>(casted),
oaknode_c_api::to_native<olive::NodeGroup>(group));
oaknode_group_free(&casted);
oaknode_node_free(&as_node);
TestNode plain;
EXPECT_EQ(oaknode_group_cast(as_handle(&plain)), nullptr);
EXPECT_EQ(oaknode_group_cast(nullptr), nullptr);
EXPECT_EQ(oaknode_group_cast(as_handle(&plain)).ctx, nullptr);
EXPECT_EQ(oaknode_group_cast(OakNodeNode{}).ctx, nullptr);
oaknode_group_free(group);
oaknode_group_free(&group);
EXPECT_EQ(group.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before);
oaknode_group_free(nullptr); // no crash
@@ -72,10 +91,10 @@ TEST(NodeGroupTest, CreateCastFree)
TEST(NodeGroupTest, PassthroughAddEnumerateRemove)
{
TestNode inner;
OakNodeNode *inner_handle = as_handle(&inner);
OakNodeGroup *group = make_group_with_inner(inner_handle);
ASSERT_NE(group, nullptr);
OakNodeNode *group_node = reinterpret_cast<OakNodeNode *>(group);
OakNodeNode inner_handle = as_handle(&inner);
OakNodeGroup group = make_group_with_inner(inner_handle);
ASSERT_NE(group.ctx, nullptr);
OakNodeNode group_node = group_as_node(group);
// Two-stage: query the generated id size first.
int float_required = oaknode_group_add_input_passthrough(
@@ -102,15 +121,17 @@ TEST(NodeGroupTest, PassthroughAddEnumerateRemove)
EXPECT_EQ(oaknode_node_input_get_type(group_node, buf, &type), OAKNODE_OK);
EXPECT_EQ(type, OAKNODE_VALUE_FLOAT);
OakNodeNode *pt_node = nullptr;
OakNodeNode pt_node = {};
int pt_element = -2;
char pt_id[64];
EXPECT_EQ(oaknode_group_passthrough_input_at(group, 0, &pt_node, pt_id,
sizeof(pt_id), &pt_element),
9);
EXPECT_EQ(pt_node, inner_handle);
EXPECT_EQ(oaknode_c_api::to_native<olive::Node>(pt_node),
oaknode_c_api::to_native<olive::Node>(inner_handle));
EXPECT_STREQ(pt_id, "float_in");
EXPECT_EQ(pt_element, -1);
oaknode_node_free(&pt_node);
EXPECT_EQ(oaknode_group_passthrough_input_at(group, 5, &pt_node, pt_id,
sizeof(pt_id), &pt_element),
OAKNODE_E_NOT_FOUND);
@@ -123,18 +144,20 @@ TEST(NodeGroupTest, PassthroughAddEnumerateRemove)
EXPECT_EQ(oaknode_group_remove_input_passthrough(group, inner_handle,
"float_in", -1),
OAKNODE_E_NOT_FOUND);
EXPECT_EQ(oaknode_group_remove_input_passthrough(nullptr, inner_handle,
EXPECT_EQ(oaknode_group_remove_input_passthrough(OakNodeGroup{},
inner_handle,
"float_in", -1),
OAKNODE_E_INVALID);
oaknode_group_free(group);
oaknode_node_free(&group_node);
oaknode_group_free(&group);
}
TEST(NodeGroupTest, PassthroughAddUndoable)
{
TestNode inner;
OakNodeGroup *group = make_group_with_inner(as_handle(&inner));
ASSERT_NE(group, nullptr);
OakNodeGroup group = make_group_with_inner(as_handle(&inner));
ASSERT_NE(group.ctx, nullptr);
OakUndoCommand command = {};
EXPECT_EQ(oaknode_group_add_input_passthrough_undoable(
@@ -155,51 +178,56 @@ TEST(NodeGroupTest, PassthroughAddUndoable)
EXPECT_EQ(count, 0);
oakundo_command_free(&command);
oaknode_group_free(group);
oaknode_group_free(&group);
}
TEST(NodeGroupTest, OutputPassthroughLiveAndUndoable)
{
TestNode inner;
OakNodeNode *inner_handle = as_handle(&inner);
OakNodeGroup *group = make_group_with_inner(inner_handle);
ASSERT_NE(group, nullptr);
OakNodeNode inner_handle = as_handle(&inner);
OakNodeGroup group = make_group_with_inner(inner_handle);
ASSERT_NE(group.ctx, nullptr);
OakNodeNode *out = reinterpret_cast<OakNodeNode *>(uintptr_t(1));
OakNodeNode out = {};
out.ctx = reinterpret_cast<void *>(uintptr_t(1)); // sentinel: must be overwritten
EXPECT_EQ(oaknode_group_get_output_passthrough(group, &out), OAKNODE_OK);
EXPECT_EQ(out, nullptr);
EXPECT_EQ(out.ctx, nullptr);
EXPECT_EQ(oaknode_group_set_output_passthrough(group, inner_handle),
OAKNODE_OK);
EXPECT_EQ(oaknode_group_get_output_passthrough(group, &out), OAKNODE_OK);
EXPECT_EQ(out, inner_handle);
EXPECT_EQ(oaknode_c_api::to_native<olive::Node>(out),
oaknode_c_api::to_native<olive::Node>(inner_handle));
oaknode_node_free(&out);
OakUndoCommand command = {};
EXPECT_EQ(oaknode_group_set_output_passthrough_undoable(group, nullptr,
&command),
EXPECT_EQ(oaknode_group_set_output_passthrough_undoable(
group, OakNodeNode{}, &command),
OAKNODE_OK);
ASSERT_NE(command.ctx, nullptr);
EXPECT_EQ(oakundo_command_redo_now(command), OAKUNDO_OK);
EXPECT_EQ(oaknode_group_get_output_passthrough(group, &out), OAKNODE_OK);
EXPECT_EQ(out, nullptr);
EXPECT_EQ(out.ctx, nullptr);
EXPECT_EQ(oakundo_command_undo_now(command), OAKUNDO_OK);
EXPECT_EQ(oaknode_group_get_output_passthrough(group, &out), OAKNODE_OK);
EXPECT_EQ(out, inner_handle);
EXPECT_EQ(oaknode_c_api::to_native<olive::Node>(out),
oaknode_c_api::to_native<olive::Node>(inner_handle));
oaknode_node_free(&out);
oakundo_command_free(&command);
EXPECT_EQ(oaknode_group_set_output_passthrough(nullptr, inner_handle),
EXPECT_EQ(oaknode_group_set_output_passthrough(OakNodeGroup{}, inner_handle),
OAKNODE_E_INVALID);
oaknode_group_free(group);
oaknode_group_free(&group);
}
TEST(NodeGroupTest, ResolveInput)
{
TestNode inner;
OakNodeNode *inner_handle = as_handle(&inner);
OakNodeGroup *group = make_group_with_inner(inner_handle);
ASSERT_NE(group, nullptr);
OakNodeNode *group_node = reinterpret_cast<OakNodeNode *>(group);
OakNodeNode inner_handle = as_handle(&inner);
OakNodeGroup group = make_group_with_inner(inner_handle);
ASSERT_NE(group.ctx, nullptr);
OakNodeNode group_node = group_as_node(group);
char id_buf[64];
int required = oaknode_group_add_input_passthrough(group, inner_handle,
@@ -208,7 +236,7 @@ TEST(NodeGroupTest, ResolveInput)
ASSERT_GT(required, 1);
// Resolving the group's passthrough id yields the inner input.
OakNodeNode *resolved_node = nullptr;
OakNodeNode resolved_node = {};
int resolved_element = -2;
char resolved_id[64];
EXPECT_EQ(oaknode_group_resolve_input(group_node, id_buf, -1,
@@ -216,9 +244,11 @@ TEST(NodeGroupTest, ResolveInput)
sizeof(resolved_id),
&resolved_element),
9);
EXPECT_EQ(resolved_node, inner_handle);
EXPECT_EQ(oaknode_c_api::to_native<olive::Node>(resolved_node),
oaknode_c_api::to_native<olive::Node>(inner_handle));
EXPECT_STREQ(resolved_id, "float_in");
EXPECT_EQ(resolved_element, -1);
oaknode_node_free(&resolved_node);
// A non-group input resolves to itself.
EXPECT_EQ(oaknode_group_resolve_input(inner_handle, "float_in", -1,
@@ -226,12 +256,15 @@ TEST(NodeGroupTest, ResolveInput)
sizeof(resolved_id),
&resolved_element),
9);
EXPECT_EQ(resolved_node, inner_handle);
EXPECT_EQ(oaknode_c_api::to_native<olive::Node>(resolved_node),
oaknode_c_api::to_native<olive::Node>(inner_handle));
EXPECT_STREQ(resolved_id, "float_in");
oaknode_node_free(&resolved_node);
// Error paths.
EXPECT_EQ(oaknode_group_resolve_input(nullptr, id_buf, -1, &resolved_node,
resolved_id, sizeof(resolved_id),
EXPECT_EQ(oaknode_group_resolve_input(OakNodeNode{}, id_buf, -1,
&resolved_node, resolved_id,
sizeof(resolved_id),
&resolved_element),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_group_resolve_input(group_node, nullptr, -1,
@@ -240,7 +273,8 @@ TEST(NodeGroupTest, ResolveInput)
&resolved_element),
OAKNODE_E_INVALID);
oaknode_group_free(group);
oaknode_node_free(&group_node);
oaknode_group_free(&group);
}
}
+55 -41
View File
@@ -24,6 +24,7 @@
#include "undo/undocommand.h"
#include "../c_api/nodehandle.h"
#include "testnode.h"
namespace
@@ -53,16 +54,16 @@ TEST(NodeKeyframeTest, EnumOrdinalsArePinned)
TEST(NodeKeyframeTest, CreateAccessorsFree)
{
TestNode node;
OakNodeNode *node_handle = as_handle(&node);
OakNodeNode node_handle = as_handle(&node);
int alive_before = oaknode_debug_alive_count();
oaknode_value value = make_float(1.5);
OakNodeKeyframe *key = oaknode_keyframe_create(2, 1, &value,
OAKNODE_KEYFRAME_LINEAR, 0,
-1, "float_in",
node_handle);
ASSERT_NE(key, nullptr);
OakNodeKeyframe key = oaknode_keyframe_create(2, 1, &value,
OAKNODE_KEYFRAME_LINEAR, 0,
-1, "float_in",
node_handle);
ASSERT_NE(key.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1);
int64_t num = 0, den = 0;
@@ -88,11 +89,14 @@ TEST(NodeKeyframeTest, CreateAccessorsFree)
EXPECT_EQ(oaknode_keyframe_get_input(key, buf, sizeof(buf)), 9);
EXPECT_STREQ(buf, "float_in");
OakNodeNode *parent = nullptr;
OakNodeNode parent = {};
EXPECT_EQ(oaknode_keyframe_get_parent(key, &parent), OAKNODE_OK);
EXPECT_EQ(parent, node_handle);
EXPECT_EQ(oaknode_c_api::to_native<olive::Node>(parent),
oaknode_c_api::to_native<olive::Node>(node_handle));
oaknode_node_free(&parent);
oaknode_keyframe_free(key);
oaknode_keyframe_free(&key);
EXPECT_EQ(key.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before);
oaknode_keyframe_free(nullptr); // no crash
@@ -102,21 +106,25 @@ TEST(NodeKeyframeTest, CreateErrorPaths)
{
oaknode_value value = make_float(1.0);
// Invalid interpolation type.
EXPECT_EQ(oaknode_keyframe_create(0, 1, &value, 99, 0, -1, "x", nullptr),
EXPECT_EQ(oaknode_keyframe_create(0, 1, &value, 99, 0, -1, "x",
OakNodeNode{})
.ctx,
nullptr);
// STRING does not fit the POD.
value.type = OAKNODE_VALUE_STRING;
EXPECT_EQ(oaknode_keyframe_create(0, 1, &value, OAKNODE_KEYFRAME_LINEAR, 0,
-1, "x", nullptr),
-1, "x", OakNodeNode{})
.ctx,
nullptr);
}
TEST(NodeKeyframeTest, TimeLiveAndUndoable)
{
OakNodeKeyframe *key = oaknode_keyframe_create(0, 1, nullptr,
OAKNODE_KEYFRAME_LINEAR, 0,
-1, "float_in", nullptr);
ASSERT_NE(key, nullptr);
OakNodeKeyframe key = oaknode_keyframe_create(0, 1, nullptr,
OAKNODE_KEYFRAME_LINEAR, 0,
-1, "float_in",
OakNodeNode{});
ASSERT_NE(key.ctx, nullptr);
EXPECT_EQ(oaknode_keyframe_set_time(key, 1, 2), OAKNODE_OK);
int64_t num = 0, den = 0;
@@ -139,19 +147,21 @@ TEST(NodeKeyframeTest, TimeLiveAndUndoable)
EXPECT_EQ(num, 1);
oakundo_command_free(&command);
oaknode_keyframe_free(key);
oaknode_keyframe_free(&key);
EXPECT_EQ(oaknode_keyframe_set_time(nullptr, 0, 1), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_keyframe_get_time(nullptr, &num, &den),
EXPECT_EQ(oaknode_keyframe_set_time(OakNodeKeyframe{}, 0, 1),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_keyframe_get_time(OakNodeKeyframe{}, &num, &den),
OAKNODE_E_INVALID);
}
TEST(NodeKeyframeTest, ValueLiveAndUndoable)
{
OakNodeKeyframe *key = oaknode_keyframe_create(0, 1, nullptr,
OAKNODE_KEYFRAME_LINEAR, 0,
-1, "float_in", nullptr);
ASSERT_NE(key, nullptr);
OakNodeKeyframe key = oaknode_keyframe_create(0, 1, nullptr,
OAKNODE_KEYFRAME_LINEAR, 0,
-1, "float_in",
OakNodeNode{});
ASSERT_NE(key.ctx, nullptr);
oaknode_value in = make_float(3.25);
EXPECT_EQ(oaknode_keyframe_set_value(key, &in), OAKNODE_OK);
@@ -180,15 +190,15 @@ TEST(NodeKeyframeTest, ValueLiveAndUndoable)
EXPECT_EQ(oaknode_keyframe_set_value(key, &in), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_keyframe_set_value(key, nullptr), OAKNODE_E_INVALID);
oaknode_keyframe_free(key);
oaknode_keyframe_free(&key);
}
TEST(NodeKeyframeTest, StringValueLiveAndUndoable)
{
OakNodeKeyframe *key = oaknode_keyframe_create(0, 1, nullptr,
OAKNODE_KEYFRAME_HOLD, 0, -1,
"text_in", nullptr);
ASSERT_NE(key, nullptr);
OakNodeKeyframe key = oaknode_keyframe_create(0, 1, nullptr,
OAKNODE_KEYFRAME_HOLD, 0, -1,
"text_in", OakNodeNode{});
ASSERT_NE(key.ctx, nullptr);
EXPECT_EQ(oaknode_keyframe_set_value_string(key, "hello"), OAKNODE_OK);
char buf[16];
@@ -210,18 +220,20 @@ TEST(NodeKeyframeTest, StringValueLiveAndUndoable)
EXPECT_EQ(oaknode_keyframe_set_value_string(key, nullptr),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_keyframe_get_value_string(nullptr, buf, sizeof(buf)),
EXPECT_EQ(oaknode_keyframe_get_value_string(OakNodeKeyframe{}, buf,
sizeof(buf)),
OAKNODE_E_INVALID);
oaknode_keyframe_free(key);
oaknode_keyframe_free(&key);
}
TEST(NodeKeyframeTest, TypeLiveAndUndoable)
{
OakNodeKeyframe *key = oaknode_keyframe_create(0, 1, nullptr,
OAKNODE_KEYFRAME_LINEAR, 0,
-1, "float_in", nullptr);
ASSERT_NE(key, nullptr);
OakNodeKeyframe key = oaknode_keyframe_create(0, 1, nullptr,
OAKNODE_KEYFRAME_LINEAR, 0,
-1, "float_in",
OakNodeNode{});
ASSERT_NE(key.ctx, nullptr);
EXPECT_EQ(oaknode_keyframe_set_type(key, OAKNODE_KEYFRAME_HOLD),
OAKNODE_OK);
@@ -243,18 +255,20 @@ TEST(NodeKeyframeTest, TypeLiveAndUndoable)
oakundo_command_free(&command);
EXPECT_EQ(oaknode_keyframe_set_type(key, 99), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_keyframe_set_type(nullptr, OAKNODE_KEYFRAME_HOLD),
EXPECT_EQ(oaknode_keyframe_set_type(OakNodeKeyframe{},
OAKNODE_KEYFRAME_HOLD),
OAKNODE_E_INVALID);
oaknode_keyframe_free(key);
oaknode_keyframe_free(&key);
}
TEST(NodeKeyframeTest, BezierControlLiveAndUndoable)
{
OakNodeKeyframe *key = oaknode_keyframe_create(0, 1, nullptr,
OAKNODE_KEYFRAME_BEZIER, 0,
-1, "float_in", nullptr);
ASSERT_NE(key, nullptr);
OakNodeKeyframe key = oaknode_keyframe_create(0, 1, nullptr,
OAKNODE_KEYFRAME_BEZIER, 0,
-1, "float_in",
OakNodeNode{});
ASSERT_NE(key.ctx, nullptr);
EXPECT_EQ(oaknode_keyframe_set_bezier_control(
key, OAKNODE_KEYFRAME_IN_HANDLE, -1.0, 0.5),
@@ -287,12 +301,12 @@ TEST(NodeKeyframeTest, BezierControlLiveAndUndoable)
EXPECT_EQ(oaknode_keyframe_set_bezier_control(key, 99, 0.0, 0.0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_keyframe_get_bezier_control(nullptr,
EXPECT_EQ(oaknode_keyframe_get_bezier_control(OakNodeKeyframe{},
OAKNODE_KEYFRAME_IN_HANDLE,
&x, &y),
OAKNODE_E_INVALID);
oaknode_keyframe_free(key);
oaknode_keyframe_free(&key);
}
}
+83 -46
View File
@@ -35,9 +35,9 @@ namespace
using oaknode_test::TestNode;
using oaknode_test::as_handle;
using oaknode_test::same_node;
std::string get_string(int (*fn)(const OakNodeNode *, char *, int),
const OakNodeNode *node)
std::string get_string(int (*fn)(OakNodeNode, char *, int), OakNodeNode node)
{
int required = fn(node, nullptr, 0);
EXPECT_GT(required, 0);
@@ -72,7 +72,7 @@ TEST(NodeValueMappingTest, OakEnumOrdinalsArePinned)
TEST(NodeValueMappingTest, OliveToOakMappingIsPinned)
{
TestNode node;
OakNodeNode *handle = as_handle(&node);
OakNodeNode handle = as_handle(&node);
struct {
const char *input;
@@ -90,12 +90,14 @@ TEST(NodeValueMappingTest, OliveToOakMappingIsPinned)
OAKNODE_OK);
EXPECT_EQ(type, c.expected) << c.input;
}
oaknode_node_free(&handle);
}
TEST(NodeMetadataTest, IdNameLabelRoundtrip)
{
TestNode node;
OakNodeNode *handle = as_handle(&node);
OakNodeNode handle = as_handle(&node);
EXPECT_EQ(get_string(oaknode_node_get_id, handle), TestNode::k_id);
EXPECT_EQ(get_string(oaknode_node_get_name, handle), "Test Node");
@@ -108,12 +110,14 @@ TEST(NodeMetadataTest, IdNameLabelRoundtrip)
char small[3];
EXPECT_EQ(oaknode_node_get_label(handle, small, sizeof(small)), 5);
EXPECT_STREQ(small, "he");
oaknode_node_free(&handle);
}
TEST(NodeMetadataTest, LabelUndoableSymmetry)
{
TestNode node;
OakNodeNode *handle = as_handle(&node);
OakNodeNode handle = as_handle(&node);
EXPECT_EQ(oaknode_node_set_label(handle, "before"), OAKNODE_OK);
@@ -129,12 +133,13 @@ TEST(NodeMetadataTest, LabelUndoableSymmetry)
EXPECT_EQ(get_string(oaknode_node_get_label, handle), "before");
oakundo_command_free(&command);
oaknode_node_free(&handle);
}
TEST(NodeMetadataTest, OverrideColorLiveAndUndoable)
{
TestNode node;
OakNodeNode *handle = as_handle(&node);
OakNodeNode handle = as_handle(&node);
int color = -2;
EXPECT_EQ(oaknode_node_get_override_color(handle, &color), OAKNODE_OK);
@@ -155,12 +160,13 @@ TEST(NodeMetadataTest, OverrideColorLiveAndUndoable)
EXPECT_EQ(oaknode_node_get_override_color(handle, &color), OAKNODE_OK);
EXPECT_EQ(color, 3);
oakundo_command_free(&command);
oaknode_node_free(&handle);
}
TEST(NodeMetadataTest, EnabledLiveAndUndoable)
{
TestNode node;
OakNodeNode *handle = as_handle(&node);
OakNodeNode handle = as_handle(&node);
int enabled = 0;
EXPECT_EQ(oaknode_node_is_enabled(handle, &enabled), OAKNODE_OK);
@@ -181,12 +187,13 @@ TEST(NodeMetadataTest, EnabledLiveAndUndoable)
EXPECT_EQ(oaknode_node_is_enabled(handle, &enabled), OAKNODE_OK);
EXPECT_EQ(enabled, 0);
oakundo_command_free(&command);
oaknode_node_free(&handle);
}
TEST(NodeInputTest, EnumerateInputs)
{
TestNode node;
OakNodeNode *handle = as_handle(&node);
OakNodeNode handle = as_handle(&node);
int count = 0;
EXPECT_EQ(oaknode_node_input_count(handle, &count), OAKNODE_OK);
@@ -212,12 +219,14 @@ TEST(NodeInputTest, EnumerateInputs)
sizeof(buf)),
8);
EXPECT_STREQ(buf, "Enabled");
oaknode_node_free(&handle);
}
TEST(NodeInputTest, ValueRoundtrip)
{
TestNode node;
OakNodeNode *handle = as_handle(&node);
OakNodeNode handle = as_handle(&node);
oaknode_value out;
EXPECT_EQ(oaknode_node_get_input(handle, "float_in", &out), OAKNODE_OK);
@@ -268,15 +277,18 @@ TEST(NodeInputTest, ValueRoundtrip)
EXPECT_EQ(out.type, OAKNODE_VALUE_COLOR);
EXPECT_FLOAT_EQ(float(out.f[0]), 0.1f);
EXPECT_FLOAT_EQ(float(out.f[3]), 0.4f);
oaknode_node_free(&handle);
}
TEST(NodeInputTest, ValueErrorPaths)
{
TestNode node;
OakNodeNode *handle = as_handle(&node);
OakNodeNode handle = as_handle(&node);
OakNodeNode empty = {};
oaknode_value out;
EXPECT_EQ(oaknode_node_get_input(nullptr, "float_in", &out),
EXPECT_EQ(oaknode_node_get_input(empty, "float_in", &out),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_get_input(handle, "unknown_in", &out),
OAKNODE_E_NOT_FOUND);
@@ -289,17 +301,19 @@ TEST(NodeInputTest, ValueErrorPaths)
EXPECT_EQ(oaknode_node_set_input(handle, "int_in", &in), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_set_input(handle, "unknown_in", &in),
OAKNODE_E_NOT_FOUND);
EXPECT_EQ(oaknode_node_set_input(nullptr, "int_in", &in),
EXPECT_EQ(oaknode_node_set_input(empty, "int_in", &in),
OAKNODE_E_INVALID);
in.type = OAKNODE_VALUE_STRING;
EXPECT_EQ(oaknode_node_set_input(handle, "text_in", &in),
OAKNODE_E_INVALID);
oaknode_node_free(&handle);
}
TEST(NodeInputTest, ValueUndoableSymmetry)
{
TestNode node;
OakNodeNode *handle = as_handle(&node);
OakNodeNode handle = as_handle(&node);
oaknode_value in = make_float(9.0);
OakUndoCommand command = {};
@@ -318,12 +332,13 @@ TEST(NodeInputTest, ValueUndoableSymmetry)
EXPECT_EQ(oaknode_node_get_input(handle, "float_in", &out), OAKNODE_OK);
EXPECT_DOUBLE_EQ(out.f[0], 0.0);
oakundo_command_free(&command);
oaknode_node_free(&handle);
}
TEST(NodeInputTest, StringValueLiveAndUndoable)
{
TestNode node;
OakNodeNode *handle = as_handle(&node);
OakNodeNode handle = as_handle(&node);
char buf[64];
EXPECT_EQ(oaknode_node_get_input_string(handle, "text_in", buf,
@@ -359,14 +374,16 @@ TEST(NodeInputTest, StringValueLiveAndUndoable)
6);
EXPECT_STREQ(buf, "hello");
oakundo_command_free(&command);
oaknode_node_free(&handle);
}
TEST(NodeGraphTest, ConnectDisconnectLive)
{
TestNode source;
TestNode dest;
OakNodeNode *src = as_handle(&source);
OakNodeNode *dst = as_handle(&dest);
OakNodeNode src = as_handle(&source);
OakNodeNode dst = as_handle(&dest);
OakNodeNode empty = {};
int connected = -1;
EXPECT_EQ(oaknode_node_input_is_connected(dst, "float_in", &connected),
@@ -378,19 +395,19 @@ TEST(NodeGraphTest, ConnectDisconnectLive)
OAKNODE_OK);
EXPECT_EQ(connected, 1);
OakNodeNode *upstream = nullptr;
OakNodeNode upstream = {};
EXPECT_EQ(oaknode_node_input_get_connected_node(dst, "float_in", &upstream),
OAKNODE_OK);
EXPECT_EQ(upstream, src);
EXPECT_TRUE(same_node(upstream, &source));
// Output side enumeration.
int count = 0;
EXPECT_EQ(oaknode_node_output_connection_count(src, &count), OAKNODE_OK);
EXPECT_EQ(count, 1);
OakNodeNode *downstream = nullptr;
OakNodeNode downstream = {};
EXPECT_EQ(oaknode_node_output_connection_node_at(src, 0, &downstream),
OAKNODE_OK);
EXPECT_EQ(downstream, dst);
EXPECT_TRUE(same_node(downstream, &dest));
char buf[64];
EXPECT_EQ(oaknode_node_output_connection_input_id_at(src, 0, buf,
sizeof(buf)),
@@ -414,15 +431,20 @@ TEST(NodeGraphTest, ConnectDisconnectLive)
OAKNODE_OK);
EXPECT_EQ(connected, 0);
EXPECT_EQ(oaknode_node_disconnect(dst, "float_in"), OAKNODE_E_NOT_FOUND);
EXPECT_EQ(oaknode_node_disconnect(nullptr, "float_in"), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_disconnect(empty, "float_in"), OAKNODE_E_INVALID);
oaknode_node_free(&downstream);
oaknode_node_free(&upstream);
oaknode_node_free(&dst);
oaknode_node_free(&src);
}
TEST(NodeGraphTest, ConnectDisconnectUndoable)
{
TestNode source;
TestNode dest;
OakNodeNode *src = as_handle(&source);
OakNodeNode *dst = as_handle(&dest);
OakNodeNode src = as_handle(&source);
OakNodeNode dst = as_handle(&dest);
OakUndoCommand add = {};
EXPECT_EQ(oaknode_node_connect_undoable(src, dst, "float_in", &add),
@@ -460,14 +482,17 @@ TEST(NodeGraphTest, ConnectDisconnectUndoable)
oakundo_command_free(&remove);
oakundo_command_free(&add);
oaknode_node_free(&dst);
oaknode_node_free(&src);
}
TEST(NodeLinkTest, LinkUnlinkLiveAndUndoable)
{
TestNode a_node;
TestNode b_node;
OakNodeNode *a = as_handle(&a_node);
OakNodeNode *b = as_handle(&b_node);
OakNodeNode a = as_handle(&a_node);
OakNodeNode b = as_handle(&b_node);
OakNodeNode empty = {};
int linked = -1;
EXPECT_EQ(oaknode_node_are_linked(a, b, &linked), OAKNODE_OK);
@@ -482,9 +507,9 @@ TEST(NodeLinkTest, LinkUnlinkLiveAndUndoable)
int count = 0;
EXPECT_EQ(oaknode_node_link_count(a, &count), OAKNODE_OK);
EXPECT_EQ(count, 1);
OakNodeNode *other = nullptr;
OakNodeNode other = {};
EXPECT_EQ(oaknode_node_link_at(a, 0, &other), OAKNODE_OK);
EXPECT_EQ(other, b);
EXPECT_TRUE(same_node(other, &b_node));
EXPECT_EQ(oaknode_node_link_at(a, 1, &other), OAKNODE_E_NOT_FOUND);
EXPECT_EQ(oaknode_node_unlink(a, b, &done), OAKNODE_OK);
@@ -503,15 +528,19 @@ TEST(NodeLinkTest, LinkUnlinkLiveAndUndoable)
EXPECT_EQ(linked, 0);
oakundo_command_free(&command);
EXPECT_EQ(oaknode_node_link(nullptr, b, &done), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_link(empty, b, &done), OAKNODE_E_INVALID);
oaknode_node_free(&other);
oaknode_node_free(&b);
oaknode_node_free(&a);
}
TEST(NodeContextTest, PositionsLiveAndUndoable)
{
TestNode node;
TestNode context;
OakNodeNode *n = as_handle(&node);
OakNodeNode *ctx = as_handle(&context);
OakNodeNode n = as_handle(&node);
OakNodeNode ctx = as_handle(&context);
double x = 0.0, y = 0.0;
int expanded = -1;
@@ -529,9 +558,9 @@ TEST(NodeContextTest, PositionsLiveAndUndoable)
int count = 0;
EXPECT_EQ(oaknode_node_context_count(n, &count), OAKNODE_OK);
EXPECT_EQ(count, 1);
OakNodeNode *entry = nullptr;
OakNodeNode entry = {};
EXPECT_EQ(oaknode_node_context_node_at(n, 0, &entry), OAKNODE_OK);
EXPECT_EQ(entry, ctx);
EXPECT_TRUE(same_node(entry, &context));
EXPECT_EQ(oaknode_node_context_node_at(n, 1, &entry), OAKNODE_E_NOT_FOUND);
OakUndoCommand command = {};
@@ -555,46 +584,54 @@ TEST(NodeContextTest, PositionsLiveAndUndoable)
EXPECT_EQ(oaknode_node_remove_from_context(n, ctx), OAKNODE_E_NOT_FOUND);
EXPECT_EQ(oaknode_node_context_count(n, &count), OAKNODE_OK);
EXPECT_EQ(count, 0);
oaknode_node_free(&entry);
oaknode_node_free(&ctx);
oaknode_node_free(&n);
}
TEST(NodeLifetimeTest, CopyAndFree)
{
TestNode node;
OakNodeNode handle = as_handle(&node);
OakNodeNode empty = {};
oaknode_value in = make_float(5.0);
EXPECT_EQ(oaknode_node_set_input(as_handle(&node), "float_in", &in),
OAKNODE_OK);
EXPECT_EQ(oaknode_node_set_input(handle, "float_in", &in), OAKNODE_OK);
int alive_before = oaknode_debug_alive_count();
OakNodeNode *copy = oaknode_node_create_copy(as_handle(&node));
ASSERT_NE(copy, nullptr);
OakNodeNode copy = oaknode_node_create_copy(handle);
ASSERT_NE(copy.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1);
// copy() clones the type, not the values.
EXPECT_EQ(get_string(oaknode_node_get_id, copy), TestNode::k_id);
oaknode_node_free(copy);
oaknode_node_free(&copy);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before);
oaknode_node_free(nullptr); // no crash
EXPECT_EQ(oaknode_node_create_copy(nullptr), nullptr);
EXPECT_EQ(oaknode_node_create_copy(empty).ctx, nullptr);
oaknode_node_free(&handle);
}
TEST(NodeHandleTest, NullHandleErrors)
{
OakNodeNode empty = {};
char buf[16];
int value = 0;
EXPECT_EQ(oaknode_node_get_id(nullptr, buf, sizeof(buf)),
EXPECT_EQ(oaknode_node_get_id(empty, buf, sizeof(buf)),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_set_label(nullptr, "x"), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_get_override_color(nullptr, &value),
EXPECT_EQ(oaknode_node_set_label(empty, "x"), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_get_override_color(empty, &value),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_is_enabled(nullptr, &value), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_input_count(nullptr, &value), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_are_linked(nullptr, nullptr, &value),
EXPECT_EQ(oaknode_node_is_enabled(empty, &value), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_input_count(empty, &value), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_are_linked(empty, empty, &value),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_context_count(nullptr, &value), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_output_connection_count(nullptr, &value),
EXPECT_EQ(oaknode_node_context_count(empty, &value), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_node_output_connection_count(empty, &value),
OAKNODE_E_INVALID);
}
+79 -57
View File
@@ -25,16 +25,19 @@
#include <vector>
#include "node/folder.h"
#include "node/node.h"
#include "node/project.h"
#include "../c_api/nodehandle.h"
namespace
{
/**
* @brief Two-stage string getter helper: query, then fetch.
*/
std::string get_string(int (*fn)(const OakNodeProject *, char *, int),
const OakNodeProject *project)
std::string get_string(int (*fn)(OakNodeProject, char *, int),
OakNodeProject project)
{
int required = fn(project, nullptr, 0);
if (required <= 0) {
@@ -45,23 +48,33 @@ std::string get_string(int (*fn)(const OakNodeProject *, char *, int),
return std::string(buf.data());
}
/**
* @brief Identity compare for value handles (each handle has its own
* control block, so compare the wrapped objects).
*/
bool same_object(OakNodeNode a, OakNodeNode b)
{
return oaknode_c_api::to_native<void>(a) == oaknode_c_api::to_native<void>(b);
}
} // namespace
TEST(NodeProject, InitAndFree)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
// No root folder before initialize()
EXPECT_EQ(oaknode_project_root(project), nullptr);
EXPECT_EQ(oaknode_project_root(project).ctx, nullptr);
EXPECT_EQ(oaknode_project_initialize(project), OAKNODE_OK);
EXPECT_NE(oaknode_project_root(project), nullptr);
EXPECT_NE(oaknode_project_root(project).ctx, nullptr);
// Initializing twice is a state error
EXPECT_EQ(oaknode_project_initialize(project), OAKNODE_E_STATE);
oaknode_project_free(project);
oaknode_project_free(&project);
EXPECT_EQ(project.ctx, nullptr);
// NULL free is a no-op (must not crash)
oaknode_project_free(nullptr);
@@ -69,39 +82,45 @@ TEST(NodeProject, InitAndFree)
TEST(NodeProject, NullHandleErrors)
{
EXPECT_EQ(oaknode_project_initialize(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_clear(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_root(nullptr), nullptr);
EXPECT_EQ(oaknode_project_name(nullptr, nullptr, 0), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_filename(nullptr, nullptr, 0),
EXPECT_EQ(oaknode_project_initialize(OakNodeProject{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_pretty_filename(nullptr, nullptr, 0),
EXPECT_EQ(oaknode_project_clear(OakNodeProject{}), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_root(OakNodeProject{}).ctx, nullptr);
EXPECT_EQ(oaknode_project_name(OakNodeProject{}, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_set_filename(nullptr, "/tmp/x.ove"),
EXPECT_EQ(oaknode_project_filename(OakNodeProject{}, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_is_modified(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_set_modified(nullptr, 1), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_is_new(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_cache_path(nullptr, nullptr, 0),
EXPECT_EQ(oaknode_project_pretty_filename(OakNodeProject{}, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_get_cache_location_setting(nullptr),
EXPECT_EQ(oaknode_project_set_filename(OakNodeProject{}, "/tmp/x.ove"),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_set_cache_location_setting(nullptr, 0),
EXPECT_EQ(oaknode_project_is_modified(OakNodeProject{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_get_custom_cache_path(nullptr, nullptr, 0),
EXPECT_EQ(oaknode_project_set_modified(OakNodeProject{}, 1),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_set_custom_cache_path(nullptr, "/tmp"),
EXPECT_EQ(oaknode_project_is_new(OakNodeProject{}), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_cache_path(OakNodeProject{}, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_get_uuid(nullptr, nullptr, 0),
EXPECT_EQ(oaknode_project_get_cache_location_setting(OakNodeProject{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_node_count(nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_node_at(nullptr, 0), nullptr);
EXPECT_EQ(oaknode_project_set_cache_location_setting(OakNodeProject{}, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_get_custom_cache_path(OakNodeProject{}, nullptr,
0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_set_custom_cache_path(OakNodeProject{}, "/tmp"),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_get_uuid(OakNodeProject{}, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_node_count(OakNodeProject{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_node_at(OakNodeProject{}, 0).ctx, nullptr);
}
TEST(NodeProject, NameAndFilename)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
// Untitled project
EXPECT_EQ(get_string(oaknode_project_name, project), "(untitled)");
@@ -121,13 +140,13 @@ TEST(NodeProject, NameAndFilename)
EXPECT_EQ(oaknode_project_set_filename(project, nullptr),
OAKNODE_E_INVALID);
oaknode_project_free(project);
oaknode_project_free(&project);
}
TEST(NodeProject, ModifiedAndIsNew)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
EXPECT_EQ(oaknode_project_is_modified(project), 0);
EXPECT_EQ(oaknode_project_is_new(project), 1);
@@ -144,13 +163,13 @@ TEST(NodeProject, ModifiedAndIsNew)
OAKNODE_OK);
EXPECT_EQ(oaknode_project_is_new(project), 0);
oaknode_project_free(project);
oaknode_project_free(&project);
}
TEST(NodeProject, CachePathSettings)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
// Default location setting
EXPECT_EQ(oaknode_project_get_cache_location_setting(project), 0);
@@ -178,24 +197,24 @@ TEST(NodeProject, CachePathSettings)
OAKNODE_OK);
EXPECT_EQ(get_string(oaknode_project_get_custom_cache_path, project), "");
oaknode_project_free(project);
oaknode_project_free(&project);
}
TEST(NodeProject, Uuid)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
std::string uuid = get_string(oaknode_project_get_uuid, project);
EXPECT_FALSE(uuid.empty());
oaknode_project_free(project);
oaknode_project_free(&project);
}
TEST(NodeProject, AddRemoveNode)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK);
// The root folder is the only node after initialize()
@@ -203,17 +222,17 @@ TEST(NodeProject, AddRemoveNode)
EXPECT_EQ(base_count, 1);
// folder_create already adds the node to the graph
OakNodeFolder *folder = oaknode_folder_create(project);
ASSERT_NE(folder, nullptr);
OakNodeFolder folder = oaknode_folder_create(project);
ASSERT_NE(folder.ctx, nullptr);
EXPECT_EQ(oaknode_project_node_count(project), base_count + 1);
// node_at returns the same node; out-of-range yields NULL
OakNodeNode *as_node = nullptr;
// node_at returns the same node; out-of-range yields an empty handle
OakNodeNode as_node = {};
bool found = false;
for (int i = 0; i < oaknode_project_node_count(project); i++) {
OakNodeNode *n = oaknode_project_node_at(project, i);
ASSERT_NE(n, nullptr);
if (n == reinterpret_cast<OakNodeNode *>(folder)) {
OakNodeNode n = oaknode_project_node_at(project, i);
ASSERT_NE(n.ctx, nullptr);
if (same_object(n, oaknode_folder_as_node(folder))) {
found = true;
}
if (i == 0) {
@@ -221,13 +240,15 @@ TEST(NodeProject, AddRemoveNode)
}
}
EXPECT_TRUE(found);
EXPECT_EQ(oaknode_project_node_at(project, -1), nullptr);
EXPECT_NE(as_node.ctx, nullptr);
EXPECT_EQ(oaknode_project_node_at(project, -1).ctx, nullptr);
EXPECT_EQ(oaknode_project_node_at(project,
oaknode_project_node_count(project)),
oaknode_project_node_count(project))
.ctx,
nullptr);
// Remove detaches without deleting
OakNodeNode *folder_node = reinterpret_cast<OakNodeNode *>(folder);
OakNodeNode folder_node = oaknode_folder_as_node(folder);
EXPECT_EQ(oaknode_project_remove_node(project, folder_node), OAKNODE_OK);
EXPECT_EQ(oaknode_project_node_count(project), base_count);
@@ -239,20 +260,21 @@ TEST(NodeProject, AddRemoveNode)
EXPECT_EQ(oaknode_project_add_node(project, folder_node), OAKNODE_OK);
EXPECT_EQ(oaknode_project_node_count(project), base_count + 1);
// NULL args
EXPECT_EQ(oaknode_project_add_node(project, nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_remove_node(project, nullptr),
// Empty handle args
EXPECT_EQ(oaknode_project_add_node(project, OakNodeNode{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_project_remove_node(project, OakNodeNode{}),
OAKNODE_E_INVALID);
oaknode_project_free(project);
oaknode_project_free(&project);
}
TEST(NodeProject, Clear)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK);
ASSERT_NE(oaknode_folder_create(project), nullptr);
ASSERT_NE(oaknode_folder_create(project).ctx, nullptr);
EXPECT_GE(oaknode_project_node_count(project), 2);
EXPECT_EQ(oaknode_project_clear(project), OAKNODE_OK);
@@ -262,5 +284,5 @@ TEST(NodeProject, Clear)
EXPECT_EQ(oaknode_project_initialize(project), OAKNODE_OK);
EXPECT_GE(oaknode_project_node_count(project), 1);
oaknode_project_free(project);
oaknode_project_free(&project);
}
+64 -50
View File
@@ -43,39 +43,41 @@ void expect_rational(int num, int den, int expected_num, int expected_den)
TEST(SequenceTest, CreateFree)
{
int base = oaknode_debug_alive_count();
OakNodeSequence *seq = oaknode_sequence_create();
ASSERT_NE(seq, nullptr);
OakNodeSequence seq = oaknode_sequence_create();
ASSERT_NE(seq.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), base + 1);
oaknode_sequence_free(seq);
oaknode_sequence_free(&seq);
EXPECT_EQ(oaknode_debug_alive_count(), base);
EXPECT_EQ(seq.ctx, nullptr);
oaknode_sequence_free(nullptr);
}
TEST(SequenceTest, NullHandleReturnsInvalid)
{
int v;
OakNodeTrackList *list;
EXPECT_EQ(oaknode_sequence_get_track_list(nullptr, OAKNODE_TRACK_TYPE_VIDEO,
OakNodeSequence empty = {};
OakNodeTrackList list = {};
EXPECT_EQ(oaknode_sequence_get_track_list(empty, OAKNODE_TRACK_TYPE_VIDEO,
&list),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_sequence_get_track_count(nullptr, 0, &v),
EXPECT_EQ(oaknode_sequence_get_track_count(empty, 0, &v),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_sequence_get_playhead(nullptr, &v, &v), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_sequence_get_video_params(nullptr, 0, nullptr),
EXPECT_EQ(oaknode_sequence_get_playhead(empty, &v, &v), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_sequence_get_video_params(empty, 0, nullptr),
OAKNODE_E_INVALID);
}
TEST(SequenceTest, TrackLists)
{
OakNodeSequence *seq = oaknode_sequence_create();
ASSERT_NE(seq, nullptr);
OakNodeSequence seq = oaknode_sequence_create();
ASSERT_NE(seq.ctx, nullptr);
for (int type = OAKNODE_TRACK_TYPE_VIDEO; type < OAKNODE_TRACK_TYPE_COUNT;
type++) {
OakNodeTrackList *list = nullptr;
OakNodeTrackList list = {};
ASSERT_EQ(oaknode_sequence_get_track_list(seq, type, &list),
OAKNODE_OK);
ASSERT_NE(list, nullptr);
ASSERT_NE(list.ctx, nullptr);
int list_type = -1;
ASSERT_EQ(oaknode_tracklist_get_type(list, &list_type), OAKNODE_OK);
@@ -86,7 +88,7 @@ TEST(SequenceTest, TrackLists)
EXPECT_EQ(count, 0);
}
OakNodeTrackList *list;
OakNodeTrackList list = {};
EXPECT_EQ(oaknode_sequence_get_track_list(seq, OAKNODE_TRACK_TYPE_NONE,
&list),
OAKNODE_E_NOT_FOUND);
@@ -94,23 +96,26 @@ TEST(SequenceTest, TrackLists)
nullptr),
OAKNODE_E_INVALID);
oaknode_sequence_free(seq);
oaknode_sequence_free(&seq);
}
TEST(SequenceTest, AddAndRemoveTracks)
{
OakNodeSequence *seq = oaknode_sequence_create();
ASSERT_NE(seq, nullptr);
OakNodeSequence seq = oaknode_sequence_create();
ASSERT_NE(seq.ctx, nullptr);
OakNodeTrackList *video = nullptr;
OakNodeTrackList video = {};
ASSERT_EQ(oaknode_sequence_get_track_list(seq, OAKNODE_TRACK_TYPE_VIDEO,
&video),
OAKNODE_OK);
OakNodeTrack *t1 = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
OakNodeTrack *t2 = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t1, nullptr);
ASSERT_NE(t2, nullptr);
OakNodeTrack t1 = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
OakNodeTrack t2 = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t1.ctx, nullptr);
ASSERT_NE(t2.ctx, nullptr);
// Marker distinguishing t2 from t1 in the identity checks below
ASSERT_EQ(oaknode_track_set_muted(t2, 1), OAKNODE_OK);
ASSERT_EQ(oaknode_tracklist_add_track(video, t1), OAKNODE_OK);
ASSERT_EQ(oaknode_tracklist_add_track(video, t2), OAKNODE_OK);
@@ -139,18 +144,25 @@ TEST(SequenceTest, AddAndRemoveTracks)
ASSERT_EQ(oaknode_tracklist_get_array_size(video, &array_size), OAKNODE_OK);
EXPECT_EQ(array_size, 2);
// Sequence back-pointer and reference
OakNodeSequence *owner = nullptr;
// Sequence back-pointer: mutating through the borrowed handle must be
// visible through the owning sequence handle (same object)
OakNodeSequence owner = {};
ASSERT_EQ(oaknode_track_get_sequence(t1, &owner), OAKNODE_OK);
EXPECT_EQ(owner, seq);
ASSERT_NE(owner.ctx, nullptr);
ASSERT_EQ(oaknode_sequence_set_playhead(owner, 7, 3), OAKNODE_OK);
int pnum = -1, pden = -1;
ASSERT_EQ(oaknode_sequence_get_playhead(seq, &pnum, &pden), OAKNODE_OK);
expect_rational(pnum, pden, 7, 3);
// Flat cache spans all types
int all = 0;
ASSERT_EQ(oaknode_sequence_get_all_track_count(seq, &all), OAKNODE_OK);
EXPECT_EQ(all, 2);
OakNodeTrack *at = nullptr;
OakNodeTrack at = {};
int muted = -1;
ASSERT_EQ(oaknode_sequence_get_all_track_at(seq, 1, &at), OAKNODE_OK);
EXPECT_EQ(at, t2);
ASSERT_EQ(oaknode_track_get_muted(at, &muted), OAKNODE_OK);
EXPECT_EQ(muted, 1); // at is t2
EXPECT_EQ(oaknode_sequence_get_all_track_at(seq, 2, &at),
OAKNODE_E_NOT_FOUND);
@@ -158,7 +170,8 @@ TEST(SequenceTest, AddAndRemoveTracks)
ASSERT_EQ(oaknode_sequence_get_track_at(seq, OAKNODE_TRACK_TYPE_VIDEO, 0,
&at),
OAKNODE_OK);
EXPECT_EQ(at, t1);
ASSERT_EQ(oaknode_track_get_muted(at, &muted), OAKNODE_OK);
EXPECT_EQ(muted, 0); // at is t1
EXPECT_EQ(oaknode_sequence_get_track_at(seq, OAKNODE_TRACK_TYPE_AUDIO, 0,
&at),
OAKNODE_E_NOT_FOUND);
@@ -172,7 +185,8 @@ TEST(SequenceTest, AddAndRemoveTracks)
ASSERT_EQ(oaknode_sequence_get_track_at(seq, OAKNODE_TRACK_TYPE_VIDEO, 0,
&at),
OAKNODE_OK);
EXPECT_EQ(at, t2);
ASSERT_EQ(oaknode_track_get_muted(at, &muted), OAKNODE_OK);
EXPECT_EQ(muted, 1); // at is t2
ASSERT_EQ(oaknode_track_get_index(t2, &index), OAKNODE_OK);
EXPECT_EQ(index, 0);
@@ -180,26 +194,26 @@ TEST(SequenceTest, AddAndRemoveTracks)
EXPECT_EQ(oaknode_tracklist_remove_track(video, t1), OAKNODE_E_NOT_FOUND);
ASSERT_EQ(oaknode_tracklist_remove_track(video, t2), OAKNODE_OK);
oaknode_track_free(t1);
oaknode_track_free(t2);
oaknode_sequence_free(seq);
oaknode_track_free(&t1);
oaknode_track_free(&t2);
oaknode_sequence_free(&seq);
}
TEST(SequenceTest, TrackLengthFlowsIntoSequence)
{
OakNodeSequence *seq = oaknode_sequence_create();
ASSERT_NE(seq, nullptr);
OakNodeSequence seq = oaknode_sequence_create();
ASSERT_NE(seq.ctx, nullptr);
OakNodeTrackList *video = nullptr;
OakNodeTrackList video = {};
ASSERT_EQ(oaknode_sequence_get_track_list(seq, OAKNODE_TRACK_TYPE_VIDEO,
&video),
OAKNODE_OK);
OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t, nullptr);
OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t.ctx, nullptr);
ASSERT_EQ(oaknode_tracklist_add_track(video, t), OAKNODE_OK);
OakNodeBlock *gap = oaknode_block_gap_create();
ASSERT_NE(gap, nullptr);
OakNodeBlock gap = oaknode_block_gap_create();
ASSERT_NE(gap.ctx, nullptr);
ASSERT_EQ(oaknode_block_set_length_and_media_out(gap, 5, 1), OAKNODE_OK);
ASSERT_EQ(oaknode_track_append_block(t, gap), OAKNODE_OK);
@@ -215,16 +229,16 @@ TEST(SequenceTest, TrackLengthFlowsIntoSequence)
expect_rational(num, den, 5, 1);
ASSERT_EQ(oaknode_track_ripple_remove_block(t, gap), OAKNODE_OK);
oaknode_block_free(gap);
oaknode_block_free(&gap);
ASSERT_EQ(oaknode_tracklist_remove_track(video, t), OAKNODE_OK);
oaknode_track_free(t);
oaknode_sequence_free(seq);
oaknode_track_free(&t);
oaknode_sequence_free(&seq);
}
TEST(SequenceTest, Playhead)
{
OakNodeSequence *seq = oaknode_sequence_create();
ASSERT_NE(seq, nullptr);
OakNodeSequence seq = oaknode_sequence_create();
ASSERT_NE(seq.ctx, nullptr);
int num = -1, den = -1;
ASSERT_EQ(oaknode_sequence_get_playhead(seq, &num, &den), OAKNODE_OK);
@@ -233,13 +247,13 @@ TEST(SequenceTest, Playhead)
ASSERT_EQ(oaknode_sequence_get_playhead(seq, &num, &den), OAKNODE_OK);
expect_rational(num, den, 7, 2);
oaknode_sequence_free(seq);
oaknode_sequence_free(&seq);
}
TEST(SequenceTest, VideoParamsRoundTrip)
{
OakNodeSequence *seq = oaknode_sequence_create();
ASSERT_NE(seq, nullptr);
OakNodeSequence seq = oaknode_sequence_create();
ASSERT_NE(seq.ctx, nullptr);
int count = 0;
ASSERT_EQ(oaknode_sequence_get_video_stream_count(seq, &count),
@@ -284,13 +298,13 @@ TEST(SequenceTest, VideoParamsRoundTrip)
expect_rational(tb_num, tb_den, 1, 25);
oakcommon_videoparams_free(&readback);
oaknode_sequence_free(seq);
oaknode_sequence_free(&seq);
}
TEST(SequenceTest, AudioParamsRoundTrip)
{
OakNodeSequence *seq = oaknode_sequence_create();
ASSERT_NE(seq, nullptr);
OakNodeSequence seq = oaknode_sequence_create();
ASSERT_NE(seq.ctx, nullptr);
int count = 0;
ASSERT_EQ(oaknode_sequence_get_audio_stream_count(seq, &count),
@@ -316,5 +330,5 @@ TEST(SequenceTest, AudioParamsRoundTrip)
EXPECT_EQ(oaknode_sequence_set_audio_params(seq, 0, nullptr),
OAKNODE_E_INVALID);
oaknode_sequence_free(seq);
oaknode_sequence_free(&seq);
}
+64 -54
View File
@@ -33,7 +33,7 @@ namespace
/**
* @brief Serialize `save_data` to an XML string using the two-stage getter.
*/
std::string save_to_string(OakNodeSerializerSaveData *save_data)
std::string save_to_string(OakNodeSerializerSaveData save_data)
{
int required = oaknode_serializer_save_to_xml(save_data, nullptr, 0);
if (required <= 0) {
@@ -53,12 +53,12 @@ TEST(NodeSerializer, SaveWithoutInitializeFails)
{
oaknode_serializer_shutdown();
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
OakNodeSerializerSaveData *save_data = oaknode_serializer_savedata_create(
OakNodeSerializerSaveData save_data = oaknode_serializer_savedata_create(
OAKNODE_SERIALIZER_LOAD_ONLY_NODES, project);
ASSERT_NE(save_data, nullptr);
ASSERT_NE(save_data.ctx, nullptr);
EXPECT_EQ(oaknode_serializer_save_to_xml(save_data, nullptr, 0),
OAKNODE_E_STATE);
@@ -68,8 +68,8 @@ TEST(NodeSerializer, SaveWithoutInitializeFails)
&result, nullptr, nullptr, 0),
OAKNODE_E_STATE);
oaknode_serializer_savedata_free(save_data);
oaknode_project_free(project);
oaknode_serializer_savedata_free(&save_data);
oaknode_project_free(&project);
}
TEST(NodeSerializer, InitializeIsIdempotent)
@@ -83,22 +83,21 @@ TEST(NodeSerializer, NodeGraphCopyPasteRoundTrip)
ASSERT_EQ(oaknode_serializer_initialize(), OAKNODE_OK);
// Source project with a folder under the root
OakNodeProject *source = oaknode_project_init();
ASSERT_NE(source, nullptr);
OakNodeProject source = oaknode_project_init();
ASSERT_NE(source.ctx, nullptr);
ASSERT_EQ(oaknode_project_initialize(source), OAKNODE_OK);
OakNodeFolder *root = oaknode_project_root(source);
OakNodeFolder *folder = oaknode_folder_create(source);
ASSERT_NE(root, nullptr);
ASSERT_NE(folder, nullptr);
ASSERT_EQ(oaknode_folder_add_child(
root, reinterpret_cast<OakNodeNode *>(folder)),
OakNodeFolder root = oaknode_project_root(source);
OakNodeFolder folder = oaknode_folder_create(source);
ASSERT_NE(root.ctx, nullptr);
ASSERT_NE(folder.ctx, nullptr);
ASSERT_EQ(oaknode_folder_add_child(root, oaknode_folder_as_node(folder)),
OAKNODE_OK);
// "Copy": serialize the folder with a custom property
OakNodeSerializerSaveData *save_data = oaknode_serializer_savedata_create(
OakNodeSerializerSaveData save_data = oaknode_serializer_savedata_create(
OAKNODE_SERIALIZER_LOAD_ONLY_NODES, source);
ASSERT_NE(save_data, nullptr);
OakNodeNode *nodes[] = { reinterpret_cast<OakNodeNode *>(folder) };
ASSERT_NE(save_data.ctx, nullptr);
OakNodeNode nodes[] = { oaknode_folder_as_node(folder) };
EXPECT_EQ(oaknode_serializer_savedata_set_nodes(save_data, nodes, 1),
OAKNODE_OK);
EXPECT_EQ(oaknode_serializer_savedata_set_property(
@@ -108,29 +107,29 @@ TEST(NodeSerializer, NodeGraphCopyPasteRoundTrip)
std::string xml = save_to_string(save_data);
EXPECT_FALSE(xml.empty());
EXPECT_NE(xml.find("<olive"), std::string::npos);
oaknode_serializer_savedata_free(save_data);
oaknode_serializer_savedata_free(&save_data);
// "Paste": load the XML into a different project
OakNodeProject *target = oaknode_project_init();
ASSERT_NE(target, nullptr);
OakNodeProject target = oaknode_project_init();
ASSERT_NE(target.ctx, nullptr);
int result = -1;
OakNodeSerializerLoadData *load_data = nullptr;
OakNodeSerializerLoadData load_data = {};
char details[256];
EXPECT_EQ(oaknode_serializer_load_from_xml(
target, xml.c_str(), OAKNODE_SERIALIZER_LOAD_ONLY_NODES,
&result, &load_data, details, sizeof(details)),
OAKNODE_OK);
EXPECT_EQ(result, OAKNODE_SERIALIZER_OK) << details;
ASSERT_NE(load_data, nullptr);
ASSERT_NE(load_data.ctx, nullptr);
EXPECT_GE(oaknode_serializer_loaddata_node_count(load_data), 1);
OakNodeNode *loaded = oaknode_serializer_loaddata_node_at(load_data, 0);
ASSERT_NE(loaded, nullptr);
EXPECT_EQ(oaknode_serializer_loaddata_node_at(load_data, -1), nullptr);
OakNodeNode loaded = oaknode_serializer_loaddata_node_at(load_data, 0);
ASSERT_NE(loaded.ctx, nullptr);
EXPECT_EQ(oaknode_serializer_loaddata_node_at(load_data, -1).ctx, nullptr);
EXPECT_EQ(oaknode_serializer_loaddata_node_at(
load_data,
oaknode_serializer_loaddata_node_count(load_data)),
oaknode_serializer_loaddata_node_count(load_data)).ctx,
nullptr);
// The property rides along, remapped to the new node
@@ -162,26 +161,26 @@ TEST(NodeSerializer, NodeGraphCopyPasteRoundTrip)
OAKNODE_OK);
}
oaknode_serializer_loaddata_free(load_data);
oaknode_serializer_loaddata_free(&load_data);
oaknode_serializer_loaddata_free(nullptr); // NULL free is a no-op
// Detach the source hierarchy before teardown (see the folder tests'
// detach_all note about Project::clear()).
EXPECT_EQ(oaknode_folder_remove_child(
root, reinterpret_cast<OakNodeNode *>(folder)),
EXPECT_EQ(oaknode_folder_remove_child(root,
oaknode_folder_as_node(folder)),
OAKNODE_OK);
oaknode_project_free(target);
oaknode_project_free(source);
oaknode_project_free(&target);
oaknode_project_free(&source);
}
TEST(NodeSerializer, LoadInvalidXmlReportsSerializerError)
{
ASSERT_EQ(oaknode_serializer_initialize(), OAKNODE_OK);
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
int result = -1;
OakNodeSerializerLoadData *load_data = nullptr;
OakNodeSerializerLoadData load_data = {};
char details[256] = { 0 };
EXPECT_EQ(oaknode_serializer_load_from_xml(
project, "this is not xml",
@@ -190,9 +189,9 @@ TEST(NodeSerializer, LoadInvalidXmlReportsSerializerError)
OAKNODE_OK);
// Not an oak document: the format version cannot be determined
EXPECT_EQ(result, OAKNODE_SERIALIZER_UNKNOWN_VERSION);
EXPECT_EQ(load_data, nullptr);
EXPECT_EQ(load_data.ctx, nullptr);
oaknode_project_free(project);
oaknode_project_free(&project);
}
TEST(NodeSerializer, NullAndInvalidArgs)
@@ -201,37 +200,48 @@ TEST(NodeSerializer, NullAndInvalidArgs)
int result = -1;
EXPECT_EQ(oaknode_serializer_savedata_create(-1, nullptr), nullptr);
EXPECT_EQ(oaknode_serializer_savedata_create(99, nullptr), nullptr);
EXPECT_EQ(oaknode_serializer_savedata_create(-1, OakNodeProject{}).ctx,
nullptr);
EXPECT_EQ(oaknode_serializer_savedata_create(99, OakNodeProject{}).ctx,
nullptr);
EXPECT_EQ(oaknode_serializer_savedata_set_nodes(nullptr, nullptr, 0),
EXPECT_EQ(oaknode_serializer_savedata_set_nodes(
OakNodeSerializerSaveData{}, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_serializer_savedata_set_property(nullptr, nullptr,
nullptr, nullptr),
EXPECT_EQ(oaknode_serializer_savedata_set_property(
OakNodeSerializerSaveData{}, OakNodeNode{}, nullptr, nullptr),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_serializer_save_to_xml(nullptr, nullptr, 0),
EXPECT_EQ(oaknode_serializer_save_to_xml(OakNodeSerializerSaveData{},
nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_serializer_load_from_xml(nullptr, nullptr, 1, &result,
nullptr, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_serializer_load_from_xml(nullptr, "<olive/>", 99,
EXPECT_EQ(oaknode_serializer_load_from_xml(OakNodeProject{}, nullptr, 1,
&result, nullptr, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_serializer_load_from_xml(nullptr, "<olive/>", 1,
EXPECT_EQ(oaknode_serializer_load_from_xml(OakNodeProject{}, "<olive/>", 99,
&result, nullptr, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_serializer_load_from_xml(OakNodeProject{}, "<olive/>", 1,
nullptr, nullptr, nullptr, 0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_serializer_loaddata_node_count(nullptr),
EXPECT_EQ(oaknode_serializer_loaddata_node_count(
OakNodeSerializerLoadData{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_serializer_loaddata_node_at(nullptr, 0), nullptr);
EXPECT_EQ(oaknode_serializer_loaddata_get_property(nullptr, nullptr,
nullptr, nullptr, 0),
EXPECT_EQ(oaknode_serializer_loaddata_node_at(OakNodeSerializerLoadData{},
0)
.ctx,
nullptr);
EXPECT_EQ(oaknode_serializer_loaddata_get_property(
OakNodeSerializerLoadData{}, OakNodeNode{}, nullptr, nullptr,
0),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_serializer_loaddata_connection_count(nullptr),
EXPECT_EQ(oaknode_serializer_loaddata_connection_count(
OakNodeSerializerLoadData{}),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_serializer_loaddata_connection_at(
nullptr, 0, nullptr, nullptr, nullptr, 0, nullptr),
OakNodeSerializerLoadData{}, 0, nullptr, nullptr, nullptr, 0,
nullptr),
OAKNODE_E_INVALID);
}
+17 -2
View File
@@ -25,6 +25,7 @@
// one input per POD-carrying value type, so the C API tests can exercise
// value mapping without depending on NodeFactory.
#include "../c_api/nodehandle.h"
#include "../src/node.h"
namespace oaknode_test
@@ -73,9 +74,23 @@ public:
inline const char *TestNode::k_id = "org.oak.TestNode";
inline OakNodeNode *as_handle(olive::Node *node)
/**
* @brief Borrowed handle to a test-owned node; releasing it only releases
* the handle, never the node.
*/
inline OakNodeNode as_handle(olive::Node *node)
{
return reinterpret_cast<OakNodeNode *>(node);
return oaknode_c_api::make_handle<OakNodeNode>(
node, false, &oaknode_c_api::delete_as<olive::Node>);
}
/**
* @brief Identity comparison for handles: two handles refer to the same
* node when they wrap the same native object.
*/
inline bool same_node(OakNodeNode a, olive::Node *node)
{
return oaknode_c_api::to_native<olive::Node>(a) == node;
}
}
+119 -78
View File
@@ -22,15 +22,16 @@
#include "node/block.h"
#include "node/error.h"
#include "node/sequence.h"
#include "node/track.h"
namespace
{
OakNodeBlock *make_gap(int length_num, int length_den)
OakNodeBlock make_gap(int length_num, int length_den)
{
OakNodeBlock *gap = oaknode_block_gap_create();
EXPECT_NE(gap, nullptr);
OakNodeBlock gap = oaknode_block_gap_create();
EXPECT_NE(gap.ctx, nullptr);
EXPECT_EQ(oaknode_block_set_length_and_media_out(gap, length_num,
length_den),
OAKNODE_OK);
@@ -43,19 +44,40 @@ void expect_rational(int num, int den, int expected_num, int expected_den)
EXPECT_EQ(den, expected_den);
}
/**
* @brief Identity probe: block handles are distinct boxes per accessor
* call, so "is the same object" is checked through per-object state
* (length / in point / enabled) instead of pointer equality.
*/
void expect_block_length(OakNodeBlock block, int expected_num,
int expected_den)
{
int num = -1, den = -1;
ASSERT_EQ(oaknode_block_get_length(block, &num, &den), OAKNODE_OK);
expect_rational(num, den, expected_num, expected_den);
}
void expect_block_in(OakNodeBlock block, int expected_num, int expected_den)
{
int num = -1, den = -1;
ASSERT_EQ(oaknode_block_get_in(block, &num, &den), OAKNODE_OK);
expect_rational(num, den, expected_num, expected_den);
}
} // namespace
TEST(TrackTest, CreateFree)
{
OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t, nullptr);
OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t.ctx, nullptr);
int type = -1;
ASSERT_EQ(oaknode_track_get_type(t, &type), OAKNODE_OK);
EXPECT_EQ(type, OAKNODE_TRACK_TYPE_VIDEO);
oaknode_track_free(t);
oaknode_track_free(&t);
EXPECT_EQ(t.ctx, nullptr);
EXPECT_EQ(oaknode_track_create(OAKNODE_TRACK_TYPE_NONE), nullptr);
EXPECT_EQ(oaknode_track_create(OAKNODE_TRACK_TYPE_COUNT), nullptr);
EXPECT_EQ(oaknode_track_create(OAKNODE_TRACK_TYPE_NONE).ctx, nullptr);
EXPECT_EQ(oaknode_track_create(OAKNODE_TRACK_TYPE_COUNT).ctx, nullptr);
oaknode_track_free(nullptr);
}
@@ -63,20 +85,24 @@ TEST(TrackTest, NullHandleReturnsInvalid)
{
int v;
double d;
EXPECT_EQ(oaknode_track_get_type(nullptr, &v), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_track_set_type(nullptr, OAKNODE_TRACK_TYPE_VIDEO),
OakNodeTrack empty_track = {};
OakNodeBlock empty_block = {};
OakNodeTrackList empty_list = {};
EXPECT_EQ(oaknode_track_get_type(empty_track, &v), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_track_set_type(empty_track, OAKNODE_TRACK_TYPE_VIDEO),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_track_get_height(nullptr, &d), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_track_set_muted(nullptr, 1), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_track_append_block(nullptr, nullptr), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_tracklist_get_track_count(nullptr, &v),
EXPECT_EQ(oaknode_track_get_height(empty_track, &d), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_track_set_muted(empty_track, 1), OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_track_append_block(empty_track, empty_block),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_tracklist_get_track_count(empty_list, &v),
OAKNODE_E_INVALID);
}
TEST(TrackTest, TypeHeightIndexFlags)
{
OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_AUDIO);
ASSERT_NE(t, nullptr);
OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_AUDIO);
ASSERT_NE(t.ctx, nullptr);
ASSERT_EQ(oaknode_track_set_type(t, OAKNODE_TRACK_TYPE_SUBTITLE),
OAKNODE_OK);
@@ -122,24 +148,25 @@ TEST(TrackTest, TypeHeightIndexFlags)
EXPECT_EQ(flag, 1);
// Fresh track has no sequence
OakNodeSequence *seq = reinterpret_cast<OakNodeSequence *>(0x1);
OakNodeSequence seq = {};
seq.ctx = reinterpret_cast<void *>(0x1);
ASSERT_EQ(oaknode_track_get_sequence(t, &seq), OAKNODE_OK);
EXPECT_EQ(seq, nullptr);
EXPECT_EQ(seq.ctx, nullptr);
oaknode_track_free(t);
oaknode_track_free(&t);
}
TEST(TrackTest, AppendAndQueryBlocks)
{
OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t, nullptr);
OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t.ctx, nullptr);
int count = -1;
ASSERT_EQ(oaknode_track_get_block_count(t, &count), OAKNODE_OK);
EXPECT_EQ(count, 0);
OakNodeBlock *a = make_gap(1, 1);
OakNodeBlock *b = make_gap(2, 1);
OakNodeBlock a = make_gap(1, 1);
OakNodeBlock b = make_gap(2, 1);
ASSERT_EQ(oaknode_track_append_block(t, a), OAKNODE_OK);
ASSERT_EQ(oaknode_track_append_block(t, b), OAKNODE_OK);
@@ -155,15 +182,22 @@ TEST(TrackTest, AppendAndQueryBlocks)
ASSERT_EQ(oaknode_block_get_out(b, &num, &den), OAKNODE_OK);
expect_rational(num, den, 3, 1);
// Adjacency and back-pointer
OakNodeBlock *nb = nullptr;
// Adjacency (identified by their distinct lengths: a = 1, b = 2)
OakNodeBlock nb = {};
ASSERT_EQ(oaknode_block_get_next(a, &nb), OAKNODE_OK);
EXPECT_EQ(nb, b);
expect_block_length(nb, 2, 1); // nb is b
ASSERT_EQ(oaknode_block_get_previous(b, &nb), OAKNODE_OK);
EXPECT_EQ(nb, a);
OakNodeTrack *owner = nullptr;
expect_block_length(nb, 1, 1); // nb is a
// Back-pointer: mutating through the borrowed track handle must be
// visible through the owning track handle (same object)
OakNodeTrack owner = {};
ASSERT_EQ(oaknode_block_get_track(a, &owner), OAKNODE_OK);
EXPECT_EQ(owner, t);
ASSERT_NE(owner.ctx, nullptr);
ASSERT_EQ(oaknode_track_set_muted(owner, 1), OAKNODE_OK);
int muted = -1;
ASSERT_EQ(oaknode_track_get_muted(t, &muted), OAKNODE_OK);
EXPECT_EQ(muted, 1);
// Length of the whole track
ASSERT_EQ(oaknode_track_get_length(t, &num, &den), OAKNODE_OK);
@@ -174,17 +208,17 @@ TEST(TrackTest, AppendAndQueryBlocks)
ASSERT_EQ(oaknode_track_get_block_index(t, b, &index), OAKNODE_OK);
EXPECT_EQ(index, 1);
OakNodeBlock *at = nullptr;
OakNodeBlock at = {};
ASSERT_EQ(oaknode_track_get_block_at(t, 0, &at), OAKNODE_OK);
EXPECT_EQ(at, a);
expect_block_length(at, 1, 1); // at is a
EXPECT_EQ(oaknode_track_get_block_at(t, 2, &at), OAKNODE_E_NOT_FOUND);
ASSERT_EQ(oaknode_track_get_block_containing_time(t, 1, 2, &at),
OAKNODE_OK);
EXPECT_EQ(at, a);
expect_block_length(at, 1, 1); // at is a
ASSERT_EQ(oaknode_track_get_block_containing_time(t, 3, 2, &at),
OAKNODE_OK);
EXPECT_EQ(at, b);
expect_block_length(at, 2, 1); // at is b
// Boundary time is not "contained"
EXPECT_EQ(oaknode_track_get_block_containing_time(t, 1, 1, &at),
OAKNODE_E_NOT_FOUND);
@@ -204,20 +238,20 @@ TEST(TrackTest, AppendAndQueryBlocks)
// Tear down manually (no project graph owns these)
ASSERT_EQ(oaknode_track_ripple_remove_block(t, a), OAKNODE_OK);
ASSERT_EQ(oaknode_track_ripple_remove_block(t, b), OAKNODE_OK);
oaknode_block_free(a);
oaknode_block_free(b);
oaknode_track_free(t);
oaknode_block_free(&a);
oaknode_block_free(&b);
oaknode_track_free(&t);
}
TEST(TrackTest, PrependInsertAndRippleRemove)
{
OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t, nullptr);
OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t.ctx, nullptr);
OakNodeBlock *a = make_gap(1, 1);
OakNodeBlock *b = make_gap(1, 1);
OakNodeBlock *c = make_gap(1, 1);
OakNodeBlock *d = make_gap(1, 1);
OakNodeBlock a = make_gap(1, 1);
OakNodeBlock b = make_gap(1, 1);
OakNodeBlock c = make_gap(1, 1);
OakNodeBlock d = make_gap(1, 1);
ASSERT_EQ(oaknode_track_append_block(t, c), OAKNODE_OK);
ASSERT_EQ(oaknode_track_prepend_block(t, a), OAKNODE_OK);
@@ -228,13 +262,15 @@ TEST(TrackTest, PrependInsertAndRippleRemove)
ASSERT_EQ(oaknode_track_get_block_count(t, &count), OAKNODE_OK);
EXPECT_EQ(count, 4);
OakNodeBlock *at = nullptr;
// All gaps have equal lengths; identity is checked through the
// per-object in point (a at 0, b at 1, d at 3)
OakNodeBlock at = {};
ASSERT_EQ(oaknode_track_get_block_at(t, 0, &at), OAKNODE_OK);
EXPECT_EQ(at, a);
expect_block_in(at, 0, 1); // at is a
ASSERT_EQ(oaknode_track_get_block_at(t, 1, &at), OAKNODE_OK);
EXPECT_EQ(at, b);
expect_block_in(at, 1, 1); // at is b
ASSERT_EQ(oaknode_track_get_block_at(t, 3, &at), OAKNODE_OK);
EXPECT_EQ(at, d);
expect_block_in(at, 3, 1); // at is d
// Ripple-removing b shifts c back to t=1
ASSERT_EQ(oaknode_track_ripple_remove_block(t, b), OAKNODE_OK);
@@ -247,67 +283,72 @@ TEST(TrackTest, PrependInsertAndRippleRemove)
ASSERT_EQ(oaknode_track_ripple_remove_block(t, a), OAKNODE_OK);
ASSERT_EQ(oaknode_track_ripple_remove_block(t, c), OAKNODE_OK);
ASSERT_EQ(oaknode_track_ripple_remove_block(t, d), OAKNODE_OK);
oaknode_block_free(a);
oaknode_block_free(b);
oaknode_block_free(c);
oaknode_block_free(d);
oaknode_track_free(t);
oaknode_block_free(&a);
oaknode_block_free(&b);
oaknode_block_free(&c);
oaknode_block_free(&d);
oaknode_track_free(&t);
}
TEST(TrackTest, InsertBefore)
{
OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t, nullptr);
OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t.ctx, nullptr);
OakNodeBlock *a = make_gap(1, 1);
OakNodeBlock *b = make_gap(1, 1);
OakNodeBlock a = make_gap(1, 1);
OakNodeBlock b = make_gap(1, 1);
ASSERT_EQ(oaknode_track_append_block(t, b), OAKNODE_OK);
ASSERT_EQ(oaknode_track_insert_block_before(t, a, b), OAKNODE_OK);
OakNodeBlock *at = nullptr;
OakNodeBlock at = {};
ASSERT_EQ(oaknode_track_get_block_at(t, 0, &at), OAKNODE_OK);
EXPECT_EQ(at, a);
expect_block_in(at, 0, 1); // at is a
ASSERT_EQ(oaknode_track_ripple_remove_block(t, a), OAKNODE_OK);
ASSERT_EQ(oaknode_track_ripple_remove_block(t, b), OAKNODE_OK);
oaknode_block_free(a);
oaknode_block_free(b);
oaknode_track_free(t);
oaknode_block_free(&a);
oaknode_block_free(&b);
oaknode_track_free(&t);
}
TEST(TrackTest, ReplaceBlock)
{
OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t, nullptr);
OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t.ctx, nullptr);
OakNodeBlock old_gap = make_gap(2, 1);
OakNodeBlock new_gap = make_gap(2, 1);
// Marker distinguishing new_gap from the equal-length old_gap
ASSERT_EQ(oaknode_block_set_enabled(new_gap, 0), OAKNODE_OK);
OakNodeBlock *old_gap = make_gap(2, 1);
OakNodeBlock *new_gap = make_gap(2, 1);
ASSERT_EQ(oaknode_track_append_block(t, old_gap), OAKNODE_OK);
ASSERT_EQ(oaknode_track_replace_block(t, old_gap, new_gap), OAKNODE_OK);
int count = 0;
ASSERT_EQ(oaknode_track_get_block_count(t, &count), OAKNODE_OK);
EXPECT_EQ(count, 1);
OakNodeBlock *at = nullptr;
OakNodeBlock at = {};
ASSERT_EQ(oaknode_track_get_block_at(t, 0, &at), OAKNODE_OK);
EXPECT_EQ(at, new_gap);
int enabled = -1;
ASSERT_EQ(oaknode_block_get_enabled(at, &enabled), OAKNODE_OK);
EXPECT_EQ(enabled, 0); // at is new_gap
int num, den;
ASSERT_EQ(oaknode_track_get_length(t, &num, &den), OAKNODE_OK);
expect_rational(num, den, 2, 1);
ASSERT_EQ(oaknode_track_ripple_remove_block(t, new_gap), OAKNODE_OK);
oaknode_block_free(old_gap);
oaknode_block_free(new_gap);
oaknode_track_free(t);
oaknode_block_free(&old_gap);
oaknode_block_free(&new_gap);
oaknode_track_free(&t);
}
TEST(TrackTest, RangeOccupiedByClipIsNotFree)
{
OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t, nullptr);
OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(t.ctx, nullptr);
OakNodeBlock *clip = oaknode_block_clip_create();
ASSERT_NE(clip, nullptr);
OakNodeBlock clip = oaknode_block_clip_create();
ASSERT_NE(clip.ctx, nullptr);
ASSERT_EQ(oaknode_block_set_length_and_media_out(clip, 2, 1), OAKNODE_OK);
ASSERT_EQ(oaknode_track_append_block(t, clip), OAKNODE_OK);
@@ -320,14 +361,14 @@ TEST(TrackTest, RangeOccupiedByClipIsNotFree)
EXPECT_EQ(free_range, 1);
ASSERT_EQ(oaknode_track_ripple_remove_block(t, clip), OAKNODE_OK);
oaknode_block_free(clip);
oaknode_track_free(t);
oaknode_block_free(&clip);
oaknode_track_free(&t);
}
TEST(TrackTest, Reference)
{
OakNodeTrack *t = oaknode_track_create(OAKNODE_TRACK_TYPE_AUDIO);
ASSERT_NE(t, nullptr);
OakNodeTrack t = oaknode_track_create(OAKNODE_TRACK_TYPE_AUDIO);
ASSERT_NE(t.ctx, nullptr);
ASSERT_EQ(oaknode_track_set_index(t, 2), OAKNODE_OK);
int type = -1, index = -1;
@@ -335,5 +376,5 @@ TEST(TrackTest, Reference)
EXPECT_EQ(type, OAKNODE_TRACK_TYPE_AUDIO);
EXPECT_EQ(index, 2);
oaknode_track_free(t);
oaknode_track_free(&t);
}
+23 -19
View File
@@ -36,11 +36,12 @@ TEST(NodeTraverserTest, InitFree)
{
int alive_before = oaknode_debug_alive_count();
OakNodeTraverser *traverser = oaknode_traverser_init();
ASSERT_NE(traverser, nullptr);
OakNodeTraverser traverser = oaknode_traverser_init();
ASSERT_NE(traverser.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1);
oaknode_traverser_free(traverser);
oaknode_traverser_free(&traverser);
EXPECT_EQ(traverser.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before);
oaknode_traverser_free(nullptr); // no crash
@@ -55,16 +56,16 @@ TEST(NodeTraverserTest, GenerateDatabaseAndEnumerate)
ASSERT_EQ(oaknode_node_set_input(as_handle(&node), "float_in", &in),
OAKNODE_OK);
OakNodeTraverser *traverser = oaknode_traverser_init();
ASSERT_NE(traverser, nullptr);
OakNodeTraverser traverser = oaknode_traverser_init();
ASSERT_NE(traverser.ctx, nullptr);
int alive_before = oaknode_debug_alive_count();
OakNodeValueDatabase *db = nullptr;
OakNodeValueDatabase db = {};
EXPECT_EQ(oaknode_traverser_generate_database(traverser, as_handle(&node),
0, 1, 1, 1, &db),
OAKNODE_OK);
ASSERT_NE(db, nullptr);
ASSERT_NE(db.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before + 1);
int rows = 0;
@@ -115,28 +116,30 @@ TEST(NodeTraverserTest, GenerateDatabaseAndEnumerate)
oaknode_value out;
EXPECT_EQ(oaknode_traverser_database_value_at(db, "nope", 0, &out),
OAKNODE_E_NOT_FOUND);
EXPECT_EQ(oaknode_traverser_database_value_at(nullptr, "float_in", 0,
&out),
EXPECT_EQ(oaknode_traverser_database_value_at(OakNodeValueDatabase{},
"float_in", 0, &out),
OAKNODE_E_INVALID);
oaknode_traverser_database_free(db);
oaknode_traverser_database_free(&db);
EXPECT_EQ(db.ctx, nullptr);
EXPECT_EQ(oaknode_debug_alive_count(), alive_before);
oaknode_traverser_database_free(nullptr); // no crash
oaknode_traverser_free(traverser);
oaknode_traverser_free(&traverser);
}
TEST(NodeTraverserTest, InvalidArguments)
{
OakNodeValueDatabase *db = nullptr;
EXPECT_EQ(oaknode_traverser_generate_database(nullptr, nullptr, 0, 1, 1, 1,
OakNodeValueDatabase db = {};
EXPECT_EQ(oaknode_traverser_generate_database(OakNodeTraverser{},
OakNodeNode{}, 0, 1, 1, 1,
&db),
OAKNODE_E_INVALID);
OakNodeTraverser *traverser = oaknode_traverser_init();
ASSERT_NE(traverser, nullptr);
EXPECT_EQ(oaknode_traverser_generate_database(traverser, nullptr, 0, 1, 1,
1, &db),
OakNodeTraverser traverser = oaknode_traverser_init();
ASSERT_NE(traverser.ctx, nullptr);
EXPECT_EQ(oaknode_traverser_generate_database(traverser, OakNodeNode{}, 0,
1, 1, 1, &db),
OAKNODE_E_INVALID);
TestNode node;
@@ -144,10 +147,11 @@ TEST(NodeTraverserTest, InvalidArguments)
0, 1, 1, 1, nullptr),
OAKNODE_E_INVALID);
EXPECT_EQ(oaknode_traverser_database_row_count(nullptr, nullptr),
EXPECT_EQ(oaknode_traverser_database_row_count(OakNodeValueDatabase{},
nullptr),
OAKNODE_E_INVALID);
oaknode_traverser_free(traverser);
oaknode_traverser_free(&traverser);
}
}
+19 -13
View File
@@ -22,6 +22,7 @@
#include <new>
#include "../../node/c_api/nodehandle.h"
#include "../src/projectcopier.h"
namespace
@@ -50,36 +51,41 @@ void oakrender_project_copier_free(OakRenderProjectCopier *copier)
}
int oakrender_project_copier_set_project(OakRenderProjectCopier *copier,
OakNodeProject *project)
OakNodeProject project)
{
if (!copier || !project) {
if (!copier || !project.ctx) {
return OAKRENDER_E_INVALID;
}
try {
impl(copier)->set_project(
reinterpret_cast<olive::Project *>(project));
oaknode_c_api::to_native<olive::Project>(project));
return OAKRENDER_OK;
} catch (...) {
return OAKRENDER_E_FAILED;
}
}
OakNodeNode *oakrender_project_copier_get_copy(
OakRenderProjectCopier *copier, OakNodeNode *original)
OakNodeNode oakrender_project_copier_get_copy(
OakRenderProjectCopier *copier, OakNodeNode original)
{
if (!copier || !original) {
return NULL;
if (!copier || !original.ctx) {
return OakNodeNode{};
}
return reinterpret_cast<OakNodeNode *>(
impl(copier)->get_copy(reinterpret_cast<olive::Node *>(original)));
// Borrowed handle: releasing it only destroys the handle box, never
// the copied node (owned by the copier's copied project).
return oaknode_c_api::make_handle<OakNodeNode>(
impl(copier)->get_copy(oaknode_c_api::to_native<olive::Node>(original)),
false, &oaknode_c_api::delete_as<olive::Node>);
}
OakNodeProject *oakrender_project_copier_get_copied_project(
OakNodeProject oakrender_project_copier_get_copied_project(
OakRenderProjectCopier *copier)
{
if (!copier) {
return NULL;
return OakNodeProject{};
}
return reinterpret_cast<OakNodeProject *>(
impl(copier)->get_copied_project());
// Borrowed handle: the copied project is owned by the copier.
return oaknode_c_api::make_handle<OakNodeProject>(
impl(copier)->get_copied_project(), false,
&oaknode_c_api::delete_as<olive::Project>);
}
+9 -5
View File
@@ -30,6 +30,8 @@
#include "alivecount.h"
#include "internalhandles.h"
#include "../../node/c_api/nodehandle.h"
#include "diskmanager.h"
#include "output/viewer/viewer.h"
#include "previewautocacher.h"
@@ -75,10 +77,10 @@ void oakrender_manager_shutdown(void)
}
}
int64_t oakrender_request_frame(OakNodeNode *viewer, int64_t ts,
int64_t oakrender_request_frame(OakNodeNode viewer, int64_t ts,
oakrender_frame_ready_fn cb, void *userdata)
{
if (!viewer || !cb) {
if (!viewer.ctx || !cb) {
return OAKRENDER_E_INVALID;
}
olive::RenderManager *manager = olive::RenderManager::instance();
@@ -86,7 +88,7 @@ int64_t oakrender_request_frame(OakNodeNode *viewer, int64_t ts,
return OAKRENDER_E_STATE;
}
auto *v = dynamic_cast<olive::ViewerOutput *>(
reinterpret_cast<olive::Node *>(viewer));
oaknode_c_api::to_native<olive::Node>(viewer));
if (!v) {
return OAKRENDER_E_INVALID;
}
@@ -157,14 +159,16 @@ int oakrender_cancel_request(int64_t request_id)
return OAKRENDER_OK;
}
int oakrender_set_cacher_multicam(OakNodeNode *multicam_or_NULL)
int oakrender_set_cacher_multicam(OakNodeNode multicam_or_NULL)
{
olive::RenderManager *manager = olive::RenderManager::instance();
if (!manager) {
return OAKRENDER_E_STATE;
}
manager->get_cacher()->set_multicam_node(
reinterpret_cast<olive::MultiCamNode *>(multicam_or_NULL));
multicam_or_NULL.ctx ?
oaknode_c_api::to_native<olive::MultiCamNode>(multicam_or_NULL) :
nullptr);
return OAKRENDER_OK;
}
+10 -6
View File
@@ -26,6 +26,7 @@
#include "../src/framehashcache.h"
#include "../src/rendermanager.h"
#include "../src/renderticket.h"
#include "../../node/c_api/nodehandle.h"
#include "internalhandles.h"
namespace
@@ -59,9 +60,9 @@ OakRenderTicket *wrap(olive::RenderTicketWatcher *w,
return reinterpret_cast<OakRenderTicket *>(h);
}
olive::Node *to_node(OakNodeNode *n)
olive::Node *to_node(OakNodeNode n)
{
return reinterpret_cast<olive::Node *>(n);
return oaknode_c_api::to_native<olive::Node>(n);
}
} // namespace
@@ -70,7 +71,7 @@ OakRenderTicket *oakrender_ticket_render_frame(
const oakrender_video_ticket_params *params,
oakrender_ticket_finished_fn cb, void *userdata)
{
if (!params || !params->output_node) {
if (!params || !params->output_node.ctx) {
return NULL;
}
@@ -96,7 +97,10 @@ OakRenderTicket *oakrender_ticket_render_frame(
: olive::AudioParams(),
olive::core::Rational(int(params->time_num),
int(params->time_den)),
reinterpret_cast<olive::ColorManager *>(params->color_manager),
params->color_manager.ctx ?
oaknode_c_api::to_native<olive::ColorManager>(
params->color_manager) :
nullptr,
static_cast<olive::RenderMode::Mode>(params->mode));
rvp.force_size =
@@ -161,11 +165,11 @@ OakRenderTicket *oakrender_ticket_render_frame(
}
OakRenderTicket *oakrender_ticket_render_audio(
OakNodeNode *output_node, int64_t in_num, int64_t in_den,
OakNodeNode output_node, int64_t in_num, int64_t in_den,
int64_t out_num, int64_t out_den, const OakAudioParams *params,
int mode, oakrender_ticket_finished_fn cb, void *userdata)
{
if (!output_node || !params) {
if (!output_node.ctx || !params) {
return NULL;
}
+9 -8
View File
@@ -57,30 +57,31 @@ TEST(OakRenderManagerTest, InitShutdown)
TEST(OakRenderManagerTest, RequestFrameRequiresManager)
{
ASSERT_EQ(oaknode_factory_initialize(), OAKNODE_OK);
OakNodeNode *viewer = oaknode_factory_create_from_id(
OakNodeNode viewer = oaknode_factory_create_from_id(
"org.olivevideoeditor.Olive.vieweroutput");
ASSERT_NE(viewer, nullptr);
ASSERT_NE(viewer.ctx, nullptr);
// No oakrender_manager_init() in this process: E_STATE
EXPECT_EQ(oakrender_request_frame(viewer, 0, noop_frame_ready, nullptr),
int64_t(OAKRENDER_E_STATE));
oaknode_node_free(viewer);
oaknode_node_free(&viewer);
oaknode_factory_destroy();
}
TEST(OakRenderManagerTest, RequestFrameInvalidArgs)
{
EXPECT_EQ(oakrender_request_frame(nullptr, 0, noop_frame_ready, nullptr),
EXPECT_EQ(oakrender_request_frame(OakNodeNode{}, 0, noop_frame_ready,
nullptr),
int64_t(OAKRENDER_E_INVALID));
ASSERT_EQ(oaknode_factory_initialize(), OAKNODE_OK);
OakNodeNode *viewer = oaknode_factory_create_from_id(
OakNodeNode viewer = oaknode_factory_create_from_id(
"org.olivevideoeditor.Olive.vieweroutput");
ASSERT_NE(viewer, nullptr);
ASSERT_NE(viewer.ctx, nullptr);
EXPECT_EQ(oakrender_request_frame(viewer, 0, nullptr, nullptr),
int64_t(OAKRENDER_E_INVALID));
oaknode_node_free(viewer);
oaknode_node_free(&viewer);
oaknode_factory_destroy();
}
@@ -98,7 +99,7 @@ TEST(OakRenderManagerTest, CancelUnknownRequest)
TEST(OakRenderManagerTest, CacherSettersRequireManager)
{
EXPECT_EQ(oakrender_set_cacher_multicam(nullptr), OAKRENDER_E_STATE);
EXPECT_EQ(oakrender_set_cacher_multicam(OakNodeNode{}), OAKRENDER_E_STATE);
EXPECT_EQ(oakrender_set_display_color_processor(nullptr),
OAKRENDER_E_STATE);
}
+25 -20
View File
@@ -66,20 +66,20 @@ OakTaskTask *oaktask_create_project_load(const char *filename)
}
}
OakNodeProject *oaktask_load_take_project(OakTaskTask *t)
OakNodeProject oaktask_load_take_project(OakTaskTask *t)
{
olive::ProjectLoadBaseTask *task = load_impl(t);
if (!task) {
return NULL;
return OakNodeProject{};
}
return task->take_project();
}
OakTaskTask *oaktask_create_project_save(OakNodeProject *project,
OakTaskTask *oaktask_create_project_save(OakNodeProject project,
const char *filename_or_NULL,
int use_compression)
{
if (!project) {
if (!project.ctx) {
return NULL;
}
try {
@@ -94,12 +94,13 @@ OakTaskTask *oaktask_create_project_save(OakNodeProject *project,
}
}
OakTaskTask *oaktask_create_project_import(OakNodeFolder *folder,
OakNodeProject *project,
OakTaskTask *oaktask_create_project_import(OakNodeFolder folder,
OakNodeProject project,
const char *const *urls,
int url_count)
{
if (!folder || !project || (!urls && url_count > 0) || url_count < 0) {
if (!folder.ctx || !project.ctx || (!urls && url_count > 0) ||
url_count < 0) {
return NULL;
}
try {
@@ -136,14 +137,18 @@ int oaktask_import_footage_count(OakTaskTask *t)
return int(task->get_imported_footage().size());
}
OakNodeFootage *oaktask_import_footage_at(OakTaskTask *t, int index)
OakNodeFootage oaktask_import_footage_at(OakTaskTask *t, int index)
{
olive::ProjectImportTask *task = import_impl(t);
if (!task || index < 0 ||
index >= int(task->get_imported_footage().size())) {
return NULL;
return OakNodeFootage{};
}
return task->get_imported_footage()[size_t(index)];
OakNodeFootage footage = task->get_imported_footage()[size_t(index)];
if (footage.ctx) {
footage.addref(footage.ctx);
}
return footage;
}
int oaktask_import_invalid_count(OakTaskTask *t)
@@ -183,10 +188,10 @@ void oaktask_import_set_image_sequence_confirm_cb(
});
}
OakTaskTask *oaktask_create_precache(OakNodeFootage *footage, int index,
OakNodeSequence *sequence)
OakTaskTask *oaktask_create_precache(OakNodeFootage footage, int index,
OakNodeSequence sequence)
{
if (!footage || !sequence) {
if (!footage.ctx || !sequence.ctx) {
return NULL;
}
try {
@@ -196,11 +201,11 @@ OakTaskTask *oaktask_create_precache(OakNodeFootage *footage, int index,
}
}
OakTaskTask *oaktask_create_export(OakNodeNode *viewer,
OakNodeColorManager *color_manager,
OakTaskTask *oaktask_create_export(OakNodeNode viewer,
OakNodeColorManager color_manager,
const oakcodec_encoding_params *params)
{
if (!viewer || !params) {
if (!viewer.ctx || !params) {
return NULL;
}
try {
@@ -222,19 +227,19 @@ OakTaskTask *oaktask_create_project_load_otio(const char *filename)
}
}
OakNodeProject *oaktask_load_otio_take_project(OakTaskTask *t)
OakNodeProject oaktask_load_otio_take_project(OakTaskTask *t)
{
olive::ProjectLoadBaseTask *task = load_impl(t);
if (!task) {
return NULL;
return OakNodeProject{};
}
return task->take_project();
}
OakTaskTask *oaktask_create_project_save_otio(OakNodeProject *project,
OakTaskTask *oaktask_create_project_save_otio(OakNodeProject project,
const char *filename)
{
if (!project || !filename) {
if (!project.ctx || !filename) {
return NULL;
}
try {
+1
View File
@@ -24,6 +24,7 @@ target_include_directories(oaktask PUBLIC
${OAK_REPO_ROOT}/include
${OAK_REPO_ROOT}/src/common/src
${OAK_REPO_ROOT}/src/undo/src
${OAK_REPO_ROOT}/src/node/c_api
${OAK_REPO_ROOT}/core/include
${OAK_REPO_ROOT}/otio-install/include
)
+30 -18
View File
@@ -31,13 +31,15 @@
#include "olive/core/util/timecodefunctions.h"
#include "render/color.h"
#include "nodehandle.h"
namespace olive
{
namespace
{
std::string node_label(OakNodeNode *node)
std::string node_label(OakNodeNode node)
{
int needed = oaknode_node_get_label(node, nullptr, 0);
if (needed <= 0) {
@@ -128,13 +130,23 @@ OakFrame copy_frame_to_codec(OakCodecFrame *render_frame)
return out;
}
/**
* @brief Borrowed sequence alias of a viewer node handle (same underlying
* node; releasing the alias only frees its handle box).
*/
OakNodeSequence sequence_alias_of(OakNodeNode node)
{
return oaknode_c_api::make_handle<OakNodeSequence>(
oaknode_c_api::to_native<void>(node), false, nullptr);
}
} // namespace
ExportTask::ExportTask(OakNodeNode *viewer_node,
OakNodeColorManager *color_manager,
ExportTask::ExportTask(OakNodeNode viewer_node,
OakNodeColorManager color_manager,
const oakcodec_encoding_params &params)
: copier_(nullptr)
, color_manager_(nullptr)
, color_manager_({})
, params_(params)
, encoder_({})
, subtitle_encoder_({})
@@ -146,24 +158,26 @@ ExportTask::ExportTask(OakNodeNode *viewer_node,
(void)color_manager;
// Create a copy of the project
OakNodeProject *source_project = nullptr;
OakNodeProject source_project = {};
oaknode_node_get_project(viewer_node, &source_project);
copier_ = oakrender_project_copier_create();
if (copier_ && source_project) {
if (copier_ && source_project.ctx) {
oakrender_project_copier_set_project(copier_, source_project);
}
oaknode_project_free(&source_project);
set_viewer(oakrender_project_copier_get_copy(copier_, viewer_node));
OakNodeProject *copied_project =
OakNodeProject copied_project =
oakrender_project_copier_get_copied_project(copier_);
color_manager_ = oaknode_colormanager_init(copied_project);
oaknode_project_free(&copied_project);
// Adjust video params to have no divider
OakNodeSequence viewer_sequence = sequence_alias_of(viewer_node);
OakVideoParams vp = {};
oaknode_sequence_get_video_params(
reinterpret_cast<OakNodeSequence *>(viewer_node), 0, &vp);
oaknode_sequence_get_video_params(viewer_sequence, 0, &vp);
oakcommon_videoparams_set_divider(vp, 1);
oakcommon_videoparams_set_time_base(vp, params_.video_time_base_num,
params_.video_time_base_den);
@@ -173,10 +187,9 @@ ExportTask::ExportTask(OakNodeNode *viewer_node,
oakcommon_videoparams_free(&vp);
OakAudioParams *audio_params = nullptr;
oaknode_sequence_get_audio_params(
reinterpret_cast<OakNodeSequence *>(viewer_node), 0,
&audio_params);
oaknode_sequence_get_audio_params(viewer_sequence, 0, &audio_params);
set_audio_params(audio_params);
oaknode_sequence_free(&viewer_sequence);
set_title("Exporting \"" + node_label(viewer_node) + "\"");
set_native_progress_signalling_enabled(false);
@@ -193,9 +206,7 @@ ExportTask::~ExportTask()
if (color_processor_) {
oakrender_color_processor_free(color_processor_);
}
if (color_manager_) {
oaknode_colormanager_free(color_manager_);
}
oaknode_colormanager_free(&color_manager_);
oakrender_project_copier_free(copier_);
if (audio_params()) {
oakcore_audioparams_free(audio_params());
@@ -282,8 +293,9 @@ bool ExportTask::run()
} else {
// Render entire sequence
int len_n = 0, len_d = 1;
oaknode_sequence_get_length(
reinterpret_cast<OakNodeSequence *>(viewer()), &len_n, &len_d);
OakNodeSequence viewer_sequence = sequence_alias_of(viewer());
oaknode_sequence_get_length(viewer_sequence, &len_n, &len_d);
oaknode_sequence_free(&viewer_sequence);
export_range_ =
TimeRange(Rational(0), Rational(len_n, len_d));
}
@@ -474,7 +486,7 @@ bool ExportTask::audio_downloaded(const TimeRange &range,
return true;
}
bool ExportTask::encode_subtitle(OakNodeBlock *sub)
bool ExportTask::encode_subtitle(OakNodeBlock sub)
{
// The subtitle block's text is its standard "text" input
char text[8192];
+4 -3
View File
@@ -41,7 +41,7 @@ namespace olive
*/
class ExportTask : public RenderTask {
public:
ExportTask(OakNodeNode *viewer_node, OakNodeColorManager *color_manager,
ExportTask(OakNodeNode viewer_node, OakNodeColorManager color_manager,
const oakcodec_encoding_params &params);
virtual ~ExportTask() override;
@@ -55,7 +55,7 @@ protected:
virtual bool audio_downloaded(const TimeRange &range,
OakSampleBuffer *samples) override;
virtual bool encode_subtitle(OakNodeBlock *sub) override;
virtual bool encode_subtitle(OakNodeBlock sub) override;
private:
bool write_audio_loop(const TimeRange &time, OakSampleBuffer *samples);
@@ -72,7 +72,8 @@ private:
};
std::map<TimeRange, OakSampleBuffer *, TimeRangeLess> audio_map_;
OakNodeColorManager *color_manager_;
/** Owned handle (created on the copied project). */
OakNodeColorManager color_manager_;
oakcodec_encoding_params params_;
+24 -15
View File
@@ -31,6 +31,8 @@
#include "rendermodes.h"
#include "timeline/workarea.h"
#include "nodehandle.h"
namespace olive
{
@@ -41,10 +43,10 @@ const char *k_viewer_output_id = "org.olivevideoeditor.Olive.vieweroutput";
} // namespace
PreCacheTask::PreCacheTask(OakNodeFootage *footage, int index,
OakNodeSequence *sequence)
: project_(nullptr)
, footage_(nullptr)
PreCacheTask::PreCacheTask(OakNodeFootage footage, int index,
OakNodeSequence sequence)
: project_({})
, footage_({})
, audio_params_(nullptr)
{
// Set video and audio params
@@ -71,31 +73,35 @@ PreCacheTask::PreCacheTask(OakNodeFootage *footage, int index,
}
// Copy project config nodes
OakNodeProject *source_project = nullptr;
OakNodeProject source_project = {};
oaknode_node_get_project(oaknode_footage_as_node(footage),
&source_project);
if (source_project) {
if (source_project.ctx) {
oaknode_project_copy_settings(project_, source_project);
oaknode_project_free(&source_project);
}
// Copy footage node so it can precache without any modifications from the user screwing it up
OakNodeNode *footage_copy =
OakNodeNode footage_copy =
oaknode_node_create_copy(oaknode_footage_as_node(footage));
footage_ = reinterpret_cast<OakNodeFootage *>(
oaknode_block_from_node(footage_copy));
if (!footage_) {
footage_ = reinterpret_cast<OakNodeFootage *>(footage_copy);
}
oaknode_project_add_node(project_, footage_copy);
oaknode_node_copy_inputs(footage_copy, oaknode_footage_as_node(footage),
0);
// Borrowed footage alias of the copied node (releasing it only frees
// the handle box; the graph owns the node).
footage_ = oaknode_c_api::make_handle<OakNodeFootage>(
oaknode_c_api::to_native<void>(footage_copy), false, nullptr);
oaknode_node_connect(footage_copy, viewer(),
OAKNODE_SEQUENCE_TEXTURE_INPUT);
oaknode_node_set_value_hint_track(viewer(),
OAKNODE_SEQUENCE_TEXTURE_INPUT,
OAKNODE_TRACK_TYPE_VIDEO, index);
// The graph owns the copy now; release our handle box.
oaknode_node_free(&footage_copy);
char filename[1024];
if (oaknode_footage_filename(footage, filename, sizeof(filename)) <=
0) {
@@ -108,7 +114,10 @@ PreCacheTask::PreCacheTask(OakNodeFootage *footage, int index,
PreCacheTask::~PreCacheTask()
{
// This should delete the footage we copied and the viewer we created
oaknode_project_free(project_);
oaknode_project_free(&project_);
// Release the borrowed alias box (the footage itself died with the
// project above).
oaknode_c_api::free_handle(&footage_);
if (audio_params_) {
oakcore_audioparams_free(audio_params_);
}
@@ -167,13 +176,13 @@ bool PreCacheTask::run()
}
}
OakNodeColorManager *color_manager =
OakNodeColorManager color_manager =
oaknode_colormanager_init(project_);
render(color_manager, video_range, TimeRangeList(), TimeRange(),
0 /* RenderMode::k_online */, cache, ForceParams());
oaknode_colormanager_free(color_manager);
oaknode_colormanager_free(&color_manager);
return true;
}
+7 -4
View File
@@ -31,8 +31,8 @@ namespace olive
class PreCacheTask : public RenderTask {
public:
PreCacheTask(OakNodeFootage *footage, int index,
OakNodeSequence *sequence);
PreCacheTask(OakNodeFootage footage, int index,
OakNodeSequence sequence);
virtual ~PreCacheTask() override;
@@ -46,9 +46,12 @@ protected:
OakSampleBuffer *samples) override;
private:
OakNodeProject *project_;
/** Owned handle; the project owns the whole graph (including the
* copied footage and the viewer), so freeing it tears them down. */
OakNodeProject project_;
OakNodeFootage *footage_;
/** Borrowed alias of the copied footage node inside `project_`. */
OakNodeFootage footage_;
OakAudioParams *audio_params_;
};
+43 -27
View File
@@ -63,7 +63,7 @@ std::string basename_of(const std::string &path)
ProjectImportTask::ImageSequenceConfirmFn ProjectImportTask::confirm_callback_;
ProjectImportTask::ProjectImportTask(
OakNodeFolder *folder, OakNodeProject *project,
OakNodeFolder folder, OakNodeProject project,
const std::vector<std::string> &filenames)
: command_({})
, folder_(folder)
@@ -83,6 +83,13 @@ ProjectImportTask::~ProjectImportTask()
if (command_.ctx) {
oakundo_command_free(&command_);
}
// Borrowed handles: releasing them only frees the handle boxes (the
// footage nodes are owned by the project).
for (OakNodeFootage &footage : imported_footage_) {
if (footage.ctx) {
footage.release(footage.ctx);
}
}
}
const int &ProjectImportTask::get_file_count() const
@@ -110,7 +117,7 @@ bool ProjectImportTask::run()
return true;
}
void ProjectImportTask::import(OakNodeFolder *folder,
void ProjectImportTask::import(OakNodeFolder folder,
const std::vector<std::string> &entries,
int &counter, OakUndoCommand parent_command)
{
@@ -133,8 +140,8 @@ void ProjectImportTask::import(OakNodeFolder *folder,
}
if (!entry_list.empty()) {
OakNodeFolder *f = oaknode_folder_create(project_);
if (!f) {
OakNodeFolder f = oaknode_folder_create(project_);
if (!f.ctx) {
continue;
}
@@ -149,9 +156,9 @@ void ProjectImportTask::import(OakNodeFolder *folder,
}
} else {
OakNodeFootage *footage =
OakNodeFootage footage =
oaknode_footage_create(project_, nullptr);
if (!footage) {
if (!footage.ctx) {
continue;
}
@@ -181,9 +188,15 @@ void ProjectImportTask::import(OakNodeFolder *folder,
// Add to list so we can tell the user about it later
invalid_files_.push_back(file_path);
oaknode_project_remove_node(project_,
oaknode_footage_as_node(footage));
oaknode_node_free(oaknode_footage_as_node(footage));
// Remove the invalid footage from the graph; the remove
// command takes ownership on redo and deletes the node
// when the command is destroyed.
OakUndoCommand remove = oaknode_command_create_remove_node(
oaknode_footage_as_node(footage));
if (remove.ctx) {
oakundo_command_redo_now(remove);
oakundo_command_free(&remove);
}
}
counter++;
@@ -195,7 +208,7 @@ void ProjectImportTask::import(OakNodeFolder *folder,
}
void ProjectImportTask::validate_image_sequence(
OakNodeFootage *footage, std::vector<std::string> &info_list,
OakNodeFootage footage, std::vector<std::string> &info_list,
size_t index)
{
char filename[1024];
@@ -245,15 +258,15 @@ void ProjectImportTask::validate_image_sequence(
oakcodec_decoder_transform_image_sequence_file_name(filename, ind + 1,
next_fn, sizeof(next_fn));
OakNodeFootage *previous_file =
OakNodeFootage previous_file =
oaknode_footage_create(project_, prev_fn);
OakNodeFootage *next_file = oaknode_footage_create(project_, next_fn);
OakNodeFootage next_file = oaknode_footage_create(project_, next_fn);
bool prev_matches =
previous_file && oaknode_footage_is_valid(previous_file) &&
previous_file.ctx && oaknode_footage_is_valid(previous_file) &&
compare_still_image_size(previous_file, width, height);
bool next_matches =
next_file && oaknode_footage_is_valid(next_file) &&
next_file.ctx && oaknode_footage_is_valid(next_file) &&
compare_still_image_size(next_file, width, height);
if (prev_matches || next_matches) {
@@ -322,20 +335,23 @@ void ProjectImportTask::validate_image_sequence(
oakcommon_videoparams_free(&video_stream);
if (previous_file) {
oaknode_project_remove_node(project_,
oaknode_footage_as_node(previous_file));
oaknode_node_free(oaknode_footage_as_node(previous_file));
}
if (next_file) {
oaknode_project_remove_node(project_,
oaknode_footage_as_node(next_file));
oaknode_node_free(oaknode_footage_as_node(next_file));
// The probe footage above was only created for comparison; remove it
// from the graph again (the remove command deletes the node, see
// import()).
for (OakNodeFootage probe : { previous_file, next_file }) {
if (probe.ctx) {
OakUndoCommand remove = oaknode_command_create_remove_node(
oaknode_footage_as_node(probe));
if (remove.ctx) {
oakundo_command_redo_now(remove);
oakundo_command_free(&remove);
}
}
}
}
void ProjectImportTask::add_item_to_folder(OakNodeFolder *folder,
OakNodeNode *item,
void ProjectImportTask::add_item_to_folder(OakNodeFolder folder,
OakNodeNode item,
OakUndoCommand command)
{
OakUndoCommand child =
@@ -346,7 +362,7 @@ void ProjectImportTask::add_item_to_folder(OakNodeFolder *folder,
}
bool ProjectImportTask::item_is_still_image_footage_only(
OakNodeFootage *footage)
OakNodeFootage footage)
{
if (oaknode_footage_total_stream_count(footage) != 1) {
// Footage with more than one stream (usually video+audio) most likely isn't an image sequence
@@ -368,7 +384,7 @@ bool ProjectImportTask::item_is_still_image_footage_only(
return valid && video_type == OAKCOMMON_VIDEO_TYPE_STILL;
}
bool ProjectImportTask::compare_still_image_size(OakNodeFootage *footage,
bool ProjectImportTask::compare_still_image_size(OakNodeFootage footage,
int width, int height)
{
if (!item_is_still_image_footage_only(footage)) {
+12 -10
View File
@@ -37,7 +37,7 @@ namespace olive
class ProjectImportTask : public Task {
public:
ProjectImportTask(OakNodeFolder *folder, OakNodeProject *project,
ProjectImportTask(OakNodeFolder folder, OakNodeProject project,
const std::vector<std::string> &filenames);
~ProjectImportTask() override;
@@ -62,7 +62,7 @@ public:
return !invalid_files_.empty();
}
const std::vector<OakNodeFootage *> &get_imported_footage() const
const std::vector<OakNodeFootage> &get_imported_footage() const
{
return imported_footage_;
}
@@ -85,20 +85,20 @@ protected:
virtual bool run() override;
private:
void import(OakNodeFolder *folder,
void import(OakNodeFolder folder,
const std::vector<std::string> &entries, int &counter,
OakUndoCommand parent_command);
void validate_image_sequence(OakNodeFootage *footage,
void validate_image_sequence(OakNodeFootage footage,
std::vector<std::string> &info_list,
size_t index);
void add_item_to_folder(OakNodeFolder *folder, OakNodeNode *item,
void add_item_to_folder(OakNodeFolder folder, OakNodeNode item,
OakUndoCommand command);
static bool item_is_still_image_footage_only(OakNodeFootage *footage);
static bool item_is_still_image_footage_only(OakNodeFootage footage);
static bool compare_still_image_size(OakNodeFootage *footage, int width,
static bool compare_still_image_size(OakNodeFootage footage, int width,
int height);
static int64_t get_image_sequence_limit(const std::string &start_fn,
@@ -106,9 +106,10 @@ private:
OakUndoCommand command_;
OakNodeFolder *folder_;
/** Borrowed handles; the caller keeps ownership of both. */
OakNodeFolder folder_;
OakNodeProject *project_;
OakNodeProject project_;
std::vector<std::string> filenames_;
@@ -118,7 +119,8 @@ private:
std::vector<std::string> image_sequence_ignore_files_;
std::vector<OakNodeFootage *> imported_footage_;
/** Borrowed footage handles; the project owns the footage nodes. */
std::vector<OakNodeFootage> imported_footage_;
static ImageSequenceConfirmFn confirm_callback_;
};
+3 -4
View File
@@ -27,7 +27,7 @@ namespace olive
{
ProjectLoadBaseTask::ProjectLoadBaseTask(const std::string &filename)
: project_(nullptr)
: project_({})
, filename_(filename)
{
set_title("Loading '" + filename + "'");
@@ -41,7 +41,7 @@ ProjectLoadTask::ProjectLoadTask(const std::string &filename)
bool ProjectLoadTask::run()
{
project_ = oaknode_project_init();
if (!project_) {
if (!project_.ctx) {
set_error("Failed to create project");
return false;
}
@@ -92,8 +92,7 @@ bool ProjectLoadTask::run()
return true;
}
oaknode_project_free(project_);
project_ = nullptr;
oaknode_project_free(&project_);
return false;
}
+6 -6
View File
@@ -38,13 +38,13 @@ public:
ProjectLoadBaseTask(const std::string &filename);
/**
* @brief Take the loaded project (ownership transfer). NULL if the
* task has not succeeded.
* @brief Take the loaded project (ownership transfer). Empty handle
* (ctx == NULL) if the task has not succeeded.
*/
OakNodeProject *take_project()
OakNodeProject take_project()
{
OakNodeProject *p = project_;
project_ = nullptr;
OakNodeProject p = project_;
project_ = OakNodeProject{};
return p;
}
@@ -54,7 +54,7 @@ public:
}
protected:
OakNodeProject *project_;
OakNodeProject project_;
private:
std::string filename_;
+28 -29
View File
@@ -58,7 +58,7 @@ const char *k_sequence_id = "org.olivevideoeditor.Olive.sequence";
const char *k_transform_id = "org.olivevideoeditor.Olive.transform";
const char *k_volume_id = "org.olivevideoeditor.Olive.volume";
void set_own_context_position(OakNodeNode *node, double x, double y)
void set_own_context_position(OakNodeNode node, double x, double y)
{
oaknode_node_set_context_position(node, node, x, y, 0);
}
@@ -87,7 +87,7 @@ bool LoadOTIOTask::run()
}
project_ = oaknode_project_init();
if (!project_) {
if (!project_.ctx) {
set_error("Failed to create project");
return false;
}
@@ -113,14 +113,13 @@ bool LoadOTIOTask::run()
} else {
// Unknown root, we don't know what to do with this
set_error("Unknown OpenTimelineIO root element");
oaknode_project_free(project_);
project_ = nullptr;
oaknode_project_free(&project_);
return false;
}
// Keep track of imported footage
std::map<std::string, OakNodeFootage *> imported_footage;
std::map<OTIO::Timeline *, OakNodeSequence *> timeline_sequence_map;
std::map<std::string, OakNodeFootage> imported_footage;
std::map<OTIO::Timeline *, OakNodeSequence> timeline_sequence_map;
// Variables used for loading bar
float number_of_clips = 0;
@@ -130,8 +129,8 @@ bool LoadOTIOTask::run()
// Assumes each timeline has a unique name.
int unnamed_sequence_count = 0;
for (auto timeline : timelines) {
OakNodeSequence *sequence = oaknode_sequence_create();
if (!sequence) {
OakNodeSequence sequence = oaknode_sequence_create();
if (!sequence.ctx) {
continue;
}
@@ -180,16 +179,16 @@ bool LoadOTIOTask::run()
if (!accepted) {
// Cancel to indicate to caller that this task did not complete and to simply dispose of it
cancel();
for (const auto &pair : timeline_sequence_map) {
oaknode_sequence_free(pair.second);
for (auto &pair : timeline_sequence_map) {
oaknode_sequence_free(&pair.second);
}
return true;
}
for (const auto &pair : timeline_sequence_map) {
OTIO::Timeline *timeline = pair.first;
OakNodeSequence *sequence = pair.second;
OakNodeNode *sequence_node = oaknode_sequence_as_node(sequence);
OakNodeSequence sequence = pair.second;
OakNodeNode sequence_node = oaknode_sequence_as_node(sequence);
oaknode_project_add_node(project_, sequence_node);
OakUndoCommand add_seq = oaknode_command_create_folder_add_child(
@@ -200,9 +199,9 @@ bool LoadOTIOTask::run()
}
// Create a folder for this sequence's footage
OakNodeFolder *sequence_footage =
OakNodeFolder sequence_footage =
oaknode_folder_create(project_);
if (sequence_footage) {
if (sequence_footage.ctx) {
oaknode_node_set_label(oaknode_folder_as_node(sequence_footage),
timeline->name().c_str());
OakUndoCommand add_folder =
@@ -220,7 +219,7 @@ bool LoadOTIOTask::run()
auto otio_track = static_cast<OTIO::Track *>(c.value);
// Create a new track
OakNodeTrack *track = nullptr;
OakNodeTrack track = {};
// Determine what kind of track it is
int track_type = OAKNODE_TRACK_TYPE_NONE;
@@ -235,7 +234,7 @@ bool LoadOTIOTask::run()
}
{
OakNodeTrackList *track_list = nullptr;
OakNodeTrackList track_list = {};
oaknode_sequence_get_track_list(sequence, track_type,
&track_list);
OakUndoCommand add_track =
@@ -253,20 +252,20 @@ bool LoadOTIOTask::run()
}
}
if (!track) {
if (!track.ctx) {
continue;
}
// Get clips from track
auto clip_map = otio_track->children();
OakNodeBlock *previous_block = nullptr;
OakNodeBlock previous_block = {};
bool prev_block_transition = false;
for (auto otio_block_retainer : clip_map) {
auto otio_block = otio_block_retainer.value;
OakNodeBlock *block = nullptr;
OakNodeBlock block = {};
if (otio_block->schema_name() == "Clip") {
block = oaknode_block_clip_create();
@@ -286,7 +285,7 @@ bool LoadOTIOTask::run()
block = oaknode_block_gap_create();
}
if (!block) {
if (!block.ctx) {
continue;
}
@@ -347,7 +346,7 @@ bool LoadOTIOTask::run()
block, in_offset.numerator(), in_offset.denominator(),
out_offset.numerator(), out_offset.denominator());
if (previous_block) {
if (previous_block.ctx) {
oaknode_node_connect(
oaknode_block_as_node(previous_block),
oaknode_block_as_node(block),
@@ -382,7 +381,7 @@ bool LoadOTIOTask::run()
otio_clip->media_reference())
->target_url();
OakNodeFootage *probed_item = nullptr;
OakNodeFootage probed_item = {};
auto it = imported_footage.find(footage_url);
if (it != imported_footage.end()) {
@@ -390,7 +389,7 @@ bool LoadOTIOTask::run()
} else {
probed_item = oaknode_footage_create(
project_, footage_url.c_str());
if (probed_item) {
if (probed_item.ctx) {
imported_footage.insert(
{ footage_url, probed_item });
@@ -402,7 +401,7 @@ bool LoadOTIOTask::run()
oaknode_footage_as_node(probed_item),
label.c_str());
if (sequence_footage) {
if (sequence_footage.ctx) {
OakUndoCommand add_footage =
oaknode_command_create_folder_add_child(
sequence_footage,
@@ -416,7 +415,7 @@ bool LoadOTIOTask::run()
}
}
if (probed_item) {
if (probed_item.ctx) {
// Position clip in its own context
set_own_context_position(
oaknode_block_as_node(block), 0, 0);
@@ -428,10 +427,10 @@ bool LoadOTIOTask::run()
0);
if (track_type == OAKNODE_TRACK_TYPE_VIDEO) {
OakNodeNode *transform =
OakNodeNode transform =
oaknode_factory_create_from_id(
k_transform_id);
if (transform) {
if (transform.ctx) {
oaknode_project_add_node(project_,
transform);
@@ -448,10 +447,10 @@ bool LoadOTIOTask::run()
transform, -1, 0, 0);
}
} else {
OakNodeNode *volume_node =
OakNodeNode volume_node =
oaknode_factory_create_from_id(
k_volume_id);
if (volume_node) {
if (volume_node.ctx) {
oaknode_project_add_node(project_,
volume_node);
+2 -2
View File
@@ -29,7 +29,7 @@ namespace olive
namespace
{
std::string project_filename(OakNodeProject *project)
std::string project_filename(OakNodeProject project)
{
int needed = oaknode_project_filename(project, nullptr, 0);
if (needed <= 0) {
@@ -43,7 +43,7 @@ std::string project_filename(OakNodeProject *project)
} // namespace
ProjectSaveTask::ProjectSaveTask(OakNodeProject *project,
ProjectSaveTask::ProjectSaveTask(OakNodeProject project,
bool use_compression)
: project_(project)
, use_compression_(use_compression)
+4 -3
View File
@@ -32,9 +32,9 @@ namespace olive
class ProjectSaveTask : public Task {
public:
ProjectSaveTask(OakNodeProject *project, bool use_compression);
ProjectSaveTask(OakNodeProject project, bool use_compression);
OakNodeProject *get_project() const
OakNodeProject get_project() const
{
return project_;
}
@@ -48,7 +48,8 @@ protected:
virtual bool run() override;
private:
OakNodeProject *project_;
/** Borrowed handle; the caller keeps ownership of the project. */
OakNodeProject project_;
std::string override_filename_;
+33 -27
View File
@@ -44,7 +44,7 @@ namespace olive
namespace
{
std::string node_label_of(OakNodeNode *node)
std::string node_label_of(OakNodeNode node)
{
char buf[256];
if (oaknode_node_get_label(node, buf, sizeof(buf)) <= 0) {
@@ -53,21 +53,21 @@ std::string node_label_of(OakNodeNode *node)
return buf;
}
Rational block_in_of(OakNodeBlock *b)
Rational block_in_of(OakNodeBlock b)
{
int n = 0, d = 1;
oaknode_block_get_in(b, &n, &d);
return Rational(n, d);
}
Rational block_length_of(OakNodeBlock *b)
Rational block_length_of(OakNodeBlock b)
{
int n = 0, d = 1;
oaknode_block_get_length(b, &n, &d);
return Rational(n, d);
}
Rational track_length_of(OakNodeTrack *t)
Rational track_length_of(OakNodeTrack t)
{
int n = 0, d = 1;
oaknode_track_get_length(t, &n, &d);
@@ -76,7 +76,7 @@ Rational track_length_of(OakNodeTrack *t)
} // namespace
SaveOTIOTask::SaveOTIOTask(OakNodeProject *project,
SaveOTIOTask::SaveOTIOTask(OakNodeProject project,
const std::string &filename)
: project_(project)
, filename_(filename)
@@ -88,18 +88,18 @@ bool SaveOTIOTask::run()
{
// Collect sequences from the root folder (non-recursive, matching the
// original list_children_of_type behavior closely enough for OTIO)
std::vector<OakNodeSequence *> sequences;
std::vector<OakNodeSequence> sequences;
OakNodeFolder *root = oaknode_project_root(project_);
if (!root) {
OakNodeFolder root = oaknode_project_root(project_);
if (!root.ctx) {
set_error("Project contains no sequences to export.");
return false;
}
int child_count = oaknode_folder_child_count(root);
for (int i = 0; i < child_count; i++) {
OakNodeNode *child = oaknode_folder_child_at(root, i);
if (!child) {
OakNodeNode child = oaknode_folder_child_at(root, i);
if (!child.ctx) {
continue;
}
@@ -108,8 +108,14 @@ bool SaveOTIOTask::run()
continue;
}
if (std::string(id) == "org.olivevideoeditor.Olive.sequence") {
sequences.push_back(
reinterpret_cast<OakNodeSequence *>(child));
// Borrowed sequence alias of the child node handle (all
// oaknode handles share the same box layout).
OakNodeSequence sequence = {};
sequence.ctx = child.ctx;
sequence.addref = child.addref;
sequence.release = child.release;
sequence.abi_version = child.abi_version;
sequences.push_back(sequence);
}
}
@@ -120,7 +126,7 @@ bool SaveOTIOTask::run()
std::vector<OTIO::SerializableObject *> serialized;
for (OakNodeSequence *seq : sequences) {
for (OakNodeSequence seq : sequences) {
auto otio_timeline = serialize_timeline(seq);
if (otio_timeline) {
@@ -163,7 +169,7 @@ bool SaveOTIOTask::run()
return (es.outcome == OTIO::ErrorStatus::Outcome::OK);
}
OTIO::Timeline *SaveOTIOTask::serialize_timeline(OakNodeSequence *sequence)
OTIO::Timeline *SaveOTIOTask::serialize_timeline(OakNodeSequence sequence)
{
auto otio_timeline = new OTIO::Timeline(
node_label_of(oaknode_sequence_as_node(sequence)));
@@ -189,8 +195,8 @@ OTIO::Timeline *SaveOTIOTask::serialize_timeline(OakNodeSequence *sequence)
return nullptr;
}
OakNodeTrackList *video_list = nullptr;
OakNodeTrackList *audio_list = nullptr;
OakNodeTrackList video_list = {};
OakNodeTrackList audio_list = {};
oaknode_sequence_get_track_list(sequence, OAKNODE_TRACK_TYPE_VIDEO,
&video_list);
oaknode_sequence_get_track_list(sequence, OAKNODE_TRACK_TYPE_AUDIO,
@@ -205,7 +211,7 @@ OTIO::Timeline *SaveOTIOTask::serialize_timeline(OakNodeSequence *sequence)
return otio_timeline;
}
OTIO::Track *SaveOTIOTask::serialize_track(OakNodeTrack *track,
OTIO::Track *SaveOTIOTask::serialize_track(OakNodeTrack track,
double sequence_rate,
Rational max_track_length)
{
@@ -234,9 +240,9 @@ OTIO::Track *SaveOTIOTask::serialize_track(OakNodeTrack *track,
oaknode_track_get_block_count(track, &block_count);
for (int i = 0; i < block_count; i++) {
OakNodeBlock *block = nullptr;
OakNodeBlock block = {};
oaknode_track_get_block_at(track, i, &block);
if (!block) {
if (!block.ctx) {
continue;
}
@@ -253,10 +259,10 @@ OTIO::Track *SaveOTIOTask::serialize_track(OakNodeTrack *track,
block_in_of(block).toRationalTime(sequence_rate),
block_length_of(block).toRationalTime(sequence_rate)));
OakNodeFootage *media = nullptr;
OakNodeFootage media = {};
oaknode_node_find_input_footage(
oaknode_block_as_node(block), &media);
if (media) {
if (media.ctx) {
OTIO::TimeRange available_range;
if (track_type == OAKNODE_TRACK_TYPE_VIDEO) {
// OTIO ExternalReference uses the source clips frame rate (or sample rate) as opposed to
@@ -353,11 +359,11 @@ fail:
return nullptr;
}
bool SaveOTIOTask::serialize_track_list(OakNodeTrackList *list,
bool SaveOTIOTask::serialize_track_list(OakNodeTrackList list,
OTIO::Timeline *otio_timeline,
double sequence_rate)
{
if (!list) {
if (!list.ctx) {
return true;
}
@@ -369,17 +375,17 @@ bool SaveOTIOTask::serialize_track_list(OakNodeTrackList *list,
oaknode_tracklist_get_track_count(list, &track_count);
for (int i = 0; i < track_count; i++) {
OakNodeTrack *track = nullptr;
OakNodeTrack track = {};
oaknode_tracklist_get_track_at(list, i, &track);
if (track && track_length_of(track) > max_track_length) {
if (track.ctx && track_length_of(track) > max_track_length) {
max_track_length = track_length_of(track);
}
}
for (int i = 0; i < track_count; i++) {
OakNodeTrack *track = nullptr;
OakNodeTrack track = {};
oaknode_tracklist_get_track_at(list, i, &track);
if (!track) {
if (!track.ctx) {
continue;
}
+6 -5
View File
@@ -43,22 +43,23 @@ using core::Rational;
class SaveOTIOTask : public Task {
public:
SaveOTIOTask(OakNodeProject *project, const std::string &filename);
SaveOTIOTask(OakNodeProject project, const std::string &filename);
protected:
virtual bool run() override;
private:
OTIO::Timeline *serialize_timeline(OakNodeSequence *sequence);
OTIO::Timeline *serialize_timeline(OakNodeSequence sequence);
OTIO::Track *serialize_track(OakNodeTrack *track, double sequence_rate,
OTIO::Track *serialize_track(OakNodeTrack track, double sequence_rate,
Rational max_track_length);
bool serialize_track_list(OakNodeTrackList *list,
bool serialize_track_list(OakNodeTrackList list,
OTIO::Timeline *otio_timeline,
double sequence_rate);
OakNodeProject *project_;
/** Borrowed handle; the caller keeps ownership of the project. */
OakNodeProject project_;
std::string filename_;
};
+40 -26
View File
@@ -27,20 +27,22 @@
#include "node/sequence.h"
#include "node/track.h"
#include "nodehandle.h"
namespace olive
{
namespace
{
Rational task_block_in(OakNodeBlock *b)
Rational task_block_in(OakNodeBlock b)
{
int n = 0, d = 1;
oaknode_block_get_in(b, &n, &d);
return Rational(n, d);
}
Rational task_block_out(OakNodeBlock *b)
Rational task_block_out(OakNodeBlock b)
{
int n = 0, d = 1;
oaknode_block_get_out(b, &n, &d);
@@ -50,7 +52,7 @@ Rational task_block_out(OakNodeBlock *b)
} // namespace
RenderTask::RenderTask()
: viewer_(nullptr)
: viewer_({})
, video_params_({})
, audio_params_(nullptr)
, running_tickets_(0)
@@ -61,6 +63,9 @@ RenderTask::RenderTask()
RenderTask::~RenderTask()
{
// Borrowed/owned indifferent: releasing the viewer handle only frees
// the handle box once the node lives in a graph.
oaknode_node_free(&viewer_);
if (video_params_.ctx) {
oakcommon_videoparams_free(&video_params_);
}
@@ -75,15 +80,15 @@ void RenderTask::on_ticket_finished(OakRenderTicket *ticket)
finished_mutex_.unlock();
}
bool RenderTask::start_video_ticket(OakNodeColorManager *manager,
bool RenderTask::start_video_ticket(OakNodeColorManager manager,
const Rational &time, int mode,
OakNodeFrameCache *cache,
const ForceParams &force)
{
OakNodeNode *output_node = nullptr;
OakNodeNode output_node = {};
oaknode_node_input_get_connected_node(
viewer_, OAKNODE_SEQUENCE_TEXTURE_INPUT, &output_node);
if (!output_node) {
if (!output_node.ctx) {
return false;
}
@@ -113,6 +118,9 @@ bool RenderTask::start_video_ticket(OakNodeColorManager *manager,
static_cast<RenderTask *>(userdata)->on_ticket_finished(t);
},
this);
// Per-frame call: release the borrowed handle box (the ticket keeps
// the native node, not the handle).
oaknode_node_free(&output_node);
if (!ticket) {
return false;
}
@@ -124,7 +132,7 @@ bool RenderTask::start_video_ticket(OakNodeColorManager *manager,
return true;
}
bool RenderTask::render(OakNodeColorManager *manager,
bool RenderTask::render(OakNodeColorManager manager,
const TimeRangeList &video_range,
const TimeRangeList &audio_range,
const TimeRange &subtitle_range, int render_mode,
@@ -137,10 +145,10 @@ bool RenderTask::render(OakNodeColorManager *manager,
// Queue audio jobs
for (const TimeRange &range : audio_range) {
OakNodeNode *output_node = nullptr;
OakNodeNode output_node = {};
oaknode_node_input_get_connected_node(
viewer_, OAKNODE_SEQUENCE_SAMPLES_INPUT, &output_node);
if (!output_node) {
if (!output_node.ctx) {
continue;
}
@@ -158,6 +166,7 @@ bool RenderTask::render(OakNodeColorManager *manager,
running_tickets_++;
finished_mutex_.unlock();
}
oaknode_node_free(&output_node);
}
// Frame timestamps
@@ -198,13 +207,16 @@ bool RenderTask::render(OakNodeColorManager *manager,
// Subtitle loop, loops over all blocks in sequence on all tracks
if (!subtitle_range.length().isNull()) {
OakNodeSequence *sequence =
reinterpret_cast<OakNodeSequence *>(viewer_);
OakNodeTrackList *list = nullptr;
// Borrowed sequence alias of the viewer handle (same underlying
// node; releasing it only frees the handle box).
OakNodeSequence sequence = oaknode_c_api::make_handle<
OakNodeSequence>(oaknode_c_api::to_native<void>(viewer_), false,
nullptr);
OakNodeTrackList list = {};
oaknode_sequence_get_track_list(
sequence, OAKNODE_TRACK_TYPE_SUBTITLE, &list);
if (list) {
if (list.ctx) {
int track_count = 0;
oaknode_tracklist_get_track_count(list, &track_count);
@@ -214,9 +226,9 @@ bool RenderTask::render(OakNodeColorManager *manager,
tracks_to_push.clear();
for (int i = 0; i < track_count; i++) {
OakNodeTrack *this_track = nullptr;
OakNodeTrack this_track = {};
oaknode_tracklist_get_track_at(list, i, &this_track);
if (!this_track) {
if (!this_track.ctx) {
continue;
}
@@ -234,27 +246,27 @@ bool RenderTask::render(OakNodeColorManager *manager,
continue;
}
OakNodeBlock *this_block = nullptr;
OakNodeBlock this_block = {};
oaknode_track_get_block_at(this_track,
this_block_index,
&this_block);
OakNodeTrack *compare_track = nullptr;
OakNodeTrack compare_track = {};
if (!tracks_to_push.empty()) {
oaknode_tracklist_get_track_at(
list, tracks_to_push.front(), &compare_track);
}
OakNodeBlock *compare_block = nullptr;
if (compare_track) {
OakNodeBlock compare_block = {};
if (compare_track.ctx) {
oaknode_track_get_block_at(
compare_track,
block_indexes[size_t(
tracks_to_push.front())],
&compare_block);
}
if (!compare_track ||
if (!compare_track.ctx ||
task_block_out(compare_block) >= task_block_in(this_block)) {
if (compare_track &&
if (compare_track.ctx &&
task_block_in(compare_block) !=
task_block_in(this_block)) {
tracks_to_push.clear();
@@ -264,17 +276,17 @@ bool RenderTask::render(OakNodeColorManager *manager,
}
for (int i : tracks_to_push) {
OakNodeTrack *this_track = nullptr;
OakNodeTrack this_track = {};
oaknode_tracklist_get_track_at(list, i, &this_track);
OakNodeBlock *this_block = nullptr;
OakNodeBlock this_block = {};
oaknode_track_get_block_at(
this_track, block_indexes[size_t(i)], &this_block);
int kind = OAKNODE_BLOCK_OTHER;
if (this_block) {
if (this_block.ctx) {
oaknode_block_get_kind(this_block, &kind);
}
if (this_block && kind != OAKNODE_BLOCK_GAP) {
if (this_block.ctx && kind != OAKNODE_BLOCK_GAP) {
int enabled = 0;
oaknode_block_get_enabled(this_block, &enabled);
if (enabled) {
@@ -289,6 +301,8 @@ bool RenderTask::render(OakNodeColorManager *manager,
}
} while (!tracks_to_push.empty());
}
oaknode_sequence_free(&sequence);
}
std::unique_lock<std::mutex> loop_lock(finished_mutex_);
@@ -400,7 +414,7 @@ bool RenderTask::download_frame(OakCodecFrame *frame, const Rational &time)
return true;
}
bool RenderTask::encode_subtitle(OakNodeBlock *subtitle)
bool RenderTask::encode_subtitle(OakNodeBlock subtitle)
{
(void)subtitle;
return true;
+6 -6
View File
@@ -69,7 +69,7 @@ protected:
OakColorTransform color_transform = {}; /**< empty ctx = default */
};
bool render(OakNodeColorManager *manager,
bool render(OakNodeColorManager manager,
const TimeRangeList &video_range,
const TimeRangeList &audio_range,
const TimeRange &subtitle_range, int render_mode,
@@ -84,14 +84,14 @@ protected:
virtual bool audio_downloaded(const TimeRange &range,
OakSampleBuffer *samples) = 0;
virtual bool encode_subtitle(OakNodeBlock *subtitle);
virtual bool encode_subtitle(OakNodeBlock subtitle);
OakNodeNode *viewer() const
OakNodeNode viewer() const
{
return viewer_;
}
void set_viewer(OakNodeNode *v)
void set_viewer(OakNodeNode v)
{
viewer_ = v;
}
@@ -162,12 +162,12 @@ private:
void on_ticket_finished(OakRenderTicket *ticket);
bool start_video_ticket(OakNodeColorManager *manager,
bool start_video_ticket(OakNodeColorManager manager,
const Rational &time, int mode,
OakNodeFrameCache *cache,
const ForceParams &force);
OakNodeNode *viewer_;
OakNodeNode viewer_;
OakVideoParams video_params_;
+50 -39
View File
@@ -41,16 +41,16 @@ protected:
void SetUp() override
{
project_ = oaknode_project_init();
ASSERT_NE(project_, nullptr);
ASSERT_NE(project_.ctx, nullptr);
ASSERT_EQ(oaknode_project_initialize(project_), OAKNODE_OK);
}
void TearDown() override
{
oaknode_project_free(project_);
oaknode_project_free(&project_);
}
OakNodeProject *project_ = nullptr;
OakNodeProject project_ = {};
};
// ---- task family ----------------------------------------------------------
@@ -104,7 +104,7 @@ TEST_F(OakTaskFixture, LoadMissingFileFails)
char err[256];
EXPECT_GT(oaktask_task_error(t, err, sizeof(err)), 0);
EXPECT_EQ(oaktask_load_take_project(t), nullptr);
EXPECT_EQ(oaktask_load_take_project(t).ctx, nullptr);
oaktask_task_free(t);
EXPECT_EQ(oaktask_create_project_load(nullptr), nullptr);
@@ -126,11 +126,11 @@ TEST_F(OakTaskFixture, SaveLoadRoundTrip)
ASSERT_NE(load, nullptr);
ASSERT_EQ(oaktask_task_start_sync(load), 1);
OakNodeProject *loaded = oaktask_load_take_project(load);
ASSERT_NE(loaded, nullptr);
EXPECT_EQ(oaktask_load_take_project(load), nullptr);
OakNodeProject loaded = oaktask_load_take_project(load);
ASSERT_NE(loaded.ctx, nullptr);
EXPECT_EQ(oaktask_load_take_project(load).ctx, nullptr);
oaknode_project_free(loaded);
oaknode_project_free(&loaded);
oaktask_task_free(load);
std::filesystem::remove(path);
@@ -138,8 +138,8 @@ TEST_F(OakTaskFixture, SaveLoadRoundTrip)
TEST_F(OakTaskFixture, ImportDemoFootage)
{
OakNodeFolder *folder = oaknode_folder_create(project_);
ASSERT_NE(folder, nullptr);
OakNodeFolder folder = oaknode_folder_create(project_);
ASSERT_NE(folder.ctx, nullptr);
const char *urls[] = { OAK_REPO_ROOT "/tests/demo.mp4" };
OakTaskTask *t =
@@ -157,8 +157,12 @@ TEST_F(OakTaskFixture, ImportDemoFootage)
}
EXPECT_EQ(oaktask_import_footage_count(t), 1);
EXPECT_NE(oaktask_import_footage_at(t, 0), nullptr);
EXPECT_EQ(oaktask_import_footage_at(t, 5), nullptr);
OakNodeFootage footage = oaktask_import_footage_at(t, 0);
EXPECT_NE(footage.ctx, nullptr);
if (footage.ctx) {
footage.release(footage.ctx);
}
EXPECT_EQ(oaktask_import_footage_at(t, 5).ctx, nullptr);
EXPECT_EQ(oaktask_import_invalid_count(t), 0);
EXPECT_EQ(oaktask_import_invalid_at(t, 0, nullptr, 0),
OAKTASK_E_NOT_FOUND);
@@ -179,8 +183,9 @@ TEST_F(OakTaskFixture, ImportDemoFootage)
oakundo_command_free(&cmd);
oaktask_task_free(t);
EXPECT_EQ(oaktask_create_project_import(nullptr, project_, urls, 1),
nullptr);
EXPECT_EQ(
oaktask_create_project_import(OakNodeFolder{}, project_, urls, 1),
nullptr);
EXPECT_EQ(oaktask_import_footage_count(nullptr), OAKTASK_E_INVALID);
EXPECT_EQ(oaktask_debug_alive_count(), 0);
@@ -210,8 +215,8 @@ TEST(OakTaskManager, AsyncStartAndSubscribe)
{
ASSERT_EQ(oaktask_manager_init(), OAKTASK_OK);
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK);
std::string path =
@@ -258,7 +263,7 @@ TEST(OakTaskManager, AsyncStartAndSubscribe)
std::filesystem::remove(path);
oaktask_task_free(t);
oaknode_project_free(project);
oaknode_project_free(&project);
oaktask_manager_shutdown();
EXPECT_EQ(oaktask_debug_alive_count(), 0);
@@ -319,29 +324,34 @@ TEST(OakTaskConform, SubmittedConformProducesPcm)
TEST(OakTaskRenderFamily, FactoryErrorPaths)
{
EXPECT_EQ(oaktask_create_precache(nullptr, 0, nullptr), nullptr);
EXPECT_EQ(oaktask_create_precache(OakNodeFootage{}, 0, OakNodeSequence{}),
nullptr);
oakcodec_encoding_params params = {};
EXPECT_EQ(oaktask_create_export(nullptr, nullptr, &params), nullptr);
EXPECT_EQ(oaktask_create_export(nullptr, nullptr, nullptr), nullptr);
EXPECT_EQ(oaktask_create_export(OakNodeNode{}, OakNodeColorManager{},
&params),
nullptr);
EXPECT_EQ(oaktask_create_export(OakNodeNode{}, OakNodeColorManager{},
nullptr),
nullptr);
}
TEST(OakTaskRenderFamily, ExportTaskConstruction)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK);
// A sequence gives us a viewer with tracks; the factory should wrap
// the task successfully (no render manager needed for construction)
OakNodeSequence *sequence = oaknode_sequence_create();
ASSERT_NE(sequence, nullptr);
OakNodeSequence sequence = oaknode_sequence_create();
ASSERT_NE(sequence.ctx, nullptr);
ASSERT_EQ(oaknode_project_add_node(project,
oaknode_sequence_as_node(sequence)),
OAKNODE_OK);
OakNodeColorManager *cm = oaknode_colormanager_init(project);
ASSERT_NE(cm, nullptr);
OakNodeColorManager cm = oaknode_colormanager_init(project);
ASSERT_NE(cm.ctx, nullptr);
oakcodec_encoding_params params = {};
strncpy(params.filename, "/tmp/oaktask_export_test.mp4",
@@ -357,8 +367,8 @@ TEST(OakTaskRenderFamily, ExportTaskConstruction)
// Running needs a render manager; not available in this binary
oaktask_task_free(t);
oaknode_colormanager_free(cm);
oaknode_project_free(project);
oaknode_colormanager_free(&cm);
oaknode_project_free(&project);
EXPECT_EQ(oaktask_debug_alive_count(), 0);
}
@@ -367,13 +377,13 @@ TEST(OakTaskRenderFamily, ExportTaskConstruction)
TEST(OakTaskOTIO, SaveLoadRoundTrip)
{
OakNodeProject *project = oaknode_project_init();
ASSERT_NE(project, nullptr);
OakNodeProject project = oaknode_project_init();
ASSERT_NE(project.ctx, nullptr);
ASSERT_EQ(oaknode_project_initialize(project), OAKNODE_OK);
// A sequence with a name so save has something to serialize
OakNodeSequence *sequence = oaknode_sequence_create();
ASSERT_NE(sequence, nullptr);
OakNodeSequence sequence = oaknode_sequence_create();
ASSERT_NE(sequence.ctx, nullptr);
ASSERT_EQ(oaknode_project_add_node(project,
oaknode_sequence_as_node(sequence)),
OAKNODE_OK);
@@ -383,8 +393,8 @@ TEST(OakTaskOTIO, SaveLoadRoundTrip)
"OTIO Test Sequence"),
OAKNODE_OK);
OakNodeFolder *root = oaknode_project_root(project);
ASSERT_NE(root, nullptr);
OakNodeFolder root = oaknode_project_root(project);
ASSERT_NE(root.ctx, nullptr);
OakUndoCommand add_seq = oaknode_command_create_folder_add_child(
root, oaknode_sequence_as_node(sequence));
ASSERT_NE(add_seq.ctx, nullptr);
@@ -409,16 +419,17 @@ TEST(OakTaskOTIO, SaveLoadRoundTrip)
ASSERT_NE(load, nullptr);
ASSERT_EQ(oaktask_task_start_sync(load), 1);
OakNodeProject *loaded = oaktask_load_otio_take_project(load);
ASSERT_NE(loaded, nullptr);
oaknode_project_free(loaded);
OakNodeProject loaded = oaktask_load_otio_take_project(load);
ASSERT_NE(loaded.ctx, nullptr);
oaknode_project_free(&loaded);
oaktask_task_free(load);
std::filesystem::remove(path, ec);
oaknode_project_free(project);
oaknode_project_free(&project);
EXPECT_EQ(oaktask_debug_alive_count(), 0);
EXPECT_EQ(oaktask_create_project_load_otio(nullptr), nullptr);
EXPECT_EQ(oaktask_create_project_save_otio(nullptr, "x"), nullptr);
EXPECT_EQ(oaktask_create_project_save_otio(OakNodeProject{}, "x"),
nullptr);
}
+28 -28
View File
@@ -52,9 +52,9 @@ olive::Timeline::MovementMode to_mode(int mode)
} // namespace
OakUndoCommand oaktimeline_add_track_command(OakNodeTrackList *list)
OakUndoCommand oaktimeline_add_track_command(OakNodeTrackList list)
{
if (!list) {
if (!list.ctx) {
return OakUndoCommand{};
}
@@ -65,9 +65,9 @@ OakUndoCommand oaktimeline_add_track_command(OakNodeTrackList *list)
}
}
OakUndoCommand oaktimeline_remove_track_command(OakNodeTrack *track)
OakUndoCommand oaktimeline_remove_track_command(OakNodeTrack track)
{
if (!track) {
if (!track.ctx) {
return OakUndoCommand{};
}
@@ -78,13 +78,13 @@ OakUndoCommand oaktimeline_remove_track_command(OakNodeTrack *track)
}
}
OakUndoCommand oaktimeline_place_block_command(OakNodeTrackList *list,
OakUndoCommand oaktimeline_place_block_command(OakNodeTrackList list,
int track_index,
OakNodeBlock *block,
OakNodeBlock block,
int64_t in_num,
int64_t in_den)
{
if (!list || !block) {
if (!list.ctx || !block.ctx) {
return OakUndoCommand{};
}
@@ -97,9 +97,9 @@ OakUndoCommand oaktimeline_place_block_command(OakNodeTrackList *list,
}
OakUndoCommand oaktimeline_replace_block_with_gap_command(
OakNodeTrack *track, OakNodeBlock *block)
OakNodeTrack track, OakNodeBlock block)
{
if (!track || !block) {
if (!track.ctx || !block.ctx) {
return OakUndoCommand{};
}
@@ -111,13 +111,13 @@ OakUndoCommand oaktimeline_replace_block_with_gap_command(
}
}
OakUndoCommand oaktimeline_trim_command(OakNodeTrack *track,
OakNodeBlock *block,
OakUndoCommand oaktimeline_trim_command(OakNodeTrack track,
OakNodeBlock block,
int64_t new_length_num,
int64_t new_length_den, int mode)
{
if (!track || !block || (mode != OAKTIMELINE_MOVEMENT_TRIM_IN &&
mode != OAKTIMELINE_MOVEMENT_TRIM_OUT)) {
if (!track.ctx || !block.ctx || (mode != OAKTIMELINE_MOVEMENT_TRIM_IN &&
mode != OAKTIMELINE_MOVEMENT_TRIM_OUT)) {
return OakUndoCommand{};
}
@@ -131,7 +131,7 @@ OakUndoCommand oaktimeline_trim_command(OakNodeTrack *track,
}
}
OakUndoCommand oaktimeline_split_command(OakNodeBlock *const *blocks,
OakUndoCommand oaktimeline_split_command(const OakNodeBlock *blocks,
int count, int64_t point_num,
int64_t point_den)
{
@@ -142,7 +142,7 @@ OakUndoCommand oaktimeline_split_command(OakNodeBlock *const *blocks,
try {
auto *multi = new olive::MultiUndoCommand();
for (int i = 0; i < count; i++) {
if (blocks[i]) {
if (blocks[i].ctx) {
multi->add_child(new olive::BlockSplitCommand(
blocks[i], rat(point_num, point_den)));
}
@@ -154,7 +154,7 @@ OakUndoCommand oaktimeline_split_command(OakNodeBlock *const *blocks,
}
OakUndoCommand oaktimeline_split_preserving_links_command(
OakNodeBlock *const *blocks, int count, const int64_t *point_nums,
const OakNodeBlock *blocks, int count, const int64_t *point_nums,
const int64_t *point_dens, int time_count)
{
if (!blocks || count <= 0 || !point_nums || !point_dens ||
@@ -163,7 +163,7 @@ OakUndoCommand oaktimeline_split_preserving_links_command(
}
try {
std::vector<OakNodeBlock *> block_vec(blocks, blocks + count);
std::vector<OakNodeBlock> block_vec(blocks, blocks + count);
std::vector<olive::core::Rational> times;
times.reserve(time_count);
for (int i = 0; i < time_count; i++) {
@@ -177,11 +177,11 @@ OakUndoCommand oaktimeline_split_preserving_links_command(
}
OakUndoCommand oaktimeline_ripple_delete_gaps_command(
OakNodeSequence *sequence, const int64_t *in_nums,
OakNodeSequence sequence, const int64_t *in_nums,
const int64_t *in_dens, const int64_t *out_nums,
const int64_t *out_dens, OakNodeTrack *const *tracks, int range_count)
const int64_t *out_dens, const OakNodeTrack *tracks, int range_count)
{
if (!sequence || !in_nums || !in_dens || !out_nums || !out_dens ||
if (!sequence.ctx || !in_nums || !in_dens || !out_nums || !out_dens ||
!tracks || range_count <= 0) {
return OakUndoCommand{};
}
@@ -204,16 +204,16 @@ OakUndoCommand oaktimeline_ripple_delete_gaps_command(
}
OakUndoCommand oaktimeline_slide_command(
OakNodeTrack *track, OakNodeBlock *const *blocks, int block_count,
OakNodeBlock *in_adjacent, OakNodeBlock *out_adjacent,
OakNodeTrack track, const OakNodeBlock *blocks, int block_count,
OakNodeBlock in_adjacent, OakNodeBlock out_adjacent,
int64_t movement_num, int64_t movement_den)
{
if (!track || !blocks || block_count <= 0) {
if (!track.ctx || !blocks || block_count <= 0) {
return OakUndoCommand{};
}
try {
std::vector<OakNodeBlock *> block_vec(blocks, blocks + block_count);
std::vector<OakNodeBlock> block_vec(blocks, blocks + block_count);
return wrap_command(new olive::TrackSlideCommand(
track, block_vec, in_adjacent, out_adjacent,
rat(movement_num, movement_den)));
@@ -223,10 +223,10 @@ OakUndoCommand oaktimeline_slide_command(
}
OakUndoCommand oaktimeline_ripple_remove_area_command(
OakNodeTrack *track, int64_t in_num, int64_t in_den, int64_t out_num,
OakNodeTrack track, int64_t in_num, int64_t in_den, int64_t out_num,
int64_t out_den)
{
if (!track) {
if (!track.ctx) {
return OakUndoCommand{};
}
@@ -239,13 +239,13 @@ OakUndoCommand oaktimeline_ripple_remove_area_command(
}
}
OakUndoCommand oaktimeline_insert_gaps_command(OakNodeTrackList *list,
OakUndoCommand oaktimeline_insert_gaps_command(OakNodeTrackList list,
int64_t point_num,
int64_t point_den,
int64_t length_num,
int64_t length_den)
{
if (!list) {
if (!list.ctx) {
return OakUndoCommand{};
}
+2 -2
View File
@@ -66,9 +66,9 @@ olive::TimelineMarker *marker_at(OakTimelineMarkerList *list, int index)
} // namespace
OakTimelineMarkerList *oaktimeline_marker_list_of(OakNodeNode *owner)
OakTimelineMarkerList *oaktimeline_marker_list_of(OakNodeNode owner)
{
if (!owner) {
if (!owner.ctx) {
return NULL;
}
+2 -2
View File
@@ -54,9 +54,9 @@ olive::core::Rational rat(int64_t n, int64_t d)
} // namespace
OakTimelineWorkArea *oaktimeline_workarea_of(OakNodeNode *owner)
OakTimelineWorkArea *oaktimeline_workarea_of(OakNodeNode owner)
{
if (!owner) {
if (!owner.ctx) {
return NULL;
}
+6 -6
View File
@@ -28,36 +28,36 @@
namespace olive
{
inline bool node_can_be_removed(OakNodeNode *n)
inline bool node_can_be_removed(OakNodeNode n)
{
int count = 0;
oaknode_node_output_connection_count(n, &count);
return count == 0;
}
inline bool node_can_be_removed(OakNodeBlock *b)
inline bool node_can_be_removed(OakNodeBlock b)
{
return node_can_be_removed(oaknode_block_as_node(b));
}
inline OakUndoCommand create_remove_command(OakNodeNode *n)
inline OakUndoCommand create_remove_command(OakNodeNode n)
{
return oaknode_command_create_remove_node(n);
}
inline OakUndoCommand create_remove_command(OakNodeBlock *b)
inline OakUndoCommand create_remove_command(OakNodeBlock b)
{
return oaknode_command_create_remove_node(oaknode_block_as_node(b));
}
inline OakUndoCommand create_and_run_remove_command(OakNodeNode *n)
inline OakUndoCommand create_and_run_remove_command(OakNodeNode n)
{
OakUndoCommand command = create_remove_command(n);
oakundo_command_redo_now(command);
return command;
}
inline OakUndoCommand create_and_run_remove_command(OakNodeBlock *b)
inline OakUndoCommand create_and_run_remove_command(OakNodeBlock b)
{
return create_and_run_remove_command(oaknode_block_as_node(b));
}
+114 -114
View File
@@ -104,7 +104,7 @@ void BlockSetMediaInCommand::undo()
//
// TimelineAddTrackCommand
//
TimelineAddTrackCommand::TimelineAddTrackCommand(OakNodeTrackList *timeline)
TimelineAddTrackCommand::TimelineAddTrackCommand(OakNodeTrackList timeline)
: TimelineAddTrackCommand(
timeline,
oakcommon_config_get_bool(NULL, "AutoMergeTracks", 0) != 0)
@@ -112,10 +112,10 @@ TimelineAddTrackCommand::TimelineAddTrackCommand(OakNodeTrackList *timeline)
}
TimelineAddTrackCommand::TimelineAddTrackCommand(
OakNodeTrackList *timeline, bool automerge_tracks)
OakNodeTrackList timeline, bool automerge_tracks)
: timeline_(timeline)
, track_(nullptr)
, merge_(nullptr)
, track_{}
, merge_{}
, position_command_({})
, automerge_tracks_(automerge_tracks)
, track_orphaned_(false)
@@ -136,7 +136,7 @@ TimelineAddTrackCommand::TimelineAddTrackCommand(
// If we have an input to connect to, check if something is already connected
if (!direct_input_.empty() && automerge_tracks_) {
OakNodeSequence *sequence = nullptr;
OakNodeSequence sequence = {};
oaknode_tracklist_get_sequence(timeline_, &sequence);
int connected = 0;
@@ -155,7 +155,7 @@ TimelineAddTrackCommand::TimelineAddTrackCommand(
blend_input_ = k_math_param_b_input;
}
if (merge_) {
if (merge_.ctx) {
merge_orphaned_ = true;
}
}
@@ -167,27 +167,27 @@ TimelineAddTrackCommand::~TimelineAddTrackCommand()
if (position_command_.ctx) {
oakundo_command_free(&position_command_);
}
if (track_orphaned_ && track_) {
oaknode_track_free(track_);
if (track_orphaned_) {
free_detached_handle(&track_);
}
if (merge_orphaned_ && merge_) {
oaknode_node_free(merge_);
if (merge_orphaned_) {
free_detached_handle(&merge_);
}
}
void TimelineAddTrackCommand::redo()
{
// Get sequence
OakNodeSequence *sequence = nullptr;
OakNodeSequence sequence = {};
oaknode_tracklist_get_sequence(timeline_, &sequence);
OakNodeNode *sequence_node = oaknode_sequence_as_node(sequence);
OakNodeNode *track_node = oaknode_track_as_node(track_);
OakNodeNode sequence_node = oaknode_sequence_as_node(sequence);
OakNodeNode track_node = oaknode_track_as_node(track_);
OakNodeProject *project = nullptr;
OakNodeProject project = {};
oaknode_node_get_project(sequence_node, &project);
// Add track to sequence's graph
if (project) {
if (project.ctx) {
oaknode_project_add_node(project, track_node);
}
track_orphaned_ = false;
@@ -195,10 +195,10 @@ void TimelineAddTrackCommand::redo()
int track_count = 0;
oaknode_tracklist_get_track_count(timeline_, &track_count);
if (track_count > 0) {
OakNodeTrack *last = nullptr;
OakNodeTrack last = {};
oaknode_tracklist_get_track_at(timeline_, track_count - 1, &last);
double height = 0;
if (last && oaknode_track_get_height(last, &height) == OAKNODE_OK) {
if (last.ctx && oaknode_track_get_height(last, &height) == OAKNODE_OK) {
oaknode_track_set_height(track_, height);
}
}
@@ -228,15 +228,15 @@ void TimelineAddTrackCommand::redo()
}
// Add merge if applicable
if (merge_) {
if (merge_.ctx) {
// Determine what was previously connected
OakNodeNode *previous_connection = nullptr;
OakNodeNode previous_connection = {};
oaknode_node_input_get_connected_node(sequence_node,
direct_input_.c_str(),
&previous_connection);
// Add merge to graph
if (project) {
if (project.ctx) {
oaknode_project_add_node(project, merge_);
}
merge_orphaned_ = false;
@@ -244,7 +244,7 @@ void TimelineAddTrackCommand::redo()
// Connect merge between what used to be here
oaknode_node_disconnect(sequence_node, direct_input_.c_str());
oaknode_node_connect(merge_, sequence_node, direct_input_.c_str());
if (previous_connection) {
if (previous_connection.ctx) {
oaknode_node_connect(previous_connection, merge_,
base_input_.c_str());
}
@@ -315,38 +315,38 @@ void TimelineAddTrackCommand::undo()
oakundo_command_undo_now(position_command_);
}
OakNodeSequence *sequence = nullptr;
OakNodeSequence sequence = {};
oaknode_tracklist_get_sequence(timeline_, &sequence);
OakNodeNode *sequence_node = oaknode_sequence_as_node(sequence);
OakNodeNode *track_node = oaknode_track_as_node(track_);
OakNodeNode sequence_node = oaknode_sequence_as_node(sequence);
OakNodeNode track_node = oaknode_track_as_node(track_);
OakNodeProject *project = nullptr;
OakNodeProject project = {};
oaknode_node_get_project(sequence_node, &project);
// Remove merge if applicable
if (merge_) {
OakNodeNode *previous_connection = nullptr;
if (merge_.ctx) {
OakNodeNode previous_connection = {};
oaknode_node_input_get_connected_node(merge_, base_input_.c_str(),
&previous_connection);
oaknode_node_disconnect(merge_, blend_input_.c_str());
oaknode_node_disconnect(merge_, base_input_.c_str());
oaknode_node_disconnect(sequence_node, direct_input_.c_str());
if (previous_connection) {
if (previous_connection.ctx) {
oaknode_node_connect(previous_connection, sequence_node,
direct_input_.c_str());
}
if (project) {
if (project.ctx) {
oaknode_project_remove_node(project, merge_);
}
merge_orphaned_ = true;
} else if (!direct_input_.empty()) {
OakNodeNode *connected_output = nullptr;
OakNodeNode connected_output = {};
oaknode_node_input_get_connected_node(sequence_node,
direct_input_.c_str(),
&connected_output);
if (connected_output == track_node) {
if (same_node(connected_output, track_node)) {
oaknode_node_disconnect(sequence_node, direct_input_.c_str());
}
}
@@ -360,7 +360,7 @@ void TimelineAddTrackCommand::undo()
oaknode_node_disconnect_element(sequence_node, input_id, array_size - 1);
oaknode_tracklist_array_remove_last(timeline_);
if (project) {
if (project.ctx) {
oaknode_project_remove_node(project, track_node);
}
track_orphaned_ = true;
@@ -384,24 +384,24 @@ void TransitionRemoveCommand::redo()
int n, d;
if (in_block_) {
if (in_block_.ctx) {
oaknode_transition_get_in_offset(block_, &n, &d);
block_set_length_and_media_in(
in_block_, block_length(in_block_) + Rational(n, d));
}
if (out_block_) {
if (out_block_.ctx) {
oaknode_transition_get_out_offset(block_, &n, &d);
block_set_length_and_media_out(
out_block_, block_length(out_block_) + Rational(n, d));
}
if (in_block_) {
if (in_block_.ctx) {
oaknode_node_disconnect(oaknode_block_as_node(block_),
OAKNODE_TRANSITION_IN_BLOCK_INPUT);
}
if (out_block_) {
if (out_block_.ctx) {
oaknode_node_disconnect(oaknode_block_as_node(block_),
OAKNODE_TRANSITION_OUT_BLOCK_INPUT);
}
@@ -423,19 +423,19 @@ void TransitionRemoveCommand::undo()
oakundo_command_undo_now(remove_command_);
}
if (in_block_) {
if (in_block_.ctx) {
oaknode_track_insert_block_before(track_, block_, in_block_);
} else {
oaknode_track_insert_block_after(track_, block_, out_block_);
}
if (in_block_) {
if (in_block_.ctx) {
oaknode_node_connect(oaknode_block_as_node(in_block_),
oaknode_block_as_node(block_),
OAKNODE_TRANSITION_IN_BLOCK_INPUT);
}
if (out_block_) {
if (out_block_.ctx) {
oaknode_node_connect(oaknode_block_as_node(out_block_),
oaknode_block_as_node(block_),
OAKNODE_TRANSITION_OUT_BLOCK_INPUT);
@@ -446,13 +446,13 @@ void TransitionRemoveCommand::undo()
// These if statements must be separated because in_offset and out_offset report different things
// if only one block is connected vs two. So we have to connect the blocks first before we have
// an accurate return value from these offset functions.
if (in_block_) {
if (in_block_.ctx) {
oaknode_transition_get_in_offset(block_, &n, &d);
block_set_length_and_media_in(
in_block_, block_length(in_block_) - Rational(n, d));
}
if (out_block_) {
if (out_block_.ctx) {
oaknode_transition_get_out_offset(block_, &n, &d);
block_set_length_and_media_out(
out_block_, block_length(out_block_) - Rational(n, d));
@@ -466,8 +466,8 @@ TrackListInsertGaps::~TrackListInsertGaps()
{
delete split_command_;
for (AddGap &add_gap : gaps_added_) {
if (add_gap.orphaned && add_gap.gap) {
oaknode_block_free(add_gap.gap);
if (add_gap.orphaned) {
free_detached_handle(&add_gap.gap);
}
}
}
@@ -478,9 +478,9 @@ void TrackListInsertGaps::prepare()
int track_count = 0;
oaknode_tracklist_get_track_count(track_list_, &track_count);
for (int i = 0; i < track_count; i++) {
OakNodeTrack *track = nullptr;
OakNodeTrack track = {};
oaknode_tracklist_get_track_at(track_list_, i, &track);
if (!track) {
if (!track.ctx) {
continue;
}
@@ -493,17 +493,17 @@ void TrackListInsertGaps::prepare()
working_tracks_.push_back(track);
}
std::vector<OakNodeBlock *> blocks_to_split;
std::vector<OakNodeBlock *> blocks_to_append_gap_to;
std::vector<OakNodeTrack *> tracks_to_append_gap_to;
std::vector<OakNodeBlock> blocks_to_split;
std::vector<OakNodeBlock> blocks_to_append_gap_to;
std::vector<OakNodeTrack> tracks_to_append_gap_to;
for (OakNodeTrack *track : working_tracks_) {
for (OakNodeTrack track : working_tracks_) {
int block_count = 0;
oaknode_track_get_block_count(track, &block_count);
for (int i = 0; i < block_count; i++) {
OakNodeBlock *b = nullptr;
OakNodeBlock b = {};
oaknode_track_get_block_at(track, i, &b);
if (!b) {
if (!b.ctx) {
continue;
}
@@ -521,11 +521,11 @@ void TrackListInsertGaps::prepare()
if (block_in(b) == point_) {
// The only reason we should be here is if this block is at the start of the track,
// in which case no split needs to occur
b = nullptr;
b = OakNodeBlock{};
} else if (block_out(b) > point_) {
// Block must be split as well as having a gap appended to it
blocks_to_split.push_back(b);
} else if (!block_next(b)) {
} else if (!block_next(b).ctx) {
// At the end of a track, no gap needs to be added at all
append_gap = false;
}
@@ -545,7 +545,7 @@ void TrackListInsertGaps::prepare()
}
for (size_t i = 0; i < blocks_to_append_gap_to.size(); i++) {
OakNodeBlock *gap = oaknode_block_gap_create();
OakNodeBlock gap = oaknode_block_gap_create();
block_set_length_and_media_out(gap, length_);
gaps_added_.push_back({ gap, true, blocks_to_append_gap_to.at(i),
tracks_to_append_gap_to.at(i) });
@@ -554,7 +554,7 @@ void TrackListInsertGaps::prepare()
void TrackListInsertGaps::redo()
{
for (OakNodeBlock *gap : gaps_to_extend_) {
for (OakNodeBlock gap : gaps_to_extend_) {
block_set_length_and_media_out(gap, block_length(gap) + length_);
}
@@ -574,8 +574,8 @@ void TrackListInsertGaps::undo()
{
// Remove added gaps
for (AddGap &add_gap : gaps_added_) {
OakNodeTrack *t = block_track(add_gap.gap);
if (t) {
OakNodeTrack t = block_track(add_gap.gap);
if (t.ctx) {
oaknode_track_ripple_remove_block(t, add_gap.gap);
block_remove_from_graph(add_gap.gap, t);
}
@@ -588,7 +588,7 @@ void TrackListInsertGaps::undo()
}
// Restore original length of gaps
for (OakNodeBlock *gap : gaps_to_extend_) {
for (OakNodeBlock gap : gaps_to_extend_) {
block_set_length_and_media_out(gap, block_length(gap) - length_);
}
}
@@ -601,11 +601,11 @@ TrackReplaceBlockWithGapCommand::~TrackReplaceBlockWithGapCommand()
for (TransitionRemoveCommand *c : transition_remove_commands_) {
delete c;
}
if (our_gap_ && our_gap_orphaned_) {
oaknode_block_free(our_gap_);
if (our_gap_orphaned_) {
free_detached_handle(&our_gap_);
}
if (existing_merged_gap_ && merged_gap_orphaned_) {
oaknode_block_free(existing_merged_gap_);
if (merged_gap_orphaned_) {
free_detached_handle(&existing_merged_gap_);
}
}
@@ -620,19 +620,19 @@ void TrackReplaceBlockWithGapCommand::redo()
c->redo_now();
}
if (block_next(block_)) {
if (block_next(block_).ctx) {
// Block has a next, which means it's NOT at the end of the sequence and thus requires a gap
Rational new_gap_length = block_length(block_);
OakNodeBlock *previous = block_previous(block_);
OakNodeBlock *next = block_next(block_);
OakNodeBlock previous = block_previous(block_);
OakNodeBlock next = block_next(block_);
int prev_kind = OAKNODE_BLOCK_OTHER;
int next_kind = OAKNODE_BLOCK_OTHER;
if (previous) {
if (previous.ctx) {
oaknode_block_get_kind(previous, &prev_kind);
}
if (next) {
if (next.ctx) {
oaknode_block_get_kind(next, &next_kind);
}
bool previous_is_a_gap = (prev_kind == OAKNODE_BLOCK_GAP);
@@ -655,16 +655,16 @@ void TrackReplaceBlockWithGapCommand::redo()
existing_gap_ = next;
}
if (existing_gap_) {
if (existing_gap_.ctx) {
// Extend an existing gap
new_gap_length += block_length(existing_gap_);
block_set_length_and_media_out(existing_gap_, new_gap_length);
oaknode_track_ripple_remove_block(track_, block_);
existing_gap_precedes_ = (existing_gap_ == previous);
existing_gap_precedes_ = same_block(existing_gap_, previous);
} else {
// No gap exists to fill this space, create a new one and swap it in
if (!our_gap_) {
if (!our_gap_.ctx) {
our_gap_ = oaknode_block_gap_create();
block_set_length_and_media_out(our_gap_, new_gap_length);
our_gap_orphaned_ = true;
@@ -677,12 +677,12 @@ void TrackReplaceBlockWithGapCommand::redo()
} else {
// Block is at the end of the track, simply remove it
OakNodeBlock *preceding = block_previous(block_);
OakNodeBlock preceding = block_previous(block_);
oaknode_track_ripple_remove_block(track_, block_);
// Determine if it's preceded by a gap, and remove that gap if so
int kind = OAKNODE_BLOCK_OTHER;
if (preceding) {
if (preceding.ctx) {
oaknode_block_get_kind(preceding, &kind);
}
if (kind == OAKNODE_BLOCK_GAP) {
@@ -697,8 +697,8 @@ void TrackReplaceBlockWithGapCommand::redo()
void TrackReplaceBlockWithGapCommand::undo()
{
if (our_gap_ || existing_gap_) {
if (our_gap_) {
if (our_gap_.ctx || existing_gap_.ctx) {
if (our_gap_.ctx) {
// We made this gap, simply swap our gap back
oaknode_track_replace_block(track_, our_gap_, block_);
block_remove_from_graph(our_gap_, track_);
@@ -710,13 +710,13 @@ void TrackReplaceBlockWithGapCommand::undo()
block_length(existing_gap_) - block_length(block_);
// If we merged two gaps together, restore the second one now
if (existing_merged_gap_) {
if (existing_merged_gap_.ctx) {
original_gap_length -= block_length(existing_merged_gap_);
block_add_to_graph(existing_merged_gap_, track_);
oaknode_track_insert_block_after(track_, existing_merged_gap_,
existing_gap_);
merged_gap_orphaned_ = false;
existing_merged_gap_ = nullptr;
existing_merged_gap_ = OakNodeBlock{};
}
// Restore original block
@@ -732,7 +732,7 @@ void TrackReplaceBlockWithGapCommand::undo()
block_set_length_and_media_out(existing_gap_,
original_gap_length);
existing_gap_ = nullptr;
existing_gap_ = OakNodeBlock{};
}
} else {
@@ -740,11 +740,11 @@ void TrackReplaceBlockWithGapCommand::undo()
// required no gap extension/replacement
// However, we may have removed an unnecessary gap that preceded it
if (existing_merged_gap_) {
if (existing_merged_gap_.ctx) {
block_add_to_graph(existing_merged_gap_, track_);
oaknode_track_append_block(track_, existing_merged_gap_);
merged_gap_orphaned_ = false;
existing_merged_gap_ = nullptr;
existing_merged_gap_ = OakNodeBlock{};
}
// Restore block
@@ -760,25 +760,25 @@ void TrackReplaceBlockWithGapCommand::undo()
void TrackReplaceBlockWithGapCommand::create_remove_transition_command_if_necessary(
bool next)
{
OakNodeBlock *relevant_block =
OakNodeBlock relevant_block =
next ? block_next(block_) : block_previous(block_);
int kind = OAKNODE_BLOCK_OTHER;
if (relevant_block) {
if (relevant_block.ctx) {
oaknode_block_get_kind(relevant_block, &kind);
}
if (kind != OAKNODE_BLOCK_TRANSITION) {
return;
}
OakNodeBlock *connected_out = nullptr;
OakNodeBlock *connected_in = nullptr;
OakNodeBlock connected_out = {};
OakNodeBlock connected_in = {};
oaknode_transition_get_connected_out_block(relevant_block,
&connected_out);
oaknode_transition_get_connected_in_block(relevant_block, &connected_in);
if ((next && connected_out == block_ && !connected_in) ||
(!next && connected_in == block_ && !connected_out)) {
if ((next && same_block(connected_out, block_) && !connected_in.ctx) ||
(!next && same_block(connected_in, block_) && !connected_out.ctx)) {
transition_remove_commands_.push_back(
new TransitionRemoveCommand(relevant_block, true));
}
@@ -796,7 +796,7 @@ TimelineRemoveTrackCommand::~TimelineRemoveTrackCommand()
void TimelineRemoveTrackCommand::prepare()
{
OakNodeSequence *sequence = nullptr;
OakNodeSequence sequence = {};
oaknode_track_get_sequence(track_, &sequence);
int type = OAKNODE_TRACK_TYPE_NONE;
@@ -815,7 +815,7 @@ void TimelineRemoveTrackCommand::redo()
{
oakundo_command_redo_now(remove_command_);
OakNodeSequence *sequence = nullptr;
OakNodeSequence sequence = {};
oaknode_tracklist_get_sequence(list_, &sequence);
char input_id[64];
@@ -826,7 +826,7 @@ void TimelineRemoveTrackCommand::redo()
void TimelineRemoveTrackCommand::undo()
{
OakNodeSequence *sequence = nullptr;
OakNodeSequence sequence = {};
oaknode_tracklist_get_sequence(list_, &sequence);
char input_id[64];
@@ -860,16 +860,16 @@ std::string config_get_string(const char *key)
void TimelineAddDefaultTransitionCommand::prepare()
{
for (OakNodeBlock *c : clips_) {
OakNodeBlock *previous = block_previous(c);
OakNodeBlock *next = block_next(c);
for (OakNodeBlock c : clips_) {
OakNodeBlock previous = block_previous(c);
OakNodeBlock next = block_next(c);
auto is_clip_in_selection = [this](OakNodeBlock *b) {
if (!b) {
auto is_clip_in_selection = [this](OakNodeBlock b) {
if (!b.ctx) {
return false;
}
for (OakNodeBlock *clip : clips_) {
if (clip == b) {
for (OakNodeBlock clip : clips_) {
if (same_block(clip, b)) {
return true;
}
}
@@ -878,17 +878,17 @@ void TimelineAddDefaultTransitionCommand::prepare()
int prev_kind = OAKNODE_BLOCK_OTHER;
int next_kind = OAKNODE_BLOCK_OTHER;
if (previous) {
if (previous.ctx) {
oaknode_block_get_kind(previous, &prev_kind);
}
if (next) {
if (next.ctx) {
oaknode_block_get_kind(next, &next_kind);
}
// Handle in transition
if (is_clip_in_selection(previous)) {
// Do nothing, assume this will be handled by a dual transition from that clip
} else if (prev_kind == OAKNODE_BLOCK_GAP || !previous) {
} else if (prev_kind == OAKNODE_BLOCK_GAP || !previous.ctx) {
// Create in transition
add_transition(c, k_in);
}
@@ -896,7 +896,7 @@ void TimelineAddDefaultTransitionCommand::prepare()
// Handle out transition
if (is_clip_in_selection(next)) {
add_transition(c, k_out_dual);
} else if (next_kind == OAKNODE_BLOCK_GAP || !next) {
} else if (next_kind == OAKNODE_BLOCK_GAP || !next.ctx) {
// Create out transition
add_transition(c, k_out);
}
@@ -904,17 +904,17 @@ void TimelineAddDefaultTransitionCommand::prepare()
}
void TimelineAddDefaultTransitionCommand::add_transition(
OakNodeBlock *c, CreateTransitionMode mode)
OakNodeBlock c, CreateTransitionMode mode)
{
OakNodeTrack *t = block_track(c);
if (!t) {
OakNodeTrack t = block_track(c);
if (!t.ctx) {
return;
}
int type = OAKNODE_TRACK_TYPE_NONE;
oaknode_track_get_type(t, &type);
OakNodeNode *p = nullptr;
OakNodeNode p = {};
if (type == OAKNODE_TRACK_TYPE_VIDEO) {
std::string id = config_get_string("DefaultVideoTransition");
if (!id.empty()) {
@@ -964,20 +964,20 @@ void TimelineAddDefaultTransitionCommand::add_transition(
}
if (transition_length <= 0) {
if (p) {
oaknode_node_free(p);
if (p.ctx) {
oaknode_node_free(&p);
}
return;
}
OakNodeBlock *transition = oaknode_block_from_node(p);
OakNodeBlock transition = oaknode_block_from_node(p);
int kind = OAKNODE_BLOCK_OTHER;
if (transition) {
if (transition.ctx) {
oaknode_block_get_kind(transition, &kind);
}
if (kind != OAKNODE_BLOCK_TRANSITION) {
if (p) {
oaknode_node_free(p);
if (p.ctx) {
oaknode_node_free(&p);
}
return;
}
@@ -985,13 +985,13 @@ void TimelineAddDefaultTransitionCommand::add_transition(
block_set_length_and_media_out(transition, transition_length);
// Add transition
OakNodeProject *project = nullptr;
OakNodeProject project = {};
oaknode_node_get_project(oaknode_block_as_node(c), &project);
commands_.push_back(
new CHandleCommandWrapper(oaknode_command_create_add_node(project, p)));
// Insert block
OakNodeBlock *insert_after = (mode == k_in) ? block_previous(c) : c;
OakNodeBlock insert_after = (mode == k_in) ? block_previous(c) : c;
commands_.push_back(
new TrackInsertBlockAfterCommand(t, transition, insert_after));
@@ -1027,7 +1027,7 @@ void TimelineAddDefaultTransitionCommand::add_transition(
}
void TimelineAddDefaultTransitionCommand::adjust_clip_length(
OakNodeBlock *c, const Rational &transition_length, bool out)
OakNodeBlock c, const Rational &transition_length, bool out)
{
Rational cur_len = lengths_.count(c) ? lengths_[c] : block_length(c);
Rational new_len = cur_len - transition_length;
@@ -1040,9 +1040,9 @@ void TimelineAddDefaultTransitionCommand::adjust_clip_length(
}
void TimelineAddDefaultTransitionCommand::validate_transition_length(
OakNodeBlock *c, Rational &transition_length)
OakNodeBlock c, Rational &transition_length)
{
if (!c) {
if (!c.ctx) {
return;
}
Rational cur_len = lengths_.count(c) ? lengths_[c] : block_length(c);
+53 -51
View File
@@ -29,9 +29,11 @@
#include <olive/core/core.h>
#include "node/block.h"
#include "node/node.h"
#include "node/sequence.h"
#include "node/track.h"
#include "timelineundosplit.h"
#include "timelineutil.h"
#include "undocommand.h"
using namespace olive::core;
@@ -41,7 +43,7 @@ namespace olive
class BlockResizeCommand : public UndoCommand {
public:
BlockResizeCommand(OakNodeBlock *block, Rational new_length)
BlockResizeCommand(OakNodeBlock block, Rational new_length)
: block_(block)
, new_length_(new_length)
{
@@ -52,14 +54,14 @@ protected:
virtual void undo() override;
private:
OakNodeBlock *block_;
OakNodeBlock block_;
Rational old_length_;
Rational new_length_;
};
class BlockResizeWithMediaInCommand : public UndoCommand {
public:
BlockResizeWithMediaInCommand(OakNodeBlock *block, Rational new_length)
BlockResizeWithMediaInCommand(OakNodeBlock block, Rational new_length)
: block_(block)
, new_length_(new_length)
{
@@ -70,14 +72,14 @@ protected:
virtual void undo();
private:
OakNodeBlock *block_;
OakNodeBlock block_;
Rational old_length_;
Rational new_length_;
};
class BlockSetMediaInCommand : public UndoCommand {
public:
BlockSetMediaInCommand(OakNodeBlock *block, Rational new_media_in)
BlockSetMediaInCommand(OakNodeBlock block, Rational new_media_in)
: block_(block)
, new_media_in_(new_media_in)
{
@@ -88,34 +90,34 @@ protected:
virtual void undo();
private:
OakNodeBlock *block_;
OakNodeBlock block_;
Rational old_media_in_;
Rational new_media_in_;
};
class TimelineAddTrackCommand : public UndoCommand {
public:
TimelineAddTrackCommand(OakNodeTrackList *timeline);
TimelineAddTrackCommand(OakNodeTrackList *timeline, bool automerge_tracks);
TimelineAddTrackCommand(OakNodeTrackList timeline);
TimelineAddTrackCommand(OakNodeTrackList timeline, bool automerge_tracks);
virtual ~TimelineAddTrackCommand() override;
static OakNodeTrack *run_immediately(OakNodeTrackList *timeline)
static OakNodeTrack run_immediately(OakNodeTrackList timeline)
{
TimelineAddTrackCommand c(timeline);
c.redo();
return c.track();
}
static OakNodeTrack *run_immediately(OakNodeTrackList *timeline,
bool automerge)
static OakNodeTrack run_immediately(OakNodeTrackList timeline,
bool automerge)
{
TimelineAddTrackCommand c(timeline, automerge);
c.redo();
return c.track();
}
OakNodeTrack *track() const
OakNodeTrack track() const
{
return track_;
}
@@ -126,10 +128,10 @@ protected:
virtual void undo() override;
private:
OakNodeTrackList *timeline_;
OakNodeTrackList timeline_;
OakNodeTrack *track_;
OakNodeNode *merge_;
OakNodeTrack track_;
OakNodeNode merge_;
std::string base_input_;
std::string blend_input_;
@@ -147,9 +149,9 @@ private:
class TimelineRemoveTrackCommand : public UndoCommand {
public:
TimelineRemoveTrackCommand(OakNodeTrack *track)
TimelineRemoveTrackCommand(OakNodeTrack track)
: track_(track)
, list_(nullptr)
, list_{}
, index_(0)
, remove_command_({})
{
@@ -165,9 +167,9 @@ protected:
virtual void undo() override;
private:
OakNodeTrack *track_;
OakNodeTrack track_;
OakNodeTrackList *list_;
OakNodeTrackList list_;
int index_;
@@ -176,11 +178,11 @@ private:
class TransitionRemoveCommand : public UndoCommand {
public:
TransitionRemoveCommand(OakNodeBlock *block, bool remove_from_graph)
TransitionRemoveCommand(OakNodeBlock block, bool remove_from_graph)
: block_(block)
, track_(nullptr)
, out_block_(nullptr)
, in_block_(nullptr)
, track_{}
, out_block_{}
, in_block_{}
, remove_from_graph_(remove_from_graph)
, remove_command_({})
{
@@ -194,12 +196,12 @@ protected:
virtual void undo() override;
private:
OakNodeBlock *block_;
OakNodeBlock block_;
OakNodeTrack *track_;
OakNodeTrack track_;
OakNodeBlock *out_block_;
OakNodeBlock *in_block_;
OakNodeBlock out_block_;
OakNodeBlock in_block_;
bool remove_from_graph_;
OakUndoCommand remove_command_;
@@ -207,14 +209,14 @@ private:
class TrackReplaceBlockWithGapCommand : public UndoCommand {
public:
TrackReplaceBlockWithGapCommand(OakNodeTrack *track, OakNodeBlock *block,
TrackReplaceBlockWithGapCommand(OakNodeTrack track, OakNodeBlock block,
bool handle_transitions = true)
: track_(track)
, block_(block)
, existing_gap_(nullptr)
, existing_merged_gap_(nullptr)
, existing_gap_{}
, existing_merged_gap_{}
, existing_gap_precedes_(false)
, our_gap_(nullptr)
, our_gap_{}
, handle_transitions_(handle_transitions)
, our_gap_orphaned_(false)
, merged_gap_orphaned_(false)
@@ -231,13 +233,13 @@ protected:
private:
void create_remove_transition_command_if_necessary(bool next);
OakNodeTrack *track_;
OakNodeBlock *block_;
OakNodeTrack track_;
OakNodeBlock block_;
OakNodeBlock *existing_gap_;
OakNodeBlock *existing_merged_gap_;
OakNodeBlock existing_gap_;
OakNodeBlock existing_merged_gap_;
bool existing_gap_precedes_;
OakNodeBlock *our_gap_;
OakNodeBlock our_gap_;
bool handle_transitions_;
@@ -249,7 +251,7 @@ private:
class BlockEnableDisableCommand : public UndoCommand {
public:
BlockEnableDisableCommand(OakNodeBlock *block, bool enabled)
BlockEnableDisableCommand(OakNodeBlock block, bool enabled)
: block_(block)
, new_enabled_(enabled)
{
@@ -269,7 +271,7 @@ protected:
}
private:
OakNodeBlock *block_;
OakNodeBlock block_;
int old_enabled_;
@@ -278,7 +280,7 @@ private:
class TrackListInsertGaps : public UndoCommand {
public:
TrackListInsertGaps(OakNodeTrackList *track_list, const Rational &point,
TrackListInsertGaps(OakNodeTrackList track_list, const Rational &point,
const Rational &length)
: track_list_(track_list)
, point_(point)
@@ -297,21 +299,21 @@ protected:
virtual void undo() override;
private:
OakNodeTrackList *track_list_;
OakNodeTrackList track_list_;
Rational point_;
Rational length_;
std::vector<OakNodeTrack *> working_tracks_;
std::vector<OakNodeTrack> working_tracks_;
std::vector<OakNodeBlock *> gaps_to_extend_;
std::vector<OakNodeBlock> gaps_to_extend_;
struct AddGap {
OakNodeBlock *gap;
OakNodeBlock gap;
bool orphaned;
OakNodeBlock *before;
OakNodeTrack *track;
OakNodeBlock before;
OakNodeTrack track;
};
std::vector<AddGap> gaps_added_;
@@ -322,7 +324,7 @@ private:
class TimelineAddDefaultTransitionCommand : public UndoCommand {
public:
TimelineAddDefaultTransitionCommand(
const std::vector<OakNodeBlock *> &clips, const Rational &timebase)
const std::vector<OakNodeBlock> &clips, const Rational &timebase)
: clips_(clips)
, timebase_(timebase)
{
@@ -355,17 +357,17 @@ protected:
private:
enum CreateTransitionMode { k_in, k_out, k_out_dual };
void add_transition(OakNodeBlock *c, CreateTransitionMode mode);
void adjust_clip_length(OakNodeBlock *c, const Rational &transition_length,
void add_transition(OakNodeBlock c, CreateTransitionMode mode);
void adjust_clip_length(OakNodeBlock c, const Rational &transition_length,
bool out);
void validate_transition_length(OakNodeBlock *c,
void validate_transition_length(OakNodeBlock c,
Rational &transition_length);
std::vector<OakNodeBlock *> clips_;
std::vector<OakNodeBlock> clips_;
Rational timebase_;
std::vector<UndoCommand *> commands_;
std::map<OakNodeBlock *, Rational> lengths_;
std::map<OakNodeBlock, Rational, BlockHandleLess> lengths_;
};
}
+20 -20
View File
@@ -36,8 +36,8 @@ BlockTrimCommand::~BlockTrimCommand()
if (deleted_adjacent_command_.ctx) {
free_command_handle(&deleted_adjacent_command_);
}
if (adjacent_orphaned_ && adjacent_) {
oaknode_block_free(adjacent_);
if (adjacent_orphaned_) {
free_detached_handle(&adjacent_);
}
}
@@ -148,7 +148,7 @@ void BlockTrimCommand::prepare()
// Will be POSITIVE if trimming shorter and NEGATIVE if trimming longer
trim_diff_ = old_length_ - new_length_;
// Retrieve our adjacent block (or nullptr if none)
// Retrieve our adjacent block (or an empty handle if none)
if (mode_ == Timeline::k_trim_in) {
adjacent_ = block_previous(block_);
} else {
@@ -157,16 +157,16 @@ void BlockTrimCommand::prepare()
// Ignore when trimming the out with no adjacent, because the user must have trimmed the end
// of the last block in the track, so we don't need to do anything elses
needs_adjacent_ = (mode_ == Timeline::k_trim_in || adjacent_);
needs_adjacent_ = (mode_ == Timeline::k_trim_in || adjacent_.ctx);
if (needs_adjacent_) {
// If we're trimming shorter, we need an adjacent, so check if we have a viable one.
int adjacent_kind = OAKNODE_BLOCK_OTHER;
if (adjacent_) {
if (adjacent_.ctx) {
oaknode_block_get_kind(adjacent_, &adjacent_kind);
}
we_created_adjacent_ =
(trim_diff_ > 0 && (!adjacent_ || (adjacent_kind !=
(trim_diff_ > 0 && (!adjacent_.ctx || (adjacent_kind !=
OAKNODE_BLOCK_GAP &&
!trim_is_a_roll_edit_)));
@@ -194,11 +194,11 @@ TrackSlideCommand::~TrackSlideCommand()
if (out_adjacent_remove_command_.ctx) {
free_command_handle(&out_adjacent_remove_command_);
}
if (in_adjacent_orphaned_ && in_adjacent_) {
oaknode_block_free(in_adjacent_);
if (in_adjacent_orphaned_) {
free_detached_handle(&in_adjacent_);
}
if (out_adjacent_orphaned_ && out_adjacent_) {
oaknode_block_free(out_adjacent_);
if (out_adjacent_orphaned_) {
free_detached_handle(&out_adjacent_);
}
}
@@ -232,7 +232,7 @@ void TrackSlideCommand::redo()
}
// We may not have an out adjacent if the slide was at the end of the track
if (out_adjacent_) {
if (out_adjacent_.ctx) {
if (we_created_out_adjacent_) {
// We created out adjacent, so we just have to insert it
block_add_to_graph(out_adjacent_, track_);
@@ -283,7 +283,7 @@ void TrackSlideCommand::undo()
in_adjacent_, block_length(in_adjacent_) - movement_);
}
if (out_adjacent_) {
if (out_adjacent_.ctx) {
if (we_created_out_adjacent_) {
// We created this, so we can remove it now
oaknode_track_ripple_remove_block(track_, out_adjacent_);
@@ -307,7 +307,7 @@ void TrackSlideCommand::undo()
void TrackSlideCommand::prepare()
{
if (!in_adjacent_) {
if (!in_adjacent_.ctx) {
in_adjacent_ = oaknode_block_gap_create();
block_set_length_and_media_out(in_adjacent_, movement_);
in_adjacent_orphaned_ = true;
@@ -316,7 +316,7 @@ void TrackSlideCommand::prepare()
we_created_in_adjacent_ = false;
}
if (!out_adjacent_ && block_next(blocks_.back())) {
if (!out_adjacent_.ctx && block_next(blocks_.back()).ctx) {
out_adjacent_ = oaknode_block_gap_create();
block_set_length_and_media_out(out_adjacent_, -movement_);
out_adjacent_orphaned_ = true;
@@ -335,8 +335,8 @@ TrackPlaceBlockCommand::~TrackPlaceBlockCommand()
for (TimelineAddTrackCommand *c : add_track_commands_) {
delete c;
}
if (gap_orphaned_ && gap_) {
oaknode_block_free(gap_);
if (gap_orphaned_) {
free_detached_handle(&gap_);
}
}
@@ -362,7 +362,7 @@ void TrackPlaceBlockCommand::redo()
}
}
OakNodeTrack *track = nullptr;
OakNodeTrack track = {};
oaknode_tracklist_get_track_at(timeline_, track_index_, &track);
bool append = (in_ >= track_length(track));
@@ -371,7 +371,7 @@ void TrackPlaceBlockCommand::redo()
if (append) {
if (in_ > track_length(track)) {
// If so, insert a gap here
if (!gap_) {
if (!gap_.ctx) {
gap_ = oaknode_block_gap_create();
block_set_length_and_media_out(gap_,
in_ - track_length(track));
@@ -398,7 +398,7 @@ void TrackPlaceBlockCommand::redo()
void TrackPlaceBlockCommand::undo()
{
OakNodeTrack *t = nullptr;
OakNodeTrack t = {};
oaknode_tracklist_get_track_at(timeline_, track_index_, &t);
// Firstly, remove our insert
@@ -407,7 +407,7 @@ void TrackPlaceBlockCommand::undo()
if (ripple_remove_command_) {
// If we ripple removed, just undo that
ripple_remove_command_->undo_now();
} else if (gap_) {
} else if (gap_.ctx) {
oaknode_track_ripple_remove_block(t, gap_);
block_remove_from_graph(gap_, t);
gap_orphaned_ = true;
+18 -18
View File
@@ -50,7 +50,7 @@ namespace olive
*/
class BlockTrimCommand : public UndoCommand {
public:
BlockTrimCommand(OakNodeTrack *track, OakNodeBlock *block,
BlockTrimCommand(OakNodeTrack track, OakNodeBlock block,
Rational new_length, Timeline::MovementMode mode)
: track_(track)
, block_(block)
@@ -60,7 +60,7 @@ public:
, trim_is_a_roll_edit_(false)
, remove_block_from_graph_(true)
, doing_nothing_(false)
, adjacent_(nullptr)
, adjacent_{}
, needs_adjacent_(false)
, we_created_adjacent_(false)
, we_removed_adjacent_(false)
@@ -99,13 +99,13 @@ private:
bool doing_nothing_;
Rational trim_diff_;
OakNodeTrack *track_;
OakNodeBlock *block_;
OakNodeTrack track_;
OakNodeBlock block_;
Rational old_length_;
Rational new_length_;
Timeline::MovementMode mode_;
OakNodeBlock *adjacent_;
OakNodeBlock adjacent_;
bool needs_adjacent_;
bool we_created_adjacent_;
bool we_removed_adjacent_;
@@ -120,9 +120,9 @@ private:
class TrackSlideCommand : public UndoCommand {
public:
TrackSlideCommand(OakNodeTrack *track,
const std::vector<OakNodeBlock *> &moving_blocks,
OakNodeBlock *in_adjacent, OakNodeBlock *out_adjacent,
TrackSlideCommand(OakNodeTrack track,
const std::vector<OakNodeBlock> &moving_blocks,
OakNodeBlock in_adjacent, OakNodeBlock out_adjacent,
const Rational &movement)
: track_(track)
, blocks_(moving_blocks)
@@ -150,18 +150,18 @@ protected:
virtual void undo() override;
private:
OakNodeTrack *track_;
std::vector<OakNodeBlock *> blocks_;
OakNodeTrack track_;
std::vector<OakNodeBlock> blocks_;
Rational movement_;
bool we_created_in_adjacent_;
bool we_removed_in_adjacent_;
OakNodeBlock *in_adjacent_;
OakNodeBlock in_adjacent_;
OakUndoCommand in_adjacent_remove_command_;
bool in_adjacent_orphaned_;
bool we_created_out_adjacent_;
bool we_removed_out_adjacent_;
OakNodeBlock *out_adjacent_;
OakNodeBlock out_adjacent_;
OakUndoCommand out_adjacent_remove_command_;
bool out_adjacent_orphaned_;
};
@@ -175,12 +175,12 @@ private:
*/
class TrackPlaceBlockCommand : public UndoCommand {
public:
TrackPlaceBlockCommand(OakNodeTrackList *timeline, int track,
OakNodeBlock *block, Rational in)
TrackPlaceBlockCommand(OakNodeTrackList timeline, int track,
OakNodeBlock block, Rational in)
: timeline_(timeline)
, track_index_(track)
, in_(in)
, gap_(nullptr)
, gap_{}
, gap_orphaned_(false)
, insert_(block)
, ripple_remove_command_(nullptr)
@@ -195,12 +195,12 @@ protected:
virtual void undo() override;
private:
OakNodeTrackList *timeline_;
OakNodeTrackList timeline_;
int track_index_;
Rational in_;
OakNodeBlock *gap_;
OakNodeBlock gap_;
bool gap_orphaned_;
OakNodeBlock *insert_;
OakNodeBlock insert_;
std::vector<TimelineAddTrackCommand *> add_track_commands_;
TrackRippleRemoveAreaCommand *ripple_remove_command_;
};
+46 -45
View File
@@ -35,15 +35,15 @@ namespace olive
// TrackRippleRemoveAreaCommand
//
TrackRippleRemoveAreaCommand::TrackRippleRemoveAreaCommand(
OakNodeTrack *track, const TimeRange &range)
OakNodeTrack track, const TimeRange &range)
: track_(track)
, range_(range)
, insert_previous_(nullptr)
, insert_previous_{}
, allow_splitting_gaps_(false)
, splice_split_command_(nullptr)
{
trim_out_.block = nullptr;
trim_in_.block = nullptr;
trim_out_.block = OakNodeBlock{};
trim_in_.block = OakNodeBlock{};
}
TrackRippleRemoveAreaCommand::~TrackRippleRemoveAreaCommand()
@@ -60,10 +60,10 @@ void TrackRippleRemoveAreaCommand::prepare()
rat_nd(range_.in(), &n, &d);
// Determine precisely what will be happening to these tracks
OakNodeBlock *first_block = nullptr;
OakNodeBlock first_block = {};
oaknode_track_get_nearest_block_before_or_at(track_, n, d, &first_block);
if (!first_block) {
if (!first_block.ctx) {
// No blocks at this time, nothing to be done on this track
return;
}
@@ -111,7 +111,7 @@ void TrackRippleRemoveAreaCommand::prepare()
// If the first block is getting in trimmed, we're already at the end of our range
if (!first_block_is_in_trimmed) {
// Loop through the rest of the blocks and determine what to do with those
for (OakNodeBlock *next = block_next(first_block); next;
for (OakNodeBlock next = block_next(first_block); next.ctx;
next = block_next(next)) {
bool trimming = (block_out(next) > range_.out());
@@ -140,16 +140,16 @@ void TrackRippleRemoveAreaCommand::redo()
splice_split_command_->redo_now();
// Trim the in of the split
OakNodeBlock *split = splice_split_command_->new_block();
OakNodeBlock split = splice_split_command_->new_block();
block_set_length_and_media_in(
split, block_length(split) - (range_.out() - block_in(split)));
} else {
if (trim_out_.block) {
if (trim_out_.block.ctx) {
block_set_length_and_media_out(trim_out_.block,
trim_out_.new_length);
}
if (trim_in_.block) {
if (trim_in_.block.ctx) {
block_set_length_and_media_in(trim_in_.block, trim_in_.new_length);
}
@@ -182,12 +182,12 @@ void TrackRippleRemoveAreaCommand::undo()
if (splice_split_command_) {
splice_split_command_->undo_now();
} else {
if (trim_out_.block) {
if (trim_out_.block.ctx) {
block_set_length_and_media_out(trim_out_.block,
trim_out_.old_length);
}
if (trim_in_.block) {
if (trim_in_.block.ctx) {
block_set_length_and_media_in(trim_in_.block, trim_in_.old_length);
}
@@ -212,9 +212,9 @@ void TrackListRippleRemoveAreaCommand::prepare()
oaknode_tracklist_get_track_count(list_, &count);
for (int i = 0; i < count; i++) {
OakNodeTrack *track = nullptr;
OakNodeTrack track = {};
oaknode_tracklist_get_track_at(list_, i, &track);
if (!track) {
if (!track.ctx) {
continue;
}
@@ -249,12 +249,12 @@ void TrackListRippleRemoveAreaCommand::undo()
// TimelineRippleRemoveAreaCommand
//
TimelineRippleRemoveAreaCommand::TimelineRippleRemoveAreaCommand(
OakNodeSequence *timeline, Rational in, Rational out)
OakNodeSequence timeline, Rational in, Rational out)
{
for (int i = 0; i < OAKNODE_TRACK_TYPE_COUNT; i++) {
OakNodeTrackList *list = nullptr;
OakNodeTrackList list = {};
oaknode_sequence_get_track_list(timeline, i, &list);
if (list) {
if (list.ctx) {
add_child(new TrackListRippleRemoveAreaCommand(list, in, out));
}
}
@@ -264,8 +264,8 @@ TimelineRippleRemoveAreaCommand::TimelineRippleRemoveAreaCommand(
// TrackListRippleToolCommand
//
TrackListRippleToolCommand::TrackListRippleToolCommand(
OakNodeTrackList *track_list,
const std::map<OakNodeTrack *, RippleInfo> &info,
OakNodeTrackList track_list,
const std::map<OakNodeTrack, RippleInfo, TrackHandleLess> &info,
const Rational &ripple_movement,
const Timeline::MovementMode &movement_mode)
: track_list_(track_list)
@@ -280,11 +280,11 @@ TrackListRippleToolCommand::~TrackListRippleToolCommand()
// Free any gaps still owned by this command (detached from the graph)
for (auto &pair : working_data_) {
WorkingData &wd = pair.second;
if (wd.created_gap && wd.created_gap_orphaned) {
oaknode_block_free(wd.created_gap);
if (wd.created_gap_orphaned) {
free_detached_handle(&wd.created_gap);
}
if (wd.removed_gap && wd.removed_gap_orphaned) {
oaknode_block_free(wd.removed_gap);
if (wd.removed_gap_orphaned) {
free_detached_handle(&wd.removed_gap);
}
}
}
@@ -304,10 +304,10 @@ void TrackListRippleToolCommand::ripple(bool redo)
// Make timeline changes
for (const auto &pair : info_) {
OakNodeTrack *track = pair.first;
OakNodeTrack track = pair.first;
const RippleInfo &info = pair.second;
WorkingData working_data = working_data_[track];
OakNodeBlock *b = info.block;
OakNodeBlock b = info.block;
// Generate block length
Rational new_block_length;
@@ -321,7 +321,7 @@ void TrackListRippleToolCommand::ripple(bool redo)
operation_movement = -operation_movement;
}
if (b) {
if (b.ctx) {
new_block_length = block_length(b) + operation_movement;
}
@@ -330,10 +330,10 @@ void TrackListRippleToolCommand::ripple(bool redo)
if (info.append_gap) {
// Rather than rippling the referenced block, we'll insert a gap and ripple with that
OakNodeBlock *gap = working_data.created_gap;
OakNodeBlock gap = working_data.created_gap;
if (redo) {
if (!gap) {
if (!gap.ctx) {
gap = oaknode_block_gap_create();
block_set_length_and_media_out(
gap, ripple_movement_ < Rational(0)
@@ -362,7 +362,7 @@ void TrackListRippleToolCommand::ripple(bool redo)
}
} else if ((redo && new_block_length.isNull()) ||
(!redo && !block_track(b))) {
(!redo && !block_track(b).ctx)) {
// The ripple is the length of this block. We assume that for this to happen, it must have
// been a gap that we will now remove.
@@ -445,9 +445,9 @@ void TrackListRippleToolCommand::ripple(bool redo)
namespace
{
bool is_gap(OakNodeBlock *b)
bool is_gap(OakNodeBlock b)
{
if (!b) {
if (!b.ctx) {
return false;
}
int kind = OAKNODE_BLOCK_OTHER;
@@ -460,16 +460,17 @@ bool is_gap(OakNodeBlock *b)
void TimelineRippleDeleteGapsAtRegionsCommand::prepare()
{
size_t max_gaps = 0;
std::map<OakNodeTrack *, std::vector<RemovalRequest>> requested_gaps;
std::map<OakNodeTrack, std::vector<RemovalRequest>, TrackHandleLess>
requested_gaps;
// Convert regions to gaps
for (const auto &region : regions_) {
OakNodeTrack *track = region.first;
OakNodeTrack track = region.first;
const TimeRange &range = region.second;
int n, d;
rat_nd(range.in(), &n, &d);
OakNodeBlock *block = nullptr;
OakNodeBlock block = {};
oaknode_track_get_nearest_block_before_or_at(track, n, d, &block);
if (is_gap(block)) {
@@ -499,7 +500,7 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare()
// For each gap on each track, find a corresponding gap on every other track (which may include
// a requested gap) to ripple in order to keep everything synchronized
std::map<OakNodeBlock *, Rational> gap_lengths;
std::map<OakNodeBlock, Rational, BlockHandleLess> gap_lengths;
for (size_t gap_index = 0; gap_index < max_gaps; gap_index++) {
Rational earliest_point = RATIONAL_MAX;
Rational ripple_length = RATIONAL_MAX;
@@ -516,14 +517,14 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare()
}
// Determine which gaps will be involved in this operation
std::vector<OakNodeBlock *> gaps;
std::vector<OakNodeBlock> gaps;
int track_count = 0;
oaknode_sequence_get_all_track_count(timeline_, &track_count);
for (int ti = 0; ti < track_count; ti++) {
OakNodeTrack *track = nullptr;
OakNodeTrack track = {};
oaknode_sequence_get_all_track_at(timeline_, ti, &track);
if (!track) {
if (!track.ctx) {
continue;
}
@@ -538,7 +539,7 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare()
const std::vector<RemovalRequest> &requested_gaps_on_track =
req_it != requested_gaps.end() ? req_it->second : empty_list;
OakNodeBlock *gap = nullptr;
OakNodeBlock gap = {};
if (gap_index < requested_gaps_on_track.size()) {
// A requested gap was at this index, use it
gap = requested_gaps_on_track.at(gap_index).gap;
@@ -546,24 +547,24 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare()
// No requested gap was at this index, find one
int n, d;
rat_nd(earliest_point, &n, &d);
OakNodeBlock *block = nullptr;
OakNodeBlock block = {};
oaknode_track_get_nearest_block_after_or_at(track, n, d,
&block);
if (block) {
if (block.ctx) {
// Found a block, test if it's a gap
if (is_gap(block)) {
gap = block;
} else {
if (block_in(block) == earliest_point) {
OakNodeBlock *next = block_next(block);
OakNodeBlock next = block_next(block);
if (is_gap(next)) {
gap = next;
} else {
ripple_length = 0;
}
} else {
OakNodeBlock *prev = block_previous(block);
OakNodeBlock prev = block_previous(block);
if (is_gap(prev)) {
gap = prev;
} else {
@@ -576,7 +577,7 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare()
}
}
if (gap) {
if (gap.ctx) {
gaps.push_back(gap);
if (!gap_lengths.count(gap)) {
@@ -592,7 +593,7 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare()
}
if (ripple_length > 0) {
for (OakNodeBlock *gap : gaps) {
for (OakNodeBlock gap : gaps) {
if (gap_lengths[gap] == ripple_length) {
commands_.push_back(new TrackRippleRemoveBlockCommand(
block_track(gap), gap));
+26 -26
View File
@@ -51,25 +51,25 @@ namespace olive
*/
class TrackRippleRemoveAreaCommand : public UndoCommand {
public:
TrackRippleRemoveAreaCommand(OakNodeTrack *track, const TimeRange &range);
TrackRippleRemoveAreaCommand(OakNodeTrack track, const TimeRange &range);
virtual ~TrackRippleRemoveAreaCommand() override;
/**
* @brief Block to insert after if you want to insert something between this ripple
*/
OakNodeBlock *get_insertion_index() const
OakNodeBlock get_insertion_index() const
{
return insert_previous_;
}
OakNodeBlock *get_spliced_block() const
OakNodeBlock get_spliced_block() const
{
if (splice_split_command_) {
return splice_split_command_->new_block();
}
return nullptr;
return OakNodeBlock{};
}
void set_allow_splitting_gaps(bool e)
@@ -86,23 +86,23 @@ protected:
private:
struct TrimOperation {
OakNodeBlock *block;
OakNodeBlock block;
Rational old_length;
Rational new_length;
};
struct RemoveOperation {
OakNodeBlock *block;
OakNodeBlock *before;
OakNodeBlock block;
OakNodeBlock before;
};
OakNodeTrack *track_;
OakNodeTrack track_;
TimeRange range_;
TrimOperation trim_out_;
std::vector<RemoveOperation> removals_;
TrimOperation trim_in_;
OakNodeBlock *insert_previous_;
OakNodeBlock insert_previous_;
bool allow_splitting_gaps_;
BlockSplitCommand *splice_split_command_;
@@ -111,7 +111,7 @@ private:
class TrackListRippleRemoveAreaCommand : public UndoCommand {
public:
TrackListRippleRemoveAreaCommand(OakNodeTrackList *list, Rational in,
TrackListRippleRemoveAreaCommand(OakNodeTrackList list, Rational in,
Rational out)
: list_(list)
, range_(in, out)
@@ -133,9 +133,9 @@ protected:
virtual void undo() override;
private:
OakNodeTrackList *list_;
OakNodeTrackList list_;
std::vector<OakNodeTrack *> working_tracks_;
std::vector<OakNodeTrack> working_tracks_;
TimeRange range_;
@@ -144,20 +144,20 @@ private:
class TimelineRippleRemoveAreaCommand : public MultiUndoCommand {
public:
TimelineRippleRemoveAreaCommand(OakNodeSequence *timeline, Rational in,
TimelineRippleRemoveAreaCommand(OakNodeSequence timeline, Rational in,
Rational out);
};
class TrackListRippleToolCommand : public UndoCommand {
public:
struct RippleInfo {
OakNodeBlock *block;
OakNodeBlock block;
bool append_gap;
};
TrackListRippleToolCommand(
OakNodeTrackList *track_list,
const std::map<OakNodeTrack *, RippleInfo> &info,
OakNodeTrackList track_list,
const std::map<OakNodeTrack, RippleInfo, TrackHandleLess> &info,
const Rational &ripple_movement,
const Timeline::MovementMode &movement_mode);
@@ -177,30 +177,30 @@ protected:
private:
void ripple(bool redo);
OakNodeTrackList *track_list_;
OakNodeTrackList track_list_;
std::map<OakNodeTrack *, RippleInfo> info_;
std::map<OakNodeTrack, RippleInfo, TrackHandleLess> info_;
Rational ripple_movement_;
Timeline::MovementMode movement_mode_;
struct WorkingData {
OakNodeBlock *created_gap = nullptr;
OakNodeBlock created_gap{};
bool created_gap_orphaned = false;
OakNodeBlock *removed_gap = nullptr;
OakNodeBlock removed_gap{};
bool removed_gap_orphaned = false;
OakNodeBlock *removed_gap_after = nullptr;
OakNodeBlock removed_gap_after{};
Rational old_length;
Rational earliest_point_of_change;
};
std::map<OakNodeTrack *, WorkingData> working_data_;
std::map<OakNodeTrack, WorkingData, TrackHandleLess> working_data_;
};
class TimelineRippleDeleteGapsAtRegionsCommand : public UndoCommand {
public:
using RangeList = std::vector<std::pair<OakNodeTrack *, TimeRange>>;
using RangeList = std::vector<std::pair<OakNodeTrack, TimeRange>>;
TimelineRippleDeleteGapsAtRegionsCommand(OakNodeSequence *vo,
TimelineRippleDeleteGapsAtRegionsCommand(OakNodeSequence vo,
const RangeList &regions)
: timeline_(vo)
, regions_(regions)
@@ -227,13 +227,13 @@ protected:
virtual void undo() override;
private:
OakNodeSequence *timeline_;
OakNodeSequence timeline_;
RangeList regions_;
std::vector<UndoCommand *> commands_;
struct RemovalRequest {
OakNodeBlock *gap;
OakNodeBlock gap;
TimeRange range;
};
};
+25 -30
View File
@@ -24,6 +24,7 @@
#include <cassert>
#include "node/node.h"
#include "timelineutil.h"
#include "undo/undostack.h"
namespace olive
@@ -32,19 +33,13 @@ namespace olive
namespace
{
void rat_nd(const Rational &r, int *n, int *d)
{
*n = r.numerator();
*d = r.denominator();
}
/**
* @brief oaknode-block-link undo command (replaces NodeLinkCommand from
* oaknode's nodeundo, which does not cross the module boundary)
*/
class BlockLinkUndoCommand : public UndoCommand {
public:
BlockLinkUndoCommand(OakNodeBlock *a, OakNodeBlock *b, bool link)
BlockLinkUndoCommand(OakNodeBlock a, OakNodeBlock b, bool link)
: a_(a)
, b_(b)
, link_(link)
@@ -71,8 +66,8 @@ protected:
}
private:
OakNodeBlock *a_;
OakNodeBlock *b_;
OakNodeBlock a_;
OakNodeBlock b_;
bool link_;
};
@@ -90,7 +85,7 @@ BlockSplitCommand::~BlockSplitCommand()
void BlockSplitCommand::prepare()
{
OakNodeNode *copy = oaknode_node_copy_in_graph(
OakNodeNode copy = oaknode_node_copy_in_graph(
oaknode_block_as_node(block_), &reconnect_tree_command_);
new_block_ = oaknode_block_from_node(copy);
}
@@ -119,7 +114,7 @@ void BlockSplitCommand::redo()
Rational new_part_length = block_out - point_;
// Begin an operation
OakNodeTrack *track = nullptr;
OakNodeTrack track = {};
oaknode_block_get_track(block_, &track);
// Set lengths
@@ -134,13 +129,13 @@ void BlockSplitCommand::redo()
oaknode_clip_add_cache_passthrough_from(new_block_, block_);
// If the block had an out transition, we move it to the new block
moved_transition_ = nullptr;
moved_transition_ = OakNodeBlock{};
moved_transition_input_.clear();
OakNodeBlock *next = nullptr;
OakNodeBlock next = {};
oaknode_block_get_next(new_block_, &next);
int next_kind = OAKNODE_BLOCK_OTHER;
if (next) {
if (next.ctx) {
oaknode_block_get_kind(next, &next_kind);
}
if (next_kind == OAKNODE_BLOCK_TRANSITION) {
@@ -149,10 +144,10 @@ void BlockSplitCommand::redo()
oaknode_node_output_connection_count(
oaknode_block_as_node(block_), &conn_count);
for (int i = 0; i < conn_count; i++) {
OakNodeNode *conn_node = nullptr;
OakNodeNode conn_node = {};
oaknode_node_output_connection_node_at(
oaknode_block_as_node(block_), i, &conn_node);
if (oaknode_block_from_node(conn_node) != next) {
if (!same_block(oaknode_block_from_node(conn_node), next)) {
continue;
}
@@ -176,10 +171,10 @@ void BlockSplitCommand::redo()
void BlockSplitCommand::undo()
{
OakNodeTrack *track = nullptr;
OakNodeTrack track = {};
oaknode_block_get_track(block_, &track);
if (moved_transition_) {
if (moved_transition_.ctx) {
oaknode_node_disconnect(oaknode_block_as_node(moved_transition_),
moved_transition_input_.c_str());
oaknode_node_connect(oaknode_block_as_node(block_),
@@ -208,19 +203,19 @@ BlockSplitPreservingLinksCommand::~BlockSplitPreservingLinksCommand()
}
}
OakNodeBlock *
BlockSplitPreservingLinksCommand::get_split(OakNodeBlock *original,
OakNodeBlock
BlockSplitPreservingLinksCommand::get_split(OakNodeBlock original,
int time_index) const
{
if (time_index >= 0 && time_index < int(times_.size())) {
for (size_t i = 0; i < blocks_.size(); i++) {
if (blocks_[i] == original) {
if (same_block(blocks_[i], original)) {
return splits_.at(time_index).at(i);
}
}
}
return nullptr;
return OakNodeBlock{};
}
void BlockSplitPreservingLinksCommand::prepare()
@@ -235,10 +230,10 @@ void BlockSplitPreservingLinksCommand::prepare()
// if this ever becomes an issue.
assert(i == 0 || time > times_.at(i - 1));
std::vector<OakNodeBlock *> splits(blocks_.size(), nullptr);
std::vector<OakNodeBlock> splits(blocks_.size(), OakNodeBlock{});
for (size_t j = 0; j < blocks_.size(); j++) {
OakNodeBlock *b = blocks_.at(j);
OakNodeBlock b = blocks_.at(j);
int in_n, in_d, out_n, out_d;
oaknode_block_get_in(b, &in_n, &in_d);
@@ -260,22 +255,22 @@ void BlockSplitPreservingLinksCommand::prepare()
// Now that we've determined all the splits, we can relink everything
for (size_t i = 0; i < blocks_.size(); i++) {
OakNodeBlock *a = blocks_.at(i);
OakNodeBlock a = blocks_.at(i);
for (size_t j = 0; j < blocks_.size(); j++) {
if (i == j) {
continue;
}
OakNodeBlock *b = blocks_.at(j);
OakNodeBlock b = blocks_.at(j);
int linked = 0;
oaknode_block_are_linked(a, b, &linked);
if (linked) {
// These blocks are linked, ensure all the splits are linked too
for (const std::vector<OakNodeBlock *> &split_list :
for (const std::vector<OakNodeBlock> &split_list :
splits_) {
if (!split_list.at(i) || !split_list.at(j)) {
if (!split_list.at(i).ctx || !split_list.at(j).ctx) {
continue;
}
BlockLinkUndoCommand *blc = new BlockLinkUndoCommand(
@@ -297,10 +292,10 @@ void TrackSplitAtTimeCommand::prepare()
int n, d;
rat_nd(point_, &n, &d);
OakNodeBlock *b = nullptr;
OakNodeBlock b = {};
oaknode_track_get_block_containing_time(track_, n, d, &b);
if (b) {
if (b.ctx) {
command_ = new BlockSplitCommand(b, point_);
}
}
+13 -13
View File
@@ -44,12 +44,12 @@ namespace olive
*/
class BlockSplitCommand : public UndoCommand {
public:
BlockSplitCommand(OakNodeBlock *block, Rational point)
BlockSplitCommand(OakNodeBlock block, Rational point)
: block_(block)
, new_block_(nullptr)
, new_block_{}
, point_(point)
, reconnect_tree_command_({})
, moved_transition_(nullptr)
, moved_transition_{}
{
}
@@ -58,7 +58,7 @@ public:
/**
* @brief Access the second block created as a result. Only valid after redo().
*/
OakNodeBlock *new_block()
OakNodeBlock new_block()
{
return new_block_;
}
@@ -71,22 +71,22 @@ protected:
virtual void undo() override;
private:
OakNodeBlock *block_;
OakNodeBlock *new_block_;
OakNodeBlock block_;
OakNodeBlock new_block_;
Rational old_length_;
Rational point_;
OakUndoCommand reconnect_tree_command_;
OakNodeBlock *moved_transition_;
OakNodeBlock moved_transition_;
std::string moved_transition_input_;
};
class BlockSplitPreservingLinksCommand : public UndoCommand {
public:
BlockSplitPreservingLinksCommand(
const std::vector<OakNodeBlock *> &blocks,
const std::vector<OakNodeBlock> &blocks,
const std::vector<Rational> &times)
: blocks_(blocks)
, times_(times)
@@ -95,7 +95,7 @@ public:
virtual ~BlockSplitPreservingLinksCommand() override;
OakNodeBlock *get_split(OakNodeBlock *original, int time_index) const;
OakNodeBlock get_split(OakNodeBlock original, int time_index) const;
protected:
virtual void prepare() override;
@@ -115,18 +115,18 @@ protected:
}
private:
std::vector<OakNodeBlock *> blocks_;
std::vector<OakNodeBlock> blocks_;
std::vector<Rational> times_;
std::vector<UndoCommand *> commands_;
std::vector<std::vector<OakNodeBlock *>> splits_;
std::vector<std::vector<OakNodeBlock>> splits_;
};
class TrackSplitAtTimeCommand : public UndoCommand {
public:
TrackSplitAtTimeCommand(OakNodeTrack *track, Rational point)
TrackSplitAtTimeCommand(OakNodeTrack track, Rational point)
: track_(track)
, point_(point)
, command_(nullptr)
@@ -156,7 +156,7 @@ protected:
}
private:
OakNodeTrack *track_;
OakNodeTrack track_;
Rational point_;
+18 -18
View File
@@ -37,10 +37,10 @@ namespace olive
*/
class TrackRippleRemoveBlockCommand : public UndoCommand {
public:
TrackRippleRemoveBlockCommand(OakNodeTrack *track, OakNodeBlock *block)
TrackRippleRemoveBlockCommand(OakNodeTrack track, OakNodeBlock block)
: track_(track)
, block_(block)
, before_(nullptr)
, before_{}
{
}
@@ -57,16 +57,16 @@ protected:
}
private:
OakNodeTrack *track_;
OakNodeTrack track_;
OakNodeBlock *block_;
OakNodeBlock block_;
OakNodeBlock *before_;
OakNodeBlock before_;
};
class TrackPrependBlockCommand : public UndoCommand {
public:
TrackPrependBlockCommand(OakNodeTrack *track, OakNodeBlock *block)
TrackPrependBlockCommand(OakNodeTrack track, OakNodeBlock block)
: track_(track)
, block_(block)
{
@@ -84,14 +84,14 @@ protected:
}
private:
OakNodeTrack *track_;
OakNodeBlock *block_;
OakNodeTrack track_;
OakNodeBlock block_;
};
class TrackInsertBlockAfterCommand : public UndoCommand {
public:
TrackInsertBlockAfterCommand(OakNodeTrack *track, OakNodeBlock *block,
OakNodeBlock *before)
TrackInsertBlockAfterCommand(OakNodeTrack track, OakNodeBlock block,
OakNodeBlock before)
: track_(track)
, block_(block)
, before_(before)
@@ -110,11 +110,11 @@ protected:
}
private:
OakNodeTrack *track_;
OakNodeTrack track_;
OakNodeBlock *block_;
OakNodeBlock block_;
OakNodeBlock *before_;
OakNodeBlock before_;
};
/**
@@ -124,8 +124,8 @@ private:
*/
class TrackReplaceBlockCommand : public UndoCommand {
public:
TrackReplaceBlockCommand(OakNodeTrack *track, OakNodeBlock *old_block,
OakNodeBlock *replace)
TrackReplaceBlockCommand(OakNodeTrack track, OakNodeBlock old_block,
OakNodeBlock replace)
: track_(track)
, old_(old_block)
, replace_(replace)
@@ -144,9 +144,9 @@ protected:
}
private:
OakNodeTrack *track_;
OakNodeBlock *old_;
OakNodeBlock *replace_;
OakNodeTrack track_;
OakNodeBlock old_;
OakNodeBlock replace_;
};
}
+96 -23
View File
@@ -25,13 +25,22 @@
#include <olive/core/core.h>
#include "node/block.h"
#include "node/node.h"
#include "node/project.h"
#include "node/sequence.h"
#include "node/track.h"
#include "../../node/c_api/nodehandle.h"
namespace olive
{
class Block;
class Node;
class Project;
class Sequence;
class Track;
/**
* @brief Small helpers translating between olive::core::Rational and the
* oaknode C ABI's numerator/denominator pairs
@@ -42,28 +51,92 @@ inline void rat_nd(const olive::core::Rational &r, int *n, int *d)
*d = r.denominator();
}
inline olive::core::Rational block_in(OakNodeBlock *b)
/**
* @brief Identity comparison for value handles: two handles refer to the
* same object when they wrap the same native pointer (borrowed accessors
* hand out a fresh handle box per call, so comparing ctx is NOT an
* identity check)
*/
inline bool same_block(OakNodeBlock a, OakNodeBlock b)
{
return oaknode_c_api::to_native<Block>(a) ==
oaknode_c_api::to_native<Block>(b);
}
inline bool same_track(OakNodeTrack a, OakNodeTrack b)
{
return oaknode_c_api::to_native<Track>(a) ==
oaknode_c_api::to_native<Track>(b);
}
inline bool same_node(OakNodeNode a, OakNodeNode b)
{
return oaknode_c_api::to_native<Node>(a) ==
oaknode_c_api::to_native<Node>(b);
}
/**
* @brief Handle-map comparators ordering by the wrapped native object
* (identity), so lookups work across freshly-boxed borrowed handles
*/
struct BlockHandleLess {
bool operator()(OakNodeBlock a, OakNodeBlock b) const
{
return oaknode_c_api::to_native<Block>(a) <
oaknode_c_api::to_native<Block>(b);
}
};
struct TrackHandleLess {
bool operator()(OakNodeTrack a, OakNodeTrack b) const
{
return oaknode_c_api::to_native<Track>(a) <
oaknode_c_api::to_native<Track>(b);
}
};
/**
* @brief Free a handle whose object is currently detached from any graph
* or container.
*
* Insertion into a graph/track flips the handle to non-owning, so a
* plain free() would only release the handle box and leak the detached
* object. This helper re-takes ownership (the caller guarantees the
* object is detached, as with the old raw-pointer delete semantics) and
* then releases, destroying the object.
*/
template <typename Handle>
inline void free_detached_handle(Handle *h)
{
if (!h || !h->ctx) {
return;
}
static_cast<OakNodeBox *>(h->ctx)->owns = true;
oaknode_c_api::free_handle(h);
}
inline olive::core::Rational block_in(OakNodeBlock b)
{
int n, d;
oaknode_block_get_in(b, &n, &d);
return olive::core::Rational(n, d);
}
inline olive::core::Rational block_out(OakNodeBlock *b)
inline olive::core::Rational block_out(OakNodeBlock b)
{
int n, d;
oaknode_block_get_out(b, &n, &d);
return olive::core::Rational(n, d);
}
inline olive::core::Rational block_length(OakNodeBlock *b)
inline olive::core::Rational block_length(OakNodeBlock b)
{
int n, d;
oaknode_block_get_length(b, &n, &d);
return olive::core::Rational(n, d);
}
inline void block_set_length_and_media_out(OakNodeBlock *b,
inline void block_set_length_and_media_out(OakNodeBlock b,
const olive::core::Rational &len)
{
int n, d;
@@ -71,7 +144,7 @@ inline void block_set_length_and_media_out(OakNodeBlock *b,
oaknode_block_set_length_and_media_out(b, n, d);
}
inline void block_set_length_and_media_in(OakNodeBlock *b,
inline void block_set_length_and_media_in(OakNodeBlock b,
const olive::core::Rational &len)
{
int n, d;
@@ -79,30 +152,30 @@ inline void block_set_length_and_media_in(OakNodeBlock *b,
oaknode_block_set_length_and_media_in(b, n, d);
}
inline olive::core::Rational track_length(OakNodeTrack *t)
inline olive::core::Rational track_length(OakNodeTrack t)
{
int n, d;
oaknode_track_get_length(t, &n, &d);
return olive::core::Rational(n, d);
}
inline OakNodeBlock *block_previous(OakNodeBlock *b)
inline OakNodeBlock block_previous(OakNodeBlock b)
{
OakNodeBlock *out = nullptr;
OakNodeBlock out = {};
oaknode_block_get_previous(b, &out);
return out;
}
inline OakNodeBlock *block_next(OakNodeBlock *b)
inline OakNodeBlock block_next(OakNodeBlock b)
{
OakNodeBlock *out = nullptr;
OakNodeBlock out = {};
oaknode_block_get_next(b, &out);
return out;
}
inline OakNodeTrack *block_track(OakNodeBlock *b)
inline OakNodeTrack block_track(OakNodeBlock b)
{
OakNodeTrack *out = nullptr;
OakNodeTrack out = {};
oaknode_block_get_track(b, &out);
return out;
}
@@ -111,30 +184,30 @@ inline OakNodeTrack *block_track(OakNodeBlock *b)
* @brief Attach/detach a block to/from the project graph that owns a
* track (replaces the original setParent(graph) / setParent(memory))
*/
inline OakNodeProject *track_project(OakNodeTrack *track)
inline OakNodeProject track_project(OakNodeTrack track)
{
OakNodeSequence *sequence = nullptr;
OakNodeSequence sequence = {};
if (oaknode_track_get_sequence(track, &sequence) != OAKNODE_OK ||
!sequence) {
return nullptr;
!sequence.ctx) {
return OakNodeProject{};
}
OakNodeProject *project = nullptr;
OakNodeProject project = {};
oaknode_node_get_project(oaknode_sequence_as_node(sequence), &project);
return project;
}
inline void block_add_to_graph(OakNodeBlock *b, OakNodeTrack *track)
inline void block_add_to_graph(OakNodeBlock b, OakNodeTrack track)
{
OakNodeProject *project = track_project(track);
if (project) {
OakNodeProject project = track_project(track);
if (project.ctx) {
oaknode_project_add_node(project, oaknode_block_as_node(b));
}
}
inline void block_remove_from_graph(OakNodeBlock *b, OakNodeTrack *track)
inline void block_remove_from_graph(OakNodeBlock b, OakNodeTrack track)
{
OakNodeProject *project = track_project(track);
if (project) {
OakNodeProject project = track_project(track);
if (project.ctx) {
oaknode_project_remove_node(project, oaknode_block_as_node(b));
}
}
+51 -46
View File
@@ -28,6 +28,8 @@
#include "timeline/workarea.h"
#include "undo/undocommand.h"
#include "../../node/c_api/nodehandle.h"
namespace
{
@@ -36,13 +38,13 @@ protected:
void SetUp() override
{
project_ = oaknode_project_init();
ASSERT_NE(project_, nullptr);
ASSERT_NE(project_.ctx, nullptr);
sequence_ = oaknode_sequence_create();
ASSERT_NE(sequence_, nullptr);
ASSERT_NE(sequence_.ctx, nullptr);
node_ = oaknode_sequence_as_node(sequence_);
OakNodeProject *owner = nullptr;
OakNodeProject owner = {};
ASSERT_EQ(oaknode_node_get_project(node_, &owner), OAKNODE_OK);
if (!owner) {
if (!owner.ctx) {
ASSERT_EQ(oaknode_project_add_node(project_, node_),
OAKNODE_OK);
}
@@ -51,12 +53,12 @@ protected:
void TearDown() override
{
// The project owns the sequence and everything in its graph
oaknode_project_free(project_);
oaknode_project_free(&project_);
}
OakNodeProject *project_ = nullptr;
OakNodeSequence *sequence_ = nullptr;
OakNodeNode *node_ = nullptr;
OakNodeProject project_ = {};
OakNodeSequence sequence_ = {};
OakNodeNode node_ = {};
};
// ---- marker ---------------------------------------------------------------
@@ -64,7 +66,7 @@ protected:
TEST_F(TimelineSequenceFixture, MarkerListOfReturnsList)
{
EXPECT_NE(oaktimeline_marker_list_of(node_), nullptr);
EXPECT_EQ(oaktimeline_marker_list_of(nullptr), nullptr);
EXPECT_EQ(oaktimeline_marker_list_of(OakNodeNode{}), nullptr);
}
TEST_F(TimelineSequenceFixture, MarkerAddCountAtRemove)
@@ -196,8 +198,8 @@ TEST_F(TimelineSequenceFixture, MarkerListXmlRoundTrip)
oakcommon_xml_writer_free(&writer);
// Load into a fresh sequence's marker list
OakNodeSequence *seq2 = oaknode_sequence_create();
ASSERT_NE(seq2, nullptr);
OakNodeSequence seq2 = oaknode_sequence_create();
ASSERT_NE(seq2.ctx, nullptr);
ASSERT_EQ(oaknode_project_add_node(project_,
oaknode_sequence_as_node(seq2)),
OAKNODE_OK);
@@ -237,7 +239,7 @@ TEST_F(TimelineSequenceFixture, WorkareaGetSetLive)
{
OakTimelineWorkArea *w = oaktimeline_workarea_of(node_);
ASSERT_NE(w, nullptr);
EXPECT_EQ(oaktimeline_workarea_of(nullptr), nullptr);
EXPECT_EQ(oaktimeline_workarea_of(OakNodeNode{}), nullptr);
int in_n = 0, in_d = 0, out_n = 0, out_d = 0, enabled = -1;
EXPECT_EQ(oaktimeline_workarea_get(w, &in_n, &in_d, &out_n, &out_d,
@@ -343,8 +345,8 @@ TEST_F(TimelineSequenceFixture, WorkareaXmlRoundTrip)
ASSERT_LT(needed, int(sizeof(xml)));
oakcommon_xml_writer_free(&writer);
OakNodeSequence *seq2 = oaknode_sequence_create();
ASSERT_NE(seq2, nullptr);
OakNodeSequence seq2 = oaknode_sequence_create();
ASSERT_NE(seq2.ctx, nullptr);
ASSERT_EQ(oaknode_project_add_node(project_,
oaknode_sequence_as_node(seq2)),
OAKNODE_OK);
@@ -374,11 +376,11 @@ TEST_F(TimelineSequenceFixture, WorkareaXmlRoundTrip)
TEST_F(TimelineSequenceFixture, AddAndRemoveTrackCommands)
{
OakNodeTrackList *list = nullptr;
OakNodeTrackList list = {};
ASSERT_EQ(oaknode_sequence_get_track_list(
sequence_, OAKNODE_TRACK_TYPE_VIDEO, &list),
OAKNODE_OK);
ASSERT_NE(list, nullptr);
ASSERT_NE(list.ctx, nullptr);
int count = -1;
EXPECT_EQ(oaknode_tracklist_get_track_count(list, &count), OAKNODE_OK);
@@ -391,10 +393,10 @@ TEST_F(TimelineSequenceFixture, AddAndRemoveTrackCommands)
EXPECT_EQ(oaknode_tracklist_get_track_count(list, &count), OAKNODE_OK);
EXPECT_EQ(count, before + 1);
OakNodeTrack *track = nullptr;
OakNodeTrack track = {};
EXPECT_EQ(oaknode_tracklist_get_track_at(list, before, &track),
OAKNODE_OK);
ASSERT_NE(track, nullptr);
ASSERT_NE(track.ctx, nullptr);
OakUndoCommand rm = oaktimeline_remove_track_command(track);
ASSERT_NE(rm.ctx, nullptr);
@@ -414,26 +416,26 @@ TEST_F(TimelineSequenceFixture, AddAndRemoveTrackCommands)
oakundo_command_free(&add);
oakundo_command_free(&rm);
EXPECT_EQ(oaktimeline_add_track_command(nullptr).ctx, nullptr);
EXPECT_EQ(oaktimeline_remove_track_command(nullptr).ctx, nullptr);
EXPECT_EQ(oaktimeline_add_track_command(OakNodeTrackList{}).ctx, nullptr);
EXPECT_EQ(oaktimeline_remove_track_command(OakNodeTrack{}).ctx, nullptr);
}
TEST_F(TimelineSequenceFixture, PlaceTrimSplitRemoveAreaCommands)
{
OakNodeTrackList *list = nullptr;
OakNodeTrackList list = {};
ASSERT_EQ(oaknode_sequence_get_track_list(
sequence_, OAKNODE_TRACK_TYPE_VIDEO, &list),
OAKNODE_OK);
OakNodeTrack *track = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(track, nullptr);
OakNodeTrack track = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(track.ctx, nullptr);
ASSERT_EQ(oaknode_project_add_node(project_,
oaknode_track_as_node(track)),
OAKNODE_OK);
ASSERT_EQ(oaknode_tracklist_add_track(list, track), OAKNODE_OK);
OakNodeBlock *clip = oaknode_block_clip_create();
ASSERT_NE(clip, nullptr);
OakNodeBlock clip = oaknode_block_clip_create();
ASSERT_NE(clip.ctx, nullptr);
EXPECT_EQ(oaknode_block_set_length_and_media_out(clip, 10, 1),
OAKNODE_OK);
ASSERT_EQ(oaknode_project_add_node(project_,
@@ -466,7 +468,7 @@ TEST_F(TimelineSequenceFixture, PlaceTrimSplitRemoveAreaCommands)
oakundo_command_free(&trim);
// Split at 5: two blocks of 5
OakNodeBlock *blocks[] = { clip };
OakNodeBlock blocks[] = { clip };
OakUndoCommand split =
oaktimeline_split_command(blocks, 1, 5, 1);
ASSERT_NE(split.ctx, nullptr);
@@ -495,9 +497,9 @@ TEST_F(TimelineSequenceFixture, PlaceTrimSplitRemoveAreaCommands)
EXPECT_EQ(oaknode_block_get_length(clip, &n, &d), OAKNODE_OK);
EXPECT_EQ(n, 3);
OakNodeBlock *second = nullptr;
OakNodeBlock second = {};
EXPECT_EQ(oaknode_track_get_block_at(track, 1, &second), OAKNODE_OK);
ASSERT_NE(second, nullptr);
ASSERT_NE(second.ctx, nullptr);
EXPECT_EQ(oaknode_block_get_length(second, &n, &d), OAKNODE_OK);
EXPECT_EQ(n, 5);
@@ -512,32 +514,32 @@ TEST_F(TimelineSequenceFixture, PlaceTrimSplitRemoveAreaCommands)
EXPECT_EQ(block_count, 0);
oakundo_command_free(&place);
EXPECT_EQ(oaktimeline_place_block_command(nullptr, 0, clip, 0, 1)
EXPECT_EQ(oaktimeline_place_block_command(OakNodeTrackList{}, 0, clip, 0, 1)
.ctx,
nullptr);
EXPECT_EQ(oaktimeline_trim_command(track, clip, 1, 1, 99).ctx, nullptr);
EXPECT_EQ(oaktimeline_split_command(blocks, 0, 1, 1).ctx, nullptr);
EXPECT_EQ(oaktimeline_ripple_remove_area_command(nullptr, 0, 1, 1, 1)
EXPECT_EQ(oaktimeline_ripple_remove_area_command(OakNodeTrack{}, 0, 1, 1, 1)
.ctx,
nullptr);
}
TEST_F(TimelineSequenceFixture, ReplaceWithGapCommand)
{
OakNodeTrackList *list = nullptr;
OakNodeTrackList list = {};
ASSERT_EQ(oaknode_sequence_get_track_list(
sequence_, OAKNODE_TRACK_TYPE_VIDEO, &list),
OAKNODE_OK);
OakNodeTrack *track = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(track, nullptr);
OakNodeTrack track = oaknode_track_create(OAKNODE_TRACK_TYPE_VIDEO);
ASSERT_NE(track.ctx, nullptr);
ASSERT_EQ(oaknode_project_add_node(project_,
oaknode_track_as_node(track)),
OAKNODE_OK);
ASSERT_EQ(oaknode_tracklist_add_track(list, track), OAKNODE_OK);
OakNodeBlock *clip = oaknode_block_clip_create();
ASSERT_NE(clip, nullptr);
OakNodeBlock clip = oaknode_block_clip_create();
ASSERT_NE(clip.ctx, nullptr);
EXPECT_EQ(oaknode_block_set_length_and_media_out(clip, 6, 1),
OAKNODE_OK);
ASSERT_EQ(oaknode_project_add_node(project_,
@@ -546,8 +548,8 @@ TEST_F(TimelineSequenceFixture, ReplaceWithGapCommand)
EXPECT_EQ(oaknode_track_append_block(track, clip), OAKNODE_OK);
// Second clip so the first has a next (gap required)
OakNodeBlock *clip2 = oaknode_block_clip_create();
ASSERT_NE(clip2, nullptr);
OakNodeBlock clip2 = oaknode_block_clip_create();
ASSERT_NE(clip2.ctx, nullptr);
EXPECT_EQ(oaknode_block_set_length_and_media_out(clip2, 6, 1),
OAKNODE_OK);
ASSERT_EQ(oaknode_project_add_node(project_,
@@ -565,7 +567,7 @@ TEST_F(TimelineSequenceFixture, ReplaceWithGapCommand)
OAKNODE_OK);
ASSERT_EQ(block_count, 2);
OakNodeBlock *first = nullptr;
OakNodeBlock first = {};
EXPECT_EQ(oaknode_track_get_block_at(track, 0, &first), OAKNODE_OK);
int kind = OAKNODE_BLOCK_OTHER;
EXPECT_EQ(oaknode_block_get_kind(first, &kind), OAKNODE_OK);
@@ -573,10 +575,13 @@ TEST_F(TimelineSequenceFixture, ReplaceWithGapCommand)
oakundo_command_undo_now(replace);
EXPECT_EQ(oaknode_track_get_block_at(track, 0, &first), OAKNODE_OK);
EXPECT_EQ(first, clip);
// Borrowed handles get a fresh box per call, so compare the wrapped
// native objects
EXPECT_EQ(oaknode_c_api::to_native<void>(first),
oaknode_c_api::to_native<void>(clip));
oakundo_command_free(&replace);
EXPECT_EQ(oaktimeline_replace_block_with_gap_command(nullptr, clip)
EXPECT_EQ(oaktimeline_replace_block_with_gap_command(OakNodeTrack{}, clip)
.ctx,
nullptr);
@@ -586,17 +591,17 @@ TEST_F(TimelineSequenceFixture, ReplaceWithGapCommand)
TEST_F(TimelineSequenceFixture, SlideAndInsertGapsAndRippleDeleteGapsFactories)
{
OakNodeBlock *clip = oaknode_block_clip_create();
EXPECT_EQ(oaktimeline_slide_command(nullptr, &clip, 1, nullptr,
nullptr, 1, 1)
OakNodeBlock clip = oaknode_block_clip_create();
EXPECT_EQ(oaktimeline_slide_command(OakNodeTrack{}, &clip, 1,
OakNodeBlock{}, OakNodeBlock{}, 1, 1)
.ctx,
nullptr);
OakNodeTrackList *list = nullptr;
OakNodeTrackList list = {};
ASSERT_EQ(oaknode_sequence_get_track_list(
sequence_, OAKNODE_TRACK_TYPE_VIDEO, &list),
OAKNODE_OK);
EXPECT_EQ(oaktimeline_insert_gaps_command(nullptr, 0, 1, 1, 1).ctx,
EXPECT_EQ(oaktimeline_insert_gaps_command(OakNodeTrackList{}, 0, 1, 1, 1).ctx,
nullptr);
EXPECT_EQ(oaktimeline_ripple_delete_gaps_command(
@@ -604,7 +609,7 @@ TEST_F(TimelineSequenceFixture, SlideAndInsertGapsAndRippleDeleteGapsFactories)
.ctx,
nullptr);
oaknode_block_free(clip);
oaknode_block_free(&clip);
}
} // namespace