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:
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
+1079
-1
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
@@ -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.
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user