From 8ed5660faf0532b230f02a9c276226d90eabd0cd Mon Sep 17 00:00:00 2001 From: Mike Solar Date: Mon, 5 Jan 2026 18:01:13 +0800 Subject: [PATCH] solve some bugs --- app/codec/decoder.cpp | 5 ++ app/main.cpp | 8 ++- app/node/factory.cpp | 9 ++- app/node/plugins/Plugin.cpp | 39 +++++++----- app/pluginSupport/OliveClip.cpp | 10 ++- app/pluginSupport/OliveHost.cpp | 19 +++--- app/pluginSupport/OliveHost.h | 8 +++ app/pluginSupport/OlivePluginInstance.h | 3 +- app/render/opengl/openglrenderer.h | 5 ++ app/render/plugin/pluginrenderer.cpp | 61 +++++++++---------- app/render/plugin/pluginrenderer.h | 3 +- app/render/previewautocacher.cpp | 32 +++++++--- app/render/previewautocacher.h | 1 + app/render/renderprocessor.cpp | 40 ++++++++++++ app/render/renderprocessor.h | 11 ++++ app/widget/viewer/viewer.cpp | 8 ++- .../HostSupport/src/ofxhImageEffectAPI.cpp | 20 +++++- 17 files changed, 203 insertions(+), 79 deletions(-) diff --git a/app/codec/decoder.cpp b/app/codec/decoder.cpp index d15fb8ce4..83d6f82af 100644 --- a/app/codec/decoder.cpp +++ b/app/codec/decoder.cpp @@ -140,6 +140,11 @@ Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, return kInvalid; } + if (params.sample_rate() <= 0 || params.channel_count() <= 0) { + qWarning() << "Invalid audio parameters, skipping audio retrieve"; + return kInvalid; + } + // Get conform state from ConformManager ConformManager::Conform conform = ConformManager::instance()->GetConformState( diff --git a/app/main.cpp b/app/main.cpp index 93586d3f5..2f779ecf8 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -287,9 +287,7 @@ int main(int argc, char *argv[]) } } - if (!no_plugin->IsSet()) { - olive::plugin::loadPlugins("plugins"); - } + const bool load_plugins = !no_plugin->IsSet(); if (crash_option->IsSet()) { startup_params.set_crash_on_startup(true); @@ -341,6 +339,10 @@ int main(int argc, char *argv[]) gui_app->setWindowIcon(QIcon(QStringLiteral(":/graphics/oak-logo.png"))); } + if (load_plugins) { + olive::plugin::loadPlugins("plugins"); + } + #ifdef _WIN32 // On Windows, users seem to frequently run into a crash caused by their graphics driver not // supporting framebuffers, which we require. I personally have only been able to recreate this diff --git a/app/node/factory.cpp b/app/node/factory.cpp index bbab2f3bb..9f351fcd6 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -88,7 +88,6 @@ void NodeFactory::Initialize() library_.append(created_node); } - olive::plugin::loadPlugins(QString()); RegisterPluginNodes(); } @@ -237,9 +236,13 @@ void NodeFactory::RegisterPluginNodes() } const auto &contexts = image_effect->getContexts(); + if (contexts.empty()) { + qWarning() << "Skipping OFX plugin with no contexts:" + << plugin_id; + continue; + } std::string context = kOfxImageEffectContextFilter; - if (!contexts.empty() && - contexts.find(kOfxImageEffectContextFilter) == contexts.end()) { + if (contexts.find(kOfxImageEffectContextFilter) == contexts.end()) { context = *contexts.begin(); } diff --git a/app/node/plugins/Plugin.cpp b/app/node/plugins/Plugin.cpp index 889cc8247..289924844 100644 --- a/app/node/plugins/Plugin.cpp +++ b/app/node/plugins/Plugin.cpp @@ -20,6 +20,7 @@ #include "render/rendermanager.h" #include "render/job/pluginjob.h" +#include "pluginSupport/OlivePluginInstance.h" static QString ClipLabelForName(const std::string &name, const OFX::Host::ImageEffect::ClipDescriptor *desc) { @@ -222,22 +223,30 @@ QString olive::plugin::PluginNode::id() const return plugin->getIdentifier().data(); } -olive::Node *olive::plugin::PluginNode::copy() const -{ - auto *node = new PluginNode(new OlivePluginInstance(*plugin_instance_)); - if (!plugin_instance_) { - return node; - } + olive::Node *olive::plugin::PluginNode::copy() const + { + if (!plugin_instance_) { + return nullptr; + } - const auto &contexts = plugin_instance_->getPlugin()->getContexts(); - std::string context = kOfxImageEffectContextFilter; - if (!contexts.empty()) { - if (contexts.find(kOfxImageEffectContextFilter) == contexts.end()) { + const auto &contexts = plugin_instance_->getPlugin()->getContexts(); + std::string context = kOfxImageEffectContextFilter; + if (!contexts.empty() && + contexts.find(kOfxImageEffectContextFilter) == contexts.end()) { context = *contexts.begin(); } - } - node->setPluginInstance( - plugin_instance_->getPlugin()->createInstance(context, node)); - return node; -} + auto *instance = + plugin_instance_->getPlugin()->createInstance(context, nullptr); + if (!instance) { + return nullptr; + } + + auto *node = new PluginNode(instance); + if (auto *olive_instance = + dynamic_cast(instance)) { + olive_instance->setNode( + std::shared_ptr(node, [](PluginNode *) {})); + } + return node; + } diff --git a/app/pluginSupport/OliveClip.cpp b/app/pluginSupport/OliveClip.cpp index eef3d0a05..d03e44efb 100644 --- a/app/pluginSupport/OliveClip.cpp +++ b/app/pluginSupport/OliveClip.cpp @@ -124,7 +124,15 @@ const std::string &olive::plugin::OliveClipInstance::getFieldOrder() const } bool olive::plugin::OliveClipInstance::getConnected() const { - return params_.format() == PixelFormat::INVALID; + if (name_ == kOfxImageEffectOutputClipName) { + return true; + } +#ifdef OFX_SUPPORTS_OPENGLRENDER + if (!input_textures_.isEmpty()) { + return true; + } +#endif + return !images_.isEmpty(); } double olive::plugin::OliveClipInstance::getUnmappedFrameRate() const { diff --git a/app/pluginSupport/OliveHost.cpp b/app/pluginSupport/OliveHost.cpp index 5cf6750a8..5a9b43f27 100644 --- a/app/pluginSupport/OliveHost.cpp +++ b/app/pluginSupport/OliveHost.cpp @@ -71,15 +71,20 @@ void AddPluginPathsFromEnv(OFX::Host::PluginCache *cache, const char *env_var) void olive::plugin::loadPlugins(QString path) { - std::shared_ptr host = std::make_shared(); - Current::getInstance().setPluginHost(host); - + std::shared_ptr host = Current::getInstance().pluginHost(); std::shared_ptr imageEffectPluginCache = - std::make_shared(*host); - Current::getInstance().setPluginCache(imageEffectPluginCache); + Current::getInstance().pluginCache(); - imageEffectPluginCache->registerInCache( - *OFX::Host::PluginCache::getPluginCache()); + if (!host || !imageEffectPluginCache) { + host = std::make_shared(); + Current::getInstance().setPluginHost(host); + + imageEffectPluginCache = std::make_shared(*host); + Current::getInstance().setPluginCache(imageEffectPluginCache); + + imageEffectPluginCache->registerInCache( + *OFX::Host::PluginCache::getPluginCache()); + } OFX::Host::PluginCache *cache = OFX::Host::PluginCache::getPluginCache(); cache->setPluginHostPath("Olive"); diff --git a/app/pluginSupport/OliveHost.h b/app/pluginSupport/OliveHost.h index 951b83716..ce8615ef2 100644 --- a/app/pluginSupport/OliveHost.h +++ b/app/pluginSupport/OliveHost.h @@ -53,6 +53,14 @@ public: bool pluginSupported(OFX::Host::ImageEffect::ImageEffectPlugin *plugin, std::string &reason) const override { + if (!plugin) { + reason = "null plugin"; + return false; + } + if (plugin->getContexts().empty()) { + reason = "no supported contexts (describe failed)"; + return false; + } return true; }; diff --git a/app/pluginSupport/OlivePluginInstance.h b/app/pluginSupport/OlivePluginInstance.h index 837bdddd9..84ce60ebe 100644 --- a/app/pluginSupport/OlivePluginInstance.h +++ b/app/pluginSupport/OlivePluginInstance.h @@ -53,7 +53,8 @@ public: { } OlivePluginInstance(OlivePluginInstance& instance) - : Instance(_plugin, *_descriptor, _context, _interactive) + : Instance(instance._plugin, *instance._descriptor, instance._context, + instance._interactive) { _clips=instance._clips; _created=instance._created; diff --git a/app/render/opengl/openglrenderer.h b/app/render/opengl/openglrenderer.h index 2bd01d8b5..2b355835c 100644 --- a/app/render/opengl/openglrenderer.h +++ b/app/render/opengl/openglrenderer.h @@ -68,6 +68,11 @@ public: virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override; + QOpenGLContext *context() const + { + return context_; + } + protected: virtual void Blit(QVariant shader, olive::AcceleratedJob& job, olive::Texture *destination, diff --git a/app/render/plugin/pluginrenderer.cpp b/app/render/plugin/pluginrenderer.cpp index 377f4a172..598a2882c 100644 --- a/app/render/plugin/pluginrenderer.cpp +++ b/app/render/plugin/pluginrenderer.cpp @@ -296,17 +296,6 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin:: if (olive_instance) { olive_instance->setOpenGLEnabled(use_opengl); } - OfxStatus stat; - stat = instance->createInstanceAction(); - if(stat != kOfxStatOK && stat != kOfxStatReplyDefault) { - return; - } - // now we need to to call getClipPreferences on the instance so that it does the clip component/depth - // logic and caches away the components and depth on each clip. - bool ok = instance->getClipPreferences(); - if (!ok) { - return; - } // current render scale of 1 OfxPointD renderScale; @@ -334,37 +323,22 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin:: int numFramesToRender=1; - stat = instance->beginRenderAction(0, numFramesToRender, - 1.0, false, renderScale, true, - interactive); - if (stat != kOfxStatOK && stat != kOfxStatReplyDefault) { - return; - } - -#ifdef OFX_SUPPORTS_OPENGLRENDER - if (use_opengl) { - instance->contextAttachedAction(); - AttachOutputTexture(destination); - } -#endif OliveClipInstance *clip=dynamic_cast(instance->getClip("Output")); if (!clip) { -#ifdef OFX_SUPPORTS_OPENGLRENDER - if (use_opengl) { - DetachOutputTexture(); - instance->contextDetachedAction(); - } -#endif - instance->endRenderAction(0, numFramesToRender, 1.0, interactive, renderScale, true,interactive - ); + return; + } + clip->setParams(destination_params); + + OfxStatus stat; + stat = instance->createInstanceAction(); + if(stat != kOfxStatOK && stat != kOfxStatReplyDefault) { return; } // call get region of interest on each of the inputs OfxTime frame = 0; - clip->setParams(destination_params); clip->setRegionOfDefinition(regionOfDefinition, frame); clip->setOutputTexture(destination, frame); @@ -388,6 +362,27 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin:: } } } + + // now we need to call getClipPreferences on the instance so that it does + // the clip component/depth logic and caches away the components and depth. + bool ok = instance->getClipPreferences(); + if (!ok) { + return; + } + + stat = instance->beginRenderAction(0, numFramesToRender, + 1.0, false, renderScale, true, + interactive); + if (stat != kOfxStatOK && stat != kOfxStatReplyDefault) { + return; + } + +#ifdef OFX_SUPPORTS_OPENGLRENDER + if (use_opengl) { + instance->contextAttachedAction(); + AttachOutputTexture(destination); + } +#endif // get the RoI for each input clip // the regions of interest for each input clip are returned in a std::map // on a real host, these will be the regions of each input clip that the diff --git a/app/render/plugin/pluginrenderer.h b/app/render/plugin/pluginrenderer.h index ac9d8ea5e..832b1a825 100644 --- a/app/render/plugin/pluginrenderer.h +++ b/app/render/plugin/pluginrenderer.h @@ -44,8 +44,7 @@ public: virtual ~PluginRenderer() override{}; void AttachOutputTexture(olive::Texture *texture); void DetachOutputTexture(); -protected: - virtual void RenderPlugin(TexturePtr src, olive::plugin::PluginJob& job, + void RenderPlugin(TexturePtr src, olive::plugin::PluginJob& job, olive::Texture *destination, olive::VideoParams destination_params, bool clear_destination, bool interactive); diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 906b953b7..6bd2601f3 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -393,8 +393,9 @@ void PreviewAutoCacher::StartCachingAudioRange(ViewerOutput *context, cache->ClearRequestRange(range); pending_audio_jobs_.push_back({ node, context, cache, range }); - audio_cache_data_[cache].job_tracker.insert(range, - copier_->GetGraphChangeTime()); + AudioCacheData &data = audio_cache_data_[cache]; + data.context = context; + data.job_tracker.insert(range, copier_->GetGraphChangeTime()); TryRender(); } @@ -675,6 +676,15 @@ RenderTicketPtr PreviewAutoCacher::RenderAudio(Node *node, running_audio_tasks_.append(watcher); AudioParams p = context->GetAudioParams(); + const bool invalid_params = + (p.sample_rate() <= 0 || p.channel_count() <= 0); + if (invalid_params) { + AudioParams fallback( + OLIVE_CONFIG("DefaultSequenceAudioFrequency").toInt(), + OLIVE_CONFIG("DefaultSequenceAudioLayout").toULongLong(), + ViewerOutput::kDefaultSampleFormat); + p = fallback; + } p.set_format(ViewerOutput::kDefaultSampleFormat); RenderManager::RenderAudioParams rap(node, r, p, RenderMode::kOffline); @@ -692,13 +702,17 @@ void PreviewAutoCacher::ConformFinished() // Got an audio conform, requeue all the audio currently needing a conform last_conform_task_.Acquire(); - qDebug() << "CONFORM RESPONSE TEMPORARILY DISABLED"; - /*for (auto it=audio_cache_data_.begin(); it!=audio_cache_data_.end(); it++) { - foreach (const TimeRange &range, it.value().needs_conform) { - it.key()->Request(range); - } - it.value().needs_conform.clear(); - }*/ + for (auto it = audio_cache_data_.begin(); it != audio_cache_data_.end(); + it++) { + if (!it.key() || !it.value().context) { + continue; + } + + for (const TimeRange &range : it.value().needs_conform) { + it.key()->Request(it.value().context, range); + } + it.value().needs_conform.clear(); + } } void PreviewAutoCacher::CacheProxyTaskCancelled() diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index 352c36d4a..2bdb606c6 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -186,6 +186,7 @@ private: struct AudioCacheData { RenderJobTracker job_tracker; TimeRangeList needs_conform; + ViewerOutput *context = nullptr; }; std::list pending_video_jobs_; diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 4dd2008a1..10a298344 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -28,6 +28,8 @@ #include "node/block/transition/transition.h" #include "node/project.h" #include "rendermanager.h" +#include "render/opengl/openglrenderer.h" +#include "render/plugin/pluginrenderer.h" #include "pluginSupport/OliveClip.h" #include "pluginSupport/OliveHost.h" @@ -606,6 +608,44 @@ void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, destination->Upload(frame->data(), frame->linesize_pixels()); } +TexturePtr RenderProcessor::ProcessPluginJob(TexturePtr texture, + TexturePtr destination, + const Node *node) +{ + (void)node; + + if (!render_ctx_ || !texture || !destination) { + return destination; + } + + auto *plugin_job = + dynamic_cast(texture->job()); + if (!plugin_job) { + return destination; + } + + if (!plugin_renderer_) { + auto *gl = dynamic_cast(render_ctx_); + if (!gl || !gl->context()) { + return destination; + } + + plugin_renderer_ = std::make_unique(); + plugin_renderer_->Init(gl->context()); + plugin_renderer_->PostInit(); + } + + plugin_renderer_->RenderPlugin( + texture, + *plugin_job, + destination.get(), + destination->params(), + true, + false); + + return destination; +} + TexturePtr RenderProcessor::ProcessVideoCacheJob(const CacheJob *val) { FramePtr frame = FrameHashCache::LoadCacheFrame(val->GetFilename()); diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index 888a90f05..8a52009eb 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -20,6 +20,7 @@ #define RENDERPROCESSOR_H #include "node/block/clip/clip.h" +#include #include "node/traverser.h" #include "render/renderer.h" #include "rendercache.h" @@ -28,6 +29,10 @@ namespace olive { +namespace plugin { +class PluginRenderer; +} + class RenderProcessor : public NodeTraverser { public: virtual NodeValueDatabase GenerateDatabase(const Node *node, @@ -66,6 +71,10 @@ protected: const Node *node, const GenerateJob *job) override; + virtual TexturePtr ProcessPluginJob(TexturePtr texture, + TexturePtr destination, + const Node *node) override; + virtual TexturePtr ProcessVideoCacheJob(const CacheJob *val) override; virtual TexturePtr CreateTexture(const VideoParams &p) override; @@ -100,6 +109,8 @@ private: Renderer *render_ctx_; + std::unique_ptr plugin_renderer_; + DecoderCache *decoder_cache_; ShaderCache *shader_cache_; diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 64de6627c..2953558b5 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -571,10 +571,14 @@ void ViewerWidget::UpdateAudioProcessor() CloseAudioProcessor(); AudioParams ap = GetConnectedNode()->GetAudioParams(); + if (ap.sample_rate() <= 0 || ap.channel_count() <= 0) { + ap = AudioParams( + OLIVE_CONFIG("DefaultSequenceAudioFrequency").toInt(), + OLIVE_CONFIG("DefaultSequenceAudioLayout").toULongLong(), + ViewerOutput::kDefaultSampleFormat); + } ap.set_format(ViewerOutput::kDefaultSampleFormat); - uint64_t layout = - OLIVE_CONFIG("AudioOutputChannelLayout").toULongLong(); AudioParams packed( OLIVE_CONFIG("AudioOutputSampleRate").toInt(), OLIVE_CONFIG("AudioOutputChannelLayout").toULongLong(), diff --git a/third_party/openfx/HostSupport/src/ofxhImageEffectAPI.cpp b/third_party/openfx/HostSupport/src/ofxhImageEffectAPI.cpp index ffb68dbe3..9ce803c81 100755 --- a/third_party/openfx/HostSupport/src/ofxhImageEffectAPI.cpp +++ b/third_party/openfx/HostSupport/src/ofxhImageEffectAPI.cpp @@ -211,6 +211,9 @@ namespace OFX { OFX::Host::Property::Set inarg(inargspec); PluginHandle *ph = getPluginHandle(); + if (!ph) { + return nullptr; + } std::unique_ptr newContext( gImageEffectHost->makeDescriptor(getDescriptor(), this)); OfxStatus stat; @@ -238,7 +241,10 @@ namespace OFX { /// (not because we are expecting the results to change, but because plugin /// might get confused otherwise), then a describe_in_context - getPluginHandle(); + PluginHandle *ph = getPluginHandle(); + if (!ph) { + return nullptr; + } Descriptor *desc = getContext(context); @@ -322,7 +328,16 @@ namespace OFX { /// whether we support this plugin. bool PluginCache::pluginSupported(OFX::Host::Plugin *p, std::string &reason) const { - return gImageEffectHost->pluginSupported(dynamic_cast(p), reason); + if (!gImageEffectHost) { + reason = "host not initialized"; + return false; + } + auto *plugin = dynamic_cast(p); + if (!plugin) { + reason = "not an image effect plugin"; + return false; + } + return gImageEffectHost->pluginSupported(plugin, reason); } /// get the plugin by label. vermaj and vermin can be specified. if they are not it will @@ -589,4 +604,3 @@ namespace OFX { } // Host } // OFX -