I'm trying to program a video buffer in a std::deque in my OFX video plug-in. I would like to access previously processed images in order to process the current image. My idea is to push processed images to the front of the deque and pop them from the back if the buffer exceeds the maximum size. The plug-in crashes when I try to free the memory of an image using delete before removing it from the buffer. I found out that I can add one or several images to the buffer and delete and remove them immediately afterwards with no problem. However, if I try to delete an image which has been added in an earlier cycle, it crashes. The plug-in consists of the main class OFXPlugin and the processor class myplugin. The instance of OFXPlugin stays over time, but for every image to be processed it creates an instance of myplugin and destroys it after processing that frame.
I'm not sure if I'm doing something wrong in the way I use the deque, if I'm not allowed to free memory which has been allocated by another instance of myplugin or if I'm doing something illegal related to the OFX API.
The Code below shows the extracts of the plug-in related to the problem. It's based on the OFX Support examples. It crashes at delete videoBuffer_.back().img;
in the function OFXPlugin::addToVBuff(OFX::Image *img, double t)
. I cannot catch an exception, apparently it is handled (ignored) in the OFX API.
Thanks a lot for your help!
myplugin.h
#include "ofxsImageEffect.h"
#include "ofxsMultiThread.h"
#include "../Support/Plugins/include/ofxsProcessing.H"
#include <deque>
// Video Buffer Element
typedef struct vBuffEl
{
OFX::Image* img;
double time;
} vBuffEl;
inline
bool operator==(const vBuffEl &a, const double b)
{
return a.time == b;
}
class myplugin : public OFX::ImageProcessor {
protected :
OFX::Image *_srcImg;
double _time;
OFXPlugin *_opInstance;
public :
// ctor
myplugin(OFX::ImageEffect &instance)
: OFX::ImageProcessor(instance)
, _srcImg(0)
, _time(0)
{}
void multiThreadProcessImages(OfxRectI procWindow);
void setOFXPlugin(OFXPlugin* opInstance) {_opInstance = opInstance;}
OFXPlugin* getOFXPlugin() {return _opInstance;}
void setTime(double argsTime) {_time = argsTime;}
double getTime() {return _time;}
void setSrcImg(OFX::Image *v) {_srcImg = v;}
OFX::Image* getSrcImg() {return _srcImg;}
};
class OFXPlugin : public OFX::ImageEffect {
protected :
OFX::Clip *dstClip_;
OFX::Clip *srcClip_;
double time_;
std::deque<vBuffEl> videoBuffer_;
public :
/** @brief ctor */
OFXPlugin(OfxImageEffectHandle handle);
/** @brief dtor */
~OFXPlugin();
/* Override the render */
virtual void render(const OFX::RenderArguments &args);
/* get the source Clip */
OFX::Clip* getSrcClip();
/* get the current time */
double getTime();
/* set up and run a processor */
void setupAndProcess(myplugin &, const OFX::RenderArguments &args);
/* add to video buffer */
void addToVBuff(OFX::Image *img, double t);
/* fetch a dst image from buffer */
void fetchDstImageBuff(double t, OFX::Image* &img, bool &buff);
};
myplugin.cpp
#include "myplugin.h"
#include <algorithm>
void myplugin::multiThreadProcessImages(OfxRectI procWindow)
{
// Do some filtering of the source image and store result in destination image
myfiltering(_dstImg, _srcImg, procWindow);
// add to buffer
_opInstance->addToVBuff(_dstImg, _time);
}
/* set up and run a processor */
void
OFXPlugin::setupAndProcess(myplugin &processor, const OFX::RenderArguments &args)
{
// get a dst image
std::auto_ptr<OFX::Image> dst(dstClip_->fetchImage(args.time));
OFX::BitDepthEnum dstBitDepth = dst->getPixelDepth();
OFX::PixelComponentEnum dstComponents = dst->getPixelComponents();
// fetch main input image
std::auto_ptr<OFX::Image> src(srcClip_->fetchImage(args.time));
// set the images
processor.setDstImg(dst.get());
processor.setSrcImg(src.get());
// set the render window
processor.setRenderWindow(args.renderWindow);
// set time
processor.setTime(args.time);
time_ = args.time;
// set OFXPlugin instance
processor.setOFXPlugin(this);
// Call the base class process member, this will call the derived templated process code
processor.process();
}
OFX::Clip* OFXPlugin::getSrcClip()
{
return srcClip_;
}
/* get the current time */
double
OFXPlugin::getTime()
{
return time_;
}
// the overridden render function
void
OFXPlugin::render(const OFX::RenderArguments &args)
{
try {
myplugin fred(*this);
setupAndProcess(fred, args);
} catch (...) {
outputMessage("ERROR: An unknown error happened!");
}
}
/* add to video buffer */
void
OFXPlugin::addToVBuff(OFX::Image *img, double t)
{
try {
// if frame already exists in buffer, remove
std::deque<vBuffEl>::iterator it;
it = find(videoBuffer_.begin(), videoBuffer_.end(), t);
if(it != videoBuffer_.end())
{
delete it->img;
videoBuffer_.erase(it);
}
// add new frame to the front
vBuffEl e;
e.time = t;
e.img = new OFX::Image(img->getPropertySet().propSetHandle());
memcpy(e.img, img, sizeof(img));
videoBuffer_.push_front(e);
// remove elements at the end, if the buffer exceeds the max size
int LASTIMG_ARRAY_SIZE = 10;
while(videoBuffer_.size() > LASTIMG_ARRAY_SIZE)
{
delete videoBuffer_.back().img;
videoBuffer_.erase(--(videoBuffer_.end()));
}
} catch (...) {
outputMessage("ERROR: An unknown error happened!");
}
}
/* fetch a dst image from buffer */
void
OFXPlugin::fetchDstImageBuff(double t, OFX::Image* &img, bool &buff)
{
try {
std::deque<vBuffEl>::iterator it;
it = find(videoBuffer_.begin(), videoBuffer_.end(), t);
if(it != videoBuffer_.end())
{
img = it->img; // return buffered dst image
buff = true;
}
else
{
img = getSrcClip()->fetchImage(t); // fetch and return src image
buff = false;
}
} catch (...) {
outputMessage("ERROR: An unknown error happened!");
}
}
The statement
memcpy(e.img, img, sizeof(img));
doesn't do what you expect it to.
The sizeof
operation of a pointer returns the size of the pointer, not what it points to. This means that in this case, you are only copying 4 or 8 bytes (depending on if you are on a 32 or 64 bit platform).
However, there is another worse problem hidden in that memcpy
call. If the OFX::Image
contains data member pointers, copying the data will copy the pointers and not the data. It's a shallow copy, not a deep copy. This is a reason C++ has copy constructors and copy assignment operators.
What you should to is a simple assignment, and hope that OFX::Image
follows the rule of three:
*e.img = *img;