/*** 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 . ***/ #ifndef OAKENGINE_RENDERER_H #define OAKENGINE_RENDERER_H #include #include "export.h" #include "init.h" #include "timeline.h" #ifdef __cplusplus extern "C" { #endif /** * @file renderer.h * @brief C ABI for synchronous frame/audio rendering of a sequence * * An OakEngineRenderer pulls finished CPU frames and audio buffers out of an * OakEngineSequence. It is a thin synchronous facade over the engine's * asynchronous render pipeline (RenderManager::render_frame()/ * render_audio() returning RenderTicket objects, engine/render/ * rendermanager.h): each render call submits a ticket and blocks until it * finishes, a timeout elapses, or it is cancelled. * * Rendering requires the engine to be initialized with OAKENGINE_INIT_RENDER * (oakengine/init.h); oakengine_renderer_create() itself only validates its * arguments, but oakengine_renderer_render_frame()/_render_audio() fail with * NULL and set the per-renderer error string (query with * oakengine_renderer_last_error()) when the render services are not up. * * Video frames are produced by the engine's render worker pool * (oak-render-worker child processes, which is where a GL context may be * needed); audio is rendered in-process on the audio render thread. * * Conventions (matching the other facade families): * - 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. * - Data pointers (oakengine_frame_data(), oakengine_audio_data()) are * borrowed: they stay valid until the owning frame/buffer is freed. * - The `pixel_format` argument and oakengine_frame_format() carry * olive::core::PixelFormat::Format values: u8 = 0, u10 = 1, u16 = 2, * f16 = 3, f32 = 4. * - Timestamps are frame numbers in the timebase of the frame rate passed * to oakengine_renderer_create() (e.g. timestamp 30 at 30000/1001 means * frame 30, 1001/1000 seconds). This is the same timestamp/timebase * convention as the timeline family (oakengine/timeline.h). * - `mode` follows olive::RenderMode: 0 = offline (preview quality), * 1 = online (export/master quality). */ /** * @brief Opaque renderer handle, bound to one sequence and one output * geometry. Owned by the caller; release with oakengine_renderer_free(). */ typedef struct OakEngineRenderer OakEngineRenderer; /** * @brief Opaque CPU video frame. Owned by the caller; release with * oakengine_frame_free(). */ typedef struct OakEngineFrame OakEngineFrame; /** * @brief Opaque planar float audio buffer. Owned by the caller; release with * oakengine_audio_free(). */ 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. * * `frame_rate_num`/`frame_rate_den` is the frame rate as a rational (e.g. * 30000/1001); it defines both the video time base and the meaning of all * timestamps passed to this renderer. `output_colorspace` names the OCIO * color space the frames are converted into after rendering in the * project's reference space (a ColorTransform to that space, applied as * RenderVideoParams::force_color_output); NULL renders straight into the * reference space without an output transform. If the named color space * cannot be resolved, the renderer falls back to no transform and records * the reason in the error string. * * Returns NULL on invalid arguments (NULL `seq`, non-positive size, frame * rate or pixel format). */ OAKENGINE_API OakEngineRenderer *oakengine_renderer_create( OakEngineSequence *seq, int width, int height, int pixel_format, int frame_rate_num, int frame_rate_den, const char *output_colorspace); OAKENGINE_API void oakengine_renderer_free(OakEngineRenderer *self); /** * @brief Set the render mode: 0 = offline/preview, 1 = online/export * (olive::RenderMode). Defaults to 0. Returns OAKENGINE_E_INVALID for other * values. */ OAKENGINE_API int oakengine_renderer_set_mode(OakEngineRenderer *self, int mode); /** * @brief Human-readable reason for the last failed render call on this * renderer (buf/size convention). Empty when the last call succeeded. */ OAKENGINE_API int oakengine_renderer_last_error( const OakEngineRenderer *self, char *buf, int buf_size); /** * @brief Synchronously render the frame at `timestamp` (a frame number in * the renderer's timebase). * * Submits a RenderVideoParams ticket (RenderManager::render_frame(), return * type k_frame = CPU frame) and blocks up to 60 seconds for it to finish. * Returns NULL on failure, timeout or cancellation; the reason is available * through oakengine_renderer_last_error(). */ OAKENGINE_API OakEngineFrame * oakengine_renderer_render_frame(OakEngineRenderer *self, int64_t timestamp); /** * @brief Synchronously render audio starting at `start_timestamp` (frame * number) and spanning `length_timestamp` timebase units, in the * sequence's audio parameters (RenderManager::render_audio()). * * Same blocking/timeout/error semantics as * oakengine_renderer_render_frame(). */ OAKENGINE_API OakEngineAudioBuffer *oakengine_renderer_render_audio( OakEngineRenderer *self, int64_t start_timestamp, int64_t length_timestamp); /** * @brief Cancel the in-flight render call, if any (RenderTicket::cancel() * plus RenderManager::remove_ticket()). * * Intended to be called from another thread while a render call blocks; * safe to call anytime, a no-op when nothing is in flight. */ OAKENGINE_API void oakengine_renderer_cancel(OakEngineRenderer *self); /* ---- OakEngineFrame ----------------------------------------------------- */ OAKENGINE_API int oakengine_frame_width(const OakEngineFrame *self); OAKENGINE_API int oakengine_frame_height(const OakEngineFrame *self); /** * @brief Pixel format as an olive::core::PixelFormat::Format value. */ OAKENGINE_API int oakengine_frame_format(const OakEngineFrame *self); OAKENGINE_API int oakengine_frame_channel_count(const OakEngineFrame *self); /** * @brief Bytes per scanline (stride). */ OAKENGINE_API int oakengine_frame_linesize_bytes(const OakEngineFrame *self); /** * @brief Borrowed pointer to the pixel data (linesize_bytes * height bytes). * Valid until the frame is freed. */ OAKENGINE_API const void *oakengine_frame_data(const OakEngineFrame *self); OAKENGINE_API void oakengine_frame_free(OakEngineFrame *self); /* ---- OakEngineAudioBuffer ------------------------------------------------ */ OAKENGINE_API int oakengine_audio_sample_rate(const OakEngineAudioBuffer *self); OAKENGINE_API int oakengine_audio_channel_count(const OakEngineAudioBuffer *self); /** * @brief Samples per channel. */ OAKENGINE_API int64_t oakengine_audio_sample_count(const OakEngineAudioBuffer *self); /** * @brief Borrowed pointer to one channel's planar float samples * (sample_count floats). Valid until the buffer is freed. Returns NULL for * an out-of-range channel. */ OAKENGINE_API const float * oakengine_audio_data(const OakEngineAudioBuffer *self, int channel); OAKENGINE_API void oakengine_audio_free(OakEngineAudioBuffer *self); #ifdef __cplusplus } #include Q_DECLARE_OPAQUE_POINTER(OakEngineRenderer *) Q_DECLARE_OPAQUE_POINTER(OakEngineFrame *) Q_DECLARE_OPAQUE_POINTER(OakEngineAudioBuffer *) #endif #endif /* OAKENGINE_RENDERER_H */