尝试解决插件bug
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
CompileFlags:
|
||||
Add: []
|
||||
Remove: [-mlongcalls, -fstrict-volatile-bitfields, -fno-shrink-wrap, -fno-tree-switch-conversion, -mno-direct-extern-access]
|
||||
|
||||
CompileFlags:
|
||||
CompilationDatabase: build
|
||||
Diagnostics:
|
||||
Suppress: ['drv_unknown_argument', 'unused-includes', 'pp_file_not_found']
|
||||
@@ -22,6 +22,10 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -g")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -g")
|
||||
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} -fsanitize=memory)
|
||||
endif ()
|
||||
|
||||
|
||||
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -DOFX_SUPPORTS_OPENGLRENDER)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOFX_SUPPORTS_OPENGLRENDER")
|
||||
option(BUILD_QT6 "Build with Qt 6 over 5 (experimental)" ON)
|
||||
option(BUILD_DOXYGEN "Build Doxygen documentation" OFF)
|
||||
option(BUILD_TESTS "Build unit tests" OFF)
|
||||
@@ -55,6 +59,7 @@ if(MSVC)
|
||||
/external:W0
|
||||
"$<$<CONFIG:RELEASE>:/O2>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:/MP>"
|
||||
/DOFX_SUPPORTS_OPENGLRENDER
|
||||
)
|
||||
if (USE_WERROR)
|
||||
list(APPEND OLIVE_COMPILE_OPTIONS "/WX")
|
||||
@@ -68,6 +73,8 @@ else()
|
||||
-Wextra
|
||||
-Wno-unused-parameter
|
||||
-Wshadow
|
||||
-DOFX_SUPPORTS_OPENGLRENDER
|
||||
|
||||
)
|
||||
if (USE_WERROR)
|
||||
list(APPEND OLIVE_COMPILE_OPTIONS "-Werror")
|
||||
|
||||
@@ -122,7 +122,24 @@ olive::plugin::PluginNode::PluginNode(
|
||||
}
|
||||
|
||||
const QString input_id = QString::fromStdString(param.second->getName());
|
||||
if (input_id.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
const auto &props = param.second->getProperties();
|
||||
if (props.getIntProperty(kOfxParamPropSecret) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (type == NodeValue::kNone) {
|
||||
continue;
|
||||
}
|
||||
AddInput(input_id, type);
|
||||
const QString label =
|
||||
QString::fromStdString(param.second->getLabel());
|
||||
if (!label.isEmpty()) {
|
||||
SetInputName(input_id, label);
|
||||
} else {
|
||||
SetInputName(input_id, input_id);
|
||||
}
|
||||
const QString parent =
|
||||
QString::fromStdString(param.second->getParentName());
|
||||
if (!parent.isEmpty()) {
|
||||
@@ -209,7 +226,7 @@ void olive::plugin::PluginNode::Value(const NodeValueRow &value,
|
||||
}
|
||||
}
|
||||
if (tex && plugin_instance_) {
|
||||
PluginJob job(plugin_instance_, this, value);
|
||||
PluginJob job(plugin_instance_, this, value, globals.time().in());
|
||||
table->Push(NodeValue::kTexture, tex->toJob(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,9 +35,11 @@
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
#include <QOpenGLFunctions>
|
||||
#endif
|
||||
|
||||
#include "common/ffmpegutils.h"
|
||||
#include "render/renderer.h"
|
||||
extern "C" {
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
}
|
||||
namespace {
|
||||
const std::string kBitDepthNoneStr(kOfxBitDepthNone);
|
||||
@@ -54,6 +56,239 @@ const std::string kImageUnPremultStr(kOfxImageUnPreMultiplied);
|
||||
const std::string kImageFieldNoneStr(kOfxImageFieldNone);
|
||||
const std::string kImageFieldUpperStr(kOfxImageFieldUpper);
|
||||
const std::string kImageFieldLowerStr(kOfxImageFieldLower);
|
||||
|
||||
static int BytesToPixels(int byte_linesize, const olive::VideoParams ¶ms)
|
||||
{
|
||||
const int bytes_per_pixel =
|
||||
params.channel_count() * params.format().byte_count();
|
||||
if (bytes_per_pixel <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return byte_linesize / bytes_per_pixel;
|
||||
}
|
||||
|
||||
static int PackedFloatChannels(AVPixelFormat fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AV_PIX_FMT_GRAYF32LE:
|
||||
case AV_PIX_FMT_GRAYF32BE:
|
||||
return 1;
|
||||
case AV_PIX_FMT_RGBF32LE:
|
||||
case AV_PIX_FMT_RGBF32BE:
|
||||
return 3;
|
||||
case AV_PIX_FMT_RGBAF32LE:
|
||||
case AV_PIX_FMT_RGBAF32BE:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool PackedDstInfo(AVPixelFormat fmt, int *channels,
|
||||
int *bytes_per_component)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AV_PIX_FMT_GRAY8:
|
||||
*channels = 1;
|
||||
*bytes_per_component = 1;
|
||||
return true;
|
||||
case AV_PIX_FMT_RGB24:
|
||||
*channels = 3;
|
||||
*bytes_per_component = 1;
|
||||
return true;
|
||||
case AV_PIX_FMT_RGBA:
|
||||
*channels = 4;
|
||||
*bytes_per_component = 1;
|
||||
return true;
|
||||
case AV_PIX_FMT_GRAY16LE:
|
||||
*channels = 1;
|
||||
*bytes_per_component = 2;
|
||||
return true;
|
||||
case AV_PIX_FMT_RGB48LE:
|
||||
*channels = 3;
|
||||
*bytes_per_component = 2;
|
||||
return true;
|
||||
case AV_PIX_FMT_RGBA64LE:
|
||||
*channels = 4;
|
||||
*bytes_per_component = 2;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static olive::AVFramePtr ReadbackTextureToFrame(olive::TexturePtr texture,
|
||||
const olive::VideoParams ¶ms)
|
||||
{
|
||||
if (!texture || texture->IsDummy() || !texture->renderer()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AVPixelFormat pix_fmt =
|
||||
olive::FFmpegUtils::GetFFmpegPixelFormat(params.format(),
|
||||
params.channel_count());
|
||||
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;
|
||||
}
|
||||
const int linesize_pixels = BytesToPixels(frame->linesize[0], params);
|
||||
texture->renderer()->DownloadFromTexture(texture->id(), params,
|
||||
frame->data[0],
|
||||
linesize_pixels);
|
||||
return frame;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const int linesize_pixels =
|
||||
BytesToPixels(rgba_frame->linesize[0], rgba_params);
|
||||
texture->renderer()->DownloadFromTexture(texture->id(), rgba_params,
|
||||
rgba_frame->data[0],
|
||||
linesize_pixels);
|
||||
|
||||
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 ConvertPackedFloatFrame(olive::AVFramePtr src,
|
||||
AVPixelFormat dst_fmt)
|
||||
{
|
||||
if (!src || !src->data[0]) {
|
||||
return nullptr;
|
||||
}
|
||||
const int src_channels =
|
||||
PackedFloatChannels(static_cast<AVPixelFormat>(src->format));
|
||||
if (src_channels == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int dst_channels = 0;
|
||||
int bytes_per_component = 0;
|
||||
if (!PackedDstInfo(dst_fmt, &dst_channels, &bytes_per_component)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
olive::AVFramePtr dst = olive::CreateAVFramePtr();
|
||||
dst->format = dst_fmt;
|
||||
dst->width = src->width;
|
||||
dst->height = src->height;
|
||||
if (av_frame_get_buffer(dst.get(), 0) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto clamp01 = [](float v) -> float {
|
||||
return std::clamp(v, 0.0f, 1.0f);
|
||||
};
|
||||
|
||||
for (int y = 0; y < src->height; ++y) {
|
||||
const float *src_row = reinterpret_cast<const float *>(
|
||||
src->data[0] + y * src->linesize[0]);
|
||||
uint8_t *dst_row = dst->data[0] + y * dst->linesize[0];
|
||||
|
||||
if (bytes_per_component == 2) {
|
||||
auto *dst_row_u16 = reinterpret_cast<uint16_t *>(dst_row);
|
||||
for (int x = 0; x < src->width; ++x) {
|
||||
const float *pix = src_row + x * src_channels;
|
||||
float r = pix[0];
|
||||
float g = (src_channels > 1) ? pix[1] : r;
|
||||
float b = (src_channels > 2) ? pix[2] : r;
|
||||
float a = (src_channels > 3) ? pix[3] : 1.0f;
|
||||
if (dst_channels == 1) {
|
||||
float luma = 0.2126f * r + 0.7152f * g + 0.0722f * b;
|
||||
dst_row_u16[x] = static_cast<uint16_t>(
|
||||
std::lround(clamp01(luma) * 65535.0f));
|
||||
continue;
|
||||
}
|
||||
dst_row_u16[x * dst_channels + 0] =
|
||||
static_cast<uint16_t>(
|
||||
std::lround(clamp01(r) * 65535.0f));
|
||||
dst_row_u16[x * dst_channels + 1] =
|
||||
static_cast<uint16_t>(
|
||||
std::lround(clamp01(g) * 65535.0f));
|
||||
dst_row_u16[x * dst_channels + 2] =
|
||||
static_cast<uint16_t>(
|
||||
std::lround(clamp01(b) * 65535.0f));
|
||||
if (dst_channels == 4) {
|
||||
dst_row_u16[x * dst_channels + 3] =
|
||||
static_cast<uint16_t>(
|
||||
std::lround(clamp01(a) * 65535.0f));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int x = 0; x < src->width; ++x) {
|
||||
const float *pix = src_row + x * src_channels;
|
||||
float r = pix[0];
|
||||
float g = (src_channels > 1) ? pix[1] : r;
|
||||
float b = (src_channels > 2) ? pix[2] : r;
|
||||
float a = (src_channels > 3) ? pix[3] : 1.0f;
|
||||
if (dst_channels == 1) {
|
||||
float luma = 0.2126f * r + 0.7152f * g + 0.0722f * b;
|
||||
dst_row[x] = static_cast<uint8_t>(
|
||||
std::lround(clamp01(luma) * 255.0f));
|
||||
continue;
|
||||
}
|
||||
dst_row[x * dst_channels + 0] =
|
||||
static_cast<uint8_t>(
|
||||
std::lround(clamp01(r) * 255.0f));
|
||||
dst_row[x * dst_channels + 1] =
|
||||
static_cast<uint8_t>(
|
||||
std::lround(clamp01(g) * 255.0f));
|
||||
dst_row[x * dst_channels + 2] =
|
||||
static_cast<uint8_t>(
|
||||
std::lround(clamp01(b) * 255.0f));
|
||||
if (dst_channels == 4) {
|
||||
dst_row[x * dst_channels + 3] =
|
||||
static_cast<uint8_t>(
|
||||
std::lround(clamp01(a) * 255.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &olive::plugin::OliveClipInstance::getUnmappedBitDepth() const
|
||||
@@ -125,14 +360,29 @@ const std::string &olive::plugin::OliveClipInstance::getFieldOrder() const
|
||||
bool olive::plugin::OliveClipInstance::getConnected() const
|
||||
{
|
||||
if (name_ == kOfxImageEffectOutputClipName) {
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
if (!output_textures_.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
for (auto it = images_.cbegin(); it != images_.cend(); ++it) {
|
||||
if (it.value()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
if (!input_textures_.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return !images_.isEmpty();
|
||||
for (auto it = images_.cbegin(); it != images_.cend(); ++it) {
|
||||
if (it.value()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
double olive::plugin::OliveClipInstance::getUnmappedFrameRate() const
|
||||
{
|
||||
@@ -227,7 +477,7 @@ OfxRectD
|
||||
olive::plugin::OliveClipInstance::getRegionOfDefinition(OfxTime time) const
|
||||
{
|
||||
if (regionOfDefinitions_.contains(time)) {
|
||||
return regionOfDefinitions_[time];
|
||||
return regionOfDefinitions_.value(time);
|
||||
}
|
||||
OfxRectD regionOfDefinition;
|
||||
regionOfDefinition.x1 = regionOfDefinition.y1 = 0;
|
||||
@@ -251,6 +501,7 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
||||
return;
|
||||
}
|
||||
VideoParams incoming = texture->params();
|
||||
// AI wrote this. Why?
|
||||
if (params_.format() != PixelFormat::INVALID &&
|
||||
params_.channel_count() > 0) {
|
||||
incoming.set_format(params_.format());
|
||||
@@ -264,7 +515,7 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
||||
|
||||
AVFramePtr frame = texture->frame();
|
||||
if (!frame || !frame->data[0]) {
|
||||
return;
|
||||
frame = ReadbackTextureToFrame(texture, params_);
|
||||
}
|
||||
AVPixelFormat expected_fmt =
|
||||
FFmpegUtils::GetFFmpegPixelFormat(params_.format(),
|
||||
@@ -295,10 +546,23 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
||||
return;
|
||||
}
|
||||
|
||||
if (!frame || !frame->data[0]) {
|
||||
std::memset(dst, 0, image->row_bytes() * image->height());
|
||||
return;
|
||||
}
|
||||
|
||||
AVFramePtr src_frame = frame;
|
||||
if (frame->format != expected_fmt ||
|
||||
frame->width != params_.width() ||
|
||||
frame->height != params_.height()) {
|
||||
if (PackedFloatChannels(static_cast<AVPixelFormat>(frame->format)) > 0) {
|
||||
AVFramePtr converted =
|
||||
ConvertPackedFloatFrame(frame, expected_fmt);
|
||||
if (converted) {
|
||||
src_frame = converted;
|
||||
goto copy_pixels;
|
||||
}
|
||||
}
|
||||
AVFramePtr converted = CreateAVFramePtr();
|
||||
converted->format = expected_fmt;
|
||||
converted->width = params_.width();
|
||||
@@ -324,6 +588,7 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
||||
src_frame = converted;
|
||||
}
|
||||
|
||||
copy_pixels:
|
||||
int bytes_per_component = params_.format().byte_count();
|
||||
int bytes_per_row = params_.width() * params_.channel_count() *
|
||||
bytes_per_component;
|
||||
@@ -340,7 +605,7 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
||||
}
|
||||
}
|
||||
|
||||
void olive::plugin::OliveClipInstance::setOutputTexture(Texture *texture,
|
||||
void olive::plugin::OliveClipInstance::setOutputTexture(TexturePtr texture,
|
||||
OfxTime time)
|
||||
{
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
@@ -361,12 +626,12 @@ olive::plugin::OliveClipInstance::loadTexture(OfxTime time, const char *format,
|
||||
{
|
||||
(void)format;
|
||||
|
||||
Texture *gl_texture = nullptr;
|
||||
TexturePtr 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;
|
||||
gl_texture = input ? input : nullptr;
|
||||
}
|
||||
|
||||
if (!gl_texture || gl_texture->IsDummy() || !gl_texture->id().isValid()) {
|
||||
@@ -394,7 +659,7 @@ olive::plugin::OliveClipInstance::loadTexture(OfxTime time, const char *format,
|
||||
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)) + "_" +
|
||||
reinterpret_cast<uintptr_t>(gl_texture.get())) + "_" +
|
||||
std::to_string(static_cast<long long>(time));
|
||||
|
||||
const int texture_id = gl_texture->id().value<GLuint>();
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
# endif
|
||||
|
||||
void setInputTexture(TexturePtr texture, OfxTime time);
|
||||
void setOutputTexture(Texture *texture, OfxTime time);
|
||||
void setOutputTexture(TexturePtr texture, OfxTime time);
|
||||
|
||||
private:
|
||||
VideoParams params_;
|
||||
@@ -86,7 +86,7 @@ private:
|
||||
QMap<OfxTime,std::shared_ptr<Image>> images_;
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
QMap<OfxTime, TexturePtr> input_textures_;
|
||||
QMap<OfxTime, Texture *> output_textures_;
|
||||
QMap<OfxTime, TexturePtr> output_textures_;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <ofxhPluginCache.h>
|
||||
#include <ofxhBinary.h>
|
||||
|
||||
@@ -108,14 +109,6 @@ void olive::plugin::loadPlugins(QString path)
|
||||
}
|
||||
OliveHost::~OliveHost()
|
||||
{
|
||||
for(auto& descriptor:descriptors_){
|
||||
delete descriptor;
|
||||
descriptor=nullptr;
|
||||
}
|
||||
for(auto& instance:instances_){
|
||||
delete instance;
|
||||
instance=nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -124,45 +117,37 @@ void OliveHost::destroyInstance(OFX::Host::ImageEffect::Instance* instance)
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
if (instances_.contains(instance))
|
||||
{
|
||||
instances_.removeOne(instance);
|
||||
delete instance;
|
||||
for (auto it = instances_.begin(); it != instances_.end(); ++it) {
|
||||
if (it->get() == instance) {
|
||||
instances_.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ImageEffect::Descriptor *
|
||||
}
|
||||
std::shared_ptr<OFX::Host::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);
|
||||
std::shared_ptr<OFX::Host::ImageEffect::Descriptor> desc =
|
||||
std::make_shared<ImageEffect::Descriptor>(plugin);
|
||||
descriptors_.append(std::shared_ptr<ImageEffect::Descriptor>(desc));
|
||||
return desc;
|
||||
}
|
||||
ImageEffect::Descriptor *
|
||||
std::shared_ptr<OFX::Host::ImageEffect::Descriptor>
|
||||
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);
|
||||
std::shared_ptr<OFX::Host::ImageEffect::Descriptor> desc =
|
||||
std::make_shared<ImageEffect::Descriptor>(rootContext, plugin);
|
||||
descriptors_.append(std::shared_ptr<ImageEffect::Descriptor>(desc));
|
||||
return desc;
|
||||
}
|
||||
ImageEffect::Descriptor *
|
||||
std::shared_ptr<OFX::Host::ImageEffect::Descriptor>
|
||||
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);
|
||||
std::shared_ptr<OFX::Host::ImageEffect::Descriptor> desc =
|
||||
std::make_shared<ImageEffect::Descriptor>(bundlePath, plugin);
|
||||
descriptors_.append(std::shared_ptr<ImageEffect::Descriptor>(desc));
|
||||
return desc;
|
||||
}
|
||||
|
||||
@@ -177,7 +162,7 @@ ImageEffect::Instance* OliveHost::newInstance(void *clientData,
|
||||
instance->setNode(
|
||||
std::shared_ptr<PluginNode>(node, [](PluginNode *) {}));
|
||||
}
|
||||
instances_.append(instance);
|
||||
instances_.append(std::shared_ptr<OlivePluginInstance>(instance));
|
||||
return instance;
|
||||
};
|
||||
OfxStatus olive::plugin::OliveHost::vmessage(const char *type, const char *id, const char *format,
|
||||
@@ -217,7 +202,7 @@ OfxStatus olive::plugin::OliveHost::vmessage(const char *type, const char *id, c
|
||||
|
||||
return kOfxStatOK;
|
||||
}
|
||||
|
||||
// TODO: Persistent messages shouldn't use pop-up window.
|
||||
OfxStatus olive::plugin::OliveHost::setPersistentMessage(
|
||||
const char *type, const char *id, const char *format, va_list args)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <QMap>
|
||||
#include <any>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <qlist.h>
|
||||
namespace olive {
|
||||
namespace plugin {
|
||||
@@ -70,15 +71,15 @@ public:
|
||||
const std::string& context) override;
|
||||
|
||||
|
||||
OFX::Host::ImageEffect::Descriptor *makeDescriptor(
|
||||
std::shared_ptr<OFX::Host::ImageEffect::Descriptor> makeDescriptor(
|
||||
OFX::Host::ImageEffect::ImageEffectPlugin* plugin) override;
|
||||
|
||||
OFX::Host::ImageEffect::Descriptor *makeDescriptor(
|
||||
const OFX::Host::ImageEffect::Descriptor &rootContext,
|
||||
std::shared_ptr<OFX::Host::ImageEffect::Descriptor>
|
||||
makeDescriptor(const OFX::Host::ImageEffect::Descriptor &rootContext,
|
||||
OFX::Host::ImageEffect::ImageEffectPlugin *plugin) override;
|
||||
|
||||
OFX::Host::ImageEffect::Descriptor *makeDescriptor(
|
||||
const std::string &bundlePath,
|
||||
std::shared_ptr<OFX::Host::ImageEffect::Descriptor>
|
||||
makeDescriptor(const std::string &bundlePath,
|
||||
OFX::Host::ImageEffect::ImageEffectPlugin *plugin) override;
|
||||
/// vmessage
|
||||
virtual OfxStatus vmessage(const char *type, const char *id,
|
||||
@@ -98,8 +99,8 @@ public:
|
||||
};
|
||||
#endif
|
||||
private:
|
||||
QList<OFX::Host::ImageEffect::Descriptor*> descriptors_;
|
||||
QList<OFX::Host::ImageEffect::Instance*> instances_;
|
||||
QList<std::shared_ptr<OFX::Host::ImageEffect::Descriptor>> descriptors_;
|
||||
QList<std::shared_ptr<OFX::Host::ImageEffect::Instance>> instances_;
|
||||
QList<HostPersistentMessage> persistent_messages_;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
#include "panel/timeline/timeline.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <QMessageBox>
|
||||
#include <QCoreApplication>
|
||||
#include <QApplication>
|
||||
#include <qmessagebox.h>
|
||||
#include <qobject.h>
|
||||
#include <QCoreApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QMetaObject>
|
||||
#include <QThread>
|
||||
#include <QtGlobal>
|
||||
#include <string.h>
|
||||
#include <QString>
|
||||
@@ -48,6 +48,35 @@ const std::string kImageFieldNoneStr(kOfxImageFieldNone);
|
||||
const std::string kImageFieldUpperStr(kOfxImageFieldUpper);
|
||||
const std::string kImageFieldLowerStr(kOfxImageFieldLower);
|
||||
|
||||
QString FormatOfxMessage(const char *format, va_list args)
|
||||
{
|
||||
char buffer[1024];
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
const int needed = vsnprintf(buffer, sizeof(buffer), format, args_copy);
|
||||
va_end(args_copy);
|
||||
if (needed < 0) {
|
||||
return QString();
|
||||
}
|
||||
if (needed < static_cast<int>(sizeof(buffer))) {
|
||||
return QString::fromUtf8(buffer);
|
||||
}
|
||||
QByteArray dynamic_buffer(needed + 1, 0);
|
||||
const int written = vsnprintf(dynamic_buffer.data(), dynamic_buffer.size(), format, args);
|
||||
if (written < 0) {
|
||||
return QString();
|
||||
}
|
||||
return QString::fromUtf8(dynamic_buffer.constData());
|
||||
}
|
||||
|
||||
bool IsGuiThread()
|
||||
{
|
||||
if (auto *app = QCoreApplication::instance()) {
|
||||
return QThread::currentThread() == app->thread();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string &FieldOrderForParams(const VideoParams ¶ms)
|
||||
{
|
||||
switch (params.interlacing()) {
|
||||
@@ -134,76 +163,102 @@ const std::string &OlivePluginInstance::getDefaultOutputFielding() const
|
||||
OfxStatus OlivePluginInstance::vmessage(const char *type, const char *id,
|
||||
const char *format, va_list args)
|
||||
{
|
||||
char *buffer = new char[1024];
|
||||
|
||||
memset(buffer, 0, 1024 * sizeof(char));
|
||||
vsprintf(buffer, format, args);
|
||||
|
||||
QString message(buffer);
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
if (strncmp(type, kOfxMessageQuestion, strlen(kOfxMessageQuestion))) {
|
||||
auto ret = QMessageBox::information(
|
||||
nullptr, "", message, QMessageBox::Ok, QMessageBox::Cancel);
|
||||
|
||||
if (ret == QMessageBox::Ok) {
|
||||
return kOfxStatReplyYes;
|
||||
} else {
|
||||
return kOfxStatReplyNo;
|
||||
const QString message = FormatOfxMessage(format, args);
|
||||
if (message.isEmpty()) {
|
||||
return kOfxStatFailed;
|
||||
}
|
||||
|
||||
const bool is_question =
|
||||
strncmp(type, kOfxMessageQuestion, strlen(kOfxMessageQuestion)) == 0;
|
||||
OfxStatus result = kOfxStatOK;
|
||||
auto show_message = [&]() {
|
||||
if (is_question) {
|
||||
const auto ret = QMessageBox::question(
|
||||
nullptr, "", message, QMessageBox::Ok, QMessageBox::Cancel);
|
||||
result = (ret == QMessageBox::Ok) ? kOfxStatReplyYes : kOfxStatReplyNo;
|
||||
} else {
|
||||
QMessageBox::information(nullptr, "", message);
|
||||
return kOfxStatOK;
|
||||
result = kOfxStatOK;
|
||||
}
|
||||
};
|
||||
|
||||
if (IsGuiThread()) {
|
||||
show_message();
|
||||
} else if (auto *app = QCoreApplication::instance()) {
|
||||
if (is_question) {
|
||||
QMetaObject::invokeMethod(app, show_message, Qt::BlockingQueuedConnection);
|
||||
} else {
|
||||
QMetaObject::invokeMethod(app, show_message, Qt::QueuedConnection);
|
||||
}
|
||||
} else if (is_question) {
|
||||
result = kOfxStatReplyNo;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
OfxStatus OlivePluginInstance::setPersistentMessage(const char *type, const char *id,
|
||||
const char *format, va_list args)
|
||||
{
|
||||
char *buffer = new char[1024];
|
||||
|
||||
memset(buffer, 0, 1024 * sizeof(char));
|
||||
int ret = vsprintf(buffer, format, args);
|
||||
if (ret < 0) {
|
||||
const QString message = FormatOfxMessage(format, args);
|
||||
if (message.isEmpty()) {
|
||||
return kOfxStatFailed;
|
||||
}
|
||||
QString message(buffer);
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
ErrorType error_type;
|
||||
// If This is a error message
|
||||
if (strncmp(type, kOfxMessageError, strlen(kOfxMessageError)) == 0) {
|
||||
persistentErrors_.append({ ErrorType::Error, message });
|
||||
QMessageBox::critical(nullptr, "", message);
|
||||
// TODO: tell the shell to show the error
|
||||
error_type = ErrorType::Error;
|
||||
}
|
||||
// A warning
|
||||
else if (strncmp(type, kOfxMessageWarning, strlen(kOfxMessageError)) == 0) {
|
||||
persistentErrors_.append({ ErrorType::Warning, message });
|
||||
QMessageBox::warning(nullptr, "", message);
|
||||
// TODO: tell the shell to show the warning
|
||||
|
||||
else if (strncmp(type, kOfxMessageWarning, strlen(kOfxMessageWarning)) == 0) {
|
||||
error_type = ErrorType::Warning;
|
||||
}
|
||||
// A simple information
|
||||
else if (strncmp(type, kOfxMessageMessage, strlen(kOfxMessageError)) == 0) {
|
||||
persistentErrors_.append({ ErrorType::Message, message });
|
||||
QMessageBox::information(nullptr, "", message);
|
||||
else if (strncmp(type, kOfxMessageMessage, strlen(kOfxMessageMessage)) == 0) {
|
||||
error_type = ErrorType::Message;
|
||||
} else {
|
||||
return kOfxStatFailed;
|
||||
}
|
||||
|
||||
auto update_ui = [this, error_type, message]() {
|
||||
persistentErrors_.append({ error_type, message });
|
||||
switch (error_type) {
|
||||
case ErrorType::Error:
|
||||
QMessageBox::critical(nullptr, "", message);
|
||||
break;
|
||||
case ErrorType::Warning:
|
||||
QMessageBox::warning(nullptr, "", message);
|
||||
break;
|
||||
case ErrorType::Message:
|
||||
QMessageBox::information(nullptr, "", message);
|
||||
break;
|
||||
}
|
||||
if (node_) {
|
||||
emit node_->MessageCountChanged();
|
||||
}
|
||||
};
|
||||
|
||||
if (IsGuiThread()) {
|
||||
update_ui();
|
||||
} else if (auto *app = QCoreApplication::instance()) {
|
||||
QMetaObject::invokeMethod(app, update_ui, Qt::QueuedConnection);
|
||||
}
|
||||
return kOfxStatOK;
|
||||
}
|
||||
OfxStatus OlivePluginInstance::clearPersistentMessage()
|
||||
{
|
||||
auto clear_ui = [this]() {
|
||||
persistentErrors_.clear();
|
||||
// TODO: tell the shell to remove message.
|
||||
if (node_) {
|
||||
emit node_->MessageCountChanged();
|
||||
}
|
||||
};
|
||||
if (IsGuiThread()) {
|
||||
clear_ui();
|
||||
} else if (auto *app = QCoreApplication::instance()) {
|
||||
QMetaObject::invokeMethod(app, clear_ui, Qt::QueuedConnection);
|
||||
}
|
||||
return kOfxStatOK;
|
||||
}
|
||||
void OlivePluginInstance::getProjectSize(double &xSize, double &ySize) const
|
||||
|
||||
@@ -65,8 +65,10 @@ protected:
|
||||
int value_ = 0;
|
||||
public:
|
||||
IntegerInstance(std::shared_ptr<PluginNode>node, OFX::Host::Param::Descriptor &descriptor)
|
||||
: _node(node), _descriptor(descriptor),
|
||||
OFX::Host::Param::IntegerInstance(_descriptor){}
|
||||
: OFX::Host::Param::IntegerInstance(descriptor)
|
||||
, _node(node)
|
||||
, _descriptor(descriptor)
|
||||
{}
|
||||
OfxStatus get(int &a)
|
||||
{
|
||||
if (!_node) {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#define PLUGINJOB_H
|
||||
#include "acceleratedjob.h"
|
||||
#include "pluginSupport/OlivePluginInstance.h"
|
||||
#include "olive/core/util/rational.h"
|
||||
|
||||
#include <any>
|
||||
#include <chrono>
|
||||
@@ -31,13 +32,20 @@ namespace plugin {
|
||||
class PluginJob :public AcceleratedJob{
|
||||
public:
|
||||
explicit PluginJob(const OFX::Host::ImageEffect::Instance* pluginInstance,
|
||||
const PluginNode* node, NodeValueRow row)
|
||||
const PluginNode* node, NodeValueRow row,
|
||||
const olive::core::rational &time)
|
||||
: AcceleratedJob()
|
||||
, time_seconds_(time.toDouble())
|
||||
{
|
||||
this->pluginInstance_ = pluginInstance;
|
||||
this->node_=node;
|
||||
Insert(row);
|
||||
}
|
||||
explicit PluginJob(const OFX::Host::ImageEffect::Instance* pluginInstance,
|
||||
const PluginNode* node, NodeValueRow row)
|
||||
: PluginJob(pluginInstance, node, row, olive::core::rational(0))
|
||||
{
|
||||
}
|
||||
|
||||
PluginNode *node() const {
|
||||
return const_cast<PluginNode *>(node_);
|
||||
@@ -47,6 +55,10 @@ public:
|
||||
return const_cast<OFX::Host::ImageEffect::Instance*>(pluginInstance_);
|
||||
}
|
||||
|
||||
double time_seconds() const {
|
||||
return time_seconds_;
|
||||
}
|
||||
|
||||
private:
|
||||
const OFX::Host::ImageEffect::Instance *pluginInstance_=nullptr;
|
||||
|
||||
@@ -55,6 +67,7 @@ private:
|
||||
QHash<QString, std::any> params;
|
||||
|
||||
const PluginNode *node_=nullptr;
|
||||
double time_seconds_ = 0.0;
|
||||
};
|
||||
|
||||
} // plugin
|
||||
|
||||
@@ -151,8 +151,9 @@ void OpenGLRenderer::DestroyInternal()
|
||||
if (context_) {
|
||||
GL_PREAMBLE;
|
||||
|
||||
// Delete framebuffer
|
||||
if (functions_ && framebuffer_) {
|
||||
functions_->glDeleteFramebuffers(1, &framebuffer_);
|
||||
}
|
||||
framebuffer_ = 0;
|
||||
|
||||
// Delete context if it belongs to us
|
||||
@@ -160,6 +161,7 @@ void OpenGLRenderer::DestroyInternal()
|
||||
delete context_;
|
||||
}
|
||||
context_ = nullptr;
|
||||
functions_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,11 +337,28 @@ void OpenGLRenderer::DownloadFromTexture(const QVariant &id,
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
|
||||
if (!EnsureContextCurrent(__FUNCTION__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLuint texture_id = id.value<GLuint>();
|
||||
if (!texture_id || !functions_->glIsTexture(texture_id)) {
|
||||
qWarning() << "DownloadFromTexture called with invalid texture";
|
||||
return;
|
||||
}
|
||||
|
||||
GLint current_tex;
|
||||
functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_tex);
|
||||
|
||||
AttachTextureAsDestination(id);
|
||||
|
||||
GLenum status = functions_->glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
qWarning() << "DownloadFromTexture framebuffer incomplete" << status;
|
||||
DetachTextureAsDestination();
|
||||
return;
|
||||
}
|
||||
|
||||
functions_->glPixelStorei(GL_PACK_ROW_LENGTH, linesize);
|
||||
|
||||
{
|
||||
@@ -921,4 +940,39 @@ GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code)
|
||||
return shader;
|
||||
}
|
||||
|
||||
bool OpenGLRenderer::EnsureContextCurrent(const char *caller)
|
||||
{
|
||||
if (!context_) {
|
||||
qWarning() << caller << "called without an OpenGL context";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (QOpenGLContext::currentContext() != context_) {
|
||||
if (context_->parent() == this && surface_.isValid()) {
|
||||
if (!context_->makeCurrent(&surface_)) {
|
||||
qWarning() << caller << "failed to make context current";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qWarning() << caller << "OpenGL context not current";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!functions_) {
|
||||
functions_ = context_->functions();
|
||||
}
|
||||
|
||||
if (!functions_) {
|
||||
qWarning() << caller << "OpenGL functions not available";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!framebuffer_) {
|
||||
functions_->glGenFramebuffers(1, &framebuffer_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -99,6 +99,8 @@ private:
|
||||
|
||||
static GLenum GetPixelFormat(int channel_count);
|
||||
|
||||
bool EnsureContextCurrent(const char *caller);
|
||||
|
||||
void PrepareInputTexture(GLenum target, Texture::Interpolation interp);
|
||||
|
||||
void ClearDestinationInternal(double r = 0.0, double g = 0.0,
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
//
|
||||
// Created by mikesolar on 25-10-19.
|
||||
//
|
||||
#include "render/texture.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <qtypes.h>
|
||||
@@ -31,6 +34,7 @@
|
||||
#include "common/ffmpegutils.h"
|
||||
#include "ofxImageEffect.h"
|
||||
#include "ofxhUtilities.h"
|
||||
#include "ofxGPURender.h"
|
||||
extern "C"{
|
||||
#include <libavutil/pixfmt.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
@@ -280,7 +284,7 @@ static const char *GetRenderFieldForParams(const olive::VideoParams ¶ms)
|
||||
return kOfxImageFieldNone;
|
||||
}
|
||||
|
||||
static olive::AVFramePtr ReadbackTextureToFrame(olive::Texture *texture,
|
||||
static olive::AVFramePtr ReadbackTextureToFrame(olive::TexturePtr texture,
|
||||
const olive::VideoParams ¶ms)
|
||||
{
|
||||
if (!texture || texture->IsDummy()) {
|
||||
@@ -307,8 +311,11 @@ static olive::AVFramePtr ReadbackTextureToFrame(olive::Texture *texture,
|
||||
}
|
||||
|
||||
if (texture->renderer()) {
|
||||
const int linesize_pixels =
|
||||
olive::plugin::detail::BytesToPixels(frame->linesize[0],
|
||||
params);
|
||||
texture->renderer()->DownloadFromTexture(
|
||||
texture->id(), params, frame->data[0], frame->linesize[0]);
|
||||
texture->id(), params, frame->data[0], linesize_pixels);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
@@ -327,9 +334,11 @@ static olive::AVFramePtr ReadbackTextureToFrame(olive::Texture *texture,
|
||||
}
|
||||
|
||||
if (texture->renderer()) {
|
||||
const int linesize_pixels =
|
||||
olive::plugin::detail::BytesToPixels(rgba_frame->linesize[0],
|
||||
rgba_params);
|
||||
texture->renderer()->DownloadFromTexture(
|
||||
texture->id(), rgba_params, rgba_frame->data[0],
|
||||
rgba_frame->linesize[0]);
|
||||
texture->id(), rgba_params, rgba_frame->data[0], linesize_pixels);
|
||||
}
|
||||
|
||||
olive::AVFramePtr dst = olive::CreateAVFramePtr();
|
||||
@@ -356,6 +365,18 @@ static olive::AVFramePtr ReadbackTextureToFrame(olive::Texture *texture,
|
||||
return dst;
|
||||
}
|
||||
|
||||
int olive::plugin::detail::BytesToPixels(int byte_linesize,
|
||||
const olive::VideoParams ¶ms)
|
||||
{
|
||||
const int bytes_per_pixel =
|
||||
olive::VideoParams::GetBytesPerPixel(params.format(),
|
||||
params.channel_count());
|
||||
if (byte_linesize <= 0 || bytes_per_pixel <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return byte_linesize / bytes_per_pixel;
|
||||
}
|
||||
|
||||
static olive::AVFramePtr ConvertFrameIfNeeded(olive::AVFramePtr src,
|
||||
const olive::VideoParams &dst_params)
|
||||
{
|
||||
@@ -382,6 +403,240 @@ static olive::AVFramePtr ConvertFrameIfNeeded(olive::AVFramePtr src,
|
||||
return src;
|
||||
}
|
||||
|
||||
auto float_channels = [](AVPixelFormat fmt) -> int {
|
||||
switch (fmt) {
|
||||
case AV_PIX_FMT_GRAYF32LE:
|
||||
case AV_PIX_FMT_GRAYF32BE:
|
||||
return 1;
|
||||
case AV_PIX_FMT_RGBF32LE:
|
||||
case AV_PIX_FMT_RGBF32BE:
|
||||
return 3;
|
||||
case AV_PIX_FMT_RGBAF32LE:
|
||||
case AV_PIX_FMT_RGBAF32BE:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
auto dst_packed_info = [](AVPixelFormat fmt, int *channels,
|
||||
int *bytes_per_component) -> bool {
|
||||
switch (fmt) {
|
||||
case AV_PIX_FMT_GRAY8:
|
||||
*channels = 1;
|
||||
*bytes_per_component = 1;
|
||||
return true;
|
||||
case AV_PIX_FMT_RGB24:
|
||||
*channels = 3;
|
||||
*bytes_per_component = 1;
|
||||
return true;
|
||||
case AV_PIX_FMT_RGBA:
|
||||
*channels = 4;
|
||||
*bytes_per_component = 1;
|
||||
return true;
|
||||
case AV_PIX_FMT_GRAY16LE:
|
||||
*channels = 1;
|
||||
*bytes_per_component = 2;
|
||||
return true;
|
||||
case AV_PIX_FMT_RGB48LE:
|
||||
*channels = 3;
|
||||
*bytes_per_component = 2;
|
||||
return true;
|
||||
case AV_PIX_FMT_RGBA64LE:
|
||||
*channels = 4;
|
||||
*bytes_per_component = 2;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto float_dst_from_packed = [&](const olive::AVFramePtr &packed_src,
|
||||
AVPixelFormat float_fmt,
|
||||
const olive::AVFramePtr &float_dst) -> bool {
|
||||
if (!packed_src || !float_dst || !packed_src->data[0] ||
|
||||
!float_dst->data[0]) {
|
||||
return false;
|
||||
}
|
||||
const int dst_channels = float_channels(float_fmt);
|
||||
if (dst_channels == 0) {
|
||||
return false;
|
||||
}
|
||||
int src_channels = 0;
|
||||
int bytes_per_component = 0;
|
||||
if (!dst_packed_info(static_cast<AVPixelFormat>(packed_src->format),
|
||||
&src_channels, &bytes_per_component)) {
|
||||
return false;
|
||||
}
|
||||
const float inv_scale =
|
||||
(bytes_per_component == 2) ? (1.0f / 65535.0f)
|
||||
: (1.0f / 255.0f);
|
||||
for (int y = 0; y < packed_src->height; ++y) {
|
||||
const uint8_t *src_row =
|
||||
packed_src->data[0] + y * packed_src->linesize[0];
|
||||
float *dst_row = reinterpret_cast<float *>(
|
||||
float_dst->data[0] + y * float_dst->linesize[0]);
|
||||
if (bytes_per_component == 2) {
|
||||
const uint16_t *src_u16 =
|
||||
reinterpret_cast<const uint16_t *>(src_row);
|
||||
for (int x = 0; x < packed_src->width; ++x) {
|
||||
const uint16_t *pix = src_u16 + x * src_channels;
|
||||
float r = pix[0] * inv_scale;
|
||||
float g = (src_channels > 1) ? pix[1] * inv_scale : r;
|
||||
float b = (src_channels > 2) ? pix[2] * inv_scale : r;
|
||||
float a = (src_channels > 3) ? pix[3] * inv_scale : 1.0f;
|
||||
dst_row[x * dst_channels + 0] = r;
|
||||
if (dst_channels > 1) {
|
||||
dst_row[x * dst_channels + 1] = g;
|
||||
}
|
||||
if (dst_channels > 2) {
|
||||
dst_row[x * dst_channels + 2] = b;
|
||||
}
|
||||
if (dst_channels > 3) {
|
||||
dst_row[x * dst_channels + 3] = a;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int x = 0; x < packed_src->width; ++x) {
|
||||
const uint8_t *pix = src_row + x * src_channels;
|
||||
float r = pix[0] * inv_scale;
|
||||
float g = (src_channels > 1) ? pix[1] * inv_scale : r;
|
||||
float b = (src_channels > 2) ? pix[2] * inv_scale : r;
|
||||
float a = (src_channels > 3) ? pix[3] * inv_scale : 1.0f;
|
||||
dst_row[x * dst_channels + 0] = r;
|
||||
if (dst_channels > 1) {
|
||||
dst_row[x * dst_channels + 1] = g;
|
||||
}
|
||||
if (dst_channels > 2) {
|
||||
dst_row[x * dst_channels + 2] = b;
|
||||
}
|
||||
if (dst_channels > 3) {
|
||||
dst_row[x * dst_channels + 3] = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const int dst_float_channels = float_channels(dst_fmt);
|
||||
if (dst_float_channels > 0) {
|
||||
int src_channels = 0;
|
||||
int bytes_per_component = 0;
|
||||
if (dst_packed_info(static_cast<AVPixelFormat>(src->format),
|
||||
&src_channels, &bytes_per_component)) {
|
||||
if (float_dst_from_packed(src, dst_fmt, dst)) {
|
||||
return dst;
|
||||
}
|
||||
} else {
|
||||
olive::AVFramePtr packed = olive::CreateAVFramePtr();
|
||||
AVPixelFormat packed_fmt = (dst_float_channels == 4)
|
||||
? AV_PIX_FMT_RGBA
|
||||
: (dst_float_channels == 3)
|
||||
? AV_PIX_FMT_RGB24
|
||||
: AV_PIX_FMT_GRAY8;
|
||||
packed->format = packed_fmt;
|
||||
packed->width = dst->width;
|
||||
packed->height = dst->height;
|
||||
if (av_frame_get_buffer(packed.get(), 0) >= 0) {
|
||||
SwsContext *pre_ctx = sws_getContext(
|
||||
src->width, src->height,
|
||||
static_cast<AVPixelFormat>(src->format),
|
||||
packed->width, packed->height, packed_fmt, SWS_POINT,
|
||||
nullptr, nullptr, nullptr);
|
||||
if (pre_ctx) {
|
||||
sws_scale(pre_ctx, src->data, src->linesize, 0, src->height,
|
||||
packed->data, packed->linesize);
|
||||
sws_freeContext(pre_ctx);
|
||||
if (float_dst_from_packed(packed, dst_fmt, dst)) {
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto clamp01 = [](float v) -> float {
|
||||
return std::clamp(v, 0.0f, 1.0f);
|
||||
};
|
||||
|
||||
const int src_float_channels = float_channels(
|
||||
static_cast<AVPixelFormat>(src->format));
|
||||
if (src_float_channels > 0) {
|
||||
int dst_channels = 0;
|
||||
int bytes_per_component = 0;
|
||||
if (dst_packed_info(dst_fmt, &dst_channels, &bytes_per_component) &&
|
||||
src->data[0] && dst->data[0]) {
|
||||
for (int y = 0; y < src->height; ++y) {
|
||||
const float *src_row = reinterpret_cast<const float *>(
|
||||
src->data[0] + y * src->linesize[0]);
|
||||
uint8_t *dst_row = dst->data[0] + y * dst->linesize[0];
|
||||
if (bytes_per_component == 2) {
|
||||
auto *dst_row_u16 =
|
||||
reinterpret_cast<uint16_t *>(dst_row);
|
||||
for (int x = 0; x < src->width; ++x) {
|
||||
const float *pix =
|
||||
src_row + x * src_float_channels;
|
||||
float r = pix[0];
|
||||
float g = (src_float_channels > 1) ? pix[1] : r;
|
||||
float b = (src_float_channels > 2) ? pix[2] : r;
|
||||
float a = (src_float_channels > 3) ? pix[3] : 1.0f;
|
||||
if (dst_channels == 1) {
|
||||
float luma = 0.2126f * r + 0.7152f * g + 0.0722f * b;
|
||||
dst_row_u16[x] = static_cast<uint16_t>(
|
||||
std::lround(clamp01(luma) * 65535.0f));
|
||||
continue;
|
||||
}
|
||||
dst_row_u16[x * dst_channels + 0] =
|
||||
static_cast<uint16_t>(
|
||||
std::lround(clamp01(r) * 65535.0f));
|
||||
dst_row_u16[x * dst_channels + 1] =
|
||||
static_cast<uint16_t>(
|
||||
std::lround(clamp01(g) * 65535.0f));
|
||||
dst_row_u16[x * dst_channels + 2] =
|
||||
static_cast<uint16_t>(
|
||||
std::lround(clamp01(b) * 65535.0f));
|
||||
if (dst_channels == 4) {
|
||||
dst_row_u16[x * dst_channels + 3] =
|
||||
static_cast<uint16_t>(
|
||||
std::lround(clamp01(a) * 65535.0f));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int x = 0; x < src->width; ++x) {
|
||||
const float *pix =
|
||||
src_row + x * src_float_channels;
|
||||
float r = pix[0];
|
||||
float g = (src_float_channels > 1) ? pix[1] : r;
|
||||
float b = (src_float_channels > 2) ? pix[2] : r;
|
||||
float a = (src_float_channels > 3) ? pix[3] : 1.0f;
|
||||
if (dst_channels == 1) {
|
||||
float luma = 0.2126f * r + 0.7152f * g + 0.0722f * b;
|
||||
dst_row[x] = static_cast<uint8_t>(
|
||||
std::lround(clamp01(luma) * 255.0f));
|
||||
continue;
|
||||
}
|
||||
dst_row[x * dst_channels + 0] =
|
||||
static_cast<uint8_t>(
|
||||
std::lround(clamp01(r) * 255.0f));
|
||||
dst_row[x * dst_channels + 1] =
|
||||
static_cast<uint8_t>(
|
||||
std::lround(clamp01(g) * 255.0f));
|
||||
dst_row[x * dst_channels + 2] =
|
||||
static_cast<uint8_t>(
|
||||
std::lround(clamp01(b) * 255.0f));
|
||||
if (dst_channels == 4) {
|
||||
dst_row[x * dst_channels + 3] =
|
||||
static_cast<uint8_t>(
|
||||
std::lround(clamp01(a) * 255.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
|
||||
SwsContext *sws_ctx = sws_getContext(
|
||||
src->width, src->height, static_cast<AVPixelFormat>(src->format),
|
||||
dst->width, dst->height, dst_fmt, SWS_POINT,
|
||||
@@ -407,6 +662,42 @@ static int LinesizeToPixels(const olive::VideoParams ¶ms, int linesize_bytes
|
||||
return linesize_bytes / bytes_per_pixel;
|
||||
}
|
||||
|
||||
static olive::TexturePtr ConvertTextureForParams(olive::TexturePtr src,
|
||||
const olive::VideoParams &dst_params)
|
||||
{
|
||||
if (!src) {
|
||||
return nullptr;
|
||||
}
|
||||
const olive::VideoParams &src_params = src->params();
|
||||
if (src_params.format() == dst_params.format() &&
|
||||
src_params.channel_count() == dst_params.channel_count() &&
|
||||
src_params.width() == dst_params.width() &&
|
||||
src_params.height() == dst_params.height()) {
|
||||
return src;
|
||||
}
|
||||
|
||||
olive::AVFramePtr frame = src->frame();
|
||||
if (!frame || !frame->data[0]) {
|
||||
frame = ReadbackTextureToFrame(src, src_params);
|
||||
}
|
||||
if (!frame || !frame->data[0]) {
|
||||
return src;
|
||||
}
|
||||
|
||||
olive::AVFramePtr converted = ConvertFrameIfNeeded(frame, dst_params);
|
||||
if (!converted || !converted->data[0]) {
|
||||
return src;
|
||||
}
|
||||
|
||||
auto dst = std::make_shared<olive::Texture>(dst_params);
|
||||
int linesize_pixels = LinesizeToPixels(dst_params, converted->linesize[0]);
|
||||
if (linesize_pixels <= 0) {
|
||||
linesize_pixels = dst_params.effective_width();
|
||||
}
|
||||
dst->Upload(converted->data[0], linesize_pixels);
|
||||
return dst;
|
||||
}
|
||||
|
||||
static QString PluginIdForInstance(const OFX::Host::ImageEffect::Instance *instance)
|
||||
{
|
||||
if (!instance) {
|
||||
@@ -432,15 +723,15 @@ static void LogOfxFailure(const char *action, OfxStatus stat,
|
||||
<< "(" << stat << ")";
|
||||
}
|
||||
|
||||
static void MarkRenderFailure(olive::Texture *destination)
|
||||
static void MarkRenderFailure(olive::TexturePtr destination)
|
||||
{
|
||||
if (destination && destination->renderer()) {
|
||||
destination->renderer()->ClearDestination(destination, 1.0, 0.0, 1.0, 1.0);
|
||||
destination->renderer()->ClearDestination(destination.get(), 1.0, 0.0, 1.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::PluginJob& job,
|
||||
olive::Texture *destination,
|
||||
olive::TexturePtr destination,
|
||||
olive::VideoParams destination_params,
|
||||
bool clear_destination, bool interactive)
|
||||
{
|
||||
@@ -448,10 +739,18 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
bool supports_opengl = false;
|
||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||
const std::string &gl_supported =
|
||||
instance->getDescriptor().getProps().getStringProperty(
|
||||
kOfxImageEffectPropOpenGLRenderSupported);
|
||||
supports_opengl = (gl_supported == "true" || gl_supported == "1");
|
||||
#endif
|
||||
auto *olive_instance =
|
||||
dynamic_cast<olive::plugin::OlivePluginInstance *>(instance);
|
||||
const bool use_opengl =
|
||||
destination && destination->renderer() && destination->id().isValid();
|
||||
supports_opengl && destination && destination->renderer() &&
|
||||
destination->id().isValid();
|
||||
if (olive_instance) {
|
||||
olive_instance->setOpenGLEnabled(use_opengl);
|
||||
olive_instance->setVideoParam(destination_params);
|
||||
@@ -501,7 +800,7 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
}
|
||||
|
||||
// call get region of interest on each of the inputs
|
||||
OfxTime frame = 0;
|
||||
OfxTime frame = job.time_seconds();
|
||||
|
||||
const NodeValueRow &values = job.GetValues();
|
||||
const auto &clips = instance->getDescriptor().getClips();
|
||||
@@ -523,7 +822,6 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
}
|
||||
if (input_tex) {
|
||||
input_textures[entry.first] = input_tex;
|
||||
input_clip->setInputTexture(input_tex, frame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,6 +836,8 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
olive::VideoParams output_params = destination_params;
|
||||
if (ApplyClipPreferencesToParams(*clip, &output_params)) {
|
||||
clip->setParams(output_params);
|
||||
@@ -551,15 +851,27 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
}
|
||||
olive::VideoParams input_params = entry.second->params();
|
||||
if (ApplyClipPreferencesToParams(*input_clip, &input_params)) {
|
||||
/*
|
||||
input_params.set_width(entry.second->params().width());
|
||||
input_params.set_height(entry.second->params().height());
|
||||
input_params.set_depth(entry.second->params().depth());
|
||||
input_params.set_pixel_aspect_ratio(
|
||||
entry.second->params().pixel_aspect_ratio());
|
||||
input_params.set_interlacing(entry.second->params().interlacing());
|
||||
input_params.set_premultiplied_alpha(
|
||||
entry.second->params().premultiplied_alpha());
|
||||
input_params.set_divider(entry.second->params().divider());*/
|
||||
input_clip->setParams(input_params);
|
||||
}
|
||||
input_clip->setInputTexture(entry.second, frame);
|
||||
olive::TexturePtr converted =
|
||||
ConvertTextureForParams(entry.second, input_params);
|
||||
input_clip->setInputTexture(converted, frame);
|
||||
}
|
||||
|
||||
clip->setRegionOfDefinition(regionOfDefinition, frame);
|
||||
clip->setOutputTexture(destination, frame);
|
||||
|
||||
stat = instance->beginRenderAction(0, numFramesToRender,
|
||||
stat = instance->beginRenderAction(frame, numFramesToRender,
|
||||
1.0, false, renderScale, true,
|
||||
interactive);
|
||||
if (stat != kOfxStatOK && stat != kOfxStatReplyDefault) {
|
||||
@@ -589,14 +901,24 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
return;
|
||||
}
|
||||
|
||||
if (!output_params.is_valid()) {
|
||||
qWarning().noquote()
|
||||
<< "OFX render skipped due to invalid output params for plugin="
|
||||
<< PluginIdForInstance(instance);
|
||||
MarkRenderFailure(destination);
|
||||
instance->endRenderAction(frame, numFramesToRender, 1.0, interactive,
|
||||
renderScale, true, interactive);
|
||||
return;
|
||||
}
|
||||
|
||||
// render a frame
|
||||
const char *render_field = GetRenderFieldForParams(destination_params);
|
||||
stat = instance->renderAction(0, render_field, renderWindow, renderScale,
|
||||
stat = instance->renderAction(frame, render_field, renderWindow, renderScale,
|
||||
true, interactive, interactive);
|
||||
if (stat != kOfxStatOK && stat != kOfxStatReplyDefault) {
|
||||
LogOfxFailure("render", stat, instance);
|
||||
MarkRenderFailure(destination);
|
||||
instance->endRenderAction(0, numFramesToRender, 1.0, interactive,
|
||||
instance->endRenderAction(frame, numFramesToRender, 1.0, interactive,
|
||||
renderScale, true, interactive);
|
||||
return;
|
||||
}
|
||||
@@ -636,19 +958,27 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
qWarning().noquote()
|
||||
<< "OFX output image conversion failed for plugin="
|
||||
<< PluginIdForInstance(instance);
|
||||
instance->endRenderAction(0, numFramesToRender, 1.0, interactive,
|
||||
instance->endRenderAction(frame, numFramesToRender, 1.0, interactive,
|
||||
renderScale, true, interactive);
|
||||
return;
|
||||
}
|
||||
AVFramePtr converted = ConvertFrameIfNeeded(frame_ptr, destination_params);
|
||||
const AVPixelFormat expected_fmt =
|
||||
GetDestinationAVPixelFormat(destination_params);
|
||||
destination->handleFrame(converted);
|
||||
if (destination->renderer() && converted && converted->data[0]) {
|
||||
if (destination->renderer() && converted && converted->data[0] &&
|
||||
(expected_fmt == AV_PIX_FMT_NONE ||
|
||||
converted->format == expected_fmt)) {
|
||||
int linesize_pixels =
|
||||
LinesizeToPixels(destination_params, converted->linesize[0]);
|
||||
if (linesize_pixels <= 0) {
|
||||
linesize_pixels = destination_params.effective_width();
|
||||
}
|
||||
destination->Upload(converted->data[0], linesize_pixels);
|
||||
} else if (destination->renderer() && converted && converted->data[0]) {
|
||||
qWarning().noquote()
|
||||
<< "OFX output pixel format mismatch for plugin="
|
||||
<< PluginIdForInstance(instance);
|
||||
}
|
||||
} else {
|
||||
AVFramePtr frame_ptr =
|
||||
@@ -669,7 +999,7 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
||||
|
||||
}
|
||||
|
||||
void olive::plugin::PluginRenderer::AttachOutputTexture(olive::Texture *texture)
|
||||
void olive::plugin::PluginRenderer::AttachOutputTexture(olive::TexturePtr texture)
|
||||
{
|
||||
if (!texture) {
|
||||
return;
|
||||
|
||||
@@ -37,15 +37,18 @@
|
||||
namespace olive
|
||||
{
|
||||
namespace plugin{
|
||||
namespace detail {
|
||||
int BytesToPixels(int byte_linesize, const olive::VideoParams ¶ms);
|
||||
}
|
||||
class PluginRenderer : public olive::OpenGLRenderer{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PluginRenderer(QObject *parent=nullptr):OpenGLRenderer(parent){};
|
||||
virtual ~PluginRenderer() override{};
|
||||
void AttachOutputTexture(olive::Texture *texture);
|
||||
void AttachOutputTexture(olive::TexturePtr texture);
|
||||
void DetachOutputTexture();
|
||||
void RenderPlugin(TexturePtr src, olive::plugin::PluginJob& job,
|
||||
olive::Texture *destination,
|
||||
olive::TexturePtr destination,
|
||||
olive::VideoParams destination_params,
|
||||
bool clear_destination, bool interactive);
|
||||
|
||||
|
||||
@@ -638,7 +638,7 @@ TexturePtr RenderProcessor::ProcessPluginJob(TexturePtr texture,
|
||||
plugin_renderer_->RenderPlugin(
|
||||
texture,
|
||||
*plugin_job,
|
||||
destination.get(),
|
||||
destination,
|
||||
destination->params(),
|
||||
true,
|
||||
false);
|
||||
|
||||
@@ -59,16 +59,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
void SetChannelLayout(AVChannelLayout &ch)
|
||||
{
|
||||
for (int i = 0; i < this->count(); i++) {
|
||||
if (this->itemData(i).toULongLong() == ch.u.mask) {
|
||||
this->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void SetChannelLayout(AVChannelLayout &&ch)
|
||||
void SetChannelLayout(const AVChannelLayout &ch)
|
||||
{
|
||||
for (int i = 0; i < this->count(); i++) {
|
||||
if (this->itemData(i).toULongLong() == ch.u.mask) {
|
||||
|
||||
@@ -18,7 +18,10 @@ add_executable(olive-gtest
|
||||
plugin_support_test.cpp
|
||||
plugin_support_image_test.cpp
|
||||
plugin_support_clip_test.cpp
|
||||
plugin_support_param_test.cpp
|
||||
opengl_readback_guard_test.cpp
|
||||
plugin_render_pipeline_test.cpp
|
||||
plugin_renderer_readback_test.cpp
|
||||
plugin_ofx_integration_test.cpp
|
||||
codec_frame_test.cpp
|
||||
codec_exportcodec_test.cpp
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <QOpenGLContext>
|
||||
#include <QVariant>
|
||||
|
||||
#include "render/opengl/openglrenderer.h"
|
||||
|
||||
TEST(OpenGLRenderer, DownloadFromTextureWithoutCurrentContext)
|
||||
{
|
||||
QOpenGLContext context;
|
||||
ASSERT_TRUE(context.create());
|
||||
ASSERT_EQ(QOpenGLContext::currentContext(), nullptr);
|
||||
|
||||
olive::OpenGLRenderer renderer;
|
||||
renderer.Init(&context);
|
||||
|
||||
olive::VideoParams params(4, 4, olive::core::PixelFormat::U8, 4,
|
||||
olive::core::rational(1, 1),
|
||||
olive::VideoParams::kInterlaceNone, 1);
|
||||
|
||||
unsigned char buffer[4 * 4 * 4] = {};
|
||||
renderer.DownloadFromTexture(QVariant::fromValue<GLuint>(0), params,
|
||||
buffer, 4 * 4);
|
||||
|
||||
EXPECT_EQ(QOpenGLContext::currentContext(), nullptr);
|
||||
}
|
||||
@@ -114,7 +114,7 @@ TEST(PluginIntegration, ChromaKeyerCreateAndRender)
|
||||
olive::TexturePtr output = std::make_shared<olive::Texture>(params);
|
||||
|
||||
olive::plugin::PluginRenderer renderer;
|
||||
renderer.RenderPlugin(input, job, output.get(), params, true, false);
|
||||
renderer.RenderPlugin(input, job, output, params, true, false);
|
||||
|
||||
EXPECT_TRUE(output->frame());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "render/plugin/pluginrenderer.h"
|
||||
|
||||
TEST(PluginRendererReadback, BytesToPixels)
|
||||
{
|
||||
olive::VideoParams params(16, 16, olive::core::PixelFormat::U8, 4,
|
||||
olive::core::rational(1, 1),
|
||||
olive::VideoParams::kInterlaceNone, 1);
|
||||
|
||||
const int bytes_per_pixel =
|
||||
olive::VideoParams::GetBytesPerPixel(params.format(),
|
||||
params.channel_count());
|
||||
ASSERT_EQ(bytes_per_pixel, 4);
|
||||
|
||||
EXPECT_EQ(olive::plugin::detail::BytesToPixels(64, params), 16);
|
||||
EXPECT_EQ(olive::plugin::detail::BytesToPixels(0, params), 0);
|
||||
EXPECT_EQ(olive::plugin::detail::BytesToPixels(-1, params), 0);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "ofxParam.h"
|
||||
#include "ofxhParam.h"
|
||||
#include "pluginSupport/paraminstance.h"
|
||||
|
||||
TEST(PluginSupportParam, IntegerInstanceNullNodeRoundTrip)
|
||||
{
|
||||
OFX::Host::Param::Descriptor descriptor(kOfxParamTypeInteger,
|
||||
"TestInteger");
|
||||
olive::plugin::IntegerInstance instance(nullptr, descriptor);
|
||||
|
||||
int value = -1;
|
||||
EXPECT_EQ(instance.get(value), kOfxStatOK);
|
||||
EXPECT_EQ(value, 0);
|
||||
|
||||
EXPECT_EQ(instance.set(7), kOfxStatOK);
|
||||
EXPECT_EQ(instance.get(value), kOfxStatOK);
|
||||
EXPECT_EQ(value, 7);
|
||||
|
||||
int time_value = -1;
|
||||
EXPECT_EQ(instance.get(1.0, time_value), kOfxStatOK);
|
||||
EXPECT_EQ(time_value, 7);
|
||||
}
|
||||
@@ -78,13 +78,13 @@ namespace OFX {
|
||||
virtual bool pluginSupported(ImageEffectPlugin *plugin, std::string &reason) const;
|
||||
|
||||
/// Override this to create a descriptor, this makes the 'root' descriptor
|
||||
virtual Descriptor *makeDescriptor(ImageEffectPlugin* plugin) = 0;
|
||||
virtual std::shared_ptr<Descriptor> makeDescriptor(ImageEffectPlugin* plugin) = 0;
|
||||
|
||||
/// used to construct a context description, rootContext is the main context
|
||||
virtual Descriptor *makeDescriptor(const Descriptor &rootContext, ImageEffectPlugin *plug) = 0;
|
||||
virtual std::shared_ptr<Descriptor> makeDescriptor(const Descriptor &rootContext, ImageEffectPlugin *plug) = 0;
|
||||
|
||||
/// used to construct populate the cache
|
||||
virtual Descriptor *makeDescriptor(const std::string &bundlePath, ImageEffectPlugin *plug) = 0;
|
||||
virtual std::shared_ptr<Descriptor> makeDescriptor(const std::string &bundlePath, ImageEffectPlugin *plug) = 0;
|
||||
|
||||
/// Override this to initialise an image effect descriptor after it has been
|
||||
/// created.
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
|
||||
// this comes off Descriptor's property set after a describe
|
||||
// context independent
|
||||
Descriptor *_baseDescriptor; /// NEEDS TO BE MADE WITH A FACTORY FUNCTION ON THE HOST!!!!!!
|
||||
std::shared_ptr<Descriptor> _baseDescriptor; /// NEEDS TO BE MADE WITH A FACTORY FUNCTION ON THE HOST!!!!!!
|
||||
|
||||
/// map to store contexts in
|
||||
std::map<std::string, std::unique_ptr<Descriptor>> _contexts;
|
||||
std::map<std::string, std::shared_ptr<Descriptor>> _contexts;
|
||||
|
||||
mutable std::set<std::string> _knownContexts;
|
||||
mutable bool _madeKnownContexts;
|
||||
@@ -75,7 +75,7 @@
|
||||
Descriptor *getContext(const std::string &context);
|
||||
|
||||
void addContext(const std::string &context);
|
||||
void addContext(const std::string &context, std::unique_ptr<Descriptor> ied);
|
||||
void addContext(const std::string &context, std::shared_ptr<Descriptor> ied);
|
||||
|
||||
virtual void saveXML(std::ostream &os);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <ctype.h>
|
||||
@@ -83,7 +84,6 @@ namespace OFX {
|
||||
} CatchAllSetStatus(stat, gImageEffectHost, op, kOfxActionUnload);
|
||||
(void)stat;
|
||||
}
|
||||
delete _baseDescriptor;
|
||||
}
|
||||
|
||||
APICache::PluginAPICacheI &ImageEffectPlugin::getApiHandler()
|
||||
@@ -102,9 +102,9 @@ namespace OFX {
|
||||
return *_baseDescriptor;
|
||||
}
|
||||
|
||||
void ImageEffectPlugin::addContext(const std::string &context, std::unique_ptr<Descriptor> ied)
|
||||
void ImageEffectPlugin::addContext(const std::string &context, std::shared_ptr<Descriptor> ied)
|
||||
{
|
||||
_contexts[context] = std::move(ied);
|
||||
_contexts[context] = ied;
|
||||
_knownContexts.insert(context);
|
||||
_madeKnownContexts = true;
|
||||
}
|
||||
@@ -190,7 +190,7 @@ namespace OFX {
|
||||
|
||||
Descriptor *ImageEffectPlugin::getContext(const std::string &context)
|
||||
{
|
||||
std::map<std::string, std::unique_ptr<Descriptor>>::iterator it = _contexts.find(context);
|
||||
std::map<std::string, std::shared_ptr<Descriptor>>::iterator it = _contexts.find(context);
|
||||
|
||||
if (it != _contexts.end()) {
|
||||
//printf("found context description.\n");
|
||||
@@ -214,7 +214,7 @@ namespace OFX {
|
||||
if (!ph) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<ImageEffect::Descriptor> newContext( gImageEffectHost->makeDescriptor(getDescriptor(), this));
|
||||
std::shared_ptr<ImageEffect::Descriptor> newContext( gImageEffectHost->makeDescriptor(getDescriptor(), this));
|
||||
|
||||
OfxStatus stat;
|
||||
try {
|
||||
@@ -463,9 +463,9 @@ namespace OFX {
|
||||
}
|
||||
|
||||
if (el == "context") {
|
||||
std::unique_ptr<Descriptor> newContext(gImageEffectHost->makeDescriptor(_currentPlugin->getBinary()->getBundlePath(), _currentPlugin));
|
||||
std::shared_ptr<Descriptor> newContext = gImageEffectHost->makeDescriptor(_currentPlugin->getBinary()->getBundlePath(), _currentPlugin);
|
||||
_currentContext = newContext.get();
|
||||
_currentPlugin->addContext(map["name"], std::move(newContext));
|
||||
_currentPlugin->addContext(map["name"], newContext);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user