feat(app): multicam panel with live angle grid, switching, timeline enable
- New MulticamPanel: rows/cols angle grid with the current angle highlighted, click-to-switch, 1-9 switch-and-split and cmd-1-9 switch-only shortcuts (focused-panel routed), deferred switch queue during playback. - src/oakui/multicam.rs: clip->connected-sequence resolution, multicam state detection (selection then playhead fallbacks), per-angle frame requests rendered through the process backend into an LRU cache. - Timeline clip context menu Multi-Cam checkable item wired to oaktimeline::multicam enable/disable with undo. - Engine trait extended (real + mock); mock drives the real command path with synthesized angle frames.
This commit is contained in:
@@ -0,0 +1,313 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_BLOCK_H
|
||||
#define OAK_EDITOR_NODE_BLOCK_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "node/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Reference-counted handle to a timeline block (olive::Block).
|
||||
*
|
||||
* Covers the whole Block family: ClipBlock, GapBlock and the concrete
|
||||
* 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 {
|
||||
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 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 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.
|
||||
*/
|
||||
typedef struct OakNodeNode OakNodeNode;
|
||||
|
||||
/**
|
||||
* @brief Concrete transition kinds for oaknode_block_transition_create().
|
||||
*/
|
||||
enum OakNodeTransitionKind {
|
||||
OAKNODE_TRANSITION_CROSS_DISSOLVE = 0, /**< CrossDissolveTransition. */
|
||||
OAKNODE_TRANSITION_DIP_TO_COLOR = 1 /**< DipToColorTransition. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Input ids of a TransitionBlock's block connections
|
||||
* (TransitionBlock::k_out_block_input / k_in_block_input). Pinned by
|
||||
* test; pass to oaknode_node_connect()/oaknode_node_disconnect().
|
||||
*/
|
||||
#define OAKNODE_TRANSITION_OUT_BLOCK_INPUT "out_block_in"
|
||||
#define OAKNODE_TRANSITION_IN_BLOCK_INPUT "in_block_in"
|
||||
|
||||
/**
|
||||
* @brief Create a ClipBlock.
|
||||
*
|
||||
* The caller owns the block until it is placed on a track that belongs to
|
||||
* a project; a block that was never placed must be released with
|
||||
* oaknode_block_free().
|
||||
*
|
||||
* @return Block handle with reference count 1; ctx is NULL on allocation
|
||||
* failure.
|
||||
*/
|
||||
OakNodeBlock oaknode_block_clip_create(void);
|
||||
|
||||
/**
|
||||
* @brief Create a GapBlock. Ownership as oaknode_block_clip_create().
|
||||
*
|
||||
* @return Block handle with reference count 1; ctx is NULL on allocation
|
||||
* failure.
|
||||
*/
|
||||
OakNodeBlock oaknode_block_gap_create(void);
|
||||
|
||||
/**
|
||||
* @brief Create a concrete TransitionBlock.
|
||||
*
|
||||
* @param kind One of the OakNodeTransitionKind values.
|
||||
* @return Block handle with reference count 1; ctx is NULL on invalid
|
||||
* kind / allocation failure.
|
||||
*/
|
||||
OakNodeBlock oaknode_block_transition_create(int kind);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
void oaknode_block_free(OakNodeBlock *block);
|
||||
|
||||
enum OakNodeBlockKind {
|
||||
OAKNODE_BLOCK_OTHER = 0,
|
||||
OAKNODE_BLOCK_CLIP = 1,
|
||||
OAKNODE_BLOCK_GAP = 2,
|
||||
OAKNODE_BLOCK_TRANSITION = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Concrete kind of a block (dynamic_cast query).
|
||||
*/
|
||||
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; releasing the result never destroys the block.
|
||||
* Empty handle for an empty handle.
|
||||
*/
|
||||
OakNodeNode oaknode_block_as_node(OakNodeBlock block);
|
||||
|
||||
/**
|
||||
* @brief Borrowed cast from a node handle to a block handle.
|
||||
*
|
||||
* Returns an empty handle if the node is not a Block (or is empty).
|
||||
*/
|
||||
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 *denominator);
|
||||
|
||||
/**
|
||||
* @brief Set the block length, keeping the media out/in point anchored
|
||||
* (olive::Block::set_length_and_media_out / _media_in).
|
||||
*
|
||||
* @return OAKNODE_OK or OAKNODE_E_INVALID.
|
||||
*/
|
||||
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 denominator);
|
||||
|
||||
/**
|
||||
* @brief Enabled flag (olive::Block::is_enabled/set_enabled).
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Link two blocks (olive::Node::link/unlink/are_linked).
|
||||
*
|
||||
* Linked blocks move together in timeline edits.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/* ---------------------------------------------------------------- Clip */
|
||||
|
||||
/**
|
||||
* @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 *denominator);
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Reverse playback flag.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/* ----------------------------------------------------------- Transition */
|
||||
|
||||
/**
|
||||
* @brief Transition offsets (olive::TransitionBlock). Non-transition blocks
|
||||
* return OAKNODE_E_INVALID.
|
||||
*/
|
||||
int oaknode_transition_get_in_offset(OakNodeBlock transition, int *numerator,
|
||||
int *denominator);
|
||||
int oaknode_transition_get_out_offset(OakNodeBlock transition, int *numerator,
|
||||
int *denominator);
|
||||
int oaknode_transition_get_offset_center(OakNodeBlock transition,
|
||||
int *numerator, int *denominator);
|
||||
int oaknode_transition_set_offset_center(OakNodeBlock transition,
|
||||
int numerator, int denominator);
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_BLOCK_H
|
||||
@@ -0,0 +1,221 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_COLORMANAGER_H
|
||||
#define OAK_EDITOR_NODE_COLORMANAGER_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#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 Reference-counted handle to a color manager
|
||||
* (olive::ColorManager).
|
||||
*
|
||||
* 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 {
|
||||
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).
|
||||
*
|
||||
* The manager is created without a config; call
|
||||
* oaknode_colormanager_initialize() (or set a config filename and
|
||||
* oaknode_colormanager_update_config_from_filename()) before using the
|
||||
* config-dependent queries.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Borrowed handle wrapping a native manager pointer held by a
|
||||
* node (olive::OCIOBaseNode::manager()).
|
||||
*
|
||||
* The manager stays owned by its project: release() on this handle
|
||||
* only frees the box. Empty handle (ctx == NULL) for a NULL native
|
||||
* pointer.
|
||||
*/
|
||||
OakNodeColorManager oaknode_colormanager_wrap_borrowed(void *native_manager);
|
||||
|
||||
/**
|
||||
* @brief Load the built-in default OCIO config and set the default input
|
||||
* colorspace (olive::ColorManager::init()).
|
||||
*
|
||||
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_FAILED (the OCIO
|
||||
* config could not be created).
|
||||
*/
|
||||
int oaknode_colormanager_initialize(OakNodeColorManager manager);
|
||||
|
||||
/**
|
||||
* @brief (Re)build the process-wide default OCIO config
|
||||
* (olive::ColorManager::set_up_default_config()).
|
||||
*
|
||||
* @return OAKNODE_OK or OAKNODE_E_FAILED.
|
||||
*/
|
||||
int oaknode_colormanager_set_up_default_config(void);
|
||||
|
||||
/**
|
||||
* @brief Config filename stored on the project. Two-stage string getter:
|
||||
* 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,
|
||||
char *buf, int buf_size);
|
||||
int oaknode_colormanager_set_config_filename(OakNodeColorManager manager,
|
||||
const char *filename);
|
||||
|
||||
/**
|
||||
* @brief Reload the OCIO config from the stored filename. Missing/invalid
|
||||
* files are tolerated (the previous config is kept), matching
|
||||
* olive::ColorManager::update_config_from_filename().
|
||||
*/
|
||||
int oaknode_colormanager_update_config_from_filename(
|
||||
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);
|
||||
int oaknode_colormanager_set_default_input_color_space(
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Return `colorspace` when the active config lists it, otherwise the
|
||||
* default input colorspace. Two-stage string getter. Requires a config
|
||||
* (OAKNODE_E_STATE when none is loaded).
|
||||
*/
|
||||
int oaknode_colormanager_get_compliant_color_space(
|
||||
OakNodeColorManager manager, const char *colorspace, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Map FFmpeg color primaries/transfer codes to a colorspace of the
|
||||
* active config. Two-stage string getter; an empty result (required size
|
||||
* 1) means "unknown tags, use the default". Requires a config
|
||||
* (OAKNODE_E_STATE when none is loaded).
|
||||
*/
|
||||
int oaknode_colormanager_get_colorspace_for_ffmpeg_tags(
|
||||
OakNodeColorManager manager, int primaries, int trc, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Config listings. Count + per-index two-stage string getters.
|
||||
* 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 *count);
|
||||
int oaknode_colormanager_get_display_at(OakNodeColorManager manager,
|
||||
int index, char *buf, int buf_size);
|
||||
int oaknode_colormanager_get_default_display(OakNodeColorManager manager,
|
||||
char *buf, int buf_size);
|
||||
int oaknode_colormanager_get_view_count(OakNodeColorManager manager,
|
||||
const char *display, int *count);
|
||||
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,
|
||||
const char *display, char *buf,
|
||||
int buf_size);
|
||||
int oaknode_colormanager_get_look_count(OakNodeColorManager manager,
|
||||
int *count);
|
||||
int oaknode_colormanager_get_look_at(OakNodeColorManager manager, int index,
|
||||
char *buf, int buf_size);
|
||||
int oaknode_colormanager_get_colorspace_count(OakNodeColorManager manager,
|
||||
int *count);
|
||||
int oaknode_colormanager_get_colorspace_at(OakNodeColorManager manager,
|
||||
int index, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @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,
|
||||
double rgb[3]);
|
||||
|
||||
/**
|
||||
* @brief Return a copy of `transform` whose display/view/look (or output
|
||||
* colorspace) is clamped to what the active config offers
|
||||
* (olive::ColorManager::get_compliant_color_space(ColorTransform, bool)).
|
||||
*
|
||||
* `out` receives a NEW by-value handle owned by the caller (reference
|
||||
* count 1, release with oakcommon_colortransform_free()). Requires a
|
||||
* loaded config (OAKNODE_E_STATE otherwise).
|
||||
*/
|
||||
int oaknode_colormanager_get_compliant_color_transform(
|
||||
OakNodeColorManager manager, OakColorTransform transform,
|
||||
int force_display, OakColorTransform *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
namespace olive { class ColorManager; }
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Borrowed access to the underlying C++ manager (C++ only, for
|
||||
* adapter layers). Valid while the handle is held. NULL-safe.
|
||||
*/
|
||||
olive::ColorManager *oaknode_colormanager_get_native(
|
||||
OakNodeColorManager manager);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_COLORMANAGER_H
|
||||
@@ -0,0 +1,137 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_DRAGGER_H
|
||||
#define OAK_EDITOR_NODE_DRAGGER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "node/error.h"
|
||||
#include "node/node.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file dragger.h
|
||||
* @brief C ABI for olive::NodeInputDragger (src/node/src/inputdragger.h):
|
||||
* live drag of an input's value with a single commit command.
|
||||
*
|
||||
* A dragger wraps the engine's NodeInputDragger state machine
|
||||
* (start -> drag* -> end). start() records the drag anchor and, when the
|
||||
* input is keyframing, creates one keyframe at the drag time (on every
|
||||
* track when requested); drag() live-sets the dragged component (clamped
|
||||
* by the input's min/max properties when present); end() returns ONE
|
||||
* undoable command that commits the whole drag -- undo removes the
|
||||
* created keyframe(s) (restoring the pre-drag keyframe count), redo
|
||||
* re-creates them with the final value.
|
||||
*
|
||||
* A dragger must be ended before it is freed; freeing a started dragger
|
||||
* leaks the created keyframe(s) (the same ownership rule as the C++
|
||||
* class).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Reference-counted handle to an input dragger
|
||||
* (olive::NodeInputDragger).
|
||||
*
|
||||
* The object never leaves the library that created it; every external
|
||||
* reference is one of these handles. Semantics are shared_ptr-like:
|
||||
* oaknode_dragger_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 OakNodeDragger {
|
||||
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. */
|
||||
} OakNodeDragger;
|
||||
|
||||
/**
|
||||
* @brief Create an input dragger for live-drag of an input's value.
|
||||
*
|
||||
* `input_id` must name an existing input of `node`; `element` addresses
|
||||
* an array input's element (-1 for non-array inputs). `track` is the
|
||||
* create-time default; the track passed to oaknode_dragger_start()
|
||||
* establishes the actual drag track.
|
||||
*
|
||||
* @return Dragger handle with count 1; ctx is NULL on invalid arguments
|
||||
* or allocation failure.
|
||||
*/
|
||||
OakNodeDragger oaknode_dragger_create(OakNodeNode node, const char *input_id,
|
||||
int element, int track);
|
||||
|
||||
/**
|
||||
* @brief Start the drag at the given rational time (creates a keyframe
|
||||
* when the input is keyframing).
|
||||
*
|
||||
* `insert_on_all_tracks` != 0 also creates sibling keyframes on every
|
||||
* other track of the input. OAKNODE_E_STATE when the dragger was already
|
||||
* started.
|
||||
*/
|
||||
int oaknode_dragger_start(OakNodeDragger dragger, int64_t time_num,
|
||||
int64_t time_den, int track,
|
||||
int insert_on_all_tracks);
|
||||
|
||||
/**
|
||||
* @brief Drag to a new per-track component value (live; no undo).
|
||||
*
|
||||
* `value` carries the dragged component of the input's declared type:
|
||||
* scalar types in f[0]/num; for split-track types (VEC2/3/4/COLOR) the
|
||||
* POD type must match the input's declared type and the dragged
|
||||
* component sits in f[0] (the facade's dragger convention). The value is
|
||||
* clamped to the input's min/max properties when present.
|
||||
* OAKNODE_E_STATE when the dragger was not started.
|
||||
*/
|
||||
int oaknode_dragger_drag(OakNodeDragger dragger, const oaknode_value *value);
|
||||
|
||||
/**
|
||||
* @brief End the drag, returning ONE undoable command for the whole drag.
|
||||
*
|
||||
* `*out_command` receives an owned command handle (execute it with
|
||||
* oakundo_command_redo_now(), push it onto an OakUndoStack, or release
|
||||
* it with oakundo_command_free()). OAKNODE_E_STATE when the dragger was
|
||||
* not started.
|
||||
*/
|
||||
int oaknode_dragger_end(OakNodeDragger dragger, OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @brief 1 if the dragger has been started and not yet ended.
|
||||
*/
|
||||
int oaknode_dragger_is_started(OakNodeDragger dragger, int *out_started);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a dragger handle.
|
||||
*
|
||||
* Convenience wrapper around handle.release(handle.ctx): destroys the
|
||||
* dragger when the count reaches zero. NULL handle or NULL ctx is a
|
||||
* no-op; clears `dragger->ctx` after releasing. The dragger must have
|
||||
* been ended (see the file comment).
|
||||
*/
|
||||
void oaknode_dragger_free(OakNodeDragger *dragger);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_DRAGGER_H
|
||||
@@ -0,0 +1,49 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_ERROR_H
|
||||
#define OAK_EDITOR_NODE_ERROR_H
|
||||
|
||||
/**
|
||||
* @brief Status and error codes shared by all oaknode C API families.
|
||||
*
|
||||
* Return-code convention (mirrors engine/include/oakengine/init.h):
|
||||
* 0 (OAKNODE_OK) on success, a negative OAKNODE_E_* error code on
|
||||
* 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 (-30001) /**< NULL handle or invalid argument. */
|
||||
#define OAKNODE_E_STATE (-30002) /**< Call not valid in the current state. */
|
||||
#define OAKNODE_E_FAILED (-30003) /**< The underlying operation failed. */
|
||||
#define OAKNODE_E_NOT_FOUND (-30004) /**< Index out of range / entry not found. */
|
||||
#define OAKNODE_E_NOMEM (-30005) /**< Allocation failed. */
|
||||
|
||||
#endif //OAK_EDITOR_NODE_ERROR_H
|
||||
@@ -0,0 +1,100 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_FACTORY_H
|
||||
#define OAK_EDITOR_NODE_FACTORY_H
|
||||
|
||||
#include "node/error.h"
|
||||
#include "node/node.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file factory.h
|
||||
* @brief C ABI for olive::NodeFactory (src/node/src/factory.h): the
|
||||
* internal node-type library.
|
||||
*
|
||||
* The library must be populated with oaknode_factory_initialize() before
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Populate the internal node library (NodeFactory::initialize()).
|
||||
* Idempotent: calling twice is a no-op.
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_factory_initialize(void);
|
||||
|
||||
/**
|
||||
* @brief Release the internal node library (NodeFactory::destroy()).
|
||||
* Safe when not initialized.
|
||||
*/
|
||||
void oaknode_factory_destroy(void);
|
||||
|
||||
/**
|
||||
* @brief Number of registered node types (the library size).
|
||||
* OAKNODE_E_STATE when not initialized.
|
||||
*/
|
||||
int oaknode_factory_id_count(int *out_count);
|
||||
|
||||
/**
|
||||
* @brief The type id of the registered node at `index`. Two-stage
|
||||
* getter; OAKNODE_E_NOT_FOUND for an out-of-range index,
|
||||
* OAKNODE_E_STATE when not initialized.
|
||||
*/
|
||||
int oaknode_factory_id_at(int index, char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief The display name of the node type `type_id`
|
||||
* (NodeFactory::get_name_from_id()). Two-stage getter; an unknown id
|
||||
* yields an empty string (required size 1).
|
||||
*/
|
||||
int oaknode_factory_name_from_id(const char *type_id, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Create a node of `type_id` WITHOUT adding it to any graph
|
||||
* (NodeFactory::create_from_id()). The caller owns the returned node
|
||||
* (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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_FACTORY_H
|
||||
@@ -0,0 +1,170 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_FOLDER_H
|
||||
#define OAK_EDITOR_NODE_FOLDER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "node/error.h"
|
||||
#include "undo/undocommand.h"
|
||||
#include "node/project.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file folder.h
|
||||
* @brief C ABI for olive::Folder (oaknode)
|
||||
*
|
||||
* A folder is a project node that organizes item children (footage,
|
||||
* sequences, subfolders). Folder handles are borrowed from the owning
|
||||
* project; they become invalid when the project is freed or cleared.
|
||||
*
|
||||
* Child add/remove/move operations execute the underlying undo commands
|
||||
* live (redo_now); wiring them onto an undo stack is the oakundo /
|
||||
* 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 returned handle is borrowed: the project owns the folder,
|
||||
* so releasing the handle only releases the handle itself.
|
||||
*
|
||||
* @return Folder handle; ctx is NULL on failure.
|
||||
*/
|
||||
OakNodeFolder oaknode_folder_create(OakNodeProject project);
|
||||
|
||||
/**
|
||||
* @brief Number of direct item children (Folder::item_child_count()).
|
||||
* Negative OAKNODE_E_* code on an empty handle.
|
||||
*/
|
||||
int oaknode_folder_child_count(OakNodeFolder folder);
|
||||
|
||||
/**
|
||||
* @brief Borrowed node handle of the item child at `index`
|
||||
* (Folder::item_child()).
|
||||
*
|
||||
* The returned handle only releases the handle itself. Empty handle
|
||||
* (ctx == NULL) when out of range.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Borrowed cast from a folder handle to its node handle.
|
||||
*
|
||||
* The returned handle only releases the handle itself. Empty handle for an
|
||||
* empty handle.
|
||||
*/
|
||||
OakNodeNode oaknode_folder_as_node(OakNodeFolder folder);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Remove `child` from `folder` without deleting it (live,
|
||||
* non-undoable; executes Folder::RemoveElementCommand::redo()).
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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`; 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(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 empty
|
||||
* handles.
|
||||
*/
|
||||
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 empty handles.
|
||||
*/
|
||||
int oaknode_folder_index_of_child(OakNodeFolder folder,
|
||||
OakNodeNode child);
|
||||
|
||||
/**
|
||||
* @brief Borrowed handle of the folder a node currently belongs to
|
||||
* (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(OakNodeNode node);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_FOLDER_H
|
||||
@@ -0,0 +1,256 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_FOOTAGE_H
|
||||
#define OAK_EDITOR_NODE_FOOTAGE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/videoparams.h"
|
||||
#include "node/error.h"
|
||||
// NOTE: quoted-relative to bypass the "render/cancelatom.h" transition
|
||||
// bridge (oakrender's C++ olive::CancelAtom) that shadows the C ABI
|
||||
// header on oaknode's include path.
|
||||
#include "../../include/render/cancelatom.h"
|
||||
#include "node/project.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file footage.h
|
||||
* @brief C ABI for olive::Footage (oaknode)
|
||||
*
|
||||
* A footage node references an external media file and caches its stream
|
||||
* metadata. Footage handles are borrowed from the owning project; they
|
||||
* become invalid when the project is freed or cleared.
|
||||
*
|
||||
* NOTE: setting a filename whose file exists on disk triggers a probe,
|
||||
* which requires the codec/render modules (outside oaknode). Tests and
|
||||
* pure-graph consumers should use nonexistent paths; probing is the
|
||||
* facade layer's job.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
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; ctx is NULL on failure.
|
||||
*/
|
||||
OakNodeFootage oaknode_footage_create(OakNodeProject project,
|
||||
const char *filename);
|
||||
|
||||
/**
|
||||
* @brief Borrowed cast from a footage handle to its node handle.
|
||||
*
|
||||
* The returned handle only releases the handle itself. Empty handle for an
|
||||
* empty handle.
|
||||
*/
|
||||
OakNodeNode oaknode_footage_as_node(OakNodeFootage footage);
|
||||
|
||||
/**
|
||||
* @brief Current media path (Footage::filename()). Two-stage string getter.
|
||||
*
|
||||
* @return Required buffer size in bytes including the NUL, or a negative
|
||||
* OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_footage_filename(OakNodeFootage footage, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Set the media path (Footage::set_filename()). Does not re-probe
|
||||
* unless the file exists (see the file comment above).
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
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 an
|
||||
* empty handle.
|
||||
*/
|
||||
int oaknode_footage_is_valid(OakNodeFootage footage);
|
||||
|
||||
/**
|
||||
* @brief Last-modified timestamp of the media file in milliseconds since the
|
||||
* epoch (Footage::timestamp()).
|
||||
*
|
||||
* @param out_timestamp Receives the timestamp. Must not be NULL.
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_footage_timestamp(OakNodeFootage footage,
|
||||
int64_t *out_timestamp);
|
||||
|
||||
/**
|
||||
* @brief Set the last-modified timestamp (Footage::set_timestamp()).
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
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(OakNodeFootage footage, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Total number of streams (Footage::get_total_stream_count()).
|
||||
* Negative OAKNODE_E_* code on an empty handle.
|
||||
*/
|
||||
int oaknode_footage_total_stream_count(OakNodeFootage footage);
|
||||
|
||||
/**
|
||||
* @brief Number of video streams (ViewerOutput::get_video_stream_count()).
|
||||
* Negative OAKNODE_E_* code on an empty handle.
|
||||
*/
|
||||
int oaknode_footage_video_stream_count(OakNodeFootage footage);
|
||||
|
||||
/**
|
||||
* @brief Number of audio streams (ViewerOutput::get_audio_stream_count()).
|
||||
* Negative OAKNODE_E_* code on an empty handle.
|
||||
*/
|
||||
int oaknode_footage_audio_stream_count(OakNodeFootage footage);
|
||||
|
||||
/**
|
||||
* @brief Number of subtitle streams (ViewerOutput::get_subtitle_stream_count()).
|
||||
* Negative OAKNODE_E_* code on an empty handle.
|
||||
*/
|
||||
int oaknode_footage_subtitle_stream_count(OakNodeFootage footage);
|
||||
|
||||
/**
|
||||
* @brief Footage duration as a rational number of seconds
|
||||
* (ViewerOutput::get_length()).
|
||||
*
|
||||
* @param out_numerator Receives the numerator. Must not be NULL.
|
||||
* @param out_denominator Receives the denominator. Must not be NULL.
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
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 an empty handle.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Proxy file path, or "" when none (Footage::proxy_path()).
|
||||
* Two-stage string getter.
|
||||
*/
|
||||
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 an empty handle.
|
||||
*/
|
||||
int oaknode_footage_proxy_state(OakNodeFootage footage);
|
||||
|
||||
/**
|
||||
* @brief Set all proxy fields at once (Footage::set_proxy()).
|
||||
*
|
||||
* @param path Proxy file path, may be NULL/empty.
|
||||
* @param state ProxyManager::ProxyState enum value.
|
||||
* @param video_stream_index Proxy's video stream index (-1 when none).
|
||||
* @param preset_version Proxy preset version.
|
||||
* @param enabled Non-zero to enable proxy playback.
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_footage_set_proxy(OakNodeFootage footage, const char *path,
|
||||
int state, int video_stream_index,
|
||||
int preset_version, int enabled);
|
||||
|
||||
/**
|
||||
* @brief Clear all proxy fields (Footage::clear_proxy()).
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_footage_clear_proxy(OakNodeFootage footage);
|
||||
|
||||
/**
|
||||
* @brief Video stream parameters as an oakcommon video-params handle
|
||||
* (ViewerOutput::get_video_params()). `out` receives a handle with
|
||||
* 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,
|
||||
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,
|
||||
const OakVideoParams *params);
|
||||
|
||||
/**
|
||||
* @brief Video length as a rational pair (ViewerOutput::get_video_length()).
|
||||
*/
|
||||
int oaknode_footage_get_video_length(OakNodeFootage footage,
|
||||
int64_t *out_num, int64_t *out_den);
|
||||
|
||||
/**
|
||||
* @brief Set the footage's cancellation atom used during probing
|
||||
* (Footage::set_cancel_pointer()). `atom` may be an empty OakCancelAtom
|
||||
* (ctx == NULL) to clear.
|
||||
*/
|
||||
int oaknode_footage_set_cancel_atom(OakNodeFootage footage,
|
||||
OakCancelAtom atom);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_FOOTAGE_H
|
||||
@@ -0,0 +1,186 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_GROUP_H
|
||||
#define OAK_EDITOR_NODE_GROUP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "node/error.h"
|
||||
#include "node/node.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file group.h
|
||||
* @brief C ABI for olive::NodeGroup (src/node/src/group/group.h):
|
||||
* input passthrough management and input resolution.
|
||||
*
|
||||
* An OakNodeGroup wraps an olive::NodeGroup (a Node subclass); group
|
||||
* handles share the reference-counted lifetime rules of OakNodeNode.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
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_group_free() while still orphaned).
|
||||
*
|
||||
* @return Group handle with count 1; ctx is NULL on allocation failure.
|
||||
*/
|
||||
OakNodeGroup oaknode_group_create(void);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Add an input passthrough for (`node`, `input_id`, `element`)
|
||||
* (live, NodeGroup::add_input_passthrough()). The generated passthrough
|
||||
* id is returned through the two-stage string convention.
|
||||
*
|
||||
* @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,
|
||||
const char *input_id, int element,
|
||||
char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Create an add-passthrough command
|
||||
* (olive::NodeGroupAddInputPassthrough). The generated id is NOT
|
||||
* retrievable through this call (the command computes it on redo).
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_group_add_input_passthrough_undoable(OakNodeGroup group,
|
||||
OakNodeNode node,
|
||||
const char *input_id,
|
||||
int element,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @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,
|
||||
const char *input_id, int element);
|
||||
|
||||
/**
|
||||
* @brief Number of registered input passthroughs.
|
||||
*/
|
||||
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(OakNodeGroup group, int index,
|
||||
char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief The inner input behind passthrough `index`: node (borrowed
|
||||
* 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(OakNodeGroup group, int index,
|
||||
OakNodeNode *out_node, char *buf,
|
||||
int buf_size, int *out_element);
|
||||
|
||||
/**
|
||||
* @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(OakNodeGroup group,
|
||||
OakNodeNode *out_node);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Create a set-output-passthrough command
|
||||
* (olive::NodeGroupSetOutputPassthrough).
|
||||
*/
|
||||
int oaknode_group_set_output_passthrough_undoable(
|
||||
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 (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,
|
||||
char *buf, int buf_size, int *out_element);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_GROUP_H
|
||||
@@ -0,0 +1,295 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_KEYFRAME_H
|
||||
#define OAK_EDITOR_NODE_KEYFRAME_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "node/error.h"
|
||||
#include "node/node.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file keyframe.h
|
||||
* @brief C ABI for olive::NodeKeyframe (src/node/src/keyframe.h).
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Interpolation type of a keyframe (olive::NodeKeyframe::Type).
|
||||
*/
|
||||
typedef enum oaknode_keyframe_type {
|
||||
OAKNODE_KEYFRAME_INVALID = -1,
|
||||
OAKNODE_KEYFRAME_LINEAR = 0,
|
||||
OAKNODE_KEYFRAME_HOLD = 1,
|
||||
OAKNODE_KEYFRAME_BEZIER = 2
|
||||
} oaknode_keyframe_type;
|
||||
|
||||
/**
|
||||
* @brief Bezier handle selector (olive::NodeKeyframe::BezierType).
|
||||
*/
|
||||
typedef enum oaknode_keyframe_bezier {
|
||||
OAKNODE_KEYFRAME_IN_HANDLE = 0,
|
||||
OAKNODE_KEYFRAME_OUT_HANDLE = 1
|
||||
} oaknode_keyframe_bezier;
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
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
|
||||
* oaknode_keyframe_free()).
|
||||
*
|
||||
* `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 an empty handle.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief The keyframe's time as a rational (numerator/denominator).
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
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,
|
||||
int64_t time_den);
|
||||
|
||||
/**
|
||||
* @brief Create a set-time command (olive::NodeParamSetKeyframeTimeCommand).
|
||||
*/
|
||||
int oaknode_keyframe_set_time_undoable(OakNodeKeyframe keyframe,
|
||||
int64_t time_num, int64_t time_den,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @brief Read the keyframe's value mapped into `out`. Values without a
|
||||
* POD representation fail with OAKNODE_E_FAILED.
|
||||
*/
|
||||
int oaknode_keyframe_get_value(OakNodeKeyframe keyframe,
|
||||
oaknode_value *out);
|
||||
|
||||
/**
|
||||
* @brief Set the keyframe's value directly (live).
|
||||
* OAKNODE_VALUE_STRING is rejected (use
|
||||
* oaknode_keyframe_set_value_string()).
|
||||
*/
|
||||
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,
|
||||
const oaknode_value *v,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @brief Read a string value. Two-stage getter.
|
||||
*
|
||||
* @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(OakNodeKeyframe keyframe,
|
||||
char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Set a string value directly (live).
|
||||
*/
|
||||
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,
|
||||
const char *value,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @brief The keyframe's interpolation type (oaknode_keyframe_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);
|
||||
|
||||
/**
|
||||
* @brief Create a set-type command (same semantics as the live variant).
|
||||
*/
|
||||
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(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,
|
||||
double x, double y);
|
||||
|
||||
/**
|
||||
* @brief Create a set-bezier-control command.
|
||||
*/
|
||||
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(OakNodeKeyframe keyframe,
|
||||
int *out_track);
|
||||
|
||||
/**
|
||||
* @brief The keyframe's element index.
|
||||
*/
|
||||
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(OakNodeKeyframe keyframe, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @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(OakNodeKeyframe keyframe,
|
||||
OakNodeNode *out_node);
|
||||
|
||||
/**
|
||||
* @brief A bezier control point guaranteed valid for animation
|
||||
* (NodeKeyframe::valid_bezier_control_in()/out()).
|
||||
*
|
||||
* Unlike oaknode_keyframe_get_bezier_control(), the returned point is
|
||||
* clamped so the curve never overlaps: the in-handle's x cannot pass the
|
||||
* previous keyframe's time and the out-handle's x cannot pass the next
|
||||
* keyframe's time. `handle` is an oaknode_keyframe_bezier.
|
||||
*/
|
||||
int oaknode_keyframe_get_valid_bezier_control(OakNodeKeyframe keyframe,
|
||||
int handle, double *out_x,
|
||||
double *out_y);
|
||||
|
||||
/**
|
||||
* @brief The opposing bezier handle type
|
||||
* (NodeKeyframe::get_opposing_bezier_type): OAKNODE_KEYFRAME_IN_HANDLE
|
||||
* (0) <-> OAKNODE_KEYFRAME_OUT_HANDLE (1).
|
||||
*
|
||||
* @return The opposing handle type, or OAKNODE_E_INVALID for a type
|
||||
* outside the two handle values.
|
||||
*/
|
||||
int oaknode_keyframe_opposing_bezier_type(int type);
|
||||
|
||||
/**
|
||||
* @brief Compute the combined node value to use when inserting
|
||||
* `keyframe` onto `target_node` (the keyframe paste path).
|
||||
*
|
||||
* Takes the target node's split value at the keyframe's time, replaces
|
||||
* the keyframe's own track with the keyframe's value, and combines the
|
||||
* per-track components into a single normal value (mirrors the facade's
|
||||
* oakengine_keyframe_compute_paste_value). OAKNODE_E_NOT_FOUND when the
|
||||
* keyframe's input id does not exist on `target_node`; OAKNODE_E_FAILED
|
||||
* for input types without a POD representation.
|
||||
*/
|
||||
int oaknode_keyframe_compute_paste_value(OakNodeNode target_node,
|
||||
OakNodeKeyframe keyframe,
|
||||
oaknode_value *out);
|
||||
|
||||
/**
|
||||
* @brief 1 if a sibling keyframe exists at the given rational time on
|
||||
* this keyframe's own track (NodeKeyframe::has_sibling_at_time(): the
|
||||
* track's key at `time` that is not this keyframe — the move-collision
|
||||
* check). Unlike the facade, the time is an exact rational rather than a
|
||||
* whole-second frame timestamp, and no track argument is needed (the
|
||||
* lookup is relative to this keyframe's track).
|
||||
*
|
||||
* An orphaned keyframe (no parent node) has no siblings: `*out_value`
|
||||
* is set to 0 and OAKNODE_OK is returned.
|
||||
*/
|
||||
int oaknode_keyframe_has_sibling_at_time(OakNodeKeyframe keyframe,
|
||||
int64_t time_num, int64_t time_den,
|
||||
int *out_value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_KEYFRAME_H
|
||||
@@ -0,0 +1,119 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_MULTICAM_H
|
||||
#define OAK_EDITOR_NODE_MULTICAM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "node/error.h"
|
||||
#include "node/node.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file multicam.h
|
||||
* @brief C ABI for olive::MultiCamNode (src/node/src/input/multicam/
|
||||
* multicamnode.h): multi-camera source switching and the source-grid
|
||||
* math used by the multicam viewer.
|
||||
*
|
||||
* The input-id getters return static strings (never freed) naming the
|
||||
* multicam node's inputs: current source (combo), sources (array),
|
||||
* sequence and sequence type. A node that is not a MultiCamNode (or a
|
||||
* NULL handle) fails the per-node queries with OAKNODE_E_INVALID.
|
||||
*
|
||||
* The grid helpers are static and pure: they only depend on their
|
||||
* arguments, not on a node.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief The input id string for the current camera ("current_in").
|
||||
*/
|
||||
const char *oaknode_multicam_input_current(void);
|
||||
|
||||
/**
|
||||
* @brief The input id string for the sources array ("sources_in").
|
||||
*/
|
||||
const char *oaknode_multicam_input_sources(void);
|
||||
|
||||
/**
|
||||
* @brief The input id string for the sequence ("sequence_in").
|
||||
*/
|
||||
const char *oaknode_multicam_input_sequence(void);
|
||||
|
||||
/**
|
||||
* @brief The input id string for the sequence type ("sequence_type_in").
|
||||
*/
|
||||
const char *oaknode_multicam_input_sequence_type(void);
|
||||
|
||||
/**
|
||||
* @brief Number of connected source cameras (MultiCamNode::
|
||||
* get_source_count(); the connected sequence's track count, or the
|
||||
* sources array size when no sequence is connected).
|
||||
*
|
||||
* OAKNODE_E_INVALID when `node` is not a multicam.
|
||||
*/
|
||||
int oaknode_multicam_get_source_count(OakNodeNode node, int *out_count);
|
||||
|
||||
/**
|
||||
* @brief Compute the grid (rows, cols) that holds `source_count` cells.
|
||||
*
|
||||
* Mirrors MultiCamNode::get_rows_and_columns(): the grid grows from
|
||||
* 1x1, widening the smaller dimension, until rows * cols >= source_count
|
||||
* (0 sources yields 1x1). OAKNODE_E_INVALID for a negative count or
|
||||
* NULL out pointers.
|
||||
*/
|
||||
int oaknode_multicam_get_rows_and_columns(int source_count, int *rows,
|
||||
int *cols);
|
||||
|
||||
/**
|
||||
* @brief Convert a flat source index to (row, col) in a rows x cols grid
|
||||
* (row-major: col = index % cols, row = index / cols).
|
||||
*
|
||||
* OAKNODE_E_INVALID for a negative index, degenerate grid or NULL out
|
||||
* pointers.
|
||||
*/
|
||||
int oaknode_multicam_index_to_row_cols(int index, int rows, int cols,
|
||||
int *out_row, int *out_col);
|
||||
|
||||
/**
|
||||
* @brief Convert (row, col) to a flat source index (col + row * cols).
|
||||
*
|
||||
* @return The flat index (>= 0), or OAKNODE_E_INVALID when the cell is
|
||||
* out of range or the grid is degenerate.
|
||||
*/
|
||||
int oaknode_multicam_rows_cols_to_index(int row, int col, int rows,
|
||||
int cols);
|
||||
|
||||
/**
|
||||
* @brief The current source index (MultiCamNode::get_current_source(),
|
||||
* the "current_in" combo value).
|
||||
*
|
||||
* OAKNODE_E_INVALID when `node` is not a multicam.
|
||||
*/
|
||||
int oaknode_multicam_get_current_source(OakNodeNode node, int *out_source);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_MULTICAM_H
|
||||
@@ -0,0 +1,679 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_NODE_H
|
||||
#define OAK_EDITOR_NODE_NODE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/videoparams.h"
|
||||
#include "node/error.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file node.h
|
||||
* @brief C ABI for olive::Node (src/node/src/node.h).
|
||||
*
|
||||
* 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
|
||||
* inputs (NodeValue::k_file/k_text/k_font/k_str_combo) do not fit the POD
|
||||
* and use the dedicated *_input_string() pair (two-stage buf/size getters
|
||||
* return the required size including the terminating NUL).
|
||||
*
|
||||
* Every mutating function comes in a live variant (applies immediately)
|
||||
* and an undoable variant (suffix _undoable) that creates an
|
||||
* olive::UndoCommand without executing it and returns it as an owned
|
||||
* OakUndoCommand handle. Execute it with oakundo_command_redo_now(),
|
||||
* push it onto an OakUndoStack, or release it with
|
||||
* oakundo_command_free().
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Value type of an oaknode_value / a node input.
|
||||
*
|
||||
* Pinned mapping to olive::NodeValue::Type (src/node/src/value.h):
|
||||
* NONE -> k_none, INT -> k_int, FLOAT -> k_float, BOOL -> k_boolean,
|
||||
* RATIONAL -> k_rational, COLOR -> k_color, VEC2 -> k_vec2,
|
||||
* VEC3 -> k_vec3, VEC4 -> k_vec4, COMBO -> k_combo,
|
||||
* STRING -> k_file (string-family inputs: k_file/k_text/k_font/
|
||||
* k_str_combo, handled by the dedicated string functions). Types without
|
||||
* a POD representation (texture, samples, matrix, params, bezier, binary,
|
||||
* ...) report as OAKNODE_VALUE_NONE.
|
||||
*/
|
||||
typedef enum oaknode_value_type {
|
||||
OAKNODE_VALUE_NONE = 0,
|
||||
OAKNODE_VALUE_INT, /**< num (olive k_int, int64_t) */
|
||||
OAKNODE_VALUE_FLOAT, /**< f[0] (olive k_float, double) */
|
||||
OAKNODE_VALUE_BOOL, /**< num 0/1 (olive k_boolean) */
|
||||
OAKNODE_VALUE_RATIONAL, /**< num/den (olive k_rational) */
|
||||
OAKNODE_VALUE_COLOR, /**< f[0..3] = r,g,b,a (olive k_color) */
|
||||
OAKNODE_VALUE_VEC2, /**< f[0..1] (olive k_vec2) */
|
||||
OAKNODE_VALUE_VEC3, /**< f[0..2] (olive k_vec3) */
|
||||
OAKNODE_VALUE_VEC4, /**< f[0..3] (olive k_vec4) */
|
||||
OAKNODE_VALUE_COMBO, /**< num = selected index (olive k_combo) */
|
||||
OAKNODE_VALUE_STRING, /**< k_file string family; string APIs only */
|
||||
OAKNODE_VALUE_COUNT
|
||||
} oaknode_value_type;
|
||||
|
||||
/**
|
||||
* @brief POD parameter value. Only the fields documented for the value's
|
||||
* `type` are meaningful.
|
||||
*/
|
||||
typedef struct oaknode_value {
|
||||
int type; /**< oaknode_value_type. */
|
||||
int64_t num; /**< INT/COMBO value, BOOL 0/1, RATIONAL numerator. */
|
||||
int64_t den; /**< RATIONAL denominator. */
|
||||
double f[4]; /**< FLOAT f[0]; VEC2/3/4 f[0..n-1]; COLOR r,g,b,a. */
|
||||
} oaknode_value;
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
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;
|
||||
|
||||
/* Re-declared here so node.h is self-contained; see node/footage.h. */
|
||||
typedef struct OakNodeFootage OakNodeFootage;
|
||||
|
||||
/**
|
||||
* @brief Timeline data owned by viewer nodes (TimelineMarkerList /
|
||||
* TimelineWorkArea in oaktimeline) cross the boundary as oaktimeline
|
||||
* value handles. Forward-declared here so node.h stays self-contained;
|
||||
* include timeline/marker.h / timeline/workarea.h for the definitions.
|
||||
*/
|
||||
struct OakTimelineMarkerList;
|
||||
struct OakTimelineWorkArea;
|
||||
|
||||
/**
|
||||
* @brief Opaque borrowed handle to a node's video frame cache
|
||||
* (olive::FrameHashCache in oakrender). oakrender reinterprets this into
|
||||
* its own handle types.
|
||||
*/
|
||||
struct OakRenderCache;
|
||||
|
||||
/* oakcore handles used by the viewer setters. */
|
||||
typedef struct OakAudioParams OakAudioParams;
|
||||
|
||||
/**
|
||||
* @brief Number of live owned objects created through this API
|
||||
* (nodes from oaknode_factory_create_from_id()/oaknode_node_create_copy(),
|
||||
* keyframes, groups, traversers, traverser databases). Debug aid for
|
||||
* leak checking; thread-unsafe, test/diagnostic use only.
|
||||
*/
|
||||
int oaknode_debug_alive_count(void);
|
||||
|
||||
/* ---- Metadata --------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief The node's unique type id (Node::id(), e.g.
|
||||
* "org.olivevideoeditor.Olive.solidgenerator"). Two-stage getter.
|
||||
*
|
||||
* @return Required buffer size in bytes including the terminating NUL
|
||||
* (non-negative), or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
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(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(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);
|
||||
|
||||
/**
|
||||
* @brief Create a label-change command (olive::NodeRenameCommand).
|
||||
*
|
||||
* The command is NOT executed; `out_command` receives an owned command
|
||||
* handle.
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_node_set_label_undoable(OakNodeNode node, const char *label,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @brief The node's override color index (Node::get_override_color();
|
||||
* -1 = none).
|
||||
*
|
||||
* @param out_value Receives the result. Must not be NULL.
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @brief 1 if the node is enabled (the boolean "enabled_in" input's
|
||||
* standard value).
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Create an enabled-state command
|
||||
* (olive::NodeParamSetStandardValueCommand on "enabled_in").
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_node_set_enabled_undoable(OakNodeNode node, int enabled,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/* ---- Input introspection ------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Number of declared inputs (Node::inputs(); array elements are not
|
||||
* counted separately).
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
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(OakNodeNode node, int index, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief The input's value type mapped to oaknode_value_type (see the
|
||||
* pinned mapping on oaknode_value_type). OAKNODE_E_NOT_FOUND for an
|
||||
* unknown 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(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(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(OakNodeNode node, const char *input_id,
|
||||
char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @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(OakNodeNode node,
|
||||
const char *input_id,
|
||||
OakNodeNode *out_node);
|
||||
|
||||
/* ---- Parameter access ----------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Read an input's standard value (Node::get_standard_value())
|
||||
* mapped into `out`.
|
||||
*
|
||||
* String-family inputs fail with OAKNODE_E_INVALID (use
|
||||
* oaknode_node_get_input_string()); types without a POD representation
|
||||
* fail with OAKNODE_E_FAILED; an unknown input id fails with
|
||||
* OAKNODE_E_NOT_FOUND.
|
||||
*/
|
||||
int oaknode_node_get_input(OakNodeNode node, const char *input_id,
|
||||
oaknode_value *out);
|
||||
|
||||
/**
|
||||
* @brief Write an input's standard value directly (live,
|
||||
* Node::set_standard_value()).
|
||||
*
|
||||
* `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,
|
||||
const oaknode_value *v);
|
||||
|
||||
/**
|
||||
* @brief Create a set-standard-value command
|
||||
* (olive::NodeParamSetStandardValueCommand, track -1 semantics via the
|
||||
* whole-value reference on track 0).
|
||||
*
|
||||
* Same type rules as oaknode_node_set_input().
|
||||
*/
|
||||
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(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,
|
||||
const char *value);
|
||||
|
||||
/**
|
||||
* @brief Create a set-standard-value command for a string-family input.
|
||||
*/
|
||||
int oaknode_node_set_input_string_undoable(OakNodeNode node,
|
||||
const char *input_id,
|
||||
const char *value,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/* ---- Graph editing -------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Connect `output_node`'s output into `input_node`'s `input_id`
|
||||
* directly (live, Node::connect_edge(), element -1).
|
||||
*
|
||||
* Fails with OAKNODE_E_NOT_FOUND for an unknown input id,
|
||||
* OAKNODE_E_INVALID when the input is not connectable, and
|
||||
* 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,
|
||||
const char *input_id);
|
||||
|
||||
/**
|
||||
* @brief Create an edge-add command (olive::NodeEdgeAddCommand,
|
||||
* element -1). Same validation as oaknode_node_connect() except the
|
||||
* different-graph check (the command may legitimately be redone after
|
||||
* graph changes).
|
||||
*/
|
||||
int oaknode_node_connect_undoable(OakNodeNode output_node,
|
||||
OakNodeNode input_node,
|
||||
const char *input_id,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @brief Remove the edge feeding `input_node`'s `input_id` directly
|
||||
* (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);
|
||||
|
||||
/**
|
||||
* @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,
|
||||
const char *input_id,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @brief Number of outgoing edges (Node::output_connections()).
|
||||
*/
|
||||
int oaknode_node_output_connection_count(OakNodeNode node, int *out_count);
|
||||
|
||||
/**
|
||||
* @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(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(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(OakNodeNode node, int index,
|
||||
int *out_element);
|
||||
|
||||
/* ---- Links --------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Link two nodes directly (live, Node::link()). `out_linked`
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Create a link/unlink command (olive::NodeLinkCommand;
|
||||
* `link` != 0 links, 0 unlinks).
|
||||
*/
|
||||
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(OakNodeNode a, OakNodeNode b, int *out_value);
|
||||
|
||||
/**
|
||||
* @brief Number of linked nodes (Node::links()).
|
||||
*/
|
||||
int oaknode_node_link_count(OakNodeNode node, int *out_count);
|
||||
|
||||
/**
|
||||
* @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(OakNodeNode node, int index,
|
||||
OakNodeNode *out_node);
|
||||
|
||||
/* ---- Context positions ---------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Number of context entries (Node::get_context_positions()).
|
||||
*/
|
||||
int oaknode_node_context_count(OakNodeNode node, int *out_count);
|
||||
|
||||
/**
|
||||
* @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(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(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,
|
||||
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,
|
||||
double y, int expanded,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/* ---- Lifetime --------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Create a standalone copy of the node (Node::copy()). The copy is
|
||||
* 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(OakNodeNode node);
|
||||
|
||||
/**
|
||||
* @brief Copy a node inside its graph (Node::copy_node_in_graph()),
|
||||
* recording the reconnect operations in a new MultiUndoCommand.
|
||||
*
|
||||
* `*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 an empty handle (ctx == NULL) on failure.
|
||||
*/
|
||||
OakNodeNode oaknode_node_copy_in_graph(OakNodeNode node,
|
||||
OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @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(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);
|
||||
|
||||
/**
|
||||
* @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,
|
||||
const char *input_id, int element);
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Create a command that sets a node's position in a context and
|
||||
* repositions its dependencies recursively
|
||||
* (olive::NodeSetPositionAndDependenciesRecursivelyCommand). Owned.
|
||||
*/
|
||||
OakUndoCommand oaknode_command_create_set_position_recursive(
|
||||
OakNodeNode node, OakNodeNode context, double x, double y);
|
||||
|
||||
/**
|
||||
* @brief Marker list / work area of a viewer node, as addref'd
|
||||
* oaktimeline value handles (release with
|
||||
* oaktimeline_marker_list_free()/oaktimeline_workarea_free()). *out is
|
||||
* an empty handle (ctx == NULL) when the node is not a viewer or for
|
||||
* an empty node handle.
|
||||
*/
|
||||
int oaknode_node_get_markers(OakNodeNode node,
|
||||
struct OakTimelineMarkerList *out);
|
||||
int oaknode_node_get_work_area(OakNodeNode node,
|
||||
struct OakTimelineWorkArea *out);
|
||||
|
||||
/**
|
||||
* @brief Video frame cache of a node as an addref'd oakrender value
|
||||
* handle (release with oakrender_cache_free()). *out is an
|
||||
* empty handle (ctx == NULL) when the node has none or for an
|
||||
* empty node handle. struct OakRenderCache is forward-declared
|
||||
* here; include render/cache.h for the definition.
|
||||
*/
|
||||
int oaknode_node_get_video_frame_cache(OakNodeNode node,
|
||||
struct OakRenderCache *out);
|
||||
|
||||
/**
|
||||
* @brief Copy input values/connections from one node to another
|
||||
* (Node::copy_inputs()). include_connections != 0 also copies
|
||||
* input connections.
|
||||
*/
|
||||
int oaknode_node_copy_inputs(OakNodeNode dst, OakNodeNode src,
|
||||
int include_connections);
|
||||
|
||||
/**
|
||||
* @brief Set a track-routing value hint on an input
|
||||
* (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 track_type, int track_index);
|
||||
|
||||
/**
|
||||
* @brief Set a viewer node's video/audio params (ViewerOutput::
|
||||
* 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,
|
||||
const OakVideoParams *params);
|
||||
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` receives
|
||||
* a borrowed handle (empty, ctx == NULL, when none; releasing it
|
||||
* only releases the handle).
|
||||
*/
|
||||
int oaknode_node_find_input_footage(OakNodeNode node, OakNodeFootage *out);
|
||||
|
||||
/**
|
||||
* @brief Value of an input at a specific time (Node::get_value_at_time(),
|
||||
* element -1). Same POD rules as oaknode_node_get_input().
|
||||
*/
|
||||
int oaknode_node_get_input_at_time(OakNodeNode node,
|
||||
const char *input_id, int64_t time_num,
|
||||
int64_t time_den, oaknode_value *out);
|
||||
|
||||
/**
|
||||
* @brief Set an input's value at a specific time with keyframe logic
|
||||
* (Node::set_value_at_time(), element -1, track 0,
|
||||
* insert_on_all_tracks_if_no_key = true). `*out_command` receives
|
||||
* an owned undo command handle.
|
||||
*/
|
||||
int oaknode_node_set_input_at_time_undoable(OakNodeNode node,
|
||||
const char *input_id, int64_t time_num, int64_t time_den,
|
||||
const oaknode_value *v, int track, OakUndoCommand *out_command);
|
||||
|
||||
/**
|
||||
* @brief Identity of the underlying node object as an opaque integer
|
||||
* (address-cast; for registry keys only, never dereference).
|
||||
*/
|
||||
uintptr_t oaknode_node_identity(OakNodeNode node);
|
||||
|
||||
/**
|
||||
* @brief Append a value-at-time set into an existing multi command
|
||||
* (same semantics as oaknode_node_set_input_at_time_undoable but
|
||||
* batches into `multi_command` from oakundo_command_init_multi()).
|
||||
*/
|
||||
int oaknode_node_set_input_at_time_into(OakNodeNode node,
|
||||
const char *input_id, int64_t time_num, int64_t time_den,
|
||||
const oaknode_value *v, int track, OakUndoCommand multi_command);
|
||||
|
||||
/**
|
||||
* @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 an
|
||||
* empty handle (ctx == NULL) on failure.
|
||||
*/
|
||||
OakUndoCommand oaknode_command_create_remove_node(OakNodeNode node);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a node handle.
|
||||
*
|
||||
* 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_NODE_H
|
||||
@@ -0,0 +1,267 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_PROJECT_H
|
||||
#define OAK_EDITOR_NODE_PROJECT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "node/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file project.h
|
||||
* @brief C ABI for olive::Project (oaknode)
|
||||
*
|
||||
* 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 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
|
||||
* failure.
|
||||
* - String getters are two-stage: pass buf == NULL (or a short buffer) to
|
||||
* 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.
|
||||
* - 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 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 {
|
||||
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 Node handle (defined by the node family; forward-declared
|
||||
* here so the headers can be included in any order).
|
||||
*/
|
||||
typedef struct OakNodeNode OakNodeNode;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @brief Create an empty project shell.
|
||||
*
|
||||
* The project has no root folder until oaknode_project_initialize() is
|
||||
* called (mirrors Project::initialize()).
|
||||
*
|
||||
* @return Project handle with reference count 1 (release with
|
||||
* oaknode_project_free()); ctx is NULL on allocation failure.
|
||||
*/
|
||||
OakNodeProject oaknode_project_init(void);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Initialize the project: create the root folder (Project::initialize()).
|
||||
*
|
||||
* @return OAKNODE_OK, or OAKNODE_E_STATE if already initialized.
|
||||
*/
|
||||
int oaknode_project_initialize(OakNodeProject project);
|
||||
|
||||
/**
|
||||
* @brief Destructively destroy all nodes in the graph (Project::clear()).
|
||||
*
|
||||
* The project shell stays usable; oaknode_project_initialize() may be called
|
||||
* again afterwards.
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_project_clear(OakNodeProject project);
|
||||
|
||||
/**
|
||||
* @brief Borrowed handle of the project's root folder (Project::root()).
|
||||
*
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Project display name (Project::name(): the filename's base name, or
|
||||
* "(untitled)"). Two-stage string getter.
|
||||
*
|
||||
* @return Required buffer size in bytes including the NUL, or a negative
|
||||
* OAKNODE_E_* error code.
|
||||
*/
|
||||
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(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(OakNodeProject project, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Set the project's filename (Project::set_filename()).
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
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 an empty handle.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief 1 if the project is new (untitled and unmodified, Project::is_new()).
|
||||
* Negative OAKNODE_E_* code on an empty handle.
|
||||
*/
|
||||
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(OakNodeProject project, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Copy all project settings (Project::copy_settings()).
|
||||
*/
|
||||
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 an
|
||||
* empty handle.
|
||||
*/
|
||||
int oaknode_project_get_cache_location_setting(OakNodeProject project);
|
||||
|
||||
/**
|
||||
* @brief Set the cache location setting (0/1/2, see
|
||||
* oaknode_project_get_cache_location_setting()).
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
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(OakNodeProject project,
|
||||
char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Set a custom cache directory (Project::set_custom_cache_path()).
|
||||
* NULL clears it.
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
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(OakNodeProject project, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Detach a node from the graph without deleting it
|
||||
* (Project::remove_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);
|
||||
|
||||
/**
|
||||
* @brief Number of nodes belonging to the graph (Project::nodes().size()).
|
||||
* Negative OAKNODE_E_* code on an empty handle.
|
||||
*/
|
||||
int oaknode_project_node_count(OakNodeProject project);
|
||||
|
||||
/**
|
||||
* @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(OakNodeProject project, int index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_PROJECT_H
|
||||
@@ -0,0 +1,219 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_SEQUENCE_H
|
||||
#define OAK_EDITOR_NODE_SEQUENCE_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/videoparams.h"
|
||||
#include "node/error.h"
|
||||
#include "olive/core/oakcore/audioparams.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Sequence texture/samples input ids (ViewerOutput::k_texture_input
|
||||
* / k_samples_input) and the track input id format
|
||||
* (Sequence::k_track_input_format). Pinned by test.
|
||||
*/
|
||||
#define OAKNODE_SEQUENCE_TEXTURE_INPUT "tex_in"
|
||||
#define OAKNODE_SEQUENCE_SAMPLES_INPUT "samples_in"
|
||||
#define OAKNODE_SEQUENCE_TRACK_INPUT_FORMAT "track_in_%1"
|
||||
|
||||
/* Re-declared here so sequence.h is self-contained; see node/node.h. */
|
||||
typedef struct OakNodeNode OakNodeNode;
|
||||
|
||||
/**
|
||||
* @brief Reference-counted handle to a sequence (olive::Sequence).
|
||||
*
|
||||
* 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 {
|
||||
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 Reference-counted handle to a track list (olive::TrackList),
|
||||
* see node/track.h.
|
||||
*/
|
||||
typedef struct OakNodeTrackList OakNodeTrackList;
|
||||
|
||||
/**
|
||||
* @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 with reference count 1 (release with
|
||||
* oaknode_sequence_free()); ctx is NULL on allocation failure.
|
||||
*/
|
||||
OakNodeSequence oaknode_sequence_create(void);
|
||||
|
||||
/**
|
||||
* @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 Non-owning cast from a node handle to a sequence handle (empty
|
||||
* ctx when the node is not a Sequence).
|
||||
*/
|
||||
OakNodeSequence oaknode_sequence_from_node(OakNodeNode node);
|
||||
|
||||
/**
|
||||
* @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).
|
||||
*/
|
||||
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 *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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Playhead position in sequence time.
|
||||
*/
|
||||
int oaknode_sequence_get_playhead(OakNodeSequence sequence, int *numerator,
|
||||
int *denominator);
|
||||
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 *denominator);
|
||||
int oaknode_sequence_get_video_length(OakNodeSequence sequence,
|
||||
int *numerator, int *denominator);
|
||||
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);
|
||||
|
||||
/* --------------------------------------------------- Video/audio params */
|
||||
|
||||
/**
|
||||
* @brief Number of video/audio parameter slots.
|
||||
*/
|
||||
int oaknode_sequence_get_video_stream_count(OakNodeSequence sequence,
|
||||
int *count);
|
||||
int oaknode_sequence_get_audio_stream_count(OakNodeSequence sequence,
|
||||
int *count);
|
||||
|
||||
/**
|
||||
* @brief Video parameters at `index` as a NEW by-value handle owned by
|
||||
* the caller (reference count 1, release with
|
||||
* oakcommon_videoparams_free()).
|
||||
*
|
||||
* @return OAKNODE_OK, OAKNODE_E_INVALID, OAKNODE_E_NOT_FOUND or
|
||||
* OAKNODE_E_NOMEM.
|
||||
*/
|
||||
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 the sequence handle is empty, params.ctx is
|
||||
* NULL, or index is negative.
|
||||
*/
|
||||
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,
|
||||
OakAudioParams **out);
|
||||
|
||||
/**
|
||||
* @brief Replace the audio parameters at `index` with a copy of `params`.
|
||||
*/
|
||||
int oaknode_sequence_set_audio_params(OakNodeSequence sequence, int index,
|
||||
const OakAudioParams *params);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_SEQUENCE_H
|
||||
@@ -0,0 +1,310 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_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
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file serializer.h
|
||||
* @brief C ABI for olive::ProjectSerializer (oaknode), in-memory form
|
||||
*
|
||||
* Clipboard copy/paste and node-graph XML round trips without touching the
|
||||
* filesystem: "copy" is oaknode_serializer_save_to_xml() (serialize a
|
||||
* SaveData to an XML string), "paste" is oaknode_serializer_load_from_xml()
|
||||
* (parse an XML string into a project, exposing the resulting LoadData).
|
||||
* System-clipboard integration and on-disk .ove save/load live in the
|
||||
* facade / oakstorage layers (M9/M10), not here.
|
||||
*
|
||||
* oaknode_serializer_initialize() must be called before any save/load; it
|
||||
* registers the versioned serializers and the node factory the loaders use
|
||||
* to instantiate nodes by id.
|
||||
*/
|
||||
|
||||
/** @brief Load type: a whole project. */
|
||||
#define OAKNODE_SERIALIZER_LOAD_PROJECT 0
|
||||
/** @brief Load type: only nodes (clipboard node-graph paste). */
|
||||
#define OAKNODE_SERIALIZER_LOAD_ONLY_NODES 1
|
||||
/** @brief Load type: only clips (timeline family). */
|
||||
#define OAKNODE_SERIALIZER_LOAD_ONLY_CLIPS 2
|
||||
/** @brief Load type: only markers (timeline family). */
|
||||
#define OAKNODE_SERIALIZER_LOAD_ONLY_MARKERS 3
|
||||
/** @brief Load type: only keyframes (keyframe family). */
|
||||
#define OAKNODE_SERIALIZER_LOAD_ONLY_KEYFRAMES 4
|
||||
|
||||
/** @brief Serializer result code: success. */
|
||||
#define OAKNODE_SERIALIZER_OK 0
|
||||
/** @brief Serializer result code: data written by a too-old format. */
|
||||
#define OAKNODE_SERIALIZER_TOO_OLD 1
|
||||
/** @brief Serializer result code: data written by a too-new format. */
|
||||
#define OAKNODE_SERIALIZER_TOO_NEW 2
|
||||
/** @brief Serializer result code: unrecognizable format version. */
|
||||
#define OAKNODE_SERIALIZER_UNKNOWN_VERSION 3
|
||||
/** @brief Serializer result code: file I/O error (unused in-memory). */
|
||||
#define OAKNODE_SERIALIZER_FILE_ERROR 4
|
||||
/** @brief Serializer result code: XML parse error. */
|
||||
#define OAKNODE_SERIALIZER_XML_ERROR 5
|
||||
/** @brief Serializer result code: overwrite error (unused in-memory). */
|
||||
#define OAKNODE_SERIALIZER_OVERWRITE_ERROR 6
|
||||
/** @brief Serializer result code: no data to load. */
|
||||
#define OAKNODE_SERIALIZER_NO_DATA 7
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
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 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 {
|
||||
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.
|
||||
* Idempotent. Must be called before any save/load.
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_serializer_initialize(void);
|
||||
|
||||
/**
|
||||
* @brief Tear down the serializers and the node factory registered by
|
||||
* oaknode_serializer_initialize(). Safe to call when not initialized.
|
||||
*/
|
||||
void oaknode_serializer_shutdown(void);
|
||||
|
||||
/**
|
||||
* @brief Create a save descriptor.
|
||||
*
|
||||
* @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 an empty handle for
|
||||
* load types that do not require it.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Restrict serialization to the given nodes
|
||||
* (SaveData::set_only_serialize_nodes()). `nodes` is an array of `count`
|
||||
* borrowed node handles.
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_serializer_savedata_set_nodes(
|
||||
OakNodeSerializerSaveData save_data, const OakNodeNode *nodes, int count);
|
||||
|
||||
/**
|
||||
* @brief Attach a free-form (key, value) property to a node in the
|
||||
* serialized output (SaveData::set_properties()); used for graph positions
|
||||
* and clip metadata. Replaces the value if the (node, key) pair exists.
|
||||
*
|
||||
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_serializer_savedata_set_property(
|
||||
OakNodeSerializerSaveData save_data, OakNodeNode node, const char *key,
|
||||
const char *value);
|
||||
|
||||
/**
|
||||
* @brief Serialize to an in-memory XML document ("copy"). Two-stage string
|
||||
* getter: pass buf == NULL to query the size.
|
||||
*
|
||||
* @return Required buffer size in bytes including the NUL, or a negative
|
||||
* OAKNODE_E_* error code (OAKNODE_E_STATE if the serializers have
|
||||
* not been initialized).
|
||||
*/
|
||||
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 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
|
||||
* (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.
|
||||
* @param details_buf_size Size of details_buf.
|
||||
*
|
||||
* @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 load_type, int *out_result,
|
||||
OakNodeSerializerLoadData *out_load_data,
|
||||
char *details_buf, int details_buf_size);
|
||||
|
||||
/**
|
||||
* @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()
|
||||
* (or attached under a folder); otherwise they leak.
|
||||
*/
|
||||
void oaknode_serializer_loaddata_free(OakNodeSerializerLoadData *load_data);
|
||||
|
||||
/**
|
||||
* @brief Number of nodes created by the load. Negative OAKNODE_E_* code on
|
||||
* an empty handle.
|
||||
*/
|
||||
int oaknode_serializer_loaddata_node_count(
|
||||
OakNodeSerializerLoadData load_data);
|
||||
|
||||
/**
|
||||
* @brief Borrowed handle of the loaded node at `index`, or an empty handle
|
||||
* when out of range.
|
||||
*/
|
||||
OakNodeNode oaknode_serializer_loaddata_node_at(
|
||||
OakNodeSerializerLoadData load_data, int index);
|
||||
|
||||
/**
|
||||
* @brief Look up a serialized property attached to a loaded node.
|
||||
* Two-stage string getter.
|
||||
*
|
||||
* @return Required buffer size in bytes including the NUL,
|
||||
* OAKNODE_E_NOT_FOUND if the (node, key) pair is absent, or another
|
||||
* negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_serializer_loaddata_get_property(
|
||||
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 an empty handle.
|
||||
*/
|
||||
int oaknode_serializer_loaddata_connection_count(
|
||||
OakNodeSerializerLoadData load_data);
|
||||
|
||||
/**
|
||||
* @brief Read the promised connection at `index`.
|
||||
*
|
||||
* All output parameters except the input-id buffer are required;
|
||||
* `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 (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.
|
||||
*
|
||||
* @return OAKNODE_OK, OAKNODE_E_NOT_FOUND when out of range, or another
|
||||
* negative OAKNODE_E_* error code.
|
||||
*/
|
||||
int oaknode_serializer_loaddata_connection_at(
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_SERIALIZER_H
|
||||
|
||||
/**
|
||||
* @brief Result codes for file-level save/load (mirror
|
||||
* ProjectSerializer::ResultCode; pinned by test).
|
||||
*/
|
||||
enum OakNodeSerializerResultCode {
|
||||
OAKNODE_SERIALIZER_RESULT_SUCCESS = 0,
|
||||
OAKNODE_SERIALIZER_RESULT_PROJECT_TOO_OLD = 1,
|
||||
OAKNODE_SERIALIZER_RESULT_PROJECT_TOO_NEW = 2,
|
||||
OAKNODE_SERIALIZER_RESULT_UNKNOWN_VERSION = 3,
|
||||
OAKNODE_SERIALIZER_RESULT_FILE_ERROR = 4,
|
||||
OAKNODE_SERIALIZER_RESULT_XML_ERROR = 5,
|
||||
OAKNODE_SERIALIZER_RESULT_OVERWRITE_ERROR = 6,
|
||||
OAKNODE_SERIALIZER_RESULT_NO_DATA = 7
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Save a project to a file (ProjectSerializer::save(), project
|
||||
* type, optional OVEC compression). Layout data is not serialized
|
||||
* through this API (app-layer concern, see oakstorage/M10).
|
||||
*
|
||||
* @param out_code Receives an OakNodeSerializerResultCode (may be NULL).
|
||||
* @param details Optional two-stage buffer for the result details
|
||||
* 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 empty
|
||||
* handles/NULL args.
|
||||
*/
|
||||
int oaknode_serializer_save_to_file(OakNodeProject project,
|
||||
const char *filename, int use_compression, int *out_code,
|
||||
char *details, int details_size);
|
||||
|
||||
/**
|
||||
* @brief Load a project from a file into `project`
|
||||
* (ProjectSerializer::load(), project type).
|
||||
*
|
||||
* Same return/out-param convention as oaknode_serializer_save_to_file().
|
||||
*/
|
||||
int oaknode_serializer_load_from_file(OakNodeProject project,
|
||||
const char *filename, int *out_code, char *details,
|
||||
int details_size);
|
||||
@@ -0,0 +1,355 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_TRACK_H
|
||||
#define OAK_EDITOR_NODE_TRACK_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "node/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Reference-counted handle to a track (olive::Track).
|
||||
*
|
||||
* 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 {
|
||||
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 Reference-counted handle to a per-type track container
|
||||
* (olive::TrackList).
|
||||
*
|
||||
* Always borrowed from oaknode_sequence_get_track_list(); releasing the
|
||||
* handle never destroys the list, which stays owned by its sequence.
|
||||
*/
|
||||
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 Reference-counted handle to a block (olive::Block), see
|
||||
* node/block.h.
|
||||
*/
|
||||
typedef struct OakNodeBlock OakNodeBlock;
|
||||
|
||||
/**
|
||||
* @brief Reference-counted handle to a sequence (olive::Sequence), see
|
||||
* node/sequence.h.
|
||||
*/
|
||||
typedef struct OakNodeSequence OakNodeSequence;
|
||||
|
||||
/**
|
||||
* @brief Track types, matching olive::Track::Type.
|
||||
*/
|
||||
enum OakNodeTrackType {
|
||||
OAKNODE_TRACK_TYPE_NONE = -1,
|
||||
OAKNODE_TRACK_TYPE_VIDEO = 0,
|
||||
OAKNODE_TRACK_TYPE_AUDIO = 1,
|
||||
OAKNODE_TRACK_TYPE_SUBTITLE = 2,
|
||||
OAKNODE_TRACK_TYPE_COUNT = 3
|
||||
};
|
||||
|
||||
/* Re-declared here so track.h is self-contained; see node/node.h. */
|
||||
typedef struct OakNodeNode OakNodeNode;
|
||||
|
||||
/**
|
||||
* @brief Borrowed cast from a track handle to its node handle.
|
||||
* Empty handle for an empty handle.
|
||||
*/
|
||||
OakNodeNode oaknode_track_as_node(OakNodeTrack track);
|
||||
|
||||
/* ---------------------------------------------------------------- Track */
|
||||
|
||||
/**
|
||||
* @brief Create a track of the given type (OakNodeTrackType value).
|
||||
*
|
||||
* 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 with reference count 1; ctx is NULL on invalid
|
||||
* type / allocation failure.
|
||||
*/
|
||||
OakNodeTrack oaknode_track_create(int type);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
void oaknode_track_free(OakNodeTrack *track);
|
||||
|
||||
/**
|
||||
* @brief Track type (OakNodeTrackType values).
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Default / minimum track heights in pixels (static).
|
||||
*/
|
||||
int oaknode_track_get_default_height_in_pixels(void);
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Track reference as a (type, index) pair (olive::Track::Reference).
|
||||
*/
|
||||
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 *denominator);
|
||||
|
||||
/**
|
||||
* @brief Owning sequence as a borrowed handle (empty when trackless).
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Append/prepend/insert primitives (olive::Track::*_block).
|
||||
*
|
||||
* The track takes over graph membership of the block; the block must have
|
||||
* a valid length before insertion.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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 *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 denominator,
|
||||
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 denominator,
|
||||
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 out_num, int out_den, int *is_free);
|
||||
|
||||
/* ------------------------------------------------------------ TrackList */
|
||||
|
||||
/**
|
||||
* @brief Track list type (OakNodeTrackType values).
|
||||
*/
|
||||
/**
|
||||
* @brief Nearest block lookups (Track::nearest_block_before_or_at /
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Borrowed sequence owning this track list.
|
||||
*/
|
||||
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,
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
int oaknode_tracklist_get_type(OakNodeTrackList list, int *type);
|
||||
|
||||
/**
|
||||
* @brief Number of connected tracks.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Combined length of the longest track in the list.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Add `track` to the list (non-undoable primitive).
|
||||
*
|
||||
* Mirrors the graph steps of TimelineAddTrackCommand::redo() minus the
|
||||
* auto-merge: the track is parented to the list's graph (when any),
|
||||
* inherits the previous track's height, a new array slot is appended and
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief Remove `track` from the list (non-undoable primitive).
|
||||
*
|
||||
* Disconnects the track from its array slot and removes the slot
|
||||
* (Node::input_array_remove). The track is NOT deleted; ownership returns
|
||||
* to the caller.
|
||||
*
|
||||
* @return OAKNODE_OK, OAKNODE_E_INVALID or OAKNODE_E_NOT_FOUND.
|
||||
*/
|
||||
int oaknode_tracklist_remove_track(OakNodeTrackList list,
|
||||
OakNodeTrack track);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_TRACK_H
|
||||
@@ -0,0 +1,154 @@
|
||||
/***
|
||||
|
||||
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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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_TRAVERSER_H
|
||||
#define OAK_EDITOR_NODE_TRAVERSER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "node/error.h"
|
||||
#include "node/node.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file traverser.h
|
||||
* @brief C ABI for olive::NodeTraverser (src/node/src/traverser.h),
|
||||
* limited to database generation: generating the value database of a node
|
||||
* over a time range and enumerating its rows.
|
||||
*
|
||||
* The base NodeTraverser resolves no render jobs (textures/samples stay
|
||||
* dummy); only value-producing nodes are meaningful here.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
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 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 {
|
||||
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 with count 1; ctx is NULL on allocation
|
||||
* failure.
|
||||
*/
|
||||
OakNodeTraverser oaknode_traverser_init(void);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Generate the value database of `node` over the time range
|
||||
* [`in_num`/`in_den`, `out_num`/`out_den`) seconds
|
||||
* (NodeTraverser::generate_database()).
|
||||
*
|
||||
* `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,
|
||||
int64_t in_den, int64_t out_num,
|
||||
int64_t out_den,
|
||||
OakNodeValueDatabase *out_db);
|
||||
|
||||
/**
|
||||
* @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(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(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(OakNodeValueDatabase db,
|
||||
const char *key,
|
||||
int *out_count);
|
||||
|
||||
/**
|
||||
* @brief Read the value at `index` of row `key` mapped into `out`.
|
||||
* 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(OakNodeValueDatabase db,
|
||||
const char *key, int index,
|
||||
oaknode_value *out);
|
||||
|
||||
/**
|
||||
* @brief Read the value at `index` of row `key` as a string
|
||||
* (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(OakNodeValueDatabase db,
|
||||
const char *key, int index,
|
||||
char *buf, int buf_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_NODE_TRAVERSER_H
|
||||
Reference in New Issue
Block a user