feat(app): multicam panel with live angle grid, switching, timeline enable
- New MulticamPanel: rows/cols angle grid with the current angle highlighted, click-to-switch, 1-9 switch-and-split and cmd-1-9 switch-only shortcuts (focused-panel routed), deferred switch queue during playback. - src/oakui/multicam.rs: clip->connected-sequence resolution, multicam state detection (selection then playhead fallbacks), per-angle frame requests rendered through the process backend into an LRU cache. - Timeline clip context menu Multi-Cam checkable item wired to oaktimeline::multicam enable/disable with undo. - Engine trait extended (real + mock); mock drives the real command path with synthesized angle frames.
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EDITOR_RENDER_CACHE_H
|
||||
#define OAK_EDITOR_RENDER_CACHE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Same-dir quoted includes: inside this build the engine-style spelling
|
||||
// "render/renderer.h" resolves to the transition bridge headers, so the
|
||||
// public headers reference each other relative to their own directory.
|
||||
#include "error.h"
|
||||
#include "renderer.h" /* OakCodecFrame */
|
||||
#include "node/node.h" /* OakNodeNode */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file cache.h
|
||||
* @brief C ABI for the oakrender playback/frame-hash caches
|
||||
* (olive::PlaybackCache / olive::FrameHashCache), M7 §2.2.
|
||||
*
|
||||
* An OakRenderCache is a by-value reference-counted handle (shared_ptr
|
||||
* semantics, see oakcommon's common/handle.h) boxing an
|
||||
* olive::FrameHashCache (created without a parent node). Handles from
|
||||
* oakrender_cache_create() are owned by the caller (reference count 1)
|
||||
* and must be released with oakrender_cache_free(); handles from
|
||||
* oakrender_cache_wrap_borrowed() are borrowed (release only frees the
|
||||
* box).
|
||||
*
|
||||
* All timestamps are int64 frame numbers in the cache's timebase (see
|
||||
* oakrender_cache_set_timebase()); a cache without a valid timebase
|
||||
* treats timestamps as whole seconds.
|
||||
*
|
||||
* No cache events cross the boundary (M7 §2.2, 2026-08 revision):
|
||||
* invalidate/validate are triggered by and known to the caller; the
|
||||
* facade re-emits notifications after the triggering command.
|
||||
*/
|
||||
typedef struct OakRenderCache {
|
||||
void *ctx; /**< Opaque pointer to the reference-counted object. */
|
||||
void (*addref)(void *ctx); /**< Atomically increments the count. */
|
||||
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
|
||||
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
|
||||
} OakRenderCache;
|
||||
|
||||
/**
|
||||
* @brief Create a detached frame hash cache (no parent node, no
|
||||
* timebase). Owned by the caller.
|
||||
*
|
||||
* @return Cache handle with reference count 1; ctx is NULL on
|
||||
* allocation failure.
|
||||
*/
|
||||
OakRenderCache oakrender_cache_create(void);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a cache created by
|
||||
* oakrender_cache_create(). Convenience wrapper around
|
||||
* cache->release(cache->ctx). NULL / empty-handle no-op; clears
|
||||
* cache->ctx after releasing.
|
||||
*/
|
||||
void oakrender_cache_free(OakRenderCache *cache);
|
||||
|
||||
/**
|
||||
* @brief Borrowed handle wrapping a native frame cache pointer obtained
|
||||
* through oaknode (oaknode_node_get_video_frame_cache()).
|
||||
*
|
||||
* The cache itself stays owned by its node: release() on this handle
|
||||
* only frees the box. Empty handle (ctx == NULL) for a NULL native
|
||||
* pointer.
|
||||
*/
|
||||
OakRenderCache oakrender_cache_wrap_borrowed(void *native_cache);
|
||||
|
||||
/**
|
||||
* @brief Cache flavours owned by a node
|
||||
* (olive::Node's video/thumbnail/audio/waveform caches).
|
||||
*/
|
||||
enum OakRenderCacheKind {
|
||||
OAKRENDER_CACHE_VIDEO_FRAME = 0, /**< olive::FrameHashCache */
|
||||
OAKRENDER_CACHE_THUMBNAIL = 1, /**< olive::ThumbnailCache */
|
||||
OAKRENDER_CACHE_AUDIO_PLAYBACK = 2, /**< olive::AudioPlaybackCache */
|
||||
OAKRENDER_CACHE_AUDIO_WAVEFORM = 3 /**< olive::AudioWaveformCache */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create a cache of the given kind with a parent node (the
|
||||
* native back-pointer stays inside oakrender; it is used for
|
||||
* project cache-path resolution and job bookkeeping only).
|
||||
*
|
||||
* Owned by the caller (reference count 1); release with
|
||||
* oakrender_cache_free(). Empty handle for an empty parent handle, an
|
||||
* unknown kind, or on allocation failure.
|
||||
*/
|
||||
OakRenderCache oakrender_cache_create_for_node(OakNodeNode parent,
|
||||
int kind);
|
||||
|
||||
/**
|
||||
* @brief Cache UUID as canonical text, two-stage
|
||||
* (PlaybackCache::get_uuid()).
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), or a negative
|
||||
* OAKRENDER_E_* code for an empty cache.
|
||||
*/
|
||||
int oakrender_cache_get_uuid(OakRenderCache cache, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Request caching of a time range on behalf of a viewer
|
||||
* (PlaybackCache::request()).
|
||||
*
|
||||
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for an empty cache /
|
||||
* context handle or a context that is not a viewer.
|
||||
*/
|
||||
int oakrender_cache_request(OakRenderCache cache, OakNodeNode context,
|
||||
int64_t in_num, int64_t in_den,
|
||||
int64_t out_num, int64_t out_den);
|
||||
|
||||
/**
|
||||
* @brief Load/save the cache's on-disk state (PlaybackCache::load_state()
|
||||
* / save_state()). OAKRENDER_E_INVALID for an empty cache.
|
||||
*/
|
||||
int oakrender_cache_load_state(OakRenderCache cache);
|
||||
int oakrender_cache_save_state(OakRenderCache cache);
|
||||
|
||||
/**
|
||||
* @brief Enable/disable persisting this cache
|
||||
* (PlaybackCache::set_saving_enabled()).
|
||||
*/
|
||||
int oakrender_cache_set_saving_enabled(OakRenderCache cache, int enabled);
|
||||
|
||||
/**
|
||||
* @brief Pass this cache's ranges through to another cache
|
||||
* (PlaybackCache::set_passthrough()). OAKRENDER_E_INVALID for an
|
||||
* empty cache or an empty `other`.
|
||||
*/
|
||||
int oakrender_cache_set_passthrough(OakRenderCache cache,
|
||||
OakRenderCache other);
|
||||
|
||||
/**
|
||||
* @brief The on-disk filename for the frame at a time
|
||||
* (FrameHashCache::get_valid_cache_filename()), two-stage.
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), or a negative
|
||||
* OAKRENDER_E_* code (OAKRENDER_E_INVALID when the cache is not
|
||||
* a frame hash cache).
|
||||
*/
|
||||
int oakrender_cache_get_valid_cache_filename(OakRenderCache cache,
|
||||
int64_t time_num,
|
||||
int64_t time_den, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief The passthrough ranges as flat {in_n, in_d, out_n, out_d}
|
||||
* quadruples (PlaybackCache::get_passthroughs(); only the ranges
|
||||
* cross the boundary, the per-range cache UUID text stays
|
||||
* internal).
|
||||
*
|
||||
* Two-stage: call with ranges == NULL (or max_ranges == 0) to get the
|
||||
* count; then call with a buffer of max_ranges * 4 int64_t values.
|
||||
*
|
||||
* @return Range count (>= 0), or a negative OAKRENDER_E_* code.
|
||||
*/
|
||||
int oakrender_cache_get_passthroughs(OakRenderCache cache, int64_t *ranges,
|
||||
int max_ranges);
|
||||
|
||||
/**
|
||||
* @brief The cache's frame timebase (FrameHashCache::get_timebase()).
|
||||
* Out params may individually be NULL. OAKRENDER_E_INVALID for
|
||||
* an empty cache or a non-frame-hash cache.
|
||||
*/
|
||||
int oakrender_cache_get_timebase(OakRenderCache cache, int *num,
|
||||
int *den);
|
||||
|
||||
/**
|
||||
* @brief Lock/unlock the cache's internal mutex (PlaybackCache::mutex()).
|
||||
* Empty cache is a no-op. Always pair the calls.
|
||||
*/
|
||||
void oakrender_cache_lock(OakRenderCache cache);
|
||||
void oakrender_cache_unlock(OakRenderCache cache);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
namespace olive { class PlaybackCache; }
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Borrowed access to the underlying C++ cache (C++ only, for
|
||||
* oakrender-internal adapters such as PreviewAutoCacher). Valid
|
||||
* while the handle is held. NULL-safe.
|
||||
*/
|
||||
olive::PlaybackCache *oakrender_cache_get_native(OakRenderCache cache);
|
||||
|
||||
/**
|
||||
* @brief Set the frame timebase used to interpret all timestamps of this
|
||||
* cache (FrameHashCache::set_timebase()).
|
||||
*
|
||||
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for an empty cache or
|
||||
* non-positive num/den.
|
||||
*/
|
||||
int oakrender_cache_set_timebase(OakRenderCache cache, int num, int den);
|
||||
|
||||
/**
|
||||
* @brief Set the cache UUID used in on-disk frame cache filenames
|
||||
* (PlaybackCache::set_uuid()).
|
||||
*
|
||||
* @return OAKRENDER_OK or OAKRENDER_E_INVALID.
|
||||
*/
|
||||
int oakrender_cache_set_uuid(OakRenderCache cache, const char *uuid);
|
||||
|
||||
/**
|
||||
* @brief Mark the timestamp range [in_ts, out_ts) invalidated
|
||||
* (PlaybackCache::invalidate()). Empty cache is a no-op.
|
||||
*/
|
||||
void oakrender_cache_invalidate(OakRenderCache cache, int64_t in_ts,
|
||||
int64_t out_ts);
|
||||
|
||||
/**
|
||||
* @brief Mark a rational time range invalidated
|
||||
* (PlaybackCache::invalidate(TimeRange)). Empty cache is a no-op.
|
||||
*/
|
||||
void oakrender_cache_invalidate_range(OakRenderCache cache,
|
||||
int64_t in_num, int64_t in_den,
|
||||
int64_t out_num, int64_t out_den);
|
||||
|
||||
/**
|
||||
* @brief Mark the timestamp range [in_ts, out_ts) validated
|
||||
* (PlaybackCache::validate()). Empty cache is a no-op.
|
||||
*/
|
||||
void oakrender_cache_validate(OakRenderCache cache, int64_t in_ts,
|
||||
int64_t out_ts);
|
||||
|
||||
/**
|
||||
* @brief 1 when the cache holds any validated range
|
||||
* (PlaybackCache::has_validated_ranges()), 0 otherwise / empty.
|
||||
*/
|
||||
int oakrender_cache_has_validated_ranges(OakRenderCache cache);
|
||||
|
||||
/**
|
||||
* @brief Timeline cache indicator height in pixels
|
||||
* (PlaybackCache::get_cache_indicator_height()). Constant query.
|
||||
*/
|
||||
int oakrender_cache_indicator_height(void);
|
||||
|
||||
/**
|
||||
* @brief The invalidated sub-ranges of [in, out) as flat
|
||||
* {in_n, in_d, out_n, out_d} quadruples
|
||||
* (PlaybackCache::get_invalidated_ranges()).
|
||||
*
|
||||
* Two-stage: call with ranges == NULL (or max_ranges == 0) to get the
|
||||
* count; then call with a buffer of max_ranges * 4 int64_t values.
|
||||
*
|
||||
* @return Range count (>= 0), or a negative OAKRENDER_E_* code.
|
||||
*/
|
||||
int oakrender_cache_get_invalidated_ranges(OakRenderCache c,
|
||||
int64_t in_num, int64_t in_den, int64_t out_num, int64_t out_den,
|
||||
int64_t *ranges, int max_ranges);
|
||||
|
||||
/**
|
||||
* @brief Load a cached frame from disk
|
||||
* (FrameHashCache::load_cache_frame(cache_path, uuid, ts)).
|
||||
*
|
||||
* @param path Cache directory (e.g. oakrender_disk_cache_path()).
|
||||
* @param uuid Cache UUID of the producing node.
|
||||
* @param out_frame Receives an owned frame handle (release with
|
||||
* oakrender_codec_frame_free()).
|
||||
*
|
||||
* @return OAKRENDER_OK, OAKRENDER_E_INVALID (empty/NULL argument), or
|
||||
* OAKRENDER_E_NOT_FOUND (no cached frame at `ts` / undecodable).
|
||||
*/
|
||||
int oakrender_frame_cache_load(OakRenderCache cache, const char *path,
|
||||
const char *uuid, int64_t ts,
|
||||
OakCodecFrame *out_frame);
|
||||
|
||||
/**
|
||||
* @brief Save a frame to the disk cache under the cache's timebase and
|
||||
* the frame's own timestamp (FrameHashCache::save_cache_frame()).
|
||||
* Empty/NULL arguments are a no-op.
|
||||
*/
|
||||
void oakrender_frame_cache_save(OakRenderCache cache, const char *path,
|
||||
const char *uuid, OakCodecFrame frame);
|
||||
|
||||
/* ---- Debug --------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Number of live oakrender-owned objects (caches, textures,
|
||||
* frames, color processors) for leak assertions in tests.
|
||||
*/
|
||||
int oakrender_debug_alive_count(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_RENDER_CACHE_H
|
||||
@@ -0,0 +1,120 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EDITOR_RENDER_CANCELATOM_H
|
||||
#define OAK_EDITOR_RENDER_CANCELATOM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
namespace olive { class CancelAtom; }
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file cancelatom.h
|
||||
* @brief C ABI for the oakrender cancellation primitive
|
||||
* (olive::CancelAtom), a thread-safe cancel flag shared between a
|
||||
* render/encode caller and its worker.
|
||||
*
|
||||
* OakCancelAtom follows the neutral by-value handle convention (see
|
||||
* oakcommon's common/handle.h): oakrender_cancelatom_init() returns a
|
||||
* handle whose underlying object has reference count 1, the addref and
|
||||
* release function pointers adjust that count atomically (release
|
||||
* destroys the object at zero), and abi_version is always
|
||||
* OAKRENDER_ABI_VERSION. Copying the struct copies the pointer, not the
|
||||
* count: call addref for every additional long-lived copy and release (or
|
||||
* oakrender_cancelatom_free()) when done with each copy. Functions that
|
||||
* only use a handle take it BY VALUE; an empty handle (ctx == NULL) is
|
||||
* reported as OAKRENDER_E_INVALID.
|
||||
*/
|
||||
typedef struct OakCancelAtom {
|
||||
void *ctx; /**< Opaque pointer to the reference-counted object. */
|
||||
void (*addref)(void *ctx); /**< Atomically increments the count. */
|
||||
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
|
||||
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
|
||||
} OakCancelAtom;
|
||||
|
||||
/**
|
||||
* @brief Create a cancellation atom in the not-cancelled state.
|
||||
*
|
||||
* @return Handle with reference count 1; ctx is NULL on allocation
|
||||
* failure.
|
||||
*/
|
||||
OakCancelAtom oakrender_cancelatom_init(void);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a cancellation atom.
|
||||
*
|
||||
* Convenience wrapper around atom->release(atom->ctx): decrements the
|
||||
* atomic reference count and destroys the object when it reaches zero,
|
||||
* then nulls atom->ctx. No-op when atom is NULL or atom->ctx is NULL.
|
||||
*/
|
||||
void oakrender_cancelatom_free(OakCancelAtom *atom);
|
||||
|
||||
/**
|
||||
* @brief Set the cancel flag (CancelAtom::cancel()). Thread-safe.
|
||||
*
|
||||
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for an empty handle.
|
||||
*/
|
||||
int oakrender_cancelatom_cancel(OakCancelAtom atom);
|
||||
|
||||
/**
|
||||
* @brief Read the cancel flag (CancelAtom::is_cancelled()).
|
||||
*
|
||||
* Reading a set flag also records that a consumer heard the
|
||||
* cancellation; see oakrender_cancelatom_heard_cancel().
|
||||
*
|
||||
* @param cancelled Receives 1 when cancelled, 0 otherwise.
|
||||
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for an empty handle or a
|
||||
* NULL out parameter.
|
||||
*/
|
||||
int oakrender_cancelatom_is_cancelled(OakCancelAtom atom, int *cancelled);
|
||||
|
||||
/**
|
||||
* @brief Whether any consumer has observed the cancel flag through
|
||||
* oakrender_cancelatom_is_cancelled() (CancelAtom::heard_cancel()).
|
||||
*
|
||||
* @param heard Receives 1 when the cancellation was heard, 0 otherwise.
|
||||
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for an empty handle or a
|
||||
* NULL out parameter.
|
||||
*/
|
||||
int oakrender_cancelatom_heard_cancel(OakCancelAtom atom, int *heard);
|
||||
#ifdef __cplusplus
|
||||
|
||||
/**
|
||||
* @brief Borrowed access to the underlying C++ atom (C++ only, for
|
||||
* adapter layers). Valid while the handle is held. NULL-safe.
|
||||
*/
|
||||
olive::CancelAtom *oakrender_cancelatom_get_native(OakCancelAtom atom);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_RENDER_CANCELATOM_H
|
||||
@@ -0,0 +1,255 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EDITOR_RENDER_COLOR_H
|
||||
#define OAK_EDITOR_RENDER_COLOR_H
|
||||
|
||||
#include "error.h"
|
||||
#include "renderer.h"
|
||||
#include "common/colortransform.h" /* OakColorTransform */
|
||||
#include "node/colormanager.h" /* OakNodeColorManager */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file color.h
|
||||
* @brief C ABI for oakrender color processing (olive::ColorProcessor) and
|
||||
* the process-wide default OCIO config (olive::ColorManager
|
||||
* statics), M7 §2.3.
|
||||
*
|
||||
* An OakColorProcessor is a by-value reference-counted handle (shared_ptr
|
||||
* semantics, see oakcommon's common/handle.h) boxing a ColorProcessorPtr
|
||||
* (ColorProcessor is shared_ptr-managed); release with
|
||||
* oakrender_color_processor_free(). Empty handles (ctx == NULL) are
|
||||
* accepted by every function and yield a no-op / OAKRENDER_E_INVALID.
|
||||
*
|
||||
* Processors are built against the process-wide default OCIO config
|
||||
* (olive::ColorManager::get_default_config()): the $OCIO config when the
|
||||
* environment variable is set, otherwise the config extracted to the
|
||||
* user configuration location. oakrender_color_manager_set_up_default_config()
|
||||
* (re)builds it.
|
||||
*/
|
||||
|
||||
/** Direction values for oakrender_color_processor_create(). */
|
||||
enum {
|
||||
OAKRENDER_COLOR_DIRECTION_NORMAL = 0,
|
||||
OAKRENDER_COLOR_DIRECTION_INVERSE = 1
|
||||
};
|
||||
|
||||
typedef struct OakColorProcessor {
|
||||
void *ctx; /**< Opaque pointer to the reference-counted object. */
|
||||
void (*addref)(void *ctx); /**< Atomically increments the count. */
|
||||
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
|
||||
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
|
||||
} OakColorProcessor;
|
||||
|
||||
/**
|
||||
* @brief Create a colorspace-to-colorspace processor on the default
|
||||
* OCIO config.
|
||||
*
|
||||
* @param src_space Source colorspace name (role names are resolved).
|
||||
* @param dst_transform Destination colorspace / output transform name.
|
||||
* @param direction OAKRENDER_COLOR_DIRECTION_NORMAL (src -> dst) or
|
||||
* OAKRENDER_COLOR_DIRECTION_INVERSE (dst -> src).
|
||||
*
|
||||
* OCIO failures are non-fatal (matching the C++ behavior): the handle is
|
||||
* still returned but oakrender_color_processor_is_valid() reports 0 and
|
||||
* conversions are pass-through.
|
||||
*
|
||||
* @return Processor handle with reference count 1; ctx is NULL for
|
||||
* NULL/empty strings, an unknown direction, no default config,
|
||||
* or allocation failure.
|
||||
*/
|
||||
OakColorProcessor oakrender_color_processor_create(const char *src_space,
|
||||
const char *dst_transform,
|
||||
int direction);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a processor handle. Convenience
|
||||
* wrapper around processor->release(processor->ctx). NULL /
|
||||
* empty-handle no-op; clears processor->ctx after releasing.
|
||||
*/
|
||||
void oakrender_color_processor_free(OakColorProcessor *processor);
|
||||
|
||||
/**
|
||||
* @brief 1 when the processor holds a valid OCIO processor
|
||||
* (ColorProcessor::get_processor() != null), 0 otherwise / empty.
|
||||
*/
|
||||
int oakrender_color_processor_is_valid(OakColorProcessor processor);
|
||||
|
||||
/**
|
||||
* @brief Create a processor from an input colorspace and a destination
|
||||
* transform on a node's color manager
|
||||
* (ColorProcessor::create(ColorManager*, input, dest, dir)).
|
||||
*
|
||||
* @param manager Borrowed manager handle (e.g.
|
||||
* oaknode_colormanager_wrap_borrowed()).
|
||||
* @param direction OAKRENDER_COLOR_DIRECTION_NORMAL / _INVERSE.
|
||||
* @return Processor handle with reference count 1; ctx is NULL for
|
||||
* empty/invalid arguments or allocation failure.
|
||||
*/
|
||||
OakColorProcessor oakrender_color_processor_create_transform(
|
||||
OakNodeColorManager manager, const char *input,
|
||||
OakColorTransform dest, int direction);
|
||||
|
||||
/**
|
||||
* @brief Create a processor from a LUT file on a node's color manager
|
||||
* (OCIO FileTransform with linear interpolation; direction
|
||||
* selects forward/inverse).
|
||||
*
|
||||
* @return Processor handle with reference count 1; ctx is NULL for
|
||||
* empty/invalid arguments, an unreadable LUT, or allocation
|
||||
* failure.
|
||||
*/
|
||||
OakColorProcessor oakrender_color_processor_create_lut(
|
||||
OakNodeColorManager manager, const char *path, int direction);
|
||||
|
||||
/**
|
||||
* @brief Grading-primary transform styles for
|
||||
* oakrender_color_processor_create_grading_primary().
|
||||
*/
|
||||
enum OakRenderGradingPrimaryStyle {
|
||||
OAKRENDER_GRADING_PRIMARY_LIN = 0, /**< OCIO GRADING_LIN */
|
||||
OAKRENDER_GRADING_PRIMARY_LOG = 1 /**< OCIO GRADING_LOG */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create a dynamic grading-primary processor on a node's color
|
||||
* manager (OCIO GradingPrimaryTransform, forward direction).
|
||||
*
|
||||
* @return Processor handle with reference count 1; ctx is NULL for
|
||||
* invalid arguments or allocation failure.
|
||||
*/
|
||||
OakColorProcessor oakrender_color_processor_create_grading_primary(
|
||||
OakNodeColorManager manager, int style);
|
||||
|
||||
/* ---- LUT library ---------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 1 when `extension` (without dot, case-insensitive) is a
|
||||
* supported LUT extension (LUTLibrary::is_supported_extension()).
|
||||
*/
|
||||
int oakrender_lut_is_supported_extension(const char *extension);
|
||||
|
||||
/**
|
||||
* @brief Number of supported LUT extensions
|
||||
* (LUTLibrary::supported_extensions()).
|
||||
*/
|
||||
int oakrender_lut_supported_extensions_count(void);
|
||||
|
||||
/**
|
||||
* @brief Supported LUT extension at `index`, two-stage string.
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), or a negative
|
||||
* OAKRENDER_E_* code for an out-of-range index.
|
||||
*/
|
||||
int oakrender_lut_supported_extension_at(int index, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Convert a single RGBA color (ColorProcessor::convert_color()).
|
||||
* On an invalid processor the input is copied through.
|
||||
*
|
||||
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for empty/NULL arguments.
|
||||
*/
|
||||
int oakrender_color_processor_convert(OakColorProcessor processor,
|
||||
double ir, double ig, double ib,
|
||||
double ia, double *out_r, double *out_g,
|
||||
double *out_b, double *out_a);
|
||||
|
||||
/**
|
||||
* @brief Convert a CPU frame's pixels through the processor, in place
|
||||
* (olive::ColorProcessor::convert_frame()).
|
||||
*
|
||||
* The frame's data buffer is rewritten through an OCIO PackedImageDesc
|
||||
* view; nothing is allocated and the frame handle stays owned by the
|
||||
* caller. A processor whose underlying OCIO processor is null
|
||||
* (oakrender_color_processor_create() treats lookup failure as
|
||||
* non-fatal) is a pass-through and returns OAKRENDER_OK, mirroring the
|
||||
* C++ API.
|
||||
*
|
||||
* @return OAKRENDER_OK, OAKRENDER_E_INVALID for empty/uninitialized
|
||||
* arguments, or OAKRENDER_E_FAILED on an internal exception.
|
||||
*/
|
||||
int oakrender_color_processor_convert_frame(OakColorProcessor processor,
|
||||
OakCodecFrame frame);
|
||||
|
||||
/* ---- ColorManager statics ------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief (Re)build the process-wide default OCIO config
|
||||
* (ColorManager::set_up_default_config()).
|
||||
*
|
||||
* @return OAKRENDER_OK, or OAKRENDER_E_FAILED when no config could be
|
||||
* created.
|
||||
*/
|
||||
int oakrender_color_manager_set_up_default_config(void);
|
||||
|
||||
/**
|
||||
* @brief Describe the active default config: the $OCIO path when set,
|
||||
* otherwise the extracted default config's path. Two-stage string
|
||||
* getter: returns the required buffer size including NUL; pass
|
||||
* buf == NULL or too small a buffer to query the size.
|
||||
*
|
||||
* @return Required size (non-negative), or OAKRENDER_E_STATE when no
|
||||
* default config exists.
|
||||
*/
|
||||
int oakrender_color_manager_get_config(char *buf, int n);
|
||||
|
||||
/**
|
||||
* @brief OCIO cache id of the display/view transform of the active
|
||||
* default config, computed from the config's reference colorspace
|
||||
* (a stable identifier usable as a conversion cache key).
|
||||
*
|
||||
* Two-stage string getter (same convention as
|
||||
* oakrender_color_manager_get_config()).
|
||||
*
|
||||
* @return Required size (non-negative), OAKRENDER_E_INVALID (NULL/empty
|
||||
* display or view), OAKRENDER_E_STATE (no default config), or
|
||||
* OAKRENDER_E_NOT_FOUND (unknown display/view).
|
||||
*/
|
||||
int oakrender_color_manager_display_transform(const char *display,
|
||||
const char *view, char *buf,
|
||||
int n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
#include <memory>
|
||||
namespace olive { class ColorProcessor; }
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Borrowed access to the underlying C++ processor (C++ only, for
|
||||
* adapter layers; a shared_ptr copy keeps the object alive).
|
||||
* Empty shared_ptr for an empty handle.
|
||||
*/
|
||||
std::shared_ptr<olive::ColorProcessor> oakrender_color_processor_get_native(
|
||||
OakColorProcessor processor);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_RENDER_COLOR_H
|
||||
@@ -0,0 +1,84 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EDITOR_RENDER_COPIER_H
|
||||
#define OAK_EDITOR_RENDER_COPIER_H
|
||||
|
||||
#include "node/node.h"
|
||||
#include "node/project.h"
|
||||
#include "render/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Reference-counted handle to a project copier
|
||||
* (olive::ProjectCopier): deep-copies a project graph for
|
||||
* background processing (export/precache).
|
||||
*
|
||||
* By-value handle (shared_ptr semantics, see oakcommon's
|
||||
* common/handle.h): oakrender_project_copier_create() returns a handle
|
||||
* with reference count 1; release it with
|
||||
* oakrender_project_copier_free().
|
||||
*/
|
||||
typedef struct OakRenderProjectCopier {
|
||||
void *ctx; /**< Opaque pointer to the reference-counted object. */
|
||||
void (*addref)(void *ctx); /**< Atomically increments the count. */
|
||||
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
|
||||
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
|
||||
} OakRenderProjectCopier;
|
||||
|
||||
/**
|
||||
* @brief Create a copier. The copy is built by
|
||||
* oakrender_project_copier_set_project().
|
||||
*
|
||||
* @return Copier handle with reference count 1; ctx is NULL on
|
||||
* allocation failure.
|
||||
*/
|
||||
OakRenderProjectCopier oakrender_project_copier_create(void);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a copier; the final release frees the
|
||||
* copier AND its copied project. NULL / empty-handle no-op; clears
|
||||
* copier->ctx after releasing.
|
||||
*/
|
||||
void oakrender_project_copier_free(OakRenderProjectCopier *copier);
|
||||
|
||||
/** @brief (Re)build the copy from `project` (borrowed handle). */
|
||||
int oakrender_project_copier_set_project(OakRenderProjectCopier copier,
|
||||
OakNodeProject project);
|
||||
|
||||
/** @brief The copied counterpart of an original node (borrowed handle;
|
||||
* freeing it only releases the handle box), empty handle when the
|
||||
* node is not in the copied project. */
|
||||
OakNodeNode oakrender_project_copier_get_copy(
|
||||
OakRenderProjectCopier copier, OakNodeNode original);
|
||||
|
||||
/** @brief The copied project (borrowed handle; freeing it only releases
|
||||
* the handle box). */
|
||||
OakNodeProject oakrender_project_copier_get_copied_project(
|
||||
OakRenderProjectCopier copier);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_RENDER_COPIER_H
|
||||
@@ -0,0 +1,49 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EDITOR_RENDER_ERROR_H
|
||||
#define OAK_EDITOR_RENDER_ERROR_H
|
||||
|
||||
/**
|
||||
* @brief Status and error codes shared by all oakrender C API families.
|
||||
*
|
||||
* Return-code convention (mirrors include/node/error.h):
|
||||
* 0 (OAKRENDER_OK) on success, a negative OAKRENDER_E_* error code on
|
||||
* failure. String getters return the required buffer size in bytes
|
||||
* (including the terminating NUL) as a non-negative value instead.
|
||||
*/
|
||||
/**
|
||||
* @brief Current ABI version stamped into every oakrender handle.
|
||||
*
|
||||
* Bump whenever a handle layout or the semantics of any exported function
|
||||
* change incompatibly. Consumers should compare a handle's abi_version
|
||||
* field against the value they were compiled with before dereferencing
|
||||
* ctx.
|
||||
*/
|
||||
#define OAKRENDER_ABI_VERSION 1
|
||||
|
||||
#define OAKRENDER_OK 0 /**< Success. */
|
||||
#define OAKRENDER_E_INVALID (-70001) /**< NULL handle or invalid argument. */
|
||||
#define OAKRENDER_E_STATE (-70002) /**< Call not valid in the current state. */
|
||||
#define OAKRENDER_E_FAILED (-70003) /**< The underlying operation failed. */
|
||||
#define OAKRENDER_E_NOT_FOUND (-70004) /**< Index out of range / entry not found. */
|
||||
#define OAKRENDER_E_NOMEM (-70005) /**< Allocation failed. */
|
||||
|
||||
#endif //OAK_EDITOR_RENDER_ERROR_H
|
||||
@@ -0,0 +1,167 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EDITOR_RENDER_MANAGER_H
|
||||
#define OAK_EDITOR_RENDER_MANAGER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// See cache.h for why these are same-dir relative includes.
|
||||
#include "node/node.h" /* OakNodeNode (by-value handle) */
|
||||
#include "cache.h" /* OakCodecFrame */
|
||||
#include "color.h" /* OakColorProcessor */
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file manager.h
|
||||
* @brief C ABI for the oakrender render manager / preview auto-cacher /
|
||||
* disk cache singletons (olive::RenderManager,
|
||||
* olive::PreviewAutoCacher, olive::DiskManager), M7 §2.4.
|
||||
*
|
||||
* The render manager is a process-wide singleton gated by
|
||||
* oakrender_manager_init() / oakrender_manager_shutdown(). Functions
|
||||
* that need it return OAKRENDER_E_STATE when it is not up.
|
||||
*
|
||||
* The frame request callback is the asynchronous command return channel
|
||||
* (M7 §2.2 note): it fires on a render worker thread, possibly after
|
||||
* cancellation. The delivered OakCodecFrame is owned by the callback
|
||||
* recipient (release with oakrender_codec_frame_free()); an empty frame
|
||||
* (ctx == NULL) signals "no result" (cancelled or failed). Beyond this
|
||||
* callback there are no event subscription interfaces.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Create the RenderManager singleton (spawns render/audio
|
||||
* threads, loads the configured backend).
|
||||
*
|
||||
* @return OAKRENDER_OK, OAKRENDER_E_STATE (already initialized), or
|
||||
* OAKRENDER_E_FAILED.
|
||||
*/
|
||||
int oakrender_manager_init(void);
|
||||
|
||||
/**
|
||||
* @brief Destroy the RenderManager singleton. No-op when not
|
||||
* initialized.
|
||||
*/
|
||||
void oakrender_manager_shutdown(void);
|
||||
|
||||
/**
|
||||
* @brief Completion callback of an asynchronous frame request.
|
||||
*
|
||||
* @param frame Owned frame handle, or an empty handle (ctx == NULL)
|
||||
* when the request finished without a result (cancelled/failed).
|
||||
* @param ts The request's timestamp, passed back verbatim.
|
||||
*/
|
||||
typedef void (*oakrender_frame_ready_fn)(OakCodecFrame frame, int64_t ts,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Asynchronously render one frame of `viewer` at `ts`
|
||||
* (PreviewAutoCacher::get_single_frame()).
|
||||
*
|
||||
* `ts` is a frame number in the viewer node's video timebase (a whole
|
||||
* second count when the viewer carries no valid timebase). The
|
||||
* completion is delivered through `cb`; until then the request can be
|
||||
* cancelled with oakrender_cancel_request().
|
||||
*
|
||||
* @return A positive request id, or a negative OAKRENDER_E_* code
|
||||
* (OAKRENDER_E_INVALID for an empty viewer handle or NULL
|
||||
* callback, OAKRENDER_E_STATE when the manager is not
|
||||
* initialized, OAKRENDER_E_FAILED when no ticket could be
|
||||
* created).
|
||||
*/
|
||||
int64_t oakrender_request_frame(OakNodeNode viewer, int64_t ts,
|
||||
oakrender_frame_ready_fn cb, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Cancel a pending frame request. The callback still fires with a
|
||||
* NULL frame.
|
||||
*
|
||||
* @return OAKRENDER_OK, or OAKRENDER_E_NOT_FOUND for an unknown id.
|
||||
*/
|
||||
int oakrender_cancel_request(int64_t request_id);
|
||||
|
||||
/**
|
||||
* @brief Set the multicam node on the manager's auto-cacher
|
||||
* (PreviewAutoCacher::set_multicam_node()). `multicam_or_NULL` is a
|
||||
* borrowed oaknode handle to a MultiCamNode (empty handle to clear).
|
||||
*
|
||||
* @return OAKRENDER_OK or OAKRENDER_E_STATE.
|
||||
*/
|
||||
int oakrender_set_cacher_multicam(OakNodeNode multicam_or_NULL);
|
||||
|
||||
/**
|
||||
* @brief Set the display color processor on the manager's auto-cacher
|
||||
* (PreviewAutoCacher::set_display_color_processor()). Borrowed handle,
|
||||
* empty ctx to clear.
|
||||
*
|
||||
* @return OAKRENDER_OK or OAKRENDER_E_STATE.
|
||||
*/
|
||||
int oakrender_set_display_color_processor(OakColorProcessor p_or_NULL);
|
||||
|
||||
/**
|
||||
* @brief 1 when the process-wide RenderManager singleton exists
|
||||
* (RenderManager::instance() != nullptr; only the main GUI
|
||||
* process creates one), 0 otherwise.
|
||||
*/
|
||||
int oakrender_manager_available(void);
|
||||
|
||||
/**
|
||||
* @brief Cancel in-flight video cache tasks on the manager's
|
||||
* auto-cacher (PreviewAutoCacher::cancel_video_tasks()). No-op
|
||||
* when no manager/auto-cacher exists (e.g. a worker process).
|
||||
*/
|
||||
void oakrender_cancel_video_tasks(int wait_for_done);
|
||||
|
||||
/* ---- Disk cache (olive::DiskManager) -------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief The default disk cache directory
|
||||
* (DiskManager::get_default_disk_cache_path()). Two-stage string getter:
|
||||
* returns the required buffer size including NUL; pass buf == NULL or
|
||||
* too small a buffer to query the size. Does not require the manager.
|
||||
*/
|
||||
int oakrender_disk_cache_path(char *buf, int n);
|
||||
|
||||
/**
|
||||
* @brief Bytes currently consumed by the default disk cache folder.
|
||||
* Lazily creates the DiskManager singleton on first use.
|
||||
*
|
||||
* @return Consumption in bytes (>= 0), or OAKRENDER_E_FAILED.
|
||||
*/
|
||||
int64_t oakrender_disk_cache_size(void);
|
||||
|
||||
/**
|
||||
* @brief Clear the default disk cache folder
|
||||
* (DiskManager::clear_disk_cache()).
|
||||
*
|
||||
* @return OAKRENDER_OK or OAKRENDER_E_FAILED.
|
||||
*/
|
||||
int oakrender_disk_cache_clear(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_RENDER_MANAGER_H
|
||||
@@ -0,0 +1,374 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EDITOR_RENDER_RENDERER_H
|
||||
#define OAK_EDITOR_RENDER_RENDERER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <memory>
|
||||
#endif
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file renderer.h
|
||||
* @brief C ABI for the oakrender display renderer (olive::Renderer) —
|
||||
* renderer/texture/frame/blit families plus backend management.
|
||||
*
|
||||
* Signatures follow the R7-A display.h rewrite
|
||||
* (docs/zh/plans/completed/r7-pure-abi-plan.md §A.2) with the
|
||||
* oakrender_ prefix (M7 §2.1).
|
||||
*
|
||||
* Ownership protocol: every public handle is a by-value
|
||||
* reference-counted struct (see oakcommon's common/handle.h; shared_ptr
|
||||
* semantics). init/create functions return a handle with reference
|
||||
* count 1, handle.addref(handle.ctx) takes another reference, and
|
||||
* handle.release(handle.ctx) (or the oakrender_*_free() convenience
|
||||
* wrappers, which also null the caller's ctx) drops one; the object is
|
||||
* destroyed in this library when the count reaches zero. Empty handles
|
||||
* (ctx == NULL) are accepted by every function and yield a no-op / zero
|
||||
* result / OAKRENDER_E_INVALID.
|
||||
*
|
||||
* Cross-thread handoff (§A.3): the producing side addrefs before
|
||||
* publishing a handle into a shared slot; the consuming side releases
|
||||
* the handle it replaced. The side holding the slot when it is torn
|
||||
* down releases the remaining handle.
|
||||
*
|
||||
* Handles:
|
||||
* - OakRenderRenderer wraps a native olive::Renderer.
|
||||
* - OakRenderTexture / OakCodecFrame box shared_ptr-managed engine
|
||||
* objects.
|
||||
* - `gl_context` is an opaque borrowed olive::OpenGLContext* (or NULL
|
||||
* to let the backend create its own offscreen surface).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief POD mirror of olive::VideoParams' user-facing fields.
|
||||
*
|
||||
* Same layout and field semantics as oak_video_params
|
||||
* (engine/include/oakengine/videoparams.h): `time_base_*` is the frame
|
||||
* duration (frame rate flipped), `format` an olive::PixelFormat::Format
|
||||
* value, `interlacing` an olive::VideoParams::Interlacing value,
|
||||
* `color_range` an olive::VideoParams::ColorRange value. The video
|
||||
* channel count is an engine-internal constant and not exposed.
|
||||
*/
|
||||
typedef struct oakrender_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). */
|
||||
int video_type; /**< olive::VideoParams::Type (0 = video). */
|
||||
int premultiplied_alpha; /**< 0/1. */
|
||||
} oakrender_video_params;
|
||||
|
||||
/**
|
||||
* @brief Reference-counted handle to a display renderer
|
||||
* (olive::Renderer). See the file-level ownership protocol.
|
||||
*/
|
||||
typedef struct OakRenderRenderer {
|
||||
void *ctx; /**< Opaque pointer to the reference-counted object. */
|
||||
void (*addref)(void *ctx); /**< Atomically increments the count. */
|
||||
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
|
||||
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
|
||||
} OakRenderRenderer;
|
||||
|
||||
/**
|
||||
* @brief Reference-counted handle to a GPU texture (olive::Texture).
|
||||
* See the file-level ownership protocol.
|
||||
*/
|
||||
typedef struct OakRenderTexture {
|
||||
void *ctx; /**< Opaque pointer to the reference-counted object. */
|
||||
void (*addref)(void *ctx); /**< Atomically increments the count. */
|
||||
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
|
||||
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
|
||||
} OakRenderTexture;
|
||||
|
||||
/**
|
||||
* @brief Reference-counted handle to a CPU frame (an olive::FramePtr
|
||||
* boxed in a control block). Declared here so the cache family
|
||||
* (render/cache.h) can use the same type; the frame functions live in
|
||||
* this header.
|
||||
* Named OakCodecFrame per the M7 §2.2 contract; the oakcodec wave (M5)
|
||||
* adopts the same handle.
|
||||
*/
|
||||
typedef struct OakCodecFrame {
|
||||
void *ctx; /**< Opaque pointer to the reference-counted object. */
|
||||
void (*addref)(void *ctx); /**< Atomically increments the count. */
|
||||
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
|
||||
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
|
||||
} OakCodecFrame;
|
||||
|
||||
/**
|
||||
* @brief Flattened POD of olive::ColorTransformJob for the display blit
|
||||
* path. `matrix`/`crop_matrix` are column-major 4x4; an all-zero matrix
|
||||
* means identity.
|
||||
*/
|
||||
typedef struct oakrender_color_transform_job {
|
||||
const void *processor; /**< OakColorProcessor ctx (borrowed), may be NULL. */
|
||||
void *input_texture; /**< OakRenderTexture ctx (borrowed, not retained). */
|
||||
int input_alpha_association; /**< 0=none, 1=associated. */
|
||||
int clear_destination; /**< 0/1. */
|
||||
int force_opaque; /**< 0/1. */
|
||||
float matrix[16];
|
||||
float crop_matrix[16];
|
||||
} oakrender_color_transform_job;
|
||||
|
||||
/* ---- Renderer lifecycle -------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Create a renderer on the named dynamic backend ("opengl",
|
||||
* "vulkan"; olive::DynamicRenderer). Loads the backend shared library;
|
||||
* falls back per DynamicRenderer rules.
|
||||
*
|
||||
* @return Renderer handle with reference count 1; ctx is NULL on
|
||||
* NULL/empty backend id, load failure, or allocation failure.
|
||||
*/
|
||||
OakRenderRenderer oakrender_display_renderer_create_dynamic(
|
||||
const char *backend_id);
|
||||
|
||||
/**
|
||||
* @brief Create an OpenGL renderer (olive::OpenGLRenderer). The renderer
|
||||
* is not initialized; call oakrender_display_renderer_init() before use.
|
||||
*
|
||||
* @return Renderer handle with reference count 1; ctx is NULL on
|
||||
* allocation failure.
|
||||
*/
|
||||
OakRenderRenderer oakrender_display_renderer_create_opengl(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize a renderer. `gl_context` is a borrowed opaque
|
||||
* olive::OpenGLContext*, or NULL to use the backend's default
|
||||
* device/context path (Renderer::init()).
|
||||
*
|
||||
* @return OAKRENDER_OK, OAKRENDER_E_INVALID (empty renderer), or
|
||||
* OAKRENDER_E_FAILED (backend init failed).
|
||||
*/
|
||||
int oakrender_display_renderer_init(OakRenderRenderer renderer,
|
||||
void *gl_context);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a renderer (the final release runs
|
||||
* Renderer::destroy() + delete). Convenience wrapper around
|
||||
* renderer->release(renderer->ctx): NULL / empty-handle no-op; clears
|
||||
* renderer->ctx after releasing.
|
||||
*/
|
||||
void oakrender_display_renderer_destroy(OakRenderRenderer *renderer);
|
||||
|
||||
/* ---- Renderer queries ---------------------------------------------------- */
|
||||
|
||||
/** @brief 1 when the renderer is OpenGL-based, 0 otherwise / empty. */
|
||||
int oakrender_display_renderer_is_open_gl(OakRenderRenderer renderer);
|
||||
|
||||
/** @brief 1 when the renderer is Vulkan-based, 0 otherwise / empty. */
|
||||
int oakrender_display_renderer_is_vulkan(OakRenderRenderer renderer);
|
||||
|
||||
/* ---- Texture handle ------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Create a GPU texture on `renderer`.
|
||||
*
|
||||
* @param pixels Initial pixel data, or NULL for an uninitialized texture.
|
||||
* @param linesize Stride of `pixels` in bytes (0 when pixels is NULL).
|
||||
* @return New texture handle (reference count 1); ctx is NULL on invalid
|
||||
* arguments / allocation failure.
|
||||
*/
|
||||
OakRenderTexture oakrender_display_texture_create(
|
||||
OakRenderRenderer renderer, const oakrender_video_params *params,
|
||||
const void *pixels, int linesize);
|
||||
|
||||
/**
|
||||
* @brief Take another reference to a texture and return the same handle.
|
||||
*
|
||||
* Convenience wrapper around handle.addref(handle.ctx). An empty handle
|
||||
* in yields an empty handle out. Every retain must be paired with
|
||||
* exactly one free/release.
|
||||
*/
|
||||
OakRenderTexture oakrender_display_texture_retain(OakRenderTexture texture);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a texture. Convenience wrapper around
|
||||
* texture->release(texture->ctx): frees the texture when the count
|
||||
* reaches zero. NULL / empty-handle no-op; clears texture->ctx after
|
||||
* releasing.
|
||||
*/
|
||||
void oakrender_display_texture_free(OakRenderTexture *texture);
|
||||
|
||||
int oakrender_display_texture_upload(OakRenderTexture texture,
|
||||
const void *pixels, int linesize);
|
||||
|
||||
int oakrender_display_texture_download(OakRenderTexture texture, void *pixels,
|
||||
int linesize);
|
||||
|
||||
/* ---- Texture queries ----------------------------------------------------- */
|
||||
|
||||
int oakrender_display_texture_get_params(OakRenderTexture texture,
|
||||
oakrender_video_params *out);
|
||||
|
||||
/** @brief Frame width/height in pixels (0 on empty). */
|
||||
int oakrender_codec_frame_width(OakCodecFrame frame);
|
||||
int oakrender_codec_frame_height(OakCodecFrame frame);
|
||||
|
||||
/** @brief ffmpeg_bridge pixel format when the frame wraps a texture's
|
||||
* CPU copy (an AVFramePtr); -1 otherwise. */
|
||||
int oakrender_codec_frame_fb_format(OakCodecFrame frame);
|
||||
|
||||
/** @brief Native texture id (0 on empty or a dummy/id-less texture). */
|
||||
int oakrender_display_texture_id(OakRenderTexture texture);
|
||||
|
||||
/** @brief 1 when the texture is a placeholder dummy (Texture::is_dummy()). */
|
||||
int oakrender_display_texture_is_dummy(OakRenderTexture texture);
|
||||
|
||||
/**
|
||||
* @brief The CPU frame stored in the texture, if any (Texture::frame()).
|
||||
* *out receives a retained frame handle (empty when none).
|
||||
*/
|
||||
int oakrender_display_texture_get_frame(OakRenderTexture texture,
|
||||
OakCodecFrame *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
namespace olive { class Texture; using TexturePtr = std::shared_ptr<Texture>; }
|
||||
|
||||
/**
|
||||
* @brief Wrap a native TexturePtr in a retained handle (C++ only; used
|
||||
* by oakrender internals when handing textures across the C ABI).
|
||||
*/
|
||||
OakRenderTexture oakrender_display_texture_wrap_native(
|
||||
const olive::TexturePtr &texture);
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- Frame handle -------------------------------------------------------- */
|
||||
|
||||
/** @brief Create an empty CPU frame. Returns a handle with count 1. */
|
||||
OakCodecFrame oakrender_codec_frame_create(void);
|
||||
|
||||
/**
|
||||
* @brief Take another reference to a frame and return the same handle.
|
||||
* Empty in yields empty out (see oakrender_display_texture_retain()).
|
||||
*/
|
||||
OakCodecFrame oakrender_codec_frame_retain(OakCodecFrame frame);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a frame. Convenience wrapper around
|
||||
* frame->release(frame->ctx). NULL / empty-handle no-op; clears
|
||||
* frame->ctx after releasing.
|
||||
*/
|
||||
void oakrender_codec_frame_free(OakCodecFrame *frame);
|
||||
|
||||
int oakrender_codec_frame_set_video_params(
|
||||
OakCodecFrame frame, const oakrender_video_params *params);
|
||||
|
||||
int oakrender_codec_frame_get_params(OakCodecFrame frame,
|
||||
oakrender_video_params *out);
|
||||
|
||||
/**
|
||||
* @brief Allocate the pixel buffer per the frame's video params
|
||||
* (Frame::allocate()).
|
||||
*
|
||||
* @return OAKRENDER_OK, OAKRENDER_E_INVALID (empty frame), or
|
||||
* OAKRENDER_E_FAILED (invalid params / allocation failed).
|
||||
*/
|
||||
int oakrender_codec_frame_allocate(OakCodecFrame frame);
|
||||
|
||||
/** @brief Borrowed pixel data pointer (valid until the final release). */
|
||||
void *oakrender_codec_frame_data(OakCodecFrame frame);
|
||||
|
||||
/** @brief Borrowed const pixel data pointer. */
|
||||
const void *oakrender_codec_frame_const_data(OakCodecFrame frame);
|
||||
|
||||
/** @brief Line stride in bytes. */
|
||||
int oakrender_codec_frame_linesize_bytes(OakCodecFrame frame);
|
||||
|
||||
/** @brief 1 when the pixel buffer is allocated, 0 otherwise / empty. */
|
||||
int oakrender_codec_frame_is_allocated(OakCodecFrame frame);
|
||||
|
||||
/* ---- Color-managed blit -------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Blit a color-managed image through the OCIO pipeline
|
||||
* (Renderer::blit_color_managed()).
|
||||
*
|
||||
* @param dst_texture Destination texture handle, or an empty handle for
|
||||
* the current output target.
|
||||
* @param params Destination video params, or NULL to use dst_texture's.
|
||||
*/
|
||||
int oakrender_display_renderer_blit_color_managed(
|
||||
OakRenderRenderer renderer, const oakrender_color_transform_job *job,
|
||||
OakRenderTexture dst_texture, const oakrender_video_params *params);
|
||||
|
||||
/* ---- Cross-backend texture download -------------------------------------- */
|
||||
|
||||
int oakrender_display_renderer_download_from_texture(
|
||||
OakRenderRenderer renderer, int texture_id,
|
||||
const oakrender_video_params *params, void *dst_pixels, int linesize);
|
||||
|
||||
/* ---- Backend management (M7 §2.1) ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Number of known render backends (olive::RenderManager::Backend:
|
||||
* opengl, vulkan, multiprocess, dummy).
|
||||
*/
|
||||
int oakrender_backend_count(void);
|
||||
|
||||
/**
|
||||
* @brief Id string of the `i`-th backend ("opengl", ...). Two-stage
|
||||
* string getter: returns the required buffer size including NUL; pass
|
||||
* buf == NULL or too small a buffer to query the size.
|
||||
*
|
||||
* @return Required size (non-negative), or OAKRENDER_E_NOT_FOUND when
|
||||
* `i` is out of range.
|
||||
*/
|
||||
int oakrender_backend_id_at(int i, char *buf, int n);
|
||||
|
||||
/**
|
||||
* @brief Record the requested backend id (applied to the RenderManager
|
||||
* instance when one exists).
|
||||
*
|
||||
* @return OAKRENDER_OK, or OAKRENDER_E_INVALID for a NULL/unknown id.
|
||||
*/
|
||||
int oakrender_set_backend(const char *backend_id);
|
||||
|
||||
/**
|
||||
* @brief The effective backend: the RenderManager instance's backend when
|
||||
* an instance exists, otherwise the requested backend. Two-stage string
|
||||
* getter (same convention as oakrender_backend_id_at()).
|
||||
*/
|
||||
int oakrender_current_backend(char *buf, int n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_RENDER_RENDERER_H
|
||||
@@ -0,0 +1,171 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_EDITOR_RENDER_TICKET_H
|
||||
#define OAK_EDITOR_RENDER_TICKET_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/colortransform.h"
|
||||
#include "common/videoparams.h"
|
||||
#include "node/colormanager.h"
|
||||
#include "node/node.h"
|
||||
#include "olive/core/oakcore/audioparams.h"
|
||||
#include "olive/core/oakcore/samplebuffer.h"
|
||||
#include "render/error.h"
|
||||
#include "render/cache.h"
|
||||
#include "render/color.h"
|
||||
#include "render/renderer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Reference-counted handle to a render ticket
|
||||
* (olive::RenderTicketWatcher).
|
||||
*
|
||||
* By-value handle (shared_ptr semantics, see oakcommon's
|
||||
* common/handle.h). Created by oakrender_ticket_render_frame() /
|
||||
* oakrender_ticket_render_audio() with reference count 1; release with
|
||||
* oakrender_ticket_free().
|
||||
*/
|
||||
typedef struct OakRenderTicket {
|
||||
void *ctx; /**< Opaque pointer to the reference-counted object. */
|
||||
void (*addref)(void *ctx); /**< Atomically increments the count. */
|
||||
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
|
||||
uint32_t abi_version; /**< OAKRENDER_ABI_VERSION. */
|
||||
} OakRenderTicket;
|
||||
|
||||
/**
|
||||
* @brief Finished callback (async command return channel, 01 §4
|
||||
* exception). Fires on the ticket's finishing thread, exactly
|
||||
* once (cancelled tickets fire with a NULL result). The ticket
|
||||
* handle is a borrowed copy of the submitter's handle; the
|
||||
* submitter keeps ownership and releases it.
|
||||
*/
|
||||
typedef void (*oakrender_ticket_finished_fn)(OakRenderTicket ticket,
|
||||
void *userdata);
|
||||
|
||||
/** @brief Ticket types (RenderManager::TicketType). */
|
||||
enum OakRenderTicketType {
|
||||
OAKRENDER_TICKET_VIDEO = 0,
|
||||
OAKRENDER_TICKET_AUDIO = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Parameters for a video frame ticket
|
||||
* (RenderManager::RenderVideoParams).
|
||||
*/
|
||||
typedef struct oakrender_video_ticket_params {
|
||||
OakNodeNode output_node; /**< Connected texture output node (borrowed). */
|
||||
OakVideoParams video_params; /**< By value (oakcommon handle). */
|
||||
OakAudioParams *audio_params; /**< Borrowed oakcore handle, may be NULL. */
|
||||
int64_t time_num; /**< Frame timestamp as rational. */
|
||||
int64_t time_den;
|
||||
OakNodeColorManager color_manager; /**< Borrowed, empty ctx = NULL. */
|
||||
int mode; /**< olive::RenderMode::Mode as int. */
|
||||
int force_width; /**< 0/0 = off. */
|
||||
int force_height;
|
||||
double force_matrix[16]; /**< Used when has_force_matrix != 0. */
|
||||
int has_force_matrix;
|
||||
int force_format; /**< PixelFormat as int, -1 = off. */
|
||||
int force_channel_count; /**< 0 = off. */
|
||||
OakColorProcessor force_color_output; /**< Borrowed; empty ctx = none. */
|
||||
OakColorTransform force_color_transform; /**< By value; empty ctx = default. */
|
||||
OakRenderCache cache; /**< Borrowed frame cache; empty ctx = none. */
|
||||
} oakrender_video_ticket_params;
|
||||
|
||||
/**
|
||||
* @brief Submit a video frame render ticket.
|
||||
*
|
||||
* @return Ticket handle with reference count 1 (caller releases); ctx is
|
||||
* NULL on failure. The finished callback fires exactly once;
|
||||
* NULL `cb` is allowed (poll with
|
||||
* oakrender_ticket_wait()/oakrender_ticket_is_finished()).
|
||||
*/
|
||||
OakRenderTicket oakrender_ticket_render_frame(
|
||||
const oakrender_video_ticket_params *params,
|
||||
oakrender_ticket_finished_fn cb, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Submit an audio render ticket (RenderManager::render_audio()).
|
||||
*
|
||||
* @param output_node Connected sample output node.
|
||||
* @param params Audio params (borrowed oakcore handle).
|
||||
*/
|
||||
OakRenderTicket oakrender_ticket_render_audio(
|
||||
OakNodeNode output_node, int64_t in_num, int64_t in_den,
|
||||
int64_t out_num, int64_t out_den, const OakAudioParams *params,
|
||||
int mode, oakrender_ticket_finished_fn cb, void *userdata);
|
||||
|
||||
int oakrender_ticket_is_finished(OakRenderTicket ticket);
|
||||
|
||||
/** @brief Block until the ticket finishes. */
|
||||
int oakrender_ticket_wait(OakRenderTicket ticket);
|
||||
|
||||
int oakrender_ticket_cancel(OakRenderTicket ticket);
|
||||
|
||||
/** @brief OAKRENDER_TICKET_* or negative error. */
|
||||
int oakrender_ticket_get_type(OakRenderTicket ticket);
|
||||
|
||||
/** @brief Ticket timestamp (video tickets). */
|
||||
int oakrender_ticket_get_time(OakRenderTicket ticket, int64_t *out_num,
|
||||
int64_t *out_den);
|
||||
|
||||
/** @brief Ticket time range (audio tickets). */
|
||||
int oakrender_ticket_get_range(OakRenderTicket ticket, int64_t *in_num,
|
||||
int64_t *in_den, int64_t *out_num,
|
||||
int64_t *out_den);
|
||||
|
||||
/**
|
||||
* @brief The resulting frame (video tickets). *out receives an owned
|
||||
* OakCodecFrame (release with oakrender_codec_frame_free()).
|
||||
* OAKRENDER_E_STATE when unfinished, OAKRENDER_E_FAILED when the
|
||||
* ticket has no frame result.
|
||||
*/
|
||||
int oakrender_ticket_get_frame(OakRenderTicket ticket, OakCodecFrame *out);
|
||||
|
||||
/**
|
||||
* @brief The resulting samples (audio tickets). *out receives a copy
|
||||
* (release with oakcore_samplebuffer_free()).
|
||||
*/
|
||||
int oakrender_ticket_get_samples(OakRenderTicket ticket,
|
||||
OakSampleBuffer **out);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a ticket (the final release is safe on
|
||||
* finished tickets; cancels and waits on running ones). Convenience
|
||||
* wrapper around ticket->release(ticket->ctx). NULL / empty-handle
|
||||
* no-op; clears ticket->ctx after releasing.
|
||||
*/
|
||||
void oakrender_ticket_free(OakRenderTicket *ticket);
|
||||
|
||||
/**
|
||||
* @brief Toggle aggressive garbage collection on the render manager
|
||||
* (RenderManager::set_aggressive_garbage_collection()).
|
||||
*/
|
||||
int oakrender_manager_set_aggressive_gc(int enabled);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_RENDER_TICKET_H
|
||||
Reference in New Issue
Block a user