videoio(plugins): support VideoCaptureParameters, CAPTURE_API_VERSION=1
authorAlexander Alekhin <alexander.a.alekhin@gmail.com>
Thu, 28 Jan 2021 06:00:38 +0000 (06:00 +0000)
committerAlexander Alekhin <alexander.a.alekhin@gmail.com>
Thu, 28 Jan 2021 06:09:23 +0000 (06:09 +0000)
- example: ffmpeg

modules/videoio/src/backend_plugin.cpp
modules/videoio/src/cap.cpp
modules/videoio/src/cap_ffmpeg.cpp
modules/videoio/src/cap_ffmpeg_impl.hpp
modules/videoio/src/cap_ffmpeg_legacy_api.hpp
modules/videoio/src/cap_interface.hpp
modules/videoio/src/plugin_capture_api.hpp
modules/videoio/test/test_ffmpeg.cpp

index 91270d9..6d79b47 100644 (file)
@@ -564,20 +564,40 @@ class PluginCapture : public cv::IVideoCapture
 public:
     static
     Ptr<PluginCapture> 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<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.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>();
     }
 
@@ -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<int> 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<PluginWriter>();
             }
+            if (params.warnUnusedParameters())
+            {
+                CV_LOG_ERROR(NULL, "VIDEOIO/FFMPEG: 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>();
     }
 
@@ -743,14 +769,21 @@ public:
 };
 
 
-Ptr<IVideoCapture> PluginBackend::createCapture(int camera) const
+Ptr<IVideoCapture> PluginBackend::createCapture(int camera, const VideoCaptureParameters& params) const
 {
     try
     {
         if (capture_api_)
-            return PluginCapture::create(capture_api_, std::string(), camera); //.staticCast<IVideoCapture>();
+            return PluginCapture::create(capture_api_, std::string(), camera, params); //.staticCast<IVideoCapture>();
         if (plugin_api_)
-            return legacy::PluginCapture::create(plugin_api_, std::string(), camera); //.staticCast<IVideoCapture>();
+        {
+            Ptr<IVideoCapture> cap = legacy::PluginCapture::create(plugin_api_, std::string(), camera); //.staticCast<IVideoCapture>();
+            if (cap && !params.empty())
+            {
+                applyParametersFallback(cap, params);
+            }
+            return cap;
+        }
     }
     catch (...)
     {
@@ -760,25 +793,21 @@ Ptr<IVideoCapture> PluginBackend::createCapture(int camera) const
     return Ptr<IVideoCapture>();
 }
 
-Ptr<IVideoCapture> PluginBackend::createCapture(int camera, const VideoCaptureParameters& params) const
-{
-    // TODO Update plugins API to support parameters
-    Ptr<IVideoCapture> cap = createCapture(camera);
-    if (cap && !params.empty())
-    {
-        applyParametersFallback(cap, params);
-    }
-    return cap;
-}
-
-Ptr<IVideoCapture> PluginBackend::createCapture(const std::string &filename) const
+Ptr<IVideoCapture> PluginBackend::createCapture(const std::string &filename, const VideoCaptureParameters& params) const
 {
     try
     {
         if (capture_api_)
-            return PluginCapture::create(capture_api_, filename, 0); //.staticCast<IVideoCapture>();
+            return PluginCapture::create(capture_api_, filename, 0, params); //.staticCast<IVideoCapture>();
         if (plugin_api_)
-            return legacy::PluginCapture::create(plugin_api_, filename, 0); //.staticCast<IVideoCapture>();
+        {
+            Ptr<IVideoCapture> cap = legacy::PluginCapture::create(plugin_api_, filename, 0); //.staticCast<IVideoCapture>();
+            if (cap && !params.empty())
+            {
+                applyParametersFallback(cap, params);
+            }
+            return cap;
+        }
     }
     catch (...)
     {
@@ -788,17 +817,6 @@ Ptr<IVideoCapture> PluginBackend::createCapture(const std::string &filename) con
     return Ptr<IVideoCapture>();
 }
 
-Ptr<IVideoCapture> PluginBackend::createCapture(const std::string &filename, const VideoCaptureParameters& params) const
-{
-    // TODO Update plugins API to support parameters
-    Ptr<IVideoCapture> cap = createCapture(filename);
-    if (cap && !params.empty())
-    {
-        applyParametersFallback(cap, params);
-    }
-    return cap;
-}
-
 Ptr<IVideoWriter> PluginBackend::createWriter(const std::string& filename, int fourcc, double fps,
                                               const cv::Size& sz, const VideoWriterParameters& params) const
 {
index f0afc1f..f896751 100644 (file)
@@ -76,7 +76,7 @@ VideoCapture::VideoCapture(const String& filename, int apiPreference) : throwOnF
 }
 
 VideoCapture::VideoCapture(const String& filename, int apiPreference, const std::vector<int>& 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<int>& params)
-    : throwOnFail(true)
+    : throwOnFail(false)
 {
     CV_TRACE_FUNCTION();
     open(index, apiPreference, params);
index 07f9998..df24a74 100644 (file)
@@ -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<int>(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<cv::IVideoWriter> 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
@@ -288,6 +267,38 @@ CvResult CV_API_CALL cv_capture_open(const char* filename, int camera_index, CV_
 }
 
 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)
 {
     if (!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,
     }
 };
 
index f806b26..7f400c6 100644 (file)
@@ -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<int>(CAP_PROP_FORMAT);
+            if (value == -1)
+            {
+                CV_LOG_INFO(NULL, "VIDEOIO/FFMPEG: enabled demuxer only mode: '" << (_filename ? _filename : "<NULL>") << "'");
+                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 )
index d918765..09ef0fb 100644 (file)
@@ -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);
index bc80300..5375265 100644 (file)
@@ -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<int>& params) : VideoParameters(params) {};
+    using VideoParameters::VideoParameters;  // reuse constructors
 };
 
 class VideoCaptureParameters : public VideoParameters
 {
 public:
-    VideoCaptureParameters() = default;
-
-    explicit VideoCaptureParameters(const std::vector<int>& params) : VideoParameters(params) {};
+    using VideoParameters::VideoParameters;  // reuse constructors
 };
 
 class IVideoCapture
index c18f776..2cb3f31 100644 (file)
@@ -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;
index 84c34ec..52ec4c7 100644 (file)
@@ -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());
 }