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:
@@ -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