From e705414bea6b4d3f7049a4dfd84c9e3977e753fb Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 28 Jan 2021 06:00:38 +0000 Subject: [PATCH] videoio(plugins): support VideoCaptureParameters, CAPTURE_API_VERSION=1 - example: ffmpeg --- modules/videoio/src/backend_plugin.cpp | 90 +++++++++++-------- modules/videoio/src/cap.cpp | 4 +- modules/videoio/src/cap_ffmpeg.cpp | 66 ++++++++------ modules/videoio/src/cap_ffmpeg_impl.hpp | 34 +++++-- modules/videoio/src/cap_ffmpeg_legacy_api.hpp | 2 +- modules/videoio/src/cap_interface.hpp | 17 ++-- modules/videoio/src/plugin_capture_api.hpp | 25 +++--- modules/videoio/test/test_ffmpeg.cpp | 8 +- 8 files changed, 154 insertions(+), 92 deletions(-) diff --git a/modules/videoio/src/backend_plugin.cpp b/modules/videoio/src/backend_plugin.cpp index 91270d98c0..6d79b47296 100644 --- a/modules/videoio/src/backend_plugin.cpp +++ b/modules/videoio/src/backend_plugin.cpp @@ -564,20 +564,40 @@ class PluginCapture : public cv::IVideoCapture public: static Ptr create(const OpenCV_VideoIO_Capture_Plugin_API* plugin_api, - const std::string &filename, int camera) + const std::string &filename, int camera, const VideoCaptureParameters& params) { CV_Assert(plugin_api); + CV_Assert(plugin_api->v0.Capture_release); + CvPluginCapture capture = NULL; - if (plugin_api->v0.Capture_open) + if (plugin_api->api_header.api_version >= 1 && plugin_api->v1.Capture_open_with_params) { - CV_Assert(plugin_api->v0.Capture_release); - if (CV_ERROR_OK == plugin_api->v0.Capture_open(filename.empty() ? 0 : filename.c_str(), camera, &capture)) + 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.Capture_open_with_params( + filename.empty() ? 0 : filename.c_str(), camera, c_params, n_params, &capture)) { CV_Assert(capture); return makePtr(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 cap = makePtr(plugin_api, capture); + if (cap && !params.empty()) + { + applyParametersFallback(cap, params); + } + return cap; + } + } + return Ptr(); } @@ -658,11 +678,13 @@ public: 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) { - 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); @@ -675,8 +697,6 @@ public: } 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) @@ -684,12 +704,18 @@ public: 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 (params.warnUnusedParameters()) + { + CV_LOG_ERROR(NULL, "VIDEOIO/FFMPEG: unsupported parameters in VideoWriter, see logger INFO channel for details"); + 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(); } @@ -743,14 +769,21 @@ public: }; -Ptr PluginBackend::createCapture(int camera) const +Ptr PluginBackend::createCapture(int camera, const VideoCaptureParameters& params) const { try { if (capture_api_) - return PluginCapture::create(capture_api_, std::string(), camera); //.staticCast(); + return PluginCapture::create(capture_api_, std::string(), camera, params); //.staticCast(); if (plugin_api_) - return legacy::PluginCapture::create(plugin_api_, std::string(), camera); //.staticCast(); + { + Ptr cap = legacy::PluginCapture::create(plugin_api_, std::string(), camera); //.staticCast(); + if (cap && !params.empty()) + { + applyParametersFallback(cap, params); + } + return cap; + } } catch (...) { @@ -760,25 +793,21 @@ Ptr PluginBackend::createCapture(int camera) const return Ptr(); } -Ptr PluginBackend::createCapture(int camera, const VideoCaptureParameters& params) const -{ - // TODO Update plugins API to support parameters - Ptr cap = createCapture(camera); - if (cap && !params.empty()) - { - applyParametersFallback(cap, params); - } - return cap; -} - -Ptr PluginBackend::createCapture(const std::string &filename) const +Ptr PluginBackend::createCapture(const std::string &filename, const VideoCaptureParameters& params) const { try { if (capture_api_) - return PluginCapture::create(capture_api_, filename, 0); //.staticCast(); + return PluginCapture::create(capture_api_, filename, 0, params); //.staticCast(); if (plugin_api_) - return legacy::PluginCapture::create(plugin_api_, filename, 0); //.staticCast(); + { + Ptr cap = legacy::PluginCapture::create(plugin_api_, filename, 0); //.staticCast(); + if (cap && !params.empty()) + { + applyParametersFallback(cap, params); + } + return cap; + } } catch (...) { @@ -788,17 +817,6 @@ Ptr PluginBackend::createCapture(const std::string &filename) con return Ptr(); } -Ptr PluginBackend::createCapture(const std::string &filename, const VideoCaptureParameters& params) const -{ - // TODO Update plugins API to support parameters - Ptr cap = createCapture(filename); - if (cap && !params.empty()) - { - applyParametersFallback(cap, params); - } - return cap; -} - Ptr PluginBackend::createWriter(const std::string& filename, int fourcc, double fps, const cv::Size& sz, const VideoWriterParameters& params) const { diff --git a/modules/videoio/src/cap.cpp b/modules/videoio/src/cap.cpp index f0afc1f4ab..f8967514ed 100644 --- a/modules/videoio/src/cap.cpp +++ b/modules/videoio/src/cap.cpp @@ -76,7 +76,7 @@ VideoCapture::VideoCapture(const String& filename, int apiPreference) : throwOnF } VideoCapture::VideoCapture(const String& filename, int apiPreference, const std::vector& params) - : throwOnFail(true) + : throwOnFail(false) { CV_TRACE_FUNCTION(); open(filename, apiPreference, params); @@ -89,7 +89,7 @@ VideoCapture::VideoCapture(int index, int apiPreference) : throwOnFail(false) } VideoCapture::VideoCapture(int index, int apiPreference, const std::vector& params) - : throwOnFail(true) + : throwOnFail(false) { CV_TRACE_FUNCTION(); open(index, apiPreference, params); diff --git a/modules/videoio/src/cap_ffmpeg.cpp b/modules/videoio/src/cap_ffmpeg.cpp index 07f9998a17..df24a7404a 100644 --- a/modules/videoio/src/cap_ffmpeg.cpp +++ b/modules/videoio/src/cap_ffmpeg.cpp @@ -49,7 +49,8 @@ #include "cap_ffmpeg_impl.hpp" -#define icvCreateFileCapture_FFMPEG_p cvCreateFileCapture_FFMPEG +// TODO drop legacy code +//#define icvCreateFileCapture_FFMPEG_p cvCreateFileCapture_FFMPEG #define icvReleaseCapture_FFMPEG_p cvReleaseCapture_FFMPEG #define icvGrabFrame_FFMPEG_p cvGrabFrame_FFMPEG #define icvRetrieveFrame_FFMPEG_p cvRetrieveFrame_FFMPEG @@ -101,36 +102,14 @@ public: return true; } - virtual bool open( const cv::String& filename ) - { - close(); - - ffmpegCapture = icvCreateFileCapture_FFMPEG_p( filename.c_str() ); - return ffmpegCapture != 0; - } bool open(const cv::String& filename, const cv::VideoCaptureParameters& params) { close(); - ffmpegCapture = icvCreateFileCapture_FFMPEG_p(filename.c_str()); - if (ffmpegCapture && !params.empty()) - { - if (params.has(CAP_PROP_FORMAT)) // just a sample code - { - int value = params.get(CAP_PROP_FORMAT); - if (!setProperty(CAP_PROP_FORMAT, value)) - { - CV_Error_(Error::StsBadArg, ("VIDEOIO/FFMPEG: CAP_PROP_FORMAT parameter value is invalid/unsupported: %d", value)); - } - } - if (params.warnUnusedParameters()) - { - CV_Error(Error::StsBadArg, "VIDEOIO/FFMPEG: unsupported parameters in .open(), see logger INFO channel for details"); - } - } + ffmpegCapture = cvCreateFileCaptureWithParams_FFMPEG(filename.c_str(), params); return ffmpegCapture != 0; } - virtual void close() + void close() { if (ffmpegCapture) icvReleaseCapture_FFMPEG_p( &ffmpegCapture ); @@ -251,7 +230,7 @@ cv::Ptr cvCreateVideoWriter_FFMPEG_proxy(const std::string& fi #include "plugin_api.hpp" #else #define CAPTURE_ABI_VERSION 1 -#define CAPTURE_API_VERSION 0 +#define CAPTURE_API_VERSION 1 #include "plugin_capture_api.hpp" #define WRITER_ABI_VERSION 1 #define WRITER_API_VERSION 0 @@ -287,6 +266,38 @@ CvResult CV_API_CALL cv_capture_open(const char* filename, int camera_index, CV_ return CV_ERROR_FAIL; } +static +CvResult CV_API_CALL cv_capture_open_with_params( + const char* filename, int camera_index, + int* params, unsigned n_params, + CV_OUT CvPluginCapture* handle +) +{ + if (!handle) + return CV_ERROR_FAIL; + *handle = NULL; + if (!filename) + return CV_ERROR_FAIL; + CV_UNUSED(camera_index); + CvCapture_FFMPEG_proxy *cap = 0; + try + { + cv::VideoCaptureParameters parameters(params, n_params); + cap = new CvCapture_FFMPEG_proxy(filename, parameters); + if (cap->isOpened()) + { + *handle = (CvPluginCapture)cap; + return CV_ERROR_OK; + } + } + catch (...) + { + } + if (cap) + delete cap; + return CV_ERROR_FAIL; +} + static CvResult CV_API_CALL cv_capture_release(CvPluginCapture handle) { @@ -505,6 +516,9 @@ static const OpenCV_VideoIO_Capture_Plugin_API capture_plugin_api = /* 5*/cv_capture_set_prop, /* 6*/cv_capture_grab, /* 7*/cv_capture_retrieve, + }, + { + /* 8*/cv_capture_open_with_params, } }; diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index f806b269ae..7f400c6395 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -456,7 +456,7 @@ static AVRational _opencv_ffmpeg_get_sample_aspect_ratio(AVStream *stream) struct CvCapture_FFMPEG { - bool open( const char* filename ); + bool open(const char* filename, const VideoCaptureParameters& params); void close(); double getProperty(int) const; @@ -857,7 +857,7 @@ public: } }; -bool CvCapture_FFMPEG::open( const char* _filename ) +bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters& params) { InternalFFMpegRegister::init(); AutoLock lock(_mutex); @@ -866,6 +866,29 @@ bool CvCapture_FFMPEG::open( const char* _filename ) close(); + if (!params.empty()) + { + if (params.has(CAP_PROP_FORMAT)) + { + int value = params.get(CAP_PROP_FORMAT); + if (value == -1) + { + CV_LOG_INFO(NULL, "VIDEOIO/FFMPEG: enabled demuxer only mode: '" << (_filename ? _filename : "") << "'"); + rawMode = true; + } + else + { + CV_LOG_ERROR(NULL, "VIDEOIO/FFMPEG: CAP_PROP_FORMAT parameter value is invalid/unsupported: " << value); + return false; + } + } + if (params.warnUnusedParameters()) + { + CV_LOG_ERROR(NULL, "VIDEOIO/FFMPEG: unsupported parameters in .open(), see logger INFO channel for details"); + return false; + } + } + #if USE_AV_INTERRUPT_CALLBACK /* interrupt callback */ interrupt_metadata.timeout_after_ms = LIBAVFORMAT_INTERRUPT_OPEN_TIMEOUT_MS; @@ -2424,13 +2447,15 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, -CvCapture_FFMPEG* cvCreateFileCapture_FFMPEG( const char* filename ) +static +CvCapture_FFMPEG* cvCreateFileCaptureWithParams_FFMPEG(const char* filename, const VideoCaptureParameters& params) { + // FIXIT: remove unsafe malloc() approach CvCapture_FFMPEG* capture = (CvCapture_FFMPEG*)malloc(sizeof(*capture)); if (!capture) return 0; capture->init(); - if( capture->open( filename )) + if (capture->open(filename, params)) return capture; capture->close(); @@ -2438,7 +2463,6 @@ CvCapture_FFMPEG* cvCreateFileCapture_FFMPEG( const char* filename ) return 0; } - void cvReleaseCapture_FFMPEG(CvCapture_FFMPEG** capture) { if( capture && *capture ) diff --git a/modules/videoio/src/cap_ffmpeg_legacy_api.hpp b/modules/videoio/src/cap_ffmpeg_legacy_api.hpp index d918765e2e..09ef0fb203 100644 --- a/modules/videoio/src/cap_ffmpeg_legacy_api.hpp +++ b/modules/videoio/src/cap_ffmpeg_legacy_api.hpp @@ -24,7 +24,7 @@ extern "C" typedef struct CvCapture_FFMPEG CvCapture_FFMPEG; typedef struct CvVideoWriter_FFMPEG CvVideoWriter_FFMPEG; -OPENCV_FFMPEG_API struct CvCapture_FFMPEG* cvCreateFileCapture_FFMPEG(const char* filename); +//OPENCV_FFMPEG_API struct CvCapture_FFMPEG* cvCreateFileCapture_FFMPEG(const char* filename); OPENCV_FFMPEG_API int cvSetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap, int prop, double value); OPENCV_FFMPEG_API double cvGetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap, int prop); diff --git a/modules/videoio/src/cap_interface.hpp b/modules/videoio/src/cap_interface.hpp index bc80300f2a..5375265d73 100644 --- a/modules/videoio/src/cap_interface.hpp +++ b/modules/videoio/src/cap_interface.hpp @@ -82,6 +82,15 @@ public: } } + VideoParameters(int* params, unsigned n_params) + { + params_.reserve(n_params); + for (unsigned i = 0; i < n_params; ++i) + { + add(params[2*i], params[2*i + 1]); + } + } + void add(int key, int value) { params_.emplace_back(key, value); @@ -190,17 +199,13 @@ private: class VideoWriterParameters : public VideoParameters { public: - VideoWriterParameters() = default; - - explicit VideoWriterParameters(const std::vector& params) : VideoParameters(params) {}; + using VideoParameters::VideoParameters; // reuse constructors }; class VideoCaptureParameters : public VideoParameters { public: - VideoCaptureParameters() = default; - - explicit VideoCaptureParameters(const std::vector& params) : VideoParameters(params) {}; + using VideoParameters::VideoParameters; // reuse constructors }; class IVideoCapture diff --git a/modules/videoio/src/plugin_capture_api.hpp b/modules/videoio/src/plugin_capture_api.hpp index c18f77685a..2cb3f3163a 100644 --- a/modules/videoio/src/plugin_capture_api.hpp +++ b/modules/videoio/src/plugin_capture_api.hpp @@ -13,7 +13,7 @@ /// 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 +#define CAPTURE_API_VERSION 1 /// increased for incompatible changes, e.g. remove function argument /// Caller ABI == Plugin ABI -> plugin is compatible @@ -103,18 +103,23 @@ struct OpenCV_VideoIO_Capture_Plugin_API_v1_0_api_entries 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 + /** @brief Open video capture with parameters - @note API-CALL XXX, API-Version == YYY + @param filename File name or NULL to use camera_index instead + @param camera_index Camera index (used if filename == NULL) + @param params pointer on 2*n_params array of 'key,value' pairs + @param n_params number of passed parameters + @param[out] handle pointer on Capture handle + + @note API-CALL 8, API-Version == 1 */ - CvResult (CV_API_CALL* zzz)( - ... - ); + CvResult (CV_API_CALL *Capture_open_with_params)( + const char* filename, int camera_index, + int* params, unsigned n_params, + CV_OUT CvPluginCapture* handle); }; // OpenCV_VideoIO_Capture_Plugin_API_v1_1_api_entries -#endif typedef struct OpenCV_VideoIO_Capture_Plugin_API_v1_0 { @@ -122,16 +127,14 @@ typedef struct OpenCV_VideoIO_Capture_Plugin_API_v1_0 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 +#if 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; diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 84c34ece95..52ec4c751d 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -509,12 +509,10 @@ TEST(videoio_ffmpeg, create_with_property_badarg) throw SkipTestException("FFmpeg backend was not found"); string video_file = findDataFile("video/big_buck_bunny.mp4"); - EXPECT_ANY_THROW( - { - VideoCapture cap(video_file, CAP_FFMPEG, { - CAP_PROP_FORMAT, -2 // invalid - }); + VideoCapture cap(video_file, CAP_FFMPEG, { + CAP_PROP_FORMAT, -2 // invalid }); + EXPECT_FALSE(cap.isOpened()); }