Fixed: fixed black screen when pulling playhead back.
This commit is contained in:
@@ -1,7 +0,0 @@
|
|||||||
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']
|
|
||||||
@@ -18,11 +18,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
|
||||||
|
|
||||||
project(olive-editor VERSION 0.2.0 LANGUAGES CXX)
|
project(olive-editor VERSION 0.2.0 LANGUAGES CXX)
|
||||||
if(${CMAKE_BUILD_TYPE} EQUAL Debug)
|
|
||||||
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_C_FLAGS ${CMAKE_C_FLAGS} -DOFX_SUPPORTS_OPENGLRENDER)
|
||||||
|
|||||||
@@ -361,9 +361,6 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qDebug() << "[DECODER] RetrieveVideoInternal time=" << p.time.toDouble()
|
|
||||||
<< "format=" << static_cast<int>(f->format) << "black=" << all_black;
|
|
||||||
|
|
||||||
// Finally, perform any GPU processing required
|
// Finally, perform any GPU processing required
|
||||||
TexturePtr texture = ProcessFrameIntoTexture(f, p, original);
|
TexturePtr texture = ProcessFrameIntoTexture(f, p, original);
|
||||||
|
|
||||||
|
|||||||
@@ -639,11 +639,13 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
|||||||
image->EnsureAllocatedFromParams(params_, bounds, regionOfDefinition,
|
image->EnsureAllocatedFromParams(params_, bounds, regionOfDefinition,
|
||||||
false);
|
false);
|
||||||
} else {
|
} else {
|
||||||
|
pruneImagesCache();
|
||||||
image = new Image(*this, params_, bounds,
|
image = new Image(*this, params_, bounds,
|
||||||
regionOfDefinition, false);
|
regionOfDefinition, false);
|
||||||
|
image->EnsureAllocatedFromParams(params_, bounds, regionOfDefinition,
|
||||||
|
false);
|
||||||
images_.insert(time, image);
|
images_.insert(time, image);
|
||||||
}
|
}
|
||||||
pruneImagesCache();
|
|
||||||
|
|
||||||
uint8_t *dst = (uint8_t*)image->data();
|
uint8_t *dst = (uint8_t*)image->data();
|
||||||
if (!dst) {
|
if (!dst) {
|
||||||
@@ -655,6 +657,35 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect NaN/Inf in float input data before passing to CImg.
|
||||||
|
// CImg::blur_bilateral computes (int)round(val / sigma) which becomes
|
||||||
|
// undefined behaviour when val is NaN, leading to out-of-bounds indexing
|
||||||
|
// and SIGSEGV on Apple Silicon (where (int)NaN often evaluates to 0 or
|
||||||
|
// INT_MIN, causing huge offsets into bgrid._data).
|
||||||
|
if (params_.format() == core::PixelFormat::F32) {
|
||||||
|
const float *fptr = reinterpret_cast<const float*>(frame->data[0]);
|
||||||
|
int row_floats = frame->linesize[0] / static_cast<int>(sizeof(float));
|
||||||
|
bool has_nan = false;
|
||||||
|
for (int y = 0; y < params_.height() && !has_nan; ++y) {
|
||||||
|
for (int x = 0; x < params_.width() * params_.channel_count(); ++x) {
|
||||||
|
float v = fptr[y * row_floats + x];
|
||||||
|
if (std::isnan(v) || std::isinf(v)) {
|
||||||
|
qWarning() << "[PLUGIN] NaN/Inf detected in input frame at pixel ("
|
||||||
|
<< x / params_.channel_count() << "," << y
|
||||||
|
<< ") channel=" << (x % params_.channel_count())
|
||||||
|
<< " value=" << v;
|
||||||
|
has_nan = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_nan) {
|
||||||
|
qWarning() << "[PLUGIN] Filling corrupted input frame with black to avoid CImg crash";
|
||||||
|
std::memset(dst, 0, image->row_bytes() * image->height());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AVFramePtr src_frame = frame;
|
AVFramePtr src_frame = frame;
|
||||||
if (frame->format != expected_fmt ||
|
if (frame->format != expected_fmt ||
|
||||||
frame->width != params_.width() ||
|
frame->width != params_.width() ||
|
||||||
@@ -703,7 +734,27 @@ copy_pixels:
|
|||||||
int copy_height = std::min(image->height(), src_frame->height);
|
int copy_height = std::min(image->height(), src_frame->height);
|
||||||
|
|
||||||
const uint8_t *src = src_frame->data[0];
|
const uint8_t *src = src_frame->data[0];
|
||||||
if (dst_row_bytes == src_row_bytes && src_row_bytes == copy_bytes) {
|
if (params_.format() == core::PixelFormat::F32) {
|
||||||
|
const float *src_f = reinterpret_cast<const float*>(src);
|
||||||
|
float *dst_f = reinterpret_cast<float*>(dst);
|
||||||
|
int src_stride = src_row_bytes / static_cast<int>(sizeof(float));
|
||||||
|
int dst_stride = dst_row_bytes / static_cast<int>(sizeof(float));
|
||||||
|
int floats_per_row = copy_bytes / static_cast<int>(sizeof(float));
|
||||||
|
bool has_nan = false;
|
||||||
|
for (int y = 0; y < copy_height; ++y) {
|
||||||
|
for (int i = 0; i < floats_per_row; ++i) {
|
||||||
|
float v = src_f[y * src_stride + i];
|
||||||
|
if (std::isnan(v) || std::isinf(v)) {
|
||||||
|
v = 0.0f;
|
||||||
|
has_nan = true;
|
||||||
|
}
|
||||||
|
dst_f[y * dst_stride + i] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_nan) {
|
||||||
|
qWarning() << "[PLUGIN] NaN/Inf scrubbed from input frame data during copy";
|
||||||
|
}
|
||||||
|
} else if (dst_row_bytes == src_row_bytes && src_row_bytes == copy_bytes) {
|
||||||
std::memcpy(dst, src, copy_bytes * copy_height);
|
std::memcpy(dst, src, copy_bytes * copy_height);
|
||||||
} else {
|
} else {
|
||||||
for (int y = 0; y < copy_height; ++y) {
|
for (int y = 0; y < copy_height; ++y) {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#ifndef OLIVE_HOST_H
|
#ifndef OLIVE_HOST_H
|
||||||
#define OLIVE_HOST_H
|
#define OLIVE_HOST_H
|
||||||
#include "node/plugins/Plugin.h"
|
#include "node/plugins/Plugin.h"
|
||||||
#include "ofxhHost.h"
|
#include "ofxhHost.h"
|
||||||
#include "ofxhImageEffectAPI.h"
|
#include "ofxhImageEffectAPI.h"
|
||||||
#include "ofxCore.h"
|
#include "ofxCore.h"
|
||||||
#include "ofxhImageEffect.h"
|
#include "ofxhImageEffect.h"
|
||||||
@@ -31,28 +31,25 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <qlist.h>
|
#include <qlist.h>
|
||||||
namespace olive {
|
namespace olive
|
||||||
namespace plugin {
|
{
|
||||||
enum class HostMessageType{
|
namespace plugin
|
||||||
Error,
|
{
|
||||||
Warning,
|
enum class HostMessageType { Error, Warning, Message };
|
||||||
Message
|
struct HostPersistentMessage {
|
||||||
};
|
|
||||||
struct HostPersistentMessage{
|
|
||||||
HostMessageType type;
|
HostMessageType type;
|
||||||
QString message;
|
QString message;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void loadPlugins(QString path);
|
void loadPlugins(QString path);
|
||||||
class OliveHost: public OFX::Host::ImageEffect::Host{
|
class OliveHost : public OFX::Host::ImageEffect::Host {
|
||||||
public:
|
public:
|
||||||
OliveHost()=default;
|
OliveHost() = default;
|
||||||
~OliveHost() override;
|
~OliveHost() override;
|
||||||
void destroyInstance(OFX::Host::ImageEffect::Instance *instance);
|
void destroyInstance(OFX::Host::ImageEffect::Instance *instance);
|
||||||
|
|
||||||
bool pluginSupported(OFX::Host::ImageEffect::ImageEffectPlugin *plugin,
|
bool pluginSupported(OFX::Host::ImageEffect::ImageEffectPlugin *plugin,
|
||||||
std::string &reason) const override
|
std::string &reason) const override
|
||||||
{
|
{
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
reason = "null plugin";
|
reason = "null plugin";
|
||||||
@@ -65,14 +62,14 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
OFX::Host::ImageEffect::Instance* newInstance(void *clientData,
|
OFX::Host::ImageEffect::Instance *
|
||||||
OFX::Host::ImageEffect::ImageEffectPlugin* plugin,
|
newInstance(void *clientData,
|
||||||
OFX::Host::ImageEffect::Descriptor& desc,
|
OFX::Host::ImageEffect::ImageEffectPlugin *plugin,
|
||||||
const std::string& context) override;
|
OFX::Host::ImageEffect::Descriptor &desc,
|
||||||
|
const std::string &context) override;
|
||||||
|
|
||||||
|
std::shared_ptr<OFX::Host::ImageEffect::Descriptor>
|
||||||
std::shared_ptr<OFX::Host::ImageEffect::Descriptor> makeDescriptor(
|
makeDescriptor(OFX::Host::ImageEffect::ImageEffectPlugin *plugin) override;
|
||||||
OFX::Host::ImageEffect::ImageEffectPlugin* plugin) override;
|
|
||||||
|
|
||||||
std::shared_ptr<OFX::Host::ImageEffect::Descriptor>
|
std::shared_ptr<OFX::Host::ImageEffect::Descriptor>
|
||||||
makeDescriptor(const OFX::Host::ImageEffect::Descriptor &rootContext,
|
makeDescriptor(const OFX::Host::ImageEffect::Descriptor &rootContext,
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
/***
|
/*** Olive - Non-Linear Video Editor Copyright (C) 2022 Olive Team
|
||||||
|
|
||||||
Olive - Non-Linear Video Editor
|
|
||||||
Copyright (C) 2022 Olive Team
|
|
||||||
Modifications Copyright (C) 2025 mikesolar
|
Modifications Copyright (C) 2025 mikesolar
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
|||||||
@@ -152,7 +152,28 @@ static void ApplyParamOverrides(OFX::Host::ImageEffect::Instance &instance,
|
|||||||
if (auto *param =
|
if (auto *param =
|
||||||
dynamic_cast<OFX::Host::Param::DoubleInstance *>(
|
dynamic_cast<OFX::Host::Param::DoubleInstance *>(
|
||||||
entry.second)) {
|
entry.second)) {
|
||||||
param->set(time, value.data().toDouble());
|
double v = value.data().toDouble();
|
||||||
|
if (std::isnan(v) || std::isinf(v)) {
|
||||||
|
qWarning() << "[PLUGIN] NaN/Inf in double param" << key
|
||||||
|
<< "replacing with default";
|
||||||
|
auto *default_prop =
|
||||||
|
entry.second->getProperties().fetchDoubleProperty(
|
||||||
|
kOfxParamPropDefault);
|
||||||
|
v = default_prop ? default_prop->getValue() : 0.0;
|
||||||
|
}
|
||||||
|
auto *min_prop =
|
||||||
|
entry.second->getProperties().fetchDoubleProperty(
|
||||||
|
kOfxParamPropMin);
|
||||||
|
if (min_prop && v < min_prop->getValue()) {
|
||||||
|
v = min_prop->getValue();
|
||||||
|
}
|
||||||
|
auto *max_prop =
|
||||||
|
entry.second->getProperties().fetchDoubleProperty(
|
||||||
|
kOfxParamPropMax);
|
||||||
|
if (max_prop && v > max_prop->getValue()) {
|
||||||
|
v = max_prop->getValue();
|
||||||
|
}
|
||||||
|
param->set(time, v);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1694,10 +1715,6 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
|||||||
|
|
||||||
// render a frame
|
// render a frame
|
||||||
const char *render_field = GetRenderFieldForParams(output_params);
|
const char *render_field = GetRenderFieldForParams(output_params);
|
||||||
qDebug() << "[PLUGIN] RenderPlugin use_opengl=" << use_opengl
|
|
||||||
<< "time=" << frame
|
|
||||||
<< "plugin=" << PluginIdForInstance(instance)
|
|
||||||
<< "dest_valid=" << (destination ? destination->id().isValid() : false);
|
|
||||||
stat = instance->renderAction(frame, render_field, renderWindow, renderScale,
|
stat = instance->renderAction(frame, render_field, renderWindow, renderScale,
|
||||||
true, interactive, interactive);
|
true, interactive, interactive);
|
||||||
if (stat != kOfxStatOK && stat != kOfxStatReplyDefault) {
|
if (stat != kOfxStatOK && stat != kOfxStatReplyDefault) {
|
||||||
@@ -1750,7 +1767,6 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qDebug() << "[PLUGIN] output_image black=" << img_black << "plugin=" << PluginIdForInstance(instance);
|
|
||||||
} else {
|
} else {
|
||||||
if (!destination || !destination->id().isValid()) {
|
if (!destination || !destination->id().isValid()) {
|
||||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||||
@@ -1796,7 +1812,6 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
|
|||||||
} else {
|
} else {
|
||||||
// OpenGL path: plugin has already rendered directly into the destination
|
// OpenGL path: plugin has already rendered directly into the destination
|
||||||
// texture via FBO/GL. No CPU readback or conversion needed.
|
// texture via FBO/GL. No CPU readback or conversion needed.
|
||||||
qDebug() << "[PLUGIN] OpenGL path done, returning directly";
|
|
||||||
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
||||||
DetachOutputTexture();
|
DetachOutputTexture();
|
||||||
instance->contextDetachedAction();
|
instance->contextDetachedAction();
|
||||||
|
|||||||
@@ -162,8 +162,6 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qDebug() << "[RENDER] GenerateFrame DownloadFromTexture all_black="
|
|
||||||
<< all_black << "total_bytes=" << total_bytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return frame;
|
return frame;
|
||||||
@@ -209,9 +207,6 @@ void RenderProcessor::Run()
|
|||||||
}
|
}
|
||||||
|
|
||||||
TexturePtr texture = GenerateTexture(time, frame_length);
|
TexturePtr texture = GenerateTexture(time, frame_length);
|
||||||
qDebug() << "[RENDER] GenerateTexture time=" << time.toDouble()
|
|
||||||
<< "tex_null=" << (texture == nullptr)
|
|
||||||
<< "tex_dummy=" << (texture ? texture->IsDummy() : true);
|
|
||||||
|
|
||||||
if (!render_ctx_) {
|
if (!render_ctx_) {
|
||||||
ticket_->Finish();
|
ticket_->Finish();
|
||||||
@@ -269,7 +264,6 @@ void RenderProcessor::Run()
|
|||||||
}
|
}
|
||||||
|
|
||||||
render_ctx_->Flush();
|
render_ctx_->Flush();
|
||||||
qDebug() << "[RENDER] Finishing with texture";
|
|
||||||
ticket_->Finish(QVariant::fromValue(texture));
|
ticket_->Finish(QVariant::fromValue(texture));
|
||||||
} else {
|
} else {
|
||||||
ticket_->Finish(QVariant::fromValue(frame));
|
ticket_->Finish(QVariant::fromValue(frame));
|
||||||
|
|||||||
@@ -382,15 +382,11 @@ void ViewerDisplayWidget::OnPaint()
|
|||||||
bg_color.blueF());
|
bg_color.blueF());
|
||||||
|
|
||||||
// We only draw if we have a pipeline
|
// We only draw if we have a pipeline
|
||||||
qDebug() << "[VIEWER] OnPaint push_mode=" << push_mode_
|
|
||||||
<< "color_service=" << (color_service() != nullptr)
|
|
||||||
<< "has_load_frame=" << !load_frame_.isNull();
|
|
||||||
if (push_mode_ != kPushNull) {
|
if (push_mode_ != kPushNull) {
|
||||||
// Draw texture through color transform
|
// Draw texture through color transform
|
||||||
VideoParams device_params = GetViewportParams();
|
VideoParams device_params = GetViewportParams();
|
||||||
|
|
||||||
if (push_mode_ == kPushBlank) {
|
if (push_mode_ == kPushBlank) {
|
||||||
qDebug() << "[VIEWER] OnPaint drawing blank";
|
|
||||||
DrawBlank(device_params);
|
DrawBlank(device_params);
|
||||||
} else if (color_service()) {
|
} else if (color_service()) {
|
||||||
if (FramePtr frame = load_frame_.value<FramePtr>()) {
|
if (FramePtr frame = load_frame_.value<FramePtr>()) {
|
||||||
@@ -494,7 +490,6 @@ void ViewerDisplayWidget::OnPaint()
|
|||||||
ctj.SetForceOpaque(true);
|
ctj.SetForceOpaque(true);
|
||||||
|
|
||||||
renderer()->BlitColorManaged(ctj, device_params);
|
renderer()->BlitColorManaged(ctj, device_params);
|
||||||
qDebug() << "[VIEWER] OnPaint BlitColorManaged done";
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "[VIEWER] OnPaint no color_service, skipping texture draw";
|
qDebug() << "[VIEWER] OnPaint no color_service, skipping texture draw";
|
||||||
|
|||||||
+1887
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user