refactor(node): de-Qt oaknode and wrap it in a pure C ABI

- copy engine/node (188 files) to src/node/src, de-Qt in waves:
  core infra (Node/Param/Value/Variant/mathtypes), project/serializer,
  block/output, color, effect leaves, generator, gizmo/plugins
- strip QObject/signals/slots: notifications move to the facade's
  oakengine_event channel, ownership becomes explicit (unique_ptr,
  add_keyframe/add_gizmo), sender() replaced by current_gizmo
- QVariant replaced by olive::Variant, Qt math types by POD mathtypes,
  QXmlStreamReader/Writer by oakcommon's expat-based classes
- sink VideoParams/SubtitleParams/LoopMode/ColorTransform to oakcommon
  (M3.5); polygon/text rasterization behind backend hooks
- pure C ABI in include/node + src/node/c_api (oaknode_ prefix,
  OAKNODE_E_* codes, undoable variants take OakUndoCommand out-params)
- fix Project::clear() root_ reset + disconnect assert, Sequence
  TrackList leak
- 96 gtest cases green in standalone build (build-oaknode)
- docs: signal/slot handling strategy + M3 implementation status
This commit is contained in:
2026-08-05 23:55:54 +08:00
parent c50017127b
commit d77348ad9f
375 changed files with 59682 additions and 1 deletions
+240
View File
@@ -0,0 +1,240 @@
/***
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 "node/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Opaque handle to a timeline block (olive::Block).
*
* Covers the whole Block family: ClipBlock, GapBlock and the concrete
* TransitionBlock subclasses. The handle IS the C++ object pointer; no
* wrapper is allocated. Concrete instances are created through the
* oaknode_block_*_create() factories below; callers never touch C++
* subclasses directly.
*/
typedef struct OakNodeBlock OakNodeBlock;
/**
* @brief Opaque 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 Concrete transition kinds for oaknode_block_transition_create().
*/
enum OakNodeTransitionKind {
OAKNODE_TRANSITION_CROSS_DISSOLVE = 0, /**< CrossDissolveTransition. */
OAKNODE_TRANSITION_DIP_TO_COLOR = 1 /**< DipToColorTransition. */
};
/**
* @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, or NULL on allocation failure.
*/
OakNodeBlock *oaknode_block_clip_create(void);
/**
* @brief Create a GapBlock. Ownership as oaknode_block_clip_create().
*
* @return Block handle, or 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, or NULL on invalid kind / allocation failure.
*/
OakNodeBlock *oaknode_block_transition_create(int kind);
/**
* @brief Destroy a block. No-op on NULL.
*
* 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);
/**
* @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 (NULL 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 OakCommonLoopMode 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 (NULL when
* unconnected).
*/
int oaknode_transition_get_connected_out_block(OakNodeBlock *transition,
OakNodeBlock **out);
int oaknode_transition_get_connected_in_block(OakNodeBlock *transition,
OakNodeBlock **out);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_BLOCK_H
+189
View File
@@ -0,0 +1,189 @@
/***
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 "common/colortransform.h"
#include "node/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Opaque handle to a color manager (olive::ColorManager).
*
* Unlike node handles this one IS a wrapper allocation (ColorManager is
* not a Node); release with oaknode_colormanager_free().
*/
typedef struct OakNodeColorManager OakNodeColorManager;
/**
* @brief Opaque borrowed handle to a project (olive::Project).
*
* Owned by the project family; re-declared here so this header is
* self-contained.
*/
typedef struct OakNodeProject OakNodeProject;
/**
* @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, or NULL on NULL project / allocation failure.
*/
OakNodeColorManager *oaknode_colormanager_init(OakNodeProject *project);
/**
* @brief Destroy a color manager. No-op on NULL.
*/
void oaknode_colormanager_free(OakNodeColorManager *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 handle owned by the caller (release with
* oakcommon_colortransform_free()). Requires a loaded config
* (OAKNODE_E_STATE otherwise).
*/
int oaknode_colormanager_get_compliant_color_transform(
OakNodeColorManager *manager, const OakCommonColorTransform *transform,
int force_display, OakCommonColorTransform **out);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_COLORMANAGER_H
+39
View File
@@ -0,0 +1,39 @@
/***
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.
*/
#define OAKNODE_OK 0 /**< Success. */
#define OAKNODE_E_INVALID (-1) /**< NULL handle or invalid argument. */
#define OAKNODE_E_STATE (-2) /**< Call not valid in the current state. */
#define OAKNODE_E_FAILED (-3) /**< The underlying operation failed. */
#define OAKNODE_E_NOT_FOUND (-4) /**< Index out of range / entry not found. */
#define OAKNODE_E_NOMEM (-5) /**< Allocation failed. */
#endif //OAK_EDITOR_NODE_ERROR_H
+96
View File
@@ -0,0 +1,96 @@
/***
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. Prototype nodes
* from oaknode_factory_node_at() are owned by the library: read-only
* metadata queries only, never free them or add them to a graph.
*/
/**
* @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
* and must release it with oaknode_node_free() while it is still
* orphaned. Returns 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.
* 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
+123
View File
@@ -0,0 +1,123 @@
/***
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 "node/error.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 Create a folder node owned by `project`.
*
* The folder is added to the project's graph (Project::add_node()) but is
* NOT attached under any parent folder; use oaknode_folder_add_child() to
* place it. The handle is borrowed: the project owns the folder.
*
* @return Folder handle, or NULL on failure.
*/
OakNodeFolder *oaknode_folder_create(OakNodeProject *project);
/**
* @brief Number of direct item children (Folder::item_child_count()).
* Negative OAKNODE_E_* code on NULL.
*/
int oaknode_folder_child_count(const OakNodeFolder *folder);
/**
* @brief Borrowed node handle of the item child at `index`
* (Folder::item_child()). NULL when out of range.
*/
OakNodeNode *oaknode_folder_child_at(const OakNodeFolder *folder, int index);
/**
* @brief Add `child` as a direct item child of `folder` (live, non-undoable;
* executes FolderAddChild::redo()).
*
* @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 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`. Nodes already directly inside `dest_folder` are skipped.
*
* @return OAKNODE_OK or a negative OAKNODE_E_* error code.
*/
int oaknode_folder_move_children(OakNodeNode *const *nodes, int count,
OakNodeFolder *dest_folder);
/**
* @brief 1 if `folder` recursively contains `child`, 0 otherwise
* (Folder::has_child_recursive()). Negative OAKNODE_E_* code on NULL args.
*/
int oaknode_folder_has_child_recursive(const OakNodeFolder *folder,
const OakNodeNode *child);
/**
* @brief Index of `child` in `folder`'s direct children
* (Folder::index_of_child()).
*
* @return The index, OAKNODE_E_NOT_FOUND if not a direct child, or
* OAKNODE_E_INVALID on NULL args.
*/
int oaknode_folder_index_of_child(const OakNodeFolder *folder,
const OakNodeNode *child);
/**
* @brief Borrowed handle of the folder a node currently belongs to
* (Node::folder()), or NULL if the node is not in any folder.
*/
OakNodeFolder *oaknode_folder_parent_of(const OakNodeNode *node);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_FOLDER_H
+199
View File
@@ -0,0 +1,199 @@
/***
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 "node/error.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 Opaque footage handle. Borrowed from the owning project.
*/
typedef struct OakNodeFootage OakNodeFootage;
/**
* @brief Create a footage node owned by `project` (added to the project's
* graph, not attached to any folder).
*
* @param filename Initial media path, may be NULL/empty.
*
* @return Footage handle, or NULL on failure.
*/
OakNodeFootage *oaknode_footage_create(OakNodeProject *project,
const char *filename);
/**
* @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(const 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 NULL.
*/
int oaknode_footage_is_valid(const 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(const 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(const OakNodeFootage *footage, char *buf,
int buf_size);
/**
* @brief Total number of streams (Footage::get_total_stream_count()).
* Negative OAKNODE_E_* code on NULL.
*/
int oaknode_footage_total_stream_count(const OakNodeFootage *footage);
/**
* @brief Number of video streams (ViewerOutput::get_video_stream_count()).
* Negative OAKNODE_E_* code on NULL.
*/
int oaknode_footage_video_stream_count(const OakNodeFootage *footage);
/**
* @brief Number of audio streams (ViewerOutput::get_audio_stream_count()).
* Negative OAKNODE_E_* code on NULL.
*/
int oaknode_footage_audio_stream_count(const OakNodeFootage *footage);
/**
* @brief Number of subtitle streams (ViewerOutput::get_subtitle_stream_count()).
* Negative OAKNODE_E_* code on NULL.
*/
int oaknode_footage_subtitle_stream_count(const 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(const OakNodeFootage *footage,
int *out_numerator, int *out_denominator);
/**
* @brief 1 if proxy playback is enabled (Footage::proxy_enabled()).
* Negative OAKNODE_E_* code on NULL.
*/
int oaknode_footage_proxy_enabled(const 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(const OakNodeFootage *footage, char *buf,
int buf_size);
/**
* @brief Proxy state enum value (Footage::proxy_state():
* ProxyManager::ProxyState). Negative OAKNODE_E_* code on NULL.
*/
int oaknode_footage_proxy_state(const 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);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_FOOTAGE_H
+163
View File
@@ -0,0 +1,163 @@
/***
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 "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 is a reinterpreted olive::NodeGroup (a Node subclass);
* group handles borrow the same lifetime rules as OakNodeNode.
*/
/**
* @brief Opaque group handle (olive::NodeGroup).
*/
typedef struct OakNodeGroup OakNodeGroup;
/**
* @brief Create a standalone NodeGroup (owned; release with
* oaknode_node_free() on the OakNodeNode view or oaknode_group_free()
* while still orphaned).
*
* @return Group handle, or NULL on allocation failure.
*/
OakNodeGroup *oaknode_group_create(void);
/**
* @brief Borrow a group view of a node, or NULL when the node is not a
* NodeGroup (dynamic_cast).
*/
OakNodeGroup *oaknode_group_cast(OakNodeNode *node);
/**
* @brief Destroy an OWNED group (same rules as oaknode_node_free()).
* NULL is a no-op.
*/
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(const OakNodeGroup *group, int *out_count);
/**
* @brief The passthrough id at `index`. Two-stage getter;
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_group_passthrough_id_at(const OakNodeGroup *group, int index,
char *buf, int buf_size);
/**
* @brief The inner input behind passthrough `index`: node (borrowed
* handle), input id (two-stage string) and element.
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_group_passthrough_input_at(const OakNodeGroup *group, int index,
OakNodeNode **out_node, char *buf,
int buf_size, int *out_element);
/**
* @brief The output passthrough node (borrowed handle), or NULL when
* unset. OAKNODE_OK is returned either way.
*/
int oaknode_group_get_output_passthrough(const OakNodeGroup *group,
OakNodeNode **out_node);
/**
* @brief Set the output passthrough node directly (live).
*/
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; 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
+226
View File
@@ -0,0 +1,226 @@
/***
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 is a reinterpreted olive::NodeKeyframe. Handles
* created by oaknode_keyframe_create() are owned and must be released
* with oaknode_keyframe_free(); keyframes attached to a node input's
* track are owned by the node.
*
* 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 Opaque keyframe handle (olive::NodeKeyframe).
*/
typedef struct OakNodeKeyframe 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 NULL.
*
* @return Keyframe handle, or 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 Destroy an OWNED keyframe. NULL is a no-op. 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(const 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(const 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(const 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(const 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(const 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(const OakNodeKeyframe *keyframe,
int *out_track);
/**
* @brief The keyframe's element index.
*/
int oaknode_keyframe_get_element(const OakNodeKeyframe *keyframe,
int *out_element);
/**
* @brief The id of the input this keyframe belongs to. Two-stage getter.
*/
int oaknode_keyframe_get_input(const OakNodeKeyframe *keyframe, char *buf,
int buf_size);
/**
* @brief The node this keyframe belongs to (borrowed handle), or NULL
* when orphaned. OAKNODE_OK either way.
*/
int oaknode_keyframe_get_parent(const OakNodeKeyframe *keyframe,
OakNodeNode **out_node);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_KEYFRAME_H
+487
View File
@@ -0,0 +1,487 @@
/***
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 "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).
*
* The handle IS the C++ object pointer (M3 handle convention 3): an
* OakNodeNode is a reinterpreted olive::Node, no wrapper allocation.
* Handles borrowed from a graph become invalid when the owning project or
* node is destroyed. Owned handles (from oaknode_factory_create_from_id()
* or oaknode_node_create_copy()) must be released with
* oaknode_node_free() while still orphaned; once a node lives in a project
* graph its lifetime belongs to the graph.
*
* 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 Opaque node handle (olive::Node).
*/
typedef struct OakNodeNode OakNodeNode;
/**
* @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(const OakNodeNode *node, char *buf, int buf_size);
/**
* @brief The node's display name (Node::name()). Two-stage getter,
* same return convention as oaknode_node_get_id().
*/
int oaknode_node_get_name(const OakNodeNode *node, char *buf, int buf_size);
/**
* @brief The node's user label (Node::get_label()). Two-stage getter,
* same return convention as oaknode_node_get_id().
*/
int oaknode_node_get_label(const OakNodeNode *node, char *buf, int buf_size);
/**
* @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(const 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(const 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(const OakNodeNode *node, int *out_count);
/**
* @brief The input id at `index` (Node::inputs()). Two-stage getter;
* returns OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_input_id(const OakNodeNode *node, int index, char *buf,
int 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(const OakNodeNode *node, const char *input_id,
int *out_type);
/**
* @brief 1 if the input currently has a connected edge
* (Node::is_input_connected()). OAKNODE_E_NOT_FOUND for an unknown id.
*/
int oaknode_node_input_is_connected(const OakNodeNode *node,
const char *input_id, int *out_value);
/**
* @brief 1 if the input accepts connections (Node::is_input_connectable()).
* OAKNODE_E_NOT_FOUND for an unknown id.
*/
int oaknode_node_input_is_connectable(const OakNodeNode *node,
const char *input_id, int *out_value);
/**
* @brief The human-readable name of the input (Node::get_input_name()).
* Two-stage getter; OAKNODE_E_NOT_FOUND for an unknown id.
*/
int oaknode_node_get_input_name(const OakNodeNode *node, const char *input_id,
char *buf, int buf_size);
/**
* @brief The node feeding this input, or NULL when not connected
* (Node::get_connected_output(), element -1). `out_node` receives a
* borrowed handle. OAKNODE_E_NOT_FOUND for an unknown input id.
*/
int oaknode_node_input_get_connected_node(const 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(const 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(const 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(const OakNodeNode *node,
int *out_count);
/**
* @brief The node at the input end of outgoing edge `index`
* (borrowed handle). OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_output_connection_node_at(const OakNodeNode *node, int index,
OakNodeNode **out_node);
/**
* @brief The input id at the input end of outgoing edge `index`.
* Two-stage getter; OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_output_connection_input_id_at(const OakNodeNode *node,
int index, char *buf,
int buf_size);
/**
* @brief The input element at the input end of outgoing edge `index`
* (-1 for non-array inputs). OAKNODE_E_NOT_FOUND for an out-of-range
* index.
*/
int oaknode_node_output_connection_element_at(const OakNodeNode *node,
int index, int *out_element);
/* ---- 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(const OakNodeNode *a, const OakNodeNode *b,
int *out_value);
/**
* @brief Number of linked nodes (Node::links()).
*/
int oaknode_node_link_count(const OakNodeNode *node, int *out_count);
/**
* @brief The linked node at `index` (borrowed handle).
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_link_at(const OakNodeNode *node, int index,
OakNodeNode **out_node);
/* ---- Context positions ---------------------------------------------------- */
/**
* @brief Number of context entries (Node::get_context_positions()).
*/
int oaknode_node_context_count(const OakNodeNode *node, int *out_count);
/**
* @brief The context node at `index` (borrowed handle).
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_node_context_node_at(const OakNodeNode *node, int index,
OakNodeNode **out_node);
/**
* @brief The node's position in `context` (any out pointer may be NULL).
* OAKNODE_E_NOT_FOUND when the context does not contain this node.
*/
int oaknode_node_get_context_position(const OakNodeNode *node,
OakNodeNode *context, double *out_x,
double *out_y, int *out_expanded);
/**
* @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 caller owns it and must release it with
* oaknode_node_free() while it is still orphaned. Returns NULL for NULL.
*/
OakNodeNode *oaknode_node_create_copy(const OakNodeNode *node);
/**
* @brief Destroy an OWNED node immediately (C++ delete). NULL is a no-op.
*
* ONLY valid for owned handles that were never added to a graph: the
* products of oaknode_factory_create_from_id(),
* oaknode_node_create_copy() and oaknode_group_create() while still
* orphaned. Freeing a graph-owned node, or freeing twice, is a
* use-after-free.
*/
void oaknode_node_free(OakNodeNode *node);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_NODE_H
+237
View File
@@ -0,0 +1,237 @@
/***
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 "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 by oaknode_project_free(). Handles to nodes,
* folders and footage obtained from a project are borrowed views and must not
* be freed.
*
* 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.
* - NULL handles yield OAKNODE_E_INVALID (or a no-op for free()).
* - Disk save/load of project files is NOT part of this layer; it belongs to
* oakstorage (milestone M10).
*/
/**
* @brief Opaque project handle. Owned by the caller; release with
* oaknode_project_free().
*/
typedef struct OakNodeProject OakNodeProject;
/**
* @brief Opaque node handle (defined by the node family; forward-declared
* here so the headers can be included in any order).
*/
typedef struct OakNodeNode OakNodeNode;
/**
* @brief Opaque folder handle (defined in node/folder.h; forward-declared
* here so the headers can be included in any order). Borrowed from the
* owning project.
*/
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, or NULL on allocation failure.
*/
OakNodeProject *oaknode_project_init(void);
/**
* @brief Destroy a project and every node it owns. NULL is a no-op.
*/
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()).
*
* 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(const OakNodeProject *project, char *buf, int buf_size);
/**
* @brief Full path the project was saved as, or "" if untitled
* (Project::filename()). Two-stage string getter.
*/
int oaknode_project_filename(const OakNodeProject *project, char *buf,
int buf_size);
/**
* @brief Display name safe for window titles (Project::pretty_filename()).
* Two-stage string getter.
*/
int oaknode_project_pretty_filename(const OakNodeProject *project, char *buf,
int 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 NULL.
*/
int oaknode_project_is_modified(const 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 NULL.
*/
int oaknode_project_is_new(const OakNodeProject *project);
/**
* @brief Effective cache directory (Project::cache_path(), honoring the cache
* location setting). Two-stage string getter.
*/
int oaknode_project_cache_path(const OakNodeProject *project, char *buf,
int buf_size);
/**
* @brief Cache location setting enum value
* (Project::get_cache_location_setting(): 0 = default location,
* 1 = alongside project, 2 = custom path). Negative OAKNODE_E_* code on NULL.
*/
int oaknode_project_get_cache_location_setting(const 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(const 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(const OakNodeProject *project, char *buf,
int buf_size);
/**
* @brief Add a node to the graph; the project takes ownership
* (Project::add_node()).
*
* @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 NULL.
*/
int oaknode_project_node_count(const OakNodeProject *project);
/**
* @brief Borrowed handle of the graph node at `index`, or NULL when out of
* range.
*/
OakNodeNode *oaknode_project_node_at(const OakNodeProject *project, int index);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_PROJECT_H
+163
View File
@@ -0,0 +1,163 @@
/***
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 "common/videoparams.h"
#include "node/error.h"
#include "olive/core/oakcore/audioparams.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Opaque handle to a sequence (olive::Sequence).
*
* The handle IS the C++ object pointer; no wrapper is allocated.
*/
typedef struct OakNodeSequence OakNodeSequence;
/**
* @brief Opaque handle to a track list (olive::TrackList), see node/track.h.
*/
typedef struct OakNodeTrackList OakNodeTrackList;
/**
* @brief Opaque handle to a track (olive::Track), see node/track.h.
*/
typedef struct OakNodeTrack OakNodeTrack;
/**
* @brief Create an empty sequence with zero tracks.
*
* @return Sequence handle, or NULL on allocation failure.
*/
OakNodeSequence *oaknode_sequence_create(void);
/**
* @brief Destroy a sequence and its track lists. No-op on NULL.
*
* 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 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 handle owned by the caller
* (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,
OakCommonVideoParams **out);
/**
* @brief Replace the video parameters at `index` with a copy of `params`.
*/
int oaknode_sequence_set_video_params(OakNodeSequence *sequence, int index,
const OakCommonVideoParams *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
+244
View File
@@ -0,0 +1,244 @@
/***
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 "node/error.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 Opaque save descriptor (wraps ProjectSerializer::SaveData).
* Owned by the caller; release with oaknode_serializer_savedata_free().
*/
typedef struct OakNodeSerializerSaveData OakNodeSerializerSaveData;
/**
* @brief Opaque load result (wraps ProjectSerializer::LoadData).
* Owned by the caller; release with oaknode_serializer_loaddata_free().
* Node handles obtained from it are borrowed from the target project.
*/
typedef struct OakNodeSerializerLoadData 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 NULL for load types
* that do not require it.
*
* @return Save-data handle, or NULL on failure.
*/
OakNodeSerializerSaveData *oaknode_serializer_savedata_create(
int load_type, OakNodeProject *project);
/**
* @brief Destroy a save descriptor. NULL is a no-op.
*/
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, OakNodeNode *const *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 NULL for load types that
* do not attach nodes to a project.
* @param xml Complete XML document text. Must not be NULL.
* @param load_type One of OAKNODE_SERIALIZER_LOAD_*.
* @param out_result Receives one of the OAKNODE_SERIALIZER_* result codes.
* Must not be NULL.
* @param out_load_data Receives the load result on OAKNODE_SERIALIZER_OK
* (caller-owned, may be NULL if the caller does not need it;
* receives NULL on failure).
* @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 Destroy a load result. NULL is 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
* NULL.
*/
int oaknode_serializer_loaddata_node_count(
const OakNodeSerializerLoadData *load_data);
/**
* @brief Borrowed handle of the loaded node at `index`, or NULL when out of
* range.
*/
OakNodeNode *oaknode_serializer_loaddata_node_at(
const 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(
const OakNodeSerializerLoadData *load_data, OakNodeNode *node,
const char *key, char *buf, int buf_size);
/**
* @brief Number of promised (deferred) connections in the load result.
* Negative OAKNODE_E_* code on NULL.
*/
int oaknode_serializer_loaddata_connection_count(
const 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.
* @param out_input_node Receives the input (destination) node.
* @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(
const 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
+279
View File
@@ -0,0 +1,279 @@
/***
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 "node/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Opaque handle to a track (olive::Track).
*
* The handle IS the C++ object pointer; no wrapper is allocated.
*/
typedef struct OakNodeTrack OakNodeTrack;
/**
* @brief Opaque handle to a per-type track container (olive::TrackList).
*
* Borrowed from oaknode_sequence_get_track_list(); invalidated when the
* owning sequence is destroyed.
*/
typedef struct OakNodeTrackList OakNodeTrackList;
/**
* @brief Opaque handle to a block (olive::Block), see node/block.h.
*/
typedef struct OakNodeBlock OakNodeBlock;
/**
* @brief Opaque handle to a sequence (olive::Sequence), see node/sequence.h.
*/
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
};
/* ---------------------------------------------------------------- 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, or NULL on invalid type / allocation failure.
*/
OakNodeTrack *oaknode_track_create(int type);
/**
* @brief Destroy a track. No-op on NULL.
*
* 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 (NULL 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).
*/
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.
*
* @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
+130
View File
@@ -0,0 +1,130 @@
/***
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 Opaque traverser handle (olive::NodeTraverser).
*/
typedef struct OakNodeTraverser OakNodeTraverser;
/**
* @brief Opaque value-database handle (an owned copy of an
* olive::NodeValueDatabase). Release with
* oaknode_traverser_database_free().
*/
typedef struct OakNodeValueDatabase OakNodeValueDatabase;
/**
* @brief Create a traverser.
*
* @return Traverser handle, or NULL on allocation failure.
*/
OakNodeTraverser *oaknode_traverser_init(void);
/**
* @brief Destroy a traverser. NULL is a no-op.
*/
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.
*
* @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 Destroy a database handle. NULL is a no-op.
*/
void oaknode_traverser_database_free(OakNodeValueDatabase *db);
/**
* @brief Number of rows (input tables) in the database.
*/
int oaknode_traverser_database_row_count(const OakNodeValueDatabase *db,
int *out_count);
/**
* @brief The input id (key) of the row at `index`. Two-stage getter;
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_traverser_database_row_key_at(const OakNodeValueDatabase *db,
int index, char *buf, int buf_size);
/**
* @brief Number of values in the row named `key`.
* OAKNODE_E_NOT_FOUND for an unknown key.
*/
int oaknode_traverser_database_row_value_count(const OakNodeValueDatabase *db,
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(const 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(const OakNodeValueDatabase *db,
const char *key, int index,
char *buf, int buf_size);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_NODE_TRAVERSER_H