From b4cba524d3815e5db5703e3808e7c37813dfff23 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 4 Mar 2019 20:06:19 +0000 Subject: [PATCH 1/3] videoio: move backend.cpp -> backend_plugin.cpp --- modules/videoio/src/{backend.cpp => backend_plugin.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/videoio/src/{backend.cpp => backend_plugin.cpp} (100%) diff --git a/modules/videoio/src/backend.cpp b/modules/videoio/src/backend_plugin.cpp similarity index 100% rename from modules/videoio/src/backend.cpp rename to modules/videoio/src/backend_plugin.cpp From 403f11fdbf9e3e6d219f3fc54d3397d0cb1d8b91 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 2 Mar 2019 22:36:18 +0000 Subject: [PATCH 2/3] videoio: update plugin API --- modules/core/include/opencv2/core.hpp | 1 + .../core/include/opencv2/core/llapi/llapi.h | 94 +++ modules/videoio/CMakeLists.txt | 3 +- modules/videoio/cmake/plugin.cmake | 4 +- modules/videoio/src/backend.hpp | 75 +-- modules/videoio/src/backend_plugin.cpp | 572 +++++++++--------- modules/videoio/src/backend_static.cpp | 72 +++ modules/videoio/src/cap_ffmpeg.cpp | 194 +++--- modules/videoio/src/cap_ffmpeg_api.hpp | 6 +- modules/videoio/src/cap_gstreamer.cpp | 199 +++--- modules/videoio/src/plugin_api.cpp | 19 - modules/videoio/src/plugin_api.hpp | 175 ++++-- 12 files changed, 863 insertions(+), 551 deletions(-) create mode 100644 modules/core/include/opencv2/core/llapi/llapi.h create mode 100644 modules/videoio/src/backend_static.cpp delete mode 100644 modules/videoio/src/plugin_api.cpp diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index 24490e0d7b..1cdb470b58 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -92,6 +92,7 @@ @{ @defgroup core_hal_intrin_impl Private implementation helpers @} + @defgroup core_lowlevel_api Low-level API for external libraries / plugins @} @} */ diff --git a/modules/core/include/opencv2/core/llapi/llapi.h b/modules/core/include/opencv2/core/llapi/llapi.h new file mode 100644 index 0000000000..e6008ce9bf --- /dev/null +++ b/modules/core/include/opencv2/core/llapi/llapi.h @@ -0,0 +1,94 @@ +// 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 OPENCV_CORE_LLAPI_LLAPI_H +#define OPENCV_CORE_LLAPI_LLAPI_H +/** +@addtogroup core_lowlevel_api + +API for OpenCV external plugins: +- HAL accelerators +- VideoIO camera backends / decoders / encoders +- Imgcodecs encoders / decoders + +Plugins are usually built separately or before OpenCV (OpenCV can depend on them - like HAL libraries). + +Using this approach OpenCV provides some basic low level functionality for external plugins. + +@note Preview only (no backward compatibility) + +@{ +*/ + +#ifndef CV_API_CALL +//! calling convention (including callbacks) +#define CV_API_CALL +#endif + +typedef enum cvResult +{ + CV_ERROR_FAIL = -1, //!< Some error occured (TODO Require to fill exception information) + CV_ERROR_OK = 0 //!< No error +} CvResult; + +typedef struct OpenCV_API_Header_t +{ + /** @brief valid size of this structure + @details `assert(api.header.valid_size >= sizeof(OpenCV__API_v));` + */ + size_t valid_size; + unsigned min_api_version; //!< backward compatible API version + unsigned api_version; //!< provided API version (features) + unsigned opencv_version_major; //!< compiled OpenCV version + unsigned opencv_version_minor; //!< compiled OpenCV version + unsigned opencv_version_patch; //!< compiled OpenCV version + const char* opencv_version_status; //!< compiled OpenCV version + const char* api_description; //!< API description (debug purposes only) +} OpenCV_API_Header; + + + +#if 0 + +typedef int (CV_API_CALL *cv_example_callback1_cb_t)(unsigned const char* cb_result, void* cb_context); + +struct OpenCV_Example_API_v1 +{ + OpenCV_API_Header header; + + /** @brief Some API call + + @param param1 description1 + @param param2 description2 + + @note API-CALL 1, API-Version >=1 + */ + CvResult (CV_API_CALL *Request1)(int param1, const char* param2); + + /** @brief Register callback + + @param callback function to handle callback + @param cb_context context data passed to callback function + @param[out] cb_handle callback id (used to unregister callback) + + @note API-CALL 2, API-Version >=1 + */ + CvResult (CV_API_CALL *RegisterCallback)(cv_example_callback1_cb_t callback, void* cb_context, CV_OUT unsigned* cb_handle); + + /** @brief Unregister callback + + @param cb_handle callback handle + + @note API-CALL 3, API-Version >=1 + */ + CvResult (CV_API_CALL *UnegisterCallback)(unsigned cb_handle); + + ... +}; +#endif // 0 + +//! @} + +#endif // OPENCV_CORE_LLAPI_LLAPI_H diff --git a/modules/videoio/CMakeLists.txt b/modules/videoio/CMakeLists.txt index 7dcabb335d..9bbe041390 100644 --- a/modules/videoio/CMakeLists.txt +++ b/modules/videoio/CMakeLists.txt @@ -14,7 +14,8 @@ set(videoio_srcs "${CMAKE_CURRENT_LIST_DIR}/src/cap_images.cpp" "${CMAKE_CURRENT_LIST_DIR}/src/cap_mjpeg_encoder.cpp" "${CMAKE_CURRENT_LIST_DIR}/src/cap_mjpeg_decoder.cpp" - "${CMAKE_CURRENT_LIST_DIR}/src/backend.cpp" + "${CMAKE_CURRENT_LIST_DIR}/src/backend_plugin.cpp" + "${CMAKE_CURRENT_LIST_DIR}/src/backend_static.cpp" "${CMAKE_CURRENT_LIST_DIR}/src/container_avi.cpp") file(GLOB videoio_ext_hdrs diff --git a/modules/videoio/cmake/plugin.cmake b/modules/videoio/cmake/plugin.cmake index 8aa05c2978..bdfb4bae46 100644 --- a/modules/videoio/cmake/plugin.cmake +++ b/modules/videoio/cmake/plugin.cmake @@ -12,7 +12,7 @@ function(ocv_create_builtin_videoio_plugin name target videoio_src_file) add_library(${name} MODULE "${CMAKE_CURRENT_LIST_DIR}/src/${videoio_src_file}" - "${CMAKE_CURRENT_LIST_DIR}/src/plugin_api.cpp") + ) target_include_directories(${name} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") target_compile_definitions(${name} PRIVATE BUILD_PLUGIN) target_link_libraries(${name} PRIVATE ${target}) @@ -60,7 +60,7 @@ function(ocv_create_videoio_plugin default_name target target_desc videoio_src_f set(imgproc_ROOT "${modules_ROOT}/imgproc") set(imgcodecs_ROOT "${modules_ROOT}/imgcodecs") - add_library(${OPENCV_PLUGIN_NAME} MODULE "${videoio_ROOT}/src/${videoio_src_file}" "${videoio_ROOT}/src/plugin_api.cpp") + add_library(${OPENCV_PLUGIN_NAME} MODULE "${videoio_ROOT}/src/${videoio_src_file}") target_include_directories(${OPENCV_PLUGIN_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" "${videoio_ROOT}/src" diff --git a/modules/videoio/src/backend.hpp b/modules/videoio/src/backend.hpp index 251b4d32fd..e8b269ba79 100644 --- a/modules/videoio/src/backend.hpp +++ b/modules/videoio/src/backend.hpp @@ -15,71 +15,30 @@ namespace cv { class IBackend { public: - Ptr tryOpenCapture(const std::string & backendName, const std::string & filename, int cameraNum) const; - Ptr tryOpenWriter(const std::string & backendName, const std::string& filename, int _fourcc, double fps, const Size &frameSize, bool isColor) const; -protected: - virtual Ptr createCapture(const std::string &filename, int camera) const = 0; - virtual Ptr createWriter(const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const = 0; virtual ~IBackend() {} + virtual Ptr createCapture(int camera) const = 0; + virtual Ptr createCapture(const std::string &filename) const = 0; + virtual Ptr createWriter(const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const = 0; }; -//================================================================================================== - -class StaticBackend : public IBackend -{ - typedef Ptr (*OpenFileFun)(const std::string &); - typedef Ptr (*OpenCamFun)(int); - typedef Ptr (*OpenWriterFun)(const std::string&, int, double, const Size&, bool); -private: - OpenFileFun FUN_FILE; - OpenCamFun FUN_CAM; - OpenWriterFun FUN_WRITE; -public: - StaticBackend(OpenFileFun f1, OpenCamFun f2, OpenWriterFun f3) - : FUN_FILE(f1), FUN_CAM(f2), FUN_WRITE(f3) - { - } -protected: - Ptr createCapture(const std::string &filename, int camera) const CV_OVERRIDE - { - if (filename.empty() && FUN_CAM) - return FUN_CAM(camera); - if (FUN_FILE) - return FUN_FILE(filename); - return 0; - } - Ptr createWriter(const std::string &filename, int fourcc, double fps, const Size &sz, bool isColor) const CV_OVERRIDE - { - if (FUN_WRITE) - return FUN_WRITE(filename, fourcc, fps, sz, isColor); - return 0; - } -}; - -//================================================================================================== - -class DynamicBackend : public IBackend +class IBackendFactory { public: - class CaptureTable; - class WriterTable; - class DynamicLib; -private: - DynamicLib * lib; - CaptureTable const * cap_tbl; - WriterTable const * wri_tbl; -public: - DynamicBackend(const std::string &filename); - ~DynamicBackend(); - static Ptr load(VideoCaptureAPIs api, int mode); -protected: - bool canCreateCapture(cv::VideoCaptureAPIs api) const; - bool canCreateWriter(VideoCaptureAPIs api) const; - Ptr createCapture(const std::string &filename, int camera) const CV_OVERRIDE; - Ptr createWriter(const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const CV_OVERRIDE; + virtual ~IBackendFactory() {} + virtual Ptr getBackend() const = 0; }; -} // cv:: +//============================================================================= +typedef Ptr (*FN_createCaptureFile)(const std::string & filename); +typedef Ptr (*FN_createCaptureCamera)(int camera); +typedef Ptr (*FN_createWriter)(const std::string& filename, int fourcc, double fps, const Size& sz, bool isColor); +Ptr createBackendFactory(FN_createCaptureFile createCaptureFile, + FN_createCaptureCamera createCaptureCamera, + FN_createWriter createWriter); + +Ptr createPluginBackendFactory(VideoCaptureAPIs id, const char* baseName); + +} // namespace cv:: #endif // BACKEND_HPP_DEFINED diff --git a/modules/videoio/src/backend_plugin.cpp b/modules/videoio/src/backend_plugin.cpp index 49e2bad40d..ab6960f398 100644 --- a/modules/videoio/src/backend_plugin.cpp +++ b/modules/videoio/src/backend_plugin.cpp @@ -12,69 +12,6 @@ #include "opencv2/core/private.hpp" #include "videoio_registry.hpp" -//================================================================================================== -// IBackend implementation - -namespace cv { - -static bool param_VIDEOIO_DEBUG = utils::getConfigurationParameterBool("OPENCV_VIDEOIO_DEBUG", false); -static bool param_VIDEOCAPTURE_DEBUG = utils::getConfigurationParameterBool("OPENCV_VIDEOCAPTURE_DEBUG", false); -static bool param_VIDEOWRITER_DEBUG = utils::getConfigurationParameterBool("OPENCV_VIDEOWRITER_DEBUG", false); - -Ptr IBackend::tryOpenCapture(const std::string & backendName, const std::string & filename, int cameraNum) const -{ - try - { - if (param_VIDEOIO_DEBUG || param_VIDEOCAPTURE_DEBUG) - CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): trying ...\n", backendName.c_str())); - Ptr icap = createCapture(filename, cameraNum); - if (param_VIDEOIO_DEBUG ||param_VIDEOCAPTURE_DEBUG) - CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): result=%p isOpened=%d ...\n", backendName.c_str(), icap.empty() ? NULL : icap.get(), icap.empty() ? -1: icap->isOpened())); - return icap; - } - catch(const cv::Exception& e) - { - CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised OpenCV exception:\n\n%s\n", backendName.c_str(), e.what())); - } - catch (const std::exception& e) - { - CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised C++ exception:\n\n%s\n", backendName.c_str(), e.what())); - } - catch(...) - { - CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised unknown C++ exception!\n\n", backendName.c_str())); - } - return 0; -} - -Ptr IBackend::tryOpenWriter(const std::string & backendName, const std::string& filename, int _fourcc, double fps, const Size &frameSize, bool isColor) const -{ - try - { - if (param_VIDEOIO_DEBUG || param_VIDEOWRITER_DEBUG) - CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): trying ...\n", backendName.c_str())); - Ptr iwriter = createWriter(filename, _fourcc, fps, frameSize, isColor); - if (param_VIDEOIO_DEBUG || param_VIDEOWRITER_DEBUG) - CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): result=%p isOpened=%d...\n", backendName.c_str(), iwriter.empty() ? NULL : iwriter.get(), iwriter.empty() ? iwriter->isOpened() : -1)); - return iwriter; - } - catch(const cv::Exception& e) - { - CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised OpenCV exception:\n\n%s\n", backendName.c_str(), e.what())); - } - catch (const std::exception& e) - { - CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised C++ exception:\n\n%s\n", backendName.c_str(), e.what())); - } - catch(...) - { - CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised unknown C++ exception!\n\n", backendName.c_str())); - } - return 0; -} - -} // cv:: - //================================================================================================== // Dynamic backend implementation @@ -88,34 +25,52 @@ using namespace std; #include #endif -inline static void * getSymbol_(void *h, const std::string &symbolName) +namespace cv { namespace impl { + +#if defined(_WIN32) +typedef HMODULE LibHandle_t; +#elif defined(__linux__) || defined(__APPLE__) +typedef void* LibHandle_t; +#endif + +static Mutex& getInitializationMutex() +{ + static Mutex initializationMutex; + return initializationMutex; +} + +static inline +void* getSymbol_(LibHandle_t h, const char* symbolName) { #if defined(_WIN32) - return (void*)GetProcAddress(static_cast(h), symbolName.c_str()); + return (void*)GetProcAddress(h, symbolName); #elif defined(__linux__) || defined(__APPLE__) - return dlsym(h, symbolName.c_str()); + return dlsym(h, symbolName); #endif } -inline static void * libraryLoad_(const std::string &filename) +static inline +LibHandle_t libraryLoad_(const char* filename) { #if defined(_WIN32) - return static_cast(LoadLibraryA(filename.c_str())); + return LoadLibraryA(filename); #elif defined(__linux__) || defined(__APPLE__) - return dlopen(filename.c_str(), RTLD_LAZY); + return dlopen(filename, RTLD_LAZY); #endif } -inline static void libraryRelease_(void *h) +static inline +void libraryRelease_(LibHandle_t h) { #if defined(_WIN32) - FreeLibrary(static_cast(h)); + FreeLibrary(h); #elif defined(__linux__) || defined(__APPLE__) dlclose(h); #endif } -inline static std::string libraryPrefix() +static inline +std::string libraryPrefix() { #if defined(_WIN32) return string(); @@ -123,23 +78,24 @@ inline static std::string libraryPrefix() return "lib"; #endif } -inline static std::string librarySuffix() +static inline +std::string librarySuffix() { #if defined(_WIN32) - return "dll"; + return ".dll"; #elif defined(__APPLE__) - return "dylib"; + return ".dylib"; #else - return "so"; + return ".so"; #endif } //============================ -class cv::DynamicBackend::DynamicLib +class DynamicLib { private: - void * handle; + LibHandle_t handle; const std::string fname; public: @@ -156,7 +112,7 @@ public: { return handle != NULL; } - void* getSymbol(const std::string & symbolName) const + void* getSymbol(const char* symbolName) const { if (!handle) { @@ -167,11 +123,11 @@ public: CV_LOG_ERROR(NULL, "No symbol '" << symbolName << "' in " << fname); return res; } - + const std::string& getName() const { return fname; } private: void libraryLoad(const std::string &filename) { - handle = libraryLoad_(filename); + handle = libraryLoad_(filename.c_str()); CV_LOG_INFO(NULL, "load " << filename << " => " << (handle ? "OK" : "FAILED")); } void libraryRelease() @@ -192,300 +148,346 @@ private: //============================ -// Utility function -static bool verifyVersion(cv_get_version_t * fun) -{ - if (!fun) - return false; - int major, minor, patch, api, abi; - fun(major, minor, patch, api, abi); - if (api < API_VERSION || abi != ABI_VERSION) - { - CV_LOG_ERROR(NULL, "Bad plugin API/ABI (" << api << "/" << abi << "), expected " << API_VERSION << "/" << ABI_VERSION); - return false; - } -#ifdef STRICT_PLUGIN_CHECK - if (major != CV_MAJOR_VERSION || minor != CV_MINOR_VERSION) - { - CV_LOG_ERROR(NULL, "Bad plugin version (" << major << "." << minor << "), expected " << CV_MAJOR_VERSION << "/" << CV_MINOR_VERSION); - return false; - } -#endif - return true; -} - -//============================ - -class cv::DynamicBackend::CaptureTable +class PluginBackend: public IBackend { public: - cv_get_version_t *cv_get_version; - cv_domain_t *cv_domain; - cv_open_capture_t *cv_open_capture; - cv_get_cap_prop_t *cv_get_cap_prop; - cv_set_cap_prop_t *cv_set_cap_prop; - cv_grab_t *cv_grab; - cv_retrieve_t *cv_retrieve; - cv_release_capture_t *cv_release_capture; - bool isComplete; -public: - CaptureTable(const cv::DynamicBackend::DynamicLib & p) - : isComplete(true) + Ptr lib_; + const OpenCV_VideoIO_Plugin_API_preview* plugin_api_; + + PluginBackend(const Ptr& lib) : + lib_(lib), plugin_api_(NULL) { - #define READ_FUN(name) \ - name = reinterpret_cast(p.getSymbol(#name)); \ - isComplete = isComplete && (name) - - READ_FUN(cv_get_version); - READ_FUN(cv_domain); - READ_FUN(cv_open_capture); - READ_FUN(cv_get_cap_prop); - READ_FUN(cv_set_cap_prop); - READ_FUN(cv_grab); - READ_FUN(cv_retrieve); - READ_FUN(cv_release_capture); - - #undef READ_FUN + 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) + { + plugin_api_ = fn_init(ABI_VERSION, API_VERSION, NULL); + if (!plugin_api_) + { + CV_LOG_INFO(NULL, "Video I/O: plugin is incompatible: " << lib->getName()); + return; + } + // TODO Preview: add compatibility API/ABI checks + CV_LOG_INFO(NULL, "Video I/O: loaded plugin '" << 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()); + } } + + Ptr createCapture(int camera) const CV_OVERRIDE; + Ptr createCapture(const std::string &filename) const CV_OVERRIDE; + Ptr createWriter(const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const CV_OVERRIDE; }; -class cv::DynamicBackend::WriterTable +class PluginBackendFactory : public IBackendFactory { public: - cv_get_version_t *cv_get_version; - cv_domain_t *cv_domain; - cv_open_writer_t *cv_open_writer; - cv_get_wri_prop_t *cv_get_wri_prop; - cv_set_wri_prop_t *cv_set_wri_prop; - cv_write_t *cv_write; - cv_release_writer_t *cv_release_writer; - bool isComplete; + VideoCaptureAPIs id_; + const char* baseName_; + Ptr backend; + bool initialized; public: - WriterTable(const cv::DynamicBackend::DynamicLib & p) - : isComplete(true) + PluginBackendFactory(VideoCaptureAPIs id, const char* baseName) : + id_(id), baseName_(baseName), + initialized(false) { - #define READ_FUN(name) \ - name = reinterpret_cast(p.getSymbol(#name)); \ - isComplete = isComplete && (name) - - READ_FUN(cv_get_version); - READ_FUN(cv_domain); - READ_FUN(cv_open_writer); - READ_FUN(cv_get_wri_prop); - READ_FUN(cv_set_wri_prop); - READ_FUN(cv_write); - READ_FUN(cv_release_writer); - - #undef READ_FUN + // nothing, plugins are loaded on demand } + + Ptr getBackend() const CV_OVERRIDE + { + if (!initialized) + { + const_cast(this)->initBackend(); + } + return backend.staticCast(); + } +protected: + void initBackend() + { + AutoLock lock(getInitializationMutex()); + try { + if (!initialized) + loadPlugin(); + } + catch (...) + { + CV_LOG_INFO(NULL, "Video I/O: exception during plugin loading: " << baseName_ << ". SKIP"); + } + initialized = true; + } + void loadPlugin(); }; -//============================ - -class DynamicCapture; -class DynamicWriter; - -cv::DynamicBackend::DynamicBackend(const std::string &filename) - : lib(0), cap_tbl(0), wri_tbl(0) -{ - lib = new DynamicLib(filename); - if (lib->isLoaded()) - { - cap_tbl = new CaptureTable(*lib); - wri_tbl = new WriterTable(*lib); - } -} - -cv::DynamicBackend::~DynamicBackend() -{ - if (cap_tbl) - delete cap_tbl; - if (wri_tbl) - delete wri_tbl; - if (lib) - delete lib; -} - -bool cv::DynamicBackend::canCreateCapture(VideoCaptureAPIs api) const -{ - return lib && lib->isLoaded() && cap_tbl && cap_tbl->isComplete && verifyVersion(cap_tbl->cv_get_version) && (cap_tbl->cv_domain() == api); -} - -bool cv::DynamicBackend::canCreateWriter(VideoCaptureAPIs api) const -{ - return lib && lib->isLoaded() && wri_tbl && wri_tbl->isComplete && verifyVersion(wri_tbl->cv_get_version) && (wri_tbl->cv_domain() == api); -} - -cv::Ptr cv::DynamicBackend::createCapture(const std::string & filename, int camera) const -{ - return makePtr(cap_tbl, filename, camera).staticCast(); -} - -cv::Ptr cv::DynamicBackend::createWriter(const std::string & filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const -{ - return makePtr(wri_tbl, filename, fourcc, fps, sz, isColor).staticCast(); -} - -inline static std::vector getPluginCandidates() +static +std::vector getPluginCandidates(const std::string& baseName) { using namespace cv::utils; using namespace cv::utils::fs; - const vector default_paths = { getParent(getBinLocation()) }; +#ifndef CV_VIDEOIO_PLUGIN_SUBDIRECTORY +#define CV_VIDEOIO_PLUGIN_SUBDIRECTORY_STR "" +#else +#define CV_VIDEOIO_PLUGIN_SUBDIRECTORY_STR CVAUX_STR(CV_VIDEOIO_PLUGIN_SUBDIRECTORY) +#endif + const vector default_paths = { utils::fs::join(getParent(getBinLocation()), CV_VIDEOIO_PLUGIN_SUBDIRECTORY_STR) }; const vector paths = getConfigurationParameterPaths("OPENCV_VIDEOIO_PLUGIN_PATH", default_paths); - const string default_expr = libraryPrefix() + "opencv_videoio_*." + librarySuffix(); - const string expr = getConfigurationParameterString("OPENCV_VIDEOIO_PLUGIN_NAME", default_expr.c_str()); - CV_LOG_INFO(NULL, "VideoIO pluigins: glob is '" << expr << "', " << paths.size() << " location(s)"); + const string baseName_l = toLowerCase(baseName); + const string baseName_u = toUpperCase(baseName); + const string default_expr = libraryPrefix() + "opencv_videoio_" + baseName_l + "*" + librarySuffix(); + const string expr = getConfigurationParameterString((std::string("OPENCV_VIDEOIO_PLUGIN_") + baseName_u).c_str(), default_expr.c_str()); + CV_LOG_INFO(NULL, "VideoIO pluigin (" << baseName << "): glob is '" << expr << "', " << paths.size() << " location(s)"); vector results; for(const string & path : paths) { if (path.empty()) continue; vector candidates; - cv::glob(join(path, expr), candidates); - CV_LOG_INFO(NULL, "VideoIO pluigins in " << path << ": " << candidates.size()); + cv::glob(utils::fs::join(path, expr), candidates); + CV_LOG_INFO(NULL, " - " << path << ": " << candidates.size()); copy(candidates.begin(), candidates.end(), back_inserter(results)); } - CV_LOG_INFO(NULL, "Found " << results.size() << " plugin(s)"); + CV_LOG_INFO(NULL, "Found " << results.size() << " plugin(s) for " << baseName); return results; } -cv::Ptr cv::DynamicBackend::load(cv::VideoCaptureAPIs api, int mode) +void PluginBackendFactory::loadPlugin() { - for(const std::string & plugin : getPluginCandidates()) + for(const std::string & plugin : getPluginCandidates(baseName_)) { - bool res = true; - Ptr factory = makePtr(plugin); - if (factory) - if (mode & cv::MODE_CAPTURE_BY_INDEX || mode & cv::MODE_CAPTURE_BY_FILENAME) - { - res = res && factory->canCreateCapture(api); - } - if (mode & cv::MODE_WRITER) + Ptr lib = makePtr(plugin); + if (!lib->isLoaded()) + continue; + try { - res = res && factory->canCreateWriter(api); + Ptr pluginBackend = makePtr(lib); + if (pluginBackend && pluginBackend->plugin_api_) + { + backend = pluginBackend; + return; + } + } + catch (...) + { + CV_LOG_INFO(NULL, "Video I/O: exception during plugin initialization: " << plugin << ". SKIP"); } - if (res) - return factory; } - return 0; } -//================================================================================================== -// DynamicCapture -class DynamicCapture : public cv::IVideoCapture +//================================================================================================== + +class PluginCapture : public cv::IVideoCapture { - const cv::DynamicBackend::CaptureTable * tbl; - void * capture; + const OpenCV_VideoIO_Plugin_API_preview* plugin_api_; + CvPluginCapture capture_; + public: - DynamicCapture(const cv::DynamicBackend::CaptureTable * tbl_, const std::string &filename, int camera) - : tbl(tbl_), capture(0) + static + Ptr create(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, + const std::string &filename, int camera) { - CV_Assert(!capture); - if (tbl->cv_open_capture) - tbl->cv_open_capture(filename.empty() ? 0 : filename.c_str(), camera, capture); - } - ~DynamicCapture() - { - if (capture) + CV_Assert(plugin_api); + CvPluginCapture capture = NULL; + if (plugin_api->Capture_open) { - CV_Assert(tbl->cv_release_capture); - tbl->cv_release_capture(capture); - capture = 0; + CV_Assert(plugin_api->Capture_release); + if (CV_ERROR_OK == plugin_api->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_->Capture_release); + if (CV_ERROR_OK != plugin_api_->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 { - CV_Assert(capture); double val = -1; - tbl->cv_get_cap_prop(capture, prop, val); + if (plugin_api_->Capture_getProperty) + if (CV_ERROR_OK != plugin_api_->Capture_getProperty(capture_, prop, &val)) + val = -1; return val; } bool setProperty(int prop, double val) CV_OVERRIDE { - CV_Assert(capture); - return tbl->cv_set_cap_prop(capture, prop, val); + if (plugin_api_->Capture_setProperty) + if (CV_ERROR_OK == plugin_api_->Capture_setProperty(capture_, prop, val)) + return true; + return false; } bool grabFrame() CV_OVERRIDE { - CV_Assert(capture); - return tbl->cv_grab(capture); + if (plugin_api_->Capture_grab) + if (CV_ERROR_OK == plugin_api_->Capture_grab(capture_)) + return true; + return false; } - static bool local_retrieve(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 cn, void* userdata) { - cv::Mat * img = static_cast(userdata); - if (!img) - return false; - cv::Mat(cv::Size(width, height), CV_MAKETYPE(CV_8U, cn), data, step).copyTo(*img); - return true; + 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 { - CV_Assert(capture); - cv::Mat frame; - bool res = tbl->cv_retrieve(capture, idx, &local_retrieve, &frame); - if (res) - frame.copyTo(img); + bool res = false; + if (plugin_api_->Capture_retreive) + if (CV_ERROR_OK == plugin_api_->Capture_retreive(capture_, idx, retrieve_callback, (cv::_OutputArray*)&img)) + res = true; return res; } bool isOpened() const CV_OVERRIDE { - return capture != NULL; + return capture_ != NULL; // TODO always true } int getCaptureDomain() CV_OVERRIDE { - return tbl->cv_domain(); + return plugin_api_->captureAPI; } }; -//================================================================================================== -// DynamicWriter -class DynamicWriter : public cv::IVideoWriter +//================================================================================================== + +class PluginWriter : public cv::IVideoWriter { - const cv::DynamicBackend::WriterTable * tbl; - void * writer; + const OpenCV_VideoIO_Plugin_API_preview* plugin_api_; + CvPluginWriter writer_; + public: - DynamicWriter(const cv::DynamicBackend::WriterTable * tbl_, const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) - : tbl(tbl_), writer(0) + static + Ptr create(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, + const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) { - CV_Assert(!writer); - if(tbl->cv_open_writer) - tbl->cv_open_writer(filename.empty() ? 0 : filename.c_str(), fourcc, fps, sz.width, sz.height, isColor, writer); - } - ~DynamicWriter() - { - if (writer) + CV_Assert(plugin_api); + CvPluginWriter writer = NULL; + if (plugin_api->Writer_open) { - CV_Assert(tbl->cv_release_writer); - tbl->cv_release_writer(writer); - writer = 0; + CV_Assert(plugin_api->Writer_release); + if (CV_ERROR_OK == plugin_api->Writer_open(filename.empty() ? 0 : 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_->Writer_release); + if (CV_ERROR_OK != plugin_api_->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 { - CV_Assert(writer); double val = -1; - tbl->cv_get_wri_prop(writer, prop, val); + if (plugin_api_->Writer_getProperty) + if (CV_ERROR_OK != plugin_api_->Writer_getProperty(writer_, prop, &val)) + val = -1; return val; } bool setProperty(int prop, double val) CV_OVERRIDE { - CV_Assert(writer); - return tbl->cv_set_wri_prop(writer, prop, val); + if (plugin_api_->Writer_setProperty) + if (CV_ERROR_OK == plugin_api_->Writer_setProperty(writer_, prop, val)) + return true; + return false; } bool isOpened() const CV_OVERRIDE { - return writer != NULL; + return writer_ != NULL; // TODO always true } void write(cv::InputArray arr) CV_OVERRIDE { cv::Mat img = arr.getMat(); - CV_Assert(writer); - tbl->cv_write(writer, img.data, (int)img.step[0], img.cols, img.rows, img.channels()); + CV_DbgAssert(writer_); + CV_Assert(plugin_api_->Writer_write); + if (CV_ERROR_OK != plugin_api_->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 tbl->cv_domain(); + return plugin_api_->captureAPI; } }; + + +Ptr PluginBackend::createCapture(int camera) const +{ + try + { + if (plugin_api_) + return PluginCapture::create(plugin_api_, std::string(), camera); //.staticCast(); + } + catch (...) + { + CV_LOG_DEBUG(NULL, "Video I/O: can't create camera capture: " << camera); + } + return Ptr(); +} + +Ptr PluginBackend::createCapture(const std::string &filename) const +{ + try + { + if (plugin_api_) + return PluginCapture::create(plugin_api_, filename, 0); //.staticCast(); + } + catch (...) + { + CV_LOG_DEBUG(NULL, "Video I/O: can't open file capture: " << filename); + } + return Ptr(); +} + +Ptr PluginBackend::createWriter(const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const +{ + try + { + if (plugin_api_) + return PluginWriter::create(plugin_api_, filename, fourcc, fps, sz, isColor); //.staticCast(); + } + catch (...) + { + CV_LOG_DEBUG(NULL, "Video I/O: can't open writer: " << filename); + } + return Ptr(); +} + +} // namespace + +Ptr createPluginBackendFactory(VideoCaptureAPIs id, const char* baseName) +{ + return makePtr(id, baseName); //.staticCast(); +} + +} // namespace diff --git a/modules/videoio/src/backend_static.cpp b/modules/videoio/src/backend_static.cpp new file mode 100644 index 0000000000..45ece063e8 --- /dev/null +++ b/modules/videoio/src/backend_static.cpp @@ -0,0 +1,72 @@ +// 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. + +#include "precomp.hpp" + +#include "backend.hpp" + +namespace cv { + +class StaticBackend: public IBackend +{ +public: + FN_createCaptureFile fn_createCaptureFile_; + FN_createCaptureCamera fn_createCaptureCamera_; + FN_createWriter fn_createWriter_; + + StaticBackend(FN_createCaptureFile fn_createCaptureFile, FN_createCaptureCamera fn_createCaptureCamera, FN_createWriter fn_createWriter) + : fn_createCaptureFile_(fn_createCaptureFile), fn_createCaptureCamera_(fn_createCaptureCamera), fn_createWriter_(fn_createWriter) + { + // nothing + } + ~StaticBackend() CV_OVERRIDE {} + + Ptr createCapture(int camera) const CV_OVERRIDE + { + if (fn_createCaptureCamera_) + return fn_createCaptureCamera_(camera); + return Ptr(); + } + Ptr createCapture(const std::string &filename) const CV_OVERRIDE + { + if (fn_createCaptureFile_) + return fn_createCaptureFile_(filename); + return Ptr(); + } + Ptr createWriter(const std::string &filename, int fourcc, double fps, const cv::Size &sz, bool isColor) const CV_OVERRIDE + { + if (fn_createWriter_) + return fn_createWriter_(filename, fourcc, fps, sz, isColor); + return Ptr(); + } +}; // StaticBackend + +class StaticBackendFactory : public IBackendFactory +{ +protected: + Ptr backend; + +public: + StaticBackendFactory(FN_createCaptureFile createCaptureFile, FN_createCaptureCamera createCaptureCamera, FN_createWriter createWriter) + : backend(makePtr(createCaptureFile, createCaptureCamera, createWriter)) + { + // nothing + } + + ~StaticBackendFactory() CV_OVERRIDE {} + + Ptr getBackend() const CV_OVERRIDE + { + return backend.staticCast(); + } +}; + +Ptr createBackendFactory(FN_createCaptureFile createCaptureFile, + FN_createCaptureCamera createCaptureCamera, + FN_createWriter createWriter) +{ + return makePtr(createCaptureFile, createCaptureCamera, createWriter).staticCast(); +} + +} // namespace diff --git a/modules/videoio/src/cap_ffmpeg.cpp b/modules/videoio/src/cap_ffmpeg.cpp index 136cc214d1..16e6637375 100644 --- a/modules/videoio/src/cap_ffmpeg.cpp +++ b/modules/videoio/src/cap_ffmpeg.cpp @@ -351,23 +351,25 @@ cv::Ptr cvCreateVideoWriter_FFMPEG_proxy(const std::string& fi #include "plugin_api.hpp" -CV_EXTERN_C int cv_domain() -{ - return cv::CAP_FFMPEG; -} +namespace cv { -CV_EXTERN_C bool cv_open_capture(const char * filename, int, void * &handle) +static +CvResult CV_API_CALL cv_capture_open(const char* filename, int camera_index, CV_OUT CvPluginCapture* handle) { + if (!handle) + return CV_ERROR_FAIL; + *handle = NULL; if (!filename) - return false; - cv::CvCapture_FFMPEG_proxy *cap = 0; + return CV_ERROR_FAIL; + CV_UNUSED(camera_index); + CvCapture_FFMPEG_proxy *cap = 0; try { - cap = new cv::CvCapture_FFMPEG_proxy(filename); + cap = new CvCapture_FFMPEG_proxy(filename); if (cap->isOpened()) { - handle = cap; - return true; + *handle = (CvPluginCapture)cap; + return CV_ERROR_OK; } } catch (...) @@ -375,94 +377,104 @@ CV_EXTERN_C bool cv_open_capture(const char * filename, int, void * &handle) } if (cap) delete cap; - return false; + return CV_ERROR_FAIL; } -CV_EXTERN_C bool cv_get_cap_prop(void * handle, int prop, double & val) +static +CvResult CV_API_CALL cv_capture_release(CvPluginCapture handle) { if (!handle) - return false; + return CV_ERROR_FAIL; + CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle; + delete instance; + return CV_ERROR_OK; +} + + +static +CvResult CV_API_CALL cv_capture_get_prop(CvPluginCapture handle, int prop, CV_OUT double* val) +{ + if (!handle) + return CV_ERROR_FAIL; + if (!val) + return CV_ERROR_FAIL; try { - cv::CvCapture_FFMPEG_proxy *instance = static_cast(handle); - val = instance->getProperty(prop); - return true; + CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle; + *val = instance->getProperty(prop); + return CV_ERROR_OK; } catch (...) { - return false; + return CV_ERROR_FAIL; } } -CV_EXTERN_C bool cv_set_cap_prop(void * handle, int prop, double val) +static +CvResult CV_API_CALL cv_capture_set_prop(CvPluginCapture handle, int prop, double val) { if (!handle) - return false; + return CV_ERROR_FAIL; try { - cv::CvCapture_FFMPEG_proxy *instance = static_cast(handle); - return instance->setProperty(prop, val); + CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle; + return instance->setProperty(prop, val) ? CV_ERROR_OK : CV_ERROR_FAIL; } catch(...) { - return false; + return CV_ERROR_FAIL; } } -CV_EXTERN_C bool cv_grab(void * handle) +static +CvResult CV_API_CALL cv_capture_grab(CvPluginCapture handle) { if (!handle) - return false; + return CV_ERROR_FAIL; try { - cv::CvCapture_FFMPEG_proxy *instance = static_cast(handle); - return instance->grabFrame(); + CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle; + return instance->grabFrame() ? CV_ERROR_OK : CV_ERROR_FAIL; } catch(...) { - return false; + return CV_ERROR_FAIL; } } -CV_EXTERN_C bool cv_retrieve(void * handle, int idx, cv_retrieve_cb_t * callback, void * userdata) +static +CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, cv_videoio_retrieve_cb_t callback, void* userdata) { if (!handle) - return false; + return CV_ERROR_FAIL; try { - cv::CvCapture_FFMPEG_proxy *instance = static_cast(handle); - cv::Mat img; + CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle; + Mat img; // TODO: avoid unnecessary copying - if (instance->retrieveFrame(idx, img)) - return callback(img.data, img.step, img.cols, img.rows, img.channels(), userdata); - return false; + if (instance->retrieveFrame(stream_idx, img)) + return callback(stream_idx, img.data, img.step, img.cols, img.rows, img.channels(), userdata); + return CV_ERROR_FAIL; } catch(...) { - return false; + return CV_ERROR_FAIL; } } -CV_EXTERN_C bool cv_release_capture(void * handle) +static +CvResult CV_API_CALL cv_writer_open(const char* filename, int fourcc, double fps, int width, int height, int isColor, + CV_OUT CvPluginWriter* handle) { - if (!handle) - return false; - cv::CvCapture_FFMPEG_proxy *instance = static_cast(handle); - delete instance; - return true; -} - -CV_EXTERN_C bool cv_open_writer(const char * filename, int fourcc, double fps, int width, int height, int isColor, void * &handle) -{ - cv::Size sz(width, height); - cv::CvVideoWriter_FFMPEG_proxy* wrt = 0; + Size sz(width, height); + CvVideoWriter_FFMPEG_proxy* wrt = 0; try { - wrt = new cv::CvVideoWriter_FFMPEG_proxy(filename, fourcc, fps, sz, isColor != 0); + wrt = new CvVideoWriter_FFMPEG_proxy(filename, fourcc, fps, sz, isColor != 0); if(wrt && wrt->isOpened()) { - handle = wrt; - return true; + *handle = (CvPluginWriter)wrt; + return CV_ERROR_OK; } } catch(...) @@ -470,43 +482,79 @@ CV_EXTERN_C bool cv_open_writer(const char * filename, int fourcc, double fps, i } if (wrt) delete wrt; - return false; + return CV_ERROR_FAIL; } -CV_EXTERN_C bool cv_get_wri_prop(void*, int, double&) -{ - return false; -} - -CV_EXTERN_C bool cv_set_wri_prop(void*, int, double) -{ - return false; -} - -CV_EXTERN_C bool cv_write(void * handle, const unsigned char * data, int step, int width, int height, int cn) +static +CvResult CV_API_CALL cv_writer_release(CvPluginWriter handle) { if (!handle) - return false; + return CV_ERROR_FAIL; + CvVideoWriter_FFMPEG_proxy* instance = (CvVideoWriter_FFMPEG_proxy*)handle; + delete instance; + return CV_ERROR_OK; +} + +static +CvResult CV_API_CALL cv_writer_get_prop(CvPluginWriter /*handle*/, int /*prop*/, CV_OUT double* /*val*/) +{ + return CV_ERROR_FAIL; +} + +static +CvResult CV_API_CALL cv_writer_set_prop(CvPluginWriter /*handle*/, int /*prop*/, double /*val*/) +{ + return CV_ERROR_FAIL; +} + +static +CvResult CV_API_CALL cv_writer_write(CvPluginWriter handle, const unsigned char *data, int step, int width, int height, int cn) +{ + if (!handle) + return CV_ERROR_FAIL; try { - cv::CvVideoWriter_FFMPEG_proxy * instance = static_cast(handle); - cv::Mat img(cv::Size(width, height), CV_MAKETYPE(CV_8U, cn), const_cast(data), step); + CvVideoWriter_FFMPEG_proxy* instance = (CvVideoWriter_FFMPEG_proxy*)handle; + Mat img(Size(width, height), CV_MAKETYPE(CV_8U, cn), const_cast(data), step); instance->write(img); - return true; + return CV_ERROR_OK; } catch(...) { - return false; + return CV_ERROR_FAIL; } } -CV_EXTERN_C bool cv_release_writer(void * handle) +static const OpenCV_VideoIO_Plugin_API_preview plugin_api_v0 = { - if (!handle) - return false; - cv::CvVideoWriter_FFMPEG_proxy * instance = static_cast(handle); - delete instance; - return true; + { + sizeof(OpenCV_VideoIO_Plugin_API_preview), ABI_VERSION, API_VERSION, + CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS, + "FFmpeg OpenCV Video I/O 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, + /* 8*/cv_writer_open, + /* 9*/cv_writer_release, + /* 10*/cv_writer_get_prop, + /* 11*/cv_writer_set_prop, + /* 12*/cv_writer_write +}; + +} // 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 != 0) + return NULL; + if (requested_api_version != 0) + return NULL; + return &cv::plugin_api_v0; } #endif // BUILD_PLUGIN diff --git a/modules/videoio/src/cap_ffmpeg_api.hpp b/modules/videoio/src/cap_ffmpeg_api.hpp index 216328e535..fe246ac68f 100644 --- a/modules/videoio/src/cap_ffmpeg_api.hpp +++ b/modules/videoio/src/cap_ffmpeg_api.hpp @@ -6,13 +6,17 @@ extern "C" { #endif -#if defined _WIN32 +#ifndef OPENCV_FFMPEG_API +#if defined(__OPENCV_BUILD) || defined(BUILD_PLUGIN) +# define OPENCV_FFMPEG_API +#elif defined _WIN32 # define OPENCV_FFMPEG_API __declspec(dllexport) #elif defined __GNUC__ && __GNUC__ >= 4 # define OPENCV_FFMPEG_API __attribute__ ((visibility ("default"))) #else # define OPENCV_FFMPEG_API #endif +#endif enum { diff --git a/modules/videoio/src/cap_gstreamer.cpp b/modules/videoio/src/cap_gstreamer.cpp index 18866530f9..6c7dd76f10 100644 --- a/modules/videoio/src/cap_gstreamer.cpp +++ b/modules/videoio/src/cap_gstreamer.cpp @@ -1629,13 +1629,16 @@ void handleMessage(GstElement * pipeline) #include "plugin_api.hpp" -CV_EXTERN_C int cv_domain() -{ - return cv::CAP_GSTREAMER; -} +namespace cv { -CV_EXTERN_C bool cv_open_capture(const char * filename, int camera_index, void * &handle) +static +CvResult CV_API_CALL cv_capture_open(const char* filename, int camera_index, CV_OUT CvPluginCapture* handle) { + if (!handle) + return CV_ERROR_FAIL; + *handle = NULL; + if (!filename) + return CV_ERROR_FAIL; GStreamerCapture *cap = 0; try { @@ -1647,8 +1650,8 @@ CV_EXTERN_C bool cv_open_capture(const char * filename, int camera_index, void * res = cap->open(camera_index); if (res) { - handle = cap; - return true; + *handle = (CvPluginCapture)cap; + return CV_ERROR_OK; } } catch (...) @@ -1656,84 +1659,94 @@ CV_EXTERN_C bool cv_open_capture(const char * filename, int camera_index, void * } if (cap) delete cap; - return false; + return CV_ERROR_FAIL; } -CV_EXTERN_C bool cv_get_cap_prop(void * handle, int prop, double & val) +static +CvResult CV_API_CALL cv_capture_release(CvPluginCapture handle) { if (!handle) - return false; + return CV_ERROR_FAIL; + GStreamerCapture* instance = (GStreamerCapture*)handle; + delete instance; + return CV_ERROR_OK; +} + + +static +CvResult CV_API_CALL cv_capture_get_prop(CvPluginCapture handle, int prop, CV_OUT double* val) +{ + if (!handle) + return CV_ERROR_FAIL; + if (!val) + return CV_ERROR_FAIL; try { - GStreamerCapture * instance = static_cast(handle); - val = instance->getProperty(prop); - return true; + GStreamerCapture* instance = (GStreamerCapture*)handle; + *val = instance->getProperty(prop); + return CV_ERROR_OK; + } + catch (...) + { + return CV_ERROR_FAIL; + } +} + +static +CvResult CV_API_CALL cv_capture_set_prop(CvPluginCapture handle, int prop, double val) +{ + if (!handle) + return CV_ERROR_FAIL; + try + { + GStreamerCapture* instance = (GStreamerCapture*)handle; + return instance->setProperty(prop, val) ? CV_ERROR_OK : CV_ERROR_FAIL; } catch(...) { - return false; + return CV_ERROR_FAIL; } } -CV_EXTERN_C bool cv_set_cap_prop(void * handle, int prop, double val) +static +CvResult CV_API_CALL cv_capture_grab(CvPluginCapture handle) { if (!handle) - return false; + return CV_ERROR_FAIL; try { - GStreamerCapture * instance = static_cast(handle); - return instance->setProperty(prop, val); + GStreamerCapture* instance = (GStreamerCapture*)handle; + return instance->grabFrame() ? CV_ERROR_OK : CV_ERROR_FAIL; } catch(...) { - return false; + return CV_ERROR_FAIL; } } -CV_EXTERN_C bool cv_grab(void * handle) +static +CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, cv_videoio_retrieve_cb_t callback, void* userdata) { if (!handle) - return false; + return CV_ERROR_FAIL; try { - GStreamerCapture * instance = static_cast(handle); - return instance->grabFrame(); - } - catch(...) - { - return false; - } -} - -CV_EXTERN_C bool cv_retrieve(void * handle, int idx, cv_retrieve_cb_t * callback, void * userdata) -{ - if (!handle) - return false; - try - { - GStreamerCapture * instance = static_cast(handle); + GStreamerCapture* instance = (GStreamerCapture*)handle; Mat img; // TODO: avoid unnecessary copying - implement lower level GStreamerCapture::retrieve - if (instance->retrieveFrame(idx, img)) - return callback(img.data, img.step, img.cols, img.rows, img.channels(), userdata); - return false; + if (instance->retrieveFrame(stream_idx, img)) + return callback(stream_idx, img.data, img.step, img.cols, img.rows, img.channels(), userdata); + return CV_ERROR_FAIL; } catch(...) { - return false; + return CV_ERROR_FAIL; } } -CV_EXTERN_C bool cv_release_capture(void * handle) -{ - if (!handle) - return false; - GStreamerCapture * instance = static_cast(handle); - delete instance; - return true; -} - -CV_EXTERN_C bool cv_open_writer(const char * filename, int fourcc, double fps, int width, int height, int isColor, void * &handle) +static +CvResult CV_API_CALL cv_writer_open(const char* filename, int fourcc, double fps, int width, int height, int isColor, + CV_OUT CvPluginWriter* handle) { CvVideoWriter_GStreamer* wrt = 0; try @@ -1742,8 +1755,8 @@ CV_EXTERN_C bool cv_open_writer(const char * filename, int fourcc, double fps, i CvSize sz = { width, height }; if(wrt && wrt->open(filename, fourcc, fps, sz, isColor)) { - handle = wrt; - return true; + *handle = (CvPluginWriter)wrt; + return CV_ERROR_OK; } } catch(...) @@ -1751,45 +1764,81 @@ CV_EXTERN_C bool cv_open_writer(const char * filename, int fourcc, double fps, i } if (wrt) delete wrt; - return false; + return CV_ERROR_FAIL; } -CV_EXTERN_C bool cv_get_wri_prop(void*, int, double&) -{ - return false; -} - -CV_EXTERN_C bool cv_set_wri_prop(void*, int, double) -{ - return false; -} - -CV_EXTERN_C bool cv_write(void * handle, const unsigned char * data, int step, int width, int height, int cn) +static +CvResult CV_API_CALL cv_writer_release(CvPluginWriter handle) { if (!handle) - return false; + return CV_ERROR_FAIL; + CvVideoWriter_GStreamer* instance = (CvVideoWriter_GStreamer*)handle; + delete instance; + return CV_ERROR_OK; +} + +static +CvResult CV_API_CALL cv_writer_get_prop(CvPluginWriter /*handle*/, int /*prop*/, CV_OUT double* /*val*/) +{ + return CV_ERROR_FAIL; +} + +static +CvResult CV_API_CALL cv_writer_set_prop(CvPluginWriter /*handle*/, int /*prop*/, double /*val*/) +{ + return CV_ERROR_FAIL; +} + +static +CvResult CV_API_CALL cv_writer_write(CvPluginWriter handle, const unsigned char *data, int step, int width, int height, int cn) +{ + if (!handle) + return CV_ERROR_FAIL; try { - CvVideoWriter_GStreamer * instance = static_cast(handle); + CvVideoWriter_GStreamer* instance = (CvVideoWriter_GStreamer*)handle; CvSize sz = { width, height }; IplImage img; cvInitImageHeader(&img, sz, IPL_DEPTH_8U, cn); cvSetData(&img, const_cast(data), step); - return instance->writeFrame(&img); + return instance->writeFrame(&img) ? CV_ERROR_OK : CV_ERROR_FAIL; } catch(...) { - return false; + return CV_ERROR_FAIL; } } -CV_EXTERN_C bool cv_release_writer(void * handle) +static const OpenCV_VideoIO_Plugin_API_preview plugin_api_v0 = { - if (!handle) - return false; - CvVideoWriter_GStreamer * instance = static_cast(handle); - delete instance; - return true; + { + sizeof(OpenCV_VideoIO_Plugin_API_preview), ABI_VERSION, API_VERSION, + CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS, + "GStreamer OpenCV Video I/O plugin" + }, + /* 1*/CAP_GSTREAMER, + /* 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, + /* 8*/cv_writer_open, + /* 9*/cv_writer_release, + /* 10*/cv_writer_get_prop, + /* 11*/cv_writer_set_prop, + /* 12*/cv_writer_write +}; + +} // 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 != 0) + return NULL; + if (requested_api_version != 0) + return NULL; + return &cv::plugin_api_v0; } #endif // BUILD_PLUGIN diff --git a/modules/videoio/src/plugin_api.cpp b/modules/videoio/src/plugin_api.cpp deleted file mode 100644 index d8119b750c..0000000000 --- a/modules/videoio/src/plugin_api.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// 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. - -#ifdef BUILD_PLUGIN - -#include "plugin_api.hpp" -#include "opencv2/core/version.hpp" - -void cv_get_version(int & major, int & minor, int & patch, int & api, int & abi) -{ - major = CV_VERSION_MAJOR; - minor = CV_VERSION_MINOR; - patch = CV_VERSION_REVISION; - api = API_VERSION; - abi = ABI_VERSION; -} - -#endif // BUILD_PLUGIN diff --git a/modules/videoio/src/plugin_api.hpp b/modules/videoio/src/plugin_api.hpp index 672f7254ff..957eda053c 100644 --- a/modules/videoio/src/plugin_api.hpp +++ b/modules/videoio/src/plugin_api.hpp @@ -5,64 +5,165 @@ #ifndef PLUGIN_API_HPP #define PLUGIN_API_HPP +#include +#include + // increase for backward-compatible changes, e.g. add new function // Main API <= Plugin API -> plugin is compatible -#define API_VERSION 1 +#define API_VERSION 0 // preview // increase for incompatible changes, e.g. remove function argument // Main ABI == Plugin ABI -> plugin is compatible -#define ABI_VERSION 1 +#define ABI_VERSION 0 // preview #ifdef __cplusplus extern "C" { #endif -// common -typedef void cv_get_version_t(int & major, int & minor, int & patch, int & api, int & abi); -typedef int cv_domain_t(); +typedef CvResult (CV_API_CALL *cv_videoio_retrieve_cb_t)(int stream_idx, unsigned const char* data, int step, int width, int height, int cn, void* userdata); -// capture -typedef bool cv_open_capture_t(const char * filename, int camera_index, void * &handle); -typedef bool cv_get_cap_prop_t(void * handle, int prop, double & val); -typedef bool cv_set_cap_prop_t(void * handle, int prop, double val); -typedef bool cv_grab_t(void * handle); -// callback function type -typedef bool cv_retrieve_cb_t(unsigned char * data, int step, int width, int height, int cn, void * userdata); -typedef bool cv_retrieve_t(void * handle, int idx, cv_retrieve_cb_t * cb, void * userdata); -typedef bool cv_release_capture_t(void * handle); +typedef struct CvPluginCapture_t* CvPluginCapture; +typedef struct CvPluginWriter_t* CvPluginWriter; -// writer -typedef bool cv_open_writer_t(const char * filename, int fourcc, double fps, int width, int height, int isColor, void * &handle); -typedef bool cv_get_wri_prop_t(void * handle, int prop, double & val); -typedef bool cv_set_wri_prop_t(void * handle, int prop, double val); -typedef bool cv_write_t(void * handle, const unsigned char * data, int step, int width, int height, int cn); -typedef bool cv_release_writer_t(void * handle); +typedef struct OpenCV_VideoIO_Plugin_API_preview +{ + OpenCV_API_Header api_header; + + /** OpenCV capture ID (VideoCaptureAPIs) + @note API-ENTRY 1, API-Version == 0 + */ + int captureAPI; + + /** @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_retrieve_cb_t callback, void* userdata); + + + /** @brief Try to open video writer + + @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 Writer handle + + @note API-CALL 8, 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 9, API-Version == 0 + */ + CvResult (CV_API_CALL *Writer_release)(CvPluginWriter handle); + + /** @brief Get property value + + @param handle Capture handle + @param prop Property index + @param[out] val property value + + @note API-CALL 10, API-Version == 0 + */ + CvResult (CV_API_CALL *Writer_getProperty)(CvPluginWriter 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 11, API-Version == 0 + */ + CvResult (CV_API_CALL *Writer_setProperty)(CvPluginWriter handle, int prop, double val); + + /** @brief Write frame + + @param handle Capture handle + @param data Capture handle + @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 12, 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_Plugin_API_preview; #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 - -CV_PLUGIN_EXPORTS cv_get_version_t cv_get_version; -CV_PLUGIN_EXPORTS cv_domain_t cv_domain; - -CV_PLUGIN_EXPORTS cv_open_capture_t cv_open_capture; -CV_PLUGIN_EXPORTS cv_get_cap_prop_t cv_get_cap_prop; -CV_PLUGIN_EXPORTS cv_set_cap_prop_t cv_set_cap_prop; -CV_PLUGIN_EXPORTS cv_grab_t cv_grab; -CV_PLUGIN_EXPORTS cv_retrieve_t cv_retrieve; -CV_PLUGIN_EXPORTS cv_release_capture_t cv_release_capture; - -CV_PLUGIN_EXPORTS cv_open_writer_t cv_open_writer; -CV_PLUGIN_EXPORTS cv_get_wri_prop_t cv_get_wri_prop; -CV_PLUGIN_EXPORTS cv_set_wri_prop_t cv_set_wri_prop; -CV_PLUGIN_EXPORTS cv_write_t cv_write; -CV_PLUGIN_EXPORTS cv_release_writer_t cv_release_writer; - #endif +CV_PLUGIN_EXPORTS +const OpenCV_VideoIO_Plugin_API_preview* CV_API_CALL opencv_videoio_plugin_init_v0 + (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/) CV_NOEXCEPT; + +#else // BUILD_PLUGIN +typedef const OpenCV_VideoIO_Plugin_API_preview* (CV_API_CALL *FN_opencv_videoio_plugin_init_t) + (int requested_abi_version, int requested_api_version, void* reserved /*NULL*/); +#endif // BUILD_PLUGIN + #ifdef __cplusplus } From 25a9a32f8e381f442259b5d95c8c6f29ad806034 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 5 Mar 2019 13:22:52 +0300 Subject: [PATCH 3/3] videoio: plugins, add strict checks --- modules/videoio/src/backend_plugin.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/modules/videoio/src/backend_plugin.cpp b/modules/videoio/src/backend_plugin.cpp index ab6960f398..e1d8f3835b 100644 --- a/modules/videoio/src/backend_plugin.cpp +++ b/modules/videoio/src/backend_plugin.cpp @@ -167,6 +167,14 @@ public: CV_LOG_INFO(NULL, "Video I/O: plugin is incompatible: " << lib->getName()); return; } + if (plugin_api_->api_header.opencv_version_major != CV_VERSION_MAJOR || + plugin_api_->api_header.opencv_version_minor != CV_VERSION_MINOR) + { + CV_LOG_ERROR(NULL, "Video I/O: wrong OpenCV 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; + } // TODO Preview: add compatibility API/ABI checks CV_LOG_INFO(NULL, "Video I/O: loaded plugin '" << plugin_api_->api_header.api_description << "'"); } @@ -264,8 +272,17 @@ void PluginBackendFactory::loadPlugin() Ptr pluginBackend = makePtr(lib); if (pluginBackend && pluginBackend->plugin_api_) { - backend = pluginBackend; - return; + if (pluginBackend->plugin_api_->captureAPI != id_) + { + CV_LOG_ERROR(NULL, "Video I/O: plugin '" << pluginBackend->plugin_api_->api_header.api_description << + "': unexpected backend ID: " << + pluginBackend->plugin_api_->captureAPI << " vs " << (int)id_ << " (expected)"); + } + else + { + backend = pluginBackend; + return; + } } } catch (...)