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:
+87
-63
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
@@ -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(¶ms);
|
||||
} 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
@@ -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
@@ -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>);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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, ×tamp), 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, ×tamp),
|
||||
EXPECT_EQ(oaknode_footage_is_valid(OakNodeFootage{}), OAKNODE_E_INVALID);
|
||||
EXPECT_EQ(oaknode_footage_timestamp(OakNodeFootage{}, ×tamp),
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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(©);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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>);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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 ¶ms)
|
||||
: 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];
|
||||
|
||||
@@ -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 ¶ms);
|
||||
|
||||
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_;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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, ¶ms), nullptr);
|
||||
EXPECT_EQ(oaktask_create_export(nullptr, nullptr, nullptr), nullptr);
|
||||
EXPECT_EQ(oaktask_create_export(OakNodeNode{}, OakNodeColorManager{},
|
||||
¶ms),
|
||||
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
@@ -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{};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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 ®ion : 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));
|
||||
|
||||
@@ -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 ®ions)
|
||||
: 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;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> ×)
|
||||
: 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_;
|
||||
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user