feat: Implement OFX plugin support infrastructure
This commit adds comprehensive support for OFX plugins in Olive, including: - Add plugin node infrastructure with getPlugin() method - Implement OliveHost for managing OFX plugin instances - Create OliveInstance to handle plugin parameter and timeline interactions - Add OliveClip implementation for plugin clip handling - Implement region of definition (RoD) and region of interest (RoI) functionality - Add interactive mode support in Current class - Integrate plugin rendering into RenderProcessor - Fix include dependencies and remove unused OlivePlugin.cpp reference
This commit is contained in:
@@ -51,6 +51,10 @@ public:
|
||||
{
|
||||
currentAudioParams_ = params;
|
||||
}
|
||||
bool interactive()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
static Current current;
|
||||
olive::VideoParams currentVideoParams_;
|
||||
|
||||
+1
-1
@@ -24,6 +24,7 @@
|
||||
* Use the navigation above to find documentation on classes or source files.
|
||||
*/
|
||||
|
||||
#include "OliveHost.h"
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavfilter/avfilter.h>
|
||||
@@ -52,7 +53,6 @@ extern "C" {
|
||||
#ifdef USE_CRASHPAD
|
||||
#include "common/crashpadinterface.h"
|
||||
#endif // USE_CRASHPAD
|
||||
#include "OlivePlugin.cpp"
|
||||
int decompress_project(const QString &project)
|
||||
{
|
||||
if (project.isEmpty()) {
|
||||
|
||||
@@ -1124,7 +1124,12 @@ public:
|
||||
|
||||
static const QString kEnabledInput;
|
||||
|
||||
std::shared_ptr<OFX::Host::ImageEffect::ImageEffectPlugin> getPlugin();
|
||||
protected:
|
||||
|
||||
// If is set, this is a plugin node.
|
||||
std::shared_ptr<OFX::Host::ImageEffect::ImageEffectPlugin> plugin;
|
||||
|
||||
void InsertInput(const QString &id, NodeValue::Type type,
|
||||
const QVariant &default_value, InputFlags flags,
|
||||
int index);
|
||||
|
||||
@@ -118,5 +118,28 @@ OFX::Host::ImageEffect::Image *
|
||||
olive::plugin::OliveClipInstance::getImage(OfxTime time,
|
||||
const OfxRectD *optionalBounds)
|
||||
{
|
||||
return &image_;
|
||||
}
|
||||
OfxRectD
|
||||
olive::plugin::OliveClipInstance::getRegionOfDefinition(OfxTime time) const
|
||||
{
|
||||
if (regionOfDefinitions_.contains(time)) {
|
||||
return regionOfDefinitions_[time];
|
||||
}
|
||||
OfxRectD regionOfDefinition;
|
||||
regionOfDefinition.x1=regionOfDefinition.y1=0;
|
||||
regionOfDefinition.x2=params_.width();
|
||||
regionOfDefinition.y2=params_.height();
|
||||
return regionOfDefinition;
|
||||
}
|
||||
void olive::plugin::OliveClipInstance::setRegionOfDefinition(
|
||||
OfxRectD regionOfDefinition, OfxTime time)
|
||||
{
|
||||
regionOfDefinitions_[time] = regionOfDefinition;
|
||||
}
|
||||
|
||||
void olive::plugin::OliveClipInstance::setDefaultRegionOfDefinition(
|
||||
OfxRectD regionOfDefinition)
|
||||
{
|
||||
defaultRegionOfDefinitions_ = regionOfDefinition;
|
||||
}
|
||||
@@ -25,6 +25,8 @@
|
||||
#define OLIVECLIP_H
|
||||
#include "ofxhClip.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
#include <QMap>
|
||||
namespace olive
|
||||
{
|
||||
namespace plugin
|
||||
@@ -37,6 +39,11 @@ public:
|
||||
{
|
||||
params_ = params;
|
||||
}
|
||||
OFX::Host::ImageEffect::Image& getOutputImage()
|
||||
{
|
||||
return image_;
|
||||
}
|
||||
|
||||
const std::string &getUnmappedBitDepth() const override;
|
||||
const std::string &getUnmappedComponents() const override;
|
||||
const std::string &getPremult() const override;
|
||||
@@ -50,10 +57,21 @@ public:
|
||||
bool getContinuousSamples() const override;
|
||||
OFX::Host::ImageEffect::Image* getImage(OfxTime time, const OfxRectD *optionalBounds) override;
|
||||
OfxRectD getRegionOfDefinition(OfxTime time) const override;
|
||||
const std::string &findSupportedComp(const std::string &s) const override;
|
||||
|
||||
void setRegionOfDefinition(OfxRectD regionOfDefinition, OfxTime time);
|
||||
void olive::plugin::OliveClipInstance::setDefaultRegionOfDefinition(
|
||||
OfxRectD regionOfDefinition);
|
||||
# ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
OFX::Host::ImageEffect::Texture* loadTexture(OfxTime time, const char *format, const OfxRectD *optionalBounds) { return NULL; };
|
||||
# endif
|
||||
private:
|
||||
VideoParams params_;
|
||||
|
||||
QMap<OfxTime, OfxRectD> regionOfDefinitions_;
|
||||
|
||||
OfxRectD defaultRegionOfDefinitions_;
|
||||
|
||||
OFX::Host::ImageEffect::Image image_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include "OliveHost.h"
|
||||
|
||||
#include "OliveInstance.h"
|
||||
#include "common/Current.h"
|
||||
using namespace OFX::Host;
|
||||
using namespace olive::plugin;
|
||||
void loadPlugins(QString path)
|
||||
@@ -79,7 +82,7 @@ OFX::Host::ImageEffect::Instance* OliveHost::newInstance(void* clientData,
|
||||
OFX::Host::ImageEffect::ImageEffectPlugin* plugin,
|
||||
OFX::Host::ImageEffect::Descriptor& desc,
|
||||
const std::string& context){
|
||||
//ImageEffect::Instance* instance=new ImageEffect::Instance(clientData,plugin,desc,context);
|
||||
ImageEffect::Instance* instance=new OliveInstance(plugin,desc,context,Current::getInstance().interactive());
|
||||
instances_.append(instance);
|
||||
return instance;
|
||||
};
|
||||
|
||||
@@ -147,6 +147,12 @@ void OliveInstance::getRenderScaleRecursive(double &x, double &y) const
|
||||
x = 1.0;
|
||||
y = 1.0;
|
||||
}
|
||||
OFX::Host::Param::Instance *
|
||||
OliveInstance::newParam(const std::string &name,
|
||||
OFX::Host::Param::Descriptor &Descriptor)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OFX::Host::ImageEffect::ClipInstance *OliveInstance::newClipInstance(
|
||||
OFX::Host::ImageEffect::Instance *plugin,
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "ofxImageEffect.h"
|
||||
#include <QString>
|
||||
#include "ofxhImageEffect.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
#include <map>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qlist.h>
|
||||
@@ -49,6 +51,10 @@ public:
|
||||
return kOfxImageFieldNone;
|
||||
};
|
||||
|
||||
void setVideoParam(VideoParams params)
|
||||
{
|
||||
this->params_=params;
|
||||
}
|
||||
OFX::Host::ImageEffect::ClipInstance *newClipInstance(
|
||||
OFX::Host::ImageEffect::Instance *plugin,
|
||||
OFX::Host::ImageEffect::ClipDescriptor *descriptor,
|
||||
@@ -88,9 +94,61 @@ public:
|
||||
/// renderScale
|
||||
void getRenderScaleRecursive(double &x, double &y) const override;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// overridden for Param::SetInstance
|
||||
|
||||
/// make a parameter instance
|
||||
///
|
||||
/// Client host code needs to implement this
|
||||
virtual OFX::Host::Param::Instance* newParam(const std::string& name, OFX::Host::Param::Descriptor& Descriptor);
|
||||
|
||||
/// Triggered when the plug-in calls OfxParameterSuiteV1::paramEditBegin
|
||||
///
|
||||
/// Client host code needs to implement this
|
||||
virtual OfxStatus editBegin(const std::string& name);
|
||||
|
||||
/// Triggered when the plug-in calls OfxParameterSuiteV1::paramEditEnd
|
||||
///
|
||||
/// Client host code needs to implement this
|
||||
virtual OfxStatus editEnd();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// overridden for Progress::ProgressI
|
||||
|
||||
/// Start doing progress.
|
||||
virtual void progressStart(const std::string &message, const std::string &messageid);
|
||||
|
||||
/// finish yer progress
|
||||
virtual void progressEnd();
|
||||
|
||||
/// set the progress to some level of completion, returns
|
||||
/// false if you should abandon processing, true to continue
|
||||
virtual bool progressUpdate(double t);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// overridden for TimeLine::TimeLineI
|
||||
|
||||
/// get the current time on the timeline. This is not necessarily the same
|
||||
/// time as being passed to an action (eg render)
|
||||
virtual double timeLineGetTime();
|
||||
|
||||
/// set the timeline to a specific time
|
||||
virtual void timeLineGotoTime(double t);
|
||||
|
||||
/// get the first and last times available on the effect's timeline
|
||||
virtual void timeLineGetBounds(double &t1, double &t2);
|
||||
private:
|
||||
QList<PersistentErrors> persistentErrors_;
|
||||
|
||||
VideoParams params_;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/project.h"
|
||||
#include "rendermanager.h"
|
||||
#include "pluginSupport/OliveClip.h"
|
||||
#include "pluginSupport/OliveHost.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -157,6 +159,7 @@ void RenderProcessor::Run()
|
||||
|
||||
SetCancelPointer(ticket_->GetCancelAtom());
|
||||
|
||||
VideoParams params=ticket_->property("vparam").value<VideoParams>();
|
||||
SetCacheVideoParams(ticket_->property("vparam").value<VideoParams>());
|
||||
SetCacheAudioParams(ticket_->property("aparam").value<AudioParams>());
|
||||
|
||||
@@ -165,6 +168,92 @@ void RenderProcessor::Run()
|
||||
return;
|
||||
}
|
||||
|
||||
// if is a plugin
|
||||
Node *node=ticket_->property("node").value<Node*>();
|
||||
if (node && node->getPlugin()) {
|
||||
std::shared_ptr<OFX::Host::ImageEffect::ImageEffectPlugin> plugin
|
||||
= node->getPlugin();
|
||||
std::unique_ptr<OFX::Host::ImageEffect::Instance> instance(plugin->createInstance(kOfxImageEffectContextFilter, NULL));
|
||||
|
||||
OfxStatus stat;
|
||||
stat = instance->createInstanceAction();
|
||||
if(stat != kOfxStatOK && stat != kOfxStatReplyDefault) {
|
||||
ticket_->Finish();
|
||||
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) {
|
||||
ticket_->Finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// current render scale of 1
|
||||
OfxPointD renderScale;
|
||||
renderScale.x = renderScale.y = 1.0;
|
||||
|
||||
// The render window is in pixel coordinates
|
||||
// ie: render scale and a PAR of not 1
|
||||
OfxRectI renderWindow;
|
||||
renderWindow.x1 = renderWindow.y1 = 0;
|
||||
renderWindow.x2 = ticket_->property("size").value<QSize>().width();
|
||||
renderWindow.y2 = ticket_->property("size").value<QSize>().height();
|
||||
|
||||
/// RoI is in canonical coords,
|
||||
OfxRectD regionOfInterest;
|
||||
regionOfInterest.x1 = regionOfInterest.y1 = 0;
|
||||
regionOfInterest.x2 = renderWindow.x2 * instance->getProjectPixelAspectRatio();
|
||||
regionOfInterest.y2 = renderWindow.y2 * instance->getProjectPixelAspectRatio();
|
||||
|
||||
OfxRectD regionOfDefinition;
|
||||
regionOfDefinition.x1 = regionOfDefinition.y1 = 0;
|
||||
regionOfDefinition.x2 = params.width();
|
||||
regionOfDefinition.y2 = params.height();
|
||||
|
||||
|
||||
int numFramesToRender=ticket_->property("time").value<rational>().toDouble()
|
||||
* params.frame_rate().toDouble();
|
||||
stat = instance->beginRenderAction(0, numFramesToRender, 1.0, false, renderScale, /*sequential=*/true, /*interactive=*/false
|
||||
);
|
||||
if (stat != kOfxStatOK && stat != kOfxStatReplyDefault) {
|
||||
ticket_->Finish();
|
||||
return;
|
||||
}
|
||||
|
||||
plugin::OliveClipInstance *clip=dynamic_cast<plugin::OliveClipInstance *>(instance->getClip("Output"));
|
||||
for(int t = 0; t <= numFramesToRender; ++t)
|
||||
{
|
||||
// call get region of interest on each of the inputs
|
||||
OfxTime frame = t;
|
||||
|
||||
// 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
|
||||
// effect needs to render a given frame (clipped to the RoD).
|
||||
//
|
||||
// In our example we are doing full frame fetches regardless.
|
||||
std::map<OFX::Host::ImageEffect::ClipInstance *, OfxRectD> rois;
|
||||
stat = instance->getRegionOfInterestAction(frame, renderScale,
|
||||
regionOfInterest, rois);
|
||||
assert(stat == kOfxStatOK || stat == kOfxStatReplyDefault);
|
||||
|
||||
// render a frame
|
||||
stat = instance->renderAction(t,kOfxImageFieldBoth,renderWindow, renderScale, /*sequential=*/true, /*interactive=*/false, /*draft=*/false);
|
||||
assert(stat == kOfxStatOK);
|
||||
|
||||
// get the output image buffer
|
||||
OFX::Host::ImageEffect::Image *outputImage = clip->getOutputImage();
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << "Output." << t << ".ppm";
|
||||
exportToPPM(ss.str(), outputImage);
|
||||
}
|
||||
|
||||
instance->endRenderAction(0, numFramesToRender, 1.0, false, renderScale, /*sequential=*/true, /*interactive=*/false
|
||||
);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case RenderManager::kTypeVideo: {
|
||||
rational time = ticket_->property("time").value<rational>();
|
||||
|
||||
Reference in New Issue
Block a user