engine: complete C ABI facade (liboakengine oakengine_* surface)

The full pure-C facade used by the app: node/project/timeline/viewer/
undo/task/events/serializer/playback/preview/renderer/gizmo/color/
audio/footage/proxy/encoding/exporter/config/disk/ipc/plugin/worker
families, plus undo-group semantics, display renderer handles,
NodeFactory accessors, and per-family pure-C engine tests.
This commit is contained in:
2026-07-26 22:43:00 +08:00
parent c486c853ff
commit e9f173916f
80 changed files with 28715 additions and 126 deletions
+463
View File
@@ -0,0 +1,463 @@
/***
Oak - 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 OAKENGINE_APP_H
#define OAKENGINE_APP_H
#include "export.h"
#include "footage.h"
#include "init.h"
#include "project.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file app.h
* @brief C ABI for the application-level engine state (EngineCore facade)
*
* This family exposes the process-wide application state the editor UI needs
* from the engine: the CoreParams-driven startup, the open/active project,
* the recent-projects list, the global tool/snapping/timecode settings, the
* status bar and the UI handler hooks the engine calls when it needs user
* interaction (image-sequence confirmation, footage relink, project save /
* close, main window layout restore, OTIO import).
*
* It wraps olive::EngineCore so that the UI layer no longer derives from or
* links against that C++ class. The engine emits its change notifications
* through the OakEngineAppCallbacks function pointers (registered with
* oakengine_app_set_callbacks()) instead of Qt signals.
*
* Conventions (matching oakengine/project.h):
* - Booleans are int (1/0).
* - Return codes: 0 (OAKENGINE_OK) on success, negative OAKENGINE_E_* on
* failure. Functions documented as returning a value return
* OAKENGINE_E_INVALID when no application core exists.
* - String output uses the buf/size convention: the return value is the
* number of characters that would have been written excluding the NUL,
* so buf == NULL or a short buffer queries the required size. The output
* is NUL-terminated whenever buf_size > 0. A negative return value is an
* OAKENGINE_E_* error code.
* - Enum values mirror the engine enums (olive::Tool::Item,
* olive::Tool::AddableObject, olive::core::Timecode::Display) and are
* passed as plain int; the numeric values are identical.
*/
/**
* @brief Application run modes (mirrors olive::EngineCore::CoreParams::RunMode).
*/
#define OAKENGINE_APP_RUN_NORMAL 0 /**< Normal GUI run. */
#define OAKENGINE_APP_RUN_HEADLESS_EXPORT 1 /**< Export without GUI. */
#define OAKENGINE_APP_RUN_HEADLESS_PRE_CACHE 2 /**< Pre-cache without GUI. */
/**
* @brief Startup parameters for oakengine_app_create().
*
* Strings may be NULL (treated as empty). The struct is copied by
* oakengine_app_create(); the pointed-to strings are only read during the
* call.
*/
typedef struct OakEngineAppParams {
int run_mode; /**< OAKENGINE_APP_RUN_* value. */
int fullscreen; /**< Start the main window fullscreen (bool). */
const char *startup_project; /**< Project file to open on startup, or NULL. */
const char *startup_language; /**< .qm file overriding the language, or NULL. */
int crash_on_startup; /**< Trigger a manual crash shortly after start (bool). */
} OakEngineAppParams;
/**
* @brief UI handler and notification callback set.
*
* Any field may be NULL. A NULL handler makes the engine fall back to its
* headless default (accept the import, close without prompting, skip the
* file write); a NULL notification simply drops the event.
*
* The callbacks are invoked synchronously on the thread that triggered the
* engine call (usually the main thread). `userdata` is passed back verbatim.
*
* `load_layout` receives a `const olive::SerializedLayoutInfo *` (engine
* data structure, only valid during the call). `otio_import` receives an
* array of borrowed olive::Sequence pointers as OakEngineSequence handles.
*/
typedef struct OakEngineAppCallbacks {
void *userdata;
/* UI handlers (engine asks the application) */
int (*confirm_image_sequence)(const char *filename, void *userdata);
int (*relink_footage)(OakEngineFootage **footage, int count,
void *userdata);
void (*save_project)(const char *override_filename, void *userdata);
int (*close_project)(void *userdata);
void (*load_layout)(const void *layout, void *userdata);
int (*otio_import)(OakEngineSequence **sequences, int count,
void *userdata);
/* Notifications (engine informs the application) */
void (*status_message_show)(const char *message, int timeout,
void *userdata);
void (*status_message_clear)(void *userdata);
void (*cache_full_warning)(void *userdata);
void (*active_project_changed)(OakEngineProject *project, void *userdata);
void (*tool_changed)(int tool, void *userdata);
void (*addable_object_changed)(int object, void *userdata);
void (*snapping_changed)(int snapping, void *userdata);
void (*timecode_display_changed)(int display, void *userdata);
void (*open_recent_list_changed)(void *userdata);
void (*color_picker_enabled)(int enabled, void *userdata);
} OakEngineAppCallbacks;
/**
* @brief Create the application engine core with the given startup params.
*
* `params` may be NULL for defaults (normal run, no startup project). Only
* one application core may exist per process: if one already exists (either
* from an earlier oakengine_app_create() or from the EngineCore shell that
* oakengine_init() creates), OAKENGINE_E_STATE is returned.
*
* The core is never destroyed; it backs the process-wide engine singleton.
*
* @return OAKENGINE_OK on success, OAKENGINE_E_STATE if a core exists.
*/
OAKENGINE_API int oakengine_app_create(const OakEngineAppParams *params);
/**
* @brief Start the engine services for the application (config, locale,
* managers, autorecovery timer, recent projects list).
*
* @return OAKENGINE_OK on success, OAKENGINE_E_STATE if no core exists or
* the application core was already started.
*/
OAKENGINE_API int oakengine_app_start(void);
/**
* @brief Stop the engine services started by oakengine_app_start().
*
* @return OAKENGINE_OK on success, OAKENGINE_E_STATE if the application
* core was not started.
*/
OAKENGINE_API int oakengine_app_stop(void);
/**
* @brief Register the UI handler/notification callback set.
*
* The struct is copied; NULL clears all callbacks and restores the headless
* default behavior.
*
* @return OAKENGINE_OK.
*/
OAKENGINE_API int
oakengine_app_set_callbacks(const OakEngineAppCallbacks *callbacks);
/**
* @brief Startup parameter accessors (valid once a core exists).
*
* oakengine_app_run_mode() returns an OAKENGINE_APP_RUN_* value,
* oakengine_app_fullscreen() a boolean; both return OAKENGINE_E_INVALID when
* no core exists. oakengine_app_startup_project() uses the buf/size
* convention.
*/
OAKENGINE_API int oakengine_app_run_mode(void);
OAKENGINE_API int oakengine_app_fullscreen(void);
OAKENGINE_API int oakengine_app_startup_project(char *buf, int buf_size);
/**
* @brief Process-wide undo stack as an opaque pointer (an
* olive::UndoStack *). Returns NULL when no core exists.
*/
OAKENGINE_API void *oakengine_app_undo_stack(void);
/**
* @brief Current tool as an olive::Tool::Item value (int).
*/
OAKENGINE_API int oakengine_app_tool(void);
/**
* @brief Set the current tool. Valid values are 0 <= tool < Tool::k_count.
* Emits the tool_changed notification.
*/
OAKENGINE_API int oakengine_app_set_tool(int tool);
/**
* @brief Currently selected addable object (olive::Tool::AddableObject).
*/
OAKENGINE_API int oakengine_app_addable_object(void);
/**
* @brief Set the addable object. Valid values are 0 <= object <
* Tool::k_addable_count. Emits addable_object_changed.
*/
OAKENGINE_API int oakengine_app_set_addable_object(int object);
/**
* @brief Currently selected transition id (buf/size convention).
*/
OAKENGINE_API int oakengine_app_selected_transition(char *buf, int buf_size);
/**
* @brief Set the selected transition id (NULL clears it).
*/
OAKENGINE_API int oakengine_app_set_selected_transition(const char *id);
/**
* @brief Current snapping setting (boolean).
*/
OAKENGINE_API int oakengine_app_snapping(void);
/**
* @brief Set snapping. Emits snapping_changed.
*/
OAKENGINE_API int oakengine_app_set_snapping(int enabled);
/**
* @brief Current timecode display mode (olive::core::Timecode::Display).
*/
OAKENGINE_API int oakengine_app_timecode_display(void);
/**
* @brief Set the timecode display mode (0 <= display <= 4). Emits
* timecode_display_changed.
*/
OAKENGINE_API int oakengine_app_set_timecode_display(int display);
/**
* @brief Number of entries in the recently opened/saved projects list.
*/
OAKENGINE_API int oakengine_app_recent_projects_count(void);
/**
* @brief Path of the recent-project entry at `index` (buf/size convention).
*
* @return the string length, or OAKENGINE_E_NOT_FOUND for an invalid index.
*/
OAKENGINE_API int oakengine_app_recent_project_at(int index, char *buf,
int buf_size);
/**
* @brief Remove the recent-project entry at `index`. Emits
* open_recent_list_changed.
*
* @return OAKENGINE_OK, or OAKENGINE_E_NOT_FOUND for an invalid index.
*/
OAKENGINE_API int oakengine_app_remove_recent_project(int index);
/**
* @brief Clear the recent projects list. Emits open_recent_list_changed.
*/
OAKENGINE_API int oakengine_app_clear_recent_projects(void);
/**
* @brief Show a message in the status bar (delivered through the
* status_message_show callback).
*/
OAKENGINE_API int oakengine_app_show_status_message(const char *message,
int timeout);
/**
* @brief Clear the status bar (delivered through the status_message_clear
* callback).
*/
OAKENGINE_API int oakengine_app_clear_status_message(void);
/**
* @brief Change the current language.
*
* @return 1 if a translation for `locale` was found and installed, 0 if
* not, OAKENGINE_E_INVALID for NULL or when no core exists.
*/
OAKENGINE_API int oakengine_app_set_language(const char *locale);
/**
* @brief Set how frequently an autorecovery is saved (minutes).
*/
OAKENGINE_API int oakengine_app_set_autorecovery_interval(int minutes);
/**
* @brief Globally enable/disable decoding from proxy media.
*/
OAKENGINE_API int oakengine_app_set_use_proxy_media(int enabled);
/**
* @brief Add/remove a pixel-sampling user. Emits color_picker_enabled when
* the user count crosses 0.
*/
OAKENGINE_API int oakengine_app_request_pixel_sampling(int enable);
/**
* @brief Debug "magic" flag accessors.
*/
OAKENGINE_API int oakengine_app_set_magic(int enabled);
OAKENGINE_API int oakengine_app_is_magic_enabled(void);
/**
* @brief Copy a string to the system clipboard.
*/
OAKENGINE_API int oakengine_app_copy_to_clipboard(const char *text);
/**
* @brief Paste a string from the system clipboard (buf/size convention).
*/
OAKENGINE_API int oakengine_app_paste_from_clipboard(char *buf, int buf_size);
/**
* @brief File filter for footage import dialogs (buf/size convention).
*/
OAKENGINE_API int oakengine_app_footage_file_dialog_filter(char *buf,
int buf_size);
/**
* @brief Whether `path` has an extension allowed for footage import.
*
* @return 1/0, or OAKENGINE_E_INVALID for NULL.
*/
OAKENGINE_API int oakengine_app_is_footage_extension_allowed(const char *path);
/**
* @brief Create a new sequence named appropriately for `project`.
*
* `name_format` is a QString::arg() pattern (e.g. "Sequence %1"); NULL uses
* the default "Sequence %1". The returned handle is owned by the caller
* (it is not yet added to the project). Returns NULL on invalid input.
*/
OAKENGINE_API OakEngineSequence *
oakengine_app_create_sequence(OakEngineProject *project,
const char *name_format);
/**
* @brief Path of the autorecovery index file (buf/size convention).
*/
OAKENGINE_API int oakengine_app_auto_recovery_index_filename(char *buf,
int buf_size);
/**
* @brief Currently open project (borrowed handle, may be NULL).
*/
OAKENGINE_API OakEngineProject *oakengine_app_open_project(void);
/**
* @brief Close the current project (through the close_project handler) and
* open a new empty one.
*/
OAKENGINE_API int oakengine_app_create_new_project(void);
/**
* @brief Open an already-loaded project, closing the current one first.
* Pushes it to the recent list when `add_to_recents` is set and the project
* has a filename.
*/
OAKENGINE_API int oakengine_app_add_open_project(OakEngineProject *project,
int add_to_recents);
/**
* @brief Adopt the project loaded by a project-load task (an olive::Task *
* as an opaque pointer).
*
* @return 1 if the project was opened, 0 if the load was cancelled or the
* footage validation was rejected, OAKENGINE_E_INVALID for NULL.
*/
OAKENGINE_API int oakengine_app_add_open_project_from_task(void *task,
int add_to_recents);
/**
* @brief Adopt an autorecovery project loaded by a project-load task (an
* olive::Task * as an opaque pointer).
*
* @return 1 on success, 0 otherwise, OAKENGINE_E_INVALID for NULL.
*/
OAKENGINE_API int oakengine_app_add_recovery_project_from_task(void *task);
/**
* @brief Update engine state after `project` was successfully saved (recent
* list, modified flag, unrecovered list).
*/
OAKENGINE_API int oakengine_app_on_project_saved(OakEngineProject *project);
/**
* @brief Set the active (open) project. Emits active_project_changed.
* `project` may be NULL.
*/
OAKENGINE_API int oakengine_app_set_active_project(OakEngineProject *project);
/**
* @brief Convenience wrapper: set just the confirm-image-sequence handler
* (same as setting cb.confirm_image_sequence in oakengine_app_set_callbacks).
* Replaces both fn and userdata.
*/
OAKENGINE_API int oakengine_app_set_confirm_image_sequence_handler(
int (*fn)(const char *filename, void *userdata), void *userdata);
/**
* @brief Convenience wrapper: set just the relink handler.
*/
OAKENGINE_API int oakengine_app_set_relink_handler(
int (*fn)(OakEngineFootage **footage, int count, void *userdata),
void *userdata);
/**
* @brief Convenience wrapper: set just the save-project handler.
*/
OAKENGINE_API int oakengine_app_set_save_project_handler(
void (*fn)(const char *override_filename, void *userdata), void *userdata);
/**
* @brief Convenience wrapper: set just the close-project handler.
*/
OAKENGINE_API int oakengine_app_set_close_project_handler(
int (*fn)(void *userdata), void *userdata);
/**
* @brief Convenience wrapper: set just the load-layout handler.
*/
OAKENGINE_API int oakengine_app_set_load_layout_handler(
void (*fn)(const void *layout, void *userdata), void *userdata);
/**
* @brief Alias for oakengine_app_auto_recovery_index_filename().
*/
OAKENGINE_API int oakengine_app_get_auto_recovery_index_filename(char *buf,
int buf_size);
/**
* @brief Alias for oakengine_app_remove_recent_project().
*/
OAKENGINE_API int oakengine_app_remove_recently_opened_project(int index);
/**
* @brief void*-based overload of oakengine_app_on_project_saved() for use
* from app code that holds a opaque QObject pointer.
*/
OAKENGINE_API int oakengine_app_on_project_saved_vp(void *project);
/**
* @brief void*-based overload of oakengine_app_set_active_project().
*/
OAKENGINE_API int oakengine_app_set_active_project_vp(void *project);
/**
* @brief void*-based overload of oakengine_app_add_open_project().
*/
OAKENGINE_API int oakengine_app_add_open_project_vp(void *project,
int add_to_recents);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_APP_H */
+330
View File
@@ -0,0 +1,330 @@
/***
Oak - 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 OAKENGINE_AUDIO_H
#define OAKENGINE_AUDIO_H
#include <stdint.h>
#include "export.h"
#include "encoding.h"
#include "init.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file audio.h
* @brief C ABI for the engine's audio I/O singleton (olive::AudioManager)
*
* A thin facade over AudioManager's instance lifecycle, input/output device
* selection, output buffer management and recording stop control. The
* AudioManager handle returned by oakengine_audio_manager_handle() is a
* borrowed opaque pointer intended only for event subscription
* (OAKENGINE_EVENT_AUDIO_MANAGER_OUTPUT_PARAMS_CHANGED); it is not a general
* purpose object handle and must not be freed.
*
* Conventions match the other facade families:
* - 0 (OAKENGINE_OK) / negative OAKENGINE_E_* codes.
* - Device indices are PortAudio PaDeviceIndex values (int64_t across the
* boundary); paNoDevice is -1.
* - String output uses the buf/size convention (error_buf for
* oakengine_audio_push_to_output).
*/
typedef struct OakAudioParams OakAudioParams;
/**
* @brief Create the AudioManager singleton.
*
* Safe to call when the instance already exists (no-op). Returns
* OAKENGINE_OK or OAKENGINE_E_FAILED.
*/
OAKENGINE_API int oakengine_audio_create_instance(void);
/**
* @brief Destroy the AudioManager singleton.
*
* Safe to call when no instance exists (no-op). Returns OAKENGINE_OK.
*/
OAKENGINE_API int oakengine_audio_destroy_instance(void);
/**
* @brief Borrowed handle to the AudioManager singleton, or NULL if none.
*
* Intended only for subscribing to
* OAKENGINE_EVENT_AUDIO_MANAGER_OUTPUT_PARAMS_CHANGED. The pointer is owned
* by the engine and becomes NULL after oakengine_audio_destroy_instance().
*/
OAKENGINE_API void *oakengine_audio_manager_handle(void);
/**
* @brief Current output device index (paNoDevice = -1 when none).
*
* Returns the current value from the AudioManager singleton, or paNoDevice if
* no instance exists.
*/
OAKENGINE_API int64_t oakengine_audio_get_output_device(void);
/**
* @brief Set the output device index.
*
* Changing the device may emit output_params_changed. Returns OAKENGINE_OK or
* OAKENGINE_E_FAILED.
*/
OAKENGINE_API int oakengine_audio_set_output_device(int64_t device);
/**
* @brief Current input device index (paNoDevice = -1 when none).
*/
OAKENGINE_API int64_t oakengine_audio_get_input_device(void);
/**
* @brief Set the input device index.
*/
OAKENGINE_API int oakengine_audio_set_input_device(int64_t device);
/**
* @brief Re-initialize PortAudio and refresh the device lists.
*/
OAKENGINE_API int oakengine_audio_hard_reset(void);
/**
* @brief Clear any buffered output samples.
*/
OAKENGINE_API int oakengine_audio_clear_buffered_output(void);
/**
* @brief Push a packed sample buffer to the current output device.
*
* `params` is an owned or borrowed OakAudioParams handle describing the
* sample data. `samples` points to `samples_size` bytes of interleaved audio
* data in the format described by `params`. On failure a human-readable
* message is written into `error_buf` (up to `error_buf_size` bytes including
* the terminating NUL) and OAKENGINE_E_FAILED is returned.
*/
OAKENGINE_API int oakengine_audio_push_to_output(const OakAudioParams *params,
const char *samples,
int64_t samples_size,
char *error_buf,
int error_buf_size);
/**
* @brief Stop an active recording session.
*/
OAKENGINE_API int oakengine_audio_stop_recording(void);
/**
* @brief Stop audio output.
*/
OAKENGINE_API int oakengine_audio_stop_output(void);
/**
* @brief Restart the output clock at zero for a new playback run.
*/
OAKENGINE_API int oakengine_audio_reset_output_clock(void);
/**
* @brief Set the output notify interval in bytes.
*
* After this many bytes of audio have been consumed by the output device,
* the AudioManager emits output_notify (which translates to the
* OAKENGINE_EVENT_AUDIO_MANAGER_OUTPUT_NOTIFY event for C subscribers).
*/
OAKENGINE_API int oakengine_audio_set_output_notify_interval(int64_t bytes);
/**
* @brief Start audio recording.
*
* Takes ownership of `params`: the handle is destroyed when the recording
* ends. On failure a human-readable message is written into `error_buf`
* (up to `error_buf_size` bytes including the terminating NUL).
*
* @return OAKENGINE_OK on success, OAKENGINE_E_FAILED on error.
*/
OAKENGINE_API int oakengine_audio_start_recording(
OakEngineEncodingParams *params, char *error_buf, int error_buf_size);
/* ---- Audio synchronization (R6 P1.3) ------------------------------------ */
/** @brief Result of envelope-offset correlation. */
typedef struct oak_audio_waveform_offset {
int64_t offset_samples;
double confidence;
/** 1 if the offset is usable, 0 otherwise. */
int valid;
} oak_audio_waveform_offset;
/** @brief Result of rate+offset correlation. */
typedef struct oak_audio_waveform_stretch_offset {
double rate;
int64_t offset_samples;
double confidence;
/** 1 if the result is usable, 0 otherwise. */
int valid;
} oak_audio_waveform_stretch_offset;
/**
* @brief Estimate the sample offset between two RMS envelopes.
*
* `reference_valid`/`candidate_valid` may be NULL to mean "all windows valid";
* if non-NULL their lengths must equal `reference_len`/`candidate_len`.
*
* @return OAKENGINE_OK with `out` filled, or OAKENGINE_E_INVALID.
*/
OAKENGINE_API int oakengine_audio_estimate_envelope_offset(
const double *reference, int reference_len,
const double *candidate, int candidate_len,
const bool *reference_valid, int reference_valid_len,
const bool *candidate_valid, int candidate_valid_len,
uint64_t window_samples, int64_t max_offset_windows,
oak_audio_waveform_offset *out);
/**
* @brief Estimate a playback-rate change plus offset aligning candidate to
* reference.
*
* See AudioWaveformSync::estimate_stretch_and_offset().
*/
OAKENGINE_API int oakengine_audio_estimate_stretch_and_offset(
const double *reference, int reference_len,
const double *candidate, int candidate_len,
const bool *reference_valid, int reference_valid_len,
const bool *candidate_valid, int candidate_valid_len,
uint64_t window_samples, int64_t max_offset_windows,
double min_rate, double max_rate, double rate_step,
oak_audio_waveform_stretch_offset *out);
/** @brief Source-clip description for source-time synchronization. */
typedef struct oak_audio_sync_source_clip {
/** Source start time as a Rational num/den pair. */
int64_t source_start_time_num;
int64_t source_start_time_den;
/** Media in-point as a Rational num/den pair. */
int64_t media_in_num;
int64_t media_in_den;
/** 1 if source_start_time is meaningful, 0 otherwise. */
int has_source_start_time;
} oak_audio_sync_source_clip;
/** @brief Timeline placement result from AudioSynchronizer. */
typedef struct oak_audio_sync_placement {
/** Timeline in-point as a Rational num/den pair. */
int64_t timeline_in_num;
int64_t timeline_in_den;
/** 1 if the placement is usable, 0 otherwise. */
int valid;
} oak_audio_sync_placement;
/**
* @brief Compute a candidate clip's timeline placement from source timecodes.
*
* `reference_timeline_in` is the reference clip's timeline in-point as a
* Rational num/den pair.
*/
OAKENGINE_API int oakengine_audio_sync_place_by_source_time(
const oak_audio_sync_source_clip *reference,
const oak_audio_sync_source_clip *candidate,
int64_t reference_timeline_in_num, int64_t reference_timeline_in_den,
oak_audio_sync_placement *out);
/**
* @brief Compute a candidate clip's timeline placement from a waveform offset.
*/
OAKENGINE_API int oakengine_audio_sync_place_by_waveform_offset(
int64_t reference_timeline_in_num, int64_t reference_timeline_in_den,
int64_t candidate_offset_samples, int sample_rate,
oak_audio_sync_placement *out);
/* ---- Audio format processor (R6 P5) ------------------------------------- */
/**
* @brief Opaque audio format converter (olive::AudioProcessor).
*
* Converts planar float samples from one format to a packed output format,
* optionally applying tempo (speed) scaling. Used by the viewer to feed the
* audio output device. Create with oakengine_audio_processor_create() and
* destroy with oakengine_audio_processor_free().
*/
typedef struct OakEngineAudioProcessor OakEngineAudioProcessor;
/**
* @brief Create an audio processor with no open graph.
*
* Returns NULL on allocation failure.
*/
OAKENGINE_API OakEngineAudioProcessor *oakengine_audio_processor_create(void);
/**
* @brief Destroy the processor, closing any open graph. Safe to call with
* NULL.
*/
OAKENGINE_API void oakengine_audio_processor_free(OakEngineAudioProcessor *p);
/**
* @brief Open the conversion graph.
*
* `from` describes the planar float input format and `to` the packed output
* format (both borrowed handles, copied internally). `tempo` is the playback
* speed (1.0 = normal). The processor must not already be open. Returns
* OAKENGINE_OK, OAKENGINE_E_INVALID, or OAKENGINE_E_FAILED.
*/
OAKENGINE_API int oakengine_audio_processor_open(OakEngineAudioProcessor *p,
const OakAudioParams *from, const OakAudioParams *to, double tempo);
/**
* @brief Close the conversion graph. Safe to call when not open or with
* NULL.
*/
OAKENGINE_API void oakengine_audio_processor_close(OakEngineAudioProcessor *p);
/**
* @brief 1 if the processor has an open graph, 0 otherwise (or NULL).
*/
OAKENGINE_API int oakengine_audio_processor_is_open(OakEngineAudioProcessor *p);
/**
* @brief Convert planar float samples to the packed output format.
*
* `in` is an array of per-channel float pointers (channel count as given to
* open()); `nb_in_samples` is the number of frames. On success (>= 0),
* `*out_data` points to the packed output bytes owned by `p` (valid until the
* next convert/close/free) and `*out_size` holds the byte count, which may be
* 0 when the tempo buffer absorbed the block. Returns a negative error code
* on failure.
*/
OAKENGINE_API int oakengine_audio_processor_convert(OakEngineAudioProcessor *p,
float **in, int nb_in_samples, const void **out_data, int *out_size);
/**
* @brief Output (packed) parameters as a new OakAudioParams handle.
*
* The caller owns the result and must free it with oakcore_audioparams_free().
* Returns NULL if `p` is NULL or not open.
*/
OAKENGINE_API OakAudioParams *oakengine_audio_processor_output_params(
OakEngineAudioProcessor *p);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_AUDIO_H */
+297
View File
@@ -0,0 +1,297 @@
/***
Oak - 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 OAKENGINE_COLOR_H
#define OAKENGINE_COLOR_H
#include "export.h"
#include "init.h"
#include "project.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file color.h
* @brief C ABI for color management (the olive::ColorManager /
* ColorTransform / ColorProcessor surface)
*
* Covers everything the application's display and color-picker paths need
* without importing an engine C++ symbol:
*
* - OakEngineColorManager: borrowed handle to a project's color manager
* (olive::ColorManager). Obtain it with
* oakengine_color_manager_from_project(); like the other borrowed
* handles it is just the engine pointer reinterpreted and its lifetime
* follows the project. All list queries use the index + buf/size
* string pattern (the return value of a string getter is the would-be
* length excluding the NUL, so buf == NULL queries the size).
*
* - oak_color_transform: POD mirror of olive::ColorTransform. `output`
* is the colorspace name when `is_display` is 0, otherwise the display
* device name with `view`/`look` selecting the display transform. NULL
* strings mean "unset" (the empty QString).
*
* - OakEngineColorProcessor: owned handle wrapping an OCIO-backed
* olive::ColorProcessorPtr. Free with
* oakengine_color_processor_free(). Color conversion is per-color
* (double RGBA in/out); the frame-level GPU path goes through
* ColorTransformJob on the engine side.
*
* - OakEngineColorConfig: owned handle to a standalone OCIO config (the
* project properties dialog lists the colorspaces of a config file
* before applying it).
*
* Change notifications (config reloads, reference space changes) are
* delivered through the event family: subscribe with
* OAKENGINE_EVENT_COLOR_MANAGER_CONFIG_CHANGED /
* OAKENGINE_EVENT_COLOR_MANAGER_REFERENCE_SPACE_CHANGED (oakengine/events.h).
*
* Error model: functions that can hit an OCIO failure report the reason
* through oakengine_color_last_error() (thread-local, buf/size
* convention). List/query functions never fail on a valid handle.
*/
/** @brief Borrowed color manager handle (olive::ColorManager). */
typedef struct OakEngineColorManager OakEngineColorManager;
/** @brief Owned color processor handle; free with oakengine_color_processor_free(). */
typedef struct OakEngineColorProcessor OakEngineColorProcessor;
/** @brief Owned standalone OCIO config handle; free with oakengine_color_config_free(). */
typedef struct OakEngineColorConfig OakEngineColorConfig;
/** @brief Processor direction: input -> output (olive k_normal). */
#define OAKENGINE_COLOR_PROCESSOR_NORMAL 0
/** @brief Processor direction: output -> input (olive k_inverse). */
#define OAKENGINE_COLOR_PROCESSOR_INVERSE 1
/**
* @brief POD mirror of olive::ColorTransform. Strings are UTF-8; NULL is
* the unset/empty value.
*/
typedef struct oak_color_transform {
int is_display; /**< 0: `output` is a colorspace; 1: display/view/look. */
const char *output; /**< Colorspace name, or display device when is_display. */
const char *view; /**< Display view (is_display only). */
const char *look; /**< Display look (is_display only). */
} oak_color_transform;
/**
* @brief Human-readable reason of the last failed color call on this
* thread (buf/size convention). Empty when the last call succeeded.
*/
OAKENGINE_API int oakengine_color_last_error(char *buf, int buf_size);
/**
* @brief The project's color manager (borrowed; NULL for a NULL project or
* a project without one).
*/
OAKENGINE_API OakEngineColorManager *
oakengine_color_manager_from_project(OakEngineProject *project);
/** @brief Current OCIO config filename of the manager (buf/size). */
OAKENGINE_API int oakengine_color_manager_get_config_filename(
const OakEngineColorManager *mgr, char *buf, int buf_size);
/**
* @brief Point the manager at a different OCIO config file
* (ColorManager::set_config_filename()). OAKENGINE_E_INVALID for NULL
* args. OCIO load failures surface lazily through the list queries.
*/
OAKENGINE_API int oakengine_color_manager_set_config_filename(
OakEngineColorManager *mgr, const char *filename);
/** @brief Number of colorspaces in the manager's active config. */
OAKENGINE_API int oakengine_color_manager_colorspace_count(
const OakEngineColorManager *mgr);
/** @brief Name of the `index`-th colorspace (buf/size); OAKENGINE_E_INVALID out of range. */
OAKENGINE_API int oakengine_color_manager_colorspace_at(
const OakEngineColorManager *mgr, int index, char *buf, int buf_size);
/** @brief Number of display devices in the active config. */
OAKENGINE_API int oakengine_color_manager_display_count(
const OakEngineColorManager *mgr);
/** @brief Name of the `index`-th display device (buf/size). */
OAKENGINE_API int oakengine_color_manager_display_at(
const OakEngineColorManager *mgr, int index, char *buf, int buf_size);
/** @brief Number of views available on `display` (NULL/empty = active display). */
OAKENGINE_API int oakengine_color_manager_view_count(
const OakEngineColorManager *mgr, const char *display);
/** @brief Name of the `index`-th view on `display` (buf/size). */
OAKENGINE_API int oakengine_color_manager_view_at(
const OakEngineColorManager *mgr, const char *display, int index,
char *buf, int buf_size);
/** @brief Number of looks in the active config. */
OAKENGINE_API int oakengine_color_manager_look_count(
const OakEngineColorManager *mgr);
/** @brief Name of the `index`-th look (buf/size). */
OAKENGINE_API int oakengine_color_manager_look_at(
const OakEngineColorManager *mgr, int index, char *buf, int buf_size);
/** @brief The config's default display device (buf/size). */
OAKENGINE_API int oakengine_color_manager_default_display(
const OakEngineColorManager *mgr, char *buf, int buf_size);
/** @brief The config's default view for `display` (buf/size). */
OAKENGINE_API int oakengine_color_manager_default_view(
const OakEngineColorManager *mgr, const char *display, char *buf,
int buf_size);
/** @brief The project's default input colorspace (buf/size). */
OAKENGINE_API int oakengine_color_manager_default_input_color_space(
const OakEngineColorManager *mgr, char *buf, int buf_size);
/** @brief Set the project's default input colorspace. */
OAKENGINE_API int oakengine_color_manager_set_default_input_color_space(
OakEngineColorManager *mgr, const char *colorspace);
/** @brief The config's reference (scene-linear) colorspace (buf/size). */
OAKENGINE_API int oakengine_color_manager_reference_color_space(
const OakEngineColorManager *mgr, char *buf, int buf_size);
/**
* @brief The config's default luma coefficients written to `rgb` (exactly
* 3 doubles; ColorManager::get_default_luma_coefs()). OAKENGINE_E_INVALID
* for NULL args.
*/
OAKENGINE_API int oakengine_color_manager_default_luma_coefs(
const OakEngineColorManager *mgr, double *rgb);
/**
* @brief Resolve `name` to a colorspace of the active config
* (ColorManager::get_compliant_color_space(QString); buf/size). Unknown
* names resolve to the default input colorspace.
*/
OAKENGINE_API int oakengine_color_manager_compliant_color_space(
const OakEngineColorManager *mgr, const char *name, char *buf,
int buf_size);
/**
* @brief Resolve a transform to one the active config supports
* (ColorManager::get_compliant_color_space(ColorTransform, force_display)).
*
* The resolved transform is written into the output buffers; any output
* pointer may be NULL. Buffers that are too small truncate (NUL-terminated
* when size > 0). `out_is_display` receives the resolved kind.
*
* @return OAKENGINE_OK, or OAKENGINE_E_INVALID for NULL mgr/in.
*/
OAKENGINE_API int oakengine_color_manager_compliant_transform(
const OakEngineColorManager *mgr, const oak_color_transform *in,
int force_display, int *out_is_display, char *out_output,
int output_size, char *out_view, int view_size, char *out_look,
int look_size);
/**
* @brief Load the engine's built-in default OCIO config (owned handle).
* NULL on failure (see oakengine_color_last_error()).
*/
OAKENGINE_API OakEngineColorConfig *oakengine_color_config_load_default(void);
/**
* @brief Load an OCIO config from `filename` (owned handle). NULL on
* failure (see oakengine_color_last_error()).
*/
OAKENGINE_API OakEngineColorConfig *
oakengine_color_config_load_file(const char *filename);
/** @brief Release a config handle (NULL-safe no-op). */
OAKENGINE_API void oakengine_color_config_free(OakEngineColorConfig *config);
/** @brief Number of colorspaces in the config. */
OAKENGINE_API int
oakengine_color_config_colorspace_count(const OakEngineColorConfig *config);
/** @brief Name of the `index`-th colorspace in the config (buf/size). */
OAKENGINE_API int oakengine_color_config_colorspace_at(
const OakEngineColorConfig *config, int index, char *buf, int buf_size);
/**
* @brief Create a color processor converting from colorspace `input` to
* the `dest` transform (ColorProcessor::create(); owned handle).
*
* `direction` is OAKENGINE_COLOR_PROCESSOR_NORMAL or
* OAKENGINE_COLOR_PROCESSOR_INVERSE. OCIO failures are non-fatal (matching
* the engine's C++ behavior): the handle is still returned but
* oakengine_color_processor_is_valid() reports 0 and conversions are
* pass-through.
*
* @return The handle, or NULL for NULL mgr/input/dest or an unknown
* direction.
*/
OAKENGINE_API OakEngineColorProcessor *oakengine_color_processor_create(
const OakEngineColorManager *mgr, const char *input,
const oak_color_transform *dest, int direction);
/** @brief Release a processor handle (NULL-safe no-op). */
OAKENGINE_API void oakengine_color_processor_free(OakEngineColorProcessor *proc);
/**
* @brief 1 when the processor holds a valid OCIO processor
* (ColorProcessor::get_processor() != null), 0 otherwise.
*/
OAKENGINE_API int
oakengine_color_processor_is_valid(const OakEngineColorProcessor *proc);
/**
* @brief Convert a single RGBA color (ColorProcessor::convert_color()).
* `in_rgba`/`out_rgba` are 4-double arrays; on an invalid processor the
* input is copied through.
*
* @return OAKENGINE_OK, or OAKENGINE_E_INVALID for NULL args.
*/
OAKENGINE_API int oakengine_color_processor_convert_color(
const OakEngineColorProcessor *proc, const double *in_rgba,
double *out_rgba);
/**
* @brief The OCIO cache id of the processor (ColorProcessor::id();
* buf/size). Used by display paths to invalidate cached conversions.
*/
OAKENGINE_API int oakengine_color_processor_id(
const OakEngineColorProcessor *proc, char *buf, int buf_size);
/**
* @brief Attach a processor to an engine ColorTransformJob
* (ColorTransformJob::set_color_processor()).
*
* Transitional bridge for the display/scopes GPU path until the blit
* family covers ColorTransformJob: `job` is an
* olive::ColorTransformJob* the caller owns, passed as void* to keep the
* C++ type out of the ABI.
*
* @return OAKENGINE_OK, or OAKENGINE_E_INVALID for a NULL job.
*/
OAKENGINE_API int oakengine_color_transform_job_set_processor(
void *job, const OakEngineColorProcessor *proc);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_COLOR_H */
+100
View File
@@ -0,0 +1,100 @@
/***
Oak - 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 OAKENGINE_CONFIG_H
#define OAKENGINE_CONFIG_H
#include <stdint.h>
#include "init.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file config.h
* @brief C ABI for the engine configuration store (olive::Config).
*
* A thin facade over the QSettings-backed key/value store used by the editor
* for persistent preferences. Only the types actually used by the UI are
* exposed (string/int); the engine keeps ownership of the singleton.
*/
typedef void (*oakengine_config_error_fn)(const char *title,
const char *message,
void *userdata);
/**
* @brief Load configuration from disk (Config::load).
*/
OAKENGINE_API int oakengine_config_load(void);
/**
* @brief Save configuration to disk (Config::save).
*/
OAKENGINE_API int oakengine_config_save(void);
/**
* @brief Read a string value (buf/size convention).
*
* @return the string length on success, 0 when the key is missing or empty,
* or a negative OAKENGINE_E_* code on error.
*/
OAKENGINE_API int oakengine_config_get_string(const char *key, char *buf,
int buf_size);
/**
* @brief Write a string value.
*/
OAKENGINE_API int oakengine_config_set_string(const char *key,
const char *value);
/**
* @brief Read an integer value. Returns `default_value` when the key is
* missing or not convertible to int.
*/
OAKENGINE_API int64_t oakengine_config_get_int(const char *key,
int64_t default_value);
/**
* @brief Write an integer value.
*/
OAKENGINE_API int oakengine_config_set_int(const char *key, int64_t value);
/**
* @brief Register a callback for configuration errors (e.g. disk write
* failures). Passing NULL clears the handler.
*/
OAKENGINE_API int oakengine_config_set_error_handler(
oakengine_config_error_fn fn, void *userdata);
/**
* @brief Report an error through the registered handler. If no handler is
* set the error is logged and discarded.
*/
OAKENGINE_API int oakengine_config_report_error(const char *title,
const char *message);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_CONFIG_H */
+154
View File
@@ -0,0 +1,154 @@
/***
Oak - 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 OAKENGINE_DISK_H
#define OAKENGINE_DISK_H
#include "export.h"
#include "init.h"
#include "project.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file disk.h
* @brief C ABI for the engine's disk cache singleton (olive::DiskManager)
*
* A thin facade over DiskManager's instance lifecycle, default/custom cache
* path management, cache clearing, settings dialog dispatch and project
* invalidation. The opaque folder handle returned by
* oakengine_disk_get_open_folder() is a borrowed pointer to the engine's
* internal DiskCacheFolder for that path; it must not be freed and becomes
* invalid when the DiskManager instance is destroyed.
*
* Conventions match the other facade families:
* - 0 (OAKENGINE_OK) / negative OAKENGINE_E_* codes.
* - String output uses the buf/size convention.
* - Booleans are int (1/0).
*/
/**
* @brief Callback invoked when the engine requests the disk cache settings
* dialog for a folder.
*
* `folder_path` is the UTF-8 path of the cache folder. `parent_window` is a
* borrowed pointer to the QWidget that should act as the dialog's parent (may
* be NULL). `userdata` is the value passed to
* oakengine_disk_set_settings_handler().
*/
typedef void (*oakengine_disk_settings_fn)(const char *folder_path,
void *parent_window,
void *userdata);
/**
* @brief Create the DiskManager singleton.
*
* Safe to call when the instance already exists (no-op). Returns
* OAKENGINE_OK or OAKENGINE_E_FAILED.
*/
OAKENGINE_API int oakengine_disk_create_instance(void);
/**
* @brief Destroy the DiskManager singleton.
*
* Safe to call when no instance exists (no-op). Returns OAKENGINE_OK.
*/
OAKENGINE_API int oakengine_disk_destroy_instance(void);
/**
* @brief Register the handler used to show the disk cache settings dialog.
*
* The engine calls this handler when the user requests the settings dialog.
* Passing NULL clears the handler. Returns OAKENGINE_OK.
*/
OAKENGINE_API int oakengine_disk_set_settings_handler(
oakengine_disk_settings_fn fn, void *userdata);
/**
* @brief Show the disk cache settings dialog for `path`.
*
* If `path` is NULL or empty, the default cache folder is used. The actual
* dialog is shown by the handler registered with
* oakengine_disk_set_settings_handler(); if no handler is registered the
* request is logged and skipped. Returns OAKENGINE_OK or an error code.
*/
OAKENGINE_API int oakengine_disk_show_settings_dialog(const char *path,
void *parent_window);
/**
* @brief Show a confirmation dialog before changing the disk cache location.
*
* Returns 1 if the user confirms, 0 otherwise. `parent_window` may be NULL.
*/
OAKENGINE_API int oakengine_disk_show_change_confirmation_dialog(
void *parent_window);
/**
* @brief Clear the disk cache in `path`.
*
* Returns 1 on success, 0 on failure. The folder is opened if necessary.
*/
OAKENGINE_API int oakengine_disk_clear_cache(const char *path);
/**
* @brief Get the default cache folder path (buf/size convention).
*
* Returns the string length on success, or a negative OAKENGINE_E_* code when
* no DiskManager instance exists.
*/
OAKENGINE_API int oakengine_disk_get_default_cache_path(char *buf,
int buf_size);
/**
* @brief Set the default cache folder path.
*
* The default folder's path is updated and will be persisted when the
* DiskManager instance is destroyed. Returns OAKENGINE_OK or an error code.
*/
OAKENGINE_API int oakengine_disk_set_default_cache_path(const char *path);
/**
* @brief Get or create a borrowed opaque handle to the cache folder for
* `path`.
*
* Returns NULL if no DiskManager instance exists or if `path` is invalid. If
* `path` is NULL or empty, the default cache folder is returned. The returned
* handle is a borrowed pointer whose lifetime follows the DiskManager
* instance; it must not be freed.
*/
OAKENGINE_API void *oakengine_disk_get_open_folder(const char *path);
/**
* @brief Emit the invalidate_project signal on the DiskManager instance.
*
* This tells consumers of the disk cache that `project` has changed and any
* cached data for it should be discarded. Returns OAKENGINE_OK or an error
* code.
*/
OAKENGINE_API int oakengine_disk_invalidate_project(
OakEngineProject *project);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_DISK_H */
+186
View File
@@ -0,0 +1,186 @@
/***
Oak - 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 OAKENGINE_DISPLAY_H
#define OAKENGINE_DISPLAY_H
#include "export.h"
#include "init.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file display.h
* @brief C ABI for the GPU display renderer used by viewer/scope widgets
*
* This family wraps the engine's interactive display renderer
* (olive::Renderer and its OpenGLRenderer/DynamicRenderer implementations,
* engine/render/renderer.h) plus the GPU texture (olive::Texture) and the
* CPU frame buffer (olive::Frame) that viewer/scope widgets use to move
* pixels between the CPU and the GPU.
*
* It is distinct from the sequence-rendering facade in oakengine/renderer.h
* (OakEngineRenderer), which pulls finished CPU frames out of the async
* render pipeline. This family drives the *on-screen* paint path instead:
* a widget creates a renderer, initializes it with the widget's GL context,
* uploads/downloads textures, and blits color-managed images each paint.
*
* Conventions (matching the other facade families):
* - All object pointers are opaque. `renderer` is an olive::Renderer*,
* `texture` an olive::Texture*, `frame` an olive::Frame*.
* - `out_texture` / `out_frame` are pointers to caller-owned
* olive::TexturePtr / olive::FramePtr (std::shared_ptr) storage; the
* callee assigns a newly created smart pointer into them, releasing any
* previously held object. This keeps shared-pointer ownership/deleter
* bookkeeping entirely on the engine side.
* - `video_params` is a `const olive::VideoParams*`; `color_job` is a
* `const olive::ColorTransformJob*`. These are passed as opaque pointers
* because they are C++ types; both the caller (app) and the callee
* (engine) are compiled as C++ against the same headers.
* - `gl_context` is a `QOpenGLContext*` or NULL.
* - `parent` is the owning `QObject*` (the display widget); the created
* renderer is a QObject child of it and is destroyed by Qt ownership.
* Do NOT call oakengine_display_renderer_destroy() and then also rely on
* Qt deletion of the same renderer's GPU resources -- destroy() releases
* GPU state, Qt deletion releases the object.
*/
/* ---- Display renderer lifecycle ---------------------------------------- */
/**
* @brief Create a dynamic-backend renderer (olive::DynamicRenderer) for
* `backend_name` and load() it.
*
* @return The renderer (olive::Renderer*), or NULL if the backend library
* could not be loaded (the failed renderer is deleted internally and
* the caller should fall back to
* oakengine_display_renderer_create_opengl()). NULL is also returned
* when the engine was built without dynamic-backend support.
*/
OAKENGINE_API void *
oakengine_display_renderer_create_dynamic(const char *backend_name,
void *parent);
/**
* @brief Create the built-in OpenGL renderer (olive::OpenGLRenderer).
*
* @return The renderer (olive::Renderer*), never NULL.
*/
OAKENGINE_API void *oakengine_display_renderer_create_opengl(void *parent);
/**
* @brief Initialize a display renderer and run its post-init step.
*
* If `gl_context` is non-NULL the OpenGL/dynamic path is taken (the renderer
* is initialized against the widget's shared QOpenGLContext); otherwise the
* backend-neutral path (Renderer::init()/post_init()) is used.
*
* @return OAKENGINE_OK on success, OAKENGINE_E_INVALID for a NULL renderer.
*/
OAKENGINE_API int oakengine_display_renderer_init(void *renderer,
void *gl_context);
/**
* @brief Release a display renderer's GPU resources (Renderer::destroy()
* followed by post_destroy()). The renderer object itself remains owned by
* its Qt parent.
*/
OAKENGINE_API void oakengine_display_renderer_destroy(void *renderer);
/* ---- Texture creation and pixel transfer -------------------------------- */
/**
* @brief Create a GPU texture on `renderer` (Renderer::create_texture()).
*
* @param renderer olive::Renderer*.
* @param video_params const olive::VideoParams* describing the texture.
* @param pixels Initial pixel data, or NULL for an empty texture.
* @param linesize Line stride of `pixels` (ignored when NULL).
* @param out_texture Pointer to an olive::TexturePtr to receive the result.
*/
OAKENGINE_API void
oakengine_display_renderer_create_texture(void *renderer,
const void *video_params,
const void *pixels, int linesize,
void *out_texture);
/**
* @brief Blit a color-managed image (Renderer::blit_color_managed()).
*
* @param renderer olive::Renderer*.
* @param color_job const olive::ColorTransformJob*.
* @param dst_texture Destination olive::Texture*, or NULL to blit to the
* current output destination.
* @param video_params const olive::VideoParams* for the destination, or NULL
* to use dst_texture's own parameters (in which case
* dst_texture must be non-NULL).
*/
OAKENGINE_API void
oakengine_display_renderer_blit_color_managed(void *renderer,
const void *color_job,
void *dst_texture,
const void *video_params);
/**
* @brief Upload CPU pixels into a GPU texture (Texture::upload()).
*/
OAKENGINE_API void oakengine_display_texture_upload(void *texture,
void *pixels, int linesize);
/**
* @brief Download GPU texture pixels into CPU memory (Texture::download()).
*/
OAKENGINE_API void oakengine_display_texture_download(void *texture,
void *pixels,
int linesize);
/* ---- CPU frame buffer --------------------------------------------------- */
/**
* @brief Create an empty CPU frame (olive::Frame::create()).
*
* @param out_frame Pointer to an olive::FramePtr to receive the new frame.
*/
OAKENGINE_API void oakengine_codec_frame_create(void *out_frame);
/**
* @brief Set a frame's video parameters (Frame::set_video_params()).
*
* @param frame olive::Frame*.
* @param video_params const olive::VideoParams*.
*/
OAKENGINE_API void oakengine_codec_frame_set_video_params(void *frame,
const void
*video_params);
/**
* @brief Allocate the frame's pixel buffer (Frame::allocate()).
*
* @return 1 on success, 0 on failure or NULL frame.
*/
OAKENGINE_API int oakengine_codec_frame_allocate(void *frame);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_DISPLAY_H */
+505
View File
@@ -0,0 +1,505 @@
/***
Oak - 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 OAKENGINE_ENCODING_H
#define OAKENGINE_ENCODING_H
#include <stdint.h>
#include "export.h"
#include "timeline.h"
#include "videoparams.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file encoding.h
* @brief C ABI for the encoding parameter surface (EncodingParams /
* ExportFormat / ExportCodec)
*
* This family exposes everything the application's export dialog (and the
* audio-recording path) needs without touching the engine's C++ classes:
*
* - Container/codec metadata queries (format names/extensions, codec lists
* per format, codec names/flags, supported pixel and sample formats).
* - An opaque OakEngineEncodingParams handle wrapping the engine's
* EncodingParams: full getter/setter surface, preset path/listing and
* preset load/save.
* - oakengine_export_render_with_params(): runs the same synchronous export
* path as oakengine_export_render_ex() (oakengine/exporter.h) using a
* params handle assembled through this family.
*
* Enum int fields carry the engine's own enum values
* (olive::ExportFormat::Format, olive::ExportCodec::Codec,
* olive::VideoParams::Interlacing/ColorRange, olive::PixelFormat::Format,
* olive::core::SampleFormat::Format). Conventions match the other facade
* families: 0 (OAKENGINE_OK) / negative OAKENGINE_E_* codes, buf/size
* strings (return value is the would-be length excluding the NUL), NULL
* handles are no-ops returning the documented failure value.
*/
/** @brief Opaque encoding-parameters handle (olive::EncodingParams). */
typedef struct OakEngineEncodingParams OakEngineEncodingParams;
/** @brief Scaling method values (EncodingParams::VideoScalingMethod). */
#define OAKENGINE_ENCODING_SCALING_FIT 0
#define OAKENGINE_ENCODING_SCALING_STRETCH 1
#define OAKENGINE_ENCODING_SCALING_CROP 2
/**
* @brief Container formats (olive::ExportFormat::Format) referenced by name
* in UI code. Only append; the values are serialized in project/preset
* files. The complete list lives in engine/codec/exportformat.h.
*/
#define OAKENGINE_ENCODING_FORMAT_MATROSKA 1
#define OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO 2
#define OAKENGINE_ENCODING_FORMAT_QUICKTIME 4
#define OAKENGINE_ENCODING_FORMAT_PNG 5
#define OAKENGINE_ENCODING_FORMAT_WAV 7
#define OAKENGINE_ENCODING_FORMAT_SRT 13
/**
* @brief Codecs (olive::ExportCodec::Codec) referenced by name in UI code.
* Only append; the values are serialized. The complete list lives in
* engine/codec/exportcodec.h.
*/
#define OAKENGINE_ENCODING_CODEC_H264 1
#define OAKENGINE_ENCODING_CODEC_H264RGB 2
#define OAKENGINE_ENCODING_CODEC_H265 3
#define OAKENGINE_ENCODING_CODEC_CINEFORM 7
#define OAKENGINE_ENCODING_CODEC_AAC 12
#define OAKENGINE_ENCODING_CODEC_PCM 13
#define OAKENGINE_ENCODING_CODEC_SRT 17
#define OAKENGINE_ENCODING_CODEC_AV1 18
/** @brief olive::VideoParams::ColorRange values. */
#define OAKENGINE_ENCODING_COLOR_RANGE_LIMITED 0
#define OAKENGINE_ENCODING_COLOR_RANGE_FULL 1
/** @brief olive::VideoParams::Interlacing values. */
#define OAKENGINE_ENCODING_INTERLACE_NONE 0
#define OAKENGINE_ENCODING_INTERLACE_TOP_FIRST 1
#define OAKENGINE_ENCODING_INTERLACE_BOTTOM_FIRST 2
/* ---- Container format / codec metadata ---------------------------------- */
/** @brief Number of container formats (olive::ExportFormat::k_format_count). */
OAKENGINE_API int oakengine_encoding_format_count(void);
/** @brief Display name of a container format (buf/size); -1 invalid. */
OAKENGINE_API int oakengine_encoding_format_name(int format, char *buf,
int buf_size);
/** @brief File extension (no dot) of a container format (buf/size). */
OAKENGINE_API int oakengine_encoding_format_extension(int format, char *buf,
int buf_size);
/**
* @brief Number of video codecs a container format supports; -1 when the
* format is invalid.
*/
OAKENGINE_API int oakengine_encoding_format_video_codec_count(int format);
/**
* @brief The `index`-th video codec of `format` as an
* olive::ExportCodec::Codec value; -1 when out of range.
*/
OAKENGINE_API int oakengine_encoding_format_video_codec_at(int format,
int index);
/** @brief Audio-codec variant of the two functions above. */
OAKENGINE_API int oakengine_encoding_format_audio_codec_count(int format);
OAKENGINE_API int oakengine_encoding_format_audio_codec_at(int format,
int index);
/** @brief Subtitle-codec variant of the two functions above. */
OAKENGINE_API int oakengine_encoding_format_subtitle_codec_count(int format);
OAKENGINE_API int oakengine_encoding_format_subtitle_codec_at(int format,
int index);
/** @brief Display name of a codec (buf/size); -1 when invalid. */
OAKENGINE_API int oakengine_encoding_codec_name(int codec, char *buf,
int buf_size);
/** @brief 1 when `codec` encodes still images (PNG/TIFF/OpenEXR). */
OAKENGINE_API int oakengine_encoding_codec_is_still_image(int codec);
/** @brief 1 when `codec` is lossless (no bit-rate setting applies). */
OAKENGINE_API int oakengine_encoding_codec_is_lossless(int codec);
/**
* @brief Number of encoded pixel formats (e.g. "yuv420p") usable with
* `codec` inside `format`; -1 when invalid.
*/
OAKENGINE_API int oakengine_encoding_pix_fmt_count(int format, int codec);
/** @brief The `index`-th encoded pixel format name (buf/size). */
OAKENGINE_API int oakengine_encoding_pix_fmt_at(int format, int codec,
int index, char *buf,
int buf_size);
/**
* @brief Index of `pix_fmt` (e.g. "yuv420p") in `codec`'s supported pixel
* format list; 0 (the codec's preferred format) when absent or `pix_fmt` is
* NULL/empty.
*/
OAKENGINE_API int oakengine_encoding_pix_fmt_index(int codec,
const char *pix_fmt);
/**
* @brief Number of sample formats usable with `codec` inside `format`;
* -1 when invalid.
*/
OAKENGINE_API int oakengine_encoding_sample_format_count(int format,
int codec);
/**
* @brief The `index`-th sample format as an olive::core::SampleFormat::Format
* value; -1 when out of range.
*/
OAKENGINE_API int oakengine_encoding_sample_format_at(int format, int codec,
int index);
/* ---- Image-sequence filename helpers (olive::Encoder statics) ----------- */
/** @brief 1 when `filename` contains a "[#####]" digit placeholder. */
OAKENGINE_API int
oakengine_encoding_filename_contains_digit_placeholder(const char *filename);
/**
* @brief Digit count of the filename's "[#####]" placeholder; 0 when none.
*/
OAKENGINE_API int
oakengine_encoding_image_sequence_digit_count(const char *filename);
/** @brief `filename` with the digit placeholder removed (buf/size). */
OAKENGINE_API int
oakengine_encoding_filename_remove_digit_placeholder(const char *filename,
char *buf, int buf_size);
/**
* @brief Fit/stretch/crop transform matrix
* (EncodingParams::generate_matrix()).
*
* Writes the 16 floats of the column-major 4x4 matrix to `out16`
* (QMatrix4x4 layout). `method` is OAKENGINE_ENCODING_SCALING_*.
*
* @return OAKENGINE_OK, or OAKENGINE_E_INVALID for bad arguments.
*/
OAKENGINE_API int oakengine_encoding_generate_matrix(int method, int src_width,
int src_height,
int dest_width,
int dest_height,
float out16[16]);
/* ---- Encoding parameters handle ----------------------------------------- */
/**
* @brief Create an empty encoding-parameters handle (all tracks disabled,
* format unset). Destroy with oakengine_encoding_params_destroy().
*/
OAKENGINE_API OakEngineEncodingParams *oakengine_encoding_params_create(void);
/** @brief Destroy a handle created by oakengine_encoding_params_create(). */
OAKENGINE_API void
oakengine_encoding_params_destroy(OakEngineEncodingParams *params);
/**
* @brief 1 when at least one of video/audio/subtitles is enabled
* (EncodingParams::is_valid()).
*/
OAKENGINE_API int
oakengine_encoding_params_is_valid(const OakEngineEncodingParams *params);
/** @brief Output filename (buf/size convention). */
OAKENGINE_API int
oakengine_encoding_params_set_filename(OakEngineEncodingParams *params,
const char *filename);
OAKENGINE_API int
oakengine_encoding_params_filename(const OakEngineEncodingParams *params,
char *buf, int buf_size);
/**
* @brief Container format as olive::ExportFormat::Format; the getter returns
* -1 when unset. The setter rejects out-of-range values with
* OAKENGINE_E_INVALID.
*/
OAKENGINE_API int
oakengine_encoding_params_set_format(OakEngineEncodingParams *params,
int format);
OAKENGINE_API int
oakengine_encoding_params_format(const OakEngineEncodingParams *params);
/**
* @brief Enable video with the given parameters and codec
* (EncodingParams::enable_video()).
*/
OAKENGINE_API int
oakengine_encoding_params_enable_video(OakEngineEncodingParams *params,
const oak_video_params *video,
int codec);
/**
* @brief Enable audio (EncodingParams::enable_audio()). `sample_format` is
* an olive::core::SampleFormat::Format value.
*/
OAKENGINE_API int
oakengine_encoding_params_enable_audio(OakEngineEncodingParams *params,
int sample_rate,
uint64_t channel_layout,
int sample_format, int codec);
/** @brief Enable embedded subtitles. */
OAKENGINE_API int
oakengine_encoding_params_enable_subtitles(OakEngineEncodingParams *params,
int codec);
/** @brief Enable sidecar subtitles with the given sidecar container. */
OAKENGINE_API int oakengine_encoding_params_enable_sidecar_subtitles(
OakEngineEncodingParams *params, int format, int codec);
OAKENGINE_API void
oakengine_encoding_params_disable_video(OakEngineEncodingParams *params);
OAKENGINE_API void
oakengine_encoding_params_disable_audio(OakEngineEncodingParams *params);
OAKENGINE_API void
oakengine_encoding_params_disable_subtitles(OakEngineEncodingParams *params);
OAKENGINE_API int
oakengine_encoding_params_video_enabled(const OakEngineEncodingParams *params);
OAKENGINE_API int
oakengine_encoding_params_video_codec(const OakEngineEncodingParams *params);
/**
* @brief Read back the video parameters (any field may be NULL);
* OAKENGINE_E_STATE when video is disabled.
*/
OAKENGINE_API int oakengine_encoding_params_get_video_params(
const OakEngineEncodingParams *params, oak_video_params *out);
OAKENGINE_API int
oakengine_encoding_params_audio_enabled(const OakEngineEncodingParams *params);
OAKENGINE_API int
oakengine_encoding_params_audio_codec(const OakEngineEncodingParams *params);
/**
* @brief Read back the audio parameters (any field may be NULL);
* OAKENGINE_E_STATE when audio is disabled.
*/
OAKENGINE_API int oakengine_encoding_params_get_audio_params(
const OakEngineEncodingParams *params, int *sample_rate,
uint64_t *channel_layout, int *sample_format);
OAKENGINE_API int oakengine_encoding_params_subtitles_enabled(
const OakEngineEncodingParams *params);
OAKENGINE_API int oakengine_encoding_params_subtitles_are_sidecar(
const OakEngineEncodingParams *params);
OAKENGINE_API int oakengine_encoding_params_subtitles_sidecar_format(
const OakEngineEncodingParams *params);
OAKENGINE_API int oakengine_encoding_params_subtitles_codec(
const OakEngineEncodingParams *params);
/** @brief Video bit rates / buffer size (bit/s, bytes). */
OAKENGINE_API void
oakengine_encoding_params_set_video_bit_rate(OakEngineEncodingParams *params,
int64_t rate);
OAKENGINE_API int64_t
oakengine_encoding_params_video_bit_rate(const OakEngineEncodingParams *params);
OAKENGINE_API void
oakengine_encoding_params_set_video_min_bit_rate(
OakEngineEncodingParams *params, int64_t rate);
OAKENGINE_API int64_t oakengine_encoding_params_video_min_bit_rate(
const OakEngineEncodingParams *params);
OAKENGINE_API void
oakengine_encoding_params_set_video_max_bit_rate(
OakEngineEncodingParams *params, int64_t rate);
OAKENGINE_API int64_t oakengine_encoding_params_video_max_bit_rate(
const OakEngineEncodingParams *params);
OAKENGINE_API void
oakengine_encoding_params_set_video_buffer_size(
OakEngineEncodingParams *params, int64_t size);
OAKENGINE_API int64_t oakengine_encoding_params_video_buffer_size(
const OakEngineEncodingParams *params);
/** @brief Encoder thread count (0 = auto). */
OAKENGINE_API void
oakengine_encoding_params_set_video_threads(OakEngineEncodingParams *params,
int threads);
OAKENGINE_API int
oakengine_encoding_params_video_threads(const OakEngineEncodingParams *params);
/** @brief Audio bit rate (bit/s). */
OAKENGINE_API void
oakengine_encoding_params_set_audio_bit_rate(OakEngineEncodingParams *params,
int64_t rate);
OAKENGINE_API int64_t
oakengine_encoding_params_audio_bit_rate(const OakEngineEncodingParams *params);
/** @brief Encoded pixel format name (e.g. "yuv420p"; buf/size getter). */
OAKENGINE_API int
oakengine_encoding_params_set_video_pix_fmt(OakEngineEncodingParams *params,
const char *pix_fmt);
OAKENGINE_API int
oakengine_encoding_params_video_pix_fmt(
const OakEngineEncodingParams *params, char *buf, int buf_size);
/** @brief Image-sequence flag (0/1). */
OAKENGINE_API void
oakengine_encoding_params_set_video_is_image_sequence(
OakEngineEncodingParams *params, int is_image_sequence);
OAKENGINE_API int oakengine_encoding_params_video_is_image_sequence(
const OakEngineEncodingParams *params);
/**
* @brief Output color transform by OCIO color space name; an empty/NULL
* name selects the reference space (no transform).
*/
OAKENGINE_API int oakengine_encoding_params_set_color_transform(
OakEngineEncodingParams *params, const char *output_name);
OAKENGINE_API int oakengine_encoding_params_color_transform_output(
const OakEngineEncodingParams *params, char *buf, int buf_size);
/** @brief Export length as rational seconds. */
OAKENGINE_API void
oakengine_encoding_params_set_export_length(OakEngineEncodingParams *params,
int num, int den);
OAKENGINE_API int
oakengine_encoding_params_get_export_length(
const OakEngineEncodingParams *params, int *num, int *den);
/**
* @brief Custom export range as rational seconds [in, out). The getter
* returns OAKENGINE_E_NOT_FOUND when no custom range is set.
*/
OAKENGINE_API void
oakengine_encoding_params_set_custom_range(OakEngineEncodingParams *params,
int64_t in_num, int64_t in_den,
int64_t out_num, int64_t out_den);
OAKENGINE_API int
oakengine_encoding_params_has_custom_range(
const OakEngineEncodingParams *params);
OAKENGINE_API int
oakengine_encoding_params_get_custom_range(
const OakEngineEncodingParams *params, int64_t *in_num, int64_t *in_den,
int64_t *out_num, int64_t *out_den);
/** @brief Scaling method (OAKENGINE_ENCODING_SCALING_*). */
OAKENGINE_API int
oakengine_encoding_params_set_video_scaling_method(
OakEngineEncodingParams *params, int method);
OAKENGINE_API int oakengine_encoding_params_video_scaling_method(
const OakEngineEncodingParams *params);
/**
* @brief Encoder-specific video option (key/value strings, e.g. "crf" =
* "18"); mirrors EncodingParams::set_video_option(). The getter returns the
* would-be length (buf/size) or OAKENGINE_E_NOT_FOUND when the key is unset.
*/
OAKENGINE_API int
oakengine_encoding_params_set_video_option(OakEngineEncodingParams *params,
const char *key, const char *value);
OAKENGINE_API int
oakengine_encoding_params_video_option(const OakEngineEncodingParams *params,
const char *key, char *buf,
int buf_size);
/* ---- Presets ------------------------------------------------------------- */
/** @brief Directory where export presets live (buf/size). */
OAKENGINE_API int oakengine_encoding_preset_path(char *buf, int buf_size);
/** @brief Number of saved presets. */
OAKENGINE_API int oakengine_encoding_preset_count(void);
/** @brief Name of the `index`-th preset (buf/size); -1 when out of range. */
OAKENGINE_API int oakengine_encoding_preset_name(int index, char *buf,
int buf_size);
/**
* @brief Load parameters from a preset/XML file (overwrites the handle's
* contents on success).
*
* @return OAKENGINE_OK, OAKENGINE_E_INVALID for bad arguments, or
* OAKENGINE_E_FAILED when the file cannot be read or parsed.
*/
OAKENGINE_API int
oakengine_encoding_params_load_file(OakEngineEncodingParams *params,
const char *path);
/** @brief Save parameters to a preset/XML file (same return convention). */
OAKENGINE_API int
oakengine_encoding_params_save_file(const OakEngineEncodingParams *params,
const char *path);
/* ---- Export execution / per-sequence last-used --------------------------- */
/**
* @brief Run a synchronous offline export with a params handle assembled
* through this family.
*
* Same blocking/progress/cancel semantics as oakengine_export_render_ex()
* (oakengine/exporter.h): progress via
* oakengine_export_set_progress_callback(), cancellation via
* oakengine_export_cancel(), failure reason via
* oakengine_export_last_error(). The output filename and image-sequence
* template come from the handle itself.
*
* @return OAKENGINE_OK / OAKENGINE_E_INVALID / OAKENGINE_E_STATE /
* OAKENGINE_E_FAILED / OAKENGINE_E_CANCELLED.
*/
OAKENGINE_API int
oakengine_export_render_with_params(OakEngineSequence *seq,
const OakEngineEncodingParams *params);
/**
* @brief Copy of the sequence's last-used encoding parameters
* (ViewerOutput::get_last_used_encoding_params()), or NULL when none is
* valid. Caller destroys with oakengine_encoding_params_destroy().
*/
OAKENGINE_API OakEngineEncodingParams *
oakengine_encoding_params_get_last_used(OakEngineSequence *seq);
/**
* @brief Store `params` as the sequence's last-used encoding parameters
* (ViewerOutput::set_last_used_encoding_params()); NULL is a no-op.
*/
OAKENGINE_API void oakengine_encoding_params_set_last_used(
OakEngineSequence *seq, const OakEngineEncodingParams *params);
/**
* @brief Start audio recording to the file described by `params`
* (AudioManager::start_recording(); audio must be enabled on the handle).
*
* @return OAKENGINE_OK on success; OAKENGINE_E_INVALID for bad arguments;
* OAKENGINE_E_STATE when the audio manager is not running;
* OAKENGINE_E_FAILED otherwise (a human-readable reason is written to
* `errbuf`/`errbuf_size` when given).
*/
OAKENGINE_API int
oakengine_encoding_start_audio_recording(const OakEngineEncodingParams *params,
char *errbuf, int errbuf_size);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_ENCODING_H */
+263
View File
@@ -0,0 +1,263 @@
/***
Oak - 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 OAKENGINE_EVENTS_H
#define OAKENGINE_EVENTS_H
#include <stdint.h>
#include "export.h"
#include "init.h"
#include "project.h"
#include "timeline.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file events.h
* @brief C ABI for engine change notifications (the signal/slot replacement)
*
* The engine's C++ API notifies observers through Qt signals (Project::
* modified_changed, Folder::begin_insert_item, Track::block_added, the
* sequence's track/marker/workarea notifications, ...). This family exposes
* the same notifications to C consumers as a subscription/callback
* mechanism, so the application never needs to connect() to an engine
* QObject directly.
*
* Usage:
*
* int64_t sub = oakengine_event_subscribe(handle, OAKENGINE_EVENT_..., fn,
* userdata);
* ...
* oakengine_event_unsubscribe(sub);
*
* `handle` is a borrowed facade handle whose static type depends on the
* event family (see the table below); a mismatch or NULL handle fails with
* 0 (an invalid subscription id). Subscribing the same (handle, event)
* twice is allowed and returns two independent subscription ids.
*
* Thread semantics: callbacks are invoked SYNCHRONOUSLY on the thread that
* emits the change (the equivalent of Qt::DirectConnection) before the
* engine's own emission returns, exactly like the C++ connections they
* replace. The callback runs under whatever locks the engine holds at the
* emission site; it must not call back into editing primitives that mutate
* the same object. All engine objects live on the GUI thread, so callbacks
* normally fire there.
*
* Lifetime: the registry drops the subscription automatically when the
* observed engine object is destroyed, so a stale subscription id is never
* a use-after-free; oakengine_event_unsubscribe() on an id whose object
* died is a harmless no-op returning OAKENGINE_E_NOT_FOUND. The inverse is
* NOT tracked: `userdata` ownership stays with the subscriber, which must
* unsubscribe (or tolerate callbacks) until its own teardown.
*
* Event payloads use POD fields only. Timestamps are frame numbers in the
* owning sequence's frame-rate timebase (same convention as timeline.h);
* `handle`/`source` are borrowed pointers the callee may use during the
* callback only.
*/
/**
* @brief Event ids for oakengine_event_subscribe().
*
* handle column: the facade handle to pass for that event.
* payload column: oakengine_event field contents on delivery.
*/
#define OAKENGINE_EVENT_PROJECT_MODIFIED_CHANGED 1 /**< handle: OakEngineProject*. a = new modified flag (0/1). */
#define OAKENGINE_EVENT_PROJECT_NAME_CHANGED 2 /**< handle: OakEngineProject*. no payload. */
#define OAKENGINE_EVENT_FOLDER_BEGIN_INSERT_ITEM 10 /**< handle: OakEngineNode* (a folder). handle field = child OakEngineNode*, a = insertion index. */
#define OAKENGINE_EVENT_FOLDER_END_INSERT_ITEM 11 /**< handle: OakEngineNode* (a folder). no payload. */
#define OAKENGINE_EVENT_FOLDER_BEGIN_REMOVE_ITEM 12 /**< handle: OakEngineNode* (a folder). handle field = child OakEngineNode*, a = child index. */
#define OAKENGINE_EVENT_FOLDER_END_REMOVE_ITEM 13 /**< handle: OakEngineNode* (a folder). no payload. */
#define OAKENGINE_EVENT_SEQUENCE_TRACK_ADDED 20 /**< handle: OakEngineSequence*. handle field = OakEngineTrack*, a = track type (OAKENGINE_TRACK_TYPE_*). */
#define OAKENGINE_EVENT_SEQUENCE_TRACK_REMOVED 21 /**< handle: OakEngineSequence*. handle field = OakEngineTrack*, a = track type. */
#define OAKENGINE_EVENT_SEQUENCE_TRACK_LIST_CHANGED 22 /**< handle: OakEngineSequence*. a = track type. Fired on TrackList::track_list_changed (order/label-affecting changes). */
#define OAKENGINE_EVENT_SEQUENCE_TRACK_HEIGHT_CHANGED 23 /**< handle: OakEngineSequence*. handle field = OakEngineTrack*, a = track type, b = new height in PIXELS (TrackList::track_height_changed). */
#define OAKENGINE_EVENT_SEQUENCE_SUBTITLES_CHANGED 24 /**< handle: OakEngineSequence*. a/b = changed range in/out (ts). */
#define OAKENGINE_EVENT_TRACK_BLOCK_ADDED 30 /**< handle: OakEngineTrack*. handle field = OakEngineBlock*, a = block in-point (ts), b = block out-point (ts). */
#define OAKENGINE_EVENT_TRACK_BLOCK_REMOVED 31 /**< handle: OakEngineTrack*. handle field = OakEngineBlock*, a = in (ts), b = out (ts) at removal time. */
#define OAKENGINE_EVENT_TRACK_INDEX_CHANGED 32 /**< handle: OakEngineTrack*. a = old index, b = new index. */
#define OAKENGINE_EVENT_TRACK_HEIGHT_CHANGED 33 /**< handle: OakEngineTrack*. a = int64 bit-cast of the new height (double, internal units; memcpy to decode). */
#define OAKENGINE_EVENT_TRACK_BLOCKS_REFRESHED 34 /**< handle: OakEngineTrack*. no payload (Track::blocks_refreshed). */
#define OAKENGINE_EVENT_TRACK_MUTED_CHANGED 35 /**< handle: OakEngineTrack*. a = muted 0/1. */
#define OAKENGINE_EVENT_BLOCK_ENABLED_CHANGED 36 /**< handle: OakEngineBlock*. no payload (Block::enabled_changed; re-read via oakengine_block_is_enabled). */
#define OAKENGINE_EVENT_BLOCK_PREVIEW_CHANGED 37 /**< handle: OakEngineBlock*. no payload (Block::preview_changed). */
#define OAKENGINE_EVENT_SEQUENCE_MARKER_ADDED 40 /**< handle: OakEngineSequence*. a = marker in-point (ts). */
#define OAKENGINE_EVENT_SEQUENCE_MARKER_REMOVED 41 /**< handle: OakEngineSequence*. a = marker in-point (ts). */
#define OAKENGINE_EVENT_SEQUENCE_MARKER_MODIFIED 42 /**< handle: OakEngineSequence*. a = marker in-point (ts). */
#define OAKENGINE_EVENT_SEQUENCE_WORKAREA_RANGE_CHANGED 50 /**< handle: OakEngineSequence*. a = in (ts), b = out (ts). */
#define OAKENGINE_EVENT_SEQUENCE_WORKAREA_ENABLED_CHANGED 51 /**< handle: OakEngineSequence*. a = enabled flag (0/1). */
#define OAKENGINE_EVENT_COLOR_MANAGER_CONFIG_CHANGED 60 /**< handle: OakEngineColorManager*. no payload. Fired when the OCIO config changes (ColorManager::config_changed). */
#define OAKENGINE_EVENT_COLOR_MANAGER_REFERENCE_SPACE_CHANGED 61 /**< handle: OakEngineColorManager*. no payload (ColorManager::reference_space_changed). */
/* Node family (handle: OakEngineNode*). `s` carries the input id where
* noted; frame timestamps use the same timebase as oakengine_node_frame_
* time_base() (the project's first sequence's frame rate). */
#define OAKENGINE_EVENT_NODE_LABEL_CHANGED 70 /**< s = new label. */
#define OAKENGINE_EVENT_NODE_INPUT_VALUE_CHANGED 71 /**< s = input id, a = element, b = range in (ts), c = range out (ts). */
#define OAKENGINE_EVENT_NODE_INPUT_CONNECTED 72 /**< handle = connected output OakEngineNode*, s = input id, a = element. */
#define OAKENGINE_EVENT_NODE_INPUT_DISCONNECTED 73 /**< handle = former output OakEngineNode*, s = input id, a = element. */
#define OAKENGINE_EVENT_NODE_INPUT_FLAGS_CHANGED 74 /**< s = input id, a = new flags (OAKENGINE_NODE_INPUT_FLAG_*). */
#define OAKENGINE_EVENT_NODE_INPUT_PROPERTY_CHANGED 75 /**< s = input id (property key/value intentionally omitted; re-read through the node family getters). */
#define OAKENGINE_EVENT_NODE_INPUT_DATA_TYPE_CHANGED 76 /**< s = input id, a = new oak_node_value_type. */
#define OAKENGINE_EVENT_NODE_INPUT_ARRAY_SIZE_CHANGED 77 /**< s = input id, a = old size, b = new size. */
#define OAKENGINE_EVENT_NODE_KEYFRAME_ENABLE_CHANGED 78 /**< s = input id, a = element, b = enabled (0/1). */
#define OAKENGINE_EVENT_NODE_KEYFRAME_ADDED 79 /**< handle = OakEngineKeyframe*, s = input id, a = element, b = track. */
#define OAKENGINE_EVENT_NODE_KEYFRAME_REMOVED 80 /**< handle = OakEngineKeyframe* (about to die; use the s/a/b fields, do not dereference), s = input id, a = element, b = track. */
#define OAKENGINE_EVENT_NODE_KEYFRAME_TIME_CHANGED 81 /**< handle = OakEngineKeyframe*. */
#define OAKENGINE_EVENT_NODE_KEYFRAME_TYPE_CHANGED 82 /**< handle = OakEngineKeyframe*. */
#define OAKENGINE_EVENT_NODE_KEYFRAME_VALUE_CHANGED 83 /**< handle = OakEngineKeyframe*. */
#define OAKENGINE_EVENT_NODE_NODE_ADDED_TO_CONTEXT 84 /**< handle = OakEngineNode* added to this context. */
#define OAKENGINE_EVENT_NODE_NODE_REMOVED_FROM_CONTEXT 85 /**< handle = OakEngineNode* removed from this context. */
#define OAKENGINE_EVENT_NODE_MESSAGE_COUNT_CHANGED 86 /**< no payload. */
/* Group family (handle: OakEngineNode*, must be a group). For 87/88 the
* handle field carries the passthrough's inner node, `s` its input id and
* `a` its element. */
#define OAKENGINE_EVENT_GROUP_INPUT_PASSTHROUGH_ADDED 87 /**< handle = inner OakEngineNode*, s = input id, a = element. */
#define OAKENGINE_EVENT_GROUP_INPUT_PASSTHROUGH_REMOVED 88 /**< handle = inner OakEngineNode*, s = input id, a = element. */
#define OAKENGINE_EVENT_GROUP_OUTPUT_PASSTHROUGH_CHANGED 89 /**< handle = new output OakEngineNode*. */
/**
* handle = OakEngineNode* whose position in this context changed; `a`/`b`
* carry the new x/y scene coordinates as int64 bit-casts of double (use
* memcpy to decode). */
#define OAKENGINE_EVENT_NODE_CONTEXT_POSITION_CHANGED 90
#define OAKENGINE_EVENT_NODE_LINKS_CHANGED 91 /**< no payload (Node::links_changed). */
#define OAKENGINE_EVENT_NODE_COLOR_CHANGED 92 /**< no payload (Node::color_changed). */
#define OAKENGINE_EVENT_NODE_INPUT_ADDED 93 /**< s = input id (Node::input_added). */
#define OAKENGINE_EVENT_NODE_INPUT_REMOVED 94 /**< s = input id (Node::input_removed). */
#define OAKENGINE_EVENT_NODE_REMOVED_FROM_GRAPH 95 /**< handle = project OakEngineNode* (Node::removed_from_graph). */
/* Viewer family (handle: OakEngineNode*, must be a viewer -- validate with
* oakengine_viewer_from_node()). Rational payloads (seconds) are carried
* as a = numerator, b = denominator. */
#define OAKENGINE_EVENT_VIEWER_LENGTH_CHANGED 100 /**< a/b = new length. */
#define OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED 101 /**< a/b = new playhead. */
#define OAKENGINE_EVENT_VIEWER_FRAME_RATE_CHANGED 102 /**< a/b = new frame rate (NOT flipped). */
#define OAKENGINE_EVENT_VIEWER_SIZE_CHANGED 103 /**< a = width, b = height. */
#define OAKENGINE_EVENT_VIEWER_PIXEL_ASPECT_CHANGED 104 /**< a/b = new pixel aspect. */
#define OAKENGINE_EVENT_VIEWER_INTERLACING_CHANGED 105 /**< a = olive::VideoParams::Interlacing. */
#define OAKENGINE_EVENT_VIEWER_VIDEO_PARAMS_CHANGED 106 /**< no payload. */
#define OAKENGINE_EVENT_VIEWER_AUDIO_PARAMS_CHANGED 107 /**< no payload. */
#define OAKENGINE_EVENT_VIEWER_TEXTURE_INPUT_CHANGED 108 /**< no payload. */
#define OAKENGINE_EVENT_VIEWER_SAMPLE_RATE_CHANGED 109 /**< a = new sample rate. */
#define OAKENGINE_EVENT_VIEWER_CONNECTED_WAVEFORM_CHANGED 110 /**< no payload. */
/* Marker list family (handle: OakEngineMarkerList*, from
* oakengine_viewer_get_marker_list()). handle field = the OakEngineMarker*
* (for REMOVED it is about to die; do not dereference). */
#define OAKENGINE_EVENT_MARKER_LIST_MARKER_ADDED 111
#define OAKENGINE_EVENT_MARKER_LIST_MARKER_REMOVED 112
#define OAKENGINE_EVENT_MARKER_LIST_MARKER_MODIFIED 113
/* Workarea family (handle: OakEngineWorkarea*, borrowed from
* oakengine_viewer_get_workarea_handle() or owned from
* oakengine_workarea_create()). */
#define OAKENGINE_EVENT_WORKAREA_RANGE_CHANGED 114 /**< no payload; re-read via oakengine_workarea_get(). */
#define OAKENGINE_EVENT_WORKAREA_ENABLED_CHANGED 115 /**< a = enabled 0/1. */
/* Task manager family (handle: oakengine_task_manager_handle(), see
* oakengine/task.h). The handle field carries the OakEngineTask* (for
* REMOVED it is about to die; do not dereference). */
#define OAKENGINE_EVENT_TASK_MANAGER_TASK_ADDED 120 /**< handle = OakEngineTask*, s = task title. */
#define OAKENGINE_EVENT_TASK_MANAGER_TASK_REMOVED 121 /**< handle = OakEngineTask* (about to die; do not dereference). */
#define OAKENGINE_EVENT_TASK_MANAGER_TASK_FAILED 122 /**< handle = OakEngineTask*. */
#define OAKENGINE_EVENT_TASK_MANAGER_LIST_CHANGED 123 /**< no payload. */
/* Task family (handle: OakEngineTask*, see oakengine/task.h). Delivered
* synchronously on the thread the task runs on. */
#define OAKENGINE_EVENT_TASK_STARTED 125 /**< a = start time (msecs since epoch). */
#define OAKENGINE_EVENT_TASK_PROGRESS 126 /**< a = int64 bit-cast of the progress double 0..1 (memcpy to decode). */
#define OAKENGINE_EVENT_TASK_FINISHED 127 /**< a = succeeded 0/1. */
/* Undo stack family (handle: oakengine_undo_handle(), see
* oakengine/undo.h). Fires after every stack mutation (push/undo/redo/
* jump/clear); re-read the command list through the oakengine_undo_*
* accessors. */
#define OAKENGINE_EVENT_UNDO_INDEX_CHANGED 130 /**< a = new index (done-command count). */
/* AudioManager family (handle: oakengine_audio_manager_handle(), see
* oakengine/audio.h). Fired when the output device or format changes. */
#define OAKENGINE_EVENT_AUDIO_MANAGER_OUTPUT_PARAMS_CHANGED 140 /**< no payload. */
#define OAKENGINE_EVENT_AUDIO_MANAGER_OUTPUT_NOTIFY 141 /**< no payload; emitted after each notify interval of audio has been consumed. */
/* ---- Playback cache / frame cache (B9c) ----------------------------------- */
#define OAKENGINE_EVENT_PLAYBACK_CACHE_INVALIDATED 141
#define OAKENGINE_EVENT_PLAYBACK_CACHE_VALIDATED 142
#define OAKENGINE_EVENT_FRAME_CACHE_INVALIDATED 143
/**
* @brief POD event payload delivered to oakengine_event_fn.
*/
typedef struct oakengine_event {
int32_t id; /**< Event id (OAKENGINE_EVENT_*). */
int32_t reserved; /**< Alignment padding; 0. */
int64_t a; /**< Event-specific integer payload (see the event table). */
int64_t b; /**< Event-specific second integer payload. */
int64_t c; /**< Event-specific third integer payload. */
void *source; /**< The subscribed handle the event was delivered for (borrowed). */
void *handle; /**< Related object, event-specific (borrowed; NULL when none). */
const char *s; /**< Event-specific string payload (valid only during the callback; NULL when none). */
} oakengine_event;
/**
* @brief Change-notification callback. Invoked synchronously on the
* emitting thread; `event` is valid only for the duration of the call.
*/
typedef void (*oakengine_event_fn)(const oakengine_event *event,
void *userdata);
/**
* @brief Subscribe to `event_id` on `handle` (an OakEngineProject*,
* OakEngineSequence*, OakEngineTrack* or OakEngineNode* per the event
* table) and return a subscription id (> 0).
*
* Returns 0 on failure: NULL handle/callback, unknown event id, or a
* handle whose engine object does not match the event's family. The
* callback starts firing with the next matching change; there is no
* replay of past state.
*/
OAKENGINE_API int64_t oakengine_event_subscribe(void *handle, int32_t event_id,
oakengine_event_fn fn,
void *userdata);
/**
* @brief Cancel a subscription. OAKENGINE_OK on success,
* OAKENGINE_E_INVALID for `id` <= 0, OAKENGINE_E_NOT_FOUND for an id that
* was never registered or whose engine object has since been destroyed.
*/
OAKENGINE_API int oakengine_event_unsubscribe(int64_t id);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_EVENTS_H */
+81
View File
@@ -117,6 +117,24 @@ typedef struct oak_footage_audio_info {
int time_base_den; /**< Seconds per time-base unit (denominator). */
} oak_footage_audio_info;
/**
* @brief POD proxy generation parameters (olive::ProxyManager::ProxyParams).
*
* divider: source resolution divider (1 = use absolute width/height,
* 2/4/8 = fraction of the source resolution). extension/preset are the
* ffmpeg output container and encoder preset (e.g. "mp4"/"veryfast").
*/
typedef struct oak_proxy_params {
int width;
int height;
int divider;
int version;
int crf;
int include_audio; /**< 1/0. */
char extension[32];
char preset[32];
} oak_proxy_params;
/**
* @brief Probe a media file (decoder, streams, durations, color tags).
*
@@ -417,6 +435,69 @@ oakengine_footage_colorspace_count(const OakEngineFootage *self);
OAKENGINE_API int oakengine_footage_colorspace_at(
const OakEngineFootage *self, int index, char *buf, int buf_size);
/* ---- Footage extras ------------------------------------------------------- */
/** @brief Filename of the imported footage (buf/size). Returns
* OAKENGINE_E_INVALID on NULL. */
OAKENGINE_API int oakengine_footage_get_filename(const OakEngineFootage *self,
char *buf, int buf_size);
/** @brief Get the (track_type, stream_index) for the real stream at
* `stream_index_in_footage` (which iterates all streams regardless of type).
* Returns OAKENGINE_OK or OAKENGINE_E_NOT_FOUND. */
OAKENGINE_API int oakengine_footage_get_stream_reference(
const OakEngineFootage *self, int stream_index_in_footage,
int *out_track_type, int *out_stream_index);
/** @brief Human-readable description of a video stream (buf/size).
* Returns OAKENGINE_E_NOT_FOUND for an out-of-range index. */
OAKENGINE_API int oakengine_footage_describe_video_stream(
const OakEngineFootage *self, int video_stream_index, char *buf,
int buf_size);
/** @brief Human-readable description of an audio stream (buf/size).
* Returns OAKENGINE_E_NOT_FOUND for an out-of-range index. */
OAKENGINE_API int oakengine_footage_describe_audio_stream(
const OakEngineFootage *self, int audio_stream_index, char *buf,
int buf_size);
/** @brief Human-readable name of a stream type
* (OAKENGINE_TRACK_TYPE_* -> translated name). buf/size convention. */
OAKENGINE_API int oakengine_footage_stream_type_name(int track_type, char *buf,
int buf_size);
/** @brief 1 if the footage has custom proxy parameters, 0 otherwise. */
OAKENGINE_API int oakengine_footage_has_custom_proxy_params(
const OakEngineFootage *self);
/** @brief Fill `out` with the effective proxy parameters
* (custom if set, otherwise the application defaults). */
OAKENGINE_API int oakengine_footage_get_effective_proxy_params(
const OakEngineFootage *self, oak_proxy_params *out);
/** @brief Set custom proxy parameters (not undoable). */
OAKENGINE_API int oakengine_footage_set_custom_proxy_params(
OakEngineFootage *self, const oak_proxy_params *params);
/** @brief Clear custom proxy parameters, reverting to defaults. */
OAKENGINE_API int oakengine_footage_clear_custom_proxy_params(
OakEngineFootage *self);
/** @brief Generate a proxy with the given parameters (synchronous).
* `path` is the proxy file path, `state` the proxy state (0=missing,
* 1=generating, 2=ready, 3=failed), `stream_index` the video stream index,
* `enabled` 1/0 to enable proxy, `version` the preset version. */
OAKENGINE_API int oakengine_footage_set_proxy(OakEngineFootage *self,
const char *path, int state,
int stream_index, int enabled,
int version);
/** @brief Delete the proxy file and reset state. */
OAKENGINE_API int oakengine_footage_clear_proxy(OakEngineFootage *self);
/** @brief Invalidate the footage (force re-probe on next use). */
OAKENGINE_API int oakengine_footage_invalidate(OakEngineFootage *self);
#ifdef __cplusplus
}
#endif
+163
View File
@@ -0,0 +1,163 @@
/***
Oak - 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 OAKENGINE_GIZMO_H
#define OAKENGINE_GIZMO_H
#include <stdint.h>
#include "export.h"
#include "node.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file gizmo.h
* @brief C ABI for gizmo data exchange (text gizmo POD + draggable helpers)
*
* TextGizmo has been POD-ified: the app retrieves a flat snapshot of the
* text v3 node's gizmo state through a single C call instead of holding a
* C++ TextGizmo pointer. The 4 Qt signals (activated/deactivated/
* rect_changed/vertical_alignment_changed) are replaced by the existing
* OAKENGINE_EVENT_NODE_INPUT_VALUE_CHANGED on the text v3 node.
*
* DraggableGizmo's drag lifecycle (start/move/end) is exposed as thin C
* wrappers so the app can drive dragging without importing engine C++ symbols.
*/
/**
* @brief Flat snapshot of a text v3 node's gizmo state.
*
* Retrieved via oakengine_text_gizmo_get(). The HTML content is accessed
* separately through oakengine_text_gizmo_get_html() because it is a
* variable-length string.
*/
typedef struct oakengine_text_gizmo {
double rect_x; /**< Bounding rect left */
double rect_y; /**< Bounding rect top */
double rect_w; /**< Bounding rect width */
double rect_h; /**< Bounding rect height */
int vertical_alignment; /**< 0 = AlignTop, 1 = AlignBottom, 2 = AlignVCenter */
} oakengine_text_gizmo;
/**
* @brief Retrieve the text gizmo POD from a TextGeneratorV3 node.
*
* @param node The text v3 node (OakEngineNode*). Must be a TextGeneratorV3
* (checked at runtime; returns OAKENGINE_E_INVALID otherwise).
* @param time_num Numerator of the rational time at which to evaluate.
* @param time_den Denominator of the rational time.
* @param out Output struct filled on success.
* @return OAKENGINE_OK on success, OAKENGINE_E_INVALID if node is not a
* TextGeneratorV3 or out is NULL.
*/
OAKENGINE_API int oakengine_text_gizmo_get(OakEngineNode *node,
int64_t time_num, int64_t time_den, oakengine_text_gizmo *out);
/**
* @brief Retrieve the text gizmo's HTML content as a string.
*
* buf/size convention: pass NULL/0 to get the required length (including
* NUL terminator). On success returns the number of bytes written (excluding
* NUL). Requires a valid TextGeneratorV3 node.
*/
OAKENGINE_API int oakengine_text_gizmo_get_html(OakEngineNode *node,
int64_t time_num, int64_t time_den, char *buf, int buf_size);
/**
* @brief Update the HTML content of a text v3 node's text input (undoable).
*
* Equivalent to the old TextGizmo::update_input_html().
*/
OAKENGINE_API int oakengine_text_gizmo_update_html(OakEngineNode *node,
const char *html, int64_t time_num, int64_t time_den);
/**
* @brief Set the vertical alignment of a text v3 node (undoable).
*
* `alignment`: 0 = AlignTop, 1 = AlignBottom, 2 = AlignVCenter.
* Equivalent to the old TextGizmo::set_vertical_alignment().
*/
OAKENGINE_API int oakengine_text_gizmo_set_vertical_alignment(
OakEngineNode *node, int alignment);
/**
* @brief Notify that a text gizmo has been activated (emits the equivalent of
* the old TextGizmo::activated signal via event mechanism).
*
* Currently a no-op since activation events are app-internal; kept for
* API completeness.
*/
OAKENGINE_API int oakengine_text_gizmo_activated(OakEngineNode *node);
/**
* @brief Notify that a text gizmo has been deactivated.
*/
OAKENGINE_API int oakengine_text_gizmo_deactivated(OakEngineNode *node);
/**
* @brief Activate/Deactivate the text gizmo on a text v3 node.
*
* These replace the old TextGizmo::activated()/deactivated() signal emissions.
* The app calls these to notify the engine that the text editor opened/closed.
*/
/**
* @brief Get the DragValueBehavior of a gizmo node.
*
* Returns: 0 = k_absolute, 1 = k_delta_from_previous, 2 = k_delta_from_start.
* Returns OAKENGINE_E_INVALID if the gizmo is not a DraggableGizmo.
*/
OAKENGINE_API int oakengine_gizmo_get_drag_value_behavior(void *gizmo);
/**
* @brief Start a drag on a DraggableGizmo.
*
* Wraps DraggableGizmo::drag_start(). The gizmo's internal NodeInputDraggers
* are started at the given time. `row` is a pointer to a NodeValueRow
* (populated e.g. by oakengine_traverse_generate_row); pass NULL for an
* empty row.
*/
OAKENGINE_API int oakengine_gizmo_drag_start(void *gizmo,
void *row, double abs_x, double abs_y, int64_t time_num,
int64_t time_den);
/**
* @brief Move a drag (emits handle_movement signal on the gizmo).
*/
OAKENGINE_API int oakengine_gizmo_drag_move(void *gizmo,
double x, double y, int qt_keyboard_modifiers);
/**
* @brief End a drag and push an undoable command.
*
* @param gizmo The DraggableGizmo pointer (void* for C ABI).
* @param command A MultiUndoCommand* (void*) to append undo entries to.
* Pass NULL to create a standalone command.
*/
OAKENGINE_API int oakengine_gizmo_drag_end(void *gizmo, void *command);
#ifdef __cplusplus
}
#endif
#endif // OAKENGINE_GIZMO_H
+87
View File
@@ -0,0 +1,87 @@
/***
Oak - 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 OAKENGINE_LUT_H
#define OAKENGINE_LUT_H
#include "export.h"
#include "init.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file lut.h
* @brief C ABI for the global LUT file library (olive::LUTLibrary)
*
* A thin facade over the user-configurable list of LUT directories and the
* supported LUT files discovered under them. The library state is kept in the
* application config ("LUTLibraryPaths"); this facade only exposes the
* directory/file list queries and the directory replacement primitive.
*
* Conventions match the other facade families:
* - 0 (OAKENGINE_OK) / negative OAKENGINE_E_* codes.
* - String output uses the buf/size convention.
* - Count queries return a non-negative integer, or a negative error code.
*/
/**
* @brief Number of directories currently in the LUT library.
*/
OAKENGINE_API int oakengine_lut_directory_count(void);
/**
* @brief Get the directory path at `index` (buf/size convention).
*
* Returns the string length on success, or a negative OAKENGINE_E_* code when
* `index` is out of range.
*/
OAKENGINE_API int oakengine_lut_directory_at(int index, char *buf,
int buf_size);
/**
* @brief Number of supported LUT files found under the library directories.
*/
OAKENGINE_API int oakengine_lut_file_count(void);
/**
* @brief Get the full path of the LUT file at `index` (buf/size convention).
*
* Files are listed in the order they are discovered; files in earlier
* directories come first. Returns the string length on success, or a negative
* OAKENGINE_E_* code when `index` is out of range.
*/
OAKENGINE_API int oakengine_lut_file_at(int index, char *buf, int buf_size);
/**
* @brief Replace the LUT library directories and persist them to config.
*
* `dirs` is an array of `count` NUL-terminated UTF-8 directory paths. Passing
* `count == 0` clears the library. Returns OAKENGINE_OK or an error code.
*/
OAKENGINE_API int oakengine_lut_set_directories(const char *const *dirs,
int count);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_LUT_H */
File diff suppressed because it is too large Load Diff
+74
View File
@@ -0,0 +1,74 @@
/***
Oak - 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 OAKENGINE_PLUGIN_H
#define OAKENGINE_PLUGIN_H
#include "export.h"
#include "init.h"
#include "node.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file plugin.h
* @brief C ABI for plugin support (active viewer, progress reporter, loading)
*/
/* ---- Active viewer provider -------------------------------------------- */
/** @brief Returns the currently active viewer node (or NULL). */
typedef OakEngineNode *(*oakengine_plugin_active_viewer_fn)(void *userdata);
OAKENGINE_API int oakengine_plugin_set_active_viewer_provider(
oakengine_plugin_active_viewer_fn fn, void *userdata);
/* ---- Progress reporter factory ----------------------------------------- */
typedef void *(*oakengine_plugin_reporter_create_fn)(
const char *message, const char *title, void *userdata);
typedef void (*oakengine_plugin_reporter_destroy_fn)(
void *reporter, void *userdata);
typedef int (*oakengine_plugin_reporter_is_cancelled_fn)(
void *reporter, void *userdata);
typedef void (*oakengine_plugin_reporter_set_progress_fn)(
void *reporter, double progress, void *userdata);
OAKENGINE_API int oakengine_plugin_set_progress_reporter_factory(
oakengine_plugin_reporter_create_fn create,
oakengine_plugin_reporter_destroy_fn destroy,
oakengine_plugin_reporter_is_cancelled_fn is_cancelled,
oakengine_plugin_reporter_set_progress_fn set_progress,
void *userdata);
/* ---- Plugin loading and interaction ------------------------------------ */
OAKENGINE_API int oakengine_plugin_load_plugins(const char *path);
OAKENGINE_API int oakengine_plugin_node_push_button_clicked(
OakEngineNode *node, const char *button_id);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_PLUGIN_H */
+120
View File
@@ -62,6 +62,24 @@ extern "C" {
#define OAKENGINE_LOOP_MODE_LOOP 1 /**< Repeat the clip (olive k_loop_mode_loop). */
#define OAKENGINE_LOOP_MODE_CLAMP 2 /**< Hold first/last frame (olive k_loop_mode_clamp). */
/**
* @brief Opaque preview request handle (an active render ticket for
* single-frame or audio-range preview).
*/
typedef struct OakEnginePreviewRequest OakEnginePreviewRequest;
/**
* @brief POD for a single video frame from a preview request
* (borrowed data, valid until the request is freed).
*/
typedef struct oak_playback_frame {
int width;
int height;
int format; /**< olive::PixelFormat::Format value. */
const void *data; /**< Planar data pointer (first plane). */
int linesize; /**< Bytes per row of the first plane. */
} oak_playback_frame;
/**
* @brief Human-readable reason for the last failed preview call on this
* thread (buf/size convention).
@@ -118,6 +136,108 @@ OAKENGINE_API int oakengine_preview_get_waveform_summary(
OakEngineFootage *footage, int channel, int64_t start_ts,
int64_t end_ts, double *min_vals, double *max_vals, int count);
/* ---- R4: waveform, audio levels, cacher, preview requests ------------------ */
/** @brief Maximum sample rate for waveform generation. > 0. */
OAKENGINE_API int oakengine_waveform_max_sample_rate(void);
/**
* @brief Analyze audio levels (linear RMS) from raw float sample data.
* `data` is an array of `channels` float pointers, each with `count` samples.
* Writes RMS values into `levels` (one per channel). Returns OAKENGINE_OK
* or OAKENGINE_E_INVALID on NULL/bad arguments.
*/
OAKENGINE_API int oakengine_audio_analyze_levels(const float *const *data,
int channels, int64_t count,
double *levels);
/**
* @brief Set the preview cacher's playhead position (num/den seconds).
* Returns OAKENGINE_E_STATE when the cacher is not available.
*/
OAKENGINE_API int oakengine_preview_cacher_set_playhead(int64_t num,
int64_t den);
/**
* @brief Pause or resume thumbnail generation in the cacher.
* Returns OAKENGINE_E_STATE when the cacher is not available.
*/
OAKENGINE_API int oakengine_preview_cacher_set_thumbnails_paused(int paused);
/**
* @brief Clear pending single-frame render requests from the cacher.
* Returns OAKENGINE_E_STATE when the cacher is not available.
*/
OAKENGINE_API int
oakengine_preview_cacher_clear_single_frame_renders(int only_finished);
/**
* @brief Force the cacher to cache a range (num/den seconds in/out).
* Returns OAKENGINE_E_INVALID on NULL node.
*/
OAKENGINE_API int oakengine_preview_cacher_force_cache_range(
OakEngineNode *node, int64_t in_num, int64_t in_den, int64_t out_num,
int64_t out_den);
/**
* @brief Request a single video frame at (num/den) seconds from `viewer`.
* Returns a request handle (caller owns it, must free) or NULL on failure.
*/
OAKENGINE_API OakEnginePreviewRequest *
oakengine_preview_request_single_frame(OakEngineNode *viewer, int64_t num,
int64_t den, int dry);
/**
* @brief Request an audio range (num/den seconds in/out) from `viewer`.
* Returns a request handle (caller owns it, must free) or NULL on failure.
*/
OAKENGINE_API OakEnginePreviewRequest *
oakengine_preview_request_audio_range(OakEngineNode *viewer, int64_t in_num,
int64_t in_den, int64_t out_num,
int64_t out_den);
/** @brief 1 if the request is done, 0 otherwise. 0 on NULL. */
OAKENGINE_API int oakengine_preview_request_is_done(
const OakEnginePreviewRequest *req);
/** @brief 1 if the request has a result, 0 otherwise. 0 on NULL. */
OAKENGINE_API int oakengine_preview_request_has_result(
const OakEnginePreviewRequest *req);
/** @brief Set a finished callback (called when the ticket completes).
* `callback` receives `user_data`. Returns OAKENGINE_E_INVALID on NULL
* request. */
OAKENGINE_API int oakengine_preview_request_set_finished_callback(
OakEnginePreviewRequest *req, void (*callback)(void *),
void *user_data);
/** @brief Copy the frame data into `out`. Returns OAKENGINE_OK or
* OAKENGINE_E_INVALID when the request has no video frame result. */
OAKENGINE_API int oakengine_preview_request_get_frame(
OakEnginePreviewRequest *req, oak_playback_frame *out);
/** @brief Number of audio channels in the result, or 0 if none. */
OAKENGINE_API int oakengine_preview_request_get_audio_channel_count(
const OakEnginePreviewRequest *req);
/** @brief Sample rate of the audio result, or 0 if none. */
OAKENGINE_API int oakengine_preview_request_get_audio_sample_rate(
const OakEnginePreviewRequest *req);
/**
* @brief Get audio sample data from the result.
* `channel` is the 0-based channel index. Writes up to `max_samples` float
* values into `samples`. Returns the number of samples written, or
* OAKENGINE_E_INVALID on bad arguments.
*/
OAKENGINE_API int oakengine_preview_request_get_audio_samples(
OakEnginePreviewRequest *req, int channel, const float *samples,
int max_samples);
/** @brief Free a preview request handle (NULL-safe). */
OAKENGINE_API void oakengine_preview_request_free(
OakEnginePreviewRequest *req);
#ifdef __cplusplus
}
#endif
+127
View File
@@ -24,6 +24,12 @@
#include "export.h"
#include "init.h"
/* Forward declarations from node.h (included by callers in either order). */
typedef struct OakEngineNode OakEngineNode;
/* Forward declaration for playback cache from viewer.h. */
typedef struct OakEnginePlaybackCache OakEnginePlaybackCache;
#ifdef __cplusplus
extern "C" {
#endif
@@ -195,6 +201,127 @@ oakengine_project_sequence_count(const OakEngineProject *self);
OAKENGINE_API OakEngineSequence *
oakengine_project_sequence_at(const OakEngineProject *self, int index);
/* ---- Folder operations ---------------------------------------------------- */
/**
* @brief Create a folder node named `name` under `parent` in `project`.
* Returns a borrowed handle, or NULL on failure.
*/
OAKENGINE_API OakEngineNode *oakengine_folder_create(OakEngineProject *project,
OakEngineNode *parent,
const char *name);
/**
* @brief 1 if `folder` recursively contains `child`, 0 otherwise.
* 0 when either handle is NULL or `folder` is not a Folder.
*/
OAKENGINE_API int oakengine_folder_has_child_recursive(
const OakEngineNode *folder, const OakEngineNode *child);
/**
* @brief Index of `child` in `folder`'s direct children, or
* OAKENGINE_E_NOT_FOUND. Returns OAKENGINE_E_INVALID when
* `folder` is not a Folder node.
*/
OAKENGINE_API int oakengine_folder_index_of_child(
const OakEngineNode *folder, const OakEngineNode *child);
/**
* @brief Static input key string for Folder children (Folder::k_child_input).
* Never freed.
*/
OAKENGINE_API const char *oakengine_folder_child_input_key(void);
/**
* @brief Add `child` to `folder` (undoable). OAKENGINE_E_INVALID when
* `folder` is not a Folder node or on NULL args.
*/
OAKENGINE_API int oakengine_folder_add_child(OakEngineNode *folder,
OakEngineNode *child);
/**
* @brief Move `node` from its current folder to `new_folder` (undoable).
* Removes the node from its old folder first — a true move, not a copy.
* Returns OAKENGINE_OK or a negative error code.
*/
OAKENGINE_API int oakengine_folder_move_child(OakEngineNode *node,
OakEngineNode *new_folder);
/**
* @brief Create a Folder::RemoveElementCommand as an opaque command pointer.
* Returns NULL on invalid arguments.
*/
OAKENGINE_API void *oakengine_folder_remove_element_command(
OakEngineNode *folder, OakEngineNode *child);
/**
* @brief Move several nodes into `dest_folder` as ONE undoable command
* (each node is removed from its old folder, then added to `dest_folder`).
* Nodes already directly inside `dest_folder` are skipped. `undo_name`
* may be NULL. Returns OAKENGINE_OK or a negative error code.
*/
OAKENGINE_API int oakengine_folder_move_children(
OakEngineNode *const *nodes, int count, OakEngineNode *dest_folder,
const char *undo_name);
/* ---- Project extras ------------------------------------------------------- */
/** @brief Root folder node of the project (Project::root()). */
OAKENGINE_API OakEngineNode *oakengine_project_root(OakEngineProject *self);
/** @brief Display name for the project that is safe for window titles
* (Project::pretty_filename()). buf/size convention. */
OAKENGINE_API int oakengine_project_pretty_filename(const OakEngineProject *self,
char *buf, int buf_size);
/** @brief Set the project's filename (Project::set_filename()).
* Returns OAKENGINE_OK or OAKENGINE_E_INVALID on NULL. */
OAKENGINE_API int oakengine_project_set_filename(OakEngineProject *self,
const char *path);
/** @brief The project's default cache directory (Project::cache_path()).
* buf/size convention. */
OAKENGINE_API int oakengine_project_cache_path(const OakEngineProject *self,
char *buf, int buf_size);
/** @brief The project's alongside cache directory
* (Project::cache_alongside_path()). buf/size convention. */
OAKENGINE_API int oakengine_project_cache_alongside_path(
const OakEngineProject *self, char *buf, int buf_size);
/** @brief Set a custom cache directory path (Project::set_custom_cache_path()).
* NULL clears it. */
OAKENGINE_API int oakengine_project_set_custom_cache_path(
OakEngineProject *self, const char *path);
/** @brief Get the custom cache directory path, or "" when none is set.
* buf/size convention; returns 0 when no custom path is set. */
OAKENGINE_API int oakengine_project_get_custom_cache_path(
const OakEngineProject *self, char *buf, int buf_size);
/** @brief Cache location setting enum value
* (Project::get_cache_location_setting()). Returns < 0 on NULL. */
OAKENGINE_API int oakengine_project_get_cache_location_setting(
const OakEngineProject *self);
/** @brief Static MIME type string for project items (Project::item_mime_type()).
* Never freed. */
OAKENGINE_API const char *oakengine_project_item_mime_type(void);
/** @brief Resolve a project node to its owning OakEngineProject
* (Project::get_project_from_object()). Returns NULL when the node is
* not part of a project or on NULL input. */
OAKENGINE_API OakEngineProject *
oakengine_project_from_object(const OakEngineNode *node);
/** @brief Get the project's color reference space name (buf/size). */
OAKENGINE_API int oakengine_project_get_color_reference_space(
const OakEngineProject *self, char *buf, int buf_size);
/** @brief Set the project's color reference space (undoable). */
OAKENGINE_API int oakengine_project_set_color_reference_space(
OakEngineProject *self, const char *colorspace);
#ifdef __cplusplus
}
#endif
+131
View File
@@ -0,0 +1,131 @@
/***
Oak - 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 OAKENGINE_PROXY_H
#define OAKENGINE_PROXY_H
#include <stdint.h>
#include "export.h"
#include "footage.h"
#include "init.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file proxy.h
* @brief C ABI for the engine's proxy generation singleton (olive::ProxyManager)
*
* A thin facade over ProxyManager's instance lifecycle, proxy parameter
* configuration, proxy state queries and proxy generation. The opaque task
* handle returned in oak_proxy_result::task is a borrowed pointer to the
* engine's internal ProxyTask; it is intended only for logging and becomes
* invalid when the proxy operation finishes.
*
* Conventions match the other facade families:
* - 0 (OAKENGINE_OK) / negative OAKENGINE_E_* codes.
* - String output uses the buf/size convention.
* - Booleans are int (1/0).
*/
#define OAKENGINE_PROXY_STATE_MISSING 0
#define OAKENGINE_PROXY_STATE_GENERATING 1
#define OAKENGINE_PROXY_STATE_READY 2
#define OAKENGINE_PROXY_STATE_FAILED 3
typedef struct oak_proxy_result {
int state; /**< OAKENGINE_PROXY_STATE_* */
char filename[1024];
int64_t task; /**< ProxyTask* as opaque handle, or 0 if none */
} oak_proxy_result;
/**
* @brief Create the ProxyManager singleton.
*
* Safe to call when the instance already exists (no-op). Returns
* OAKENGINE_OK or OAKENGINE_E_FAILED.
*/
OAKENGINE_API int oakengine_proxy_create_instance(void);
/**
* @brief Destroy the ProxyManager singleton.
*
* Safe to call when no instance exists (no-op). Returns OAKENGINE_OK.
*/
OAKENGINE_API int oakengine_proxy_destroy_instance(void);
/**
* @brief Build proxy parameters from the global application config.
*
* Fills `out` with the configured width/height/divider/version/crf/extension
* /preset/include_audio values. Returns OAKENGINE_OK or an error code.
*/
OAKENGINE_API int oakengine_proxy_params_from_config(oak_proxy_params *out);
/**
* @brief Query the state of a proxy file on disk.
*
* Returns one of the OAKENGINE_PROXY_STATE_* values, or
* OAKENGINE_PROXY_STATE_MISSING if `proxy_filename` is NULL/empty or the
* proxy does not exist.
*/
OAKENGINE_API int oakengine_proxy_get_state(const char *proxy_filename);
/**
* @brief Human-readable string for a proxy state (buf/size convention).
*
* Returns the string length on success, or a negative OAKENGINE_E_* code for
* an unknown state.
*/
OAKENGINE_API int oakengine_proxy_state_to_string(int state, char *buf,
int buf_size);
/**
* @brief Get or start generating a proxy for `source_filename`.
*
* `cache_path` is the project cache directory. `stream_index` is the source
* stream to proxy. `params` are the proxy generation parameters (width/height
* etc.). On return `out->state` and `out->filename` describe the proxy; if a
* generation task was started, `out->task` is a borrowed opaque handle to it,
* otherwise it is 0.
*/
OAKENGINE_API int oakengine_proxy_get_or_start(const char *cache_path,
const char *source_filename,
int stream_index,
const oak_proxy_params *params,
oak_proxy_result *out);
/**
* @brief Get the "working" filename for a proxy file (buf/size convention).
*
* The working filename is used by the proxy generator while the proxy is being
* generated. Returns the string length on success, or a negative
* OAKENGINE_E_* code on error.
*/
OAKENGINE_API int oakengine_proxy_get_working_filename(const char *proxy_filename,
char *buf, int buf_size);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_PROXY_H */
+39
View File
@@ -87,6 +87,45 @@ typedef struct OakEngineFrame OakEngineFrame;
*/
typedef struct OakEngineAudioBuffer OakEngineAudioBuffer;
/**
* @brief Set aggressive garbage collection on the render manager
* (RenderManager::set_aggressive_garbage_collection()). Returns
* OAKENGINE_E_STATE when the render manager is not available.
*/
OAKENGINE_API int
oakengine_render_manager_set_aggressive_garbage_collection(int aggressive);
/**
* @brief The render backend that was requested (RenderManager::requested_backend()).
* Returns 0 (k_open_gl) when the render manager is not available.
*/
OAKENGINE_API int oakengine_render_manager_requested_backend(void);
/**
* @brief Convert a render backend enum value to a human-readable string
* (RenderManager::backend_to_string()). buf/size convention. Returns the
* would-be length or a negative error code.
*/
OAKENGINE_API int oakengine_render_manager_backend_to_string(int backend,
char *buf,
int buf_size);
/**
* @brief Set the display color processor on the render manager's cacher.
* `processor` is a borrowed OakEngineColorProcessor handle (NULL to clear).
* Returns OAKENGINE_OK or OAKENGINE_E_STATE.
*/
OAKENGINE_API int oakengine_render_cache_set_display_color_processor(
void *processor);
/**
* @brief Set the multicam node on the render manager's cacher.
* `node` is a borrowed OakEngineNode handle (NULL to clear).
* Returns OAKENGINE_OK or OAKENGINE_E_STATE.
*/
OAKENGINE_API int oakengine_render_cache_set_multicam_node(
OakEngineNode *node);
/**
* @brief Create a renderer for `seq` producing `width`x`height` frames of
* `pixel_format` at the given frame rate.
+254
View File
@@ -0,0 +1,254 @@
/***
Oak - 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 OAKENGINE_SERIALIZER_H
#define OAKENGINE_SERIALIZER_H
#include "export.h"
#include "init.h"
#include "node.h"
#include "project.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file serializer.h
* @brief C ABI for project serialization / copy-paste (olive::ProjectSerializer)
*
* A thin facade over ProjectSerializer's load/save/copy/paste primitives. The
* opaque OakEngineClipboard handle bundles a SaveData object (for copy/save)
* or the LoadData result of the last paste operation. Clipboard handles are
* owned by the caller and must be released with oakengine_clipboard_free().
*
* Conventions match the other facade families:
* - 0 (OAKENGINE_OK) / negative OAKENGINE_E_* codes.
* - String output uses the buf/size convention.
*/
/** @brief Opaque clipboard context. */
typedef struct OakEngineClipboard OakEngineClipboard;
/** @brief Marker handle (defined in oakengine/timeline.h). */
typedef struct OakEngineMarker OakEngineMarker;
#define OAKENGINE_CLIPBOARD_PROJECT 0
#define OAKENGINE_CLIPBOARD_NODES 1
#define OAKENGINE_CLIPBOARD_CLIPS 2
#define OAKENGINE_CLIPBOARD_MARKERS 3
#define OAKENGINE_CLIPBOARD_KEYFRAMES 4
#define OAKENGINE_SERIALIZER_OK 0
#define OAKENGINE_SERIALIZER_TOO_OLD 1
#define OAKENGINE_SERIALIZER_TOO_NEW 2
#define OAKENGINE_SERIALIZER_UNKNOWN_VERSION 3
#define OAKENGINE_SERIALIZER_FILE_ERROR 4
#define OAKENGINE_SERIALIZER_XML_ERROR 5
#define OAKENGINE_SERIALIZER_OVERWRITE_ERROR 6
#define OAKENGINE_SERIALIZER_NO_DATA 7
/**
* @brief Returns 1 if `filename` is a compressed project file, 0 otherwise.
*/
OAKENGINE_API int oakengine_serializer_check_compressed(const char *filename);
/**
* @brief Create a clipboard context for copy/save operations.
*
* `load_type` is one of OAKENGINE_CLIPBOARD_*. `project` may be NULL for
* load types that do not require it. `filename` may be NULL.
*/
OAKENGINE_API OakEngineClipboard *oakengine_clipboard_create(
int load_type, OakEngineProject *project, const char *filename);
/**
* @brief Set the nodes to serialize on this clipboard.
*
* Replaces any previously set nodes. `nodes` is an array of `count` borrowed
* OakEngineNode handles. Returns OAKENGINE_OK or an error code.
*/
OAKENGINE_API int oakengine_clipboard_set_nodes(OakEngineClipboard *cb,
const OakEngineNode *const *nodes,
int count);
/**
* @brief Set the markers to serialize on this clipboard.
*/
OAKENGINE_API int oakengine_clipboard_set_markers(
OakEngineClipboard *cb, const OakEngineMarker *const *markers, int count);
/**
* @brief Set the keyframes to serialize on this clipboard.
*/
OAKENGINE_API int oakengine_clipboard_set_keyframes(
OakEngineClipboard *cb, const OakEngineKeyframe *const *keyframes,
int count);
/**
* @brief Set a serialized property attached to a node.
*
* Properties are free-form (key, value) strings attached to pasted nodes; the
* editor uses them for clip in-points/track-refs and node graph positions.
* Replaces the value if the same (node, key) pair is set twice. Returns
* OAKENGINE_OK or an error code.
*/
OAKENGINE_API int oakengine_clipboard_set_property(OakEngineClipboard *cb,
OakEngineNode *node,
const char *key,
const char *value);
/**
* @brief Copy this clipboard's data to the system clipboard.
*
* Returns OAKENGINE_OK or an error code.
*/
OAKENGINE_API int oakengine_clipboard_copy(OakEngineClipboard *cb);
/**
* @brief Serialize this clipboard's data to XML (buf/size convention).
*
* Returns the string length on success, or a negative OAKENGINE_E_* code on
* error.
*/
OAKENGINE_API int oakengine_clipboard_save_to_xml(OakEngineClipboard *cb,
char *buf, int buf_size);
/**
* @brief Paste data from the system clipboard into `project`.
*
* `load_type` selects what kind of data to paste. On success `*result_code`
* receives OAKENGINE_SERIALIZER_OK and the clipboard is populated with the
* paste result (accessible through the oakengine_clipboard_get_loaded_*
* accessors). On failure `*result_code` receives one of the
* OAKENGINE_SERIALIZER_* error codes and a human-readable detail string is
* written to `details_buf` (may be NULL). Returns OAKENGINE_OK on success or
* an error code.
*/
OAKENGINE_API int oakengine_clipboard_paste(OakEngineClipboard *cb,
int load_type,
OakEngineProject *project,
int *result_code,
char *details_buf,
int details_buf_size);
/**
* @brief Paste data from the system clipboard, invoking `map_fn` for each
* original->new node mapping.
*
* The callback is called once per (original node pointer, pasted node pointer)
* pair found in the paste result. The app can use it to build an existing-node
* map without exposing C++ containers across the boundary. Returning non-zero
* from the callback stops iteration early. Other semantics match
* oakengine_clipboard_paste().
*/
OAKENGINE_API int oakengine_clipboard_paste_with_map(
OakEngineClipboard *cb, int load_type, OakEngineProject *project,
int (*map_fn)(OakEngineNode *old, OakEngineNode *new_node, void *userdata),
void *userdata, int *result_code, char *details_buf,
int details_buf_size);
/**
* @brief Destroy a clipboard context.
*/
OAKENGINE_API void oakengine_clipboard_free(OakEngineClipboard *cb);
/* ---- Paste result accessors (valid after a successful paste) -------------- */
/**
* @brief Number of nodes loaded by the last paste operation.
*/
OAKENGINE_API int oakengine_clipboard_get_loaded_node_count(
OakEngineClipboard *cb);
/**
* @brief Borrowed node handle loaded at `index`.
*/
OAKENGINE_API OakEngineNode *oakengine_clipboard_get_loaded_node_at(
OakEngineClipboard *cb, int index);
/**
* @brief Number of markers loaded by the last paste operation.
*/
OAKENGINE_API int oakengine_clipboard_get_loaded_marker_count(
OakEngineClipboard *cb);
/**
* @brief Borrowed marker handle loaded at `index`.
*/
OAKENGINE_API OakEngineMarker *oakengine_clipboard_get_loaded_marker_at(
OakEngineClipboard *cb, int index);
/**
* @brief Number of keyframes loaded by the last paste operation.
*/
OAKENGINE_API int oakengine_clipboard_get_loaded_keyframe_count(
OakEngineClipboard *cb);
/**
* @brief Borrowed keyframe handle loaded at `index`.
*/
OAKENGINE_API OakEngineKeyframe *oakengine_clipboard_get_loaded_keyframe_at(
OakEngineClipboard *cb, int index);
/**
* @brief Iterate over the serialized properties attached to pasted nodes.
*
* For each (node, key, value) triple `fn` is called. Returning non-zero stops
* iteration early. Returns OAKENGINE_OK or an error code.
*/
OAKENGINE_API int oakengine_clipboard_foreach_property(
OakEngineClipboard *cb,
int (*fn)(OakEngineNode *node, const char *key, const char *value,
void *userdata),
void *userdata);
/**
* @brief Iterate over keyframes loaded by the last paste operation.
*
* For each keyframe `fn` is called with the node id string it belongs to and
* the keyframe handle. Returning non-zero stops iteration early. The app can
* group keyframes by node id and route them to the correct destination node.
* Returns OAKENGINE_OK or an error code.
*/
OAKENGINE_API int oakengine_clipboard_foreach_keyframe(
OakEngineClipboard *cb,
int (*fn)(const char *node_id, OakEngineKeyframe *keyframe,
void *userdata),
void *userdata);
/**
* @brief Iterate over promised connections from the paste result.
*
* For each promised edge `fn` is called with the output node, input node,
* input id and element index. Returning non-zero stops iteration early.
* Returns OAKENGINE_OK or an error code.
*/
OAKENGINE_API int oakengine_clipboard_foreach_connection(
OakEngineClipboard *cb,
int (*fn)(OakEngineNode *output_node, OakEngineNode *input_node,
const char *input_id, int element, void *userdata),
void *userdata);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_SERIALIZER_H */
+137
View File
@@ -0,0 +1,137 @@
/***
Oak - 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 OAKENGINE_SYNC_H
#define OAKENGINE_SYNC_H
#include "export.h"
#include "timeline.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file sync.h
* @brief C ABI for waveform-based audio synchronization estimation
*
* Wraps the engine's AudioWaveformSync estimators
* (engine/audio/audiowaveformsync.h): the full audio of two clips is
* rendered through the renderer family and cross-correlated, yielding
* the time offset that aligns the target clip with the reference clip
* (the application's timeline "synchronize clips by waveform" feature).
*
* Both estimators validate first and change nothing on failure (these
* are pure measurements). They return OAKENGINE_OK when the correlation
* is conclusive (OffsetResult::valid), OAKENGINE_E_STATE when it is
* inconclusive -- in that case `out_confidence` is still written so the
* caller can compare it against a fallback estimator, and the offset
* outputs are set to 0 / the stretch output to 1. OAKENGINE_E_INVALID
* covers NULL handles, clips without an on-track range, and sequences
* without audio; estimation itself requires the engine initialized
* with OAKENGINE_INIT_RENDER (OAKENGINE_E_STATE as well).
*
* Note this family renders audio freshly per call (no waveform-cache
* dependency); the application keeps its cache-envelope path for the
* envelope source and uses these functions for the estimation step.
* Errors follow the family model: per-thread human-readable reason via
* oakengine_sync_last_error().
*/
/**
* @brief Estimate the time offset aligning `target` to `reference`
* (AudioWaveformSync::estimate_envelope_offset).
*
* `out_offset_seconds` receives the signed offset in seconds: the
* shift to ADD to the target's timeline position so it aligns with the
* reference (negative = move the target earlier -- e.g. the
* application's AudioSynchronizer adds it to the reference in-point).
* The estimate is quantized to the RMS envelope window
* (sample_rate/20 seconds), so callers should expect up to one window
* of quantization error. `out_confidence` receives the correlation
* confidence in [0, 1] and is always written. Any output pointer may
* be NULL. Search bounds mirror the application (sample_rate/20
* window, 10-minute maximum offset).
*/
OAKENGINE_API int oakengine_sync_estimate_offset(
OakEngineSequence *seq, OakEngineClip *reference, OakEngineClip *target,
double *out_offset_seconds, double *out_confidence);
/**
* @brief Estimate a playback-rate change plus offset aligning `target`
* to `reference` (AudioWaveformSync::estimate_stretch_and_offset).
*
* `out_stretch` receives the rate the target must be played at to
* align (> 1 = the target runs slower and must be sped up; the search
* range mirrors the application: 0.75..1.34 in 0.005 steps, 30-second
* offset radius). `out_offset_seconds` and `out_confidence` behave
* like oakengine_sync_estimate_offset(). Any output pointer may be
* NULL.
*/
OAKENGINE_API int oakengine_sync_estimate_stretch_offset(
OakEngineSequence *seq, OakEngineClip *reference, OakEngineClip *target,
double *out_stretch, double *out_offset_seconds,
double *out_confidence);
/**
* @brief Human-readable reason for the last failed sync call on this
* thread (buf/size convention). Empty when the last call succeeded.
*/
OAKENGINE_API int oakengine_sync_last_error(char *buf, int buf_size);
/* ---- Place by source time / waveform offset (replaces AudioSynchronizer) - */
/** @brief POD for sync placement result (timeline_in rational). */
typedef struct oak_sync_placement {
int64_t timeline_in_num;
int64_t timeline_in_den;
} oak_sync_placement;
/**
* @brief Place a clip by source time (AudioSynchronizer::place_by_source_time).
*
* Computes: timeline_in = anchor_in + (cand_source_start + cand_media_in)
* - (ref_source_start + ref_media_in)
* Returns OAKENGINE_OK and fills `out`, or OAKENGINE_E_INVALID on NaN input.
*/
OAKENGINE_API int oakengine_sync_place_by_source_time(
int64_t ref_source_start_num, int64_t ref_source_start_den,
int64_t ref_media_in_num, int64_t ref_media_in_den,
int64_t cand_source_start_num, int64_t cand_source_start_den,
int64_t cand_media_in_num, int64_t cand_media_in_den,
int64_t anchor_num, int64_t anchor_den,
oak_sync_placement *out);
/**
* @brief Place a clip by waveform offset (AudioSynchronizer::place_by_waveform_offset).
*
* Computes: timeline_in = ref_timeline_in + candidate_offset_samples / sample_rate
* Returns OAKENGINE_OK and fills `out`, or OAKENGINE_E_INVALID when sample_rate <= 0.
*/
OAKENGINE_API int oakengine_sync_place_by_waveform_offset(
int64_t ref_timeline_in_num, int64_t ref_timeline_in_den,
int64_t candidate_offset_samples, int sample_rate,
oak_sync_placement *out);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_SYNC_H */
+293
View File
@@ -0,0 +1,293 @@
/***
Oak - 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 OAKENGINE_TASK_H
#define OAKENGINE_TASK_H
#include <stdint.h>
#include "encoding.h"
#include "init.h"
#include "node.h"
#include "project.h"
#include "timeline.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file task.h
* @brief C ABI for the engine background-task system (olive::Task /
* olive::TaskManager)
*
* Tasks are engine objects that run a job (project load/save, footage
* import, proxy generation, export) on a worker thread. This family lets
* C consumers create the concrete task they need, run it synchronously or
* hand it to the global TaskManager queue, and observe its lifecycle
* through the event mechanism (oakengine/events.h, task family
* OAKENGINE_EVENT_TASK_* and manager family
* OAKENGINE_EVENT_TASK_MANAGER_*), without ever seeing the C++ classes.
*
* Conventions (matching oakengine/project.h):
* - OakEngineTask is an opaque borrowed/owned pointer to an
* olive::Task subclass.
* - A task returned by an oakengine_task_create_*() function is OWNED by
* the caller until either oakengine_task_manager_add() (the manager
* takes ownership and deletes the task when done) or
* oakengine_task_free() (the caller deletes it). A task that ran via
* oakengine_task_start_sync() is still owned by the caller and must be
* released with oakengine_task_free() (or handed to the manager,
* though re-running is unusual).
* - Once a task was added to the manager its handle must be treated as
* borrowed: the manager may delete it at any time after the
* OAKENGINE_EVENT_TASK_MANAGER_TASK_REMOVED notification.
* - Return codes: 0 (OAKENGINE_OK) on success, negative OAKENGINE_E_*
* on failure. String output uses the buf/size convention (return value
* is the length that would have been written excluding the NUL; a
* negative value is an OAKENGINE_E_* error).
*/
/**
* @brief Opaque task handle (an olive::Task subclass instance).
*/
typedef struct OakEngineTask OakEngineTask;
/* ---- Global task manager ------------------------------------------------- */
/**
* @brief Borrowed handle of the global TaskManager singleton, for use as
* the subscription handle of the OAKENGINE_EVENT_TASK_MANAGER_* events.
* Returns NULL when the engine is not initialized.
*/
OAKENGINE_API void *oakengine_task_manager_handle(void);
/**
* @brief Number of tasks currently known to the manager (running plus
* failed-but-kept), or OAKENGINE_E_INVALID when no manager exists.
*/
OAKENGINE_API int oakengine_task_manager_count(void);
/**
* @brief Borrowed handle of an arbitrary running task (the manager's
* "first" task, used by the status bar), or NULL when the queue is empty
* or no manager exists.
*/
OAKENGINE_API OakEngineTask *oakengine_task_manager_first(void);
/**
* @brief Hand `task` to the global manager queue (takes ownership). The
* task starts as soon as a worker thread is available.
*
* @return OAKENGINE_OK, OAKENGINE_E_INVALID for NULL, OAKENGINE_E_STATE
* when no manager exists.
*/
OAKENGINE_API int oakengine_task_manager_add(OakEngineTask *task);
/**
* @brief Ask the manager to cancel `task` (TaskManager::cancel_task
* semantics: a running task is signalled; a failed-but-kept task is
* removed and deleted).
*/
OAKENGINE_API int oakengine_task_manager_cancel(OakEngineTask *task);
/* ---- Task accessors ------------------------------------------------------ */
/**
* @brief Title of `task` (buf/size convention).
*/
OAKENGINE_API int oakengine_task_title(OakEngineTask *task, char *buf,
int buf_size);
/**
* @brief Error message of `task` (buf/size convention). Meaningful after a
* failed run.
*/
OAKENGINE_API int oakengine_task_error(OakEngineTask *task, char *buf,
int buf_size);
/**
* @brief Start timestamp of `task` (milliseconds since epoch), 0 when the
* task never started, OAKENGINE_E_INVALID for NULL.
*/
OAKENGINE_API int64_t oakengine_task_start_time(OakEngineTask *task);
/**
* @brief 1 when `task` was asked to cancel, 0 otherwise,
* OAKENGINE_E_INVALID for NULL.
*/
OAKENGINE_API int oakengine_task_is_cancelled(OakEngineTask *task);
/**
* @brief Signal `task` to cancel as soon as possible (Task::Cancel).
*/
OAKENGINE_API int oakengine_task_cancel(OakEngineTask *task);
/**
* @brief Run `task` synchronously on the CALLING thread (Task::start).
* Emits the task events on this thread. Ownership stays with the caller.
*
* @return 1 when the task succeeded, 0 when it failed or was cancelled
* (read oakengine_task_error()), OAKENGINE_E_INVALID for NULL.
*/
OAKENGINE_API int oakengine_task_start_sync(OakEngineTask *task);
/**
* @brief Delete a task that was never added to the manager.
*/
OAKENGINE_API int oakengine_task_free(OakEngineTask *task);
/**
* @brief Run `task` through the engine's CLI modal progress dialog and return
* 1 when it succeeds, 0 when it fails or is cancelled.
*
* The dialog shows the task's title and progress on the terminal. `parent`
* is an optional QObject parent (may be NULL). The task is started
* synchronously; the caller retains ownership and must free it with
* oakengine_task_free() when done.
*/
OAKENGINE_API int oakengine_cli_task_dialog_run(OakEngineTask *task,
void *parent_or_NULL);
/* ---- Task creators --------------------------------------------------------
*
* All creators return an OWNED task (NULL on invalid input). The task is
* not started by creation.
*/
/**
* @brief Task that loads an OVE project from `filename`.
*/
OAKENGINE_API OakEngineTask *
oakengine_task_create_project_load(const char *filename);
/**
* @brief Task that loads an OpenTimelineIO project from `filename`.
* Returns NULL when the engine was built without OTIO support.
*/
OAKENGINE_API OakEngineTask *
oakengine_task_create_project_load_otio(const char *filename);
/**
* @brief Task that saves `project` (ProjectSaveTask semantics).
*
* `use_compression` selects the compressed .ove writer (0 writes the
* uncompressed .ovexml form). `override_filename` may be NULL to save to
* the project's own filename. `layout` is an opaque
* `const olive::SerializedLayoutInfo *` (may be NULL) whose contents are
* copied into the saved file.
*/
OAKENGINE_API OakEngineTask *oakengine_task_create_project_save(
OakEngineProject *project, int use_compression,
const char *override_filename, const void *layout);
/**
* @brief Task that saves `project` in OpenTimelineIO format. Returns NULL
* when the engine was built without OTIO support.
*/
OAKENGINE_API OakEngineTask *
oakengine_task_create_project_save_otio(OakEngineProject *project);
/**
* @brief Task that imports `url_count` media files into `folder` (a folder
* node of the target project; use oakengine_project_root() for the top
* level). The URL array is copied during the call.
*/
OAKENGINE_API OakEngineTask *oakengine_task_create_project_import(
OakEngineNode *folder, const char **urls, int url_count);
/**
* @brief Task that generates the proxy media for `footage` (a footage node
* handle, as accepted by oakengine_footage_borrow(); the task keeps the
* underlying node).
*/
OAKENGINE_API OakEngineTask *
oakengine_task_create_proxy(OakEngineNode *footage);
/**
* @brief Task that renders an export of `sequence` with `params`.
*
* Takes ownership of `params` (destroyed with the task). Progress is
* reported through the OAKENGINE_EVENT_TASK_PROGRESS event; cancelling
* the task cancels the engine export render.
*/
OAKENGINE_API OakEngineTask *oakengine_task_create_export(
OakEngineSequence *sequence, OakEngineEncodingParams *params);
/* ---- Import task results --------------------------------------------------
*
* Valid on a task created by oakengine_task_create_project_import() after
* it ran; all return OAKENGINE_E_INVALID (or 0/NULL) for other tasks.
*/
/**
* @brief Number of files the import task will process (valid right after
* creation; 0 means "nothing to import" and the task should be freed
* instead of run).
*/
OAKENGINE_API int oakengine_task_import_file_count(OakEngineTask *task);
/**
* @brief The undo command built by a successful import run as an opaque
* `olive::MultiUndoCommand *` (NULL before the run, after a cancelled
* run, or on a second call). Ownership is DETACHED from the task and
* passes to the caller: push it with oakengine_undo_push() or delete it.
*/
OAKENGINE_API void *oakengine_task_import_get_command(OakEngineTask *task);
/**
* @brief Number of footage items a successful import run created.
*/
OAKENGINE_API int oakengine_task_import_footage_count(OakEngineTask *task);
/**
* @brief Borrowed node handle of the imported footage item at `index`
* (NULL when out of range).
*/
OAKENGINE_API OakEngineNode *
oakengine_task_import_footage_at(OakEngineTask *task, int index);
/**
* @brief Number of files the import task rejected.
*/
OAKENGINE_API int
oakengine_task_import_invalid_files_count(OakEngineTask *task);
/**
* @brief Rejected file path at `index` (buf/size convention).
*/
OAKENGINE_API int oakengine_task_import_invalid_file_at(OakEngineTask *task,
int index, char *buf,
int buf_size);
/* ---- Save task results ---------------------------------------------------- */
/**
* @brief Borrowed handle of the project a save task wrote (NULL for other
* tasks).
*/
OAKENGINE_API OakEngineProject *
oakengine_task_save_get_project(OakEngineTask *task);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_TASK_H */
+464
View File
@@ -301,6 +301,48 @@ OAKENGINE_API int oakengine_sequence_marker_at(const OakEngineSequence *self,
*/
typedef struct OakEngineClip OakEngineClip;
/**
* @brief Opaque marker list handle (olive::TimelineMarkerList).
*
* Borrowed from oakengine_viewer_get_marker_list(). Invalidated when the
* owning viewer is freed.
*/
typedef struct OakEngineMarkerList OakEngineMarkerList;
/**
* @brief Opaque marker handle (olive::TimelineMarker).
*
* Borrowed from oakengine_marker_list_at() / oakengine_marker_list_marker_at_time().
* Invalidated when the owning project is freed.
*/
typedef struct OakEngineMarker OakEngineMarker;
/**
* @brief Opaque workarea handle (olive::TimelineWorkArea).
*
* Borrowed from oakengine_viewer_get_workarea_handle() or created standalone
* with oakengine_workarea_create(). Must be freed with oakengine_workarea_free()
* when created standalone; borrowed handles are invalidated with their owner.
*/
typedef struct OakEngineWorkarea OakEngineWorkarea;
/**
* @brief Opaque track handle (olive::Track).
*
* Borrowed from oakengine_sequence_track_at(). Invalidated when the owning
* sequence is freed.
*/
typedef struct OakEngineTrack OakEngineTrack;
/**
* @brief Opaque block handle (olive::Block).
*
* A generic block on a track (ClipBlock, GapBlock, TransitionBlock, etc).
* Borrowed from events or cast from OakEngineClip* / OakEngineTrack*. The
* handle is invalidated when the owning project is freed.
*/
typedef struct OakEngineBlock OakEngineBlock;
/**
* @brief Human-readable reason for the last failed editing call on this
* thread (buf/size convention). Editing calls return NULL or a negative
@@ -322,6 +364,41 @@ OAKENGINE_API int oakengine_sequence_last_error(char *buf, int buf_size);
OAKENGINE_API int oakengine_sequence_add_track(OakEngineSequence *self,
int track_type);
/**
* @brief Create a TimelineAddTrackCommand as an opaque command pointer without
* executing or pushing it. If `out_track` is non-NULL, it receives a borrowed
* handle to the track that the command will create on redo.
*/
OAKENGINE_API void *oakengine_sequence_add_track_command(
OakEngineSequence *self, int track_type, int auto_merge,
OakEngineTrack **out_track);
/** Movement modes for ripple/trim commands (mirror olive::Timeline::MovementMode). */
#define OAKENGINE_MOVEMENT_MODE_NONE 0
#define OAKENGINE_MOVEMENT_MODE_MOVE 1
#define OAKENGINE_MOVEMENT_MODE_TRIM_IN 2
#define OAKENGINE_MOVEMENT_MODE_TRIM_OUT 3
/**
* @brief One entry in a TrackListRippleToolCommand hash: the track to ripple,
* the block being moved, and whether a gap should be appended after it.
*/
typedef struct oakengine_ripple_info {
OakEngineTrack *track;
OakEngineBlock *block;
int append_gap;
} oakengine_ripple_info;
/**
* @brief Create a TrackListRippleToolCommand as an opaque command pointer.
* `infos` holds one entry per affected track; `movement` is a rational offset
* in seconds. Returns NULL on invalid arguments.
*/
OAKENGINE_API void *oakengine_sequence_ripple_tracks_command(
OakEngineSequence *self, int track_type,
const oakengine_ripple_info *infos, int info_count,
int64_t movement_num, int64_t movement_den, int movement_mode);
/**
* @brief Place a clip of `footage` on a track (undoable).
*
@@ -374,6 +451,15 @@ OAKENGINE_API int oakengine_clip_get_range(const OakEngineClip *self,
int64_t *in, int64_t *out,
int64_t *media_in);
/**
* @brief The sequence that owns the clip's track.
*
* Returns a borrowed handle (the clip's track's parent sequence) or NULL if
* the clip is not on a track.
*/
OAKENGINE_API OakEngineSequence *oakengine_clip_get_sequence(
const OakEngineClip *self);
/* ---- Editing primitives, round 2: split / ripple delete / trim / move ----
*
* All four are undoable like the other editing primitives and report
@@ -712,6 +798,384 @@ OAKENGINE_API int oakengine_sequence_marker_rename(OakEngineSequence *seq,
int64_t time_ts,
const char *name);
/* ---- Marker handle family ----------------------------------------------------
*
* Marker list and individual marker operations on opaque handles. The list
* is obtained from oakengine_viewer_get_marker_list() (declared in viewer.h).
* These functions operate on the handle level rather than through the sequence,
* for fine-grained undo/redo and direct marker manipulation.
*
* All times are rational seconds (numerator/denominator pairs) matching the
* engine's internal time representation.
*/
/** @brief Number of markers in the list. 0 on a NULL handle. */
OAKENGINE_API int oakengine_marker_list_count(const OakEngineMarkerList *list);
/** @brief Add a marker with the given rational time range, name, and color.
* Returns OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_marker_list_add(OakEngineMarkerList *list,
int64_t in_num, int64_t in_den,
int64_t out_num, int64_t out_den,
const char *name, int color);
/**
* @brief Create a detached marker (not yet added to any list).
*
* The returned handle can be passed to MarkerPropertiesDialog, then either
* added with oakengine_marker_list_add_existing() or freed with
* oakengine_marker_free().
*/
OAKENGINE_API OakEngineMarker *oakengine_marker_create(
int color, int64_t in_num, int64_t in_den, int64_t out_num, int64_t out_den,
const char *name);
/** @brief Free a detached marker created by oakengine_marker_create(). */
OAKENGINE_API void oakengine_marker_free(OakEngineMarker *marker);
/** @brief Re-add an existing (removed) marker to the list. Returns
* OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_marker_list_add_existing(OakEngineMarkerList *list,
OakEngineMarker *marker);
/** @brief Marker at the given sorted index, or NULL if out of range. */
OAKENGINE_API OakEngineMarker *
oakengine_marker_list_at(const OakEngineMarkerList *list, int index);
/** @brief Find a marker by its exact in-point time (rational seconds).
* Returns the marker or NULL if not found. */
OAKENGINE_API OakEngineMarker *
oakengine_marker_list_marker_at_time(const OakEngineMarkerList *list,
int64_t num, int64_t den);
/** @brief Get the marker's time range as rational seconds. Any pointer
* may be NULL. Returns OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_marker_get_time(const OakEngineMarker *self,
int64_t *in_num, int64_t *in_den,
int64_t *out_num,
int64_t *out_den);
/** @brief Get the marker's name (buf/size convention). Returns the
* would-be length (excluding NUL) or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_marker_get_name(const OakEngineMarker *self,
char *buf, int buf_size);
/** @brief Get the marker's color index. Returns -1 on a NULL handle. */
OAKENGINE_API int oakengine_marker_get_color(const OakEngineMarker *self);
/** @brief 1 if the marker list has another marker at the given rational
* time, 0 otherwise. 0 on a NULL marker handle. */
OAKENGINE_API int
oakengine_marker_has_sibling_at_time(const OakEngineMarker *self, int64_t num,
int64_t den);
/** @brief Set the marker's time range live (non-undoable). Returns
* OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_marker_set_time_live(OakEngineMarker *self,
int64_t in_num,
int64_t in_den,
int64_t out_num,
int64_t out_den);
/** @brief Commit a time change as an undoable command (undo restores the
* pre-commit state). Returns OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_marker_commit_time(
OakEngineMarker *self, int64_t old_in_num, int64_t old_in_den,
int64_t old_out_num, int64_t old_out_den, int64_t new_in_num,
int64_t new_in_den, int64_t new_out_num, int64_t new_out_den,
void *command);
/**
* @brief Create a MarkerChangeTimeCommand as an opaque command pointer.
* `new_time_num`/`new_time_den` is the new in-point in rational seconds;
* the marker's out-point offset is preserved.
*/
OAKENGINE_API void *oakengine_marker_set_time_command(
OakEngineMarker *marker, int64_t new_time_num, int64_t new_time_den);
/** @brief Remove the marker from its list (undoable). Returns
* OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_marker_remove(OakEngineMarker *self);
/** @brief Batch-set properties on one or more markers (undoable, ONE
* command). Pass -1 for color to leave it unchanged; pass NULL for name
* to leave it unchanged. When `count` == 1, optionally move the marker's
* time range. Returns OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_marker_set_properties(
OakEngineMarker **markers, int count, int color, const char *name,
int move_time, int64_t new_in_num, int64_t new_in_den,
int64_t new_out_num, int64_t new_out_den, void *command);
/* ---- Workarea handle family ---------------------------------------------------
*
* Workarea operations on opaque OakEngineWorkarea handles. Create with
* oakengine_workarea_create() or borrow from a viewer with
* oakengine_viewer_get_workarea_handle(). Standalone workareas must be
* freed with oakengine_workarea_free(). All times are rational seconds.
*/
/** @brief Create a standalone workarea (caller owns it). */
OAKENGINE_API OakEngineWorkarea *oakengine_workarea_create(void);
/** @brief Free a standalone workarea. NULL-safe. */
OAKENGINE_API void oakengine_workarea_free(OakEngineWorkarea *wa);
/** @brief Read the workarea state. Any pointer may be NULL. Returns
* OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_workarea_get(const OakEngineWorkarea *self,
int64_t *in_num, int64_t *in_den,
int64_t *out_num, int64_t *out_den,
int *enabled);
/** @brief Set the workarea range (non-undoable). Returns OAKENGINE_OK or
* OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_workarea_set_range(OakEngineWorkarea *self,
int64_t in_num, int64_t in_den,
int64_t out_num, int64_t out_den);
/** @brief Enable/disable the workarea (non-undoable). Returns
* OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_workarea_set_enabled(OakEngineWorkarea *self,
int enabled);
/** @brief Set the workarea range with undo support. Pass the reset
* sentinels (from oakengine_workarea_reset_in_out()) for the old range
* when creating fresh. Returns OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_workarea_set_range_undoable(
OakEngineWorkarea *self, int64_t in_num, int64_t in_den, int64_t out_num,
int64_t out_den, int64_t old_in_num, int64_t old_in_den,
int64_t old_out_num, int64_t old_out_den, void *command);
/** @brief Enable/disable the workarea with undo support. Pass NULL for
* command (creates a standalone undo command that is pushed onto the
* global stack when the workarea has an owning project). Returns
* OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_workarea_set_enabled_undoable(
OakEngineWorkarea *self, int enabled, void *command);
/** @brief Fill the reset sentinel values (in = 0/1, out = RATIONAL_MAX).
* Any pointer may be NULL. */
OAKENGINE_API void oakengine_workarea_reset_in_out(int64_t *in_num,
int64_t *in_den,
int64_t *out_num,
int64_t *out_den);
/* ---- Clip media range / cache / media in --------------------------------- */
/** @brief Get the clip's media range as rational seconds
* (ClipBlock::media_range()). Any pointer may be NULL. Returns
* OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_clip_get_media_range_rational(
const OakEngineClip *self, int64_t *in_num, int64_t *in_den,
int64_t *out_num, int64_t *out_den);
/** @brief Get the clip's media in-point as rational seconds
* (ClipBlock::media_in()). Any pointer may be NULL. Returns OAKENGINE_OK
* or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_clip_get_media_in_rational(
const OakEngineClip *self, int64_t *num, int64_t *den);
/** @brief Move the clip's media in-point (undoable when undoable != 0,
* else direct). Returns OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_clip_set_media_in(OakEngineClip *self,
int64_t media_in_ts,
int undoable);
/** @brief Move the clip's media in-point as a rational seconds value
* (undoable when undoable != 0, else direct). This variant does not
* require the clip to be on a track yet. Returns OAKENGINE_OK or
* OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_clip_set_media_in_rational(OakEngineClip *self,
int64_t num,
int64_t den,
int undoable);
/** @brief Request invalidation of the cache for the given range. NULL-safe
* (no-op). */
OAKENGINE_API void oakengine_clip_request_invalidate(OakEngineClip *self,
int64_t in_ts,
int64_t out_ts,
int type);
/** @brief Add a cache passthrough dependency (copy results from `source`
* to `dest`). NULL-safe (no-op). */
OAKENGINE_API void oakengine_clip_add_cache_passthrough(
OakEngineClip *dest, OakEngineClip *source);
/** @brief Discard the clip's cache. NULL-safe (no-op). */
OAKENGINE_API void oakengine_clip_discard_cache(OakEngineClip *self);
/** @brief Create a new empty ClipBlock. The caller owns the returned node
* and must add it to a project (e.g. via oakengine_project_add_node or a
* custom undo command) before the engine can manage its lifecycle. The
* optional `label` sets the node's user label (Node::set_label()). */
OAKENGINE_API OakEngineClip *oakengine_clip_create_empty(const char *label);
/** @brief Request invalidated cache ranges from the node connected to the
* clip's buffer input (ClipBlock::request_invalidated_from_connected()).
* Pass in_den == 0 or out_den == 0 to intersect the full media range. */
OAKENGINE_API void oakengine_clip_request_invalidate_connected(
OakEngineClip *self, int force_all, int64_t in_num, int64_t in_den,
int64_t out_num, int64_t out_den);
/* ---- Block functions (generic block, not just ClipBlock) ------------------ */
/** @brief 1 if the block is enabled (Block::is_enabled()). 0 on NULL. */
OAKENGINE_API int oakengine_block_is_enabled(const OakEngineBlock *self);
/** @brief Enable or disable the block (undoable). Returns OAKENGINE_OK
* or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_block_set_enabled(OakEngineBlock *self,
int enabled);
/* ---- Block traversal -------------------------------------------------------- */
/** @brief Number of blocks (including gaps) on the track. Returns
* OAKENGINE_E_INVALID for a NULL handle. */
OAKENGINE_API int oakengine_track_block_count(const OakEngineTrack *track);
/** @brief The block at `index` on the track (0-based, includes gaps).
* Returns NULL when out of range or on a NULL handle. */
OAKENGINE_API OakEngineBlock *
oakengine_track_block_at(const OakEngineTrack *track, int index);
/** @brief The block at the given timestamp, or NULL if the time falls in
* a gap or past the end. Timestamp is in the track's sequence timebase. */
OAKENGINE_API OakEngineBlock *
oakengine_track_block_at_time(const OakEngineTrack *track, int64_t timestamp);
/** @brief Nearest block whose out-point is strictly before `timestamp`.
* Returns NULL when none. */
OAKENGINE_API OakEngineBlock *
oakengine_track_nearest_block_before(const OakEngineTrack *track,
int64_t timestamp);
/** @brief Nearest block whose in-point is strictly after `timestamp`.
* Returns NULL when none. */
OAKENGINE_API OakEngineBlock *
oakengine_track_nearest_block_after(const OakEngineTrack *track,
int64_t timestamp);
/** @brief Nearest block whose out-point >= `timestamp`
* (i.e. the block containing or immediately before the time).
* Returns NULL when none. */
OAKENGINE_API OakEngineBlock *
oakengine_track_nearest_block_before_or_at(const OakEngineTrack *track,
int64_t timestamp);
/** @brief Nearest block whose in-point <= `timestamp`
* (i.e. the block containing or immediately after the time).
* Returns NULL when none. */
OAKENGINE_API OakEngineBlock *
oakengine_track_nearest_block_after_or_at(const OakEngineTrack *track,
int64_t timestamp);
/** @brief 1 if the block is a GapBlock, 0 otherwise. 0 on NULL. */
OAKENGINE_API int oakengine_block_is_gap(const OakEngineBlock *block);
/** @brief Next block in the track's linked list, or NULL. NULL on NULL. */
OAKENGINE_API OakEngineBlock *oakengine_block_next(const OakEngineBlock *block);
/** @brief Previous block in the track's linked list, or NULL. NULL on NULL. */
OAKENGINE_API OakEngineBlock *oakengine_block_prev(const OakEngineBlock *block);
/** @brief Fill `in` and `out` with the block's range as timestamps in the
* owning track's sequence timebase. Either pointer may be NULL. Returns
* OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_block_get_range(const OakEngineBlock *block,
int64_t *in, int64_t *out);
/* ---- Clip input ID getters ------------------------------------------------- */
/** @brief ClipBlock::k_buffer_in. Static string, never freed. */
OAKENGINE_API const char *oakengine_clip_buffer_input_id(void);
/** @brief ClipBlock::k_speed_input. */
OAKENGINE_API const char *oakengine_clip_speed_input_id(void);
/** @brief ClipBlock::k_reverse_input. */
OAKENGINE_API const char *oakengine_clip_reverse_input_id(void);
/** @brief ClipBlock::k_maintain_audio_pitch_input. */
OAKENGINE_API const char *
oakengine_clip_maintain_audio_pitch_input_id(void);
/** @brief ClipBlock::k_loop_mode_input. */
OAKENGINE_API const char *oakengine_clip_loop_mode_input_id(void);
/** @brief ClipBlock::k_auto_cache_input. */
OAKENGINE_API const char *oakengine_clip_auto_cache_input_id(void);
/* ---- Sequence: add_default_nodes ------------------------------------------ */
/** @brief Add one video and one audio track as ONE undoable command
* (ViewerOutput helper used by the application). Returns OAKENGINE_OK
* or OAKENGINE_E_INVALID. */
OAKENGINE_API int
oakengine_sequence_add_default_nodes(OakEngineSequence *seq);
/* ---- Sequence: add_sequence_clip ------------------------------------------- */
/** @brief Place a nested Sequence as a clip on a track (undoable).
*
* Same semantics as oakengine_sequence_add_footage_clip() but creates a
* clip whose buffer input feeds from another Sequence node (nested
* timeline). Self-nesting and circular nesting are detected and rejected.
* Returns a borrowed clip handle or NULL on failure. */
OAKENGINE_API OakEngineClip *
oakengine_sequence_add_sequence_clip(OakEngineSequence *seq,
OakEngineSequence *nested,
int track_type, int track_index,
int64_t in, int64_t out,
int64_t media_in);
/* ---- Track handle queries -------------------------------------------------- */
/** @brief Borrowed track handle, or NULL if the track does not exist. */
OAKENGINE_API OakEngineTrack *
oakengine_sequence_track_at(const OakEngineSequence *seq, int track_type,
int track_index);
/** @brief Track type (OAKENGINE_TRACK_TYPE_*), or -1 on a NULL handle. */
OAKENGINE_API int oakengine_track_type(const OakEngineTrack *track);
/** @brief Track content length in frame timestamps (Track::get_length()
* converted to timebase units). Returns OAKENGINE_OK or
* OAKENGINE_E_NOT_FOUND/OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_track_get_length(const OakEngineSequence *seq,
int track_type, int track_index,
int64_t *length);
/** @brief 1 if the range [in_ts, out_ts) is free (no blocks intersect it),
* 0 if it intersects. Returns OAKENGINE_E_NOT_FOUND when the track does
* not exist, OAKENGINE_E_INVALID for bad arguments. */
OAKENGINE_API int oakengine_track_is_range_free(const OakEngineSequence *seq,
int track_type,
int track_index,
int64_t in_ts, int64_t out_ts);
/** @brief Track height helpers (matching the engine's
* Track::k_height_* constants). */
OAKENGINE_API double oakengine_track_height_default(void);
OAKENGINE_API int oakengine_track_default_height_in_pixels(void);
OAKENGINE_API int oakengine_track_height_internal_to_pixels(double height);
OAKENGINE_API double oakengine_track_height_pixels_to_internal(int pixels);
/** @brief Track height step interval (e.g. 0.5). > 0.0. */
OAKENGINE_API double oakengine_track_height_interval(void);
/** @brief Minimum track height (e.g. 1.5). > 0.0. */
OAKENGINE_API double oakengine_track_height_minimum(void);
/* ---- Multicam helpers --------------------------------------------------- */
/** @brief Find the MultiCamNode ancestor of a clip, or NULL. Accepts
* OakEngineNode* (a clip or any node). */
OAKENGINE_API OakEngineNode *
oakengine_clip_find_multicam(OakEngineNode *node);
/** @brief Switch the multicam source to the given track/stream at the
* given time. Returns OAKENGINE_OK or OAKENGINE_E_INVALID. */
OAKENGINE_API int oakengine_multicam_switch_source(
OakEngineNode *multicam_node, OakEngineNode *footage_node,
int track_type, int track_index, double time_seconds,
void *command);
#ifdef __cplusplus
}
#endif
+167
View File
@@ -0,0 +1,167 @@
/***
Oak - 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 OAKENGINE_TRAVERSE_H
#define OAKENGINE_TRAVERSE_H
#include <stdint.h>
#include "export.h"
#include "init.h"
#include "node.h"
#include "videoparams.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file traverse.h
* @brief C ABI for synchronous node-graph value evaluation
* (olive::NodeTraverser)
*
* The engine evaluates a node's value at a given time by traversing its
* input graph (olive::NodeTraverser). The application uses this in three
* places: the node table view (per-input value databases), the node value
* tree (one output table + the element a downstream input's value hint
* selects) and the viewer display gizmos (a transform between two nodes
* and the gizmo node's input row at drag start). This family exposes
* those paths without leaking NodeValueTable/NodeValueRow C++ types.
*
* All evaluation is SYNCHRONOUS on the calling thread and CPU-only in
* this family (textures are resolved as engine-side dummy textures, which
* is exactly what the table/tree views need -- they only read metadata).
* Call from the GUI thread.
*
* OakEngineTraverseDb is an OWNED result object; free it with
* oakengine_traverse_db_free(). Strings it returns point into the object
* and are valid until freed. Times are Rational seconds as int64
* numerator/denominator pairs, like the rest of the facade.
*/
typedef struct OakEngineTraverseDb OakEngineTraverseDb;
/**
* @brief Evaluate every input of `node` over [in_num/in_den,
* out_num/out_den] seconds and return the per-input value database
* (NodeTraverser::generate_database()). NULL on invalid arguments.
*/
OAKENGINE_API OakEngineTraverseDb *oakengine_traverse_generate_database(
OakEngineNode *node, int64_t in_num, int64_t in_den, int64_t out_num,
int64_t out_den);
/**
* @brief Evaluate the single output table of `node`
* (NodeTraverser::generate_table()). Returned as a database with exactly
* one entry whose input id is an empty string.
*/
OAKENGINE_API OakEngineTraverseDb *oakengine_traverse_generate_table(
OakEngineNode *node, int64_t in_num, int64_t in_den, int64_t out_num,
int64_t out_den);
/** @brief Free a database returned by this family. NULL is a no-op. */
OAKENGINE_API void oakengine_traverse_db_free(OakEngineTraverseDb *db);
/** @brief Number of input entries (generate_database: one per input id
* that produced a table; generate_table: exactly 1). */
OAKENGINE_API int oakengine_traverse_db_input_count(
const OakEngineTraverseDb *db);
/** @brief Input id of entry `input_index` (valid until db is freed). */
OAKENGINE_API const char *oakengine_traverse_db_input_id(
const OakEngineTraverseDb *db, int input_index);
/** @brief Row count of the table at `input_index`
* (NodeValueTable::count()). */
OAKENGINE_API int oakengine_traverse_db_row_count(
const OakEngineTraverseDb *db, int input_index);
/**
* @brief Row accessors. `row` is 0-based in table order (the views
* reverse it themselves where needed). Strings are valid until db is
* freed.
*
* - type: oak_node_value_type of the value.
* - source: borrowed node that produced the value, or NULL.
* - tag: the value's tag (may be empty, never NULL).
* - value_string: NodeValue::value_to_string(value, false).
* - split_count / split_string: NodeValue::to_split_value() count and
* NodeValue::value_to_string(type, split[k], true) per element.
*/
OAKENGINE_API int oakengine_traverse_row_type(const OakEngineTraverseDb *db,
int input_index, int row);
OAKENGINE_API OakEngineNode *oakengine_traverse_row_source(
const OakEngineTraverseDb *db, int input_index, int row);
OAKENGINE_API const char *oakengine_traverse_row_tag(
const OakEngineTraverseDb *db, int input_index, int row);
OAKENGINE_API const char *oakengine_traverse_row_value_string(
const OakEngineTraverseDb *db, int input_index, int row);
OAKENGINE_API int oakengine_traverse_row_split_count(
const OakEngineTraverseDb *db, int input_index, int row);
OAKENGINE_API const char *oakengine_traverse_row_split_string(
const OakEngineTraverseDb *db, int input_index, int row, int split);
/**
* @brief The table element selected by `hint_node`'s value hint for input
* `input_id`@`element` against a table produced by
* oakengine_traverse_generate_table() (pass its db; must contain exactly
* one entry) -- NodeTraverser::generate_row_value_element_index().
* Returns -1 when no element matches.
*/
OAKENGINE_API int oakengine_traverse_table_element_index_for_hint(
OakEngineNode *hint_node, const char *input_id, int element,
const OakEngineTraverseDb *table_db);
/**
* @brief Fill a caller-allocated olive::NodeValueRow with `node`'s input
* values over the given range (NodeTraverser::generate_row()) -- the
* viewer display gizmo drag-start path. `cache_video_params` (may be NULL
* for engine defaults) and `sample_rate`/`channel_layout` seed the
* traverser's cache params (NodeTraverser::set_cache_video_params /
* set_cache_audio_params).
*
* Transition bridge (same pattern as
* replaced by internal ColorTransformJob API): `row_out` is opaque to C
* consumers; the application passes a pointer to its own
* olive::NodeValueRow (a QHash typedef, no engine symbols) which the
* engine fills in place.
*/
OAKENGINE_API int oakengine_traverse_generate_row(
OakEngineNode *node, int64_t in_num, int64_t in_den, int64_t out_num,
int64_t out_den, const oak_video_params *cache_video_params,
int sample_rate, uint64_t channel_layout, void *row_out);
/**
* @brief Accumulate the transform from `start` to `end` over the given
* range (NodeTraverser::transform()) and return it as the 6 affine
* coefficients of a QTransform (m11, m12, m21, m22, dx, dy), suitable for
* `QTransform(m11, m12, m21, m22, dx, dy)`. `cache_video_params` may be
* NULL for engine defaults.
*/
OAKENGINE_API int oakengine_traverse_transform(
OakEngineNode *start, OakEngineNode *end, int64_t in_num, int64_t in_den,
int64_t out_num, int64_t out_den, const oak_video_params *cache_video_params,
double out_m[6]);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_TRAVERSE_H */
+272
View File
@@ -0,0 +1,272 @@
/***
Oak - 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 OAKENGINE_UNDO_H
#define OAKENGINE_UNDO_H
#include <stdint.h>
#include "init.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file undo.h
* @brief C ABI for the global undo stack (olive::UndoStack)
*
* Exposes the process-wide undo stack that backs every editing primitive:
* pushing commands (the command objects themselves are still created by
* the caller as opaque engine pointers), jumping to an arbitrary history
* position, reading the command list for a history view, and the
* undo/redo QActions for menus.
*
* Change notification: subscribe to OAKENGINE_EVENT_UNDO_INDEX_CHANGED on
* oakengine_undo_handle(). The event fires after every stack mutation
* (push/undo/redo/jump/clear); the command list must be re-read through
* oakengine_undo_count()/oakengine_undo_command_text().
*
* Conventions (matching oakengine/project.h):
* - Return codes: 0 (OAKENGINE_OK) on success, negative OAKENGINE_E_*
* on failure. Functions returning a value return OAKENGINE_E_INVALID
* when no application core exists.
* - String output uses the buf/size convention.
*/
/**
* @brief Borrowed handle of the global undo stack, for use as the
* subscription handle of OAKENGINE_EVENT_UNDO_INDEX_CHANGED. Returns NULL
* when no application core exists.
*/
OAKENGINE_API void *oakengine_undo_handle(void);
/**
* @brief Push `command` (an opaque `olive::UndoCommand *`, e.g. the result
* of oakengine_task_import_get_command()) onto the stack and execute its
* redo. Takes ownership of `command` (an empty MultiUndoCommand is deleted
* immediately, matching UndoStack::push). `name` is the user-visible
* command label (NULL behaves like an empty label).
*/
OAKENGINE_API int oakengine_undo_push(void *command, const char *name);
/**
* @brief Start collecting: subsequent facade undoable operations are added
* as children to a group and executed eagerly, but not pushed individually.
* Returns OAKENGINE_E_STATE if a group is already open.
*/
OAKENGINE_API int oakengine_undo_group_begin(const char *name);
/**
* @brief End the group and push it as ONE undo entry.
*
* An empty group is discarded (no undo entry). Returns OAKENGINE_E_STATE
* if no group is open.
*/
OAKENGINE_API int oakengine_undo_group_end(void);
/**
* @brief Abort the open group: undo all already-executed children and
* discard the group. Returns OAKENGINE_E_STATE if no group is open.
*/
OAKENGINE_API int oakengine_undo_group_abort(void);
/**
* @brief Execute the redo of `command` without taking ownership
* (UndoCommand::redo_now semantics). This is the facade replacement for
* app code that used to call MultiUndoCommand::redo_now() directly.
*/
OAKENGINE_API int oakengine_undo_command_redo_now(void *command);
/**
* @brief Execute the undo of `command` without taking ownership
* (UndoCommand::undo_now semantics).
*/
OAKENGINE_API int oakengine_undo_command_undo_now(void *command);
/**
* @brief Callback signatures for app-defined undo commands.
*
* These allow UI-side code to create undoable actions without defining
* C++ subclasses of olive::UndoCommand. The engine wraps the callbacks
* in an internal UndoCommand and forwards redo/undo/free calls.
*/
typedef void (*oakengine_undo_command_redo_fn)(void *userdata);
typedef void (*oakengine_undo_command_undo_fn)(void *userdata);
typedef void (*oakengine_undo_command_free_fn)(void *userdata);
/**
* @brief Create an app-defined undo command backed by C callbacks.
*
* The returned pointer is an opaque `olive::UndoCommand *` suitable for
* oakengine_undo_push() or oakengine_undo_command_multi_add_child().
* The command takes ownership of `userdata`; `free_fn` is called when
* the command is destroyed (whether pushed or freed directly).
*
* `name` is the user-visible label. Any callback may be NULL; a NULL
* redo/undo callback makes that direction a no-op.
*/
OAKENGINE_API void *oakengine_undo_command_create(
const char *name,
oakengine_undo_command_redo_fn redo,
oakengine_undo_command_undo_fn undo,
oakengine_undo_command_free_fn free_fn,
void *userdata);
/**
* @brief Create an empty MultiUndoCommand as an opaque command pointer.
*
* The returned pointer is owned by the caller until it is passed to
* oakengine_undo_push() or freed with oakengine_undo_command_free().
*/
OAKENGINE_API void *oakengine_undo_command_create_multi(void);
OAKENGINE_API void *oakengine_node_add_command(void *project, void *node);
OAKENGINE_API void *oakengine_node_set_position_command(
void *node, void *context, double x, double y, int expanded);
OAKENGINE_API void *oakengine_node_remove_position_command(
void *node, void *context);
OAKENGINE_API void *oakengine_node_set_value_hint_command(
void *node, const char *input, int element, int type, int index,
const char *tag);
OAKENGINE_API void *oakengine_node_remove_and_disconnect_command(void *node);
OAKENGINE_API void *oakengine_track_place_block_command(
void *track_list, int track_index, void *block, int64_t in_ts);
OAKENGINE_API void *oakengine_track_replace_block_with_gap_command(
void *track, void *block, int handle_transitions);
OAKENGINE_API void *oakengine_block_trim_command(
void *track, void *block, int64_t new_length_num, int64_t new_length_den,
int movement_mode, int roll_edit);
OAKENGINE_API void *oakengine_transition_remove_command(
void *transition, int remove_from_graph);
OAKENGINE_API void *oakengine_track_slide_command(
void *track, void *const *blocks, int block_count,
void *in_adjacent, void *out_adjacent,
int64_t movement_num, int64_t movement_den);
OAKENGINE_API void *oakengine_block_split_preserving_links_command(
void *const *blocks, int count, int64_t point_ts);
OAKENGINE_API void *oakengine_block_split_get_split(
void *command, void *block, int time_index);
OAKENGINE_API void *oakengine_block_resize_with_media_in_command(
void *block, int64_t length_num, int64_t length_den);
OAKENGINE_API void *oakengine_block_set_media_in_command(
void *block, int64_t media_in_num, int64_t media_in_den);
OAKENGINE_API void *oakengine_timeline_ripple_delete_gaps_command(
void *sequence, const int64_t *range_in_ts, const int64_t *range_out_ts,
const int *track_types, const int *track_indexes, int range_count);
/**
* @brief Create a TrackListInsertGaps command as an opaque command pointer.
* `point_num`/`point_den` is the insertion point in rational seconds;
* `length_num`/`length_den` is the gap length in rational seconds.
*/
OAKENGINE_API void *oakengine_track_list_insert_gaps_command(
void *track_list, int64_t point_num, int64_t point_den,
int64_t length_num, int64_t length_den);
/**
* @brief Add `child` (an opaque command pointer) to the MultiUndoCommand
* `multi`. Returns OAKENGINE_OK on success, OAKENGINE_E_INVALID if either
* argument is NULL.
*/
OAKENGINE_API int oakengine_undo_command_multi_add_child(void *multi,
void *child);
/**
* @brief Return the number of children in the MultiUndoCommand `multi`,
* or OAKENGINE_E_INVALID if `multi` is NULL.
*/
OAKENGINE_API int oakengine_undo_command_multi_child_count(void *multi);
/**
* @brief Destroy a command created by oakengine_undo_command_create() or
* oakengine_undo_command_create_multi() without pushing it onto the stack.
* Commands passed to oakengine_undo_push() are owned by the stack and
* must not be freed by the caller.
*/
OAKENGINE_API void oakengine_undo_command_free(void *command);
/**
* @brief Total number of history rows (done + undone commands), or
* OAKENGINE_E_INVALID when no stack exists.
*/
OAKENGINE_API int64_t oakengine_undo_count(void);
/**
* @brief Current position in the history: the number of done commands
* (rows below this index are undone). Emitted as payload `a` of
* OAKENGINE_EVENT_UNDO_INDEX_CHANGED.
*/
OAKENGINE_API int64_t oakengine_undo_index(void);
/**
* @brief Label of the history row at `row` (0-based, buf/size convention).
* Falls back to the translated "Command" placeholder for empty labels.
*
* @return the label length, or OAKENGINE_E_NOT_FOUND for an invalid row.
*/
OAKENGINE_API int oakengine_undo_command_text(int64_t row, char *buf,
int buf_size);
/**
* @brief 1 when the row at `row` is currently done (not undone), 0 when it
* is undone, OAKENGINE_E_NOT_FOUND for an invalid row.
*/
OAKENGINE_API int oakengine_undo_command_is_done(int64_t row);
/**
* @brief Undo/redo until the done-command count equals `index`
* (UndoStack::jump semantics).
*/
OAKENGINE_API int oakengine_undo_jump(int64_t index);
/**
* @brief Delete all commands and push the fresh "New/Open Project" empty
* command (UndoStack::clear).
*/
OAKENGINE_API int oakengine_undo_clear(void);
/**
* @brief Refresh the undo/redo action labels and enabled state
* (UndoStack::update_actions).
*/
OAKENGINE_API int oakengine_undo_update_actions(void);
/**
* @brief 1/0 whether undo (redo) is currently possible,
* OAKENGINE_E_INVALID when no stack exists.
*/
OAKENGINE_API int oakengine_undo_can_undo(void);
OAKENGINE_API int oakengine_undo_can_redo(void);
/**
* @brief The stack's undo (redo) QAction as an opaque `void *` (actually a
* `QAction *`; Qt types are allowed at this boundary). Borrowed; owned by
* the stack. NULL when no stack exists.
*/
OAKENGINE_API void *oakengine_undo_undo_action(void);
OAKENGINE_API void *oakengine_undo_redo_action(void);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_UNDO_H */
+211
View File
@@ -0,0 +1,211 @@
/***
Oak - 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 OAKENGINE_VIDEOPARAMS_H
#define OAKENGINE_VIDEOPARAMS_H
#include <stdint.h>
#include "export.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file videoparams.h
* @brief C ABI POD and static-data accessors for the engine's VideoParams
*
* Covers the parameter surface the export and sequence dialogs need:
* the POD carried by the encoding family (oakengine/encoding.h) and the
* static metadata behind the standard combo boxes (supported frame rates,
* pixel aspect ratios, dividers, pixel format names). Display/render-path
* helpers (bytes per pixel, scaled texture sizes, ...) are out of scope for
* now.
*
* Conventions match the other facade families: buf/size strings (return
* value is the would-be length excluding the NUL), -1 on invalid indexes,
* 0 (OAKENGINE_OK) / negative OAKENGINE_E_* codes where applicable.
*/
/**
* @brief POD mirror of olive::VideoParams' user-facing fields.
*
* `time_base_*` is the frame duration (frame rate flipped), matching
* VideoParams::time_base(); the frame rate is den/num. `format` is an
* olive::PixelFormat::Format value, `interlacing` an
* olive::VideoParams::Interlacing value (0 = none/progressive, 1 = top
* field first, 2 = bottom field first), `color_range` an
* olive::VideoParams::ColorRange value. The video channel count is an
* engine-internal constant and not exposed.
*/
typedef struct oak_video_params {
int width;
int height;
int time_base_num; /**< Frame duration numerator (e.g. 1001/30000 s). */
int time_base_den;
int format; /**< olive::PixelFormat::Format. */
int pixel_aspect_num;
int pixel_aspect_den;
int interlacing; /**< olive::VideoParams::Interlacing. */
int color_range; /**< olive::VideoParams::ColorRange. */
int divider; /**< Preview resolution divider (1 = full). */
/* The two fields below are only populated by the viewer family
* (oakengine_viewer_get_video_params(), B8c); other producers leave
* them 0 (k_video_type_video / not premultiplied). */
int video_type; /**< olive::VideoParams::Type. */
int premultiplied_alpha; /**< 0/1. */
} oak_video_params;
/** @brief Number of standard frame rates (VideoParams::k_supported_frame_rates). */
OAKENGINE_API int oakengine_video_params_supported_frame_rate_count(void);
/**
* @brief The `index`-th standard frame rate as num/den (e.g. 24000/1001);
* OAKENGINE_E_INVALID when out of range.
*/
OAKENGINE_API int oakengine_video_params_supported_frame_rate_at(int index,
int *num,
int *den);
/**
* @brief User-friendly label of a frame rate num/den pair
* (VideoParams::frame_rate_to_string(); buf/size).
*/
OAKENGINE_API int oakengine_video_params_frame_rate_to_string(int num, int den,
char *buf,
int buf_size);
/** @brief Number of standard pixel aspect ratios. */
OAKENGINE_API int oakengine_video_params_standard_pixel_aspect_count(void);
/** @brief The `index`-th standard pixel aspect ratio as num/den. */
OAKENGINE_API int oakengine_video_params_standard_pixel_aspect_at(int index,
int *num,
int *den);
/** @brief Display name of the `index`-th standard pixel aspect (buf/size). */
OAKENGINE_API int
oakengine_video_params_standard_pixel_aspect_name(int index, char *buf,
int buf_size);
/**
* @brief VideoParams::format_pixel_aspect_ratio_string(): formats `format`
* (a printf-style "%1" template) with the pixel aspect ratio num/den
* (buf/size).
*/
OAKENGINE_API int oakengine_video_params_format_pixel_aspect_ratio_string(
const char *format, int num, int den, char *buf, int buf_size);
/** @brief Number of supported preview dividers. */
OAKENGINE_API int oakengine_video_params_supported_divider_count(void);
/** @brief The `index`-th supported divider; -1 when out of range. */
OAKENGINE_API int oakengine_video_params_supported_divider_at(int index);
/** @brief Display name of a divider (VideoParams::get_name_for_divider()). */
OAKENGINE_API int oakengine_video_params_divider_name(int divider, char *buf,
int buf_size);
/**
* @brief 1 when `format` (a PixelFormat::Format value) is a float format
* (VideoParams::format_is_float()).
*/
OAKENGINE_API int oakengine_video_params_format_is_float(int format);
/** @brief Display name of a PixelFormat::Format value (buf/size). */
OAKENGINE_API int oakengine_video_params_pixel_format_name(int format,
char *buf,
int buf_size);
/**
* @brief Effective (divider-scaled) dimensions of width/height at `divider`
* (VideoParams::effective_width()/effective_height()). Any output pointer
* may be NULL.
*
* @return OAKENGINE_OK, or OAKENGINE_E_INVALID for non-positive
* width/height/divider.
*/
OAKENGINE_API int oakengine_video_params_effective_size(int width, int height,
int divider,
int *out_width,
int *out_height);
/**
* @brief Fill an oak_video_params POD (the display-path VideoParams
* constructor equivalent). No validation is performed beyond rejecting a
* NULL `p`; use oakengine_video_params_is_valid() to validate.
*
* @return OAKENGINE_OK, or OAKENGINE_E_INVALID for NULL `p`.
*/
OAKENGINE_API int oakengine_video_params_make(oak_video_params *p, int width,
int height, int time_base_num,
int time_base_den, int format,
int pixel_aspect_num,
int pixel_aspect_den,
int interlacing, int color_range,
int divider);
/**
* @brief Create an engine-side olive::VideoParams object from a POD.
*
* The returned pointer must be freed with oakengine_video_params_free().
* This is the only legal way for app code to construct a VideoParams object
* during the R6 C ABI migration.
*
* @return Engine-owned VideoParams pointer, or NULL if pod is NULL.
*/
OAKENGINE_API void *oakengine_video_params_create(const oak_video_params *pod);
/** @brief Free a VideoParams object created by oakengine_video_params_create(). */
OAKENGINE_API void oakengine_video_params_free(void *params);
/**
* @brief 1 when all user-facing fields of `a` and `b` match
* (VideoParams::operator==), 0 otherwise or when either is NULL.
*/
OAKENGINE_API int oakengine_video_params_equal(const oak_video_params *a,
const oak_video_params *b);
/**
* @brief 1 when the POD describes a usable video stream
* (VideoParams::is_valid(): positive dimensions, non-null pixel aspect,
* in-range pixel format), 0 otherwise or when `p` is NULL.
*/
OAKENGINE_API int oakengine_video_params_is_valid(const oak_video_params *p);
/**
* @brief Bytes per pixel of `format` (a PixelFormat::Format value) with
* `channels` channels (VideoParams::get_bytes_per_pixel()).
*/
OAKENGINE_API int oakengine_video_params_bytes_per_pixel(int format,
int channels);
/**
* @brief The engine-internal video channel count
* (VideoParams::k_internal_channel_count, i.e. RGBA).
*/
OAKENGINE_API int oakengine_video_params_internal_channel_count(void);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_VIDEOPARAMS_H */
+350
View File
@@ -0,0 +1,350 @@
/***
Oak - 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 OAKENGINE_VIEWER_H
#define OAKENGINE_VIEWER_H
#include <stdint.h>
#include "export.h"
#include "init.h"
#include "node.h"
#include "timeline.h"
#include "videoparams.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file viewer.h
* @brief C ABI for viewer nodes (olive::ViewerOutput and subclasses:
* Sequence, Footage)
*
* A viewer node is the bridge between a node graph and a monitor: it owns
* a playhead, a length, per-stream video/audio/subtitle parameters, a
* workarea and a marker list. This family covers the application-side
* uses of olive::ViewerOutput that are not already exposed through the
* sequence (timeline.h) or node (node.h) families.
*
* Handles: a viewer handle is simply an OakEngineNode* whose engine object
* is a ViewerOutput (validate with oakengine_viewer_from_node()). Borrowed,
* same lifetime rules as node.h. Change notifications (length/playhead/
* params/workarea-adjacent) are delivered through the event mechanism --
* subscribe with the OAKENGINE_EVENT_VIEWER_* ids from oakengine/events.h
* on the node handle.
*
* Conventions match the rest of the facade: rationals are int64
* numerator/denominator pairs (seconds), booleans are int, 0
* (OAKENGINE_OK)/negative OAKENGINE_E_* return codes, NULL handles are
* no-ops returning OAKENGINE_E_INVALID.
*/
/**
* @brief POD snapshot of a viewer's workarea (olive::TimelineWorkArea:
* range in/out + enabled flag). Rationals in seconds.
*/
typedef struct oakengine_viewer_workarea {
int64_t in_num;
int64_t in_den;
int64_t out_num;
int64_t out_den;
int enabled;
} oakengine_viewer_workarea;
/**
* @brief Return `node` if its engine object is a viewer (olive::ViewerOutput
* or subclass, e.g. Sequence/Footage), NULL otherwise. Replaces
* dynamic_cast<ViewerOutput*> at the app boundary; also the canonical way
* to validate a handle for this family.
*/
OAKENGINE_API OakEngineNode *oakengine_viewer_from_node(OakEngineNode *node);
/** @brief const overload of oakengine_viewer_from_node(). */
OAKENGINE_API const OakEngineNode *
oakengine_viewer_from_const_node(const OakEngineNode *node);
/* ---- Input ids / constants (ViewerOutput::k_* statics) ------------------ */
/** @brief ViewerOutput::k_video_params_input. Static string, never freed. */
OAKENGINE_API const char *oakengine_viewer_video_params_input_id(void);
/** @brief ViewerOutput::k_audio_params_input. */
OAKENGINE_API const char *oakengine_viewer_audio_params_input_id(void);
/** @brief ViewerOutput::k_subtitle_params_input. */
OAKENGINE_API const char *oakengine_viewer_subtitle_params_input_id(void);
/** @brief ViewerOutput::k_texture_input. */
OAKENGINE_API const char *oakengine_viewer_texture_input_id(void);
/** @brief ViewerOutput::k_samples_input. */
OAKENGINE_API const char *oakengine_viewer_samples_input_id(void);
/** @brief ViewerOutput::k_default_sample_format (olive::core::SampleFormat). */
OAKENGINE_API int oakengine_viewer_default_sample_format(void);
/* ---- Playhead / length --------------------------------------------------- */
/** @brief Current playhead in seconds (ViewerOutput::get_playhead()). */
OAKENGINE_API int oakengine_viewer_get_playhead(const OakEngineNode *self,
int64_t *num, int64_t *den);
/** @brief Move the playhead (ViewerOutput::set_playhead()). Emits
* OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED. */
OAKENGINE_API int oakengine_viewer_set_playhead(OakEngineNode *self,
int64_t num, int64_t den);
/**
* @brief Set the video parameters of stream `index` on `self`
* (ViewerOutput::set_video_params()). `self` must be a viewer node.
*/
OAKENGINE_API int oakengine_viewer_set_video_params(OakEngineNode *self,
const oak_video_params *params,
int index);
/**
* @brief Set the audio parameters of stream `index` on `self`
* (ViewerOutput::set_audio_params()). `self` must be a viewer node.
*/
OAKENGINE_API int oakengine_viewer_set_audio_params(OakEngineNode *self,
int sample_rate,
uint64_t channel_layout,
int format, int index);
/** @brief Content length in seconds (ViewerOutput::get_length()). */
OAKENGINE_API int oakengine_viewer_get_length(const OakEngineNode *self,
int64_t *num, int64_t *den);
/** @brief Video content length in seconds (ViewerOutput::get_video_length()). */
OAKENGINE_API int oakengine_viewer_get_video_length(const OakEngineNode *self,
int64_t *num,
int64_t *den);
/** @brief Audio content length in seconds (ViewerOutput::get_audio_length()). */
OAKENGINE_API int oakengine_viewer_get_audio_length(const OakEngineNode *self,
int64_t *num,
int64_t *den);
/* ---- Stream parameters ---------------------------------------------------- */
/**
* @brief Video params of stream `index` (ViewerOutput::get_video_params()).
* `out` is always written; an out-of-range index yields a zeroed struct
* (width/height 0 = invalid, matches an invalid olive::VideoParams).
*/
OAKENGINE_API int oakengine_viewer_get_video_params(
const OakEngineNode *self, int index, oak_video_params *out);
/**
* @brief Audio params of stream `index` (ViewerOutput::get_audio_params()).
* Any of the out pointers may be NULL. `format` is an
* olive::core::SampleFormat value; out-of-range index yields 0/0/0.
*/
OAKENGINE_API int oakengine_viewer_get_audio_params(
const OakEngineNode *self, int index, int *sample_rate,
uint64_t *channel_layout, int *format);
/** @brief Number of video streams (ViewerOutput::get_video_stream_count()). */
OAKENGINE_API int oakengine_viewer_get_video_stream_count(
const OakEngineNode *self);
/** @brief Number of audio streams (ViewerOutput::get_audio_stream_count()). */
OAKENGINE_API int oakengine_viewer_get_audio_stream_count(
const OakEngineNode *self);
/** @brief Number of subtitle streams (ViewerOutput::get_subtitle_stream_count()). */
OAKENGINE_API int oakengine_viewer_get_subtitle_stream_count(
const OakEngineNode *self);
/**
* @brief 1 if stream `index` of `track_type` (OAKENGINE_TRACK_TYPE_*) is
* enabled (VideoParams/AudioParams/SubtitleParams::enabled()), else 0;
* OAKENGINE_E_INVALID (< 0) on bad arguments.
*/
OAKENGINE_API int oakengine_viewer_get_stream_enabled(
const OakEngineNode *self, int track_type, int index);
/**
* @brief Number of subtitles in subtitle stream `index`
* (SubtitleParams::size()); < 0 on bad arguments.
*/
OAKENGINE_API int oakengine_viewer_get_subtitle_count(
const OakEngineNode *self, int index);
/**
* @brief Borrowed pointer to subtitle `sub_index` of subtitle stream
* `index` (a const olive::Subtitle*; the application copies the value out,
* it must not free or store it beyond the footage's lifetime). NULL on
* bad arguments.
*/
OAKENGINE_API const void *oakengine_viewer_get_subtitle_at(
const OakEngineNode *self, int index, int sub_index);
/**
* @brief 1 if the viewer has at least one enabled stream of `track_type`
* (OAKENGINE_TRACK_TYPE_* from timeline.h), else 0
* (ViewerOutput::has_enabled_video/audio/subtitle_streams()).
*/
OAKENGINE_API int oakengine_viewer_has_enabled_streams(
const OakEngineNode *self, int track_type);
/**
* @brief Params of the first enabled video stream
* (ViewerOutput::get_first_enabled_video_stream()); zeroed struct when
* none is enabled.
*/
OAKENGINE_API int oakengine_viewer_get_first_enabled_video_stream(
const OakEngineNode *self, oak_video_params *out);
/**
* @brief Number of enabled streams of all types
* (ViewerOutput::get_enabled_streams_as_references().size()).
*/
OAKENGINE_API int oakengine_viewer_get_enabled_stream_count(
const OakEngineNode *self);
/**
* @brief Write the enabled stream references
* (ViewerOutput::get_enabled_streams_as_references()) into caller arrays:
* `types[k]` = OAKENGINE_TRACK_TYPE_*, `indices[k]` = stream index within
* that type. At most `max` entries are written; returns the total count
* (call with max=0/NULL arrays to query, or use
* oakengine_viewer_get_enabled_stream_count()).
*/
OAKENGINE_API int oakengine_viewer_get_enabled_streams(
const OakEngineNode *self, int *types, int *indices, int max);
/* ---- Workarea -------------------------------------------------------------- */
/** @brief Snapshot of the viewer's workarea (ViewerOutput::get_work_area()
* range/enabled as POD). */
OAKENGINE_API int oakengine_viewer_get_workarea(
const OakEngineNode *self, oakengine_viewer_workarea *out);
/** @brief Set the workarea range (TimelineWorkArea::set_range()). Emits the
* workarea range notification on the underlying workarea object. */
OAKENGINE_API int oakengine_viewer_set_workarea_range(OakEngineNode *self,
int64_t in_num,
int64_t in_den,
int64_t out_num,
int64_t out_den);
/** @brief Enable/disable the workarea (TimelineWorkArea::set_enabled()). */
OAKENGINE_API int oakengine_viewer_set_workarea_enabled(OakEngineNode *self,
int enabled);
/* ---- Parameter setup / waveform --------------------------------------------- */
/** @brief Apply the application default parameters
* (ViewerOutput::set_default_parameters(): width/height/pixel aspect/
* interlacing/audio layout from Config, frame rate from
* DefaultSequenceFrameRate). */
OAKENGINE_API int oakengine_viewer_set_default_parameters(OakEngineNode *self);
/**
* @brief Create a command that sets the viewer's preview resolution divider
* (changes the k_video_params_input standard value). Returns an opaque command
* pointer, or NULL when `self` is not a viewer or `divider` is invalid.
*/
OAKENGINE_API void *oakengine_viewer_set_preview_divider_command(
OakEngineNode *self, int divider);
/**
* @brief Adopt the parameters of the given footage viewers
* (ViewerOutput::set_parameters_from_footage()). Every element of
* `footage` must itself be a viewer handle.
*/
OAKENGINE_API int oakengine_viewer_set_parameters_from_footage(
OakEngineNode *self, OakEngineNode *const *footage, int count);
/** @brief Enable/disable waveform cache requests
* (ViewerOutput::set_waveform_enabled()). */
OAKENGINE_API int oakengine_viewer_set_waveform_enabled(OakEngineNode *self,
int enabled);
/**
* @brief The waveform cache of the connected sample output, or NULL
* (ViewerOutput::get_connected_waveform()). Opaque borrowed pointer; the
* application only passes it through to its own audio monitor, it must not
* dereference it.
*/
OAKENGINE_API const void *
oakengine_viewer_get_connected_waveform(const OakEngineNode *self);
/**
* @brief Borrowed handle of the viewer's timeline marker list
* (ViewerOutput::get_markers()), for the oakengine_marker_list_* family
* and the OAKENGINE_EVENT_MARKER_LIST_* events. NULL when `self` is not a
* viewer.
*/
OAKENGINE_API OakEngineMarkerList *
oakengine_viewer_get_marker_list(OakEngineNode *self);
/**
* @brief Borrowed handle of the viewer's workarea
* (ViewerOutput::get_work_area()), for the oakengine_workarea_* family and
* the OAKENGINE_EVENT_WORKAREA_* events. NULL when `self` is not a viewer.
*/
OAKENGINE_API OakEngineWorkarea *
oakengine_viewer_get_workarea_handle(OakEngineNode *self);
/* ---- Playback cache / frame cache ------------------------------------------ */
/**
* @brief Opaque playback cache handle (olive::PlaybackCache).
*/
typedef struct OakEnginePlaybackCache OakEnginePlaybackCache;
/**
* @brief Opaque frame cache handle (olive::FrameHashCache).
*/
typedef struct OakEngineFrameCache OakEngineFrameCache;
/**
* @brief Borrowed playback cache of a viewer's connected output
* (ViewerOutput::get_connected_video_cache() for video, or from the
* ClipBlock::connected_video_cache()). Returns NULL when not available
* or when `self` is not a viewer/clip node.
*/
OAKENGINE_API OakEnginePlaybackCache *
oakengine_viewer_get_playback_cache(OakEngineNode *self);
/**
* @brief Static indicator height for playback cache rendering
* (PlaybackCache::get_cache_indicator_height()). > 0.
*/
OAKENGINE_API int oakengine_playback_cache_indicator_height(void);
/**
* @brief Fill `ranges` with the valid (cached) time ranges from the
* playback cache. `ranges` is an array of (in_num,in_den,out_num,out_den)
* int64_t quads; at most `max` ranges are written. Returns the number of
* ranges written, or OAKENGINE_E_INVALID on NULL cache.
*/
OAKENGINE_API int oakengine_playback_cache_valid_ranges(
OakEnginePlaybackCache *cache, int64_t *ranges, int max);
/**
* @brief Borrowed frame hash cache (FrameHashCache) of a viewer node
* (ViewerOutput has a get_video_cache(), etc.). Returns NULL when not
* available or when `self` is not a viewer node.
*/
OAKENGINE_API OakEngineFrameCache *
oakengine_viewer_get_frame_cache(OakEngineNode *self);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_VIEWER_H */
+146
View File
@@ -0,0 +1,146 @@
/***
Oak - 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 OAKENGINE_WORKER_H
#define OAKENGINE_WORKER_H
#include "export.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file worker.h
* @brief C ABI for the render worker process logic
*
* The render worker (oak-render-worker) is a headless render process spawned
* by the editor through the render worker pool. All of its runtime logic —
* Qt application setup, render backend initialization, the startup handshake
* and the NDJSON control-message loop — lives inside liboakengine behind this
* pure C interface, so the worker executable itself contains no engine C++
* ABI usage.
*
* Two entry levels are exposed:
*
* - oakengine_worker_main(): a drop-in main() for the worker executable.
* It creates the QGuiApplication, parses --backend, initializes the
* renderer, sends the startup handshake and runs the stdin/stdout NDJSON
* loop until a shutdown message or EOF.
*
* - The OakWorkerSession family: the same message-handling state machine
* in a transport-agnostic form, so tests (and alternative transports)
* can drive it line by line without spawning a process. Responses that
* the worker would write to stdout are returned through the buf/size
* convention instead.
*
* Conventions (mirrors ipc.h):
* - Returned handles are owned by the caller and must be released with the
* matching _free(). NULL is accepted by every function and yields a
* no-op / zero result.
* - String output uses the buf/size convention: the return value is the
* number of characters that would have been written excluding the NUL,
* so buf == NULL or a short buffer queries the required size. The output
* is NUL-terminated whenever buf_size > 0.
*/
typedef struct OakWorkerSession OakWorkerSession;
/**
* @brief Create a worker session for the given render backend.
*
* `backend` names the render backend ("opengl", "vulkan"); the session tries
* the dynamic backend first and falls back to the direct OpenGL renderer,
* exactly like the worker main. NULL, "" or "none" skips renderer creation
* entirely, producing a session that can parse and answer control messages
* but cannot actually render (useful for exercising error paths in tests).
*
* A QGuiApplication must exist before creating a session with a real
* backend. The returned handle is owned by the caller.
*/
OAKENGINE_API OakWorkerSession *
oakengine_worker_session_create(const char *backend);
OAKENGINE_API void oakengine_worker_session_free(OakWorkerSession *self);
/**
* @brief 1 if the session holds a successfully initialized render backend.
*/
OAKENGINE_API int
oakengine_worker_session_has_renderer(const OakWorkerSession *self);
/**
* @brief Load the engine runtime services the session depends on (config,
* node factory, color manager, frame/disk managers, project serializer).
*
* Idempotent in practice: the underlying services are process-wide
* singletons. Returns 1 on success, 0 on failure (NULL session).
*/
OAKENGINE_API int
oakengine_worker_session_initialize_runtime(OakWorkerSession *self);
/**
* @brief Build the startup handshake the worker sends to its parent
* (buf/size convention).
*
* Announces the protocol version and, when a renderer is present, the
* negotiated GL version. Returns the required size, or -1 on failure.
*/
OAKENGINE_API int
oakengine_worker_session_startup_handshake(OakWorkerSession *self, char *buf,
int buf_size);
/**
* @brief Handle one NDJSON control line and produce the response, if any.
*
* `line` is one complete JSON message (with or without the trailing
* newline). The response — what the worker main loop would write to stdout —
* is serialized into response_buf using the buf/size convention: the return
* value is the number of characters that would have been written excluding
* the NUL, so 0 means "no response" (e.g. a successful handshake or a
* shutdown message) and a positive value queries/fills the response. A
* malformed `line` yields an error response, not a failure.
*
* Returns -1 when the handler itself failed (the worker main treats this as
* a fatal error for its exit code, though it keeps draining input).
*/
OAKENGINE_API int
oakengine_worker_session_handle_json(OakWorkerSession *self, const char *line,
char *response_buf, int response_buf_size);
/**
* @brief 1 once a shutdown control message has been received.
*/
OAKENGINE_API int
oakengine_worker_session_shutdown_requested(const OakWorkerSession *self);
/**
* @brief Full render-worker main(). `argc`/`argv` are passed through from
* the executable's main; "--backend <name>" selects the render backend.
*
* Returns the process exit code (0 on clean shutdown).
*/
OAKENGINE_API int oakengine_worker_main(int argc, char **argv);
#ifdef __cplusplus
}
#endif
#endif /* OAKENGINE_WORKER_H */