From b81299cfa1c9b8235f78cbaa7ecaf1bfe6de23be Mon Sep 17 00:00:00 2001 From: John Zhao Date: Thu, 22 Mar 2018 12:57:35 +0800 Subject: [PATCH] Add libuvc on macOS --- .gitignore | 1 + CMakeLists.txt | 111 +++++++++++++++++++++++++++++++++++++-- CommonDefs.mk | 1 + Makefile | 28 ++++++---- cmake/Common.cmake | 10 ++++ include/mynteye/global.h | 2 + mynteye-config.cmake.in | 3 ++ scripts/init.sh | 13 ++++- src/uvc/uvc-libuvc.cc | 102 +++++++++++++++++++++++++++++++++++ src/uvc/uvc.h | 34 ++++++++++++ 10 files changed, 290 insertions(+), 15 deletions(-) create mode 100644 mynteye-config.cmake.in create mode 100644 src/uvc/uvc-libuvc.cc create mode 100644 src/uvc/uvc.h diff --git a/.gitignore b/.gitignore index 99d828a..d493f9d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_Store _build/ +_install/ _output/ /doc/output/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 24367ec..0542be6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,8 @@ message(STATUS "Found glog: ${glog_VERSION}") LIST(APPEND CMAKE_MODULE_PATH cmake) +include(CMakePackageConfigHelpers) + # config set(MYNTEYE_NAMESPACE "mynteye") @@ -41,17 +43,116 @@ configure_file( include(cmake/Common.cmake) -set(OUT_DIR "${PROJECT_SOURCE_DIR}/_output") +set(OUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/_output") set_outdir( "${OUT_DIR}/lib" "${OUT_DIR}/lib" "${OUT_DIR}/bin" ) -include_directories( - include - ${CMAKE_BINARY_DIR}/include -) +set(MYNTEYE_CMAKE_INCLUDE_DIR "${CMAKE_INSTALL_PREFIX}/include") +set(MYNTEYE_CMAKE_BINDIR "${CMAKE_INSTALL_PREFIX}/bin") +set(MYNTEYE_CMAKE_LIBDIR "${CMAKE_INSTALL_PREFIX}/lib") +set(MYNTEYE_CMAKE_INSTALLDIR "${MYNTEYE_CMAKE_LIBDIR}/cmake/${MYNTEYE_NAME}") + +## main add_executable(main src/main.cc) target_link_libraries(main glog::glog) +target_include_directories(main PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include +) + +## libmynteye + +set(MYNTEYE_NAME ${PROJECT_NAME}) + +set(MYNTEYE_PUBLIC_H + ${CMAKE_CURRENT_SOURCE_DIR}/include/mynteye/global.h + ${CMAKE_CURRENT_BINARY_DIR}/include/mynteye/mynteye.h +) + +if(OS_WIN) + set(UVC_SRC src/uvc/uvc-wmf.cc) +elseif(OS_MAC) + set(UVC_SRC src/uvc/uvc-libuvc.cc) + + find_package(libuvc REQUIRED) + set(UVC_LIB ${libuvc_LIBRARIES}) + + include_directories(${libuvc_INCLUDE_DIRS}) +elseif(OS_LINUX) + set(UVC_SRC src/uvc/uvc-v4l2.cc) +else() + message(FATAL_ERROR "Unsupported OS.") +endif() + +set(MYNTEYE_SRCS + ${UVC_SRC} +) + +set(MYNTEYE_LINKLIBS + glog::glog + ${UVC_LIB} +) +#message(STATUS "MYNTEYE_LINKLIBS: ${MYNTEYE_LINKLIBS}") + +add_library(${MYNTEYE_NAME} SHARED ${MYNTEYE_SRCS}) +target_link_libraries(${MYNTEYE_NAME} ${MYNTEYE_LINKLIBS}) + +if(OS_WIN) + target_compile_definitions(${MYNTEYE_NAME} + PUBLIC GLOG_NO_ABBREVIATED_SEVERITIES + ) +endif() + +target_include_directories(${MYNTEYE_NAME} PUBLIC + "$" + "$" + "$" + "$" +) + +set_target_properties(${MYNTEYE_NAME} PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} +) +set_target_properties(${MYNTEYE_NAME} PROPERTIES + PUBLIC_HEADER "${MYNTEYE_PUBLIC_H}" +) + +# install + +#message(STATUS "MYNTEYE_CMAKE_INCLUDE_DIR: ${MYNTEYE_CMAKE_INCLUDE_DIR}") +#message(STATUS "MYNTEYE_CMAKE_BINDIR: ${MYNTEYE_CMAKE_BINDIR}") +#message(STATUS "MYNTEYE_CMAKE_LIBDIR: ${MYNTEYE_CMAKE_LIBDIR}") +#message(STATUS "MYNTEYE_CMAKE_INSTALLDIR: ${MYNTEYE_CMAKE_INSTALLDIR}") + +install(TARGETS ${MYNTEYE_NAME} + EXPORT ${MYNTEYE_NAME}-targets + PUBLIC_HEADER DESTINATION ${MYNTEYE_CMAKE_INCLUDE_DIR}/${MYNTEYE_NAME} + RUNTIME DESTINATION ${MYNTEYE_CMAKE_BINDIR} + LIBRARY DESTINATION ${MYNTEYE_CMAKE_LIBDIR} + ARCHIVE DESTINATION ${MYNTEYE_CMAKE_LIBDIR} +) + +configure_package_config_file(mynteye-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/mynteye-config.cmake + INSTALL_DESTINATION ${MYNTEYE_CMAKE_INSTALLDIR} + NO_CHECK_REQUIRED_COMPONENTS_MACRO +) + +write_basic_package_version_file(mynteye-config-version.cmake + VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion +) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/mynteye-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/mynteye-config-version.cmake + DESTINATION ${MYNTEYE_CMAKE_INSTALLDIR} +) + +install(EXPORT ${MYNTEYE_NAME}-targets + DESTINATION ${MYNTEYE_CMAKE_INSTALLDIR} +) diff --git a/CommonDefs.mk b/CommonDefs.mk index f578785..dae46bb 100644 --- a/CommonDefs.mk +++ b/CommonDefs.mk @@ -150,6 +150,7 @@ ifneq ($(MAKE),) endif CMAKE_OPTIONS := +CMAKE_OPTIONS += -DCMAKE_INSTALL_PREFIX=$(shell pwd)/_install # CMAKE_OPTIONS += -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON CMAKE_OPTIONS_AFTER := diff --git a/Makefile b/Makefile index b445eca..2e3cb5d 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ help: @echo " make init init project" @echo " make build build project" @echo " make test build test and run" + @echo " make install install project" @echo " make clean|cleanall clean generated or useless things" .PHONY: help @@ -30,14 +31,6 @@ opendoc: apidoc .PHONY: apidoc opendoc -# init - -init: - @$(call echo,Make $@) - @$(SH) ./scripts/init.sh - -.PHONY: init - # deps submodules: @@ -50,6 +43,14 @@ third_party: submodules .PHONY: submodules third_party +# init + +init: submodules + @$(call echo,Make $@) + @$(SH) ./scripts/init.sh + +.PHONY: init + # build build: third_party @@ -67,12 +68,21 @@ test: submodules .PHONY: test +# install + +install: build + @$(call echo,Make $@) + @cd ./_build; make install + +.PHONY: install + # clean clean: @$(call echo,Make $@) @$(call rm,./_build/) @$(call rm,./_output/) + @$(call rm,./_install/) @$(MAKE) cleanlog cleanall: clean @@ -107,7 +117,7 @@ host: .PHONY: host -cpplint: +cpplint: submodules @$(call echo,Make $@) @$(SH) ./scripts/$@.sh diff --git a/cmake/Common.cmake b/cmake/Common.cmake index d3fe769..c79cc39 100755 --- a/cmake/Common.cmake +++ b/cmake/Common.cmake @@ -1,5 +1,15 @@ include(CMakeParseArguments) +if(MSVC OR MSYS OR MINGW) + set(OS_WIN TRUE) +elseif(APPLE) + set(OS_MAC TRUE) +elseif(UNIX) + set(OS_LINUX TRUE) +else() + message(FATAL_ERROR "Unsupported OS.") +endif() + # set_outdir(ARCHIVE_OUTPUT_DIRECTORY # LIBRARY_OUTPUT_DIRECTORY # RUNTIME_OUTPUT_DIRECTORY) diff --git a/include/mynteye/global.h b/include/mynteye/global.h index 33a1249..3694943 100644 --- a/include/mynteye/global.h +++ b/include/mynteye/global.h @@ -66,6 +66,8 @@ Class(const Class &) = delete; \ Class &operator=(const Class &) = delete; +#define UNUSED(x) (void)x; + template void unused(T &&...) {} diff --git a/mynteye-config.cmake.in b/mynteye-config.cmake.in new file mode 100644 index 0000000..837b02e --- /dev/null +++ b/mynteye-config.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/mynteye-targets.cmake") diff --git a/scripts/init.sh b/scripts/init.sh index a819899..9a77142 100755 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -18,6 +18,16 @@ fi _detect curl _detect $PYTHON +if [ "$HOST_OS" = "Mac" ]; then + _detect_install() { + brew ls --versions "$1" > /dev/null + } +else + _detect_install() { + _detect_cmd "$1" + } +fi + _install_deps() { _cmd="$1"; shift; _deps_all=($@) _echo "Install cmd: $_cmd" @@ -29,7 +39,7 @@ _install_deps() { fi _deps=() for _dep in "${_deps_all[@]}"; do - _detect_cmd $_dep || _deps+=($_dep) + _detect_install $_dep || _deps+=($_dep) done if [ ${#_deps[@]} -eq 0 ]; then _echo_i "All deps already exist" @@ -62,6 +72,7 @@ elif [ "$HOST_OS" = "Mac" ]; then # link clang-format-diff (if not compatible with Python 3, fix it by yourself) [ -f "/usr/local/bin/clang-format-diff" ] || \ ln -s /usr/local/share/clang/clang-format-diff.py /usr/local/bin/clang-format-diff + _install_deps "brew install" libuvc elif [ "$HOST_OS" = "Win" ]; then # detect pacman on MSYS _detect pacman diff --git a/src/uvc/uvc-libuvc.cc b/src/uvc/uvc-libuvc.cc new file mode 100644 index 0000000..2d9eceb --- /dev/null +++ b/src/uvc/uvc-libuvc.cc @@ -0,0 +1,102 @@ +#include "uvc.h" // NOLINT + +#include +#include + +// #define ENABLE_DEBUG_SPAM + +MYNTEYE_BEGIN_NAMESPACE + +namespace uvc { + +static void check(const char *call, uvc_error_t status) { + LOG_IF(FATAL, status < 0) + << call << "(...) returned " << uvc_strerror(status); +} +#define CALL_UVC(name, ...) check(#name, name(__VA_ARGS__)) + +struct context { + uvc_context_t *ctx; + + context() : ctx() { + CALL_UVC(uvc_init, &ctx, nullptr); + } + + ~context() { + if (ctx) + uvc_exit(ctx); + } +}; + +struct device { + const std::shared_ptr parent; + + uvc_device_t *uvcdevice; + uvc_device_handle_t *handle = nullptr; + + int vid, pid; + + // uvc_stream_ctrl_t ctrl; + // uint8_t unit; + // video_channel_callback callback = nullptr; + + device(std::shared_ptr parent, uvc_device_t *uvcdevice) + : parent(parent), uvcdevice(uvcdevice) { + open(); + + uvc_device_descriptor_t *desc; + CALL_UVC(uvc_get_device_descriptor, uvcdevice, &desc); + vid = desc->idVendor; + pid = desc->idProduct; + uvc_free_device_descriptor(desc); + } + + ~device() { + if (handle) + uvc_close(handle); + if (uvcdevice) + uvc_unref_device(uvcdevice); + } + + void open() { + if (!handle) + CALL_UVC(uvc_open, uvcdevice, &handle); + } +}; + +std::shared_ptr create_context() { + return std::make_shared(); +} + +std::vector> query_devices( + std::shared_ptr context) { + std::vector> devices; + + uvc_device_t **list; + CALL_UVC(uvc_get_device_list, context->ctx, &list); + for (auto it = list; *it; ++it) { + try { + auto dev = std::make_shared(context, *it); + devices.push_back(dev); + } catch (std::runtime_error &e) { + LOG(WARNING) << "usb:" << static_cast(uvc_get_bus_number(*it)) << ':' + << static_cast(uvc_get_device_address(*it)) << ": " + << e.what(); + } + } + uvc_free_device_list(list, 1); + + return devices; +} + +int get_vendor_id(const device &device) { + return device.vid; +} + +int get_product_id(const device &device) { + return device.pid; +} + +} // namespace uvc + +MYNTEYE_END_NAMESPACE diff --git a/src/uvc/uvc.h b/src/uvc/uvc.h new file mode 100644 index 0000000..7eaf2d3 --- /dev/null +++ b/src/uvc/uvc.h @@ -0,0 +1,34 @@ +#ifndef MYNTEYE_UVC_H_ // NOLINT +#define MYNTEYE_UVC_H_ +#pragma once + +#include +#include + +#include "mynteye/mynteye.h" + +#define MYNTEYE_VID 0x04B4 +#define MYNTEYE_PID 0x00F9 + +MYNTEYE_BEGIN_NAMESPACE + +namespace uvc { + +struct context; // Opaque type representing access to the underlying UVC + // implementation +struct device; // Opaque type representing access to a specific UVC device + +// Enumerate devices +std::shared_ptr create_context(); +std::vector> query_devices( + std::shared_ptr context); + +// Static device properties +int get_vendor_id(const device &device); +int get_product_id(const device &device); + +} // namespace uvc + +MYNTEYE_END_NAMESPACE + +#endif // MYNTEYE_UVC_H_ NOLINT