/*** 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_VIEWER_H #define OAKENGINE_VIEWER_H #include #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 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 */