vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:10:33 +08:00
commit f7f077da11
6933 changed files with 2335208 additions and 0 deletions
+309
View File
@@ -0,0 +1,309 @@
set(VIDEOIO_ENABLE_PLUGINS_DEFAULT ON)
if(EMSCRIPTEN OR IOS OR XROS OR WINRT)
set(VIDEOIO_ENABLE_PLUGINS_DEFAULT OFF)
endif()
set(VIDEOIO_PLUGIN_LIST "" CACHE STRING "List of videoio backends to be compiled as plugins (ffmpeg, gstreamer, mfx, msmf or special value 'all')")
set(VIDEOIO_ENABLE_PLUGINS "${VIDEOIO_ENABLE_PLUGINS_DEFAULT}" CACHE BOOL "Allow building and using of videoio plugins")
mark_as_advanced(VIDEOIO_PLUGIN_LIST VIDEOIO_ENABLE_PLUGINS)
string(REPLACE "," ";" VIDEOIO_PLUGIN_LIST "${VIDEOIO_PLUGIN_LIST}") # support comma-separated list (,) too
string(TOLOWER "${VIDEOIO_PLUGIN_LIST}" VIDEOIO_PLUGIN_LIST)
if(NOT VIDEOIO_ENABLE_PLUGINS)
if(VIDEOIO_PLUGIN_LIST)
message(WARNING "VideoIO: plugins are disabled through VIDEOIO_ENABLE_PLUGINS, so VIDEOIO_PLUGIN_LIST='${VIDEOIO_PLUGIN_LIST}' is ignored")
set(VIDEOIO_PLUGIN_LIST "")
endif()
else()
# Make virtual opencv_videoio_plugins target
if(NOT TARGET opencv_videoio_plugins)
add_custom_target(opencv_videoio_plugins ALL)
endif()
endif()
ocv_add_module(videoio opencv_imgproc opencv_imgcodecs WRAP java objc python)
set(videoio_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/precomp.hpp)
set(videoio_srcs
"${CMAKE_CURRENT_LIST_DIR}/src/videoio_registry.cpp"
"${CMAKE_CURRENT_LIST_DIR}/src/cap.cpp"
"${CMAKE_CURRENT_LIST_DIR}/src/cap_images.cpp"
"${CMAKE_CURRENT_LIST_DIR}/src/cap_mjpeg_encoder.cpp"
"${CMAKE_CURRENT_LIST_DIR}/src/cap_mjpeg_decoder.cpp"
"${CMAKE_CURRENT_LIST_DIR}/src/backend_plugin.cpp"
"${CMAKE_CURRENT_LIST_DIR}/src/backend_static.cpp"
"${CMAKE_CURRENT_LIST_DIR}/src/container_avi.cpp")
file(GLOB videoio_ext_hdrs
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/*.hpp")
if(OPENCV_DEBUG_POSTFIX)
ocv_append_source_file_compile_definitions("${CMAKE_CURRENT_LIST_DIR}/src/backend_plugin.cpp" "DEBUG_POSTFIX=${OPENCV_DEBUG_POSTFIX}")
endif()
# Removing WinRT API headers by default
list(REMOVE_ITEM videoio_ext_hdrs "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/cap_winrt.hpp")
# Remove iOS API header by default
list(REMOVE_ITEM videoio_ext_hdrs "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/cap_ios.h")
if(DEFINED WINRT AND NOT DEFINED ENABLE_WINRT_MODE_NATIVE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW")
endif()
# Dependencies used by the implementation referenced
# below are not available on WinRT 8.0.
# Enabling it for WiRT 8.1+ only.
if(DEFINED WINRT AND NOT DEFINED WINRT_8_0 AND NOT DEFINED ENABLE_WINRT_MODE_NATIVE)
# WinRT detected. Adding WinRT API header
message(STATUS " ${name}: WinRT detected. Adding WinRT API header")
list(APPEND videoio_ext_hdrs "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/cap_winrt.hpp")
# Adding WinRT internal sources and headers
list(APPEND videoio_srcs
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt_capture.cpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt_bridge.cpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt_video.cpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt/CaptureFrameGrabber.cpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt/MediaStreamSink.cpp)
list(APPEND videoio_hdrs
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt_capture.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt_bridge.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt_video.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt/MFIncludes.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt/CaptureFrameGrabber.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt/MediaSink.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_winrt/MediaStreamSink.hpp)
endif()
include(${CMAKE_CURRENT_LIST_DIR}/cmake/plugin.cmake)
set(tgts "PRIVATE")
if(TARGET ocv.3rdparty.mediasdk)
if("mfx" IN_LIST VIDEOIO_PLUGIN_LIST OR VIDEOIO_PLUGIN_LIST STREQUAL "all")
ocv_create_builtin_videoio_plugin("opencv_videoio_intel_mfx" ocv.3rdparty.mediasdk "cap_mfx_common.cpp" "cap_mfx_reader.cpp" "cap_mfx_writer.cpp" "cap_mfx_plugin.cpp")
else()
list(APPEND videoio_srcs
${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_common.cpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_reader.cpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_writer.cpp)
list(APPEND videoio_hdrs
${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_common.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_reader.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_mfx_writer.hpp)
list(APPEND tgts ocv.3rdparty.mediasdk)
endif()
endif()
if(TARGET ocv.3rdparty.dshow)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_dshow.cpp)
list(APPEND videoio_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/cap_dshow.hpp)
list(APPEND tgts ocv.3rdparty.dshow)
endif()
if(TARGET ocv.3rdparty.msmf)
if("msmf" IN_LIST VIDEOIO_PLUGIN_LIST OR VIDEOIO_PLUGIN_LIST STREQUAL "all")
ocv_create_builtin_videoio_plugin("opencv_videoio_msmf" ocv.3rdparty.msmf "cap_msmf.cpp")
else()
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_msmf.hpp)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_msmf.cpp)
list(APPEND tgts ocv.3rdparty.msmf)
endif()
endif()
if(TARGET ocv.3rdparty.xine)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_xine.cpp)
list(APPEND tgts ocv.3rdparty.xine)
endif()
if(TARGET ocv.3rdparty.dc1394_2)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_dc1394_v2.cpp)
list(APPEND tgts ocv.3rdparty.dc1394_2)
endif()
if(TARGET ocv.3rdparty.gstreamer)
if("gstreamer" IN_LIST VIDEOIO_PLUGIN_LIST OR VIDEOIO_PLUGIN_LIST STREQUAL "all")
ocv_create_builtin_videoio_plugin("opencv_videoio_gstreamer" ocv.3rdparty.gstreamer "cap_gstreamer.cpp")
else()
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_gstreamer.cpp)
list(APPEND tgts ocv.3rdparty.gstreamer)
endif()
endif()
if(TARGET ocv.3rdparty.v4l)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_v4l.cpp)
list(APPEND tgts ocv.3rdparty.v4l)
endif()
if(TARGET ocv.3rdparty.openni2)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_openni2.cpp)
list(APPEND tgts ocv.3rdparty.openni2)
endif()
if(TARGET ocv.3rdparty.ximea)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_ximea.cpp)
list(APPEND tgts ocv.3rdparty.ximea)
endif()
if(TARGET ocv.3rdparty.ueye)
if("ueye" IN_LIST VIDEOIO_PLUGIN_LIST OR VIDEOIO_PLUGIN_LIST STREQUAL "all")
ocv_create_builtin_videoio_plugin("opencv_videoio_ueye" ocv.3rdparty.ueye "cap_ueye.cpp")
else()
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_ueye.cpp)
list(APPEND tgts ocv.3rdparty.ueye)
endif()
endif()
if(TARGET ocv.3rdparty.ffmpeg)
if(HAVE_FFMPEG_WRAPPER)
list(APPEND tgts ocv.3rdparty.ffmpeg)
elseif("ffmpeg" IN_LIST VIDEOIO_PLUGIN_LIST OR VIDEOIO_PLUGIN_LIST STREQUAL "all")
ocv_create_builtin_videoio_plugin("opencv_videoio_ffmpeg" ocv.3rdparty.ffmpeg "cap_ffmpeg.cpp")
if(TARGET ocv.3rdparty.ffmpeg.plugin_deps)
ocv_target_link_libraries(opencv_videoio_ffmpeg ocv.3rdparty.ffmpeg.plugin_deps)
endif()
if(TARGET ocv.3rdparty.mediasdk
AND NOT OPENCV_FFMPEG_DISABLE_MEDIASDK
)
ocv_target_link_libraries(opencv_videoio_ffmpeg ocv.3rdparty.mediasdk)
endif()
else()
list(APPEND videoio_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/cap_ffmpeg_impl.hpp)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_ffmpeg.cpp)
list(APPEND tgts ocv.3rdparty.ffmpeg)
if(TARGET ocv.3rdparty.ffmpeg.builtin_deps)
list(APPEND tgts ocv.3rdparty.ffmpeg.builtin_deps)
endif()
if(TARGET ocv.3rdparty.mediasdk
AND NOT OPENCV_FFMPEG_DISABLE_MEDIASDK
)
list(APPEND tgts ocv.3rdparty.mediasdk)
endif()
endif()
endif()
if(TARGET ocv.3rdparty.pvapi)
set(videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_pvapi.cpp ${videoio_srcs})
list(APPEND tgts ocv.3rdparty.pvapi)
endif()
if(TARGET ocv.3rdparty.aravis)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_aravis.cpp)
list(APPEND tgts ocv.3rdparty.aravis)
endif()
if(TARGET ocv.3rdparty.avfoundation)
if(IOS OR XROS)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_avfoundation.mm)
else()
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_avfoundation_mac.mm)
endif()
list(APPEND tgts ocv.3rdparty.avfoundation)
endif()
if(TARGET ocv.3rdparty.librealsense)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_librealsense.cpp)
list(APPEND tgts ocv.3rdparty.librealsense)
endif()
if(TARGET ocv.3rdparty.gphoto2)
list(APPEND videoio_srcs ${CMAKE_CURRENT_LIST_DIR}/src/cap_gphoto2.cpp)
list(APPEND tgts ocv.3rdparty.gphoto2)
endif()
if(TARGET ocv.3rdparty.cap_ios)
list(APPEND videoio_srcs
${CMAKE_CURRENT_LIST_DIR}/src/cap_ios_abstract_camera.mm
${CMAKE_CURRENT_LIST_DIR}/src/cap_ios_photo_camera.mm
${CMAKE_CURRENT_LIST_DIR}/src/cap_ios_video_camera.mm)
list(APPEND tgts ocv.3rdparty.cap_ios)
list(APPEND videoio_ext_hdrs "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/cap_ios.h")
endif()
if(TARGET ocv.3rdparty.android_mediandk)
list(APPEND videoio_srcs
${CMAKE_CURRENT_LIST_DIR}/src/cap_android_mediandk.cpp)
list(APPEND tgts ocv.3rdparty.android_mediandk)
endif()
if(TARGET ocv.3rdparty.android_native_camera)
list(APPEND videoio_srcs
${CMAKE_CURRENT_LIST_DIR}/src/cap_android_camera.cpp)
list(APPEND tgts ocv.3rdparty.android_native_camera)
endif()
if(TARGET ocv.3rdparty.obsensor)
if(OBSENSOR_USE_ORBBEC_SDK)
list(APPEND videoio_srcs
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor_liborbbec.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor_liborbbec.cpp)
else()
list(APPEND videoio_srcs
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor_capture.cpp)
list(APPEND videoio_hdrs
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor_capture.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor/obsensor_stream_channel_interface.hpp)
if(HAVE_OBSENSOR_MSMF)
list(APPEND videoio_srcs
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor/obsensor_uvc_stream_channel.cpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor/obsensor_stream_channel_msmf.cpp)
list(APPEND videoio_hdrs
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor/obsensor_uvc_stream_channel.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor/obsensor_stream_channel_msmf.hpp)
elseif(HAVE_OBSENSOR_V4L2)
list(APPEND videoio_srcs
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor/obsensor_uvc_stream_channel.cpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor/obsensor_stream_channel_v4l2.cpp)
list(APPEND videoio_hdrs
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor/obsensor_uvc_stream_channel.hpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_obsensor/obsensor_stream_channel_v4l2.hpp)
endif()
endif()
list(APPEND tgts ocv.3rdparty.obsensor)
endif()
if(tgts STREQUAL "PRIVATE")
set(tgts "")
endif()
ocv_install_used_external_targets(${tgts})
ocv_set_module_sources(HEADERS ${videoio_ext_hdrs} ${videoio_hdrs} SOURCES ${videoio_srcs})
ocv_module_include_directories()
ocv_create_module()
ocv_add_accuracy_tests(${tgts})
ocv_add_perf_tests(${tgts})
if(VIDEOIO_ENABLE_PLUGINS)
ocv_target_compile_definitions(${the_module} PRIVATE ENABLE_PLUGINS)
endif()
ocv_target_link_libraries(${the_module} LINK_PRIVATE ${tgts})
# copy FFmpeg dll to the output folder
if(WIN32 AND HAVE_FFMPEG_WRAPPER)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(FFMPEG_SUFFIX _64)
endif()
set(ffmpeg_dir "${OpenCV_BINARY_DIR}/3rdparty/ffmpeg")
set(ffmpeg_bare_name "opencv_videoio_ffmpeg${FFMPEG_SUFFIX}.dll")
set(ffmpeg_bare_name_ver "opencv_videoio_ffmpeg${OPENCV_DLLVERSION}${FFMPEG_SUFFIX}.dll")
set(ffmpeg_path "${ffmpeg_dir}/${ffmpeg_bare_name}")
if(MSVC_IDE)
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ffmpeg_path}" "${EXECUTABLE_OUTPUT_PATH}/Release/${ffmpeg_bare_name_ver}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ffmpeg_path}" "${EXECUTABLE_OUTPUT_PATH}/Debug/${ffmpeg_bare_name_ver}")
elseif(MSVC AND (CMAKE_GENERATOR MATCHES "Visual"))
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ffmpeg_path}" "${EXECUTABLE_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}/${ffmpeg_bare_name_ver}")
else()
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ffmpeg_path}" "${EXECUTABLE_OUTPUT_PATH}/${ffmpeg_bare_name_ver}")
endif()
install(FILES "${ffmpeg_path}" DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT libs RENAME "${ffmpeg_bare_name_ver}")
if(INSTALL_CREATE_DISTRIB)
install(FILES "${ffmpeg_dir}/opencv_videoio_ffmpeg${FFMPEG_SUFFIX}.dll" DESTINATION "bin/" COMPONENT libs RENAME "opencv_videoio_ffmpeg${OPENCV_DLLVERSION}${FFMPEG_SUFFIX}.dll")
endif()
endif()
@@ -0,0 +1,6 @@
# if(ANDROID AND ANDROID_NATIVE_API_LEVEL GREATER_EQUAL 24) <-- would be nicer but requires CMake 3.7 or later
if(ANDROID AND ANDROID_NATIVE_API_LEVEL GREATER 23)
set(HAVE_ANDROID_NATIVE_CAMERA TRUE)
set(libs "-landroid -llog -lcamera2ndk")
ocv_add_external_target(android_native_camera "" "${libs}" "HAVE_ANDROID_NATIVE_CAMERA")
endif()
@@ -0,0 +1,6 @@
# if(ANDROID AND ANDROID_NATIVE_API_LEVEL GREATER_EQUAL 21) <-- would be nicer but requires CMake 3.7 or later
if(ANDROID AND ANDROID_NATIVE_API_LEVEL GREATER 20)
set(HAVE_ANDROID_MEDIANDK TRUE)
set(libs "-landroid -llog -lmediandk")
ocv_add_external_target(android_mediandk "" "${libs}" "HAVE_ANDROID_MEDIANDK")
endif()
+30
View File
@@ -0,0 +1,30 @@
# --- Aravis SDK ---
if(NOT HAVE_ARAVIS_API AND PKG_CONFIG_FOUND)
ocv_check_modules(ARAVIS aravis-0.8 QUIET)
if(ARAVIS_FOUND)
set(HAVE_ARAVIS_API TRUE)
endif()
endif()
if(NOT HAVE_ARAVIS_API)
find_path(ARAVIS_INCLUDE "arv.h"
PATHS "${ARAVIS_ROOT}" ENV ARAVIS_ROOT
PATH_SUFFIXES "include/aravis-0.8")
find_library(ARAVIS_LIBRARY "aravis-0.8"
PATHS "${ARAVIS_ROOT}" ENV ARAVIS_ROOT
PATH_SUFFIXES "lib")
if(ARAVIS_INCLUDE AND ARAVIS_LIBRARY)
set(HAVE_ARAVIS_API TRUE)
file(STRINGS "${ARAVIS_INCLUDE}/arvversion.h" ver_strings REGEX "#define +ARAVIS_(MAJOR|MINOR|MICRO)_VERSION.*")
string(REGEX REPLACE ".*ARAVIS_MAJOR_VERSION[^0-9]+([0-9]+).*" "\\1" ver_major "${ver_strings}")
string(REGEX REPLACE ".*ARAVIS_MINOR_VERSION[^0-9]+([0-9]+).*" "\\1" ver_minor "${ver_strings}")
string(REGEX REPLACE ".*ARAVIS_MICRO_VERSION[^0-9]+([0-9]+).*" "\\1" ver_micro "${ver_strings}")
set(ARAVIS_VERSION "${ver_major}.${ver_minor}.${ver_micro}") # informational
set(ARAVIS_INCLUDE_DIRS "${ARAVIS_INCLUDE}")
set(ARAVIS_LIBRARIES "${ARAVIS_LIBRARY}")
endif()
endif()
if(HAVE_ARAVIS_API)
ocv_add_external_target(aravis "${ARAVIS_INCLUDE_DIRS}" "${ARAVIS_LIBRARIES}" "HAVE_ARAVIS_API")
endif()
@@ -0,0 +1,16 @@
if(APPLE)
set(HAVE_AVFOUNDATION TRUE)
if(IOS)
set(libs "-framework AVFoundation" "-framework QuartzCore")
else()
set(libs
"-framework Cocoa"
"-framework Accelerate"
"-framework AVFoundation"
"-framework CoreGraphics"
"-framework CoreMedia"
"-framework CoreVideo"
"-framework QuartzCore")
endif()
ocv_add_external_target(avfoundation "" "${libs}" "HAVE_AVFOUNDATION")
endif()
+28
View File
@@ -0,0 +1,28 @@
# --- Dc1394 ---
if(NOT HAVE_DC1394_2 AND PKG_CONFIG_FOUND)
ocv_check_modules(DC1394_2 libdc1394-2)
if(DC1394_2_FOUND)
set(HAVE_DC1394_2 TRUE)
endif()
endif()
if(NOT HAVE_DC1394_2)
find_path(DC1394_INCLUDE "dc1394/dc1394.h"
PATHS "${DC1394_ROOT}" ENV DC1394_ROOT
PATH_SUFFIXES "include"
NO_DEFAULT_PATH)
find_library(DC1394_LIBRARY "dc1394"
PATHS "${DC1394_ROOT}" ENV DC1394_ROOT
PATH_SUFFIXES "lib"
NO_DEFAULT_PATH)
if(DC1394_INCLUDE AND DC1394_LIBRARY)
set(HAVE_DC1394_2 TRUE)
set(DC1394_2_INCLUDE_DIRS "${DC1394_INCLUDE}")
set(DC1394_2_LIBRARIES "${DC1394_LIBRARY}")
set(DC1394_2_VERSION "unknown") # informational
endif()
endif()
if(HAVE_DC1394_2)
ocv_add_external_target(dc1394_2 "${DC1394_2_INCLUDE_DIRS}" "${DC1394_2_LIBRARIES}" "HAVE_DC1394_2")
endif()
+12
View File
@@ -0,0 +1,12 @@
# --- VideoInput/DirectShow ---
if(NOT HAVE_DSHOW AND MSVC AND NOT MSVC_VERSION LESS 1500)
set(HAVE_DSHOW TRUE)
endif()
if(NOT HAVE_DSHOW)
check_include_file(dshow.h HAVE_DSHOW)
endif()
if(HAVE_DSHOW)
ocv_add_external_target(dshow "" "" "HAVE_DSHOW")
endif()
+139
View File
@@ -0,0 +1,139 @@
# --- FFMPEG ---
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)
if(OPENCV_FFMPEG_USE_FIND_PACKAGE STREQUAL "1" OR OPENCV_FFMPEG_USE_FIND_PACKAGE STREQUAL "ON")
set(OPENCV_FFMPEG_USE_FIND_PACKAGE "FFMPEG")
endif()
find_package(${OPENCV_FFMPEG_USE_FIND_PACKAGE}) # Required components: AVCODEC AVFORMAT AVUTIL SWSCALE
if(FFMPEG_FOUND OR FFmpeg_FOUND)
set(HAVE_FFMPEG TRUE)
endif()
endif()
if(NOT HAVE_FFMPEG AND WIN32 AND NOT ARM AND NOT AARCH64 AND NOT OPENCV_FFMPEG_SKIP_DOWNLOAD)
include("${OpenCV_SOURCE_DIR}/3rdparty/ffmpeg/ffmpeg.cmake")
download_win_ffmpeg(FFMPEG_CMAKE_SCRIPT)
if(FFMPEG_CMAKE_SCRIPT)
include("${FFMPEG_CMAKE_SCRIPT}")
set(HAVE_FFMPEG TRUE)
set(HAVE_FFMPEG_WRAPPER TRUE)
endif()
endif()
set(_required_ffmpeg_libraries libavcodec libavformat libavutil libswscale)
set(_used_ffmpeg_libraries ${_required_ffmpeg_libraries})
if(NOT HAVE_FFMPEG AND PKG_CONFIG_FOUND)
ocv_check_modules(FFMPEG libavcodec libavformat libavutil libswscale)
if(FFMPEG_FOUND)
if(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE)
ocv_check_modules(FFMPEG_libavdevice libavdevice) # optional
if(FFMPEG_libavdevice_FOUND)
list(APPEND FFMPEG_LIBRARIES ${FFMPEG_libavdevice_LIBRARIES})
list(APPEND _used_ffmpeg_libraries libavdevice)
endif()
endif()
set(HAVE_FFMPEG TRUE)
else()
set(_missing_ffmpeg_libraries "")
foreach (ffmpeg_lib ${_required_ffmpeg_libraries})
if (NOT FFMPEG_${ffmpeg_lib}_FOUND)
list(APPEND _missing_ffmpeg_libraries ${ffmpeg_lib})
endif()
endforeach ()
message(STATUS "FFMPEG is disabled. Required libraries: ${_required_ffmpeg_libraries}."
" Missing libraries: ${_missing_ffmpeg_libraries}")
unset(_missing_ffmpeg_libraries)
endif()
endif()
#=================================
# Versions check.
if(HAVE_FFMPEG AND NOT HAVE_FFMPEG_WRAPPER)
set(_min_libavcodec_version 54.35.0)
set(_min_libavformat_version 54.20.4)
set(_min_libavutil_version 52.3.0)
set(_min_libswscale_version 2.1.1)
set(_min_libavdevice_version 53.2.0)
foreach(ffmpeg_lib ${_used_ffmpeg_libraries})
if(FFMPEG_${ffmpeg_lib}_VERSION VERSION_LESS _min_${ffmpeg_lib}_version)
message(STATUS "FFMPEG is disabled. Can't find suitable ${ffmpeg_lib} library"
" (minimal ${_min_${ffmpeg_lib}_version}, found ${FFMPEG_${ffmpeg_lib}_VERSION}).")
set(HAVE_FFMPEG FALSE)
endif()
endforeach()
if(NOT HAVE_FFMPEG)
message(STATUS "FFMPEG libraries version check failed "
"(minimal libav release 9.20, minimal FFMPEG release 1.1.16).")
endif()
unset(_min_libavcodec_version)
unset(_min_libavformat_version)
unset(_min_libavutil_version)
unset(_min_libswscale_version)
unset(_min_libavdevice_version)
endif()
#==================================
if(HAVE_FFMPEG AND NOT HAVE_FFMPEG_WRAPPER AND NOT OPENCV_FFMPEG_SKIP_BUILD_CHECK)
try_compile(__VALID_FFMPEG
"${OpenCV_BINARY_DIR}"
"${OpenCV_SOURCE_DIR}/cmake/checks/ffmpeg_test.cpp"
CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${FFMPEG_INCLUDE_DIRS}"
"-DLINK_LIBRARIES:STRING=${FFMPEG_LIBRARIES}"
OUTPUT_VARIABLE TRY_OUT
)
if(NOT __VALID_FFMPEG)
# message(FATAL_ERROR "FFMPEG: test check build log:\n${TRY_OUT}")
message(STATUS "WARNING: Can't build ffmpeg test code")
set(HAVE_FFMPEG FALSE)
endif()
endif()
#==================================
unset(_required_ffmpeg_libraries)
unset(_used_ffmpeg_libraries)
if(HAVE_FFMPEG_WRAPPER)
ocv_add_external_target(ffmpeg "" "" "HAVE_FFMPEG_WRAPPER")
elseif(HAVE_FFMPEG)
if(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE AND FFMPEG_libavdevice_FOUND)
set(HAVE_FFMPEG_LIBAVDEVICE TRUE)
ocv_add_external_target(ffmpeg "${FFMPEG_INCLUDE_DIRS}" "${FFMPEG_LIBRARIES}" "HAVE_FFMPEG;HAVE_FFMPEG_LIBAVDEVICE")
else()
ocv_add_external_target(ffmpeg "${FFMPEG_INCLUDE_DIRS}" "${FFMPEG_LIBRARIES}" "HAVE_FFMPEG")
endif()
set(__builtin_defines "")
set(__builtin_include_dirs "")
set(__builtin_libs "")
set(__plugin_defines "")
set(__plugin_include_dirs "")
set(__plugin_libs "")
if(HAVE_OPENCL)
set(__opencl_dirs "")
if(OPENCL_INCLUDE_DIRS)
set(__opencl_dirs "${OPENCL_INCLUDE_DIRS}")
elseif(OPENCL_INCLUDE_DIR)
set(__opencl_dirs "${OPENCL_INCLUDE_DIR}")
else()
set(__opencl_dirs "${OpenCV_SOURCE_DIR}/3rdparty/include/opencl/1.2")
endif()
# extra dependencies for buildin code (OpenCL dir is required for extensions like cl_d3d11.h)
# buildin HAVE_OPENCL is already defined through cvconfig.h
list(APPEND __builtin_include_dirs "${__opencl_dirs}")
# extra dependencies for
list(APPEND __plugin_defines "HAVE_OPENCL")
list(APPEND __plugin_include_dirs "${__opencl_dirs}")
endif()
# TODO: libva, d3d11
if(__builtin_include_dirs OR __builtin_include_defines OR __builtin_include_libs)
ocv_add_external_target(ffmpeg.builtin_deps "${__builtin_include_dirs}" "${__builtin_include_libs}" "${__builtin_defines}")
endif()
if(VIDEOIO_ENABLE_PLUGINS AND __plugin_include_dirs OR __plugin_include_defines OR __plugin_include_libs)
ocv_add_external_target(ffmpeg.plugin_deps "${__plugin_include_dirs}" "${__plugin_include_libs}" "${__plugin_defines}")
endif()
endif()
+11
View File
@@ -0,0 +1,11 @@
# --- gPhoto2 ---
if(NOT HAVE_GPHOTO2 AND PKG_CONFIG_FOUND)
ocv_check_modules(GPHOTO2 libgphoto2)
if(GPHOTO2_FOUND)
set(HAVE_GPHOTO2 TRUE)
endif()
endif()
if(HAVE_GPHOTO2)
ocv_add_external_target(gphoto2 "${GPHOTO2_INCLUDE_DIRS}" "${GPHOTO2_LIBRARIES}" "HAVE_GPHOTO2")
endif()
@@ -0,0 +1,113 @@
# --- GStreamer ---
if(NOT HAVE_GSTREAMER AND WIN32)
set(env_paths "${GSTREAMER_DIR}" ENV GSTREAMER_ROOT)
if(X86_64)
list(APPEND env_paths ENV GSTREAMER_1_0_ROOT_X86_64 ENV GSTREAMER_ROOT_X86_64)
else()
list(APPEND env_paths ENV GSTREAMER_1_0_ROOT_X86 ENV GSTREAMER_ROOT_X86)
endif()
find_path(GSTREAMER_gst_INCLUDE_DIR
gst/gst.h
PATHS ${env_paths}
PATH_SUFFIXES "include/gstreamer-1.0")
find_path(GSTREAMER_glib_INCLUDE_DIR
glib.h
PATHS ${env_paths}
PATH_SUFFIXES "include/glib-2.0")
find_path(GSTREAMER_glibconfig_INCLUDE_DIR
glibconfig.h
PATHS ${env_paths}
PATH_SUFFIXES "lib/glib-2.0/include")
find_library(GSTREAMER_gstreamer_LIBRARY
NAMES gstreamer gstreamer-1.0
PATHS ${env_paths}
PATH_SUFFIXES "lib")
find_library(GSTREAMER_app_LIBRARY
NAMES gstapp gstapp-1.0
PATHS ${env_paths}
PATH_SUFFIXES "lib")
find_library(GSTREAMER_base_LIBRARY
NAMES gstbase gstbase-1.0
PATHS ${env_paths}
PATH_SUFFIXES "lib")
find_library(GSTREAMER_pbutils_LIBRARY
NAMES gstpbutils gstpbutils-1.0
PATHS ${env_paths}
PATH_SUFFIXES "lib")
find_library(GSTREAMER_riff_LIBRARY
NAMES gstriff gstriff-1.0
PATHS ${env_paths}
PATH_SUFFIXES "lib")
find_library(GSTREAMER_video_LIBRARY
NAMES gstvideo gstvideo-1.0
PATHS ${env_paths}
PATH_SUFFIXES "lib")
find_library(GSTREAMER_audio_LIBRARY
NAMES gstaudio gstaudio-1.0
PATHS ${env_paths}
PATH_SUFFIXES "lib")
find_library(GSTREAMER_glib_LIBRARY
NAMES glib-2.0
PATHS ${env_paths}
PATH_SUFFIXES "lib")
find_library(GSTREAMER_gobject_LIBRARY
NAMES gobject-2.0
PATHS ${env_paths}
PATH_SUFFIXES "lib")
if(GSTREAMER_gst_INCLUDE_DIR
AND GSTREAMER_glib_INCLUDE_DIR
AND GSTREAMER_glibconfig_INCLUDE_DIR
AND GSTREAMER_gstreamer_LIBRARY
AND GSTREAMER_app_LIBRARY
AND GSTREAMER_base_LIBRARY
AND GSTREAMER_pbutils_LIBRARY
AND GSTREAMER_riff_LIBRARY
AND GSTREAMER_video_LIBRARY
AND GSTREAMER_audio_LIBRARY
AND GSTREAMER_glib_LIBRARY
AND GSTREAMER_gobject_LIBRARY)
file(STRINGS "${GSTREAMER_gst_INCLUDE_DIR}/gst/gstversion.h" ver_strings REGEX "#define +GST_VERSION_(MAJOR|MINOR|MICRO|NANO).*")
string(REGEX REPLACE ".*GST_VERSION_MAJOR[^0-9]+([0-9]+).*" "\\1" ver_major "${ver_strings}")
string(REGEX REPLACE ".*GST_VERSION_MINOR[^0-9]+([0-9]+).*" "\\1" ver_minor "${ver_strings}")
string(REGEX REPLACE ".*GST_VERSION_MICRO[^0-9]+([0-9]+).*" "\\1" ver_micro "${ver_strings}")
set(GSTREAMER_VERSION "${ver_major}.${ver_minor}.${ver_micro}") # informational
set(HAVE_GSTREAMER TRUE)
set(GSTREAMER_LIBRARIES
${GSTREAMER_gstreamer_LIBRARY}
${GSTREAMER_base_LIBRARY}
${GSTREAMER_app_LIBRARY}
${GSTREAMER_riff_LIBRARY}
${GSTREAMER_video_LIBRARY}
${GSTREAMER_audio_LIBRARY}
${GSTREAMER_pbutils_LIBRARY}
${GSTREAMER_glib_LIBRARY}
${GSTREAMER_gobject_LIBRARY})
set(GSTREAMER_INCLUDE_DIRS
${GSTREAMER_gst_INCLUDE_DIR}
${GSTREAMER_glib_INCLUDE_DIR}
${GSTREAMER_glibconfig_INCLUDE_DIR})
endif()
endif()
if(NOT HAVE_GSTREAMER AND PKG_CONFIG_FOUND)
ocv_check_modules(GSTREAMER_base gstreamer-base-1.0)
ocv_check_modules(GSTREAMER_app gstreamer-app-1.0)
ocv_check_modules(GSTREAMER_riff gstreamer-riff-1.0)
ocv_check_modules(GSTREAMER_pbutils gstreamer-pbutils-1.0)
ocv_check_modules(GSTREAMER_video gstreamer-video-1.0)
ocv_check_modules(GSTREAMER_audio gstreamer-audio-1.0)
if(GSTREAMER_base_FOUND AND GSTREAMER_app_FOUND AND GSTREAMER_riff_FOUND AND GSTREAMER_pbutils_FOUND AND GSTREAMER_video_FOUND AND GSTREAMER_audio_FOUND)
set(HAVE_GSTREAMER TRUE)
set(GSTREAMER_VERSION ${GSTREAMER_base_VERSION}) # informational
set(GSTREAMER_LIBRARIES ${GSTREAMER_base_LIBRARIES} ${GSTREAMER_app_LIBRARIES} ${GSTREAMER_riff_LIBRARIES} ${GSTREAMER_pbutils_LIBRARIES} ${GSTREAMER_video_LIBRARIES} ${GSTREAMER_audio_LIBRARIES})
set(GSTREAMER_INCLUDE_DIRS ${GSTREAMER_base_INCLUDE_DIRS} ${GSTREAMER_app_INCLUDE_DIRS} ${GSTREAMER_riff_INCLUDE_DIRS} ${GSTREAMER_pbutils_INCLUDE_DIRS} ${GSTREAMER_video_INCLUDE_DIRS} ${GSTREAMER_audio_INCLUDE_DIRS})
endif()
endif()
if(HAVE_GSTREAMER)
ocv_add_external_target(gstreamer "${GSTREAMER_INCLUDE_DIRS}" "${GSTREAMER_LIBRARIES}" "HAVE_GSTREAMER")
endif()
+13
View File
@@ -0,0 +1,13 @@
if(APPLE AND IOS)
set(HAVE_CAP_IOS TRUE)
set(libs
"-framework Accelerate"
"-framework AVFoundation"
"-framework CoreGraphics"
"-framework CoreImage"
"-framework CoreMedia"
"-framework CoreVideo"
"-framework QuartzCore"
"-framework UIKit")
ocv_add_external_target(cap_ios "" "${libs}" "HAVE_CAP_IOS")
endif()
+81
View File
@@ -0,0 +1,81 @@
set(MFX_DEFS "")
if(NOT HAVE_MFX)
find_package(VPL QUIET)
if(VPL_FOUND)
message(STATUS "VPL_VERSION: ${VPL_VERSION}")
# NOTE: oneVPL since 2021.4 have version 2.4, version 2021.1 does not work
if (VPL_VERSION VERSION_LESS "2021.2" AND VPL_VERSION VERSION_GREATER "2021.0")
message(STATUS "VPL version is too old (${VPL_DIR} : ${VPL_VERSION}) - skipping")
else()
set(MFX_INCLUDE_DIRS "")
set(MFX_LIBRARIES "${VPL_IMPORTED_TARGETS}")
set(HAVE_MFX TRUE)
list(APPEND MFX_DEFS "HAVE_ONEVPL")
endif()
endif()
endif()
set(paths)
if(NOT HAVE_MFX)
set(paths "${MFX_HOME}" ENV "MFX_HOME" ENV "INTELMEDIASDKROOT" ${paths})
if(MSVC)
if(MSVC_VERSION LESS 1900)
set(vs_suffix)
else()
set(vs_suffix "_vs2015")
endif()
if(X86_64)
set(vs_arch "x64")
else()
set(vs_arch "win32")
endif()
endif()
find_path(MFX_INCLUDE mfxdefs.h
PATHS ${paths}
PATH_SUFFIXES "include" "include/mfx"
NO_DEFAULT_PATH)
find_library(MFX_LIBRARY NAMES mfx libmfx${vs_suffix}
PATHS ${paths}
PATH_SUFFIXES "lib64" "lib/lin_x64" "lib/${vs_arch}" "lib"
NO_DEFAULT_PATH)
if(MFX_INCLUDE AND MFX_LIBRARY)
set(HAVE_MFX TRUE)
set(MFX_INCLUDE_DIRS "${MFX_INCLUDE}")
set(MFX_LIBRARIES "${MFX_LIBRARY}")
list(APPEND MFX_DEFS "HAVE_MFX_PLUGIN")
endif()
endif()
if(NOT HAVE_MFX AND PKG_CONFIG_FOUND)
ocv_check_modules(MFX mfx)
endif()
if(HAVE_MFX AND UNIX)
set(paths "${VA_ROOT_DIR}" ENV "VA_ROOT_DIR" ${paths})
foreach(mode NO_DEFAULT_PATH "")
find_path(MFX_va_INCLUDE va/va.h PATHS ${paths} PATH_SUFFIXES "include" ${mode})
find_library(MFX_va_LIBRARY va PATHS ${paths} PATH_SUFFIXES "lib64" "lib/lin_x64" "lib" ${mode})
find_library(MFX_va_drm_LIBRARY va-drm PATHS ${paths} PATH_SUFFIXES "lib64" "lib/lin_x64" "lib" ${mode})
if(MFX_va_INCLUDE AND MFX_va_LIBRARY AND MFX_va_drm_LIBRARY)
list(APPEND MFX_INCLUDE_DIRS "${MFX_va_INCLUDE}")
list(APPEND MFX_LIBRARIES "${MFX_va_LIBRARY}" "${MFX_va_drm_LIBRARY}")
# list(APPEND MFX_LIBRARIES "-Wl,--exclude-libs=libmfx")
break()
endif()
unset(MFX_va_INCLUDE CACHE)
unset(MFX_va_LIBRARY CACHE)
unset(MFX_va_drm_LIBRARY CACHE)
endforeach()
if(NOT(MFX_va_INCLUDE AND MFX_va_LIBRARY AND MFX_va_drm_LIBRARY))
message(STATUS "libva not found - turning MFX OFF")
set(HAVE_MFX FALSE)
endif()
endif()
if(HAVE_MFX)
list(APPEND MFX_DEFS "HAVE_MFX")
ocv_add_external_target(mediasdk "${MFX_INCLUDE_DIRS}" "${MFX_LIBRARIES}" "${MFX_DEFS}")
endif()
+22
View File
@@ -0,0 +1,22 @@
# --- VideoInput/Microsoft Media Foundation ---
if(NOT HAVE_MSMF)
check_include_file(mfapi.h HAVE_MFAPI)
if(HAVE_MFAPI)
set(HAVE_MSMF TRUE)
endif()
endif()
if(HAVE_MSMF)
if(WITH_MSMF_DXVA)
check_include_file(d3d11.h HAVE_D3D11)
check_include_file(d3d11_4.h HAVE_D3D11_4)
if(HAVE_D3D11 AND HAVE_D3D11_4)
set(HAVE_MSMF_DXVA TRUE)
endif()
endif()
set(defs "HAVE_MSMF")
if(HAVE_MSMF_DXVA)
list(APPEND defs "HAVE_MSMF_DXVA")
endif()
ocv_add_external_target(msmf "" "" "${defs}")
endif()
@@ -0,0 +1,53 @@
# --- obsensor ---
if(NOT HAVE_OBSENSOR)
if(OBSENSOR_USE_ORBBEC_SDK)
include("${OpenCV_SOURCE_DIR}/3rdparty/orbbecsdk/orbbecsdk.cmake")
download_orbbec_sdk(ORBBEC_SDK_ROOT_DIR)
message(STATUS "ORBBEC_SDK_ROOT_DIR: ${ORBBEC_SDK_ROOT_DIR}")
if(ORBBEC_SDK_ROOT_DIR)
set(OrbbecSDK_DIR "${ORBBEC_SDK_ROOT_DIR}")
find_package(OrbbecSDK REQUIRED)
message(STATUS "OrbbecSDK_FOUND: ${OrbbecSDK_FOUND}")
message(STATUS "OrbbecSDK_INCLUDE_DIRS: ${OrbbecSDK_INCLUDE_DIRS}")
if(OrbbecSDK_FOUND)
set(HAVE_OBSENSOR TRUE)
set(HAVE_OBSENSOR_ORBBEC_SDK TRUE)
ocv_add_external_target(obsensor "${OrbbecSDK_INCLUDE_DIRS}" "${OrbbecSDK_LIBRARY}" "HAVE_OBSENSOR;HAVE_OBSENSOR_ORBBEC_SDK")
file(COPY ${OrbbecSDK_DLL_FILES} DESTINATION ${CMAKE_BINARY_DIR}/lib)
if(NOT ORBBEC_SDK_VERSION STREQUAL "1")
# OrbbecSDK v2 loads some libraries at runtime.
file(COPY ${OrbbecSDK_RUNTIME_RESOURCE_FILES} DESTINATION ${CMAKE_BINARY_DIR}/lib)
install(DIRECTORY ${OrbbecSDK_RUNTIME_RESOURCE_FILES} DESTINATION ${OPENCV_LIB_INSTALL_PATH})
endif()
install(FILES ${OrbbecSDK_DLL_FILES} DESTINATION ${OPENCV_LIB_INSTALL_PATH})
ocv_install_3rdparty_licenses(OrbbecSDK ${OrbbecSDK_DIR}/LICENSE.txt)
endif()
endif()
else()
if(WIN32)
check_include_file(mfapi.h HAVE_MFAPI)
check_include_file(vidcap.h HAVE_VIDCAP)
if(HAVE_MFAPI AND HAVE_VIDCAP)
set(HAVE_OBSENSOR TRUE)
set(HAVE_OBSENSOR_MSMF TRUE)
ocv_add_external_target(obsensor "" "" "HAVE_OBSENSOR;HAVE_OBSENSOR_MSMF")
else()
set(HAVE_OBSENSOR OFF)
set(HAVE_OBSENSOR_MSMF OFF)
if(NOT HAVE_MFAPI)
MESSAGE(STATUS "Could not find mfapi.h. Turning HAVE_OBSENSOR OFF")
endif()
if(NOT HAVE_VIDCAP)
MESSAGE(STATUS "Could not find vidcap.h. Turning HAVE_OBSENSOR OFF")
endif()
endif()
elseif(UNIX)
check_include_file(linux/videodev2.h HAVE_CAMV4L2_OBSENSOR)
if(HAVE_CAMV4L2_OBSENSOR)
set(HAVE_OBSENSOR TRUE)
set(HAVE_OBSENSOR_V4L2 TRUE)
ocv_add_external_target(obsensor "" "" "HAVE_OBSENSOR;HAVE_OBSENSOR_V4L2")
endif()
endif()
endif()
endif()
@@ -0,0 +1,47 @@
# --- OpenNI2 ---
if(NOT HAVE_OPENNI2)
set(paths "${OPENNI2_DIR}")
if(MSVC AND X86_64)
list(APPEND paths ENV OPENNI2_INCLUDE64 ENV OPENNI2_LIB64 ENV OPENNI2_REDIST64)
else()
list(APPEND paths ENV OPENNI2_INCLUDE ENV OPENNI2_LIB ENV OPENNI2_REDIST)
endif()
# From SDK
find_path(OPENNI2_INCLUDE "OpenNI.h"
PATHS ${paths}
PATH_SUFFIXES "Include"
NO_DEFAULT_PATH)
find_library(OPENNI2_LIBRARY "OpenNI2"
PATHS ${paths}
PATH_SUFFIXES "Redist" "Lib"
NO_DEFAULT_PATH)
if(OPENNI2_LIBRARY AND OPENNI2_INCLUDE)
set(HAVE_OPENNI2 TRUE)
set(OPENNI2_INCLUDE_DIRS "${OPENNI2_INCLUDE}")
set(OPENNI2_LIBRARIES "${OPENNI2_LIBRARY}")
endif()
endif()
if(NOT HAVE_OPENNI2)
# From system
find_path(OPENNI2_SYS_INCLUDE "OpenNI.h" PATH_SUFFIXES "openni2" "ni2")
find_library(OPENNI2_SYS_LIBRARY "OpenNI2")
if(OPENNI2_SYS_LIBRARY AND OPENNI2_SYS_INCLUDE)
set(HAVE_OPENNI2 TRUE)
set(OPENNI2_INCLUDE_DIRS "${OPENNI2_SYS_INCLUDE}")
set(OPENNI2_LIBRARIES "${OPENNI2_SYS_LIBRARY}")
endif()
endif()
if(HAVE_OPENNI2)
file(STRINGS "${OPENNI2_INCLUDE_DIRS}/OniVersion.h" ver_strings REGEX "#define +ONI_VERSION_(MAJOR|MINOR|MAINTENANCE|BUILD).*")
string(REGEX REPLACE ".*ONI_VERSION_MAJOR[^0-9]+([0-9]+).*" "\\1" ver_major "${ver_strings}")
string(REGEX REPLACE ".*ONI_VERSION_MINOR[^0-9]+([0-9]+).*" "\\1" ver_minor "${ver_strings}")
string(REGEX REPLACE ".*ONI_VERSION_MAINTENANCE[^0-9]+([0-9]+).*" "\\1" ver_maint "${ver_strings}")
set(OPENNI2_VERSION "${ver_major}.${ver_minor}.${ver_maint}") # informational
ocv_add_external_target(openni2 "${OPENNI2_INCLUDE_DIRS}" "${OPENNI2_LIBRARIES}" "HAVE_OPENNI2")
endif()
+21
View File
@@ -0,0 +1,21 @@
# --- PvApi ---
if(NOT HAVE_PVAPI)
if(X86_64)
set(arch x64)
else()
set(arch x86)
endif()
find_path(PVAPI_INCLUDE "PvApi.h"
PATHS "${PVAPI_ROOT}" ENV PVAPI_ROOT
PATH_SUFFIXES "inc-pc")
find_library(PVAPI_LIBRARY "PvAPI"
PATHS "${PVAPI_ROOT}" ENV PVAPI_ROOT
PATH_SUFFIXES "bin-pc/${arch}/${gcc}")
if(PVAPI_INCLUDE AND PVAPI_LIBRARY)
set(HAVE_PVAPI TRUE)
endif()
endif()
if(HAVE_PVAPI)
ocv_add_external_target(pvapi "${PVAPI_INCLUDE}" "${PVAPI_LIBRARY}" "HAVE_PVAPI")
endif()
@@ -0,0 +1,28 @@
# --- Intel librealsense ---
if(NOT HAVE_LIBREALSENSE)
find_package(realsense2 QUIET)
if(realsense2_FOUND)
set(HAVE_LIBREALSENSE TRUE)
set(LIBREALSENSE_VERSION "${realsense2_VERSION}") # informational
ocv_add_external_target(librealsense "" "${realsense2_LIBRARY}" "HAVE_LIBREALSENSE")
endif()
endif()
if(NOT HAVE_LIBREALSENSE)
find_path(LIBREALSENSE_INCLUDE_DIR "librealsense2/rs.hpp"
PATHS "${LIBREALSENSE_INCLUDE}" ENV LIBREALSENSE_INCLUDE)
find_library(LIBREALSENSE_LIBRARIES "realsense2"
PATHS "${LIBREALSENSE_LIB}" ENV LIBREALSENSE_LIB)
if(LIBREALSENSE_INCLUDE_DIR AND LIBREALSENSE_LIBRARIES)
set(HAVE_LIBREALSENSE TRUE)
file(STRINGS "${LIBREALSENSE_INCLUDE_DIR}/librealsense2/rs.h" ver_strings REGEX "#define +RS2_API_(MAJOR|MINOR|PATCH|BUILD)_VERSION.*")
string(REGEX REPLACE ".*RS2_API_MAJOR_VERSION[^0-9]+([0-9]+).*" "\\1" ver_major "${ver_strings}")
string(REGEX REPLACE ".*RS2_API_MINOR_VERSION[^0-9]+([0-9]+).*" "\\1" ver_minor "${ver_strings}")
string(REGEX REPLACE ".*RS2_API_PATCH_VERSION[^0-9]+([0-9]+).*" "\\1" ver_patch "${ver_strings}")
set(LIBREALSENSE_VERSION "${ver_major}.${ver_minor}.${ver_patch}") # informational
ocv_add_external_target(librealsense "${LIBREALSENSE_INCLUDE_DIR}" "${LIBREALSENSE_LIBRARIES}" "HAVE_LIBREALSENSE")
endif()
endif()
set(HAVE_LIBREALSENSE ${HAVE_LIBREALSENSE} PARENT_SCOPE)
+23
View File
@@ -0,0 +1,23 @@
if(NOT HAVE_UEYE)
if(WIN32)
if(X86_64)
set(_WIN_LIB_SUFFIX "_64")
endif()
endif()
find_path(UEYE_INCLUDE "ueye.h"
PATHS "${UEYE_ROOT}" ENV UEYE_ROOT "/usr" "C:/Program Files/IDS/uEye/Develop"
HINTS "${regpath}"
PATH_SUFFIXES "include")
find_library(UEYE_LIBRARY ueye_api${_WIN_LIB_SUFFIX}
PATHS "${UEYE_ROOT}" ENV UEYE_ROOT "/usr" "C:/Program Files/IDS/uEye/Develop"
HINTS "${regpath}"
PATH_SUFFIXES "lib")
if(UEYE_INCLUDE AND UEYE_LIBRARY)
set(HAVE_UEYE TRUE)
endif()
endif()
unset(_WIN_LIB_SUFFIX)
if(HAVE_UEYE)
ocv_add_external_target(ueye "${UEYE_INCLUDE}" "${UEYE_LIBRARY}" "HAVE_UEYE")
endif()
+17
View File
@@ -0,0 +1,17 @@
# --- V4L ---
if(NOT HAVE_V4L)
set(CMAKE_REQUIRED_QUIET TRUE) # for check_include_file
check_include_file(linux/videodev2.h HAVE_CAMV4L2)
check_include_file(sys/videoio.h HAVE_VIDEOIO)
if(HAVE_CAMV4L2 OR HAVE_VIDEOIO)
set(HAVE_V4L TRUE)
set(defs)
if(HAVE_CAMV4L2)
list(APPEND defs "HAVE_CAMV4L2")
endif()
if(HAVE_VIDEOIO)
list(APPEND defs "HAVE_VIDEOIO")
endif()
ocv_add_external_target(v4l "" "" "${defs}")
endif()
endif()
+30
View File
@@ -0,0 +1,30 @@
if(NOT HAVE_XIMEA)
if(WIN32)
get_filename_component(regpath "[HKEY_CURRENT_USER\\Software\\XIMEA\\CamSupport\\API;Path]" ABSOLUTE)
if(NOT EXISTS ${regpath})
get_filename_component(regpath "[HKEY_LOCAL_MACHINE\\SOFTWARE\\XIMEA\\API_SoftwarePackage;Path]" ABSOLUTE)
endif()
endif()
if(X86_64)
set(lib_dir "API/x64" "API/64bit")
set(lib_suffix "64")
else()
set(lib_dir "API/x86" "API/32bit")
set(lib_suffix "32")
endif()
find_path(XIMEA_INCLUDE "xiApi.h"
PATHS "${XIMEA_ROOT}" ENV XIMEA_ROOT "/opt/XIMEA"
HINTS "${regpath}"
PATH_SUFFIXES "include" "API")
find_library(XIMEA_LIBRARY m3api xiapi${lib_suffix}
PATHS "${XIMEA_ROOT}" ENV XIMEA_ROOT "/opt/XIMEA"
HINTS "${regpath}"
PATH_SUFFIXES ${lib_dir})
if(XIMEA_INCLUDE AND XIMEA_LIBRARY)
set(HAVE_XIMEA TRUE)
endif()
endif()
if(HAVE_XIMEA)
ocv_add_external_target(ximea "${XIMEA_INCLUDE}" "${XIMEA_LIBRARY}" "HAVE_XIMEA")
endif()
+7
View File
@@ -0,0 +1,7 @@
if(NOT HAVE_XINE AND PKG_CONFIG_FOUND)
ocv_check_modules(XINE libxine QUIET)
endif()
if(HAVE_XINE)
ocv_add_external_target(xine "${XINE_INCLUDE_DIRS}" "${XINE_LIBRARIES}" "HAVE_XINE")
endif()
+34
View File
@@ -0,0 +1,34 @@
if(NOT PROJECT_NAME STREQUAL "OpenCV")
include(FindPkgConfig)
endif()
macro(add_backend backend_id cond_var)
if(${cond_var})
include("${CMAKE_CURRENT_LIST_DIR}/detect_${backend_id}.cmake")
endif()
endmacro()
add_backend("ffmpeg" WITH_FFMPEG)
add_backend("gstreamer" WITH_GSTREAMER)
add_backend("v4l" WITH_V4L)
add_backend("aravis" WITH_ARAVIS)
add_backend("dc1394" WITH_1394)
add_backend("gphoto" WITH_GPHOTO2)
add_backend("msdk" WITH_MFX)
add_backend("openni2" WITH_OPENNI2)
add_backend("pvapi" WITH_PVAPI)
add_backend("realsense" WITH_LIBREALSENSE)
add_backend("ueye" WITH_UEYE)
add_backend("ximea" WITH_XIMEA)
add_backend("xine" WITH_XINE)
add_backend("obsensor" WITH_OBSENSOR)
add_backend("avfoundation" WITH_AVFOUNDATION)
add_backend("ios" WITH_CAP_IOS)
add_backend("dshow" WITH_DSHOW)
add_backend("msmf" WITH_MSMF)
add_backend("android_mediandk" WITH_ANDROID_MEDIANDK)
add_backend("android_camera" WITH_ANDROID_NATIVE_CAMERA)
+56
View File
@@ -0,0 +1,56 @@
function(ocv_create_builtin_videoio_plugin name target)
ocv_debug_message("ocv_create_builtin_videoio_plugin(${ARGV})")
if(NOT TARGET ${target})
message(FATAL_ERROR "${target} does not exist!")
endif()
if(NOT OpenCV_SOURCE_DIR)
message(FATAL_ERROR "OpenCV_SOURCE_DIR must be set to build the plugin!")
endif()
message(STATUS "Video I/O: add builtin plugin '${name}'")
foreach(src ${ARGN})
list(APPEND sources "${CMAKE_CURRENT_LIST_DIR}/src/${src}")
endforeach()
add_library(${name} MODULE ${sources})
target_include_directories(${name} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
target_compile_definitions(${name} PRIVATE BUILD_PLUGIN)
target_link_libraries(${name} PRIVATE ${target})
foreach(mod opencv_videoio opencv_core opencv_imgproc opencv_imgcodecs)
ocv_target_link_libraries(${name} LINK_PRIVATE ${mod})
ocv_target_include_directories(${name} "${OPENCV_MODULE_${mod}_LOCATION}/include")
endforeach()
if(WIN32)
set(OPENCV_PLUGIN_VERSION "${OPENCV_DLLVERSION}" CACHE STRING "")
if(CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
set(OPENCV_PLUGIN_ARCH "_64" CACHE STRING "")
else()
set(OPENCV_PLUGIN_ARCH "" CACHE STRING "")
endif()
else()
set(OPENCV_PLUGIN_VERSION "" CACHE STRING "")
set(OPENCV_PLUGIN_ARCH "" CACHE STRING "")
endif()
set_target_properties(${name} PROPERTIES
CXX_STANDARD 11
CXX_VISIBILITY_PRESET hidden
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
OUTPUT_NAME "${name}${OPENCV_PLUGIN_VERSION}${OPENCV_PLUGIN_ARCH}"
)
if(WIN32)
set_target_properties(${name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
install(TARGETS ${name} OPTIONAL LIBRARY DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT plugins)
else()
install(TARGETS ${name} OPTIONAL LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT plugins)
endif()
add_dependencies(opencv_videoio_plugins ${name})
endfunction()
@@ -0,0 +1,877 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="559.99994"
height="520"
viewBox="0 0 559.99993 520"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="opencv-videoio-structure.svg"
inkscape:export-filename="./opencv-videoio-structure.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.3701928"
inkscape:cx="397.12938"
inkscape:cy="243.08432"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-page="false"
inkscape:snap-bbox="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="false"
inkscape:bbox-paths="false"
inkscape:object-nodes="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-bbox-midpoints="false"
inkscape:snap-center="true"
inkscape:snap-global="true"
inkscape:window-width="1920"
inkscape:window-height="1028"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:snap-nodes="false"
inkscape:snap-others="false"
inkscape:snap-grids="true"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10"
units="px">
<inkscape:grid
originy="-179.99988"
originx="-1620"
visible="true"
empspacing="1"
spacingy="10"
spacingx="9.9999999"
dotted="false"
id="grid6826"
type="xygrid" />
</sodipodi:namedview>
<title
id="title7860">OpenCV video I/O Structure</title>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>OpenCV video I/O Structure</dc:title>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by/3.0/" />
<dc:creator>
<cc:Agent>
<dc:title>PkLab.net</dc:title>
</cc:Agent>
</dc:creator>
<dc:language>English</dc:language>
<dc:subject>
<rdf:Bag>
<rdf:li>OpenCV</rdf:li>
<rdf:li>Video I/O</rdf:li>
</rdf:Bag>
</dc:subject>
<dc:description />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by/3.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<g
transform="translate(-1620.0001,-352.36227)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<rect
ry="10"
rx="10"
y="372.86224"
x="1640.5001"
height="79"
width="518.99982"
id="rect4136-9-7"
style="fill:#9aba59;fill-opacity:1;stroke:#6f00c7;stroke-width:1.00000012;stroke-miterlimit:4;stroke-dasharray:1.00000003, 1.00000003;stroke-dashoffset:0;stroke-opacity:1" />
<rect
ry="10"
rx="10"
y="402.86224"
x="1820.5001"
height="39"
width="99"
id="rect4615-2-7-5-2-1-4-4-6"
style="fill:#809f41;fill-opacity:1;stroke:#000000;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:2.99999982, 2.99999982;stroke-dashoffset:0;stroke-opacity:1" />
<rect
ry="10"
rx="10"
y="772.86224"
x="1640.5001"
height="79"
width="519"
id="rect4136-8-5-3-3-3"
style="fill:#cccccc;fill-opacity:1;stroke:#6f00c7;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:1.00000001, 1.00000001;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text5457-0-7-9"
y="792.29303"
x="1899.3917"
style="font-style:normal;font-weight:normal;font-size:15px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
style="font-size:15px"
y="792.29303"
x="1899.3917"
id="tspan5459-7-2-3"
sodipodi:role="line">MEDIA DEVICES</tspan></text>
<rect
ry="10"
rx="10"
y="682.86224"
x="1640.5001"
height="79"
width="519"
id="rect4136-8-5-3-8"
style="fill:#ffb380;fill-opacity:1;stroke:#6f00c7;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:1.00000003, 1.00000003;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text5457-0-6"
y="702.27844"
x="1899.941"
style="font-style:normal;font-weight:normal;font-size:15px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
style="font-size:15px"
y="702.27844"
x="1899.941"
id="tspan5459-7-3"
sodipodi:role="line">SYSTEM</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="1330.3986"
y="308.75192"
id="text5748"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5750"
x="1330.3986"
y="308.75192" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="1330.3986"
y="308.75192"
id="text5752"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5754"
x="1330.3986"
y="308.75192" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="1490.257"
y="308.75192"
id="text5748-3"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5750-9"
x="1490.257"
y="308.75192" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="1490.257"
y="308.75192"
id="text5752-1"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5754-8"
x="1490.257"
y="308.75192" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="1170.5402"
y="308.75192"
id="text5748-3-9"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5750-9-4"
x="1170.5402"
y="308.75192" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="1170.5402"
y="308.75192"
id="text5752-1-5"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5754-8-4"
x="1170.5402"
y="308.75192" /></text>
<text
transform="scale(0.96032163,1.0413178)"
sodipodi:linespacing="125%"
id="text4165-3-0"
y="376.56689"
x="1978.5697"
style="font-style:normal;font-weight:normal;font-size:15px;line-height:125%;font-family:Verdana;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="376.56689"
x="1978.5697"
id="tspan4167-5-0"
sodipodi:role="line"
style="font-size:15px;text-align:center;text-anchor:middle">USER APPLICATION</tspan></text>
<rect
ry="10"
rx="10"
y="462.86221"
x="1820.5001"
height="209"
width="339"
id="rect4136-8-4-82-8"
style="fill:#80b3ff;fill-opacity:1;stroke:#6f00c7;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:0.99999996, 0.99999996;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4577-6-1"
y="478.42764"
x="2061.6013"
style="font-style:normal;font-weight:normal;font-size:15px;line-height:125%;font-family:Verdana;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
style="font-size:15px"
id="tspan4581-0-0"
y="478.42764"
x="2061.6013"
sodipodi:role="line">OpenCV Video I/O</tspan></text>
<path
inkscape:connector-curvature="0"
id="path4789-4-4"
d="m 1820.0815,567.77306 340,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.99999991, 0.99999995;stroke-dashoffset:0;stroke-opacity:1" />
<text
transform="scale(1.0001622,0.99983783)"
sodipodi:linespacing="125%"
id="text4577-9-3-2"
y="581.95209"
x="1990.1963"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan4581-2-5-1"
y="581.95209"
x="1990.1963"
sodipodi:role="line">OpenCV Video I/O API Backends</tspan></text>
<rect
ry="5"
rx="5"
y="592.86224"
x="1913.8334"
height="29.000002"
width="69"
id="rect4615-4-6-4"
style="fill:#999999;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-9-4"
y="612.11469"
x="1947.6696"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="612.11469"
x="1947.6696"
id="tspan4365-0-2"
sodipodi:role="line">DShow</tspan></text>
<rect
ry="5"
rx="5"
y="592.86224"
x="2080.5"
height="29.000002"
width="69"
id="rect4615-5-7-0"
style="fill:#999999;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-6-1-4"
y="612.00482"
x="2114.123"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="612.00482"
x="2114.123"
id="tspan4365-8-0-4"
sodipodi:role="line">MSMF</tspan></text>
<rect
ry="5"
rx="5"
y="592.86224"
x="1830.5001"
height="29"
width="69"
id="rect4615-11-0"
style="fill:#999999;fill-opacity:1;stroke:#000000;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
transform="scale(1.0001622,0.99983783)"
sodipodi:linespacing="125%"
id="text4363-5-6-4"
y="611.99872"
x="1864.5328"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="611.99872"
x="1864.5328"
id="tspan4365-6-9-6"
sodipodi:role="line">FFMPEG</tspan></text>
<rect
ry="5"
rx="5"
y="632.79645"
x="1913.8334"
height="29"
width="69"
id="rect4615-94-7-1"
style="fill:#999999;fill-opacity:1;stroke:#000000;stroke-width:0.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-4-8-8"
y="651.9455"
x="1947.932"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="651.9455"
x="1947.932"
id="tspan4365-7-09-1"
sodipodi:role="line">V4L</tspan></text>
<rect
ry="5"
rx="5"
y="592.86224"
x="1997.1666"
height="29.000002"
width="69"
id="rect4615-9-6-4"
style="fill:#999999;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-7-5-3"
y="612.00482"
x="2031.5355"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="612.00482"
x="2031.5355"
id="tspan4365-9-0-9"
sodipodi:role="line">VFW</tspan></text>
<rect
ry="5"
rx="5"
y="632.79645"
x="1830.5001"
height="29"
width="69"
id="rect4615-0-7-6"
style="fill:#999999;fill-opacity:1;stroke:#000000;stroke-width:0.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-4-6-1-7"
y="651.22223"
x="1864.9784"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="651.22223"
x="1864.9784"
id="tspan4365-7-0-4-1"
sodipodi:role="line">AVF/IOS</tspan></text>
<rect
ry="5"
rx="5"
y="632.79645"
x="2080.5"
height="29"
width="69"
id="rect4615-1-2-1"
style="fill:#999999;fill-opacity:1;stroke:#000000;stroke-width:0.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-4-6-7-6-2"
y="651.71051"
x="2115.1025"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="651.71051"
x="2115.1025"
id="tspan4365-7-0-1-8-6"
sodipodi:role="line">etc...</tspan></text>
<rect
ry="5"
rx="5"
y="632.79645"
x="1997.1666"
height="29"
width="69"
id="rect4615-6-6-4"
style="fill:#999999;fill-opacity:1;stroke:#000000;stroke-width:0.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4409-2-7"
y="651.9455"
x="2031.4104"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="651.9455"
x="2031.4104"
id="tspan4411-9-8"
sodipodi:role="line">OPENNI</tspan></text>
<rect
ry="10"
rx="10"
y="462.86221"
x="1640.5001"
height="209"
width="169"
id="rect4136-8-4-8-3-9"
style="fill:#e9afaf;fill-opacity:1;stroke:#6f00c7;stroke-width:0.99999964;stroke-miterlimit:4;stroke-dasharray:1.00000002, 1.00000002;stroke-dashoffset:0;stroke-opacity:1" />
<text
transform="scale(1.0001622,0.99983783)"
sodipodi:linespacing="125%"
id="text4577-8-4-3"
y="479.85953"
x="1723.9659"
style="font-style:normal;font-weight:normal;font-size:15px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-size:15px;text-align:center;text-anchor:middle"
id="tspan4581-5-1-7"
y="479.85953"
x="1723.9659"
sodipodi:role="line">Manufacturer Driver</tspan></text>
<rect
ry="10"
rx="10"
y="557.86224"
x="1650.5001"
height="39"
width="149"
id="rect4615-2-7-1-9"
style="fill:#d35f5f;fill-opacity:1;stroke:#000000;stroke-width:0.99999982;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-8-9"
y="581.27667"
x="1724.7936"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="581.27667"
x="1724.7936"
id="tspan4365-6-3-6-7-2"
sodipodi:role="line">C / C++ / JAVA API</tspan></text>
<rect
ry="10"
rx="10"
y="402.86224"
x="1665.5001"
height="39"
width="119"
id="rect4615-2-7-5-2-0"
style="fill:#809f41;fill-opacity:1;stroke:#000000;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:2.99999982, 2.99999982;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-7-3-4"
y="419.3027"
x="1724.9401"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="419.3027"
x="1724.9401"
id="tspan4365-6-3-6-2-2-2"
sodipodi:role="line"
style="font-size:12.5px;text-align:center;text-anchor:middle">cv::Mat from</tspan><tspan
y="434.9277"
x="1724.9401"
sodipodi:role="line"
id="tspan5401-7-0"
style="font-size:12.5px;text-align:center;text-anchor:middle">buffer</tspan></text>
<rect
ry="10"
rx="10"
y="712.86224"
x="1650.5001"
height="39"
width="149"
id="rect4615-2-7-0-6-8"
style="fill:#d35f5f;fill-opacity:1;stroke:#000000;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-6-5-7"
y="728.19025"
x="1724.0917"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="728.19025"
x="1724.0917"
id="tspan4365-6-3-6-6-1-0"
sodipodi:role="line"
style="font-size:12.5px;text-align:center;text-anchor:middle">Manufacturer</tspan><tspan
y="743.81525"
x="1724.0917"
sodipodi:role="line"
id="tspan5372-4-1">Library</tspan></text>
<rect
ry="10"
rx="10"
y="712.86224"
x="1825.5001"
height="39"
width="99"
id="rect4615-2-7-0-2-8-7"
style="fill:#c87137;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-6-7-2-5"
y="729.32245"
x="1874.3451"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="729.32245"
x="1874.3451"
id="tspan4365-6-3-6-6-8-7-6"
sodipodi:role="line"
style="font-size:12.5px;text-align:center;text-anchor:middle;fill:#ffffff">Backends</tspan><tspan
y="744.94745"
x="1874.3451"
sodipodi:role="line"
id="tspan5372-3-26-8">Libraries</tspan></text>
<rect
ry="10"
rx="10"
y="712.86224"
x="2050.5"
height="39"
width="99.000038"
id="rect4615-2-7-0-2-5-1-6"
style="fill:#c87137;fill-opacity:1;stroke:#000000;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-6-7-1-6-4"
y="729.21259"
x="2099.3086"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="729.21259"
x="2099.3086"
id="tspan4365-6-3-6-6-8-6-5-6"
sodipodi:role="line"
style="font-size:12.5px;text-align:center;text-anchor:middle;fill:#ffffff">O.S.</tspan><tspan
y="744.83759"
x="2099.3086"
sodipodi:role="line"
id="tspan5372-3-2-4-8">Libraries</tspan></text>
<rect
ry="10"
rx="10"
y="402.86224"
x="1935.5"
height="39"
width="99"
id="rect4615-2-7-5-2-1-2"
style="fill:#809f41;fill-opacity:1;stroke:#000000;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:2.99999982, 2.99999982;stroke-dashoffset:0;stroke-opacity:1" />
<rect
ry="10"
rx="10"
y="402.86224"
x="2050.5"
height="39"
width="99"
id="rect4615-2-7-5-2-1-4-8"
style="fill:#809f41;fill-opacity:1;stroke:#000000;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:2.99999982, 2.99999982;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-7-3-0-1-3"
y="423.06659"
x="1961.3533"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0118989,0.98824102)"><tspan
y="423.06659"
x="1961.3533"
id="tspan4365-6-3-6-2-2-1-7-5"
sodipodi:role="line"
style="font-size:12.5px;text-align:center;text-anchor:middle">set / get</tspan><tspan
y="438.69159"
x="1961.3533"
sodipodi:role="line"
id="tspan5401-7-6-4-7"
style="font-size:12.5px;text-align:center;text-anchor:middle">properties</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-7-3-0-1-4-5"
y="419.27219"
x="2099.635"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="419.27219"
x="2099.635"
id="tspan4365-6-3-6-2-2-1-7-4-3"
sodipodi:role="line"
style="font-size:12.5px;text-align:center;text-anchor:middle">grab / write</tspan><tspan
y="434.89719"
x="2099.635"
sodipodi:role="line"
id="tspan5401-7-6-4-3-2"
style="font-size:12.5px;text-align:center;text-anchor:middle">frame</tspan></text>
<rect
ry="10"
rx="10"
y="802.86224"
x="1650.5001"
height="39"
width="149"
id="rect4615-2-7-0-6-6-5"
style="fill:#4d4d4d;fill-opacity:1;stroke:#000000;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-6-5-2-08"
y="827.02747"
x="1724.9156"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="827.02747"
x="1724.9156"
sodipodi:role="line"
id="tspan5372-4-2-73">Camera</tspan></text>
<rect
ry="10"
rx="10"
y="802.86224"
x="1825.5001"
height="39"
width="149"
id="rect4615-2-7-0-6-6-4-6"
style="fill:#4d4d4d;fill-opacity:1;stroke:#000000;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-6-5-2-0-1"
y="827.14954"
x="1899.9178"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="827.14954"
x="1899.9178"
sodipodi:role="line"
id="tspan5372-4-2-7-0">Video File</tspan></text>
<rect
ry="10"
rx="10"
y="802.86224"
x="2000.5001"
height="39"
width="149"
id="rect4615-2-7-0-6-6-6-6"
style="fill:#4d4d4d;fill-opacity:1;stroke:#000000;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-6-5-2-03-1"
y="827.14954"
x="2074.5935"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="827.14954"
x="2074.5935"
sodipodi:role="line"
id="tspan5372-4-2-6-7">Network Stream</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect8223"
width="538.99994"
height="499.00003"
x="1630.5001"
y="362.86227"
rx="10"
ry="10" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-7-3-0-2"
y="419.27219"
x="1869.9166"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="419.27219"
x="1869.9166"
id="tspan4365-6-3-6-2-2-1-2"
sodipodi:role="line"
style="font-size:12.5px;text-align:center;text-anchor:middle">create / open</tspan><tspan
y="434.89719"
x="1869.9166"
sodipodi:role="line"
id="tspan5401-7-6-8"
style="font-size:12.5px;text-align:center;text-anchor:middle">device</tspan></text>
<rect
ry="10"
rx="10"
y="712.86224"
x="1938"
height="39"
width="99"
id="rect4615-2-7-0-2-8-7-8"
style="fill:#c87137;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-2-6-7-2-5-2"
y="729.32245"
x="1986.8268"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="729.32245"
x="1986.8268"
sodipodi:role="line"
id="tspan5372-3-26-8-5">CODECS</tspan><tspan
y="744.94745"
x="1986.8268"
sodipodi:role="line"
id="tspan4295">(fourcc)</tspan></text>
<rect
ry="10"
rx="9.3800001"
y="507.86713"
x="1841.6537"
height="43.995113"
width="90.999962"
id="rect4615-2-8-4-2"
style="fill:#6b98c9;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0;stroke-opacity:1" />
<rect
ry="10"
rx="9.3800011"
y="507.86713"
x="1945.6538"
height="43.995113"
width="198.99985"
id="rect4615-2-8-4-2-4"
style="fill:#6b98c9;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0;stroke-opacity:1" />
<rect
ry="10"
rx="9.3800001"
y="483.86713"
x="1833.6537"
height="29"
width="149"
id="rect4615-2-8-4"
style="fill:#4d82be;fill-opacity:1;stroke:#000000;stroke-width:0.99999988;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-3-3"
y="501.93918"
x="1908.0701"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="501.93918"
x="1908.0701"
id="tspan4365-6-3-90-7"
sodipodi:role="line">VideoCapture</tspan></text>
<rect
ry="10"
rx="9.3800001"
y="483.86713"
x="2003.6537"
height="29"
width="149"
id="rect4615-2-9-5-7"
style="fill:#4d82be;fill-opacity:1;stroke:#000000;stroke-width:0.99999982;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text4363-5-2-8-1-8"
y="503.1019"
x="2077.719"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="503.1019"
x="2077.719"
id="tspan4365-6-3-9-0-6"
sodipodi:role="line">VideoWriter</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4363-5-2-3-3-4"
y="534.48248"
x="1887.043"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="534.48248"
x="1887.043"
id="tspan4365-6-3-90-7-2"
sodipodi:role="line">Camera</tspan></text>
<text
sodipodi:linespacing="125%"
id="text4363-5-2-3-3-6"
y="526.79205"
x="2044.752"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Verdana;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="scale(1.0001622,0.99983783)"><tspan
y="526.79205"
x="2044.752"
sodipodi:role="line"
id="tspan4367">File or URL stream</tspan><tspan
y="542.41705"
x="2044.752"
sodipodi:role="line"
id="tspan4371">+ fourcc codec</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 37 KiB

@@ -0,0 +1,101 @@
Video I/O with OpenCV Overview {#videoio_overview}
==============================
@tableofcontents
@sa
- @ref videoio "Video I/O Code Reference"
- Tutorials: @ref tutorial_table_of_content_app
General Information
-------------------
The OpenCV @ref videoio module is a set of classes and functions to read and write video or images sequences.
Basically, the module provides the cv::VideoCapture and cv::VideoWriter classes as 2-layer interface to many video
I/O APIs used as backend.
![Video I/O with OpenCV](pics/videoio_overview.svg)
Some backends such as Direct Show (DSHOW), Microsoft Media Foundation (MSMF),
Video 4 Linux (V4L), etc... are interfaces to the video I/O library provided by the operating system.
Some other backends like OpenNI2 for Kinect, Intel Perceptual Computing SDK, GStreamer,
XIMEA Camera API, etc... are interfaces to proprietary drivers or to external library.
See the list of supported backends here: cv::VideoCaptureAPIs
@warning Some backends are experimental use them at your own risk
@note Each backend supports devices properties (cv::VideoCaptureProperties) in a different way or might not support any property at all.
Select the backend at runtime
-----------------------------
OpenCV automatically selects and uses first available backend (`apiPreference=cv::CAP_ANY`).
As advanced usage you can select the backend to use at runtime.
For example to grab from default camera using Microsoft Media Foundation (MSMF) as backend
```cpp
//declare a capture object
cv::VideoCapture cap(0, cv::CAP_MSMF);
//or specify the apiPreference with open
cap.open(0, cv::CAP_MSMF);
```
If you want to grab from a file using the Microsoft Media Foundation (MSMF) as backend:
```cpp
//declare a capture object
cv::VideoCapture cap(filename, cv::CAP_MSMF);
//or specify the apiPreference with open
cap.open(filename, cv::CAP_MSMF);
```
@sa cv::VideoCapture::open() , cv::VideoCapture::VideoCapture()
How to enable backends
----------------------
There are two kinds of videoio backends: built-in backends and plugins which will be loaded at runtime (since OpenCV 4.1.0). Use functions cv::videoio_registry::getBackends, cv::videoio_registry::hasBackend and cv::videoio_registry::getBackendName to check actual presence of backend during runtime.
To enable built-in videoio backends:
1. Enable corresponding CMake option, e.g. `-DWITH_GSTREAMER=ON`
2. Rebuild OpenCV
To enable dynamically-loaded videoio backend (currently supported: GStreamer and FFmpeg on Linux, MediaSDK on Linux and Windows):
1. Enable backend and add it to the list of plugins: `-DWITH_GSTREAMER=ON -DVIDEOIO_PLUGIN_LIST=gstreamer` CMake options
2. Rebuild OpenCV
3. Check that `libopencv_videoio_gstreamer.so` library exists in the `lib` directory
@note Don't forget to clean CMake cache when switching between these two modes
Use 3rd party drivers or cameras
--------------------------------
Many industrial cameras or some video I/O devices don't provide standard driver interfaces
for the operating system. Thus you can't use VideoCapture or VideoWriter with these devices.
To get access to their devices, manufacturers provide their own C++ API and library that you have to
include and link with your OpenCV application.
It is a common case that these libraries read/write images from/to a memory buffer. If so, it is possible to make a `Mat` header for memory buffer (user-allocated data) and process it
in-place using OpenCV functions. See cv::Mat::Mat() for more details.
The FFmpeg library
------------------
OpenCV can use the FFmpeg library (http://ffmpeg.org/) as backend to record, convert and stream audio and video.
FFmpeg is a complete, cross-reference solution. If you enable FFmpeg while configuring OpenCV then
CMake will download and install the binaries in `OPENCV_SOURCE_CODE/3rdparty/ffmpeg/`. To use
FFmpeg at runtime, you must deploy the FFmpeg binaries with your application.
@note FFmpeg is licensed under the GNU Lesser General Public License (LGPL) version 2.1 or later.
See `OPENCV_SOURCE_CODE/3rdparty/ffmpeg/readme.txt` and http://ffmpeg.org/legal.html for details and
licensing information
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,150 @@
/* For iOS video I/O
* by Eduard Feicho on 29/07/12
* Copyright 2012. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import <UIKit/UIKit.h>
#import <Accelerate/Accelerate.h>
#import <AVFoundation/AVFoundation.h>
#import <ImageIO/ImageIO.h>
#include "opencv2/core.hpp"
//! @addtogroup videoio_ios
//! @{
/////////////////////////////////////// CvAbstractCamera /////////////////////////////////////
@class CvAbstractCamera;
CV_EXPORTS @interface CvAbstractCamera : NSObject
{
UIDeviceOrientation currentDeviceOrientation;
BOOL cameraAvailable;
}
@property (nonatomic, strong) AVCaptureSession* captureSession;
@property (nonatomic, strong) AVCaptureConnection* videoCaptureConnection;
@property (nonatomic, readonly) BOOL running;
@property (nonatomic, readonly) BOOL captureSessionLoaded;
@property (nonatomic, assign) int defaultFPS;
@property (nonatomic, readonly) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;
@property (nonatomic, assign) AVCaptureDevicePosition defaultAVCaptureDevicePosition;
@property (nonatomic, assign) AVCaptureVideoOrientation defaultAVCaptureVideoOrientation;
@property (nonatomic, assign) BOOL useAVCaptureVideoPreviewLayer;
@property (nonatomic, strong) NSString *const defaultAVCaptureSessionPreset;
@property (nonatomic, assign) int imageWidth;
@property (nonatomic, assign) int imageHeight;
@property (nonatomic, strong) UIView* parentView;
- CV_UNUSED(start);
- CV_UNUSED(stop);
- CV_UNUSED(switchCameras);
- (id)initWithParentView:(UIView*)parent;
- CV_UNUSED(createCaptureOutput);
- CV_UNUSED(createVideoPreviewLayer);
- CV_UNUSED(updateOrientation);
- CV_UNUSED(lockFocus);
- CV_UNUSED(unlockFocus);
- CV_UNUSED(lockExposure);
- CV_UNUSED(unlockExposure);
- CV_UNUSED(lockBalance);
- CV_UNUSED(unlockBalance);
@end
///////////////////////////////// CvVideoCamera ///////////////////////////////////////////
@class CvVideoCamera;
CV_EXPORTS @protocol CvVideoCameraDelegate <NSObject>
#ifdef __cplusplus
// delegate method for processing image frames
- (void)processImage:(cv::Mat&)image;
#endif
@end
CV_EXPORTS @interface CvVideoCamera : CvAbstractCamera<AVCaptureVideoDataOutputSampleBufferDelegate>
{
AVCaptureVideoDataOutput *videoDataOutput;
dispatch_queue_t videoDataOutputQueue;
CALayer *customPreviewLayer;
CMTime lastSampleTime;
}
@property (nonatomic, weak) id<CvVideoCameraDelegate> delegate;
@property (nonatomic, assign) BOOL grayscaleMode;
@property (nonatomic, assign) BOOL recordVideo;
@property (nonatomic, assign) BOOL rotateVideo;
@property (nonatomic, strong) AVAssetWriterInput* recordAssetWriterInput;
@property (nonatomic, strong) AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor;
@property (nonatomic, strong) AVAssetWriter* recordAssetWriter;
- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
- CV_UNUSED(layoutPreviewLayer);
- CV_UNUSED(saveVideo);
- (NSURL *)videoFileURL;
- (NSString *)videoFileString;
@end
///////////////////////////////// CvPhotoCamera ///////////////////////////////////////////
@class CvPhotoCamera;
CV_EXPORTS @protocol CvPhotoCameraDelegate <NSObject>
- (void)photoCamera:(CvPhotoCamera*)photoCamera capturedImage:(UIImage *)image;
- (void)photoCameraCancel:(CvPhotoCamera*)photoCamera;
@end
CV_EXPORTS @interface CvPhotoCamera : CvAbstractCamera
{
AVCaptureStillImageOutput *stillImageOutput;
}
@property (nonatomic, weak) id<CvPhotoCameraDelegate> delegate;
- CV_UNUSED(takePicture);
@end
//! @} videoio_ios
@@ -0,0 +1,132 @@
// Video support for Windows Runtime
// Copyright (c) Microsoft Open Technologies, Inc.
// All rights reserved.
//
// (3 - clause BSD License)
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that
// the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
// following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or
// promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <ppl.h>
#include <functional>
#include <concrt.h>
#include <agile.h>
#include "opencv2/core/cvdef.h"
namespace cv
{
//! @addtogroup videoio_winrt
//! @{
enum {
OPEN_CAMERA = 300,
CLOSE_CAMERA,
UPDATE_IMAGE_ELEMENT,
SHOW_TRACKBAR
};
/********************************** WinRT API ************************************************/
template <typename ...Args>
CV_EXPORTS void winrt_startMessageLoop(std::function<void(Args...)>&& callback, Args... args);
template <typename ...Args>
CV_EXPORTS void winrt_startMessageLoop(void callback(Args...), Args... args);
/** @brief
@note
Starts (1) frame-grabbing loop and (2) message loop
1. Function passed as an argument must implement common OCV reading frames
pattern (see cv::VideoCapture documentation) AND call cv::winrt_imgshow().
2. Message processing loop required to overcome WinRT container and type
conversion restrictions. OCV provides default implementation
Here is how the class can be used:
@code
void cvMain()
{
Mat frame;
VideoCapture cam;
cam.open(0);
while (1)
{
cam >> frame;
// don't reprocess the same frame again
if (!cam.grab()) continue;
// your processing logic goes here
// obligatory step to get XAML image component updated
winrt_imshow();
}
}
MainPage::MainPage()
{
InitializeComponent();
cv::winrt_setFrameContainer(cvImage);
cv::winrt_startMessageLoop(cvMain);
}
@endcode
*/
template
CV_EXPORTS void winrt_startMessageLoop(void callback(void));
/** @brief
@note
Must be called from WinRT specific callback to handle image grabber state.
Here is how the class can be used:
@code
MainPage::MainPage()
{
// ...
Window::Current->VisibilityChanged += ref new Windows::UI::Xaml::WindowVisibilityChangedEventHandler(this, &Application::MainPage::OnVisibilityChanged);
// ...
}
void Application::MainPage::OnVisibilityChanged(Platform::Object ^sender,
Windows::UI::Core::VisibilityChangedEventArgs ^e)
{
cv::winrt_onVisibilityChanged(e->Visible);
}
@endcode
*/
CV_EXPORTS void winrt_onVisibilityChanged(bool visible);
/** @brief
@note
Must be called to assign WinRT control holding image you're working with.
Code sample is available for winrt_startMessageLoop().
*/
CV_EXPORTS void winrt_setFrameContainer(::Windows::UI::Xaml::Controls::Image^ image);
/** @brief
@note
Must be called to update attached image source.
Code sample is available for winrt_startMessageLoop().
*/
CV_EXPORTS void winrt_imshow();
//! @} videoio_winrt
} // cv
@@ -0,0 +1,194 @@
// 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 CONTAINER_AVI_HPP
#define CONTAINER_AVI_HPP
#ifndef __OPENCV_BUILD
# error this is a private header which should not be used from outside of the OpenCV library
#endif
#include "opencv2/core/types.hpp"
#include <deque>
#include <vector>
#include <string>
#include <memory>
namespace cv
{
/*
AVI struct:
RIFF ('AVI '
LIST ('hdrl'
'avih'(<Main AVI Header>)
LIST ('strl'
'strh'(<Stream header>)
'strf'(<Stream format>)
[ 'strd'(<Additional header data>) ]
[ 'strn'(<Stream name>) ]
[ 'indx'(<Odml index data>) ]
...
)
[LIST ('strl' ...)]
[LIST ('strl' ...)]
...
[LIST ('odml'
'dmlh'(<ODML header data>)
...
)
]
...
)
[LIST ('INFO' ...)]
[JUNK]
LIST ('movi'
{{xxdb|xxdc|xxpc|xxwb}(<Data>) | LIST ('rec '
{xxdb|xxdc|xxpc|xxwb}(<Data>)
{xxdb|xxdc|xxpc|xxwb}(<Data>)
...
)
...
}
...
)
['idx1' (<AVI Index>) ]
)
{xxdb|xxdc|xxpc|xxwb}
xx - stream number: 00, 01, 02, ...
db - uncompressed video frame
dc - compressed video frame
pc - palette change
wb - audio frame
JUNK section may pad any data section and must be ignored
*/
typedef std::deque< std::pair<uint64_t, uint32_t> > frame_list;
typedef frame_list::iterator frame_iterator;
struct RiffChunk;
struct RiffList;
class VideoInputStream;
enum Codecs { MJPEG };
//Represents single MJPEG video stream within single AVI/AVIX entry
//Multiple video streams within single AVI/AVIX entry are not supported
//ODML index is not supported
class CV_EXPORTS AVIReadContainer
{
public:
AVIReadContainer();
void initStream(const std::string& filename);
void initStream(std::shared_ptr<VideoInputStream> m_file_stream_);
void close();
//stores founded frames in m_frame_list which can be accessed via getFrames
bool parseAvi(Codecs codec_) { return parseAviWithFrameList(m_frame_list, codec_); }
//stores founded frames in in_frame_list. getFrames() would return empty list
bool parseAvi(frame_list& in_frame_list, Codecs codec_) { return parseAviWithFrameList(in_frame_list, codec_); }
size_t getFramesCount() { return m_frame_list.size(); }
frame_list& getFrames() { return m_frame_list; }
unsigned int getWidth() { return m_width; }
unsigned int getHeight() { return m_height; }
double getFps() { return m_fps; }
std::vector<char> readFrame(frame_iterator it);
bool parseRiff(frame_list &m_mjpeg_frames);
protected:
bool parseAviWithFrameList(frame_list& in_frame_list, Codecs codec_);
void skipJunk(RiffChunk& chunk);
void skipJunk(RiffList& list);
bool parseHdrlList(Codecs codec_);
bool parseIndex(unsigned int index_size, frame_list& in_frame_list);
bool parseMovi(frame_list& in_frame_list)
{
//not implemented
CV_UNUSED(in_frame_list);
// FIXIT: in_frame_list.empty();
return true;
}
bool parseStrl(char stream_id, Codecs codec_);
bool parseInfo()
{
//not implemented
return true;
}
void printError(RiffList& list, unsigned int expected_fourcc);
void printError(RiffChunk& chunk, unsigned int expected_fourcc);
std::shared_ptr<VideoInputStream> m_file_stream;
unsigned int m_stream_id;
unsigned long long int m_movi_start;
unsigned long long int m_movi_end;
frame_list m_frame_list;
unsigned int m_width;
unsigned int m_height;
double m_fps;
bool m_is_indx_present;
};
enum { COLORSPACE_GRAY=0, COLORSPACE_RGBA=1, COLORSPACE_BGR=2, COLORSPACE_YUV444P=3 };
enum StreamType { db, dc, pc, wb };
class BitStream;
// {xxdb|xxdc|xxpc|xxwb}
// xx - stream number: 00, 01, 02, ...
// db - uncompressed video frame
// dc - compressed video frame
// pc - palette change
// wb - audio frame
class CV_EXPORTS AVIWriteContainer
{
public:
AVIWriteContainer();
~AVIWriteContainer();
bool initContainer(const std::string& filename, double fps, cv::Size size, bool iscolor);
void startWriteAVI(int stream_count);
void writeStreamHeader(Codecs codec_);
void startWriteChunk(uint32_t fourcc);
void endWriteChunk();
int getAVIIndex(int stream_number, StreamType strm_type);
void writeIndex(int stream_number, StreamType strm_type);
void finishWriteAVI();
bool isOpenedStream() const;
bool isEmptyFrameOffset() const { return frameOffset.empty(); }
int getWidth() const { return width; }
int getHeight() const { return height; }
int getChannels() const { return channels; }
size_t getMoviPointer() const { return moviPointer; }
size_t getStreamPos() const;
void pushFrameOffset(size_t elem) { frameOffset.push_back(elem); }
void pushFrameSize(size_t elem) { frameSize.push_back(elem); }
bool isEmptyFrameSize() const { return frameSize.empty(); }
size_t atFrameSize(size_t i) const { return frameSize[i]; }
size_t countFrameSize() const { return frameSize.size(); }
void jputStreamShort(int val);
void putStreamBytes(const uchar* buf, int count);
void putStreamByte(int val);
void jputStream(unsigned currval);
void jflushStream(unsigned currval, int bitIdx);
private:
std::shared_ptr<BitStream> strm;
int outfps;
int width, height, channels;
size_t moviPointer;
std::vector<size_t> frameOffset, frameSize, AVIChunkSizeIndex, frameNumIndexes;
};
}
#endif //CONTAINER_AVI_HPP
@@ -0,0 +1,34 @@
// 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.
//
// This file should not be used with compiler (documentation only)
//
namespace cv {
/** @addtogroup videoio_hwaccel
This section contains information about API to control Hardware-accelerated video decoding and encoding.
@note Check [Wiki page](https://github.com/opencv/opencv/wiki/Video-IO-hardware-acceleration)
for description of supported hardware / software configurations and available benchmarks
cv::VideoCapture properties:
- #CAP_PROP_HW_ACCELERATION (as #VideoAccelerationType)
- #CAP_PROP_HW_DEVICE
cv::VideoWriter properties:
- #VIDEOWRITER_PROP_HW_ACCELERATION (as #VideoAccelerationType)
- #VIDEOWRITER_PROP_HW_DEVICE
Properties are supported by these backends:
- #CAP_FFMPEG
- #CAP_GSTREAMER
- #CAP_MSMF (Windows)
@{
*/
/** @} */
} // namespace
@@ -0,0 +1,82 @@
// 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 <opencv2/videoio.hpp>
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_<backend>=9999`
- disable backend: `OPENCV_VIDEOIO_PRIORITY_<backend>=0`
- specify list of backends with high priority (>100000): `OPENCV_VIDEOIO_PRIORITY_LIST=FFMPEG,GSTREAMER`
@{
*/
/** @brief Returns backend API name or "UnknownVideoAPI(xxx)"
@param api backend ID (#VideoCaptureAPIs)
*/
CV_EXPORTS_W cv::String getBackendName(VideoCaptureAPIs api);
/** @brief Returns list of all available backends */
CV_EXPORTS_W std::vector<VideoCaptureAPIs> getBackends();
/** @brief Returns list of available backends which works via `cv::VideoCapture(int index)` */
CV_EXPORTS_W std::vector<VideoCaptureAPIs> getCameraBackends();
/** @brief Returns list of available backends which works via `cv::VideoCapture(filename)` */
CV_EXPORTS_W std::vector<VideoCaptureAPIs> getStreamBackends();
/** @brief Returns list of available backends which works via `cv::VideoCapture(buffer)` */
CV_EXPORTS_W std::vector<VideoCaptureAPIs> getStreamBufferedBackends();
/** @brief Returns list of available backends which works via `cv::VideoWriter()` */
CV_EXPORTS_W std::vector<VideoCaptureAPIs> getWriterBackends();
/** @brief Returns true if backend is available */
CV_EXPORTS_W bool hasBackend(VideoCaptureAPIs api);
/** @brief Returns true if backend is built in (false if backend is used as plugin) */
CV_EXPORTS_W bool isBackendBuiltIn(VideoCaptureAPIs api);
/** @brief Returns description and ABI/API version of videoio plugin's camera interface */
CV_EXPORTS_W std::string getCameraBackendPluginVersion(
VideoCaptureAPIs api,
CV_OUT int& version_ABI,
CV_OUT int& version_API
);
/** @brief Returns description and ABI/API version of videoio plugin's stream capture interface */
CV_EXPORTS_W std::string getStreamBackendPluginVersion(
VideoCaptureAPIs api,
CV_OUT int& version_ABI,
CV_OUT int& version_API
);
/** @brief Returns description and ABI/API version of videoio plugin's buffer capture interface */
CV_EXPORTS_W std::string getStreamBufferedBackendPluginVersion(
VideoCaptureAPIs api,
CV_OUT int& version_ABI,
CV_OUT int& version_API
);
/** @brief Returns description and ABI/API version of videoio plugin's writer interface */
CV_EXPORTS_W std::string getWriterBackendPluginVersion(
VideoCaptureAPIs api,
CV_OUT int& version_ABI,
CV_OUT int& version_API
);
//! @}
}} // namespace
#endif // OPENCV_VIDEOIO_REGISTRY_HPP
@@ -0,0 +1,46 @@
// 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_UTILS_PRIVATE_HPP
#define OPENCV_VIDEOIO_UTILS_PRIVATE_HPP
#include "opencv2/core/cvdef.h"
#include <string>
namespace cv {
CV_EXPORTS std::string icvExtractPattern(const std::string& filename, unsigned *offset);
class PluginStreamReader : public IStreamReader
{
public:
PluginStreamReader(void* _opaque,
long long (*_read)(void* opaque, char* buffer, long long size),
long long (*_seek)(void* opaque, long long offset, int way))
{
opaque = _opaque;
readCallback = _read;
seekCallback = _seek;
}
virtual ~PluginStreamReader() {}
long long read(char* buffer, long long size) override
{
return readCallback(opaque, buffer, size);
}
long long seek(long long offset, int way) override
{
return seekCallback(opaque, offset, way);
}
private:
void* opaque;
long long (*readCallback)(void* opaque, char* buffer, long long size);
long long (*seekCallback)(void* opaque, long long offset, int way);
};
}
#endif // OPENCV_VIDEOIO_UTILS_PRIVATE_HPP
@@ -0,0 +1,48 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifdef __OPENCV_BUILD
#error this is a compatibility header which should not be used inside the OpenCV library
#endif
#include "opencv2/videoio.hpp"
+81
View File
@@ -0,0 +1,81 @@
#!/bin/bash
set -e
if [ -z $1 ] ; then
echo "$0 <destination directory>"
exit 1
fi
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
OCV="$( cd "${DIR}/../../.." >/dev/null 2>&1 && pwd )"
mkdir -p "${1}" # Docker creates non-existed mounts with 'root' owner, lets ensure that dir exists under the current user to avoid "Permission denied" problem
DST="$( cd "$1" >/dev/null 2>&1 && pwd )"
CFG=$2
do_build()
{
TAG=$1
D=$2
F=$3
shift 3
docker build \
--build-arg http_proxy \
--build-arg https_proxy \
$@ \
-t $TAG \
-f "${D}/${F}" \
"${D}"
}
do_run()
{
TAG=$1
shift 1
docker run \
-it \
--rm \
-v "${OCV}":/opencv:ro \
-v "${DST}":/dst \
-e CFG=$CFG \
--user $(id -u):$(id -g) \
$TAG \
$@
}
build_gstreamer()
{
TAG=opencv_gstreamer_builder
do_build $TAG "${DIR}/plugin_gstreamer" Dockerfile
do_run $TAG /opencv/modules/videoio/misc/plugin_gstreamer/build.sh /dst $CFG
}
build_ffmpeg_ubuntu()
{
VER=$1
TAG=opencv_ffmpeg_ubuntu_builder:${VER}
do_build $TAG "${DIR}/plugin_ffmpeg" Dockerfile-ubuntu --build-arg VER=${VER}
do_run $TAG /opencv/modules/videoio/misc/plugin_ffmpeg/build-ubuntu.sh /dst ${VER} ${CFG}
}
build_ffmpeg()
{
VER=$1
TAG=opencv_ffmpeg_builder:${VER}
ARCHIVE="${DIR}/plugin_ffmpeg/ffmpeg-${VER}.tar.xz"
if [ ! -f "${ARCHIVE}" ] ; then
wget https://www.ffmpeg.org/releases/ffmpeg-${VER}.tar.xz -O "${ARCHIVE}"
fi
do_build $TAG "${DIR}/plugin_ffmpeg" Dockerfile-ffmpeg --build-arg VER=${VER}
do_run $TAG /opencv/modules/videoio/misc/plugin_ffmpeg/build-standalone.sh /dst ${VER} ${CFG}
}
echo "OpenCV: ${OCV}"
echo "Destination: ${DST}"
build_gstreamer
build_ffmpeg_ubuntu 18.04
build_ffmpeg_ubuntu 16.04
build_ffmpeg 4.1
build_ffmpeg 3.4.5
build_ffmpeg 2.8.15
+24
View File
@@ -0,0 +1,24 @@
{
"const_ignore_list": [
"CV_CAP_OPENNI2",
"CV_CAP_INTELPERC",
"CV_CAP_ANY",
"CV_CAP_MIL",
"CV_CAP_V4L",
"CV_CAP_V4L2",
"CV_CAP_FIREWARE",
"CV_CAP_FIREWIRE",
"CV_CAP_IEEE1394",
"CV_CAP_DC1394",
"CV_CAP_CMU1394",
"CV_CAP_STEREO",
"CV_CAP_TYZX",
"CV_CAP_DSHOW",
"CV_CAP_PVAPI",
"CV_CAP_ARAVIS",
"CV_TYZX_LEFT",
"CV_TYZX_RIGHT",
"CV_TYZX_COLOR",
"CV_TYZX_Z"
]
}
@@ -0,0 +1 @@
misc/java/src/cpp/videoio_converters.hpp
+40
View File
@@ -0,0 +1,40 @@
{
"ManualFuncs" : {
"IStreamReader" : {
"IStreamReader" : {
"j_code" : [
"\n",
"/**",
" * Constructor of streaming callback object with abstract 'read' and 'seek' methods that should be implemented in Java code.<br>",
" * <b>NOTE</b>: Implemented callbacks should be called from the creation thread to avoid JNI performance degradation",
"*/",
"protected IStreamReader() { nativeObj = 0; }",
"\n"
],
"jn_code": [],
"cpp_code": []
}
}
},
"func_arg_fix" : {
"read": { "buffer": {"ctype" : "byte[]"} }
},
"type_dict": {
"Ptr_IStreamReader": {
"j_type": "IStreamReader",
"jn_type": "IStreamReader",
"jni_name": "n_%(n)s",
"jni_type": "jobject",
"jni_var": "auto n_%(n)s = makePtr<JavaStreamReader>(env, source)",
"j_import": "org.opencv.videoio.IStreamReader"
},
"vector_VideoCaptureAPIs": {
"j_type": "List<Integer>",
"jn_type": "List<Integer>",
"jni_type": "jobject",
"jni_var": "std::vector< cv::VideoCaptureAPIs > %(n)s",
"suffix": "Ljava_util_List",
"v_type": "vector_VideoCaptureAPIs"
}
}
}
@@ -0,0 +1,97 @@
#include "videoio_converters.hpp"
class JNIEnvHandler
{
public:
JNIEnvHandler(JavaVM* _vm) : vm(_vm)
{
jint res = vm->GetEnv((void**)&env, JNI_VERSION_1_6);
if (res == JNI_EDETACHED)
{
#ifdef __ANDROID__
res = vm->AttachCurrentThread(&env, NULL);
#else
res = vm->AttachCurrentThread((void**)&env, NULL);
#endif // __ANDROID__
detach = true;
}
}
~JNIEnvHandler()
{
if (env && detach)
{
vm->DetachCurrentThread();
}
}
JavaVM* vm;
JNIEnv* env = nullptr;
bool detach = false;
};
JavaStreamReader::JavaStreamReader(JNIEnv* env, jobject _obj)
{
obj = env->NewGlobalRef(_obj);
jclass cls = env->GetObjectClass(obj);
m_read = env->GetMethodID(cls, "read", "([BJ)J");
m_seek = env->GetMethodID(cls, "seek", "(JI)J");
env->GetJavaVM(&vm);
}
JavaStreamReader::~JavaStreamReader()
{
JNIEnvHandler handler(vm);
JNIEnv* env = handler.env;
if (!env)
return;
env->DeleteGlobalRef(obj);
}
long long JavaStreamReader::read(char* buffer, long long size)
{
if (!m_read)
return 0;
JNIEnvHandler handler(vm);
JNIEnv* env = handler.env;
if (!env)
return 0;
jbyteArray jBuffer = env->NewByteArray(static_cast<jsize>(size));
if (!jBuffer)
return 0;
jlong res = env->CallLongMethod(obj, m_read, jBuffer, size);
env->GetByteArrayRegion(jBuffer, 0, static_cast<jsize>(size), reinterpret_cast<jbyte*>(buffer));
env->DeleteLocalRef(jBuffer);
return res;
}
long long JavaStreamReader::seek(long long offset, int way)
{
JNIEnvHandler handler(vm);
JNIEnv* env = handler.env;
if (!env)
return 0;
if (!m_seek)
return 0;
return env->CallLongMethod(obj, m_seek, offset, way);
}
// Same as dnn::vector_Target_to_List
jobject vector_VideoCaptureAPIs_to_List(JNIEnv* env, std::vector<cv::VideoCaptureAPIs>& vs)
{
static jclass juArrayList = ARRAYLIST(env);
static jmethodID m_create = CONSTRUCTOR(env, juArrayList);
jmethodID m_add = LIST_ADD(env, juArrayList);
static jclass jInteger = env->FindClass("java/lang/Integer");
static jmethodID m_create_Integer = env->GetMethodID(jInteger, "<init>", "(I)V");
jobject result = env->NewObject(juArrayList, m_create, vs.size());
for (size_t i = 0; i < vs.size(); ++i)
{
jobject element = env->NewObject(jInteger, m_create_Integer, vs[i]);
env->CallBooleanMethod(result, m_add, element);
env->DeleteLocalRef(element);
}
return result;
}
@@ -0,0 +1,25 @@
#ifndef VIDEOIO_CONVERTERS_HPP
#define VIDEOIO_CONVERTERS_HPP
#include <jni.h>
#include "opencv_java.hpp"
#include "opencv2/core.hpp"
#include "opencv2/videoio.hpp"
class JavaStreamReader : public cv::IStreamReader
{
public:
JavaStreamReader(JNIEnv* env, jobject obj);
~JavaStreamReader();
long long read(char* buffer, long long size) CV_OVERRIDE;
long long seek(long long offset, int way) CV_OVERRIDE;
private:
JavaVM* vm;
jobject obj;
jmethodID m_read, m_seek;
};
jobject vector_VideoCaptureAPIs_to_List(JNIEnv* env, std::vector<cv::VideoCaptureAPIs>& vs);
#endif
@@ -0,0 +1,148 @@
package org.opencv.test.videoio;
import java.util.List;
import java.io.File;
import java.io.RandomAccessFile;
import java.io.IOException;
import java.io.FileNotFoundException;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.core.MatOfInt;
import org.opencv.videoio.Videoio;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.IStreamReader;
import org.opencv.test.OpenCVTestCase;
public class VideoCaptureTest extends OpenCVTestCase {
private final static String ENV_OPENCV_TEST_DATA_PATH = "OPENCV_TEST_DATA_PATH";
private VideoCapture capture;
private boolean isOpened;
private boolean isSucceed;
private File testDataPath;
@Override
protected void setUp() throws Exception {
super.setUp();
capture = null;
isSucceed = false;
isOpened = false;
String envTestDataPath = System.getenv(ENV_OPENCV_TEST_DATA_PATH);
if(envTestDataPath == null) throw new Exception(ENV_OPENCV_TEST_DATA_PATH + " has to be defined!");
testDataPath = new File(envTestDataPath);
}
public void testGrab() {
capture = new VideoCapture();
isSucceed = capture.grab();
assertFalse(isSucceed);
}
public void testIsOpened() {
capture = new VideoCapture();
assertFalse(capture.isOpened());
}
public void testDefaultConstructor() {
capture = new VideoCapture();
assertNotNull(capture);
assertFalse(capture.isOpened());
}
public void testConstructorWithFilename() {
capture = new VideoCapture("some_file.avi");
assertNotNull(capture);
}
public void testConstructorWithFilenameAndExplicitlySpecifiedAPI() {
capture = new VideoCapture("some_file.avi", Videoio.CAP_ANY);
assertNotNull(capture);
}
public void testConstructorWithIndex() {
capture = new VideoCapture(0);
assertNotNull(capture);
}
public void testConstructorWithIndexAndExplicitlySpecifiedAPI() {
capture = new VideoCapture(0, Videoio.CAP_ANY);
assertNotNull(capture);
}
public void testConstructorStream() throws FileNotFoundException {
// Check backend is available
Integer apiPref = Videoio.CAP_ANY;
for (Integer backend : Videoio.getStreamBufferedBackends())
{
if (!Videoio.hasBackend(backend))
continue;
if (!Videoio.isBackendBuiltIn(backend))
{
int[] abi = new int[1], api = new int[1];
Videoio.getStreamBufferedBackendPluginVersion(backend, abi, api);
if (abi[0] < 1 || (abi[0] == 1 && api[0] < 2))
continue;
}
apiPref = backend;
break;
}
if (apiPref == Videoio.CAP_ANY)
{
throw new TestSkipException();
}
RandomAccessFile f = new RandomAccessFile(new File(testDataPath, "cv/video/768x576.avi"), "r");
IStreamReader stream = new IStreamReader()
{
@Override
public long read(byte[] buffer, long size)
{
assertEquals(buffer.length, size);
try
{
return Math.max(f.read(buffer), 0);
}
catch (IOException e)
{
System.out.println(e.getMessage());
return 0;
}
}
@Override
public long seek(long offset, int origin)
{
try
{
if (origin == 0)
f.seek(offset);
else if (origin == 1)
f.seek(f.getFilePointer() + offset);
else if (origin == 2)
f.seek(f.length() + offset);
return f.getFilePointer();
}
catch (IOException e)
{
System.out.println(e.getMessage());
return 0;
}
}
};
capture = new VideoCapture(stream, apiPref, new MatOfInt());
assertNotNull(capture);
assertTrue(capture.isOpened());
Mat frame = new Mat();
assertTrue(capture.read(frame));
assertEquals(frame.rows(), 576);
assertEquals(frame.cols(), 768);
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"AdditionalImports" : {
"Videoio" :
[ "\"videoio/registry.hpp\"" ]
},
"ManualFuncs" : {
"VideoCapture" : {
"release" : {"declaration" : [""], "implementation" : [""] }
},
"VideoWriter" : {
"release" : {"declaration" : [""], "implementation" : [""] }
}
},
"func_arg_fix" : {
"VideoCapture" : {
"(BOOL)open:(int)index apiPreference:(int)apiPreference" : { "open" : {"name" : "openWithIndex"} },
"(BOOL)open:(int)index apiPreference:(int)apiPreference params:(IntVector*)params" : { "open" : {"name" : "openWithIndexAndParameters"} },
"(BOOL)open:(IStreamReader*)source apiPreference:(int)apiPreference params:(IntVector*)params" : { "open" : {"name" : "openWithStreamReader"} }
}
}
}
@@ -0,0 +1,445 @@
//
// CvAbstractCamera2.mm
//
// Created by Giles Payne on 2020/04/01.
//
#import "CvCamera2.h"
#pragma mark - Private Interface
@interface CvAbstractCamera2 ()
@property (nonatomic, strong) AVCaptureVideoPreviewLayer* captureVideoPreviewLayer;
- (void)deviceOrientationDidChange:(NSNotification*)notification;
- (void)startCaptureSession;
- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
- (void)updateSize;
@end
#pragma mark - Implementation
@implementation CvAbstractCamera2
#pragma mark - Constructors
- (id)init;
{
self = [super init];
if (self) {
// react to device orientation notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceOrientationDidChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
self.currentDeviceOrientation = [[UIDevice currentDevice] orientation];
// check if camera available
self.cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
NSLog(@"camera available: %@", (self.cameraAvailable ? @"YES" : @"NO") );
_running = NO;
// set camera default configuration
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
self.defaultFPS = 15;
self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset352x288;
self.parentView = nil;
self.useAVCaptureVideoPreviewLayer = NO;
}
return self;
}
- (id)initWithParentView:(UIView*)parent;
{
self = [super init];
if (self) {
// react to device orientation notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceOrientationDidChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
self.currentDeviceOrientation = [[UIDevice currentDevice] orientation];
// check if camera available
self.cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
NSLog(@"camera available: %@", (self.cameraAvailable ? @"YES" : @"NO") );
_running = NO;
// set camera default configuration
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
self.defaultFPS = 15;
self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480;
self.parentView = parent;
self.useAVCaptureVideoPreviewLayer = YES;
}
return self;
}
- (void)dealloc;
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}
#pragma mark - Public interface
- (void)start;
{
if (![NSThread isMainThread]) {
NSLog(@"[Camera] Warning: Call start only from main thread");
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
return;
}
if (self.running == YES) {
return;
}
_running = YES;
// TODO: update image size data before actually starting (needed for recording)
[self updateSize];
if (self.cameraAvailable) {
[self startCaptureSession];
}
}
- (void)pause;
{
_running = NO;
[self.captureSession stopRunning];
}
- (void)stop;
{
_running = NO;
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
if (self.captureSession) {
for (AVCaptureInput *input in self.captureSession.inputs) {
[self.captureSession removeInput:input];
}
for (AVCaptureOutput *output in self.captureSession.outputs) {
[self.captureSession removeOutput:output];
}
[self.captureSession stopRunning];
}
_captureSessionLoaded = NO;
}
// use front/back camera
- (void)switchCameras;
{
BOOL was_running = self.running;
if (was_running) {
[self stop];
}
if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;
} else {
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
}
if (was_running) {
[self start];
}
}
#pragma mark - Device Orientation Changes
- (void)deviceOrientationDidChange:(NSNotification*)notification
{
(void)notification;
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
switch (orientation)
{
case UIDeviceOrientationPortrait:
case UIDeviceOrientationPortraitUpsideDown:
case UIDeviceOrientationLandscapeLeft:
case UIDeviceOrientationLandscapeRight:
self.currentDeviceOrientation = orientation;
break;
case UIDeviceOrientationFaceUp:
case UIDeviceOrientationFaceDown:
default:
break;
}
NSLog(@"deviceOrientationDidChange: %d", (int)orientation);
[self updateOrientation];
}
#pragma mark - Private Interface
- (void)createCaptureSession;
{
// set a av capture session preset
self.captureSession = [[AVCaptureSession alloc] init];
if ([self.captureSession canSetSessionPreset:self.defaultAVCaptureSessionPreset]) {
[self.captureSession setSessionPreset:self.defaultAVCaptureSessionPreset];
} else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetLow]) {
[self.captureSession setSessionPreset:AVCaptureSessionPresetLow];
} else {
NSLog(@"[Camera] Error: could not set session preset");
}
}
- (void)createCaptureDevice;
{
// setup the device
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[self setDesiredCameraPosition:self.defaultAVCaptureDevicePosition];
NSLog(@"[Camera] device connected? %@", device.connected ? @"YES" : @"NO");
NSLog(@"[Camera] device position %@", (device.position == AVCaptureDevicePositionBack) ? @"back" : @"front");
}
- (void)createVideoPreviewLayer;
{
self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
if ([self.captureVideoPreviewLayer.connection isVideoOrientationSupported])
{
[self.captureVideoPreviewLayer.connection setVideoOrientation:self.defaultAVCaptureVideoOrientation];
}
if (self.parentView != nil) {
self.captureVideoPreviewLayer.frame = self.parentView.bounds;
self.captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.parentView.layer addSublayer:self.captureVideoPreviewLayer];
}
NSLog(@"[Camera] created AVCaptureVideoPreviewLayer");
}
- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
{
for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
if ([device position] == desiredPosition) {
[self.captureSession beginConfiguration];
NSError* error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
NSLog(@"error creating input %@", [error description]);
}
// support for autofocus
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
error = nil;
if ([device lockForConfiguration:&error]) {
device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for autofocus configuration %@", [error description]);
}
}
[self.captureSession addInput:input];
for (AVCaptureInput *oldInput in self.captureSession.inputs) {
[self.captureSession removeInput:oldInput];
}
[self.captureSession addInput:input];
[self.captureSession commitConfiguration];
break;
}
}
}
- (void)startCaptureSession
{
if (!self.cameraAvailable) {
return;
}
if (self.captureSessionLoaded == NO) {
[self createCaptureSession];
[self createCaptureDevice];
[self createCaptureOutput];
// setup preview layer
if (self.useAVCaptureVideoPreviewLayer) {
[self createVideoPreviewLayer];
} else {
[self createCustomVideoPreview];
}
_captureSessionLoaded = YES;
}
[self.captureSession startRunning];
}
- (void)createCaptureOutput;
{
[NSException raise:NSInternalInconsistencyException
format:@"You must override %s in a subclass", __FUNCTION__];
}
- (void)createCustomVideoPreview;
{
[NSException raise:NSInternalInconsistencyException
format:@"You must override %s in a subclass", __FUNCTION__];
}
- (void)updateOrientation;
{
// nothing to do here
}
- (void)updateSize;
{
if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetPhoto]) {
//TODO: find the correct resolution
self.imageWidth = 640;
self.imageHeight = 480;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetHigh]) {
//TODO: find the correct resolution
self.imageWidth = 640;
self.imageHeight = 480;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetMedium]) {
//TODO: find the correct resolution
self.imageWidth = 640;
self.imageHeight = 480;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetLow]) {
//TODO: find the correct resolution
self.imageWidth = 640;
self.imageHeight = 480;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset352x288]) {
self.imageWidth = 352;
self.imageHeight = 288;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset640x480]) {
self.imageWidth = 640;
self.imageHeight = 480;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset1280x720]) {
self.imageWidth = 1280;
self.imageHeight = 720;
} else {
self.imageWidth = 640;
self.imageHeight = 480;
}
}
- (void)lockFocus;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.focusMode = AVCaptureFocusModeLocked;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for locked focus configuration %@", [error description]);
}
}
}
- (void) unlockFocus;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for autofocus configuration %@", [error description]);
}
}
}
- (void)lockExposure;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isExposureModeSupported:AVCaptureExposureModeLocked]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.exposureMode = AVCaptureExposureModeLocked;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for locked exposure configuration %@", [error description]);
}
}
}
- (void) unlockExposure;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.exposureMode = AVCaptureExposureModeContinuousAutoExposure;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for autoexposure configuration %@", [error description]);
}
}
}
- (void)lockBalance;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.whiteBalanceMode = AVCaptureWhiteBalanceModeLocked;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for locked white balance configuration %@", [error description]);
}
}
}
- (void) unlockBalance;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.whiteBalanceMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for auto white balance configuration %@", [error description]);
}
}
}
@end
+85
View File
@@ -0,0 +1,85 @@
//
// CvCamera2.h
//
// Created by Giles Payne on 2020/03/11.
//
#import <UIKit/UIKit.h>
#import <Accelerate/Accelerate.h>
#import <AVFoundation/AVFoundation.h>
#import <ImageIO/ImageIO.h>
#import "CVObjcUtil.h"
@class Mat;
@class CvAbstractCamera2;
CV_EXPORTS @interface CvAbstractCamera2 : NSObject
@property UIDeviceOrientation currentDeviceOrientation;
@property BOOL cameraAvailable;
@property (nonatomic, strong) AVCaptureSession* captureSession;
@property (nonatomic, strong) AVCaptureConnection* videoCaptureConnection;
@property (nonatomic, readonly) BOOL running;
@property (nonatomic, readonly) BOOL captureSessionLoaded;
@property (nonatomic, assign) int defaultFPS;
@property (nonatomic, readonly) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;
@property (nonatomic, assign) AVCaptureDevicePosition defaultAVCaptureDevicePosition;
@property (nonatomic, assign) AVCaptureVideoOrientation defaultAVCaptureVideoOrientation;
@property (nonatomic, assign) BOOL useAVCaptureVideoPreviewLayer;
@property (nonatomic, strong) NSString *const defaultAVCaptureSessionPreset;
@property (nonatomic, assign) int imageWidth;
@property (nonatomic, assign) int imageHeight;
@property (nonatomic, strong) UIView* parentView;
- (void)start;
- (void)stop;
- (void)switchCameras;
- (id)initWithParentView:(UIView*)parent;
- (void)createCaptureOutput;
- (void)createVideoPreviewLayer;
- (void)updateOrientation;
- (void)lockFocus;
- (void)unlockFocus;
- (void)lockExposure;
- (void)unlockExposure;
- (void)lockBalance;
- (void)unlockBalance;
@end
///////////////////////////////// CvVideoCamera ///////////////////////////////////////////
@class CvVideoCamera2;
@protocol CvVideoCameraDelegate2 <NSObject>
- (void)processImage:(Mat*)image;
@end
CV_EXPORTS @interface CvVideoCamera2 : CvAbstractCamera2<AVCaptureVideoDataOutputSampleBufferDelegate>
@property (nonatomic, weak) id<CvVideoCameraDelegate2> delegate;
@property (nonatomic, assign) BOOL grayscaleMode;
@property (nonatomic, assign) BOOL recordVideo;
@property (nonatomic, assign) BOOL rotateVideo;
@property (nonatomic, strong) AVAssetWriterInput* recordAssetWriterInput;
@property (nonatomic, strong) AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor;
@property (nonatomic, strong) AVAssetWriter* recordAssetWriter;
- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
- (void)layoutPreviewLayer;
- (void)saveVideo;
- (NSURL *)videoFileURL;
- (NSString *)videoFileString;
@end
///////////////////////////////// CvPhotoCamera ///////////////////////////////////////////
@class CvPhotoCamera2;
@protocol CvPhotoCameraDelegate2 <NSObject>
- (void)photoCamera:(CvPhotoCamera2*)photoCamera capturedImage:(UIImage*)image;
- (void)photoCameraCancel:(CvPhotoCamera2*)photoCamera;
@end
CV_EXPORTS @interface CvPhotoCamera2 : CvAbstractCamera2<AVCapturePhotoCaptureDelegate>
@property (nonatomic, weak) id<CvPhotoCameraDelegate2> delegate;
- (void)takePicture;
@end
@@ -0,0 +1,138 @@
//
// CvPhotoCamera2.mm
//
// Created by Giles Payne on 2020/04/01.
//
#import "CvCamera2.h"
#pragma mark - Private Interface
@interface CvPhotoCamera2 ()
{
id<CvPhotoCameraDelegate2> _delegate;
}
@property (nonatomic, strong) AVCaptureStillImageOutput* stillImageOutput;
@end
#pragma mark - Implementation
@implementation CvPhotoCamera2
#pragma mark Public
- (void)setDelegate:(id<CvPhotoCameraDelegate2>)newDelegate {
_delegate = newDelegate;
}
- (id<CvPhotoCameraDelegate2>)delegate {
return _delegate;
}
#pragma mark - Public interface
- (void)takePicture
{
if (self.cameraAvailable == NO) {
return;
}
self.cameraAvailable = NO;
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:self.videoCaptureConnection
completionHandler:
^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
if (error == nil && imageSampleBuffer != NULL)
{
// TODO check
// NSNumber* imageOrientation = [UIImage cgImageOrientationForUIDeviceOrientation:currentDeviceOrientation];
// CMSetAttachment(imageSampleBuffer, kCGImagePropertyOrientation, imageOrientation, 1);
NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
dispatch_async(dispatch_get_main_queue(), ^{
[self.captureSession stopRunning];
// Make sure we create objects on the main thread in the main context
UIImage* newImage = [UIImage imageWithData:jpegData];
//UIImageOrientation orientation = [newImage imageOrientation];
// TODO: only apply rotation, don't scale, since we can set this directly in the camera
/*
switch (orientation) {
case UIImageOrientationUp:
case UIImageOrientationDown:
newImage = [newImage imageWithAppliedRotationAndMaxSize:CGSizeMake(640.0, 480.0)];
break;
case UIImageOrientationLeft:
case UIImageOrientationRight:
newImage = [newImage imageWithMaxSize:CGSizeMake(640.0, 480.0)];
default:
break;
}
*/
// We have captured the image, we can allow the user to take another picture
self.cameraAvailable = YES;
NSLog(@"CvPhotoCamera2 captured image");
[self.delegate photoCamera:self capturedImage:newImage];
[self.captureSession startRunning];
});
}
}];
}
- (void)stop;
{
[super stop];
self.stillImageOutput = nil;
}
#pragma mark - Private Interface
- (void)createStillImageOutput;
{
// setup still image output with jpeg codec
self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecTypeJPEG, AVVideoCodecKey, nil];
[self.stillImageOutput setOutputSettings:outputSettings];
[self.captureSession addOutput:self.stillImageOutput];
for (AVCaptureConnection *connection in self.stillImageOutput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([port.mediaType isEqual:AVMediaTypeVideo]) {
self.videoCaptureConnection = connection;
break;
}
}
if (self.videoCaptureConnection) {
break;
}
}
NSLog(@"[Camera] still image output created");
}
- (void)createCaptureOutput;
{
[self createStillImageOutput];
}
- (void)createCustomVideoPreview;
{
//do nothing, always use AVCaptureVideoPreviewLayer
}
@end
@@ -0,0 +1,575 @@
//
// CvVideoCamera2.mm
//
// Created by Giles Payne on 2020/03/11.
//
#import "Mat.h"
#import "CvCamera2.h"
#import <UIKit/UIKit.h>
static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}
#pragma mark - Private Interface
@interface CvVideoCamera2 () {
int recordingCountDown;
}
- (void)createVideoDataOutput;
- (void)createVideoFileOutput;
@property (nonatomic, strong) CALayer *customPreviewLayer;
@property (nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput;
@end
#pragma mark - Implementation
@implementation CvVideoCamera2
{
id<CvVideoCameraDelegate2> _delegate;
dispatch_queue_t videoDataOutputQueue;
CMTime lastSampleTime;
}
- (void)setDelegate:(id<CvVideoCameraDelegate2>)newDelegate {
_delegate = newDelegate;
}
- (id<CvVideoCameraDelegate2>)delegate {
return _delegate;
}
#pragma mark - Constructors
- (id)initWithParentView:(UIView*)parent {
self = [super initWithParentView:parent];
if (self) {
parent.contentMode = UIViewContentModeScaleAspectFill;
self.useAVCaptureVideoPreviewLayer = NO;
self.recordVideo = NO;
self.rotateVideo = NO;
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;
self.defaultAVCaptureSessionPreset = AVCaptureSessionPresetHigh;
self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait;
self.defaultFPS = 30;
self.grayscaleMode = NO;
}
return self;
}
#pragma mark - Public interface
- (void)start {
if (self.running == YES) {
return;
}
recordingCountDown = 10;
[super start];
if (self.recordVideo == YES) {
NSError* error = nil;
if ([[NSFileManager defaultManager] fileExistsAtPath:[self videoFileString]]) {
[[NSFileManager defaultManager] removeItemAtPath:[self videoFileString] error:&error];
}
if (error == nil) {
NSLog(@"[Camera] Delete file %@", [self videoFileString]);
}
}
}
- (void)stop {
if (self.running == YES) {
[super stop];
if (self.recordVideo == YES) {
if (self.recordAssetWriter) {
if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
[self.recordAssetWriter finishWritingWithCompletionHandler:^void() {
NSLog(@"[Camera] recording stopped");
}];
} else {
NSLog(@"[Camera] Recording Error: asset writer status is not writing");
}
}
}
if (self.customPreviewLayer) {
[self.customPreviewLayer removeFromSuperlayer];
self.customPreviewLayer = nil;
}
}
}
// TODO fix
- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
NSLog(@"layout preview layer");
if (self.parentView != nil) {
CALayer* layer = self.customPreviewLayer;
CGRect bounds = self.customPreviewLayer.bounds;
int rotation_angle = 0;
bool flip_bounds = false;
switch (interfaceOrientation) {
case UIInterfaceOrientationPortrait:
NSLog(@"to Portrait");
rotation_angle = 270;
break;
case UIInterfaceOrientationPortraitUpsideDown:
rotation_angle = 90;
NSLog(@"to UpsideDown");
break;
case UIInterfaceOrientationLandscapeLeft:
rotation_angle = 0;
NSLog(@"to LandscapeLeft");
break;
case UIInterfaceOrientationLandscapeRight:
rotation_angle = 180;
NSLog(@"to LandscapeRight");
break;
default:
break; // leave the layer in its last known orientation
}
switch (self.defaultAVCaptureVideoOrientation) {
case AVCaptureVideoOrientationLandscapeRight:
rotation_angle += 180;
break;
case AVCaptureVideoOrientationPortraitUpsideDown:
rotation_angle += 270;
break;
case AVCaptureVideoOrientationPortrait:
rotation_angle += 90;
case AVCaptureVideoOrientationLandscapeLeft:
break;
default:
break;
}
rotation_angle = rotation_angle % 360;
if (rotation_angle == 90 || rotation_angle == 270) {
flip_bounds = true;
}
if (flip_bounds) {
NSLog(@"flip bounds");
bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width);
}
layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.);
self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) );
layer.bounds = bounds;
}
}
// TODO fix
- (void)layoutPreviewLayer {
NSLog(@"layout preview layer");
if (self.parentView != nil) {
CALayer* layer = self.customPreviewLayer;
CGRect bounds = self.customPreviewLayer.bounds;
int rotation_angle = 0;
bool flip_bounds = false;
switch (self.currentDeviceOrientation) {
case UIDeviceOrientationPortrait:
rotation_angle = 270;
break;
case UIDeviceOrientationPortraitUpsideDown:
rotation_angle = 90;
break;
case UIDeviceOrientationLandscapeLeft:
NSLog(@"left");
rotation_angle = 180;
break;
case UIDeviceOrientationLandscapeRight:
NSLog(@"right");
rotation_angle = 0;
break;
case UIDeviceOrientationFaceUp:
case UIDeviceOrientationFaceDown:
default:
break; // leave the layer in its last known orientation
}
switch (self.defaultAVCaptureVideoOrientation) {
case AVCaptureVideoOrientationLandscapeRight:
rotation_angle += 180;
break;
case AVCaptureVideoOrientationPortraitUpsideDown:
rotation_angle += 270;
break;
case AVCaptureVideoOrientationPortrait:
rotation_angle += 90;
case AVCaptureVideoOrientationLandscapeLeft:
break;
default:
break;
}
rotation_angle = rotation_angle % 360;
if (rotation_angle == 90 || rotation_angle == 270) {
flip_bounds = true;
}
if (flip_bounds) {
NSLog(@"flip bounds");
bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width);
}
layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.);
layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) );
layer.bounds = bounds;
}
}
#pragma mark - Private Interface
- (void)createVideoDataOutput {
// Make a video data output
self.videoDataOutput = [AVCaptureVideoDataOutput new];
// In grayscale mode we want YUV (YpCbCr 4:2:0) so we can directly access the graylevel intensity values (Y component)
// In color mode we, BGRA format is used
OSType format = self.grayscaleMode ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA;
self.videoDataOutput.videoSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:format]
forKey:(id)kCVPixelBufferPixelFormatTypeKey];
// discard if the data output queue is blocked (as we process the still image)
[self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];
if ( [self.captureSession canAddOutput:self.videoDataOutput] ) {
[self.captureSession addOutput:self.videoDataOutput];
}
[[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo] setEnabled:YES];
// set default FPS
AVCaptureDeviceInput *currentInput = [self.captureSession.inputs objectAtIndex:0];
AVCaptureDevice *device = currentInput.device;
NSError *error = nil;
[device lockForConfiguration:&error];
float maxRate = ((AVFrameRateRange*) [device.activeFormat.videoSupportedFrameRateRanges objectAtIndex:0]).maxFrameRate;
if (maxRate > self.defaultFPS - 1 && error == nil) {
[device setActiveVideoMinFrameDuration:CMTimeMake(1, self.defaultFPS)];
[device setActiveVideoMaxFrameDuration:CMTimeMake(1, self.defaultFPS)];
NSLog(@"[Camera] FPS set to %d", self.defaultFPS);
} else {
NSLog(@"[Camera] unable to set defaultFPS at %d FPS, max is %f FPS", self.defaultFPS, maxRate);
}
if (error != nil) {
NSLog(@"[Camera] unable to set defaultFPS: %@", error);
}
[device unlockForConfiguration];
// set video mirroring for front camera (more intuitive)
if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMirroring) {
if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {
[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = YES;
} else {
[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = NO;
}
}
// set default video orientation
if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoOrientation) {
[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoOrientation = self.defaultAVCaptureVideoOrientation;
}
// create a custom preview layer
self.customPreviewLayer = [CALayer layer];
self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
self.customPreviewLayer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.);
[self updateOrientation];
// create a serial dispatch queue used for the sample buffer delegate as well as when a still image is captured
// a serial dispatch queue must be used to guarantee that video frames will be delivered in order
// see the header doc for setSampleBufferDelegate:queue: for more information
videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL);
[self.videoDataOutput setSampleBufferDelegate:self queue:videoDataOutputQueue];
NSLog(@"[Camera] created AVCaptureVideoDataOutput");
}
- (void)createVideoFileOutput {
/* Video File Output in H.264, via AVAsserWriter */
NSLog(@"Create Video with dimensions %dx%d", self.imageWidth, self.imageHeight);
NSDictionary *outputSettings
= [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:self.imageWidth], AVVideoWidthKey,
[NSNumber numberWithInt:self.imageHeight], AVVideoHeightKey,
AVVideoCodecTypeH264, AVVideoCodecKey,
nil
];
self.recordAssetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings];
int pixelBufferFormat = (self.grayscaleMode == YES) ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA;
self.recordPixelBufferAdaptor =
[[AVAssetWriterInputPixelBufferAdaptor alloc]
initWithAssetWriterInput:self.recordAssetWriterInput
sourcePixelBufferAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:pixelBufferFormat], kCVPixelBufferPixelFormatTypeKey, nil]];
NSError* error = nil;
NSLog(@"Create AVAssetWriter with url: %@", [self videoFileURL]);
self.recordAssetWriter = [AVAssetWriter assetWriterWithURL:[self videoFileURL]
fileType:AVFileTypeMPEG4
error:&error];
if (error != nil) {
NSLog(@"[Camera] Unable to create AVAssetWriter: %@", error);
}
[self.recordAssetWriter addInput:self.recordAssetWriterInput];
self.recordAssetWriterInput.expectsMediaDataInRealTime = YES;
NSLog(@"[Camera] created AVAssetWriter");
}
- (void)createCaptureOutput {
[self createVideoDataOutput];
if (self.recordVideo == YES) {
[self createVideoFileOutput];
}
}
- (void)createCustomVideoPreview {
[self.parentView.layer addSublayer:self.customPreviewLayer];
}
- (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image {
CGSize frameSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kCVPixelBufferCGImageCompatibilityKey,
[NSNumber numberWithBool:NO], kCVPixelBufferCGBitmapContextCompatibilityKey,
nil];
CVPixelBufferRef pxbuffer = NULL;
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, frameSize.width,
frameSize.height, kCVPixelFormatType_32ARGB, (CFDictionaryRef) CFBridgingRetain(options),
&pxbuffer);
NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata, frameSize.width,
frameSize.height, 8, 4*frameSize.width, rgbColorSpace,
kCGImageAlphaPremultipliedFirst);
CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image),
CGImageGetHeight(image)), image);
CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
return pxbuffer;
}
#pragma mark - Protocol AVCaptureVideoDataOutputSampleBufferDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
(void)captureOutput;
(void)connection;
auto strongDelegate = self.delegate;
if (strongDelegate) {
// convert from Core Media to Core Video
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(imageBuffer, 0);
void* bufferAddress;
size_t width;
size_t height;
size_t bytesPerRow;
CGColorSpaceRef colorSpace;
CGContextRef context;
int format_opencv;
OSType format = CVPixelBufferGetPixelFormatType(imageBuffer);
if (format == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
format_opencv = CV_8UC1;
bufferAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
width = CVPixelBufferGetWidthOfPlane(imageBuffer, 0);
height = CVPixelBufferGetHeightOfPlane(imageBuffer, 0);
bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0);
} else { // expect kCVPixelFormatType_32BGRA
format_opencv = CV_8UC4;
bufferAddress = CVPixelBufferGetBaseAddress(imageBuffer);
width = CVPixelBufferGetWidth(imageBuffer);
height = CVPixelBufferGetHeight(imageBuffer);
bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
}
// delegate image processing to the delegate
cv::Mat image((int)height, (int)width, format_opencv, bufferAddress, bytesPerRow);
CGImage* dstImage;
if ([strongDelegate respondsToSelector:@selector(processImage:)]) {
[strongDelegate processImage:[Mat fromNative:image]];
}
// check if matrix data pointer or dimensions were changed by the delegate
bool iOSimage = false;
if (height == (size_t)image.rows && width == (size_t)image.cols && format_opencv == image.type() && bufferAddress == image.data && bytesPerRow == image.step) {
iOSimage = true;
}
// (create color space, create graphics context, render buffer)
CGBitmapInfo bitmapInfo;
// basically we decide if it's a grayscale, rgb or rgba image
if (image.channels() == 1) {
colorSpace = CGColorSpaceCreateDeviceGray();
bitmapInfo = kCGImageAlphaNone;
} else if (image.channels() == 3) {
colorSpace = CGColorSpaceCreateDeviceRGB();
bitmapInfo = kCGImageAlphaNone;
if (iOSimage) {
bitmapInfo |= kCGBitmapByteOrder32Little;
} else {
bitmapInfo |= kCGBitmapByteOrder32Big;
}
} else {
colorSpace = CGColorSpaceCreateDeviceRGB();
bitmapInfo = kCGImageAlphaPremultipliedFirst;
if (iOSimage) {
bitmapInfo |= kCGBitmapByteOrder32Little;
} else {
bitmapInfo |= kCGBitmapByteOrder32Big;
}
}
if (iOSimage) {
context = CGBitmapContextCreate(bufferAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo);
dstImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
} else {
NSData *data = [NSData dataWithBytes:image.data length:image.elemSize()*image.total()];
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
// Creating CGImage from cv::Mat
dstImage = CGImageCreate(image.cols, // width
image.rows, // height
8, // bits per component
8 * image.elemSize(), // bits per pixel
image.step, // bytesPerRow
colorSpace, // colorspace
bitmapInfo, // bitmap info
provider, // CGDataProviderRef
NULL, // decode
false, // should interpolate
kCGRenderingIntentDefault // intent
);
CGDataProviderRelease(provider);
}
// render buffer
dispatch_sync(dispatch_get_main_queue(), ^{
self.customPreviewLayer.contents = (__bridge id)dstImage;
});
recordingCountDown--;
if (self.recordVideo == YES && recordingCountDown < 0) {
lastSampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
// CMTimeShow(lastSampleTime);
if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) {
[self.recordAssetWriter startWriting];
[self.recordAssetWriter startSessionAtSourceTime:lastSampleTime];
if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) {
NSLog(@"[Camera] Recording Error: asset writer status is not writing: %@", self.recordAssetWriter.error);
return;
} else {
NSLog(@"[Camera] Video recording started");
}
}
if (self.recordAssetWriterInput.readyForMoreMediaData) {
CVImageBufferRef pixelBuffer = [self pixelBufferFromCGImage:dstImage];
if (! [self.recordPixelBufferAdaptor appendPixelBuffer:pixelBuffer
withPresentationTime:lastSampleTime] ) {
NSLog(@"Video Writing Error");
}
if (pixelBuffer != nullptr)
CVPixelBufferRelease(pixelBuffer);
}
}
// cleanup
CGImageRelease(dstImage);
CGColorSpaceRelease(colorSpace);
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
}
}
- (void)updateOrientation {
if (self.rotateVideo == YES)
{
NSLog(@"rotate..");
self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
[self layoutPreviewLayer];
}
}
- (void)saveVideo {
if (self.recordVideo == NO) {
return;
}
UISaveVideoAtPathToSavedPhotosAlbum([self videoFileString], nil, nil, NULL);
}
- (NSURL *)videoFileURL {
NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:outputPath]) {
NSLog(@"file exists");
}
return outputURL;
}
- (NSString *)videoFileString {
NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
return outputPath;
}
@end
@@ -0,0 +1 @@
*.tar.xz
@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.13)
get_filename_component(OpenCV_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../.." ABSOLUTE)
include("${OpenCV_SOURCE_DIR}/cmake/OpenCVPluginStandalone.cmake")
# scan dependencies
set(WITH_FFMPEG ON)
set(OPENCV_FFMPEG_SKIP_BUILD_CHECK ON)
include("${OpenCV_SOURCE_DIR}/modules/videoio/cmake/init.cmake")
set(OPENCV_PLUGIN_DEPS core imgproc imgcodecs)
ocv_create_plugin(videoio "opencv_videoio_ffmpeg" "ocv.3rdparty.ffmpeg" "FFmpeg" "src/cap_ffmpeg.cpp")
message(STATUS "FFMPEG_libavcodec_VERSION=${FFMPEG_libavcodec_VERSION}")
message(STATUS "FFMPEG_libavformat_VERSION=${FFMPEG_libavformat_VERSION}")
message(STATUS "FFMPEG_libavutil_VERSION=${FFMPEG_libavutil_VERSION}")
message(STATUS "FFMPEG_libswscale_VERSION=${FFMPEG_libswscale_VERSION}")
if(OPENCV_FFMPEG_ENABLE_LIBAVDEVICE)
message(STATUS "FFMPEG_libavdevice_VERSION=${FFMPEG_libavdevice_VERSION}")
endif()
@@ -0,0 +1,45 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get --no-install-recommends install -y \
pkg-config \
cmake \
g++ \
ninja-build \
make \
nasm \
&& \
rm -rf /var/lib/apt/lists/*
ARG VER
ADD ffmpeg-${VER}.tar.xz /ffmpeg/
WORKDIR /ffmpeg/ffmpeg-${VER}
RUN ./configure \
--enable-avresample \
--prefix=/ffmpeg-shared \
--enable-shared \
--disable-static \
--disable-programs \
--disable-doc \
--disable-avdevice \
--disable-postproc \
&& make -j8 install \
&& make clean \
&& make distclean
RUN ./configure \
--enable-avresample \
--prefix=/ffmpeg-static \
--disable-shared \
--enable-static \
--enable-pic \
--disable-programs \
--disable-doc \
--disable-avdevice \
--disable-postproc \
&& make -j8 install \
&& make clean \
&& make distclean
WORKDIR /tmp
@@ -0,0 +1,17 @@
ARG VER
FROM ubuntu:$VER
RUN apt-get update && apt-get --no-install-recommends install -y \
libavcodec-dev \
libavfilter-dev \
libavformat-dev \
libavresample-dev \
libavutil-dev \
pkg-config \
cmake \
g++ \
ninja-build \
&& \
rm -rf /var/lib/apt/lists/*
WORKDIR /tmp
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
set -e
mkdir -p build_shared && pushd build_shared
PKG_CONFIG_PATH=/ffmpeg-shared/lib/pkgconfig \
cmake -GNinja \
-DOPENCV_PLUGIN_NAME=opencv_videoio_ffmpeg_shared_$2 \
-DOPENCV_PLUGIN_DESTINATION=$1 \
-DCMAKE_BUILD_TYPE=$3 \
/opencv/modules/videoio/misc/plugin_ffmpeg
ninja
popd
mkdir -p build_static && pushd build_static
PKG_CONFIG_PATH=/ffmpeg-static/lib/pkgconfig \
cmake -GNinja \
-DOPENCV_PLUGIN_NAME=opencv_videoio_ffmpeg_static_$2 \
-DOPENCV_PLUGIN_DESTINATION=$1 \
-DCMAKE_MODULE_LINKER_FLAGS=-Wl,-Bsymbolic \
-DCMAKE_BUILD_TYPE=$3 \
/opencv/modules/videoio/misc/plugin_ffmpeg
ninja
popd
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
set -e
cmake -GNinja \
-DOPENCV_PLUGIN_NAME=opencv_videoio_ffmpeg_ubuntu_$2 \
-DOPENCV_PLUGIN_DESTINATION=$1 \
-DCMAKE_BUILD_TYPE=$3 \
/opencv/modules/videoio/misc/plugin_ffmpeg
ninja
@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.13)
get_filename_component(OpenCV_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../.." ABSOLUTE)
include("${OpenCV_SOURCE_DIR}/cmake/OpenCVPluginStandalone.cmake")
# scan dependencies
set(WITH_GSTREAMER ON)
include("${OpenCV_SOURCE_DIR}/modules/videoio/cmake/init.cmake")
set(OPENCV_PLUGIN_DEPS core imgproc imgcodecs)
ocv_create_plugin(videoio "opencv_videoio_gstreamer" "ocv.3rdparty.gstreamer" "GStreamer" "src/cap_gstreamer.cpp")
message(STATUS "Using GStreamer: ${GSTREAMER_VERSION}")
@@ -0,0 +1,13 @@
FROM ubuntu:18.04
RUN apt-get update && apt-get --no-install-recommends install -y \
libgstreamer-plugins-base1.0-dev \
libgstreamer-plugins-good1.0-dev \
libgstreamer1.0-dev \
cmake \
g++ \
ninja-build \
&& \
rm -rf /var/lib/apt/lists/*
WORKDIR /tmp
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
set -e
cmake -GNinja \
-DOPENCV_PLUGIN_NAME=opencv_videoio_gstreamer \
-DOPENCV_PLUGIN_DESTINATION=$1 \
-DCMAKE_BUILD_TYPE=$2 \
/opencv/modules/videoio/misc/plugin_gstreamer
ninja
@@ -0,0 +1,174 @@
#ifdef HAVE_OPENCV_VIDEOIO
typedef std::vector<VideoCaptureAPIs> vector_VideoCaptureAPIs;
typedef std::vector<VideoCapture> vector_VideoCapture;
template<> struct pyopencvVecConverter<cv::VideoCaptureAPIs>
{
static bool to(PyObject* obj, std::vector<cv::VideoCaptureAPIs>& value, const ArgInfo& info)
{
return pyopencv_to_generic_vec(obj, value, info);
}
static PyObject* from(const std::vector<cv::VideoCaptureAPIs>& value)
{
return pyopencv_from_generic_vec(value);
}
};
template<>
bool pyopencv_to(PyObject *o, std::vector<cv::VideoCaptureAPIs>& apis, const ArgInfo& info)
{
return pyopencvVecConverter<cv::VideoCaptureAPIs>::to(o, apis, info);
}
template<> bool pyopencv_to(PyObject* obj, cv::VideoCapture& stream, const ArgInfo& info)
{
Ptr<VideoCapture> * obj_getp = nullptr;
if (!pyopencv_VideoCapture_getp(obj, obj_getp))
return (failmsgp("Incorrect type of self (must be 'VideoCapture' or its derivative)") != nullptr);
stream = **obj_getp;
return true;
}
class PythonStreamReader : public cv::IStreamReader
{
public:
PythonStreamReader(PyObject* _obj = nullptr) : obj(_obj)
{
if (obj)
Py_INCREF(obj);
}
~PythonStreamReader()
{
if (obj)
Py_DECREF(obj);
}
long long read(char* buffer, long long size) CV_OVERRIDE
{
if (!obj)
return 0;
PyObject* ioBase = reinterpret_cast<PyObject*>(obj);
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
PyObject* py_size = pyopencv_from(static_cast<int>(size));
PyObject* res = PyObject_CallMethodObjArgs(ioBase, PyString_FromString("read"), py_size, NULL);
bool hasPyReadError = PyErr_Occurred() != nullptr;
char* src = PyBytes_AsString(res);
size_t len = static_cast<size_t>(PyBytes_Size(res));
bool hasPyBytesError = PyErr_Occurred() != nullptr;
if (src && len <= static_cast<size_t>(size))
{
std::memcpy(buffer, src, len);
}
Py_DECREF(res);
Py_DECREF(py_size);
PyGILState_Release(gstate);
if (hasPyReadError)
CV_Error(cv::Error::StsError, "Python .read() call error");
if (hasPyBytesError)
CV_Error(cv::Error::StsError, "Python buffer access error");
CV_CheckLE(len, static_cast<size_t>(size), "Stream chunk size should be less or equal than requested size");
return len;
}
long long seek(long long offset, int way) CV_OVERRIDE
{
if (!obj)
return 0;
PyObject* ioBase = reinterpret_cast<PyObject*>(obj);
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
PyObject* py_offset = pyopencv_from(static_cast<int>(offset));
PyObject* py_whence = pyopencv_from(way);
PyObject* res = PyObject_CallMethodObjArgs(ioBase, PyString_FromString("seek"), py_offset, py_whence, NULL);
bool hasPySeekError = PyErr_Occurred() != nullptr;
long long pos = PyLong_AsLongLong(res);
bool hasPyConvertError = PyErr_Occurred() != nullptr;
Py_DECREF(res);
Py_DECREF(py_offset);
Py_DECREF(py_whence);
PyGILState_Release(gstate);
if (hasPySeekError)
CV_Error(cv::Error::StsError, "Python .seek() call error");
if (hasPyConvertError)
CV_Error(cv::Error::StsError, "Python .seek() result => long long conversion error");
return pos;
}
private:
PyObject* obj;
};
template<>
bool pyopencv_to(PyObject* obj, Ptr<cv::IStreamReader>& p, const ArgInfo&)
{
if (!obj)
return false;
PyObject* ioModule = PyImport_ImportModule("io");
PyObject* type = PyObject_GetAttrString(ioModule, "BufferedIOBase");
Py_DECREF(ioModule);
bool isValidPyType = PyObject_IsInstance(obj, type) == 1;
Py_DECREF(type);
if (!isValidPyType)
{
PyErr_SetString(PyExc_TypeError, "Input stream should be derived from io.BufferedIOBase");
return false;
}
if (!PyErr_Occurred()) {
p = makePtr<PythonStreamReader>(obj);
return true;
}
return false;
}
static PyObject* pycvVideoEnter(PyObject* self, PyObject* /*args*/, PyObject* /*kw*/) {
Py_INCREF(self);
return self;
}
static PyObject* pycvVideoCaptureExit(PyObject* self, PyObject* /*args*/, PyObject* /*kw*/) {
Ptr<cv::VideoCapture>* obj_getp = nullptr;
if (pyopencv_VideoCapture_getp(self, obj_getp) && obj_getp && *obj_getp) {
(*obj_getp)->release();
}
Py_RETURN_NONE;
}
static PyObject* pycvVideoWriterExit(PyObject* self, PyObject* /*args*/, PyObject* /*kw*/) {
Ptr<cv::VideoWriter>* obj_getp = nullptr;
if (pyopencv_VideoWriter_getp(self, obj_getp) && obj_getp && *obj_getp) {
(*obj_getp)->release();
}
Py_RETURN_NONE;
}
#define PYOPENCV_EXTRA_METHODS_VideoCapture \
{"__enter__", CV_PY_FN_WITH_KW(pycvVideoEnter), "Context manager enter"}, \
{"__exit__", CV_PY_FN_WITH_KW(pycvVideoCaptureExit), "Context manager exit"},
#define PYOPENCV_EXTRA_METHODS_VideoWriter \
{"__enter__", CV_PY_FN_WITH_KW(pycvVideoEnter), "Context manager enter"}, \
{"__exit__", CV_PY_FN_WITH_KW(pycvVideoWriterExit), "Context manager exit"},
#endif // HAVE_OPENCV_VIDEOIO
@@ -0,0 +1,110 @@
#!/usr/bin/env python
from __future__ import print_function
import numpy as np
import cv2 as cv
import io
import sys
import tempfile
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))
def test_capture_stream_file(self):
if sys.version_info[0] < 3:
raise self.skipTest('Python 3.x required')
api_pref = None
for backend in cv.videoio_registry.getStreamBufferedBackends():
if not cv.videoio_registry.hasBackend(backend):
continue
if not cv.videoio_registry.isBackendBuiltIn(backend):
_, abi, api = cv.videoio_registry.getStreamBufferedBackendPluginVersion(backend)
if (abi < 1 or (abi == 1 and api < 2)):
continue
api_pref = backend
break
if not api_pref:
raise self.skipTest("No available backends")
with open(self.find_file("cv/video/768x576.avi"), "rb") as f:
cap = cv.VideoCapture(f, api_pref, [])
self.assertTrue(cap.isOpened())
self.assertEqual(cv.CAP_PROP_UNKNOWN, cap.get(-1))
hasFrame, frame = cap.read()
self.assertTrue(hasFrame)
self.assertEqual(frame.shape, (576, 768, 3))
def test_capture_stream_buffer(self):
if sys.version_info[0] < 3:
raise self.skipTest('Python 3.x required')
api_pref = None
for backend in cv.videoio_registry.getStreamBufferedBackends():
if not cv.videoio_registry.hasBackend(backend):
continue
if not cv.videoio_registry.isBackendBuiltIn(backend):
_, abi, api = cv.videoio_registry.getStreamBufferedBackendPluginVersion(backend)
if (abi < 1 or (abi == 1 and api < 2)):
continue
api_pref = backend
break
if not api_pref:
raise self.skipTest("No available backends")
class BufferStream(io.BufferedIOBase):
def __init__(self, filepath):
self.f = open(filepath, "rb")
def read(self, size=-1):
return self.f.read(size)
def seek(self, offset, whence):
return self.f.seek(offset, whence)
def __del__(self):
self.f.close()
stream = BufferStream(self.find_file("cv/video/768x576.avi"))
cap = cv.VideoCapture(stream, api_pref, [])
self.assertTrue(cap.isOpened())
hasFrame, frame = cap.read()
self.assertTrue(hasFrame)
self.assertEqual(frame.shape, (576, 768, 3))
def test_context_manager(self):
video_file = self.find_file("cv/video/768x576.avi")
with cv.VideoCapture(video_file) as cap:
self.assertTrue(cap.isOpened(), "VideoCapture should be opened within context manager")
with tempfile.NamedTemporaryFile(suffix='.avi') as tmp:
with cv.VideoWriter(tmp.name, cv.VideoWriter_fourcc(*'MJPG'), 25, (640, 480)) as writer:
self.assertTrue(isinstance(writer, cv.VideoWriter))
try:
with cv.VideoCapture(video_file) as cap:
self.assertTrue(cap.isOpened())
raise RuntimeError("Testing context manager exception safety")
except RuntimeError:
pass
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
+76
View File
@@ -0,0 +1,76 @@
// 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
// Not a standalone header.
#include <opencv2/core/utils/configuration.private.hpp>
namespace opencv_test {
using namespace perf;
static
utils::Paths getTestCameras()
{
static utils::Paths cameras = utils::getConfigurationParameterPaths("OPENCV_TEST_PERF_CAMERA_LIST");
return cameras;
}
PERF_TEST(VideoCapture_Camera, waitAny_V4L)
{
auto cameraNames = getTestCameras();
if (cameraNames.empty())
throw SkipTestException("No list of tested cameras. Use OPENCV_TEST_PERF_CAMERA_LIST parameter");
const int totalFrames = 50; // number of expected frames (summary for all cameras)
const int64 timeoutNS = 100 * 1000000;
const Size frameSize(640, 480);
const int fpsDefaultEven = 30;
const int fpsDefaultOdd = 15;
std::vector<VideoCapture> cameras;
for (size_t i = 0; i < cameraNames.size(); ++i)
{
const auto& name = cameraNames[i];
int fps = (int)utils::getConfigurationParameterSizeT(cv::format("OPENCV_TEST_CAMERA%d_FPS", (int)i).c_str(), (i & 1) ? fpsDefaultOdd : fpsDefaultEven);
std::cout << "Camera[" << i << "] = '" << name << "', fps=" << fps << std::endl;
VideoCapture cap(name, CAP_V4L);
ASSERT_TRUE(cap.isOpened()) << name;
EXPECT_TRUE(cap.set(CAP_PROP_FRAME_WIDTH, frameSize.width)) << name;
EXPECT_TRUE(cap.set(CAP_PROP_FRAME_HEIGHT, frameSize.height)) << name;
EXPECT_TRUE(cap.set(CAP_PROP_FPS, fps)) << name;
//launch cameras
Mat firstFrame;
EXPECT_TRUE(cap.read(firstFrame));
EXPECT_EQ(frameSize.width, firstFrame.cols);
EXPECT_EQ(frameSize.height, firstFrame.rows);
cameras.push_back(cap);
}
TEST_CYCLE()
{
int counter = 0;
std::vector<int> cameraReady;
do
{
EXPECT_TRUE(VideoCapture::waitAny(cameras, cameraReady, timeoutNS));
EXPECT_FALSE(cameraReady.empty());
for (int idx : cameraReady)
{
VideoCapture& c = cameras[idx];
Mat frame;
ASSERT_TRUE(c.retrieve(frame));
EXPECT_EQ(frameSize.width, frame.cols);
EXPECT_EQ(frameSize.height, frame.rows);
++counter;
}
}
while(counter < totalFrames);
}
SANITY_CHECK_NOTHING();
}
} // namespace
+37
View File
@@ -0,0 +1,37 @@
// 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 "perf_precomp.hpp"
#include "perf_camera.impl.hpp"
namespace opencv_test
{
using namespace perf;
typedef perf::TestBaseWithParam<std::string> VideoCapture_Reading;
const string bunny_files[] = {
"highgui/video/big_buck_bunny.avi",
"highgui/video/big_buck_bunny.mov",
"highgui/video/big_buck_bunny.mp4",
#ifndef HAVE_MSMF
// MPEG2 is not supported by Media Foundation yet
// http://social.msdn.microsoft.com/Forums/en-US/mediafoundationdevelopment/thread/39a36231-8c01-40af-9af5-3c105d684429
"highgui/video/big_buck_bunny.mpg",
#endif
"highgui/video/big_buck_bunny.wmv"
};
PERF_TEST_P(VideoCapture_Reading, ReadFile, testing::ValuesIn(bunny_files) )
{
string filename = getDataPath(GetParam());
VideoCapture cap;
TEST_CYCLE() cap.open(filename);
SANITY_CHECK_NOTHING();
}
} // namespace
+10
View File
@@ -0,0 +1,10 @@
// 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 "perf_precomp.hpp"
#if defined(HAVE_HPX)
#include <hpx/hpx_main.hpp>
#endif
CV_PERF_TEST_MAIN(videoio)
+47
View File
@@ -0,0 +1,47 @@
// 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 "perf_precomp.hpp"
namespace opencv_test
{
using namespace perf;
typedef tuple<std::string, bool> VideoWriter_Writing_t;
typedef perf::TestBaseWithParam<VideoWriter_Writing_t> VideoWriter_Writing;
const string image_files[] = {
"python/images/QCIF_00.bmp",
"python/images/QCIF_01.bmp",
"python/images/QCIF_02.bmp",
"python/images/QCIF_03.bmp",
"python/images/QCIF_04.bmp",
"python/images/QCIF_05.bmp"
};
PERF_TEST_P(VideoWriter_Writing, WriteFrame,
testing::Combine(
testing::ValuesIn(image_files),
testing::Bool()))
{
const string filename = getDataPath(get<0>(GetParam()));
const bool isColor = get<1>(GetParam());
Mat image = imread(filename, isColor ? IMREAD_COLOR : IMREAD_GRAYSCALE );
#if defined(HAVE_MSMF) && !defined(HAVE_FFMPEG)
const string outfile = cv::tempfile(".wmv");
const int fourcc = VideoWriter::fourcc('W', 'M', 'V', '3');
#else
const string outfile = cv::tempfile(".avi");
const int fourcc = VideoWriter::fourcc('X', 'V', 'I', 'D');
#endif
VideoWriter writer(outfile, fourcc, 25, cv::Size(image.cols, image.rows), isColor);
if (!writer.isOpened())
throw SkipTestException("Video file can not be opened");
TEST_CYCLE_N(100) { writer << image; }
SANITY_CHECK_NOTHING();
remove(outfile.c_str());
}
} // namespace
+10
View File
@@ -0,0 +1,10 @@
// 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_PERF_PRECOMP_HPP__
#define __OPENCV_PERF_PRECOMP_HPP__
#include "opencv2/ts.hpp"
#include "opencv2/videoio.hpp"
#endif
+70
View File
@@ -0,0 +1,70 @@
// 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 BACKEND_HPP_DEFINED
#define BACKEND_HPP_DEFINED
#include "cap_interface.hpp"
#include "opencv2/videoio/registry.hpp"
namespace cv {
// TODO: move to public interface
// TODO: allow runtime backend registration
class IBackend
{
public:
virtual ~IBackend() {}
virtual Ptr<IVideoCapture> createCapture(int camera, const VideoCaptureParameters& params) const = 0;
virtual Ptr<IVideoCapture> createCapture(const std::string &filename, const VideoCaptureParameters& params) const = 0;
virtual Ptr<IVideoCapture> createCapture(const Ptr<IStreamReader>&stream, const VideoCaptureParameters& params) const = 0;
virtual Ptr<IVideoWriter> createWriter(const std::string& filename, int fourcc, double fps, const cv::Size& sz,
const VideoWriterParameters& params) const = 0;
};
class IBackendFactory
{
public:
virtual ~IBackendFactory() {}
virtual Ptr<IBackend> getBackend() const = 0;
virtual bool isBuiltIn() const = 0;
};
//=============================================================================
typedef Ptr<IVideoCapture> (*FN_createCaptureFile)(const std::string & filename);
typedef Ptr<IVideoCapture> (*FN_createCaptureCamera)(int camera);
typedef Ptr<IVideoCapture> (*FN_createCaptureStream)(const Ptr<IStreamReader>& stream);
typedef Ptr<IVideoCapture> (*FN_createCaptureFileWithParams)(const std::string & filename, const VideoCaptureParameters& params);
typedef Ptr<IVideoCapture> (*FN_createCaptureCameraWithParams)(int camera, const VideoCaptureParameters& params);
typedef Ptr<IVideoCapture> (*FN_createCaptureStreamWithParams)(const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params);
typedef Ptr<IVideoWriter> (*FN_createWriter)(const std::string& filename, int fourcc, double fps, const Size& sz,
const VideoWriterParameters& params);
Ptr<IBackendFactory> createBackendFactory(FN_createCaptureFile createCaptureFile,
FN_createCaptureCamera createCaptureCamera,
FN_createCaptureStream createCaptureStream,
FN_createWriter createWriter);
Ptr<IBackendFactory> createBackendFactory(FN_createCaptureFileWithParams createCaptureFile,
FN_createCaptureCameraWithParams createCaptureCamera,
FN_createCaptureStreamWithParams createCaptureStream,
FN_createWriter createWriter);
Ptr<IBackendFactory> createPluginBackendFactory(VideoCaptureAPIs id, const char* baseName);
void applyParametersFallback(const Ptr<IVideoCapture>& cap, const VideoCaptureParameters& params);
std::string getCapturePluginVersion(
const Ptr<IBackendFactory>& backend_factory,
CV_OUT int& version_ABI,
CV_OUT int& version_API
);
std::string getWriterPluginVersion(
const Ptr<IBackendFactory>& backend_factory,
CV_OUT int& version_ABI,
CV_OUT int& version_API
);
} // namespace cv::
#endif // BACKEND_HPP_DEFINED
+843
View File
@@ -0,0 +1,843 @@
// 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 = CAP_PROP_UNKNOWN;
if (plugin_api_->v0.Capture_getProperty)
if (CV_ERROR_OK != plugin_api_->v0.Capture_getProperty(capture_, prop, &val))
val = CAP_PROP_UNKNOWN;
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;
int depth = CV_MAT_DEPTH(type);
// [TODO] Remove this condition after rebuilding plugins or add a new
// version of plugins. Convert type from the old one to the new one (5 bits)
if (depth > 7) {
type = CV_MAKETYPE((type & 7), (type >> 3) + 1);
}
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 = CAP_PROP_UNKNOWN;
if (plugin_api_->v0.Writer_getProperty)
if (CV_ERROR_OK != plugin_api_->v0.Writer_getProperty(writer_, prop, &val))
val = CAP_PROP_UNKNOWN;
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
}
bool 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 << "'");
return false;
}
return true;
}
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
@@ -0,0 +1,200 @@
// 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.
//
// Not a standalone header.
//
namespace cv { namespace impl { namespace legacy {
//==================================================================================================
class PluginCapture : public cv::IVideoCapture
{
const OpenCV_VideoIO_Plugin_API_preview* plugin_api_;
CvPluginCapture capture_;
public:
static
Ptr<PluginCapture> create(const OpenCV_VideoIO_Plugin_API_preview* plugin_api,
const std::string &filename, int camera)
{
CV_Assert(plugin_api);
CvPluginCapture capture = NULL;
if (plugin_api->v0.Capture_open)
{
CV_Assert(plugin_api->v0.Capture_release);
if (CV_ERROR_OK == plugin_api->v0.Capture_open(filename.empty() ? 0 : filename.c_str(), camera, &capture))
{
CV_Assert(capture);
return makePtr<PluginCapture>(plugin_api, capture);
}
}
return Ptr<PluginCapture>();
}
PluginCapture(const OpenCV_VideoIO_Plugin_API_preview* plugin_api, CvPluginCapture capture)
: plugin_api_(plugin_api), capture_(capture)
{
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 = CAP_PROP_UNKNOWN;
if (plugin_api_->v0.Capture_getProperty)
if (CV_ERROR_OK != plugin_api_->v0.Capture_getProperty(capture_, prop, &val))
val = CAP_PROP_UNKNOWN;
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 cn, 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), CV_MAKETYPE(CV_8U, cn), (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.captureAPI;
}
};
//==================================================================================================
class PluginWriter : public cv::IVideoWriter
{
const OpenCV_VideoIO_Plugin_API_preview* plugin_api_;
CvPluginWriter writer_;
public:
static
Ptr<PluginWriter> create(const OpenCV_VideoIO_Plugin_API_preview* plugin_api,
const std::string& filename, int fourcc, double fps, const cv::Size& sz,
const VideoWriterParameters& params)
{
CV_Assert(plugin_api);
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);
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)
{
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)
{
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 (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_Plugin_API_preview* 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 = CAP_PROP_UNKNOWN;
if (plugin_api_->v0.Writer_getProperty)
if (CV_ERROR_OK != plugin_api_->v0.Writer_getProperty(writer_, prop, &val))
val = CAP_PROP_UNKNOWN;
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
}
bool 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 << "'");
return false;
}
return true;
}
int getCaptureDomain() const CV_OVERRIDE
{
return plugin_api_->v0.captureAPI;
}
};
}}} // namespace
+206
View File
@@ -0,0 +1,206 @@
// 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"
namespace cv {
void applyParametersFallback(const Ptr<IVideoCapture>& cap, const VideoCaptureParameters& params)
{
std::vector<int> props = params.getUnused();
CV_LOG_INFO(NULL, "VIDEOIO: Backend '" << videoio_registry::getBackendName((VideoCaptureAPIs)cap->getCaptureDomain()) <<
"' implementation doesn't support parameters in .open(). Applying " <<
props.size() << " properties through .setProperty()");
for (int prop : props)
{
double value = params.get<double>(prop, -1);
CV_LOG_INFO(NULL, "VIDEOIO: apply parameter: [" << prop << "]=" <<
cv::format("%g / %lld / 0x%016llx", value, (long long)value, (long long)value));
if (!cap->setProperty(prop, value))
{
if (prop != CAP_PROP_HW_ACCELERATION && prop != CAP_PROP_HW_DEVICE) { // optional parameters
CV_Error_(cv::Error::StsNotImplemented, ("VIDEOIO: Failed to apply invalid or unsupported parameter: [%d]=%g / %lld / 0x%08llx", prop, value, (long long)value, (long long)value));
}
}
}
// NB: there is no dedicated "commit" parameters event, implementations should commit after each property automatically
}
// Legacy API. Modern API with parameters is below
class StaticBackend: public IBackend
{
public:
FN_createCaptureFile fn_createCaptureFile_;
FN_createCaptureCamera fn_createCaptureCamera_;
FN_createCaptureStream fn_createCaptureStream_;
FN_createWriter fn_createWriter_;
StaticBackend(FN_createCaptureFile fn_createCaptureFile, FN_createCaptureCamera fn_createCaptureCamera, FN_createCaptureStream fn_createCaptureStream, FN_createWriter fn_createWriter)
: fn_createCaptureFile_(fn_createCaptureFile), fn_createCaptureCamera_(fn_createCaptureCamera), fn_createCaptureStream_(fn_createCaptureStream), fn_createWriter_(fn_createWriter)
{
// nothing
}
~StaticBackend() CV_OVERRIDE {}
Ptr<IVideoCapture> createCapture(int camera, const VideoCaptureParameters& params) const CV_OVERRIDE
{
if (fn_createCaptureCamera_)
{
Ptr<IVideoCapture> cap = fn_createCaptureCamera_(camera);
if (cap && !params.empty())
{
applyParametersFallback(cap, params);
}
return cap;
}
return Ptr<IVideoCapture>();
}
Ptr<IVideoCapture> createCapture(const std::string &filename, const VideoCaptureParameters& params) const CV_OVERRIDE
{
if (fn_createCaptureFile_)
{
Ptr<IVideoCapture> cap = fn_createCaptureFile_(filename);
if (cap && !params.empty())
{
applyParametersFallback(cap, params);
}
return cap;
}
return Ptr<IVideoCapture>();
}
Ptr<IVideoCapture> createCapture(const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params) const CV_OVERRIDE
{
if (fn_createCaptureStream_)
{
Ptr<IVideoCapture> cap = fn_createCaptureStream_(stream);
if (cap && !params.empty())
{
applyParametersFallback(cap, params);
}
return cap;
}
return Ptr<IVideoCapture>();
}
Ptr<IVideoWriter> createWriter(const std::string& filename, int fourcc, double fps,
const cv::Size& sz, const VideoWriterParameters& params) const CV_OVERRIDE
{
if (fn_createWriter_)
return fn_createWriter_(filename, fourcc, fps, sz, params);
return Ptr<IVideoWriter>();
}
}; // StaticBackend
class StaticBackendFactory : public IBackendFactory
{
protected:
Ptr<StaticBackend> backend;
public:
StaticBackendFactory(FN_createCaptureFile createCaptureFile, FN_createCaptureCamera createCaptureCamera, FN_createCaptureStream createCaptureStream, FN_createWriter createWriter)
: backend(makePtr<StaticBackend>(createCaptureFile, createCaptureCamera, createCaptureStream, createWriter))
{
// nothing
}
~StaticBackendFactory() CV_OVERRIDE {}
Ptr<IBackend> getBackend() const CV_OVERRIDE
{
return backend.staticCast<IBackend>();
}
bool isBuiltIn() const CV_OVERRIDE { return true; }
};
Ptr<IBackendFactory> createBackendFactory(FN_createCaptureFile createCaptureFile,
FN_createCaptureCamera createCaptureCamera,
FN_createCaptureStream createCaptureStream,
FN_createWriter createWriter)
{
return makePtr<StaticBackendFactory>(createCaptureFile, createCaptureCamera, createCaptureStream, createWriter).staticCast<IBackendFactory>();
}
class StaticBackendWithParams: public IBackend
{
public:
FN_createCaptureFileWithParams fn_createCaptureFile_;
FN_createCaptureCameraWithParams fn_createCaptureCamera_;
FN_createCaptureStreamWithParams fn_createCaptureStream_;
FN_createWriter fn_createWriter_;
StaticBackendWithParams(FN_createCaptureFileWithParams fn_createCaptureFile, FN_createCaptureCameraWithParams fn_createCaptureCamera, FN_createCaptureStreamWithParams fn_createCaptureStream, FN_createWriter fn_createWriter)
: fn_createCaptureFile_(fn_createCaptureFile), fn_createCaptureCamera_(fn_createCaptureCamera), fn_createCaptureStream_(fn_createCaptureStream), fn_createWriter_(fn_createWriter)
{
// nothing
}
~StaticBackendWithParams() CV_OVERRIDE {}
Ptr<IVideoCapture> createCapture(int camera, const VideoCaptureParameters& params) const CV_OVERRIDE
{
if (fn_createCaptureCamera_)
return fn_createCaptureCamera_(camera, params);
return Ptr<IVideoCapture>();
}
Ptr<IVideoCapture> createCapture(const std::string &filename, const VideoCaptureParameters& params) const CV_OVERRIDE
{
if (fn_createCaptureFile_)
return fn_createCaptureFile_(filename, params);
return Ptr<IVideoCapture>();
}
Ptr<IVideoCapture> createCapture(const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params) const CV_OVERRIDE
{
if (fn_createCaptureStream_)
return fn_createCaptureStream_(stream, params);
return Ptr<IVideoCapture>();
}
Ptr<IVideoWriter> createWriter(const std::string& filename, int fourcc, double fps,
const cv::Size& sz, const VideoWriterParameters& params) const CV_OVERRIDE
{
if (fn_createWriter_)
return fn_createWriter_(filename, fourcc, fps, sz, params);
return Ptr<IVideoWriter>();
}
}; // StaticBackendWithParams
class StaticBackendWithParamsFactory : public IBackendFactory
{
protected:
Ptr<StaticBackendWithParams> backend;
public:
StaticBackendWithParamsFactory(FN_createCaptureFileWithParams createCaptureFile, FN_createCaptureCameraWithParams createCaptureCamera, FN_createCaptureStreamWithParams createCaptureStream, FN_createWriter createWriter)
: backend(makePtr<StaticBackendWithParams>(createCaptureFile, createCaptureCamera, createCaptureStream, createWriter))
{
// nothing
}
~StaticBackendWithParamsFactory() CV_OVERRIDE {}
Ptr<IBackend> getBackend() const CV_OVERRIDE
{
return backend.staticCast<IBackend>();
}
bool isBuiltIn() const CV_OVERRIDE { return true; }
};
Ptr<IBackendFactory> createBackendFactory(FN_createCaptureFileWithParams createCaptureFile,
FN_createCaptureCameraWithParams createCaptureCamera,
FN_createCaptureStreamWithParams createCaptureStream,
FN_createWriter createWriter)
{
return makePtr<StaticBackendWithParamsFactory>(createCaptureFile, createCaptureCamera, createCaptureStream, createWriter).staticCast<IBackendFactory>();
}
} // namespace
+897
View File
@@ -0,0 +1,897 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include "opencv2/videoio/registry.hpp"
#include "videoio_registry.hpp"
namespace cv {
static bool param_VIDEOIO_DEBUG = utils::getConfigurationParameterBool("OPENCV_VIDEOIO_DEBUG", false);
static bool param_VIDEOCAPTURE_DEBUG = utils::getConfigurationParameterBool("OPENCV_VIDEOCAPTURE_DEBUG", false);
static bool param_VIDEOWRITER_DEBUG = utils::getConfigurationParameterBool("OPENCV_VIDEOWRITER_DEBUG", false);
#define CV_CAPTURE_LOG_DEBUG(tag, ...) \
if (param_VIDEOIO_DEBUG || param_VIDEOCAPTURE_DEBUG) \
{ \
CV_LOG_WARNING(nullptr, __VA_ARGS__); \
}
#define CV_WRITER_LOG_DEBUG(tag, ...) \
if (param_VIDEOIO_DEBUG || param_VIDEOWRITER_DEBUG) \
{ \
CV_LOG_WARNING(nullptr, __VA_ARGS__) \
}
IStreamReader::~IStreamReader()
{
// nothing
}
VideoCapture::VideoCapture() : throwOnFail(false)
{}
VideoCapture::VideoCapture(const String& filename, int apiPreference) : throwOnFail(false)
{
CV_TRACE_FUNCTION();
open(filename, apiPreference);
}
VideoCapture::VideoCapture(const String& filename, int apiPreference, const std::vector<int>& params)
: throwOnFail(false)
{
CV_TRACE_FUNCTION();
open(filename, apiPreference, params);
}
VideoCapture::VideoCapture(const Ptr<IStreamReader>& source, int apiPreference, const std::vector<int>& params)
: throwOnFail(false)
{
CV_TRACE_FUNCTION();
open(source, apiPreference, params);
}
VideoCapture::VideoCapture(int index, int apiPreference) : throwOnFail(false)
{
CV_TRACE_FUNCTION();
open(index, apiPreference);
}
VideoCapture::VideoCapture(int index, int apiPreference, const std::vector<int>& params)
: throwOnFail(false)
{
CV_TRACE_FUNCTION();
open(index, apiPreference, params);
}
VideoCapture::~VideoCapture()
{
CV_TRACE_FUNCTION();
icap.release();
}
bool VideoCapture::open(const String& filename, int apiPreference)
{
return open(filename, apiPreference, std::vector<int>());
}
bool VideoCapture::open(const String& filename, int apiPreference, const std::vector<int>& params)
{
CV_INSTRUMENT_REGION();
if (isOpened())
{
release();
}
const VideoCaptureParameters parameters(params);
const std::vector<VideoBackendInfo> backends = cv::videoio_registry::getAvailableBackends_CaptureByFilename();
for (size_t i = 0; i < backends.size(); i++)
{
const VideoBackendInfo& info = backends[i];
if (apiPreference == CAP_ANY || apiPreference == info.id)
{
if (!info.backendFactory)
{
CV_LOG_DEBUG(NULL, "VIDEOIO(" << info.name << "): factory is not available (plugins require filesystem support)");
continue;
}
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): trying capture filename='%s' ...",
info.name, filename.c_str()));
CV_Assert(!info.backendFactory.empty());
const Ptr<IBackend> backend = info.backendFactory->getBackend();
if (!backend.empty())
{
try
{
icap = backend->createCapture(filename, parameters);
if (!icap.empty())
{
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): created, isOpened=%d",
info.name, icap->isOpened()));
if (icap->isOpened())
{
return true;
}
icap.release();
}
else
{
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): can't create capture",
info.name));
}
}
catch (const cv::Exception& e)
{
if (throwOnFail && apiPreference != CAP_ANY)
{
throw;
}
CV_LOG_WARNING(NULL,
cv::format("VIDEOIO(%s): raised OpenCV exception:\n\n%s\n",
info.name, e.what()));
}
catch (const std::exception& e)
{
if (throwOnFail && apiPreference != CAP_ANY)
{
throw;
}
CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): raised C++ exception:\n\n%s\n",
info.name, e.what()));
}
catch (...)
{
if (throwOnFail && apiPreference != CAP_ANY)
{
throw;
}
CV_LOG_WARNING(NULL,
cv::format("VIDEOIO(%s): raised unknown C++ exception!\n\n",
info.name));
}
}
else
{
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): backend is not available "
"(plugin is missing, or can't be loaded due "
"dependencies or it is not compatible)",
info.name));
}
}
}
if(apiPreference != CAP_ANY)
{
bool found = cv::videoio_registry::isBackendBuiltIn(static_cast<VideoCaptureAPIs>(apiPreference));
if (found)
{
CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): backend is generally available "
"but can't be used to capture by name",
cv::videoio_registry::getBackendName(static_cast<VideoCaptureAPIs>(apiPreference)).c_str()));
}
}
if (throwOnFail)
{
CV_Error_(Error::StsError, ("could not open '%s'", filename.c_str()));
}
if (cv::videoio_registry::checkDeprecatedBackend(apiPreference))
{
CV_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): backend is removed from OpenCV",
cv::videoio_registry::getBackendName((VideoCaptureAPIs) apiPreference).c_str()));
}
else
{
CV_LOG_DEBUG(NULL, "VIDEOIO: chosen backend does not work or wrong. "
"Please make sure that your computer support chosen backend and OpenCV built "
"with right flags.");
}
return false;
}
bool VideoCapture::open(const Ptr<IStreamReader>& stream, int apiPreference, const std::vector<int>& params)
{
CV_INSTRUMENT_REGION();
if (apiPreference == CAP_ANY)
{
CV_Error_(Error::StsBadArg, ("Avoid CAP_ANY - explicit backend expected to avoid read data stream reset"));
}
if (isOpened())
{
release();
}
const VideoCaptureParameters parameters(params);
const std::vector<VideoBackendInfo> backends = cv::videoio_registry::getAvailableBackends_CaptureByStream();
for (size_t i = 0; i < backends.size(); i++)
{
const VideoBackendInfo& info = backends[i];
if (apiPreference != info.id)
continue;
if (!info.backendFactory)
{
CV_LOG_DEBUG(NULL, "VIDEOIO(" << info.name << "): factory is not available (plugins require filesystem support)");
continue;
}
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): trying capture buffer ...",
info.name));
CV_Assert(!info.backendFactory.empty());
const Ptr<IBackend> backend = info.backendFactory->getBackend();
if (!backend.empty())
{
try
{
icap = backend->createCapture(stream, parameters);
if (!icap.empty())
{
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): created, isOpened=%d",
info.name, icap->isOpened()));
if (icap->isOpened())
{
return true;
}
icap.release();
}
else
{
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): can't create capture",
info.name));
}
}
catch (const cv::Exception& e)
{
if (throwOnFail)
{
throw;
}
CV_LOG_WARNING(NULL,
cv::format("VIDEOIO(%s): raised OpenCV exception:\n\n%s\n",
info.name, e.what()));
}
catch (const std::exception& e)
{
if (throwOnFail)
{
throw;
}
CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): raised C++ exception:\n\n%s\n",
info.name, e.what()));
}
catch (...)
{
if (throwOnFail)
{
throw;
}
CV_LOG_WARNING(NULL,
cv::format("VIDEOIO(%s): raised unknown C++ exception!\n\n",
info.name));
}
}
else
{
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): backend is not available "
"(plugin is missing, or can't be loaded due "
"dependencies or it is not compatible)",
info.name));
}
}
bool found = cv::videoio_registry::isBackendBuiltIn(static_cast<VideoCaptureAPIs>(apiPreference));
if (found)
{
CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): backend is generally available "
"but can't be used to capture by read data stream",
cv::videoio_registry::getBackendName(static_cast<VideoCaptureAPIs>(apiPreference)).c_str()));
}
if (throwOnFail)
{
CV_Error_(Error::StsError, ("could not open read data stream"));
}
if (cv::videoio_registry::checkDeprecatedBackend(apiPreference))
{
CV_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): backend is removed from OpenCV",
cv::videoio_registry::getBackendName((VideoCaptureAPIs) apiPreference).c_str()));
}
else
{
CV_LOG_DEBUG(NULL, "VIDEOIO: chosen backend does not work or wrong. "
"Please make sure that your computer support chosen backend and OpenCV built "
"with right flags.");
}
return false;
}
bool VideoCapture::open(int cameraNum, int apiPreference)
{
return open(cameraNum, apiPreference, std::vector<int>());
}
bool VideoCapture::open(int cameraNum, int apiPreference, const std::vector<int>& params)
{
CV_TRACE_FUNCTION();
if (isOpened())
{
release();
}
if (apiPreference == CAP_ANY)
{
// interpret preferred interface (0 = autodetect)
int backendID = (cameraNum / 100) * 100;
if (backendID)
{
cameraNum %= 100;
apiPreference = backendID;
}
}
const VideoCaptureParameters parameters(params);
const std::vector<VideoBackendInfo> backends = cv::videoio_registry::getAvailableBackends_CaptureByIndex();
for (size_t i = 0; i < backends.size(); i++)
{
const VideoBackendInfo& info = backends[i];
if (apiPreference == CAP_ANY || apiPreference == info.id)
{
if (!info.backendFactory)
{
CV_LOG_DEBUG(NULL, "VIDEOIO(" << info.name << "): factory is not available (plugins require filesystem support)");
continue;
}
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): trying capture cameraNum=%d ...",
info.name, cameraNum));
CV_Assert(!info.backendFactory.empty());
const Ptr<IBackend> backend = info.backendFactory->getBackend();
if (!backend.empty())
{
try
{
icap = backend->createCapture(cameraNum, parameters);
if (!icap.empty())
{
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): created, isOpened=%d",
info.name, icap->isOpened()));
if (icap->isOpened())
{
return true;
}
icap.release();
}
else
{
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): can't create capture",
info.name));
}
}
catch (const cv::Exception& e)
{
if (throwOnFail && apiPreference != CAP_ANY)
{
throw;
}
CV_LOG_WARNING(NULL,
cv::format("VIDEOIO(%s): raised OpenCV exception:\n\n%s\n",
info.name, e.what()));
}
catch (const std::exception& e)
{
if (throwOnFail && apiPreference != CAP_ANY)
{
throw;
}
CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): raised C++ exception:\n\n%s\n",
info.name, e.what()));
}
catch (...)
{
if (throwOnFail && apiPreference != CAP_ANY)
{
throw;
}
CV_LOG_WARNING(NULL,
cv::format("VIDEOIO(%s): raised unknown C++ exception!\n\n",
info.name));
}
}
else
{
CV_CAPTURE_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): backend is not available "
"(plugin is missing, or can't be loaded due "
"dependencies or it is not compatible)",
info.name));
}
}
}
if(apiPreference != CAP_ANY)
{
bool found = cv::videoio_registry::isBackendBuiltIn(static_cast<VideoCaptureAPIs>(apiPreference));
if (found)
{
CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): backend is generally available "
"but can't be used to capture by index",
cv::videoio_registry::getBackendName(static_cast<VideoCaptureAPIs>(apiPreference)).c_str()));
}
}
if (throwOnFail)
{
CV_Error_(Error::StsError, ("could not open camera %d", cameraNum));
}
if (cv::videoio_registry::checkDeprecatedBackend(apiPreference))
{
CV_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): backend is removed from OpenCV",
cv::videoio_registry::getBackendName((VideoCaptureAPIs) apiPreference).c_str()));
}
else
{
CV_LOG_DEBUG(NULL, "VIDEOIO: chosen backend does not work or wrong."
"Please make sure that your computer support chosen backend and OpenCV built "
"with right flags.");
}
return false;
}
bool VideoCapture::isOpened() const
{
return !icap.empty() ? icap->isOpened() : false;
}
String VideoCapture::getBackendName() const
{
int api = 0;
if (icap)
{
api = icap->isOpened() ? icap->getCaptureDomain() : 0;
}
CV_Assert(api != 0);
return cv::videoio_registry::getBackendName(static_cast<VideoCaptureAPIs>(api));
}
void VideoCapture::release()
{
CV_TRACE_FUNCTION();
icap.release();
}
bool VideoCapture::grab()
{
CV_INSTRUMENT_REGION();
bool ret = !icap.empty() ? icap->grabFrame() : false;
if (!ret && throwOnFail)
{
CV_Error(Error::StsError, "");
}
return ret;
}
bool VideoCapture::retrieve(OutputArray image, int channel)
{
CV_INSTRUMENT_REGION();
bool ret = false;
if (!icap.empty())
{
ret = icap->retrieveFrame(channel, image);
}
if (!ret && throwOnFail)
{
CV_Error_(Error::StsError, ("could not retrieve channel %d", channel));
}
return ret;
}
bool VideoCapture::read(OutputArray image)
{
CV_INSTRUMENT_REGION();
if (grab())
{
retrieve(image);
} else {
image.release();
}
return !image.empty();
}
VideoCapture& VideoCapture::operator >> (Mat& image)
{
#ifdef WINRT_VIDEO
// FIXIT grab/retrieve methods() should work too
if (grab())
{
if (retrieve(image))
{
std::lock_guard<std::mutex> lock(VideoioBridge::getInstance().inputBufferMutex);
VideoioBridge& bridge = VideoioBridge::getInstance();
// double buffering
bridge.swapInputBuffers();
auto p = bridge.frontInputPtr;
bridge.bIsFrameNew = false;
// needed here because setting Mat 'image' is not allowed by OutputArray in read()
Mat m(bridge.getHeight(), bridge.getWidth(), CV_8UC3, p);
image = m;
}
}
#else
read(image);
#endif
return *this;
}
VideoCapture& VideoCapture::operator >> (UMat& image)
{
CV_INSTRUMENT_REGION();
read(image);
return *this;
}
bool VideoCapture::set(int propId, double value)
{
CV_CheckNE(propId, (int)CAP_PROP_BACKEND, "Can't set read-only property");
bool ret = !icap.empty() ? icap->setProperty(propId, value) : false;
if (!ret && throwOnFail)
{
CV_Error_(Error::StsError, ("could not set prop %d = %f", propId, value));
}
return ret;
}
double VideoCapture::get(int propId) const
{
if (propId == CAP_PROP_BACKEND)
{
int api = 0;
if (icap && icap->isOpened())
{
api = icap->getCaptureDomain();
}
if (api <= 0)
{
return CAP_PROP_UNKNOWN;
}
return static_cast<double>(api);
}
return !icap.empty() ? icap->getProperty(propId) : static_cast<double>(CAP_PROP_UNKNOWN);
}
bool VideoCapture::waitAny(const std::vector<VideoCapture>& streams,
CV_OUT std::vector<int>& readyIndex, int64 timeoutNs)
{
CV_Assert(!streams.empty());
VideoCaptureAPIs backend = (VideoCaptureAPIs)streams[0].icap->getCaptureDomain();
for (size_t i = 1; i < streams.size(); ++i)
{
VideoCaptureAPIs backend_i = (VideoCaptureAPIs)streams[i].icap->getCaptureDomain();
CV_CheckEQ((int)backend, (int)backend_i, "All captures must have the same backend");
}
#if (defined HAVE_CAMV4L2 || defined HAVE_VIDEOIO) // see cap_v4l.cpp guard
if (backend == CAP_V4L2)
{
return VideoCapture_V4L_waitAny(streams, readyIndex, timeoutNs);
}
#else
CV_UNUSED(readyIndex);
CV_UNUSED(timeoutNs);
#endif
CV_Error(Error::StsNotImplemented, "VideoCapture::waitAny() is supported by V4L backend only");
}
//=================================================================================================
VideoWriter::VideoWriter()
{}
VideoWriter::VideoWriter(const String& filename, int _fourcc, double fps, Size frameSize,
bool isColor)
{
open(filename, _fourcc, fps, frameSize, isColor);
}
VideoWriter::VideoWriter(const String& filename, int apiPreference, int _fourcc, double fps,
Size frameSize, bool isColor)
{
open(filename, apiPreference, _fourcc, fps, frameSize, isColor);
}
VideoWriter::VideoWriter(const cv::String& filename, int fourcc, double fps,
const cv::Size& frameSize, const std::vector<int>& params)
{
open(filename, fourcc, fps, frameSize, params);
}
VideoWriter::VideoWriter(const cv::String& filename, int apiPreference, int fourcc, double fps,
const cv::Size& frameSize, const std::vector<int>& params)
{
open(filename, apiPreference, fourcc, fps, frameSize, params);
}
void VideoWriter::release()
{
iwriter.release();
}
VideoWriter::~VideoWriter()
{
release();
}
bool VideoWriter::open(const String& filename, int _fourcc, double fps, Size frameSize,
bool isColor)
{
return open(filename, CAP_ANY, _fourcc, fps, frameSize,
std::vector<int> { VIDEOWRITER_PROP_IS_COLOR, static_cast<int>(isColor) });
}
bool VideoWriter::open(const String& filename, int apiPreference, int _fourcc, double fps,
Size frameSize, bool isColor)
{
return open(filename, apiPreference, _fourcc, fps, frameSize,
std::vector<int> { VIDEOWRITER_PROP_IS_COLOR, static_cast<int>(isColor) });
}
bool VideoWriter::open(const String& filename, int fourcc, double fps, const Size& frameSize,
const std::vector<int>& params)
{
return open(filename, CAP_ANY, fourcc, fps, frameSize, params);
}
bool VideoWriter::open(const String& filename, int apiPreference, int fourcc, double fps,
const Size& frameSize, const std::vector<int>& params)
{
CV_INSTRUMENT_REGION();
if (isOpened())
{
release();
}
const VideoWriterParameters parameters(params);
for (const auto& info : videoio_registry::getAvailableBackends_Writer())
{
if (apiPreference == CAP_ANY || apiPreference == info.id)
{
CV_WRITER_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): trying writer with filename='%s' "
"fourcc=0x%08x fps=%g sz=%dx%d isColor=%d...",
info.name, filename.c_str(), (unsigned)fourcc, fps,
frameSize.width, frameSize.height,
parameters.get(VIDEOWRITER_PROP_IS_COLOR, true)));
CV_Assert(!info.backendFactory.empty());
const Ptr<IBackend> backend = info.backendFactory->getBackend();
if (!backend.empty())
{
try
{
iwriter = backend->createWriter(filename, fourcc, fps, frameSize, parameters);
if (!iwriter.empty())
{
CV_WRITER_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): created, isOpened=%d",
info.name, iwriter->isOpened()));
if (param_VIDEOIO_DEBUG || param_VIDEOWRITER_DEBUG)
{
for (int key: parameters.getUnused())
{
CV_LOG_WARNING(NULL,
cv::format("VIDEOIO(%s): parameter with key '%d' was unused",
info.name, key));
}
}
if (iwriter->isOpened())
{
return true;
}
iwriter.release();
}
else
{
CV_WRITER_LOG_DEBUG(NULL, cv::format("VIDEOIO(%s): can't create writer",
info.name));
}
}
catch (const cv::Exception& e)
{
CV_LOG_WARNING(NULL,
cv::format("VIDEOIO(%s): raised OpenCV exception:\n\n%s\n",
info.name, e.what()));
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): raised C++ exception:\n\n%s\n",
info.name, e.what()));
}
catch (...)
{
CV_LOG_WARNING(NULL,
cv::format("VIDEOIO(%s): raised unknown C++ exception!\n\n",
info.name));
}
}
else
{
CV_WRITER_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): backend is not available "
"(plugin is missing, or can't be loaded due "
"dependencies or it is not compatible)",
info.name));
}
}
}
if (cv::videoio_registry::checkDeprecatedBackend(apiPreference))
{
CV_LOG_DEBUG(NULL,
cv::format("VIDEOIO(%s): backend is removed from OpenCV",
cv::videoio_registry::getBackendName((VideoCaptureAPIs) apiPreference).c_str()));
}
else
{
CV_LOG_DEBUG(NULL, "VIDEOIO: chosen backend does not work or wrong."
"Please make sure that your computer support chosen backend and OpenCV built "
"with right flags.");
}
return false;
}
bool VideoWriter::isOpened() const
{
return !iwriter.empty();
}
bool VideoWriter::set(int propId, double value)
{
CV_CheckNE(propId, (int)CAP_PROP_BACKEND, "Can't set read-only property");
if (!iwriter.empty())
{
return iwriter->setProperty(propId, value);
}
return false;
}
double VideoWriter::get(int propId) const
{
if (propId == CAP_PROP_BACKEND)
{
int api = 0;
if (iwriter)
{
api = iwriter->getCaptureDomain();
}
return (api <= 0) ? -1. : static_cast<double>(api);
}
if (!iwriter.empty())
{
return iwriter->getProperty(propId);
}
return CAP_PROP_UNKNOWN;
}
String VideoWriter::getBackendName() const
{
int api = 0;
if (iwriter)
{
api = iwriter->getCaptureDomain();
}
CV_Assert(api != 0);
return cv::videoio_registry::getBackendName(static_cast<VideoCaptureAPIs>(api));
}
bool VideoWriter::write(InputArray image)
{
CV_INSTRUMENT_REGION();
if (iwriter)
{
return iwriter->write(image);
}
return false;
}
VideoWriter& VideoWriter::operator << (const Mat& image)
{
CV_INSTRUMENT_REGION();
write(image);
return *this;
}
VideoWriter& VideoWriter::operator << (const UMat& image)
{
CV_INSTRUMENT_REGION();
write(image);
return *this;
}
// FIXIT OpenCV 4.0: make inline
int VideoWriter::fourcc(char c1, char c2, char c3, char c4)
{
return (c1 & 255) + ((c2 & 255) << 8) + ((c3 & 255) << 16) + ((c4 & 255) << 24);
}
} // namespace cv
+809
View File
@@ -0,0 +1,809 @@
// 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
// Contributed by Giles Payne
#include "precomp.hpp"
#include <memory>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <chrono>
#include <android/log.h>
#include <camera/NdkCameraManager.h>
#include <camera/NdkCameraError.h>
#include <camera/NdkCameraDevice.h>
#include <camera/NdkCameraMetadataTags.h>
#include <media/NdkImageReader.h>
using namespace cv;
#define TAG "NativeCamera"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define MAX_BUF_COUNT 4
#define COLOR_FormatUnknown -1
#define COLOR_FormatYUV420Planar 19
#define COLOR_FormatYUV420SemiPlanar 21
#define FOURCC_BGR CV_FOURCC_MACRO('B','G','R','3')
#define FOURCC_RGB CV_FOURCC_MACRO('R','G','B','3')
#define FOURCC_BGRA CV_FOURCC_MACRO('B','G','R','4')
#define FOURCC_RGBA CV_FOURCC_MACRO('R','G','B','4')
#define FOURCC_GRAY CV_FOURCC_MACRO('G','R','E','Y')
#define FOURCC_NV21 CV_FOURCC_MACRO('N','V','2','1')
#define FOURCC_YV12 CV_FOURCC_MACRO('Y','V','1','2')
#define FOURCC_UNKNOWN 0xFFFFFFFF
template <typename T> struct RangeValue {
T min, max;
RangeValue(T minv = 0, T maxv = 0) : min(minv), max(maxv) {}
bool isValid() const { return (min != max); }
T percentage(int percent) const {
return static_cast<T>(min + ((max - min) * percent) / 100);
}
T clamp( T value ) const {
return (value > max) ? max : ((value < min) ? min : value);
}
};
template <typename T>
using AObjPtr = std::unique_ptr<T, std::function<void(T *)>>;
enum class CaptureSessionState {
INITIALIZING, // session is ready
READY, // session is ready
ACTIVE, // session is busy
CLOSED // session was closed
};
#define CAPTURE_TIMEOUT_SECONDS 2
#define CAPTURE_POLL_INTERVAL_MS 5
/**
* Range of Camera Exposure Time:
* Camera's capability range have a very long range which may be disturbing
* on camera. For this sample purpose, clamp to a range showing visible
* video on preview: 100000ns ~ 250000000ns
*/
static const RangeValue<int64_t> exposureTimeLimits = { 1000000, 250000000 };
static double elapsedTimeFrom(std::chrono::time_point<std::chrono::system_clock> start) {
return std::chrono::duration<double>(std::chrono::system_clock::now() - start).count();
}
class AndroidCameraCapture : public IVideoCapture
{
int deviceIndex;
AObjPtr<ACameraManager> cameraManager { nullptr, ACameraManager_delete };
AObjPtr<ACameraDevice> cameraDevice { nullptr, ACameraDevice_close };
AObjPtr<AImageReader> imageReader { nullptr, AImageReader_delete };
AObjPtr<ACaptureSessionOutputContainer> outputContainer { nullptr, ACaptureSessionOutputContainer_free };
AObjPtr<ACaptureSessionOutput> sessionOutput { nullptr, ACaptureSessionOutput_free };
AObjPtr<ANativeWindow> nativeWindow { nullptr, ANativeWindow_release };
AObjPtr<ACameraOutputTarget> outputTarget { nullptr, ACameraOutputTarget_free };
AObjPtr<ACaptureRequest> captureRequest { nullptr, ACaptureRequest_free };
AObjPtr<ACameraCaptureSession> captureSession { nullptr, ACameraCaptureSession_close };
CaptureSessionState sessionState = CaptureSessionState::INITIALIZING;
int32_t frameWidth = 0;
int32_t frameStride = 0;
int32_t frameHeight = 0;
int32_t colorFormat = COLOR_FormatUnknown;
std::vector<uint8_t> buffer;
bool sessionOutputAdded = false;
bool targetAdded = false;
// properties
uint32_t fourCC = FOURCC_UNKNOWN;
int32_t desiredWidth = 640;
int32_t desiredHeight = 480;
enum SetupState { setupDone = 0, setupWidth = 0x01, setupHeight = 0x02 } widthHeightState = setupDone;
uint8_t flashMode = ACAMERA_FLASH_MODE_OFF;
uint8_t aeMode = ACAMERA_CONTROL_AE_MODE_ON;
int64_t exposureTime = 0;
RangeValue<int64_t> exposureRange;
int32_t sensitivity = 0;
RangeValue<int32_t> sensitivityRange;
float zoomRatio = 1.0f;
RangeValue<float> zoomRange;
ACameraDevice_stateCallbacks deviceCallbacks = {};
ACameraCaptureSession_stateCallbacks sessionCallbacks = {};
ACameraCaptureSession_captureCallbacks captureCallbacks = {};
static void OnDeviceDisconnect(void* ctx, ACameraDevice* dev);
static void OnDeviceError(void* ctx, ACameraDevice* dev, int err);
static void OnSessionClosed(void* context, ACameraCaptureSession* session);
static void OnSessionReady(void* context, ACameraCaptureSession* session);
static void OnSessionActive(void* context, ACameraCaptureSession* session);
static void OnCaptureCompleted(void* context,
ACameraCaptureSession* session,
ACaptureRequest* request,
const ACameraMetadata* result);
static void OnCaptureFailed(void* context,
ACameraCaptureSession* session,
ACaptureRequest* request,
ACameraCaptureFailure* failure);
// for synchronization with NDK capture callback
bool waitingCapture = false;
bool captureSuccess = false;
std::mutex mtx;
std::condition_variable condition;
public:
AndroidCameraCapture(int index, const VideoCaptureParameters& params)
: deviceIndex(index)
{
deviceCallbacks.context = this;
deviceCallbacks.onError = OnDeviceError;
deviceCallbacks.onDisconnected = OnDeviceDisconnect,
sessionCallbacks.context = this;
sessionCallbacks.onReady = OnSessionReady;
sessionCallbacks.onActive = OnSessionActive;
sessionCallbacks.onClosed = OnSessionClosed;
captureCallbacks.context = this;
captureCallbacks.onCaptureCompleted = OnCaptureCompleted;
captureCallbacks.onCaptureFailed = OnCaptureFailed;
desiredWidth = params.get<int32_t>(CAP_PROP_FRAME_WIDTH, desiredWidth);
desiredHeight = params.get<int32_t>(CAP_PROP_FRAME_HEIGHT, desiredHeight);
static const struct {
int propId;
uint32_t defaultValue;
} items[] = {
{ CAP_PROP_AUTO_EXPOSURE, 1 },
{ CAP_PROP_FOURCC, FOURCC_UNKNOWN },
{ CAP_PROP_ANDROID_DEVICE_TORCH, 0 }
};
for (auto it = std::begin(items); it != std::end(items); ++it) {
setProperty(it->propId, params.get<double>(it->propId, it->defaultValue));
}
}
~AndroidCameraCapture() { cleanUp(); }
bool isOpened() const CV_OVERRIDE { return imageReader && captureSession; }
int getCaptureDomain() CV_OVERRIDE { return CAP_ANDROID; }
bool grabFrame() CV_OVERRIDE
{
AImage* img;
{
std::unique_lock<std::mutex> lock(mtx);
media_status_t mStatus = AImageReader_acquireLatestImage(imageReader.get(), &img);
if (mStatus != AMEDIA_OK) {
if (mStatus == AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE) {
// this error is not fatal - we just need to wait for a buffer to become available
LOGW("No Buffer Available error occurred - waiting for callback");
waitingCapture = true;
captureSuccess = false;
auto start = std::chrono::system_clock::now();
bool captured = condition.wait_for(lock, std::chrono::seconds(
CAPTURE_TIMEOUT_SECONDS), [this]{ return captureSuccess; });
waitingCapture = false;
if (captured) {
mStatus = AImageReader_acquireLatestImage(imageReader.get(), &img);
// even though an image has been captured we may not be able to acquire it
// straight away so we poll every 10ms
while (mStatus == AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE &&
elapsedTimeFrom(start) < CAPTURE_TIMEOUT_SECONDS) {
std::this_thread::sleep_for(std::chrono::milliseconds(CAPTURE_POLL_INTERVAL_MS));
mStatus = AImageReader_acquireLatestImage(imageReader.get(), &img);
}
if (mStatus != AMEDIA_OK) {
LOGE("Acquire image failed with error code: %d", mStatus);
if (elapsedTimeFrom(start) >= CAPTURE_TIMEOUT_SECONDS) {
LOGE("Image acquisition timed out");
}
return false;
}
} else {
LOGE("Capture failed or callback timed out");
return false;
}
} else {
LOGE("Acquire image failed with error code: %d", mStatus);
return false;
}
}
}
AObjPtr<AImage> image(img, AImage_delete);
int32_t srcFormat = -1;
AImage_getFormat(image.get(), &srcFormat);
if (srcFormat != AIMAGE_FORMAT_YUV_420_888) {
LOGE("Incorrect image format");
return false;
}
int32_t srcPlanes = 0;
AImage_getNumberOfPlanes(image.get(), &srcPlanes);
if (srcPlanes != 3) {
LOGE("Incorrect number of planes in image data");
return false;
}
int32_t yStride, uvStride;
uint8_t *yPixel, *uPixel, *vPixel;
int32_t yLen, uLen, vLen;
int32_t uvPixelStride;
AImage_getPlaneRowStride(image.get(), 0, &yStride);
AImage_getPlaneRowStride(image.get(), 1, &uvStride);
AImage_getPlaneData(image.get(), 0, &yPixel, &yLen);
AImage_getPlaneData(image.get(), 1, &uPixel, &uLen);
AImage_getPlaneData(image.get(), 2, &vPixel, &vLen);
AImage_getPlanePixelStride(image.get(), 1, &uvPixelStride);
int32_t yBufferLen = yLen;
if ( (uvPixelStride == 2) && (uPixel == vPixel + 1) &&
(yLen == (yStride * (frameHeight - 1)) + frameWidth) &&
(uLen == (uvStride * ((frameHeight / 2) - 1)) + frameWidth - 1) &&
(uvStride == yStride) && (vLen == uLen) ) {
frameStride = yStride;
yBufferLen = frameStride * frameHeight;
colorFormat = COLOR_FormatYUV420SemiPlanar;
if (fourCC == FOURCC_UNKNOWN) {
fourCC = FOURCC_NV21;
}
} else if ( (uvPixelStride == 1) && (uPixel == vPixel + vLen) &&
(yLen == frameWidth * frameHeight) &&
(uLen == yLen / 4) && (vLen == uLen) ) {
colorFormat = COLOR_FormatYUV420Planar;
if (fourCC == FOURCC_UNKNOWN) {
fourCC = FOURCC_YV12;
}
} else {
colorFormat = COLOR_FormatUnknown;
fourCC = FOURCC_UNKNOWN;
LOGE("Unsupported format");
return false;
}
buffer.clear();
buffer.insert(buffer.end(), yPixel, yPixel + yBufferLen);
buffer.insert(buffer.end(), vPixel, vPixel + yBufferLen / 2);
return true;
}
bool retrieveFrame(int, OutputArray out) CV_OVERRIDE
{
if (buffer.empty()) {
return false;
}
if (colorFormat == COLOR_FormatYUV420Planar) {
const Mat yuv(frameHeight + frameHeight/2, frameWidth, CV_8UC1, buffer.data());
switch (fourCC) {
case FOURCC_BGRA:
cvtColor(yuv, out, COLOR_YUV2BGRA_YV12);
break;
case FOURCC_RGBA:
cvtColor(yuv, out, COLOR_YUV2RGBA_YV12);
break;
case FOURCC_BGR:
cvtColor(yuv, out, COLOR_YUV2BGR_YV12);
break;
case FOURCC_RGB:
cvtColor(yuv, out, COLOR_YUV2RGB_YV12);
break;
case FOURCC_GRAY:
cvtColor(yuv, out, COLOR_YUV2GRAY_YV12);
break;
case FOURCC_YV12:
yuv.copyTo(out);
break;
default:
LOGE("Unexpected FOURCC value: %d", fourCC);
return false;
}
} else if (colorFormat == COLOR_FormatYUV420SemiPlanar) {
const Mat yuv(frameHeight + frameHeight/2, frameWidth, CV_8UC1, buffer.data(), frameStride);
switch (fourCC) {
case FOURCC_BGRA:
cvtColor(yuv, out, COLOR_YUV2BGRA_NV21);
break;
case FOURCC_RGBA:
cvtColor(yuv, out, COLOR_YUV2RGBA_NV21);
break;
case FOURCC_BGR:
cvtColor(yuv, out, COLOR_YUV2BGR_NV21);
break;
case FOURCC_RGB:
cvtColor(yuv, out, COLOR_YUV2RGB_NV21);
break;
case FOURCC_GRAY:
cvtColor(yuv, out, COLOR_YUV2GRAY_NV21);
break;
case FOURCC_NV21:
yuv.copyTo(out);
break;
default:
LOGE("Unexpected FOURCC value: %d", fourCC);
return false;
}
} else {
LOGE("Unsupported video format: %d", colorFormat);
return false;
}
return true;
}
double getProperty(int property_id) const CV_OVERRIDE
{
switch (property_id) {
case CAP_PROP_FRAME_WIDTH:
return isOpened() ? frameWidth : desiredWidth;
case CAP_PROP_FRAME_HEIGHT:
return isOpened() ? frameHeight : desiredHeight;
case CAP_PROP_AUTO_EXPOSURE:
return (aeMode == ACAMERA_CONTROL_AE_MODE_ON) ? 1 : 0;
case CAP_PROP_EXPOSURE:
return exposureTime;
case CAP_PROP_ISO_SPEED:
return sensitivity;
case CAP_PROP_FOURCC:
return fourCC;
case CAP_PROP_ANDROID_DEVICE_TORCH:
return (flashMode == ACAMERA_FLASH_MODE_TORCH) ? 1 : 0;
case CAP_PROP_ZOOM:
return zoomRange.isValid() ? zoomRatio : -1;
default:
break;
}
// unknown parameter or value not available
return CAP_PROP_UNKNOWN;
}
bool setProperty(int property_id, double value) CV_OVERRIDE
{
switch (property_id) {
case CAP_PROP_FRAME_WIDTH:
desiredWidth = static_cast<int32_t>(value);
setWidthHeight(setupWidth);
return true;
case CAP_PROP_FRAME_HEIGHT:
desiredHeight = static_cast<int32_t>(value);
setWidthHeight(setupHeight);
return true;
case CAP_PROP_FOURCC:
{
uint32_t newFourCC = cvRound(value);
if (fourCC == newFourCC) {
return true;
} else {
switch (newFourCC) {
case FOURCC_BGR:
case FOURCC_RGB:
case FOURCC_BGRA:
case FOURCC_RGBA:
case FOURCC_GRAY:
fourCC = newFourCC;
return true;
case FOURCC_YV12:
if (colorFormat == COLOR_FormatYUV420Planar) {
fourCC = newFourCC;
return true;
} else {
LOGE("Unsupported FOURCC conversion COLOR_FormatYUV420SemiPlanar"
" -> COLOR_FormatYUV420Planar");
return false;
}
case FOURCC_NV21:
if (colorFormat == COLOR_FormatYUV420SemiPlanar) {
fourCC = newFourCC;
return true;
} else {
LOGE("Unsupported FOURCC conversion COLOR_FormatYUV420Planar"
" -> COLOR_FormatYUV420SemiPlanar");
return false;
}
default:
LOGE("Unsupported FOURCC value: %d\n", fourCC);
return false;
}
}
}
case CAP_PROP_AUTO_EXPOSURE:
aeMode = (value != 0) ? ACAMERA_CONTROL_AE_MODE_ON : ACAMERA_CONTROL_AE_MODE_OFF;
if (isOpened()) {
return submitRequest(ACaptureRequest_setEntry_u8, ACAMERA_CONTROL_AE_MODE, aeMode);
}
return true;
case CAP_PROP_EXPOSURE:
if (isOpened() && exposureRange.isValid()) {
exposureTime = exposureRange.clamp(static_cast<int64_t>(value));
LOGI("Setting CAP_PROP_EXPOSURE will have no effect unless CAP_PROP_AUTO_EXPOSURE is off");
return submitRequest(ACaptureRequest_setEntry_i64, ACAMERA_SENSOR_EXPOSURE_TIME, exposureTime);
}
return false;
case CAP_PROP_ISO_SPEED:
if (isOpened() && sensitivityRange.isValid()) {
sensitivity = sensitivityRange.clamp(static_cast<int32_t>(value));
LOGI("Setting CAP_PROP_ISO_SPEED will have no effect unless CAP_PROP_AUTO_EXPOSURE is off");
return submitRequest(ACaptureRequest_setEntry_i32, ACAMERA_SENSOR_SENSITIVITY, sensitivity);
}
return false;
case CAP_PROP_ZOOM:
if (isOpened() && zoomRange.isValid()) {
zoomRatio = zoomRange.clamp(static_cast<float>(value));
return submitRequest(ACaptureRequest_setEntry_float, ACAMERA_CONTROL_ZOOM_RATIO, zoomRatio);
}
return true;
case CAP_PROP_ANDROID_DEVICE_TORCH:
flashMode = (value != 0) ? ACAMERA_FLASH_MODE_TORCH : ACAMERA_FLASH_MODE_OFF;
if (isOpened()) {
return submitRequest(ACaptureRequest_setEntry_u8, ACAMERA_FLASH_MODE, flashMode);
}
return true;
default:
break;
}
return false;
}
bool initCapture()
{
cameraManager.reset(ACameraManager_create());
if (!cameraManager) {
LOGE("Cannot create camera manager!");
return false;
}
ACameraIdList* cameraIds;
camera_status_t cStatus = ACameraManager_getCameraIdList(cameraManager.get(), &cameraIds);
if (cStatus != ACAMERA_OK) {
LOGE("Get camera list failed with error code: %d", cStatus);
return false;
}
AObjPtr<ACameraIdList> cameraIdList(cameraIds, ACameraManager_deleteCameraIdList);
if (deviceIndex < 0 || deviceIndex >= cameraIds->numCameras) {
LOGE("Camera index out of range %d (Number of cameras: %d)", deviceIndex, cameraIds->numCameras);
return false;
}
const char *cameraId = cameraIdList.get()->cameraIds[deviceIndex];
ACameraDevice* camera;
cStatus = ACameraManager_openCamera(cameraManager.get(), cameraId, &deviceCallbacks, &camera);
if (cStatus != ACAMERA_OK) {
LOGE("Open camera failed with error code: %d", cStatus);
return false;
}
cameraDevice.reset(camera);
ACameraMetadata* metadata;
cStatus = ACameraManager_getCameraCharacteristics(cameraManager.get(), cameraId, &metadata);
if (cStatus != ACAMERA_OK) {
LOGE("Get camera characteristics failed with error code: %d", cStatus);
return false;
}
AObjPtr<ACameraMetadata> cameraMetadata(metadata, ACameraMetadata_free);
getPropertyRanges(cameraMetadata.get());
int32_t bestMatchWidth = 0, bestMatchHeight = 0;
findResolutionMatch(cameraMetadata.get(), bestMatchWidth, bestMatchHeight);
LOGI("Best resolution match: %dx%d", bestMatchWidth, bestMatchHeight);
AImageReader* reader;
media_status_t mStatus = AImageReader_new(bestMatchWidth, bestMatchHeight,
AIMAGE_FORMAT_YUV_420_888, MAX_BUF_COUNT, &reader);
if (mStatus != AMEDIA_OK) {
LOGE("ImageReader creation failed with error code: %d", mStatus);
return false;
}
frameWidth = bestMatchWidth;
frameHeight = bestMatchHeight;
imageReader.reset(reader);
ANativeWindow* window;
mStatus = AImageReader_getWindow(imageReader.get(), &window);
if (mStatus != AMEDIA_OK) {
LOGE("Could not get ANativeWindow: %d", mStatus);
return false;
}
nativeWindow.reset(window);
ANativeWindow_acquire(nativeWindow.get());
ACaptureSessionOutputContainer* container;
cStatus = ACaptureSessionOutputContainer_create(&container);
if (cStatus != ACAMERA_OK) {
LOGE("CaptureSessionOutputContainer creation failed with error code: %d", cStatus);
return false;
}
outputContainer.reset(container);
ACaptureSessionOutput* output;
cStatus = ACaptureSessionOutput_create(nativeWindow.get(), &output);
if (cStatus != ACAMERA_OK) {
LOGE("CaptureSessionOutput creation failed with error code: %d", cStatus);
return false;
}
sessionOutput.reset(output);
cStatus = ACaptureSessionOutputContainer_add(outputContainer.get(), sessionOutput.get());
if (cStatus != ACAMERA_OK) {
LOGE("CaptureSessionOutput Container add failed with error code: %d", cStatus);
return false;
}
sessionOutputAdded = true;
ACameraOutputTarget* target;
cStatus = ACameraOutputTarget_create(nativeWindow.get(), &target);
if (cStatus != ACAMERA_OK) {
LOGE("CameraOutputTarget creation failed with error code: %d", cStatus);
return false;
}
outputTarget.reset(target);
ACaptureRequest* request;
cStatus = ACameraDevice_createCaptureRequest(cameraDevice.get(), TEMPLATE_PREVIEW, &request);
if (cStatus != ACAMERA_OK) {
LOGE("CaptureRequest creation failed with error code: %d", cStatus);
return false;
}
captureRequest.reset(request);
cStatus = ACaptureRequest_addTarget(captureRequest.get(), outputTarget.get());
if (cStatus != ACAMERA_OK) {
LOGE("Add target to CaptureRequest failed with error code: %d", cStatus);
return false;
}
targetAdded = true;
ACameraCaptureSession* session;
cStatus = ACameraDevice_createCaptureSession(cameraDevice.get(),
outputContainer.get(), &sessionCallbacks, &session);
if (cStatus != ACAMERA_OK) {
LOGE("CaptureSession creation failed with error code: %d", cStatus);
return false;
}
captureSession.reset(session);
ACaptureRequest_setEntry_u8(captureRequest.get(), ACAMERA_CONTROL_AE_MODE, 1, &aeMode);
if (aeMode != ACAMERA_CONTROL_AE_MODE_ON) {
ACaptureRequest_setEntry_i32(captureRequest.get(), ACAMERA_SENSOR_SENSITIVITY, 1, &sensitivity);
ACaptureRequest_setEntry_i64(captureRequest.get(), ACAMERA_SENSOR_EXPOSURE_TIME, 1, &exposureTime);
}
if (zoomRange.isValid()) {
ACaptureRequest_setEntry_float(captureRequest.get(), ACAMERA_CONTROL_ZOOM_RATIO, 1, &zoomRatio);
}
ACaptureRequest_setEntry_u8(captureRequest.get(), ACAMERA_FLASH_MODE, 1, &flashMode);
cStatus = ACameraCaptureSession_setRepeatingRequest(captureSession.get(),
&captureCallbacks, 1, &request, nullptr);
if (cStatus != ACAMERA_OK) {
LOGE("CameraCaptureSession set repeating request failed with error code: %d", cStatus);
return false;
}
return true;
}
private:
void getPropertyRanges(const ACameraMetadata* metadata)
{
camera_status_t cStatus;
ACameraMetadata_const_entry val;
cStatus = ACameraMetadata_getConstEntry(metadata, ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE, &val);
if (cStatus == ACAMERA_OK) {
exposureRange.min = exposureTimeLimits.clamp(val.data.i64[0]);
exposureRange.max = exposureTimeLimits.clamp(val.data.i64[1]);
exposureTime = exposureRange.percentage(2);
} else {
LOGW("Unsupported ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE");
exposureRange.min = exposureRange.max = 0;
exposureTime = 0;
}
cStatus = ACameraMetadata_getConstEntry(metadata, ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE, &val);
if (cStatus == ACAMERA_OK){
sensitivityRange.min = val.data.i32[0];
sensitivityRange.max = val.data.i32[1];
sensitivity = sensitivityRange.percentage(2);
} else {
LOGW("Unsupported ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE");
sensitivityRange.min = sensitivityRange.max = 0;
sensitivity = 0;
}
cStatus = ACameraMetadata_getConstEntry(metadata, ACAMERA_CONTROL_ZOOM_RATIO_RANGE, &val);
if (cStatus == ACAMERA_OK){
zoomRange.min = val.data.f[0];
zoomRange.max = val.data.f[1];
zoomRatio = zoomRange.clamp(zoomRatio);
} else {
LOGW("Unsupported ACAMERA_CONTROL_ZOOM_RATIO_RANGE");
zoomRange.min = zoomRange.max = 0;
zoomRatio = 1.0f;
}
}
// calculate a score based on how well the width and height match the desired width and height
// basically draw the 2 rectangle on top of each other and take the ratio of the non-overlapping
// area to the overlapping area
double getScore(int32_t width, int32_t height) const {
double area1 = width * height;
double area2 = desiredWidth * desiredHeight;
if ((width < desiredWidth) == (height < desiredHeight)) {
return (width < desiredWidth) ? (area2 - area1)/area1 : (area1 - area2)/area2;
} else {
int32_t overlappedWidth = std::min(width, desiredWidth);
int32_t overlappedHeight = std::min(height, desiredHeight);
double overlappedArea = overlappedWidth * overlappedHeight;
return (area1 + area2 - overlappedArea)/overlappedArea;
}
}
void findResolutionMatch(const ACameraMetadata* metadata,
int32_t &bestMatchWidth, int32_t &bestMatchHeight) const {
ACameraMetadata_const_entry entry = {};
ACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry);
double bestScore = std::numeric_limits<double>::max();
for (uint32_t i = 0; i < entry.count; i += 4) {
int32_t input = entry.data.i32[i + 3];
int32_t format = entry.data.i32[i + 0];
if (!input && format == AIMAGE_FORMAT_YUV_420_888) {
int32_t width = entry.data.i32[i + 1];
int32_t height = entry.data.i32[i + 2];
if (width == desiredWidth && height == desiredHeight) {
bestMatchWidth = width;
bestMatchHeight = height;
return;
}
double score = getScore(width, height);
if (score < bestScore) {
bestMatchWidth = width;
bestMatchHeight = height;
bestScore = score;
}
}
}
}
void setWidthHeight(SetupState newState) {
if ((widthHeightState | newState) == (setupWidth | setupHeight)) {
cleanUp();
initCapture();
newState = setupDone;
}
widthHeightState = newState;
}
void cleanUp() {
if (sessionState == CaptureSessionState::ACTIVE) {
ACameraCaptureSession_stopRepeating(captureSession.get());
}
captureSession.reset();
if (targetAdded) {
ACaptureRequest_removeTarget(captureRequest.get(), outputTarget.get());
targetAdded = false;
}
captureRequest.reset();
outputTarget.reset();
if (sessionOutputAdded) {
ACaptureSessionOutputContainer_remove(outputContainer.get(), sessionOutput.get());
sessionOutputAdded = false;
}
sessionOutput.reset();
nativeWindow.reset();
outputContainer.reset();
cameraDevice.reset();
cameraManager.reset();
imageReader.reset();
}
template<typename FuncT, typename T>
bool submitRequest(FuncT setFn, uint32_t tag, const T &data)
{
ACaptureRequest *request = captureRequest.get();
return request &&
setFn(request, tag, 1, &data) == ACAMERA_OK &&
ACameraCaptureSession_setRepeatingRequest(captureSession.get(),
&captureCallbacks,
1, &request, nullptr) == ACAMERA_OK;
}
};
/******************************** Device management *******************************/
void AndroidCameraCapture::OnDeviceDisconnect(void* /* ctx */, ACameraDevice* dev) {
const char *id = ACameraDevice_getId(dev);
LOGW("Device %s disconnected", id ? id : "<null>");
}
void AndroidCameraCapture::OnDeviceError(void* /* ctx */, ACameraDevice* dev, int err) {
const char *id = ACameraDevice_getId(dev);
LOGI("Camera Device Error: %#x, Device %s", err, id ? id : "<null>");
switch (err) {
case ERROR_CAMERA_IN_USE:
LOGI("Camera in use");
break;
case ERROR_CAMERA_SERVICE:
LOGI("Fatal Error occurred in Camera Service");
break;
case ERROR_CAMERA_DEVICE:
LOGI("Fatal Error occurred in Camera Device");
break;
case ERROR_CAMERA_DISABLED:
LOGI("Camera disabled");
break;
case ERROR_MAX_CAMERAS_IN_USE:
LOGI("System limit for maximum concurrent cameras used was exceeded");
break;
default:
LOGI("Unknown Camera Device Error: %#x", err);
}
}
/******************************** Session management *******************************/
void AndroidCameraCapture::OnSessionClosed(void* context, ACameraCaptureSession* session) {
if (context == nullptr) return;
LOGW("session %p closed", session);
static_cast<AndroidCameraCapture*>(context)->sessionState = CaptureSessionState::CLOSED;
}
void AndroidCameraCapture::OnSessionReady(void* context, ACameraCaptureSession* session) {
if (context == nullptr) return;
LOGW("session %p ready", session);
static_cast<AndroidCameraCapture*>(context)->sessionState = CaptureSessionState::READY;
}
void AndroidCameraCapture::OnSessionActive(void* context, ACameraCaptureSession* session) {
if (context == nullptr) return;
LOGW("session %p active", session);
static_cast<AndroidCameraCapture*>(context)->sessionState = CaptureSessionState::ACTIVE;
}
void AndroidCameraCapture::OnCaptureCompleted(void* context,
ACameraCaptureSession* session,
ACaptureRequest* /* request */,
const ACameraMetadata* /* result */) {
if (context == nullptr) return;
LOGV("session %p capture completed", session);
AndroidCameraCapture* cameraCapture = static_cast<AndroidCameraCapture*>(context);
std::unique_lock<std::mutex> lock(cameraCapture->mtx);
if (cameraCapture->waitingCapture) {
cameraCapture->waitingCapture = false;
cameraCapture->captureSuccess = true;
cameraCapture->condition.notify_one();
}
}
void AndroidCameraCapture::OnCaptureFailed(void* context,
ACameraCaptureSession* session,
ACaptureRequest* /* request */,
ACameraCaptureFailure* /* failure */) {
if (context == nullptr) return;
LOGV("session %p capture failed", session);
AndroidCameraCapture* cameraCapture = static_cast<AndroidCameraCapture*>(context);
std::unique_lock<std::mutex> lock(cameraCapture->mtx);
if (cameraCapture->waitingCapture) {
cameraCapture->waitingCapture = false;
cameraCapture->captureSuccess = false;
cameraCapture->condition.notify_one();
}
}
/****************** Implementation of interface functions ********************/
Ptr<IVideoCapture> cv::createAndroidCapture_cam(int index, const VideoCaptureParameters& params) {
Ptr<AndroidCameraCapture> res = makePtr<AndroidCameraCapture>(index, params);
if (res && res->initCapture())
return res;
return Ptr<IVideoCapture>();
}
@@ -0,0 +1,773 @@
// 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 <stdio.h>
#include <string.h>
#include <fstream>
#include <iostream>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <android/log.h>
#include <android/native_window.h>
#include "media/NdkMediaCodec.h"
#include "media/NdkMediaMuxer.h"
#include "media/NdkMediaExtractor.h"
#include "media/NdkMediaFormat.h"
#define INPUT_TIMEOUT_MS 2000
#define COLOR_FormatUnknown -1
#define COLOR_FormatYUV420Planar 19
#define COLOR_FormatYUV420SemiPlanar 21
#define COLOR_FormatSurface 0x7f000789 //See https://developer.android.com/reference/android/media/MediaCodecInfo.CodecCapabilities for codes
#define FOURCC_BGR CV_FOURCC_MACRO('B','G','R','3')
#define FOURCC_RGB CV_FOURCC_MACRO('R','G','B','3')
#define FOURCC_BGRA CV_FOURCC_MACRO('B','G','R','4')
#define FOURCC_RGBA CV_FOURCC_MACRO('R','G','B','4')
#define FOURCC_GRAY CV_FOURCC_MACRO('G','R','E','Y')
#define FOURCC_NV12 CV_FOURCC_MACRO('N','V','1','2')
#define FOURCC_YV12 CV_FOURCC_MACRO('Y','V','1','2')
#define FOURCC_UNKNOWN 0xFFFFFFFF
using namespace cv;
#define TAG "NativeCodec"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
static inline void deleter_AMediaExtractor(AMediaExtractor *extractor) {
AMediaExtractor_delete(extractor);
}
static inline void deleter_AMediaCodec(AMediaCodec *codec) {
AMediaCodec_stop(codec);
AMediaCodec_delete(codec);
}
static inline void deleter_AMediaFormat(AMediaFormat *format) {
AMediaFormat_delete(format);
}
class AndroidMediaNdkCapture : public IVideoCapture
{
public:
AndroidMediaNdkCapture():
sawInputEOS(false), sawOutputEOS(false),
frameStride(0), frameWidth(0), frameHeight(0),
colorFormat(COLOR_FormatUnknown), fourCC(FOURCC_BGR),
videoWidth(0), videoHeight(0), videoFrameCount(0),
videoRotation(0), videoRotationCode(-1),
videoOrientationAuto(false) {}
std::shared_ptr<AMediaExtractor> mediaExtractor;
std::shared_ptr<AMediaCodec> mediaCodec;
bool sawInputEOS;
bool sawOutputEOS;
int32_t frameStride;
int32_t frameWidth;
int32_t frameHeight;
int32_t colorFormat;
uint32_t fourCC;
int32_t videoWidth;
int32_t videoHeight;
float videoFrameRate;
int32_t videoFrameCount;
int32_t videoRotation;
int32_t videoRotationCode;
bool videoOrientationAuto;
std::vector<uint8_t> buffer;
~AndroidMediaNdkCapture() { cleanUp(); }
bool decodeFrame() {
while (!sawInputEOS || !sawOutputEOS) {
if (!sawInputEOS) {
auto bufferIndex = AMediaCodec_dequeueInputBuffer(mediaCodec.get(), INPUT_TIMEOUT_MS);
LOGV("input buffer %zd", bufferIndex);
if (bufferIndex >= 0) {
size_t bufferSize;
auto inputBuffer = AMediaCodec_getInputBuffer(mediaCodec.get(), bufferIndex, &bufferSize);
auto sampleSize = AMediaExtractor_readSampleData(mediaExtractor.get(), inputBuffer, bufferSize);
if (sampleSize < 0) {
sampleSize = 0;
sawInputEOS = true;
LOGV("EOS");
}
auto presentationTimeUs = AMediaExtractor_getSampleTime(mediaExtractor.get());
AMediaCodec_queueInputBuffer(mediaCodec.get(), bufferIndex, 0, sampleSize,
presentationTimeUs, sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
AMediaExtractor_advance(mediaExtractor.get());
}
}
if (!sawOutputEOS) {
AMediaCodecBufferInfo info;
auto bufferIndex = AMediaCodec_dequeueOutputBuffer(mediaCodec.get(), &info, 0);
if (bufferIndex >= 0) {
size_t bufferSize = 0;
auto mediaFormat = std::shared_ptr<AMediaFormat>(AMediaCodec_getOutputFormat(mediaCodec.get()), deleter_AMediaFormat);
AMediaFormat_getInt32(mediaFormat.get(), AMEDIAFORMAT_KEY_WIDTH, &frameWidth);
AMediaFormat_getInt32(mediaFormat.get(), AMEDIAFORMAT_KEY_STRIDE, &frameStride);
AMediaFormat_getInt32(mediaFormat.get(), AMEDIAFORMAT_KEY_HEIGHT, &frameHeight);
AMediaFormat_getInt32(mediaFormat.get(), AMEDIAFORMAT_KEY_COLOR_FORMAT, &colorFormat);
uint8_t* codecBuffer = AMediaCodec_getOutputBuffer(mediaCodec.get(), bufferIndex, &bufferSize);
buffer = std::vector<uint8_t>(codecBuffer, codecBuffer + bufferSize);
LOGV("colorFormat: %d", colorFormat);
LOGV("buffer size: %zu", bufferSize);
LOGV("width (frame): %d", frameWidth);
LOGV("stride (frame): %d", frameStride);
LOGV("height (frame): %d", frameHeight);
if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)
{
LOGV("output EOS");
sawOutputEOS = true;
}
AMediaCodec_releaseOutputBuffer(mediaCodec.get(), bufferIndex, info.size != 0);
return true;
} else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
LOGV("output buffers changed");
} else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
auto format = AMediaCodec_getOutputFormat(mediaCodec.get());
LOGV("format changed to: %s", AMediaFormat_toString(format));
AMediaFormat_delete(format);
} else if (bufferIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
LOGV("no output buffer right now");
} else {
LOGV("unexpected info code: %zd", bufferIndex);
}
}
}
return false;
}
bool isOpened() const CV_OVERRIDE { return mediaCodec.get() != nullptr; }
int getCaptureDomain() CV_OVERRIDE { return CAP_ANDROID; }
bool grabFrame() CV_OVERRIDE
{
// clear the previous frame
buffer.clear();
return decodeFrame();
}
bool retrieveFrame(int, OutputArray out) CV_OVERRIDE
{
if (buffer.empty()) {
return false;
}
ColorConversionCodes ccCode;
const Mat yuv(frameHeight + frameHeight/2,
frameWidth, CV_8UC1, buffer.data(), frameStride);
if (colorFormat == COLOR_FormatYUV420Planar) {
switch(fourCC)
{
case FOURCC_BGR: ccCode = COLOR_YUV2BGR_YV12; break;
case FOURCC_RGB: ccCode = COLOR_YUV2RGB_YV12; break;
case FOURCC_BGRA: ccCode = COLOR_YUV2BGRA_YV12; break;
case FOURCC_RGBA: ccCode = COLOR_YUV2RGBA_YV12; break;
case FOURCC_GRAY: ccCode = COLOR_YUV2GRAY_YV12; break;
case FOURCC_YV12: break;
case FOURCC_UNKNOWN: fourCC = FOURCC_YV12; break;
default: LOGE("Unexpected FOURCC value: %d", fourCC);
return false;
}
} else if (colorFormat == COLOR_FormatYUV420SemiPlanar) {
// Attention: COLOR_FormatYUV420SemiPlanar seems to correspond to NV12.
// This is different from the Camera2 interface, where NV21
// is used in this situation.
switch(fourCC)
{
case FOURCC_BGR: ccCode = COLOR_YUV2BGR_NV12; break;
case FOURCC_RGB: ccCode = COLOR_YUV2RGB_NV12; break;
case FOURCC_BGRA: ccCode = COLOR_YUV2BGRA_NV12; break;
case FOURCC_RGBA: ccCode = COLOR_YUV2RGBA_NV12; break;
case FOURCC_GRAY: ccCode = COLOR_YUV2GRAY_NV12; break;
case FOURCC_NV12: break;
case FOURCC_UNKNOWN: fourCC = FOURCC_NV12; break;
default: LOGE("Unexpected FOURCC value: %d", fourCC);
return false;
}
} else {
LOGE("Unsupported video format: %d", colorFormat);
return false;
}
if (fourCC == FOURCC_YV12 || fourCC == FOURCC_NV12)
yuv.copyTo(out);
else
cvtColor(yuv, out, ccCode);
if (videoOrientationAuto && -1 != videoRotationCode)
rotate(out, out, videoRotationCode);
return true;
}
double getProperty(int property_id) const CV_OVERRIDE
{
switch (property_id)
{
case CAP_PROP_FRAME_WIDTH:
return (( videoOrientationAuto &&
(cv::ROTATE_90_CLOCKWISE == videoRotationCode || cv::ROTATE_90_COUNTERCLOCKWISE == videoRotationCode))
? videoHeight : videoWidth);
case CAP_PROP_FRAME_HEIGHT:
return (( videoOrientationAuto &&
(cv::ROTATE_90_CLOCKWISE == videoRotationCode || cv::ROTATE_90_COUNTERCLOCKWISE == videoRotationCode))
? videoWidth : videoHeight);
case CAP_PROP_FPS: return videoFrameRate;
case CAP_PROP_FRAME_COUNT: return videoFrameCount;
case CAP_PROP_ORIENTATION_META: return videoRotation;
case CAP_PROP_ORIENTATION_AUTO: return videoOrientationAuto ? 1 : 0;
case CAP_PROP_FOURCC: return fourCC;
}
// unknown parameter or value not available
return CAP_PROP_UNKNOWN;
}
bool setProperty(int property_id, double value) CV_OVERRIDE
{
switch (property_id)
{
case CAP_PROP_ORIENTATION_AUTO: {
videoOrientationAuto = value != 0 ? true : false;
return true;
}
case CAP_PROP_FOURCC: {
uint32_t newFourCC = cvRound(value);
switch (newFourCC)
{
case FOURCC_BGR:
case FOURCC_RGB:
case FOURCC_BGRA:
case FOURCC_RGBA:
case FOURCC_GRAY:
fourCC = newFourCC;
return true;
case FOURCC_YV12:
if (colorFormat != COLOR_FormatYUV420SemiPlanar) {
fourCC = (colorFormat == COLOR_FormatUnknown) ? FOURCC_UNKNOWN : FOURCC_YV12;
return true;
}
break;
case FOURCC_NV12:
if (colorFormat != COLOR_FormatYUV420Planar) {
fourCC = (colorFormat == COLOR_FormatUnknown) ? FOURCC_UNKNOWN : FOURCC_NV12;
return true;
}
break;
}
}
}
return false;
}
bool initCapture(const char * filename)
{
struct stat statBuffer;
if (stat(filename, &statBuffer) != 0) {
LOGE("failed to stat file: %s (%s)", filename, strerror(errno));
return false;
}
int fd = open(filename, O_RDONLY);
if (fd < 0) {
LOGE("failed to open file: %s %d (%s)", filename, fd, strerror(errno));
return false;
}
mediaExtractor = std::shared_ptr<AMediaExtractor>(AMediaExtractor_new(), deleter_AMediaExtractor);
if (!mediaExtractor) {
return false;
}
media_status_t err = AMediaExtractor_setDataSourceFd(mediaExtractor.get(), fd, 0, statBuffer.st_size);
close(fd);
if (err != AMEDIA_OK) {
LOGV("setDataSource error: %d", err);
return false;
}
int numtracks = AMediaExtractor_getTrackCount(mediaExtractor.get());
LOGV("input has %d tracks", numtracks);
for (int i = 0; i < numtracks; i++) {
auto format = std::shared_ptr<AMediaFormat>(AMediaExtractor_getTrackFormat(mediaExtractor.get(), i), deleter_AMediaFormat);
if (!format) {
continue;
}
const char *s = AMediaFormat_toString(format.get());
LOGV("track %d format: %s", i, s);
const char *mime;
if (!AMediaFormat_getString(format.get(), AMEDIAFORMAT_KEY_MIME, &mime)) {
LOGV("no mime type");
} else if (!strncmp(mime, "video/", 6)) {
int32_t trackWidth, trackHeight, fps, frameCount = 0, rotation = 0;
AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_WIDTH, &trackWidth);
AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_HEIGHT, &trackHeight);
AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_FRAME_RATE, &fps);
#if __ANDROID_API__ >= 28
AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_ROTATION, &rotation);
LOGV("rotation (track): %d", rotation);
#endif
#if __ANDROID_API__ >= 29
AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_FRAME_COUNT, &frameCount);
#endif
LOGV("width (track): %d", trackWidth);
LOGV("height (track): %d", trackHeight);
if (AMediaExtractor_selectTrack(mediaExtractor.get(), i) != AMEDIA_OK) {
continue;
}
mediaCodec = std::shared_ptr<AMediaCodec>(AMediaCodec_createDecoderByType(mime), deleter_AMediaCodec);
if (!mediaCodec) {
continue;
}
if (AMediaCodec_configure(mediaCodec.get(), format.get(), NULL, NULL, 0) != AMEDIA_OK) {
continue;
}
sawInputEOS = false;
sawOutputEOS = false;
if (AMediaCodec_start(mediaCodec.get()) != AMEDIA_OK) {
continue;
}
videoWidth = trackWidth;
videoHeight = trackHeight;
videoFrameRate = fps;
videoFrameCount = frameCount;
videoRotation = rotation;
switch(videoRotation) {
case 90:
videoRotationCode = cv::ROTATE_90_CLOCKWISE;
break;
case 180:
videoRotationCode = cv::ROTATE_180;
break;
case 270:
videoRotationCode = cv::ROTATE_90_COUNTERCLOCKWISE;
break;
default:
videoRotationCode = -1;
break;
}
return true;
}
}
return false;
}
void cleanUp() {
sawInputEOS = true;
sawOutputEOS = true;
frameStride = 0;
frameWidth = 0;
frameHeight = 0;
colorFormat = 0;
videoWidth = 0;
videoHeight = 0;
videoFrameRate = 0;
videoFrameCount = 0;
videoRotation = 0;
videoRotationCode = -1;
}
};
class AndroidMediaNdkVideoWriter CV_FINAL :
public cv::IVideoWriter
{
typedef struct {
int fourcc;
const char* mime;
OutputFormat muxerFormat;
}
FourCCInfo;
static const int64_t TIMEOUT = 1000L;
static const FourCCInfo FOURCC_INFO[];
static const FourCCInfo* findInfo(int fourcc) {
for( const FourCCInfo *it = FOURCC_INFO; NULL != it->mime; it++ ) {
if (fourcc == it->fourcc) return it;
}
return NULL;
}
AMediaFormat* format;
AMediaCodec* encoder;
AMediaMuxer* muxer;
#if __ANDROID_API__ >= 26
ANativeWindow* surface;
#endif
long frameIndex;
int width;
int height;
double frameRate;
ssize_t videoTrackIndex;
int fd;
void drainEncoder(bool end) {
if (end) {
#if __ANDROID_API__ >= 26
AMediaCodec_signalEndOfInputStream(encoder);
#else
writeBytes(NULL, 0);
#endif
}
AMediaCodecBufferInfo bufferInfo;
ssize_t bufferIndex;
size_t bufferSize;
uint8_t *buffer;
while (true) {
bufferIndex = AMediaCodec_dequeueOutputBuffer(encoder, &bufferInfo, TIMEOUT);
if (bufferIndex >= 0) {
buffer = AMediaCodec_getOutputBuffer(encoder, (size_t)bufferIndex, &bufferSize);
if (NULL == buffer || 0 == bufferSize){
LOGE("Can't get output buffer");
break;
}
if (videoTrackIndex >= 0) {
bufferInfo.presentationTimeUs = frameIndex * 1000000L / frameRate;
LOGV("Muxer write to track %d: %d byte(s)", (int)videoTrackIndex, (int)bufferInfo.size);
AMediaMuxer_writeSampleData(muxer, (size_t)videoTrackIndex, buffer, &bufferInfo);
} else {
LOGE("Invalid video track !");
}
AMediaCodec_releaseOutputBuffer(encoder, (size_t)bufferIndex, false);
if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) break;
} else if (AMEDIACODEC_INFO_TRY_AGAIN_LATER == bufferIndex) {
if (!end) break;
} else if (AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED == bufferIndex) {
videoTrackIndex = AMediaMuxer_addTrack(muxer, AMediaCodec_getOutputFormat(encoder));
if (videoTrackIndex >= 0) {
AMediaMuxer_start(muxer);
}
LOGV("New videoTrackIndex: %d", (int)videoTrackIndex);
}
}
}
#if __ANDROID_API__ < 26
void writeBytes( uint8_t* inputBuffer, size_t inputBufferSize ) {
LOGV("[writeBytes] inputBufferSize=%u", (unsigned int)inputBufferSize);
ssize_t bufferIndex;
size_t bufferSize;
uint8_t* buffer;
size_t partialSize;
bool firstCall = true;
uint32_t flags;
while(inputBufferSize > 0 || firstCall) {
bufferIndex = AMediaCodec_dequeueInputBuffer(encoder, TIMEOUT);
if (bufferIndex >= 0) {
firstCall = false;
buffer = AMediaCodec_getInputBuffer(encoder, (size_t)bufferIndex, &bufferSize);
if (NULL == buffer || 0 == bufferSize) break;
flags = 0;
partialSize = (inputBufferSize > bufferSize) ? bufferSize : inputBufferSize;
if (partialSize > 0) {
memcpy(buffer, inputBuffer, partialSize);
inputBuffer += partialSize;
inputBufferSize -= partialSize;
if (inputBufferSize > 0) {
flags = AMEDIACODEC_BUFFER_FLAG_PARTIAL_FRAME;
}
} else {
flags = AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
}
LOGV(
"[writeBytes] partial - bufferIndex=%d, bufferSize=%u, partialSize=%u, remaining inputBufferSize=%u",
(int)bufferIndex, (unsigned int)bufferSize, (unsigned int)partialSize, (unsigned int)inputBufferSize
);
AMediaCodec_queueInputBuffer(encoder, (size_t)bufferIndex, 0, partialSize, frameIndex * 1000000L / frameRate, flags);
if (NULL != inputBuffer) drainEncoder(false);
}
}
}
#endif
public:
AndroidMediaNdkVideoWriter(const cv::String& filename, int fourcc, double fps, cv::Size frameSize, const VideoWriterParameters& params)
: format(NULL),
encoder(NULL),
muxer(NULL),
#if __ANDROID_API__ >= 26
surface(NULL),
#endif
frameIndex(0),
width(0),
height(0),
frameRate(0.),
videoTrackIndex(-1),
fd(-1) {
open(filename, fourcc, fps, frameSize, params);
}
virtual ~AndroidMediaNdkVideoWriter() { close(); }
virtual int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_ANDROID; }
virtual bool write(cv::InputArray image_ ) CV_OVERRIDE
{
if (!image_.isMat()) {
LOGE("Support only Mat input");
return false;
}
Mat image = image_.getMat();
if (image.cols > width || image.rows > height ||
(CV_8UC1 != image.type() && CV_8UC3 != image.type() && CV_8UC4 != image.type())) {
LOGE(
"Expected input to be a mat of maximum %d x %d of type CV_8UC1, CV_8UC3 or CV_8UC4"
" (%d, %d, %d), but received %d x %d of type %d",
width, height, CV_8UC1, CV_8UC3, CV_8UC4,
image.cols, image.rows, image.type()
);
return false;
}
#if __ANDROID_API__ >= 26
ANativeWindow_Buffer buffer;
if (0 != ANativeWindow_lock(surface, &buffer, NULL)) {
LOGE("Failed to lock the surface");
return false;
}
bool format_supported = true;
if (AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM == buffer.format) {
Mat bufferMat(image.rows, image.cols, CV_8UC4, buffer.bits, buffer.stride * 4);
switch (image.type()) {
case CV_8UC4: image.copyTo(bufferMat); break;
case CV_8UC3: cvtColor(image, bufferMat, COLOR_BGR2RGBA); break;
case CV_8UC1: cvtColor(image, bufferMat, COLOR_GRAY2RGBA); break;
}
} else {
LOGE("Unknown surface buffer format: 0x%x", buffer.format);
format_supported = false;
}
ANativeWindow_unlockAndPost(surface);
if (!format_supported) {
return false;
}
#else
//OpenCV doesn't support RGB to NV12 so we need to convert to YV12 and then manually changed it to NV12
Mat imageYV12;
switch (image.type()) {
case CV_8UC4: cvtColor(image, imageYV12, COLOR_RGBA2YUV_YV12); break;
case CV_8UC3: cvtColor(image, imageYV12, COLOR_BGR2YUV_YV12); break;
case CV_8UC1: imageYV12.create(image.rows + image.rows/2, image.cols, CV_8UC1);
image.copyTo(imageYV12.rowRange(0, image.rows));
imageYV12.rowRange(image.rows, imageYV12.rows) = 128;
break;
}
//convert from YV12 to NV12
if (image.type() != CV_8UC1) {
size_t yPlaneSize = width * height;
size_t vPlaneSize = yPlaneSize / 4;
Mat channels[2] = {
Mat( vPlaneSize, 1, CV_8UC1, imageYV12.ptr() + yPlaneSize + vPlaneSize ).clone(),
Mat( vPlaneSize, 1, CV_8UC1, imageYV12.ptr() + yPlaneSize ).clone()
};
Mat vuMat( vPlaneSize, 1, CV_8UC2, imageYV12.ptr() + yPlaneSize );
merge(channels, 2, vuMat);
}
writeBytes( imageYV12.ptr(), imageYV12.rows * imageYV12.cols );
#endif
drainEncoder(false);
frameIndex++;
return true;
}
virtual bool open( const cv::String& filename, int fourcc, double fps, cv::Size frameSize, const VideoWriterParameters& params )
{
media_status_t status;
close();
const FourCCInfo* info = findInfo(fourcc);
if (NULL == info) {
LOGE("ERROR: findInfo");
return false;
}
format = AMediaFormat_new();
if (NULL == format) {
LOGE("ERROR: AMediaFormat_new");
goto error;
}
LOGV("mime: %s, width: %d, height: %d, fps: %f", info->mime, frameSize.width, frameSize.height, fps);
AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, info->mime);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, frameSize.width);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, frameSize.height);
AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_FRAME_RATE, (float)fps);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 5);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, frameSize.width * frameSize.height * 5);
AMediaFormat_setInt32(
format, AMEDIAFORMAT_KEY_COLOR_FORMAT,
#if __ANDROID_API__ >= 26
COLOR_FormatSurface
#else
COLOR_FormatYUV420SemiPlanar
#endif
);
encoder = AMediaCodec_createEncoderByType(info->mime);
if (NULL == encoder) {
LOGE("ERROR: AMediaCodec_createEncoderByType");
goto error;
}
status = AMediaCodec_configure(encoder, format, NULL, NULL, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
if (AMEDIA_OK != status) {
LOGE("ERROR: AMediaCodec_configure (%d)", status);
goto error;
}
#if __ANDROID_API__ >= 26
status = AMediaCodec_createInputSurface(encoder, &surface);
if (AMEDIA_OK != status || NULL == surface) {
LOGE("ERROR: AMediaCodec_createInputSurface (%d)", status);
goto error;
}
ANativeWindow_setBuffersGeometry(surface, width, height, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM);
#endif
AMediaCodec_start(encoder);
fd = ::open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
LOGE("ERROR: open");
goto error;
}
muxer = AMediaMuxer_new(fd, info->muxerFormat);
if (NULL == muxer) {
LOGE("ERROR: AMediaMuxer_new");
goto error;
}
AMediaMuxer_setOrientationHint(muxer, params.get(CAP_PROP_ORIENTATION_META, 0));
frameIndex = 0;
width = frameSize.width;
height = frameSize.height;
frameRate = fps;
videoTrackIndex = -1;
return true;
error:
close();
return false;
}
virtual void close()
{
if (videoTrackIndex >= 0 && NULL != muxer) {
drainEncoder(true);
AMediaMuxer_stop(muxer);
}
if (NULL != encoder) AMediaCodec_delete(encoder);
if (NULL != muxer) AMediaMuxer_delete(muxer);
#if __ANDROID_API__ >= 26
if (NULL != surface) ANativeWindow_release(surface);
#endif
if (fd >= 0) ::close(fd);
if (NULL != format) AMediaFormat_delete(format);
format = NULL;
encoder = NULL;
muxer = NULL;
#if __ANDROID_API__ >= 26
surface = NULL;
#endif
frameIndex = 0;
width = 0;
height = 0;
frameRate = 0.;
videoTrackIndex = -1;
fd = -1;
}
virtual double getProperty(int) const CV_OVERRIDE { return VIDEOWRITER_PROP_UNKNOWN; }
virtual bool setProperty(int, double) CV_OVERRIDE { return false; }
virtual bool isOpened() const CV_OVERRIDE { return NULL != encoder; }
};
const AndroidMediaNdkVideoWriter::FourCCInfo AndroidMediaNdkVideoWriter::FOURCC_INFO[] = {
{ CV_FOURCC('H', '2', '6', '4'), "video/avc", AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4 },
{ CV_FOURCC('H', '2', '6', '5'), "video/hevc", AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4 },
{ CV_FOURCC('H', '2', '6', '3'), "video/3gpp", AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4 },
{ CV_FOURCC('M', 'P', '4', 'V'), "video/mp4v-es", AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4 },
{ 0, NULL, AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4 },
};
/****************** Implementation of interface functions ********************/
Ptr<IVideoCapture> cv::createAndroidCapture_file(const std::string &filename, const VideoCaptureParameters& ) {
Ptr<AndroidMediaNdkCapture> res = makePtr<AndroidMediaNdkCapture>();
if (res && res->initCapture(filename.c_str()))
return res;
return Ptr<IVideoCapture>();
}
Ptr<IVideoWriter> cv::createAndroidVideoWriter(
const std::string& filename, int fourcc,
double fps, const cv::Size& frameSize,
const VideoWriterParameters& params) {
Ptr<AndroidMediaNdkVideoWriter> writer = makePtr<AndroidMediaNdkVideoWriter>(filename, fourcc, fps, frameSize, params);
if (writer && writer->isOpened())
return writer;
return Ptr<IVideoWriter>();
}
+647
View File
@@ -0,0 +1,647 @@
////////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//
//
// The code has been contributed by Arkadiusz Raj on 2016 Oct
//
#include "precomp.hpp"
#include "cap_interface.hpp"
using namespace cv;
#ifdef HAVE_ARAVIS_API
#include <arv.h>
//
// This file provides wrapper for using Aravis SDK library to access GigE and USB 3 Vision cameras.
// Aravis library (version 0.8) shall be installed else this code will not be included in build.
//
// To include this module invoke cmake with -DWITH_ARAVIS=ON
//
// Please obvserve, that jumbo frames are required when high fps & 16bit data is selected.
// (camera, switches/routers and the computer this software is running on)
//
// Basic usage: VideoCapture cap(<camera id>, CAP_ARAVIS);
//
// Supported properties:
// read/write
// CAP_PROP_AUTO_EXPOSURE(0|1)
// CAP_PROP_EXPOSURE(t), t in seconds
// CAP_PROP_BRIGHTNESS (ev), exposure compensation in EV for auto exposure algorithm
// CAP_PROP_GAIN(g), g >=0 or -1 for automatic control if CAP_PROP_AUTO_EXPOSURE is true
// CAP_PROP_FPS(f)
// CAP_PROP_FOURCC(type)
// CAP_PROP_BUFFERSIZE(n)
// read only:
// CAP_PROP_POS_MSEC
// CAP_PROP_FRAME_WIDTH
// CAP_PROP_FRAME_HEIGHT
//
// Supported types of data:
// video/x-raw, fourcc:'GREY' -> 8bit, 1 channel
// video/x-raw, fourcc:'Y800' -> 8bit, 1 channel
// video/x-raw, fourcc:'Y12 ' -> 12bit, 1 channel
// video/x-raw, fourcc:'Y16 ' -> 16bit, 1 channel
// video/x-raw, fourcc:'GRBG' -> 8bit, 1 channel
//
#define MODE_GREY CV_FOURCC_MACRO('G','R','E','Y')
#define MODE_Y800 CV_FOURCC_MACRO('Y','8','0','0')
#define MODE_Y12 CV_FOURCC_MACRO('Y','1','2',' ')
#define MODE_Y16 CV_FOURCC_MACRO('Y','1','6',' ')
#define MODE_GRBG CV_FOURCC_MACRO('G','R','B','G')
#define CLIP(a,b,c) (cv::max(cv::min((a),(c)),(b)))
/********************* Capturing video from camera via Aravis *********************/
class CvCaptureCAM_Aravis : public IVideoCapture
{
public:
CvCaptureCAM_Aravis();
~CvCaptureCAM_Aravis()
{
close();
}
bool open(int);
void close();
double getProperty(int) const CV_OVERRIDE;
bool setProperty(int, double) CV_OVERRIDE;
bool grabFrame() CV_OVERRIDE;
bool retrieveFrame(int, OutputArray) CV_OVERRIDE;
int getCaptureDomain() CV_OVERRIDE
{
return cv::CAP_ARAVIS;
}
bool isOpened() const CV_OVERRIDE { return stream != NULL; }
protected:
bool create(int);
bool init_buffers();
void stopCapture();
bool startCapture();
bool getDeviceNameById(int id, std::string &device);
void autoExposureControl(const Mat &);
double getExpectedMidGrey(ArvPixelFormat fmt) const;
ArvCamera *camera; // Camera to control.
ArvStream *stream; // Object for video stream reception.
void *framebuffer; //
unsigned int payload; // Width x height x Pixel width.
int widthMin; // Camera sensor minimum width.
int widthMax; // Camera sensor maximum width.
int heightMin; // Camera sensor minimum height.
int heightMax; // Camera sensor maximum height.
bool fpsAvailable;
double fpsMin; // Camera minimum fps.
double fpsMax; // Camera maximum fps.
bool gainAvailable;
double gainMin; // Camera minimum gain.
double gainMax; // Camera maximum gain.
bool exposureAvailable;
double exposureMin; // Camera's minimum exposure time.
double exposureMax; // Camera's maximum exposure time.
bool controlExposure; // Flag if automatic exposure shall be done by this SW
double exposureCompensation;
bool autoGain;
double targetGrey; // Target grey value (mid grey))
bool softwareTriggered; // Flag if the camera is software triggered
bool allowAutoTrigger; // Flag that user allowed to trigger software triggered cameras automatically
int num_buffers; // number of payload transmission buffers
ArvPixelFormat pixelFormat; // pixel format
int xoffset; // current frame region x offset
int yoffset; // current frame region y offset
int width; // current frame width of frame
int height; // current frame height of image
double fps; // current value of fps
double exposure; // current value of exposure time
double gain; // current value of gain
double midGrey; // current value of mid grey (brightness)
unsigned frameID; // current frame id
unsigned prevFrameID;
};
CvCaptureCAM_Aravis::CvCaptureCAM_Aravis()
{
camera = NULL;
stream = NULL;
framebuffer = NULL;
payload = 0;
widthMin = widthMax = heightMin = heightMax = 0;
xoffset = yoffset = width = height = 0;
fpsMin = fpsMax = gainMin = gainMax = exposureMin = exposureMax = 0;
controlExposure = false;
exposureCompensation = 0;
targetGrey = 0;
frameID = prevFrameID = 0;
allowAutoTrigger = false;
num_buffers = 10;
}
void CvCaptureCAM_Aravis::close()
{
if(camera) {
stopCapture();
g_object_unref(camera);
camera = NULL;
}
}
bool CvCaptureCAM_Aravis::getDeviceNameById(int id, std::string &device)
{
arv_update_device_list();
if((id >= 0) && (id < (int)arv_get_n_devices())) {
device = arv_get_device_id(id);
return true;
}
return false;
}
bool CvCaptureCAM_Aravis::create( int index )
{
std::string deviceName;
if(!getDeviceNameById(index, deviceName))
return false;
return NULL != (camera = arv_camera_new(deviceName.c_str(), NULL));
}
bool CvCaptureCAM_Aravis::init_buffers()
{
if(stream) {
g_object_unref(stream);
stream = NULL;
}
if( (stream = arv_camera_create_stream(camera, NULL, NULL, NULL)) ) {
if( arv_camera_is_gv_device(camera) ) {
g_object_set(stream,
"socket-buffer", ARV_GV_STREAM_SOCKET_BUFFER_AUTO,
"socket-buffer-size", 0, NULL);
g_object_set(stream,
"packet-resend", ARV_GV_STREAM_PACKET_RESEND_NEVER, NULL);
g_object_set(stream,
"packet-timeout", (unsigned) 40000,
"frame-retention", (unsigned) 200000, NULL);
}
payload = arv_camera_get_payload (camera, NULL);
for (int i = 0; i < num_buffers; i++)
arv_stream_push_buffer(stream, arv_buffer_new(payload, NULL));
return true;
}
return false;
}
bool CvCaptureCAM_Aravis::open( int index )
{
if(create(index)) {
// fetch properties bounds
arv_camera_get_width_bounds(camera, &widthMin, &widthMax, NULL);
arv_camera_get_height_bounds(camera, &heightMin, &heightMax, NULL);
arv_camera_set_region(camera, 0, 0, widthMax, heightMax, NULL);
if( (fpsAvailable = arv_camera_is_frame_rate_available(camera, NULL)) )
arv_camera_get_frame_rate_bounds(camera, &fpsMin, &fpsMax, NULL);
if( (gainAvailable = arv_camera_is_gain_available(camera, NULL)) )
arv_camera_get_gain_bounds (camera, &gainMin, &gainMax, NULL);
if( (exposureAvailable = arv_camera_is_exposure_time_available(camera, NULL)) )
arv_camera_get_exposure_time_bounds (camera, &exposureMin, &exposureMax, NULL);
// get initial values
pixelFormat = arv_camera_get_pixel_format(camera, NULL);
// If camera's pixel format is not one of the supported formats, set a default
if (pixelFormat != ARV_PIXEL_FORMAT_MONO_8 &&
pixelFormat != ARV_PIXEL_FORMAT_BAYER_GR_8 &&
pixelFormat != ARV_PIXEL_FORMAT_MONO_12 &&
pixelFormat != ARV_PIXEL_FORMAT_MONO_16) {
pixelFormat = ARV_PIXEL_FORMAT_MONO_8;
arv_camera_set_pixel_format(camera, pixelFormat, NULL);
CV_LOG_WARNING(NULL, "Current camera pixel format is not supported. Failed back to MONO_8.");
}
midGrey = getExpectedMidGrey(pixelFormat);
exposure = exposureAvailable ? arv_camera_get_exposure_time(camera, NULL) : 0;
gain = gainAvailable ? arv_camera_get_gain(camera, NULL) : 0;
fps = arv_camera_get_frame_rate(camera, NULL);
softwareTriggered = (strcmp(arv_camera_get_trigger_source(camera, NULL), "Software") == 0);
return startCapture();
}
return false;
}
bool CvCaptureCAM_Aravis::grabFrame()
{
// remove content of previous frame
framebuffer = NULL;
if(stream) {
ArvBuffer *arv_buffer = NULL;
int max_tries = 10;
int tries = 0;
if (softwareTriggered && allowAutoTrigger) {
arv_camera_software_trigger (camera, NULL);
}
for(; tries < max_tries; tries ++) {
arv_buffer = arv_stream_timeout_pop_buffer (stream, 200000);
if (arv_buffer != NULL && arv_buffer_get_status (arv_buffer) != ARV_BUFFER_STATUS_SUCCESS) {
arv_stream_push_buffer (stream, arv_buffer);
} else break;
}
if(arv_buffer != NULL && tries < max_tries) {
size_t buffer_size;
framebuffer = (void*)arv_buffer_get_data (arv_buffer, &buffer_size);
// retrieve image size properties
arv_buffer_get_image_region (arv_buffer, &xoffset, &yoffset, &width, &height);
// retrieve image ID set by camera
frameID = arv_buffer_get_frame_id(arv_buffer);
arv_stream_push_buffer(stream, arv_buffer);
return true;
}
}
return false;
}
bool CvCaptureCAM_Aravis::retrieveFrame(int, OutputArray arr)
{
if(framebuffer) {
int depth = 0, channels = 0;
switch(pixelFormat) {
case ARV_PIXEL_FORMAT_MONO_8:
case ARV_PIXEL_FORMAT_BAYER_GR_8:
depth = CV_8U;
channels = 1;
break;
case ARV_PIXEL_FORMAT_MONO_12:
case ARV_PIXEL_FORMAT_MONO_16:
depth = CV_16U;
channels = 1;
break;
default:
return false;
}
Mat src(Size( width, height ), CV_MAKE_TYPE(depth, channels), framebuffer);
if(controlExposure && ((frameID - prevFrameID) >= 3)) {
// control exposure every third frame
// i.e. skip frame taken with previous exposure setup
autoExposureControl(src);
}
src.copyTo(arr);
return true;
}
return false;
}
void CvCaptureCAM_Aravis::autoExposureControl(const Mat & image)
{
// Software control of exposure parameters utilizing
// automatic change of exposure time & gain
// Priority is set as follows:
// - to increase brightness, first increase time then gain
// - to decrease brightness, first decrease gain then time
// calc mean value for luminance or green channel
double brightness = cv::mean(image)[image.channels() > 1 ? 1 : 0];
if(brightness < 1) brightness = 1;
// mid point - 100 % means no change
static const double dmid = 100;
// distance from optimal value as a percentage
double d = (targetGrey * dmid) / brightness;
if(d >= dmid) d = ( d + (dmid * 2) ) / 3;
prevFrameID = frameID;
midGrey = brightness;
double maxe = 1e6 / fps;
double ne = CLIP( ( exposure * d ) / ( dmid * std::pow(sqrt(2), -2 * exposureCompensation) ), exposureMin, maxe);
// if change of value requires intervention
if(std::fabs(d-dmid) > 5) {
double ev, ng = 0;
if(gainAvailable && autoGain) {
ev = log( d / dmid ) / log(2);
ng = CLIP( gain + ev + exposureCompensation, gainMin, gainMax);
if( ng < gain ) {
// priority 1 - reduce gain
arv_camera_set_gain(camera, (gain = ng), NULL);
return;
}
}
if(exposureAvailable) {
// priority 2 - control of exposure time
if(std::fabs(exposure - ne) > 2) {
// we have not yet reach the max-e level
arv_camera_set_exposure_time(camera, (exposure = ne), NULL);
return;
}
}
if(gainAvailable && autoGain) {
if(exposureAvailable) {
// exposure at maximum - increase gain if possible
if(ng > gain && ng < gainMax && ne >= maxe) {
arv_camera_set_gain(camera, (gain = ng), NULL);
return;
}
} else {
// priority 3 - increase gain
arv_camera_set_gain(camera, (gain = ng), NULL);
return;
}
}
}
// if gain can be reduced - do it
if(gainAvailable && autoGain && exposureAvailable) {
if(gain > gainMin && exposure < maxe) {
exposure = CLIP( ne * 1.05, exposureMin, maxe);
arv_camera_set_exposure_time(camera, exposure, NULL);
}
}
}
double CvCaptureCAM_Aravis::getProperty( int property_id ) const
{
switch(property_id) {
case CAP_PROP_POS_MSEC:
return (double)frameID/fps;
case CAP_PROP_FRAME_WIDTH:
return width;
case CAP_PROP_FRAME_HEIGHT:
return height;
case CAP_PROP_AUTO_EXPOSURE:
return (controlExposure ? 1 : 0);
case CAP_PROP_BRIGHTNESS:
return exposureCompensation;
case CAP_PROP_EXPOSURE:
if(exposureAvailable) {
/* exposure time in seconds, like 1/100 s */
return arv_camera_get_exposure_time(camera, NULL) / 1e6;
}
break;
case CAP_PROP_FPS:
if(fpsAvailable) {
return arv_camera_get_frame_rate(camera, NULL);
}
break;
case CAP_PROP_GAIN:
if(gainAvailable) {
return arv_camera_get_gain(camera, NULL);
}
break;
case CAP_PROP_FOURCC:
{
ArvPixelFormat currFormat = arv_camera_get_pixel_format(camera, NULL);
switch( currFormat ) {
case ARV_PIXEL_FORMAT_MONO_8:
return MODE_Y800;
case ARV_PIXEL_FORMAT_MONO_12:
return MODE_Y12;
case ARV_PIXEL_FORMAT_MONO_16:
return MODE_Y16;
case ARV_PIXEL_FORMAT_BAYER_GR_8:
return MODE_GRBG;
}
}
break;
case CAP_PROP_BUFFERSIZE:
if(stream) {
int in, out;
arv_stream_get_n_buffers(stream, &in, &out);
// return number of available buffers in Aravis output queue
return out;
}
break;
case cv::CAP_PROP_ARAVIS_AUTOTRIGGER:
{
return allowAutoTrigger ? 1. : 0.;
}
break;
}
return CAP_PROP_UNKNOWN;
}
double CvCaptureCAM_Aravis::getExpectedMidGrey(ArvPixelFormat fmt) const
{
double grey = 0.;
switch(fmt)
{
case ARV_PIXEL_FORMAT_MONO_8:
case ARV_PIXEL_FORMAT_BAYER_GR_8:
grey = 128.;
break;
case ARV_PIXEL_FORMAT_MONO_12:
grey = 2048.;
break;
case ARV_PIXEL_FORMAT_MONO_16:
grey = 32768.;
break;
}
return grey;
}
bool CvCaptureCAM_Aravis::setProperty( int property_id, double value )
{
switch(property_id) {
case CAP_PROP_AUTO_EXPOSURE:
if(exposureAvailable || gainAvailable) {
if( (controlExposure = (bool)(int)value) ) {
exposure = exposureAvailable ? arv_camera_get_exposure_time(camera, NULL) : 0;
gain = gainAvailable ? arv_camera_get_gain(camera, NULL) : 0;
}
}
break;
case CAP_PROP_BRIGHTNESS:
exposureCompensation = CLIP(value, -3., 3.);
break;
case CAP_PROP_EXPOSURE:
if(exposureAvailable) {
/* exposure time in seconds, like 1/100 s */
value *= 1e6; // -> from s to us
arv_camera_set_exposure_time(camera, exposure = CLIP(value, exposureMin, exposureMax), NULL);
break;
} else return false;
case CAP_PROP_FPS:
if(fpsAvailable) {
arv_camera_set_frame_rate(camera, fps = CLIP(value, fpsMin, fpsMax), NULL);
break;
} else return false;
case CAP_PROP_GAIN:
if(gainAvailable) {
if ( (autoGain = (-1 == value) ) )
break;
arv_camera_set_gain(camera, gain = CLIP(value, gainMin, gainMax), NULL);
break;
} else return false;
case CAP_PROP_FOURCC:
{
ArvPixelFormat newFormat = pixelFormat;
switch((int)value) {
case MODE_GREY:
case MODE_Y800:
newFormat = ARV_PIXEL_FORMAT_MONO_8;
break;
case MODE_Y12:
newFormat = ARV_PIXEL_FORMAT_MONO_12;
break;
case MODE_Y16:
newFormat = ARV_PIXEL_FORMAT_MONO_16;
break;
case MODE_GRBG:
newFormat = ARV_PIXEL_FORMAT_BAYER_GR_8;
break;
}
if(newFormat != pixelFormat) {
stopCapture();
arv_camera_set_pixel_format(camera, pixelFormat = newFormat, NULL);
midGrey = getExpectedMidGrey(newFormat);
startCapture();
}
}
break;
case CAP_PROP_BUFFERSIZE:
{
int x = (int)value;
if((x > 0) && (x != num_buffers)) {
stopCapture();
num_buffers = x;
startCapture();
}
}
break;
case cv::CAP_PROP_ARAVIS_AUTOTRIGGER:
{
allowAutoTrigger = (bool) value;
}
break;
default:
return false;
}
return true;
}
void CvCaptureCAM_Aravis::stopCapture()
{
arv_camera_stop_acquisition(camera, NULL);
if(stream) {
g_object_unref(stream);
stream = NULL;
}
}
bool CvCaptureCAM_Aravis::startCapture()
{
if(init_buffers() ) {
arv_camera_set_acquisition_mode(camera, ARV_ACQUISITION_MODE_CONTINUOUS, NULL);
arv_camera_start_acquisition(camera, NULL);
return true;
}
return false;
}
cv::Ptr<cv::IVideoCapture> cv::create_Aravis_capture( int index )
{
Ptr<CvCaptureCAM_Aravis> capture = makePtr<CvCaptureCAM_Aravis>();
if(capture->open(index)) {
return capture;
}
return NULL;
}
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+706
View File
@@ -0,0 +1,706 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include "cap_interface.hpp"
#ifdef HAVE_DC1394_2
#include <unistd.h>
#include <stdint.h>
#ifdef _WIN32
// On Windows, we have no sys/select.h, but we need to pick up
// select() which is in winsock2.
#ifndef __SYS_SELECT_H__
#define __SYS_SELECT_H__ 1
#include <winsock2.h>
#endif
#else
#include <sys/select.h>
#endif /*_WIN32*/
#include <dc1394/dc1394.h>
#include <stdlib.h>
#include <string.h>
using namespace cv;
struct CvDC1394
{
CvDC1394();
~CvDC1394();
dc1394_t* dc;
fd_set camFds;
};
CvDC1394::CvDC1394()
{
dc = dc1394_new();
FD_ZERO(&camFds);
}
CvDC1394::~CvDC1394()
{
if (dc)
dc1394_free(dc);
dc = 0;
}
static CvDC1394& getDC1394()
{
static CvDC1394 dc1394;
return dc1394;
}
#define CAP_PROP_MAX_DC1394 31
class CvCaptureCAM_DC1394_v2_CPP : public IVideoCapture
{
public:
static int dc1394properties[CAP_PROP_MAX_DC1394];
CvCaptureCAM_DC1394_v2_CPP();
~CvCaptureCAM_DC1394_v2_CPP()
{
close();
}
bool open(int index);
void close();
double getProperty(int) const CV_OVERRIDE;
bool setProperty(int, double) CV_OVERRIDE;
bool grabFrame() CV_OVERRIDE;
bool retrieveFrame(int, OutputArray) CV_OVERRIDE;
int getCaptureDomain() CV_OVERRIDE { return CAP_DC1394; }
bool isOpened() const CV_OVERRIDE { return dcCam && started; }
protected:
virtual bool startCapture();
uint64_t guid;
dc1394camera_t* dcCam;
int isoSpeed;
int videoMode;
int frameWidth, frameHeight;
double fps;
int nDMABufs;
bool started;
int userMode;
enum { VIDERE = 0x5505 };
int cameraId;
bool colorStereo;
dc1394bayer_method_t bayer;
dc1394color_filter_t bayerFilter;
enum { NIMG = 2 };
Mat img[NIMG];
dc1394video_frame_t* frameC;
int nimages;
dc1394featureset_t feature_set;
};
//mapping CAP_PROP_ to DC1394_FEATUREs
int CvCaptureCAM_DC1394_v2_CPP::dc1394properties[CAP_PROP_MAX_DC1394] = {
-1, //no corresponding feature for CAP_PROP_POS_MSEC
-1,-1,-1,-1,
DC1394_FEATURE_FRAME_RATE, //CAP_PROP_FPS - fps can be set for format 7 only!
-1,-1,-1,-1,
DC1394_FEATURE_BRIGHTNESS, //CAP_PROP_BRIGHTNESS 10
-1,
DC1394_FEATURE_SATURATION, //CAP_PROP_SATURATION
DC1394_FEATURE_HUE,
DC1394_FEATURE_GAIN,
DC1394_FEATURE_SHUTTER, //CAP_PROP_EXPOSURE
-1, //CAP_PROP_CONVERT_RGB
DC1394_FEATURE_WHITE_BALANCE, //corresponds to CAP_PROP_WHITE_BALANCE_BLUE_U and CAP_PROP_WHITE_BALANCE_RED_V, see set function to check these props are set
-1,-1,
DC1394_FEATURE_SHARPNESS, //20
DC1394_FEATURE_EXPOSURE, //CAP_PROP_AUTO_EXPOSURE - this is auto exposure according to the IIDC standard
DC1394_FEATURE_GAMMA, //CAP_PROP_GAMMA
DC1394_FEATURE_TEMPERATURE, //CAP_PROP_TEMPERATURE
DC1394_FEATURE_TRIGGER, //CAP_PROP_TRIGGER
DC1394_FEATURE_TRIGGER_DELAY, //CAP_PROP_TRIGGER_DELAY
DC1394_FEATURE_WHITE_BALANCE, //CAP_PROP_WHITE_BALANCE_RED_V
DC1394_FEATURE_ZOOM, //CAP_PROP_ZOOM
DC1394_FEATURE_FOCUS, //CAP_PROP_FOCUS
-1 //CAP_PROP_GUID
};
CvCaptureCAM_DC1394_v2_CPP::CvCaptureCAM_DC1394_v2_CPP()
{
guid = 0;
dcCam = 0;
isoSpeed = 400;
fps = 15;
// Reset the value here to 1 in order to ensure only a single frame is stored in the buffer!
nDMABufs = 8;
started = false;
cameraId = 0;
colorStereo = false;
bayer = DC1394_BAYER_METHOD_BILINEAR;
bayerFilter = DC1394_COLOR_FILTER_GRBG;
frameWidth = 640;
frameHeight = 480;
frameC = 0;
nimages = 1;
userMode = -1;
}
bool CvCaptureCAM_DC1394_v2_CPP::startCapture()
{
int i;
int code = 0;
if (!dcCam)
return false;
if (isoSpeed > 0)
{
// if capable set operation mode to 1394b for iso speeds above 400
if (isoSpeed > 400 && dcCam->bmode_capable == DC1394_TRUE)
{
dc1394_video_set_operation_mode(dcCam, DC1394_OPERATION_MODE_1394B);
}
code = dc1394_video_set_iso_speed(dcCam,
isoSpeed <= 100 ? DC1394_ISO_SPEED_100 :
isoSpeed <= 200 ? DC1394_ISO_SPEED_200 :
isoSpeed <= 400 ? DC1394_ISO_SPEED_400 :
isoSpeed <= 800 ? DC1394_ISO_SPEED_800 :
isoSpeed == 1600 ? DC1394_ISO_SPEED_1600 :
DC1394_ISO_SPEED_3200);
}
dc1394video_modes_t videoModes;
dc1394_video_get_supported_modes(dcCam, &videoModes);
// should a specific mode be used
while (userMode >= 0) // 'if' semantic, no real loop here
{
dc1394video_mode_t wantedMode;
if (userMode < (int)videoModes.num)
{
// set mode from number, for example the second supported mode, i.e userMode = 1
wantedMode = videoModes.modes[userMode];
}
else if ((userMode >= DC1394_VIDEO_MODE_MIN) && (userMode <= DC1394_VIDEO_MODE_MAX))
{
// set modes directly from DC134 constants (from dc1394video_mode_t)
//search for wanted mode, to check if camera supports it
int j = 0;
while ((j < (int)videoModes.num) && videoModes.modes[j] != userMode)
{
j++;
}
if (!(j < (int)videoModes.num))
{
userMode = -1; // wanted mode not supported, search for best mode
break;
}
wantedMode = videoModes.modes[j];
}
else
{
userMode = -1; // wanted mode not supported, search for best mode
break;
}
//if userMode is available: set it and update size
{
code = dc1394_video_set_mode(dcCam, wantedMode);
uint32_t width = 0, height = 0;
dc1394_get_image_size_from_video_mode(dcCam, wantedMode, &width, &height);
frameWidth = (int)width;
frameHeight = (int)height;
}
break;
}
if (userMode == -1 && (frameWidth > 0 || frameHeight > 0))
{
dc1394video_mode_t bestMode = (dc1394video_mode_t)(-1);
for (i = 0; i < (int)videoModes.num; i++)
{
dc1394video_mode_t mode = videoModes.modes[i];
if (mode >= DC1394_VIDEO_MODE_FORMAT7_MIN && mode <= DC1394_VIDEO_MODE_FORMAT7_MAX)
continue;
int pref = -1;
dc1394color_coding_t colorCoding;
dc1394_get_color_coding_from_video_mode(dcCam, mode, &colorCoding);
uint32_t width, height;
dc1394_get_image_size_from_video_mode(dcCam, mode, &width, &height);
if ((int)width == frameWidth || (int)height == frameHeight)
{
if (colorCoding == DC1394_COLOR_CODING_RGB8 ||
colorCoding == DC1394_COLOR_CODING_RAW8)
{
bestMode = mode;
break;
}
if (colorCoding == DC1394_COLOR_CODING_YUV411 ||
colorCoding == DC1394_COLOR_CODING_YUV422 ||
(colorCoding == DC1394_COLOR_CODING_YUV444 &&
pref < 1))
{
bestMode = mode;
pref = 1;
break;
}
if (colorCoding == DC1394_COLOR_CODING_MONO8)
{
bestMode = mode;
pref = 0;
}
}
}
if ((int)bestMode >= 0)
code = dc1394_video_set_mode(dcCam, bestMode);
}
if (fps > 0)
{
dc1394video_mode_t mode;
dc1394framerates_t framerates;
double minDiff = DBL_MAX;
dc1394framerate_t bestFps = (dc1394framerate_t) - 1;
dc1394_video_get_mode(dcCam, &mode);
dc1394_video_get_supported_framerates(dcCam, mode, &framerates);
for (i = 0; i < (int)framerates.num; i++)
{
dc1394framerate_t ifps = framerates.framerates[i];
double fps1 = (1 << (ifps - DC1394_FRAMERATE_1_875)) * 1.875;
double diff = fabs(fps1 - fps);
if (diff < minDiff)
{
minDiff = diff;
bestFps = ifps;
}
}
if ((int)bestFps >= 0)
code = dc1394_video_set_framerate(dcCam, bestFps);
}
if (cameraId == VIDERE)
{
bayerFilter = DC1394_COLOR_FILTER_GBRG;
nimages = 2;
uint32_t value = 0;
dc1394_get_control_register(dcCam, 0x50c, &value);
colorStereo = (value & 0x80000000) != 0;
}
code = dc1394_capture_setup(dcCam, nDMABufs, DC1394_CAPTURE_FLAGS_DEFAULT);
if (code >= 0)
{
FD_SET(dc1394_capture_get_fileno(dcCam), &getDC1394().camFds);
dc1394_video_set_transmission(dcCam, DC1394_ON);
started = true;
}
return code >= 0;
}
bool CvCaptureCAM_DC1394_v2_CPP::open(int index)
{
bool result = false;
dc1394camera_list_t* cameraList = 0;
dc1394error_t err;
close();
if (!getDC1394().dc)
goto _exit_;
err = dc1394_camera_enumerate(getDC1394().dc, &cameraList);
if (err < 0 || !cameraList || (unsigned)index >= (unsigned)cameraList->num)
goto _exit_;
guid = cameraList->ids[index].guid;
dcCam = dc1394_camera_new(getDC1394().dc, guid);
if (!dcCam)
goto _exit_;
cameraId = dcCam->vendor_id;
//get all features
if (dc1394_feature_get_all(dcCam,&feature_set) == DC1394_SUCCESS)
result = true;
else
result = false;
_exit_:
if (cameraList)
dc1394_camera_free_list(cameraList);
return result;
}
void CvCaptureCAM_DC1394_v2_CPP::close()
{
if (dcCam)
{
// check for fileno valid before using
int fileno=dc1394_capture_get_fileno(dcCam);
if (fileno>=0 && FD_ISSET(fileno, &getDC1394().camFds))
FD_CLR(fileno, &getDC1394().camFds);
dc1394_video_set_transmission(dcCam, DC1394_OFF);
dc1394_capture_stop(dcCam);
dc1394_camera_free(dcCam);
dcCam = 0;
started = false;
}
for (int i = 0; i < NIMG; i++)
{
img[i].release();
}
if (frameC)
{
if (frameC->image)
free(frameC->image);
free(frameC);
frameC = 0;
}
}
bool CvCaptureCAM_DC1394_v2_CPP::grabFrame()
{
dc1394capture_policy_t policy = DC1394_CAPTURE_POLICY_WAIT;
bool code = false, isColor;
dc1394video_frame_t *dcFrame = 0, *fs = 0;
int i, nch;
if (!dcCam || (!started && !startCapture()))
return false;
dc1394_capture_dequeue(dcCam, policy, &dcFrame);
if (!dcFrame)
return false;
if (/*dcFrame->frames_behind > 1 ||*/ dc1394_capture_is_frame_corrupt(dcCam, dcFrame) == DC1394_TRUE)
{
goto _exit_;
}
isColor = dcFrame->color_coding != DC1394_COLOR_CODING_MONO8 &&
dcFrame->color_coding != DC1394_COLOR_CODING_MONO16 &&
dcFrame->color_coding != DC1394_COLOR_CODING_MONO16S;
if (nimages == 2)
{
fs = (dc1394video_frame_t*)calloc(1, sizeof(*fs));
dc1394_deinterlace_stereo_frames(dcFrame, fs, DC1394_STEREO_METHOD_INTERLACED);
dc1394_capture_enqueue(dcCam, dcFrame); // release the captured frame as soon as possible
dcFrame = 0;
if (!fs->image)
goto _exit_;
isColor = colorStereo;
}
nch = isColor ? 3 : 1;
for (i = 0; i < nimages; i++)
{
dc1394video_frame_t f = fs ? *fs : *dcFrame, *fc = &f;
f.size[1] /= nimages;
f.image += f.size[0] * f.size[1] * i; // TODO: make it more universal
if (isColor)
{
if (!frameC)
frameC = (dc1394video_frame_t*)calloc(1, sizeof(*frameC));
frameC->color_coding = nch == 3 ? DC1394_COLOR_CODING_RGB8 : DC1394_COLOR_CODING_MONO8;
if (nimages == 1)
{
dc1394_convert_frames(&f, frameC);
dc1394_capture_enqueue(dcCam, dcFrame);
dcFrame = 0;
}
else
{
f.color_filter = bayerFilter;
dc1394_debayer_frames(&f, frameC, bayer);
}
fc = frameC;
}
Mat frame(Size(fc->size[0], fc->size[1]), CV_MAKE_TYPE(CV_8U, nch), fc->image);
img[i].create(frame.size(), frame.type());
// Swap R&B channels:
if (nch==3)
{
cv::cvtColor(frame, img[i], cv::COLOR_RGB2BGR);
}
else
{
frame.copyTo(img[i]);
}
}
code = true;
_exit_:
if (dcFrame)
dc1394_capture_enqueue(dcCam, dcFrame);
if (fs)
{
if (fs->image)
free(fs->image);
free(fs);
}
return code;
}
bool CvCaptureCAM_DC1394_v2_CPP::retrieveFrame(int idx, OutputArray arr)
{
if (0 <= idx && idx < nimages)
img[idx].copyTo(arr);
else
return false;
return true;
}
double CvCaptureCAM_DC1394_v2_CPP::getProperty(int propId) const
{
// Simulate mutable (C++11-like) member variable
dc1394featureset_t& fs = const_cast<dc1394featureset_t&>(feature_set);
switch (propId)
{
case CAP_PROP_FRAME_WIDTH:
return frameWidth ? frameWidth : frameHeight*4 / 3;
case CAP_PROP_FRAME_HEIGHT:
return frameHeight ? frameHeight : frameWidth*3 / 4;
case CAP_PROP_FPS:
return fps;
case CAP_PROP_RECTIFICATION:
CV_LOG_WARNING(NULL, "cap_dc1394: rectification support has been removed from videoio module");
return CAP_PROP_UNKNOWN;
case CAP_PROP_WHITE_BALANCE_BLUE_U:
if (dc1394_feature_whitebalance_get_value(dcCam,
&fs.feature[DC1394_FEATURE_WHITE_BALANCE-DC1394_FEATURE_MIN].BU_value,
&fs.feature[DC1394_FEATURE_WHITE_BALANCE-DC1394_FEATURE_MIN].RV_value) == DC1394_SUCCESS)
return feature_set.feature[DC1394_FEATURE_WHITE_BALANCE-DC1394_FEATURE_MIN].BU_value;
break;
case CAP_PROP_WHITE_BALANCE_RED_V:
if (dc1394_feature_whitebalance_get_value(dcCam,
&fs.feature[DC1394_FEATURE_WHITE_BALANCE-DC1394_FEATURE_MIN].BU_value,
&fs.feature[DC1394_FEATURE_WHITE_BALANCE-DC1394_FEATURE_MIN].RV_value) == DC1394_SUCCESS)
return feature_set.feature[DC1394_FEATURE_WHITE_BALANCE-DC1394_FEATURE_MIN].RV_value;
break;
case CAP_PROP_GUID:
//the least 32 bits are enough to identify the camera
return (double) (guid & 0x00000000FFFFFFFF);
break;
case CAP_PROP_MODE:
return (double) userMode;
break;
case CAP_PROP_ISO_SPEED:
return (double) isoSpeed;
case CAP_PROP_BUFFERSIZE:
return (double) nDMABufs;
default:
if (propId<CAP_PROP_MAX_DC1394 && dc1394properties[propId]!=-1
&& dcCam)
//&& feature_set.feature[dc1394properties[propId]-DC1394_FEATURE_MIN].on_off_capable)
if (dc1394_feature_get_value(dcCam,(dc1394feature_t)dc1394properties[propId],
&fs.feature[dc1394properties[propId]-DC1394_FEATURE_MIN].value) == DC1394_SUCCESS)
return feature_set.feature[dc1394properties[propId]-DC1394_FEATURE_MIN].value;
}
return CAP_PROP_UNKNOWN;
}
bool CvCaptureCAM_DC1394_v2_CPP::setProperty(int propId, double value)
{
switch (propId)
{
case CAP_PROP_FRAME_WIDTH:
if(started)
return false;
frameWidth = cvRound(value);
frameHeight = 0;
break;
case CAP_PROP_FRAME_HEIGHT:
if(started)
return false;
frameWidth = 0;
frameHeight = cvRound(value);
break;
case CAP_PROP_FPS:
if(started)
return false;
fps = value;
break;
case CAP_PROP_RECTIFICATION:
CV_LOG_WARNING(NULL, "cap_dc1394: rectification support has been removed from videoio module");
return false;
case CAP_PROP_MODE:
if(started)
return false;
userMode = cvRound(value);
break;
case CAP_PROP_ISO_SPEED:
if(started)
return false;
isoSpeed = cvRound(value);
break;
case CAP_PROP_BUFFERSIZE:
if(started)
return false;
nDMABufs = value;
break;
//The code below is based on coriander, callbacks.c:795, refer to case RANGE_MENU_MAN :
default:
if (propId<CAP_PROP_MAX_DC1394 && dc1394properties[propId]!=-1
&& dcCam)
{
//get the corresponding feature from property-id
dc1394feature_info_t *act_feature = &feature_set.feature[dc1394properties[propId]-DC1394_FEATURE_MIN];
if (cvRound(value) == CAP_PROP_DC1394_OFF)
{
if ( (act_feature->on_off_capable)
&& (dc1394_feature_set_power(dcCam, act_feature->id, DC1394_OFF) == DC1394_SUCCESS))
{
act_feature->is_on=DC1394_OFF;
return true;
}
return false;
}
//try to turn the feature ON, feature can be ON and at the same time it can be not capable to change state to OFF
if ( (act_feature->is_on == DC1394_OFF) && (act_feature->on_off_capable == DC1394_TRUE))
{
if (dc1394_feature_set_power(dcCam, act_feature->id, DC1394_ON) == DC1394_SUCCESS)
feature_set.feature[dc1394properties[propId]-DC1394_FEATURE_MIN].is_on=DC1394_ON;
}
//turn off absolute mode - the actual value will be stored in the value field,
//otherwise it would be stored into CSR (control and status register) absolute value
if (act_feature->absolute_capable
&& dc1394_feature_set_absolute_control(dcCam, act_feature->id, DC1394_OFF) !=DC1394_SUCCESS)
return false;
else
act_feature->abs_control=DC1394_OFF;
//set AUTO
if (cvRound(value) == CAP_PROP_DC1394_MODE_AUTO)
{
if (dc1394_feature_set_mode(dcCam, act_feature->id, DC1394_FEATURE_MODE_AUTO)!=DC1394_SUCCESS)
return false;
act_feature->current_mode=DC1394_FEATURE_MODE_AUTO;
return true;
}
//set ONE PUSH
if (cvRound(value) == CAP_PROP_DC1394_MODE_ONE_PUSH_AUTO)
{
//have to set to manual first, otherwise one push will be ignored (AVT manual 4.3.0 p. 115)
if (dc1394_feature_set_mode(dcCam, act_feature->id, DC1394_FEATURE_MODE_ONE_PUSH_AUTO)!=DC1394_SUCCESS)
return false;
//will change to
act_feature->current_mode=DC1394_FEATURE_MODE_ONE_PUSH_AUTO;
return true;
}
//set the feature to MANUAL mode,
if (dc1394_feature_set_mode(dcCam, act_feature->id, DC1394_FEATURE_MODE_MANUAL)!=DC1394_SUCCESS)
return false;
else
act_feature->current_mode=DC1394_FEATURE_MODE_MANUAL;
// if property is one of the white balance features treat it in different way
if (propId == CAP_PROP_WHITE_BALANCE_BLUE_U)
{
if (dc1394_feature_whitebalance_set_value(dcCam,cvRound(value), act_feature->RV_value)!=DC1394_SUCCESS)
return false;
else
{
act_feature->BU_value = cvRound(value);
return true;
}
}
if (propId == CAP_PROP_WHITE_BALANCE_RED_V)
{
if (dc1394_feature_whitebalance_set_value(dcCam, act_feature->BU_value, cvRound(value))!=DC1394_SUCCESS)
return false;
else
{
act_feature->RV_value = cvRound(value);
return true;
}
}
//first: check boundaries
if (value < act_feature->min)
{
value = act_feature->min;
}
else if (value > act_feature->max)
{
value = act_feature->max;
}
if (dc1394_feature_set_value(dcCam, act_feature->id, cvRound(value)) == DC1394_SUCCESS)
{
act_feature->value = value;
return true;
}
}
return false;
}
return true;
}
cv::Ptr<cv::IVideoCapture> cv::create_DC1394_capture(int index)
{
Ptr<CvCaptureCAM_DC1394_v2_CPP> capture = makePtr<CvCaptureCAM_DC1394_v2_CPP>();
if (capture->open(index))
return capture;
return 0;
}
#endif
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// 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.
//
// Copyright (C) 2014, Itseez, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
//M*/
#ifndef _CAP_DSHOW_HPP_
#define _CAP_DSHOW_HPP_
#ifdef HAVE_DSHOW
class videoInput;
namespace cv
{
class VideoCapture_DShow : public IVideoCapture
{
public:
VideoCapture_DShow(int index, const VideoCaptureParameters& params);
virtual ~VideoCapture_DShow();
virtual double getProperty(int propIdx) const CV_OVERRIDE;
virtual bool setProperty(int propIdx, double propVal) CV_OVERRIDE;
virtual bool grabFrame() CV_OVERRIDE;
virtual bool retrieveFrame(int outputType, OutputArray frame) CV_OVERRIDE;
virtual int getCaptureDomain() CV_OVERRIDE;
virtual bool isOpened() const CV_OVERRIDE;
protected:
void open(int index);
void close();
int m_index, m_width, m_height, m_fourcc;
int m_widthSet, m_heightSet;
bool m_convertRGBSet;
static videoInput g_VI;
};
}
#endif //HAVE_DSHOW
#endif //_CAP_DSHOW_HPP_
+768
View File
@@ -0,0 +1,768 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#if !defined(HAVE_FFMPEG)
#error "Build configuration error"
#endif
#include <string>
#include "cap_ffmpeg_impl.hpp"
// 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
#define icvRetrieveFrame2_FFMPEG_p cvRetrieveFrame2_FFMPEG
#define icvSetCaptureProperty_FFMPEG_p cvSetCaptureProperty_FFMPEG
#define icvGetCaptureProperty_FFMPEG_p cvGetCaptureProperty_FFMPEG
#define icvCreateVideoWriter_FFMPEG_p cvCreateVideoWriter_FFMPEG
#define icvReleaseVideoWriter_FFMPEG_p cvReleaseVideoWriter_FFMPEG
#define icvWriteFrame_FFMPEG_p cvWriteFrame_FFMPEG
namespace cv {
namespace {
class CvCapture_FFMPEG_proxy CV_FINAL : public cv::VideoCaptureBase
{
public:
CvCapture_FFMPEG_proxy() { ffmpegCapture = 0; }
CvCapture_FFMPEG_proxy(const cv::String& filename, const cv::VideoCaptureParameters& params)
: ffmpegCapture(NULL)
{
open(filename, params);
}
CvCapture_FFMPEG_proxy(int index, const cv::VideoCaptureParameters& params)
: ffmpegCapture(NULL)
{
open(index, params);
}
CvCapture_FFMPEG_proxy(const Ptr<IStreamReader>& stream, const cv::VideoCaptureParameters& params)
: ffmpegCapture(NULL)
{
open(stream, params);
}
virtual ~CvCapture_FFMPEG_proxy() { close(); }
virtual double getProperty_(int propId) const CV_OVERRIDE
{
return ffmpegCapture ? icvGetCaptureProperty_FFMPEG_p(ffmpegCapture, propId) : static_cast<double>(CAP_PROP_UNKNOWN);
}
virtual bool setProperty_(int propId, double value) CV_OVERRIDE
{
return ffmpegCapture ? icvSetCaptureProperty_FFMPEG_p(ffmpegCapture, propId, value)!=0 : false;
}
virtual bool grabFrame() CV_OVERRIDE
{
return ffmpegCapture ? icvGrabFrame_FFMPEG_p(ffmpegCapture)!=0 : false;
}
virtual bool retrieveFrame_(int flag, cv::OutputArray frame) CV_OVERRIDE
{
unsigned char* data = 0;
int step=0, width=0, height=0, cn=0, depth=0;
if (!ffmpegCapture)
return false;
// if UMat, try GPU to GPU copy using OpenCL extensions
if (frame.isUMat()) {
if (ffmpegCapture->retrieveHWFrame(frame)) {
return true;
}
}
if (flag == 0) {
if (!icvRetrieveFrame2_FFMPEG_p(ffmpegCapture, &data, &step, &width, &height, &cn, &depth))
return false;
}
else {
if (!ffmpegCapture->retrieveFrame(flag, &data, &step, &width, &height, &cn, &depth))
return false;
}
cv::Mat(height, width, CV_MAKETYPE(depth, cn), data, step).copyTo(frame);
return true;
}
bool open(const cv::String& filename, const cv::VideoCaptureParameters& params)
{
close();
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<IStreamReader>& stream, const cv::VideoCaptureParameters& params)
{
close();
readStream = stream; // Increase counter
ffmpegCapture = cvCreateStreamCaptureWithParams_FFMPEG(stream, params);
return ffmpegCapture != 0;
}
void close()
{
if (ffmpegCapture)
icvReleaseCapture_FFMPEG_p( &ffmpegCapture );
CV_Assert(ffmpegCapture == 0);
ffmpegCapture = 0;
}
virtual bool isOpened() const CV_OVERRIDE { return ffmpegCapture != 0; }
virtual int getCaptureDomain() CV_OVERRIDE { return cv::CAP_FFMPEG; }
protected:
CvCapture_FFMPEG* ffmpegCapture;
Ptr<IStreamReader> readStream;
};
} // namespace
cv::Ptr<cv::IVideoCapture> cvCreateFileCapture_FFMPEG_proxy(const std::string &filename, const cv::VideoCaptureParameters& params)
{
cv::Ptr<CvCapture_FFMPEG_proxy> capture = cv::makePtr<CvCapture_FFMPEG_proxy>(filename, params);
if (capture && capture->isOpened())
return capture;
return cv::Ptr<cv::IVideoCapture>();
}
cv::Ptr<cv::IVideoCapture> cvCreateCameraCapture_FFMPEG_proxy(int index, const cv::VideoCaptureParameters& params)
{
cv::Ptr<CvCapture_FFMPEG_proxy> capture = cv::makePtr<CvCapture_FFMPEG_proxy>(index, params);
if (capture && capture->isOpened())
return capture;
return cv::Ptr<cv::IVideoCapture>();
}
cv::Ptr<cv::IVideoCapture> cvCreateStreamCapture_FFMPEG_proxy(const Ptr<IStreamReader>& stream, const cv::VideoCaptureParameters& params)
{
cv::Ptr<CvCapture_FFMPEG_proxy> capture = std::make_shared<CvCapture_FFMPEG_proxy>(stream, params);
if (capture && capture->isOpened())
return capture;
return cv::Ptr<cv::IVideoCapture>();
}
namespace {
class CvVideoWriter_FFMPEG_proxy CV_FINAL :
public cv::IVideoWriter
{
public:
CvVideoWriter_FFMPEG_proxy() { ffmpegWriter = 0; }
CvVideoWriter_FFMPEG_proxy(const cv::String& filename, int fourcc, double fps, cv::Size frameSize, const VideoWriterParameters& params) { ffmpegWriter = 0; open(filename, fourcc, fps, frameSize, params); }
virtual ~CvVideoWriter_FFMPEG_proxy() { close(); }
int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_FFMPEG; }
virtual bool write(cv::InputArray image ) CV_OVERRIDE
{
if(!ffmpegWriter)
return false;
CV_Assert(image.depth() == CV_8U || image.depth() == CV_16U);
// if UMat, try GPU to GPU copy using OpenCL extensions
if (image.isUMat()) {
if (ffmpegWriter->writeHWFrame(image)) {
return true;
}
}
if (!icvWriteFrame_FFMPEG_p(ffmpegWriter, (const uchar*)image.getMat().ptr(), (int)image.step(), image.cols(), image.rows(), image.channels(), 0))
{
CV_LOG_WARNING(NULL, "FFmpeg: Failed to write frame");
return false;
}
return true;
}
virtual bool open( const cv::String& filename, int fourcc, double fps, cv::Size frameSize, const VideoWriterParameters& params )
{
close();
ffmpegWriter = cvCreateVideoWriterWithParams_FFMPEG( filename.c_str(), fourcc, fps, frameSize.width, frameSize.height, params );
return ffmpegWriter != 0;
}
virtual void close()
{
if (ffmpegWriter)
icvReleaseVideoWriter_FFMPEG_p( &ffmpegWriter );
CV_Assert(ffmpegWriter == 0);
ffmpegWriter = 0;
}
virtual double getProperty(int propId) const CV_OVERRIDE {
if(!ffmpegWriter)
return VIDEOWRITER_PROP_UNKNOWN;
return ffmpegWriter->getProperty(propId);
}
virtual bool setProperty(int propId, double value) CV_OVERRIDE {
if (!ffmpegWriter)
return 0;
return ffmpegWriter->setProperty(propId, value);
}
virtual bool isOpened() const CV_OVERRIDE { return ffmpegWriter != 0; }
protected:
CvVideoWriter_FFMPEG* ffmpegWriter;
};
} // namespace
cv::Ptr<cv::IVideoWriter> cvCreateVideoWriter_FFMPEG_proxy(const std::string& filename, int fourcc,
double fps, const cv::Size& frameSize,
const VideoWriterParameters& params)
{
cv::Ptr<CvVideoWriter_FFMPEG_proxy> writer = cv::makePtr<CvVideoWriter_FFMPEG_proxy>(filename, fourcc, fps, frameSize, params);
if (writer && writer->isOpened())
return writer;
return cv::Ptr<cv::IVideoWriter>();
}
} // namespace
//==================================================================================================
#if defined(BUILD_PLUGIN)
#define NEW_PLUGIN
#ifndef NEW_PLUGIN
#define ABI_VERSION 0
#define API_VERSION 0
#include "plugin_api.hpp"
#else
#define CAPTURE_ABI_VERSION 1
#define CAPTURE_API_VERSION 2
#include "plugin_capture_api.hpp"
#define WRITER_ABI_VERSION 1
#define WRITER_API_VERSION 1
#include "plugin_writer_api.hpp"
#endif
namespace cv {
static
CvResult CV_API_CALL cv_capture_open(const char* filename, int camera_index, CV_OUT CvPluginCapture* handle)
{
if (!handle)
return CV_ERROR_FAIL;
*handle = NULL;
if (!filename && camera_index < 0)
return CV_ERROR_FAIL;
CvCapture_FFMPEG_proxy *cap = 0;
try
{
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;
return CV_ERROR_OK;
}
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
}
if (cap)
delete cap;
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 && camera_index < 0)
return CV_ERROR_FAIL;
CvCapture_FFMPEG_proxy *cap = 0;
try
{
cv::VideoCaptureParameters parameters(params, n_params);
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;
return CV_ERROR_OK;
}
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
}
if (cap)
delete cap;
return CV_ERROR_FAIL;
}
static
CvResult CV_API_CALL cv_capture_open_buffer(
void* opaque,
long long(*read)(void* opaque, char* buffer, long long size),
long long(*seek)(void* opaque, long long offset, int way),
int* params, unsigned n_params,
CV_OUT CvPluginCapture* handle
)
{
if (!handle)
return CV_ERROR_FAIL;
*handle = NULL;
CvCapture_FFMPEG_proxy *cap = 0;
try
{
cv::VideoCaptureParameters parameters(params, n_params);
cap = new CvCapture_FFMPEG_proxy(makePtr<PluginStreamReader>(opaque, read, seek), parameters);
if (cap->isOpened())
{
*handle = (CvPluginCapture)cap;
return CV_ERROR_OK;
}
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
}
if (cap)
delete cap;
return CV_ERROR_FAIL;
}
static
CvResult CV_API_CALL cv_capture_release(CvPluginCapture handle)
{
if (!handle)
return CV_ERROR_FAIL;
CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle;
delete instance;
return CV_ERROR_OK;
}
static
CvResult CV_API_CALL cv_capture_get_prop(CvPluginCapture handle, int prop, CV_OUT double* val)
{
if (!handle)
return CV_ERROR_FAIL;
if (!val)
return CV_ERROR_FAIL;
try
{
CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle;
*val = instance->getProperty(prop);
return CV_ERROR_OK;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
static
CvResult CV_API_CALL cv_capture_set_prop(CvPluginCapture handle, int prop, double val)
{
if (!handle)
return CV_ERROR_FAIL;
try
{
CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle;
if (instance->setProperty(prop, val))
{
return CV_ERROR_OK;
}
else
{
CV_LOG_INFO(NULL, "FFmpeg: Unsupported property or value: prop=" << prop << " val=" << val);
return CV_ERROR_FAIL;
}
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
static
CvResult CV_API_CALL cv_capture_grab(CvPluginCapture handle)
{
if (!handle)
return CV_ERROR_FAIL;
try
{
CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle;
return instance->grabFrame() ? CV_ERROR_OK : CV_ERROR_FAIL;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
#ifndef NEW_PLUGIN
static
CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, cv_videoio_retrieve_cb_t callback, void* userdata)
{
if (!handle)
return CV_ERROR_FAIL;
try
{
CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle;
Mat img;
// TODO: avoid unnecessary copying
if (instance->retrieveFrame(stream_idx, img))
return callback(stream_idx, img.data, (int)img.step, img.cols, img.rows, img.channels(), userdata);
return CV_ERROR_FAIL;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
#else
static
CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, cv_videoio_capture_retrieve_cb_t callback, void* userdata)
{
if (!handle)
return CV_ERROR_FAIL;
try
{
CvCapture_FFMPEG_proxy* instance = (CvCapture_FFMPEG_proxy*)handle;
Mat img;
// TODO: avoid unnecessary copying
if (instance->retrieveFrame(stream_idx, img))
return callback(stream_idx, img.data, (int)img.step, img.cols, img.rows, img.type(), userdata);
return CV_ERROR_FAIL;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
#endif
static
CvResult CV_API_CALL cv_writer_open_with_params(
const char* filename, int fourcc, double fps, int width, int height,
int* params, unsigned n_params,
CV_OUT CvPluginWriter* handle)
{
Size sz(width, height);
CvVideoWriter_FFMPEG_proxy* wrt = 0;
try
{
VideoWriterParameters parameters(params, n_params);
wrt = new CvVideoWriter_FFMPEG_proxy(filename, fourcc, fps, sz, parameters);
if(wrt && wrt->isOpened())
{
*handle = (CvPluginWriter)wrt;
return CV_ERROR_OK;
}
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
}
if (wrt)
delete wrt;
return CV_ERROR_FAIL;
}
static
CvResult CV_API_CALL cv_writer_open(const char* filename, int fourcc, double fps, int width, int height, int isColor,
CV_OUT CvPluginWriter* handle)
{
int params[2] = { VIDEOWRITER_PROP_IS_COLOR, isColor };
return cv_writer_open_with_params(filename, fourcc, fps, width, height, params, 1, handle);
}
static
CvResult CV_API_CALL cv_writer_release(CvPluginWriter handle)
{
if (!handle)
return CV_ERROR_FAIL;
CvVideoWriter_FFMPEG_proxy* instance = (CvVideoWriter_FFMPEG_proxy*)handle;
delete instance;
return CV_ERROR_OK;
}
static
CvResult CV_API_CALL cv_writer_get_prop(CvPluginWriter handle, int prop, CV_OUT double* val)
{
if (!handle)
return CV_ERROR_FAIL;
if (!val)
return CV_ERROR_FAIL;
try
{
CvVideoWriter_FFMPEG_proxy* instance = (CvVideoWriter_FFMPEG_proxy*)handle;
*val = instance->getProperty(prop);
return CV_ERROR_OK;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
static
CvResult CV_API_CALL cv_writer_set_prop(CvPluginWriter handle, int prop, double val)
{
if (!handle)
return CV_ERROR_FAIL;
try
{
CvVideoWriter_FFMPEG_proxy* instance = (CvVideoWriter_FFMPEG_proxy*)handle;
if (instance->setProperty(prop, val))
{
return CV_ERROR_OK;
}
else
{
CV_LOG_INFO(NULL, "FFmpeg: Unsupported property or value: prop=" << prop << " val=" << val);
return CV_ERROR_FAIL;
}
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
static
CvResult CV_API_CALL cv_writer_write(CvPluginWriter handle, const unsigned char *data, int step, int width, int height, int cn)
{
if (!handle)
return CV_ERROR_FAIL;
try
{
CvVideoWriter_FFMPEG_proxy* instance = (CvVideoWriter_FFMPEG_proxy*)handle;
Mat img(Size(width, height), CV_MAKETYPE(CV_8U, cn), const_cast<uchar*>(data), step);
instance->write(img);
return CV_ERROR_OK;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "FFmpeg: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "FFmpeg: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
} // namespace
#ifndef NEW_PLUGIN
static const OpenCV_VideoIO_Plugin_API_preview plugin_api =
{
{
sizeof(OpenCV_VideoIO_Plugin_API_preview), ABI_VERSION, API_VERSION,
CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS,
"FFmpeg OpenCV Video I/O plugin"
},
{
/* 1*/CAP_FFMPEG,
/* 2*/cv_capture_open,
/* 3*/cv_capture_release,
/* 4*/cv_capture_get_prop,
/* 5*/cv_capture_set_prop,
/* 6*/cv_capture_grab,
/* 7*/cv_capture_retrieve,
/* 8*/cv_writer_open,
/* 9*/cv_writer_release,
/* 10*/cv_writer_get_prop,
/* 11*/cv_writer_set_prop,
/* 12*/cv_writer_write
}
};
const OpenCV_VideoIO_Plugin_API_preview* opencv_videoio_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT
{
if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION)
return &plugin_api;
return NULL;
}
#else // NEW_PLUGIN
static const OpenCV_VideoIO_Capture_Plugin_API capture_plugin_api =
{
{
sizeof(OpenCV_VideoIO_Capture_Plugin_API), CAPTURE_ABI_VERSION, CAPTURE_API_VERSION,
CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS,
"FFmpeg OpenCV Video I/O Capture plugin"
},
{
/* 1*/CAP_FFMPEG,
/* 2*/cv_capture_open,
/* 3*/cv_capture_release,
/* 4*/cv_capture_get_prop,
/* 5*/cv_capture_set_prop,
/* 6*/cv_capture_grab,
/* 7*/cv_capture_retrieve,
},
{
/* 8*/cv_capture_open_with_params,
},
{
/* 9*/cv_capture_open_buffer,
}
};
const OpenCV_VideoIO_Capture_Plugin_API* opencv_videoio_capture_plugin_init_v1(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT
{
if (requested_abi_version == CAPTURE_ABI_VERSION && requested_api_version <= CAPTURE_API_VERSION)
return &capture_plugin_api;
return NULL;
}
static const OpenCV_VideoIO_Writer_Plugin_API writer_plugin_api =
{
{
sizeof(OpenCV_VideoIO_Writer_Plugin_API), WRITER_ABI_VERSION, WRITER_API_VERSION,
CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS,
"FFmpeg OpenCV Video I/O Writer plugin"
},
{
/* 1*/CAP_FFMPEG,
/* 2*/cv_writer_open,
/* 3*/cv_writer_release,
/* 4*/cv_writer_get_prop,
/* 5*/cv_writer_set_prop,
/* 6*/cv_writer_write
},
{
/* 7*/cv_writer_open_with_params
}
};
const OpenCV_VideoIO_Writer_Plugin_API* opencv_videoio_writer_plugin_init_v1(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT
{
if (requested_abi_version == WRITER_ABI_VERSION && requested_api_version <= WRITER_API_VERSION)
return &writer_plugin_api;
return NULL;
}
#endif // NEW_PLUGIN
#endif // BUILD_PLUGIN
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,48 @@
// 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_FFMPEG_LEGACY_API_H__
#define __OPENCV_FFMPEG_LEGACY_API_H__
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef OPENCV_FFMPEG_API
#if defined(__OPENCV_BUILD)
# define OPENCV_FFMPEG_API
#elif defined _WIN32
# define OPENCV_FFMPEG_API __declspec(dllexport)
#elif defined __GNUC__ && __GNUC__ >= 4
# define OPENCV_FFMPEG_API __attribute__ ((visibility ("default")))
#else
# define OPENCV_FFMPEG_API
#endif
#endif
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 int cvSetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap,
int prop, double value);
OPENCV_FFMPEG_API double cvGetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap, int prop);
OPENCV_FFMPEG_API int cvGrabFrame_FFMPEG(struct CvCapture_FFMPEG* cap);
OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG(struct CvCapture_FFMPEG* capture, unsigned char** data,
int* step, int* width, int* height, int* cn);
OPENCV_FFMPEG_API int cvRetrieveFrame2_FFMPEG(struct CvCapture_FFMPEG* capture, unsigned char** data,
int* step, int* width, int* height, int* cn, int* depth);
OPENCV_FFMPEG_API void cvReleaseCapture_FFMPEG(struct CvCapture_FFMPEG** cap);
OPENCV_FFMPEG_API struct CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG(const char* filename,
int fourcc, double fps, int width, int height, int isColor );
OPENCV_FFMPEG_API int cvWriteFrame_FFMPEG(struct CvVideoWriter_FFMPEG* writer, const unsigned char* data,
int step, int width, int height, int cn, int origin);
OPENCV_FFMPEG_API void cvReleaseVideoWriter_FFMPEG(struct CvVideoWriter_FFMPEG** writer);
#ifdef __cplusplus
}
#endif
#endif // __OPENCV_FFMPEG_LEGACY_API_H__
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+453
View File
@@ -0,0 +1,453 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2008, Nils Hasler, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
// Author: Nils Hasler <hasler@mpi-inf.mpg.de>
//
// Max-Planck-Institut Informatik
//
// capture video from a sequence of images
// the filename when opening can either be a printf pattern such as
// video%04d.png or the first frame of the sequence i.e. video0001.png
//
#include "precomp.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/core/utils/filesystem.hpp"
#include "opencv2/videoio/utils.private.hpp"
#if 0
#define CV_WARN(message)
#else
#define CV_WARN(message) CV_LOG_INFO(NULL, "CAP_IMAGES warning: %s (%s:%d)" << message)
#endif
namespace cv {
class CvCapture_Images: public IVideoCapture
{
public:
void init()
{
filename_pattern.clear();
frame.release();
currentframe = firstframe = 0;
length = 0;
grabbedInOpen = false;
}
CvCapture_Images()
{
init();
}
virtual ~CvCapture_Images() CV_OVERRIDE
{
close();
}
virtual double getProperty(int) const CV_OVERRIDE;
virtual bool setProperty(int, double) CV_OVERRIDE;
virtual bool grabFrame() CV_OVERRIDE;
virtual bool retrieveFrame(int, OutputArray) CV_OVERRIDE;
virtual bool isOpened() const CV_OVERRIDE;
virtual int getCaptureDomain() /*const*/ CV_OVERRIDE { return cv::CAP_IMAGES; }
bool open(const String&, int image_seq_start = -1);
void close();
protected:
std::string filename_pattern; // actually a printf-pattern
unsigned currentframe;
unsigned firstframe; // number of first frame
unsigned length; // length of sequence
Mat frame;
bool grabbedInOpen;
};
void CvCapture_Images::close()
{
init();
}
bool CvCapture_Images::grabFrame()
{
if (length == 1 && currentframe >= length)
return false;
const cv::String filename = cv::format(filename_pattern.c_str(), (int)(firstframe + currentframe));
CV_Assert(!filename.empty());
if (grabbedInOpen)
{
grabbedInOpen = false;
++currentframe;
return !frame.empty();
}
frame = imread(filename, IMREAD_UNCHANGED);
if( !frame.empty() )
currentframe++;
return !frame.empty();
}
bool CvCapture_Images::retrieveFrame(int, OutputArray out)
{
frame.copyTo(out);
return grabbedInOpen ? false : !frame.empty();
}
double CvCapture_Images::getProperty(int id) const
{
switch(id)
{
case cv::CAP_PROP_POS_MSEC:
CV_WARN("collections of images don't have framerates");
return CAP_PROP_UNKNOWN;
case cv::CAP_PROP_POS_FRAMES:
return currentframe;
case cv::CAP_PROP_FRAME_COUNT:
return length;
case cv::CAP_PROP_POS_AVI_RATIO:
return (double)currentframe / (double)(length - 1);
case cv::CAP_PROP_FRAME_WIDTH:
return frame.cols;
case cv::CAP_PROP_FRAME_HEIGHT:
return frame.rows;
case cv::CAP_PROP_FPS:
CV_WARN("collections of images don't have framerates");
return CAP_PROP_UNKNOWN;
case cv::CAP_PROP_FOURCC:
CV_WARN("collections of images don't have 4-character codes");
return CAP_PROP_UNKNOWN;
}
return CAP_PROP_UNKNOWN;
}
bool CvCapture_Images::setProperty(int id, double value)
{
switch(id)
{
case cv::CAP_PROP_POS_MSEC:
case cv::CAP_PROP_POS_FRAMES:
if(value < 0) {
CV_WARN("seeking to negative positions does not work - clamping");
value = 0;
}
if(value >= length) {
CV_WARN("seeking beyond end of sequence - clamping");
value = length - 1;
}
currentframe = cvRound(value);
if (currentframe != 0)
grabbedInOpen = false; // grabbed frame is not valid anymore
return true;
case cv::CAP_PROP_POS_AVI_RATIO:
if(value > 1) {
CV_WARN("seeking beyond end of sequence - clamping");
value = 1;
} else if(value < 0) {
CV_WARN("seeking to negative positions does not work - clamping");
value = 0;
}
currentframe = cvRound((length - 1) * value);
if (currentframe != 0)
grabbedInOpen = false; // grabbed frame is not valid anymore
return true;
}
CV_WARN("unknown/unhandled property");
return false;
}
// static
std::string icvExtractPattern(const std::string& filename, unsigned *offset)
{
size_t len = filename.size();
CV_Assert(!filename.empty());
CV_Assert(offset);
*offset = 0;
// check whether this is a valid image sequence filename
std::string::size_type pos = filename.find('%');
if (pos != std::string::npos)
{
pos++; CV_Assert(pos < len);
if (filename[pos] == '0') // optional zero prefix
{
pos++; CV_Assert(pos < len);
}
if (filename[pos] >= '1' && filename[pos] <= '9') // optional numeric size (1..9) (one symbol only)
{
pos++; CV_Assert(pos < len);
}
if (filename[pos] == 'd' || filename[pos] == 'u')
{
pos++;
if (pos == len)
return filename; // end of string '...%5d'
CV_Assert(pos < len);
if (filename.find('%', pos) == std::string::npos)
return filename; // no more patterns
CV_Error_(Error::StsBadArg, ("CAP_IMAGES: invalid multiple patterns: %s", filename.c_str()));
}
CV_Error_(Error::StsBadArg, ("CAP_IMAGES: error, expected '0?[1-9][du]' pattern, got: %s", filename.c_str()));
}
else // no pattern filename was given - extract the pattern
{
pos = filename.rfind('/');
#ifdef _WIN32
if (pos == std::string::npos)
pos = filename.rfind('\\');
#endif
if (pos != std::string::npos)
pos++;
else
pos = 0;
while (pos < len && !isdigit(filename[pos])) pos++;
if (pos == len)
return "";
std::string::size_type pos0 = pos;
const int64_t max_number = 1000000000;
CV_Assert(max_number < INT_MAX); // offset is 'int'
int number_str_size = 0;
uint64_t number = 0;
while (pos < len && isdigit(filename[pos]))
{
char ch = filename[pos];
number = (number * 10) + (uint64_t)((int)ch - (int)'0');
CV_Assert(number < max_number);
number_str_size++;
CV_Assert(number_str_size <= 64); // don't allow huge zero prefixes
pos++;
}
CV_Assert(number_str_size > 0);
*offset = (int)number;
std::string result;
if (pos0 > 0)
result += filename.substr(0, pos0);
result += cv::format("%%0%dd", number_str_size);
if (pos < len)
result += filename.substr(pos);
CV_LOG_INFO(NULL, "Pattern: " << result << " @ " << number);
return result;
}
}
bool CvCapture_Images::open(const std::string& _filename, int image_seq_start)
{
unsigned offset = 0;
close();
CV_Assert(!_filename.empty());
filename_pattern = icvExtractPattern(_filename, &offset);
const bool explicit_start = (image_seq_start >= 0);
if (explicit_start)
offset = static_cast<unsigned>(image_seq_start);
if (filename_pattern.empty())
{
filename_pattern = _filename;
if (!utils::fs::exists(filename_pattern))
{
CV_LOG_INFO(NULL, "CAP_IMAGES: File does not exist: " << filename_pattern);
close();
return false;
}
if (!haveImageReader(filename_pattern))
{
CV_LOG_INFO(NULL, "CAP_IMAGES: File is not an image: " << filename_pattern);
close();
return false;
}
length = 1;
}
else
{
// determine the length of the sequence
for (length = 0; ;)
{
cv::String filename = cv::format(filename_pattern.c_str(), (int)(offset + length));
if (!utils::fs::exists(filename))
{
if (length == 0 && offset == 0 && !explicit_start) // allow starting with 0 or 1
{
offset++;
continue;
}
CV_LOG_INFO(NULL, "CAP_IMAGES: File does not exist: " << filename);
break;
}
if(!haveImageReader(filename))
{
CV_LOG_INFO(NULL, "CAP_IMAGES: File is not an image: " << filename);
break;
}
length++;
}
if (length == 0)
{
close();
return false;
}
firstframe = offset;
}
// grab frame to enable properties retrieval
bool grabRes = CvCapture_Images::grabFrame();
grabbedInOpen = true;
currentframe = 0;
return grabRes;
}
bool CvCapture_Images::isOpened() const
{
return !filename_pattern.empty();
}
Ptr<IVideoCapture> create_Images_capture(const std::string& filename, const VideoCaptureParameters& params)
{
int image_seq_start = -1;
if (params.has(CAP_PROP_IMAGE_SEQ_START))
{
image_seq_start = params.get<int>(CAP_PROP_IMAGE_SEQ_START);
}
if (params.warnUnusedParameters())
{
CV_LOG_ERROR(NULL, "VIDEOIO/IMAGES: unsupported parameters in .open(), see logger INFO channel for details. Bailout");
return Ptr<IVideoCapture>();
}
Ptr<CvCapture_Images> cap = makePtr<CvCapture_Images>();
if (!cap->open(filename, image_seq_start))
return Ptr<IVideoCapture>();
return cap;
}
//
//
// image sequence writer
//
//
class CvVideoWriter_Images CV_FINAL : public IVideoWriter
{
public:
CvVideoWriter_Images(const std::string & _filename);
void close();
~CvVideoWriter_Images() CV_OVERRIDE { close(); }
double getProperty(int) const CV_OVERRIDE { return VIDEOWRITER_PROP_UNKNOWN; }
bool setProperty( int, double ) CV_OVERRIDE; // FIXIT doesn't work: IVideoWriter interface only!
bool isOpened() const CV_OVERRIDE { return !filename_pattern.empty(); }
bool write( InputArray ) CV_OVERRIDE;
int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_IMAGES; }
protected:
std::string filename_pattern;
unsigned currentframe;
std::vector<int> params;
};
bool CvVideoWriter_Images::write(InputArray image)
{
CV_Assert(!filename_pattern.empty());
cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe);
CV_Assert(!filename.empty());
cv::Mat img = image.getMat();
const bool ok = cv::imwrite(filename, img);
currentframe++;
return ok;
}
void CvVideoWriter_Images::close()
{
filename_pattern.clear();
currentframe = 0;
params.clear();
}
CvVideoWriter_Images::CvVideoWriter_Images(const std::string & _filename)
{
unsigned offset = 0;
close();
filename_pattern = icvExtractPattern(_filename, &offset);
CV_Assert(!filename_pattern.empty());
cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe);
if (!cv::haveImageWriter(filename))
{
close();
}
currentframe = offset;
params.clear();
}
bool CvVideoWriter_Images::setProperty( int id, double value )
{
if (id >= cv::CAP_PROP_IMAGES_BASE && id < cv::CAP_PROP_IMAGES_LAST)
{
params.push_back( id - cv::CAP_PROP_IMAGES_BASE );
params.push_back( static_cast<int>( value ) );
return true;
}
return false; // not supported
}
Ptr<IVideoWriter> create_Images_writer(const std::string &filename, int, double, const Size &,
const cv::VideoWriterParameters&)
{
return makePtr<CvVideoWriter_Images>(filename);
}
} // cv::
+405
View File
@@ -0,0 +1,405 @@
// 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 CAP_INTERFACE_HPP
#define CAP_INTERFACE_HPP
#include "opencv2/core.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/videoio/utils.private.hpp"
//===================================================
// Modern classes
namespace cv
{
namespace
{
template <class T>
inline T castParameterTo(int paramValue)
{
return static_cast<T>(paramValue);
}
template <>
inline bool castParameterTo(int paramValue)
{
return paramValue != 0;
}
}
class VideoParameters
{
public:
struct VideoParameter {
VideoParameter() = default;
VideoParameter(int key_, int value_) : key(key_), value(value_) {}
int key{-1};
int value{-1};
mutable bool isConsumed{false};
};
VideoParameters() = default;
explicit VideoParameters(const std::vector<int>& params)
{
const auto count = params.size();
if (count % 2 != 0)
{
CV_Error_(Error::StsVecLengthErr,
("Vector of VideoWriter parameters should have even length"));
}
params_.reserve(count / 2);
for (std::size_t i = 0; i < count; i += 2)
{
add(params[i], params[i + 1]);
}
}
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);
}
bool has(int key) const
{
auto it = std::find_if(params_.begin(), params_.end(),
[key](const VideoParameter &param)
{
return param.key == key;
}
);
return it != params_.end();
}
template <class ValueType>
ValueType get(int key) const
{
auto it = std::find_if(params_.begin(), params_.end(),
[key](const VideoParameter &param)
{
return param.key == key;
}
);
if (it != params_.end())
{
it->isConsumed = true;
return castParameterTo<ValueType>(it->value);
}
else
{
CV_Error_(Error::StsBadArg, ("Missing value for parameter: [%d]", key));
}
}
template <class ValueType>
ValueType get(int key, ValueType defaultValue) const
{
auto it = std::find_if(params_.begin(), params_.end(),
[key](const VideoParameter &param)
{
return param.key == key;
}
);
if (it != params_.end())
{
it->isConsumed = true;
return castParameterTo<ValueType>(it->value);
}
else
{
return defaultValue;
}
}
std::vector<int> getUnused() const
{
std::vector<int> unusedParams;
for (const auto &param : params_)
{
if (!param.isConsumed)
{
unusedParams.push_back(param.key);
}
}
return unusedParams;
}
std::vector<int> getIntVector() const
{
std::vector<int> vint_params;
for (const auto& param : params_)
{
vint_params.push_back(param.key);
vint_params.push_back(param.value);
}
return vint_params;
}
bool empty() const
{
return params_.empty();
}
bool warnUnusedParameters() const
{
bool found = false;
for (const auto &param : params_)
{
if (!param.isConsumed)
{
found = true;
CV_LOG_INFO(NULL, "VIDEOIO: unused parameter: [" << param.key << "]=" <<
cv::format("%lld / 0x%016llx", (long long)param.value, (long long)param.value));
}
}
return found;
}
private:
std::vector<VideoParameter> params_;
};
class VideoWriterParameters : public VideoParameters
{
public:
using VideoParameters::VideoParameters; // reuse constructors
};
class VideoCaptureParameters : public VideoParameters
{
public:
using VideoParameters::VideoParameters; // reuse constructors
};
class IVideoCapture
{
public:
virtual ~IVideoCapture() {}
virtual double getProperty(int) const { return CAP_PROP_UNKNOWN; }
virtual bool setProperty(int, double) { return false; }
virtual bool grabFrame() = 0;
virtual bool retrieveFrame(int, OutputArray) = 0;
virtual bool isOpened() const = 0;
virtual int getCaptureDomain() { return CAP_ANY; } // Return the type of the capture object: CAP_DSHOW, etc...
};
class IVideoWriter
{
public:
virtual ~IVideoWriter() {}
virtual double getProperty(int) const { return CAP_PROP_UNKNOWN; }
virtual bool setProperty(int, double) { return false; }
virtual bool isOpened() const = 0;
virtual bool write(InputArray) = 0;
virtual int getCaptureDomain() const { return cv::CAP_ANY; } // Return the type of the capture object: CAP_FFMPEG, etc...
};
namespace internal {
class VideoCapturePrivateAccessor
{
public:
static
IVideoCapture* getIVideoCapture(const VideoCapture& cap) { return cap.icap.get(); }
};
} // namespace
// Advanced base class for VideoCapture backends providing some extra functionality
class VideoCaptureBase : public IVideoCapture
{
public:
VideoCaptureBase() : autorotate(true) {}
double getProperty(int propId) const CV_OVERRIDE
{
switch(propId)
{
case cv::CAP_PROP_ORIENTATION_AUTO:
return static_cast<double>(autorotate);
case cv::CAP_PROP_FRAME_WIDTH:
return shouldSwapWidthHeight() ? getProperty_(cv::CAP_PROP_FRAME_HEIGHT) : getProperty_(cv::CAP_PROP_FRAME_WIDTH);
case cv::CAP_PROP_FRAME_HEIGHT:
return shouldSwapWidthHeight() ? getProperty_(cv::CAP_PROP_FRAME_WIDTH) : getProperty_(cv::CAP_PROP_FRAME_HEIGHT);
default:
return getProperty_(propId);
}
}
bool setProperty(int propId, double value) CV_OVERRIDE
{
switch(propId)
{
case cv::CAP_PROP_ORIENTATION_AUTO:
autorotate = (value != 0);
return true;
default:
return setProperty_(propId, value);
}
}
bool retrieveFrame(int channel, OutputArray image) CV_OVERRIDE
{
const bool res = retrieveFrame_(channel, image);
if (res)
applyMetadataRotation(image);
return res;
}
protected:
virtual double getProperty_(int) const = 0;
virtual bool setProperty_(int, double) = 0;
virtual bool retrieveFrame_(int, OutputArray) = 0;
protected:
bool shouldSwapWidthHeight() const
{
if (!autorotate)
return false;
int rotation = static_cast<int>(getProperty(cv::CAP_PROP_ORIENTATION_META));
return std::abs(rotation % 180) == 90;
}
void applyMetadataRotation(OutputArray mat) const
{
bool rotation_auto = 0 != getProperty(CAP_PROP_ORIENTATION_AUTO);
int rotation_angle = static_cast<int>(getProperty(CAP_PROP_ORIENTATION_META));
if(!rotation_auto || rotation_angle%360 == 0)
{
return;
}
cv::RotateFlags flag;
if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees
flag = cv::ROTATE_90_CLOCKWISE;
} else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees
flag = cv::ROTATE_90_COUNTERCLOCKWISE;
} else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees
flag = cv::ROTATE_180;
} else { // Unsupported rotation
return;
}
cv::rotate(mat, mat, flag);
}
protected:
bool autorotate;
};
//==================================================================================================
Ptr<IVideoCapture> cvCreateFileCapture_FFMPEG_proxy(const std::string &filename, const VideoCaptureParameters& params);
Ptr<IVideoCapture> cvCreateCameraCapture_FFMPEG_proxy(int index, const VideoCaptureParameters& params);
Ptr<IVideoCapture> cvCreateStreamCapture_FFMPEG_proxy(const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params);
Ptr<IVideoWriter> cvCreateVideoWriter_FFMPEG_proxy(const std::string& filename, int fourcc,
double fps, const Size& frameSize,
const VideoWriterParameters& params);
Ptr<IVideoCapture> createGStreamerCapture_file(const std::string& filename, const cv::VideoCaptureParameters& params);
Ptr<IVideoCapture> createGStreamerCapture_cam(int index, const cv::VideoCaptureParameters& params);
Ptr<IVideoWriter> create_GStreamer_writer(const std::string& filename, int fourcc,
double fps, const Size& frameSize,
const VideoWriterParameters& params);
Ptr<IVideoCapture> create_MFX_capture(const std::string &filename);
Ptr<IVideoWriter> create_MFX_writer(const std::string& filename, int _fourcc,
double fps, const Size& frameSize,
const VideoWriterParameters& params);
Ptr<IVideoCapture> create_AVFoundation_capture_file(const std::string &filename);
Ptr<IVideoCapture> create_AVFoundation_capture_cam(int index);
Ptr<IVideoWriter> create_AVFoundation_writer(const std::string& filename, int fourcc,
double fps, const Size& frameSize,
const VideoWriterParameters& params);
Ptr<IVideoCapture> create_WRT_capture(int device);
Ptr<IVideoCapture> cvCreateCapture_MSMF(int index, const VideoCaptureParameters& params);
Ptr<IVideoCapture> cvCreateCapture_MSMF(const std::string& filename, const VideoCaptureParameters& params);
Ptr<IVideoCapture> cvCreateCapture_MSMF(const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params);
Ptr<IVideoWriter> cvCreateVideoWriter_MSMF(const std::string& filename, int fourcc,
double fps, const Size& frameSize,
const VideoWriterParameters& params);
Ptr<IVideoCapture> create_DShow_capture(int index, const VideoCaptureParameters& params);
Ptr<IVideoCapture> create_V4L_capture_cam(int index);
Ptr<IVideoCapture> create_V4L_capture_file(const std::string &filename);
Ptr<IVideoCapture> create_OpenNI2_capture_cam( int index );
Ptr<IVideoCapture> create_OpenNI2_capture_file( const std::string &filename );
Ptr<IVideoCapture> create_Images_capture(const std::string& filename, const VideoCaptureParameters& params);
Ptr<IVideoWriter> create_Images_writer(const std::string& filename, int fourcc,
double fps, const Size& frameSize,
const VideoWriterParameters& params);
Ptr<IVideoCapture> create_DC1394_capture(int index);
Ptr<IVideoCapture> create_RealSense_capture(int index);
Ptr<IVideoCapture> create_PvAPI_capture( int index );
Ptr<IVideoCapture> create_XIMEA_capture_cam( int index );
Ptr<IVideoCapture> create_XIMEA_capture_file( const std::string &serialNumber );
Ptr<IVideoCapture> create_ueye_camera(int camera);
Ptr<IVideoCapture> create_Aravis_capture( int index );
Ptr<IVideoCapture> createMotionJpegCapture(const std::string& filename);
Ptr<IVideoWriter> createMotionJpegWriter(const std::string& filename, int fourcc,
double fps, const Size& frameSize,
const VideoWriterParameters& params);
Ptr<IVideoCapture> createGPhoto2Capture(int index);
Ptr<IVideoCapture> createGPhoto2Capture(const std::string& deviceName);
Ptr<IVideoCapture> createXINECapture(const std::string &filename);
Ptr<IVideoCapture> createAndroidCapture_cam(int index, const VideoCaptureParameters& params);
Ptr<IVideoCapture> createAndroidCapture_file(const std::string &filename, const VideoCaptureParameters& params);
Ptr<IVideoWriter> createAndroidVideoWriter(const std::string& filename, int fourcc,
double fps, const Size& frameSize,
const VideoWriterParameters& params);
Ptr<IVideoCapture> create_obsensor_capture(int index, const cv::VideoCaptureParameters& params);
bool VideoCapture_V4L_waitAny(
const std::vector<VideoCapture>& streams,
CV_OUT std::vector<int>& ready,
int64 timeoutNs);
static inline
std::ostream& operator<<(std::ostream& out, const VideoAccelerationType& va_type)
{
switch (va_type)
{
case VIDEO_ACCELERATION_NONE: out << "NONE"; return out;
case VIDEO_ACCELERATION_ANY: out << "ANY"; return out;
case VIDEO_ACCELERATION_D3D11: out << "D3D11"; return out;
case VIDEO_ACCELERATION_VAAPI: out << "VAAPI"; return out;
case VIDEO_ACCELERATION_MFX: out << "MFX"; return out;
case VIDEO_ACCELERATION_DRM: out << "DRM"; return out;
}
out << cv::format("UNKNOWN(0x%ux)", static_cast<unsigned int>(va_type));
return out;
}
} // cv::
#endif // CAP_INTERFACE_HPP
@@ -0,0 +1,516 @@
/*
* cap_ios_abstract_camera.mm
* For iOS video I/O
* by Eduard Feicho on 29/07/12
* by Alexander Shishkov on 17/07/13
* Copyright 2012. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import "opencv2/videoio/cap_ios.h"
#include "precomp.hpp"
#pragma mark - Private Interface
@interface CvAbstractCamera ()
@property (nonatomic, strong) AVCaptureVideoPreviewLayer* captureVideoPreviewLayer;
- (void)deviceOrientationDidChange:(NSNotification*)notification;
- (void)startCaptureSession;
- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
- (void)updateSize;
@end
#pragma mark - Implementation
@implementation CvAbstractCamera
#pragma mark Public
@synthesize imageWidth;
@synthesize imageHeight;
@synthesize defaultFPS;
@synthesize defaultAVCaptureDevicePosition;
@synthesize defaultAVCaptureVideoOrientation;
@synthesize defaultAVCaptureSessionPreset;
@synthesize captureSession;
@synthesize captureVideoPreviewLayer;
@synthesize videoCaptureConnection;
@synthesize running;
@synthesize captureSessionLoaded;
@synthesize useAVCaptureVideoPreviewLayer;
@synthesize parentView;
#pragma mark - Constructors
- (id)init;
{
self = [super init];
if (self) {
// react to device orientation notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceOrientationDidChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
currentDeviceOrientation = [[UIDevice currentDevice] orientation];
// check if camera available
cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") );
running = NO;
// set camera default configuration
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
self.defaultFPS = 15;
self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset352x288;
self.parentView = nil;
self.useAVCaptureVideoPreviewLayer = NO;
}
return self;
}
- (id)initWithParentView:(UIView*)parent;
{
self = [super init];
if (self) {
// react to device orientation notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceOrientationDidChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
currentDeviceOrientation = [[UIDevice currentDevice] orientation];
// check if camera available
cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") );
running = NO;
// set camera default configuration
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
self.defaultFPS = 15;
self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480;
self.parentView = parent;
self.useAVCaptureVideoPreviewLayer = YES;
}
return self;
}
- (void)dealloc;
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
[super dealloc];
}
#pragma mark - Public interface
- (void)start;
{
if (![NSThread isMainThread]) {
NSLog(@"[Camera] Warning: Call start only from main thread");
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
return;
}
if (running == YES) {
return;
}
running = YES;
// TODO: update image size data before actually starting (needed for recording)
[self updateSize];
if (cameraAvailable) {
[self startCaptureSession];
}
}
- (void)pause;
{
running = NO;
[self.captureSession stopRunning];
}
- (void)stop;
{
running = NO;
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
if (self.captureSession) {
for (AVCaptureInput *input in self.captureSession.inputs) {
[self.captureSession removeInput:input];
}
for (AVCaptureOutput *output in self.captureSession.outputs) {
[self.captureSession removeOutput:output];
}
[self.captureSession stopRunning];
[captureSession release];
}
[captureVideoPreviewLayer release];
[videoCaptureConnection release];
captureSessionLoaded = NO;
}
// use front/back camera
- (void)switchCameras;
{
BOOL was_running = self.running;
if (was_running) {
[self stop];
}
if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;
} else {
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
}
if (was_running) {
[self start];
}
}
#pragma mark - Device Orientation Changes
- (void)deviceOrientationDidChange:(NSNotification*)notification
{
(void)notification;
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
switch (orientation)
{
case UIDeviceOrientationPortrait:
case UIDeviceOrientationPortraitUpsideDown:
case UIDeviceOrientationLandscapeLeft:
case UIDeviceOrientationLandscapeRight:
currentDeviceOrientation = orientation;
break;
case UIDeviceOrientationFaceUp:
case UIDeviceOrientationFaceDown:
default:
break;
}
NSLog(@"deviceOrientationDidChange: %d", (int)orientation);
[self updateOrientation];
}
#pragma mark - Private Interface
- (void)createCaptureSession;
{
// set a av capture session preset
self.captureSession = [[AVCaptureSession alloc] init];
if ([self.captureSession canSetSessionPreset:self.defaultAVCaptureSessionPreset]) {
[self.captureSession setSessionPreset:self.defaultAVCaptureSessionPreset];
} else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetLow]) {
[self.captureSession setSessionPreset:AVCaptureSessionPresetLow];
} else {
NSLog(@"[Camera] Error: could not set session preset");
}
}
- (void)createCaptureDevice;
{
// setup the device
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[self setDesiredCameraPosition:self.defaultAVCaptureDevicePosition];
NSLog(@"[Camera] device connected? %@", device.connected ? @"YES" : @"NO");
NSLog(@"[Camera] device position %@", (device.position == AVCaptureDevicePositionBack) ? @"back" : @"front");
}
- (void)createVideoPreviewLayer;
{
self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
if ([self.captureVideoPreviewLayer respondsToSelector:@selector(connection)])
{
if ([self.captureVideoPreviewLayer.connection isVideoOrientationSupported])
{
[self.captureVideoPreviewLayer.connection setVideoOrientation:self.defaultAVCaptureVideoOrientation];
}
}
else
{
#if (!defined(TARGET_OS_MACCATALYST) || !TARGET_OS_MACCATALYST)
// Deprecated in 6.0; here for backward compatibility
if ([self.captureVideoPreviewLayer isOrientationSupported])
{
[self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation];
}
#endif
}
if (parentView != nil) {
self.captureVideoPreviewLayer.frame = self.parentView.bounds;
self.captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.parentView.layer addSublayer:self.captureVideoPreviewLayer];
}
NSLog(@"[Camera] created AVCaptureVideoPreviewLayer");
}
- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
{
for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
if ([device position] == desiredPosition) {
[self.captureSession beginConfiguration];
NSError* error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
NSLog(@"error creating input %@", [error description]);
}
// support for autofocus
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
error = nil;
if ([device lockForConfiguration:&error]) {
device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for autofocus configuration %@", [error description]);
}
}
[self.captureSession addInput:input];
for (AVCaptureInput *oldInput in self.captureSession.inputs) {
[self.captureSession removeInput:oldInput];
}
[self.captureSession addInput:input];
[self.captureSession commitConfiguration];
break;
}
}
}
- (void)startCaptureSession
{
if (!cameraAvailable) {
return;
}
if (self.captureSessionLoaded == NO) {
[self createCaptureSession];
[self createCaptureDevice];
[self createCaptureOutput];
// setup preview layer
if (self.useAVCaptureVideoPreviewLayer) {
[self createVideoPreviewLayer];
} else {
[self createCustomVideoPreview];
}
captureSessionLoaded = YES;
}
[self.captureSession startRunning];
}
- (void)createCaptureOutput;
{
[NSException raise:NSInternalInconsistencyException
format:@"You must override %s in a subclass", __FUNCTION__];
}
- (void)createCustomVideoPreview;
{
[NSException raise:NSInternalInconsistencyException
format:@"You must override %s in a subclass", __FUNCTION__];
}
- (void)updateOrientation;
{
// nothing to do here
}
- (void)updateSize;
{
if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetPhoto]) {
//TODO: find the correct resolution
self.imageWidth = 640;
self.imageHeight = 480;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetHigh]) {
//TODO: find the correct resolution
self.imageWidth = 640;
self.imageHeight = 480;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetMedium]) {
//TODO: find the correct resolution
self.imageWidth = 640;
self.imageHeight = 480;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetLow]) {
//TODO: find the correct resolution
self.imageWidth = 640;
self.imageHeight = 480;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset352x288]) {
self.imageWidth = 352;
self.imageHeight = 288;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset640x480]) {
self.imageWidth = 640;
self.imageHeight = 480;
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset1280x720]) {
self.imageWidth = 1280;
self.imageHeight = 720;
} else {
self.imageWidth = 640;
self.imageHeight = 480;
}
}
- (void)lockFocus;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.focusMode = AVCaptureFocusModeLocked;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for locked focus configuration %@", [error description]);
}
}
}
- (void) unlockFocus;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for autofocus configuration %@", [error description]);
}
}
}
- (void)lockExposure;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isExposureModeSupported:AVCaptureExposureModeLocked]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.exposureMode = AVCaptureExposureModeLocked;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for locked exposure configuration %@", [error description]);
}
}
}
- (void) unlockExposure;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.exposureMode = AVCaptureExposureModeContinuousAutoExposure;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for autoexposure configuration %@", [error description]);
}
}
}
- (void)lockBalance;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.whiteBalanceMode = AVCaptureWhiteBalanceModeLocked;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for locked white balance configuration %@", [error description]);
}
}
}
- (void) unlockBalance;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.whiteBalanceMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance;
[device unlockForConfiguration];
} else {
NSLog(@"unable to lock device for auto white balance configuration %@", [error description]);
}
}
}
@end
+172
View File
@@ -0,0 +1,172 @@
/*
* cap_ios_photo_camera.mm
* For iOS video I/O
* by Eduard Feicho on 29/07/12
* Copyright 2012. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import "opencv2/videoio/cap_ios.h"
#include "precomp.hpp"
#pragma mark - Private Interface
@interface CvPhotoCamera ()
{
id<CvPhotoCameraDelegate> _delegate;
}
@property (nonatomic, strong) AVCaptureStillImageOutput* stillImageOutput;
@end
#pragma mark - Implementation
@implementation CvPhotoCamera
#pragma mark Public
@synthesize stillImageOutput;
- (void)setDelegate:(id<CvPhotoCameraDelegate>)newDelegate {
_delegate = newDelegate;
}
- (id<CvPhotoCameraDelegate>)delegate {
return _delegate;
}
#pragma mark - Public interface
- (void)takePicture
{
if (cameraAvailable == NO) {
return;
}
cameraAvailable = NO;
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:self.videoCaptureConnection
completionHandler:
^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
if (error == nil && imageSampleBuffer != NULL)
{
// TODO check
// NSNumber* imageOrientation = [UIImage cgImageOrientationForUIDeviceOrientation:currentDeviceOrientation];
// CMSetAttachment(imageSampleBuffer, kCGImagePropertyOrientation, imageOrientation, 1);
NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
dispatch_async(dispatch_get_main_queue(), ^{
[self.captureSession stopRunning];
// Make sure we create objects on the main thread in the main context
UIImage* newImage = [UIImage imageWithData:jpegData];
//UIImageOrientation orientation = [newImage imageOrientation];
// TODO: only apply rotation, don't scale, since we can set this directly in the camera
/*
switch (orientation) {
case UIImageOrientationUp:
case UIImageOrientationDown:
newImage = [newImage imageWithAppliedRotationAndMaxSize:CGSizeMake(640.0, 480.0)];
break;
case UIImageOrientationLeft:
case UIImageOrientationRight:
newImage = [newImage imageWithMaxSize:CGSizeMake(640.0, 480.0)];
default:
break;
}
*/
// We have captured the image, we can allow the user to take another picture
cameraAvailable = YES;
NSLog(@"CvPhotoCamera captured image");
[self.delegate photoCamera:self capturedImage:newImage];
[self.captureSession startRunning];
});
}
}];
}
- (void)stop;
{
[super stop];
self.stillImageOutput = nil;
}
#pragma mark - Private Interface
- (void)createStillImageOutput;
{
// setup still image output with jpeg codec
self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecTypeJPEG, AVVideoCodecKey, nil];
[self.stillImageOutput setOutputSettings:outputSettings];
[self.captureSession addOutput:self.stillImageOutput];
for (AVCaptureConnection *connection in self.stillImageOutput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([port.mediaType isEqual:AVMediaTypeVideo]) {
self.videoCaptureConnection = connection;
break;
}
}
if (self.videoCaptureConnection) {
break;
}
}
NSLog(@"[Camera] still image output created");
}
- (void)createCaptureOutput;
{
[self createStillImageOutput];
}
- (void)createCustomVideoPreview;
{
//do nothing, always use AVCaptureVideoPreviewLayer
}
@end
+653
View File
@@ -0,0 +1,653 @@
/*
* cap_ios_video_camera.mm
* For iOS video I/O
* by Eduard Feicho on 29/07/12
* by Alexander Shishkov on 17/07/13
* Copyright 2012. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import "opencv2/videoio/cap_ios.h"
#include "precomp.hpp"
#import <UIKit/UIKit.h>
static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}
#pragma mark - Private Interface
@interface CvVideoCamera () {
int recordingCountDown;
}
- (void)createVideoDataOutput;
- (void)createVideoFileOutput;
@property (nonatomic, strong) CALayer *customPreviewLayer;
@property (nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput;
@end
#pragma mark - Implementation
@implementation CvVideoCamera
{
id<CvVideoCameraDelegate> _delegate;
}
@synthesize grayscaleMode;
@synthesize customPreviewLayer;
@synthesize videoDataOutput;
@synthesize recordVideo;
@synthesize rotateVideo;
//@synthesize videoFileOutput;
@synthesize recordAssetWriterInput;
@synthesize recordPixelBufferAdaptor;
@synthesize recordAssetWriter;
- (void)setDelegate:(id<CvVideoCameraDelegate>)newDelegate {
_delegate = newDelegate;
}
- (id<CvVideoCameraDelegate>)delegate {
return _delegate;
}
#pragma mark - Constructors
- (id)initWithParentView:(UIView*)parent;
{
self = [super initWithParentView:parent];
if (self) {
self.useAVCaptureVideoPreviewLayer = NO;
self.recordVideo = NO;
self.rotateVideo = NO;
}
return self;
}
#pragma mark - Public interface
- (void)start;
{
if (self.running == YES) {
return;
}
recordingCountDown = 10;
[super start];
if (self.recordVideo == YES) {
NSError* error = nil;
if ([[NSFileManager defaultManager] fileExistsAtPath:[self videoFileString]]) {
[[NSFileManager defaultManager] removeItemAtPath:[self videoFileString] error:&error];
}
if (error == nil) {
NSLog(@"[Camera] Delete file %@", [self videoFileString]);
}
}
}
- (void)stop;
{
if (self.running == YES) {
[super stop];
[videoDataOutput release];
if (videoDataOutputQueue) {
dispatch_release(videoDataOutputQueue);
}
if (self.recordVideo == YES) {
if (self.recordAssetWriter) {
if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
[self.recordAssetWriter finishWriting];
NSLog(@"[Camera] recording stopped");
} else {
NSLog(@"[Camera] Recording Error: asset writer status is not writing");
}
[recordAssetWriter release];
}
[recordAssetWriterInput release];
[recordPixelBufferAdaptor release];
}
if (self.customPreviewLayer) {
[self.customPreviewLayer removeFromSuperlayer];
self.customPreviewLayer = nil;
}
}
}
// TODO fix
- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
{
NSLog(@"layout preview layer");
if (self.parentView != nil) {
CALayer* layer = self.customPreviewLayer;
CGRect bounds = self.customPreviewLayer.bounds;
int rotation_angle = 0;
bool flip_bounds = false;
switch (interfaceOrientation) {
case UIInterfaceOrientationPortrait:
NSLog(@"to Portrait");
rotation_angle = 270;
break;
case UIInterfaceOrientationPortraitUpsideDown:
rotation_angle = 90;
NSLog(@"to UpsideDown");
break;
case UIInterfaceOrientationLandscapeLeft:
rotation_angle = 0;
NSLog(@"to LandscapeLeft");
break;
case UIInterfaceOrientationLandscapeRight:
rotation_angle = 180;
NSLog(@"to LandscapeRight");
break;
default:
break; // leave the layer in its last known orientation
}
switch (self.defaultAVCaptureVideoOrientation) {
case AVCaptureVideoOrientationLandscapeRight:
rotation_angle += 180;
break;
case AVCaptureVideoOrientationPortraitUpsideDown:
rotation_angle += 270;
break;
case AVCaptureVideoOrientationPortrait:
rotation_angle += 90;
case AVCaptureVideoOrientationLandscapeLeft:
break;
default:
break;
}
rotation_angle = rotation_angle % 360;
if (rotation_angle == 90 || rotation_angle == 270) {
flip_bounds = true;
}
if (flip_bounds) {
NSLog(@"flip bounds");
bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width);
}
layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.);
self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) );
layer.bounds = bounds;
}
}
// TODO fix
- (void)layoutPreviewLayer;
{
NSLog(@"layout preview layer");
if (self.parentView != nil) {
CALayer* layer = self.customPreviewLayer;
CGRect bounds = self.customPreviewLayer.bounds;
int rotation_angle = 0;
bool flip_bounds = false;
switch (currentDeviceOrientation) {
case UIDeviceOrientationPortrait:
rotation_angle = 270;
break;
case UIDeviceOrientationPortraitUpsideDown:
rotation_angle = 90;
break;
case UIDeviceOrientationLandscapeLeft:
NSLog(@"left");
rotation_angle = 180;
break;
case UIDeviceOrientationLandscapeRight:
NSLog(@"right");
rotation_angle = 0;
break;
case UIDeviceOrientationFaceUp:
case UIDeviceOrientationFaceDown:
default:
break; // leave the layer in its last known orientation
}
switch (self.defaultAVCaptureVideoOrientation) {
case AVCaptureVideoOrientationLandscapeRight:
rotation_angle += 180;
break;
case AVCaptureVideoOrientationPortraitUpsideDown:
rotation_angle += 270;
break;
case AVCaptureVideoOrientationPortrait:
rotation_angle += 90;
case AVCaptureVideoOrientationLandscapeLeft:
break;
default:
break;
}
rotation_angle = rotation_angle % 360;
if (rotation_angle == 90 || rotation_angle == 270) {
flip_bounds = true;
}
if (flip_bounds) {
NSLog(@"flip bounds");
bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width);
}
layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.);
layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) );
layer.bounds = bounds;
}
}
#pragma mark - Private Interface
- (void)createVideoDataOutput;
{
// Make a video data output
self.videoDataOutput = [AVCaptureVideoDataOutput new];
// In grayscale mode we want YUV (YpCbCr 4:2:0) so we can directly access the graylevel intensity values (Y component)
// In color mode we, BGRA format is used
OSType format = self.grayscaleMode ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA;
self.videoDataOutput.videoSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:format]
forKey:(id)kCVPixelBufferPixelFormatTypeKey];
// discard if the data output queue is blocked (as we process the still image)
[self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];
if ( [self.captureSession canAddOutput:self.videoDataOutput] ) {
[self.captureSession addOutput:self.videoDataOutput];
}
[[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo] setEnabled:YES];
// set default FPS
AVCaptureDeviceInput *currentInput = [self.captureSession.inputs objectAtIndex:0];
AVCaptureDevice *device = currentInput.device;
NSError *error = nil;
[device lockForConfiguration:&error];
float maxRate = ((AVFrameRateRange*) [device.activeFormat.videoSupportedFrameRateRanges objectAtIndex:0]).maxFrameRate;
if (maxRate > self.defaultFPS - 1 && error == nil) {
[device setActiveVideoMinFrameDuration:CMTimeMake(1, self.defaultFPS)];
[device setActiveVideoMaxFrameDuration:CMTimeMake(1, self.defaultFPS)];
NSLog(@"[Camera] FPS set to %d", self.defaultFPS);
} else {
NSLog(@"[Camera] unable to set defaultFPS at %d FPS, max is %f FPS", self.defaultFPS, maxRate);
}
if (error != nil) {
NSLog(@"[Camera] unable to set defaultFPS: %@", error);
}
[device unlockForConfiguration];
// set video mirroring for front camera (more intuitive)
if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMirroring) {
if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {
[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = YES;
} else {
[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = NO;
}
}
// set default video orientation
if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoOrientation) {
[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoOrientation = self.defaultAVCaptureVideoOrientation;
}
// create a custom preview layer
self.customPreviewLayer = [CALayer layer];
self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
self.customPreviewLayer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.);
[self updateOrientation];
// create a serial dispatch queue used for the sample buffer delegate as well as when a still image is captured
// a serial dispatch queue must be used to guarantee that video frames will be delivered in order
// see the header doc for setSampleBufferDelegate:queue: for more information
videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL);
[self.videoDataOutput setSampleBufferDelegate:self queue:videoDataOutputQueue];
NSLog(@"[Camera] created AVCaptureVideoDataOutput");
}
- (void)createVideoFileOutput;
{
/* Video File Output in H.264, via AVAsserWriter */
NSLog(@"Create Video with dimensions %dx%d", self.imageWidth, self.imageHeight);
NSDictionary *outputSettings
= [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:self.imageWidth], AVVideoWidthKey,
[NSNumber numberWithInt:self.imageHeight], AVVideoHeightKey,
AVVideoCodecTypeH264, AVVideoCodecKey,
nil
];
self.recordAssetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings];
int pixelBufferFormat = (self.grayscaleMode == YES) ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA;
self.recordPixelBufferAdaptor =
[[AVAssetWriterInputPixelBufferAdaptor alloc]
initWithAssetWriterInput:self.recordAssetWriterInput
sourcePixelBufferAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:pixelBufferFormat], kCVPixelBufferPixelFormatTypeKey, nil]];
NSError* error = nil;
NSLog(@"Create AVAssetWriter with url: %@", [self videoFileURL]);
self.recordAssetWriter = [AVAssetWriter assetWriterWithURL:[self videoFileURL]
fileType:AVFileTypeMPEG4
error:&error];
if (error != nil) {
NSLog(@"[Camera] Unable to create AVAssetWriter: %@", error);
}
[self.recordAssetWriter addInput:self.recordAssetWriterInput];
self.recordAssetWriterInput.expectsMediaDataInRealTime = YES;
NSLog(@"[Camera] created AVAssetWriter");
}
- (void)createCaptureOutput;
{
[self createVideoDataOutput];
if (self.recordVideo == YES) {
[self createVideoFileOutput];
}
}
- (void)createCustomVideoPreview;
{
[self.parentView.layer addSublayer:self.customPreviewLayer];
}
- (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image
{
CGSize frameSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kCVPixelBufferCGImageCompatibilityKey,
[NSNumber numberWithBool:NO], kCVPixelBufferCGBitmapContextCompatibilityKey,
nil];
CVPixelBufferRef pxbuffer = NULL;
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, frameSize.width,
frameSize.height, kCVPixelFormatType_32ARGB, (CFDictionaryRef) CFBridgingRetain(options),
&pxbuffer);
NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata, frameSize.width,
frameSize.height, 8, 4*frameSize.width, rgbColorSpace,
kCGImageAlphaPremultipliedFirst);
CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image),
CGImageGetHeight(image)), image);
CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
return pxbuffer;
}
#pragma mark - Protocol AVCaptureVideoDataOutputSampleBufferDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
(void)captureOutput;
(void)connection;
auto strongDelegate = self.delegate;
if (strongDelegate) {
// convert from Core Media to Core Video
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(imageBuffer, 0);
void* bufferAddress;
size_t width;
size_t height;
size_t bytesPerRow;
CGColorSpaceRef colorSpace;
CGContextRef context;
int format_opencv;
OSType format = CVPixelBufferGetPixelFormatType(imageBuffer);
if (format == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
format_opencv = CV_8UC1;
bufferAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
width = CVPixelBufferGetWidthOfPlane(imageBuffer, 0);
height = CVPixelBufferGetHeightOfPlane(imageBuffer, 0);
bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0);
} else { // expect kCVPixelFormatType_32BGRA
format_opencv = CV_8UC4;
bufferAddress = CVPixelBufferGetBaseAddress(imageBuffer);
width = CVPixelBufferGetWidth(imageBuffer);
height = CVPixelBufferGetHeight(imageBuffer);
bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
}
// delegate image processing to the delegate
cv::Mat image((int)height, (int)width, format_opencv, bufferAddress, bytesPerRow);
CGImage* dstImage;
if ([strongDelegate respondsToSelector:@selector(processImage:)]) {
[strongDelegate processImage:image];
}
// check if matrix data pointer or dimensions were changed by the delegate
bool iOSimage = false;
if (height == (size_t)image.rows && width == (size_t)image.cols && format_opencv == image.type() && bufferAddress == image.data && bytesPerRow == image.step) {
iOSimage = true;
}
// (create color space, create graphics context, render buffer)
CGBitmapInfo bitmapInfo;
// basically we decide if it's a grayscale, rgb or rgba image
if (image.channels() == 1) {
colorSpace = CGColorSpaceCreateDeviceGray();
bitmapInfo = kCGImageAlphaNone;
} else if (image.channels() == 3) {
colorSpace = CGColorSpaceCreateDeviceRGB();
bitmapInfo = kCGImageAlphaNone;
if (iOSimage) {
bitmapInfo |= kCGBitmapByteOrder32Little;
} else {
bitmapInfo |= kCGBitmapByteOrder32Big;
}
} else {
colorSpace = CGColorSpaceCreateDeviceRGB();
bitmapInfo = kCGImageAlphaPremultipliedFirst;
if (iOSimage) {
bitmapInfo |= kCGBitmapByteOrder32Little;
} else {
bitmapInfo |= kCGBitmapByteOrder32Big;
}
}
if (iOSimage) {
context = CGBitmapContextCreate(bufferAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo);
dstImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
} else {
NSData *data = [NSData dataWithBytes:image.data length:image.elemSize()*image.total()];
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
// Creating CGImage from cv::Mat
dstImage = CGImageCreate(image.cols, // width
image.rows, // height
8, // bits per component
8 * image.elemSize(), // bits per pixel
image.step, // bytesPerRow
colorSpace, // colorspace
bitmapInfo, // bitmap info
provider, // CGDataProviderRef
NULL, // decode
false, // should interpolate
kCGRenderingIntentDefault // intent
);
CGDataProviderRelease(provider);
}
// render buffer
dispatch_sync(dispatch_get_main_queue(), ^{
self.customPreviewLayer.contents = (__bridge id)dstImage;
});
recordingCountDown--;
if (self.recordVideo == YES && recordingCountDown < 0) {
lastSampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
// CMTimeShow(lastSampleTime);
if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) {
[self.recordAssetWriter startWriting];
[self.recordAssetWriter startSessionAtSourceTime:lastSampleTime];
if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) {
NSLog(@"[Camera] Recording Error: asset writer status is not writing: %@", self.recordAssetWriter.error);
return;
} else {
NSLog(@"[Camera] Video recording started");
}
}
if (self.recordAssetWriterInput.readyForMoreMediaData) {
CVImageBufferRef pixelBuffer = [self pixelBufferFromCGImage:dstImage];
if (! [self.recordPixelBufferAdaptor appendPixelBuffer:pixelBuffer
withPresentationTime:lastSampleTime] ) {
NSLog(@"Video Writing Error");
}
if (pixelBuffer != nullptr)
CVPixelBufferRelease(pixelBuffer);
}
}
// cleanup
CGImageRelease(dstImage);
CGColorSpaceRelease(colorSpace);
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
}
}
- (void)updateOrientation;
{
if (self.rotateVideo == YES)
{
NSLog(@"rotate..");
self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
[self layoutPreviewLayer];
}
}
- (void)saveVideo;
{
if (self.recordVideo == NO) {
return;
}
UISaveVideoAtPathToSavedPhotosAlbum([self videoFileString], nil, nil, NULL);
}
- (NSURL *)videoFileURL;
{
NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:outputPath]) {
NSLog(@"file exists");
}
return outputURL;
}
- (NSString *)videoFileString;
{
NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
return outputPath;
}
@end
+249
View File
@@ -0,0 +1,249 @@
// 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"
#ifdef HAVE_LIBREALSENSE
#include "cap_librealsense.hpp"
namespace cv
{
VideoCapture_LibRealsense::VideoCapture_LibRealsense(int) : mAlign(RS2_STREAM_COLOR)
{
try
{
rs2::config config;
// Configure all streams to run at VGA resolution at default fps
config.enable_stream(RS2_STREAM_DEPTH, 640, 480, RS2_FORMAT_Z16);
config.enable_stream(RS2_STREAM_COLOR, 640, 480, RS2_FORMAT_BGR8);
config.enable_stream(RS2_STREAM_INFRARED, 640, 480, RS2_FORMAT_Y8);
mPipe.start(config);
}
catch (const rs2::error&)
{
}
}
VideoCapture_LibRealsense::~VideoCapture_LibRealsense(){}
double VideoCapture_LibRealsense::getProperty(int propIdx) const
{
double propValue = CAP_PROP_UNKNOWN;
const int purePropIdx = propIdx & ~CAP_INTELPERC_GENERATORS_MASK;
if((propIdx & CAP_INTELPERC_GENERATORS_MASK) == CAP_INTELPERC_IMAGE_GENERATOR)
{
propValue = getImageGeneratorProperty(purePropIdx);
}
else if((propIdx & CAP_INTELPERC_GENERATORS_MASK) == CAP_INTELPERC_DEPTH_GENERATOR)
{
propValue = getDepthGeneratorProperty(purePropIdx);
}
else if((propIdx & CAP_INTELPERC_GENERATORS_MASK) == CAP_INTELPERC_IR_GENERATOR)
{
propValue = getIrGeneratorProperty(purePropIdx);
}
else
{
propValue = getCommonProperty(purePropIdx);
}
return propValue;
}
double VideoCapture_LibRealsense::getImageGeneratorProperty(int propIdx) const
{
double propValue = CAP_PROP_UNKNOWN;
const rs2::video_stream_profile profile = mPipe.get_active_profile().get_stream(RS2_STREAM_COLOR).as<rs2::video_stream_profile>();
if(!profile)
{
return propValue;
}
switch(propIdx)
{
case CAP_PROP_FRAME_WIDTH:
propValue = static_cast<double>(profile.width());
break;
case CAP_PROP_FRAME_HEIGHT:
propValue = static_cast<double>(profile.height());
break;
case CAP_PROP_FPS:
propValue = static_cast<double>(profile.fps());
break;
}
return propValue;
}
double VideoCapture_LibRealsense::getDepthGeneratorProperty(int propIdx) const
{
double propValue = CAP_PROP_UNKNOWN;
const rs2::video_stream_profile profile = mPipe.get_active_profile().get_stream(RS2_STREAM_DEPTH).as<rs2::video_stream_profile>();
const rs2::depth_sensor sensor = mPipe.get_active_profile().get_device().first<rs2::depth_sensor>();
if(!profile || !sensor)
{
return propValue;
}
switch(propIdx)
{
case CAP_PROP_FRAME_WIDTH:
propValue = static_cast<double>(profile.width());
break;
case CAP_PROP_FRAME_HEIGHT:
propValue = static_cast<double>(profile.height());
break;
case CAP_PROP_FPS:
propValue = static_cast<double>(profile.fps());
break;
case CAP_PROP_INTELPERC_DEPTH_SATURATION_VALUE:
propValue = static_cast<double>(sensor.get_depth_scale());
break;
case CAP_PROP_INTELPERC_DEPTH_FOCAL_LENGTH_HORZ:
propValue = static_cast<double>(profile.get_intrinsics().fx);
break;
case CAP_PROP_INTELPERC_DEPTH_FOCAL_LENGTH_VERT:
propValue = static_cast<double>(profile.get_intrinsics().fy);
break;
}
return propValue;
}
double VideoCapture_LibRealsense::getIrGeneratorProperty(int propIdx) const
{
double propValue = CAP_PROP_UNKNOWN;
const rs2::video_stream_profile profile = mPipe.get_active_profile().get_stream(RS2_STREAM_INFRARED).as<rs2::video_stream_profile>();
if(!profile)
{
return propValue;
}
switch(propIdx)
{
case CAP_PROP_FRAME_WIDTH:
propValue = static_cast<double>(profile.width());
break;
case CAP_PROP_FRAME_HEIGHT:
propValue = static_cast<double>(profile.height());
break;
case CAP_PROP_FPS:
propValue = static_cast<double>(profile.fps());
break;
}
return propValue;
}
double VideoCapture_LibRealsense::getCommonProperty(int propIdx) const
{
double propValue = CAP_PROP_UNKNOWN;
const rs2::video_stream_profile profile = mPipe.get_active_profile().get_stream(RS2_STREAM_DEPTH).as<rs2::video_stream_profile>();
const rs2::depth_sensor sensor = mPipe.get_active_profile().get_device().first<rs2::depth_sensor>();
if(!profile || !sensor)
{
return propValue;
}
switch(propIdx)
{
case CAP_PROP_FRAME_WIDTH:
case CAP_PROP_FRAME_HEIGHT:
case CAP_PROP_FPS:
propValue = getDepthGeneratorProperty(propIdx);
break;
case CAP_PROP_INTELPERC_DEPTH_SATURATION_VALUE:
propValue = static_cast<double>(sensor.get_depth_scale());
break;
case CAP_PROP_INTELPERC_DEPTH_FOCAL_LENGTH_HORZ:
propValue = static_cast<double>(profile.get_intrinsics().fx);
break;
case CAP_PROP_INTELPERC_DEPTH_FOCAL_LENGTH_VERT:
propValue = static_cast<double>(profile.get_intrinsics().fy);
break;
}
return propValue;
}
bool VideoCapture_LibRealsense::setProperty(int, double)
{
bool isSet = false;
return isSet;
}
bool VideoCapture_LibRealsense::grabFrame()
{
if (!isOpened())
return false;
try
{
mData = mAlign.process(mPipe.wait_for_frames());
}
catch (const rs2::error&)
{
return false;
}
return true;
}
bool VideoCapture_LibRealsense::retrieveFrame(int outputType, cv::OutputArray frame)
{
rs2::video_frame _frame(nullptr);
int type;
switch (outputType)
{
case CAP_INTELPERC_DEPTH_MAP:
_frame = mData.get_depth_frame().as<rs2::video_frame>();
type = CV_16UC1;
break;
case CAP_INTELPERC_IR_MAP:
_frame = mData.get_infrared_frame();
type = CV_8UC1;
break;
case CAP_INTELPERC_IMAGE:
_frame = mData.get_color_frame();
type = CV_8UC3;
break;
default:
return false;
}
try
{
// we copy the data straight away, so const_cast should be fine
void* data = const_cast<void*>(_frame.get_data());
Mat(_frame.get_height(), _frame.get_width(), type, data, _frame.get_stride_in_bytes()).copyTo(frame);
if(_frame.get_profile().format() == RS2_FORMAT_RGB8)
cvtColor(frame, frame, COLOR_RGB2BGR);
}
catch (const rs2::error&)
{
return false;
}
return true;
}
int VideoCapture_LibRealsense::getCaptureDomain()
{
return CAP_INTELPERC;
}
bool VideoCapture_LibRealsense::isOpened() const
{
return bool(std::shared_ptr<rs2_pipeline>(mPipe));
}
Ptr<IVideoCapture> create_RealSense_capture(int index)
{
return makePtr<VideoCapture_LibRealsense>(index);
}
}
#endif
+43
View File
@@ -0,0 +1,43 @@
// 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 _CAP_LIBREALSENE_HPP_
#define _CAP_LIBREALSENE_HPP_
#ifdef HAVE_LIBREALSENSE
#include <librealsense2/rs.hpp>
namespace cv
{
class VideoCapture_LibRealsense : public IVideoCapture
{
public:
VideoCapture_LibRealsense(int index);
virtual ~VideoCapture_LibRealsense();
virtual double getProperty(int propIdx) const CV_OVERRIDE;
virtual bool setProperty(int propIdx, double propVal) CV_OVERRIDE;
virtual bool grabFrame() CV_OVERRIDE;
virtual bool retrieveFrame(int outputType, OutputArray frame) CV_OVERRIDE;
virtual int getCaptureDomain() CV_OVERRIDE;
virtual bool isOpened() const CV_OVERRIDE;
protected:
rs2::pipeline mPipe;
rs2::frameset mData;
rs2::align mAlign;
double getDepthGeneratorProperty(int propIdx) const;
double getImageGeneratorProperty(int propIdx) const;
double getIrGeneratorProperty(int propIdx) const;
double getCommonProperty(int propIdx) const;
};
}
#endif
#endif
+262
View File
@@ -0,0 +1,262 @@
// 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 "cap_mfx_common.hpp"
// Linux specific
#ifdef __linux__
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
using namespace std;
using namespace cv;
#ifndef HAVE_ONEVPL
static mfxIMPL getImpl()
{
static const size_t res = utils::getConfigurationParameterSizeT("OPENCV_VIDEOIO_MFX_IMPL", MFX_IMPL_AUTO_ANY);
return (mfxIMPL)res;
}
#endif
static size_t getExtraSurfaceNum()
{
static const size_t res = cv::utils::getConfigurationParameterSizeT("OPENCV_VIDEOIO_MFX_EXTRA_SURFACE_NUM", 1);
return res;
}
static size_t getPoolTimeoutSec()
{
static const size_t res = utils::getConfigurationParameterSizeT("OPENCV_VIDEOIO_MFX_POOL_TIMEOUT", 1);
return res;
}
#ifdef HAVE_ONEVPL
// oneVPL loader singleton (HW implementation only)
static mfxLoader setupVPLLoader()
{
mfxLoader instance = MFXLoad();
mfxConfig cfg = MFXCreateConfig(instance);
mfxVariant impl;
impl.Type = MFX_VARIANT_TYPE_U32;
impl.Data.U32 = MFX_IMPL_TYPE_HARDWARE;
MFXSetConfigFilterProperty(cfg, (const mfxU8*)"mfxImplDescription.Impl", impl);
DBG(cerr << "MFX Load: " << instance << endl);
return instance;
}
mfxLoader getVPLLoaderInstance()
{
static mfxLoader instance = setupVPLLoader();
return instance;
}
#endif
//==================================================================================================
bool DeviceHandler::init(MFXVideoSession_WRAP &session)
{
mfxStatus res = MFX_ERR_NONE;
mfxVersion ver = { {19, 1} };
#ifdef HAVE_ONEVPL
res = session.CreateSession();
DBG(cout << "MFX CreateSession: " << res << endl);
#else
mfxIMPL impl = getImpl();
res = session.Init(impl, &ver);
DBG(cout << "MFX SessionInit: " << res << endl);
res = session.QueryIMPL(&impl);
DBG(cout << "MFX QueryIMPL: " << res << " => " << asHex(impl) << endl);
#endif
res = session.QueryVersion(&ver);
DBG(cout << "MFX QueryVersion: " << res << " => " << ver.Major << "." << ver.Minor << endl);
if (res != MFX_ERR_NONE)
return false;
return initDeviceSession(session);
}
//==================================================================================================
#ifdef __linux__
VAHandle::VAHandle() {
// TODO: provide a way of modifying this path
const string filename = "/dev/dri/renderD128";
file = open(filename.c_str(), O_RDWR);
if (file < 0)
CV_Error(Error::StsError, "Can't open file: " + filename);
display = vaGetDisplayDRM(file);
}
VAHandle::~VAHandle() {
if (display) {
vaTerminate(display);
}
if (file >= 0) {
close(file);
}
}
bool VAHandle::initDeviceSession(MFXVideoSession_WRAP &session) {
int majorVer = 0, minorVer = 0;
VAStatus va_res = vaInitialize(display, &majorVer, &minorVer);
DBG(cout << "vaInitialize: " << va_res << endl << majorVer << '.' << minorVer << endl);
if (va_res == VA_STATUS_SUCCESS) {
mfxStatus mfx_res = session.SetHandle(static_cast<mfxHandleType>(MFX_HANDLE_VA_DISPLAY), display);
DBG(cout << "MFX SetHandle: " << mfx_res << endl);
if (mfx_res == MFX_ERR_NONE) {
return true;
}
}
return false;
}
#endif // __linux__
DeviceHandler * createDeviceHandler()
{
#if defined __linux__
return new VAHandle();
#elif defined _WIN32
return new DXHandle();
#else
return 0;
#endif
}
//==================================================================================================
SurfacePool::SurfacePool(ushort width_, ushort height_, ushort count, const mfxFrameInfo &frameInfo, uchar bpp)
: width(alignSize(width_, 32)),
height(alignSize(height_, 32)),
oneSize(width * height * bpp / 8),
buffers(count * oneSize),
surfaces(count)
{
for(int i = 0; i < count; ++i)
{
mfxFrameSurface1 &surface = surfaces[i];
uint8_t * dataPtr = buffers.data() + oneSize * i;
memset(&surface, 0, sizeof(mfxFrameSurface1));
surface.Info = frameInfo;
surface.Data.Y = dataPtr;
surface.Data.UV = dataPtr + width * height;
surface.Data.PitchLow = width & 0xFFFF;
surface.Data.PitchHigh = (width >> 16) & 0xFFFF;
DBG(cout << "allocate surface " << (void*)&surface << ", Y = " << (void*)dataPtr << " (" << width << "x" << height << ")" << endl);
}
DBG(cout << "Allocated: " << endl
<< "- surface data: " << buffers.size() << " bytes" << endl
<< "- surface headers: " << surfaces.size() * sizeof(mfxFrameSurface1) << " bytes" << endl);
}
SurfacePool::~SurfacePool()
{
}
SurfacePool * SurfacePool::_create(const mfxFrameAllocRequest &request, const mfxVideoParam &params)
{
return new SurfacePool(request.Info.Width,
request.Info.Height,
saturate_cast<ushort>((size_t)request.NumFrameSuggested + getExtraSurfaceNum()),
params.mfx.FrameInfo);
}
mfxFrameSurface1 *SurfacePool::getFreeSurface()
{
const int64 start = cv::getTickCount();
do
{
for(std::vector<mfxFrameSurface1>::iterator i = surfaces.begin(); i != surfaces.end(); ++i)
if (!i->Data.Locked)
return &(*i);
sleep_ms(10);
}
while((cv::getTickCount() - start) / cv::getTickFrequency() < getPoolTimeoutSec()); // seconds
DBG(cout << "No free surface!" << std::endl);
return 0;
}
//==================================================================================================
ReadBitstream::ReadBitstream(const char *filename, size_t maxSize) : drain(false)
{
input.open(filename, std::ios::in | std::ios::binary);
DBG(cout << "Open " << filename << " -> " << input.is_open() << std::endl);
memset(&stream, 0, sizeof(stream));
stream.MaxLength = (mfxU32)maxSize;
stream.Data = new mfxU8[stream.MaxLength];
CV_Assert(stream.Data);
}
ReadBitstream::~ReadBitstream()
{
delete[] stream.Data;
}
bool ReadBitstream::isOpened() const
{
return input.is_open();
}
bool ReadBitstream::isDone() const
{
return input.eof();
}
bool ReadBitstream::read()
{
memmove(stream.Data, stream.Data + stream.DataOffset, stream.DataLength);
stream.DataOffset = 0;
input.read((char*)(stream.Data + stream.DataLength), stream.MaxLength - stream.DataLength);
if (input.eof() || input.good())
{
mfxU32 bytesRead = (mfxU32)input.gcount();
if (bytesRead > 0)
{
stream.DataLength += bytesRead;
DBG(cout << "read " << bytesRead << " bytes" << endl);
return true;
}
}
return false;
}
//==================================================================================================
WriteBitstream::WriteBitstream(const char * filename, size_t maxSize)
{
output.open(filename, std::ios::out | std::ios::binary);
DBG(cout << "BS Open " << filename << " -> " << output.is_open() << std::endl);
memset(&stream, 0, sizeof(stream));
stream.MaxLength = (mfxU32)maxSize;
stream.Data = new mfxU8[stream.MaxLength];
DBG(cout << "BS Allocate " << maxSize << " bytes (" << ((float)maxSize / (1 << 20)) << " Mb)" << endl);
CV_Assert(stream.Data);
}
WriteBitstream::~WriteBitstream()
{
delete[] stream.Data;
}
bool WriteBitstream::write()
{
output.write((char*)(stream.Data + stream.DataOffset), stream.DataLength);
stream.DataLength = 0;
return output.good();
}
bool WriteBitstream::isOpened() const
{
return output.is_open();
}
+384
View File
@@ -0,0 +1,384 @@
// 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 MFXHELPER_H
#define MFXHELPER_H
#include "opencv2/core.hpp"
#include "opencv2/core/utils/configuration.private.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
CV_SUPPRESS_DEPRECATED_START
# if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable:4201) // nonstandard extension used: nameless struct/union
# endif
#ifdef HAVE_ONEVPL
# include <vpl/mfxcommon.h>
# include <vpl/mfxstructures.h>
# include <vpl/mfxvideo++.h>
# include <vpl/mfxvp8.h>
# include <vpl/mfxjpeg.h>
# include <vpl/mfxdispatcher.h>
#else
# include <mfxcommon.h>
# include <mfxstructures.h>
# include <mfxvideo++.h>
# include <mfxvp8.h>
# include <mfxjpeg.h>
# ifdef HAVE_MFX_PLUGIN
# include <mfxplugin++.h>
# endif
#endif
# if defined(_MSC_VER)
# pragma warning(pop)
# endif
CV_SUPPRESS_DEPRECATED_END
// //
// Debug helpers //
// //
#if 0
# define DBG(i) i
#else
# define DBG(i)
#endif
#if 1
# define MSG(i) i
#else
# define MSG(i)
#endif
template <typename T>
struct HexWrap {
HexWrap(T val_) : val(val_) {}
T val;
};
template <typename T>
inline std::ostream & operator<<(std::ostream &out, const HexWrap<T> &wrap) {
std::ios_base::fmtflags flags = out.flags(std::ios::hex | std::ios::showbase);
out << wrap.val;
out.flags(flags);
return out;
}
template <typename T>
inline ::HexWrap<T> asHex(const T & val) {
return ::HexWrap<T>(val);
}
struct FourCC
{
FourCC(uint val) : val32(val) {}
FourCC(char a, char b, char c, char d) { val8[0] = a; val8[1] = b; val8[2] = c; val8[3] = d; }
union {
uint val32;
int vali32;
uchar val8[4];
};
};
inline std::ostream & operator<<(std::ostream &out, FourCC cc) {
for (size_t i = 0; i < 4; out << cc.val8[i++]) {}
out << " (" << asHex(cc.val32) << ")";
return out;
}
inline std::string mfxStatusToString(mfxStatus s) {
switch (s)
{
case MFX_ERR_NONE: return "MFX_ERR_NONE";
case MFX_ERR_UNKNOWN: return "MFX_ERR_UNKNOWN";
case MFX_ERR_NULL_PTR: return "MFX_ERR_NULL_PTR";
case MFX_ERR_UNSUPPORTED: return "MFX_ERR_UNSUPPORTED";
case MFX_ERR_MEMORY_ALLOC: return "MFX_ERR_MEMORY_ALLOC";
case MFX_ERR_NOT_ENOUGH_BUFFER: return "MFX_ERR_NOT_ENOUGH_BUFFER";
case MFX_ERR_INVALID_HANDLE: return "MFX_ERR_INVALID_HANDLE";
case MFX_ERR_LOCK_MEMORY: return "MFX_ERR_LOCK_MEMORY";
case MFX_ERR_NOT_INITIALIZED: return "MFX_ERR_NOT_INITIALIZED";
case MFX_ERR_NOT_FOUND: return "MFX_ERR_NOT_FOUND";
case MFX_ERR_MORE_DATA: return "MFX_ERR_MORE_DATA";
case MFX_ERR_MORE_SURFACE: return "MFX_ERR_MORE_SURFACE";
case MFX_ERR_ABORTED: return "MFX_ERR_ABORTED";
case MFX_ERR_DEVICE_LOST: return "MFX_ERR_DEVICE_LOST";
case MFX_ERR_INCOMPATIBLE_VIDEO_PARAM: return "MFX_ERR_INCOMPATIBLE_VIDEO_PARAM";
case MFX_ERR_INVALID_VIDEO_PARAM: return "MFX_ERR_INVALID_VIDEO_PARAM";
case MFX_ERR_UNDEFINED_BEHAVIOR: return "MFX_ERR_UNDEFINED_BEHAVIOR";
case MFX_ERR_DEVICE_FAILED: return "MFX_ERR_DEVICE_FAILED";
case MFX_ERR_MORE_BITSTREAM: return "MFX_ERR_MORE_BITSTREAM";
case MFX_ERR_GPU_HANG: return "MFX_ERR_GPU_HANG";
case MFX_ERR_REALLOC_SURFACE: return "MFX_ERR_REALLOC_SURFACE";
case MFX_WRN_IN_EXECUTION: return "MFX_WRN_IN_EXECUTION";
case MFX_WRN_DEVICE_BUSY: return "MFX_WRN_DEVICE_BUSY";
case MFX_WRN_VIDEO_PARAM_CHANGED: return "MFX_WRN_VIDEO_PARAM_CHANGED";
case MFX_WRN_PARTIAL_ACCELERATION: return "MFX_WRN_PARTIAL_ACCELERATION";
case MFX_WRN_INCOMPATIBLE_VIDEO_PARAM: return "MFX_WRN_INCOMPATIBLE_VIDEO_PARAM";
case MFX_WRN_VALUE_NOT_CHANGED: return "MFX_WRN_VALUE_NOT_CHANGED";
case MFX_WRN_OUT_OF_RANGE: return "MFX_WRN_OUT_OF_RANGE";
case MFX_WRN_FILTER_SKIPPED: return "MFX_WRN_FILTER_SKIPPED";
default: return "<Invalid or unknown mfxStatus>";
}
}
inline std::ostream & operator<<(std::ostream &out, mfxStatus s) {
out << mfxStatusToString(s) << " (" << (int)s << ")"; return out;
}
inline std::ostream & operator<<(std::ostream &out, const mfxInfoMFX &info) {
out << "InfoMFX:" << std::endl
<< "| Codec: " << FourCC(info.CodecId) << " / " << info.CodecProfile << " / " << info.CodecLevel << std::endl
<< "| DecodedOrder: " << info.DecodedOrder << std::endl
<< "| TimeStampCalc: " << info.TimeStampCalc << std::endl
;
return out;
}
inline std::ostream & operator<<(std::ostream & out, const mfxFrameInfo & info) {
out << "FrameInfo: " << std::endl
<< "| FourCC: " << FourCC(info.FourCC) << std::endl
<< "| Size: " << info.Width << "x" << info.Height << std::endl
<< "| ROI: " << "(" << info.CropX << ";" << info.CropY << ") " << info.CropW << "x" << info.CropH << std::endl
<< "| BitDepth(L/C): " << info.BitDepthLuma << " / " << info.BitDepthChroma << std::endl
<< "| Shift: " << info.Shift << std::endl
<< "| TemporalID: " << info.FrameId.TemporalId << std::endl
<< "| FrameRate: " << info.FrameRateExtN << "/" << info.FrameRateExtD << std::endl
<< "| AspectRatio: " << info.AspectRatioW << "x" << info.AspectRatioH << std::endl
<< "| PicStruct: " << info.PicStruct << std::endl
<< "| ChromaFormat: " << info.ChromaFormat << std::endl
;
return out;
}
inline std::ostream & operator<<(std::ostream &out, const mfxFrameData &data) {
out << "FrameData:" << std::endl
<< "| NumExtParam: " << data.NumExtParam << std::endl
<< "| MemType: " << data.MemType << std::endl
<< "| PitchHigh: " << data.PitchHigh << std::endl
<< "| TimeStamp: " << data.TimeStamp << std::endl
<< "| FrameOrder: " << data.FrameOrder << std::endl
<< "| Locked: " << data.Locked << std::endl
<< "| Pitch: " << data.PitchHigh << ", " << data.PitchLow << std::endl
<< "| Y: " << (void*)data.Y << std::endl
<< "| U: " << (void*)data.U << std::endl
<< "| V: " << (void*)data.V << std::endl
;
return out;
}
//==================================================================================================
template <typename T>
inline void cleanup(T * &ptr)
{
if (ptr)
{
delete ptr;
ptr = 0;
}
}
//==================================================================================================
#ifdef HAVE_ONEVPL
mfxLoader getVPLLoaderInstance();
#endif
//==================================================================================================
class MFXVideoSession_WRAP : public MFXVideoSession
{
#ifdef HAVE_ONEVPL
public:
mfxStatus CreateSession()
{
return MFXCreateSession(getVPLLoaderInstance(), 0, &m_session);
}
#endif
};
//==================================================================================================
class Plugin
{
public:
static Plugin * loadEncoderPlugin(MFXVideoSession_WRAP &session, mfxU32 codecId)
{
#ifdef HAVE_MFX_PLUGIN
static const mfxPluginUID hevc_enc_uid = { 0x6f, 0xad, 0xc7, 0x91, 0xa0, 0xc2, 0xeb, 0x47, 0x9a, 0xb6, 0xdc, 0xd5, 0xea, 0x9d, 0xa3, 0x47 };
if (codecId == MFX_CODEC_HEVC)
return new Plugin(session, hevc_enc_uid);
#else
CV_UNUSED(session); CV_UNUSED(codecId);
#endif
return 0;
}
static Plugin * loadDecoderPlugin(MFXVideoSession_WRAP &session, mfxU32 codecId)
{
#ifdef HAVE_MFX_PLUGIN
static const mfxPluginUID hevc_dec_uid = { 0x33, 0xa6, 0x1c, 0x0b, 0x4c, 0x27, 0x45, 0x4c, 0xa8, 0xd8, 0x5d, 0xde, 0x75, 0x7c, 0x6f, 0x8e };
if (codecId == MFX_CODEC_HEVC)
return new Plugin(session, hevc_dec_uid);
#else
CV_UNUSED(session); CV_UNUSED(codecId);
#endif
return 0;
}
~Plugin()
{
#ifdef HAVE_MFX_PLUGIN
if (isGood())
MFXVideoUSER_UnLoad(session, &uid);
#endif
}
bool isGood() const { return res >= MFX_ERR_NONE; }
private:
mfxStatus res;
private:
#ifdef HAVE_MFX_PLUGIN
MFXVideoSession_WRAP &session;
mfxPluginUID uid;
Plugin(MFXVideoSession_WRAP &_session, mfxPluginUID _uid) : session(_session), uid(_uid)
{
res = MFXVideoUSER_Load(session, &uid, 1);
}
#endif
Plugin(const Plugin &);
Plugin &operator=(const Plugin &);
};
//==================================================================================================
class ReadBitstream
{
public:
ReadBitstream(const char * filename, size_t maxSize = 10 * 1024 * 1024);
~ReadBitstream();
bool isOpened() const;
bool isDone() const;
bool read();
private:
ReadBitstream(const ReadBitstream &);
ReadBitstream &operator=(const ReadBitstream &);
public:
std::fstream input;
mfxBitstream stream;
bool drain;
};
//==================================================================================================
class WriteBitstream
{
public:
WriteBitstream(const char * filename, size_t maxSize);
~WriteBitstream();
bool write();
bool isOpened() const;
private:
WriteBitstream(const WriteBitstream &);
WriteBitstream &operator=(const WriteBitstream &);
public:
std::fstream output;
mfxBitstream stream;
};
//==================================================================================================
class SurfacePool
{
public:
SurfacePool(ushort width_, ushort height_, ushort count, const mfxFrameInfo & frameInfo, uchar bpp = 12);
~SurfacePool();
mfxFrameSurface1 *getFreeSurface();
template <typename T>
static SurfacePool * create(T * instance, mfxVideoParam &params)
{
CV_Assert(instance);
mfxFrameAllocRequest request;
memset(&request, 0, sizeof(request));
mfxStatus res = instance->QueryIOSurf(&params, &request);
DBG(std::cout << "MFX QueryIOSurf: " << res << std::endl);
if (res < MFX_ERR_NONE)
return 0;
return _create(request, params);
}
private:
static SurfacePool* _create(const mfxFrameAllocRequest& request, const mfxVideoParam& params);
private:
SurfacePool(const SurfacePool &);
SurfacePool &operator=(const SurfacePool &);
public:
size_t width, height;
size_t oneSize;
cv::AutoBuffer<uchar, 0> buffers;
std::vector<mfxFrameSurface1> surfaces;
};
//==================================================================================================
class DeviceHandler {
public:
virtual ~DeviceHandler() {}
bool init(MFXVideoSession_WRAP &session);
protected:
virtual bool initDeviceSession(MFXVideoSession_WRAP &session) = 0;
};
// TODO: move to core::util?
#include <thread>
static void sleep_ms(int64 ms)
{
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
// Linux specific
#ifdef __linux__
#include <unistd.h>
#include <va/va_drm.h>
class VAHandle : public DeviceHandler {
public:
VAHandle();
~VAHandle();
private:
VAHandle(const VAHandle &);
VAHandle &operator=(const VAHandle &);
bool initDeviceSession(MFXVideoSession_WRAP &session) CV_OVERRIDE;
private:
VADisplay display;
int file;
};
#endif // __linux__
// Windows specific
#ifdef _WIN32
#include <Windows.h>
class DXHandle : public DeviceHandler {
public:
DXHandle() {}
~DXHandle() {}
private:
DXHandle(const DXHandle &);
DXHandle &operator=(const DXHandle &);
bool initDeviceSession(MFXVideoSession_WRAP &) CV_OVERRIDE { return true; }
};
#endif // _WIN32
DeviceHandler * createDeviceHandler();
#endif // MFXHELPER_H
+263
View File
@@ -0,0 +1,263 @@
// 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
#if defined(BUILD_PLUGIN)
#include <string>
#include "cap_mfx_reader.hpp"
#include "cap_mfx_writer.hpp"
#define ABI_VERSION 0
#define API_VERSION 0
#include "plugin_api.hpp"
using namespace std;
namespace cv {
static
CvResult CV_API_CALL cv_capture_open(const char* filename, int, CV_OUT CvPluginCapture* handle)
{
if (!handle)
return CV_ERROR_FAIL;
*handle = NULL;
if (!filename)
return CV_ERROR_FAIL;
VideoCapture_IntelMFX *cap = 0;
try
{
if (filename)
{
cap = new VideoCapture_IntelMFX(string(filename));
if (cap->isOpened())
{
*handle = (CvPluginCapture)cap;
return CV_ERROR_OK;
}
}
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "MFX: Exception is raised: " << e.what());
}
catch (...)
{
CV_LOG_WARNING(NULL, "MFX: Unknown C++ exception is raised");
}
if (cap)
delete cap;
return CV_ERROR_FAIL;
}
static
CvResult CV_API_CALL cv_capture_release(CvPluginCapture handle)
{
if (!handle)
return CV_ERROR_FAIL;
VideoCapture_IntelMFX* instance = (VideoCapture_IntelMFX*)handle;
delete instance;
return CV_ERROR_OK;
}
static
CvResult CV_API_CALL cv_capture_get_prop(CvPluginCapture handle, int prop, CV_OUT double* val)
{
if (!handle)
return CV_ERROR_FAIL;
if (!val)
return CV_ERROR_FAIL;
try
{
VideoCapture_IntelMFX* instance = (VideoCapture_IntelMFX*)handle;
*val = instance->getProperty(prop);
return CV_ERROR_OK;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "MFX: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "MFX: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
static
CvResult CV_API_CALL cv_capture_set_prop(CvPluginCapture handle, int prop, double val)
{
if (!handle)
return CV_ERROR_FAIL;
try
{
VideoCapture_IntelMFX* instance = (VideoCapture_IntelMFX*)handle;
return instance->setProperty(prop, val) ? CV_ERROR_OK : CV_ERROR_FAIL;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "MFX: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "MFX: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
static
CvResult CV_API_CALL cv_capture_grab(CvPluginCapture handle)
{
if (!handle)
return CV_ERROR_FAIL;
try
{
VideoCapture_IntelMFX* instance = (VideoCapture_IntelMFX*)handle;
return instance->grabFrame() ? CV_ERROR_OK : CV_ERROR_FAIL;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "MFX: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "MFX: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
static
CvResult CV_API_CALL cv_capture_retrieve(CvPluginCapture handle, int stream_idx, cv_videoio_retrieve_cb_t callback, void* userdata)
{
if (!handle)
return CV_ERROR_FAIL;
try
{
VideoCapture_IntelMFX* instance = (VideoCapture_IntelMFX*)handle;
Mat img;
if (instance->retrieveFrame(stream_idx, img))
return callback(stream_idx, img.data, (int)img.step, img.cols, img.rows, img.channels(), userdata);
return CV_ERROR_FAIL;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "MFX: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "MFX: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
static
CvResult CV_API_CALL cv_writer_open(const char* filename, int fourcc, double fps, int width, int height, int isColor,
CV_OUT CvPluginWriter* handle)
{
VideoWriter_IntelMFX* wrt = 0;
try
{
wrt = new VideoWriter_IntelMFX(filename, fourcc, fps, Size(width, height), isColor);
if(wrt->isOpened())
{
*handle = (CvPluginWriter)wrt;
return CV_ERROR_OK;
}
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "MFX: Exception is raised: " << e.what());
}
catch (...)
{
CV_LOG_WARNING(NULL, "MFX: Unknown C++ exception is raised");
}
if (wrt)
delete wrt;
return CV_ERROR_FAIL;
}
static
CvResult CV_API_CALL cv_writer_release(CvPluginWriter handle)
{
if (!handle)
return CV_ERROR_FAIL;
VideoWriter_IntelMFX* instance = (VideoWriter_IntelMFX*)handle;
delete instance;
return CV_ERROR_OK;
}
static
CvResult CV_API_CALL cv_writer_get_prop(CvPluginWriter /*handle*/, int /*prop*/, CV_OUT double* /*val*/)
{
return CV_ERROR_FAIL;
}
static
CvResult CV_API_CALL cv_writer_set_prop(CvPluginWriter /*handle*/, int /*prop*/, double /*val*/)
{
return CV_ERROR_FAIL;
}
static
CvResult CV_API_CALL cv_writer_write(CvPluginWriter handle, const unsigned char *data, int step, int width, int height, int cn)
{
if (!handle)
return CV_ERROR_FAIL;
try
{
VideoWriter_IntelMFX* instance = (VideoWriter_IntelMFX*)handle;
Mat img(Size(width, height), CV_MAKETYPE(CV_8U, cn), const_cast<uchar*>(data), step);
instance->write(img);
return CV_ERROR_OK;
}
catch (const std::exception& e)
{
CV_LOG_WARNING(NULL, "MFX: Exception is raised: " << e.what());
return CV_ERROR_FAIL;
}
catch (...)
{
CV_LOG_WARNING(NULL, "MFX: Unknown C++ exception is raised");
return CV_ERROR_FAIL;
}
}
static const OpenCV_VideoIO_Plugin_API_preview plugin_api =
{
{
sizeof(OpenCV_VideoIO_Plugin_API_preview), ABI_VERSION, API_VERSION,
CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS,
"MediaSDK OpenCV Video I/O plugin"
},
{
/* 1*/CAP_INTEL_MFX,
/* 2*/cv_capture_open,
/* 3*/cv_capture_release,
/* 4*/cv_capture_get_prop,
/* 5*/cv_capture_set_prop,
/* 6*/cv_capture_grab,
/* 7*/cv_capture_retrieve,
/* 8*/cv_writer_open,
/* 9*/cv_writer_release,
/* 10*/cv_writer_get_prop,
/* 11*/cv_writer_set_prop,
/* 12*/cv_writer_write
}
};
} // namespace
const OpenCV_VideoIO_Plugin_API_preview* opencv_videoio_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT
{
if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION)
return &cv::plugin_api;
return NULL;
}
#endif // BUILD_PLUGIN
+290
View File
@@ -0,0 +1,290 @@
// 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 "cap_mfx_reader.hpp"
#include "opencv2/core/base.hpp"
#include "cap_mfx_common.hpp"
#include "opencv2/imgproc/hal/hal.hpp"
#include "cap_interface.hpp"
using namespace cv;
using namespace std;
inline bool hasExtension(const String &filename, const String &ext)
{
if (filename.size() <= ext.size())
return false;
const size_t diff = filename.size() - ext.size();
const size_t found_at = filename.rfind(ext);
return found_at == diff;
}
inline mfxU32 determineCodecId(const String &filename)
{
if (hasExtension(filename, ".h264") || hasExtension(filename, ".264"))
return MFX_CODEC_AVC;
else if (hasExtension(filename, ".mp2") || hasExtension(filename, ".mpeg2"))
return MFX_CODEC_MPEG2;
else if (hasExtension(filename, ".265") || hasExtension(filename, ".hevc"))
return MFX_CODEC_HEVC;
else
return (mfxU32)-1;
}
//==========================================================================
VideoCapture_IntelMFX::VideoCapture_IntelMFX(const cv::String &filename)
: session(0), plugin(0), deviceHandler(0), bs(0), decoder(0), pool(0), outSurface(0), good(false)
{
mfxStatus res = MFX_ERR_NONE;
// Init device and session
deviceHandler = createDeviceHandler();
session = new MFXVideoSession_WRAP();
if (!deviceHandler->init(*session))
{
MSG(cerr << "MFX: Can't initialize session" << endl);
return;
}
// Load appropriate plugin
mfxU32 codecId = determineCodecId(filename);
if (codecId == (mfxU32)-1)
{
MSG(cerr << "MFX: Unsupported extension: " << filename << endl);
return;
}
plugin = Plugin::loadDecoderPlugin(*session, codecId);
if (plugin && !plugin->isGood())
{
MSG(cerr << "MFX: LoadPlugin failed for codec: " << codecId << " (" << filename << ")" << endl);
return;
}
// Read some content from file
bs = new ReadBitstream(filename.c_str());
if (!bs->read())
{
MSG(cerr << "MFX: Failed to read bitstream" << endl);
return;
}
// Create decoder and decode stream header
decoder = new MFXVideoDECODE(*session);
mfxVideoParam params;
memset(&params, 0, sizeof(params));
params.mfx.CodecId = codecId;
params.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
res = decoder->DecodeHeader(&bs->stream, &params);
DBG(cout << "DecodeHeader: " << res << endl << params.mfx << params.mfx.FrameInfo << endl);
if (res < MFX_ERR_NONE)
{
MSG(cerr << "MFX: Failed to decode stream header: " << res << endl);
return;
}
// Adjust parameters - COMMENTED: h265 decoder resets crop size to 0 (oneVPL/Win)
//res = decoder->Query(&params, &params);
//DBG(cout << "MFX Query: " << res << endl << params.mfx << params.mfx.FrameInfo);
//CV_Assert(res >= MFX_ERR_NONE);
// Init surface pool
pool = SurfacePool::create(decoder, params);
if (!pool)
{
MSG(cerr << "MFX: Failed to create surface pool" << endl);
return;
}
// Init decoder
res = decoder->Init(&params);
DBG(cout << "MFX decoder Init: " << res << endl << params.mfx.FrameInfo);
if (res < MFX_ERR_NONE)
{
MSG(cerr << "MFX: Failed to init decoder: " << res << endl);
return;
}
frameSize = Size(params.mfx.FrameInfo.CropW, params.mfx.FrameInfo.CropH);
if (frameSize == Size(0, 0)) // sometimes Crop size is 0
{
frameSize = Size(params.mfx.FrameInfo.Width, params.mfx.FrameInfo.Height);
}
good = true;
}
VideoCapture_IntelMFX::~VideoCapture_IntelMFX()
{
cleanup(plugin);
cleanup(bs);
cleanup(decoder);
cleanup(pool);
session->Close();
cleanup(session);
cleanup(deviceHandler);
}
double VideoCapture_IntelMFX::getProperty(int prop) const
{
if (!good)
{
MSG(cerr << "MFX: can not call getProperty(), backend has not been initialized" << endl);
return CAP_PROP_UNKNOWN;
}
switch (prop)
{
case CAP_PROP_FRAME_WIDTH:
return frameSize.width;
case CAP_PROP_FRAME_HEIGHT:
return frameSize.height;
default:
MSG(cerr << "MFX: unsupported property" << endl);
return CAP_PROP_UNKNOWN;
}
}
bool VideoCapture_IntelMFX::setProperty(int, double)
{
MSG(cerr << "MFX: setProperty() is not implemented" << endl);
return false;
}
bool VideoCapture_IntelMFX::grabFrame()
{
mfxStatus res;
mfxFrameSurface1 *workSurface = 0;
mfxSyncPoint sync;
workSurface = pool->getFreeSurface();
while (true)
{
if (!workSurface)
{
// not enough surfaces
MSG(cerr << "MFX: Failed to get free surface" << endl);
return false;
}
outSurface = 0;
res = decoder->DecodeFrameAsync(bs->drain ? 0 : &bs->stream, workSurface, (mfxFrameSurface1**)&outSurface, &sync);
if (res == MFX_ERR_NONE)
{
res = session->SyncOperation(sync, 1000); // 1 sec, TODO: provide interface to modify timeout
if (res == MFX_ERR_NONE)
{
// ready to retrieve
DBG(cout << "Frame ready to retrieve" << endl);
return true;
}
else
{
MSG(cerr << "MFX: Sync error: " << res << endl);
return false;
}
}
else if (res == MFX_ERR_MORE_DATA)
{
if (bs->isDone())
{
if (bs->drain)
{
// finish
DBG(cout << "Drain finished" << endl);
return false;
}
else
{
DBG(cout << "Bitstream finished - Drain started" << endl);
bs->drain = true;
continue;
}
}
else
{
bool read_res = bs->read();
if (!read_res)
{
// failed to read
MSG(cerr << "MFX: Bitstream read failure" << endl);
return false;
}
else
{
DBG(cout << "Bitstream read success" << endl);
continue;
}
}
}
else if (res == MFX_ERR_MORE_SURFACE)
{
DBG(cout << "Getting another surface" << endl);
workSurface = pool->getFreeSurface();
continue;
}
else if (res == MFX_WRN_DEVICE_BUSY)
{
DBG(cout << "Waiting for device" << endl);
sleep_ms(1000);
continue;
}
else if (res == MFX_WRN_VIDEO_PARAM_CHANGED)
{
DBG(cout << "Video param changed" << endl);
continue;
}
else
{
MSG(cerr << "MFX: Bad status: " << res << endl);
return false;
}
}
}
bool VideoCapture_IntelMFX::retrieveFrame(int, OutputArray out)
{
if (!outSurface)
{
MSG(cerr << "MFX: No frame ready to retrieve" << endl);
return false;
}
mfxFrameSurface1 * s = (mfxFrameSurface1*)outSurface;
mfxFrameInfo &info = s->Info;
mfxFrameData &data = s->Data;
const int cols = info.CropW;
const int rows = info.CropH;
out.create(rows, cols, CV_8UC3);
Mat res = out.getMat();
hal::cvtTwoPlaneYUVtoBGR(data.Y, data.UV, data.Pitch, res.data, res.step, cols, rows, 3, false, 0);
return true;
}
bool VideoCapture_IntelMFX::isOpened() const
{
return good;
}
int VideoCapture_IntelMFX::getCaptureDomain()
{
return CAP_INTEL_MFX;
}
//==================================================================================================
cv::Ptr<IVideoCapture> cv::create_MFX_capture(const std::string &filename)
{
return cv::makePtr<VideoCapture_IntelMFX>(filename);
}
+42
View File
@@ -0,0 +1,42 @@
// 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 CAP_MFX_HPP
#define CAP_MFX_HPP
#include "precomp.hpp"
class MFXVideoSession_WRAP;
class Plugin;
class DeviceHandler;
class ReadBitstream;
class SurfacePool;
class MFXVideoDECODE;
class VideoCapture_IntelMFX : public cv::IVideoCapture
{
public:
VideoCapture_IntelMFX(const cv::String &filename);
~VideoCapture_IntelMFX();
double getProperty(int) const CV_OVERRIDE;
bool setProperty(int, double) CV_OVERRIDE;
bool grabFrame() CV_OVERRIDE;
bool retrieveFrame(int, cv::OutputArray out) CV_OVERRIDE;
bool isOpened() const CV_OVERRIDE;
int getCaptureDomain() CV_OVERRIDE;
private:
MFXVideoSession_WRAP *session;
Plugin *plugin;
DeviceHandler *deviceHandler;
ReadBitstream *bs;
MFXVideoDECODE *decoder;
SurfacePool *pool;
void *outSurface;
cv::Size frameSize;
bool good;
};
#endif
+302
View File
@@ -0,0 +1,302 @@
// 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 "cap_mfx_writer.hpp"
#include "opencv2/core/base.hpp"
#include "cap_mfx_common.hpp"
#include "opencv2/imgproc/hal/hal.hpp"
#include "cap_interface.hpp"
using namespace std;
using namespace cv;
static float estimateBitrate(int codecId, size_t pixelNum, float fps)
{
float bitrate = 0.f;
const float mp = pixelNum / 1000000.f;
if (codecId == MFX_CODEC_MPEG2)
{
bitrate = (mp * 43) * fps + 360;
}
else if (codecId == MFX_CODEC_AVC)
{
bitrate = (mp * 140 + 19) * std::pow(fps, 0.60f);
}
else if (codecId == MFX_CODEC_HEVC)
{
bitrate = (mp * 63 + 45) * std::pow(fps, 0.60f);
}
else
{
MSG(cerr << "MFX encoder Bitrate estimation FAILED" << endl);
}
DBG(cout << "MFX encoder Bitrate estimation (" << mp << " MP x " << fps << " fps): " << bitrate << endl);
return bitrate;
}
static size_t getBitrateDivisor()
{
static const size_t res = utils::getConfigurationParameterSizeT("OPENCV_VIDEOIO_MFX_BITRATE_DIVISOR", 300);
return res;
}
static mfxU32 getWriterTimeoutMS()
{
static const size_t res = utils::getConfigurationParameterSizeT("OPENCV_VIDEOIO_MFX_WRITER_TIMEOUT", 1);
return saturate_cast<mfxU32>(res * 1000); // convert from seconds
}
inline mfxU32 codecIdByFourCC(int fourcc)
{
const int CC_MPG2 = FourCC('M', 'P', 'G', '2').vali32;
const int CC_H264 = FourCC('H', '2', '6', '4').vali32;
const int CC_X264 = FourCC('X', '2', '6', '4').vali32;
const int CC_AVC = FourCC('A', 'V', 'C', ' ').vali32;
const int CC_H265 = FourCC('H', '2', '6', '5').vali32;
const int CC_HEVC = FourCC('H', 'E', 'V', 'C').vali32;
if (fourcc == CC_X264 || fourcc == CC_H264 || fourcc == CC_AVC)
return MFX_CODEC_AVC;
else if (fourcc == CC_H265 || fourcc == CC_HEVC)
return MFX_CODEC_HEVC;
else if (fourcc == CC_MPG2)
return MFX_CODEC_MPEG2;
else
return (mfxU32)-1;
}
VideoWriter_IntelMFX::VideoWriter_IntelMFX(const String &filename, int _fourcc, double fps, Size frameSize_, bool)
: session(0), plugin(0), deviceHandler(0), bs(0), encoder(0), pool(0), outSurface(NULL), frameSize(frameSize_), good(false)
{
mfxStatus res = MFX_ERR_NONE;
if (frameSize.width % 2 || frameSize.height % 2)
{
MSG(cerr << "MFX: Invalid frame size passed to encoder" << endl);
return;
}
if (fps <= 0)
{
MSG(cerr << "MFX: Invalid FPS passed to encoder" << endl);
return;
}
// Init device and session
deviceHandler = createDeviceHandler();
session = new MFXVideoSession_WRAP();
if (!deviceHandler->init(*session))
{
MSG(cerr << "MFX: Can't initialize session" << endl);
return;
}
// Load appropriate plugin
mfxU32 codecId = codecIdByFourCC(_fourcc);
if (codecId == (mfxU32)-1)
{
MSG(cerr << "MFX: Unsupported FourCC: " << FourCC(_fourcc) << endl);
return;
}
plugin = Plugin::loadEncoderPlugin(*session, codecId);
if (plugin && !plugin->isGood())
{
MSG(cerr << "MFX: LoadPlugin failed for codec: " << codecId << " (" << FourCC(_fourcc) << ")" << endl);
return;
}
// Init encoder
encoder = new MFXVideoENCODE(*session);
mfxVideoParam params;
memset(&params, 0, sizeof(params));
params.mfx.CodecId = codecId;
params.mfx.TargetUsage = MFX_TARGETUSAGE_BALANCED;
params.mfx.TargetKbps = saturate_cast<mfxU16>(estimateBitrate(codecId, frameSize.area(), (float)fps) * 300 / getBitrateDivisor()); // TODO: set in options
params.mfx.RateControlMethod = MFX_RATECONTROL_VBR;
params.mfx.FrameInfo.FrameRateExtN = cvRound(fps * 1000);
params.mfx.FrameInfo.FrameRateExtD = 1000;
params.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
params.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
params.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
params.mfx.FrameInfo.CropX = 0;
params.mfx.FrameInfo.CropY = 0;
params.mfx.FrameInfo.CropW = (mfxU16)frameSize.width;
params.mfx.FrameInfo.CropH = (mfxU16)frameSize.height;
params.mfx.FrameInfo.Width = (mfxU16)alignSize(frameSize.width, 32);
params.mfx.FrameInfo.Height = (mfxU16)alignSize(frameSize.height, 32);
params.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY;
res = encoder->Query(&params, &params);
DBG(cout << "MFX Query: " << res << endl << params.mfx << params.mfx.FrameInfo);
if (res < MFX_ERR_NONE)
{
MSG(cerr << "MFX: Query failed: " << res << endl);
return;
}
// Init surface pool
pool = SurfacePool::create(encoder, params);
if (!pool)
{
MSG(cerr << "MFX: Failed to create surface pool" << endl);
return;
}
// Init encoder
res = encoder->Init(&params);
DBG(cout << "MFX encoder Init: " << res << endl << params.mfx.FrameInfo);
if (res < MFX_ERR_NONE)
{
MSG(cerr << "MFX: Failed to init encoder: " << res << endl);
return;
}
// Open output bitstream
{
mfxVideoParam par;
memset(&par, 0, sizeof(par));
res = encoder->GetVideoParam(&par);
DBG(cout << "MFX GetVideoParam: " << res << endl << "requested " << par.mfx.BufferSizeInKB << " kB" << endl);
CV_Assert(res >= MFX_ERR_NONE);
bs = new WriteBitstream(filename.c_str(), par.mfx.BufferSizeInKB * 1024 * 2);
if (!bs->isOpened())
{
MSG(cerr << "MFX: Failed to open output file: " << filename << endl);
return;
}
}
good = true;
}
VideoWriter_IntelMFX::~VideoWriter_IntelMFX()
{
if (isOpened())
{
DBG(cout << "====== Drain bitstream..." << endl);
Mat dummy;
while (write_one(dummy)) {}
DBG(cout << "====== Drain Finished" << endl);
}
cleanup(bs);
cleanup(pool);
cleanup(encoder);
cleanup(plugin);
cleanup(session);
cleanup(deviceHandler);
}
double VideoWriter_IntelMFX::getProperty(int) const
{
MSG(cerr << "MFX: getProperty() is not implemented" << endl);
return CAP_PROP_UNKNOWN;
}
bool VideoWriter_IntelMFX::setProperty(int, double)
{
MSG(cerr << "MFX: setProperty() is not implemented" << endl);
return false;
}
bool VideoWriter_IntelMFX::isOpened() const
{
return good;
}
bool VideoWriter_IntelMFX::write(cv::InputArray input)
{
return write_one(input);
}
bool VideoWriter_IntelMFX::write_one(cv::InputArray bgr)
{
mfxStatus res;
mfxFrameSurface1 *workSurface = 0;
mfxSyncPoint sync;
if (!bgr.empty() && (bgr.dims() != 2 || bgr.type() != CV_8UC3 || bgr.size() != frameSize))
{
MSG(cerr << "MFX: invalid frame passed to encoder: "
<< "dims/depth/cn=" << bgr.dims() << "/" << bgr.depth() << "/" << bgr.channels()
<< ", size=" << bgr.size() << endl);
return false;
}
if (!bgr.empty())
{
workSurface = pool->getFreeSurface();
if (!workSurface)
{
// not enough surfaces
MSG(cerr << "MFX: Failed to get free surface" << endl);
return false;
}
Mat src = bgr.getMat();
hal::cvtBGRtoTwoPlaneYUV(src.data, src.step,
workSurface->Data.Y, workSurface->Data.UV, workSurface->Data.Pitch,
workSurface->Info.CropW, workSurface->Info.CropH,
3, false, 1);
}
while (true)
{
outSurface = 0;
DBG(cout << "Calling with surface: " << workSurface << endl);
res = encoder->EncodeFrameAsync(NULL, workSurface, &bs->stream, &sync);
if (res == MFX_ERR_NONE)
{
res = session->SyncOperation(sync, getWriterTimeoutMS()); // TODO: provide interface to modify timeout
if (res == MFX_ERR_NONE)
{
// ready to write
if (!bs->write())
{
MSG(cerr << "MFX: Failed to write bitstream" << endl);
return false;
}
else
{
DBG(cout << "Write bitstream" << endl);
return true;
}
}
else
{
MSG(cerr << "MFX: Sync error: " << res << endl);
return false;
}
}
else if (res == MFX_ERR_MORE_DATA)
{
DBG(cout << "ERR_MORE_DATA" << endl);
return false;
}
else if (res == MFX_WRN_DEVICE_BUSY)
{
DBG(cout << "Waiting for device" << endl);
sleep_ms(1000);
continue;
}
else
{
MSG(cerr << "MFX: Bad status: " << res << endl);
return false;
}
}
}
Ptr<IVideoWriter> cv::create_MFX_writer(const std::string& filename, int _fourcc, double fps,
const Size& frameSize, const VideoWriterParameters& params)
{
if (codecIdByFourCC(_fourcc) > 0)
{
const bool isColor = params.get(VIDEOWRITER_PROP_IS_COLOR, true);
Ptr<VideoWriter_IntelMFX> a = makePtr<VideoWriter_IntelMFX>(filename, _fourcc, fps, frameSize, isColor);
if (a->isOpened())
return a;
}
return Ptr<VideoWriter_IntelMFX>();
}
+47
View File
@@ -0,0 +1,47 @@
// 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 CAP_MFX_WRITER_HPP
#define CAP_MFX_WRITER_HPP
#include "precomp.hpp"
class MFXVideoSession_WRAP;
class Plugin;
class DeviceHandler;
class WriteBitstream;
class SurfacePool;
class MFXVideoDECODE;
class MFXVideoENCODE;
class VideoWriter_IntelMFX : public cv::IVideoWriter
{
public:
VideoWriter_IntelMFX(const cv::String &filename, int _fourcc, double fps, cv::Size frameSize, bool isColor);
~VideoWriter_IntelMFX() CV_OVERRIDE;
double getProperty(int) const CV_OVERRIDE;
bool setProperty(int, double) CV_OVERRIDE;
bool isOpened() const CV_OVERRIDE;
bool write(cv::InputArray input) CV_OVERRIDE;
int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_INTEL_MFX; }
protected:
bool write_one(cv::InputArray bgr);
private:
VideoWriter_IntelMFX(const VideoWriter_IntelMFX &);
VideoWriter_IntelMFX & operator=(const VideoWriter_IntelMFX &);
private:
MFXVideoSession_WRAP *session;
Plugin *plugin;
DeviceHandler *deviceHandler;
WriteBitstream *bs;
MFXVideoENCODE *encoder;
SurfacePool *pool;
void *outSurface;
cv::Size frameSize;
bool good;
};
#endif // CAP_MFX_WRITER_HPP
+233
View File
@@ -0,0 +1,233 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2015, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include "opencv2/videoio/container_avi.private.hpp"
namespace cv
{
class MotionJpegCapture: public IVideoCapture
{
public:
virtual ~MotionJpegCapture() CV_OVERRIDE;
virtual double getProperty(int) const CV_OVERRIDE;
virtual bool setProperty(int, double) CV_OVERRIDE;
virtual bool grabFrame() CV_OVERRIDE;
virtual bool retrieveFrame(int, OutputArray) CV_OVERRIDE;
virtual bool isOpened() const CV_OVERRIDE;
virtual int getCaptureDomain() CV_OVERRIDE { return CAP_OPENCV_MJPEG; }
MotionJpegCapture(const String&);
bool open(const String&);
void close();
protected:
inline uint64_t getFramePos() const;
Ptr<AVIReadContainer> m_avi_container;
bool m_is_first_frame;
frame_list m_mjpeg_frames;
frame_iterator m_frame_iterator;
Mat m_current_frame;
//frame width/height and fps could be different for
//each frame/stream. At the moment we suppose that they
//stays the same within single avi file.
uint32_t m_frame_width;
uint32_t m_frame_height;
double m_fps;
};
uint64_t MotionJpegCapture::getFramePos() const
{
if(m_is_first_frame)
return 0;
if(m_frame_iterator == m_mjpeg_frames.end())
return m_mjpeg_frames.size();
return m_frame_iterator - m_mjpeg_frames.begin() + 1;
}
bool MotionJpegCapture::setProperty(int property, double value)
{
if(property == CAP_PROP_POS_FRAMES)
{
if(int(value) == 0)
{
m_is_first_frame = true;
m_frame_iterator = m_mjpeg_frames.end();
return true;
}
else if(m_mjpeg_frames.size() > value)
{
m_frame_iterator = m_mjpeg_frames.begin() + int(value - 1);
m_is_first_frame = false;
return true;
}
}
return false;
}
double MotionJpegCapture::getProperty(int property) const
{
switch(property)
{
case CAP_PROP_POS_FRAMES:
return (double)getFramePos();
case CAP_PROP_POS_MSEC:
return (double)getFramePos() * (1000. / m_fps);
case CAP_PROP_POS_AVI_RATIO:
return double(getFramePos())/m_mjpeg_frames.size();
case CAP_PROP_FRAME_WIDTH:
return (double)m_frame_width;
case CAP_PROP_FRAME_HEIGHT:
return (double)m_frame_height;
case CAP_PROP_FPS:
return m_fps;
case CAP_PROP_FOURCC:
return (double)CV_FOURCC('M','J','P','G');
case CAP_PROP_FRAME_COUNT:
return (double)m_mjpeg_frames.size();
case CAP_PROP_FORMAT:
default:
return CAP_PROP_UNKNOWN;
}
}
bool MotionJpegCapture::grabFrame()
{
if(isOpened())
{
if(m_is_first_frame)
{
m_is_first_frame = false;
m_frame_iterator = m_mjpeg_frames.begin();
}
else
{
if (m_frame_iterator == m_mjpeg_frames.end())
return false;
++m_frame_iterator;
}
}
return m_frame_iterator != m_mjpeg_frames.end();
}
bool MotionJpegCapture::retrieveFrame(int, OutputArray output_frame)
{
if(m_frame_iterator != m_mjpeg_frames.end())
{
std::vector<char> data = m_avi_container->readFrame(m_frame_iterator);
if(data.size())
{
m_current_frame = imdecode(data, IMREAD_ANYDEPTH | IMREAD_COLOR | IMREAD_IGNORE_ORIENTATION);
}
m_current_frame.copyTo(output_frame);
return true;
}
return false;
}
MotionJpegCapture::~MotionJpegCapture()
{
close();
}
MotionJpegCapture::MotionJpegCapture(const String& filename)
{
m_avi_container = makePtr<AVIReadContainer>();
m_avi_container->initStream(filename);
open(filename);
}
bool MotionJpegCapture::isOpened() const
{
return m_mjpeg_frames.size() > 0;
}
void MotionJpegCapture::close()
{
m_avi_container->close();
m_frame_iterator = m_mjpeg_frames.end();
}
bool MotionJpegCapture::open(const String& filename)
{
close();
m_avi_container = makePtr<AVIReadContainer>();
m_avi_container->initStream(filename);
m_frame_iterator = m_mjpeg_frames.end();
m_is_first_frame = true;
if(!m_avi_container->parseRiff(m_mjpeg_frames))
{
close();
} else
{
m_frame_width = m_avi_container->getWidth();
m_frame_height = m_avi_container->getHeight();
m_fps = m_avi_container->getFps();
}
return isOpened();
}
Ptr<IVideoCapture> createMotionJpegCapture(const String& filename)
{
Ptr<MotionJpegCapture> mjdecoder(new MotionJpegCapture(filename));
if( mjdecoder->isOpened() )
return mjdecoder;
return Ptr<MotionJpegCapture>();
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
View File
@@ -0,0 +1,128 @@
// 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.
/*
* Copyright(C) 2022 by ORBBEC Technology., Inc.
* Authors:
* Huang Zhenchang <yufeng@orbbec.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_INTERFACE_HPP
#define OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_INTERFACE_HPP
#ifdef HAVE_OBSENSOR
#include "../precomp.hpp" // #include "precomp.hpp : compile error on linux
#include <functional>
#include <vector>
#include <memory>
namespace cv {
namespace obsensor {
#define OBSENSOR_CAM_VID 0x2bc5 // usb vid
#define OBSENSOR_ASTRA2_PID 0x0660 // pid of Orbbec Astra 2 Camera
#define OBSENSOR_GEMINI2_PID 0x0670 // pid of Orbbec Gemini 2 Camera
#define OBSENSOR_FEMTO_MEGA_PID 0x0669 // pid of Orbbec Femto Mega Camera
#define OBSENSOR_GEMINI2L_PID 0x0673 // pid of Orbbec Gemini 2 L Camera
#define OBSENSOR_GEMINI2XL_PID 0x0671 // pid of Orbbec Gemini 2 XL Camera
#define OBSENSOR_GEMINI335_PID 0x0800 // pid of Orbbec Gemini 335 Camera
#define OBSENSOR_GEMINI330_PID 0x0801 // pid of Orbbec Gemini 330 Camera
#define OBSENSOR_GEMINI336_PID 0x0803 // pid of Orbbec Gemini 336 Camera
#define OBSENSOR_GEMINI335L_PID 0x0804 // pid of Orbbec Gemini 335L Camera
#define OBSENSOR_GEMINI330L_PID 0x0805 // pid of Orbbec Gemini 330L Camera
#define OBSENSOR_GEMINI336L_PID 0x0807 // pid of Orbbec Gemini 336L Camera
#define IS_OBSENSOR_GEMINI330_SHORT_PID(pid) \
((pid) == OBSENSOR_GEMINI335_PID || (pid) == OBSENSOR_GEMINI330_PID || (pid) == OBSENSOR_GEMINI336_PID)
#define IS_OBSENSOR_GEMINI330_LONG_PID(pid) \
((pid) == OBSENSOR_GEMINI335L_PID || (pid) == OBSENSOR_GEMINI330L_PID || (pid) == OBSENSOR_GEMINI336L_PID)
#define IS_OBSENSOR_GEMINI330_PID(pid) \
(IS_OBSENSOR_GEMINI330_SHORT_PID(pid) || IS_OBSENSOR_GEMINI330_LONG_PID(pid))
enum StreamType
{
OBSENSOR_STREAM_IR = 1,
OBSENSOR_STREAM_COLOR = 2,
OBSENSOR_STREAM_DEPTH = 3,
};
enum FrameFormat
{
FRAME_FORMAT_UNKNOWN = -1,
FRAME_FORMAT_YUYV = 0,
FRAME_FORMAT_MJPG = 5,
FRAME_FORMAT_Y16 = 8,
FRAME_FORMAT_Y14 = 9,
};
enum PropertyId
{
DEPTH_TO_COLOR_ALIGN = 42,
CAMERA_PARAM = 1001,
};
struct Frame
{
FrameFormat format;
uint32_t width;
uint32_t height;
uint32_t dataSize;
uint8_t* data;
};
struct StreamProfile
{
uint32_t width;
uint32_t height;
uint32_t fps;
FrameFormat format;
};
struct CameraParam
{
float p0[4];
float p1[4];
float p2[9];
float p3[3];
float p4[5];
float p5[5];
uint32_t p6[2];
uint32_t p7[2];
};
typedef std::function<void(Frame*)> FrameCallback;
class IStreamChannel
{
public:
virtual ~IStreamChannel() noexcept {}
virtual void start(const StreamProfile& profile, FrameCallback frameCallback) = 0;
virtual void stop() = 0;
virtual bool setProperty(int propId, const uint8_t* data, uint32_t dataSize) = 0;
virtual bool getProperty(int propId, uint8_t* recvData, uint32_t* recvDataSize) = 0;
virtual StreamType streamType() const = 0;
virtual uint16_t getPid() const =0;
};
// "StreamChannelGroup" mean a group of stream channels from same one physical device
std::vector<Ptr<IStreamChannel>> getStreamChannelGroup(uint32_t groupIdx = 0);
}} // namespace cv::obsensor::
#endif // HAVE_OBSENSOR
#endif // OPENCV_VIDEOIO_OBSENSOR_STREAM_CHANNEL_INTERFACE_HPP
@@ -0,0 +1,517 @@
// 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.
/*
* Copyright(C) 2022 by ORBBEC Technology., Inc.
* Authors:
* Huang Zhenchang <yufeng@orbbec.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_OBSENSOR_MSMF
#include "obsensor_stream_channel_msmf.hpp"
#include <shlwapi.h> // QISearch
#include <Mferror.h>
#pragma warning(disable : 4503)
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mf")
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "Strmiids")
#pragma comment(lib, "Mfreadwrite")
#pragma comment(lib, "dxgi")
#pragma comment(lib, "Shlwapi")
namespace cv {
namespace obsensor {
std::string wideCharToUTF8(const WCHAR* s)
{
auto len = WideCharToMultiByte(CP_UTF8, 0, s, -1, nullptr, 0, nullptr, nullptr);
if (len == 0)
return "";
std::string buffer(len - 1, ' ');
len = WideCharToMultiByte(CP_UTF8, 0, s, -1, &buffer[0], static_cast<int>(buffer.size()) + 1, nullptr, nullptr);
return buffer;
}
std::string hr_to_string(HRESULT hr)
{
_com_error err(hr);
std::stringstream ss;
ss << "HResult 0x" << std::hex << hr << ": \"" << err.ErrorMessage() << "\"";
return ss.str();
}
#define HR_FAILED_RETURN(x) \
if (x < 0) \
{ \
CV_LOG_INFO(NULL, "Media Foundation error return: " << hr_to_string(x)); \
return; \
}
#define HR_FAILED_LOG(x) \
if (x < 0) \
{ \
CV_LOG_INFO(NULL, "Media Foundation error return: " << hr_to_string(x)); \
}
#define HR_FAILED_EXEC(x, statement) \
if (x < 0) \
{ \
CV_LOG_INFO(NULL, "Media Foundation error return: " << hr_to_string(x)); \
statement; \
}
std::vector<std::string> stringSplit(std::string string, char separator)
{
std::vector<std::string> tokens;
std::string::size_type i1 = 0;
while (true)
{
auto i2 = string.find(separator, i1);
if (i2 == std::string::npos)
{
tokens.push_back(string.substr(i1));
return tokens;
}
tokens.push_back(string.substr(i1, i2 - i1));
i1 = i2 + 1;
}
}
bool parseUvcDeviceSymbolicLink(const std::string& symbolicLink, uint16_t& vid, uint16_t& pid, uint16_t& mi, std::string& unique_id,
std::string& device_guid)
{
std::string lowerStr = symbolicLink;
for (size_t i = 0; i < lowerStr.length(); i++)
{
lowerStr[i] = (char)tolower(lowerStr[i]);
}
auto tokens = stringSplit(lowerStr, '#');
if (tokens.size() < 1 || (tokens[0] != R"(\\?\usb)" && tokens[0] != R"(\\?\hid)"))
return false; // Not a USB device
if (tokens.size() < 3)
{
return false;
}
auto ids = stringSplit(tokens[1], '&');
if (ids[0].size() != 8 || ids[0].substr(0, 4) != "vid_" || !(std::istringstream(ids[0].substr(4, 4)) >> std::hex >> vid))
{
return false;
}
if (ids[1].size() != 8 || ids[1].substr(0, 4) != "pid_" || !(std::istringstream(ids[1].substr(4, 4)) >> std::hex >> pid))
{
return false;
}
if (ids.size() > 2 && (ids[2].size() != 5 || ids[2].substr(0, 3) != "mi_" || !(std::istringstream(ids[2].substr(3, 2)) >> mi)))
{
return false;
}
ids = stringSplit(tokens[2], '&');
if (ids.size() == 0)
{
return false;
}
if (ids.size() > 2)
unique_id = ids[1];
else
unique_id = "";
if (tokens.size() >= 3)
device_guid = tokens[3];
return true;
}
#pragma pack(push, 1)
template <class T>
class big_endian
{
T be_value;
public:
operator T() const
{
T le_value = 0;
for (unsigned int i = 0; i < sizeof(T); ++i)
reinterpret_cast<char*>(&le_value)[i] = reinterpret_cast<const char*>(&be_value)[sizeof(T) - i - 1];
return le_value;
}
};
#pragma pack(pop)
MFContext::~MFContext(void)
{
CoUninitialize();
}
MFContext& MFContext::getInstance()
{
static MFContext instance;
return instance;
}
std::vector<UvcDeviceInfo> MFContext::queryUvcDeviceInfoList()
{
std::vector<UvcDeviceInfo> uvcDevList;
ComPtr<IMFAttributes> pAttributes = nullptr;
MFCreateAttributes(&pAttributes, 1);
pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
// pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_CATEGORY, KSCATEGORY_SENSOR_CAMERA);
IMFActivate** ppDevices;
uint32_t numDevices;
MFEnumDeviceSources(pAttributes.Get(), &ppDevices, &numDevices);
for (uint32_t i = 0; i < numDevices; ++i)
{
ComPtr<IMFActivate> pDevice;
pDevice = ppDevices[i];
WCHAR* wCharStr = nullptr;
uint32_t length;
pDevice->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &wCharStr, &length);
auto symbolicLink = wideCharToUTF8(wCharStr);
CoTaskMemFree(wCharStr);
pDevice->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &wCharStr, &length);
auto name = wideCharToUTF8(wCharStr);
CoTaskMemFree(wCharStr);
uint16_t vid, pid, mi;
std::string uid, guid;
if (!parseUvcDeviceSymbolicLink(symbolicLink, vid, pid, mi, uid, guid))
continue;
uvcDevList.emplace_back(UvcDeviceInfo{ symbolicLink, name, uid, vid, pid, mi });
CV_LOG_INFO(NULL, "UVC device found: name=" << name << ", vid=" << vid << ", pid=" << pid << ", mi=" << mi << ", uid=" << uid << ", guid=" << guid);
}
return uvcDevList;
}
Ptr<IStreamChannel> MFContext::createStreamChannel(const UvcDeviceInfo& devInfo)
{
return makePtr<MSMFStreamChannel>(devInfo);
}
MFContext::MFContext()
{
CoInitialize(0);
CV_Assert(SUCCEEDED(MFStartup(MF_VERSION)));
}
MSMFStreamChannel::MSMFStreamChannel(const UvcDeviceInfo& devInfo) :
IUvcStreamChannel(devInfo),
mfContext_(MFContext::getInstance()),
xuNodeId_(-1)
{
HR_FAILED_RETURN(MFCreateAttributes(&deviceAttrs_, 2));
HR_FAILED_RETURN(deviceAttrs_->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID));
WCHAR* buffer = new wchar_t[devInfo_.id.length() + 1];
MultiByteToWideChar(CP_UTF8, 0, devInfo_.id.c_str(), (int)devInfo_.id.length() + 1, buffer, (int)devInfo_.id.length() * sizeof(WCHAR));
HR_FAILED_EXEC(deviceAttrs_->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, buffer), {
delete[] buffer;
return;
})
delete[] buffer;
HR_FAILED_RETURN(MFCreateDeviceSource(deviceAttrs_.Get(), &deviceSource_));
HR_FAILED_LOG(deviceSource_->QueryInterface(__uuidof(IAMCameraControl), reinterpret_cast<void**>(&cameraControl_)));
HR_FAILED_LOG(deviceSource_->QueryInterface(__uuidof(IAMVideoProcAmp), reinterpret_cast<void**>(&videoProcAmp_)));
HR_FAILED_RETURN(MFCreateAttributes(&readerAttrs_, 3));
HR_FAILED_RETURN(readerAttrs_->SetUINT32(MF_SOURCE_READER_DISCONNECT_MEDIASOURCE_ON_SHUTDOWN, false));
HR_FAILED_RETURN(readerAttrs_->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true));
HR_FAILED_RETURN(readerAttrs_->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, static_cast<IUnknown*>(this)));
HR_FAILED_RETURN(MFCreateSourceReaderFromMediaSource(deviceSource_.Get(), readerAttrs_.Get(), &streamReader_));
HR_FAILED_RETURN(streamReader_->SetStreamSelection(static_cast<DWORD>(MF_SOURCE_READER_ALL_STREAMS), true));
HR_FAILED_RETURN(deviceSource_->QueryInterface(__uuidof(IKsTopologyInfo), reinterpret_cast<void**>(&xuKsTopologyInfo_)));
DWORD nNodes = 0;
HR_FAILED_RETURN(xuKsTopologyInfo_->get_NumNodes(&nNodes));
for (DWORD i = 0; i < nNodes; i++)
{
GUID nodeType;
HR_FAILED_EXEC(xuKsTopologyInfo_->get_NodeType(i, &nodeType), { continue; })
if (nodeType == KSNODETYPE_DEV_SPECIFIC)
{
xuNodeId_ = i;
}
}
if (xuNodeId_ != -1)
{
HR_FAILED_RETURN(xuKsTopologyInfo_->CreateNodeInstance(xuNodeId_, IID_IUnknown, reinterpret_cast<LPVOID*>(&xuNodeInstance_)));
HR_FAILED_RETURN(xuNodeInstance_->QueryInterface(__uuidof(IKsControl), reinterpret_cast<void**>(&xuKsControl_)));
}
if (streamType_ == OBSENSOR_STREAM_DEPTH)
{
initDepthFrameProcessor();
}
}
MSMFStreamChannel::~MSMFStreamChannel()
{
stop();
if (cameraControl_)
{
cameraControl_.Release();
}
if (videoProcAmp_)
{
videoProcAmp_.Release();
}
if (streamReader_)
{
streamReader_.Release();
}
if (readerAttrs_)
{
readerAttrs_.Release();
}
if (deviceAttrs_)
{
deviceAttrs_.Release();
}
if (deviceSource_)
{
deviceSource_.Release();
}
if (xuKsTopologyInfo_)
{
xuKsTopologyInfo_.Release();
}
if (xuNodeInstance_)
{
xuNodeInstance_.Release();
}
if (xuKsControl_)
{
xuKsControl_.Release();
}
}
void MSMFStreamChannel::start(const StreamProfile& profile, FrameCallback frameCallback)
{
ComPtr<IMFMediaType> mediaType = nullptr;
unsigned int width, height, fps;
FrameRate frameRateMin, frameRateMax;
bool quit = false;
frameCallback_ = frameCallback;
currentProfile_ = profile;
currentStreamIndex_ = -1;
for (uint8_t index = 0; index < 5; index++)
{
for (uint32_t k = 0;; k++)
{
auto hr = streamReader_->GetNativeMediaType(index, k, &mediaType);
if(hr == MF_E_INVALIDSTREAMNUMBER || hr == MF_E_NO_MORE_TYPES){
break;
}
HR_FAILED_EXEC(hr, { continue; })
GUID subtype;
HR_FAILED_RETURN(mediaType->GetGUID(MF_MT_SUBTYPE, &subtype));
HR_FAILED_RETURN(MFGetAttributeSize(mediaType.Get(), MF_MT_FRAME_SIZE, &width, &height));
HR_FAILED_RETURN(MFGetAttributeRatio(mediaType.Get(), MF_MT_FRAME_RATE_RANGE_MIN, &frameRateMin.numerator, &frameRateMin.denominator));
HR_FAILED_RETURN(MFGetAttributeRatio(mediaType.Get(), MF_MT_FRAME_RATE_RANGE_MAX, &frameRateMax.numerator, &frameRateMax.denominator));
if (static_cast<float>(frameRateMax.numerator) / frameRateMax.denominator < static_cast<float>(frameRateMin.numerator) / frameRateMin.denominator)
{
std::swap(frameRateMax, frameRateMin);
}
fps = frameRateMax.numerator / frameRateMax.denominator;
uint32_t device_fourcc = reinterpret_cast<const big_endian<uint32_t> &>(subtype.Data1);
if (width == profile.width &&
height == profile.height &&
fps == profile.fps &&
frameFourccToFormat(device_fourcc) == profile.format)
{
for (uint8_t i = 0; i < 5; ++i) {
if (index == i)
continue;
streamReader_->SetStreamSelection(i, FALSE);
}
HR_FAILED_RETURN(streamReader_->SetCurrentMediaType(index, nullptr, mediaType.Get()));
HR_FAILED_RETURN(streamReader_->SetStreamSelection(index, true));
streamReader_->ReadSample(index, 0, nullptr, nullptr, nullptr, nullptr);
streamState_ = STREAM_STARTING;
currentStreamIndex_ = index;
quit = true;
// wait for frame
std::unique_lock<std::mutex> lock(streamStateMutex_);
auto success = streamStateCv_.wait_for(lock, std::chrono::milliseconds(3000), [&]() {
return streamState_ == STREAM_STARTED;
});
if (!success)
{
stop();
}
break;
}
mediaType.Release();
}
if (quit)
{
break;
}
}
streamState_ = quit ? streamState_ : STREAM_STOPPED;
}
void MSMFStreamChannel::stop()
{
if (streamState_ == STREAM_STARTING || streamState_ == STREAM_STARTED)
{
streamState_ = STREAM_STOPPING;
streamReader_->SetStreamSelection(currentStreamIndex_, false);
streamReader_->Flush(currentStreamIndex_);
std::unique_lock<std::mutex> lk(streamStateMutex_);
streamStateCv_.wait_for(lk, std::chrono::milliseconds(1000), [&]() {
return streamState_ == STREAM_STOPPED;
});
}
}
bool MSMFStreamChannel::setXu(uint8_t ctrl, const uint8_t* data, uint32_t len)
{
if (xuSendBuf_.size() < XU_MAX_DATA_LENGTH) {
xuSendBuf_.resize(XU_MAX_DATA_LENGTH);
}
memcpy(xuSendBuf_.data(), data, len);
KSP_NODE node;
memset(&node, 0, sizeof(KSP_NODE));
node.Property.Set = reinterpret_cast<const GUID &>(xuUnit_.id);
node.Property.Id = ctrl;
node.Property.Flags = KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY;
node.NodeId = xuNodeId_;
ULONG bytes_received = 0;
HR_FAILED_EXEC(xuKsControl_->KsProperty(reinterpret_cast<PKSPROPERTY>(&node), sizeof(KSP_NODE), (void*)xuSendBuf_.data(), XU_MAX_DATA_LENGTH, &bytes_received), {
return false;
});
return true;
}
bool MSMFStreamChannel::getXu(uint8_t ctrl, uint8_t** data, uint32_t* len)
{
if (xuRecvBuf_.size() < XU_MAX_DATA_LENGTH) {
xuRecvBuf_.resize(XU_MAX_DATA_LENGTH);
}
KSP_NODE node;
memset(&node, 0, sizeof(KSP_NODE));
node.Property.Set = reinterpret_cast<const GUID&>(xuUnit_.id);
node.Property.Id = ctrl;
node.Property.Flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY;
node.NodeId = xuNodeId_;
ULONG bytes_received = 0;
HR_FAILED_EXEC(xuKsControl_->KsProperty(reinterpret_cast<PKSPROPERTY>(&node), sizeof(node), xuRecvBuf_.data(), XU_MAX_DATA_LENGTH, &bytes_received), {
*len = 0;
data = nullptr;
return false;
});
*data = xuRecvBuf_.data();
*len = bytes_received;
return true;
}
STDMETHODIMP MSMFStreamChannel::QueryInterface(REFIID iid, void** ppv)
{
#pragma warning(push)
#pragma warning(disable : 4838)
static const QITAB qit[] = {
QITABENT(MSMFStreamChannel, IMFSourceReaderCallback),
{nullptr},
};
return QISearch(this, qit, iid, ppv);
#pragma warning(pop)
}
STDMETHODIMP_(ULONG)
MSMFStreamChannel::AddRef()
{
return InterlockedIncrement(&refCount_);
}
STDMETHODIMP_(ULONG)
MSMFStreamChannel::Release()
{
ULONG count = InterlockedDecrement(&refCount_);
if (count <= 0)
{
delete this;
}
return count;
}
STDMETHODIMP MSMFStreamChannel::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD /*dwStreamFlags*/, LONGLONG /*timeStamp*/, IMFSample* sample)
{
HR_FAILED_LOG(hrStatus);
if (streamState_ == STREAM_STARTING)
{
std::unique_lock<std::mutex> lock(streamStateMutex_);
streamState_ = STREAM_STARTED;
streamStateCv_.notify_all();
}
if (streamState_ != STREAM_STOPPING && streamState_ != STREAM_STOPPED)
{
HR_FAILED_LOG(streamReader_->ReadSample(dwStreamIndex, 0, nullptr, nullptr, nullptr, nullptr));
if (sample)
{
ComPtr<IMFMediaBuffer> buffer = nullptr;
DWORD maxLength, currentLength;
byte* byte_buffer = nullptr;
HR_FAILED_EXEC(sample->GetBufferByIndex(0, &buffer), { return S_OK; });
buffer->Lock(&byte_buffer, &maxLength, &currentLength);
Frame fo = { currentProfile_.format, currentProfile_.width, currentProfile_.height, currentLength, (uint8_t*)byte_buffer };
if (depthFrameProcessor_)
{
depthFrameProcessor_->process(&fo);
}
frameCallback_(&fo);
buffer->Unlock();
}
}
return S_OK;
}
STDMETHODIMP MSMFStreamChannel::OnEvent(DWORD /*sidx*/, IMFMediaEvent* /*event*/)
{
return S_OK;
}
STDMETHODIMP MSMFStreamChannel::OnFlush(DWORD)
{
if (streamState_ != STREAM_STOPPED)
{
std::unique_lock<std::mutex> lock(streamStateMutex_);
streamState_ = STREAM_STOPPED;
streamStateCv_.notify_all();
}
return S_OK;
}
}} // namespace cv::obsensor::
#endif // HAVE_OBSENSOR_MSMF

Some files were not shown because too many files have changed in this diff Show More