From 270cc3bcbc6de8549c3dd861ef392accee793397 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 19 Jul 2018 16:14:50 +0300 Subject: [PATCH] videoio: add routines to query information about backends API into cv::videoio_registry namespace --- modules/python/src2/cv2.cpp | 3 +- modules/python/test/test_videoio.py | 25 ++++++++ modules/videoio/include/opencv2/videoio.hpp | 1 + .../include/opencv2/videoio/registry.hpp | 44 +++++++++++++ .../videoio/misc/python/pyopencv_videoio.hpp | 50 +++++++++++++++ modules/videoio/src/videoio_registry.cpp | 56 ++++++++++++++++ modules/videoio/test/test_precomp.hpp | 27 ++++++++ modules/videoio/test/test_video_io.cpp | 64 +++---------------- 8 files changed, 213 insertions(+), 57 deletions(-) create mode 100644 modules/python/test/test_videoio.py create mode 100644 modules/videoio/include/opencv2/videoio/registry.hpp create mode 100644 modules/videoio/misc/python/pyopencv_videoio.hpp diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 179bf678b2..03fd912104 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -1563,8 +1563,6 @@ PyObject* pyopencv_from(const Moments& m) "nu30", m.nu30, "nu21", m.nu21, "nu12", m.nu12, "nu03", m.nu03); } -#include "pyopencv_custom_headers.h" - static int OnError(int status, const char *func_name, const char *err_msg, const char *file_name, int line, void *userdata) { PyGILState_STATE gstate; @@ -1802,6 +1800,7 @@ static int convert_to_char(PyObject *o, char *dst, const char *name = "no_name") # pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif +#include "pyopencv_custom_headers.h" #include "pyopencv_generated_types.h" #include "pyopencv_generated_funcs.h" diff --git a/modules/python/test/test_videoio.py b/modules/python/test/test_videoio.py new file mode 100644 index 0000000000..2bbfeecda0 --- /dev/null +++ b/modules/python/test/test_videoio.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +from __future__ import print_function + +import numpy as np +import cv2 as cv + +from tests_common import NewOpenCVTests + +class Bindings(NewOpenCVTests): + + def check_name(self, name): + #print(name) + self.assertFalse(name == None) + self.assertFalse(name == "") + + def test_registry(self): + self.check_name(cv.videoio_registry.getBackendName(cv.CAP_ANY)); + self.check_name(cv.videoio_registry.getBackendName(cv.CAP_FFMPEG)) + self.check_name(cv.videoio_registry.getBackendName(cv.CAP_OPENCV_MJPEG)) + backends = cv.videoio_registry.getBackends() + for backend in backends: + self.check_name(cv.videoio_registry.getBackendName(backend)) + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index d43e703984..eef840b6ec 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -59,6 +59,7 @@ @defgroup videoio_c C API for video I/O @defgroup videoio_ios iOS glue for video I/O @defgroup videoio_winrt WinRT glue for video I/O + @defgroup videoio_registry Query I/O API backends registry @} */ diff --git a/modules/videoio/include/opencv2/videoio/registry.hpp b/modules/videoio/include/opencv2/videoio/registry.hpp new file mode 100644 index 0000000000..7404c68116 --- /dev/null +++ b/modules/videoio/include/opencv2/videoio/registry.hpp @@ -0,0 +1,44 @@ +// 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_VIDEOIO_REGISTRY_HPP +#define OPENCV_VIDEOIO_REGISTRY_HPP + +#include + +namespace cv { namespace videoio_registry { +/** @addtogroup videoio_registry +This section contains API description how to query/configure available Video I/O backends. + +Runtime configuration options: +- enable debug mode: `OPENCV_VIDEOIO_DEBUG=1` +- change backend priority: `OPENCV_VIDEOIO_PRIORITY_=9999` +- disable backend: `OPENCV_VIDEOIO_PRIORITY_=0` +- specify list of backends with high priority (>100000): `OPENCV_VIDEOIO_PRIORITY_LIST=FFMPEG,GSTREAMER` + +@{ + */ + + +/** @brief Returns backend API name or "unknown" +@param api backend ID (#VideoCaptureAPIs) +*/ +CV_EXPORTS_W cv::String getBackendName(VideoCaptureAPIs api); + +/** @brief Returns list of all builtin backends */ +CV_EXPORTS_W std::vector getBackends(); + +/** @brief Returns list of available backends which works via `cv::VideoCapture(int index)` */ +CV_EXPORTS_W std::vector getCameraBackends(); + +/** @brief Returns list of available backends which works via `cv::VideoCapture(filename)` */ +CV_EXPORTS_W std::vector getStreamBackends(); + +/** @brief Returns list of available backends which works via `cv::VideoWriter()` */ +CV_EXPORTS_W std::vector getWriterBackends(); + +//! @} +}} // namespace + +#endif // OPENCV_VIDEOIO_REGISTRY_HPP diff --git a/modules/videoio/misc/python/pyopencv_videoio.hpp b/modules/videoio/misc/python/pyopencv_videoio.hpp new file mode 100644 index 0000000000..453a57a126 --- /dev/null +++ b/modules/videoio/misc/python/pyopencv_videoio.hpp @@ -0,0 +1,50 @@ +#ifdef HAVE_OPENCV_VIDEOIO +typedef std::vector vector_VideoCaptureAPIs; + +template<> +bool pyopencv_to(PyObject *o, cv::VideoCaptureAPIs &v, const char *name) +{ + (void)name; + v = CAP_ANY; + if (!o || o == Py_None) + return false; + else if (PyLong_Check(o)) + { + v = VideoCaptureAPIs((int64)PyLong_AsLongLong(o)); + return true; + } + else if (PyInt_Check(o)) + { + v = VideoCaptureAPIs((int64)PyInt_AS_LONG(o)); + return true; + } + else + return false; +} + +template<> +PyObject* pyopencv_from(const cv::VideoCaptureAPIs &v) +{ + return pyopencv_from((int)(v)); +} + +template<> struct pyopencvVecConverter +{ + static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + { + return pyopencv_to_generic_vec(obj, value, info); + } + + static PyObject* from(const std::vector& value) + { + return pyopencv_from_generic_vec(value); + } +}; + +template<> +bool pyopencv_to(PyObject *o, std::vector& apis, const char *name) +{ + return pyopencvVecConverter::to(o, apis, ArgInfo(name, false)); +} + +#endif // HAVE_OPENCV_VIDEOIO diff --git a/modules/videoio/src/videoio_registry.cpp b/modules/videoio/src/videoio_registry.cpp index 9f0abc512b..85fc239ad9 100644 --- a/modules/videoio/src/videoio_registry.cpp +++ b/modules/videoio/src/videoio_registry.cpp @@ -6,6 +6,8 @@ #include "videoio_registry.hpp" +#include "opencv2/videoio/registry.hpp" + #include "cap_intelperc.hpp" #include "cap_dshow.hpp" @@ -247,6 +249,8 @@ public: return g_instance; } + inline std::vector getEnabledBackends() const { return enabledBackends; } + inline std::vector getAvailableBackends_CaptureByIndex() const { std::vector result; @@ -302,6 +306,58 @@ std::vector getAvailableBackends_Writer() return result; } +cv::String getBackendName(VideoCaptureAPIs api) +{ + if (api == CAP_ANY) + return "CAP_ANY"; // special case, not a part of backends list + const int N = sizeof(builtin_backends)/sizeof(builtin_backends[0]); + for (size_t i = 0; i < N; i++) + { + const VideoBackendInfo& backend = builtin_backends[i]; + if (backend.id == api) + return backend.name; + } + return cv::format("UnknownVideoAPI(%d)", (int)api); +} + +std::vector getBackends() +{ + std::vector backends = VideoBackendRegistry::getInstance().getEnabledBackends(); + std::vector result; + for (size_t i = 0; i < backends.size(); i++) + result.push_back((VideoCaptureAPIs)backends[i].id); + return result; +} + +std::vector getCameraBackends() +{ + const std::vector backends = VideoBackendRegistry::getInstance().getAvailableBackends_CaptureByIndex(); + std::vector result; + for (size_t i = 0; i < backends.size(); i++) + result.push_back((VideoCaptureAPIs)backends[i].id); + return result; + +} + +std::vector getStreamBackends() +{ + const std::vector backends = VideoBackendRegistry::getInstance().getAvailableBackends_CaptureByFilename(); + std::vector result; + for (size_t i = 0; i < backends.size(); i++) + result.push_back((VideoCaptureAPIs)backends[i].id); + return result; + +} + +std::vector getWriterBackends() +{ + const std::vector backends = VideoBackendRegistry::getInstance().getAvailableBackends_Writer(); + std::vector result; + for (size_t i = 0; i < backends.size(); i++) + result.push_back((VideoCaptureAPIs)backends[i].id); + return result; +} + } // namespace registry #define TRY_OPEN(backend_func) \ diff --git a/modules/videoio/test/test_precomp.hpp b/modules/videoio/test/test_precomp.hpp index 8d9f5e0358..e3612adc1e 100644 --- a/modules/videoio/test/test_precomp.hpp +++ b/modules/videoio/test/test_precomp.hpp @@ -6,10 +6,26 @@ #include "opencv2/ts.hpp" #include "opencv2/videoio.hpp" +#include "opencv2/videoio/registry.hpp" #include "opencv2/imgproc/imgproc_c.h" #include "opencv2/core/private.hpp" +namespace cv { + +inline std::ostream &operator<<(std::ostream &out, const VideoCaptureAPIs& api) +{ + out << cv::videoio_registry::getBackendName(api); return out; +} + +static inline void PrintTo(const cv::VideoCaptureAPIs& api, std::ostream* os) +{ + *os << cv::videoio_registry::getBackendName(api); +} + +} // namespace + + inline std::string fourccToString(int fourcc) { return cv::format("%c%c%c%c", fourcc & 255, (fourcc >> 8) & 255, (fourcc >> 16) & 255, (fourcc >> 24) & 255); @@ -55,4 +71,15 @@ public: } }; + +static inline bool isBackendAvailable(cv::VideoCaptureAPIs api, const std::vector& api_list) +{ + for (size_t i = 0; i < api_list.size(); i++) + { + if (api_list[i] == api) + return true; + } + return false; +} + #endif diff --git a/modules/videoio/test/test_video_io.cpp b/modules/videoio/test/test_video_io.cpp index c1834be5ec..7dcdc1d574 100644 --- a/modules/videoio/test/test_video_io.cpp +++ b/modules/videoio/test/test_video_io.cpp @@ -46,62 +46,12 @@ namespace opencv_test { -struct VideoCaptureAPI -{ - VideoCaptureAPIs api; - - inline const char * toString() const - { - switch (api) - { - case CAP_ANY: return "CAP_ANY"; - #ifdef __linux__ - case CAP_V4L2: return "CAP_V4L/CAP_V4L2"; - #else - case CAP_VFW: return "CAP_VFW"; - #endif - case CAP_FIREWIRE: return "CAP_FIREWIRE"; - case CAP_QT: return "CAP_QT"; - case CAP_UNICAP: return "CAP_UNICAP"; - case CAP_DSHOW: return "CAP_DSHOW"; - case CAP_PVAPI: return "CAP_PVAPI"; - case CAP_OPENNI: return "CAP_OPENNI"; - case CAP_OPENNI_ASUS: return "CAP_OPENNI_ASUS"; - case CAP_ANDROID: return "CAP_ANDROID"; - case CAP_XIAPI: return "CAP_XIAPI"; - case CAP_AVFOUNDATION: return "CAP_AVFOUNDATION"; - case CAP_GIGANETIX: return "CAP_GIGANETIX"; - case CAP_MSMF: return "CAP_MSMF"; - case CAP_WINRT: return "CAP_WINRT"; - case CAP_INTELPERC: return "CAP_INTELPERC"; - case CAP_OPENNI2: return "CAP_OPENNI2"; - case CAP_OPENNI2_ASUS: return "CAP_OPENNI2_ASUS"; - case CAP_GPHOTO2: return "CAP_GPHOTO2"; - case CAP_GSTREAMER: return "CAP_GSTREAMER"; - case CAP_FFMPEG: return "CAP_FFMPEG"; - case CAP_IMAGES: return "CAP_IMAGES"; - case CAP_ARAVIS: return "CAP_ARAVIS"; - case CAP_OPENCV_MJPEG: return "CAP_OPENCV_MJPEG"; - case CAP_INTEL_MFX: return "CAP_INTEL_MFX"; - case CAP_XINE: return "CAP_XINE"; - } - return "unknown"; - } - VideoCaptureAPI(int api_ = CAP_ANY) : api((VideoCaptureAPIs)api_) {} - operator int() { return api; } -}; - -inline std::ostream &operator<<(std::ostream &out, const VideoCaptureAPI & api) -{ - out << api.toString(); return out; -} - class Videoio_Test_Base { protected: string ext; string video_file; - VideoCaptureAPI apiPref; + VideoCaptureAPIs apiPref; protected: Videoio_Test_Base() {} virtual ~Videoio_Test_Base() {} @@ -131,6 +81,8 @@ protected: public: void doTest() { + if (!isBackendAvailable(apiPref, cv::videoio_registry::getStreamBackends())) + throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref)); VideoCapture cap; ASSERT_NO_THROW(cap.open(video_file, apiPref)); if (!cap.isOpened()) @@ -200,7 +152,7 @@ public: }; //================================================================================================== -typedef tuple Backend_Type_Params; +typedef tuple Backend_Type_Params; class Videoio_Bunny : public Videoio_Test_Base, public testing::TestWithParam { @@ -214,6 +166,8 @@ public: } void doFrameCountTest() { + if (!isBackendAvailable(apiPref, cv::videoio_registry::getStreamBackends())) + throw SkipTestException(cv::String("Backend is not available/disabled: ") + cv::videoio_registry::getBackendName(apiPref)); VideoCapture cap; EXPECT_NO_THROW(cap.open(video_file, apiPref)); if (!cap.isOpened()) @@ -274,7 +228,7 @@ struct Ext_Fourcc_PSNR string ext; string fourcc; float PSNR; - VideoCaptureAPI api; + VideoCaptureAPIs api; }; typedef tuple Size_Ext_Fourcc_PSNR; @@ -348,7 +302,7 @@ public: //================================================================================================== -static VideoCaptureAPI backend_params[] = { +static const VideoCaptureAPIs backend_params[] = { #ifdef HAVE_QUICKTIME CAP_QT, #endif @@ -383,7 +337,7 @@ static VideoCaptureAPI backend_params[] = { // CAP_INTEL_MFX }; -static string bunny_params[] = { +static const string bunny_params[] = { #ifdef HAVE_VIDEO_INPUT string("wmv"), string("mov"),