Firstly, we need to create a directory for our module under modules directory. Copy folder dummy and name it imagemagick
We can use the module with name DummyModule, but for any real module, we probably want to give some beeter name. So the next few steps are optional, but recommended:
Rename DummyModule.cpp to ImageMagickSource.cpp and DummyModule.h to ImageMagickSource.h
Change DummyModule.h in #include in ImageMagickSource.cpp to ImageMagickSource.h
Rename the class: change all occurences of DummyModule class to ImageMagickSource
We're gonna use ImageMagick library, specifically it's C++ bindings called Magick++. CMake know about this packafe, so it's easy to add test for in. We add following line into CMakeLists.txt in modules directory:
find_package(ImageMagick COMPONENTS Magick++)
IF(${ImageMagick_Magick++_FOUND})
add_subdirectory(imagemagick)
ENDIF()
Now we have to edit CMakeLists.txt in imagemagick directory: change SET(MODULE dummy) to SET(MODULE imagemagick_source) and filenames in SET(SRC…. to ImageMagickSource.*
And lastly, we add the library to CMakeLists.txt in imagemagick directory: add following line for imagemagick includes:
include_directories(${ImageMagick_Magick++_INCLUDE_DIR})
and change linking to this:
target_link_libraries(${MODULE} ${LIBNAME} ${ImageMagick_Magick++_LIBRARY})
- ImageMagickSource.h
#ifndef DUMMYMODULE_H_
#define DUMMYMODULE_H_
#include "yuri/io/BasicIOThread.h"
namespace yuri {
namespace imagemagick_module {
using yuri::log::Log;
using yuri::config::Parameter;
using yuri::config::Parameters;
using yuri::io::pThreadBase;
class ImageMagickSource: public yuri::io::BasicIOThread
{
public:
IO_THREAD_GENERATOR_DECLARATION
static shared_ptr<Parameters> configure();
virtual ~ImageMagickSource();
private:
ImageMagickSource(Log &log_,pThreadBase parent,Parameters ¶meters);
virtual bool step();
virtual bool set_param(Parameter& param);
};
} /* namespace dummy_module */
} /* namespace yuri */
The only difference between this and the DummyModule.h is (except for the name) that we removed the attribute dummy_name.
Not for the cpp file:
- ImageMagickSource.cpp
#include "ImageMagickSource.h"
#include "yuri/config/RegisteredClass.h"
#include "Magick++.h"
#include <map>
namespace yuri {
namespace imagemagick_module {
REGISTER("imagemagick_source",ImageMagickSource)
IO_THREAD_GENERATOR(ImageMagickSource)
using namespace yuri::io;
shared_ptr<Parameters> ImageMagickSource::configure()
{
shared_ptr<Parameters> p = BasicIOThread::configure();
p->set_description("Image loader based on ImageMagick.");
p->set_max_pipes(1,1);
return p;
}
ImageMagickSource::ImageMagickSource(Log &log_,pThreadBase parent,Parameters ¶meters):
BasicIOThread(log_,parent,1,1,std::string("ImageMagickSource"))
{
IO_THREAD_INIT("ImageMagickSource")
}
ImageMagickSource::~ImageMagickSource()
{
}
bool ImageMagickSource::step()
{
pBasicFrame frame = in[0]->pop_frame();
if (!frame) return true;
if (frame->get_planes_count()>1) {
log[warning] << "Input frame has more than 1 plane, ignoring them\n";
}
try {
Magick::Blob blob(PLANE_RAW_DATA(frame,0),PLANE_SIZE(frame,0));
Magick::Image image(blob);
image.modifyImage();
yuri::size_t width = image.columns();
yuri::size_t height = image.rows();
log[debug] << "Loaded image " << width << "x" <<height <<"\n";
pBasicFrame out_frame = allocate_empty_frame(YURI_FMT_RGB24,width,height);
image.write(0,0,width,height,"RGB",Magick::CharPixel,PLANE_RAW_DATA(out_frame,0));
push_raw_video_frame(0,out_frame);
}
catch (std::exception& e) {
log[error] << "Exception during decoding: " << e.what() << "\n";
}
return true;
}
bool ImageMagickSource::set_param(Parameter& param)
{
return BasicIOThread::set_param(param);
}
} /* namespace dummy_module */
} /* namespace yuri */
The only relevant changes are in the step() method.
First we get input frame. If there's no frame available, we return. Always return true, unless some unrecoverable fatal error occurred. Returning false will most often terminate the program.
We support only frames with single plane, so we print a warning when we get more.
Then it's just using
API of Magick++. Note how we use
PLANE_RAW_DATA and
PLANE_SIZE to get data and size of first (index 0) plane in the incoming frame.
Next important thing is the call to allocate_empty_frame, that allocates just enough data for a frame with given resolution and format.
Lastly, we call push_raw_video_frame to push the frame to the output pipe.
That's all, this is working code for imagemagick_source module! Look at the actual version in source distribution, there are some minor enhancements, but this was the first version.