diff --git a/modules/videoio/cmake/detect_ffmpeg.cmake b/modules/videoio/cmake/detect_ffmpeg.cmake index 1d4faa8394..dd5eefaac9 100644 --- a/modules/videoio/cmake/detect_ffmpeg.cmake +++ b/modules/videoio/cmake/detect_ffmpeg.cmake @@ -1,5 +1,5 @@ # --- FFMPEG --- -OCV_OPTION(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE "Include FFMPEG/libavdevice library support." OFF +OCV_OPTION(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE "Include FFMPEG/libavdevice library support." ON VISIBLE_IF WITH_FFMPEG) if(NOT HAVE_FFMPEG AND OPENCV_FFMPEG_USE_FIND_PACKAGE) diff --git a/modules/videoio/src/cap_ffmpeg.cpp b/modules/videoio/src/cap_ffmpeg.cpp index cc62920c74..09ed1f43c2 100644 --- a/modules/videoio/src/cap_ffmpeg.cpp +++ b/modules/videoio/src/cap_ffmpeg.cpp @@ -74,6 +74,11 @@ public: { open(filename, params); } + CvCapture_FFMPEG_proxy(int index, const cv::VideoCaptureParameters& params) + : ffmpegCapture(NULL) + { + open(index, params); + } CvCapture_FFMPEG_proxy(const Ptr& stream, const cv::VideoCaptureParameters& params) : ffmpegCapture(NULL) { @@ -127,6 +132,13 @@ public: ffmpegCapture = cvCreateFileCaptureWithParams_FFMPEG(filename.c_str(), params); return ffmpegCapture != 0; } + bool open(int index, const cv::VideoCaptureParameters& params) + { + close(); + + ffmpegCapture = cvCreateFileCaptureWithParams_FFMPEG(index, params); + return ffmpegCapture != 0; + } bool open(const Ptr& stream, const cv::VideoCaptureParameters& params) { close(); @@ -161,6 +173,14 @@ cv::Ptr cvCreateFileCapture_FFMPEG_proxy(const std::string &f return cv::Ptr(); } +cv::Ptr cvCreateCameraCapture_FFMPEG_proxy(int index, const cv::VideoCaptureParameters& params) +{ + cv::Ptr capture = cv::makePtr(index, params); + if (capture && capture->isOpened()) + return capture; + return cv::Ptr(); +} + cv::Ptr cvCreateStreamCapture_FFMPEG_proxy(const Ptr& stream, const cv::VideoCaptureParameters& params) { cv::Ptr capture = std::make_shared(stream, params); @@ -272,13 +292,15 @@ CvResult CV_API_CALL cv_capture_open(const char* filename, int camera_index, CV_ if (!handle) return CV_ERROR_FAIL; *handle = NULL; - if (!filename) + if (!filename && camera_index < 0) return CV_ERROR_FAIL; - CV_UNUSED(camera_index); CvCapture_FFMPEG_proxy *cap = 0; try { - cap = new CvCapture_FFMPEG_proxy(String(filename), cv::VideoCaptureParameters()); + if (filename) + cap = new CvCapture_FFMPEG_proxy(String(filename), cv::VideoCaptureParameters()); + else + cap = new CvCapture_FFMPEG_proxy(camera_index, cv::VideoCaptureParameters()); if (cap->isOpened()) { *handle = (CvPluginCapture)cap; @@ -308,14 +330,16 @@ CvResult CV_API_CALL cv_capture_open_with_params( if (!handle) return CV_ERROR_FAIL; *handle = NULL; - if (!filename) + if (!filename && camera_index < 0) 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(String(filename), parameters); + if (filename) + cap = new CvCapture_FFMPEG_proxy(String(filename), parameters); + else + cap = new CvCapture_FFMPEG_proxy(camera_index, parameters); if (cap->isOpened()) { *handle = (CvPluginCapture)cap; diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 6d7749d21c..7834c59111 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -526,7 +526,7 @@ inline static std::string _opencv_ffmpeg_get_error_string(int error_code) struct CvCapture_FFMPEG { - bool open(const char* filename, const Ptr& stream, const VideoCaptureParameters& params); + bool open(const char* filename, int index, const Ptr& stream, const VideoCaptureParameters& params); void close(); double getProperty(int) const; @@ -1043,7 +1043,7 @@ static bool isThreadSafe() { return threadSafe; } -bool CvCapture_FFMPEG::open(const char* _filename, const Ptr& stream, const VideoCaptureParameters& params) +bool CvCapture_FFMPEG::open(const char* _filename, int index, const Ptr& stream, const VideoCaptureParameters& params) { const bool threadSafe = isThreadSafe(); InternalFFMpegRegister::init(threadSafe); @@ -1145,16 +1145,6 @@ bool CvCapture_FFMPEG::open(const char* _filename, const Ptr& str } } -#if USE_AV_INTERRUPT_CALLBACK - /* interrupt callback */ - interrupt_metadata.timeout_after_ms = open_timeout; - get_monotonic_time(&interrupt_metadata.value); - - ic = avformat_alloc_context(); - ic->interrupt_callback.callback = _opencv_ffmpeg_interrupt_callback; - ic->interrupt_callback.opaque = &interrupt_metadata; -#endif - std::string options = utils::getConfigurationParameterString("OPENCV_FFMPEG_CAPTURE_OPTIONS"); if (options.empty()) { @@ -1180,7 +1170,53 @@ bool CvCapture_FFMPEG::open(const char* _filename, const Ptr& str input_format = av_find_input_format(entry->value); } - if (!_filename) + AVDeviceInfoList* device_list = nullptr; + if (index >= 0) + { +#ifdef HAVE_FFMPEG_LIBAVDEVICE + entry = av_dict_get(dict, "f", NULL, 0); + const char* backend = nullptr; + if (entry) + { + backend = entry->value; + } + else + { +#ifdef __linux__ + backend = "v4l2"; +#endif +#ifdef _WIN32 + backend = "dshow"; +#endif +#ifdef __APPLE__ + backend = "avfoundation"; +#endif + } + avdevice_list_input_sources(nullptr, backend, nullptr, &device_list); + if (!device_list) + { + CV_LOG_ONCE_WARNING(NULL, "VIDEOIO/FFMPEG: Failed list devices for backend " << backend); + return false; + } + CV_CheckLT(index, device_list->nb_devices, "VIDEOIO/FFMPEG: Camera index out of range"); + _filename = device_list->devices[index]->device_name; +#else + CV_LOG_ONCE_WARNING(NULL, "VIDEOIO/FFMPEG: OpenCV should be configured with libavdevice to open a camera device"); + return false; +#endif + } + +#if USE_AV_INTERRUPT_CALLBACK + /* interrupt callback */ + interrupt_metadata.timeout_after_ms = open_timeout; + get_monotonic_time(&interrupt_metadata.value); + + ic = avformat_alloc_context(); + ic->interrupt_callback.callback = _opencv_ffmpeg_interrupt_callback; + ic->interrupt_callback.opaque = &interrupt_metadata; +#endif + + if (stream) { size_t avio_ctx_buffer_size = 4096; uint8_t* avio_ctx_buffer = (uint8_t*)av_malloc(avio_ctx_buffer_size); @@ -1231,6 +1267,13 @@ bool CvCapture_FFMPEG::open(const char* _filename, const Ptr& str ic->pb = avio_context; } int err = avformat_open_input(&ic, _filename, input_format, &dict); + if (device_list) + { +#ifdef HAVE_FFMPEG_LIBAVDEVICE + avdevice_free_list_devices(&device_list); + device_list = nullptr; +#endif + } if (err < 0) { @@ -3447,7 +3490,22 @@ CvCapture_FFMPEG* cvCreateFileCaptureWithParams_FFMPEG(const char* filename, con if (!capture) return 0; capture->init(); - if (capture->open(filename, nullptr, params)) + if (capture->open(filename, -1, nullptr, params)) + return capture; + + capture->close(); + delete capture; + return 0; +} + +static +CvCapture_FFMPEG* cvCreateFileCaptureWithParams_FFMPEG(int index, const VideoCaptureParameters& params) +{ + CvCapture_FFMPEG* capture = new CvCapture_FFMPEG(); + if (!capture) + return 0; + capture->init(); + if (capture->open(nullptr, index, nullptr, params)) return capture; capture->close(); @@ -3462,7 +3520,7 @@ CvCapture_FFMPEG* cvCreateStreamCaptureWithParams_FFMPEG(const Ptrinit(); - if (capture->open(nullptr, stream, params)) + if (capture->open(nullptr, -1, stream, params)) return capture; capture->close(); diff --git a/modules/videoio/src/cap_interface.hpp b/modules/videoio/src/cap_interface.hpp index bb1e0b4ec9..ba50f6dcad 100644 --- a/modules/videoio/src/cap_interface.hpp +++ b/modules/videoio/src/cap_interface.hpp @@ -327,6 +327,7 @@ protected: //================================================================================================== Ptr cvCreateFileCapture_FFMPEG_proxy(const std::string &filename, const VideoCaptureParameters& params); +Ptr cvCreateCameraCapture_FFMPEG_proxy(int index, const VideoCaptureParameters& params); Ptr cvCreateStreamCapture_FFMPEG_proxy(const Ptr& stream, const VideoCaptureParameters& params); Ptr cvCreateVideoWriter_FFMPEG_proxy(const std::string& filename, int fourcc, double fps, const Size& frameSize, diff --git a/modules/videoio/src/videoio_registry.cpp b/modules/videoio/src/videoio_registry.cpp index a84258ad90..9c599d2c71 100644 --- a/modules/videoio/src/videoio_registry.cpp +++ b/modules/videoio/src/videoio_registry.cpp @@ -112,6 +112,12 @@ static const struct VideoBackendInfo builtin_backends[] = DECLARE_STATIC_BACKEND(CAP_V4L, "V4L_BSD", MODE_CAPTURE_ALL, create_V4L_capture_file, create_V4L_capture_cam, 0) #endif + // FFmpeg webcamera by underlying backend (DShow, V4L2, AVFoundation) +#ifdef HAVE_FFMPEG + DECLARE_STATIC_BACKEND(CAP_FFMPEG, "FFMPEG", MODE_CAPTURE_BY_INDEX, 0, cvCreateCameraCapture_FFMPEG_proxy, 0) +#elif defined(ENABLE_PLUGINS) || defined(HAVE_FFMPEG_WRAPPER) + DECLARE_DYNAMIC_BACKEND(CAP_FFMPEG, "FFMPEG", MODE_CAPTURE_BY_INDEX) +#endif // RGB-D universal #ifdef HAVE_OPENNI2 diff --git a/modules/videoio/test/test_camera.cpp b/modules/videoio/test/test_camera.cpp index f11fa3f251..30592b2932 100644 --- a/modules/videoio/test/test_camera.cpp +++ b/modules/videoio/test/test_camera.cpp @@ -327,4 +327,18 @@ TEST(DISABLED_videoio_camera, waitAny_V4L) } } +TEST(DISABLED_videoio_camera, ffmpeg_index) +{ + int idx = (int)utils::getConfigurationParameterSizeT("OPENCV_TEST_FFMPEG_DEVICE_IDX", (size_t)-1); + if (idx == -1) + { + throw SkipTestException("OPENCV_TEST_FFMPEG_DEVICE_IDX is not set"); + } + VideoCapture cap; + ASSERT_TRUE(cap.open(idx, CAP_FFMPEG)); + Mat frame; + ASSERT_TRUE(cap.read(frame)); + ASSERT_FALSE(frame.empty()); +} + }} // namespace diff --git a/modules/videoio/test/test_v4l2.cpp b/modules/videoio/test/test_v4l2.cpp index 6ea1f67e6d..d560e718bd 100644 --- a/modules/videoio/test/test_v4l2.cpp +++ b/modules/videoio/test/test_v4l2.cpp @@ -158,6 +158,20 @@ inline static std::string param_printer(const testing::TestParamInfo:: #endif // HAVE_CAMV4L2