Finish TODO 8,9
This commit is contained in:
+8
-2
@@ -45,12 +45,18 @@
|
||||
8) Project Extent / Fielding 行为待确认
|
||||
- 现状:`OlivePluginInstance::getProjectExtent()` 有 “TODO” 注释。
|
||||
- 为什么需要:OFX 插件会根据项目尺寸、扫描线场信息做渲染决策。
|
||||
- 可能改动:明确工程是否支持不同 extent/fielding,确保返回值与项目设置一致。
|
||||
- 已完成:
|
||||
- `getProjectExtent()` 明确返回项目宽高,不再留 TODO。
|
||||
- `getDefaultOutputFielding()` 根据 `VideoParams::interlacing()` 映射为 OFX fielding。
|
||||
- 渲染时 `renderAction()` 的 field 参数根据项目是否交错决定(progressive 用 `None`,interlaced 用 `Both`)。
|
||||
|
||||
9) OpenGL Render Suite 支持
|
||||
- 现状:`OliveClipInstance::loadTexture()` 返回 null,OpenGL 渲染路径未实现。
|
||||
- 为什么需要:有些 OFX 插件只支持 OpenGL 渲染,不支持 CPU 渲染。
|
||||
- 可能改动:要么实现 OpenGL texture 的加载与生命周期,要么在 host capability 中明确禁用 OpenGL render。
|
||||
- 已完成:
|
||||
- Host 端声明 `kOfxImageEffectPropOpenGLRenderSupported = true`。
|
||||
- `OliveClipInstance::loadTexture()` 返回真实的 OpenGL 纹理句柄。
|
||||
- 渲染前设置 Output Clip 的目标纹理,输入纹理走 OpenGL 纹理路径。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
#include <QOpenGLFunctions>
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include <libswscale/swscale.h>
|
||||
@@ -203,12 +206,15 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
this->params_ = texture->params();
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
input_textures_.insert(time, texture);
|
||||
#endif
|
||||
|
||||
AVFramePtr frame = texture->frame();
|
||||
if (!frame || !frame->data[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->params_=texture->params();
|
||||
AVPixelFormat expected_fmt =
|
||||
FFmpegUtils::GetFFmpegPixelFormat(params_.format(),
|
||||
params_.channel_count());
|
||||
@@ -282,3 +288,70 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
||||
copy_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
void olive::plugin::OliveClipInstance::setOutputTexture(Texture *texture,
|
||||
OfxTime time)
|
||||
{
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
output_textures_.insert(time, texture);
|
||||
#else
|
||||
(void)texture;
|
||||
(void)time;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
OFX::Host::ImageEffect::Texture *
|
||||
olive::plugin::OliveClipInstance::loadTexture(OfxTime time, const char *format,
|
||||
const OfxRectD *optionalBounds)
|
||||
{
|
||||
(void)format;
|
||||
|
||||
Texture *gl_texture = nullptr;
|
||||
if (isOutput()) {
|
||||
gl_texture = output_textures_.value(time, nullptr);
|
||||
} else {
|
||||
TexturePtr input = input_textures_.value(time);
|
||||
gl_texture = input ? input.get() : nullptr;
|
||||
}
|
||||
|
||||
if (!gl_texture || gl_texture->IsDummy() || !gl_texture->id().isValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OfxRectD rod_d = getRegionOfDefinition(time);
|
||||
OfxRectI rod = { static_cast<int>(std::floor(rod_d.x1)),
|
||||
static_cast<int>(std::floor(rod_d.y1)),
|
||||
static_cast<int>(std::ceil(rod_d.x2)),
|
||||
static_cast<int>(std::ceil(rod_d.y2)) };
|
||||
OfxRectI bounds = rod;
|
||||
if (optionalBounds) {
|
||||
bounds.x1 = static_cast<int>(std::floor(optionalBounds->x1));
|
||||
bounds.y1 = static_cast<int>(std::floor(optionalBounds->y1));
|
||||
bounds.x2 = static_cast<int>(std::ceil(optionalBounds->x2));
|
||||
bounds.y2 = static_cast<int>(std::ceil(optionalBounds->y2));
|
||||
}
|
||||
bounds.x1 = std::max(bounds.x1, rod.x1);
|
||||
bounds.y1 = std::max(bounds.y1, rod.y1);
|
||||
bounds.x2 = std::min(bounds.x2, rod.x2);
|
||||
bounds.y2 = std::min(bounds.y2, rod.y2);
|
||||
|
||||
const int bytes_per_row =
|
||||
params_.width() * params_.channel_count() * params_.format().byte_count();
|
||||
const std::string &field = getFieldOrder();
|
||||
const std::string unique_id = std::to_string(
|
||||
reinterpret_cast<uintptr_t>(gl_texture)) + "_" +
|
||||
std::to_string(static_cast<long long>(time));
|
||||
|
||||
const int texture_id = gl_texture->id().value<GLuint>();
|
||||
OFX::Host::ImageEffect::Texture *texture =
|
||||
new OFX::Host::ImageEffect::Texture(
|
||||
*this, 1.0, 1.0, texture_id, GL_TEXTURE_2D, bounds, rod,
|
||||
bytes_per_row, field, unique_id);
|
||||
texture->addReference();
|
||||
return texture;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -70,10 +70,13 @@ public:
|
||||
params_ = params;
|
||||
}
|
||||
# ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
OFX::Host::ImageEffect::Texture* loadTexture(OfxTime time, const char *format, const OfxRectD *optionalBounds) { return NULL; };
|
||||
OFX::Host::ImageEffect::Texture* loadTexture(OfxTime time,
|
||||
const char *format,
|
||||
const OfxRectD *optionalBounds) override;
|
||||
# endif
|
||||
|
||||
void setInputTexture(TexturePtr texture, OfxTime time);
|
||||
void setOutputTexture(Texture *texture, OfxTime time);
|
||||
|
||||
private:
|
||||
VideoParams params_;
|
||||
@@ -84,6 +87,10 @@ private:
|
||||
|
||||
std::string name_;
|
||||
QMap<OfxTime,std::shared_ptr<Image>> images_;
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
QMap<OfxTime, TexturePtr> input_textures_;
|
||||
QMap<OfxTime, Texture *> output_textures_;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,10 @@ ImageEffect::Descriptor *
|
||||
OliveHost::makeDescriptor(ImageEffect::ImageEffectPlugin* plugin)
|
||||
{
|
||||
ImageEffect::Descriptor* desc = new ImageEffect::Descriptor(plugin);
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
desc->getProps().setStringProperty(kOfxImageEffectPropOpenGLRenderSupported,
|
||||
"true");
|
||||
#endif
|
||||
descriptors_.append(desc);
|
||||
return desc;
|
||||
}
|
||||
@@ -85,6 +89,10 @@ OliveHost::makeDescriptor(const ImageEffect::Descriptor &rootContext,
|
||||
ImageEffect::ImageEffectPlugin *plugin)
|
||||
{
|
||||
ImageEffect::Descriptor* desc = new ImageEffect::Descriptor(rootContext,plugin);
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
desc->getProps().setStringProperty(kOfxImageEffectPropOpenGLRenderSupported,
|
||||
"true");
|
||||
#endif
|
||||
descriptors_.append(desc);
|
||||
return desc;
|
||||
}
|
||||
@@ -93,6 +101,10 @@ OliveHost::makeDescriptor(const std::string &bundlePath,
|
||||
ImageEffect::ImageEffectPlugin *plugin)
|
||||
{
|
||||
ImageEffect::Descriptor* desc = new ImageEffect::Descriptor(bundlePath, plugin);
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
desc->getProps().setStringProperty(kOfxImageEffectPropOpenGLRenderSupported,
|
||||
"true");
|
||||
#endif
|
||||
descriptors_.append(desc);
|
||||
return desc;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "OlivePluginInstance.h"
|
||||
|
||||
#include "OliveClip.h"
|
||||
#include "ofxGPURender.h"
|
||||
#include "ofxCore.h"
|
||||
#include "ofxMessage.h"
|
||||
#include "common/Current.h"
|
||||
@@ -42,6 +43,19 @@ namespace olive
|
||||
namespace plugin
|
||||
{
|
||||
namespace {
|
||||
const std::string &FieldOrderForParams(const VideoParams ¶ms)
|
||||
{
|
||||
switch (params.interlacing()) {
|
||||
case VideoParams::kInterlaceNone:
|
||||
return kOfxImageFieldNone;
|
||||
case VideoParams::kInterlacedTopFirst:
|
||||
return kOfxImageFieldUpper;
|
||||
case VideoParams::kInterlacedBottomFirst:
|
||||
return kOfxImageFieldLower;
|
||||
}
|
||||
return kOfxImageFieldNone;
|
||||
}
|
||||
|
||||
class DeferredRedoCommand : public UndoCommand {
|
||||
public:
|
||||
explicit DeferredRedoCommand(UndoCommand *inner)
|
||||
@@ -107,6 +121,11 @@ ViewerOutput *GetActiveViewerOutput()
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const std::string &OlivePluginInstance::getDefaultOutputFielding() const
|
||||
{
|
||||
return FieldOrderForParams(params_);
|
||||
}
|
||||
|
||||
OfxStatus OlivePluginInstance::vmessage(const char *type, const char *id,
|
||||
const char *format, va_list args)
|
||||
{
|
||||
@@ -196,7 +215,6 @@ void OlivePluginInstance::getProjectExtent(double &xSize, double &ySize) const
|
||||
{
|
||||
xSize =params_.width();
|
||||
ySize =params_.height();
|
||||
// TODO: Ensure this project does not support this.
|
||||
}
|
||||
double OlivePluginInstance::getProjectPixelAspectRatio() const
|
||||
{
|
||||
@@ -392,6 +410,22 @@ bool OlivePluginInstance::progressUpdate(double t)
|
||||
return !progress_cancelled_;
|
||||
}
|
||||
|
||||
OfxStatus OlivePluginInstance::contextAttachedAction()
|
||||
{
|
||||
if (!open_gl_enabled_) {
|
||||
return kOfxStatReplyDefault;
|
||||
}
|
||||
return kOfxStatOK;
|
||||
}
|
||||
|
||||
OfxStatus OlivePluginInstance::contextDetachedAction()
|
||||
{
|
||||
if (!open_gl_enabled_) {
|
||||
return kOfxStatReplyDefault;
|
||||
}
|
||||
return kOfxStatOK;
|
||||
}
|
||||
|
||||
double OlivePluginInstance::timeLineGetTime()
|
||||
{
|
||||
if (ViewerOutput *viewer = GetActiveViewerOutput()) {
|
||||
@@ -420,6 +454,17 @@ void OlivePluginInstance::timeLineGetBounds(double &t1, double &t2)
|
||||
t2 = 0.0;
|
||||
}
|
||||
|
||||
void OlivePluginInstance::setCustomInArgs(const std::string &action,
|
||||
OFX::Host::Property::Set &inArgs)
|
||||
{
|
||||
if (action == kOfxImageEffectActionRender ||
|
||||
action == kOfxImageEffectActionBeginSequenceRender ||
|
||||
action == kOfxImageEffectActionEndSequenceRender) {
|
||||
inArgs.setIntProperty(kOfxImageEffectPropOpenGLEnabled,
|
||||
open_gl_enabled_ ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
OFX::Host::ImageEffect::ClipInstance *OlivePluginInstance::newClipInstance(
|
||||
OFX::Host::ImageEffect::Instance *plugin,
|
||||
OFX::Host::ImageEffect::ClipDescriptor *descriptor,
|
||||
|
||||
@@ -67,9 +67,7 @@ public:
|
||||
}
|
||||
explicit OlivePluginInstance(Instance & instance):Instance(instance){};
|
||||
~OlivePluginInstance() override = default;
|
||||
const std::string &getDefaultOutputFielding() const{
|
||||
return kOfxImageFieldNone;
|
||||
};
|
||||
const std::string &getDefaultOutputFielding() const override;
|
||||
|
||||
void setVideoParam(VideoParams params)
|
||||
{
|
||||
@@ -79,6 +77,10 @@ public:
|
||||
{
|
||||
node_ = node;
|
||||
}
|
||||
void setOpenGLEnabled(bool enabled)
|
||||
{
|
||||
open_gl_enabled_ = enabled;
|
||||
}
|
||||
OFX::Host::ImageEffect::ClipInstance *newClipInstance(
|
||||
OFX::Host::ImageEffect::Instance *plugin,
|
||||
OFX::Host::ImageEffect::ClipDescriptor *descriptor,
|
||||
@@ -165,6 +167,11 @@ public:
|
||||
/// false if you should abandon processing, true to continue
|
||||
virtual bool progressUpdate(double t) override;
|
||||
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
virtual OfxStatus contextAttachedAction() override;
|
||||
virtual OfxStatus contextDetachedAction() override;
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -182,6 +189,9 @@ public:
|
||||
/// get the first and last times available on the effect's timeline
|
||||
virtual void timeLineGetBounds(double &t1, double &t2);
|
||||
|
||||
void setCustomInArgs(const std::string &action,
|
||||
OFX::Host::Property::Set &inArgs) override;
|
||||
|
||||
|
||||
private:
|
||||
QList<PersistentErrors> persistentErrors_;
|
||||
@@ -195,6 +205,7 @@ private:
|
||||
QPointer<ProgressDialog> progress_dialog_;
|
||||
bool progress_cancelled_ = false;
|
||||
bool progress_active_ = false;
|
||||
bool open_gl_enabled_ = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +151,94 @@ static AVPixelFormat GetDestinationAVPixelFormat(const olive::VideoParams ¶m
|
||||
return pix_fmt;
|
||||
}
|
||||
|
||||
static const char *GetRenderFieldForParams(const olive::VideoParams ¶ms)
|
||||
{
|
||||
switch (params.interlacing()) {
|
||||
case olive::VideoParams::kInterlaceNone:
|
||||
return kOfxImageFieldNone;
|
||||
case olive::VideoParams::kInterlacedTopFirst:
|
||||
case olive::VideoParams::kInterlacedBottomFirst:
|
||||
return kOfxImageFieldBoth;
|
||||
}
|
||||
return kOfxImageFieldNone;
|
||||
}
|
||||
|
||||
static olive::AVFramePtr ReadbackTextureToFrame(olive::Texture *texture,
|
||||
const olive::VideoParams ¶ms)
|
||||
{
|
||||
if (!texture || texture->IsDummy()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AVPixelFormat pix_fmt = GetDestinationAVPixelFormat(params);
|
||||
if (pix_fmt == AV_PIX_FMT_NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
|
||||
if (!desc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!(desc->flags & AV_PIX_FMT_FLAG_PLANAR)) {
|
||||
olive::AVFramePtr frame = olive::CreateAVFramePtr();
|
||||
frame->format = pix_fmt;
|
||||
frame->width = params.width();
|
||||
frame->height = params.height();
|
||||
if (av_frame_get_buffer(frame.get(), 0) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (texture->renderer()) {
|
||||
texture->renderer()->DownloadFromTexture(
|
||||
texture->id(), params, frame->data[0], frame->linesize[0]);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
// Planar formats: read back as RGBA and convert.
|
||||
olive::VideoParams rgba_params(
|
||||
params.width(), params.height(), olive::core::PixelFormat::U8, 4,
|
||||
params.pixel_aspect_ratio(), params.interlacing(), params.divider());
|
||||
|
||||
olive::AVFramePtr rgba_frame = olive::CreateAVFramePtr();
|
||||
rgba_frame->format = AV_PIX_FMT_RGBA;
|
||||
rgba_frame->width = params.width();
|
||||
rgba_frame->height = params.height();
|
||||
if (av_frame_get_buffer(rgba_frame.get(), 0) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (texture->renderer()) {
|
||||
texture->renderer()->DownloadFromTexture(
|
||||
texture->id(), rgba_params, rgba_frame->data[0],
|
||||
rgba_frame->linesize[0]);
|
||||
}
|
||||
|
||||
olive::AVFramePtr dst = olive::CreateAVFramePtr();
|
||||
dst->format = pix_fmt;
|
||||
dst->width = params.width();
|
||||
dst->height = params.height();
|
||||
if (av_frame_get_buffer(dst.get(), 0) < 0) {
|
||||
return rgba_frame;
|
||||
}
|
||||
|
||||
SwsContext *sws_ctx = sws_getContext(
|
||||
rgba_frame->width, rgba_frame->height,
|
||||
static_cast<AVPixelFormat>(rgba_frame->format),
|
||||
dst->width, dst->height, pix_fmt, SWS_POINT,
|
||||
nullptr, nullptr, nullptr);
|
||||
if (!sws_ctx) {
|
||||
return rgba_frame;
|
||||
}
|
||||
|
||||
sws_scale(sws_ctx, rgba_frame->data, rgba_frame->linesize, 0,
|
||||
rgba_frame->height, dst->data, dst->linesize);
|
||||
sws_freeContext(sws_ctx);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
static olive::AVFramePtr ConvertFrameIfNeeded(olive::AVFramePtr src,
|
||||
const olive::VideoParams &dst_params)
|
||||
{
|
||||
@@ -198,6 +286,16 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
bool clear_destination, bool interactive)
|
||||
{
|
||||
auto instance=job.pluginInstance();
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
auto *olive_instance =
|
||||
dynamic_cast<olive::plugin::OlivePluginInstance *>(instance);
|
||||
const bool use_opengl =
|
||||
destination && destination->renderer() && destination->id().isValid();
|
||||
if (olive_instance) {
|
||||
olive_instance->setOpenGLEnabled(use_opengl);
|
||||
}
|
||||
OfxStatus stat;
|
||||
stat = instance->createInstanceAction();
|
||||
if(stat != kOfxStatOK && stat != kOfxStatReplyDefault) {
|
||||
@@ -243,8 +341,17 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
return;
|
||||
}
|
||||
|
||||
if (use_opengl) {
|
||||
instance->contextAttachedAction();
|
||||
AttachOutputTexture(destination);
|
||||
}
|
||||
|
||||
OliveClipInstance *clip=dynamic_cast<plugin::OliveClipInstance *>(instance->getClip("Output"));
|
||||
if (!clip) {
|
||||
if (use_opengl) {
|
||||
DetachOutputTexture();
|
||||
instance->contextDetachedAction();
|
||||
}
|
||||
instance->endRenderAction(0, numFramesToRender, 1.0, interactive, renderScale, true,interactive
|
||||
);
|
||||
return;
|
||||
@@ -255,6 +362,7 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
|
||||
clip->setParams(destination_params);
|
||||
clip->setRegionOfDefinition(regionOfDefinition, frame);
|
||||
clip->setOutputTexture(destination, frame);
|
||||
|
||||
const NodeValueRow &values = job.GetValues();
|
||||
const auto &clips = instance->getDescriptor().getClips();
|
||||
@@ -288,28 +396,65 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
assert(stat == kOfxStatOK || stat == kOfxStatReplyDefault);
|
||||
|
||||
// render a frame
|
||||
stat = instance->renderAction(0,kOfxImageFieldBoth,renderWindow, renderScale, true, interactive, interactive);
|
||||
const char *render_field = GetRenderFieldForParams(destination_params);
|
||||
stat = instance->renderAction(0, render_field, renderWindow, renderScale,
|
||||
true, interactive, interactive);
|
||||
assert(stat == kOfxStatOK);
|
||||
|
||||
// get the output image buffer
|
||||
std::shared_ptr<OFX::Host::ImageEffect::Image> output_image = clip->getOutputImage(frame);
|
||||
if (!output_image) {
|
||||
instance->endRenderAction(frame, numFramesToRender, 1.0, interactive, renderScale, true,interactive
|
||||
);
|
||||
return;
|
||||
// get the output image buffer (CPU path only)
|
||||
std::shared_ptr<OFX::Host::ImageEffect::Image> output_image;
|
||||
if (!use_opengl) {
|
||||
output_image = clip->getOutputImage(frame);
|
||||
if (!output_image) {
|
||||
instance->endRenderAction(frame, numFramesToRender, 1.0, interactive,
|
||||
renderScale, true, interactive);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!destination || !destination->id().isValid()) {
|
||||
DetachOutputTexture();
|
||||
instance->contextDetachedAction();
|
||||
instance->endRenderAction(frame, numFramesToRender, 1.0, interactive,
|
||||
renderScale, true, interactive);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AVFramePtr frame_ptr = create_avframe_from_ofx_image(*output_image);
|
||||
if (!frame_ptr) {
|
||||
instance->endRenderAction(0, numFramesToRender, 1.0, interactive, renderScale, true,interactive
|
||||
);
|
||||
return;
|
||||
if (!use_opengl) {
|
||||
AVFramePtr frame_ptr = create_avframe_from_ofx_image(*output_image);
|
||||
if (!frame_ptr) {
|
||||
instance->endRenderAction(0, numFramesToRender, 1.0, interactive,
|
||||
renderScale, true, interactive);
|
||||
return;
|
||||
}
|
||||
AVFramePtr converted = ConvertFrameIfNeeded(frame_ptr, destination_params);
|
||||
destination->handleFrame(converted);
|
||||
} else {
|
||||
AVFramePtr frame_ptr =
|
||||
ReadbackTextureToFrame(destination, destination_params);
|
||||
DetachOutputTexture();
|
||||
instance->contextDetachedAction();
|
||||
if (frame_ptr) {
|
||||
AVFramePtr converted =
|
||||
ConvertFrameIfNeeded(frame_ptr, destination_params);
|
||||
destination->handleFrame(converted);
|
||||
}
|
||||
}
|
||||
|
||||
AVFramePtr converted = ConvertFrameIfNeeded(frame_ptr, destination_params);
|
||||
destination->handleFrame(converted);
|
||||
|
||||
instance->endRenderAction(0, numFramesToRender, 1.0, interactive, renderScale, true,interactive
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void olive::plugin::PluginRenderer::AttachOutputTexture(olive::Texture *texture)
|
||||
{
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
AttachTextureAsDestination(texture->id());
|
||||
}
|
||||
|
||||
void olive::plugin::PluginRenderer::DetachOutputTexture()
|
||||
{
|
||||
DetachTextureAsDestination();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ class PluginRenderer : public olive::OpenGLRenderer{
|
||||
public:
|
||||
PluginRenderer(QObject *parent=nullptr):OpenGLRenderer(parent){};
|
||||
virtual ~PluginRenderer() override{};
|
||||
void AttachOutputTexture(olive::Texture *texture);
|
||||
void DetachOutputTexture();
|
||||
protected:
|
||||
virtual void RenderPlugin(TexturePtr src, olive::plugin::PluginJob& job,
|
||||
olive::Texture *destination,
|
||||
|
||||
Reference in New Issue
Block a user