vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b
This commit is contained in:
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
|
||||

|
||||
|
||||
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"
|
||||
Executable
+81
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>();
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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_
|
||||
@@ -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
@@ -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::
|
||||
@@ -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 ¶m)
|
||||
{
|
||||
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 ¶m)
|
||||
{
|
||||
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 ¶m)
|
||||
{
|
||||
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 ¶m : 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 ¶m : 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 ¶ms)
|
||||
{
|
||||
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();
|
||||
}
|
||||
@@ -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 ¶ms)
|
||||
{
|
||||
CV_Assert(instance);
|
||||
mfxFrameAllocRequest request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
mfxStatus res = instance->QueryIOSurf(¶ms, &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
|
||||
@@ -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
|
||||
@@ -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(¶ms, 0, sizeof(params));
|
||||
params.mfx.CodecId = codecId;
|
||||
params.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
|
||||
res = decoder->DecodeHeader(&bs->stream, ¶ms);
|
||||
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(¶ms, ¶ms);
|
||||
//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(¶ms);
|
||||
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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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(¶ms, 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(¶ms, ¶ms);
|
||||
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(¶ms);
|
||||
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>();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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, ¤tLength);
|
||||
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
Reference in New Issue
Block a user