mirror of
https://github.com/opencv/opencv.git
synced 2026-07-25 13:23:02 +04:00
e9982e856f
Open VideoCapture from data stream #25584 ### Pull Request Readiness Checklist Add VideoCapture option to read a raw binary video data from `std::streambuf`. There are multiple motivations: 1. Avoid disk file creation in case of video already in memory (received by network or from database). 2. Streaming mode. Frames decoding starts during sequential file transfer by chunks. Suppoted backends: * FFmpeg * MSMF (no streaming mode) Supporter interfaces: * C++ (std::streambuf) * Python (io.BufferedIOBase) resolves https://github.com/opencv/opencv/issues/24400 - [x] test h264 - [x] test IP camera like approach with no metadata but key frame only? - [x] C API plugin See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
837 lines
31 KiB
C++
837 lines
31 KiB
C++
// 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"
|
|
#include "plugin_api.hpp"
|
|
#include "plugin_capture_api.hpp"
|
|
#include "plugin_writer_api.hpp"
|
|
|
|
#include "opencv2/core/utils/configuration.private.hpp"
|
|
#include "opencv2/core/utils/logger.hpp"
|
|
|
|
#include "opencv2/core/private.hpp"
|
|
#include "videoio_registry.hpp"
|
|
|
|
//==================================================================================================
|
|
// Dynamic backend implementation
|
|
|
|
#include "opencv2/core/utils/plugin_loader.private.hpp"
|
|
|
|
|
|
#include "backend_plugin_legacy.impl.hpp"
|
|
|
|
|
|
namespace cv { namespace impl {
|
|
|
|
#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS)
|
|
|
|
using namespace cv::plugin::impl; // plugin_loader.hpp
|
|
|
|
static Mutex& getInitializationMutex()
|
|
{
|
|
static Mutex initializationMutex;
|
|
return initializationMutex;
|
|
}
|
|
|
|
|
|
class PluginBackend: public IBackend
|
|
{
|
|
protected:
|
|
|
|
void initCaptureAPI()
|
|
{
|
|
const char* init_name = "opencv_videoio_capture_plugin_init_v1";
|
|
FN_opencv_videoio_capture_plugin_init_t fn_init = reinterpret_cast<FN_opencv_videoio_capture_plugin_init_t>(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<FN_opencv_videoio_writer_plugin_init_t>(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<FN_opencv_videoio_plugin_init_t>(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);
|
|
if (plugin_api_)
|
|
break;
|
|
}
|
|
if (!plugin_api_)
|
|
{
|
|
CV_LOG_INFO(NULL, "Video I/O: plugin is incompatible (can't be initialized): " << lib_->getName());
|
|
return;
|
|
}
|
|
if (!checkCompatibility(
|
|
plugin_api_->api_header, ABI_VERSION, API_VERSION,
|
|
plugin_api_->v0.captureAPI != CAP_FFMPEG))
|
|
{
|
|
plugin_api_ = NULL;
|
|
return;
|
|
}
|
|
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());
|
|
}
|
|
}
|
|
|
|
|
|
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<cv::plugin::impl::DynamicLib> 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<cv::plugin::impl::DynamicLib>& lib)
|
|
: lib_(lib)
|
|
, capture_api_(NULL), writer_api_(NULL)
|
|
, plugin_api_(NULL)
|
|
{
|
|
initCaptureAPI();
|
|
initWriterAPI();
|
|
if (capture_api_ == NULL && writer_api_ == NULL)
|
|
{
|
|
initPluginLegacyAPI();
|
|
}
|
|
}
|
|
|
|
Ptr<IVideoCapture> createCapture(int camera) const;
|
|
Ptr<IVideoCapture> createCapture(int camera, const VideoCaptureParameters& params) const CV_OVERRIDE;
|
|
Ptr<IVideoCapture> createCapture(const std::string &filename) const;
|
|
Ptr<IVideoCapture> createCapture(const std::string &filename, const VideoCaptureParameters& params) const CV_OVERRIDE;
|
|
Ptr<IVideoCapture> createCapture(const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params) const CV_OVERRIDE;
|
|
Ptr<IVideoWriter> createWriter(const std::string& filename, int fourcc, double fps,
|
|
const cv::Size& sz, const VideoWriterParameters& params) const CV_OVERRIDE;
|
|
|
|
std::string getCapturePluginVersion(CV_OUT int& version_ABI, CV_OUT int& version_API)
|
|
{
|
|
CV_Assert(capture_api_ || plugin_api_);
|
|
const OpenCV_API_Header& api_header = capture_api_ ? capture_api_->api_header : plugin_api_->api_header;
|
|
version_ABI = api_header.min_api_version;
|
|
version_API = api_header.api_version;
|
|
return api_header.api_description;
|
|
}
|
|
|
|
std::string getWriterPluginVersion(CV_OUT int& version_ABI, CV_OUT int& version_API)
|
|
{
|
|
CV_Assert(writer_api_ || plugin_api_);
|
|
const OpenCV_API_Header& api_header = writer_api_ ? writer_api_->api_header : plugin_api_->api_header;
|
|
version_ABI = api_header.min_api_version;
|
|
version_API = api_header.api_version;
|
|
return api_header.api_description;
|
|
}
|
|
};
|
|
|
|
class PluginBackendFactory : public IBackendFactory
|
|
{
|
|
public:
|
|
VideoCaptureAPIs id_;
|
|
const char* baseName_;
|
|
Ptr<PluginBackend> backend;
|
|
bool initialized;
|
|
public:
|
|
PluginBackendFactory(VideoCaptureAPIs id, const char* baseName) :
|
|
id_(id), baseName_(baseName),
|
|
initialized(false)
|
|
{
|
|
// nothing, plugins are loaded on demand
|
|
}
|
|
|
|
Ptr<IBackend> getBackend() const CV_OVERRIDE
|
|
{
|
|
initBackend();
|
|
return backend.staticCast<IBackend>();
|
|
}
|
|
|
|
bool isBuiltIn() const CV_OVERRIDE { return false; }
|
|
|
|
std::string getCapturePluginVersion(
|
|
CV_OUT int& version_ABI,
|
|
CV_OUT int& version_API) const
|
|
{
|
|
initBackend();
|
|
if (!backend)
|
|
CV_Error_(Error::StsNotImplemented, ("Backend '%s' is not available", baseName_));
|
|
return backend->getCapturePluginVersion(version_ABI, version_API);
|
|
}
|
|
|
|
std::string getWriterPluginVersion(
|
|
CV_OUT int& version_ABI,
|
|
CV_OUT int& version_API) const
|
|
{
|
|
initBackend();
|
|
if (!backend)
|
|
CV_Error_(Error::StsNotImplemented, ("Backend '%s' is not available", baseName_));
|
|
return backend->getWriterPluginVersion(version_ABI, version_API);
|
|
}
|
|
|
|
protected:
|
|
inline void initBackend() const
|
|
{
|
|
if (!initialized)
|
|
{
|
|
const_cast<PluginBackendFactory*>(this)->initBackend_();
|
|
}
|
|
}
|
|
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();
|
|
};
|
|
|
|
static
|
|
std::vector<FileSystemPath_t> getPluginCandidates(const std::string& baseName)
|
|
{
|
|
using namespace cv::utils;
|
|
using namespace cv::utils::fs;
|
|
const std::string baseName_l = toLowerCase(baseName);
|
|
const std::string baseName_u = toUpperCase(baseName);
|
|
const FileSystemPath_t baseName_l_fs = toFileSystemPath(baseName_l);
|
|
std::vector<FileSystemPath_t> paths;
|
|
const std::vector<std::string> paths_ = getConfigurationParameterPaths("OPENCV_VIDEOIO_PLUGIN_PATH", std::vector<std::string>());
|
|
if (paths_.size() != 0)
|
|
{
|
|
for (size_t i = 0; i < paths_.size(); i++)
|
|
{
|
|
paths.push_back(toFileSystemPath(paths_[i]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FileSystemPath_t binaryLocation;
|
|
if (getBinLocation(binaryLocation))
|
|
{
|
|
binaryLocation = getParent(binaryLocation);
|
|
#ifndef CV_VIDEOIO_PLUGIN_SUBDIRECTORY
|
|
paths.push_back(binaryLocation);
|
|
#else
|
|
paths.push_back(binaryLocation + toFileSystemPath("/") + toFileSystemPath(CV_VIDEOIO_PLUGIN_SUBDIRECTORY_STR));
|
|
#endif
|
|
}
|
|
}
|
|
const std::string default_expr = libraryPrefix() + "opencv_videoio_" + baseName_l + "*" + librarySuffix();
|
|
const std::string plugin_expr = getConfigurationParameterString((std::string("OPENCV_VIDEOIO_PLUGIN_") + baseName_u).c_str(), default_expr.c_str());
|
|
std::vector<FileSystemPath_t> results;
|
|
#ifdef _WIN32
|
|
FileSystemPath_t moduleName = toFileSystemPath(libraryPrefix() + "opencv_videoio_" + baseName_l + librarySuffix());
|
|
if (baseName_u == "FFMPEG") // backward compatibility
|
|
{
|
|
const std::string ffmpeg_env_path = cv::utils::getConfigurationParameterString("OPENCV_FFMPEG_DLL_DIR");
|
|
if (!ffmpeg_env_path.empty())
|
|
{
|
|
results.push_back(toFileSystemPath(ffmpeg_env_path + "\\" + toPrintablePath(moduleName)));
|
|
}
|
|
}
|
|
if (plugin_expr != default_expr)
|
|
{
|
|
moduleName = toFileSystemPath(plugin_expr);
|
|
results.push_back(moduleName);
|
|
}
|
|
for (const FileSystemPath_t& path : paths)
|
|
{
|
|
results.push_back(path + L"\\" + moduleName);
|
|
}
|
|
results.push_back(moduleName);
|
|
#if defined(_DEBUG) && defined(DEBUG_POSTFIX)
|
|
if (baseName_u == "FFMPEG") // backward compatibility
|
|
{
|
|
const FileSystemPath_t templ = toFileSystemPath(CVAUX_STR(DEBUG_POSTFIX) ".dll");
|
|
FileSystemPath_t nonDebugName(moduleName);
|
|
size_t suf = nonDebugName.rfind(templ);
|
|
if (suf != FileSystemPath_t::npos)
|
|
{
|
|
nonDebugName.replace(suf, suf + templ.size(), L".dll");
|
|
results.push_back(nonDebugName);
|
|
}
|
|
}
|
|
#endif // _DEBUG && DEBUG_POSTFIX
|
|
#else
|
|
CV_LOG_INFO(NULL, "VideoIO plugin (" << baseName << "): glob is '" << plugin_expr << "', " << paths.size() << " location(s)");
|
|
for (const std::string& path : paths)
|
|
{
|
|
if (path.empty())
|
|
continue;
|
|
std::vector<std::string> candidates;
|
|
cv::glob(utils::fs::join(path, plugin_expr), candidates);
|
|
// Prefer candisates with higher versions
|
|
// TODO: implemented accurate versions-based comparator
|
|
std::sort(candidates.begin(), candidates.end(), std::greater<std::string>());
|
|
CV_LOG_INFO(NULL, " - " << path << ": " << candidates.size());
|
|
copy(candidates.begin(), candidates.end(), back_inserter(results));
|
|
}
|
|
#endif
|
|
CV_LOG_INFO(NULL, "Found " << results.size() << " plugin(s) for " << baseName);
|
|
return results;
|
|
}
|
|
|
|
void PluginBackendFactory::loadPlugin()
|
|
{
|
|
for (const FileSystemPath_t& plugin : getPluginCandidates(baseName_))
|
|
{
|
|
auto lib = makePtr<cv::plugin::impl::DynamicLib>(plugin);
|
|
if (!lib->isLoaded())
|
|
continue;
|
|
try
|
|
{
|
|
Ptr<PluginBackend> pluginBackend = makePtr<PluginBackend>(lib);
|
|
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)");
|
|
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 (...)
|
|
{
|
|
CV_LOG_WARNING(NULL, "Video I/O: exception during plugin initialization: " << toPrintablePath(plugin) << ". SKIP");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//==================================================================================================
|
|
|
|
class PluginCapture : public cv::IVideoCapture
|
|
{
|
|
const OpenCV_VideoIO_Capture_Plugin_API* plugin_api_;
|
|
CvPluginCapture capture_;
|
|
Ptr<IStreamReader> readStream_;
|
|
|
|
public:
|
|
static
|
|
Ptr<PluginCapture> create(const OpenCV_VideoIO_Capture_Plugin_API* plugin_api,
|
|
const std::string &filename, const Ptr<IStreamReader>& stream, int camera, const VideoCaptureParameters& params)
|
|
{
|
|
CV_Assert(plugin_api);
|
|
CV_Assert(plugin_api->v0.Capture_release);
|
|
|
|
CvPluginCapture capture = NULL;
|
|
if (stream && plugin_api->api_header.api_version >= 2 && plugin_api->v2.Capture_open_stream)
|
|
{
|
|
std::vector<int> vint_params = params.getIntVector();
|
|
int* c_params = vint_params.data();
|
|
unsigned n_params = (unsigned)(vint_params.size() / 2);
|
|
|
|
if (CV_ERROR_OK == plugin_api->v2.Capture_open_stream(
|
|
stream.get(),
|
|
[](void* opaque, char* buffer, long long size) -> long long {
|
|
CV_LOG_VERBOSE(NULL, 0, "IStreamReader::read(" << size << ")...");
|
|
auto is = reinterpret_cast<IStreamReader*>(opaque);
|
|
try {
|
|
return is->read(buffer, size);
|
|
} catch (...) {
|
|
CV_LOG_WARNING(NULL, "IStreamReader::read(" << size << ") failed");
|
|
return 0;
|
|
}
|
|
},
|
|
[](void* opaque, long long offset, int way) -> long long {
|
|
CV_LOG_VERBOSE(NULL, 0, "IStreamReader::seek(" << offset << ", way=" << way << ")...");
|
|
auto is = reinterpret_cast<IStreamReader*>(opaque);
|
|
try {
|
|
return is->seek(offset, way);
|
|
} catch (...) {
|
|
CV_LOG_WARNING(NULL, "IStreamReader::seek(" << offset << ", way=" << way << ") failed");
|
|
return -1;
|
|
}
|
|
}, c_params, n_params, &capture))
|
|
{
|
|
CV_Assert(capture);
|
|
return makePtr<PluginCapture>(plugin_api, capture, stream);
|
|
}
|
|
}
|
|
else if (stream)
|
|
return Ptr<PluginCapture>();
|
|
|
|
if (plugin_api->api_header.api_version >= 1 && plugin_api->v1.Capture_open_with_params)
|
|
{
|
|
std::vector<int> vint_params = params.getIntVector();
|
|
int* c_params = vint_params.data();
|
|
unsigned n_params = (unsigned)(vint_params.size() / 2);
|
|
|
|
if (CV_ERROR_OK == plugin_api->v1.Capture_open_with_params(
|
|
filename.empty() ? 0 : filename.c_str(), camera, c_params, n_params, &capture))
|
|
{
|
|
CV_Assert(capture);
|
|
return makePtr<PluginCapture>(plugin_api, capture);
|
|
}
|
|
}
|
|
else if (plugin_api->v0.Capture_open)
|
|
{
|
|
if (CV_ERROR_OK == plugin_api->v0.Capture_open(filename.empty() ? 0 : filename.c_str(), camera, &capture))
|
|
{
|
|
CV_Assert(capture);
|
|
Ptr<PluginCapture> cap = makePtr<PluginCapture>(plugin_api, capture);
|
|
if (cap && !params.empty())
|
|
{
|
|
applyParametersFallback(cap, params);
|
|
}
|
|
return cap;
|
|
}
|
|
}
|
|
|
|
return Ptr<PluginCapture>();
|
|
}
|
|
|
|
PluginCapture(const OpenCV_VideoIO_Capture_Plugin_API* plugin_api, CvPluginCapture capture, const Ptr<IStreamReader>& readStream = Ptr<IStreamReader>())
|
|
: plugin_api_(plugin_api), capture_(capture), readStream_(readStream)
|
|
{
|
|
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 type, void* userdata)
|
|
{
|
|
CV_UNUSED(stream_idx);
|
|
cv::_OutputArray* dst = static_cast<cv::_OutputArray*>(userdata);
|
|
if (!dst)
|
|
return CV_ERROR_FAIL;
|
|
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
|
|
{
|
|
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.id;
|
|
}
|
|
};
|
|
|
|
|
|
//==================================================================================================
|
|
|
|
class PluginWriter : public cv::IVideoWriter
|
|
{
|
|
const OpenCV_VideoIO_Writer_Plugin_API* plugin_api_;
|
|
CvPluginWriter writer_;
|
|
|
|
public:
|
|
static
|
|
Ptr<PluginWriter> create(const OpenCV_VideoIO_Writer_Plugin_API* plugin_api,
|
|
const std::string& filename, int fourcc, double fps, const cv::Size& sz,
|
|
const VideoWriterParameters& params)
|
|
{
|
|
CV_Assert(plugin_api);
|
|
CV_Assert(plugin_api->v0.Writer_release);
|
|
CV_Assert(!filename.empty());
|
|
|
|
CvPluginWriter writer = NULL;
|
|
|
|
if (plugin_api->api_header.api_version >= 1 && plugin_api->v1.Writer_open_with_params)
|
|
{
|
|
std::vector<int> 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<PluginWriter>(plugin_api, writer);
|
|
}
|
|
}
|
|
else if (plugin_api->v0.Writer_open)
|
|
{
|
|
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<PluginWriter>();
|
|
}
|
|
if (params.warnUnusedParameters())
|
|
{
|
|
CV_LOG_ERROR(NULL, "VIDEOIO: unsupported parameters in VideoWriter, see logger INFO channel for details");
|
|
return Ptr<PluginWriter>();
|
|
}
|
|
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<PluginWriter>(plugin_api, writer);
|
|
}
|
|
}
|
|
|
|
return Ptr<PluginWriter>();
|
|
}
|
|
|
|
PluginWriter(const OpenCV_VideoIO_Writer_Plugin_API* 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.id;
|
|
}
|
|
};
|
|
|
|
|
|
Ptr<IVideoCapture> PluginBackend::createCapture(int camera, const VideoCaptureParameters& params) const
|
|
{
|
|
try
|
|
{
|
|
if (capture_api_)
|
|
return PluginCapture::create(capture_api_, std::string(), nullptr, camera, params); //.staticCast<IVideoCapture>();
|
|
if (plugin_api_)
|
|
{
|
|
Ptr<IVideoCapture> cap = legacy::PluginCapture::create(plugin_api_, std::string(), camera); //.staticCast<IVideoCapture>();
|
|
if (cap && !params.empty())
|
|
{
|
|
applyParametersFallback(cap, params);
|
|
}
|
|
return cap;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
CV_LOG_DEBUG(NULL, "Video I/O: can't create camera capture: " << camera);
|
|
throw;
|
|
}
|
|
return Ptr<IVideoCapture>();
|
|
}
|
|
|
|
Ptr<IVideoCapture> PluginBackend::createCapture(const std::string &filename, const VideoCaptureParameters& params) const
|
|
{
|
|
try
|
|
{
|
|
if (capture_api_)
|
|
return PluginCapture::create(capture_api_, filename, nullptr, 0, params); //.staticCast<IVideoCapture>();
|
|
if (plugin_api_)
|
|
{
|
|
Ptr<IVideoCapture> cap = legacy::PluginCapture::create(plugin_api_, filename, 0); //.staticCast<IVideoCapture>();
|
|
if (cap && !params.empty())
|
|
{
|
|
applyParametersFallback(cap, params);
|
|
}
|
|
return cap;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
CV_LOG_DEBUG(NULL, "Video I/O: can't open file capture: " << filename);
|
|
throw;
|
|
}
|
|
return Ptr<IVideoCapture>();
|
|
}
|
|
|
|
Ptr<IVideoCapture> PluginBackend::createCapture(const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params) const
|
|
{
|
|
try
|
|
{
|
|
if (capture_api_)
|
|
return PluginCapture::create(capture_api_, std::string(), stream, 0, params); //.staticCast<IVideoCapture>();
|
|
if (plugin_api_)
|
|
{
|
|
CV_Error(Error::StsNotImplemented, "Legacy plugin API for stream capture");
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
CV_LOG_DEBUG(NULL, "Video I/O: can't open stream capture");
|
|
throw;
|
|
}
|
|
return Ptr<IVideoCapture>();
|
|
}
|
|
|
|
Ptr<IVideoWriter> PluginBackend::createWriter(const std::string& filename, int fourcc, double fps,
|
|
const cv::Size& sz, const VideoWriterParameters& params) const
|
|
{
|
|
try
|
|
{
|
|
if (writer_api_)
|
|
return PluginWriter::create(writer_api_, filename, fourcc, fps, sz, params); //.staticCast<IVideoWriter>();
|
|
if (plugin_api_)
|
|
return legacy::PluginWriter::create(plugin_api_, filename, fourcc, fps, sz, params); //.staticCast<IVideoWriter>();
|
|
}
|
|
catch (...)
|
|
{
|
|
CV_LOG_DEBUG(NULL, "Video I/O: can't open writer: " << filename);
|
|
}
|
|
return Ptr<IVideoWriter>();
|
|
}
|
|
|
|
#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS)
|
|
|
|
} // namespace
|
|
|
|
Ptr<IBackendFactory> createPluginBackendFactory(VideoCaptureAPIs id, const char* baseName)
|
|
{
|
|
#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS)
|
|
return makePtr<impl::PluginBackendFactory>(id, baseName); //.staticCast<IBackendFactory>();
|
|
#else
|
|
CV_UNUSED(id);
|
|
CV_UNUSED(baseName);
|
|
return Ptr<IBackendFactory>();
|
|
#endif
|
|
}
|
|
|
|
|
|
std::string getCapturePluginVersion(
|
|
const Ptr<IBackendFactory>& backend_factory,
|
|
CV_OUT int& version_ABI,
|
|
CV_OUT int& version_API
|
|
)
|
|
{
|
|
#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS)
|
|
using namespace impl;
|
|
CV_Assert(backend_factory);
|
|
PluginBackendFactory* plugin_backend_factory = dynamic_cast<PluginBackendFactory*>(backend_factory.get());
|
|
CV_Assert(plugin_backend_factory);
|
|
return plugin_backend_factory->getCapturePluginVersion(version_ABI, version_API);
|
|
#else
|
|
CV_UNUSED(backend_factory);
|
|
CV_UNUSED(version_ABI);
|
|
CV_UNUSED(version_API);
|
|
CV_Error(Error::StsBadFunc, "Plugins are not available in this build");
|
|
#endif
|
|
}
|
|
|
|
std::string getWriterPluginVersion(
|
|
const Ptr<IBackendFactory>& backend_factory,
|
|
CV_OUT int& version_ABI,
|
|
CV_OUT int& version_API
|
|
)
|
|
{
|
|
#if OPENCV_HAVE_FILESYSTEM_SUPPORT && defined(ENABLE_PLUGINS)
|
|
using namespace impl;
|
|
CV_Assert(backend_factory);
|
|
PluginBackendFactory* plugin_backend_factory = dynamic_cast<PluginBackendFactory*>(backend_factory.get());
|
|
CV_Assert(plugin_backend_factory);
|
|
return plugin_backend_factory->getWriterPluginVersion(version_ABI, version_API);
|
|
#else
|
|
CV_UNUSED(backend_factory);
|
|
CV_UNUSED(version_ABI);
|
|
CV_UNUSED(version_API);
|
|
CV_Error(Error::StsBadFunc, "Plugins are not available in this build");
|
|
#endif
|
|
}
|
|
|
|
} // namespace
|