diff --git a/modules/videoio/src/backend_plugin.cpp b/modules/videoio/src/backend_plugin.cpp index a37cb872c1..1cdf4bd149 100644 --- a/modules/videoio/src/backend_plugin.cpp +++ b/modules/videoio/src/backend_plugin.cpp @@ -6,6 +6,8 @@ #include "backend.hpp" #include "plugin_api.hpp" +#include "plugin_capture_api.hpp" +#include "plugin_writer_api.hpp" #include "opencv2/core/utils/filesystem.hpp" #include "opencv2/core/utils/configuration.private.hpp" @@ -25,6 +27,8 @@ using namespace std; #include #endif +#include "backend_plugin_legacy.impl.hpp" + namespace cv { namespace impl { #if defined(_WIN32) @@ -164,7 +168,7 @@ public: } void * res = getSymbol_(handle, symbolName); if (!res) - CV_LOG_ERROR(NULL, "No symbol '" << symbolName << "' in " << toPrintablePath(fname)); + CV_LOG_DEBUG(NULL, "No symbol '" << symbolName << "' in " << toPrintablePath(fname)); return res; } const std::string getName() const { return toPrintablePath(fname); } @@ -194,17 +198,83 @@ private: class PluginBackend: public IBackend { -public: - Ptr lib_; - const OpenCV_VideoIO_Plugin_API_preview* plugin_api_; +protected: - PluginBackend(const Ptr& lib) : - lib_(lib), plugin_api_(NULL) + void initCaptureAPI() + { + const char* init_name = "opencv_videoio_capture_plugin_init_v1"; + FN_opencv_videoio_capture_plugin_init_t fn_init = reinterpret_cast(lib_->getSymbol(init_name)); + if (fn_init) + { + CV_LOG_INFO(NULL, "Found entry: '" << init_name << "'"); + for (int supported_api_version = CAPTURE_API_VERSION; supported_api_version >= 0; supported_api_version--) + { + capture_api_ = fn_init(CAPTURE_ABI_VERSION, supported_api_version, NULL); + if (capture_api_) + break; + } + if (!capture_api_) + { + CV_LOG_INFO(NULL, "Video I/O: plugin is incompatible (can't be initialized): " << lib_->getName()); + return; + } + if (!checkCompatibility( + capture_api_->api_header, CAPTURE_ABI_VERSION, CAPTURE_API_VERSION, + capture_api_->v0.id != CAP_FFMPEG)) + { + capture_api_ = NULL; + return; + } + CV_LOG_INFO(NULL, "Video I/O: plugin is ready to use '" << capture_api_->api_header.api_description << "'"); + } + else + { + CV_LOG_INFO(NULL, "Video I/O: missing plugin init function: '" << init_name << "', file: " << lib_->getName()); + } + } + + + void initWriterAPI() + { + const char* init_name = "opencv_videoio_writer_plugin_init_v1"; + FN_opencv_videoio_writer_plugin_init_t fn_init = reinterpret_cast(lib_->getSymbol(init_name)); + if (fn_init) + { + CV_LOG_INFO(NULL, "Found entry: '" << init_name << "'"); + for (int supported_api_version = WRITER_API_VERSION; supported_api_version >= 0; supported_api_version--) + { + writer_api_ = fn_init(WRITER_ABI_VERSION, supported_api_version, NULL); + if (writer_api_) + break; + } + if (!writer_api_) + { + CV_LOG_INFO(NULL, "Video I/O: plugin is incompatible (can't be initialized): " << lib_->getName()); + return; + } + if (!checkCompatibility( + writer_api_->api_header, WRITER_ABI_VERSION, WRITER_API_VERSION, + writer_api_->v0.id != CAP_FFMPEG)) + { + writer_api_ = NULL; + return; + } + CV_LOG_INFO(NULL, "Video I/O: plugin is ready to use '" << writer_api_->api_header.api_description << "'"); + } + else + { + CV_LOG_INFO(NULL, "Video I/O: missing plugin init function: '" << init_name << "', file: " << lib_->getName()); + } + } + + + void initPluginLegacyAPI() { const char* init_name = "opencv_videoio_plugin_init_v0"; FN_opencv_videoio_plugin_init_t fn_init = reinterpret_cast(lib_->getSymbol(init_name)); if (fn_init) { + CV_LOG_INFO(NULL, "Found entry: '" << init_name << "'"); for (int supported_api_version = API_VERSION; supported_api_version >= 0; supported_api_version--) { plugin_api_ = fn_init(ABI_VERSION, supported_api_version, NULL); @@ -213,57 +283,83 @@ public: } if (!plugin_api_) { - CV_LOG_INFO(NULL, "Video I/O: plugin is incompatible (can't be initialized): " << lib->getName()); + CV_LOG_INFO(NULL, "Video I/O: plugin is incompatible (can't be initialized): " << lib_->getName()); return; } - if (plugin_api_->api_header.opencv_version_major != CV_VERSION_MAJOR) + if (!checkCompatibility( + plugin_api_->api_header, ABI_VERSION, API_VERSION, + plugin_api_->v0.captureAPI != CAP_FFMPEG)) { - CV_LOG_ERROR(NULL, "Video I/O: wrong OpenCV major version used by plugin '" << plugin_api_->api_header.api_description << "': " << - cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", plugin_api_->api_header.opencv_version_major, plugin_api_->api_header.opencv_version_minor)) plugin_api_ = NULL; return; } -#ifdef HAVE_FFMPEG_WRAPPER - if (plugin_api_->v0.captureAPI == CAP_FFMPEG) - { - // no checks for OpenCV minor version - } - else -#endif - if (plugin_api_->api_header.opencv_version_minor != CV_VERSION_MINOR) - { - CV_LOG_ERROR(NULL, "Video I/O: wrong OpenCV minor version used by plugin '" << plugin_api_->api_header.api_description << "': " << - cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", plugin_api_->api_header.opencv_version_major, plugin_api_->api_header.opencv_version_minor)) - plugin_api_ = NULL; - return; - } - CV_LOG_INFO(NULL, "Video I/O: initialized '" << plugin_api_->api_header.api_description << "': built with " - << cv::format("OpenCV %d.%d (ABI/API = %d/%d)", - plugin_api_->api_header.opencv_version_major, plugin_api_->api_header.opencv_version_minor, - plugin_api_->api_header.min_api_version, plugin_api_->api_header.api_version) - << ", current OpenCV version is '" CV_VERSION "' (ABI/API = " << ABI_VERSION << "/" << API_VERSION << ")" - ); - if (plugin_api_->api_header.min_api_version != ABI_VERSION) // future: range can be here - { - // actually this should never happen due to checks in plugin's init() function - CV_LOG_ERROR(NULL, "Video I/O: plugin is not supported due to incompatible ABI = " << plugin_api_->api_header.min_api_version); - plugin_api_ = NULL; - return; - } - if (plugin_api_->api_header.api_version != API_VERSION) - { - CV_LOG_INFO(NULL, "Video I/O: NOTE: plugin is supported, but there is API version mismath: " - << cv::format("plugin API level (%d) != OpenCV API level (%d)", plugin_api_->api_header.api_version, API_VERSION)); - if (plugin_api_->api_header.api_version < API_VERSION) - { - CV_LOG_INFO(NULL, "Video I/O: NOTE: some functionality may be unavailable due to lack of support by plugin implementation"); - } - } CV_LOG_INFO(NULL, "Video I/O: plugin is ready to use '" << plugin_api_->api_header.api_description << "'"); } else { - CV_LOG_INFO(NULL, "Video I/O: plugin is incompatible, missing init function: '" << init_name << "', file: " << lib->getName()); + CV_LOG_INFO(NULL, "Video I/O: plugin is incompatible, missing init function: '" << init_name << "', file: " << lib_->getName()); + } + } + + + bool checkCompatibility(const OpenCV_API_Header& api_header, unsigned int abi_version, unsigned int api_version, bool checkMinorOpenCVVersion) + { + if (api_header.opencv_version_major != CV_VERSION_MAJOR) + { + CV_LOG_ERROR(NULL, "Video I/O: wrong OpenCV major version used by plugin '" << api_header.api_description << "': " << + cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", api_header.opencv_version_major, api_header.opencv_version_minor)) + return false; + } + if (!checkMinorOpenCVVersion) + { + // no checks for OpenCV minor version + } + else if (api_header.opencv_version_minor != CV_VERSION_MINOR) + { + CV_LOG_ERROR(NULL, "Video I/O: wrong OpenCV minor version used by plugin '" << api_header.api_description << "': " << + cv::format("%d.%d, OpenCV version is '" CV_VERSION "'", api_header.opencv_version_major, api_header.opencv_version_minor)) + return false; + } + CV_LOG_INFO(NULL, "Video I/O: initialized '" << api_header.api_description << "': built with " + << cv::format("OpenCV %d.%d (ABI/API = %d/%d)", + api_header.opencv_version_major, api_header.opencv_version_minor, + api_header.min_api_version, api_header.api_version) + << ", current OpenCV version is '" CV_VERSION "' (ABI/API = " << abi_version << "/" << api_version << ")" + ); + if (api_header.min_api_version != abi_version) // future: range can be here + { + // actually this should never happen due to checks in plugin's init() function + CV_LOG_ERROR(NULL, "Video I/O: plugin is not supported due to incompatible ABI = " << api_header.min_api_version); + return false; + } + if (api_header.api_version != api_version) + { + CV_LOG_INFO(NULL, "Video I/O: NOTE: plugin is supported, but there is API version mismath: " + << cv::format("plugin API level (%d) != OpenCV API level (%d)", api_header.api_version, api_version)); + if (api_header.api_version < api_version) + { + CV_LOG_INFO(NULL, "Video I/O: NOTE: some functionality may be unavailable due to lack of support by plugin implementation"); + } + } + return true; + } + +public: + Ptr lib_; + const OpenCV_VideoIO_Capture_Plugin_API* capture_api_; + const OpenCV_VideoIO_Writer_Plugin_API* writer_api_; + const OpenCV_VideoIO_Plugin_API_preview* plugin_api_; //!< deprecated + + PluginBackend(const Ptr& lib) + : lib_(lib) + , capture_api_(NULL), writer_api_(NULL) + , plugin_api_(NULL) + { + initCaptureAPI(); + initWriterAPI(); + if (capture_api_ == NULL && writer_api_ == NULL) + { + initPluginLegacyAPI(); } } @@ -407,20 +503,46 @@ void PluginBackendFactory::loadPlugin() try { Ptr pluginBackend = makePtr(lib); - if (pluginBackend && pluginBackend->plugin_api_) + if (!pluginBackend) + return; + if (pluginBackend->capture_api_) + { + if (pluginBackend->capture_api_->v0.id != id_) + { + CV_LOG_ERROR(NULL, "Video I/O: plugin '" << pluginBackend->capture_api_->api_header.api_description << + "': unexpected backend ID: " << + pluginBackend->capture_api_->v0.id << " vs " << (int)id_ << " (expected)"); + return; + } + } + if (pluginBackend->writer_api_) + { + if (pluginBackend->writer_api_->v0.id != id_) + { + CV_LOG_ERROR(NULL, "Video I/O: plugin '" << pluginBackend->writer_api_->api_header.api_description << + "': unexpected backend ID: " << + pluginBackend->writer_api_->v0.id << " vs " << (int)id_ << " (expected)"); + return; + } + } + if (pluginBackend->plugin_api_) { if (pluginBackend->plugin_api_->v0.captureAPI != id_) { CV_LOG_ERROR(NULL, "Video I/O: plugin '" << pluginBackend->plugin_api_->api_header.api_description << "': unexpected backend ID: " << pluginBackend->plugin_api_->v0.captureAPI << " vs " << (int)id_ << " (expected)"); - } - else - { - backend = pluginBackend; return; } } + if (pluginBackend->capture_api_ == NULL && pluginBackend->writer_api_ == NULL + && pluginBackend->plugin_api_ == NULL) + { + CV_LOG_ERROR(NULL, "Video I/O: no compatible plugin API for backend ID: " << (int)id_); + return; + } + backend = pluginBackend; + return; } catch (...) { @@ -434,16 +556,17 @@ void PluginBackendFactory::loadPlugin() class PluginCapture : public cv::IVideoCapture { - const OpenCV_VideoIO_Plugin_API_preview* plugin_api_; + const OpenCV_VideoIO_Capture_Plugin_API* plugin_api_; CvPluginCapture capture_; public: static - Ptr create(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, + Ptr create(const OpenCV_VideoIO_Capture_Plugin_API* plugin_api, const std::string &filename, int camera) { CV_Assert(plugin_api); CvPluginCapture capture = NULL; + if (plugin_api->v0.Capture_open) { CV_Assert(plugin_api->v0.Capture_release); @@ -456,7 +579,7 @@ public: return Ptr(); } - PluginCapture(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, CvPluginCapture capture) + PluginCapture(const OpenCV_VideoIO_Capture_Plugin_API* plugin_api, CvPluginCapture capture) : plugin_api_(plugin_api), capture_(capture) { CV_Assert(plugin_api_); CV_Assert(capture_); @@ -491,13 +614,13 @@ public: return true; return false; } - static CvResult CV_API_CALL retrieve_callback(int stream_idx, const unsigned char* data, int step, int width, int height, int cn, void* userdata) + static CvResult CV_API_CALL retrieve_callback(int stream_idx, const unsigned char* data, int step, int width, int height, int type, void* userdata) { CV_UNUSED(stream_idx); cv::_OutputArray* dst = static_cast(userdata); if (!dst) return CV_ERROR_FAIL; - cv::Mat(cv::Size(width, height), CV_MAKETYPE(CV_8U, cn), (void*)data, step).copyTo(*dst); + cv::Mat(cv::Size(width, height), type, (void*)data, step).copyTo(*dst); return CV_ERROR_OK; } bool retrieveFrame(int idx, cv::OutputArray img) CV_OVERRIDE @@ -514,7 +637,7 @@ public: } int getCaptureDomain() CV_OVERRIDE { - return plugin_api_->v0.captureAPI; + return plugin_api_->v0.id; } }; @@ -523,12 +646,12 @@ public: class PluginWriter : public cv::IVideoWriter { - const OpenCV_VideoIO_Plugin_API_preview* plugin_api_; + const OpenCV_VideoIO_Writer_Plugin_API* plugin_api_; CvPluginWriter writer_; public: static - Ptr create(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, + Ptr create(const OpenCV_VideoIO_Writer_Plugin_API* plugin_api, const std::string& filename, int fourcc, double fps, const cv::Size& sz, const VideoWriterParameters& params) { @@ -568,7 +691,7 @@ public: return Ptr(); } - PluginWriter(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, CvPluginWriter writer) + PluginWriter(const OpenCV_VideoIO_Writer_Plugin_API* plugin_api, CvPluginWriter writer) : plugin_api_(plugin_api), writer_(writer) { CV_Assert(plugin_api_); CV_Assert(writer_); @@ -613,7 +736,7 @@ public: } int getCaptureDomain() const CV_OVERRIDE { - return plugin_api_->v0.captureAPI; + return plugin_api_->v0.id; } }; @@ -622,8 +745,10 @@ Ptr PluginBackend::createCapture(int camera) const { try { + if (capture_api_) + return PluginCapture::create(capture_api_, std::string(), camera); //.staticCast(); if (plugin_api_) - return PluginCapture::create(plugin_api_, std::string(), camera); //.staticCast(); + return legacy::PluginCapture::create(plugin_api_, std::string(), camera); //.staticCast(); } catch (...) { @@ -636,8 +761,10 @@ Ptr PluginBackend::createCapture(const std::string &filename) con { try { + if (capture_api_) + return PluginCapture::create(capture_api_, filename, 0); //.staticCast(); if (plugin_api_) - return PluginCapture::create(plugin_api_, filename, 0); //.staticCast(); + return legacy::PluginCapture::create(plugin_api_, filename, 0); //.staticCast(); } catch (...) { @@ -651,8 +778,10 @@ Ptr PluginBackend::createWriter(const std::string& filename, int f { try { + if (writer_api_) + return PluginWriter::create(writer_api_, filename, fourcc, fps, sz, params); //.staticCast(); if (plugin_api_) - return PluginWriter::create(plugin_api_, filename, fourcc, fps, sz, params); //.staticCast(); + return legacy::PluginWriter::create(plugin_api_, filename, fourcc, fps, sz, params); //.staticCast(); } catch (...) { diff --git a/modules/videoio/src/backend_plugin_legacy.impl.hpp b/modules/videoio/src/backend_plugin_legacy.impl.hpp new file mode 100644 index 0000000000..fa9def4cc5 --- /dev/null +++ b/modules/videoio/src/backend_plugin_legacy.impl.hpp @@ -0,0 +1,199 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// +// Not a standalone header. +// + +namespace cv { namespace impl { namespace legacy { + +//================================================================================================== + +class PluginCapture : public cv::IVideoCapture +{ + const OpenCV_VideoIO_Plugin_API_preview* plugin_api_; + CvPluginCapture capture_; + +public: + static + Ptr create(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, + const std::string &filename, int camera) + { + CV_Assert(plugin_api); + CvPluginCapture capture = NULL; + if (plugin_api->v0.Capture_open) + { + CV_Assert(plugin_api->v0.Capture_release); + if (CV_ERROR_OK == plugin_api->v0.Capture_open(filename.empty() ? 0 : filename.c_str(), camera, &capture)) + { + CV_Assert(capture); + return makePtr(plugin_api, capture); + } + } + return Ptr(); + } + + PluginCapture(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, CvPluginCapture capture) + : plugin_api_(plugin_api), capture_(capture) + { + CV_Assert(plugin_api_); CV_Assert(capture_); + } + + ~PluginCapture() + { + CV_DbgAssert(plugin_api_->v0.Capture_release); + if (CV_ERROR_OK != plugin_api_->v0.Capture_release(capture_)) + CV_LOG_ERROR(NULL, "Video I/O: Can't release capture by plugin '" << plugin_api_->api_header.api_description << "'"); + capture_ = NULL; + } + double getProperty(int prop) const CV_OVERRIDE + { + double val = -1; + if (plugin_api_->v0.Capture_getProperty) + if (CV_ERROR_OK != plugin_api_->v0.Capture_getProperty(capture_, prop, &val)) + val = -1; + return val; + } + bool setProperty(int prop, double val) CV_OVERRIDE + { + if (plugin_api_->v0.Capture_setProperty) + if (CV_ERROR_OK == plugin_api_->v0.Capture_setProperty(capture_, prop, val)) + return true; + return false; + } + bool grabFrame() CV_OVERRIDE + { + if (plugin_api_->v0.Capture_grab) + if (CV_ERROR_OK == plugin_api_->v0.Capture_grab(capture_)) + return true; + return false; + } + static CvResult CV_API_CALL retrieve_callback(int stream_idx, const unsigned char* data, int step, int width, int height, int cn, void* userdata) + { + CV_UNUSED(stream_idx); + cv::_OutputArray* dst = static_cast(userdata); + if (!dst) + return CV_ERROR_FAIL; + cv::Mat(cv::Size(width, height), CV_MAKETYPE(CV_8U, cn), (void*)data, step).copyTo(*dst); + return CV_ERROR_OK; + } + bool retrieveFrame(int idx, cv::OutputArray img) CV_OVERRIDE + { + bool res = false; + if (plugin_api_->v0.Capture_retreive) + if (CV_ERROR_OK == plugin_api_->v0.Capture_retreive(capture_, idx, retrieve_callback, (cv::_OutputArray*)&img)) + res = true; + return res; + } + bool isOpened() const CV_OVERRIDE + { + return capture_ != NULL; // TODO always true + } + int getCaptureDomain() CV_OVERRIDE + { + return plugin_api_->v0.captureAPI; + } +}; + + +//================================================================================================== + +class PluginWriter : public cv::IVideoWriter +{ + const OpenCV_VideoIO_Plugin_API_preview* plugin_api_; + CvPluginWriter writer_; + +public: + static + Ptr create(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, + const std::string& filename, int fourcc, double fps, const cv::Size& sz, + const VideoWriterParameters& params) + { + CV_Assert(plugin_api); + CvPluginWriter writer = NULL; + if (plugin_api->api_header.api_version >= 1 && plugin_api->v1.Writer_open_with_params) + { + CV_Assert(plugin_api->v0.Writer_release); + CV_Assert(!filename.empty()); + std::vector vint_params = params.getIntVector(); + int* c_params = &vint_params[0]; + unsigned n_params = (unsigned)(vint_params.size() / 2); + + if (CV_ERROR_OK == plugin_api->v1.Writer_open_with_params(filename.c_str(), fourcc, fps, sz.width, sz.height, c_params, n_params, &writer)) + { + CV_Assert(writer); + return makePtr(plugin_api, writer); + } + } + else if (plugin_api->v0.Writer_open) + { + CV_Assert(plugin_api->v0.Writer_release); + CV_Assert(!filename.empty()); + const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true); + const int depth = params.get(VIDEOWRITER_PROP_DEPTH, CV_8U); + if (depth != CV_8U) + { + CV_LOG_WARNING(NULL, "Video I/O plugin doesn't support (due to lower API level) creation of VideoWriter with depth != CV_8U"); + return Ptr(); + } + if (CV_ERROR_OK == plugin_api->v0.Writer_open(filename.c_str(), fourcc, fps, sz.width, sz.height, isColor, &writer)) + { + CV_Assert(writer); + return makePtr(plugin_api, writer); + } + } + return Ptr(); + } + + PluginWriter(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, CvPluginWriter writer) + : plugin_api_(plugin_api), writer_(writer) + { + CV_Assert(plugin_api_); CV_Assert(writer_); + } + + ~PluginWriter() + { + CV_DbgAssert(plugin_api_->v0.Writer_release); + if (CV_ERROR_OK != plugin_api_->v0.Writer_release(writer_)) + CV_LOG_ERROR(NULL, "Video I/O: Can't release writer by plugin '" << plugin_api_->api_header.api_description << "'"); + writer_ = NULL; + } + double getProperty(int prop) const CV_OVERRIDE + { + double val = -1; + if (plugin_api_->v0.Writer_getProperty) + if (CV_ERROR_OK != plugin_api_->v0.Writer_getProperty(writer_, prop, &val)) + val = -1; + return val; + } + bool setProperty(int prop, double val) CV_OVERRIDE + { + if (plugin_api_->v0.Writer_setProperty) + if (CV_ERROR_OK == plugin_api_->v0.Writer_setProperty(writer_, prop, val)) + return true; + return false; + } + bool isOpened() const CV_OVERRIDE + { + return writer_ != NULL; // TODO always true + } + void write(cv::InputArray arr) CV_OVERRIDE + { + cv::Mat img = arr.getMat(); + CV_DbgAssert(writer_); + CV_Assert(plugin_api_->v0.Writer_write); + if (CV_ERROR_OK != plugin_api_->v0.Writer_write(writer_, img.data, (int)img.step[0], img.cols, img.rows, img.channels())) + { + CV_LOG_DEBUG(NULL, "Video I/O: Can't write frame by plugin '" << plugin_api_->api_header.api_description << "'"); + } + // TODO return bool result? + } + int getCaptureDomain() const CV_OVERRIDE + { + return plugin_api_->v0.captureAPI; + } +}; + + +}}} // namespace diff --git a/modules/videoio/src/cap_ffmpeg.cpp b/modules/videoio/src/cap_ffmpeg.cpp index 42124c527c..e80372106a 100644 --- a/modules/videoio/src/cap_ffmpeg.cpp +++ b/modules/videoio/src/cap_ffmpeg.cpp @@ -217,9 +217,20 @@ cv::Ptr cvCreateVideoWriter_FFMPEG_proxy(const std::string& fi #if defined(BUILD_PLUGIN) +#define NEW_PLUGIN + +#ifndef NEW_PLUGIN #define ABI_VERSION 0 #define API_VERSION 0 #include "plugin_api.hpp" +#else +#define CAPTURE_ABI_VERSION 1 +#define CAPTURE_API_VERSION 0 +#include "plugin_capture_api.hpp" +#define WRITER_ABI_VERSION 1 +#define WRITER_API_VERSION 0 +#include "plugin_writer_api.hpp" +#endif namespace cv { @@ -312,6 +323,7 @@ CvResult CV_API_CALL cv_capture_grab(CvPluginCapture handle) } } +#ifndef NEW_PLUGIN static CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, cv_videoio_retrieve_cb_t callback, void* userdata) { @@ -331,6 +343,27 @@ CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, return CV_ERROR_FAIL; } } +#else +static +CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, cv_videoio_capture_retrieve_cb_t callback, void* userdata) +{ + if (!handle) + return CV_ERROR_FAIL; + try + { + CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle; + Mat img; + // TODO: avoid unnecessary copying + if (instance->retrieveFrame(stream_idx, img)) + return callback(stream_idx, img.data, img.step, img.cols, img.rows, img.type(), userdata); + return CV_ERROR_FAIL; + } + catch(...) + { + return CV_ERROR_FAIL; + } +} +#endif static CvResult CV_API_CALL cv_writer_open(const char* filename, int fourcc, double fps, int width, int height, int isColor, @@ -395,6 +428,10 @@ CvResult CV_API_CALL cv_writer_write(CvPluginWriter handle, const unsigned char } } +} // namespace + +#ifndef NEW_PLUGIN + static const OpenCV_VideoIO_Plugin_API_preview plugin_api = { { @@ -418,13 +455,64 @@ static const OpenCV_VideoIO_Plugin_API_preview plugin_api = } }; -} // namespace - const OpenCV_VideoIO_Plugin_API_preview* opencv_videoio_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT { if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION) - return &cv::plugin_api; + return &plugin_api; return NULL; } +#else // NEW_PLUGIN + +static const OpenCV_VideoIO_Capture_Plugin_API capture_plugin_api = +{ + { + sizeof(OpenCV_VideoIO_Capture_Plugin_API), CAPTURE_ABI_VERSION, CAPTURE_API_VERSION, + CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS, + "FFmpeg OpenCV Video I/O Capture plugin" + }, + { + /* 1*/CAP_FFMPEG, + /* 2*/cv_capture_open, + /* 3*/cv_capture_release, + /* 4*/cv_capture_get_prop, + /* 5*/cv_capture_set_prop, + /* 6*/cv_capture_grab, + /* 7*/cv_capture_retrieve, + } +}; + +const OpenCV_VideoIO_Capture_Plugin_API* opencv_videoio_capture_plugin_init_v1(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT +{ + if (requested_abi_version == CAPTURE_ABI_VERSION && requested_api_version <= CAPTURE_API_VERSION) + return &capture_plugin_api; + return NULL; +} + +static const OpenCV_VideoIO_Writer_Plugin_API writer_plugin_api = +{ + { + sizeof(OpenCV_VideoIO_Writer_Plugin_API), WRITER_ABI_VERSION, WRITER_API_VERSION, + CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS, + "FFmpeg OpenCV Video I/O Writer plugin" + }, + { + /* 1*/CAP_FFMPEG, + /* 2*/cv_writer_open, + /* 3*/cv_writer_release, + /* 4*/cv_writer_get_prop, + /* 5*/cv_writer_set_prop, + /* 6*/cv_writer_write + } +}; + +const OpenCV_VideoIO_Writer_Plugin_API* opencv_videoio_writer_plugin_init_v1(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT +{ + if (requested_abi_version == WRITER_ABI_VERSION && requested_api_version <= WRITER_API_VERSION) + return &writer_plugin_api; + return NULL; +} + +#endif // NEW_PLUGIN + #endif // BUILD_PLUGIN diff --git a/modules/videoio/src/cap_gstreamer.cpp b/modules/videoio/src/cap_gstreamer.cpp index 4fb4afc362..0c2149f1c3 100644 --- a/modules/videoio/src/cap_gstreamer.cpp +++ b/modules/videoio/src/cap_gstreamer.cpp @@ -1846,9 +1846,12 @@ void handleMessage(GstElement * pipeline) #if defined(BUILD_PLUGIN) -#define ABI_VERSION 0 -#define API_VERSION 1 -#include "plugin_api.hpp" +#define CAPTURE_ABI_VERSION 1 +#define CAPTURE_API_VERSION 0 +#include "plugin_capture_api.hpp" +#define WRITER_ABI_VERSION 1 +#define WRITER_API_VERSION 1 +#include "plugin_writer_api.hpp" namespace cv { @@ -1946,7 +1949,7 @@ CvResult CV_API_CALL cv_capture_grab(CvPluginCapture handle) } static -CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, cv_videoio_retrieve_cb_t callback, void* userdata) +CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, cv_videoio_capture_retrieve_cb_t callback, void* userdata) { if (!handle) return CV_ERROR_FAIL; @@ -1956,7 +1959,7 @@ CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, Mat img; // TODO: avoid unnecessary copying - implement lower level GStreamerCapture::retrieve if (instance->retrieveFrame(stream_idx, img)) - return callback(stream_idx, img.data, img.step, img.cols, img.rows, img.channels(), userdata); + return callback(stream_idx, img.data, img.step, img.cols, img.rows, img.type(), userdata); return CV_ERROR_FAIL; } catch(...) @@ -2063,12 +2066,12 @@ CvResult CV_API_CALL cv_writer_write(CvPluginWriter handle, const unsigned char } } -static const OpenCV_VideoIO_Plugin_API_preview plugin_api = +static const OpenCV_VideoIO_Capture_Plugin_API capture_api = { { - sizeof(OpenCV_VideoIO_Plugin_API_preview), ABI_VERSION, API_VERSION, + sizeof(OpenCV_VideoIO_Capture_Plugin_API), CAPTURE_ABI_VERSION, CAPTURE_API_VERSION, CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS, - "GStreamer OpenCV Video I/O plugin" + "GStreamer OpenCV Video I/O Capture plugin" }, { /* 1*/CAP_GSTREAMER, @@ -2078,23 +2081,42 @@ static const OpenCV_VideoIO_Plugin_API_preview plugin_api = /* 5*/cv_capture_set_prop, /* 6*/cv_capture_grab, /* 7*/cv_capture_retrieve, - /* 8*/cv_writer_open, - /* 9*/cv_writer_release, - /* 10*/cv_writer_get_prop, - /* 11*/cv_writer_set_prop, - /* 12*/cv_writer_write + } +}; + +static const OpenCV_VideoIO_Writer_Plugin_API writer_api = +{ + { + sizeof(OpenCV_VideoIO_Writer_Plugin_API), WRITER_ABI_VERSION, WRITER_API_VERSION, + CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS, + "GStreamer OpenCV Video I/O Writer plugin" }, { - /* 13*/cv_writer_open_with_params + /* 1*/CAP_GSTREAMER, + /* 2*/cv_writer_open, + /* 3*/cv_writer_release, + /* 4*/cv_writer_get_prop, + /* 5*/cv_writer_set_prop, + /* 6*/cv_writer_write + }, + { + /* 7*/cv_writer_open_with_params } }; } // namespace -const OpenCV_VideoIO_Plugin_API_preview* opencv_videoio_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT +const OpenCV_VideoIO_Capture_Plugin_API* opencv_videoio_capture_plugin_init_v1(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT { - if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION) - return &cv::plugin_api; + if (requested_abi_version == CAPTURE_ABI_VERSION && requested_api_version <= CAPTURE_API_VERSION) + return &cv::capture_api; + return NULL; +} + +const OpenCV_VideoIO_Writer_Plugin_API* opencv_videoio_writer_plugin_init_v1(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT +{ + if (requested_abi_version == WRITER_ABI_VERSION && requested_api_version <= WRITER_API_VERSION) + return &cv::writer_api; return NULL; } diff --git a/modules/videoio/src/plugin_api.hpp b/modules/videoio/src/plugin_api.hpp index 1281ab57ee..011dff5ff6 100644 --- a/modules/videoio/src/plugin_api.hpp +++ b/modules/videoio/src/plugin_api.hpp @@ -2,6 +2,10 @@ // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. +// +// DEPRECATED. Do not use in new plugins +// + #ifndef PLUGIN_API_HPP #define PLUGIN_API_HPP @@ -129,7 +133,7 @@ struct OpenCV_VideoIO_Plugin_API_v0_0_api_entries /** @brief Get property value - @param handle Capture handle + @param handle Writer handle @param prop Property index @param[out] val property value @@ -139,7 +143,7 @@ struct OpenCV_VideoIO_Plugin_API_v0_0_api_entries /** @brief Set property value - @param handle Capture handle + @param handle Writer handle @param prop Property index @param val property value @@ -149,8 +153,8 @@ struct OpenCV_VideoIO_Plugin_API_v0_0_api_entries /** @brief Write frame - @param handle Capture handle - @param data Capture handle + @param handle Writer handle + @param data frame data @param step step in bytes @param width frame width in pixels @param height frame height diff --git a/modules/videoio/src/plugin_capture_api.hpp b/modules/videoio/src/plugin_capture_api.hpp new file mode 100644 index 0000000000..c18f77685a --- /dev/null +++ b/modules/videoio/src/plugin_capture_api.hpp @@ -0,0 +1,166 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef PLUGIN_CAPTURE_API_HPP +#define PLUGIN_CAPTURE_API_HPP + +#include +#include + +#if !defined(BUILD_PLUGIN) + +/// increased for backward-compatible changes, e.g. add new function +/// Caller API <= Plugin API -> plugin is fully compatible +/// Caller API > Plugin API -> plugin is not fully compatible, caller should use extra checks to use plugins with older API +#define CAPTURE_API_VERSION 0 + +/// increased for incompatible changes, e.g. remove function argument +/// Caller ABI == Plugin ABI -> plugin is compatible +/// Caller ABI > Plugin ABI -> plugin is not compatible, caller should use shim code to use old ABI plugins (caller may know how lower ABI works, so it is possible) +/// Caller ABI < Plugin ABI -> plugin can't be used (plugin should provide interface with lower ABI to handle that) +#define CAPTURE_ABI_VERSION 1 + +#else // !defined(BUILD_PLUGIN) + +#if !defined(CAPTURE_ABI_VERSION) || !defined(CAPTURE_API_VERSION) +#error "Plugin must define CAPTURE_ABI_VERSION and CAPTURE_API_VERSION before including plugin_capture_api.hpp" +#endif + +#endif // !defined(BUILD_PLUGIN) + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef CvResult (CV_API_CALL *cv_videoio_capture_retrieve_cb_t)(int stream_idx, unsigned const char* data, int step, int width, int height, int type, void* userdata); + +typedef struct CvPluginCapture_t* CvPluginCapture; + +struct OpenCV_VideoIO_Capture_Plugin_API_v1_0_api_entries +{ + /** OpenCV capture ID (VideoCaptureAPIs) + @note API-ENTRY 1, API-Version == 0 + */ + int id; + + /** @brief Open video capture + + @param filename File name or NULL to use camera_index instead + @param camera_index Camera index (used if filename == NULL) + @param[out] handle pointer on Capture handle + + @note API-CALL 2, API-Version == 0 + */ + CvResult (CV_API_CALL *Capture_open)(const char* filename, int camera_index, CV_OUT CvPluginCapture* handle); + + /** @brief Release Capture handle + + @param handle Capture handle + + @note API-CALL 3, API-Version == 0 + */ + CvResult (CV_API_CALL *Capture_release)(CvPluginCapture handle); + + /** @brief Get property value + + @param handle Capture handle + @param prop Property index + @param[out] val property value + + @note API-CALL 4, API-Version == 0 + */ + CvResult (CV_API_CALL *Capture_getProperty)(CvPluginCapture handle, int prop, CV_OUT double* val); + + /** @brief Set property value + + @param handle Capture handle + @param prop Property index + @param val property value + + @note API-CALL 5, API-Version == 0 + */ + CvResult (CV_API_CALL *Capture_setProperty)(CvPluginCapture handle, int prop, double val); + + /** @brief Grab frame + + @param handle Capture handle + + @note API-CALL 6, API-Version == 0 + */ + CvResult (CV_API_CALL *Capture_grab)(CvPluginCapture handle); + + /** @brief Retrieve frame + + @param handle Capture handle + @param stream_idx stream index to retrieve (BGR/IR/depth data) + @param callback retrieve callback (synchronous) + @param userdata callback context data + + @note API-CALL 7, API-Version == 0 + */ + CvResult (CV_API_CALL *Capture_retreive)(CvPluginCapture handle, int stream_idx, cv_videoio_capture_retrieve_cb_t callback, void* userdata); +}; // OpenCV_VideoIO_Capture_Plugin_API_v1_0_api_entries + +#if 0 +struct OpenCV_VideoIO_Capture_Plugin_API_v1_1_api_entries +{ + /** @brief TBD + + @note API-CALL XXX, API-Version == YYY + */ + CvResult (CV_API_CALL* zzz)( + ... + ); +}; // OpenCV_VideoIO_Capture_Plugin_API_v1_1_api_entries +#endif + +typedef struct OpenCV_VideoIO_Capture_Plugin_API_v1_0 +{ + OpenCV_API_Header api_header; + struct OpenCV_VideoIO_Capture_Plugin_API_v1_0_api_entries v0; +} OpenCV_VideoIO_Capture_Plugin_API_v1_0; + +#if 0 +typedef struct OpenCV_VideoIO_Capture_Plugin_API_v1_1 +{ + OpenCV_API_Header api_header; + struct OpenCV_VideoIO_Capture_Plugin_API_v1_0_api_entries v0; + struct OpenCV_VideoIO_Capture_Plugin_API_v1_1_api_entries v1; +} OpenCV_VideoIO_Capture_Plugin_API_v1_1; +#endif + +#if 0 //CAPTURE_ABI_VERSION == 1 && CAPTURE_API_VERSION == 1 +typedef struct OpenCV_VideoIO_Capture_Plugin_API_v1_1 OpenCV_VideoIO_Capture_Plugin_API; +#elif CAPTURE_ABI_VERSION == 1 && CAPTURE_API_VERSION == 0 +typedef struct OpenCV_VideoIO_Capture_Plugin_API_v1_0 OpenCV_VideoIO_Capture_Plugin_API; +#else +#error "Not supported configuration: check CAPTURE_ABI_VERSION/CAPTURE_API_VERSION" +#endif + +#ifdef BUILD_PLUGIN + +#ifndef CV_PLUGIN_EXPORTS +#if (defined _WIN32 || defined WINCE || defined __CYGWIN__) +# define CV_PLUGIN_EXPORTS __declspec(dllexport) +#elif defined __GNUC__ && __GNUC__ >= 4 +# define CV_PLUGIN_EXPORTS __attribute__ ((visibility ("default"))) +#endif +#endif + +CV_PLUGIN_EXPORTS +const OpenCV_VideoIO_Capture_Plugin_API* CV_API_CALL opencv_videoio_capture_plugin_init_v1 + (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/) CV_NOEXCEPT; + +#else // BUILD_PLUGIN +typedef const OpenCV_VideoIO_Capture_Plugin_API* (CV_API_CALL *FN_opencv_videoio_capture_plugin_init_t) + (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/); +#endif // BUILD_PLUGIN + + +#ifdef __cplusplus +} +#endif + +#endif // PLUGIN_CAPTURE_API_HPP diff --git a/modules/videoio/src/plugin_writer_api.hpp b/modules/videoio/src/plugin_writer_api.hpp new file mode 100644 index 0000000000..e1ce014e98 --- /dev/null +++ b/modules/videoio/src/plugin_writer_api.hpp @@ -0,0 +1,171 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef PLUGIN_WRITER_API_HPP +#define PLUGIN_WRITER_API_HPP + +#include +#include + +#if !defined(BUILD_PLUGIN) + +/// increased for backward-compatible changes, e.g. add new function +/// Caller API <= Plugin API -> plugin is fully compatible +/// Caller API > Plugin API -> plugin is not fully compatible, caller should use extra checks to use plugins with older API +#define WRITER_API_VERSION 1 + +/// increased for incompatible changes, e.g. remove function argument +/// Caller ABI == Plugin ABI -> plugin is compatible +/// Caller ABI > Plugin ABI -> plugin is not compatible, caller should use shim code to use old ABI plugins (caller may know how lower ABI works, so it is possible) +/// Caller ABI < Plugin ABI -> plugin can't be used (plugin should provide interface with lower ABI to handle that) +#define WRITER_ABI_VERSION 1 + +#else // !defined(BUILD_PLUGIN) + +#if !defined(WRITER_ABI_VERSION) || !defined(WRITER_API_VERSION) +#error "Plugin must define WRITER_ABI_VERSION and WRITER_API_VERSION before including plugin_writer_api.hpp" +#endif + +#endif // !defined(BUILD_PLUGIN) + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CvPluginWriter_t* CvPluginWriter; + +struct OpenCV_VideoIO_Writer_Plugin_API_v1_0_api_entries +{ + /** OpenCV capture ID (VideoCaptureAPIs) + @note API-ENTRY 1, API-Version == 0 + */ + int id; + + /** @brief Try to open video writer + + @param filename Destination location + @param fourcc FOURCC code + @param fps FPS + @param width frame width + @param height frame height + @param isColor true if video stream should save color frames + @param[out] handle pointer on Writer handle + + @note API-CALL 2, API-Version == 0 + */ + CvResult (CV_API_CALL *Writer_open)(const char* filename, int fourcc, double fps, int width, int height, int isColor, + CV_OUT CvPluginWriter* handle); + + /** @brief Release Writer handle + + @param handle Writer handle + + @note API-CALL 3, API-Version == 0 + */ + CvResult (CV_API_CALL *Writer_release)(CvPluginWriter handle); + + /** @brief Get property value + + @param handle Writer handle + @param prop Property index + @param[out] val property value + + @note API-CALL 4, API-Version == 0 + */ + CvResult (CV_API_CALL *Writer_getProperty)(CvPluginWriter handle, int prop, CV_OUT double* val); + + /** @brief Set property value + + @param handle Writer handle + @param prop Property index + @param val property value + + @note API-CALL 5, API-Version == 0 + */ + CvResult (CV_API_CALL *Writer_setProperty)(CvPluginWriter handle, int prop, double val); + + /** @brief Write frame + + @param handle Writer handle + @param data frame data + @param step step in bytes + @param width frame width in pixels + @param height frame height + @param cn number of channels per pixel + + @note API-CALL 6, API-Version == 0 + */ + CvResult (CV_API_CALL *Writer_write)(CvPluginWriter handle, const unsigned char *data, int step, int width, int height, int cn); +}; // OpenCV_VideoIO_Writer_Plugin_API_v1_0_api_entries + +struct OpenCV_VideoIO_Writer_Plugin_API_v1_1_api_entries +{ + /** @brief Try to open video writer + + @param filename Destination location + @param fourcc FOURCC code + @param fps FPS + @param width frame width + @param height frame height + @param params pointer on 2*n_params array of 'key,value' pairs + @param n_params number of passed parameters + @param[out] handle pointer on Writer handle + + @note API-CALL 7, API-Version == 1 + */ + CvResult (CV_API_CALL* Writer_open_with_params)( + const char* filename, int fourcc, double fps, int width, int height, + int* params, unsigned n_params, + CV_OUT CvPluginWriter* handle + ); +}; // OpenCV_VideoIO_Writer_Plugin_API_v1_1_api_entries + +typedef struct OpenCV_VideoIO_Writer_Plugin_API_v1_0 +{ + OpenCV_API_Header api_header; + struct OpenCV_VideoIO_Writer_Plugin_API_v1_0_api_entries v0; +} OpenCV_VideoIO_Writer_Plugin_API_v1_0; + +typedef struct OpenCV_VideoIO_Writer_Plugin_API_v1_1 +{ + OpenCV_API_Header api_header; + struct OpenCV_VideoIO_Writer_Plugin_API_v1_0_api_entries v0; + struct OpenCV_VideoIO_Writer_Plugin_API_v1_1_api_entries v1; +} OpenCV_VideoIO_Writer_Plugin_API_v1_1; + + +#if WRITER_ABI_VERSION == 1 && WRITER_API_VERSION == 1 +typedef struct OpenCV_VideoIO_Writer_Plugin_API_v1_1 OpenCV_VideoIO_Writer_Plugin_API; +#elif WRITER_ABI_VERSION == 1 && WRITER_API_VERSION == 0 +typedef struct OpenCV_VideoIO_Writer_Plugin_API_v1_0 OpenCV_VideoIO_Writer_Plugin_API; +#else +#error "Not supported configuration: check WRITER_ABI_VERSION/WRITER_API_VERSION" +#endif + +#ifdef BUILD_PLUGIN + +#ifndef CV_PLUGIN_EXPORTS +#if (defined _WIN32 || defined WINCE || defined __CYGWIN__) +# define CV_PLUGIN_EXPORTS __declspec(dllexport) +#elif defined __GNUC__ && __GNUC__ >= 4 +# define CV_PLUGIN_EXPORTS __attribute__ ((visibility ("default"))) +#endif +#endif + +CV_PLUGIN_EXPORTS +const OpenCV_VideoIO_Writer_Plugin_API* CV_API_CALL opencv_videoio_writer_plugin_init_v1 + (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/) CV_NOEXCEPT; + +#else // BUILD_PLUGIN +typedef const OpenCV_VideoIO_Writer_Plugin_API* (CV_API_CALL *FN_opencv_videoio_writer_plugin_init_t) + (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/); +#endif // BUILD_PLUGIN + + +#ifdef __cplusplus +} +#endif + +#endif // PLUGIN_WRITER_API_HPP