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
+87 -63
View File
@@ -25,6 +25,8 @@
#include <stdbool.h>
#endif
#include <stdint.h>
#include "node/error.h"
#ifdef __cplusplus
@@ -32,25 +34,40 @@ extern "C" {
#endif
/**
* @brief Opaque handle to a timeline block (olive::Block).
* @brief Reference-counted handle to a timeline block (olive::Block).
*
* Covers the whole Block family: ClipBlock, GapBlock and the concrete
* TransitionBlock subclasses. The handle IS the C++ object pointer; no
* wrapper is allocated. Concrete instances are created through the
* oaknode_block_*_create() factories below; callers never touch C++
* subclasses directly.
* TransitionBlock subclasses. The object never leaves the library that
* created it; every external reference is one of these handles.
* Semantics are shared_ptr-like: the oaknode_block_*_create() factories
* below return a handle with count 1, addref(ctx) takes another
* reference, release(ctx) drops one and the library destroys the object
* when the count reaches zero. Callers never touch C++ subclasses
* directly.
*
* Placing a block on a track (the oaknode_track_*_block() primitives)
* transfers ownership to the track; handles obtained from accessors
* (neighbours, lookups) are borrowed and never destroy the underlying
* object.
*/
typedef struct OakNodeBlock OakNodeBlock;
typedef struct OakNodeBlock {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeBlock;
/**
* @brief Opaque handle to a track (olive::Track), see node/track.h.
* @brief Reference-counted handle to a track (olive::Track), see
* node/track.h.
*
* Re-declared here so block.h is self-contained; the typedef is identical.
*/
typedef struct OakNodeTrack OakNodeTrack;
/**
* @brief Opaque handle to a node (olive::Node), see node/node.h.
* @brief Reference-counted handle to a node (olive::Node), see
* node/node.h.
*
* Re-declared here so block.h is self-contained; the typedef is identical.
*/
@@ -79,27 +96,33 @@ enum OakNodeTransitionKind {
* a project; a block that was never placed must be released with
* oaknode_block_free().
*
* @return Block handle, or NULL on allocation failure.
* @return Block handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OakNodeBlock *oaknode_block_clip_create(void);
OakNodeBlock oaknode_block_clip_create(void);
/**
* @brief Create a GapBlock. Ownership as oaknode_block_clip_create().
*
* @return Block handle, or NULL on allocation failure.
* @return Block handle with reference count 1; ctx is NULL on allocation
* failure.
*/
OakNodeBlock *oaknode_block_gap_create(void);
OakNodeBlock oaknode_block_gap_create(void);
/**
* @brief Create a concrete TransitionBlock.
*
* @param kind One of the OakNodeTransitionKind values.
* @return Block handle, or NULL on invalid kind / allocation failure.
* @return Block handle with reference count 1; ctx is NULL on invalid
* kind / allocation failure.
*/
OakNodeBlock *oaknode_block_transition_create(int kind);
OakNodeBlock oaknode_block_transition_create(int kind);
/**
* @brief Destroy a block. No-op on NULL.
* @brief Release one reference to a block handle.
*
* Destroys the block when the reference count reaches zero. NULL handle
* or NULL ctx is a no-op; clears `block->ctx` after releasing.
*
* The block must not be placed on a track or linked to other nodes; the
* caller is responsible for detaching it first.
@@ -116,32 +139,33 @@ enum OakNodeBlockKind {
/**
* @brief Concrete kind of a block (dynamic_cast query).
*/
int oaknode_block_get_kind(OakNodeBlock *block, int *out_kind);
int oaknode_block_get_kind(OakNodeBlock block, int *out_kind);
/**
* @brief Borrowed cast from a block handle to its node handle.
*
* Every Block is a Node; the result must not be freed. NULL for NULL.
* Every Block is a Node; releasing the result never destroys the block.
* Empty handle for an empty handle.
*/
OakNodeNode *oaknode_block_as_node(OakNodeBlock *block);
OakNodeNode oaknode_block_as_node(OakNodeBlock block);
/**
* @brief Borrowed cast from a node handle to a block handle.
*
* Returns NULL if the node is not a Block (or for NULL).
* Returns an empty handle if the node is not a Block (or is empty).
*/
OakNodeBlock *oaknode_block_from_node(OakNodeNode *node);
OakNodeBlock oaknode_block_from_node(OakNodeNode node);
/**
* @brief Rational getters/setters use numerator/denominator out pairs.
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_block_get_in(OakNodeBlock *block, int *numerator, int *denominator);
int oaknode_block_set_in(OakNodeBlock *block, int numerator, int denominator);
int oaknode_block_get_out(OakNodeBlock *block, int *numerator, int *denominator);
int oaknode_block_set_out(OakNodeBlock *block, int numerator, int denominator);
int oaknode_block_get_length(OakNodeBlock *block, int *numerator,
int oaknode_block_get_in(OakNodeBlock block, int *numerator, int *denominator);
int oaknode_block_set_in(OakNodeBlock block, int numerator, int denominator);
int oaknode_block_get_out(OakNodeBlock block, int *numerator, int *denominator);
int oaknode_block_set_out(OakNodeBlock block, int numerator, int denominator);
int oaknode_block_get_length(OakNodeBlock block, int *numerator,
int *denominator);
/**
@@ -150,9 +174,9 @@ int oaknode_block_get_length(OakNodeBlock *block, int *numerator,
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
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);
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);
/**
@@ -160,18 +184,18 @@ int oaknode_block_set_length_and_media_in(OakNodeBlock *block, int numerator,
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_block_get_enabled(OakNodeBlock *block, int *enabled);
int oaknode_block_set_enabled(OakNodeBlock *block, int enabled);
int oaknode_block_get_enabled(OakNodeBlock block, int *enabled);
int oaknode_block_set_enabled(OakNodeBlock block, int enabled);
/**
* @brief Adjacency accessors. `out` receives a borrowed handle (NULL when
* @brief Adjacency accessors. `out` receives a borrowed handle (empty when
* there is no neighbour / the block is not on a track).
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_block_get_previous(OakNodeBlock *block, OakNodeBlock **out);
int oaknode_block_get_next(OakNodeBlock *block, OakNodeBlock **out);
int oaknode_block_get_track(OakNodeBlock *block, OakNodeTrack **out);
int oaknode_block_get_previous(OakNodeBlock block, OakNodeBlock *out);
int oaknode_block_get_next(OakNodeBlock block, OakNodeBlock *out);
int oaknode_block_get_track(OakNodeBlock block, OakNodeTrack *out);
/**
* @brief Link two blocks (olive::Node::link/unlink/are_linked).
@@ -181,24 +205,24 @@ int oaknode_block_get_track(OakNodeBlock *block, OakNodeTrack **out);
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_FAILED (already
* linked / not linked).
*/
int oaknode_block_link(OakNodeBlock *a, OakNodeBlock *b);
int oaknode_block_unlink(OakNodeBlock *a, OakNodeBlock *b);
int oaknode_block_are_linked(OakNodeBlock *a, OakNodeBlock *b, int *linked);
int oaknode_block_link(OakNodeBlock a, OakNodeBlock b);
int oaknode_block_unlink(OakNodeBlock a, OakNodeBlock b);
int oaknode_block_are_linked(OakNodeBlock a, OakNodeBlock b, int *linked);
/**
* @brief Number of blocks linked to `block` (olive::Node::links()).
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_block_get_link_count(OakNodeBlock *block, int *count);
int oaknode_block_get_link_count(OakNodeBlock block, int *count);
/**
* @brief Borrowed handle to the linked block at `index`.
*
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND.
*/
int oaknode_block_get_link_at(OakNodeBlock *block, int index,
OakNodeBlock **out);
int oaknode_block_get_link_at(OakNodeBlock block, int index,
OakNodeBlock *out);
/* ---------------------------------------------------------------- Clip */
@@ -206,41 +230,41 @@ int oaknode_block_get_link_at(OakNodeBlock *block, int index,
* @brief Media in/out accessors (olive::ClipBlock). Non-clip blocks return
* OAKNODE_E_INVALID.
*/
int oaknode_clip_get_media_in(OakNodeBlock *clip, int *numerator,
int oaknode_clip_get_media_in(OakNodeBlock clip, int *numerator,
int *denominator);
int oaknode_clip_set_media_in(OakNodeBlock *clip, int numerator,
int oaknode_clip_set_media_in(OakNodeBlock clip, int numerator,
int denominator);
/**
* @brief Playback speed factor, 1.0 = normal (olive::ClipBlock speed input).
*/
int oaknode_clip_get_speed(OakNodeBlock *clip, double *speed);
int oaknode_clip_set_speed(OakNodeBlock *clip, double speed);
int oaknode_clip_get_speed(OakNodeBlock clip, double *speed);
int oaknode_clip_set_speed(OakNodeBlock clip, double speed);
/**
* @brief Reverse playback flag.
*/
int oaknode_clip_get_reverse(OakNodeBlock *clip, int *reverse);
int oaknode_clip_set_reverse(OakNodeBlock *clip, int reverse);
int oaknode_clip_get_reverse(OakNodeBlock clip, int *reverse);
int oaknode_clip_set_reverse(OakNodeBlock clip, int reverse);
/**
* @brief Maintain-audio-pitch flag.
*/
int oaknode_clip_get_maintain_audio_pitch(OakNodeBlock *clip, int *maintain);
int oaknode_clip_set_maintain_audio_pitch(OakNodeBlock *clip, int maintain);
int oaknode_clip_get_maintain_audio_pitch(OakNodeBlock clip, int *maintain);
int oaknode_clip_set_maintain_audio_pitch(OakNodeBlock clip, int maintain);
/**
* @brief Loop mode, one of the OakLoopMode values
* (olive::ClipBlock::loop_mode/set_loop_mode).
*/
int oaknode_clip_get_loop_mode(OakNodeBlock *clip, int *loop_mode);
int oaknode_clip_set_loop_mode(OakNodeBlock *clip, int loop_mode);
int oaknode_clip_get_loop_mode(OakNodeBlock clip, int *loop_mode);
int oaknode_clip_set_loop_mode(OakNodeBlock clip, int loop_mode);
/**
* @brief Type of the track the clip sits on (OakNodeTrackType values,
* OAKNODE_TRACK_TYPE_NONE when trackless).
*/
int oaknode_clip_get_track_type(OakNodeBlock *clip, int *type);
int oaknode_clip_get_track_type(OakNodeBlock clip, int *type);
/* ----------------------------------------------------------- Transition */
@@ -248,39 +272,39 @@ int oaknode_clip_get_track_type(OakNodeBlock *clip, int *type);
* @brief Transition offsets (olive::TransitionBlock). Non-transition blocks
* return OAKNODE_E_INVALID.
*/
int oaknode_transition_get_in_offset(OakNodeBlock *transition, int *numerator,
int oaknode_transition_get_in_offset(OakNodeBlock transition, int *numerator,
int *denominator);
int oaknode_transition_get_out_offset(OakNodeBlock *transition, int *numerator,
int oaknode_transition_get_out_offset(OakNodeBlock transition, int *numerator,
int *denominator);
int oaknode_transition_get_offset_center(OakNodeBlock *transition,
int oaknode_transition_get_offset_center(OakNodeBlock transition,
int *numerator, int *denominator);
int oaknode_transition_set_offset_center(OakNodeBlock *transition,
int oaknode_transition_set_offset_center(OakNodeBlock transition,
int numerator, int denominator);
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);
/**
* @brief Whether both sides of the transition are connected to clips.
*/
int oaknode_transition_is_dual(OakNodeBlock *transition, int *dual);
int oaknode_transition_is_dual(OakNodeBlock transition, int *dual);
/**
* @brief Borrowed handles to the connected out/in side blocks (NULL when
* @brief Borrowed handles to the connected out/in side blocks (empty when
* unconnected).
*/
int oaknode_transition_get_connected_out_block(OakNodeBlock *transition,
OakNodeBlock **out);
int oaknode_transition_get_connected_in_block(OakNodeBlock *transition,
OakNodeBlock **out);
int oaknode_transition_get_connected_out_block(OakNodeBlock transition,
OakNodeBlock *out);
int oaknode_transition_get_connected_in_block(OakNodeBlock transition,
OakNodeBlock *out);
/**
* @brief Forward cache passthroughs from another clip
* (ClipBlock::add_cache_passthrough_from()). Used after splitting a
* clip so the new part shares the render caches.
*/
int oaknode_clip_add_cache_passthrough_from(OakNodeBlock *clip,
OakNodeBlock *other);
int oaknode_clip_add_cache_passthrough_from(OakNodeBlock clip,
OakNodeBlock other);
#ifdef __cplusplus
}
+43 -36
View File
@@ -25,28 +25,31 @@
#include <stdbool.h>
#endif
#include <stdint.h>
#include "common/colortransform.h"
#include "node/error.h"
#include "node/project.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Opaque handle to a color manager (olive::ColorManager).
* @brief Reference-counted handle to a color manager
* (olive::ColorManager).
*
* Unlike node handles this one IS a wrapper allocation (ColorManager is
* not a Node); release with oaknode_colormanager_free().
* Semantics are shared_ptr-like: oaknode_colormanager_init() returns a
* handle whose object has reference count 1, addref(ctx) takes another
* reference, and release(ctx) (or oaknode_colormanager_free()) drops
* one; the library destroys the object when the count reaches zero.
*/
typedef struct OakNodeColorManager OakNodeColorManager;
/**
* @brief Opaque borrowed handle to a project (olive::Project).
*
* Owned by the project family; re-declared here so this header is
* self-contained.
*/
typedef struct OakNodeProject OakNodeProject;
typedef struct OakNodeColorManager {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeColorManager;
/**
* @brief Create a color manager bound to `project` (borrowed).
@@ -56,12 +59,16 @@ typedef struct OakNodeProject OakNodeProject;
* oaknode_colormanager_update_config_from_filename()) before using the
* config-dependent queries.
*
* @return Manager handle, or NULL on NULL project / allocation failure.
* @return Manager handle with reference count 1 (release with
* oaknode_colormanager_free()); ctx is NULL on an empty project
* handle or allocation failure.
*/
OakNodeColorManager *oaknode_colormanager_init(OakNodeProject *project);
OakNodeColorManager oaknode_colormanager_init(OakNodeProject project);
/**
* @brief Destroy a color manager. No-op on NULL.
* @brief Release the caller's reference to the color manager and null
* out the handle. No-op on NULL or an empty handle; the object is
* destroyed when its reference count reaches zero.
*/
void oaknode_colormanager_free(OakNodeColorManager *manager);
@@ -72,7 +79,7 @@ void oaknode_colormanager_free(OakNodeColorManager *manager);
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_FAILED (the OCIO
* config could not be created).
*/
int oaknode_colormanager_initialize(OakNodeColorManager *manager);
int oaknode_colormanager_initialize(OakNodeColorManager manager);
/**
* @brief (Re)build the process-wide default OCIO config
@@ -87,9 +94,9 @@ int oaknode_colormanager_set_up_default_config(void);
* returns the required buffer size in bytes including NUL; pass
* buf == NULL or a too-small buffer to query the size.
*/
int oaknode_colormanager_get_config_filename(OakNodeColorManager *manager,
int oaknode_colormanager_get_config_filename(OakNodeColorManager manager,
char *buf, int buf_size);
int oaknode_colormanager_set_config_filename(OakNodeColorManager *manager,
int oaknode_colormanager_set_config_filename(OakNodeColorManager manager,
const char *filename);
/**
@@ -98,21 +105,21 @@ int oaknode_colormanager_set_config_filename(OakNodeColorManager *manager,
* olive::ColorManager::update_config_from_filename().
*/
int oaknode_colormanager_update_config_from_filename(
OakNodeColorManager *manager);
OakNodeColorManager manager);
/**
* @brief Default input colorspace. Two-stage string accessor.
*/
int oaknode_colormanager_get_default_input_color_space(
OakNodeColorManager *manager, char *buf, int buf_size);
OakNodeColorManager manager, char *buf, int buf_size);
int oaknode_colormanager_set_default_input_color_space(
OakNodeColorManager *manager, const char *colorspace);
OakNodeColorManager manager, const char *colorspace);
/**
* @brief Reference (working) colorspace. Two-stage string getter.
*/
int oaknode_colormanager_get_reference_color_space(
OakNodeColorManager *manager, char *buf, int buf_size);
OakNodeColorManager manager, char *buf, int buf_size);
/**
* @brief Return `colorspace` when the active config lists it, otherwise the
@@ -120,7 +127,7 @@ int oaknode_colormanager_get_reference_color_space(
* (OAKNODE_E_STATE when none is loaded).
*/
int oaknode_colormanager_get_compliant_color_space(
OakNodeColorManager *manager, const char *colorspace, char *buf,
OakNodeColorManager manager, const char *colorspace, char *buf,
int buf_size);
/**
@@ -130,7 +137,7 @@ int oaknode_colormanager_get_compliant_color_space(
* (OAKNODE_E_STATE when none is loaded).
*/
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);
/**
@@ -138,27 +145,27 @@ int oaknode_colormanager_get_colorspace_for_ffmpeg_tags(
* All require a loaded config (OAKNODE_E_STATE otherwise); index out of
* range yields OAKNODE_E_NOT_FOUND.
*/
int oaknode_colormanager_get_display_count(OakNodeColorManager *manager,
int oaknode_colormanager_get_display_count(OakNodeColorManager manager,
int *count);
int oaknode_colormanager_get_display_at(OakNodeColorManager *manager,
int oaknode_colormanager_get_display_at(OakNodeColorManager manager,
int index, char *buf, int buf_size);
int oaknode_colormanager_get_default_display(OakNodeColorManager *manager,
int oaknode_colormanager_get_default_display(OakNodeColorManager manager,
char *buf, int buf_size);
int oaknode_colormanager_get_view_count(OakNodeColorManager *manager,
int oaknode_colormanager_get_view_count(OakNodeColorManager manager,
const char *display, int *count);
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);
int oaknode_colormanager_get_default_view(OakNodeColorManager *manager,
int oaknode_colormanager_get_default_view(OakNodeColorManager manager,
const char *display, char *buf,
int buf_size);
int oaknode_colormanager_get_look_count(OakNodeColorManager *manager,
int oaknode_colormanager_get_look_count(OakNodeColorManager manager,
int *count);
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);
int oaknode_colormanager_get_colorspace_count(OakNodeColorManager *manager,
int oaknode_colormanager_get_colorspace_count(OakNodeColorManager manager,
int *count);
int oaknode_colormanager_get_colorspace_at(OakNodeColorManager *manager,
int oaknode_colormanager_get_colorspace_at(OakNodeColorManager manager,
int index, char *buf,
int buf_size);
@@ -166,7 +173,7 @@ int oaknode_colormanager_get_colorspace_at(OakNodeColorManager *manager,
* @brief Default luma coefficients of the active config into rgb[3].
* Requires a loaded config (OAKNODE_E_STATE otherwise).
*/
int oaknode_colormanager_get_default_luma_coefs(OakNodeColorManager *manager,
int oaknode_colormanager_get_default_luma_coefs(OakNodeColorManager manager,
double rgb[3]);
/**
@@ -179,7 +186,7 @@ int oaknode_colormanager_get_default_luma_coefs(OakNodeColorManager *manager,
* loaded config (OAKNODE_E_STATE otherwise).
*/
int oaknode_colormanager_get_compliant_color_transform(
OakNodeColorManager *manager, OakColorTransform transform,
OakNodeColorManager manager, OakColorTransform transform,
int force_display, OakColorTransform *out);
#ifdef __cplusplus
+10
View File
@@ -29,6 +29,16 @@
* failure. String getters return the required buffer size in bytes
* (including the terminating NUL) as a non-negative value instead.
*/
/**
* @brief Current ABI version stamped into every oaknode handle.
*
* Bump whenever a handle layout or the semantics of any exported
* function change incompatibly. Consumers should compare a handle's
* abi_version field against the value they were compiled with before
* dereferencing ctx.
*/
#define OAKNODE_ABI_VERSION 1
#define OAKNODE_OK 0 /**< Success. */
#define OAKNODE_E_INVALID (-1) /**< NULL handle or invalid argument. */
#define OAKNODE_E_STATE (-2) /**< Call not valid in the current state. */
+12 -8
View File
@@ -34,9 +34,11 @@ extern "C" {
* internal node-type library.
*
* The library must be populated with oaknode_factory_initialize() before
* any other call; oaknode_factory_destroy() releases it. Prototype nodes
* from oaknode_factory_node_at() are owned by the library: read-only
* metadata queries only, never free them or add them to a graph.
* any other call; oaknode_factory_destroy() releases it. The factory is
* a process-wide singleton (static olive::NodeFactory), so there is no
* OakNodeFactory handle type. Prototype nodes from
* oaknode_factory_node_at() are owned by the library: read-only metadata
* queries only, never add them to a graph.
*/
/**
@@ -77,17 +79,19 @@ int oaknode_factory_name_from_id(const char *type_id, char *buf,
/**
* @brief Create a node of `type_id` WITHOUT adding it to any graph
* (NodeFactory::create_from_id()). The caller owns the returned node
* and must release it with oaknode_node_free() while it is still
* orphaned. Returns NULL when the id is unknown or not initialized.
* (reference count 1) and must release it with oaknode_node_free() while
* it is still orphaned. ctx is NULL when the id is unknown or not
* initialized.
*/
OakNodeNode *oaknode_factory_create_from_id(const char *type_id);
OakNodeNode oaknode_factory_create_from_id(const char *type_id);
/**
* @brief Borrow the prototype node at `index` in the library.
* @brief Borrow the prototype node at `index` in the library (non-owning
* handle written to `out_node`; release it with oaknode_node_free()).
* OAKNODE_E_NOT_FOUND for an out-of-range index, OAKNODE_E_STATE when
* not initialized.
*/
int oaknode_factory_node_at(int index, OakNodeNode **out_node);
int oaknode_factory_node_at(int index, OakNodeNode *out_node);
#ifdef __cplusplus
}
+58 -25
View File
@@ -21,6 +21,8 @@
#ifndef OAK_EDITOR_NODE_FOLDER_H
#define OAK_EDITOR_NODE_FOLDER_H
#include <stdint.h>
#include "node/error.h"
#include "undo/undocommand.h"
#include "node/project.h"
@@ -42,50 +44,76 @@ extern "C" {
* facade layer's job, not this layer's.
*/
/**
* @brief Reference-counted handle to a folder node (olive::Folder).
*
* Semantics are shared_ptr-like (see OakNodeProject): addref(ctx) takes a
* reference, release(ctx) drops one. Folder handles handed out by this API
* are borrowed views into the owning project's graph: releasing them only
* releases the handle itself, never the folder.
*/
typedef struct OakNodeFolder {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeFolder;
/**
* @brief Create a folder node owned by `project`.
*
* The folder is added to the project's graph (Project::add_node()) but is
* NOT attached under any parent folder; use oaknode_folder_add_child() to
* place it. The handle is borrowed: the project owns the folder.
* place it. The returned handle is borrowed: the project owns the folder,
* so releasing the handle only releases the handle itself.
*
* @return Folder handle, or NULL on failure.
* @return Folder handle; ctx is NULL on failure.
*/
OakNodeFolder *oaknode_folder_create(OakNodeProject *project);
OakNodeFolder oaknode_folder_create(OakNodeProject project);
/**
* @brief Number of direct item children (Folder::item_child_count()).
* Negative OAKNODE_E_* code on NULL.
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_folder_child_count(const OakNodeFolder *folder);
int oaknode_folder_child_count(OakNodeFolder folder);
/**
* @brief Borrowed node handle of the item child at `index`
* (Folder::item_child()). NULL when out of range.
* (Folder::item_child()).
*
* The returned handle only releases the handle itself. Empty handle
* (ctx == NULL) when out of range.
*/
OakNodeNode *oaknode_folder_child_at(const OakNodeFolder *folder, int index);
OakNodeNode oaknode_folder_child_at(OakNodeFolder folder, int index);
/**
* @brief Add `child` as a direct item child of `folder` (live, non-undoable;
* executes FolderAddChild::redo()).
*
* After a successful call the graph owns `child`: releasing the child
* handle only releases the handle itself.
*
* @return OAKNODE_OK, OAKNODE_E_STATE if `child` already belongs to a
* folder, or another negative OAKNODE_E_* error code.
*/
int oaknode_folder_add_child(OakNodeFolder *folder, OakNodeNode *child);
int oaknode_folder_add_child(OakNodeFolder folder, OakNodeNode child);
/**
* @brief Borrowed cast from a folder handle to its node handle.
* NULL for NULL.
*
* The returned handle only releases the handle itself. Empty handle for an
* empty handle.
*/
OakNodeNode *oaknode_folder_as_node(OakNodeFolder *folder);
OakNodeNode oaknode_folder_as_node(OakNodeFolder folder);
/**
* @brief Create an undoable FolderAddChild command. Owned; free with
* oakundo_command_free().
* @brief Create an undoable FolderAddChild command.
*
* @return Command handle with reference count 1 (release with
* oakundo_command_free()); ctx is NULL on failure.
*/
OakUndoCommand oaknode_command_create_folder_add_child(
OakNodeFolder *folder, OakNodeNode *child);
OakNodeFolder folder, OakNodeNode child);
/**
* @brief Remove `child` from `folder` without deleting it (live,
@@ -94,41 +122,46 @@ OakUndoCommand oaknode_command_create_folder_add_child(
* @return OAKNODE_OK, OAKNODE_E_NOT_FOUND if `child` is not a direct child,
* or another negative OAKNODE_E_* error code.
*/
int oaknode_folder_remove_child(OakNodeFolder *folder, OakNodeNode *child);
int oaknode_folder_remove_child(OakNodeFolder folder, OakNodeNode child);
/**
* @brief Move several nodes into `dest_folder` (live, non-undoable).
*
* Each node is removed from its current folder (if any) and appended to
* `dest_folder`. Nodes already directly inside `dest_folder` are skipped.
* `dest_folder`; the graph assumes the lifetime of every moved node. Nodes
* already directly inside `dest_folder` are skipped.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
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);
/**
* @brief 1 if `folder` recursively contains `child`, 0 otherwise
* (Folder::has_child_recursive()). Negative OAKNODE_E_* code on NULL args.
* (Folder::has_child_recursive()). Negative OAKNODE_E_* code on empty
* handles.
*/
int oaknode_folder_has_child_recursive(const OakNodeFolder *folder,
const OakNodeNode *child);
int oaknode_folder_has_child_recursive(OakNodeFolder folder,
OakNodeNode child);
/**
* @brief Index of `child` in `folder`'s direct children
* (Folder::index_of_child()).
*
* @return The index, OAKNODE_E_NOT_FOUND if not a direct child, or
* OAKNODE_E_INVALID on NULL args.
* OAKNODE_E_INVALID on empty handles.
*/
int oaknode_folder_index_of_child(const OakNodeFolder *folder,
const OakNodeNode *child);
int oaknode_folder_index_of_child(OakNodeFolder folder,
OakNodeNode child);
/**
* @brief Borrowed handle of the folder a node currently belongs to
* (Node::folder()), or NULL if the node is not in any folder.
* (Node::folder()).
*
* The returned handle only releases the handle itself. Empty handle
* (ctx == NULL) if the node is not in any folder.
*/
OakNodeFolder *oaknode_folder_parent_of(const OakNodeNode *node);
OakNodeFolder oaknode_folder_parent_of(OakNodeNode node);
#ifdef __cplusplus
}
+52 -36
View File
@@ -50,26 +50,41 @@ extern "C" {
*/
/**
* @brief Opaque footage handle. Borrowed from the owning project.
* @brief Reference-counted handle to a footage node (olive::Footage).
*
* Semantics are shared_ptr-like (see OakNodeProject): addref(ctx) takes a
* reference, release(ctx) drops one. Footage handles handed out by this
* API are borrowed views into the owning project's graph: releasing them
* only releases the handle itself, never the footage.
*/
typedef struct OakNodeFootage OakNodeFootage;
typedef struct OakNodeFootage {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeFootage;
/**
* @brief Create a footage node owned by `project` (added to the project's
* graph, not attached to any folder).
*
* The returned handle is borrowed: the project owns the footage, so
* releasing the handle only releases the handle itself.
*
* @param filename Initial media path, may be NULL/empty.
*
* @return Footage handle, or NULL on failure.
* @return Footage handle; ctx is NULL on failure.
*/
OakNodeFootage *oaknode_footage_create(OakNodeProject *project,
const char *filename);
OakNodeFootage oaknode_footage_create(OakNodeProject project,
const char *filename);
/**
* @brief Borrowed cast from a footage handle to its node handle.
* NULL for NULL.
*
* The returned handle only releases the handle itself. Empty handle for an
* empty handle.
*/
OakNodeNode *oaknode_footage_as_node(OakNodeFootage *footage);
OakNodeNode oaknode_footage_as_node(OakNodeFootage footage);
/**
* @brief Current media path (Footage::filename()). Two-stage string getter.
@@ -77,7 +92,7 @@ OakNodeNode *oaknode_footage_as_node(OakNodeFootage *footage);
* @return Required buffer size in bytes including the NUL, or a negative
* OAKNODE_E_* error code.
*/
int oaknode_footage_filename(const OakNodeFootage *footage, char *buf,
int oaknode_footage_filename(OakNodeFootage footage, char *buf,
int buf_size);
/**
@@ -86,13 +101,14 @@ int oaknode_footage_filename(const OakNodeFootage *footage, char *buf,
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_set_filename(OakNodeFootage *footage, const char *filename);
int oaknode_footage_set_filename(OakNodeFootage footage, const char *filename);
/**
* @brief 1 if the footage was successfully probed and is ready for use
* (Footage::is_valid()), 0 otherwise. Negative OAKNODE_E_* code on NULL.
* (Footage::is_valid()), 0 otherwise. Negative OAKNODE_E_* code on an
* empty handle.
*/
int oaknode_footage_is_valid(const OakNodeFootage *footage);
int oaknode_footage_is_valid(OakNodeFootage footage);
/**
* @brief Last-modified timestamp of the media file in milliseconds since the
@@ -102,7 +118,7 @@ int oaknode_footage_is_valid(const OakNodeFootage *footage);
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_timestamp(const OakNodeFootage *footage,
int oaknode_footage_timestamp(OakNodeFootage footage,
int64_t *out_timestamp);
/**
@@ -110,38 +126,38 @@ int oaknode_footage_timestamp(const OakNodeFootage *footage,
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_set_timestamp(OakNodeFootage *footage, int64_t timestamp);
int oaknode_footage_set_timestamp(OakNodeFootage footage, int64_t timestamp);
/**
* @brief Decoder ID recorded when the footage was probed
* (Footage::decoder()). Two-stage string getter.
*/
int oaknode_footage_decoder(const OakNodeFootage *footage, char *buf,
int oaknode_footage_decoder(OakNodeFootage footage, char *buf,
int buf_size);
/**
* @brief Total number of streams (Footage::get_total_stream_count()).
* Negative OAKNODE_E_* code on NULL.
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_total_stream_count(const OakNodeFootage *footage);
int oaknode_footage_total_stream_count(OakNodeFootage footage);
/**
* @brief Number of video streams (ViewerOutput::get_video_stream_count()).
* Negative OAKNODE_E_* code on NULL.
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_video_stream_count(const OakNodeFootage *footage);
int oaknode_footage_video_stream_count(OakNodeFootage footage);
/**
* @brief Number of audio streams (ViewerOutput::get_audio_stream_count()).
* Negative OAKNODE_E_* code on NULL.
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_audio_stream_count(const OakNodeFootage *footage);
int oaknode_footage_audio_stream_count(OakNodeFootage footage);
/**
* @brief Number of subtitle streams (ViewerOutput::get_subtitle_stream_count()).
* Negative OAKNODE_E_* code on NULL.
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_subtitle_stream_count(const OakNodeFootage *footage);
int oaknode_footage_subtitle_stream_count(OakNodeFootage footage);
/**
* @brief Footage duration as a rational number of seconds
@@ -152,34 +168,34 @@ int oaknode_footage_subtitle_stream_count(const OakNodeFootage *footage);
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_duration(const OakNodeFootage *footage,
int *out_numerator, int *out_denominator);
int oaknode_footage_duration(OakNodeFootage footage, int *out_numerator,
int *out_denominator);
/**
* @brief 1 if proxy playback is enabled (Footage::proxy_enabled()).
* Negative OAKNODE_E_* code on NULL.
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_proxy_enabled(const OakNodeFootage *footage);
int oaknode_footage_proxy_enabled(OakNodeFootage footage);
/**
* @brief Enable/disable proxy playback (Footage::set_proxy_enabled()).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_set_proxy_enabled(OakNodeFootage *footage, int enabled);
int oaknode_footage_set_proxy_enabled(OakNodeFootage footage, int enabled);
/**
* @brief Proxy file path, or "" when none (Footage::proxy_path()).
* Two-stage string getter.
*/
int oaknode_footage_proxy_path(const OakNodeFootage *footage, char *buf,
int oaknode_footage_proxy_path(OakNodeFootage footage, char *buf,
int buf_size);
/**
* @brief Proxy state enum value (Footage::proxy_state():
* ProxyManager::ProxyState). Negative OAKNODE_E_* code on NULL.
* ProxyManager::ProxyState). Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_footage_proxy_state(const OakNodeFootage *footage);
int oaknode_footage_proxy_state(OakNodeFootage footage);
/**
* @brief Set all proxy fields at once (Footage::set_proxy()).
@@ -192,7 +208,7 @@ int oaknode_footage_proxy_state(const OakNodeFootage *footage);
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
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);
@@ -201,7 +217,7 @@ int oaknode_footage_set_proxy(OakNodeFootage *footage, const char *path,
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_footage_clear_proxy(OakNodeFootage *footage);
int oaknode_footage_clear_proxy(OakNodeFootage footage);
/**
* @brief Video stream parameters as an oakcommon video-params handle
@@ -209,20 +225,20 @@ int oaknode_footage_clear_proxy(OakNodeFootage *footage);
* reference count 1 (release with oakcommon_videoparams_free()).
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_footage_get_video_params(OakNodeFootage *footage, int index,
int oaknode_footage_get_video_params(OakNodeFootage footage, int index,
OakVideoParams *out);
/**
* @brief Set a video stream's parameters from an oakcommon handle
* (ViewerOutput::set_video_params()).
*/
int oaknode_footage_set_video_params(OakNodeFootage *footage, int index,
int oaknode_footage_set_video_params(OakNodeFootage footage, int index,
const OakVideoParams *params);
/**
* @brief Video length as a rational pair (ViewerOutput::get_video_length()).
*/
int oaknode_footage_get_video_length(OakNodeFootage *footage,
int oaknode_footage_get_video_length(OakNodeFootage footage,
int64_t *out_num, int64_t *out_den);
/**
@@ -230,7 +246,7 @@ int oaknode_footage_get_video_length(OakNodeFootage *footage,
* (Footage::set_cancel_pointer()). `atom` may be an empty OakCancelAtom
* (ctx == NULL) to clear.
*/
int oaknode_footage_set_cancel_atom(OakNodeFootage *footage,
int oaknode_footage_set_cancel_atom(OakNodeFootage footage,
OakCancelAtom atom);
#ifdef __cplusplus
+60 -37
View File
@@ -21,6 +21,8 @@
#ifndef OAK_EDITOR_NODE_GROUP_H
#define OAK_EDITOR_NODE_GROUP_H
#include <stdint.h>
#include "node/error.h"
#include "node/node.h"
#include "undo/undocommand.h"
@@ -34,33 +36,51 @@ extern "C" {
* @brief C ABI for olive::NodeGroup (src/node/src/group/group.h):
* input passthrough management and input resolution.
*
* An OakNodeGroup is a reinterpreted olive::NodeGroup (a Node subclass);
* group handles borrow the same lifetime rules as OakNodeNode.
* An OakNodeGroup wraps an olive::NodeGroup (a Node subclass); group
* handles share the reference-counted lifetime rules of OakNodeNode.
*/
/**
* @brief Opaque group handle (olive::NodeGroup).
* @brief Reference-counted handle to a node group (olive::NodeGroup).
*
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* oaknode_group_create() returns a handle with count 1, addref(ctx)
* takes another reference, release(ctx) drops one and the library
* destroys the object when the count reaches zero. Handles returned by
* oaknode_group_cast() are borrowed views of a node: releasing them
* never destroys the underlying group.
*/
typedef struct OakNodeGroup OakNodeGroup;
typedef struct OakNodeGroup {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeGroup;
/**
* @brief Create a standalone NodeGroup (owned; release with
* oaknode_node_free() on the OakNodeNode view or oaknode_group_free()
* while still orphaned).
* oaknode_group_free() while still orphaned).
*
* @return Group handle, or NULL on allocation failure.
* @return Group handle with count 1; ctx is NULL on allocation failure.
*/
OakNodeGroup *oaknode_group_create(void);
OakNodeGroup oaknode_group_create(void);
/**
* @brief Borrow a group view of a node, or NULL when the node is not a
* NodeGroup (dynamic_cast).
* @brief Borrow a group view of a node (dynamic_cast). The returned
* handle is non-owning; release it with oaknode_group_free().
*
* @return Borrowed group handle; ctx is NULL when the node is not a
* NodeGroup.
*/
OakNodeGroup *oaknode_group_cast(OakNodeNode *node);
OakNodeGroup oaknode_group_cast(OakNodeNode node);
/**
* @brief Destroy an OWNED group (same rules as oaknode_node_free()).
* NULL is a no-op.
* @brief Release one reference to a group handle.
*
* Convenience wrapper around handle.release(handle.ctx): destroys the
* group when the count reaches zero and the handle owns it. NULL handle
* or NULL ctx is a no-op; clears `group->ctx` after releasing.
*/
void oaknode_group_free(OakNodeGroup *group);
@@ -72,8 +92,8 @@ void oaknode_group_free(OakNodeGroup *group);
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKNODE_E_* error code.
*/
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);
@@ -84,8 +104,8 @@ int oaknode_group_add_input_passthrough(OakNodeGroup *group,
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
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);
@@ -94,66 +114,69 @@ int oaknode_group_add_input_passthrough_undoable(OakNodeGroup *group,
* @brief Remove the passthrough for (`node`, `input_id`, `element`)
* (live). OAKNODE_E_NOT_FOUND when no such passthrough exists.
*/
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);
/**
* @brief Number of registered input passthroughs.
*/
int oaknode_group_passthrough_count(const OakNodeGroup *group, int *out_count);
int oaknode_group_passthrough_count(OakNodeGroup group, int *out_count);
/**
* @brief The passthrough id at `index`. Two-stage getter;
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
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);
/**
* @brief The inner input behind passthrough `index`: node (borrowed
* handle), input id (two-stage string) and element.
* handle written to `out_node` when non-NULL; release it with
* oaknode_node_free()), input id (two-stage string) and element.
* OAKNODE_E_NOT_FOUND for an out-of-range 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);
/**
* @brief The output passthrough node (borrowed handle), or NULL when
* @brief The output passthrough node (borrowed handle written to
* `out_node`; release it with oaknode_node_free()), an empty handle when
* unset. OAKNODE_OK is returned either way.
*/
int oaknode_group_get_output_passthrough(const OakNodeGroup *group,
OakNodeNode **out_node);
int oaknode_group_get_output_passthrough(OakNodeGroup group,
OakNodeNode *out_node);
/**
* @brief Set the output passthrough node directly (live).
* @brief Set the output passthrough node directly (live). `node` may be
* an empty handle to clear the passthrough.
*/
int oaknode_group_set_output_passthrough(OakNodeGroup *group,
OakNodeNode *node);
int oaknode_group_set_output_passthrough(OakNodeGroup group,
OakNodeNode node);
/**
* @brief Create a set-output-passthrough command
* (olive::NodeGroupSetOutputPassthrough).
*/
int oaknode_group_set_output_passthrough_undoable(
OakNodeGroup *group, OakNodeNode *node, OakUndoCommand *out_command);
OakNodeGroup group, OakNodeNode node, OakUndoCommand *out_command);
/**
* @brief Resolve an input through group passthroughs
* (NodeGroup::resolve_input()): follows a group's passthrough id to the
* inner node input. Non-group inputs resolve to themselves.
*
* `out_node` (may be NULL) receives a borrowed handle; the resolved input
* id uses the two-stage string convention; `out_element` (may be NULL)
* receives the element. OAKNODE_E_NOT_FOUND when the input does not
* resolve to a valid target.
* `out_node` (may be NULL) receives a borrowed handle (release it with
* oaknode_node_free()); the resolved input id uses the two-stage string
* convention; `out_element` (may be NULL) receives the element.
* OAKNODE_E_NOT_FOUND when the input does not resolve to a valid target.
*
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKNODE_E_* error code.
*/
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);
#ifdef __cplusplus
+54 -38
View File
@@ -35,10 +35,10 @@ extern "C" {
* @file keyframe.h
* @brief C ABI for olive::NodeKeyframe (src/node/src/keyframe.h).
*
* An OakNodeKeyframe is a reinterpreted olive::NodeKeyframe. Handles
* created by oaknode_keyframe_create() are owned and must be released
* with oaknode_keyframe_free(); keyframes attached to a node input's
* track are owned by the node.
* An OakNodeKeyframe wraps an olive::NodeKeyframe. Handles created by
* oaknode_keyframe_create() are owned and must be released with
* oaknode_keyframe_free(); keyframes attached to a node input's track
* are owned by the node.
*
* Every setter comes in a live variant and an undoable variant (suffix
* _undoable) returning an owned, un-executed OakUndoCommand.
@@ -63,9 +63,20 @@ typedef enum oaknode_keyframe_bezier {
} oaknode_keyframe_bezier;
/**
* @brief Opaque keyframe handle (olive::NodeKeyframe).
* @brief Reference-counted handle to a keyframe (olive::NodeKeyframe).
*
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* oaknode_keyframe_create() returns a handle with count 1, addref(ctx)
* takes another reference, release(ctx) drops one and the library
* destroys the object when the count reaches zero.
*/
typedef struct OakNodeKeyframe OakNodeKeyframe;
typedef struct OakNodeKeyframe {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeKeyframe;
/**
* @brief Create a standalone keyframe (owned; release with
@@ -73,20 +84,24 @@ typedef struct OakNodeKeyframe OakNodeKeyframe;
*
* `value` may be NULL (null variant); OAKNODE_VALUE_STRING is rejected
* (use oaknode_keyframe_set_value_string() after creation). `type` is an
* oaknode_keyframe_type. `parent_or_null` may be NULL.
* oaknode_keyframe_type. `parent_or_null` may be an empty handle.
*
* @return Keyframe handle, or NULL on invalid argument or allocation
* failure.
* @return Keyframe handle with count 1; ctx is NULL on invalid argument
* or allocation failure.
*/
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);
/**
* @brief Destroy an OWNED keyframe. NULL is a no-op. Never free a
* keyframe that is attached to a node's track.
* @brief Release one reference to a keyframe handle.
*
* Convenience wrapper around handle.release(handle.ctx): destroys the
* keyframe when the count reaches zero and the handle owns it. NULL
* handle or NULL ctx is a no-op; clears `keyframe->ctx` after releasing.
* Never free a keyframe that is attached to a node's track.
*/
void oaknode_keyframe_free(OakNodeKeyframe *keyframe);
@@ -95,19 +110,19 @@ void oaknode_keyframe_free(OakNodeKeyframe *keyframe);
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_keyframe_get_time(const OakNodeKeyframe *keyframe,
int oaknode_keyframe_get_time(OakNodeKeyframe keyframe,
int64_t *out_num, int64_t *out_den);
/**
* @brief Set the keyframe's time directly (live).
*/
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);
/**
* @brief Create a set-time command (olive::NodeParamSetKeyframeTimeCommand).
*/
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);
@@ -115,7 +130,7 @@ int oaknode_keyframe_set_time_undoable(OakNodeKeyframe *keyframe,
* @brief Read the keyframe's value mapped into `out`. Values without a
* POD representation fail with OAKNODE_E_FAILED.
*/
int oaknode_keyframe_get_value(const OakNodeKeyframe *keyframe,
int oaknode_keyframe_get_value(OakNodeKeyframe keyframe,
oaknode_value *out);
/**
@@ -123,14 +138,14 @@ int oaknode_keyframe_get_value(const OakNodeKeyframe *keyframe,
* OAKNODE_VALUE_STRING is rejected (use
* oaknode_keyframe_set_value_string()).
*/
int oaknode_keyframe_set_value(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_value(OakNodeKeyframe keyframe,
const oaknode_value *v);
/**
* @brief Create a set-value command
* (olive::NodeParamSetKeyframeValueCommand).
*/
int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_value_undoable(OakNodeKeyframe keyframe,
const oaknode_value *v,
OakUndoCommand *out_command);
@@ -140,84 +155,85 @@ int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe,
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKNODE_E_* error code.
*/
int oaknode_keyframe_get_value_string(const OakNodeKeyframe *keyframe,
int oaknode_keyframe_get_value_string(OakNodeKeyframe keyframe,
char *buf, int buf_size);
/**
* @brief Set a string value directly (live).
*/
int oaknode_keyframe_set_value_string(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_value_string(OakNodeKeyframe keyframe,
const char *value);
/**
* @brief Create a set-string-value command.
*/
int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe keyframe,
const char *value,
OakUndoCommand *out_command);
/**
* @brief The keyframe's interpolation type (oaknode_keyframe_type).
*/
int oaknode_keyframe_get_type(const OakNodeKeyframe *keyframe, int *out_type);
int oaknode_keyframe_get_type(OakNodeKeyframe keyframe, int *out_type);
/**
* @brief Set the interpolation type directly (live,
* NodeKeyframe::set_type(), which adjusts neighbouring bezier handles).
*/
int oaknode_keyframe_set_type(OakNodeKeyframe *keyframe, int type);
int oaknode_keyframe_set_type(OakNodeKeyframe keyframe, int type);
/**
* @brief Create a set-type command (same semantics as the live variant).
*/
int oaknode_keyframe_set_type_undoable(OakNodeKeyframe *keyframe, int type,
int oaknode_keyframe_set_type_undoable(OakNodeKeyframe keyframe, int type,
OakUndoCommand *out_command);
/**
* @brief A bezier control point (`handle` is an
* oaknode_keyframe_bezier).
*/
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);
/**
* @brief Set a bezier control point directly (live).
*/
int oaknode_keyframe_set_bezier_control(OakNodeKeyframe *keyframe, int handle,
int oaknode_keyframe_set_bezier_control(OakNodeKeyframe keyframe, int handle,
double x, double y);
/**
* @brief Create a set-bezier-control command.
*/
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);
/**
* @brief The keyframe's track index.
*/
int oaknode_keyframe_get_track(const OakNodeKeyframe *keyframe,
int oaknode_keyframe_get_track(OakNodeKeyframe keyframe,
int *out_track);
/**
* @brief The keyframe's element index.
*/
int oaknode_keyframe_get_element(const OakNodeKeyframe *keyframe,
int oaknode_keyframe_get_element(OakNodeKeyframe keyframe,
int *out_element);
/**
* @brief The id of the input this keyframe belongs to. Two-stage getter.
*/
int oaknode_keyframe_get_input(const OakNodeKeyframe *keyframe, char *buf,
int oaknode_keyframe_get_input(OakNodeKeyframe keyframe, char *buf,
int buf_size);
/**
* @brief The node this keyframe belongs to (borrowed handle), or NULL
* when orphaned. OAKNODE_OK either way.
* @brief The node this keyframe belongs to (borrowed handle written to
* `out_node`; release it with oaknode_node_free()), an empty handle when
* orphaned. OAKNODE_OK either way.
*/
int oaknode_keyframe_get_parent(const OakNodeKeyframe *keyframe,
OakNodeNode **out_node);
int oaknode_keyframe_get_parent(OakNodeKeyframe keyframe,
OakNodeNode *out_node);
#ifdef __cplusplus
}
+134 -124
View File
@@ -35,13 +35,15 @@ extern "C" {
* @file node.h
* @brief C ABI for olive::Node (src/node/src/node.h).
*
* The handle IS the C++ object pointer (M3 handle convention 3): an
* OakNodeNode is a reinterpreted olive::Node, no wrapper allocation.
* Handles borrowed from a graph become invalid when the owning project or
* node is destroyed. Owned handles (from oaknode_factory_create_from_id()
* or oaknode_node_create_copy()) must be released with
* oaknode_node_free() while still orphaned; once a node lives in a project
* graph its lifetime belongs to the graph.
* Handles are by-value reference-counted structs (see
* include/common/handle.h): every OakNodeNode carries ctx/addref/release/
* abi_version and behaves like a shared_ptr at the ABI level. Factory
* functions return a handle with reference count 1; release it with
* oaknode_node_free(). Handles borrowed from a graph only release the
* handle itself when freed; once a node lives in a project graph its
* lifetime belongs to the graph (the implementation flips ownership
* internally), and borrowed handles become invalid when the owning project
* or node is destroyed.
*
* Parameter values cross the boundary as the POD oaknode_value; the
* meaningful fields depend on its type (oaknode_value_type). String-typed
@@ -96,9 +98,21 @@ typedef struct oaknode_value {
} oaknode_value;
/**
* @brief Opaque node handle (olive::Node).
* @brief Reference-counted handle to a node (olive::Node).
*
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* init/factory functions return a handle with reference count 1,
* addref(ctx) takes another reference, release(ctx) drops one; release a
* handle with oaknode_node_free(). Borrowed handles into graph-owned
* objects only release the handle itself.
*/
typedef struct OakNodeNode OakNodeNode;
typedef struct OakNodeNode {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeNode;
/* Re-declared here so node.h is self-contained; see node/project.h. */
typedef struct OakNodeProject OakNodeProject;
@@ -141,26 +155,26 @@ int oaknode_debug_alive_count(void);
* @return Required buffer size in bytes including the terminating NUL
* (non-negative), or a negative OAKNODE_E_* error code.
*/
int oaknode_node_get_id(const OakNodeNode *node, char *buf, int buf_size);
int oaknode_node_get_id(OakNodeNode node, char *buf, int buf_size);
/**
* @brief The node's display name (Node::name()). Two-stage getter,
* same return convention as oaknode_node_get_id().
*/
int oaknode_node_get_name(const OakNodeNode *node, char *buf, int buf_size);
int oaknode_node_get_name(OakNodeNode node, char *buf, int buf_size);
/**
* @brief The node's user label (Node::get_label()). Two-stage getter,
* same return convention as oaknode_node_get_id().
*/
int oaknode_node_get_label(const OakNodeNode *node, char *buf, int buf_size);
int oaknode_node_get_label(OakNodeNode node, char *buf, int buf_size);
/**
* @brief Set the node's user label directly (Node::set_label(), live).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_label(OakNodeNode *node, const char *label);
int oaknode_node_set_label(OakNodeNode node, const char *label);
/**
* @brief Create a label-change command (olive::NodeRenameCommand).
@@ -170,7 +184,7 @@ int oaknode_node_set_label(OakNodeNode *node, const char *label);
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_label_undoable(OakNodeNode *node, const char *label,
int oaknode_node_set_label_undoable(OakNodeNode node, const char *label,
OakUndoCommand *out_command);
/**
@@ -181,21 +195,21 @@ int oaknode_node_set_label_undoable(OakNodeNode *node, const char *label,
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_get_override_color(const OakNodeNode *node, int *out_value);
int oaknode_node_get_override_color(OakNodeNode node, int *out_value);
/**
* @brief Set the override color index directly (-1 = none; live).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_override_color(OakNodeNode *node, int index);
int oaknode_node_set_override_color(OakNodeNode node, int index);
/**
* @brief Create an override-color command (olive::NodeOverrideColorCommand).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_override_color_undoable(OakNodeNode *node, int index,
int oaknode_node_set_override_color_undoable(OakNodeNode node, int index,
OakUndoCommand *out_command);
/**
@@ -204,14 +218,14 @@ int oaknode_node_set_override_color_undoable(OakNodeNode *node, int index,
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_is_enabled(const OakNodeNode *node, int *out_value);
int oaknode_node_is_enabled(OakNodeNode node, int *out_value);
/**
* @brief Set the node's enabled state directly (live).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_enabled(OakNodeNode *node, int enabled);
int oaknode_node_set_enabled(OakNodeNode node, int enabled);
/**
* @brief Create an enabled-state command
@@ -219,7 +233,7 @@ int oaknode_node_set_enabled(OakNodeNode *node, int enabled);
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_set_enabled_undoable(OakNodeNode *node, int enabled,
int oaknode_node_set_enabled_undoable(OakNodeNode node, int enabled,
OakUndoCommand *out_command);
/* ---- Input introspection ------------------------------------------------ */
@@ -230,13 +244,13 @@ int oaknode_node_set_enabled_undoable(OakNodeNode *node, int enabled,
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_node_input_count(const OakNodeNode *node, int *out_count);
int oaknode_node_input_count(OakNodeNode node, int *out_count);
/**
* @brief The input id at `index` (Node::inputs()). Two-stage getter;
* returns OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_input_id(const OakNodeNode *node, int index, char *buf,
int oaknode_node_input_id(OakNodeNode node, int index, char *buf,
int buf_size);
/**
@@ -244,38 +258,39 @@ int oaknode_node_input_id(const OakNodeNode *node, int index, char *buf,
* pinned mapping on oaknode_value_type). OAKNODE_E_NOT_FOUND for an
* unknown input id.
*/
int oaknode_node_input_get_type(const OakNodeNode *node, const char *input_id,
int oaknode_node_input_get_type(OakNodeNode node, const char *input_id,
int *out_type);
/**
* @brief 1 if the input currently has a connected edge
* (Node::is_input_connected()). OAKNODE_E_NOT_FOUND for an unknown id.
*/
int oaknode_node_input_is_connected(const OakNodeNode *node,
const char *input_id, int *out_value);
int oaknode_node_input_is_connected(OakNodeNode node, const char *input_id,
int *out_value);
/**
* @brief 1 if the input accepts connections (Node::is_input_connectable()).
* OAKNODE_E_NOT_FOUND for an unknown id.
*/
int oaknode_node_input_is_connectable(const OakNodeNode *node,
const char *input_id, int *out_value);
int oaknode_node_input_is_connectable(OakNodeNode node, const char *input_id,
int *out_value);
/**
* @brief The human-readable name of the input (Node::get_input_name()).
* Two-stage getter; OAKNODE_E_NOT_FOUND for an unknown id.
*/
int oaknode_node_get_input_name(const OakNodeNode *node, const char *input_id,
int oaknode_node_get_input_name(OakNodeNode node, const char *input_id,
char *buf, int buf_size);
/**
* @brief The node feeding this input, or NULL when not connected
* (Node::get_connected_output(), element -1). `out_node` receives a
* borrowed handle. OAKNODE_E_NOT_FOUND for an unknown input id.
* @brief The node feeding this input (Node::get_connected_output(),
* element -1). `out_node` receives a borrowed handle (empty, ctx == NULL,
* when not connected; releasing it only releases the handle).
* OAKNODE_E_NOT_FOUND for an unknown input id.
*/
int oaknode_node_input_get_connected_node(const OakNodeNode *node,
int oaknode_node_input_get_connected_node(OakNodeNode node,
const char *input_id,
OakNodeNode **out_node);
OakNodeNode *out_node);
/* ---- Parameter access ----------------------------------------------------- */
@@ -288,7 +303,7 @@ int oaknode_node_input_get_connected_node(const OakNodeNode *node,
* fail with OAKNODE_E_FAILED; an unknown input id fails with
* OAKNODE_E_NOT_FOUND.
*/
int oaknode_node_get_input(const OakNodeNode *node, const char *input_id,
int oaknode_node_get_input(OakNodeNode node, const char *input_id,
oaknode_value *out);
/**
@@ -298,7 +313,7 @@ int oaknode_node_get_input(const OakNodeNode *node, const char *input_id,
* `v->type` must match the input's declared type; OAKNODE_VALUE_STRING is
* rejected (use oaknode_node_set_input_string()).
*/
int oaknode_node_set_input(OakNodeNode *node, const char *input_id,
int oaknode_node_set_input(OakNodeNode node, const char *input_id,
const oaknode_value *v);
/**
@@ -308,27 +323,26 @@ int oaknode_node_set_input(OakNodeNode *node, const char *input_id,
*
* Same type rules as oaknode_node_set_input().
*/
int oaknode_node_set_input_undoable(OakNodeNode *node, const char *input_id,
int oaknode_node_set_input_undoable(OakNodeNode node, const char *input_id,
const oaknode_value *v,
OakUndoCommand *out_command);
/**
* @brief Read a string-family input's standard value. Two-stage getter.
*/
int oaknode_node_get_input_string(const OakNodeNode *node,
const char *input_id, char *buf,
int buf_size);
int oaknode_node_get_input_string(OakNodeNode node, const char *input_id,
char *buf, int buf_size);
/**
* @brief Write a string-family input's standard value directly (live).
*/
int oaknode_node_set_input_string(OakNodeNode *node, const char *input_id,
int oaknode_node_set_input_string(OakNodeNode node, const char *input_id,
const char *value);
/**
* @brief Create a set-standard-value command for a string-family input.
*/
int oaknode_node_set_input_string_undoable(OakNodeNode *node,
int oaknode_node_set_input_string_undoable(OakNodeNode node,
const char *input_id,
const char *value,
OakUndoCommand *out_command);
@@ -344,7 +358,7 @@ int oaknode_node_set_input_string_undoable(OakNodeNode *node,
* OAKNODE_E_STATE when the input is already connected or the nodes belong
* to different graphs.
*/
int oaknode_node_connect(OakNodeNode *output_node, OakNodeNode *input_node,
int oaknode_node_connect(OakNodeNode output_node, OakNodeNode input_node,
const char *input_id);
/**
@@ -353,8 +367,8 @@ int oaknode_node_connect(OakNodeNode *output_node, OakNodeNode *input_node,
* different-graph check (the command may legitimately be redone after
* graph changes).
*/
int oaknode_node_connect_undoable(OakNodeNode *output_node,
OakNodeNode *input_node,
int oaknode_node_connect_undoable(OakNodeNode output_node,
OakNodeNode input_node,
const char *input_id,
OakUndoCommand *out_command);
@@ -363,44 +377,43 @@ int oaknode_node_connect_undoable(OakNodeNode *output_node,
* (live, Node::disconnect_edge(), element -1). OAKNODE_E_NOT_FOUND when
* the input is unknown or not connected.
*/
int oaknode_node_disconnect(OakNodeNode *input_node, const char *input_id);
int oaknode_node_disconnect(OakNodeNode input_node, const char *input_id);
/**
* @brief Create an edge-remove command (olive::NodeEdgeRemoveCommand,
* element -1). OAKNODE_E_NOT_FOUND when not connected.
*/
int oaknode_node_disconnect_undoable(OakNodeNode *input_node,
int oaknode_node_disconnect_undoable(OakNodeNode input_node,
const char *input_id,
OakUndoCommand *out_command);
/**
* @brief Number of outgoing edges (Node::output_connections()).
*/
int oaknode_node_output_connection_count(const OakNodeNode *node,
int *out_count);
int oaknode_node_output_connection_count(OakNodeNode node, int *out_count);
/**
* @brief The node at the input end of outgoing edge `index`
* (borrowed handle). OAKNODE_E_NOT_FOUND for an out-of-range index.
* @brief The node at the input end of outgoing edge `index` (borrowed
* handle; releasing it only releases the handle). OAKNODE_E_NOT_FOUND for
* an out-of-range index.
*/
int oaknode_node_output_connection_node_at(const OakNodeNode *node, int index,
OakNodeNode **out_node);
int oaknode_node_output_connection_node_at(OakNodeNode node, int index,
OakNodeNode *out_node);
/**
* @brief The input id at the input end of outgoing edge `index`.
* Two-stage getter; OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_output_connection_input_id_at(const OakNodeNode *node,
int index, char *buf,
int buf_size);
int oaknode_node_output_connection_input_id_at(OakNodeNode node, int index,
char *buf, int buf_size);
/**
* @brief The input element at the input end of outgoing edge `index`
* (-1 for non-array inputs). OAKNODE_E_NOT_FOUND for an out-of-range
* index.
*/
int oaknode_node_output_connection_element_at(const OakNodeNode *node,
int index, int *out_element);
int oaknode_node_output_connection_element_at(OakNodeNode node, int index,
int *out_element);
/* ---- Links --------------------------------------------------------------- */
@@ -409,73 +422,72 @@ int oaknode_node_output_connection_element_at(const OakNodeNode *node,
* receives 1 on success, 0 when the link was rejected (e.g. either node
* rejects links). `out_linked` may be NULL.
*/
int oaknode_node_link(OakNodeNode *a, OakNodeNode *b, int *out_linked);
int oaknode_node_link(OakNodeNode a, OakNodeNode b, int *out_linked);
/**
* @brief Unlink two nodes directly (live, Node::unlink()).
* `out_unlinked` receives 1 on success, 0 otherwise; may be NULL.
*/
int oaknode_node_unlink(OakNodeNode *a, OakNodeNode *b, int *out_unlinked);
int oaknode_node_unlink(OakNodeNode a, OakNodeNode b, int *out_unlinked);
/**
* @brief Create a link/unlink command (olive::NodeLinkCommand;
* `link` != 0 links, 0 unlinks).
*/
int oaknode_node_link_undoable(OakNodeNode *a, OakNodeNode *b, int link,
int oaknode_node_link_undoable(OakNodeNode a, OakNodeNode b, int link,
OakUndoCommand *out_command);
/**
* @brief 1 if the two nodes are linked (Node::are_linked()).
*/
int oaknode_node_are_linked(const OakNodeNode *a, const OakNodeNode *b,
int *out_value);
int oaknode_node_are_linked(OakNodeNode a, OakNodeNode b, int *out_value);
/**
* @brief Number of linked nodes (Node::links()).
*/
int oaknode_node_link_count(const OakNodeNode *node, int *out_count);
int oaknode_node_link_count(OakNodeNode node, int *out_count);
/**
* @brief The linked node at `index` (borrowed handle).
* OAKNODE_E_NOT_FOUND for an out-of-range index.
* @brief The linked node at `index` (borrowed handle; releasing it only
* releases the handle). OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_link_at(const OakNodeNode *node, int index,
OakNodeNode **out_node);
int oaknode_node_link_at(OakNodeNode node, int index,
OakNodeNode *out_node);
/* ---- Context positions ---------------------------------------------------- */
/**
* @brief Number of context entries (Node::get_context_positions()).
*/
int oaknode_node_context_count(const OakNodeNode *node, int *out_count);
int oaknode_node_context_count(OakNodeNode node, int *out_count);
/**
* @brief The context node at `index` (borrowed handle).
* OAKNODE_E_NOT_FOUND for an out-of-range index.
* @brief The context node at `index` (borrowed handle; releasing it only
* releases the handle). OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_context_node_at(const OakNodeNode *node, int index,
OakNodeNode **out_node);
int oaknode_node_context_node_at(OakNodeNode node, int index,
OakNodeNode *out_node);
/**
* @brief The node's position in `context` (any out pointer may be NULL).
* OAKNODE_E_NOT_FOUND when the context does not contain this node.
*/
int oaknode_node_get_context_position(const OakNodeNode *node,
OakNodeNode *context, double *out_x,
double *out_y, int *out_expanded);
int oaknode_node_get_context_position(OakNodeNode node, OakNodeNode context,
double *out_x, double *out_y,
int *out_expanded);
/**
* @brief Set the node's position in `context` directly (live,
* Node::set_node_position_in_context() + set_node_expanded_in_context()).
*/
int oaknode_node_set_context_position(OakNodeNode *node, OakNodeNode *context,
int oaknode_node_set_context_position(OakNodeNode node, OakNodeNode context,
double x, double y, int expanded);
/**
* @brief Create a set-position command (olive::NodeSetPositionCommand).
*/
int oaknode_node_set_context_position_undoable(OakNodeNode *node,
OakNodeNode *context, double x,
int oaknode_node_set_context_position_undoable(OakNodeNode node,
OakNodeNode context, double x,
double y, int expanded,
OakUndoCommand *out_command);
@@ -483,16 +495,17 @@ int oaknode_node_set_context_position_undoable(OakNodeNode *node,
* @brief Remove the node from `context` directly (live).
* OAKNODE_E_NOT_FOUND when not contained.
*/
int oaknode_node_remove_from_context(OakNodeNode *node, OakNodeNode *context);
int oaknode_node_remove_from_context(OakNodeNode node, OakNodeNode context);
/* ---- Lifetime --------------------------------------------------------------- */
/**
* @brief Create a standalone copy of the node (Node::copy()). The copy is
* NOT added to any graph; the caller owns it and must release it with
* oaknode_node_free() while it is still orphaned. Returns NULL for NULL.
* NOT added to any graph; the returned handle has reference count 1 and
* must be released with oaknode_node_free() while it is still orphaned.
* Returns an empty handle (ctx == NULL) for an empty handle or on failure.
*/
OakNodeNode *oaknode_node_create_copy(const OakNodeNode *node);
OakNodeNode oaknode_node_create_copy(OakNodeNode node);
/**
* @brief Copy a node inside its graph (Node::copy_node_in_graph()),
@@ -501,44 +514,44 @@ OakNodeNode *oaknode_node_create_copy(const OakNodeNode *node);
* `*out_command` receives an owned undo command handle (free with
* oakundo_command_free()). The copy is inserted into the graph only when
* the returned command is redone; treat it as owned (oaknode_node_free())
* until then. Returns NULL on failure.
* until then. Returns an empty handle (ctx == NULL) on failure.
*/
OakNodeNode *oaknode_node_copy_in_graph(OakNodeNode *node,
OakUndoCommand *out_command);
OakNodeNode oaknode_node_copy_in_graph(OakNodeNode node,
OakUndoCommand *out_command);
/**
* @brief Get the project this node belongs to (borrowed). *out may be
* NULL if the node is orphaned.
* @brief Get the project this node belongs to. `out` receives a borrowed
* handle (empty, ctx == NULL, if the node is orphaned; releasing it only
* releases the handle).
*/
int oaknode_node_get_project(const OakNodeNode *node,
OakNodeProject **out);
int oaknode_node_get_project(OakNodeNode node, OakNodeProject *out);
/**
* @brief Insert/remove an element in an input array (live,
* Node::input_array_insert/remove()). OAKNODE_E_NOT_FOUND for an
* unknown input id.
*/
int oaknode_node_input_array_insert(OakNodeNode *node,
const char *input_id, int index);
int oaknode_node_input_array_remove(OakNodeNode *node,
const char *input_id, int index);
int oaknode_node_input_array_insert(OakNodeNode node, const char *input_id,
int index);
int oaknode_node_input_array_remove(OakNodeNode node, const char *input_id,
int index);
/**
* @brief Element-aware variants of oaknode_node_connect()/disconnect()
* (NodeInput element != -1, e.g. Sequence's track_in_N array inputs).
*/
int oaknode_node_connect_element(OakNodeNode *output_node,
OakNodeNode *input_node,
int oaknode_node_connect_element(OakNodeNode output_node,
OakNodeNode input_node,
const char *input_id, int element);
int oaknode_node_disconnect_element(OakNodeNode *input_node,
int oaknode_node_disconnect_element(OakNodeNode input_node,
const char *input_id, int element);
/**
* @brief Create a command that adds a node to a project's graph
* (olive::NodeAddCommand). Owned; free with oakundo_command_free().
*/
OakUndoCommand oaknode_command_create_add_node(OakNodeProject *graph,
OakNodeNode *node);
OakUndoCommand oaknode_command_create_add_node(OakNodeProject graph,
OakNodeNode node);
/**
* @brief Create a command that sets a node's position in a context and
@@ -546,22 +559,20 @@ OakUndoCommand oaknode_command_create_add_node(OakNodeProject *graph,
* (olive::NodeSetPositionAndDependenciesRecursivelyCommand). Owned.
*/
OakUndoCommand oaknode_command_create_set_position_recursive(
OakNodeNode *node, OakNodeNode *context, double x, double y);
OakNodeNode node, OakNodeNode context, double x, double y);
/**
* @brief Borrowed marker list / work area of a viewer node. *out is NULL
* when the node is not a viewer (or for NULL input).
* when the node is not a viewer (or for an empty handle).
*/
int oaknode_node_get_markers(const OakNodeNode *node,
OakNodeMarkerList **out);
int oaknode_node_get_work_area(const OakNodeNode *node,
OakNodeWorkArea **out);
int oaknode_node_get_markers(OakNodeNode node, OakNodeMarkerList **out);
int oaknode_node_get_work_area(OakNodeNode node, OakNodeWorkArea **out);
/**
* @brief Borrowed video frame cache of a node (NULL when the node has
* none or for NULL input).
* none or for an empty handle).
*/
int oaknode_node_get_video_frame_cache(const OakNodeNode *node,
int oaknode_node_get_video_frame_cache(OakNodeNode node,
OakNodeFrameCache **out);
/**
@@ -569,7 +580,7 @@ int oaknode_node_get_video_frame_cache(const OakNodeNode *node,
* (Node::copy_inputs()). include_connections != 0 also copies
* input connections.
*/
int oaknode_node_copy_inputs(OakNodeNode *dst, const OakNodeNode *src,
int oaknode_node_copy_inputs(OakNodeNode dst, OakNodeNode src,
int include_connections);
/**
@@ -577,8 +588,7 @@ int oaknode_node_copy_inputs(OakNodeNode *dst, const OakNodeNode *src,
* (Node::set_value_hint_for_input() with a single texture type
* and a Track::Reference string).
*/
int oaknode_node_set_value_hint_track(OakNodeNode *node,
const char *input_id,
int oaknode_node_set_value_hint_track(OakNodeNode node, const char *input_id,
int track_type, int track_index);
/**
@@ -586,37 +596,37 @@ int oaknode_node_set_value_hint_track(OakNodeNode *node,
* set_video_params/set_audio_params, stream index 0). `params` is an
* oakcommon handle (video) or borrowed oakcore handle (audio).
*/
int oaknode_viewer_set_video_params(OakNodeNode *viewer,
int oaknode_viewer_set_video_params(OakNodeNode viewer,
const OakVideoParams *params);
int oaknode_viewer_set_audio_params(OakNodeNode *viewer,
int oaknode_viewer_set_audio_params(OakNodeNode viewer,
const OakAudioParams *params);
/**
* @brief Find a footage node upstream of this node's inputs
* (Node::find_input_nodes<Footage>(), first match). *out is a
* borrowed handle or NULL when none.
* (Node::find_input_nodes<Footage>(), first match). `out` receives
* a borrowed handle (empty, ctx == NULL, when none; releasing it
* only releases the handle).
*/
int oaknode_node_find_input_footage(const OakNodeNode *node,
OakNodeFootage **out);
int oaknode_node_find_input_footage(OakNodeNode node, OakNodeFootage *out);
/**
* @brief Create a command that removes a node from its graph together
* with its exclusive dependencies and disconnects its edges
* (NodeRemoveWithExclusiveDependenciesAndDisconnect).
*
* Owned command handle; free with oakundo_command_free(). Returns NULL
* on failure.
* Owned command handle; free with oakundo_command_free(). Returns an
* empty handle (ctx == NULL) on failure.
*/
OakUndoCommand oaknode_command_create_remove_node(OakNodeNode *node);
OakUndoCommand oaknode_command_create_remove_node(OakNodeNode node);
/**
* @brief Destroy an OWNED node immediately (C++ delete). NULL is a no-op.
* @brief Release one reference to a node handle.
*
* ONLY valid for owned handles that were never added to a graph: the
* products of oaknode_factory_create_from_id(),
* oaknode_node_create_copy() and oaknode_group_create() while still
* orphaned. Freeing a graph-owned node, or freeing twice, is a
* use-after-free.
* Convenience wrapper around handle.release(handle.ctx): the underlying
* node is destroyed only when the last reference of an OWNED handle is
* released; releasing a borrowed handle into a graph-owned object only
* destroys the handle itself. NULL handle or NULL ctx is a no-op; clears
* `node->ctx` after releasing.
*/
void oaknode_node_free(OakNodeNode *node);
+68 -44
View File
@@ -21,6 +21,8 @@
#ifndef OAK_EDITOR_NODE_PROJECT_H
#define OAK_EDITOR_NODE_PROJECT_H
#include <stdint.h>
#include "node/error.h"
#ifdef __cplusplus
@@ -33,9 +35,9 @@ extern "C" {
*
* An OakNodeProject owns its whole node graph: nodes added with
* oaknode_project_add_node() (directly, or indirectly through the folder and
* footage families) are deleted by oaknode_project_free(). Handles to nodes,
* folders and footage obtained from a project are borrowed views and must not
* be freed.
* footage families) are deleted when the project's last reference is
* released. Handles to nodes, folders and footage obtained from a project
* are borrowed views: releasing them only releases the handle itself.
*
* Conventions (shared by all oaknode C API families):
* - Return codes: 0 (OAKNODE_OK) on success, a negative OAKNODE_E_* code on
@@ -44,27 +46,37 @@ extern "C" {
* query the required size; the return value is the required buffer size in
* bytes INCLUDING the terminating NUL. The output is NUL-terminated
* whenever buf_size > 0.
* - NULL handles yield OAKNODE_E_INVALID (or a no-op for free()).
* - Empty handles (ctx == NULL) yield OAKNODE_E_INVALID (or a no-op for
* free()).
* - Disk save/load of project files is NOT part of this layer; it belongs to
* oakstorage (milestone M10).
*/
/**
* @brief Opaque project handle. Owned by the caller; release with
* oaknode_project_free().
* @brief Reference-counted handle to a project (olive::Project).
*
* Semantics are shared_ptr-like: oaknode_project_init() returns a handle
* whose underlying object has reference count 1, addref(ctx) takes another
* reference, and release(ctx) (or oaknode_project_free()) drops one; the
* project and every node it owns are destroyed when the count reaches zero.
*/
typedef struct OakNodeProject OakNodeProject;
typedef struct OakNodeProject {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeProject;
/**
* @brief Opaque node handle (defined by the node family; forward-declared
* @brief Node handle (defined by the node family; forward-declared
* here so the headers can be included in any order).
*/
typedef struct OakNodeNode OakNodeNode;
/**
* @brief Opaque folder handle (defined in node/folder.h; forward-declared
* here so the headers can be included in any order). Borrowed from the
* owning project.
* @brief Folder handle (defined in node/folder.h; forward-declared
* here so the headers can be included in any order). Handles obtained from
* a project are borrowed from it.
*/
typedef struct OakNodeFolder OakNodeFolder;
@@ -74,12 +86,16 @@ typedef struct OakNodeFolder OakNodeFolder;
* The project has no root folder until oaknode_project_initialize() is
* called (mirrors Project::initialize()).
*
* @return Project handle, or NULL on allocation failure.
* @return Project handle with reference count 1 (release with
* oaknode_project_free()); ctx is NULL on allocation failure.
*/
OakNodeProject *oaknode_project_init(void);
OakNodeProject oaknode_project_init(void);
/**
* @brief Destroy a project and every node it owns. NULL is a no-op.
* @brief Release one reference to a project handle.
*
* Destroys the project and every node it owns when the count reaches zero.
* NULL handle or NULL ctx is a no-op; clears `project->ctx` after releasing.
*/
void oaknode_project_free(OakNodeProject *project);
@@ -88,7 +104,7 @@ void oaknode_project_free(OakNodeProject *project);
*
* @return OAKNODE_OK, or OAKNODE_E_STATE if already initialized.
*/
int oaknode_project_initialize(OakNodeProject *project);
int oaknode_project_initialize(OakNodeProject project);
/**
* @brief Destructively destroy all nodes in the graph (Project::clear()).
@@ -98,14 +114,16 @@ int oaknode_project_initialize(OakNodeProject *project);
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_clear(OakNodeProject *project);
int oaknode_project_clear(OakNodeProject project);
/**
* @brief Borrowed handle of the project's root folder (Project::root()).
*
* NULL if the project has not been initialized.
* The returned handle only releases the handle itself; the project owns the
* folder. Empty handle (ctx == NULL) if the project has not been
* initialized.
*/
OakNodeFolder *oaknode_project_root(OakNodeProject *project);
OakNodeFolder oaknode_project_root(OakNodeProject project);
/**
* @brief Project display name (Project::name(): the filename's base name, or
@@ -114,20 +132,20 @@ OakNodeFolder *oaknode_project_root(OakNodeProject *project);
* @return Required buffer size in bytes including the NUL, or a negative
* OAKNODE_E_* error code.
*/
int oaknode_project_name(const OakNodeProject *project, char *buf, int buf_size);
int oaknode_project_name(OakNodeProject project, char *buf, int buf_size);
/**
* @brief Full path the project was saved as, or "" if untitled
* (Project::filename()). Two-stage string getter.
*/
int oaknode_project_filename(const OakNodeProject *project, char *buf,
int oaknode_project_filename(OakNodeProject project, char *buf,
int buf_size);
/**
* @brief Display name safe for window titles (Project::pretty_filename()).
* Two-stage string getter.
*/
int oaknode_project_pretty_filename(const OakNodeProject *project, char *buf,
int oaknode_project_pretty_filename(OakNodeProject project, char *buf,
int buf_size);
/**
@@ -135,46 +153,47 @@ int oaknode_project_pretty_filename(const OakNodeProject *project, char *buf,
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_set_filename(OakNodeProject *project, const char *filename);
int oaknode_project_set_filename(OakNodeProject project, const char *filename);
/**
* @brief 1 if the project has unsaved changes, 0 otherwise
* (Project::is_modified()). Negative OAKNODE_E_* code on NULL.
* (Project::is_modified()). Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_project_is_modified(const OakNodeProject *project);
int oaknode_project_is_modified(OakNodeProject project);
/**
* @brief Set the modified flag (Project::set_modified()).
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_set_modified(OakNodeProject *project, int modified);
int oaknode_project_set_modified(OakNodeProject project, int modified);
/**
* @brief 1 if the project is new (untitled and unmodified, Project::is_new()).
* Negative OAKNODE_E_* code on NULL.
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_project_is_new(const OakNodeProject *project);
int oaknode_project_is_new(OakNodeProject project);
/**
* @brief Effective cache directory (Project::cache_path(), honoring the cache
* location setting). Two-stage string getter.
*/
int oaknode_project_cache_path(const OakNodeProject *project, char *buf,
int oaknode_project_cache_path(OakNodeProject project, char *buf,
int buf_size);
/**
* @brief Copy all project settings (Project::copy_settings()).
*/
int oaknode_project_copy_settings(OakNodeProject *dst,
const OakNodeProject *src);
int oaknode_project_copy_settings(OakNodeProject dst,
OakNodeProject src);
/**
* @brief Cache location setting enum value
* (Project::get_cache_location_setting(): 0 = default location,
* 1 = alongside project, 2 = custom path). Negative OAKNODE_E_* code on NULL.
* 1 = alongside project, 2 = custom path). Negative OAKNODE_E_* code on an
* empty handle.
*/
int oaknode_project_get_cache_location_setting(const OakNodeProject *project);
int oaknode_project_get_cache_location_setting(OakNodeProject project);
/**
* @brief Set the cache location setting (0/1/2, see
@@ -182,14 +201,14 @@ int oaknode_project_get_cache_location_setting(const OakNodeProject *project);
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_set_cache_location_setting(OakNodeProject *project,
int oaknode_project_set_cache_location_setting(OakNodeProject project,
int setting);
/**
* @brief Custom cache directory, or "" when none is set
* (Project::get_custom_cache_path()). Two-stage string getter.
*/
int oaknode_project_get_custom_cache_path(const OakNodeProject *project,
int oaknode_project_get_custom_cache_path(OakNodeProject project,
char *buf, int buf_size);
/**
@@ -198,22 +217,25 @@ int oaknode_project_get_custom_cache_path(const OakNodeProject *project,
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_set_custom_cache_path(OakNodeProject *project,
int oaknode_project_set_custom_cache_path(OakNodeProject project,
const char *path);
/**
* @brief Project UUID string (Project::get_uuid()). Two-stage string getter.
*/
int oaknode_project_get_uuid(const OakNodeProject *project, char *buf,
int oaknode_project_get_uuid(OakNodeProject project, char *buf,
int buf_size);
/**
* @brief Add a node to the graph; the project takes ownership
* @brief Add a node to the graph; the graph assumes the node's lifetime
* (Project::add_node()).
*
* After a successful call the graph owns the node: releasing `node` only
* releases the handle itself.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_project_add_node(OakNodeProject *project, OakNodeNode *node);
int oaknode_project_add_node(OakNodeProject project, OakNodeNode node);
/**
* @brief Detach a node from the graph without deleting it
@@ -222,19 +244,21 @@ int oaknode_project_add_node(OakNodeProject *project, OakNodeNode *node);
* @return OAKNODE_OK, OAKNODE_E_NOT_FOUND if the node is not in the graph, or
* another negative OAKNODE_E_* error code.
*/
int oaknode_project_remove_node(OakNodeProject *project, OakNodeNode *node);
int oaknode_project_remove_node(OakNodeProject project, OakNodeNode node);
/**
* @brief Number of nodes belonging to the graph (Project::nodes().size()).
* Negative OAKNODE_E_* code on NULL.
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_project_node_count(const OakNodeProject *project);
int oaknode_project_node_count(OakNodeProject project);
/**
* @brief Borrowed handle of the graph node at `index`, or NULL when out of
* range.
* @brief Borrowed handle of the graph node at `index`.
*
* The returned handle only releases the handle itself. Empty handle
* (ctx == NULL) when out of range.
*/
OakNodeNode *oaknode_project_node_at(const OakNodeProject *project, int index);
OakNodeNode oaknode_project_node_at(OakNodeProject project, int index);
#ifdef __cplusplus
}
+64 -42
View File
@@ -25,6 +25,8 @@
#include <stdbool.h>
#endif
#include <stdint.h>
#include "common/videoparams.h"
#include "node/error.h"
#include "olive/core/oakcore/audioparams.h"
@@ -46,109 +48,129 @@ extern "C" {
typedef struct OakNodeNode OakNodeNode;
/**
* @brief Opaque handle to a sequence (olive::Sequence).
* @brief Reference-counted handle to a sequence (olive::Sequence).
*
* The handle IS the C++ object pointer; no wrapper is allocated.
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* oaknode_sequence_create() returns a handle with count 1, addref(ctx)
* takes another reference, release(ctx) drops one and the library
* destroys the object when the count reaches zero.
*
* Handles obtained from accessors (track lists, tracks) are borrowed:
* releasing them does not destroy the underlying object, which stays
* owned by the sequence graph.
*/
typedef struct OakNodeSequence OakNodeSequence;
typedef struct OakNodeSequence {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeSequence;
/**
* @brief Opaque handle to a track list (olive::TrackList), see node/track.h.
* @brief Reference-counted handle to a track list (olive::TrackList),
* see node/track.h.
*/
typedef struct OakNodeTrackList OakNodeTrackList;
/**
* @brief Opaque handle to a track (olive::Track), see node/track.h.
* @brief Reference-counted handle to a track (olive::Track), see
* node/track.h.
*/
typedef struct OakNodeTrack OakNodeTrack;
/**
* @brief Create an empty sequence with zero tracks.
*
* @return Sequence handle, or NULL on allocation failure.
* @return Sequence handle with reference count 1 (release with
* oaknode_sequence_free()); ctx is NULL on allocation failure.
*/
OakNodeSequence *oaknode_sequence_create(void);
OakNodeSequence oaknode_sequence_create(void);
/**
* @brief Destroy a sequence and its track lists. No-op on NULL.
* @brief Release one reference to a sequence handle.
*
* Destroys the sequence (and its owned track lists) when the reference
* count reaches zero. NULL handle or NULL ctx is a no-op; clears
* `sequence->ctx` after releasing.
*
* Tracks and blocks connected to the sequence are owned by the graph and
* are not deleted here; the caller must have torn them down first.
*/
void oaknode_sequence_free(OakNodeSequence *sequence);
/**
* @brief Apply the default video/audio parameters
* (ViewerOutput::set_default_parameters()).
*/
int oaknode_sequence_set_default_parameters(OakNodeSequence sequence);
/**
* @brief Borrowed cast from a sequence handle to its node handle.
* Empty handle for an empty handle.
*/
OakNodeNode oaknode_sequence_as_node(OakNodeSequence sequence);
/**
* @brief Borrowed handle to the per-type track list.
*
* @param type One of OAKNODE_TRACK_TYPE_VIDEO / _AUDIO / _SUBTITLE.
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND (bad type).
*/
/**
* @brief Apply the default video/audio parameters
* (ViewerOutput::set_default_parameters()).
*/
int oaknode_sequence_set_default_parameters(OakNodeSequence *sequence);
/**
* @brief Borrowed cast from a sequence handle to its node handle.
* NULL for NULL.
*/
OakNodeNode *oaknode_sequence_as_node(OakNodeSequence *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);
/**
* @brief Number of connected tracks of the given type.
*/
int oaknode_sequence_get_track_count(OakNodeSequence *sequence, int type,
int oaknode_sequence_get_track_count(OakNodeSequence sequence, int type,
int *count);
/**
* @brief Borrowed handle to the track of `type` at `index`.
*/
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);
/**
* @brief Flat track cache across all types (olive::Sequence::get_tracks()).
*/
int oaknode_sequence_get_all_track_count(OakNodeSequence *sequence, int *count);
int oaknode_sequence_get_all_track_at(OakNodeSequence *sequence, int index,
OakNodeTrack **out);
int oaknode_sequence_get_all_track_count(OakNodeSequence sequence, int *count);
int oaknode_sequence_get_all_track_at(OakNodeSequence sequence, int index,
OakNodeTrack *out);
/**
* @brief Playhead position in sequence time.
*/
int oaknode_sequence_get_playhead(OakNodeSequence *sequence, int *numerator,
int oaknode_sequence_get_playhead(OakNodeSequence sequence, int *numerator,
int *denominator);
int oaknode_sequence_set_playhead(OakNodeSequence *sequence, int numerator,
int oaknode_sequence_set_playhead(OakNodeSequence sequence, int numerator,
int denominator);
/**
* @brief Cached overall/video/audio lengths (olive::ViewerOutput).
*/
int oaknode_sequence_get_length(OakNodeSequence *sequence, int *numerator,
int oaknode_sequence_get_length(OakNodeSequence sequence, int *numerator,
int *denominator);
int oaknode_sequence_get_video_length(OakNodeSequence *sequence,
int oaknode_sequence_get_video_length(OakNodeSequence sequence,
int *numerator, int *denominator);
int oaknode_sequence_get_audio_length(OakNodeSequence *sequence,
int oaknode_sequence_get_audio_length(OakNodeSequence sequence,
int *numerator, int *denominator);
/**
* @brief Recompute the cached lengths from the track lists
* (olive::ViewerOutput::verify_length()).
*/
int oaknode_sequence_verify_length(OakNodeSequence *sequence);
int oaknode_sequence_verify_length(OakNodeSequence sequence);
/* --------------------------------------------------- Video/audio params */
/**
* @brief Number of video/audio parameter slots.
*/
int oaknode_sequence_get_video_stream_count(OakNodeSequence *sequence,
int oaknode_sequence_get_video_stream_count(OakNodeSequence sequence,
int *count);
int oaknode_sequence_get_audio_stream_count(OakNodeSequence *sequence,
int oaknode_sequence_get_audio_stream_count(OakNodeSequence sequence,
int *count);
/**
@@ -159,29 +181,29 @@ int oaknode_sequence_get_audio_stream_count(OakNodeSequence *sequence,
* @return OAKNODE_OK, OAKNODE_E_INVALID, OAKNODE_E_NOT_FOUND or
* OAKNODE_E_NOMEM.
*/
int oaknode_sequence_get_video_params(OakNodeSequence *sequence, int index,
int oaknode_sequence_get_video_params(OakNodeSequence sequence, int index,
OakVideoParams *out);
/**
* @brief Replace the video parameters at `index` with a copy of `params`.
*
* @return OAKNODE_E_INVALID if sequence is NULL, params.ctx is NULL, or
* index is negative.
* @return OAKNODE_E_INVALID if the sequence handle is empty, params.ctx is
* NULL, or index is negative.
*/
int oaknode_sequence_set_video_params(OakNodeSequence *sequence, int index,
int oaknode_sequence_set_video_params(OakNodeSequence sequence, int index,
OakVideoParams params);
/**
* @brief Audio parameters at `index` as a NEW handle owned by the caller
* (release with oakcore_audioparams_free()).
*/
int oaknode_sequence_get_audio_params(OakNodeSequence *sequence, int index,
int oaknode_sequence_get_audio_params(OakNodeSequence sequence, int index,
OakAudioParams **out);
/**
* @brief Replace the audio parameters at `index` with a copy of `params`.
*/
int oaknode_sequence_set_audio_params(OakNodeSequence *sequence, int index,
int oaknode_sequence_set_audio_params(OakNodeSequence sequence, int index,
const OakAudioParams *params);
#ifdef __cplusplus
+64 -39
View File
@@ -21,7 +21,10 @@
#ifndef OAK_EDITOR_NODE_SERIALIZER_H
#define OAK_EDITOR_NODE_SERIALIZER_H
#include <stdint.h>
#include "node/error.h"
#include "node/node.h"
#include "node/project.h"
#ifdef __cplusplus
@@ -73,17 +76,33 @@ extern "C" {
#define OAKNODE_SERIALIZER_NO_DATA 7
/**
* @brief Opaque save descriptor (wraps ProjectSerializer::SaveData).
* Owned by the caller; release with oaknode_serializer_savedata_free().
* @brief Reference-counted save descriptor (wraps
* olive::ProjectSerializer::SaveData).
*
* oaknode_serializer_savedata_create() returns a handle whose object has
* reference count 1; release it with oaknode_serializer_savedata_free().
*/
typedef struct OakNodeSerializerSaveData OakNodeSerializerSaveData;
typedef struct OakNodeSerializerSaveData {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeSerializerSaveData;
/**
* @brief Opaque load result (wraps ProjectSerializer::LoadData).
* Owned by the caller; release with oaknode_serializer_loaddata_free().
* @brief Reference-counted load result (wraps
* olive::ProjectSerializer::LoadData).
*
* The handle returned through oaknode_serializer_load_from_xml() has
* reference count 1; release it with oaknode_serializer_loaddata_free().
* Node handles obtained from it are borrowed from the target project.
*/
typedef struct OakNodeSerializerLoadData OakNodeSerializerLoadData;
typedef struct OakNodeSerializerLoadData {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeSerializerLoadData;
/**
* @brief Register the versioned serializers and initialize the node factory.
@@ -104,16 +123,19 @@ void oaknode_serializer_shutdown(void);
*
* @param load_type One of OAKNODE_SERIALIZER_LOAD_*; use
* OAKNODE_SERIALIZER_LOAD_ONLY_NODES for clipboard-style node copies.
* @param project Context project (borrowed), may be NULL for load types
* that do not require it.
* @param project Context project (borrowed), may be an empty handle for
* load types that do not require it.
*
* @return Save-data handle, or NULL on failure.
* @return Save-data handle with reference count 1 (release with
* oaknode_serializer_savedata_free()); ctx is NULL on failure.
*/
OakNodeSerializerSaveData *oaknode_serializer_savedata_create(
int load_type, OakNodeProject *project);
OakNodeSerializerSaveData oaknode_serializer_savedata_create(
int load_type, OakNodeProject project);
/**
* @brief Destroy a save descriptor. NULL is a no-op.
* @brief Release the caller's reference to the save descriptor and null
* out the handle. NULL and empty handles are a no-op; the object is
* destroyed when its reference count reaches zero.
*/
void oaknode_serializer_savedata_free(OakNodeSerializerSaveData *save_data);
@@ -125,7 +147,7 @@ void oaknode_serializer_savedata_free(OakNodeSerializerSaveData *save_data);
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_serializer_savedata_set_nodes(
OakNodeSerializerSaveData *save_data, OakNodeNode *const *nodes, int count);
OakNodeSerializerSaveData save_data, const OakNodeNode *nodes, int count);
/**
* @brief Attach a free-form (key, value) property to a node in the
@@ -135,7 +157,7 @@ int oaknode_serializer_savedata_set_nodes(
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
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);
/**
@@ -146,21 +168,22 @@ int oaknode_serializer_savedata_set_property(
* OAKNODE_E_* error code (OAKNODE_E_STATE if the serializers have
* not been initialized).
*/
int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData *save_data,
int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData save_data,
char *buf, int buf_size);
/**
* @brief Parse an in-memory XML document into `project` ("paste").
*
* @param project Target project (borrowed), may be NULL for load types that
* do not attach nodes to a project.
* @param project Target project (borrowed), may be an empty handle for
* load types that do not attach nodes to a project.
* @param xml Complete XML document text. Must not be NULL.
* @param load_type One of OAKNODE_SERIALIZER_LOAD_*.
* @param out_result Receives one of the OAKNODE_SERIALIZER_* result codes.
* Must not be NULL.
* @param out_load_data Receives the load result on OAKNODE_SERIALIZER_OK
* (caller-owned, may be NULL if the caller does not need it;
* receives NULL on failure).
* (reference count 1, release with oaknode_serializer_loaddata_free();
* may be NULL if the caller does not need it; receives an empty
* handle on failure).
* @param details_buf Optional human-readable error detail buffer
* (two-stage convention is NOT used; truncation is silent). May be
* NULL.
@@ -169,13 +192,14 @@ int oaknode_serializer_save_to_xml(OakNodeSerializerSaveData *save_data,
* @return OAKNODE_OK if the call itself succeeded (inspect *out_result for
* the serializer outcome), or a negative OAKNODE_E_* error code.
*/
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);
/**
* @brief Destroy a load result. NULL is a no-op.
* @brief Release the caller's reference to the load result and null out
* the handle. NULL and empty handles are a no-op.
*
* Does not delete the loaded nodes: they are newly created objects owned by
* the CALLER until adopted into a project with oaknode_project_add_node()
@@ -185,17 +209,17 @@ void oaknode_serializer_loaddata_free(OakNodeSerializerLoadData *load_data);
/**
* @brief Number of nodes created by the load. Negative OAKNODE_E_* code on
* NULL.
* an empty handle.
*/
int oaknode_serializer_loaddata_node_count(
const OakNodeSerializerLoadData *load_data);
OakNodeSerializerLoadData load_data);
/**
* @brief Borrowed handle of the loaded node at `index`, or NULL when out of
* range.
* @brief Borrowed handle of the loaded node at `index`, or an empty handle
* when out of range.
*/
OakNodeNode *oaknode_serializer_loaddata_node_at(
const OakNodeSerializerLoadData *load_data, int index);
OakNodeNode oaknode_serializer_loaddata_node_at(
OakNodeSerializerLoadData load_data, int index);
/**
* @brief Look up a serialized property attached to a loaded node.
@@ -206,15 +230,15 @@ OakNodeNode *oaknode_serializer_loaddata_node_at(
* negative OAKNODE_E_* error code.
*/
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);
/**
* @brief Number of promised (deferred) connections in the load result.
* Negative OAKNODE_E_* code on NULL.
* Negative OAKNODE_E_* code on an empty handle.
*/
int oaknode_serializer_loaddata_connection_count(
const OakNodeSerializerLoadData *load_data);
OakNodeSerializerLoadData load_data);
/**
* @brief Read the promised connection at `index`.
@@ -223,8 +247,8 @@ int oaknode_serializer_loaddata_connection_count(
* `input_id_buf` follows the two-stage string convention inside a
* fixed call: pass NULL/0 to skip copying the id.
*
* @param out_output_node Receives the output (source) node.
* @param out_input_node Receives the input (destination) node.
* @param out_output_node Receives the output (source) node (borrowed).
* @param out_input_node Receives the input (destination) node (borrowed).
* @param input_id_buf Receives the input id string, may be NULL.
* @param input_id_buf_size Size of input_id_buf.
* @param out_element Receives the input element index.
@@ -233,8 +257,8 @@ int oaknode_serializer_loaddata_connection_count(
* negative OAKNODE_E_* error code.
*/
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);
#ifdef __cplusplus
@@ -268,9 +292,10 @@ enum OakNodeSerializerResultCode {
* string (e.g. the fallback filename on overwrite errors).
* @return OAKNODE_OK when the result code is
* OAKNODE_SERIALIZER_RESULT_SUCCESS, OAKNODE_E_FAILED otherwise
* (details in out_code/details), OAKNODE_E_INVALID for NULL args.
* (details in out_code/details), OAKNODE_E_INVALID for empty
* handles/NULL args.
*/
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);
@@ -280,6 +305,6 @@ int oaknode_serializer_save_to_file(OakNodeProject *project,
*
* Same return/out-param convention as oaknode_serializer_save_to_file().
*/
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);
+101 -70
View File
@@ -25,6 +25,8 @@
#include <stdbool.h>
#endif
#include <stdint.h>
#include "node/error.h"
#ifdef __cplusplus
@@ -32,27 +34,49 @@ extern "C" {
#endif
/**
* @brief Opaque handle to a track (olive::Track).
* @brief Reference-counted handle to a track (olive::Track).
*
* The handle IS the C++ object pointer; no wrapper is allocated.
* The object never leaves the library that created it; every external
* reference is one of these handles. Semantics are shared_ptr-like:
* oaknode_track_create() returns a handle with count 1, addref(ctx)
* takes another reference, release(ctx) drops one and the library
* destroys the object when the count reaches zero.
*
* Adding a track to a track list (oaknode_tracklist_add_track())
* transfers ownership to the graph; handles obtained from accessors
* (sequence/track-list lookups) are borrowed and never destroy the
* underlying object.
*/
typedef struct OakNodeTrack OakNodeTrack;
typedef struct OakNodeTrack {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeTrack;
/**
* @brief Opaque handle to a per-type track container (olive::TrackList).
* @brief Reference-counted handle to a per-type track container
* (olive::TrackList).
*
* Borrowed from oaknode_sequence_get_track_list(); invalidated when the
* owning sequence is destroyed.
* Always borrowed from oaknode_sequence_get_track_list(); releasing the
* handle never destroys the list, which stays owned by its sequence.
*/
typedef struct OakNodeTrackList OakNodeTrackList;
typedef struct OakNodeTrackList {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeTrackList;
/**
* @brief Opaque handle to a block (olive::Block), see node/block.h.
* @brief Reference-counted handle to a block (olive::Block), see
* node/block.h.
*/
typedef struct OakNodeBlock OakNodeBlock;
/**
* @brief Opaque handle to a sequence (olive::Sequence), see node/sequence.h.
* @brief Reference-counted handle to a sequence (olive::Sequence), see
* node/sequence.h.
*/
typedef struct OakNodeSequence OakNodeSequence;
@@ -72,9 +96,9 @@ typedef struct OakNodeNode OakNodeNode;
/**
* @brief Borrowed cast from a track handle to its node handle.
* NULL for NULL.
* Empty handle for an empty handle.
*/
OakNodeNode *oaknode_track_as_node(OakNodeTrack *track);
OakNodeNode oaknode_track_as_node(OakNodeTrack track);
/* ---------------------------------------------------------------- Track */
@@ -84,12 +108,16 @@ OakNodeNode *oaknode_track_as_node(OakNodeTrack *track);
* The caller owns the track until it is added to a track list; a track
* that was never added must be released with oaknode_track_free().
*
* @return Track handle, or NULL on invalid type / allocation failure.
* @return Track handle with reference count 1; ctx is NULL on invalid
* type / allocation failure.
*/
OakNodeTrack *oaknode_track_create(int type);
OakNodeTrack oaknode_track_create(int type);
/**
* @brief Destroy a track. No-op on NULL.
* @brief Release one reference to a track handle.
*
* Destroys the track when the reference count reaches zero. NULL handle
* or NULL ctx is a no-op; clears `track->ctx` after releasing.
*
* The track must have been removed from its track list first.
*/
@@ -100,20 +128,20 @@ void oaknode_track_free(OakNodeTrack *track);
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_track_get_type(OakNodeTrack *track, int *type);
int oaknode_track_set_type(OakNodeTrack *track, int type);
int oaknode_track_get_type(OakNodeTrack track, int *type);
int oaknode_track_set_type(OakNodeTrack track, int type);
/**
* @brief Track height in internal units (olive::Track::get/set_track_height).
*/
int oaknode_track_get_height(OakNodeTrack *track, double *height);
int oaknode_track_set_height(OakNodeTrack *track, double height);
int oaknode_track_get_height(OakNodeTrack track, double *height);
int oaknode_track_set_height(OakNodeTrack track, double height);
/**
* @brief Track height in pixels (converted through the default font height).
*/
int oaknode_track_get_height_in_pixels(OakNodeTrack *track, int *height);
int oaknode_track_set_height_in_pixels(OakNodeTrack *track, int height);
int oaknode_track_get_height_in_pixels(OakNodeTrack track, int *height);
int oaknode_track_set_height_in_pixels(OakNodeTrack track, int height);
/**
* @brief Default / minimum track heights in pixels (static).
@@ -124,47 +152,47 @@ int oaknode_track_get_minimum_height_in_pixels(void);
/**
* @brief Index of the track inside its track list.
*/
int oaknode_track_get_index(OakNodeTrack *track, int *index);
int oaknode_track_set_index(OakNodeTrack *track, int index);
int oaknode_track_get_index(OakNodeTrack track, int *index);
int oaknode_track_set_index(OakNodeTrack track, int index);
/**
* @brief Mute / lock flags.
*/
int oaknode_track_get_muted(OakNodeTrack *track, int *muted);
int oaknode_track_set_muted(OakNodeTrack *track, int muted);
int oaknode_track_get_locked(OakNodeTrack *track, int *locked);
int oaknode_track_set_locked(OakNodeTrack *track, int locked);
int oaknode_track_get_muted(OakNodeTrack track, int *muted);
int oaknode_track_set_muted(OakNodeTrack track, int muted);
int oaknode_track_get_locked(OakNodeTrack track, int *locked);
int oaknode_track_set_locked(OakNodeTrack track, int locked);
/**
* @brief Track reference as a (type, index) pair (olive::Track::Reference).
*/
int oaknode_track_get_reference(OakNodeTrack *track, int *type, int *index);
int oaknode_track_get_reference(OakNodeTrack track, int *type, int *index);
/**
* @brief Total length of the track (end of the last block).
*/
int oaknode_track_get_length(OakNodeTrack *track, int *numerator,
int oaknode_track_get_length(OakNodeTrack track, int *numerator,
int *denominator);
/**
* @brief Owning sequence as a borrowed handle (NULL when trackless).
* @brief Owning sequence as a borrowed handle (empty when trackless).
*/
int oaknode_track_get_sequence(OakNodeTrack *track, OakNodeSequence **out);
int oaknode_track_get_sequence(OakNodeTrack track, OakNodeSequence *out);
/* ------------------------------------------------------- Track blocks */
/**
* @brief Number of blocks on the track.
*/
int oaknode_track_get_block_count(OakNodeTrack *track, int *count);
int oaknode_track_get_block_count(OakNodeTrack track, int *count);
/**
* @brief Borrowed handle to the block at `index`.
*
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND.
*/
int oaknode_track_get_block_at(OakNodeTrack *track, int index,
OakNodeBlock **out);
int oaknode_track_get_block_at(OakNodeTrack track, int index,
OakNodeBlock *out);
/**
* @brief Append/prepend/insert primitives (olive::Track::*_block).
@@ -174,54 +202,54 @@ int oaknode_track_get_block_at(OakNodeTrack *track, int index,
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_track_append_block(OakNodeTrack *track, OakNodeBlock *block);
int oaknode_track_prepend_block(OakNodeTrack *track, OakNodeBlock *block);
int oaknode_track_insert_block_at_index(OakNodeTrack *track,
OakNodeBlock *block, int index);
int oaknode_track_insert_block_after(OakNodeTrack *track, OakNodeBlock *block,
OakNodeBlock *before);
int oaknode_track_insert_block_before(OakNodeTrack *track, OakNodeBlock *block,
OakNodeBlock *after);
int oaknode_track_append_block(OakNodeTrack track, OakNodeBlock block);
int oaknode_track_prepend_block(OakNodeTrack track, OakNodeBlock block);
int oaknode_track_insert_block_at_index(OakNodeTrack track,
OakNodeBlock block, int index);
int oaknode_track_insert_block_after(OakNodeTrack track, OakNodeBlock block,
OakNodeBlock before);
int oaknode_track_insert_block_before(OakNodeTrack track, OakNodeBlock block,
OakNodeBlock after);
/**
* @brief Remove `block` and shift all subsequent blocks earlier
* (olive::Track::ripple_remove_block). The block is NOT deleted; ownership
* returns to the caller.
*/
int oaknode_track_ripple_remove_block(OakNodeTrack *track, OakNodeBlock *block);
int oaknode_track_ripple_remove_block(OakNodeTrack track, OakNodeBlock block);
/**
* @brief Replace `old_block` with `new_block`; both must have equal lengths.
*/
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);
/**
* @brief Index of `block` in the track's block array, or OAKNODE_E_NOT_FOUND.
*/
int oaknode_track_get_block_index(OakNodeTrack *track, OakNodeBlock *block,
int oaknode_track_get_block_index(OakNodeTrack track, OakNodeBlock block,
int *index);
/**
* @brief Block strictly containing `time` (in < time < out), or
* OAKNODE_E_NOT_FOUND.
*/
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);
/**
* @brief Block visible at `time` (in <= time < out), or OAKNODE_E_NOT_FOUND.
*/
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);
/**
* @brief Whether the [in, out) range holds no block or only a gap
* (olive::Track::is_range_free). `is_free` receives 1/0.
*/
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);
/* ------------------------------------------------------------ TrackList */
@@ -231,66 +259,66 @@ int oaknode_track_is_range_free(OakNodeTrack *track, int in_num, int in_den,
*/
/**
* @brief Nearest block lookups (Track::nearest_block_before_or_at /
* nearest_block_after_or_at). *out is a borrowed handle or NULL.
* nearest_block_after_or_at). *out is a borrowed handle (empty when none).
*/
int oaknode_track_get_nearest_block_before_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);
int oaknode_track_get_nearest_block_before_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);
/**
* @brief Borrowed sequence owning this track list.
*/
int oaknode_tracklist_get_sequence(OakNodeTrackList *list,
OakNodeSequence **out);
int oaknode_tracklist_get_sequence(OakNodeTrackList list,
OakNodeSequence *out);
/**
* @brief The list's track input id on the parent sequence
* (e.g. "track_in_0"). Two-stage string getter.
*/
int oaknode_tracklist_get_track_input_id(OakNodeTrackList *list,
int oaknode_tracklist_get_track_input_id(OakNodeTrackList list,
char *buf, int buf_size);
/**
* @brief Live input-array append/remove on the parent sequence for this
* list's track input (TrackList::array_append/array_remove_last()).
*/
int oaknode_tracklist_array_append(OakNodeTrackList *list);
int oaknode_tracklist_array_remove_last(OakNodeTrackList *list);
int oaknode_tracklist_array_append(OakNodeTrackList list);
int oaknode_tracklist_array_remove_last(OakNodeTrackList list);
/**
* @brief Map a cached track index to the input-array element index
* (TrackList::get_array_index_from_cache_index()).
*/
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);
int oaknode_tracklist_get_type(OakNodeTrackList *list, int *type);
int oaknode_tracklist_get_type(OakNodeTrackList list, int *type);
/**
* @brief Number of connected tracks.
*/
int oaknode_tracklist_get_track_count(OakNodeTrackList *list, int *count);
int oaknode_tracklist_get_track_count(OakNodeTrackList list, int *count);
/**
* @brief Borrowed handle to the track at `index`.
*
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND.
*/
int oaknode_tracklist_get_track_at(OakNodeTrackList *list, int index,
OakNodeTrack **out);
int oaknode_tracklist_get_track_at(OakNodeTrackList list, int index,
OakNodeTrack *out);
/**
* @brief Combined length of the longest track in the list.
*/
int oaknode_tracklist_get_total_length(OakNodeTrackList *list, int *numerator,
int oaknode_tracklist_get_total_length(OakNodeTrackList list, int *numerator,
int *denominator);
/**
* @brief Size of the underlying input array (>= track count; may contain
* disconnected slots).
*/
int oaknode_tracklist_get_array_size(OakNodeTrackList *list, int *size);
int oaknode_tracklist_get_array_size(OakNodeTrackList list, int *size);
/**
* @brief Add `track` to the list (non-undoable primitive).
@@ -301,9 +329,12 @@ int oaknode_tracklist_get_array_size(OakNodeTrackList *list, int *size);
* the track is connected to it. The sequence's flat track cache and
* lengths are refreshed before returning.
*
* The list takes ownership of the track on success; the caller's handle
* becomes a non-owning reference.
*
* @return OAKNODE_OK or OAKNODE_E_INVALID.
*/
int oaknode_tracklist_add_track(OakNodeTrackList *list, OakNodeTrack *track);
int oaknode_tracklist_add_track(OakNodeTrackList list, OakNodeTrack track);
/**
* @brief Remove `track` from the list (non-undoable primitive).
@@ -314,8 +345,8 @@ int oaknode_tracklist_add_track(OakNodeTrackList *list, OakNodeTrack *track);
*
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND.
*/
int oaknode_tracklist_remove_track(OakNodeTrackList *list,
OakNodeTrack *track);
int oaknode_tracklist_remove_track(OakNodeTrackList list,
OakNodeTrack track);
#ifdef __cplusplus
}
+43 -19
View File
@@ -41,26 +41,46 @@ extern "C" {
*/
/**
* @brief Opaque traverser handle (olive::NodeTraverser).
* @brief Reference-counted handle to a traverser (olive::NodeTraverser).
*
* Semantics are shared_ptr-like: oaknode_traverser_init() returns a
* handle with count 1, addref(ctx) takes another reference, release(ctx)
* drops one and the library destroys the object when the count reaches
* zero.
*/
typedef struct OakNodeTraverser OakNodeTraverser;
typedef struct OakNodeTraverser {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeTraverser;
/**
* @brief Opaque value-database handle (an owned copy of an
* olive::NodeValueDatabase). Release with
* oaknode_traverser_database_free().
* @brief Reference-counted handle to an owned copy of an
* olive::NodeValueDatabase. Same reference-counting rules as
* OakNodeTraverser; release with oaknode_traverser_database_free().
*/
typedef struct OakNodeValueDatabase OakNodeValueDatabase;
typedef struct OakNodeValueDatabase {
void *ctx; /**< Opaque pointer to the reference-counted object. */
void (*addref)(void *ctx); /**< Atomically increments the count. */
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
uint32_t abi_version; /**< OAKNODE_ABI_VERSION. */
} OakNodeValueDatabase;
/**
* @brief Create a traverser.
*
* @return Traverser handle, or NULL on allocation failure.
* @return Traverser handle with count 1; ctx is NULL on allocation
* failure.
*/
OakNodeTraverser *oaknode_traverser_init(void);
OakNodeTraverser oaknode_traverser_init(void);
/**
* @brief Destroy a traverser. NULL is a no-op.
* @brief Release one reference to a traverser handle.
*
* Convenience wrapper around handle.release(handle.ctx): destroys the
* traverser when the count reaches zero. NULL handle or NULL ctx is a
* no-op; clears `traverser->ctx` after releasing.
*/
void oaknode_traverser_free(OakNodeTraverser *traverser);
@@ -69,39 +89,43 @@ void oaknode_traverser_free(OakNodeTraverser *traverser);
* [`in_num`/`in_den`, `out_num`/`out_den`) seconds
* (NodeTraverser::generate_database()).
*
* `out_db` receives an owned database handle.
* `out_db` receives an owned database handle with count 1.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
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);
/**
* @brief Destroy a database handle. NULL is a no-op.
* @brief Release one reference to a database handle.
*
* Convenience wrapper around handle.release(handle.ctx): destroys the
* database when the count reaches zero. NULL handle or NULL ctx is a
* no-op; clears `db->ctx` after releasing.
*/
void oaknode_traverser_database_free(OakNodeValueDatabase *db);
/**
* @brief Number of rows (input tables) in the database.
*/
int oaknode_traverser_database_row_count(const OakNodeValueDatabase *db,
int oaknode_traverser_database_row_count(OakNodeValueDatabase db,
int *out_count);
/**
* @brief The input id (key) of the row at `index`. Two-stage getter;
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
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);
/**
* @brief Number of values in the row named `key`.
* OAKNODE_E_NOT_FOUND for an unknown key.
*/
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);
@@ -110,7 +134,7 @@ int oaknode_traverser_database_row_value_count(const OakNodeValueDatabase *db,
* Values without a POD representation fail with OAKNODE_E_FAILED;
* OAKNODE_E_NOT_FOUND for an unknown key or out-of-range index.
*/
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);
@@ -119,7 +143,7 @@ int oaknode_traverser_database_value_at(const OakNodeValueDatabase *db,
* (NodeValue::value_to_string()). Two-stage getter;
* OAKNODE_E_NOT_FOUND for an unknown key or out-of-range index.
*/
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);
+10 -8
View File
@@ -45,17 +45,19 @@ OakRenderProjectCopier *oakrender_project_copier_create(void);
/** @brief Free the copier AND its copied project. NULL-safe. */
void oakrender_project_copier_free(OakRenderProjectCopier *copier);
/** @brief (Re)build the copy from `project`. */
/** @brief (Re)build the copy from `project` (borrowed handle). */
int oakrender_project_copier_set_project(OakRenderProjectCopier *copier,
OakNodeProject *project);
OakNodeProject project);
/** @brief The copied counterpart of an original node (borrowed), NULL
* when the node is not in the copied project. */
OakNodeNode *oakrender_project_copier_get_copy(
OakRenderProjectCopier *copier, OakNodeNode *original);
/** @brief The copied counterpart of an original node (borrowed handle;
* freeing it only releases the handle box), empty handle when the
* node is not in the copied project. */
OakNodeNode oakrender_project_copier_get_copy(
OakRenderProjectCopier *copier, OakNodeNode original);
/** @brief The copied project (borrowed). */
OakNodeProject *oakrender_project_copier_get_copied_project(
/** @brief The copied project (borrowed handle; freeing it only releases
* the handle box). */
OakNodeProject oakrender_project_copier_get_copied_project(
OakRenderProjectCopier *copier);
#ifdef __cplusplus
+8 -12
View File
@@ -24,6 +24,7 @@
#include <stdint.h>
// See cache.h for why these are same-dir relative includes.
#include "node/node.h" /* OakNodeNode (by-value handle) */
#include "cache.h" /* OakCodecFrame */
#include "color.h" /* OakColorProcessor */
#include "error.h"
@@ -50,12 +51,6 @@ extern "C" {
* are no event subscription interfaces.
*/
/**
* @brief Borrowed node handle (olive::Node) from the oaknode ABI,
* re-declared here so this header is self-contained.
*/
typedef struct OakNodeNode OakNodeNode;
/**
* @brief Create the RenderManager singleton (spawns render/audio
* threads, loads the configured backend).
@@ -91,11 +86,12 @@ typedef void (*oakrender_frame_ready_fn)(OakCodecFrame *frame, int64_t ts,
* cancelled with oakrender_cancel_request().
*
* @return A positive request id, or a negative OAKRENDER_E_* code
* (OAKRENDER_E_INVALID for NULL viewer/callback,
* OAKRENDER_E_STATE when the manager is not initialized,
* OAKRENDER_E_FAILED when no ticket could be created).
* (OAKRENDER_E_INVALID for an empty viewer handle or NULL
* callback, OAKRENDER_E_STATE when the manager is not
* initialized, OAKRENDER_E_FAILED when no ticket could be
* created).
*/
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);
/**
@@ -109,11 +105,11 @@ int oakrender_cancel_request(int64_t request_id);
/**
* @brief Set the multicam node on the manager's auto-cacher
* (PreviewAutoCacher::set_multicam_node()). `multicam_or_NULL` is a
* borrowed oaknode handle to a MultiCamNode (NULL to clear).
* borrowed oaknode handle to a MultiCamNode (empty handle to clear).
*
* @return OAKRENDER_OK or OAKRENDER_E_STATE.
*/
int oakrender_set_cacher_multicam(OakNodeNode *multicam_or_NULL);
int oakrender_set_cacher_multicam(OakNodeNode multicam_or_NULL);
/**
* @brief Set the display color processor on the manager's auto-cacher
+3 -3
View File
@@ -64,12 +64,12 @@ enum OakRenderTicketType {
* (RenderManager::RenderVideoParams).
*/
typedef struct oakrender_video_ticket_params {
OakNodeNode *output_node; /**< Connected texture output node. */
OakNodeNode output_node; /**< Connected texture output node (borrowed). */
OakVideoParams video_params; /**< By value (oakcommon handle). */
OakAudioParams *audio_params; /**< Borrowed oakcore handle, may be NULL. */
int64_t time_num; /**< Frame timestamp as rational. */
int64_t time_den;
OakNodeColorManager *color_manager; /**< Borrowed, may be NULL. */
OakNodeColorManager color_manager; /**< Borrowed, empty ctx = NULL. */
int mode; /**< olive::RenderMode::Mode as int. */
int force_width; /**< 0/0 = off. */
int force_height;
@@ -100,7 +100,7 @@ OakRenderTicket *oakrender_ticket_render_frame(
* @param params Audio params (borrowed oakcore handle).
*/
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);
+28 -19
View File
@@ -41,18 +41,21 @@ extern "C" {
/** @brief olive::ProjectLoadTask. */
OakTaskTask *oaktask_create_project_load(const char *filename);
/** @brief Take the loaded project (ownership transfer). */
OakNodeProject *oaktask_load_take_project(OakTaskTask *t);
/** @brief Take the loaded project (ownership transfer). Empty handle
* (ctx == NULL) when the task has not succeeded or the project was
* already taken. */
OakNodeProject oaktask_load_take_project(OakTaskTask *t);
/** @brief olive::ProjectSaveTask. `filename_or_NULL` overrides the
* project's own filename. */
OakTaskTask *oaktask_create_project_save(OakNodeProject *project,
* project's own filename. `project` is borrowed by the task. */
OakTaskTask *oaktask_create_project_save(OakNodeProject project,
const char *filename_or_NULL,
int use_compression);
/** @brief olive::ProjectImportTask. */
OakTaskTask *oaktask_create_project_import(OakNodeFolder *folder,
OakNodeProject *project,
/** @brief olive::ProjectImportTask. `folder`/`project` are borrowed by
* the task. */
OakTaskTask *oaktask_create_project_import(OakNodeFolder folder,
OakNodeProject project,
const char *const *urls,
int url_count);
@@ -61,8 +64,10 @@ OakUndoCommand oaktask_import_take_command(OakTaskTask *t);
int oaktask_import_footage_count(OakTaskTask *t);
/** @brief Borrowed footage handle at index, NULL when out of range. */
OakNodeFootage *oaktask_import_footage_at(OakTaskTask *t, int index);
/** @brief Footage handle at index (addref'd; release with
* handle.release(handle.ctx) - box only, the project owns the
* footage). Empty handle when out of range. */
OakNodeFootage oaktask_import_footage_at(OakTaskTask *t, int index);
int oaktask_import_invalid_count(OakTaskTask *t);
@@ -73,11 +78,13 @@ int oaktask_import_invalid_at(OakTaskTask *t, int index, char *buf,
/** @brief olive::LoadOTIOTask. */
OakTaskTask *oaktask_create_project_load_otio(const char *filename);
/** @brief Take the loaded project (ownership transfer). */
OakNodeProject *oaktask_load_otio_take_project(OakTaskTask *t);
/** @brief Take the loaded project (ownership transfer). Empty handle
* (ctx == NULL) when the task has not succeeded or the project was
* already taken. */
OakNodeProject oaktask_load_otio_take_project(OakTaskTask *t);
/** @brief olive::SaveOTIOTask. */
OakTaskTask *oaktask_create_project_save_otio(OakNodeProject *project,
/** @brief olive::SaveOTIOTask. `project` is borrowed by the task. */
OakTaskTask *oaktask_create_project_save_otio(OakNodeProject project,
const char *filename);
/**
@@ -89,13 +96,15 @@ typedef int (*oaktask_otio_import_confirm_fn)(
void oaktask_load_otio_set_confirm_cb(oaktask_otio_import_confirm_fn fn,
void *userdata);
/** @brief olive::PreCacheTask. */
OakTaskTask *oaktask_create_precache(OakNodeFootage *footage, int index,
OakNodeSequence *sequence);
/** @brief olive::PreCacheTask. `footage`/`sequence` are borrowed by the
* task. */
OakTaskTask *oaktask_create_precache(OakNodeFootage footage, int index,
OakNodeSequence sequence);
/** @brief olive::ExportTask (params POD from codec/encoder.h). */
OakTaskTask *oaktask_create_export(OakNodeNode *viewer,
OakNodeColorManager *color_manager,
/** @brief olive::ExportTask (params POD from codec/encoder.h).
* `viewer`/`color_manager` are borrowed by the task. */
OakTaskTask *oaktask_create_export(OakNodeNode viewer,
OakNodeColorManager color_manager,
const oakcodec_encoding_params *params);
/**
+19 -15
View File
@@ -38,63 +38,67 @@ extern "C" {
* consumers create commands through these factories, receiving base
* OakUndoCommand handles (owned; free with oakundo_command_free()).
* Redo a command directly or push it on an undo stack.
*
* OakNode* handles are passed by value per the oaknode handle
* convention; an empty handle (ctx == NULL) yields an empty
* OakUndoCommand result.
*/
/** @brief olive::TimelineAddTrackCommand. */
OakUndoCommand oaktimeline_add_track_command(OakNodeTrackList *list);
OakUndoCommand oaktimeline_add_track_command(OakNodeTrackList list);
/** @brief olive::TimelineRemoveTrackCommand. */
OakUndoCommand oaktimeline_remove_track_command(OakNodeTrack *track);
OakUndoCommand oaktimeline_remove_track_command(OakNodeTrack track);
/** @brief olive::TrackPlaceBlockCommand. */
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);
/** @brief olive::TrackReplaceBlockWithGapCommand. */
OakUndoCommand oaktimeline_replace_block_with_gap_command(
OakNodeTrack *track, OakNodeBlock *block);
OakNodeTrack track, OakNodeBlock block);
/**
* @brief olive::BlockTrimCommand. `mode` is an OakTimelineMovementMode
* value (k_trim_in / k_trim_out).
*/
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);
/** @brief olive::BlockSplitCommand on a set of blocks at one point. */
OakUndoCommand oaktimeline_split_command(OakNodeBlock *const *blocks,
OakUndoCommand oaktimeline_split_command(const OakNodeBlock *blocks,
int count, int64_t point_num,
int64_t point_den);
/** @brief olive::BlockSplitPreservingLinksCommand. */
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);
/** @brief olive::TimelineRippleDeleteGapsAtRegionsCommand. */
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);
/** @brief olive::TrackSlideCommand. */
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);
/** @brief olive::TrackRippleRemoveAreaCommand. */
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);
/** @brief olive::TrackListInsertGaps. */
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,
+3 -3
View File
@@ -39,10 +39,10 @@ extern "C" {
typedef struct OakTimelineMarkerList OakTimelineMarkerList;
/**
* @brief Borrowed marker list of a viewer node (sequence). NULL for NULL
* or when the node is not a viewer.
* @brief Borrowed marker list of a viewer node (sequence). NULL for an
* empty handle or when the node is not a viewer.
*/
OakTimelineMarkerList *oaktimeline_marker_list_of(OakNodeNode *owner);
OakTimelineMarkerList *oaktimeline_marker_list_of(OakNodeNode owner);
/**
* @brief Number of markers. Out-param convention; OAKTIMELINE_E_INVALID
+3 -3
View File
@@ -37,10 +37,10 @@ extern "C" {
typedef struct OakTimelineWorkArea OakTimelineWorkArea;
/**
* @brief Borrowed work area of a viewer node (sequence). NULL for NULL
* or when the node is not a viewer.
* @brief Borrowed work area of a viewer node (sequence). NULL for an
* empty handle or when the node is not a viewer.
*/
OakTimelineWorkArea *oaktimeline_workarea_of(OakNodeNode *owner);
OakTimelineWorkArea *oaktimeline_workarea_of(OakNodeNode owner);
/**
* @brief Read the work area state. Out params may individually be NULL.
+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