refactor(node,render): route oaknode->oakrender calls through the C ABI
- oakrender cache C API: add create_for_node (four cache kinds), get_uuid, request, load/save_state, set_saving_enabled, set_passthrough, get_passthroughs, get_valid_cache_filename, get_timebase, lock/unlock, invalidate_range (rational variant), get_native (C++ only, for the in-module PreviewAutoCacher) - Node's four caches become owned OakRenderCache value handles; all cache access in node.cpp/clip/viewer/traverser/serializers goes through the C ABI - oaknode_node_get_video_frame_cache returns an addref'd OakRenderCache; drop OakNodeFrameCache; oaktask precache/export and oakrender_video_ticket_params take the value handle - color: OCIOBaseNode/OCIOLutNode hold OakColorProcessor handles (dtors added); oakrender gains color_processor_create_transform/ create_lut/create_grading_primary/get_native and LUT library queries, keeping all OCIO transform construction inside oakrender; oaknode gains colormanager_wrap_borrowed/get_native - RenderManager::instance() check -> oakrender_manager_available(); cancel_video_tasks and disk cache path go through manager C API - remaining node->render C++ symbol: only olive::Texture::~Texture via Variant's shared_ptr payload (recorded exception, same class as the UndoCommand inheritance) Tests: new cache/color/manager/colormanager cases; all standalone trees green (common 196, codec 22, audio 40, task 112, render 63, timeline 125, node 98, plugin 102).
This commit is contained in:
@@ -352,3 +352,46 @@ ColorManager)去Qt化过程中的删除与语义变化,迁移调用方时需
|
||||
oakcommon_xml_writer_wrap_native(C++ only 借用包装),
|
||||
XmlReaderState/XmlWriterState 支持 owning/borrowed 双模式。
|
||||
- liboaknode 对 liboaktimeline 的 C++ 符号引用降为 0(nm 验证)。
|
||||
|
||||
## oaknode→oakrender 切到 C ABI(2026-08-07)
|
||||
|
||||
- oakrender cache C API 大扩(include/render/cache.h):
|
||||
cache_create_for_node(parent, kind)(四种缓存:视频帧/缩略图/音频
|
||||
播放/波形,parent 的原生回指针留在 oakrender 内部)、get_uuid
|
||||
(两段式)、request、load/save_state、set_saving_enabled、
|
||||
set_passthrough、get_passthroughs、get_valid_cache_filename、
|
||||
get_timebase、lock/unlock(替代直交 std::mutex)、
|
||||
invalidate_range(有理数版,原有 invalidate 是时间戳制)、
|
||||
get_native(C++ only,供 oakrender 内部的 PreviewAutoCacher 用)。
|
||||
- Node 的四个缓存成员(video_cache_/thumbnail_cache_/audio_cache_/
|
||||
waveform_cache_)从原生指针改为 OakRenderCache 拥有型值句柄,
|
||||
访问器返回借用副本;构造/析构/拷贝 UUID/加载保存全部走 C ABI。
|
||||
- ClipBlock 的 connected_video_cache()/thumbnails()/waveform() 等
|
||||
返回借用句柄;request/invalidate/passthrough 流程走 C ABI;
|
||||
passthrough 列表查询只传 range(原 Passthrough::cache 文本不出界)。
|
||||
- oaknode_node_get_video_frame_cache 改为返回 addref 后的
|
||||
OakRenderCache(out 参数,node.h 前置声明 struct OakRenderCache);
|
||||
OakNodeFrameCache 不透明指针类型删除;oaktask 的 precachetask
|
||||
直接收句柄,不再 wrap_borrowed;RenderTask::render/
|
||||
start_video_ticket 的 cache 参数与 oakrender_video_ticket_params.cache
|
||||
改为 OakRenderCache 值(借用语义,空 ctx=无)。
|
||||
- 色彩:OCIOBaseNode 的 processor_ 改 OakColorProcessor 拥有型句柄
|
||||
(新增析构释放;OCIOLutNode 同理并补了 ~OCIOLutNode);
|
||||
job.set_color_processor 经 C++-only
|
||||
oakrender_color_processor_get_native 取 shared_ptr。
|
||||
oakrender 新增 color_processor_create_transform/create_lut/
|
||||
create_grading_primary/get_native 与 lut_is_supported_extension/
|
||||
supported_extensions_count/at;oaknode 新增
|
||||
colormanager_wrap_borrowed/get_native(C++ only)。
|
||||
OCIO 的 FileTransform/GradingPrimaryTransform 构造全部内收到
|
||||
oakrender(grading log/linear、LUT 加载)。
|
||||
- RenderManager::instance() 空检查 → oakrender_manager_available();
|
||||
PreviewAutoCacher::cancel_video_tasks → oakrender_cancel_video_tasks;
|
||||
DiskManager 默认缓存路径 → 已有 oakrender_disk_cache_path。
|
||||
- PreviewAutoCacher(oakrender 内部)改用 oakrender_cache_get_native
|
||||
解包节点缓存,回调接线保持模块内 C++。
|
||||
- **例外(记录为 01 §5 例外)**:NodeValue 的纹理载荷
|
||||
TexturePtr=shared_ptr<olive::Texture> 经 Variant 类型擦除流经
|
||||
oaknode 的 35 个 TU,析构引用 olive::Texture::~Texture——值系统
|
||||
载荷问题,与 UndoCommand 跨模块继承同类,留待值系统重做。
|
||||
除此之外 liboaknode→liboakrender 的 C++ 符号引用为 0。
|
||||
|
||||
@@ -72,6 +72,16 @@ OakNodeColorManager oaknode_colormanager_init(OakNodeProject project);
|
||||
*/
|
||||
void oaknode_colormanager_free(OakNodeColorManager *manager);
|
||||
|
||||
/**
|
||||
* @brief Borrowed handle wrapping a native manager pointer held by a
|
||||
* node (olive::OCIOBaseNode::manager()).
|
||||
*
|
||||
* The manager stays owned by its project: release() on this handle
|
||||
* only frees the box. Empty handle (ctx == NULL) for a NULL native
|
||||
* pointer.
|
||||
*/
|
||||
OakNodeColorManager oaknode_colormanager_wrap_borrowed(void *native_manager);
|
||||
|
||||
/**
|
||||
* @brief Load the built-in default OCIO config and set the default input
|
||||
* colorspace (olive::ColorManager::init()).
|
||||
@@ -189,6 +199,21 @@ int oaknode_colormanager_get_compliant_color_transform(
|
||||
OakNodeColorManager manager, OakColorTransform transform,
|
||||
int force_display, OakColorTransform *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
namespace olive { class ColorManager; }
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Borrowed access to the underlying C++ manager (C++ only, for
|
||||
* adapter layers). Valid while the handle is held. NULL-safe.
|
||||
*/
|
||||
olive::ColorManager *oaknode_colormanager_get_native(
|
||||
OakNodeColorManager manager);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+7
-4
@@ -134,7 +134,7 @@ struct OakTimelineWorkArea;
|
||||
* (olive::FrameHashCache in oakrender). oakrender reinterprets this into
|
||||
* its own handle types.
|
||||
*/
|
||||
typedef struct OakNodeFrameCache OakNodeFrameCache;
|
||||
struct OakRenderCache;
|
||||
|
||||
/* oakcore handles used by the viewer setters. */
|
||||
typedef struct OakAudioParams OakAudioParams;
|
||||
@@ -575,11 +575,14 @@ int oaknode_node_get_work_area(OakNodeNode node,
|
||||
struct OakTimelineWorkArea *out);
|
||||
|
||||
/**
|
||||
* @brief Borrowed video frame cache of a node (NULL when the node has
|
||||
* none or for an empty handle).
|
||||
* @brief Video frame cache of a node as an addref'd oakrender value
|
||||
* handle (release with oakrender_cache_free()). *out is an
|
||||
* empty handle (ctx == NULL) when the node has none or for an
|
||||
* empty node handle. struct OakRenderCache is forward-declared
|
||||
* here; include render/cache.h for the definition.
|
||||
*/
|
||||
int oaknode_node_get_video_frame_cache(OakNodeNode node,
|
||||
OakNodeFrameCache **out);
|
||||
struct OakRenderCache *out);
|
||||
|
||||
/**
|
||||
* @brief Copy input values/connections from one node to another
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
// 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" {
|
||||
@@ -88,6 +89,128 @@ void oakrender_cache_free(OakRenderCache *cache);
|
||||
*/
|
||||
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()).
|
||||
@@ -112,6 +235,14 @@ int oakrender_cache_set_uuid(OakRenderCache cache, const char *uuid);
|
||||
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.
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
#include "error.h"
|
||||
#include "renderer.h"
|
||||
#include "common/colortransform.h" /* OakColorTransform */
|
||||
#include "node/colormanager.h" /* OakNodeColorManager */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -94,6 +96,75 @@ void oakrender_color_processor_free(OakColorProcessor *processor);
|
||||
*/
|
||||
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.
|
||||
@@ -160,6 +231,23 @@ 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
|
||||
|
||||
@@ -120,6 +120,20 @@ int oakrender_set_cacher_multicam(OakNodeNode multicam_or_NULL);
|
||||
*/
|
||||
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) -------------------------------------- */
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#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"
|
||||
|
||||
@@ -89,7 +90,7 @@ typedef struct oakrender_video_ticket_params {
|
||||
int force_channel_count; /**< 0 = off. */
|
||||
OakColorProcessor force_color_output; /**< Borrowed; empty ctx = none. */
|
||||
OakColorTransform force_color_transform; /**< By value; empty ctx = default. */
|
||||
OakNodeFrameCache *cache; /**< Borrowed frame cache, may be NULL. */
|
||||
OakRenderCache cache; /**< Borrowed frame cache; empty ctx = none. */
|
||||
} oakrender_video_ticket_params;
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,6 +75,19 @@ void oaknode_colormanager_free(OakNodeColorManager *manager)
|
||||
oaknode_c_api::free_handle(manager);
|
||||
}
|
||||
|
||||
OakNodeColorManager oaknode_colormanager_wrap_borrowed(
|
||||
void *native_manager)
|
||||
{
|
||||
return oaknode_c_api::make_handle<OakNodeColorManager>(
|
||||
native_manager, false, nullptr);
|
||||
}
|
||||
|
||||
olive::ColorManager *oaknode_colormanager_get_native(
|
||||
OakNodeColorManager manager)
|
||||
{
|
||||
return oaknode_c_api::to_native<olive::ColorManager>(manager);
|
||||
}
|
||||
|
||||
int oaknode_colormanager_initialize(OakNodeColorManager manager)
|
||||
{
|
||||
olive::ColorManager *cm =
|
||||
|
||||
+11
-3
@@ -24,6 +24,7 @@
|
||||
#include "node/footage.h"
|
||||
#include "node/project.h"
|
||||
#include "olive/core/oakcore/audioparams.h"
|
||||
#include "render/cache.h"
|
||||
#include "timeline/marker.h"
|
||||
#include "timeline/workarea.h"
|
||||
|
||||
@@ -1320,13 +1321,20 @@ int oaknode_node_get_work_area(OakNodeNode node,
|
||||
}
|
||||
|
||||
int oaknode_node_get_video_frame_cache(OakNodeNode node,
|
||||
OakNodeFrameCache **out)
|
||||
struct OakRenderCache *out)
|
||||
{
|
||||
if (!node.ctx || !out) {
|
||||
return OAKNODE_E_INVALID;
|
||||
}
|
||||
*out = reinterpret_cast<OakNodeFrameCache *>(
|
||||
oaknode_c_api::to_native<olive::Node>(node)->video_frame_cache());
|
||||
olive::Node *n = oaknode_c_api::to_native<olive::Node>(node);
|
||||
if (!n) {
|
||||
*out = OakRenderCache{};
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
*out = n->video_frame_cache();
|
||||
if (out->ctx) {
|
||||
out->addref(out->ctx);
|
||||
}
|
||||
return OAKNODE_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "project/sequence/sequence.h"
|
||||
#include "sliderdisplaytype.h"
|
||||
#include "timeline/displaymode.h"
|
||||
#include "../../../c_api/nodehandle.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -185,11 +186,17 @@ void ClipBlock::discard_cache()
|
||||
if (Node *connected = get_connected_output(k_buffer_in)) {
|
||||
Track::Type type = get_track_type();
|
||||
if (type == Track::k_video) {
|
||||
connected->video_frame_cache()->invalidate(
|
||||
TimeRange(RATIONAL_MIN, RATIONAL_MAX));
|
||||
oakrender_cache_invalidate_range(connected->video_frame_cache(),
|
||||
RATIONAL_MIN.numerator(),
|
||||
RATIONAL_MIN.denominator(),
|
||||
RATIONAL_MAX.numerator(),
|
||||
RATIONAL_MAX.denominator());
|
||||
} else if (type == Track::k_audio) {
|
||||
connected->audio_playback_cache()->invalidate(
|
||||
TimeRange(RATIONAL_MIN, RATIONAL_MAX));
|
||||
oakrender_cache_invalidate_range(connected->audio_playback_cache(),
|
||||
RATIONAL_MIN.numerator(),
|
||||
RATIONAL_MIN.denominator(),
|
||||
RATIONAL_MAX.numerator(),
|
||||
RATIONAL_MAX.denominator());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,8 +291,9 @@ void ClipBlock::request_range_from_connected(const TimeRange &range)
|
||||
{
|
||||
TimeRange thumb_range = range.intersected(max_range);
|
||||
if (get_adjusted_thumbnail_range(&thumb_range)) {
|
||||
connected->thumbnail_cache()->request(
|
||||
this->track()->sequence(), thumb_range);
|
||||
request_range_for_cache(connected->thumbnail_cache(),
|
||||
max_range, thumb_range, false,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,29 +359,69 @@ void ClipBlock::request_invalidated_from_connected(bool force_all,
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::request_range_for_cache(PlaybackCache *cache,
|
||||
void ClipBlock::request_range_for_cache(const OakRenderCache &cache,
|
||||
const TimeRange &max_range,
|
||||
const TimeRange &range, bool invalidate,
|
||||
bool request)
|
||||
{
|
||||
if (!cache.ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
TimeRange r = range.intersected(max_range);
|
||||
|
||||
if (invalidate) {
|
||||
cache->invalidate(r);
|
||||
oakrender_cache_invalidate_range(cache, r.in().numerator(),
|
||||
r.in().denominator(),
|
||||
r.out().numerator(),
|
||||
r.out().denominator());
|
||||
}
|
||||
|
||||
if (request) {
|
||||
cache->request(this->track()->sequence(), r);
|
||||
// Borrowed context handle: the sequence outlives the call
|
||||
OakNodeNode context = oaknode_c_api::make_handle<OakNodeNode>(
|
||||
this->track()->sequence(), false, nullptr);
|
||||
oakrender_cache_request(cache, context, r.in().numerator(),
|
||||
r.in().denominator(), r.out().numerator(),
|
||||
r.out().denominator());
|
||||
context.release(context.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipBlock::request_invalidated_for_cache(PlaybackCache *cache,
|
||||
void ClipBlock::request_invalidated_for_cache(const OakRenderCache &cache,
|
||||
const TimeRange &max_range)
|
||||
{
|
||||
core::TimeRangeList invalid = cache->get_invalidated_ranges(max_range);
|
||||
if (!cache.ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const PlaybackCache::Passthrough &p : cache->get_passthroughs()) {
|
||||
invalid.remove(p);
|
||||
core::TimeRangeList invalid;
|
||||
int n = oakrender_cache_get_invalidated_ranges(
|
||||
cache, max_range.in().numerator(), max_range.in().denominator(),
|
||||
max_range.out().numerator(), max_range.out().denominator(), NULL, 0);
|
||||
if (n > 0) {
|
||||
std::vector<int64_t> ranges(size_t(n) * 4);
|
||||
oakrender_cache_get_invalidated_ranges(
|
||||
cache, max_range.in().numerator(), max_range.in().denominator(),
|
||||
max_range.out().numerator(), max_range.out().denominator(),
|
||||
ranges.data(), n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
invalid.insert(TimeRange(Rational(ranges[i * 4],
|
||||
ranges[i * 4 + 1]),
|
||||
Rational(ranges[i * 4 + 2],
|
||||
ranges[i * 4 + 3])));
|
||||
}
|
||||
}
|
||||
|
||||
int np = oakrender_cache_get_passthroughs(cache, NULL, 0);
|
||||
if (np > 0) {
|
||||
std::vector<int64_t> passthroughs(size_t(np) * 4);
|
||||
oakrender_cache_get_passthroughs(cache, passthroughs.data(), np);
|
||||
for (int i = 0; i < np; i++) {
|
||||
invalid.remove(TimeRange(
|
||||
Rational(passthroughs[i * 4], passthroughs[i * 4 + 1]),
|
||||
Rational(passthroughs[i * 4 + 2], passthroughs[i * 4 + 3])));
|
||||
}
|
||||
}
|
||||
|
||||
for (const TimeRange &r : invalid) {
|
||||
@@ -392,7 +440,9 @@ bool ClipBlock::get_adjusted_thumbnail_range(TimeRange *r) const
|
||||
Rational in = this->media_range().in();
|
||||
if (r->contains(in)) {
|
||||
// Cache only the in point
|
||||
*r = TimeRange(in, in + thumbnail_cache()->get_timebase());
|
||||
int tb_num = 0, tb_den = 1;
|
||||
oakrender_cache_get_timebase(thumbnail_cache(), &tb_num, &tb_den);
|
||||
*r = TimeRange(in, in + Rational(tb_num, tb_den));
|
||||
return true;
|
||||
} else {
|
||||
// Cache nothing
|
||||
@@ -541,29 +591,12 @@ void ClipBlock::retranslate()
|
||||
|
||||
void ClipBlock::add_cache_passthrough_from(ClipBlock *other)
|
||||
{
|
||||
if (auto tc = this->video_frame_cache()) {
|
||||
if (auto oc = other->video_frame_cache()) {
|
||||
tc->set_passthrough(oc);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto tc = this->audio_playback_cache()) {
|
||||
if (auto oc = other->audio_playback_cache()) {
|
||||
tc->set_passthrough(oc);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto tc = this->thumbnails()) {
|
||||
if (auto oc = other->thumbnails()) {
|
||||
tc->set_passthrough(oc);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto tc = this->waveform()) {
|
||||
if (auto oc = other->waveform()) {
|
||||
tc->set_passthrough(oc);
|
||||
}
|
||||
}
|
||||
oakrender_cache_set_passthrough(this->video_frame_cache(),
|
||||
other->video_frame_cache());
|
||||
oakrender_cache_set_passthrough(this->audio_playback_cache(),
|
||||
other->audio_playback_cache());
|
||||
oakrender_cache_set_passthrough(this->thumbnails(), other->thumbnails());
|
||||
oakrender_cache_set_passthrough(this->waveform(), other->waveform());
|
||||
}
|
||||
|
||||
void ClipBlock::ConnectedToPreviewEvent()
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "audio/audiovisualwaveform.h"
|
||||
#include "codec/decoder.h"
|
||||
#include "block/block.h"
|
||||
#include "render/cache.h"
|
||||
#include "input/multicam/multicamnode.h"
|
||||
#include "output/track/track.h"
|
||||
|
||||
@@ -143,39 +144,44 @@ public:
|
||||
return block_links_;
|
||||
}
|
||||
|
||||
FrameHashCache *connected_video_cache() const
|
||||
/**
|
||||
* @brief Borrowed copies of the connected node's cache handles
|
||||
* (empty handle when nothing is connected). Callers must NOT
|
||||
* free them.
|
||||
*/
|
||||
OakRenderCache connected_video_cache() const
|
||||
{
|
||||
if (Node *n = get_connected_output(k_buffer_in)) {
|
||||
return n->video_frame_cache();
|
||||
} else {
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
AudioPlaybackCache *connected_audio_cache() const
|
||||
OakRenderCache connected_audio_cache() const
|
||||
{
|
||||
if (Node *n = get_connected_output(k_buffer_in)) {
|
||||
return n->audio_playback_cache();
|
||||
} else {
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
ThumbnailCache *thumbnails()
|
||||
OakRenderCache thumbnails()
|
||||
{
|
||||
if (Node *n = get_connected_output(k_buffer_in)) {
|
||||
return n->thumbnail_cache();
|
||||
} else {
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
AudioWaveformCache *waveform()
|
||||
OakRenderCache waveform()
|
||||
{
|
||||
if (Node *n = get_connected_output(k_buffer_in)) {
|
||||
return n->waveform_cache();
|
||||
} else {
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,10 +257,11 @@ private:
|
||||
|
||||
void request_range_from_connected(const TimeRange &range);
|
||||
|
||||
void request_range_for_cache(PlaybackCache *cache, const TimeRange &max_range,
|
||||
void request_range_for_cache(const OakRenderCache &cache,
|
||||
const TimeRange &max_range,
|
||||
const TimeRange &range, bool invalidate,
|
||||
bool request);
|
||||
void request_invalidated_for_cache(PlaybackCache *cache,
|
||||
void request_invalidated_for_cache(const OakRenderCache &cache,
|
||||
const TimeRange &max_range);
|
||||
|
||||
bool get_adjusted_thumbnail_range(TimeRange *r) const;
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
***/
|
||||
|
||||
#include "displaytransform.h"
|
||||
#include "common/colortransform.h"
|
||||
#include "node/colormanager.h"
|
||||
#include "render/color.h"
|
||||
|
||||
#include "color/colormanager/colormanager.h"
|
||||
|
||||
@@ -146,10 +149,24 @@ void DisplayTransformNode::config_changed()
|
||||
void DisplayTransformNode::generate_processor()
|
||||
{
|
||||
if (manager()) {
|
||||
ColorTransform transform(get_display(), get_view(), std::string());
|
||||
set_processor(ColorProcessor::create(
|
||||
manager(), manager()->get_reference_color_space(), transform,
|
||||
get_direction()));
|
||||
OakNodeColorManager mgr =
|
||||
oaknode_colormanager_wrap_borrowed(manager());
|
||||
OakColorTransform transform = oakcommon_colortransform_init_display(
|
||||
get_display().c_str(), get_view().c_str(), "");
|
||||
|
||||
char ref_space[256];
|
||||
int needed = oaknode_colormanager_get_reference_color_space(
|
||||
mgr, ref_space, sizeof(ref_space));
|
||||
if (needed > 0 && needed <= int(sizeof(ref_space))) {
|
||||
set_processor(oakrender_color_processor_create_transform(
|
||||
mgr, ref_space, transform,
|
||||
get_direction() == ColorProcessor::k_normal ?
|
||||
OAKRENDER_COLOR_DIRECTION_NORMAL :
|
||||
OAKRENDER_COLOR_DIRECTION_INVERSE));
|
||||
}
|
||||
|
||||
oakcommon_colortransform_free(&transform);
|
||||
mgr.release(mgr.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ const std::string OCIOBaseNode::k_texture_input = "tex_in";
|
||||
|
||||
OCIOBaseNode::OCIOBaseNode()
|
||||
: manager_(nullptr)
|
||||
, processor_(nullptr)
|
||||
{
|
||||
add_input(k_texture_input, NodeValue::k_texture,
|
||||
InputFlags(k_input_flag_not_keyframable));
|
||||
@@ -41,6 +40,11 @@ OCIOBaseNode::OCIOBaseNode()
|
||||
set_flag(k_video_effect);
|
||||
}
|
||||
|
||||
OCIOBaseNode::~OCIOBaseNode()
|
||||
{
|
||||
oakrender_color_processor_free(&processor_);
|
||||
}
|
||||
|
||||
void OCIOBaseNode::AddedToGraphEvent(Project *p)
|
||||
{
|
||||
manager_ = p->color_manager();
|
||||
@@ -60,10 +64,11 @@ void OCIOBaseNode::value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
auto tex_met = value.at(k_texture_input);
|
||||
TexturePtr t = tex_met.to_texture();
|
||||
if (t) {
|
||||
if (processor_) {
|
||||
if (processor_.ctx) {
|
||||
ColorTransformJob job;
|
||||
|
||||
job.set_color_processor(processor_);
|
||||
job.set_color_processor(
|
||||
oakrender_color_processor_get_native(processor_));
|
||||
job.set_input_texture(tex_met);
|
||||
|
||||
table->push(NodeValue::k_texture, t->to_job(job), this);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#define OAK_OCIOBASENODE_H
|
||||
|
||||
#include "node.h"
|
||||
#include "render/color.h"
|
||||
#include "render/job/colortransformjob.h"
|
||||
|
||||
namespace olive
|
||||
@@ -31,6 +32,7 @@ namespace olive
|
||||
class OCIOBaseNode : public Node {
|
||||
public:
|
||||
OCIOBaseNode();
|
||||
virtual ~OCIOBaseNode() override;
|
||||
|
||||
virtual void AddedToGraphEvent(Project *p) override;
|
||||
virtual void RemovedFromGraphEvent(Project *p) override;
|
||||
@@ -51,19 +53,29 @@ protected:
|
||||
return manager_;
|
||||
}
|
||||
|
||||
ColorProcessorPtr processor() const
|
||||
/**
|
||||
* @brief Borrowed copy of the processor handle owned by this node.
|
||||
* Callers must NOT free it.
|
||||
*/
|
||||
const OakColorProcessor &processor() const
|
||||
{
|
||||
return processor_;
|
||||
}
|
||||
void set_processor(ColorProcessorPtr p)
|
||||
|
||||
/**
|
||||
* @brief Take ownership of a processor handle (the old one is
|
||||
* released).
|
||||
*/
|
||||
void set_processor(OakColorProcessor p)
|
||||
{
|
||||
oakrender_color_processor_free(&processor_);
|
||||
processor_ = p;
|
||||
}
|
||||
|
||||
private:
|
||||
ColorManager *manager_;
|
||||
|
||||
ColorProcessorPtr processor_;
|
||||
OakColorProcessor processor_ = {};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
***/
|
||||
|
||||
#include "ociogradingtransformlinear.h"
|
||||
#include "node/colormanager.h"
|
||||
#include "render/color.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
@@ -194,16 +196,13 @@ void OCIOGradingTransformLinearNode::update_clamp_white_minimum()
|
||||
void OCIOGradingTransformLinearNode::generate_processor()
|
||||
{
|
||||
if (manager()) {
|
||||
ocio::GradingPrimaryTransformRcPtr gp =
|
||||
ocio::GradingPrimaryTransform::Create(ocio::GRADING_LIN);
|
||||
gp->makeDynamic();
|
||||
gp->setDirection(ocio::TransformDirection::TRANSFORM_DIR_FORWARD);
|
||||
|
||||
try {
|
||||
set_processor(ColorProcessor::create(
|
||||
manager()->get_config()->getProcessor(gp)));
|
||||
} catch (const ocio::Exception &e) {
|
||||
std::cerr << std::endl << e.what() << std::endl;
|
||||
OakNodeColorManager mgr =
|
||||
oaknode_colormanager_wrap_borrowed(manager());
|
||||
OakColorProcessor processor =
|
||||
oakrender_color_processor_create_grading_primary(mgr, OAKRENDER_GRADING_PRIMARY_LIN);
|
||||
mgr.release(mgr.ctx);
|
||||
if (processor.ctx) {
|
||||
set_processor(processor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,10 +212,11 @@ void OCIOGradingTransformLinearNode::value(const NodeValueRow &value,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
if (TexturePtr tex = value.at(k_texture_input).to_texture()) {
|
||||
if (processor()) {
|
||||
if (processor().ctx) {
|
||||
ColorTransformJob job(value);
|
||||
|
||||
job.set_color_processor(processor());
|
||||
job.set_color_processor(
|
||||
oakrender_color_processor_get_native(processor()));
|
||||
job.set_input_texture(value.at(k_texture_input));
|
||||
|
||||
// Vector4D components stand in for the former QVector4D indices:
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
***/
|
||||
|
||||
#include "ociogradingtransformlog.h"
|
||||
#include "node/colormanager.h"
|
||||
#include "render/color.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
@@ -190,16 +192,13 @@ void OCIOGradingTransformLogNode::update_clamp_white_minimum()
|
||||
void OCIOGradingTransformLogNode::generate_processor()
|
||||
{
|
||||
if (manager()) {
|
||||
OCIO_NAMESPACE::GradingPrimaryTransformRcPtr gp =
|
||||
OCIO_NAMESPACE::GradingPrimaryTransform::Create(OCIO_NAMESPACE::GRADING_LOG);
|
||||
gp->makeDynamic();
|
||||
gp->setDirection(OCIO_NAMESPACE::TransformDirection::TRANSFORM_DIR_FORWARD);
|
||||
|
||||
try {
|
||||
set_processor(ColorProcessor::create(
|
||||
manager()->get_config()->getProcessor(gp)));
|
||||
} catch (const OCIO_NAMESPACE::Exception &e) {
|
||||
std::cerr << std::endl << e.what() << std::endl;
|
||||
OakNodeColorManager mgr =
|
||||
oaknode_colormanager_wrap_borrowed(manager());
|
||||
OakColorProcessor processor =
|
||||
oakrender_color_processor_create_grading_primary(mgr, OAKRENDER_GRADING_PRIMARY_LOG);
|
||||
mgr.release(mgr.ctx);
|
||||
if (processor.ctx) {
|
||||
set_processor(processor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,10 +208,11 @@ void OCIOGradingTransformLogNode::value(const NodeValueRow &value,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
if (TexturePtr tex = value.at(k_texture_input).to_texture()) {
|
||||
if (processor()) {
|
||||
if (processor().ctx) {
|
||||
ColorTransformJob job(value);
|
||||
|
||||
job.set_color_processor(processor());
|
||||
job.set_color_processor(
|
||||
oakrender_color_processor_get_native(processor()));
|
||||
job.set_input_texture(value.at(k_texture_input));
|
||||
|
||||
// Vector4D components stand in for the former QVector4D indices:
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
***/
|
||||
|
||||
#include "ociolut.h"
|
||||
#include "node/colormanager.h"
|
||||
#include "render/color.h"
|
||||
#include "render/manager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
@@ -47,9 +50,20 @@ bool is_main_process()
|
||||
// Qt-free replacement for qobject_cast<QApplication*>(QCoreApplication::instance()):
|
||||
// only the main GUI process creates a RenderManager (the render worker
|
||||
// never does), so its presence identifies the main process.
|
||||
return RenderManager::instance() != nullptr;
|
||||
return oakrender_manager_available() != 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
OCIOLutNode::~OCIOLutNode()
|
||||
{
|
||||
disconnect_all();
|
||||
oakrender_color_processor_free(&last_processor_);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
int read_direction_input(const Node *node)
|
||||
{
|
||||
Variant v = node->get_standard_value(OCIOLutNode::k_direction_input);
|
||||
@@ -82,13 +96,21 @@ OCIOLutNode::OCIOLutNode()
|
||||
{
|
||||
add_input(k_file_input, NodeValue::k_file, std::string(),
|
||||
InputFlags(k_input_flag_not_keyframable | k_input_flag_not_connectable));
|
||||
const StringList &extensions = LUTLibrary::supported_extensions();
|
||||
std::string all_luts = "*.";
|
||||
for (size_t i = 0; i < extensions.size(); i++) {
|
||||
if (i > 0) {
|
||||
all_luts += " *.";
|
||||
std::string all_luts;
|
||||
int ext_count = oakrender_lut_supported_extensions_count();
|
||||
for (int i = 0; i < ext_count; i++) {
|
||||
char ext[32];
|
||||
if (oakrender_lut_supported_extension_at(i, ext, sizeof(ext)) <= 0) {
|
||||
continue;
|
||||
}
|
||||
all_luts += extensions[i];
|
||||
if (!all_luts.empty()) {
|
||||
all_luts += " ";
|
||||
}
|
||||
all_luts += "*.";
|
||||
all_luts += ext;
|
||||
}
|
||||
if (all_luts.empty()) {
|
||||
all_luts = "*.*";
|
||||
}
|
||||
set_input_property(k_file_input, "filter",
|
||||
"LUT Files (" + all_luts + ");;All Files (*)");
|
||||
@@ -180,11 +202,7 @@ void OCIOLutNode::generate_processor()
|
||||
// RenderManager/PreviewAutoCacher, so skip this step to avoid crashing.
|
||||
if (is_main_process()) {
|
||||
invalidate_all(k_texture_input);
|
||||
if (RenderManager *rm = RenderManager::instance()) {
|
||||
if (PreviewAutoCacher *cacher = rm->get_cacher()) {
|
||||
cacher->cancel_video_tasks(false);
|
||||
}
|
||||
}
|
||||
oakrender_cancel_video_tasks(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +210,7 @@ void OCIOLutNode::ensure_processor() const
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(gen_mutex_);
|
||||
|
||||
if (!processor_dirty_ && last_processor_ &&
|
||||
if (!processor_dirty_ && last_processor_.ctx &&
|
||||
get_standard_value(k_file_input).to_string() == last_path_ &&
|
||||
read_direction_input(this) == last_direction_) {
|
||||
return;
|
||||
@@ -217,8 +235,8 @@ void OCIOLutNode::set_last_error(const std::string &error) const
|
||||
bool OCIOLutNode::create_processor_from_inputs() const
|
||||
{
|
||||
if (!manager()) {
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(nullptr);
|
||||
last_processor_.reset();
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(OakColorProcessor{});
|
||||
oakrender_color_processor_free(&last_processor_);
|
||||
last_path_.clear();
|
||||
last_direction_ = -1;
|
||||
processor_dirty_ = false;
|
||||
@@ -229,8 +247,8 @@ bool OCIOLutNode::create_processor_from_inputs() const
|
||||
const int direction = read_direction_input(this);
|
||||
|
||||
if (path.empty()) {
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(nullptr);
|
||||
last_processor_.reset();
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(OakColorProcessor{});
|
||||
oakrender_color_processor_free(&last_processor_);
|
||||
last_path_.clear();
|
||||
last_direction_ = -1;
|
||||
processor_dirty_ = false;
|
||||
@@ -239,7 +257,8 @@ bool OCIOLutNode::create_processor_from_inputs() const
|
||||
}
|
||||
|
||||
// Re-use the existing processor if the file and direction haven't changed.
|
||||
if (path == last_path_ && direction == last_direction_ && last_processor_) {
|
||||
if (path == last_path_ && direction == last_direction_ &&
|
||||
last_processor_.ctx) {
|
||||
processor_dirty_ = false;
|
||||
return false;
|
||||
}
|
||||
@@ -249,8 +268,8 @@ bool OCIOLutNode::create_processor_from_inputs() const
|
||||
std::filesystem::is_regular_file(path, fs_ec) && !fs_ec;
|
||||
if (!is_file) {
|
||||
fprintf(stderr, "OCIO LUT file does not exist: %s\n", path.c_str());
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(nullptr);
|
||||
last_processor_.reset();
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(OakColorProcessor{});
|
||||
oakrender_color_processor_free(&last_processor_);
|
||||
last_path_.clear();
|
||||
last_direction_ = -1;
|
||||
processor_dirty_ = false;
|
||||
@@ -262,11 +281,11 @@ bool OCIOLutNode::create_processor_from_inputs() const
|
||||
if (!suffix.empty() && suffix.front() == '.') {
|
||||
suffix.erase(suffix.begin());
|
||||
}
|
||||
if (!LUTLibrary::is_supported_extension(suffix)) {
|
||||
if (!oakrender_lut_is_supported_extension(suffix.c_str())) {
|
||||
fprintf(stderr, "Unsupported OCIO LUT file extension: %s\n",
|
||||
path.c_str());
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(nullptr);
|
||||
last_processor_.reset();
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(OakColorProcessor{});
|
||||
oakrender_color_processor_free(&last_processor_);
|
||||
last_path_.clear();
|
||||
last_direction_ = -1;
|
||||
processor_dirty_ = false;
|
||||
@@ -274,30 +293,23 @@ bool OCIOLutNode::create_processor_from_inputs() const
|
||||
return false;
|
||||
}
|
||||
|
||||
ColorProcessorPtr processor;
|
||||
try {
|
||||
const bool forward = static_cast<ColorProcessor::Direction>(
|
||||
direction) == ColorProcessor::k_normal;
|
||||
fprintf(stderr,
|
||||
"OCIOLutNode: creating processor for %s direction=%d "
|
||||
"ocio_dir=%s process=%s\n",
|
||||
path.c_str(), direction, forward ? "FORWARD" : "INVERSE",
|
||||
is_main_process() ? "main" : "worker");
|
||||
const bool forward = direction == 0; // ColorProcessor::k_normal
|
||||
fprintf(stderr,
|
||||
"OCIOLutNode: creating processor for %s direction=%d "
|
||||
"ocio_dir=%s process=%s\n",
|
||||
path.c_str(), direction, forward ? "FORWARD" : "INVERSE",
|
||||
is_main_process() ? "main" : "worker");
|
||||
|
||||
ocio::FileTransformRcPtr transform = ocio::FileTransform::Create();
|
||||
transform->setSrc(path.c_str());
|
||||
transform->setInterpolation(ocio::INTERP_LINEAR);
|
||||
transform->setDirection(forward ? ocio::TRANSFORM_DIR_FORWARD :
|
||||
ocio::TRANSFORM_DIR_INVERSE);
|
||||
OakNodeColorManager mgr =
|
||||
oaknode_colormanager_wrap_borrowed(manager());
|
||||
OakColorProcessor processor = oakrender_color_processor_create_lut(
|
||||
mgr, path.c_str(),
|
||||
forward ? OAKRENDER_COLOR_DIRECTION_NORMAL :
|
||||
OAKRENDER_COLOR_DIRECTION_INVERSE);
|
||||
mgr.release(mgr.ctx);
|
||||
|
||||
processor = ColorProcessor::create(
|
||||
manager()->get_config()->getProcessor(transform));
|
||||
} catch (const std::exception &e) {
|
||||
fprintf(stderr, "OCIO LUT processor error: %s\n", e.what());
|
||||
processor = nullptr;
|
||||
}
|
||||
|
||||
if (!processor) {
|
||||
if (!processor.ctx) {
|
||||
fprintf(stderr, "OCIO LUT processor error for %s\n", path.c_str());
|
||||
set_last_error("OCIO LUT: failed to load LUT file: " + path);
|
||||
} else {
|
||||
set_last_error(std::string());
|
||||
@@ -305,6 +317,7 @@ bool OCIOLutNode::create_processor_from_inputs() const
|
||||
|
||||
last_path_ = path;
|
||||
last_direction_ = direction;
|
||||
oakrender_color_processor_free(&last_processor_);
|
||||
last_processor_ = processor;
|
||||
const_cast<OCIOLutNode *>(this)->set_processor(processor);
|
||||
processor_dirty_ = false;
|
||||
|
||||
@@ -33,8 +33,9 @@ namespace olive
|
||||
class OCIOLutNode : public OCIOBaseNode {
|
||||
public:
|
||||
OCIOLutNode();
|
||||
virtual ~OCIOLutNode() override;
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(OCIOLutNode)
|
||||
NODE_COPY_FUNCTION(OCIOLutNode)
|
||||
|
||||
virtual std::string name() const override;
|
||||
virtual std::string id() const override;
|
||||
@@ -76,7 +77,7 @@ private:
|
||||
mutable bool processor_dirty_ = true;
|
||||
mutable std::string last_path_;
|
||||
mutable int last_direction_ = -1;
|
||||
mutable ColorProcessorPtr last_processor_;
|
||||
mutable OakColorProcessor last_processor_ = {};
|
||||
mutable std::string last_error_;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
***/
|
||||
|
||||
#include "chromakey.h"
|
||||
#include "common/colortransform.h"
|
||||
#include "node/colormanager.h"
|
||||
#include "render/color.h"
|
||||
|
||||
#include "color/colormanager/colormanager.h"
|
||||
#include "render/colorprocessor.h"
|
||||
@@ -134,12 +137,25 @@ ShaderCode ChromaKeyNode::get_shader_code(const ShaderRequest &request) const
|
||||
void ChromaKeyNode::generate_processor()
|
||||
{
|
||||
if (manager()) {
|
||||
try {
|
||||
ColorTransform transform("cie_xyz_d65_interchange");
|
||||
set_processor(ColorProcessor::create(
|
||||
manager(), manager()->get_reference_color_space(), transform));
|
||||
} catch (const ocio::Exception &e) {
|
||||
std::cerr << std::endl << e.what() << std::endl;
|
||||
OakNodeColorManager mgr =
|
||||
oaknode_colormanager_wrap_borrowed(manager());
|
||||
OakColorTransform transform =
|
||||
oakcommon_colortransform_init_output("cie_xyz_d65_interchange");
|
||||
|
||||
char ref_space[256];
|
||||
int needed = oaknode_colormanager_get_reference_color_space(
|
||||
mgr, ref_space, sizeof(ref_space));
|
||||
OakColorProcessor processor = {};
|
||||
if (needed > 0 && needed <= int(sizeof(ref_space))) {
|
||||
processor = oakrender_color_processor_create_transform(
|
||||
mgr, ref_space, transform, OAKRENDER_COLOR_DIRECTION_NORMAL);
|
||||
}
|
||||
|
||||
oakcommon_colortransform_free(&transform);
|
||||
mgr.release(mgr.ctx);
|
||||
|
||||
if (processor.ctx) {
|
||||
set_processor(processor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,10 +164,11 @@ void ChromaKeyNode::value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
NodeValueTable *table) const
|
||||
{
|
||||
if (TexturePtr tex = value.at(k_texture_input).to_texture()) {
|
||||
if (processor()) {
|
||||
if (processor().ctx) {
|
||||
ColorTransformJob job(value);
|
||||
|
||||
job.set_color_processor(processor());
|
||||
job.set_color_processor(
|
||||
oakrender_color_processor_get_native(processor()));
|
||||
job.set_input_texture(value.at(k_texture_input));
|
||||
job.set_needs_custom_shader(this);
|
||||
job.set_function_name("SceneLinearToCIEXYZ_d65");
|
||||
|
||||
+77
-24
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "lerp.h"
|
||||
#include "configaccessor.h"
|
||||
#include "../c_api/nodehandle.h"
|
||||
#include "group/group.h"
|
||||
#include "project/serializer/typeserializer.h"
|
||||
#include "nodeundo.h"
|
||||
@@ -54,6 +55,39 @@ static std::string number_to_string(double d)
|
||||
|
||||
const std::string Node::k_enabled_input = "enabled_in";
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Invalidate a node-owned cache over a rational time range
|
||||
* through the oakrender C ABI.
|
||||
*/
|
||||
void invalidate_handle_cache(const OakRenderCache &cache,
|
||||
const olive::TimeRange &range)
|
||||
{
|
||||
oakrender_cache_invalidate_range(cache, range.in().numerator(),
|
||||
range.in().denominator(),
|
||||
range.out().numerator(),
|
||||
range.out().denominator());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fetch a cache UUID through the two-stage string API.
|
||||
*/
|
||||
std::string cache_uuid_string(const OakRenderCache &cache)
|
||||
{
|
||||
int needed = oakrender_cache_get_uuid(cache, nullptr, 0);
|
||||
if (needed <= 0) {
|
||||
return std::string();
|
||||
}
|
||||
std::string uuid(size_t(needed), '\0');
|
||||
oakrender_cache_get_uuid(cache, uuid.data(), needed);
|
||||
uuid.resize(size_t(needed - 1));
|
||||
return uuid;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Node::Node()
|
||||
: override_color_(-1)
|
||||
, folder_(nullptr)
|
||||
@@ -63,12 +97,21 @@ Node::Node()
|
||||
{
|
||||
add_input(k_enabled_input, NodeValue::k_boolean, true);
|
||||
|
||||
video_cache_ = new FrameHashCache(this);
|
||||
thumbnail_cache_ = new ThumbnailCache(this);
|
||||
audio_cache_ = new AudioPlaybackCache(this);
|
||||
waveform_cache_ = new AudioWaveformCache(this);
|
||||
// Borrowed self-handle: the caches keep a native back-pointer
|
||||
// inside oakrender, the box goes away right here
|
||||
OakNodeNode self = oaknode_c_api::make_handle<OakNodeNode>(
|
||||
this, false, nullptr);
|
||||
video_cache_ = oakrender_cache_create_for_node(
|
||||
self, OAKRENDER_CACHE_VIDEO_FRAME);
|
||||
thumbnail_cache_ = oakrender_cache_create_for_node(
|
||||
self, OAKRENDER_CACHE_THUMBNAIL);
|
||||
audio_cache_ = oakrender_cache_create_for_node(
|
||||
self, OAKRENDER_CACHE_AUDIO_PLAYBACK);
|
||||
waveform_cache_ = oakrender_cache_create_for_node(
|
||||
self, OAKRENDER_CACHE_AUDIO_WAVEFORM);
|
||||
self.release(self.ctx);
|
||||
|
||||
waveform_cache_->set_saving_enabled(false);
|
||||
oakrender_cache_set_saving_enabled(waveform_cache_, 0);
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
@@ -89,6 +132,11 @@ Node::~Node()
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
oakrender_cache_free(&video_cache_);
|
||||
oakrender_cache_free(&thumbnail_cache_);
|
||||
oakrender_cache_free(&audio_cache_);
|
||||
oakrender_cache_free(&waveform_cache_);
|
||||
}
|
||||
|
||||
std::string Node::short_name() const
|
||||
@@ -225,10 +273,14 @@ void Node::disconnect_edge(Node *output, const NodeInput &input, bool silent)
|
||||
|
||||
void Node::copy_cache_uuids_from(Node *n)
|
||||
{
|
||||
video_cache_->set_uuid(n->video_cache_->get_uuid());
|
||||
audio_cache_->set_uuid(n->audio_cache_->get_uuid());
|
||||
thumbnail_cache_->set_uuid(n->thumbnail_cache_->get_uuid());
|
||||
waveform_cache_->set_uuid(n->waveform_cache_->get_uuid());
|
||||
auto copy_cache_uuid = [](const OakRenderCache &from, OakRenderCache *to) {
|
||||
oakrender_cache_set_uuid(*to, cache_uuid_string(from).c_str());
|
||||
};
|
||||
|
||||
copy_cache_uuid(n->video_cache_, &video_cache_);
|
||||
copy_cache_uuid(n->audio_cache_, &audio_cache_);
|
||||
copy_cache_uuid(n->thumbnail_cache_, &thumbnail_cache_);
|
||||
copy_cache_uuid(n->waveform_cache_, &waveform_cache_);
|
||||
}
|
||||
|
||||
std::string Node::get_input_name(const std::string &id) const
|
||||
@@ -996,13 +1048,13 @@ void Node::invalidate_cache(const TimeRange &range, const std::string &from,
|
||||
if (range.in() != range.out()) {
|
||||
TimeRange vr = range.intersected(get_video_cache_range());
|
||||
if (vr.length() != 0) {
|
||||
video_frame_cache()->invalidate(vr);
|
||||
thumbnail_cache()->invalidate(vr);
|
||||
invalidate_handle_cache(video_cache_, vr);
|
||||
invalidate_handle_cache(thumbnail_cache_, vr);
|
||||
}
|
||||
TimeRange ar = range.intersected(get_audio_cache_range());
|
||||
if (ar.length() != 0) {
|
||||
audio_playback_cache()->invalidate(ar);
|
||||
waveform_cache()->invalidate(ar);
|
||||
invalidate_handle_cache(audio_cache_, ar);
|
||||
invalidate_handle_cache(waveform_cache_, ar);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1410,13 +1462,17 @@ bool Node::load(XmlStreamReader *reader, SerializedData *data)
|
||||
} else if (reader->name() == "caches") {
|
||||
while (xml_read_next_start_element(reader)) {
|
||||
if (reader->name() == "audio") {
|
||||
this->audio_playback_cache()->set_uuid(reader->read_element_text());
|
||||
oakrender_cache_set_uuid(
|
||||
audio_cache_, reader->read_element_text().c_str());
|
||||
} else if (reader->name() == "video") {
|
||||
this->video_frame_cache()->set_uuid(reader->read_element_text());
|
||||
oakrender_cache_set_uuid(
|
||||
video_cache_, reader->read_element_text().c_str());
|
||||
} else if (reader->name() == "thumb") {
|
||||
this->thumbnail_cache()->set_uuid(reader->read_element_text());
|
||||
oakrender_cache_set_uuid(
|
||||
thumbnail_cache_, reader->read_element_text().c_str());
|
||||
} else if (reader->name() == "waveform") {
|
||||
this->waveform_cache()->set_uuid(reader->read_element_text());
|
||||
oakrender_cache_set_uuid(
|
||||
waveform_cache_, reader->read_element_text().c_str());
|
||||
} else {
|
||||
reader->skip_current_element();
|
||||
}
|
||||
@@ -1518,14 +1574,11 @@ void Node::save(XmlStreamWriter *writer) const
|
||||
|
||||
writer->write_start_element("caches");
|
||||
|
||||
writer->write_text_element("audio",
|
||||
this->audio_playback_cache()->get_uuid());
|
||||
writer->write_text_element("video",
|
||||
this->video_frame_cache()->get_uuid());
|
||||
writer->write_text_element("thumb",
|
||||
this->thumbnail_cache()->get_uuid());
|
||||
writer->write_text_element("audio", cache_uuid_string(audio_cache_));
|
||||
writer->write_text_element("video", cache_uuid_string(video_cache_));
|
||||
writer->write_text_element("thumb", cache_uuid_string(thumbnail_cache_));
|
||||
writer->write_text_element("waveform",
|
||||
this->waveform_cache()->get_uuid());
|
||||
cache_uuid_string(waveform_cache_));
|
||||
|
||||
writer->write_end_element(); // caches
|
||||
|
||||
|
||||
+14
-11
@@ -37,9 +37,7 @@
|
||||
#include "param.h"
|
||||
#include "ofxhImageEffectAPI.h"
|
||||
#include "olive/core/util/timerange.h"
|
||||
#include "render/audioplaybackcache.h"
|
||||
#include "render/audiowaveformcache.h"
|
||||
#include "render/framehashcache.h"
|
||||
#include "render/cache.h"
|
||||
#include "render/job/generatejob.h"
|
||||
#include "render/job/samplejob.h"
|
||||
#include "render/job/shaderjob.h"
|
||||
@@ -293,22 +291,27 @@ public:
|
||||
return has_input_with_id(id);
|
||||
}
|
||||
|
||||
FrameHashCache *video_frame_cache() const
|
||||
/**
|
||||
* @brief Borrowed copies of the cache handles owned by this node
|
||||
* (oakrender caches). Callers must NOT free them; addref
|
||||
* first to keep one beyond the node's lifetime.
|
||||
*/
|
||||
const OakRenderCache &video_frame_cache() const
|
||||
{
|
||||
return video_cache_;
|
||||
}
|
||||
|
||||
ThumbnailCache *thumbnail_cache() const
|
||||
const OakRenderCache &thumbnail_cache() const
|
||||
{
|
||||
return thumbnail_cache_;
|
||||
}
|
||||
|
||||
AudioPlaybackCache *audio_playback_cache() const
|
||||
const OakRenderCache &audio_playback_cache() const
|
||||
{
|
||||
return audio_cache_;
|
||||
}
|
||||
|
||||
AudioWaveformCache *waveform_cache() const
|
||||
const OakRenderCache &waveform_cache() const
|
||||
{
|
||||
return waveform_cache_;
|
||||
}
|
||||
@@ -1505,11 +1508,11 @@ private:
|
||||
|
||||
std::string effect_input_;
|
||||
|
||||
FrameHashCache *video_cache_;
|
||||
ThumbnailCache *thumbnail_cache_;
|
||||
OakRenderCache video_cache_ = {};
|
||||
OakRenderCache thumbnail_cache_ = {};
|
||||
|
||||
AudioPlaybackCache *audio_cache_;
|
||||
AudioWaveformCache *waveform_cache_;
|
||||
OakRenderCache audio_cache_ = {};
|
||||
OakRenderCache waveform_cache_ = {};
|
||||
|
||||
bool caches_enabled_;
|
||||
};
|
||||
|
||||
@@ -23,6 +23,28 @@
|
||||
|
||||
#include "common/xmlutils.h"
|
||||
#include "configaccessor.h"
|
||||
#include "../../../c_api/nodehandle.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief PlaybackCache::request(context, range) through the oakrender
|
||||
* C ABI; the context handle is a borrowed box made on the spot.
|
||||
*/
|
||||
void request_cache_range(const OakRenderCache &cache, olive::Node *context,
|
||||
const olive::TimeRange &range)
|
||||
{
|
||||
OakNodeNode ctx = oaknode_c_api::make_handle<OakNodeNode>(
|
||||
context, false, nullptr);
|
||||
oakrender_cache_request(cache, ctx, range.in().numerator(),
|
||||
range.in().denominator(),
|
||||
range.out().numerator(),
|
||||
range.out().denominator());
|
||||
ctx.release(ctx.ctx);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#include "coreengine.h"
|
||||
#include "traverser.h"
|
||||
#include "olive/core/util/timecodefunctions.h"
|
||||
@@ -263,19 +285,19 @@ void ViewerOutput::invalidate_cache(const TimeRange &range,
|
||||
if (autocache_input_video_) {
|
||||
TimeRange max_range = input_time_adjustment(
|
||||
from, element, TimeRange(0, get_video_length()), false);
|
||||
connected->video_frame_cache()->request(
|
||||
this, range.intersected(max_range));
|
||||
request_cache_range(connected->video_frame_cache(), this,
|
||||
range.intersected(max_range));
|
||||
}
|
||||
} else if (from == k_samples_input) {
|
||||
TimeRange max_range = input_time_adjustment(
|
||||
from, element, TimeRange(0, get_audio_length()), false);
|
||||
if (waveform_requests_enabled_) {
|
||||
connected->waveform_cache()->request(
|
||||
this, range.intersected(max_range));
|
||||
request_cache_range(connected->waveform_cache(), this,
|
||||
range.intersected(max_range));
|
||||
}
|
||||
if (autocache_input_audio_) {
|
||||
connected->audio_playback_cache()->request(
|
||||
this, range.intersected(max_range));
|
||||
request_cache_range(connected->audio_playback_cache(), this,
|
||||
range.intersected(max_range));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -434,10 +456,26 @@ void ViewerOutput::set_waveform_enabled(bool e)
|
||||
if (Node *connected = this->get_connected_sample_output()) {
|
||||
TimeRange max_range = input_time_adjustment(
|
||||
k_samples_input, -1, TimeRange(0, get_audio_length()), false);
|
||||
core::TimeRangeList invalid =
|
||||
connected->waveform_cache()->get_invalidated_ranges(max_range);
|
||||
for (const TimeRange &r : invalid) {
|
||||
connected->waveform_cache()->request(this, r);
|
||||
const OakRenderCache &wave_cache =
|
||||
connected->waveform_cache();
|
||||
int n = oakrender_cache_get_invalidated_ranges(
|
||||
wave_cache, max_range.in().numerator(),
|
||||
max_range.in().denominator(), max_range.out().numerator(),
|
||||
max_range.out().denominator(), NULL, 0);
|
||||
if (n > 0) {
|
||||
std::vector<int64_t> ranges(size_t(n) * 4);
|
||||
oakrender_cache_get_invalidated_ranges(
|
||||
wave_cache, max_range.in().numerator(),
|
||||
max_range.in().denominator(),
|
||||
max_range.out().numerator(),
|
||||
max_range.out().denominator(), ranges.data(), n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
request_cache_range(
|
||||
wave_cache, this,
|
||||
TimeRange(Rational(ranges[i * 4], ranges[i * 4 + 1]),
|
||||
Rational(ranges[i * 4 + 2],
|
||||
ranges[i * 4 + 3])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
#include "codec/encoder.h"
|
||||
#include "node.h"
|
||||
#include "output/track/track.h"
|
||||
#include "render/audioplaybackcache.h"
|
||||
#include "render/framehashcache.h"
|
||||
#include "subtitleparams.h"
|
||||
#include "videoparams.h"
|
||||
#include "timeline/marker.h"
|
||||
@@ -145,12 +143,17 @@ public:
|
||||
get_subtitle_stream_count();
|
||||
}
|
||||
|
||||
const AudioWaveformCache *get_connected_waveform()
|
||||
/**
|
||||
* @brief Borrowed copy of the connected node's waveform cache handle
|
||||
* (empty handle when nothing is connected). Callers must NOT
|
||||
* free it.
|
||||
*/
|
||||
OakRenderCache get_connected_waveform()
|
||||
{
|
||||
if (Node *n = get_connected_sample_output()) {
|
||||
return n->waveform_cache();
|
||||
} else {
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <set>
|
||||
|
||||
#include "current.h"
|
||||
#include "render/manager.h"
|
||||
#include "xmlutils.h"
|
||||
#include "color/ociobase/ociobase.h"
|
||||
#include "factory.h"
|
||||
@@ -469,9 +470,14 @@ std::string Project::cache_path() const
|
||||
}
|
||||
}
|
||||
|
||||
// ADAPT(M7): render/diskmanager.h is converted in M7; it must return
|
||||
// std::string here.
|
||||
return DiskManager::instance()->get_default_cache_path();
|
||||
int needed = oakrender_disk_cache_path(NULL, 0);
|
||||
if (needed <= 0) {
|
||||
return std::string();
|
||||
}
|
||||
std::string path(size_t(needed), '\0');
|
||||
oakrender_disk_cache_path(path.data(), needed);
|
||||
path.resize(size_t(needed - 1));
|
||||
return path;
|
||||
}
|
||||
|
||||
void Project::regenerate_uuid()
|
||||
|
||||
@@ -516,17 +516,17 @@ void ProjectSerializer220403::load_node(Node *node, XMLNodeData &xml_node_data,
|
||||
} else if (reader->name() == "caches") {
|
||||
while (xml_read_next_start_element(reader)) {
|
||||
if (reader->name() == "audio") {
|
||||
node->audio_playback_cache()->set_uuid(
|
||||
reader->read_element_text());
|
||||
oakrender_cache_set_uuid(node->audio_playback_cache(),
|
||||
reader->read_element_text().c_str());
|
||||
} else if (reader->name() == "video") {
|
||||
node->video_frame_cache()->set_uuid(
|
||||
reader->read_element_text());
|
||||
oakrender_cache_set_uuid(node->video_frame_cache(),
|
||||
reader->read_element_text().c_str());
|
||||
} else if (reader->name() == "thumb") {
|
||||
node->thumbnail_cache()->set_uuid(
|
||||
reader->read_element_text());
|
||||
oakrender_cache_set_uuid(node->thumbnail_cache(),
|
||||
reader->read_element_text().c_str());
|
||||
} else if (reader->name() == "waveform") {
|
||||
node->waveform_cache()->set_uuid(
|
||||
reader->read_element_text());
|
||||
oakrender_cache_set_uuid(node->waveform_cache(),
|
||||
reader->read_element_text().c_str());
|
||||
} else {
|
||||
reader->skip_current_element();
|
||||
}
|
||||
|
||||
@@ -147,12 +147,25 @@ NodeValue NodeTraverser::generate_row_value_element(const Node *node,
|
||||
|
||||
if (value.type() == NodeValue::k_texture && use_cache()) {
|
||||
if (TexturePtr tex = value.to_texture()) {
|
||||
std::lock_guard<std::mutex> locker(node->video_frame_cache()->mutex());
|
||||
const OakRenderCache &frame_cache = node->video_frame_cache();
|
||||
oakrender_cache_lock(frame_cache);
|
||||
|
||||
node->video_frame_cache()->load_state();
|
||||
oakrender_cache_load_state(frame_cache);
|
||||
|
||||
std::string cache;
|
||||
int needed = oakrender_cache_get_valid_cache_filename(
|
||||
frame_cache, time.in().numerator(), time.in().denominator(),
|
||||
nullptr, 0);
|
||||
if (needed > 0) {
|
||||
cache.resize(size_t(needed), '\0');
|
||||
oakrender_cache_get_valid_cache_filename(
|
||||
frame_cache, time.in().numerator(),
|
||||
time.in().denominator(), cache.data(), needed);
|
||||
cache.resize(size_t(needed - 1));
|
||||
}
|
||||
|
||||
oakrender_cache_unlock(frame_cache);
|
||||
|
||||
std::string cache =
|
||||
node->video_frame_cache()->get_valid_cache_filename(time.in());
|
||||
if (!cache.empty()) {
|
||||
value.set_value(tex->to_job(CacheJob(cache, value)));
|
||||
}
|
||||
|
||||
@@ -326,3 +326,35 @@ TEST_F(ColorManagerTest, CompliantColorTransform)
|
||||
oaknode_colormanager_free(&m);
|
||||
release_borrowed(ph);
|
||||
}
|
||||
|
||||
TEST_F(ColorManagerTest, WrapBorrowedNull)
|
||||
{
|
||||
EXPECT_EQ(oaknode_colormanager_wrap_borrowed(nullptr).ctx, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(ColorManagerTest, WrapBorrowedAndGetNative)
|
||||
{
|
||||
olive::Project project;
|
||||
OakNodeProject ph = borrow_project(&project);
|
||||
|
||||
OakNodeColorManager m = oaknode_colormanager_init(ph);
|
||||
ASSERT_NE(m.ctx, nullptr);
|
||||
|
||||
olive::ColorManager *native = oaknode_colormanager_get_native(m);
|
||||
ASSERT_NE(native, nullptr);
|
||||
|
||||
OakNodeColorManager borrowed =
|
||||
oaknode_colormanager_wrap_borrowed(native);
|
||||
ASSERT_NE(borrowed.ctx, nullptr);
|
||||
EXPECT_EQ(oaknode_colormanager_get_native(borrowed), native);
|
||||
|
||||
// Releasing the borrowed box must not destroy the object
|
||||
borrowed.release(borrowed.ctx);
|
||||
EXPECT_EQ(oaknode_colormanager_get_native(m), native);
|
||||
|
||||
EXPECT_EQ(oaknode_colormanager_get_native(OakNodeColorManager{}),
|
||||
nullptr);
|
||||
|
||||
oaknode_colormanager_free(&m);
|
||||
release_borrowed(ph);
|
||||
}
|
||||
|
||||
@@ -21,13 +21,18 @@
|
||||
#include "../../../include/render/cache.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
|
||||
#include "alivecount.h"
|
||||
#include "internalhandles.h"
|
||||
|
||||
#include "audioplaybackcache.h"
|
||||
#include "audiowaveformcache.h"
|
||||
#include "framehashcache.h"
|
||||
#include "playbackcache.h"
|
||||
#include "output/viewer/viewer.h"
|
||||
#include "../../node/c_api/nodehandle.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -114,6 +119,231 @@ OakRenderCache oakrender_cache_wrap_borrowed(void *native_cache)
|
||||
native_cache, false, nullptr);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Recover the boxed cache as its PlaybackCache base (NULL-safe).
|
||||
*/
|
||||
olive::PlaybackCache *base(OakRenderCache c)
|
||||
{
|
||||
if (!c.ctx) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<olive::PlaybackCache *>(
|
||||
oakrender_c_api::box_object(c.ctx));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy @p value into the two-stage string buffer.
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL). The buffer is
|
||||
* only written to when it is large enough.
|
||||
*/
|
||||
int copy_string(const std::string &value, char *buf, int buf_size)
|
||||
{
|
||||
int needed = static_cast<int>(value.size()) + 1;
|
||||
if (buf && buf_size >= needed) {
|
||||
memcpy(buf, value.c_str(), needed);
|
||||
}
|
||||
return needed;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
OakRenderCache oakrender_cache_create_for_node(OakNodeNode parent, int kind)
|
||||
{
|
||||
olive::Node *native_parent =
|
||||
oaknode_c_api::to_native<olive::Node>(parent);
|
||||
if (!native_parent) {
|
||||
return OakRenderCache{};
|
||||
}
|
||||
|
||||
olive::PlaybackCache *cache = nullptr;
|
||||
switch (kind) {
|
||||
case OAKRENDER_CACHE_VIDEO_FRAME:
|
||||
cache = new (std::nothrow) olive::FrameHashCache(native_parent);
|
||||
break;
|
||||
case OAKRENDER_CACHE_THUMBNAIL:
|
||||
cache = new (std::nothrow) olive::ThumbnailCache(native_parent);
|
||||
break;
|
||||
case OAKRENDER_CACHE_AUDIO_PLAYBACK:
|
||||
cache = new (std::nothrow) olive::AudioPlaybackCache(native_parent);
|
||||
break;
|
||||
case OAKRENDER_CACHE_AUDIO_WAVEFORM:
|
||||
cache = new (std::nothrow) olive::AudioWaveformCache(native_parent);
|
||||
break;
|
||||
default:
|
||||
return OakRenderCache{};
|
||||
}
|
||||
if (!cache) {
|
||||
return OakRenderCache{};
|
||||
}
|
||||
|
||||
return oakrender_c_api::make_handle<OakRenderCache>(
|
||||
cache, true, &oakrender_c_api::delete_as<olive::PlaybackCache>);
|
||||
}
|
||||
|
||||
int oakrender_cache_get_uuid(OakRenderCache cache, char *buf, int buf_size)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
return copy_string(c->get_uuid(), buf, buf_size);
|
||||
}
|
||||
|
||||
void oakrender_cache_invalidate_range(OakRenderCache cache,
|
||||
int64_t in_num, int64_t in_den,
|
||||
int64_t out_num, int64_t out_den)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
c->invalidate(
|
||||
olive::core::TimeRange(olive::Rational(in_num, in_den),
|
||||
olive::Rational(out_num, out_den)));
|
||||
}
|
||||
|
||||
int oakrender_cache_request(OakRenderCache cache, OakNodeNode context,
|
||||
int64_t in_num, int64_t in_den,
|
||||
int64_t out_num, int64_t out_den)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
olive::Node *native_context =
|
||||
oaknode_c_api::to_native<olive::Node>(context);
|
||||
auto *viewer = dynamic_cast<olive::ViewerOutput *>(native_context);
|
||||
if (!c || !viewer) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
c->request(viewer,
|
||||
olive::core::TimeRange(olive::Rational(in_num, in_den),
|
||||
olive::Rational(out_num, out_den)));
|
||||
return OAKRENDER_OK;
|
||||
}
|
||||
|
||||
int oakrender_cache_load_state(OakRenderCache cache)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
try {
|
||||
c->load_state();
|
||||
return OAKRENDER_OK;
|
||||
} catch (...) {
|
||||
return OAKRENDER_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakrender_cache_save_state(OakRenderCache cache)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
try {
|
||||
c->save_state();
|
||||
return OAKRENDER_OK;
|
||||
} catch (...) {
|
||||
return OAKRENDER_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakrender_cache_set_saving_enabled(OakRenderCache cache, int enabled)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
c->set_saving_enabled(enabled != 0);
|
||||
return OAKRENDER_OK;
|
||||
}
|
||||
|
||||
int oakrender_cache_set_passthrough(OakRenderCache cache,
|
||||
OakRenderCache other)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
olive::PlaybackCache *o = base(other);
|
||||
if (!c || !o) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
c->set_passthrough(o);
|
||||
return OAKRENDER_OK;
|
||||
}
|
||||
|
||||
int oakrender_cache_get_valid_cache_filename(OakRenderCache cache,
|
||||
int64_t time_num,
|
||||
int64_t time_den, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
auto *c = dynamic_cast<olive::FrameHashCache *>(base(cache));
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
return copy_string(
|
||||
c->get_valid_cache_filename(olive::Rational(time_num, time_den)),
|
||||
buf, buf_size);
|
||||
}
|
||||
|
||||
int oakrender_cache_get_passthroughs(OakRenderCache cache, int64_t *ranges,
|
||||
int max_ranges)
|
||||
{
|
||||
olive::PlaybackCache *c = base(cache);
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
|
||||
const std::vector<olive::PlaybackCache::Passthrough> &all =
|
||||
c->get_passthroughs();
|
||||
int count = static_cast<int>(all.size());
|
||||
if (ranges && max_ranges > 0) {
|
||||
int n = count < max_ranges ? count : max_ranges;
|
||||
for (int i = 0; i < n; i++) {
|
||||
ranges[i * 4] = all[i].in().numerator();
|
||||
ranges[i * 4 + 1] = all[i].in().denominator();
|
||||
ranges[i * 4 + 2] = all[i].out().numerator();
|
||||
ranges[i * 4 + 3] = all[i].out().denominator();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int oakrender_cache_get_timebase(OakRenderCache cache, int *num, int *den)
|
||||
{
|
||||
auto *c = dynamic_cast<olive::FrameHashCache *>(base(cache));
|
||||
if (!c) {
|
||||
return OAKRENDER_E_INVALID;
|
||||
}
|
||||
if (num) {
|
||||
*num = c->get_timebase().numerator();
|
||||
}
|
||||
if (den) {
|
||||
*den = c->get_timebase().denominator();
|
||||
}
|
||||
return OAKRENDER_OK;
|
||||
}
|
||||
|
||||
void oakrender_cache_lock(OakRenderCache cache)
|
||||
{
|
||||
if (olive::PlaybackCache *c = base(cache)) {
|
||||
c->mutex().lock();
|
||||
}
|
||||
}
|
||||
|
||||
void oakrender_cache_unlock(OakRenderCache cache)
|
||||
{
|
||||
if (olive::PlaybackCache *c = base(cache)) {
|
||||
c->mutex().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
olive::PlaybackCache *oakrender_cache_get_native(OakRenderCache cache)
|
||||
{
|
||||
return base(cache);
|
||||
}
|
||||
|
||||
int oakrender_cache_set_timebase(OakRenderCache cache, int num, int den)
|
||||
{
|
||||
if (!cache.ctx || num <= 0 || den <= 0) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "color/colormanager/colormanager.h"
|
||||
#include "filefunctions.h"
|
||||
#include "lutlibrary.h"
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
|
||||
namespace
|
||||
@@ -105,6 +106,129 @@ int oakrender_color_processor_is_valid(OakColorProcessor processor)
|
||||
return p && p->ptr && p->ptr->get_processor() ? 1 : 0;
|
||||
}
|
||||
|
||||
OakColorProcessor oakrender_color_processor_create_transform(
|
||||
OakNodeColorManager manager, const char *input,
|
||||
OakColorTransform dest, int direction)
|
||||
{
|
||||
olive::ColorManager *native_manager =
|
||||
oaknode_colormanager_get_native(manager);
|
||||
const olive::ColorTransform *native_dest =
|
||||
oakcommon_colortransform_get_native(dest);
|
||||
if (!native_manager || !input || !*input || !native_dest) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
if (direction != OAKRENDER_COLOR_DIRECTION_NORMAL &&
|
||||
direction != OAKRENDER_COLOR_DIRECTION_INVERSE) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
try {
|
||||
auto *impl = new OakColorProcessorImpl;
|
||||
impl->ptr = olive::ColorProcessor::create(
|
||||
native_manager, input, *native_dest,
|
||||
direction == OAKRENDER_COLOR_DIRECTION_NORMAL ?
|
||||
olive::ColorProcessor::k_normal :
|
||||
olive::ColorProcessor::k_inverse);
|
||||
return oakrender_c_api::make_handle<OakColorProcessor>(
|
||||
impl, true, &oakrender_c_api::delete_as<OakColorProcessorImpl>);
|
||||
} catch (...) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
}
|
||||
|
||||
OakColorProcessor oakrender_color_processor_create_lut(
|
||||
OakNodeColorManager manager, const char *path, int direction)
|
||||
{
|
||||
olive::ColorManager *native_manager =
|
||||
oaknode_colormanager_get_native(manager);
|
||||
if (!native_manager || !path || !*path) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
if (direction != OAKRENDER_COLOR_DIRECTION_NORMAL &&
|
||||
direction != OAKRENDER_COLOR_DIRECTION_INVERSE) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
try {
|
||||
OCIO_NAMESPACE::FileTransformRcPtr transform =
|
||||
OCIO_NAMESPACE::FileTransform::Create();
|
||||
transform->setSrc(path);
|
||||
transform->setInterpolation(OCIO_NAMESPACE::INTERP_LINEAR);
|
||||
transform->setDirection(
|
||||
direction == OAKRENDER_COLOR_DIRECTION_NORMAL ?
|
||||
OCIO_NAMESPACE::TRANSFORM_DIR_FORWARD :
|
||||
OCIO_NAMESPACE::TRANSFORM_DIR_INVERSE);
|
||||
|
||||
auto *impl = new OakColorProcessorImpl;
|
||||
impl->ptr = olive::ColorProcessor::create(
|
||||
native_manager->get_config()->getProcessor(transform));
|
||||
return oakrender_c_api::make_handle<OakColorProcessor>(
|
||||
impl, true, &oakrender_c_api::delete_as<OakColorProcessorImpl>);
|
||||
} catch (...) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
}
|
||||
|
||||
OakColorProcessor oakrender_color_processor_create_grading_primary(
|
||||
OakNodeColorManager manager, int style)
|
||||
{
|
||||
olive::ColorManager *native_manager =
|
||||
oaknode_colormanager_get_native(manager);
|
||||
if (!native_manager || (style != OAKRENDER_GRADING_PRIMARY_LIN &&
|
||||
style != OAKRENDER_GRADING_PRIMARY_LOG)) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
try {
|
||||
OCIO_NAMESPACE::GradingPrimaryTransformRcPtr gp =
|
||||
OCIO_NAMESPACE::GradingPrimaryTransform::Create(
|
||||
style == OAKRENDER_GRADING_PRIMARY_LIN ?
|
||||
OCIO_NAMESPACE::GRADING_LIN :
|
||||
OCIO_NAMESPACE::GRADING_LOG);
|
||||
gp->makeDynamic();
|
||||
gp->setDirection(
|
||||
OCIO_NAMESPACE::TransformDirection::TRANSFORM_DIR_FORWARD);
|
||||
|
||||
auto *impl = new OakColorProcessorImpl;
|
||||
impl->ptr = olive::ColorProcessor::create(
|
||||
native_manager->get_config()->getProcessor(gp));
|
||||
return oakrender_c_api::make_handle<OakColorProcessor>(
|
||||
impl, true, &oakrender_c_api::delete_as<OakColorProcessorImpl>);
|
||||
} catch (...) {
|
||||
return OakColorProcessor{};
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<olive::ColorProcessor> oakrender_color_processor_get_native(
|
||||
OakColorProcessor processor)
|
||||
{
|
||||
OakColorProcessorImpl *p =
|
||||
oakrender_c_api::to_native<OakColorProcessorImpl>(processor);
|
||||
return p ? p->ptr : nullptr;
|
||||
}
|
||||
|
||||
int oakrender_lut_is_supported_extension(const char *extension)
|
||||
{
|
||||
if (!extension) {
|
||||
return 0;
|
||||
}
|
||||
return olive::LUTLibrary::is_supported_extension(extension) ? 1 : 0;
|
||||
}
|
||||
|
||||
int oakrender_lut_supported_extensions_count(void)
|
||||
{
|
||||
return static_cast<int>(
|
||||
olive::LUTLibrary::supported_extensions().size());
|
||||
}
|
||||
|
||||
int oakrender_lut_supported_extension_at(int index, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
const std::vector<std::string> &all =
|
||||
olive::LUTLibrary::supported_extensions();
|
||||
if (index < 0 || index >= static_cast<int>(all.size())) {
|
||||
return OAKRENDER_E_NOT_FOUND;
|
||||
}
|
||||
return write_string(all[size_t(index)], buf, buf_size);
|
||||
}
|
||||
|
||||
int oakrender_color_processor_convert(OakColorProcessor processor,
|
||||
double ir, double ig, double ib,
|
||||
double ia, double *out_r, double *out_g,
|
||||
|
||||
@@ -161,6 +161,22 @@ int oakrender_cancel_request(int64_t request_id)
|
||||
return OAKRENDER_OK;
|
||||
}
|
||||
|
||||
int oakrender_manager_available(void)
|
||||
{
|
||||
return olive::RenderManager::instance() != nullptr ? 1 : 0;
|
||||
}
|
||||
|
||||
void oakrender_cancel_video_tasks(int wait_for_done)
|
||||
{
|
||||
olive::RenderManager *manager = olive::RenderManager::instance();
|
||||
if (!manager) {
|
||||
return;
|
||||
}
|
||||
if (olive::PreviewAutoCacher *cacher = manager->get_cacher()) {
|
||||
cacher->cancel_video_tasks(wait_for_done != 0);
|
||||
}
|
||||
}
|
||||
|
||||
int oakrender_set_cacher_multicam(OakNodeNode multicam_or_NULL)
|
||||
{
|
||||
olive::RenderManager *manager = olive::RenderManager::instance();
|
||||
|
||||
@@ -149,9 +149,11 @@ OakRenderTicket oakrender_ticket_render_frame(
|
||||
}
|
||||
}
|
||||
|
||||
if (params->cache) {
|
||||
rvp.add_cache(reinterpret_cast<olive::FrameHashCache *>(
|
||||
params->cache));
|
||||
if (params->cache.ctx) {
|
||||
if (auto *fc = dynamic_cast<olive::FrameHashCache *>(
|
||||
oakrender_cache_get_native(params->cache))) {
|
||||
rvp.add_cache(fc);
|
||||
}
|
||||
}
|
||||
|
||||
auto *watcher = new olive::RenderTicketWatcher();
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "inputdragger.h"
|
||||
#include "input/multicam/multicamnode.h"
|
||||
#include "playbackcache.h"
|
||||
#include "render/cache.h"
|
||||
#include "project.h"
|
||||
#include "qtutils.h"
|
||||
#include "rendermanager.h"
|
||||
@@ -338,10 +339,12 @@ void PreviewAutoCacher::connect_to_node_cache(Node *node)
|
||||
return;
|
||||
}
|
||||
|
||||
PlaybackCache *video_cache = node->video_frame_cache();
|
||||
PlaybackCache *thumb_cache = node->thumbnail_cache();
|
||||
PlaybackCache *audio_cache = node->audio_playback_cache();
|
||||
PlaybackCache *wave_cache = node->waveform_cache();
|
||||
// The caches live behind oakrender handles on the node; unwrap the
|
||||
// natives for the in-module callback wiring.
|
||||
PlaybackCache *video_cache = oakrender_cache_get_native(node->video_frame_cache());
|
||||
PlaybackCache *thumb_cache = oakrender_cache_get_native(node->thumbnail_cache());
|
||||
PlaybackCache *audio_cache = oakrender_cache_get_native(node->audio_playback_cache());
|
||||
PlaybackCache *wave_cache = oakrender_cache_get_native(node->waveform_cache());
|
||||
|
||||
video_cache->set_requested_callback(
|
||||
[this, video_cache](ViewerOutput *context, const TimeRange &range) {
|
||||
@@ -369,21 +372,26 @@ void PreviewAutoCacher::connect_to_node_cache(Node *node)
|
||||
audio_cache->set_cancel_all_callback(
|
||||
[this, audio_cache]() { cancel_for_cache(audio_cache); });
|
||||
|
||||
node->video_frame_cache()->resignal_requests();
|
||||
node->thumbnail_cache()->resignal_requests();
|
||||
node->audio_playback_cache()->resignal_requests();
|
||||
node->waveform_cache()->resignal_requests();
|
||||
video_cache->resignal_requests();
|
||||
thumb_cache->resignal_requests();
|
||||
audio_cache->resignal_requests();
|
||||
wave_cache->resignal_requests();
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::disconnect_from_node_cache(Node *node)
|
||||
{
|
||||
node->video_frame_cache()->set_requested_callback(nullptr);
|
||||
node->thumbnail_cache()->set_requested_callback(nullptr);
|
||||
node->audio_playback_cache()->set_requested_callback(nullptr);
|
||||
node->waveform_cache()->set_requested_callback(nullptr);
|
||||
PlaybackCache *video_cache = oakrender_cache_get_native(node->video_frame_cache());
|
||||
PlaybackCache *thumb_cache = oakrender_cache_get_native(node->thumbnail_cache());
|
||||
PlaybackCache *audio_cache = oakrender_cache_get_native(node->audio_playback_cache());
|
||||
PlaybackCache *wave_cache = oakrender_cache_get_native(node->waveform_cache());
|
||||
|
||||
node->video_frame_cache()->set_cancel_all_callback(nullptr);
|
||||
node->audio_playback_cache()->set_cancel_all_callback(nullptr);
|
||||
video_cache->set_requested_callback(nullptr);
|
||||
thumb_cache->set_requested_callback(nullptr);
|
||||
audio_cache->set_requested_callback(nullptr);
|
||||
wave_cache->set_requested_callback(nullptr);
|
||||
|
||||
video_cache->set_cancel_all_callback(nullptr);
|
||||
audio_cache->set_cancel_all_callback(nullptr);
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::cancel_queued_single_frame_render()
|
||||
@@ -816,7 +824,9 @@ void PreviewAutoCacher::force_cache_range(ViewerOutput *context,
|
||||
custom_autocache_range_ = range;
|
||||
|
||||
// Re-hash these frames and start rendering
|
||||
start_caching_video_range(context, context->video_frame_cache(), range);
|
||||
start_caching_video_range(
|
||||
context, oakrender_cache_get_native(context->video_frame_cache()),
|
||||
range);
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::project_destroyed()
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include "configaccessor.h"
|
||||
#include "colorprocessorcache.h"
|
||||
#include "framehashcache.h"
|
||||
#include "output/viewer/viewer.h"
|
||||
#include "project.h"
|
||||
#include "traverser.h"
|
||||
|
||||
@@ -22,6 +22,12 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "node/node.h"
|
||||
#include "node/project.h"
|
||||
#include "node/sequence.h"
|
||||
|
||||
TEST(OakRenderCacheTest, CreateFree)
|
||||
{
|
||||
const int alive_before = oakrender_debug_alive_count();
|
||||
@@ -186,3 +192,201 @@ TEST(OakRenderCacheTest, AliveCountReturnsToBaseline)
|
||||
oakrender_cache_free(&b);
|
||||
EXPECT_EQ(oakrender_debug_alive_count(), alive_before);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Minimal project+sequence fixture for node-owned caches.
|
||||
*/
|
||||
class NodeCacheFixture : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
project_ = oaknode_project_init();
|
||||
ASSERT_NE(project_.ctx, nullptr);
|
||||
sequence_ = oaknode_sequence_create();
|
||||
ASSERT_NE(sequence_.ctx, nullptr);
|
||||
node_ = oaknode_sequence_as_node(sequence_);
|
||||
OakNodeProject owner = {};
|
||||
if (oaknode_node_get_project(node_, &owner) != OAKNODE_OK ||
|
||||
!owner.ctx) {
|
||||
ASSERT_EQ(oaknode_project_add_node(project_, node_),
|
||||
OAKNODE_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
oaknode_project_free(&project_);
|
||||
}
|
||||
|
||||
OakNodeProject project_ = {};
|
||||
OakNodeSequence sequence_ = {};
|
||||
OakNodeNode node_ = {};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(NodeCacheFixture, CreateForNodeAllKinds)
|
||||
{
|
||||
const int alive_before = oakrender_debug_alive_count();
|
||||
const int kinds[] = { OAKRENDER_CACHE_VIDEO_FRAME,
|
||||
OAKRENDER_CACHE_THUMBNAIL,
|
||||
OAKRENDER_CACHE_AUDIO_PLAYBACK,
|
||||
OAKRENDER_CACHE_AUDIO_WAVEFORM };
|
||||
for (int kind : kinds) {
|
||||
OakRenderCache c = oakrender_cache_create_for_node(node_, kind);
|
||||
ASSERT_NE(c.ctx, nullptr) << "kind " << kind;
|
||||
oakrender_cache_free(&c);
|
||||
}
|
||||
EXPECT_EQ(oakrender_debug_alive_count(), alive_before);
|
||||
|
||||
EXPECT_EQ(oakrender_cache_create_for_node(OakNodeNode{},
|
||||
OAKRENDER_CACHE_VIDEO_FRAME)
|
||||
.ctx,
|
||||
nullptr);
|
||||
EXPECT_EQ(oakrender_cache_create_for_node(node_, -1).ctx, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(NodeCacheFixture, UuidRoundTripTwoStage)
|
||||
{
|
||||
OakRenderCache c =
|
||||
oakrender_cache_create_for_node(node_, OAKRENDER_CACHE_VIDEO_FRAME);
|
||||
ASSERT_NE(c.ctx, nullptr);
|
||||
|
||||
EXPECT_EQ(oakrender_cache_set_uuid(c, "{test-uuid-1}"), OAKRENDER_OK);
|
||||
int needed = oakrender_cache_get_uuid(c, nullptr, 0);
|
||||
ASSERT_GT(needed, 0);
|
||||
std::vector<char> buf(static_cast<size_t>(needed));
|
||||
EXPECT_EQ(oakrender_cache_get_uuid(c, buf.data(), needed), needed);
|
||||
EXPECT_STREQ(buf.data(), "{test-uuid-1}");
|
||||
|
||||
EXPECT_EQ(oakrender_cache_set_uuid(OakRenderCache{}, "x"),
|
||||
OAKRENDER_E_INVALID);
|
||||
EXPECT_LT(oakrender_cache_get_uuid(OakRenderCache{}, nullptr, 0), 0);
|
||||
|
||||
oakrender_cache_free(&c);
|
||||
}
|
||||
|
||||
TEST_F(NodeCacheFixture, InvalidateRangeAndQuery)
|
||||
{
|
||||
OakRenderCache c =
|
||||
oakrender_cache_create_for_node(node_, OAKRENDER_CACHE_VIDEO_FRAME);
|
||||
ASSERT_NE(c.ctx, nullptr);
|
||||
|
||||
// A fresh cache has nothing validated: the whole query range is
|
||||
// reported invalidated regardless of prior invalidate() calls.
|
||||
oakrender_cache_invalidate_range(c, 1, 2, 3, 2);
|
||||
int n = oakrender_cache_get_invalidated_ranges(c, 0, 1, 2, 1, nullptr,
|
||||
0);
|
||||
ASSERT_EQ(n, 1);
|
||||
|
||||
std::vector<int64_t> ranges(4);
|
||||
EXPECT_EQ(oakrender_cache_get_invalidated_ranges(c, 0, 1, 2, 1,
|
||||
ranges.data(), 1),
|
||||
1);
|
||||
EXPECT_EQ(ranges[0], 0);
|
||||
EXPECT_EQ(ranges[1], 1);
|
||||
EXPECT_EQ(ranges[2], 2);
|
||||
EXPECT_EQ(ranges[3], 1);
|
||||
|
||||
oakrender_cache_invalidate_range(OakRenderCache{}, 0, 1, 1, 1); // no-op
|
||||
oakrender_cache_free(&c);
|
||||
}
|
||||
|
||||
TEST_F(NodeCacheFixture, PassthroughRoundTrip)
|
||||
{
|
||||
OakRenderCache a =
|
||||
oakrender_cache_create_for_node(node_, OAKRENDER_CACHE_VIDEO_FRAME);
|
||||
OakRenderCache b =
|
||||
oakrender_cache_create_for_node(node_, OAKRENDER_CACHE_VIDEO_FRAME);
|
||||
ASSERT_NE(a.ctx, nullptr);
|
||||
ASSERT_NE(b.ctx, nullptr);
|
||||
|
||||
EXPECT_EQ(oakrender_cache_set_passthrough(a, b), OAKRENDER_OK);
|
||||
// The passthrough list mirrors the other cache's validated ranges;
|
||||
// with none, the list stays empty but the link is established.
|
||||
EXPECT_GE(oakrender_cache_get_passthroughs(a, nullptr, 0), 0);
|
||||
|
||||
EXPECT_EQ(oakrender_cache_set_passthrough(a, OakRenderCache{}),
|
||||
OAKRENDER_E_INVALID);
|
||||
EXPECT_EQ(oakrender_cache_set_passthrough(OakRenderCache{}, b),
|
||||
OAKRENDER_E_INVALID);
|
||||
|
||||
oakrender_cache_free(&a);
|
||||
oakrender_cache_free(&b);
|
||||
}
|
||||
|
||||
TEST_F(NodeCacheFixture, TimebaseAndFilename)
|
||||
{
|
||||
OakRenderCache c =
|
||||
oakrender_cache_create_for_node(node_, OAKRENDER_CACHE_VIDEO_FRAME);
|
||||
ASSERT_NE(c.ctx, nullptr);
|
||||
|
||||
EXPECT_EQ(oakrender_cache_set_timebase(c, 1, 25), OAKRENDER_OK);
|
||||
int num = 0, den = 0;
|
||||
EXPECT_EQ(oakrender_cache_get_timebase(c, &num, &den), OAKRENDER_OK);
|
||||
EXPECT_EQ(num, 1);
|
||||
EXPECT_EQ(den, 25);
|
||||
|
||||
// A frame-hash cache reports a filename for a time
|
||||
int needed = oakrender_cache_get_valid_cache_filename(c, 1, 25,
|
||||
nullptr, 0);
|
||||
EXPECT_GT(needed, 0);
|
||||
|
||||
// ... but an audio cache is not a frame-hash cache
|
||||
OakRenderCache audio = oakrender_cache_create_for_node(
|
||||
node_, OAKRENDER_CACHE_AUDIO_PLAYBACK);
|
||||
ASSERT_NE(audio.ctx, nullptr);
|
||||
EXPECT_EQ(oakrender_cache_get_timebase(audio, &num, &den),
|
||||
OAKRENDER_E_INVALID);
|
||||
EXPECT_LT(oakrender_cache_get_valid_cache_filename(audio, 0, 1,
|
||||
nullptr, 0),
|
||||
0);
|
||||
|
||||
oakrender_cache_free(&audio);
|
||||
oakrender_cache_free(&c);
|
||||
}
|
||||
|
||||
TEST_F(NodeCacheFixture, SavingEnabledLockUnlockStateErrors)
|
||||
{
|
||||
OakRenderCache c = oakrender_cache_create_for_node(
|
||||
node_, OAKRENDER_CACHE_AUDIO_WAVEFORM);
|
||||
ASSERT_NE(c.ctx, nullptr);
|
||||
|
||||
EXPECT_EQ(oakrender_cache_set_saving_enabled(c, 0), OAKRENDER_OK);
|
||||
EXPECT_EQ(oakrender_cache_set_saving_enabled(OakRenderCache{}, 1),
|
||||
OAKRENDER_E_INVALID);
|
||||
|
||||
// Lock/unlock pair (no-op on empty handle)
|
||||
oakrender_cache_lock(c);
|
||||
oakrender_cache_unlock(c);
|
||||
oakrender_cache_lock(OakRenderCache{});
|
||||
oakrender_cache_unlock(OakRenderCache{});
|
||||
|
||||
EXPECT_EQ(oakrender_cache_load_state(OakRenderCache{}),
|
||||
OAKRENDER_E_INVALID);
|
||||
EXPECT_EQ(oakrender_cache_save_state(OakRenderCache{}),
|
||||
OAKRENDER_E_INVALID);
|
||||
|
||||
oakrender_cache_free(&c);
|
||||
}
|
||||
|
||||
TEST_F(NodeCacheFixture, RequestValidation)
|
||||
{
|
||||
OakRenderCache c =
|
||||
oakrender_cache_create_for_node(node_, OAKRENDER_CACHE_VIDEO_FRAME);
|
||||
ASSERT_NE(c.ctx, nullptr);
|
||||
|
||||
// The sequence node is a viewer: valid request
|
||||
EXPECT_EQ(oakrender_cache_request(c, node_, 0, 1, 1, 1), OAKRENDER_OK);
|
||||
|
||||
// Empty cache / empty context are rejected
|
||||
EXPECT_EQ(oakrender_cache_request(OakRenderCache{}, node_, 0, 1, 1, 1),
|
||||
OAKRENDER_E_INVALID);
|
||||
EXPECT_EQ(oakrender_cache_request(c, OakNodeNode{}, 0, 1, 1, 1),
|
||||
OAKRENDER_E_INVALID);
|
||||
|
||||
oakrender_cache_free(&c);
|
||||
}
|
||||
|
||||
@@ -190,3 +190,139 @@ TEST(OakRenderColorTest, DisplayTransformInvalidArgs)
|
||||
sizeof(buf)),
|
||||
OAKRENDER_E_NOT_FOUND);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Project + initialized color manager for processor factories.
|
||||
*/
|
||||
class ColorProcessorFactoryFixture : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
project_ = oaknode_project_init();
|
||||
ASSERT_NE(project_.ctx, nullptr);
|
||||
manager_ = oaknode_colormanager_init(project_);
|
||||
ASSERT_NE(manager_.ctx, nullptr);
|
||||
ASSERT_EQ(oaknode_colormanager_initialize(manager_), OAKNODE_OK);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
oaknode_colormanager_free(&manager_);
|
||||
oaknode_project_free(&project_);
|
||||
}
|
||||
|
||||
OakNodeProject project_ = {};
|
||||
OakNodeColorManager manager_ = {};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(ColorProcessorFactoryFixture, CreateTransformInvalidArgs)
|
||||
{
|
||||
OakColorTransform t =
|
||||
oakcommon_colortransform_init_display("sRGB", "sRGB OETF", "");
|
||||
ASSERT_NE(t.ctx, nullptr);
|
||||
|
||||
EXPECT_EQ(oakrender_color_processor_create_transform(
|
||||
OakNodeColorManager{}, "Linear", t,
|
||||
OAKRENDER_COLOR_DIRECTION_NORMAL)
|
||||
.ctx,
|
||||
nullptr);
|
||||
EXPECT_EQ(oakrender_color_processor_create_transform(
|
||||
manager_, nullptr, t, OAKRENDER_COLOR_DIRECTION_NORMAL)
|
||||
.ctx,
|
||||
nullptr);
|
||||
EXPECT_EQ(oakrender_color_processor_create_transform(
|
||||
manager_, "Linear", OakColorTransform{},
|
||||
OAKRENDER_COLOR_DIRECTION_NORMAL)
|
||||
.ctx,
|
||||
nullptr);
|
||||
EXPECT_EQ(oakrender_color_processor_create_transform(
|
||||
manager_, "Linear", t, 42)
|
||||
.ctx,
|
||||
nullptr);
|
||||
|
||||
oakcommon_colortransform_free(&t);
|
||||
}
|
||||
|
||||
TEST_F(ColorProcessorFactoryFixture, CreateTransformHappyPath)
|
||||
{
|
||||
OakColorTransform t =
|
||||
oakcommon_colortransform_init_display("sRGB", "sRGB OETF", "");
|
||||
ASSERT_NE(t.ctx, nullptr);
|
||||
|
||||
const int alive_before = oakrender_debug_alive_count();
|
||||
OakColorProcessor p = oakrender_color_processor_create_transform(
|
||||
manager_, "Linear", t, OAKRENDER_COLOR_DIRECTION_NORMAL);
|
||||
ASSERT_NE(p.ctx, nullptr);
|
||||
EXPECT_EQ(oakrender_color_processor_is_valid(p), 1);
|
||||
EXPECT_NE(oakrender_color_processor_get_native(p), nullptr);
|
||||
|
||||
oakrender_color_processor_free(&p);
|
||||
EXPECT_EQ(oakrender_debug_alive_count(), alive_before);
|
||||
oakcommon_colortransform_free(&t);
|
||||
|
||||
EXPECT_TRUE(oakrender_color_processor_get_native(OakColorProcessor{}) ==
|
||||
nullptr);
|
||||
}
|
||||
|
||||
TEST_F(ColorProcessorFactoryFixture, CreateGradingPrimary)
|
||||
{
|
||||
OakColorProcessor p = oakrender_color_processor_create_grading_primary(
|
||||
manager_, OAKRENDER_GRADING_PRIMARY_LIN);
|
||||
ASSERT_NE(p.ctx, nullptr);
|
||||
EXPECT_EQ(oakrender_color_processor_is_valid(p), 1);
|
||||
oakrender_color_processor_free(&p);
|
||||
|
||||
EXPECT_EQ(oakrender_color_processor_create_grading_primary(manager_, 9)
|
||||
.ctx,
|
||||
nullptr);
|
||||
EXPECT_EQ(oakrender_color_processor_create_grading_primary(
|
||||
OakNodeColorManager{}, OAKRENDER_GRADING_PRIMARY_LOG)
|
||||
.ctx,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
TEST_F(ColorProcessorFactoryFixture, CreateLutErrorPaths)
|
||||
{
|
||||
// Nonexistent LUT file fails cleanly
|
||||
EXPECT_EQ(oakrender_color_processor_create_lut(
|
||||
manager_, "/nonexistent/never.cube",
|
||||
OAKRENDER_COLOR_DIRECTION_NORMAL)
|
||||
.ctx,
|
||||
nullptr);
|
||||
EXPECT_EQ(oakrender_color_processor_create_lut(
|
||||
OakNodeColorManager{}, "x.cube",
|
||||
OAKRENDER_COLOR_DIRECTION_NORMAL)
|
||||
.ctx,
|
||||
nullptr);
|
||||
EXPECT_EQ(oakrender_color_processor_create_lut(
|
||||
manager_, nullptr, OAKRENDER_COLOR_DIRECTION_NORMAL)
|
||||
.ctx,
|
||||
nullptr);
|
||||
EXPECT_EQ(oakrender_color_processor_create_lut(manager_, "x.cube", 7)
|
||||
.ctx,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
TEST(OakRenderLutTest, SupportedExtensions)
|
||||
{
|
||||
int count = oakrender_lut_supported_extensions_count();
|
||||
ASSERT_GT(count, 0);
|
||||
|
||||
char ext[32];
|
||||
ASSERT_GT(oakrender_lut_supported_extension_at(0, ext, sizeof(ext)),
|
||||
0);
|
||||
EXPECT_EQ(oakrender_lut_is_supported_extension(ext), 1);
|
||||
|
||||
EXPECT_EQ(oakrender_lut_is_supported_extension("definitely-not-a-lut"),
|
||||
0);
|
||||
EXPECT_EQ(oakrender_lut_is_supported_extension(nullptr), 0);
|
||||
EXPECT_LT(oakrender_lut_supported_extension_at(-1, ext, sizeof(ext)),
|
||||
0);
|
||||
EXPECT_LT(oakrender_lut_supported_extension_at(count, ext, sizeof(ext)),
|
||||
0);
|
||||
}
|
||||
|
||||
@@ -129,3 +129,18 @@ TEST(OakRenderDiskCacheTest, SizeAndClear)
|
||||
// Clearing an existing (possibly empty) default folder succeeds.
|
||||
EXPECT_EQ(oakrender_disk_cache_clear(), OAKRENDER_OK);
|
||||
}
|
||||
|
||||
TEST(OakRenderManagerTest, AvailableReturnsBool)
|
||||
{
|
||||
int available = oakrender_manager_available();
|
||||
EXPECT_TRUE(available == 0 || available == 1);
|
||||
}
|
||||
|
||||
TEST(OakRenderManagerTest, CancelVideoTasksWithoutManagerIsNoOp)
|
||||
{
|
||||
if (oakrender_manager_available()) {
|
||||
GTEST_SKIP() << "manager exists in this process";
|
||||
}
|
||||
oakrender_cancel_video_tasks(0);
|
||||
oakrender_cancel_video_tasks(1);
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ bool ExportTask::run()
|
||||
}
|
||||
|
||||
render(color_manager_, video_range, audio_range, subtitle_range,
|
||||
0 /* RenderMode::k_online */, nullptr, force);
|
||||
0 /* RenderMode::k_online */, {}, force);
|
||||
|
||||
bool success = true;
|
||||
|
||||
|
||||
@@ -151,11 +151,10 @@ bool PreCacheTask::run()
|
||||
intersection = TimeRange(Rational(0), video_length);
|
||||
}
|
||||
|
||||
OakNodeFrameCache *cache = nullptr;
|
||||
oaknode_node_get_video_frame_cache(viewer(), &cache);
|
||||
|
||||
// Borrowed wrapper: the cache stays owned by the viewer node.
|
||||
OakRenderCache cache_handle = oakrender_cache_wrap_borrowed(cache);
|
||||
// Addref'd handle: the cache itself stays owned by the viewer node,
|
||||
// this box is released at the end of the scope.
|
||||
OakRenderCache cache_handle = {};
|
||||
oaknode_node_get_video_frame_cache(viewer(), &cache_handle);
|
||||
|
||||
int range_count = oakrender_cache_get_invalidated_ranges(
|
||||
cache_handle,
|
||||
@@ -183,7 +182,7 @@ bool PreCacheTask::run()
|
||||
oaknode_colormanager_init(project_);
|
||||
|
||||
render(color_manager, video_range, TimeRangeList(), TimeRange(),
|
||||
0 /* RenderMode::k_online */, cache, ForceParams());
|
||||
0 /* RenderMode::k_online */, cache_handle, ForceParams());
|
||||
|
||||
oaknode_colormanager_free(&color_manager);
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ void RenderTask::on_ticket_finished(OakRenderTicket ticket)
|
||||
|
||||
bool RenderTask::start_video_ticket(OakNodeColorManager manager,
|
||||
const Rational &time, int mode,
|
||||
OakNodeFrameCache *cache,
|
||||
OakRenderCache cache,
|
||||
const ForceParams &force)
|
||||
{
|
||||
OakNodeNode output_node = {};
|
||||
@@ -136,7 +136,7 @@ bool RenderTask::render(OakNodeColorManager manager,
|
||||
const TimeRangeList &video_range,
|
||||
const TimeRangeList &audio_range,
|
||||
const TimeRange &subtitle_range, int render_mode,
|
||||
OakNodeFrameCache *cache, const ForceParams &force)
|
||||
OakRenderCache cache, const ForceParams &force)
|
||||
{
|
||||
oakrender_manager_set_aggressive_gc(1);
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ protected:
|
||||
const TimeRangeList &video_range,
|
||||
const TimeRangeList &audio_range,
|
||||
const TimeRange &subtitle_range, int render_mode,
|
||||
OakNodeFrameCache *cache,
|
||||
OakRenderCache cache,
|
||||
const ForceParams &force);
|
||||
|
||||
virtual bool download_frame(OakCodecFrame frame, const Rational &time);
|
||||
@@ -164,7 +164,7 @@ private:
|
||||
|
||||
bool start_video_ticket(OakNodeColorManager manager,
|
||||
const Rational &time, int mode,
|
||||
OakNodeFrameCache *cache,
|
||||
OakRenderCache cache,
|
||||
const ForceParams &force);
|
||||
|
||||
OakNodeNode viewer_;
|
||||
|
||||
Reference in New Issue
Block a user